@rayadesu/dsh-client-ui-billing 0.1.0-rc.8 → 0.2.0

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.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: 509fb81169ba38ef35a3a364eb88765456143443
6
- README.zh.md: 19fa2ed3c3f4692c60945c614c44c5b832075100
5
+ README.md: 8672854e338576b027810cbfe8bf1a976ffa0824
6
+ README.zh.md: 6c7e0cbd95094d437535def3f2d5d01d5bc98f6c
package/README.md CHANGED
@@ -4,7 +4,7 @@ English | [中文](README.zh.md)
4
4
 
5
5
  Web billing feature owner: contributes one entry to `conversation.session.header.utilities` that mounts its own `billing` Remote, reads the DeepSeek account balance, this session's billed spend, and today's spend across every session through `billing/getBalance`, `billing/getSessionSpend`, and `billing/getTodaySpend`, and renders them as a top-right label box. The host half of the capability lives in [`dsh-llm-billing`](../llm-billing/README.md), which owns the `/user/balance` transport, the peak/off-peak pricing table, and the `billing` Remote namespace; this package mounts that Remote and renders what it returns.
6
6
 
7
- The trigger shows two lines — the remaining balance and this conversation's billed spend ("本轮对话花费 ¥X") — and renders nothing while the first fetch is in flight. Clicking it opens a label box with the remaining amount, this session's billed spend (本会话花费, priced per message at the official peak/off-peak rate) with today's all-session spend beside it (今日共花费), one priced row per model with the cache-hit / cache-miss-input / output cost breakdown ("缓存命中 ¥X · 未命中输入 ¥Y · 输出 ¥Z"), a manual refresh action, and a spend disclaimer. Refreshing keeps the last values visible rather than blanking them, and a refresh failure retains the last good value. A session or day without priced usage shows a "no usage recorded" word rather than a fabricated figure. A failure — no API key configured, a rejected credential, a transport error — renders a muted "Balance unavailable" word whose tooltip carries the Remote's own error message.
7
+ The trigger shows two lines — the remaining balance and this conversation's billed spend ("本轮对话花费 ¥X") — and renders nothing while the first fetch is in flight. Clicking it opens a label box with the remaining amount, this session's billed spend (本会话花费, priced per message at the official peak/off-peak rate — peak windows apply weekdays Mon–Fri only, weekends are off-peak) with today's all-session spend beside it (今日共花费), one priced row per model with the cache-hit / cache-miss-input / output cost breakdown ("缓存命中 ¥X · 未命中输入 ¥Y · 输出 ¥Z"), a manual refresh action, and a spend disclaimer. Refreshing keeps the last values visible rather than blanking them, and a refresh failure retains the last good value. A session or day without priced usage shows a "no usage recorded" word rather than a fabricated figure. A failure — no API key configured, a rejected credential, a transport error — renders a muted "Balance unavailable" word whose tooltip carries the Remote's own error message.
8
8
 
9
9
  The badge is account-level even though the slot is session-scoped: the header utilities row is simply the one always-visible title-bar seat the shell offers, and the balance it shows does not vary by session — but the spend lines do, because they price the current session's own token usage through `billing/getSessionSpend` and every session's usage through `billing/getTodaySpend`. Copy goes through the package's own `billing` locale namespace; styling uses tokens only.
10
10
 
@@ -12,9 +12,9 @@ The badge is account-level even though the slot is session-scoped: the header ut
12
12
 
13
13
  The spends follow the conversation and the balance stays a manual snapshot:
14
14
 
15
- - **Spends follow new messages** — the component subscribes to the current session's message count through the framework `useSession` seat. When a new message lands, it recomputes this session's spend (`billing/getSessionSpend`) and today's spend (`billing/getTodaySpend`), local pricing passes with no network request, so the spend lines stay live during an ongoing conversation.
15
+ - **Spends follow new messages, debounced** — the component subscribes to the current session's message count through the framework `useSession` seat. When a new message lands, it recomputes this session's spend (`billing/getSessionSpend`) and today's spend (`billing/getTodaySpend`), local pricing passes with no network request, so the spend lines stay live during an ongoing conversation. The recompute is debounced for two seconds, so a burst of messages (a streaming agent turn) prices once instead of once per message; the host-side cache (see `dsh-llm-billing`) then serves the first miss for the rest of the minute.
16
16
  - **Balance is manual** — the account balance is fetched on mount, on session switch, and on the explicit refresh action (`billing/getBalance`). There is no polling and no automatic refetch: the balance line only changes when one of those events happens.
17
- - **Refresh keeps the last values** — an in-flight refresh leaves the previous values on screen, and a failed refresh retains the last good value instead of blanking it.
17
+ - **Refresh keeps the last values** — an in-flight refresh leaves the previous values on screen, and a failed refresh retains the last good value instead of blanking it. The refresh action calls `billing/getTodaySpend(true)`, which bypasses the host-side cache; the message-triggered recompute and the mount/session-switch read use the cached path.
18
18
 
19
19
  ## Model Experience
20
20
 
package/README.zh.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Web 额度特性的归属方:向 `conversation.session.header.utilities` 贡献一个条目,自己挂载 `billing` Remote,通过 `billing/getBalance`、`billing/getSessionSpend` 与 `billing/getTodaySpend` 读取 DeepSeek 账户余额、本会话的计费花费与今日所有会话的计费花费,并在右上角渲染成一个标签框。该能力的宿主侧在 [`dsh-llm-billing`](../llm-billing/README.md),它拥有 `/user/balance` 传输、峰谷计价表与 `billing` Remote 命名空间;本包挂载那个 Remote 并渲染它返回的内容。
6
6
 
7
- 触发器显示两行——剩余额度与本轮对话的计费花费(「本轮对话花费 ¥X」)——首个请求在途时不渲染任何东西。点击后展开一个标签框:剩余金额、本会话的计费花费(本会话花费,按官方峰/谷单价逐条消息计价,右侧并列今日所有会话的合计「今日共花费」)、每个模型一行的花费及其缓存命中/未命中输入/输出分项(「缓存命中 ¥X · 未命中输入 ¥Y · 输出 ¥Z」)、手动刷新按钮与花费说明。刷新时旧值不消失,刷新失败保留上一次有效值。没有可计价消耗的会话或日子显示「暂无消耗记录」而不是编造数字。失败——未配置 API key、凭据被拒绝、传输错误——渲染弱化的「额度不可用」,其提示携带 Remote 自己的错误信息。
7
+ 触发器显示两行——剩余额度与本轮对话的计费花费(「本轮对话花费 ¥X」)——首个请求在途时不渲染任何东西。点击后展开一个标签框:剩余金额、本会话的计费花费(本会话花费,按官方峰/谷单价逐条消息计价——高峰窗口仅周一至周五适用,周末按低谷价——右侧并列今日所有会话的合计「今日共花费」)、每个模型一行的花费及其缓存命中/未命中输入/输出分项(「缓存命中 ¥X · 未命中输入 ¥Y · 输出 ¥Z」)、手动刷新按钮与花费说明。刷新时旧值不消失,刷新失败保留上一次有效值。没有可计价消耗的会话或日子显示「暂无消耗记录」而不是编造数字。失败——未配置 API key、凭据被拒绝、传输错误——渲染弱化的「额度不可用」,其提示携带 Remote 自己的错误信息。
8
8
 
9
9
  角标是账户级的,尽管插槽是会话作用域:顶栏工具行不过是外壳提供的那个始终可见的标题栏席位,余额并不随会话变化——但花费行会,因为它通过 `billing/getSessionSpend` 按当前会话自己的 token 用量计价、通过 `billing/getTodaySpend` 按所有会话的用量计价。文案走本包自己的 `billing` locale 命名空间;样式只用 token。
10
10
 
@@ -12,9 +12,9 @@ Web 额度特性的归属方:向 `conversation.session.header.utilities` 贡
12
12
 
13
13
  花费跟随会话、额度保持手动快照:
14
14
 
15
- - **花费随新消息自动更新** —— 组件通过框架 `useSession` 座位订阅当前会话的消息数。每到达一条新消息,就只重算**本会话的花费**(`billing/getSessionSpend`)与**今日共花费**(`billing/getTodaySpend`,纯本地计价、不发网络请求),连续对话时花费行实时跟随。
15
+ - **花费随新消息自动更新(去抖)** —— 组件通过框架 `useSession` 座位订阅当前会话的消息数。每到达一条新消息,就只重算**本会话的花费**(`billing/getSessionSpend`)与**今日共花费**(`billing/getTodaySpend`,纯本地计价、不发网络请求),连续对话时花费行实时跟随。重算有 2 秒去抖,消息突发(流式 agent 回合)只计价一次;宿主侧缓存(见 `dsh-llm-billing`)随后在一分钟内服务首次未命中。
16
16
  - **额度保持手动** —— 账户余额在挂载、切换会话、点刷新按钮时拉取(`billing/getBalance`)。**没有轮询、不会自动重拉**:余额行只在这几个时机变化。
17
- - **刷新保留旧值** —— 刷新在途时旧值留在屏幕上,刷新失败保留上一次有效值,不清空。
17
+ - **刷新保留旧值** —— 刷新在途时旧值留在屏幕上,刷新失败保留上一次有效值,不清空。刷新按钮调用 `billing/getTodaySpend(true)`,绕过宿主侧缓存;消息触发的重算与挂载/切换会话的读取走缓存路径。
18
18
 
19
19
  ## 模型体验
20
20
 
package/lib/client.js CHANGED
@@ -613,6 +613,7 @@ window.__ModuleLoader__.load({
613
613
  const integer = /^-?\d+$/;
614
614
  const number$1 = /^-?\d+(?:\.\d+)?$/;
615
615
  const boolean$1 = /^(?:true|false)$/i;
616
+ const _undefined$2 = /^undefined$/i;
616
617
  const lowercase = /^[^A-Z]*$/;
617
618
  const uppercase = /^[^a-z]*$/;
618
619
  //#endregion
@@ -1425,6 +1426,22 @@ window.__ModuleLoader__.load({
1425
1426
  return payload;
1426
1427
  };
1427
1428
  });
1429
+ const $ZodUndefined = /*@__PURE__*/ $constructor("$ZodUndefined", (inst, def) => {
1430
+ $ZodType.init(inst, def);
1431
+ inst._zod.pattern = _undefined$2;
1432
+ inst._zod.values = new Set([void 0]);
1433
+ inst._zod.parse = (payload, _ctx) => {
1434
+ const input = payload.value;
1435
+ if (typeof input === "undefined") return payload;
1436
+ payload.issues.push({
1437
+ expected: "undefined",
1438
+ code: "invalid_type",
1439
+ input,
1440
+ inst
1441
+ });
1442
+ return payload;
1443
+ };
1444
+ });
1428
1445
  const $ZodUnknown = /*@__PURE__*/ $constructor("$ZodUnknown", (inst, def) => {
1429
1446
  $ZodType.init(inst, def);
1430
1447
  inst._zod.parse = (payload) => payload;
@@ -1886,6 +1903,24 @@ window.__ModuleLoader__.load({
1886
1903
  return payload;
1887
1904
  };
1888
1905
  });
1906
+ const $ZodLiteral = /*@__PURE__*/ $constructor("$ZodLiteral", (inst, def) => {
1907
+ $ZodType.init(inst, def);
1908
+ if (def.values.length === 0) throw new Error("Cannot create literal schema with no valid values");
1909
+ const values = new Set(def.values);
1910
+ inst._zod.values = values;
1911
+ inst._zod.pattern = new RegExp(`^(${def.values.map((o) => typeof o === "string" ? escapeRegex(o) : o ? escapeRegex(o.toString()) : String(o)).join("|")})$`);
1912
+ inst._zod.parse = (payload, _ctx) => {
1913
+ const input = payload.value;
1914
+ if (values.has(input)) return payload;
1915
+ payload.issues.push({
1916
+ code: "invalid_value",
1917
+ values: def.values,
1918
+ input,
1919
+ inst
1920
+ });
1921
+ return payload;
1922
+ };
1923
+ });
1889
1924
  const $ZodTransform = /*@__PURE__*/ $constructor("$ZodTransform", (inst, def) => {
1890
1925
  $ZodType.init(inst, def);
1891
1926
  inst._zod.optin = "optional";
@@ -2464,6 +2499,13 @@ window.__ModuleLoader__.load({
2464
2499
  });
2465
2500
  }
2466
2501
  // @__NO_SIDE_EFFECTS__
2502
+ function _undefined$1(Class, params) {
2503
+ return new Class({
2504
+ type: "undefined",
2505
+ ...normalizeParams(params)
2506
+ });
2507
+ }
2508
+ // @__NO_SIDE_EFFECTS__
2467
2509
  function _unknown(Class) {
2468
2510
  return new Class({ type: "unknown" });
2469
2511
  }
@@ -3012,6 +3054,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
3012
3054
  const booleanProcessor = (_schema, _ctx, json, _params) => {
3013
3055
  json.type = "boolean";
3014
3056
  };
3057
+ const undefinedProcessor = (_schema, ctx, _json, _params) => {
3058
+ if (ctx.unrepresentable === "throw") throw new Error("Undefined cannot be represented in JSON Schema");
3059
+ };
3015
3060
  const neverProcessor = (_schema, _ctx, json, _params) => {
3016
3061
  json.not = {};
3017
3062
  };
@@ -3022,6 +3067,27 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
3022
3067
  if (values.every((v) => typeof v === "string")) json.type = "string";
3023
3068
  json.enum = values;
3024
3069
  };
3070
+ const literalProcessor = (schema, ctx, json, _params) => {
3071
+ const def = schema._zod.def;
3072
+ const vals = [];
3073
+ for (const val of def.values) if (val === void 0) {
3074
+ if (ctx.unrepresentable === "throw") throw new Error("Literal `undefined` cannot be represented in JSON Schema");
3075
+ } else if (typeof val === "bigint") if (ctx.unrepresentable === "throw") throw new Error("BigInt literals cannot be represented in JSON Schema");
3076
+ else vals.push(Number(val));
3077
+ else vals.push(val);
3078
+ if (vals.length === 0) {} else if (vals.length === 1) {
3079
+ const val = vals[0];
3080
+ json.type = val === null ? "null" : typeof val;
3081
+ if (ctx.target === "draft-04" || ctx.target === "openapi-3.0") json.enum = [val];
3082
+ else json.const = val;
3083
+ } else {
3084
+ if (vals.every((v) => typeof v === "number")) json.type = "number";
3085
+ if (vals.every((v) => typeof v === "string")) json.type = "string";
3086
+ if (vals.every((v) => typeof v === "boolean")) json.type = "boolean";
3087
+ if (vals.every((v) => v === null)) json.type = "null";
3088
+ json.enum = vals;
3089
+ }
3090
+ };
3025
3091
  const customProcessor = (_schema, ctx, _json, _params) => {
3026
3092
  if (ctx.unrepresentable === "throw") throw new Error("Custom types cannot be represented in JSON Schema");
3027
3093
  };
@@ -3648,6 +3714,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
3648
3714
  function boolean(params) {
3649
3715
  return /* @__PURE__ */ _boolean(ZodBoolean, params);
3650
3716
  }
3717
+ const ZodUndefined = /*@__PURE__*/ $constructor("ZodUndefined", (inst, def) => {
3718
+ $ZodUndefined.init(inst, def);
3719
+ ZodType.init(inst, def);
3720
+ inst._zod.processJSONSchema = (ctx, json, params) => undefinedProcessor(inst, ctx, json, params);
3721
+ });
3722
+ function _undefined(params) {
3723
+ return /* @__PURE__ */ _undefined$1(ZodUndefined, params);
3724
+ }
3651
3725
  const ZodUnknown = /*@__PURE__*/ $constructor("ZodUnknown", (inst, def) => {
3652
3726
  $ZodUnknown.init(inst, def);
3653
3727
  ZodType.init(inst, def);
@@ -3823,6 +3897,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
3823
3897
  ...normalizeParams(params)
3824
3898
  });
3825
3899
  }
3900
+ const ZodLiteral = /*@__PURE__*/ $constructor("ZodLiteral", (inst, def) => {
3901
+ $ZodLiteral.init(inst, def);
3902
+ ZodType.init(inst, def);
3903
+ inst._zod.processJSONSchema = (ctx, json, params) => literalProcessor(inst, ctx, json, params);
3904
+ inst.values = new Set(def.values);
3905
+ Object.defineProperty(inst, "value", { get() {
3906
+ if (def.values.length > 1) throw new Error("This schema contains multiple valid literal values. Use `.values` instead.");
3907
+ return def.values[0];
3908
+ } });
3909
+ });
3910
+ function literal(value, params) {
3911
+ return new ZodLiteral({
3912
+ type: "literal",
3913
+ values: Array.isArray(value) ? value : [value],
3914
+ ...normalizeParams(params)
3915
+ });
3916
+ }
3826
3917
  const ZodTransform = /*@__PURE__*/ $constructor("ZodTransform", (inst, def) => {
3827
3918
  $ZodTransform.init(inst, def);
3828
3919
  ZodType.init(inst, def);
@@ -3990,7 +4081,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
3990
4081
  }
3991
4082
  //#endregion
3992
4083
  //#region ../../llm/llm-billing/lib/typert.remote-client.js
3993
- const _deepseek_ai_dsh_llm_billing_billing_getBalance_result$schema = object({
4084
+ const _rayadesu_dsh_llm_billing_billing_getBalance_result$schema = object({
3994
4085
  "isAvailable": boolean(),
3995
4086
  "lines": array(object({
3996
4087
  "currency": string(),
@@ -3999,8 +4090,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
3999
4090
  "toppedUp": string()
4000
4091
  }))
4001
4092
  });
4002
- const _deepseek_ai_dsh_llm_billing_billing_getSessionSpend_parameter_0$schema = intersection(string(), unknown());
4003
- const _deepseek_ai_dsh_llm_billing_billing_getSessionSpend_result$schema = object({
4093
+ const _rayadesu_dsh_llm_billing_billing_getSessionSpend_parameter_0$schema = intersection(string(), unknown());
4094
+ const _rayadesu_dsh_llm_billing_billing_getSessionSpend_result$schema = object({
4004
4095
  "total": number(),
4005
4096
  "models": array(object({
4006
4097
  "model": string(),
@@ -4016,7 +4107,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4016
4107
  "outputCost": number()
4017
4108
  }))
4018
4109
  });
4019
- const _deepseek_ai_dsh_llm_billing_billing_getTodaySpend_result$schema = object({
4110
+ const _rayadesu_dsh_llm_billing_billing_getTodaySpend_parameter_0$schema = union([
4111
+ _undefined(),
4112
+ literal(false),
4113
+ literal(true)
4114
+ ]);
4115
+ const _rayadesu_dsh_llm_billing_billing_getTodaySpend_result$schema = object({
4020
4116
  "total": number(),
4021
4117
  "models": array(object({
4022
4118
  "model": string(),
@@ -4045,11 +4141,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4045
4141
  result: {
4046
4142
  mode: "strict",
4047
4143
  typeSymbol: "@rayadesu/dsh-llm-billing/types#DeepSeekBalance",
4048
- schema: _deepseek_ai_dsh_llm_billing_billing_getBalance_result$schema
4144
+ schema: _rayadesu_dsh_llm_billing_billing_getBalance_result$schema
4049
4145
  },
4050
4146
  sourceLocation: {
4051
4147
  "file": "packages/llm/llm-billing/src/balance.ts",
4052
- "line": 139,
4148
+ "line": 143,
4053
4149
  "column": 3
4054
4150
  }
4055
4151
  },
@@ -4066,17 +4162,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4066
4162
  codec: {
4067
4163
  mode: "strict",
4068
4164
  typeSymbol: "@deepseek-ai/dsh-session/types#SessionId",
4069
- schema: _deepseek_ai_dsh_llm_billing_billing_getSessionSpend_parameter_0$schema
4165
+ schema: _rayadesu_dsh_llm_billing_billing_getSessionSpend_parameter_0$schema
4070
4166
  }
4071
4167
  }],
4072
4168
  result: {
4073
4169
  mode: "strict",
4074
4170
  typeSymbol: "@rayadesu/dsh-llm-billing/types#DeepSeekSessionSpend",
4075
- schema: _deepseek_ai_dsh_llm_billing_billing_getSessionSpend_result$schema
4171
+ schema: _rayadesu_dsh_llm_billing_billing_getSessionSpend_result$schema
4076
4172
  },
4077
4173
  sourceLocation: {
4078
4174
  "file": "packages/llm/llm-billing/src/balance.ts",
4079
- "line": 149,
4175
+ "line": 154,
4080
4176
  "column": 3
4081
4177
  }
4082
4178
  },
@@ -4086,23 +4182,33 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4086
4182
  namespace: "billing",
4087
4183
  method: "getTodaySpend",
4088
4184
  invocation: { kind: "direct" },
4089
- parameters: [],
4185
+ parameters: [{
4186
+ name: "force",
4187
+ wire: "force",
4188
+ source: "json",
4189
+ acceptsUndefined: true,
4190
+ codec: {
4191
+ mode: "strict",
4192
+ typeSymbol: "@rayadesu/dsh-llm-billing#billing/getTodaySpend:force",
4193
+ schema: _rayadesu_dsh_llm_billing_billing_getTodaySpend_parameter_0$schema
4194
+ }
4195
+ }],
4090
4196
  result: {
4091
4197
  mode: "strict",
4092
4198
  typeSymbol: "@rayadesu/dsh-llm-billing/types#DeepSeekTodaySpend",
4093
- schema: _deepseek_ai_dsh_llm_billing_billing_getTodaySpend_result$schema
4199
+ schema: _rayadesu_dsh_llm_billing_billing_getTodaySpend_result$schema
4094
4200
  },
4095
4201
  sourceLocation: {
4096
4202
  "file": "packages/llm/llm-billing/src/balance.ts",
4097
- "line": 159,
4203
+ "line": 167,
4098
4204
  "column": 3
4099
4205
  }
4100
4206
  }
4101
4207
  ]
4102
4208
  };
4103
4209
  //#endregion
4104
- //#region \0dsh-css:D:\code\deepseek-harness\packages\client\ui-billing\src\client\BalanceBadge.module.css.mjs
4105
- const css = ".YAT53G_root{position:relative}.YAT53G_trigger{background:var(--dsw-alias-fill-l2);min-height:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;border:0;border-radius:8px;align-items:center;gap:4px;padding:4px 8px;display:inline-flex}.YAT53G_trigger:hover,.YAT53G_trigger:focus-visible{color:var(--dsw-alias-label-primary)}.YAT53G_trigger svg{transition:transform .12s}.YAT53G_chevronOpen{transform:rotate(180deg)}.YAT53G_triggerLines{text-align:left;flex-direction:column;align-items:flex-start;gap:0;display:flex}.YAT53G_linePrimary{font-variant-numeric:tabular-nums;font-size:12px;line-height:16px}.YAT53G_lineSecondary{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px;line-height:15px}.YAT53G_unavailable{color:var(--dsw-alias-label-tertiary)}.YAT53G_inlineIcon{color:var(--dsw-alias-label-tertiary);flex:none}.YAT53G_spinning{color:var(--dsw-alias-label-tertiary);flex:none;animation:.8s linear infinite YAT53G_spin}@keyframes YAT53G_spin{to{transform:rotate(360deg)}}.YAT53G_panel{z-index:100;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-specific-menu);width:336px;max-width:min(360px,100vw - 32px);box-shadow:var(--dsw-shadow-lv3);border-radius:12px;flex-direction:column;gap:2px;padding:6px;display:flex;position:absolute;top:calc(100% + 6px);right:0}.YAT53G_amountRow{border-radius:8px;justify-content:space-between;align-items:center;gap:8px;padding:6px 8px;display:flex}.YAT53G_amountLabel{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-size:13px;line-height:18px}.YAT53G_spendRow{border-top:1px solid var(--dsw-alias-border-l2);border-radius:0;justify-content:space-between;align-items:center;gap:8px;padding:6px 8px;display:flex}.YAT53G_costRow{align-items:center;padding:2px 8px 6px;display:flex}.YAT53G_costBreakdown{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px;line-height:16px}.YAT53G_amountActions{align-items:center;gap:2px;display:inline-flex}.YAT53G_infoButton,.YAT53G_refreshButton{width:24px;height:24px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:6px;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.YAT53G_infoButton:hover,.YAT53G_infoButton:focus-visible,.YAT53G_refreshButton:hover,.YAT53G_refreshButton:focus-visible{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-l2)}.YAT53G_modelRow{border-radius:8px;justify-content:space-between;align-items:baseline;gap:8px;padding:6px 8px;display:flex}.YAT53G_modelName{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px;line-height:18px}.YAT53G_tasks{text-align:right;min-width:0;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;overflow-wrap:anywhere;font-size:12px;line-height:18px}";
4210
+ //#region \0dsh-css:C:\Users\admin\Desktop\me\code\deepseek-harness\packages\client\ui-billing\src\client\BalanceBadge.module.css.mjs
4211
+ const css = ".v_-kjq_root{position:relative}.v_-kjq_trigger{background:var(--dsw-alias-fill-l2);min-height:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;border:0;border-radius:8px;align-items:center;gap:4px;padding:4px 8px;display:inline-flex}.v_-kjq_trigger:hover,.v_-kjq_trigger:focus-visible{color:var(--dsw-alias-label-primary)}.v_-kjq_trigger svg{transition:transform .12s}.v_-kjq_chevronOpen{transform:rotate(180deg)}.v_-kjq_triggerLines{text-align:left;flex-direction:column;align-items:flex-start;gap:0;display:flex}.v_-kjq_linePrimary{font-variant-numeric:tabular-nums;font-size:12px;line-height:16px}.v_-kjq_lineSecondary{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px;line-height:15px}.v_-kjq_unavailable{color:var(--dsw-alias-label-tertiary)}.v_-kjq_inlineIcon{color:var(--dsw-alias-label-tertiary);flex:none}.v_-kjq_spinning{color:var(--dsw-alias-label-tertiary);flex:none;animation:.8s linear infinite v_-kjq_spin}@keyframes v_-kjq_spin{to{transform:rotate(360deg)}}.v_-kjq_panel{z-index:100;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-specific-menu);width:336px;max-width:min(360px,100vw - 32px);box-shadow:var(--dsw-shadow-lv3);border-radius:12px;flex-direction:column;gap:2px;padding:6px;display:flex;position:absolute;top:calc(100% + 6px);right:0}.v_-kjq_amountRow{border-radius:8px;justify-content:space-between;align-items:center;gap:8px;padding:6px 8px;display:flex}.v_-kjq_amountLabel{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-size:13px;line-height:18px}.v_-kjq_spendRow{border-top:1px solid var(--dsw-alias-border-l2);border-radius:0;justify-content:space-between;align-items:center;gap:8px;padding:6px 8px;display:flex}.v_-kjq_costRow{align-items:center;padding:2px 8px 6px;display:flex}.v_-kjq_costBreakdown{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px;line-height:16px}.v_-kjq_amountActions{align-items:center;gap:2px;display:inline-flex}.v_-kjq_infoButton,.v_-kjq_refreshButton{width:24px;height:24px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:6px;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.v_-kjq_infoButton:hover,.v_-kjq_infoButton:focus-visible,.v_-kjq_refreshButton:hover,.v_-kjq_refreshButton:focus-visible{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-l2)}.v_-kjq_modelRow{border-radius:8px;justify-content:space-between;align-items:baseline;gap:8px;padding:6px 8px;display:flex}.v_-kjq_modelName{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px;line-height:18px}.v_-kjq_tasks{text-align:right;min-width:0;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;overflow-wrap:anywhere;font-size:12px;line-height:18px}";
4106
4212
  const tagId = "@rayadesu/dsh-client-ui-billing/BalanceBadge.module.css";
4107
4213
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
4108
4214
  const tag = document.createElement("style");
@@ -4112,28 +4218,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4112
4218
  document.head.appendChild(tag);
4113
4219
  }
4114
4220
  var BalanceBadge_module_css_default = {
4115
- "amountActions": "YAT53G_amountActions",
4116
- "amountLabel": "YAT53G_amountLabel",
4117
- "amountRow": "YAT53G_amountRow",
4118
- "chevronOpen": "YAT53G_chevronOpen",
4119
- "costBreakdown": "YAT53G_costBreakdown",
4120
- "costRow": "YAT53G_costRow",
4121
- "infoButton": "YAT53G_infoButton",
4122
- "inlineIcon": "YAT53G_inlineIcon",
4123
- "linePrimary": "YAT53G_linePrimary",
4124
- "lineSecondary": "YAT53G_lineSecondary",
4125
- "modelName": "YAT53G_modelName",
4126
- "modelRow": "YAT53G_modelRow",
4127
- "panel": "YAT53G_panel",
4128
- "refreshButton": "YAT53G_refreshButton",
4129
- "root": "YAT53G_root",
4130
- "spendRow": "YAT53G_spendRow",
4131
- "spin": "YAT53G_spin",
4132
- "spinning": "YAT53G_spinning",
4133
- "tasks": "YAT53G_tasks",
4134
- "trigger": "YAT53G_trigger",
4135
- "triggerLines": "YAT53G_triggerLines",
4136
- "unavailable": "YAT53G_unavailable"
4221
+ "amountActions": "v_-kjq_amountActions",
4222
+ "amountLabel": "v_-kjq_amountLabel",
4223
+ "amountRow": "v_-kjq_amountRow",
4224
+ "chevronOpen": "v_-kjq_chevronOpen",
4225
+ "costBreakdown": "v_-kjq_costBreakdown",
4226
+ "costRow": "v_-kjq_costRow",
4227
+ "infoButton": "v_-kjq_infoButton",
4228
+ "inlineIcon": "v_-kjq_inlineIcon",
4229
+ "linePrimary": "v_-kjq_linePrimary",
4230
+ "lineSecondary": "v_-kjq_lineSecondary",
4231
+ "modelName": "v_-kjq_modelName",
4232
+ "modelRow": "v_-kjq_modelRow",
4233
+ "panel": "v_-kjq_panel",
4234
+ "refreshButton": "v_-kjq_refreshButton",
4235
+ "root": "v_-kjq_root",
4236
+ "spendRow": "v_-kjq_spendRow",
4237
+ "spin": "v_-kjq_spin",
4238
+ "spinning": "v_-kjq_spinning",
4239
+ "tasks": "v_-kjq_tasks",
4240
+ "trigger": "v_-kjq_trigger",
4241
+ "triggerLines": "v_-kjq_triggerLines",
4242
+ "unavailable": "v_-kjq_unavailable"
4137
4243
  };
4138
4244
  //#endregion
4139
4245
  //#region lib/types/client/BalanceBadge.js
@@ -4199,7 +4305,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4199
4305
  if (!current) return;
4200
4306
  setSpend(value);
4201
4307
  }, () => {});
4202
- const todaySpendRequest = Promise.resolve().then(getTodaySpend).then((value) => {
4308
+ const todaySpendRequest = Promise.resolve().then(() => getTodaySpend(request > 0)).then((value) => {
4203
4309
  if (!current) return;
4204
4310
  setTodaySpend(value);
4205
4311
  }, () => {});
@@ -4225,17 +4331,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4225
4331
  if (messageCount === pricedMessageCountRef.current) return;
4226
4332
  pricedMessageCountRef.current = messageCount;
4227
4333
  let current = true;
4228
- Promise.resolve().then(() => {
4229
- Promise.resolve().then(() => getSessionSpend(sessionId)).then((value) => {
4230
- if (!current) return;
4231
- setSpend(value);
4232
- }, () => {});
4233
- Promise.resolve().then(getTodaySpend).then((value) => {
4234
- if (!current) return;
4235
- setTodaySpend(value);
4236
- }, () => {});
4237
- });
4334
+ const timer = setTimeout(() => {
4335
+ Promise.resolve().then(() => {
4336
+ Promise.resolve().then(() => getSessionSpend(sessionId)).then((value) => {
4337
+ if (!current) return;
4338
+ setSpend(value);
4339
+ }, () => {});
4340
+ Promise.resolve().then(() => getTodaySpend()).then((value) => {
4341
+ if (!current) return;
4342
+ setTodaySpend(value);
4343
+ }, () => {});
4344
+ });
4345
+ }, 2e3);
4238
4346
  return () => {
4347
+ clearTimeout(timer);
4239
4348
  current = false;
4240
4349
  };
4241
4350
  }, [
@@ -4387,7 +4496,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4387
4496
  "state.unavailable": "额度不可用",
4388
4497
  "action.refresh": "刷新",
4389
4498
  "info.aria": "花费说明",
4390
- "info.hint": "仅能预估 DeepSeek 相关模型。本会话花费按每条消息的发生时刻(北京时间)所在峰谷时段单价计费:缓存命中输入、未命中输入(含缓存写入)、输出(含推理)分别计价。费用按 8 月 17 日实行的标准。",
4499
+ "info.hint": "仅能预估 DeepSeek 相关模型。本会话花费按每条消息的发生时刻(北京时间)所在峰谷时段单价计费:缓存命中输入、未命中输入(含缓存写入)、输出(含推理)分别计价。费率按 8 月 17 日实行的标准,高峰时段为北京时间周一至周五 9:00–12:00、14:00–18:00,周末全天按低谷价计费。",
4391
4500
  "badge.aria": "DeepSeek 额度:{amount}",
4392
4501
  "panel.aria": "DeepSeek 额度详情"
4393
4502
  };
@@ -4405,7 +4514,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4405
4514
  "state.unavailable": "Balance unavailable",
4406
4515
  "action.refresh": "Refresh",
4407
4516
  "info.aria": "About this spend",
4408
- "info.hint": "Only DeepSeek models are estimated. This session's spend is priced per message at the rate of its Beijing-time peak/off-peak hour: cache-hit input, cache-miss input (including cache writes), and output (including reasoning) are billed separately. Pricing follows the August 17 rates.",
4517
+ "info.hint": "Only DeepSeek models are estimated. This session's spend is priced per message at the rate of its Beijing-time peak/off-peak hour: cache-hit input, cache-miss input (including cache writes), and output (including reasoning) are billed separately. Rates follow the August 17 pricing; peak hours are Beijing time Monday–Friday 09:00–12:00 and 14:00–18:00, and weekends are billed at off-peak prices all day.",
4409
4518
  "badge.aria": "DeepSeek balance {amount}",
4410
4519
  "panel.aria": "DeepSeek balance details"
4411
4520
  };
@@ -4442,8 +4551,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4442
4551
  if (!result.ok) throw new Error(`billing.getSessionSpend failed: ${result.error.code}: ${result.error.message}`);
4443
4552
  return result.value;
4444
4553
  },
4445
- getTodaySpend: async () => {
4446
- const result = await billing.getTodaySpend();
4554
+ getTodaySpend: async (force) => {
4555
+ const result = await billing.getTodaySpend(force);
4447
4556
  if (!result.ok) throw new Error(`billing.getTodaySpend failed: ${result.error.code}: ${result.error.message}`);
4448
4557
  return result.value;
4449
4558
  }
@@ -9,8 +9,12 @@ export interface BalanceBadgeInjected {
9
9
  getBalance: () => Promise<DeepSeekBalance>;
10
10
  /** Read one session's billed spend; rejects with the Remote error message. */
11
11
  getSessionSpend: (sessionId: SessionId) => Promise<DeepSeekSessionSpend>;
12
- /** Read today's billed spend across every session; rejects with the Remote error message. */
13
- getTodaySpend: () => Promise<DeepSeekTodaySpend>;
12
+ /**
13
+ * Read today's billed spend across every session; rejects with the Remote
14
+ * error message. `force` bypasses the host-side cache — the manual refresh
15
+ * passes it, the message-triggered recompute does not.
16
+ */
17
+ getTodaySpend: (force?: boolean) => Promise<DeepSeekTodaySpend>;
14
18
  }
15
19
  /** Full props assembled by the header utilities slot renderer. */
16
20
  export type BalanceBadgeProps = PropsRuntime<'conversation.session.header.utilities'> & InjectFace<BalanceBadgeInjected> & PropsLocale<typeof NS>;
@@ -15,7 +15,7 @@ export declare const zh: {
15
15
  readonly 'state.unavailable': "额度不可用";
16
16
  readonly 'action.refresh': "刷新";
17
17
  readonly 'info.aria': "花费说明";
18
- readonly 'info.hint': "仅能预估 DeepSeek 相关模型。本会话花费按每条消息的发生时刻(北京时间)所在峰谷时段单价计费:缓存命中输入、未命中输入(含缓存写入)、输出(含推理)分别计价。费用按 8 月 17 日实行的标准。";
18
+ readonly 'info.hint': "仅能预估 DeepSeek 相关模型。本会话花费按每条消息的发生时刻(北京时间)所在峰谷时段单价计费:缓存命中输入、未命中输入(含缓存写入)、输出(含推理)分别计价。费率按 8 月 17 日实行的标准,高峰时段为北京时间周一至周五 9:00–12:00、14:00–18:00,周末全天按低谷价计费。";
19
19
  readonly 'badge.aria': "DeepSeek 额度:{amount}";
20
20
  readonly 'panel.aria': "DeepSeek 额度详情";
21
21
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rayadesu/dsh-client-ui-billing",
3
3
  "description": "Session-header DeepSeek account-balance and conversation-spend badge over the billing Remote",
4
- "version": "0.1.0-rc.8",
4
+ "version": "0.2.0",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
@@ -31,6 +31,10 @@
31
31
  "platform": "web"
32
32
  }
33
33
  },
34
+ "scripts": {
35
+ "bundle": "tsdown",
36
+ "watch": "tsdown --watch"
37
+ },
34
38
  "license": "MIT",
35
39
  "repository": {
36
40
  "type": "git",
@@ -41,7 +45,7 @@
41
45
  "access": "public"
42
46
  },
43
47
  "peerDependencies": {
44
- "@rayadesu/dsh-llm-billing": "^0.1.0-rc.8",
48
+ "@rayadesu/dsh-llm-billing": "^0.2.0",
45
49
  "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
46
50
  "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
47
51
  "@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.8",
@@ -50,7 +54,7 @@
50
54
  "@deepseek-ai/cordis": "^4.0.1"
51
55
  },
52
56
  "devDependencies": {
53
- "@rayadesu/dsh-llm-billing": "^0.1.0-rc.8",
57
+ "@rayadesu/dsh-llm-billing": "^0.2.0",
54
58
  "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
55
59
  "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
56
60
  "@deepseek-ai/dsh-client-test-runtime": "^0.1.0-rc.8",
@@ -68,9 +72,5 @@
68
72
  "lib/invariant.js",
69
73
  "lib/client.js",
70
74
  "lib/types/**/*.d.ts"
71
- ],
72
- "scripts": {
73
- "bundle": "tsdown",
74
- "watch": "tsdown --watch"
75
- }
76
- }
75
+ ]
76
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 WilliamLIiii
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.