dsh-multi-chat 1.0.2 β 1.0.4
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 +287 -268
- package/README.zh.md +269 -232
- package/bin/dsh-multi-chat.mjs +33 -0
- package/lib/client.js +66 -28
- package/lib/client.js.map +1 -1
- package/lib/index.js +211 -22
- package/lib/invariant.js +23 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types/client/WallToggle.js +2 -2
- package/lib/types/client/WallToggle.js.map +1 -1
- package/lib/types/client/WallView.js +8 -8
- package/lib/types/client/WallView.js.map +1 -1
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js.map +1 -1
- package/lib/types/client/store.d.ts +1 -1
- package/lib/types/client/store.d.ts.map +1 -1
- package/lib/types/client/store.js +1 -1
- package/lib/types/client/store.js.map +1 -1
- package/lib/types/index.d.ts +80 -80
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +117 -12
- package/lib/types/index.js.map +1 -1
- package/package.json +139 -108
- package/scripts/check-platform-contract.mjs +171 -0
- package/src/client/WallToggle.tsx +52 -52
- package/src/client/WallView.tsx +406 -406
- package/src/client/index.ts +108 -103
- package/src/client/store.ts +40 -40
- package/src/index.ts +715 -618
- package/scripts/sync-vitest-paths.mjs +0 -63
package/README.md
CHANGED
|
@@ -1,268 +1,287 @@
|
|
|
1
|
-
# π¬ dsh-multi-chat β Multi-chat, one screen
|
|
2
|
-
|
|
3
|
-
**English** | [δΈζ](README.zh.md)
|
|
4
|
-
|
|
5
|
-
<p align="center">
|
|
6
|
-
<a href="https://www.npmjs.com/package/dsh-multi-chat"><img src="https://img.shields.io/npm/v/dsh-multi-chat" alt="npm version"></a>
|
|
7
|
-
<a href="https://www.npmjs.com/package/dsh-multi-chat"><img src="https://img.shields.io/npm/dm/dsh-multi-chat" alt="npm downloads"></a>
|
|
8
|
-
<a href="https://github.com/daetz-coder/dsh-multi-chat/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="license"></a>
|
|
9
|
-
<a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/dsh--plugin-community-brightgreen" alt="dsh-plugin"></a>
|
|
10
|
-
</p>
|
|
11
|
-
|
|
12
|
-
<p align="center">
|
|
13
|
-
<a href="https://awesome-dsh-plugin.com/p/daetz-coder/dsh-multi-chat/"><img src="https://awesome-dsh-plugin.com/badge.svg" alt="awesome-dsh-plugin"></a>
|
|
14
|
-
<a href="https://dshfind.com/plugins/daetz-coder/dsh-multi-chat"><img src="https://dshfind.com/api/badge/daetz-coder/dsh-multi-chat" alt="dshfind"></a>
|
|
15
|
-
<a href="https://github.com/awesome-dsh-plugin/awesome-dsh-plugin"><img src="https://img.shields.io/badge/listed%20on-awesome--dsh--plugin-4d6bfe" alt="listed on awesome-dsh-plugin"></a>
|
|
16
|
-
</p>
|
|
17
|
-
|
|
18
|
-
> **Run N conversations in DeepSeek Harness at once, watch every Agent's live progress side-by-side, and check in from your phone or tablet.** One browser tab goes from "one conversation at a time" to "a panoramic multi-conversation cockpit."
|
|
19
|
-
|
|
20
|
-
Install a **multi-window wall** into the official [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) Web UI: a grid that shows N running DSH conversation instances simultaneously (each instance runs its own task), so every Agent's live progress, chat, and output are **visible at a glance** β no more hopping between endless tabs and windows.
|
|
21
|
-
|
|
22
|
-
## β¨ What it does
|
|
23
|
-
|
|
24
|
-
| Capability | Description |
|
|
25
|
-
|------------|-------------|
|
|
26
|
-
| πΊ **Multi-window** | One-click entry from the sidebar; the chat area becomes a window grid showing every task side-by-side, one pane per port |
|
|
27
|
-
| π **Auto-discovery** | Scans a port range to auto-find running DSH instances; manual management is also supported |
|
|
28
|
-
| β **One-click new window** | Launch a brand-new DSH instance right inside the wall to grow your conversation matrix |
|
|
29
|
-
| π± **Phone access** | The "Phone access" button starts a **built-in authenticated LAN gateway** β open the URL on your phone, enter the token, and watch progress |
|
|
30
|
-
| π **Window controls** | Maximize, refresh, open in a new tab, stop an instance, and switch column count (auto/1/2/3/4/6) |
|
|
31
|
-
|
|
32
|
-
> **Multi-chat = multi-port.** Start N `dsh web --port <n>` instances (each running one conversation/task), open the wall from any of them, and you see all of them side-by-side.
|
|
33
|
-
|
|
34
|
-
## πΈ Screenshots
|
|
35
|
-
|
|
36
|
-
**π₯οΈ Windows Β· Two chats side-by-side** β two running DSH instances laid out together, each pane a full official conversation UI with live online status dots and per-window controls (maximize / refresh / new tab / remove):
|
|
37
|
-
|
|
38
|
-

|
|
39
|
-
|
|
40
|
-
**π± iPad Β· Two chats on mobile** β on the same LAN, open the token-authenticated gateway URL on an iPad to watch two Agents' live progress on one tablet screen:
|
|
41
|
-
|
|
42
|
-

|
|
43
|
-
|
|
44
|
-
**π₯οΈ Windows Β· Three-chat panorama** β a 3-column grid of three running instances, all Agents on one screen, upgrading you from "one conversation at a time" to "a panoramic multi-conversation cockpit":
|
|
45
|
-
|
|
46
|
-

|
|
47
|
-
|
|
48
|
-
## π 30-second quick start
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
# 1. Install β ONE command straight from the npm registry (no CLI to download):
|
|
52
|
-
dsh plugin --profile web add dsh-multi-chat
|
|
53
|
-
|
|
54
|
-
# β¦or via the plugin's own npx CLI (packs a tarball and installs it):
|
|
55
|
-
npx dsh-multi-chat install
|
|
56
|
-
|
|
57
|
-
# 2. Start a few instances
|
|
58
|
-
npx dsh-multi-chat start --ports 3080,3081,3082
|
|
59
|
-
|
|
60
|
-
# 3. (Re)start any instance and click "Multi-window" in the sidebar footer β done π
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Why this approach
|
|
64
|
-
|
|
65
|
-
- **No official logic is touched**: the plugin only registers two **additive list slots** (`conversation.view` ring entry, `sidebar.footer.action` sidebar shortcut) and five read-only JSON routes (`/multi/api/ports`, `/multi/api/status`, `/multi/api/stop`, `/multi/api/create`, `/multi/api/link`). No existing slot is replaced, no line is rewritten, and no core session/agent/tool logic is touched.
|
|
66
|
-
- **The UI is the official UI**: the wall is a view in the official view ring rendered inside the chat panel (not a popup). Theme, type scale, icons, and controls all use the official `--dsw-*` tokens and official primitives (Button/Input/Menu/StateDot).
|
|
67
|
-
- **Recursion guard**: the wall never embeds its own port; embedded pages carry a `?multi-wall=embed` flag and register no wall UI, preventing infinite "wall-in-wall" recursion.
|
|
68
|
-
- **Minimal footprint**: one declarative client plugin package β it ships its own `dsh.bundle.patch` + `cordis.patch.yml`, so DSH mounts it as a bundle layer automatically.
|
|
69
|
-
|
|
70
|
-
## Directory layout
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
dsh-multi-chat/ # single-package structure
|
|
74
|
-
lib/ # built artifacts (lib/index.js + lib/client.js + types)
|
|
75
|
-
src/ # source (node half + browser half)
|
|
76
|
-
bin/dsh-multi-chat.mjs # cross-platform npx CLI (install/start/stop/gateway)
|
|
77
|
-
scripts/
|
|
78
|
-
install-plugin.ps1 # pack + install into profile (DSH auto-mounts the bundle)
|
|
79
|
-
start-multi.ps1 / stop-multi.ps1 # start/stop multiple dsh web instances
|
|
80
|
-
gateway.mjs # token-authenticated reverse-proxy gateway (phone/remote)
|
|
81
|
-
cordis.patch.yml # DSH bundle layer declaration
|
|
82
|
-
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Install & enable (Windows)
|
|
86
|
-
|
|
87
|
-
```powershell
|
|
88
|
-
# 1) Pack and install into the web profile. The plugin declares dsh.bundle.patch,
|
|
89
|
-
# so DSH auto-mounts its bundle layer β no manual patch edit.
|
|
90
|
-
.\scripts\install-plugin.ps1
|
|
91
|
-
|
|
92
|
-
# 2) Restart dsh web and open any instance
|
|
93
|
-
dsh web --port 3084
|
|
94
|
-
# Browser: http://127.0.0.1:3084 β a "Multi-window" button appears in the sidebar footer
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
Or manual:
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
npm pack # produce a tarball (dsh-multi-chat-1.0.
|
|
101
|
-
dsh plugin --profile web add dsh-multi-chat-1.0.
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
Uninstall (one command, nothing manual to clean up β restart `dsh web` to unload it):
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
dsh plugin --profile web remove dsh-multi-chat
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
## Usage
|
|
111
|
-
|
|
112
|
-
1. Start several instances: `.\scripts\start-multi.ps1 -Ports "3080,3081,3082,3084"` (or manual `dsh web --port <n>`).
|
|
113
|
-
2. Open any instance and click the "Multi-window" shortcut in the sidebar footer (or the "Multi-window" tab at the top of the chat area).
|
|
114
|
-
3. Inside the wall view: auto-discovery (own port excluded), column switching (auto/1/2/3/4/6, horizontally filled by default), click title to maximize, β³ refresh one, β open in a new tab, β remove from view, refresh all, and live online status dots. The layout is persisted to `localStorage`.
|
|
115
|
-
4. To exit the wall, click the **"Exit" button in the toolbar's top-right** to switch back to the chat view in one click.
|
|
116
|
-
|
|
117
|
-
## Phone / remote access (built-in authenticated gateway)
|
|
118
|
-
|
|
119
|
-
The official `dsh web` **deliberately forbids `--host 0.0.0.0`** (it would expose remote code execution to the network). This plugin ships a built-in **token-authenticated intranet gateway**: click the "Phone access" button and it **automatically** starts a gateway for the current instance (listening on `0.0.0.0`, reverse-proxying to `127.0.0.1:<this instance's port>`), returning a LAN URL + login token.
|
|
120
|
-
|
|
121
|
-
```text
|
|
122
|
-
Click "Phone access" β you get:
|
|
123
|
-
Available on your phone on the same network: http://10.105.7.204:9477 token: 2efb23eade16
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
Open that URL on your phone and enter the token to reach the full DSH UI. The gateway's security model:
|
|
127
|
-
|
|
128
|
-
- HMAC-signed HttpOnly/SameSite session cookie (12h default), `?token=` for script convenience, per-IP rate limiting on failed logins
|
|
129
|
-
- All proxied requests rewrite Host/Origin to the loopback target, so the official `/api` browser-trust fence (the DNS-rebinding defense) treats it as a local request β no restart / `--trusted-host` needed
|
|
130
|
-
- WebSocket upgrades and SSE streams pass through unchanged
|
|
131
|
-
- When the intended port hits a Windows excluded range or is already bound, it automatically falls back to an OS-assigned free port
|
|
132
|
-
|
|
133
|
-
> A standalone `scripts/gateway.mjs` (with optional TLS) is also available for advanced manual use.
|
|
134
|
-
|
|
135
|
-
## Distribution & install
|
|
136
|
-
|
|
137
|
-
`dsh-multi-chat` is published to npm (unscoped public package) and released on GitHub (source zip/tarball per tag). Every channel below ends in the same three things: the package lands as a dependency of the web profile, DSH reconciles it into the bundle layer stack (the package declares `dsh.bundle.patch`, so its own `cordis.patch.yml` is mounted automatically β no manual patch edits), and a `dsh web` restart loads the wall.
|
|
138
|
-
|
|
139
|
-
### Install / uninstall cheat-sheet
|
|
140
|
-
|
|
141
|
-
| Channel | Install | Uninstall |
|
|
142
|
-
|---------|---------|-----------|
|
|
143
|
-
| **One-command (registry)** | `dsh plugin --profile web add dsh-multi-chat` | `dsh plugin --profile web remove dsh-multi-chat` |
|
|
144
|
-
| **npx (no download)** | `npx dsh-multi-chat install` | `dsh plugin --profile web remove dsh-multi-chat` |
|
|
145
|
-
| **Global CLI (npm)** | `npm i -g dsh-multi-chat` then `dsh-multi-chat install` | `dsh plugin --profile web remove dsh-multi-chat` then `npm rm -g dsh-multi-chat` |
|
|
146
|
-
| **Tarball (offline)** | `npm pack` β `dsh plugin --profile web add ./dsh-multi-chat-1.0.
|
|
147
|
-
| **Git clone** | `node bin/dsh-multi-chat.mjs install` | `dsh plugin --profile web remove dsh-multi-chat` |
|
|
148
|
-
|
|
149
|
-
> `dsh plugin --profile web remove dsh-multi-chat` is the **single uninstall
|
|
150
|
-
> command for every channel**: it removes the dependency from the profile, and
|
|
151
|
-
> DSH strips it from the `dsh.profile.bundles` layer stack automatically
|
|
152
|
-
> (reconciled from the installed dependency set β see `dsh plugin --help`).
|
|
153
|
-
> Restart `dsh web` afterwards to unload the wall.
|
|
154
|
-
|
|
155
|
-
> **pnpm β₯ 11 gotcha**: pnpm 11's `minimumReleaseAge` supply-chain guard skips
|
|
156
|
-
> freshly published versions (
|
|
157
|
-
>
|
|
158
|
-
> dsh-multi-chat` reports an older version than the latest, add
|
|
159
|
-
> `minimumReleaseAge: 0` to the profile's
|
|
160
|
-
> `~/.dsh/profiles/web/pnpm-workspace.yaml` and re-run the add.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
dsh-multi-chat
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
npx dsh-multi-chat
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
node bin/dsh-multi-chat.mjs
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
node bin/dsh-multi-chat.mjs
|
|
200
|
-
node bin/dsh-multi-chat.mjs
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
node bin/dsh-multi-chat.mjs
|
|
210
|
-
node bin/dsh-multi-chat.mjs
|
|
211
|
-
node bin/dsh-multi-chat.mjs
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
- **
|
|
220
|
-
- **
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
npm
|
|
229
|
-
npm
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
1
|
+
# π¬ dsh-multi-chat β Multi-chat, one screen
|
|
2
|
+
|
|
3
|
+
**English** | [δΈζ](README.zh.md)
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/dsh-multi-chat"><img src="https://img.shields.io/npm/v/dsh-multi-chat" alt="npm version"></a>
|
|
7
|
+
<a href="https://www.npmjs.com/package/dsh-multi-chat"><img src="https://img.shields.io/npm/dm/dsh-multi-chat" alt="npm downloads"></a>
|
|
8
|
+
<a href="https://github.com/daetz-coder/dsh-multi-chat/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="license"></a>
|
|
9
|
+
<a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/dsh--plugin-community-brightgreen" alt="dsh-plugin"></a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://awesome-dsh-plugin.com/p/daetz-coder/dsh-multi-chat/"><img src="https://awesome-dsh-plugin.com/badge.svg" alt="awesome-dsh-plugin"></a>
|
|
14
|
+
<a href="https://dshfind.com/plugins/daetz-coder/dsh-multi-chat"><img src="https://dshfind.com/api/badge/daetz-coder/dsh-multi-chat" alt="dshfind"></a>
|
|
15
|
+
<a href="https://github.com/awesome-dsh-plugin/awesome-dsh-plugin"><img src="https://img.shields.io/badge/listed%20on-awesome--dsh--plugin-4d6bfe" alt="listed on awesome-dsh-plugin"></a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
> **Run N conversations in DeepSeek Harness at once, watch every Agent's live progress side-by-side, and check in from your phone or tablet.** One browser tab goes from "one conversation at a time" to "a panoramic multi-conversation cockpit."
|
|
19
|
+
|
|
20
|
+
Install a **multi-window wall** into the official [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) Web UI: a grid that shows N running DSH conversation instances simultaneously (each instance runs its own task), so every Agent's live progress, chat, and output are **visible at a glance** β no more hopping between endless tabs and windows.
|
|
21
|
+
|
|
22
|
+
## β¨ What it does
|
|
23
|
+
|
|
24
|
+
| Capability | Description |
|
|
25
|
+
|------------|-------------|
|
|
26
|
+
| πΊ **Multi-window** | One-click entry from the sidebar; the chat area becomes a window grid showing every task side-by-side, one pane per port |
|
|
27
|
+
| π **Auto-discovery** | Scans a port range to auto-find running DSH instances; manual management is also supported |
|
|
28
|
+
| β **One-click new window** | Launch a brand-new DSH instance right inside the wall to grow your conversation matrix |
|
|
29
|
+
| π± **Phone access** | The "Phone access" button starts a **built-in authenticated LAN gateway** β open the URL on your phone, enter the token, and watch progress |
|
|
30
|
+
| π **Window controls** | Maximize, refresh, open in a new tab, stop an instance, and switch column count (auto/1/2/3/4/6) |
|
|
31
|
+
|
|
32
|
+
> **Multi-chat = multi-port.** Start N `dsh web --port <n>` instances (each running one conversation/task), open the wall from any of them, and you see all of them side-by-side.
|
|
33
|
+
|
|
34
|
+
## πΈ Screenshots
|
|
35
|
+
|
|
36
|
+
**π₯οΈ Windows Β· Two chats side-by-side** β two running DSH instances laid out together, each pane a full official conversation UI with live online status dots and per-window controls (maximize / refresh / new tab / remove):
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
**π± iPad Β· Two chats on mobile** β on the same LAN, open the token-authenticated gateway URL on an iPad to watch two Agents' live progress on one tablet screen:
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
**π₯οΈ Windows Β· Three-chat panorama** β a 3-column grid of three running instances, all Agents on one screen, upgrading you from "one conversation at a time" to "a panoramic multi-conversation cockpit":
|
|
45
|
+
|
|
46
|
+

|
|
47
|
+
|
|
48
|
+
## π 30-second quick start
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# 1. Install β ONE command straight from the npm registry (no CLI to download):
|
|
52
|
+
dsh plugin --profile web add dsh-multi-chat
|
|
53
|
+
|
|
54
|
+
# β¦or via the plugin's own npx CLI (packs a tarball and installs it):
|
|
55
|
+
npx dsh-multi-chat install
|
|
56
|
+
|
|
57
|
+
# 2. Start a few instances
|
|
58
|
+
npx dsh-multi-chat start --ports 3080,3081,3082
|
|
59
|
+
|
|
60
|
+
# 3. (Re)start any instance and click "Multi-window" in the sidebar footer β done π
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Why this approach
|
|
64
|
+
|
|
65
|
+
- **No official logic is touched**: the plugin only registers two **additive list slots** (`conversation.view` ring entry, `sidebar.footer.action` sidebar shortcut) and five read-only JSON routes (`/multi/api/ports`, `/multi/api/status`, `/multi/api/stop`, `/multi/api/create`, `/multi/api/link`). No existing slot is replaced, no line is rewritten, and no core session/agent/tool logic is touched.
|
|
66
|
+
- **The UI is the official UI**: the wall is a view in the official view ring rendered inside the chat panel (not a popup). Theme, type scale, icons, and controls all use the official `--dsw-*` tokens and official primitives (Button/Input/Menu/StateDot).
|
|
67
|
+
- **Recursion guard**: the wall never embeds its own port; embedded pages carry a `?multi-wall=embed` flag and register no wall UI, preventing infinite "wall-in-wall" recursion.
|
|
68
|
+
- **Minimal footprint**: one declarative client plugin package β it ships its own `dsh.bundle.patch` + `cordis.patch.yml`, so DSH mounts it as a bundle layer automatically.
|
|
69
|
+
|
|
70
|
+
## Directory layout
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
dsh-multi-chat/ # single-package structure
|
|
74
|
+
lib/ # built artifacts (lib/index.js + lib/client.js + types)
|
|
75
|
+
src/ # source (node half + browser half)
|
|
76
|
+
bin/dsh-multi-chat.mjs # cross-platform npx CLI (install/start/stop/gateway)
|
|
77
|
+
scripts/
|
|
78
|
+
install-plugin.ps1 # pack + install into profile (DSH auto-mounts the bundle)
|
|
79
|
+
start-multi.ps1 / stop-multi.ps1 # start/stop multiple dsh web instances
|
|
80
|
+
gateway.mjs # token-authenticated reverse-proxy gateway (phone/remote)
|
|
81
|
+
cordis.patch.yml # DSH bundle layer declaration
|
|
82
|
+
platform-seeds.json # the DSH module-table contract this plugin builds against
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Install & enable (Windows)
|
|
86
|
+
|
|
87
|
+
```powershell
|
|
88
|
+
# 1) Pack and install into the web profile. The plugin declares dsh.bundle.patch,
|
|
89
|
+
# so DSH auto-mounts its bundle layer β no manual patch edit.
|
|
90
|
+
.\scripts\install-plugin.ps1
|
|
91
|
+
|
|
92
|
+
# 2) Restart dsh web and open any instance
|
|
93
|
+
dsh web --port 3084
|
|
94
|
+
# Browser: http://127.0.0.1:3084 β a "Multi-window" button appears in the sidebar footer
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Or manual:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npm pack # produce a tarball (dsh-multi-chat-1.0.3.tgz)
|
|
101
|
+
dsh plugin --profile web add dsh-multi-chat-1.0.3.tgz # DSH adds the package to the bundle layer stack automatically
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Uninstall (one command, nothing manual to clean up β restart `dsh web` to unload it):
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
dsh plugin --profile web remove dsh-multi-chat
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Usage
|
|
111
|
+
|
|
112
|
+
1. Start several instances: `.\scripts\start-multi.ps1 -Ports "3080,3081,3082,3084"` (or manual `dsh web --port <n>`).
|
|
113
|
+
2. Open any instance and click the "Multi-window" shortcut in the sidebar footer (or the "Multi-window" tab at the top of the chat area).
|
|
114
|
+
3. Inside the wall view: auto-discovery (own port excluded), column switching (auto/1/2/3/4/6, horizontally filled by default), click title to maximize, β³ refresh one, β open in a new tab, β remove from view, refresh all, and live online status dots. The layout is persisted to `localStorage`.
|
|
115
|
+
4. To exit the wall, click the **"Exit" button in the toolbar's top-right** to switch back to the chat view in one click.
|
|
116
|
+
|
|
117
|
+
## Phone / remote access (built-in authenticated gateway)
|
|
118
|
+
|
|
119
|
+
The official `dsh web` **deliberately forbids `--host 0.0.0.0`** (it would expose remote code execution to the network). This plugin ships a built-in **token-authenticated intranet gateway**: click the "Phone access" button and it **automatically** starts a gateway for the current instance (listening on `0.0.0.0`, reverse-proxying to `127.0.0.1:<this instance's port>`), returning a LAN URL + login token.
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
Click "Phone access" β you get:
|
|
123
|
+
Available on your phone on the same network: http://10.105.7.204:9477 token: 2efb23eade16
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Open that URL on your phone and enter the token to reach the full DSH UI. The gateway's security model:
|
|
127
|
+
|
|
128
|
+
- HMAC-signed HttpOnly/SameSite session cookie (12h default), `?token=` for script convenience, per-IP rate limiting on failed logins
|
|
129
|
+
- All proxied requests rewrite Host/Origin to the loopback target, so the official `/api` browser-trust fence (the DNS-rebinding defense) treats it as a local request β no restart / `--trusted-host` needed
|
|
130
|
+
- WebSocket upgrades and SSE streams pass through unchanged
|
|
131
|
+
- When the intended port hits a Windows excluded range or is already bound, it automatically falls back to an OS-assigned free port
|
|
132
|
+
|
|
133
|
+
> A standalone `scripts/gateway.mjs` (with optional TLS) is also available for advanced manual use.
|
|
134
|
+
|
|
135
|
+
## Distribution & install
|
|
136
|
+
|
|
137
|
+
`dsh-multi-chat` is published to npm (unscoped public package) and released on GitHub (source zip/tarball per tag). Every channel below ends in the same three things: the package lands as a dependency of the web profile, DSH reconciles it into the bundle layer stack (the package declares `dsh.bundle.patch`, so its own `cordis.patch.yml` is mounted automatically β no manual patch edits), and a `dsh web` restart loads the wall.
|
|
138
|
+
|
|
139
|
+
### Install / uninstall cheat-sheet
|
|
140
|
+
|
|
141
|
+
| Channel | Install | Uninstall |
|
|
142
|
+
|---------|---------|-----------|
|
|
143
|
+
| **One-command (registry)** | `dsh plugin --profile web add dsh-multi-chat` | `dsh plugin --profile web remove dsh-multi-chat` |
|
|
144
|
+
| **npx (no download)** | `npx dsh-multi-chat install` | `dsh plugin --profile web remove dsh-multi-chat` |
|
|
145
|
+
| **Global CLI (npm)** | `npm i -g dsh-multi-chat` then `dsh-multi-chat install` | `dsh plugin --profile web remove dsh-multi-chat` then `npm rm -g dsh-multi-chat` |
|
|
146
|
+
| **Tarball (offline)** | `npm pack` β `dsh plugin --profile web add ./dsh-multi-chat-1.0.3.tgz` | `dsh plugin --profile web remove dsh-multi-chat` |
|
|
147
|
+
| **Git clone** | `node bin/dsh-multi-chat.mjs install` | `dsh plugin --profile web remove dsh-multi-chat` |
|
|
148
|
+
|
|
149
|
+
> `dsh plugin --profile web remove dsh-multi-chat` is the **single uninstall
|
|
150
|
+
> command for every channel**: it removes the dependency from the profile, and
|
|
151
|
+
> DSH strips it from the `dsh.profile.bundles` layer stack automatically
|
|
152
|
+
> (reconciled from the installed dependency set β see `dsh plugin --help`).
|
|
153
|
+
> Restart `dsh web` afterwards to unload the wall.
|
|
154
|
+
|
|
155
|
+
> **pnpm β₯ 11 gotcha**: pnpm 11's `minimumReleaseAge` supply-chain guard skips
|
|
156
|
+
> freshly published versions (default cutoff: 1 day) and silently installs the
|
|
157
|
+
> newest "aged" release instead. If `dsh plugin --profile web add
|
|
158
|
+
> dsh-multi-chat` reports an older version than the latest, add
|
|
159
|
+
> `minimumReleaseAge: 0` to the profile's
|
|
160
|
+
> `~/.dsh/profiles/web/pnpm-workspace.yaml` and re-run the add. The bundled CLI
|
|
161
|
+
> (`npx dsh-multi-chat install`) writes that setting for you automatically.
|
|
162
|
+
|
|
163
|
+
The repo also bundles a cross-platform CLI, `dsh-multi-chat` (`bin/dsh-multi-chat.mjs`). Its `install` command probes `$DSH_HOME` (default `~/.dsh`), packs the package into the profile's `plugins/` folder, and runs `dsh plugin --profile web add <tarball>` (same behavior as `install-plugin.ps1`).
|
|
164
|
+
|
|
165
|
+
### Channel 1: npm / npx (recommended, easiest)
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# Published on npm β one line installs on any machine (node + pnpm required)
|
|
169
|
+
npx dsh-multi-chat install
|
|
170
|
+
|
|
171
|
+
# Or install the CLI globally, then install the plugin from anywhere
|
|
172
|
+
npm i -g dsh-multi-chat
|
|
173
|
+
dsh-multi-chat install
|
|
174
|
+
|
|
175
|
+
# Or run single commands straight from npx (no plugin install needed)
|
|
176
|
+
npx dsh-multi-chat start --remote --token <token> --ports 3080,3081
|
|
177
|
+
npx dsh-multi-chat gateway --target 127.0.0.1:3080 --token <token>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Maintaining / republishing (maintainer): `npm publish` (unscoped public package `dsh-multi-chat`).
|
|
181
|
+
|
|
182
|
+
### Channel 2: GitHub Release
|
|
183
|
+
|
|
184
|
+
Download the source zip/tarball from [Releases](https://github.com/daetz-coder/dsh-multi-chat/releases), unpack it, and cd in:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
node bin/dsh-multi-chat.mjs install # pack + dsh plugin add (see cheat-sheet)
|
|
188
|
+
node bin/dsh-multi-chat.mjs start --ports 3080,3081
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
> Tagging a release makes GitHub auto-generate the source zip/tarball assets; you can also attach a `npm pack`-produced `.tgz` as an offline install bundle.
|
|
192
|
+
|
|
193
|
+
### Channel 3: direct git install
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
git clone https://github.com/daetz-coder/dsh-multi-chat.git
|
|
197
|
+
cd dsh-multi-chat
|
|
198
|
+
|
|
199
|
+
node bin/dsh-multi-chat.mjs install # pack + dsh plugin add (see cheat-sheet)
|
|
200
|
+
node bin/dsh-multi-chat.mjs start --ports 3080,3081
|
|
201
|
+
node bin/dsh-multi-chat.mjs gateway --target 127.0.0.1:3080 --token <token>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
> For the one-command-on-this-machine flow, `dsh plugin --profile web add dsh-multi-chat` also works straight from a git clone β install the plugin _from_ the repo checkout without packaging it yourself.
|
|
205
|
+
|
|
206
|
+
### Running straight from this repo (development)
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
node bin/dsh-multi-chat.mjs install
|
|
210
|
+
node bin/dsh-multi-chat.mjs start --ports 3080,3081
|
|
211
|
+
node bin/dsh-multi-chat.mjs stop
|
|
212
|
+
node bin/dsh-multi-chat.mjs gateway --target 127.0.0.1:3080 --token <token>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## π Discovery & ecosystem
|
|
216
|
+
|
|
217
|
+
This plugin follows the official [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) client plugin spec:
|
|
218
|
+
|
|
219
|
+
- **Be found in the GitHub plugin ecosystem**: adding the [`dsh-plugin`](https://github.com/topics/dsh-plugin) topic to this repo makes it searchable on the official [`dsh-plugin` topic page](https://github.com/topics/dsh-plugin) (the officially recommended third-party discovery path).
|
|
220
|
+
- **Bilingual technical docs**: this repo ships `README.md` (English) and `README.zh.md` (Chinese) at the root, matching the bilingual convention of official `packages/client/*` plugins.
|
|
221
|
+
- **Purely additive, no core touching**: registers only the `conversation.view` / `sidebar.footer.action` list slots + `/multi/api/*` read-only routes, changing no official core logic.
|
|
222
|
+
|
|
223
|
+
## Building from source
|
|
224
|
+
|
|
225
|
+
The single-package layout uses `tsdown` for bundling and `tsc` for type declarations:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
npm install
|
|
229
|
+
npm run build # tsc + tsdown β lib/
|
|
230
|
+
npm test # vitest (browser half in jsdom + node half)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Testing
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
npm test # builds first, then runs vitest
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
`npm test` builds before it tests, so the suite always exercises the artifact
|
|
240
|
+
that ships rather than a stale `lib/`.
|
|
241
|
+
|
|
242
|
+
- `tests/browser-plugin.client.spec.tsx` β the browser half (view-ring entry,
|
|
243
|
+
sidebar shortcut, wall store, recursion guard, HMR disposal) against a real
|
|
244
|
+
cordis Context in jsdom, plus the node half (probe routes, config schema,
|
|
245
|
+
stop semantics).
|
|
246
|
+
- `tests/client-bundle.spec.ts` β loads the **built** `lib/client.js` through
|
|
247
|
+
`tests/dsh-module-loader.ts`, a stand-in for the web shell's module table.
|
|
248
|
+
This is the regression test for the v1.0.3 boot failure described below.
|
|
249
|
+
|
|
250
|
+
## The platform contract
|
|
251
|
+
|
|
252
|
+
A client plugin bundle is **not** an ES module. The web shell loads it as a
|
|
253
|
+
script whose only top-level effect is `window.__ModuleLoader__.load({ id,
|
|
254
|
+
factory })`, and every `require(spec)` inside the factory resolves against the
|
|
255
|
+
shell's module table. That table has exactly two sources:
|
|
256
|
+
|
|
257
|
+
1. the platform **seed table** β a fixed set of specifiers the shell hardcodes
|
|
258
|
+
into its own Vite bundle, and
|
|
259
|
+
2. **boot-graph package rows** β other plugins' bundles.
|
|
260
|
+
|
|
261
|
+
A plugin cannot conjure another package's row, so requiring anything outside
|
|
262
|
+
the seed table throws `missed the module table`. The boot is fail-closed: the
|
|
263
|
+
whole web UI stops on its "Failed to load plugins" card.
|
|
264
|
+
|
|
265
|
+
That is how v1.0.3 broke. It required `@deepseek-ai/dsh-client-runtime/client`,
|
|
266
|
+
a package upstream deleted, and nothing in the build noticed β the types came
|
|
267
|
+
from an unversioned local checkout of the harness monorepo, so the plugin
|
|
268
|
+
compiled happily against a platform that no longer existed.
|
|
269
|
+
|
|
270
|
+
`platform-seeds.json` pins the seed table, and is the single source of truth
|
|
271
|
+
for both `tsdown.config.ts` (which specifiers to leave unresolved) and
|
|
272
|
+
`scripts/check-platform-contract.mjs` (which fails the build if the bundle
|
|
273
|
+
requires anything else):
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
npm run check:platform # runs inside `npm run build`
|
|
277
|
+
npm run check:platform:against -- <path-to-installed-dsh> # diff against a real install
|
|
278
|
+
npm run sync:platform -- <path-to-installed-dsh> # adopt a new release's table
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
The `@deepseek-ai/*` platform packages are pinned as **exact** devDependencies,
|
|
282
|
+
so the surface this plugin compiles against is versioned and reviewable instead
|
|
283
|
+
of coming from a local checkout.
|
|
284
|
+
|
|
285
|
+
## License
|
|
286
|
+
|
|
287
|
+
MIT
|