Global

Members

NO_SIDE_EFFECT

Description:
  • 防副作用prompt前綴:禁止寫入類副作用、豁免唯讀查閱

    用法:NO_SIDE_EFFECT + prompt後交dispatchAiFallback; callAiWithFallback預設自動掛上(傳promptPrefix:''關閉), 直接呼叫dispatchAiFallback的呼叫端自行前綴

Source:

防副作用prompt前綴:禁止寫入類副作用、豁免唯讀查閱

用法:NO_SIDE_EFFECT + prompt後交dispatchAiFallback; callAiWithFallback預設自動掛上(傳promptPrefix:''關閉), 直接呼叫dispatchAiFallback的呼叫端自行前綴

Example
import NO_SIDE_EFFECT from './src/wkf/noSideEffectPrefix.mjs'

//let r = await dispatchAiFallback(NO_SIDE_EFFECT + prompt, { providers })

WDispatchAi

Description:
  • AI供應商分派

Source:

AI供應商分派

Example
詳見dispatchAi、dispatchAiFallback、dispatchAiWkf、dispatchOpencode、dispatchClaude、dispatchCodex、dispatchAntigravity、dispatchApiOpenaiCompat、dispatchApiOpenaiResponses、dispatchApiTypesafeSystemone、resolveProviders、getQuotaClaude、getQuotaCodex、getQuotaAntigravity範例

adapters

Description:
  • 各AI供應商種類(kind)對CLI轉接器函數之對照表

    本對照表為kind之唯一來源,dispatchAi以其鍵值分派,WDispatchAi以其鍵名產生KINDS, 新增供應商時僅須於此加入一個鍵值對即可

Source:

各AI供應商種類(kind)對CLI轉接器函數之對照表

本對照表為kind之唯一來源,dispatchAi以其鍵值分派,WDispatchAi以其鍵名產生KINDS, 新增供應商時僅須於此加入一個鍵值對即可

Example
import adapters from './src/adapters.mjs'

console.log(Object.keys(adapters))
// => ['opencode', 'claude', 'codex', 'antigravity', 'api-openai-compat', 'api-openai-responses', 'api-typesafe-systemone']

dfQuotaTimeoutMs

Description:
  • 額度查詢類函數統一之預設逾時毫秒

Source:

額度查詢類函數統一之預設逾時毫秒

Example
import dfQuotaTimeoutMs from './src/quota/dfQuotaTimeoutMs.mjs'

console.log(dfQuotaTimeoutMs)
// => 20000

dfTimeoutMs

Description:
  • 全套件統一之預設逾時毫秒

Source:

全套件統一之預設逾時毫秒

Example
import dfTimeoutMs from './src/dfTimeoutMs.mjs'

console.log(dfTimeoutMs)
// => 300000

Methods

(async) agyPrint(exe, cmd, o) → {Promise}

Description:
  • 執行agy之print模式讀取型slash指令並解析其JSON回應

Source:
Parameters:
Name Type Description
exe String

輸入agy執行檔

cmd String

輸入slash指令字串, 例如'/usage'

o Object

輸入設定物件{timeoutMs, cwd, logFile}

Returns:

回傳物件{ok, data, response, stderr, error, errorType, code}

Type
Promise

attachErrorType(r) → {Object}

Description:
  • 失敗結果補上errorType欄位(已帶有效errorType或成功結果則原樣回傳)

Source:
Example
import { attachErrorType } from './src/getErrorType.mjs'

console.log(attachErrorType({ ok: false, error: 'TIMEOUT after 10s' }).errorType)
// => 'timeout'

console.log(attachErrorType({ ok: true, stdout: 'abc' }).errorType)
// => undefined
Parameters:
Name Type Description
r Object

輸入結果物件

Returns:

回傳結果物件,失敗且未帶errorType時追加之

Type
Object

budgetFor(providers) → {Number}

Description:
  • 計算遞補鏈走滿所需之時間預算(=各條目timeoutMs之總和, 未帶者以套件統一預設計)

    特點: 條目timeoutMs取正整數者計入,未帶或無效者以dfTimeoutMs(全套件統一預設300000)計—— 與dispatchAiFallback執行時之實際取值一致,故總和即為走滿全鏈之上限; 輸入非陣列回傳0

Source:
Example
import budgetFor from './src/budgetFor.mjs'

console.log(budgetFor([{ timeoutMs: 180000 }, { timeoutMs: 240000 }]))
// => 420000

console.log(budgetFor([{ timeoutMs: 180000 }, {}])) //未帶者以套件統一預設300000計
// => 480000
Parameters:
Name Type Description
providers Array

輸入供應商條目物件陣列(dispatchAiFallback之providers)

Returns:

回傳時間預算毫秒整數

Type
Number

buildChain(providers, spec) → {Object}

Description:
  • 依providers定義表把「名稱規格」展開成dispatchAiFallback的providers陣列

Source:
Example
import { buildChain } from './src/wkf/callAiWithFallback.mjs'

let providers = { a: { kind: 'claude' }, b: { kind: 'codex' } }
console.log(buildChain(providers, { use: 'a', fallback: ['b', 'c'] }))
// => { chain: [ { id: 'a', kind: 'claude' }, { id: 'b', kind: 'codex' } ], missing: [ 'c' ] }
Parameters:
Name Type Description
providers Object

輸入定義表物件(名稱 → 條目)

spec Object

輸入名額規格物件{ use, fallback }

Returns:

回傳物件,內含chain(條目物件陣列,id一律用名稱)與missing(查無定義之名稱字串陣列)

Type
Object

buildValidator(rule) → {function|null}

Description:
  • 建立驗證函式(規則語法同execCli之validate)

    特點: 傳入自訂函式時直接使用; 規則字串支援'nonempty'、'json'、'min:100',多規則可用逗號串接(須全部通過); 規則本身無效(如'min:abc')視為驗證失敗而非靜默跳過——避免打錯規則卻以為有在驗

Source:
Example
import buildValidator from './src/buildValidator.mjs'

let v = buildValidator('nonempty,min:3')
console.log(v('abcd'), v('ab'), v(''))
// => true false false

console.log(buildValidator(null))
// => null
Parameters:
Name Type Description
rule String | function

輸入驗證規則字串('nonempty'、'json'、'min:100', 逗號可串接)或自訂函式

Returns:

回傳驗證函式,無有效規則回傳null

Type
function | null

(async) callAiWithFallback(prompt, optopt) → {Promise}

Description:
  • 呼叫一個AI名額(主模型+自帶遞補鏈),回覆經parse+check驗證後回傳結構化結果

    特點: spec.use為主模型名稱、spec.fallback為遞補名稱陣列,依序展開為遞補鏈,名稱查無定義即回報錯誤(fail fast); parse+check接進遞補層之validate——非法回覆視為該家失敗而自動換下一家,不把壞結果帶回來; 預設掛防寫檔前綴(promptPrefix傳空字串可關閉); 本函數不會reject,一律以結果物件之ok與error欄位回報成敗

Source:
Example
//need cli in system PATH

import callAiWithFallback from './src/wkf/callAiWithFallback.mjs'

//鍵名須區分到模型並帶上路徑, 詳見dispatchAiFallback.mjs檔頭之id設計規則
let providers = {
    'agnes:agnes-3.0-flash': { kind: 'api-openai-compat', baseURL: 'https://apihub.agnes-ai.com/v1', model: 'agnes-3.0-flash', keys: ['sk-xxx'] },
    'claude:sonnet': { kind: 'claude', model: 'sonnet' },
}

let test = async () => {

    let r = await callAiWithFallback('只回覆JSON: {"a":1}', {
        providers,
        spec: { use: 'agnes:agnes-3.0-flash', fallback: ['claude:sonnet'] },
        check: (j) => j.a === 1,
    })
    console.log(r.ok, r.json, r.providerId)
    // => true { a: 1 } 'agnes:agnes-3.0-flash'

}
await test()
    .catch((err) => {
        console.log(err)
    })
Parameters:
Name Type Attributes Default Description
prompt String

輸入提示詞字串

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
providers Object

輸入provider定義表物件(名稱 → dispatchAiFallback條目,條目內含kind、model、keys、exe、provider、config等)

spec Object

輸入名額規格物件{ use:'主模型名稱', fallback:['遞補名稱', ...] }

check function <optional>
null

輸入結果檢核函數(json)=>Boolean,預設null代表只要能解析出JSON即通過

parse function <optional>
extractJsonLoose

輸入回覆解析函數(stdout)=>Object|null,預設寬鬆JSON抽取

rawText Boolean <optional>
false

輸入是否以純文字模式運作布林值,true代表不解析JSON(json欄位為修剪後文字、check收文字),預設false

acceptTruncated Boolean <optional>

輸入是否接受REST文字類轉接器回報之截斷內容(交parse+check裁決)布林值,預設為「有給自訂parse且非rawText」(自訂parse即搶救策略之同意訊號,見salvageTruncatedArray.mjs),其餘情況截斷一律判失敗換家

promptPrefix String <optional>
防寫檔約束

輸入prompt前綴字串,預設為防寫檔約束(見wkf/noSideEffectPrefix.mjs),傳''關閉

timeoutMs Number <optional>
300000

輸入單次嘗試逾時毫秒正整數,全套件統一預設300000

budgetMs Number <optional>
null

輸入整條遞補鏈之時間預算毫秒正整數,預設null代表不限

minAttemptMs Number <optional>
20000

輸入搭配budgetMs之開工門檻毫秒正整數,剩餘預算低於此值即不再開工,預設20000

maxRetries Number <optional>
0

輸入同家重試次數非負整數,預設0(韌性交給遞補;端點不穩偶發空回之模型可調高令同鍵重試;截斷不重試)

cwd String <optional>
process.cwd()

輸入子進程工作目錄字串,預設process.cwd()

store Object <optional>
null

輸入游標持久化物件{get,set},預設null代表用行程內記憶體

meta * <optional>

輸入呼叫端自有資訊(分類、標籤、註記),保留鍵保證永不轉傳下層,預設undefined

onEvent function <optional>
null

輸入遞補層事件回調函數,預設null。除上列外之其餘鍵(retryDelayMs、maxBuffer、shouldStop、coolDetect、cooldownMs等)亦一律原樣轉傳dispatchAiFallback

Returns:

回傳Promise,resolve回傳結果物件,內含ok(是否取得可用結果布林值)、json(解析後物件,rawText模式下為文字)、providerId(實際使用之名稱)、keyIndex、keyId、ms(總耗時毫秒)、tried(遞補嘗試歷程陣列)、usage(api類之token用量原樣透傳,CLI類為null)、finishReason(REST文字類之正規化終止原因字串,CLI類為'')、truncated(是否為截斷內容布林值,接受搶救時ok亦可能為true)、error(錯誤訊息字串)、errorType(僅失敗時,機器可讀錯誤類別字串,一覽見getErrorType.mjs檔頭),本函數不會reject

Type
Promise

(async) callOnce(url, headers, body, timeoutMs, validator, acceptTruncated) → {Promise}

Description:
  • 單次HTTP呼叫(內部使用, 不含重試邏輯)

Source:
Parameters:
Name Type Description
url String

輸入完整端點網址字串

headers Object

輸入請求標頭物件

body Object

輸入請求本體物件

timeoutMs Number

輸入逾時毫秒

validator function | null

輸入驗證函式

acceptTruncated Boolean

輸入是否明示接受截斷內容

Returns:

回傳Promise,resolve回傳結果物件

Type
Promise

(async) callOnce(url, headers, body, timeoutMs, validator, acceptTruncated) → {Promise}

Description:
  • 單次HTTP呼叫(內部使用, 不含重試邏輯)

Source:
Parameters:
Name Type Description
url String

輸入完整端點網址字串

headers Object

輸入請求標頭物件

body Object

輸入請求本體物件

timeoutMs Number

輸入逾時毫秒

validator function | null

輸入驗證函式

acceptTruncated Boolean

輸入是否明示接受截斷內容

Returns:

回傳Promise,resolve回傳結果物件

Type
Promise

(async) callOnce(url, headers, body, ids, timeoutMs, validator) → {Promise}

Description:
  • 單次HTTP呼叫(內部使用, 不含重試邏輯)

Source:
Parameters:
Name Type Description
url String

輸入完整端點網址字串

headers Object

輸入請求標頭物件

body Object

輸入請求本體物件

ids Array

輸入請求之題目id陣列,用於檢核回應是否每題皆有答案

timeoutMs Number

輸入逾時毫秒

validator function | null

輸入驗證函式

Returns:

回傳Promise,resolve回傳結果物件

Type
Promise

castPintOr(v, df) → {Number|*}

Description:
  • 正整數正規化:有效正整數即轉整數回傳,否則回傳預設值

    本套件各層之數值設定(timeoutMs、budgetMs、minAttemptMs、cooldownMs、retryDelayMs等) 皆採同一寬容策略「無效即靜默回退預設」,統一收斂於此, 避免同一7行判斷區塊散落各轉接器(曾重複6處)

Source:
Example
import castPintOr from './src/castPintOr.mjs'

console.log(castPintOr(5000, 300000))
// => 5000

console.log(castPintOr('abc', 300000))
// => 300000

console.log(castPintOr(undefined, null))
// => null
Parameters:
Name Type Description
v *

輸入待正規化之值

df *

輸入無效時之預設值(可為null代表不限)

Returns:

回傳正規化後之正整數,v非有效正整數時回傳df

Type
Number | *

collectAdditional(data) → {Array}

Description:
  • 取出additional_rate_limits之集合並統一為陣列

Source:
Parameters:
Name Type Description
data Object

輸入回應物件

Returns:

回傳單筆物件陣列, 無則[]

Type
Array

createFileStore(optopt) → {Object}

Description:
  • 建立dispatchAiFallback之store的檔案持久化(游標與冷卻跨行程有效)

    特點: 採排除式passthrough——state原封存還,僅剔除本層自用欄位(預設只有at:人讀的最後更新時間戳), 日後套件擴充state欄位(如cooling)即自動相容,不需改本函數; 檔案不存在或非法JSON時get回空物件(套件視為全新狀態); stamp可注入時間戳函數(排程環境建議注入時區錨定者),寫入時補進at欄位供人工debug

Source:
Example
import createFileStore from './src/wkf/createFileStore.mjs'
import dispatchAiFallback from './src/dispatchAiFallback.mjs'

let store = createFileStore({ dir: './state' })
//let r = await dispatchAiFallback(prompt, { providers, store, cooldownMs: 3600000 })
Parameters:
Name Type Attributes Default Description
opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
file String <optional>
null

輸入狀態檔完整路徑字串,與dir/name二擇一

dir String <optional>
null

輸入狀態目錄字串,與name組成檔案路徑

name String <optional>
'ai-cursor.json'

輸入狀態檔名字串,預設'ai-cursor.json'

ownKeys Array <optional>
['at']

輸入本層自用、不屬於套件狀態之欄位名字串陣列,get時剔除set時補回,預設['at']

stamp function <optional>
ISO時間戳

輸入時間戳函數()=>String,寫入at欄位用,預設回傳new Date().toISOString()

Returns:

回傳store物件,內含get、set(可直接餵dispatchAiFallback之store)與file(狀態檔路徑)

Type
Object

createUsageCounter(optopt) → {Object}

Description:
  • 建立逐日、逐鍵之用量計數器(純觀測, 不參與任何判斷)

    特點: onEvent可直接掛進dispatchAiFallback/dispatchAiWkf,於type為'try'時依keyOf取鍵累加—— 嘗試時即記帳,行程被外部時限中途砍掉已發出的請求仍有紀錄; 逐日分桶並僅保留最近keepDays天,避免檔案無限成長; getDate可注入時區錨定之日期函數——預設隨系統時區,排程session之系統時區可能為UTC+0 而使日界錯8小時,排程環境務必注入

Source:
Example
import createUsageCounter from './src/wkf/createUsageCounter.mjs'

let usage = createUsageCounter({ dir: './state' })
//let r = await dispatchAiFallback(prompt, { providers, onEvent: usage.onEvent })
//console.log(usage.today())
// => { today: '2026-08-18', byKey: { 'agnes:agnes-2.5-flash#0': 3 }, total: 3 }
Parameters:
Name Type Attributes Default Description
opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
file String <optional>
null

輸入用量檔完整路徑字串,與dir/name二擇一

dir String <optional>
null

輸入狀態目錄字串

name String <optional>
'ai-usage.json'

輸入用量檔名字串,預設'ai-usage.json'

getDate function <optional>
系統日期

輸入日期函數()=>'YYYY-MM-DD',預設隨系統時區(排程環境建議注入時區錨定者)

keepDays Number <optional>
14

輸入保留天數正整數,預設14

keyOf function <optional>
(ev)=>ev.keyId||ev.providerId

輸入計帳鍵函數(ev)=>String,決定粒度(金鑰或條目),預設優先keyId

Returns:

回傳計數器物件,內含onEvent(可直接餵dispatch之onEvent)、bump(手動累加)、today(今日統計)與file(用量檔路徑)

Type
Object

decodeJwtPayload(jwt) → {Object|null}

Description:
  • 解出JWT之payload, 僅供顯示用途, 不驗簽

Source:
Parameters:
Name Type Description
jwt String

輸入JWT字串

Returns:

回傳payload物件, 解析失敗回傳null

Type
Object | null

defaultIntegratePrompt(candidates, optopt) → {String}

Description:
  • 預設整合提示詞模板:把成功候選JSON併入整合任務

Source:
Parameters:
Name Type Attributes Default Description
candidates Array

輸入成功候選物件陣列

opt Object <optional>
{}

輸入設定物件(取schema作為輸出格式示意),預設{}

Returns:

回傳整合提示詞字串

Type
String

describeNonJsonBody(bytes, headers) → {String}

Description:
  • 組出「HTTP 200但本體非JSON」之錯誤訊息

Source:
Example
import describeNonJsonBody from './src/describeNonJsonBody.mjs'

let h = new Headers({ 'content-type': 'application/json' })
console.log(describeNonJsonBody(new Uint8Array([0x8b, 0xef, 0x02]), h))
// => 'INVALID_RESPONSE: body is not JSON (3 bytes, first bytes 8bef02, content-encoding=none, content-type=application/json)'
Parameters:
Name Type Description
bytes Uint8Array

輸入回應本體之原始位元組(未解碼)

headers Headers | Object

輸入回應標頭(fetch之Headers,或具get方法之物件)

Returns:

回傳錯誤訊息字串,以BODY_NOT_JSON開頭,附位元組數、前16位元組hex、content-encoding與content-type

Type
String

directWindow(it) → {Object|null}

Description:
  • 單筆本身即為窗口時取出之

Source:
Parameters:
Name Type Description
it Object

輸入單筆物件

Returns:

回傳窗口物件, 無回傳null

Type
Object | null

(async) dispatchAi(kind, prompt, optopt) → {Promise}

Description:
  • 依供應商種類(kind)分派至對應之轉接器

    kind清單以adapters.mjs對照表為唯一來源(目前為'opencode'、'claude'、'codex'、'antigravity'、 'api-openai-compat',CLI或API之選型判準見adapters.mjs檔頭)。 各家金鑰模式不同(2026-08-08起於本機實測確認):opencode與api-openai-compat支援逐次注入金鑰, 故可多把金鑰輪替;claude/codex/antigravity沿用CLI既有登入狀態,無逐次金鑰概念。 故「輪替」的單位是「供應商條目」而非單純的金鑰:一個條目即一組(kind, model, 可選的key/provider), 輪到誰就用誰的轉接器與模型

Source:
Example
//need claude, codex or opencode cli in system PATH

import dispatchAi from './src/dispatchAi.mjs'

let test = async () => {

    let r = await dispatchAi('claude', '請只回覆兩個字:完成', { model: 'sonnet' })
    console.log(r.ok, r.stdout.trim())
    // => true '完成'

    let re = await dispatchAi('gemini', 'abc')
    console.log(re.ok, re.error.indexOf('unknown ai kind: "gemini"') === 0)
    // => false true

}
await test()
    .catch((err) => {
        console.log(err)
    })
Parameters:
Name Type Attributes Default Description
kind String

輸入供應商種類字串,須為adapters.mjs對照表之鍵名,目前可選'opencode'、'claude'、'codex'、'antigravity'、'api-openai-compat'

prompt String

輸入提示詞字串

opt Object <optional>
{}

輸入設定物件,原樣轉傳對應轉接器,各轉接器可用設定詳見dispatchOpencode、dispatchClaude、dispatchCodex、dispatchAntigravity、dispatchApiOpenaiCompat,預設{}

Returns:

回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(標準輸出字串)、stderr(標準錯誤字串)、code(離開碼)、error(錯誤訊息字串,成功時為空字串)、errorType(僅失敗時,機器可讀錯誤類別字串)、durationMs(耗時毫秒)、attempts(實際嘗試次數),本函數不會reject

Type
Promise

(async) dispatchAiFallback(prompt, optopt) → {Promise}

Description:
  • 依供應商清單順序自動遞補調用AI,組內多金鑰以游標輪替

    特點: providers陣列順序即優先序,排前面的先用; 條目本身即該次調用之opt(除id與keys外原樣透傳對應轉接器),與dispatchAi「條目直接當opt」同一約定; 條目給予keys(多把金鑰)時以游標輪替,某把失敗自動換下一把,全數失敗才遞補下一組; 與金鑰無關之失敗(逾時/執行檔不存在/參數錯誤/輸出未過驗證/未知kind/截斷/工具不支援/HTTP 200但本體非JSON)直接整組跳過,不逐把空耗; 跨次執行僅記憶游標(經store注入持久化),不設金鑰停用清單——額度視窗形態多樣(5小時滾動/逐時/逐日), 停用會把已恢復的金鑰閒置,而重探的代價僅一次快速失敗; 本函數不會reject,一律以結果物件之ok與error欄位回報成敗

Source:
Example
//need opencode, claude, codex cli in system PATH

import dispatchAiFallback from './src/dispatchAiFallback.mjs'

let test = async () => {

    let r = await dispatchAiFallback('請只回覆兩個字:完成', {
        providers: [
            {
                //id區分到模型且帶路徑: 同一模型經REST與CLI取得屬兩個供應商
                id: 'agnes:agnes-3.0-flash',
                kind: 'api-openai-compat',
                baseURL: 'https://apihub.agnes-ai.com/v1',
                model: 'agnes-3.0-flash',
                keys: ['sk-aaa', 'sk-bbb'], //多把金鑰, 某把失敗自動換下一把
            },
            {
                id: 'oc:opencode/muse-spark-1.3-contributor-free', //CLI版(有工具, 較慢)
                kind: 'opencode',
                model: 'opencode/muse-spark-1.3-contributor-free',
                useStoredAuth: false, //opencode自家免費模型以匿名存取, 不沿用本機auth.json之登入
                timeoutMs: 180000,
            },
            { id: 'claude:sonnet', kind: 'claude', model: 'sonnet' }, //以上全敗時遞補
            { id: 'codex:gpt-5.6-luna', kind: 'codex', model: 'gpt-5.6-luna', sandbox: 'read-only' },
        ],
        budgetMs: 600000,
        onEvent: (ev) => console.log(ev.type, ev.providerId, ev.keyIndex),
    })
    console.log(r.ok, r.providerId, r.keyIndex, r.tried.length)
    // => true 'agnes:agnes-3.0-flash' 0 1

}
await test()
    .catch((err) => {
        console.log(err)
    })
Parameters:
Name Type Attributes Default Description
prompt String

輸入提示詞字串,一律以stdin傳入子進程

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
providers Array

輸入供應商條目物件陣列,順序即優先序。各條目除下列鍵外,其餘鍵(kind、model、exe、provider、config、sandbox、timeoutMs等)即該條目之opt原樣透傳對應轉接器

Properties
Name Type Attributes Default Description
id String <optional>
條目索引字串

輸入群組識別字串,游標以此為鍵、亦為日誌標籤,本套件不解讀其內容。須區分到「模型」而非只到「廠商」(如'claude:sonnet'而非'claude'),同一模型經不同路徑取得時須帶上路徑(如'poolside:laguna-s-2.1'與'or:poolside/laguna-s-2.1:free'),且務必唯一。省略時回退為陣列索引字串——索引是位置不是身分,日後插入條目會令後續條目繼承他人游標進度,故正式設定一律明給。詳見本檔檔頭之id設計規則

keys Array <optional>
[]

輸入同一服務之多把API key字串陣列,逐次注入輪替(kind為opencode時須同時於條目給予provider),省略代表沿用CLI既有登入狀態之單一虛擬金鑰

meta * <optional>

輸入呼叫端自有資訊(分類、標籤、註記),保留鍵保證永不轉傳對應轉接器——條目其餘鍵一律原樣轉傳,自有欄位放此鍵可與轉傳機制永久絕緣,預設undefined

budgetMs Number <optional>
null

輸入整輪遞補之時間上限毫秒正整數,剩餘預算會壓進每次呼叫之timeoutMs,預設null代表不限

minAttemptMs Number <optional>
20000

輸入單次嘗試之最低剩餘預算毫秒正整數,剩餘低於此值即停止嘗試回報budget exhausted,預設20000

store Object <optional>
null

輸入狀態持久化物件{get:()=>state,set:(state)=>{}},state內含cursors(逐群組游標)與cooling(供應商冷卻時間戳,僅cooldownMs>0時使用),省略代表用行程內記憶體(跨呼叫有效,重啟歸零)。假定單行程序列調用,並行請自行加鎖

cooldownMs Number <optional>
0

輸入供應商冷卻視窗毫秒非負整數,>0啟用:條目(限有明給id者)遭遇限流(HTTP 429,僅api類可偵測;CLI類可經coolDetect注入判定)或逾時(TIMEOUT開頭)後,於冷卻視窗內之後續呼叫中被移至鏈尾——只降序不移除,前面全敗時仍會被嘗試,任一次成功立即解除;注意啟用時「providers順序即優先序」會被暫時重排,此即本機制之目的;預設0代表不啟用

coolDetect function <optional>
null

輸入冷卻觸發判定函數(r)=>Boolean,收完整失敗結果物件(含stdout、stderr、code、error),回傳true即視同冷卻觸發(內建429/TIMEOUT觸發不受影響)——CLI類限流埋在stderr且各家字樣不同,簽章表由觀察到字樣的呼叫端維護,如(r)=>/FreeUsageLimitError/i.test(r.stderr||'');僅cooldownMs>0時有效,回調拋出例外視同false,預設null

shouldStop function <optional>
null

輸入中止判定函數()=>Boolean,於每次嘗試之間檢查,回傳true即停止遞補回報ABORTED(不中止進行中之嘗試)——供呼叫端於成果已無人接收時(如客戶端斷線)止損;經工作流層原樣轉傳,中止後各後續呼叫進門即回ABORTED令整條工作流快速收束;回調拋出例外視同false,預設null

meta * <optional>

輸入呼叫端自有資訊,保留鍵保證永不轉傳各轉接器,預設undefined

onEvent function <optional>
null

輸入事件回調函數(ev)=>{},ev.type可為'try'、'ok'、'next-key'、'skip-group'、'budget-out'、'aborted'、'cooled'(冷卻觸發,帶error與cooldownMs,僅cooldownMs>0時出現)、'group-exhausted'(本組未成交而試完,每次呼叫每組恰一次,位於本組最後一個next-key或skip-group之後、下一組首個try之前;帶keys(有效金鑰數,0代表登入態之單一虛擬金鑰)、attempted(本組實際嘗試數)、by('all-keys'每把皆換鑰失敗,或'skip-group'以與金鑰無關之失敗收尾)、errorTypes(本組各次嘗試之errorType依序)與error(本組最後一次錯誤);成交、預算用盡、中止之組不發,亦不寫入tried);失敗事件(next-key/skip-group)另帶errorType、stdout(被拒回覆)與stderr(錯誤輸出)供診斷,後兩者於失敗路徑已由轉接器截斷;回調拋出例外不影響主流程,預設null

timeoutMs Number <optional>
300000

輸入各attempt共用之逾時毫秒正整數,條目可覆寫,全套件統一預設300000

validate String | function <optional>

輸入各attempt共用之stdout驗證規則,條目可覆寫,預設undefined

acceptTruncated Boolean <optional>
false

輸入是否接受REST文字類轉接器回報之截斷內容布林值(原樣轉傳轉接器),1.0.37起截斷於validate之前判失敗,validate內含搶救策略者須給true,預設false

maxRetries Number <optional>
0

輸入各attempt共用之同家重試次數非負整數,韌性建議交給換家而非重試同一家,預設0

Returns:

回傳Promise,resolve回傳結果物件,除execCli既有欄位(ok、stdout、stderr、code、error、durationMs、attempts、pid)外,追加providerId(實際使用之群組)、keyIndex(實際使用之金鑰索引,無keys時為null)、kind、model、tried(全部嘗試歷程陣列,成功時亦回傳;失敗項含errorType、stdout與stderr供診斷被拒原因);失敗結果帶機器可讀之errorType(一覽見getErrorType.mjs檔頭);api類轉接器提供usage(token用量)時原樣流出於結果與tried各項,CLI類無此欄;REST文字類轉接器另帶finishReason與truncated(是否截斷),同樣流出於結果與tried各項;本函數不會reject

Type
Promise

dispatchAiWkf(opt) → {Object}

Description:
  • 建立AI工作流執行環境(工廠),注入provider定義表與共用預設後回傳綁定版API

    特點: providers為名稱對dispatchAiFallback條目之定義表,之後各工作流以名稱宣告主模型與遞補鏈; defaults為共用呼叫設定,各工作流之callOpt與名額規格可逐項覆寫; 回傳之各函數皆不reject;本工廠為同步函數,providers無效時throw(設定錯誤應於啟動期即失敗)

Source:
Example
//need cli in system PATH

import dispatchAiWkf from './src/dispatchAiWkf.mjs'

//定義表之鍵名即dispatchAiFallback之條目id, 須區分到模型而非只到廠商,
//同一模型經不同路徑取得時須帶上路徑(REST與CLI屬兩個供應商, 能力與速度皆不同)
let wkf = dispatchAiWkf({
    providers: {
        'agnes:agnes-3.0-flash': { kind: 'api-openai-compat', baseURL: 'https://apihub.agnes-ai.com/v1', model: 'agnes-3.0-flash', keys: ['sk-xxx'] },
        'oc:opencode/muse-spark-1.3-contributor-free': { kind: 'opencode', model: 'opencode/muse-spark-1.3-contributor-free', useStoredAuth: false },
        'claude:sonnet': { kind: 'claude', model: 'sonnet' },
        'claude:opus': { kind: 'claude', model: 'opus' },
        'codex:gpt-5.6-luna': { kind: 'codex', model: 'gpt-5.6-luna' },
    },
    defaults: { timeoutMs: 300000 },
})

let test = async () => {

    //單一名額: 主模型+遞補鏈
    let r1 = await wkf.callAi('只回覆JSON: {"a":1}', { spec: { use: 'agnes:agnes-3.0-flash', fallback: ['claude:sonnet'] }, check: (j) => j.a === 1 })
    console.log(r1.ok, r1.json)
    // => true { a: 1 }

    //Fanout工作流: 多開執行+單點整合
    //純文字階段用REST(快), 需要讀專案檔案之階段才用CLI(有工具)
    let r2 = await wkf.runFanout({
        task: '分析並只回覆JSON: {"essence":"..."}',
        agents: [
            { use: 'agnes:agnes-3.0-flash', fallback: ['claude:sonnet'] },
            { use: 'claude:sonnet' },
        ],
        integrate: { use: 'codex:gpt-5.6-luna' },
        check: (j) => !!j.essence,
    })
    console.log(r2.ok, r2.integrated)
    // => true true

}
await test()
    .catch((err) => {
        console.log(err)
    })
Parameters:
Name Type Description
opt Object

輸入設定物件

Properties
Name Type Attributes Default Description
providers Object

輸入provider定義表物件(名稱 → dispatchAiFallback條目:{ kind, model, keys, exe, provider, config, sandbox, extraArgs... })

defaults Object <optional>
{}

輸入共用呼叫設定物件(cwd、store、onEvent、timeoutMs、budgetMs、maxRetries、promptPrefix、parse等),預設{}

Returns:

回傳綁定版API物件,內含callAi(單一名額呼叫)、runFanout(多開+整合)、runRolePipeline(串行角色鏈)、runFanoutPipeline(多開+整合+角色鏈)、providers(定義表原樣)

Type
Object

(async) dispatchAntigravity(prompt, optopt) → {Promise}

Description:
  • 以Google Antigravity CLI(agy)呼叫AI模型

    特點: prompt作為--print旗標之值傳遞而非stdin(agy介面如此,塞stdin會進互動模式卡住), 故prompt受命令列長度上限約束,超過30000字元回傳錯誤結果物件; model須為agy models第一欄之slug(如gemini-3.6-flash-low),注意agy錯誤訊息列出的是顯示名稱而非slug; 帶檔位之slug(-high/-medium/-low結尾)與effort同時給定且檔位不一致時agy回conflicts錯誤(一致則放行), effort需agy>=1.1.11,建議搭配不帶檔位之基礎slug(如gemini-3.1-pro)使用; 預設帶--dangerously-skip-permissions令非互動print模式不卡權限確認,可給予skipPermissions為false保留權限閘門; agy自身之--print-timeout未給時由timeoutMs推導並預留30秒緩衝,令CLI先於外層逾時; 沿用agy既有OAuth登入狀態(首次須於桌面互動模式完成登入); 本函數不會reject,一律以結果物件之ok與error欄位回報成敗

Source:
Example
//need agy cli in system PATH, and OAuth login completed

import dispatchAntigravity from './src/dispatchAntigravity.mjs'

let test = async () => {

    let r = await dispatchAntigravity('請只回覆兩個字:完成', { model: 'gemini-3.6-flash-low' })
    console.log(r.ok, r.stdout.trim())
    // => true 完成

    //基礎slug搭配effort(不可用帶檔位之slug併用effort)
    let r2 = await dispatchAntigravity('請只回覆兩個字:完成', { model: 'gemini-3.1-pro', effort: 'low' })
    console.log(r2.ok)
    // => true

    let re = await dispatchAntigravity('')
    console.log(re.ok, re.error)
    // => false 'prompt must be a non-empty string'

}
await test()
    .catch((err) => {
        console.log(err)
    })
Parameters:
Name Type Attributes Default Description
prompt String

輸入提示詞字串,作為--print旗標之值傳遞,長度上限30000字元

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
exe String <optional>
'agy'

輸入agy執行檔名稱或絕對路徑字串,命令名為agy非antigravity,給予名稱時由execCli自系統PATH解析,預設'agy'

model String <optional>
''

輸入模型slug字串,須為agy models第一欄之slug,例如'gemini-3.6-flash-low'、'gemini-3.1-pro-high',預設''代表不帶--model旗標由agy自行決定

effort String <optional>
''

輸入推理深度字串,可選'low'、'medium'、'high',需agy>=1.1.11,建議搭配不帶檔位之基礎slug;與帶檔位slug併用且檔位不一致時agy回conflicts錯誤,預設''代表不帶

skipPermissions Boolean <optional>
true

輸入是否帶--dangerously-skip-permissions旗標布林值,false代表保留CLI權限閘門,預設true

printTimeout String <optional>
''

輸入agy自身等待上限字串(如'10m'、'570s'),預設''代表由timeoutMs推導(扣30秒緩衝,下限30秒)

addDirs Array <optional>
自動納入cwd

輸入加入workspace之目錄字串陣列,逐項展開為--add-dir。agy以自身scratch目錄為工作區而不採子進程cwd,故未給本參數時自動納入有效cwd令檔案可視範圍與其他CLI轉接器一致;明示給陣列(含空陣列[]代表不揭露任何目錄)則完全尊重呼叫端

extraArgs Array <optional>
[]

輸入額外命令列旗標字串陣列(如--output-format、--json-schema、--mode),將接於固定旗標之後、--print之前,預設[]

timeoutMs Number <optional>
300000

輸入逾時毫秒正整數,逾時將強制關閉子進程及其子孫程序,全套件統一預設300000(恰對齊agy自身print-timeout預設5m0s)

cwd String <optional>
process.cwd()

輸入子進程工作目錄字串,預設process.cwd()。注意本參數不影響agy之檔案可視範圍(agy以自身scratch目錄為工作區),可視範圍由addDirs決定(未給addDirs時自動納入本目錄)

validate String | function <optional>

輸入stdout驗證規則字串或自訂驗證函數,規則字串支援'nonempty'、'json'、'min:100',多規則可用逗號串接,預設undefined代表不驗證

maxRetries Number <optional>
0

輸入失敗後最大重試次數非負整數,預設0

Returns:

回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(標準輸出字串)、stderr(標準錯誤字串)、code(離開碼)、error(錯誤訊息字串,成功時為空字串)、durationMs(耗時毫秒)、attempts(實際嘗試次數),本函數不會reject

Type
Promise

(async) dispatchApiOpenaiCompat(prompt, optopt) → {Promise}

Description:
  • 以fetch直呼OpenAI相容API(chat/completions)呼叫AI模型

    特點: 免安裝CLI、免預先登入,給baseURL+key+model即可呼叫(如OpenCode Zen、Agnes等OpenAI相容閘道); 僅供純文字生成(摘要、分析、改寫、產出JSON等素材已在prompt內之任務)—— 需要讀本機檔案、grep、執行指令、抓網頁等工具能力時,請改用CLI類kind(opencode/claude/codex/antigravity); prompt走HTTP body,無命令列長度限制; 錯誤依HTTP狀態碼分流:4xx(429除外)為客戶端錯誤不重試,429/5xx/網路錯誤/逾時依maxRetries線性退避重試; 結果結構與逾時/驗證失敗之error字樣對齊execCli,可直接作為dispatchAi與dispatchAiFallback之kind('api-openai-compat')使用; 本函數不會reject,一律以結果物件之ok與error欄位回報成敗

Source:
Example
//need network, no cli required

import dispatchApiOpenaiCompat from './src/dispatchApiOpenaiCompat.mjs'

let test = async () => {

    //Agnes
    let r1 = await dispatchApiOpenaiCompat('請只回覆兩個字:完成', {
        baseURL: 'https://apihub.agnes-ai.com/v1',
        key: 'sk-xxxxxx',
        model: 'agnes-3.0-flash',
    })
    console.log(r1.ok, r1.stdout.trim())
    // => true 完成

    //OpenCode Zen(即opencode CLI之自家閘道), 模型名不帶opencode/前綴; 走此端點者見providers.mjs檔頭之端點表
    //注意Zen之免費模型自2026-09-17起多數禁止REST直呼(403 FreeTierError), 該類模型須改走opencode CLI(kind:'opencode');
    //閘門係逐模型套用(如space-bunny-free於2026-09-24實測REST仍200), 能否走REST以實測為準, 例外見providers.mjs檔頭
    let r2 = await dispatchApiOpenaiCompat('請只回覆兩個字:完成', {
        baseURL: 'https://opencode.ai/zen/v1',
        key: 'sk-xxxxxx',
        model: 'kimi-k2.7-code', //付費模型不受免費層限制
    })
    console.log(r2.ok, r2.stdout.trim())
    // => true 完成

    let re = await dispatchApiOpenaiCompat('abc', { baseURL: 'https://apihub.agnes-ai.com/v1', key: 'sk-bad', model: 'agnes-3.0-flash' })
    console.log(re.ok, re.code, re.error)
    // => false 401 HTTP 401

}
await test()
    .catch((err) => {
        console.log(err)
    })
Parameters:
Name Type Attributes Default Description
prompt String

輸入提示詞字串,作為user訊息置於HTTP body

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
baseURL String

輸入API基底網址字串,例如'https://opencode.ai/zen/v1'、'https://apihub.agnes-ai.com/v1',將於尾端接上/chat/completions

model String

輸入模型ID字串,例如'agnes-3.0-flash'、'kimi-k2.7-code'(Zen之模型名不帶opencode/前綴)

key String <optional>
''

輸入API key字串,以Bearer置於Authorization標頭,預設''代表不帶認證標頭

system String <optional>
''

輸入system提示詞字串,將以system角色置於messages首位,預設''代表不帶

body Object <optional>
{}

輸入額外請求本體物件(如temperature、max_tokens、response_format),將併入預設body(同名鍵以此為準),預設{}。注意本轉接器不支援工具,帶入tools而模型回tool_calls時一律以TOOL_CALLS_UNSUPPORTED回報失敗,需要工具請改用CLI類kind

headers Object <optional>
{}

輸入額外請求標頭物件,同名鍵覆寫預設標頭;預設標頭含'Accept-Encoding: identity'(防伺服器壓縮卻漏標Content-Encoding,見檔頭),要改回允許壓縮可給{'Accept-Encoding':'gzip, deflate, br'},預設{}

timeoutMs Number <optional>
300000

輸入逾時毫秒正整數,逾時將中止請求(含回應串流讀取),全套件統一預設300000

validate String | function <optional>

輸入回覆內容驗證規則字串或自訂驗證函數,規則字串支援'nonempty'、'json'、'min:100',多規則可用逗號串接,自訂函數拋錯視同驗證失敗,預設undefined代表不驗證

acceptTruncated Boolean <optional>
false

輸入是否接受被截斷(finish_reason為length)之內容布林值,true代表交validate裁決(無validate則直接接受)且結果標truncated:true;content_filter與可見輸出為空者一律失敗,預設false代表截斷一律失敗(errorType incomplete)

maxRetries Number <optional>
0

輸入失敗後最大重試次數非負整數,4xx(429除外)與截斷不重試,預設0

retryDelayMs Number <optional>
5000

輸入重試間隔毫秒正整數,實際間隔為retryDelayMs乘以重試次數且上限15000ms,預設5000

Returns:

回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(回覆內容字串)、stderr(失敗時之原始回應本體)、code(HTTP狀態碼,網路錯誤與逾時為null)、error(錯誤訊息字串,成功時為空字串)、errorType(僅失敗時,機器可讀錯誤類別字串,一覽見getErrorType.mjs檔頭)、durationMs(耗時毫秒)、attempts(實際嘗試次數)、usage(原始回應之token用量物件原樣透傳,無則null)、finishReason(已取得回應時之正規化終止原因字串,缺值為'')、truncated(已取得回應時是否被截斷布林值),本函數不會reject

Type
Promise

(async) dispatchApiOpenaiResponses(prompt, optopt) → {Promise}

Description:
  • 以fetch直呼OpenAI Responses API(/responses)呼叫AI模型

    特點: 免安裝CLI、免預先登入,給baseURL+key+model即可呼叫(如OpenCode Zen之muse-spark系與GPT系); 端點型別與dispatchApiOpenaiCompat不同——Zen之端點依模型家族而異,收錄前須查官方文件端點欄 (https://opencode.ai/docs/zh-tw/zen/),打錯端點會得到HTTP 500而非404,詳見providers.mjs檔頭; 僅供純文字生成,需要工具能力請改用CLI類kind(opencode/claude/codex/antigravity); status非completed(如max_output_tokens耗盡)一律以INCOMPLETE_RESPONSE回報,不回半截內容; 結果結構與dispatchApiOpenaiCompat完全一致,可直接作為dispatchAi與dispatchAiFallback之kind('api-openai-responses')使用; 本函數不會reject,一律以結果物件之ok與error欄位回報成敗

Source:
Example
//need network, no cli required

import dispatchApiOpenaiResponses from './src/dispatchApiOpenaiResponses.mjs'

let test = async () => {

    //OpenCode Zen之muse-spark系走/responses(非chat/completions), 詳見providers.mjs檔頭
    let r = await dispatchApiOpenaiResponses('請只回覆兩個字:完成', {
        baseURL: 'https://opencode.ai/zen/v1',
        key: 'sk-xxxxxx',
        model: 'muse-spark-1.3-contributor-free',
    })
    console.log(r.ok, r.stdout.trim())
    // => true 完成

    let re = await dispatchApiOpenaiResponses('abc', { baseURL: 'https://opencode.ai/zen/v1', key: 'sk-bad', model: 'muse-spark-1.3-contributor-free' })
    console.log(re.ok, re.code, re.errorType)
    // => false 401 http

}
await test()
    .catch((err) => {
        console.log(err)
    })
Parameters:
Name Type Attributes Default Description
prompt String

輸入提示詞字串,作為input置於HTTP body

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
baseURL String

輸入API基底網址字串,例如'https://opencode.ai/zen/v1',將於尾端接上/responses

model String

輸入模型ID字串,例如'muse-spark-1.3-contributor-free'

key String <optional>
''

輸入API key字串,以Bearer置於Authorization標頭,預設''代表不帶認證標頭

system String <optional>
''

輸入system提示詞字串,將置於instructions欄位(Responses API之system管道),預設''代表不帶

body Object <optional>
{}

輸入額外請求本體物件(如temperature、max_output_tokens、reasoning),將併入預設body(同名鍵以此為準),預設{}。注意輸出上限欄位名為max_output_tokens而非max_tokens;本轉接器不支援工具,帶入tools而模型回function_call時一律以TOOL_CALLS_UNSUPPORTED回報失敗

headers Object <optional>
{}

輸入額外請求標頭物件,同名鍵覆寫預設標頭;預設標頭含'Accept-Encoding: identity'(防伺服器壓縮卻漏標Content-Encoding,見檔頭),預設{}

timeoutMs Number <optional>
300000

輸入逾時毫秒正整數,逾時將中止請求(含回應串流讀取),全套件統一預設300000

validate String | function <optional>

輸入回覆內容驗證規則字串或自訂驗證函數,規則字串支援'nonempty'、'json'、'min:100',多規則可用逗號串接,自訂函數拋錯視同驗證失敗,預設undefined代表不驗證

acceptTruncated Boolean <optional>
false

輸入是否接受被截斷(status為incomplete且reason為max_output_tokens)之內容布林值,true代表交validate裁決(無validate則直接接受)且結果標truncated:true;content_filter與可見輸出為空者一律失敗,預設false代表截斷一律失敗

maxRetries Number <optional>
0

輸入失敗後最大重試次數非負整數,4xx(429除外)與截斷不重試,預設0

retryDelayMs Number <optional>
5000

輸入重試間隔毫秒正整數,實際間隔為retryDelayMs乘以重試次數且上限15000ms,預設5000

Returns:

回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(回覆內容字串)、stderr(失敗時之原始回應本體)、code(HTTP狀態碼,網路錯誤與逾時為null)、error(錯誤訊息字串,成功時為空字串)、errorType(僅失敗時,機器可讀錯誤類別字串,一覽見getErrorType.mjs檔頭)、durationMs(耗時毫秒)、attempts(實際嘗試次數)、usage(原始回應之token用量物件原樣透傳,欄位名為input_tokens/output_tokens/total_tokens,無則null)、finishReason(已取得回應時之正規化終止原因字串,completed為'stop'、max_output_tokens為'length')、truncated(已取得回應時是否被截斷布林值,僅status為incomplete時為true),本函數不會reject

Type
Promise

(async) dispatchApiTypesafeSystemone(prompt, optopt) → {Promise}

Description:
  • 以fetch直呼TypeSafe AI之System One API(POST /v1/systemone),對prompt(即state)回答型別化問題

    特點: 非文字生成——jev等System One模型對state回答呼叫端定義之questions(noul是非題、choice單選題、score量表), 每題回傳受限於選項之答案與機率;官方無CLI,僅此API路徑; prompt即state,結構化內容以JSON.stringify(物件)傳入(實測答案與物件state一致); stdout為answers之JSON字串,另於結果追加answers(已解析物件)與modelResolved(實際模型版本); 請求之題目id於回應缺任一即以INVALID_RESPONSE回報; 不可與文字生成條目混在同一條遞補鏈,經工作流callAi呼叫時須傳promptPrefix:''(理由見檔頭); 本函數不會reject,一律以結果物件之ok與error欄位回報成敗

Source:
Example
//need network and a TypeSafe api key, no cli required

import dispatchApiTypesafeSystemone from './src/dispatchApiTypesafeSystemone.mjs'

let test = async () => {

    let r = await dispatchApiTypesafeSystemone('房間浴室水龍頭一直滴水,吵到睡不著', {
        key: 'apikey_xxxxxx',
        questions: {
            category: {
                type: 'choice',
                instructions: '這則客房訊息屬於哪一類?',
                criteria: {
                    '設備故障報修': '客人回報房間硬體設備損壞、水電問題或故障',
                    '索取備品': '客人需要毛巾、牙刷、礦泉水等客房備品',
                    '其他': null,
                },
            },
            urgent: { type: 'noul', instructions: '客人是否表達急迫性?' },
        },
    })
    console.log(r.ok, r.answers.category.choice, r.answers.urgent.noul)
    // => true 設備故障報修 0.86 (2026-09-17實測; noul為機率, 每次可能差0.01)

    let re = await dispatchApiTypesafeSystemone('abc', { key: 'apikey_bad', questions: { a: { type: 'noul', instructions: 'x?' } } })
    console.log(re.ok, re.code, re.errorType)
    // => false 401 http

}
await test()
    .catch((err) => {
        console.log(err)
    })
Parameters:
Name Type Attributes Default Description
prompt String

輸入被評估之內容字串,作為state置於HTTP body,結構化內容請先JSON.stringify

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
questions Object

輸入題目物件(必填且非空),鍵為自訂題目id,值為{type:'noul'|'choice'|'score', instructions, criteria},格式見檔頭與https://docs.typesafe.ai/api

baseURL String <optional>
'https://api.typesafe.ai/v1'

輸入API基底網址字串,將於尾端接上/systemone,預設官方端點

model String <optional>
'jev-latest'

輸入模型名稱字串,預設'jev-latest'(另有'jev-preview')

key String <optional>
''

輸入API key字串,以Bearer置於Authorization標頭,預設''代表不帶認證標頭

body Object <optional>
{}

輸入額外請求本體物件,將併入預設body(同名鍵以此為準),預設{}

headers Object <optional>
{}

輸入額外請求標頭物件,同名鍵覆寫預設標頭;預設標頭含'Accept-Encoding: identity'(防伺服器壓縮卻漏標Content-Encoding,見檔頭),預設{}

timeoutMs Number <optional>
300000

輸入逾時毫秒正整數,逾時將中止請求,全套件統一預設300000

validate String | function <optional>

輸入stdout(answers之JSON字串)驗證規則字串或自訂驗證函數,規則字串支援'nonempty'、'json'、'min:100',多規則可用逗號串接,自訂函數拋錯視同驗證失敗,預設undefined代表不驗證

maxRetries Number <optional>
0

輸入失敗後最大重試次數非負整數,4xx(429除外)不重試,預設0

retryDelayMs Number <optional>
5000

輸入重試間隔毫秒正整數,實際間隔為retryDelayMs乘以重試次數且上限15000ms,預設5000

Returns:

回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(answers之JSON字串)、stderr(失敗時之原始回應本體)、code(HTTP狀態碼,網路錯誤與逾時為null)、error(錯誤訊息字串,成功時為空字串)、errorType(僅失敗時,機器可讀錯誤類別字串,一覽見getErrorType.mjs檔頭)、durationMs(耗時毫秒)、attempts(實際嘗試次數)、usage(原始回應之token用量物件原樣透傳,欄位名為input_tokens/output_tokens,無則null)、answers(成功時為已解析之答案物件,否則null)、modelResolved(回應所載之實際模型版本字串,如'jev-1.13.0',無則''),本函數不會reject

Type
Promise

(async) dispatchClaude(prompt, optopt) → {Promise}

Description:
  • 以Claude Code CLI呼叫Claude模型

    特點: prompt一律走stdin而非位置參數,因摘要內文可達數萬字,當命令列參數會spawn ENAMETOOLONG; 沿用Claude Code既有登入狀態,無逐次注入API key之概念,故無key參數; 未給model時不帶--model旗標,由CLI自行決定使用模型; 預設帶--dangerously-skip-permissions令非互動之-p模式不因權限確認而卡住, 惟prompt含不可信內容(例如待摘要之網頁)時該內容之指示亦將無權限閘門執行, 可給予skipPermissions為false保留CLI權限閘門; 本函數不會reject,一律以結果物件之ok與error欄位回報成敗

Source:
Example
//need claude cli in system PATH

import dispatchClaude from './src/dispatchClaude.mjs'

let test = async () => {

    let r = await dispatchClaude('請只回覆兩個字:完成', { model: 'sonnet' })
    console.log(r.ok, r.stdout.trim())
    // => true '完成'

    let re = await dispatchClaude('')
    console.log(re.ok, re.error)
    // => false 'prompt must be a non-empty string'

}
await test()
    .catch((err) => {
        console.log(err)
    })
Parameters:
Name Type Attributes Default Description
prompt String

輸入提示詞字串,一律以stdin傳入子進程(Claude Code之stdin上限10MB,超過即非零離開)

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
exe String <optional>
'claude'

輸入claude執行檔名稱或絕對路徑字串,給予名稱時由execCli自系統PATH解析,預設'claude'

model String <optional>
''

輸入模型別名或模型ID字串,例如'claude-opus-5-5'(全名, 固定版本)、'opus'或'sonnet'(別名, 隨CLI指向最新版),預設''代表不帶--model旗標,由CLI依方案決定(2.1.280起Pro/Team Standard亦預設Opus)

skipPermissions Boolean <optional>
true

輸入是否帶--dangerously-skip-permissions旗標布林值,false代表保留CLI權限閘門,預設true

extraArgs Array <optional>
[]

輸入額外命令列旗標字串陣列,將接於固定旗標之後,預設[]

timeoutMs Number <optional>
300000

輸入逾時毫秒正整數,逾時將強制關閉子進程及其子孫程序,全套件統一預設300000

cwd String <optional>
process.cwd()

輸入子進程工作目錄字串,預設process.cwd()

validate String | function <optional>

輸入stdout驗證規則字串或自訂驗證函數,規則字串支援'nonempty'、'json'、'min:100',多規則可用逗號串接,預設undefined代表不驗證

maxRetries Number <optional>
0

輸入失敗後最大重試次數非負整數,預設0

Returns:

回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(標準輸出字串)、stderr(標準錯誤字串)、code(離開碼)、error(錯誤訊息字串,成功時為空字串)、durationMs(耗時毫秒)、attempts(實際嘗試次數),本函數不會reject

Type
Promise

(async) dispatchCodex(prompt, optopt) → {Promise}

Description:
  • 以OpenAI Codex CLI呼叫GPT模型

    特點: prompt一律走stdin而非位置參數,因摘要內文可達數萬字,當命令列參數會spawn ENAMETOOLONG; 固定帶--skip-git-repo-check,令非git倉庫之工作目錄亦可執行; 沿用Codex CLI既有登入狀態,無逐次注入API key之概念,故無key參數; 未給model時不帶-m旗標,由CLI自行決定使用模型; 本函數不會reject,一律以結果物件之ok與error欄位回報成敗

Source:
Example
//need codex cli in system PATH

import dispatchCodex from './src/dispatchCodex.mjs'

let test = async () => {

    let r = await dispatchCodex('請只回覆兩個字:完成', { model: 'gpt-5.6-luna', sandbox: 'read-only' })
    console.log(r.ok, r.stdout.includes('完成'))
    // => true true

    let re = await dispatchCodex('abc', { exe: 'codex-not-exist' })
    console.log(re.ok, re.error.includes('ENOENT'))
    // => false true

}
await test()
    .catch((err) => {
        console.log(err)
    })
Parameters:
Name Type Attributes Default Description
prompt String

輸入提示詞字串,一律以stdin傳入子進程

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
exe String <optional>
'codex'

輸入codex執行檔名稱或絕對路徑字串,給予名稱時由execCli自系統PATH解析,預設'codex'

model String <optional>
''

輸入模型ID字串,例如'gpt-5.6-luna'、'gpt-6.1-sol',預設''代表不帶-m旗標(此時由CLI依config.toml之model或官方推薦預設決定)

sandbox String <optional>
'workspace-write'

輸入沙箱模式字串,例如'read-only'、'workspace-write'、'danger-full-access',預設'workspace-write'

extraArgs Array <optional>
[]

輸入額外命令列旗標字串陣列,例如['--config', 'model_reasoning_effort="max"'],將接於固定旗標之後,預設[]

timeoutMs Number <optional>
300000

輸入逾時毫秒正整數,逾時將強制關閉子進程及其子孫程序,全套件統一預設300000

cwd String <optional>
process.cwd()

輸入子進程工作目錄字串,預設process.cwd()

validate String | function <optional>

輸入stdout驗證規則字串或自訂驗證函數,規則字串支援'nonempty'、'json'、'min:100',多規則可用逗號串接,預設undefined代表不驗證

maxRetries Number <optional>
0

輸入失敗後最大重試次數非負整數,預設0

Returns:

回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(標準輸出字串)、stderr(標準錯誤字串)、code(離開碼)、error(錯誤訊息字串,成功時為空字串)、durationMs(耗時毫秒)、attempts(實際嘗試次數),本函數不會reject

Type
Promise

(async) dispatchOpencode(prompt, optopt) → {Promise}

Description:
  • 以opencode CLI呼叫AI模型

    特點: prompt一律走stdin而非位置參數,因摘要內文可達數萬字,當命令列參數會spawn ENAMETOOLONG, 而opencode run未帶位置message時即從stdin讀取; 同時給予key與provider時,以OPENCODE_AUTH_CONTENT環境變數逐次注入金鑰, 該注入僅作用於當次子進程且不改寫auth.json,故可多把金鑰輪替並與其他程序並行; 未給key或provider時沿用CLI既有登入狀態(auth.json),useStoredAuth為false則改以匿名存取; 使用opencode未內建之第三方provider時,須另以config給予其provider定義; 本函數不會reject,一律以結果物件之ok與error欄位回報成敗

Source:
Example
//need opencode cli in system PATH

import dispatchOpencode from './src/dispatchOpencode.mjs'

let test = async () => {

    //沿用CLI既有登入狀態(auth.json)
    let r1 = await dispatchOpencode('請只回覆兩個字:完成', { model: 'opencode/muse-spark-1.3-contributor-free' })
    console.log(r1.ok, r1.stdout.includes('完成'))
    // => true true

    //不沿用本機登入之匿名存取(opencode自家免費模型建議如此, 見檔頭【Zen免費層閘門】)
    let r2 = await dispatchOpencode('請只回覆兩個字:完成', {
        model: 'opencode/muse-spark-1.3-contributor-free',
        useStoredAuth: false,
    })
    console.log(r2.ok)
    // => true

    //opencode未內建之第三方provider, 須另以config給予其定義
    let r3 = await dispatchOpencode('請只回覆兩個字:完成', {
        model: 'agnes-ai/agnes-2.0-flash',
        provider: 'agnes-ai',
        key: 'sk-xxxxxx',
        config: {
            provider: {
                'agnes-ai': {
                    npm: '@ai-sdk/openai-compatible',
                    name: 'Agnes',
                    options: { baseURL: 'https://apihub.agnes-ai.com/v1' },
                    models: { 'agnes-2.0-flash': { name: 'Agnes 2.0 Flash' } },
                },
            },
        },
    })
    console.log(r3.ok)
    // => true

}
await test()
    .catch((err) => {
        console.log(err)
    })
Parameters:
Name Type Attributes Default Description
prompt String

輸入提示詞字串,一律以stdin傳入子進程

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
exe String <optional>
'opencode'

輸入opencode執行檔名稱或絕對路徑字串,給予名稱時由execCli自系統PATH解析,預設'opencode'

model String <optional>
''

輸入模型ID字串,例如'opencode/muse-spark-1.3-contributor-free',預設''代表不帶-m旗標

key String <optional>
''

輸入該provider之API key字串,須與provider同時給予才會注入,預設''代表沿用CLI既有登入狀態

provider String <optional>
''

輸入key所屬provider名稱字串,須與key同時給予才會注入,且須與model為同一組,預設''

useStoredAuth Boolean <optional>
true

輸入未注入金鑰時是否沿用本機auth.json之登入布林值,false代表注入空憑證令本次以匿名存取(適用opencode之免費模型,避免結果隨本機登入帳號而異);已同時給key與provider時不作用,預設true

config Object | String <optional>
null

輸入opencode設定內容物件或其JSON字串,將以OPENCODE_CONFIG_CONTENT逐次注入,供補上第三方provider之定義,預設null代表沿用使用者既有設定檔

agent String <optional>
'build'

輸入opencode代理名稱字串,預設'build'

extraArgs Array <optional>
[]

輸入額外命令列旗標字串陣列,將接於固定旗標之後,預設[]

env Object <optional>

輸入本次調用額外注入之環境變數物件,同時給予key與provider時會再併入OPENCODE_AUTH_CONTENT,預設undefined

timeoutMs Number <optional>
300000

輸入逾時毫秒正整數,逾時將強制關閉子進程及其子孫程序,全套件統一預設300000

cwd String <optional>
process.cwd()

輸入子進程工作目錄字串,預設process.cwd();其絕對路徑並同步注入環境變數PWD(opencode以繼承之PWD優先於真實cwd決定session目錄,見檔頭),呼叫端env內之PWD會被覆寫

validate String | function <optional>

輸入stdout驗證規則字串或自訂驗證函數,規則字串支援'nonempty'、'json'、'min:100',多規則可用逗號串接,預設undefined代表不驗證

maxRetries Number <optional>
0

輸入失敗後最大重試次數非負整數,預設0

Returns:

回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(標準輸出字串)、stderr(標準錯誤字串)、code(離開碼)、error(錯誤訊息字串,成功時為空字串)、durationMs(耗時毫秒)、attempts(實際嘗試次數),本函數不會reject

Type
Promise

extractJsonLoose(text) → {Object|Array|null}

Description:
  • 從文字中抽取第一個完整的JSON物件或陣列

    特點: 先去除ANSI色碼與code fence標記後嘗試整段解析(最常見情境之最快路徑); 整段非法時自第一個{或[起以括號配對(跳過字串與跳脫)取得第一個完整片段再解析; 僅接受物件與陣列,純量(字串/數字/布林)回傳null; 括號未閉合(輸出被截斷)或片段非法一律回傳null,不throw

Source:
Example
import extractJsonLoose from './src/wkf/extractJsonLoose.mjs'

console.log(extractJsonLoose('{"a":1}'))
// => { a: 1 }

console.log(extractJsonLoose('說明文字\n```json\n{"a":1}\n```\n後記'))
// => { a: 1 }

console.log(extractJsonLoose('{"a":1')) //截斷
// => null

console.log(extractJsonLoose('純文字回覆'))
// => null
Parameters:
Name Type Description
text String

輸入AI回覆文字字串

Returns:

回傳解析成功之物件或陣列,失敗回傳null

Type
Object | Array | null

extractOutputText(output) → {String}

Description:
  • 自Responses API之output陣列取出文字內容(僅取message型元素之output_text)

    reasoning型元素為模型思考過程(Zen實測其content為空陣列),不屬回覆內容故略過; 多個message元素依序串接

Source:
Example
import { extractOutputText } from './src/dispatchApiOpenaiResponses.mjs'

let output = [
    { type: 'reasoning', content: [] },
    { type: 'message', content: [{ type: 'output_text', text: '完成' }] },
]
console.log(extractOutputText(output))
// => '完成'
Parameters:
Name Type Description
output Array

輸入回應之output陣列

Returns:

回傳串接後之文字內容,無有效內容回傳空字串

Type
String

(async) fetchQuotaJson(url, optopt) → {Promise}

Description:
  • 各額度查詢端點共用之HTTP請求並解析JSON

    本函數不會reject,一律以結果物件之ok與error欄位回報成敗, 並將失敗歸入機器可讀之errorType,令呼叫端無須解析錯誤訊息字串即可分流處置

Source:
Example
//need network

import fetchQuotaJson from './src/quota/fetchQuotaJson.mjs'

let test = async () => {
    let r = await fetchQuotaJson('https://api.anthropic.com/api/oauth/usage', {
        headers: { Authorization: 'Bearer xxx' },
    })
    console.log(r.ok, r.status)
    // => false 401
}
test()
Parameters:
Name Type Attributes Default Description
url String

輸入查詢端點網址字串

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
method String <optional>
'GET'

輸入HTTP方法字串,預設'GET'

body Object | String <optional>
null

輸入請求內容,給物件時序列化為JSON並自動補Content-Type,給字串時原樣送出,預設null代表無內容

headers Object <optional>
{}

輸入額外HTTP標頭物件,預設{}

timeoutMs Number <optional>
20000

輸入逾時毫秒正整數,涵蓋連線至本文讀完,逾時將中止連線,預設20000

maxBodyBytes Number <optional>
1048576

輸入回應本文上限位元組正整數,超過即中止並回errorType 'toolarge',預設1048576

redact Array <optional>
[]

輸入須自錯誤訊息中遮蔽之機密值字串陣列(例如權杖、帳號ID),長度不足6者不處理,預設[]

Returns:

回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、status(HTTP狀態碼整數,未連上為0)、data(解析後之JSON物件,失敗為null)、error(錯誤訊息字串,成功時為空字串,已套用redact)、errorType(錯誤類別字串:params/auth/forbidden/ratelimit/http/parse/timeout/network/toolarge)、durationMs(耗時毫秒),本函數不會reject

Type
Promise

finOf(r) → {Object}

Description:
  • 取結果之截斷資訊供tried各項記錄(REST文字類轉接器才帶, 其餘kind回空物件)

Source:
Parameters:
Name Type Description
r Object

輸入dispatchAi結果物件

Returns:

回傳物件,含truncated與finishReason,或空物件

Type
Object

firstStr(o, keys) → {String}

Description:
  • 取第一個為非空字串之鍵值

Source:
Parameters:
Name Type Description
o Object

輸入物件

keys Array

輸入鍵陣列

Returns:

回傳字串, 皆無回傳''

Type
String

fromBucket(g, b) → {Object|null}

Description:
  • 將群組內之單一桶正規化為統一窗口物件

Source:
Parameters:
Name Type Description
g Object

輸入群組物件

b Object

輸入桶物件

Returns:

回傳統一窗口物件, 桶無id或無剩餘比例回傳null

Type
Object | null

fromCodexUsageHttp(data) → {Object}

Description:
  • 將chatgpt.com之/wham/usage回應對映為統一結構

    主窗口(rate_limit.primary_window / secondary_window)、程式碼審查限額(code_review_rate_limit)、 以及寬容解析之additional_rate_limits皆納入windows;額外限額之窗口鍵為「<識別>:primary|secondary」, scope為其顯示名或識別。任何欄位缺漏皆略過該窗口而不拋錯

Source:
Example
import fromCodexUsageHttp from './src/quota/fromCodexUsageHttp.mjs'

let m = fromCodexUsageHttp({
    email: 'a@b.c',
    plan_type: 'plus',
    rate_limit: { primary_window: { used_percent: 42, limit_window_seconds: 18000, reset_after_seconds: 100 } },
    additional_rate_limits: [{ limit_name: 'codex-spark', primary_window: { used_percent: 12, limit_window_seconds: 18000 } }],
})
console.log(m.windows.map((w) => `${w.key}=${w.label}`))
// => [ 'primary=5小時', 'codex-spark:primary=5小時(codex-spark)' ]
Parameters:
Name Type Description
data Object

輸入/wham/usage之回應物件

Returns:

回傳物件,內含email(字串,無則'')、plan(字串,無則'')、windows(統一窗口陣列)、credits(點數與重置券資訊物件)

Type
Object

fromLimitItem(it) → {Object}

Description:
  • 將limits[]之單筆正規化為統一窗口物件

Source:
Parameters:
Name Type Description
it Object

輸入limits[]之單筆物件

Returns:

回傳統一窗口物件

Type
Object

fromRpcWindow(o, key, scope) → {Object|null}

Description:
  • 將app-server之單一窗口(primary/secondary)正規化為統一窗口物件

Source:
Parameters:
Name Type Description
o Object

輸入窗口物件{usedPercent, windowDurationMins, resetsAt}

key String

輸入窗口鍵字串

scope String

輸入範圍字串

Returns:

回傳統一窗口物件, 輸入非物件回傳null

Type
Object | null

fromTopField(data, key, windowSeconds, scope) → {Object|null}

Description:
  • 將頂層舊欄位正規化為統一窗口物件

Source:
Parameters:
Name Type Description
data Object

輸入端點回應物件

key String

輸入欄位鍵字串

windowSeconds Number

輸入窗口長度秒數

scope String

輸入適用範圍字串

Returns:

回傳統一窗口物件, 該欄位不存在或為null回傳null

Type
Object | null

fromWindow(o, key, scope) → {Object|null}

Description:
  • 將單一窗口{used_percent, limit_window_seconds, reset_at, reset_after_seconds}正規化

Source:
Parameters:
Name Type Description
o Object

輸入窗口物件

key String

輸入窗口鍵字串

scope String

輸入範圍字串

Returns:

回傳統一窗口物件, 輸入非物件回傳null

Type
Object | null

getCliArgs(…args) → {Array}

Description:
  • 將各段命令列參數展平為字串陣列,並濾除非有效字串

    各轉接器之參數為「固定旗標」加「可選旗標」加「額外旗標」之組合, 其中可選旗標於未給值時須整段消失(例如未給model就不可出現懸空的--model), 故呼叫端須以「整段陣列給或不給」表達,本函數僅負責展平與濾除非有效字串,不判斷旗標配對; 過濾之必要在於Nodejs之spawn要求各參數必為字串,混入undefined或數字會直接拋出TypeError, 破壞本套件「不reject、僅以結果物件回報」之約定

Source:
Example
import getCliArgs from './src/getCliArgs.mjs'

console.log(getCliArgs('-p', ['--model', 'sonnet']))
// => ['-p', '--model', 'sonnet']

console.log(getCliArgs('-p', null, 123, ['', '--verbose']))
// => ['-p', '--verbose']
Parameters:
Name Type Attributes Description
args String | Array <repeatable>

輸入參數字串或參數字串陣列,可給多個

Returns:

回傳展平且濾除非有效字串後之參數字串陣列

Type
Array

getErrorResult(error, errorTypeopt) → {Object}

Description:
  • 產生與execCli同結構之錯誤結果物件

    本套件各dispatch函數一律不reject,參數檢核失敗時即以本函數回傳錯誤結果物件, 其欄位與wsemi之execCli回傳結構一致,故呼叫端可用同一套欄位判斷成敗, 無須區分「參數錯誤」與「CLI執行失敗」兩種來源

Source:
Example
import getErrorResult from './src/getErrorResult.mjs'

console.log(getErrorResult('prompt must be a non-empty string'))
// => { ok: false, stdout: '', stderr: '', code: null, error: 'prompt must be a non-empty string', errorType: 'params', durationMs: 0, attempts: 0 }

console.log(getErrorResult(null).error)
// => 'unknown error'
Parameters:
Name Type Attributes Default Description
error String

輸入錯誤訊息字串

errorType String <optional>
'params'

輸入機器可讀之錯誤類別字串(一覽見getErrorType.mjs檔頭),預設'params'(參數/設定檢核失敗)

Returns:

回傳結果物件,內含ok(布林值,恆為false)、stdout(空字串)、stderr(空字串)、code(null)、error(錯誤訊息字串)、errorType(錯誤類別字串)、durationMs(0)、attempts(0)

Type
Object

getErrorType(r) → {String}

Description:
  • 由失敗結果物件推導機器可讀之errorType(僅機械可判者,其餘歸'exec',不猜測)

    判準與dispatchAiFallback之isKeyIndependentFail同組:error以TIMEOUT開頭為'timeout'、 含ENOENT或ENAMETOOLONG為'spawn'、恰為OUTPUT_VALIDATION_FAILED為'validation', 其餘失敗一律'exec'(各家CLI字樣不同且隨版本漂移,不維護簽章表)

Source:
Example
import getErrorType from './src/getErrorType.mjs'

console.log(getErrorType({ error: 'TIMEOUT after 300s' }))
// => 'timeout'

console.log(getErrorType({ error: 'spawn cli ENOENT' }))
// => 'spawn'

console.log(getErrorType({ error: 'Exit code 1' }))
// => 'exec'
Parameters:
Name Type Description
r Object

輸入失敗結果物件(取其error欄位判別)

Returns:

回傳errorType字串

Type
String

(async) getQuotaAntigravity(emailopt, optopt) → {Promise}

Description:
  • 查詢Antigravity(Google側, agy CLI)帳號之當前額度

    以agy自身之print模式取得額度(官方、無頭、不碰憑證):agy -p "/usage" --output-format json, 並以同一次執行之--log-file取得登入帳號email。 額度綁定本機agy之登入憑證,email參數之作用為比對——不符時ok為false且matched為false; 未給email時不比對。本函數不會reject,一律以結果物件之ok與error欄位回報成敗

Source:
Example
//need agy cli (>=1.1.11) logged in

import getQuotaAntigravity from './src/quota/getQuotaAntigravity.mjs'

let test = async () => {
    let r = await getQuotaAntigravity('firsemisphere@gmail.com')
    console.log(r.ok, r.email)
    // => true firsemisphere@gmail.com
    console.log(r.windows.map((w) => `${w.label}:${w.remainingPercent}%`).join(' '))
    // => 7天(Gemini Models):85% 5小時(Gemini Models):90% 7天(Claude and GPT models):100% 5小時(Claude and GPT models):100% (即時值)
}
test()
Parameters:
Name Type Attributes Default Description
email String <optional>
''

輸入欲查詢之帳號email字串,預設''代表不比對而直接回報本機當前帳號

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
exe String <optional>
'agy'

輸入agy執行檔名稱或路徑字串,預設'agy'

checkVersion Boolean <optional>
true

輸入是否先以agy --version把關版本布林值,低於1.1.11即回unsupported而不執行(舊版會把/usage當prompt跑而耗額度),預設true

withCredits Boolean <optional>
false

輸入是否另執行/credits取得G1點數餘額布林值,會多一次約5秒之啟動,預設false

cwd String <optional>
os.tmpdir()

輸入子進程工作目錄字串,預設系統暫存目錄以免在專案目錄留下痕跡

timeoutMs Number <optional>
60000

輸入單次agy執行之逾時毫秒正整數,預設60000(agy啟動加兩個後端往返實測5~8秒)

Returns:

回傳Promise,resolve回傳結果物件,內含ok、provider('antigravity')、email(agy登入帳號)、matched、plan(空字串,/usage不提供方案別)、planTier(空字串)、source('agy-print-usage')、windows(每群組之5小時與7天窗口,scope為群組名)、credits(withCredits時為{remainingCredits, upgradeUri},否則null)、raw(含agy版本、authMethod、原始回應)、error、errorType、durationMs,本函數不會reject

Type
Promise

(async) getQuotaClaude(emailopt, optopt) → {Promise}

Description:
  • 查詢Claude(Claude Code訂閱)帳號之當前額度

    額度綁定本機Claude Code之登入憑證,無「給email查任意帳號」之公開介面, 故email參數之作用為比對——查出本機實際登入者後與其核對, 不符時ok為false且matched為false,並於error載明本機實際登入之帳號; 未給email時不比對,直接回報本機當前帳號之額度。 本函數唯讀憑證、不自行刷新權杖(理由見檔頭);不會reject,一律以結果物件之ok與error欄位回報成敗

Source:
Example
//need claude code logged in

import getQuotaClaude from './src/quota/getQuotaClaude.mjs'

let test = async () => {
    let r = await getQuotaClaude('firsemisphere2@gmail.com')
    console.log(r.ok, r.plan)
    // => true max
    console.log(r.windows[0].label, r.windows[0].usedPercent)
    // => 5小時 11 (百分比為查詢當下之即時值, 每次不同)
}
test()
Parameters:
Name Type Attributes Default Description
email String <optional>
''

輸入欲查詢之帳號email字串,預設''代表不比對而直接回報本機當前帳號

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
configDir String <optional>

輸入Claude設定目錄字串,預設取環境變數CLAUDE_CONFIG_DIR,未設則/.claude

homeDir String <optional>
os.homedir()

輸入家目錄字串,僅於未指定configDir且未設環境變數時使用,供測試指向替身目錄

userAgent String <optional>
'claude-code/2.1.259'

輸入User-Agent字串,須為claude-code/<版本>形式否則端點將以嚴苛頻率限制回應429

profileFallback Boolean <optional>
true

輸入本機帳號檔無email時是否改打/api/oauth/profile取得布林值,預設true

usageUrl String <optional>
'https://api.anthropic.com/api/oauth/usage'

輸入額度端點網址字串,供測試指向假伺服器或經企業代理,預設官方端點

profileUrl String <optional>
'https://api.anthropic.com/api/oauth/profile'

輸入帳號端點網址字串,用途同usageUrl,預設官方端點

env Object <optional>
process.env

輸入環境變數來源物件(讀CLAUDE_CONFIG_DIR、非訂閱模式判定用之ANTHROPIC_API_KEY等;CLAUDE_CODE_OAUTH_TOKEN不作為權杖來源,僅於無憑證時用於錯誤提示),供測試隔離本機環境,預設process.env

timeoutMs Number <optional>
20000

輸入單次請求之逾時毫秒正整數,預設20000

Returns:

回傳Promise,resolve回傳結果物件,內含ok、provider('claude')、email(本機實際登入帳號)、matched、plan(方案別,例如'max')、planTier(級距,例如'default_claude_max_20x')、source('anthropic-oauth-usage-api')、windows(額度窗口陣列,含5小時、7天、7天模型別)、credits(額外用量與花費資訊)、raw(原始回應)、error、errorType、durationMs,本函數不會reject

Type
Promise

(async) getQuotaCodex(emailopt, optopt) → {Promise}

Description:
  • 查詢Codex(ChatGPT訂閱)帳號之當前額度

    主路徑以codex app-server之JSON-RPC取得帳號與額度(認證與刷新由codex自理), codex不存在或RPC失敗時退回直打chatgpt.com之用量端點。 額度綁定本機Codex CLI之登入憑證,無「給email查任意帳號」之公開介面, 故email參數之作用為比對——不符時ok為false且matched為false,並於error載明本機實際登入之帳號; 未給email時不比對。本函數不會reject,一律以結果物件之ok與error欄位回報成敗

Source:
Example
//need codex cli logged in

import getQuotaCodex from './src/quota/getQuotaCodex.mjs'

let test = async () => {
    let r = await getQuotaCodex('firsemisphere@gmail.com')
    console.log(r.ok, r.plan, r.source)
    // => true plus codex-app-server
    console.log(r.windows[0].label, r.windows[0].usedPercent)
    // => 5小時 37 (百分比為查詢當下之即時值, 每次不同)
}
test()
Parameters:
Name Type Attributes Default Description
email String <optional>
''

輸入欲查詢之帳號email字串,預設''代表不比對而直接回報本機當前帳號

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
exe String <optional>
'codex'

輸入codex執行檔名稱或路徑字串,預設'codex'

useAppServer Boolean <optional>
true

輸入是否以app-server為主路徑布林值,false代表直接走備援端點,預設true

fallbackHttp Boolean <optional>
true

輸入app-server失敗時是否退回備援端點布林值,預設true

codexHome String <optional>

輸入codex設定目錄字串,僅備援路徑讀其auth.json時生效;app-server主路徑之子進程繼承本進程環境,查的是本進程CODEX_HOME所指之帳號,不受此參數影響(要讓主路徑查別的目錄,須於本進程環境設CODEX_HOME,或以useAppServer:false改走備援),預設取環境變數CODEX_HOME,未設則/.codex

homeDir String <optional>
os.homedir()

輸入家目錄字串,僅於未指定codexHome且未設環境變數時使用

userAgent String <optional>
'codex-cli/0.153.0'

輸入備援端點之User-Agent字串

originator String <optional>
'codex_cli_rs'

輸入備援端點之originator標頭字串

usageUrl String <optional>
'https://chatgpt.com/backend-api/wham/usage'

輸入備援端點網址字串,供測試指向假伺服器或經企業代理,預設官方端點

env Object <optional>
process.env

輸入環境變數來源物件(讀CODEX_HOME與API key模式判定用之OPENAI_API_KEY),供測試隔離本機環境,預設process.env

timeoutMs Number <optional>
20000

輸入逾時毫秒正整數(app-server整體或單次HTTP),預設20000

Returns:

回傳Promise,resolve回傳結果物件,內含ok、provider('codex')、email、matched、plan(例如'plus')、planTier(空字串)、source('codex-app-server'或'chatgpt-wham-usage-api')、windows(含5小時與7天,另有依limitId分列之窗口)、credits(點數、重置券清單、限額狀態)、raw、error、errorType、durationMs,本函數不會reject

Type
Promise

initState(store) → {Object}

Description:
  • 初始化游標與冷卻狀態(store有效即載入持久化狀態, 否則用行程內記憶體)

Source:
Parameters:
Name Type Description
store Object

輸入狀態持久化物件{get,set},無效代表用行程內記憶體

Returns:

回傳物件,內含state(狀態物件,保證有cursors與cooling)與saveState(寫回函數,store無效或寫入失敗皆靜默)

Type
Object

isKeyIndependentFail(r) → {Boolean}

Description:
  • 判斷失敗結果是否與「哪一把金鑰」無關(換組內金鑰必然再敗, 應整組跳過)

Source:
Parameters:
Name Type Description
r Object

輸入dispatchAi失敗結果物件

Returns:

回傳是否應整組跳過之布林值

Type
Boolean

judgeTruncated(o) → {Object}

Description:
  • 裁定一次已截斷之回應應失敗或放行(呼叫端先判定truncated為true才呼叫)

Source:
Example
import { judgeTruncated } from './src/checkTruncation.mjs'

console.log(judgeTruncated({ finishReason: 'length', content: '', reasoningTokens: 600, label: 'finish_reason=length' }).error)
// => 'INCOMPLETE_RESPONSE: finish_reason=length; no visible output (reasoning may have used up the output token limit, reasoning_tokens=600)'

console.log(judgeTruncated({ finishReason: 'length', content: '[{"a":1},', acceptTruncated: true, label: 'finish_reason=length' }).accept)
// => true
Parameters:
Name Type Description
o Object

輸入設定物件

Properties
Name Type Attributes Default Description
finishReason String

輸入正規化後之終止原因字串,'length'才可能放行

content String | null

輸入可見輸出文字,null代表無

acceptTruncated Boolean <optional>
false

輸入呼叫端是否明示接受截斷內容布林值

validator function | null <optional>
null

輸入驗證函式,放行時交其裁決

reasoningTokens Number | null <optional>
null

輸入推理token數,可見輸出為空時附於訊息

label String

輸入錯誤訊息主體字串,例如'finish_reason=length'或'status=incomplete (max_output_tokens)'

Returns:

回傳物件,內含accept(是否放行布林值)、error(不放行時之錯誤訊息字串)、threw(validate拋錯訊息字串)

Type
Object

normalizeFinishReason(v) → {String}

Description:
  • 正規化終止原因字串(去頭尾空白、轉小寫;非字串回傳空字串)

Source:
Example
import { normalizeFinishReason } from './src/checkTruncation.mjs'

console.log(normalizeFinishReason(' LENGTH '), normalizeFinishReason(null))
// => 'length' ''
Parameters:
Name Type Description
v *

輸入原始終止原因

Returns:

回傳正規化字串

Type
String

pickWindow(it, snake, camel) → {Object|null}

Description:
  • 自單筆取出指定窗口(頂層、rate_limit、rateLimit之下, snake或camel)

Source:
Parameters:
Name Type Description
it Object

輸入單筆物件

snake String

輸入snake鍵, 例如'primary_window'

camel String

輸入camel鍵, 例如'primaryWindow'

Returns:

回傳窗口物件, 無回傳null

Type
Object | null

(async) readBodyCapped(r, maxBytes) → {Promise}

Description:
  • 讀取回應本文, 超過上限即中止

Source:
Parameters:
Name Type Description
r Object

輸入fetch之Response

maxBytes Number

輸入上限位元組

Returns:

回傳物件{ok, text, bytes}, 超限時ok為false且bytes為已讀位元組(或content-length)

Type
Promise

readEnvFile(file) → {Object}

Description:
  • 讀取.env檔為鍵值物件(金鑰來源, 不寫入process.env)

    特點: 只解析KEY=value形式(KEY限英數底線),忽略空行、註解與無效行; value修剪空白並剝除成對之首尾引號(單雙引號皆可); 檔案不存在或不可讀一律回空物件不throw(金鑰缺失交由resolveProviders之skipped回報)

Source:
Example
import readEnvFile from './src/readEnvFile.mjs'
import resolveProviders from './src/resolveProviders.mjs'
import providersAll from './src/providers.mjs'

//金鑰放.env(變數值以逗號分隔多把), 讀成物件交resolveProviders, 不污染process.env
let env = readEnvFile('./.env')
let { providers, skipped } = resolveProviders(providersAll, { env, pick: ['agnes:agnes-3.0-flash'] })
Parameters:
Name Type Description
file String

輸入.env檔案路徑字串

Returns:

回傳鍵值物件(變數名 → 字串值),讀取失敗回空物件

Type
Object

readJsonOrNull(fp) → {Object|Array|null}

Description:
  • 讀取JSON檔,任何失敗一律回傳null

    檔案不存在、無讀取權限、內容非合法JSON三種情形對呼叫端而言皆為「取不到」, 無須區分,故一律回null,由呼叫端決定後續處置(例如回報未登入)

Source:
Example
import readJsonOrNull from './src/quota/readJsonOrNull.mjs'

console.log(readJsonOrNull('./package.json') !== null)
// => true

console.log(readJsonOrNull('./no-such-file.json'))
// => null
Parameters:
Name Type Description
fp String

輸入檔案路徑字串

Returns:

回傳解析後之JSON值,讀取或解析失敗回傳null

Type
Object | Array | null

redactText(text, redact) → {String}

Description:
  • 將文字中之機密值替換為遮蔽字

Source:
Parameters:
Name Type Description
text String

輸入文字

redact Array

輸入機密值字串陣列

Returns:

回傳替換後文字

Type
String

reorderByCooling(providers, state, cooldownMs, saveState) → {Array}

Description:
  • 依冷卻狀態重排providers:冷卻中的條目「只降序不移除」——移到鏈尾, 前面全敗時仍會被嘗試, 故不存在把已恢復服務冰住的問題(此為與「金鑰停用清單」的關鍵差異, 後者已被否決)。 僅追蹤有明給id之條目(索引式id會因重排而錯位); 過期紀錄順手清除並寫回

Source:
Parameters:
Name Type Description
providers Array

輸入供應商條目陣列

state Object

輸入狀態物件(取其cooling)

cooldownMs Number

輸入冷卻視窗毫秒正整數

saveState function

輸入狀態寫回函數

Returns:

回傳重排後之條目陣列(active在前, 冷卻中殿後, 各自保持原相對順序)

Type
Array

resolveProviders(providers, optopt) → {Object}

Description:
  • 展開providers定義條目:envVar → keys,並可依id自選子集

    特點: 條目之envVar依env來源(預設process.env)展開為keys陣列(變數值以逗號分隔多把),envVar欄位自輸出移除; 缺對應環境變數(或值為空)之條目停用並列入skipped,不中斷不throw; 無envVar之條目(訂閱登入態CLI)原樣通過;條目已自帶有效keys者以keys為準; opt.pick可依id自選子集並以pick順序回傳(順序即遞補優先序); opt.exes依kind注入CLI執行檔路徑、opt.patch依id淺合併覆寫欄位——皆於展開後施作, providers與table同源產出故必然一致; 輸入陣列與條目皆不被改動(輸出為淺拷貝)

Source:
Example
import resolveProviders from './src/resolveProviders.mjs'
import readEnvFile from './src/readEnvFile.mjs'
import providersAll from './src/providers.mjs'

//金鑰放.env(變數值以逗號分隔多把), 以readEnvFile讀成物件, 不污染process.env
let env = readEnvFile('./.env')

//全取: envVar展開為keys, 缺環境變數者列入skipped
let { providers, table, skipped } = resolveProviders(providersAll, { env })
console.log(providers.length, skipped)
// => 17 []

//pick打錯字時, missing附拼寫提示hints(最接近之可用id)
let rm = resolveProviders(providersAll, { env, pick: ['poolside/laguna-s-2.1'] })
console.log(rm.missing, rm.hints)
// => [ 'poolside/laguna-s-2.1' ] { 'poolside/laguna-s-2.1': 'poolside:laguna-s-2.1' }

//自選: 依pick順序回傳(順序即遞補優先序), 可直接餵dispatchAiFallback
let r2 = resolveProviders(providersAll, { env, pick: ['agnes:agnes-3.0-flash', 'claude:sonnet'] })
console.log(r2.providers.map((p) => p.id))
// => [ 'agnes:agnes-3.0-flash', 'claude:sonnet' ]

//後處理: exes逐kind注入執行檔、patch逐id覆寫欄位, 陣列與table同步生效
let r3 = resolveProviders(providersAll, {
    env,
    pick: ['agnes:agnes-3.0-flash', 'claude:sonnet'],
    exes: { claude: 'C:/Users/x/.local/bin/claude.exe' },
    patch: { 'claude:sonnet': { timeoutMs: 360000 } },
})
console.log(r3.table['claude:sonnet'].exe, r3.table['claude:sonnet'].timeoutMs)
// => 'C:/Users/x/.local/bin/claude.exe' 360000

//table可直接餵dispatchAiWkf之providers定義表
//let wkf = dispatchAiWkf({ providers: r2.table, defaults: { timeoutMs: 1200000 } })
Parameters:
Name Type Attributes Default Description
providers Array

輸入providers條目物件陣列(如providers.mjs之預設匯出)

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
env Object <optional>
process.env

輸入金鑰來源物件(變數名 → 逗號分隔之金鑰字串),預設process.env。建議以readEnvFile('./.env')讀成物件傳入,不污染process.env

pick Array <optional>
null

輸入自選id字串陣列,依此順序回傳對應條目,查無之id列入missing,預設null代表全取(依原順序)

exes Object <optional>
null

輸入kind對執行檔絕對路徑之物件(如{claude:'C:/.../claude.exe'}),命中kind之條目補上exe欄位(條目已自帶exe者不覆寫——條目層設定優先),預設null代表不注入

patch Object <optional>
null

輸入id對部分欄位之物件(如{'claude:sonnet':{timeoutMs:360000}}),命中id之條目淺合併覆寫,於exes之後施作故可覆寫exe,預設null代表不覆寫

Returns:

回傳物件,內含providers(可直接餵dispatchAiFallback之條目陣列)、table(id對條目之物件,可直接餵dispatchAiWkf)、skipped(缺環境變數而停用之{id,envVar}陣列)、missing(pick查無之id字串陣列)、hints(missing id對最接近可用id之拼寫提示物件,pick查無多半是打錯字,附最接近id供直接定位;僅為相似度最高者非保證正解,無missing時為空物件)

Type
Object

(async) runFanout(optopt) → {Promise}

Description:
  • 執行Fanout工作流:多開執行與單點整合

    特點: 前段各名額並行執行同一任務,各名額可指定主模型(use)與自帶遞補鏈(fallback); 後段為單一整合名額,同樣可帶遞補鏈; 個別名額失敗不中斷整輪,成功候選未達minCandidates時以首位候選為成果(integrated:false)不硬整合; 成功候選完整保留於回傳(部分接受、便於接續重試整合); 本函數不會reject

Source:
Example
//need cli in system PATH

import runFanout from './src/wkf/runFanout.mjs'

let providers = {
    'agnes:agnes-3.0-flash': { kind: 'api-openai-compat', baseURL: 'https://apihub.agnes-ai.com/v1', model: 'agnes-3.0-flash', keys: ['sk-xxx'] },
    'claude:sonnet': { kind: 'claude', model: 'sonnet' },
}

let test = async () => {

    let r = await runFanout({
        providers,
        task: '分析並只回覆JSON: {"essence":"..."}',
        agents: [
            { use: 'agnes:agnes-3.0-flash', fallback: ['claude:sonnet'] },
            { use: 'claude:sonnet' },
        ],
        integrate: { use: 'claude:sonnet' },
        check: (j) => !!j.essence,
    })
    console.log(r.ok, r.integrated, r.candidates.length)
    // => true true 2

}
await test()
    .catch((err) => {
        console.log(err)
    })
Parameters:
Name Type Attributes Default Description
opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
providers Object

輸入provider定義表物件(名稱 → 條目),透傳callAiWithFallback

task String

輸入前段各名額共用之任務提示詞字串

agents Array

輸入前段名額規格陣列,各元素{ use, fallback, check?, maxRetries?, timeoutMs? }等(check可覆寫頂層檢核;除use/fallback/check/meta外之鍵覆寫該名額呼叫設定;meta為保留鍵,呼叫端自有資訊掛此鍵保證永不轉傳下層)

integrate Object

輸入整合名額規格物件{ use, fallback, prompt?, check?, ... },prompt可為(candidates)=>String自訂整合提示詞(省略用預設模板);check為終稿專屬檢核(終稿判準常與候選不同,如須含固定段落),未給則沿用頂層check;meta為保留鍵同agents

check function <optional>
null

輸入檢核函數(json)=>Boolean,作為候選與終稿之共用預設,名額規格與integrate可各自帶check覆寫,預設null

schema String <optional>
''

輸入輸出格式示意字串,供預設整合模板嵌入,預設''

minCandidates Number <optional>
2

輸入進入整合所需之最少成功候選數正整數,未達門檻以首位候選為成果,預設2

callOpt Object <optional>
{}

輸入透傳callAiWithFallback之共用設定(cwd、store、onEvent、timeoutMs、promptPrefix等),預設{}

Returns:

回傳Promise,resolve回傳結果物件,內含ok(布林值)、result(工作流成果)、integrated(是否經過整合布林值)、agents(各名額完整結果陣列)、candidates(成功候選陣列)、integrateDetail(整合呼叫完整結果)、totalMs(總耗時毫秒)、error(錯誤訊息字串),本函數不會reject

Type
Promise

(async) runFanoutPipeline(optopt) → {Promise}

Description:
  • 執行FanoutPipeline工作流(Fanout+RolePipeline):多開+整合+串行角色鏈

    特點: 前段同runFanout(agents各名額可自帶fallback、integrate單點整合); 後段同runRolePipeline(stages各階段可自帶AI/fallback/提示詞),其input即前段成果; 本函數不會reject

Source:
Example
//need cli in system PATH

import runFanoutPipeline from './src/wkf/runFanoutPipeline.mjs'

let providers = {
    'claude:sonnet': { kind: 'claude', model: 'sonnet' },
    'codex:gpt-5.6-luna': { kind: 'codex', model: 'gpt-5.6-luna' },
}

let test = async () => {

    let r = await runFanoutPipeline({
        providers,
        task: '分析並只回覆JSON: {"essence":"..."}',
        agents: [{ use: 'claude:sonnet' }, { use: 'codex:gpt-5.6-luna' }],
        integrate: { use: 'claude:sonnet' },
        stages: [
            { id: 'audit', use: 'codex:gpt-5.6-luna', prompt: (ctx) => `審計此稿並修訂, 只回覆同格式JSON: ${JSON.stringify(ctx.input)}` },
        ],
        check: (j) => !!j.essence,
    })
    console.log(r.ok, r.A.integrated, r.B.order)
    // => true true [ 'audit' ]

}
await test()
    .catch((err) => {
        console.log(err)
    })
Parameters:
Name Type Attributes Default Description
opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
providers Object

輸入provider定義表物件(名稱 → 條目)

task String

輸入前段各名額共用之任務提示詞字串

agents Array

輸入前段名額規格陣列(同runFanout)

integrate Object

輸入前段整合名額規格物件(同runFanout)

stages Array

輸入後段階段規格陣列(同runRolePipeline),各階段以ctx.input取得前段成果

check function <optional>
null

輸入前段共用檢核函數(前段名額規格與integrate可各自帶check覆寫,同runFanout),後段各階段自帶check,預設null

schema String <optional>
''

輸入輸出格式示意字串(供前段預設整合模板),預設''

minCandidates Number <optional>
2

輸入前段整合門檻正整數,預設2

callOpt Object <optional>
{}

輸入透傳兩段之共用呼叫設定,預設{}

Returns:

回傳Promise,resolve回傳結果物件,內含ok(布林值)、result(工作流成果)、A(前段runFanout完整結果)、B(後段runRolePipeline完整結果)、totalMs(總耗時毫秒)、error(錯誤訊息字串),本函數不會reject

Type
Promise

(async) runRolePipeline(optopt) → {Promise}

Description:
  • 執行RolePipeline工作流:多角色串行鏈

    特點: 各階段可各自指定use/fallback/prompt/check(含rawText純文字階段); 階段成果依序傳遞,最末階段成果即工作流成果; 失敗即止但已完成成果完整回傳(部分接受、便於接續重跑失敗段); 本函數不會reject

Source:
Example
//need cli in system PATH

import runRolePipeline from './src/wkf/runRolePipeline.mjs'

let providers = {
    'claude:sonnet': { kind: 'claude', model: 'sonnet' },
    'codex:gpt-5.6-luna': { kind: 'codex', model: 'gpt-5.6-luna' },
}

let test = async () => {

    let r = await runRolePipeline({
        providers,
        input: '原始任務',
        stages: [
            { id: 'draft', use: 'claude:sonnet', prompt: (ctx) => `就「${ctx.input}」寫初稿, 只回覆JSON: {"text":"..."}` },
            { id: 'review', use: 'codex:gpt-5.6-luna', prompt: (ctx) => `審閱並修訂, 只回覆同格式JSON: ${JSON.stringify(ctx.prev)}` },
        ],
    })
    console.log(r.ok, r.order, r.failedStage)
    // => true [ 'draft', 'review' ] null

}
await test()
    .catch((err) => {
        console.log(err)
    })
Parameters:
Name Type Attributes Default Description
opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
providers Object

輸入provider定義表物件(名稱 → 條目)

input * <optional>
null

輸入工作流輸入(原始任務字串或前一工作流之成果物件),提供給各階段ctx.input,預設null

stages Array

輸入階段規格陣列,各元素{ id, use, fallback, prompt:(ctx)=>String, check?, rawText?, maxRetries?, timeoutMs? }等;meta為保留鍵,呼叫端自有資訊(分類、標籤)掛此鍵保證永不轉傳下層

callOpt Object <optional>
{}

輸入透傳callAiWithFallback之共用設定,預設{}

Returns:

回傳Promise,resolve回傳結果物件,內含ok(布林值)、result(最末階段成果)、stages(id對階段完整呼叫結果之物件)、results(id對階段成果之物件)、order(階段id順序陣列)、failedStage(失敗階段id,無失敗為null)、totalMs(總耗時毫秒)、error(錯誤訊息字串),本函數不會reject

Type
Promise

safeValidate(validator, text) → {Object}

Description:
  • 以不拋錯方式執行驗證函式(拋錯視同拒絕)

Source:
Example
import { safeValidate } from './src/checkTruncation.mjs'

console.log(safeValidate((s) => s.length > 1, 'ab'))
// => { pass: true, threw: '' }

console.log(safeValidate(() => { throw new Error('x') }, 'ab'))
// => { pass: false, threw: 'x' }
Parameters:
Name Type Description
validator function

輸入驗證函式(text)=>Boolean

text String

輸入待驗證文字

Returns:

回傳物件,內含pass(是否通過布林值)與threw(拋錯訊息字串,未拋錯為空字串)

Type
Object

salvageTruncatedArray(text) → {Array|null}

Description:
  • 自截斷的JSON陣列文字搶救出前段完整元素(頂層元素須為物件)

    特點: 自第一個[起逐字元掃描(字串與跳脫感知),追蹤「最後一個完整結束的頂層物件元素」, 補上]重新解析——回傳的每個元素皆完整合法,不是半成品; 陣列有正常閉合(=非截斷)、無[、或救回後為空陣列,一律回null不硬救; 典型用法:extractJsonLoose回null時之後備,或組成自訂parse注入callAiWithFallback

Source:
Example
import salvageTruncatedArray from './src/wkf/salvageTruncatedArray.mjs'

console.log(salvageTruncatedArray('[{"a":1},{"b":2},{"c":'))
// => [ { a: 1 }, { b: 2 } ]

console.log(salvageTruncatedArray('[{"a":1}]')) //有閉合=非截斷, 不在搶救範圍
// => null
Parameters:
Name Type Description
text String

輸入模型輸出文字字串

Returns:

回傳搶救出之非空陣列,無法搶救回null

Type
Array | null

toQuotaLabel(sec) → {String}

Description:
  • 由額度窗口長度秒數推導人類可讀之中文標籤

    能整除日數即以「N天」表示,其次「N小時」、「N分鐘」,皆不整除則「N秒」; 無效或非正數回傳空字串,令呼叫端可用空字串判斷「供應商未提供窗口長度」

Source:
Example
import toQuotaLabel from './src/quota/toQuotaLabel.mjs'

console.log(toQuotaLabel(18000))
// => 5小時

console.log(toQuotaLabel(604800))
// => 7天

console.log(toQuotaLabel(900))
// => 15分鐘

console.log(toQuotaLabel(null))
// => (空字串)
Parameters:
Name Type Description
sec Number

輸入窗口長度秒數

Returns:

回傳中文標籤字串,無效秒數回傳空字串

Type
String

toQuotaResult(provider, optopt) → {Object}

Description:
  • 將各家額度查詢結果正規化為統一結構,並判定帳號是否相符

    帳號比對採去空白且不分大小寫之比較,因email本地部分雖理論上區分大小寫, 但三家供應商實務上皆以不分大小寫視為同一帳號; 未指定email時不做比對,matched為null,ok僅取決於查詢本身是否成功

Source:
Example
import toQuotaResult from './src/quota/toQuotaResult.mjs'

let r = toQuotaResult('claude', { email: 'a@b.com', emailWant: 'c@d.com' })
console.log(r.ok, r.matched)
// => false false
Parameters:
Name Type Attributes Default Description
provider String

輸入供應商種類字串,例如'claude'、'codex'、'antigravity'

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
email String <optional>
''

輸入本機該CLI實際登入之帳號email字串,預設''代表無從取得

emailWant String <optional>
''

輸入呼叫端指定欲查詢之帳號email字串,預設''代表不比對

plan String <optional>
''

輸入方案別字串,例如'max'、'plus',預設''

planTier String <optional>
''

輸入方案細部級距字串,例如'default_claude_max_20x',預設''

source String <optional>
''

輸入資料來源介面字串,例如'oauth-usage-api'、'codex-app-server'、'agy-print',預設''

windows Array <optional>
[]

輸入額度窗口物件陣列,預設[]

credits Object <optional>
null

輸入額外用量或點數資訊物件,預設null代表該供應商無此概念或未啟用

raw Object <optional>
null

輸入供應商原始回應物件,預設null

error String <optional>
''

輸入錯誤訊息字串,預設''代表查詢成功

errorType String <optional>
''

輸入機器可讀之錯誤類別字串,預設''

durationMs Number <optional>
0

輸入耗時毫秒,預設0

Returns:

回傳結果物件,內含ok(查詢成功且帳號相符布林值)、provider、email(本機實際登入帳號)、matched(帳號是否相符布林值,未指定email時為null)、plan、planTier、source、windows(窗口陣列)、credits、raw、error、errorType、durationMs

Type
Object

toQuotaScopedLabel(windowSeconds, scope) → {String}

Description:
  • 組出帶適用範圍之額度窗口標籤

    無範圍時回傳空字串,令toQuotaWindow自行由windowSeconds推導預設標籤; 有範圍但窗口秒數無效(供應商未提供)時僅以範圍為標籤

Source:
Example
import toQuotaScopedLabel from './src/quota/toQuotaScopedLabel.mjs'

console.log(toQuotaScopedLabel(604800, 'Fable'))
// => 7天(Fable)

console.log(toQuotaScopedLabel(null, 'codex-spark'))
// => codex-spark

console.log(toQuotaScopedLabel(18000, ''))
// => (空字串)
Parameters:
Name Type Description
windowSeconds Number

輸入窗口長度秒數

scope String

輸入適用範圍字串,例如模型名或群組名

Returns:

回傳標籤字串,例如'7天(Fable)';無範圍回傳''

Type
String

toQuotaWindow(optopt) → {Object}

Description:
  • 將各家額度窗口正規化為統一結構

    resetAt可給ISO字串或unix秒數(整數),一律轉為ISO字串; resetAt與resetAfterSeconds任一有值即自動補算另一; usedPercent夾至0~100,remainingPercent由其推得,未知時兩者皆為null(不假造100); label未給時由windowSeconds推導,故供應商調整窗口長度時標籤自動跟著正確

Source:
Example
import toQuotaWindow from './src/quota/toQuotaWindow.mjs'

let w = toQuotaWindow({ key: 'five_hour', windowSeconds: 18000, usedPercent: 11 })
console.log(w.label, w.remainingPercent)
// => 5小時 89
Parameters:
Name Type Attributes Default Description
opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
key String <optional>
''

輸入窗口之機器可讀鍵字串,為供應商原生識別原樣透傳(Claude之limits[].kind如'session'、Codex之'primary'/'secondary'、agy之buckets[].id如'gemini-5h'),各家不同且刻意不統一;跨家比較請用windowSeconds配scope而非key,預設''

label String <optional>
''

輸入窗口之人類可讀標籤字串,預設''代表由windowSeconds推導

windowSeconds Number <optional>
null

輸入窗口長度秒數,預設null代表供應商未提供

usedPercent Number <optional>
null

輸入已用百分比數值(0~100),預設null代表未知

resetAt String | Number <optional>
''

輸入窗口重置時刻,可為ISO字串或unix秒數整數,預設''

resetAfterSeconds Number <optional>
null

輸入距重置之剩餘秒數,預設null代表由resetAt推算

scope String <optional>
''

輸入窗口適用範圍字串,例如模型名稱或群組名稱,預設''代表全域

active Boolean <optional>
false

輸入是否為當前生效(最先觸頂)窗口布林值,預設false

severity String <optional>
''

輸入嚴重度字串,例如'normal'、'warning'、'exhausted',預設''代表由usedPercent推導(用罄為'exhausted'、其餘'normal'、usedPercent未知則'')

Returns:

回傳窗口物件,內含key(供應商原生識別,各家不同)、label、windowSeconds(跨家比較之正規化維度:18000=5小時、604800=7天)、usedPercent、remainingPercent、resetAt(ISO字串)、resetAfterSeconds、scope、active、severity

Type
Object

(async) viaAppServer(opt) → {Promise}

Description:
  • 以app-server取得額度並正規化

Source:
Parameters:
Name Type Description
opt Object

輸入設定物件

Returns:

回傳結果片段物件{ok, email, plan, accountType, windows, credits, raw, error, errorType}

Type
Promise

(async) viaHttp(opt, codexHome, timeoutMs) → {Promise}

Description:
  • 以備援端點取得額度並正規化

Source:
Parameters:
Name Type Description
opt Object

輸入設定物件

codexHome String

輸入codex設定目錄字串

timeoutMs Number

輸入逾時毫秒

Returns:

回傳結果片段物件{ok, email, plan, windows, credits, raw, error, errorType}

Type
Promise

windowToSeconds(w) → {Number|null}

Description:
  • 由桶之window字串推得窗口秒數

Source:
Parameters:
Name Type Description
w String

輸入window字串, 例如'5h'、'weekly'

Returns:

回傳秒數, 無法推得回傳null

Type
Number | null