@mars-sea/dsh-commandcode-provider 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +59 -135
- package/README.zh-CN.md +58 -136
- package/lib/client.js +154 -23
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +16 -2
- package/lib/index.js +37 -7
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -61,8 +61,9 @@ declare const PLAN_LABELS: Readonly<Record<string, string>>;
|
|
|
61
61
|
*/
|
|
62
62
|
declare const PLAN_ORDER: Readonly<Record<string, number>>;
|
|
63
63
|
/**
|
|
64
|
-
* Comparator for the model picker:
|
|
65
|
-
*
|
|
64
|
+
* Comparator for the model picker: free models first (zero credit cost, usable
|
|
65
|
+
* by every account), then by plan tier (lowest first), then by model name,
|
|
66
|
+
* then by id as a tiebreak. Models with no known plan sort last.
|
|
66
67
|
*/
|
|
67
68
|
declare function compareByPlan(a: {
|
|
68
69
|
id: string;
|
|
@@ -313,6 +314,12 @@ interface CommandCodePlan {
|
|
|
313
314
|
/** Billing period end in millis; 0 when the endpoint did not report one. */
|
|
314
315
|
currentPeriodEnd: number;
|
|
315
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* Why every account endpoint failed at once (the report then carries no data
|
|
319
|
+
* at all, so the degraded per-endpoint view would hide the root cause behind
|
|
320
|
+
* a generic "partial data" note). Undefined for partial failures.
|
|
321
|
+
*/
|
|
322
|
+
type UsageBlockReason = 'invalid-key' | 'service-unavailable' | 'network';
|
|
316
323
|
/** Everything the usage endpoints report, fetched together. */
|
|
317
324
|
interface CommandCodeUsageReport {
|
|
318
325
|
account?: CommandCodeAccount;
|
|
@@ -321,6 +328,13 @@ interface CommandCodeUsageReport {
|
|
|
321
328
|
plan?: CommandCodePlan;
|
|
322
329
|
/** Endpoint failures degrade the report instead of failing it. */
|
|
323
330
|
failures: string[];
|
|
331
|
+
/**
|
|
332
|
+
* The single reason every endpoint failed, when they all did: `invalid-key`
|
|
333
|
+
* (every call rejected with 401 — the stored key is wrong or expired),
|
|
334
|
+
* `service-unavailable` (every call answered 5xx), or `network` (no HTTP
|
|
335
|
+
* response at all). Undefined when any endpoint succeeded.
|
|
336
|
+
*/
|
|
337
|
+
blocked?: UsageBlockReason;
|
|
324
338
|
}
|
|
325
339
|
declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> extends LlmAdapter {
|
|
326
340
|
private readonly deps;
|
package/lib/index.js
CHANGED
|
@@ -160,11 +160,11 @@ var CommandCodeAccountPool = class {
|
|
|
160
160
|
const revived = selectActiveAccount(await this.resolvedAccounts(), this.deps.preferredId?.());
|
|
161
161
|
if (revived !== void 0) return this.pick(revived);
|
|
162
162
|
const latest = await this.resolvedAccounts();
|
|
163
|
-
if (latest.filter((account) => account.state?.kind === "disabled").length === latest.length) throw new LlmError(`llm-commandcode: every configured Command Code account (${latest.length}) was rejected with 401 — check the stored API keys (Models page / settings) or the auth file`, "INVALID_CREDENTIAL");
|
|
163
|
+
if (latest.filter((account) => account.state?.kind === "disabled").length === latest.length) throw new LlmError(`llm-commandcode: every configured Command Code account (${latest.length}) was rejected with 401 — check the stored API keys (Models page / settings) or the auth file;已配置的 ${latest.length} 个 Command Code 账户密钥均被拒绝(401)——请在设置页检查存储的 API 密钥,或重新运行 command-code login`, "INVALID_CREDENTIAL");
|
|
164
164
|
const resets = latest.map((account) => account.state).filter((state) => state !== void 0 && state.kind === "cooldown" && state.until > 0).map((state) => state.until);
|
|
165
165
|
const earliest = resets.length > 0 ? Math.min(...resets) : 0;
|
|
166
166
|
const wait = earliest > 0 ? Math.max(1e3, earliest - Date.now()) : 0;
|
|
167
|
-
throw new LlmError(`llm-commandcode: all ${latest.length} Command Code account(s) have exhausted their usage window` + (earliest > 0 ? `; the earliest window resets at ${clockLabel(earliest)}` : "") +
|
|
167
|
+
throw new LlmError(`llm-commandcode: all ${latest.length} Command Code account(s) have exhausted their usage window` + (earliest > 0 ? `; the earliest window resets at ${clockLabel(earliest)}` : "") + ` — requests will succeed again after the reset (or add another account);已用尽全部 ${latest.length} 个 Command Code 账户的用量窗口` + (earliest > 0 ? `,最早的重置时间为 ${clockLabel(earliest)}` : "") + "——窗口重置后请求会自动恢复(也可以添加更多账户)", "RATE_LIMIT", wait > 0 && wait <= 9e5 ? { providerRetryAfterMs: wait } : void 0);
|
|
168
168
|
}
|
|
169
169
|
/**
|
|
170
170
|
* Record a rejection against one key. `rate-limit` (429) marks the key
|
|
@@ -556,10 +556,22 @@ const PLAN_ORDER = {
|
|
|
556
556
|
max: 4
|
|
557
557
|
};
|
|
558
558
|
/**
|
|
559
|
-
*
|
|
560
|
-
*
|
|
559
|
+
* Whether a model is free (requests cost no credits), per the pricing page's
|
|
560
|
+
* deals (`KNOWN_DEALS` `free: true`). Free models lead the picker regardless
|
|
561
|
+
* of tier — they are usable by every account, so they are the best default
|
|
562
|
+
* candidates.
|
|
563
|
+
*/
|
|
564
|
+
function isFreeModel(modelId) {
|
|
565
|
+
return KNOWN_DEALS[modelId]?.free === true;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Comparator for the model picker: free models first (zero credit cost, usable
|
|
569
|
+
* by every account), then by plan tier (lowest first), then by model name,
|
|
570
|
+
* then by id as a tiebreak. Models with no known plan sort last.
|
|
561
571
|
*/
|
|
562
572
|
function compareByPlan(a, b) {
|
|
573
|
+
const freeDelta = Number(isFreeModel(b.id)) - Number(isFreeModel(a.id));
|
|
574
|
+
if (freeDelta !== 0) return freeDelta;
|
|
563
575
|
const pa = PLAN_ORDER[KNOWN_PLANS[a.id] ?? ""] ?? Number.MAX_SAFE_INTEGER;
|
|
564
576
|
const pb = PLAN_ORDER[KNOWN_PLANS[b.id] ?? ""] ?? Number.MAX_SAFE_INTEGER;
|
|
565
577
|
if (pa !== pb) return pa - pb;
|
|
@@ -1009,6 +1021,8 @@ async function messagesToCC(messages, readImage) {
|
|
|
1009
1021
|
}
|
|
1010
1022
|
return out;
|
|
1011
1023
|
}
|
|
1024
|
+
/** Account endpoints fetched by one `getUsage()` run (see the classification there). */
|
|
1025
|
+
const USAGE_ENDPOINT_COUNT = 4;
|
|
1012
1026
|
var CommandCodeAdapter = class extends LlmAdapter {
|
|
1013
1027
|
deps;
|
|
1014
1028
|
catalog = [];
|
|
@@ -1206,6 +1220,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1206
1220
|
const base = this.deps.options().apiBase;
|
|
1207
1221
|
const headers = await this.accountHeaders(apiKey);
|
|
1208
1222
|
const failures = [];
|
|
1223
|
+
const failedStatuses = [];
|
|
1209
1224
|
const getJson = async (path) => {
|
|
1210
1225
|
try {
|
|
1211
1226
|
const response = await this.fetchImpl(`${base}${path}`, {
|
|
@@ -1214,12 +1229,14 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1214
1229
|
});
|
|
1215
1230
|
if (!response.ok) {
|
|
1216
1231
|
failures.push(`${path}: HTTP ${response.status}`);
|
|
1232
|
+
failedStatuses.push(response.status);
|
|
1217
1233
|
return;
|
|
1218
1234
|
}
|
|
1219
1235
|
const parsed = await response.json();
|
|
1220
1236
|
return isRecord(parsed) ? parsed : void 0;
|
|
1221
1237
|
} catch (error) {
|
|
1222
1238
|
failures.push(`${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1239
|
+
failedStatuses.push(void 0);
|
|
1223
1240
|
return;
|
|
1224
1241
|
}
|
|
1225
1242
|
};
|
|
@@ -1280,6 +1297,12 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1280
1297
|
currentPeriodEnd: periodEndValue(subData?.currentPeriodEnd)
|
|
1281
1298
|
};
|
|
1282
1299
|
}
|
|
1300
|
+
if (failures.length === USAGE_ENDPOINT_COUNT) {
|
|
1301
|
+
const codes = failedStatuses.filter((status) => status !== void 0);
|
|
1302
|
+
if (codes.length === USAGE_ENDPOINT_COUNT && codes.every((code) => code === 401)) report.blocked = "invalid-key";
|
|
1303
|
+
else if (codes.length === USAGE_ENDPOINT_COUNT && codes.every((code) => code >= 500)) report.blocked = "service-unavailable";
|
|
1304
|
+
else if (codes.length === 0) report.blocked = "network";
|
|
1305
|
+
}
|
|
1283
1306
|
return report;
|
|
1284
1307
|
}
|
|
1285
1308
|
/**
|
|
@@ -1678,7 +1701,7 @@ function generateHttpError(status, errText, retryAfterMs) {
|
|
|
1678
1701
|
if (isRecord(parsed) && isRecord(parsed.error)) providerCode = stringValue(parsed.error.code);
|
|
1679
1702
|
} catch {}
|
|
1680
1703
|
const detail = providerCode ?? `HTTP ${status}`;
|
|
1681
|
-
if (status === 401) return new LlmError(`Command Code API error 401 (${detail}): the API key is missing or invalid — check the key stored for COMMANDCODE_API_KEY (Models page) or the auth file
|
|
1704
|
+
if (status === 401) return new LlmError(`Command Code API error 401 (${detail}): the API key is missing or invalid — check the key stored for COMMANDCODE_API_KEY (Models page) or the auth file;Command Code API 返回 401:API 密钥缺失或无效——请在设置页检查 COMMANDCODE_API_KEY 存储的密钥,或检查 auth 文件`, "INVALID_CREDENTIAL", { status: 401 });
|
|
1682
1705
|
return new LlmError(`Command Code API error ${status}${detail === `HTTP ${status}` ? "" : ` (${detail})`}: ${errText.slice(0, 500)}`, status === 429 ? "RATE_LIMIT" : "PROVIDER_HTTP_ERROR", {
|
|
1683
1706
|
status,
|
|
1684
1707
|
...retryAfterMs !== void 0 && retryAfterMs > 0 && retryAfterMs <= 9e5 ? { providerRetryAfterMs: retryAfterMs } : {}
|
|
@@ -1719,8 +1742,7 @@ function money(value) {
|
|
|
1719
1742
|
function moneyShort(value) {
|
|
1720
1743
|
return `$${value.toFixed(2)}`;
|
|
1721
1744
|
}
|
|
1722
|
-
/** Format a token count
|
|
1723
|
-
/** Format a large token count compactly (1.9亿 style). */
|
|
1745
|
+
/** Format a large token count compactly (1.9M style). */
|
|
1724
1746
|
function tokensCompact(value) {
|
|
1725
1747
|
if (value >= 1e9) return `${(value / 1e9).toFixed(1)}B`;
|
|
1726
1748
|
if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
|
|
@@ -1754,6 +1776,9 @@ function renderReport(report, title) {
|
|
|
1754
1776
|
const lines = [];
|
|
1755
1777
|
const account = report.account ? ` (${report.account.userName || report.account.name})` : "";
|
|
1756
1778
|
lines.push(title ?? `📊 Command Code 用量${account}`, "");
|
|
1779
|
+
if (report.blocked === "invalid-key") lines.push("⛔ API 密钥无效或已过期 — 服务端拒绝了全部请求(401),请检查该账户的密钥配置", "");
|
|
1780
|
+
else if (report.blocked === "service-unavailable") lines.push("⚠️ Command Code 服务暂时不可用(5xx),稍后重试", "");
|
|
1781
|
+
else if (report.blocked === "network") lines.push("⚠️ 无法连接 Command Code 服务 — 请检查网络或 API 地址", "");
|
|
1757
1782
|
if (report.plan && report.plan.name !== "") {
|
|
1758
1783
|
const p = report.plan;
|
|
1759
1784
|
const status = p.status !== "" && p.status !== "active" ? ` (${p.status})` : "";
|
|
@@ -1864,6 +1889,11 @@ function parseUsageReport(value) {
|
|
|
1864
1889
|
const failures = source.failures;
|
|
1865
1890
|
if (!Array.isArray(failures) || failures.some((entry) => typeof entry !== "string")) reject("failures");
|
|
1866
1891
|
const report = { failures };
|
|
1892
|
+
if (source.blocked !== void 0) {
|
|
1893
|
+
const blocked = source.blocked;
|
|
1894
|
+
if (blocked !== "invalid-key" && blocked !== "service-unavailable" && blocked !== "network") reject("blocked");
|
|
1895
|
+
report.blocked = blocked;
|
|
1896
|
+
}
|
|
1867
1897
|
if (source.account !== void 0) {
|
|
1868
1898
|
const account = record(source.account, "account");
|
|
1869
1899
|
report.account = {
|