dsh-token-gauge 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -6
- package/lib/client.mjs +77 -13
- package/package.json +14 -12
- package/src/client.ts +105 -13
package/README.md
CHANGED
|
@@ -19,13 +19,19 @@ DeepSeek Harness(DSH)Web 侧边栏插件:以「机车风格仪表盘」实
|
|
|
19
19
|
## 安装
|
|
20
20
|
|
|
21
21
|
> **兼容性**:本插件面向 **DSH 0.1.1-rc.1 / 0.1.1-rc.2(稳定版)**。采用 `dsh.bundle.patch` 清单 + `cordis.patch.yml` 注册,符合稳定版的插件机制(不兼容新版 0.1.2-alpha 的 `dsh.host/client` 清单)。
|
|
22
|
-
>
|
|
22
|
+
> **前置依赖 `dsh-better-sidebar`**:本插件的 Tab 通过 `ctx.betterSidebar.registerTab` 注册到它的**右侧工作台**,所以它必须是 **profile 的直接依赖、且已挂进 `dsh.profile.bundles`**:
|
|
23
|
+
>
|
|
24
|
+
> `dsh plugin --profile web add dsh-better-sidebar`
|
|
25
|
+
>
|
|
26
|
+
> ⚠️ **陷阱(很常见)**:它若只是本插件的**传递依赖**,只会被装进 `node_modules` 而**不会**被挂成 bundle 层 —— 加载树里没有条目、`betterSidebar` 服务不会被 provide,表现就是本插件「已启用 / 已挂载」却没有任何侧边栏 Tab,**而且全程零报错**(0.1.7 起会在浏览器控制台打出明确指引)。同理请确认 `cordis.patch.yml` 与 `.dsh-market/state.json` 没有把它列为 `disabled`。
|
|
27
|
+
>
|
|
28
|
+
> 另注:Tab 注册后会出现在侧边栏 **「+ 新建 Tab」菜单**里(这是 better-sidebar 的设计:注册的是 tab *类型*,需你点开一次才会出现在标签栏),详见下方「排查」。
|
|
23
29
|
|
|
24
30
|
### 方式一:从市场安装(推荐)
|
|
25
31
|
|
|
26
|
-
> **强烈建议钉版本安装**:`dsh plugin add` 底层是 pnpm,受你本地 `.npmrc` 指向的镜像(如 npmmirror)同步延迟影响,裸 `add dsh-token-gauge` 在 `^0.1.0` 范围内可能解析到尚未带 `dsh.bundle` 的旧版 `0.1.0`(装上去只会是普通依赖、不进 bundle 层、侧边栏不出现)。**显式钉 `0.1.
|
|
32
|
+
> **强烈建议钉版本安装**:`dsh plugin add` 底层是 pnpm,受你本地 `.npmrc` 指向的镜像(如 npmmirror)同步延迟影响,裸 `add dsh-token-gauge` 在 `^0.1.0` 范围内可能解析到尚未带 `dsh.bundle` 的旧版 `0.1.0`(装上去只会是普通依赖、不进 bundle 层、侧边栏不出现)。**显式钉 `0.1.7` 可彻底规避这一坑**:
|
|
27
33
|
```bash
|
|
28
|
-
dsh plugin --profile web add dsh-token-gauge@0.1.
|
|
34
|
+
dsh plugin --profile web add dsh-token-gauge@0.1.7
|
|
29
35
|
dsh web
|
|
30
36
|
```
|
|
31
37
|
> 若想装最新版,可先 `dsh plugin --profile web update dsh-token-gauge` 升级,再确认 `node_modules/dsh-token-gauge/package.json` 的 `dsh.bundle.patch` 字段存在。
|
|
@@ -35,9 +41,9 @@ dsh web
|
|
|
35
41
|
若此前已误装 `0.1.0`(表现为 `dsh.profile.bundles` 未新增 `token-gauge` 层,并出现 `declares no dsh.bundle — installed as a plain dependency` 警告),按下面两步切换即可,无需手动改 bundles:
|
|
36
42
|
```bash
|
|
37
43
|
dsh plugin --profile web remove dsh-token-gauge
|
|
38
|
-
dsh plugin --profile web add dsh-token-gauge@0.1.
|
|
44
|
+
dsh plugin --profile web add dsh-token-gauge@0.1.7 # 钉版本,避免又解析回 0.1.0
|
|
39
45
|
```
|
|
40
|
-
装完后 `dsh.profile.bundles` 在 reconcile 时自动把 `token-gauge`
|
|
46
|
+
装完后 `dsh.profile.bundles` 在 reconcile 时自动把 `token-gauge` 加为最后一层,重启 `dsh web` 后,在侧边栏 **「+ 新建 Tab」菜单**里选「Token 仪表盘」即可打开。
|
|
41
47
|
|
|
42
48
|
### 方式二:本地源码安装
|
|
43
49
|
```bash
|
|
@@ -54,7 +60,7 @@ node build.mjs
|
|
|
54
60
|
dsh web --patch ./cordis.patch.yml
|
|
55
61
|
```
|
|
56
62
|
|
|
57
|
-
|
|
63
|
+
安装后**完整重启** DSH Web(进程内 client 缓存只在重启时清空,硬刷新不够),然后在侧边栏点 **「+ 新建 Tab」→「Token 仪表盘」** 打开它。开始对话产生 Token 后,仪表盘即实时更新。
|
|
58
64
|
|
|
59
65
|
### 排查:侧边栏 Tab 不出现
|
|
60
66
|
|
|
@@ -62,6 +68,8 @@ DSH 把插件拆成 host(Node 端)与 client(浏览器端)两半,走
|
|
|
62
68
|
|
|
63
69
|
- **host 半边是否正常**:浏览器访问 `http://<dsh-host>/plugins/dsh-token-gauge/state` 应返回 200 + 状态 JSON,且 `~/.dsh-token-gauge/` 目录已生成。host 不工作通常是 bundle 层没挂上(`dsh.profile.bundles` 缺 `token-gauge`)。
|
|
64
70
|
- **client 半边是否正常**:访问 `/plugins/dsh-token-gauge/client.js` 应返回 200(打包后的客户端脚本);首页 `window.__DSH_BOOT__` 里应包含该 client。若此处 404,常见原因是 `package.json` 的 `exports` 未导出 `./package.json`——DSH 的 client 加载器用 `require.resolve('<包名>/package.json')` 定位 client 包,缺少该子路径导出会被静默判为「非 client 包」且(进程内)缓存。**本包已显式导出 `"./package.json"`,如遇此问题请确认装的是 0.1.2+**。
|
|
71
|
+
- **`dsh-better-sidebar` 是否真的挂载了**(「已启用/已挂载却无 Tab」的头号原因):Tab 挂在它的右侧工作台上。检查 `~/.dsh/profiles/web/package.json` 的 `dsh.profile.bundles` **和** `dependencies` 里是否**都**有 `dsh-better-sidebar` —— **只存在于 `node_modules` 不算**:作为传递依赖装进来的包不会被挂成 bundle 层,服务不会被 provide,于是本插件完全静默(0.1.7 起浏览器控制台会打出指引)。同时确认 `cordis.patch.yml` 与 `.dsh-market/state.json` 未把它标为 `disabled`。
|
|
72
|
+
- **类型已注册,但标签栏里看不到**:`registerTab` 注册的是 tab **类型**,不会自动出现在标签栏 —— 需到侧边栏 **「+ 新建 Tab」菜单**点选「Token 仪表盘」打开一次。若菜单里也找不到,去看侧边栏**设置面板的 Tab 列表**:列表里有 = 注册成功只是没打开;列表里也没有 = 服务没拿到(回到上一条)。
|
|
65
73
|
- 改动后务必**完整重启 `dsh web`**(进程内 client 缓存只在重启时清空),仅硬刷新不够。
|
|
66
74
|
|
|
67
75
|
## 数据来源与费用口径
|
package/lib/client.mjs
CHANGED
|
@@ -946,23 +946,87 @@ var gaugeRef = (el) => {
|
|
|
946
946
|
function GaugeTab() {
|
|
947
947
|
return import_react.default.createElement("div", { ref: gaugeRef, className: "tg-tab-root" });
|
|
948
948
|
}
|
|
949
|
+
var GaugeIcon = (size) => {
|
|
950
|
+
const s = typeof size === "number" && size > 0 ? size : 16;
|
|
951
|
+
const svgProps = {
|
|
952
|
+
width: s,
|
|
953
|
+
height: s,
|
|
954
|
+
viewBox: "0 0 16 16",
|
|
955
|
+
fill: "none",
|
|
956
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
957
|
+
"aria-hidden": "true"
|
|
958
|
+
};
|
|
959
|
+
const arc = import_react.default.createElement("path", {
|
|
960
|
+
d: "M2.6 11.6a5.6 5.6 0 1 1 10.8 0",
|
|
961
|
+
stroke: "currentColor",
|
|
962
|
+
strokeWidth: 1.5,
|
|
963
|
+
strokeLinecap: "round"
|
|
964
|
+
});
|
|
965
|
+
const needle = import_react.default.createElement("path", {
|
|
966
|
+
d: "M8 11.6 10.4 7.2",
|
|
967
|
+
stroke: "currentColor",
|
|
968
|
+
strokeWidth: 1.5,
|
|
969
|
+
strokeLinecap: "round"
|
|
970
|
+
});
|
|
971
|
+
return import_react.default.createElement("svg", svgProps, arc, needle);
|
|
972
|
+
};
|
|
973
|
+
var TAB_ORDER = 90;
|
|
949
974
|
var name = PLUGIN_ID;
|
|
950
975
|
function apply(ctx) {
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
976
|
+
let registered = false;
|
|
977
|
+
let unregister = void 0;
|
|
978
|
+
const register = (sidebar) => {
|
|
979
|
+
if (registered || !sidebar || typeof sidebar.registerTab !== "function") return;
|
|
980
|
+
registered = true;
|
|
981
|
+
try {
|
|
982
|
+
unregister = sidebar.registerTab({
|
|
983
|
+
id: PLUGIN_ID,
|
|
984
|
+
title: "Token \u4EEA\u8868\u76D8",
|
|
985
|
+
single: true,
|
|
986
|
+
order: TAB_ORDER,
|
|
987
|
+
icon: GaugeIcon,
|
|
988
|
+
component: GaugeTab
|
|
989
|
+
});
|
|
990
|
+
} catch (e) {
|
|
991
|
+
registered = false;
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
let disposeInject = void 0;
|
|
995
|
+
try {
|
|
996
|
+
disposeInject = ctx.inject(["betterSidebar"], (sidebarCtx) => {
|
|
997
|
+
register(sidebarCtx && sidebarCtx.betterSidebar);
|
|
959
998
|
});
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
999
|
+
} catch (e) {
|
|
1000
|
+
}
|
|
1001
|
+
const tryRead = () => {
|
|
1002
|
+
if (registered) return;
|
|
1003
|
+
try {
|
|
1004
|
+
const s = typeof ctx.get === "function" ? ctx.get("betterSidebar") : ctx.betterSidebar;
|
|
1005
|
+
register(s);
|
|
1006
|
+
} catch (e) {
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
tryRead();
|
|
1010
|
+
let timer = void 0;
|
|
1011
|
+
if (!registered) {
|
|
1012
|
+
timer = setInterval(() => {
|
|
1013
|
+
tryRead();
|
|
1014
|
+
if (registered && timer !== void 0) clearInterval(timer);
|
|
1015
|
+
}, 500);
|
|
1016
|
+
}
|
|
1017
|
+
const stop = setTimeout(() => {
|
|
1018
|
+
if (timer !== void 0) clearInterval(timer);
|
|
1019
|
+
if (!registered) {
|
|
1020
|
+
console.error(
|
|
1021
|
+
"[dsh-token-gauge] \u672A\u53D6\u5F97 dsh-better-sidebar \u670D\u52A1\uFF0C\u4FA7\u8FB9\u680F Tab \u672A\u6CE8\u518C\u3002\n \u8BF7\u628A\u5B83\u88C5\u6210 profile \u7684\u300C\u76F4\u63A5\u4F9D\u8D56\u300D\u5E76\u6302\u8F7D\uFF1A\n dsh plugin --profile web add dsh-better-sidebar\n \u7136\u540E\u5B8C\u6574\u91CD\u542F dsh web\u3002\n \u6CE8\u610F\uFF1A\u4EC5\u4F5C\u4E3A\u672C\u63D2\u4EF6\u7684\u4F20\u9012\u4F9D\u8D56\u5B58\u5728\u4E8E node_modules \u662F\u4E0D\u591F\u7684\u2014\u2014DSH \u4E0D\u4F1A\u628A\n \u4F20\u9012\u4F9D\u8D56\u6302\u6210 bundle \u5C42\uFF0C\u90A3\u79CD\u60C5\u51B5\u4E0B\u672C\u63D2\u4EF6\u4F1A\u9759\u9ED8\u5730\u6CA1\u6709 Tab\u3002"
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
}, 3e4);
|
|
964
1025
|
return () => {
|
|
965
|
-
if (
|
|
1026
|
+
if (timer !== void 0) clearInterval(timer);
|
|
1027
|
+
clearTimeout(stop);
|
|
1028
|
+
if (typeof unregister === "function") unregister();
|
|
1029
|
+
if (typeof disposeInject === "function") disposeInject();
|
|
966
1030
|
};
|
|
967
1031
|
}
|
|
968
1032
|
; return module.exports; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-token-gauge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "DSH 机车风格 Token 用量/费用仪表盘侧边栏插件(稳定版 0.1.1-rc.x 兼容)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.mjs",
|
|
@@ -47,13 +47,6 @@
|
|
|
47
47
|
"bundle": {
|
|
48
48
|
"patch": "./cordis.patch.yml"
|
|
49
49
|
},
|
|
50
|
-
"client": {
|
|
51
|
-
"inject": [
|
|
52
|
-
"@deepseek-ai/dsh-client-ui-sidebar",
|
|
53
|
-
"dsh-better-sidebar"
|
|
54
|
-
],
|
|
55
|
-
"platform": "web"
|
|
56
|
-
},
|
|
57
50
|
"compatibility": {
|
|
58
51
|
"dshReleases": {
|
|
59
52
|
"0.1.1-rc.1": "compatible",
|
|
@@ -65,12 +58,21 @@
|
|
|
65
58
|
"build": "node build.mjs",
|
|
66
59
|
"prepack": "node build.mjs"
|
|
67
60
|
},
|
|
68
|
-
"dependencies": {
|
|
69
|
-
"dsh-better-sidebar": "^0.15.2"
|
|
70
|
-
},
|
|
71
61
|
"peerDependencies": {
|
|
72
62
|
"@deepseek-ai/cordis": "*",
|
|
73
|
-
"@deepseek-ai/dsh-host-webserver": "*"
|
|
63
|
+
"@deepseek-ai/dsh-host-webserver": "*",
|
|
64
|
+
"dsh-better-sidebar": ">=0.15.2"
|
|
65
|
+
},
|
|
66
|
+
"peerDependenciesMeta": {
|
|
67
|
+
"@deepseek-ai/cordis": {
|
|
68
|
+
"optional": true
|
|
69
|
+
},
|
|
70
|
+
"@deepseek-ai/dsh-host-webserver": {
|
|
71
|
+
"optional": true
|
|
72
|
+
},
|
|
73
|
+
"dsh-better-sidebar": {
|
|
74
|
+
"optional": true
|
|
75
|
+
}
|
|
74
76
|
},
|
|
75
77
|
"devDependencies": {
|
|
76
78
|
"@deepseek-ai/cordis": "*",
|
package/src/client.ts
CHANGED
|
@@ -203,6 +203,39 @@ function GaugeTab(): React.ReactElement {
|
|
|
203
203
|
return React.createElement('div', { ref: gaugeRef, className: 'tg-tab-root' })
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
+
// 侧边栏图标:better-sidebar 以 `icon(size)` 形式调用(见其内置 tab:
|
|
207
|
+
// `icon: (size) => jsx(Icon, { size })`),故这里接收数字尺寸。用 currentColor
|
|
208
|
+
// 着色以自适应明暗主题,纯 SVG 零依赖。
|
|
209
|
+
const GaugeIcon = (size?: number): any => {
|
|
210
|
+
const s = typeof size === 'number' && size > 0 ? size : 16
|
|
211
|
+
const svgProps: any = {
|
|
212
|
+
width: s,
|
|
213
|
+
height: s,
|
|
214
|
+
viewBox: '0 0 16 16',
|
|
215
|
+
fill: 'none',
|
|
216
|
+
xmlns: 'http://www.w3.org/2000/svg',
|
|
217
|
+
'aria-hidden': 'true',
|
|
218
|
+
}
|
|
219
|
+
// 仪表盘弧 + 指针
|
|
220
|
+
const arc = React.createElement('path', {
|
|
221
|
+
d: 'M2.6 11.6a5.6 5.6 0 1 1 10.8 0',
|
|
222
|
+
stroke: 'currentColor',
|
|
223
|
+
strokeWidth: 1.5,
|
|
224
|
+
strokeLinecap: 'round',
|
|
225
|
+
})
|
|
226
|
+
const needle = React.createElement('path', {
|
|
227
|
+
d: 'M8 11.6 10.4 7.2',
|
|
228
|
+
stroke: 'currentColor',
|
|
229
|
+
strokeWidth: 1.5,
|
|
230
|
+
strokeLinecap: 'round',
|
|
231
|
+
})
|
|
232
|
+
return React.createElement('svg', svgProps, arc, needle)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// 在「+ 新建 Tab」菜单里的排序位。内置 tab 用 20/91/110 等,order 缺省为 100
|
|
236
|
+
// 会排到菜单最底部不易发现,这里显式给一个偏前的值。
|
|
237
|
+
const TAB_ORDER = 90
|
|
238
|
+
|
|
206
239
|
export const name = PLUGIN_ID
|
|
207
240
|
|
|
208
241
|
// 兄弟 bundle 提供的服务(betterSidebar)在本机这个 cordis 里两种读法都不行,必须用
|
|
@@ -221,22 +254,81 @@ export const name = PLUGIN_ID
|
|
|
221
254
|
// 合法属性,读它不触发守卫;且注册被延迟到服务就绪那一刻(兄弟 bundle 提供得早或晚
|
|
222
255
|
// 都能注册上)。生态插件 @linxin666/dsh-client-ui-aionui-panel 用的正是这个模式。
|
|
223
256
|
export function apply(ctx: any): (() => void) | void {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
257
|
+
let registered = false
|
|
258
|
+
let unregister: unknown = void 0
|
|
259
|
+
|
|
260
|
+
// 注册一次即止(registerTab 对重复 id 会抛 "already registered")
|
|
261
|
+
const register = (sidebar: any): void => {
|
|
262
|
+
if (registered || !sidebar || typeof sidebar.registerTab !== 'function') return
|
|
263
|
+
registered = true
|
|
264
|
+
try {
|
|
265
|
+
unregister = sidebar.registerTab({
|
|
266
|
+
id: PLUGIN_ID,
|
|
267
|
+
title: 'Token 仪表盘',
|
|
268
|
+
single: true,
|
|
269
|
+
order: TAB_ORDER,
|
|
270
|
+
icon: GaugeIcon,
|
|
271
|
+
component: GaugeTab,
|
|
272
|
+
})
|
|
273
|
+
} catch (e) {
|
|
274
|
+
registered = false // 失败则允许后续重试
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// 主路径:ctx.inject 回调——服务就绪时触发,回调内读 sidebarCtx.betterSidebar 合法
|
|
279
|
+
let disposeInject: unknown = void 0
|
|
280
|
+
try {
|
|
281
|
+
disposeInject = ctx.inject(['betterSidebar'], (sidebarCtx: any) => {
|
|
282
|
+
register(sidebarCtx && sidebarCtx.betterSidebar)
|
|
232
283
|
})
|
|
233
|
-
|
|
234
|
-
|
|
284
|
+
} catch (e) {
|
|
285
|
+
/* 运行时无 ctx.inject:交由下面的轮询兜底 */
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// 兜底:轮询读取。0811 严格注入下未声明 inject 时 ctx.get('betterSidebar')
|
|
289
|
+
// 会「抛异常」(而非返回 undefined),所以必须用 try/catch 包住读取。
|
|
290
|
+
const tryRead = (): void => {
|
|
291
|
+
if (registered) return
|
|
292
|
+
try {
|
|
293
|
+
const s = typeof ctx.get === 'function' ? ctx.get('betterSidebar') : ctx.betterSidebar
|
|
294
|
+
register(s)
|
|
295
|
+
} catch (e) {
|
|
296
|
+
/* 服务此刻不可达,下一轮再试 */
|
|
235
297
|
}
|
|
236
|
-
}
|
|
298
|
+
}
|
|
299
|
+
tryRead()
|
|
300
|
+
|
|
301
|
+
let timer: unknown = void 0
|
|
302
|
+
if (!registered) {
|
|
303
|
+
timer = setInterval(() => {
|
|
304
|
+
tryRead()
|
|
305
|
+
if (registered && timer !== void 0) clearInterval(timer as number)
|
|
306
|
+
}, 500)
|
|
307
|
+
}
|
|
308
|
+
const stop = setTimeout(() => {
|
|
309
|
+
if (timer !== void 0) clearInterval(timer as number)
|
|
310
|
+
if (!registered) {
|
|
311
|
+
// 服务始终不可达 ≈ dsh-better-sidebar 没被挂成 profile 层。
|
|
312
|
+
// 关键陷阱:它若只是本插件的「传递依赖」,只会落进 node_modules 而不会被
|
|
313
|
+
// 加入 dsh.profile.bundles —— 加载树里没有条目,服务根本不会被 provide,
|
|
314
|
+
// 本插件于是「已挂载但无 Tab」且全程零报错(真·静默失败)。故这里必须
|
|
315
|
+
// 打出可操作的指引,而不是默默放弃。
|
|
316
|
+
console.error(
|
|
317
|
+
'[dsh-token-gauge] 未取得 dsh-better-sidebar 服务,侧边栏 Tab 未注册。\n' +
|
|
318
|
+
' 请把它装成 profile 的「直接依赖」并挂载:\n' +
|
|
319
|
+
' dsh plugin --profile web add dsh-better-sidebar\n' +
|
|
320
|
+
' 然后完整重启 dsh web。\n' +
|
|
321
|
+
' 注意:仅作为本插件的传递依赖存在于 node_modules 是不够的——DSH 不会把\n' +
|
|
322
|
+
' 传递依赖挂成 bundle 层,那种情况下本插件会静默地没有 Tab。',
|
|
323
|
+
)
|
|
324
|
+
}
|
|
325
|
+
}, 30000)
|
|
237
326
|
|
|
238
|
-
//
|
|
327
|
+
// 插件级 disposer
|
|
239
328
|
return () => {
|
|
240
|
-
if (
|
|
329
|
+
if (timer !== void 0) clearInterval(timer as number)
|
|
330
|
+
clearTimeout(stop)
|
|
331
|
+
if (typeof unregister === 'function') (unregister as () => void)()
|
|
332
|
+
if (typeof disposeInject === 'function') (disposeInject as () => void)()
|
|
241
333
|
}
|
|
242
334
|
}
|