Jeffery jiantw83

@jiantw83/cliproxyworker (0.0.5)

Published 2026-08-23 15:44:02 +00:00 by jiantw83

Installation

@jiantw83:registry=https://gitea.jsc.idv.tw/api/packages/jiantw83/npm/
npm install @jiantw83/cliproxyworker@0.0.5
"@jiantw83/cliproxyworker": "0.0.5"

About this package

CLIProxyWorker:註冊到 CLIProxy,上報系統資訊與日誌、領取任務、操控本機 CLI 工具

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 安裝(推薦)

  1. 設定 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>
      
  2. 全域安裝:

    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 指令

註冊與啟動

  1. 向 CLIProxy 管理員取得一個一次性註冊 token(見 CLIProxy README 的「Worker 註冊流程」)。
  2. 註冊本機並產生設定檔:
    cliproxyworker register --server https://<CLIProxy 位址> --token <註冊 token>
    
    設定檔會寫到 ~/.config/cliproxyworker/config.json(權限 600, 內含 workerId/workerToken,可用 CLIPROXYWORKER_CONFIG_DIR 環境變數改變路徑,方便測試或容器化部署)。
  3. 啟動常駐程序:
    cliproxyworker daemon
    
    會開始定時心跳上報並長輪詢領取任務;建議搭配 systemd/pm2 等 常駐管理工具,確保程序異常結束後會自動重啟。
  4. 用 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 依序嘗試以下四個來源:

  1. 設定檔:解析 ~/.codex/config.toml,抓出頂層與各 [profiles.x] 區段內的 model = "..."。
  2. codex doctor --json:遞迴走訪輸出 JSON,取出鍵名含 model (排除 provider/布林字串)的字串值。
  3. codex exec --help:抓取 --model/-m 附近的 [possible values: ...] 區塊(目前版本的 codex 抽不到,是為之後版本 預留)。
  4. 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 工具是否已登入,依序嘗試:

  1. credentialPaths(antigravity/copilot/codex/claude 皆用此法): 逐一讀取憑證檔並解析為 JSON,檔案存在且(視設定)含指定的登入證據 鍵才判定為已登入;只讀取 credentialInspect 列出的非機密欄位 (到期時間、帳號、登入方式等),絕不讀取或回傳 token/secret 本體。
  2. 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)擷取帳號與登入方式,同樣不觸碰憑證本體。
  3. 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
Details
npm
2026-08-23 15:44:02 +00:00
2
MIT
66 KiB
Assets (1)
Versions (12) View all
0.1.4 2026-09-14
0.1.2 2026-08-25
0.1.1 2026-08-25
0.1.0 2026-08-25
0.0.9 2026-08-25