@mars-sea/dsh-commandcode-provider 0.6.0 → 0.6.2
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 +29 -0
- package/README.md +59 -135
- package/README.zh-CN.md +58 -136
- package/lib/client.js +278 -31
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +35 -12
- package/lib/index.js +313 -207
- package/lib/index.js.map +1 -1
- package/package.json +19 -16
package/lib/client.js
CHANGED
|
@@ -59,8 +59,15 @@ window.__ModuleLoader__.load({
|
|
|
59
59
|
}
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
|
-
/**
|
|
63
|
-
|
|
62
|
+
/**
|
|
63
|
+
* A numeric field; an empty draft clears it, anything non-numeric blocks
|
|
64
|
+
* save, and an optional inclusive `bounds` range rejects out-of-range values
|
|
65
|
+
* with a specific reason (the Host schema would reject them at save time with
|
|
66
|
+
* only a generic failure — catching it here names the problem while typing).
|
|
67
|
+
* Decimals pass: the Host schema is `z.number()` too, and a fractional
|
|
68
|
+
* millisecond value is harmless even if pointless.
|
|
69
|
+
*/
|
|
70
|
+
function numberField$1(field, bounds) {
|
|
64
71
|
return {
|
|
65
72
|
field,
|
|
66
73
|
format: (value) => typeof value === "number" ? String(value) : "",
|
|
@@ -68,10 +75,22 @@ window.__ModuleLoader__.load({
|
|
|
68
75
|
const trimmed = text.trim();
|
|
69
76
|
if (trimmed === "") return { kind: "clear" };
|
|
70
77
|
const parsed = Number(trimmed);
|
|
71
|
-
|
|
78
|
+
if (!Number.isFinite(parsed)) return {
|
|
79
|
+
kind: "invalid",
|
|
80
|
+
reason: "format"
|
|
81
|
+
};
|
|
82
|
+
if (bounds?.min !== void 0 && parsed < bounds.min) return {
|
|
83
|
+
kind: "invalid",
|
|
84
|
+
reason: "tooSmall"
|
|
85
|
+
};
|
|
86
|
+
if (bounds?.max !== void 0 && parsed > bounds.max) return {
|
|
87
|
+
kind: "invalid",
|
|
88
|
+
reason: "tooLarge"
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
72
91
|
kind: "set",
|
|
73
92
|
value: parsed
|
|
74
|
-
}
|
|
93
|
+
};
|
|
75
94
|
}
|
|
76
95
|
};
|
|
77
96
|
}
|
|
@@ -95,16 +114,26 @@ window.__ModuleLoader__.load({
|
|
|
95
114
|
kind: "set",
|
|
96
115
|
value: false
|
|
97
116
|
};
|
|
98
|
-
return {
|
|
117
|
+
return {
|
|
118
|
+
kind: "invalid",
|
|
119
|
+
reason: "format"
|
|
120
|
+
};
|
|
99
121
|
}
|
|
100
122
|
};
|
|
101
123
|
}
|
|
124
|
+
const MAX_TIMEOUT_MS = 2147483647;
|
|
102
125
|
/** The fields this page edits inside the `llm-commandcode` namespace. */
|
|
103
126
|
const SECTION_FIELDS = [
|
|
104
127
|
textField("apiBase"),
|
|
105
128
|
textField("workingDir"),
|
|
106
|
-
numberField$1("requestTimeoutMs"
|
|
107
|
-
|
|
129
|
+
numberField$1("requestTimeoutMs", {
|
|
130
|
+
min: 1,
|
|
131
|
+
max: MAX_TIMEOUT_MS
|
|
132
|
+
}),
|
|
133
|
+
numberField$1("streamIdleTimeoutMs", {
|
|
134
|
+
min: 1,
|
|
135
|
+
max: MAX_TIMEOUT_MS
|
|
136
|
+
}),
|
|
108
137
|
booleanField$1("filterModelsByPlan"),
|
|
109
138
|
textField("activeAccount")
|
|
110
139
|
];
|
|
@@ -134,8 +163,11 @@ window.__ModuleLoader__.load({
|
|
|
134
163
|
labelDrafts = /* @__PURE__ */ new Map();
|
|
135
164
|
/** Staged key drafts, by credential reference (blank = keep stored key). */
|
|
136
165
|
keyDrafts = /* @__PURE__ */ new Map();
|
|
166
|
+
/** Credential references staged for removal on the next save. */
|
|
167
|
+
keyClears = /* @__PURE__ */ new Set();
|
|
137
168
|
saving = false;
|
|
138
169
|
failed = false;
|
|
170
|
+
savedCount = 0;
|
|
139
171
|
/**
|
|
140
172
|
* @param scope - bound scope for the `llm-commandcode` namespace.
|
|
141
173
|
* @param api - credentials wire face.
|
|
@@ -206,8 +238,10 @@ window.__ModuleLoader__.load({
|
|
|
206
238
|
text: this.staged.get("apiKey")?.text ?? "",
|
|
207
239
|
clear: false,
|
|
208
240
|
overridden: false,
|
|
209
|
-
invalid: false
|
|
241
|
+
invalid: false,
|
|
242
|
+
invalidReason: void 0
|
|
210
243
|
},
|
|
244
|
+
apiKeyClearStaged: this.keyClears.has(this.credentialRef),
|
|
211
245
|
apiBase: this.field("apiBase"),
|
|
212
246
|
workingDir: this.field("workingDir"),
|
|
213
247
|
defaultWorkingDir: this.defaultWorkingDir,
|
|
@@ -220,7 +254,8 @@ window.__ModuleLoader__.load({
|
|
|
220
254
|
dirty: plan.length > 0 || this.accountsDirty(),
|
|
221
255
|
invalid: plan.some((item) => item.run === void 0),
|
|
222
256
|
saving: this.saving,
|
|
223
|
-
failed: this.failed
|
|
257
|
+
failed: this.failed,
|
|
258
|
+
savedCount: this.savedCount
|
|
224
259
|
};
|
|
225
260
|
}
|
|
226
261
|
/** Stage a new extra account (saved on the next `save()`). */
|
|
@@ -265,15 +300,37 @@ window.__ModuleLoader__.load({
|
|
|
265
300
|
/** Stage one extra account's key draft (blank keeps the stored key). */
|
|
266
301
|
editAccountKey(id, text) {
|
|
267
302
|
this.keyDrafts.set(id, text);
|
|
303
|
+
this.keyClears.delete(id);
|
|
268
304
|
this.failed = false;
|
|
269
305
|
this.publish();
|
|
270
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* Toggle the staged removal of one account's stored key: the next save
|
|
309
|
+
* unsets the credential so the account reports unconfigured and falls back
|
|
310
|
+
* to its other key sources. Only meaningful while a key is actually
|
|
311
|
+
* stored. `target` is `'default'` (the implicit first account) or an extra
|
|
312
|
+
* account's credential reference.
|
|
313
|
+
*/
|
|
314
|
+
toggleKeyClear(target) {
|
|
315
|
+
const ref = target === "default" ? this.credentialRef : target;
|
|
316
|
+
if (this.keyClears.has(ref)) this.keyClears.delete(ref);
|
|
317
|
+
else {
|
|
318
|
+
if (this.credentialStates.get(ref)?.configured !== true) return;
|
|
319
|
+
this.keyDrafts.delete(ref);
|
|
320
|
+
if (ref === this.credentialRef) this.staged.delete("apiKey");
|
|
321
|
+
this.keyClears.add(ref);
|
|
322
|
+
}
|
|
323
|
+
this.failed = false;
|
|
324
|
+
this.describeAll();
|
|
325
|
+
this.publish();
|
|
326
|
+
}
|
|
271
327
|
/** Stage one field's draft text. */
|
|
272
328
|
edit(field, text) {
|
|
273
329
|
this.staged.set(field, {
|
|
274
330
|
text,
|
|
275
331
|
clear: false
|
|
276
332
|
});
|
|
333
|
+
if (field === "apiKey") this.keyClears.delete(this.credentialRef);
|
|
277
334
|
this.failed = false;
|
|
278
335
|
this.publish();
|
|
279
336
|
}
|
|
@@ -322,6 +379,7 @@ window.__ModuleLoader__.load({
|
|
|
322
379
|
this.saving = false;
|
|
323
380
|
this.failed = !landed;
|
|
324
381
|
if (landed) {
|
|
382
|
+
this.savedCount += 1;
|
|
325
383
|
this.staged.clear();
|
|
326
384
|
this.clearAccountStaging();
|
|
327
385
|
} else this.reconcileAccountStaging();
|
|
@@ -342,6 +400,7 @@ window.__ModuleLoader__.load({
|
|
|
342
400
|
const storedLabel = this.storedExtras().find((extra) => extra.ref === ref)?.label;
|
|
343
401
|
if (storedLabel === void 0 || storedLabel === text.trim()) this.labelDrafts.delete(ref);
|
|
344
402
|
}
|
|
403
|
+
for (const ref of [...this.keyClears]) if (this.credentialStates.get(ref)?.configured !== true) this.keyClears.delete(ref);
|
|
345
404
|
}
|
|
346
405
|
spec(field) {
|
|
347
406
|
const spec = this.specs.get(field);
|
|
@@ -356,14 +415,16 @@ window.__ModuleLoader__.load({
|
|
|
356
415
|
text: spec.format(this.sectionValue(field)),
|
|
357
416
|
clear: false,
|
|
358
417
|
overridden: this.stored(field),
|
|
359
|
-
invalid: false
|
|
418
|
+
invalid: false,
|
|
419
|
+
invalidReason: void 0
|
|
360
420
|
};
|
|
361
421
|
const parsed = staged.clear ? { kind: "clear" } : spec.parse(staged.text);
|
|
362
422
|
return {
|
|
363
423
|
text: staged.text,
|
|
364
424
|
clear: staged.clear,
|
|
365
425
|
overridden: parsed.kind === "set",
|
|
366
|
-
invalid: parsed.kind === "invalid"
|
|
426
|
+
invalid: parsed.kind === "invalid",
|
|
427
|
+
invalidReason: parsed.kind === "invalid" ? parsed.reason : void 0
|
|
367
428
|
};
|
|
368
429
|
}
|
|
369
430
|
sectionValue(field) {
|
|
@@ -510,12 +571,13 @@ window.__ModuleLoader__.load({
|
|
|
510
571
|
keyText: this.keyDrafts.get(extra.ref) ?? "",
|
|
511
572
|
configured: this.credentialStates.get(extra.ref)?.configured ?? false,
|
|
512
573
|
writable: this.credentialStates.get(extra.ref)?.writable ?? true,
|
|
513
|
-
added: extra.added
|
|
574
|
+
added: extra.added,
|
|
575
|
+
clearStaged: this.keyClears.has(extra.ref)
|
|
514
576
|
}));
|
|
515
577
|
}
|
|
516
|
-
/** Whether any account-level staging (add/remove/label/key) exists. */
|
|
578
|
+
/** Whether any account-level staging (add/remove/label/key/clear) exists. */
|
|
517
579
|
accountsStaged() {
|
|
518
|
-
return this.addedAccounts.length > 0 || this.removedRefs.size > 0 || this.labelDrafts.size > 0 || this.keyDrafts.size > 0;
|
|
580
|
+
return this.addedAccounts.length > 0 || this.removedRefs.size > 0 || this.labelDrafts.size > 0 || this.keyDrafts.size > 0 || this.keyClears.size > 0;
|
|
519
581
|
}
|
|
520
582
|
/** Whether the staged account edits differ from the stored section. */
|
|
521
583
|
accountsDirty() {
|
|
@@ -525,6 +587,7 @@ window.__ModuleLoader__.load({
|
|
|
525
587
|
if (base !== void 0 && text.trim() !== "" && text !== base) return true;
|
|
526
588
|
}
|
|
527
589
|
for (const text of this.keyDrafts.values()) if (text.trim() !== "") return true;
|
|
590
|
+
for (const ref of this.keyClears) if (this.credentialStates.get(ref)?.configured === true) return true;
|
|
528
591
|
return false;
|
|
529
592
|
}
|
|
530
593
|
/** Reset every account-level staged edit. */
|
|
@@ -533,14 +596,26 @@ window.__ModuleLoader__.load({
|
|
|
533
596
|
this.removedRefs.clear();
|
|
534
597
|
this.labelDrafts.clear();
|
|
535
598
|
this.keyDrafts.clear();
|
|
599
|
+
this.keyClears.clear();
|
|
600
|
+
}
|
|
601
|
+
/** Unset one stored credential, then re-read the Host's credential states. */
|
|
602
|
+
async unsetKey(ref) {
|
|
603
|
+
try {
|
|
604
|
+
if (!(await this.api.credentials.unset({ ref })).result.ok) return false;
|
|
605
|
+
} catch {
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
await this.describeAll();
|
|
609
|
+
return this.credentialStates.get(ref)?.configured !== true;
|
|
536
610
|
}
|
|
537
611
|
/** The account-level writes a save performs (empty when nothing staged). */
|
|
538
612
|
accountPlan() {
|
|
539
613
|
if (!this.accountsDirty()) return [];
|
|
540
614
|
const runs = [];
|
|
615
|
+
for (const ref of this.keyClears) if (this.credentialStates.get(ref)?.configured === true) runs.push(() => this.unsetKey(ref));
|
|
541
616
|
for (const [ref, text] of this.keyDrafts) {
|
|
542
617
|
const value = text.trim();
|
|
543
|
-
if (value !== "" && !this.removedRefs.has(ref)) runs.push(() => this.writeKeyTo(ref, value));
|
|
618
|
+
if (value !== "" && !this.removedRefs.has(ref) && !this.keyClears.has(ref)) runs.push(() => this.writeKeyTo(ref, value));
|
|
544
619
|
}
|
|
545
620
|
runs.push(() => this.writeAccounts());
|
|
546
621
|
return runs;
|
|
@@ -726,6 +801,11 @@ window.__ModuleLoader__.load({
|
|
|
726
801
|
const failures = source.failures;
|
|
727
802
|
if (!Array.isArray(failures) || failures.some((entry) => typeof entry !== "string")) reject("failures");
|
|
728
803
|
const report = { failures };
|
|
804
|
+
if (source.blocked !== void 0) {
|
|
805
|
+
const blocked = source.blocked;
|
|
806
|
+
if (blocked !== "invalid-key" && blocked !== "service-unavailable" && blocked !== "network") reject("blocked");
|
|
807
|
+
report.blocked = blocked;
|
|
808
|
+
}
|
|
729
809
|
if (source.account !== void 0) {
|
|
730
810
|
const account = record(source.account, "account");
|
|
731
811
|
report.account = {
|
|
@@ -809,6 +889,21 @@ window.__ModuleLoader__.load({
|
|
|
809
889
|
}]
|
|
810
890
|
};
|
|
811
891
|
//#endregion
|
|
892
|
+
//#region src/client/version.ts
|
|
893
|
+
/**
|
|
894
|
+
* The plugin's own version, read from package.json at build time.
|
|
895
|
+
*
|
|
896
|
+
* The client bundle inlines the JSON import (rolldown resolves it during the
|
|
897
|
+
* tsdown build; node tests read it through tsx), so the rendered value always
|
|
898
|
+
* matches the published package version with no second constant to keep in
|
|
899
|
+
* sync. Rendered as a muted footer line on the settings page so a user can
|
|
900
|
+
* report the exact build they run.
|
|
901
|
+
*
|
|
902
|
+
* @module dsh-commandcode-provider/client/version
|
|
903
|
+
*/
|
|
904
|
+
/** The published package version (e.g. `'0.6.0'`). */
|
|
905
|
+
const PLUGIN_VERSION = "0.6.2";
|
|
906
|
+
//#endregion
|
|
812
907
|
//#region src/client/section.tsx
|
|
813
908
|
/**
|
|
814
909
|
* React component for the "Command Code" settings page (browser half).
|
|
@@ -861,11 +956,17 @@ window.__ModuleLoader__.load({
|
|
|
861
956
|
}),
|
|
862
957
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
863
958
|
className: state.invalid ? "cc-invalid" : "cc-hint",
|
|
864
|
-
children: state.invalid ? t
|
|
959
|
+
children: state.invalid ? invalidCopy(state.invalidReason, t) : hint
|
|
865
960
|
})
|
|
866
961
|
]
|
|
867
962
|
});
|
|
868
963
|
}
|
|
964
|
+
/** The per-field error copy for a staged draft's failure reason. */
|
|
965
|
+
function invalidCopy(reason, t) {
|
|
966
|
+
if (reason === "tooSmall") return t("numberTooSmall");
|
|
967
|
+
if (reason === "tooLarge") return t("numberTooLarge");
|
|
968
|
+
return t("invalidNumber");
|
|
969
|
+
}
|
|
869
970
|
/**
|
|
870
971
|
* One boolean field row rendered as a toggle. The staged text is `'true'` /
|
|
871
972
|
* `'false'` / `''` (unset → `defaultChecked`); toggling stages the string the
|
|
@@ -911,8 +1012,14 @@ window.__ModuleLoader__.load({
|
|
|
911
1012
|
})]
|
|
912
1013
|
});
|
|
913
1014
|
}
|
|
914
|
-
/**
|
|
915
|
-
|
|
1015
|
+
/**
|
|
1016
|
+
* The API-key control: write-only, reports configured state, never echoes the
|
|
1017
|
+
* key. The input is masked by default with a Show/Hide toggle so a pasted key
|
|
1018
|
+
* can be spot-checked without leaving the field, and a stored key can be
|
|
1019
|
+
* staged for removal (the next save unsets it) when it is bad or unwanted.
|
|
1020
|
+
*/
|
|
1021
|
+
function SecretKeyField({ label, hint, state, disabled, configured, configuredLabel, unconfiguredLabel, clearStaged, showLabel, hideLabel, clearLabel, clearStagedLabel, undoClearLabel, onEdit, onToggleClear }) {
|
|
1022
|
+
const [visible, setVisible] = (0, react.useState)(false);
|
|
916
1023
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
917
1024
|
className: "cc-field",
|
|
918
1025
|
children: [
|
|
@@ -922,19 +1029,40 @@ window.__ModuleLoader__.load({
|
|
|
922
1029
|
className: "cc-label",
|
|
923
1030
|
htmlFor: "cc-api-key",
|
|
924
1031
|
children: label
|
|
925
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
1032
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
926
1033
|
className: "cc-badges",
|
|
927
|
-
children:
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1034
|
+
children: [
|
|
1035
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1036
|
+
className: configured ? "cc-badge" : "cc-badgeMuted",
|
|
1037
|
+
children: configured ? configuredLabel : unconfiguredLabel
|
|
1038
|
+
}),
|
|
1039
|
+
clearStaged ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1040
|
+
className: "cc-badge cc-badgeWarn",
|
|
1041
|
+
children: clearStagedLabel
|
|
1042
|
+
}) : null,
|
|
1043
|
+
configured ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1044
|
+
type: "button",
|
|
1045
|
+
className: "cc-reset",
|
|
1046
|
+
disabled,
|
|
1047
|
+
onClick: onToggleClear,
|
|
1048
|
+
children: clearStaged ? undoClearLabel : clearLabel
|
|
1049
|
+
}) : null,
|
|
1050
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1051
|
+
type: "button",
|
|
1052
|
+
className: "cc-reset",
|
|
1053
|
+
disabled,
|
|
1054
|
+
onClick: () => setVisible((value) => !value),
|
|
1055
|
+
children: visible ? hideLabel : showLabel
|
|
1056
|
+
})
|
|
1057
|
+
]
|
|
931
1058
|
})]
|
|
932
1059
|
}),
|
|
933
1060
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
934
1061
|
id: "cc-api-key",
|
|
935
1062
|
className: "cc-input",
|
|
936
|
-
type: "password",
|
|
1063
|
+
type: visible ? "text" : "password",
|
|
937
1064
|
autoComplete: "off",
|
|
1065
|
+
spellCheck: false,
|
|
938
1066
|
value: state.text,
|
|
939
1067
|
disabled,
|
|
940
1068
|
onChange: (event) => onEdit(event.target.value)
|
|
@@ -1087,6 +1215,17 @@ window.__ModuleLoader__.load({
|
|
|
1087
1215
|
className: "cc-usageHint",
|
|
1088
1216
|
children: t("usageUnconfigured")
|
|
1089
1217
|
}) : null,
|
|
1218
|
+
report.blocked !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1219
|
+
className: "cc-usageBlocked",
|
|
1220
|
+
role: "alert",
|
|
1221
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1222
|
+
className: "cc-usageBlockedTitle",
|
|
1223
|
+
children: blockedTitle(report.blocked, t)
|
|
1224
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1225
|
+
className: "cc-usageBlockedHint",
|
|
1226
|
+
children: blockedHint(report.blocked, t)
|
|
1227
|
+
})]
|
|
1228
|
+
}) : null,
|
|
1090
1229
|
report.usage !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1091
1230
|
className: "cc-usageStats",
|
|
1092
1231
|
children: [
|
|
@@ -1152,13 +1291,13 @@ window.__ModuleLoader__.load({
|
|
|
1152
1291
|
]
|
|
1153
1292
|
}),
|
|
1154
1293
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-usageMetaSpacer" }),
|
|
1155
|
-
report.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1294
|
+
report.failures.length > 0 && report.blocked === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1156
1295
|
className: "cc-usagePartial",
|
|
1157
1296
|
title: report.failures.join("; "),
|
|
1158
1297
|
children: t("usagePartial")
|
|
1159
1298
|
}) : null
|
|
1160
1299
|
]
|
|
1161
|
-
}) : report.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1300
|
+
}) : report.failures.length > 0 && report.blocked === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1162
1301
|
className: "cc-usageMeta",
|
|
1163
1302
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-usageMetaSpacer" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1164
1303
|
className: "cc-usagePartial",
|
|
@@ -1169,6 +1308,18 @@ window.__ModuleLoader__.load({
|
|
|
1169
1308
|
]
|
|
1170
1309
|
});
|
|
1171
1310
|
}
|
|
1311
|
+
/** The headline copy for a report whose every endpoint failed the same way. */
|
|
1312
|
+
function blockedTitle(reason, t) {
|
|
1313
|
+
if (reason === "invalid-key") return t("usageKeyInvalid");
|
|
1314
|
+
if (reason === "service-unavailable") return t("usageServiceUnavailable");
|
|
1315
|
+
return t("usageNetworkError");
|
|
1316
|
+
}
|
|
1317
|
+
/** The actionable hint under a blocked report's headline. */
|
|
1318
|
+
function blockedHint(reason, t) {
|
|
1319
|
+
if (reason === "invalid-key") return t("usageKeyInvalidHint");
|
|
1320
|
+
if (reason === "service-unavailable") return t("usageServiceUnavailableHint");
|
|
1321
|
+
return t("usageNetworkHint");
|
|
1322
|
+
}
|
|
1172
1323
|
/** The status dot on an account tab: cooling/invalid warn, everything else ok. */
|
|
1173
1324
|
function AccountTabDot({ entry }) {
|
|
1174
1325
|
const cls = entry.mark === "invalid-credential" ? "cc-tabDot cc-tabDotError" : entry.mark !== "" || entry.cooldownUntil > 0 ? "cc-tabDot cc-tabDotWarn" : "cc-tabDot cc-tabDotOk";
|
|
@@ -1285,8 +1436,9 @@ window.__ModuleLoader__.load({
|
|
|
1285
1436
|
* otherwise the only way to undo a mistaken Add would be discarding every
|
|
1286
1437
|
* other staged edit.
|
|
1287
1438
|
*/
|
|
1288
|
-
function AccountRow({ account, disabled, t, onLabel, onKey, onRemove }) {
|
|
1439
|
+
function AccountRow({ account, disabled, t, onLabel, onKey, onToggleClear, onRemove }) {
|
|
1289
1440
|
const locked = !account.writable;
|
|
1441
|
+
const [keyVisible, setKeyVisible] = (0, react.useState)(false);
|
|
1290
1442
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1291
1443
|
className: "cc-field",
|
|
1292
1444
|
children: [
|
|
@@ -1307,6 +1459,31 @@ window.__ModuleLoader__.load({
|
|
|
1307
1459
|
className: account.configured ? "cc-badge" : "cc-badgeMuted",
|
|
1308
1460
|
children: account.configured ? t("apiKeySet") : t("apiKeyUnset")
|
|
1309
1461
|
}),
|
|
1462
|
+
account.clearStaged ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1463
|
+
className: "cc-badge cc-badgeWarn",
|
|
1464
|
+
children: t("usageKeyClearStaged")
|
|
1465
|
+
}) : null,
|
|
1466
|
+
account.configured && !account.clearStaged ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1467
|
+
type: "button",
|
|
1468
|
+
className: "cc-reset",
|
|
1469
|
+
disabled,
|
|
1470
|
+
onClick: onToggleClear,
|
|
1471
|
+
children: t("usageKeyClear")
|
|
1472
|
+
}) : null,
|
|
1473
|
+
account.clearStaged ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1474
|
+
type: "button",
|
|
1475
|
+
className: "cc-reset",
|
|
1476
|
+
disabled,
|
|
1477
|
+
onClick: onToggleClear,
|
|
1478
|
+
children: t("usageUndoKeyClear")
|
|
1479
|
+
}) : null,
|
|
1480
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1481
|
+
type: "button",
|
|
1482
|
+
className: "cc-reset",
|
|
1483
|
+
disabled,
|
|
1484
|
+
onClick: () => setKeyVisible((value) => !value),
|
|
1485
|
+
children: keyVisible ? t("hide") : t("show")
|
|
1486
|
+
}),
|
|
1310
1487
|
account.added ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1311
1488
|
type: "button",
|
|
1312
1489
|
className: "cc-reset",
|
|
@@ -1328,8 +1505,9 @@ window.__ModuleLoader__.load({
|
|
|
1328
1505
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1329
1506
|
id: `cc-account-key-${account.id}`,
|
|
1330
1507
|
className: "cc-input",
|
|
1331
|
-
type: "password",
|
|
1508
|
+
type: keyVisible ? "text" : "password",
|
|
1332
1509
|
autoComplete: "off",
|
|
1510
|
+
spellCheck: false,
|
|
1333
1511
|
placeholder: t("accountKey"),
|
|
1334
1512
|
value: account.keyText,
|
|
1335
1513
|
disabled: disabled || locked,
|
|
@@ -1343,7 +1521,7 @@ window.__ModuleLoader__.load({
|
|
|
1343
1521
|
});
|
|
1344
1522
|
}
|
|
1345
1523
|
/** The multi-account card: the active-account selector + extra accounts in rotation order + add button. */
|
|
1346
|
-
function AccountsCard({ t, state, disabled, onAdd, onRemove, onLabel, onKey, onActive, onActiveReset }) {
|
|
1524
|
+
function AccountsCard({ t, state, disabled, onAdd, onRemove, onLabel, onKey, onToggleClear, onActive, onActiveReset }) {
|
|
1347
1525
|
const active = state.activeAccount;
|
|
1348
1526
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1349
1527
|
className: "cc-card",
|
|
@@ -1427,11 +1605,27 @@ window.__ModuleLoader__.load({
|
|
|
1427
1605
|
t,
|
|
1428
1606
|
onLabel: (text) => onLabel(account.id, text),
|
|
1429
1607
|
onKey: (text) => onKey(account.id, text),
|
|
1608
|
+
onToggleClear: () => onToggleClear(account.id),
|
|
1430
1609
|
onRemove: () => onRemove(account.id)
|
|
1431
1610
|
}, account.id))
|
|
1432
1611
|
]
|
|
1433
1612
|
});
|
|
1434
1613
|
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Show the "Saved ✓" affordance for a short window after each accepted save.
|
|
1616
|
+
* The controller only counts saves (`savedCount`); the flash timing lives
|
|
1617
|
+
* here so the state machine stays timer-free.
|
|
1618
|
+
*/
|
|
1619
|
+
function useSavedFlash(tick) {
|
|
1620
|
+
const [visible, setVisible] = (0, react.useState)(false);
|
|
1621
|
+
(0, react.useEffect)(() => {
|
|
1622
|
+
if (tick === 0) return;
|
|
1623
|
+
setVisible(true);
|
|
1624
|
+
const timer = setTimeout(() => setVisible(false), 2500);
|
|
1625
|
+
return () => clearTimeout(timer);
|
|
1626
|
+
}, [tick]);
|
|
1627
|
+
return visible;
|
|
1628
|
+
}
|
|
1435
1629
|
/** The settings page body: connection facts for the Command Code provider. */
|
|
1436
1630
|
function CommandCodeSettingsPage(props) {
|
|
1437
1631
|
const { t } = props;
|
|
@@ -1439,6 +1633,7 @@ window.__ModuleLoader__.load({
|
|
|
1439
1633
|
const usage = props.useCommandCodeUsage((snapshot) => snapshot);
|
|
1440
1634
|
const disabled = !state.writable;
|
|
1441
1635
|
const keyLocked = !state.apiKeyWritable;
|
|
1636
|
+
const savedVisible = useSavedFlash(state.savedCount);
|
|
1442
1637
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1443
1638
|
className: "cc-section",
|
|
1444
1639
|
"aria-label": t("title"),
|
|
@@ -1474,6 +1669,7 @@ window.__ModuleLoader__.load({
|
|
|
1474
1669
|
onRemove: props.removeAccount,
|
|
1475
1670
|
onLabel: props.editAccountLabel,
|
|
1476
1671
|
onKey: props.editAccountKey,
|
|
1672
|
+
onToggleClear: (id) => props.toggleKeyClear(id),
|
|
1477
1673
|
onActive: (text) => props.edit("activeAccount", text),
|
|
1478
1674
|
onActiveReset: () => props.resetField("activeAccount")
|
|
1479
1675
|
}),
|
|
@@ -1488,7 +1684,14 @@ window.__ModuleLoader__.load({
|
|
|
1488
1684
|
configured: state.apiKeyConfigured,
|
|
1489
1685
|
configuredLabel: t("apiKeySet"),
|
|
1490
1686
|
unconfiguredLabel: t("apiKeyUnset"),
|
|
1491
|
-
|
|
1687
|
+
clearStaged: state.apiKeyClearStaged,
|
|
1688
|
+
showLabel: t("show"),
|
|
1689
|
+
hideLabel: t("hide"),
|
|
1690
|
+
clearLabel: t("usageKeyClear"),
|
|
1691
|
+
clearStagedLabel: t("usageKeyClearStaged"),
|
|
1692
|
+
undoClearLabel: t("usageUndoKeyClear"),
|
|
1693
|
+
onEdit: (text) => props.edit("apiKey", text),
|
|
1694
|
+
onToggleClear: () => props.toggleKeyClear("default")
|
|
1492
1695
|
}),
|
|
1493
1696
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1494
1697
|
id: "cc-api-base",
|
|
@@ -1554,6 +1757,11 @@ window.__ModuleLoader__.load({
|
|
|
1554
1757
|
role: "status",
|
|
1555
1758
|
children: t("saveFailed")
|
|
1556
1759
|
}) : null,
|
|
1760
|
+
savedVisible ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1761
|
+
className: "cc-saved",
|
|
1762
|
+
role: "status",
|
|
1763
|
+
children: t("saved")
|
|
1764
|
+
}) : null,
|
|
1557
1765
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1558
1766
|
variant: "ghost",
|
|
1559
1767
|
size: "sm",
|
|
@@ -1569,6 +1777,10 @@ window.__ModuleLoader__.load({
|
|
|
1569
1777
|
children: t(state.saving ? "saving" : "save")
|
|
1570
1778
|
})
|
|
1571
1779
|
]
|
|
1780
|
+
}),
|
|
1781
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1782
|
+
className: "cc-version",
|
|
1783
|
+
children: ["Command Code Provider v", PLUGIN_VERSION]
|
|
1572
1784
|
})
|
|
1573
1785
|
]
|
|
1574
1786
|
});
|
|
@@ -1608,13 +1820,18 @@ window.__ModuleLoader__.load({
|
|
|
1608
1820
|
overridden: "已覆盖",
|
|
1609
1821
|
reset: "重置",
|
|
1610
1822
|
invalidNumber: "无效数字",
|
|
1823
|
+
numberTooSmall: "不能小于 1(毫秒)",
|
|
1824
|
+
numberTooLarge: "超出允许上限(2147483647 毫秒)",
|
|
1611
1825
|
readOnly: "当前配置为只读。",
|
|
1612
1826
|
unsaved: "未保存",
|
|
1613
1827
|
save: "保存",
|
|
1614
1828
|
saving: "保存中",
|
|
1829
|
+
saved: "已保存 ✓",
|
|
1615
1830
|
saveFailed: "保存失败,请重试。",
|
|
1616
1831
|
discard: "放弃",
|
|
1617
1832
|
cancel: "取消",
|
|
1833
|
+
show: "显示",
|
|
1834
|
+
hide: "隐藏",
|
|
1618
1835
|
usageTitle: "账户用量",
|
|
1619
1836
|
usageRefresh: "刷新",
|
|
1620
1837
|
usageRefreshing: "刷新中…",
|
|
@@ -1636,6 +1853,15 @@ window.__ModuleLoader__.load({
|
|
|
1636
1853
|
usageExceeded: "已超限",
|
|
1637
1854
|
usageReset: "重置于",
|
|
1638
1855
|
usagePartial: "部分端点数据不可用",
|
|
1856
|
+
usageKeyClear: "清除已存密钥",
|
|
1857
|
+
usageKeyClearStaged: "将清除(保存后生效)",
|
|
1858
|
+
usageUndoKeyClear: "撤销清除",
|
|
1859
|
+
usageKeyInvalid: "API 密钥无效或已过期",
|
|
1860
|
+
usageKeyInvalidHint: "服务端拒绝了全部请求(401)。请检查该账户配置的密钥,或到 commandcode.ai 控制台重新生成。",
|
|
1861
|
+
usageServiceUnavailable: "Command Code 服务暂时不可用",
|
|
1862
|
+
usageServiceUnavailableHint: "服务端返回了错误(5xx),稍后点击刷新重试。",
|
|
1863
|
+
usageNetworkError: "无法连接 Command Code 服务",
|
|
1864
|
+
usageNetworkHint: "所有请求都没有到达服务端。请检查网络连接或 API 地址设置。",
|
|
1639
1865
|
usageUpdated: "更新于",
|
|
1640
1866
|
usagePeriodEnd: "账期截止",
|
|
1641
1867
|
usageActive: "当前使用",
|
|
@@ -1676,13 +1902,18 @@ window.__ModuleLoader__.load({
|
|
|
1676
1902
|
overridden: "Overridden",
|
|
1677
1903
|
reset: "Reset",
|
|
1678
1904
|
invalidNumber: "Invalid number",
|
|
1905
|
+
numberTooSmall: "Must be at least 1 (ms)",
|
|
1906
|
+
numberTooLarge: "Above the allowed maximum (2147483647 ms)",
|
|
1679
1907
|
readOnly: "Settings are read-only.",
|
|
1680
1908
|
unsaved: "Unsaved",
|
|
1681
1909
|
save: "Save",
|
|
1682
1910
|
saving: "Saving",
|
|
1911
|
+
saved: "Saved ✓",
|
|
1683
1912
|
saveFailed: "Save failed, please retry.",
|
|
1684
1913
|
discard: "Discard",
|
|
1685
1914
|
cancel: "Cancel",
|
|
1915
|
+
show: "Show",
|
|
1916
|
+
hide: "Hide",
|
|
1686
1917
|
usageTitle: "Account usage",
|
|
1687
1918
|
usageRefresh: "Refresh",
|
|
1688
1919
|
usageRefreshing: "Refreshing…",
|
|
@@ -1704,6 +1935,15 @@ window.__ModuleLoader__.load({
|
|
|
1704
1935
|
usageExceeded: "Exceeded",
|
|
1705
1936
|
usageReset: "Resets",
|
|
1706
1937
|
usagePartial: "Some endpoint data unavailable",
|
|
1938
|
+
usageKeyClear: "Clear stored key",
|
|
1939
|
+
usageKeyClearStaged: "Will be cleared on save",
|
|
1940
|
+
usageUndoKeyClear: "Undo clear",
|
|
1941
|
+
usageKeyInvalid: "API key invalid or expired",
|
|
1942
|
+
usageKeyInvalidHint: "The server rejects every request (401). Check the key configured for this account, or generate a new one in the commandcode.ai console.",
|
|
1943
|
+
usageServiceUnavailable: "The Command Code service is temporarily unavailable",
|
|
1944
|
+
usageServiceUnavailableHint: "The server returned errors (5xx); try Refresh again later.",
|
|
1945
|
+
usageNetworkError: "Could not reach the Command Code service",
|
|
1946
|
+
usageNetworkHint: "No request reached the server. Check your network connection or the API base setting.",
|
|
1707
1947
|
usageUpdated: "Updated",
|
|
1708
1948
|
usagePeriodEnd: "Period ends",
|
|
1709
1949
|
usageActive: "Active",
|
|
@@ -1783,6 +2023,12 @@ window.__ModuleLoader__.load({
|
|
|
1783
2023
|
.cc-usageMetaSpacer{flex:1}
|
|
1784
2024
|
.cc-usageUpdated{color:var(--dsw-alias-label-tertiary);margin:0;font-size:11px;line-height:1.5}
|
|
1785
2025
|
.cc-usagePartial{color:var(--dsw-alias-label-error);margin:0;font-size:11px;line-height:1.5}
|
|
2026
|
+
.cc-usageBlocked{border:1px solid var(--dsw-alias-label-error);border-radius:10px;padding:10px 12px;display:flex;flex-direction:column;gap:4px}
|
|
2027
|
+
.cc-usageBlockedTitle{color:var(--dsw-alias-label-error);margin:0;font-size:13px;font-weight:600;line-height:1.5}
|
|
2028
|
+
.cc-usageBlockedHint{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:1.5}
|
|
2029
|
+
.cc-version{margin:4px 0 0;text-align:center;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}
|
|
2030
|
+
.cc-saved{color:var(--dsw-alias-state-success-primary,var(--dsw-alias-label-secondary));margin:0;font-size:12px;font-weight:500;line-height:1.5}
|
|
2031
|
+
.cc-badgeWarn{background:var(--dsw-alias-state-warning-secondary,var(--dsw-alias-bg-module-platform));color:var(--dsw-alias-state-warning-primary,var(--dsw-alias-label-secondary))}
|
|
1786
2032
|
`;
|
|
1787
2033
|
/** Inject the page stylesheet once (idempotent per tag). */
|
|
1788
2034
|
function injectPageCss() {
|
|
@@ -1868,7 +2114,8 @@ window.__ModuleLoader__.load({
|
|
|
1868
2114
|
addAccount: () => controller.addAccount(),
|
|
1869
2115
|
removeAccount: (id) => controller.removeAccount(id),
|
|
1870
2116
|
editAccountLabel: (id, text) => controller.editAccountLabel(id, text),
|
|
1871
|
-
editAccountKey: (id, text) => controller.editAccountKey(id, text)
|
|
2117
|
+
editAccountKey: (id, text) => controller.editAccountKey(id, text),
|
|
2118
|
+
toggleKeyClear: (id) => controller.toggleKeyClear(id)
|
|
1872
2119
|
});
|
|
1873
2120
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
1874
2121
|
name: "settings.section",
|