dsh-bridge-gateway 0.1.7 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +301 -0
- package/README.md +134 -9
- package/README.zh.md +178 -16
- package/client/build.mjs +1 -1
- package/client/client.js +927 -1072
- package/client/index.js +851 -1079
- package/cordis.patch.yml +11 -0
- package/lib/auth/login-template.js +1 -1
- package/lib/auth/manager.js +105 -2
- package/lib/bridge-rpc-constants.js +20 -1
- package/lib/bridge-rpc.js +75 -13
- package/lib/cloudflared-manager.mjs +61 -8
- package/lib/feishu/index.js +5 -2
- package/lib/feishu/lark-bundled.mjs +33 -1
- package/lib/gateway/cert.js +1 -1
- package/lib/index.js +638 -82
- package/lib/paths.js +204 -0
- package/lib/platform/config-restore.js +70 -0
- package/lib/platform/conversation-bridge.js +104 -13
- package/lib/platform/session-config.js +69 -0
- package/lib/qq/index.js +6 -3
- package/lib/telegram/gateway.js +28 -25
- package/lib/telegram/index.js +5 -2
- package/lib/tunnel-client.mjs +74 -3
- package/lib/wechat/gateway.js +3 -2
- package/lib/wechat/index.js +8 -3
- package/package.json +5 -7
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,307 @@
|
|
|
2
2
|
|
|
3
3
|
本项目 `dsh-bridge-gateway` 是基于 [dsh-bridge](https://github.com/wenbin-wb/dsh-bridge) 的移植增强分支,在保留原版能力之外,重点新增「公网直连网关」。
|
|
4
4
|
|
|
5
|
+
## [0.2.1] - 2026-09-25
|
|
6
|
+
|
|
7
|
+
### 兼容性核查:DSH 0.1.7 无需改动
|
|
8
|
+
|
|
9
|
+
对 `0.1.7-rc.2` 逐项复核后确认本插件不需要适配:宿主侧注入的 `connection`、`webServer`、
|
|
10
|
+
`sessions`、`agents`、`approval`、`workspaceRegistry`、`sessionPersistence`、`loader`
|
|
11
|
+
八个服务全部仍在;客户端注入的 `slots` / `connection` / `workspaces` / `sessions` 与注册的
|
|
12
|
+
`settings.section` 槽位也仍在;`dsh-llm` 的 `createUserMessage`、`@deepseek-ai/cordis` 的
|
|
13
|
+
`Service` 导出未变。`0.1.5` 线继续兼容(peer 范围 `^4.0.1` / `>=0.1.0-rc.6` 两条线都满足)。
|
|
14
|
+
|
|
15
|
+
### 修复:HTML 响应强制 no-store(远程端"改了页面不变")
|
|
16
|
+
|
|
17
|
+
DSH 的 index HTML **不带 cache-control**,浏览器按启发式缓存它;而该 HTML 里钉着各客户端模块的
|
|
18
|
+
`rev`,模块 bundle 又是 `immutable` 长缓存 —— 于是 host 重启/重建后换了新 rev,旧标签页与
|
|
19
|
+
**普通刷新**仍然指向旧 rev 的旧代码,表现为"改动永远不生效"(远程直连场景尤其明显:
|
|
20
|
+
页面只重连、不重新取 index)。
|
|
21
|
+
|
|
22
|
+
透明反向代理分支对 `content-type: text/html` 的响应统一加
|
|
23
|
+
`Cache-Control: no-store, must-revalidate`,并删除 `etag` / `last-modified`;
|
|
24
|
+
非 HTML(含带 rev 的模块 bundle)保持原样,缓存策略不变。
|
|
25
|
+
|
|
26
|
+
注意:这条只对**新取到的响应**生效;浏览器里已经存下的旧 index 需要一次硬刷新
|
|
27
|
+
(Cmd+Shift+R)或带新查询串的地址来替换。
|
|
28
|
+
|
|
29
|
+
## [0.2.0] - 2026-09-19
|
|
30
|
+
|
|
31
|
+
### 破坏性变更(移除 dsh-mobile 协议舱;访问配置 Tab 重构;新增 Tailscale 隧道)
|
|
32
|
+
|
|
33
|
+
**移除 dsh-mobile 相关功能**
|
|
34
|
+
|
|
35
|
+
- **移动协议舱整体删除**:`lib/mobile/`(`/ws/mobile` WebSocket + 设备配对 + 会话跟随)、
|
|
36
|
+
`lib/shared/kernel-link.mjs`(行间通信内核)、`client/mobile-panel.js`(移动设备卡)、
|
|
37
|
+
面板「移动设备」Tab 及其全部 RPC 端点(`mobileStatus` / `mobilePair` / `mobileSetMode` /
|
|
38
|
+
`mobileRevoke` / `mobileSaveEndpoints` / `mobileDetectTailnet`)。
|
|
39
|
+
- **`cordis.patch.yml` 从两个 cordis row 恢复为单个**:`dsh-bridge-gateway/mobile` row 一并
|
|
40
|
+
移除;随之移除为移动舱 `search` 通道打开 `session-query-sqlite` 全文索引的那层 patch
|
|
41
|
+
(恢复 dsh-web-app 默认的 `openAt: never`)。Kernel 的 `pathPolicy` 接缝删除后,
|
|
42
|
+
代理的 WebSocket upgrade 鉴权恢复为单路 `authManager.verifyRequest()` 判断。
|
|
43
|
+
- **Linux 一键公网部署 CLI 整体删除**:`bin/setup-ip.mjs` 与
|
|
44
|
+
`helper/dsh_mobile_gateway_helper.py`(`package.json` 的 `bin` 字段一并移除)。
|
|
45
|
+
该链路只服务 `/ws/mobile` 的 443 发布(Nginx + certbot IP 证书 + root helper),
|
|
46
|
+
协议舱移除后已无服务对象。**`npx dsh-bridge-gateway init / setup / status / remove`
|
|
47
|
+
命令不再存在**,公网访问请改用 Tailscale Serve 或 Cloudflare 隧道。
|
|
48
|
+
- 依赖 `@deepseek-ai/schemastery` 移除(仅协议舱使用)。
|
|
49
|
+
|
|
50
|
+
> 注意:手机浏览器的**网页版移动端适配**(抽屉侧边栏、44px 热区、输入框贴底、
|
|
51
|
+
`injectMobileStyles` / `setupMobileExperience` 等)与协议舱无关,**全部保留**。
|
|
52
|
+
|
|
53
|
+
**新增 Tailscale 隧道卡片(访问配置 Tab)**
|
|
54
|
+
|
|
55
|
+
- 网关**自动探测**本机 Tailscale Serve 地址(`tailscale status --json` 取
|
|
56
|
+
`Self.DNSName`,`tailscale serve status --json` 反查 `Handlers["/"].Proxy` 指向本机
|
|
57
|
+
反代端口的 host),点「🔍 自动探测并填入」填入、可手动编辑、保存后生成扫码二维码。
|
|
58
|
+
- **探测为只读**:只查询状态,绝不代用户执行 `tailscale serve` 修改 tailscale 配置。
|
|
59
|
+
未 serve 时给出可复制的 `tailscale serve --bg <端口>` 命令引导用户自行执行一次。
|
|
60
|
+
- 探测结果 **30s TTL 缓存**,后台定时刷新;`getStatus()` 只读快照,
|
|
61
|
+
不会因面板 3s 轮询而起子进程。用户显式点击「自动探测」时绕过缓存。
|
|
62
|
+
- 二维码 URL 复用既有 `appendToken` 逻辑:开启安全认证时自动附加 `?auth=<token>` 免密扫码。
|
|
63
|
+
- 新增配置项 `tailscale.url`(保存的用户地址)与 RPC 端点 `tailscaleDetect` /
|
|
64
|
+
`tailscaleSaveUrl`(均需管理员门禁)。
|
|
65
|
+
|
|
66
|
+
**访客管理:查看开放,写操作仍归管理员**
|
|
67
|
+
|
|
68
|
+
- 「远程连接监控」的连接列表、来源 IP 与黑名单内容改为**对所有已通过访问门禁的
|
|
69
|
+
访客可见**——「谁正连着本机」是只读事实,不是管理凭据。
|
|
70
|
+
`getConnections` 不再要求 adminToken,改为用新增的 `canManage` 字段回报管理员身份。
|
|
71
|
+
- **写操作保持管理员门禁**:`connectionKick`(断开 / 断开并拉黑)与 `blacklistSet`
|
|
72
|
+
(黑名单增删)仍走 `checkAdminAuth`。UI 侧用 `canManage` 决定是否渲染这些按钮,
|
|
73
|
+
非管理员只看到列表与一行「断开与拉黑需要管理员权限」的说明,没有输入框与按钮。
|
|
74
|
+
|
|
75
|
+
**面板结构重构**
|
|
76
|
+
|
|
77
|
+
- **「局域网」+「公网访问」合并为「访问配置」Tab**,卡片顺序为
|
|
78
|
+
直连网关 → Cloudflare 隧道 → Tailscale 隧道 → 自建隧道 → 局域网访问
|
|
79
|
+
(局域网卡片移至最末)。
|
|
80
|
+
- **新增「访客管理」Tab**:远程连接监控(来源 IP 可见 / 断开 / 断开并拉黑)与
|
|
81
|
+
持久黑名单从公网 Tab 独立出来。服务端 `getConnections` / `kickIp` / `setBlacklist`
|
|
82
|
+
与 auth 模块的黑名单持久化逻辑保持原样,仅 UI 位置变更。
|
|
83
|
+
- Tab 数从 6 降为 5:访问配置 / 访客管理 / IM 机器人 / 安全认证 / 运维监控。
|
|
84
|
+
- **修复「运维监控」Tab 在桌面端不可见**:面板根容器 `maxWidth: 620`,6 个
|
|
85
|
+
`flexWrap: nowrap` 的 Tab 合计约 626px 溢出,而 `injectMobileStyles()` 全局隐藏了
|
|
86
|
+
滚动条(无 `@media` 包裹,桌面端同样生效),最后一个 Tab 被挤出可视区且无提示。
|
|
87
|
+
Tab 合并后宽度恢复余量,无需改动容器尺寸。
|
|
88
|
+
|
|
89
|
+
**卡片折叠(新增交互)**
|
|
90
|
+
|
|
91
|
+
- 访问配置 Tab 的 5 张卡片、安全认证 Tab 的 3 张卡片全部支持折叠,
|
|
92
|
+
点击标题行切换,右侧状态标签与开关按钮不触发折叠(`stopPropagation`)。
|
|
93
|
+
- 默认只展开每组的第一张:访问配置 → 直连网关;安全认证 → 全局访问安全防护体系。
|
|
94
|
+
- 折叠时保留一行关键信息(当前地址 / 异常阶段 / 未配置提示),
|
|
95
|
+
避免折叠后完全看不到状态。折叠状态由 `BridgePanel` 持有,切换 Tab 不丢失。
|
|
96
|
+
- 抽出共用外壳 `CardShell`,`TunnelCard` / `GatewayCard` / `TailscaleCard` /
|
|
97
|
+
安全认证三卡共用同一份标题行与折叠实现。
|
|
98
|
+
|
|
99
|
+
### 变更(命名统一:插件叫 dsh-bridge-gateway,数据目录也跟着改名)
|
|
100
|
+
|
|
101
|
+
- **数据目录 `~/.dsh/dsh-bridge/` → `~/.dsh/dsh-bridge-gateway/`**:fork 时把包名与
|
|
102
|
+
cordis loader id 都改成了 `dsh-bridge-gateway`,但**数据目录名是散落在 8+ 处的硬编码
|
|
103
|
+
字面量**,没跟着改,于是出现「插件叫 dsh-bridge-gateway,目录却叫 dsh-bridge」的
|
|
104
|
+
不一致。现统一改名。
|
|
105
|
+
- **新增 `lib/paths.js`(路径中心)**:所有持久化路径的唯一来源
|
|
106
|
+
(`dataDir()` / `configFile()` / `sessionsFile()` / `resetAuthFile()` /
|
|
107
|
+
`accessLogFile()` / `certDir(port)` / `wechatContextTokenFile()`)。
|
|
108
|
+
此前这些路径在 8 个文件里各自硬编码,改一次名字要动十几处且容易漏。
|
|
109
|
+
- **启动时自动迁移旧目录**(`migrateLegacyDataDir()`),规则按「绝不丢数据」排序:
|
|
110
|
+
1. 新目录已存在 → **不动**(已迁移过,绝不覆盖新数据);旧目录保留不删;
|
|
111
|
+
2. 旧目录不存在 → 直接创建新目录;
|
|
112
|
+
3. 旧目录存在 → 优先 `rename`(原子、瞬时);跨设备失败时退化为递归复制,
|
|
113
|
+
**复制成功才**处理旧目录(改名为 `.migrated-<ts>` 留档)。
|
|
114
|
+
迁移失败**不抛出**、只记日志,插件仍能正常启动,旧数据留在原处。
|
|
115
|
+
幂等,可安全重复调用。
|
|
116
|
+
- **同步修正命名**:`lib/index.js` 的 logger 命名空间与 `client/index.js` 导出的
|
|
117
|
+
`name` 由 `dsh-bridge` 改为 `dsh-bridge-gateway`;设置页 section id 同步;
|
|
118
|
+
**救急指令文案**(登录页 + 设置页共 3 处 `touch .../reset-auth`)指向新目录。
|
|
119
|
+
- **RPC 通道名 `/dsh-bridge` → `/dsh-bridge-gateway`,并对老用户双向兼容**:
|
|
120
|
+
通道名会被**构建期内联进浏览器 bundle**,因此升级后「浏览器里已缓存的旧 bundle」
|
|
121
|
+
仍会打旧路径 —— 若服务端只注册新名,这些客户端会全部 404,表现为
|
|
122
|
+
**「升级后设置页所有按钮点了没反应」**。兼容做法是两侧各自兜住:
|
|
123
|
+
- **服务端**:`webServer.register` 以 path 为键、不同 path 互不冲突,故**同时注册
|
|
124
|
+
新旧两条路径**(`BRIDGE_RPC_CHANNELS`)。已实测两条路径均返回 200。
|
|
125
|
+
- **客户端**:新 bundle 默认走新名,调用失败时**回落旧名**,覆盖「host 侧插件
|
|
126
|
+
尚未更新」的中间态。
|
|
127
|
+
- 端点名(`getStatus` / `authSetConfig` 等)**保持不变**,避免 second wire break。
|
|
128
|
+
- 待旧客户端自然淘汰后(重大版本)再移除兼容路径。
|
|
129
|
+
- **cloudflared 缓存目录 `~/.dsh-bridge/` → `~/.dsh-bridge-cloudflared/`,并自动迁移**:
|
|
130
|
+
这是外部依赖缓存(约 39MB 二进制),与插件数据目录分开存放。
|
|
131
|
+
启动时 `migrateLegacyCloudflaredDir()` 自动迁移(优先 `rename`,跨设备退化复制),
|
|
132
|
+
使老用户**免于重新下载 39MB**。该迁移失败代价仅为重新下载,故策略比数据目录更宽松
|
|
133
|
+
(失败只记 warn,绝不阻断启动)。已实测:本机 39MB 二进制迁移成功,旧目录已消失。
|
|
134
|
+
- **未改动**:RPC **端点名**(除通道名外的全部 wire 词汇)、`BRIDGE_ENDPOINTS` 的键。
|
|
135
|
+
|
|
136
|
+
### 老用户升级路径(本次改名的完整兼容说明)
|
|
137
|
+
|
|
138
|
+
| 场景 | 行为 |
|
|
139
|
+
|---|---|
|
|
140
|
+
| 数据目录 `dsh-bridge/` | 启动时自动迁移到 `dsh-bridge-gateway/`;新目录已存在则**不动**且保留旧目录 |
|
|
141
|
+
| cloudflared 缓存 `.dsh-bridge/` | 启动时自动迁移,免于重下 39MB |
|
|
142
|
+
| 浏览器缓存着**旧** bundle | 服务端仍注册旧通道 `/dsh-bridge`,调用照常成功 |
|
|
143
|
+
| 浏览器加载**新** bundle、host 仍是旧的 | 客户端回落旧通道 |
|
|
144
|
+
| 认证密码 / token / 证书 / 登录会话 | **完全保留**(迁移为 rename 语义,已逐字节比对) |
|
|
145
|
+
|
|
146
|
+
### 修复(P2 批次,移植自上游 dsh-bridge)
|
|
147
|
+
|
|
148
|
+
- **Telegram 代理在 Node ≥ 24 下失效(上游 issue #32)**:把自定义 `createConnection`
|
|
149
|
+
作为 `https.Agent` 的**构造参数**会被 Node ≥ 24 静默忽略(`https.Agent` 不拷贝构造
|
|
150
|
+
入参里的 `createConnection`,回退到原型上的默认直连方法),表现是「配了代理却永远走
|
|
151
|
+
直连」且**不报任何错**。本机 Node v24.19.0 已实证复现。现改为**构造后赋值实例自有
|
|
152
|
+
属性**(自有属性正确覆盖原型方法,Node 22/24 均生效)。
|
|
153
|
+
- **自建隧道 WebSocket 长连接被 DSH 主动掐断**:隧道此前是纯裸 TCP 字节转发,
|
|
154
|
+
而 WebSocket 的 Ping/Pong 属协议层控制帧,不会被自动应答;DSH API Gateway 每 2s 发
|
|
155
|
+
一次 Ping,收不到 Pong 就 terminate —— 表现为隧道频繁断连、会话中断。现新增帧层解析
|
|
156
|
+
`_processWsFrames()`:拦截 Ping (0x9) **自动回复遮罩后的 Pong (0xA)**,其余帧原样转发。
|
|
157
|
+
回复必须加 mask(client→server 方向协议要求),否则服务端 ws 库判定协议错误(1002)
|
|
158
|
+
—— 修好一个断连反而制造另一个。已覆盖分片重组、单 chunk 多帧、126/64K 长度分支。
|
|
159
|
+
- **Cloudflare 隧道崩溃后不再恢复(上游 issue #34)**:此前已就绪后意外退出(崩溃 /
|
|
160
|
+
OOM / 被误杀 / autoupdate 自替换)只把状态置为 `idle` 就「死透无人管」,公网入口
|
|
161
|
+
失联且永不恢复。现按**指数退避自动重启**(5s 起步、翻倍、封顶 5min、最多 12 次),
|
|
162
|
+
成功连接后计数清零;用户手动「关闭 / 重置」会取消待执行的重连。
|
|
163
|
+
- **禁用 cloudflared 24h autoupdate 自我替换**:cloudflared 的自动更新会在运行中把自身
|
|
164
|
+
替换成新版本,导致连接静默中断且版本控制权外流。现于参数层注入 `--no-autoupdate`
|
|
165
|
+
(已核对本机 cloudflared 2024.10.0 确实支持该 flag 及其 `NO_AUTOUPDATE` 环境变量),
|
|
166
|
+
并同时设置 `NO_AUTOUPDATE=true` 双保险。注意该全局 flag 必须位于 `run` 子命令**之前**。
|
|
167
|
+
- **登录 Session 持久化(上游 issue #36 / PR #37)**:`sessions` 此前是纯内存 Map,
|
|
168
|
+
宿主(`dsh web` 进程)一重启,所有已登录设备都要重新输访问密码。现落盘到
|
|
169
|
+
`<DSH_HOME>/dsh-bridge/sessions.json`(**权限 600**),只恢复结构合法且未过期的会话;
|
|
170
|
+
文件缺失/损坏安全降级为空(等价于旧行为:重新登录一次)。
|
|
171
|
+
**安全语义保持不变**:改密码 / 切模式 / 重新生成 token 时走的 `sessions.clear()`
|
|
172
|
+
会同步清空落盘文件,吊销后重启**不会**让旧会话复活。
|
|
173
|
+
|
|
174
|
+
### 测试基础设施
|
|
175
|
+
|
|
176
|
+
- **测试隔离**:`npm test` 现通过 `--import ./test/isolate-home.mjs` 把 `DSH_HOME`
|
|
177
|
+
指向一次性临时目录。此前部分测试构造 `AuthManager` 时会命中默认路径,
|
|
178
|
+
在真实的 `~/.dsh` 下留下文件(本次开发中实际发生过)。现在跑测试不会污染真实环境。
|
|
179
|
+
|
|
180
|
+
### 新增(设置页「高级设置」:会话级配置终于有界面入口)
|
|
181
|
+
|
|
182
|
+
- **每个 IM 平台卡片新增「⚙️ 高级设置」**:可直接配置该平台远程会话的 **工作区目录
|
|
183
|
+
(cwd)**、**Agent 预设**、**模型提供方**、**模型**。此前这四个字段**没有任何界面
|
|
184
|
+
入口**,只能手改 `config.json`,且改了还会在重启后丢失。留空表示使用 DSH 默认值。
|
|
185
|
+
- **新增 `lib/platform/session-config.js`**:会话级配置的读写归一
|
|
186
|
+
(`SESSION_CONFIG_FIELDS` / `readSessionConfig` / `applySessionConfig` /
|
|
187
|
+
`sessionConfigPatch`)。四个字段同时出现在「cordis 配置 → 设置页写入 → 启动恢复」
|
|
188
|
+
三处,共用同一套字段名与空值语义,避免再次出现「配了不生效 / 重启就丢」。
|
|
189
|
+
空串是**显式清除**(回落 DSH 默认值),非字符串入参一律忽略。
|
|
190
|
+
|
|
191
|
+
### 修复(平台配置重启即丢,移植自上游 dsh-bridge issue #40)
|
|
192
|
+
|
|
193
|
+
- **新增 `lib/platform/config-restore.js`**,把四个平台(微信 / QQ / 飞书 / Telegram)
|
|
194
|
+
的重启恢复逻辑收敛成一个纯函数 `applyRestoredPlatformConfig()`。此前各平台恢复链路
|
|
195
|
+
只挑字段回写:
|
|
196
|
+
- 微信 / Telegram:只恢复 `allowFrom` + 时序参数,**四个会话级字符串字段全丢**;
|
|
197
|
+
- **QQ / 飞书:连时序参数都没恢复**(`digestIntervalSec` / `approvalTimeoutSec` /
|
|
198
|
+
`sendChunkDelayMs` 只活到下一次重启)。
|
|
199
|
+
现在统一透传 `cwd` / `agentPreset` / `agentProvider` / `agentModel` 与时序参数。
|
|
200
|
+
- **写入侧同步打通**:四个平台的 `setConfig()` 此前**只接受时序参数**,会话级配置根本
|
|
201
|
+
没有入口——已扩展为接受并持久化这四个字段(`...sessionPatch` + `sessionConfigPatch`),
|
|
202
|
+
否则恢复侧无源可读。配置**导入 / 备份恢复**链路一并补齐(此前同样只恢复 `allowFrom`)。
|
|
203
|
+
- **设置页新增的四个输入框参与脏检查**,否则只改这几项时「保存」按钮不激活。
|
|
204
|
+
|
|
205
|
+
### 修复(`/rename` 重启即失效,移植自上游 dsh-bridge)
|
|
206
|
+
|
|
207
|
+
- 此前调用的是 `ctx.sessionPersistence.update()`——**DSH 的持久化服务根本没有这个方法**
|
|
208
|
+
(本机核实:`update` 在该包中出现 0 次),可选链把调用静默跳过,改名只改了内存对象,
|
|
209
|
+
重启与 Web 侧边栏都看不到。现改用 **DSH 原生会话标题服务**
|
|
210
|
+
`ctx.get('sessionTitle').rename(session, title)`(本机核实:服务存在,签名为
|
|
211
|
+
`rename(session, title)`,会追加 `session/title` 事件并落盘)。
|
|
212
|
+
会话尚未在内存中恢复时**明确告知**「需先发一条消息重新挂载」,而不是假报成功。
|
|
213
|
+
|
|
214
|
+
### 修复(DSH 0.1.5-rc.2 适配,移植自上游 dsh-bridge)
|
|
215
|
+
|
|
216
|
+
- **Agent preset 现在会真正挂载(对应上游 issue #40)**:DSH 从 `0.1.5` 起把 agent
|
|
217
|
+
侧的工具、提示词与技能目录整体挪到了 agent preset 后面(宿主 composition 里
|
|
218
|
+
`tool-fs` / `tool-bash` / `tool-jobs` / `tool-skill` 等行被显式禁用),改由每个
|
|
219
|
+
会话在创建时挂载 preset 提供。此前桥只把预设名写进会话 meta、**从未真正挂载**,
|
|
220
|
+
会话因此落在「空预设层」,表现为**远程 Agent 缺少工具**(例如没有读文件的工具),
|
|
221
|
+
且 `dsh-agent-presets` 会打 `published without joining an agent preset` 警告。
|
|
222
|
+
现新增 `_composeAgentPreset()`:经 `ctx.get('agentPresets')` 解析真实 preset id,
|
|
223
|
+
并提供 `agents.create` / `agents.resume` 所需的 **`setup` 回调**
|
|
224
|
+
(`await presets.mount(agentCtx, resolvedId)`)——与 DSH 自身会话控制器的
|
|
225
|
+
`resolve()` + `mount()` 一致。旧版 DSH(无 `agentPresets` 服务)保持原行为不传
|
|
226
|
+
`setup`;配置的 preset 在本机不存在时**回退默认 preset 并在会话创建提示里明示**,
|
|
227
|
+
不再静默降级,也绝不在空层上建会话。
|
|
228
|
+
- **移除硬编码的 `'routing-suite'` 默认 preset**:此前 `createSession` 在未配置
|
|
229
|
+
preset 时硬编码 `agentPreset: 'routing-suite'`,而该 preset **在本机并不存在**
|
|
230
|
+
(实际可用为内置 `standard`/`minimal`/`ptc`/`cordis` 与自定义 `liangshen`),
|
|
231
|
+
等于每个 IM 新会话都指向一个空预设。现改为不设硬编码默认,交给 DSH 默认 preset。
|
|
232
|
+
- **修复已存在会话被误判为新会话(对应上游 issue #39)**:判断会话是否已持久化时
|
|
233
|
+
沿用了旧的数据结构假设。DSH 0.1.5 的 `sessionPersistence.list()` 返回
|
|
234
|
+
`{ header, revision, sizeBytes }` 快照,`entry.id` 恒为空 → 已存在的历史会话被
|
|
235
|
+
误判成新会话 → 走 `agents.create` 建同名会话失败,报
|
|
236
|
+
`session "..." already exists`,随后卡在「当前没有活动会话」。现新增
|
|
237
|
+
`sessionHeaderOf()` 按真实结构取会话头(兼容新旧两种形态),**恢复会话时还会读回
|
|
238
|
+
该会话自身记录的 preset**(与 DSH 会话控制器一致),未记录时才回落到桥配置。
|
|
239
|
+
- **测试**:新增 `test/dsh-015-compat.test.mjs`(10 项),钉住 `sessionHeaderOf`
|
|
240
|
+
的新旧形态兼容、`_composeAgentPreset` 的解析/回退/`setup` 装配语义,以及
|
|
241
|
+
"不再硬编码不存在的默认 preset"这条不变量。
|
|
242
|
+
|
|
243
|
+
> 说明:上游同版本还包含 `lib/connection-compat.js`(DSH 0.1.5 的
|
|
244
|
+
> `connection.rpc.handle` 注册回归垫片)。**本项目无需该垫片** —— 本地早在
|
|
245
|
+
> `[0.1.5]` 就已完成「RPC 通道从 `connection.rpc.handle` 迁到 `webServer` 直注册」,
|
|
246
|
+
> 根本不触发该回归(上游是因为未迁移才需要垫片)。
|
|
247
|
+
|
|
248
|
+
### 新增(远程可用的目录选择器)
|
|
249
|
+
|
|
250
|
+
- **挂载 browse 目录选择器,修复远程/手机端「添加工作区」无入口**:DSH 原生的
|
|
251
|
+
`dsh-host-directory-picker-auto` 按 `ctx.webServer.host` 解析,而 `dsh web`
|
|
252
|
+
的 CLI 层拒绝 `--host 0.0.0.0`(*"Binding all network interfaces is not
|
|
253
|
+
supported"*),故 `bindHost` 恒为 `127.0.0.1`,在 macOS 上**恒解析为
|
|
254
|
+
`native`** —— 只在主机屏幕弹系统对话框,远程浏览器看不到也点不到。
|
|
255
|
+
本插件改为仿 `-auto` 的做法,通过 `ctx.loader.create` **成对挂载**
|
|
256
|
+
`@deepseek-ai/dsh-host-directory-picker-browse`(host 后端)与
|
|
257
|
+
`@deepseek-ai/dsh-client-ui-directory-picker-browse`(浏览器 UI 面),
|
|
258
|
+
使本机与远程统一使用网页版目录树。该组件自带 Miller 双列布局、面包屑、
|
|
259
|
+
可编辑路径、新建文件夹与「显示隐藏文件」,并已做窄屏适配。
|
|
260
|
+
- 新增 `loader` 到插件 `inject`。
|
|
261
|
+
- 两面包必须成对挂载:`dsh-host-directory-picker-browse` 是纯 host 包
|
|
262
|
+
(`package.json` 无 `dsh.client` 字段),其 UI 面只由该 client 包自己的
|
|
263
|
+
`apply` 注册;且 `directoryFlow` 是 `single` 洞口,只取
|
|
264
|
+
`entriesOfSlot[0]`,后注册者被静默丢弃。
|
|
265
|
+
- 需要同时在 profile 的 `cordis.patch.yml` 中禁用原生 `directory-picker`
|
|
266
|
+
行,否则 `-auto` 会抢先注册 `native` 面并赢得单例洞口。
|
|
267
|
+
|
|
268
|
+
### 移除(与 DSH 原生能力重复、且会破坏宿主行为的实现)
|
|
269
|
+
|
|
270
|
+
- **不再抢占原生目录选择 Slot(修复「添加工作区点了没反应」)**:此前插件以
|
|
271
|
+
`priority: -10` 注册 `conversation.hero.workspace.directoryFlow` 与
|
|
272
|
+
`sidebar.workspaces.directoryFlow`,覆盖了 DSH 原生 directory-picker;且
|
|
273
|
+
`inject` 里只尝试 `ctx.workspaces.pickDirectory`(官方服务名是
|
|
274
|
+
`ctx.uiWorkspace.pickDirectory`),拿不到 `pick` 就退化成依赖 bridge RPC 的
|
|
275
|
+
自绘弹窗。这两个洞口的职责本就由原生承担:本机走 `native`,远程走 `browse`
|
|
276
|
+
(网页版目录树)。插件不再注册该 Slot,`RemoteDirectoryFlow` 一并删除。
|
|
277
|
+
- **不再用 capture 阶段 click 拦截劫持「添加工作区 / 打开文件夹」按钮**:此前
|
|
278
|
+
对匹配按钮执行 `preventDefault + stopImmediatePropagation`,会掐断宿主事件链,
|
|
279
|
+
使原生选择器永远无法弹出(远程访问时尤甚)。拦截已整体删除。
|
|
280
|
+
- **移除「远程工作区管理」设置卡片**(`RemoteWorkspaceCard`)及其弹窗与相关死
|
|
281
|
+
CSS:原生工作区面板 + `browse` picker 已覆盖该能力。
|
|
282
|
+
- **不再改写 HTML 响应体**:代理层此前会截取未压缩 HTML、用正则往 `<head>`
|
|
283
|
+
插入 viewport/PWA meta 与 `crypto.randomUUID` polyfill,并手工重算
|
|
284
|
+
`content-length`。现改由 DSH 官方 `webserver/index-inject` 机制贡献结构化注入
|
|
285
|
+
行,由宿主统一渲染与转义;`ProxyServer` 回归纯透明转发,直连与代理两条路径
|
|
286
|
+
看到的页面一致。相关常量 `HTML_HEAD_INJECTIONS` / `INJECT_MARK` /
|
|
287
|
+
`isCompressed` 已删除。
|
|
288
|
+
- **局域网反向代理默认不再自动启动**:局域网可达性交给 DSH 原生
|
|
289
|
+
`dsh web --host 0.0.0.0 --trusted-host <authority>`,避免与原生监听重复。需
|
|
290
|
+
要时可在「远程访问」面板手动开启,或设
|
|
291
|
+
`<DSH_HOME>/dsh-bridge/config.json` 的 `lan.autoStart = true`。
|
|
292
|
+
|
|
293
|
+
### 保留(原生没有的能力)
|
|
294
|
+
|
|
295
|
+
- 公网直连网关(`0.0.0.0:port` + 自签 HTTPS)、Cloudflare / 自建隧道、访问门禁与
|
|
296
|
+
访问日志、多 IM 平台会话桥(微信 / QQ / 飞书 / Telegram)保持不变。
|
|
297
|
+
- 移动端适配本轮**未改动**。
|
|
298
|
+
|
|
299
|
+
### 修复(公网直连网关:外网长连接会永久挂起 + 无法追溯来源)
|
|
300
|
+
- **长连接保活与回收**:WebSocket 在 upgrade 成功后即脱离 HTTP 解析器,`server.timeout` / `keepAliveTimeout` / `headersTimeout` 对它全部失效;此前网关没有任何应用层保活,对端静默消失(进电梯断网、被系统杀进程、NAT 表项过期)的连接会一直挂到内核 TCP 保活兜底(macOS 默认 2 小时)。现在每 15s 巡检一次台账:连续 60s 无任何入向字节先发一个 WebSocket ping 探活,再等 30s 一个字节都收不回来即判定死链并销毁(原因记为 `dead-peer`),真正死掉的连接最长 90s 内回收。
|
|
301
|
+
- **不再误杀活跃连接**:判定只依据「有没有入向字节」,客户端回的 pong 或任何业务帧都会解除探活状态;可选的绝对空闲上限 `GW_WS_MAX_IDLE_MS` 默认关闭。
|
|
302
|
+
- **来源可追溯(新增访问日志)**:新增 `<DSH_HOME>/dsh-bridge/access.log`(JSONL,5MB 滚动保留一份历史),记录每一次 WebSocket 建立/关闭(含**来源 IP、鉴权方式 token/session/loopback、连接时长、收发字节、关闭原因**)、被拒绝的 upgrade、访问密码登录成功/失败、URL Token 免密登录。此前插件完全不记录来访 IP,出事后无法判断连接归属。
|
|
303
|
+
- **真实连接台账**:`activeConnections` 此前只在初始化和 `stop()` 时归零、从未自增,面板「活动连接数」恒为 0;现在随 socket 增减实时维护,并且 `gatewayGetStatus` 额外返回 `wsConnections`(当前长连接数)与 `clients`(按来源 IP 聚合的明细,**仅管理员可见**)。
|
|
304
|
+
- **显式锁定 HTTP 层超时**:`headersTimeout` 60s / `requestTimeout` 300s / `keepAliveTimeout` 5s,保住「握手前慢速攻击」防护,不因存在长连接而放宽。
|
|
305
|
+
|
|
5
306
|
## [0.1.7] - 2026-09-12
|
|
6
307
|
|
|
7
308
|
### 变更(移动端第三批:标题行密度)
|
package/README.md
CHANGED
|
@@ -2,25 +2,105 @@
|
|
|
2
2
|
|
|
3
3
|
English | [简体中文](./README.zh.md)
|
|
4
4
|
|
|
5
|
-
Remote-access plugin for DeepSeek Harness (DSH). Built on top of [dsh-bridge](https://github.com/wenbin-wb/dsh-bridge), it adds a **public direct-connect gateway** so phones and other out-of-network devices can
|
|
5
|
+
Remote-access plugin for DeepSeek Harness (DSH). Built on top of [wenbin-wb/dsh-bridge](https://github.com/wenbin-wb/dsh-bridge), it adds a **public direct-connect gateway** and a **Tailscale tunnel**, so phones and other out-of-network devices can either connect straight to your machine or reach it over a stable Tailscale HTTPS hostname.
|
|
6
6
|
|
|
7
7
|
LAN access, Cloudflare tunnels, custom tunnels, and the IM bots all keep working exactly as before. The plugin is fully inert until you enable the features you want.
|
|
8
8
|
|
|
9
|
+
> **Lineage**: this repository is an enhanced fork of [wenbin-wb/dsh-bridge](https://github.com/wenbin-wb/dsh-bridge) and keeps tracking its fixes. See [Relationship to upstream](#relationship-to-upstream).
|
|
10
|
+
|
|
11
|
+
## Relationship to upstream
|
|
12
|
+
|
|
13
|
+
This project's **upstream is [wenbin-wb/dsh-bridge](https://github.com/wenbin-wb/dsh-bridge)**, which provides all of the plugin's foundational capabilities. This repository adds two things and fixes two classes of problems on top of it.
|
|
14
|
+
|
|
15
|
+
### Who upstream is
|
|
16
|
+
|
|
17
|
+
| | |
|
|
18
|
+
|---|---|
|
|
19
|
+
| Repository | [wenbin-wb/dsh-bridge](https://github.com/wenbin-wb/dsh-bridge) |
|
|
20
|
+
| Role | Multi-channel remote access and security plugin for DSH |
|
|
21
|
+
| Capabilities | LAN QR direct-connect, Cloudflare / self-built tunnels, WeChat / QQ / Feishu / Telegram bots, full-protocol access authentication |
|
|
22
|
+
| License | MIT |
|
|
23
|
+
| Relationship | The fork source for this repository. LAN access, tunnels, IM bots, and security authentication all come from upstream. |
|
|
24
|
+
|
|
25
|
+
**Upstream is the pioneering implementation of this class of plugin**: it made "keep using DSH from your phone" work the easy way — scan into the LAN, tunnel out to the internet, talk straight from an IM app. This plugin stands on its shoulders.
|
|
26
|
+
|
|
27
|
+
### What this repository adds
|
|
28
|
+
|
|
29
|
+
**1. New: public direct-connect gateway (not in upstream)**
|
|
30
|
+
|
|
31
|
+
Upstream solves "access from outside" with **tunnels** (Cloudflare or self-built). Tunnels require a third-party relay, and the free temporary hostname changes on every restart.
|
|
32
|
+
|
|
33
|
+
This plugin adds a **direct gateway**: the computer listens on `0.0.0.0:<port>` with its own self-signed HTTPS certificate and a forced login gate, and outside devices connect **without any third party in the middle**. It fits deployments that have a public IP / port-forwarding, or that simply do not want traffic routed through someone else's infrastructure.
|
|
34
|
+
|
|
35
|
+
**2. New: Tailscale tunnel (not in upstream)**
|
|
36
|
+
|
|
37
|
+
Upstream's tunnels either go through Cloudflare or require your own server. This plugin adds a **Tailscale Serve** channel: reuse the tailnet you already have and publish the local WebUI as `https://<host>.<tailnet>.ts.net`.
|
|
38
|
+
|
|
39
|
+
- **Automatic address detection**: the gateway runs read-only queries against `tailscale status` / `tailscale serve status` to fill in the Serve address; manual editing is also supported.
|
|
40
|
+
- **Read-only — it never changes your Tailscale config**: the plugin will not run `tailscale serve` for you. When Serve is not configured it only offers a copyable command for you to run yourself.
|
|
41
|
+
- TLS is issued automatically by Tailscale — no self-signed certificate and no registered domain needed.
|
|
42
|
+
|
|
43
|
+
**3. Fixed: conflicts with native DSH capabilities (still present upstream)**
|
|
44
|
+
|
|
45
|
+
| Problem | Upstream state | This repository |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| Directory picker | Claims DSH's native `directoryFlow` slots with `priority:-10`, plus a capture-phase click interceptor that hijacks the "Add workspace" button | **Uses the official browse directory picker** (mounts the host backend and browser surface as a pair). No slot claiming, no DOM interception. |
|
|
48
|
+
| Proxy-layer HTML rewriting | Intercepts HTML responses during reverse proxying and does string replacement to inject PWA metas | Uses the official `webserver/index-inject` mechanism so the host renders it; the proxy is a **pure transparent forwarder**. |
|
|
49
|
+
| Directory-picker service name | Calls `ctx.workspaces.pickDirectory` (the official service is `ctx.uiWorkspace`) | No longer depends on that service; directory picking is fully handed back to native. |
|
|
50
|
+
|
|
51
|
+
> Upstream describes the first two as features in its CHANGELOG, but they cause "clicking Add workspace does nothing". This repository fixes them and would welcome upstream adopting the fix.
|
|
52
|
+
|
|
53
|
+
**4. Tracking: continuously porting upstream fixes**
|
|
54
|
+
|
|
55
|
+
Upstream iterates quickly and this repository keeps up. Ported fixes are listed in [CHANGELOG](./CHANGELOG.md), notably:
|
|
56
|
+
|
|
57
|
+
- DSH 0.1.5 adaptation: real agent-preset mounting, already-persisted session misdetection, platform config lost on restart, `/rename` durability;
|
|
58
|
+
- DSH 0.1.7 compatibility check: no change needed (the injected host services `connection` / `webServer` / `sessions` / `agents` / `approval` / `workspaceRegistry` / `sessionPersistence` / `loader`, the `settings.section` slot, and `createUserMessage` from `dsh-llm` all still exist);
|
|
59
|
+
- Tunnel keepalive: WebSocket Ping/Pong answered at the frame layer, cloudflared crash self-healing and autoupdate disabled;
|
|
60
|
+
- Reliability: Telegram proxy broken on Node >= 24, login session persistence.
|
|
61
|
+
|
|
62
|
+
> Both sides evolve, so **feature coverage may lead on either side**. When in doubt, check upstream's README to see which side owns a given capability.
|
|
63
|
+
|
|
64
|
+
### Naming
|
|
65
|
+
|
|
66
|
+
The plugin was renamed from `dsh-bridge` to **`dsh-bridge-gateway`** (reflecting the added gateway capability). Existing users need no manual work — the data directory, configuration, passwords, certificates, and login state all migrate automatically:
|
|
67
|
+
|
|
68
|
+
| Path | Change |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `<DSH_HOME>/dsh-bridge/` | → `<DSH_HOME>/dsh-bridge-gateway/` (migrated automatically at startup) |
|
|
71
|
+
| `~/.dsh-bridge/` | → `~/.dsh-bridge-cloudflared/` (cloudflared cache; migrated automatically, no re-download) |
|
|
72
|
+
| RPC channel `/dsh-bridge` | → `/dsh-bridge-gateway`, with **both channels served**, so stale cached pages keep working |
|
|
73
|
+
|
|
9
74
|
## Features
|
|
10
75
|
|
|
11
|
-
- **Public direct-connect gateway.** The computer listens on `0.0.0.0:<port>` with its own HTTPS self-signed certificate and a forced login gate, so outside devices connect straight to your machine. The link is terminated at the gateway and proxied to the DSH loopback, so plugin pages and RPC channels work unchanged.
|
|
76
|
+
- **Public direct-connect gateway (added here).** The computer listens on `0.0.0.0:<port>` with its own HTTPS self-signed certificate and a forced login gate, so outside devices connect straight to your machine. The link is terminated at the gateway and proxied to the DSH loopback, so plugin pages and RPC channels work unchanged.
|
|
77
|
+
- **Tailscale tunnel (added here).** Reuse your existing tailnet and publish the WebUI at `https://<host>.<tailnet>.ts.net`: address detection is automatic (read-only queries, never modifies your Tailscale config), manual editing is supported, and a scan-to-connect QR code is generated. TLS is issued by Tailscale.
|
|
12
78
|
- **LAN access.** Scan a QR code from the remote-access panel on the same Wi-Fi and you are in.
|
|
13
|
-
- **Public tunnels.** Cloudflare (temporary or fixed hostname) or a self-built tunnel, selectable per need alongside the direct gateway.
|
|
14
|
-
- **Remote workspace.** Browse the host's directory tree and pick workspace conversations from the phone.
|
|
15
|
-
- **Security.** Password gate + QR-based password-free token + admin unlock/lock for sensitive config + rate limiting against brute force. Client-claimed `isLocalhost` is never trusted.
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
79
|
+
- **Public tunnels.** Cloudflare (temporary or fixed hostname) or a self-built tunnel, selectable per need alongside the direct gateway and Tailscale.
|
|
80
|
+
- **Remote workspace.** Browse the host's directory tree and pick workspace conversations from the phone (via DSH's official browse picker).
|
|
81
|
+
- **Security.** Password gate + QR-based password-free token + admin unlock/lock for sensitive config + rate limiting against brute force, plus persisted login state (no re-login after a host restart). Client-claimed `isLocalhost` is never trusted.
|
|
82
|
+
- **Visitor management.** See live who is currently connected (LAN / direct gateway / tunnel, with the real source IP behind a tunnel) plus an IP blacklist. **Viewing is open to every visitor**; disconnecting and blacklisting require admin rights.
|
|
83
|
+
- **IM bots.** WeChat / QQ / Feishu / Telegram bots connect through their own gateway links — no public IP needed. Per-platform workspace / agent preset / model selection is configurable. Guides live in [docs/](./docs).
|
|
84
|
+
- **Collapsible cards.** Cards in the Access and Security tabs collapse; only the first in each group is expanded by default, and a collapsed card keeps its status tag plus one line of key information.
|
|
85
|
+
- **Mobile-friendly web.** The remote web UI is deeply adapted for phone screens (drawer sidebar, ~44px touch targets, bottom-stuck composer, and more).
|
|
18
86
|
- **All configuration in the UI.** Every feature above is configured from DSH Web's remote-access panel; nothing is edited by hand.
|
|
19
87
|
|
|
88
|
+
## Interface
|
|
89
|
+
|
|
90
|
+
The panel has 5 tabs: **Access** / **Visitors** / **IM Bots** / **Security** / **Ops**.
|
|
91
|
+
|
|
92
|
+
| Tab | Contents |
|
|
93
|
+
|---|---|
|
|
94
|
+
| Access | Direct gateway · Cloudflare tunnel · Tailscale tunnel · Custom tunnel · LAN access (only the direct gateway is expanded by default) |
|
|
95
|
+
| Visitors | Live connections (source IPs visible) · persistent blacklist |
|
|
96
|
+
| IM Bots | WeChat / QQ / Feishu / Telegram platform cards |
|
|
97
|
+
| Security | Global control · First line (external access gate) · Second line (admin tamper protection) |
|
|
98
|
+
| Ops | System metrics · network diagnostics · backup/restore · restart DSH |
|
|
99
|
+
|
|
20
100
|
## Requirements
|
|
21
101
|
|
|
22
102
|
- Node `^22.19.0 || >=24.0.0`.
|
|
23
|
-
- DSH with a web profile (built and tested against `0.1.
|
|
103
|
+
- DSH with a web profile (built and tested against `0.1.7-rc.2`, and still compatible with the `0.1.5` line: the host services, client slots and settings entry points it uses exist in both, so no extra adaptation is needed).
|
|
24
104
|
|
|
25
105
|
## Install
|
|
26
106
|
|
|
@@ -45,34 +125,75 @@ Nothing changes on install: the gateway is off by default and the original LAN/t
|
|
|
45
125
|
|
|
46
126
|
No tunnel needed — the computer exposes a port itself and outside devices connect directly.
|
|
47
127
|
|
|
48
|
-
1. Install, then open DSH Web -> Settings -> **
|
|
128
|
+
1. Install, then open DSH Web -> Settings -> **Access** tab.
|
|
49
129
|
2. In the **Direct Gateway** card, set the port (default `7443`) and click save.
|
|
50
130
|
3. Click **Enable Direct Gateway**.
|
|
51
131
|
4. Map that port to this machine on your router / cloud server, then open `https://<public-IP-or-domain>:<port>` from outside.
|
|
52
132
|
|
|
53
133
|
First visit shows a certificate warning because the HTTPS certificate is self-signed — choose "always allow" to continue. External visitors must pass the login gate configured under **Security**; the gate policy is independent of your LAN settings.
|
|
54
134
|
|
|
135
|
+
### Tailscale tunnel
|
|
136
|
+
|
|
137
|
+
For when you already use Tailscale and want a **stable, trusted HTTPS address** (no self-signed certificate, no domain of your own).
|
|
138
|
+
|
|
139
|
+
1. Run `tailscale serve --bg 3082` once on this machine (`3082` is this plugin's reverse-proxy port — substitute your actual port). **You must run this yourself** — the plugin only performs read-only detection and will not modify your Tailscale configuration.
|
|
140
|
+
2. Open Settings -> **Access** -> the **Tailscale Tunnel** card and click **Detect and fill in**.
|
|
141
|
+
3. Once an address is detected, click **Save**. A QR code appears — scan it with your phone and open `https://<host>.<tailnet>.ts.net`.
|
|
142
|
+
|
|
143
|
+
You can also skip detection and click **Edit manually** to type the address. When detection fails the card shows a copyable serve command along with the current Tailscale state (not installed / offline / Serve not configured).
|
|
144
|
+
|
|
145
|
+
> To undo the publish: run `tailscale serve --bg off`.
|
|
146
|
+
|
|
147
|
+
### Visitor management
|
|
148
|
+
|
|
149
|
+
The **Visitors** tab lists the source IPs currently connected to this machine (aggregated across LAN / direct gateway / tunnel, showing the real visitor IP behind a tunnel) and lets you maintain an IP blacklist.
|
|
150
|
+
|
|
151
|
+
- **Viewing is open to every visitor** — "who is connected right now" is a read-only fact, not a management credential.
|
|
152
|
+
- **Disconnect / blacklist / unblacklist require admin rights**; non-admins do not see those buttons.
|
|
153
|
+
- A blacklisted IP is rejected before any page or connection is served (highest priority, and it applies to the real source behind a tunnel too). The plugin **never blacklists automatically**: behind carrier-grade NAT many users share one egress IP, so automatic blacklisting easily hits the wrong people.
|
|
154
|
+
|
|
55
155
|
### LAN access
|
|
56
156
|
|
|
57
157
|
Open the remote-access panel in DSH Web and scan the QR code with the phone on the same Wi-Fi.
|
|
58
158
|
|
|
159
|
+
> LAN reachability is also delegated to DSH's native `--host` / `trustedHosts`. The plugin's own LAN reverse proxy is **off by default** and can be started from the panel when needed.
|
|
160
|
+
|
|
59
161
|
### Public tunnel
|
|
60
162
|
|
|
61
163
|
In the same panel choose Cloudflare (temporary or fixed hostname) or a self-built tunnel. See [docs/custom-tunnel.md](./docs/custom-tunnel.md) for the custom-tunnel protocol.
|
|
62
164
|
|
|
165
|
+
Tunnels are self-healing: cloudflared restarts with exponential backoff after an unexpected exit, and its 24h self-replacing autoupdate is disabled. The custom tunnel answers DSH's WebSocket heartbeats at the frame layer so long-lived connections are not terminated.
|
|
166
|
+
|
|
167
|
+
### Workspace selection
|
|
168
|
+
|
|
169
|
+
"Add workspace" opens DSH's official directory browser (Miller two-column layout, breadcrumb, editable path, new folder, show hidden files) with **identical behavior locally and remotely** — it works on phones too.
|
|
170
|
+
|
|
63
171
|
### Security configuration
|
|
64
172
|
|
|
65
173
|
Settings -> Security: enable protection, set the password, manage the password-free token, and unlock/lock the admin surface. Unauthorized visitors are rejected before any page content is served.
|
|
66
174
|
|
|
175
|
+
Login state is persisted to `<DSH_HOME>/dsh-bridge-gateway/sessions.json` (mode `600`), so already-signed-in devices survive a host restart without re-entering the password. Changing the password, switching mode, or regenerating the token still revokes every existing session.
|
|
176
|
+
|
|
67
177
|
### IM bots
|
|
68
178
|
|
|
69
179
|
WeChat / QQ / Feishu / Telegram guides: [docs/wechat-usage.md](./docs/wechat-usage.md), [docs/qq-usage.md](./docs/qq-usage.md), [docs/feishu-usage.md](./docs/feishu-usage.md), [docs/telegram-usage.md](./docs/telegram-usage.md).
|
|
70
180
|
|
|
181
|
+
Each platform card has an **"Advanced settings"** section where you can configure that platform's remote-session **workspace directory**, **agent preset**, and **provider / model** (leave blank to use DSH defaults). These survive host restarts.
|
|
182
|
+
|
|
183
|
+
### Upgrading from dsh-bridge
|
|
184
|
+
|
|
185
|
+
Nothing manual is required: the data directory, configuration, access password, self-signed certificate, and login state all migrate automatically. Migration is **idempotent** — an existing new directory is never overwritten, and the old directory is never deleted (clean it up yourself whenever you like). If migration hits a problem the plugin still starts, the old data stays where it was, and the log explains what happened.
|
|
186
|
+
|
|
187
|
+
> **As of v0.2.0 the dsh-mobile protocol cabin** (`/ws/mobile` device pairing) **and its bundled Linux one-shot deploy CLI** (`init` / `setup` / `status` / `remove`) **were removed.** Use Tailscale Serve or a Cloudflare tunnel for public access instead. The **web UI's mobile layout adaptation is unaffected** and remains in place.
|
|
188
|
+
|
|
71
189
|
## Security notes
|
|
72
190
|
|
|
73
191
|
- The direct gateway always enforces `public_only`-style gating: external visitors authenticate through the same AuthManager as LAN access, and loopback-only resources stay loopback-only.
|
|
74
192
|
- RPC channels are registered through the host web server and pass request-rejection authentication, so forged or unauthenticated channel calls are dropped at the entry.
|
|
75
193
|
- The self-signed certificate encrypts transport; it does not add identity. Anyone with the password can log in — keep the password strong and enable admin lock.
|
|
194
|
+
- The session file is mode `600`; a missing or corrupt file degrades safely to empty (equivalent to signing in once more).
|
|
195
|
+
- Tailscale detection is **read-only**: it queries state only and never runs `tailscale serve` on the user's behalf.
|
|
196
|
+
- Visitor-management write operations (disconnect / blacklist) are gated by the server-side `checkAdminAuth`, not by a client-claimed identity.
|
|
76
197
|
|
|
77
198
|
## Development
|
|
78
199
|
|
|
@@ -85,6 +206,10 @@ npm test
|
|
|
85
206
|
dsh plugin --profile web add .
|
|
86
207
|
```
|
|
87
208
|
|
|
209
|
+
Tests point `DSH_HOME` at a throwaway temporary directory via `test/isolate-home.mjs`, so they never touch your real environment.
|
|
210
|
+
|
|
211
|
+
This project is forked from [wenbin-wb/dsh-bridge](https://github.com/wenbin-wb/dsh-bridge). Report general issues upstream and direct-gateway issues to this repository.
|
|
212
|
+
|
|
88
213
|
## Release notes
|
|
89
214
|
|
|
90
215
|
Pushing a `v*` tag to GitHub triggers automatic `npm publish` (GitHub Actions; the repository needs an `NPM_TOKEN` secret).
|