@rayadesu/dsh-billing 0.3.16 → 0.3.18

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/AGENTS.md CHANGED
@@ -11,20 +11,24 @@ deepseek-harness 官方仓库([deepseek-ai/deepseek-harness](https://github.co
11
11
  ```
12
12
  packages/llm-billing/ 宿主插件 @rayadesu/dsh-llm-billing(/user/balance 传输、峰谷计价、billing Remote)
13
13
  packages/ui-billing/ 浏览器插件 @rayadesu/dsh-client-ui-billing(会话头部徽标与详情面板)
14
- packages/typert-protocol/ 内嵌 Typert 协议声明(@deepseek-ai/dsh-typert-protocol@0.1.6-alpha.1 的 lib/types),构建期供 typert 生成器识别装饰器;刻意不在 pnpm workspace 内,让 @deepseek-ai/dsh-typert-protocol 从 npm 解析(内嵌副本只有声明,无运行时实现)。它没有自己的 node_modules——其中的 cordis 必须与 workspace 根解析到同一个副本,否则 `TypertRemoteService` 的 `Context` 会与插件源码的 `Context` 变成两个类型。
14
+ packages/typert-protocol/ 内嵌 Typert 协议声明(@deepseek-ai/dsh-typert-protocol@0.2.0-rc.1 的 lib/types),构建期供 typert 生成器识别装饰器;刻意不在 pnpm workspace 内,让 @deepseek-ai/dsh-typert-protocol 从 npm 解析(内嵌副本只有声明,无运行时实现)。它没有自己的 node_modules——其中的 cordis 必须与 workspace 根解析到同一个副本,否则 `TypertRemoteService` 的 `Context` 会与插件源码的 `Context` 变成两个类型。
15
15
  cordis.patch.yml DSH profile bundle 补丁层:挂载 llm-billing + ui-billing 两个插件行
16
16
  ```
17
17
 
18
18
  ## DSH 集成方式
19
19
 
20
20
  - **bundle(推荐)**:根 `package.json` 声明 `dsh.bundle.patch`,`cordis.patch.yml`
21
- 挂载两个插件行。三个包已发布到 npm(`@rayadesu` scope),pnpm 不会把 bundle 的
22
- 本地依赖装进 profile,所以一条命令同时安装 bundle 与两个包(让行名能从 profile 的
23
- node_modules 解析):
21
+ 挂载两个插件行。三个包已发布到 npm(`@rayadesu` scope)。bundle 把两个插件包声明为
22
+ 普通 `dependencies`(官方组合包同款;profile 初始化为 `nodeLinker: hoisted` +
23
+ `autoInstallPeers: false`——peer 不会进 profile,组件包必须是 dependencies 才会随
24
+ bundle 装入并 hoist 到 profile 根、让行名从 node_modules 解析),所以单个包名即可装全:
24
25
 
25
26
  ```sh
26
- dsh plugin --profile web add @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing
27
+ dsh plugin --profile web add @rayadesu/dsh-billing
27
28
  ```
29
+
30
+ Web 官方安装方式用同一个包名:侧栏 插件 → 添加插件 → 输入 `@rayadesu/dsh-billing`
31
+ (对话框也接受 GitHub 仓库地址或本地目录绝对路径;安装源可选默认源或中国大陆镜像源)。
28
32
  - **手动**:把 `cordis.patch.yml` 的 insert 合并进 `$DSH_HOME/profiles/<name>/cordis.patch.yml`,
29
33
  并用 `dsh plugin --profile <name> add @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing`
30
34
  安装两个包(行名解析同上)。
@@ -41,7 +45,7 @@ cordis.patch.yml DSH profile bundle 补丁层:挂载 llm-billing + ui-
41
45
  - **发布前校验**:`pnpm run verify`(每个包 `prepublishOnly` 自动运行)检查
42
46
  `lib/typert.host.js` 的 `TYPERT.package` 必须等于导出它的包名,且 lib 中不得残留
43
47
  其他包名的清单;失败即禁止发布。
44
- - **依赖以发布形态声明**:`@deepseek-ai/dsh-*` 依赖写 `^0.1.7-alpha.2`(对应官方 monorepo 当前发布基线,monorepo 内为
48
+ - **依赖以发布形态声明**:`@deepseek-ai/dsh-*` 依赖写 `^0.2.0-rc.1`(对应官方 monorepo 当前发布基线,monorepo 内为
45
49
  `workspace:^`);本插件的三个包发布到 npm 的
46
50
  `@rayadesu` scope,直接 `dsh plugin add @rayadesu/...` 安装。
47
51
  - **补客户端包漏声明的运行时依赖**:`dsh-client-store`(`zustand`/`immer`)与
@@ -63,13 +67,28 @@ cordis.patch.yml DSH profile bundle 补丁层:挂载 llm-billing + ui-
63
67
  - **workspace 关掉 pnpm 发布龄门槛**:`pnpm-workspace.yaml` 显式 `minimumReleaseAge: 0`。
64
68
  pnpm ≥11 默认 1 天门槛会把刚发布的 alpha 包挡在 lockfile 校验外,而校验阶段不认
65
69
  `minimumReleaseAgeExclude`(那是解析期自动追加的),本仓库又要紧跟 DSH alpha 基线。
70
+ - **DSH 依赖线升级 checklist**:`@deepseek-ai/dsh-*` 全线对齐同一基线(`llm-billing` 的 peer+dev、`ui-billing`
71
+ 的 peer+dev、根的 devDependencies 三处一起改)。升级按序做:① 改三处版本号 → ② `pnpm install` 刷
72
+ lockfile(本仓库已关发布龄门槛)→ ③ `pnpm run build`(顺带确认 typert generator 仍产出 `create` 工厂,
73
+ 没产出时 `scripts/typert-compat.mjs` 会非零退出)→ ④ `pnpm run test`;⑤ 若测试报 `Cannot find package`,
74
+ 按上一条「补漏声明依赖」补进 `ui-billing` 的 devDependencies。
66
75
  - **密钥不进仓库**:`DEEPSEEK_API_KEY` 等一律由用户环境或凭据 seam 提供,仓库不含真实值。
67
76
  - **README 双语**:每个 README 遵循 DSH 结构 `README.md`(EN) + `README.zh.md`(ZH) +
68
77
  `README.i18n.yaml`(记录两文件 git blob hash,改动后需更新)。
69
- - **版本对齐**:根 bundle 与两个包统一版本号(当前 0.3.16),`pnpm-lock.yaml` 随依赖变更更新。
78
+ - **版本对齐**:根 bundle 与两个包统一版本号(当前 0.3.18),`pnpm-lock.yaml` 随依赖变更更新。
70
79
  - **提交与发布流程**:见 `.agents/skills/dsh-release/SKILL.md` —— 阶段 A(改代码 → 按档位校验/打包 →
71
80
  本地 pack 安装 → 交用户验证)**不提交**,改动留在工作区;用户说「发布」进入阶段 B 才 bump 版本、
72
81
  **按类型分别提交**、推送、发 npm 与 GitHub Release。
82
+ - **浮动说明卡一律用 `HoverCard`,不要用 `Tooltip`**:两者形态不同 —— `Tooltip` 是「单行短标签」容器
83
+ (`padding: 3px 7px` 的 26px 条带、`pointer-events: none`、`label` 只收 `string`),官方自己那颗信息按钮
84
+ 装的是 ~50 字 / 2–3 行;把 4 行说明硬塞进去会渲染成一整块贴边白字方块,版本号跟正文同权重,且球泡用的是
85
+ 13px 纯白 `--dsw-static-neutral-bluish-00`,比面板里任何一行都重。**超过两行的说明就是 `HoverCard` 的活**:
86
+ `content` 收 JSX(可做 secondary/tertiary 分层)、指针能停留可选中(长文本才读得了)、并且**自带 portal**。
87
+ 选 `inline` 变体(`display: inline`,进得了行盒;支持 focus-visible 打开 + Esc 关闭;placement 会夹进视口)——
88
+ `compact` 把卡片放在锚点**右侧**,贴右上角的按钮必然溢出屏幕,且它的定位分支根本没有水平边界检查;
89
+ `preview` 需要 `widthAnchorRef` 提供的宽度锚,得让纯展示组件持 ref。
90
+ - **面板的 CSS 变量到不了 portaled 卡片**:`.panel` 上定义的 `--billing-type-*` 自定义属性进
91
+ `document.body` 上的悬浮层就失效了,卡片要用到同款层级必须把值重抄一遍并在注释里写清两边要手动对齐。
73
92
  - **成本纪律(省 token)**:一轮的开销 ≈ 请求数 × 当时上下文,所以按改动定档做事——文案/样式/注释这类
74
93
  微调只跑受影响用例、只重打并重装改动的那个包;工具输出只留尾巴(`Select-Object -Last/First N`、
75
94
  `git diff -U0`);全套 `test`/`build`/`verify` 一轮只跑一次;同一批微调的文档与 hash 攒到定稿后一次补。
@@ -83,7 +102,7 @@ pnpm install # 安装本仓库依赖(dsh-* 从 registry 解析)
83
102
  pnpm run build # host + client 两个编译面(tsc + tsdown + typert 产物)
84
103
  pnpm run test # vitest
85
104
  pnpm run verify # 发布前校验
86
- dsh plugin --profile web add @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing # 安装进 DSH
105
+ dsh plugin --profile web add @rayadesu/dsh-billing # 安装进 DSH(bundle 依赖带齐两个插件包)
87
106
  ```
88
107
 
89
108
  **从零构建顺序是硬约束**:`ui-billing` 的浏览器半面(`tsconfig.client.json`)导入
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # last confirmed-consistent state. Both languages carry equal authority; after
3
3
  # editing either side, bring the other along and re-record both hashes with:
4
4
  # git hash-object README.md README.zh.md
5
- README.md: 2b5bde11041d108657cab40bd4afa04c8cf88195
6
- README.zh.md: 2d5570b3700887ff928ba559a195c48d98217d61
5
+ README.md: b920ff298cffadf7b35678f80d77358b4c0bc38a
6
+ README.zh.md: 623d1b5d3c292f1dea3978a2a69fbfe01c8ad3da
package/README.md CHANGED
@@ -2,32 +2,34 @@
2
2
 
3
3
  English | [中文](README.zh.md)
4
4
 
5
- A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin that shows your **DeepSeek account balance**, **this session's (this conversation's) billed spend**, and **today's total spend across all sessions** directly in the web session header; each completed turn also shows its **turn cost** as a static amount at the end of the message actions row, and the detail panel ends with a **today session-spend ranking**.
5
+ A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin that shows your **DeepSeek account balance** and **today's total spend across all sessions** directly in the web session header, and **this conversation's own billed spend** on a pill under the composer; each completed turn also shows its **turn cost** as a static amount at the end of the message actions row, and the detail panel ends with a **today session-spend ranking**.
6
6
 
7
7
  > The balance is the real `GET /user/balance` figure; the session, turn, and today spends price each message's billed tokens at the official peak/off-peak rates and are estimates, not billing promises.
8
8
 
9
9
  ## What it shows
10
10
 
11
- - **Session-header badge** — two lines: remaining balance (`剩余金额:¥X`, the panel's headline without its `API` prefix — the chip is narrow) and this conversation's billed spend (`本会话花费:¥X`, its own work plus every subagent session it delegated, worded exactly as the panel labels it).
12
- - **Detail panel** — the remaining amount with today's consumption measured from the balance series after it (see *How session spend is computed*); today's billed token count next to today's all-session spend (`今日 Token` / `今日花费`, one row; the count is DSH's compact notation with its own unit — `12.2K tok`, followed by the day's cache-hit share as a bare, unparenthesized percentage, rendered by DSH's own hit-rate rule: an integer percent that grows decimals only as far as a partial hit needs to stay below 100, and none at all when the day billed no prompt-side input); directly under that row, today's two bucket detail lines — tokens on top, costs below, each on its own with its natural ` · ` spacing (no column alignment between them), in the per-model breakdown line's typography but on the third section's row spacing (the today session-spend ranking's tight 6px rhythm); every spend amount renders at **three significant digits** (`¥9.58`) but never finer than four decimals — an amount below ¥0.0001 reads `¥0` — while the balance line alone keeps four decimals; this conversation's spend on its own line below them (`本会话花费` — this session plus the subagent sessions it delegated, per-model rows included; with this conversation's share of today trailing it, bare and unparenthesized (`¥X`), rendered **only when the conversation did not start today** — the session's own creation day decides (the delegated read reports `crossedDay`), not a comparison of the two amounts, because the live session figure and the 60-second-cached ranking row routinely differ mid-turn; one priced row per model (`未缓存输入 ¥X · 缓存读取 ¥Y · 输出 ¥Z` — DSH's own bucket wording and row order), plus a manual refresh action and a spend disclaimer on the `?` button (one line of estimate scope, then a line stating that the amounts include the subagent sessions this session delegated, then a line naming the amount trailing the session figure as this conversation's spend today, and the running plugin version as the last line, e.g. `v0.3.13`). The panel ends with a **today session-spend ranking**: sessions sorted by today's spend, highest first — one row per conversation, since each subagent session's spend is merged into the row of the session that delegated it (names come from the log's Chinese titles and follow renames automatically; at most the top 10 rows, with a "…N more sessions" hint).
11
+ - **Session-header badge** — two lines: remaining balance (`剩余金额:¥X`, the panel's headline without its `API` prefix — the chip is narrow) and today's spend across every session (`今日花费:¥X`, worded exactly as the panel labels it — the chip's second line and the panel's row read the same state, so they cannot disagree). Being account-level, it moves on the day's own reads (mount, refresh, a settled turn, and a panel open) rather than streaming with the turn.
12
+ - **Detail panel** — the remaining amount with today's consumption measured from the balance series after it (see *How session spend is computed*); today's billed token count next to today's all-session spend (`今日 Token` / `今日花费`, one row; the count is DSH's compact notation with its own unit — `12.2K tok`, followed by the day's cache-hit share as a bare, unparenthesized percentage, rendered by DSH's own hit-rate rule: an integer percent that grows decimals only as far as a partial hit needs to stay below 100, and none at all when the day billed no prompt-side input); directly under that row, today's two bucket detail lines — tokens on top, costs below, each on its own with its natural ` · ` spacing (no column alignment between them), in the per-model breakdown line's typography but on the third section's row spacing (the today session-spend ranking's tight 6px rhythm); every spend amount renders at **three significant digits** (`¥9.58`) but never finer than four decimals — an amount below ¥0.0001 reads `¥0` — while the balance line alone keeps four decimals; and a manual refresh action and a spend disclaimer on the `?` button (one line of estimate scope, then a line stating that a conversation's amount includes the subagent sessions it delegated, and the running plugin version as the last line, e.g. `v0.3.13`). The panel ends with a **today session-spend ranking**: sessions sorted by today's spend, highest first — one row per conversation, since each subagent session's spend is merged into the row of the session that delegated it (names come from the log's Chinese titles and follow renames automatically; at most the top 10 rows, with a "…N more sessions" hint).
13
13
  - **Turn cost amount** — each completed turn's closing message shows a plain static `¥X` at the **end** of the actions row, after the clock: non-interactive (no icon, no "cost" word, no card), its typography replicates the clock text (13px secondary tier, tertiary tone, nowrap), and it is **always visible** (not hover-revealed like the clock text — the row's own hover reveal shows both together); turns without DeepSeek usage (zero cost) or failed loads stay hidden.
14
+ - **Composer spend pill** — the composer's own stat row (the one DSH's time/token pills sit in) carries one more entry: this plugin's ring-and-sparkle mark plus this conversation's billed spend, at the same three-significant-digit precision as the badge, opening a cost card whose three rows are the spend's three billing buckets (uncached input / cached input / output) under DSH's own token-card wording. Its amount is the **conversation's** — this session plus the subagent sessions it delegated, merged by the same rule the host's own sums use; it is the ONLY surface that shows the conversation's own spend (the badge's second line and its panel report the day), so a session that priced nothing anywhere shows no pill, while one that priced nothing itself but delegated a priced subagent still shows.
14
15
  - **Failures and empty states** — a session or day without priced usage shows "no usage recorded" instead of a fabricated figure; a missing key, rejected credential, or transport error renders a muted "Balance unavailable" whose tooltip carries the Remote's own error message.
15
16
 
16
17
  ## Data update mechanics
17
18
 
18
- - **Session spend follows the conversation** — the host prices every committed event into a per-session projection (`billingTodaySpend`) and pushes it to the browser, so **this session's own spend** updates live with no Remote call; the Remote read remains the fallback when the projection registry is absent, and `billing/getDelegatedSpend` adds the subagent sessions this session delegated (fetched on mount, on refresh, and when a turn settles) so the amount shown is the whole conversation's. **Today's spend** is recomputed on turn settle (one shared scan serves the aggregate, the ranking, and the delegated subtotal), and the parenthesized today share on that same session line reads that same ranking fetch (no extra request, at the cost of moving with it); each turn's cost comes from **one batch fetch per session** instead of one call per rendered message.
19
- - **Balance is cached and polled** — the host reuses one `/user/balance` snapshot for 15 seconds (manual refresh forces a fresh one) and caps each request at 5 seconds; the browser keeps the last settled value so a session switch renders the amount immediately and revalidates in the background, and polls every 5 minutes while the page is visible (paused while the document is hidden, refreshed once on return) — the balance-series consumption below is measured from the first balance each local day samples, so that cadence is its resolution.
19
+ - **Session spend follows the conversation** — the host prices every committed event into a per-session projection (`billingTodaySpend`) and pushes it to the browser, so **this conversation's own spend** updates live with no Remote call — on the composer pill, the surface that shows it; the Remote read remains the fallback when the projection registry is absent, and `billing/getDelegatedSpend` adds the subagent sessions this session delegated (fetched on mount, on a session switch, on refresh, and when a turn settles) so the amount shown is the whole conversation's. **Today's spend** — the badge's second line and the panel's row — is recomputed on turn settle (one shared scan serves the aggregate and the ranking); each turn's cost comes from **one batch fetch per session** instead of one call per rendered message.
20
+ - **A session switch re-reads only the session's own lines** — the balance and today's spend do not vary by session, so they are not re-fetched when you switch; only `billing/getSessionSpend` and `billing/getDelegatedSpend` are. A **settled turn and the manual refresh ask for a fresh day figure** (`force`), so the day row never reports a turn's cost a turn late; **opening the detail panel** re-reads the day row too, so browsing sessions while idle cannot leave it behind. A plain read of the day cache is answered from the last value at once, with the scan running behind it — so nothing the user did not just cause ever waits on the day's all-session scan.
21
+ - **Balance is cached and polled** — the host reuses one `/user/balance` snapshot for 15 seconds (manual refresh forces a fresh one) and caps each request at 5 seconds; the browser keeps the last settled value so a session switch renders the amount immediately, and polls every 5 minutes while the page is visible (paused while the document is hidden, refreshed once on return) — the balance-series consumption below is measured from the first balance each local day samples, so that cadence is its resolution.
20
22
  - **Old values survive refreshes** — a failed refresh keeps the last good value instead of blanking it.
21
23
 
22
24
  ## Preview
23
25
 
24
26
  A real session: the session-header badge and the open detail panel:
25
27
 
26
- ![Billing plugin overview: session header badge and the open detail panel (remaining amount, today's tokens and spend, this session's spend, per-model breakdown, today's session ranking)](preview-overview.png)
28
+ ![Billing plugin overview: session header badge and the open detail panel (remaining amount, today's tokens and spend, today's bucket lines, today's session ranking)](preview-overview.png)
27
29
 
28
- Close-up of the detail panel — the `API 剩余金额` figure, `今日 Token` and `今日花费`, `本会话花费` (with its parenthesized today share once the session crossed a day), the per-model breakdown (`未缓存输入 · 缓存读取 · 输出`), and the today session-spend ranking:
30
+ Close-up of the detail panel — the `API 剩余金额` figure, `今日 Token` and `今日花费`, today's bucket detail lines (`未缓存输入 · 缓存读取 · 输出`), and the today session-spend ranking:
29
31
 
30
- ![Detail panel close-up: API remaining amount, today's tokens and spend, this session's spend with its parenthesized today share, the uncached-input / cached-input / output breakdown, and the today session ranking](preview-detail.png)
32
+ ![Detail panel close-up: API remaining amount, today's tokens and spend, the uncached-input / cached-input / output bucket lines, and the today session ranking](preview-detail.png)
31
33
 
32
34
  Close-up of the turn-cost amount — the static `¥` amount at the end of the actions row, after the clock:
33
35
 
@@ -39,7 +41,16 @@ Close-up of the turn-cost amount — the static `¥` amount at the end of the ac
39
41
  | Package | Side | Role |
40
42
  | --- | --- | --- |
41
43
  | [`packages/llm-billing`](packages/llm-billing) — `@rayadesu/dsh-llm-billing` | Host | Owns the `/user/balance` transport and the peak/off-peak pricing table. Exposes the `billing` Remote (`getBalance(force?)`, `getSessionSpend`, `getTodaySpend`, `getTodaySessionsSpend`, `getTurnSpend`, `getSessionTurnSpends`) and registers the client-visible `billingTodaySpend` projection unit. |
42
- | [`packages/ui-billing`](packages/ui-billing) — `@rayadesu/dsh-client-ui-billing` | Browser | Mounts the `billing` Remote itself and contributes the session-header badge and detail panel, plus the static turn-cost amount at the end of the message actions strip. |
44
+ | [`packages/ui-billing`](packages/ui-billing) — `@rayadesu/dsh-client-ui-billing` | Browser | Mounts the `billing` Remote itself and contributes the session-header badge and detail panel, plus the static turn-cost amount at the end of the message actions strip and the spend pill in the composer's stat row (beside the built-in token pill; its amount is this session plus the subagent sessions it delegated). |
45
+
46
+ ### Plugin manager display metadata
47
+
48
+ The sidebar's **Plugins** page and the **Settings → Plugins** list show each entry's title, description, and icon. Both come from files inside the package itself — the host reads no plugin code:
49
+
50
+ - `locale/en.json` and `locale/zh.json` — `{"meta": {"title": …, "description": …}}`, resolved against the active interface language and falling back to English.
51
+ - `icon.svg` — a self-contained SVG (no external font or image references; it renders through an `<img>` data URL) declared as the package's top-level `icon` field. The entry's rounded tile is drawn by the page (dark: `#151517` on a `#3b3b3c` border; light: white on a light border), so the icon itself must stay **transparent** — bake no background plate, or it turns into a dark square in the light theme.
52
+
53
+ Both are reached through the package's `exports` map, so a package that declares `exports` must also export `./locale/*.json`: without it the locale files are silently dropped and the entry falls back to its bare package name. Each of the three packages carries its own pair of locale files and its own icon.
43
54
 
44
55
  ## Prerequisites
45
56
 
@@ -48,19 +59,29 @@ Close-up of the turn-cost amount — the static `¥` amount at the end of the ac
48
59
 
49
60
  ## Installation
50
61
 
51
- ### Install (published to npm)
62
+ ### Install from the Web plugin page (official)
52
63
 
53
- The three packages are published to npm under the `@rayadesu` scope. Install the
54
- bundle plus the two plugin packages in one command — the bundle declares the two
55
- plugin packages as peer dependencies, which pnpm does not auto-install into the
56
- profile, so they must be named explicitly.
64
+ The three packages are published to npm under the `@rayadesu` scope. The bundle
65
+ declares the two plugin packages as its regular `dependencies`, so **one package
66
+ name installs everything**: pnpm pulls the bundle's dependency closure into the
67
+ profile, where the hoisted `node_modules` makes the two row names resolvable.
68
+
69
+ 1. In the sidebar open **Plugins** → **Add plugin**.
70
+ 2. Enter `@rayadesu/dsh-billing`. The dialog also accepts the GitHub repository
71
+ address (`https://github.com/rayadesune/DeepSeek-Harness-chat-billing`) or an
72
+ absolute local directory path — the two plugin packages themselves always
73
+ come from npm.
74
+ 3. Pick an install source (the default npm registry or the **Mainland China
75
+ mirror**), press **Install**, then **Enable now**.
76
+
77
+ ### Install with the CLI
57
78
 
58
79
  The `dsh` command you use depends on how dsh is installed:
59
80
 
60
81
  - **Global install** — use the global `dsh` from anywhere:
61
82
 
62
83
  ```bash
63
- dsh plugin --profile web add @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing
84
+ dsh plugin --profile web add @rayadesu/dsh-billing
64
85
  ```
65
86
 
66
87
  - **Source-built dsh** (a deepseek-harness checkout) — the CLI only resolves from
@@ -69,7 +90,7 @@ The `dsh` command you use depends on how dsh is installed:
69
90
 
70
91
  ```bash
71
92
  cd deepseek-harness
72
- pnpm dsh plugin --profile web add @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing
93
+ pnpm dsh plugin --profile web add @rayadesu/dsh-billing
73
94
  ```
74
95
 
75
96
  ### pnpm 11 release-age gate
@@ -86,9 +107,11 @@ latest version right after a publish:
86
107
  minimumReleaseAge: 0
87
108
  ```
88
109
 
89
- - Or, within the 24-hour window, install with an explicitly pinned version (an
90
- explicit pin bypasses the age gate; replace `0.3.0` with the version you want;
91
- from a source checkout, use `pnpm dsh …` as above):
110
+ - Or, within the 24-hour window, install with explicitly pinned versions (an
111
+ explicit pin bypasses the age gate; pin all three names — the bundle's two
112
+ plugin packages install transitively and need their own pin to pass the gate;
113
+ replace `0.3.0` with the version you want; from a source checkout, use
114
+ `pnpm dsh …` as above):
92
115
 
93
116
  ```bash
94
117
  dsh plugin --profile web add @rayadesu/dsh-billing@0.3.0 @rayadesu/dsh-llm-billing@0.3.0 @rayadesu/dsh-client-ui-billing@0.3.0
@@ -112,12 +135,17 @@ deepseek-harness checkout instead — the subcommands are identical.
112
135
 
113
136
  ```sh
114
137
  dsh plugin --profile web list # list the web profile's installed plugins
115
- dsh plugin --profile web add @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing
116
- dsh plugin --profile web remove @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing
138
+ dsh plugin --profile web add @rayadesu/dsh-billing
139
+ dsh plugin --profile web remove @rayadesu/dsh-billing
117
140
  dsh plugin --profile web update # update plugins to the latest allowed versions
118
141
  dsh plugin --profile web update --latest # ignore declared ranges; upgrade every plugin to its newest published version
119
142
  ```
120
143
 
144
+ Both commands take the bundle alone — its two plugin packages travel with it as
145
+ dependencies. A profile installed by the older three-package command lists all
146
+ three in its `package.json`, and pnpm only removes names listed there: on such
147
+ a profile, give `remove` all three names to clear the leftovers.
148
+
121
149
  `update` respects the version ranges in the profile's `package.json`, so it
122
150
  stays within the semver range each plugin declares. Adding `--latest` (a pnpm
123
151
  `update` flag) instead ignores those ranges and upgrades every plugin to its
@@ -130,7 +158,7 @@ commands.
130
158
 
131
159
  The two plugin packages declare the DeepSeek Harness packages they build on
132
160
  (`@deepseek-ai/cordis`, `@deepseek-ai/dsh-credentials`, `@deepseek-ai/dsh-session`,
133
- and the client runtime packages) as `peerDependencies` at `^0.1.7-alpha.2`. A dsh
161
+ and the client runtime packages) as `peerDependencies` at `^0.2.0-rc.1`. A dsh
134
162
  profile does not auto-install peers, so these are provided by the dsh
135
163
  installation itself through the `profiles/node_modules` fallback rather than
136
164
  fetched from the registry — no extra packages to install, and no registry token
@@ -146,7 +174,7 @@ are external and load them from the dsh installation at runtime, but this
146
174
  standalone workspace resolves them itself, so `ui-billing` declares them as its
147
175
  own `devDependencies` for the browser-half specs.
148
176
 
149
- The plugin builds against the 0.1.7-alpha.2 published line and keeps both DSH
177
+ The plugin builds against the 0.2.0-rc.1 published line and keeps both DSH
150
178
  runtime families readable: the live `Session` log surface
151
179
  (`Session.events` + `header.seedLength` at/before 0.1.1-rc.2,
152
180
  `snapshotEvents()` + `inheritedEventCount` since 0.1.2-alpha.4), and the
@@ -203,7 +231,7 @@ it. If a typert manifest ever names a package other than its own
203
231
 
204
232
  The typert generator recognizes `Remote`/`TypertRemoteService` only from a
205
233
  workspace-registered protocol package, so `packages/typert-protocol` vendors
206
- the published `@deepseek-ai/dsh-typert-protocol@0.1.7-alpha.2` declarations; when
234
+ the published `@deepseek-ai/dsh-typert-protocol@0.2.0-rc.1` declarations; when
207
235
  the dsh dependency line moves, refresh it from the installed package.
208
236
 
209
237
  The generated codecs must carry a `create` factory: the `dsh-typert-loader`
@@ -265,17 +293,18 @@ Both packages ship sane defaults; everything below is optional.
265
293
  - Each sample is priced at the peak/off-peak rate of its own **Beijing-time** hour — and of the rate revision in effect at its own timestamp — the three buckets are billed separately (`未缓存输入 ¥X · 缓存读取 ¥Y · 输出 ¥Z`, DSH's own bucket names), then summed per model. Peak windows apply weekdays (Monday–Friday) only; weekends are always off-peak.
266
294
  - **Today's spend** aggregates every session's events on the current Beijing-time calendar day with the same pricing rules; **today's tokens** is the sum of those same priced rows' three billing buckets (cache-hit input / cache-miss input / output), rendered in DSH's compact notation with its ` tok` unit (`517 tok`, `12.2K tok`, `1.2M tok`); event dates are also assigned in Beijing time.
267
295
  - **Turn cost** prices the events inside the turn's `turn/start`..`turn/end` range with the same rules (located by the closing message's session id + message id), folded in one pass for the whole session and served as a `messageId → cost` map.
268
- - **Today session ranking** aggregates today's spend per session with the same rules (a cross-day session counts only today's part), sorted descending; names come from the log's latest `session/title` event (the auto-generated Chinese title or a user rename). It ranks **conversations**: a subagent session (DSH stamps its header with `origin: 'subagent'` and a `delegationDepth`; a user fork carries neither) is merged into the row of the top-level session that delegated it, so one row is one conversation. Each row reports `total` (the conversation's whole day, subagents included) plus `ownTotal` (that session's own spend) — the panel's trailing share reads `ownTotal`, and the day's aggregate total is unchanged by the regrouping.
269
- - Models without a rate row are not priced (the built-in table covers the DSH `llm-deepseek` catalog — V4.1 Flash `deepseek-flash`, V4 Flash, V4 Pro, V4 Flash Vision Exp — plus the retired `deepseek-v4.1-flash-expires-on-0910` preview id and the MiMo-V2.5/V2.6 series; every flash-series route shares the same pair). Each sample takes the rate revision in effect at its own timestamp: the base schedule is the DeepSeek pricing effective **August 17**; the **flash series** (V4.1 Flash, V4 Flash, V4 Flash Vision Exp, and the retired id) was re-priced effective **September 10, 12:00 Beijing time** to off-peak 0.02 / 1.0 / 4.0 CNY per 1M tokens with peak at twice those prices — samples from before that instant, the V4.1 Flash route's own earlier usage included, keep the superseded rates; the **V4 Pro** route is announced to switch to V4.1 Flash and its rates on **September 14, 12:00 Beijing time**; the MiMo series is untouched (MiMo-V2.6 — launched September 22, 2026 — kept V2.5's published rates and ships the same rows). The weekend-off-peak rule (weekends billed at off-peak prices all day) follows the adjustment effective **August 23**.
296
+ - **Today session ranking** aggregates today's spend per session with the same rules (a cross-day session counts only today's part), sorted descending; names come from the log's latest `session/title` event (the auto-generated Chinese title or a user rename). It ranks **conversations**: a subagent session (DSH stamps its header with `origin: 'subagent'` and a `delegationDepth`; a user fork carries neither) is merged into the row of the top-level session that delegated it, so one row is one conversation. Each row reports `total` (the conversation's whole day, subagents included) plus `ownTotal` (that session's own spend); the day's aggregate total is unchanged by the regrouping.
297
+ - An unlisted model is NEVER priced by inference: no rate row means no price. The usage is recorded as `unpriced` instead and warned about once per model, so a new upstream model shows ¥0 WITH the model named as the reason rather than as an ordinary empty day — which is exactly how MiMo-V2.6 looked before its rows existed. Model ids are chosen by whoever ships the model, so a similar-looking name is no basis for a price; add one row under `billing.models` instead. The built-in table covers the DSH `llm-deepseek` catalog — V4.1 Flash `deepseek-flash`, V4 Flash, V4 Pro, V4 Flash Vision Exp — plus the retired `deepseek-v4.1-flash-expires-on-0910` preview id and the MiMo-V2.5/V2.6 series; every flash-series route shares the same pair). Each sample takes the rate revision in effect at its own timestamp: the base schedule is the DeepSeek pricing effective **August 17**; the **flash series** (V4.1 Flash, V4 Flash, V4 Flash Vision Exp, and the retired id) was re-priced effective **September 10, 12:00 Beijing time** to off-peak 0.02 / 1.0 / 4.0 CNY per 1M tokens with peak at twice those prices — samples from before that instant, the V4.1 Flash route's own earlier usage included, keep the superseded rates; the **V4 Pro** route is announced to switch to V4.1 Flash and its rates on **September 14, 12:00 Beijing time**; the MiMo series is untouched (MiMo-V2.6 — launched September 22, 2026 — kept V2.5's published rates and ships the same rows). The weekend-off-peak rule (weekends billed at off-peak prices all day) follows the adjustment effective **August 23**.
270
298
 
271
299
  - **The balance-series "today's consumption"** (the figure right after `API 剩余金额`) is pure account arithmetic and never enters the token pricing above: the first balance queried on the local calendar day − the current balance + the day's detected top-ups (an increase rounds **up to the next ¥10 step**, because the provider only tops up in round tens). It is the caliber of the `balanceinfo` program this plugin mirrors, and it is deliberately a separate figure from the priced 今日花费 — spend from another client, or from before this browser was opened, shows up only here; the two figures disagreeing is normal. The day record lives in this browser's `localStorage` (key `dsh.billing.balance-day.v1`), survives reloads and `dsh` restarts, rolls over on the **local** calendar day (the browser's day, not the host's Beijing day key), is sampled on mount, on session switch, on the manual refresh, and every 5 minutes while the page is visible, renders only once the day holds a sample, and is not shared between clients. Details in [`packages/ui-billing/README.md`](packages/ui-billing/README.md).
272
300
 
273
301
  ## Known limitations
274
302
 
275
303
  - **Priced rows only** — the session, turn, and today spends only price models that have a `billing.models` row (today's token count reads those same rows, so it covers priced models only).
276
- - **On-demand aggregation** — today's spend and the session ranking are computed on the host behind a 60-second cache and share ONE scan; a miss resolves live sessions from their eager projection cells and cold sessions from the zero-I/O projection-cache row when that row's own day is not the queried one, reading a log only for sessions whose persisted revision changed (or whose cached row covers the queried day). A failed resolution is remembered by revision instead of being retried every scan. The ranking's subagent roll-up regroups those rows in the same pass, with no extra read, and the aggregate still sums every session — so merging never moves money. The ranking is also fetched only on demand — see the next bullet — and the trailing today share on the 本会话花费 row rides that same fetch, so it costs no extra request ((—) until the first one settles, and up to 60 seconds behind afterwards).
277
- - **Ranking is fetched on demand** — the panel loads the ranking when it is opened (and on refresh), so a badge that stays closed never pays for the all-session scan. The trailing today share on the 本会话花费 row rides that same fetch (reading the row's `ownTotal`, so a merged subagent total never poses as this session's own), so it costs no extra request (nothing shows until the ranking settles, and it can be up to 60 seconds behind afterwards).
278
- - **The delegated subtotal moves with the turn, not per event** — the session line's own part is live (pushed projection), while the subagent part comes from `billing/getDelegatedSpend`, refreshed on mount, on manual refresh, and when a turn settles, and served from the same 60-second host cache as today's spend. A subagent that burns money mid-turn therefore lands on the parent's amount at the next read (turn settle, refresh, or a panel open), not per event.
304
+ - **On-demand aggregation** — today's spend and the session ranking are computed on the host behind a 60-second cache and share ONE scan; a miss resolves live sessions from their eager projection cells and cold sessions from the zero-I/O projection-cache row when that row's own day is not the queried one, reading a log only for sessions whose persisted revision changed (or whose cached row covers the queried day). A failed resolution is remembered by revision instead of being retried every scan. The ranking's subagent roll-up regroups those rows in the same pass, with no extra read, and the aggregate still sums every session — so merging never moves money. The ranking is also fetched only on demand — see the next bullet.
305
+ - **Ranking is fetched on demand** — the panel loads the ranking when it is opened (and on refresh), so a badge that stays closed never pays for the all-session scan; the ranking can be up to 60 seconds behind afterwards.
306
+ - **The delegated subtotal moves with the turn, not per event** — the pill's own part is live (pushed projection), while the subagent part comes from `billing/getDelegatedSpend`, refreshed on mount, on a session switch, on manual refresh, and when a turn settles, and served from the same 60-second host cache as today's spend. A subagent that burns money mid-turn therefore lands on the conversation's amount at the next read (turn settle, refresh, or a session switch), not per event.
307
+ - **The badge's second line is not live per turn** — today's spend is account-level and has no pushed projection behind it, so the line moves on mount, on the manual refresh, on a panel open, and when a turn settles (debounced ~2s); the composer pill, which reads the pushed projection, is the surface that streams with the turn.
279
308
  - **A merged row can be untitled** — a subagent whose parent session is outside the scan (a deleted or archived parent log, for instance) is still attributed to the parent id its own header names, but that log is never read, so the merged row shows the untitled fallback until the parent is scanned.
280
309
  - **Ranking capped at 10** — the panel shows at most the top 10 conversations (the host's subagent roll-up runs first, so one row is one conversation), with a "…N more sessions" hint.
281
310
  - **Turn cost needs a finalized closing message** — interrupted turns have no actions row, so no turn cost; cold sessions served straight from the projection cache may rank with an "Untitled" name until their log is read again.
package/README.zh.md CHANGED
@@ -2,32 +2,34 @@
2
2
 
3
3
  [English](README.md) | 中文
4
4
 
5
- 一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 插件,在 Web 会话头部直接显示你的 **DeepSeek 账户余额**、**当前会话(本轮对话)的花费**,以及**今日所有会话的共花费**;每条已完成的回合还会在消息操作行**行尾**以静态金额显示**本轮花费**,详情面板底部带**今日各会话花费排行**。
5
+ 一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 插件,在 Web 会话头部直接显示你的 **DeepSeek 账户余额**与**今日所有会话的合计花费**,而**当前会话(本轮对话)自己的计费花费**显示在输入框下方的 pill 上;每条已完成的回合还会在消息操作行**行尾**以静态金额显示**本轮花费**,详情面板底部带**今日各会话花费排行**。
6
6
 
7
7
  > 余额是 `GET /user/balance` 的真实数字;会话花费、本轮花费与今日花费是按官方峰/谷单价对每条消息的计费 token 逐条计价的结果,不是计费承诺。
8
8
 
9
9
  ## 显示什么
10
10
 
11
- - **会话头部徽标** —— 两行:剩余余额(`剩余金额:¥X`,是面板标题去掉 `API` 前缀的短版,徽标较窄)+ 本轮对话的计费花费(`本会话花费:¥X`,含本次对话委派的每个子代理会话,文案与面板完全一致)。
12
- - **详情面板** —— 剩余金额(后跟按余额序列算出的今日消费,见「会话花费是怎么算的」);今日计费 token 数与今日所有会话合计同一行(`今日 Token` / `今日花费`,token 数按 DSH 的紧凑记数法带自己的单位,如 `12.2K tok`,token 数值后紧跟当天的缓存命中率(裸数字、无括号)——按 DSH 官方命中率规则渲染:整数百分比,只有部分命中会被凑到 100% 时才逐位多留小数;当天没有 prompt 侧输入就不显示);紧跟该行下面是**今日两行桶明细**——token 在上、花费在下——两行各自成行、各自保持自然的 ` · ` 间距(两行之间不做列对齐),排版沿用下方模型分项那一套,行距用第三部分(今日会话花费排行)那一套紧凑节奏;**面板上所有花费金额都按三位有效数字渲染**(`¥9.58`),但**不细于四位小数**——低于 ¥0.0001 的金额显示 `¥0`;只有余额行保留四位小数;下一行才是本会话花费(`本会话花费`——本会话加上它委派的子代理会话,模型分项同样合并;金额后**只在这段对话不是今天才创建时**才紧跟今日份金额 `¥X`——判定用的是会话自身的创建日(子代理读取给出 `crossedDay`),不再比较两个金额,因为实时的本会话数字与 60 秒缓存的排行行在回合中途本来就会不一致;以及每个模型一行的花费分项(用 DSH 自己的桶名与行序:`未缓存输入 ¥X · 缓存读取 ¥Y · 输出 ¥Z`),外加手动刷新按钮与「?」上的花费说明(一句估算口径,一行说明金额含本会话委派的子代理会话,一行说明紧跟的金额是本次对话今日花费,最后一行顶格是当前插件版本号,如 `v0.3.13`);面板底部是**今日会话花费排行**:按今日花费从高到低排列的会话列表——一行就是一个对话,子代理会话的花费已并入委派它的那个会话行(会话名取日志中的中文标题,重命名后自动同步;最多显示前 10 条,其余以「…还有 N 个会话」提示)。
11
+ - **会话头部徽标** —— 两行:剩余余额(`剩余金额:¥X`,是面板标题去掉 `API` 前缀的短版,徽标较窄)+ 今日所有会话的合计花费(`今日花费:¥X`,文案与面板逐字一致——徽标第二行与面板那一行读的是同一份 state,所以不可能互相矛盾)。它是账户级的:只在当日自身的读取(挂载、手动刷新、回合定稿、打开面板)时变动,不随回合实时跳动。
12
+ - **详情面板** —— 剩余金额(后跟按余额序列算出的今日消费,见「会话花费是怎么算的」);今日计费 token 数与今日所有会话合计同一行(`今日 Token` / `今日花费`,token 数按 DSH 的紧凑记数法带自己的单位,如 `12.2K tok`,token 数值后紧跟当天的缓存命中率(裸数字、无括号)——按 DSH 官方命中率规则渲染:整数百分比,只有部分命中会被凑到 100% 时才逐位多留小数;当天没有 prompt 侧输入就不显示);紧跟该行下面是**今日两行桶明细**——token 在上、花费在下——两行各自成行、各自保持自然的 ` · ` 间距(两行之间不做列对齐),字体沿用模型分项行那一套,行距用第三部分(今日会话花费排行)那一套紧凑节奏(6px);**面板上所有花费金额都按三位有效数字渲染**(`¥9.58`),但**不细于四位小数**——低于 ¥0.0001 的金额显示 `¥0`;只有余额行保留四位小数;外加手动刷新按钮与「?」上的花费说明(一句估算口径,一行说明会话金额含它委派的子代理会话,最后一行顶格是当前插件版本号,如 `v0.3.13`);面板底部是**今日会话花费排行**:按今日花费从高到低排列的会话列表——一行就是一个对话,子代理会话的花费已并入委派它的那个会话行(会话名取日志中的中文标题,重命名后自动同步;最多显示前 10 条,其余以「…还有 N 个会话」提示)。
13
13
  - **本轮花费金额** —— 每条已完成回合的收尾消息操作行**行尾**(时钟之后)显示纯静态的 `¥X`:不可点击、无图标、无「花费」字样、不弹卡片,字体样式逐项复刻时钟文本(13px 次级字号、tertiary 色、nowrap),并且**始终显示**(不随悬停隐藏,与时钟文本一致——整行的悬停显隐规则让两者同进退);回合没有 DeepSeek 用量(花费为 0)或加载失败时不显示。
14
+ - **输入框花费 pill** —— 输入框自己的统计行(官方 time/token pill 所在那条)多一枚:本插件的「环 + 四角星」标记 + 本会话的计费花费,精度与徽标同为三位有效数字;点击向上弹出花费卡片,三行就是花费的三个计费桶(未缓存输入 / 缓存读取 / 输出),文案沿用 DSH token 卡那一套。金额是**整段对话**的——本会话加上它委派的子代理会话,合并方式与主机自己的汇总一致;它是**唯一**显示会话自身花费的界面(徽标第二行与面板报的都是当日口径),所以任何地方都没计价的会话不显示 pill,而自身没计价、委派的子代理却烧了钱的会话照常显示。
14
15
  - **失败与空态** —— 会话或今日没有可计价消耗时显示「暂无消耗记录」而不是编造数字;未配置 key、凭据被拒或传输错误时显示弱化的「额度不可用」,其提示携带 Remote 自己的错误信息。
15
16
 
16
17
  ## 数据更新机制
17
18
 
18
- - **会话花费自动跟随** —— 主机端把每条已提交事件计价进每会话投影(`billingTodaySpend`)并推送给浏览器,**本会话自身的花费**因此零 Remote 调用、实时更新;投影注册表不存在时回退到 Remote 读取,而 `billing/getDelegatedSpend` 再把本会话委派的子代理会话加上去(挂载时、手动刷新时与回合结束时各拉一次),所以显示的是整次对话的金额。**今日花费**(与同一行的**今日 Token**)在回合结束时重算(聚合、排行与子代理小计共用同一次扫描),本会话花费行紧跟的今日份金额也来自这次排行读取(不额外发请求,代价是与排行同进同退);每条消息的行尾金额来自**每会话一次批量拉取**,不再逐条消息各发一次请求。
19
- - **额度有缓存、可见时每 5 分钟轮询** —— 主机端 15 秒内复用同一份 `/user/balance` 快照(手动刷新强制取新),单次请求 5 秒超时;浏览器保留最后一次结果,切会话时立即渲染旧值并在后台校验。此外**页面可见时每 5 分钟轮询一次**(页面隐藏则暂停,回到前台立刻补一次)——下面那条「余额口径的今日消费」以当天第一次采样到的余额为基准,这个节奏就是它的分辨率。
19
+ - **会话花费自动跟随** —— 主机端把每条已提交事件计价进每会话投影(`billingTodaySpend`)并推送给浏览器,**本会话自身的花费**因此零 Remote 调用、实时更新——显示在输入框 pill 上,那是展示它的界面;投影注册表不存在时回退到 Remote 读取,而 `billing/getDelegatedSpend` 再把本会话委派的子代理会话加上去(挂载时、切换会话时、手动刷新时与回合结束时各拉一次),所以显示的是整次对话的金额。**今日花费**(徽标第二行与面板那一行,与同一行的**今日 Token**)在回合结束时重算(聚合、排行与子代理小计共用同一次扫描);每条消息的行尾金额来自**每会话一次批量拉取**,不再逐条消息各发一次请求。
20
+ - **切会话只重取会话自己的两条线** —— 余额与今日花费都不随「切到哪个会话」变化,所以切换时不再重取,只有 `billing/getSessionSpend` 与 `billing/getDelegatedSpend` 会重发。**回合结束与手动刷新会带 `force` 要一个新值**,所以今日行不会把上一轮的花费拖到下一轮才显示;**打开详情面板**也会补读今日行,因此闲置时来回切会话不会把它落下。不带 `force` 的读取则先拿到手上的值、重扫在背后跑——凡不是用户刚造成的读取,都不会等当天那次全会话扫描。
21
+ - **额度有缓存、可见时每 5 分钟轮询** —— 主机端 15 秒内复用同一份 `/user/balance` 快照(手动刷新强制取新),单次请求 5 秒超时;浏览器保留最后一次结果,切会话时立即渲染旧值。此外**页面可见时每 5 分钟轮询一次**(页面隐藏则暂停,回到前台立刻补一次)——下面那条「余额口径的今日消费」以当天第一次采样到的余额为基准,这个节奏就是它的分辨率。
20
22
  - **刷新期间旧值保留** —— 刷新失败保留上一次有效值,不会清空。
21
23
 
22
24
  ## 显示样式
23
25
 
24
26
  真实会话中的会话头部徽标与展开的详情面板:
25
27
 
26
- ![计费插件总览:会话头部徽标与详情面板(剩余金额、今日 Token 与今日花费、本会话花费、按模型分项与今日会话花费排行)](preview-overview.png)
28
+ ![计费插件总览:会话头部徽标与详情面板(剩余金额、今日 Token 与今日花费、今日桶明细行与今日会话花费排行)](preview-overview.png)
27
29
 
28
- 详情面板特写 —— `API 剩余金额`、`今日 Token` 与 `今日花费`、`本会话花费`(跨天时含括号内的今日份金额)、按模型分项(`未缓存输入 · 缓存读取 · 输出`)与今日会话花费排行:
30
+ 详情面板特写 —— `API 剩余金额`、`今日 Token` 与 `今日花费`、今日桶明细行(`未缓存输入 · 缓存读取 · 输出`)与今日会话花费排行:
29
31
 
30
- ![详情面板特写:API 剩余金额、今日 Token 与今日花费、本会话花费(含括号内的今日份金额)、未缓存输入/缓存读取/输出分项与今日会话花费排行](preview-detail.png)
32
+ ![详情面板特写:API 剩余金额、今日 Token 与今日花费、未缓存输入/缓存读取/输出桶明细行与今日会话花费排行](preview-detail.png)
31
33
 
32
34
  本轮花费金额特写 —— 操作行行尾(时钟之后)的静态 `¥` 金额:
33
35
 
@@ -39,7 +41,16 @@
39
41
  | 包 | 侧 | 作用 |
40
42
  | --- | --- | --- |
41
43
  | [`packages/llm-billing`](packages/llm-billing) —— `@rayadesu/dsh-llm-billing` | 主机端 | 负责 `/user/balance` 传输与峰/谷计价表。对外暴露 `billing` Remote(`getBalance(force?)`、`getSessionSpend`、`getTodaySpend`、`getTodaySessionsSpend`、`getTurnSpend`、`getSessionTurnSpends`),并注册客户端可见的 `billingTodaySpend` 投影单元。 |
42
- | [`packages/ui-billing`](packages/ui-billing) —— `@rayadesu/dsh-client-ui-billing` | 浏览器端 | 自己挂载 `billing` Remote,并贡献会话头部徽标与详情面板、消息操作行行尾的静态本轮花费金额。 |
44
+ | [`packages/ui-billing`](packages/ui-billing) —— `@rayadesu/dsh-client-ui-billing` | 浏览器端 | 自己挂载 `billing` Remote,并贡献会话头部徽标与详情面板、消息操作行行尾的静态本轮花费金额、以及输入框下方统计行里的花费 pill(与官方 token pill 并排,金额 = 本会话自身 + 委派的子代理)。 |
45
+
46
+ ### 插件管理页的展示元数据
47
+
48
+ 侧栏「插件」页与 设置 → 插件 清单里,每一项的标题、描述与图标都取自**包自身**的文件——宿主不读插件代码:
49
+
50
+ - `locale/en.json` 与 `locale/zh.json` —— 形状 `{"meta": {"title": …, "description": …}}`,按当前界面语言解析、回落英文。
51
+ - `icon.svg` —— 自包含 SVG(不引用任何外部字体或图片;它经 `<img>` 以 data URL 渲染),由 `package.json` 顶层的 `icon` 字段声明。条目那圈圆角方块由**页面**绘制(深色 `#151517` + `#3b3b3c` 边框,浅色白底 + 浅灰边框),所以图标本身必须**透明底**——不要把底色画进 SVG,否则浅色主题下会变成一块黑方。
52
+
53
+ 两者都经包的 `exports` 对外暴露,所以声明了 `exports` 的包必须同时导出 `./locale/*.json`:否则 locale 文件会被静默丢弃,条目回落到裸包名。三个包各自带一套 locale 与自己的图标。
43
54
 
44
55
  ## 前置条件
45
56
 
@@ -48,17 +59,26 @@
48
59
 
49
60
  ## 安装
50
61
 
51
- ### 安装(已发布到 npm,一条命令)
62
+ ### 从 Web 插件页安装(官方)
52
63
 
53
- 三个包已发布到 npm 的 `@rayadesu` scope。一条命令同时安装 bundle 与两个插件包
54
- (bundle 把两个插件包声明为 peer 依赖,而 profile 默认不自动安装 peer,所以要显式列出)。
64
+ 三个包已发布到 npm 的 `@rayadesu` scope。bundle 把两个插件包声明为普通
65
+ `dependencies`,所以**只需一个包名即可装全**:pnpm 会把 bundle 的依赖闭包一并装进
66
+ profile,hoisted 的 `node_modules` 让两个组件行名可解析。
67
+
68
+ 1. 侧栏进入 **插件** → **添加插件**。
69
+ 2. 输入 `@rayadesu/dsh-billing`。对话框也接受 GitHub 仓库地址
70
+ (`https://github.com/rayadesune/DeepSeek-Harness-chat-billing`)或本地目录绝对路径
71
+ ——两个插件包本身始终从 npm 解析。
72
+ 3. 选安装源(默认 npm 源或**中国大陆镜像源**),点**安装**,完成后**立即启用**。
73
+
74
+ ### 用命令行安装
55
75
 
56
76
  用哪个 `dsh` 命令取决于你的 dsh 安装方式:
57
77
 
58
78
  - **全局安装** —— 任意目录直接用全局 `dsh`:
59
79
 
60
80
  ```bash
61
- dsh plugin --profile web add @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing
81
+ dsh plugin --profile web add @rayadesu/dsh-billing
62
82
  ```
63
83
 
64
84
  - **源码构建的 dsh**(deepseek-harness 源码目录)—— CLI 只在源码目录里能解析,
@@ -66,7 +86,7 @@
66
86
 
67
87
  ```bash
68
88
  cd deepseek-harness
69
- pnpm dsh plugin --profile web add @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing
89
+ pnpm dsh plugin --profile web add @rayadesu/dsh-billing
70
90
  ```
71
91
 
72
92
  ### pnpm 11 发布龄门槛
@@ -81,7 +101,8 @@ dsh profile 通过 pnpm 安装插件,而 pnpm 11 的供应链发布龄门槛
81
101
  minimumReleaseAge: 0
82
102
  ```
83
103
 
84
- - 或者在 24 小时窗口内用**显式钉版本**安装(显式钉版本可绕开门槛,把 `0.3.0` 换成你要的版本;
104
+ - 或者在 24 小时窗口内用**显式钉版本**安装(显式钉版本可绕开门槛;三个包名都要钉——两个
105
+ 插件包随 bundle 传递安装,也要各自钉住才能过门槛;把 `0.3.0` 换成你要的版本;
85
106
  源码构建的 dsh 用 `pnpm dsh …`,同上):
86
107
 
87
108
  ```bash
@@ -106,12 +127,16 @@ dsh profile 通过 pnpm 安装插件,而 pnpm 11 的供应链发布龄门槛
106
127
 
107
128
  ```sh
108
129
  dsh plugin --profile web list # 列出 web profile 已安装的插件
109
- dsh plugin --profile web add @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing
110
- dsh plugin --profile web remove @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing
130
+ dsh plugin --profile web add @rayadesu/dsh-billing
131
+ dsh plugin --profile web remove @rayadesu/dsh-billing
111
132
  dsh plugin --profile web update # 把插件更新到当前允许的最新版本
112
133
  dsh plugin --profile web update --latest # 忽略声明的版本区间,把所有插件升到最新发布版本
113
134
  ```
114
135
 
136
+ `add`/`remove` 只需 bundle 一个包名——两个插件包作为它的依赖随行安装。按旧版三包方式
137
+ 安装过的 profile 其 `package.json` 里列了全部三个包,而 pnpm 只能 remove 其中列出的
138
+ 依赖:那种 profile 在 `remove` 后把三个包名一并列出即可清掉残留。
139
+
115
140
  `update` 遵循 profile `package.json` 里的版本区间,只在该插件声明的 semver 范围内升级。
116
141
  加上 `--latest`(pnpm `update` 的选项)则忽略这些区间,把所有插件直接升到最新发布的版本——
117
142
  用于在版本可解析后立刻拿到新发布。源码构建的 dsh 要在 deepseek-harness 目录里用
@@ -121,7 +146,7 @@ dsh plugin --profile web update --latest # 忽略声明的版本区间,把所
121
146
 
122
147
  两个插件包把它们依赖的 DeepSeek Harness 包(`@deepseek-ai/cordis`、
123
148
  `@deepseek-ai/dsh-credentials`、`@deepseek-ai/dsh-session` 以及客户端运行时包)
124
- 声明为 `peerDependencies`(`^0.1.7-alpha.2`)。dsh profile 默认不自动安装 peer,所以
149
+ 声明为 `peerDependencies`(`^0.2.0-rc.1`)。dsh profile 默认不自动安装 peer,所以
125
150
  这些由 dsh 安装本身通过 `profiles/node_modules` 回退提供,而不是从 registry 拉取——
126
151
  无需额外安装,安装机也不需要 registry token。
127
152
 
@@ -132,7 +157,7 @@ dsh plugin --profile web update --latest # 忽略声明的版本区间,把所
132
157
  把这些留作外部依赖、运行时由 dsh 安装提供,但本仓库是独立 workspace、要自己解析,所以
133
158
  `ui-billing` 把它们声明为自己的 `devDependencies` 供浏览器半测使用。
134
159
 
135
- 插件按 0.1.7-alpha.2 发布线构建,同时兼容读取两代 DSH 运行时:live `Session` 日志面
160
+ 插件按 0.2.0-rc.1 发布线构建,同时兼容读取两代 DSH 运行时:live `Session` 日志面
136
161
  (0.1.1-rc.2 及以前为 `Session.events` + `header.seedLength`,0.1.2-alpha.4 起为
137
162
  `snapshotEvents()` + `inheritedEventCount`),以及持久化服务面(0.1.1-rc.2 及以前为
138
163
  `inspect`/`listSnapshots`,0.1.2-alpha.5 的 handle 化改造后为
@@ -181,7 +206,7 @@ host 面会从源码重新生成 `lib/typert.host.js` 与 `lib/typert.remote-cli
181
206
  的 name 不一致,`verify` 会在发布前直接失败。
182
207
 
183
208
  typert 生成器只认工作区内已注册协议包里的 `Remote`/`TypertRemoteService` 声明,所以
184
- `packages/typert-protocol` 内嵌了 npm 上 `@deepseek-ai/dsh-typert-protocol@0.1.7-alpha.2` 的
209
+ `packages/typert-protocol` 内嵌了 npm 上 `@deepseek-ai/dsh-typert-protocol@0.2.0-rc.1` 的
185
210
  声明文件;dsh 依赖线升级时,从安装包重新刷新它。
186
211
 
187
212
  生成的每个 strict codec 必须带 `create` 工厂:`dsh-typert-loader` 对没有 `create` 的
@@ -235,8 +260,8 @@ npm publish # @rayadesu/dsh-billing bundle(仓库根)
235
260
  - 每份样本按其**发生时刻**(北京时间)所在的峰/谷时段单价——以及该时刻生效的费率版本——计价,三个桶分别计费(`未缓存输入 ¥X · 缓存读取 ¥Y · 输出 ¥Z`,桶名用 DSH 自己的说法),再按模型汇总。高峰窗口仅周一至周五适用;周末全天按低谷价计费。
236
261
  - **今日花费**按同一个计价规则汇总当天(北京时间自然日)所有会话的事件,**今日 Token** 是同一批计价行的三个计费桶(缓存读取 / 未缓存输入 / 输出)之和,按 DSH 的紧凑记数法加 ` tok` 单位渲染(`517 tok`、`12.2K tok`、`1.2M tok`);事件归属的日期同样按北京时间计算。
237
262
  - **本轮花费**按同一规则计价该回合 `turn/start`..`turn/end` 区间内的事件(定位到收尾消息的会话 id + 消息 id),整会话一趟折出 `messageId → 金额` 映射后下发。
238
- - **今日会话花费排行**按同一规则按会话汇总今日花费(跨天会话只统计今天的部分),从高到低排序;会话名取日志中最后一条 `session/title` 事件(自动生成的中文标题或用户重命名的新标题)。排行排的是**对话**:子代理会话(DSH 在其 header 上盖 `origin: 'subagent'` 与 `delegationDepth`;用户手动分叉两者都没有)会并入委派它的顶层会话那一行,所以一行就是一个对话。每行给出 `total`(这次对话的整日花费,含子代理)与 `ownTotal`(该会话自己的花费);面板里紧跟的今日份金额读的是 `ownTotal`,而归组不改变整日合计。
239
- - 没有费率行的模型不计入(内置价目表覆盖 DSH `llm-deepseek` 目录——V4.1 Flash `deepseek-flash`、V4 Flash、V4 Pro、V4 Flash Vision Exp——外加已退役的 `deepseek-v4.1-flash-expires-on-0910` 预览 id 与 MiMo-V2.5/V2.6 系列;flash 系列各路由同价)。每份样本取**自身时刻生效的费率版本**:基础价目为 DeepSeek **8 月 17 日实行**的费率;**flash 系列**(V4.1 Flash、V4 Flash、V4 Flash Vision Exp 及退役 id)自 **9 月 10 日 12:00(北京时间)** 起降为谷时 0.02 / 1.0 / 4.0 元每百万 token、峰时为其两倍——该时刻之前的样本(含 V4.1 Flash 路由自身的早先用量的)沿用被取代的旧价;**V4 Pro** 路由公告于 **9 月 14 日 12:00(北京时间)** 改由 V4.1 Flash 服务并按其实施费率计费;MiMo 系列不受影响(MiMo-V2.6 于 2026 年 9 月 22 日发布,沿用 V2.5 公布的费率,共用同一组行)。**周末按低谷价计费**的规则按 **8 月 23 日**生效的调整执行。
263
+ - **今日会话花费排行**按同一规则按会话汇总今日花费(跨天会话只统计今天的部分),从高到低排序;会话名取日志中最后一条 `session/title` 事件(自动生成的中文标题或用户重命名的新标题)。排行排的是**对话**:子代理会话(DSH 在其 header 上盖 `origin: 'subagent'` 与 `delegationDepth`;用户手动分叉两者都没有)会并入委派它的顶层会话那一行,所以一行就是一个对话。每行给出 `total`(这次对话的整日花费,含子代理)与 `ownTotal`(该会话自己的花费);归组不改变当日合计。
264
+ - 未列出的模型**不猜费率**:没有费率行就不计价,但会把用量记为 `unpriced` 并按模型告警一次,于是新模型显示 ¥0 时**带着原因**(列出模型名),而不只是一个普通的空日——MiMo-V2.6 在其费率行补齐前正是这个样子。模型 id 由厂家自己定,名字相近不代表价格相近,所以不做任何前缀推断;要计价就在 `billing.models` 加一行。内置价目表覆盖 DSH `llm-deepseek` 目录——V4.1 Flash `deepseek-flash`、V4 Flash、V4 Pro、V4 Flash Vision Exp——外加已退役的 `deepseek-v4.1-flash-expires-on-0910` 预览 id 与 MiMo-V2.5/V2.6 系列;flash 系列各路由同价)。每份样本取**自身时刻生效的费率版本**:基础价目为 DeepSeek **8 月 17 日实行**的费率;**flash 系列**(V4.1 Flash、V4 Flash、V4 Flash Vision Exp 及退役 id)自 **9 月 10 日 12:00(北京时间)** 起降为谷时 0.02 / 1.0 / 4.0 元每百万 token、峰时为其两倍——该时刻之前的样本(含 V4.1 Flash 路由自身的早先用量的)沿用被取代的旧价;**V4 Pro** 路由公告于 **9 月 14 日 12:00(北京时间)** 改由 V4.1 Flash 服务并按其实施费率计费;MiMo 系列不受影响(MiMo-V2.6 于 2026 年 9 月 22 日发布,沿用 V2.5 公布的费率,共用同一组行)。**周末按低谷价计费**的规则按 **8 月 23 日**生效的调整执行。
240
265
 
241
266
  - **余额口径的「今日消费」**(`API 剩余金额` 后紧跟的那个数字)是纯账户加减,不参与上面的 token 计价:当天(本地自然日)第一次查询到的余额 − 当前余额 + 当天识别出的充值(余额上涨按 **10 元步进向上取整**,因为厂商只按整十充值)。这条口径与本插件对标的 `balanceinfo` 程序一致,并且**刻意与计价的「今日花费」分成两个数**——别的客户端花的钱、或本浏览器打开前花的钱,只体现在这里;两个数字不一致是正常的。当日记录写在本浏览器的 `localStorage`(键 `dsh.billing.balance-day.v1`),跨页面刷新与 `dsh` 重启保留,按**本地自然日**翻篇(浏览器所在的日,不是宿主的北京日键);采样发生在挂载、切换会话、手动刷新与页面可见时的 5 分钟轮询,只有当天已经有过一次采样才显示,且不在客户端之间共享。细则见 [`packages/ui-billing/README.zh.md`](packages/ui-billing/README.zh.md)。
242
267
 
@@ -244,9 +269,10 @@ npm publish # @rayadesu/dsh-billing bundle(仓库根)
244
269
 
245
270
  - **有费率行才计价** —— 会话花费、本轮花费与今日花费(以及同一行的今日 Token:token 数也只统计这些行)只统计价目表(`billing.models`)里有的模型。
246
271
  - **按需聚合** —— **今日花费/今日 Token** 与今日会话排行在主机端 60 秒缓存之后计算,且共用同一次扫描;未命中时,活跃会话直接读投影单元,冷会话若缓存行自身的日期不是查询日则零 I/O 直接作答,只有持久化修订变化(或缓存行覆盖查询日)的会话才读日志。读取失败的会话按修订号记住,不再每轮重试。排行的子代理归组在同一趟扫描里完成,不额外读日志;合计仍然对每个会话求和,所以合并只在总额之间归组、不搬钱。
247
- - **排行按需拉取** —— 详情面板打开(或手动刷新)时才拉取排行,徽标一直关着就不会为全量会话扫描买单;「本会话花费」行紧跟的今日份金额也来自这次读取(读该行的 `ownTotal`,合并进来的子代理金额不会被当成这个会话自己的),所以它不额外发请求(代价是排行未落定时不显示这个数字,且最多滞后 60 秒)。
272
+ - **排行按需拉取** —— 详情面板打开(或手动刷新)时才拉取排行,徽标一直关着就不会为全量会话扫描买单;排行最多滞后 60 秒。
248
273
  - **排行只显示前 10** —— 详情面板最多展示前 10 个会话,其余以「…还有 N 个会话」提示;上限在子代理合并之后生效,所以一行就是一个对话。
249
- - **子代理小计随回合更新,不逐事件跟进** —— 会话行里本会话自身那部分是实时的(推送投影),子代理那部分来自 `billing/getDelegatedSpend`:挂载、手动刷新与回合结束时各拉一次,并与今日花费共用同一个 60 秒宿主缓存。因此子代理在回合中途烧掉的钱会在下一次读取(回合结束、手动刷新或打开面板)时落到父会话金额上,而不是逐事件实时跳动。
274
+ - **子代理小计随回合更新,不逐事件跟进** —— pill 里本会话自身那部分是实时的(推送投影),子代理那部分来自 `billing/getDelegatedSpend`:挂载、切换会话、手动刷新与回合结束时各拉一次,并与今日花费共用同一个 60 秒宿主缓存。因此子代理在回合中途烧掉的钱会在下一次读取(回合结束、手动刷新或切换会话)时落到会话金额上,而不是逐事件实时跳动。
275
+ - **徽标第二行不随回合实时跳** —— 今日花费是账户级的,背后没有推送投影,所以这一行只在挂载、手动刷新、打开面板与回合定稿(约 2 秒防抖)时变动;读推送投影的输入框 pill 才是随回合实时跳的界面。
250
276
  - **合并行可能显示「未命名」** —— 子代理的父会话不在本次扫描范围内时(例如父日志已被删除或归档),该行仍按 header 里写的父会话 id 归属,但那份日志从未被读取,标题要等父会话被扫描到才显示。
251
277
  - **本轮花费只出现在已定稿的收尾消息** —— 中断的回合没有操作行,不显示本轮花费;冷会话(投影缓存直接命中)排行标题可能显示「未命名」,待其日志被重新读取后恢复。
252
278
  - **额度在两次轮询之间最多旧 15 秒** —— 主机端最多复用 15 秒内的同一份快照,单次请求 5 秒超时;账户在其他客户端产生消耗时,界面值会在下一次轮询(页面可见时 5 分钟一次,隐藏时没有轮询)、手动刷新或刷新浏览器时变化。
package/cordis.patch.yml CHANGED
@@ -3,16 +3,20 @@
3
3
  # Mounts the host-side billing Remote provider (llm-billing) and the
4
4
  # session-header balance/spend badge (ui-billing). The deepseek-harness
5
5
  # official repo does not ship these packages, so the row names must resolve
6
- # from the profile's own node_modules: install the three published packages
7
- # (bundle + the two plugin packages) in one command (see below).
6
+ # from the profile's own node_modules: the bundle declares both plugin
7
+ # packages as regular `dependencies` (profiles init with `nodeLinker: hoisted`
8
+ # and `autoInstallPeers: false`, so rows must be dependencies, never peers),
9
+ # and one install of the bundle pulls them into the profile's flat
10
+ # node_modules (see below).
8
11
  #
9
12
  # No machine-specific literals: this patch carries only plugin ids and names;
10
13
  # no credentials or paths are needed — the plugins read the DeepSeek API key
11
14
  # from the credential seam / environment at runtime.
12
15
  #
13
- # Install with one command (the bundle plus the two plugin packages, since
14
- # pnpm does not install the bundle's local dependencies into the profile):
15
- # dsh plugin --profile web add @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing
16
+ # Install the bundle package alone — the Web plugin page's "Add plugin" dialog
17
+ # takes the same package name (it also accepts a GitHub URL or a local
18
+ # directory path), or use the CLI:
19
+ # dsh plugin --profile web add @rayadesu/dsh-billing
16
20
  - insert:
17
21
  - id: llm-billing
18
22
  name: '@rayadesu/dsh-llm-billing'
package/icon.svg ADDED
@@ -0,0 +1,14 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48" role="img" aria-label="Billing">
2
+ <defs>
3
+ <linearGradient id="g" x1="24" y1="8.0" x2="24" y2="40.0" gradientUnits="userSpaceOnUse">
4
+ <stop offset="0" stop-color="#2f6fed"/>
5
+ <stop offset=".5" stop-color="#35b8f0"/>
6
+ <stop offset="1" stop-color="#3fddbe"/>
7
+ </linearGradient>
8
+ </defs>
9
+ <circle cx="24" cy="24" r="14.53" fill="none" stroke="url(#g)" stroke-width="2.94"/>
10
+ <path d="M33.5 24.0 L32.9 24.4 L32.0 24.7 L31.1 24.9 L30.5 25.1 L30.0 25.3 L29.5 25.5 L29.1 25.6 L28.8 25.7 L28.5 25.9 L28.3 26.0 L28.1 26.1 L27.9 26.3 L27.7 26.4 L27.6 26.5 L27.4 26.6 L27.2 26.7 L27.1 26.8 L26.9 26.9 L26.8 27.1 L26.7 27.2 L26.6 27.4 L26.5 27.6 L26.4 27.7 L26.3 27.9 L26.1 28.1 L26.0 28.3 L25.9 28.6 L25.8 28.9 L25.6 29.2 L25.5 29.6 L25.3 30.1 L25.2 30.6 L25.0 31.2 L24.7 32.1 L24.4 33.0 L24.0 33.5 L24.0 29.5 L24.2 29.2 L24.4 28.7 L24.6 28.3 L24.7 27.9 L24.8 27.4 L24.8 27.1 L24.9 26.8 L25.0 26.6 L25.0 26.5 L25.1 26.3 L25.1 26.1 L25.2 26.0 L25.2 25.9 L25.3 25.8 L25.4 25.8 L25.4 25.7 L25.5 25.6 L25.5 25.5 L25.6 25.5 L25.7 25.4 L25.8 25.4 L25.8 25.3 L26.0 25.3 L26.1 25.2 L26.2 25.2 L26.4 25.1 L26.6 25.1 L26.8 25.0 L27.0 24.9 L27.3 24.9 L27.6 24.8 L27.9 24.7 L28.3 24.6 L28.7 24.4 L29.3 24.2 L29.5 24.0 Z" fill="url(#g)" transform="rotate(0 24 24)"/>
11
+ <path d="M33.5 24.0 L32.9 24.4 L32.0 24.7 L31.1 24.9 L30.5 25.1 L30.0 25.3 L29.5 25.5 L29.1 25.6 L28.8 25.7 L28.5 25.9 L28.3 26.0 L28.1 26.1 L27.9 26.3 L27.7 26.4 L27.6 26.5 L27.4 26.6 L27.2 26.7 L27.1 26.8 L26.9 26.9 L26.8 27.1 L26.7 27.2 L26.6 27.4 L26.5 27.6 L26.4 27.7 L26.3 27.9 L26.1 28.1 L26.0 28.3 L25.9 28.6 L25.8 28.9 L25.6 29.2 L25.5 29.6 L25.3 30.1 L25.2 30.6 L25.0 31.2 L24.7 32.1 L24.4 33.0 L24.0 33.5 L24.0 29.5 L24.2 29.2 L24.4 28.7 L24.6 28.3 L24.7 27.9 L24.8 27.4 L24.8 27.1 L24.9 26.8 L25.0 26.6 L25.0 26.5 L25.1 26.3 L25.1 26.1 L25.2 26.0 L25.2 25.9 L25.3 25.8 L25.4 25.8 L25.4 25.7 L25.5 25.6 L25.5 25.5 L25.6 25.5 L25.7 25.4 L25.8 25.4 L25.8 25.3 L26.0 25.3 L26.1 25.2 L26.2 25.2 L26.4 25.1 L26.6 25.1 L26.8 25.0 L27.0 24.9 L27.3 24.9 L27.6 24.8 L27.9 24.7 L28.3 24.6 L28.7 24.4 L29.3 24.2 L29.5 24.0 Z" fill="url(#g)" transform="rotate(90 24 24)"/>
12
+ <path d="M33.5 24.0 L32.9 24.4 L32.0 24.7 L31.1 24.9 L30.5 25.1 L30.0 25.3 L29.5 25.5 L29.1 25.6 L28.8 25.7 L28.5 25.9 L28.3 26.0 L28.1 26.1 L27.9 26.3 L27.7 26.4 L27.6 26.5 L27.4 26.6 L27.2 26.7 L27.1 26.8 L26.9 26.9 L26.8 27.1 L26.7 27.2 L26.6 27.4 L26.5 27.6 L26.4 27.7 L26.3 27.9 L26.1 28.1 L26.0 28.3 L25.9 28.6 L25.8 28.9 L25.6 29.2 L25.5 29.6 L25.3 30.1 L25.2 30.6 L25.0 31.2 L24.7 32.1 L24.4 33.0 L24.0 33.5 L24.0 29.5 L24.2 29.2 L24.4 28.7 L24.6 28.3 L24.7 27.9 L24.8 27.4 L24.8 27.1 L24.9 26.8 L25.0 26.6 L25.0 26.5 L25.1 26.3 L25.1 26.1 L25.2 26.0 L25.2 25.9 L25.3 25.8 L25.4 25.8 L25.4 25.7 L25.5 25.6 L25.5 25.5 L25.6 25.5 L25.7 25.4 L25.8 25.4 L25.8 25.3 L26.0 25.3 L26.1 25.2 L26.2 25.2 L26.4 25.1 L26.6 25.1 L26.8 25.0 L27.0 24.9 L27.3 24.9 L27.6 24.8 L27.9 24.7 L28.3 24.6 L28.7 24.4 L29.3 24.2 L29.5 24.0 Z" fill="url(#g)" transform="rotate(180 24 24)"/>
13
+ <path d="M33.5 24.0 L32.9 24.4 L32.0 24.7 L31.1 24.9 L30.5 25.1 L30.0 25.3 L29.5 25.5 L29.1 25.6 L28.8 25.7 L28.5 25.9 L28.3 26.0 L28.1 26.1 L27.9 26.3 L27.7 26.4 L27.6 26.5 L27.4 26.6 L27.2 26.7 L27.1 26.8 L26.9 26.9 L26.8 27.1 L26.7 27.2 L26.6 27.4 L26.5 27.6 L26.4 27.7 L26.3 27.9 L26.1 28.1 L26.0 28.3 L25.9 28.6 L25.8 28.9 L25.6 29.2 L25.5 29.6 L25.3 30.1 L25.2 30.6 L25.0 31.2 L24.7 32.1 L24.4 33.0 L24.0 33.5 L24.0 29.5 L24.2 29.2 L24.4 28.7 L24.6 28.3 L24.7 27.9 L24.8 27.4 L24.8 27.1 L24.9 26.8 L25.0 26.6 L25.0 26.5 L25.1 26.3 L25.1 26.1 L25.2 26.0 L25.2 25.9 L25.3 25.8 L25.4 25.8 L25.4 25.7 L25.5 25.6 L25.5 25.5 L25.6 25.5 L25.7 25.4 L25.8 25.4 L25.8 25.3 L26.0 25.3 L26.1 25.2 L26.2 25.2 L26.4 25.1 L26.6 25.1 L26.8 25.0 L27.0 24.9 L27.3 24.9 L27.6 24.8 L27.9 24.7 L28.3 24.6 L28.7 24.4 L29.3 24.2 L29.5 24.0 Z" fill="url(#g)" transform="rotate(270 24 24)"/>
14
+ </svg>
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Billing",
4
+ "description": "View balance, spend (per message, per conversation), and session spend rankings."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "计费",
4
+ "description": "查看余额,消费(每条消息,每次对话)与会话消费排行。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@rayadesu/dsh-billing",
3
- "version": "0.3.16",
3
+ "version": "0.3.18",
4
4
  "description": "DeepSeek Harness billing plugin: account balance and this session's billed spend with a session-header badge.",
5
+ "icon": "./icon.svg",
5
6
  "license": "MIT",
6
7
  "repository": {
7
8
  "type": "git",
@@ -25,8 +26,15 @@
25
26
  "test": "vitest run",
26
27
  "verify": "node scripts/verify-packages.mjs"
27
28
  },
29
+ "exports": {
30
+ "./package.json": "./package.json",
31
+ "./locale/*.json": "./locale/*.json",
32
+ "./cordis.patch.yml": "./cordis.patch.yml"
33
+ },
28
34
  "files": [
29
35
  "cordis.patch.yml",
36
+ "locale/*.json",
37
+ "icon.svg",
30
38
  "README.md",
31
39
  "README.zh.md",
32
40
  "README.i18n.yaml",
@@ -39,14 +47,14 @@
39
47
  "publishConfig": {
40
48
  "access": "public"
41
49
  },
42
- "peerDependencies": {
43
- "@rayadesu/dsh-client-ui-billing": "^0.3.16",
44
- "@rayadesu/dsh-llm-billing": "^0.3.16"
50
+ "dependencies": {
51
+ "@rayadesu/dsh-client-ui-billing": "^0.3.18",
52
+ "@rayadesu/dsh-llm-billing": "^0.3.18"
45
53
  },
46
54
  "devDependencies": {
47
55
  "@deepseek-ai/cordis": "^4.0.4",
48
- "@deepseek-ai/dsh-invariants": "^0.1.7-alpha.2",
49
- "@deepseek-ai/dsh-typert-generator": "^0.1.7-alpha.2",
56
+ "@deepseek-ai/dsh-invariants": "^0.2.0-rc.1",
57
+ "@deepseek-ai/dsh-typert-generator": "^0.2.0-rc.1",
50
58
  "@eslint/js": "^10.0.1",
51
59
  "@types/node": "^22.20.0",
52
60
  "eslint": "^10.9.1",