@tianyulinfeng1/dsh-plugin-deepseek-chat 1.0.0 → 1.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/CHANGELOG.md +67 -0
- package/README.en.md +166 -138
- package/README.md +186 -157
- package/cordis.patch.yml +3 -3
- package/docs/RELEASE_NOTES_v1.1.1.md +64 -0
- package/docs/login-persistence.md +187 -0
- package/docs/screenshot.png +0 -0
- package/lib/client.js +942 -549
- package/locale/en.json +6 -6
- package/locale/zh.json +6 -6
- package/package.json +67 -62
- package/scripts/patch-dsh-persistent-partition.cjs +174 -0
- package/scripts/patch-dsh-persistent-partition.mjs +16 -0
package/README.md
CHANGED
|
@@ -1,157 +1,186 @@
|
|
|
1
|
-
# dsh-plugin-deepseek-chat
|
|
2
|
-
|
|
3
|
-
[English](
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
-
|
|
100
|
-
-
|
|
101
|
-
-
|
|
102
|
-
|
|
103
|
-
##
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
##
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
-
|
|
150
|
-
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
1
|
+
# dsh-plugin-deepseek-chat
|
|
2
|
+
|
|
3
|
+
[English](README.en.md) | 中文
|
|
4
|
+
|
|
5
|
+
> **当前版本:v1.1.1** |适配 DSH **0.2.2-rc.1**
|
|
6
|
+
|
|
7
|
+
在 **DeepSeek Harness(DSH)桌面版**的左侧栏加入 **DeepSeek 聊天** 入口,点开后 `chat.deepseek.com` 直接占满主内容区,记住上次所在的会话,并显示当前 DSH 账号状态。
|
|
8
|
+
|
|
9
|
+
> 仓库:[GitHub](https://github.com/sweet560/dsh-plugin-deepseek-chat) | [Gitee 镜像](https://gitee.com/wtt520wyl/dsh-plugin-deepseek-chat)
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
## 功能
|
|
14
|
+
|
|
15
|
+
- 左侧栏图标入口,排在「插件」和「自动化任务」之间。
|
|
16
|
+
- 主内容区全宽内嵌 `https://chat.deepseek.com/`(Electron `<webview>`,走桌面壳的 guest 租约桥 `window.dshDesktop.browser`)。
|
|
17
|
+
- **记住上次会话**:切到别的面板、切换会话、重启 App 后再进来,直接回到上次那条对话,而不是新对话。
|
|
18
|
+
- 登录/鉴权页不会覆盖记忆;记住的会话失效或打不开时自动回退到首页。
|
|
19
|
+
- **稳定的登录会话(v1.1.1 修复)**:guest 分区固定为 `panel:deepseek-chat`,切换面板/会话回来不会重建分区、不会重新要求扫码。
|
|
20
|
+
- **DSH 账号状态展示**:面板顶部条显示「已与 DSH 登录账号同步 · 昵称」/「未登录」/「账号状态不可用」,通过官方 `ctx.remote.account` 读取;**仅作展示,绝不参与 guest 生命周期**。
|
|
21
|
+
- **崩溃隔离**:内嵌页进程退出、被销毁或启动超时,只在面板里显示「重试」,不会再出现整个 App 变空白窗口的问题(v0.4.0 修复,见下文)。
|
|
22
|
+
- 纯 Web 表层(`dsh web` 在浏览器里打开)自动不注册——该站点 `frame-ancestors 'none'`,iframe 嵌不了,guest 租约桥也只有桌面壳提供。
|
|
23
|
+
|
|
24
|
+
## 环境要求
|
|
25
|
+
|
|
26
|
+
| 项 | 要求 |
|
|
27
|
+
|---|---|
|
|
28
|
+
| DSH | 桌面版 `0.2.2-rc.1` 或更新 |
|
|
29
|
+
| 平台 | Windows / macOS / Linux 桌面壳 |
|
|
30
|
+
| git | 仅「GitHub 安装」方式需要(pnpm 通过 git 拉取仓库) |
|
|
31
|
+
|
|
32
|
+
## 安装
|
|
33
|
+
|
|
34
|
+
npm 包名:**`@tianyulinfeng1/dsh-plugin-deepseek-chat`**(GitHub 仓库名仍是 `dsh-plugin-deepseek-chat`)。
|
|
35
|
+
|
|
36
|
+
### 方式一:npm(推荐)
|
|
37
|
+
|
|
38
|
+
```powershell
|
|
39
|
+
dsh plugin --profile desktop add @tianyulinfeng1/dsh-plugin-deepseek-chat
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
插件页的「安装第三方插件」里也可以直接填包名 `@tianyulinfeng1/dsh-plugin-deepseek-chat`。
|
|
43
|
+
|
|
44
|
+
### 方式二:插件页安装(GitHub)
|
|
45
|
+
|
|
46
|
+
1. 打开 DSH 桌面版,左侧栏点 **插件**。
|
|
47
|
+
2. 点列表上方的 **安装第三方插件**。
|
|
48
|
+
3. 在「包名或地址」里填入:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
github:sweet560/dsh-plugin-deepseek-chat
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
也可以填 `https://github.com/sweet560/dsh-plugin-deepseek-chat`。
|
|
55
|
+
4. 点 **安装**,等 pnpm 跑完(首次会拉取仓库并安装依赖)。
|
|
56
|
+
5. 安装完成后插件自动启用,左侧栏出现 **DeepSeek 聊天**。
|
|
57
|
+
|
|
58
|
+
### 方式三:命令行(GitHub)
|
|
59
|
+
|
|
60
|
+
```powershell
|
|
61
|
+
dsh plugin --profile desktop add github:sweet560/dsh-plugin-deepseek-chat
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
装完打开 **插件 → 已安装插件**,确认「DeepSeek 聊天」处于启用状态(命令行只负责装依赖,启用状态以插件页为准)。
|
|
65
|
+
|
|
66
|
+
### 方式四:本地目录(二次开发)
|
|
67
|
+
|
|
68
|
+
```powershell
|
|
69
|
+
dsh plugin --profile desktop add file:"<本仓库绝对路径>"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 关于「免扫码 / 与 DSH 账号同步」的真相
|
|
73
|
+
|
|
74
|
+
**纯插件做不到让 chat.deepseek.com 直接复用 DSH 已登录的账号**,这不是实现遗漏,而是 DSH 的架构限制(已在 0.2.2-rc.1 源码中逐项核对):
|
|
75
|
+
|
|
76
|
+
| 项 | DSH 桌面版账号 | chat.deepseek.com 网页版 |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| 凭据 | platform.deepseek.com 的 `x-dsh-auth-token`(Host-only `getPlatformSession()`) | 站点自带的 Token(`api/v0/users/current` 无 token 返回 `40002 Missing Token`) |
|
|
79
|
+
| 存放 | Host 进程凭据存储 | guest session 的 httpOnly cookie |
|
|
80
|
+
| 兑换入口 | **无**;归档中 `chat.deepseek.com` 零引用,无 SSO、无 cookie 注入 | — |
|
|
81
|
+
|
|
82
|
+
- 渲染层桥 `globalThis.dshDesktop` 只有 `browser.acquire/release/onOpenRequested` 等,**没有** `getAuthToken`、写 cookie、打开 Platform 取 token 的 API;`getAuthToken()` 只存在于桌面壳内嵌 Platform 页(`usage` / `top_up`)的专用 preload,且受主帧 + origin 限制。
|
|
83
|
+
- `browserAcquire` IPC 只按 workspace 字符串分配分区/租约,`configureSession()` 只设权限与下载拦截,**不注入任何凭据**;guest 被强制 `sandbox/contextIsolation/webSecurity`。
|
|
84
|
+
- 因此:DSH 账号状态只能**显示**在面板上,无法换成网页版登录态;首次仍需在网页里扫码一次。
|
|
85
|
+
|
|
86
|
+
**v1.1.0 的教训(v1.1.1 已修复)**:曾把 guest 分区绑到异步账号探测结果上。DSH 的 `getState()` 在重连/切会话时会瞬时失败,分区一变就等于换了个全新 cookie 罐 → 每次切回面板都要求重新扫码。现在:
|
|
87
|
+
|
|
88
|
+
- `acquire()` 的 workspace 恒为 `panel:deepseek-chat`,会话记忆 key 恒为 `dsh-plugin-deepseek-chat:last-url`;
|
|
89
|
+
- 账号状态变化只更新显示条,不 bump epoch、不重建 guest、不换分区;
|
|
90
|
+
- 瞬时 `unknown` 被忽略,只有明确的 `signed-out` 才更新显示;
|
|
91
|
+
- guest 在挂载时立即启动,不再等待异步 Remote 探测(探测失败也不阻塞聊天);
|
|
92
|
+
- 弹窗导航只放行 `chat.deepseek.com` 与 `platform.deepseek.com`(HTTPS、默认端口)。
|
|
93
|
+
|
|
94
|
+
> 想做到「重启 DSH 后也不用重新扫码」,需要按 [docs/login-persistence.md](docs/login-persistence.md) 把 DSH 的随机内存分区改成**确定性的 `persist:` 分区**(运行 [scripts/patch-dsh-persistent-partition.cjs](scripts/patch-dsh-persistent-partition.cjs),属修改已安装应用,风险自负)。**该脚本会同步 `0.2.2-rc.1` 的 ASAR 逐文件 SHA256 integrity**,纯字节替换会让 DSH 重启崩溃,切勿使用废弃的 `.mjs` 版本。补丁要求完全退出 DSH 后才能写入 `app.asar`,且只解决「重启后保留网页 cookie」,不能让网页复用 DSH token。
|
|
95
|
+
|
|
96
|
+
## 使用
|
|
97
|
+
|
|
98
|
+
- 第一次打开会停在 chat.deepseek.com 的登录页,登录一次即可。
|
|
99
|
+
- 本次 App 运行期间,切换面板/会话**不需要**重新登录(v1.1.1 起分区固定)。
|
|
100
|
+
- **重启 DSH 后仍需要重新登录一次**:DSH 目前给内嵌浏览器的是随机内存分区(`dsh-sidebar-browser-<uuid>`,没有 `persist:` 前缀),进程退出即清空 cookie。本插件不修改 DSH 的安装文件;想做到「登录一次、长期免登录」,可按 **[docs/login-persistence.md](docs/login-persistence.md)** 做一次等长字节替换(含脚本、验证与回滚)。
|
|
101
|
+
- 会话链接记在 App 的 localStorage(固定 key `dsh-plugin-deepseek-chat:last-url`),重启后仍会打开上次的会话(前提是已登录且该会话仍存在)。
|
|
102
|
+
|
|
103
|
+
## 登录态持久化(可选,免登录)
|
|
104
|
+
|
|
105
|
+
DSH 目前给内嵌浏览器分配的是**内存分区**(`dsh-sidebar-browser-${randomUUID()}`,无 `persist:` 前缀),所以每次重启 App 都会清空 cookie。想做到「登录一次、以后不再登录」,需要把桌面主进程里的那行分区名改成固定且带 `persist:` 前缀的值——两条语句**字节数相同**,可以直接在 `resources/app.asar` 里原地替换。
|
|
106
|
+
|
|
107
|
+
完整原因、脚本、验证步骤、回滚方式和各平台路径都整理在这里:
|
|
108
|
+
|
|
109
|
+
**→ [docs/login-persistence.md](docs/login-persistence.md)**
|
|
110
|
+
|
|
111
|
+
注意:这属于对已安装应用的修改,不在 DSH 支持范围内;DSH 升级会覆盖 `app.asar`,需要重新打补丁(`Partitions` 里的登录数据不会丢)。请先备份,风险自负。
|
|
112
|
+
|
|
113
|
+
## 已知限制
|
|
114
|
+
|
|
115
|
+
- chat.deepseek.com 会检测 Electron 内嵌环境,偶尔弹出「使用环境异常…建议使用官方产品」的提示;这是站点侧行为,与本插件无关。
|
|
116
|
+
- 仅桌面版有效;`dsh web` 在浏览器里打开时不显示入口。
|
|
117
|
+
- 站点若调整会话 URL 规则(当前识别 `/a/chat/s/<id>` 形式),「记住会话」可能失效并回退到首页。
|
|
118
|
+
- DSH 账号状态只作展示,不能把 DSH 的 OAuth token 兑换成 chat.deepseek.com 的网页会话;首次仍需在该网页登录一次(见上文)。
|
|
119
|
+
- `getProfile()` 只用于显示昵称;该接口暂不可用时显示状态可能短暂滞后,但**绝不影响** guest 与登录态。
|
|
120
|
+
- 重启 DSH 后仍需重新登录网页版 chat,除非按 [docs/login-persistence.md](docs/login-persistence.md) 打持久分区补丁。
|
|
121
|
+
|
|
122
|
+
## 崩溃隔离(v0.4.0)
|
|
123
|
+
|
|
124
|
+
早期版本出现过:点开面板、聊天页正常显示 1~2 秒后**整个 App 变成空白窗口**(只剩原生菜单和窗口按钮),没有崩溃对话框也没有 `crash-*.log`。
|
|
125
|
+
|
|
126
|
+
根因是 React 卸载面板时**先移除 DOM、后执行 effect 清理**,而 Electron 的 `<webview>` 一旦脱离文档,`getURL()` 就会抛错;DSH 把插件里任何未捕获异常都当作 `slot entry crashed in 'root'`,直接卸载 `root` slot —— 整个 App UI 因此消失。
|
|
127
|
+
|
|
128
|
+
现在的实现:
|
|
129
|
+
|
|
130
|
+
- 所有 guest 调用先判断 `element.isConnected` 再 `try/catch`,清理阶段「先读 URL、再移除元素」;
|
|
131
|
+
- 每个事件处理器都包 `try/catch`,异常只落在面板自己的状态里;
|
|
132
|
+
- 面板外包一层 React 错误边界;
|
|
133
|
+
- 监听 `render-process-gone` / `destroyed` / `did-fail-load`,失败时**先移除失效的 guest 元素**再显示「重试」,避免重试叠加 webview;
|
|
134
|
+
- 15 秒启动看门狗,guest 一直不 `dom-ready` 就转重试卡片;
|
|
135
|
+
- 事件监听器若注册失败则不启动 guest;弹窗 URL 走白名单校验(v1.1.0)。
|
|
136
|
+
|
|
137
|
+
## 版本历史
|
|
138
|
+
|
|
139
|
+
| 版本 | 内容 |
|
|
140
|
+
|---|---|
|
|
141
|
+
| **1.1.1** | **修复每次切回面板/重启都要重新扫码的回归**:guest 分区与会话 key 固定为常量,账号状态仅作展示、不参与 guest 生命周期;瞬时 `unknown` 被忽略;guest 不再等待异步账号探测;自检改为验证固定分区与状态抖动不重建(35 项)。明确记录「纯插件无法免扫码复用 DSH token」的源码结论。 |
|
|
142
|
+
| 1.1.0 | 支持 DSH `0.2.2-rc.1`:新增 DSH 账号状态条;修正生成式 Remote 信封解析;收紧会话 URL 与弹窗 URL 校验;失败时移除失效 guest。**此版把分区绑到账号状态,导致登录态抖动(已由 1.1.1 修复)**。 |
|
|
143
|
+
| 1.0.0 | 首个本地版本:侧栏入口、全宽内嵌聊天、会话记忆、崩溃隔离。 |
|
|
144
|
+
|
|
145
|
+
## 开发
|
|
146
|
+
|
|
147
|
+
```powershell
|
|
148
|
+
git clone https://github.com/sweet560/dsh-plugin-deepseek-chat.git
|
|
149
|
+
cd dsh-plugin-deepseek-chat
|
|
150
|
+
node test/check-client.mjs
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`test/check-client.mjs` 是离线自检:在 `node:vm` 里加载 `lib/client.js`,用假的 React/DOM/租约桥验证注册、Remote 信封解析、固定分区与会话 key、状态抖动不重建 guest、切换面板复用分区、会话记忆、租约释放、弹窗白名单和崩溃隔离。
|
|
154
|
+
|
|
155
|
+
目录:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
lib/index.js 宿主半侧(空实现,只为让 client bundle 有 Loader 入口)
|
|
159
|
+
lib/client.js 浏览器半侧:入口、面板、账号状态展示、webview 生命周期、崩溃隔离
|
|
160
|
+
cordis.patch.yml bundle 补丁:插入本插件的 Loader 行
|
|
161
|
+
locale/zh.json 插件页显示名称/描述(中文)
|
|
162
|
+
locale/en.json 插件页显示名称/描述(英文)
|
|
163
|
+
icon.svg 插件页图标
|
|
164
|
+
docs/login-persistence.md 可选:DSH 内存分区改持久分区的字节补丁
|
|
165
|
+
test/check-client.mjs 离线自检
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 卸载
|
|
169
|
+
|
|
170
|
+
```powershell
|
|
171
|
+
dsh plugin --profile desktop remove @tianyulinfeng1/dsh-plugin-deepseek-chat
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## 作者
|
|
175
|
+
|
|
176
|
+
- **作者**:伍悦匿匿
|
|
177
|
+
- **联系方式**:437830488@qq.com
|
|
178
|
+
- **GitHub**:https://github.com/sweet560
|
|
179
|
+
- **博客园**:https://www.cnblogs.com/wuyuenini/
|
|
180
|
+
- **CSDN**:https://blog.csdn.net/weixin_42076389
|
|
181
|
+
- **Gitee**:https://gitee.com/wtt520wyl
|
|
182
|
+
- **Gitee 镜像仓库**:https://gitee.com/wtt520wyl/dsh-plugin-deepseek-chat
|
|
183
|
+
|
|
184
|
+
## 许可证
|
|
185
|
+
|
|
186
|
+
[MIT](LICENSE)
|
package/cordis.patch.yml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# dsh-plugin-deepseek-chat bundle patch: one insert registers
|
|
2
|
-
# insert.name must be the package name
|
|
3
|
-
# composed loader tree.
|
|
1
|
+
# @tianyulinfeng1/dsh-plugin-deepseek-chat bundle patch: one insert registers
|
|
2
|
+
# this plugin. insert.name must be the package name (the scoped npm name);
|
|
3
|
+
# insert.id must be unique across the composed loader tree.
|
|
4
4
|
- insert:
|
|
5
5
|
- id: deepseek-chat
|
|
6
6
|
name: '@tianyulinfeng1/dsh-plugin-deepseek-chat'
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# dsh-plugin-deepseek-chat v1.1.1
|
|
2
|
+
|
|
3
|
+
**DeepSeek Chat panel for DeepSeek Harness Desktop — stable login session, DSH account status, and full ASAR integrity support for the restart-free login patch.**
|
|
4
|
+
|
|
5
|
+
- Requires DSH Desktop **0.2.2-rc.1** or newer.
|
|
6
|
+
- Requires Node >= 20.
|
|
7
|
+
|
|
8
|
+
## What's fixed
|
|
9
|
+
|
|
10
|
+
- **No more QR scan on every panel/session switch.** The guest partition is now a
|
|
11
|
+
fixed constant (`panel:deepseek-chat`) instead of being derived from the async
|
|
12
|
+
DSH account probe. DSH caches one session per workspace string for the process
|
|
13
|
+
lifetime, so a stable string preserves the chat login.
|
|
14
|
+
- The DSH account state strip is display-only and never rebuilds the guest.
|
|
15
|
+
- The guest starts immediately, without waiting for the account Remote.
|
|
16
|
+
|
|
17
|
+
## Restart-free login (the important part)
|
|
18
|
+
|
|
19
|
+
DSH `0.2.1-rc.3+` (including `0.2.2-rc.1`) records a SHA256 **integrity** for
|
|
20
|
+
every file in `app.asar`. A byte-only replacement of the in-memory partition name
|
|
21
|
+
would crash DSH on restart with "ASAR Integrity Violation".
|
|
22
|
+
|
|
23
|
+
This release ships `scripts/patch-dsh-persistent-partition.cjs` (from upstream),
|
|
24
|
+
which:
|
|
25
|
+
|
|
26
|
+
1. Replaces `dsh-sidebar-browser-${randomUUID()}` with the equal-length
|
|
27
|
+
`persist:dsh-sidebar-browser-chat-v1`;
|
|
28
|
+
2. Recomputes `lib/main.js`'s SHA256 and syncs the ASAR header `hash`/`blocks`;
|
|
29
|
+
3. Verifies every file/block hash before and after writing (verified on
|
|
30
|
+
0.2.2-rc.1: **20519 files, 0 failures**);
|
|
31
|
+
4. Creates a timestamped backup and supports `--dry-run` and idempotent reruns.
|
|
32
|
+
|
|
33
|
+
```powershell
|
|
34
|
+
# Exit DSH completely from the tray first!
|
|
35
|
+
node scripts/patch-dsh-persistent-partition.cjs "F:\DeepSeek Harness\resources\app.asar" --dry-run
|
|
36
|
+
node scripts/patch-dsh-persistent-partition.cjs "F:\DeepSeek Harness\resources\app.asar"
|
|
37
|
+
# Restart DSH, sign in to chat.deepseek.com ONCE, then restart again — no more scans.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
See [docs/login-persistence.md](docs/login-persistence.md) for the full
|
|
41
|
+
procedure, rollback, and per-platform paths.
|
|
42
|
+
|
|
43
|
+
## Honest limitation
|
|
44
|
+
|
|
45
|
+
A pure plugin still **cannot** make the web chat reuse the already-signed-in DSH
|
|
46
|
+
account: DSH uses a Platform `x-dsh-auth-token`, `chat.deepseek.com` uses its own
|
|
47
|
+
Token, the renderer bridge exposes no token/cookie API, and there is no official
|
|
48
|
+
SSO/exchange. One manual sign-in is required; the patch then keeps that session
|
|
49
|
+
permanently.
|
|
50
|
+
|
|
51
|
+
## Assets
|
|
52
|
+
|
|
53
|
+
- `dsh-plugin-deepseek-chat-1.1.1.tgz` — the complete installable npm/bundle
|
|
54
|
+
package (`npm pack` output). Install from the Plugins page via
|
|
55
|
+
`github:sweet560/dsh-plugin-deepseek-chat`, or with
|
|
56
|
+
`dsh plugin --profile desktop add file:"<path to tgz>"`.
|
|
57
|
+
- `Source code (zip/tar.gz)` — generated by GitHub/Gitee automatically.
|
|
58
|
+
|
|
59
|
+
## Verification
|
|
60
|
+
|
|
61
|
+
- Offline self-check: 35/35 passing (`node test/check-client.mjs`).
|
|
62
|
+
- `lib/client.js` and the patch script pass `node --check`.
|
|
63
|
+
- `app.asar` patch independently verified: header integrity hash equals the
|
|
64
|
+
actual SHA256 of the patched `lib/main.js` bytes; archive size unchanged.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# 让「DeepSeek 聊天」在 DSH 重启后免登录
|
|
2
|
+
|
|
3
|
+
> **TL;DR** — DSH 桌面壳目前给内嵌浏览器的是**内存分区**,进程退出即清空 cookie,所以本插件每次重启都要重新登录。把 `resources/app.asar` 里 `lib/main.js` 的那行分区名改成**固定且带 `persist:` 前缀**的值(等长字节替换 + **同步 ASAR 逐文件 SHA256 integrity**),登录一次后即可长期免登录。本文给出原因、完整步骤、脚本、验证与回滚方式。
|
|
4
|
+
>
|
|
5
|
+
> **版本核对**:已在 DSH Desktop `0.2.2-rc.1`(`app.asar` 版本 `0.2.2-rc.1`,构建提交 `c59d10a`)中核对并**实际应用**:`DesktopBrowserGuests.acquire()` 原本使用 `dsh-sidebar-browser-${randomUUID()}`、无 `persist:` 前缀;该构建 **22102 个文件全部带 integrity**,因此必须使用会重算哈希的 `patch-dsh-persistent-partition.cjs`,纯字节替换会导致 DSH 重启崩溃。补丁后全量校验 20519 文件、0 失败。`dshDesktop.browser` 的 `protocolVersion` 仍为 `1`,`acquire()` 仍返回 `{ lease, partition }`。插件 v1.1.1 要求 DSH ≥ `0.2.2-rc.1`。
|
|
6
|
+
>
|
|
7
|
+
> **两件不同的事,请勿混淆**:
|
|
8
|
+
> - **切换面板/会话回来就要重新扫码** → 这是插件 v1.1.0 的回归(分区被绑到异步账号状态),**v1.1.1 已修复**,无需改 `app.asar`,见 README「关于免扫码的真相」。
|
|
9
|
+
> - **重启 DSH 后要重新扫码** → 这才是本文补丁要解决的问题(内存分区不落盘),需要改 `app.asar`。
|
|
10
|
+
> - **想让网页直接复用 DSH 已登录账号、完全不扫码** → 纯插件做不到(两套 token、渲染层无 token/cookie 通道、归档中 `chat.deepseek.com` 零引用),本文补丁也做不到,它只保留首次扫码后的网页 cookie。
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. 现象与原因
|
|
15
|
+
|
|
16
|
+
**现象**:在「DeepSeek 聊天」面板登录 chat.deepseek.com 后,关掉 DSH 再打开,又回到登录页。
|
|
17
|
+
|
|
18
|
+
**原因**:DSH 桌面主进程给每个浏览器 guest 分配的是**内存分区**:
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
// resources/app.asar → lib/main.js(DesktopBrowserGuests.acquire)
|
|
22
|
+
let partition = this.partitions.get(workspace);
|
|
23
|
+
if (partition === void 0) {
|
|
24
|
+
partition = `dsh-sidebar-browser-${randomUUID()}`; // ← 没有 persist: 前缀
|
|
25
|
+
this.configureSession(session.fromPartition(partition));
|
|
26
|
+
this.partitions.set(workspace, partition);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Electron 的 `partition` 只有在带 `persist:` 前缀时才会把 cookie、localStorage、IndexedDB 等写到磁盘:
|
|
31
|
+
|
|
32
|
+
| 分区名 | 存储位置 | 进程退出后 |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `dsh-sidebar-browser-<uuid>` | 内存 | **清空** |
|
|
35
|
+
| `persist:dsh-sidebar-browser-chat-v1` | `…/Partitions/dsh-sidebar-browser-chat-v1/` | **保留** |
|
|
36
|
+
|
|
37
|
+
而且这里的 `randomUUID()` 每次启动都不同,所以连"跨进程复用同一分区"都做不到。
|
|
38
|
+
|
|
39
|
+
**为什么插件自己解决不了**:分区名由桌面主进程决定,渲染层只能使用桥(`window.dshDesktop.browser.acquire()`)返回的 `partition`,插件无权指定。因此只能在主进程侧改这一行。
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 2. 解决思路
|
|
44
|
+
|
|
45
|
+
把上面那行改成固定名字 + `persist:` 前缀:
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
// 修改前(分区名 36 字符,整条语句 50 字节)
|
|
49
|
+
partition = `dsh-sidebar-browser-${randomUUID()}`;
|
|
50
|
+
|
|
51
|
+
// 修改后(分区名同为 36 字符,整条语句同为 50 字节)
|
|
52
|
+
partition = `persist:dsh-sidebar-browser-chat-v1`;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
两条语句**字节数完全相同**(均 50 字节),因此可以在 `app.asar` 里做等长字节替换,ASAR 头部记录的文件偏移与大小都不变。
|
|
56
|
+
|
|
57
|
+
> **⚠️ 关键(0.2.1-rc.3+,含当前 0.2.2-rc.1):必须同步 ASAR integrity**
|
|
58
|
+
> 该构建的 ASAR 头部为**每个文件**记录了 SHA256 `integrity`(`hash` + 按 `blockSize` 分块的 `blocks`),Electron 读取时会校验。实测 0.2.2-rc.1 的 `app.asar` 有 **22102 个文件带 integrity**(`lib/main.js` 单块 4 MiB)。
|
|
59
|
+
> **只替换字节而不重算并重填 `lib/main.js` 的哈希,DSH 重启会直接 `ASAR Integrity Violation` 退出。** 因此不要用「纯字节替换」脚本;必须使用下方会同步哈希并做全量校验的 `patch-dsh-persistent-partition.cjs`(来自上游仓库 sweet560/dsh-plugin-deepseek-chat)。
|
|
60
|
+
|
|
61
|
+
替换后的效果:
|
|
62
|
+
|
|
63
|
+
- 登录状态写入 `%APPDATA%\@deepseek-ai\dsh-desktop\Partitions\dsh-sidebar-browser-chat-v1\`;
|
|
64
|
+
- 重启 DSH 直接带着 cookie,不再扫码/输密码;
|
|
65
|
+
- 左侧栏「DeepSeek 聊天」和右侧栏内置浏览器**共用**这个分区,两边登录态一致。
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 3. 操作步骤(Windows)
|
|
70
|
+
|
|
71
|
+
> 适用于 DSH Desktop `0.2.2-rc.1`(在 Windows 上核对并应用;`0.2.1-rc.2` / `0.2.1-rc.3` 亦适用)。其他版本请先按第 5 节用 `--dry-run` 确认待替换的字符串存在、且 integrity 校验能通过。
|
|
72
|
+
>
|
|
73
|
+
> 注意:`partitions` 是**进程内** Map,按传入 `acquire()` 的 workspace 字符串缓存;`release()` 只销毁 guest、不清 cookie。本插件 v1.1.1 起 workspace 恒为 `panel:deepseek-chat`,补丁后的固定 `persist:` 分区在**同一进程内切换面板**和**重启后同名分区**都能保留 cookie。
|
|
74
|
+
|
|
75
|
+
1. **完全退出 DSH**:托盘图标右键 → 退出(只关窗口是隐藏到托盘,`app.asar` 仍被独占锁定,脚本会写入失败)。
|
|
76
|
+
2. **备份** `F:\DeepSeek Harness\resources\app.asar`(脚本会自动生成带时间戳的 `.bak-*`)。
|
|
77
|
+
3. 用仓库自带的 [scripts/patch-dsh-persistent-partition.cjs](../scripts/patch-dsh-persistent-partition.cjs) 打补丁(Node 18+,零依赖),见下。
|
|
78
|
+
4. 重新启动 DSH,打开「DeepSeek 聊天」,**扫码登录一次**。
|
|
79
|
+
5. 退出 DSH 再打开,验证仍是登录状态——此后不再需要扫码。
|
|
80
|
+
|
|
81
|
+
### 脚本(唯一推荐)
|
|
82
|
+
|
|
83
|
+
上游仓库配套的 `patch-dsh-persistent-partition.cjs` 已适配逐文件 ASAR integrity,支持 `--dry-run`、幂等重跑、自动备份与**全量哈希校验**:
|
|
84
|
+
|
|
85
|
+
```powershell
|
|
86
|
+
cd dsh-plugin-deepseek-chat
|
|
87
|
+
$asar = "F:\DeepSeek Harness\resources\app.asar"
|
|
88
|
+
|
|
89
|
+
# 1) 先只看不改:确认目标唯一、等长、全量 integrity 无失败
|
|
90
|
+
node scripts/patch-dsh-persistent-partition.cjs $asar --dry-run
|
|
91
|
+
|
|
92
|
+
# 2) 完全退出 DSH 后应用(自动备份;重算 lib/main.js 的 SHA256 并同步头部)
|
|
93
|
+
node scripts/patch-dsh-persistent-partition.cjs $asar
|
|
94
|
+
|
|
95
|
+
# 3) 已打过补丁时再跑也安全(幂等,会再次全量校验)
|
|
96
|
+
node scripts/patch-dsh-persistent-partition.cjs $asar --dry-run
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
成功输出形如:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
dry-run: patch would be applied
|
|
103
|
+
integrity: 20519 file(s) verified, 0 failed
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
脚本行为:
|
|
107
|
+
- **等长替换**:两条语句均 50 字节,补丁后文件大小不变,ASAR 偏移不受影响;
|
|
108
|
+
- **integrity 同步(关键)**:检测 `lib/main.js` 的 `integrity`,用补丁后的字节重算 SHA256,替换头部 JSON 中的 `hash`/`blocks`(长度不变);多块布局会拒绝而不是猜测;
|
|
109
|
+
- **全量校验**:写入前后遍历所有文件的 integrity 分块逐块核对,任一失败即拒绝写入(实测 0.2.2-rc.1 为 20519 个文件、0 失败);
|
|
110
|
+
- **幂等**:已是补丁态直接返回 0 并复验;
|
|
111
|
+
- **自动备份**:写入前生成 `app.asar.bak-<时间戳>`;
|
|
112
|
+
- **安全兜底**:目标不存在/出现多次、长度不一致、头部 JSON 长度变化、多块 integrity 等情况一律中止。
|
|
113
|
+
|
|
114
|
+
退出码:`0` 成功或已是补丁态,`1` 拒绝/校验失败,`2` 参数错误。
|
|
115
|
+
|
|
116
|
+
> `scripts/patch-dsh-persistent-partition.mjs` 是早期**纯字节替换**版本,缺少 integrity 同步,会让 0.2.1-rc.3+ 的 DSH 重启崩溃,已废弃(运行即报错退出)。**不要使用**。
|
|
117
|
+
|
|
118
|
+
### 验证是否生效
|
|
119
|
+
|
|
120
|
+
```powershell
|
|
121
|
+
# 1) 补丁本身:再跑一次 dry-run,应显示 already patched + 全量 integrity 0 failed
|
|
122
|
+
node scripts/patch-dsh-persistent-partition.cjs "F:\DeepSeek Harness\resources\app.asar" --dry-run
|
|
123
|
+
|
|
124
|
+
# 2) 登录一次后,确认分区目录和 cookie 已落盘
|
|
125
|
+
Get-Item "$env:APPDATA\@deepseek-ai\dsh-desktop\Partitions\dsh-sidebar-browser-chat-v1\Cookies" |
|
|
126
|
+
Select-Object FullName, Length, LastWriteTime
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
- `already patched` + `integrity: … 0 failed` → ASAR 完整性没问题,重启不会崩;
|
|
130
|
+
- `Cookies` 存在且非 0 → 分区已持久化;
|
|
131
|
+
- 重启 DSH 打开聊天面板仍是登录态 → 完成。
|
|
132
|
+
|
|
133
|
+
### 回滚
|
|
134
|
+
|
|
135
|
+
```powershell
|
|
136
|
+
# 先完全退出 DSH
|
|
137
|
+
Copy-Item "F:\DeepSeek Harness\resources\app.asar.bak-<时间戳>" "F:\DeepSeek Harness\resources\app.asar" -Force
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
备份是**补丁前**的完整字节副本(含原始 integrity),直接覆盖即可恢复。磁盘上的 `Partitions\dsh-sidebar-browser-chat-v1\` 数据不会被删,之后重新打补丁仍可复用。
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 4. 其他平台路径
|
|
145
|
+
|
|
146
|
+
| 平台 | `app.asar` | 分区数据目录 |
|
|
147
|
+
|---|---|---|
|
|
148
|
+
| Windows | `…\DeepSeek Harness\resources\app.asar` | `%APPDATA%\@deepseek-ai\dsh-desktop\Partitions\…` |
|
|
149
|
+
| macOS | `/Applications/DeepSeek Harness.app/Contents/Resources/app.asar` | `~/Library/Application Support/@deepseek-ai/dsh-desktop/Partitions/…` |
|
|
150
|
+
| Linux | `<安装目录>/resources/app.asar` | `~/.config/@deepseek-ai/dsh-desktop/Partitions/…` |
|
|
151
|
+
|
|
152
|
+
macOS 上若应用有代码签名,修改 `app.asar` 可能破坏签名校验,需要自行重新签名或使用未签名构建;替换前请先备份。
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 5. 注意事项与风险
|
|
157
|
+
|
|
158
|
+
- **非官方修改**:改动已安装应用的 `app.asar` 不在 DSH 支持范围内,出问题请用备份回滚;请在动手前确认你接受这个风险。
|
|
159
|
+
- **DSH 升级会覆盖 `app.asar`**:每次更新后需要重新执行一次本补丁;`Partitions` 目录里的登录数据不会丢,重新打补丁后照旧免登录。
|
|
160
|
+
- **版本相关 + integrity 是硬性要求**:字符串在不同 DSH 版本可能变化,且 `0.2.1-rc.3+` 起 ASAR 带逐文件 SHA256 integrity。补丁前**必须**用 `--dry-run` 全量体检:
|
|
161
|
+
|
|
162
|
+
```powershell
|
|
163
|
+
node scripts/patch-dsh-persistent-partition.cjs "F:\DeepSeek Harness\resources\app.asar" --dry-run
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
输出 `dry-run: patch would be applied` + `integrity: … 0 failed` 才可打;`target statement not found` 表示该版本已变化或官方已内置 `persist:`。**千万不要用纯字节替换脚本**,否则重启必崩。
|
|
167
|
+
- **等长是硬性要求**:脚本会校验长度,不等长直接中止(修改长度会破坏 ASAR 头部记录的偏移)。若要自定义分区名,必须把名字凑成与 `${randomUUID()}` 等长(`persist:` + `dsh-sidebar-browser-` + 7 字符)。
|
|
168
|
+
- **未来版本**:如果 DSH 官方把 guest 分区改成 `persist:`,就不再需要本补丁——升级后先检查原文是否还在,不在即说明已内置支持。
|
|
169
|
+
- **共享分区**:补丁对**所有**浏览器 guest 生效,左侧聊天面板与右侧内置浏览器会共用同一个持久分区(这是有意为之:两边登录态一致)。若你希望两者隔离,需要改为按 workspace 区分的固定分区名(保持总长度不变)。
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 6. 与 DSH 账号的关系(v1.1.1 核对)
|
|
174
|
+
|
|
175
|
+
DSH 自身的登录是 platform.deepseek.com 的 OAuth 授权(Host `deepseekAccount` 服务,`x-dsh-auth-token`),而 chat.deepseek.com 用的是**站点自带的 Token**(`api/v0/users/current` 无 token 返回 `40002 Missing Token`),两者不同源、不同凭据,归档中 `chat.deepseek.com` 零引用、**没有任何官方兑换/SSO/cookie 注入入口**。因此本方案的实际效果是:
|
|
176
|
+
|
|
177
|
+
> 首次用你的 DeepSeek 账号登录一次 chat.deepseek.com,之后长期记住、点开即用;而不是让网页版复用 DSH 的 token。
|
|
178
|
+
|
|
179
|
+
**渲染层也拿不到 DSH token**:`globalThis.dshDesktop` 只暴露 `browser.acquire/release/onOpenRequested` 等,没有 `getAuthToken`、写 cookie、打开 Platform 取 token 的 API;`getAuthToken()` 只存在于桌面壳内嵌 Platform 页(`usage`/`top_up`)的专用 preload 且受主帧 + origin 限制。`browserAcquire` IPC 只按 workspace 分配分区/租约,`configureSession()` 不注入凭据。所以**纯插件无法免扫码**。
|
|
180
|
+
|
|
181
|
+
**v1.1.1 起,本插件的账号状态条仅作展示**:读取 `ctx.remote.account.getState()` / `getProfile()` 显示昵称与登录状态,但**绝不参与 guest 分区或生命周期**。v1.1.0 曾把分区绑到账号状态,导致 `getState()` 瞬时失败就换分区、每次切回面板都要重新扫码——这是已修复的回归,不是「账号同步」的应有行为。
|
|
182
|
+
|
|
183
|
+
本文的持久分区补丁负责「网页 cookie 不因**重启**丢失」;v1.1.1 的固定分区负责「网页 cookie 不因**切换面板**丢失」。两者都保留首次扫码后的网页会话,都不能把 DSH token 变成网页登录态。
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
*本文档基于 DSH Desktop `0.2.2-rc.1` / Windows 核对(原文字符串仍在;早期验证于 `0.2.1-rc.2`);其他版本请以第 5 节的检查结果为准。*
|
|
Binary file
|