跳至主要內容

沙箱

沙箱是在你自己的機器上啟動的容器,裡面只有你自己的代理程式、Motir CLI 和你的儲存庫副本——別無他物。你自帶代理程式憑證,並以唯讀方式掛載;迴圈在容器內執行,因此行為失當的代理程式只能觸及你的工作樹,而無法觸及機器上的其他部分。

開始之前

  • Docker,已在執行。 同時建置了 linux/amd64 和 linux/arm64 兩種版本,因此 Apple Silicon 是原生支援,沒有任何模擬。沒有建置步驟——直接拉取。
  • 你的代理程式自己的登入狀態,就在這台機器上。 它的憑證掛載是唯讀的,所以容器可以使用某個登入狀態,卻不能更新。你會遇到的例外是 macOS 上的 Claude Code:它把權杖儲存在登入鑰匙圈中,因此沒有可掛載的檔案,需要改為在容器 內部 登入 claude——映像為它提供了可寫的設定目錄,登入狀態就落在那裡。(Antigravity 同理——你選擇它時,第 2 步會說明。)
  • 你的工作區根目錄——包含各個儲存庫副本的那個資料夾。 一個專案通常橫跨多個儲存庫,迴圈會在所有這些儲存庫中執行。

你的電腦

~/work/                 ← start the container from HERE
├── motir-core/         ← a checkout
└── motir-ai/           ← another

你使用哪個代理程式?

也支援

或

下面的每條指令都針對 Claude Code。切換後,會重寫 第 1、2 和 2b 步 中的標籤和憑證掛載——也就是它們出現的三處。

設定

共五步。每一步只做一件事。

  1. 拉取適用於你的代理程式的映像

    指令

    沒有建置步驟——映像按代理程式設定檔分別釋出。

    pull

    docker pull ghcr.io/moooon-b-v/motir-sandbox:claude
  2. 在工作區根目錄啟動容器

    指令

    請在 包含 各個儲存庫副本的資料夾中執行,而不是在其中任何一個儲存庫裡。

    run

    docker run -it --rm --pull=always \
      -v "$PWD:/workspace" \
      -v motir-auth:/home/node/.config/motir \
      -v "$HOME/.claude:/home/node/.claude:ro" \
      ghcr.io/moooon-b-v/motir-sandbox:claude

    改用 VS Code? 下面的第 2a–2c 步取代本步。之後的所有內容,兩種方式都相同。

  3. 安裝 Dev Containers 擴充

    在編輯器中

    可以在「擴充」檢視中安裝,或透過命令選擇區——macOS 上按 ⇧⌘P,其他系統按 Ctrl+Shift+P,三個平台都可以按 F1——然後選擇 Extensions: Install Extensions。這三步裡有兩步要在命令選擇區中完成,所以現在就把它固定下來很值得。

  4. 建立開發容器設定

    指令

    在你要掛載的資料夾中執行。一次貼上即可:它會建立 .devcontainer 資料夾,並把檔案寫入其中。不要嘗試透過檔案選擇器建立它們——Finder 和大多數圖形介面的選擇器都不接受以點開頭的名稱,而且拒絕時不會告訴你原因。

    你的機器 — 在你掛載的資料夾中

    mkdir -p .devcontainer
    cat > .devcontainer/devcontainer.json <<'JSON'
    {
      "name": "Motir sandbox (Claude Code)",
      "image": "ghcr.io/moooon-b-v/motir-sandbox:claude",
      "workspaceFolder": "/workspace",
      "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind",
      "mounts": [
        "source=motir-auth,target=/home/node/.config/motir,type=volume",
        "source=${localEnv:HOME}/.claude,target=/home/node/.claude,type=bind,readonly"
      ],
      "remoteUser": "node",
      "overrideCommand": true,
      "postStartCommand": "motir-sandbox-agent-config || true"
    }
    JSON

    開發容器會保留建立它時所用的映像。 --pull=always 屬於第 2 步的執行指令,不屬於這條路線。要切換到目前的映像和 motir CLI:1. 在你機器上的終端機裡執行第 1 步的 docker pull;2. 對此資料夾執行 Dev Containers: Open Folder in Container…,這會附加視窗;3. 執行 Dev Containers: Rebuild Container,它會用你剛拉取的映像重新建立容器。「Rebuild Container」只會出現在已附加到容器的視窗中,所以必須先做第 2 步。重新建置會保留你的 Motir 登入狀態(它存放在 motir-auth 磁碟區上),但不會保留在容器內完成的 Claude Code 登入——請執行 claude 並重新登入。

  5. 在容器中開啟該資料夾

    在編輯器中

    命令選擇區 → Dev Containers: Open Folder in Container…,然後選擇你剛剛寫入該檔案的資料夾。它的終端機就是第 2 步本會帶你進入的同一個 shell——從第 3 步繼續。

  6. 在容器內登入

    指令

    會輸出一個程式碼和一個 URL;在任意瀏覽器中核准即可。登入狀態會落在 motir-auth 磁碟區上,所以只需做一次。

    在容器中

    motir login
  7. 把該資料夾關聯到你的專案

    指令

    把 ACME 換成你的專案鍵。如果你的工作區恰好只有一個專案,就省略該參數——這就是整個步驟。

    在容器中

    motir link --project ACME
  8. 檢查——全部透過,就是本頁的終點

    指令

    認證、關聯、代理程式二進位檔及其憑證。只有這一步能告訴你,容器是否真的拿到了你傳給它的東西。

    在容器中

    motir doctor

該指令寫入的檔案

僅供參考,不是步驟——2b 已經把它寫好了。這裡提供給更願意手動建立該檔案的讀者,同時也因為 <<’JSON’ 周圍的引號起著關鍵作用:它們阻止你的 shell 在 ${localWorkspaceFolder} 和 ${localEnv:HOME} 到達檔案之前就把它們展開。這些是 Dev Containers 的替換變數,由編輯器來解析。

.devcontainer/devcontainer.json

{
  "name": "Motir sandbox (Claude Code)",
  "image": "ghcr.io/moooon-b-v/motir-sandbox:claude",
  "workspaceFolder": "/workspace",
  "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind",
  "mounts": [
    "source=motir-auth,target=/home/node/.config/motir,type=volume",
    "source=${localEnv:HOME}/.claude,target=/home/node/.claude,type=bind,readonly"
  ],
  "remoteUser": "node",
  "overrideCommand": true,
  "postStartCommand": "motir-sandbox-agent-config || true"
}

為什麼是這樣

設定檔選擇器改變了什麼

選擇一個代理程式會改寫三樣東西,僅此而已:映像 標籤、憑證的 -v 行,以及開發容器的 image、name 和 mounts。之所以做成一個控制項,而不是一段讓你自己去替換的文字,是因為這裡的每條指令都有「複製」按鈕,而會去複製的讀者,就是不會去讀替換說明的讀者。

並非每個設定檔都只有一個憑證目錄。opencode 有兩個,需要兩條 -v 行;antigravity 把權杖儲存在作業系統的金鑰環中,一條也不需要,改為在容器內登入;aider 繫結的是一個檔案,並從環境變數讀取模型金鑰。你選擇它們時,步驟中會有相應說明。

執行指令不會保留任何可能過時的東西

--pull=always 會在每次啟動時取得目前映像,因此已經移動的設定檔標籤無需你察覺就能到達你手中,而 --rm 意味著不會保留任何可能過時的東西。沒有單獨的「回頭再用」路徑——過去正是這一點讓人執行著比所讀頁面舊了好幾個月的 motir。你的登入狀態不受這一切影響:它寫入 motir-auth 磁碟區,該磁碟區位於容器之外,所以只需登入一次,之後每次執行都會沿用——徹底登出用 docker volume rm motir-auth。離線工作?去掉 --pull=always:它在每次啟動時都會存取映像登錄庫,所以沒有網路時執行會失敗,而不是回退到你已有的映像。以上全都是執行指令的行為。開發容器(第 2a–2c 步)會一直保留建立它時所用的映像,直到你拉取、透過 Dev Containers: Open Folder in Container… 附加,並選擇 Dev Containers: Rebuild Container。

下一步

motir run 接收一個範圍——一個工作項目、一整個使用者故事,或用 sprint 表示目前活躍的 Sprint。motir auto 則無人值守地清空就緒集合,一次處理一個工作項目,提交到一個工作階段分支上。兩者接受的每個參數都在 CLI 頁面上。

它限制了什麼——以及沒有限制什麼

在你依賴它之前值得讀一讀,因為這三項中有一項是例外,而不是保證。

  • 檔案系統——受限。 容器內唯一的主機介面,是一個可寫的 /workspace 和你的代理程式自己的憑證(以唯讀方式掛載)。沒有 Docker socket,沒有其他主機繫結。
  • 網路——開放,這是有意為之。 每個代理程式都需要它的供應商 API,每個被排程的工作項目都需要 git 遠端儲存庫,因此映像限制的是檔案系統的影響範圍,而不是出站流量。如果你的威脅模型要求更多,請使用 Docker 自身的網路控制——容器不會阻止代理程式存取網際網路。
  • 權限——無特權。 它以 node 使用者(uid 1000)執行,所以寫入掛載目錄的檔案仍歸你所有,而不是 root。

環境為你提供了什麼

  • 你的資料夾,已掛載。 $PWD 會變成 /workspace,所以執行所處理的儲存庫副本是你自己的,它產生的提交在結束時就在你的磁碟上。
  • 每個工作項目一份簽出,位於 git 工作樹上。 一次執行不會編輯你目前所在的目錄樹;它會為每個工作項目新增一個工作樹,因此並行執行不會在分支簽出上發生衝突。
  • 你的代理程式憑證,唯讀。 設定檔的憑證目錄以 :ro 繫結掛載。容器內沒有任何東西能改寫它,它的任何內容也不會傳送給 Motir——這是自帶金鑰模式,所以代理程式的費用由你承擔,API 呼叫也不會經過我們。
  • 預裝的 CLI。 映像帶有 motir 以及標籤所指定的代理程式二進位檔,所以首次執行前無需安裝任何東西。
  • 代理程式的輸出預設留在本機。 只有執行的生命週期會到達 Motir。傳入 --report-log 會額外傳送輸出的末尾部分,使失敗的執行能在執行頁面上顯示它;除非你要求,否則它是關閉的,而且無論哪種情況,檔案內容、路徑和差異都不會被發送。

權杖可以做什麼——以及它拒絕什麼

由 motir login 產生的權杖帶有固定且收窄的授權。核准介面會顯示它,但不能更改——既不能放寬也不能收窄,因為手動收窄的授權會讓無人值守的迴圈在中途出錯。

透過裝置簽發的權杖所帶有的授權

project:browse      read the project and its work items
lesson:view         search the recorded lessons before building
lesson:reinforce    record that a lesson described what went wrong
work_item:edit      edit the item it is running, and file a bug
comment:add         comment on the item
ai:plan             open a plan

它不帶有的那個權限是 ai:view_plan,隨之而來的拒絕是設計如此,而不是錯誤。 開啟計畫只需要 work_item:edit,所以沙箱中的執行可以開啟一個——然後在第一次追加時被拒絕,因為新增提案所檢查的正是那個權限。執行某個工作項目的執行無權重塑交給它的計畫。遇到這個拒絕時,說明代理程式做對了:它把更正記錄為留言,讓該工作項目保持已封鎖,然後停止。不會遺失任何東西,由人來決定計畫應該怎麼寫。

想讓執行更安靜時,有兩個參數可以進一步收窄:--disable-log-bug 讓代理程式不再為它在別處發現的錯誤提交錯誤工作項目(改為留下留言),--disable-replan 讓它不再為它判定有誤的工作項目提交重新規劃(改為留下留言並停止)。僅在 motir auto 上,--auto-approve-replan 走相反的方向:它會核准已提交的重新規劃並繼續迴圈,而不是停下來等你。

一次執行會產出什麼,以及在哪裡檢視

  • 一個分支和一個提取要求,位於該工作項目涉及的每個儲存庫,由容器內用你的 git 憑證推送。
  • 工作項目上的一個連結。 執行會宣告每個提取要求交付的是哪個工作項目,所以合併它就會推進該工作項目。工作項目頁面的「開發」面板顯示的正是這個連結,在合併時關閉工作項目的也是它——不是分支名,也不是標題。
  • 狀態,隨進度更新。 執行領取工作項目時,它變為進行中;提取要求開啟時,它變為已實作。審查中由 CI 在檢查透過時寫入,已完成則由合併寫入。
  • 終端機。 除非你傳入了 --report-log,否則代理程式自己的輸出會留在你的終端機中。

它無法正常運作時

找不到代理程式二進位檔

標籤與代理程式不一致。檢查你啟動的是哪個設定檔,或用 --agent <cmd> 讓執行指向另一個二進位檔。motir doctor 會在執行白白占用一次領取之前報告這一點。

代理程式已啟動,但未透過認證

憑證掛載缺失,或指向了錯誤的目錄——每個設定檔各自掛載自己的目錄。請針對你實際拉取的標籤,重新執行 docker run 那一行。

沒有可執行的內容

每個候選項都有未滿足的依賴。motir ready 會顯示就緒集合;對某個工作項目執行 motir show,會指出是什麼擋住了它。無論如何都要排程,則用 --force,且一次只能排程一個工作項目。

執行因已提交的重新規劃而停止

代理程式判定該工作項目有誤,並提出了修正後的結構。這是預期的停止:請在 Motir 中閱讀該計畫,並核准或拒絕它。若想讓無人值守的迴圈繼續,請執行 motir auto 並加上 --auto-approve-replan。

執行結束後留下了未完成的工作

工作樹和分支在你的磁碟上,位於你掛載的資料夾下——容器停止並不會把它們帶走。motir done 會收尾一個已合併的工作項目,或用 --session <branch> 收尾整個已合併的工作階段分支。

本頁不涵蓋的內容

每一條指令和每一個參數——那是 CLI,它由 CLI 自身的目錄產生,不會與之偏離。不透過 CLI、直接把代理程式串接 Motir,見 MCP 伺服器。透過 HTTP 而不是從終端機驅動同一個工作迴圈,見 API 參考。在你自己的機器之外執行沙箱,這裡尚未提供文件。(VS Code 路線已在上文記錄——這句話過去說的是另一回事,它把一個已刪除的章節當作決定記錄了下來。)