Terminal API

App 1.22.0 的本機 HTTP 介面:終端、agent session、版面、keep-alive、通知、service token 與 Webhook。

設定與安全

在設定 > 遠端控制 > Terminal API 啟用服務並產生 token。伺服器綁定 127.0.0.1,正式版預設埠為 3037,開發版為 3036。一般 API 預設停用,請保持本機連線,不要對公網公開。

Authorization: Bearer $TERMINAL_API_TOKEN
Content-Type: application/json

JSON 成功回應為 { success: true, data: ... };失敗回應為 { success: false, error: { code, message, reason?, details? } },請依 error.code 判斷。一般限額為每個 IP/method/path 每 60 秒 120 次,超過回傳 429 RATE_LIMITED。Bootstrap challenge 與 Webhook 入口的驗證例外見下文。

工作區與 session

GET /api/terminal/workspaces

回傳 data.workspaces,包含 id、name、rootPath。

{ "success": true, "data": { "workspaces": [
  { "id": "ws_123", "name": "my-project", "rootPath": "/path/to/project" }
] } }

POST /api/terminal/sessions

必填 workspaceId。選填 name、cols(80–500,預設 80)、rows(24–100,預設 24)、logPolicy: "persist"、background(預設 false)、paneId、stealFocus。回傳 data.session,包含 id、套用後的 name、workspaceId、status、isActive、background。新 session 從工作區根目錄開始。

Agent 自動化請設 background: true,避免占用分頁或窗格。前景 session 可指定 paneId 並設 stealFocus: false;放置選項不能與 background: true 同用,否則回傳 INVALID_PLACEMENT。

GET /api/terminal/sessions

可用 query workspaceId 篩選,回傳 data.sessions。終端 session 沒有閒置 TTL,會在刪除、PTY 結束、app 關閉或 worker 崩潰清理時結束。

PATCH /api/terminal/sessions/:id

Body: { name }。回傳 data.sessionId 與 data.name;名稱衝突會加上 -2、-3 等後綴,請使用回傳名稱。名稱上限 200 字元。

Session 路徑可用 id 或唯一分頁名稱,名稱須做 URL 編碼。id 優先於同名分頁;找不到或有多個同名分頁時回傳 SESSION_NOT_FOUND,解析程序失敗則回傳 SESSION_RESOLVE_FAILED。需要跨重新命名保持目標不變時,請使用 id。SSE 在訂閱時解析一次。

GET /api/terminal/sessions/:id/status

包含 activity、readiness、hasPrompt、waitingForInput 與 prompt.kind(shell、confirm、select、password、open)。傳送下一個指令前檢查 readiness === "idle",單看 activity 不足以判斷。

GET /api/terminal/sessions/:id/log

適用於建立時設 logPolicy: "persist" 的 session。data 包含 sessionId、logPath、content;記錄檔可在 session 結束後保留。

GET /api/terminal/sessions/:id/ports

回傳 data.sessionId 與 data.ports: [{ pid, port, protocol, address }]。

DELETE /api/terminal/sessions/:id

刪除終端 session 並釋放資源。

輸入

POST /api/terminal/sessions/:id/input

必填 JSON 字串 data,或傳送 text/plain body。JSON 或 query 可帶 appendEnter、submitKey、queueUntilReady、readyTimeoutMs、agentSessionId。submitKey 優先於 appendEnter,鍵名與 /keys 相同;文字與送出鍵分開寫入。

{ "data": "npm test", "appendEnter": true, "queueUntilReady": true, "readyTimeoutMs": 30000 }

queueUntilReady 等待 readiness 為 "idle"。等待逾時回傳 TERMINAL_NOT_READY,不寫入資料。無效 data 回傳 400 INVALID_INPUT_DATA;無效 submitKey 也不寫入。進入輸入處理後,成功回傳 data.requestId,失敗回傳 error.details.requestId;在進入處理前就被拒絕的請求不含此欄位。

POST /api/terminal/sessions/:id/submit

JSON { data, settleMs?, queueUntilReady?, readyTimeoutMs?, agentSessionId? }。用於已進入互動提示的 session,無須在每個 shell 指令後呼叫。

POST /api/terminal/sessions/:id/keys

JSON { key }。支援 enter、up、down、left、right、tab、esc、ctrl+c、ctrl+d。

POST /api/terminal/sessions/:id/interrupt

送出 Ctrl+C,保留 session。

遠端 peer 持有的 session 會拒絕 /input、/submit、/keys、/interrupt,回傳 409 SESSION_HELD_BY_PEER。請先在本機接手,不要重送相同的被拒請求。

輸出與 SSE

GET /api/terminal/sessions/:id/output

Query: mode、lines、since、chromeProfile、ifHash、source、waitForChange、timeoutMs。text 移除 ANSI 並保留可見文字;raw 保留 ANSI;content 盡可能過濾 TUI 裝飾;screen 讀取目前畫面,不讀歷史。

回應包含 data.sessionId、mode、lines、cursor、nextCursor、hasMore、truncated、windowStart、windowEnd、lineCount。增量讀取使用前次 nextCursor 作為 since。screen 不支援 since,優先讀 headless 狀態,DOM 備援需要可見窗格;source=headless 或 source=dom 可指定讀取路徑。content 的 chromeProfile 支援 claude-code、default、none。

Screen 回應包含 outputHash、unchanged。下次讀取帶 ifHash,hash 相同時回傳 unchanged: true 與空 lines。waitForChange=true 啟用長輪詢,data.wait 包含 changed、timedOut 與選填 reason: "LONGPOLL_TIMEOUT"。

GET /api/terminal/sessions/:id/events

SSE frame 包含 TerminalSessionOutputEvent JSON: { type: "output.new", sessionId, timestamp, payload },payload 與 /output 相同。Query: mode=raw|text|content、since、lines(限制在 1–500)、chromeProfile、timeoutMs(至少 1000 ms)。screen 回傳 400 INVALID_TERMINAL_SSE_QUERY。未帶 since 時從目前尾端開始。

終端 SSE 不重播保留的歷史。需要補讀時先呼叫 /output?since=... 再訂閱,之後以 payload.nextCursor 推進游標。讀取太慢可能被斷線。

版面

GET /api/terminal/layout

回傳 data.layout。?full=true 包含 contentType/contentId、窗格尺寸與 activePanelId;預設精簡格式包含 id、terminalId。

POST /api/terminal/layout

JSON { type, sessionIds? },例如 { "type": "horizontal-2", "sessionIds": ["build", "test"] }。

POST /api/terminal/layout/panes/:paneId/assign

JSON { sessionId },null 清空窗格。回傳 data.paneId 與解析後的 data.terminalId。

POST /api/terminal/layout/panes/:paneId/activate

聚焦窗格。

POST /api/terminal/layout/panels/:panelId/activate

啟用分頁層的 panel,可包含未綁定窗格的分頁。

POST /api/terminal/layout/restore

Body 使用 ?full=true 的 data.layout 快照。無法放置的內容列於 restored.skipped,有未解析目標時列於 restored.unresolvedTargets。精簡快照會讓窗格留空並回報 SLIM_PANE。

curl -s -H "Authorization: Bearer $TOKEN" \
  "http://127.0.0.1:3037/api/terminal/layout?full=true" | jq .data.layout > layout.json
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d @layout.json \
  http://127.0.0.1:3037/api/terminal/layout/restore

Keep-alive 與通知

GET /api/terminal/sessions/:id/keepalive

列出規則。

PUT /api/terminal/sessions/:id/keepalive

JSON { rule: { id, enabled, schedule, message, promptRef? } },建立或取代 rule.id。Schedule: interval/intervalMs、idle/thresholdMs、daily/time(HH:mm)、once/atMs。Idle 規則限本機 session。

DELETE /api/terminal/sessions/:id/keepalive/:ruleId

移除規則。三種 keep-alive 操作都回傳更新後的規則清單,包含 nextFireAt,停用時為 null。

{ "rule": { "id": "review", "enabled": true,
  "schedule": { "kind": "interval", "intervalMs": 1800000 },
  "message": "continue" } }

每個 session 最多 10 條啟用規則。若要每次觸發讀取 Prompt Library,設定 message: "" 與 promptRef: { slug: "review", scope: "project" },也可用 scope: "global"。參照與內嵌文字互斥,prompt 不可含變數。專案參照取自目標的本機工作區。無效 prompt 會略過該次觸發,session 刪除時移除規則。

POST /api/terminal/notify

JSON { message, sessionId?, title? }。須啟用遠端控制、推送通知並有已連線 provider。message 修剪空白後為 1–4000 字元;title 為 1–100 字元,僅用於首次建立 Discord 討論串。每個 session 每分鐘最多 6 則。

有 sessionId 且 Discord 支援 session 討論串時,訊息選擇 Discord;否則使用已連線的遠端 provider。未指定 session 時送至已連線的遠端平台。成功代表至少一個 provider 接受傳送;未送達回傳非 2xx 回應。

Agent sessions

此介面管理 provider session 與結構化對話記錄,與原始終端 session 分開。Provider: claude、codex、agy、duo、opencode;gemini 是 agy 的輸入別名,新回應使用 agy。

POST /api/agent-sessions

JSON { provider, cwd, prompt, env?, settingSources? }。回傳 data.session,識別碼是 sessionId。env 依 provider 過濾,互動式 Claude 不套用每次請求的 env。憑證請在 app 啟動前設定於 shell 環境。settingSources 會儲存,但不傳給 CLI。

GET /api/agent-sessions

選填 query cwd,回傳 data.sessions。

POST /api/agent-sessions/resolve

JSON { cwd, shellPid? },回傳 data.target,供連回既有 provider session。

POST /api/agent-sessions/attach

JSON { provider, sessionId, cwd },回傳 data.session。Attach 接受 Termdock 執行個體的 sessionId 或 provider 對話 id(metadata.aiSessionId)。後續操作使用回傳的執行個體 sessionId。

GET /api/agent-sessions/:id

回傳 data.status,包含 session(內含 session.lifetime)、dispatch、bufferedEventCount、lastEventSequence、hasRenderedView、renderedEntryCount、lastRenderedSequence 與 canRestart。

POST /api/agent-sessions/:id/input

JSON { input, env? }。Provider 傳送後回傳 data.sessionId。Dispatch 逾時可能無法確定是否送達,重送前請先核對。

POST /api/agent-sessions/:id/interrupt

Provider 支援時中斷目前回合,回傳 data.sessionId。

POST /api/agent-sessions/:id/restart

使用快取的建立參數重啟,回傳 data.session;參數已不可用時回傳 AGENT_SESSION_RESTART_UNAVAILABLE。

DELETE /api/agent-sessions/:id

停止 agent 並回傳 data.sessionId;保留事件可持續到 session.lifetime.evictsAt。

GET /api/agent-sessions/:id/events

SSE 傳送生命週期、助理、工具、結果與錯誤事件。Query since 是不含該筆的序號游標。最多保留最新 200 筆,發現缺口時從 /rendered 重新同步。

GET /api/agent-sessions/:id/rendered

回傳 data.view,這是對話記錄快照,不是終端截圖。

GET /api/agent-sessions/:id/rendered/stream

SSE 傳送對話快照與更新。快照有容量限制,應用來替換目前檢視,不可當作只追加的歷史。

POST /api/agent-sessions/hooks

接收 Workstream 的 provider hook。各 provider 的 payload 與回覆請優先透過 CLI hooks setup、hook ingest 處理。

POST /api/agent-sessions/:id/callbacks

必填 source、eventKind、message;選填 dedupeKey 與 JSON object metadata。上限:source/eventKind 256 字元、dedupeKey 512、message 32768。拒絕未知欄位;回傳 data { sessionId, deliveryId, createdAt }。

Callback 目標可用既有 AgentSession id,或其終端 id/唯一名稱。不會建立或附加 agent,也不用來回答權限提示。Key 不會自動去除重複事件;metadata 由呼叫端提供,不是經驗證的身分。成功只確認 provider 傳送,不代表任務完成。

Service tokens

UI token 可存取完整 API;service token 可限制為 terminal 與/或 agent-session,省略 scopes 時包含兩者。一般 API 停用時仍可做 bootstrap provisioning,但終端與 agent 端點回傳 403 DISABLED。

POST /api/auth/bootstrap-challenge

不需 Bearer token。JSON { nonce },長度 32–256 字元。回傳 data.proof: hex HMAC-SHA256(bootstrapSecret, "<nonce>|<port>")。送出 secret 前,先在本機以常數時間比較驗證,proof 缺少或不符時停止 provisioning。

POST /api/auth/service-tokens

JSON { name, scopes? }。使用 UI token,或從 ~/.termdock/config/terminal-api-bootstrap.json 取得 X-Termdock-Bootstrap-Token。回傳 data.serviceToken,token 僅在建立時提供,請妥善保存。

POST /api/auth/service-tokens/:id/rotate

JSON { graceSeconds? }。Service token 只能輪替自己;UI token 可管理 token。舊 token 到期時間為 previousTokenExpiresAt。

DELETE /api/auth/service-tokens/:id

使用 UI token 或該 service token 本身撤銷。

Webhook 入口

POST /api/webhooks/:id/:secret

在設定 > 自動化 > Webhooks 選擇本機工作區與分頁名稱並建立網址。不需 Bearer token,一般 API 停用時仍可用。JSON 必須恰好包含 { message, submit };message 為單行 1–4096 個 UTF-16 字元,不含 ASCII 控制字元,submit 為 boolean。Body 上限 8 KiB;每個 Webhook 每 60 秒最多 10 次。

網址只顯示一次,請視為密鑰。目標須為停在輸入框等待的 agent;忙碌、不存在、名稱不明確、非 agent 或無法讀取畫面時拒絕,不寫入也不建立 session。submit: true 同時送出 Enter。成功為 { success: true, data: { delivered: true } }。

外部傳送者須自行設定支援 TLS 的 tunnel 或反向代理,只公開 /api/webhooks/...,避免存取記錄洩漏密鑰。其他 Terminal API 路徑不要公開;可在設定撤銷網址。

自動化設定

範例:背景終端

BASE="http://127.0.0.1:3037"
AUTH="Authorization: Bearer $TERMINAL_API_TOKEN"
WS=$(curl -s -H "$AUTH" "$BASE/api/terminal/workspaces" | jq -r ".data.workspaces[0].id")
SID=$(curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
  -d "{\"workspaceId\":\"$WS\",\"background\":true}" \
  "$BASE/api/terminal/sessions" | jq -r ".data.session.id")
curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"data":"pwd","appendEnter":true,"queueUntilReady":true}' \
  "$BASE/api/terminal/sessions/$SID/input"
curl -s -H "$AUTH" "$BASE/api/terminal/sessions/$SID/output?mode=text&lines=20"
curl -s -X DELETE -H "$AUTH" "$BASE/api/terminal/sessions/$SID"
CLI 指令對照