@unieai/uad-fs-ssh
v0.1.21
Published
The filesystem seam placed on a machine reached over SSH
Readme
@unieai/uad-fs-ssh
English | 中文
檔案系統接縫,放在一台透過 SSH 連到的機器上。與 subprocess-ssh 並排掛載、指向同一台機器時,執行世界就完整了:檔案工具、搜尋與 language server 看到的都是那台機器上的檔案,而它們都不知道為什麼。
用 shell 指令,不裝 agent
所有操作都以 POSIX shell 指令的形式,走機器名冊已經開著的那條連線過去。機器上不用裝任何東西——有 sshd 就夠了——代價是每一個操作都必須寫得出 shell,而且必須在兩種 userland 上都活得下來。
有兩個可攜性事實決定了一切:
stat有兩種方言。 GNU 與 busybox 認stat -c '%s %Y';BSD 與 macOS 認stat -f '%z %m'。每條連線探測一次,而不是從uname推斷——因為 Linux 主機可能裝著 BSD 工具,Mac 也可能透過 Homebrew 裝了 GNU 工具,答案必須描述真正會執行的那個stat。- 正規化用
cd加pwd -P,不用realpath或readlink -f——在同一條分界線兩側,它們不是不存在就是拼法不同。而且這個做法對「還不存在的路徑」也有效,這是每一次建檔都需要的:進到父目錄,再把檔名接上去。
檔名以 NUL 結尾讀回。檔名可以包含 NUL 以外的任何位元組——包括換行——而以行為單位的列表會把這種名字拆成兩筆,兩筆都指向不存在的東西。
列一個目錄的代價
一次往返,不管目錄裡有多少東西。對每個子項各做一次 stat,等於每個檔案一次連線往返;在延遲 30 ms 的線路上,兩百個檔案的目錄要六秒才打得開。這裡由 shell 在單一指令內列舉並回報每一筆的類型、大小與修改時間。
寫入
寫入會在目標自己的目錄裡建立暫存檔,再改名蓋過目標。mv 只有在同一個檔案系統內才是原子的,所以把暫存檔放到別處的暫存目錄,會悄悄退化成一次可以被讀者看到「寫到一半」的複製。傳輸中斷時原檔完好,暫存檔會被移除。
既有檔案的權限會沿用。chmod --reference 是 GNU 專有的,所以模式用機器自己的方言讀出來再套用——一個被改寫後失去執行權限的腳本、或一把被改寫後變成全世界可讀的金鑰,比一次失敗的寫入糟得多。
版本
新鮮度 token 由大小與修改時間推導,那是遠端能便宜回報的東西。同一秒內、且位元組數不變的兩次寫入因此無法區分——這是所有以 mtime 為基礎的守衛共同的極限。這也是為什麼寫入會回傳它產生的版本,而不是讓呼叫端自己再讀一次。
Model Experience
間接的,透過建立在 ctx.fs 之上的檔案工具——read、write、字串編輯器、搜尋——由它們擁有所有面向模型的契約;此套件自己不註冊任何 tool、prompt 或 schema,只改變它們的檔案位於哪一台機器上。
KV Cache effect
無。這裡不產生任何 prompt 片段、工具定義或 context 條目。
已知限制與未完成項目
- 沒有檔案監看。 接縫本身不要求它,但想要它的消費端(例如要顯示外部變更的編輯器)在這裡拿不到:shell 之上沒有 inotify,而輪詢遠端目錄是每個檔案一次往返。
- 版本只有秒級精度。 同一秒內、且大小不變的兩次寫入會相撞,於是本該擋下的過期寫入會通過。用內容雜湊可以關掉這個洞,代價是每次檢查都要把整個檔案讀兩次。
readBytes拒絕得晚。 上限是在位元組抵達的過程中執行的,所以超大的檔案會付出「傳到上限為止」的成本,而不是先從 metadata 就被擋下。- 遠端必須是 POSIX 類的登入 shell,而且要有兩種
stat方言之一。兩者皆無的機器會在第一個操作就被拒絕,並說明缺什麼。 - 一次掛載一台機器,與 subprocess adapter 相同;要按 workspace 選擇,需要一個 harness 目前還沒有的路由器。
