@mars-sea/dsh-commandcode-provider 0.11.9 → 0.11.11

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
@@ -586,6 +586,16 @@ window.__ModuleLoader__.load({
586
586
  booleanField$1("filterModelsByPlan"),
587
587
  booleanField$1("webSearch"),
588
588
  booleanField$1("showSidebarQuota"),
589
+ booleanField$1("commandGuard"),
590
+ numberField$1("commandGuardThreshold", {
591
+ min: .5,
592
+ max: 1
593
+ }),
594
+ numberField$1("commandGuardTimeoutMs", {
595
+ min: 200,
596
+ max: 1e4
597
+ }),
598
+ booleanField$1("zdr"),
589
599
  textField("activeAccount")
590
600
  ];
591
601
  /** Whether two model-id lists are equal as sets (order-insensitive). */
@@ -740,6 +750,10 @@ window.__ModuleLoader__.load({
740
750
  filterModelsByPlan: this.field("filterModelsByPlan"),
741
751
  webSearch: this.field("webSearch"),
742
752
  showSidebarQuota: this.field("showSidebarQuota"),
753
+ commandGuard: this.field("commandGuard"),
754
+ commandGuardThreshold: this.field("commandGuardThreshold"),
755
+ commandGuardTimeoutMs: this.field("commandGuardTimeoutMs"),
756
+ zdr: this.field("zdr"),
743
757
  sidebarQuota: this.sectionValue("showSidebarQuota") === true,
744
758
  activeAccount: this.field("activeAccount"),
745
759
  accounts,
@@ -1789,6 +1803,21 @@ window.__ModuleLoader__.load({
1789
1803
  };
1790
1804
  //#endregion
1791
1805
  //#region src/client/login.ts
1806
+ /** Keep one account's login status out of every other account's row. */
1807
+ function loginStateForTarget(state, targetRef) {
1808
+ if (state.targetRef === targetRef) return {
1809
+ visible: state,
1810
+ busyElsewhere: false
1811
+ };
1812
+ return {
1813
+ visible: {
1814
+ ...state,
1815
+ phase: "idle",
1816
+ authUrl: void 0
1817
+ },
1818
+ busyElsewhere: state.phase === "starting" || state.phase === "waiting"
1819
+ };
1820
+ }
1792
1821
  /** How often a live attempt is polled. */
1793
1822
  const POLL_INTERVAL_MS = 1e3;
1794
1823
  /**
@@ -1803,6 +1832,7 @@ window.__ModuleLoader__.load({
1803
1832
  generation = 0;
1804
1833
  disposed = false;
1805
1834
  phase = "idle";
1835
+ targetRef;
1806
1836
  authUrl;
1807
1837
  userName;
1808
1838
  keyName;
@@ -1821,6 +1851,7 @@ window.__ModuleLoader__.load({
1821
1851
  state() {
1822
1852
  return {
1823
1853
  phase: this.phase,
1854
+ ...this.targetRef === void 0 ? {} : { targetRef: this.targetRef },
1824
1855
  authUrl: this.authUrl,
1825
1856
  userName: this.userName,
1826
1857
  keyName: this.keyName,
@@ -1829,9 +1860,10 @@ window.__ModuleLoader__.load({
1829
1860
  };
1830
1861
  }
1831
1862
  /** Start (or rejoin) a login attempt and begin polling its status. */
1832
- async begin() {
1863
+ async begin(targetRef) {
1833
1864
  if (this.disposed || this.phase === "starting" || this.phase === "waiting") return;
1834
1865
  const generation = ++this.generation;
1866
+ this.targetRef = targetRef;
1835
1867
  this.set({
1836
1868
  phase: "starting",
1837
1869
  authUrl: void 0,
@@ -1854,7 +1886,7 @@ window.__ModuleLoader__.load({
1854
1886
  }
1855
1887
  let result;
1856
1888
  try {
1857
- result = await remote.loginBegin();
1889
+ result = await remote.loginBegin(targetRef);
1858
1890
  } catch (error) {
1859
1891
  result = {
1860
1892
  ok: false,
@@ -2092,6 +2124,15 @@ window.__ModuleLoader__.load({
2092
2124
  booleanField
2093
2125
  };
2094
2126
  }
2127
+ /** Use the same strict-codec shape for invocation parameters and results. */
2128
+ function makeStrictCodec(typeSymbol, schema) {
2129
+ return {
2130
+ mode: "strict",
2131
+ typeSymbol,
2132
+ schema,
2133
+ create: () => schema
2134
+ };
2135
+ }
2095
2136
  /**
2096
2137
  * Build one strict invocation descriptor. Every Command Code Remote shares the
2097
2138
  * `commandcode` namespace, the `commandcodeUsage` service, and a strict
@@ -2100,12 +2141,7 @@ window.__ModuleLoader__.load({
2100
2141
  * only its own facts.
2101
2142
  */
2102
2143
  function makeRemoteDescriptor(endpoint, method, typeSymbol, schema) {
2103
- const result = {
2104
- mode: "strict",
2105
- typeSymbol,
2106
- schema,
2107
- create: () => schema
2108
- };
2144
+ const result = makeStrictCodec(typeSymbol, schema);
2109
2145
  return {
2110
2146
  id: `${REMOTE_PACKAGE}#${endpoint}`,
2111
2147
  service: REMOTE_SERVICE,
@@ -2158,7 +2194,7 @@ window.__ModuleLoader__.load({
2158
2194
  const report = { failures };
2159
2195
  if (source.blocked !== void 0) {
2160
2196
  const blocked = source.blocked;
2161
- if (blocked === "invalid-key" || blocked === "service-unavailable" || blocked === "network") report.blocked = blocked;
2197
+ if (blocked === "invalid-key" || blocked === "service-unavailable" || blocked === "invalid-response" || blocked === "network") report.blocked = blocked;
2162
2198
  else reject$1("blocked");
2163
2199
  }
2164
2200
  if (source.account !== void 0) {
@@ -2374,9 +2410,24 @@ window.__ModuleLoader__.load({
2374
2410
  }
2375
2411
  /** The strict result codec shared by all three login endpoints. */
2376
2412
  const loginStatusSchema = { parse: parseLoginStatus };
2377
- /** Build one login invocation descriptor (uniform result, no parameters). */
2413
+ /** Optional account target; the Host still checks it against saved slots. */
2414
+ const loginTargetSchema = { parse(value) {
2415
+ if (value === void 0 || typeof value === "string") return value;
2416
+ return reject("targetRef");
2417
+ } };
2418
+ /** Build one login invocation descriptor. Only begin accepts an account ref. */
2378
2419
  function loginDescriptor(endpoint, method) {
2379
- return makeRemoteDescriptor(endpoint, method, `${REMOTE_PACKAGE}#CommandCodeLoginStatus`, loginStatusSchema);
2420
+ const descriptor = makeRemoteDescriptor(endpoint, method, `${REMOTE_PACKAGE}#CommandCodeLoginStatus`, loginStatusSchema);
2421
+ return method === "loginBegin" ? {
2422
+ ...descriptor,
2423
+ parameters: [{
2424
+ name: "targetRef",
2425
+ wire: "targetRef",
2426
+ source: "json",
2427
+ codec: makeStrictCodec(`${REMOTE_PACKAGE}#CommandCodeLoginTargetRef`, loginTargetSchema),
2428
+ acceptsUndefined: true
2429
+ }]
2430
+ } : descriptor;
2380
2431
  }
2381
2432
  /** The Client-face contribution fragment mounted on `ctx.remote`. */
2382
2433
  const LOGIN_REMOTE_CONTRIBUTION = {
@@ -2390,9 +2441,10 @@ window.__ModuleLoader__.load({
2390
2441
  //#endregion
2391
2442
  //#region src/client/login-row.tsx
2392
2443
  /** The sign-in alternative to pasting a key (settings page + Models card). */
2393
- function LoginRow({ state, disabled, t, onBegin, onCancel }) {
2394
- const busy = state.phase === "starting" || state.phase === "waiting";
2395
- const hint = loginHint(state, t);
2444
+ function LoginRow({ state, targetRef, disabled, disabledHint, t, onBegin, onCancel }) {
2445
+ const { visible: visibleState, busyElsewhere } = loginStateForTarget(state, targetRef);
2446
+ const busy = visibleState.phase === "starting" || visibleState.phase === "waiting";
2447
+ const hint = loginHint(visibleState, t);
2396
2448
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2397
2449
  className: "cc-field",
2398
2450
  children: [
@@ -2411,17 +2463,17 @@ window.__ModuleLoader__.load({
2411
2463
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2412
2464
  type: "button",
2413
2465
  className: "cc-reset",
2414
- disabled,
2466
+ disabled: disabled || busyElsewhere,
2415
2467
  onClick: onBegin,
2416
2468
  children: t("loginButton")
2417
2469
  })
2418
2470
  })]
2419
2471
  }),
2420
- state.authUrl !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2472
+ visibleState.authUrl !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2421
2473
  className: "cc-hint",
2422
2474
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
2423
2475
  className: "cc-loginLink",
2424
- href: state.authUrl,
2476
+ href: visibleState.authUrl,
2425
2477
  target: "_blank",
2426
2478
  rel: "noreferrer",
2427
2479
  children: t("loginOpenLink")
@@ -2430,7 +2482,7 @@ window.__ModuleLoader__.load({
2430
2482
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2431
2483
  className: hint.className,
2432
2484
  title: hint.title,
2433
- children: hint.text
2485
+ children: disabled && !busy && disabledHint !== void 0 ? disabledHint : hint.text
2434
2486
  })
2435
2487
  ]
2436
2488
  });
@@ -2579,7 +2631,7 @@ window.__ModuleLoader__.load({
2579
2631
  }
2580
2632
  //#endregion
2581
2633
  //#region package.json
2582
- var version = "0.11.9";
2634
+ var version = "0.11.11";
2583
2635
  var repository = {
2584
2636
  "type": "git",
2585
2637
  "url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
@@ -2781,7 +2833,11 @@ window.__ModuleLoader__.load({
2781
2833
  "transportMaxRetries",
2782
2834
  "filterModelsByPlan",
2783
2835
  "webSearch",
2784
- "showSidebarQuota"
2836
+ "showSidebarQuota",
2837
+ "commandGuard",
2838
+ "commandGuardThreshold",
2839
+ "commandGuardTimeoutMs",
2840
+ "zdr"
2785
2841
  ];
2786
2842
  /** One labelled field row in the page body. */
2787
2843
  function Field({ id, label, hint, state, disabled, numeric, placeholder, onEdit, onReset, t }) {
@@ -2967,6 +3023,49 @@ window.__ModuleLoader__.load({
2967
3023
  onEdit: (text) => onEdit("showSidebarQuota", text),
2968
3024
  onReset: () => onReset("showSidebarQuota"),
2969
3025
  t
3026
+ }),
3027
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
3028
+ id: "cc-command-guard",
3029
+ label: t("commandGuard"),
3030
+ hint: t("commandGuardHint"),
3031
+ state: state.commandGuard,
3032
+ disabled,
3033
+ defaultChecked: false,
3034
+ onEdit: (text) => onEdit("commandGuard", text),
3035
+ onReset: () => onReset("commandGuard"),
3036
+ t
3037
+ }),
3038
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
3039
+ id: "cc-command-guard-threshold",
3040
+ label: t("commandGuardThreshold"),
3041
+ hint: t("commandGuardThresholdHint"),
3042
+ state: state.commandGuardThreshold,
3043
+ disabled,
3044
+ onEdit: (text) => onEdit("commandGuardThreshold", text),
3045
+ onReset: () => onReset("commandGuardThreshold"),
3046
+ t
3047
+ }),
3048
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
3049
+ id: "cc-command-guard-timeout",
3050
+ label: t("commandGuardTimeoutMs"),
3051
+ hint: t("commandGuardTimeoutMsHint"),
3052
+ state: state.commandGuardTimeoutMs,
3053
+ disabled,
3054
+ numeric: true,
3055
+ onEdit: (text) => onEdit("commandGuardTimeoutMs", text),
3056
+ onReset: () => onReset("commandGuardTimeoutMs"),
3057
+ t
3058
+ }),
3059
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
3060
+ id: "cc-zdr",
3061
+ label: t("zdr"),
3062
+ hint: t("zdrHint"),
3063
+ state: state.zdr,
3064
+ disabled,
3065
+ defaultChecked: false,
3066
+ onEdit: (text) => onEdit("zdr", text),
3067
+ onReset: () => onReset("zdr"),
3068
+ t
2970
3069
  })
2971
3070
  ]
2972
3071
  }) : null,
@@ -3337,12 +3436,14 @@ window.__ModuleLoader__.load({
3337
3436
  function blockedTitle(reason, t) {
3338
3437
  if (reason === "invalid-key") return t("usageKeyInvalid");
3339
3438
  if (reason === "service-unavailable") return t("usageServiceUnavailable");
3439
+ if (reason === "invalid-response") return t("usageInvalidResponse");
3340
3440
  return t("usageNetworkError");
3341
3441
  }
3342
3442
  /** The actionable hint under a blocked report's headline. */
3343
3443
  function blockedHint(reason, t) {
3344
3444
  if (reason === "invalid-key") return t("usageKeyInvalidHint");
3345
3445
  if (reason === "service-unavailable") return t("usageServiceUnavailableHint");
3446
+ if (reason === "invalid-response") return t("usageInvalidResponseHint");
3346
3447
  return t("usageNetworkHint");
3347
3448
  }
3348
3449
  /** The status dot on an account tab: cooling/invalid warn, everything else ok. */
@@ -3533,7 +3634,7 @@ window.__ModuleLoader__.load({
3533
3634
  });
3534
3635
  }
3535
3636
  /** The multi-account card: the active-account selector + extra accounts in rotation order + add button. */
3536
- function AccountsCard({ t, state, disabled, onAdd, onRemove, onLabel, onKey, onToggleClear, onActive, onActiveReset }) {
3637
+ function AccountsCard({ t, state, login, disabled, onAdd, onRemove, onLabel, onKey, onToggleClear, onActive, onActiveReset, onBeginLogin, onCancelLogin }) {
3537
3638
  const active = state.activeAccount;
3538
3639
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3539
3640
  className: "cc-card",
@@ -3611,14 +3712,31 @@ window.__ModuleLoader__.load({
3611
3712
  })
3612
3713
  ]
3613
3714
  }),
3614
- state.accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountRow, {
3615
- account,
3616
- disabled,
3617
- t,
3618
- onLabel: (text) => onLabel(account.id, text),
3619
- onKey: (text) => onKey(account.id, text),
3620
- onToggleClear: () => onToggleClear(account.id),
3621
- onRemove: () => onRemove(account.id)
3715
+ state.accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3716
+ className: "cc-accountEditor",
3717
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountRow, {
3718
+ account,
3719
+ disabled: disabled || login.targetRef === account.ref && (login.phase === "starting" || login.phase === "waiting"),
3720
+ t,
3721
+ onLabel: (text) => onLabel(account.id, text),
3722
+ onKey: (text) => onKey(account.id, text),
3723
+ onToggleClear: () => onToggleClear(account.id),
3724
+ onRemove: () => onRemove(account.id)
3725
+ }), account.added ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3726
+ className: "cc-accountLoginHint",
3727
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3728
+ className: "cc-hint",
3729
+ children: t("accountLoginAfterSave")
3730
+ })
3731
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginRow, {
3732
+ state: login,
3733
+ targetRef: account.ref,
3734
+ disabled: disabled || !account.writable || state.dirty,
3735
+ disabledHint: state.dirty ? t("loginSaveBefore") : void 0,
3736
+ t,
3737
+ onBegin: () => onBeginLogin(account.ref),
3738
+ onCancel: onCancelLogin
3739
+ })]
3622
3740
  }, account.id))
3623
3741
  ]
3624
3742
  });
@@ -3930,6 +4048,8 @@ window.__ModuleLoader__.load({
3930
4048
  /** The settings page body: connection facts for the Command Code provider. */
3931
4049
  function CommandCodeSettingsPage(props) {
3932
4050
  const { t } = props;
4051
+ const footerMarker = (0, react.useRef)(null);
4052
+ const [footerFloating, setFooterFloating] = (0, react.useState)(false);
3933
4053
  const state = props.useCommandCodeSettings((snapshot) => snapshot);
3934
4054
  const usage = props.useCommandCodeUsage((snapshot) => snapshot);
3935
4055
  const login = props.useCommandCodeLogin((snapshot) => snapshot);
@@ -3937,6 +4057,15 @@ window.__ModuleLoader__.load({
3937
4057
  const keyLocked = !state.apiKeyWritable;
3938
4058
  const savedVisible = useSavedFlash(state.savedCount);
3939
4059
  const updateVersion = usePluginUpdate();
4060
+ (0, react.useEffect)(() => {
4061
+ const marker = footerMarker.current;
4062
+ if (!marker || typeof IntersectionObserver === "undefined") return;
4063
+ const observer = new IntersectionObserver(([entry]) => {
4064
+ if (entry) setFooterFloating(!entry.isIntersecting);
4065
+ });
4066
+ observer.observe(marker);
4067
+ return () => observer.disconnect();
4068
+ }, []);
3940
4069
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3941
4070
  className: "cc-section",
3942
4071
  "aria-label": t("title"),
@@ -3966,6 +4095,7 @@ window.__ModuleLoader__.load({
3966
4095
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountsCard, {
3967
4096
  t,
3968
4097
  state,
4098
+ login,
3969
4099
  disabled,
3970
4100
  onAdd: props.addAccount,
3971
4101
  onRemove: props.removeAccount,
@@ -3973,7 +4103,9 @@ window.__ModuleLoader__.load({
3973
4103
  onKey: props.editAccountKey,
3974
4104
  onToggleClear: (id) => props.toggleKeyClear(id),
3975
4105
  onActive: (text) => props.edit("activeAccount", text),
3976
- onActiveReset: () => props.resetField("activeAccount")
4106
+ onActiveReset: () => props.resetField("activeAccount"),
4107
+ onBeginLogin: props.beginLogin,
4108
+ onCancelLogin: props.cancelLogin
3977
4109
  }),
3978
4110
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RulesCard, {
3979
4111
  t,
@@ -3997,7 +4129,7 @@ window.__ModuleLoader__.load({
3997
4129
  label: t("apiKey"),
3998
4130
  hint: keyLocked ? t("apiKeyLocked") : t("apiKeyHint"),
3999
4131
  state: state.apiKey,
4000
- disabled: disabled || keyLocked,
4132
+ disabled: disabled || keyLocked || login.targetRef === void 0 && (login.phase === "starting" || login.phase === "waiting"),
4001
4133
  configured: state.apiKeyConfigured,
4002
4134
  configuredLabel: t("apiKeySet"),
4003
4135
  unconfiguredLabel: t("apiKeyUnset"),
@@ -4011,7 +4143,8 @@ window.__ModuleLoader__.load({
4011
4143
  onToggleClear: () => props.toggleKeyClear("default")
4012
4144
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginRow, {
4013
4145
  state: login,
4014
- disabled: disabled || keyLocked,
4146
+ disabled: disabled || keyLocked || state.dirty,
4147
+ disabledHint: state.dirty ? t("loginSaveBefore") : void 0,
4015
4148
  t,
4016
4149
  onBegin: props.beginLogin,
4017
4150
  onCancel: props.cancelLogin
@@ -4024,34 +4157,42 @@ window.__ModuleLoader__.load({
4024
4157
  onEdit: props.edit,
4025
4158
  onReset: props.resetField
4026
4159
  }),
4027
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4028
- className: "cc-footer",
4029
- children: [
4030
- state.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4031
- className: "cc-failed",
4032
- role: "status",
4033
- children: t("saveFailed")
4034
- }) : null,
4035
- savedVisible ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4036
- className: "cc-saved",
4037
- role: "status",
4038
- children: t("saved")
4039
- }) : null,
4040
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4041
- variant: "ghost",
4042
- size: "sm",
4043
- disabled: !state.dirty || state.saving,
4044
- onClick: props.discard,
4045
- children: t("discard")
4046
- }),
4047
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4048
- variant: "primary",
4049
- size: "sm",
4050
- disabled: !state.dirty || state.invalid || state.saving,
4051
- onClick: props.save,
4052
- children: t(state.saving ? "saving" : "save")
4053
- })
4054
- ]
4160
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4161
+ ref: footerMarker,
4162
+ className: "cc-footerMarker",
4163
+ "aria-hidden": "true"
4164
+ }),
4165
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4166
+ className: `cc-footer cc-footerSticky${footerFloating ? " cc-footerFloating" : ""}`,
4167
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4168
+ className: "cc-footerActions",
4169
+ children: [
4170
+ state.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4171
+ className: "cc-failed",
4172
+ role: "status",
4173
+ children: t("saveFailed")
4174
+ }) : null,
4175
+ savedVisible ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4176
+ className: "cc-saved",
4177
+ role: "status",
4178
+ children: t("saved")
4179
+ }) : null,
4180
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4181
+ variant: "ghost",
4182
+ size: "sm",
4183
+ disabled: !state.dirty || state.saving,
4184
+ onClick: props.discard,
4185
+ children: t("discard")
4186
+ }),
4187
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4188
+ variant: "primary",
4189
+ size: "sm",
4190
+ disabled: !state.dirty || state.invalid || state.saving,
4191
+ onClick: props.save,
4192
+ children: t(state.saving ? "saving" : "save")
4193
+ })
4194
+ ]
4195
+ })
4055
4196
  }),
4056
4197
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
4057
4198
  className: "cc-version",
@@ -4338,6 +4479,8 @@ window.__ModuleLoader__.load({
4338
4479
  errorServiceUnavailableHint: "服务端返回错误(5xx),请稍后点击刷新重试。",
4339
4480
  errorNetwork: "无法连接 Command Code 服务",
4340
4481
  errorNetworkHint: "所有请求都没有到达服务端。请检查网络连接或 API 地址设置。",
4482
+ errorInvalidResponse: "Command Code 响应无法读取",
4483
+ errorInvalidResponseHint: "请求已收到响应,但内容无法解析。请检查宿主的 HTTP 代理或响应解压配置。",
4341
4484
  errorGeneric: "用量获取失败"
4342
4485
  };
4343
4486
  /** The panel's English string table (must mirror {@link PANEL_COPY_ZH}). */
@@ -4390,6 +4533,8 @@ window.__ModuleLoader__.load({
4390
4533
  errorServiceUnavailableHint: "The server returned errors (5xx). Try Refresh again in a moment.",
4391
4534
  errorNetwork: "Could not reach the Command Code service",
4392
4535
  errorNetworkHint: "No request reached the server. Check your network connection or the API base setting.",
4536
+ errorInvalidResponse: "Could not read the Command Code response",
4537
+ errorInvalidResponseHint: "The requests received responses, but their bodies could not be parsed. Check the host HTTP proxy or response decoding.",
4393
4538
  errorGeneric: "Could not fetch account usage"
4394
4539
  };
4395
4540
  /** Every panel key, in the English table's declaration order. */
@@ -4570,6 +4715,11 @@ window.__ModuleLoader__.load({
4570
4715
  hint: "errorNetworkHint",
4571
4716
  detail
4572
4717
  };
4718
+ if (blocked === "invalid-response") return {
4719
+ title: "errorInvalidResponse",
4720
+ hint: "errorInvalidResponseHint",
4721
+ detail
4722
+ };
4573
4723
  if (state.status === "error" && state.report === void 0) return {
4574
4724
  title: "errorGeneric",
4575
4725
  hint: "errorGeneric",
@@ -6174,6 +6324,13 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
6174
6324
  .cc-advancedBody>.cc-field:first-of-type{border-top:1px solid var(--dsw-alias-border-l2)}
6175
6325
  .cc-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}
6176
6326
  .cc-footer{justify-content:flex-end;align-items:center;gap:8px;display:flex}
6327
+ /* Keep the original footer in flow and inside the settings scrollport. */
6328
+ .cc-footerMarker{height:1px;margin:-6px 0 -7px;pointer-events:none}
6329
+ .cc-footerSticky{position:sticky;bottom:0;z-index:2;flex-shrink:0;min-height:48px;pointer-events:none}
6330
+ .cc-footerActions{align-items:center;gap:8px;display:flex;pointer-events:auto}
6331
+ .cc-footerFloating .cc-footerActions{padding:6px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2));box-shadow:0 8px 24px rgba(0,0,0,.12)}
6332
+ .cc-accountEditor{border-top:1px solid var(--dsw-alias-border-l2)}
6333
+ .cc-accountLoginHint{padding:0 0 12px}
6177
6334
  .cc-toggleRow{align-items:center;gap:8px;cursor:pointer;display:flex}
6178
6335
  .cc-toggleRow:has(.cc-toggle:disabled){cursor:default}
6179
6336
  .cc-toggle{appearance:none;flex-shrink:0;background:var(--dsw-alias-border-l2);border-radius:999px;width:30px;height:18px;margin:0;cursor:pointer;position:relative;transition:background .15s ease}
@@ -6450,6 +6607,14 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
6450
6607
  webSearchHint: "开启后,dsh 的 web_search 工具由 Command Code 承担(复用同一个 API key 与地址),并优先于其他搜索后端;关闭则把选择权交还给之前的后端(如 modsearch),而不是强制回退到 DeepSeek 搜索。",
6451
6608
  showSidebarQuota: "在侧边栏显示额度卡片",
6452
6609
  showSidebarQuotaHint: "开启后,侧边栏底部(Settings 上方)显示 Command Code 套餐与配额卡片,点击可在中间栏打开完整仪表盘。默认关闭:左侧不显示任何额度信息,也不会为其后台刷新用量。保存后立即生效。",
6610
+ commandGuard: "AI 命令安全预判",
6611
+ commandGuardHint: "命令确认弹窗前,先由 typesafe/jev 判断它是否安全:安全就自动放行,其余照常弹窗。默认关闭。",
6612
+ commandGuardThreshold: "自动放行阈值(0.5–1)",
6613
+ commandGuardThresholdHint: "typesafe/jev 判定\"安全\"的概率达到该值即放行,默认 0.9。",
6614
+ commandGuardTimeoutMs: "判定超时(毫秒,200–10000)",
6615
+ commandGuardTimeoutMsHint: "typesafe/jev 超过该时间未返回就照常弹窗,默认 1500。",
6616
+ zdr: "零数据保留(ZDR)",
6617
+ zdrHint: "开启后,请求只经由不留存提示词与回复、也不用于训练的上游(等价于 CLI 的 CMD_ZDR=1)。没有可用 ZDR 上游的模型会返回错误,请换用其他模型或关闭此开关。ZDR 容量按上游实价透传,通常更贵;费用读数仍按常规目录价估算。默认关闭。",
6453
6618
  accountsTitle: "多账户轮换",
6454
6619
  accountsHint: "当前账户达到用量限额(429)或密钥失效(401)时,请求自动切换到下一个账户;全部耗尽时会提示最早的重置时间。",
6455
6620
  accountAdd: "添加账户",
@@ -6457,6 +6622,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
6457
6622
  accountLabel: "账户备注名",
6458
6623
  accountKey: "API 密钥",
6459
6624
  accountKeyHint: "该账户的 API 密钥。留空保存不会覆盖已存储的密钥。",
6625
+ accountLoginAfterSave: "先点击页面底部的「保存」,再点击该账户的「登录 Command Code」通过网页获取密钥。",
6460
6626
  accountDefault: "默认账户",
6461
6627
  activeAccount: "当前使用账户",
6462
6628
  activeAccountAuto: "自动(第一个可用账户)",
@@ -6527,6 +6693,8 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
6527
6693
  usageServiceUnavailableHint: "服务端返回了错误(5xx),稍后点击刷新重试。",
6528
6694
  usageNetworkError: "无法连接 Command Code 服务",
6529
6695
  usageNetworkHint: "所有请求都没有到达服务端。请检查网络连接或 API 地址设置。",
6696
+ usageInvalidResponse: "Command Code 响应无法读取",
6697
+ usageInvalidResponseHint: "请求已收到响应,但内容无法解析。请检查宿主的 HTTP 代理或响应解压配置。",
6530
6698
  usageUpdated: "更新于",
6531
6699
  usagePeriodEnd: "账期截止",
6532
6700
  usageActive: "当前使用",
@@ -6537,6 +6705,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
6537
6705
  updateHint: "已发布新版本,点击查看发布说明;更新插件后刷新本页,提示会自动消失。",
6538
6706
  loginTitle: "通过官方登录获取密钥",
6539
6707
  loginHintIdle: "不想手动创建密钥?点击登录后浏览器会打开 commandcode.ai 授权页,完成后密钥自动写入本机凭据服务,下次请求即生效。",
6708
+ loginSaveBefore: "请先保存本页更改,再通过网页登录此账户。",
6540
6709
  loginButton: "登录 Command Code",
6541
6710
  loginStarting: "正在启动本地回调服务…",
6542
6711
  loginWaiting: "等待在浏览器中完成授权…",
@@ -6586,6 +6755,14 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
6586
6755
  webSearchHint: "When on, the model-facing web_search tool is backed by Command Code (same API key and base URL as chat), winning over other search backends. Off hands the selection back to the previous backend (e.g. modsearch) instead of forcing the shipped DeepSeek search.",
6587
6756
  showSidebarQuota: "Show the quota card in the sidebar",
6588
6757
  showSidebarQuotaHint: "When on, a Command Code plan & quota card sits at the bottom of the sidebar (above Settings) and opens the full dashboard in the centre column. Off by default: nothing is shown on the left, and no background usage poll runs for it. Applies on save.",
6758
+ commandGuard: "AI command guard",
6759
+ commandGuardHint: "Before a shell command asks for approval, typesafe/jev judges whether it is safe: safe commands run without a prompt, everything else asks as usual. Off by default.",
6760
+ commandGuardThreshold: "Auto-approve threshold (0.5–1)",
6761
+ commandGuardThresholdHint: "Approve when typesafe/jev rates the command \"safe\" at least this likely; default 0.9.",
6762
+ commandGuardTimeoutMs: "Judgement timeout (ms, 200–10000)",
6763
+ commandGuardTimeoutMsHint: "If typesafe/jev does not answer in time, the normal prompt appears; default 1500.",
6764
+ zdr: "Zero data retention (ZDR)",
6765
+ zdrHint: "Serve requests only through upstreams that keep no prompts or completions and never train on them (the same opt-in as the CLI's CMD_ZDR=1). A few models have no ZDR upstream; requests for those models fail rather than losing ZDR protection. ZDR capacity is billed at the upstream's pass-through rates and usually costs more; the cost readout keeps quoting the ordinary catalog rates. Off by default.",
6589
6766
  accountsTitle: "Account rotation",
6590
6767
  accountsHint: "When the active account hits its usage limit (429) or its key fails (401), requests switch to the next account; when every account is exhausted the error names the earliest window reset.",
6591
6768
  accountAdd: "Add account",
@@ -6593,6 +6770,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
6593
6770
  accountLabel: "Account label",
6594
6771
  accountKey: "API key",
6595
6772
  accountKeyHint: "This account’s API key. Saving with the field blank keeps the stored key.",
6773
+ accountLoginAfterSave: "Use Save at the bottom of the page, then sign in to this account in your browser to obtain a key.",
6596
6774
  accountDefault: "Default account",
6597
6775
  activeAccount: "Active account",
6598
6776
  activeAccountAuto: "Auto (first usable account)",
@@ -6663,6 +6841,8 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
6663
6841
  usageServiceUnavailableHint: "The server returned errors (5xx); try Refresh again later.",
6664
6842
  usageNetworkError: "Could not reach the Command Code service",
6665
6843
  usageNetworkHint: "No request reached the server. Check your network connection or the API base setting.",
6844
+ usageInvalidResponse: "Could not read the Command Code response",
6845
+ usageInvalidResponseHint: "The requests received responses, but their bodies could not be parsed. Check the host HTTP proxy or response decoding.",
6666
6846
  usageUpdated: "Updated",
6667
6847
  usagePeriodEnd: "Period ends",
6668
6848
  usageActive: "Active",
@@ -6673,6 +6853,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
6673
6853
  updateHint: "A newer version has been published; click for release notes. The notice disappears once the plugin is updated.",
6674
6854
  loginTitle: "Sign in to fetch a key",
6675
6855
  loginHintIdle: "Rather not create a key by hand? Sign in and your browser opens the commandcode.ai authorization page; the approved key is stored in the local credential service and applies to the next request.",
6856
+ loginSaveBefore: "Save page changes before signing in to this account.",
6676
6857
  loginButton: "Sign in to Command Code",
6677
6858
  loginStarting: "Starting the local callback server…",
6678
6859
  loginWaiting: "Waiting for authorization in your browser…",
@@ -6887,12 +7068,12 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
6887
7068
  const pricesStore = createSnapshotStore(pricesController.state());
6888
7069
  pricesController.subscribe(() => pricesStore.set(pricesController.state()));
6889
7070
  const loginRemote = {
6890
- loginBegin: async () => {
7071
+ loginBegin: async (targetRef) => {
6891
7072
  if (usageNamespace === void 0) return {
6892
7073
  ok: false,
6893
7074
  error: { message: usageMountError ?? "commandcode remote is not mounted" }
6894
7075
  };
6895
- return usageNamespace.loginBegin();
7076
+ return usageNamespace.loginBegin(targetRef);
6896
7077
  },
6897
7078
  loginStatus: async () => {
6898
7079
  if (usageNamespace === void 0) return {
@@ -6936,7 +7117,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
6936
7117
  }),
6937
7118
  discard: () => controller.discard(),
6938
7119
  refreshUsage: () => void usageController.refresh(),
6939
- beginLogin: () => void loginController.begin(),
7120
+ beginLogin: (targetRef) => void loginController.begin(targetRef),
6940
7121
  cancelLogin: () => void loginController.cancel(),
6941
7122
  addAccount: () => controller.addAccount(),
6942
7123
  removeAccount: (id) => controller.removeAccount(id),