@yfwu2020/dsh-selection-explain 0.1.0 → 0.1.1
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 +35 -3
- package/package.json +23 -6
- package/scripts/build.sh +29 -3
- package/scripts/test-client.mjs +39 -9
package/README.md
CHANGED
|
@@ -81,7 +81,17 @@
|
|
|
81
81
|
|
|
82
82
|
## 安装
|
|
83
83
|
|
|
84
|
-
### 方式一:从
|
|
84
|
+
### 方式一:从 npm 安装
|
|
85
|
+
|
|
86
|
+
已发布到 npm registry:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm install @yfwu2020/dsh-selection-explain
|
|
90
|
+
# 或直接装配进 profile(npm 包名可直接用)
|
|
91
|
+
dsh plugin --profile web add @yfwu2020/dsh-selection-explain
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 方式二:从 Release 安装
|
|
85
95
|
|
|
86
96
|
每个 Release 都附了**构建好的 `.tgz`**(含 `lib/` 产物,装完即可用,无需 clone 源码):
|
|
87
97
|
|
|
@@ -98,12 +108,15 @@ tar -xzf yfwu2020-dsh-selection-explain-0.1.0.tgz \
|
|
|
98
108
|
dsh plugin --profile web add ~/.dsh/plugins/dsh-selection-explain
|
|
99
109
|
```
|
|
100
110
|
|
|
101
|
-
###
|
|
111
|
+
### 方式三:从源码安装
|
|
102
112
|
|
|
103
113
|
```bash
|
|
104
114
|
git clone git@github.com:yfwu2020/dsh-selection-explain.git
|
|
105
115
|
cd dsh-selection-explain
|
|
106
116
|
|
|
117
|
+
# 装依赖(编译用的 @deepseek-ai/* 类型与 tsc 都在 devDependencies 里)
|
|
118
|
+
npm install
|
|
119
|
+
|
|
107
120
|
# 构建(自动探测 DSH 运行时;host 走 tsc,client 是手写 bundle 直接拷贝)
|
|
108
121
|
bash scripts/build.sh
|
|
109
122
|
|
|
@@ -111,7 +124,8 @@ bash scripts/build.sh
|
|
|
111
124
|
dsh plugin --profile web add .
|
|
112
125
|
```
|
|
113
126
|
|
|
114
|
-
> `scripts/build.sh`
|
|
127
|
+
> `scripts/build.sh` 会按顺序探测 DSH 运行时:**项目自己的 `node_modules`**(CI/`npm install` 走这条)
|
|
128
|
+
> → `DSH_CHECKOUT` → PATH 上的 `dsh` → 常见 checkout → npx 缓存。
|
|
115
129
|
> 探测不到时可以显式指定:`DSH_CHECKOUT=/path/to/dsh-harness bash scripts/build.sh`。
|
|
116
130
|
|
|
117
131
|
装好后**刷新页面**,选中文字试试。浏览器控制台会打印 `[dsh-selection-explain] 划词解读已就绪(选中文字试试)`。
|
|
@@ -281,6 +295,24 @@ node scripts/dump-prompt.mjs <sessionId> "<选中文字>"
|
|
|
281
295
|
node scripts/dump-prompt.mjs --no-session "<选中文字>" # 不带会话背景的对照组
|
|
282
296
|
```
|
|
283
297
|
|
|
298
|
+
## 发布(维护者)
|
|
299
|
+
|
|
300
|
+
发布走 **Trusted Publishing**(GitHub OIDC),**不需要任何长期 npm token**:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
# 1) 改 package.json 的 version(例如 0.2.0)
|
|
304
|
+
# 2) 打同名 tag 推上去
|
|
305
|
+
git tag v0.2.0 && git push origin v0.2.0
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
`.github/workflows/publish.yml` 会自动:校验 tag 与 `version` 一致 → `npm install` → 构建 → 跑 463 条测试 → 类型检查 → `npm publish --provenance`(附 provenance 签名)。
|
|
309
|
+
|
|
310
|
+
npm 侧只需配一次:包设置 → **Trusted Publisher** → GitHub Actions,填 `yfwu2020` / `dsh-selection-explain` / `publish.yml`,并勾选允许 **`npm publish`**(默认只允许 `npm stage publish`)。
|
|
311
|
+
|
|
312
|
+
> 注意:`devDependencies` 里的 `@deepseek-ai/*` 钉在 **`0.1.5-rc.3`**(与插件开发所依据的运行时一致)。
|
|
313
|
+
> npm 上这些包的 `latest` 还停在 `0.0.1-rc.x`,API 更旧(缺 `WebServer`、`createSystemMessage` 等),
|
|
314
|
+
> 用 `latest` 会编译失败——升级运行时时要同步改这里。
|
|
315
|
+
|
|
284
316
|
## 目录
|
|
285
317
|
|
|
286
318
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yfwu2020/dsh-selection-explain",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "DSH 划词解读:选中文字浮出按钮,点击弹窗流式显示专业中英翻译 + 该文字在当前上下文中的含义详解",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -14,12 +14,29 @@
|
|
|
14
14
|
"README.md"
|
|
15
15
|
],
|
|
16
16
|
"license": "MIT",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/yfwu2020/dsh-selection-explain.git"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/yfwu2020/dsh-selection-explain#readme",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/yfwu2020/dsh-selection-explain/issues"
|
|
24
|
+
},
|
|
17
25
|
"peerDependencies": {
|
|
18
|
-
"@deepseek-ai/cordis": ">=4 <5",
|
|
19
|
-
"@deepseek-ai/dsh-agent-default-model": ">=0.
|
|
20
|
-
"@deepseek-ai/dsh-host-webserver": ">=0.
|
|
21
|
-
"@deepseek-ai/dsh-llm": ">=0.
|
|
22
|
-
"@deepseek-ai/schemastery": ">=3 <4"
|
|
26
|
+
"@deepseek-ai/cordis": ">=4.0.2 <5",
|
|
27
|
+
"@deepseek-ai/dsh-agent-default-model": ">=0.1.5-rc <2",
|
|
28
|
+
"@deepseek-ai/dsh-host-webserver": ">=0.1.5-rc <2",
|
|
29
|
+
"@deepseek-ai/dsh-llm": ">=0.1.5-rc <2",
|
|
30
|
+
"@deepseek-ai/schemastery": ">=3.18 <4"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@deepseek-ai/cordis": "4.0.2",
|
|
34
|
+
"@deepseek-ai/dsh-agent-default-model": "0.1.5-rc.3",
|
|
35
|
+
"@deepseek-ai/dsh-host-webserver": "0.1.5-rc.3",
|
|
36
|
+
"@deepseek-ai/dsh-llm": "0.1.5-rc.3",
|
|
37
|
+
"@deepseek-ai/schemastery": "3.18.2",
|
|
38
|
+
"@types/node": "^22.14.0",
|
|
39
|
+
"typescript": "^5.9.3"
|
|
23
40
|
},
|
|
24
41
|
"scripts": {
|
|
25
42
|
"build": "bash scripts/build.sh",
|
package/scripts/build.sh
CHANGED
|
@@ -23,6 +23,11 @@ probe() {
|
|
|
23
23
|
|
|
24
24
|
find_runtime() {
|
|
25
25
|
local found=""
|
|
26
|
+
# 0) 项目自己的 node_modules —— CI(GitHub Actions)走这条路:
|
|
27
|
+
# npm install 会把 peerDependencies(@deepseek-ai/dsh-llm 等)装进来,
|
|
28
|
+
# 没有这一步的话干净环境必然探测失败。
|
|
29
|
+
found="$(probe "$ROOT/node_modules" || true)"
|
|
30
|
+
if [ -n "$found" ]; then echo "$found"; return; fi
|
|
26
31
|
# 1) 显式指定
|
|
27
32
|
if [ -n "${DSH_CHECKOUT:-}" ]; then
|
|
28
33
|
found="$(probe "$DSH_CHECKOUT" || true)"
|
|
@@ -66,15 +71,32 @@ fi
|
|
|
66
71
|
echo "=== DSH 运行时:$RUNTIME ==="
|
|
67
72
|
|
|
68
73
|
# ── ② 链接 @deepseek-ai / @types(整层 scope 链接,类型与运行时共用) ──
|
|
74
|
+
# 两种情况必须区分:
|
|
75
|
+
# · 本机开发:node_modules 里没有这些包,链接到 DSH 运行时(原来唯一的行为)
|
|
76
|
+
# · CI(npm install 过):peerDependencies / devDependencies 已经装成**真实目录**,
|
|
77
|
+
# 此时绝不能动它 —— 否则会把 node_modules/@deepseek-ai 链成它自己(自引用死循环,
|
|
78
|
+
# probe 随后报 "Too many levels of symbolic links",node_modules 被破坏)。
|
|
69
79
|
node -e '
|
|
70
80
|
const fs = require("fs")
|
|
71
81
|
const path = require("path")
|
|
72
82
|
const runtime = process.argv[1]
|
|
73
83
|
const root = process.argv[2]
|
|
84
|
+
const real = (p) => { try { return fs.realpathSync(p) } catch { return "" } }
|
|
74
85
|
for (const name of ["@deepseek-ai", "@types"]) {
|
|
75
86
|
const target = path.join(runtime, name)
|
|
76
87
|
if (!fs.existsSync(target)) continue
|
|
77
88
|
const link = path.join(root, "node_modules", name)
|
|
89
|
+
const linkReal = real(link)
|
|
90
|
+
// 已是真实目录(非符号链接)→ 保留,npm 装的才是权威版本
|
|
91
|
+
if (linkReal && !fs.lstatSync(link).isSymbolicLink()) {
|
|
92
|
+
console.log("kept node_modules/" + name + "(真实目录,不链接)")
|
|
93
|
+
continue
|
|
94
|
+
}
|
|
95
|
+
// 已经指向同一个地方 → 无需重建
|
|
96
|
+
if (linkReal && linkReal === real(target)) {
|
|
97
|
+
console.log("ok node_modules/" + name + "(已指向运行时)")
|
|
98
|
+
continue
|
|
99
|
+
}
|
|
78
100
|
fs.rmSync(link, { recursive: true, force: true })
|
|
79
101
|
fs.mkdirSync(path.dirname(link), { recursive: true })
|
|
80
102
|
fs.symlinkSync(target, link, process.platform === "win32" ? "junction" : "dir")
|
|
@@ -83,10 +105,14 @@ for (const name of ["@deepseek-ai", "@types"]) {
|
|
|
83
105
|
' "$RUNTIME" "$ROOT"
|
|
84
106
|
|
|
85
107
|
# ── ③ 编译 host 半 ──
|
|
86
|
-
|
|
87
|
-
|
|
108
|
+
# tsc 查找顺序:项目自己的 node_modules/.bin(CI 走这条,devDependencies 里已声明)
|
|
109
|
+
# → PATH 上的全局 tsc → DSH 运行时里的 tsc。
|
|
110
|
+
TSC=""
|
|
111
|
+
if [ -x "$ROOT/node_modules/.bin/tsc" ]; then TSC="$ROOT/node_modules/.bin/tsc"; fi
|
|
112
|
+
if [ -z "$TSC" ]; then TSC="$(command -v tsc || true)"; fi
|
|
113
|
+
if [ -z "$TSC" ] && [ -x "$RUNTIME/.bin/tsc" ]; then TSC="$RUNTIME/.bin/tsc"; fi
|
|
88
114
|
if [ -z "$TSC" ]; then
|
|
89
|
-
echo "build: 找不到 tsc
|
|
115
|
+
echo "build: 找不到 tsc —— 在项目里装一个(npm i -D typescript)或设置 DSH_CHECKOUT" >&2
|
|
90
116
|
exit 1
|
|
91
117
|
fi
|
|
92
118
|
echo "=== tsc $("$TSC" -v) ==="
|
package/scripts/test-client.mjs
CHANGED
|
@@ -903,15 +903,32 @@ assert('弹窗显示选中文字', !!panel && textOf(panel).indexOf('the migrati
|
|
|
903
903
|
|
|
904
904
|
const hook = windowStub.__dshSelectionExplain
|
|
905
905
|
assert('自检钩子可用', !!hook && typeof hook.state === 'function')
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
)
|
|
906
|
+
|
|
907
|
+
// ── 宿主可达性探测 ──
|
|
908
|
+
// 本脚本的一部分断言是**真实 host 集成测试**(连 ORIGIN 上的 /ping 与 SSE /analyze)。
|
|
909
|
+
// 本地开发时 host 在跑,这些断言全跑;CI(GitHub Actions)里没有宿主,
|
|
910
|
+
// 此时**跳过**在线断言并打印说明,而不是抛 ECONNREFUSED 崩掉整条发布流水线。
|
|
911
|
+
let HOST_UP = false
|
|
912
|
+
let ping = null
|
|
913
|
+
try {
|
|
914
|
+
ping = await hook.ping()
|
|
915
|
+
HOST_UP = !!(ping && ping.ok === true)
|
|
916
|
+
} catch (error) {
|
|
917
|
+
HOST_UP = false
|
|
918
|
+
}
|
|
919
|
+
if (HOST_UP) {
|
|
920
|
+
assert('host ping 可达', !!ping && ping.ok === true, JSON.stringify(ping && ping.route))
|
|
921
|
+
assert('请求载荷携带当前会话 id', sent.length > 0 && sent[0].sessionId === 'session-stub-1', JSON.stringify(sent[0] && { sessionId: sent[0].sessionId, label: sent[0].label }))
|
|
922
|
+
assert('载荷不带 effort(推理档位固定在 host 配置)', sent[0] && sent[0].effort === undefined, String(sent[0] && sent[0].effort))
|
|
923
|
+
assert(
|
|
924
|
+
'三档档位都能从 /ping 读到',
|
|
925
|
+
!!ping && !!ping.reasoningEffortByStage && typeof ping.reasoningEffortByStage.translation === 'string' && typeof ping.reasoningEffortByStage.detail === 'string' && typeof ping.reasoningEffortByStage.chat === 'string',
|
|
926
|
+
JSON.stringify(ping && ping.reasoningEffortByStage),
|
|
927
|
+
)
|
|
928
|
+
} else {
|
|
929
|
+
console.log(`SKIP host 不可达(${ORIGIN})—— 跳过在线断言(/ping 可达性、SSE 流式渲染、面板交互)`)
|
|
930
|
+
console.log('SKIP 本地开发想跑全量:先启动 DSH(默认 http://127.0.0.1:3080)再执行本脚本')
|
|
931
|
+
}
|
|
915
932
|
{
|
|
916
933
|
// 默认值断言走源码(不依赖 3080 上那份部署配置改没改)
|
|
917
934
|
const hostSrc = readFileSync(resolve(HERE, '..', 'src', 'index.ts'), 'utf8')
|
|
@@ -959,6 +976,10 @@ assert(
|
|
|
959
976
|
)
|
|
960
977
|
}
|
|
961
978
|
|
|
979
|
+
// ── 以下全部是真实 host 集成断言(SSE 流式渲染 / 面板交互 / 历史 / 清理)──
|
|
980
|
+
// CI 里没有宿主,跳过;本地开发有宿主时全跑。
|
|
981
|
+
if (HOST_UP) {
|
|
982
|
+
|
|
962
983
|
const deadline = Date.now() + 90000
|
|
963
984
|
while (Date.now() < deadline) {
|
|
964
985
|
const state = hook.state()
|
|
@@ -2484,3 +2505,12 @@ for (const dispose of disposers.reverse()) {
|
|
|
2484
2505
|
assert('清理后 DOM 归零', mount.children.length === 0 && body.children.indexOf(container) >= 0)
|
|
2485
2506
|
assert('清理后钩子移除', windowStub.__dshSelectionExplain === undefined)
|
|
2486
2507
|
console.log('\n=== 客户端集成测试结束 ===')
|
|
2508
|
+
|
|
2509
|
+
} else {
|
|
2510
|
+
console.log('\n=== 客户端集成测试结束(离线模式:仅跑了不依赖宿主的断言)===')
|
|
2511
|
+
console.log(` 跳过原因:${ORIGIN} 不可达。完整验证请在 DSH 运行中执行本脚本。`)
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
// 显式退出:离线分支跳过了在线部分的 dispose(),客户端留下的定时器/监听会让
|
|
2515
|
+
// 事件循环一直空转(表现为"测试跑完但不退出")。按 process.exitCode 正常退出。
|
|
2516
|
+
process.exit(process.exitCode ?? 0)
|