dsh-mesh-access 0.0.0-stage → 1.0.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 +65 -0
- package/INSTALL.en.md +96 -0
- package/INSTALL.md +97 -0
- package/LICENSE +21 -0
- package/README.en.md +115 -0
- package/README.md +131 -2
- package/assets/icon-180.png +0 -0
- package/assets/screenshot-1.png +0 -0
- package/cordis.patch.yml +8 -0
- package/docs/RELEASE.md +63 -0
- package/docs/architecture.md +91 -0
- package/icon.svg +16 -0
- package/lib/client.js +229 -0
- package/lib/index.js +315 -0
- package/package.json +72 -4
- package/screenshots.json +3 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# 更新日志
|
|
2
|
+
|
|
3
|
+
## 1.0.1 — 修「一直重新连接中」
|
|
4
|
+
|
|
5
|
+
`Origin` 没有跟着 `Host` 一起改写,DSH 的 WebSocket 升级被自己的同源校验挡成 **403**,
|
|
6
|
+
于是 HTTP 一切正常(页面能开、API 能通),但实时通道连不上,界面底部一直显示「重新连接中…」。这一版修掉它,并顺手把长连接的 TCP keepalive 打开。
|
|
7
|
+
|
|
8
|
+
- `rewrite()` 在改写 `Host` 的同时改写 `Origin`(`http://<upstream>`);
|
|
9
|
+
- upgrade 路径两个 socket 都 `setNoDelay(true)` + `setKeepAlive(true, 30000)`,让中继上的空闲长连接不容易被悄悄丢掉。
|
|
10
|
+
|
|
11
|
+
## 1.0.0 — 改名(原名 dsh-phone-tunnel)
|
|
12
|
+
|
|
13
|
+
插件和「手机」没关系:它做的是把组网地址上的请求转给本进程。所以改了名,顺便把定位放宽。
|
|
14
|
+
|
|
15
|
+
- **包名 / 仓库名**:`dsh-phone-tunnel` → `dsh-mesh-access`(npm 上的旧名已 `deprecate`,仍可按旧名装到最后那版 0.1.5);
|
|
16
|
+
- 面板文案、「不只手机」章节、npm 描述、市场条目描述都改成「组网里的任何设备」;
|
|
17
|
+
- API 路径 `/api/dsh-phone-tunnel/*` → `/api/dsh-mesh-access/*`;
|
|
18
|
+
- 挂载 id `mesh` → `mesh-access`;
|
|
19
|
+
- 命名空间变了,所以版本从 1.0.0 重新起(不是 0.1.6)。
|
|
20
|
+
|
|
21
|
+
从 0.1.5 升级:先 `dsh plugin --profile web remove dsh-phone-tunnel`,再 `add dsh-mesh-access@1.0.0`,然后重启 `dsh web`。
|
|
22
|
+
|
|
23
|
+
## 0.1.5
|
|
24
|
+
|
|
25
|
+
- **把定位从「手机」放宽到「组网里的任何设备」**:手机、平板、另一台电脑、带图形界面的 NAS(DSM / QTS / TrueNAS Scale)、电视盒子,只要能连到组网 IP 就能打开,对方设备**不需要装任何东西**;
|
|
26
|
+
- 面板文案跟着改:「手机地址」→「访问地址」,说明里写清「组网里的任何设备」;
|
|
27
|
+
- npm 描述与市场条目描述同步放宽,关键词补 `nas` / `remote-desktop`。
|
|
28
|
+
|
|
29
|
+
## 0.1.4
|
|
30
|
+
|
|
31
|
+
- 面板的请求改成**可中止**(`AbortController`):动作请求 20 秒、状态轮询 6 秒,超时就 abort,不会再把界面吊死;
|
|
32
|
+
- 加了**看门狗**:按钮「处理中…」超过 22 秒会自动解除并提示,配合「刷新」按钮自愈;
|
|
33
|
+
- 说明一下:这两个数字是给「进程正忙」留的余量 —— DSH 正在推流生成回答时,同一个 Node 进程处理面板请求会明显变慢,看起来像卡住。
|
|
34
|
+
|
|
35
|
+
## 0.1.3
|
|
36
|
+
|
|
37
|
+
- **修「点第二次就卡死」**:`start()` 原来会 `await server.close()`,而 `close()` 要等所有连接结束 —— DSH 的实时通道是长连接,于是永远等不到。现在同端口重复点击直接返回现状,并发的点击单飞;
|
|
38
|
+
- 关闭改成「先拒绝新连接 → `closeAllConnections()` → 强制销毁自管 socket → 最多等 500ms」,不再阻塞;
|
|
39
|
+
- 面板:反代运行时「③ 回到上面开端口」变「③ 端口已开」并禁用;动作请求加 20 秒超时兜底,不会再永远「处理中…」。
|
|
40
|
+
|
|
41
|
+
## 0.1.2
|
|
42
|
+
|
|
43
|
+
- 面板直接给出**带 token 的完整地址**(借用 `connection` 服务的 `authenticatedUrl()`),复制到手机点开即可,不必再去终端里找 token。
|
|
44
|
+
- 「第一次用星空组网?」改成引导式三步按钮:注册 / 下载客户端 / 回到上面开端口。邀请链接只放在按钮的 `href` 里,不再把 URL 明文印在页面上。
|
|
45
|
+
- `inject` 增加 `connection`;`proxy.urlPlain` 保留不带 token 的裸地址。
|
|
46
|
+
|
|
47
|
+
## 0.1.1 — 2026-11-07
|
|
48
|
+
|
|
49
|
+
文档语言调整,功能与 0.1.0 完全一致。
|
|
50
|
+
|
|
51
|
+
- `README.md`、`INSTALL.md`、`CHANGELOG.md`、`docs/architecture.md` 改为**中文**(这个包主要面向中文用户);
|
|
52
|
+
英文原文保留在 `README.en.md`、`INSTALL.en.md`,两个 README 开头互相跳转。
|
|
53
|
+
- 修掉导航行被反引号包住、在 GitHub 上渲染成代码而点不动的问题。
|
|
54
|
+
- 包的 `files` 白名单显式列出四个文档文件;构建产物 `*.tgz` 不再进仓库。
|
|
55
|
+
|
|
56
|
+
## 0.1.0 — 2026-11-07
|
|
57
|
+
|
|
58
|
+
第一个版本。已发布到 npm:`dsh-mesh-access@0.1.0`。
|
|
59
|
+
|
|
60
|
+
- **宿主半边**:组网网卡探测(`192.168.*` 上的 `StarVPN`、`utun*`、`tap*`、`tun*`、`wg*`、`gpd*`、
|
|
61
|
+
`ppp*`、`ipsec*`)、进程内反代并改写 `Host`、WebSocket / upgrade 转发、
|
|
62
|
+
`GET /api/dsh-mesh-access/status`、`POST /api/dsh-mesh-access/proxy`。
|
|
63
|
+
- **客户端半边**:`sidebar.panellist` + `main` 两个槽位的面板(状态、开/关端口、手机地址与复制、
|
|
64
|
+
首次使用星空组网的提示),每 5 秒轮询一次。
|
|
65
|
+
- **没有构建步骤**:两边都是普通的 ESM / 浏览器 JS,所以发布出去的包就是源码本身。
|
package/INSTALL.en.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Install
|
|
2
|
+
|
|
3
|
+
[English](INSTALL.en.md) | [中文](INSTALL.md)
|
|
4
|
+
|
|
5
|
+
## Requirement
|
|
6
|
+
|
|
7
|
+
- DSH `>= 0.2.1-rc.2` (`dsh --version`)
|
|
8
|
+
- Node `>= 22` (whatever DSH already runs on)
|
|
9
|
+
- An initialized `web` profile (`~/.dsh/profiles/web` exists — DSH creates it on first `dsh web`)
|
|
10
|
+
|
|
11
|
+
## From npm (one line)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
dsh plugin --profile web add dsh-mesh-access
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Current: `dsh-mesh-access@0.1.3` (published 2026-11-07).
|
|
18
|
+
|
|
19
|
+
> If your machine points npm at a China mirror (`registry.npmmirror.com`), the mirror can lag
|
|
20
|
+
> behind or serve a partially synced record, and pnpm then fails with
|
|
21
|
+
> `numeric field was not a number: when getting size for package`. Add the registry explicitly:
|
|
22
|
+
>
|
|
23
|
+
> ```bash
|
|
24
|
+
> dsh plugin --profile web add dsh-mesh-access@0.1.3 --registry=https://registry.npmjs.org
|
|
25
|
+
> ```
|
|
26
|
+
|
|
27
|
+
## From the release tarball (no npm account, any registry)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
dsh plugin --profile web add \
|
|
31
|
+
https://github.com/SilverKurali/dsh-mesh-access/releases/latest/download/dsh-mesh-access.tgz
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## From a local checkout
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
git clone https://github.com/SilverKurali/dsh-mesh-access
|
|
38
|
+
dsh plugin --profile web add link:"$PWD/dsh-mesh-access"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`link:` installs the directory itself; pnpm prints a peer-resolution warning that is expected for
|
|
42
|
+
link installs and harmless here (the plugin has no peer for `@deepseek-ai/dsh` beyond the host).
|
|
43
|
+
|
|
44
|
+
## From a git URL
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
dsh plugin --profile web add git+https://github.com/SilverKurali/dsh-mesh-access.git
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Verify
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# 1. the profile now lists the plugin
|
|
54
|
+
grep -n 'dsh-mesh-access' ~/.dsh/profiles/web/package.json
|
|
55
|
+
|
|
56
|
+
# 2. restart the profile, then ask the host directly
|
|
57
|
+
curl -s http://127.0.0.1:3080/api/dsh-mesh-access/status | head -20
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
A healthy answer looks like:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"ok": true,
|
|
65
|
+
"host": { "upstream": "127.0.0.1", "port": 3080 },
|
|
66
|
+
"mesh": [{ "iface": "StarVPN", "address": "192.168.188.1", "netmask": "255.255.255.0" }],
|
|
67
|
+
"proxy": { "running": false, "port": 3081, "url": null }
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Then open the **组网访问** panel and press *在组网地址上开端口*, or:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
curl -s -X POST -H 'content-type: application/json' \
|
|
75
|
+
-d '{"action":"start","port":3081}' \
|
|
76
|
+
http://127.0.0.1:3080/api/dsh-mesh-access/proxy
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Uninstall
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
dsh plugin --profile web remove dsh-mesh-access
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Nothing is written outside the profile directory and `~/.dsh`; there is no service, no autostart,
|
|
86
|
+
no firewall rule. Stopping the proxy (or restarting the profile) closes the mesh port.
|
|
87
|
+
|
|
88
|
+
## Troubleshooting
|
|
89
|
+
|
|
90
|
+
| Symptom | Cause and fix |
|
|
91
|
+
|---|---|
|
|
92
|
+
| `没找到组网网卡` | The mesh client is not connected, or the interface is named differently. Run `ip -br addr` / `ifconfig` and set `interfacePattern` to match, and check `addressPrefix`. |
|
|
93
|
+
| Panel shows the proxy running but the phone times out | Something between the phone and this host drops it — on Linux check `firewall-cmd --zone=<mesh zone> --list-ports`, on Windows the inbound rule for the port. |
|
|
94
|
+
| Phone gets the page but every API call fails | The page was opened on a stale token/cookie; open a fresh `?token=` URL once. |
|
|
95
|
+
| `listen EADDRINUSE` | Another process owns the port (a previous `dsh web` or the kit's script). Stop it, or start this proxy on a different port. |
|
|
96
|
+
| Port opens, phone sees `401` | Expected: no cookie yet. `?token=` once. |
|
package/INSTALL.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# 安装
|
|
2
|
+
|
|
3
|
+
[中文](INSTALL.md) | [English](INSTALL.en.md)
|
|
4
|
+
|
|
5
|
+
## 前置条件
|
|
6
|
+
|
|
7
|
+
- DSH `>= 0.2.1-rc.2`(`dsh --version`)
|
|
8
|
+
- Node `>= 22`(DSH 本身就在用的那个即可)
|
|
9
|
+
- 已经初始化过 `web` profile(`~/.dsh/profiles/web` 存在 —— 第一次 `dsh web` 时 DSH 会创建)
|
|
10
|
+
|
|
11
|
+
## 一、从 npm 装(一条命令)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
dsh plugin --profile web add dsh-mesh-access
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
最新版:`dsh-mesh-access@0.1.3`(2026-11-07 发布)。
|
|
18
|
+
|
|
19
|
+
> **国内镜像的坑**:如果你的 npm 指向 `registry.npmmirror.com`,镜像可能只同步了一半元数据,
|
|
20
|
+
> pnpm 会报
|
|
21
|
+
> `numeric field was not a number: when getting size for package`。
|
|
22
|
+
> 这时显式给官方源:
|
|
23
|
+
>
|
|
24
|
+
> ```bash
|
|
25
|
+
> dsh plugin --profile web add dsh-mesh-access@0.1.3 --registry=https://registry.npmjs.org
|
|
26
|
+
> ```
|
|
27
|
+
|
|
28
|
+
## 二、从 Release 的 tgz 装(不需要 npm 账号,任何镜像都行)
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
dsh plugin --profile web add \
|
|
32
|
+
https://github.com/SilverKurali/dsh-mesh-access/releases/latest/download/dsh-mesh-access.tgz
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 三、从本地检出装(改代码时用)
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/SilverKurali/dsh-mesh-access
|
|
39
|
+
dsh plugin --profile web add link:"$PWD/dsh-mesh-access"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`link:` 装的是目录本身。pnpm 会打印一条 peer 依赖解析的警告,`link:` 装法必然出现,
|
|
43
|
+
这里无害(本插件对 `@deepseek-ai/dsh` 没有运行时依赖,只是声明了 peer)。
|
|
44
|
+
|
|
45
|
+
## 四、从 git 地址装
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
dsh plugin --profile web add git+https://github.com/SilverKurali/dsh-mesh-access.git
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 验证
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# 1. profile 里已经有这个插件
|
|
55
|
+
grep -n 'dsh-mesh-access' ~/.dsh/profiles/web/package.json
|
|
56
|
+
|
|
57
|
+
# 2. 重启 profile,然后直接问宿主
|
|
58
|
+
curl -s http://127.0.0.1:3080/api/dsh-mesh-access/status | head -20
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
正常的样子:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"ok": true,
|
|
66
|
+
"host": { "upstream": "127.0.0.1", "port": 3080 },
|
|
67
|
+
"mesh": [{ "iface": "StarVPN", "address": "192.168.188.1", "netmask": "255.255.255.0" }],
|
|
68
|
+
"proxy": { "running": false, "port": 3081, "url": null }
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
然后打开**「组网访问」**面板点「在组网地址上开端口」,或者:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
curl -s -X POST -H 'content-type: application/json' \
|
|
76
|
+
-d '{"action":"start","port":3081}' \
|
|
77
|
+
http://127.0.0.1:3080/api/dsh-mesh-access/proxy
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## 卸载
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
dsh plugin --profile web remove dsh-mesh-access
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
除了 profile 目录和 `~/.dsh` 之外什么都没写:没有系统服务、没有开机自启、没有防火墙规则。
|
|
87
|
+
关掉反代(或重启 profile)组网端口就关了。
|
|
88
|
+
|
|
89
|
+
## 故障排查
|
|
90
|
+
|
|
91
|
+
| 现象 | 原因与处理 |
|
|
92
|
+
|---|---|
|
|
93
|
+
| 面板显示「没找到组网网卡」 | 组网客户端没连上,或者网卡名字不在默认正则里。用 `ip -br addr` 或 `ifconfig` 看真实名字,改配置里的 `interfacePattern`,并检查 `addressPrefix`。 |
|
|
94
|
+
| 面板说反代在跑,但手机超时 | 手机到这台机器之间被拦了。Linux 上查 `firewall-cmd --zone=<组网域> --list-ports`,Windows 上查该端口的入站规则。 |
|
|
95
|
+
| 手机能打开页面,但每个接口都失败 | 页面用的是过期 token/cookie。重新点一次带 `?token=` 的地址。 |
|
|
96
|
+
| `listen EADDRINUSE` | 端口被别的进程占了(上一次的 `dsh web`,或者 kit 脚本起的实例)。先停掉它,或换个端口起反代。 |
|
|
97
|
+
| 端口开了,手机看到 `401` | 正常:还没有 cookie。用带 `?token=` 的地址访问一次。 |
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SilverKurali
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# dsh-mesh-access
|
|
2
|
+
|
|
3
|
+
Expose the **running** DeepSeek Harness web UI on a mesh/VPN interface — from inside the server,
|
|
4
|
+
without a second instance, without SSH, without touching the router.
|
|
5
|
+
|
|
6
|
+
[English](#what-it-does) | [中文](README.md)
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
## What it does
|
|
11
|
+
|
|
12
|
+
`dsh web --host` binds exactly one concrete interface address, and it rejects wildcards. So a
|
|
13
|
+
server that serves `127.0.0.1:3080` is invisible to your mesh, and a TAP (`StarVPN`) or `utun`
|
|
14
|
+
interface created *after* boot is not where it listens.
|
|
15
|
+
|
|
16
|
+
This plugin adds a mesh port **inside the running process**:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
any device ──▶ 192.168.188.1:3081 ──▶ (plugin proxy) ──▶ 127.0.0.1:3080 (the same dsh you are using)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
One instance. One session list. No `task-board ledger is already owned by …` warning, no
|
|
23
|
+
half-synced state between two servers.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
dsh plugin --profile web add dsh-mesh-access
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
No npm / behind a China mirror? Use the release tarball:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
dsh plugin --profile web add \
|
|
35
|
+
https://github.com/SilverKurali/dsh-mesh-access/releases/latest/download/dsh-mesh-access.tgz
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
From a checkout: `dsh plugin --profile web add link:/absolute/path/to/dsh-mesh-access`.
|
|
39
|
+
Full matrix (npm / tarball / checkout / git URL), verification and troubleshooting: [INSTALL.en.md](INSTALL.en.md).
|
|
40
|
+
|
|
41
|
+
Restart the profile. A **组网访问 / Mesh access** entry appears in the left rail:
|
|
42
|
+
|
|
43
|
+
| Block | What it shows |
|
|
44
|
+
|---|---|
|
|
45
|
+
| 当前状态 | proxy state, DSH's own listen port, how many mesh interfaces were found |
|
|
46
|
+
| 动作 | open the mesh port / close it / refresh |
|
|
47
|
+
| 手机地址 | the URL to open on the phone, with a copy button |
|
|
48
|
+
| 第一次用星空组网? | the referral sign-up link and the member-account rule |
|
|
49
|
+
|
|
50
|
+
## HTTP surface
|
|
51
|
+
|
|
52
|
+
The panel is a view over two routes kept deliberately small:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
GET /api/dsh-mesh-access/status → { host:{upstream,port}, interfaces[], mesh[], proxy{...}, referral }
|
|
56
|
+
POST /api/dsh-mesh-access/proxy → {action:'start'|'stop', port?}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`curl -s localhost:3090/api/dsh-mesh-access/status` is enough to debug without a browser.
|
|
60
|
+
|
|
61
|
+
## Configuration
|
|
62
|
+
|
|
63
|
+
Optional, written in the profile's patch layer; every key has a default:
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
- id: mesh
|
|
67
|
+
name: dsh-mesh-access
|
|
68
|
+
config:
|
|
69
|
+
interfacePattern: '^(StarVPN|utun\d+|tap\d+|tun\d+|wg\d+|gpd\d+|ppp\d+|ipsec\d+)$'
|
|
70
|
+
addressPrefix: '192.168.'
|
|
71
|
+
proxyPort: 3081
|
|
72
|
+
upstream: 127.0.0.1
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`addressPrefix` exists so a public address on a `tun*` interface is never mistaken for a mesh
|
|
76
|
+
address.
|
|
77
|
+
|
|
78
|
+
## Why the cookie survives
|
|
79
|
+
|
|
80
|
+
A request with `?token=` answers `303` and sets `dsh-auth-*` (`HttpOnly`, `SameSite=Strict`,
|
|
81
|
+
`Max-Age=2592000`). The cookie payload embeds the **authority it was issued for**, signed with a
|
|
82
|
+
secret kept in a persistent credential record — not a per-process random — so it outlives a
|
|
83
|
+
restart. The proxy rewrites `Host` to the upstream authority on every request, which keeps the
|
|
84
|
+
baked-in authority equal to what the server expects, which is why the same cookie keeps working
|
|
85
|
+
through the mesh port.
|
|
86
|
+
|
|
87
|
+
Corollary: change the mesh IP or the port and you must open a fresh token URL once.
|
|
88
|
+
|
|
89
|
+
## Limitations
|
|
90
|
+
|
|
91
|
+
- It cannot move *its own* server's listener; a plugin runs inside a process that is already
|
|
92
|
+
listening. The mesh port is a proxy, not a second bind of the primary server.
|
|
93
|
+
- Only IPv4 mesh addresses are considered.
|
|
94
|
+
- Still requires the phone to reach the mesh address — the tunnel's own quality applies. On
|
|
95
|
+
StarVPN's relay path expect ~0.4 MB/s and occasional multi-second RTT spikes, which can stall a
|
|
96
|
+
long streaming answer; reload resumes it.
|
|
97
|
+
|
|
98
|
+
## Not just phones
|
|
99
|
+
|
|
100
|
+
The name says phone, the mechanism says otherwise: the plugin forwards requests that arrive on a
|
|
101
|
+
mesh address to the port this process already serves. Anything that can open a web page on the
|
|
102
|
+
mesh works — a phone or tablet, another Windows/macOS/Linux laptop, a NAS with a GUI
|
|
103
|
+
(Synology DSM, QNAP QTS, TrueNAS Scale), even a TV stick's browser.
|
|
104
|
+
|
|
105
|
+
Requirements are only: same mesh, a route to the mesh IP, and a browser. **Nothing** has to be
|
|
106
|
+
installed on the other device, and no SSH is involved.
|
|
107
|
+
|
|
108
|
+
## Companion
|
|
109
|
+
|
|
110
|
+
The shell-script edition (Linux/macOS/Windows launchers that start a *second* dsh instance bound
|
|
111
|
+
to the mesh IP, with firewall rules and QR codes) is
|
|
112
|
+
[dsh-mesh-kit](https://github.com/SilverKurali/dsh-mesh-kit). Use this plugin when you want one
|
|
113
|
+
instance; use that kit when DSH is not running yet, or on a machine without a Node plugin install.
|
|
114
|
+
|
|
115
|
+
MIT.
|
package/README.md
CHANGED
|
@@ -1,3 +1,132 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-mesh-access
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
把**正在运行**的 DeepSeek Harness 网页界面挂到组网网卡上 —— 在服务进程内部完成,
|
|
4
|
+
不需要第二个实例,不需要 SSH,也不用碰路由器。
|
|
5
|
+
|
|
6
|
+
[中文](#它做什么) | [English](README.en.md)
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
## 它做什么
|
|
11
|
+
|
|
12
|
+
`dsh web --host` 只接受**一个具体的网卡地址**,并且拒绝通配。所以只监听 `127.0.0.1:3080` 的实例,
|
|
13
|
+
组网里的手机、笔记本、另一台电脑、带图形界面的 NAS 都看不见;而开机之后才出现的 TAP(星空组网的 `StarVPN`)或 `utun` 网卡,也不在它启动时的那一份地址表里。
|
|
14
|
+
|
|
15
|
+
这个插件在**已经在跑的那个进程内部**多开一个组网端口:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
任何设备 ──▶ 192.168.188.1:3081 ──▶(插件反代)──▶ 127.0.0.1:3080(你正在用的这个 dsh)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
一个实例、一份会话列表:不会出现 `task-board ledger is already owned by …`,也不会出现两台服务状态不同步。
|
|
22
|
+
|
|
23
|
+
## 安装
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
dsh plugin --profile web add dsh-mesh-access
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
不方便用 npm,或者你的 npm 指向国内镜像?用 Release 里的 tgz:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
dsh plugin --profile web add \
|
|
33
|
+
https://github.com/SilverKurali/dsh-mesh-access/releases/latest/download/dsh-mesh-access.tgz
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
从本地目录装(改代码的人用这个):
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
dsh plugin --profile web add link:/绝对路径/dsh-mesh-access
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
完整的四种装法、验证步骤、卸载与故障表见 [INSTALL.md](INSTALL.md)。
|
|
43
|
+
|
|
44
|
+
装完重启 profile,左侧导航会多出**「组网访问」**:
|
|
45
|
+
|
|
46
|
+
| 功能块 | 内容 |
|
|
47
|
+
|---|---|
|
|
48
|
+
| 当前状态 | 反代是否在跑、DSH 自己的监听端口、找到几张组网网卡 |
|
|
49
|
+
| 动作 | 在组网地址上开端口 / 关闭 / 刷新 |
|
|
50
|
+
| 访问地址 | 给组网内任何设备打开的地址(带 token,可直接复制) |
|
|
51
|
+
| 第一次用星空组网? | 邀请注册链接 + 成员账号规则 |
|
|
52
|
+
|
|
53
|
+
## HTTP 接口
|
|
54
|
+
|
|
55
|
+
面板只是两个接口的外壳,故意做得极小:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
GET /api/dsh-mesh-access/status → { ok, host:{upstream,port}, interfaces[], mesh[], proxy{...}, referral }
|
|
59
|
+
POST /api/dsh-mesh-access/proxy → { action:'start' | 'stop', port? }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
不开浏览器也能排查:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
curl -s http://127.0.0.1:3080/api/dsh-mesh-access/status
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 配置(可选)
|
|
69
|
+
|
|
70
|
+
写在 profile 的 patch 层里,每一项都有默认值:
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
- id: mesh
|
|
74
|
+
name: dsh-mesh-access
|
|
75
|
+
config:
|
|
76
|
+
interfacePattern: '^(StarVPN|utun\d+|tap\d+|tun\d+|wg\d+|gpd\d+|ppp\d+|ipsec\d+)$'
|
|
77
|
+
addressPrefix: '192.168.'
|
|
78
|
+
proxyPort: 3081
|
|
79
|
+
upstream: 127.0.0.1
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`addressPrefix` 的作用是别把 `tun*` 上的公网地址当成组网地址。
|
|
83
|
+
|
|
84
|
+
## 为什么 cookie 能一直用
|
|
85
|
+
|
|
86
|
+
带 `?token=` 的请求返回 `303` 并种下 `dsh-auth-*`(`HttpOnly`、`SameSite=Strict`、`Max-Age=2592000`,
|
|
87
|
+
即 30 天)。cookie 载荷里写死了**签发时的 authority**:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{"version":1,"authority":"192.168.188.1:3081","issuedAt":…,"expiresAt":…}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
签名密钥存在**持久的凭据记录**里(不是每进程随机),所以重启也在。反代每次请求都把 `Host`
|
|
94
|
+
改写成上游 authority,于是 cookie 里的 authority 与服务端期望的一致 —— 这就是换个端口访问还能继续用
|
|
95
|
+
同一份 cookie 的原因。
|
|
96
|
+
|
|
97
|
+
反过来也成立:**换了组网 IP 或端口,就得重新点一次 token 链接。**
|
|
98
|
+
|
|
99
|
+
## 限制
|
|
100
|
+
|
|
101
|
+
- 插件动不了**自己进程**的监听地址。组网端口是反代,不是把主服务再绑一次。
|
|
102
|
+
- 只考虑 IPv4 组网地址。
|
|
103
|
+
- 仍然依赖隧道本身的质量。星空组网走中继时实测单向约 0.4 MB/s,`[SN-RTT]` 会抖到几秒,
|
|
104
|
+
长回答可能中途停住,刷新一下能接回来。
|
|
105
|
+
- 同一份 `~/.dsh` 只允许一个实例持有 task-board 锁;这是刻意的,本插件就是为避开它而存在。
|
|
106
|
+
|
|
107
|
+
## 不只手机
|
|
108
|
+
|
|
109
|
+
名字叫 phone,但插件本身跟“手机”没有关系 —— 它做的是**把组网地址上的请求转给本进程**。
|
|
110
|
+
组网里任何能打开网页的设备都能用:
|
|
111
|
+
|
|
112
|
+
| 设备 | 用法 |
|
|
113
|
+
|---|---|
|
|
114
|
+
| 手机 / 平板 | 浏览器打开那条带 token 的地址,可加到桌面当 App |
|
|
115
|
+
| 另一台电脑(Win / macOS / Linux) | 同一个地址,直接开;不用装 DSH,也不用装这个插件 |
|
|
116
|
+
| 带图形界面的 NAS(群晖 DSM、威联通 QTS、TrueNAS Scale 等) | NAS 上的浏览器打开即可 |
|
|
117
|
+
| 电视盒子 / 平板上的浏览器 | 同理,只要能连到 `192.168.188.1` |
|
|
118
|
+
|
|
119
|
+
条件是三条:它在**同一个星空组网**里、能路由到组网 IP、浏览器能发 HTTP。
|
|
120
|
+
**不需要**在对方设备上装任何东西,也不需要 SSH。
|
|
121
|
+
|
|
122
|
+
## 配套
|
|
123
|
+
|
|
124
|
+
命令行版(在 DSH 还没启动、或者机器上没有 Node 插件环境时使用:
|
|
125
|
+
起**第二个**实例绑到组网 IP,附带防火墙规则与二维码):
|
|
126
|
+
[dsh-mesh-kit](https://github.com/SilverKurali/dsh-mesh-kit),支持 Linux / macOS / Windows。
|
|
127
|
+
|
|
128
|
+
设计取舍与实测数据见 [docs/architecture.md](docs/architecture.md)。
|
|
129
|
+
|
|
130
|
+
## 许可
|
|
131
|
+
|
|
132
|
+
MIT。
|
|
Binary file
|
|
Binary file
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# dsh-mesh-access — bundle patch
|
|
2
|
+
#
|
|
3
|
+
# Mounts the mesh-access plugin into the web profile. Install with:
|
|
4
|
+
# dsh plugin --profile web add dsh-mesh-access
|
|
5
|
+
# dsh plugin --profile web add link:/absolute/path/to/dsh-mesh-access # local dev
|
|
6
|
+
- insert:
|
|
7
|
+
- id: mesh-access
|
|
8
|
+
name: dsh-mesh-access
|
package/docs/RELEASE.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# 发布清单
|
|
2
|
+
|
|
3
|
+
每改一次代码,下面四块**必须一起动**,否则就会出现「npm 上是新版、Release 还是旧版、文档写着上上个版本」这种漂移。
|
|
4
|
+
|
|
5
|
+
## 1. 版本号
|
|
6
|
+
|
|
7
|
+
- `package.json` 的 `version`
|
|
8
|
+
- `CHANGELOG.md` 顶部加一节
|
|
9
|
+
- `INSTALL.md` / `INSTALL.en.md` 里出现的 `dsh-mesh-access@x.y.z` 命令
|
|
10
|
+
- `README.md` / `README.en.md` 里若有版本号(能不带就不带,用 `dsh-mesh-access` 拉 latest)
|
|
11
|
+
|
|
12
|
+
## 2. npm
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm publish --registry=https://registry.npmjs.org
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
⚠️ `+ dsh-mesh-access@x.y.z` 只代表**元数据**提交成功,tarball 可能还要几分钟才可下载。必须探到 200 才算发完:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
curl -sI https://registry.npmjs.org/dsh-mesh-access/-/dsh-mesh-access-<版本>.tgz | head -1 # 期望 200
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
再看一眼 dist-tags 与 shasum 对不对:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
curl -s https://registry.npmjs.org/dsh-mesh-access | python3 -c "import json,sys;d=json.load(sys.stdin);v=d['versions'][d['dist-tags']['latest']];print(d['dist-tags']['latest'], v['dist']['shasum'])"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
2026-11-07 那次实测:404 持续了约 6 分钟才变 200。这期间 `npm install` 会报 `npm error 404`,看着像发布失败。
|
|
31
|
+
|
|
32
|
+
## 3. GitHub Release
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm pack --registry=https://registry.npmjs.org
|
|
36
|
+
mv dsh-mesh-access-<版本>.tgz dsh-mesh-access.tgz # 附件名不带版本号
|
|
37
|
+
git -c tag.gpgSign=false tag -a v<版本> -m "<摘要>"
|
|
38
|
+
git -c http.version=HTTP/1.1 push origin refs/tags/v<版本>
|
|
39
|
+
gh release create v<版本> dsh-mesh-access.tgz --title "v<版本> — <摘要>" --notes-file /tmp/rel-notes.md
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
附件名**必须不带版本号**,因为文档里用的是稳定地址
|
|
43
|
+
`https://github.com/SilverKurali/dsh-mesh-access/releases/latest/download/dsh-mesh-access.tgz`。
|
|
44
|
+
发完验一次:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
curl -sIL .../releases/latest/download/dsh-mesh-access.tgz | grep -i '^HTTP'
|
|
48
|
+
tar -xzOf dsh-mesh-access.tgz package/package.json | grep version
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 4. 装一次(别信代码,信安装)
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
rm -rf ~/.dsh/profiles/reltest && cp -a ~/.dsh/profiles/headless ~/.dsh/profiles/reltest
|
|
55
|
+
dsh plugin --profile reltest add dsh-mesh-access@<版本> --registry=https://registry.npmjs.org
|
|
56
|
+
# 起临时实例 → GET /api/dsh-mesh-access/status → POST proxy start → curl 期望 401
|
|
57
|
+
rm -rf ~/.dsh/profiles/reltest
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 5. 市场条目
|
|
61
|
+
|
|
62
|
+
`store/dsh-mesh-access.yml` 只在**分类、描述、图标、tarball 地址**变化时才需要改;`npm:` 键由 registry 自动采集,**不要手写**。
|
|
63
|
+
提交给 `awesome-dsh-plugin` 的 PR 由仓库所有者本人开,AI 只备料。
|