@mars-sea/dsh-commandcode-provider 0.11.10 → 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/CHANGELOG.md +8 -0
- package/README.md +4 -4
- package/README.zh-CN.md +4 -4
- package/lib/client.js +166 -62
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +14 -9
- package/lib/index.js +111 -42
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.11.11] - 2026-09-23
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Saving account-rotation edits is visible on a long settings page** ([#60](https://github.com/Mars-Sea/dsh-commandcode-provider/issues/60)). The page's single global Save/Discard bar stays inside the settings panel, attached to the bottom of its scrolling area. A new account can be saved with only a label, then signed in through the same browser authorization flow as the default account; the Host writes the resulting key to that saved account's credential reference. Login attempts for different accounts cannot silently rejoin one another.
|
|
12
|
+
|
|
13
|
+
- **Command Code responses remain readable when a DSH HTTP dispatcher passes through compressed bytes** ([#59](https://github.com/Mars-Sea/dsh-commandcode-provider/issues/59)). Host-side chat, account usage, catalog, web search, decision, and login requests now ask for `Accept-Encoding: identity`. This also lets a Go account's `403 upgrade_required` body reach the existing CLI-transport fallback. If every usage endpoint answers but its body still cannot be parsed, the account view reports an unreadable response instead of claiming that no request reached the server.
|
|
14
|
+
|
|
7
15
|
## [0.11.10] - 2026-09-23
|
|
8
16
|
|
|
9
17
|
### Added
|
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ Pick the release line that matches your DeepSeek Harness version:
|
|
|
54
54
|
**pnpm 11 holds back new releases.** Its `minimumReleaseAge` defaults to 1440 minutes, so a version published less than a day ago is skipped and `@latest` resolves to the *previous* release — silently, with a success exit code. To install a release from the last 24 hours, name it exactly:
|
|
55
55
|
|
|
56
56
|
```sh
|
|
57
|
-
dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@0.11.
|
|
57
|
+
dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@0.11.11
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
The same applies to every profile you install into, including the terminal UI below.
|
|
@@ -73,7 +73,7 @@ dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@0.9.1 #
|
|
|
73
73
|
Each profile updates separately — the terminal UI owns its own plugin list (see below):
|
|
74
74
|
|
|
75
75
|
```sh
|
|
76
|
-
dsh plugin --profile dsh-tui update @mars-sea/dsh-commandcode-provider@0.11.
|
|
76
|
+
dsh plugin --profile dsh-tui update @mars-sea/dsh-commandcode-provider@0.11.11
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
To move to a version published less than 24 hours ago, name it exactly as in Install above; pnpm 11's age gate resolves `@latest` to the previous release instead.
|
|
@@ -102,7 +102,7 @@ After restart, enter your API key in **Settings → Command Code** and save; **S
|
|
|
102
102
|
The plugin also works under a terminal front door. **Each dsh profile owns its own plugin list**, so the web install above does not reach the terminal — add the plugin to the `dsh-tui` profile as well:
|
|
103
103
|
|
|
104
104
|
```sh
|
|
105
|
-
dsh plugin --profile dsh-tui add @mars-sea/dsh-commandcode-provider@0.11.
|
|
105
|
+
dsh plugin --profile dsh-tui add @mars-sea/dsh-commandcode-provider@0.11.11
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
Pin the exact version here. For the first 24 hours after a release, a bare package name (or `@latest`) is silently resolved to the previous one: the install succeeds, but the profile gets the older build — which is how a fresh terminal install ends up with no **`/settings` → Command Code** page and no `commandcode` models at all.
|
|
@@ -156,7 +156,7 @@ The command's user-facing copy follows the shell's locale: explicit `lang: 'en'
|
|
|
156
156
|
|
|
157
157
|
With several Command Code subscriptions, the plugin **switches to the next account automatically** when one hits its usage limit:
|
|
158
158
|
|
|
159
|
-
- **Setup** — use the **Account rotation** card at Settings → **Command Code** to add
|
|
159
|
+
- **Setup** — use the **Account rotation** card at Settings → **Command Code** to add an account and label it. Paste its API key, or use the page's global **Save** button, then **Sign in to Command Code** and open the authorization link shown on that account's row in a new browser tab. The global Save/Discard bar stays inside the settings panel, attached to the bottom of its scrolling area. The top-level key always serves first as the `default` account.
|
|
160
160
|
- **Manual switching** — the **Active account** dropdown pins a preferred account; if it is exhausted, requests fall back to other accounts and return once its window resets.
|
|
161
161
|
- **Route models to accounts** — the **Route models to accounts** card picks catalog models (multi-select, fetched from the live catalog) and routes them to an account. A request whose model is in a rule serves from that account while it is usable; an exhausted or invalid routed account falls back to the normal rotation. Rules match in list order — the first hit wins.
|
|
162
162
|
- **Show only favorite models** — the **Model allowlist** card keeps only the checked models in the model picker; unchecked shows all (the default).
|
package/README.zh-CN.md
CHANGED
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
**pnpm 11 会拦下刚发布的新版本。** 它的 `minimumReleaseAge` 默认为 1440 分钟,发布不足一天的版本会被跳过,`@latest` 解析到**上一个**版本 —— 而且是静默的,命令照样以成功退出。想装 24 小时内发布的版本,必须写精确版本号:
|
|
53
53
|
|
|
54
54
|
```sh
|
|
55
|
-
dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@0.11.
|
|
55
|
+
dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@0.11.11
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
这一点对每个 profile 都成立,包括下面的终端界面。
|
|
@@ -71,7 +71,7 @@ dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@0.9.1 #
|
|
|
71
71
|
每个 profile 各自更新 —— 终端界面有独立的插件列表(见下文):
|
|
72
72
|
|
|
73
73
|
```sh
|
|
74
|
-
dsh plugin --profile dsh-tui update @mars-sea/dsh-commandcode-provider@0.11.
|
|
74
|
+
dsh plugin --profile dsh-tui update @mars-sea/dsh-commandcode-provider@0.11.11
|
|
75
75
|
```
|
|
76
76
|
|
|
77
77
|
要更新到发布不足 24 小时的版本,和上面的安装一样写精确版本号;pnpm 11 的年龄门禁会把 `@latest` 解析成上一个版本。
|
|
@@ -100,7 +100,7 @@ cmd login # macOS/Linux;Windows 原生版:cmdc login
|
|
|
100
100
|
插件同样支持终端前端。**每个 dsh profile 有独立的插件列表**,所以上面那条 Web 安装命令不会装到终端里 —— 还要把插件装进 `dsh-tui` profile:
|
|
101
101
|
|
|
102
102
|
```sh
|
|
103
|
-
dsh plugin --profile dsh-tui add @mars-sea/dsh-commandcode-provider@0.11.
|
|
103
|
+
dsh plugin --profile dsh-tui add @mars-sea/dsh-commandcode-provider@0.11.11
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
这里请写精确版本号。新版本发布后的 24 小时内,只写包名(或 `@latest`)会被静默解析到上一个版本 —— 安装命令照样成功,但 profile 里拿到的是旧版本,结果就是全新的终端安装里既没有 **`/settings` → Command Code** 页面,也看不到任何 `commandcode` 模型。
|
|
@@ -154,7 +154,7 @@ cmd login # 写入 ~/.commandcode/auth.json
|
|
|
154
154
|
|
|
155
155
|
有多个 Command Code 订阅时,插件可以在一个账户达到用量限额后**自动切换到下一个账户**:
|
|
156
156
|
|
|
157
|
-
- **配置**:在 **设置 → Command Code**
|
|
157
|
+
- **配置**:在 **设置 → Command Code** 的「多账户轮换」卡片添加账户并填写备注名。可直接填写 API key,或先点击页面底部的全局「保存」,再在该账户行点击「登录 Command Code」并打开出现的授权链接,在新网页授权获取 key;全局「放弃 / 保存」操作栏在设置面板内部随滚动吸附于底部。顶层 key 始终是第一顺位的 `default` 账户。
|
|
158
158
|
- **手动切换**:卡片上的「当前使用账户」下拉框可指定优先账户;所选账户耗尽时自动回落到其他账户,窗口重置后自动恢复。
|
|
159
159
|
- **按模型切换账户**:在「按模型切换账户」卡片从实时模型目录**多选**模型并固定到某个账户。请求的模型在规则列表中且该账户可用时使用该账户;账户耗尽或密钥失效时自动回落到常规轮换。规则按列表顺序匹配,第一条命中生效。
|
|
160
160
|
- **只显示常用模型**:在「模型白名单」卡片勾选要保留的模型,模型选择器只列出这些;不勾选则显示全部(默认行为不变)。
|
package/lib/client.js
CHANGED
|
@@ -1803,6 +1803,21 @@ window.__ModuleLoader__.load({
|
|
|
1803
1803
|
};
|
|
1804
1804
|
//#endregion
|
|
1805
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
|
+
}
|
|
1806
1821
|
/** How often a live attempt is polled. */
|
|
1807
1822
|
const POLL_INTERVAL_MS = 1e3;
|
|
1808
1823
|
/**
|
|
@@ -1817,6 +1832,7 @@ window.__ModuleLoader__.load({
|
|
|
1817
1832
|
generation = 0;
|
|
1818
1833
|
disposed = false;
|
|
1819
1834
|
phase = "idle";
|
|
1835
|
+
targetRef;
|
|
1820
1836
|
authUrl;
|
|
1821
1837
|
userName;
|
|
1822
1838
|
keyName;
|
|
@@ -1835,6 +1851,7 @@ window.__ModuleLoader__.load({
|
|
|
1835
1851
|
state() {
|
|
1836
1852
|
return {
|
|
1837
1853
|
phase: this.phase,
|
|
1854
|
+
...this.targetRef === void 0 ? {} : { targetRef: this.targetRef },
|
|
1838
1855
|
authUrl: this.authUrl,
|
|
1839
1856
|
userName: this.userName,
|
|
1840
1857
|
keyName: this.keyName,
|
|
@@ -1843,9 +1860,10 @@ window.__ModuleLoader__.load({
|
|
|
1843
1860
|
};
|
|
1844
1861
|
}
|
|
1845
1862
|
/** Start (or rejoin) a login attempt and begin polling its status. */
|
|
1846
|
-
async begin() {
|
|
1863
|
+
async begin(targetRef) {
|
|
1847
1864
|
if (this.disposed || this.phase === "starting" || this.phase === "waiting") return;
|
|
1848
1865
|
const generation = ++this.generation;
|
|
1866
|
+
this.targetRef = targetRef;
|
|
1849
1867
|
this.set({
|
|
1850
1868
|
phase: "starting",
|
|
1851
1869
|
authUrl: void 0,
|
|
@@ -1868,7 +1886,7 @@ window.__ModuleLoader__.load({
|
|
|
1868
1886
|
}
|
|
1869
1887
|
let result;
|
|
1870
1888
|
try {
|
|
1871
|
-
result = await remote.loginBegin();
|
|
1889
|
+
result = await remote.loginBegin(targetRef);
|
|
1872
1890
|
} catch (error) {
|
|
1873
1891
|
result = {
|
|
1874
1892
|
ok: false,
|
|
@@ -2106,6 +2124,15 @@ window.__ModuleLoader__.load({
|
|
|
2106
2124
|
booleanField
|
|
2107
2125
|
};
|
|
2108
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
|
+
}
|
|
2109
2136
|
/**
|
|
2110
2137
|
* Build one strict invocation descriptor. Every Command Code Remote shares the
|
|
2111
2138
|
* `commandcode` namespace, the `commandcodeUsage` service, and a strict
|
|
@@ -2114,12 +2141,7 @@ window.__ModuleLoader__.load({
|
|
|
2114
2141
|
* only its own facts.
|
|
2115
2142
|
*/
|
|
2116
2143
|
function makeRemoteDescriptor(endpoint, method, typeSymbol, schema) {
|
|
2117
|
-
const result =
|
|
2118
|
-
mode: "strict",
|
|
2119
|
-
typeSymbol,
|
|
2120
|
-
schema,
|
|
2121
|
-
create: () => schema
|
|
2122
|
-
};
|
|
2144
|
+
const result = makeStrictCodec(typeSymbol, schema);
|
|
2123
2145
|
return {
|
|
2124
2146
|
id: `${REMOTE_PACKAGE}#${endpoint}`,
|
|
2125
2147
|
service: REMOTE_SERVICE,
|
|
@@ -2172,7 +2194,7 @@ window.__ModuleLoader__.load({
|
|
|
2172
2194
|
const report = { failures };
|
|
2173
2195
|
if (source.blocked !== void 0) {
|
|
2174
2196
|
const blocked = source.blocked;
|
|
2175
|
-
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;
|
|
2176
2198
|
else reject$1("blocked");
|
|
2177
2199
|
}
|
|
2178
2200
|
if (source.account !== void 0) {
|
|
@@ -2388,9 +2410,24 @@ window.__ModuleLoader__.load({
|
|
|
2388
2410
|
}
|
|
2389
2411
|
/** The strict result codec shared by all three login endpoints. */
|
|
2390
2412
|
const loginStatusSchema = { parse: parseLoginStatus };
|
|
2391
|
-
/**
|
|
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. */
|
|
2392
2419
|
function loginDescriptor(endpoint, method) {
|
|
2393
|
-
|
|
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;
|
|
2394
2431
|
}
|
|
2395
2432
|
/** The Client-face contribution fragment mounted on `ctx.remote`. */
|
|
2396
2433
|
const LOGIN_REMOTE_CONTRIBUTION = {
|
|
@@ -2404,9 +2441,10 @@ window.__ModuleLoader__.load({
|
|
|
2404
2441
|
//#endregion
|
|
2405
2442
|
//#region src/client/login-row.tsx
|
|
2406
2443
|
/** The sign-in alternative to pasting a key (settings page + Models card). */
|
|
2407
|
-
function LoginRow({ state, disabled, t, onBegin, onCancel }) {
|
|
2408
|
-
const
|
|
2409
|
-
const
|
|
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);
|
|
2410
2448
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2411
2449
|
className: "cc-field",
|
|
2412
2450
|
children: [
|
|
@@ -2425,17 +2463,17 @@ window.__ModuleLoader__.load({
|
|
|
2425
2463
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2426
2464
|
type: "button",
|
|
2427
2465
|
className: "cc-reset",
|
|
2428
|
-
disabled,
|
|
2466
|
+
disabled: disabled || busyElsewhere,
|
|
2429
2467
|
onClick: onBegin,
|
|
2430
2468
|
children: t("loginButton")
|
|
2431
2469
|
})
|
|
2432
2470
|
})]
|
|
2433
2471
|
}),
|
|
2434
|
-
|
|
2472
|
+
visibleState.authUrl !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2435
2473
|
className: "cc-hint",
|
|
2436
2474
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2437
2475
|
className: "cc-loginLink",
|
|
2438
|
-
href:
|
|
2476
|
+
href: visibleState.authUrl,
|
|
2439
2477
|
target: "_blank",
|
|
2440
2478
|
rel: "noreferrer",
|
|
2441
2479
|
children: t("loginOpenLink")
|
|
@@ -2444,7 +2482,7 @@ window.__ModuleLoader__.load({
|
|
|
2444
2482
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2445
2483
|
className: hint.className,
|
|
2446
2484
|
title: hint.title,
|
|
2447
|
-
children: hint.text
|
|
2485
|
+
children: disabled && !busy && disabledHint !== void 0 ? disabledHint : hint.text
|
|
2448
2486
|
})
|
|
2449
2487
|
]
|
|
2450
2488
|
});
|
|
@@ -2593,7 +2631,7 @@ window.__ModuleLoader__.load({
|
|
|
2593
2631
|
}
|
|
2594
2632
|
//#endregion
|
|
2595
2633
|
//#region package.json
|
|
2596
|
-
var version = "0.11.
|
|
2634
|
+
var version = "0.11.11";
|
|
2597
2635
|
var repository = {
|
|
2598
2636
|
"type": "git",
|
|
2599
2637
|
"url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
|
|
@@ -3398,12 +3436,14 @@ window.__ModuleLoader__.load({
|
|
|
3398
3436
|
function blockedTitle(reason, t) {
|
|
3399
3437
|
if (reason === "invalid-key") return t("usageKeyInvalid");
|
|
3400
3438
|
if (reason === "service-unavailable") return t("usageServiceUnavailable");
|
|
3439
|
+
if (reason === "invalid-response") return t("usageInvalidResponse");
|
|
3401
3440
|
return t("usageNetworkError");
|
|
3402
3441
|
}
|
|
3403
3442
|
/** The actionable hint under a blocked report's headline. */
|
|
3404
3443
|
function blockedHint(reason, t) {
|
|
3405
3444
|
if (reason === "invalid-key") return t("usageKeyInvalidHint");
|
|
3406
3445
|
if (reason === "service-unavailable") return t("usageServiceUnavailableHint");
|
|
3446
|
+
if (reason === "invalid-response") return t("usageInvalidResponseHint");
|
|
3407
3447
|
return t("usageNetworkHint");
|
|
3408
3448
|
}
|
|
3409
3449
|
/** The status dot on an account tab: cooling/invalid warn, everything else ok. */
|
|
@@ -3594,7 +3634,7 @@ window.__ModuleLoader__.load({
|
|
|
3594
3634
|
});
|
|
3595
3635
|
}
|
|
3596
3636
|
/** The multi-account card: the active-account selector + extra accounts in rotation order + add button. */
|
|
3597
|
-
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 }) {
|
|
3598
3638
|
const active = state.activeAccount;
|
|
3599
3639
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3600
3640
|
className: "cc-card",
|
|
@@ -3672,14 +3712,31 @@ window.__ModuleLoader__.load({
|
|
|
3672
3712
|
})
|
|
3673
3713
|
]
|
|
3674
3714
|
}),
|
|
3675
|
-
state.accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
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
|
+
})]
|
|
3683
3740
|
}, account.id))
|
|
3684
3741
|
]
|
|
3685
3742
|
});
|
|
@@ -3991,6 +4048,8 @@ window.__ModuleLoader__.load({
|
|
|
3991
4048
|
/** The settings page body: connection facts for the Command Code provider. */
|
|
3992
4049
|
function CommandCodeSettingsPage(props) {
|
|
3993
4050
|
const { t } = props;
|
|
4051
|
+
const footerMarker = (0, react.useRef)(null);
|
|
4052
|
+
const [footerFloating, setFooterFloating] = (0, react.useState)(false);
|
|
3994
4053
|
const state = props.useCommandCodeSettings((snapshot) => snapshot);
|
|
3995
4054
|
const usage = props.useCommandCodeUsage((snapshot) => snapshot);
|
|
3996
4055
|
const login = props.useCommandCodeLogin((snapshot) => snapshot);
|
|
@@ -3998,6 +4057,15 @@ window.__ModuleLoader__.load({
|
|
|
3998
4057
|
const keyLocked = !state.apiKeyWritable;
|
|
3999
4058
|
const savedVisible = useSavedFlash(state.savedCount);
|
|
4000
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
|
+
}, []);
|
|
4001
4069
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4002
4070
|
className: "cc-section",
|
|
4003
4071
|
"aria-label": t("title"),
|
|
@@ -4027,6 +4095,7 @@ window.__ModuleLoader__.load({
|
|
|
4027
4095
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountsCard, {
|
|
4028
4096
|
t,
|
|
4029
4097
|
state,
|
|
4098
|
+
login,
|
|
4030
4099
|
disabled,
|
|
4031
4100
|
onAdd: props.addAccount,
|
|
4032
4101
|
onRemove: props.removeAccount,
|
|
@@ -4034,7 +4103,9 @@ window.__ModuleLoader__.load({
|
|
|
4034
4103
|
onKey: props.editAccountKey,
|
|
4035
4104
|
onToggleClear: (id) => props.toggleKeyClear(id),
|
|
4036
4105
|
onActive: (text) => props.edit("activeAccount", text),
|
|
4037
|
-
onActiveReset: () => props.resetField("activeAccount")
|
|
4106
|
+
onActiveReset: () => props.resetField("activeAccount"),
|
|
4107
|
+
onBeginLogin: props.beginLogin,
|
|
4108
|
+
onCancelLogin: props.cancelLogin
|
|
4038
4109
|
}),
|
|
4039
4110
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RulesCard, {
|
|
4040
4111
|
t,
|
|
@@ -4058,7 +4129,7 @@ window.__ModuleLoader__.load({
|
|
|
4058
4129
|
label: t("apiKey"),
|
|
4059
4130
|
hint: keyLocked ? t("apiKeyLocked") : t("apiKeyHint"),
|
|
4060
4131
|
state: state.apiKey,
|
|
4061
|
-
disabled: disabled || keyLocked,
|
|
4132
|
+
disabled: disabled || keyLocked || login.targetRef === void 0 && (login.phase === "starting" || login.phase === "waiting"),
|
|
4062
4133
|
configured: state.apiKeyConfigured,
|
|
4063
4134
|
configuredLabel: t("apiKeySet"),
|
|
4064
4135
|
unconfiguredLabel: t("apiKeyUnset"),
|
|
@@ -4072,7 +4143,8 @@ window.__ModuleLoader__.load({
|
|
|
4072
4143
|
onToggleClear: () => props.toggleKeyClear("default")
|
|
4073
4144
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginRow, {
|
|
4074
4145
|
state: login,
|
|
4075
|
-
disabled: disabled || keyLocked,
|
|
4146
|
+
disabled: disabled || keyLocked || state.dirty,
|
|
4147
|
+
disabledHint: state.dirty ? t("loginSaveBefore") : void 0,
|
|
4076
4148
|
t,
|
|
4077
4149
|
onBegin: props.beginLogin,
|
|
4078
4150
|
onCancel: props.cancelLogin
|
|
@@ -4085,34 +4157,42 @@ window.__ModuleLoader__.load({
|
|
|
4085
4157
|
onEdit: props.edit,
|
|
4086
4158
|
onReset: props.resetField
|
|
4087
4159
|
}),
|
|
4088
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
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
|
+
})
|
|
4116
4196
|
}),
|
|
4117
4197
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
4118
4198
|
className: "cc-version",
|
|
@@ -4399,6 +4479,8 @@ window.__ModuleLoader__.load({
|
|
|
4399
4479
|
errorServiceUnavailableHint: "服务端返回错误(5xx),请稍后点击刷新重试。",
|
|
4400
4480
|
errorNetwork: "无法连接 Command Code 服务",
|
|
4401
4481
|
errorNetworkHint: "所有请求都没有到达服务端。请检查网络连接或 API 地址设置。",
|
|
4482
|
+
errorInvalidResponse: "Command Code 响应无法读取",
|
|
4483
|
+
errorInvalidResponseHint: "请求已收到响应,但内容无法解析。请检查宿主的 HTTP 代理或响应解压配置。",
|
|
4402
4484
|
errorGeneric: "用量获取失败"
|
|
4403
4485
|
};
|
|
4404
4486
|
/** The panel's English string table (must mirror {@link PANEL_COPY_ZH}). */
|
|
@@ -4451,6 +4533,8 @@ window.__ModuleLoader__.load({
|
|
|
4451
4533
|
errorServiceUnavailableHint: "The server returned errors (5xx). Try Refresh again in a moment.",
|
|
4452
4534
|
errorNetwork: "Could not reach the Command Code service",
|
|
4453
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.",
|
|
4454
4538
|
errorGeneric: "Could not fetch account usage"
|
|
4455
4539
|
};
|
|
4456
4540
|
/** Every panel key, in the English table's declaration order. */
|
|
@@ -4631,6 +4715,11 @@ window.__ModuleLoader__.load({
|
|
|
4631
4715
|
hint: "errorNetworkHint",
|
|
4632
4716
|
detail
|
|
4633
4717
|
};
|
|
4718
|
+
if (blocked === "invalid-response") return {
|
|
4719
|
+
title: "errorInvalidResponse",
|
|
4720
|
+
hint: "errorInvalidResponseHint",
|
|
4721
|
+
detail
|
|
4722
|
+
};
|
|
4634
4723
|
if (state.status === "error" && state.report === void 0) return {
|
|
4635
4724
|
title: "errorGeneric",
|
|
4636
4725
|
hint: "errorGeneric",
|
|
@@ -6235,6 +6324,13 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6235
6324
|
.cc-advancedBody>.cc-field:first-of-type{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
6236
6325
|
.cc-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}
|
|
6237
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}
|
|
6238
6334
|
.cc-toggleRow{align-items:center;gap:8px;cursor:pointer;display:flex}
|
|
6239
6335
|
.cc-toggleRow:has(.cc-toggle:disabled){cursor:default}
|
|
6240
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}
|
|
@@ -6526,6 +6622,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6526
6622
|
accountLabel: "账户备注名",
|
|
6527
6623
|
accountKey: "API 密钥",
|
|
6528
6624
|
accountKeyHint: "该账户的 API 密钥。留空保存不会覆盖已存储的密钥。",
|
|
6625
|
+
accountLoginAfterSave: "先点击页面底部的「保存」,再点击该账户的「登录 Command Code」通过网页获取密钥。",
|
|
6529
6626
|
accountDefault: "默认账户",
|
|
6530
6627
|
activeAccount: "当前使用账户",
|
|
6531
6628
|
activeAccountAuto: "自动(第一个可用账户)",
|
|
@@ -6596,6 +6693,8 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6596
6693
|
usageServiceUnavailableHint: "服务端返回了错误(5xx),稍后点击刷新重试。",
|
|
6597
6694
|
usageNetworkError: "无法连接 Command Code 服务",
|
|
6598
6695
|
usageNetworkHint: "所有请求都没有到达服务端。请检查网络连接或 API 地址设置。",
|
|
6696
|
+
usageInvalidResponse: "Command Code 响应无法读取",
|
|
6697
|
+
usageInvalidResponseHint: "请求已收到响应,但内容无法解析。请检查宿主的 HTTP 代理或响应解压配置。",
|
|
6599
6698
|
usageUpdated: "更新于",
|
|
6600
6699
|
usagePeriodEnd: "账期截止",
|
|
6601
6700
|
usageActive: "当前使用",
|
|
@@ -6606,6 +6705,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6606
6705
|
updateHint: "已发布新版本,点击查看发布说明;更新插件后刷新本页,提示会自动消失。",
|
|
6607
6706
|
loginTitle: "通过官方登录获取密钥",
|
|
6608
6707
|
loginHintIdle: "不想手动创建密钥?点击登录后浏览器会打开 commandcode.ai 授权页,完成后密钥自动写入本机凭据服务,下次请求即生效。",
|
|
6708
|
+
loginSaveBefore: "请先保存本页更改,再通过网页登录此账户。",
|
|
6609
6709
|
loginButton: "登录 Command Code",
|
|
6610
6710
|
loginStarting: "正在启动本地回调服务…",
|
|
6611
6711
|
loginWaiting: "等待在浏览器中完成授权…",
|
|
@@ -6670,6 +6770,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6670
6770
|
accountLabel: "Account label",
|
|
6671
6771
|
accountKey: "API key",
|
|
6672
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.",
|
|
6673
6774
|
accountDefault: "Default account",
|
|
6674
6775
|
activeAccount: "Active account",
|
|
6675
6776
|
activeAccountAuto: "Auto (first usable account)",
|
|
@@ -6740,6 +6841,8 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6740
6841
|
usageServiceUnavailableHint: "The server returned errors (5xx); try Refresh again later.",
|
|
6741
6842
|
usageNetworkError: "Could not reach the Command Code service",
|
|
6742
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.",
|
|
6743
6846
|
usageUpdated: "Updated",
|
|
6744
6847
|
usagePeriodEnd: "Period ends",
|
|
6745
6848
|
usageActive: "Active",
|
|
@@ -6750,6 +6853,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6750
6853
|
updateHint: "A newer version has been published; click for release notes. The notice disappears once the plugin is updated.",
|
|
6751
6854
|
loginTitle: "Sign in to fetch a key",
|
|
6752
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.",
|
|
6753
6857
|
loginButton: "Sign in to Command Code",
|
|
6754
6858
|
loginStarting: "Starting the local callback server…",
|
|
6755
6859
|
loginWaiting: "Waiting for authorization in your browser…",
|
|
@@ -6964,12 +7068,12 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6964
7068
|
const pricesStore = createSnapshotStore(pricesController.state());
|
|
6965
7069
|
pricesController.subscribe(() => pricesStore.set(pricesController.state()));
|
|
6966
7070
|
const loginRemote = {
|
|
6967
|
-
loginBegin: async () => {
|
|
7071
|
+
loginBegin: async (targetRef) => {
|
|
6968
7072
|
if (usageNamespace === void 0) return {
|
|
6969
7073
|
ok: false,
|
|
6970
7074
|
error: { message: usageMountError ?? "commandcode remote is not mounted" }
|
|
6971
7075
|
};
|
|
6972
|
-
return usageNamespace.loginBegin();
|
|
7076
|
+
return usageNamespace.loginBegin(targetRef);
|
|
6973
7077
|
},
|
|
6974
7078
|
loginStatus: async () => {
|
|
6975
7079
|
if (usageNamespace === void 0) return {
|
|
@@ -7013,7 +7117,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
7013
7117
|
}),
|
|
7014
7118
|
discard: () => controller.discard(),
|
|
7015
7119
|
refreshUsage: () => void usageController.refresh(),
|
|
7016
|
-
beginLogin: () => void loginController.begin(),
|
|
7120
|
+
beginLogin: (targetRef) => void loginController.begin(targetRef),
|
|
7017
7121
|
cancelLogin: () => void loginController.cancel(),
|
|
7018
7122
|
addAccount: () => controller.addAccount(),
|
|
7019
7123
|
removeAccount: (id) => controller.removeAccount(id),
|