@ggboy123/dsh-paper-reader 1.2.0 → 1.2.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 +32 -26
- package/dist/babeldoc-install.js +58 -10
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
[DeepSeek Harness (dsh)](https://github.com/deepseek-ai/deepseek-harness) 插件:论文伴读工作台 —— PDF 转录 / 检索 + 原生对话伴读 + 内置阅读器。
|
|
11
11
|
|
|
12
|
-
> **👉
|
|
12
|
+
> **👉 普通用户直接看「安装 → 方式一」**:装 DeepSeek 官方桌面端,添加插件即可,不需要任何开发环境。本仓库面向的是想自己装插件、或想改代码的用户。
|
|
13
13
|
|
|
14
14
|
功能迁移自本地独立产品 pdfqa(Go 实现的论文伴读工具)。原则:agent 循环 / 模型层 / 会话持久化全部交给 dsh 框架,插件只做「转录 + 检索 + 阅读器 UI」的薄壳组合。
|
|
15
15
|
|
|
@@ -35,38 +35,25 @@
|
|
|
35
35
|
|
|
36
36
|
## 安装
|
|
37
37
|
|
|
38
|
-
###
|
|
39
|
-
|
|
40
|
-
[PaperReader](https://github.com/GGboya/PaperReader) 是把本插件**预装好**的桌面应用(基于社区桌面客户端 [DSH Desktop](https://github.com/anywhere-labs/dsh-desktop) 打包):
|
|
41
|
-
|
|
42
|
-
1. 从 [Releases](https://github.com/GGboya/PaperReader/releases/latest) 下载 DMG(macOS Universal,Intel / M 系列芯片都行;Windows 版打包中)
|
|
43
|
-
2. 打开 DMG,把 PaperReader 拖进「应用程序」
|
|
44
|
-
3. 首次打开用 右键 → 打开(免签名发布,只弹这一次);首次启动有几分钟一次性初始化
|
|
45
|
-
|
|
46
|
-
打开就是文献库 + 阅读器 + 伴读对话,不用再执行任何命令。
|
|
47
|
-
|
|
48
|
-
### 方式二:已有桌面客户端,自己装插件
|
|
38
|
+
### 方式一:官方桌面端装插件(推荐)
|
|
49
39
|
|
|
50
40
|
**DeepSeek 官方桌面端**([deepseek.com/download](https://www.deepseek.com/download/) 下载,即 DeepSeek Harness 桌面版):
|
|
51
41
|
|
|
52
42
|
1. 安装并启动,登录 DeepSeek 账号
|
|
53
|
-
2. 侧栏 → **插件** → **添加插件**,输入 `@ggboy123/dsh-paper-reader` 安装
|
|
54
|
-
3.
|
|
43
|
+
2. 侧栏 → **插件** → **添加插件**,输入 `@ggboy123/dsh-paper-reader@1.2.1` 安装
|
|
44
|
+
3. 装完默认**停用**:点进插件详情,打开「启用」开关
|
|
45
|
+
4. **重启桌面端**(热启用状态下打开论文的链路会静默失效,重启进开机组合才正常,实测)
|
|
55
46
|
|
|
56
|
-
|
|
57
|
-
> 需要 ≥1.2.0:更早版本会被 0.2.x 运行时的 peer 兼容校验拒载(插件列表里显示异常)。
|
|
47
|
+
完整流程演示(添加插件 → 启用 → 重启 → 打开论文直接提问):
|
|
58
48
|
|
|
59
|
-
|
|
49
|
+

|
|
60
50
|
|
|
61
|
-
|
|
62
|
-
2.
|
|
63
|
-
3. 执行 `dsh plugin add @ggboy123/dsh-paper-reader@1.2.0`
|
|
64
|
-
4. 退出并重开 DSH Desktop(插件变更要重启才进 Loader 组合)
|
|
51
|
+
> 已在官方桌面端 V0.2.0-rc.2(内置 dsh 0.2.0-rc.2,macOS arm64)实测通过:侧栏文献库、新建专题、上传 PDF、PDF 阅读器、原生对话伴读、历史/新建对话按钮全部可用。
|
|
52
|
+
> 需要 ≥1.2.0:更早版本会被 0.2.x 运行时的 peer 兼容校验拒载(插件列表里显示异常)。
|
|
65
53
|
|
|
66
|
-
>
|
|
67
|
-
> 注意:Desktop 的 `desktop` profile 被 Electron 独占管理,外部 CLI 直接 `dsh plugin --profile desktop add` 会被拒(`managed exclusively by the Electron application`)——必须从 Desktop 自己的终端进。
|
|
54
|
+
> 曾经的 PaperReader 桌面版(基于社区 DSH Desktop 的预装打包)**已不再维护**——统一收敛到官方桌面端 + 插件的形式,老用户请迁移到本方式。
|
|
68
55
|
|
|
69
|
-
###
|
|
56
|
+
### 方式二:命令行 dsh(开发者)
|
|
70
57
|
|
|
71
58
|
```bash
|
|
72
59
|
dsh plugin --profile web add @ggboy123/dsh-paper-reader
|
|
@@ -80,7 +67,7 @@ dsh plugin --profile web add @ggboy123/dsh-paper-reader
|
|
|
80
67
|
**已装过的用户升级必须带显式版本号**——不带版本的 `add` 对已存在的依赖是 no-op(pnpm 按首次安装时记录的版本范围解析,不会追新):
|
|
81
68
|
|
|
82
69
|
```bash
|
|
83
|
-
dsh plugin --profile web add @ggboy123/dsh-paper-reader@1.2.
|
|
70
|
+
dsh plugin --profile web add @ggboy123/dsh-paper-reader@1.2.1
|
|
84
71
|
# 重启 dsh web 生效;浏览器 Cmd+Shift+R 强刷,避免旧阅读器页面缓存
|
|
85
72
|
```
|
|
86
73
|
|
|
@@ -92,7 +79,11 @@ grep '"version"' ~/.dsh/profiles/web/node_modules/@ggboy123/dsh-paper-reader/pac
|
|
|
92
79
|
|
|
93
80
|
> pnpm 若开了供应链策略 `minimumReleaseAge`(发布 N 小时内的新包拒装):要么等过窗口期,要么在 `~/.dsh/profiles/web/.npmrc` 加一行 `minimum-release-age-exclude[]=@ggboy123/dsh-paper-reader`(只豁免本插件,不动全局策略)。
|
|
94
81
|
|
|
95
|
-
|
|
82
|
+
### 参与开发(本地调试)
|
|
83
|
+
|
|
84
|
+
欢迎改代码。流程:clone → 构建 → 以本地目录装进 profile,之后改完 `pnpm run build` 重启对应 profile 即可验证:
|
|
85
|
+
|
|
86
|
+

|
|
96
87
|
|
|
97
88
|
```bash
|
|
98
89
|
git clone https://github.com/GGboya/dsh-paper-reader
|
|
@@ -123,6 +114,21 @@ dsh --profile web # 侧栏变为文献库树
|
|
|
123
114
|
|
|
124
115
|
> 伴读模式是**按需开启**的:默认不注册 `sidebar.workspaces`(官方工作区/会话列表原样),侧栏底部「📚 论文伴读」开关点击才接管,再点还原;模式选择记在 localStorage。退出模式时已打开的 PDF 页签和伴读会话不受影响。
|
|
125
116
|
|
|
117
|
+
### 翻译引擎冒烟测试(Windows 依赖完整性)
|
|
118
|
+
|
|
119
|
+
Windows 用户最常见的翻车点是翻译引擎装不上(杀软拦 uv、网络截断、venv 残缺)。安装逻辑 `src/babeldoc-install.ts` 是纯 Node 模块(不依赖 Cordis/Electron),配了 GitHub Actions 在**全新干净机器**上跑冒烟,无需本地 Windows:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pnpm build
|
|
123
|
+
pnpm smoke:engine clean # 全新安装全链路
|
|
124
|
+
pnpm smoke:engine managed-uv # 强制走插件托管 uv 下载(sha256 校验路径)
|
|
125
|
+
pnpm smoke:engine pip-fallback # 强制走 pip + 国内镜像备用通道
|
|
126
|
+
pnpm smoke:engine repair # 破坏 venv(删 pymupdf)后验证自检→自动修复
|
|
127
|
+
pnpm smoke:engine translate # 再起本地 mock OpenAI 端点,真跑一遍 babeldoc 翻译管线
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
CI(`.github/workflows/engine-smoke.yml`):`windows-latest` 跑全部四个场景 + translate-e2e,`macos-latest`/`ubuntu-latest` 跑 clean 防回归;改安装/翻译代码的 push 触发,外加每周一定时(uv/PyPI 在变,代码不动也可能突然坏)。两个测试钩子环境变量:`DSH_PR_MANAGED_UV_ONLY=1`(跳过系统 uv 查找)、`DSH_PR_FORCE_PIP_CHANNEL=1`(跳过 uv 装包通道)。
|
|
131
|
+
|
|
126
132
|
## 架构
|
|
127
133
|
|
|
128
134
|
```
|
package/dist/babeldoc-install.js
CHANGED
|
@@ -65,6 +65,10 @@ async function findUv(home) {
|
|
|
65
65
|
const own = uvPath(home);
|
|
66
66
|
if (existsSync(own))
|
|
67
67
|
return own;
|
|
68
|
+
// 测试钩子:CI runner 常在 PATH 预装 uv,会静默绕过 downloadUv;
|
|
69
|
+
// DSH_PR_MANAGED_UV_ONLY=1 时跳过系统查找,强制走插件托管下载链路(含 sha256 校验)。
|
|
70
|
+
if (process.env.DSH_PR_MANAGED_UV_ONLY === '1')
|
|
71
|
+
return null;
|
|
68
72
|
if (await whichOk('uv'))
|
|
69
73
|
return 'uv';
|
|
70
74
|
const candidates = isWin
|
|
@@ -76,6 +80,36 @@ async function findUv(home) {
|
|
|
76
80
|
}
|
|
77
81
|
return null;
|
|
78
82
|
}
|
|
83
|
+
/** GitHub API 请求头:有 GITHUB_TOKEN/GH_TOKEN 就带上——匿名限流 60/h 按出口 IP 算,
|
|
84
|
+
* CI runner 和企业 NAT 都是共享 IP,极易撞 403;带 token 后按账号 5000/h。 */
|
|
85
|
+
function ghHeaders(accept) {
|
|
86
|
+
const h = { accept, 'user-agent': 'dsh-paper-reader' };
|
|
87
|
+
const token = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
|
|
88
|
+
if (token)
|
|
89
|
+
h.authorization = `Bearer ${token}`;
|
|
90
|
+
return h;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* fetch 带重试:用户网络抖一下(尤其国内到 GitHub)就直接失败太脆了。
|
|
94
|
+
* 只对网络错误和 5xx 重试,4xx 是确定性错误直接抛。
|
|
95
|
+
*/
|
|
96
|
+
async function fetchWithRetry(url, init, tries = 3) {
|
|
97
|
+
let lastErr = null;
|
|
98
|
+
for (let i = 0; i < tries; i++) {
|
|
99
|
+
try {
|
|
100
|
+
const r = await fetch(url, init);
|
|
101
|
+
if (r.ok || (r.status >= 400 && r.status < 500))
|
|
102
|
+
return r;
|
|
103
|
+
lastErr = new Error(`HTTP ${r.status}`);
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
lastErr = e;
|
|
107
|
+
}
|
|
108
|
+
if (i < tries - 1)
|
|
109
|
+
await new Promise((r) => setTimeout(r, 2000 * (i + 1)));
|
|
110
|
+
}
|
|
111
|
+
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
|
|
112
|
+
}
|
|
79
113
|
/**
|
|
80
114
|
* 下载 uv 独立二进制。全程走 api.github.com(资产接口 + octet-stream 直连),
|
|
81
115
|
* 不依赖 github.com 页面域——后者在部分网络环境不可达,而 API 域通常可达。
|
|
@@ -92,8 +126,8 @@ async function downloadUv(home, onPhase) {
|
|
|
92
126
|
await mkdir(tmp, { recursive: true });
|
|
93
127
|
try {
|
|
94
128
|
onPhase('正在安装翻译引擎(1/3 下载 uv)…');
|
|
95
|
-
const rel = await
|
|
96
|
-
headers:
|
|
129
|
+
const rel = await fetchWithRetry('https://api.github.com/repos/astral-sh/uv/releases/latest', {
|
|
130
|
+
headers: ghHeaders('application/vnd.github+json'),
|
|
97
131
|
signal: AbortSignal.timeout(30_000),
|
|
98
132
|
});
|
|
99
133
|
if (!rel.ok)
|
|
@@ -104,8 +138,8 @@ async function downloadUv(home, onPhase) {
|
|
|
104
138
|
throw new Error(`uv 最新版没有 ${want} 资产`);
|
|
105
139
|
const sumAsset = assets.find((a) => a.name === want + '.sha256');
|
|
106
140
|
const download = async (a) => {
|
|
107
|
-
const r = await
|
|
108
|
-
headers:
|
|
141
|
+
const r = await fetchWithRetry(`https://api.github.com/repos/astral-sh/uv/releases/assets/${a.id}`, {
|
|
142
|
+
headers: ghHeaders('application/octet-stream'),
|
|
109
143
|
signal: AbortSignal.timeout(180_000),
|
|
110
144
|
});
|
|
111
145
|
if (!r.ok)
|
|
@@ -127,7 +161,10 @@ async function downloadUv(home, onPhase) {
|
|
|
127
161
|
// 不带 -z:bsdtar(macOS 自带 / Win10 1803+ 的 tar.exe)和 GNU tar 解包时都自动探测格式,
|
|
128
162
|
// 同一条命令通吃 tar.gz 与 zip(win32 的 uv 只发 zip)
|
|
129
163
|
await run('tar', ['-xf', pkgPath, '-C', tmp], { timeout: 60_000 });
|
|
130
|
-
|
|
164
|
+
// tar.gz 有顶层目录 uv-<triple>/;win32 的 zip 是平铺的(uv.exe 直接在根)
|
|
165
|
+
const binName = isWin ? 'uv.exe' : 'uv';
|
|
166
|
+
const nested = join(tmp, `uv-${triple}`, binName);
|
|
167
|
+
const extracted = existsSync(nested) ? nested : join(tmp, binName);
|
|
131
168
|
if (!existsSync(extracted))
|
|
132
169
|
throw new Error('uv 解包结果不符合预期');
|
|
133
170
|
const dst = uvPath(home);
|
|
@@ -210,13 +247,24 @@ async function installBabeldoc(uv, venvDir, onPhase) {
|
|
|
210
247
|
await run(uv, ['venv', venvDir, '--python', '3.12', '--python-preference', 'only-managed'], { timeout: 10 * 60_000 });
|
|
211
248
|
}
|
|
212
249
|
onPhase('正在安装翻译引擎(3/3 安装 babeldoc,首次约几分钟)…');
|
|
213
|
-
|
|
214
|
-
await run(uv, ['pip', 'install', '--python', py, 'babeldoc'], { timeout: 20 * 60_000 });
|
|
215
|
-
}
|
|
216
|
-
catch (uvErr) {
|
|
250
|
+
const viaPipFallback = async (cause) => {
|
|
217
251
|
onPhase('正在安装翻译引擎(3/3 备用通道:pip + 国内镜像)…');
|
|
218
|
-
|
|
252
|
+
if (cause) {
|
|
253
|
+
console.warn('[dsh-paper-reader] uv 安装 babeldoc 失败,改用 pip 镜像通道:', cause instanceof Error ? cause.message : cause);
|
|
254
|
+
}
|
|
219
255
|
await pipInstallBabeldoc(py);
|
|
256
|
+
};
|
|
257
|
+
// 测试钩子:DSH_PR_FORCE_PIP_CHANNEL=1 跳过 uv 通道,让 CI 确定性覆盖 pip 备用路径
|
|
258
|
+
//(杀软锁 uv 入口 exe、PyPI 截断时的逃生通道,Windows 上最关键的一条路径)
|
|
259
|
+
if (process.env.DSH_PR_FORCE_PIP_CHANNEL === '1')
|
|
260
|
+
await viaPipFallback(null);
|
|
261
|
+
else {
|
|
262
|
+
try {
|
|
263
|
+
await run(uv, ['pip', 'install', '--python', py, 'babeldoc'], { timeout: 20 * 60_000 });
|
|
264
|
+
}
|
|
265
|
+
catch (uvErr) {
|
|
266
|
+
await viaPipFallback(uvErr);
|
|
267
|
+
}
|
|
220
268
|
}
|
|
221
269
|
if (!existsSync(bin))
|
|
222
270
|
throw new Error('babeldoc 安装完成但未找到可执行文件');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ggboy123/dsh-paper-reader",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "DeepSeek Harness 插件:论文伴读工作台 — PDF 转录 / 检索 / 原生对话伴读 + 内置阅读器页面。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "tsc -p tsconfig.json",
|
|
47
47
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
48
|
+
"smoke:engine": "node scripts/engine-smoke.mjs",
|
|
48
49
|
"prepublishOnly": "tsc -p tsconfig.json"
|
|
49
50
|
},
|
|
50
51
|
"keywords": [
|