@mars-sea/dsh-commandcode-provider 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +28 -2
- package/README.zh-CN.md +28 -2
- package/lib/client.js +556 -97
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +236 -23
- package/lib/index.js +510 -100
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -105,7 +105,8 @@ window.__ModuleLoader__.load({
|
|
|
105
105
|
textField("workingDir"),
|
|
106
106
|
numberField$1("requestTimeoutMs"),
|
|
107
107
|
numberField$1("streamIdleTimeoutMs"),
|
|
108
|
-
booleanField$1("filterModelsByPlan")
|
|
108
|
+
booleanField$1("filterModelsByPlan"),
|
|
109
|
+
textField("activeAccount")
|
|
109
110
|
];
|
|
110
111
|
/**
|
|
111
112
|
* Controller bridging the `llm-commandcode` scope and the credentials domain
|
|
@@ -121,11 +122,18 @@ window.__ModuleLoader__.load({
|
|
|
121
122
|
disposers = [];
|
|
122
123
|
disposed = false;
|
|
123
124
|
defaultWorkingDir;
|
|
124
|
-
credential
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
/** The credential reference the default account resolves. */
|
|
126
|
+
credentialRef = DEFAULT_API_KEY_REF;
|
|
127
|
+
/** Host-reported configured/writable state per credential reference. */
|
|
128
|
+
credentialStates = /* @__PURE__ */ new Map();
|
|
129
|
+
/** Staged account additions (not yet saved). */
|
|
130
|
+
addedAccounts = [];
|
|
131
|
+
/** Staged removals of stored extra accounts, by credential reference. */
|
|
132
|
+
removedRefs = /* @__PURE__ */ new Set();
|
|
133
|
+
/** Staged label drafts, by credential reference. */
|
|
134
|
+
labelDrafts = /* @__PURE__ */ new Map();
|
|
135
|
+
/** Staged key drafts, by credential reference (blank = keep stored key). */
|
|
136
|
+
keyDrafts = /* @__PURE__ */ new Map();
|
|
129
137
|
saving = false;
|
|
130
138
|
failed = false;
|
|
131
139
|
/**
|
|
@@ -139,6 +147,7 @@ window.__ModuleLoader__.load({
|
|
|
139
147
|
this.api = api;
|
|
140
148
|
this.disposers.push(scope.subscribe(() => {
|
|
141
149
|
this.recomputeCredentialRef();
|
|
150
|
+
this.describeAll();
|
|
142
151
|
this.publish();
|
|
143
152
|
}));
|
|
144
153
|
if (hostDescription !== void 0) {
|
|
@@ -153,7 +162,7 @@ window.__ModuleLoader__.load({
|
|
|
153
162
|
}));
|
|
154
163
|
}
|
|
155
164
|
this.recomputeCredentialRef();
|
|
156
|
-
this.
|
|
165
|
+
this.describeAll();
|
|
157
166
|
}
|
|
158
167
|
/** Release every subscription held on external sources. Idempotent. */
|
|
159
168
|
dispose() {
|
|
@@ -172,13 +181,9 @@ window.__ModuleLoader__.load({
|
|
|
172
181
|
recomputeCredentialRef() {
|
|
173
182
|
const snapshot = this.scope.getSnapshot();
|
|
174
183
|
const named = typeof snapshot.value?.apiKeyEnv === "string" && snapshot.value.apiKeyEnv.length > 0 ? snapshot.value.apiKeyEnv : DEFAULT_API_KEY_REF;
|
|
175
|
-
if (named === this.
|
|
176
|
-
this.
|
|
177
|
-
|
|
178
|
-
configured: false,
|
|
179
|
-
writable: true
|
|
180
|
-
};
|
|
181
|
-
this.readCredential();
|
|
184
|
+
if (named === this.credentialRef) return;
|
|
185
|
+
this.credentialRef = named;
|
|
186
|
+
this.credentialStates.delete(named);
|
|
182
187
|
}
|
|
183
188
|
/** Subscribe to state projections. @returns the disposer. */
|
|
184
189
|
subscribe(listener) {
|
|
@@ -189,11 +194,14 @@ window.__ModuleLoader__.load({
|
|
|
189
194
|
state() {
|
|
190
195
|
const snapshot = this.scope.getSnapshot();
|
|
191
196
|
const plan = this.plan();
|
|
197
|
+
const credential = this.credentialStates.get(this.credentialRef);
|
|
198
|
+
const accounts = this.effectiveAccounts();
|
|
192
199
|
return {
|
|
193
200
|
available: snapshot.status === "ready",
|
|
194
201
|
writable: snapshot.writable,
|
|
195
|
-
apiKeyConfigured:
|
|
196
|
-
|
|
202
|
+
apiKeyConfigured: credential?.configured ?? false,
|
|
203
|
+
anyAccountConfigured: (credential?.configured ?? false) || accounts.some((account) => account.configured),
|
|
204
|
+
apiKeyWritable: credential?.writable ?? true,
|
|
197
205
|
apiKey: {
|
|
198
206
|
text: this.staged.get("apiKey")?.text ?? "",
|
|
199
207
|
clear: false,
|
|
@@ -206,12 +214,59 @@ window.__ModuleLoader__.load({
|
|
|
206
214
|
requestTimeoutMs: this.field("requestTimeoutMs"),
|
|
207
215
|
streamIdleTimeoutMs: this.field("streamIdleTimeoutMs"),
|
|
208
216
|
filterModelsByPlan: this.field("filterModelsByPlan"),
|
|
209
|
-
|
|
217
|
+
activeAccount: this.field("activeAccount"),
|
|
218
|
+
accounts,
|
|
219
|
+
dirty: plan.length > 0 || this.accountsDirty(),
|
|
210
220
|
invalid: plan.some((item) => item.run === void 0),
|
|
211
221
|
saving: this.saving,
|
|
212
222
|
failed: this.failed
|
|
213
223
|
};
|
|
214
224
|
}
|
|
225
|
+
/** Stage a new extra account (saved on the next `save()`). */
|
|
226
|
+
addAccount() {
|
|
227
|
+
const used = /* @__PURE__ */ new Set([
|
|
228
|
+
this.credentialRef,
|
|
229
|
+
...this.storedExtras().map((extra) => extra.ref),
|
|
230
|
+
...this.addedAccounts.map((extra) => extra.ref)
|
|
231
|
+
]);
|
|
232
|
+
let n = 2;
|
|
233
|
+
while (used.has(`COMMANDCODE_API_KEY_${n}`)) n += 1;
|
|
234
|
+
const index = this.storedExtras().length + this.addedAccounts.length + 2;
|
|
235
|
+
this.addedAccounts.push({
|
|
236
|
+
label: `Account ${index}`,
|
|
237
|
+
ref: `COMMANDCODE_API_KEY_${n}`
|
|
238
|
+
});
|
|
239
|
+
this.failed = false;
|
|
240
|
+
this.describeAll();
|
|
241
|
+
this.publish();
|
|
242
|
+
}
|
|
243
|
+
/** Stage one extra account's removal (or drop an unsaved addition). */
|
|
244
|
+
removeAccount(id) {
|
|
245
|
+
const addedIndex = this.addedAccounts.findIndex((extra) => extra.ref === id);
|
|
246
|
+
if (addedIndex >= 0) this.addedAccounts.splice(addedIndex, 1);
|
|
247
|
+
else this.removedRefs.add(id);
|
|
248
|
+
this.labelDrafts.delete(id);
|
|
249
|
+
this.keyDrafts.delete(id);
|
|
250
|
+
const stagedActive = this.staged.get("activeAccount");
|
|
251
|
+
if ((stagedActive !== void 0 ? stagedActive.clear ? "" : stagedActive.text : typeof this.sectionValue("activeAccount") === "string" ? this.sectionValue("activeAccount") : "") === id) this.staged.set("activeAccount", {
|
|
252
|
+
text: "",
|
|
253
|
+
clear: true
|
|
254
|
+
});
|
|
255
|
+
this.failed = false;
|
|
256
|
+
this.publish();
|
|
257
|
+
}
|
|
258
|
+
/** Stage one extra account's label draft. */
|
|
259
|
+
editAccountLabel(id, text) {
|
|
260
|
+
this.labelDrafts.set(id, text);
|
|
261
|
+
this.failed = false;
|
|
262
|
+
this.publish();
|
|
263
|
+
}
|
|
264
|
+
/** Stage one extra account's key draft (blank keeps the stored key). */
|
|
265
|
+
editAccountKey(id, text) {
|
|
266
|
+
this.keyDrafts.set(id, text);
|
|
267
|
+
this.failed = false;
|
|
268
|
+
this.publish();
|
|
269
|
+
}
|
|
215
270
|
/** Stage one field's draft text. */
|
|
216
271
|
edit(field, text) {
|
|
217
272
|
this.staged.set(field, {
|
|
@@ -239,15 +294,17 @@ window.__ModuleLoader__.load({
|
|
|
239
294
|
}
|
|
240
295
|
/** Discard every staged edit. */
|
|
241
296
|
discard() {
|
|
242
|
-
if (this.staged.size === 0 && !this.failed) return;
|
|
297
|
+
if (this.staged.size === 0 && !this.accountsStaged() && !this.failed) return;
|
|
243
298
|
this.staged.clear();
|
|
299
|
+
this.clearAccountStaging();
|
|
244
300
|
this.failed = false;
|
|
245
301
|
this.publish();
|
|
246
302
|
}
|
|
247
303
|
/** Write every staged edit, then re-read the Host's accepted state. */
|
|
248
304
|
async save() {
|
|
249
305
|
const plan = this.plan();
|
|
250
|
-
|
|
306
|
+
const accountRuns = this.accountPlan();
|
|
307
|
+
if (plan.length === 0 && accountRuns.length === 0 || this.saving) return;
|
|
251
308
|
const runs = [];
|
|
252
309
|
for (const item of plan) {
|
|
253
310
|
if (item.run === void 0) return;
|
|
@@ -257,12 +314,34 @@ window.__ModuleLoader__.load({
|
|
|
257
314
|
this.failed = false;
|
|
258
315
|
this.publish();
|
|
259
316
|
let landed = true;
|
|
260
|
-
for (const run of runs)
|
|
317
|
+
for (const run of [...runs, ...accountRuns]) if (!await run()) {
|
|
318
|
+
landed = false;
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
261
321
|
this.saving = false;
|
|
262
322
|
this.failed = !landed;
|
|
263
|
-
if (landed)
|
|
323
|
+
if (landed) {
|
|
324
|
+
this.staged.clear();
|
|
325
|
+
this.clearAccountStaging();
|
|
326
|
+
} else this.reconcileAccountStaging();
|
|
264
327
|
this.publish();
|
|
265
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Drop account staging the stored section already reflects: additions whose
|
|
331
|
+
* ref is now stored, removals whose ref is gone, and label drafts matching
|
|
332
|
+
* the stored label. Key drafts are kept — a landed key write is idempotent
|
|
333
|
+
* on retry, and the draft carries the user's intent when it was the
|
|
334
|
+
* accounts write that failed.
|
|
335
|
+
*/
|
|
336
|
+
reconcileAccountStaging() {
|
|
337
|
+
const stored = new Set(this.storedExtras().map((extra) => extra.ref));
|
|
338
|
+
this.addedAccounts = this.addedAccounts.filter((extra) => !stored.has(extra.ref));
|
|
339
|
+
for (const ref of [...this.removedRefs]) if (!stored.has(ref)) this.removedRefs.delete(ref);
|
|
340
|
+
for (const [ref, text] of [...this.labelDrafts]) {
|
|
341
|
+
const storedLabel = this.storedExtras().find((extra) => extra.ref === ref)?.label;
|
|
342
|
+
if (storedLabel === void 0 || storedLabel === text.trim()) this.labelDrafts.delete(ref);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
266
345
|
spec(field) {
|
|
267
346
|
const spec = this.specs.get(field);
|
|
268
347
|
if (spec === void 0) throw new Error(`commandcode settings page has no field ${field}`);
|
|
@@ -349,38 +428,136 @@ window.__ModuleLoader__.load({
|
|
|
349
428
|
await this.scope.set(field, value);
|
|
350
429
|
return this.userLayer()?.[field] === value;
|
|
351
430
|
}
|
|
352
|
-
/** Write the staged key, then re-read whether the Host
|
|
431
|
+
/** Write the staged default key, then re-read whether the Host holds it. */
|
|
353
432
|
async writeKey(value) {
|
|
433
|
+
return this.writeKeyTo(this.credentialRef, value);
|
|
434
|
+
}
|
|
435
|
+
/** Write one account's key, then re-read the Host's credential states. */
|
|
436
|
+
async writeKeyTo(ref, value) {
|
|
354
437
|
try {
|
|
355
438
|
if (!(await this.api.credentials.set({
|
|
356
|
-
ref
|
|
439
|
+
ref,
|
|
357
440
|
value
|
|
358
441
|
})).result.ok) return false;
|
|
359
442
|
} catch {
|
|
360
443
|
return false;
|
|
361
444
|
}
|
|
362
|
-
await this.
|
|
363
|
-
return this.
|
|
445
|
+
await this.describeAll();
|
|
446
|
+
return this.credentialStates.get(ref)?.configured ?? false;
|
|
364
447
|
}
|
|
365
|
-
/** Ask the credentials domain about
|
|
366
|
-
async
|
|
367
|
-
const
|
|
448
|
+
/** Ask the credentials domain about every reference this page writes. */
|
|
449
|
+
async describeAll() {
|
|
450
|
+
const refs = [
|
|
451
|
+
this.credentialRef,
|
|
452
|
+
...this.storedExtras().map((extra) => extra.ref),
|
|
453
|
+
...this.addedAccounts.map((extra) => extra.ref)
|
|
454
|
+
];
|
|
368
455
|
let response;
|
|
369
456
|
try {
|
|
370
|
-
response = await this.api.credentials.describe({ refs
|
|
457
|
+
response = await this.api.credentials.describe({ refs });
|
|
371
458
|
} catch {
|
|
372
459
|
return;
|
|
373
460
|
}
|
|
374
461
|
if (!response.result.ok) return;
|
|
375
|
-
|
|
376
|
-
const
|
|
377
|
-
ref
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
462
|
+
let changed = false;
|
|
463
|
+
for (const ref of refs) {
|
|
464
|
+
const view = response.result.value.credentials[ref];
|
|
465
|
+
const next = {
|
|
466
|
+
configured: view?.configured ?? false,
|
|
467
|
+
writable: view?.writable ?? true
|
|
468
|
+
};
|
|
469
|
+
const prev = this.credentialStates.get(ref);
|
|
470
|
+
if (prev === void 0 || prev.configured !== next.configured || prev.writable !== next.writable) {
|
|
471
|
+
this.credentialStates.set(ref, next);
|
|
472
|
+
changed = true;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
if (changed) this.publish();
|
|
476
|
+
}
|
|
477
|
+
/** The stored extra accounts from the settings section (`accounts`). */
|
|
478
|
+
storedExtras() {
|
|
479
|
+
const raw = this.scope.getSnapshot().value?.accounts;
|
|
480
|
+
if (!Array.isArray(raw)) return [];
|
|
481
|
+
const out = [];
|
|
482
|
+
for (const entry of raw) {
|
|
483
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) continue;
|
|
484
|
+
const record = entry;
|
|
485
|
+
const ref = record.apiKeyEnv;
|
|
486
|
+
if (typeof ref !== "string" || ref === "") continue;
|
|
487
|
+
const label = record.label;
|
|
488
|
+
out.push({
|
|
489
|
+
label: typeof label === "string" && label !== "" ? label : ref,
|
|
490
|
+
ref
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
return out;
|
|
494
|
+
}
|
|
495
|
+
/** Every extra account row: stored (minus staged removals) + staged adds. */
|
|
496
|
+
effectiveAccounts() {
|
|
497
|
+
const stored = this.storedExtras().filter((extra) => !this.removedRefs.has(extra.ref)).map((extra) => ({
|
|
498
|
+
...extra,
|
|
499
|
+
added: false
|
|
500
|
+
}));
|
|
501
|
+
const added = this.addedAccounts.map((extra) => ({
|
|
502
|
+
...extra,
|
|
503
|
+
added: true
|
|
504
|
+
}));
|
|
505
|
+
return [...stored, ...added].map((extra) => ({
|
|
506
|
+
id: extra.ref,
|
|
507
|
+
ref: extra.ref,
|
|
508
|
+
label: this.labelDrafts.get(extra.ref) ?? extra.label,
|
|
509
|
+
keyText: this.keyDrafts.get(extra.ref) ?? "",
|
|
510
|
+
configured: this.credentialStates.get(extra.ref)?.configured ?? false,
|
|
511
|
+
writable: this.credentialStates.get(extra.ref)?.writable ?? true,
|
|
512
|
+
added: extra.added
|
|
513
|
+
}));
|
|
514
|
+
}
|
|
515
|
+
/** Whether any account-level staging (add/remove/label/key) exists. */
|
|
516
|
+
accountsStaged() {
|
|
517
|
+
return this.addedAccounts.length > 0 || this.removedRefs.size > 0 || this.labelDrafts.size > 0 || this.keyDrafts.size > 0;
|
|
518
|
+
}
|
|
519
|
+
/** Whether the staged account edits differ from the stored section. */
|
|
520
|
+
accountsDirty() {
|
|
521
|
+
if (this.addedAccounts.length > 0 || this.removedRefs.size > 0) return true;
|
|
522
|
+
for (const [ref, text] of this.labelDrafts) {
|
|
523
|
+
const base = this.storedExtras().find((extra) => extra.ref === ref)?.label;
|
|
524
|
+
if (base !== void 0 && text.trim() !== "" && text !== base) return true;
|
|
525
|
+
}
|
|
526
|
+
for (const text of this.keyDrafts.values()) if (text.trim() !== "") return true;
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
/** Reset every account-level staged edit. */
|
|
530
|
+
clearAccountStaging() {
|
|
531
|
+
this.addedAccounts = [];
|
|
532
|
+
this.removedRefs.clear();
|
|
533
|
+
this.labelDrafts.clear();
|
|
534
|
+
this.keyDrafts.clear();
|
|
535
|
+
}
|
|
536
|
+
/** The account-level writes a save performs (empty when nothing staged). */
|
|
537
|
+
accountPlan() {
|
|
538
|
+
if (!this.accountsDirty()) return [];
|
|
539
|
+
const runs = [];
|
|
540
|
+
for (const [ref, text] of this.keyDrafts) {
|
|
541
|
+
const value = text.trim();
|
|
542
|
+
if (value !== "" && !this.removedRefs.has(ref)) runs.push(() => this.writeKeyTo(ref, value));
|
|
543
|
+
}
|
|
544
|
+
runs.push(() => this.writeAccounts());
|
|
545
|
+
return runs;
|
|
546
|
+
}
|
|
547
|
+
/** Persist the staged accounts list into the settings section. */
|
|
548
|
+
async writeAccounts() {
|
|
549
|
+
const base = [...this.storedExtras().filter((extra) => !this.removedRefs.has(extra.ref)), ...this.addedAccounts];
|
|
550
|
+
const seen = /* @__PURE__ */ new Set();
|
|
551
|
+
const list = base.filter((extra) => !seen.has(extra.ref) && (seen.add(extra.ref), true)).map((extra) => {
|
|
552
|
+
const draft = this.labelDrafts.get(extra.ref)?.trim();
|
|
553
|
+
return {
|
|
554
|
+
label: draft !== void 0 && draft !== "" ? draft : extra.label,
|
|
555
|
+
apiKeyEnv: extra.ref
|
|
556
|
+
};
|
|
557
|
+
});
|
|
558
|
+
await this.scope.set("accounts", list);
|
|
559
|
+
const after = this.storedExtras();
|
|
560
|
+
return after.length === list.length && list.every((item, index) => after[index]?.ref === item.apiKeyEnv);
|
|
384
561
|
}
|
|
385
562
|
publish() {
|
|
386
563
|
if (this.disposed) return;
|
|
@@ -594,6 +771,25 @@ window.__ModuleLoader__.load({
|
|
|
594
771
|
}
|
|
595
772
|
return report;
|
|
596
773
|
}
|
|
774
|
+
/** Parse one untrusted boundary value into a {@link CommandCodeAccountUsage}. */
|
|
775
|
+
function parseAccountUsage(value) {
|
|
776
|
+
const source = record(value, "account");
|
|
777
|
+
return {
|
|
778
|
+
id: stringField(source, "id", "account.id"),
|
|
779
|
+
label: stringField(source, "label", "account.label"),
|
|
780
|
+
configured: booleanField(source, "configured", "account.configured"),
|
|
781
|
+
active: booleanField(source, "active", "account.active"),
|
|
782
|
+
mark: stringField(source, "mark", "account.mark"),
|
|
783
|
+
cooldownUntil: numberField(source, "cooldownUntil", "account.cooldownUntil"),
|
|
784
|
+
report: parseUsageReport(source.report)
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
/** Parse the wire result into a {@link CommandCodeAccountsReport}. */
|
|
788
|
+
function parseAccountsReport(value) {
|
|
789
|
+
const accounts = record(value, "result").accounts;
|
|
790
|
+
if (!Array.isArray(accounts)) reject("accounts");
|
|
791
|
+
return { accounts: accounts.map(parseAccountUsage) };
|
|
792
|
+
}
|
|
597
793
|
/** The Client-face contribution mounted on `ctx.remote`. */
|
|
598
794
|
const USAGE_REMOTE_CONTRIBUTION = {
|
|
599
795
|
package: USAGE_REMOTE_PACKAGE,
|
|
@@ -606,8 +802,8 @@ window.__ModuleLoader__.load({
|
|
|
606
802
|
parameters: [],
|
|
607
803
|
result: {
|
|
608
804
|
mode: "strict",
|
|
609
|
-
typeSymbol: `${USAGE_REMOTE_PACKAGE}#
|
|
610
|
-
schema: { parse:
|
|
805
|
+
typeSymbol: `${USAGE_REMOTE_PACKAGE}#CommandCodeAccountsReport`,
|
|
806
|
+
schema: { parse: parseAccountsReport }
|
|
611
807
|
}
|
|
612
808
|
}]
|
|
613
809
|
};
|
|
@@ -815,38 +1011,55 @@ window.__ModuleLoader__.load({
|
|
|
815
1011
|
]
|
|
816
1012
|
});
|
|
817
1013
|
}
|
|
1014
|
+
/** One account's rotation state as a short badge next to its label. */
|
|
1015
|
+
function AccountMark({ entry, t }) {
|
|
1016
|
+
if (entry.active) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1017
|
+
className: "cc-usagePlan",
|
|
1018
|
+
children: t("usageActive")
|
|
1019
|
+
});
|
|
1020
|
+
if (entry.mark === "invalid-credential") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1021
|
+
className: "cc-usagePlanStatus",
|
|
1022
|
+
children: t("usageInvalidKey")
|
|
1023
|
+
});
|
|
1024
|
+
if (entry.cooldownUntil > 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1025
|
+
className: "cc-usagePlanStatus",
|
|
1026
|
+
children: [
|
|
1027
|
+
t("usageCooldown"),
|
|
1028
|
+
" ",
|
|
1029
|
+
formatResetAt(entry.cooldownUntil)
|
|
1030
|
+
]
|
|
1031
|
+
});
|
|
1032
|
+
if (entry.mark === "rate-limit") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1033
|
+
className: "cc-usagePlanStatus",
|
|
1034
|
+
children: t("usageCooldown")
|
|
1035
|
+
});
|
|
1036
|
+
return null;
|
|
1037
|
+
}
|
|
818
1038
|
/**
|
|
819
|
-
*
|
|
820
|
-
*
|
|
821
|
-
* arrives through the `commandcode/report` Remote; the API key never leaves
|
|
822
|
-
* the Host.
|
|
1039
|
+
* One pool account's facts (identity, totals, credits, window limits)
|
|
1040
|
+
* rendered inside the account-usage card.
|
|
823
1041
|
*/
|
|
824
|
-
function
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
}, [
|
|
828
|
-
apiKeyConfigured,
|
|
829
|
-
usage.status,
|
|
830
|
-
onRefresh
|
|
831
|
-
]);
|
|
832
|
-
const loading = usage.status === "loading";
|
|
833
|
-
const report = usage.report;
|
|
834
|
-
const account = report?.account;
|
|
1042
|
+
function AccountReport({ entry, t }) {
|
|
1043
|
+
const report = entry.report;
|
|
1044
|
+
const account = report.account;
|
|
835
1045
|
const accountName = account === void 0 ? "" : account.userName || account.name;
|
|
836
|
-
const credits = report
|
|
837
|
-
const plan = report
|
|
1046
|
+
const credits = report.credits;
|
|
1047
|
+
const plan = report.plan;
|
|
838
1048
|
const planName = plan?.name ?? "";
|
|
839
1049
|
const planStatus = plan !== void 0 && plan.status !== "" && plan.status !== "active" ? plan.status : "";
|
|
840
1050
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
841
|
-
className: "cc-
|
|
842
|
-
"aria-label": t("usageTitle"),
|
|
1051
|
+
className: "cc-accountReport",
|
|
843
1052
|
children: [
|
|
844
1053
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
845
1054
|
className: "cc-usageHead",
|
|
846
1055
|
children: [
|
|
847
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
1056
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
|
|
848
1057
|
className: "cc-usageTitle",
|
|
849
|
-
children:
|
|
1058
|
+
children: entry.label
|
|
1059
|
+
}),
|
|
1060
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountMark, {
|
|
1061
|
+
entry,
|
|
1062
|
+
t
|
|
850
1063
|
}),
|
|
851
1064
|
accountName !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
852
1065
|
className: "cc-usageAccount",
|
|
@@ -859,30 +1072,14 @@ window.__ModuleLoader__.load({
|
|
|
859
1072
|
planStatus !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
860
1073
|
className: "cc-usagePlanStatus",
|
|
861
1074
|
children: planStatus
|
|
862
|
-
}) : null
|
|
863
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
864
|
-
type: "button",
|
|
865
|
-
className: "cc-usageRefresh",
|
|
866
|
-
disabled: loading || !apiKeyConfigured,
|
|
867
|
-
onClick: onRefresh,
|
|
868
|
-
children: loading ? t("usageRefreshing") : t("usageRefresh")
|
|
869
|
-
})
|
|
1075
|
+
}) : null
|
|
870
1076
|
]
|
|
871
1077
|
}),
|
|
872
|
-
!
|
|
1078
|
+
!entry.configured ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
873
1079
|
className: "cc-usageHint",
|
|
874
|
-
children: t("
|
|
1080
|
+
children: t("usageUnconfigured")
|
|
875
1081
|
}) : null,
|
|
876
|
-
|
|
877
|
-
className: "cc-usageHint",
|
|
878
|
-
children: t("usageLoading")
|
|
879
|
-
}) : null,
|
|
880
|
-
usage.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
881
|
-
className: "cc-usageError",
|
|
882
|
-
role: "status",
|
|
883
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [t("usageError"), usage.error !== void 0 && usage.error !== "" ? ` — ${usage.error}` : ""] })
|
|
884
|
-
}) : null,
|
|
885
|
-
report?.usage !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1082
|
+
report.usage !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
886
1083
|
className: "cc-usageStats",
|
|
887
1084
|
children: [
|
|
888
1085
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageStat, {
|
|
@@ -935,25 +1132,17 @@ window.__ModuleLoader__.load({
|
|
|
935
1132
|
t
|
|
936
1133
|
})]
|
|
937
1134
|
}) : null,
|
|
938
|
-
|
|
1135
|
+
plan !== void 0 && plan.currentPeriodEnd > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
939
1136
|
className: "cc-usageMeta",
|
|
940
1137
|
children: [
|
|
941
|
-
|
|
1138
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
942
1139
|
className: "cc-usageUpdated",
|
|
943
1140
|
children: [
|
|
944
1141
|
t("usagePeriodEnd"),
|
|
945
1142
|
" ",
|
|
946
1143
|
new Date(plan.currentPeriodEnd).toLocaleDateString()
|
|
947
1144
|
]
|
|
948
|
-
})
|
|
949
|
-
usage.fetchedAt !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
950
|
-
className: "cc-usageUpdated",
|
|
951
|
-
children: [
|
|
952
|
-
t("usageUpdated"),
|
|
953
|
-
" ",
|
|
954
|
-
new Date(usage.fetchedAt).toLocaleTimeString()
|
|
955
|
-
]
|
|
956
|
-
}) : null,
|
|
1145
|
+
}),
|
|
957
1146
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-usageMetaSpacer" }),
|
|
958
1147
|
report.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
959
1148
|
className: "cc-usagePartial",
|
|
@@ -961,10 +1150,233 @@ window.__ModuleLoader__.load({
|
|
|
961
1150
|
children: t("usagePartial")
|
|
962
1151
|
}) : null
|
|
963
1152
|
]
|
|
1153
|
+
}) : report.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1154
|
+
className: "cc-usageMeta",
|
|
1155
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-usageMetaSpacer" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1156
|
+
className: "cc-usagePartial",
|
|
1157
|
+
title: report.failures.join("; "),
|
|
1158
|
+
children: t("usagePartial")
|
|
1159
|
+
})]
|
|
964
1160
|
}) : null
|
|
965
1161
|
]
|
|
966
1162
|
});
|
|
967
1163
|
}
|
|
1164
|
+
/**
|
|
1165
|
+
* The account-usage card: the `/commandcode` dashboard's facts rendered as
|
|
1166
|
+
* a native settings card — one section per pool account. Data arrives
|
|
1167
|
+
* through the `commandcode/report` Remote; the API keys never leave the
|
|
1168
|
+
* Host.
|
|
1169
|
+
*/
|
|
1170
|
+
function UsageCard({ t, usage, apiKeyConfigured, onRefresh }) {
|
|
1171
|
+
(0, react.useEffect)(() => {
|
|
1172
|
+
if (apiKeyConfigured && usage.status === "idle") onRefresh();
|
|
1173
|
+
}, [
|
|
1174
|
+
apiKeyConfigured,
|
|
1175
|
+
usage.status,
|
|
1176
|
+
onRefresh
|
|
1177
|
+
]);
|
|
1178
|
+
const loading = usage.status === "loading";
|
|
1179
|
+
const report = usage.report;
|
|
1180
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1181
|
+
className: "cc-usageCard",
|
|
1182
|
+
"aria-label": t("usageTitle"),
|
|
1183
|
+
children: [
|
|
1184
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1185
|
+
className: "cc-usageHead",
|
|
1186
|
+
children: [
|
|
1187
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
1188
|
+
className: "cc-usageTitle",
|
|
1189
|
+
children: t("usageTitle")
|
|
1190
|
+
}),
|
|
1191
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-usageMetaSpacer" }),
|
|
1192
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1193
|
+
type: "button",
|
|
1194
|
+
className: "cc-usageRefresh",
|
|
1195
|
+
disabled: loading || !apiKeyConfigured,
|
|
1196
|
+
onClick: onRefresh,
|
|
1197
|
+
children: loading ? t("usageRefreshing") : t("usageRefresh")
|
|
1198
|
+
})
|
|
1199
|
+
]
|
|
1200
|
+
}),
|
|
1201
|
+
!apiKeyConfigured ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1202
|
+
className: "cc-usageHint",
|
|
1203
|
+
children: t("usageNoKey")
|
|
1204
|
+
}) : null,
|
|
1205
|
+
apiKeyConfigured && report === void 0 && loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1206
|
+
className: "cc-usageHint",
|
|
1207
|
+
children: t("usageLoading")
|
|
1208
|
+
}) : null,
|
|
1209
|
+
usage.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1210
|
+
className: "cc-usageError",
|
|
1211
|
+
role: "status",
|
|
1212
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [t("usageError"), usage.error !== void 0 && usage.error !== "" ? ` — ${usage.error}` : ""] })
|
|
1213
|
+
}) : null,
|
|
1214
|
+
report?.accounts.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountReport, {
|
|
1215
|
+
entry,
|
|
1216
|
+
t
|
|
1217
|
+
}, entry.id)),
|
|
1218
|
+
report !== void 0 && usage.fetchedAt !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1219
|
+
className: "cc-usageMeta",
|
|
1220
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-usageMetaSpacer" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1221
|
+
className: "cc-usageUpdated",
|
|
1222
|
+
children: [
|
|
1223
|
+
t("usageUpdated"),
|
|
1224
|
+
" ",
|
|
1225
|
+
new Date(usage.fetchedAt).toLocaleTimeString()
|
|
1226
|
+
]
|
|
1227
|
+
})]
|
|
1228
|
+
}) : null
|
|
1229
|
+
]
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
/** One extra account row: label, key, configured badge, remove affordance. */
|
|
1233
|
+
function AccountRow({ account, disabled, t, onLabel, onKey, onRemove }) {
|
|
1234
|
+
const locked = !account.writable;
|
|
1235
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1236
|
+
className: "cc-field",
|
|
1237
|
+
children: [
|
|
1238
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1239
|
+
className: "cc-fieldHead",
|
|
1240
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
1241
|
+
className: "cc-label",
|
|
1242
|
+
htmlFor: `cc-account-label-${account.id}`,
|
|
1243
|
+
children: t("accountLabel")
|
|
1244
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1245
|
+
className: "cc-badges",
|
|
1246
|
+
children: [
|
|
1247
|
+
account.added ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1248
|
+
className: "cc-badge",
|
|
1249
|
+
children: t("unsaved")
|
|
1250
|
+
}) : null,
|
|
1251
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1252
|
+
className: account.configured ? "cc-badge" : "cc-badgeMuted",
|
|
1253
|
+
children: account.configured ? t("apiKeySet") : t("apiKeyUnset")
|
|
1254
|
+
}),
|
|
1255
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1256
|
+
type: "button",
|
|
1257
|
+
className: "cc-reset",
|
|
1258
|
+
disabled,
|
|
1259
|
+
onClick: onRemove,
|
|
1260
|
+
children: t("accountRemove")
|
|
1261
|
+
})
|
|
1262
|
+
]
|
|
1263
|
+
})]
|
|
1264
|
+
}),
|
|
1265
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1266
|
+
id: `cc-account-label-${account.id}`,
|
|
1267
|
+
className: "cc-input",
|
|
1268
|
+
type: "text",
|
|
1269
|
+
value: account.label,
|
|
1270
|
+
disabled,
|
|
1271
|
+
onChange: (event) => onLabel(event.target.value)
|
|
1272
|
+
}),
|
|
1273
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1274
|
+
id: `cc-account-key-${account.id}`,
|
|
1275
|
+
className: "cc-input",
|
|
1276
|
+
type: "password",
|
|
1277
|
+
autoComplete: "off",
|
|
1278
|
+
placeholder: t("accountKey"),
|
|
1279
|
+
value: account.keyText,
|
|
1280
|
+
disabled: disabled || locked,
|
|
1281
|
+
onChange: (event) => onKey(event.target.value)
|
|
1282
|
+
}),
|
|
1283
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1284
|
+
className: "cc-hint",
|
|
1285
|
+
children: locked ? t("apiKeyLocked") : t("accountKeyHint")
|
|
1286
|
+
})
|
|
1287
|
+
]
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
/** The multi-account card: extra accounts in rotation order + add button. */
|
|
1291
|
+
function AccountsCard({ t, state, disabled, onAdd, onRemove, onLabel, onKey, onActive, onActiveReset }) {
|
|
1292
|
+
const active = state.activeAccount;
|
|
1293
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1294
|
+
className: "cc-card",
|
|
1295
|
+
"aria-label": t("accountsTitle"),
|
|
1296
|
+
children: [
|
|
1297
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1298
|
+
className: "cc-field",
|
|
1299
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1300
|
+
className: "cc-fieldHead",
|
|
1301
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
1302
|
+
className: "cc-label",
|
|
1303
|
+
children: t("accountsTitle")
|
|
1304
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1305
|
+
className: "cc-badges",
|
|
1306
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1307
|
+
type: "button",
|
|
1308
|
+
className: "cc-reset",
|
|
1309
|
+
disabled,
|
|
1310
|
+
onClick: onAdd,
|
|
1311
|
+
children: t("accountAdd")
|
|
1312
|
+
})
|
|
1313
|
+
})]
|
|
1314
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1315
|
+
className: "cc-hint",
|
|
1316
|
+
children: t("accountsHint")
|
|
1317
|
+
})]
|
|
1318
|
+
}),
|
|
1319
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1320
|
+
className: "cc-field",
|
|
1321
|
+
children: [
|
|
1322
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1323
|
+
className: "cc-fieldHead",
|
|
1324
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
1325
|
+
className: "cc-label",
|
|
1326
|
+
htmlFor: "cc-active-account",
|
|
1327
|
+
children: t("activeAccount")
|
|
1328
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1329
|
+
className: "cc-badges",
|
|
1330
|
+
children: [active.overridden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1331
|
+
className: "cc-badge",
|
|
1332
|
+
children: t("overridden")
|
|
1333
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1334
|
+
type: "button",
|
|
1335
|
+
className: "cc-reset",
|
|
1336
|
+
disabled,
|
|
1337
|
+
onClick: onActiveReset,
|
|
1338
|
+
children: t("reset")
|
|
1339
|
+
})]
|
|
1340
|
+
})]
|
|
1341
|
+
}),
|
|
1342
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1343
|
+
id: "cc-active-account",
|
|
1344
|
+
className: "cc-input",
|
|
1345
|
+
value: active.text,
|
|
1346
|
+
disabled,
|
|
1347
|
+
onChange: (event) => onActive(event.target.value),
|
|
1348
|
+
children: [
|
|
1349
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1350
|
+
value: "",
|
|
1351
|
+
children: t("activeAccountAuto")
|
|
1352
|
+
}),
|
|
1353
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1354
|
+
value: "default",
|
|
1355
|
+
children: t("accountDefault")
|
|
1356
|
+
}),
|
|
1357
|
+
state.accounts.filter((account) => !account.added).map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1358
|
+
value: account.ref,
|
|
1359
|
+
children: account.label
|
|
1360
|
+
}, account.id))
|
|
1361
|
+
]
|
|
1362
|
+
}),
|
|
1363
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1364
|
+
className: "cc-hint",
|
|
1365
|
+
children: t("activeAccountHint")
|
|
1366
|
+
})
|
|
1367
|
+
]
|
|
1368
|
+
}),
|
|
1369
|
+
state.accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountRow, {
|
|
1370
|
+
account,
|
|
1371
|
+
disabled,
|
|
1372
|
+
t,
|
|
1373
|
+
onLabel: (text) => onLabel(account.id, text),
|
|
1374
|
+
onKey: (text) => onKey(account.id, text),
|
|
1375
|
+
onRemove: () => onRemove(account.id)
|
|
1376
|
+
}, account.id))
|
|
1377
|
+
]
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
968
1380
|
/** The settings page body: connection facts for the Command Code provider. */
|
|
969
1381
|
function CommandCodeSettingsPage(props) {
|
|
970
1382
|
const { t } = props;
|
|
@@ -992,9 +1404,20 @@ window.__ModuleLoader__.load({
|
|
|
992
1404
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageCard, {
|
|
993
1405
|
t,
|
|
994
1406
|
usage,
|
|
995
|
-
apiKeyConfigured: state.
|
|
1407
|
+
apiKeyConfigured: state.anyAccountConfigured,
|
|
996
1408
|
onRefresh: props.refreshUsage
|
|
997
1409
|
}),
|
|
1410
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountsCard, {
|
|
1411
|
+
t,
|
|
1412
|
+
state,
|
|
1413
|
+
disabled,
|
|
1414
|
+
onAdd: props.addAccount,
|
|
1415
|
+
onRemove: props.removeAccount,
|
|
1416
|
+
onLabel: props.editAccountLabel,
|
|
1417
|
+
onKey: props.editAccountKey,
|
|
1418
|
+
onActive: (text) => props.edit("activeAccount", text),
|
|
1419
|
+
onActiveReset: () => props.resetField("activeAccount")
|
|
1420
|
+
}),
|
|
998
1421
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
999
1422
|
className: "cc-card",
|
|
1000
1423
|
children: [
|
|
@@ -1112,6 +1535,17 @@ window.__ModuleLoader__.load({
|
|
|
1112
1535
|
streamIdleTimeoutMsHint: "生成流停滞多久视为断连;默认 300000(长思考模型可静默数分钟,默认值刻意放宽)。",
|
|
1113
1536
|
filterModelsByPlan: "隐藏套餐外模型",
|
|
1114
1537
|
filterModelsByPlanHint: "开启后,模型选择器只列出当前套餐可用的模型;账户持有按需余额时会显示全部。",
|
|
1538
|
+
accountsTitle: "多账户轮换",
|
|
1539
|
+
accountsHint: "当前账户达到用量限额(429)或密钥失效(401)时,请求自动切换到下一个账户;全部耗尽时会提示最早的重置时间。",
|
|
1540
|
+
accountAdd: "添加账户",
|
|
1541
|
+
accountRemove: "移除",
|
|
1542
|
+
accountLabel: "账户备注名",
|
|
1543
|
+
accountKey: "API 密钥",
|
|
1544
|
+
accountKeyHint: "该账户的 API 密钥。留空保存不会覆盖已存储的密钥。",
|
|
1545
|
+
accountDefault: "默认账户",
|
|
1546
|
+
activeAccount: "当前使用账户",
|
|
1547
|
+
activeAccountAuto: "自动(第一个可用账户)",
|
|
1548
|
+
activeAccountHint: "手动指定优先使用的账户,保存后下次请求即生效;所选账户耗尽时仍会自动切换到其他可用账户。",
|
|
1115
1549
|
overridden: "已覆盖",
|
|
1116
1550
|
reset: "重置",
|
|
1117
1551
|
invalidNumber: "无效数字",
|
|
@@ -1144,7 +1578,11 @@ window.__ModuleLoader__.load({
|
|
|
1144
1578
|
usageReset: "重置于",
|
|
1145
1579
|
usagePartial: "部分端点数据不可用",
|
|
1146
1580
|
usageUpdated: "更新于",
|
|
1147
|
-
usagePeriodEnd: "账期截止"
|
|
1581
|
+
usagePeriodEnd: "账期截止",
|
|
1582
|
+
usageActive: "当前使用",
|
|
1583
|
+
usageCooldown: "限额冷却中",
|
|
1584
|
+
usageInvalidKey: "密钥无效",
|
|
1585
|
+
usageUnconfigured: "该账户尚未配置 API 密钥。"
|
|
1148
1586
|
};
|
|
1149
1587
|
const en = {
|
|
1150
1588
|
nav: "Command Code",
|
|
@@ -1165,6 +1603,17 @@ window.__ModuleLoader__.load({
|
|
|
1165
1603
|
streamIdleTimeoutMsHint: "How long a stalled stream is treated as dead; default 300000 (deliberately generous — long-thinking models can stay silent for minutes).",
|
|
1166
1604
|
filterModelsByPlan: "Hide out-of-plan models",
|
|
1167
1605
|
filterModelsByPlanHint: "When on, the model picker lists only models your subscription includes; any on-demand credit balance shows the full catalog.",
|
|
1606
|
+
accountsTitle: "Account rotation",
|
|
1607
|
+
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.",
|
|
1608
|
+
accountAdd: "Add account",
|
|
1609
|
+
accountRemove: "Remove",
|
|
1610
|
+
accountLabel: "Account label",
|
|
1611
|
+
accountKey: "API key",
|
|
1612
|
+
accountKeyHint: "This account’s API key. Saving with the field blank keeps the stored key.",
|
|
1613
|
+
accountDefault: "Default account",
|
|
1614
|
+
activeAccount: "Active account",
|
|
1615
|
+
activeAccountAuto: "Auto (first usable account)",
|
|
1616
|
+
activeAccountHint: "Pin the preferred account; applies to the next request after saving. If the selected account is exhausted, requests still rotate to another usable account.",
|
|
1168
1617
|
overridden: "Overridden",
|
|
1169
1618
|
reset: "Reset",
|
|
1170
1619
|
invalidNumber: "Invalid number",
|
|
@@ -1197,7 +1646,11 @@ window.__ModuleLoader__.load({
|
|
|
1197
1646
|
usageReset: "Resets",
|
|
1198
1647
|
usagePartial: "Some endpoint data unavailable",
|
|
1199
1648
|
usageUpdated: "Updated",
|
|
1200
|
-
usagePeriodEnd: "Period ends"
|
|
1649
|
+
usagePeriodEnd: "Period ends",
|
|
1650
|
+
usageActive: "Active",
|
|
1651
|
+
usageCooldown: "Cooling down",
|
|
1652
|
+
usageInvalidKey: "Invalid key",
|
|
1653
|
+
usageUnconfigured: "No API key configured for this account yet."
|
|
1201
1654
|
};
|
|
1202
1655
|
//#endregion
|
|
1203
1656
|
//#region src/client/index.ts
|
|
@@ -1260,6 +1713,8 @@ window.__ModuleLoader__.load({
|
|
|
1260
1713
|
.cc-usageBarFillWarn{background:var(--dsw-alias-label-error)}
|
|
1261
1714
|
.cc-usageWindowReset{color:var(--dsw-alias-label-tertiary);margin:0;font-size:11px;line-height:1.5}
|
|
1262
1715
|
.cc-usageMeta{align-items:center;gap:8px;display:flex}
|
|
1716
|
+
.cc-accountReport{border-top:1px solid var(--dsw-alias-border-l2);padding-top:12px;flex-direction:column;gap:12px;display:flex}
|
|
1717
|
+
.cc-accountReport:first-of-type{border-top:none;padding-top:0}
|
|
1263
1718
|
.cc-usageMetaSpacer{flex:1}
|
|
1264
1719
|
.cc-usageUpdated{color:var(--dsw-alias-label-tertiary);margin:0;font-size:11px;line-height:1.5}
|
|
1265
1720
|
.cc-usagePartial{color:var(--dsw-alias-label-error);margin:0;font-size:11px;line-height:1.5}
|
|
@@ -1341,10 +1796,14 @@ window.__ModuleLoader__.load({
|
|
|
1341
1796
|
resetField: (field) => controller.resetField(field),
|
|
1342
1797
|
save: () => void controller.save().then(() => {
|
|
1343
1798
|
const settled = controller.state();
|
|
1344
|
-
if (!settled.failed && settled.
|
|
1799
|
+
if (!settled.failed && settled.anyAccountConfigured) usageController.refresh();
|
|
1345
1800
|
}),
|
|
1346
1801
|
discard: () => controller.discard(),
|
|
1347
|
-
refreshUsage: () => void usageController.refresh()
|
|
1802
|
+
refreshUsage: () => void usageController.refresh(),
|
|
1803
|
+
addAccount: () => controller.addAccount(),
|
|
1804
|
+
removeAccount: (id) => controller.removeAccount(id),
|
|
1805
|
+
editAccountLabel: (id, text) => controller.editAccountLabel(id, text),
|
|
1806
|
+
editAccountKey: (id, text) => controller.editAccountKey(id, text)
|
|
1348
1807
|
});
|
|
1349
1808
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
1350
1809
|
name: "settings.section",
|