sandbox_sync

把本機資料夾或單一檔案上傳到箱子的 /work/<dest>,只傳不一樣的檔案。只能經 stdio 轉接器用。

輸入 輸出
id、localPath、dest、commit、alsoPaths、includeIgnored、exclude、prune、submodules、baseline、baselineDeps、dryRun、maxMB(只能經轉接器) ok、dest、remotePath、selected、files、sentFiles、sentPaths、changedOnBox、uploadedBytes、skipped;有的時候才有 staleInDest、pruned、excluded、deps、uncommitted、commitSha、submodules、submodulesMissing、notes;帶 dryRun 時多 dryRun、sendMB、totalMB、largest;帶 baseline 時多 baseline 與 note

sandbox_sync(只能經轉接器)把本機目錄送到 /work/<dest>。localPath 請給絕對路徑;相對路徑是以轉接器程序的工作目錄解析,agent 看不到那個目錄。dest 相對於 /work:寫 app,不是 /work/app;開頭的 /work/ 會被拿掉,所以寫 /work/app 也行,其他絕對路徑或跑出 /work 的路徑,在送任何東西之前就回錯。

在 git 工作區裡取 git 追蹤的檔案加上沒被 .gitignore 排除的新檔,先問箱子哪些跟 dest 裡的不一樣,只傳那些,所以在 /work/<dest> 上跑的 dev server 只會看到你改的地方;skipped 列出沒送的頂層名稱。不是 git 工作區時取 node_modules、.git、dist、build、coverage、.venv 這些以外的所有檔,帶 commit 時取那個 commit 的檔,一樣先比對、只送不一樣的,所以第二次同步只送有改的。不會送 .git。結果會說發生了什麼:

  • remotePath:落在箱子裡的哪裡,/work/<dest>。
  • sentPaths:這次送了哪些檔(前 50 個;sentFiles 是總數);changedOnBox:箱子實際改寫了幾個檔。0 表示箱子裡本來就都有了。
  • notes:要注意的事,例如送了 package.json、vite.config.*、tsconfig* 或 .env*,開著的 dev server(Vite、webpack、Metro)會整頁重載或重啟,語言、路由、塞進去的資料這類頁面狀態都會不見。另外還有:
    • 依賴:node_modules 不會送。有 package-lock.json、pnpm-lock.yaml 或 yarn.lock、箱子裡卻沒有 node_modules 的資料夾會列出來,附上在那裡安裝的指令,例如 sandbox_build { "dir": "repo/sdk", "cmd": "npm ci", "out": ["node_modules"] };npm 的話,裝好的套件跟 lockfile 比起來少了或版本不同時,講有幾個、是哪些。同樣的內容也放在 deps(dir、lock、state 是 missing 或 stale)。那裡出現的 "Cannot find module" 是這個造成的,不是你的改動。
    • 第一次同步進某個 dest 時:被 ignore 規則擋下的建置設定(tsconfig.json、.env.local、*.config.*)與補送的方法(把它們列在 alsoPaths,或用 includeIgnored),以及不會送 .git,所以在那裡跑 git 命令會回 "not a git repository"。
    • 送過去的 gradlew、mvnw 或 #! 開頭的 *.sh 沒有執行權限(git 記成 100644):用 bash 跑,或在箱子裡 chmod +x。
    • 遇到 fetch failed、連線被重設或 HTTP 502/504 而重送過一次的上傳。
  • 同步失敗時,結果寫 NOT SYNCED: /work/<dest> still has its old files (or only part of this sync),並列出原本要送的檔。接下來在那裡跑的命令看到的是舊程式,這個箱子下一次 sandbox_exec 的結果後面也會附一次提醒:先重新同步,再相信測試結果。

大小:要送的量(磁碟上的大小)超過 100 MB 的同步什麼都不送,錯誤裡會列出最大的資料夾,例如 renderer/ 195.2 MB in 1203 files (renderer/dist-win/ 191.6 MB)。這種建置產物請加進 .gitignore 或 exclude,或用 commit 加 alsoPaths 只送自己的檔;大小本來就該那麼大時傳 maxMB。"dryRun": true 會跟箱子比對,回傳同步的話會送什麼(sentFiles、sentPaths、sendMB、largest、staleInDest、notes),什麼都不送也不刪。同步進行中,要進度的 client 每 10 秒會收到一行(算雜湊、比對、已上傳幾 MB)。上傳兩分鐘都沒動,或箱子收完十分鐘內都沒回應,會直接回錯並寫明卡在哪一步,不會一直掛著。

單一檔案:localPath 是檔案時,dest 就是它在箱子裡的路徑:"localPath": "/abs/repo/renderer/.env", "dest": "renderer/.env" 寫到 /work/renderer/.env。dest 結尾是 /、或在箱子裡已經是資料夾時,檔案照原名放進去。落在哪裡看 remotePath。commit、alsoPaths 與 prune 要給資料夾。

刪除:dest 裡有、localPath 沒有的檔(本機刪掉或改名的,或你送的 commit 裡已經沒有的)會留在箱子裡,在那裡跑的型別檢查或測試會繼續報它們的錯。結果用 staleInDest 列出(count 與前 20 個 paths;超過 1000 個時有 countIsPartial),notes 也會提。"prune": true 會刪掉它們,pruned 是刪了幾個。本機忽略規則跳過的路徑(node_modules、建置產物、.env)不列也不刪,所以箱子自己建出來的東西不會被動到。prune 要 localPath 在 git 工作區或有 commit、dest 在 /work 底下(不能是 /work 本身),也不能跟 includeIgnored 一起用。同步到新的 dest 也能拿到乾淨的樹。

選項:

  • commit:送這個 git revision(HEAD、origin/main)的樹而不是工作區,共用的工作目錄裡別人改到一半的檔案就不會跟著進去。不存在的 revision 會回錯,什麼都不送。結果的 commitSha 是送的那個 commit。
  • alsoPaths:搭配 commit,localPath 底下這些路徑改用工作區裡的樣子,測「那個 commit 加上你自己的修改」。資料夾會把 commit 那份整個換掉,內容是不帶 commit 同步時會從那裡送的檔:你在工作區刪掉或改名的檔不會從 commit 冒回來,node_modules 這類被忽略的檔也不會送,除非加了 includeIgnored。不帶 commit 時,alsoPaths 就是只送這幾個路徑(檔案或資料夾,例如 git diff --name-only 的清單),各自送到 dest 底下的同一個位置,挑檔方式一樣;staleInDest 也只在這些資料夾裡找。它們從 repo 其他地方 import 的檔不會跟著送:箱子裡還沒有完整的一份時,不要只同步某個子資料夾,改送 "commit": "HEAD"、把這些路徑放在 alsoPaths。
  • includeIgnored:連 ignore 規則會跳過的檔案(dist、產生的檔案)也送,.git 以外全部。預設關閉。
  • exclude:相對於 localPath 的 glob,這次同步兩個方向都不碰:不送、不比對,箱子裡那份也不會列進 staleInDest 或被 prune 刪掉,所以你在箱子裡改過的檔(改成指向 dev server 的 capacitor.config.ts)會留著。沒有 / 的樣式比對任何一層的名字(*.log、dist-win),有 / 的從 localPath 算起(android/app/build.gradle),** 可以跨資料夾。擋下了哪些列在 excluded。
  • dryRun:什麼都不送也不刪,只回傳同步的話會做什麼(見上)。
  • maxMB:一次同步的大小上限,預設 100(見上)。
  • prune:刪掉 staleInDest 列的檔(見上)。
  • submodules:搭配 commit,也照樹裡記的 commit 送每個子模組(見下)。
  • baseline:同一次呼叫裡,再把這個 git revision(HEAD、origin/main、你這個分支的 merge base)的樹送到 <dest>-baseline,兩份都來自同一個 repo 的同一刻。結果多一個 baseline(那份的 dest、commit 與檔數,或它的 error)和一句 note。用法見下面。
  • baselineDeps:搭配 baseline,把 dest 裡每個 node_modules 複製到 baseline(預設 true,見下)。

共用的工作目錄裡(同一個 repo 有好幾個 session 在改),不帶 commit 的同步會把別人改到一半的檔一起送進去,箱子報的錯看起來就像你的。這種同步會在 uncommitted 列出跟 HEAD 不一樣的路徑(count 與前 20 個,附 git status 的代碼),並附一句提醒。改送 "commit": "HEAD",把你自己還沒 commit 的檔列在 alsoPaths,或同步你自己的 git worktree:

sandbox_sync { "id": "<id>", "localPath": "/absolute/path/to/repo", "dest": "repo", "commit": "HEAD", "alsoPaths": ["src/refund.ts", "src/refund.test.ts"] }

測試失敗、又不知道是不是你的改動造成的,就加 baseline,兩份各跑同一條命令:

sandbox_sync { "id": "<id>", "localPath": "/absolute/path/to/repo", "dest": "repo", "baseline": "origin/main" }
sandbox_exec { "id": "<id>", "cmd": "go test ./... 2>&1 | grep -E '^(--- FAIL|FAIL|ok)'", "cwd": "repo", "note": "跑有我改動的測試" }
sandbox_exec { "id": "<id>", "cmd": "go test ./... 2>&1 | grep -E '^(--- FAIL|FAIL|ok)'", "cwd": "repo-baseline", "note": "跑改動前的同一批測試" }

兩邊都失敗的,在你改之前就已經壞了。一次呼叫就能跑完兩邊並比較:sandbox_exec 帶 "baseline": true、cwd 在 dest 裡,會先在 repo、再在 repo-baseline 跑這個命令,忽略耗時與測試編號,回 onlyMine、alreadyFailing 與 fixedByMine(sandbox_exec):

sandbox_exec { "id": "<id>", "cmd": "go test ./...", "cwd": "repo", "baseline": true, "note": "比較有沒有我的改動時的測試失敗" }

要證明新加的測試抓得到 bug,把它複製到 repo-baseline,看它在那裡失敗。baseline 那份有那個 commit 追蹤的檔,再加上 dest 裡每個 node_modules 在同一個位置的複本:只複製 baseline 還沒有的,用硬連結做(不另外佔空間,幾秒完成),baseline.depsCopied 列出複製了哪些。兩棵樹各有自己的資料夾與 Vite 快取,可以同時跑(兩邊 symlink 同一份 node_modules 的話,Vite 的快取會互相覆蓋)。baseline 那個 commit 的 lockfile 要的版本不一樣時,baseline 的 notes 會說,在那裡跑 npm ci(會換掉複本)。"baselineDeps": false 就不複製。也給 baseline 一顆自己的測試資料庫(psbx-testdb up base),兩邊才不會互相清表。

git 子模組:

  • 不帶 commit 時,已 init 的子模組照磁碟上的樣子整個送過去:裡面被忽略的檔(它的 node_modules、建置產物)也在內,而且每次同步都整個送,不是有改才送。沒 init 的子模組到箱子裡是空資料夾。
  • 帶 commit 時,以及 baseline 那份,子模組是空資料夾(git archive 不會展開子模組),除非加了 "submodules": true。加了之後,每個子模組(含巢狀的)都照樹裡記的 commit、從本機的子模組 checkout 取出送過去:結果在 submodules 列出(path、commit),送不了的列在 submodulesMissing 並附原因(沒 init:跑 git submodule update --init;本機子模組裡沒有那個 commit:在那裡 git fetch)。它們不會擋下整次同步。
  • 不會送 .git,所以同步過去的那份裡跑不了 git 命令(git submodule update、git describe);建置需要它們時,改在箱子裡 git clone --recurse-submodules。

所有工具與主題都列在工具參考。