dsh-usage-state 0.4.3 → 0.4.5
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 +118 -0
- package/README.en.md +61 -118
- package/README.md +58 -147
- package/docs/adapters.md +49 -80
- package/lib/client.js +190 -59
- package/lib/index.js +279 -167
- package/lib/typert.js +1 -0
- package/package.json +17 -7
package/lib/client.js
CHANGED
|
@@ -299,13 +299,17 @@ window.__ModuleLoader__.load({
|
|
|
299
299
|
*/
|
|
300
300
|
function resolveProvider(input) {
|
|
301
301
|
const entry = input.config.providers[input.provider];
|
|
302
|
+
const sourceId = entry?.sourceId ?? suggestSourceId(input.provider, input.endpointHint) ?? null;
|
|
303
|
+
const defaults = sourceId === null ? void 0 : input.config.sources[sourceId];
|
|
302
304
|
const declared = originOf(input.endpointHint);
|
|
303
|
-
const
|
|
305
|
+
const explicitBaseUrl = entry?.baseUrl ?? defaults?.baseUrl;
|
|
306
|
+
const baseUrl = explicitBaseUrl ?? declared;
|
|
307
|
+
const apiKeyRef = entry?.apiKeyRef ?? defaults?.apiKeyRef;
|
|
304
308
|
const base = {
|
|
305
309
|
provider: input.provider,
|
|
306
310
|
...baseUrl === void 0 ? {} : { baseUrl },
|
|
307
|
-
...
|
|
308
|
-
...
|
|
311
|
+
...explicitBaseUrl === void 0 ? {} : { baseUrlPinned: true },
|
|
312
|
+
...apiKeyRef === void 0 ? {} : { apiKeyRef }
|
|
309
313
|
};
|
|
310
314
|
if (entry?.mode === "hidden") return {
|
|
311
315
|
...base,
|
|
@@ -313,7 +317,6 @@ window.__ModuleLoader__.load({
|
|
|
313
317
|
mode: null,
|
|
314
318
|
reason: "hidden"
|
|
315
319
|
};
|
|
316
|
-
const sourceId = entry?.sourceId ?? suggestSourceId(input.provider, input.endpointHint) ?? null;
|
|
317
320
|
if (sourceId === null) return {
|
|
318
321
|
...base,
|
|
319
322
|
sourceId: null,
|
|
@@ -550,6 +553,7 @@ window.__ModuleLoader__.load({
|
|
|
550
553
|
const [editing, setEditing] = (0, react.useState)(false);
|
|
551
554
|
const shown = editing ? draft : props.value;
|
|
552
555
|
const commit = () => {
|
|
556
|
+
if (!editing || props.disabled) return;
|
|
553
557
|
setEditing(false);
|
|
554
558
|
const next = draft.trim();
|
|
555
559
|
if (next !== props.value.trim()) props.onCommit(next);
|
|
@@ -579,29 +583,54 @@ window.__ModuleLoader__.load({
|
|
|
579
583
|
const { t } = props;
|
|
580
584
|
const [draft, setDraft] = (0, react.useState)("");
|
|
581
585
|
const [note, setNote] = (0, react.useState)(void 0);
|
|
586
|
+
const [pending, setPending] = (0, react.useState)(false);
|
|
587
|
+
const busy = (0, react.useRef)(false);
|
|
582
588
|
const candidates = props.description?.candidates ?? [];
|
|
583
|
-
const configuredRef = props.description?.ref ?? candidates.find((candidate) => candidate.configured)?.ref ?? props.refs[0];
|
|
584
|
-
const
|
|
589
|
+
const configuredRef = props.refOverride ?? props.description?.ref ?? candidates.find((candidate) => candidate.configured)?.ref ?? props.refs[0];
|
|
590
|
+
const candidate = candidates.find((value) => value.ref === configuredRef);
|
|
591
|
+
const sameDescription = props.description?.ref === configuredRef;
|
|
592
|
+
const writable = (candidate?.writable ?? (sameDescription ? props.description?.writable : void 0)) !== false;
|
|
593
|
+
const configured = candidate?.configured ?? (sameDescription && props.description?.configured === true);
|
|
585
594
|
const save = async () => {
|
|
586
|
-
if (props.credentials === void 0 || configuredRef === void 0 || draft.trim() === "") return;
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
595
|
+
if (busy.current || !writable || props.credentials === void 0 || configuredRef === void 0 || draft.trim() === "") return;
|
|
596
|
+
busy.current = true;
|
|
597
|
+
setPending(true);
|
|
598
|
+
setNote(void 0);
|
|
599
|
+
try {
|
|
600
|
+
const result = await props.credentials.set(configuredRef, draft.trim());
|
|
601
|
+
if (!result.ok) {
|
|
602
|
+
setNote(t("credentialFailed", { message: result.error.message }));
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
setDraft("");
|
|
606
|
+
setNote(t("credentialSaved"));
|
|
607
|
+
props.onChanged();
|
|
608
|
+
} catch (error) {
|
|
609
|
+
setNote(t("credentialFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
610
|
+
} finally {
|
|
611
|
+
busy.current = false;
|
|
612
|
+
setPending(false);
|
|
591
613
|
}
|
|
592
|
-
setDraft("");
|
|
593
|
-
setNote(t("credentialSaved"));
|
|
594
|
-
props.onChanged();
|
|
595
614
|
};
|
|
596
615
|
const clear = async () => {
|
|
597
|
-
if (props.credentials === void 0 || configuredRef === void 0) return;
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
616
|
+
if (busy.current || !writable || props.credentials === void 0 || configuredRef === void 0) return;
|
|
617
|
+
busy.current = true;
|
|
618
|
+
setPending(true);
|
|
619
|
+
setNote(void 0);
|
|
620
|
+
try {
|
|
621
|
+
const result = await props.credentials.unset(configuredRef);
|
|
622
|
+
if (!result.ok) {
|
|
623
|
+
setNote(t("credentialFailed", { message: result.error.message }));
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
setNote(t("credentialSaved"));
|
|
627
|
+
props.onChanged();
|
|
628
|
+
} catch (error) {
|
|
629
|
+
setNote(t("credentialFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
630
|
+
} finally {
|
|
631
|
+
busy.current = false;
|
|
632
|
+
setPending(false);
|
|
602
633
|
}
|
|
603
|
-
setNote(t("credentialSaved"));
|
|
604
|
-
props.onChanged();
|
|
605
634
|
};
|
|
606
635
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
607
636
|
style: {
|
|
@@ -646,7 +675,7 @@ window.__ModuleLoader__.load({
|
|
|
646
675
|
type: "password",
|
|
647
676
|
autoComplete: "off",
|
|
648
677
|
value: draft,
|
|
649
|
-
disabled: !writable || props.credentials === void 0,
|
|
678
|
+
disabled: pending || !writable || props.credentials === void 0,
|
|
650
679
|
placeholder: t("credentialPlaceholder"),
|
|
651
680
|
onChange: (event) => setDraft(event.target.value),
|
|
652
681
|
style: { maxWidth: "280px" }
|
|
@@ -654,14 +683,14 @@ window.__ModuleLoader__.load({
|
|
|
654
683
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
655
684
|
size: "sm",
|
|
656
685
|
variant: "primary",
|
|
657
|
-
disabled: draft.trim() === "",
|
|
686
|
+
disabled: pending || !writable || props.credentials === void 0 || configuredRef === void 0 || draft.trim() === "",
|
|
658
687
|
onClick: () => void save(),
|
|
659
688
|
children: t("credentialSave")
|
|
660
689
|
}),
|
|
661
690
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
662
691
|
size: "sm",
|
|
663
692
|
variant: "outline",
|
|
664
|
-
disabled: configuredRef === void 0 ||
|
|
693
|
+
disabled: pending || !writable || props.credentials === void 0 || configuredRef === void 0 || !configured,
|
|
665
694
|
onClick: () => void clear(),
|
|
666
695
|
children: t("credentialClear")
|
|
667
696
|
})
|
|
@@ -717,7 +746,7 @@ window.__ModuleLoader__.load({
|
|
|
717
746
|
size: "sm",
|
|
718
747
|
variant: "ghost",
|
|
719
748
|
"aria-label": t("moveUp"),
|
|
720
|
-
disabled: props.index === 0,
|
|
749
|
+
disabled: props.disabled || props.index === 0,
|
|
721
750
|
onClick: () => props.onMove(-1),
|
|
722
751
|
children: "↑"
|
|
723
752
|
}),
|
|
@@ -725,13 +754,14 @@ window.__ModuleLoader__.load({
|
|
|
725
754
|
size: "sm",
|
|
726
755
|
variant: "ghost",
|
|
727
756
|
"aria-label": t("moveDown"),
|
|
728
|
-
disabled: props.index === props.total - 1,
|
|
757
|
+
disabled: props.disabled || props.index === props.total - 1,
|
|
729
758
|
onClick: () => props.onMove(1),
|
|
730
759
|
children: "↓"
|
|
731
760
|
}),
|
|
732
761
|
row.modes.map((mode) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
733
762
|
size: "sm",
|
|
734
763
|
variant: row.selected === mode ? "primary" : "outline",
|
|
764
|
+
disabled: props.disabled,
|
|
735
765
|
onClick: () => props.onMode(mode),
|
|
736
766
|
children: modeLabel(mode, t)
|
|
737
767
|
}, mode))
|
|
@@ -774,6 +804,7 @@ window.__ModuleLoader__.load({
|
|
|
774
804
|
style: MUTED,
|
|
775
805
|
children: t("sourceLabel")
|
|
776
806
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
807
|
+
disabled: props.disabled,
|
|
777
808
|
value: entry?.sourceId ?? "",
|
|
778
809
|
onChange: (event) => {
|
|
779
810
|
const value = event.target.value;
|
|
@@ -805,6 +836,7 @@ window.__ModuleLoader__.load({
|
|
|
805
836
|
children: t("baseUrl")
|
|
806
837
|
}),
|
|
807
838
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftInput, {
|
|
839
|
+
disabled: props.disabled,
|
|
808
840
|
value: entry?.baseUrl ?? "",
|
|
809
841
|
placeholder: row.resolution.mode === null ? t("baseUrlPlaceholder") : resolvedSource?.defaultBaseUrl[row.resolution.mode] ?? t("baseUrlPlaceholder"),
|
|
810
842
|
width: "320px",
|
|
@@ -835,6 +867,7 @@ window.__ModuleLoader__.load({
|
|
|
835
867
|
children: t("apiKeyRef")
|
|
836
868
|
}),
|
|
837
869
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftInput, {
|
|
870
|
+
disabled: props.disabled,
|
|
838
871
|
value: entry?.apiKeyRef ?? "",
|
|
839
872
|
width: "220px",
|
|
840
873
|
onCommit: (value) => {
|
|
@@ -859,6 +892,7 @@ window.__ModuleLoader__.load({
|
|
|
859
892
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CredentialPanel, {
|
|
860
893
|
t,
|
|
861
894
|
refs,
|
|
895
|
+
refOverride: row.resolution.apiKeyRef,
|
|
862
896
|
description: props.description,
|
|
863
897
|
credentials: props.credentials,
|
|
864
898
|
onChanged: props.onCredentialChanged
|
|
@@ -878,6 +912,7 @@ window.__ModuleLoader__.load({
|
|
|
878
912
|
const { t } = props;
|
|
879
913
|
const snapshot = useSettingsValue(props.settings);
|
|
880
914
|
const state = useStoreState(props.usageState);
|
|
915
|
+
const [writeError, setWriteError] = (0, react.useState)(void 0);
|
|
881
916
|
(0, react.useEffect)(() => {
|
|
882
917
|
props.usageState.refreshModels();
|
|
883
918
|
props.usageState.refreshCredentials();
|
|
@@ -891,17 +926,27 @@ window.__ModuleLoader__.load({
|
|
|
891
926
|
models: state.models,
|
|
892
927
|
config,
|
|
893
928
|
catalog: state.catalog,
|
|
894
|
-
registry: state.modelRegistry
|
|
929
|
+
registry: state.modelRegistry,
|
|
930
|
+
endpointHints: state.endpointHints
|
|
895
931
|
});
|
|
932
|
+
const mutate = async (ops) => {
|
|
933
|
+
if (!snapshot.writable) return;
|
|
934
|
+
setWriteError(void 0);
|
|
935
|
+
try {
|
|
936
|
+
await props.settings.mutate(ops);
|
|
937
|
+
} catch (error) {
|
|
938
|
+
setWriteError(error instanceof Error ? error.message : String(error));
|
|
939
|
+
}
|
|
940
|
+
};
|
|
896
941
|
const write = (path, value) => {
|
|
897
|
-
|
|
942
|
+
mutate([{
|
|
898
943
|
op: "set",
|
|
899
944
|
path,
|
|
900
945
|
value
|
|
901
946
|
}]);
|
|
902
947
|
};
|
|
903
948
|
const clear = (path) => {
|
|
904
|
-
|
|
949
|
+
mutate([{
|
|
905
950
|
op: "unset",
|
|
906
951
|
path
|
|
907
952
|
}]);
|
|
@@ -952,6 +997,15 @@ window.__ModuleLoader__.load({
|
|
|
952
997
|
state.modelsError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
953
998
|
style: MUTED,
|
|
954
999
|
children: state.modelsError
|
|
1000
|
+
}),
|
|
1001
|
+
state.credentialsError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1002
|
+
style: MUTED,
|
|
1003
|
+
children: t("credentialFailed", { message: state.credentialsError })
|
|
1004
|
+
}),
|
|
1005
|
+
writeError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1006
|
+
role: "alert",
|
|
1007
|
+
style: MUTED,
|
|
1008
|
+
children: t("settingsFailed", { message: writeError })
|
|
955
1009
|
})
|
|
956
1010
|
]
|
|
957
1011
|
})
|
|
@@ -979,14 +1033,19 @@ window.__ModuleLoader__.load({
|
|
|
979
1033
|
failure: row.resolution.key === void 0 ? void 0 : state.snapshots[row.resolution.key]?.error,
|
|
980
1034
|
index,
|
|
981
1035
|
total: rows.length,
|
|
1036
|
+
disabled: !snapshot.writable,
|
|
982
1037
|
onMode: (mode) => write(["providers"], setProviderMode(config.providers, row.provider, mode)),
|
|
983
1038
|
onMove: (delta) => {
|
|
984
|
-
const next = reorderProviders(
|
|
1039
|
+
const next = reorderProviders(rows.map((candidate) => candidate.provider), row.provider, delta);
|
|
985
1040
|
if (next !== void 0) write(["order"], next);
|
|
986
1041
|
},
|
|
987
1042
|
onField: (path, value) => write(path, value),
|
|
988
1043
|
onClearField: (path) => clear(path),
|
|
989
|
-
onCredentialChanged: () =>
|
|
1044
|
+
onCredentialChanged: () => {
|
|
1045
|
+
props.usageState.invalidate();
|
|
1046
|
+
props.usageState.refreshCredentials();
|
|
1047
|
+
props.usageState.refresh(true);
|
|
1048
|
+
}
|
|
990
1049
|
}, row.provider))
|
|
991
1050
|
]
|
|
992
1051
|
}),
|
|
@@ -1003,6 +1062,7 @@ window.__ModuleLoader__.load({
|
|
|
1003
1062
|
}),
|
|
1004
1063
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftInput, {
|
|
1005
1064
|
type: "number",
|
|
1065
|
+
disabled: !snapshot.writable,
|
|
1006
1066
|
value: String(config.display.thresholdWarnPercent),
|
|
1007
1067
|
width: "90px",
|
|
1008
1068
|
onCommit: (value) => write(["display", "thresholdWarnPercent"], Number(value))
|
|
@@ -1013,6 +1073,7 @@ window.__ModuleLoader__.load({
|
|
|
1013
1073
|
}),
|
|
1014
1074
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftInput, {
|
|
1015
1075
|
type: "number",
|
|
1076
|
+
disabled: !snapshot.writable,
|
|
1016
1077
|
value: String(config.display.thresholdCriticalPercent),
|
|
1017
1078
|
width: "90px",
|
|
1018
1079
|
onCommit: (value) => write(["display", "thresholdCriticalPercent"], Number(value))
|
|
@@ -1026,12 +1087,14 @@ window.__ModuleLoader__.load({
|
|
|
1026
1087
|
children: t("intervalMinutes")
|
|
1027
1088
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftInput, {
|
|
1028
1089
|
type: "number",
|
|
1090
|
+
disabled: !snapshot.writable,
|
|
1029
1091
|
value: String(config.refresh.intervalMinutes),
|
|
1030
1092
|
width: "90px",
|
|
1031
1093
|
onCommit: (value) => write(["refresh", "intervalMinutes"], Number(value))
|
|
1032
1094
|
})]
|
|
1033
1095
|
}),
|
|
1034
1096
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
1097
|
+
disabled: !snapshot.writable,
|
|
1035
1098
|
checked: config.display.progressBar,
|
|
1036
1099
|
label: t("progressBar"),
|
|
1037
1100
|
onChange: (next) => write(["display", "progressBar"], next)
|
|
@@ -1123,7 +1186,9 @@ window.__ModuleLoader__.load({
|
|
|
1123
1186
|
kind: "label",
|
|
1124
1187
|
text: input.sourceLabel,
|
|
1125
1188
|
stale: true,
|
|
1126
|
-
|
|
1189
|
+
staleSince: snapshot.fetchedAt,
|
|
1190
|
+
...snapshot.error === void 0 ? {} : { errorKind: snapshot.error.kind },
|
|
1191
|
+
...snapshot.error?.detail === void 0 ? {} : { errorDetail: snapshot.error.detail }
|
|
1127
1192
|
} : {
|
|
1128
1193
|
kind: "label",
|
|
1129
1194
|
text: input.sourceLabel
|
|
@@ -1178,6 +1243,10 @@ window.__ModuleLoader__.load({
|
|
|
1178
1243
|
const present = pieces.filter((piece) => piece !== void 0 && piece !== "");
|
|
1179
1244
|
return present.length === 0 ? void 0 : present.join(" · ");
|
|
1180
1245
|
}
|
|
1246
|
+
function failureReason(kind, detail, t) {
|
|
1247
|
+
if (kind === void 0) return detail;
|
|
1248
|
+
return detail === void 0 ? t(`error.${kind}`) : `${t(`error.${kind}`)}: ${detail}`;
|
|
1249
|
+
}
|
|
1181
1250
|
function localTime(epochMs) {
|
|
1182
1251
|
return new Date(epochMs).toLocaleTimeString();
|
|
1183
1252
|
}
|
|
@@ -1195,20 +1264,17 @@ window.__ModuleLoader__.load({
|
|
|
1195
1264
|
for (const segment of segments) switch (segment.kind) {
|
|
1196
1265
|
case "label": {
|
|
1197
1266
|
const staleHint = segment.stale === true ? t("staleHint") : void 0;
|
|
1198
|
-
const
|
|
1267
|
+
const tooltip = tooltipOf([
|
|
1268
|
+
staleHint,
|
|
1269
|
+
segment.staleSince === void 0 ? void 0 : t("tipUpdated", { age: formatAge(segment.staleSince, now) }),
|
|
1270
|
+
failureReason(segment.errorKind, segment.errorDetail, t),
|
|
1271
|
+
context
|
|
1272
|
+
]);
|
|
1199
1273
|
parts.push({
|
|
1200
1274
|
kind: "label",
|
|
1201
1275
|
text: segment.text,
|
|
1202
1276
|
stale: segment.stale === true,
|
|
1203
|
-
...
|
|
1204
|
-
staleHint,
|
|
1205
|
-
refreshHint,
|
|
1206
|
-
context
|
|
1207
|
-
]) === void 0 ? {} : { tooltip: tooltipOf([
|
|
1208
|
-
staleHint,
|
|
1209
|
-
refreshHint,
|
|
1210
|
-
context
|
|
1211
|
-
]) }
|
|
1277
|
+
...tooltip === void 0 ? {} : { tooltip }
|
|
1212
1278
|
});
|
|
1213
1279
|
if (segment.staleSince !== void 0) parts.push({
|
|
1214
1280
|
kind: "age",
|
|
@@ -1246,7 +1312,7 @@ window.__ModuleLoader__.load({
|
|
|
1246
1312
|
break;
|
|
1247
1313
|
}
|
|
1248
1314
|
case "state": {
|
|
1249
|
-
const reason = segment.errorKind
|
|
1315
|
+
const reason = failureReason(segment.errorKind, segment.errorDetail, t);
|
|
1250
1316
|
parts.push({
|
|
1251
1317
|
kind: "state",
|
|
1252
1318
|
state: segment.state,
|
|
@@ -1435,7 +1501,7 @@ window.__ModuleLoader__.load({
|
|
|
1435
1501
|
* One read-only usage line: balance in API mode, 5h/7d quota in coding-plan mode.
|
|
1436
1502
|
*
|
|
1437
1503
|
* It reports the account's **current** reading, so it has exactly one home: the
|
|
1438
|
-
* line
|
|
1504
|
+
* line above the composer. There is no per-turn copy — an account-level number
|
|
1439
1505
|
* cannot honestly describe a single turn (see `slots.ts`).
|
|
1440
1506
|
*/
|
|
1441
1507
|
function StatusLine(props) {
|
|
@@ -1449,9 +1515,19 @@ window.__ModuleLoader__.load({
|
|
|
1449
1515
|
const status = statusOf(resolveProvider({
|
|
1450
1516
|
provider: current.provider,
|
|
1451
1517
|
config,
|
|
1452
|
-
catalog: state.catalog
|
|
1518
|
+
catalog: state.catalog,
|
|
1519
|
+
endpointHint: state.endpointHints?.[current.provider]
|
|
1453
1520
|
}), state.catalog.length === 0);
|
|
1454
|
-
|
|
1521
|
+
if (status.kind === "hidden") return null;
|
|
1522
|
+
const kept = status.kind === "ready" ? state.snapshots[status.key] : void 0;
|
|
1523
|
+
const snapshot = kept === void 0 || state.error === void 0 ? kept : {
|
|
1524
|
+
...kept,
|
|
1525
|
+
stale: true,
|
|
1526
|
+
error: {
|
|
1527
|
+
kind: "network",
|
|
1528
|
+
detail: state.error
|
|
1529
|
+
}
|
|
1530
|
+
};
|
|
1455
1531
|
const sourceLabel = status.kind === "ready" ? state.catalog.find((entry) => entry.id === status.sourceId)?.displayName ?? status.sourceId : "";
|
|
1456
1532
|
const fullParts = statusParts({
|
|
1457
1533
|
segments: state.catalog.length === 0 && state.status === "error" && state.error !== void 0 ? [{
|
|
@@ -1491,7 +1567,7 @@ window.__ModuleLoader__.load({
|
|
|
1491
1567
|
const zh = {
|
|
1492
1568
|
nav: "用量状态",
|
|
1493
1569
|
title: "用量状态",
|
|
1494
|
-
intro: "
|
|
1570
|
+
intro: "为每个供应商选择显示账户余额(API 模式)或套餐额度(Coding Plan 模式)。数据由宿主定时刷新,密钥交给 DSH 凭据库保存,本插件不另存明文。",
|
|
1495
1571
|
refreshNow: "立即刷新",
|
|
1496
1572
|
refreshing: "刷新中…",
|
|
1497
1573
|
lastChecked: "更新于 {time}",
|
|
@@ -1527,7 +1603,7 @@ window.__ModuleLoader__.load({
|
|
|
1527
1603
|
credential: "密钥",
|
|
1528
1604
|
credentialConfigured: "已配置(来源:{source})",
|
|
1529
1605
|
credentialMissing: "未配置",
|
|
1530
|
-
credentialLocked: "
|
|
1606
|
+
credentialLocked: "当前凭据来源无法在此修改",
|
|
1531
1607
|
credentialPlaceholder: "粘贴密钥…",
|
|
1532
1608
|
credentialSave: "保存",
|
|
1533
1609
|
credentialClear: "清除",
|
|
@@ -1537,7 +1613,8 @@ window.__ModuleLoader__.load({
|
|
|
1537
1613
|
intervalMinutes: "空闲刷新间隔(分钟)",
|
|
1538
1614
|
thresholdWarn: "黄色阈值(已用 %)",
|
|
1539
1615
|
thresholdCritical: "红色阈值(已用 %)",
|
|
1540
|
-
progressBar: "
|
|
1616
|
+
progressBar: "显示进度环",
|
|
1617
|
+
settingsFailed: "无法保存设置:{message}",
|
|
1541
1618
|
refreshFailed: "刷新失败:{message}",
|
|
1542
1619
|
"state.loading": "读取中…",
|
|
1543
1620
|
"state.unconfigured": "未配置",
|
|
@@ -1566,7 +1643,7 @@ window.__ModuleLoader__.load({
|
|
|
1566
1643
|
const en = {
|
|
1567
1644
|
nav: "Usage state",
|
|
1568
1645
|
title: "Usage state",
|
|
1569
|
-
intro: "Choose what to show for each
|
|
1646
|
+
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
1647
|
refreshNow: "Refresh now",
|
|
1571
1648
|
refreshing: "Refreshing…",
|
|
1572
1649
|
lastChecked: "Updated {time}",
|
|
@@ -1602,7 +1679,7 @@ window.__ModuleLoader__.load({
|
|
|
1602
1679
|
credential: "API key",
|
|
1603
1680
|
credentialConfigured: "Configured ({source})",
|
|
1604
1681
|
credentialMissing: "Not configured",
|
|
1605
|
-
credentialLocked: "
|
|
1682
|
+
credentialLocked: "This credential source cannot be edited here",
|
|
1606
1683
|
credentialPlaceholder: "Paste the key…",
|
|
1607
1684
|
credentialSave: "Save",
|
|
1608
1685
|
credentialClear: "Clear",
|
|
@@ -1612,7 +1689,8 @@ window.__ModuleLoader__.load({
|
|
|
1612
1689
|
intervalMinutes: "Idle refresh interval (minutes)",
|
|
1613
1690
|
thresholdWarn: "Amber threshold (used %)",
|
|
1614
1691
|
thresholdCritical: "Red threshold (used %)",
|
|
1615
|
-
progressBar: "Show progress
|
|
1692
|
+
progressBar: "Show progress ring",
|
|
1693
|
+
settingsFailed: "Could not save settings: {message}",
|
|
1616
1694
|
refreshFailed: "Refresh failed: {message}",
|
|
1617
1695
|
"state.loading": "Reading…",
|
|
1618
1696
|
"state.unconfigured": "Not configured",
|
|
@@ -1660,6 +1738,9 @@ window.__ModuleLoader__.load({
|
|
|
1660
1738
|
}
|
|
1661
1739
|
//#endregion
|
|
1662
1740
|
//#region src/client/settings-form.ts
|
|
1741
|
+
async function requireAccepted(write) {
|
|
1742
|
+
if (!await write) throw new Error("The host did not accept the settings write");
|
|
1743
|
+
}
|
|
1663
1744
|
/**
|
|
1664
1745
|
* Present one `configForms` entry as the settings scope the components consume.
|
|
1665
1746
|
*
|
|
@@ -1696,13 +1777,13 @@ window.__ModuleLoader__.load({
|
|
|
1696
1777
|
getSnapshot: snapshot,
|
|
1697
1778
|
subscribe: (listener) => form.subscribe(listener),
|
|
1698
1779
|
set: async (field, value) => {
|
|
1699
|
-
await form.set(field, value);
|
|
1780
|
+
await requireAccepted(form.set(field, value));
|
|
1700
1781
|
},
|
|
1701
1782
|
unset: async (field) => {
|
|
1702
|
-
await form.unset(field);
|
|
1783
|
+
await requireAccepted(form.unset(field));
|
|
1703
1784
|
},
|
|
1704
1785
|
mutate: async (ops, expectedRevision) => {
|
|
1705
|
-
await form.mutate(ops, expectedRevision);
|
|
1786
|
+
await requireAccepted(form.mutate(ops, expectedRevision));
|
|
1706
1787
|
}
|
|
1707
1788
|
};
|
|
1708
1789
|
}
|
|
@@ -1766,7 +1847,11 @@ window.__ModuleLoader__.load({
|
|
|
1766
1847
|
credentialsError: void 0,
|
|
1767
1848
|
modelsError: void 0
|
|
1768
1849
|
};
|
|
1850
|
+
generation = 0;
|
|
1851
|
+
inflightGeneration = 0;
|
|
1852
|
+
inflightForce = false;
|
|
1769
1853
|
inflight;
|
|
1854
|
+
forcedAfterInflight;
|
|
1770
1855
|
constructor(deps) {
|
|
1771
1856
|
this.deps = deps;
|
|
1772
1857
|
}
|
|
@@ -1786,12 +1871,38 @@ window.__ModuleLoader__.load({
|
|
|
1786
1871
|
};
|
|
1787
1872
|
for (const listener of [...this.listeners]) listener();
|
|
1788
1873
|
}
|
|
1874
|
+
/** A provider/credential identity changed: its previous RPC cannot restore old values. */
|
|
1875
|
+
invalidate() {
|
|
1876
|
+
this.generation++;
|
|
1877
|
+
this.forcedAfterInflight = void 0;
|
|
1878
|
+
this.publish({
|
|
1879
|
+
status: "loading",
|
|
1880
|
+
snapshots: {},
|
|
1881
|
+
credentials: {},
|
|
1882
|
+
checkedAt: void 0,
|
|
1883
|
+
error: void 0,
|
|
1884
|
+
credentialsError: void 0
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1789
1887
|
refresh(force = false) {
|
|
1790
|
-
|
|
1888
|
+
const generation = this.generation;
|
|
1889
|
+
if (this.inflight !== void 0 && this.inflightGeneration === generation) {
|
|
1890
|
+
if (!force || this.inflightForce) return this.inflight;
|
|
1891
|
+
if (this.forcedAfterInflight === void 0) {
|
|
1892
|
+
const queued = this.inflight.then(() => {
|
|
1893
|
+
if (this.generation === generation) return this.refresh(true);
|
|
1894
|
+
}).finally(() => {
|
|
1895
|
+
if (this.forcedAfterInflight === queued) this.forcedAfterInflight = void 0;
|
|
1896
|
+
});
|
|
1897
|
+
this.forcedAfterInflight = queued;
|
|
1898
|
+
}
|
|
1899
|
+
return this.forcedAfterInflight;
|
|
1900
|
+
}
|
|
1791
1901
|
this.publish({ status: "loading" });
|
|
1792
1902
|
const run = (async () => {
|
|
1793
1903
|
try {
|
|
1794
1904
|
const result = await this.deps.getState(force);
|
|
1905
|
+
if (generation !== this.generation) return;
|
|
1795
1906
|
if (!result.ok) {
|
|
1796
1907
|
this.publish({
|
|
1797
1908
|
status: "error",
|
|
@@ -1802,19 +1913,22 @@ window.__ModuleLoader__.load({
|
|
|
1802
1913
|
this.publish({
|
|
1803
1914
|
status: "ready",
|
|
1804
1915
|
catalog: result.value.sources,
|
|
1916
|
+
endpointHints: result.value.endpointHints ?? {},
|
|
1805
1917
|
snapshots: result.value.snapshots,
|
|
1806
1918
|
checkedAt: result.value.checkedAt,
|
|
1807
1919
|
error: void 0
|
|
1808
1920
|
});
|
|
1809
1921
|
} catch (error) {
|
|
1810
|
-
this.publish({
|
|
1922
|
+
if (generation === this.generation) this.publish({
|
|
1811
1923
|
status: "error",
|
|
1812
1924
|
error: messageOf(error)
|
|
1813
1925
|
});
|
|
1814
1926
|
}
|
|
1815
1927
|
})().finally(() => {
|
|
1816
|
-
this.inflight = void 0;
|
|
1928
|
+
if (this.inflight === run) this.inflight = void 0;
|
|
1817
1929
|
});
|
|
1930
|
+
this.inflightGeneration = generation;
|
|
1931
|
+
this.inflightForce = force;
|
|
1818
1932
|
this.inflight = run;
|
|
1819
1933
|
return run;
|
|
1820
1934
|
}
|
|
@@ -1843,8 +1957,10 @@ window.__ModuleLoader__.load({
|
|
|
1843
1957
|
}
|
|
1844
1958
|
}
|
|
1845
1959
|
async refreshCredentials() {
|
|
1960
|
+
const generation = this.generation;
|
|
1846
1961
|
try {
|
|
1847
1962
|
const result = await this.deps.describeCredentials();
|
|
1963
|
+
if (generation !== this.generation) return;
|
|
1848
1964
|
if (!result.ok) {
|
|
1849
1965
|
this.publish({ credentialsError: result.error.message });
|
|
1850
1966
|
return;
|
|
@@ -1854,7 +1970,7 @@ window.__ModuleLoader__.load({
|
|
|
1854
1970
|
credentialsError: void 0
|
|
1855
1971
|
});
|
|
1856
1972
|
} catch (error) {
|
|
1857
|
-
this.publish({ credentialsError: messageOf(error) });
|
|
1973
|
+
if (generation === this.generation) this.publish({ credentialsError: messageOf(error) });
|
|
1858
1974
|
}
|
|
1859
1975
|
}
|
|
1860
1976
|
/**
|
|
@@ -1946,6 +2062,15 @@ window.__ModuleLoader__.load({
|
|
|
1946
2062
|
store.refresh(false);
|
|
1947
2063
|
if (store.getSnapshot().models.length === 0) store.refreshModels();
|
|
1948
2064
|
}, POLL_INTERVAL_MS);
|
|
2065
|
+
const configurationIdentity = () => {
|
|
2066
|
+
const config = settings.getSnapshot().value;
|
|
2067
|
+
return config === void 0 ? void 0 : JSON.stringify([
|
|
2068
|
+
config.order,
|
|
2069
|
+
config.providers,
|
|
2070
|
+
config.sources
|
|
2071
|
+
]);
|
|
2072
|
+
};
|
|
2073
|
+
let configuredIdentity = configurationIdentity();
|
|
1949
2074
|
const disposers = [
|
|
1950
2075
|
ctx.on("api-session/status", (...args) => {
|
|
1951
2076
|
if (args[1] === false) store.refresh(false);
|
|
@@ -1954,7 +2079,13 @@ window.__ModuleLoader__.load({
|
|
|
1954
2079
|
store.refresh(true);
|
|
1955
2080
|
}),
|
|
1956
2081
|
settings.subscribe(() => {
|
|
2082
|
+
const nextIdentity = configurationIdentity();
|
|
2083
|
+
if (nextIdentity !== configuredIdentity) {
|
|
2084
|
+
configuredIdentity = nextIdentity;
|
|
2085
|
+
store.invalidate();
|
|
2086
|
+
}
|
|
1957
2087
|
store.refresh(false);
|
|
2088
|
+
store.refreshCredentials();
|
|
1958
2089
|
})
|
|
1959
2090
|
];
|
|
1960
2091
|
return () => {
|