Agent CLI Contract
Coding agent 或 script 可透過 Orbit CLI 的結構化輸出檢查 runtime 狀態、 處理錯誤。這份契約涵蓋 JSON 回應、log stream、錯誤代碼與建議動作。
自動更新的 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:
orbit status --json
orbit doctor --json
orbit up --infra --json
orbit logs redis --json給人看的輸出是為 terminal 最佳化的,未來可能變動。JSON 輸出才是 agent 所依賴的 contract。
JSON Envelope
已轉換到 agent contract 的指令會回傳一個 JSON 物件,最上層結構如下:
{
"schema_version": "orbit.cli.v1",
"ok": true,
"command": "doctor",
"instance": "ci-123",
"data": {},
"error": null,
"notices": [],
"recommended_actions": []
}欄位說明:
| Field | 意義 |
|---|---|
schema_version | Contract 版本,目前為 orbit.cli.v1。 |
ok | 成功為 true,失敗為 false。 |
command | 產生這個 response 的 Orbit 指令。URL 參數中的 credentials(userinfo 與 query string)會被移除,與訊息和 recommended actions 相同。SSH URL(ssh://、git+ssh://、ssh+git://)保留 username,因為它指定登入帳號而非機密;password 仍會被移除。 |
instance | --instance 指定的 named runtime;default runtime 會省略。 |
data | 成功時的 command-specific payload。 |
error | 失敗時的結構化 error payload,否則為 null。 |
notices | 不會使指令失敗的結構化事件,例如一次性的舊 source 遷移。 |
recommended_actions | Agent 應考慮的後續指令。 |
所有 lifecycle commands 都接受 --instance <name>。使用 orbit instance list --json 取得各 instance 的 environment、daemon state、dashboard 與解析後的 resource endpoints。orbit instance clean <name> --json 只會停止並移除帶有 該 instance ownership 的 resources。其 data 為 {instance, removed};若有 保留執行中的已記錄 PIDs(無法證明 ownership 或停止失敗),另含 skipped_processes[](service、pid、reason)。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
結構化錯誤使用以下形狀:
{
"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。
停在 starting 的 resource 會回報它停在哪裡。starting_phase 為 launching(port 檢查、pre_start、啟動 process 或 container)或 probing (等待 readiness)。受監管的 host process 會帶 pid;執行中的 container 會帶解析後的 image,有 sidecar 時另帶 sidecars({name, url})。http、tcp、exec 或 healthcheck check 的 probe 一旦開始(log check 沒有 probe), health_progress 就帶 attempts、max_retries、last_err、recovering、 probe_started_at(只在 probe 進行中出現)、last_probe_at(最近一次 probe 返回的時間)與 protocol(回應上一次 cleartext HTTP probe 的 h2c 或 HTTP/1.x 版本,例如 HTTP/1.1)。它描述最近一次執行,所以在已停止的 resource 上只是歷史。仍是 starting 時合起來判讀:
probe_started_at一直變舊:probe 本身沒有返回。- 沒有進行中的 probe、
last_err為空,且last_probe_at不再前進:probe 已通過,但結果的傳遞卡住了。請取得 goroutine dump。 - 同上,但
last_probe_at持續前進:probe 已通過、monitoring 在跑,但healthy狀態轉換被丟掉了。 probing時兩個時間欄位都沒有:還沒有任何 probe 開始。
要在不停止 daemon 的情況下取得它的 goroutine stack,對 daemon pid(orbit inspect --json 的 data.daemon.pid)送 SIGUSR1。dump 會附加到 daemon.log。Windows 不支援。
需要明確授權的破壞性步驟使用穩定的 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。有 resource 正在執行時,非互動或 JSON 模式的切換若沒帶 --yes,會以 confirmation_required 失敗,並附上確切的 orbit up --yes --json action;目標會在這個確認之前先驗證,拒絕時不會停止任何 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。
orbit source add 首次同步失敗時不會登記任何 source,因此它的 env_repo_access recovery 是重試 orbit source add <name> --url <url> --json, 而不是 orbit source sync --json。URL 中的 credentials 會從重試指令與訊息中移除; GitHub 認證與安裝 Git 的步驟維持不變。 orbit init 也只在新 source 首次同步成功後才登記它,因此以 flags 指定的 source 首次同步失敗時(env_repo_access 或 init_incomplete),會以該 source 重試整個 init:orbit init --yes --source <name> --url <url> --json(或 --path,並帶上原本的 --ref、--workspace 與 --env)。單純的 orbit init 則照原樣重試。
已轉換控制指令的穩定 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。
Daemon Status 與 History
orbit daemon status --json 回傳 data.running,daemon 執行中時另附 daemon 細節。Daemon 已停止時仍是成功 envelope,值為 running: false。
orbit history --json 回傳 data.records,orbit history gaps --json 回傳 data.gaps。兩者都是陣列,沒有資料時為空陣列而非 null,項目沿用 dashboard 讀取的 camelCase 欄位。
串流例外
只有一個 --json 指令是即時串流而非 envelope:
| Command | 行為 |
|---|---|
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 會包含其名稱。
Passthrough Commands
以下指令包裝互動式或外部程式,所以(全域的)--json 會被接受但沒有效果—— 輸出就是被包裝工具的原始串流:
orbit exec、orbit query redis|mongo|postgres、orbit topics *、 orbit sqlserver query。orbit seed 成功時印人類可讀進度; 只有失敗會用錯誤 envelope。
不要預設每一個 --json 指令都有 envelope。解析前請先確認該指令各自的 contract。
Daemon HTTP API 屬於 Orbit CLI 與 dashboard 的內部通道,不是相容性契約 (見 versioning);請改為解析 CLI 的 --json 輸出。
Logs
非串流的 logs 回傳單一 envelope:
orbit logs redis --json成功時的 payload 包含 service 名稱、請求的行數、一個非 null 的 lines array,以及輸出是否被截斷。
串流的 logs 使用 newline-delimited JSON:
orbit logs redis -f --json每一行都是一個完整的 JSON 物件。Agent 應當作 NDJSON 解析,而不是一整個 JSON array。串流先以一般的 log event 輸出最後 --lines(或 --tail)行,預設 100 行,接著追蹤新行;backlog 與新行之間不會遺漏或重複;讀取端落後太多時,仍可能漏掉新行。--lines 0 只串流新行。
如果 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 的即時狀態。
orbit trace --json # 近期 trace;--limit N 控制筆數
orbit trace --search item/1 --json # span 名稱或 attribute 值含該文字的 trace
orbit trace --service api --json # 經過該 service 的 trace
orbit trace <trace-id> --json # 一條完整 trace 與其 span tree
orbit trace -f --json # NDJSON 串流 trace-summary 更新Orbit 自己 health probe 的 trace 預設隱藏(除非出錯,見 tracing.zh-TW.md);--include-health 會顯示它們。data.hiddenHealthTraces 是被隱藏的相符 trace 數。-f 模式下 --search 只比對 rootService、rootName 與 services,因為 summary 不帶 span;root span 尚未抵達前發出的 summary 也還 認不出是 health 流量。
trace summary 帶有 traceId、rootService、rootName、startUnixNano、 durationMs、spanCount、services(去重、首見順序)、status (ok | error),health-probe 流量另帶 health: true(否則省略)。完整 trace 多了 spans,每個 span 有 traceId/spanId/parentId、service、name、kind、 startUnixNano/endUnixNano/durationMs、status/statusMsg 與 attributes。這些欄位名由 contract test(internal/app/traces_json_test.go) 釘住 —— 改名視同 breaking change。
-f --json 模式下,同一個 traceId 會隨 span 累積而重複發出;後到的 event 取代先前的。
典型 agent 工作流:orbit trace --search <路徑或文字> --json(或 orbit trace --json → 挑出 errored trace)→ orbit trace <id> --logs --json 找失敗的 span,並取得帶有該 trace id 的 OTLP log record(data.logs),以及提到該 trace 的 stdout log 行 (data.stdout_lines,每筆為 {service, line})。OTLP log record 只存在於有匯出 log 的服務;stdout 行則是在各服務仍保留在 buffer 的輸出中,以 trace id 比對而得。 任一份讀取失敗時,該清單為 [],notices 會有 log_records_unavailable 或 stdout_lines_unavailable。
Log records
服務的 OpenTelemetry exporter 也送 log 時,結構化 log record 會進到同一個 receiver,用 orbit trace logs --json 讀取;orbit logs <resource> 仍然讀該 resource 的 stdout。旗標、data.logs 欄位與 --since cursor 規則只維護一份, 見英文版 agent-cli.md 的 Log records。
Inspect
orbit inspect --json 是 agents 理解 Orbit 目前是否適合自動化操作的建議第一步。 它回傳一般 orbit.cli.v1 envelope,且 data.schema_version 是 orbit.inspect.v2。
Inspect payload 包含:
| Field | 意義 |
|---|---|
readiness | 穩定的決策狀態,包含 state、blocked、summary。 |
daemon | daemon 是否執行、PID、版本、更新資訊,以及可用時的 dashboard URL。 |
environment | 與 status、env list 共用 state、selected_name、selected_path、environments selection object,另包含可用時的 preview/daemon 資訊。 |
resources | running 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_actions | agent 應考慮的安全下一步指令。 |
穩定的 readiness.state 值:
| State | Blocked | 意義 |
|---|---|---|
setup_required | true | 尚未選到可用 environment;唯一下一步是 orbit init --yes --json。 |
selection_required | true | 先前 selection 已失效;actions 會提供精確的 orbit switch <source>/<env> --json 選項,沒有候選時則提供 orbit source sync --json。 |
config_invalid | true | 選到的 config 無法載入。較舊的共用 schema 唯一 action 是 orbit source sync --json;較新的 schema 唯一 action 是 orbit update --json。語法錯誤與未知欄位需要編輯回報的檔案,因此 Orbit 不會回傳沒有推進效果的 inspect 自我重試。 |
update_required | true | 新版 Orbit binary 已安裝,但 daemon 仍執行舊版本;唯一 action 是 orbit daemon restart --json。 |
stopped | true | 已設定 environment 但尚未執行;configured resources 會列為 stopped,唯一 action 是 orbit up --json;若 config 是用 -c 指定,則是 orbit up -c <config> --json(設定 --instance 時都會帶上)。 |
needs_daemon | true | running daemon 使用的 env 與選取的 config 不同。 |
degraded | false | 至少一個 resource 回報 degraded。 |
converging | false | 至少一個 resource 是 pending、starting、building、stopping 或 restarting。 |
partial | false | 至少一個 resource 停止,且沒有更高優先級的風險。 |
ready | false | inspect 沒有偵測到風險。 |
對於有 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。
Recommended Agent Workflow
本文件負責 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。