@yezack/dsh-remote-mobile 1.9.2 → 1.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ import type { WebRoute } from './api.js';
2
+ /**
3
+ * 将插件路由注册到 DSH WebServer。
4
+ *
5
+ * DSH 0.2.1+ 的 `register(route)` 返回一个撤销函数。这里统一收集这些函数,
6
+ * 并通过 Cordis `ctx.effect` 绑定到插件生命周期,避免插件重复激活时旧路由
7
+ * 残留导致 `duplicate undefined route`。
8
+ */
9
+ export declare function registerWebRoutes(ctx: any, routes: WebRoute[]): void;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 将插件路由注册到 DSH WebServer。
3
+ *
4
+ * DSH 0.2.1+ 的 `register(route)` 返回一个撤销函数。这里统一收集这些函数,
5
+ * 并通过 Cordis `ctx.effect` 绑定到插件生命周期,避免插件重复激活时旧路由
6
+ * 残留导致 `duplicate undefined route`。
7
+ */
8
+ export function registerWebRoutes(ctx, routes) {
9
+ const webServer = ctx?.webServer;
10
+ if (!webServer || typeof webServer.register !== 'function')
11
+ return;
12
+ const registerAll = () => {
13
+ const disposers = [];
14
+ try {
15
+ for (const route of routes) {
16
+ const dispose = webServer.register({
17
+ ...route,
18
+ kind: route.kind ?? 'exact',
19
+ });
20
+ if (typeof dispose === 'function') {
21
+ disposers.push(dispose);
22
+ }
23
+ }
24
+ }
25
+ catch (error) {
26
+ // 中途失败时撤销本轮已经注册的路由,避免留下半套 API。
27
+ for (const dispose of disposers.reverse()) {
28
+ try {
29
+ dispose();
30
+ }
31
+ catch { }
32
+ }
33
+ throw error;
34
+ }
35
+ return () => {
36
+ for (const dispose of disposers.reverse()) {
37
+ try {
38
+ dispose();
39
+ }
40
+ catch { }
41
+ }
42
+ };
43
+ };
44
+ if (typeof ctx.effect === 'function') {
45
+ ctx.effect(registerAll, 'dsh-remote-mobile:web-routes');
46
+ return;
47
+ }
48
+ // 兼容没有 ctx.effect 的旧宿主:至少尝试在 dispose 事件中回收路由。
49
+ const cleanup = registerAll();
50
+ if (typeof ctx.on === 'function') {
51
+ ctx.on('dispose', cleanup);
52
+ }
53
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 在 DSH webserver 校验失败退出后追加一条针对通配地址错误的友好提示。
3
+ * 只在 stderr 命中 DSH 的 wildcard 报错时触发,其他启动错误不输出。
4
+ */
5
+ export declare function installStartupFailureHint(): void;
@@ -0,0 +1,72 @@
1
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { DSH_HOME } from './auth/token.js';
4
+ const WILDCARD_ERROR_RE = /host\s+["']?(?:0\.0\.0\.0|::)["']?\s+is an unspecified \(wildcard\) address|binding every interface would expose remote code execution/i;
5
+ let installed = false;
6
+ let sawWildcardError = false;
7
+ /** 查找配置中 webserver 使用通配地址的 profile patch 文件 */
8
+ function findWildcardConfigFile() {
9
+ const files = [join(DSH_HOME, 'cordis.patch.yml')];
10
+ try {
11
+ for (const entry of readdirSync(join(DSH_HOME, 'profiles'), { withFileTypes: true })) {
12
+ if (!entry.isDirectory())
13
+ continue;
14
+ files.push(join(DSH_HOME, 'profiles', entry.name, 'cordis.patch.yml'));
15
+ }
16
+ }
17
+ catch { }
18
+ for (const file of files) {
19
+ if (!existsSync(file))
20
+ continue;
21
+ let content = '';
22
+ try {
23
+ content = readFileSync(file, 'utf8');
24
+ }
25
+ catch {
26
+ continue;
27
+ }
28
+ const lines = content.split(/\r?\n/);
29
+ for (let i = 0; i < lines.length; i++) {
30
+ if (!/^\s*-\s*id:\s*['"]?webserver['"]?\s*$/.test(lines[i]))
31
+ continue;
32
+ for (let j = i + 1; j < lines.length; j++) {
33
+ if (/^\s*-\s*id:/.test(lines[j]))
34
+ break;
35
+ if (/^\s*host:\s*['"]?(?:0\.0\.0\.0|::)['"]?\s*$/.test(lines[j]))
36
+ return file;
37
+ }
38
+ }
39
+ }
40
+ return null;
41
+ }
42
+ /**
43
+ * 在 DSH webserver 校验失败退出后追加一条针对通配地址错误的友好提示。
44
+ * 只在 stderr 命中 DSH 的 wildcard 报错时触发,其他启动错误不输出。
45
+ */
46
+ export function installStartupFailureHint() {
47
+ if (installed)
48
+ return;
49
+ installed = true;
50
+ const originalWrite = process.stderr.write.bind(process.stderr);
51
+ process.stderr.write = ((chunk, encoding, callback) => {
52
+ try {
53
+ if (WILDCARD_ERROR_RE.test(String(chunk)))
54
+ sawWildcardError = true;
55
+ }
56
+ catch { }
57
+ return originalWrite(chunk, encoding, callback);
58
+ });
59
+ process.once('exit', () => {
60
+ if (!sawWildcardError)
61
+ return;
62
+ const file = findWildcardConfigFile();
63
+ console.error(`
64
+ [dsh-remote-mobile] 启动失败提示:
65
+ 检测到 DSH webserver 使用了通配地址 0.0.0.0 / ::。
66
+ DSH 0.2.1+ 不允许这种监听方式,插件无法在 webserver 校验前接管。
67
+ ${file ? `请删除或注释 ${file} 中的 webserver 配置块;` : '请删除或注释 profile 的 cordis.patch.yml 中的 webserver 配置块;'}
68
+ 保持默认 127.0.0.1:3080,然后重启 dsh web。
69
+ 重启后 dsh-remote-mobile 会自动使用 3081 安全接入代理。
70
+ `);
71
+ });
72
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yezack/dsh-remote-mobile",
3
- "version": "1.9.2",
4
- "description": "DeepSeek Harness 远程与移动端安全网关插件:零修改 DSH 底层代码安全开放局域网与 Tailscale 连接 | DeepSeek Harness (DSH) Remote & Mobile Security Guard: safely opens Tailscale/LAN with zero core modifications, QR scan auth, RSA encryption & brute-force defense.",
3
+ "version": "1.10.2",
4
+ "description": "DeepSeek Harness 远程与移动端安全接入代理插件:零修改 DSH 底层代码安全开放局域网与 Tailscale 连接 | DeepSeek Harness (DSH) Remote & Mobile Secure Access Proxy: safely opens Tailscale/LAN with zero core modifications, QR scan auth, RSA encryption & brute-force defense.",
5
5
  "keywords": [
6
6
  "dsh",
7
7
  "dsh-plugin",
@@ -31,8 +31,8 @@
31
31
  "build:server": "tsc",
32
32
  "build:client": "node scripts/build-client.js",
33
33
  "typecheck": "tsc --noEmit",
34
- "pretest": "node scripts/build-client.js",
35
- "test": "node --test test/**/*.test.js",
34
+ "pretest": "npm run build",
35
+ "test": "node --test",
36
36
  "prepublishOnly": "npm run build && npm test",
37
37
  "release": "node scripts/release.js",
38
38
  "release:patch": "node scripts/release.js patch",