@mars-sea/dsh-commandcode-provider 0.15.3 → 0.15.5
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 +47 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/lib/client.js +134 -73
- package/lib/index.d.ts +58 -6
- package/lib/index.js +303 -56
- package/package.json +8 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,53 @@ 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.15.5] - 2026-10-27
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **请求路径与账户读取的一批性能优化(线上字节不变)。**
|
|
13
|
+
- `streamAttempt()` 的凭据解析与正文装配改为并发:两者互不依赖(正文从不携带密钥,健康账号池的解析只读内存),从前却是严格串行,带图请求因此把图片读取与 JSON 装配整个排在 `resolveApiKey()` 之后。与密钥无关的通道判断先算成 `provisionalProtocol`,凭据返回后目标一致即复用,不一致才重建一次(与既有协议回退、账号轮换同价)。代价是 Go 计划/已学 CLI 通道的账号每次请求会多装配一次正文(线上字节不变,纯 CPU 开销)。图片预算的**会计**仍先于任何 I/O 执行,但**抛错**推迟到凭据与真实通道落定之后:预算门与缓存门一次算全、一次抛出。
|
|
14
|
+
- `listModels()` 在**本进程联网成功过**该网关目录时不再等一次联网刷新,改用内存中的行立即返回、后台刷新留给下次打开;进程首次加载仍照旧等待。新判据 `GatewayFacts.loaded()` 以「来源」为准而不是「内存里有行」:`snapshot()` 从磁盘灌入的行、以及刷新失败后的空目录都不算热数据(磁盘副本没有年龄校验)。`resolveModel()` 与 `stream()` 不变,仍等真实刷新。
|
|
15
|
+
- `gradeHeaderTimeout()` 的指纹改用**已经序列化好的实发字符串**求摘要(`sha256Hex()`),不再对 `body` 重新 `JSON.stringify`:超时是最常重复的路径,而带图历史可达数 MB。这也更准确——实发字符串就是网关看到的字节,重新 stringify 会在 surrogate 清洗真的删掉过字符时描述到不同的字节,把一次连续超时拆成两条 streak。
|
|
16
|
+
- `toolParametersSchema()` 的根节点结果按 `parameters` 对象身份记忆化(`WeakMap`)。每个通道在每次尝试时都重建工具列表,而账号轮换、协议回退、降档重试都会让同一批 schema 再走一遍。同时纠正原注释「每条路径都返回副本」——`type: 'object'` 快路径一直返回调用方自己的对象,调用方靠它拿到同一个已冻结的 schema。
|
|
17
|
+
- 用量卡片与选择器的套餐过滤共享账户端点响应:两处读的是同一批端点、同一个账号,成功响应按 `[apiBase, key, 完整 URL]` 记入短命表(`ACCOUNT_FACTS_TTL_MS` 30 秒)。实际共享的是 `whoami` 与 `subscriptions`——`credits` 的 URL 形状带 `orgId`,两处不同,不会互相命中。只缓存成功响应(失败必须重试,否则报告的 `failures` 与 `classifyTotalFailure` 结论会描述一次没真正发出的请求);`/alpha/usage/summary` 明确不共享——那是用户正盯着、旁边写着读取时间的数字。
|
|
18
|
+
- 用量刷新区分自动与显式路径:`refresh({ auto: true })` 用于账户卡片首屏、面板首屏与面板 tick(injected 面因此分成 `refreshUsageAuto()` 与 `refreshUsage()` 两个入口),可在 `USAGE_AUTO_REFRESH_COALESCE_MS`(5 秒)内复用刚刚读到的结果;按钮、保存、换凭据等显式调用不查合并窗口(请求在途时仍按既有 `inFlight` 规则折叠),`fetchedAt` 因此仍如实反映读取时刻。
|
|
19
|
+
- 面板自动刷新在文档隐藏时跳过读取但照旧重排定时器,重新可见立即补读一次(该补读走 auto,5 秒内仍可能被合并);首屏仍无条件读取。可见性做成可注入接缝(`AutoRefreshVisibility`),非浏览器环境视为始终可见;监听句柄随循环状态放在模块层,任意卸载顺序都能解绑。
|
|
20
|
+
- **设置页账户卡片:账期与更新时间并入余额标签行。** 「月额度剩余」标签右接账期区间、同行右侧显示「更新于」;原单独一行的「官方账期用量 …」与底部重复的「账期截止」删除。账期未对齐时标签改接订阅自身的「账期截止」日期,不再显示「统计周期未确认」(额度面板不变);余额没取到时该行仍保留,区间前补「官方账期用量」。底部只在部分接口失败时显示「部分数据未取到」。
|
|
21
|
+
|
|
22
|
+
- **同步上游两处:Command Code 目录新增 `gpt-6.1-terra`,DSH `0.2.1-rc.4` 补入验证矩阵。**
|
|
23
|
+
- **Command Code(CLI 版本不变,仍 `1.88.0`)**:2026-10-27 复核 npm `latest` 与官网 changelog 均无新 CLI 版本(最新条目仍是 `1.87.3`);服务端目录 `?view=client` 的 `catalogVersion` 由 `f41a48ed…` 变为 `7f405e24…`,默认视图 93 → 94、client 视图 105 → 106,唯一新增行是 `gpt-6.1-terra`(GPT-6.1 Terra):1,050,000 上下文、文本 + 图像、推理档位 `low`/`medium`/`high`/`xhigh`/`max`、最低计划 Pro、`$1/$5`(≤272K 上下文)与 `$2/$7.5`(>272K)、缓存读取 `$0.05`/`$0.1`、缓存写入 `$1.25`/`$2.5`、月度额度 Go `$6`/GOAT `$20`/Pro `$20`,ZDR 默认覆盖,无促销、无峰值时段、无输出上限公布值。官方 models 页与 pricing 页均已上架,`1.88.0` 的 CLI 注册表尚未收录(审计 `incomplete`,能力字段以 `?view=client` 目录为准)。快照 +1:`KNOWN_PLANS` 93 → 94、`KNOWN_EFFORTS` 66 → 67、`KNOWN_IMAGE_MODELS` 72 → 73、内置价格行 93 → 94;ZDR 例外集(18)、自动思考模型表(16)、订阅表(9)、促销(4)、输出上限表(79)不变。`scripts/sync-model-prices.mjs` 重跑(页面 94 行,diff 仅新增该行)。
|
|
24
|
+
- **DSH `0.2.1-rc.4`**:npm `next` 于北京时间 2026-10-26 23:28(`2026-10-26T15:28:13Z`)从 `0.2.1-rc.3` 前进到 `0.2.1-rc.4`,`latest` 仍是 `0.2.1-rc.2`;GitHub 无对应 tag/release(均 404),`master` 停在 10-23,产物内构建哈希 `ec17a2c` 在公开仓库不可见——官方没有发布说明。产物级全量比对 235 个双版本包,实质变化仅 4 处:`dsh-mcp-client` 的资源能力探测(服务器未声明 `resources` 时直接返回空列表)、`dsh-client-ui-conversation` 的 View 标签提示框(按字符宽度估算、仅溢出时显示)与「更多会话视图」文案精简、`dsh-llm-deepseek-account` 的一条日志级别(`console.info` → `console.error`)、`dsh-mcp-resources` 的文档;另有 3 个包仅构建元数据(commit 短哈希、版本串)变化。插件导入的宿主包(`llm`/`settings`/`credentials`/`client-*`/`web`/`typert`/`api-remotes`/`attachment`/`commands`/`timeout`/`launch-environment`/`app-boot`)全部零功能变化。`dsh.compatibility.dshReleases` 补入 `0.2.1-rc.4`(三条实测记录),`test:engine`/`test:install` 矩阵随之变为 3 个引擎。
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **图片预算门不再抢在凭据之前抛错。** 预算门只做会计,凭据解析失败时先报 `MISSING_CREDENTIAL`:避免宿主对一次根本发不出去的请求先做不可逆的持久化图片卸载。预算门与缓存门在凭据与真实通道落定后一次算全(`max(预算, 缓存)`),不再退化成两步卸载,也不会把 cache 溢出报成 budget。(`src/adapter.ts`)
|
|
29
|
+
- **`loaded()` 只认联网来源。** `snapshot()` 灌入的磁盘行与刷新失败后的空目录都不再被当成选择器首屏:冷启动"先聊天再开选择器"不会画出无年龄校验的旧目录,刷新失败后也不会先闪一次空列表。(`src/gateway-facts.ts`)
|
|
30
|
+
- **可见性监听按任意卸载顺序解绑。** `unsubscribe` 句柄从函数局部提升到模块层,启动循环的 surface 先卸载时不再泄漏 `visibilitychange` 监听。(`src/client/panel.ts`)
|
|
31
|
+
- **账户卡片首屏接线补真实渲染测试。** 新增 `tests/accounts-card-render.test.ts`(`react-test-renderer`):首屏必须调 `onRefreshAuto()`、刷新按钮必须调 `onRefresh()`;把组件改回旧接线时该用例失败。此前只有"两个入口存在"的断言,回退后测试仍全绿。
|
|
32
|
+
- **非安全上下文(`http://` 局域网访问)下设置页不再整块空白(issue #102)。** 页面身份原来直接取 `crypto.randomUUID()`,而该 API 只在安全上下文存在:以 `http://<局域网 IP>:3080` 打开时它是 `undefined`,控制器又在 `AccountsCard` 的 `useState(createEnrollment)` 惰性初始化里构造,异常被设置页插槽的错误边界接住——左侧「Command Code」标签正常、右侧内容区空白且无任何提示。新增 `src/client/random-id.ts` 的 `randomId()`:`randomUUID` 可用时照旧,否则退回非安全上下文同样可用的 `crypto.getRandomValues`,按 RFC 4122 拼出同形 v4;`AccountEnrollmentController` 的默认 `newId` 改用它。页面身份只是宿主把开通过程绑到单个浏览器页面的分组键,不参与鉴权,因此不要求 HTTPS。(`src/client/random-id.ts`、`src/client/enrollment.ts`)
|
|
33
|
+
|
|
34
|
+
用例:`tests/adapter.test.ts` 新增「凭据解析与图片读取并行」「目录已在内存时不再等一次联网刷新」「超时定级复用已序列化的正文」「同一份工具 schema 只归一化一次」「用量卡片与选择器共享同一份账户事实」「凭据缺失时预算门不抢跑」「双超限一次报数」;`tests/gateway-facts.test.ts` 新增 4 条 `loaded()` 来源判据;`tests/panel.test.ts` 与 `tests/usage-client.test.ts` 新增自动/显式刷新、可见性与任意卸载顺序解绑用例;`tests/accounts-card-render.test.ts` 新增 2 条真实渲染接线用例。**验证边界**:`npm run typecheck` 退出码 0;`npm ci` 干净安装后 `npm test` **1125/1125** 通过;`npm run build` 重建 `lib/`。未在真实宿主中人工验证。
|
|
35
|
+
|
|
36
|
+
issue #102 的用例:`tests/random-id.test.ts` 3 条——安全上下文走平台 `randomUUID`(哨兵值分辨分支,不走自行拼装)、非安全上下文退回 `getRandomValues` 时 100 个身份互不相同且为 v4、非安全上下文下构造 `AccountEnrollmentController` 并把页面身份带给宿主。三处定向变异各命中一条:关闭快速路径只失败第一条,把版本位置 0 只失败后两条,把默认 `newId` 改回 `() => crypto.randomUUID()` 只失败第三条。**验证边界**:`npm run typecheck` 退出码 0;`npm ci` 干净安装后 `npm test` **1129/1129** 通过;`npm run build` 重建 `lib/`(`lib/client.js` 含 `randomId`,`lib/index.js` 与 `lib/index.d.ts` 字节不变)。未在真实 NAS/局域网环境人工验证。
|
|
37
|
+
|
|
38
|
+
**本轮(2026-10-27 上游同步)验证**:`npm run typecheck` 退出码 0;`npm test` **1126/1126** 通过、0 失败;`npm run build` 两段 `Build complete`,`lib/index.js` 已重建(含 `gpt-6.1-terra`);`npm run test:engine` **3/3**(`0.2.1-rc.2`/`0.2.1-rc.3`/`0.2.1-rc.4`,宿主准入 preflight 全部放行,末行 `engine-load smoke passed on all 3 declared-compatible engines`);`npm run test:install` **3/3**(pnpm 10.34.5);`npm run test:pack` 16 文件(版本 0.15.4)。未做真实账号登录、付费生成、服务端协议验证与浏览器渲染。
|
|
39
|
+
|
|
40
|
+
## [0.15.4] - 2026-10-24
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- **同步 Command Code CLI `1.87.3` → `1.88.0`(纯请求头同步)。** `1.88.0` 于 `2026-10-24T02:55Z` 发布,官方变更日志还没有条目(最新 CLI 条目仍是 `1.87.3`),因此结论来自产物级静态比对:端点集合、自定义请求头、`/alpha/generate` 调用点、`promptCache` / `reasoning_effort` / `max_tokens` 字面量、模型注册表字段、推理档位、订阅表与 ZDR(零数据保留)例外集逐项相同(审计 `cliDiff` 的 efforts / subscriptions / zdrExceptions 三类全空);注册表唯一变化是 `xiaomi/mimo-v2.5` 与 `xiaomi/mimo-v2.5-pro` 增加 `hidden: true`,与 10-23 目录隐藏这两行一致。`COMMAND_CODE_CLI_VERSION` 只随请求头更新,运行时行为不变。
|
|
45
|
+
|
|
46
|
+
- **新增 `moonshotai/Kimi-K3.1-Pro`(服务端目录先行,CLI 产物尚未收录)。** `catalogVersion` 由 `02bfc7ac…` 变为 `f41a48ed…`,默认目录 92 → 93、`?view=client` 104 → 105,唯一新增行就是 Kimi K3.1 Pro:1,048,576 上下文、文本 + 图像、推理档位 `low` / `high` / `max`、最低计划 Go、`$2/$10/$0.2`、月度额度 Go `$6` / GOAT `$20` / Pro `$30`,无促销、无峰值时段、无上下文分档,ZDR 默认覆盖。官方 models 页、pricing 页与 go/goat/pro 计划页均已上架;`1.88.0` 的 CLI 注册表还没写这一行,审计把「absent from CLI registry」报成 error——能力字段以 `?view=client` 目录为准。快照随之 +1:`KNOWN_PLANS` 92 → 93、`KNOWN_EFFORTS` 65 → 66、`KNOWN_IMAGE_MODELS` 71 → 72、内置价格行 92 → 93;ZDR 例外集(18)、自动思考模型表(16)、输出上限表(79)、订阅表(9)、促销(4)与峰值定价(4)不变。
|
|
47
|
+
|
|
48
|
+
### Verification
|
|
49
|
+
|
|
50
|
+
- `npm run typecheck` 退出码 0;`npm test` **1107/1107** 通过、0 失败;`npm run build` 两段 `Build complete`,`lib/index.js` 与 `lib/index.d.ts` 已重建(含 `1.88.0` 与 `kimi-k3.1-pro`)。
|
|
51
|
+
- 上游公开 GET 采集 14 个源全部 HTTP 200;审计状态 `incomplete` 的 3 条 error 为 Kimi K3.1 Pro 不在 CLI 产物注册表,以及 MiniMax M3.1 Flash 与 Ornith 1.5 122B 的已知定价别名缺失,均非抓取失败。
|
|
52
|
+
- **未做**:真实账号登录、付费生成、服务端协议验证与浏览器渲染。
|
|
53
|
+
|
|
7
54
|
## [0.15.3] - 2026-10-24
|
|
8
55
|
|
|
9
56
|
### Changed
|
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ Unofficial [DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harnes
|
|
|
61
61
|
dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@latest
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
This build admits **dsh `>=0.2.1-rc.2 <0.2.2-0`** — every RC of the `0.2.1` channel from `0.2.1-rc.2` onward. `0.2.1-rc.2` is what npm `latest` runs and `0.2.1-rc.
|
|
64
|
+
This build admits **dsh `>=0.2.1-rc.2 <0.2.2-0`** — every RC of the `0.2.1` channel from `0.2.1-rc.2` onward. `0.2.1-rc.2` is what npm `latest` runs and `0.2.1-rc.4` ships on `next`; `0.2.1-rc.2`, `0.2.1-rc.3` and `0.2.1-rc.4` are all covered by the full engine matrix (`npm run test:engine`, `npm run test:install`). A later `0.2.1-rc.5` is admitted without a plugin update, but stays unverified until a record is added to `dsh.compatibility.dshReleases`. The next minor is deliberately excluded, `0.2.2-rc.*` included — the upper bound is written `<0.2.2-0` for exactly that reason. A profile on `0.2.0-rc.2`, `0.2.1-alpha.5` or older must upgrade its engine first. Compatibility details — the `engines.dsh` admission rule, package pairings, evidence — live in [engine compatibility](docs/agent-reference/engine-compatibility.md).
|
|
65
65
|
|
|
66
66
|
**Older dsh releases** are no longer supported: the last plugin release for dsh 0.1.7 is `0.11.17`, for 0.1.2–0.1.6 it is `0.11.11`, and for the 0.5.0-era line `0.9.1` — all installed by exact version, none maintained.
|
|
67
67
|
|
package/README.zh-CN.md
CHANGED
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@latest
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
本构建准入 **dsh `>=0.2.1-rc.2 <0.2.2-0`** —— `0.2.1` 通道中 `0.2.1-rc.2` 及以后的每个 RC。`0.2.1-rc.2` 是 npm `latest` 指向的版本,`0.2.1-rc.
|
|
64
|
+
本构建准入 **dsh `>=0.2.1-rc.2 <0.2.2-0`** —— `0.2.1` 通道中 `0.2.1-rc.2` 及以后的每个 RC。`0.2.1-rc.2` 是 npm `latest` 指向的版本,`0.2.1-rc.4` 挂在 `next`;`0.2.1-rc.2`、`0.2.1-rc.3` 与 `0.2.1-rc.4` 都跑过完整引擎矩阵(`npm run test:engine`、`npm run test:install`)。更晚的 `0.2.1-rc.5` 无需更新插件即可准入,但在 `dsh.compatibility.dshReleases` 补上记录之前不算已验证。下一个 minor 被刻意排除(含 `0.2.2-rc.*`)—— 区间上界写成 `<0.2.2-0` 正是为了这一点。使用 `0.2.0-rc.2`、`0.2.1-alpha.5` 或更早版本的配置需先升级引擎,再安装本构建。兼容性细节 —— `engines.dsh` 准入规则、按引擎配对的包版本、验证证据 —— 见[引擎兼容](docs/agent-reference/engine-compatibility.md)。
|
|
65
65
|
|
|
66
66
|
**更早的 dsh 版本**已不再支持:覆盖 dsh 0.1.7 的最后一个插件版本是 `0.11.17`,0.1.2–0.1.6 线的最后一个版本是 `0.11.11`,0.5.0 时代 Harness 线的最后一个版本是 `0.9.1` —— 三者都按精确版本安装,且都不再维护。
|
|
67
67
|
|
package/lib/client.js
CHANGED
|
@@ -1856,6 +1856,8 @@ window.__ModuleLoader__.load({
|
|
|
1856
1856
|
generation = 0;
|
|
1857
1857
|
inFlight = false;
|
|
1858
1858
|
disposed = false;
|
|
1859
|
+
/** When the last fetch SETTLED successfully, for {@link USAGE_AUTO_REFRESH_COALESCE_MS}. */
|
|
1860
|
+
settledAt;
|
|
1859
1861
|
constructor(remote) {
|
|
1860
1862
|
this.remote = remote;
|
|
1861
1863
|
}
|
|
@@ -1877,9 +1879,16 @@ window.__ModuleLoader__.load({
|
|
|
1877
1879
|
/**
|
|
1878
1880
|
* Fetch (or refetch) the report. Concurrent refreshes collapse onto one
|
|
1879
1881
|
* request; a superseded fetch's late result is dropped, never published.
|
|
1882
|
+
*
|
|
1883
|
+
* `auto` marks a refresh nobody asked for — a surface appearing, or the
|
|
1884
|
+
* background tick. Those also reuse a result fetched within
|
|
1885
|
+
* {@link USAGE_AUTO_REFRESH_COALESCE_MS}, because two surfaces mounting a
|
|
1886
|
+
* second apart are asking the same question. Every other caller is a user
|
|
1887
|
+
* action and always goes to the wire.
|
|
1880
1888
|
*/
|
|
1881
|
-
async refresh() {
|
|
1889
|
+
async refresh(options) {
|
|
1882
1890
|
if (this.disposed || this.inFlight) return;
|
|
1891
|
+
if (options?.auto === true && this.current.status === "ready" && this.settledAt !== void 0 && Date.now() - this.settledAt < 5e3) return;
|
|
1883
1892
|
const generation = ++this.generation;
|
|
1884
1893
|
this.inFlight = true;
|
|
1885
1894
|
this.current = {
|
|
@@ -1891,13 +1900,16 @@ window.__ModuleLoader__.load({
|
|
|
1891
1900
|
try {
|
|
1892
1901
|
const response = await this.remote.report();
|
|
1893
1902
|
if (this.disposed || generation !== this.generation) return;
|
|
1894
|
-
if (response.ok)
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1903
|
+
if (response.ok) {
|
|
1904
|
+
const fetchedAt = Date.now();
|
|
1905
|
+
this.settledAt = fetchedAt;
|
|
1906
|
+
this.current = {
|
|
1907
|
+
status: "ready",
|
|
1908
|
+
report: response.value,
|
|
1909
|
+
error: void 0,
|
|
1910
|
+
fetchedAt
|
|
1911
|
+
};
|
|
1912
|
+
} else this.current = {
|
|
1901
1913
|
...this.current,
|
|
1902
1914
|
status: "error",
|
|
1903
1915
|
error: response.error.message
|
|
@@ -2887,6 +2899,23 @@ window.__ModuleLoader__.load({
|
|
|
2887
2899
|
]
|
|
2888
2900
|
};
|
|
2889
2901
|
//#endregion
|
|
2902
|
+
//#region src/client/random-id.ts
|
|
2903
|
+
/**
|
|
2904
|
+
* 页面身份(issue #102)。`crypto.randomUUID` 是安全上下文专属:局域网里用
|
|
2905
|
+
* `http://<ip>:3080` 打开设置页时它是 `undefined`,而这里在控制器构造期就调用,
|
|
2906
|
+
* 异常会落到设置页插槽的错误边界上,整块内容变成空白。`crypto.getRandomValues`
|
|
2907
|
+
* 在非安全上下文同样可用,因此只在 `randomUUID` 不可用时按 RFC 4122 自行拼一个 v4。
|
|
2908
|
+
*/
|
|
2909
|
+
function randomId() {
|
|
2910
|
+
const source = globalThis.crypto;
|
|
2911
|
+
if (typeof source.randomUUID === "function") return source.randomUUID();
|
|
2912
|
+
const bytes = source.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
|
|
2913
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
2914
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
2915
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
2916
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
2917
|
+
}
|
|
2918
|
+
//#endregion
|
|
2890
2919
|
//#region src/client/enrollment.ts
|
|
2891
2920
|
const live = (state) => [
|
|
2892
2921
|
"creating",
|
|
@@ -2912,7 +2941,7 @@ window.__ModuleLoader__.load({
|
|
|
2912
2941
|
watch;
|
|
2913
2942
|
ready;
|
|
2914
2943
|
openedPage = false;
|
|
2915
|
-
constructor(remote, changed, pollMs = 750, newId =
|
|
2944
|
+
constructor(remote, changed, pollMs = 750, newId = randomId, openPage = openLoginPage) {
|
|
2916
2945
|
this.remote = remote;
|
|
2917
2946
|
this.changed = changed;
|
|
2918
2947
|
this.pollMs = pollMs;
|
|
@@ -3243,7 +3272,6 @@ window.__ModuleLoader__.load({
|
|
|
3243
3272
|
usageCost: "花费",
|
|
3244
3273
|
usageCompleted: "成功",
|
|
3245
3274
|
usageBillingPeriod: "官方账期用量",
|
|
3246
|
-
usagePeriodUnknown: "官方用量汇总,统计周期未确认",
|
|
3247
3275
|
usageTokens: "Token",
|
|
3248
3276
|
usageTokensIn: "入",
|
|
3249
3277
|
usageTokensOut: "出",
|
|
@@ -3330,7 +3358,7 @@ window.__ModuleLoader__.load({
|
|
|
3330
3358
|
accountEnrollmentCleanupRetry: "重试清理",
|
|
3331
3359
|
accountRefSeparator: ":",
|
|
3332
3360
|
accountModels: "专用模型",
|
|
3333
|
-
accountModelsHint: "
|
|
3361
|
+
accountModelsHint: "选中即专属:优先此账户处理,不可用时自动切换",
|
|
3334
3362
|
accountModelOwner: "属于 {name}",
|
|
3335
3363
|
modelPick: "选择模型…",
|
|
3336
3364
|
modelCount: "已选 {count} 个模型",
|
|
@@ -3515,7 +3543,6 @@ window.__ModuleLoader__.load({
|
|
|
3515
3543
|
usageCost: "Spend",
|
|
3516
3544
|
usageCompleted: "Completed",
|
|
3517
3545
|
usageBillingPeriod: "Official billing-period usage",
|
|
3518
|
-
usagePeriodUnknown: "Official usage summary; the billing period is unconfirmed",
|
|
3519
3546
|
usageTokens: "Tokens",
|
|
3520
3547
|
usageTokensIn: "in",
|
|
3521
3548
|
usageTokensOut: "out",
|
|
@@ -3602,7 +3629,7 @@ window.__ModuleLoader__.load({
|
|
|
3602
3629
|
accountEnrollmentCleanupRetry: "Retry cleanup",
|
|
3603
3630
|
accountRefSeparator: ": ",
|
|
3604
3631
|
accountModels: "Dedicated models",
|
|
3605
|
-
accountModelsHint: "
|
|
3632
|
+
accountModelsHint: "Selected models are dedicated to this account and still switch when unavailable",
|
|
3606
3633
|
accountModelOwner: "on {name}",
|
|
3607
3634
|
modelPick: "Choose models…",
|
|
3608
3635
|
modelCount: "{count} models selected",
|
|
@@ -3837,7 +3864,7 @@ window.__ModuleLoader__.load({
|
|
|
3837
3864
|
}
|
|
3838
3865
|
//#endregion
|
|
3839
3866
|
//#region package.json
|
|
3840
|
-
var version = "0.15.
|
|
3867
|
+
var version = "0.15.5";
|
|
3841
3868
|
var repository = {
|
|
3842
3869
|
"type": "git",
|
|
3843
3870
|
"url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
|
|
@@ -4877,6 +4904,19 @@ window.__ModuleLoader__.load({
|
|
|
4877
4904
|
]
|
|
4878
4905
|
});
|
|
4879
4906
|
}
|
|
4907
|
+
/**
|
|
4908
|
+
* Left text of the balance label row: the billing period, stated once per card.
|
|
4909
|
+
* 用量汇总与订阅起点对齐时给完整区间;对不上(或用量汇总没取到)时退回订阅
|
|
4910
|
+
* 自身的截止日,它不依赖对齐也可靠。余额没取到时没有「月额度剩余」可挂,完整
|
|
4911
|
+
* 区间前补回「官方账期用量」,避免一串裸日期没有上下文。
|
|
4912
|
+
*/
|
|
4913
|
+
function balanceLabelText(report, t) {
|
|
4914
|
+
const plan = report.plan;
|
|
4915
|
+
const usagePeriod = formatUsageBillingPeriod(report.usage?.periodStart, plan?.currentPeriodStart, plan?.currentPeriodEnd);
|
|
4916
|
+
const periodText = usagePeriod !== "" ? usagePeriod : plan !== void 0 && plan.currentPeriodEnd > 0 ? `${t("usagePeriodEnd")} ${new Date(plan.currentPeriodEnd).toLocaleDateString()}` : "";
|
|
4917
|
+
if (report.credits !== void 0) return periodText !== "" ? `${t("usageMonthlyLeft")} · ${periodText}` : t("usageMonthlyLeft");
|
|
4918
|
+
return usagePeriod !== "" ? `${t("usageBillingPeriod")} ${usagePeriod}` : periodText;
|
|
4919
|
+
}
|
|
4880
4920
|
/** One pool account's facts (identity, totals, credits, window limits). */
|
|
4881
4921
|
function AccountReport({ entry, fetchedAt, t, headless = false }) {
|
|
4882
4922
|
const report = entry.report;
|
|
@@ -4886,9 +4926,12 @@ window.__ModuleLoader__.load({
|
|
|
4886
4926
|
const plan = report.plan;
|
|
4887
4927
|
const planName = plan?.name ?? "";
|
|
4888
4928
|
const planStatus = plan !== void 0 && plan.status !== "" && plan.status !== "active" ? plan.status : "";
|
|
4889
|
-
const showPeriod = plan !== void 0 && plan.currentPeriodEnd > 0;
|
|
4890
4929
|
const showPartial = report.failures.length > 0 && report.blocked === void 0;
|
|
4891
|
-
const
|
|
4930
|
+
const label = balanceLabelText(report, t);
|
|
4931
|
+
const updatedText = fetchedAt === void 0 ? "" : `${t("usageUpdated")} ${new Date(fetchedAt).toLocaleTimeString(void 0, {
|
|
4932
|
+
hour: "2-digit",
|
|
4933
|
+
minute: "2-digit"
|
|
4934
|
+
})}`;
|
|
4892
4935
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4893
4936
|
className: "cc-accountReport",
|
|
4894
4937
|
children: [
|
|
@@ -4927,26 +4970,33 @@ window.__ModuleLoader__.load({
|
|
|
4927
4970
|
children: report.failures.join(" · ")
|
|
4928
4971
|
}) : null]
|
|
4929
4972
|
}) : null,
|
|
4930
|
-
|
|
4931
|
-
const monthlyKnown = credits.monthlyReported !== false;
|
|
4932
|
-
const depleted = monthlyKnown && credits.monthlyCredits <= 0;
|
|
4973
|
+
(() => {
|
|
4974
|
+
const monthlyKnown = credits !== void 0 && credits.monthlyReported !== false;
|
|
4975
|
+
const depleted = credits !== void 0 && monthlyKnown && credits.monthlyCredits <= 0;
|
|
4976
|
+
if (label === "" && updatedText === "") return null;
|
|
4933
4977
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4934
4978
|
className: depleted ? "cc-balance cc-balanceEmpty" : "cc-balance",
|
|
4935
4979
|
children: [
|
|
4936
4980
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4937
|
-
className: "cc-
|
|
4981
|
+
className: "cc-balanceRow",
|
|
4938
4982
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4939
4983
|
className: "cc-balanceLabel",
|
|
4940
|
-
children:
|
|
4941
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4984
|
+
children: label
|
|
4985
|
+
}), updatedText !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4986
|
+
className: "cc-balanceLabel",
|
|
4987
|
+
children: updatedText
|
|
4988
|
+
}) : null]
|
|
4989
|
+
}),
|
|
4990
|
+
credits !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4991
|
+
className: "cc-balanceRow",
|
|
4992
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4942
4993
|
className: "cc-balanceValue",
|
|
4943
4994
|
children: monthlyKnown ? formatMoney(Math.max(0, credits.monthlyCredits)) : "—"
|
|
4995
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4996
|
+
className: "cc-balanceSub",
|
|
4997
|
+
children: `${t("usagePurchased")} ${credits.purchasedReported === false ? "—" : formatMoney(credits.purchasedCredits)} · ${t("usageFree")} ${credits.freeReported === false ? "—" : formatMoney(credits.freeCredits)}`
|
|
4944
4998
|
})]
|
|
4945
|
-
}),
|
|
4946
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4947
|
-
className: "cc-balanceSub",
|
|
4948
|
-
children: `${t("usagePurchased")} ${credits.purchasedReported === false ? "—" : formatMoney(credits.purchasedCredits)} · ${t("usageFree")} ${credits.freeReported === false ? "—" : formatMoney(credits.freeCredits)}`
|
|
4949
|
-
}),
|
|
4999
|
+
}) : null,
|
|
4950
5000
|
depleted ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4951
5001
|
className: "cc-balanceNote",
|
|
4952
5002
|
role: "status",
|
|
@@ -4954,11 +5004,7 @@ window.__ModuleLoader__.load({
|
|
|
4954
5004
|
}) : null
|
|
4955
5005
|
]
|
|
4956
5006
|
});
|
|
4957
|
-
})()
|
|
4958
|
-
report.usage !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4959
|
-
className: "cc-usageUpdated",
|
|
4960
|
-
children: usagePeriod ? `${t("usageBillingPeriod")} ${usagePeriod}` : t("usagePeriodUnknown")
|
|
4961
|
-
}) : null,
|
|
5007
|
+
})(),
|
|
4962
5008
|
report.usage !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4963
5009
|
className: "cc-usageStats cc-usageStatsActivity",
|
|
4964
5010
|
children: [
|
|
@@ -5000,35 +5046,10 @@ window.__ModuleLoader__.load({
|
|
|
5000
5046
|
t
|
|
5001
5047
|
}) : null]
|
|
5002
5048
|
}) : null,
|
|
5003
|
-
|
|
5004
|
-
className: "cc-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
className: "cc-usageUpdated",
|
|
5008
|
-
children: [
|
|
5009
|
-
t("usagePeriodEnd"),
|
|
5010
|
-
" ",
|
|
5011
|
-
new Date(plan.currentPeriodEnd).toLocaleDateString()
|
|
5012
|
-
]
|
|
5013
|
-
}) : null,
|
|
5014
|
-
showPartial ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5015
|
-
className: "cc-usagePartial",
|
|
5016
|
-
title: report.failures.join("; "),
|
|
5017
|
-
children: t("usagePartial")
|
|
5018
|
-
}) : null,
|
|
5019
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-usageMetaSpacer" }),
|
|
5020
|
-
fetchedAt !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
5021
|
-
className: "cc-usageUpdated",
|
|
5022
|
-
children: [
|
|
5023
|
-
t("usageUpdated"),
|
|
5024
|
-
" ",
|
|
5025
|
-
new Date(fetchedAt).toLocaleTimeString(void 0, {
|
|
5026
|
-
hour: "2-digit",
|
|
5027
|
-
minute: "2-digit"
|
|
5028
|
-
})
|
|
5029
|
-
]
|
|
5030
|
-
}) : null
|
|
5031
|
-
]
|
|
5049
|
+
showPartial ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5050
|
+
className: "cc-usagePartial",
|
|
5051
|
+
title: report.failures.join("; "),
|
|
5052
|
+
children: t("usagePartial")
|
|
5032
5053
|
}) : null
|
|
5033
5054
|
]
|
|
5034
5055
|
});
|
|
@@ -6459,8 +6480,13 @@ window.__ModuleLoader__.load({
|
|
|
6459
6480
|
* The unified account card: every account the pool rotates through, one row
|
|
6460
6481
|
* each, with its status and quota inline — so one account's facts no longer
|
|
6461
6482
|
* have to be cross-read across separate surfaces.
|
|
6483
|
+
*
|
|
6484
|
+
* Exported for the render test (`tests/accounts-card-render.test.ts`) that pins
|
|
6485
|
+
* the first-paint auto-refresh wiring against a real React tree: the page mounts
|
|
6486
|
+
* this card, and a test that only checks the injected function exists cannot see
|
|
6487
|
+
* the card reaching for it.
|
|
6462
6488
|
*/
|
|
6463
|
-
function AccountsCard({ t, state, usage, login, disabled, actions, onRefresh, openAnalytics, createEnrollment, expanded, onToggle }) {
|
|
6489
|
+
function AccountsCard({ t, state, usage, login, disabled, actions, onRefresh, onRefreshAuto, openAnalytics, createEnrollment, expanded, onToggle }) {
|
|
6464
6490
|
const [adding, setAdding] = (0, react.useState)(false);
|
|
6465
6491
|
const [enrollment] = (0, react.useState)(createEnrollment);
|
|
6466
6492
|
const snapshot = (0, react.useSyncExternalStore)(enrollment.store.subscribe, enrollment.store.getSnapshot);
|
|
@@ -6471,8 +6497,8 @@ window.__ModuleLoader__.load({
|
|
|
6471
6497
|
const closeAdd = (0, react.useCallback)(() => setAdding(false), []);
|
|
6472
6498
|
const { loading, shouldRefresh } = usageCardState(usage);
|
|
6473
6499
|
(0, react.useEffect)(() => {
|
|
6474
|
-
if (shouldRefresh)
|
|
6475
|
-
}, [shouldRefresh,
|
|
6500
|
+
if (shouldRefresh) onRefreshAuto();
|
|
6501
|
+
}, [shouldRefresh, onRefreshAuto]);
|
|
6476
6502
|
const rows = accountRows(state, usage, t).filter((row) => row.ref === void 0 || !state.accountEnrollmentRefs.includes(row.ref));
|
|
6477
6503
|
const multi = rows.length > 1;
|
|
6478
6504
|
const pinnedRow = rows.find((row) => row.id === state.activeAccount);
|
|
@@ -6960,6 +6986,7 @@ window.__ModuleLoader__.load({
|
|
|
6960
6986
|
disabled,
|
|
6961
6987
|
actions,
|
|
6962
6988
|
onRefresh: props.refreshUsage,
|
|
6989
|
+
onRefreshAuto: props.refreshUsageAuto,
|
|
6963
6990
|
openAnalytics: props.openAnalytics,
|
|
6964
6991
|
createEnrollment: props.createEnrollment,
|
|
6965
6992
|
...foldProps("accounts")
|
|
@@ -7899,6 +7926,14 @@ window.__ModuleLoader__.load({
|
|
|
7899
7926
|
set: (callback, ms) => setTimeout(callback, ms),
|
|
7900
7927
|
clear: (handle) => clearTimeout(handle)
|
|
7901
7928
|
};
|
|
7929
|
+
const DOCUMENT_VISIBILITY = {
|
|
7930
|
+
isVisible: () => typeof document === "undefined" ? true : document.visibilityState !== "hidden",
|
|
7931
|
+
subscribe: (onChange) => {
|
|
7932
|
+
if (typeof document === "undefined") return () => void 0;
|
|
7933
|
+
document.addEventListener("visibilitychange", onChange);
|
|
7934
|
+
return () => document.removeEventListener("visibilitychange", onChange);
|
|
7935
|
+
}
|
|
7936
|
+
};
|
|
7902
7937
|
/** Live mount count of {@link startPanelAutoRefresh}. */
|
|
7903
7938
|
let references = 0;
|
|
7904
7939
|
/** Owner ticket of the running loop, if any. */
|
|
@@ -7908,6 +7943,16 @@ window.__ModuleLoader__.load({
|
|
|
7908
7943
|
/** Pending timer handle of the running loop. */
|
|
7909
7944
|
let handle;
|
|
7910
7945
|
/**
|
|
7946
|
+
* Unsubscribe of the visibility listener owned by the RUNNING loop.
|
|
7947
|
+
*
|
|
7948
|
+
* Module-level on purpose: only the surface that started the loop receives the
|
|
7949
|
+
* subscribe handle, but that surface is not necessarily the last to unmount.
|
|
7950
|
+
* Keeping the handle on the same layer as `activeTicket`/`handle` lets any
|
|
7951
|
+
* teardown order release it; a function-local handle leaked the listener
|
|
7952
|
+
* whenever the starter unmounted first.
|
|
7953
|
+
*/
|
|
7954
|
+
let visibilityUnsubscribe;
|
|
7955
|
+
/**
|
|
7911
7956
|
* Start the shared auto refresh. One fetch when the surface appears (the sidebar
|
|
7912
7957
|
* row is the point of the panel — it must be current, not wait for a click), then
|
|
7913
7958
|
* a tick every {@link PANEL_AUTO_REFRESH_MS} while a surface stays mounted.
|
|
@@ -7917,22 +7962,37 @@ window.__ModuleLoader__.load({
|
|
|
7917
7962
|
* tick goes through `usage.refresh()`, which collapses a concurrent fetch onto
|
|
7918
7963
|
* the in-flight one. Returns the disposer that drops this surface's reference.
|
|
7919
7964
|
*
|
|
7965
|
+
* A hidden document is still mounted, so its ticks are SKIPPED: a background
|
|
7966
|
+
* tab must not keep paying for a four-call report nobody reads. The tick is
|
|
7967
|
+
* rescheduled either way, so the loop survives, and becoming visible again
|
|
7968
|
+
* fetches at once — the data on screen is by then older than a tick would have
|
|
7969
|
+
* left it. The first paint still fetches unconditionally: it is the moment the
|
|
7970
|
+
* row appears, and there is nothing to show without it.
|
|
7971
|
+
*
|
|
7920
7972
|
* @param usage - the shared usage controller.
|
|
7921
7973
|
* @param isConfigured - whether a credential exists right now (re-read per tick).
|
|
7922
7974
|
* @param timer - timer seam for tests.
|
|
7975
|
+
* @param visibility - visibility seam; see {@link AutoRefreshVisibility}.
|
|
7923
7976
|
*/
|
|
7924
|
-
function startPanelAutoRefresh(usage, isConfigured, timer = REAL_TIMER) {
|
|
7977
|
+
function startPanelAutoRefresh(usage, isConfigured, timer = REAL_TIMER, visibility = DOCUMENT_VISIBILITY) {
|
|
7925
7978
|
references += 1;
|
|
7926
7979
|
const ticket = ++ticketSeq;
|
|
7980
|
+
const fetch = () => {
|
|
7981
|
+
if (isConfigured()) usage.refresh({ auto: true });
|
|
7982
|
+
};
|
|
7927
7983
|
const tick = () => {
|
|
7928
7984
|
if (activeTicket !== ticket) return;
|
|
7929
|
-
if (
|
|
7985
|
+
if (visibility.isVisible()) fetch();
|
|
7930
7986
|
handle = timer.set(tick, PANEL_AUTO_REFRESH_MS);
|
|
7931
7987
|
};
|
|
7932
7988
|
if (activeTicket === void 0) {
|
|
7933
7989
|
activeTicket = ticket;
|
|
7934
7990
|
handle = timer.set(tick, PANEL_AUTO_REFRESH_MS);
|
|
7935
|
-
|
|
7991
|
+
fetch();
|
|
7992
|
+
visibilityUnsubscribe = visibility.subscribe?.(() => {
|
|
7993
|
+
if (activeTicket !== ticket || !visibility.isVisible()) return;
|
|
7994
|
+
fetch();
|
|
7995
|
+
});
|
|
7936
7996
|
}
|
|
7937
7997
|
let stopped = false;
|
|
7938
7998
|
return () => {
|
|
@@ -7941,6 +8001,8 @@ window.__ModuleLoader__.load({
|
|
|
7941
8001
|
references -= 1;
|
|
7942
8002
|
if (references > 0) return;
|
|
7943
8003
|
activeTicket = void 0;
|
|
8004
|
+
visibilityUnsubscribe?.();
|
|
8005
|
+
visibilityUnsubscribe = void 0;
|
|
7944
8006
|
if (handle !== void 0) {
|
|
7945
8007
|
timer.clear(handle);
|
|
7946
8008
|
handle = void 0;
|
|
@@ -10463,13 +10525,14 @@ window.__ModuleLoader__.load({
|
|
|
10463
10525
|
.cc-usageStats{grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px;display:grid}
|
|
10464
10526
|
.cc-usageStatsActivity{grid-template-columns:repeat(4,minmax(0,1fr))}
|
|
10465
10527
|
@container (max-width:460px){.cc-usageStatsActivity{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
|
10466
|
-
.cc-balance{flex-
|
|
10467
|
-
|
|
10528
|
+
.cc-balance{flex-direction:column;gap:2px;display:flex}
|
|
10529
|
+
/* 两行左右分栏:标签行左账期、右更新时间;数值行左剩余、右已购与赠送。 */
|
|
10530
|
+
.cc-balanceRow{flex-wrap:wrap;justify-content:space-between;align-items:baseline;gap:4px 16px;display:flex}
|
|
10468
10531
|
.cc-balanceLabel{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
|
|
10469
10532
|
.cc-balanceValue{color:var(--dsw-alias-label-primary);font-size:20px;font-weight:600;line-height:28px;font-variant-numeric:tabular-nums}
|
|
10470
10533
|
.cc-balanceSub{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;font-variant-numeric:tabular-nums}
|
|
10471
10534
|
.cc-balanceEmpty .cc-balanceValue{color:var(--dsw-alias-state-error-primary)}
|
|
10472
|
-
.cc-balanceNote{
|
|
10535
|
+
.cc-balanceNote{margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}
|
|
10473
10536
|
.cc-usageStat{min-width:0;flex-direction:column;gap:2px;display:flex;padding:10px 12px;border-radius:8px;background:var(--dsw-alias-bg-module-platform)}
|
|
10474
10537
|
.cc-usageStatLabel{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
|
|
10475
10538
|
.cc-usageStatValue{color:var(--dsw-alias-label-primary);font-size:16px;font-weight:500;line-height:24px;font-variant-numeric:tabular-nums}
|
|
@@ -10484,9 +10547,6 @@ window.__ModuleLoader__.load({
|
|
|
10484
10547
|
.cc-usageBarFill{background:var(--dsw-alias-brand-primary);border-radius:999px;height:100%;transition:width .3s ease}
|
|
10485
10548
|
.cc-usageBarFillWarn{background:var(--dsw-alias-state-error-primary)}
|
|
10486
10549
|
.cc-usageWindowReset{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:18px}
|
|
10487
|
-
.cc-usageMeta{align-items:center;gap:8px;display:flex}
|
|
10488
|
-
.cc-usageMetaSpacer{flex:1}
|
|
10489
|
-
.cc-usageUpdated{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:18px}
|
|
10490
10550
|
.cc-usagePartial{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}
|
|
10491
10551
|
.cc-notices{flex-direction:column;gap:8px;display:flex;margin-bottom:12px}
|
|
10492
10552
|
.cc-section>.cc-notice{margin-top:12px}
|
|
@@ -11176,6 +11236,7 @@ window.__ModuleLoader__.load({
|
|
|
11176
11236
|
discard: () => controller.discard(),
|
|
11177
11237
|
refreshCredentials: () => void controller.refreshCredentials(),
|
|
11178
11238
|
refreshUsage: () => void usageController.refresh(),
|
|
11239
|
+
refreshUsageAuto: () => void usageController.refresh({ auto: true }),
|
|
11179
11240
|
...typeof ctx.get("layout")?.selectPanel === "function" ? { openAnalytics } : {},
|
|
11180
11241
|
beginLogin: (targetRef) => void loginController.begin(targetRef),
|
|
11181
11242
|
cancelLogin: () => void loginController.cancel(),
|
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.
|
|
64
|
+
declare const COMMAND_CODE_CLI_VERSION = "1.88.0";
|
|
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.
|
|
@@ -382,6 +382,25 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
|
|
|
382
382
|
private readonly surfaceOffload;
|
|
383
383
|
private readonly billingAccess;
|
|
384
384
|
private readonly billingAccessInflight;
|
|
385
|
+
/**
|
|
386
|
+
* Successful account-endpoint responses, keyed by gateway + key + exact URL.
|
|
387
|
+
*
|
|
388
|
+
* The settings card and the picker's advisory plan filter ask the SAME three
|
|
389
|
+
* endpoints about the SAME account (`/alpha/whoami`, then credits and
|
|
390
|
+
* subscriptions in parallel). Here that is four round trips per surface for
|
|
391
|
+
* two answers — and the card refreshes on a timer, so the picker keeps paying
|
|
392
|
+
* for rows it already has. Sharing the raw responses removes the duplicate
|
|
393
|
+
* without either surface having to know about the other: whoever asks second
|
|
394
|
+
* within {@link ACCOUNT_FACTS_TTL_MS} reads the same record instead of the
|
|
395
|
+
* wire.
|
|
396
|
+
*
|
|
397
|
+
* Only SUCCESSFUL records are kept. A failure must be retried rather than
|
|
398
|
+
* replayed, or an endpoint that recovered a second ago would keep reporting
|
|
399
|
+
* yesterday's outage — and the usage report's per-endpoint `failures` and
|
|
400
|
+
* {@link classifyTotalFailure} verdict would describe a request this process
|
|
401
|
+
* never made.
|
|
402
|
+
*/
|
|
403
|
+
private readonly accountJson;
|
|
385
404
|
private readonly protocolCache;
|
|
386
405
|
/** Namespace account-derived facts by gateway without exposing the key. */
|
|
387
406
|
private accountCacheKey;
|
|
@@ -488,8 +507,24 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
|
|
|
488
507
|
*/
|
|
489
508
|
allowanceTier(): Promise<'go' | 'goat' | 'pro' | undefined>;
|
|
490
509
|
resolveModel(provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
|
|
510
|
+
/**
|
|
511
|
+
* Resolve and validate the key one account-endpoint call will use.
|
|
512
|
+
*
|
|
513
|
+
* Every account endpoint runs the chat path's own `assertUsableApiKey` check,
|
|
514
|
+
* or a key carrying a character no HTTP header can hold throws inside `fetch`
|
|
515
|
+
* BEFORE any I/O — once per endpoint — and the report reads as "the network is
|
|
516
|
+
* down". The credential reference is read structurally: it belongs to the
|
|
517
|
+
* host's resolved options, not to this adapter's connection type, and it only
|
|
518
|
+
* names the key in the rejection message.
|
|
519
|
+
*
|
|
520
|
+
* Separate from {@link accountHeadersFor} so a caller that also needs to key a
|
|
521
|
+
* cache by the same identity does not have to resolve the key twice.
|
|
522
|
+
*/
|
|
523
|
+
private usableAccountKey;
|
|
491
524
|
/** The headers every authenticated account endpoint shares. */
|
|
492
525
|
private accountHeaders;
|
|
526
|
+
/** Headers for an already-validated key; see {@link usableAccountKey}. */
|
|
527
|
+
private accountHeadersFor;
|
|
493
528
|
/**
|
|
494
529
|
* Fetch one JSON GET and return its HTTP status with a parsed record. The
|
|
495
530
|
* catalog, billing probe, and usage report each apply their own failure
|
|
@@ -524,6 +559,23 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
|
|
|
524
559
|
* everything" (fail-open).
|
|
525
560
|
*/
|
|
526
561
|
private loadBillingAccessForKey;
|
|
562
|
+
/**
|
|
563
|
+
* One account-endpoint GET, served from {@link accountJson} when `share` and
|
|
564
|
+
* the same URL answered successfully within {@link ACCOUNT_FACTS_TTL_MS}.
|
|
565
|
+
*
|
|
566
|
+
* `timeoutMs` is the CALLER's budget; a cache hit spends none of it. Failures
|
|
567
|
+
* are never cached, so this can only ever save a request that would have
|
|
568
|
+
* re-read an answer this process already had.
|
|
569
|
+
*
|
|
570
|
+
* `share: false` marks a VOLATILE endpoint — one whose answer is the number
|
|
571
|
+
* the user is actually looking at, and which must therefore never be replayed
|
|
572
|
+
* from an earlier request. The usage card's summary is exactly that: its
|
|
573
|
+
* headline counts and cost are the reason the card exists, and stamping a
|
|
574
|
+
* cached copy with the current time would misreport when they were read.
|
|
575
|
+
* Identity and billing facts are the opposite: a plan tier or an org id that
|
|
576
|
+
* is 30 s old is still the truth, and those are what the picker duplicates.
|
|
577
|
+
*/
|
|
578
|
+
private accountJsonGet;
|
|
527
579
|
/**
|
|
528
580
|
* The billing facts behind the picker's plan filter, mirroring the CLI's
|
|
529
581
|
* `createBilling` flow: whoami yields the org id, then the subscriptions
|
|
@@ -1165,10 +1217,9 @@ declare const KNOWN_THINKING_MODELS: ReadonlySet<string>;
|
|
|
1165
1217
|
* `/docs/plans/max` and `/docs/resources/pricing-limits`). Each plan's model
|
|
1166
1218
|
* list is a superset of the one below it: Go ⊂ GOAT ⊂ Pro ⊂ Provider/Max.
|
|
1167
1219
|
* Models absent from every plan list (Claude Opus/Fable, Fugu Ultra) are
|
|
1168
|
-
* Provider-tier. Re-verified at command-code@1.
|
|
1169
|
-
* live `?view=client` catalog:
|
|
1170
|
-
* Large 4
|
|
1171
|
-
* (1.75.0) was the only addition at the 1.77.0 check; the 1.78.0/1.79.0 pair
|
|
1220
|
+
* Provider-tier. Re-verified at command-code@1.88.0 (2026-10-27) against the
|
|
1221
|
+
* live `?view=client` catalog: 106 ids, still a strict superset chain. Mistral
|
|
1222
|
+
* Large 4 (1.75.0) was the only addition at the 1.77.0 check; the 1.78.0/1.79.0 pair
|
|
1172
1223
|
* added Claude Haiku 5.5 at Pro and the free Glyph Cluster preview at Go.
|
|
1173
1224
|
* 1.79.2 lowers Haiku 5.5's minimum plan to GOAT; 1.80.0 adds Muse Wonder at
|
|
1174
1225
|
* Max; 1.82.0 adds Gemini 4 Argon at Pro and nothing else moves. The 1.82.4
|
|
@@ -1177,7 +1228,8 @@ declare const KNOWN_THINKING_MODELS: ReadonlySet<string>;
|
|
|
1177
1228
|
* which left the live catalog. Every other
|
|
1178
1229
|
* tier is unchanged. The 1.86.1 sync (2026-10-21) adds only the Ornith 1.5 122B
|
|
1179
1230
|
* launch pair at Go; the 1.87.1 check (2026-10-22) adds no model and moves no
|
|
1180
|
-
* tier.
|
|
1231
|
+
* tier, the 2026-10-24 check adds only Kimi K3.1 Pro at Go, and the 2026-10-27
|
|
1232
|
+
* check adds only GPT-6.1 Terra at Pro.
|
|
1181
1233
|
* The only removals since 1.49.0 are the two retired stealth previews (Pixel
|
|
1182
1234
|
* Canary at 1.73.1, Space Bunny Alpha at 1.74.3), Ling 3.0 Flash Sante
|
|
1183
1235
|
* (2026-10-16) and the retired Cat Jam preview (2026-10-20). Per-entry tags
|
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.
|
|
126
|
+
* bundle's model table (`dist/cli.mjs`, re-verified at command-code@1.88.0), 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
|
|
@@ -131,10 +131,18 @@ const PLAN_ORDER = Object.fromEntries(PLAN_TIER_ORDER.map((tier, index) => [tier
|
|
|
131
131
|
* 1.5 122B launch pair in the catalog, the ZDR exclusion-set prune and the
|
|
132
132
|
* MiniMax M3.1 Flash promo extension; the follow-up check later that day moved
|
|
133
133
|
* the pinned constant to 1.86.3 (request header only). The 2026-10-22 check
|
|
134
|
-
*
|
|
135
|
-
* and
|
|
136
|
-
*
|
|
137
|
-
*
|
|
134
|
+
* moved it to 1.87.1 (one output-limit row: `thinkingmachines/inkling-small`),
|
|
135
|
+
* and the 2026-10-23 check to 1.87.3 with every snapshot unchanged apart from
|
|
136
|
+
* the four retired catalog rows. The 2026-10-24 check moves it to 1.88.0 — a
|
|
137
|
+
* silent artifact-only release — and picks up the one row that did move in the
|
|
138
|
+
* catalog: Kimi K3.1 Pro (`moonshotai/Kimi-K3.1-Pro`, 1M context, text + image,
|
|
139
|
+
* efforts `low/high/max`, Go tier, `$2/$10/$0.2`). The 1.88.0 artifact does not
|
|
140
|
+
* name that row yet; the default catalog, `?view=client`, the models docs page
|
|
141
|
+
* and the pricing page all do, so the capability fields come from the catalog
|
|
142
|
+
* as usual. The 2026-10-27 check finds the CLI still at 1.88.0 and one more
|
|
143
|
+
* catalog-only addition, GPT-6.1 Terra (`gpt-6.1-terra`): 1.05M context, text +
|
|
144
|
+
* image, the same five-level effort set, Pro tier, `$1/$5` up to a 272K context
|
|
145
|
+
* step and `$2/$7.5` beyond. Keeping the snapshot in its own module
|
|
138
146
|
* confines those frequent sync diffs here: src/adapter.ts holds only the stable
|
|
139
147
|
* wire/runtime logic. The read helpers live here too, for the same reason.
|
|
140
148
|
*
|
|
@@ -339,6 +347,11 @@ const KNOWN_EFFORTS = {
|
|
|
339
347
|
"high",
|
|
340
348
|
"max"
|
|
341
349
|
],
|
|
350
|
+
"moonshotai/Kimi-K3.1-Pro": [
|
|
351
|
+
"low",
|
|
352
|
+
"high",
|
|
353
|
+
"max"
|
|
354
|
+
],
|
|
342
355
|
"google/gemini-3.8-flash": [
|
|
343
356
|
"low",
|
|
344
357
|
"medium",
|
|
@@ -489,6 +502,13 @@ const KNOWN_EFFORTS = {
|
|
|
489
502
|
"xhigh",
|
|
490
503
|
"max"
|
|
491
504
|
],
|
|
505
|
+
"gpt-6.1-terra": [
|
|
506
|
+
"low",
|
|
507
|
+
"medium",
|
|
508
|
+
"high",
|
|
509
|
+
"xhigh",
|
|
510
|
+
"max"
|
|
511
|
+
],
|
|
492
512
|
"inclusionai/ling-3.1-flash": [
|
|
493
513
|
"low",
|
|
494
514
|
"medium",
|
|
@@ -571,6 +591,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
571
591
|
"gpt-6-luna",
|
|
572
592
|
"gpt-6-sol",
|
|
573
593
|
"gpt-6.1-sol",
|
|
594
|
+
"gpt-6.1-terra",
|
|
574
595
|
"meta/muse-spark-1.1",
|
|
575
596
|
"meta/muse-spark-1.2",
|
|
576
597
|
"meta/muse-spark-1.2-contributor",
|
|
@@ -586,6 +607,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
586
607
|
"moonshotai/Kimi-K2.7-Code",
|
|
587
608
|
"moonshotai/Kimi-K2.7-Code-Highspeed",
|
|
588
609
|
"moonshotai/Kimi-K3",
|
|
610
|
+
"moonshotai/Kimi-K3.1-Pro",
|
|
589
611
|
"ornith/ornith-1.5-122b",
|
|
590
612
|
"sakana/fugu-ultra",
|
|
591
613
|
"stepfun/Step-3.7-Flash",
|
|
@@ -923,10 +945,9 @@ const MODEL_OUTPUT_TOKEN_LIMITS = /* @__PURE__ */ new Map([
|
|
|
923
945
|
* `/docs/plans/max` and `/docs/resources/pricing-limits`). Each plan's model
|
|
924
946
|
* list is a superset of the one below it: Go ⊂ GOAT ⊂ Pro ⊂ Provider/Max.
|
|
925
947
|
* Models absent from every plan list (Claude Opus/Fable, Fugu Ultra) are
|
|
926
|
-
* Provider-tier. Re-verified at command-code@1.
|
|
927
|
-
* live `?view=client` catalog:
|
|
928
|
-
* Large 4
|
|
929
|
-
* (1.75.0) was the only addition at the 1.77.0 check; the 1.78.0/1.79.0 pair
|
|
948
|
+
* Provider-tier. Re-verified at command-code@1.88.0 (2026-10-27) against the
|
|
949
|
+
* live `?view=client` catalog: 106 ids, still a strict superset chain. Mistral
|
|
950
|
+
* Large 4 (1.75.0) was the only addition at the 1.77.0 check; the 1.78.0/1.79.0 pair
|
|
930
951
|
* added Claude Haiku 5.5 at Pro and the free Glyph Cluster preview at Go.
|
|
931
952
|
* 1.79.2 lowers Haiku 5.5's minimum plan to GOAT; 1.80.0 adds Muse Wonder at
|
|
932
953
|
* Max; 1.82.0 adds Gemini 4 Argon at Pro and nothing else moves. The 1.82.4
|
|
@@ -935,7 +956,8 @@ const MODEL_OUTPUT_TOKEN_LIMITS = /* @__PURE__ */ new Map([
|
|
|
935
956
|
* which left the live catalog. Every other
|
|
936
957
|
* tier is unchanged. The 1.86.1 sync (2026-10-21) adds only the Ornith 1.5 122B
|
|
937
958
|
* launch pair at Go; the 1.87.1 check (2026-10-22) adds no model and moves no
|
|
938
|
-
* tier.
|
|
959
|
+
* tier, the 2026-10-24 check adds only Kimi K3.1 Pro at Go, and the 2026-10-27
|
|
960
|
+
* check adds only GPT-6.1 Terra at Pro.
|
|
939
961
|
* The only removals since 1.49.0 are the two retired stealth previews (Pixel
|
|
940
962
|
* Canary at 1.73.1, Space Bunny Alpha at 1.74.3), Ling 3.0 Flash Sante
|
|
941
963
|
* (2026-10-16) and the retired Cat Jam preview (2026-10-20). Per-entry tags
|
|
@@ -984,6 +1006,7 @@ const KNOWN_PLANS = {
|
|
|
984
1006
|
"moonshotai/Kimi-K2.7-Code": "go",
|
|
985
1007
|
"moonshotai/Kimi-K2.7-Code-Highspeed": "go",
|
|
986
1008
|
"moonshotai/Kimi-K3": "go",
|
|
1009
|
+
"moonshotai/Kimi-K3.1-Pro": "go",
|
|
987
1010
|
"nvidia/nemotron-3-ultra-550b-a55b": "go",
|
|
988
1011
|
"nvidia/nemotron-3.5-super-vl": "go",
|
|
989
1012
|
"ornith/ornith-1.5-122b": "go",
|
|
@@ -1031,6 +1054,7 @@ const KNOWN_PLANS = {
|
|
|
1031
1054
|
"gpt-5.5": "pro",
|
|
1032
1055
|
"gpt-5.6-terra": "pro",
|
|
1033
1056
|
"gpt-6-sol": "pro",
|
|
1057
|
+
"gpt-6.1-terra": "pro",
|
|
1034
1058
|
"meta/muse-spark-1.1": "pro",
|
|
1035
1059
|
"claude-fable-5-1": "provider",
|
|
1036
1060
|
"claude-fable-5": "provider",
|
|
@@ -2094,6 +2118,34 @@ const MODEL_PRICE_ROWS = [
|
|
|
2094
2118
|
5
|
|
2095
2119
|
] }]
|
|
2096
2120
|
},
|
|
2121
|
+
{
|
|
2122
|
+
id: "gpt-6.1-terra",
|
|
2123
|
+
rates: [
|
|
2124
|
+
1,
|
|
2125
|
+
5,
|
|
2126
|
+
.05,
|
|
2127
|
+
1.25
|
|
2128
|
+
],
|
|
2129
|
+
allowance: {
|
|
2130
|
+
go: 6,
|
|
2131
|
+
goat: 20,
|
|
2132
|
+
pro: 20
|
|
2133
|
+
},
|
|
2134
|
+
contextTiers: [{
|
|
2135
|
+
"maxContext": 272e3,
|
|
2136
|
+
"rates": [
|
|
2137
|
+
1,
|
|
2138
|
+
5,
|
|
2139
|
+
.05,
|
|
2140
|
+
1.25
|
|
2141
|
+
]
|
|
2142
|
+
}, { "rates": [
|
|
2143
|
+
2,
|
|
2144
|
+
7.5,
|
|
2145
|
+
.1,
|
|
2146
|
+
2.5
|
|
2147
|
+
] }]
|
|
2148
|
+
},
|
|
2097
2149
|
{
|
|
2098
2150
|
id: "grok-4.5",
|
|
2099
2151
|
rates: [
|
|
@@ -2248,6 +2300,19 @@ const MODEL_PRICE_ROWS = [
|
|
|
2248
2300
|
pro: 30
|
|
2249
2301
|
}
|
|
2250
2302
|
},
|
|
2303
|
+
{
|
|
2304
|
+
id: "kimi-k3.1-pro",
|
|
2305
|
+
rates: [
|
|
2306
|
+
2,
|
|
2307
|
+
10,
|
|
2308
|
+
.2
|
|
2309
|
+
],
|
|
2310
|
+
allowance: {
|
|
2311
|
+
go: 6,
|
|
2312
|
+
goat: 20,
|
|
2313
|
+
pro: 30
|
|
2314
|
+
}
|
|
2315
|
+
},
|
|
2251
2316
|
{
|
|
2252
2317
|
id: "ling-3.1-flash",
|
|
2253
2318
|
rates: [
|
|
@@ -6344,7 +6409,8 @@ var GatewayFacts = class {
|
|
|
6344
6409
|
if (this.state?.source !== identity) this.state = {
|
|
6345
6410
|
source: identity,
|
|
6346
6411
|
rows: [],
|
|
6347
|
-
expiresAt: 0
|
|
6412
|
+
expiresAt: 0,
|
|
6413
|
+
origin: "none"
|
|
6348
6414
|
};
|
|
6349
6415
|
return this.state;
|
|
6350
6416
|
}
|
|
@@ -6353,6 +6419,20 @@ var GatewayFacts = class {
|
|
|
6353
6419
|
const row = this.forSource(source).rows.find((candidate) => candidate.id === model);
|
|
6354
6420
|
return row === void 0 ? void 0 : copyRows([row])[0];
|
|
6355
6421
|
}
|
|
6422
|
+
/**
|
|
6423
|
+
* 本进程**联网成功过**的目录;否则返回 undefined。
|
|
6424
|
+
*
|
|
6425
|
+
* 判据是来源而不是「内存里有行」:`snapshot()` 会把磁盘缓存写进同一份
|
|
6426
|
+
* `state.rows`(生成路径需要它),若只看行数,冷启动先发过一次聊天再打开
|
|
6427
|
+
* 选择器时,首屏拿到的就是没有年龄校验的磁盘副本——正是本方法要排除的东西。
|
|
6428
|
+
* 空目录同样返回 undefined:刷新失败后的空行(`expiresAt` 已进入重试窗口)
|
|
6429
|
+
* 不能让选择器把「暂时读不到」画成「没有模型」,调用方应回落到 `refresh()`。
|
|
6430
|
+
*/
|
|
6431
|
+
loaded(source) {
|
|
6432
|
+
const state = this.forSource(source);
|
|
6433
|
+
if (state.origin !== "network" || state.rows.length === 0) return void 0;
|
|
6434
|
+
return copyRows(state.rows);
|
|
6435
|
+
}
|
|
6356
6436
|
/** 生成只读取本地目录;捕获状态后不再转向实例的新来源。 */
|
|
6357
6437
|
async snapshot(source) {
|
|
6358
6438
|
const state = this.forSource(source);
|
|
@@ -6360,7 +6440,10 @@ var GatewayFacts = class {
|
|
|
6360
6440
|
state.disk ??= readCache(source).catch(() => []);
|
|
6361
6441
|
const rows = await state.disk;
|
|
6362
6442
|
if (state.rows.length > 0 || state.expiresAt > 0) return copyRows(state.rows);
|
|
6363
|
-
if (state.inflight === void 0)
|
|
6443
|
+
if (state.inflight === void 0) {
|
|
6444
|
+
state.rows = rows;
|
|
6445
|
+
if (rows.length > 0 && state.origin === "none") state.origin = "disk";
|
|
6446
|
+
}
|
|
6364
6447
|
return copyRows(rows);
|
|
6365
6448
|
}
|
|
6366
6449
|
async refresh(source, signal) {
|
|
@@ -6373,8 +6456,10 @@ var GatewayFacts = class {
|
|
|
6373
6456
|
const mayPublish = () => this.state === state && generations.get(source.apiBase) === generation;
|
|
6374
6457
|
state.inflight = (async () => {
|
|
6375
6458
|
let rows, ttl = MODEL_CATALOG_TTL_MS;
|
|
6459
|
+
let network = false;
|
|
6376
6460
|
try {
|
|
6377
6461
|
rows = parseCatalog$1(await this.fetchCatalog(source), source.apiBase);
|
|
6462
|
+
network = true;
|
|
6378
6463
|
if (mayPublish()) {
|
|
6379
6464
|
publish(source.apiBase, rows);
|
|
6380
6465
|
await writeCache(source, rows, mayPublish).catch(() => void 0);
|
|
@@ -6386,6 +6471,8 @@ var GatewayFacts = class {
|
|
|
6386
6471
|
}
|
|
6387
6472
|
state.rows = rows;
|
|
6388
6473
|
state.expiresAt = Date.now() + ttl;
|
|
6474
|
+
if (network) state.origin = "network";
|
|
6475
|
+
else if (state.origin === "none" && rows.length > 0) state.origin = "disk";
|
|
6389
6476
|
return rows;
|
|
6390
6477
|
})().finally(() => {
|
|
6391
6478
|
delete state.inflight;
|
|
@@ -6422,7 +6509,7 @@ var GatewayFacts = class {
|
|
|
6422
6509
|
* per-request options thunk and an API-key resolver from the plugin entry
|
|
6423
6510
|
* (src/index.ts), so a settings change reaches the very next request.
|
|
6424
6511
|
*/
|
|
6425
|
-
const COMMAND_CODE_CLI_VERSION = "1.
|
|
6512
|
+
const COMMAND_CODE_CLI_VERSION = "1.88.0";
|
|
6426
6513
|
const DEFAULT_API_BASE = "https://api.commandcode.ai";
|
|
6427
6514
|
/**
|
|
6428
6515
|
* The output budget this bundle asks for, and the ceiling it will never exceed.
|
|
@@ -6436,6 +6523,17 @@ const MODELS_TIMEOUT_MS = 1e4;
|
|
|
6436
6523
|
/** How long the picker's plan-filter billing facts stay cached before refetching. */
|
|
6437
6524
|
const BILLING_ACCESS_TTL_MS = 3e5;
|
|
6438
6525
|
/**
|
|
6526
|
+
* How long the raw account facts captured by one usage-report run stay usable
|
|
6527
|
+
* for the picker's advisory plan filter.
|
|
6528
|
+
*
|
|
6529
|
+
* This is much shorter than {@link BILLING_ACCESS_TTL_MS} because it only ever
|
|
6530
|
+
* removes a DUPLICATE read: the picker and the settings card ask the same three
|
|
6531
|
+
* endpoints about the same key, and whichever ran last already paid for them.
|
|
6532
|
+
* Anything older than this window is simply fetched again — the filter is
|
|
6533
|
+
* fail-open advisory data, so a miss costs one round trip and nothing else.
|
|
6534
|
+
*/
|
|
6535
|
+
const ACCOUNT_FACTS_TTL_MS = 3e4;
|
|
6536
|
+
/**
|
|
6439
6537
|
* How long a learned CLI-only protocol preference stays cached per account.
|
|
6440
6538
|
* After this TTL a request may probe Provider API again, so an upgraded Go
|
|
6441
6539
|
* account can recover without a restart.
|
|
@@ -6527,9 +6625,13 @@ function fingerprintValue(value) {
|
|
|
6527
6625
|
}
|
|
6528
6626
|
return {
|
|
6529
6627
|
bytes: Buffer.byteLength(serialized),
|
|
6530
|
-
sha256:
|
|
6628
|
+
sha256: sha256Hex(serialized)
|
|
6531
6629
|
};
|
|
6532
6630
|
}
|
|
6631
|
+
/** Digest of already-serialized text: no second stringify, no retained copy. */
|
|
6632
|
+
function sha256Hex(serialized) {
|
|
6633
|
+
return createHash("sha256").update(serialized).digest("hex");
|
|
6634
|
+
}
|
|
6533
6635
|
/**
|
|
6534
6636
|
* Structural request facts for issue #64's cache diagnosis.
|
|
6535
6637
|
*
|
|
@@ -6645,6 +6747,16 @@ function headersTimeoutError(input) {
|
|
|
6645
6747
|
/** How deep a root `$ref` / combinator chain is followed while normalizing. */
|
|
6646
6748
|
const SCHEMA_NORMALIZE_MAX_DEPTH = 4;
|
|
6647
6749
|
/**
|
|
6750
|
+
* Depth-0 normalization results, keyed by the caller's own `parameters` object.
|
|
6751
|
+
*
|
|
6752
|
+
* {@link toolParametersSchema} only ever reads the root, so a schema that has
|
|
6753
|
+
* already been normalized returns the identical answer however many times it is
|
|
6754
|
+
* asked. The tool list is rebuilt for EVERY attempt — a rotation or a protocol
|
|
6755
|
+
* fallback rebuilds it again — and the objects come from the harness, so they
|
|
6756
|
+
* stay identical for the life of a turn: identity is an exact key.
|
|
6757
|
+
*/
|
|
6758
|
+
const TOOL_SCHEMA_CACHE = /* @__PURE__ */ new WeakMap();
|
|
6759
|
+
/**
|
|
6648
6760
|
* Whether a type-less node is object-shaped enough to be one with the type
|
|
6649
6761
|
* declared: `properties`/`required`/`additionalProperties` only make sense on
|
|
6650
6762
|
* an object, and a schema carrying them was meant to be one.
|
|
@@ -6690,7 +6802,7 @@ function mergeCombinatorBranches(node, depth) {
|
|
|
6690
6802
|
const objectBranches = [];
|
|
6691
6803
|
for (const branch of branches) {
|
|
6692
6804
|
if (!isRecord(branch)) continue;
|
|
6693
|
-
objectBranches.push(
|
|
6805
|
+
objectBranches.push(normalizeToolParameters(branch, depth + 1));
|
|
6694
6806
|
}
|
|
6695
6807
|
if (objectBranches.length === 0) continue;
|
|
6696
6808
|
sawBranch = true;
|
|
@@ -6720,10 +6832,32 @@ function mergeCombinatorBranches(node, depth) {
|
|
|
6720
6832
|
* resolved/merged; anything else (an array/scalar root, or no schema at all)
|
|
6721
6833
|
* degrades to a permissive free-form object, because a request the provider
|
|
6722
6834
|
* refuses helps no one and the tool's own description is still in the prompt.
|
|
6723
|
-
*
|
|
6724
|
-
*
|
|
6725
|
-
|
|
6726
|
-
function
|
|
6835
|
+
*
|
|
6836
|
+
* Only the ROOT is touched. The object-root fast path deliberately returns the
|
|
6837
|
+
* caller's own object: an already-normalized schema is the common case, the
|
|
6838
|
+
* function has always answered with that same identity, and callers rely on it
|
|
6839
|
+
* to get the same frozen schema they handed in. Everything this function
|
|
6840
|
+
* CONSTRUCTS is a fresh object, so no caller can mutate another's schema.
|
|
6841
|
+
*
|
|
6842
|
+
* The ROOT is memoized per `parameters` object, because every transport rebuilds
|
|
6843
|
+
* its tool list on every attempt — on a rotating or downshifting request the
|
|
6844
|
+
* same handful of tool schemas is walked four, five, six times for an answer
|
|
6845
|
+
* that cannot have changed. The tool objects come from the harness and are
|
|
6846
|
+
* stable for the life of a turn, so a WeakMap keyed on identity is exact: a
|
|
6847
|
+
* genuinely different schema is a different object and misses. Nested
|
|
6848
|
+
* `$ref`/combinator branches are reached through {@link normalizeToolParameters}
|
|
6849
|
+
* and are not memoized; only the root is asked for on a hot path.
|
|
6850
|
+
*/
|
|
6851
|
+
function toolParametersSchema(parameters) {
|
|
6852
|
+
if (!isRecord(parameters)) return normalizeToolParameters(parameters, 0);
|
|
6853
|
+
const cached = TOOL_SCHEMA_CACHE.get(parameters);
|
|
6854
|
+
if (cached !== void 0) return cached;
|
|
6855
|
+
const normalized = normalizeToolParameters(parameters, 0);
|
|
6856
|
+
TOOL_SCHEMA_CACHE.set(parameters, normalized);
|
|
6857
|
+
return normalized;
|
|
6858
|
+
}
|
|
6859
|
+
/** Depth-0 normalization of {@link toolParametersSchema}; see its memo there. */
|
|
6860
|
+
function normalizeToolParameters(parameters, depth) {
|
|
6727
6861
|
if (!isRecord(parameters)) return {
|
|
6728
6862
|
type: "object",
|
|
6729
6863
|
properties: {},
|
|
@@ -6739,7 +6873,7 @@ function toolParametersSchema(parameters, depth = 0) {
|
|
|
6739
6873
|
const target = resolveLocalRef(parameters, parameters.$ref);
|
|
6740
6874
|
if (target !== void 0) {
|
|
6741
6875
|
const { $ref: _ref, ...rest } = parameters;
|
|
6742
|
-
return
|
|
6876
|
+
return normalizeToolParameters({
|
|
6743
6877
|
...target,
|
|
6744
6878
|
...rest
|
|
6745
6879
|
}, depth + 1);
|
|
@@ -6975,26 +7109,47 @@ function coreBudget(budget) {
|
|
|
6975
7109
|
*/
|
|
6976
7110
|
const imageVersionBytes = (block) => block.attachment.bytes;
|
|
6977
7111
|
/**
|
|
7112
|
+
* The accounting half of the image gate: project the surface's durable offload
|
|
7113
|
+
* marks and count what the byte budget still needs — WITHOUT throwing.
|
|
7114
|
+
*
|
|
7115
|
+
* The throw has to wait until the credential is known. A request that cannot
|
|
7116
|
+
* authenticate must not trigger a durable (irreversible) image offload before
|
|
7117
|
+
* the caller learns the key is missing; keeping the count pure lets
|
|
7118
|
+
* `streamAttempt()` run it before ANY I/O and report ONE error later, once the
|
|
7119
|
+
* real transport is known.
|
|
7120
|
+
*/
|
|
7121
|
+
function projectSurfaceBudget(options, policy, budget) {
|
|
7122
|
+
const marked = policy.projectOffloadedImages(options.messages, (ref) => offloadedImageText(ref));
|
|
7123
|
+
const messages = marked === options.messages ? options.messages : [...marked];
|
|
7124
|
+
return {
|
|
7125
|
+
options: messages === options.messages ? options : {
|
|
7126
|
+
...options,
|
|
7127
|
+
messages
|
|
7128
|
+
},
|
|
7129
|
+
budgetMissing: policy.requiredImageOffload(messages, coreBudget(budget), imageVersionBytes)
|
|
7130
|
+
};
|
|
7131
|
+
}
|
|
7132
|
+
/**
|
|
6978
7133
|
* Request options under one budget rung: the surface's durable marks become
|
|
6979
7134
|
* placeholder text, and an over-budget history FAILS with the count to offload
|
|
6980
7135
|
* instead of being evicted here. Throws rather than returning, so no caller can
|
|
6981
7136
|
* accidentally send a body this route already knows is too large.
|
|
6982
7137
|
*
|
|
7138
|
+
* This is the one-shot form (budget + cache in a single error) used by the 413
|
|
7139
|
+
* downshift and the protocol fallback. `streamAttempt()`'s first pass uses
|
|
7140
|
+
* {@link projectSurfaceBudget} instead, because there the throw must wait for
|
|
7141
|
+
* the credential.
|
|
7142
|
+
*
|
|
6983
7143
|
* The next rung is applied to the CURRENT projection, never to the raw history —
|
|
6984
7144
|
* otherwise the 413 retry could reintroduce images the first rung had already
|
|
6985
7145
|
* evicted.
|
|
6986
7146
|
*/
|
|
6987
7147
|
function withSurfaceOffload(options, policy, budget, cacheOffloadModel) {
|
|
6988
|
-
const
|
|
6989
|
-
const
|
|
6990
|
-
const
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
if (missing > 0) throw imageOffloadRequired(missing, cacheMissing > budgetMissing ? "cache" : "budget");
|
|
6994
|
-
return messages === options.messages ? options : {
|
|
6995
|
-
...options,
|
|
6996
|
-
messages
|
|
6997
|
-
};
|
|
7148
|
+
const projected = projectSurfaceBudget(options, policy, budget);
|
|
7149
|
+
const cacheMissing = cacheOffloadModel === void 0 ? 0 : seenImageOffloadCount(projected.options.messages, cacheOffloadModel);
|
|
7150
|
+
const missing = Math.max(projected.budgetMissing, cacheMissing);
|
|
7151
|
+
if (missing > 0) throw imageOffloadRequired(missing, cacheMissing > projected.budgetMissing ? "cache" : "budget");
|
|
7152
|
+
return projected.options;
|
|
6998
7153
|
}
|
|
6999
7154
|
/**
|
|
7000
7155
|
* Count retained images already followed by an answer from this same model.
|
|
@@ -7952,6 +8107,25 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
7952
8107
|
surfaceOffload;
|
|
7953
8108
|
billingAccess = /* @__PURE__ */ new Map();
|
|
7954
8109
|
billingAccessInflight = /* @__PURE__ */ new Map();
|
|
8110
|
+
/**
|
|
8111
|
+
* Successful account-endpoint responses, keyed by gateway + key + exact URL.
|
|
8112
|
+
*
|
|
8113
|
+
* The settings card and the picker's advisory plan filter ask the SAME three
|
|
8114
|
+
* endpoints about the SAME account (`/alpha/whoami`, then credits and
|
|
8115
|
+
* subscriptions in parallel). Here that is four round trips per surface for
|
|
8116
|
+
* two answers — and the card refreshes on a timer, so the picker keeps paying
|
|
8117
|
+
* for rows it already has. Sharing the raw responses removes the duplicate
|
|
8118
|
+
* without either surface having to know about the other: whoever asks second
|
|
8119
|
+
* within {@link ACCOUNT_FACTS_TTL_MS} reads the same record instead of the
|
|
8120
|
+
* wire.
|
|
8121
|
+
*
|
|
8122
|
+
* Only SUCCESSFUL records are kept. A failure must be retried rather than
|
|
8123
|
+
* replayed, or an endpoint that recovered a second ago would keep reporting
|
|
8124
|
+
* yesterday's outage — and the usage report's per-endpoint `failures` and
|
|
8125
|
+
* {@link classifyTotalFailure} verdict would describe a request this process
|
|
8126
|
+
* never made.
|
|
8127
|
+
*/
|
|
8128
|
+
accountJson = /* @__PURE__ */ new Map();
|
|
7955
8129
|
protocolCache = /* @__PURE__ */ new Map();
|
|
7956
8130
|
/** Namespace account-derived facts by gateway without exposing the key. */
|
|
7957
8131
|
accountCacheKey(apiBase, apiKey) {
|
|
@@ -7991,10 +8165,10 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
7991
8165
|
* @returns the error to surface: unchanged for any other failure, the
|
|
7992
8166
|
* original timeout below the threshold, and a terminal one at it.
|
|
7993
8167
|
*/
|
|
7994
|
-
gradeHeaderTimeout(error, protocol, body, timeoutMs, sessionId) {
|
|
8168
|
+
gradeHeaderTimeout(error, protocol, body, timeoutMs, sessionId, sent) {
|
|
7995
8169
|
if (!(error instanceof LlmError && error.code === "TIMEOUT")) return error;
|
|
7996
8170
|
const key = sessionId ?? GLOBAL_HEADER_TIMEOUT_KEY;
|
|
7997
|
-
const fingerprint = `${protocol}:${fingerprintValue(body).sha256}`;
|
|
8171
|
+
const fingerprint = `${protocol}:${sent === void 0 ? fingerprintValue(body).sha256 : sha256Hex(sent)}`;
|
|
7998
8172
|
const previous = this.headerTimeoutStreaks.get(key);
|
|
7999
8173
|
const count = previous?.fingerprint === fingerprint ? previous.count + 1 : 1;
|
|
8000
8174
|
if (previous === void 0) {
|
|
@@ -8097,7 +8271,9 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8097
8271
|
}
|
|
8098
8272
|
async listModels(provider, opts) {
|
|
8099
8273
|
const connection = { ...this.deps.options() };
|
|
8100
|
-
const
|
|
8274
|
+
const cached = this.gatewayFacts.loaded(connection);
|
|
8275
|
+
const catalog = cached ?? await this.gatewayFacts.refresh(connection);
|
|
8276
|
+
if (cached !== void 0) this.gatewayFacts.refresh(connection).catch(() => void 0);
|
|
8101
8277
|
const toInfo = (model) => {
|
|
8102
8278
|
const vision = KNOWN_IMAGE_MODELS.has(model.id);
|
|
8103
8279
|
return {
|
|
@@ -8162,13 +8338,33 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8162
8338
|
})) } } : {}
|
|
8163
8339
|
};
|
|
8164
8340
|
}
|
|
8165
|
-
/**
|
|
8166
|
-
|
|
8341
|
+
/**
|
|
8342
|
+
* Resolve and validate the key one account-endpoint call will use.
|
|
8343
|
+
*
|
|
8344
|
+
* Every account endpoint runs the chat path's own `assertUsableApiKey` check,
|
|
8345
|
+
* or a key carrying a character no HTTP header can hold throws inside `fetch`
|
|
8346
|
+
* BEFORE any I/O — once per endpoint — and the report reads as "the network is
|
|
8347
|
+
* down". The credential reference is read structurally: it belongs to the
|
|
8348
|
+
* host's resolved options, not to this adapter's connection type, and it only
|
|
8349
|
+
* names the key in the rejection message.
|
|
8350
|
+
*
|
|
8351
|
+
* Separate from {@link accountHeadersFor} so a caller that also needs to key a
|
|
8352
|
+
* cache by the same identity does not have to resolve the key twice.
|
|
8353
|
+
*/
|
|
8354
|
+
async usableAccountKey(apiKey) {
|
|
8167
8355
|
const connection = this.deps.options();
|
|
8168
8356
|
const raw = apiKey ?? await this.deps.resolveApiKey(connection);
|
|
8169
8357
|
const named = connection.apiKeyEnv;
|
|
8358
|
+
return assertUsableApiKey(raw, "llm-commandcode", typeof named === "string" && named !== "" ? named : "the stored credential");
|
|
8359
|
+
}
|
|
8360
|
+
/** The headers every authenticated account endpoint shares. */
|
|
8361
|
+
async accountHeaders(apiKey) {
|
|
8362
|
+
return this.accountHeadersFor(await this.usableAccountKey(apiKey));
|
|
8363
|
+
}
|
|
8364
|
+
/** Headers for an already-validated key; see {@link usableAccountKey}. */
|
|
8365
|
+
accountHeadersFor(key) {
|
|
8170
8366
|
return {
|
|
8171
|
-
Authorization: `Bearer ${
|
|
8367
|
+
Authorization: `Bearer ${key}`,
|
|
8172
8368
|
...IDENTITY_ENCODING_HEADER,
|
|
8173
8369
|
"x-command-code-version": COMMAND_CODE_CLI_VERSION,
|
|
8174
8370
|
"x-cli-environment": "production",
|
|
@@ -8288,6 +8484,42 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8288
8484
|
return inflight;
|
|
8289
8485
|
}
|
|
8290
8486
|
/**
|
|
8487
|
+
* One account-endpoint GET, served from {@link accountJson} when `share` and
|
|
8488
|
+
* the same URL answered successfully within {@link ACCOUNT_FACTS_TTL_MS}.
|
|
8489
|
+
*
|
|
8490
|
+
* `timeoutMs` is the CALLER's budget; a cache hit spends none of it. Failures
|
|
8491
|
+
* are never cached, so this can only ever save a request that would have
|
|
8492
|
+
* re-read an answer this process already had.
|
|
8493
|
+
*
|
|
8494
|
+
* `share: false` marks a VOLATILE endpoint — one whose answer is the number
|
|
8495
|
+
* the user is actually looking at, and which must therefore never be replayed
|
|
8496
|
+
* from an earlier request. The usage card's summary is exactly that: its
|
|
8497
|
+
* headline counts and cost are the reason the card exists, and stamping a
|
|
8498
|
+
* cached copy with the current time would misreport when they were read.
|
|
8499
|
+
* Identity and billing facts are the opposite: a plan tier or an org id that
|
|
8500
|
+
* is 30 s old is still the truth, and those are what the picker duplicates.
|
|
8501
|
+
*/
|
|
8502
|
+
async accountJsonGet(apiBase, apiKey, path, timeoutMs, share = true) {
|
|
8503
|
+
const cacheKey = JSON.stringify([
|
|
8504
|
+
apiBase,
|
|
8505
|
+
apiKey,
|
|
8506
|
+
path
|
|
8507
|
+
]);
|
|
8508
|
+
if (share) {
|
|
8509
|
+
const hit = this.accountJson.get(cacheKey);
|
|
8510
|
+
if (hit !== void 0 && Date.now() - hit.at < ACCOUNT_FACTS_TTL_MS) return {
|
|
8511
|
+
status: 200,
|
|
8512
|
+
record: hit.record
|
|
8513
|
+
};
|
|
8514
|
+
}
|
|
8515
|
+
const answer = await this.fetchJson(`${apiBase}${path}`, this.accountHeadersFor(apiKey), timeoutMs);
|
|
8516
|
+
if (share && answer.record !== void 0) this.accountJson.set(cacheKey, {
|
|
8517
|
+
record: answer.record,
|
|
8518
|
+
at: Date.now()
|
|
8519
|
+
});
|
|
8520
|
+
return answer;
|
|
8521
|
+
}
|
|
8522
|
+
/**
|
|
8291
8523
|
* The billing facts behind the picker's plan filter, mirroring the CLI's
|
|
8292
8524
|
* `createBilling` flow: whoami yields the org id, then the subscriptions
|
|
8293
8525
|
* and credits endpoints answer in parallel. The plan id is honored only
|
|
@@ -8298,8 +8530,8 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8298
8530
|
*/
|
|
8299
8531
|
async fetchBillingAccess(apiBase, apiKey) {
|
|
8300
8532
|
try {
|
|
8301
|
-
const
|
|
8302
|
-
const getJson = async (path) => (await this.
|
|
8533
|
+
const key = await this.usableAccountKey(apiKey);
|
|
8534
|
+
const getJson = async (path) => (await this.accountJsonGet(apiBase, key, path, MODELS_TIMEOUT_MS)).record;
|
|
8303
8535
|
const whoami = await getJson("/alpha/whoami");
|
|
8304
8536
|
const orgData = whoami && isRecord(whoami.org) ? whoami.org : void 0;
|
|
8305
8537
|
const orgId = orgData === void 0 ? void 0 : stringValue(orgData.id);
|
|
@@ -8406,9 +8638,9 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8406
8638
|
async getUsage(apiKey, capturedConnection) {
|
|
8407
8639
|
const connection = capturedConnection ?? { ...this.deps.options() };
|
|
8408
8640
|
const base = connection.apiBase;
|
|
8409
|
-
let
|
|
8641
|
+
let key;
|
|
8410
8642
|
try {
|
|
8411
|
-
|
|
8643
|
+
key = await this.usableAccountKey(apiKey);
|
|
8412
8644
|
} catch (error) {
|
|
8413
8645
|
if (error instanceof LlmError && error.code === "INVALID_CREDENTIAL") return {
|
|
8414
8646
|
failures: [error.message],
|
|
@@ -8418,9 +8650,9 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8418
8650
|
}
|
|
8419
8651
|
const failures = [];
|
|
8420
8652
|
const failed = [];
|
|
8421
|
-
const getJson = async (path) => {
|
|
8653
|
+
const getJson = async (path, share = true) => {
|
|
8422
8654
|
try {
|
|
8423
|
-
const { status, record, responseFailure } = await this.
|
|
8655
|
+
const { status, record, responseFailure } = await this.accountJsonGet(base, key, path, connection.requestTimeoutMs, share);
|
|
8424
8656
|
if (record === void 0) {
|
|
8425
8657
|
failures.push(`${path}: HTTP ${status}${responseFailure ? `;${responseFailure}` : ""}`);
|
|
8426
8658
|
failed.push(responseFailure ? {
|
|
@@ -8454,7 +8686,7 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8454
8686
|
const rawStart = subData?.currentPeriodStart;
|
|
8455
8687
|
const periodStart = periodTimestampValue(rawStart);
|
|
8456
8688
|
const validStart = periodStart > 0 && Number.isFinite(new Date(periodStart).getTime());
|
|
8457
|
-
const totals = parseUsageTotals(await getJson(scopedPath("/alpha/usage/summary", validStart ? String(rawStart) : void 0)));
|
|
8689
|
+
const totals = parseUsageTotals(await getJson(scopedPath("/alpha/usage/summary", validStart ? String(rawStart) : void 0), false));
|
|
8458
8690
|
if (totals !== void 0) report.usage = totals;
|
|
8459
8691
|
const limits = parseCreditLimits(credits);
|
|
8460
8692
|
if (limits !== void 0) report.credits = limits;
|
|
@@ -8609,23 +8841,15 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8609
8841
|
if (attachments === void 0) throw new LlmError("Command Code image input requires the durable attachment service", "UNSUPPORTED_CONTENT");
|
|
8610
8842
|
readImage = requestImages(attachments, options.signal, timing);
|
|
8611
8843
|
}
|
|
8612
|
-
const finishCredentials = timing.phase("credentials");
|
|
8613
|
-
let apiKey;
|
|
8614
|
-
try {
|
|
8615
|
-
apiKey = await this.deps.resolveApiKey(connection, options.model);
|
|
8616
|
-
} finally {
|
|
8617
|
-
finishCredentials();
|
|
8618
|
-
}
|
|
8619
|
-
options.signal?.throwIfAborted();
|
|
8620
8844
|
let maxTokens = Math.min(budget.max, learnedOutputLimit(connection.apiBase, options.model));
|
|
8845
|
+
const builtMaxTokens = maxTokens;
|
|
8621
8846
|
const supported = KNOWN_EFFORTS[options.model];
|
|
8622
8847
|
const effort = options.purpose === "session-title" && supported?.includes("off") ? "off" : options.reasoningEffort;
|
|
8623
8848
|
const declared = effort !== void 0 && supported?.includes(effort) ? effort : void 0;
|
|
8624
8849
|
const reasoningEffort = effort === "off" && declared === void 0 ? void 0 : declared;
|
|
8625
8850
|
const systemText = [options.system ?? "", ...options.messages.filter((m) => m.role === "system").map((m) => m.content.map(blockText).filter(Boolean).join("\n"))].filter(Boolean).join("\n\n");
|
|
8626
|
-
|
|
8627
|
-
let
|
|
8628
|
-
let requestOptions = withSurfaceOffload(options, this.surfaceOffload, REQUEST_IMAGE_BUDGETS[0], connection.offloadSeenImagesForCache === true && protocol === "cli" ? options.model : void 0);
|
|
8851
|
+
const projected = projectSurfaceBudget(options, this.surfaceOffload, REQUEST_IMAGE_BUDGETS[0]);
|
|
8852
|
+
let requestOptions = projected.options;
|
|
8629
8853
|
const buildBody = async (target) => {
|
|
8630
8854
|
const finishBody = timing.phase("body");
|
|
8631
8855
|
try {
|
|
@@ -8643,7 +8867,30 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8643
8867
|
finishBody();
|
|
8644
8868
|
}
|
|
8645
8869
|
};
|
|
8646
|
-
|
|
8870
|
+
const provisionalProtocol = connection.protocol === "cli" ? "cli" : this.routesToMessages(options.model, modelEntry) ? "messages" : "openai";
|
|
8871
|
+
const finishCredentials = timing.phase("credentials");
|
|
8872
|
+
const credentials = this.deps.resolveApiKey(connection, options.model);
|
|
8873
|
+
credentials.catch(() => void 0);
|
|
8874
|
+
const provisionalBody = connection.offloadSeenImagesForCache === true || projected.budgetMissing > 0 ? void 0 : buildBody(provisionalProtocol);
|
|
8875
|
+
provisionalBody?.catch(() => void 0);
|
|
8876
|
+
let apiKey;
|
|
8877
|
+
try {
|
|
8878
|
+
apiKey = await credentials;
|
|
8879
|
+
} finally {
|
|
8880
|
+
finishCredentials();
|
|
8881
|
+
}
|
|
8882
|
+
options.signal?.throwIfAborted();
|
|
8883
|
+
maxTokens = Math.min(budget.max, learnedOutputLimit(connection.apiBase, options.model));
|
|
8884
|
+
let protocol = this.resolveProtocol(connection, apiKey, options.model, modelEntry);
|
|
8885
|
+
const cacheMissing = protocol === "cli" && connection.offloadSeenImagesForCache === true ? seenImageOffloadCount(requestOptions.messages, options.model) : 0;
|
|
8886
|
+
const missing = Math.max(projected.budgetMissing, cacheMissing);
|
|
8887
|
+
if (missing > 0) throw imageOffloadRequired(missing, cacheMissing > projected.budgetMissing ? "cache" : "budget");
|
|
8888
|
+
let messagesPermissionRejection;
|
|
8889
|
+
let body = provisionalBody !== void 0 && protocol === provisionalProtocol ? await provisionalBody : await buildBody(protocol);
|
|
8890
|
+
if (maxTokens !== builtMaxTokens) {
|
|
8891
|
+
budget.max = Math.min(budget.max, maxTokens);
|
|
8892
|
+
if (!omitOutputTokens(connection.maxOutputTokens, protocol, options.maxTokens)) assignMaxTokens(body, protocol, maxTokens);
|
|
8893
|
+
}
|
|
8647
8894
|
const serializedBodies = /* @__PURE__ */ new WeakMap();
|
|
8648
8895
|
const trace = openStreamTrace(`${options.provider}/${options.model}`);
|
|
8649
8896
|
const tried = /* @__PURE__ */ new Set();
|
|
@@ -8765,7 +9012,7 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8765
9012
|
});
|
|
8766
9013
|
trace.close();
|
|
8767
9014
|
if (options.signal?.aborted) throw error;
|
|
8768
|
-
throw failureWithRequestId(this.gradeHeaderTimeout(error, protocol, body, connection.requestTimeoutMs, options.sessionId), responseMetadata);
|
|
9015
|
+
throw failureWithRequestId(this.gradeHeaderTimeout(error, protocol, body, connection.requestTimeoutMs, options.sessionId, serializedBodies.get(body)), responseMetadata);
|
|
8769
9016
|
}
|
|
8770
9017
|
if (connected === void 0) throw new LlmError("Command Code API connection failed without a response", "TRANSPORT");
|
|
8771
9018
|
const { response, cleanup } = connected;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mars-sea/dsh-commandcode-provider",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.5",
|
|
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",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"compatibility": {
|
|
25
25
|
"dshReleases": {
|
|
26
26
|
"0.2.1-rc.2": "compatible",
|
|
27
|
-
"0.2.1-rc.3": "compatible"
|
|
27
|
+
"0.2.1-rc.3": "compatible",
|
|
28
|
+
"0.2.1-rc.4": "compatible"
|
|
28
29
|
},
|
|
29
30
|
"dsh": ">=0.2.1-rc.2 <0.2.2-0"
|
|
30
31
|
},
|
|
@@ -141,10 +142,15 @@
|
|
|
141
142
|
"@deepseek-ai/schemastery": "~3.18.5",
|
|
142
143
|
"@types/node": "^22.0.0",
|
|
143
144
|
"@types/react": "^18.3.1",
|
|
145
|
+
"@types/react-test-renderer": "^19.3.0",
|
|
144
146
|
"parse5": "8.0.1",
|
|
145
147
|
"react": "^18.2.0",
|
|
148
|
+
"react-test-renderer": "^18.3.1",
|
|
146
149
|
"tsdown": "^0.22.2",
|
|
147
150
|
"tsx": "^4.23.12",
|
|
148
151
|
"typescript": "^6.0.3"
|
|
152
|
+
},
|
|
153
|
+
"overrides": {
|
|
154
|
+
"zod": "^4.6.6"
|
|
149
155
|
}
|
|
150
156
|
}
|