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 +1 -1
- package/lib/client.js +38 -19
- package/lib/index.js +4 -2
- package/package.json +1 -1
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
|
-
[](https://www.npmjs.com/package/dsh-subusage)
|
|
10
10
|
[](#license--security--许可与安全)
|
|
11
11
|
[](#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
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
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
|
|
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 ? (
|
|
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.
|
|
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
|
-
|
|
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 }));
|