@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/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: sort by plan tier (lowest first), then by
65
- * model name, then by id as a tiebreak. Models with no known plan sort last.
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)}` : "") + " — requests will succeed again after the reset (or add another account)", "RATE_LIMIT", wait > 0 && wait <= 9e5 ? { providerRetryAfterMs: wait } : void 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)}` : "") + ` — 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
- * Comparator for the model picker: sort by plan tier (lowest first), then by
560
- * model name, then by id as a tiebreak. Models with no known plan sort last.
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`, "INVALID_CREDENTIAL", { status: 401 });
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 with thousands separators. */
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 = {