dsh-multi-provider-usage 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,7 +36,8 @@ panel settings page — extended from one provider to three.
36
36
  console token is auto-discovered from `OPENCODE_CONSOLE_TOKEN`, the OpenCode
37
37
  CLI auth file, or its local `opencode.db`; the token never leaves the host.
38
38
  When that token is missing, the balance slot becomes a clickable "Set up" chip
39
- that opens the plugin settings instead of silently showing nothing. Command
39
+ that opens the plugin settings, where the OpenCode GO card renders a
40
+ step-by-step setup guide, instead of silently showing nothing. Command
40
41
  Code's tooltip breaks its amount into purchased + free credits; a credits
41
42
  response without either pool renders a muted **No top-up** badge.
42
43
  - **Auto-update** — on boot and every configured interval the host checks the
@@ -53,6 +54,11 @@ panel settings page — extended from one provider to three.
53
54
  the old value to the refreshed one while a red/green delta chip (for example
54
55
  `-0.1`) floats up and fades away. There is no popup and no turn-event
55
56
  tracking, so the animation may appear up to one poll interval later.
57
+ - **Low-balance tone** — a balance at or below 5 renders amber and at or below 1
58
+ renders red (thresholds are in the currency unit and apply to USD and CNY
59
+ plans alike), in both the sidebar and the settings page's live-usage sections.
60
+ - **Click-to-retry failures** — an error line in the widget is itself clickable
61
+ (and keyboard-activatable) and forces one fresh upstream read.
56
62
  - **Same-origin proxy** — the browser only talks to `/provider-usage/usage` and
57
63
  `/provider-usage/settings`; API keys stay in the host process.
58
64
  - **Failure isolation** — one broken provider never hides the other two. The
@@ -65,6 +71,12 @@ panel settings page — extended from one provider to three.
65
71
  - **Bundle settings page** — sidebar **Plugins** panel → this bundle's card:
66
72
  enable/disable each provider, edit credential refs and endpoints, choose the
67
73
  GLM region, and see non-network detection status. Save applies immediately.
74
+ Each provider card embeds a **live usage** section — quota windows with
75
+ percentages, time-to-reset and the top-up balance, from the same canonical
76
+ snapshot the widget renders. While the page is open it follows the widget:
77
+ a 30-second poll (when the tab is visible) plus an "Updated HH:MM" stamp,
78
+ a **Refresh usage** control forces a re-read, and every save re-reads with
79
+ `refresh=1`, so a changed credential ref shows its own numbers immediately.
68
80
  The GLM card also carries the **referral link** (Z.ai "Invite Friends, Get
69
81
  Credits") as a clickable, copyable row. The URL is a bundle constant: it never
70
82
  goes through the settings route and a settings save cannot overwrite it.
@@ -78,16 +90,22 @@ panel settings page — extended from one provider to three.
78
90
 
79
91
  ## Install
80
92
 
81
- From a checkout:
93
+ From npm:
82
94
 
83
95
  ```bash
84
- dsh plugin --profile desktop add /path/to/dsh-provider-usage
96
+ dsh plugin --profile desktop add dsh-multi-provider-usage
85
97
  ```
86
98
 
87
99
  or, for the web profile:
88
100
 
89
101
  ```bash
90
- dsh plugin --profile web add /path/to/dsh-provider-usage
102
+ dsh plugin --profile web add dsh-multi-provider-usage
103
+ ```
104
+
105
+ From a checkout (for development):
106
+
107
+ ```bash
108
+ dsh plugin --profile desktop add /path/to/dsh-provider-usage
91
109
  ```
92
110
 
93
111
  The package declares `dsh.bundle.patch`, so the CLI reconciles it into the
@@ -127,21 +145,48 @@ in this order:
127
145
 
128
146
  1. `opencodeConsoleApiKeyEnv` (`OPENCODE_CONSOLE_TOKEN` by default), resolved
129
147
  through DSH credentials;
130
- 2. `~/.local/share/opencode/auth.json` (`$XDG_DATA_HOME/opencode/auth.json` when
131
- set), provider `opencode`;
132
- 3. the `credential` table of `~/.local/share/opencode/opencode.db` (read via
148
+ 2. the `credential` table of `~/.local/share/opencode/opencode.db` (read via
133
149
  `node:sqlite` when available; this is where current OpenCode releases keep
134
- the OAuth grant).
150
+ the OAuth grant and rotate it);
151
+ 3. `~/.local/share/opencode/auth.json` (`$XDG_DATA_HOME/opencode/auth.json` when
152
+ set), provider `opencode` — the legacy location kept by older releases.
153
+
154
+ The database is preferred deliberately: OpenCode migrated its grant there but
155
+ often leaves the old `auth.json` behind, and that stale token returns 401. Every
156
+ usable OAuth candidate is tried in turn (at most four), so one expired store
157
+ cannot hide the live one. Channel builds (`opencode-beta.db`, `opencode-dev.db`,
158
+ …) and `$OPENCODE_DB` are discovered too. The billing host comes from the grant's
159
+ `metadata.server` (so a self-hosted console works), falling back to the public
160
+ origin; `opencodeConsoleBaseUrl` overrides it.
161
+
162
+ An OpenCode **API-key** login (`oc_sk_…`, the service-account method) carries no
163
+ console session and is never sent to the billing endpoint. Such a login, a
164
+ missing token, or an expired/revoked grant renders a clickable **Set up** chip
165
+ (instead of a mute dash) that opens this bundle's page in the Plugins panel and
166
+ names the fix: run `opencode auth login` and choose the **OpenCode Console
167
+ account**, or store a console token as `OPENCODE_CONSOLE_TOKEN`.
168
+
169
+ When the balance is not configured (or the console login is fixable), the
170
+ OpenCode GO card in the plugin settings renders a step-by-step **setup guide**
171
+ in place of the static hint. The lead-in names the state-specific fix (nothing
172
+ configured, an API-key login, or an expired sign-in). Step ① shows the
173
+ `opencode auth login` CLI command with a **Copy command** button; step ② is the
174
+ manual path — copy the console token and store it as the `OPENCODE_CONSOLE_TOKEN`
175
+ credential — with a **Copy credential ref** button and a link to the OpenCode
176
+ console website (derived from `opencodeConsoleBaseUrl`, ending in `/console`);
177
+ step ③ offers a **Re-check** button that re-reads the sign-in state and forces
178
+ one upstream usage read, so a fresh login shows the balance without restarting
179
+ DSH (unsaved form edits are never discarded by the re-check). The
180
+ `/provider-usage` command gives the same state-specific guidance as text for
181
+ all three fixable states.
135
182
 
136
183
  The optional `opencodeConsoleAuthFile` overrides the auth file (and its
137
- directory is used for `opencode.db`), `opencodeConsoleOrgId` supplies the
184
+ directory is used to locate `opencode.db`), `opencodeConsoleOrgId` supplies the
138
185
  `x-org-id` header, and `opencodeConsoleEnabled: false` turns the whole balance
139
186
  read off. Balance failures never hide the GO quota: the provider stays `ok` with
140
- `balance: null` and a `meta.balanceError`. A missing console credential is
141
- actionable rather than hidden: the balance slot renders a clickable **Set up**
142
- chip that opens this bundle's page in the Plugins panel, whose tooltip names the
143
- fix (`opencode auth login` or `OPENCODE_CONSOLE_TOKEN`). Any other balance
144
- failure renders a muted **Balance —** badge whose tooltip carries the error.
187
+ `balance: null` and a `meta.balanceError` (plus `meta.balanceSetup` when the
188
+ user can fix it). Any other balance failure renders a muted **Balance —** badge
189
+ whose tooltip carries the error.
145
190
 
146
191
  ### Command Code balance
147
192
 
@@ -285,10 +330,12 @@ the settings page and the auto-generated form hot-apply.
285
330
  Failed providers have `"status": "error"`, `code`, `error` and `windows: []`;
286
331
  their siblings are still returned. A body with `providers: []` means no provider
287
332
  is enabled. `meta.balanceError` carries a tolerated balance failure (the
288
- provider still answers `ok`); `meta.balanceMissing` marks a Command Code credits
289
- response without purchased/free pools. `meta.errors` lists partial Command Code
290
- endpoint failures: the numbers stay, the health dot turns amber, and the tooltip
291
- names the failed calls. Error cards keep the plan they already resolved.
333
+ provider still answers `ok`); `meta.balanceSetup` (`no-credential`, `api-key` or
334
+ `expired`) marks a failure the user can fix by signing in again;
335
+ `meta.balanceMissing` marks a Command Code credits response without
336
+ purchased/free pools. `meta.errors` lists partial Command Code endpoint
337
+ failures: the numbers stay, the health dot turns amber, and the tooltip names
338
+ the failed calls. Error cards keep the plan they already resolved.
292
339
 
293
340
  ### `GET /provider-usage/settings`
294
341
 
@@ -374,7 +421,7 @@ network and no real keys. They cover the schema, all three adapters, OpenCode
374
421
  GO service integration/fallback, aggregation and error isolation, caching and
375
422
  forced refresh, settings conflicts/whitelisting, the client bundle contract,
376
423
  top-up balance discovery (credential and local auth.json) and failure
377
- isolation, and the session-header injection. 75 tests, all offline.
424
+ isolation, and the session-header injection. 98 tests, all offline.
378
425
 
379
426
  ## License
380
427
 
package/README.zh.md CHANGED
@@ -30,7 +30,8 @@
30
30
  购买额度 + 免费额度的 “Extra Credits” 余额;两者都显示在账号/套餐旁,并出现在
31
31
  `/provider-usage` 文本中。OpenCode 控制台令牌依次从 `OPENCODE_CONSOLE_TOKEN`、
32
32
  OpenCode CLI 的 auth.json 或本机 `opencode.db` 自动发现,令牌不会离开宿主进程。
33
- 没找到控制台令牌时,余额位置显示可点击的「配置余额」,点击直接打开本插件设置页。
33
+ 没找到控制台令牌时,余额位置显示可点击的「配置余额」,点击直接打开本插件设置页;
34
+ 设置页内为分步引导:可复制的登录命令、控制台网页链接与「重新检测」按钮。
34
35
  Command Code 的悬停提示会拆成「购买 + 免费」;credits 响应没有这两个池时显示
35
36
  灰色「无充值额度」。
36
37
  - **自动更新** — 宿主启动后以及按配置的间隔检查 GitHub Release。本地
@@ -42,6 +43,10 @@
42
43
  - **余额变化动画** — 组件按正常轮询周期刷新;某个服务商的余额相比上一次轮询
43
44
  发生变化时,数字会从旧值滚动到新值,同时一个红色/绿色差值(例如 `-0.1`)
44
45
  向上飘走并淡出。没有弹窗,也不依赖对话轮次事件;动画最多可能晚一个轮询周期。
46
+ - **余额低额预警** — 余额 ≤ 5 时余额徽章转黄、≤ 1 转红(货币单位通用),侧边栏
47
+ 和设置页的「实时用量」一致生效,不用点开卡片也能一眼看到余额告急。
48
+ - **失败可一键重试** — 读取失败时,组件里的错误文字本身可点击(也支持键盘
49
+ Enter/空格),立即强制重新读取。
45
50
  - **同源代理** — 浏览器只访问 `/provider-usage/usage` 和
46
51
  `/provider-usage/settings`,API Key 始终留在宿主进程。
47
52
  - **故障隔离** — 一个服务商失败不会影响另外两个;只要还有服务商成功,路由就返回
@@ -50,8 +55,12 @@
50
55
  - **设置快捷入口** — 组件标题栏的「设置」按钮直接打开侧边栏「插件」中的本插件
51
56
  页面,并定位到下面的配置页。
52
57
  - **插件设置页** — 侧边栏「插件」→ 本插件卡片:启用/禁用服务商、编辑凭据引用和
53
- 接口地址、选择 GLM 区域,并查看无网络请求的检测状态。保存后立即生效。GLM
54
- 卡片底部还有一条**推广链接**(Z.ai「邀请好友得积分」),可点击打开或一键复制;
58
+ 接口地址、选择 GLM 区域,并查看无网络请求的检测状态。保存后立即生效。每个服务商
59
+ 卡片内嵌「实时用量」:显示各额度窗口的百分比、剩余重置时间与订阅外充值余额(与
60
+ 侧边栏组件同一份数据);页面打开期间每 30 秒自动跟随刷新,并显示「更新于 HH:MM」
61
+ 时间戳;「刷新用量」按钮强制读取;保存设置(例如换了 apiKeyEnv 凭据引用)后会以
62
+ `refresh=1` 立即重新读取,马上反映新凭据的用量与余额。
63
+ GLM 卡片底部还有一条**推广链接**(Z.ai「邀请好友得积分」),可点击打开或一键复制;
55
64
  链接是写死的常量,不经过设置接口,也不会被保存的表单覆盖。
56
65
  - **`/provider-usage` 聊天命令** — 在对话中打印所有启用服务商的用量文本。
57
66
  - **时间条** — 额度条下方,灰色是窗口已流逝时间,蓝色是剩余时间,几何尺寸与参考
@@ -62,7 +71,13 @@
62
71
 
63
72
  ## 安装
64
73
 
65
- 从本地 checkout 安装:
74
+ 从 npm 安装:
75
+
76
+ ```bash
77
+ dsh plugin --profile desktop add dsh-multi-provider-usage
78
+ ```
79
+
80
+ 开发时也可以直接从本地 checkout 安装:
66
81
 
67
82
  ```bash
68
83
  dsh plugin --profile desktop add /path/to/dsh-provider-usage
@@ -104,18 +119,38 @@ GO 额度接口不包含账户预付费余额。余额来自
104
119
  OAuth 令牌(GO API Key 访问会返回 403)。宿主按以下顺序解析:
105
120
 
106
121
  1. `opencodeConsoleApiKeyEnv`(默认 `OPENCODE_CONSOLE_TOKEN`),通过 DSH 凭据解析;
107
- 2. `~/.local/share/opencode/auth.json`(设置了 `$XDG_DATA_HOME` 时用
108
- `$XDG_DATA_HOME/opencode/auth.json`)中的 `opencode` provider;
109
- 3. `~/.local/share/opencode/opencode.db` 的 `credential` 表(Node 支持
110
- `node:sqlite` 时读取;当前 OpenCode 将 OAuth grant 存在这里)。
122
+ 2. `~/.local/share/opencode/opencode.db` 的 `credential` 表(Node 支持
123
+ `node:sqlite` 时读取;当前 OpenCode 将 OAuth grant 存在这里并在运行时轮换);
124
+ 3. `~/.local/share/opencode/auth.json`(设置了 `$XDG_DATA_HOME` 时用
125
+ `$XDG_DATA_HOME/opencode/auth.json`)中的 `opencode` provider——旧版本的位置。
126
+
127
+ 数据库优先是刻意的:OpenCode 已把登录态迁到数据库,但经常把旧的 `auth.json`
128
+ 留在磁盘上,里面的过期令牌会返回 401。宿主会依次尝试每个可用的 OAuth 登录(最多
129
+ 四个),所以一份过期数据不会遮住仍然有效的登录。渠道版数据库(`opencode-beta.db`、
130
+ `opencode-dev.db` 等)和 `$OPENCODE_DB` 也会被发现。余额请求使用 grant 里的
131
+ `metadata.server`(自建控制台可用),缺省回落到公开域名;
132
+ `opencodeConsoleBaseUrl` 可覆盖。
133
+
134
+ OpenCode 的 **API Key** 登录(`oc_sk_…`,service-account 方式)不含控制台会话,
135
+ 不会被发到只接受控制台令牌的 billing 接口。API Key 登录、缺少令牌或登录已过期/
136
+ 被吊销时,余额位置显示可点击的「配置余额」而不是灰色横杠;点击打开本插件在
137
+ Plugins 面板中的设置页,悬停提示修复方式:运行 `opencode auth login` 并选择
138
+ **OpenCode Console account**,或把控制台令牌保存为 `OPENCODE_CONSOLE_TOKEN`。
139
+
140
+ 余额未配置或控制台登录可修复时,插件设置页的 OpenCode GO 卡片不再给一句静态
141
+ 提示,而是渲染分步**引导**:引言按当前状态指明修复方式(未配置 / API Key 登录 /
142
+ 登录已过期)。第①步给出 `opencode auth login` 命令与「复制命令」按钮;第②步是
143
+ 手动路径——从控制台网页复制令牌、保存为 `OPENCODE_CONSOLE_TOKEN` 凭据——带
144
+ 「复制凭据名」按钮和打开 OpenCode 控制台网页的链接(由 `opencodeConsoleBaseUrl`
145
+ 推导,以 `/console` 结尾);第③步提供「重新检测」按钮,重读登录状态并强制刷新
146
+ 一次用量,新登录无需重启 DSH 即可显示余额(重新检测不会丢弃表单里未保存的修改)。
147
+ `/provider-usage` 命令对三种可修复状态都会给出同样的文字引导。
111
148
 
112
149
  可用 `opencodeConsoleAuthFile` 覆盖 auth.json 路径(其目录也用于定位
113
150
  `opencode.db`),`opencodeConsoleOrgId` 提供 `x-org-id`,
114
151
  `opencodeConsoleEnabled: false` 可关闭余额读取。余额失败不会影响 GO 额度:
115
- provider 仍为 `ok`,`balance` 为 `null` 并带 `meta.balanceError`。缺少控制台
116
- 令牌时不再静默隐藏:余额位置显示可点击的「配置余额」,点击打开本插件在
117
- Plugins 面板中的设置页,悬停提示修复方式(`opencode auth login` 或
118
- `OPENCODE_CONSOLE_TOKEN`)。其它余额失败显示灰色「余额 —」,悬停给出原因。
152
+ provider 仍为 `ok`,`balance` 为 `null` 并带 `meta.balanceError`(可由用户修复时
153
+ 另带 `meta.balanceSetup`)。其它余额失败显示灰色「余额 —」,悬停给出原因。
119
154
 
120
155
  ### Command Code 余额
121
156
 
@@ -252,10 +287,11 @@ Loader 直接读取导出的 `Config` schema;所有字段都是 `.volatile()`
252
287
 
253
288
  失败的服务商为 `"status": "error"`,带 `code`、`error` 和空 `windows`,其余服务商
254
289
  照常返回。`providers: []` 表示没有启用任何服务商。`meta.balanceError` 表示被容忍
255
- 的余额失败(provider 仍为 `ok`);`meta.balanceMissing` 表示 Command Code 的
256
- credits 响应没有购买/免费额度。`meta.errors` 列出 Command Code 的部分接口失败:
257
- 数字照常显示,但健康点变黄,悬停可看到失败的接口。错误卡片仍保留已经解析出的
258
- 套餐信息。
290
+ 的余额失败(provider 仍为 `ok`);`meta.balanceSetup`(`no-credential`、`api-key`
291
+ 或 `expired`)表示用户重新登录即可修复的余额状态;`meta.balanceMissing` 表示
292
+ Command Code 的 credits 响应没有购买/免费额度。`meta.errors` 列出 Command Code
293
+ 的部分接口失败:数字照常显示,但健康点变黄,悬停可看到失败的接口。错误卡片仍
294
+ 保留已经解析出的套餐信息。
259
295
 
260
296
  ### `GET /provider-usage/settings`
261
297
 
@@ -334,7 +370,7 @@ npm test
334
370
  测试是封闭的:伪造 `globalThis.fetch` + stub Cordis 上下文,不访问网络、不使用真实
335
371
  密钥。覆盖 schema、三个适配器、OpenCode GO 服务集成/回退、聚合与故障隔离、缓存与
336
372
  强制刷新、设置冲突/白名单、客户端 bundle 契约、充值余额发现(凭据 / 本地
337
- auth.json)与失败隔离、会话头注入、余额变化动画;共 75 个测试。
373
+ auth.json)与失败隔离、会话头注入、余额变化动画;共 98 个测试。
338
374
 
339
375
  ## 许可证
340
376
 
@@ -471,7 +471,18 @@ export function startAutoUpdate(options) {
471
471
  installKind: install.kind,
472
472
  releaseUrl: release.url
473
473
  });
474
- if (!isNewerVersion(release.version, currentVersion)) {
474
+ // `currentVersion` never changes in a running host, so a release
475
+ // this session already applied would be re-downloaded every cycle
476
+ // until restart. Compare against what this session applied.
477
+ const appliedVersion = typeof state.version === "string" && parseVersion(state.version) !== null
478
+ ? state.version
479
+ : currentVersion;
480
+ if (!isNewerVersion(release.version, appliedVersion)) {
481
+ if (appliedVersion !== currentVersion) {
482
+ // Already self-updated to this release; restore the earlier
483
+ // "restart required" notice instead of re-applying.
484
+ return patch({ status: "updated" });
485
+ }
475
486
  return patch({ status: "up-to-date", message: null, restartRequired: false });
476
487
  }
477
488
  if (install.kind === "checkout" && typeof install.checkout === "string") {