w-dispatch-ai 1.0.21 → 1.0.23

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.
Files changed (82) hide show
  1. package/README.md +627 -569
  2. package/dist/w-dispatch-ai.umd.js +2 -2
  3. package/dist/w-dispatch-ai.umd.js.map +1 -1
  4. package/docs/WDispatchAi.mjs.html +12 -4
  5. package/docs/adapters.mjs.html +5 -3
  6. package/docs/budgetFor.mjs.html +2 -2
  7. package/docs/buildValidator.mjs.html +166 -0
  8. package/docs/castPintOr.mjs.html +2 -2
  9. package/docs/dfTimeoutMs.mjs.html +2 -2
  10. package/docs/dispatchAi.mjs.html +2 -2
  11. package/docs/dispatchAiFallback.mjs.html +2 -2
  12. package/docs/dispatchAiWkf.mjs.html +2 -2
  13. package/docs/dispatchAntigravity.mjs.html +2 -2
  14. package/docs/dispatchApiOpenaiCompat.mjs.html +4 -68
  15. package/docs/dispatchApiOpenaiResponses.mjs.html +498 -0
  16. package/docs/dispatchClaude.mjs.html +2 -2
  17. package/docs/dispatchCodex.mjs.html +2 -2
  18. package/docs/dispatchOpencode.mjs.html +2 -2
  19. package/docs/getCliArgs.mjs.html +2 -2
  20. package/docs/getErrorResult.mjs.html +2 -2
  21. package/docs/getErrorType.mjs.html +5 -3
  22. package/docs/global.html +11790 -3425
  23. package/docs/index.html +2 -2
  24. package/docs/quota_dfQuotaTimeoutMs.mjs.html +99 -0
  25. package/docs/quota_fetchQuotaJson.mjs.html +348 -0
  26. package/docs/quota_fromCodexUsageHttp.mjs.html +335 -0
  27. package/docs/quota_getQuotaAntigravity.mjs.html +463 -0
  28. package/docs/quota_getQuotaClaude.mjs.html +457 -0
  29. package/docs/quota_getQuotaCodex.mjs.html +515 -0
  30. package/docs/quota_readJsonOrNull.mjs.html +113 -0
  31. package/docs/quota_toQuotaLabel.mjs.html +146 -0
  32. package/docs/quota_toQuotaResult.mjs.html +214 -0
  33. package/docs/quota_toQuotaScopedLabel.mjs.html +117 -0
  34. package/docs/quota_toQuotaWindow.mjs.html +239 -0
  35. package/docs/readEnvFile.mjs.html +2 -2
  36. package/docs/resolveProviders.mjs.html +3 -3
  37. package/docs/wkf_callAiWithFallback.mjs.html +2 -2
  38. package/docs/wkf_createFileStore.mjs.html +2 -2
  39. package/docs/wkf_createUsageCounter.mjs.html +2 -2
  40. package/docs/wkf_extractJsonLoose.mjs.html +2 -2
  41. package/docs/wkf_noSideEffectPrefix.mjs.html +2 -2
  42. package/docs/wkf_runFanout.mjs.html +2 -2
  43. package/docs/wkf_runFanoutPipeline.mjs.html +2 -2
  44. package/docs/wkf_runRolePipeline.mjs.html +2 -2
  45. package/docs/wkf_salvageTruncatedArray.mjs.html +2 -2
  46. package/g.mjs +2 -2
  47. package/package.json +2 -2
  48. package/src/WDispatchAi.mjs +10 -2
  49. package/src/adapters.mjs +3 -1
  50. package/src/buildValidator.mjs +94 -0
  51. package/src/dispatchApiOpenaiCompat.mjs +2 -66
  52. package/src/dispatchApiOpenaiResponses.mjs +426 -0
  53. package/src/getErrorType.mjs +3 -1
  54. package/src/providers.mjs +303 -213
  55. package/src/quota/dfQuotaTimeoutMs.mjs +27 -0
  56. package/src/quota/fetchQuotaJson.mjs +276 -0
  57. package/src/quota/fromCodexUsageHttp.mjs +263 -0
  58. package/src/quota/getQuotaAntigravity.mjs +391 -0
  59. package/src/quota/getQuotaClaude.mjs +385 -0
  60. package/src/quota/getQuotaCodex.mjs +443 -0
  61. package/src/quota/readJsonOrNull.mjs +41 -0
  62. package/src/quota/toQuotaLabel.mjs +74 -0
  63. package/src/quota/toQuotaResult.mjs +142 -0
  64. package/src/quota/toQuotaScopedLabel.mjs +45 -0
  65. package/src/quota/toQuotaWindow.mjs +167 -0
  66. package/src/resolveProviders.mjs +1 -1
  67. package/test/tools/fakeCliForTest.mjs +4 -3
  68. package/test/tools/fakeServerForApiTest.mjs +108 -3
  69. package/test/tools/fakeServerForQuotaTest.mjs +171 -0
  70. package/test/unit-WDispatchAi.test.mjs +19 -6
  71. package/test/unit-adapters.test.mjs +5 -3
  72. package/test/unit-dispatchAi.test.mjs +1 -1
  73. package/test/unit-dispatchApiOpenaiResponses.test.mjs +198 -0
  74. package/test/unit-fetchQuotaJson.test.mjs +70 -0
  75. package/test/unit-fromCodexUsageHttp.test.mjs +76 -0
  76. package/test/unit-getQuotaAntigravity.test.mjs +123 -0
  77. package/test/unit-getQuotaClaude.test.mjs +137 -0
  78. package/test/unit-getQuotaCodex.test.mjs +191 -0
  79. package/test/unit-readJsonOrNull.test.mjs +44 -0
  80. package/test/unit-toQuotaLabel.test.mjs +43 -0
  81. package/test/unit-toQuotaResult.test.mjs +53 -0
  82. package/test/unit-toQuotaWindow.test.mjs +73 -0
@@ -0,0 +1,146 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>quota/toQuotaLabel.mjs - Documentation</title>
7
+
8
+
9
+ <script src="scripts/prettify/prettify.js"></script>
10
+ <script src="scripts/prettify/lang-css.js"></script>
11
+ <!--[if lt IE 9]>
12
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13
+ <![endif]-->
14
+ <link type="text/css" rel="stylesheet" href="styles/prettify.css">
15
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
16
+ <script src="scripts/nav.js" defer></script>
17
+
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ </head>
20
+ <body>
21
+
22
+ <input type="checkbox" id="nav-trigger" class="nav-trigger" />
23
+ <label for="nav-trigger" class="navicon-button x">
24
+ <div class="navicon"></div>
25
+ </label>
26
+
27
+ <label for="nav-trigger" class="overlay"></label>
28
+
29
+ <nav >
30
+
31
+
32
+ <h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#NO_SIDE_EFFECT">NO_SIDE_EFFECT</a></li><li><a href="global.html#WDispatchAi">WDispatchAi</a></li><li><a href="global.html#adapters">adapters</a></li><li><a href="global.html#agyPrint">agyPrint</a></li><li><a href="global.html#attachErrorType">attachErrorType</a></li><li><a href="global.html#budgetFor">budgetFor</a></li><li><a href="global.html#buildChain">buildChain</a></li><li><a href="global.html#buildValidator">buildValidator</a></li><li><a href="global.html#callAiWithFallback">callAiWithFallback</a></li><li><a href="global.html#callOnce">callOnce</a></li><li><a href="global.html#castPintOr">castPintOr</a></li><li><a href="global.html#collectAdditional">collectAdditional</a></li><li><a href="global.html#createFileStore">createFileStore</a></li><li><a href="global.html#createUsageCounter">createUsageCounter</a></li><li><a href="global.html#decodeJwtPayload">decodeJwtPayload</a></li><li><a href="global.html#defaultIntegratePrompt">defaultIntegratePrompt</a></li><li><a href="global.html#dfQuotaTimeoutMs">dfQuotaTimeoutMs</a></li><li><a href="global.html#dfTimeoutMs">dfTimeoutMs</a></li><li><a href="global.html#directWindow">directWindow</a></li><li><a href="global.html#dispatchAi">dispatchAi</a></li><li><a href="global.html#dispatchAiFallback">dispatchAiFallback</a></li><li><a href="global.html#dispatchAiWkf">dispatchAiWkf</a></li><li><a href="global.html#dispatchAntigravity">dispatchAntigravity</a></li><li><a href="global.html#dispatchApiOpenaiCompat">dispatchApiOpenaiCompat</a></li><li><a href="global.html#dispatchApiOpenaiResponses">dispatchApiOpenaiResponses</a></li><li><a href="global.html#dispatchClaude">dispatchClaude</a></li><li><a href="global.html#dispatchCodex">dispatchCodex</a></li><li><a href="global.html#dispatchOpencode">dispatchOpencode</a></li><li><a href="global.html#extractJsonLoose">extractJsonLoose</a></li><li><a href="global.html#extractOutputText">extractOutputText</a></li><li><a href="global.html#fetchQuotaJson">fetchQuotaJson</a></li><li><a href="global.html#firstStr">firstStr</a></li><li><a href="global.html#fromBucket">fromBucket</a></li><li><a href="global.html#fromCodexUsageHttp">fromCodexUsageHttp</a></li><li><a href="global.html#fromLimitItem">fromLimitItem</a></li><li><a href="global.html#fromRpcWindow">fromRpcWindow</a></li><li><a href="global.html#fromTopField">fromTopField</a></li><li><a href="global.html#fromWindow">fromWindow</a></li><li><a href="global.html#getCliArgs">getCliArgs</a></li><li><a href="global.html#getErrorResult">getErrorResult</a></li><li><a href="global.html#getErrorType">getErrorType</a></li><li><a href="global.html#getQuotaAntigravity">getQuotaAntigravity</a></li><li><a href="global.html#getQuotaClaude">getQuotaClaude</a></li><li><a href="global.html#getQuotaCodex">getQuotaCodex</a></li><li><a href="global.html#initState">initState</a></li><li><a href="global.html#isKeyIndependentFail">isKeyIndependentFail</a></li><li><a href="global.html#pickWindow">pickWindow</a></li><li><a href="global.html#readBodyCapped">readBodyCapped</a></li><li><a href="global.html#readEnvFile">readEnvFile</a></li><li><a href="global.html#readJsonOrNull">readJsonOrNull</a></li><li><a href="global.html#redactText">redactText</a></li><li><a href="global.html#reorderByCooling">reorderByCooling</a></li><li><a href="global.html#resolveProviders">resolveProviders</a></li><li><a href="global.html#runFanout">runFanout</a></li><li><a href="global.html#runFanoutPipeline">runFanoutPipeline</a></li><li><a href="global.html#runRolePipeline">runRolePipeline</a></li><li><a href="global.html#salvageTruncatedArray">salvageTruncatedArray</a></li><li><a href="global.html#toQuotaLabel">toQuotaLabel</a></li><li><a href="global.html#toQuotaResult">toQuotaResult</a></li><li><a href="global.html#toQuotaScopedLabel">toQuotaScopedLabel</a></li><li><a href="global.html#toQuotaWindow">toQuotaWindow</a></li><li><a href="global.html#viaAppServer">viaAppServer</a></li><li><a href="global.html#viaHttp">viaHttp</a></li><li><a href="global.html#windowToSeconds">windowToSeconds</a></li></ul>
33
+
34
+ </nav>
35
+
36
+ <div id="main">
37
+
38
+ <h1 class="page-title">quota/toQuotaLabel.mjs</h1>
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+ <section>
47
+ <article>
48
+ <pre class="prettyprint source linenums"><code>import isnum from 'wsemi/src/isnum.mjs'
49
+ import cdbl from 'wsemi/src/cdbl.mjs'
50
+
51
+
52
+ // toQuotaLabel.mjs — 由額度窗口長度秒數推導人類可讀之中文標籤(單一來源)
53
+ //
54
+ // 【為何獨立成檔】toQuotaWindow推導預設標籤用它; 各轉接器對「帶模型別或群組別」之窗口
55
+ // 須自組「7天(Fable)」「5小時(Gemini)」這類標籤, 也用它取基底。
56
+ // 原版把此邏輯藏在toQuotaWindow內部, 轉接器只好各自手寫「7天(」字串,
57
+ // 結果session窗口與weekly窗口之標籤不對稱(前者漏了「5小時」基底); 抽出後只剩一份規則。
58
+ //
59
+ // 【為何由秒數推導而非寫死】寫死「5小時/7天」等於把當下的窗口長度當成永久事實,
60
+ // 供應商調整窗口時字面會說謊且無人察覺; 由秒數推導則窗口一變標籤即隨之正確。
61
+
62
+
63
+ //每分鐘/每小時/每日之秒數
64
+ let SEC_MIN = 60
65
+ let SEC_HOUR = 3600
66
+ let SEC_DAY = 86400
67
+
68
+
69
+ /**
70
+ * 由額度窗口長度秒數推導人類可讀之中文標籤
71
+ *
72
+ * 能整除日數即以「N天」表示,其次「N小時」、「N分鐘」,皆不整除則「N秒」;
73
+ * 無效或非正數回傳空字串,令呼叫端可用空字串判斷「供應商未提供窗口長度」
74
+ *
75
+ * @param {Number} sec 輸入窗口長度秒數
76
+ * @returns {String} 回傳中文標籤字串,無效秒數回傳空字串
77
+ * @example
78
+ *
79
+ * import toQuotaLabel from './src/quota/toQuotaLabel.mjs'
80
+ *
81
+ * console.log(toQuotaLabel(18000))
82
+ * // => 5小時
83
+ *
84
+ * console.log(toQuotaLabel(604800))
85
+ * // => 7天
86
+ *
87
+ * console.log(toQuotaLabel(900))
88
+ * // => 15分鐘
89
+ *
90
+ * console.log(toQuotaLabel(null))
91
+ * // => (空字串)
92
+ *
93
+ */
94
+ function toQuotaLabel(sec) {
95
+
96
+ //check, 無效或非正數無從推導
97
+ if (!isnum(sec) || cdbl(sec) &lt;= 0) {
98
+ return ''
99
+ }
100
+ sec = cdbl(sec)
101
+
102
+ //day
103
+ if (sec % SEC_DAY === 0) {
104
+ return `${sec / SEC_DAY}天`
105
+ }
106
+
107
+ //hour
108
+ if (sec % SEC_HOUR === 0) {
109
+ return `${sec / SEC_HOUR}小時`
110
+ }
111
+
112
+ //minute
113
+ if (sec % SEC_MIN === 0) {
114
+ return `${sec / SEC_MIN}分鐘`
115
+ }
116
+
117
+ return `${sec}秒`
118
+ }
119
+
120
+
121
+ export default toQuotaLabel
122
+ </code></pre>
123
+ </article>
124
+ </section>
125
+
126
+
127
+
128
+
129
+
130
+
131
+ </div>
132
+
133
+ <br class="clear">
134
+
135
+ <footer>
136
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Sep 05 2026 22:03:08 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
137
+ </footer>
138
+
139
+ <script>prettyPrint();</script>
140
+ <script src="scripts/polyfill.js"></script>
141
+ <script src="scripts/linenumber.js"></script>
142
+
143
+
144
+
145
+ </body>
146
+ </html>
@@ -0,0 +1,214 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>quota/toQuotaResult.mjs - Documentation</title>
7
+
8
+
9
+ <script src="scripts/prettify/prettify.js"></script>
10
+ <script src="scripts/prettify/lang-css.js"></script>
11
+ <!--[if lt IE 9]>
12
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13
+ <![endif]-->
14
+ <link type="text/css" rel="stylesheet" href="styles/prettify.css">
15
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
16
+ <script src="scripts/nav.js" defer></script>
17
+
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ </head>
20
+ <body>
21
+
22
+ <input type="checkbox" id="nav-trigger" class="nav-trigger" />
23
+ <label for="nav-trigger" class="navicon-button x">
24
+ <div class="navicon"></div>
25
+ </label>
26
+
27
+ <label for="nav-trigger" class="overlay"></label>
28
+
29
+ <nav >
30
+
31
+
32
+ <h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#NO_SIDE_EFFECT">NO_SIDE_EFFECT</a></li><li><a href="global.html#WDispatchAi">WDispatchAi</a></li><li><a href="global.html#adapters">adapters</a></li><li><a href="global.html#agyPrint">agyPrint</a></li><li><a href="global.html#attachErrorType">attachErrorType</a></li><li><a href="global.html#budgetFor">budgetFor</a></li><li><a href="global.html#buildChain">buildChain</a></li><li><a href="global.html#buildValidator">buildValidator</a></li><li><a href="global.html#callAiWithFallback">callAiWithFallback</a></li><li><a href="global.html#callOnce">callOnce</a></li><li><a href="global.html#castPintOr">castPintOr</a></li><li><a href="global.html#collectAdditional">collectAdditional</a></li><li><a href="global.html#createFileStore">createFileStore</a></li><li><a href="global.html#createUsageCounter">createUsageCounter</a></li><li><a href="global.html#decodeJwtPayload">decodeJwtPayload</a></li><li><a href="global.html#defaultIntegratePrompt">defaultIntegratePrompt</a></li><li><a href="global.html#dfQuotaTimeoutMs">dfQuotaTimeoutMs</a></li><li><a href="global.html#dfTimeoutMs">dfTimeoutMs</a></li><li><a href="global.html#directWindow">directWindow</a></li><li><a href="global.html#dispatchAi">dispatchAi</a></li><li><a href="global.html#dispatchAiFallback">dispatchAiFallback</a></li><li><a href="global.html#dispatchAiWkf">dispatchAiWkf</a></li><li><a href="global.html#dispatchAntigravity">dispatchAntigravity</a></li><li><a href="global.html#dispatchApiOpenaiCompat">dispatchApiOpenaiCompat</a></li><li><a href="global.html#dispatchApiOpenaiResponses">dispatchApiOpenaiResponses</a></li><li><a href="global.html#dispatchClaude">dispatchClaude</a></li><li><a href="global.html#dispatchCodex">dispatchCodex</a></li><li><a href="global.html#dispatchOpencode">dispatchOpencode</a></li><li><a href="global.html#extractJsonLoose">extractJsonLoose</a></li><li><a href="global.html#extractOutputText">extractOutputText</a></li><li><a href="global.html#fetchQuotaJson">fetchQuotaJson</a></li><li><a href="global.html#firstStr">firstStr</a></li><li><a href="global.html#fromBucket">fromBucket</a></li><li><a href="global.html#fromCodexUsageHttp">fromCodexUsageHttp</a></li><li><a href="global.html#fromLimitItem">fromLimitItem</a></li><li><a href="global.html#fromRpcWindow">fromRpcWindow</a></li><li><a href="global.html#fromTopField">fromTopField</a></li><li><a href="global.html#fromWindow">fromWindow</a></li><li><a href="global.html#getCliArgs">getCliArgs</a></li><li><a href="global.html#getErrorResult">getErrorResult</a></li><li><a href="global.html#getErrorType">getErrorType</a></li><li><a href="global.html#getQuotaAntigravity">getQuotaAntigravity</a></li><li><a href="global.html#getQuotaClaude">getQuotaClaude</a></li><li><a href="global.html#getQuotaCodex">getQuotaCodex</a></li><li><a href="global.html#initState">initState</a></li><li><a href="global.html#isKeyIndependentFail">isKeyIndependentFail</a></li><li><a href="global.html#pickWindow">pickWindow</a></li><li><a href="global.html#readBodyCapped">readBodyCapped</a></li><li><a href="global.html#readEnvFile">readEnvFile</a></li><li><a href="global.html#readJsonOrNull">readJsonOrNull</a></li><li><a href="global.html#redactText">redactText</a></li><li><a href="global.html#reorderByCooling">reorderByCooling</a></li><li><a href="global.html#resolveProviders">resolveProviders</a></li><li><a href="global.html#runFanout">runFanout</a></li><li><a href="global.html#runFanoutPipeline">runFanoutPipeline</a></li><li><a href="global.html#runRolePipeline">runRolePipeline</a></li><li><a href="global.html#salvageTruncatedArray">salvageTruncatedArray</a></li><li><a href="global.html#toQuotaLabel">toQuotaLabel</a></li><li><a href="global.html#toQuotaResult">toQuotaResult</a></li><li><a href="global.html#toQuotaScopedLabel">toQuotaScopedLabel</a></li><li><a href="global.html#toQuotaWindow">toQuotaWindow</a></li><li><a href="global.html#viaAppServer">viaAppServer</a></li><li><a href="global.html#viaHttp">viaHttp</a></li><li><a href="global.html#windowToSeconds">windowToSeconds</a></li></ul>
33
+
34
+ </nav>
35
+
36
+ <div id="main">
37
+
38
+ <h1 class="page-title">quota/toQuotaResult.mjs</h1>
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+ <section>
47
+ <article>
48
+ <pre class="prettyprint source linenums"><code>import get from 'lodash-es/get.js'
49
+ import isestr from 'wsemi/src/isestr.mjs'
50
+ import isearr from 'wsemi/src/isearr.mjs'
51
+
52
+
53
+ // toQuotaResult.mjs — 將各家額度查詢結果正規化為統一結構, 並判定帳號是否相符
54
+ //
55
+ // 【email在此類查詢中的真實角色】三家訂閱額度皆綁定「本機該CLI當前登入之憑證」,
56
+ // 無任一家提供「給email即可查任意帳號額度」之公開介面(那將是帳號列舉漏洞)。
57
+ // 故email之作用為比對——查出本機實際登入者,與呼叫端指定之email核對,
58
+ // 相符才視為查到「該帳號」之額度。此設計於一機多帳號(實測本機claude/codex/agy
59
+ // 分屬不同gmail)之情境尤其必要,否則呼叫端會把甲帳號的額度當成乙帳號的。
60
+ //
61
+ // 【為何不符時仍回傳額度資料】資料已取得,丟棄只是浪費一次往返;
62
+ // 但ok一律為false且matched為false,令呼叫端不會誤把他人額度當成指定帳號之額度。
63
+ //
64
+ // 【source欄位】各轉接器可能有主路徑與備援(例如codex以app-server為主、HTTP為備援),
65
+ // 同一供應商回來的資料可能來自不同介面,呼叫端排錯時須知道走了哪條,故獨立一欄記錄。
66
+
67
+
68
+ /**
69
+ * 將各家額度查詢結果正規化為統一結構,並判定帳號是否相符
70
+ *
71
+ * 帳號比對採去空白且不分大小寫之比較,因email本地部分雖理論上區分大小寫,
72
+ * 但三家供應商實務上皆以不分大小寫視為同一帳號;
73
+ * 未指定email時不做比對,matched為null,ok僅取決於查詢本身是否成功
74
+ *
75
+ * @param {String} provider 輸入供應商種類字串,例如'claude'、'codex'、'antigravity'
76
+ * @param {Object} [opt={}] 輸入設定物件,預設{}
77
+ * @param {String} [opt.email=''] 輸入本機該CLI實際登入之帳號email字串,預設''代表無從取得
78
+ * @param {String} [opt.emailWant=''] 輸入呼叫端指定欲查詢之帳號email字串,預設''代表不比對
79
+ * @param {String} [opt.plan=''] 輸入方案別字串,例如'max'、'plus',預設''
80
+ * @param {String} [opt.planTier=''] 輸入方案細部級距字串,例如'default_claude_max_20x',預設''
81
+ * @param {String} [opt.source=''] 輸入資料來源介面字串,例如'oauth-usage-api'、'codex-app-server'、'agy-print',預設''
82
+ * @param {Array} [opt.windows=[]] 輸入額度窗口物件陣列,預設[]
83
+ * @param {Object} [opt.credits=null] 輸入額外用量或點數資訊物件,預設null代表該供應商無此概念或未啟用
84
+ * @param {Object} [opt.raw=null] 輸入供應商原始回應物件,預設null
85
+ * @param {String} [opt.error=''] 輸入錯誤訊息字串,預設''代表查詢成功
86
+ * @param {String} [opt.errorType=''] 輸入機器可讀之錯誤類別字串,預設''
87
+ * @param {Number} [opt.durationMs=0] 輸入耗時毫秒,預設0
88
+ * @returns {Object} 回傳結果物件,內含ok(查詢成功且帳號相符布林值)、provider、email(本機實際登入帳號)、matched(帳號是否相符布林值,未指定email時為null)、plan、planTier、source、windows(窗口陣列)、credits、raw、error、errorType、durationMs
89
+ * @example
90
+ *
91
+ * import toQuotaResult from './src/quota/toQuotaResult.mjs'
92
+ *
93
+ * let r = toQuotaResult('claude', { email: 'a@b.com', emailWant: 'c@d.com' })
94
+ * console.log(r.ok, r.matched)
95
+ * // => false false
96
+ *
97
+ */
98
+ function toQuotaResult(provider, opt = {}) {
99
+
100
+ //provider
101
+ if (!isestr(provider)) {
102
+ provider = ''
103
+ }
104
+
105
+ //email, 本機該CLI實際登入之帳號
106
+ let email = get(opt, 'email', '')
107
+ if (!isestr(email)) {
108
+ email = ''
109
+ }
110
+
111
+ //emailWant, 呼叫端指定欲查詢之帳號
112
+ let emailWant = get(opt, 'emailWant', '')
113
+ if (!isestr(emailWant)) {
114
+ emailWant = ''
115
+ }
116
+
117
+ //error
118
+ let error = get(opt, 'error', '')
119
+ if (!isestr(error)) {
120
+ error = ''
121
+ }
122
+
123
+ //errorType
124
+ let errorType = get(opt, 'errorType', '')
125
+ if (!isestr(errorType)) {
126
+ errorType = ''
127
+ }
128
+
129
+ //matched, 未指定emailWant時不比對(null); 指定但無從取得本機帳號時視為不符並補述原因
130
+ let matched = null
131
+ if (emailWant !== '') {
132
+ if (email === '') {
133
+ matched = false
134
+ if (error === '') {
135
+ error = `cannot determine the local ${provider} login account, so it is unknown whether it is the requested account [${emailWant}]`
136
+ errorType = 'account'
137
+ }
138
+ }
139
+ else {
140
+ matched = email.trim().toLowerCase() === emailWant.trim().toLowerCase()
141
+ if (!matched &amp;&amp; error === '') {
142
+ error = `local ${provider} login account is [${email}], which does not match the requested account [${emailWant}]`
143
+ errorType = 'account'
144
+ }
145
+ }
146
+ }
147
+
148
+ //windows
149
+ let windows = get(opt, 'windows', null)
150
+ if (!isearr(windows)) {
151
+ windows = []
152
+ }
153
+
154
+ //plan與planTier與source
155
+ let plan = get(opt, 'plan', '')
156
+ if (!isestr(plan)) {
157
+ plan = ''
158
+ }
159
+ let planTier = get(opt, 'planTier', '')
160
+ if (!isestr(planTier)) {
161
+ planTier = ''
162
+ }
163
+ let source = get(opt, 'source', '')
164
+ if (!isestr(source)) {
165
+ source = ''
166
+ }
167
+
168
+ //ok, 須查詢無誤且帳號相符(未指定email時視為相符)
169
+ let ok = error === '' &amp;&amp; matched !== false
170
+
171
+ return {
172
+ ok,
173
+ provider,
174
+ email,
175
+ matched,
176
+ plan,
177
+ planTier,
178
+ source,
179
+ windows,
180
+ credits: get(opt, 'credits', null),
181
+ raw: get(opt, 'raw', null),
182
+ error,
183
+ errorType,
184
+ durationMs: get(opt, 'durationMs', 0),
185
+ }
186
+ }
187
+
188
+
189
+ export default toQuotaResult
190
+ </code></pre>
191
+ </article>
192
+ </section>
193
+
194
+
195
+
196
+
197
+
198
+
199
+ </div>
200
+
201
+ <br class="clear">
202
+
203
+ <footer>
204
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Sep 05 2026 22:03:08 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
205
+ </footer>
206
+
207
+ <script>prettyPrint();</script>
208
+ <script src="scripts/polyfill.js"></script>
209
+ <script src="scripts/linenumber.js"></script>
210
+
211
+
212
+
213
+ </body>
214
+ </html>
@@ -0,0 +1,117 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>quota/toQuotaScopedLabel.mjs - Documentation</title>
7
+
8
+
9
+ <script src="scripts/prettify/prettify.js"></script>
10
+ <script src="scripts/prettify/lang-css.js"></script>
11
+ <!--[if lt IE 9]>
12
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13
+ <![endif]-->
14
+ <link type="text/css" rel="stylesheet" href="styles/prettify.css">
15
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
16
+ <script src="scripts/nav.js" defer></script>
17
+
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ </head>
20
+ <body>
21
+
22
+ <input type="checkbox" id="nav-trigger" class="nav-trigger" />
23
+ <label for="nav-trigger" class="navicon-button x">
24
+ <div class="navicon"></div>
25
+ </label>
26
+
27
+ <label for="nav-trigger" class="overlay"></label>
28
+
29
+ <nav >
30
+
31
+
32
+ <h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#NO_SIDE_EFFECT">NO_SIDE_EFFECT</a></li><li><a href="global.html#WDispatchAi">WDispatchAi</a></li><li><a href="global.html#adapters">adapters</a></li><li><a href="global.html#agyPrint">agyPrint</a></li><li><a href="global.html#attachErrorType">attachErrorType</a></li><li><a href="global.html#budgetFor">budgetFor</a></li><li><a href="global.html#buildChain">buildChain</a></li><li><a href="global.html#buildValidator">buildValidator</a></li><li><a href="global.html#callAiWithFallback">callAiWithFallback</a></li><li><a href="global.html#callOnce">callOnce</a></li><li><a href="global.html#castPintOr">castPintOr</a></li><li><a href="global.html#collectAdditional">collectAdditional</a></li><li><a href="global.html#createFileStore">createFileStore</a></li><li><a href="global.html#createUsageCounter">createUsageCounter</a></li><li><a href="global.html#decodeJwtPayload">decodeJwtPayload</a></li><li><a href="global.html#defaultIntegratePrompt">defaultIntegratePrompt</a></li><li><a href="global.html#dfQuotaTimeoutMs">dfQuotaTimeoutMs</a></li><li><a href="global.html#dfTimeoutMs">dfTimeoutMs</a></li><li><a href="global.html#directWindow">directWindow</a></li><li><a href="global.html#dispatchAi">dispatchAi</a></li><li><a href="global.html#dispatchAiFallback">dispatchAiFallback</a></li><li><a href="global.html#dispatchAiWkf">dispatchAiWkf</a></li><li><a href="global.html#dispatchAntigravity">dispatchAntigravity</a></li><li><a href="global.html#dispatchApiOpenaiCompat">dispatchApiOpenaiCompat</a></li><li><a href="global.html#dispatchApiOpenaiResponses">dispatchApiOpenaiResponses</a></li><li><a href="global.html#dispatchClaude">dispatchClaude</a></li><li><a href="global.html#dispatchCodex">dispatchCodex</a></li><li><a href="global.html#dispatchOpencode">dispatchOpencode</a></li><li><a href="global.html#extractJsonLoose">extractJsonLoose</a></li><li><a href="global.html#extractOutputText">extractOutputText</a></li><li><a href="global.html#fetchQuotaJson">fetchQuotaJson</a></li><li><a href="global.html#firstStr">firstStr</a></li><li><a href="global.html#fromBucket">fromBucket</a></li><li><a href="global.html#fromCodexUsageHttp">fromCodexUsageHttp</a></li><li><a href="global.html#fromLimitItem">fromLimitItem</a></li><li><a href="global.html#fromRpcWindow">fromRpcWindow</a></li><li><a href="global.html#fromTopField">fromTopField</a></li><li><a href="global.html#fromWindow">fromWindow</a></li><li><a href="global.html#getCliArgs">getCliArgs</a></li><li><a href="global.html#getErrorResult">getErrorResult</a></li><li><a href="global.html#getErrorType">getErrorType</a></li><li><a href="global.html#getQuotaAntigravity">getQuotaAntigravity</a></li><li><a href="global.html#getQuotaClaude">getQuotaClaude</a></li><li><a href="global.html#getQuotaCodex">getQuotaCodex</a></li><li><a href="global.html#initState">initState</a></li><li><a href="global.html#isKeyIndependentFail">isKeyIndependentFail</a></li><li><a href="global.html#pickWindow">pickWindow</a></li><li><a href="global.html#readBodyCapped">readBodyCapped</a></li><li><a href="global.html#readEnvFile">readEnvFile</a></li><li><a href="global.html#readJsonOrNull">readJsonOrNull</a></li><li><a href="global.html#redactText">redactText</a></li><li><a href="global.html#reorderByCooling">reorderByCooling</a></li><li><a href="global.html#resolveProviders">resolveProviders</a></li><li><a href="global.html#runFanout">runFanout</a></li><li><a href="global.html#runFanoutPipeline">runFanoutPipeline</a></li><li><a href="global.html#runRolePipeline">runRolePipeline</a></li><li><a href="global.html#salvageTruncatedArray">salvageTruncatedArray</a></li><li><a href="global.html#toQuotaLabel">toQuotaLabel</a></li><li><a href="global.html#toQuotaResult">toQuotaResult</a></li><li><a href="global.html#toQuotaScopedLabel">toQuotaScopedLabel</a></li><li><a href="global.html#toQuotaWindow">toQuotaWindow</a></li><li><a href="global.html#viaAppServer">viaAppServer</a></li><li><a href="global.html#viaHttp">viaHttp</a></li><li><a href="global.html#windowToSeconds">windowToSeconds</a></li></ul>
33
+
34
+ </nav>
35
+
36
+ <div id="main">
37
+
38
+ <h1 class="page-title">quota/toQuotaScopedLabel.mjs</h1>
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+ <section>
47
+ <article>
48
+ <pre class="prettyprint source linenums"><code>import isestr from 'wsemi/src/isestr.mjs'
49
+ import toQuotaLabel from './toQuotaLabel.mjs'
50
+
51
+
52
+ // toQuotaScopedLabel.mjs — 組出帶適用範圍之額度窗口標籤(單一來源)
53
+ //
54
+ // 【為何獨立成檔】三個額度轉接器都要把「窗口長度」與「範圍」拼成「7天(Fable)」「5小時(Gemini Models)」
55
+ // 這類標籤, 同一段拼接規則曾於fromCodexUsageHttp/getQuotaClaude/getQuotaCodex各手寫一份——
56
+ // 依全域規範「同一規則手寫≥2處即補丁訊號」收斂於此。基底一律取自toQuotaLabel,
57
+ // 窗口長度變動時標籤自動跟著正確, 不得手寫「7天」字串。
58
+
59
+
60
+ /**
61
+ * 組出帶適用範圍之額度窗口標籤
62
+ *
63
+ * 無範圍時回傳空字串,令toQuotaWindow自行由windowSeconds推導預設標籤;
64
+ * 有範圍但窗口秒數無效(供應商未提供)時僅以範圍為標籤
65
+ *
66
+ * @param {Number} windowSeconds 輸入窗口長度秒數
67
+ * @param {String} scope 輸入適用範圍字串,例如模型名或群組名
68
+ * @returns {String} 回傳標籤字串,例如'7天(Fable)';無範圍回傳''
69
+ * @example
70
+ *
71
+ * import toQuotaScopedLabel from './src/quota/toQuotaScopedLabel.mjs'
72
+ *
73
+ * console.log(toQuotaScopedLabel(604800, 'Fable'))
74
+ * // => 7天(Fable)
75
+ *
76
+ * console.log(toQuotaScopedLabel(null, 'codex-spark'))
77
+ * // => codex-spark
78
+ *
79
+ * console.log(toQuotaScopedLabel(18000, ''))
80
+ * // => (空字串)
81
+ *
82
+ */
83
+ function toQuotaScopedLabel(windowSeconds, scope) {
84
+ if (!isestr(scope)) {
85
+ return ''
86
+ }
87
+ let base = toQuotaLabel(windowSeconds)
88
+ return base === '' ? scope : `${base}(${scope})`
89
+ }
90
+
91
+
92
+ export default toQuotaScopedLabel
93
+ </code></pre>
94
+ </article>
95
+ </section>
96
+
97
+
98
+
99
+
100
+
101
+
102
+ </div>
103
+
104
+ <br class="clear">
105
+
106
+ <footer>
107
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Sep 05 2026 22:03:08 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
108
+ </footer>
109
+
110
+ <script>prettyPrint();</script>
111
+ <script src="scripts/polyfill.js"></script>
112
+ <script src="scripts/linenumber.js"></script>
113
+
114
+
115
+
116
+ </body>
117
+ </html>