dsh-cost-meter 1.8.10 → 1.8.12
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/README.md +10 -6
- package/README.zh-CN.md +10 -6
- package/docs/qwen-cli-quota.md +91 -0
- package/docs/session-sidebar-recovery.md +93 -0
- package/lib/bailian-cli.js +59 -3
- package/lib/cli-bridge.js +7 -3
- package/lib/client.js +5 -5
- package/lib/index.js +76 -4
- package/lib/typert.host.js +13 -0
- package/package.json +150 -148
package/README.md
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
Per-conversation cost · daily totals · OpenCode Go subscription quota display · budget with usage percentage · official account balance · custom provider balance · balance progress bar · history · peak/off-peak pricing hours display (peak hours UTC 01:00–04:00, 06:00–10:00; weekends and Chinese public holidays are off-peak all day, with separate labels) · pre-switch popup & system-notification alerts for peak/off-peak changes (position / lead time / alert type configurable) · one-click price sync from the official docs · Codex-style token usage heat grid · multi-vendor model pricing (built-in 170+ model-ID catalog with auto-matching) · mainstream Coding Plan quota queries & display (Anthropic / Z.ai / MiniMax / Kimi / OpenRouter / SiliconFlow / CommandCode / SCNet / Volcano Ark / Qwen / Xiaomi MiMo) plan/API dual-track billing (subscription quota vs pay-as-you-go money separated, per-1% & full-window token/equivalent-cost estimates with daily/weekly/monthly curves) · · quota strip above the input box (budget / Go / coding-plan usage in one row, toggleable)
|
|
10
10
|
|
|
11
|
-
[](https://github.com/Han-1413141/dsh-cost-meter)
|
|
12
12
|
|
|
13
|
-
**v1.8.
|
|
13
|
+
**v1.8.12** adds a Bailian CLI console-login button with verified credentials, automatic quota refresh and concurrent-login cancellation. See the [release notes](docs/release-notes/v1.8.12.md).
|
|
14
14
|
|
|
15
15
|
Desktop users: follow the [Desktop installation instructions](docs/install-troubleshooting.md#desktop-安装与更新) for the application's own CLI and `desktop` Profile.
|
|
16
16
|
|
|
@@ -320,22 +320,22 @@ On Node.js 20, use `npm install -g pnpm@10` instead. See [pnpm installation and
|
|
|
320
320
|
dsh plugin --profile web add dsh-cost-meter
|
|
321
321
|
```
|
|
322
322
|
|
|
323
|
-
**PowerShell one-click script** (copy the whole line, paste, press Enter; pnpm is provisioned automatically, git is auto-detected — no clone needed; the install chain is **pinned to the release tag `v1.8.
|
|
323
|
+
**PowerShell one-click script** (copy the whole line, paste, press Enter; pnpm is provisioned automatically, git is auto-detected — no clone needed; the install chain is **pinned to the release tag `v1.8.12`** — review the script before running):
|
|
324
324
|
|
|
325
325
|
```powershell
|
|
326
|
-
irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.8.
|
|
326
|
+
irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.8.12/install.ps1 | iex
|
|
327
327
|
```
|
|
328
328
|
|
|
329
329
|
**Or a plain command line** (the machine must already have pnpm and git; also pinned to the tag):
|
|
330
330
|
|
|
331
331
|
```sh
|
|
332
|
-
dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.8.
|
|
332
|
+
dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.8.12
|
|
333
333
|
```
|
|
334
334
|
|
|
335
335
|
Without git, use the GitHub tag archive:
|
|
336
336
|
|
|
337
337
|
```sh
|
|
338
|
-
dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.8.
|
|
338
|
+
dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.8.12.tar.gz
|
|
339
339
|
```
|
|
340
340
|
|
|
341
341
|
After installing, **restart** `dsh web` (plugin rows, the Typert manifest and the client bundle are all scanned at startup):
|
|
@@ -493,6 +493,10 @@ dsh --profile web --port 3099 # real startup (watch lo
|
|
|
493
493
|
|
|
494
494
|
## Known limitations
|
|
495
495
|
|
|
496
|
+
For loading placeholders followed by “No matching sessions” after opening Cost,
|
|
497
|
+
see [non-destructive sidebar search recovery](docs/session-sidebar-recovery.md).
|
|
498
|
+
Check and clear the host search before changing stored data.
|
|
499
|
+
|
|
496
500
|
- Official-page parsing depends on the current page structure; after a redesign, “Sync prices from official docs” fails — edit the price table manually as a fallback;
|
|
497
501
|
- The session badge's fallback estimate (used when ledger data is unavailable) prices all of a session's calls at the tier of the *current* moment, including plan-type sessions: a session spanning peak and off-peak hours gets its off-peak portion overestimated during peak hours; exact figures come from the ledger (which bills each call at its own initiation moment);
|
|
498
502
|
- Price sync overwrites the same-named models listed on the official page; custom model entries are unaffected;
|
package/README.zh-CN.md
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
本会话费用 · 当日费用 · OpenCode Go 订阅额度显示 · 预算与已用百分比 · 官方账户余额 · 自定义 Provider 余额查询(可配任意 HTTP 端点) · 余额三段进度条 · 历史记录 · 峰谷计价时段显示(UTC 01:00–04:00、06:00–10:00 为峰时段;周末与中国法定假日全天按谷价,分别标注) · 峰/谷切换前弹窗与系统通知提醒(位置/提前量/提醒类型可配) · 官方价格一键同步 · 类 Codex Token 用量热图 · 多厂商多模型价格计费(内置 170+ 模型 ID 价格目录与自动匹配) · 主流 Coding Plan 额度查询与显示(Anthropic / Z.ai / MiniMax / Kimi / OpenRouter / SiliconFlow / CommandCode / SCNet / 火山方舟 / 千问 / 小米 MiMo 十一家,含 Volcano Ark AK/SK 签名与 MiMo 控制台 Cookie 查询) · Plan/API 双轨计费(订阅额度与按量金额分离统计,每 1% 额度与满窗的 token/等值金额估算及日/周/月曲线) · 输入框上方额度横条(预算/Go/Coding Plan 用量一条横排显示,可开关)
|
|
10
10
|
|
|
11
|
-
[](https://github.com/Han-1413141/dsh-cost-meter)
|
|
12
12
|
|
|
13
|
-
**v1.8.
|
|
13
|
+
**v1.8.12** 增加百炼 CLI 控制台登录按钮,登录后核验凭据并刷新额度,支持并发合并与切源取消。详见[更新说明](docs/release-notes/v1.8.12.md)。
|
|
14
14
|
|
|
15
15
|
桌面端用户请按 [Desktop 安装说明](docs/install-troubleshooting.md#desktop-安装与更新),使用应用自带的 CLI 和 `desktop` Profile。
|
|
16
16
|
|
|
@@ -320,22 +320,22 @@ Node.js 20 请改用 `npm install -g pnpm@10`。版本要求见 [pnpm 官方安
|
|
|
320
320
|
dsh plugin --profile web add dsh-cost-meter
|
|
321
321
|
```
|
|
322
322
|
|
|
323
|
-
**PowerShell 一键脚本**(复制整行粘贴回车;自动补齐 pnpm、自动探测 git,无需克隆仓库;安装链**固定到发布 tag `v1.8.
|
|
323
|
+
**PowerShell 一键脚本**(复制整行粘贴回车;自动补齐 pnpm、自动探测 git,无需克隆仓库;安装链**固定到发布 tag `v1.8.12`**,建议先下载审阅再运行):
|
|
324
324
|
|
|
325
325
|
```powershell
|
|
326
|
-
irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.8.
|
|
326
|
+
irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.8.12/install.ps1 | iex
|
|
327
327
|
```
|
|
328
328
|
|
|
329
329
|
**或直接命令行**(机器上需已有 pnpm 与 git;同样固定到 tag):
|
|
330
330
|
|
|
331
331
|
```sh
|
|
332
|
-
dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.8.
|
|
332
|
+
dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.8.12
|
|
333
333
|
```
|
|
334
334
|
|
|
335
335
|
没有 git 时可用 GitHub tag 打包直链:
|
|
336
336
|
|
|
337
337
|
```sh
|
|
338
|
-
dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.8.
|
|
338
|
+
dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.8.12.tar.gz
|
|
339
339
|
```
|
|
340
340
|
|
|
341
341
|
安装后**重启** `dsh web`(插件行、Typert 清单与客户端 bundle 均在启动时扫描):
|
|
@@ -496,6 +496,10 @@ dsh --profile web --port 3099 # 真机启动(观察启
|
|
|
496
496
|
|
|
497
497
|
## 已知限制
|
|
498
498
|
|
|
499
|
+
打开费用设置后若侧栏出现加载占位条与「No matching sessions」,请先查看
|
|
500
|
+
[无损侧栏搜索恢复说明](docs/session-sidebar-recovery.md),检查宿主搜索内容,
|
|
501
|
+
不要删除会话、账本或浏览器存储。
|
|
502
|
+
|
|
499
503
|
- 历史按模型回填依赖宿主会话日志仍在盘:日志已被清理的早期调用无法逐模型重建,只能以「未分模型」残差行计入当日合计;
|
|
500
504
|
- 官方页面解析依赖当前页面结构;改版后「从官方文档同步价格」会报错,可手动编辑价格表兜底;
|
|
501
505
|
- 会话徽章在账本数据不可用时的回退估算按「当前时刻」的价格档位给该会话全部调用定价(含 Plan 类会话):跨峰谷时段的会话在峰时会高估其谷时部分,精确费用以账本为准(账本按每次调用的发起时刻逐笔计价);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# 千问 CLI 订阅额度(#146)
|
|
2
|
+
|
|
3
|
+
在「设置 → 费用 → 额度 → 千问 Qwen Token Plan」展开卡片,启用后将「额度来源」切换为「官方 CLI」或「百炼 CLI」。默认仍为「本地估算」,已有配置无需迁移。
|
|
4
|
+
|
|
5
|
+
## 准备与使用
|
|
6
|
+
|
|
7
|
+
在**运行 DSH 的机器**上,以同一系统账号执行:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install -g @qianwenai/qianwen-cli
|
|
11
|
+
qianwen auth login
|
|
12
|
+
qianwen usage summary --format json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
安装后重启 DSH,使宿主读取新的 `PATH`。远程 DSH、容器或服务账号需要在对应环境安装和登录;只在本地浏览器所在机器登录不生效。插件使用 CLI 自己保存的登录态,不要求复制模型 API Key、Cookie 或管理令牌。
|
|
16
|
+
|
|
17
|
+
选择 CLI 后可设置查询间隔(1–1440 分钟,默认 15),点击卡片刷新可立即重查。同一来源的并发刷新合并为一次查询;普通轮询复用缓存,失败也遵守查询间隔。关闭千问、关闭显示或切回本地估算后,不再启动 CLI;切换期间的旧结果不会覆盖新来源。
|
|
18
|
+
|
|
19
|
+
## 显示口径
|
|
20
|
+
|
|
21
|
+
- 只读取 `usage summary --format json` 中的 `token_plan`:总 Credits、剩余 Credits、已用比例,以及可选加量包剩余量。已用量由总量减剩余量计算,小数 Credits 保留,标记 `(CLI)`。
|
|
22
|
+
- CLI 的 `resetDate` 在核验版本中来自订阅实例 `EndTime`,因此显示为「CLI 订阅到期」,不作为月度重置倒计时或采样边界。
|
|
23
|
+
- 官方快照包含该账号在其他工具中的用量;插件不会把这些总量写进 DSH 账本。CLI 没有可靠的当前额度周期边界,因此此模式不生成千问「每 1% / 满窗 Token」估算。
|
|
24
|
+
- 未登录、超时、异常 JSON 或无有效订阅额度时显示提示并清空本次额度。CLI 会把部分上游失败转换为 `subscribed: false`,插件不能据此断言账号没有订阅。可检查终端输出后刷新,或手动切回本地估算;不会自动用本地数值冒充官方数值。
|
|
25
|
+
- 本地估算的月度额度、起始日和抵扣率保留,切回后继续使用。`usage summary` 未提供可用主订阅额度、只有加量包时,当前显示无有效订阅额度提示。
|
|
26
|
+
|
|
27
|
+
## 执行与验证
|
|
28
|
+
|
|
29
|
+
插件执行固定命令,不经过 shell,不接受自定义命令参数。Windows 的 npm 安装通过包内 Node 入口运行,避免执行 `.cmd` 包装脚本。只搜索绝对 `PATH` 目录;单次执行最长 15 秒,stdout/stderr 各不超过 1 MiB,stdin 关闭,窗口隐藏。错误不会包含 CLI 原始输出,登录凭据仍由 CLI 管理。
|
|
30
|
+
|
|
31
|
+
回归使用合成输出与真实子进程,覆盖小数和零额度、缺失字段、异常输出、登录失败、超时、并发、切源、关闭、持久化与 RPC codec;另有双语设置组件回归。未使用真实千问订阅账号验证额度。
|
|
32
|
+
## 百炼 CLI(第三档额度来源)
|
|
33
|
+
|
|
34
|
+
「百炼 CLI」档使用阿里云官方 [modelstudioai/cli](https://github.com/modelstudioai/cli)(npm 包 `bailian-cli`,命令 `bl` / `bailian`)查询百炼账号的 Token Plan 与 Coding Plan 订阅额度。在**运行 DSH 的机器**上以同一系统账号执行:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
npm install -g bailian-cli
|
|
38
|
+
bl auth login --console
|
|
39
|
+
bl usage token-plan --output json
|
|
40
|
+
bl usage coding-plan --output json
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
安装后重启 DSH 使宿主读取新的 `PATH`。这些额度子命令使用 CLI 的控制台登录;插件不复制凭据。
|
|
44
|
+
|
|
45
|
+
### 从卡片登录(#235)
|
|
46
|
+
|
|
47
|
+
额度查询失败后,插件会用 `bl auth status --output json --quiet` 检查控制台凭据。明确缺少控制台凭据或 CLI 返回认证失败(退出码 3)时,卡片按钮显示“登录”;仅有模型 API Key 或 AK/SK 不满足这两个额度命令的认证要求。检测本身失败时保留查询错误,不据此要求重新登录。
|
|
48
|
+
|
|
49
|
+
点击“登录”才会执行固定的 `bl auth login --console`,并在 **DSH 主机**打开授权浏览器。完成后重新检查控制台凭据,再刷新额度。登录成功但未返回有效订阅额度时,界面分别说明这两个结果。远程、容器或无界面主机请在对应环境以同一系统账号手动登录;按钮不能在访问 Web 页面的另一台机器上配置主机凭据。
|
|
50
|
+
|
|
51
|
+
登录最长等待 5 分钟,之后可重试。凭据仍由官方 CLI 保存;插件丢弃登录输出,错误只显示双语分类提示。并发登录合并为一次;切换来源、关闭显示或卸载插件时取消在途登录,旧结果不覆盖新配置。回归使用合成凭据与真实子进程,未执行真实账号浏览器授权。
|
|
52
|
+
|
|
53
|
+
- 一次查询并行执行 `usage token-plan` 与 `usage coding-plan` 两个只读子命令并合并:Token Plan 优先提供 5 小时 / 周窗口,Coding Plan 提供 5 小时 / 周 / 账单月三窗(`per5Hour` / `perWeek` / `perBillMonth`),另附「source」行标注实际应答的订阅与实例(如 `Coding Plan (pro) (CLI)`)。
|
|
54
|
+
- Token Plan 读取 `per5HourPercentage` / `per1WeekPercentage` 及对应的 `ResetTime`;Coding Plan 读取各窗口的 `percentage`(缺失时按 `usedQuota/totalQuota` 推算)。官方 CLI 返回比例值,乘以 100 后保留一位小数,例如 `0.5` 显示为 `50%`。重置时间以 epoch 毫秒转为 ISO。缺失的 Token Plan 窗口、无正额度上限的 Coding Plan 窗口不生成进度条。
|
|
55
|
+
- 单个子命令失败时以另一命令的结果作答;两者都失败时给出登录/失败提示,两者皆无有效额度时为软提示(无订阅不冒充错误)。与官方 CLI 档一致:不生成千问「每 1% / 满窗 Token」估算,账号总用量不写入 DSH 账本。
|
|
56
|
+
- 执行安全与 CLI 桥一致:固定参数、不经 shell、单命令 15 秒超时、stdout/stderr 各 1 MiB、窗口隐藏、错误不含子进程原始输出;Windows 下直接以 Node 运行包内 ESM 入口,不执行 npm 的 `.cmd` / `.ps1` 包装脚本。
|
|
57
|
+
- 查询间隔(1–1440 分钟)与并发合并同官方 CLI 档;回归见 `test/bailian-cli.mjs`(真实子进程、双命令合并、单边失败容错、超时/脱敏、缓存/并发/切源、账本三态与 codec),使用合成输出验证,未使用真实百炼订阅账号(`bl usage token-plan` 对未开通账号返回 `{}`)。
|
|
58
|
+
|
|
59
|
+
参考:[官方 CLI 文档](https://platform.qianwenai.com/docs/api-reference/preparation/cli#usage-summary)、[核验源码的 Token Plan 映射](https://github.com/QianWen-AI/qianwen-cli/blob/bb7f7151494f0ffaee5ecb77d40b1f979beb7005/src/services/tokenplan-service.ts)、[输出类型](https://github.com/QianWen-AI/qianwen-cli/blob/bb7f7151494f0ffaee5ecb77d40b1f979beb7005/src/types/usage.ts)。核验日期:2026-09-15。
|
|
60
|
+
|
|
61
|
+
## English
|
|
62
|
+
|
|
63
|
+
Expand **Settings → Cost → Quota → Qwen Token Plan**, enable it and select **Official CLI** as the quota source. Existing configurations keep **Local estimate**.
|
|
64
|
+
|
|
65
|
+
Install `@qianwenai/qianwen-cli` on the DSH host, run `qianwen auth login` as the same OS user, then verify `qianwen usage summary --format json`. Restart DSH after installation so it receives the updated PATH. Browser-side login alone does not configure a remote host or container.
|
|
66
|
+
|
|
67
|
+
The card reads the current `token_plan` credits, computes used credits from total minus remaining, preserves fractional credits, and labels the result `(CLI)`. Optional add-on credits appear separately. The verified CLI maps `resetDate` from subscription `EndTime`; it is shown as subscription expiry, never a monthly reset. No account-wide totals are imported into the ledger, and this mode does not produce Qwen per-1% / full-window token estimates without a reliable billing period.
|
|
68
|
+
|
|
69
|
+
Refreshes share one child process and use the configured 1–1440 minute cache (15 by default), including failed attempts. Manual refresh retries immediately. Disabling the source or switching to local estimates cancels pending work. Errors clear the quota and provide a setup/retry hint. Some upstream failures become `subscribed: false` inside the CLI, so that result is treated as unavailable data. Add-on-only results without a usable main subscription also show this hint. Select local estimates explicitly when needed; saved local settings remain available.
|
|
70
|
+
|
|
71
|
+
Execution uses fixed arguments without a shell, an absolute PATH entry, closed stdin, a hidden window, a 15-second timeout and 1 MiB limits for stdout/stderr. Windows npm installations run their Node entry directly. Credentials remain in the CLI's store; raw child output is never returned to the browser or saved in the ledger.
|
|
72
|
+
|
|
73
|
+
## Bailian CLI (third quota source)
|
|
74
|
+
|
|
75
|
+
The **Bailian CLI** source reads Token Plan and Coding Plan subscription quota through the official [modelstudioai/cli](https://github.com/modelstudioai/cli) (npm package `bailian-cli`, commands `bl` / `bailian`). On the DSH host, as the same OS user: `npm install -g bailian-cli`, then `bl auth login --console`. Restart DSH afterwards to refresh PATH; credentials stay in the CLI.
|
|
76
|
+
|
|
77
|
+
### Sign in from the card (#235)
|
|
78
|
+
|
|
79
|
+
After a failed quota query, the plugin checks `bl auth status --output json --quiet`. The button becomes **Login** only when the status confirms missing console credentials or the CLI reports an authentication failure (exit code 3). Model API keys or OpenAPI AK/SK alone cannot authenticate these quota commands. A failed status check remains a query error.
|
|
80
|
+
|
|
81
|
+
Clicking **Login** runs the fixed `bl auth login --console` command and opens the authorization browser on the **DSH host**. After completion, the plugin verifies console credentials and refreshes quota; successful login and unavailable subscription quota are reported separately. For a remote host, container or headless service, sign in manually in that environment as the same OS user. The button cannot configure host credentials on a different browser machine.
|
|
82
|
+
|
|
83
|
+
Login waits up to five minutes and can be retried. The official CLI owns credentials; the plugin discards login output and returns only classified bilingual errors. Concurrent logins share one process. Source changes, hidden quota display and plugin unload cancel pending login. Regression uses synthetic credentials and real child processes; live account browser authorization was not performed.
|
|
84
|
+
|
|
85
|
+
One query runs `usage token-plan` and `usage coding-plan` in parallel and merges them: Token Plan wins the shared 5-hour/week windows, Coding Plan contributes the 5-hour/week/billing-month windows, and a `source` row names the subscriptions that answered (e.g. `Coding Plan (pro) (CLI)`). Token Plan uses the flat `per5HourPercentage` / `per1WeekPercentage` fields and their reset times. Coding Plan uses nested `per5Hour` / `perWeek` / `perBillMonth` windows. CLI ratios are multiplied by 100 (0.5 becomes 50%); missing Coding Plan ratios are derived from used/total. Percentages retain one decimal, and reset times are epoch milliseconds. Missing Token Plan windows and Coding Plan windows without positive limits are skipped. When one subcommand fails the other answers; both failing shows the login/failure hint, and no valid quota is a soft no-subscription notice. Like the Official CLI source this mode creates no per-1%/full-window estimates and writes no account totals into the ledger. Execution uses the shared CLI bridge (fixed arguments, no shell, 15s timeout, 1 MiB caps, hidden window, no raw child output; Windows runs the package ESM entry directly via Node). Refresh interval and concurrency coalescing match the Official CLI source. Regression: `test/bailian-cli.mjs` with synthetic output and real child processes; no live Bailian subscription was used (`bl usage token-plan` returns `{}` for accounts without Token Plan).
|
|
86
|
+
|
|
87
|
+
Tests use synthetic output, real child processes and bilingual component callbacks. No live Qianwen subscription account was used.
|
|
88
|
+
|
|
89
|
+
Bailian source verification (2026-09-23): [Token Plan output](https://github.com/modelstudioai/cli/blob/72bc8fcce7f5ede6a5df81c1ce72d86de3f3ccd5/packages/commands/src/commands/usage/token-plan.ts), [Coding Plan output](https://github.com/modelstudioai/cli/blob/72bc8fcce7f5ede6a5df81c1ce72d86de3f3ccd5/packages/commands/src/commands/usage/coding-plan.ts), [ratio formatting](https://github.com/modelstudioai/cli/blob/72bc8fcce7f5ede6a5df81c1ce72d86de3f3ccd5/packages/commands/src/commands/usage/quota-box.ts). Tested with source-shaped fixtures and real local child processes; no live paid subscription was queried.
|
|
90
|
+
|
|
91
|
+
Login source verification (2026-10-05): [auth status](https://github.com/modelstudioai/cli/blob/8bbbbc722d70fb200641ef22b6f6d033aeae9f74/packages/commands/src/commands/auth/status.ts), [console login](https://github.com/modelstudioai/cli/blob/8bbbbc722d70fb200641ef22b6f6d033aeae9f74/packages/commands/src/commands/auth/login-console.ts), [exit codes](https://github.com/modelstudioai/cli/blob/8bbbbc722d70fb200641ef22b6f6d033aeae9f74/packages/core/src/errors/codes.ts).
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Sidebar search recovery / 侧栏搜索恢复
|
|
2
|
+
|
|
3
|
+
## English
|
|
4
|
+
|
|
5
|
+
If Settings → Cost is followed by loading placeholders and “No matching
|
|
6
|
+
sessions”, first check the sidebar search. **Do not delete sessions, reset cost
|
|
7
|
+
history, remove the workspace database, or clear browser storage.**
|
|
8
|
+
|
|
9
|
+
1. Close Settings, click the magnifying glass (“Search sessions”), and inspect
|
|
10
|
+
the revealed query. If it is unexpected, use “Clear search”, or press Escape
|
|
11
|
+
while that input has focus. Do not post the query if it contains private data.
|
|
12
|
+
2. Check that the original workspaces return. For [issue #232](https://github.com/Han-1413141/dsh-cost-meter/issues/232),
|
|
13
|
+
the expected count is 18. Open a known conversation and check an existing
|
|
14
|
+
message, without sending a new model request.
|
|
15
|
+
3. If needed, reload the page. This recreates the host’s local search state;
|
|
16
|
+
it does not require resetting stored conversations or billing history.
|
|
17
|
+
4. If it still fails, record the browser version, other DSH plugin package
|
|
18
|
+
names/versions/configuration, password-manager/autofill extensions, whether
|
|
19
|
+
the revealed search was nonempty, and any failed session-search request or
|
|
20
|
+
console error. Redact keys, tokens, account data and private conversation text.
|
|
21
|
+
5. Compare one change at a time in the same browser, with the search cleared:
|
|
22
|
+
password-manager/autofill extension enabled vs disabled, then theme glass
|
|
23
|
+
enabled vs disabled. A fresh browser alone changes too many variables to
|
|
24
|
+
identify a conflicting plugin. Keep the original data and configuration.
|
|
25
|
+
|
|
26
|
+
### What the evidence establishes
|
|
27
|
+
|
|
28
|
+
The reported DSH 0.1.7-rc.2
|
|
29
|
+
[WorkspaceBrowser](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.1.7-rc.2/packages/client/ui-workspace/src/client/rows/WorkspaceBrowser.tsx)
|
|
30
|
+
selects `SearchResults` only for a nonempty query. That branch renders the exact
|
|
31
|
+
two-row skeleton and “No matching sessions” text in the screenshots. A failed
|
|
32
|
+
content-search request can show the same empty text as a successful search with
|
|
33
|
+
zero matches. These screenshots do not establish that sessions were deleted.
|
|
34
|
+
|
|
35
|
+
The search input remains mounted while collapsed. Published Cost Overview in
|
|
36
|
+
both 1.8.6 and 1.8.10 eagerly mounts an unowned password input. A saved-login filler may
|
|
37
|
+
pair that password with the preceding text input, which can be the host search.
|
|
38
|
+
Chromium’s [security FAQ](https://github.com/chromium/chromium/blob/main/docs/security/faq.md)
|
|
39
|
+
explains why `autocomplete=off` is insufficient for password fields. This is a
|
|
40
|
+
source-supported hypothesis, **not confirmation of the reporter’s browser trigger**.
|
|
41
|
+
|
|
42
|
+
The defensive change mounts a credential editor only after “Edit credential”,
|
|
43
|
+
uses its own form and `new-password` hint, and removes the secret draft on Cancel,
|
|
44
|
+
successful Save or Clear. It never clears or changes the host’s search, session
|
|
45
|
+
store, workspace view preferences or ledger. Existing affected tabs still need
|
|
46
|
+
the host’s normal search-clear action, or a page reload.
|
|
47
|
+
|
|
48
|
+
Pinned [Catppuccin 0.5.8 source](https://github.com/NoNameLeGo/dsh-catppuccin-theme/tree/7544bc422609c0f2fae218565172d12dcf72adce)
|
|
49
|
+
changes material/geometry and stamps its own DOM attributes; inspection found no
|
|
50
|
+
session/search mutation or global input handler. That does not exclude a visual
|
|
51
|
+
interaction, but it does not justify blaming the theme for an empty search result.
|
|
52
|
+
|
|
53
|
+
### Verification boundary
|
|
54
|
+
|
|
55
|
+
- `test/sidebar-search-recovery.mjs` executes published host search components
|
|
56
|
+
with deterministic VM hooks. It restores 18 synthetic workspaces and checks
|
|
57
|
+
original message files, workspace preferences and a 78,000-call ledger remain
|
|
58
|
+
unchanged. Slow, failed, aborted and late replies, intentional searches and
|
|
59
|
+
remount recovery are covered on DSH 0.1.7-rc.2 and 0.2.0-rc.2
|
|
60
|
+
- `test/credential-editor-dom.mjs --baseline v1.8.6` uses real React and jsdom.
|
|
61
|
+
An explicitly modeled saved-login pairing reproduces the old unowned-input
|
|
62
|
+
mechanism; the fixed mount and scoped editor avoid that model. Edit, focus,
|
|
63
|
+
Cancel, Save/Clear concurrency, failure/retry and disabled controls are covered
|
|
64
|
+
- Generated dictionary packing round-trips all 527 Chinese and 527 English
|
|
65
|
+
translations; no UI wording is dropped to meet the 262,144-byte bundle limit
|
|
66
|
+
- Native Chromium launch and cloud-browser loopback access were unavailable in
|
|
67
|
+
this execution environment. No native password-manager or reporter-profile
|
|
68
|
+
end-to-end reproduction is claimed. Keep #232 open until the normal-tab
|
|
69
|
+
recovery and upgrade checks are confirmed
|
|
70
|
+
|
|
71
|
+
## 中文
|
|
72
|
+
|
|
73
|
+
出现「加载占位条 → No matching sessions」时,先关闭设置,点击侧栏放大镜,查看
|
|
74
|
+
是否存在意外的搜索内容。使用宿主的「清除搜索」按钮,或在搜索框获得焦点后按
|
|
75
|
+
Escape。然后检查原工作区是否恢复,并打开一个已知对话核对原消息;#232 报告中
|
|
76
|
+
应有 18 个工作区。必要时刷新页面,**不要删除会话、清空费用历史、删除工作区
|
|
77
|
+
数据库或清空浏览器存储**。
|
|
78
|
+
|
|
79
|
+
宿主的非空搜索分支会显示截图中的占位条和空结果提示;搜索请求失败也可能显示
|
|
80
|
+
相同提示,因此不能由截图推断会话已删除。费用概览中的密码框与宿主隐藏搜索框
|
|
81
|
+
被自动填充配对,是有源码依据、尚待用户正常浏览器确认的假说。此修订仅在明确
|
|
82
|
+
点击「编辑凭据」后挂载独立表单中的密码框,取消、成功保存或清除后移除并清空
|
|
83
|
+
草稿,不改动宿主搜索、会话、工作区偏好或账本。
|
|
84
|
+
|
|
85
|
+
如果仍异常,请补充浏览器版本、其他 DSH 插件的完整包名/版本/相关配置、密码
|
|
86
|
+
管理器和自动填充扩展,以及同一浏览器中逐项启停扩展、启停 glass 的对照结果。
|
|
87
|
+
截图和日志需脱敏,不要公开凭据、搜索中的私人信息或原对话内容。静态检查未
|
|
88
|
+
发现 Catppuccin 0.5.8 修改搜索或会话状态的行为,不能据此认定主题是原因。
|
|
89
|
+
|
|
90
|
+
回归覆盖真实宿主组件回调、18 个合成工作区、原消息文件与 78,000 次调用账本的
|
|
91
|
+
完整性,以及真实 React/DOM 凭据编辑流程;自动填充测试明确使用模拟配对规则。
|
|
92
|
+
尚未在报告者的浏览器或原生密码管理器中完成端到端验证,需其确认恢复与升级
|
|
93
|
+
结果后再关闭问题。
|
package/lib/bailian-cli.js
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
* Credentials remain owned by the CLI (bl auth login --console / --api-key).
|
|
4
4
|
* Reads Token Plan + Coding Plan quota windows via two fixed read-only subcommands.
|
|
5
5
|
*/
|
|
6
|
-
import { bridgeCode, resolveNpmCli, runCliJson } from './cli-bridge.js'
|
|
6
|
+
import { bridgeCode, resolveNpmCli, runCliJson, runCliCommand } from './cli-bridge.js'
|
|
7
7
|
|
|
8
8
|
export const BAILIAN_TOKEN_PLAN_ARGS = Object.freeze(['usage', 'token-plan', '--output', 'json', '--quiet'])
|
|
9
9
|
export const BAILIAN_CODING_PLAN_ARGS = Object.freeze(['usage', 'coding-plan', '--output', 'json', '--quiet'])
|
|
10
|
+
export const BAILIAN_AUTH_STATUS_ARGS = Object.freeze(['auth', 'status', '--output', 'json', '--quiet'])
|
|
11
|
+
export const BAILIAN_LOGIN_ARGS = Object.freeze(['auth', 'login', '--console'])
|
|
10
12
|
|
|
11
13
|
const MESSAGES = {
|
|
12
14
|
zh: {
|
|
@@ -15,6 +17,10 @@ const MESSAGES = {
|
|
|
15
17
|
timeout: '百炼 CLI 查询超时,请检查网络后刷新。',
|
|
16
18
|
invalid: '百炼 CLI 返回的额度格式无效,请更新 CLI 后重试。',
|
|
17
19
|
unavailable: '百炼 CLI 未返回有效计划额度(可能未订阅 Token Plan / Coding Plan),请检查 CLI 或切换其他额度来源。',
|
|
20
|
+
auth: '百炼 CLI 未登录,请点击"登录"按钮或运行 bl auth login --console 完成授权。',
|
|
21
|
+
loginFailed: '百炼 CLI 登录失败,请在 DSH 主机运行 bl auth login --console 检查后重试。',
|
|
22
|
+
loginTimeout: '百炼 CLI 登录超时,请重新点击登录并在 DSH 主机的浏览器完成授权。',
|
|
23
|
+
loginInvalid: '百炼 CLI 登录状态格式无效,请更新 CLI 后重试。',
|
|
18
24
|
},
|
|
19
25
|
en: {
|
|
20
26
|
missing: 'Bailian CLI not found. Install bailian-cli on the DSH host (npm install -g bailian-cli), run bl auth login --console, then restart DSH to refresh PATH.',
|
|
@@ -22,6 +28,10 @@ const MESSAGES = {
|
|
|
22
28
|
timeout: 'Bailian CLI query timed out. Check the network and refresh.',
|
|
23
29
|
invalid: 'Bailian CLI returned invalid quota data. Update the CLI and retry.',
|
|
24
30
|
unavailable: 'Bailian CLI returned no valid plan quota (no Token Plan / Coding Plan subscription). Check the CLI or select another quota source.',
|
|
31
|
+
auth: 'Bailian CLI not authenticated. Click the "Login" button or run bl auth login --console to authorize.',
|
|
32
|
+
loginFailed: 'Bailian CLI login failed. Run bl auth login --console on the DSH host and retry.',
|
|
33
|
+
loginTimeout: 'Bailian CLI login timed out. Retry and complete authorization in the DSH host browser.',
|
|
34
|
+
loginInvalid: 'Bailian CLI returned invalid login status. Update the CLI and retry.',
|
|
25
35
|
},
|
|
26
36
|
}
|
|
27
37
|
const message = (locale, code) => MESSAGES[locale === 'en' ? 'en' : 'zh'][code]
|
|
@@ -101,6 +111,52 @@ export function parseBailianUsage(payloads, locale = 'zh') {
|
|
|
101
111
|
return { windows }
|
|
102
112
|
}
|
|
103
113
|
|
|
114
|
+
/**
|
|
115
|
+
* 检查额度查询所需的 console 凭据,而非任意 API Key / AK/SK 登录态。
|
|
116
|
+
* @returns {Promise<{authenticated: boolean}>}
|
|
117
|
+
*/
|
|
118
|
+
export async function checkBailianAuth(locale = 'zh', options = {}) {
|
|
119
|
+
const command = await resolveNpmCli({ packageName: 'bailian-cli', binName: 'bl', env: options.env, platform: options.platform })
|
|
120
|
+
if (!command) throw fail(locale, 'missing')
|
|
121
|
+
const { env = process.env, signal, timeoutMs = 5000, maxBuffer = 1024 * 1024 } = options
|
|
122
|
+
try {
|
|
123
|
+
const status = await runCliJson({
|
|
124
|
+
...command,
|
|
125
|
+
args: [...command.args, ...BAILIAN_AUTH_STATUS_ARGS],
|
|
126
|
+
env, signal, timeoutMs, maxBuffer
|
|
127
|
+
})
|
|
128
|
+
signal?.throwIfAborted()
|
|
129
|
+
if (!record(status) || typeof status.authenticated !== 'boolean'
|
|
130
|
+
|| (status.console != null && !record(status.console))) throw fail(locale, 'invalid')
|
|
131
|
+
return { authenticated: status.authenticated === true && record(status.console) }
|
|
132
|
+
} catch (error) {
|
|
133
|
+
signal?.throwIfAborted()
|
|
134
|
+
throw fail(locale, error.code === 'invalid' ? 'invalid' : bridgeCode(error))
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* 执行百炼 CLI 登录。
|
|
140
|
+
* @returns {Promise<{ok: boolean, message: string}>}
|
|
141
|
+
*/
|
|
142
|
+
export async function loginBailianCli(locale = 'zh', options = {}) {
|
|
143
|
+
const command = await resolveNpmCli({ packageName: 'bailian-cli', binName: 'bl', env: options.env, platform: options.platform })
|
|
144
|
+
if (!command) throw fail(locale, 'missing')
|
|
145
|
+
const { env = process.env, signal, timeoutMs = 300000, maxBuffer = 1024 * 1024 } = options
|
|
146
|
+
try {
|
|
147
|
+
await runCliCommand({ ...command, args: [...command.args, ...BAILIAN_LOGIN_ARGS], env, signal, timeoutMs, maxBuffer })
|
|
148
|
+
signal?.throwIfAborted()
|
|
149
|
+
const status = await checkBailianAuth(locale, { ...options, timeoutMs: Math.min(timeoutMs, 5000) })
|
|
150
|
+
if (!status.authenticated) throw fail(locale, 'auth')
|
|
151
|
+
return { ok: true, message: '' }
|
|
152
|
+
} catch (error) {
|
|
153
|
+
signal?.throwIfAborted()
|
|
154
|
+
const kind = error.code ?? bridgeCode(error)
|
|
155
|
+
throw fail(locale, kind === 'auth' ? 'auth' : kind === 'timeout' ? 'loginTimeout'
|
|
156
|
+
: kind === 'invalid' ? 'loginInvalid' : 'loginFailed')
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
104
160
|
export async function queryBailianCli(locale = 'zh', options = {}) {
|
|
105
161
|
const command = await resolveNpmCli({ packageName: 'bailian-cli', binName: 'bl', env: options.env, platform: options.platform })
|
|
106
162
|
if (!command) throw fail(locale, 'missing')
|
|
@@ -111,7 +167,7 @@ export async function queryBailianCli(locale = 'zh', options = {}) {
|
|
|
111
167
|
if (settled.every(x => x.status === 'rejected')) {
|
|
112
168
|
const errors = settled.map(x => x.reason)
|
|
113
169
|
// Surface timeout first: it is the most actionable failure across both commands.
|
|
114
|
-
throw fail(locale, bridgeCode(errors.find(e => e.kind === 'timeout') ?? errors[0]))
|
|
170
|
+
throw fail(locale, bridgeCode(errors.find(e => e.kind === 'timeout') ?? errors.find(e => e.kind === 'exit' && e.exitCode === 3) ?? errors[0], { authExitCode: 3 }))
|
|
115
171
|
}
|
|
116
172
|
try {
|
|
117
173
|
return parseBailianUsage({
|
|
@@ -123,7 +179,7 @@ export async function queryBailianCli(locale = 'zh', options = {}) {
|
|
|
123
179
|
// valid remains does the broken command's real failure become the message.
|
|
124
180
|
if (error.code === 'unavailable') {
|
|
125
181
|
const broken = settled.map(x => x.reason).find((_, i) => settled[i].status === 'rejected')
|
|
126
|
-
if (broken) throw fail(locale, bridgeCode(broken))
|
|
182
|
+
if (broken) throw fail(locale, bridgeCode(broken, { authExitCode: 3 }))
|
|
127
183
|
}
|
|
128
184
|
throw error
|
|
129
185
|
}
|
package/lib/cli-bridge.js
CHANGED
|
@@ -38,10 +38,10 @@ export async function resolveNpmCli({ packageName, binName, env = process.env, p
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* Run a fixed CLI command and
|
|
41
|
+
* Run a fixed CLI command and consume stdout. Rejects only { kind, exitCode }:
|
|
42
42
|
* 'maxbuffer' | 'timeout' | 'exit' | 'spawn' | 'json' — never raw output or paths.
|
|
43
43
|
*/
|
|
44
|
-
|
|
44
|
+
function runCli({ file, args, env = process.env, signal, timeoutMs = 15000, maxBuffer = 1024 * 1024 }, readOutput) {
|
|
45
45
|
return new Promise((done, reject) => {
|
|
46
46
|
const child = execFile(file, args, {
|
|
47
47
|
encoding: 'utf8', windowsHide: true, shell: false, env, signal, timeout: timeoutMs, killSignal: 'SIGKILL', maxBuffer,
|
|
@@ -54,12 +54,16 @@ export function runCliJson({ file, args, env = process.env, signal, timeoutMs =
|
|
|
54
54
|
reject(Object.assign(new Error('cli'), { kind, exitCode: typeof error.code === 'number' ? error.code : undefined }))
|
|
55
55
|
return
|
|
56
56
|
}
|
|
57
|
-
try { done(
|
|
57
|
+
try { done(readOutput(out)) } catch { reject(Object.assign(new Error('cli'), { kind: 'json' })) }
|
|
58
58
|
})
|
|
59
59
|
child.stdin?.end()
|
|
60
60
|
})
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
export const runCliJson = options => runCli(options, JSON.parse)
|
|
64
|
+
/** Login output can contain private paths or credentials; discard it completely. */
|
|
65
|
+
export const runCliCommand = options => runCli(options, () => undefined)
|
|
66
|
+
|
|
63
67
|
/** Map a bridge error kind to a per-CLI message code (exitCode may refine 'exit'). */
|
|
64
68
|
export const bridgeCode = (error, { authExitCode } = {}) =>
|
|
65
69
|
error.kind === 'timeout' ? 'timeout'
|