w-dispatch-ai 1.0.37 → 1.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -6
- package/dist/w-dispatch-ai.umd.js +2 -2
- package/dist/w-dispatch-ai.umd.js.map +1 -1
- package/docs/WDispatchAi.mjs.html +2 -2
- package/docs/adapters.mjs.html +2 -2
- package/docs/budgetFor.mjs.html +2 -2
- package/docs/buildValidator.mjs.html +2 -2
- package/docs/castPintOr.mjs.html +2 -2
- package/docs/checkTruncation.mjs.html +2 -2
- package/docs/describeNonJsonBody.mjs.html +122 -0
- package/docs/dfTimeoutMs.mjs.html +2 -2
- package/docs/dispatchAi.mjs.html +2 -2
- package/docs/dispatchAiFallback.mjs.html +37 -5
- package/docs/dispatchAiWkf.mjs.html +2 -2
- package/docs/dispatchAntigravity.mjs.html +2 -2
- package/docs/dispatchApiOpenaiCompat.mjs.html +33 -7
- package/docs/dispatchApiOpenaiResponses.mjs.html +13 -4
- package/docs/dispatchApiTypesafeSystemone.mjs.html +21 -4
- package/docs/dispatchClaude.mjs.html +2 -2
- package/docs/dispatchCodex.mjs.html +2 -2
- package/docs/dispatchOpencode.mjs.html +2 -2
- package/docs/getCliArgs.mjs.html +2 -2
- package/docs/getErrorResult.mjs.html +2 -2
- package/docs/getErrorType.mjs.html +5 -3
- package/docs/global.html +255 -21
- package/docs/index.html +2 -2
- package/docs/quota_dfQuotaTimeoutMs.mjs.html +2 -2
- package/docs/quota_fetchQuotaJson.mjs.html +2 -2
- package/docs/quota_fromCodexUsageHttp.mjs.html +2 -2
- package/docs/quota_getQuotaAntigravity.mjs.html +2 -2
- package/docs/quota_getQuotaClaude.mjs.html +2 -2
- package/docs/quota_getQuotaCodex.mjs.html +2 -2
- package/docs/quota_readJsonOrNull.mjs.html +2 -2
- package/docs/quota_toQuotaLabel.mjs.html +2 -2
- package/docs/quota_toQuotaResult.mjs.html +2 -2
- package/docs/quota_toQuotaScopedLabel.mjs.html +2 -2
- package/docs/quota_toQuotaWindow.mjs.html +2 -2
- package/docs/readEnvFile.mjs.html +2 -2
- package/docs/resolveProviders.mjs.html +2 -2
- package/docs/wkf_callAiWithFallback.mjs.html +4 -3
- package/docs/wkf_createFileStore.mjs.html +2 -2
- package/docs/wkf_createUsageCounter.mjs.html +2 -2
- package/docs/wkf_extractJsonLoose.mjs.html +2 -2
- package/docs/wkf_noSideEffectPrefix.mjs.html +2 -2
- package/docs/wkf_runFanout.mjs.html +2 -2
- package/docs/wkf_runFanoutPipeline.mjs.html +2 -2
- package/docs/wkf_runRolePipeline.mjs.html +2 -2
- package/docs/wkf_salvageTruncatedArray.mjs.html +5 -3
- package/package.json +1 -1
- package/src/describeNonJsonBody.mjs +50 -0
- package/src/dispatchAiFallback.mjs +35 -3
- package/src/dispatchApiOpenaiCompat.mjs +31 -5
- package/src/dispatchApiOpenaiResponses.mjs +11 -2
- package/src/dispatchApiTypesafeSystemone.mjs +19 -2
- package/src/getErrorType.mjs +3 -1
- package/src/wkf/callAiWithFallback.mjs +2 -1
- package/src/wkf/salvageTruncatedArray.mjs +3 -1
- package/test/tools/fakeServerForApiTest.mjs +17 -2
- package/test/unit-dispatchAiFallback.test.mjs +245 -2
- package/test/unit-dispatchApiOpenaiCompat.test.mjs +41 -3
- package/test/unit-dispatchApiOpenaiResponses.test.mjs +11 -0
- package/test/unit-dispatchApiTypesafeSystemone.test.mjs +8 -1
package/README.md
CHANGED
|
@@ -211,6 +211,21 @@ await test()
|
|
|
211
211
|
})
|
|
212
212
|
```
|
|
213
213
|
|
|
214
|
+
#### 行為變更紀錄(依賴方升版前請檢查):
|
|
215
|
+
**1.0.37 起**(皆為安裝方驗收時需逐一檢查之既有呼叫結果變化):
|
|
216
|
+
1. **REST 文字類截斷預設失敗**:`finish_reason`為`length`/`content_filter`(Responses API 為`status: 'incomplete'`)時,於`validate`之前回`errorType: 'incomplete'`;此前會回`ok`或交`validate`判斷(不過時為`validation`)。**直接呼叫`dispatchAiFallback`或轉接器、且`validate`內含搶救策略者,須自行給`acceptTruncated: true`**;工作流`callAi`有自訂`parse`者自動同意、不必改。
|
|
217
|
+
2. 截斷與`tool-unsupported`改為**整組跳過**(此前逐把換金鑰);截斷不重試。
|
|
218
|
+
3. `validate`(及工作流之`parse`/`check`)拋錯改回驗證失敗(`validation`),不再令整條鏈 reject。
|
|
219
|
+
4. 工作流層:條目自帶`validate`與工作流`validate`取交集(兩者皆過才算過)。
|
|
220
|
+
5. 三個 REST 轉接器預設帶`Accept-Encoding: identity`。
|
|
221
|
+
6. 依`errorType`做健康計數或監控者:截斷且驗證不過者由`validation`改為`incomplete`,須一併納入;結果另帶`finishReason`與`truncated`。
|
|
222
|
+
|
|
223
|
+
**1.0.38 起**:
|
|
224
|
+
7. HTTP 200 但本體非 JSON,另報`INVALID_RESPONSE: body is not JSON (<位元組數>, first bytes <前16位元組hex>, content-encoding=…, content-type=…)`(此前與「JSON 缺欄位」同一句),並改為**整組跳過**;`errorType`仍為`invalid-response`,「JSON 缺欄位」維持換金鑰。依`errorType`計數者若要把此類整組失敗計入,須納入`invalid-response`。
|
|
225
|
+
|
|
226
|
+
**1.0.38 之後**:
|
|
227
|
+
8. `dispatchAiFallback`之`onEvent`新增事件`'group-exhausted'`:一組試完仍無成交時發出(每次呼叫每組恰一次,位於該組最後一個`next-key`/`skip-group`之後、下一組首個`try`之前),欄位見下方 Options for dispatchAiFallback 之`onEvent`;成交、預算用盡、中止之組不發,`tried`不變,既有 7 種事件之順序、欄位與觸發條件不變。**逐事件寫日誌者每個試完的組多一行**;只認特定`ev.type`者不受影響。以「本次呼叫整組全敗」計數(如健康層降序)者,改為每收到一次此事件計一次,不必再以金鑰數與逐把失敗次數重建(並行下不精確)。
|
|
228
|
+
|
|
214
229
|
#### Options shared by all dispatch functions:
|
|
215
230
|
| key | type | default | description |
|
|
216
231
|
| --- | --- | --- | --- |
|
|
@@ -298,10 +313,10 @@ Codex 0.149 起 Windows 預設走 elevated 沙箱(專用使用者 `CodexSandbo
|
|
|
298
313
|
| `key` | String | `''` | API key,以`Bearer`置於`Authorization`標頭,省略代表不帶認證 |
|
|
299
314
|
| `system` | String | `''` | system提示詞,置於messages首位 |
|
|
300
315
|
| `body` | Object | `{}` | 額外請求本體(`temperature`、`max_tokens`、`response_format`等),同名鍵覆寫預設 |
|
|
301
|
-
| `headers` | Object | `{}` | 額外請求標頭,同名鍵覆寫預設。**預設帶 `Accept-Encoding: identity`**:伺服器若壓縮了回應卻漏標 `Content-Encoding`,Node 內建 fetch 不會解壓,本轉接器只拿到亂碼而回 `INVALID_RESPONSE
|
|
316
|
+
| `headers` | Object | `{}` | 額外請求標頭,同名鍵覆寫預設。**預設帶 `Accept-Encoding: identity`**:伺服器若壓縮了回應卻漏標 `Content-Encoding`,Node 內建 fetch 不會解壓,本轉接器只拿到亂碼而回 `INVALID_RESPONSE`(三個 REST 轉接器同步)。重現條件:安裝方 2026-09-24 於 Zen 長回應(約 35 秒)實測,fetch 所見標頭為 0 個、本體 9,224 bytes 為 brotli,改帶 identity 則為 22,124 bytes 之 JSON;本機同日取樣 7 次未重現,推測漏標只在特定條件(長回應)出現。要改回允許壓縮可給 `{ 'Accept-Encoding': 'gzip, deflate, br' }` |
|
|
302
317
|
| `timeoutMs` | Integer | `300000` | 逾時毫秒,逾時中止請求(含回應串流讀取);全套件統一預設 |
|
|
303
318
|
| `maxRetries` | Integer | `0` | 失敗重試次數;**4xx(429除外)為客戶端錯誤不重試**,截斷(見`acceptTruncated`)亦不重試(同一請求必然再截斷),429/5xx/網路錯誤/逾時線性退避重試 |
|
|
304
|
-
| `acceptTruncated` | Boolean | `false` | **截斷預設失敗**:`finish_reason`為`length`或`content_filter`時,於`validate`之前回`errorType: 'incomplete'`(結果帶`truncated: true`,遞補層整組跳過)。`true`才放行`length`之截斷:有`validate`交其裁決、無則直接接受,結果仍標`truncated: true`;`content_filter`與可見輸出為空者一律失敗(空輸出之訊息附`reasoning_tokens`,常見於推理耗盡`max_tokens`)。`dispatchApiOpenaiResponses`同規則(`status: 'incomplete'`即截斷;`failed
|
|
319
|
+
| `acceptTruncated` | Boolean | `false` | **截斷預設失敗**:`finish_reason`為`length`或`content_filter`時,於`validate`之前回`errorType: 'incomplete'`(結果帶`truncated: true`,遞補層整組跳過)。`true`才放行`length`之截斷:有`validate`交其裁決、無則直接接受,結果仍標`truncated: true`;`content_filter`與可見輸出為空者一律失敗(空輸出之訊息附`reasoning_tokens`,常見於推理耗盡`max_tokens`)。`dispatchApiOpenaiResponses`同規則(`status: 'incomplete'`即截斷;`failed`不屬截斷)。**1.0.37 起行為改變**(此前截斷內容會交給`validate`判斷):**直接呼叫`dispatchAiFallback`或轉接器、且`validate`內含搶救策略者,須自行給`acceptTruncated: true`**(`dispatchAiFallback`會原樣轉傳給轉接器);工作流層`callAi`之預設見`salvageTruncatedArray`列 |
|
|
305
320
|
| `retryDelayMs` | Integer | `5000` | 重試間隔,實際為`retryDelayMs`×次數且上限15000ms |
|
|
306
321
|
|
|
307
322
|
結果結構對齊execCli:`stdout`為回覆內容、`code`為HTTP狀態碼(網路錯誤/逾時為`null`)、逾時`error`以`TIMEOUT`開頭、驗證失敗為`OUTPUT_VALIDATION_FAILED`——故可直接作為`dispatchAiFallback`條目(`kind: 'api-openai-compat'`,`keys`多金鑰輪替同樣適用)與工作流provider。
|
|
@@ -320,7 +335,7 @@ Codex 0.149 起 Windows 預設走 elevated 沙箱(專用使用者 `CodexSandbo
|
|
|
320
335
|
| `http` | HTTP非2xx(`code`為狀態碼) | api類 |
|
|
321
336
|
| `fetch` | 網路層錯誤(DNS/連線拒絕) | api類 |
|
|
322
337
|
| `tool-unsupported` | 模型回tool_calls而api類不支援工具 | api類 |
|
|
323
|
-
| `invalid-response` | 回應結構不合規(缺`choices[0].message.content`、缺`output`陣列,或 systemone 缺`answers
|
|
338
|
+
| `invalid-response` | 回應結構不合規(缺`choices[0].message.content`、缺`output`陣列,或 systemone 缺`answers`/缺所請求題目之答案);HTTP 200 但**本體非 JSON**時`error`另為`INVALID_RESPONSE: body is not JSON (<位元組數>, first bytes <前16位元組hex>, content-encoding=…, content-type=…)`,壓縮或損壞之本體可一眼辨識 | api類 |
|
|
324
339
|
| `incomplete` | 回應未完整:截斷(`finish_reason`為`length`/`content_filter`、Responses API 之`status: 'incomplete'`;結果帶`truncated: true`)或 Responses API 之其餘非完成狀態(如`failed`,`truncated: false`)。截斷判定只適用 REST 文字類;CLI 類拿不到終止訊號,截斷無從判別(已知限制) | api類 |
|
|
325
340
|
| `aborted` | `shouldStop`中止 | fallback層 |
|
|
326
341
|
| `budget` | 時間預算用盡 | fallback層 |
|
|
@@ -394,7 +409,7 @@ let r = await wdi.dispatchAiFallback(state, { providers: jev, questions })
|
|
|
394
409
|
| `coolDetect` | Function | 無 | 冷卻觸發之**注入判定**`(r)=>Boolean`,收完整失敗結果(含`stdout`、`stderr`、`code`、`error`),回傳`true`即視同冷卻觸發(內建429/TIMEOUT觸發不受影響)。CLI類限流字樣各家不同、隨版本漂移,且不一定在stderr(Claude Code之執行期失敗以result印在stdout,官方headless文件),**簽章表由觀察到字樣的呼叫端維護**,如`(r) => /FreeUsageLimitError/i.test(r.stderr \|\| '')`(注意:opencode 1.18.32 遇 Zen 免費層 429 時 `run` **不會結束**、stderr 只有 session 標頭,直到逾時才以 `TIMEOUT` 回報——內建 TIMEOUT 觸發已涵蓋;要讓字樣出現在 stderr 須於 `extraArgs` 加 `--print-logs --log-level ERROR`,2026-09-23 實測 DEBUG log 內為 `AI_APICallError: Rate limit exceeded`;不經本套件的原始 CLI 亦同);漏判僅退回現狀(每階段重探一次)、誤判也只是降尾非移除,兩邊代價都有上限。僅`cooldownMs>0`時有效;回調拋出例外視同`false` |
|
|
395
410
|
| `shouldStop` | Function | 無 | 中止判定`()=>Boolean`,於**每次嘗試之間**檢查,`true`即停止遞補回報`ABORTED`——供成果已無人接收時(如server端客戶端斷線)止損,把「斷線後仍空耗整條鏈」縮成「至多再耗當前這一家」。**不中止進行中之嘗試**(不殺子進程/不斷開請求,見Known design notes)。經工作流層原樣轉傳:中止後每個後續呼叫進門即回`ABORTED`,整條工作流自然快速收束,無須逐層處理;回調拋出例外視同`false` |
|
|
396
411
|
| `meta` | any | 無 | 保留鍵,同`providers[].meta`,永不轉傳 |
|
|
397
|
-
| `onEvent` | Function | 無 | 事件回調`(ev)=>{}`,`ev.type`為`'try'`、`'ok'`、`'next-key'`、`'skip-group'`、`'budget-out'`、`'aborted'`、`'cooled'`(冷卻觸發,帶`error`與`cooldownMs`,僅啟用cooldownMs時出現);失敗事件另帶`errorType`、`stdout`(被拒回覆)與`stderr`(錯誤輸出,皆已截斷)供診斷;回調拋出例外不影響主流程 |
|
|
412
|
+
| `onEvent` | Function | 無 | 事件回調`(ev)=>{}`,`ev.type`為`'try'`、`'ok'`、`'next-key'`、`'skip-group'`、`'budget-out'`、`'aborted'`、`'cooled'`(冷卻觸發,帶`error`與`cooldownMs`,僅啟用cooldownMs時出現)、`'group-exhausted'`(一組試完仍無成交,每次呼叫每組恰一次,位於該組最後一個`next-key`/`skip-group`之後、下一組首個`try`之前;帶`keys`(有效金鑰數,`0`代表登入態之單一虛擬金鑰)、`attempted`(本組實際嘗試數)、`by`(`'all-keys'`每把皆換鑰失敗,或`'skip-group'`以與金鑰無關之失敗收尾)、`errorTypes`(本組各次嘗試之`errorType`依序)與`error`(本組最後一次錯誤);成交、預算用盡、中止之組不發,亦不寫入`tried`);失敗事件另帶`errorType`、`stdout`(被拒回覆)與`stderr`(錯誤輸出,皆已截斷)供診斷;回調拋出例外不影響主流程 |
|
|
398
413
|
|
|
399
414
|
頂層其餘設定(`timeoutMs`、`validate`、`maxRetries`等)為各attempt之共用預設,條目可覆寫;`maxRetries`建議維持預設`0`,韌性交給換家而非重試同一家。
|
|
400
415
|
|
|
@@ -419,6 +434,7 @@ let r = await wdi.dispatchAiFallback(state, { providers: jev, questions })
|
|
|
419
434
|
| 輸出未過驗證 | `error === 'OUTPUT_VALIDATION_FAILED'` | 整組跳過 |
|
|
420
435
|
| 截斷(REST 文字類) | `truncated === true` | 整組跳過(同模型同請求換金鑰必然再截斷;`status: 'failed'`不屬此列,照「其餘」換下一把) |
|
|
421
436
|
| 模型回工具呼叫而 api 類不支援 | `error`以`TOOL_CALLS_UNSUPPORTED`開頭 | 整組跳過 |
|
|
437
|
+
| HTTP 200 但本體非 JSON(api 類) | `error`以`INVALID_RESPONSE: body is not JSON`開頭 | 整組跳過(傳輸或閘道狀態;「JSON 缺欄位」不在此列,照「其餘」換下一把) |
|
|
422
438
|
| kind無效 | `error`以`unknown ai kind`開頭 | 整組跳過 |
|
|
423
439
|
| 其餘(含額度上限、金鑰無效、服務回錯) | — | 換組內下一把 |
|
|
424
440
|
|
|
@@ -478,6 +494,8 @@ let r = await wdi.dispatchAiFallback(state, { providers: jev, questions })
|
|
|
478
494
|
}
|
|
479
495
|
```
|
|
480
496
|
|
|
497
|
+
**全數失敗時,頂層`error`/`errorType`只反映「最後一次」嘗試**:多把金鑰或多家依序失敗時(例如第一把本體非 JSON、第二把以剩餘預算重打而逾時),只記最終錯誤會誤判歸因。記日誌或評比時請一併記下各次嘗試,例如`` r.tried.filter((t) => t.outcome !== 'ok').map((t) => `${t.keyId}:${t.errorType}`) ``。
|
|
498
|
+
|
|
481
499
|
#### dispatchAiWkf (workflow factory):
|
|
482
500
|
注入一次provider定義表(名稱 → `dispatchAiFallback`條目)與共用預設,之後以名稱宣告工作流;名稱查無定義即回報錯誤(fail fast)。回覆經寬鬆JSON解析(`extractJsonLoose`)+自訂`check`驗證,非法回覆視為該家失敗而自動遞補;預設於prompt前掛「禁止建檔」約束(`promptPrefix: ''`可關閉);措辭豁免唯讀查閱——codex以shell讀檔,一律禁指令會令其無法讀取專案檔案且靜默回拒答(2026-08-13實測)。
|
|
483
501
|
|
|
@@ -621,7 +639,7 @@ let resolved = wdi.resolveProviders(merged, { env, pick: [...] })
|
|
|
621
639
|
| `createFileStore({ dir })` | `dispatchAiFallback`之`store`的檔案持久化——排程任務每次執行都是新行程,記憶體游標/冷卻每次歸零;本實作採**排除式passthrough**(state原封存還,僅剔自用欄位`at`),日後套件擴充state欄位自動相容(殷鑑:白名單store曾把1.0.7新增的`cooling`靜默丟棄) |
|
|
622
640
|
| `createUsageCounter({ dir })` | 逐日逐鍵用量計帳,`onEvent`直接掛進dispatch即於`try`事件記帳;**純觀測絕不據以節流**(額度視窗形態多樣,臆測門檻擋自己的呼叫等同拿猜測當事實);排程環境務必注入`getDate`錨定時區 |
|
|
623
641
|
| `budgetFor(chain)` | 遞補鏈走滿全鏈之時間預算(Σ各條目`timeoutMs`,未帶者以統一預設300000計);與外部排程硬上限取小者交`budgetMs` |
|
|
624
|
-
| `salvageTruncatedArray(text)` | 截斷JSON陣列之前段搶救(救回的每個元素皆完整合法);**不併入預設解析**——「判失敗換家重產」與「搶救前段部分接受」是同一問題的兩種合法策略,組成自訂`parse`注入即可。REST 文字類截斷預設失敗,但工作流`callAi`之`acceptTruncated`預設為「有自訂`parse`且非`rawText`」,故注入自訂`parse`即同意接受截斷內容、既有用法不必改;結果之`truncated: true
|
|
642
|
+
| `salvageTruncatedArray(text)` | 截斷JSON陣列之前段搶救(救回的每個元素皆完整合法);**不併入預設解析**——「判失敗換家重產」與「搶救前段部分接受」是同一問題的兩種合法策略,組成自訂`parse`注入即可。REST 文字類截斷預設失敗,但工作流`callAi`之`acceptTruncated`預設為「有自訂`parse`且非`rawText`」,故注入自訂`parse`即同意接受截斷內容、既有用法不必改;結果之`truncated: true`可辨識救回的是半批;**直接呼叫`dispatchAiFallback`或轉接器、把搶救寫在`validate`裡者,須自行給`acceptTruncated: true`**(1.0.37 起,否則截斷在`validate`之前即判失敗)。限制:只救頂層元素為物件之陣列,從第一個`[`起算(外包物件如`{"items":[…`會救回內層陣列、前言含`[`會失效) |
|
|
625
643
|
| `NO_SIDE_EFFECT` | 防副作用prompt前綴之單一來源(措辭含唯讀查閱豁免——codex以shell讀檔,一律禁指令等同禁讀檔);工作流`callAi`預設自動掛上,直呼`dispatchAiFallback`者自行前綴 |
|
|
626
644
|
|
|
627
645
|
#### 訂閱額度查詢(quota):`getQuotaClaude`/`getQuotaCodex`/`getQuotaAntigravity`
|
|
@@ -681,7 +699,7 @@ let a = await wdi.getQuotaAntigravity() // source: 'agy-print-usage'; windows
|
|
|
681
699
|
#### Known design notes:
|
|
682
700
|
- `package.json`**刻意不設**`exports`欄位:wsemi與w-*系列皆為自有套件,呼叫端以按需深層引入(`w-dispatch-ai/src/xxx.mjs`)為既定路線;增設exports會封死此路徑,勿加。
|
|
683
701
|
- `dispatchAi(kind, prompt, opt)`會把整個`opt`原樣轉傳對應轉接器,該轉接器用不到的鍵(例如輪替條目物件內的`kind`)會被忽略,故「供應商條目物件直接當`opt`」是預期用法;`dispatchAiFallback`之providers條目沿用同一約定。
|
|
684
|
-
- `dispatchAiFallback`為單向單輪:全數群組試畢即回傳最後一筆失敗結果與`tried`歷程,不回頭重試已敗的組。跨次執行僅記憶游標,不設金鑰停用清單(理由見上方失敗分流說明);需跨次跳過特定金鑰時,由呼叫端依`tried`/`onEvent`內之`error`與`stderr
|
|
702
|
+
- `dispatchAiFallback`為單向單輪:全數群組試畢即回傳最後一筆失敗結果與`tried`歷程,不回頭重試已敗的組。跨次執行僅記憶游標,不設金鑰停用清單(理由見上方失敗分流說明);需跨次跳過特定金鑰時,由呼叫端依`tried`/`onEvent`內之`error`與`stderr`自行決策。要以條目為單位判斷「本次呼叫整組試完仍無成交」者(如健康層據以降序),每收到一次`group-exhausted`事件計一次即可,勿以金鑰數與逐把`next-key`次數重建——事件不帶呼叫識別,並行呼叫跨越一次成交時(游標只在成交時推進,各呼叫起點不同)重建會多計或少計。
|
|
685
703
|
- `shouldStop`**只在嘗試邊界檢查,不中止進行中之嘗試**(不殺子進程、不斷開HTTP請求):進行中嘗試之強制中止需侵入execCli層與各轉接器,屬已知設計取捨——最小版已把斷線後的損失從「整條鏈」縮成「至多再耗當前這一家」;如有實測場景證明不足再議完整版。
|
|
686
704
|
- CLI類限流簽章**不進套件**:各家字樣(stdout或stderr)不同且隨CLI版本漂移,套件維護簽章表等同養一個自己驗證不了的分類器(與否決金鑰停用清單同一理由)。偵測經`coolDetect`依賴注入,由觀察到字樣的呼叫端維護。
|
|
687
705
|
- `dispatchOpencode`之`key`與`provider`須同時給予才會注入金鑰;只給其一(或範例中`.env`缺鍵導致`key`為`undefined`)時不會報錯,而是靜默沿用CLI既有登入狀態。
|