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 +66 -19
- package/README.zh.md +53 -17
- package/lib/auto-update.js +12 -1
- package/lib/client.js +540 -67
- package/lib/index.js +13 -1
- package/lib/opencode-console.js +329 -94
- package/lib/opencode-session.js +5 -1
- package/lib/providers.js +24 -7
- package/package.json +1 -1
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
|
|
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
|
|
93
|
+
From npm:
|
|
82
94
|
|
|
83
95
|
```bash
|
|
84
|
-
dsh plugin --profile desktop add
|
|
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
|
|
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/
|
|
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
|
|
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
|
|
141
|
-
|
|
142
|
-
|
|
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.
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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.
|
|
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
|
|
54
|
-
|
|
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
|
-
|
|
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/
|
|
108
|
-
|
|
109
|
-
3. `~/.local/share/opencode/
|
|
110
|
-
`
|
|
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.
|
|
256
|
-
|
|
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)与失败隔离、会话头注入、余额变化动画;共
|
|
373
|
+
auth.json)与失败隔离、会话头注入、余额变化动画;共 98 个测试。
|
|
338
374
|
|
|
339
375
|
## 许可证
|
|
340
376
|
|
package/lib/auto-update.js
CHANGED
|
@@ -471,7 +471,18 @@ export function startAutoUpdate(options) {
|
|
|
471
471
|
installKind: install.kind,
|
|
472
472
|
releaseUrl: release.url
|
|
473
473
|
});
|
|
474
|
-
|
|
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") {
|