@tianyulinfeng1/dsh-plugin-deepseek-chat 1.0.0 → 1.1.2

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 ADDED
@@ -0,0 +1,137 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ ## 1.1.2 — 2026-11-09
6
+
7
+ ### ⚠️ 重点修复:web boot 崩溃(duplicate factory registration)
8
+
9
+ 这是 **v1.1.1 的打包回归**:升级到 v1.1.1 后,DSH 桌面版页面在启动阶段
10
+ 直接失败、整个界面打不开。用户提供的崩溃日志文件为
11
+ `C:\Users\Administrator\AppData\Roaming\@deepseek-ai\dsh-desktop\logs\crash-2026-11-09T08-27-01-314Z-web-boot.log`。
12
+
13
+ 日志环境(原文):`time: 2026-11-09T08:27:01.314Z`、`source: web-boot`、
14
+ `app: @deepseek-ai/dsh-desktop 0.2.2-rc.1`、`win32 x64`、
15
+ `electron 44.7.0`、`node 24.21.0`、`locale zh-CN`。
16
+
17
+ 日志中的原始报错文本(原文):
18
+
19
+ > `Error: web boot: 1 entry did not activate`
20
+ >
21
+ > `@tianyulinfeng1/dsh-plugin-deepseek-chat: import failed (see console for the import error)`
22
+
23
+ 渲染进程(renderer)控制台的原始报错(原文):
24
+
25
+ > `client-modules: duplicate factory registration for "dsh-plugin-deepseek-chat" (bundle executed twice without invalidate?)`
26
+
27
+ 根因链(逐条):
28
+
29
+ 1. v1.1.1 把 npm 包名从无 scope 的 `dsh-plugin-deepseek-chat` 改成了带
30
+ scope 的 `@tianyulinfeng1/dsh-plugin-deepseek-chat`(`package.json` 的 `name`)。
31
+ 2. 但 `lib/client.js` 里 `window.__ModuleLoader__.load({ id: ... })` 注册的
32
+ 工厂 id 仍是旧的短名 `dsh-plugin-deepseek-chat`,**工厂 id 与包名不一致**。
33
+ 3. DSH 的 `dsh-client-modules` Host 半侧用 `package.json` 的 `name`(带 scope
34
+ 的完整包名)作为 client 图的行 id。
35
+ 4. `arrive()` 执行完 bundle 脚本后会检查 `factories.has(<行 id>)`;因为工厂
36
+ 实际只注册在短名下,对 `@tianyulinfeng1/dsh-plugin-deepseek-chat` 的检查
37
+ 结果为 `false`,Host 便判定该包「没有注册」。
38
+ 5. Host 因此把**同一个 bundle 脚本再执行一次**。
39
+ 6. 第二次注册时短名 id `dsh-plugin-deepseek-chat` 已存在,于是抛出
40
+ `duplicate factory registration ... (bundle executed twice without invalidate?)`。
41
+ 7. 该包被记为 `import failed`,web boot 统计到「1 个 entry 未激活」,抛出
42
+ `Error: web boot: 1 entry did not activate`,**整个页面启动失败**。
43
+ 8. 日志里 `dshmarket` 反复 install/remove 只是历史操作记录,**不是**本次崩溃
44
+ 的原因。
45
+
46
+ **修改了哪里、为什么能解决**:只改 `lib/client.js` 中
47
+ `window.__ModuleLoader__.load({ id: ... })` 的工厂 `id`,把它从短名
48
+ `dsh-plugin-deepseek-chat` 改为与 `package.json` 的 `name` 完全一致的
49
+ `@tianyulinfeng1/dsh-plugin-deepseek-chat`。对齐之后,`arrive()` 的
50
+ `factories.has(<行 id>)` 在 bundle 执行完即为 `true`,Host 认为该包已正常
51
+ 注册,不会再重复执行同一脚本,也就不会出现二次注册和
52
+ `duplicate factory registration`;entry 正常激活,web boot 完成。
53
+
54
+ **回归防护**:`test/check-client.mjs` 新增断言——「工厂 id 必须等于
55
+ `package.json` 的 `name`」。今后再改包名而忘了同步工厂 id,离线自检会立刻
56
+ 失败,不会把同样的回归带进发布包。
57
+
58
+ ### Verification
59
+
60
+ - 离线自检 35 项全部通过(`RESULT: PASS`),其中包含新增的「工厂 id 必须
61
+ 等于 `package.json` 的 `name`」回归断言。
62
+ - 扫描当前 DSH profile 全部 **30 个 web client bundle** 的 id / 包名一致性,
63
+ **0 处不匹配**。
64
+ - 完整发布说明见 `docs/RELEASE_NOTES_v1.1.2.md`。
65
+
66
+ ### Packaging
67
+
68
+ - 版本 `1.1.2`,npm 包名保持 `@tianyulinfeng1/dsh-plugin-deepseek-chat` 不变;
69
+ GitHub / Gitee 安装路由不变。
70
+ - 发布产物:`dsh-plugin-deepseek-chat-1.1.2.tgz`(`npm pack` 对 scoped 包的
71
+ 原生输出名为 `tianyulinfeng1-dsh-plugin-deepseek-chat-1.1.2.tgz`)。
72
+ - 本次只修 client bundle 的工厂 id,其余功能(固定 guest 分区、账号状态仅
73
+ 展示、崩溃隔离等)与 v1.1.1 相同。
74
+
75
+ ## 1.1.1 — 2026-11-09
76
+
77
+ ### Fixed
78
+
79
+ - **Switching panels/sessions no longer asks for a fresh QR scan.** v1.1.0 keyed the
80
+ desktop guest partition to the asynchronous DSH account probe. DSH's
81
+ `getState()` can transiently fail during a reconnect, so the partition changed
82
+ and handed the panel a brand-new cookie jar. The guest workspace is now the
83
+ constant `panel:deepseek-chat` and the conversation key is the constant
84
+ `dsh-plugin-deepseek-chat:last-url`.
85
+ - The DSH account status strip is now **display only**: it never feeds the lease
86
+ identity, an epoch, or the guest lifecycle. A transient `unknown` is ignored;
87
+ only a confirmed `signed-out` updates the strip.
88
+ - The guest starts on mount immediately instead of waiting for the async account
89
+ Remote, so a failed/late probe never blocks chat.
90
+
91
+ ### Documentation
92
+
93
+ - Documented, with source evidence, why a pure plugin **cannot** make
94
+ `chat.deepseek.com` reuse the already-signed-in DSH account: the two use
95
+ different credentials (Platform `x-dsh-auth-token` vs the site's own Token),
96
+ the renderer bridge exposes no token/cookie API, and the archive has no SSO or
97
+ cookie-injection entry point.
98
+ - Reworked `docs/login-persistence.md` around the upstream
99
+ `docs/patch-dsh-persistent-partition.cjs`: DSH `0.2.1-rc.3+` (including
100
+ `0.2.2-rc.1`) ships per-file SHA256 **ASAR integrity**, so the patch must
101
+ recompute and sync `lib/main.js`'s hash. A byte-only replacement would crash
102
+ DSH on restart with "ASAR Integrity Violation".
103
+ - Added `scripts/patch-dsh-persistent-partition.cjs` (upstream script: equal-length
104
+ replacement, integrity re-sync, full-archive verification, `--dry-run`,
105
+ idempotency, automatic backup). The earlier byte-only `.mjs` script is
106
+ deprecated and now fails loudly.
107
+
108
+ ### Verification
109
+
110
+ - Offline self-check: 35 cases, all passing (fixed partition, no rebuild on
111
+ account-state churn, partition reuse across panel switches, envelope parsing,
112
+ crash containment, popup allowlist).
113
+ - `app.asar` patch verified independently: 20519 files, 0 integrity failures;
114
+ `lib/main.js` header hash matches the actual SHA256 of the patched bytes.
115
+
116
+ ### Packaging
117
+
118
+ - Published to npm as **`@tianyulinfeng1/dsh-plugin-deepseek-chat`** (the
119
+ registry name this project owns). `package.json` and `cordis.patch.yml` now
120
+ carry that scoped package name so an npm-installed copy mounts under the same
121
+ `deepseek-chat` bundle id; the GitHub install routes (`github:sweet560/...`)
122
+ are unchanged.
123
+
124
+ ## 1.1.0 — 2026-11-09
125
+
126
+ - Added support for DSH `0.2.2-rc.1` (`engines.dsh >=0.2.2-rc.1`).
127
+ - Added a DSH account status strip via the official `ctx.remote.account` Remote
128
+ (correct generated `{ ok, value }` envelope parsing).
129
+ - Tightened conversation and popup URL validation; removed the dead guest element
130
+ before showing Retry.
131
+ - **Note:** keyed the guest partition to account state, which broke login
132
+ persistence. Fixed in 1.1.1.
133
+
134
+ ## 1.0.0
135
+
136
+ First local release: sidebar entry, full-width embedded chat, last-conversation
137
+ memory, and crash containment.
package/README.en.md CHANGED
@@ -1,12 +1,14 @@
1
1
  # dsh-plugin-deepseek-chat
2
2
 
3
- English | [中文](https://github.com/sweet560/dsh-plugin-deepseek-chat/blob/main/README.md)
3
+ English | [中文](README.md)
4
4
 
5
- Adds a **DeepSeek Chat** entry to the **DeepSeek Harness (DSH) desktop** sidebar. Clicking it opens `chat.deepseek.com` full-width in the main area and remembers the last conversation.
5
+ > **Current version: v1.1.2** | Targets DSH **0.2.2-rc.1**
6
+
7
+ Adds a **DeepSeek Chat** entry to the **DeepSeek Harness (DSH) desktop** sidebar. Clicking it opens `chat.deepseek.com` full-width in the main area, remembers the last conversation, and shows the current DSH account state.
6
8
 
7
9
  > Repository: [GitHub](https://github.com/sweet560/dsh-plugin-deepseek-chat) | [Gitee mirror](https://gitee.com/wtt520wyl/dsh-plugin-deepseek-chat)
8
10
 
9
- ![panel screenshot](https://raw.githubusercontent.com/sweet560/dsh-plugin-deepseek-chat/main/docs/screenshot.png)
11
+ ![panel screenshot](docs/screenshot.png)
10
12
 
11
13
  ## Features
12
14
 
@@ -14,14 +16,18 @@ Adds a **DeepSeek Chat** entry to the **DeepSeek Harness (DSH) desktop** sidebar
14
16
  - Full-width embedded `https://chat.deepseek.com/` (Electron `<webview>` through the desktop guest-lease bridge `window.dshDesktop.browser`).
15
17
  - **Last-conversation memory**: switching panels/sessions or restarting the app reopens the conversation you left, not a new chat.
16
18
  - Auth pages never overwrite that memory; a dead conversation falls back to the site root.
19
+ - **Stable login session (fixed in v1.1.1)**: the guest partition is the constant `panel:deepseek-chat`; switching panels/sessions back no longer rebuilds the partition or asks for a fresh QR scan.
20
+ - **DSH account status**: a strip shows "Synced with the DSH account · name" / "No account" / "Account state unavailable" via the official `ctx.remote.account` Remote. **Display only — it never participates in the guest lifecycle.**
17
21
  - **Crash containment**: if the embedded page's process dies, is destroyed, or never starts, the panel shows a Retry card — the app window no longer goes blank (fixed in v0.4.0).
18
22
  - Inert on the plain Web surface (`dsh web`): the site sends `frame-ancestors 'none'`, so an iframe cannot carry it, and the guest bridge only exists in the desktop shell.
19
23
 
24
+ > **v1.1.2 web boot crash fix**: v1.1.1 renamed the npm package to the scoped `@tianyulinfeng1/dsh-plugin-deepseek-chat`, but the client-bundle factory id in `lib/client.js` stayed the old short name `dsh-plugin-deepseek-chat`. That mismatch made the DSH Host re-execute the same bundle and throw `duplicate factory registration`, ending in `web boot: 1 entry did not activate` and a failed page boot. v1.1.2 aligns the factory id with the full package name and adds a regression assertion that the factory id must equal `package.json`'s `name`. See [docs/RELEASE_NOTES_v1.1.2.md](docs/RELEASE_NOTES_v1.1.2.md).
25
+
20
26
  ## Requirements
21
27
 
22
28
  | Item | Requirement |
23
29
  |---|---|
24
- | DSH | Desktop `0.2.1-rc.2` or newer |
30
+ | DSH | Desktop `0.2.2-rc.1` or newer |
25
31
  | Platform | Windows / macOS / Linux desktop shell |
26
32
  | git | Only for the GitHub install route (pnpm fetches the repository) |
27
33
 
@@ -65,12 +71,36 @@ Then open **Plugins → Installed plugins** and make sure **DeepSeek Chat** is s
65
71
  dsh plugin --profile desktop add file:"<absolute path to this repository>"
66
72
  ```
67
73
 
74
+ ## Why the web chat cannot reuse the DSH sign-in
75
+
76
+ **A pure plugin cannot make chat.deepseek.com reuse the already-signed-in DSH account.** This is an architecture limit of DSH 0.2.2-rc.1, verified against its source:
77
+
78
+ | | DSH desktop account | chat.deepseek.com web |
79
+ |---|---|---|
80
+ | Credential | platform.deepseek.com `x-dsh-auth-token` (Host-only `getPlatformSession()`) | the site's own Token (`api/v0/users/current` returns `40002 Missing Token` without it) |
81
+ | Storage | Host credential store | httpOnly cookie in the guest session |
82
+ | Exchange | **None** — zero `chat.deepseek.com` references, no SSO, no cookie injection in the whole archive | — |
83
+
84
+ - The renderer bridge `globalThis.dshDesktop` exposes only `browser.acquire/release/onOpenRequested` etc. — **no** `getAuthToken`, cookie write, or "open Platform to fetch token" API. `getAuthToken()` exists only in the desktop shell's embedded Platform view (`usage` / `top_up`) preload, main-frame + origin restricted.
85
+ - The `browserAcquire` IPC only allocates a partition/lease by workspace string; `configureSession()` sets permissions and download interception, **it injects no credentials**. The guest is forced to `sandbox / contextIsolation / webSecurity`.
86
+ - So the DSH account state can only be **displayed**; it cannot become a web-session login. One manual QR sign-in is still required.
87
+
88
+ **What went wrong in v1.1.0 (fixed in v1.1.1)**: the guest partition had been keyed to the asynchronous account probe. DSH's `getState()` can transiently fail during a reconnect or session switch; changing the partition hands the panel a brand-new cookie jar → a fresh QR scan every time you switched back. Now:
89
+
90
+ - `acquire()`'s workspace is the constant `panel:deepseek-chat`, and the conversation key is the constant `dsh-plugin-deepseek-chat:last-url`;
91
+ - account state changes only update the strip — no epoch, no guest rebuild, no partition change;
92
+ - a transient `unknown` is ignored; only a confirmed `signed-out` updates the display;
93
+ - the guest starts on mount immediately, without waiting for the async Remote (a failed probe never blocks chat);
94
+ - popup navigation is allowlisted to `chat.deepseek.com` and `platform.deepseek.com` (HTTPS, default port).
95
+
96
+ > To also avoid re-scanning **after restarting DSH**, run [scripts/patch-dsh-persistent-partition.cjs](scripts/patch-dsh-persistent-partition.cjs) per [docs/login-persistence.md](docs/login-persistence.md) to change DSH's random in-memory partition into a deterministic `persist:` one (an `app.asar` edit — modifying the installed app, at your own risk). **It also syncs the per-file SHA256 ASAR integrity required by 0.2.2-rc.1** — a byte-only replacement (the deprecated `.mjs` script) would crash DSH on restart. It requires fully exiting DSH first. That patch preserves web cookies across restarts; it does not make the web chat reuse the DSH token.
97
+
68
98
  ## Usage
69
99
 
70
100
  - The first visit shows the chat.deepseek.com sign-in page; sign in once.
71
- - Within one app run, switching panels or sessions keeps you signed in.
72
- - **Sign in again after restarting DSH**: DSH currently gives embedded browsers an in-memory partition (`dsh-sidebar-browser-<uuid>`, no `persist:` prefix), so cookies are dropped when the process exits. This plugin does not modify DSH's installation files, so it cannot bypass that. To sign in once and stay signed in, run the bundled patch script **[docs/patch-dsh-persistent-partition.cjs](https://github.com/sweet560/dsh-plugin-deepseek-chat/blob/main/docs/patch-dsh-persistent-partition.cjs)** (rc.3 ASAR-integrity aware, supports `--dry-run`, automatic backup and full verification); see **[docs/login-persistence.md](https://github.com/sweet560/dsh-plugin-deepseek-chat/blob/main/docs/login-persistence.md)**.
73
- - The conversation URL is stored in the app's localStorage (`dsh-plugin-deepseek-chat:last-url`), so the last conversation is restored after a restart (while signed in and while it still exists).
101
+ - Within one app run, switching panels or sessions keeps you signed in (fixed partition since v1.1.1).
102
+ - **Sign in again after restarting DSH**: DSH currently gives embedded browsers a random in-memory partition (`dsh-sidebar-browser-<uuid>`, no `persist:` prefix), so cookies are dropped when the process exits. This plugin does not modify DSH's installation files; to sign in once and stay signed in, apply the equal-length byte patch described in **[docs/login-persistence.md](docs/login-persistence.md)**.
103
+ - The conversation URL is stored under the fixed key `dsh-plugin-deepseek-chat:last-url`, so the last conversation is restored after a restart (while signed in and while it still exists).
74
104
 
75
105
  ## Persistent login (optional)
76
106
 
@@ -78,19 +108,7 @@ DSH hands embedded browsers an **in-memory partition** (`dsh-sidebar-browser-${r
78
108
 
79
109
  Cause, script, verification, rollback and per-platform paths:
80
110
 
81
- **→ [docs/login-persistence.md](https://github.com/sweet560/dsh-plugin-deepseek-chat/blob/main/docs/login-persistence.md)**
82
-
83
- Bundled patch script (Node 18+, no dependencies):
84
-
85
- ```powershell
86
- # Patch (creates a timestamped backup first)
87
- node docs/patch-dsh-persistent-partition.cjs "F:\DeepSeek Harness\resources\app.asar"
88
-
89
- # Dry run: check without writing
90
- node docs/patch-dsh-persistent-partition.cjs "F:\DeepSeek Harness\resources\app.asar" --dry-run
91
- ```
92
-
93
- The script understands the per-file ASAR `integrity` metadata shipped by `0.2.1-rc.3` (it recomputes and syncs `lib/main.js`'s SHA256), is idempotent (already patched returns 0), creates an automatic backup, verifies every block hash before and after writing, and supports `--dry-run`.
111
+ **→ [docs/login-persistence.md](docs/login-persistence.md)**
94
112
 
95
113
  Note: this modifies the installed application and is outside DSH's support; a DSH update overwrites `app.asar` and the patch must be re-applied (the `Partitions` data survives). Back up first and proceed at your own risk.
96
114
 
@@ -99,6 +117,9 @@ Note: this modifies the installed application and is outside DSH's support; a DS
99
117
  - chat.deepseek.com detects the Electron embedding and occasionally shows a "usage environment is abnormal" notice. That is site-side behaviour, unrelated to this plugin.
100
118
  - Desktop only; `dsh web` in a browser shows no entry.
101
119
  - If the site changes its conversation URL scheme (currently `/a/chat/s/<id>`), the memory falls back to the site root.
120
+ - The DSH account status is display-only; it cannot turn the DSH OAuth token into a chat.deepseek.com web session, so the web chat still needs one manual sign-in on first use.
121
+ - `getProfile()` is used only for the display name. If it is temporarily unavailable the strip may lag briefly, but it **never** affects the guest or the login state.
122
+ - Restarting DSH still requires a fresh web sign-in unless the persistent-partition patch in [docs/login-persistence.md](docs/login-persistence.md) is applied.
102
123
 
103
124
  ## Crash containment (v0.4.0)
104
125
 
@@ -106,7 +127,16 @@ Earlier versions could leave the whole app a blank window a second or two after
106
127
 
107
128
  Root cause: React runs effect cleanups after it has removed the DOM, and Electron's `<webview>` throws from `getURL()` once detached. DSH treats any uncaught plugin error as `slot entry crashed in 'root'` and unmounts the root slot — the entire UI.
108
129
 
109
- The current implementation guards every guest call behind an `isConnected` check and `try/catch`, reads the URL before removing the element, wraps every event handler, puts a React error boundary around the panel, listens for `render-process-gone` / `destroyed` / `did-fail-load`, and has a 15-second start watchdog.
130
+ The current implementation guards every guest call behind an `isConnected` check and `try/catch`, reads the URL before removing the element, wraps every event handler, puts a React error boundary around the panel, listens for `render-process-gone` / `destroyed` / `did-fail-load`, and has a 15-second start watchdog. On failure the dead guest element is removed **before** the Retry card, so retrying cannot stack webviews; a listener-registration failure never starts a guest, and popup URLs are allowlisted (v1.1.0).
131
+
132
+ ## Version history
133
+
134
+ | Version | Changes |
135
+ |---|---|
136
+ | **1.1.2** | **Fixes the v1.1.1 web boot crash (highlight)**: the client-bundle factory id did not match the npm package name, so DSH re-executed the same bundle, threw `duplicate factory registration`, and failed with `1 entry did not activate` — the whole page would not boot. The factory id in `lib/client.js` is now aligned to `@tianyulinfeng1/dsh-plugin-deepseek-chat`, with a new regression assertion that it must equal `package.json`'s `name`; 35/35 offline checks pass and all 30 client bundles scan with 0 mismatches. See [docs/RELEASE_NOTES_v1.1.2.md](docs/RELEASE_NOTES_v1.1.2.md). |
137
+ | **1.1.1** | **Fixes the "scan the QR code again on every panel switch" regression**: the guest partition and conversation key are constants; account state is display-only and never touches the guest lifecycle; a transient `unknown` is ignored; the guest no longer waits for the async probe; self-check now asserts a fixed partition and no rebuild on state churn (35 cases). Documents why a pure plugin cannot reuse the DSH token. |
138
+ | 1.1.0 | DSH `0.2.2-rc.1`: DSH account status strip; correct generated Remote envelope parsing; stricter conversation and popup URL validation; dead guest removal on failure. **This release keyed the partition to account state and broke login persistence (fixed in 1.1.1).** |
139
+ | 1.0.0 | First local release: sidebar entry, full-width chat, conversation memory, crash containment. |
110
140
 
111
141
  ## Development
112
142
 
@@ -116,21 +146,22 @@ cd dsh-plugin-deepseek-chat
116
146
  node test/check-client.mjs
117
147
  ```
118
148
 
119
- `test/check-client.mjs` loads `lib/client.js` in a `node:vm` sandbox with fake React/DOM/bridge objects and checks registration, conversation memory, lease release, and crash containment.
149
+ `test/check-client.mjs` loads `lib/client.js` in a `node:vm` sandbox with fake React/DOM/bridge objects and checks registration, Remote envelope parsing, a fixed partition/key, no rebuild on account-state churn, partition reuse across panel switches, conversation memory, lease release, popup allowlisting and crash containment.
120
150
 
121
151
  ```
122
152
  lib/index.js host half (empty; it only gives the client bundle a Loader entry)
123
- lib/client.js browser half: entry, panel, webview lifecycle, crash containment
153
+ lib/client.js browser half: entry, panel, account status strip, webview lifecycle, crash containment
124
154
  cordis.patch.yml bundle patch that inserts this plugin's Loader row
125
155
  locale/{zh,en}.json Plugins-page title/description
126
156
  icon.svg Plugins-page icon
157
+ docs/login-persistence.md optional byte patch for DSH's in-memory browser partition
127
158
  test/check-client.mjs offline self-check
128
159
  ```
129
160
 
130
161
  ## Uninstall
131
162
 
132
163
  ```powershell
133
- dsh plugin --profile desktop remove dsh-plugin-deepseek-chat
164
+ dsh plugin --profile desktop remove @tianyulinfeng1/dsh-plugin-deepseek-chat
134
165
  ```
135
166
 
136
167
  ## License
package/README.md CHANGED
@@ -1,12 +1,14 @@
1
1
  # dsh-plugin-deepseek-chat
2
2
 
3
- [English](https://github.com/sweet560/dsh-plugin-deepseek-chat/blob/main/README.en.md) | 中文
3
+ [English](README.en.md) | 中文
4
4
 
5
- 在 **DeepSeek Harness(DSH)桌面版**的左侧栏加入 **DeepSeek 聊天** 入口,点开后 `chat.deepseek.com` 直接占满主内容区,并记住上次所在的会话。
5
+ > **当前版本:v1.1.2** |适配 DSH **0.2.2-rc.1**
6
+
7
+ 在 **DeepSeek Harness(DSH)桌面版**的左侧栏加入 **DeepSeek 聊天** 入口,点开后 `chat.deepseek.com` 直接占满主内容区,记住上次所在的会话,并显示当前 DSH 账号状态。
6
8
 
7
9
  > 仓库:[GitHub](https://github.com/sweet560/dsh-plugin-deepseek-chat) | [Gitee 镜像](https://gitee.com/wtt520wyl/dsh-plugin-deepseek-chat)
8
10
 
9
- ![面板截图](https://raw.githubusercontent.com/sweet560/dsh-plugin-deepseek-chat/main/docs/screenshot.png)
11
+ ![面板截图](docs/screenshot.png)
10
12
 
11
13
  ## 功能
12
14
 
@@ -14,14 +16,18 @@
14
16
  - 主内容区全宽内嵌 `https://chat.deepseek.com/`(Electron `<webview>`,走桌面壳的 guest 租约桥 `window.dshDesktop.browser`)。
15
17
  - **记住上次会话**:切到别的面板、切换会话、重启 App 后再进来,直接回到上次那条对话,而不是新对话。
16
18
  - 登录/鉴权页不会覆盖记忆;记住的会话失效或打不开时自动回退到首页。
19
+ - **稳定的登录会话(v1.1.1 修复)**:guest 分区固定为 `panel:deepseek-chat`,切换面板/会话回来不会重建分区、不会重新要求扫码。
20
+ - **DSH 账号状态展示**:面板顶部条显示「已与 DSH 登录账号同步 · 昵称」/「未登录」/「账号状态不可用」,通过官方 `ctx.remote.account` 读取;**仅作展示,绝不参与 guest 生命周期**。
17
21
  - **崩溃隔离**:内嵌页进程退出、被销毁或启动超时,只在面板里显示「重试」,不会再出现整个 App 变空白窗口的问题(v0.4.0 修复,见下文)。
18
22
  - 纯 Web 表层(`dsh web` 在浏览器里打开)自动不注册——该站点 `frame-ancestors 'none'`,iframe 嵌不了,guest 租约桥也只有桌面壳提供。
19
23
 
24
+ > **v1.1.2 修复(web boot 启动失败)**:v1.1.1 把 npm 包名改为带 scope 的 `@tianyulinfeng1/dsh-plugin-deepseek-chat` 后,`lib/client.js` 里 client bundle 的工厂 id 仍是旧短名 `dsh-plugin-deepseek-chat`,两者不一致使 DSH Host 重复执行同一 bundle、抛出 `duplicate factory registration`,最终 `web boot: 1 entry did not activate`、整个页面启动失败。v1.1.2 已把工厂 id 对齐为完整包名,并在离线自检中新增「工厂 id 必须等于 `package.json` 的 `name`」回归断言。详见 [docs/RELEASE_NOTES_v1.1.2.md](docs/RELEASE_NOTES_v1.1.2.md)。
25
+
20
26
  ## 环境要求
21
27
 
22
28
  | 项 | 要求 |
23
29
  |---|---|
24
- | DSH | 桌面版 `0.2.1-rc.2` 或更新 |
30
+ | DSH | 桌面版 `0.2.2-rc.1` 或更新 |
25
31
  | 平台 | Windows / macOS / Linux 桌面壳 |
26
32
  | git | 仅「GitHub 安装」方式需要(pnpm 通过 git 拉取仓库) |
27
33
 
@@ -35,7 +41,7 @@ npm 包名:**`@tianyulinfeng1/dsh-plugin-deepseek-chat`**(GitHub 仓库名
35
41
  dsh plugin --profile desktop add @tianyulinfeng1/dsh-plugin-deepseek-chat
36
42
  ```
37
43
 
38
- 插件页「安装第三方插件」里也可以直接填包名 `@tianyulinfeng1/dsh-plugin-deepseek-chat`。
44
+ 插件页的「安装第三方插件」里也可以直接填包名 `@tianyulinfeng1/dsh-plugin-deepseek-chat`。
39
45
 
40
46
  ### 方式二:插件页安装(GitHub)
41
47
 
@@ -65,12 +71,36 @@ dsh plugin --profile desktop add github:sweet560/dsh-plugin-deepseek-chat
65
71
  dsh plugin --profile desktop add file:"<本仓库绝对路径>"
66
72
  ```
67
73
 
74
+ ## 关于「免扫码 / 与 DSH 账号同步」的真相
75
+
76
+ **纯插件做不到让 chat.deepseek.com 直接复用 DSH 已登录的账号**,这不是实现遗漏,而是 DSH 的架构限制(已在 0.2.2-rc.1 源码中逐项核对):
77
+
78
+ | 项 | DSH 桌面版账号 | chat.deepseek.com 网页版 |
79
+ |---|---|---|
80
+ | 凭据 | platform.deepseek.com 的 `x-dsh-auth-token`(Host-only `getPlatformSession()`) | 站点自带的 Token(`api/v0/users/current` 无 token 返回 `40002 Missing Token`) |
81
+ | 存放 | Host 进程凭据存储 | guest session 的 httpOnly cookie |
82
+ | 兑换入口 | **无**;归档中 `chat.deepseek.com` 零引用,无 SSO、无 cookie 注入 | — |
83
+
84
+ - 渲染层桥 `globalThis.dshDesktop` 只有 `browser.acquire/release/onOpenRequested` 等,**没有** `getAuthToken`、写 cookie、打开 Platform 取 token 的 API;`getAuthToken()` 只存在于桌面壳内嵌 Platform 页(`usage` / `top_up`)的专用 preload,且受主帧 + origin 限制。
85
+ - `browserAcquire` IPC 只按 workspace 字符串分配分区/租约,`configureSession()` 只设权限与下载拦截,**不注入任何凭据**;guest 被强制 `sandbox/contextIsolation/webSecurity`。
86
+ - 因此:DSH 账号状态只能**显示**在面板上,无法换成网页版登录态;首次仍需在网页里扫码一次。
87
+
88
+ **v1.1.0 的教训(v1.1.1 已修复)**:曾把 guest 分区绑到异步账号探测结果上。DSH 的 `getState()` 在重连/切会话时会瞬时失败,分区一变就等于换了个全新 cookie 罐 → 每次切回面板都要求重新扫码。现在:
89
+
90
+ - `acquire()` 的 workspace 恒为 `panel:deepseek-chat`,会话记忆 key 恒为 `dsh-plugin-deepseek-chat:last-url`;
91
+ - 账号状态变化只更新显示条,不 bump epoch、不重建 guest、不换分区;
92
+ - 瞬时 `unknown` 被忽略,只有明确的 `signed-out` 才更新显示;
93
+ - guest 在挂载时立即启动,不再等待异步 Remote 探测(探测失败也不阻塞聊天);
94
+ - 弹窗导航只放行 `chat.deepseek.com` 与 `platform.deepseek.com`(HTTPS、默认端口)。
95
+
96
+ > 想做到「重启 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。
97
+
68
98
  ## 使用
69
99
 
70
100
  - 第一次打开会停在 chat.deepseek.com 的登录页,登录一次即可。
71
- - 本次 App 运行期间,切换面板/会话不需要重新登录。
72
- - **重启 DSH 后需要重新登录一次**:DSH 目前给内嵌浏览器的是内存分区(`dsh-sidebar-browser-<uuid>`,没有 `persist:` 前缀),进程退出即清空 cookie。本插件不修改 DSH 的安装文件,所以不会绕过这一限制;想做到「登录一次、长期免登录」,可直接运行仓库自带的补丁脚本 **[docs/patch-dsh-persistent-partition.cjs](https://github.com/sweet560/dsh-plugin-deepseek-chat/blob/main/docs/patch-dsh-persistent-partition.cjs)**(已适配 `0.2.1-rc.3` 的 ASAR integrity,支持 `--dry-run`、自动备份与全量校验),说明见 **[docs/login-persistence.md](https://github.com/sweet560/dsh-plugin-deepseek-chat/blob/main/docs/login-persistence.md)**。
73
- - 会话链接记在 App 的 localStorage(key `dsh-plugin-deepseek-chat:last-url`),重启后仍会打开上次的会话(前提是已登录且该会话仍存在)。
101
+ - 本次 App 运行期间,切换面板/会话**不需要**重新登录(v1.1.1 起分区固定)。
102
+ - **重启 DSH 后仍需要重新登录一次**:DSH 目前给内嵌浏览器的是随机内存分区(`dsh-sidebar-browser-<uuid>`,没有 `persist:` 前缀),进程退出即清空 cookie。本插件不修改 DSH 的安装文件;想做到「登录一次、长期免登录」,可按 **[docs/login-persistence.md](docs/login-persistence.md)** 做一次等长字节替换(含脚本、验证与回滚)。
103
+ - 会话链接记在 App 的 localStorage(固定 key `dsh-plugin-deepseek-chat:last-url`),重启后仍会打开上次的会话(前提是已登录且该会话仍存在)。
74
104
 
75
105
  ## 登录态持久化(可选,免登录)
76
106
 
@@ -78,19 +108,7 @@ DSH 目前给内嵌浏览器分配的是**内存分区**(`dsh-sidebar-browser-
78
108
 
79
109
  完整原因、脚本、验证步骤、回滚方式和各平台路径都整理在这里:
80
110
 
81
- **→ [docs/login-persistence.md](https://github.com/sweet560/dsh-plugin-deepseek-chat/blob/main/docs/login-persistence.md)**
82
-
83
- 补丁脚本(仓库自带,Node 18+,零依赖):
84
-
85
- ```powershell
86
- # 打补丁(自动备份)
87
- node docs/patch-dsh-persistent-partition.cjs "F:\DeepSeek Harness\resources\app.asar"
88
-
89
- # 只检查、不写盘
90
- node docs/patch-dsh-persistent-partition.cjs "F:\DeepSeek Harness\resources\app.asar" --dry-run
91
- ```
92
-
93
- 脚本已适配 `0.2.1-rc.3` 的逐文件 ASAR `integrity`(自动重算并同步 `lib/main.js` 的 SHA256),支持幂等重跑(已打补丁直接返回 0)、自动备份、写入前后全量校验,以及 `--dry-run` 预演。
111
+ **→ [docs/login-persistence.md](docs/login-persistence.md)**
94
112
 
95
113
  注意:这属于对已安装应用的修改,不在 DSH 支持范围内;DSH 升级会覆盖 `app.asar`,需要重新打补丁(`Partitions` 里的登录数据不会丢)。请先备份,风险自负。
96
114
 
@@ -99,6 +117,9 @@ node docs/patch-dsh-persistent-partition.cjs "F:\DeepSeek Harness\resources\app.
99
117
  - chat.deepseek.com 会检测 Electron 内嵌环境,偶尔弹出「使用环境异常…建议使用官方产品」的提示;这是站点侧行为,与本插件无关。
100
118
  - 仅桌面版有效;`dsh web` 在浏览器里打开时不显示入口。
101
119
  - 站点若调整会话 URL 规则(当前识别 `/a/chat/s/<id>` 形式),「记住会话」可能失效并回退到首页。
120
+ - DSH 账号状态只作展示,不能把 DSH 的 OAuth token 兑换成 chat.deepseek.com 的网页会话;首次仍需在该网页登录一次(见上文)。
121
+ - `getProfile()` 只用于显示昵称;该接口暂不可用时显示状态可能短暂滞后,但**绝不影响** guest 与登录态。
122
+ - 重启 DSH 后仍需重新登录网页版 chat,除非按 [docs/login-persistence.md](docs/login-persistence.md) 打持久分区补丁。
102
123
 
103
124
  ## 崩溃隔离(v0.4.0)
104
125
 
@@ -111,8 +132,18 @@ node docs/patch-dsh-persistent-partition.cjs "F:\DeepSeek Harness\resources\app.
111
132
  - 所有 guest 调用先判断 `element.isConnected` 再 `try/catch`,清理阶段「先读 URL、再移除元素」;
112
133
  - 每个事件处理器都包 `try/catch`,异常只落在面板自己的状态里;
113
134
  - 面板外包一层 React 错误边界;
114
- - 监听 `render-process-gone` / `destroyed` / `did-fail-load`,guest 出问题时面板内显示「重试」;
115
- - 15 秒启动看门狗,guest 一直不 `dom-ready` 就转重试卡片。
135
+ - 监听 `render-process-gone` / `destroyed` / `did-fail-load`,失败时**先移除失效的 guest 元素**再显示「重试」,避免重试叠加 webview;
136
+ - 15 秒启动看门狗,guest 一直不 `dom-ready` 就转重试卡片;
137
+ - 事件监听器若注册失败则不启动 guest;弹窗 URL 走白名单校验(v1.1.0)。
138
+
139
+ ## 版本历史
140
+
141
+ | 版本 | 内容 |
142
+ |---|---|
143
+ | **1.1.2** | **修复 v1.1.1 的 web boot 启动失败(重点)**:工厂 id 与 npm 包名不一致 → DSH 重复执行同一 bundle → `duplicate factory registration` → `1 entry did not activate`、整个页面打不开。已把 `lib/client.js` 工厂 id 对齐为 `@tianyulinfeng1/dsh-plugin-deepseek-chat`,并新增「工厂 id 必须等于 `package.json` 的 `name`」回归断言;离线自检 35 项全过、30 个 client bundle 扫描 0 不匹配。详见 [docs/RELEASE_NOTES_v1.1.2.md](docs/RELEASE_NOTES_v1.1.2.md)。 |
144
+ | **1.1.1** | **修复每次切回面板/重启都要重新扫码的回归**:guest 分区与会话 key 固定为常量,账号状态仅作展示、不参与 guest 生命周期;瞬时 `unknown` 被忽略;guest 不再等待异步账号探测;自检改为验证固定分区与状态抖动不重建(35 项)。明确记录「纯插件无法免扫码复用 DSH token」的源码结论。 |
145
+ | 1.1.0 | 支持 DSH `0.2.2-rc.1`:新增 DSH 账号状态条;修正生成式 Remote 信封解析;收紧会话 URL 与弹窗 URL 校验;失败时移除失效 guest。**此版把分区绑到账号状态,导致登录态抖动(已由 1.1.1 修复)**。 |
146
+ | 1.0.0 | 首个本地版本:侧栏入口、全宽内嵌聊天、会话记忆、崩溃隔离。 |
116
147
 
117
148
  ## 开发
118
149
 
@@ -122,24 +153,25 @@ cd dsh-plugin-deepseek-chat
122
153
  node test/check-client.mjs
123
154
  ```
124
155
 
125
- `test/check-client.mjs` 是离线自检:在 `node:vm` 里加载 `lib/client.js`,用假的 React/DOM/租约桥验证注册、会话记忆、租约释放和崩溃隔离(DOM 先移除再清理不抛错、guest 崩溃不逃逸、反复开关不漏租约)。
156
+ `test/check-client.mjs` 是离线自检:在 `node:vm` 里加载 `lib/client.js`,用假的 React/DOM/租约桥验证注册、Remote 信封解析、固定分区与会话 key、状态抖动不重建 guest、切换面板复用分区、会话记忆、租约释放、弹窗白名单和崩溃隔离。
126
157
 
127
158
  目录:
128
159
 
129
160
  ```
130
161
  lib/index.js 宿主半侧(空实现,只为让 client bundle 有 Loader 入口)
131
- lib/client.js 浏览器半侧:入口、面板、webview 生命周期、崩溃隔离
162
+ lib/client.js 浏览器半侧:入口、面板、账号状态展示、webview 生命周期、崩溃隔离
132
163
  cordis.patch.yml bundle 补丁:插入本插件的 Loader 行
133
164
  locale/zh.json 插件页显示名称/描述(中文)
134
165
  locale/en.json 插件页显示名称/描述(英文)
135
166
  icon.svg 插件页图标
167
+ docs/login-persistence.md 可选:DSH 内存分区改持久分区的字节补丁
136
168
  test/check-client.mjs 离线自检
137
169
  ```
138
170
 
139
171
  ## 卸载
140
172
 
141
173
  ```powershell
142
- dsh plugin --profile desktop remove dsh-plugin-deepseek-chat
174
+ dsh plugin --profile desktop remove @tianyulinfeng1/dsh-plugin-deepseek-chat
143
175
  ```
144
176
 
145
177
  ## 作者
package/cordis.patch.yml CHANGED
@@ -1,6 +1,6 @@
1
- # dsh-plugin-deepseek-chat bundle patch: one insert registers this plugin.
2
- # insert.name must be the package name; insert.id must be unique across the
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.