@mars-sea/dsh-commandcode-provider 0.8.4 โ 0.10.0-alpha.1
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 +39 -0
- package/README.md +24 -4
- package/README.zh-CN.md +24 -4
- package/lib/client.js +699 -111
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +43 -4
- package/lib/index.js +146 -40
- package/lib/index.js.map +1 -1
- package/package.json +38 -39
package/lib/client.js
CHANGED
|
@@ -4,17 +4,146 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
-
let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
|
|
8
7
|
let react = require("react");
|
|
9
8
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
10
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
+
//#region src/client/snapshot-store.ts
|
|
11
|
+
/** Notify every subscriber without letting one faulty UI consumer suppress the rest. */
|
|
12
|
+
function notifyListeners(listeners) {
|
|
13
|
+
for (const listener of listeners) try {
|
|
14
|
+
listener();
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error("[dsh-commandcode-provider] snapshot subscriber failed:", error);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Create one observable snapshot store. */
|
|
20
|
+
function createSnapshotStore(initial) {
|
|
21
|
+
let snapshot = initial;
|
|
22
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
23
|
+
return {
|
|
24
|
+
getSnapshot: () => snapshot,
|
|
25
|
+
subscribe(listener) {
|
|
26
|
+
listeners.add(listener);
|
|
27
|
+
return () => {
|
|
28
|
+
listeners.delete(listener);
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
set(value) {
|
|
32
|
+
if (Object.is(value, snapshot)) return;
|
|
33
|
+
snapshot = value;
|
|
34
|
+
notifyListeners(listeners);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/command-locales.ts
|
|
40
|
+
const commandcodeCommand = {
|
|
41
|
+
zh: {
|
|
42
|
+
title: "๐ Command Code ็จ้{account}",
|
|
43
|
+
accountTitle: "๐ {label}{badges}",
|
|
44
|
+
accountSeparator: "โโโโโโโโโโโโโโโโโโโโ",
|
|
45
|
+
activeBadge: " โ
ๅฝๅไฝฟ็จ",
|
|
46
|
+
invalidCredentialBadge: " โ ๅฏ้ฅๆ ๆ",
|
|
47
|
+
cooldownBadge: " โณ ้้ขๅทๅดไธญ๏ผ้็ฝฎ {when}",
|
|
48
|
+
rateLimitBadge: " โณ ๅทฒ่พพ้้ข๏ผ็ญๅพ
็ชๅฃๆขๆต๏ผ",
|
|
49
|
+
unconfigured: " (ๆช้
็ฝฎ API ๅฏ้ฅ)",
|
|
50
|
+
blockedInvalidKey: "โ API ๅฏ้ฅๆ ๆๆๅทฒ่ฟๆ โ ๆๅก็ซฏๆ็ปไบๅ
จ้จ่ฏทๆฑ๏ผ401๏ผ๏ผ่ฏทๆฃๆฅ่ฏฅ่ดฆๆท็ๅฏ้ฅ้
็ฝฎ",
|
|
51
|
+
blockedServiceUnavailable: "โ ๏ธ Command Code ๆๅกๆๆถไธๅฏ็จ๏ผ5xx๏ผ๏ผ็จๅ้่ฏ",
|
|
52
|
+
blockedNetwork: "โ ๏ธ ๆ ๆณ่ฟๆฅ Command Code ๆๅก โ ่ฏทๆฃๆฅ็ฝ็ปๆ API ๅฐๅ",
|
|
53
|
+
planLine: " ๐ฆ ๅฅ้ค {name}{status}{period}",
|
|
54
|
+
planPeriodSuffix: " ยท ่ดฆๆๆชๆญข {date}",
|
|
55
|
+
usageHeader: "โโ ่ฏทๆฑ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ",
|
|
56
|
+
requestsLine: " ๐ฌ ่ฏทๆฑ {n} ๆฌก / ๅคฑ่ดฅ {f} ๆๅ็ {r}%",
|
|
57
|
+
costLine: " ๐ฐ ่ฑ่ดน {money} ({credits} credits)",
|
|
58
|
+
tokensLine: " ๐ค Token {in} ๅ
ฅ / {out} ๅบ",
|
|
59
|
+
creditsHeader: "โโ ไฟก็จ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ",
|
|
60
|
+
monthlyLine: " ๐ณ ๆ้ขๅบฆ {monthly} (ๅทฒ่ดญ {purchased} / ่ต ้ {free})",
|
|
61
|
+
barLine: " โ {bar} {pct}%",
|
|
62
|
+
windowsHeader: "โโ ็ชๅฃ็จ้ โโโโโโโโโโโโโโโโโโโโโโโโโโ",
|
|
63
|
+
fiveHourLine: " โฑ 5 ๅฐๆถ {used} / {cap}{warn}",
|
|
64
|
+
weeklyLine: " ๐
ๆฏๅจ {used} / {cap}{warn}",
|
|
65
|
+
windowBarLine: " โ {bar} ้็ฝฎ {when}",
|
|
66
|
+
exceededWarning: " โ ๏ธ ่ถ
้!",
|
|
67
|
+
resetSuffix: "้็ฝฎ {when}",
|
|
68
|
+
partialFailures: "โ ๏ธ ้จๅ็ซฏ็นๅคฑ่ดฅ: {list}",
|
|
69
|
+
noData: "(no data โ check your API key)",
|
|
70
|
+
errorText: "Could not fetch Command Code usage: {message}",
|
|
71
|
+
imageGate: "ๅฝๅไผ่ฏๅทฒๅ
ๅซๅพ็๏ผ่ๆจกๅ {model} ไธๆฏๆๅพ็่พๅ
ฅ๏ผ่ฏท้ๆฉๆฏๆๅพ็็ๆจกๅ๏ผๆๅ
็งป้คไผ่ฏไธญ็ๅพ็ใ"
|
|
72
|
+
},
|
|
73
|
+
en: {
|
|
74
|
+
title: "๐ Command Code usage{account}",
|
|
75
|
+
accountTitle: "๐ {label}{badges}",
|
|
76
|
+
accountSeparator: "โโโโโโโโโโโโโโโโโโโโ",
|
|
77
|
+
activeBadge: " โ
active",
|
|
78
|
+
invalidCredentialBadge: " โ invalid key",
|
|
79
|
+
cooldownBadge: " โณ cooling down, resets {when}",
|
|
80
|
+
rateLimitBadge: " โณ rate-limited (waiting for window probe)",
|
|
81
|
+
unconfigured: " (no API key configured)",
|
|
82
|
+
blockedInvalidKey: "โ API key invalid or expired โ the server rejected every request (401); check the key configured for this account",
|
|
83
|
+
blockedServiceUnavailable: "โ ๏ธ Command Code service temporarily unavailable (5xx); try again later",
|
|
84
|
+
blockedNetwork: "โ ๏ธ could not reach the Command Code service โ check your network or the API base setting",
|
|
85
|
+
planLine: " ๐ฆ Plan {name}{status}{period}",
|
|
86
|
+
planPeriodSuffix: " ยท period ends {date}",
|
|
87
|
+
usageHeader: "โโ Requests โโโโโโโโโโโโโโโโโโโโโโโโโโ",
|
|
88
|
+
requestsLine: " ๐ฌ Requests {n} / failed {f} success rate {r}%",
|
|
89
|
+
costLine: " ๐ฐ Spend {money} ({credits} credits)",
|
|
90
|
+
tokensLine: " ๐ค Tokens {in} in / {out} out",
|
|
91
|
+
creditsHeader: "โโ Credits โโโโโโโโโโโโโโโโโโโโโโโโโโโ",
|
|
92
|
+
monthlyLine: " ๐ณ Monthly {monthly} (purchased {purchased} / free {free})",
|
|
93
|
+
barLine: " โ {bar} {pct}%",
|
|
94
|
+
windowsHeader: "โโ Window usage โโโโโโโโโโโโโโโโโโโโโโ",
|
|
95
|
+
fiveHourLine: " โฑ 5-hour {used} / {cap}{warn}",
|
|
96
|
+
weeklyLine: " ๐
Weekly {used} / {cap}{warn}",
|
|
97
|
+
windowBarLine: " โ {bar} resets {when}",
|
|
98
|
+
exceededWarning: " โ ๏ธ exceeded!",
|
|
99
|
+
resetSuffix: "resets {when}",
|
|
100
|
+
partialFailures: "โ ๏ธ some endpoints failed: {list}",
|
|
101
|
+
noData: "(no data โ check your API key)",
|
|
102
|
+
errorText: "Could not fetch Command Code usage: {message}",
|
|
103
|
+
imageGate: "This session already contains images, and model {model} does not accept image input; please select an image-capable model, or remove the images from the session first."
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
//#endregion
|
|
11
107
|
//#region src/client/sessions.ts
|
|
108
|
+
/**
|
|
109
|
+
* Friendly-error wrapper for the harness's image-session gate.
|
|
110
|
+
*
|
|
111
|
+
* The host rejects switching to a text-only model while the session already
|
|
112
|
+
* contains images with a `model-unavailable` error
|
|
113
|
+
* (`dsh-host-apiproxy`'s `session.selectModel` handler). That rejection is
|
|
114
|
+
* intentional and cannot be relaxed from the plugin side โ the adapter's
|
|
115
|
+
* `inputModalities` is exactly what makes the guard work. What we CAN do is
|
|
116
|
+
* make the error message friendlier: wrap the shared
|
|
117
|
+
* `connection.api.sessions.selectModel` face so a `model-unavailable`
|
|
118
|
+
* rejection shows a clear, actionable hint (with the requested model name)
|
|
119
|
+
* instead of the raw English harness message.
|
|
120
|
+
*
|
|
121
|
+
* The wrapper is deliberately narrow: only the `model-unavailable` code is
|
|
122
|
+
* rewritten, only when the message matches the image-session gate, and only
|
|
123
|
+
* the message text changes โ the error code and details pass through
|
|
124
|
+
* untouched so any caller that switches on `error.code` keeps working.
|
|
125
|
+
*
|
|
126
|
+
* The wire types are spelled structurally here (not imported from
|
|
127
|
+
* `@deepseek-ai/dsh-host-apiproxy`) so this client bundle does not drag an
|
|
128
|
+
* extra peer dependency into the package; the shapes are stable and the
|
|
129
|
+
* client build inlines them anyway.
|
|
130
|
+
*
|
|
131
|
+
* The wrapper takes a `getLocale` thunk because it is reached from a
|
|
132
|
+
* non-React path that has no `t` in scope; the supplied thunk reads the
|
|
133
|
+
* active locale at call time (typically `() => ctx.locale.getLocale().active`
|
|
134
|
+
* in the client entry), and the message template lives in the shared
|
|
135
|
+
* `commandcodeCommand` dictionary used by the Host-side `/commandcode`
|
|
136
|
+
* command โ the same bilingual surface serves both.
|
|
137
|
+
*
|
|
138
|
+
* This module is deliberately free of React and other client-platform
|
|
139
|
+
* imports so the node test runner can exercise it directly.
|
|
140
|
+
*/
|
|
12
141
|
/** Whether a selectModel rejection is the harness's image-session gate. */
|
|
13
142
|
function isImageSessionRejection(result) {
|
|
14
143
|
return !result.result.ok && result.result.error.code === "model-unavailable" && result.result.error.message.includes("does not accept image input");
|
|
15
144
|
}
|
|
16
145
|
/** Wrap the shared sessions API so selectModel failures read friendlier. */
|
|
17
|
-
function withFriendlyImageError(sessions) {
|
|
146
|
+
function withFriendlyImageError(sessions, getLocale) {
|
|
18
147
|
const selectModel = sessions.selectModel.bind(sessions);
|
|
19
148
|
return {
|
|
20
149
|
...sessions,
|
|
@@ -22,25 +151,63 @@ window.__ModuleLoader__.load({
|
|
|
22
151
|
const result = await selectModel(payload, signal);
|
|
23
152
|
if (!isImageSessionRejection(result)) return result;
|
|
24
153
|
const model = result.result.error.details?.model ?? payload.model;
|
|
154
|
+
const template = commandcodeCommand[getLocale()].imageGate ?? commandcodeCommand.en.imageGate;
|
|
25
155
|
return {
|
|
26
156
|
...result,
|
|
27
157
|
result: {
|
|
28
158
|
...result.result,
|
|
29
159
|
error: {
|
|
30
160
|
...result.result.error,
|
|
31
|
-
message:
|
|
161
|
+
message: template.replace("{model}", model)
|
|
32
162
|
}
|
|
33
163
|
}
|
|
34
164
|
};
|
|
35
165
|
}
|
|
36
166
|
};
|
|
37
167
|
}
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Install the friendly-error wrapper when a legacy sessions face is present.
|
|
170
|
+
*
|
|
171
|
+
* @returns whether the wrapper was installed. The rewrite is only UX polish;
|
|
172
|
+
* a 0.1.2 connection has no `api.sessions`, and its absence must never block
|
|
173
|
+
* the plugin from mounting its settings, credential, or usage surfaces.
|
|
174
|
+
*/
|
|
175
|
+
function installFriendlyImageError(connection, getLocale) {
|
|
176
|
+
const api = connection.api;
|
|
177
|
+
const sessions = api?.sessions;
|
|
178
|
+
if (api === void 0 || sessions === void 0 || typeof sessions.selectModel !== "function") return false;
|
|
179
|
+
api.sessions = withFriendlyImageError(sessions, getLocale);
|
|
180
|
+
return true;
|
|
41
181
|
}
|
|
42
182
|
//#endregion
|
|
43
183
|
//#region src/client/settings.ts
|
|
184
|
+
/**
|
|
185
|
+
* Browser controller for the "Command Code" settings page.
|
|
186
|
+
*
|
|
187
|
+
* The page lives at the same settings-nav level as General / Models / Plugins
|
|
188
|
+
* (a `settings.section` entry, id `commandcode`). It exists because the
|
|
189
|
+
* Models page renders an unknown-adapter-family card for the `commandcode`
|
|
190
|
+
* provider and deliberately disables its submit โ the API key cannot be
|
|
191
|
+
* configured there. This page owns the connection facts the plugin resolves
|
|
192
|
+
* per request:
|
|
193
|
+
*
|
|
194
|
+
* - API key -> written through the credentials domain under the reference
|
|
195
|
+
* the plugin resolves (`apiKeyEnv`, default
|
|
196
|
+
* `COMMANDCODE_API_KEY`). The literal never rides a response,
|
|
197
|
+
* so the control only reports whether one is configured.
|
|
198
|
+
* - API base -> the `llm-commandcode` settings namespace (`apiBase`), same
|
|
199
|
+
* namespace the Models page card addresses.
|
|
200
|
+
* - Working dir, request/stream timeouts -> the same namespace.
|
|
201
|
+
*
|
|
202
|
+
* The controller mirrors the plugin-card pattern from the harness's own
|
|
203
|
+
* settings UI: it binds the `llm-commandcode` namespace through the
|
|
204
|
+
* `settingsScope` service, keeps a staged draft of edits, and writes them on
|
|
205
|
+
* save through `scope.set` / the credentials domain. The Host stays the
|
|
206
|
+
* single fact source; the snapshot is republished after each accepted write.
|
|
207
|
+
*
|
|
208
|
+
* This module is deliberately free of JSX โ it only produces the state face
|
|
209
|
+
* the React component renders.
|
|
210
|
+
*/
|
|
44
211
|
/** The settings namespace the plugin registers (host half, src/index.ts). */
|
|
45
212
|
const COMMANDCODE_NS = "llm-commandcode";
|
|
46
213
|
/** Default credential reference the plugin resolves when none is named. */
|
|
@@ -505,10 +672,7 @@ window.__ModuleLoader__.load({
|
|
|
505
672
|
/** Write one account's key, then re-read the Host's credential states. */
|
|
506
673
|
async writeKeyTo(ref, value) {
|
|
507
674
|
try {
|
|
508
|
-
if (!(await this.api.credentials.set(
|
|
509
|
-
ref,
|
|
510
|
-
value
|
|
511
|
-
})).result.ok) return false;
|
|
675
|
+
if (!(await this.api.credentials.set(ref, value)).ok) return false;
|
|
512
676
|
} catch {
|
|
513
677
|
return false;
|
|
514
678
|
}
|
|
@@ -524,14 +688,14 @@ window.__ModuleLoader__.load({
|
|
|
524
688
|
];
|
|
525
689
|
let response;
|
|
526
690
|
try {
|
|
527
|
-
response = await this.api.credentials.describe(
|
|
691
|
+
response = await this.api.credentials.describe(refs);
|
|
528
692
|
} catch {
|
|
529
693
|
return;
|
|
530
694
|
}
|
|
531
|
-
if (!response.
|
|
695
|
+
if (!response.ok) return;
|
|
532
696
|
let changed = false;
|
|
533
697
|
for (const ref of refs) {
|
|
534
|
-
const view = response.
|
|
698
|
+
const view = response.value?.[ref];
|
|
535
699
|
const next = {
|
|
536
700
|
configured: view?.configured ?? false,
|
|
537
701
|
writable: view?.writable ?? true
|
|
@@ -609,7 +773,7 @@ window.__ModuleLoader__.load({
|
|
|
609
773
|
/** Unset one stored credential, then re-read the Host's credential states. */
|
|
610
774
|
async unsetKey(ref) {
|
|
611
775
|
try {
|
|
612
|
-
if (!(await this.api.credentials.unset(
|
|
776
|
+
if (!(await this.api.credentials.unset(ref)).ok) return false;
|
|
613
777
|
} catch {
|
|
614
778
|
return false;
|
|
615
779
|
}
|
|
@@ -649,6 +813,47 @@ window.__ModuleLoader__.load({
|
|
|
649
813
|
}
|
|
650
814
|
};
|
|
651
815
|
//#endregion
|
|
816
|
+
//#region src/client/legacy-credentials.ts
|
|
817
|
+
/** Convert a legacy credentials ApiProxy into the current settings-page face. */
|
|
818
|
+
function adaptLegacyCredentials(legacy) {
|
|
819
|
+
if (legacy === void 0) return void 0;
|
|
820
|
+
return { credentials: {
|
|
821
|
+
describe: async (refs) => {
|
|
822
|
+
const response = await legacy.describe({ refs });
|
|
823
|
+
return response.result.ok ? {
|
|
824
|
+
ok: true,
|
|
825
|
+
value: response.result.value.credentials
|
|
826
|
+
} : {
|
|
827
|
+
ok: false,
|
|
828
|
+
error: response.result.error
|
|
829
|
+
};
|
|
830
|
+
},
|
|
831
|
+
set: async (ref, value) => {
|
|
832
|
+
const response = await legacy.set({
|
|
833
|
+
ref,
|
|
834
|
+
value
|
|
835
|
+
});
|
|
836
|
+
return response.result.ok ? {
|
|
837
|
+
ok: true,
|
|
838
|
+
value: void 0
|
|
839
|
+
} : {
|
|
840
|
+
ok: false,
|
|
841
|
+
error: response.result.error
|
|
842
|
+
};
|
|
843
|
+
},
|
|
844
|
+
unset: async (ref) => {
|
|
845
|
+
const response = await legacy.unset({ ref });
|
|
846
|
+
return response.result.ok ? {
|
|
847
|
+
ok: true,
|
|
848
|
+
value: void 0
|
|
849
|
+
} : {
|
|
850
|
+
ok: false,
|
|
851
|
+
error: response.result.error
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
} };
|
|
855
|
+
}
|
|
856
|
+
//#endregion
|
|
652
857
|
//#region src/client/usage.ts
|
|
653
858
|
const IDLE = {
|
|
654
859
|
status: "idle",
|
|
@@ -1197,7 +1402,7 @@ window.__ModuleLoader__.load({
|
|
|
1197
1402
|
};
|
|
1198
1403
|
//#endregion
|
|
1199
1404
|
//#region package.json
|
|
1200
|
-
var version = "0.
|
|
1405
|
+
var version = "0.10.0-alpha.1";
|
|
1201
1406
|
var repository = {
|
|
1202
1407
|
"type": "git",
|
|
1203
1408
|
"url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
|
|
@@ -1377,9 +1582,19 @@ window.__ModuleLoader__.load({
|
|
|
1377
1582
|
*
|
|
1378
1583
|
* The layout mirrors the harness's settings pages: a max-width content
|
|
1379
1584
|
* column, labelled fields with hints, a reset affordance, and a
|
|
1380
|
-
* save/discard footer.
|
|
1585
|
+
* save/discard footer. Rarely touched connection facts (API base, working
|
|
1586
|
+
* directory, timeouts, plan filter) fold into a collapsed Advanced card
|
|
1587
|
+
* (`AdvancedSection`) so the page leads with the key and the usage facts.
|
|
1588
|
+
* Styles are injected once by the client entry
|
|
1381
1589
|
* (see src/client/index.ts) and class-prefixed `cc-` to stay local.
|
|
1382
1590
|
*/
|
|
1591
|
+
const ADVANCED_FIELDS = [
|
|
1592
|
+
"apiBase",
|
|
1593
|
+
"workingDir",
|
|
1594
|
+
"requestTimeoutMs",
|
|
1595
|
+
"streamIdleTimeoutMs",
|
|
1596
|
+
"filterModelsByPlan"
|
|
1597
|
+
];
|
|
1383
1598
|
/** One labelled field row in the page body. */
|
|
1384
1599
|
function Field({ id, label, hint, state, disabled, numeric, placeholder, onEdit, onReset, t }) {
|
|
1385
1600
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1429,6 +1644,114 @@ window.__ModuleLoader__.load({
|
|
|
1429
1644
|
return t("invalidNumber");
|
|
1430
1645
|
}
|
|
1431
1646
|
/**
|
|
1647
|
+
* The collapsed "Advanced" card: API base, working dir, both timeouts, and
|
|
1648
|
+
* the plan filter live here so the page leads with the facts a user actually
|
|
1649
|
+
* touches. Starts collapsed on every visit; expands on demand. While
|
|
1650
|
+
* collapsed, a badge names the customized count so a nonzero override stays
|
|
1651
|
+
* visible (a number field's error blocks save and must be reachable).
|
|
1652
|
+
*/
|
|
1653
|
+
function AdvancedSection({ state, disabled, t, onEdit, onReset }) {
|
|
1654
|
+
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
1655
|
+
const overridden = ADVANCED_FIELDS.filter((field) => state[field].overridden).length;
|
|
1656
|
+
const invalid = ADVANCED_FIELDS.some((field) => state[field].invalid);
|
|
1657
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1658
|
+
className: "cc-card",
|
|
1659
|
+
children: [
|
|
1660
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1661
|
+
type: "button",
|
|
1662
|
+
className: "cc-advancedHead",
|
|
1663
|
+
"aria-expanded": expanded,
|
|
1664
|
+
"aria-controls": "cc-advanced-body",
|
|
1665
|
+
onClick: () => setExpanded((value) => !value),
|
|
1666
|
+
children: [
|
|
1667
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1668
|
+
className: "cc-advancedTitle",
|
|
1669
|
+
children: t("advancedSettings")
|
|
1670
|
+
}),
|
|
1671
|
+
overridden > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1672
|
+
className: "cc-badge",
|
|
1673
|
+
children: overridden === 1 ? t("advancedOverriddenOne") : t("advancedOverriddenMany").replace("{count}", String(overridden))
|
|
1674
|
+
}) : null,
|
|
1675
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-advancedSpacer" }),
|
|
1676
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1677
|
+
className: expanded ? "cc-chevron cc-chevronUp" : "cc-chevron",
|
|
1678
|
+
"aria-hidden": "true"
|
|
1679
|
+
})
|
|
1680
|
+
]
|
|
1681
|
+
}),
|
|
1682
|
+
expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1683
|
+
id: "cc-advanced-body",
|
|
1684
|
+
className: "cc-advancedBody",
|
|
1685
|
+
children: [
|
|
1686
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1687
|
+
className: "cc-hint",
|
|
1688
|
+
children: t("advancedSettingsHint")
|
|
1689
|
+
}),
|
|
1690
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1691
|
+
id: "cc-api-base",
|
|
1692
|
+
label: t("apiBase"),
|
|
1693
|
+
hint: t("apiBaseHint"),
|
|
1694
|
+
state: state.apiBase,
|
|
1695
|
+
disabled,
|
|
1696
|
+
onEdit: (text) => onEdit("apiBase", text),
|
|
1697
|
+
onReset: () => onReset("apiBase"),
|
|
1698
|
+
t
|
|
1699
|
+
}),
|
|
1700
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1701
|
+
id: "cc-working-dir",
|
|
1702
|
+
label: t("workingDir"),
|
|
1703
|
+
hint: t("workingDirHint"),
|
|
1704
|
+
state: state.workingDir,
|
|
1705
|
+
disabled,
|
|
1706
|
+
placeholder: state.defaultWorkingDir,
|
|
1707
|
+
onEdit: (text) => onEdit("workingDir", text),
|
|
1708
|
+
onReset: () => onReset("workingDir"),
|
|
1709
|
+
t
|
|
1710
|
+
}),
|
|
1711
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1712
|
+
id: "cc-request-timeout",
|
|
1713
|
+
label: t("requestTimeoutMs"),
|
|
1714
|
+
hint: t("requestTimeoutMsHint"),
|
|
1715
|
+
state: state.requestTimeoutMs,
|
|
1716
|
+
disabled,
|
|
1717
|
+
numeric: true,
|
|
1718
|
+
onEdit: (text) => onEdit("requestTimeoutMs", text),
|
|
1719
|
+
onReset: () => onReset("requestTimeoutMs"),
|
|
1720
|
+
t
|
|
1721
|
+
}),
|
|
1722
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1723
|
+
id: "cc-stream-idle-timeout",
|
|
1724
|
+
label: t("streamIdleTimeoutMs"),
|
|
1725
|
+
hint: t("streamIdleTimeoutMsHint"),
|
|
1726
|
+
state: state.streamIdleTimeoutMs,
|
|
1727
|
+
disabled,
|
|
1728
|
+
numeric: true,
|
|
1729
|
+
onEdit: (text) => onEdit("streamIdleTimeoutMs", text),
|
|
1730
|
+
onReset: () => onReset("streamIdleTimeoutMs"),
|
|
1731
|
+
t
|
|
1732
|
+
}),
|
|
1733
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
1734
|
+
id: "cc-filter-models-by-plan",
|
|
1735
|
+
label: t("filterModelsByPlan"),
|
|
1736
|
+
hint: t("filterModelsByPlanHint"),
|
|
1737
|
+
state: state.filterModelsByPlan,
|
|
1738
|
+
disabled,
|
|
1739
|
+
defaultChecked: true,
|
|
1740
|
+
onEdit: (text) => onEdit("filterModelsByPlan", text),
|
|
1741
|
+
onReset: () => onReset("filterModelsByPlan"),
|
|
1742
|
+
t
|
|
1743
|
+
})
|
|
1744
|
+
]
|
|
1745
|
+
}) : null,
|
|
1746
|
+
expanded && invalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1747
|
+
className: "cc-invalid",
|
|
1748
|
+
role: "status",
|
|
1749
|
+
children: t("advancedInvalid")
|
|
1750
|
+
}) : null
|
|
1751
|
+
]
|
|
1752
|
+
});
|
|
1753
|
+
}
|
|
1754
|
+
/**
|
|
1432
1755
|
* One boolean field row rendered as a toggle. The staged text is `'true'` /
|
|
1433
1756
|
* `'false'` / `''` (unset โ `defaultChecked`); toggling stages the string the
|
|
1434
1757
|
* boolean field spec parses back into a real boolean on save.
|
|
@@ -1536,7 +1859,7 @@ window.__ModuleLoader__.load({
|
|
|
1536
1859
|
});
|
|
1537
1860
|
}
|
|
1538
1861
|
/** The per-reason copy for a failed login attempt. */
|
|
1539
|
-
function loginFailureCopy(reason, t) {
|
|
1862
|
+
function loginFailureCopy$1(reason, t) {
|
|
1540
1863
|
if (reason === "denied") return t("loginDenied");
|
|
1541
1864
|
if (reason === "timeout") return t("loginTimeout");
|
|
1542
1865
|
if (reason === "invalid-key") return t("loginInvalidKey");
|
|
@@ -1562,7 +1885,7 @@ window.__ModuleLoader__.load({
|
|
|
1562
1885
|
hint = `${t("loginSuccess")} ${state.userName ?? ""}${keyName}`.trim();
|
|
1563
1886
|
hintClass = "cc-loginDone";
|
|
1564
1887
|
} else if (state.phase === "failed") {
|
|
1565
|
-
hint = loginFailureCopy(state.reason, t);
|
|
1888
|
+
hint = loginFailureCopy$1(state.reason, t);
|
|
1566
1889
|
hintClass = "cc-loginError";
|
|
1567
1890
|
if (state.message !== void 0) hintTitle = state.message;
|
|
1568
1891
|
} else if (state.phase === "unavailable") {
|
|
@@ -2230,86 +2553,36 @@ window.__ModuleLoader__.load({
|
|
|
2230
2553
|
}),
|
|
2231
2554
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2232
2555
|
className: "cc-card",
|
|
2233
|
-
children: [
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
disabled,
|
|
2264
|
-
onEdit: (text) => props.edit("apiBase", text),
|
|
2265
|
-
onReset: () => props.resetField("apiBase"),
|
|
2266
|
-
t
|
|
2267
|
-
}),
|
|
2268
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2269
|
-
id: "cc-working-dir",
|
|
2270
|
-
label: t("workingDir"),
|
|
2271
|
-
hint: t("workingDirHint"),
|
|
2272
|
-
state: state.workingDir,
|
|
2273
|
-
disabled,
|
|
2274
|
-
placeholder: state.defaultWorkingDir,
|
|
2275
|
-
onEdit: (text) => props.edit("workingDir", text),
|
|
2276
|
-
onReset: () => props.resetField("workingDir"),
|
|
2277
|
-
t
|
|
2278
|
-
}),
|
|
2279
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2280
|
-
id: "cc-request-timeout",
|
|
2281
|
-
label: t("requestTimeoutMs"),
|
|
2282
|
-
hint: t("requestTimeoutMsHint"),
|
|
2283
|
-
state: state.requestTimeoutMs,
|
|
2284
|
-
disabled,
|
|
2285
|
-
numeric: true,
|
|
2286
|
-
onEdit: (text) => props.edit("requestTimeoutMs", text),
|
|
2287
|
-
onReset: () => props.resetField("requestTimeoutMs"),
|
|
2288
|
-
t
|
|
2289
|
-
}),
|
|
2290
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2291
|
-
id: "cc-stream-idle-timeout",
|
|
2292
|
-
label: t("streamIdleTimeoutMs"),
|
|
2293
|
-
hint: t("streamIdleTimeoutMsHint"),
|
|
2294
|
-
state: state.streamIdleTimeoutMs,
|
|
2295
|
-
disabled,
|
|
2296
|
-
numeric: true,
|
|
2297
|
-
onEdit: (text) => props.edit("streamIdleTimeoutMs", text),
|
|
2298
|
-
onReset: () => props.resetField("streamIdleTimeoutMs"),
|
|
2299
|
-
t
|
|
2300
|
-
}),
|
|
2301
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
2302
|
-
id: "cc-filter-models-by-plan",
|
|
2303
|
-
label: t("filterModelsByPlan"),
|
|
2304
|
-
hint: t("filterModelsByPlanHint"),
|
|
2305
|
-
state: state.filterModelsByPlan,
|
|
2306
|
-
disabled,
|
|
2307
|
-
defaultChecked: true,
|
|
2308
|
-
onEdit: (text) => props.edit("filterModelsByPlan", text),
|
|
2309
|
-
onReset: () => props.resetField("filterModelsByPlan"),
|
|
2310
|
-
t
|
|
2311
|
-
})
|
|
2312
|
-
]
|
|
2556
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SecretKeyField, {
|
|
2557
|
+
label: t("apiKey"),
|
|
2558
|
+
hint: keyLocked ? t("apiKeyLocked") : t("apiKeyHint"),
|
|
2559
|
+
state: state.apiKey,
|
|
2560
|
+
disabled: disabled || keyLocked,
|
|
2561
|
+
configured: state.apiKeyConfigured,
|
|
2562
|
+
configuredLabel: t("apiKeySet"),
|
|
2563
|
+
unconfiguredLabel: t("apiKeyUnset"),
|
|
2564
|
+
clearStaged: state.apiKeyClearStaged,
|
|
2565
|
+
showLabel: t("show"),
|
|
2566
|
+
hideLabel: t("hide"),
|
|
2567
|
+
clearLabel: t("usageKeyClear"),
|
|
2568
|
+
clearStagedLabel: t("usageKeyClearStaged"),
|
|
2569
|
+
undoClearLabel: t("usageUndoKeyClear"),
|
|
2570
|
+
onEdit: (text) => props.edit("apiKey", text),
|
|
2571
|
+
onToggleClear: () => props.toggleKeyClear("default")
|
|
2572
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginPanel, {
|
|
2573
|
+
state: login,
|
|
2574
|
+
disabled: disabled || keyLocked,
|
|
2575
|
+
t,
|
|
2576
|
+
onBegin: props.beginLogin,
|
|
2577
|
+
onCancel: props.cancelLogin
|
|
2578
|
+
})]
|
|
2579
|
+
}),
|
|
2580
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AdvancedSection, {
|
|
2581
|
+
state,
|
|
2582
|
+
disabled,
|
|
2583
|
+
t,
|
|
2584
|
+
onEdit: props.edit,
|
|
2585
|
+
onReset: props.resetField
|
|
2313
2586
|
}),
|
|
2314
2587
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2315
2588
|
className: "cc-footer",
|
|
@@ -2364,6 +2637,260 @@ window.__ModuleLoader__.load({
|
|
|
2364
2637
|
});
|
|
2365
2638
|
}
|
|
2366
2639
|
//#endregion
|
|
2640
|
+
//#region src/client/card.tsx
|
|
2641
|
+
/**
|
|
2642
|
+
* The Command Code provider card inside the harness Models settings page
|
|
2643
|
+
* (browser half). Rendered through the `settings.models.provider-card` keyed
|
|
2644
|
+
* slot available in dsh 0.1.2-alpha.2, registered with
|
|
2645
|
+
* `entryKey = 'llm-commandcode'` (the plugin's settings namespace, the key the
|
|
2646
|
+
* Models page dispatches for every Command Code provider row).
|
|
2647
|
+
*
|
|
2648
|
+
* The slot's owner props (`configured`, `keyConfigured`) mirror what the
|
|
2649
|
+
* Models page already knows; the authoritative credential facts still come
|
|
2650
|
+
* from this plugin's `CommandCodeSettingsController` shared with the dedicated
|
|
2651
|
+
* settings page, so the two surfaces can never disagree about whether a key
|
|
2652
|
+
* is stored.
|
|
2653
|
+
*
|
|
2654
|
+
* Not configured: an "Unconfigured" badge, a paste-a-key field, and the
|
|
2655
|
+
* official sign-in button โ the whole flow a first-run user needs, inline.
|
|
2656
|
+
* Configured: a green "Configured" badge plus a pointer to the full
|
|
2657
|
+
* Command Code settings page for rotation, usage, and connection facts.
|
|
2658
|
+
*
|
|
2659
|
+
* The card renders nothing until the shared controller's first snapshot is
|
|
2660
|
+
* ready (`available`), which also keeps the stale-facts window of the older
|
|
2661
|
+
* join from ever being visible. A controller-less render (card mounted before
|
|
2662
|
+
* the section registered its inject face โ the composition runs one apply)
|
|
2663
|
+
* degrades to the stateless registration-notice form.
|
|
2664
|
+
*
|
|
2665
|
+
* Styles ride the page stylesheet the client entry injects once (`cc-`
|
|
2666
|
+
* prefixed classes); the card adds no CSS of its own.
|
|
2667
|
+
*/
|
|
2668
|
+
/** Decide the card's posture from the injected face and the owner facts. */
|
|
2669
|
+
function cardMode(props) {
|
|
2670
|
+
if (props.useCommandCodeSettings === void 0) return { kind: "registration" };
|
|
2671
|
+
const snapshot = props.useCommandCodeSettings((state) => state);
|
|
2672
|
+
return {
|
|
2673
|
+
kind: "live",
|
|
2674
|
+
ready: snapshot.available,
|
|
2675
|
+
controllerConfigured: snapshot.apiKeyConfigured,
|
|
2676
|
+
writable: snapshot.writable,
|
|
2677
|
+
apiKeyWritable: snapshot.apiKeyWritable
|
|
2678
|
+
};
|
|
2679
|
+
}
|
|
2680
|
+
/** Status badge for the credential state (green when configured). */
|
|
2681
|
+
function StatusBadge({ ok, okLabel, pendingLabel }) {
|
|
2682
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2683
|
+
className: ok ? "cc-badge" : "cc-badgeMuted",
|
|
2684
|
+
children: ok ? okLabel : pendingLabel
|
|
2685
|
+
});
|
|
2686
|
+
}
|
|
2687
|
+
/**
|
|
2688
|
+
* The per-reason copy for a failed login attempt (mirrors the settings
|
|
2689
|
+
* page's LoginPanel; the same reasons can surface from this card).
|
|
2690
|
+
*/
|
|
2691
|
+
function loginFailureCopy(reason, t) {
|
|
2692
|
+
if (reason === "denied") return t("loginDenied");
|
|
2693
|
+
if (reason === "timeout") return t("loginTimeout");
|
|
2694
|
+
if (reason === "invalid-key") return t("loginInvalidKey");
|
|
2695
|
+
if (reason === "network") return t("loginNetwork");
|
|
2696
|
+
if (reason === "unavailable") return t("loginStoreFailed");
|
|
2697
|
+
if (reason === "cancelled") return t("loginCancelled");
|
|
2698
|
+
return t("loginFailedGeneric");
|
|
2699
|
+
}
|
|
2700
|
+
/** The sign-in affordance row for the not-configured card (see LoginPanel). */
|
|
2701
|
+
function CardLoginRow({ state, disabled, t, onBegin, onCancel }) {
|
|
2702
|
+
const busy = state.phase === "starting" || state.phase === "waiting";
|
|
2703
|
+
let hint = t("loginHintIdle");
|
|
2704
|
+
let hintClass = "cc-hint";
|
|
2705
|
+
if (state.phase === "starting" || state.phase === "waiting") hint = t(state.phase === "starting" ? "loginStarting" : "loginWaiting");
|
|
2706
|
+
else if (state.phase === "success") {
|
|
2707
|
+
const keyName = state.keyName !== void 0 && state.keyName !== "" ? ` ยท ${state.keyName}` : "";
|
|
2708
|
+
hint = `${t("loginSuccess")} ${state.userName ?? ""}${keyName}`.trim();
|
|
2709
|
+
hintClass = "cc-loginDone";
|
|
2710
|
+
} else if (state.phase === "failed") {
|
|
2711
|
+
hint = loginFailureCopy(state.reason, t);
|
|
2712
|
+
hintClass = "cc-loginError";
|
|
2713
|
+
} else if (state.phase === "unavailable") {
|
|
2714
|
+
hint = `${t("loginUnavailable")} ${state.message ?? ""}`.trim();
|
|
2715
|
+
hintClass = "cc-loginError";
|
|
2716
|
+
}
|
|
2717
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2718
|
+
className: "cc-field",
|
|
2719
|
+
children: [
|
|
2720
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2721
|
+
className: "cc-fieldHead",
|
|
2722
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2723
|
+
className: "cc-label",
|
|
2724
|
+
children: t("loginTitle")
|
|
2725
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2726
|
+
className: "cc-badges",
|
|
2727
|
+
children: busy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2728
|
+
type: "button",
|
|
2729
|
+
className: "cc-reset",
|
|
2730
|
+
onClick: onCancel,
|
|
2731
|
+
children: t("loginCancel")
|
|
2732
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2733
|
+
type: "button",
|
|
2734
|
+
className: "cc-reset",
|
|
2735
|
+
disabled,
|
|
2736
|
+
onClick: onBegin,
|
|
2737
|
+
children: t("loginButton")
|
|
2738
|
+
})
|
|
2739
|
+
})]
|
|
2740
|
+
}),
|
|
2741
|
+
state.authUrl !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2742
|
+
className: "cc-hint",
|
|
2743
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2744
|
+
className: "cc-loginLink",
|
|
2745
|
+
href: state.authUrl,
|
|
2746
|
+
target: "_blank",
|
|
2747
|
+
rel: "noreferrer",
|
|
2748
|
+
children: t("loginOpenLink")
|
|
2749
|
+
})
|
|
2750
|
+
}) : null,
|
|
2751
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2752
|
+
className: hintClass,
|
|
2753
|
+
children: hint
|
|
2754
|
+
})
|
|
2755
|
+
]
|
|
2756
|
+
});
|
|
2757
|
+
}
|
|
2758
|
+
/** Compact key field for the not-configured card. */
|
|
2759
|
+
function CardKeyField({ state, disabled, t, onEdit }) {
|
|
2760
|
+
const [visible, setVisible] = (0, react.useState)(false);
|
|
2761
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2762
|
+
className: "cc-field",
|
|
2763
|
+
children: [
|
|
2764
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2765
|
+
className: "cc-fieldHead",
|
|
2766
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2767
|
+
className: "cc-label",
|
|
2768
|
+
htmlFor: "cc-card-api-key",
|
|
2769
|
+
children: t("apiKey")
|
|
2770
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2771
|
+
className: "cc-badges",
|
|
2772
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2773
|
+
type: "button",
|
|
2774
|
+
className: "cc-reset",
|
|
2775
|
+
disabled,
|
|
2776
|
+
onClick: () => setVisible((value) => !value),
|
|
2777
|
+
children: visible ? t("hide") : t("show")
|
|
2778
|
+
})
|
|
2779
|
+
})]
|
|
2780
|
+
}),
|
|
2781
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2782
|
+
id: "cc-card-api-key",
|
|
2783
|
+
className: "cc-input",
|
|
2784
|
+
type: visible ? "text" : "password",
|
|
2785
|
+
autoComplete: "off",
|
|
2786
|
+
spellCheck: false,
|
|
2787
|
+
value: state.text,
|
|
2788
|
+
disabled,
|
|
2789
|
+
onChange: (event) => onEdit(event.target.value)
|
|
2790
|
+
}),
|
|
2791
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2792
|
+
className: "cc-hint",
|
|
2793
|
+
children: t("apiKeyHint")
|
|
2794
|
+
})
|
|
2795
|
+
]
|
|
2796
|
+
});
|
|
2797
|
+
}
|
|
2798
|
+
/** One "Configured" badge + pointer to the full settings page (footer area). */
|
|
2799
|
+
function CardConfiguredBody({ t }) {
|
|
2800
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2801
|
+
className: "cc-field",
|
|
2802
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2803
|
+
className: "cc-hint",
|
|
2804
|
+
children: t("cardConfiguredHint")
|
|
2805
|
+
})
|
|
2806
|
+
});
|
|
2807
|
+
}
|
|
2808
|
+
/**
|
|
2809
|
+
* The slot component body. Dispatched on every Command Code provider card of
|
|
2810
|
+
* the Models page (saved row, first-run setup posture, and add-provider
|
|
2811
|
+
* draft).
|
|
2812
|
+
*/
|
|
2813
|
+
function CommandCodeProviderCard(props) {
|
|
2814
|
+
const { t } = props;
|
|
2815
|
+
const mode = cardMode(props);
|
|
2816
|
+
const state = props.useCommandCodeSettings !== void 0 ? props.useCommandCodeSettings((snapshot) => snapshot) : void 0;
|
|
2817
|
+
const login = props.useCommandCodeLogin !== void 0 ? props.useCommandCodeLogin((snapshot) => snapshot) : void 0;
|
|
2818
|
+
const dirty = state?.dirty ?? false;
|
|
2819
|
+
const saving = state?.saving ?? false;
|
|
2820
|
+
const invalid = state?.invalid ?? false;
|
|
2821
|
+
const failed = state?.failed ?? false;
|
|
2822
|
+
const savingBlocked = !dirty || invalid;
|
|
2823
|
+
const configured = mode.kind === "live" && mode.ready ? mode.controllerConfigured : props.keyConfigured;
|
|
2824
|
+
const disabled = mode.kind === "live" && (!mode.writable || state !== void 0 && !mode.apiKeyWritable);
|
|
2825
|
+
const showBody = mode.kind === "live" && mode.ready && state !== void 0;
|
|
2826
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2827
|
+
className: "cc-providerCard",
|
|
2828
|
+
"data-cc-models-card": "true",
|
|
2829
|
+
children: [
|
|
2830
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2831
|
+
className: "cc-field",
|
|
2832
|
+
children: [
|
|
2833
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2834
|
+
className: "cc-fieldHead",
|
|
2835
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2836
|
+
className: "cc-label",
|
|
2837
|
+
children: t("cardTitle")
|
|
2838
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2839
|
+
className: "cc-badges",
|
|
2840
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusBadge, {
|
|
2841
|
+
ok: configured,
|
|
2842
|
+
okLabel: t("apiKeySet"),
|
|
2843
|
+
pendingLabel: t("apiKeyUnset")
|
|
2844
|
+
}), props.provider.active ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2845
|
+
className: "cc-badge",
|
|
2846
|
+
children: t("cardRouteActive")
|
|
2847
|
+
}) : null]
|
|
2848
|
+
})]
|
|
2849
|
+
}),
|
|
2850
|
+
mode.kind === "registration" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2851
|
+
className: "cc-hint",
|
|
2852
|
+
children: t("cardRegistrationHint")
|
|
2853
|
+
}) : null,
|
|
2854
|
+
mode.kind === "live" && !mode.ready ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2855
|
+
className: "cc-hint",
|
|
2856
|
+
children: t("cardLoadingHint")
|
|
2857
|
+
}) : null
|
|
2858
|
+
]
|
|
2859
|
+
}),
|
|
2860
|
+
showBody && !configured ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2861
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardKeyField, {
|
|
2862
|
+
state: state.apiKey,
|
|
2863
|
+
disabled,
|
|
2864
|
+
t,
|
|
2865
|
+
onEdit: (text) => props.edit("apiKey", text)
|
|
2866
|
+
}),
|
|
2867
|
+
login !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardLoginRow, {
|
|
2868
|
+
state: login,
|
|
2869
|
+
disabled,
|
|
2870
|
+
t,
|
|
2871
|
+
onBegin: props.beginLogin,
|
|
2872
|
+
onCancel: props.cancelLogin
|
|
2873
|
+
}) : null,
|
|
2874
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2875
|
+
className: "cc-footer",
|
|
2876
|
+
children: [failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2877
|
+
className: "cc-failed",
|
|
2878
|
+
role: "status",
|
|
2879
|
+
children: t("saveFailed")
|
|
2880
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2881
|
+
type: "button",
|
|
2882
|
+
className: "cc-reset",
|
|
2883
|
+
disabled: savingBlocked || saving,
|
|
2884
|
+
onClick: props.save,
|
|
2885
|
+
children: t(saving ? "saving" : "save")
|
|
2886
|
+
})]
|
|
2887
|
+
})
|
|
2888
|
+
] }) : null,
|
|
2889
|
+
showBody && configured ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardConfiguredBody, { t }) : null
|
|
2890
|
+
]
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
//#endregion
|
|
2367
2894
|
//#region src/client/locales.ts
|
|
2368
2895
|
const zh = {
|
|
2369
2896
|
nav: "Command Code",
|
|
@@ -2382,6 +2909,11 @@ window.__ModuleLoader__.load({
|
|
|
2382
2909
|
requestTimeoutMsHint: "็ญๅพ
ๅๅบ้ฆไธชๅญ่็่ถ
ๆถ๏ผ้ป่ฎค 60000ใ",
|
|
2383
2910
|
streamIdleTimeoutMs: "ๆต็ฉบ้ฒ่ถ
ๆถ๏ผๆฏซ็ง๏ผ",
|
|
2384
2911
|
streamIdleTimeoutMsHint: "็ๆๆตๅๆปๅคไน
่งไธบๆญ่ฟ๏ผ้ป่ฎค 300000๏ผ้ฟๆ่ๆจกๅๅฏ้้ปๆฐๅ้๏ผ้ป่ฎคๅผๅปๆๆพๅฎฝ๏ผใ",
|
|
2912
|
+
advancedSettings: "้ซ็บง่ฎพ็ฝฎ",
|
|
2913
|
+
advancedSettingsHint: "API ๅฐๅใๅทฅไฝ็ฎๅฝใ่ถ
ๆถไธๆจกๅ่ฟๆปค็ญไธๅธธไฟฎๆน็้้กนใ",
|
|
2914
|
+
advancedOverriddenOne: "ๅทฒ่ชๅฎไน 1 ้กน",
|
|
2915
|
+
advancedOverriddenMany: "ๅทฒ่ชๅฎไน {count} ้กน",
|
|
2916
|
+
advancedInvalid: "้ซ็บง่ฎพ็ฝฎไธญๆๆชๅกซๅฅฝ็ๆฐๅญ๏ผ่ฏทๅฑๅผไฟฎๆญฃๅๅไฟๅญใ",
|
|
2385
2917
|
filterModelsByPlan: "้่ๅฅ้คๅคๆจกๅ",
|
|
2386
2918
|
filterModelsByPlanHint: "ๅผๅฏๅ๏ผๆจกๅ้ๆฉๅจๅชๅๅบๅฝๅๅฅ้คๅฏ็จ็ๆจกๅ๏ผ่ดฆๆทๆๆๆ้ไฝ้ขๆถไผๆพ็คบๅ
จ้จใ",
|
|
2387
2919
|
accountsTitle: "ๅค่ดฆๆท่ฝฎๆข",
|
|
@@ -2463,7 +2995,12 @@ window.__ModuleLoader__.load({
|
|
|
2463
2995
|
loginNetwork: "ๆ ๆณ่ฟๆฅ Command Code ๆๅกๆ ก้ชๅฏ้ฅ๏ผ่ฏทๆฃๆฅ็ฝ็ปๅ้่ฏใ",
|
|
2464
2996
|
loginStoreFailed: "ๅฏ้ฅๆ ๆณๅๅ
ฅๆฌๆบๅญๆฎๆๅก๏ผ่ฏทๆๅจ็ฒ่ดดใ",
|
|
2465
2997
|
loginCancelled: "็ปๅฝๅทฒๅๆถใ",
|
|
2466
|
-
loginFailedGeneric: "็ปๅฝๅคฑ่ดฅ๏ผ่ฏท้่ฏๆๆๅจ็ฒ่ดดๅฏ้ฅใ"
|
|
2998
|
+
loginFailedGeneric: "็ปๅฝๅคฑ่ดฅ๏ผ่ฏท้่ฏๆๆๅจ็ฒ่ดดๅฏ้ฅใ",
|
|
2999
|
+
cardTitle: "Command Code ่ฟๆฅ",
|
|
3000
|
+
cardRouteActive: "ๅทฒๅฏ็จ",
|
|
3001
|
+
cardLoadingHint: "ๆญฃๅจ่ฏปๅ Command Code ้
็ฝฎโฆ",
|
|
3002
|
+
cardRegistrationHint: "ๆญคๅก็้ Command Code ๆไปถๆณจๅ๏ผ้่ฆ่พๆฐ็ๆฌ็ DeepSeek Harness ๆไผๆพ็คบๅฎๆดๅ
ๅฎนใ",
|
|
3003
|
+
cardConfiguredHint: "API ๅฏ้ฅๅทฒๅฐฑ็ปชใๅฆ้ๆดๆขๅฏ้ฅใๆทปๅ ๅค่ดฆๆท่ฝฎๆขใๆฅ็็จ้ๆไฟฎๆน API ๅฐๅ๏ผ่ฏทๅๅพ่ฎพ็ฝฎไธญ็ใCommand Codeใ้กตใ"
|
|
2467
3004
|
};
|
|
2468
3005
|
const en = {
|
|
2469
3006
|
nav: "Command Code",
|
|
@@ -2482,6 +3019,11 @@ window.__ModuleLoader__.load({
|
|
|
2482
3019
|
requestTimeoutMsHint: "Time to wait for the first response byte; default 60000.",
|
|
2483
3020
|
streamIdleTimeoutMs: "Stream idle timeout (ms)",
|
|
2484
3021
|
streamIdleTimeoutMsHint: "How long a stalled stream is treated as dead; default 300000 (deliberately generous โ long-thinking models can stay silent for minutes).",
|
|
3022
|
+
advancedSettings: "Advanced",
|
|
3023
|
+
advancedSettingsHint: "Rarely touched options: API base URL, working directory, timeouts, and model filtering.",
|
|
3024
|
+
advancedOverriddenOne: "1 customized",
|
|
3025
|
+
advancedOverriddenMany: "{count} customized",
|
|
3026
|
+
advancedInvalid: "A number in Advanced settings is not ready to save; expand to fix it.",
|
|
2485
3027
|
filterModelsByPlan: "Hide out-of-plan models",
|
|
2486
3028
|
filterModelsByPlanHint: "When on, the model picker lists only models your subscription includes; any on-demand credit balance shows the full catalog.",
|
|
2487
3029
|
accountsTitle: "Account rotation",
|
|
@@ -2563,7 +3105,12 @@ window.__ModuleLoader__.load({
|
|
|
2563
3105
|
loginNetwork: "Could not reach the Command Code service to validate the key; check your network and retry.",
|
|
2564
3106
|
loginStoreFailed: "The key could not be stored in the local credential service; paste it manually.",
|
|
2565
3107
|
loginCancelled: "Sign-in cancelled.",
|
|
2566
|
-
loginFailedGeneric: "Sign-in failed; try again or paste the key manually."
|
|
3108
|
+
loginFailedGeneric: "Sign-in failed; try again or paste the key manually.",
|
|
3109
|
+
cardTitle: "Command Code connection",
|
|
3110
|
+
cardRouteActive: "Active",
|
|
3111
|
+
cardLoadingHint: "Loading the Command Code configurationโฆ",
|
|
3112
|
+
cardRegistrationHint: "This card is contributed by the Command Code plugin; a newer DeepSeek Harness is needed to show the full controls.",
|
|
3113
|
+
cardConfiguredHint: "The API key is ready. To replace it, add account rotation, review usage, or change the API base, open the \"Command Code\" page in Settings."
|
|
2567
3114
|
};
|
|
2568
3115
|
//#endregion
|
|
2569
3116
|
//#region src/client/index.ts
|
|
@@ -2596,6 +3143,16 @@ window.__ModuleLoader__.load({
|
|
|
2596
3143
|
select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box-sizing:content-box;padding-right:32px;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='7' viewBox='0 0 12 7'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23888f98' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 11px center}
|
|
2597
3144
|
.cc-inputInvalid{border-color:var(--dsw-alias-label-error)}
|
|
2598
3145
|
.cc-invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}
|
|
3146
|
+
/* The Advanced card: its header row is one full-width toggle button; the
|
|
3147
|
+
* chevron is two borders on a rotated square (no icon dependency). */
|
|
3148
|
+
.cc-advancedHead{align-items:center;gap:8px;display:flex;width:100%;padding:12px 0;background:0 0;border:none;cursor:pointer;font:inherit;text-align:left}
|
|
3149
|
+
.cc-advancedHead:hover .cc-advancedTitle{color:var(--dsw-alias-label-primary)}
|
|
3150
|
+
.cc-advancedTitle{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:1.5}
|
|
3151
|
+
.cc-advancedSpacer{flex:1}
|
|
3152
|
+
.cc-chevron{flex-shrink:0;border-right:1.5px solid var(--dsw-alias-label-tertiary);border-bottom:1.5px solid var(--dsw-alias-label-tertiary);width:8px;height:8px;margin-right:4px;margin-bottom:2px;transform:rotate(45deg);transition:transform .15s ease}
|
|
3153
|
+
.cc-chevronUp{transform:rotate(-135deg);margin-bottom:-3px}
|
|
3154
|
+
.cc-advancedBody{flex-direction:column;display:flex}
|
|
3155
|
+
.cc-advancedBody>.cc-field:first-of-type{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
2599
3156
|
.cc-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}
|
|
2600
3157
|
.cc-footer{justify-content:flex-end;align-items:center;gap:8px;display:flex}
|
|
2601
3158
|
.cc-toggleRow{align-items:center;gap:8px;cursor:pointer;display:flex}
|
|
@@ -2674,25 +3231,38 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
2674
3231
|
document.head.appendChild(tag);
|
|
2675
3232
|
}
|
|
2676
3233
|
/**
|
|
2677
|
-
* Client plugin body. Gates on the services
|
|
2678
|
-
* (`slots`, `locale`, `connection`, `remote`, `settingsScope`)
|
|
2679
|
-
*
|
|
2680
|
-
*
|
|
3234
|
+
* Client plugin body. Gates on the services shared by both client generations
|
|
3235
|
+
* (`slots`, `locale`, `connection`, `remote`, `settingsScope`). Current clients
|
|
3236
|
+
* mount the page after `remote.credentials` appears; legacy clients mount it
|
|
3237
|
+
* from the connection ApiProxy credential face.
|
|
2681
3238
|
*/
|
|
2682
3239
|
function apply(ctx) {
|
|
2683
3240
|
injectPageCss();
|
|
2684
3241
|
const connection = ctx.get("connection");
|
|
2685
|
-
if (connection !== void 0) installFriendlyImageError(connection);
|
|
3242
|
+
if (connection !== void 0) installFriendlyImageError(connection, () => ctx.locale.getLocale().active === "zh" ? "zh" : "en");
|
|
2686
3243
|
ctx.effect(() => ctx.locale.register("settings.commandcode", {
|
|
2687
3244
|
zh,
|
|
2688
3245
|
en
|
|
2689
3246
|
}), "dsh-commandcode-provider: page copy");
|
|
2690
|
-
const
|
|
2691
|
-
|
|
2692
|
-
|
|
3247
|
+
const legacyApi = adaptLegacyCredentials(connection?.api?.credentials);
|
|
3248
|
+
if (legacyApi !== void 0) {
|
|
3249
|
+
applyClientSurfaces(ctx, legacyApi, connection?.hostDescription);
|
|
3250
|
+
return;
|
|
3251
|
+
}
|
|
3252
|
+
ctx.inject(["remote.credentials"], (remoteCtx) => {
|
|
3253
|
+
const credentials = remoteCtx.remote.credentials;
|
|
3254
|
+
applyClientSurfaces(remoteCtx, { credentials });
|
|
3255
|
+
});
|
|
3256
|
+
}
|
|
3257
|
+
/** Mount the one shared UI implementation over either credential transport. */
|
|
3258
|
+
function applyClientSurfaces(ctx, api, hostDescription) {
|
|
3259
|
+
const controller = new CommandCodeSettingsController(ctx.settingsScope.bind({ namespace: COMMANDCODE_NS }), api, hostDescription);
|
|
2693
3260
|
ctx.effect(() => () => controller.dispose(), "dsh-commandcode-provider: settings controller");
|
|
2694
|
-
const store =
|
|
3261
|
+
const store = createSnapshotStore(controller.state());
|
|
2695
3262
|
controller.subscribe(() => store.set(controller.state()));
|
|
3263
|
+
ctx.effect(() => ctx.remote.$on("credentials/reference-updated", () => {
|
|
3264
|
+
controller.refreshCredentials();
|
|
3265
|
+
}), "dsh-commandcode-provider: credential invalidations");
|
|
2696
3266
|
let usageNamespace;
|
|
2697
3267
|
let usageMountError;
|
|
2698
3268
|
const contribution = {
|
|
@@ -2732,7 +3302,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
2732
3302
|
return namespace.report();
|
|
2733
3303
|
} });
|
|
2734
3304
|
ctx.effect(() => () => usageController.dispose(), "dsh-commandcode-provider: usage controller");
|
|
2735
|
-
const usageStore =
|
|
3305
|
+
const usageStore = createSnapshotStore(usageController.state());
|
|
2736
3306
|
usageController.subscribe(() => usageStore.set(usageController.state()));
|
|
2737
3307
|
const loginRemote = {
|
|
2738
3308
|
loginBegin: async () => {
|
|
@@ -2759,7 +3329,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
2759
3329
|
};
|
|
2760
3330
|
const loginController = new CommandCodeLoginController(() => loginRemote);
|
|
2761
3331
|
ctx.effect(() => () => loginController.dispose(), "dsh-commandcode-provider: login controller");
|
|
2762
|
-
const loginStore =
|
|
3332
|
+
const loginStore = createSnapshotStore(loginController.state());
|
|
2763
3333
|
let lastLoginPhase = loginController.state().phase;
|
|
2764
3334
|
loginController.subscribe(() => {
|
|
2765
3335
|
const phase = loginController.state().phase;
|
|
@@ -2800,6 +3370,24 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
2800
3370
|
locale: "settings.commandcode",
|
|
2801
3371
|
inject: injected
|
|
2802
3372
|
}, CommandCodeSettingsPage));
|
|
3373
|
+
ctx.slots.inject("settings.models.provider-card", () => ctx.slots.register({
|
|
3374
|
+
name: "settings.models.provider-card",
|
|
3375
|
+
key: "llm-commandcode",
|
|
3376
|
+
locale: "settings.commandcode",
|
|
3377
|
+
inject: () => ({
|
|
3378
|
+
hooks: {
|
|
3379
|
+
commandCodeSettings: store,
|
|
3380
|
+
commandCodeLogin: loginStore
|
|
3381
|
+
},
|
|
3382
|
+
edit: (field, text) => controller.edit(field, text),
|
|
3383
|
+
save: () => void controller.save().then(() => {
|
|
3384
|
+
const settled = controller.state();
|
|
3385
|
+
if (!settled.failed && settled.anyAccountConfigured) usageController.refresh();
|
|
3386
|
+
}),
|
|
3387
|
+
beginLogin: () => void loginController.begin(),
|
|
3388
|
+
cancelLogin: () => void loginController.cancel()
|
|
3389
|
+
})
|
|
3390
|
+
}, CommandCodeProviderCard));
|
|
2803
3391
|
}
|
|
2804
3392
|
const inject = [
|
|
2805
3393
|
"slots",
|