@mars-sea/dsh-commandcode-provider 0.16.2 → 0.16.4
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 +21 -0
- package/lib/client.js +52 -20
- package/lib/index.d.ts +2 -2
- package/lib/index.js +141 -22
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.16.4] - 2026-11-02
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **新增免费模型 Ling 3.1 Flash 1019(`inclusionai/ling-3.1-flash-1019:free`,服务端目录先行)。** `?view=client` 的 `catalogVersion` 由 `7f405e24…` 变为 `f3a51355…`,默认目录 94 → 95、client 视图 106 → 107,唯一新增行是 Ling 3.1 Flash 1019:1,000,000 上下文、262,144 输出上限、纯文本输入、推理档位 `low`/`medium`/`high`、Go 档、ZDR 覆盖、`/chat/completions` 端点;100% 免费促销自 `2026-10-29T16:00Z` 起至 `2026-11-03T16:00Z`(北京时间 11-04 00:00 到期),每账号每天 100 次请求。官方 models、pricing 与 go/goat/pro/max 计划页均已上架;`1.88.2` 产物注册表尚未收录该行(审计报 `absent from CLI registry`,与 `gpt-6.1-terra` 的目录先行形态一致)。快照 +1:`KNOWN_PLANS` 94 → 95、`KNOWN_EFFORTS` 67 → 68、`KNOWN_DEALS` 4 → 5(带 `expiresAt`,到期后 FREE 徽标自动消失,不跟随下架);`KNOWN_IMAGE_MODELS`(73)、`KNOWN_THINKING_MODELS`(16)、`KNOWN_NON_ZDR_MODELS`(18)、订阅表(9)、峰值定价(5)、内置价格行(94)与输出上限表(79)均不变——免费行照旧不进价格表,`MODEL_OUTPUT_TOKEN_LIMITS` 也不含 Ling 行(走 chat/completions)。`COMMAND_CODE_CLI_VERSION` 保持 `1.88.2`。**验证**:`typecheck` 退出码 0,`npm test` 1156/1156(扩展既有快照断言,未新增用例),`build` 两段 `Build complete`;本轮仅公开 GET 采集(14 个源全部 HTTP 200),未做真实账号登录、付费生成与服务端协议验证。
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **账户余额为 0 时提示指向错误方向(2026-10-31 实测)。** 上游对 `POST /provider/v1/chat/completions` 返回 `400`,正文为 `You have insufficient credits to make this request…`、结构化代码只有 `BAD_REQUEST`;插件已能据此把账号轮换掉,但单账号场景没有可换对象,最终只显示通用文案「服务端拒绝了本次请求,请检查模型、套餐和请求设置」——按这三条查一条也查不到点上。现在该形状在通用分支之前被识别,提示为「账户额度不足,无法完成本次请求——请充值额度,或切换到其他账号」。判定复用账号轮换的同一组 pattern(新增 `isInsufficientCreditsText()` 与 `INSUFFICIENT_CREDITS`/`PREMIUM_CREDITS_EXHAUSTED` 代码集),两处不会漂移;失败码仍取 `httpErrorCode(status)`,重试预算与轮换行为不变。**验证**:`typecheck` 退出码 0,`npm test` 1156/1156(新增 3 条:实测正文、仅结构化代码、普通 400 不被吞)。
|
|
16
|
+
|
|
17
|
+
## [0.16.3] - 2026-10-31
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **同步 Command Code CLI `1.88.1` → `1.88.2`(纯请求头同步)。** npm `latest` 于 `2026-10-29T02:05Z` 前进到 `1.88.2`。CLI 的变更日志在 npm 包内 `CHANGELOG.md`(官网 `/changelog` 只覆盖 Desktop 应用),该版本只有一条 patch:「修复全新安装或更新后启动崩溃」。产物级比对:包内 80 个文件只有 `CHANGELOG.md`、`dist/cli.mjs`、`package.json`(仅版本号)与 VSIX 变化,依赖零变化;请求路径(193)、自定义请求头(13)、环境变量(48)集合相同,`/alpha/generate` 调用点在屏蔽混淆标识符后相似度 1.0,`promptCache`(5)/`reasoning_effort`(7)/`max_tokens`(12)/`cache_control`(1)计数相同,审计 `cliDiff`(模型、档位、订阅、ZDR 例外集)为空。服务端目录未动(`?view=client` 的 `catalogVersion` 仍 `7f405e24…`,默认 94、client 106 行),所有能力快照零变化,`COMMAND_CODE_CLI_VERSION` 只随请求头更新,运行时行为不变。
|
|
22
|
+
- **同步定价页两条模型额度(`planAllowanceUsd`)。** 官网把 DeepSeek V4.1 Flash 与 GLM-5.3 Flash 的加成月度额度从 GOAT `$80`/Pro `$90` 上调到 GOAT `$100`/Pro `$110`(Go `$10` 不变),per-token 价格、Peak 时段与套餐可用性均未变;设置页「模型额度」随之显示新值。价格表由 `node scripts/sync-model-prices.mjs` 重新生成,全表只有这两行变化。GLM-5.3 Flash 仍标注 `while capacity lasts`;V4.1 Flash 的文案从「with no end date」改为「comes with bigger monthly credits」,官方未给结束时间,若之后回调按同一脚本再同步。**验证**:`typecheck` 退出码 0,`npm test` 1142/1142,`build` 两段 `Build complete`;未做真实账号登录与付费生成验证。
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **探活丢路径前缀、断连与保存失败提示不可诊断(#111、#110)。** 探活原先用 `new URL('/', apiBase)` 探 API base 根,会丢掉带路径前缀的反向代理前缀(如 `http://127.0.0.1:18797/bili/https://api.commandcode.ai`),状态行必然误报「本机无法连接」;现在改探真实端点 `${apiBase}/alpha/whoami` 并用与真实请求相同的字符串拼接保留前缀,未认证的 401/403 计为「可达」(探活刻意不带凭据),404/429/5xx 仍判异常,超时与传输失败继续分开。用量报告的每条端点失败在原文后追加中文原因与目标 `scheme://host:port`(映射 fetch `error.cause` 的 `ECONNREFUSED`/`ENOTFOUND`/连接超时等,原始错误码入括号),反代换端口、代理未启动与 API 故障因此可区分。设置保存的「配置提交未获确认」不再固定说「密钥未写入」:只有本次提交带密钥时才提密钥,否则只报配置未写入;宿主拒绝的原文(如 profile 写锁超时)以「宿主返回:…」附在提示后,折叠为一行并限长 240 字符。**验证**:`typecheck` 退出码 0,`npm test` 1153/1153,`build` 两段 `Build complete`;另在隔离 `DSH_HOME` + 真实宿主(`dsh 0.2.2-alpha.2`)+ 带路径前缀反代 + 无头 Chrome 下端到端复现:反代在线时状态行「本机 连通正常 · 781 ms」(旧探测目标反代根 `/` 返回 404),停反代后「本机无法连接」且失败详情带目标与 `ECONNREFUSED`,写锁超时保存提示带宿主原文;再用真实密钥复验:只读接口全部 200、状态行「连通正常」、真实生成 `POST /alpha/generate` 返回 200(会话费用 `≈$0.0015` 与上游扣减 `0.001471284` 一致);最后复验浏览器登录链路:隔离宿主用凭据库中不存在的引用起步(界面「未配置」),点「网页登录」后按 Studio 回调契约提交真实密钥,回调 `200 {"success":true}`、校验请求经反代 `/alpha/whoami` 200、凭据落库且界面转为「已登录为 mars-sea」。Studio 页面本身的账号登录与 Cloudflare 挑战需真人完成,不在自动验证范围内。
|
|
27
|
+
|
|
7
28
|
## [0.16.2] - 2026-10-30
|
|
8
29
|
|
|
9
30
|
### Changed
|
package/lib/client.js
CHANGED
|
@@ -76,6 +76,13 @@ window.__ModuleLoader__.load({
|
|
|
76
76
|
}
|
|
77
77
|
//#endregion
|
|
78
78
|
//#region src/client/settings-scope.ts
|
|
79
|
+
/** 组装报告;没有原文时不带 `message` 键(`exactOptionalPropertyTypes`)。 */
|
|
80
|
+
function mutationReport(result, message) {
|
|
81
|
+
return message === void 0 ? { result } : {
|
|
82
|
+
result,
|
|
83
|
+
message
|
|
84
|
+
};
|
|
85
|
+
}
|
|
79
86
|
/**
|
|
80
87
|
* Backoff before re-reading a directory whose FIRST read failed, in millis.
|
|
81
88
|
*
|
|
@@ -328,19 +335,20 @@ window.__ModuleLoader__.load({
|
|
|
328
335
|
const response = await this.mutateRemote(ownedOps, revision);
|
|
329
336
|
if (!response.ok) {
|
|
330
337
|
await this.recover(generation);
|
|
331
|
-
|
|
338
|
+
const message = response.error?.message;
|
|
339
|
+
return message?.includes("SETTINGS_CONFLICT") ? mutationReport("conflict", message) : mutationReport("failed", message);
|
|
332
340
|
}
|
|
333
341
|
const row = response.value;
|
|
334
342
|
if (row?.ns !== this.namespace || decodeRow(row.value) === void 0 || typeof row.revision !== "number") {
|
|
335
343
|
await this.recover(generation);
|
|
336
|
-
return "failed";
|
|
344
|
+
return mutationReport("failed");
|
|
337
345
|
}
|
|
338
|
-
if (this.disposed) return "accepted";
|
|
346
|
+
if (this.disposed) return mutationReport("accepted");
|
|
339
347
|
if (generation === this.writeGeneration) {
|
|
340
348
|
this.pendingRevision = void 0;
|
|
341
349
|
this.mirror.acceptView(row);
|
|
342
350
|
} else this.pendingRevision = typeof row.revision === "number" ? row.revision : void 0;
|
|
343
|
-
return "accepted";
|
|
351
|
+
return mutationReport("accepted");
|
|
344
352
|
});
|
|
345
353
|
}
|
|
346
354
|
async mutateRemote(ops, revision) {
|
|
@@ -406,9 +414,9 @@ window.__ModuleLoader__.load({
|
|
|
406
414
|
}
|
|
407
415
|
/** Queue operations one at a time; disposal makes the queue inert. */
|
|
408
416
|
enqueue(operation) {
|
|
409
|
-
if (this.disposed) return Promise.resolve("cancelled");
|
|
417
|
+
if (this.disposed) return Promise.resolve(mutationReport("cancelled"));
|
|
410
418
|
const task = this.tail.then(async () => {
|
|
411
|
-
if (this.disposed) return "cancelled";
|
|
419
|
+
if (this.disposed) return mutationReport("cancelled");
|
|
412
420
|
return operation();
|
|
413
421
|
});
|
|
414
422
|
this.tail = task.then(() => {}, () => {});
|
|
@@ -780,22 +788,26 @@ window.__ModuleLoader__.load({
|
|
|
780
788
|
return success;
|
|
781
789
|
}
|
|
782
790
|
async commit(ops, baseline) {
|
|
783
|
-
if (this.closed) return "cancelled";
|
|
791
|
+
if (this.closed) return { result: "cancelled" };
|
|
784
792
|
const snapshot = this.scope.getSnapshot();
|
|
785
|
-
if (!snapshot.writable || this.scope.mutate === void 0) return "failed";
|
|
786
|
-
if (baseline && [...baseline].some(([field, before]) => !sameValue(before, fieldBaseline(snapshot, field)))) return "conflict";
|
|
793
|
+
if (!snapshot.writable || this.scope.mutate === void 0) return { result: "failed" };
|
|
794
|
+
if (baseline && [...baseline].some(([field, before]) => !sameValue(before, fieldBaseline(snapshot, field)))) return { result: "conflict" };
|
|
787
795
|
try {
|
|
788
796
|
return await this.scope.mutate(ops, snapshot.revision);
|
|
789
|
-
} catch {
|
|
790
|
-
return
|
|
797
|
+
} catch (error) {
|
|
798
|
+
return {
|
|
799
|
+
result: "failed",
|
|
800
|
+
message: readableErrorText(error)
|
|
801
|
+
};
|
|
791
802
|
}
|
|
792
803
|
}
|
|
793
804
|
async configure(outcome, ops, baseline) {
|
|
794
805
|
if (ops.length === 0) return true;
|
|
795
806
|
const confirmation = await this.commit(ops, baseline);
|
|
796
|
-
outcome.config = confirmation === "accepted" ? "confirmed" : confirmation === "conflict" ? "conflict" : "unconfirmed";
|
|
797
|
-
if (confirmation === "
|
|
798
|
-
|
|
807
|
+
outcome.config = confirmation.result === "accepted" ? "confirmed" : confirmation.result === "conflict" ? "conflict" : "unconfirmed";
|
|
808
|
+
if (confirmation.result === "failed" && confirmation.message !== void 0) outcome.hostError = confirmation.message;
|
|
809
|
+
if (confirmation.result === "cancelled" || this.closed) outcome.issue = "closed";
|
|
810
|
+
return confirmation.result === "accepted" && !this.closed;
|
|
799
811
|
}
|
|
800
812
|
async changeCredential(outcome, ref, value, defaultRef) {
|
|
801
813
|
outcome.credential = "not-issued";
|
|
@@ -963,9 +975,15 @@ window.__ModuleLoader__.load({
|
|
|
963
975
|
path: ["credentialCleanupRefs"],
|
|
964
976
|
value: this.cleanupRefs().filter((entry) => entry !== ref)
|
|
965
977
|
}])) {
|
|
966
|
-
if (outcome.config === "none")
|
|
978
|
+
if (outcome.config === "none") {
|
|
979
|
+
outcome.config = cleanup.config;
|
|
980
|
+
if (cleanup.hostError !== void 0) outcome.hostError = cleanup.hostError;
|
|
981
|
+
}
|
|
967
982
|
outcome.issue = cleanup.issue ?? "cleanup-pending";
|
|
968
|
-
} else if (outcome.config === "none")
|
|
983
|
+
} else if (outcome.config === "none") {
|
|
984
|
+
outcome.config = cleanup.config;
|
|
985
|
+
if (cleanup.hostError !== void 0) outcome.hostError = cleanup.hostError;
|
|
986
|
+
}
|
|
969
987
|
}
|
|
970
988
|
};
|
|
971
989
|
//#endregion
|
|
@@ -3243,7 +3261,9 @@ window.__ModuleLoader__.load({
|
|
|
3243
3261
|
saved: "设置已保存",
|
|
3244
3262
|
saveFailed: "保存失败,请重试。",
|
|
3245
3263
|
writeConflict: "待提交字段已被其他操作修改,配置未写入;请核对后再保存。",
|
|
3246
|
-
writeConfigUnconfirmed: "
|
|
3264
|
+
writeConfigUnconfirmed: "配置提交未获确认,未写入宿主;请核对后重试。",
|
|
3265
|
+
writeConfigUnconfirmedKey: "配置与密钥均未写入宿主;请核对后重试。",
|
|
3266
|
+
writeHostError: "宿主返回:{message}",
|
|
3247
3267
|
writeConfigSaved: "配置已保存。",
|
|
3248
3268
|
writeKeyNotIssued: "密钥操作尚未发出;请核对后重试。",
|
|
3249
3269
|
writeKeyUnconfirmed: "密钥操作未获确认;请核对后手动重试。",
|
|
@@ -3514,7 +3534,9 @@ window.__ModuleLoader__.load({
|
|
|
3514
3534
|
saved: "Settings saved",
|
|
3515
3535
|
saveFailed: "Save failed, please retry.",
|
|
3516
3536
|
writeConflict: "Pending fields were modified by another operation; the configuration was not written. Please review and save again.",
|
|
3517
|
-
writeConfigUnconfirmed: "The configuration submission was not confirmed
|
|
3537
|
+
writeConfigUnconfirmed: "The configuration submission was not confirmed and was not written. Please review and retry.",
|
|
3538
|
+
writeConfigUnconfirmedKey: "Neither the configuration nor the key was written. Please review and retry.",
|
|
3539
|
+
writeHostError: "Host returned: {message}",
|
|
3518
3540
|
writeConfigSaved: "Configuration saved.",
|
|
3519
3541
|
writeKeyNotIssued: "The key operation has not been issued; please review and retry.",
|
|
3520
3542
|
writeKeyUnconfirmed: "The key operation was not confirmed; please review and retry manually.",
|
|
@@ -3699,7 +3721,7 @@ window.__ModuleLoader__.load({
|
|
|
3699
3721
|
else if (result.issue === "target-changed") key = result.credential === "not-issued" ? "writeTargetBefore" : "writeTargetAfter";
|
|
3700
3722
|
else if (result.issue === "cleanup-pending") key = "writeCleanupPending";
|
|
3701
3723
|
else if (result.config === "conflict") key = "writeConflict";
|
|
3702
|
-
else if (result.config === "unconfirmed") key = "writeConfigUnconfirmed";
|
|
3724
|
+
else if (result.config === "unconfirmed") key = result.credential === "not-issued" ? "writeConfigUnconfirmedKey" : "writeConfigUnconfirmed";
|
|
3703
3725
|
else if (result.credential === "unconfirmed") key = "writeKeyUnconfirmed";
|
|
3704
3726
|
else if (result.credential === "not-issued") key = "writeKeyNotIssued";
|
|
3705
3727
|
const messages = key ? [t(key)] : [];
|
|
@@ -3709,8 +3731,18 @@ window.__ModuleLoader__.load({
|
|
|
3709
3731
|
}
|
|
3710
3732
|
if (key && result.config === "confirmed" && result.issue !== "cleanup-pending") messages.unshift(t("writeConfigSaved"));
|
|
3711
3733
|
if (result.refreshFailed) messages.push(t("writeRefreshFailed"));
|
|
3734
|
+
const hostError = result.hostError;
|
|
3735
|
+
if (hostError !== void 0 && hostError.trim() !== "") messages.push(t("writeHostError").replace("{message}", () => hostErrorText(hostError)));
|
|
3712
3736
|
return messages.join(" ");
|
|
3713
3737
|
}
|
|
3738
|
+
/**
|
|
3739
|
+
* 宿主原文压成一行并限长:锁超时、写权限这类诊断足够;换行会撑高提示区,
|
|
3740
|
+
* 超长正文(整页 HTML、大对象)也不该被原样铺到设置页上。
|
|
3741
|
+
*/
|
|
3742
|
+
function hostErrorText(message) {
|
|
3743
|
+
const oneLine = message.replace(/\s+/g, " ").trim();
|
|
3744
|
+
return oneLine.length > 240 ? `${oneLine.slice(0, 240)}…` : oneLine;
|
|
3745
|
+
}
|
|
3714
3746
|
//#endregion
|
|
3715
3747
|
//#region src/plan-tiers.ts
|
|
3716
3748
|
/** Shared plan-tier presentation facts; safe to inline into the browser bundle. */
|
|
@@ -3864,7 +3896,7 @@ window.__ModuleLoader__.load({
|
|
|
3864
3896
|
}
|
|
3865
3897
|
//#endregion
|
|
3866
3898
|
//#region package.json
|
|
3867
|
-
var version = "0.16.
|
|
3899
|
+
var version = "0.16.4";
|
|
3868
3900
|
var repository = {
|
|
3869
3901
|
"type": "git",
|
|
3870
3902
|
"url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
|
package/lib/index.d.ts
CHANGED
|
@@ -61,7 +61,7 @@ type CommandCodeProtocol = 'cli' | 'openai' | 'messages';
|
|
|
61
61
|
declare const DEFAULT_MAX_OUTPUT_TOKENS = 131072;
|
|
62
62
|
//#endregion
|
|
63
63
|
//#region src/adapter.d.ts
|
|
64
|
-
declare const COMMAND_CODE_CLI_VERSION = "1.88.
|
|
64
|
+
declare const COMMAND_CODE_CLI_VERSION = "1.88.2";
|
|
65
65
|
declare const DEFAULT_API_BASE = "https://api.commandcode.ai";
|
|
66
66
|
/**
|
|
67
67
|
* The output budget this bundle asks for, and the ceiling it will never exceed.
|
|
@@ -1232,7 +1232,7 @@ declare const KNOWN_THINKING_MODELS: ReadonlySet<string>;
|
|
|
1232
1232
|
* `/docs/plans/max` and `/docs/resources/pricing-limits`). Each plan's model
|
|
1233
1233
|
* list is a superset of the one below it: Go ⊂ GOAT ⊂ Pro ⊂ Provider/Max.
|
|
1234
1234
|
* Models absent from every plan list (Claude Opus/Fable, Fugu Ultra) are
|
|
1235
|
-
* Provider-tier. Re-verified at command-code@1.88.
|
|
1235
|
+
* Provider-tier. Re-verified at command-code@1.88.2 (2026-10-31) against the
|
|
1236
1236
|
* live `?view=client` catalog: 106 ids, still a strict superset chain. Mistral
|
|
1237
1237
|
* Large 4 (1.75.0) was the only addition at the 1.77.0 check; the 1.78.0/1.79.0 pair
|
|
1238
1238
|
* added Claude Haiku 5.5 at Pro and the free Glyph Cluster preview at Go.
|
package/lib/index.js
CHANGED
|
@@ -123,7 +123,7 @@ const PLAN_ORDER = Object.fromEntries(PLAN_TIER_ORDER.map((tier, index) => [tier
|
|
|
123
123
|
* subscription-plan labels, deals, and hourly (peak/off-peak) pricing.
|
|
124
124
|
*
|
|
125
125
|
* Everything here is synced from official sources — the command-code CLI
|
|
126
|
-
* bundle's model table (`dist/cli.mjs`, re-verified at command-code@1.88.
|
|
126
|
+
* bundle's model table (`dist/cli.mjs`, re-verified at command-code@1.88.2), the
|
|
127
127
|
* live model catalog (`GET /provider/v1/models?view=client`, the authority for
|
|
128
128
|
* capability fields since 1.82.4), and the official plan/pricing/model docs; see
|
|
129
129
|
* the dsh-commandcode-upstream skill for the extraction procedures. The
|
|
@@ -145,7 +145,11 @@ const PLAN_ORDER = Object.fromEntries(PLAN_TIER_ORDER.map((tier, index) => [tier
|
|
|
145
145
|
* step and `$2/$7.5` beyond. The 2026-10-28 check moves the constant to 1.88.1,
|
|
146
146
|
* whose registry now names both catalog-only rows (Kimi K3.1 Pro and GPT-6.1
|
|
147
147
|
* Terra) with efforts and context identical to this snapshot, so no table
|
|
148
|
-
* changes.
|
|
148
|
+
* changes. The 2026-10-31 check moves it to 1.88.2 — the npm package's own
|
|
149
|
+
* CHANGELOG names its sole change as a launch-crash fix after a fresh install
|
|
150
|
+
* or update — and again leaves every table here identical; that round's only
|
|
151
|
+
* data change is two per-model allowances in ./model-prices.ts. Keeping the
|
|
152
|
+
* snapshot in its own module
|
|
149
153
|
* confines those frequent sync diffs here: src/adapter.ts holds only the stable
|
|
150
154
|
* wire/runtime logic. The read helpers live here too, for the same reason.
|
|
151
155
|
*
|
|
@@ -517,6 +521,11 @@ const KNOWN_EFFORTS = {
|
|
|
517
521
|
"medium",
|
|
518
522
|
"high"
|
|
519
523
|
],
|
|
524
|
+
"inclusionai/ling-3.1-flash-1019:free": [
|
|
525
|
+
"low",
|
|
526
|
+
"medium",
|
|
527
|
+
"high"
|
|
528
|
+
],
|
|
520
529
|
"mistral/mistral-large-4": [
|
|
521
530
|
"low",
|
|
522
531
|
"medium",
|
|
@@ -948,7 +957,7 @@ const MODEL_OUTPUT_TOKEN_LIMITS = /* @__PURE__ */ new Map([
|
|
|
948
957
|
* `/docs/plans/max` and `/docs/resources/pricing-limits`). Each plan's model
|
|
949
958
|
* list is a superset of the one below it: Go ⊂ GOAT ⊂ Pro ⊂ Provider/Max.
|
|
950
959
|
* Models absent from every plan list (Claude Opus/Fable, Fugu Ultra) are
|
|
951
|
-
* Provider-tier. Re-verified at command-code@1.88.
|
|
960
|
+
* Provider-tier. Re-verified at command-code@1.88.2 (2026-10-31) against the
|
|
952
961
|
* live `?view=client` catalog: 106 ids, still a strict superset chain. Mistral
|
|
953
962
|
* Large 4 (1.75.0) was the only addition at the 1.77.0 check; the 1.78.0/1.79.0 pair
|
|
954
963
|
* added Claude Haiku 5.5 at Pro and the free Glyph Cluster preview at Go.
|
|
@@ -999,6 +1008,7 @@ const KNOWN_PLANS = {
|
|
|
999
1008
|
"gpt-6-luna": "go",
|
|
1000
1009
|
"meituan/LongCat-2.0": "go",
|
|
1001
1010
|
"inclusionai/ling-3.1-flash": "go",
|
|
1011
|
+
"inclusionai/ling-3.1-flash-1019:free": "go",
|
|
1002
1012
|
"meta/muse-spark-1.2-contributor": "go",
|
|
1003
1013
|
"meta/muse-spark-1.3-contributor": "go",
|
|
1004
1014
|
"meta/muse-spark-open": "go",
|
|
@@ -1203,6 +1213,11 @@ function modelVisibleForAnyAccount(modelId, accounts) {
|
|
|
1203
1213
|
}
|
|
1204
1214
|
const KNOWN_DEALS = {
|
|
1205
1215
|
"MiniMaxAI/MiniMax-M3": { label: "50% off" },
|
|
1216
|
+
"inclusionai/ling-3.1-flash-1019:free": {
|
|
1217
|
+
label: "FREE",
|
|
1218
|
+
free: true,
|
|
1219
|
+
expiresAt: "2026-11-03T16:00:00Z"
|
|
1220
|
+
},
|
|
1206
1221
|
"poolside/laguna-s-2.1-free": {
|
|
1207
1222
|
label: "FREE",
|
|
1208
1223
|
free: true
|
|
@@ -1651,8 +1666,8 @@ const MODEL_PRICE_ROWS = [
|
|
|
1651
1666
|
],
|
|
1652
1667
|
allowance: {
|
|
1653
1668
|
go: 10,
|
|
1654
|
-
goat:
|
|
1655
|
-
pro:
|
|
1669
|
+
goat: 100,
|
|
1670
|
+
pro: 110
|
|
1656
1671
|
}
|
|
1657
1672
|
},
|
|
1658
1673
|
{
|
|
@@ -1852,8 +1867,8 @@ const MODEL_PRICE_ROWS = [
|
|
|
1852
1867
|
],
|
|
1853
1868
|
allowance: {
|
|
1854
1869
|
go: 10,
|
|
1855
|
-
goat:
|
|
1856
|
-
pro:
|
|
1870
|
+
goat: 100,
|
|
1871
|
+
pro: 110
|
|
1857
1872
|
}
|
|
1858
1873
|
},
|
|
1859
1874
|
{
|
|
@@ -3934,6 +3949,76 @@ function readableErrorText(error) {
|
|
|
3934
3949
|
}
|
|
3935
3950
|
return String(error);
|
|
3936
3951
|
}
|
|
3952
|
+
//#endregion
|
|
3953
|
+
//#region src/transport-failure.ts
|
|
3954
|
+
/**
|
|
3955
|
+
* Transport failure reasons for the usage and settings diagnostics.
|
|
3956
|
+
*
|
|
3957
|
+
* Node's fetch rejects with `TypeError('fetch failed')` and keeps the real
|
|
3958
|
+
* cause one level down (`error.cause`): `ECONNREFUSED` when nothing listens on
|
|
3959
|
+
* the port, `ENOTFOUND` on DNS, a connect timeout behind a dead proxy. That
|
|
3960
|
+
* message alone cannot tell "the reverse proxy changed port" from "the proxy is
|
|
3961
|
+
* not running" from "the API is down", so per-endpoint failure lines and the
|
|
3962
|
+
* settings notice append a short Chinese reason plus the raw code (issue #111).
|
|
3963
|
+
*
|
|
3964
|
+
* Only the code is quoted back, never the system message: those may embed
|
|
3965
|
+
* internal paths, and the original message stays available through
|
|
3966
|
+
* `readableErrorText` for the detail line. An error with no code yields no
|
|
3967
|
+
* reason — the caller still renders what it has instead of an invented guess.
|
|
3968
|
+
*
|
|
3969
|
+
* @module dsh-commandcode-provider/transport-failure
|
|
3970
|
+
*/
|
|
3971
|
+
/** 原始错误码 → 界面原因;未列出的码只回显错误码,不做猜测。 */
|
|
3972
|
+
const REASONS$1 = {
|
|
3973
|
+
ECONNREFUSED: "连接被拒绝",
|
|
3974
|
+
ECONNRESET: "连接被重置",
|
|
3975
|
+
ENOTFOUND: "域名解析失败",
|
|
3976
|
+
EAI_AGAIN: "域名解析临时失败",
|
|
3977
|
+
EHOSTUNREACH: "主机不可达",
|
|
3978
|
+
ENETUNREACH: "网络不可达",
|
|
3979
|
+
ETIMEDOUT: "连接超时",
|
|
3980
|
+
UND_ERR_CONNECT_TIMEOUT: "连接超时",
|
|
3981
|
+
UND_ERR_SOCKET: "连接中断",
|
|
3982
|
+
UND_ERR_CLOSED: "连接已关闭",
|
|
3983
|
+
CERT_HAS_EXPIRED: "证书已过期",
|
|
3984
|
+
DEPTH_ZERO_SELF_SIGNED_CERT: "自签名证书不受信任",
|
|
3985
|
+
SELF_SIGNED_CERT_IN_CHAIN: "证书链中有自签名证书",
|
|
3986
|
+
UNABLE_TO_VERIFY_LEAF_SIGNATURE: "无法验证证书"
|
|
3987
|
+
};
|
|
3988
|
+
/** 取错误链上的第一个错误码:先看自身,再看 AggregateError 子项,最后看 cause。 */
|
|
3989
|
+
function codeOf(value, depth = 0) {
|
|
3990
|
+
if (depth > 4 || value === null || typeof value !== "object") return void 0;
|
|
3991
|
+
const entry = value;
|
|
3992
|
+
if (typeof entry.code === "string" && entry.code !== "") return entry.code;
|
|
3993
|
+
if (Array.isArray(entry.errors)) for (const nested of entry.errors) {
|
|
3994
|
+
const code = codeOf(nested, depth + 1);
|
|
3995
|
+
if (code !== void 0) return code;
|
|
3996
|
+
}
|
|
3997
|
+
return codeOf(entry.cause, depth + 1);
|
|
3998
|
+
}
|
|
3999
|
+
/**
|
|
4000
|
+
* One diagnostic clause for a transport failure, or undefined when the error
|
|
4001
|
+
* carries no recognizable code.
|
|
4002
|
+
*/
|
|
4003
|
+
function transportFailureReason(error) {
|
|
4004
|
+
const code = codeOf(error);
|
|
4005
|
+
if (code === void 0) return void 0;
|
|
4006
|
+
const reason = REASONS$1[code];
|
|
4007
|
+
return reason === void 0 ? `网络错误(${code})` : `${reason}(${code})`;
|
|
4008
|
+
}
|
|
4009
|
+
/**
|
|
4010
|
+
* `scheme://host:port` of a base, with path and query dropped: enough to name
|
|
4011
|
+
* the endpoint a failed request aimed at without echoing proxy prefixes, and
|
|
4012
|
+
* without any chance of a token riding along in a path.
|
|
4013
|
+
*/
|
|
4014
|
+
function httpOrigin(base) {
|
|
4015
|
+
try {
|
|
4016
|
+
const url = new URL(base);
|
|
4017
|
+
return `${url.protocol}//${url.host}`;
|
|
4018
|
+
} catch {
|
|
4019
|
+
return;
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
3937
4022
|
const THROTTLED_CODE = "THROTTLED";
|
|
3938
4023
|
const boundedCodes = /* @__PURE__ */ new Set([
|
|
3939
4024
|
"EMPTY_RESPONSE",
|
|
@@ -4758,6 +4843,8 @@ function generateHttpError(status, errText, retryAfterMs, parsed = parseProvider
|
|
|
4758
4843
|
if (status === 422 && isZdrNoProviders(providerCode, providerDetail, errText)) return bilingual("PROVIDER_HTTP_ERROR", `Command Code API error 422 (${detail}): zero data retention is enforced but no ZDR-capable upstream is available for this model — turn the ZDR setting off (or use another model)`, "Command Code API 返回 422:已开启零数据保留(ZDR),但该模型当前没有可用的 ZDR 上游——请关闭 ZDR 开关,或改用其他模型", { status: 422 });
|
|
4759
4844
|
if (status === 401) return bilingual("INVALID_CREDENTIAL", `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 文件", { status: 401 });
|
|
4760
4845
|
if (status === 413) return bilingual("PROVIDER_HTTP_ERROR", "Command Code API error 413: the request body exceeds the provider's size limit (the cap is undocumented, measured at about 50 MB) — the session history is too large to send, usually because of accumulated image attachments. Start a new session, or drop the image-heavy part of this one, and retry", "Command Code API 返回 413:请求体超过服务端的体积上限(官方未公开,实测约 50 MB)——会话历史过大,通常是历史图片累积所致。请新建会话,或移除本会话中图片较多的部分后重试", { status: 413 });
|
|
4846
|
+
const creditsCode = providerCode?.toUpperCase();
|
|
4847
|
+
if (creditsCode !== void 0 && INSUFFICIENT_CREDITS_CODES.has(creditsCode) || isInsufficientCreditsText(overflowDetail) || isInsufficientCreditsText(errText)) return bilingual(httpErrorCode(status), `Command Code API error ${status}: this account has insufficient credits for the request — top up credits or switch to another account`, `Command Code API 返回 ${status}:账户额度不足,无法完成本次请求——请充值额度,或切换到其他账号`, { status });
|
|
4761
4848
|
const safeIdentifier = (value) => value !== void 0 && /^[A-Za-z0-9_.:-]{1,80}$/.test(value) ? value : void 0;
|
|
4762
4849
|
const diagnosticCode = safeIdentifier(parsed.code) ?? /^\s*(MODEL_NOT_IN_PLAN|INSUFFICIENT_CREDITS|USAGE_EXCEEDED|PREMIUM_CREDITS_EXHAUSTED)\b/i.exec(parsed.message ?? "")?.[1];
|
|
4763
4850
|
const diagnosticType = safeIdentifier(parsed.type);
|
|
@@ -4850,6 +4937,30 @@ const ACCOUNT_UNAVAILABLE_CODES = /* @__PURE__ */ new Set([
|
|
|
4850
4937
|
"PREMIUM_CREDITS_EXHAUSTED",
|
|
4851
4938
|
"MODEL_NOT_IN_PLAN"
|
|
4852
4939
|
]);
|
|
4940
|
+
/**
|
|
4941
|
+
* 余额用尽的两种结构化代码(`USAGE_EXCEEDED`/`MODEL_NOT_IN_PLAN` 不属于这一类:
|
|
4942
|
+
* 前者是窗口额度,后者是套餐不含该模型,用户可行动的方向不同)。
|
|
4943
|
+
*/
|
|
4944
|
+
const INSUFFICIENT_CREDITS_CODES = /* @__PURE__ */ new Set(["INSUFFICIENT_CREDITS", "PREMIUM_CREDITS_EXHAUSTED"]);
|
|
4945
|
+
/**
|
|
4946
|
+
* 正文里「额度用尽」的特征。
|
|
4947
|
+
*
|
|
4948
|
+
* 账号轮换判定与用户可见文案共用这一份 pattern:两处各写一份必然漂移,出现
|
|
4949
|
+
* 「轮换认得出、文案认不出」的错位——2026-10-31 的实测就是这样,账号池已经按
|
|
4950
|
+
* 这个原因换号,而单账号场景下用户看到的仍是通用 400 提示。
|
|
4951
|
+
*
|
|
4952
|
+
* 下划线与空格是同一个词:JSON 正文常写 `insufficient_credits`,CLI 自己的
|
|
4953
|
+
* marker 列表写 `insufficient credits`,先归一分隔符再匹配。
|
|
4954
|
+
*/
|
|
4955
|
+
function isInsufficientCreditsText(text) {
|
|
4956
|
+
const lower = text.toLowerCase().replaceAll("_", " ");
|
|
4957
|
+
return lower.includes("insufficient credits") || lower.includes("premium credits exhausted") || /insufficient (credit|balance)/.test(lower) || /out of credit/.test(lower);
|
|
4958
|
+
}
|
|
4959
|
+
/** 正文里「模型不在本账号套餐内」的特征,与上者同一归一规则。 */
|
|
4960
|
+
function isModelOutsidePlanText(text) {
|
|
4961
|
+
const lower = text.toLowerCase().replaceAll("_", " ");
|
|
4962
|
+
return lower.includes("model not in plan") || /not (available|included)[^.]{0,40}plan/.test(lower) || lower.includes("upgrade your plan");
|
|
4963
|
+
}
|
|
4853
4964
|
function classifyAccountRejection(status, errText, parsed = parseProviderError(errText)) {
|
|
4854
4965
|
const code = (parsed.code ?? parsed.type)?.toUpperCase();
|
|
4855
4966
|
if (status === 429 || code === "RATE_LIMITED") {
|
|
@@ -4863,8 +4974,7 @@ function classifyAccountRejection(status, errText, parsed = parseProviderError(e
|
|
|
4863
4974
|
if (status === 401) return { reason: "invalid-credential" };
|
|
4864
4975
|
if (code !== void 0 && ACCOUNT_UNAVAILABLE_CODES.has(code)) return { reason: "unavailable" };
|
|
4865
4976
|
if (status < 400 || status >= 500) return void 0;
|
|
4866
|
-
|
|
4867
|
-
if (lower.includes("insufficient credits") || lower.includes("premium credits exhausted") || lower.includes("model not in plan") || /insufficient (credit|balance)/.test(lower) || /out of credit/.test(lower) || /not (available|included)[^.]{0,40}plan/.test(lower) || lower.includes("upgrade your plan")) return { reason: "unavailable" };
|
|
4977
|
+
if (isInsufficientCreditsText(errText) || isModelOutsidePlanText(errText)) return { reason: "unavailable" };
|
|
4868
4978
|
}
|
|
4869
4979
|
/**
|
|
4870
4980
|
* The usage-window evidence carried by a rejected request, mirroring the CLI's
|
|
@@ -6535,7 +6645,7 @@ var GatewayFacts = class {
|
|
|
6535
6645
|
* per-request options thunk and an API-key resolver from the plugin entry
|
|
6536
6646
|
* (src/index.ts), so a settings change reaches the very next request.
|
|
6537
6647
|
*/
|
|
6538
|
-
const COMMAND_CODE_CLI_VERSION = "1.88.
|
|
6648
|
+
const COMMAND_CODE_CLI_VERSION = "1.88.2";
|
|
6539
6649
|
const DEFAULT_API_BASE = "https://api.commandcode.ai";
|
|
6540
6650
|
/**
|
|
6541
6651
|
* The output budget this bundle asks for, and the ceiling it will never exceed.
|
|
@@ -8719,7 +8829,9 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8719
8829
|
}
|
|
8720
8830
|
return record;
|
|
8721
8831
|
} catch (error) {
|
|
8722
|
-
|
|
8832
|
+
const reason = transportFailureReason(error);
|
|
8833
|
+
const origin = httpOrigin(base);
|
|
8834
|
+
failures.push(`${path}: ${readableErrorText(error)}${reason === void 0 ? "" : `;${reason}`}${origin === void 0 ? "" : `;目标 ${origin}`}`);
|
|
8723
8835
|
failed.push({ kind: "transport" });
|
|
8724
8836
|
return;
|
|
8725
8837
|
}
|
|
@@ -9923,13 +10035,18 @@ function applyUsageRemote(ctx, deps) {
|
|
|
9923
10035
|
remoteCtx.effect(() => () => void unregister(), "dsh-commandcode-provider: usage remote");
|
|
9924
10036
|
});
|
|
9925
10037
|
}
|
|
10038
|
+
/** The endpoint whose answer proves the API's auth layer is reachable. */
|
|
10039
|
+
const PROBE_PATH = "/alpha/whoami";
|
|
9926
10040
|
/**
|
|
9927
|
-
* HTTP statuses
|
|
9928
|
-
*
|
|
9929
|
-
*
|
|
9930
|
-
*
|
|
10041
|
+
* HTTP statuses that prove the probed endpoint answered.
|
|
10042
|
+
*
|
|
10043
|
+
* 2xx/3xx are the positive answers. 401/403 belong here because the probe is
|
|
10044
|
+
* deliberately credential-less: the expected answer to an unauthenticated GET
|
|
10045
|
+
* of `/alpha/whoami` IS 401, and treating it as a failure would report every
|
|
10046
|
+
* healthy host as down. Everything else (404 from a wrong base path, 429, 5xx)
|
|
10047
|
+
* means the endpoint the plugin actually uses did not answer as a live API.
|
|
9931
10048
|
*/
|
|
9932
|
-
const
|
|
10049
|
+
const REACHABLE_STATUSES = /* @__PURE__ */ new Set([
|
|
9933
10050
|
200,
|
|
9934
10051
|
201,
|
|
9935
10052
|
202,
|
|
@@ -9948,25 +10065,27 @@ const UP_STATUSES = /* @__PURE__ */ new Set([
|
|
|
9948
10065
|
305,
|
|
9949
10066
|
306,
|
|
9950
10067
|
307,
|
|
9951
|
-
308
|
|
10068
|
+
308,
|
|
10069
|
+
401,
|
|
10070
|
+
403
|
|
9952
10071
|
]);
|
|
9953
10072
|
/** Turn the response status into the outcome the row renders. */
|
|
9954
10073
|
function probeOutcomeForStatus(status) {
|
|
9955
|
-
return
|
|
10074
|
+
return REACHABLE_STATUSES.has(status) ? "ok" : "http-error";
|
|
9956
10075
|
}
|
|
9957
10076
|
/**
|
|
9958
10077
|
* Probe the configured API base once. Never rejects: an invalid base, a
|
|
9959
10078
|
* timeout and a transport failure all resolve to an outcome.
|
|
9960
10079
|
*
|
|
9961
|
-
* `redirect: 'manual'` keeps the classification on the FIRST response
|
|
9962
|
-
*
|
|
9963
|
-
* destination's status as if it were the API's own.
|
|
10080
|
+
* `redirect: 'manual'` keeps the classification on the FIRST response; the
|
|
10081
|
+
* probe reports what this Host reached, not where a redirect chain ends up.
|
|
9964
10082
|
*/
|
|
9965
10083
|
async function probeCommandCode(options) {
|
|
9966
10084
|
const now = options.now ?? Date.now;
|
|
9967
10085
|
let url;
|
|
9968
10086
|
try {
|
|
9969
|
-
|
|
10087
|
+
if (!URL.canParse(options.apiBase)) throw new TypeError("invalid api base");
|
|
10088
|
+
url = `${options.apiBase}${PROBE_PATH}`;
|
|
9970
10089
|
} catch {
|
|
9971
10090
|
return {
|
|
9972
10091
|
outcome: "unreachable",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mars-sea/dsh-commandcode-provider",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.4",
|
|
4
4
|
"description": "Unofficial DeepSeek Harness LLM provider plugin for Command Code, ported from pi-commandcode-provider (MIT). Registers the 'commandcode' provider route with a Models-page card and live model catalog.",
|
|
5
5
|
"icon": "./icon.svg",
|
|
6
6
|
"type": "module",
|