@jiantw83/cliproxyworker (0.0.4)
Installation
@jiantw83:registry=https://gitea.jsc.idv.tw/api/packages/jiantw83/npm/npm install @jiantw83/cliproxyworker@0.0.4"@jiantw83/cliproxyworker": "0.0.4"About this package
CLIProxyWorker
CLIProxyWorker 是分散式派工系統的執行端:註冊到
CLIProxy 後,以常駐程序定時上報系統資訊(核心/記憶體
/硬碟/網路/處理程序)與 CLI 工具狀態、socket 優先領取任務並在
socket 不可用時回退長輪詢,並實際
操控本機安裝的 AI Agent CLI 工具(antigravity/copilot/
codex/claude/kiro)執行安裝、解除安裝、啟用、停用、prompt
執行、互動式登入等操作。
系統需求
- Node.js ≥ 20.11.0
- Linux(採集器讀取
/proc、statfs;互動式連線操控用到系統的script(1)指令,util-linux 套件通常內建) - 要操控哪個 CLI 工具就要先在本機安裝好該工具本身(
antigravity/copilot/codex/claude/kiro-cli),CLIProxyWorker 只負責 偵測與操控,不含這些工具本身
安裝
CLIProxyWorker 發布在本組織 Gitea 的 npm package registry(非公開的 npmjs.com),安裝前要先讓 npm 知道去哪裡拉套件、並帶上有讀取權限的 Gitea token。
方式一:npm 安裝(推薦)
-
設定 registry 與認證(擇一即可,二選一設定會沿用到後續所有
npm install -g/npm update -g):- 全域設定(寫進
~/.npmrc,之後每次安裝都不用再帶):npm config set @jiantw83:registry https://gitea.jsc.idv.tw/api/packages/jiantw83/npm/ npm config set //gitea.jsc.idv.tw/api/packages/jiantw83/npm/:_authToken <你的 Gitea token> - 或單次安裝時用參數帶入(不寫進任何設定檔):
npm install -g @jiantw83/cliproxyworker \ --@jiantw83:registry=https://gitea.jsc.idv.tw/api/packages/jiantw83/npm/ \ --//gitea.jsc.idv.tw/api/packages/jiantw83/npm/:_authToken=<你的 Gitea token>
- 全域設定(寫進
-
全域安裝:
npm install -g @jiantw83/cliproxyworker安裝完成後可直接用
cliproxyworker指令(scope 只影響套件名稱, 不影響指令名稱)。
Gitea token 需具備該 repo 的讀取權限,向管理員索取或在 Gitea
「設定 → 應用程式」自行建立(read:package 權限即可)。
方式二:從原始碼建置
git clone https://gitea.jsc.idv.tw/jiantw83/CLIProxyWorker.git
cd CLIProxyWorker
npm install
npm run build
npm run install:global # 全域安裝,之後可直接用 cliproxyworker 指令
註冊與啟動
- 向 CLIProxy 管理員取得一個一次性註冊 token(見 CLIProxy README 的「Worker 註冊流程」)。
- 註冊本機並產生設定檔:
設定檔會寫到
cliproxyworker register --server https://<CLIProxy 位址> --token <註冊 token>~/.config/cliproxyworker/config.json(權限 600, 內含workerId/workerToken,可用CLIPROXYWORKER_CONFIG_DIR環境變數改變路徑,方便測試或容器化部署)。 - 啟動常駐程序:
會開始定時心跳上報並長輪詢領取任務;建議搭配
cliproxyworker daemonsystemd/pm2等 常駐管理工具,確保程序異常結束後會自動重啟。 - 用
cliproxyworker status隨時檢查目前是否已註冊、註冊到哪台 CLIProxy。
更新
方式一:npm 更新
npm update -g @jiantw83/cliproxyworker
若要指定更新到某個版本,改用:
npm install -g @jiantw83/cliproxyworker@<版本號>
方式二:從原始碼更新
git pull
npm install
npm run build
npm run install:global # 重新全域安裝新版本
設定檔(workerId/workerToken)不受更新影響,不需重新註冊;
重新啟動 cliproxyworker daemon 即可套用新版本。
移除
方式一:npm 解除安裝
npm uninstall -g @jiantw83/cliproxyworker
rm -rf ~/.config/cliproxyworker # 移除設定檔(含 workerToken)
方式二:從原始碼安裝時的解除安裝
npm run uninstall:global
rm -rf ~/.config/cliproxyworker # 移除設定檔(含 workerToken)
移除前若還想讓 CLIProxy 端知道這台 Worker 已經下線,可以直接停掉
cliproxyworker daemon;CLIProxy 會依 WORKER_OFFLINE_MS(預設 5
分鐘沒收到心跳)自動判定離線,不需要額外操作。
環境變數
| 變數 | 預設值 | 說明 |
|---|---|---|
CLIPROXYWORKER_CONFIG_DIR |
~/.config/cliproxyworker |
設定檔(config.json)所在目錄,測試與容器化部署可覆蓋成別的路徑 |
HEARTBEAT_INTERVAL_MS |
30000 |
心跳上報間隔;需與 CLIProxy 端設定一致,否則 Q 因子的新鮮度判斷會失準 |
LOG_BUFFER_CAPACITY |
— | 心跳夾帶日誌的環狀緩衝區大小 |
TASK_CONCURRENCY |
4 |
常駐程序同時執行的任務數上限;任何型別的任務都計入,同一個 CLI 工具不另外限制;設為 0 代表不限制同時執行數 |
TASK_PREFETCH_MAX |
同 TASK_CONCURRENCY,若 TASK_CONCURRENCY=0 則為 4 |
一次長輪詢最多領幾筆任務;實際併發由 TASK_CONCURRENCY 的槽位數控制,長輪詢只是把「還有多少空位」預先領回來,實際上限仍受 CLIProxy 端 TASK_CLAIM_MAX_BATCH 夾限 |
TASK_FALLBACK_FAST_WAIT_SECONDS |
1 |
任務 socket 失敗或中斷後,第一輪長輪詢使用的短等待秒數,讓登入視窗與互動任務更快被 fallback 路徑領取 |
LOG_FLUSH_MAX_ENTRIES |
20 |
日誌即時上報:緩衝滿這個筆數就立即送出,不必等排程間隔 |
LOG_FLUSH_INTERVAL_MS |
3000 |
日誌即時上報:距上次上報超過這個時間就送出目前緩衝(即使未滿 LOG_FLUSH_MAX_ENTRIES) |
本專案不需要,也不會用到 DATABASE_PROVIDER/DATABASE_URL/Gitea
OAuth 相關設定——那些是 CLIProxy(管理端)的設定,CLIProxyWorker
只是單純的執行端,沒有自己的資料庫。
支援的任務類型
cli-install/cli-uninstall/cli-enable/cli-disable(CLI 工具
生命週期)、prompt(實際執行一次派工並回傳結果/耗時/token 用量)
、interactive-session(以 pty 建立互動式連線,用於需要互動的 OAuth
登入流程,本身有獨立逾時,不會讓沒人完成的登入流程無限占用名額)。
interactive-session 依請求的 purpose 決定實際帶給 tool.command
的參數:purpose === 'login' 且該工具的 auth.loginArgs 有設定時,
用 loginArgs(目前僅 kiro 設為 ['login'],其餘工具未設定即裸指令
就能進登入流程);其餘一律用 interactiveArgs ?? [](裸指令)。kiro
的一般互動終端因此預設直接進聊天 TUI,登入改由管理介面「登入」按鈕
帶 purpose=login 才會觸發 kiro-cli login。
antigravity/kiro 路徑回歸驗證
當 antigravity 或 kiro 已安裝,但後備安裝目錄未加入 PATH 時,Worker
仍必須使用 resolveCommandPath() 找到的絕對路徑執行登入與互動終端,
不得回到裸指令造成 command not found。
驗證清單:
- 確認工具已安裝在定義檔支援的後備路徑,例如
~/.local/bin/agy或~/.kiro/bin/kiro-cli。 - 以不含該後備目錄的
PATH啟動cliproxyworker daemon。 - 在 CLIProxy 管理介面對該 Worker 開啟 antigravity 或 kiro 的「登入」。
- Worker 日誌不得出現
command not found。 - 登入視窗應正常啟動該工具;kiro 登入應執行
kiro-cli login。 - 對同一工具再開一般互動終端,應同樣使用解析後的絕對路徑。
2026-08-21 驗證結果:
PATH=/usr/bin:/bin command -v agy與command -v kiro-cli皆找不到裸指令。PATH=/usr/bin:/bin /home/coder/.local/bin/agy --version成功,版本1.1.17。PATH=/usr/bin:/bin /home/coder/.local/bin/kiro-cli --version成功,版本2.16.2。interactive-session-handler.spec.ts已驗證 antigravity/kiro 登入會先解析成絕對 command path。prompt-exec.spec.ts與cli-maintenance.spec.ts已驗證 prompt、登出、更新與憑證檢查使用同一路徑解析語意。
CLI 工具模型清單如何取得
各 CLI 工具回報給 CLIProxy 的模型清單,除了 definitions.ts 內建的靜態
清單,有支援動態探索的工具(目前為 codex/kiro)還會依序嘗試對應來源,
合併去重後回報(src/tools/model-discovery.ts)。
codex 依序嘗試以下四個來源:
- 設定檔:解析
~/.codex/config.toml,抓出頂層與各[profiles.x]區段內的model = "..."。 codex doctor --json:遞迴走訪輸出 JSON,取出鍵名含model(排除provider/布林字串)的字串值。codex exec --help:抓取--model/-m附近的[possible values: ...]區塊(目前版本的 codex 抽不到,是為之後版本 預留)。codex completion bash:同上,從 shell 補全腳本輸出裡抓取。
kiro 只用一個來源:kiro-cli chat --list-models --format json,
解析回應的 models 陣列、取每筆的 model_id 欄位(parse: 'json-model-list',明確知道陣列與欄位位置,不像 codex 的
json-model-keys 是「抓鍵名含 model 的字串值」,誤抓風險較低)。
任一來源失敗(檔案不存在、子指令不存在、逾時、輸出格式不符)都只記
DBG 並跳過,不影響心跳;一旦探索找到至少一個真實模型,該工具標記
fallbackOnly: true 的佔位項目就會被排除,只在探索全數落空時才出現,
確保派工永遠至少有一個可用模型。
探索結果依「工具狀態雜湊」(state|version|authMethod)快取在 Worker
記憶體:狀態沒變就沿用快取,不會每次心跳(預設 30 秒一次)都重跑子
指令;狀態改變(重新登入、版本更新、行程重啟)或快取未命中時,才在
背景以 fire-and-forget 方式觸發重新探索,本次心跳先回靜態清單,下一次
心跳才帶出新結果。
codex/kiro 以外的其他工具(antigravity/copilot/claude)目前仍只用
definitions.ts 的靜態清單,未接上 modelDiscovery;ToolDefinition
已預留 modelDiscovery 欄位,之後要幫其他工具接上動態探索可直接沿用
同一套機制。
CLI 工具更新檢查
Worker 在採集 CLI 工具狀態時(隨每次心跳,預設 30 秒一次)會順便查詢
該工具目前是否有新版可用(src/tools/version-check.ts):
- 檢查時機:只在工具已安裝(
enabled/loggedIn)時觸發,未安裝 或已停用一律回latestVersion: null,不做多餘的查詢。 - 快取:查詢結果以
toolId為鍵快取在 Worker 記憶體,預設存活 6 小時(可用環境變數CLI_VERSION_CHECK_TTL_MS覆寫,單位毫秒), 過期才在背景以 fire-and-forget 方式重新查詢,本次心跳先用舊值, 不會阻塞心跳、也不會每次心跳都重跑子指令。 - 資料來源:安裝方式為
npm(ToolInstallDefinition.kind === 'npm')的工具(目前為 copilot/codex/claude)以npm view <套件名> version查詢 registry 上的最新版本,逾時上限 15 秒;套件名直接從installCommand的最後一個元素解析。 - 非 npm 工具不支援:安裝方式為
script(目前為 antigravity/ kiro)的工具一律回{ latestVersion: null, updateCheckSupported: false },不會用其他方式猜測版本。 - 失敗處理:任何查詢失敗(逾時、非零離開碼、無網路、輸出格式
不符)都只記 DBG 並回
latestVersion: null,不會向上拋出例外, 不影響collectCliTools()本次採集結果。 - 只提示,不自動更新:查到新版只會透過
latestVersion/updateCheckSupported欄位回報給 CLIProxy 在管理介面顯示提示, 不會自動送出cli-update任務;是否更新仍由使用者手動按下既有的 「更新」按鈕決定。 - 更新任務依實際安裝來源選擇更新方式:按下「更新」後,
handleCliUpdateTask(src/tasks/handlers/cli-install.ts)會先判斷 執行檔的實際安裝來源——非 npm 全域安裝(isNpmGlobalInstall()判定 為false)且該工具宣告了install.updateCommand(工具自帶的update子指令)時,改執行該子指令;其餘情況才重跑既有的installCommand。指令執行成功後會重新讀取版本並與更新前比對, 版號沒有改變就回報失敗,並在訊息中列出resolveAllCommandPaths()找到的所有同名執行檔候選路徑(同名指令若同時存在於 PATH 上多個目錄, 例如 standalone 安裝與 npm 全域安裝並存,只更新其中一份不會反映在 實際生效的執行檔上);版號確實提升則回報成功,並帶出「舊版 → 新版」 與本次採用的更新方式。
CLI 工具登入偵測
src/tools/registry.ts 判斷一個 CLI 工具是否已登入,依序嘗試:
credentialPaths(antigravity/copilot/codex/claude 皆用此法): 逐一讀取憑證檔並解析為 JSON,檔案存在且(視設定)含指定的登入證據 鍵才判定為已登入;只讀取credentialInspect列出的非機密欄位 (到期時間、帳號、登入方式等),絕不讀取或回傳 token/secret 本體。credentialCheckCommand(kiro 專用):憑證不是可直接讀取解析的 檔案時使用——kiro 登入後把 token 寫進 SQLite (~/.local/share/kiro-cli/data.sqlite3的auth_kv表),不是 JSON 檔,方法 1 的機制無法判讀。改執行kiro-cli whoami,指令 exit code 0 視為已登入,並用accountPattern/authMethodPattern兩個 regex 從 stdout(例如Logged in with Google/Email: xxx@example.com)擷取帳號與登入方式,同樣不觸碰憑證本體。envTokenKeys:以上兩種都判定為未登入時,退回檢查環境變數是否 存在(COPILOT_GITHUB_TOKEN/ANTHROPIC_API_KEY/KIRO_API_KEY等),只檢查是否存在、不讀取其值寫入任何欄位。
登出(cli-logout 任務)的行為對應:credentialPaths 有值就刪除對應
檔案;有宣告 logoutCommand(kiro 為 ['logout'])則改執行該指令,
執行失敗直接回報失敗,不會誤報成功;兩者可並存。
開發
npm run dev # NestJS 熱重載(不含 CLI 指令,直接跑 daemon 邏輯)
npm run test # 測試(含用真的子行程模擬 CLI 工具、真的 SQLite 假 Worker 對打)
npm run lint
更新時間:2026/08/20 17:25:39(Asia/Taipei)
Dependencies
Dependencies
| ID | Version |
|---|---|
| @nestjs/common | ^11.0.0 |
| @nestjs/core | ^11.0.0 |
| @nestjs/platform-express | ^11.0.0 |
| commander | ^12.1.0 |
| reflect-metadata | ^0.2.2 |
| rxjs | ^7.8.1 |
| socket.io-client | ^4.8.3 |
Development Dependencies
| ID | Version |
|---|---|
| @eslint/js | ^9.9.0 |
| @nestjs/cli | ^11.0.0 |
| @nestjs/testing | ^11.0.0 |
| @types/jest | ^29.5.0 |
| @types/node | ^20.11.0 |
| eslint | ^9.0.0 |
| eslint-config-prettier | ^9.1.0 |
| jest | ^29.7.0 |
| prettier | ^3.3.0 |
| source-map-support | ^0.5.21 |
| ts-jest | ^29.1.0 |
| ts-node | ^10.9.2 |
| typescript | ^5.5.0 |
| typescript-eslint | ^8.0.0 |