dsh-connect-workbuddy 4.4.6 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -8
- package/lib/bin.js +1 -1
- package/lib/client.js +191 -6
- package/lib/{host-heartbeat-B2jrUDhg.js → host-heartbeat-DvcOd-3C.js} +1 -1
- package/lib/index.d.ts +40 -0
- package/lib/index.js +20 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.5.0 (2026-10-14)
|
|
4
|
+
|
|
5
|
+
> 版本号是 **minor** 而不是 patch:这一版新增了一个**用户可见的设置**(积分读数的位置)和一个**新的渲染席位**,而且配置 schema 多了一个字段 —— 按语义化版本这是特性,不是修错。`4.4.6` 之后没有任何以 4.4.7 发布过的产物或 Release(tag 已撤回重打)。
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- **积分读数只有一个固定位置:装了别的插件后它就不显示了 —— 现在位置可选(输入框工具栏 / 输入框下方)**(issue #62 报告者)
|
|
10
|
+
- **现象**:报告者装了第三方插件(`dsh-auto-continue`)之后,对话输入框里的 WorkBuddy 积分余额消失。根因是那个位置本身:`conversation.input.left`(输入框工具栏)是一个**共享的 list 插槽**,shell 自己的控件和任何其他 bundle 都注册在那里 —— 而 list 插槽无法保证每个占用者都可见,别人挤进来就可能把本插件的读数挤没。原注释里其实已经记着同类问题(更早放在侧边栏底部时,两个连接器插件互相挤压,才迁到输入框这一行)。
|
|
11
|
+
- **修法**:把「位置」做成**每区域一个选项**,两个席位都注册、由 gate 决定在哪个席位渲染:
|
|
12
|
+
- `composer`(默认)—— 输入框工具栏 `conversation.input.left`,与模型选择同一行;
|
|
13
|
+
- `stats` —— 输入框下方 `conversation.composer.dock`,shell 自己渲染「轮次 / 步数 · tok/s · token · 缓存命中」那一行;`order: 10` 排在 shell 的统计之后,所以官方那几颗统计仍在最左,读数是**追加**而不是顶替。
|
|
14
|
+
- 选默认值是为了**升级不动任何人的界面**:设置在 schema 里默认 `composer`,升级前写下的 profile 读出来仍是 `composer`。
|
|
15
|
+
- **为什么两个席位都注册**:位置这种东西用户是「一边看着结果一边改」的,注册哪个席位取决于设置就意味着每次改设置都要**重新注册插槽**;而本仓库的注释里已经写明这种绑定是脆的(晚到的 settings 命名空间必须产生新的注册而不是改闭包)。现在**决定权在 gate 里一处**:席位不符就 `return null`,另一个席位照常渲染。
|
|
16
|
+
- **判定的顺序**:先归属(是不是本插件的 provider)、再两个开关(区域是否启用、读数是否显示)、**最后**才是席位。所以一个被关掉的区域在**两个席位都不渲染**,而不是先在非当前席位画一帧再被拒。
|
|
17
|
+
- **卡片新增一个两选一控件**(就在「对话框显示积分」那个勾选旁边,只作用于当前标签页的区域):读取走 Host 的 usage 应答(与那个勾选同源,因为浏览器 settings 镜像在本命名空间上是过期的那份),写完**重新读一次 Host** —— 否则控件会一直显示旧席位,看起来像没生效。
|
|
18
|
+
- **守卫**:`tests/issue62-credit-position.spec.tsx`(9 例)覆盖:默认 `composer`(含升级前的 profile 与手改的未知值)、按区域而非全局读取、写入只改该区域该字段(模型列表/预算/兄弟区域都不动)、席位与显示开关相互独立、gate 在**指定席位渲染 / 另一席位渲染 0 个 / 两个席位都不渲染(隐藏或区域关闭)/ 别的 provider 两个席位都不渲染**。**变异验证**:把 gate 的席位判定删掉(两个席位都渲染)→ 2 例红;把读取的默认值去掉(升级前的 profile 读成 stats)→ 2 例红。
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
- **新增「上游请求体构成」诊断(`WORKBUDDY_BODY_REPORT`),用于定案 issue #59:两份请求体到底是不是同一份历史。**(issue #59)
|
|
22
|
+
- **为什么需要**:报告里同一份历史被国内网关算约 70.8 万 token、国际网关算约 137.4 万(1.94 倍),切区域即 `code 11115 prompt is too long`。两条独立测量已经把范围压到唯一一个问题:报告者在**两个区域**跑本仓库的内容类别探针,逐字节相同的散文/代码/JSON 六格比值全在 **0.98~1.06**(同一段字节两边计价一致);代码链路核对又表明**历史 reasoning 根本没进请求体**(pi-ai 只在 `compat.requiresThinkingAsText` 为真时把 `thinking` 当文本转发,本插件从未设置;`reasoning_details` 那条路需要流式解析从不写出的签名)。**剩下唯一可能是两份请求体不同**,而这件事**不需要任何区域有积分**就能查:请求体是本地拼好的,`14018 Credits exhausted` 是发出去之后才可能发生的。
|
|
23
|
+
- **做法**:`src/body-report.ts` 在 shim 把 body 交给上游**之前**记录它的**构成** —— 每条消息的 `role`、逐块的类型(`system`/`text`/`tool-call`/`tool-result`/`reasoning`/`image`)、字符数与其中**中日韩字符**数(实测中文 0.669、英文 0.224 token/字符,差 3 倍,混在一起看不出来)、`tools` 数组、`messages.length` 与 body 总字节数。按区域写 `<dir>/<region>.jsonl`,一次请求一行(一轮对话有很多次上游调用,**最后一次**才是要比的那份)。
|
|
24
|
+
- **只记计数、不记文本**:不写任何消息内容、账号名或令牌 —— 一个要被贴进公开 issue 的诊断不能有泄漏用户会话的能力。`tests/issue59-body-report.spec.ts` 里有一条专门的断言:用带特征串的 body 生成报告后,序列化结果**不含**该特征串(变异验证:让 `block()` 把 `text` 一起带上 → 该例与 reasoning 那例同时变红)。
|
|
25
|
+
- **开关**:环境变量 `WORKBUDDY_BODY_REPORT` 未设置或为空白即**关闭**;`1`/`true` 写到 `<DSH home>/body-reports/`;其他值按**目录路径**解释。写文件失败只走日志,**绝不影响聊天请求**(有专门断言)。
|
|
26
|
+
- **守卫**:`tests/issue59-body-report.spec.ts`(16 例)。
|
|
27
|
+
- **配套**:`scripts/probe-region-token-accounting.mjs` 新增 `--language en|zh` 与 `--kind prose|code|json`(六种组合确定性填充、跨区域逐字节相同),并补上国内侧实测基线:prose 214.0 / code 287.0 / json 288.3(en),prose 671.0 / code 480.7 / json 397.3(zh),单位 tokens/1k chars。
|
|
28
|
+
|
|
3
29
|
## 4.4.6 (2026-10-14)
|
|
4
30
|
|
|
5
31
|
### Fixes
|
|
@@ -17,14 +43,6 @@
|
|
|
17
43
|
|
|
18
44
|
### Features
|
|
19
45
|
|
|
20
|
-
- **新增「上游请求体构成」诊断(`WORKBUDDY_BODY_REPORT`),用于定案 issue #59:两份请求体到底是不是同一份历史。**(issue #59)
|
|
21
|
-
- **为什么需要**:报告里同一份历史被国内网关算约 70.8 万 token、国际网关算约 137.4 万(1.94 倍),切区域即 `code 11115 prompt is too long`。两条独立测量已经把范围压到唯一一个问题:报告者在**两个区域**跑本仓库的内容类别探针,逐字节相同的散文/代码/JSON 六格比值全在 **0.98~1.06**(同一段字节两边计价一致);代码链路核对又表明**历史 reasoning 根本没进请求体**(pi-ai 只在 `compat.requiresThinkingAsText` 为真时把 `thinking` 当文本转发,本插件从未设置;`reasoning_details` 那条路需要流式解析从不写出的签名)。**剩下唯一可能是两份请求体不同**,而这件事**不需要任何区域有积分**就能查:请求体是本地拼好的,`14018 Credits exhausted` 是发出去之后才可能发生的。
|
|
22
|
-
- **做法**:`src/body-report.ts` 在 shim 把 body 交给上游**之前**记录它的**构成** —— 每条消息的 `role`、逐块的类型(`system`/`text`/`tool-call`/`tool-result`/`reasoning`/`image`)、字符数与其中**中日韩字符**数(实测中文 0.669、英文 0.224 token/字符,差 3 倍,混在一起看不出来)、`tools` 数组、`messages.length` 与 body 总字节数。按区域写 `<dir>/<region>.jsonl`,一次请求一行(一轮对话有很多次上游调用,**最后一次**才是要比的那份)。
|
|
23
|
-
- **只记计数、不记文本**:不写任何消息内容、账号名或令牌 —— 一个要被贴进公开 issue 的诊断不能有泄漏用户会话的能力。`tests/issue59-body-report.spec.ts` 里有一条专门的断言:用带特征串的 body 生成报告后,序列化结果**不含**该特征串(变异验证:让 `block()` 把 `text` 一起带上 → 该例与 reasoning 那例同时变红)。
|
|
24
|
-
- **开关**:环境变量 `WORKBUDDY_BODY_REPORT` 未设置或为空白即**关闭**;`1`/`true` 写到 `<DSH home>/body-reports/`;其他值按**目录路径**解释。写文件失败只走日志,**绝不影响聊天请求**(有专门断言)。
|
|
25
|
-
- **守卫**:`tests/issue59-body-report.spec.ts`(16 例)。
|
|
26
|
-
- **配套**:`scripts/probe-region-token-accounting.mjs` 新增 `--language en|zh` 与 `--kind prose|code|json`(六种组合确定性填充、跨区域逐字节相同),并补上国内侧实测基线:prose 214.0 / code 287.0 / json 288.3(en),prose 671.0 / code 480.7 / json 397.3(zh),单位 tokens/1k chars。
|
|
27
|
-
|
|
28
46
|
## 4.4.5 (2026-10-14)
|
|
29
47
|
|
|
30
48
|
### Fixes
|
package/lib/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as defaultDesktopAuthDirs, F as WorkBuddyUpstreamClient, K as WORKBUDDY_APP_EXECUTABLE_ENV, O as legacyWorkbuddyOwnAuthPath, Q as findWorkbuddyAppExecutableWithSource, S as defaultDesktopAuthCandidates, a as readHostHeartbeat, c as WORKBUDDY_CONNECT_VERSION, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, j as workbuddyOwnAuthPath, l as piAiRuntimeInfo, o as workbuddyHostHeartbeatPath, ot as hostPeersInfo, r as isHeartbeatProcessAlive, u as FALLBACK_WORKBUDDY_MODELS, v as WORKBUDDY_AUTH_FILE_ENV, y as WorkBuddyCredentialStore } from "./host-heartbeat-
|
|
2
|
+
import { C as defaultDesktopAuthDirs, F as WorkBuddyUpstreamClient, K as WORKBUDDY_APP_EXECUTABLE_ENV, O as legacyWorkbuddyOwnAuthPath, Q as findWorkbuddyAppExecutableWithSource, S as defaultDesktopAuthCandidates, a as readHostHeartbeat, c as WORKBUDDY_CONNECT_VERSION, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, j as workbuddyOwnAuthPath, l as piAiRuntimeInfo, o as workbuddyHostHeartbeatPath, ot as hostPeersInfo, r as isHeartbeatProcessAlive, u as FALLBACK_WORKBUDDY_MODELS, v as WORKBUDDY_AUTH_FILE_ENV, y as WorkBuddyCredentialStore } from "./host-heartbeat-DvcOd-3C.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
|
@@ -233,6 +233,23 @@ window.__ModuleLoader__.load({
|
|
|
233
233
|
function regionCreditsShownOf(value, region) {
|
|
234
234
|
return regionSlotOf(value, region)["showCreditsInMainUi"] !== false;
|
|
235
235
|
}
|
|
236
|
+
/** The seat a region's readout uses, defaulting to the composer row. */
|
|
237
|
+
function regionCreditPositionOf(value, region) {
|
|
238
|
+
return regionSlotOf(value, region)["creditPosition"] === "stats" ? "stats" : "composer";
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Build the next `regions` settings value for one region's credit seat.
|
|
242
|
+
*
|
|
243
|
+
* Same merge rule as {@link nextRegionCreditsShown}: only the target region's
|
|
244
|
+
* field changes, so every other field of that slot and every other region's slot
|
|
245
|
+
* survive the write.
|
|
246
|
+
*/
|
|
247
|
+
function nextRegionCreditPosition(value, region, position) {
|
|
248
|
+
return nextRegionSlots(regionsMapOf(value), region, {
|
|
249
|
+
...regionSlotOf(value, region),
|
|
250
|
+
creditPosition: position
|
|
251
|
+
});
|
|
252
|
+
}
|
|
236
253
|
/**
|
|
237
254
|
* Build the next `regions` settings value for the sidebar-credit on/off toggle.
|
|
238
255
|
*
|
|
@@ -632,6 +649,20 @@ window.__ModuleLoader__.load({
|
|
|
632
649
|
});
|
|
633
650
|
}
|
|
634
651
|
/**
|
|
652
|
+
* Write one region's credit SEAT (issue #62), verifying it landed.
|
|
653
|
+
*
|
|
654
|
+
* Sibling of {@link writeRegionCreditsShown}: same whole-slot merge, same
|
|
655
|
+
* landed check, same reason for both. The seat decides only WHERE the readout
|
|
656
|
+
* renders, so a lost write would leave the user looking at the old position
|
|
657
|
+
* while the control claimed the new one.
|
|
658
|
+
*/
|
|
659
|
+
async function writeRegionCreditPosition(scope, region, position, slot) {
|
|
660
|
+
await writeField(scope, "regions", region, slot, (readBack) => {
|
|
661
|
+
const stored = readBack;
|
|
662
|
+
return stored !== null && stored !== void 0 && stored["creditPosition"] === position;
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
635
666
|
* Write one region's account-pool PREFERENCES, verifying they landed.
|
|
636
667
|
*
|
|
637
668
|
* Routed through {@link writeField} like every other settings write, so it gets
|
|
@@ -3044,6 +3075,15 @@ window.__ModuleLoader__.load({
|
|
|
3044
3075
|
render (no Host pool state), so there is no heading row to share. This is the
|
|
3045
3076
|
spacing the control carried before it moved beside that heading. */
|
|
3046
3077
|
.dsm-workbuddy-credits-switch-row{display:flex;align-items:center;margin:8px 0 2px}
|
|
3078
|
+
/* The credit readout's seat picker (issue #62). Narrower than the pool's model
|
|
3079
|
+
select so the show/hide box and this control share one heading row, and it
|
|
3080
|
+
wraps onto its own line rather than squashing the pool title. */
|
|
3081
|
+
/* Its OWN label class, deliberately not the switch's: a query for "the
|
|
3082
|
+
sidebar-credit switch" must not be able to pick this control up, and the two
|
|
3083
|
+
are different widgets (a checkbox and a two-way picker). */
|
|
3084
|
+
.dsm-workbuddy-credit-position-switch{display:inline-flex;align-items:center;gap:6px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0)}
|
|
3085
|
+
.dsm-workbuddy-credit-position{max-width:150px;padding:2px 6px;font-size:12px}
|
|
3086
|
+
.dsm-workbuddy-credits-switch-row .dsm-workbuddy-credit-position-switch{margin-left:12px}
|
|
3047
3087
|
.dsm-workbuddy-credits-switch input{margin:0;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
|
|
3048
3088
|
.dsm-workbuddy-credits-switch input:disabled{opacity:.4;cursor:default}
|
|
3049
3089
|
.dsm-workbuddy-tab-off{opacity:.55}
|
|
@@ -4140,6 +4180,40 @@ window.__ModuleLoader__.load({
|
|
|
4140
4180
|
* hide never drops the directory, model picks, budgets or pool preferences
|
|
4141
4181
|
* that live alongside it.
|
|
4142
4182
|
*/
|
|
4183
|
+
/**
|
|
4184
|
+
* WHERE the active region's credit readout sits (issue #62).
|
|
4185
|
+
*
|
|
4186
|
+
* Read off the Host's usage answer, like {@link creditsShownOn} and for the
|
|
4187
|
+
* same measured reason: the browser's settings mirror for this namespace is
|
|
4188
|
+
* documented as stale on the affected 0.1.7 deployments, so a control reading
|
|
4189
|
+
* it would keep showing the previous seat after a successful write. Absent
|
|
4190
|
+
* means `composer`, which is what the Host resolves for a profile written
|
|
4191
|
+
* before the setting existed.
|
|
4192
|
+
*/
|
|
4193
|
+
const creditPositionOn = (item) => {
|
|
4194
|
+
return statusByRegion[item]?.creditPosition === "stats" ? "stats" : "composer";
|
|
4195
|
+
};
|
|
4196
|
+
/**
|
|
4197
|
+
* Move one region's credit readout between its two seats.
|
|
4198
|
+
*
|
|
4199
|
+
* Same shape as {@link toggleCredits}: whole region slot in, landed check,
|
|
4200
|
+
* then a Host re-read. The re-read is load-bearing here for the same reason —
|
|
4201
|
+
* the control renders the Host's answer, so without it the picker keeps
|
|
4202
|
+
* showing the seat it had and the change looks like it did nothing.
|
|
4203
|
+
*/
|
|
4204
|
+
const changeCreditPosition = async (item, position) => {
|
|
4205
|
+
if (settingsScope === void 0) return;
|
|
4206
|
+
setTogglingCredits(item);
|
|
4207
|
+
try {
|
|
4208
|
+
const slot = nextRegionCreditPosition(settingsScope.getSnapshot().value, item, position)[item];
|
|
4209
|
+
await writeRegionCreditPosition(settingsScope, item, position, typeof slot === "object" && slot !== null ? slot : {});
|
|
4210
|
+
await refreshUsage(item);
|
|
4211
|
+
} catch (error) {
|
|
4212
|
+
if (mounted.current) setAccountError(error instanceof Error ? error.message : t("row.requestFailed"));
|
|
4213
|
+
} finally {
|
|
4214
|
+
if (mounted.current) setTogglingCredits(void 0);
|
|
4215
|
+
}
|
|
4216
|
+
};
|
|
4143
4217
|
const toggleCredits = async (item, shown) => {
|
|
4144
4218
|
if (settingsScope === void 0) return;
|
|
4145
4219
|
setTogglingCredits(item);
|
|
@@ -4446,6 +4520,40 @@ window.__ModuleLoader__.load({
|
|
|
4446
4520
|
}
|
|
4447
4521
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: activeRegion === "cn" ? t("row.showCreditsCn") : t("row.showCreditsGlobal") })]
|
|
4448
4522
|
});
|
|
4523
|
+
/**
|
|
4524
|
+
* WHERE the readout sits — a two-way choice, active tab's region only
|
|
4525
|
+
* (issue #62).
|
|
4526
|
+
*
|
|
4527
|
+
* It sits directly under the show/hide switch because the two answer one
|
|
4528
|
+
* question together ("show it, and where"), and because a user only reaches
|
|
4529
|
+
* for this when the default seat is unavailable — typically after installing
|
|
4530
|
+
* another plugin that occupies the composer row. Rendered as a `<select>`
|
|
4531
|
+
* rather than a pair of radios so it stays one compact row on the pool
|
|
4532
|
+
* heading, matching the switch beside it.
|
|
4533
|
+
*
|
|
4534
|
+
* Disabled with the region, exactly like the switch: a region that is off has
|
|
4535
|
+
* no readout to place.
|
|
4536
|
+
*/
|
|
4537
|
+
const creditPositionPicker = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4538
|
+
className: "dsm-workbuddy-credit-position-switch",
|
|
4539
|
+
title: t("row.creditPositionHint"),
|
|
4540
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
4541
|
+
className: "dsm-workbuddy-pool-select dsm-workbuddy-credit-position",
|
|
4542
|
+
value: activeRegionOn ? creditPositionOn(activeRegion) : "composer",
|
|
4543
|
+
disabled: !activeRegionOn || togglingCredits === activeRegion || !canWrite || saving || poolBusy,
|
|
4544
|
+
"aria-label": t("row.creditPositionAria", { region: activeRegion === "cn" ? t("row.tabCn") : t("row.tabGlobal") }),
|
|
4545
|
+
onChange: (event) => {
|
|
4546
|
+
changeCreditPosition(activeRegion, event.currentTarget.value === "stats" ? "stats" : "composer");
|
|
4547
|
+
},
|
|
4548
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4549
|
+
value: "composer",
|
|
4550
|
+
children: t("row.creditPositionComposer")
|
|
4551
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4552
|
+
value: "stats",
|
|
4553
|
+
children: t("row.creditPositionStats")
|
|
4554
|
+
})]
|
|
4555
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.creditPositionLabel") })]
|
|
4556
|
+
});
|
|
4449
4557
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
4450
4558
|
className: `dsm-plugin-card${open ? " dsm-plugin-card-open" : ""}`,
|
|
4451
4559
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -4586,15 +4694,15 @@ window.__ModuleLoader__.load({
|
|
|
4586
4694
|
},
|
|
4587
4695
|
selectingAccount: switchingAccount,
|
|
4588
4696
|
accountTools,
|
|
4589
|
-
...status.pool === void 0 ? {} : { headerExtra: creditsSwitch },
|
|
4697
|
+
...status.pool === void 0 ? {} : { headerExtra: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [creditsSwitch, creditPositionPicker] }) },
|
|
4590
4698
|
siblingBusy: saving || togglingRegion !== void 0,
|
|
4591
4699
|
onBusyChange: setPoolBusy,
|
|
4592
4700
|
onSaved: async () => await refreshUsage(activeRegion) !== void 0,
|
|
4593
4701
|
onRefresh: () => refreshUsage(activeRegion)
|
|
4594
4702
|
}),
|
|
4595
|
-
status.pool === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.
|
|
4703
|
+
status.pool === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4596
4704
|
className: "dsm-workbuddy-credits-switch-row",
|
|
4597
|
-
children: creditsSwitch
|
|
4705
|
+
children: [creditsSwitch, creditPositionPicker]
|
|
4598
4706
|
}) : null,
|
|
4599
4707
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
4600
4708
|
className: "dsm-workbuddy-models",
|
|
@@ -5380,6 +5488,17 @@ window.__ModuleLoader__.load({
|
|
|
5380
5488
|
* rows). The composer row is per-session and provider-scoped, so exactly one
|
|
5381
5489
|
* plugin's credits are relevant at a time and nothing overlaps.
|
|
5382
5490
|
*
|
|
5491
|
+
* WHY THERE IS A SECOND SEAT (issue #62). The composer tool row is a LIST seat,
|
|
5492
|
+
* so it is shared not only with the shell's own controls but with any other
|
|
5493
|
+
* plugin that registers there — and one report is exactly that: with a
|
|
5494
|
+
* third-party plugin installed the readout stopped appearing at all. A list seat
|
|
5495
|
+
* cannot promise that every occupant is visible, so the seat is the USER's
|
|
5496
|
+
* choice: `composer` (default, unchanged for anyone who does not choose) or
|
|
5497
|
+
* `stats`, the ambient dock below the composer card where the shell renders its
|
|
5498
|
+
* own round/token pills. The gate is registered in BOTH seats and renders in the
|
|
5499
|
+
* one the setting names, which keeps the decision in one function instead of
|
|
5500
|
+
* re-registering slots when the setting changes.
|
|
5501
|
+
*
|
|
5383
5502
|
* Kept separate from {@link ComposerCredits} (and from the browser-plugin
|
|
5384
5503
|
* entry) so the decision can be unit-tested without a Host: the entry imports
|
|
5385
5504
|
* browser-only DSH packages the test environment cannot load, while this module
|
|
@@ -5416,6 +5535,16 @@ window.__ModuleLoader__.load({
|
|
|
5416
5535
|
* the authority; the scope is the fallback for a Host line that predates the
|
|
5417
5536
|
* field.
|
|
5418
5537
|
*/
|
|
5538
|
+
/**
|
|
5539
|
+
* The settings tree this plugin writes, unwrapped to a plain object.
|
|
5540
|
+
*
|
|
5541
|
+
* `unwrapVolatileDeep` first: the scope's value is a live cosmokit reference on
|
|
5542
|
+
* the 0.1.7 line, and reading fields off it without unwrapping yields proxies
|
|
5543
|
+
* whose `===` comparisons against a string are false.
|
|
5544
|
+
*/
|
|
5545
|
+
function scopeValueOf(scope) {
|
|
5546
|
+
return scope === void 0 ? void 0 : unwrapVolatileDeep(scope.getSnapshot().value);
|
|
5547
|
+
}
|
|
5419
5548
|
function composerCreditsRegionOf(projection, scope, hostValue) {
|
|
5420
5549
|
const provider = selectedProviderOf(projection);
|
|
5421
5550
|
const region = provider === void 0 ? void 0 : WORKBUDDY_COMPOSER_PROVIDERS[provider];
|
|
@@ -5427,7 +5556,7 @@ window.__ModuleLoader__.load({
|
|
|
5427
5556
|
return region;
|
|
5428
5557
|
}
|
|
5429
5558
|
function ComposerCreditsGate(props) {
|
|
5430
|
-
const { t, settingsScope, useProjection } = props;
|
|
5559
|
+
const { t, position, settingsScope, useProjection } = props;
|
|
5431
5560
|
const [, setRevision] = (0, react.useState)(0);
|
|
5432
5561
|
(0, react.useEffect)(() => settingsScope?.subscribe(() => {
|
|
5433
5562
|
setRevision((value) => value + 1);
|
|
@@ -5435,6 +5564,7 @@ window.__ModuleLoader__.load({
|
|
|
5435
5564
|
const projection = useProjection === void 0 ? void 0 : useProjection("modelSelection");
|
|
5436
5565
|
const region = composerCreditsRegionOf(projection, settingsScope);
|
|
5437
5566
|
if (region === void 0) return null;
|
|
5567
|
+
if (regionCreditPositionOf(scopeValueOf(settingsScope), region) !== (position ?? "composer")) return null;
|
|
5438
5568
|
const provider = selectedProviderOf(projection);
|
|
5439
5569
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ComposerCredits, {
|
|
5440
5570
|
t,
|
|
@@ -5704,6 +5834,11 @@ window.__ModuleLoader__.load({
|
|
|
5704
5834
|
"row.showCreditsCn": "WB CN credits in composer",
|
|
5705
5835
|
"row.showCreditsGlobal": "WB AI credits in composer",
|
|
5706
5836
|
"row.showCreditsCnHint": "Show the domestic (WB CN) credit balance in the composer toolbar, but only while this session uses a WB CN model. Click it for per-account balances and a manual refresh (refreshes by itself every 5 minutes).",
|
|
5837
|
+
"row.creditPositionLabel": "position",
|
|
5838
|
+
"row.creditPositionComposer": "composer toolbar",
|
|
5839
|
+
"row.creditPositionStats": "below the input box",
|
|
5840
|
+
"row.creditPositionAria": "Credit readout position for {region}",
|
|
5841
|
+
"row.creditPositionHint": "Where the credit readout sits. \"composer toolbar\" is the default (the row with the model picker). Choose \"below the input box\" — the row carrying the round / step / token statistics — if another plugin occupies the composer toolbar and the readout does not appear.",
|
|
5707
5842
|
"row.showCreditsGlobalHint": "Show the international (WB AI) credit balance in the composer toolbar, but only while this session uses a WB AI model. Click it for per-account balances and a manual refresh (refreshes by itself every 5 minutes).",
|
|
5708
5843
|
"composer.pointsCn": "WB CN",
|
|
5709
5844
|
"composer.pointsGlobal": "WB AI",
|
|
@@ -5975,6 +6110,11 @@ window.__ModuleLoader__.load({
|
|
|
5975
6110
|
"row.showCreditsCn": "对话框显示 WB CN 积分",
|
|
5976
6111
|
"row.showCreditsGlobal": "对话框显示 WB AI 积分",
|
|
5977
6112
|
"row.showCreditsCnHint": "在对话输入框工具栏显示国内版(WB CN)剩余积分,仅当本会话使用 WB CN 模型时出现。点击可看各账号余额并手动刷新(每 5 分钟也会自动刷新)。",
|
|
6113
|
+
"row.creditPositionLabel": "位置",
|
|
6114
|
+
"row.creditPositionComposer": "输入框工具栏",
|
|
6115
|
+
"row.creditPositionStats": "输入框下方",
|
|
6116
|
+
"row.creditPositionAria": "{region} 的积分显示位置",
|
|
6117
|
+
"row.creditPositionHint": "积分显示在哪里。「输入框工具栏」是默认位置(与模型选择同一行)。如果装了别的插件后读数不显示,改成「输入框下方」—— 那是「轮次 / 步数 / token」统计所在的那一行。",
|
|
5978
6118
|
"row.showCreditsGlobalHint": "在对话输入框工具栏显示国际版(WB AI)剩余积分,仅当本会话使用 WB AI 模型时出现。点击可看各账号余额并手动刷新(每 5 分钟也会自动刷新)。",
|
|
5979
6119
|
"composer.pointsCn": "WB CN",
|
|
5980
6120
|
"composer.pointsGlobal": "WB AI",
|
|
@@ -6098,8 +6238,12 @@ window.__ModuleLoader__.load({
|
|
|
6098
6238
|
id: "dsh-connect-workbuddy-composer-credits",
|
|
6099
6239
|
order: 4,
|
|
6100
6240
|
locale: namespace,
|
|
6101
|
-
inject: () => settingsScope === void 0 ? {
|
|
6241
|
+
inject: () => settingsScope === void 0 ? {
|
|
6102
6242
|
t,
|
|
6243
|
+
position: "composer"
|
|
6244
|
+
} : {
|
|
6245
|
+
t,
|
|
6246
|
+
position: "composer",
|
|
6103
6247
|
settingsScope
|
|
6104
6248
|
}
|
|
6105
6249
|
}, ComposerCreditsGate));
|
|
@@ -6109,6 +6253,46 @@ window.__ModuleLoader__.load({
|
|
|
6109
6253
|
}
|
|
6110
6254
|
};
|
|
6111
6255
|
/**
|
|
6256
|
+
* The SAME readout in the ambient dock below the composer card (issue #62).
|
|
6257
|
+
*
|
|
6258
|
+
* Registered unconditionally, like the composer seat, because the CHOICE
|
|
6259
|
+
* between the two seats lives in the gate: it renders in the seat the
|
|
6260
|
+
* setting names and returns null in the other. Registering only the chosen
|
|
6261
|
+
* seat would mean re-registering on every settings change, and the seat is
|
|
6262
|
+
* exactly the kind of thing a user flips once while looking at the result.
|
|
6263
|
+
*
|
|
6264
|
+
* WHY THIS SEAT EXISTS: the composer tool row is a list seat shared with any
|
|
6265
|
+
* other plugin that registers there, and issue #62 is one report of the
|
|
6266
|
+
* readout disappearing when another plugin was installed. This row is where
|
|
6267
|
+
* the shell renders its own round/step/token pills, so it is a place a user
|
|
6268
|
+
* already reads session numbers from.
|
|
6269
|
+
*
|
|
6270
|
+
* `order` places it after the shell's own pills (`stats` registers at 0), so
|
|
6271
|
+
* the shipped statistics stay leftmost and the readout reads as an addition
|
|
6272
|
+
* rather than a replacement.
|
|
6273
|
+
*/
|
|
6274
|
+
const registerStatsCredits = () => {
|
|
6275
|
+
try {
|
|
6276
|
+
return ctx.slots.inject("conversation.composer.dock", () => ctx.slots.register({
|
|
6277
|
+
name: "conversation.composer.dock",
|
|
6278
|
+
id: "dsh-connect-workbuddy-stats-credits",
|
|
6279
|
+
order: 10,
|
|
6280
|
+
locale: namespace,
|
|
6281
|
+
inject: () => settingsScope === void 0 ? {
|
|
6282
|
+
t,
|
|
6283
|
+
position: "stats"
|
|
6284
|
+
} : {
|
|
6285
|
+
t,
|
|
6286
|
+
position: "stats",
|
|
6287
|
+
settingsScope
|
|
6288
|
+
}
|
|
6289
|
+
}, ComposerCreditsGate));
|
|
6290
|
+
} catch (error) {
|
|
6291
|
+
console.error("[dsh-connect-workbuddy] stats credits slot failed to register (host provider unaffected):", error);
|
|
6292
|
+
return;
|
|
6293
|
+
}
|
|
6294
|
+
};
|
|
6295
|
+
/**
|
|
6112
6296
|
* Every slot this plugin contributes, and how to (re)create them.
|
|
6113
6297
|
*
|
|
6114
6298
|
* Kept as one list because the binding of `settingsScope` and the slot
|
|
@@ -6126,7 +6310,8 @@ window.__ModuleLoader__.load({
|
|
|
6126
6310
|
const registerAllSlots = () => [
|
|
6127
6311
|
...WORKBUDDY_CARD_KEYS.map((key) => registerCard("plugins.bundle.config", key)),
|
|
6128
6312
|
...WORKBUDDY_CARD_KEYS.map((key) => registerCard("plugins.row.config", `${key}#${key}`)),
|
|
6129
|
-
registerComposerCredits()
|
|
6313
|
+
registerComposerCredits(),
|
|
6314
|
+
registerStatsCredits()
|
|
6130
6315
|
].filter((disposer) => disposer !== void 0);
|
|
6131
6316
|
/**
|
|
6132
6317
|
* Disposers of the slots registered for the CURRENT binding.
|
|
@@ -3825,7 +3825,7 @@ function generationOf(version) {
|
|
|
3825
3825
|
//#endregion
|
|
3826
3826
|
//#region src/version.ts
|
|
3827
3827
|
/** The npm package version this build was produced from. */
|
|
3828
|
-
const WORKBUDDY_CONNECT_VERSION = "4.
|
|
3828
|
+
const WORKBUDDY_CONNECT_VERSION = "4.5.0";
|
|
3829
3829
|
//#endregion
|
|
3830
3830
|
//#region src/host-heartbeat.ts
|
|
3831
3831
|
/**
|
package/lib/index.d.ts
CHANGED
|
@@ -2173,6 +2173,16 @@ declare function regionEnabledOf(value: unknown, region: WorkBuddyWebRegion): bo
|
|
|
2173
2173
|
/** Build the next `regions` settings value for a signed-in tab's save. */
|
|
2174
2174
|
declare function nextRegionSlots<Slot extends object>(regions: unknown, region: WorkBuddyWebRegion, slot: Slot): Record<string, unknown>;
|
|
2175
2175
|
declare function nextRegionEnabled(value: unknown, region: WorkBuddyWebRegion, enabled: boolean): Record<string, unknown>;
|
|
2176
|
+
/**
|
|
2177
|
+
* The two seats the credit readout can occupy (issue #62).
|
|
2178
|
+
*
|
|
2179
|
+
* `composer` is the composer tool row (`conversation.input.left`); `stats` is
|
|
2180
|
+
* the ambient dock below the composer card (`conversation.composer.dock`),
|
|
2181
|
+
* beside the shell's own round/step/token pills. Named as a union rather than a
|
|
2182
|
+
* boolean because a third seat is plausible and `showAtTop: boolean` would have
|
|
2183
|
+
* to be migrated rather than extended.
|
|
2184
|
+
*/
|
|
2185
|
+
type WorkBuddyCreditPosition = 'composer' | 'stats';
|
|
2176
2186
|
/** One credit package as the upstream returns it, node-free. */
|
|
2177
2187
|
interface WorkBuddyWebCreditPackage {
|
|
2178
2188
|
packageName: string;
|
|
@@ -2361,6 +2371,8 @@ type WorkBuddyWebUsage =
|
|
|
2361
2371
|
* "关闭不了" report.
|
|
2362
2372
|
*/
|
|
2363
2373
|
showCreditsInMainUi?: boolean;
|
|
2374
|
+
/** WHERE this region's readout sits (issue #62); absent means `composer`. */
|
|
2375
|
+
creditPosition?: WorkBuddyCreditPosition;
|
|
2364
2376
|
message?: string;
|
|
2365
2377
|
/** The persisted account id matches no local account. */
|
|
2366
2378
|
selectionLost?: boolean;
|
|
@@ -2395,6 +2407,8 @@ type WorkBuddyWebUsage =
|
|
|
2395
2407
|
* settings mirror.
|
|
2396
2408
|
*/
|
|
2397
2409
|
showCreditsInMainUi?: boolean;
|
|
2410
|
+
/** WHERE this region's readout sits (issue #62); absent means `composer`. */
|
|
2411
|
+
creditPosition?: WorkBuddyCreditPosition;
|
|
2398
2412
|
source?: 'desktop' | 'dsh';
|
|
2399
2413
|
tokenExpiresAtMs: number;
|
|
2400
2414
|
/** A saved per-region choice is in effect (false = following the app). */
|
|
@@ -2784,6 +2798,13 @@ interface WorkBuddyStatusRouteOptions {
|
|
|
2784
2798
|
* absent reads as ON, matching the schema's `default(true)`.
|
|
2785
2799
|
*/
|
|
2786
2800
|
showCreditsInMainUi?(region: WorkBuddyRegion): boolean;
|
|
2801
|
+
/**
|
|
2802
|
+
* WHERE this region's credit readout sits (issue #62).
|
|
2803
|
+
*
|
|
2804
|
+
* Optional, and absent reads as `composer` — the only seat before the setting
|
|
2805
|
+
* existed, so a Host that predates it renders exactly as it used to.
|
|
2806
|
+
*/
|
|
2807
|
+
creditPosition?(region: WorkBuddyRegion): WorkBuddyCreditPosition;
|
|
2787
2808
|
/**
|
|
2788
2809
|
* Send one real-volume request to each named model of a region and report what
|
|
2789
2810
|
* came back.
|
|
@@ -3107,6 +3128,25 @@ interface WorkBuddyRegionState {
|
|
|
3107
3128
|
* written before this switch existed keeps whatever the default was.
|
|
3108
3129
|
*/
|
|
3109
3130
|
showCreditsInMainUi?: boolean;
|
|
3131
|
+
/**
|
|
3132
|
+
* WHERE this region's credit readout sits (issue #62).
|
|
3133
|
+
*
|
|
3134
|
+
* Two seats, because the first one is not always available:
|
|
3135
|
+
*
|
|
3136
|
+
* - `composer` — the composer tool row (`conversation.input.left`), shared
|
|
3137
|
+
* with the shell's own controls. It is the closer, more glanceable seat,
|
|
3138
|
+
* and it is the default so nothing moves for anyone who does not choose.
|
|
3139
|
+
* - `stats` — the ambient dock below the composer card
|
|
3140
|
+
* (`conversation.composer.dock`), where the shell renders its own
|
|
3141
|
+
* round/step/token pills. That row is a LIST seat whose shipped occupant
|
|
3142
|
+
* registers separately, so a third-party plugin that ALSO parks something
|
|
3143
|
+
* in the composer row cannot crowd this readout out of existence.
|
|
3144
|
+
*
|
|
3145
|
+
* The report behind this (issue #62) was exactly that: with another plugin
|
|
3146
|
+
* installed, the readout vanished from the composer row. Making the seat a
|
|
3147
|
+
* choice is what keeps one plugin's presence from deciding another's.
|
|
3148
|
+
*/
|
|
3149
|
+
creditPosition?: WorkBuddyCreditPosition;
|
|
3110
3150
|
/** The last-refreshed directory for this region; what the card displays. */
|
|
3111
3151
|
lastCatalog?: WorkBuddyModelInfo[];
|
|
3112
3152
|
/** The user's selection in this region, as model ids. */
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as isEncryptedFieldWrapper, A as workbuddyAccountId, B as modelIdOfBody, C as defaultDesktopAuthDirs, D as isVaultAccountId, E as isEncryptedCredentialError, F as WorkBuddyUpstreamClient, G as regionOf, H as parseReasoning, I as classifyUpstreamError, J as deriveAtRestKey, K as WORKBUDDY_APP_EXECUTABLE_ENV, L as declaredTools, M as CREDENTIAL_REJECTED_CODE, N as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, O as legacyWorkbuddyOwnAuthPath, P as WorkBuddyCredentialRejectedError, R as isCredentialRejectedError, S as defaultDesktopAuthCandidates, T as hasEncryptedCredentialFields, U as parseUpstreamModel, V as parseCreditMultiplier, W as prepareChatBody, X as fetchAtRestKeyPayload, Y as deriveAtRestKeyId, Z as findWorkbuddyAppExecutable, _ as WORKBUDDY_AUTH_FILENAME, a as readHostHeartbeat, at as workbuddyAppExecutableCandidates, b as WorkBuddyEncryptedCredentialError, c as WORKBUDDY_CONNECT_VERSION, ct as withFileLock, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, et as isWorkbuddyBundle, f as WorkBuddyCatalog, g as ENCRYPTED_CREDENTIAL_CODE, h as fallbackModelsFor, i as processStartTimeMs, it as readAtRestKey, j as workbuddyOwnAuthPath, k as parseWorkBuddyAuth, l as piAiRuntimeInfo, lt as writeFileAtomic, m as deriveCatalog, n as clearHostHeartbeat, nt as macosNestedAppBundles, o as workbuddyHostHeartbeatPath, p as applyContextBudgets, q as clearAtRestKeyCache, r as isHeartbeatProcessAlive, rt as openEncryptedField, s as writeHostHeartbeat, st as resolveDshHome, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, tt as macosBundleExecutable, u as FALLBACK_WORKBUDDY_MODELS, v as WORKBUDDY_AUTH_FILE_ENV, w as defaultDesktopAuthPath, x as authFileName, y as WorkBuddyCredentialStore, z as knownRegionOf } from "./host-heartbeat-
|
|
1
|
+
import { $ as isEncryptedFieldWrapper, A as workbuddyAccountId, B as modelIdOfBody, C as defaultDesktopAuthDirs, D as isVaultAccountId, E as isEncryptedCredentialError, F as WorkBuddyUpstreamClient, G as regionOf, H as parseReasoning, I as classifyUpstreamError, J as deriveAtRestKey, K as WORKBUDDY_APP_EXECUTABLE_ENV, L as declaredTools, M as CREDENTIAL_REJECTED_CODE, N as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, O as legacyWorkbuddyOwnAuthPath, P as WorkBuddyCredentialRejectedError, R as isCredentialRejectedError, S as defaultDesktopAuthCandidates, T as hasEncryptedCredentialFields, U as parseUpstreamModel, V as parseCreditMultiplier, W as prepareChatBody, X as fetchAtRestKeyPayload, Y as deriveAtRestKeyId, Z as findWorkbuddyAppExecutable, _ as WORKBUDDY_AUTH_FILENAME, a as readHostHeartbeat, at as workbuddyAppExecutableCandidates, b as WorkBuddyEncryptedCredentialError, c as WORKBUDDY_CONNECT_VERSION, ct as withFileLock, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, et as isWorkbuddyBundle, f as WorkBuddyCatalog, g as ENCRYPTED_CREDENTIAL_CODE, h as fallbackModelsFor, i as processStartTimeMs, it as readAtRestKey, j as workbuddyOwnAuthPath, k as parseWorkBuddyAuth, l as piAiRuntimeInfo, lt as writeFileAtomic, m as deriveCatalog, n as clearHostHeartbeat, nt as macosNestedAppBundles, o as workbuddyHostHeartbeatPath, p as applyContextBudgets, q as clearAtRestKeyCache, r as isHeartbeatProcessAlive, rt as openEncryptedField, s as writeHostHeartbeat, st as resolveDshHome, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, tt as macosBundleExecutable, u as FALLBACK_WORKBUDDY_MODELS, v as WORKBUDDY_AUTH_FILE_ENV, w as defaultDesktopAuthPath, x as authFileName, y as WorkBuddyCredentialStore, z as knownRegionOf } from "./host-heartbeat-DvcOd-3C.js";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import { appendFile, mkdir, readFile } from "node:fs/promises";
|
|
4
4
|
import { randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
@@ -4207,6 +4207,7 @@ async function workBuddyWebStatus(deps, region) {
|
|
|
4207
4207
|
deps.regionUsable?.(region, accounts.length > 0);
|
|
4208
4208
|
const enabled = deps.regionEnabled(region);
|
|
4209
4209
|
const showCreditsInMainUi = deps.showCreditsInMainUi?.(region) ?? true;
|
|
4210
|
+
const creditPosition = deps.creditPosition?.(region) ?? "composer";
|
|
4210
4211
|
const authStatus = await store.status();
|
|
4211
4212
|
const selectionExplicit = store.hasExplicitSelection();
|
|
4212
4213
|
if (authStatus.state !== "signed-out" && accounts.length === 0) return {
|
|
@@ -4228,6 +4229,7 @@ async function workBuddyWebStatus(deps, region) {
|
|
|
4228
4229
|
selectionExplicit,
|
|
4229
4230
|
enabled,
|
|
4230
4231
|
showCreditsInMainUi,
|
|
4232
|
+
creditPosition,
|
|
4231
4233
|
...await store.selectionLost() ? { selectionLost: true } : {},
|
|
4232
4234
|
...webAccounts.length === 0 ? await searchedPaths(store) : {}
|
|
4233
4235
|
};
|
|
@@ -4243,6 +4245,7 @@ async function workBuddyWebStatus(deps, region) {
|
|
|
4243
4245
|
selectionExplicit,
|
|
4244
4246
|
enabled,
|
|
4245
4247
|
showCreditsInMainUi,
|
|
4248
|
+
creditPosition,
|
|
4246
4249
|
accounts: accounts.map(toWebAccount),
|
|
4247
4250
|
models: deps.displayModels(region).map((model) => toWebModel(model, deps.contextBudgets(region))),
|
|
4248
4251
|
enabledModelIds: [...deps.enabledModelIds(region)],
|
|
@@ -5013,6 +5016,7 @@ const regionStateConfig = z.object({
|
|
|
5013
5016
|
* which is reachable exactly where the feature renders.
|
|
5014
5017
|
*/
|
|
5015
5018
|
showCreditsInMainUi: z.boolean().default(true).description("Show this region's remaining credits at the DSH sidebar foot (opt-out; the line carries its own hide button)"),
|
|
5019
|
+
creditPosition: z.union([z.const("composer"), z.const("stats")]).default("composer").description("Where the credit readout sits: \"composer\" = the composer tool row (default), \"stats\" = the ambient row below the composer card, next to the shell's own token pills. Choose \"stats\" if another plugin occupies the composer row."),
|
|
5016
5020
|
lastCatalog: z.array(modelConfig).default([]),
|
|
5017
5021
|
enabledModelIds: z.array(z.string()).default([]),
|
|
5018
5022
|
imageModelIds: z.array(z.string()).default([]),
|
|
@@ -5405,6 +5409,13 @@ function apply(ctx, config) {
|
|
|
5405
5409
|
*/
|
|
5406
5410
|
showCreditsInMainUi: (region) => regionStateOf(current(), region).showCreditsInMainUi !== false,
|
|
5407
5411
|
/**
|
|
5412
|
+
* WHERE the readout sits, resolved here rather than read from the browser
|
|
5413
|
+
* settings mirror, for the same measured reason as the flag above: on the
|
|
5414
|
+
* affected 0.1.7 deployments that mirror never picks up this plugin's
|
|
5415
|
+
* writes, and a control reading it would keep showing the old seat.
|
|
5416
|
+
*/
|
|
5417
|
+
creditPosition: (region) => creditPositionOf(current(), region),
|
|
5418
|
+
/**
|
|
5408
5419
|
* One minimal request per named model, through the region's own credential.
|
|
5409
5420
|
*
|
|
5410
5421
|
* Resolved here rather than in the route so the browser half never touches a
|
|
@@ -5950,6 +5961,14 @@ function apply(ctx, config) {
|
|
|
5950
5961
|
return resolveTargetModel(displayModels(current(), region), targetModelPreference(poolPreferencesOf(current(), region), accountId)).modelId;
|
|
5951
5962
|
};
|
|
5952
5963
|
/** One region's pool preferences, with the schema's defaults applied. */
|
|
5964
|
+
/**
|
|
5965
|
+
* WHERE this region's credit readout sits (issue #62).
|
|
5966
|
+
*
|
|
5967
|
+
* A local reader rather than a schema default so a config written before the
|
|
5968
|
+
* setting existed reads as `composer` — the seat that was the only one, so an
|
|
5969
|
+
* upgrade moves nothing on screen.
|
|
5970
|
+
*/
|
|
5971
|
+
const creditPositionOf = (config, region) => regionStateOf(config, region).creditPosition === "stats" ? "stats" : "composer";
|
|
5953
5972
|
const poolPreferencesOf = (config, region) => {
|
|
5954
5973
|
const pool = regionStateOf(config, region).pool;
|
|
5955
5974
|
const probeInputTokens = pool?.probeInputTokens;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-connect-workbuddy",
|
|
3
3
|
"displayName": "DSH Connect WorkBuddy",
|
|
4
4
|
"description": "把本机登录的 WorkBuddy 模型接入 DeepSeek Harness,并提供只读的积分概览与模型管理。",
|
|
5
|
-
"version": "4.
|
|
5
|
+
"version": "4.5.0",
|
|
6
6
|
"author": "dingminhua",
|
|
7
7
|
"copyright": "Copyright (c) 2026 LaoDing",
|
|
8
8
|
"license": "MIT",
|