Skip to content

Agent CLI Contract ​

English · 繁體中文

Orbit 的設計初衷就是讓 coding agent 透過 CLI 來驅動。Agent 在需要解析狀態、決定下一步動作,或從錯誤中復原時,應優先選擇結構化輸出,而非給人看的文字格式。

自動更新的 policy、release target、defer reason 與 per-runtime outcome 會以 additive fields 出現在 status --json/inspect --json;update transaction 進行期間,這兩個 read-only command 仍可觀察 durable state。完整 command behavior table 由 English contract 維護。

Rule of Thumb ​

程式化的讀寫請使用 --json:

bash
orbit status --json
orbit doctor --json
orbit up --infra --json
orbit logs redis --json

給人看的輸出是為 terminal 最佳化的,未來可能變動。JSON 輸出才是 agent 所依賴的 contract。

JSON Envelope ​

已轉換到 agent contract 的指令會回傳一個 JSON 物件,最上層結構如下:

json
{
  "schema_version": "orbit.cli.v1",
  "ok": true,
  "command": "doctor",
  "instance": "ci-123",
  "data": {},
  "error": null,
  "notices": [],
  "recommended_actions": []
}

欄位說明:

Field意義
schema_versionContract 版本,目前為 orbit.cli.v1。
ok成功為 true,失敗為 false。
command產生這個 response 的 Orbit 指令。
instance--instance 指定的 named runtime;default runtime 會省略。
data成功時的 command-specific payload。
error失敗時的結構化 error payload,否則為 null。
notices不會使指令失敗的結構化事件,例如一次性的舊 source 遷移。
recommended_actionsAgent 應考慮的後續指令。

所有 lifecycle commands 都接受 --instance <name>。使用 orbit instance list --json 取得各 instance 的 environment、state、dashboard 與解析後的 resource endpoints。orbit instance clean <name> --json 只會停止並移除帶有 該 instance ownership 的 resources。Named runtime 產生的 recommended actions 會保留 instance target。完整 targeting 與 cleanup model 見 隔離的 runtime instances。

已轉換的指令在 --json 模式下若失敗,Orbit 會在 stdout 印出單一 JSON 物件,並以 exit code 1 結束。

orbit up --json 與 orbit env apply --json 共用同一個 operation-wide --timeout,預設為五分鐘。Deadline 從指令開始執行時計算,並涵蓋 daemon readiness、lifecycle RPC、status polling 與 failure-evidence collection。 Deadline 用盡時回傳 timeout;caller 或可攔截 signal 取消時回傳 canceled。 若 environment reconcile request 已 dispatch,canceled message 會如實說明 daemon 可能已接受並繼續該工作,並建議執行 orbit status --json。

Streams ​

stdout 只承載最終 envelope,請單獨解析。stderr 則承載供人閱讀的 diagnostics 與 progress,不屬於穩定機器契約;內容、措辭與時間點可能隨版本改變。 orbit up --json 與 orbit env apply --json 會在 stderr 簡短提示 lifecycle phase。 同一 phase 持續 30 秒後,heartbeat 才會加入 elapsed time 與約略的剩餘 operation budget。Readiness interval 若已有 resource heartbeat,就不會再輸出 generic phase heartbeat。Agent 應以最終 envelope 做決策,不應解析 stderr 文字。

若在解析前把 stderr 合併進 stdout,progress 行會破壞 JSON。請分開 redirect,或在 只需要 envelope 時捨棄 stderr。

Error Shape ​

結構化錯誤使用以下形狀:

json
{
  "schema_version": "orbit.cli.v1",
  "ok": false,
  "command": "logs",
  "data": null,
  "error": {
    "code": "unknown_resource",
    "message": "unknown resource: redisx",
    "hint": "Run orbit status --json to list configured resources.",
    "retryable": false,
    "next_command": "orbit status --json"
  },
  "recommended_actions": [
    {
      "command": "orbit status --json",
      "reason": "List configured resources and current states."
    }
  ]
}

Agent 應優先依據 error.next_command 與 recommended_actions 行動,而不是從錯誤訊息文字去猜測恢復路徑。

recommended_actions[].command 的 machine/display contract 只維護在英文版 Executable recommended actions。

有些失敗沒有任何指令可以推進。socket_path_too_long 必須先把 ORBIT_HOME 換成較短的路徑,任何 Orbit 指令才能成功,因此它只帶 hint,不帶 next_command 或 recommended action。缺少 next_command 應理解為「依 hint 處理」,而不是回應格式有問題。

遇到 resource_port_conflict 時,error.next_command 是該平台用來查看 目前 port owner 的唯讀指令。Resource status 也會提供 port_conflict evidence(port、resource、可取得時的 owner,以及 inspect_command)。 在停止 owner 或於 shared environment 選用其他 host port 前,不應盲目 retry 或讀取 logs。

只有 running daemon 已替該 resource 緩衝輸出時,resource status 才會包含 logs_available: true。欄位不存在代表目前沒有歷史輸出可檢查;dependency 卡住的 resource 若沒有這項證據,client 不應把 Logs 當成復原動作。若 resource 從未成功啟動,直接執行 orbit logs <resource> --json 會回傳 logs_unavailable,而不是空的成功結果。唯一 recommended action 會依即時 port 重驗結果決定:仍被占用時檢查目前 owner,釋放後則只 retry 該 resource。

Degraded host process 可能同時包含 state_reason(例如 exited: exit status 1)與 failure_evidence;後者是針對該次失敗 generation 保存的最後一行有效 application log。它是 lifecycle reason 的 佐證,不是取代 reason。

失敗的 orbit up --json 會把證據放在 data.failed_resources:每個沒有達到 healthy 的 requested resource 一筆,含 name、kind(container 或 service)、role、state、state_reason、failure_kind 與 log_tail (最多 20 行最近的 log)。直接從這裡讀失敗原因,不必再補一次 orbit logs。

Orbit 還在等 container 變 healthy 時,若 container 停止,等待會立刻結束, 並帶 failure_kind: runtime 與 Docker 的 exit_code(被 kernel 殺掉時另有 oom_killed: true)。orbit status --json 也會回報同樣的欄位。請在 failed_resources 找這筆,不要只看 error.code:相依的 resource 可能先被 回報成 dependency_blocked。

需要明確授權的破壞性步驟使用穩定的 confirmation_required 錯誤碼。被拒絕 時什麼都不會改變;第一個 recommended action 就是加上 --yes 的同一個指令。 只有在 caller(或其操作者)確實想要這個破壞性結果時才執行它。

穩定錯誤碼 ​

error.code 的值屬於版本化契約。完整清單只維護一份,見英文版 agent-cli.md 的 Stable error codes。

Converted Commands ​

下列指令在加上 --json 時,目前都使用 orbit.cli.v1 envelope:

每個指令的 JSON 行為只維護一份完整對照表,見英文版 agent-cli.md。新增或變更指令時只更新那份表。

Lifecycle 指令在 JSON 模式下不會把裝飾性或 diagnostic progress 寫入 stdout, 因此最終 envelope 仍可解析。

Lifecycle actions 會依結果提供。成功的 up 只回傳一個主要下一步: orbit open --json。啟動失敗時則建議查看 status、根因 resource 的 logs, 並在修正原因後只 restart 該 resource;不會把 agent 導向無關的 setup 診斷。

Lifecycle payload 全面使用 resource vocabulary:

  • requested_resources 是 daemon 解析後的集合,包含被選中的相依,並排除 由 groups 篩掉的 resources。
  • resources 帶有該集合觀察到的最終狀態。
  • degraded_resources 與 timed_out_resources 直接指出未成功的結果,不再 把 container 稱為 service。
  • environment_changes 只在啟動前發生 config handoff 時出現,包含 previously_running、restored_resources、started_dependencies 與 unavailable_resources。

指令只會等待這個集合進入 terminal state。Status 與 inspect 同樣把混合的 host processes 與 containers 放在 resources;log payload 與 NDJSON event 則使用 resource 標示來源。

command 找到最近的 project orbit.yaml 時,status 會把這份實際啟用的 config 回報為 data.environment.source: "project",selected_path 則指向找到的 檔案。這個 project context 優先於其他位置選過的 managed environment。 Agent 應依回傳的 source 與 path 判斷,不要從 ~/.orbit/envs 猜測 active config。

若另一個 project-local environment 正在執行,status 仍會成功,而且只描述 目前專案設定的 resource。它會設定 data.environment.context_switch_required: true、回報 running_name 與 running_path,並只建議 orbit up --json。Doctor 會驗證目前專案,且把執行中 專案占用的 port 視為切換時可釋放。可能誤控另一個專案 resource 的操作指令會 以 project_context_inactive 失敗;agent 應遵循唯一的 orbit up --json action。切換成功後,up 結果的 data.context_switch 會包含兩邊的專案名稱與 啟動前停止的 resource。orbit up -c <config> 也以同樣方式切換明確指定的 config,確認後的 action 會保留 -c;其他指令遇到不同 config 時仍以 env_mismatch fail closed,其 next_command 是 orbit up -c <config> --json。 Inspect 遵守同一個 ownership boundary,不會把另一個專案的 resource 或 dashboard 回報成目前專案所有。

第一次 setup 前,status 仍是成功的狀態查詢,但會回傳 data.setup_required: true、可讀的 setup_message,以及唯一的 orbit init --yes --json action。若 environment 檔案存在但內容無效,則回傳 invalid_environment error,因為 Orbit 無法安全使用它啟動。 若原先選取的 environment 已被改名或移除,status 會改回傳 data.selection_required: true 與 selection_message,並在 recommended_actions 中提供現存 environment 的精確 orbit switch <env> --json 指令;這不代表需要重新 init。

orbit init --yes --json 不會把 current directory 猜成 source workspace。自給自足的 environment 會省略此欄位。若自訂 environment 需要 ${WORKSPACE_ROOT},但沒有可證明的 local workspace, init 會回傳 service_working_directory_missing,並說明需移除 source,然後用 --workspace "$PWD" 重新新增。 其他未解析的 path variable 會保留自己的名稱,並指向 orbit settings set-env <NAME> "$PWD" --json;不會產生 source workspace 提示。

當 GitHub 回覆 environment repo 不存在時,Orbit 會回傳 env_repo_unavailable,且不提供 recommended actions。GitHub 對拼錯/不存在 的 repo,以及目前 credentials 看不到的 private repo,刻意使用相同回覆; 因此 agent 不應自動登入或用原指令盲目重試。應先核對 owner 與 repo 名稱, 確認 URL 正確且 repo 為 private 後才進行 Git 認證。能明確判定的認證失敗仍 使用 env_repo_access。

已轉換控制指令的穩定 data.operation 值:

穩定的 data.operation 值只維護一份完整清單,見英文版 agent-cli.md。

settings list 的 map-field wire shape 只維護在英文版 Settings map fields。

switch 會先停掉正在運行的環境,所以在有資源運行且未加 --yes 時會回傳 confirmation_required 而不動手——切換環境是機器層級的 provisioning 步驟, 不該由 harness 隱式觸發。對 switch 而言,previous_environment_stopped 表示 Orbit 是否在選取前 停止了原本執行中的 environment。Orbit 停止時不會只為了記錄 selection 而被啟動;orbit up 仍是唯一啟動動作。新 env 缺少必要 runtime 或 package installation 時,prerequisites_ready 會是 false;prerequisites 使用與 Doctor 相同的 checks,而 Orbit 能判斷修復方式時,recommended_actions 會包含可直接執行的指令。

Host service path 尚未解析或不存在時,會使用穩定的 service_working_directory_missing error code。switch 會讓 daemon 保持 停止,Doctor 或 up 會回傳精確的 workspace 設定或 config 修改 action。 執行該 action 後再重試;此時 up 尚未啟動任何相依資源。

當先前已有 daemon 在跑時,daemon stop/restart payload 可能包含 stop_method。穩定值為 graceful、terminated、killed。

Legacy JSON Commands ​

部分指令早於 envelope 存在,為了相容性會刻意保留既有的 JSON 形狀:

Command行為
orbit daemon status --json回傳 legacy daemon status 物件。
orbit history --json保持既有的 history payload。
orbit history gaps --json保持既有的 history gaps payload。
orbit tunnel claim --json串流 Tunlease 形狀的 NDJSON 事件(schema_version: 1、每事件一個 type)——它是即時串流,不是請求-回應。list/release 的上游事件形狀仍可用 -o json 取得。

Open ​

orbit open [resource] --json 會開啟目標並回傳一個成功 envelope。Payload 包含 url、opened,以及值為 dashboard、service 或 container 的 target。指定資源時,resource 會包含其名稱。為了向後相容,service target 也會在 service 保留相同名稱;container target 不使用這個 legacy 欄位。

Passthrough Commands ​

以下指令包裝互動式或外部程式,所以(全域的)--json 會被接受但沒有效果—— 輸出就是被包裝工具的原始串流:

orbit exec、orbit query redis|mongo|postgres、orbit topics *、 orbit sqlserver query。orbit seed 成功時印人類可讀進度; 只有失敗會用錯誤 envelope。

不要預設每一個 --json 指令都有 envelope。解析前請先確認該指令各自的 contract。

Logs ​

非串流的 logs 回傳單一 envelope:

bash
orbit logs redis --json

成功時的 payload 包含 service 名稱、請求的行數、一個非 null 的 lines array,以及輸出是否被截斷。

串流的 logs 使用 newline-delimited JSON:

bash
orbit logs redis -f --json

每一行都是一個完整的 JSON 物件。Agent 應當作 NDJSON 解析,而不是一整個 JSON array。

如果 followed log stream 在 NDJSON 輸出開始後失敗,Orbit 會輸出最後一筆 type: "error" 的 NDJSON event,而不是追加另一種格式的 envelope,然後以非 0 exit code 結束。

Traces ​

Tracing 預設開啟;只有在使用中的 env 明確設 tracing.enabled: false 時才關閉 (見 tracing.zh-TW.md)。用 orbit tracing status --json 查 receiver 的即時狀態。

bash
orbit trace --json             # 近期 trace;--limit N 控制筆數
orbit trace <trace-id> --json  # 一條完整 trace 與其 span tree
orbit trace -f --json          # NDJSON 串流 trace-summary 更新

trace summary 帶有 traceId、rootService、rootName、startUnixNano、 durationMs、spanCount、services(去重、首見順序)與 status (ok | error)。完整 trace 多了 spans,每個 span 有 traceId/spanId/parentId、service、name、kind、 startUnixNano/endUnixNano/durationMs、status/statusMsg 與 attributes。這些欄位名由 contract test(app/traces_json_test.go) 釘住 —— 改名視同 breaking change。

-f --json 模式下,同一個 traceId 會隨 span 累積而重複發出;後到的 event 取代先前的。

典型 agent 工作流:orbit trace --json → 挑出 errored trace → orbit trace <id> --json 找失敗的 span → 該 trace 的 log 行走 daemon 的 GET /api/traces/{id}/logs,或人類可讀輸出用 orbit trace <id> --logs。

Inspect ​

orbit inspect --json 是 agents 理解 Orbit 目前是否適合自動化操作的建議第一步。 它回傳一般 orbit.cli.v1 envelope,且 data.schema_version 是 orbit.inspect.v2。

Inspect payload 包含:

Field意義
readiness穩定的決策狀態,包含 state、blocked、summary。
daemondaemon 是否執行、PID、版本、更新資訊,以及可用時的 dashboard URL。
environment與 status、env list 共用 state、selected_name、selected_path、environments selection object,另包含可用時的 preview/daemon 資訊。
resourcesrunning daemon 的 resource states 摘要。它不列舉 declared configuration;後者請使用 orbit env info --json。
risks排序過的 machine-readable risks,例如 setup_required、environment_selection_required、orbit_update_pending、config_invalid、environment_stopped、env_mismatch、status_unavailable、dependency_readiness_ambiguous、resource_degraded、resource_converging、resource_stopped。Dependency-readiness risk 是 advisory config evidence,因此可以與 blocking lifecycle risk 同時出現。
recommended_actionsagent 應考慮的安全下一步指令。

穩定的 readiness.state 值:

StateBlocked意義
setup_requiredtrue尚未選到可用 environment;唯一下一步是 orbit init --yes --json。
selection_requiredtrue先前 selection 已失效;actions 會提供精確的 orbit switch <source>/<env> --json 選項,沒有候選時則提供 orbit source sync --json。
config_invalidtrue選到的 config 無法載入。較舊的共用 schema 唯一 action 是 orbit source sync --json;較新的 schema 唯一 action 是 orbit update --json。語法錯誤與未知欄位需要編輯回報的檔案,因此 Orbit 不會回傳沒有推進效果的 inspect 自我重試。
update_requiredtrue新版 Orbit binary 已安裝,但 daemon 仍執行舊版本;唯一 action 是 orbit daemon restart --json。
stoppedtrue已設定 environment 但尚未執行;configured resources 會列為 stopped,唯一 action 是 orbit up --json;若 config 是用 -c 指定,則是 orbit up -c <config> --json(設定 --instance 時都會帶上)。
needs_daemontruerunning daemon 使用的 env 與選取的 config 不同。
degradedfalse至少一個 resource 回報 degraded。
convergingfalse至少一個 resource 是 pending、starting、building、stopping 或 restarting。
partialfalse至少一個 resource 停止,且沒有更高優先級的風險。
readyfalseinspect 沒有偵測到風險。

對於有 buffered output 的 terminal degraded resource,inspect、status 與 doctor 只會引導至 orbit logs <resource> --json。logs 回傳 exit output 後會重新檢查即時狀態。若支援的 project dependency check 失敗, 唯一 action 會先安裝已宣告 dependencies,再只 restart 該 resource;否則 唯一 action 才是 orbit restart <resource> --json。Agent 應依序執行,不要 直接跳過 log;若期間出現新的 port、dependency 或 runtime 原因,Orbit 會用更安全的 cause-specific action 取代 blind restart。

當 daemon resource status 暫時不可用時,也可能回傳 converging。這種情況會用 status_unavailable risk 說明原因。

Agents 應把 blocked: true 視為需要先恢復的狀態。Agents 可以直接執行 non-destructive 的 recommended_actions,但任何 destructive: true 的 action 仍必須先取得明確 human approval。

readiness 是 needs_daemon 時,agents 應先執行建議的切換 action,再依據 service state 行動,因為 running environment 與選取的 CLI config 不同。 不在已偵測到的專案中時,這個 action 是 orbit up -c <config> --json:up 只會 切換到明確指定的 config,不會改變 selection,並像專案切換一樣先確認。

Readiness 是 update_required 時,agents 必須先 restart Orbit 才能送出 resource mutation。這些 mutation 會回傳 orbit_update_pending,並且只建議 orbit daemon restart --json。

本文件負責 JSON contract,不定義 agent decision policy。與版本配對的 Orbit skill 是 inspect、action、 verification、failure recovery、instance targeting 與 destructive operation workflow 的唯一來源。JSON response 帶有 recommended_actions 時,agent 應先 遵循它們,再退回臨時 debug。

Exit Codes ​

Code意義
0成功。
1錯誤。在 --json 模式下,已轉換的指令仍會輸出結構化 JSON。

inspect blocked readiness 與 exit status 的精確關係只維護在英文版 Exit Codes。