@mars-sea/dsh-commandcode-provider 0.6.1 → 0.7.0

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/lib/client.js CHANGED
@@ -163,6 +163,8 @@ window.__ModuleLoader__.load({
163
163
  labelDrafts = /* @__PURE__ */ new Map();
164
164
  /** Staged key drafts, by credential reference (blank = keep stored key). */
165
165
  keyDrafts = /* @__PURE__ */ new Map();
166
+ /** Credential references staged for removal on the next save. */
167
+ keyClears = /* @__PURE__ */ new Set();
166
168
  saving = false;
167
169
  failed = false;
168
170
  savedCount = 0;
@@ -239,6 +241,7 @@ window.__ModuleLoader__.load({
239
241
  invalid: false,
240
242
  invalidReason: void 0
241
243
  },
244
+ apiKeyClearStaged: this.keyClears.has(this.credentialRef),
242
245
  apiBase: this.field("apiBase"),
243
246
  workingDir: this.field("workingDir"),
244
247
  defaultWorkingDir: this.defaultWorkingDir,
@@ -297,15 +300,37 @@ window.__ModuleLoader__.load({
297
300
  /** Stage one extra account's key draft (blank keeps the stored key). */
298
301
  editAccountKey(id, text) {
299
302
  this.keyDrafts.set(id, text);
303
+ this.keyClears.delete(id);
300
304
  this.failed = false;
301
305
  this.publish();
302
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
+ }
303
327
  /** Stage one field's draft text. */
304
328
  edit(field, text) {
305
329
  this.staged.set(field, {
306
330
  text,
307
331
  clear: false
308
332
  });
333
+ if (field === "apiKey") this.keyClears.delete(this.credentialRef);
309
334
  this.failed = false;
310
335
  this.publish();
311
336
  }
@@ -375,6 +400,7 @@ window.__ModuleLoader__.load({
375
400
  const storedLabel = this.storedExtras().find((extra) => extra.ref === ref)?.label;
376
401
  if (storedLabel === void 0 || storedLabel === text.trim()) this.labelDrafts.delete(ref);
377
402
  }
403
+ for (const ref of [...this.keyClears]) if (this.credentialStates.get(ref)?.configured !== true) this.keyClears.delete(ref);
378
404
  }
379
405
  spec(field) {
380
406
  const spec = this.specs.get(field);
@@ -545,12 +571,13 @@ window.__ModuleLoader__.load({
545
571
  keyText: this.keyDrafts.get(extra.ref) ?? "",
546
572
  configured: this.credentialStates.get(extra.ref)?.configured ?? false,
547
573
  writable: this.credentialStates.get(extra.ref)?.writable ?? true,
548
- added: extra.added
574
+ added: extra.added,
575
+ clearStaged: this.keyClears.has(extra.ref)
549
576
  }));
550
577
  }
551
- /** Whether any account-level staging (add/remove/label/key) exists. */
578
+ /** Whether any account-level staging (add/remove/label/key/clear) exists. */
552
579
  accountsStaged() {
553
- 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;
554
581
  }
555
582
  /** Whether the staged account edits differ from the stored section. */
556
583
  accountsDirty() {
@@ -560,6 +587,7 @@ window.__ModuleLoader__.load({
560
587
  if (base !== void 0 && text.trim() !== "" && text !== base) return true;
561
588
  }
562
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;
563
591
  return false;
564
592
  }
565
593
  /** Reset every account-level staged edit. */
@@ -568,14 +596,26 @@ window.__ModuleLoader__.load({
568
596
  this.removedRefs.clear();
569
597
  this.labelDrafts.clear();
570
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;
571
610
  }
572
611
  /** The account-level writes a save performs (empty when nothing staged). */
573
612
  accountPlan() {
574
613
  if (!this.accountsDirty()) return [];
575
614
  const runs = [];
615
+ for (const ref of this.keyClears) if (this.credentialStates.get(ref)?.configured === true) runs.push(() => this.unsetKey(ref));
576
616
  for (const [ref, text] of this.keyDrafts) {
577
617
  const value = text.trim();
578
- 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));
579
619
  }
580
620
  runs.push(() => this.writeAccounts());
581
621
  return runs;
@@ -761,6 +801,11 @@ window.__ModuleLoader__.load({
761
801
  const failures = source.failures;
762
802
  if (!Array.isArray(failures) || failures.some((entry) => typeof entry !== "string")) reject("failures");
763
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
+ }
764
809
  if (source.account !== void 0) {
765
810
  const account = record(source.account, "account");
766
811
  report.account = {
@@ -857,7 +902,7 @@ window.__ModuleLoader__.load({
857
902
  * @module dsh-commandcode-provider/client/version
858
903
  */
859
904
  /** The published package version (e.g. `'0.6.0'`). */
860
- const PLUGIN_VERSION = "0.6.1";
905
+ const PLUGIN_VERSION = "0.7.0";
861
906
  //#endregion
862
907
  //#region src/client/section.tsx
863
908
  /**
@@ -970,9 +1015,10 @@ window.__ModuleLoader__.load({
970
1015
  /**
971
1016
  * The API-key control: write-only, reports configured state, never echoes the
972
1017
  * key. The input is masked by default with a Show/Hide toggle so a pasted key
973
- * can be spot-checked without leaving the field.
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.
974
1020
  */
975
- function SecretKeyField({ label, hint, state, disabled, configured, configuredLabel, unconfiguredLabel, showLabel, hideLabel, onEdit }) {
1021
+ function SecretKeyField({ label, hint, state, disabled, configured, configuredLabel, unconfiguredLabel, clearStaged, showLabel, hideLabel, clearLabel, clearStagedLabel, undoClearLabel, onEdit, onToggleClear }) {
976
1022
  const [visible, setVisible] = (0, react.useState)(false);
977
1023
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
978
1024
  className: "cc-field",
@@ -985,16 +1031,30 @@ window.__ModuleLoader__.load({
985
1031
  children: label
986
1032
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
987
1033
  className: "cc-badges",
988
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
989
- className: configured ? "cc-badge" : "cc-badgeMuted",
990
- children: configured ? configuredLabel : unconfiguredLabel
991
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
992
- type: "button",
993
- className: "cc-reset",
994
- disabled,
995
- onClick: () => setVisible((value) => !value),
996
- children: visible ? hideLabel : showLabel
997
- })]
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
+ ]
998
1058
  })]
999
1059
  }),
1000
1060
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
@@ -1155,6 +1215,17 @@ window.__ModuleLoader__.load({
1155
1215
  className: "cc-usageHint",
1156
1216
  children: t("usageUnconfigured")
1157
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,
1158
1229
  report.usage !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1159
1230
  className: "cc-usageStats",
1160
1231
  children: [
@@ -1220,13 +1291,13 @@ window.__ModuleLoader__.load({
1220
1291
  ]
1221
1292
  }),
1222
1293
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-usageMetaSpacer" }),
1223
- 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", {
1224
1295
  className: "cc-usagePartial",
1225
1296
  title: report.failures.join("; "),
1226
1297
  children: t("usagePartial")
1227
1298
  }) : null
1228
1299
  ]
1229
- }) : 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", {
1230
1301
  className: "cc-usageMeta",
1231
1302
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-usageMetaSpacer" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1232
1303
  className: "cc-usagePartial",
@@ -1237,6 +1308,18 @@ window.__ModuleLoader__.load({
1237
1308
  ]
1238
1309
  });
1239
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
+ }
1240
1323
  /** The status dot on an account tab: cooling/invalid warn, everything else ok. */
1241
1324
  function AccountTabDot({ entry }) {
1242
1325
  const cls = entry.mark === "invalid-credential" ? "cc-tabDot cc-tabDotError" : entry.mark !== "" || entry.cooldownUntil > 0 ? "cc-tabDot cc-tabDotWarn" : "cc-tabDot cc-tabDotOk";
@@ -1353,7 +1436,7 @@ window.__ModuleLoader__.load({
1353
1436
  * otherwise the only way to undo a mistaken Add would be discarding every
1354
1437
  * other staged edit.
1355
1438
  */
1356
- function AccountRow({ account, disabled, t, onLabel, onKey, onRemove }) {
1439
+ function AccountRow({ account, disabled, t, onLabel, onKey, onToggleClear, onRemove }) {
1357
1440
  const locked = !account.writable;
1358
1441
  const [keyVisible, setKeyVisible] = (0, react.useState)(false);
1359
1442
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -1376,6 +1459,24 @@ window.__ModuleLoader__.load({
1376
1459
  className: account.configured ? "cc-badge" : "cc-badgeMuted",
1377
1460
  children: account.configured ? t("apiKeySet") : t("apiKeyUnset")
1378
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,
1379
1480
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1380
1481
  type: "button",
1381
1482
  className: "cc-reset",
@@ -1420,7 +1521,7 @@ window.__ModuleLoader__.load({
1420
1521
  });
1421
1522
  }
1422
1523
  /** The multi-account card: the active-account selector + extra accounts in rotation order + add button. */
1423
- function AccountsCard({ t, state, disabled, onAdd, onRemove, onLabel, onKey, onActive, onActiveReset }) {
1524
+ function AccountsCard({ t, state, disabled, onAdd, onRemove, onLabel, onKey, onToggleClear, onActive, onActiveReset }) {
1424
1525
  const active = state.activeAccount;
1425
1526
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1426
1527
  className: "cc-card",
@@ -1504,6 +1605,7 @@ window.__ModuleLoader__.load({
1504
1605
  t,
1505
1606
  onLabel: (text) => onLabel(account.id, text),
1506
1607
  onKey: (text) => onKey(account.id, text),
1608
+ onToggleClear: () => onToggleClear(account.id),
1507
1609
  onRemove: () => onRemove(account.id)
1508
1610
  }, account.id))
1509
1611
  ]
@@ -1567,6 +1669,7 @@ window.__ModuleLoader__.load({
1567
1669
  onRemove: props.removeAccount,
1568
1670
  onLabel: props.editAccountLabel,
1569
1671
  onKey: props.editAccountKey,
1672
+ onToggleClear: (id) => props.toggleKeyClear(id),
1570
1673
  onActive: (text) => props.edit("activeAccount", text),
1571
1674
  onActiveReset: () => props.resetField("activeAccount")
1572
1675
  }),
@@ -1581,9 +1684,14 @@ window.__ModuleLoader__.load({
1581
1684
  configured: state.apiKeyConfigured,
1582
1685
  configuredLabel: t("apiKeySet"),
1583
1686
  unconfiguredLabel: t("apiKeyUnset"),
1687
+ clearStaged: state.apiKeyClearStaged,
1584
1688
  showLabel: t("show"),
1585
1689
  hideLabel: t("hide"),
1586
- onEdit: (text) => props.edit("apiKey", text)
1690
+ clearLabel: t("usageKeyClear"),
1691
+ clearStagedLabel: t("usageKeyClearStaged"),
1692
+ undoClearLabel: t("usageUndoKeyClear"),
1693
+ onEdit: (text) => props.edit("apiKey", text),
1694
+ onToggleClear: () => props.toggleKeyClear("default")
1587
1695
  }),
1588
1696
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1589
1697
  id: "cc-api-base",
@@ -1745,6 +1853,15 @@ window.__ModuleLoader__.load({
1745
1853
  usageExceeded: "已超限",
1746
1854
  usageReset: "重置于",
1747
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 地址设置。",
1748
1865
  usageUpdated: "更新于",
1749
1866
  usagePeriodEnd: "账期截止",
1750
1867
  usageActive: "当前使用",
@@ -1818,6 +1935,15 @@ window.__ModuleLoader__.load({
1818
1935
  usageExceeded: "Exceeded",
1819
1936
  usageReset: "Resets",
1820
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.",
1821
1947
  usageUpdated: "Updated",
1822
1948
  usagePeriodEnd: "Period ends",
1823
1949
  usageActive: "Active",
@@ -1897,8 +2023,12 @@ window.__ModuleLoader__.load({
1897
2023
  .cc-usageMetaSpacer{flex:1}
1898
2024
  .cc-usageUpdated{color:var(--dsw-alias-label-tertiary);margin:0;font-size:11px;line-height:1.5}
1899
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}
1900
2029
  .cc-version{margin:4px 0 0;text-align:center;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}
1901
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))}
1902
2032
  `;
1903
2033
  /** Inject the page stylesheet once (idempotent per tag). */
1904
2034
  function injectPageCss() {
@@ -1984,7 +2114,8 @@ window.__ModuleLoader__.load({
1984
2114
  addAccount: () => controller.addAccount(),
1985
2115
  removeAccount: (id) => controller.removeAccount(id),
1986
2116
  editAccountLabel: (id, text) => controller.editAccountLabel(id, text),
1987
- editAccountKey: (id, text) => controller.editAccountKey(id, text)
2117
+ editAccountKey: (id, text) => controller.editAccountKey(id, text),
2118
+ toggleKeyClear: (id) => controller.toggleKeyClear(id)
1988
2119
  });
1989
2120
  ctx.slots.inject("settings.section", () => ctx.slots.register({
1990
2121
  name: "settings.section",