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。
所有工具與主題都列在工具參考。