dsh-workbuddy-connect 0.5.3 → 0.6.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/README.en.md +41 -5
- package/README.md +40 -5
- package/lib/bin.js +13 -6
- package/lib/client.js +307 -94
- package/lib/index.d.ts +314 -48
- package/lib/index.js +372 -102
- package/lib/{variants-CdmzOPIT.js → variants-CnrmSn0Q.js} +584 -101
- package/package.json +23 -23
package/lib/client.js
CHANGED
|
@@ -53,7 +53,13 @@ window.__ModuleLoader__.load({
|
|
|
53
53
|
}
|
|
54
54
|
//#endregion
|
|
55
55
|
//#region src/client/WorkBuddyPluginCard.tsx
|
|
56
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* WorkBuddy status card, rendered on whichever settings surface the host
|
|
58
|
+
* provides: dispatched directly by DSH 0.1.5's settings Plugins tab (one card
|
|
59
|
+
* per variant), or mounted by the bundle configuration page DSH 0.1.6+'s
|
|
60
|
+
* Plugins page renders. The component itself is surface-agnostic — its props
|
|
61
|
+
* are only the injected copy and variant.
|
|
62
|
+
*/
|
|
57
63
|
/** CN WorkBuddy; the plugin's long-standing card and default. */
|
|
58
64
|
const CN_CARD_VARIANT = {
|
|
59
65
|
id: "workbuddy",
|
|
@@ -79,7 +85,8 @@ window.__ModuleLoader__.load({
|
|
|
79
85
|
overflow: "hidden",
|
|
80
86
|
border: "1px solid var(--dsw-alias-border-l2)",
|
|
81
87
|
borderRadius: 10,
|
|
82
|
-
background: "var(--dsw-alias-bg-module-platform)"
|
|
88
|
+
background: "var(--dsw-alias-bg-module-platform)",
|
|
89
|
+
listStyle: "none"
|
|
83
90
|
};
|
|
84
91
|
const headerStyle = {
|
|
85
92
|
boxSizing: "border-box",
|
|
@@ -217,6 +224,17 @@ window.__ModuleLoader__.load({
|
|
|
217
224
|
flexDirection: "column",
|
|
218
225
|
gap: 2
|
|
219
226
|
};
|
|
227
|
+
/**
|
|
228
|
+
* Left half of one merged context/visibility row: the visibility checkbox (when
|
|
229
|
+
* the account has one) beside the model's name and rate. Kept as a flex span so
|
|
230
|
+
* the capacity column stays flush right no matter how long the name runs.
|
|
231
|
+
*/
|
|
232
|
+
const contextRowMainStyle = {
|
|
233
|
+
display: "flex",
|
|
234
|
+
alignItems: "center",
|
|
235
|
+
gap: 9,
|
|
236
|
+
minWidth: 0
|
|
237
|
+
};
|
|
220
238
|
const modelBadgeChipStyle = {
|
|
221
239
|
padding: "1px 8px",
|
|
222
240
|
borderRadius: 999,
|
|
@@ -455,26 +473,41 @@ window.__ModuleLoader__.load({
|
|
|
455
473
|
});
|
|
456
474
|
}
|
|
457
475
|
/**
|
|
458
|
-
* Context
|
|
476
|
+
* Context window and model visibility, one row per catalog model.
|
|
477
|
+
*
|
|
478
|
+
* The list is driven by the full current catalog, not by context metadata:
|
|
479
|
+
* hiding a model is a statement about the picker, and a model without a
|
|
480
|
+
* declared window is still hideable — its row just shows an em dash where the
|
|
481
|
+
* capacity would be. Rows with a window keep the original ordering (largest
|
|
482
|
+
* first); rows without one trail at the end in catalog order.
|
|
459
483
|
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
462
|
-
*
|
|
463
|
-
*
|
|
484
|
+
* Each row is one <label>, so the checkbox is named by its row without a
|
|
485
|
+
* duplicated aria string. The checkbox state comes from the status document
|
|
486
|
+
* only — no optimistic flip — so a save that fails leaves the box where the
|
|
487
|
+
* host's truth says it is, next to the failure notice `control` records.
|
|
488
|
+
* Checkboxes render only when the document carries a visibility section (a
|
|
489
|
+
* signed-in account with a stable user id); a uid-less credential shows the
|
|
490
|
+
* plain capacity list rather than editing a bucket every such account would
|
|
491
|
+
* share.
|
|
464
492
|
*
|
|
465
|
-
* Purely a report of the upstream's own numbers. The plugin offers
|
|
466
|
-
* picker: the CN catalog declares one capacity per model and publishes
|
|
467
|
-
* alternatives, so a menu there would mean inventing client-side policy.
|
|
468
|
-
* international document does declare alternatives (`supportedLengths`),
|
|
469
|
-
* they are shown as a secondary figure rather than merged into one number —
|
|
493
|
+
* Purely a report of the upstream's own numbers otherwise. The plugin offers
|
|
494
|
+
* no tier picker: the CN catalog declares one capacity per model and publishes
|
|
495
|
+
* no alternatives, so a menu there would mean inventing client-side policy.
|
|
496
|
+
* The international document does declare alternatives (`supportedLengths`),
|
|
497
|
+
* and they are shown as a secondary figure rather than merged into one number —
|
|
470
498
|
* the default is the budget actually requested, while the larger value is a
|
|
471
499
|
* ceiling the upstream would accept.
|
|
472
500
|
*/
|
|
473
|
-
function ContextTable({ models, t, useMaximumContextWindow,
|
|
474
|
-
const
|
|
475
|
-
|
|
501
|
+
function ContextTable({ models, t, useMaximumContextWindow, contextPreferenceDisabled, onUseMaximumContextWindow, visibility, visibilityControlsDisabled, visibilityToggling, onVisibilityToggle }) {
|
|
502
|
+
const rows = [...models ?? []].sort((a, b) => {
|
|
503
|
+
if (a.contextWindow === void 0) return b.contextWindow === void 0 ? 0 : 1;
|
|
504
|
+
if (b.contextWindow === void 0) return -1;
|
|
505
|
+
return b.contextWindow - a.contextWindow;
|
|
506
|
+
});
|
|
507
|
+
const canSelectMaximum = rows.some((model) => model.maxContextWindow !== void 0 && model.maxContextWindow > (model.defaultContextWindow ?? model.contextWindow ?? 0));
|
|
476
508
|
const showPreference = onUseMaximumContextWindow !== void 0 && (canSelectMaximum || useMaximumContextWindow === true);
|
|
477
|
-
if (
|
|
509
|
+
if (rows.length === 0 && !showPreference) return null;
|
|
510
|
+
const hidden = new Set(visibility?.disabled ?? []);
|
|
478
511
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
479
512
|
style: quotaListStyle,
|
|
480
513
|
children: [
|
|
@@ -487,7 +520,7 @@ window.__ModuleLoader__.load({
|
|
|
487
520
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
488
521
|
type: "checkbox",
|
|
489
522
|
checked: useMaximumContextWindow === true,
|
|
490
|
-
disabled,
|
|
523
|
+
disabled: contextPreferenceDisabled,
|
|
491
524
|
onChange: (event) => {
|
|
492
525
|
onUseMaximumContextWindow(event.currentTarget.checked);
|
|
493
526
|
}
|
|
@@ -499,25 +532,65 @@ window.__ModuleLoader__.load({
|
|
|
499
532
|
})]
|
|
500
533
|
})]
|
|
501
534
|
}) : null,
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
535
|
+
visibility === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
536
|
+
style: bodyStyle,
|
|
537
|
+
children: t("visibilityIntro")
|
|
538
|
+
}),
|
|
539
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
540
|
+
style: quotaGroupStyle,
|
|
541
|
+
children: rows.map((model) => {
|
|
542
|
+
const capacity = model.contextWindow;
|
|
543
|
+
const alternative = capacity !== void 0 && model.maxContextWindow !== void 0 && model.maxContextWindow > capacity ? model.maxContextWindow : void 0;
|
|
544
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
545
|
+
style: quotaLabelStyle,
|
|
546
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
547
|
+
style: contextRowMainStyle,
|
|
548
|
+
children: [visibility === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
549
|
+
type: "checkbox",
|
|
550
|
+
checked: !hidden.has(model.id),
|
|
551
|
+
disabled: visibilityControlsDisabled || visibilityToggling.has(model.id),
|
|
552
|
+
onChange: (event) => {
|
|
553
|
+
onVisibilityToggle?.(model.id, event.currentTarget.checked);
|
|
554
|
+
}
|
|
555
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
556
|
+
style: modelOfferStyle,
|
|
557
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
558
|
+
style: modelBadgeStyle,
|
|
559
|
+
children: [
|
|
560
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name }),
|
|
561
|
+
model.badges?.map((badge) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
562
|
+
style: modelBadgeChipStyle,
|
|
563
|
+
children: modelBadgeLabel(badge, t)
|
|
564
|
+
}, badge)),
|
|
565
|
+
model.free === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
566
|
+
style: modelBadgeChipStyle,
|
|
567
|
+
children: t("freeModel")
|
|
568
|
+
}) : null
|
|
569
|
+
]
|
|
570
|
+
}), model.credits === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
571
|
+
style: modelRateStyle,
|
|
572
|
+
children: model.credits
|
|
573
|
+
})]
|
|
574
|
+
})]
|
|
575
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
576
|
+
style: modelOfferStyle,
|
|
577
|
+
children: [capacity === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
578
|
+
style: modelRateStyle,
|
|
579
|
+
"aria-label": t("contextUnknown"),
|
|
580
|
+
children: "—"
|
|
581
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
582
|
+
style: { textAlign: "right" },
|
|
583
|
+
children: formatTokens(capacity)
|
|
584
|
+
}), alternative !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
585
|
+
style: modelRateStyle,
|
|
586
|
+
children: t("contextUpTo", { size: formatTokens(alternative) })
|
|
587
|
+
}) : capacity !== void 0 && model.defaultContextWindow !== void 0 && model.defaultContextWindow < capacity ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
588
|
+
style: modelRateStyle,
|
|
589
|
+
children: t("contextDefault", { size: formatTokens(model.defaultContextWindow) })
|
|
590
|
+
}) : null]
|
|
591
|
+
})]
|
|
592
|
+
}, model.id);
|
|
593
|
+
})
|
|
521
594
|
})
|
|
522
595
|
]
|
|
523
596
|
});
|
|
@@ -543,7 +616,7 @@ window.__ModuleLoader__.load({
|
|
|
543
616
|
* parameter ("this model does not check it"), never as a statement that a
|
|
544
617
|
* level is unsupported.
|
|
545
618
|
*/
|
|
546
|
-
function ProbeSection({ probe, t, onDetect, onClear, busy }) {
|
|
619
|
+
function ProbeSection({ probe, models, t, onDetect, onClear, busy }) {
|
|
547
620
|
const [pending, setPending] = (0, react.useState)();
|
|
548
621
|
const [runningModel, setRunningModel] = (0, react.useState)();
|
|
549
622
|
(0, react.useEffect)(() => {
|
|
@@ -590,7 +663,7 @@ window.__ModuleLoader__.load({
|
|
|
590
663
|
style: quotaGroupStyle,
|
|
591
664
|
children: probe.candidates.map((id) => {
|
|
592
665
|
const result = probe.results.find((entry) => entry.id === id);
|
|
593
|
-
const name = result?.name ?? id;
|
|
666
|
+
const name = models?.find((model) => model.id === id)?.name ?? result?.name ?? id;
|
|
594
667
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
595
668
|
style: modelOfferStyle,
|
|
596
669
|
children: [
|
|
@@ -608,7 +681,7 @@ window.__ModuleLoader__.load({
|
|
|
608
681
|
onClick: () => {
|
|
609
682
|
setPending(id);
|
|
610
683
|
},
|
|
611
|
-
children: runningModel === id ? t("probeRunning", { model:
|
|
684
|
+
children: runningModel === id ? t("probeRunning", { model: name }) : t(result === void 0 ? "probeStart" : "probeRedetect")
|
|
612
685
|
})]
|
|
613
686
|
})]
|
|
614
687
|
}),
|
|
@@ -688,6 +761,17 @@ window.__ModuleLoader__.load({
|
|
|
688
761
|
*/
|
|
689
762
|
const [readFailure, setReadFailure] = (0, react.useState)();
|
|
690
763
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
764
|
+
/**
|
|
765
|
+
* The model ids whose visibility writes are in flight, empty when none are.
|
|
766
|
+
* Deliberately NOT the card-wide `busy` (that flag drives the Refresh
|
|
767
|
+
* buttons' labels, which must not claim a refresh the user never pressed)
|
|
768
|
+
* and deliberately per-row rather than whole-list: a visibility write only
|
|
769
|
+
* adds or removes one model's id, so the rows are independent — locking
|
|
770
|
+
* every checkbox for one row's write made the whole list visibly blink for
|
|
771
|
+
* no correctness gain. A set, because two writes can be open at once when
|
|
772
|
+
* the user moves down the list; only the rows being written lock.
|
|
773
|
+
*/
|
|
774
|
+
const [togglingModels, setTogglingModels] = (0, react.useState)(() => /* @__PURE__ */ new Set());
|
|
691
775
|
const [tab, setTab] = (0, react.useState)("status");
|
|
692
776
|
const mounted = (0, react.useRef)(true);
|
|
693
777
|
/**
|
|
@@ -843,14 +927,16 @@ window.__ModuleLoader__.load({
|
|
|
843
927
|
const control = (0, react.useCallback)(async (action) => {
|
|
844
928
|
const key = status?.status === "signed-in" ? status.probeKey : void 0;
|
|
845
929
|
if (key === void 0) return;
|
|
846
|
-
|
|
930
|
+
const visibility = action.action === "set-model-visibility";
|
|
931
|
+
if (visibility) setTogglingModels((previous) => new Set(previous).add(action.model));
|
|
932
|
+
else setBusy(true);
|
|
847
933
|
const controller = trackController();
|
|
848
934
|
try {
|
|
849
935
|
const response = await fetch(variant.probePath, {
|
|
850
936
|
method: "POST",
|
|
851
937
|
headers: {
|
|
852
938
|
"Content-Type": "application/json",
|
|
853
|
-
"X-
|
|
939
|
+
"X-Workbuddy-Probe-Key": key
|
|
854
940
|
},
|
|
855
941
|
credentials: "same-origin",
|
|
856
942
|
signal: controller.signal,
|
|
@@ -861,7 +947,12 @@ window.__ModuleLoader__.load({
|
|
|
861
947
|
const message = typeof value === "object" && value !== null && "error" in value ? String(value["error"]) : `HTTP ${response.status}`;
|
|
862
948
|
throw new Error(message);
|
|
863
949
|
}
|
|
864
|
-
if (action.action === "set-maximum-context-window" && (typeof value !== "object" || value === null || value["state"] !== "updated")) {
|
|
950
|
+
if ((action.action === "set-maximum-context-window" || action.action === "set-model-visibility") && (typeof value !== "object" || value === null || value["state"] !== "updated")) {
|
|
951
|
+
const state = typeof value === "object" && value !== null ? value["state"] : void 0;
|
|
952
|
+
if (action.action === "set-model-visibility" && state === "stale-account") {
|
|
953
|
+
await refresh(controller.signal);
|
|
954
|
+
throw new Error(t("visibilityStaleAccount"));
|
|
955
|
+
}
|
|
865
956
|
const reason = typeof value === "object" && value !== null && "reason" in value ? String(value["reason"]) : t("requestFailed");
|
|
866
957
|
throw new Error(reason);
|
|
867
958
|
}
|
|
@@ -870,7 +961,13 @@ window.__ModuleLoader__.load({
|
|
|
870
961
|
if (mounted.current && controller.signal.aborted !== true) setReadFailure(error instanceof Error ? error.message : t("requestFailed"));
|
|
871
962
|
} finally {
|
|
872
963
|
manualControllers.current.delete(controller);
|
|
873
|
-
if (mounted.current)
|
|
964
|
+
if (!mounted.current) return;
|
|
965
|
+
if (visibility) setTogglingModels((previous) => {
|
|
966
|
+
const next = new Set(previous);
|
|
967
|
+
next.delete(action.model);
|
|
968
|
+
return next;
|
|
969
|
+
});
|
|
970
|
+
else setBusy(false);
|
|
874
971
|
}
|
|
875
972
|
}, [
|
|
876
973
|
refresh,
|
|
@@ -1019,6 +1116,7 @@ window.__ModuleLoader__.load({
|
|
|
1019
1116
|
}),
|
|
1020
1117
|
status.probe === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProbeSection, {
|
|
1021
1118
|
probe: status.probe,
|
|
1119
|
+
models: status.models,
|
|
1022
1120
|
t,
|
|
1023
1121
|
busy,
|
|
1024
1122
|
onDetect: confirmDetect,
|
|
@@ -1032,14 +1130,27 @@ window.__ModuleLoader__.load({
|
|
|
1032
1130
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextTable, {
|
|
1033
1131
|
models: status.models,
|
|
1034
1132
|
t,
|
|
1035
|
-
|
|
1036
|
-
...status.useMaximumContextWindow
|
|
1037
|
-
|
|
1133
|
+
contextPreferenceDisabled: busy,
|
|
1134
|
+
...variant.id === AI_CARD_VARIANT.id && status.useMaximumContextWindow !== void 0 ? {
|
|
1135
|
+
useMaximumContextWindow: status.useMaximumContextWindow,
|
|
1136
|
+
onUseMaximumContextWindow: (enabled) => {
|
|
1137
|
+
control({
|
|
1138
|
+
action: "set-maximum-context-window",
|
|
1139
|
+
enabled
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
} : {},
|
|
1143
|
+
visibility: status.visibility,
|
|
1144
|
+
visibilityControlsDisabled: busy,
|
|
1145
|
+
visibilityToggling: togglingModels,
|
|
1146
|
+
onVisibilityToggle: (modelId, visible) => {
|
|
1038
1147
|
control({
|
|
1039
|
-
action: "set-
|
|
1040
|
-
|
|
1148
|
+
action: "set-model-visibility",
|
|
1149
|
+
model: modelId,
|
|
1150
|
+
visible,
|
|
1151
|
+
account: status.visibility?.account ?? ""
|
|
1041
1152
|
});
|
|
1042
|
-
}
|
|
1153
|
+
}
|
|
1043
1154
|
})
|
|
1044
1155
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1045
1156
|
style: tabPanelStyle,
|
|
@@ -1510,6 +1621,44 @@ window.__ModuleLoader__.load({
|
|
|
1510
1621
|
return t("probeNoteUnknown");
|
|
1511
1622
|
}
|
|
1512
1623
|
//#endregion
|
|
1624
|
+
//#region src/client/WorkBuddyConfigPage.tsx
|
|
1625
|
+
/**
|
|
1626
|
+
* The cards' list; the page supplies no other chrome. A semantic `<ul>` —
|
|
1627
|
+
* each card below is an `<li>` — with the user-agent list defaults cleared so
|
|
1628
|
+
* only the column/gap rhythm remains, keeping the layout identical to the
|
|
1629
|
+
* flex column it replaced while giving the page real list semantics.
|
|
1630
|
+
*/
|
|
1631
|
+
const pageStyle = {
|
|
1632
|
+
display: "flex",
|
|
1633
|
+
flexDirection: "column",
|
|
1634
|
+
gap: 12,
|
|
1635
|
+
listStyle: "none",
|
|
1636
|
+
margin: 0,
|
|
1637
|
+
padding: 0
|
|
1638
|
+
};
|
|
1639
|
+
/**
|
|
1640
|
+
* Render the bundle's configuration: one status card per WorkBuddy variant.
|
|
1641
|
+
*
|
|
1642
|
+
* Both variants share one page. DSH 0.1.6 replaced the settings section's
|
|
1643
|
+
* keyed `settings.plugin.item` slot — dispatched once per served settings
|
|
1644
|
+
* namespace, which is why the two variants used to be two cards — with the
|
|
1645
|
+
* Plugins page's `plugins.bundle.config`, keyed by the bundle's package name.
|
|
1646
|
+
* A bundle therefore carries exactly one configuration entry, so the cards are
|
|
1647
|
+
* stacked here instead of being dispatched separately. (The 0.1.5 settings tab
|
|
1648
|
+
* and its two dispatched cards still exist on 0.1.5 hosts; this page only
|
|
1649
|
+
* mounts where the Plugins page declares its slot.)
|
|
1650
|
+
*/
|
|
1651
|
+
function WorkBuddyConfigPage({ view, t }) {
|
|
1652
|
+
if (view === "summary") return t("intro");
|
|
1653
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
1654
|
+
style: pageStyle,
|
|
1655
|
+
children: CARD_VARIANTS.map((variant) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkBuddyPluginCard, {
|
|
1656
|
+
t,
|
|
1657
|
+
variant
|
|
1658
|
+
}, variant.id))
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
//#endregion
|
|
1513
1662
|
//#region src/client/locales.ts
|
|
1514
1663
|
/** Plugin-card copy registered under the settings.workbuddy locale namespace. */
|
|
1515
1664
|
const en = {
|
|
@@ -1557,8 +1706,11 @@ window.__ModuleLoader__.load({
|
|
|
1557
1706
|
contextHeading: "Context window",
|
|
1558
1707
|
contextUpTo: "up to {size}",
|
|
1559
1708
|
contextDefault: "default {size}",
|
|
1709
|
+
contextUnknown: "no declared context window",
|
|
1560
1710
|
useMaximumContextWindow: "Use the largest declared context window",
|
|
1561
1711
|
useMaximumContextWindowHint: "Applies to WorkBuddy AI models that offer a larger window.",
|
|
1712
|
+
visibilityIntro: "Uncheck a model to hide it from the model picker. Saved per signed-in account; chats already using a hidden model keep working.",
|
|
1713
|
+
visibilityStaleAccount: "The signed-in account changed — this change was not saved.",
|
|
1562
1714
|
freeModel: "Free",
|
|
1563
1715
|
badgeLimitedFree: "Limited-time free",
|
|
1564
1716
|
badgeNightDiscount: "Night discount",
|
|
@@ -1640,8 +1792,11 @@ window.__ModuleLoader__.load({
|
|
|
1640
1792
|
contextHeading: "上下文窗口",
|
|
1641
1793
|
contextUpTo: "最高 {size}",
|
|
1642
1794
|
contextDefault: "默认 {size}",
|
|
1795
|
+
contextUnknown: "未声明上下文窗口",
|
|
1643
1796
|
useMaximumContextWindow: "使用上游声明的最大上下文窗口",
|
|
1644
1797
|
useMaximumContextWindowHint: "仅作用于 WorkBuddy AI 中声明了更大窗口的模型。",
|
|
1798
|
+
visibilityIntro: "取消勾选即可在模型选择器中隐藏该模型;按当前登录账号分别保存,已在用该模型的会话不受影响。",
|
|
1799
|
+
visibilityStaleAccount: "登录账号已切换——本次修改未保存。",
|
|
1645
1800
|
freeModel: "免费",
|
|
1646
1801
|
badgeLimitedFree: "限时免费",
|
|
1647
1802
|
badgeNightDiscount: "夜间折扣",
|
|
@@ -1683,16 +1838,26 @@ window.__ModuleLoader__.load({
|
|
|
1683
1838
|
/** Stable browser-plugin name. */
|
|
1684
1839
|
const name = "dsh-workbuddy-connect-client";
|
|
1685
1840
|
/**
|
|
1686
|
-
*
|
|
1841
|
+
* The bundle's package name, which is also this half's configuration key.
|
|
1842
|
+
*
|
|
1843
|
+
* The Plugins page dispatches `plugins.bundle.config` by the bundle's package
|
|
1844
|
+
* name, so the key has to spell exactly what the profile installs.
|
|
1845
|
+
*/
|
|
1846
|
+
const BUNDLE_NAME = "dsh-workbuddy-connect";
|
|
1847
|
+
/**
|
|
1848
|
+
* Client services required by this browser half.
|
|
1687
1849
|
*
|
|
1688
1850
|
* DSH 0.1.2 removed `@deepseek-ai/dsh-client-runtime` (the package that used to
|
|
1689
|
-
* hold the browser `ClientContext` alias and the `slots` service)
|
|
1690
|
-
*
|
|
1691
|
-
*
|
|
1692
|
-
* `@deepseek-ai/dsh-client-locale
|
|
1693
|
-
*
|
|
1694
|
-
*
|
|
1695
|
-
*
|
|
1851
|
+
* hold the browser `ClientContext` alias and the `slots` service), so the
|
|
1852
|
+
* services come from narrower packages: the `slots` registry lives in
|
|
1853
|
+
* `@deepseek-ai/dsh-client-ui-renderer` and `locale` in
|
|
1854
|
+
* `@deepseek-ai/dsh-client-locale`. Neither slot owner is named here on
|
|
1855
|
+
* purpose: `settings.plugin.item`'s declarer (`…-ui-settings-plugins`) is
|
|
1856
|
+
* absent from 0.1.6+ hosts and `plugins.bundle.config`'s declarer
|
|
1857
|
+
* (`…-ui-plugin-manager`) is absent from 0.1.5 hosts, and the seam choice is
|
|
1858
|
+
* made by slot-declaration lifetime, not by activation order — `ctx.slots.inject`
|
|
1859
|
+
* fires whenever the declaring package commits the slot, before or after this
|
|
1860
|
+
* fiber starts.
|
|
1696
1861
|
*/
|
|
1697
1862
|
const inject = [
|
|
1698
1863
|
"slots",
|
|
@@ -1700,57 +1865,105 @@ window.__ModuleLoader__.load({
|
|
|
1700
1865
|
"remote",
|
|
1701
1866
|
"remote.session"
|
|
1702
1867
|
];
|
|
1868
|
+
/** Prefix every guarded client contribution's degradation logs with this. */
|
|
1869
|
+
const CLIENT_CONTRIBUTION_FAILED = "[dsh-workbuddy-connect] client contribution failed to load (host provider unaffected):";
|
|
1870
|
+
/** Disposer handed back when a deferred registration degraded: nothing to undo. */
|
|
1871
|
+
const NOOP_DISPOSER = () => {};
|
|
1703
1872
|
/**
|
|
1704
|
-
*
|
|
1873
|
+
* Run ONE browser-side contribution, degrading its failure to a `console.error`
|
|
1874
|
+
* instead of throwing into the DSH loader. Returns the contribution's own
|
|
1875
|
+
* value on success, or `undefined` when it degraded — the deferred slot
|
|
1876
|
+
* callbacks below substitute `NOOP_DISPOSER` for that, because the slot
|
|
1877
|
+
* runtime always expects a disposer back.
|
|
1878
|
+
*
|
|
1879
|
+
* Every contribution is guarded at BOTH boundaries where it can throw:
|
|
1705
1880
|
*
|
|
1706
|
-
*
|
|
1707
|
-
*
|
|
1708
|
-
*
|
|
1709
|
-
* the
|
|
1710
|
-
* the
|
|
1711
|
-
*
|
|
1881
|
+
* 1. the eager `ctx.slots.inject(...)` / `ctx.inject(...)` call itself, which
|
|
1882
|
+
* runs synchronously inside `apply()` — e.g. a slot-API shape break such as
|
|
1883
|
+
* the rc.6→rc.7 `id`→`key` rename;
|
|
1884
|
+
* 2. the deferred callback, which the slot runtime invokes later — when the
|
|
1885
|
+
* owner commits the slot's declaration, or when the injected services
|
|
1886
|
+
* arrive — long after `apply()` has returned, where no enclosing try/catch
|
|
1887
|
+
* could still catch it.
|
|
1712
1888
|
*
|
|
1713
|
-
*
|
|
1714
|
-
*
|
|
1715
|
-
*
|
|
1716
|
-
*
|
|
1717
|
-
* shape. If you change the guarded body or the `console.error` message here,
|
|
1718
|
-
* update the mirrored `apply()` in that spec too, or the fallback test will
|
|
1719
|
-
* silently diverge from this real implementation.
|
|
1889
|
+
* The pair is what makes the contributions independent: a failure in one
|
|
1890
|
+
* settings seam, or in the probe control, leaves every other registration
|
|
1891
|
+
* intact. Guards are for THIS browser half only; the host half reports its own
|
|
1892
|
+
* errors through `ctx.logger`.
|
|
1720
1893
|
*/
|
|
1721
|
-
function
|
|
1894
|
+
function guardClientContribution(label, fn) {
|
|
1722
1895
|
try {
|
|
1723
|
-
|
|
1896
|
+
return fn();
|
|
1897
|
+
} catch (error) {
|
|
1898
|
+
console.error(`${CLIENT_CONTRIBUTION_FAILED} ${label}`, error);
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Register the card copy and both settings-surface seams, one guarded
|
|
1904
|
+
* contribution at a time.
|
|
1905
|
+
*
|
|
1906
|
+
* A DSH slot-API breaking change degrades to a `console.error` per
|
|
1907
|
+
* contribution instead of throwing into the DSH loader and raising the red
|
|
1908
|
+
* "Failed to load plugins" banner; because each contribution carries its own
|
|
1909
|
+
* guard, one failing registration never takes the others with it (the old
|
|
1910
|
+
* settings cards survive a broken Plugins-page seam, and the probe control
|
|
1911
|
+
* survives either). The host provider keeps working throughout: the
|
|
1912
|
+
* `workbuddy` model channel is unaffected, and `dsh-workbuddy-connect status`
|
|
1913
|
+
* reports host health via the heartbeat file.
|
|
1914
|
+
*
|
|
1915
|
+
* The tests import this function directly (`tests/client-fallback.spec.ts`),
|
|
1916
|
+
* so its isolation semantics are pinned against the real entry — keep any
|
|
1917
|
+
* change to the guarded structure in sync with that spec.
|
|
1918
|
+
*/
|
|
1919
|
+
function apply(ctx) {
|
|
1920
|
+
const namespace = "settings.workbuddy";
|
|
1921
|
+
guardClientContribution("settings copy", () => {
|
|
1724
1922
|
ctx.effect(() => ctx.locale.register(namespace, {
|
|
1725
1923
|
zh,
|
|
1726
1924
|
en
|
|
1727
1925
|
}), "dsh-workbuddy-connect: settings copy");
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
inject
|
|
1734
|
-
|
|
1735
|
-
variant
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
name: "conversation.input.right",
|
|
1741
|
-
id: "workbuddy-probe",
|
|
1742
|
-
order: 10,
|
|
1743
|
-
inject: (sessionId) => ({
|
|
1744
|
-
directory: scope.modelDirectories.directoryFor(sessionId).store,
|
|
1745
|
-
t
|
|
1926
|
+
});
|
|
1927
|
+
const t = ctx.locale.bind(namespace);
|
|
1928
|
+
for (const [index, variant] of CARD_VARIANTS.entries()) {
|
|
1929
|
+
const label = `settings.plugin.item card "${variant.id}"`;
|
|
1930
|
+
guardClientContribution(label, () => {
|
|
1931
|
+
ctx.slots.inject("settings.plugin.item", () => guardClientContribution(label, () => ctx.slots.register({
|
|
1932
|
+
name: "settings.plugin.item",
|
|
1933
|
+
key: variant.id,
|
|
1934
|
+
priority: 30 - index,
|
|
1935
|
+
inject: () => ({
|
|
1936
|
+
t,
|
|
1937
|
+
variant
|
|
1746
1938
|
})
|
|
1747
|
-
},
|
|
1939
|
+
}, WorkBuddyPluginCard)) ?? NOOP_DISPOSER);
|
|
1748
1940
|
});
|
|
1749
|
-
} catch (error) {
|
|
1750
|
-
console.error("[dsh-workbuddy-connect] client card failed to load (host provider unaffected):", error);
|
|
1751
1941
|
}
|
|
1942
|
+
guardClientContribution("plugins.bundle.config page", () => {
|
|
1943
|
+
ctx.slots.inject("plugins.bundle.config", () => guardClientContribution("plugins.bundle.config page", () => ctx.slots.register({
|
|
1944
|
+
name: "plugins.bundle.config",
|
|
1945
|
+
key: "dsh-workbuddy-connect",
|
|
1946
|
+
locale: namespace
|
|
1947
|
+
}, WorkBuddyConfigPage)) ?? NOOP_DISPOSER);
|
|
1948
|
+
});
|
|
1949
|
+
guardClientContribution("conversation probe control", () => {
|
|
1950
|
+
ctx.inject(["modelDirectories"], (scope) => {
|
|
1951
|
+
guardClientContribution("conversation probe control", () => {
|
|
1952
|
+
scope.slots.inject("conversation.input.right", () => guardClientContribution("conversation probe control", () => scope.slots.register({
|
|
1953
|
+
name: "conversation.input.right",
|
|
1954
|
+
id: "workbuddy-probe",
|
|
1955
|
+
order: 10,
|
|
1956
|
+
inject: (sessionId) => ({
|
|
1957
|
+
directory: scope.modelDirectories.directoryFor(sessionId).store,
|
|
1958
|
+
t
|
|
1959
|
+
})
|
|
1960
|
+
}, WorkBuddyProbeControl)) ?? NOOP_DISPOSER);
|
|
1961
|
+
});
|
|
1962
|
+
});
|
|
1963
|
+
});
|
|
1752
1964
|
}
|
|
1753
1965
|
//#endregion
|
|
1966
|
+
exports.BUNDLE_NAME = BUNDLE_NAME;
|
|
1754
1967
|
exports.apply = apply;
|
|
1755
1968
|
exports.inject = inject;
|
|
1756
1969
|
exports.name = name;
|