@mars-sea/dsh-commandcode-provider 0.10.0-alpha.1 → 0.10.0-alpha.2
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 +15 -1
- package/README.md +9 -0
- package/README.zh-CN.md +9 -0
- package/lib/client.js +578 -89
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +91 -12
- package/lib/index.js +120 -27
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -304,6 +304,12 @@ window.__ModuleLoader__.load({
|
|
|
304
304
|
booleanField$1("filterModelsByPlan"),
|
|
305
305
|
textField("activeAccount")
|
|
306
306
|
];
|
|
307
|
+
/** Whether two model-id lists are equal as sets (order-insensitive). */
|
|
308
|
+
function sameModels(a, b) {
|
|
309
|
+
if (a.length !== b.length) return false;
|
|
310
|
+
const set = new Set(a);
|
|
311
|
+
return b.every((id) => set.has(id));
|
|
312
|
+
}
|
|
307
313
|
/**
|
|
308
314
|
* Controller bridging the `llm-commandcode` scope and the credentials domain
|
|
309
315
|
* onto the page. Public API mirrors the harness's CardForm actions, so the
|
|
@@ -332,6 +338,15 @@ window.__ModuleLoader__.load({
|
|
|
332
338
|
keyDrafts = /* @__PURE__ */ new Map();
|
|
333
339
|
/** Credential references staged for removal on the next save. */
|
|
334
340
|
keyClears = /* @__PURE__ */ new Set();
|
|
341
|
+
/** Staged model→account routing rules (not yet saved). */
|
|
342
|
+
addedRules = [];
|
|
343
|
+
/** Staged edits to stored routing rules, by stored row id. */
|
|
344
|
+
ruleDrafts = /* @__PURE__ */ new Map();
|
|
345
|
+
/** Stored routing rule rows staged for removal. */
|
|
346
|
+
removedRuleIds = /* @__PURE__ */ new Set();
|
|
347
|
+
/** The catalog the rule editor offers (Host-side). */
|
|
348
|
+
catalogModels = [];
|
|
349
|
+
catalogFailed = false;
|
|
335
350
|
saving = false;
|
|
336
351
|
failed = false;
|
|
337
352
|
savedCount = 0;
|
|
@@ -362,6 +377,7 @@ window.__ModuleLoader__.load({
|
|
|
362
377
|
}
|
|
363
378
|
this.recomputeCredentialRef();
|
|
364
379
|
this.describeAll();
|
|
380
|
+
this.refreshCatalog();
|
|
365
381
|
}
|
|
366
382
|
/** Release every subscription held on external sources. Idempotent. */
|
|
367
383
|
dispose() {
|
|
@@ -418,7 +434,10 @@ window.__ModuleLoader__.load({
|
|
|
418
434
|
activeAccount: this.field("activeAccount"),
|
|
419
435
|
accounts,
|
|
420
436
|
accountsRemoving: [...this.removedRefs],
|
|
421
|
-
|
|
437
|
+
rules: this.effectiveRules(),
|
|
438
|
+
catalogModels: this.catalogModels,
|
|
439
|
+
catalogFailed: this.catalogFailed,
|
|
440
|
+
dirty: plan.length > 0 || this.accountsDirty() || this.rulesDirty(),
|
|
422
441
|
invalid: plan.some((item) => item.run === void 0),
|
|
423
442
|
saving: this.saving,
|
|
424
443
|
failed: this.failed,
|
|
@@ -491,6 +510,64 @@ window.__ModuleLoader__.load({
|
|
|
491
510
|
this.describeAll();
|
|
492
511
|
this.publish();
|
|
493
512
|
}
|
|
513
|
+
/** Stage a new model → account routing rule (saved on the next `save()`). */
|
|
514
|
+
addRule() {
|
|
515
|
+
this.addedRules.push({
|
|
516
|
+
models: [],
|
|
517
|
+
account: "default"
|
|
518
|
+
});
|
|
519
|
+
this.failed = false;
|
|
520
|
+
this.publish();
|
|
521
|
+
}
|
|
522
|
+
/** Stage one routing rule's removal (or drop an unsaved addition). */
|
|
523
|
+
removeRule(id) {
|
|
524
|
+
const addedIndex = this.addedRules.findIndex((_, index) => `new-${index}` === id);
|
|
525
|
+
if (addedIndex >= 0) this.addedRules.splice(addedIndex, 1);
|
|
526
|
+
else this.removedRuleIds.add(id);
|
|
527
|
+
this.ruleDrafts.delete(id);
|
|
528
|
+
this.failed = false;
|
|
529
|
+
this.publish();
|
|
530
|
+
}
|
|
531
|
+
/** Stage one routing rule's selected model ids (multi-select). */
|
|
532
|
+
editRuleModels(id, models) {
|
|
533
|
+
const addedIndex = this.addedRules.findIndex((_, index) => `new-${index}` === id);
|
|
534
|
+
if (addedIndex >= 0) this.addedRules[addedIndex] = {
|
|
535
|
+
...this.addedRules[addedIndex],
|
|
536
|
+
models
|
|
537
|
+
};
|
|
538
|
+
else {
|
|
539
|
+
const current = this.ruleDrafts.get(id) ?? this.storedRules().find((rule) => rule.id === id) ?? {
|
|
540
|
+
models: [],
|
|
541
|
+
account: "default"
|
|
542
|
+
};
|
|
543
|
+
this.ruleDrafts.set(id, {
|
|
544
|
+
...current,
|
|
545
|
+
models
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
this.failed = false;
|
|
549
|
+
this.publish();
|
|
550
|
+
}
|
|
551
|
+
/** Stage one routing rule's target account draft. */
|
|
552
|
+
editRuleAccount(id, text) {
|
|
553
|
+
const addedIndex = this.addedRules.findIndex((_, index) => `new-${index}` === id);
|
|
554
|
+
if (addedIndex >= 0) this.addedRules[addedIndex] = {
|
|
555
|
+
...this.addedRules[addedIndex],
|
|
556
|
+
account: text
|
|
557
|
+
};
|
|
558
|
+
else {
|
|
559
|
+
const current = this.ruleDrafts.get(id) ?? this.storedRules().find((rule) => rule.id === id) ?? {
|
|
560
|
+
models: [],
|
|
561
|
+
account: "default"
|
|
562
|
+
};
|
|
563
|
+
this.ruleDrafts.set(id, {
|
|
564
|
+
...current,
|
|
565
|
+
account: text
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
this.failed = false;
|
|
569
|
+
this.publish();
|
|
570
|
+
}
|
|
494
571
|
/** Stage one field's draft text. */
|
|
495
572
|
edit(field, text) {
|
|
496
573
|
this.staged.set(field, {
|
|
@@ -519,9 +596,10 @@ window.__ModuleLoader__.load({
|
|
|
519
596
|
}
|
|
520
597
|
/** Discard every staged edit. */
|
|
521
598
|
discard() {
|
|
522
|
-
if (this.staged.size === 0 && !this.accountsStaged() && !this.failed) return;
|
|
599
|
+
if (this.staged.size === 0 && !this.accountsStaged() && !this.rulesStaged() && !this.failed) return;
|
|
523
600
|
this.staged.clear();
|
|
524
601
|
this.clearAccountStaging();
|
|
602
|
+
this.clearRuleStaging();
|
|
525
603
|
this.failed = false;
|
|
526
604
|
this.publish();
|
|
527
605
|
}
|
|
@@ -537,7 +615,8 @@ window.__ModuleLoader__.load({
|
|
|
537
615
|
async save() {
|
|
538
616
|
const plan = this.plan();
|
|
539
617
|
const accountRuns = this.accountPlan();
|
|
540
|
-
|
|
618
|
+
const ruleRuns = this.rulesPlan();
|
|
619
|
+
if (plan.length === 0 && accountRuns.length === 0 && ruleRuns.length === 0 || this.saving) return;
|
|
541
620
|
const runs = [];
|
|
542
621
|
for (const item of plan) {
|
|
543
622
|
if (item.run === void 0) return;
|
|
@@ -547,7 +626,11 @@ window.__ModuleLoader__.load({
|
|
|
547
626
|
this.failed = false;
|
|
548
627
|
this.publish();
|
|
549
628
|
let landed = true;
|
|
550
|
-
for (const run of [
|
|
629
|
+
for (const run of [
|
|
630
|
+
...runs,
|
|
631
|
+
...accountRuns,
|
|
632
|
+
...ruleRuns
|
|
633
|
+
]) if (!await run()) {
|
|
551
634
|
landed = false;
|
|
552
635
|
break;
|
|
553
636
|
}
|
|
@@ -557,7 +640,11 @@ window.__ModuleLoader__.load({
|
|
|
557
640
|
this.savedCount += 1;
|
|
558
641
|
this.staged.clear();
|
|
559
642
|
this.clearAccountStaging();
|
|
560
|
-
|
|
643
|
+
this.clearRuleStaging();
|
|
644
|
+
} else {
|
|
645
|
+
this.reconcileAccountStaging();
|
|
646
|
+
this.reconcileRuleStaging();
|
|
647
|
+
}
|
|
561
648
|
this.publish();
|
|
562
649
|
}
|
|
563
650
|
/**
|
|
@@ -708,6 +795,28 @@ window.__ModuleLoader__.load({
|
|
|
708
795
|
}
|
|
709
796
|
if (changed) this.publish();
|
|
710
797
|
}
|
|
798
|
+
/**
|
|
799
|
+
* Fetch the model catalog for the routing-rule editor through the Host
|
|
800
|
+
* Remote. Runs once at construction; call again (e.g. from the client entry
|
|
801
|
+
* once the Remote mount lands) to (re)try — a later success clears a prior
|
|
802
|
+
* failure flag so the editor recovers without a page reload.
|
|
803
|
+
*/
|
|
804
|
+
refreshCatalog() {
|
|
805
|
+
const models = this.api.models;
|
|
806
|
+
if (models === void 0) {
|
|
807
|
+
this.catalogFailed = true;
|
|
808
|
+
this.publish();
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
models().then((response) => {
|
|
812
|
+
if (response.ok && Array.isArray(response.value?.models)) {
|
|
813
|
+
this.catalogModels = response.value.models;
|
|
814
|
+
this.catalogFailed = false;
|
|
815
|
+
} else this.catalogFailed = true;
|
|
816
|
+
}, () => {
|
|
817
|
+
this.catalogFailed = true;
|
|
818
|
+
}).then(() => this.publish());
|
|
819
|
+
}
|
|
711
820
|
/** The stored extra accounts from the settings section (`accounts`). */
|
|
712
821
|
storedExtras() {
|
|
713
822
|
const raw = this.scope.getSnapshot().value?.accounts;
|
|
@@ -807,6 +916,93 @@ window.__ModuleLoader__.load({
|
|
|
807
916
|
const after = this.storedExtras();
|
|
808
917
|
return after.length === list.length && list.every((item, index) => after[index]?.ref === item.apiKeyEnv);
|
|
809
918
|
}
|
|
919
|
+
/** The stored routing rules from the settings section (`modelAccountRules`). */
|
|
920
|
+
storedRules() {
|
|
921
|
+
const raw = this.scope.getSnapshot().value?.modelAccountRules;
|
|
922
|
+
if (!Array.isArray(raw)) return [];
|
|
923
|
+
const out = [];
|
|
924
|
+
for (const [index, entry] of raw.entries()) {
|
|
925
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) continue;
|
|
926
|
+
const record = entry;
|
|
927
|
+
const models = record.models;
|
|
928
|
+
const account = record.account;
|
|
929
|
+
const modelsList = Array.isArray(models) && models.every((m) => typeof m === "string") ? models.filter((m) => m !== "") : [];
|
|
930
|
+
if (modelsList.length === 0) continue;
|
|
931
|
+
out.push({
|
|
932
|
+
id: `rule-${index}`,
|
|
933
|
+
models: modelsList,
|
|
934
|
+
account: typeof account === "string" && account !== "" ? account : "default"
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
return out;
|
|
938
|
+
}
|
|
939
|
+
/** Every routing rule row: stored (minus staged removals, with drafts) + staged adds. */
|
|
940
|
+
effectiveRules() {
|
|
941
|
+
const stored = this.storedRules().filter((rule) => !this.removedRuleIds.has(rule.id)).map((rule) => {
|
|
942
|
+
const draft = this.ruleDrafts.get(rule.id);
|
|
943
|
+
return {
|
|
944
|
+
id: rule.id,
|
|
945
|
+
models: draft?.models ?? rule.models,
|
|
946
|
+
account: draft?.account ?? rule.account,
|
|
947
|
+
added: false
|
|
948
|
+
};
|
|
949
|
+
});
|
|
950
|
+
const added = this.addedRules.map((rule, index) => ({
|
|
951
|
+
id: `new-${index}`,
|
|
952
|
+
models: rule.models,
|
|
953
|
+
account: rule.account,
|
|
954
|
+
added: true
|
|
955
|
+
}));
|
|
956
|
+
return [...stored, ...added];
|
|
957
|
+
}
|
|
958
|
+
/** Whether any routing-rule staging (add/remove/edit) exists. */
|
|
959
|
+
rulesStaged() {
|
|
960
|
+
return this.addedRules.length > 0 || this.removedRuleIds.size > 0 || this.ruleDrafts.size > 0;
|
|
961
|
+
}
|
|
962
|
+
/** Whether the staged routing rules differ from the stored section. */
|
|
963
|
+
rulesDirty() {
|
|
964
|
+
if (this.addedRules.length > 0 || this.removedRuleIds.size > 0) return true;
|
|
965
|
+
for (const [id, draft] of this.ruleDrafts) {
|
|
966
|
+
const base = this.storedRules().find((rule) => rule.id === id);
|
|
967
|
+
if (base === void 0) continue;
|
|
968
|
+
if (draft.models.length > 0 && !sameModels(draft.models, base.models)) return true;
|
|
969
|
+
if (draft.account !== base.account) return true;
|
|
970
|
+
}
|
|
971
|
+
return false;
|
|
972
|
+
}
|
|
973
|
+
/** Reset every routing-rule staged edit. */
|
|
974
|
+
clearRuleStaging() {
|
|
975
|
+
this.addedRules = [];
|
|
976
|
+
this.ruleDrafts.clear();
|
|
977
|
+
this.removedRuleIds.clear();
|
|
978
|
+
}
|
|
979
|
+
/** Drop rule staging the stored section already reflects (partial-save retry). */
|
|
980
|
+
reconcileRuleStaging() {
|
|
981
|
+
const storedIds = new Set(this.storedRules().map((rule) => rule.id));
|
|
982
|
+
for (const id of [...this.removedRuleIds]) if (!storedIds.has(id)) this.removedRuleIds.delete(id);
|
|
983
|
+
for (const id of [...this.ruleDrafts.keys()]) if (!storedIds.has(id)) this.ruleDrafts.delete(id);
|
|
984
|
+
}
|
|
985
|
+
/** The routing-rule writes a save performs (empty when nothing staged). */
|
|
986
|
+
rulesPlan() {
|
|
987
|
+
if (!this.rulesDirty()) return [];
|
|
988
|
+
return [() => this.writeRules()];
|
|
989
|
+
}
|
|
990
|
+
/** Persist the staged routing rules into the settings section. */
|
|
991
|
+
async writeRules() {
|
|
992
|
+
const list = [...this.storedRules().filter((rule) => !this.removedRuleIds.has(rule.id)).map((rule) => {
|
|
993
|
+
const draft = this.ruleDrafts.get(rule.id);
|
|
994
|
+
return {
|
|
995
|
+
models: draft !== void 0 && draft.models.length > 0 ? draft.models : rule.models,
|
|
996
|
+
account: draft?.account !== void 0 && draft.account !== "" ? draft.account : rule.account
|
|
997
|
+
};
|
|
998
|
+
}), ...this.addedRules.map((rule) => ({
|
|
999
|
+
models: rule.models,
|
|
1000
|
+
account: rule.account
|
|
1001
|
+
}))].filter((rule) => rule.models.length > 0);
|
|
1002
|
+
await this.scope.set("modelAccountRules", list);
|
|
1003
|
+
const after = this.storedRules();
|
|
1004
|
+
return after.length === list.length && list.every((item, index) => after[index] !== void 0 && sameModels(after[index].models, item.models) && after[index].account === item.account);
|
|
1005
|
+
}
|
|
810
1006
|
publish() {
|
|
811
1007
|
if (this.disposed) return;
|
|
812
1008
|
for (const listener of this.listeners) listener();
|
|
@@ -1321,6 +1517,39 @@ window.__ModuleLoader__.load({
|
|
|
1321
1517
|
}
|
|
1322
1518
|
}]
|
|
1323
1519
|
};
|
|
1520
|
+
/** Canonical `<namespace>/<method>` endpoint of the model-catalog Remote. */
|
|
1521
|
+
const MODELS_ENDPOINT = "commandcode/models";
|
|
1522
|
+
/** Parse one untrusted boundary value into a {@link CommandCodeCatalogModel}. */
|
|
1523
|
+
function parseCatalogModel(value) {
|
|
1524
|
+
const source = record(value, "model");
|
|
1525
|
+
return {
|
|
1526
|
+
id: stringField(source, "id", "model.id"),
|
|
1527
|
+
name: stringField(source, "name", "model.name")
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
/** Parse the wire result into a {@link CommandCodeCatalog}. */
|
|
1531
|
+
function parseCatalog(value) {
|
|
1532
|
+
const models = record(value, "result").models;
|
|
1533
|
+
if (!Array.isArray(models)) reject$1("models");
|
|
1534
|
+
return { models: models.map(parseCatalogModel) };
|
|
1535
|
+
}
|
|
1536
|
+
/** The Client-face contribution for the model-catalog endpoint. */
|
|
1537
|
+
const MODELS_REMOTE_CONTRIBUTION = {
|
|
1538
|
+
package: USAGE_REMOTE_PACKAGE,
|
|
1539
|
+
descriptors: [{
|
|
1540
|
+
id: `${USAGE_REMOTE_PACKAGE}#${MODELS_ENDPOINT}`,
|
|
1541
|
+
service: "commandcodeUsage",
|
|
1542
|
+
namespace: "commandcode",
|
|
1543
|
+
method: "models",
|
|
1544
|
+
invocation: { kind: "direct" },
|
|
1545
|
+
parameters: [],
|
|
1546
|
+
result: {
|
|
1547
|
+
mode: "strict",
|
|
1548
|
+
typeSymbol: `${USAGE_REMOTE_PACKAGE}#CommandCodeCatalog`,
|
|
1549
|
+
schema: { parse: parseCatalog }
|
|
1550
|
+
}
|
|
1551
|
+
}]
|
|
1552
|
+
};
|
|
1324
1553
|
//#endregion
|
|
1325
1554
|
//#region src/login-wire.ts
|
|
1326
1555
|
/** The canonical endpoint paths of the three login Remotes. */
|
|
@@ -1402,7 +1631,7 @@ window.__ModuleLoader__.load({
|
|
|
1402
1631
|
};
|
|
1403
1632
|
//#endregion
|
|
1404
1633
|
//#region package.json
|
|
1405
|
-
var version = "0.10.0-alpha.
|
|
1634
|
+
var version = "0.10.0-alpha.2";
|
|
1406
1635
|
var repository = {
|
|
1407
1636
|
"type": "git",
|
|
1408
1637
|
"url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
|
|
@@ -2464,6 +2693,173 @@ window.__ModuleLoader__.load({
|
|
|
2464
2693
|
]
|
|
2465
2694
|
});
|
|
2466
2695
|
}
|
|
2696
|
+
/** One model → account routing rule row. */
|
|
2697
|
+
function RuleRow({ rule, accounts, catalog, disabled, t, onModels, onAccount, onRemove }) {
|
|
2698
|
+
const targets = [{
|
|
2699
|
+
value: "default",
|
|
2700
|
+
label: t("accountDefault")
|
|
2701
|
+
}, ...accounts.filter((account) => !account.added).map((account) => ({
|
|
2702
|
+
value: account.ref,
|
|
2703
|
+
label: account.label
|
|
2704
|
+
}))];
|
|
2705
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2706
|
+
className: "cc-field",
|
|
2707
|
+
children: [
|
|
2708
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2709
|
+
className: "cc-fieldHead",
|
|
2710
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2711
|
+
className: "cc-label",
|
|
2712
|
+
htmlFor: `cc-rule-model-${rule.id}`,
|
|
2713
|
+
children: t("ruleModel")
|
|
2714
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2715
|
+
className: "cc-badges",
|
|
2716
|
+
children: [rule.added ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2717
|
+
className: "cc-badge",
|
|
2718
|
+
children: t("unsaved")
|
|
2719
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2720
|
+
type: "button",
|
|
2721
|
+
className: "cc-reset",
|
|
2722
|
+
disabled,
|
|
2723
|
+
onClick: onRemove,
|
|
2724
|
+
children: t("ruleRemove")
|
|
2725
|
+
})]
|
|
2726
|
+
})]
|
|
2727
|
+
}),
|
|
2728
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMultiSelect, {
|
|
2729
|
+
id: `cc-rule-model-${rule.id}`,
|
|
2730
|
+
selected: rule.models,
|
|
2731
|
+
catalog,
|
|
2732
|
+
disabled,
|
|
2733
|
+
t,
|
|
2734
|
+
onSelect: onModels
|
|
2735
|
+
}),
|
|
2736
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
2737
|
+
id: `cc-rule-account-${rule.id}`,
|
|
2738
|
+
className: "cc-input",
|
|
2739
|
+
value: rule.account,
|
|
2740
|
+
disabled,
|
|
2741
|
+
onChange: (event) => onAccount(event.target.value),
|
|
2742
|
+
children: targets.map((target) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
2743
|
+
value: target.value,
|
|
2744
|
+
children: target.label
|
|
2745
|
+
}, target.value))
|
|
2746
|
+
}),
|
|
2747
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2748
|
+
className: "cc-hint",
|
|
2749
|
+
children: t("ruleHint")
|
|
2750
|
+
})
|
|
2751
|
+
]
|
|
2752
|
+
});
|
|
2753
|
+
}
|
|
2754
|
+
/**
|
|
2755
|
+
* A checkbox multi-select dropdown for routing-rule models. The trigger shows
|
|
2756
|
+
* the selection count; the anchored Menu lists every catalog model with a
|
|
2757
|
+
* checkbox, toggled by clicking the row. Selected ids the catalog no longer
|
|
2758
|
+
* carries still render so a saved rule never silently loses a selection.
|
|
2759
|
+
*/
|
|
2760
|
+
function ModelMultiSelect({ id, selected, catalog, disabled, t, onSelect }) {
|
|
2761
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
2762
|
+
const options = [...catalog.map((model) => ({
|
|
2763
|
+
value: model.id,
|
|
2764
|
+
label: model.name
|
|
2765
|
+
})), ...selected.filter((id) => !catalog.some((model) => model.id === id)).map((id) => ({
|
|
2766
|
+
value: id,
|
|
2767
|
+
label: id
|
|
2768
|
+
}))];
|
|
2769
|
+
const selectedSet = new Set(selected);
|
|
2770
|
+
const items = options.map((option) => ({
|
|
2771
|
+
id: option.value,
|
|
2772
|
+
label: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2773
|
+
className: "cc-checkRow",
|
|
2774
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2775
|
+
type: "checkbox",
|
|
2776
|
+
className: "cc-check",
|
|
2777
|
+
checked: selectedSet.has(option.value),
|
|
2778
|
+
readOnly: true,
|
|
2779
|
+
tabIndex: -1
|
|
2780
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2781
|
+
className: "cc-checkName",
|
|
2782
|
+
children: option.label
|
|
2783
|
+
})]
|
|
2784
|
+
})
|
|
2785
|
+
}));
|
|
2786
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
2787
|
+
open,
|
|
2788
|
+
onClose: () => setOpen(false),
|
|
2789
|
+
onSelect: (modelId) => {
|
|
2790
|
+
onSelect(selectedSet.has(modelId) ? selected.filter((value) => value !== modelId) : [...selected, modelId]);
|
|
2791
|
+
},
|
|
2792
|
+
selectedIds: selected,
|
|
2793
|
+
items,
|
|
2794
|
+
portal: true,
|
|
2795
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2796
|
+
id,
|
|
2797
|
+
type: "button",
|
|
2798
|
+
className: "cc-input cc-ruleTrigger",
|
|
2799
|
+
disabled: disabled || catalog.length === 0,
|
|
2800
|
+
onClick: () => setOpen((value) => !value),
|
|
2801
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2802
|
+
className: "cc-ruleTriggerText",
|
|
2803
|
+
children: selected.length === 0 ? t("ruleModelPick") : t("ruleModelCount", { count: selected.length })
|
|
2804
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2805
|
+
className: "cc-ruleCaret",
|
|
2806
|
+
"aria-hidden": "true"
|
|
2807
|
+
})]
|
|
2808
|
+
})
|
|
2809
|
+
});
|
|
2810
|
+
}
|
|
2811
|
+
/** The model → account routing card: rules in list order (first match wins). */
|
|
2812
|
+
function RulesCard({ t, state, disabled, onAdd, onRemove, onModels, onAccount }) {
|
|
2813
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2814
|
+
className: "cc-card",
|
|
2815
|
+
"aria-label": t("rulesTitle"),
|
|
2816
|
+
children: [
|
|
2817
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2818
|
+
className: "cc-field",
|
|
2819
|
+
children: [
|
|
2820
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2821
|
+
className: "cc-fieldHead",
|
|
2822
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2823
|
+
className: "cc-label",
|
|
2824
|
+
children: t("rulesTitle")
|
|
2825
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2826
|
+
className: "cc-badges",
|
|
2827
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2828
|
+
type: "button",
|
|
2829
|
+
className: "cc-reset",
|
|
2830
|
+
disabled,
|
|
2831
|
+
onClick: onAdd,
|
|
2832
|
+
children: t("ruleAdd")
|
|
2833
|
+
})
|
|
2834
|
+
})]
|
|
2835
|
+
}),
|
|
2836
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2837
|
+
className: "cc-hint",
|
|
2838
|
+
children: t("rulesHint")
|
|
2839
|
+
}),
|
|
2840
|
+
state.catalogFailed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2841
|
+
className: "cc-invalid",
|
|
2842
|
+
children: t("rulesCatalogFailed")
|
|
2843
|
+
}) : null
|
|
2844
|
+
]
|
|
2845
|
+
}),
|
|
2846
|
+
state.rules.map((rule) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RuleRow, {
|
|
2847
|
+
rule,
|
|
2848
|
+
accounts: state.accounts,
|
|
2849
|
+
catalog: state.catalogModels,
|
|
2850
|
+
disabled,
|
|
2851
|
+
t,
|
|
2852
|
+
onModels: (ids) => onModels(rule.id, ids),
|
|
2853
|
+
onAccount: (text) => onAccount(rule.id, text),
|
|
2854
|
+
onRemove: () => onRemove(rule.id)
|
|
2855
|
+
}, rule.id)),
|
|
2856
|
+
state.rules.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2857
|
+
className: "cc-hint",
|
|
2858
|
+
children: t("rulesEmpty")
|
|
2859
|
+
}) : null
|
|
2860
|
+
]
|
|
2861
|
+
});
|
|
2862
|
+
}
|
|
2467
2863
|
/**
|
|
2468
2864
|
* Show the "Saved ✓" affordance for a short window after each accepted save.
|
|
2469
2865
|
* The controller only counts saves (`savedCount`); the flash timing lives
|
|
@@ -2551,6 +2947,15 @@ window.__ModuleLoader__.load({
|
|
|
2551
2947
|
onActive: (text) => props.edit("activeAccount", text),
|
|
2552
2948
|
onActiveReset: () => props.resetField("activeAccount")
|
|
2553
2949
|
}),
|
|
2950
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RulesCard, {
|
|
2951
|
+
t,
|
|
2952
|
+
state,
|
|
2953
|
+
disabled,
|
|
2954
|
+
onAdd: props.addRule,
|
|
2955
|
+
onRemove: props.removeRule,
|
|
2956
|
+
onModels: props.editRuleModels,
|
|
2957
|
+
onAccount: props.editRuleAccount
|
|
2958
|
+
}),
|
|
2554
2959
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2555
2960
|
className: "cc-card",
|
|
2556
2961
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SecretKeyField, {
|
|
@@ -2636,35 +3041,23 @@ window.__ModuleLoader__.load({
|
|
|
2636
3041
|
]
|
|
2637
3042
|
});
|
|
2638
3043
|
}
|
|
2639
|
-
//#endregion
|
|
2640
|
-
//#region src/client/card.tsx
|
|
2641
3044
|
/**
|
|
2642
|
-
*
|
|
2643
|
-
*
|
|
2644
|
-
*
|
|
2645
|
-
*
|
|
2646
|
-
*
|
|
2647
|
-
*
|
|
2648
|
-
*
|
|
2649
|
-
*
|
|
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.
|
|
3045
|
+
* Find the official editor card among the slot outlet's siblings, or null
|
|
3046
|
+
* while it is closed. The Models page renders the editor as an immediate
|
|
3047
|
+
* sibling of the outlet wrapper — after it in a provider row (the target of
|
|
3048
|
+
* the row's 编辑 toggle), before it in the first-run setup card and the
|
|
3049
|
+
* add-provider card, where it is always open. The editor is the only such
|
|
3050
|
+
* sibling whose CSS module class carries the `editor` stem
|
|
3051
|
+
* (`<hash>_editor`); the row header and the add card's provider select
|
|
3052
|
+
* never do, so the lookup needs no hash knowledge.
|
|
2667
3053
|
*/
|
|
3054
|
+
function adjacentEditorCard(wrapper) {
|
|
3055
|
+
if (wrapper === null) return null;
|
|
3056
|
+
for (const sibling of [wrapper.previousElementSibling, wrapper.nextElementSibling]) if (sibling !== null && typeof sibling.className === "string" && sibling.className.includes("editor")) return sibling;
|
|
3057
|
+
return null;
|
|
3058
|
+
}
|
|
3059
|
+
/** The closed-panel style: the outlet stays mounted as the detection anchor. */
|
|
3060
|
+
const HIDDEN_STYLE = { display: "none" };
|
|
2668
3061
|
/** Decide the card's posture from the injected face and the owner facts. */
|
|
2669
3062
|
function cardMode(props) {
|
|
2670
3063
|
if (props.useCommandCodeSettings === void 0) return { kind: "registration" };
|
|
@@ -2795,20 +3188,18 @@ window.__ModuleLoader__.load({
|
|
|
2795
3188
|
]
|
|
2796
3189
|
});
|
|
2797
3190
|
}
|
|
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
3191
|
/**
|
|
2809
3192
|
* The slot component body. Dispatched on every Command Code provider card of
|
|
2810
3193
|
* the Models page (saved row, first-run setup posture, and add-provider
|
|
2811
3194
|
* draft).
|
|
3195
|
+
*
|
|
3196
|
+
* Closed (the official 编辑 toggle off) the panel renders nothing: the row
|
|
3197
|
+
* head the Models page owns already names the provider and shows the
|
|
3198
|
+
* credential dot, so a page full of providers stays compact. Opening the
|
|
3199
|
+
* official editor mounts the editor shell as the outlet's sibling; the panel
|
|
3200
|
+
* watches for it, hides the shell (it carries only the settings.yaml hint and
|
|
3201
|
+
* a disabled apply for this namespace), and shows the real controls — badges,
|
|
3202
|
+
* API-key field, sign-in, discard/save.
|
|
2812
3203
|
*/
|
|
2813
3204
|
function CommandCodeProviderCard(props) {
|
|
2814
3205
|
const { t } = props;
|
|
@@ -2823,14 +3214,50 @@ window.__ModuleLoader__.load({
|
|
|
2823
3214
|
const configured = mode.kind === "live" && mode.ready ? mode.controllerConfigured : props.keyConfigured;
|
|
2824
3215
|
const disabled = mode.kind === "live" && (!mode.writable || state !== void 0 && !mode.apiKeyWritable);
|
|
2825
3216
|
const showBody = mode.kind === "live" && mode.ready && state !== void 0;
|
|
3217
|
+
const rootRef = (0, react.useRef)(null);
|
|
3218
|
+
const [editorOpen, setEditorOpen] = (0, react.useState)(false);
|
|
3219
|
+
(0, react.useEffect)(() => {
|
|
3220
|
+
const root = rootRef.current;
|
|
3221
|
+
if (root === null || typeof MutationObserver === "undefined") return;
|
|
3222
|
+
const wrapper = root.closest(`[data-slot="settings.models.provider-card"]`) ?? root.parentElement;
|
|
3223
|
+
if (wrapper === null) return;
|
|
3224
|
+
const row = wrapper.parentElement;
|
|
3225
|
+
if (row === null) return;
|
|
3226
|
+
let hiddenEditor = null;
|
|
3227
|
+
const sync = () => {
|
|
3228
|
+
const editor = adjacentEditorCard(wrapper);
|
|
3229
|
+
setEditorOpen(editor !== null);
|
|
3230
|
+
if (editor !== null) {
|
|
3231
|
+
editor.style.display = "none";
|
|
3232
|
+
hiddenEditor = editor;
|
|
3233
|
+
}
|
|
3234
|
+
};
|
|
3235
|
+
sync();
|
|
3236
|
+
const observer = new MutationObserver(sync);
|
|
3237
|
+
observer.observe(row, { childList: true });
|
|
3238
|
+
return () => {
|
|
3239
|
+
observer.disconnect();
|
|
3240
|
+
if (hiddenEditor !== null) hiddenEditor.style.display = "";
|
|
3241
|
+
};
|
|
3242
|
+
}, []);
|
|
2826
3243
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3244
|
+
ref: rootRef,
|
|
2827
3245
|
className: "cc-providerCard",
|
|
2828
3246
|
"data-cc-models-card": "true",
|
|
3247
|
+
style: editorOpen ? void 0 : HIDDEN_STYLE,
|
|
2829
3248
|
children: [
|
|
2830
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2831
|
-
className: "cc-
|
|
2832
|
-
children:
|
|
2833
|
-
|
|
3249
|
+
editorOpen && mode.kind === "registration" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3250
|
+
className: "cc-hint",
|
|
3251
|
+
children: t("cardRegistrationHint")
|
|
3252
|
+
}) : null,
|
|
3253
|
+
editorOpen && mode.kind === "live" && !mode.ready ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3254
|
+
className: "cc-hint",
|
|
3255
|
+
children: t("cardLoadingHint")
|
|
3256
|
+
}) : null,
|
|
3257
|
+
editorOpen && showBody ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3258
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3259
|
+
className: "cc-field",
|
|
3260
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2834
3261
|
className: "cc-fieldHead",
|
|
2835
3262
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2836
3263
|
className: "cc-label",
|
|
@@ -2846,18 +3273,8 @@ window.__ModuleLoader__.load({
|
|
|
2846
3273
|
children: t("cardRouteActive")
|
|
2847
3274
|
}) : null]
|
|
2848
3275
|
})]
|
|
2849
|
-
})
|
|
2850
|
-
|
|
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: [
|
|
3276
|
+
})
|
|
3277
|
+
}),
|
|
2861
3278
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardKeyField, {
|
|
2862
3279
|
state: state.apiKey,
|
|
2863
3280
|
disabled,
|
|
@@ -2873,20 +3290,29 @@ window.__ModuleLoader__.load({
|
|
|
2873
3290
|
}) : null,
|
|
2874
3291
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2875
3292
|
className: "cc-footer",
|
|
2876
|
-
children: [
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
3293
|
+
children: [
|
|
3294
|
+
failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3295
|
+
className: "cc-failed",
|
|
3296
|
+
role: "status",
|
|
3297
|
+
children: t("saveFailed")
|
|
3298
|
+
}) : null,
|
|
3299
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3300
|
+
type: "button",
|
|
3301
|
+
className: "cc-reset",
|
|
3302
|
+
disabled: !dirty || saving,
|
|
3303
|
+
onClick: props.discard,
|
|
3304
|
+
children: t("discard")
|
|
3305
|
+
}),
|
|
3306
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3307
|
+
type: "button",
|
|
3308
|
+
className: "cc-reset",
|
|
3309
|
+
disabled: savingBlocked || saving,
|
|
3310
|
+
onClick: props.save,
|
|
3311
|
+
children: t(saving ? "saving" : "save")
|
|
3312
|
+
})
|
|
3313
|
+
]
|
|
2887
3314
|
})
|
|
2888
|
-
] }) : null
|
|
2889
|
-
showBody && configured ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardConfiguredBody, { t }) : null
|
|
3315
|
+
] }) : null
|
|
2890
3316
|
]
|
|
2891
3317
|
});
|
|
2892
3318
|
}
|
|
@@ -2927,6 +3353,17 @@ window.__ModuleLoader__.load({
|
|
|
2927
3353
|
activeAccount: "当前使用账户",
|
|
2928
3354
|
activeAccountAuto: "自动(第一个可用账户)",
|
|
2929
3355
|
activeAccountHint: "手动指定优先使用的账户,保存后下次请求即生效;所选账户耗尽时仍会自动切换到其他可用账户。",
|
|
3356
|
+
rulesTitle: "按模型切换账户",
|
|
3357
|
+
rulesHint: "选择模型并路由到某个账户(可多选)。命中规则的模型且该账户可用时优先使用;账户耗尽或密钥失效时仍自动回落到其他账户。规则按列表顺序匹配,第一条命中生效。",
|
|
3358
|
+
rulesEmpty: "尚未配置规则。",
|
|
3359
|
+
rulesCatalogFailed: "模型目录获取失败,暂时无法选择模型;已保存的规则仍会生效。",
|
|
3360
|
+
ruleAdd: "添加规则",
|
|
3361
|
+
ruleRemove: "移除",
|
|
3362
|
+
ruleModel: "模型",
|
|
3363
|
+
ruleModelPick: "选择模型…",
|
|
3364
|
+
ruleModelCount: "已选 {count} 个模型",
|
|
3365
|
+
ruleAccount: "目标账户",
|
|
3366
|
+
ruleHint: "从下拉列表勾选要路由的模型(可多选),再选择目标账户。",
|
|
2930
3367
|
overridden: "已覆盖",
|
|
2931
3368
|
reset: "重置",
|
|
2932
3369
|
invalidNumber: "无效数字",
|
|
@@ -2996,11 +3433,10 @@ window.__ModuleLoader__.load({
|
|
|
2996
3433
|
loginStoreFailed: "密钥无法写入本机凭据服务,请手动粘贴。",
|
|
2997
3434
|
loginCancelled: "登录已取消。",
|
|
2998
3435
|
loginFailedGeneric: "登录失败,请重试或手动粘贴密钥。",
|
|
2999
|
-
cardTitle: "Command Code
|
|
3436
|
+
cardTitle: "Command Code",
|
|
3000
3437
|
cardRouteActive: "已启用",
|
|
3001
3438
|
cardLoadingHint: "正在读取 Command Code 配置…",
|
|
3002
|
-
cardRegistrationHint: "此卡片随 Command Code 插件注册,需要较新版本的 DeepSeek Harness 才会显示完整内容。"
|
|
3003
|
-
cardConfiguredHint: "API 密钥已就绪。如需更换密钥、添加多账户轮换、查看用量或修改 API 地址,请前往设置中的「Command Code」页。"
|
|
3439
|
+
cardRegistrationHint: "此卡片随 Command Code 插件注册,需要较新版本的 DeepSeek Harness 才会显示完整内容。"
|
|
3004
3440
|
};
|
|
3005
3441
|
const en = {
|
|
3006
3442
|
nav: "Command Code",
|
|
@@ -3037,6 +3473,17 @@ window.__ModuleLoader__.load({
|
|
|
3037
3473
|
activeAccount: "Active account",
|
|
3038
3474
|
activeAccountAuto: "Auto (first usable account)",
|
|
3039
3475
|
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.",
|
|
3476
|
+
rulesTitle: "Route models to accounts",
|
|
3477
|
+
rulesHint: "Pick models (multi-select) and route them to an account. When the request’s model is in a rule and that account is usable, it serves; an exhausted or invalid routed account falls back to the normal rotation. Rules match in list order — the first hit wins.",
|
|
3478
|
+
rulesEmpty: "No rules yet.",
|
|
3479
|
+
rulesCatalogFailed: "Could not load the model catalog — selecting models is unavailable; saved rules still apply.",
|
|
3480
|
+
ruleAdd: "Add rule",
|
|
3481
|
+
ruleRemove: "Remove",
|
|
3482
|
+
ruleModel: "Models",
|
|
3483
|
+
ruleModelPick: "Select models…",
|
|
3484
|
+
ruleModelCount: "{count} model(s) selected",
|
|
3485
|
+
ruleAccount: "Target account",
|
|
3486
|
+
ruleHint: "Check the models to route from the dropdown (multi-select), then pick the target account.",
|
|
3040
3487
|
overridden: "Overridden",
|
|
3041
3488
|
reset: "Reset",
|
|
3042
3489
|
invalidNumber: "Invalid number",
|
|
@@ -3106,11 +3553,10 @@ window.__ModuleLoader__.load({
|
|
|
3106
3553
|
loginStoreFailed: "The key could not be stored in the local credential service; paste it manually.",
|
|
3107
3554
|
loginCancelled: "Sign-in cancelled.",
|
|
3108
3555
|
loginFailedGeneric: "Sign-in failed; try again or paste the key manually.",
|
|
3109
|
-
cardTitle: "Command Code
|
|
3556
|
+
cardTitle: "Command Code",
|
|
3110
3557
|
cardRouteActive: "Active",
|
|
3111
3558
|
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."
|
|
3559
|
+
cardRegistrationHint: "This card is contributed by the Command Code plugin; a newer DeepSeek Harness is needed to show the full controls."
|
|
3114
3560
|
};
|
|
3115
3561
|
//#endregion
|
|
3116
3562
|
//#region src/client/index.ts
|
|
@@ -3134,6 +3580,19 @@ window.__ModuleLoader__.load({
|
|
|
3134
3580
|
.cc-input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}
|
|
3135
3581
|
.cc-input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}
|
|
3136
3582
|
.cc-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
|
|
3583
|
+
/* The routing-rule model multi-select: a button trigger that opens an
|
|
3584
|
+
* anchored Menu of checkbox rows. The trigger mirrors .cc-input sizing so it
|
|
3585
|
+
* sits flush with the sibling account select. */
|
|
3586
|
+
.cc-ruleTrigger{align-items:center;gap:8px;display:flex;width:100%;text-align:left;cursor:pointer}
|
|
3587
|
+
.cc-ruleTrigger:disabled{cursor:default}
|
|
3588
|
+
.cc-ruleTriggerText{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3589
|
+
.cc-ruleCaret{flex-shrink:0;border-right:1.5px solid var(--dsw-alias-label-tertiary);border-bottom:1.5px solid var(--dsw-alias-label-tertiary);width:6px;height:6px;margin-right:4px;margin-bottom:2px;transform:rotate(45deg)}
|
|
3590
|
+
.cc-checkRow{align-items:center;gap:8px;display:inline-flex;min-width:0}
|
|
3591
|
+
.cc-checkRow:hover{cursor:pointer}
|
|
3592
|
+
.cc-check{appearance:none;flex-shrink:0;width:15px;height:15px;margin:0;border:1px solid var(--dsw-alias-border-l2);border-radius:4px;background:var(--dsw-alias-bg-layer-1);position:relative}
|
|
3593
|
+
.cc-check:checked{background:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}
|
|
3594
|
+
.cc-check:checked::after{content:'';position:absolute;top:2px;left:5px;width:3px;height:7px;border:solid #fff;border-width:0 1.5px 1.5px 0;transform:rotate(45deg)}
|
|
3595
|
+
.cc-checkName{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3137
3596
|
/* Selects need their own treatment to sit flush with the text inputs:
|
|
3138
3597
|
* the UA stylesheet renders <select> border-box (34px total vs the inputs'
|
|
3139
3598
|
* 36px) and forces its own menulist text metrics, so drop the native
|
|
@@ -3256,7 +3715,15 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
3256
3715
|
}
|
|
3257
3716
|
/** Mount the one shared UI implementation over either credential transport. */
|
|
3258
3717
|
function applyClientSurfaces(ctx, api, hostDescription) {
|
|
3259
|
-
const
|
|
3718
|
+
const scope = ctx.settingsScope.bind({ namespace: COMMANDCODE_NS });
|
|
3719
|
+
let modelsRemote;
|
|
3720
|
+
const controller = new CommandCodeSettingsController(scope, {
|
|
3721
|
+
...api,
|
|
3722
|
+
models: () => modelsRemote?.() ?? Promise.resolve({
|
|
3723
|
+
ok: false,
|
|
3724
|
+
error: { message: "commandcode/models remote is not mounted" }
|
|
3725
|
+
})
|
|
3726
|
+
}, hostDescription);
|
|
3260
3727
|
ctx.effect(() => () => controller.dispose(), "dsh-commandcode-provider: settings controller");
|
|
3261
3728
|
const store = createSnapshotStore(controller.state());
|
|
3262
3729
|
controller.subscribe(() => store.set(controller.state()));
|
|
@@ -3267,7 +3734,11 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
3267
3734
|
let usageMountError;
|
|
3268
3735
|
const contribution = {
|
|
3269
3736
|
package: USAGE_REMOTE_CONTRIBUTION.package,
|
|
3270
|
-
descriptors: [
|
|
3737
|
+
descriptors: [
|
|
3738
|
+
...USAGE_REMOTE_CONTRIBUTION.descriptors,
|
|
3739
|
+
...MODELS_REMOTE_CONTRIBUTION.descriptors,
|
|
3740
|
+
...LOGIN_REMOTE_CONTRIBUTION.descriptors
|
|
3741
|
+
]
|
|
3271
3742
|
};
|
|
3272
3743
|
ctx.effect(() => {
|
|
3273
3744
|
let cancelled = false;
|
|
@@ -3280,6 +3751,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
3280
3751
|
unmount = dispose;
|
|
3281
3752
|
ctx.inject(["remote.commandcode"], (namespaceCtx) => {
|
|
3282
3753
|
usageNamespace = namespaceCtx.remote.commandcode;
|
|
3754
|
+
controller.refreshCatalog();
|
|
3283
3755
|
namespaceCtx.effect(() => () => {
|
|
3284
3756
|
usageNamespace = void 0;
|
|
3285
3757
|
}, "dsh-commandcode-provider: usage namespace");
|
|
@@ -3293,14 +3765,26 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
3293
3765
|
if (unmount !== void 0) unmount();
|
|
3294
3766
|
};
|
|
3295
3767
|
}, "dsh-commandcode-provider: usage remote");
|
|
3296
|
-
const
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3768
|
+
const usageRemote = {
|
|
3769
|
+
report: async () => {
|
|
3770
|
+
const namespace = usageNamespace;
|
|
3771
|
+
if (namespace === void 0) return {
|
|
3772
|
+
ok: false,
|
|
3773
|
+
error: { message: usageMountError ?? "commandcode/report remote is not mounted" }
|
|
3774
|
+
};
|
|
3775
|
+
return namespace.report();
|
|
3776
|
+
},
|
|
3777
|
+
models: async () => {
|
|
3778
|
+
const namespace = usageNamespace;
|
|
3779
|
+
if (namespace === void 0) return {
|
|
3780
|
+
ok: false,
|
|
3781
|
+
error: { message: usageMountError ?? "commandcode/models remote is not mounted" }
|
|
3782
|
+
};
|
|
3783
|
+
return namespace.models();
|
|
3784
|
+
}
|
|
3785
|
+
};
|
|
3786
|
+
modelsRemote = () => usageRemote.models();
|
|
3787
|
+
const usageController = new CommandCodeUsageController(usageRemote);
|
|
3304
3788
|
ctx.effect(() => () => usageController.dispose(), "dsh-commandcode-provider: usage controller");
|
|
3305
3789
|
const usageStore = createSnapshotStore(usageController.state());
|
|
3306
3790
|
usageController.subscribe(() => usageStore.set(usageController.state()));
|
|
@@ -3360,7 +3844,11 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
3360
3844
|
removeAccount: (id) => controller.removeAccount(id),
|
|
3361
3845
|
editAccountLabel: (id, text) => controller.editAccountLabel(id, text),
|
|
3362
3846
|
editAccountKey: (id, text) => controller.editAccountKey(id, text),
|
|
3363
|
-
toggleKeyClear: (id) => controller.toggleKeyClear(id)
|
|
3847
|
+
toggleKeyClear: (id) => controller.toggleKeyClear(id),
|
|
3848
|
+
addRule: () => controller.addRule(),
|
|
3849
|
+
removeRule: (id) => controller.removeRule(id),
|
|
3850
|
+
editRuleModels: (id, ids) => controller.editRuleModels(id, ids),
|
|
3851
|
+
editRuleAccount: (id, text) => controller.editRuleAccount(id, text)
|
|
3364
3852
|
});
|
|
3365
3853
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
3366
3854
|
name: "settings.section",
|
|
@@ -3384,6 +3872,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
3384
3872
|
const settled = controller.state();
|
|
3385
3873
|
if (!settled.failed && settled.anyAccountConfigured) usageController.refresh();
|
|
3386
3874
|
}),
|
|
3875
|
+
discard: () => controller.discard(),
|
|
3387
3876
|
beginLogin: () => void loginController.begin(),
|
|
3388
3877
|
cancelLogin: () => void loginController.cancel()
|
|
3389
3878
|
})
|