docs(ai-code-review): 補齊各模組 JSDoc、指令檔逐行註解並重建 README

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jeffery
2026-06-26 14:16:04 +08:00
co-authored by Claude Opus 4.8
parent 303104bb20
commit 1378f03595
18 changed files with 2243 additions and 68 deletions
+61 -10
View File
@@ -4,6 +4,12 @@ import { warn } from './log.js';
/** 本次執行的 token 累計(跨所有 LLM 呼叫)。 */
const runUsage = { calls: 0, promptTokens: 0, completionTokens: 0, totalTokens: 0 };
/**
* 安全數字轉換:將任意輸入轉為有限數字,無法轉換或非有限值(NaN/Infinity)一律回 0。
* 常用於正規化外部 API 回應或 HTTP header 取出的值,避免污染後續加總與百分比運算。
* @param {*} x 任意待轉換的值。
* @returns {number} 有限數字;否則為 0。
*/
function num(x) {
const n = Number(x);
return Number.isFinite(n) ? n : 0;
@@ -83,7 +89,12 @@ export function resetRunUsage() {
/** 最近一次回應的速率配額(rate limit)快照,用來計算「當前視窗剩餘百分比」。 */
const rateLimit = { hasData: false, remaining: null, limit: null, kind: null };
/** 將物件的 key 全部轉小寫,方便對大小寫不敏感的 HTTP header 取值。 */
/**
* 將物件第一層的 key 全部轉為小寫並回傳新物件,方便對大小寫不敏感的 HTTP header 取值。
* 不修改傳入物件;僅處理第一層 key。呼叫端須自行確保傳入為物件。
* @param {Object<string, *>} obj 來源物件(通常為 HTTP response headers)。
* @returns {Object<string, *>} key 全小寫的新物件。
*/
function lowerCaseKeys(obj) {
const out = {};
for (const k of Object.keys(obj)) out[k.toLowerCase()] = obj[k];
@@ -128,12 +139,20 @@ export function resetRateLimit() {
rateLimit.kind = null;
}
/**
* 去除字串結尾的單一斜線(常用於正規化 baseURL 以利串接路徑)。
* 空值會被視為空字串;僅移除最後一個斜線,不處理連續尾斜線。
* @param {*} s 來源字串(通常為 URL)。
* @returns {string} 去除結尾斜線後的字串。
*/
const stripSlash = (s) => String(s || '').replace(/\/$/, '');
/**
* 以實際 hostname 精確比對是否為 OpenRouter(僅接受 apex 域名 `openrouter.ai`),
* 避免被偽造的 baseURL(如 `openrouter.ai.evil.com`、`evil.com/openrouter.ai` 或任何子網域)
* 矇騙而把 API key 送往非 OpenRouter 主機
* 以解析後的 hostname 精確比對 baseURL 是否為 OpenRouter(僅接受 apex 域名 openrouter.ai)。
* 用於 fetchAccountQuota 的安全守門:防止被偽造或子網域 baseURL 矇騙而外洩 API key。
* 無法解析為合法 URL 時回 false(不丟例外)
* @param {string} baseURL 待驗證的 base URL。
* @returns {boolean} hostname 恰為 openrouter.ai 時為 true,否則 false。
*/
function isOpenRouterBaseURL(baseURL) {
try {
@@ -144,8 +163,14 @@ function isOpenRouterBaseURL(baseURL) {
}
/**
* OpenRouter:以 API key 呼叫 GET /auth/key 取得額度(可靠)。
* 回傳金額單位為 USD credits
* 呼叫 OpenRouter GET /auth/key,以 API key 取得帳號額度(單位 USD credits)。
* remaining 缺漏時以 limit - used 推算;limit 為 null(無上限)時 remaining 亦為 null
* 本函式不攔截例外;HTTP/網路錯誤會向上拋出,由呼叫端負責降級。
* @param {{apiKey:string, baseURL:string}} cfg 連線設定(API key 與 base URL)。
* @param {function(string, object): Promise<{data:*}>} get HTTP GET 函式(可注入,預設 axios.get)。
* @returns {Promise<{available:true, used:number, limit:number|null, remaining:number|null, currency:'USD', source:'openrouter'}>}
* 額度資訊。
* @throws {Error} HTTP 請求失敗(逾時、非 2xx、網路錯誤等)時拋出。
*/
async function fetchOpenRouterQuota({ apiKey, baseURL }, get) {
const resp = await get(`${stripSlash(baseURL)}/auth/key`, {
@@ -193,7 +218,11 @@ export async function fetchAccountQuota(provider, config = {}, deps = {}) {
}
}
/** 千分位整數/小數格式。 */
/**
* 將數字格式化為千分位字串(保留原有小數)。null/undefinedNaN 一律回 '0'。
* @param {number|string|null|undefined} n 待格式化的數值。
* @returns {string} 千分位字串,例如 1234567 → '1,234,567'。
*/
function fmt(n) {
if (n == null || Number.isNaN(Number(n))) return '0';
const [int, frac] = String(Number(n)).split('.');
@@ -201,10 +230,22 @@ function fmt(n) {
return frac ? `${withCommas}.${frac}` : withCommas;
}
/**
* 將數值格式化為金額字串:有幣別時前綴幣別(如 'USD 1,234'),無幣別則只回千分位數字。
* @param {string} currency 幣別代碼(空字串/falsy 表示無幣別)。
* @param {number|string|null|undefined} n 數值。
* @returns {string} 格式化後的金額字串。
*/
function money(currency, n) {
return currency ? `${currency} ${fmt(n)}` : fmt(n);
}
/**
* 四捨五入到小數一位(用於百分比顯示)。
* 不對非數字防呆;非有限輸入會得到 NaN(呼叫端應先確保為有限數)。
* @param {number|string} n 數值。
* @returns {number} 四捨五入到一位小數的結果。
*/
function round1(n) {
return Math.round(Number(n) * 10) / 10;
}
@@ -212,9 +253,12 @@ function round1(n) {
const RATE_KIND_LABEL = { tokens: 'token', requests: '次數' };
/**
* 計算剩餘百分比」= remaining / limit × 100。
* limit 或 remaining 為 nullundefinedNaNInfinity,或 limit ≤ 0 時回 null
* 避免算出 Infinity%NaN%負百分比或除以零。
* 計算剩餘百分比remaining / limit × 100),四捨五入到一位小數
* 任一參數為 nullundefinedNaNInfinity,或 limit ≤ 0 時回 null
* 避免算出 Infinity%NaN%負百分比或除以零。
* @param {number|null|undefined} remaining 剩餘量。
* @param {number|null|undefined} limit 上限。
* @returns {number|null} 百分比(一位小數);無法計算時為 null。
*/
function calculatePercent(remaining, limit) {
if (remaining == null || limit == null) return null;
@@ -253,6 +297,13 @@ export function resolveRemainingPercent(quota, rate) {
return { percent: null, reason };
}
/**
* 把 resolveRemainingPercent 的結果格式化為一行 Markdown 文字(剩餘可用百分比與明細)。
* 無法計算時輸出帶原因的說明字串。
* @param {{percent:number, basis:string, remaining:number, limit:number, unit:string}|{percent:null, reason:string}} pct
* resolveRemainingPercent 的回傳值。
* @returns {string} 單行 Markdown 字串。
*/
function remainingLine(pct) {
if (pct.percent == null) return `剩餘可用:無法計算百分比(${pct.reason}`;
const detail = `${pct.basis}${money(pct.unit, pct.remaining)} / ${money(pct.unit, pct.limit)}`;