dsh-usage-state 0.4.4 → 0.4.6
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/CHANGELOG.md +99 -52
- package/README.en.md +62 -127
- package/README.md +59 -156
- package/docs/adapters.md +65 -77
- package/lib/client.js +209 -64
- package/lib/index.js +343 -179
- package/lib/typert.js +1 -0
- package/package.json +17 -8
package/lib/client.js
CHANGED
|
@@ -244,7 +244,7 @@ window.__ModuleLoader__.load({
|
|
|
244
244
|
},
|
|
245
245
|
{
|
|
246
246
|
sourceId: "kimi",
|
|
247
|
-
pattern: /(^|\.)(api\.kimi\.com|api\.moonshot\.cn|moonshot\.cn)$/
|
|
247
|
+
pattern: /(^|\.)(api\.kimi\.(com|ai)|api\.moonshot\.(cn|ai)|moonshot\.cn)$/
|
|
248
248
|
},
|
|
249
249
|
{
|
|
250
250
|
sourceId: "opencode",
|
|
@@ -285,6 +285,16 @@ window.__ModuleLoader__.load({
|
|
|
285
285
|
return;
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
|
+
/** Kimi Code and Moonshot use distinct keys and billing endpoints. */
|
|
289
|
+
function kimiMode(provider, endpoint) {
|
|
290
|
+
const origin = originOf(endpoint);
|
|
291
|
+
if (origin !== void 0) {
|
|
292
|
+
const host = new URL(origin).hostname;
|
|
293
|
+
if (host === "api.kimi.com" || host === "api.kimi.ai") return "coding-plan";
|
|
294
|
+
if (host === "api.moonshot.cn" || host === "api.moonshot.ai") return "api";
|
|
295
|
+
}
|
|
296
|
+
return /(?:^|[^a-z])kimi[-_ ]?(?:for[-_ ]?coding|coding|code)(?:$|[^a-z])/i.test(provider) ? "coding-plan" : "api";
|
|
297
|
+
}
|
|
288
298
|
function withTarget(resolution) {
|
|
289
299
|
if (resolution.sourceId === null || resolution.mode === null) return resolution;
|
|
290
300
|
return {
|
|
@@ -299,13 +309,17 @@ window.__ModuleLoader__.load({
|
|
|
299
309
|
*/
|
|
300
310
|
function resolveProvider(input) {
|
|
301
311
|
const entry = input.config.providers[input.provider];
|
|
312
|
+
const sourceId = entry?.sourceId ?? suggestSourceId(input.provider, input.endpointHint) ?? null;
|
|
313
|
+
const defaults = sourceId === null ? void 0 : input.config.sources[sourceId];
|
|
302
314
|
const declared = originOf(input.endpointHint);
|
|
303
|
-
const
|
|
315
|
+
const explicitBaseUrl = entry?.baseUrl ?? defaults?.baseUrl;
|
|
316
|
+
const baseUrl = explicitBaseUrl ?? declared;
|
|
317
|
+
const apiKeyRef = entry?.apiKeyRef ?? defaults?.apiKeyRef;
|
|
304
318
|
const base = {
|
|
305
319
|
provider: input.provider,
|
|
306
320
|
...baseUrl === void 0 ? {} : { baseUrl },
|
|
307
|
-
...
|
|
308
|
-
...
|
|
321
|
+
...explicitBaseUrl === void 0 ? {} : { baseUrlPinned: true },
|
|
322
|
+
...apiKeyRef === void 0 ? {} : { apiKeyRef }
|
|
309
323
|
};
|
|
310
324
|
if (entry?.mode === "hidden") return {
|
|
311
325
|
...base,
|
|
@@ -313,7 +327,6 @@ window.__ModuleLoader__.load({
|
|
|
313
327
|
mode: null,
|
|
314
328
|
reason: "hidden"
|
|
315
329
|
};
|
|
316
|
-
const sourceId = entry?.sourceId ?? suggestSourceId(input.provider, input.endpointHint) ?? null;
|
|
317
330
|
if (sourceId === null) return {
|
|
318
331
|
...base,
|
|
319
332
|
sourceId: null,
|
|
@@ -347,8 +360,8 @@ window.__ModuleLoader__.load({
|
|
|
347
360
|
mode: null,
|
|
348
361
|
reason: "needs-endpoint"
|
|
349
362
|
};
|
|
350
|
-
const mode = source.modes[0];
|
|
351
|
-
if (mode === void 0) return {
|
|
363
|
+
const mode = sourceId === "kimi" ? kimiMode(input.provider, baseUrl) : source.modes[0];
|
|
364
|
+
if (mode === void 0 || !source.modes.includes(mode)) return {
|
|
352
365
|
...base,
|
|
353
366
|
sourceId,
|
|
354
367
|
mode: null,
|
|
@@ -550,6 +563,7 @@ window.__ModuleLoader__.load({
|
|
|
550
563
|
const [editing, setEditing] = (0, react.useState)(false);
|
|
551
564
|
const shown = editing ? draft : props.value;
|
|
552
565
|
const commit = () => {
|
|
566
|
+
if (!editing || props.disabled) return;
|
|
553
567
|
setEditing(false);
|
|
554
568
|
const next = draft.trim();
|
|
555
569
|
if (next !== props.value.trim()) props.onCommit(next);
|
|
@@ -579,29 +593,54 @@ window.__ModuleLoader__.load({
|
|
|
579
593
|
const { t } = props;
|
|
580
594
|
const [draft, setDraft] = (0, react.useState)("");
|
|
581
595
|
const [note, setNote] = (0, react.useState)(void 0);
|
|
596
|
+
const [pending, setPending] = (0, react.useState)(false);
|
|
597
|
+
const busy = (0, react.useRef)(false);
|
|
582
598
|
const candidates = props.description?.candidates ?? [];
|
|
583
|
-
const configuredRef = props.description?.ref ?? candidates.find((candidate) => candidate.configured)?.ref ?? props.refs[0];
|
|
584
|
-
const
|
|
599
|
+
const configuredRef = props.refOverride ?? props.description?.ref ?? candidates.find((candidate) => candidate.configured)?.ref ?? props.refs[0];
|
|
600
|
+
const candidate = candidates.find((value) => value.ref === configuredRef);
|
|
601
|
+
const sameDescription = props.description?.ref === configuredRef;
|
|
602
|
+
const writable = (candidate?.writable ?? (sameDescription ? props.description?.writable : void 0)) !== false;
|
|
603
|
+
const configured = candidate?.configured ?? (sameDescription && props.description?.configured === true);
|
|
585
604
|
const save = async () => {
|
|
586
|
-
if (props.credentials === void 0 || configuredRef === void 0 || draft.trim() === "") return;
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
605
|
+
if (busy.current || !writable || props.credentials === void 0 || configuredRef === void 0 || draft.trim() === "") return;
|
|
606
|
+
busy.current = true;
|
|
607
|
+
setPending(true);
|
|
608
|
+
setNote(void 0);
|
|
609
|
+
try {
|
|
610
|
+
const result = await props.credentials.set(configuredRef, draft.trim());
|
|
611
|
+
if (!result.ok) {
|
|
612
|
+
setNote(t("credentialFailed", { message: result.error.message }));
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
setDraft("");
|
|
616
|
+
setNote(t("credentialSaved"));
|
|
617
|
+
props.onChanged();
|
|
618
|
+
} catch (error) {
|
|
619
|
+
setNote(t("credentialFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
620
|
+
} finally {
|
|
621
|
+
busy.current = false;
|
|
622
|
+
setPending(false);
|
|
591
623
|
}
|
|
592
|
-
setDraft("");
|
|
593
|
-
setNote(t("credentialSaved"));
|
|
594
|
-
props.onChanged();
|
|
595
624
|
};
|
|
596
625
|
const clear = async () => {
|
|
597
|
-
if (props.credentials === void 0 || configuredRef === void 0) return;
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
626
|
+
if (busy.current || !writable || props.credentials === void 0 || configuredRef === void 0) return;
|
|
627
|
+
busy.current = true;
|
|
628
|
+
setPending(true);
|
|
629
|
+
setNote(void 0);
|
|
630
|
+
try {
|
|
631
|
+
const result = await props.credentials.unset(configuredRef);
|
|
632
|
+
if (!result.ok) {
|
|
633
|
+
setNote(t("credentialFailed", { message: result.error.message }));
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
setNote(t("credentialSaved"));
|
|
637
|
+
props.onChanged();
|
|
638
|
+
} catch (error) {
|
|
639
|
+
setNote(t("credentialFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
640
|
+
} finally {
|
|
641
|
+
busy.current = false;
|
|
642
|
+
setPending(false);
|
|
602
643
|
}
|
|
603
|
-
setNote(t("credentialSaved"));
|
|
604
|
-
props.onChanged();
|
|
605
644
|
};
|
|
606
645
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
607
646
|
style: {
|
|
@@ -646,7 +685,7 @@ window.__ModuleLoader__.load({
|
|
|
646
685
|
type: "password",
|
|
647
686
|
autoComplete: "off",
|
|
648
687
|
value: draft,
|
|
649
|
-
disabled: !writable || props.credentials === void 0,
|
|
688
|
+
disabled: pending || !writable || props.credentials === void 0,
|
|
650
689
|
placeholder: t("credentialPlaceholder"),
|
|
651
690
|
onChange: (event) => setDraft(event.target.value),
|
|
652
691
|
style: { maxWidth: "280px" }
|
|
@@ -654,14 +693,14 @@ window.__ModuleLoader__.load({
|
|
|
654
693
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
655
694
|
size: "sm",
|
|
656
695
|
variant: "primary",
|
|
657
|
-
disabled: draft.trim() === "",
|
|
696
|
+
disabled: pending || !writable || props.credentials === void 0 || configuredRef === void 0 || draft.trim() === "",
|
|
658
697
|
onClick: () => void save(),
|
|
659
698
|
children: t("credentialSave")
|
|
660
699
|
}),
|
|
661
700
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
662
701
|
size: "sm",
|
|
663
702
|
variant: "outline",
|
|
664
|
-
disabled: configuredRef === void 0 ||
|
|
703
|
+
disabled: pending || !writable || props.credentials === void 0 || configuredRef === void 0 || !configured,
|
|
665
704
|
onClick: () => void clear(),
|
|
666
705
|
children: t("credentialClear")
|
|
667
706
|
})
|
|
@@ -717,7 +756,7 @@ window.__ModuleLoader__.load({
|
|
|
717
756
|
size: "sm",
|
|
718
757
|
variant: "ghost",
|
|
719
758
|
"aria-label": t("moveUp"),
|
|
720
|
-
disabled: props.index === 0,
|
|
759
|
+
disabled: props.disabled || props.index === 0,
|
|
721
760
|
onClick: () => props.onMove(-1),
|
|
722
761
|
children: "↑"
|
|
723
762
|
}),
|
|
@@ -725,13 +764,14 @@ window.__ModuleLoader__.load({
|
|
|
725
764
|
size: "sm",
|
|
726
765
|
variant: "ghost",
|
|
727
766
|
"aria-label": t("moveDown"),
|
|
728
|
-
disabled: props.index === props.total - 1,
|
|
767
|
+
disabled: props.disabled || props.index === props.total - 1,
|
|
729
768
|
onClick: () => props.onMove(1),
|
|
730
769
|
children: "↓"
|
|
731
770
|
}),
|
|
732
771
|
row.modes.map((mode) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
733
772
|
size: "sm",
|
|
734
773
|
variant: row.selected === mode ? "primary" : "outline",
|
|
774
|
+
disabled: props.disabled,
|
|
735
775
|
onClick: () => props.onMode(mode),
|
|
736
776
|
children: modeLabel(mode, t)
|
|
737
777
|
}, mode))
|
|
@@ -774,6 +814,7 @@ window.__ModuleLoader__.load({
|
|
|
774
814
|
style: MUTED,
|
|
775
815
|
children: t("sourceLabel")
|
|
776
816
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
817
|
+
disabled: props.disabled,
|
|
777
818
|
value: entry?.sourceId ?? "",
|
|
778
819
|
onChange: (event) => {
|
|
779
820
|
const value = event.target.value;
|
|
@@ -805,6 +846,7 @@ window.__ModuleLoader__.load({
|
|
|
805
846
|
children: t("baseUrl")
|
|
806
847
|
}),
|
|
807
848
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftInput, {
|
|
849
|
+
disabled: props.disabled,
|
|
808
850
|
value: entry?.baseUrl ?? "",
|
|
809
851
|
placeholder: row.resolution.mode === null ? t("baseUrlPlaceholder") : resolvedSource?.defaultBaseUrl[row.resolution.mode] ?? t("baseUrlPlaceholder"),
|
|
810
852
|
width: "320px",
|
|
@@ -835,6 +877,7 @@ window.__ModuleLoader__.load({
|
|
|
835
877
|
children: t("apiKeyRef")
|
|
836
878
|
}),
|
|
837
879
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftInput, {
|
|
880
|
+
disabled: props.disabled,
|
|
838
881
|
value: entry?.apiKeyRef ?? "",
|
|
839
882
|
width: "220px",
|
|
840
883
|
onCommit: (value) => {
|
|
@@ -859,6 +902,7 @@ window.__ModuleLoader__.load({
|
|
|
859
902
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CredentialPanel, {
|
|
860
903
|
t,
|
|
861
904
|
refs,
|
|
905
|
+
refOverride: row.resolution.apiKeyRef,
|
|
862
906
|
description: props.description,
|
|
863
907
|
credentials: props.credentials,
|
|
864
908
|
onChanged: props.onCredentialChanged
|
|
@@ -878,6 +922,7 @@ window.__ModuleLoader__.load({
|
|
|
878
922
|
const { t } = props;
|
|
879
923
|
const snapshot = useSettingsValue(props.settings);
|
|
880
924
|
const state = useStoreState(props.usageState);
|
|
925
|
+
const [writeError, setWriteError] = (0, react.useState)(void 0);
|
|
881
926
|
(0, react.useEffect)(() => {
|
|
882
927
|
props.usageState.refreshModels();
|
|
883
928
|
props.usageState.refreshCredentials();
|
|
@@ -891,17 +936,27 @@ window.__ModuleLoader__.load({
|
|
|
891
936
|
models: state.models,
|
|
892
937
|
config,
|
|
893
938
|
catalog: state.catalog,
|
|
894
|
-
registry: state.modelRegistry
|
|
939
|
+
registry: state.modelRegistry,
|
|
940
|
+
endpointHints: state.endpointHints
|
|
895
941
|
});
|
|
942
|
+
const mutate = async (ops) => {
|
|
943
|
+
if (!snapshot.writable) return;
|
|
944
|
+
setWriteError(void 0);
|
|
945
|
+
try {
|
|
946
|
+
await props.settings.mutate(ops);
|
|
947
|
+
} catch (error) {
|
|
948
|
+
setWriteError(error instanceof Error ? error.message : String(error));
|
|
949
|
+
}
|
|
950
|
+
};
|
|
896
951
|
const write = (path, value) => {
|
|
897
|
-
|
|
952
|
+
mutate([{
|
|
898
953
|
op: "set",
|
|
899
954
|
path,
|
|
900
955
|
value
|
|
901
956
|
}]);
|
|
902
957
|
};
|
|
903
958
|
const clear = (path) => {
|
|
904
|
-
|
|
959
|
+
mutate([{
|
|
905
960
|
op: "unset",
|
|
906
961
|
path
|
|
907
962
|
}]);
|
|
@@ -952,6 +1007,15 @@ window.__ModuleLoader__.load({
|
|
|
952
1007
|
state.modelsError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
953
1008
|
style: MUTED,
|
|
954
1009
|
children: state.modelsError
|
|
1010
|
+
}),
|
|
1011
|
+
state.credentialsError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1012
|
+
style: MUTED,
|
|
1013
|
+
children: t("credentialFailed", { message: state.credentialsError })
|
|
1014
|
+
}),
|
|
1015
|
+
writeError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1016
|
+
role: "alert",
|
|
1017
|
+
style: MUTED,
|
|
1018
|
+
children: t("settingsFailed", { message: writeError })
|
|
955
1019
|
})
|
|
956
1020
|
]
|
|
957
1021
|
})
|
|
@@ -979,14 +1043,19 @@ window.__ModuleLoader__.load({
|
|
|
979
1043
|
failure: row.resolution.key === void 0 ? void 0 : state.snapshots[row.resolution.key]?.error,
|
|
980
1044
|
index,
|
|
981
1045
|
total: rows.length,
|
|
1046
|
+
disabled: !snapshot.writable,
|
|
982
1047
|
onMode: (mode) => write(["providers"], setProviderMode(config.providers, row.provider, mode)),
|
|
983
1048
|
onMove: (delta) => {
|
|
984
|
-
const next = reorderProviders(
|
|
1049
|
+
const next = reorderProviders(rows.map((candidate) => candidate.provider), row.provider, delta);
|
|
985
1050
|
if (next !== void 0) write(["order"], next);
|
|
986
1051
|
},
|
|
987
1052
|
onField: (path, value) => write(path, value),
|
|
988
1053
|
onClearField: (path) => clear(path),
|
|
989
|
-
onCredentialChanged: () =>
|
|
1054
|
+
onCredentialChanged: () => {
|
|
1055
|
+
props.usageState.invalidate();
|
|
1056
|
+
props.usageState.refreshCredentials();
|
|
1057
|
+
props.usageState.refresh(true);
|
|
1058
|
+
}
|
|
990
1059
|
}, row.provider))
|
|
991
1060
|
]
|
|
992
1061
|
}),
|
|
@@ -1003,6 +1072,7 @@ window.__ModuleLoader__.load({
|
|
|
1003
1072
|
}),
|
|
1004
1073
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftInput, {
|
|
1005
1074
|
type: "number",
|
|
1075
|
+
disabled: !snapshot.writable,
|
|
1006
1076
|
value: String(config.display.thresholdWarnPercent),
|
|
1007
1077
|
width: "90px",
|
|
1008
1078
|
onCommit: (value) => write(["display", "thresholdWarnPercent"], Number(value))
|
|
@@ -1013,6 +1083,7 @@ window.__ModuleLoader__.load({
|
|
|
1013
1083
|
}),
|
|
1014
1084
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftInput, {
|
|
1015
1085
|
type: "number",
|
|
1086
|
+
disabled: !snapshot.writable,
|
|
1016
1087
|
value: String(config.display.thresholdCriticalPercent),
|
|
1017
1088
|
width: "90px",
|
|
1018
1089
|
onCommit: (value) => write(["display", "thresholdCriticalPercent"], Number(value))
|
|
@@ -1026,12 +1097,14 @@ window.__ModuleLoader__.load({
|
|
|
1026
1097
|
children: t("intervalMinutes")
|
|
1027
1098
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftInput, {
|
|
1028
1099
|
type: "number",
|
|
1100
|
+
disabled: !snapshot.writable,
|
|
1029
1101
|
value: String(config.refresh.intervalMinutes),
|
|
1030
1102
|
width: "90px",
|
|
1031
1103
|
onCommit: (value) => write(["refresh", "intervalMinutes"], Number(value))
|
|
1032
1104
|
})]
|
|
1033
1105
|
}),
|
|
1034
1106
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
1107
|
+
disabled: !snapshot.writable,
|
|
1035
1108
|
checked: config.display.progressBar,
|
|
1036
1109
|
label: t("progressBar"),
|
|
1037
1110
|
onChange: (next) => write(["display", "progressBar"], next)
|
|
@@ -1123,7 +1196,9 @@ window.__ModuleLoader__.load({
|
|
|
1123
1196
|
kind: "label",
|
|
1124
1197
|
text: input.sourceLabel,
|
|
1125
1198
|
stale: true,
|
|
1126
|
-
|
|
1199
|
+
staleSince: snapshot.fetchedAt,
|
|
1200
|
+
...snapshot.error === void 0 ? {} : { errorKind: snapshot.error.kind },
|
|
1201
|
+
...snapshot.error?.detail === void 0 ? {} : { errorDetail: snapshot.error.detail }
|
|
1127
1202
|
} : {
|
|
1128
1203
|
kind: "label",
|
|
1129
1204
|
text: input.sourceLabel
|
|
@@ -1178,6 +1253,10 @@ window.__ModuleLoader__.load({
|
|
|
1178
1253
|
const present = pieces.filter((piece) => piece !== void 0 && piece !== "");
|
|
1179
1254
|
return present.length === 0 ? void 0 : present.join(" · ");
|
|
1180
1255
|
}
|
|
1256
|
+
function failureReason(kind, detail, t) {
|
|
1257
|
+
if (kind === void 0) return detail;
|
|
1258
|
+
return detail === void 0 ? t(`error.${kind}`) : `${t(`error.${kind}`)}: ${detail}`;
|
|
1259
|
+
}
|
|
1181
1260
|
function localTime(epochMs) {
|
|
1182
1261
|
return new Date(epochMs).toLocaleTimeString();
|
|
1183
1262
|
}
|
|
@@ -1195,20 +1274,17 @@ window.__ModuleLoader__.load({
|
|
|
1195
1274
|
for (const segment of segments) switch (segment.kind) {
|
|
1196
1275
|
case "label": {
|
|
1197
1276
|
const staleHint = segment.stale === true ? t("staleHint") : void 0;
|
|
1198
|
-
const
|
|
1277
|
+
const tooltip = tooltipOf([
|
|
1278
|
+
staleHint,
|
|
1279
|
+
segment.staleSince === void 0 ? void 0 : t("tipUpdated", { age: formatAge(segment.staleSince, now) }),
|
|
1280
|
+
failureReason(segment.errorKind, segment.errorDetail, t),
|
|
1281
|
+
context
|
|
1282
|
+
]);
|
|
1199
1283
|
parts.push({
|
|
1200
1284
|
kind: "label",
|
|
1201
1285
|
text: segment.text,
|
|
1202
1286
|
stale: segment.stale === true,
|
|
1203
|
-
...
|
|
1204
|
-
staleHint,
|
|
1205
|
-
refreshHint,
|
|
1206
|
-
context
|
|
1207
|
-
]) === void 0 ? {} : { tooltip: tooltipOf([
|
|
1208
|
-
staleHint,
|
|
1209
|
-
refreshHint,
|
|
1210
|
-
context
|
|
1211
|
-
]) }
|
|
1287
|
+
...tooltip === void 0 ? {} : { tooltip }
|
|
1212
1288
|
});
|
|
1213
1289
|
if (segment.staleSince !== void 0) parts.push({
|
|
1214
1290
|
kind: "age",
|
|
@@ -1246,7 +1322,7 @@ window.__ModuleLoader__.load({
|
|
|
1246
1322
|
break;
|
|
1247
1323
|
}
|
|
1248
1324
|
case "state": {
|
|
1249
|
-
const reason = segment.errorKind
|
|
1325
|
+
const reason = failureReason(segment.errorKind, segment.errorDetail, t);
|
|
1250
1326
|
parts.push({
|
|
1251
1327
|
kind: "state",
|
|
1252
1328
|
state: segment.state,
|
|
@@ -1435,7 +1511,7 @@ window.__ModuleLoader__.load({
|
|
|
1435
1511
|
* One read-only usage line: balance in API mode, 5h/7d quota in coding-plan mode.
|
|
1436
1512
|
*
|
|
1437
1513
|
* It reports the account's **current** reading, so it has exactly one home: the
|
|
1438
|
-
* line
|
|
1514
|
+
* line above the composer. There is no per-turn copy — an account-level number
|
|
1439
1515
|
* cannot honestly describe a single turn (see `slots.ts`).
|
|
1440
1516
|
*/
|
|
1441
1517
|
function StatusLine(props) {
|
|
@@ -1449,9 +1525,19 @@ window.__ModuleLoader__.load({
|
|
|
1449
1525
|
const status = statusOf(resolveProvider({
|
|
1450
1526
|
provider: current.provider,
|
|
1451
1527
|
config,
|
|
1452
|
-
catalog: state.catalog
|
|
1528
|
+
catalog: state.catalog,
|
|
1529
|
+
endpointHint: state.endpointHints?.[current.provider]
|
|
1453
1530
|
}), state.catalog.length === 0);
|
|
1454
|
-
|
|
1531
|
+
if (status.kind === "hidden") return null;
|
|
1532
|
+
const kept = status.kind === "ready" ? state.snapshots[status.key] : void 0;
|
|
1533
|
+
const snapshot = kept === void 0 || state.error === void 0 ? kept : {
|
|
1534
|
+
...kept,
|
|
1535
|
+
stale: true,
|
|
1536
|
+
error: {
|
|
1537
|
+
kind: "network",
|
|
1538
|
+
detail: state.error
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1455
1541
|
const sourceLabel = status.kind === "ready" ? state.catalog.find((entry) => entry.id === status.sourceId)?.displayName ?? status.sourceId : "";
|
|
1456
1542
|
const fullParts = statusParts({
|
|
1457
1543
|
segments: state.catalog.length === 0 && state.status === "error" && state.error !== void 0 ? [{
|
|
@@ -1491,7 +1577,7 @@ window.__ModuleLoader__.load({
|
|
|
1491
1577
|
const zh = {
|
|
1492
1578
|
nav: "用量状态",
|
|
1493
1579
|
title: "用量状态",
|
|
1494
|
-
intro: "
|
|
1580
|
+
intro: "为每个供应商选择显示账户余额(API 模式)或套餐额度(Coding Plan 模式)。数据由宿主定时刷新,密钥交给 DSH 凭据库保存,本插件不另存明文。",
|
|
1495
1581
|
refreshNow: "立即刷新",
|
|
1496
1582
|
refreshing: "刷新中…",
|
|
1497
1583
|
lastChecked: "更新于 {time}",
|
|
@@ -1527,7 +1613,7 @@ window.__ModuleLoader__.load({
|
|
|
1527
1613
|
credential: "密钥",
|
|
1528
1614
|
credentialConfigured: "已配置(来源:{source})",
|
|
1529
1615
|
credentialMissing: "未配置",
|
|
1530
|
-
credentialLocked: "
|
|
1616
|
+
credentialLocked: "当前凭据来源无法在此修改",
|
|
1531
1617
|
credentialPlaceholder: "粘贴密钥…",
|
|
1532
1618
|
credentialSave: "保存",
|
|
1533
1619
|
credentialClear: "清除",
|
|
@@ -1537,7 +1623,8 @@ window.__ModuleLoader__.load({
|
|
|
1537
1623
|
intervalMinutes: "空闲刷新间隔(分钟)",
|
|
1538
1624
|
thresholdWarn: "黄色阈值(已用 %)",
|
|
1539
1625
|
thresholdCritical: "红色阈值(已用 %)",
|
|
1540
|
-
progressBar: "
|
|
1626
|
+
progressBar: "显示进度环",
|
|
1627
|
+
settingsFailed: "无法保存设置:{message}",
|
|
1541
1628
|
refreshFailed: "刷新失败:{message}",
|
|
1542
1629
|
"state.loading": "读取中…",
|
|
1543
1630
|
"state.unconfigured": "未配置",
|
|
@@ -1561,12 +1648,14 @@ window.__ModuleLoader__.load({
|
|
|
1561
1648
|
"window.5h": "5h",
|
|
1562
1649
|
"window.1d": "1d",
|
|
1563
1650
|
"window.7d": "7d",
|
|
1564
|
-
"window.30d": "30d"
|
|
1651
|
+
"window.30d": "30d",
|
|
1652
|
+
"window.month": "月总额度",
|
|
1653
|
+
"window.month-code": "编程月用量"
|
|
1565
1654
|
};
|
|
1566
1655
|
const en = {
|
|
1567
1656
|
nav: "Usage state",
|
|
1568
1657
|
title: "Usage state",
|
|
1569
|
-
intro: "Choose what to show for each
|
|
1658
|
+
intro: "Choose what to show for each provider: account balance (API mode) or coding-plan quota (Coding Plan mode). The host owns refresh timing, and DSH stores credentials — this plugin keeps no separate plaintext copy.",
|
|
1570
1659
|
refreshNow: "Refresh now",
|
|
1571
1660
|
refreshing: "Refreshing…",
|
|
1572
1661
|
lastChecked: "Updated {time}",
|
|
@@ -1602,7 +1691,7 @@ window.__ModuleLoader__.load({
|
|
|
1602
1691
|
credential: "API key",
|
|
1603
1692
|
credentialConfigured: "Configured ({source})",
|
|
1604
1693
|
credentialMissing: "Not configured",
|
|
1605
|
-
credentialLocked: "
|
|
1694
|
+
credentialLocked: "This credential source cannot be edited here",
|
|
1606
1695
|
credentialPlaceholder: "Paste the key…",
|
|
1607
1696
|
credentialSave: "Save",
|
|
1608
1697
|
credentialClear: "Clear",
|
|
@@ -1612,7 +1701,8 @@ window.__ModuleLoader__.load({
|
|
|
1612
1701
|
intervalMinutes: "Idle refresh interval (minutes)",
|
|
1613
1702
|
thresholdWarn: "Amber threshold (used %)",
|
|
1614
1703
|
thresholdCritical: "Red threshold (used %)",
|
|
1615
|
-
progressBar: "Show progress
|
|
1704
|
+
progressBar: "Show progress ring",
|
|
1705
|
+
settingsFailed: "Could not save settings: {message}",
|
|
1616
1706
|
refreshFailed: "Refresh failed: {message}",
|
|
1617
1707
|
"state.loading": "Reading…",
|
|
1618
1708
|
"state.unconfigured": "Not configured",
|
|
@@ -1636,7 +1726,9 @@ window.__ModuleLoader__.load({
|
|
|
1636
1726
|
"window.5h": "5h",
|
|
1637
1727
|
"window.1d": "1d",
|
|
1638
1728
|
"window.7d": "7d",
|
|
1639
|
-
"window.30d": "30d"
|
|
1729
|
+
"window.30d": "30d",
|
|
1730
|
+
"window.month": "Monthly total",
|
|
1731
|
+
"window.month-code": "Monthly code"
|
|
1640
1732
|
};
|
|
1641
1733
|
//#endregion
|
|
1642
1734
|
//#region src/client/remote.ts
|
|
@@ -1660,6 +1752,9 @@ window.__ModuleLoader__.load({
|
|
|
1660
1752
|
}
|
|
1661
1753
|
//#endregion
|
|
1662
1754
|
//#region src/client/settings-form.ts
|
|
1755
|
+
async function requireAccepted(write) {
|
|
1756
|
+
if (!await write) throw new Error("The host did not accept the settings write");
|
|
1757
|
+
}
|
|
1663
1758
|
/**
|
|
1664
1759
|
* Present one `configForms` entry as the settings scope the components consume.
|
|
1665
1760
|
*
|
|
@@ -1696,13 +1791,13 @@ window.__ModuleLoader__.load({
|
|
|
1696
1791
|
getSnapshot: snapshot,
|
|
1697
1792
|
subscribe: (listener) => form.subscribe(listener),
|
|
1698
1793
|
set: async (field, value) => {
|
|
1699
|
-
await form.set(field, value);
|
|
1794
|
+
await requireAccepted(form.set(field, value));
|
|
1700
1795
|
},
|
|
1701
1796
|
unset: async (field) => {
|
|
1702
|
-
await form.unset(field);
|
|
1797
|
+
await requireAccepted(form.unset(field));
|
|
1703
1798
|
},
|
|
1704
1799
|
mutate: async (ops, expectedRevision) => {
|
|
1705
|
-
await form.mutate(ops, expectedRevision);
|
|
1800
|
+
await requireAccepted(form.mutate(ops, expectedRevision));
|
|
1706
1801
|
}
|
|
1707
1802
|
};
|
|
1708
1803
|
}
|
|
@@ -1766,7 +1861,11 @@ window.__ModuleLoader__.load({
|
|
|
1766
1861
|
credentialsError: void 0,
|
|
1767
1862
|
modelsError: void 0
|
|
1768
1863
|
};
|
|
1864
|
+
generation = 0;
|
|
1865
|
+
inflightGeneration = 0;
|
|
1866
|
+
inflightForce = false;
|
|
1769
1867
|
inflight;
|
|
1868
|
+
forcedAfterInflight;
|
|
1770
1869
|
constructor(deps) {
|
|
1771
1870
|
this.deps = deps;
|
|
1772
1871
|
}
|
|
@@ -1786,12 +1885,38 @@ window.__ModuleLoader__.load({
|
|
|
1786
1885
|
};
|
|
1787
1886
|
for (const listener of [...this.listeners]) listener();
|
|
1788
1887
|
}
|
|
1888
|
+
/** A provider/credential identity changed: its previous RPC cannot restore old values. */
|
|
1889
|
+
invalidate() {
|
|
1890
|
+
this.generation++;
|
|
1891
|
+
this.forcedAfterInflight = void 0;
|
|
1892
|
+
this.publish({
|
|
1893
|
+
status: "loading",
|
|
1894
|
+
snapshots: {},
|
|
1895
|
+
credentials: {},
|
|
1896
|
+
checkedAt: void 0,
|
|
1897
|
+
error: void 0,
|
|
1898
|
+
credentialsError: void 0
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1789
1901
|
refresh(force = false) {
|
|
1790
|
-
|
|
1902
|
+
const generation = this.generation;
|
|
1903
|
+
if (this.inflight !== void 0 && this.inflightGeneration === generation) {
|
|
1904
|
+
if (!force || this.inflightForce) return this.inflight;
|
|
1905
|
+
if (this.forcedAfterInflight === void 0) {
|
|
1906
|
+
const queued = this.inflight.then(() => {
|
|
1907
|
+
if (this.generation === generation) return this.refresh(true);
|
|
1908
|
+
}).finally(() => {
|
|
1909
|
+
if (this.forcedAfterInflight === queued) this.forcedAfterInflight = void 0;
|
|
1910
|
+
});
|
|
1911
|
+
this.forcedAfterInflight = queued;
|
|
1912
|
+
}
|
|
1913
|
+
return this.forcedAfterInflight;
|
|
1914
|
+
}
|
|
1791
1915
|
this.publish({ status: "loading" });
|
|
1792
1916
|
const run = (async () => {
|
|
1793
1917
|
try {
|
|
1794
1918
|
const result = await this.deps.getState(force);
|
|
1919
|
+
if (generation !== this.generation) return;
|
|
1795
1920
|
if (!result.ok) {
|
|
1796
1921
|
this.publish({
|
|
1797
1922
|
status: "error",
|
|
@@ -1802,19 +1927,22 @@ window.__ModuleLoader__.load({
|
|
|
1802
1927
|
this.publish({
|
|
1803
1928
|
status: "ready",
|
|
1804
1929
|
catalog: result.value.sources,
|
|
1930
|
+
endpointHints: result.value.endpointHints ?? {},
|
|
1805
1931
|
snapshots: result.value.snapshots,
|
|
1806
1932
|
checkedAt: result.value.checkedAt,
|
|
1807
1933
|
error: void 0
|
|
1808
1934
|
});
|
|
1809
1935
|
} catch (error) {
|
|
1810
|
-
this.publish({
|
|
1936
|
+
if (generation === this.generation) this.publish({
|
|
1811
1937
|
status: "error",
|
|
1812
1938
|
error: messageOf(error)
|
|
1813
1939
|
});
|
|
1814
1940
|
}
|
|
1815
1941
|
})().finally(() => {
|
|
1816
|
-
this.inflight = void 0;
|
|
1942
|
+
if (this.inflight === run) this.inflight = void 0;
|
|
1817
1943
|
});
|
|
1944
|
+
this.inflightGeneration = generation;
|
|
1945
|
+
this.inflightForce = force;
|
|
1818
1946
|
this.inflight = run;
|
|
1819
1947
|
return run;
|
|
1820
1948
|
}
|
|
@@ -1843,8 +1971,10 @@ window.__ModuleLoader__.load({
|
|
|
1843
1971
|
}
|
|
1844
1972
|
}
|
|
1845
1973
|
async refreshCredentials() {
|
|
1974
|
+
const generation = this.generation;
|
|
1846
1975
|
try {
|
|
1847
1976
|
const result = await this.deps.describeCredentials();
|
|
1977
|
+
if (generation !== this.generation) return;
|
|
1848
1978
|
if (!result.ok) {
|
|
1849
1979
|
this.publish({ credentialsError: result.error.message });
|
|
1850
1980
|
return;
|
|
@@ -1854,7 +1984,7 @@ window.__ModuleLoader__.load({
|
|
|
1854
1984
|
credentialsError: void 0
|
|
1855
1985
|
});
|
|
1856
1986
|
} catch (error) {
|
|
1857
|
-
this.publish({ credentialsError: messageOf(error) });
|
|
1987
|
+
if (generation === this.generation) this.publish({ credentialsError: messageOf(error) });
|
|
1858
1988
|
}
|
|
1859
1989
|
}
|
|
1860
1990
|
/**
|
|
@@ -1946,6 +2076,15 @@ window.__ModuleLoader__.load({
|
|
|
1946
2076
|
store.refresh(false);
|
|
1947
2077
|
if (store.getSnapshot().models.length === 0) store.refreshModels();
|
|
1948
2078
|
}, POLL_INTERVAL_MS);
|
|
2079
|
+
const configurationIdentity = () => {
|
|
2080
|
+
const config = settings.getSnapshot().value;
|
|
2081
|
+
return config === void 0 ? void 0 : JSON.stringify([
|
|
2082
|
+
config.order,
|
|
2083
|
+
config.providers,
|
|
2084
|
+
config.sources
|
|
2085
|
+
]);
|
|
2086
|
+
};
|
|
2087
|
+
let configuredIdentity = configurationIdentity();
|
|
1949
2088
|
const disposers = [
|
|
1950
2089
|
ctx.on("api-session/status", (...args) => {
|
|
1951
2090
|
if (args[1] === false) store.refresh(false);
|
|
@@ -1954,7 +2093,13 @@ window.__ModuleLoader__.load({
|
|
|
1954
2093
|
store.refresh(true);
|
|
1955
2094
|
}),
|
|
1956
2095
|
settings.subscribe(() => {
|
|
2096
|
+
const nextIdentity = configurationIdentity();
|
|
2097
|
+
if (nextIdentity !== configuredIdentity) {
|
|
2098
|
+
configuredIdentity = nextIdentity;
|
|
2099
|
+
store.invalidate();
|
|
2100
|
+
}
|
|
1957
2101
|
store.refresh(false);
|
|
2102
|
+
store.refreshCredentials();
|
|
1958
2103
|
})
|
|
1959
2104
|
];
|
|
1960
2105
|
return () => {
|