dsh-workbuddy-connect 0.5.0 → 0.5.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/lib/bin.js +1 -1
- package/lib/client.js +187 -44
- package/lib/index.d.ts +93 -1
- package/lib/index.js +2 -2
- package/lib/{variants-GgLTJgZs.js → variants-CExA7lJt.js} +231 -10
- package/package.json +3 -1
package/lib/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { E as WorkBuddyUpstreamClient,
|
|
2
|
+
import { E as WorkBuddyUpstreamClient, J as resolveAppVersion, b as WorkBuddyCredentialStore, f as WORKBUDDY_CONNECT_VERSION, g as FALLBACK_WORKBUDDY_MODELS, h as FALLBACK_WORKBUDDY_AI_MODELS, i as variantFor, l as readHostHeartbeat, n as CN_VARIANT, r as WORKBUDDY_VARIANTS, s as isHeartbeatProcessAlive, u as workbuddyHostHeartbeatPath } from "./variants-CExA7lJt.js";
|
|
3
3
|
import { realpathSync } from "node:fs";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
//#region src/bin.ts
|
package/lib/client.js
CHANGED
|
@@ -31,6 +31,27 @@ window.__ModuleLoader__.load({
|
|
|
31
31
|
const WORKBUDDY_AI_STATUS_PATH = "/plugins/dsh-workbuddy-connect/ai/status";
|
|
32
32
|
const WORKBUDDY_AI_PROBE_PATH = "/plugins/dsh-workbuddy-connect/ai/probe";
|
|
33
33
|
//#endregion
|
|
34
|
+
//#region src/client/status-document.ts
|
|
35
|
+
/**
|
|
36
|
+
* Whether a parsed status response really is a status document.
|
|
37
|
+
*
|
|
38
|
+
* A 200 is not a promise about the body: it may be empty, literal `null`, a
|
|
39
|
+
* non-JSON page from a proxy, or an array. Both halves of the browser plugin
|
|
40
|
+
* read the same route, so both must agree on what is valid — storing an
|
|
41
|
+
* unreadable value puts something in state that the next render dereferences.
|
|
42
|
+
*
|
|
43
|
+
* The check is deliberately limited to the discriminator (plus `error`'s
|
|
44
|
+
* `message`, which the error paragraph renders): validating optional fields
|
|
45
|
+
* here would reject documents the host legitimately omits fields from.
|
|
46
|
+
*/
|
|
47
|
+
function isWorkBuddyWebStatus(value) {
|
|
48
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
49
|
+
const wrapped = value;
|
|
50
|
+
const status = wrapped["status"];
|
|
51
|
+
if (status === "signed-out" || status === "signed-in") return true;
|
|
52
|
+
return status === "error" && typeof wrapped["message"] === "string";
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
34
55
|
//#region src/client/WorkBuddyPluginCard.tsx
|
|
35
56
|
/** WorkBuddy status card contributed to Harness Plugin configuration. */
|
|
36
57
|
/** CN WorkBuddy; the plugin's long-standing card and default. */
|
|
@@ -291,6 +312,12 @@ window.__ModuleLoader__.load({
|
|
|
291
312
|
background: "var(--dsw-alias-brand-primary, #1677ff)"
|
|
292
313
|
};
|
|
293
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* Status dot colour. Takes `'loading'` as well as the document's own states:
|
|
317
|
+
* before the first response the card knows nothing about the account, so it must
|
|
318
|
+
* not borrow the signed-out grey — that would read as "nothing is wrong, nobody
|
|
319
|
+
* is signed in" when the truth is "not read yet".
|
|
320
|
+
*/
|
|
294
321
|
function dotStyle(status) {
|
|
295
322
|
return {
|
|
296
323
|
width: 9,
|
|
@@ -309,29 +336,40 @@ window.__ModuleLoader__.load({
|
|
|
309
336
|
timeStyle: "short"
|
|
310
337
|
}).format(new Date(ms));
|
|
311
338
|
}
|
|
312
|
-
/**
|
|
339
|
+
/**
|
|
340
|
+
* One billing package as a labeled progress bar.
|
|
341
|
+
*
|
|
342
|
+
* A package whose allowance the upstream never reported (`size` not positive)
|
|
343
|
+
* has no percentage to state. It must not fall back to 100%: the plugin would be
|
|
344
|
+
* claiming a full quota it knows nothing about, which is the opposite of the
|
|
345
|
+
* honest "remaining N" line printed below it. Unknown size therefore renders the
|
|
346
|
+
* percent slot as unknown copy and an unfilled, indeterminate track.
|
|
347
|
+
*/
|
|
313
348
|
function CreditBar({ label, remain, size, t }) {
|
|
314
|
-
const
|
|
349
|
+
const sizeKnown = size > 0;
|
|
350
|
+
const detail = sizeKnown ? t("exactRemaining", {
|
|
315
351
|
remain: formatNumber(remain),
|
|
316
352
|
size: formatNumber(size)
|
|
317
353
|
}) : t("creditPackageUnknownSize", { remain: formatNumber(remain) });
|
|
318
|
-
const percent =
|
|
319
|
-
const display = new Intl.NumberFormat(void 0, { maximumFractionDigits: 1 }).format(percent);
|
|
354
|
+
const percent = sizeKnown ? remain / size * 100 : void 0;
|
|
355
|
+
const display = percent === void 0 ? t("percentUnknown") : t("percentRemaining", { percent: new Intl.NumberFormat(void 0, { maximumFractionDigits: 1 }).format(percent) });
|
|
320
356
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
321
357
|
style: quotaGroupStyle,
|
|
322
358
|
children: [
|
|
323
359
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
324
360
|
style: quotaLabelStyle,
|
|
325
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children:
|
|
361
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: display })]
|
|
326
362
|
}),
|
|
327
363
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
328
364
|
style: progressTrackStyle,
|
|
329
365
|
role: "progressbar",
|
|
330
366
|
"aria-label": label,
|
|
331
|
-
"aria-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
367
|
+
...percent === void 0 ? { "aria-valuetext": detail } : {
|
|
368
|
+
"aria-valuemin": 0,
|
|
369
|
+
"aria-valuemax": 100,
|
|
370
|
+
"aria-valuenow": percent
|
|
371
|
+
},
|
|
372
|
+
children: percent === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: progressFillStyle(percent) })
|
|
335
373
|
}),
|
|
336
374
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
337
375
|
style: bodyStyle,
|
|
@@ -555,17 +593,72 @@ window.__ModuleLoader__.load({
|
|
|
555
593
|
function WorkBuddyPluginCard({ t, variant = CN_CARD_VARIANT }) {
|
|
556
594
|
if (t === void 0) throw new Error("WorkBuddy plugin card requires its translation function");
|
|
557
595
|
const [open, setOpen] = (0, react.useState)(false);
|
|
558
|
-
|
|
596
|
+
/**
|
|
597
|
+
* The document to render. `undefined` means *not read yet*, which is a
|
|
598
|
+
* distinct state from "signed out": seeding this with a signed-out document
|
|
599
|
+
* told an already-signed-in user they were signed out for the whole first
|
|
600
|
+
* round trip (and forever, if the read never settled).
|
|
601
|
+
*/
|
|
602
|
+
const [status, setStatus] = (0, react.useState)();
|
|
603
|
+
/**
|
|
604
|
+
* Whether the last **successful** read found a usable credential.
|
|
605
|
+
*
|
|
606
|
+
* Kept apart from `status` because the poll's liveness must depend on what the
|
|
607
|
+
* account actually is, not on what the card last displayed: a failed read
|
|
608
|
+
* leaves this untouched, so a transient failure cannot disarm the interval,
|
|
609
|
+
* while a genuine signed-out answer still stops it.
|
|
610
|
+
*
|
|
611
|
+
* `undefined` therefore means "no successful read yet", which is also the
|
|
612
|
+
* condition that decides whether a failed read has anything to preserve.
|
|
613
|
+
*/
|
|
614
|
+
const [signedIn, setSignedIn] = (0, react.useState)();
|
|
615
|
+
/**
|
|
616
|
+
* Why the most recent read failed, when it did. Rendered as a notice beside
|
|
617
|
+
* whatever document is still on screen, rather than replacing it.
|
|
618
|
+
*/
|
|
619
|
+
const [readFailure, setReadFailure] = (0, react.useState)();
|
|
559
620
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
560
621
|
const [tab, setTab] = (0, react.useState)("status");
|
|
561
622
|
const mounted = (0, react.useRef)(true);
|
|
623
|
+
/**
|
|
624
|
+
* Identity of the newest read that may write. Assigned when a read *starts*,
|
|
625
|
+
* so a response is superseded by anything begun after it — "the response whose
|
|
626
|
+
* request started last wins". Without this, a slow poll begun before a manual
|
|
627
|
+
* action could settle after the action's own refresh and restore the older
|
|
628
|
+
* document.
|
|
629
|
+
*/
|
|
630
|
+
const readSeq = (0, react.useRef)(0);
|
|
631
|
+
/** Manual requests in flight, so unmount can abort them like the poll's. */
|
|
632
|
+
const manualControllers = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
562
633
|
(0, react.useEffect)(() => {
|
|
563
634
|
mounted.current = true;
|
|
564
635
|
return () => {
|
|
565
636
|
mounted.current = false;
|
|
637
|
+
for (const controller of manualControllers.current) controller.abort();
|
|
638
|
+
manualControllers.current.clear();
|
|
566
639
|
};
|
|
567
640
|
}, []);
|
|
641
|
+
/** Register a manual request's controller so unmount aborts it. */
|
|
642
|
+
const trackController = (0, react.useCallback)(() => {
|
|
643
|
+
const controller = new AbortController();
|
|
644
|
+
manualControllers.current.add(controller);
|
|
645
|
+
return controller;
|
|
646
|
+
}, []);
|
|
647
|
+
/**
|
|
648
|
+
* Read the status document and apply it under the two policies the card's
|
|
649
|
+
* correctness rests on:
|
|
650
|
+
*
|
|
651
|
+
* - a non-document body (empty, `null`, a non-JSON page) is a failed read, not
|
|
652
|
+
* something to store and then dereference in the render;
|
|
653
|
+
* - a failed read never discards a document already on screen. It is recorded
|
|
654
|
+
* and shown as a notice beside that document; only when nothing has been
|
|
655
|
+
* read yet does the failure itself become the rendered state.
|
|
656
|
+
*
|
|
657
|
+
* Returns whether this read produced the current document.
|
|
658
|
+
*/
|
|
568
659
|
const refresh = (0, react.useCallback)(async (signal) => {
|
|
660
|
+
const seq = ++readSeq.current;
|
|
661
|
+
const current = () => mounted.current && signal?.aborted !== true && seq === readSeq.current;
|
|
569
662
|
try {
|
|
570
663
|
const response = await fetch(variant.statusPath, {
|
|
571
664
|
headers: { accept: "application/json" },
|
|
@@ -574,12 +667,23 @@ window.__ModuleLoader__.load({
|
|
|
574
667
|
});
|
|
575
668
|
const value = await response.json().catch(() => void 0);
|
|
576
669
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
577
|
-
if (
|
|
670
|
+
if (!isWorkBuddyWebStatus(value)) throw new Error(t("statusResponseInvalid"));
|
|
671
|
+
if (!current()) return false;
|
|
672
|
+
setStatus(value);
|
|
673
|
+
if (value.status === "signed-in") setSignedIn(true);
|
|
674
|
+
else if (value.status === "signed-out") setSignedIn(false);
|
|
675
|
+
setReadFailure(void 0);
|
|
676
|
+
return true;
|
|
578
677
|
} catch (error) {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
message
|
|
582
|
-
|
|
678
|
+
const message = error instanceof Error ? error.message : t("requestFailed");
|
|
679
|
+
if (current()) {
|
|
680
|
+
setReadFailure(message);
|
|
681
|
+
setStatus((previous) => previous === void 0 ? {
|
|
682
|
+
status: "error",
|
|
683
|
+
message
|
|
684
|
+
} : previous);
|
|
685
|
+
}
|
|
686
|
+
return false;
|
|
583
687
|
}
|
|
584
688
|
}, [t, variant.statusPath]);
|
|
585
689
|
(0, react.useEffect)(() => {
|
|
@@ -591,7 +695,7 @@ window.__ModuleLoader__.load({
|
|
|
591
695
|
};
|
|
592
696
|
}, [open, refresh]);
|
|
593
697
|
(0, react.useEffect)(() => {
|
|
594
|
-
if (!open ||
|
|
698
|
+
if (!open || signedIn === false) return;
|
|
595
699
|
const controller = new AbortController();
|
|
596
700
|
const timer = window.setInterval(() => {
|
|
597
701
|
refresh(controller.signal);
|
|
@@ -603,13 +707,15 @@ window.__ModuleLoader__.load({
|
|
|
603
707
|
}, [
|
|
604
708
|
open,
|
|
605
709
|
refresh,
|
|
606
|
-
|
|
710
|
+
signedIn
|
|
607
711
|
]);
|
|
608
712
|
const manualRefresh = async () => {
|
|
609
713
|
setBusy(true);
|
|
714
|
+
const controller = trackController();
|
|
610
715
|
try {
|
|
611
|
-
await refresh();
|
|
716
|
+
await refresh(controller.signal);
|
|
612
717
|
} finally {
|
|
718
|
+
manualControllers.current.delete(controller);
|
|
613
719
|
if (mounted.current) setBusy(false);
|
|
614
720
|
}
|
|
615
721
|
};
|
|
@@ -622,9 +728,10 @@ window.__ModuleLoader__.load({
|
|
|
622
728
|
* than thrown away, so the reason survives the round trip.
|
|
623
729
|
*/
|
|
624
730
|
const refreshModels = (0, react.useCallback)(async () => {
|
|
625
|
-
const key = status
|
|
731
|
+
const key = status?.status === "signed-in" ? status.probeKey : void 0;
|
|
626
732
|
if (key === void 0) return;
|
|
627
733
|
setBusy(true);
|
|
734
|
+
const controller = trackController();
|
|
628
735
|
try {
|
|
629
736
|
const response = await fetch(variant.probePath, {
|
|
630
737
|
method: "POST",
|
|
@@ -633,23 +740,27 @@ window.__ModuleLoader__.load({
|
|
|
633
740
|
"X-WorkBuddy-Probe-Key": key
|
|
634
741
|
},
|
|
635
742
|
credentials: "same-origin",
|
|
743
|
+
signal: controller.signal,
|
|
636
744
|
body: JSON.stringify({ action: "refresh" })
|
|
637
745
|
});
|
|
638
746
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
639
747
|
} catch (error) {
|
|
640
|
-
if (mounted.current)
|
|
641
|
-
|
|
642
|
-
message: error instanceof Error ? error.message : t("requestFailed")
|
|
643
|
-
}));
|
|
748
|
+
if (mounted.current && controller.signal.aborted !== true) setReadFailure(error instanceof Error ? error.message : t("requestFailed"));
|
|
749
|
+
manualControllers.current.delete(controller);
|
|
644
750
|
return;
|
|
645
751
|
} finally {
|
|
646
752
|
if (mounted.current) setBusy(false);
|
|
647
753
|
}
|
|
648
|
-
|
|
754
|
+
try {
|
|
755
|
+
await refresh(controller.signal);
|
|
756
|
+
} finally {
|
|
757
|
+
manualControllers.current.delete(controller);
|
|
758
|
+
}
|
|
649
759
|
}, [
|
|
650
760
|
refresh,
|
|
651
761
|
status,
|
|
652
762
|
t,
|
|
763
|
+
trackController,
|
|
653
764
|
variant.probePath
|
|
654
765
|
]);
|
|
655
766
|
/**
|
|
@@ -660,9 +771,10 @@ window.__ModuleLoader__.load({
|
|
|
660
771
|
* catalog from here.
|
|
661
772
|
*/
|
|
662
773
|
const control = (0, react.useCallback)(async (action) => {
|
|
663
|
-
const key = status
|
|
774
|
+
const key = status?.status === "signed-in" ? status.probeKey : void 0;
|
|
664
775
|
if (key === void 0) return;
|
|
665
776
|
setBusy(true);
|
|
777
|
+
const controller = trackController();
|
|
666
778
|
try {
|
|
667
779
|
const response = await fetch(variant.probePath, {
|
|
668
780
|
method: "POST",
|
|
@@ -671,6 +783,7 @@ window.__ModuleLoader__.load({
|
|
|
671
783
|
"X-WorkBuddy-Probe-Key": key
|
|
672
784
|
},
|
|
673
785
|
credentials: "same-origin",
|
|
786
|
+
signal: controller.signal,
|
|
674
787
|
body: JSON.stringify(action)
|
|
675
788
|
});
|
|
676
789
|
const value = await response.json().catch(() => void 0);
|
|
@@ -678,19 +791,18 @@ window.__ModuleLoader__.load({
|
|
|
678
791
|
const message = typeof value === "object" && value !== null && "error" in value ? String(value["error"]) : `HTTP ${response.status}`;
|
|
679
792
|
throw new Error(message);
|
|
680
793
|
}
|
|
681
|
-
await refresh();
|
|
794
|
+
await refresh(controller.signal);
|
|
682
795
|
} catch (error) {
|
|
683
|
-
if (mounted.current)
|
|
684
|
-
status: "error",
|
|
685
|
-
message: error instanceof Error ? error.message : t("requestFailed")
|
|
686
|
-
}));
|
|
796
|
+
if (mounted.current && controller.signal.aborted !== true) setReadFailure(error instanceof Error ? error.message : t("requestFailed"));
|
|
687
797
|
} finally {
|
|
798
|
+
manualControllers.current.delete(controller);
|
|
688
799
|
if (mounted.current) setBusy(false);
|
|
689
800
|
}
|
|
690
801
|
}, [
|
|
691
802
|
refresh,
|
|
692
803
|
status,
|
|
693
804
|
t,
|
|
805
|
+
trackController,
|
|
694
806
|
variant.probePath
|
|
695
807
|
]);
|
|
696
808
|
/**
|
|
@@ -704,7 +816,7 @@ window.__ModuleLoader__.load({
|
|
|
704
816
|
});
|
|
705
817
|
}, [control]);
|
|
706
818
|
const title = t(variant.titleKey);
|
|
707
|
-
const label = status.status === "signed-in" ? status.nickname === void 0 ? t("signedInAs", { nickname: "" }).trimEnd().replace(/[::]$/, "") : t("signedInAs", { nickname: status.nickname }) : status.status === "error" ? t("requestFailed") : t("signedOut");
|
|
819
|
+
const label = status === void 0 ? t("loading") : status.status === "signed-in" ? status.nickname === void 0 ? t("signedInAs", { nickname: "" }).trimEnd().replace(/[::]$/, "") : t("signedInAs", { nickname: status.nickname }) : status.status === "error" ? t("requestFailed") : t("signedOut");
|
|
708
820
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
709
821
|
style: cardStyle,
|
|
710
822
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -744,9 +856,10 @@ window.__ModuleLoader__.load({
|
|
|
744
856
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
745
857
|
style: statusStyle,
|
|
746
858
|
role: "status",
|
|
859
|
+
"aria-busy": status === void 0,
|
|
747
860
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
748
861
|
"aria-hidden": "true",
|
|
749
|
-
style: dotStyle(status.status)
|
|
862
|
+
style: dotStyle(status === void 0 ? "loading" : status.status)
|
|
750
863
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
|
|
751
864
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
752
865
|
type: "button",
|
|
@@ -758,7 +871,11 @@ window.__ModuleLoader__.load({
|
|
|
758
871
|
children: busy ? t("refreshing") : t("refresh")
|
|
759
872
|
})]
|
|
760
873
|
}),
|
|
761
|
-
|
|
874
|
+
readFailure === void 0 || signedIn === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
875
|
+
style: errorStyle,
|
|
876
|
+
children: t("statusRefreshFailed", { message: readFailure })
|
|
877
|
+
}),
|
|
878
|
+
status?.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
762
879
|
status.expiresAt === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
763
880
|
style: bodyStyle,
|
|
764
881
|
children: t("accessTokenExpires", { time: formatTime(status.expiresAt) })
|
|
@@ -864,11 +981,11 @@ window.__ModuleLoader__.load({
|
|
|
864
981
|
})]
|
|
865
982
|
})
|
|
866
983
|
] }) : null,
|
|
867
|
-
status
|
|
984
|
+
status?.status === "signed-out" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
868
985
|
style: status.reason === void 0 ? bodyStyle : errorStyle,
|
|
869
986
|
children: status.reason ?? t(variant.signedOutKey)
|
|
870
987
|
}) : null,
|
|
871
|
-
status
|
|
988
|
+
status?.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
872
989
|
style: errorStyle,
|
|
873
990
|
children: status.message
|
|
874
991
|
}) : null
|
|
@@ -1064,15 +1181,24 @@ window.__ModuleLoader__.load({
|
|
|
1064
1181
|
/**
|
|
1065
1182
|
* The one-line tooltip: current state first, then what a click does — the same
|
|
1066
1183
|
* two-part shape Fast Mode uses.
|
|
1184
|
+
*
|
|
1185
|
+
* A recorded result outranks a remembered failure. `failed` only means "the last
|
|
1186
|
+
* run from this control did not complete"; the host can record a result for the
|
|
1187
|
+
* same model at any time (a detection started from the settings card, another
|
|
1188
|
+
* conversation, or a finished sweep), and the levels the user paid for are the
|
|
1189
|
+
* more useful answer than the stale failure. Failure copy is what remains when
|
|
1190
|
+
* there is no result to report.
|
|
1067
1191
|
*/
|
|
1068
1192
|
function tooltipText(t, model, state) {
|
|
1069
1193
|
if (state.busy) return t("probeRunning", { model });
|
|
1070
|
-
if (state.failed) return t("probeTooltipRetry");
|
|
1071
1194
|
const result = state.result;
|
|
1072
|
-
if (result
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1195
|
+
if (result !== void 0) {
|
|
1196
|
+
if (result.validation === "validating" && result.efforts.length > 0) return t("probeTooltipVerified", { levels: result.efforts.join(" / ") });
|
|
1197
|
+
if (result.validation === "non-validating") return t("probeTooltipNotValidating");
|
|
1198
|
+
return t("probeTooltipRetry");
|
|
1199
|
+
}
|
|
1200
|
+
if (state.failed) return t("probeTooltipRetry");
|
|
1201
|
+
return t("probeTooltipIdle", { model });
|
|
1076
1202
|
}
|
|
1077
1203
|
/** Model-independent shell: resolves the selection, then delegates per model. */
|
|
1078
1204
|
function WorkBuddyProbeControl({ directory, t }) {
|
|
@@ -1097,17 +1223,20 @@ window.__ModuleLoader__.load({
|
|
|
1097
1223
|
const [note, setNote] = (0, react.useState)();
|
|
1098
1224
|
const inFlight = (0, react.useRef)(false);
|
|
1099
1225
|
const mounted = (0, react.useRef)(false);
|
|
1226
|
+
const readSeq = (0, react.useRef)(0);
|
|
1100
1227
|
const tooltipId = (0, react.useId)();
|
|
1101
1228
|
const refresh = (0, react.useCallback)(async (signal) => {
|
|
1229
|
+
const seq = ++readSeq.current;
|
|
1102
1230
|
const response = await fetch(card.statusPath, {
|
|
1103
1231
|
credentials: "same-origin",
|
|
1104
1232
|
headers: { accept: "application/json" },
|
|
1105
1233
|
...signal === void 0 ? {} : { signal }
|
|
1106
1234
|
});
|
|
1107
1235
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
1108
|
-
const value = await response.json();
|
|
1109
|
-
if (
|
|
1110
|
-
|
|
1236
|
+
const value = await response.json().catch(() => void 0);
|
|
1237
|
+
if (!isWorkBuddyWebStatus(value)) throw new Error(t("statusResponseInvalid"));
|
|
1238
|
+
if (mounted.current && !signal?.aborted && seq === readSeq.current) setStatus(value);
|
|
1239
|
+
}, [card.statusPath, t]);
|
|
1111
1240
|
(0, react.useEffect)(() => {
|
|
1112
1241
|
mounted.current = true;
|
|
1113
1242
|
const controller = new AbortController();
|
|
@@ -1128,6 +1257,9 @@ window.__ModuleLoader__.load({
|
|
|
1128
1257
|
const key = status?.status === "signed-in" ? status.probeKey : void 0;
|
|
1129
1258
|
const result = status === void 0 ? void 0 : resultFor(status, model);
|
|
1130
1259
|
const visible = probe?.candidates.includes(model) === true || result !== void 0;
|
|
1260
|
+
(0, react.useEffect)(() => {
|
|
1261
|
+
if (result !== void 0) setFailed(false);
|
|
1262
|
+
}, [result]);
|
|
1131
1263
|
(0, react.useEffect)(() => {
|
|
1132
1264
|
setConfirming(false);
|
|
1133
1265
|
setNote(void 0);
|
|
@@ -1314,6 +1446,7 @@ window.__ModuleLoader__.load({
|
|
|
1314
1446
|
creditsDetailHeading: "By package",
|
|
1315
1447
|
creditsTotal: "Total: {total}",
|
|
1316
1448
|
percentRemaining: "{percent}% remaining",
|
|
1449
|
+
percentUnknown: "Remaining share unknown",
|
|
1317
1450
|
exactRemaining: "{remain} / {size} remaining",
|
|
1318
1451
|
creditPackageUnknownSize: "{remain} remaining",
|
|
1319
1452
|
creditsError: "Credit unavailable: {message}",
|
|
@@ -1327,6 +1460,8 @@ window.__ModuleLoader__.load({
|
|
|
1327
1460
|
catalogError: "Last update failed: {message}",
|
|
1328
1461
|
catalogAppVersion: "App version {version}",
|
|
1329
1462
|
requestFailed: "Request failed",
|
|
1463
|
+
statusRefreshFailed: "Refresh failed: {message} — showing the last known state",
|
|
1464
|
+
statusResponseInvalid: "WorkBuddy returned an unreadable status reply",
|
|
1330
1465
|
accountHeading: "Account",
|
|
1331
1466
|
modelsHeading: "Model offers",
|
|
1332
1467
|
contextHeading: "Context window",
|
|
@@ -1387,6 +1522,7 @@ window.__ModuleLoader__.load({
|
|
|
1387
1522
|
creditsDetailHeading: "按套餐",
|
|
1388
1523
|
creditsTotal: "合计:{total}",
|
|
1389
1524
|
percentRemaining: "剩余 {percent}%",
|
|
1525
|
+
percentUnknown: "剩余占比未知",
|
|
1390
1526
|
exactRemaining: "剩余 {remain} / {size}",
|
|
1391
1527
|
creditPackageUnknownSize: "剩余 {remain}",
|
|
1392
1528
|
creditsError: "积分查询失败:{message}",
|
|
@@ -1400,6 +1536,8 @@ window.__ModuleLoader__.load({
|
|
|
1400
1536
|
catalogError: "上次更新失败:{message}",
|
|
1401
1537
|
catalogAppVersion: "App 版本 {version}",
|
|
1402
1538
|
requestFailed: "请求失败",
|
|
1539
|
+
statusRefreshFailed: "刷新失败:{message} — 当前显示的是上次成功获取的状态",
|
|
1540
|
+
statusResponseInvalid: "WorkBuddy 返回的状态数据无法识别",
|
|
1403
1541
|
accountHeading: "账号",
|
|
1404
1542
|
modelsHeading: "模型优惠",
|
|
1405
1543
|
contextHeading: "上下文窗口",
|
|
@@ -1456,7 +1594,12 @@ window.__ModuleLoader__.load({
|
|
|
1456
1594
|
* named in the package's `dsh.client.inject` list, so cordis has activated
|
|
1457
1595
|
* them before this plugin's fiber starts.
|
|
1458
1596
|
*/
|
|
1459
|
-
const inject = [
|
|
1597
|
+
const inject = [
|
|
1598
|
+
"slots",
|
|
1599
|
+
"locale",
|
|
1600
|
+
"remote",
|
|
1601
|
+
"remote.session"
|
|
1602
|
+
];
|
|
1460
1603
|
/**
|
|
1461
1604
|
* Register card copy and the WorkBuddy card under Plugin configuration.
|
|
1462
1605
|
*
|
package/lib/index.d.ts
CHANGED
|
@@ -74,6 +74,90 @@ declare function resolveAppVersion(options?: ResolveAppVersionOptions): Promise<
|
|
|
74
74
|
*/
|
|
75
75
|
declare function appUserAgent(version: string): string;
|
|
76
76
|
//#endregion
|
|
77
|
+
//#region src/client-identity.d.ts
|
|
78
|
+
/**
|
|
79
|
+
* Compiled-in CN fallback for the `WorkBuddy/<v>` tokens.
|
|
80
|
+
*
|
|
81
|
+
* Observed on the CN desktop app installed here (research §3.1, verified
|
|
82
|
+
* 2026-09-11); like the international fallback it is a shape requirement,
|
|
83
|
+
* not a currency claim — the gateway has not been observed to branch on it.
|
|
84
|
+
*/
|
|
85
|
+
declare const FALLBACK_CN_APP_VERSION = "5.5.6";
|
|
86
|
+
/**
|
|
87
|
+
* Basename of the CN saved-version cache under `$DSH_HOME`.
|
|
88
|
+
*
|
|
89
|
+
* Deliberately not the international `.workbuddy-ai-version.json`: that file
|
|
90
|
+
* feeds the international catalog's User-Agent, and a CN App writing its
|
|
91
|
+
* version into it would relabel that request. The two caches stay isolated
|
|
92
|
+
* the way the per-variant catalog files are.
|
|
93
|
+
*/
|
|
94
|
+
declare const CN_APP_VERSION_FILENAME = ".workbuddy-app-version.json";
|
|
95
|
+
/** The resolved identity a chat request presents as. */
|
|
96
|
+
interface ChatIdentity {
|
|
97
|
+
/** Desktop App version; drives both `WorkBuddy/<v>` product tokens. */
|
|
98
|
+
clientVersion: string;
|
|
99
|
+
/** Bundled agent-CLI version; absent drops the `CLI/…` UA token. */
|
|
100
|
+
cliVersion?: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Whether a value is a CLI version that may reach a header.
|
|
104
|
+
*
|
|
105
|
+
* Tolerates a prerelease suffix (`2.137.1-rc.1`) because the bundled CLI's
|
|
106
|
+
* own metadata uses that spelling; anything with whitespace, CR or LF never
|
|
107
|
+
* passes — the value is interpolated into an HTTP header.
|
|
108
|
+
*/
|
|
109
|
+
declare function validCliVersion(value: unknown): value is string;
|
|
110
|
+
/**
|
|
111
|
+
* The bundled agent CLI's real version, or `undefined` when it does not resolve.
|
|
112
|
+
*
|
|
113
|
+
* `cli/package.json` ships a `0.0.0` placeholder in `version` with the real
|
|
114
|
+
* version in `publishConfig.customPackage.version`; a valid non-placeholder
|
|
115
|
+
* `version` wins, otherwise the custom-package value applies, and unreadable
|
|
116
|
+
* or invalid metadata yields `undefined` (the caller drops the `CLI/…` UA
|
|
117
|
+
* token rather than guessing).
|
|
118
|
+
*/
|
|
119
|
+
declare function readCliVersion(bundle: string): Promise<string | undefined>;
|
|
120
|
+
/**
|
|
121
|
+
* Build the chat User-Agent for one region.
|
|
122
|
+
*
|
|
123
|
+
* Throws on an invalid version rather than interpolating one into a header;
|
|
124
|
+
* `resolveChatIdentity` never produces such an identity, so the throw is a
|
|
125
|
+
* last gate against future call-site mistakes, not an expected path.
|
|
126
|
+
*/
|
|
127
|
+
declare function chatUserAgent(identity: ChatIdentity, region: WorkBuddyRegion): string;
|
|
128
|
+
/** Constructor dependencies; every reader is injectable so tests never touch a real App or home. */
|
|
129
|
+
interface ResolveChatIdentityOptions {
|
|
130
|
+
/** Installed CN desktop-bundle reader; defaults to the macOS probe. */
|
|
131
|
+
installedCn?: () => Promise<{
|
|
132
|
+
version: string;
|
|
133
|
+
bundle: string;
|
|
134
|
+
} | undefined>;
|
|
135
|
+
/** International version resolver; defaults to `app-version.ts`'s chain. */
|
|
136
|
+
resolveIntl?: () => Promise<AppVersionInfo>;
|
|
137
|
+
/** CLI-version reader; defaults to reading the bundle's `cli/package.json`. */
|
|
138
|
+
cliVersion?: (bundle: string) => Promise<string | undefined>;
|
|
139
|
+
/** CN saved-cache path; defaults to `$DSH_HOME/.workbuddy-app-version.json`. */
|
|
140
|
+
cnSavedPath?: string;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Resolve the chat identity for one region: installed App → region's saved
|
|
144
|
+
* value → compiled-in fallback. Never throws — a missing App, an unreadable
|
|
145
|
+
* plist, a failed cache write, or a reader that throws outright all degrade
|
|
146
|
+
* to {@link fallbackChatIdentity}; resolution never blocks a message.
|
|
147
|
+
*
|
|
148
|
+
* The production path caches per region (a message must not re-read the
|
|
149
|
+
* install tree); any injected option bypasses the cache entirely so tests
|
|
150
|
+
* with different readers cannot observe each other's resolutions.
|
|
151
|
+
*/
|
|
152
|
+
declare function resolveChatIdentity(region: WorkBuddyRegion, options?: ResolveChatIdentityOptions): Promise<ChatIdentity>;
|
|
153
|
+
/**
|
|
154
|
+
* The region's compiled-in fallback identity: the desktop form with the
|
|
155
|
+
* built-in version and no `CLI/…` segment. This is the single degraded
|
|
156
|
+
* shape every failure path converges on — a thrown reader, an unreadable
|
|
157
|
+
* bundle, or a missing cache all present this, never the legacy CLI UA.
|
|
158
|
+
*/
|
|
159
|
+
declare function fallbackChatIdentity(region: WorkBuddyRegion): ChatIdentity;
|
|
160
|
+
//#endregion
|
|
77
161
|
//#region src/probe.d.ts
|
|
78
162
|
/**
|
|
79
163
|
* The canonical values a probe tests, in a fixed order.
|
|
@@ -278,6 +362,12 @@ interface WorkBuddyCatalogFetch {
|
|
|
278
362
|
interface WorkBuddyUpstreamClientOptions {
|
|
279
363
|
/** App-version resolver for international catalog requests; injectable for tests. */
|
|
280
364
|
resolveAppVersion?: () => Promise<AppVersionInfo>;
|
|
365
|
+
/**
|
|
366
|
+
* Chat-identity resolver for chat and probe requests; injectable for tests.
|
|
367
|
+
* Defaults to `client-identity.ts`'s per-region chain. Refresh, catalog, and
|
|
368
|
+
* billing never consult it — those requests keep their long-standing headers.
|
|
369
|
+
*/
|
|
370
|
+
resolveChatIdentity?: (region: WorkBuddyRegion) => Promise<ChatIdentity>;
|
|
281
371
|
}
|
|
282
372
|
/**
|
|
283
373
|
* Upstream HTTP client. One instance serves the whole plugin; requests take
|
|
@@ -293,6 +383,8 @@ declare class WorkBuddyUpstreamClient {
|
|
|
293
383
|
* Injectable so tests never read the real filesystem.
|
|
294
384
|
*/
|
|
295
385
|
private readonly resolveAppVersion;
|
|
386
|
+
/** Chat-identity resolver; see {@link WorkBuddyUpstreamClientOptions.resolveChatIdentity}. */
|
|
387
|
+
private readonly resolveChatIdentity;
|
|
296
388
|
/** Provenance of the most recent successful catalog fetch, for the card. */
|
|
297
389
|
lastCatalog: WorkBuddyCatalogFetch | undefined;
|
|
298
390
|
constructor(options?: WorkBuddyUpstreamClientOptions);
|
|
@@ -1048,4 +1140,4 @@ declare const Config: z<Config>;
|
|
|
1048
1140
|
*/
|
|
1049
1141
|
declare function apply(ctx: Context, config: Config): void;
|
|
1050
1142
|
//#endregion
|
|
1051
|
-
export { AI_VARIANT, type AppVersionInfo, CN_VARIANT, Config, FALLBACK_WORKBUDDY_AI_MODELS, FALLBACK_WORKBUDDY_MODELS, PROBE_EFFORT_CANDIDATES, type ProbeAttempt, type ProbeOutcome, type ProbeSender, type UpstreamErrorKind, WORKBUDDY_AI_SETTINGS_NS, WORKBUDDY_APP_VERSION_FILENAME, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_CATALOG_FILENAME, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_PROBE_FILENAME, WORKBUDDY_PROVIDER, WORKBUDDY_SETTINGS_NS, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, WORKBUDDY_VARIANTS, type WorkBuddyAdapter, type WorkBuddyAppVersionSource, type WorkBuddyAuthStatus, WorkBuddyCatalog, type WorkBuddyCatalogFetch, WorkBuddyCatalogStore, type WorkBuddyChatResult, type WorkBuddyCredential, WorkBuddyCredentialStore, type WorkBuddyCredits, type WorkBuddyEffort, type WorkBuddyHostHeartbeat, type WorkBuddyModelBilling, type WorkBuddyModelInfo, type WorkBuddyModelReasoning, type WorkBuddyProbeRecord, WorkBuddyProbeService, type WorkBuddyProbeStatus, WorkBuddyProbeStore, type WorkBuddyProbeValidation, type WorkBuddyPromotion, type WorkBuddyRefreshOutcome, type WorkBuddyShim, WorkBuddyUpstreamClient, type WorkBuddyUpstreamModel, type WorkBuddyVariant, appUserAgent, apply, classifyUpstreamError, clearHostHeartbeat, createWorkBuddyAdapter, createWorkBuddyShim, defaultDesktopAuthCandidates, defaultDesktopAuthPath, desktopAuthCandidatesFor, fingerprintModel, inject, installedAppVersion, isHeartbeatProcessAlive, modelWithCurrentPromotion, name, normalizeCredits, parseModelCatalog, parseWorkBuddyAuth, prepareChatBody, prepareInternationalChatBody, probeModel, processStartTimeMs, randomSentinel, readBundleVersion, readHostHeartbeat, regionOf, resolveAppVersion, validAppVersion, variantFor, workbuddyCatalogPath, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath, workbuddyProbePath };
|
|
1143
|
+
export { AI_VARIANT, type AppVersionInfo, CN_APP_VERSION_FILENAME, CN_VARIANT, type ChatIdentity, Config, FALLBACK_CN_APP_VERSION, FALLBACK_WORKBUDDY_AI_MODELS, FALLBACK_WORKBUDDY_MODELS, PROBE_EFFORT_CANDIDATES, type ProbeAttempt, type ProbeOutcome, type ProbeSender, type ResolveChatIdentityOptions, type UpstreamErrorKind, WORKBUDDY_AI_SETTINGS_NS, WORKBUDDY_APP_VERSION_FILENAME, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_CATALOG_FILENAME, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_PROBE_FILENAME, WORKBUDDY_PROVIDER, WORKBUDDY_SETTINGS_NS, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, WORKBUDDY_VARIANTS, type WorkBuddyAdapter, type WorkBuddyAppVersionSource, type WorkBuddyAuthStatus, WorkBuddyCatalog, type WorkBuddyCatalogFetch, WorkBuddyCatalogStore, type WorkBuddyChatResult, type WorkBuddyCredential, WorkBuddyCredentialStore, type WorkBuddyCredits, type WorkBuddyEffort, type WorkBuddyHostHeartbeat, type WorkBuddyModelBilling, type WorkBuddyModelInfo, type WorkBuddyModelReasoning, type WorkBuddyProbeRecord, WorkBuddyProbeService, type WorkBuddyProbeStatus, WorkBuddyProbeStore, type WorkBuddyProbeValidation, type WorkBuddyPromotion, type WorkBuddyRefreshOutcome, type WorkBuddyShim, WorkBuddyUpstreamClient, type WorkBuddyUpstreamModel, type WorkBuddyVariant, appUserAgent, apply, chatUserAgent, classifyUpstreamError, clearHostHeartbeat, createWorkBuddyAdapter, createWorkBuddyShim, defaultDesktopAuthCandidates, defaultDesktopAuthPath, desktopAuthCandidatesFor, fallbackChatIdentity, fingerprintModel, inject, installedAppVersion, isHeartbeatProcessAlive, modelWithCurrentPromotion, name, normalizeCredits, parseModelCatalog, parseWorkBuddyAuth, prepareChatBody, prepareInternationalChatBody, probeModel, processStartTimeMs, randomSentinel, readBundleVersion, readCliVersion, readHostHeartbeat, regionOf, resolveAppVersion, resolveChatIdentity, validAppVersion, validCliVersion, variantFor, workbuddyCatalogPath, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath, workbuddyProbePath };
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as parseModelCatalog, B as
|
|
1
|
+
import { A as parseModelCatalog, B as fallbackChatIdentity, C as desktopAuthCandidatesFor, D as classifyUpstreamError, E as WorkBuddyUpstreamClient, F as probeModel, G as appUserAgent, H as resolveChatIdentity, I as randomSentinel, J as resolveAppVersion, K as installedAppVersion, L as CN_APP_VERSION_FILENAME, M as prepareInternationalChatBody, N as regionOf, O as modelWithCurrentPromotion, P as PROBE_EFFORT_CANDIDATES, R as FALLBACK_CN_APP_VERSION, S as defaultDesktopAuthPath, T as workbuddyOwnAuthPath, U as validCliVersion, V as readCliVersion, W as WORKBUDDY_APP_VERSION_FILENAME, Y as validAppVersion, _ as WorkBuddyCatalog, a as WORKBUDDY_HOST_HEARTBEAT_FILENAME, b as WorkBuddyCredentialStore, c as processStartTimeMs, d as writeHostHeartbeat, f as WORKBUDDY_CONNECT_VERSION, g as FALLBACK_WORKBUDDY_MODELS, h as FALLBACK_WORKBUDDY_AI_MODELS, i as variantFor, j as prepareChatBody, k as normalizeCredits, l as readHostHeartbeat, n as CN_VARIANT, o as clearHostHeartbeat, q as readBundleVersion, r as WORKBUDDY_VARIANTS, s as isHeartbeatProcessAlive, t as AI_VARIANT, u as workbuddyHostHeartbeatPath, v as WORKBUDDY_AUTH_FILENAME, w as parseWorkBuddyAuth, x as defaultDesktopAuthCandidates, y as WORKBUDDY_AUTH_FILE_ENV, z as chatUserAgent } from "./variants-CExA7lJt.js";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
|
|
@@ -1794,4 +1794,4 @@ function apply(ctx, config) {
|
|
|
1794
1794
|
});
|
|
1795
1795
|
}
|
|
1796
1796
|
//#endregion
|
|
1797
|
-
export { AI_VARIANT, CN_VARIANT, Config, FALLBACK_WORKBUDDY_AI_MODELS, FALLBACK_WORKBUDDY_MODELS, PROBE_EFFORT_CANDIDATES, WORKBUDDY_AI_SETTINGS_NS, WORKBUDDY_APP_VERSION_FILENAME, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_CATALOG_FILENAME, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_PROBE_FILENAME, WORKBUDDY_PROVIDER, WORKBUDDY_SETTINGS_NS, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, WORKBUDDY_VARIANTS, WorkBuddyCatalog, WorkBuddyCatalogStore, WorkBuddyCredentialStore, WorkBuddyProbeService, WorkBuddyProbeStore, WorkBuddyUpstreamClient, appUserAgent, apply, classifyUpstreamError, clearHostHeartbeat, createWorkBuddyAdapter, createWorkBuddyShim, defaultDesktopAuthCandidates, defaultDesktopAuthPath, desktopAuthCandidatesFor, fingerprintModel, inject, installedAppVersion, isHeartbeatProcessAlive, modelWithCurrentPromotion, name, normalizeCredits, parseModelCatalog, parseWorkBuddyAuth, prepareChatBody, prepareInternationalChatBody, probeModel, processStartTimeMs, randomSentinel, readBundleVersion, readHostHeartbeat, regionOf, resolveAppVersion, validAppVersion, variantFor, workbuddyCatalogPath, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath, workbuddyProbePath };
|
|
1797
|
+
export { AI_VARIANT, CN_APP_VERSION_FILENAME, CN_VARIANT, Config, FALLBACK_CN_APP_VERSION, FALLBACK_WORKBUDDY_AI_MODELS, FALLBACK_WORKBUDDY_MODELS, PROBE_EFFORT_CANDIDATES, WORKBUDDY_AI_SETTINGS_NS, WORKBUDDY_APP_VERSION_FILENAME, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_CATALOG_FILENAME, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_PROBE_FILENAME, WORKBUDDY_PROVIDER, WORKBUDDY_SETTINGS_NS, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, WORKBUDDY_VARIANTS, WorkBuddyCatalog, WorkBuddyCatalogStore, WorkBuddyCredentialStore, WorkBuddyProbeService, WorkBuddyProbeStore, WorkBuddyUpstreamClient, appUserAgent, apply, chatUserAgent, classifyUpstreamError, clearHostHeartbeat, createWorkBuddyAdapter, createWorkBuddyShim, defaultDesktopAuthCandidates, defaultDesktopAuthPath, desktopAuthCandidatesFor, fallbackChatIdentity, fingerprintModel, inject, installedAppVersion, isHeartbeatProcessAlive, modelWithCurrentPromotion, name, normalizeCredits, parseModelCatalog, parseWorkBuddyAuth, prepareChatBody, prepareInternationalChatBody, probeModel, processStartTimeMs, randomSentinel, readBundleVersion, readCliVersion, readHostHeartbeat, regionOf, resolveAppVersion, resolveChatIdentity, validAppVersion, validCliVersion, variantFor, workbuddyCatalogPath, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath, workbuddyProbePath };
|
|
@@ -45,7 +45,7 @@ function validAppVersion(value) {
|
|
|
45
45
|
return typeof value === "string" && /^\d{1,6}(?:\.\d{1,6}){1,3}$/u.test(value);
|
|
46
46
|
}
|
|
47
47
|
/** macOS App-bundle roots: system-wide first, then the user's own install. */
|
|
48
|
-
function macAppRoots() {
|
|
48
|
+
function macAppRoots$1() {
|
|
49
49
|
return ["/Applications", join(homedir(), "Applications")];
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
@@ -76,7 +76,7 @@ async function readBundleVersion(plistPath) {
|
|
|
76
76
|
*/
|
|
77
77
|
async function installedAppVersion() {
|
|
78
78
|
if (process.platform !== "darwin") return void 0;
|
|
79
|
-
for (const root of macAppRoots()) {
|
|
79
|
+
for (const root of macAppRoots$1()) {
|
|
80
80
|
const bundle = join(root, "WorkBuddy AI.app");
|
|
81
81
|
const version = await readBundleVersion(join(bundle, "Contents", "Info.plist"));
|
|
82
82
|
if (version !== void 0) return {
|
|
@@ -145,6 +145,203 @@ function appUserAgent(version) {
|
|
|
145
145
|
return `WorkBuddyAI/${version}`;
|
|
146
146
|
}
|
|
147
147
|
//#endregion
|
|
148
|
+
//#region src/client-identity.ts
|
|
149
|
+
/**
|
|
150
|
+
* The desktop-client identity chat requests present as (phase 1 of
|
|
151
|
+
* `docs/upstream-identity-alignment-plan.md`).
|
|
152
|
+
*
|
|
153
|
+
* Chat and its probe sibling carry the User-Agent shape the official desktop
|
|
154
|
+
* client composes — `WorkBuddy/<v> <product>/<v> CLI/<cli>` — where the
|
|
155
|
+
* product token names the app that owns the region's requests: `WorkBuddy`
|
|
156
|
+
* for CN, `WorkBuddy AI` for international. Versions come from the installed
|
|
157
|
+
* App when it can be read, degrade to a per-region saved value, and finally
|
|
158
|
+
* to a compiled-in constant. A CLI version that does not resolve drops the
|
|
159
|
+
* `CLI/…` token instead of inventing one (the official client's own rule for
|
|
160
|
+
* a missing extension).
|
|
161
|
+
*
|
|
162
|
+
* Scope: chat and probe requests ONLY. Refresh, catalog, and billing keep
|
|
163
|
+
* the headers they have always sent; the plan holds the blast radius to this
|
|
164
|
+
* one variable so the live verification matrix stays readable.
|
|
165
|
+
*
|
|
166
|
+
* @module dsh-workbuddy-connect/client-identity
|
|
167
|
+
*/
|
|
168
|
+
/**
|
|
169
|
+
* Compiled-in CN fallback for the `WorkBuddy/<v>` tokens.
|
|
170
|
+
*
|
|
171
|
+
* Observed on the CN desktop app installed here (research §3.1, verified
|
|
172
|
+
* 2026-09-11); like the international fallback it is a shape requirement,
|
|
173
|
+
* not a currency claim — the gateway has not been observed to branch on it.
|
|
174
|
+
*/
|
|
175
|
+
const FALLBACK_CN_APP_VERSION = "5.5.6";
|
|
176
|
+
/**
|
|
177
|
+
* Basename of the CN saved-version cache under `$DSH_HOME`.
|
|
178
|
+
*
|
|
179
|
+
* Deliberately not the international `.workbuddy-ai-version.json`: that file
|
|
180
|
+
* feeds the international catalog's User-Agent, and a CN App writing its
|
|
181
|
+
* version into it would relabel that request. The two caches stay isolated
|
|
182
|
+
* the way the per-variant catalog files are.
|
|
183
|
+
*/
|
|
184
|
+
const CN_APP_VERSION_FILENAME = ".workbuddy-app-version.json";
|
|
185
|
+
/**
|
|
186
|
+
* Whether a value is a CLI version that may reach a header.
|
|
187
|
+
*
|
|
188
|
+
* Tolerates a prerelease suffix (`2.137.1-rc.1`) because the bundled CLI's
|
|
189
|
+
* own metadata uses that spelling; anything with whitespace, CR or LF never
|
|
190
|
+
* passes — the value is interpolated into an HTTP header.
|
|
191
|
+
*/
|
|
192
|
+
function validCliVersion(value) {
|
|
193
|
+
return typeof value === "string" && /^\d{1,6}(?:\.\d{1,6}){1,3}(?:-[0-9A-Za-z.]+)?$/u.test(value);
|
|
194
|
+
}
|
|
195
|
+
/** macOS App-bundle roots, searched in the order `app-version.ts` uses. */
|
|
196
|
+
function macAppRoots() {
|
|
197
|
+
return ["/Applications", join(homedir(), "Applications")];
|
|
198
|
+
}
|
|
199
|
+
/** Path of the bundled agent CLI's package.json inside an App bundle. */
|
|
200
|
+
function cliPackagePath(bundle) {
|
|
201
|
+
return join(bundle, "Contents", "Resources", "app.asar.unpacked", "cli", "package.json");
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* The bundled agent CLI's real version, or `undefined` when it does not resolve.
|
|
205
|
+
*
|
|
206
|
+
* `cli/package.json` ships a `0.0.0` placeholder in `version` with the real
|
|
207
|
+
* version in `publishConfig.customPackage.version`; a valid non-placeholder
|
|
208
|
+
* `version` wins, otherwise the custom-package value applies, and unreadable
|
|
209
|
+
* or invalid metadata yields `undefined` (the caller drops the `CLI/…` UA
|
|
210
|
+
* token rather than guessing).
|
|
211
|
+
*/
|
|
212
|
+
async function readCliVersion(bundle) {
|
|
213
|
+
let document;
|
|
214
|
+
try {
|
|
215
|
+
document = JSON.parse(await readFile(cliPackagePath(bundle), "utf8"));
|
|
216
|
+
} catch {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (typeof document !== "object" || document === null || Array.isArray(document)) return void 0;
|
|
220
|
+
const pkg = document;
|
|
221
|
+
const declared = pkg["version"];
|
|
222
|
+
if (validCliVersion(declared) && declared !== "0.0.0") return declared;
|
|
223
|
+
const publishConfig = pkg["publishConfig"];
|
|
224
|
+
const customPackage = typeof publishConfig === "object" && publishConfig !== null && !Array.isArray(publishConfig) ? publishConfig : void 0;
|
|
225
|
+
const customVersion = (typeof customPackage?.["customPackage"] === "object" && customPackage["customPackage"] !== null && !Array.isArray(customPackage["customPackage"]) ? customPackage["customPackage"] : void 0)?.["version"];
|
|
226
|
+
return validCliVersion(customVersion) ? customVersion : void 0;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Build the chat User-Agent for one region.
|
|
230
|
+
*
|
|
231
|
+
* Throws on an invalid version rather than interpolating one into a header;
|
|
232
|
+
* `resolveChatIdentity` never produces such an identity, so the throw is a
|
|
233
|
+
* last gate against future call-site mistakes, not an expected path.
|
|
234
|
+
*/
|
|
235
|
+
function chatUserAgent(identity, region) {
|
|
236
|
+
if (!validAppVersion(identity.clientVersion)) throw new Error(`invalid client version for chat User-Agent: ${JSON.stringify(identity.clientVersion)}`);
|
|
237
|
+
if (identity.cliVersion !== void 0 && !validCliVersion(identity.cliVersion)) throw new Error(`invalid CLI version for chat User-Agent: ${JSON.stringify(identity.cliVersion)}`);
|
|
238
|
+
const product = region === "global" ? "WorkBuddy AI" : "WorkBuddy";
|
|
239
|
+
const parts = [`WorkBuddy/${identity.clientVersion}`, `${product}/${identity.clientVersion}`];
|
|
240
|
+
if (identity.cliVersion !== void 0) parts.push(`CLI/${identity.cliVersion}`);
|
|
241
|
+
return parts.join(" ");
|
|
242
|
+
}
|
|
243
|
+
/** The installed CN desktop bundle, or `undefined` when it is not installed (or not readable). */
|
|
244
|
+
async function installedCnApp() {
|
|
245
|
+
if (process.platform !== "darwin") return void 0;
|
|
246
|
+
for (const root of macAppRoots()) {
|
|
247
|
+
const bundle = join(root, "WorkBuddy.app");
|
|
248
|
+
const version = await readBundleVersion(join(bundle, "Contents", "Info.plist"));
|
|
249
|
+
if (version !== void 0) return {
|
|
250
|
+
version,
|
|
251
|
+
bundle
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/** Default CN saved-cache path. */
|
|
256
|
+
function cnSavedVersionPath() {
|
|
257
|
+
return join(resolveDshHome(), CN_APP_VERSION_FILENAME);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Resolve the chat identity for one region: installed App → region's saved
|
|
261
|
+
* value → compiled-in fallback. Never throws — a missing App, an unreadable
|
|
262
|
+
* plist, a failed cache write, or a reader that throws outright all degrade
|
|
263
|
+
* to {@link fallbackChatIdentity}; resolution never blocks a message.
|
|
264
|
+
*
|
|
265
|
+
* The production path caches per region (a message must not re-read the
|
|
266
|
+
* install tree); any injected option bypasses the cache entirely so tests
|
|
267
|
+
* with different readers cannot observe each other's resolutions.
|
|
268
|
+
*/
|
|
269
|
+
async function resolveChatIdentity(region, options = {}) {
|
|
270
|
+
const injectable = options.installedCn !== void 0 || options.resolveIntl !== void 0 || options.cliVersion !== void 0 || options.cnSavedPath !== void 0;
|
|
271
|
+
if (!injectable) {
|
|
272
|
+
const cached = cache.get(region);
|
|
273
|
+
if (cached !== void 0) return cached;
|
|
274
|
+
}
|
|
275
|
+
let identity;
|
|
276
|
+
try {
|
|
277
|
+
identity = region === "global" ? await resolveGlobalIdentity(options) : await resolveCnIdentity(options);
|
|
278
|
+
} catch {
|
|
279
|
+
return fallbackChatIdentity(region);
|
|
280
|
+
}
|
|
281
|
+
if (!injectable) cache.set(region, identity);
|
|
282
|
+
return identity;
|
|
283
|
+
}
|
|
284
|
+
const cache = /* @__PURE__ */ new Map();
|
|
285
|
+
/**
|
|
286
|
+
* The region's compiled-in fallback identity: the desktop form with the
|
|
287
|
+
* built-in version and no `CLI/…` segment. This is the single degraded
|
|
288
|
+
* shape every failure path converges on — a thrown reader, an unreadable
|
|
289
|
+
* bundle, or a missing cache all present this, never the legacy CLI UA.
|
|
290
|
+
*/
|
|
291
|
+
function fallbackChatIdentity(region) {
|
|
292
|
+
return { clientVersion: region === "global" ? FALLBACK_APP_VERSION : FALLBACK_CN_APP_VERSION };
|
|
293
|
+
}
|
|
294
|
+
/** CN: installed `WorkBuddy.app` → CN saved cache → CN fallback. */
|
|
295
|
+
async function resolveCnIdentity(options) {
|
|
296
|
+
const savedPath = options.cnSavedPath ?? cnSavedVersionPath();
|
|
297
|
+
const installed = await (options.installedCn ?? installedCnApp)();
|
|
298
|
+
if (installed !== void 0 && validAppVersion(installed.version)) {
|
|
299
|
+
const cliVersion = await (options.cliVersion ?? readCliVersion)(installed.bundle);
|
|
300
|
+
const identity = {
|
|
301
|
+
clientVersion: installed.version,
|
|
302
|
+
...cliVersion !== void 0 && validCliVersion(cliVersion) ? { cliVersion } : {}
|
|
303
|
+
};
|
|
304
|
+
try {
|
|
305
|
+
await writeFileAtomic(savedPath, `${JSON.stringify({
|
|
306
|
+
version: identity.clientVersion,
|
|
307
|
+
observedAt: Date.now()
|
|
308
|
+
}, null, 2)}\n`, {
|
|
309
|
+
mode: 384,
|
|
310
|
+
dirMode: 448
|
|
311
|
+
});
|
|
312
|
+
} catch {}
|
|
313
|
+
return identity;
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
const saved = JSON.parse(await readFile(savedPath, "utf8"));
|
|
317
|
+
if (typeof saved === "object" && saved !== null && !Array.isArray(saved)) {
|
|
318
|
+
const document = saved;
|
|
319
|
+
if (validAppVersion(document["version"])) return { clientVersion: document["version"] };
|
|
320
|
+
}
|
|
321
|
+
} catch {}
|
|
322
|
+
return fallbackChatIdentity("cn");
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* International: reuse `app-version.ts`'s installed → saved → fallback chain
|
|
326
|
+
* (its cache format and the catalog's version source stay untouched). The
|
|
327
|
+
* CLI version is read only when that chain reports the installed bundle; a
|
|
328
|
+
* saved or fallback resolution has no bundle path and drops the `CLI/…` token.
|
|
329
|
+
* The CN cache is never read or written on this path.
|
|
330
|
+
*/
|
|
331
|
+
async function resolveGlobalIdentity(options) {
|
|
332
|
+
const info = await (options.resolveIntl ?? resolveAppVersion)();
|
|
333
|
+
const clientVersion = validAppVersion(info.version) ? info.version : FALLBACK_APP_VERSION;
|
|
334
|
+
let cliVersion;
|
|
335
|
+
if (info.bundle !== void 0) {
|
|
336
|
+
const read = await (options.cliVersion ?? readCliVersion)(info.bundle);
|
|
337
|
+
if (read !== void 0 && validCliVersion(read)) cliVersion = read;
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
clientVersion,
|
|
341
|
+
...cliVersion === void 0 ? {} : { cliVersion }
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
//#endregion
|
|
148
345
|
//#region src/probe.ts
|
|
149
346
|
/**
|
|
150
347
|
* The reasoning-effort probe: decide whether a model's `reasoning_effort`
|
|
@@ -288,6 +485,7 @@ async function probeModel(options) {
|
|
|
288
485
|
const CN_CHAT_BASE = "https://copilot.tencent.com";
|
|
289
486
|
const CN_BILLING_BASE = "https://www.codebuddy.cn";
|
|
290
487
|
const GLOBAL_BASE = "https://www.workbuddy.ai";
|
|
488
|
+
/** Shared CLI-form User-Agent for refresh and the CN catalog; chat and probe present the desktop identity (client-identity.ts). */
|
|
291
489
|
const CLIENT_UA = "CLI/2.63.2 CodeBuddy/2.63.2";
|
|
292
490
|
const JSON_TIMEOUT_MS = 3e4;
|
|
293
491
|
const ERROR_BODY_LIMIT = 4096;
|
|
@@ -427,10 +625,17 @@ function commonHeaders(credential) {
|
|
|
427
625
|
"User-Agent": CLIENT_UA
|
|
428
626
|
};
|
|
429
627
|
}
|
|
430
|
-
/**
|
|
431
|
-
|
|
628
|
+
/**
|
|
629
|
+
* Chat request headers, including the X-No-* conventions the official CLI uses.
|
|
630
|
+
*
|
|
631
|
+
* `userAgent` carries the desktop identity for chat and probe requests; when
|
|
632
|
+
* it is absent the shared CLI-form UA applies. Refresh shares `commonHeaders`
|
|
633
|
+
* but never this override, so the two paths cannot drift into each other.
|
|
634
|
+
*/
|
|
635
|
+
function chatHeaders(credential, userAgent) {
|
|
432
636
|
return {
|
|
433
637
|
...commonHeaders(credential),
|
|
638
|
+
...userAgent === void 0 ? {} : { "User-Agent": userAgent },
|
|
434
639
|
"Content-Type": "application/json",
|
|
435
640
|
...credential.uid === "" ? { "X-No-User-Id": "1" } : { "X-User-Id": credential.uid },
|
|
436
641
|
...credential.enterpriseId === void 0 || credential.enterpriseId === "" ? { "X-No-Enterprise-Id": "1" } : { "X-Enterprise-Id": credential.enterpriseId },
|
|
@@ -568,22 +773,32 @@ var WorkBuddyUpstreamClient = class {
|
|
|
568
773
|
* Injectable so tests never read the real filesystem.
|
|
569
774
|
*/
|
|
570
775
|
resolveAppVersion;
|
|
776
|
+
/** Chat-identity resolver; see {@link WorkBuddyUpstreamClientOptions.resolveChatIdentity}. */
|
|
777
|
+
resolveChatIdentity;
|
|
571
778
|
/** Provenance of the most recent successful catalog fetch, for the card. */
|
|
572
779
|
lastCatalog;
|
|
573
780
|
constructor(options = {}) {
|
|
574
781
|
this.resolveAppVersion = options.resolveAppVersion ?? (() => resolveAppVersion());
|
|
782
|
+
this.resolveChatIdentity = options.resolveChatIdentity ?? ((region) => resolveChatIdentity(region));
|
|
575
783
|
}
|
|
576
784
|
/** POST the chat endpoint; a successful answer is the raw SSE response. */
|
|
577
785
|
async chatStream(credential, bodyJson, signal) {
|
|
786
|
+
const region = regionOf(credential.domain);
|
|
787
|
+
let userAgent;
|
|
788
|
+
try {
|
|
789
|
+
userAgent = chatUserAgent(await this.resolveChatIdentity(region), region);
|
|
790
|
+
} catch {
|
|
791
|
+
userAgent = chatUserAgent(fallbackChatIdentity(region), region);
|
|
792
|
+
}
|
|
578
793
|
let response;
|
|
579
794
|
try {
|
|
580
795
|
response = await fetch(`${chatBase(credential)}/v2/chat/completions`, {
|
|
581
796
|
method: "POST",
|
|
582
797
|
headers: {
|
|
583
|
-
...chatHeaders(credential),
|
|
798
|
+
...chatHeaders(credential, userAgent),
|
|
584
799
|
"Authorization": `Bearer ${credential.accessToken}`
|
|
585
800
|
},
|
|
586
|
-
body:
|
|
801
|
+
body: region === "global" ? prepareInternationalChatBody(bodyJson) : bodyJson,
|
|
587
802
|
...signal === void 0 ? {} : { signal }
|
|
588
803
|
});
|
|
589
804
|
} catch (error) {
|
|
@@ -751,6 +966,12 @@ var WorkBuddyUpstreamClient = class {
|
|
|
751
966
|
*/
|
|
752
967
|
async probeEffort(credential, model, effort, signal) {
|
|
753
968
|
const international = regionOf(credential.domain) === "global";
|
|
969
|
+
let userAgent;
|
|
970
|
+
try {
|
|
971
|
+
userAgent = chatUserAgent(await this.resolveChatIdentity(international ? "global" : "cn"), international ? "global" : "cn");
|
|
972
|
+
} catch {
|
|
973
|
+
userAgent = chatUserAgent(fallbackChatIdentity(international ? "global" : "cn"), international ? "global" : "cn");
|
|
974
|
+
}
|
|
754
975
|
const payload = {
|
|
755
976
|
model,
|
|
756
977
|
stream: true,
|
|
@@ -769,7 +990,7 @@ var WorkBuddyUpstreamClient = class {
|
|
|
769
990
|
response = await fetch(`${chatBase(credential)}/v2/chat/completions`, {
|
|
770
991
|
method: "POST",
|
|
771
992
|
headers: {
|
|
772
|
-
...chatHeaders(credential),
|
|
993
|
+
...chatHeaders(credential, userAgent),
|
|
773
994
|
"Authorization": `Bearer ${credential.accessToken}`
|
|
774
995
|
},
|
|
775
996
|
body: JSON.stringify(payload),
|
|
@@ -1415,7 +1636,7 @@ const FALLBACK_WORKBUDDY_MODELS = [
|
|
|
1415
1636
|
},
|
|
1416
1637
|
{
|
|
1417
1638
|
id: "hy3-x",
|
|
1418
|
-
name: "Hy3",
|
|
1639
|
+
name: "Hy3-X",
|
|
1419
1640
|
contextWindow: 192e3,
|
|
1420
1641
|
maxTokens: 64e3,
|
|
1421
1642
|
supportsImages: true,
|
|
@@ -2123,7 +2344,7 @@ const WORKBUDDY_AI_STATUS_PATH = "/plugins/dsh-workbuddy-connect/ai/status";
|
|
|
2123
2344
|
const WORKBUDDY_AI_PROBE_PATH = "/plugins/dsh-workbuddy-connect/ai/probe";
|
|
2124
2345
|
//#endregion
|
|
2125
2346
|
//#region src/version.ts
|
|
2126
|
-
const WORKBUDDY_CONNECT_VERSION = "0.5.
|
|
2347
|
+
const WORKBUDDY_CONNECT_VERSION = "0.5.2";
|
|
2127
2348
|
//#endregion
|
|
2128
2349
|
//#region src/host-heartbeat.ts
|
|
2129
2350
|
/**
|
|
@@ -2318,4 +2539,4 @@ function variantFor(id) {
|
|
|
2318
2539
|
return WORKBUDDY_VARIANTS.find((variant) => variant.id === id);
|
|
2319
2540
|
}
|
|
2320
2541
|
//#endregion
|
|
2321
|
-
export { parseModelCatalog as A,
|
|
2542
|
+
export { parseModelCatalog as A, fallbackChatIdentity as B, desktopAuthCandidatesFor as C, classifyUpstreamError as D, WorkBuddyUpstreamClient as E, probeModel as F, appUserAgent as G, resolveChatIdentity as H, randomSentinel as I, resolveAppVersion as J, installedAppVersion as K, CN_APP_VERSION_FILENAME as L, prepareInternationalChatBody as M, regionOf as N, modelWithCurrentPromotion as O, PROBE_EFFORT_CANDIDATES as P, FALLBACK_CN_APP_VERSION as R, defaultDesktopAuthPath as S, workbuddyOwnAuthPath as T, validCliVersion as U, readCliVersion as V, WORKBUDDY_APP_VERSION_FILENAME as W, validAppVersion as Y, WorkBuddyCatalog as _, WORKBUDDY_HOST_HEARTBEAT_FILENAME as a, WorkBuddyCredentialStore as b, processStartTimeMs as c, writeHostHeartbeat as d, WORKBUDDY_CONNECT_VERSION as f, FALLBACK_WORKBUDDY_MODELS as g, FALLBACK_WORKBUDDY_AI_MODELS as h, variantFor as i, prepareChatBody as j, normalizeCredits as k, readHostHeartbeat as l, WORKBUDDY_STATUS_PATH as m, CN_VARIANT as n, clearHostHeartbeat as o, WORKBUDDY_PROBE_PATH as p, readBundleVersion as q, WORKBUDDY_VARIANTS as r, isHeartbeatProcessAlive as s, AI_VARIANT as t, workbuddyHostHeartbeatPath as u, WORKBUDDY_AUTH_FILENAME as v, parseWorkBuddyAuth as w, defaultDesktopAuthCandidates as x, WORKBUDDY_AUTH_FILE_ENV as y, chatUserAgent as z };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-workbuddy-connect",
|
|
3
3
|
"displayName": "DSH WorkBuddy Connect",
|
|
4
4
|
"description": "将 WorkBuddy 桌面 App 包含的模型自动接入 DeepSeek Harness — bring WorkBuddy desktop app models into DeepSeek Harness with zero configuration.",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.2",
|
|
6
6
|
"author": "corrinehu",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"dsh-plugin",
|
|
@@ -56,6 +56,8 @@
|
|
|
56
56
|
},
|
|
57
57
|
"client": {
|
|
58
58
|
"inject": [
|
|
59
|
+
"@deepseek-ai/dsh-api-remotes",
|
|
60
|
+
"@deepseek-ai/dsh-api-session-controller",
|
|
59
61
|
"@deepseek-ai/dsh-client-ui-renderer",
|
|
60
62
|
"@deepseek-ai/dsh-client-ui-settings-plugins",
|
|
61
63
|
"@deepseek-ai/dsh-client-ui-conversation",
|