dsh-bottom-info-bar 1.16.1 → 1.16.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 +98 -210
- package/README.zh-CN.md +99 -208
- package/lib/client.js +25 -5
- package/lib/index.js +50 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,292 +1,180 @@
|
|
|
1
1
|
# Bottom Info Bar
|
|
2
2
|
|
|
3
|
-
<img src="assets/wechat-group.png" width="118" align="right" alt="WeChat group DeepThinking — scan to join">
|
|
4
|
-
|
|
5
3
|
**English** | [**中文**](README.zh-CN.md)
|
|
6
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/dsh-bottom-info-bar)
|
|
7
6
|
[](https://github.com/songoao25/dsh-bottom-info-bar/blob/main/LICENSE)
|
|
8
|
-
[](https://github.com/songoao25/dsh-bottom-info-bar/releases)
|
|
9
|
-
[](https://github.com/songoao25/dsh-bottom-info-bar)
|
|
10
|
-
[](https://github.com/songoao25/dsh-bottom-info-bar/actions)
|
|
11
7
|
|
|
12
|
-
|
|
8
|
+
A DeepSeek Harness plugin that replaces the stats row under the composer with one line: provider and model, real balance or subscription quota, peak/off-peak pricing, and what this session has cost.
|
|
13
9
|
|
|
14
|
-
|
|
10
|
+

|
|
15
11
|
|
|
16
|
-
|
|
17
|
-

|
|
12
|
+
## What it shows
|
|
18
13
|
|
|
19
|
-
|
|
14
|
+
| Group | Fields |
|
|
15
|
+
|---|---|
|
|
16
|
+
| Provider | the exact provider and model, mirroring DSH's model switcher |
|
|
17
|
+
| Native stats (kept) | turns and steps, model time, tool time, cache hit rate, input/output tokens, context usage |
|
|
18
|
+
| Money | real balance, subscription quota windows, or this month's cloud bill |
|
|
19
|
+
| Pricing | peak and off-peak prices, the current period, a countdown to the next price change |
|
|
20
|
+
| Spend | this session (including subagents), today, last 30 days, all time |
|
|
21
|
+
| Extras | main time, world time, custom text |
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
Click the bar to switch between **full** and **compact** mode; colors follow DSH's light or dark theme.
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+

|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
- **Real data, honestly labelled** — balances, quotas, plans and bills come from official provider APIs. The one figure that cannot (OpenAI has no public balance API) is derived from your spending rate and marked `(estimated)` in the bar.
|
|
27
|
-
- **Exactly what DSH shows** — the provider and model match DSH's model switcher, and newly published models are picked up automatically.
|
|
28
|
-
- **Peak / off-peak pricing** — both prices plus a countdown to the next switch (weekends count as off-peak all day).
|
|
29
|
-
- **Honest spend tracking** — this conversation (including subagents), today, last 30 days, and all time — persisted to disk, nothing lost on restart.
|
|
30
|
-
- **Native by design** — it replaces the native row instead of duplicating it; click to switch full/compact, and pick fields and colours on the plugin page.
|
|
27
|
+
## Three billing modes
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
The bar follows the active session and picks the mode from the provider. The three modes are mutually exclusive — there is no manual switch.
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
- **[pnpm](https://pnpm.io/)** — used by `dsh plugin` to manage profile packages.
|
|
36
|
-
- An API key for whichever provider you use, set in DSH under **Settings → Models**.
|
|
31
|
+
### Balance
|
|
37
32
|
|
|
38
|
-
|
|
33
|
+
Shows the real balance from the provider's own API. It refetches when the bar opens, the page refreshes or the provider changes, then polls every 60 seconds; a failed refresh keeps the last known figure on screen. Below 20 (in the account's currency) the amount and a **Low** label turn red.
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
dsh plugin --profile web add dsh-bottom-info-bar
|
|
42
|
-
```
|
|
35
|
+

|
|
43
36
|
|
|
44
|
-
|
|
37
|
+
### Subscription quota
|
|
45
38
|
|
|
46
|
-
|
|
39
|
+
Shows the remaining quota per window (5-hour / weekly / monthly) and a countdown to the next reset — both always come from the same window, so they can never disagree. Windows show **remaining** percent by default and can be switched to **used** in settings; the low-quota warning always follows the remaining ≤ 20% rule. Compact mode prefers the shortest available window (5-hour → weekly → monthly).
|
|
47
40
|
|
|
48
|
-
|
|
41
|
+
### Cloud billing
|
|
49
42
|
|
|
50
|
-
|
|
43
|
+
Shows this month's real spend from the provider's official billing API, for example `Together | This month $12.34` or `AWS Bedrock | This month $45.60 · Budget 46%`. Cloudflare also shows the daily free-quota remainder with a UTC-midnight reset countdown when the API actually reports a free allowance.
|
|
51
44
|
|
|
52
|
-
|
|
53
|
-
<summary>Other ways to install (and which one to pick)</summary>
|
|
45
|
+
## Dark mode
|
|
54
46
|
|
|
55
|
-
|
|
47
|
+
Colors adapt to DSH's theme — there is no separate setting.
|
|
56
48
|
|
|
57
|
-
|
|
49
|
+

|
|
58
50
|
|
|
59
|
-
|
|
60
|
-
dsh plugin --profile web add https://github.com/songoao25/dsh-bottom-info-bar
|
|
61
|
-
```
|
|
51
|
+
## Install
|
|
62
52
|
|
|
63
|
-
|
|
53
|
+
Requires DeepSeek Harness with the web interface (`dsh web`) and pnpm.
|
|
64
54
|
|
|
65
|
-
**From
|
|
55
|
+
**From npm** (recommended — installs the released version):
|
|
66
56
|
|
|
67
57
|
```bash
|
|
68
|
-
|
|
69
|
-
dsh plugin --profile web add /path/to/dsh-bottom-info-bar
|
|
58
|
+
dsh plugin --profile web add dsh-bottom-info-bar
|
|
70
59
|
```
|
|
71
60
|
|
|
72
|
-
|
|
61
|
+
**From the GitHub repository** — [github.com/songoao25/dsh-bottom-info-bar](https://github.com/songoao25/dsh-bottom-info-bar) (tracks the default branch; `lib/` is committed, so no build runs on install):
|
|
73
62
|
|
|
74
|
-
|
|
63
|
+
```bash
|
|
64
|
+
dsh plugin --profile web add https://github.com/songoao25/dsh-bottom-info-bar
|
|
65
|
+
```
|
|
75
66
|
|
|
76
|
-
**
|
|
67
|
+
**With the local one-click script** (clone, build and install in one step):
|
|
77
68
|
|
|
78
69
|
```bash
|
|
79
70
|
git clone https://github.com/songoao25/dsh-bottom-info-bar.git
|
|
80
71
|
cd dsh-bottom-info-bar
|
|
81
|
-
./install.sh
|
|
72
|
+
./install.sh
|
|
82
73
|
```
|
|
83
74
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Detailed instructions and troubleshooting: [docs/INSTALL.md](docs/INSTALL.md).
|
|
87
|
-
</details>
|
|
88
|
-
|
|
89
|
-
## Configuration
|
|
90
|
-
|
|
91
|
-
Everything is on the **plugin page** — **Plugins → bottom-info-bar** — not in DSH's global settings. Field switches, colours, custom text and the billing ledger all live there, and changes save as you make them.
|
|
75
|
+
Then **restart `dsh web`** — plugins are composed when the host starts, so a page refresh is not enough. The plugin shows up in the Plugins list, enabled:
|
|
92
76
|
|
|
93
|
-

|
|
94
78
|
|
|
95
|
-
|
|
79
|
+
More detail and troubleshooting: [docs/INSTALL.md](docs/INSTALL.md).
|
|
96
80
|
|
|
97
|
-
|
|
81
|
+
## Settings
|
|
98
82
|
|
|
99
|
-
|
|
83
|
+
Everything lives on the plugin page — **Plugins → bottom-info-bar**. Changes save as you make them.
|
|
100
84
|
|
|
101
|
-

|
|
102
86
|
|
|
103
|
-
|
|
87
|
+
**Fields and colors** — one switch and one color per field, in two groups. Turn a field off and the bar drops it.
|
|
104
88
|
|
|
105
|
-
|
|
89
|
+
- **Native information** — the fields DSH's own stats row already showed.
|
|
90
|
+
- **Plugin information** — everything this bar adds: provider and model, subscriptions, spend, balance, pricing and quota.
|
|
106
91
|
|
|
107
|
-
|
|
92
|
+

|
|
93
|
+

|
|
108
94
|
|
|
109
|
-
|
|
110
|
-
- **It never updates itself.** The version reminder only tells you a newer version exists and prepares the command; nothing on your machine changes until you run it.
|
|
111
|
-
- **It never binds your ChatGPT account.** That belongs to the companion plugin [dsh-chatgpt-subscription](https://github.com/songoao25). This bar only reads the local token.
|
|
112
|
-
- **It never stores conversations.** The ledger records tokens, model, provider and cost — no prompts, no messages, no API keys.
|
|
113
|
-
- **It does not work outside the web interface.** There is no terminal or headless equivalent of the bar.
|
|
95
|
+
**Subscription window percentage** — show quota windows as **remaining** (default) or **used**. The low-quota warning always follows remaining ≤ 20%.
|
|
114
96
|
|
|
115
|
-
|
|
97
|
+

|
|
98
|
+

|
|
116
99
|
|
|
117
|
-
|
|
100
|
+
**Time and date** — main and world time zones, plus which of year / month / day / hour / minute / second to display.
|
|
118
101
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
Shows the **real balance** from the provider's `/user/balance` (or equivalent) API. It refetches the moment the bar opens, refreshes, or the provider changes, then polls every 60 seconds. The last known snapshot is kept on failure so the row never goes blank.
|
|
122
|
-
|
|
123
|
-
When the balance drops below ¥20, the amount and a `Low` label turn red.
|
|
124
|
-
|
|
125
|
-
### Subscription mode (ChatGPT / Codex, OpenCode Go, Zhipu, Xiaomi MiMo Token Plan, Command Code, MiniMax Token Plan)
|
|
126
|
-
|
|
127
|
-
Shows **quota remaining per window** (5-hour / weekly / monthly, where remaining = 100 − used) and a **countdown to the next reset**. Quota and countdown always come from the same window, so they can never disagree.
|
|
128
|
-
|
|
129
|
-
- **ChatGPT / Codex** — decoded **locally** from `~/.codex/auth.json`, showing the real plan tier and expiry date (for example `ChatGPT · Plus | Expires 2026-09-16`). Zero network calls: the values come straight from OpenAI's own login token, never estimated. Not signed in → **Refresh failed** with a reauthorization hint. Binding, token refresh and the `openai-codex` model route belong to the companion plugin [dsh-chatgpt-subscription](https://github.com/songoao25) — this bar only reads the token, and never writes it back.
|
|
130
|
-
- **OpenCode Go** — reads quota from `opencode.ai/zen/go/v1/usage` using `OPENCODE_GO_API_KEY` (Settings → Models) or the opencode CLI login at `~/.local/share/opencode/auth.json`. Missing key → a "not configured" hint, not an error.
|
|
131
|
-
- **Zhipu** — GLM Coding Plan quota via `ZAI_CODING_CN_API_KEY` (fallback `ZAI_API_KEY`): plan tier plus the 5-hour and weekly windows (including the credit-based plans introduced on 2026-07-30).
|
|
132
|
-
- **Xiaomi MiMo Token Plan** — monthly Credits quota via `XIAOMI_TOKEN_PLAN_CN/SGP/AMS_API_KEY` per region (fallback `XIAOMI_API_KEY`): plan name plus the monthly window.
|
|
133
|
-
- **Command Code** — reads the official CLI quota endpoints using `COMMAND_CODE_API_KEY` or `CMD_API_KEY` from Settings → Models, the same environment variables, or the CLI login at `~/.commandcode/auth.json`: monthly credits plus 5-hour and weekly windows. Credits are displayed as credits, never as currency; unknown plan IDs do not get an invented monthly percentage.
|
|
134
|
-
- **MiniMax Token Plan** — 5-hour and weekly quota from `api.minimax.io` (Global, `MINIMAX_API_KEY`) or `api.minimaxi.com` (CN, `MINIMAX_CN_API_KEY`). Queries require a **Subscription Key**; a pay-as-you-go key is rejected by the server, and the bar says so instead of showing a number it cannot know. Several model buckets are aggregated by the tightest remaining quota.
|
|
135
|
-
|
|
136
|
-
Quota windows show **remaining** percent by default; the plugin settings can switch them to **used** under *Subscription window percentage*. Either way, the low-quota warning follows the same rule: remaining 20% or less.
|
|
137
|
-
|
|
138
|
-
In compact mode the bar prefers the shortest window (5-hour > weekly > monthly), because it refreshes fastest; if the 5-hour window is unavailable it falls back to weekly, then monthly.
|
|
139
|
-
|
|
140
|
-
### Billing mode (Together, Fireworks, AWS Bedrock, Cloudflare)
|
|
141
|
-
|
|
142
|
-
Shows **this month's real spend** from the provider's official billing API, for example `Together | This month $12.34` or `AWS Bedrock | This month $45.60 · Budget 46%`. Cloudflare also shows daily free-quota remaining and a UTC-midnight reset countdown — but **only when the API actually reports a free allowance**; otherwise it shows real usage alone rather than inventing a quota. Missing keys → a "not configured" hint.
|
|
143
|
-
|
|
144
|
-
## Spend tracking
|
|
145
|
-
|
|
146
|
-
Every `llm/stream` request is recorded (usage × unit price) and aggregated four ways: **this conversation** (including subagents), **today**, **last 30 days**, and **all time**.
|
|
147
|
-
|
|
148
|
-
Subagents bill to the same provider account, so their records are folded into the current session. The billed price is fixed the moment a response completes, so later price-table updates never rewrite history. Models with no known price keep their token counts but are excluded from money totals, and are never given an invented cost.
|
|
149
|
-
|
|
150
|
-
Nothing is lost on restart: the journal is synchronously confirmed before the UI counts a new charge, and if that write fails the bar says **Spend not saved** instead of quietly dropping the amount. Ledger details auto-archive into summaries and stats are computed incrementally, so refreshing stays fast even after months of use.
|
|
151
|
-
|
|
152
|
-
## Updating
|
|
102
|
+

|
|
153
103
|
|
|
154
|
-
|
|
104
|
+
**Custom text** — up to 64 characters, shown in the bar.
|
|
155
105
|
|
|
156
|
-
|
|
106
|
+

|
|
157
107
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
The copied command matches how the plugin was installed:
|
|
161
|
-
|
|
162
|
-
| Installed via | Command you get |
|
|
163
|
-
|---|---|
|
|
164
|
-
| npm (`dsh plugin add dsh-bottom-info-bar`) | `dsh plugin --profile <profile> add dsh-bottom-info-bar@latest` |
|
|
165
|
-
| GitHub address | `dsh plugin --profile <profile> add <the same address>` |
|
|
166
|
-
| `link:` — local checkout or the one-command script | `git -C <repo> fetch origin && git -C <repo> checkout main && git -C <repo> merge --ff-only origin/main && node <repo>/scripts/build.mjs` |
|
|
167
|
-
|
|
168
|
-
For a `link:` install the plugin reads your checkout's git state (read-only, no command is run) and composes that command from three facts:
|
|
169
|
-
|
|
170
|
-
- **It targets the repository's default branch, not the branch you happen to be on.** A checkout parked on a feature branch fast-forwards to nothing useful, so the command switches to the default branch first. Nothing is thrown away: if the working tree is dirty or the branch has local commits, git refuses by itself instead of overwriting anything.
|
|
171
|
-
- **It never uses `git pull`.** `pull` fails outright on a branch that was never pushed to a remote (`no such ref was fetched`), which is exactly what a local development branch looks like.
|
|
172
|
-
- **It rebuilds `lib/` at the end.** A `link:` install loads the built `lib/`, and your checkout may have `src/` changes that were never built — rebuilding keeps the running code in step with the checkout. This is the same build step `install.sh` runs.
|
|
173
|
-
|
|
174
|
-
The version shown in the tooltip is read from what is installed on disk and refreshes with the page, so an update clears the label without waiting for a restart. The host code itself takes effect on the next `dsh web` restart.
|
|
175
|
-
|
|
176
|
-
It is never applied automatically. The plugin only tells you a newer version exists; nothing on your machine changes until you run the command yourself.
|
|
108
|
+
**Billing data** — export the ledger as CSV or JSON, or clear it after confirmation. Settings and sign-in information stay untouched.
|
|
177
109
|
|
|
178
110
|
## Supported providers
|
|
179
111
|
|
|
180
|
-
The bar detects the provider from DSH's current model
|
|
112
|
+
The bar detects the provider from DSH's current model — no configuration. Set the key in DSH under **Settings → Models**.
|
|
181
113
|
|
|
182
|
-
### Balance
|
|
114
|
+
### Balance
|
|
183
115
|
|
|
184
|
-
| Provider | Display name | Credential
|
|
185
|
-
|
|
186
|
-
| deepseek / deepseek-official | DeepSeek | `DEEPSEEK_API_KEY` |
|
|
187
|
-
| openai | OpenAI | `OPENAI_API_KEY`
|
|
188
|
-
| moonshotai / moonshotai-cn / kimi-coding | Kimi | `MOONSHOT_API_KEY` (fallback `KIMI_API_KEY`) |
|
|
189
|
-
| openrouter | OpenRouter | `OPENROUTER_API_KEY` |
|
|
190
|
-
| stepfun | StepFun | `STEPFUN_API_KEY` |
|
|
191
|
-
| xiaomi | Xiaomi MiMo | `XIAOMI_API_KEY` |
|
|
116
|
+
| Provider | Display name | Credential / source |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| deepseek / deepseek-official | DeepSeek | `DEEPSEEK_API_KEY` |
|
|
119
|
+
| openai | OpenAI | `OPENAI_API_KEY` — estimated from your spending rate; there is no public balance API |
|
|
120
|
+
| moonshotai / moonshotai-cn / kimi-coding | Kimi | `MOONSHOT_API_KEY` (fallback `KIMI_API_KEY`) |
|
|
121
|
+
| openrouter | OpenRouter | `OPENROUTER_API_KEY` |
|
|
122
|
+
| stepfun | StepFun | `STEPFUN_API_KEY` |
|
|
123
|
+
| xiaomi | Xiaomi MiMo | `XIAOMI_API_KEY` |
|
|
192
124
|
|
|
193
|
-
### Subscription
|
|
125
|
+
### Subscription quota
|
|
194
126
|
|
|
195
|
-
| Provider | Display name |
|
|
127
|
+
| Provider | Display name | Credential / source |
|
|
196
128
|
|---|---|---|
|
|
197
|
-
| codex / chatgpt / openai-codex | ChatGPT / Codex | `~/.codex/auth.json` (read-only
|
|
198
|
-
| opencode-go / opencode | OpenCode Go | `OPENCODE_GO_API_KEY` or the opencode
|
|
129
|
+
| codex / chatgpt / openai-codex | ChatGPT / Codex | `~/.codex/auth.json` (read-only, decoded locally) |
|
|
130
|
+
| opencode-go / opencode | OpenCode Go | `OPENCODE_GO_API_KEY` or the opencode CLI login |
|
|
199
131
|
| zai / zai-coding-cn | Zhipu | `ZAI_CODING_CN_API_KEY` (fallback `ZAI_API_KEY`) |
|
|
200
132
|
| xiaomi-token-plan-cn / -sgp / -ams | Xiaomi MiMo | `XIAOMI_TOKEN_PLAN_CN/SGP/AMS_API_KEY` (fallback `XIAOMI_API_KEY`) |
|
|
201
133
|
| command / command-code | Command Code | `COMMAND_CODE_API_KEY` or `CMD_API_KEY`, or `~/.commandcode/auth.json` |
|
|
202
|
-
| minimax / minimax-cn | MiniMax | `MINIMAX_API_KEY` (Global) / `MINIMAX_CN_API_KEY` (CN) |
|
|
203
|
-
|
|
204
|
-
### Cloud-billing (real monthly bill)
|
|
205
|
-
|
|
206
|
-
| Provider | Display name | Credential | Source |
|
|
207
|
-
|---|---|---|---|
|
|
208
|
-
| together | Together | `TOGETHER_API_KEY` | Official Usage API (month spend) |
|
|
209
|
-
| fireworks | Fireworks | `FIREWORKS_API_KEY` | Official Billing API (period spend) |
|
|
210
|
-
| amazon-bedrock | AWS Bedrock | `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` | Cost Explorer + Budgets (spend + budget %) |
|
|
211
|
-
| cloudflare-ai-gateway / cloudflare-workers-ai | Cloudflare | `CLOUDFLARE_API_KEY` + `CLOUDFLARE_ACCOUNT_ID` (token needs Billing read) | Billable Usage API (real usage) |
|
|
212
|
-
|
|
213
|
-
**Anything else** shows a **Not supported** hint instead of borrowing another provider's numbers.
|
|
214
|
-
|
|
215
|
-
### About the model name `DeepSeek-V41-Flash`
|
|
216
|
-
|
|
217
|
-
If you have seen `V41-Flash` and read it as a typo, here is the whole story:
|
|
218
|
-
|
|
219
|
-
- **It is DeepSeek V4.1 Flash.** That is the model actually serving your requests, priced at V4.1 Flash's official peak/off-peak rates.
|
|
220
|
-
- **`DeepSeek-V41-Flash` is how DSH's own model catalogue spells it** — model id `deepseek-flash`, display name written without the dot. The bar mirrors DSH's model switcher, so the odd spelling is DSH's, not this plugin's.
|
|
221
|
-
- **It has no effect on anything you do.** Same model, same pricing, same quota. The bar shows whatever DSH shows, so if DSH ever renames it, the bar follows automatically with no update needed.
|
|
222
|
-
|
|
223
|
-
`DeepSeek-V4-Flash` is a **different, older** model id — not the same model.
|
|
134
|
+
| minimax / minimax-cn | MiniMax | `MINIMAX_API_KEY` (Global) / `MINIMAX_CN_API_KEY` (CN) — must be a **Subscription Key** |
|
|
224
135
|
|
|
225
|
-
|
|
136
|
+
### Cloud billing
|
|
226
137
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
138
|
+
| Provider | Display name | Credential / source |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| together | Together | `TOGETHER_API_KEY` — official Usage API, this month's spend |
|
|
141
|
+
| fireworks | Fireworks | `FIREWORKS_API_KEY` — official Billing API, this period's spend |
|
|
142
|
+
| amazon-bedrock | AWS Bedrock | `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` — Cost Explorer + Budgets |
|
|
143
|
+
| cloudflare-ai-gateway / cloudflare-workers-ai | Cloudflare | `CLOUDFLARE_API_KEY` + `CLOUDFLARE_ACCOUNT_ID` (token needs Billing read) |
|
|
230
144
|
|
|
231
|
-
|
|
145
|
+
Anything else shows a **Not supported** hint instead of borrowing another provider's numbers.
|
|
232
146
|
|
|
233
|
-
|
|
234
|
-
~/.dsh/dsh-bottom-info-bar/
|
|
235
|
-
├── usage-records.json # the complete, human-readable ledger
|
|
236
|
-
├── usage-records.journal.jsonl # recovery journal — do not edit by hand
|
|
237
|
-
└── usage-records.json.bak # previous complete snapshot, for recovery
|
|
238
|
-
```
|
|
147
|
+
## Spend tracking
|
|
239
148
|
|
|
240
|
-
|
|
241
|
-
- **Relocate** — set `DSH_BOTTOM_INFO_BAR_DATA_DIR` to move the whole directory (external drive, synced folder, …).
|
|
242
|
-
- **Inspect or migrate** — open `usage-records.json` in any editor. To move machines, copy the directory while DSH is closed.
|
|
243
|
-
- **Contents** — one entry per model response (`id / ts / model / provider / sessionId / input / cacheRead / cacheWrite / output / currency / cost / status`), where `status` is `completed` or `interrupted`. **No conversation content, prompts, or API keys are ever stored.**
|
|
244
|
-
- **Retention** — there is no silent entry cap; keep the directory in your normal backups if you need history beyond this machine.
|
|
245
|
-
- **Manage** — **Plugins → bottom-info-bar → Billing data** can export CSV/JSON or clear the plugin's records after confirmation. Settings, sign-in information and pricing data are untouched, and **uninstalling does not delete your data**.
|
|
149
|
+
Every model response is recorded (usage × unit price) and aggregated four ways: **this session** (including subagents), **today**, **last 30 days** and **all time**. The price is locked the moment a response completes, so later price-table updates never rewrite history. A model with no known price keeps its token counts but is excluded from money totals — no amount is ever invented. Records are written to disk before they count, so a restart loses nothing; if a write fails, the bar says **Spend not saved**.
|
|
246
150
|
|
|
247
|
-
|
|
151
|
+
## Updating
|
|
248
152
|
|
|
249
|
-
|
|
153
|
+
Once per DSH startup the plugin checks npm for a newer version. When one exists, a red **Update available** label appears in the bar: click it to copy the update command, run it in a terminal, then restart `dsh web`.
|
|
250
154
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
155
|
+
| Installed via | Command |
|
|
156
|
+
|---|---|
|
|
157
|
+
| npm | `dsh plugin --profile <profile> add dsh-bottom-info-bar@latest` |
|
|
158
|
+
| GitHub address | `dsh plugin --profile <profile> add <the same address>` |
|
|
159
|
+
| local checkout or install script | `git -C <repo> fetch origin && git -C <repo> checkout main && git -C <repo> merge --ff-only origin/main && node <repo>/scripts/build.mjs` |
|
|
256
160
|
|
|
257
|
-
|
|
161
|
+
Nothing updates by itself — the bar only tells you that a newer version exists.
|
|
258
162
|
|
|
259
|
-
|
|
163
|
+
## Privacy and security
|
|
260
164
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
| Bar does not appear after refreshing the page | **Restart** `dsh web` — the host composes plugins at startup |
|
|
266
|
-
| Balance says **Not configured: DEEPSEEK_API_KEY** | Add the key under Settings → Models |
|
|
267
|
-
| Balance shows **Refresh failed** but a figure is still visible | Transient network or key issue; it retries automatically after 60 s and keeps the last known value. **Hover the warning** for details. |
|
|
268
|
-
| OpenCode Go shows **Refresh failed** with a missing-credentials tooltip | Add `OPENCODE_GO_API_KEY` under Settings → Models, or log in with the opencode CLI |
|
|
269
|
-
| Command Code shows **Refresh failed** or no quota | Add `COMMAND_CODE_API_KEY` or `CMD_API_KEY` under Settings → Models, or log in with the Command Code CLI. The plugin only reads `~/.commandcode/auth.json`; it never writes the login file. |
|
|
270
|
-
| ChatGPT shows **Not connected** | Install the companion plugin `dsh-chatgpt-subscription` and sign in |
|
|
271
|
-
| ChatGPT plan or expiry is blank | Sign in again or rebind. If the token genuinely lacks those fields the bar leaves them empty rather than guessing. |
|
|
272
|
-
| How do I update? | Click the red **Update available** label to copy the command, then restart `dsh web`. See [Updating](#updating). |
|
|
273
|
-
| I updated and the label is still there | The version is read from the checkout on disk: refresh the page. If the label persists, check that the update actually changed the installed copy (`link:` installs must rebuild `lib/`). |
|
|
274
|
-
| Install fails with **declares no bundle** (中文界面:「这个包没有声明组合包」) | DSH installed a repository whose root was not a package — that was this repository before the package moved to the root. Use the package name `dsh-bottom-info-bar` in the plugin page, or the GitHub address on a release that includes the fix. |
|
|
275
|
-
| The model is shown as `V41-Flash` | Not a typo — that is DSH's spelling of **V4.1 Flash**. See [About the model name](#about-the-model-name-deepseek-v41-flash). |
|
|
276
|
-
| Compact mode shows a different quota window than full mode | Intentional: compact prefers the shortest window (5-hour > weekly > monthly). Quota and countdown still come from the same window. |
|
|
277
|
-
| Why is the model's reasoning not shown? | DSH does not render internal reasoning — a DSH interface limitation, not this plugin |
|
|
278
|
-
| I want the original stats row back | Uninstall and restart |
|
|
165
|
+
- **Read-only credentials.** API keys stay in DSH; `~/.codex/auth.json` and `~/.commandcode/auth.json` are read locally and never written back. This plugin does not bind or refresh accounts.
|
|
166
|
+
- **No conversation content.** The ledger stores tokens, model, provider, currency and cost — never prompts, messages or keys.
|
|
167
|
+
- **Local only.** Data lives in `~/.dsh/dsh-bottom-info-bar/` (directory `0700`, files `0600`). Nothing leaves your machine except the provider API requests the bar itself makes.
|
|
168
|
+
- **Uninstalling keeps your data.** Export or clear it from the plugin page (Billing data) if you want a clean slate.
|
|
279
169
|
|
|
280
170
|
## Development
|
|
281
171
|
|
|
282
|
-
-
|
|
283
|
-
-
|
|
284
|
-
-
|
|
285
|
-
- **Release history** — [CHANGELOG.md](CHANGELOG.md)
|
|
286
|
-
- **Contributing** — [CONTRIBUTING.md](CONTRIBUTING.md); day-to-day workflow and the release process: [docs/WORKFLOW.md](docs/WORKFLOW.md)
|
|
287
|
-
|
|
288
|
-
Every rule this project learned the hard way is enforced by CI rather than by convention — the test suite, the source guards and the release-chain contract all run on every pull request. See [AGENTS.md](AGENTS.md) for the constraints an agent must respect.
|
|
172
|
+
- Build: `npm run build`
|
|
173
|
+
- Test: `node tests/run-all.mjs`
|
|
174
|
+
- Contributing: [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
289
175
|
|
|
290
176
|
## License
|
|
291
177
|
|
|
292
178
|
[MIT](LICENSE) © 2026 songoao25
|
|
179
|
+
|
|
180
|
+
💬 Questions or ideas? Join our WeChat group **DeepThinking** — [QR code](assets/wechat-group.png).
|
package/README.zh-CN.md
CHANGED
|
@@ -1,291 +1,182 @@
|
|
|
1
1
|
# 底部信息栏
|
|
2
2
|
|
|
3
|
-
<img src="assets/wechat-group.png" width="118" align="right" alt="微信群 DeepThinking —— 扫码加入">
|
|
4
|
-
|
|
5
3
|
[**English**](README.md) | **中文**
|
|
6
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/dsh-bottom-info-bar)
|
|
7
6
|
[](https://github.com/songoao25/dsh-bottom-info-bar/blob/main/LICENSE)
|
|
8
|
-
[](https://github.com/songoao25/dsh-bottom-info-bar/releases)
|
|
9
|
-
[](https://github.com/songoao25/dsh-bottom-info-bar)
|
|
10
|
-
[](https://github.com/songoao25/dsh-bottom-info-bar/actions)
|
|
11
7
|
|
|
12
|
-
DeepSeek Harness
|
|
8
|
+
DeepSeek Harness 插件:把输入框下方那行统计栏换成一行信息栏——服务商与模型、真实余额或订阅额度、高峰/空闲定价,以及本会话已经花了多少。
|
|
13
9
|
|
|
14
|
-
|
|
10
|
+

|
|
15
11
|
|
|
16
|
-
|
|
17
|
-

|
|
12
|
+
## 它显示什么
|
|
18
13
|
|
|
19
|
-
|
|
14
|
+
| 分组 | 内容 |
|
|
15
|
+
|---|---|
|
|
16
|
+
| 服务商 | 当前会话的服务商与具体模型,跟随 DSH 模型切换器 |
|
|
17
|
+
| 原生统计(保留) | 轮次与步数、模型耗时、工具耗时、缓存命中率、输入/输出 token、上下文占用 |
|
|
18
|
+
| 金额 | 真实余额、订阅额度窗口,或本月云账单 |
|
|
19
|
+
| 定价 | 高峰价与空闲价、当前时段、下次价格切换倒计时 |
|
|
20
|
+
| 花费 | 本会话(含子代理)、今天、近 30 天、累计 |
|
|
21
|
+
| 其它 | 主时间、世界时间、自定义文字 |
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
点击信息栏可在**完整**与**简洁**之间切换;配色跟随 DSH 的深色或浅色主题。
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+

|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
- **真实数据,且诚实标注** —— 余额、额度、套餐、账单全部来自各家官方 API。唯一拿不到接口的那一项(OpenAI 无公开余额接口)按你的消耗速度推算,并在界面上标出**(估算)**。
|
|
27
|
-
- **与 DSH 显示完全一致** —— 服务商与模型名取自 DSH 模型切换器,新模型自动跟随,不用改插件。
|
|
28
|
-
- **高峰 / 空闲价** —— 两个价格并列,并给出切换倒计时(周末全天空闲价)。
|
|
29
|
-
- **诚实的花费记账** —— 本对话(含子代理)、今天、近 30 天、累计,落盘保存,重启不丢。
|
|
30
|
-
- **原生观感** —— 顶替原生统计栏而不是并列重复;点击切换完整/简洁,字段与配色在**插件页**里自选。
|
|
27
|
+
## 三种计费模式
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
信息栏跟随当前会话,按服务商自动选择模式。三种模式互斥,无需手动切换。
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
- **[pnpm](https://pnpm.io/)** —— `dsh plugin` 用它管理 profile 里的包。
|
|
36
|
-
- 你所使用服务商的 API Key,在 DSH 的 **设置 → 模型** 里配置。
|
|
31
|
+
### 余额制
|
|
37
32
|
|
|
38
|
-
|
|
33
|
+
显示服务商官方接口返回的真实余额。信息栏打开、页面刷新或切换服务商时立即重查,之后每 60 秒轮询一次;刷新失败时保留上一次的数值。余额低于 20(按账户币种)时,金额与**低**标记转红。
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
dsh plugin --profile web add dsh-bottom-info-bar
|
|
42
|
-
```
|
|
35
|
+

|
|
43
36
|
|
|
44
|
-
|
|
37
|
+
### 订阅额度制
|
|
45
38
|
|
|
46
|
-
|
|
39
|
+
显示各窗口剩余额度(5 小时 / 周 / 月)与距下次重置的倒计时——两者始终取自同一个窗口,不会互相矛盾。窗口默认按**剩余**显示百分比,可在设置里切换为**已用**;低额度告警始终按剩余 ≤ 20% 判定。简洁模式优先显示时长最短的可用窗口(5 小时 → 周 → 月)。
|
|
47
40
|
|
|
48
|
-
|
|
41
|
+
### 云账单制
|
|
49
42
|
|
|
50
|
-
|
|
43
|
+
显示官方账单接口返回的本月真实花费,例如 `Together | 本月 $12.34`、`AWS Bedrock | 本月 $45.60 · 预算 46%`。Cloudflare 还会在接口确实返回免费额度时,显示每日免费额度剩余与 UTC 零点重置倒计时。
|
|
51
44
|
|
|
52
|
-
|
|
53
|
-
<summary>其它安装方式(以及该怎么选)</summary>
|
|
45
|
+
## 深色模式
|
|
54
46
|
|
|
55
|
-
|
|
47
|
+
配色跟随 DSH 主题,无需单独设置。
|
|
56
48
|
|
|
57
|
-
|
|
49
|
+

|
|
58
50
|
|
|
59
|
-
|
|
60
|
-
dsh plugin --profile web add https://github.com/songoao25/dsh-bottom-info-bar
|
|
61
|
-
```
|
|
51
|
+
## 安装
|
|
62
52
|
|
|
63
|
-
|
|
53
|
+
需要带 Web 界面的 DeepSeek Harness(`dsh web`)与 pnpm。
|
|
64
54
|
|
|
65
|
-
|
|
55
|
+
**npm 安装**(推荐,装的是已发布版本):
|
|
66
56
|
|
|
67
57
|
```bash
|
|
68
|
-
|
|
69
|
-
dsh plugin --profile web add /path/to/dsh-bottom-info-bar
|
|
58
|
+
dsh plugin --profile web add dsh-bottom-info-bar
|
|
70
59
|
```
|
|
71
60
|
|
|
72
|
-
|
|
61
|
+
**从 GitHub 仓库安装** —— [github.com/songoao25/dsh-bottom-info-bar](https://github.com/songoao25/dsh-bottom-info-bar)(跟随默认分支;`lib/` 已入库,安装时不跑构建):
|
|
73
62
|
|
|
74
|
-
|
|
63
|
+
```bash
|
|
64
|
+
dsh plugin --profile web add https://github.com/songoao25/dsh-bottom-info-bar
|
|
65
|
+
```
|
|
75
66
|
|
|
76
|
-
|
|
67
|
+
**本地一键脚本**(clone、构建、安装一步完成):
|
|
77
68
|
|
|
78
69
|
```bash
|
|
79
70
|
git clone https://github.com/songoao25/dsh-bottom-info-bar.git
|
|
80
71
|
cd dsh-bottom-info-bar
|
|
81
|
-
./install.sh
|
|
72
|
+
./install.sh
|
|
82
73
|
```
|
|
83
74
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
详细步骤与故障排查见 [docs/INSTALL.md](docs/INSTALL.md)。
|
|
87
|
-
</details>
|
|
88
|
-
|
|
89
|
-
## 插件配置
|
|
90
|
-
|
|
91
|
-
配置都在**插件页**(插件 → bottom-info-bar)里,不在 DSH 的全局设置里:字段开关、配色、自定义文字、账单数据都在这里,改完自动保存。
|
|
92
|
-
|
|
93
|
-

|
|
94
|
-
|
|
95
|
-
<sub>**信息栏**里有搜索框、两个字段分组、**恢复默认**与**自定义文字**;在搜索框输入会自动展开两个分组。**账单数据**可导出 CSV/JSON 或清除记录。</sub>
|
|
96
|
-
|
|
97
|
-

|
|
98
|
-
|
|
99
|
-
<sub>**原生信息**——DSH 原生统计栏本来就有的六个字段。每行都有自己的开关与颜色,默认配色跟随 DSH。</sub>
|
|
100
|
-
|
|
101
|
-

|
|
102
|
-
|
|
103
|
-
<sub>**插件信息**——信息栏新增的全部内容:服务商与模型、订阅、花费、余额、高峰/空闲价格与额度。关掉哪个字段,信息栏就不再显示它。</sub>
|
|
104
|
-
|
|
105
|
-
## 它不做什么
|
|
106
|
-
|
|
107
|
-
边界也是设计的一部分,值得说清楚:
|
|
108
|
-
|
|
109
|
-
- **绝不把猜测当成事实。** 每个数字要么来自服务商接口——要么,在唯一没有接口的那一项(OpenAI 无公开余额接口)上,按你的消耗速度推算,并在界面上标注**(估算)**。拿不到数字时,信息栏会明说,而不是填一个猜测上去。
|
|
110
|
-
- **绝不自动更新自己。** 版本提醒只负责告诉你"有新版本"并准备好命令;在你亲手运行之前,机器上不会有任何改动。
|
|
111
|
-
- **绝不绑定你的 ChatGPT 账号。** 那是配套插件 [dsh-chatgpt-subscription](https://github.com/songoao25) 的职责。本插件只读本地令牌。
|
|
112
|
-
- **绝不保存对话内容。** 账本记录的是 token、模型、服务商与花费——没有提示词、没有消息、没有 API Key。
|
|
113
|
-
- **不适用于 Web 之外的界面。** 终端或 headless 下没有对应的信息栏。
|
|
114
|
-
|
|
115
|
-
## 三种计费模式
|
|
116
|
-
|
|
117
|
-
信息栏跟随 DSH 的当前会话,按服务商自动决定显示内容,**无需手动切换模式**。
|
|
118
|
-
|
|
119
|
-
### 余额制(DeepSeek / Kimi / OpenRouter / StepFun / 小米 MiMo / OpenAI 参考价)
|
|
120
|
-
|
|
121
|
-
显示服务商官方接口返回的**真实余额**。信息栏打开、刷新、或切换服务商时会立即重新查询,之后每 60 秒轮询一次。查询失败时保留上一次的快照,**这一行永远不会变空白**。
|
|
122
|
-
|
|
123
|
-
余额低于 ¥20 时,金额与「低」标记转为红色。
|
|
75
|
+
然后**重启 `dsh web`** —— 插件在宿主启动时组合,刷新页面不够。重启后插件出现在插件列表并处于启用状态:
|
|
124
76
|
|
|
125
|
-
|
|
77
|
+

|
|
126
78
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
- **ChatGPT / Codex** —— 在**本机离线解析** `~/.codex/auth.json`,显示真实套餐与到期时间(例如 `ChatGPT · Plus | Expires 2026-09-16`)。**零网络请求**:数值直接来自 OpenAI 自己的登录令牌,不做估算。未登录 → 显示**刷新失败**并提示重新授权。令牌的**绑定与续期**由配套插件 [dsh-chatgpt-subscription](https://github.com/songoao25) 负责——本插件**只读**令牌,绝不写回。
|
|
130
|
-
- **OpenCode Go** —— 通过 `OPENCODE_GO_API_KEY`(设置 → 模型)或 opencode CLI 的登录(`~/.local/share/opencode/auth.json`)读取 `opencode.ai/zen/go/v1/usage` 的额度。未配置 → 显示「未配置」提示而非报错。
|
|
131
|
-
- **智谱** —— 通过 `ZAI_CODING_CN_API_KEY`(回退 `ZAI_API_KEY`)读取 GLM Coding Plan 额度:套餐档位 + 5 小时窗口 + 周窗口(含 2026-07-30 起的积分制套餐)。
|
|
132
|
-
- **小米 MiMo Token Plan** —— 通过 `XIAOMI_TOKEN_PLAN_CN/SGP/AMS_API_KEY` 按区域读取月度 Credits 额度(回退 `XIAOMI_API_KEY`):套餐名 + 月度窗口。
|
|
133
|
-
- **Command Code** —— 通过 `COMMAND_CODE_API_KEY` / `CMD_API_KEY`(设置 → 模型)、同名环境变量或 CLI 登录文件 `~/.commandcode/auth.json` 读取官方额度端点:月度 credits + 5 小时窗口 + 周窗口。credits 按积分显示,绝不折算成货币;未知套餐不会编造月度百分比。
|
|
134
|
-
- **MiniMax Token Plan** —— 通过 `MINIMAX_API_KEY`(Global,`api.minimax.io`)或 `MINIMAX_CN_API_KEY`(国内,`api.minimaxi.com`)读取 5 小时与周窗口额度。查询必须使用 **Subscription Key**;按量 API Key 会被服务端拒绝,信息栏会直接说明原因,而不是显示一个它无从得知的数字。多个模型桶按**最紧的剩余额度**聚合。
|
|
135
|
-
|
|
136
|
-
额度窗口默认显示**剩余**百分比;插件设置里的「订阅窗口百分比方向」可切换为**已用**。无论选哪种,低额度告警始终按「剩余 20%」判定。
|
|
137
|
-
|
|
138
|
-
简洁模式下优先显示**时长最短的窗口**(5 小时 > 周 > 月),因为它刷新最快;5 小时窗口不可用时依次回退到周、月。
|
|
139
|
-
|
|
140
|
-
### 账单制(Together / Fireworks / AWS Bedrock / Cloudflare)
|
|
141
|
-
|
|
142
|
-
显示官方账单接口返回的**本月真实花费**,例如 `Together | 本月 $12.34`、`AWS Bedrock | 本月 $45.60 · 预算 46%`。Cloudflare 还会显示每日免费额度剩余与 UTC 零点重置倒计时——**但仅在接口确实返回免费额度时才显示**;否则只显示真实用量,**绝不编造额度**。未配置密钥 → 显示「未配置」提示。
|
|
79
|
+
详细步骤与故障排查见 [docs/INSTALL.md](docs/INSTALL.md)。
|
|
143
80
|
|
|
144
|
-
##
|
|
81
|
+
## 设置
|
|
145
82
|
|
|
146
|
-
|
|
83
|
+
配置都在插件页(**插件 → bottom-info-bar**),改完自动保存。
|
|
147
84
|
|
|
148
|
-
|
|
85
|
+

|
|
149
86
|
|
|
150
|
-
|
|
87
|
+
**字段与配色** —— 每个字段一个开关、一个颜色,分两组。关掉哪个字段,信息栏就不再显示它。
|
|
151
88
|
|
|
152
|
-
|
|
89
|
+
- **原生信息** —— DSH 原生统计栏本来就有的字段。
|
|
90
|
+
- **插件信息** —— 信息栏新增的全部内容:服务商与模型、订阅、花费、余额、定价与额度。
|
|
153
91
|
|
|
154
|
-
|
|
92
|
+

|
|
93
|
+

|
|
155
94
|
|
|
156
|
-
|
|
95
|
+
**订阅窗口百分比方向** —— 按**剩余**(默认)或**已用**显示额度窗口。低额度告警始终按剩余 ≤ 20% 判定。
|
|
157
96
|
|
|
158
|
-
|
|
97
|
+

|
|
98
|
+

|
|
159
99
|
|
|
160
|
-
|
|
100
|
+
**时间与日期** —— 主时间与世界时间的时区,以及年 / 月 / 日 / 时 / 分 / 秒的显示格式。
|
|
161
101
|
|
|
162
|
-
|
|
163
|
-
|---|---|
|
|
164
|
-
| npm(`dsh plugin add dsh-bottom-info-bar`) | `dsh plugin --profile <profile> add dsh-bottom-info-bar@latest` |
|
|
165
|
-
| GitHub 地址 | `dsh plugin --profile <profile> add <同一个地址>` |
|
|
166
|
-
| `link:`(本地代码 / 一键脚本) | `git -C <仓库> fetch origin && git -C <仓库> checkout main && git -C <仓库> merge --ff-only origin/main && node <仓库>/scripts/build.mjs` |
|
|
102
|
+

|
|
167
103
|
|
|
168
|
-
|
|
104
|
+
**自定义文字** —— 最多 64 个字符,显示在信息栏中。
|
|
169
105
|
|
|
170
|
-
-
|
|
171
|
-
- **不会用 `git pull`。** 分支从没推到远端时 `pull` 会直接失败(`no such ref was fetched`)——而本地开发分支本来就是这种状态。
|
|
172
|
-
- **末尾会重新构建 `lib/`。** `link:` 安装加载的是构建产物 `lib/`,而你的副本可能改过 `src/` 却没重建——重建一次才保证跑起来的代码和副本一致。这和 `install.sh` 里的构建步骤是同一件事。
|
|
106
|
+

|
|
173
107
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
**它绝不会自动执行更新。** 插件只负责告诉你「有新版本」,在你亲手运行命令之前,机器上不会有任何改动。
|
|
108
|
+
**账单数据** —— 导出 CSV / JSON,或确认后清除记录。设置与登录信息不受影响。
|
|
177
109
|
|
|
178
110
|
## 支持的服务商
|
|
179
111
|
|
|
180
|
-
信息栏从 DSH
|
|
112
|
+
信息栏从 DSH 当前模型识别服务商,零配置;密钥在 DSH 的**设置 → 模型**里填写。
|
|
181
113
|
|
|
182
114
|
### 余额制
|
|
183
115
|
|
|
184
|
-
| 服务商 | 显示名 | 凭据
|
|
185
|
-
|
|
186
|
-
| deepseek / deepseek-official | DeepSeek | `DEEPSEEK_API_KEY` |
|
|
187
|
-
| openai | OpenAI | `OPENAI_API_KEY`
|
|
188
|
-
| moonshotai / moonshotai-cn / kimi-coding | Kimi | `MOONSHOT_API_KEY`(回退 `KIMI_API_KEY`) |
|
|
189
|
-
| openrouter | OpenRouter | `OPENROUTER_API_KEY` |
|
|
190
|
-
| stepfun | StepFun | `STEPFUN_API_KEY` |
|
|
191
|
-
| xiaomi | 小米 MiMo | `XIAOMI_API_KEY` |
|
|
116
|
+
| 服务商 | 显示名 | 凭据 / 数据来源 |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| deepseek / deepseek-official | DeepSeek | `DEEPSEEK_API_KEY` |
|
|
119
|
+
| openai | OpenAI | `OPENAI_API_KEY` —— 按消耗速度估算;官方没有公开余额接口 |
|
|
120
|
+
| moonshotai / moonshotai-cn / kimi-coding | Kimi | `MOONSHOT_API_KEY`(回退 `KIMI_API_KEY`) |
|
|
121
|
+
| openrouter | OpenRouter | `OPENROUTER_API_KEY` |
|
|
122
|
+
| stepfun | StepFun | `STEPFUN_API_KEY` |
|
|
123
|
+
| xiaomi | 小米 MiMo | `XIAOMI_API_KEY` |
|
|
192
124
|
|
|
193
|
-
###
|
|
125
|
+
### 订阅额度制
|
|
194
126
|
|
|
195
|
-
| 服务商 | 显示名 |
|
|
127
|
+
| 服务商 | 显示名 | 凭据 / 数据来源 |
|
|
196
128
|
|---|---|---|
|
|
197
129
|
| codex / chatgpt / openai-codex | ChatGPT / Codex | `~/.codex/auth.json`(只读,本机解析) |
|
|
198
|
-
| opencode-go / opencode | OpenCode Go | `OPENCODE_GO_API_KEY` 或 opencode
|
|
130
|
+
| opencode-go / opencode | OpenCode Go | `OPENCODE_GO_API_KEY` 或 opencode CLI 登录 |
|
|
199
131
|
| zai / zai-coding-cn | 智谱 | `ZAI_CODING_CN_API_KEY`(回退 `ZAI_API_KEY`) |
|
|
200
132
|
| xiaomi-token-plan-cn / -sgp / -ams | 小米 MiMo | `XIAOMI_TOKEN_PLAN_CN/SGP/AMS_API_KEY`(回退 `XIAOMI_API_KEY`) |
|
|
201
|
-
| command / command-code | Command Code | `COMMAND_CODE_API_KEY`
|
|
202
|
-
| minimax / minimax-cn | MiniMax | `MINIMAX_API_KEY`(Global)/ `MINIMAX_CN_API_KEY
|
|
203
|
-
|
|
204
|
-
### 账单制(本月真实账单)
|
|
205
|
-
|
|
206
|
-
| 服务商 | 显示名 | 凭据 | 数据来源 |
|
|
207
|
-
|---|---|---|---|
|
|
208
|
-
| together | Together | `TOGETHER_API_KEY` | 官方用量接口(本月花费) |
|
|
209
|
-
| fireworks | Fireworks | `FIREWORKS_API_KEY` | 官方账单接口(周期花费) |
|
|
210
|
-
| amazon-bedrock | AWS Bedrock | `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` | Cost Explorer + Budgets(花费 + 预算占比) |
|
|
211
|
-
| cloudflare-ai-gateway / cloudflare-workers-ai | Cloudflare | `CLOUDFLARE_API_KEY` + `CLOUDFLARE_ACCOUNT_ID`(令牌需 Billing 读权限) | 计费用量接口(真实用量) |
|
|
212
|
-
|
|
213
|
-
**不在列表中的服务商**会显示**不支持**提示,而不是拿别的服务商的数据顶上。
|
|
214
|
-
|
|
215
|
-
### 关于模型名 V41-Flash
|
|
216
|
-
|
|
217
|
-
如果你看到 `V41-Flash` 并以为写错了,这里是完整的来龙去脉:
|
|
218
|
-
|
|
219
|
-
- **它就是 DeepSeek V4.1 Flash。** 真正在为你提供服务的模型,按 V4.1 Flash 官方公布的高峰/空闲价计费。
|
|
220
|
-
- **`DeepSeek-V41-Flash` 是 DSH 自己模型目录里的写法**——模型 id 为 `deepseek-flash`,显示名少了那个点。信息栏刻意与 DSH 的模型切换器保持一致,所以这个写法来自 DSH,不是本插件。
|
|
221
|
-
- **它对你的使用没有任何影响。** 同一个模型、同样的价格、同样的额度。信息栏显示的就是 DSH 显示的内容;DSH 哪天改名了,信息栏会自动跟着变,**不需要更新插件**。
|
|
133
|
+
| command / command-code | Command Code | `COMMAND_CODE_API_KEY` 或 `CMD_API_KEY`,或 `~/.commandcode/auth.json` |
|
|
134
|
+
| minimax / minimax-cn | MiniMax | `MINIMAX_API_KEY`(Global)/ `MINIMAX_CN_API_KEY`(国内)—— 必须是 **Subscription Key** |
|
|
222
135
|
|
|
223
|
-
|
|
136
|
+
### 云账单制
|
|
224
137
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
插件记录的一切都放在它自己的目录里,与其它插件和 DSH 配置完全隔离:
|
|
138
|
+
| 服务商 | 显示名 | 凭据 / 数据来源 |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| together | Together | `TOGETHER_API_KEY` —— 官方 Usage API,本月花费 |
|
|
141
|
+
| fireworks | Fireworks | `FIREWORKS_API_KEY` —— 官方 Billing API,本周期花费 |
|
|
142
|
+
| amazon-bedrock | AWS Bedrock | `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` —— Cost Explorer + Budgets |
|
|
143
|
+
| cloudflare-ai-gateway / cloudflare-workers-ai | Cloudflare | `CLOUDFLARE_API_KEY` + `CLOUDFLARE_ACCOUNT_ID`(令牌需 Billing 读权限) |
|
|
232
144
|
|
|
233
|
-
|
|
234
|
-
~/.dsh/dsh-bottom-info-bar/
|
|
235
|
-
├── usage-records.json # 完整账本,人类可读
|
|
236
|
-
├── usage-records.journal.jsonl # 恢复用流水,请勿手改
|
|
237
|
-
└── usage-records.json.bak # 上一份完整快照,用于恢复
|
|
238
|
-
```
|
|
145
|
+
不在列表中的服务商显示**未适配**提示,绝不借用别的服务商的数据。
|
|
239
146
|
|
|
240
|
-
|
|
241
|
-
- **换位置** —— 设置环境变量 `DSH_BOTTOM_INFO_BAR_DATA_DIR` 可整体迁移(外置硬盘、同步盘等)。
|
|
242
|
-
- **查看与迁移** —— 用任意编辑器打开 `usage-records.json`。换电脑时,在 DSH 关闭状态下整目录拷贝即可。
|
|
243
|
-
- **内容** —— 每条模型响应一条记录(`id / ts / model / provider / sessionId / input / cacheRead / cacheWrite / output / currency / cost / status`),`status` 为 `completed` 或 `interrupted`。**绝不保存对话内容、提示词或 API Key。**
|
|
244
|
-
- **保留** —— 没有静默的条数上限;若需要跨机器长期留存,请把该目录纳入你的常规备份。
|
|
245
|
-
- **管理** —— **插件页 → 账单数据**可导出 CSV/JSON,或在确认后清除插件记录。设置、登录信息与价目数据不受影响;**卸载插件也不会删除你的数据**。
|
|
147
|
+
## 花费记账
|
|
246
148
|
|
|
247
|
-
|
|
149
|
+
每次模型响应都会记录一条(用量 × 单价),并按四个口径聚合:**本会话**(含子代理)、**今天**、**近 30 天**、**累计**。单价在响应完成的那一刻锁定,之后的价目表更新不会改写历史金额。价目表里没有的模型保留 token 用量但不计入金额,绝不编造金额。记录先落盘再计入,重启不丢;写入失败时信息栏显示**账单未保存**。
|
|
248
150
|
|
|
249
|
-
##
|
|
151
|
+
## 更新
|
|
250
152
|
|
|
251
|
-
|
|
252
|
-
cd dsh-bottom-info-bar
|
|
253
|
-
./uninstall.sh
|
|
254
|
-
# 或手动:dsh plugin --profile web remove dsh-bottom-info-bar
|
|
255
|
-
```
|
|
153
|
+
插件在一次完整的 DSH 启动后检查一次 npm 上的新版本。有新版时,信息栏出现红色**新版本提醒**标签:点击即可复制更新命令,到终端执行后重启 `dsh web`。
|
|
256
154
|
|
|
257
|
-
|
|
155
|
+
| 安装方式 | 命令 |
|
|
156
|
+
|---|---|
|
|
157
|
+
| npm | `dsh plugin --profile <profile> add dsh-bottom-info-bar@latest` |
|
|
158
|
+
| GitHub 地址 | `dsh plugin --profile <profile> add <同一个地址>` |
|
|
159
|
+
| 本地代码或一键脚本 | `git -C <仓库> fetch origin && git -C <仓库> checkout main && git -C <仓库> merge --ff-only origin/main && node <仓库>/scripts/build.mjs` |
|
|
258
160
|
|
|
259
|
-
|
|
161
|
+
插件绝不自动更新——它只负责告诉你「有新版本」。
|
|
260
162
|
|
|
261
|
-
##
|
|
163
|
+
## 隐私与安全
|
|
262
164
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
| 余额显示**刷新失败**但仍能看到数字 | 网络或密钥的临时问题;60 秒后自动重试,并保留上次数值。**把鼠标停在警告上**可看详情 |
|
|
268
|
-
| OpenCode Go 显示**刷新失败**且提示缺少凭据 | 到「设置 → 模型」填 `OPENCODE_GO_API_KEY`,或用 opencode CLI 登录 |
|
|
269
|
-
| ChatGPT 显示**未连接** | 安装配套插件 `dsh-chatgpt-subscription` 并登录 |
|
|
270
|
-
| ChatGPT 的套餐或到期时间为空 | 重新登录或重新绑定。若令牌里确实没有这些字段,信息栏会留空而不是猜 |
|
|
271
|
-
| 怎么更新插件? | 点红色的**新版本提醒**标签复制命令,然后重启 `dsh web`。详见[更新版本](#更新版本) |
|
|
272
|
-
| 更新完了,提醒还在 | 版本号读的是磁盘上已安装的那一份:先刷新页面。若提醒仍在,确认更新真的换掉了已安装的副本(`link:` 安装必须重新构建 `lib/`) |
|
|
273
|
-
| 安装报错「这个包没有声明组合包」 | DSH 装到的是一个「根目录不是包」的仓库——那是包移到仓库根之前的本仓库。插件页里改填包名 `dsh-bottom-info-bar`,或用包含该修复的版本上的仓库地址 |
|
|
274
|
-
| 模型显示成 `V41-Flash` | 不是错字——那是 DSH 对 **V4.1 Flash** 的写法。详见[关于模型名](#关于模型名-v41-flash) |
|
|
275
|
-
| 简洁模式显示的额度窗口和完整模式不同 | 这是有意的:简洁模式优先最短窗口(5 小时 > 周 > 月)。额度与倒计时仍取自同一窗口 |
|
|
276
|
-
| 为什么看不到模型的思考过程? | DSH 不在界面上渲染内部推理——这是 DSH 界面层的限制,与本插件无关 |
|
|
277
|
-
| 我想换回原来的统计栏 | 卸载插件并重启 |
|
|
165
|
+
- **只读凭据。** API Key 始终留在 DSH 里;`~/.codex/auth.json` 与 `~/.commandcode/auth.json` 只在本机读取,绝不写回。本插件不负责绑定或续期账号。
|
|
166
|
+
- **不保存对话内容。** 账本只记录 token、模型、服务商、币种与花费——没有提示词、消息或密钥。
|
|
167
|
+
- **只存本地。** 数据在 `~/.dsh/dsh-bottom-info-bar/`(目录 `0700`、文件 `0600`)。除了信息栏自身发起的服务商接口请求,没有任何数据离开本机。
|
|
168
|
+
- **卸载不删数据。** 想彻底清零,在插件页的「账单数据」里导出或清除。
|
|
278
169
|
|
|
279
170
|
## 开发
|
|
280
171
|
|
|
281
|
-
-
|
|
282
|
-
-
|
|
283
|
-
-
|
|
284
|
-
- **版本记录** —— [CHANGELOG.md](CHANGELOG.md)
|
|
285
|
-
- **参与贡献** —— [CONTRIBUTING.md](CONTRIBUTING.md);日常流程与发布机制见 [docs/WORKFLOW.md](docs/WORKFLOW.md)
|
|
286
|
-
|
|
287
|
-
这个项目所有"踩过坑才学会"的规矩都由 **CI 强制执行**而不是靠口头约定——测试套件、源码守卫、发布链条契约都会在每个 PR 上运行。Agent 需要遵守的约束见 [AGENTS.md](AGENTS.md)。
|
|
172
|
+
- 构建:`npm run build`
|
|
173
|
+
- 测试:`node tests/run-all.mjs`
|
|
174
|
+
- 贡献:[CONTRIBUTING.md](CONTRIBUTING.md)
|
|
288
175
|
|
|
289
176
|
## 许可证
|
|
290
177
|
|
|
291
178
|
[MIT](LICENSE) © 2026 songoao25
|
|
179
|
+
|
|
180
|
+
💬 有问题或想法?扫码加入微信群 **DeepThinking**:
|
|
181
|
+
|
|
182
|
+
<img src="assets/wechat-group.png" width="180" alt="微信群 DeepThinking 二维码">
|
package/lib/client.js
CHANGED
|
@@ -158,6 +158,9 @@ const RPC_TIMEOUT_MS = 20000;
|
|
|
158
158
|
// 不必干等后台 60s 自动周期。覆盖窗口需容纳会话模型从空到确定的短暂翻转期。
|
|
159
159
|
const BOOT_AT = Date.now();
|
|
160
160
|
const FORCE_REFRESH_WINDOW_MS = 6000;
|
|
161
|
+
// 版本提醒的重读间隔:npm 侧由 host 按 TTL 缓存,这里只是读本地接口,
|
|
162
|
+
// 60 秒一次足够让「本地副本更新完」的提醒自己消失,也不会给宿主添负担。
|
|
163
|
+
const UPDATE_INFO_REFRESH_MS = 60000;
|
|
161
164
|
|
|
162
165
|
// 复制文本到剪贴板。
|
|
163
166
|
// 优先用异步剪贴板 API——它只在「安全上下文」可用:本 GUI 走 http://127.0.0.1 属安全上下文,
|
|
@@ -2723,13 +2726,30 @@ module.exports = {
|
|
|
2723
2726
|
return function () { document.removeEventListener(BIB_LEDGER_EVENT, onLedgerChanged); };
|
|
2724
2727
|
}, [load, activeSessionModel]);
|
|
2725
2728
|
|
|
2726
|
-
//
|
|
2729
|
+
// 版本信息:打开页面读一次,之后每 60 秒、以及页面重新可见/窗口重新获得焦点时重读。
|
|
2730
|
+
// 这样本地副本更新完(git pull + 重建,或 dsh plugin add …@latest)之后,红色提醒会自己消失,
|
|
2731
|
+
// 不必等用户刷新页面或重启 dsh web —— 2026-09-24 用户报的「更新完还显示提醒」就出在这里。
|
|
2732
|
+
// 每次读的都是本地 host 接口,NPM 只在 host 侧按 TTL 重查,这里不轮询 NPM。
|
|
2727
2733
|
React.useEffect(function () {
|
|
2728
2734
|
let active = true;
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2735
|
+
const readUpdateInfo = function () {
|
|
2736
|
+
rpc('getUpdateInfo').then(function (info) {
|
|
2737
|
+
if (active && info && typeof info.current === 'string') setUpdateInfo(info);
|
|
2738
|
+
}).catch(function () { /* 版本检查失败静默,不影响信息栏 */ });
|
|
2739
|
+
};
|
|
2740
|
+
readUpdateInfo();
|
|
2741
|
+
const timer = window.setInterval(readUpdateInfo, UPDATE_INFO_REFRESH_MS);
|
|
2742
|
+
const onVisible = function () {
|
|
2743
|
+
if (document.visibilityState === 'visible') readUpdateInfo();
|
|
2744
|
+
};
|
|
2745
|
+
document.addEventListener('visibilitychange', onVisible);
|
|
2746
|
+
window.addEventListener('focus', onVisible);
|
|
2747
|
+
return function () {
|
|
2748
|
+
active = false;
|
|
2749
|
+
window.clearInterval(timer);
|
|
2750
|
+
document.removeEventListener('visibilitychange', onVisible);
|
|
2751
|
+
window.removeEventListener('focus', onVisible);
|
|
2752
|
+
};
|
|
2733
2753
|
}, []);
|
|
2734
2754
|
|
|
2735
2755
|
// 会话统计变化(回复中 turns/steps/tokens 增长,回复完成时停止)→ 防抖后即时刷新花费,
|
package/lib/index.js
CHANGED
|
@@ -243,24 +243,49 @@ function updateCommandForInstall() {
|
|
|
243
243
|
return { installMode: 'npm', updateCommand: npmCommand }
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
|
|
247
|
-
|
|
246
|
+
// npm 上的最新版本:进程内缓存 + TTL 定期重查。
|
|
247
|
+
// 两个「版本」的时间尺度必须分开,否则就会出现「本地更新完了,提醒还挂着」:
|
|
248
|
+
// · latest(npm 发布版)——变化慢,按 TTL 重查(默认 15 分钟),新版本不必等宿主重启才出现;
|
|
249
|
+
// · current(本机已安装版)——每次 RPC 都从磁盘重读,更新完本地副本后提醒会自己消失。
|
|
250
|
+
// 2026-09-24 用户报的正是这里:旧实现只在进程启动时查一次 npm,客户端也只读一次结果,
|
|
251
|
+
// 于是更新完本地副本后,提醒要等刷新页面(甚至重启宿主)才消失。
|
|
252
|
+
const UPDATE_LATEST_TTL_MS = 15 * 60 * 1000
|
|
253
|
+
const UPDATE_LATEST_RETRY_MS = 60 * 1000
|
|
254
|
+
|
|
255
|
+
// TTL 可被测试覆盖(生产不设置):0 表示每次都重查。
|
|
256
|
+
function updateLatestTtlMs() {
|
|
257
|
+
const raw = Number(process.env.DSH_BOTTOM_INFO_BAR_UPDATE_TTL_MS)
|
|
258
|
+
return Number.isFinite(raw) && raw >= 0 ? raw : UPDATE_LATEST_TTL_MS
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const updateLatestCache = { value: null, expiresAt: 0, pending: null }
|
|
262
|
+
|
|
263
|
+
// 查一次 npm。任何失败都返回 null —— 提醒宁可不显示,也不误报。
|
|
264
|
+
function fetchLatestVersion() {
|
|
248
265
|
const controller = new AbortController()
|
|
249
266
|
const timer = setTimeout(() => controller.abort(), UPDATE_CHECK_TIMEOUT_MS)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
267
|
+
return fetch(UPDATE_REGISTRY_URL, {
|
|
268
|
+
headers: { accept: 'application/json' },
|
|
269
|
+
signal: controller.signal,
|
|
270
|
+
}).then((response) => (response.ok ? response.json() : null))
|
|
271
|
+
.then((body) => (body && typeof body.version === 'string' ? body.version : null))
|
|
272
|
+
.catch(() => null)
|
|
273
|
+
.finally(() => clearTimeout(timer))
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// 读最新版本:命中缓存直接返回;过期则重查(并发调用共用同一次请求)。
|
|
277
|
+
// 查失败时保留上一次的已知值(网络抖动不该让提醒闪烁),并把下次重试提前到 1 分钟。
|
|
278
|
+
function latestVersion() {
|
|
279
|
+
const now = Date.now()
|
|
280
|
+
if (now < updateLatestCache.expiresAt) return Promise.resolve(updateLatestCache.value)
|
|
281
|
+
if (updateLatestCache.pending) return updateLatestCache.pending
|
|
282
|
+
const pending = fetchLatestVersion().then((value) => {
|
|
283
|
+
if (value !== null) updateLatestCache.value = value
|
|
284
|
+
updateLatestCache.expiresAt = Date.now() + (value !== null ? updateLatestTtlMs() : UPDATE_LATEST_RETRY_MS)
|
|
285
|
+
return updateLatestCache.value
|
|
286
|
+
}).finally(() => { updateLatestCache.pending = null })
|
|
287
|
+
updateLatestCache.pending = pending
|
|
288
|
+
return pending
|
|
264
289
|
}
|
|
265
290
|
|
|
266
291
|
// ---------- 双模式(余额制 / 订阅制)配置 ----------
|
|
@@ -1670,8 +1695,8 @@ export default {
|
|
|
1670
1695
|
const t = hostLocale.createHostTranslator(ctx);
|
|
1671
1696
|
// 窗口标签:在 apply 内部按当前语言偏好重新计算(模块顶层 WINDOW_LABELS 仅作安全兜底)。
|
|
1672
1697
|
const windowLabels = { five_hour: t('host.hour'), seven_day: t('ui.weekly'), monthly: t('ui.monthly') };
|
|
1673
|
-
//
|
|
1674
|
-
|
|
1698
|
+
// 预热一次 npm 版本查询(失败不影响信息栏);此后由 latestVersion() 按 TTL 自行重查。
|
|
1699
|
+
latestVersion()
|
|
1675
1700
|
// 会话谱系列表是冷安全读取,但仍可能触发持久化查询;短暂缓存避免信息栏轮询
|
|
1676
1701
|
// 每次都重新扫描全部会话。缓存失效时再次读取,保证新建子代理最终能被纳入。
|
|
1677
1702
|
const SESSION_LINEAGE_CACHE_MS = 1000
|
|
@@ -4516,14 +4541,16 @@ export default {
|
|
|
4516
4541
|
const ROUTE_PREFIX = '/_dsh/dsh-bottom-info-bar';
|
|
4517
4542
|
const ROUTES = {
|
|
4518
4543
|
getUpdateInfo: async function () {
|
|
4519
|
-
//
|
|
4520
|
-
//
|
|
4544
|
+
// latest 按 TTL 重查、current 每次从磁盘重读(见 latestVersion):
|
|
4545
|
+
// 客户端每 60 秒读一次这个接口,所以本地副本更新完后提醒会自己消失,
|
|
4546
|
+
// 不必刷新页面或重启宿主。
|
|
4521
4547
|
// 命令按安装形态区分,避免把 link: 安装的用户引导到 npm 命令而丢掉本地代码。
|
|
4522
|
-
const
|
|
4548
|
+
const latest = await latestVersion()
|
|
4523
4549
|
const current = packageVersion()
|
|
4524
|
-
return Object.assign({},
|
|
4550
|
+
return Object.assign({}, updateCommandForInstall(), {
|
|
4525
4551
|
current: current,
|
|
4526
|
-
|
|
4552
|
+
latest: latest,
|
|
4553
|
+
available: !!latest && compareVersions(latest, current) > 0,
|
|
4527
4554
|
})
|
|
4528
4555
|
},
|
|
4529
4556
|
getBalanceSnapshot: async function (args) {
|