dsh-subusage 0.10.3 → 0.10.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  > **English.** dsh-subusage shows subscription quota and balance for the AI providers you already use, as a pill next to the model selector in DeepSeek Harness. Provider switches and credentials live in one settings page, and other plugins or agents can read the same snapshot through a read-only quota API.
8
8
 
9
- [![npm version](https://img.shields.io/badge/npm-0.10.3-blue)](https://www.npmjs.com/package/dsh-subusage)
9
+ [![npm version](https://img.shields.io/badge/npm-0.10.4-blue)](https://www.npmjs.com/package/dsh-subusage)
10
10
  [![license](https://img.shields.io/badge/license-MIT-green)](#license--security--许可与安全)
11
11
  [![DSH](https://img.shields.io/badge/DSH-%3E%3D0.2.0--rc.1%20%3C0.3.0--0-informational)](#compatibility--兼容性)
12
12
 
package/lib/client.js CHANGED
@@ -891,10 +891,27 @@ window.__ModuleLoader__.load({
891
891
  );
892
892
  }
893
893
 
894
- /** 单窗口行(药丸弹层与设置页共用):徽标 + 已用% 主数字 + 进度条 + 明细行。 */
894
+ /**
895
+ * 单窗口行(药丸弹层与设置页共用):徽标 + 已用% 主数字 + 进度条 + 明细行。
896
+ * 明细分两行:用量/适用范围一行,重置时间一行。挤在一行时「重置于 …(还有 …)」
897
+ * 会在窄弹层里被从中间折断,读起来像属性和数值分了家。
898
+ */
895
899
  function UsageWindowRow({ w, t, getLocale, uncertain = false }) {
896
900
  const tier = uncertain && w.status !== "rate-limited" ? { icon: "?", badge: "badgeUnknown", color: "#b7791f", rank: 2 } : usageTier(w);
897
901
  const blocked = w.status === "rate-limited";
902
+ const usageLine = [
903
+ w.detail && typeof w.detail.used === "number" && typeof w.detail.limit === "number" && w.detail.limit > 0
904
+ ? t("detailUsed").replace("{used}", fmtTokens(w.detail.used)).replace("{limit}", fmtTokens(w.detail.limit)).replace("{unit}", w.detail.unit || "")
905
+ : null,
906
+ // 不同额度线之间不可比(例如 AFP 的日限额只覆盖视觉 / 语音模型与 Harness):如实标出适用范围,
907
+ // 否则"日比周还高"会被读成插件算错。
908
+ w.detail && w.detail.note ? w.detail.note : null,
909
+ w.detail && w.detail.limitSource === "plan-snapshot" ? t("planSnapshotLimit") : null
910
+ ].filter(Boolean).join(" · ");
911
+ const resetParts = [
912
+ resetLine(w, t, getLocale),
913
+ w.cascade ? t("cascadeLimited") + (w.detail && w.detail.blockedBy ? ` [${w.detail.blockedBy.join(", ")}]` : "") : null
914
+ ].filter(Boolean).join(" · ");
898
915
  return h("div", {
899
916
  style: {
900
917
  marginTop: 8, padding: "8px 10px", borderRadius: 8,
@@ -918,21 +935,27 @@ window.__ModuleLoader__.load({
918
935
  max: 100, value: Math.min(100, w.percent),
919
936
  style: { width: "100%", height: 6, marginTop: 6, accentColor: tier.color }
920
937
  }),
921
- h("div", { style: { fontSize: 11, opacity: 0.7, marginTop: 4 } },
922
- [
923
- w.detail && typeof w.detail.used === "number" && typeof w.detail.limit === "number" && w.detail.limit > 0
924
- ? t("detailUsed").replace("{used}", fmtTokens(w.detail.used)).replace("{limit}", fmtTokens(w.detail.limit)).replace("{unit}", w.detail.unit || "")
925
- : null,
926
- // 不同额度线之间不可比(例如 AFP 的日限额只覆盖视觉 / 语音模型与 Harness):如实标出适用范围,
927
- // 否则"日比周还高"会被读成插件算错。
928
- w.detail && w.detail.note ? w.detail.note : null,
929
- w.detail && w.detail.limitSource === "plan-snapshot" ? t("planSnapshotLimit") : null,
930
- resetLine(w, t, getLocale),
931
- w.cascade ? t("cascadeLimited") + (w.detail && w.detail.blockedBy ? ` [${w.detail.blockedBy.join(", ")}]` : "") : null
932
- ].filter(Boolean).join(" · "))
938
+ usageLine || resetParts
939
+ ? h("div", { style: { fontSize: 11, opacity: 0.7, marginTop: 4 } },
940
+ usageLine ? h("div", { style: { overflowWrap: "anywhere" } }, usageLine) : null,
941
+ resetParts ? h("div", { style: { marginTop: usageLine ? 2 : 0, overflowWrap: "anywhere" } }, resetParts) : null)
942
+ : null
933
943
  );
934
944
  }
935
945
 
946
+ /**
947
+ * 窗口列表:额度线切换时插一条分组标题。AFP 的日限额只覆盖视觉 / 语音模型与 Harness,
948
+ * 与文本模型的 5 小时 / 周 / 月不是同一条线,混排会让人把两条线的配额当成一组互相比较。
949
+ * 弹层与设置页共用,避免只有其中一处带分组。
950
+ */
951
+ function UsageWindowList({ windows, t, getLocale, uncertain = false }) {
952
+ return (windows || []).map((w, index) => h(react.Fragment, { key: w.kind },
953
+ w.groupLabel && w.groupLabel !== windows[index - 1]?.groupLabel
954
+ ? h("p", { role: "note", style: { margin: index ? "14px 0 0" : "6px 0 0", fontSize: 11, opacity: 0.6 } }, w.groupLabel)
955
+ : null,
956
+ h(UsageWindowRow, { w, t, getLocale, uncertain })));
957
+ }
958
+
936
959
  function UsagePill({ providerId, label, readEntry, t, getLocale, cc, setCommandCodeAccount }) {
937
960
  const [snapshot, setSnapshot] = react.useState(null);
938
961
  const [failed, setFailed] = react.useState(null);
@@ -1086,11 +1109,7 @@ window.__ModuleLoader__.load({
1086
1109
  : null,
1087
1110
  // 额度线切换时插一条分组标题:AFP 的日限额只覆盖视觉 / 语音模型与 Harness,与文本模型的
1088
1111
  // 5 小时 / 周 / 月不是同一条线,混排会让人把两条线的配额当成一组来互相比较。
1089
- windows.map((w, index) => h(react.Fragment, { key: w.kind },
1090
- w.groupLabel && w.groupLabel !== windows[index - 1]?.groupLabel
1091
- ? h("p", { style: { margin: index ? "14px 0 0" : "6px 0 0", fontSize: 11, opacity: 0.6 } }, w.groupLabel)
1092
- : null,
1093
- h(UsageWindowRow, { w, t, getLocale, uncertain: degraded }))),
1112
+ UsageWindowList({ windows, t, getLocale, uncertain: degraded }),
1094
1113
  entry && entry.extras && entry.extras.length > 0
1095
1114
  ? h("div", { style: { marginTop: 10, opacity: 0.85 } },
1096
1115
  entry.extras.map((x) => h("div", { key: x.kind }, `${extraLabel(x, t)}: ${x.value}`)))
@@ -1469,7 +1488,7 @@ window.__ModuleLoader__.load({
1469
1488
  h("span", { style: { flex: 1, minWidth: 150, fontSize: 12 } }, usageAction === "mimo-relogin" ? text("Cookie 已过期,重新登录即可恢复。", "Cookie expired — sign in again to restore.") : usageAction === "mimo-login" ? text("需要登录 MiMo(或 Cookie 即将到期)。", "MiMo sign-in needed (or the cookie is about to expire).") : text("这家还没有可用的凭据。", "No usable credentials for this provider.")),
1470
1489
  usageAction !== "credentials" ? h("button", { type: "button", style: primary, disabled: busy || loginActive, onClick: startLogin }, usageAction === "mimo-relogin" ? text("重新登录", "Sign in again") : text("登录并自动导入", "Sign in & import")) : null,
1471
1490
  h("button", { type: "button", style: usageAction === "credentials" ? primary : btnStyle, disabled: busy, onClick: () => revealEditor(usageId) }, usageAction === "credentials" ? text("配置凭据", "Configure credentials") : text("手动填写", "Enter manually"))) : null,
1472
- canShowUsage(usageEntry) && (usageEntry.windows || []).length ? (usageEntry.windows || []).map((w) => h(UsageWindowRow, { key: w.kind, w, t, getLocale, uncertain: usageEntry.coverage === "partial" || usageEntry.freshness === "stale" || usageEntry.state !== "ok" })) : h("p", { style: { opacity: .75 } }, usageEntry ? t("noUsageData") : t("loading")),
1491
+ canShowUsage(usageEntry) && (usageEntry.windows || []).length ? UsageWindowList({ windows: usageEntry.windows, t, getLocale, uncertain: usageEntry.coverage === "partial" || usageEntry.freshness === "stale" || usageEntry.state !== "ok" }) : h("p", { style: { opacity: .75 } }, usageEntry ? t("noUsageData") : t("loading")),
1473
1492
  canShowUsage(usageEntry) && usageEntry.extras ? h("p", { style: { opacity: .75 } }, usageEntry.extras.map((x) => `${extraLabel(x, t)}: ${x.value}`).join(" · ")) : null,
1474
1493
  usageEntry?.lastAttemptAt ? h("p", { style: { opacity: .65 } }, `${t("lastUpdated")} ${new Date(usageEntry.lastAttemptAt).toLocaleString(getLocale())}`) : null
1475
1494
  ) : null,
package/lib/index.js CHANGED
@@ -312,7 +312,7 @@ function volcFailure(action, error) {
312
312
  }
313
313
  async function fetchJson(fetcher, url, headers, lifetimeSignal, init = {}) {
314
314
  let response;
315
- try { response = await fetcher(url, { method: init.method ?? "GET", ...(init.body !== undefined ? { body: init.body } : {}), headers: { accept: "application/json", "user-agent": "dsh-subusage/0.10.3", ...headers }, signal: lifetimeSignal ? AbortSignal.any([AbortSignal.timeout(10000), lifetimeSignal]) : AbortSignal.timeout(10000), redirect: "manual" }); }
315
+ try { response = await fetcher(url, { method: init.method ?? "GET", ...(init.body !== undefined ? { body: init.body } : {}), headers: { accept: "application/json", "user-agent": "dsh-subusage/0.10.4", ...headers }, signal: lifetimeSignal ? AbortSignal.any([AbortSignal.timeout(10000), lifetimeSignal]) : AbortSignal.timeout(10000), redirect: "manual" }); }
316
316
  catch { throw failure("subusage/network", "网络请求失败或超时", true); }
317
317
  if (response.status >= 300 && response.status < 400 || response.status === 401 || response.status === 403) throw failure("subusage/auth", "登录或凭据已失效");
318
318
  if (!response.ok) {
@@ -883,7 +883,9 @@ export class SubUsageService extends TypertRemoteService {
883
883
  if (apiError) throw volcFailure(action, apiError);
884
884
  return response;
885
885
  };
886
- const toWindows = parsed => cascadeRateLimited(parsed.windows.map(w => windowRow(w.kind, w.percent, w.resetsAt, w.percent >= 100 ? "rate-limited" : undefined, w.detail)));
886
+ // 额度线分组标注(AFP 的日限额只覆盖视觉 / 语音模型与 Harness)由 volcengine.js 的解析器产出,
887
+ // 这里是把它带进 wire 的唯一通道:windowRow 只认五个字段,漏掉 groupLabel 会让前端静默失去分组。
888
+ const toWindows = parsed => cascadeRateLimited(parsed.windows.map(w => ({ ...windowRow(w.kind, w.percent, w.resetsAt, w.percent >= 100 ? "rate-limited" : undefined, w.detail), ...(w.groupLabel ? { groupLabel: w.groupLabel } : {}) })));
887
889
  // 企业版/团队版:额度挂在席位上,先取 SeatID 再查该席位。
888
890
  if (spec.volcSeat) {
889
891
  const seatIds = parseVolcSeatIds(await call("ListSeatInfos", { Filter: {}, Scene: spec.volcScene, PageNum: 1, PageSize: 100 }));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-subusage",
3
3
  "displayName": "订阅用量显示",
4
- "version": "0.10.3",
4
+ "version": "0.10.4",
5
5
  "publishConfig": {
6
6
  "access": "public",
7
7
  "registry": "https://registry.npmjs.org/"