dsh-pocket 1.7.3 → 1.7.5

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.en.md CHANGED
@@ -91,6 +91,11 @@ On the same page click "**Enable anywhere**" → wait for the tunnel (first run
91
91
  - LAN mode exposes nothing publicly; only devices on the same network can reach it.
92
92
  - Built for personal use; the PIN lives in `$DSH_HOME/dsh-pocket/token` and is re-rolled by restarting the public tunnel.
93
93
 
94
+ ## 💻 DSH Desktop
95
+
96
+ - In the desktop app, **QR screen-mirroring works**; **update/restart are managed by the desktop app** (auto-disabled here).
97
+ - ⚠️ The desktop **advanced mode** doesn't support phone access yet (it disables the web layout; the phone gets no layout service → blank screen). Switch back to **compatibility** mode and restart; phones opening an advanced-mode page will see a clear notice overlay.
98
+
94
99
  ## 🩹 Troubleshooting (traps users step on)
95
100
 
96
101
  | Symptom | Cause & fix |
package/README.md CHANGED
@@ -91,6 +91,11 @@ npx @deepseek-ai/dsh web
91
91
  - 局域网模式不暴露公网,只有同一网络内的设备能访问
92
92
  - 适合个人自用;密码存本机 `$DSH_HOME/dsh-pocket/token`,可随时重开公网换新
93
93
 
94
+ ## 💻 DSH Desktop(桌面版)
95
+
96
+ - 桌面版里 dsh-pocket 的**扫码同屏**正常可用;**更新/重启由桌面版管理**(插件内这两项自动停用)
97
+ - ⚠️ 桌面端 **advanced 模式**暂不支持手机访问(该模式禁用网页布局、手机拿不到 layout 服务,会白屏)——请切回 **compatibility** 模式后重启;advanced 模式下手机打开会看到明确的提示层
98
+
94
99
  ## 🩹 常见问题(别踩的坑)
95
100
 
96
101
  | 现象 | 原因与解决 |
package/lib/index.js CHANGED
@@ -19,7 +19,7 @@ import { homedir } from 'node:os';
19
19
  import { createPocketService } from './service.mjs';
20
20
  import { installPocketRpc } from './web-rpc.js';
21
21
  import { restartHost } from './restart.js';
22
- import { desktopEnvPatchScript, DEFAULT_INJECT } from './proxy.mjs';
22
+ import { desktopEnvPatchScript, advancedNoticeScript, DEFAULT_INJECT } from './proxy.mjs';
23
23
 
24
24
  const name = 'dsh-pocket';
25
25
  const inject = ['connection', 'webServer'];
@@ -152,6 +152,19 @@ export function apply(ctx, config = {}, internals = {}) {
152
152
  logger.info('dsh-pocket: DSH Desktop detected — update/restart disabled here | 检测到桌面端环境,更新/重启已关闭');
153
153
  }
154
154
 
155
+ // 桌面端 advanced 模式检测(issue #19):dsh-plugin-desktop 的 mode 配置存在
156
+ // $DSH_HOME/settings.yaml 的 dsh-plugin-desktop 命名空间下。advanced 组合禁用网页版
157
+ // ui-layout、手机页面又拿不到桌面 layout → 手机访问白屏,这里注入覆盖层提示用户切回。
158
+ const desktopAdvanced = isDesktop && (() => {
159
+ try {
160
+ const raw = readFileSync(join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'settings.yaml'), 'utf8');
161
+ return /dsh-plugin-desktop\s*:\s*[\s\S]{0,300}?mode\s*:\s*advanced/i.test(raw);
162
+ } catch { return false; }
163
+ })();
164
+ if (desktopAdvanced) {
165
+ logger.warn('dsh-pocket: DSH Desktop advanced mode — phone access unsupported, injecting notice | 桌面端 advanced 模式:手机访问暂不支持,已注入提示');
166
+ }
167
+
155
168
  const service = internals.service ?? createPocketService({
156
169
  dshPort,
157
170
  port: internals.port ?? config.port ?? 3081,
@@ -160,7 +173,10 @@ export function apply(ctx, config = {}, internals = {}) {
160
173
  // 桌面端:手机扫码访问的页面缺 dsh-desktop-mode/platform 参数会让 dsh-plugin-desktop
161
174
  // client 崩溃(issue #3/#4)。给代理注入「桌面参数补丁」(history.replaceState 补齐参数,
162
175
  // 无跳转;compatibility 模式不套桌面布局,避免移动端按钮叠加)。保留默认 polyfill。
163
- injectHtml: isDesktop ? DEFAULT_INJECT + desktopEnvPatchScript(process.platform) : undefined,
176
+ // advanced 模式再加警告覆盖层(issue #19)。
177
+ injectHtml: isDesktop
178
+ ? DEFAULT_INJECT + desktopEnvPatchScript(process.platform) + (desktopAdvanced ? advancedNoticeScript() : '')
179
+ : undefined,
164
180
  // 公网访问密码(issue #13):仅公网隧道 Host 强制;局域网免密码
165
181
  auth: {
166
182
  getToken: () => getAccessToken(),
package/lib/proxy.mjs CHANGED
@@ -52,6 +52,16 @@ function isCompressed(headers) {
52
52
  /** 默认注入到经代理的 HTML 文档里:crypto.randomUUID polyfill(非安全上下文必需)。 */
53
53
  export const DEFAULT_INJECT = RANDOM_UUID_POLYFILL;
54
54
 
55
+ /**
56
+ * DSH Desktop advanced 模式不支持的提示覆盖层(issue #19)。
57
+ * advanced 组合会禁用网页版 ui-layout,而桌面 layout 只在 advanced client 提供——
58
+ * 手机页面被注入 compatibility 后无任何 layout 服务 → 启动白屏(Failed to load plugins)。
59
+ * 该脚本在页面上叠加一个固定警告层,让用户明确知道原因(而不是无解白屏)。
60
+ */
61
+ export function advancedNoticeScript() {
62
+ return `<script data-dsh-pocket-advanced-notice="1">!function(){try{var d=document.createElement('div');d.style.cssText='position:fixed;inset:0;z-index:2147483647;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.55);color:#fff;font:15px/1.7 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;text-align:center;padding:24px';d.textContent='DSH 桌面端处于 advanced 模式,手机访问暂不支持。请在桌面端设置中切回 compatibility 模式后重启。| DSH Desktop is in advanced mode — phone access is not supported yet. Switch back to compatibility in the desktop app and restart.';document.documentElement.appendChild(d);}catch(e){}}();</script>`;
63
+ }
64
+
55
65
  // ---------- 可选访问令牌认证(issue #13) ----------
56
66
  // 只对公网隧道 Host(trycloudflare.com)强制;局域网免密码。
57
67
  // 登录成功后种 HttpOnly 会话 cookie(浏览器关闭失效)→ SPA 内部 API/WS 自动携带。
package/lib/tunnel.mjs CHANGED
@@ -4,7 +4,7 @@
4
4
  // 无密码模式:URL 即钥匙(dsh web 能执行代码,请勿把二维码/URL 发给别人)。
5
5
 
6
6
  import { spawn, execSync } from 'node:child_process';
7
- import { mkdir, access, chmod, rm, stat, rename, cp } from 'node:fs/promises';
7
+ import { mkdir, access, chmod, rm, stat, rename, cp, open } from 'node:fs/promises';
8
8
  import { homedir } from 'node:os';
9
9
  import { join, dirname } from 'node:path';
10
10
  import { pipeline } from 'node:stream/promises';
@@ -146,13 +146,15 @@ export async function downloadFile(url, dest, { signal, segments = PARALLEL_SEGM
146
146
 
147
147
  /**
148
148
  * 清华 TUNA 镜像的 cloudflared Homebrew bottle URL(国内 CDN,实测 ~3MB/s)。
149
- * 只覆盖 macOS/Linux(Windows Homebrew bottle,返回 null)。
149
+ * **仅 macOS**——Linux Homebrew bottle 其 ELF 解释器是 `@@HOMEBREW_PREFIX@@`
150
+ * 占位符(需 brew install 时 patchelf 替换),没装 Homebrew 的机器直接 spawn 会
151
+ * ENOENT(issue #22);Linux 走官方 GitHub tgz(解压即用)+ 加速源。
150
152
  * 匹配按 CPU 架构取清华目录里版本号最新的 bottle——Homebrew 构建时部署目标
151
153
  * 设得较老、向后兼容,所以旧系统(如 Ventura)也能用新一点的 bottle。
152
154
  * 抓目录失败/无匹配 → null(调用方回退 GitHub/加速源,不影响可用性)。
153
155
  */
154
156
  async function tsinghuaBottleUrl({ os, a }) {
155
- if (os !== 'darwin' && os !== 'linux') return null;
157
+ if (os !== 'darwin') return null;
156
158
  let res;
157
159
  try {
158
160
  res = await fetch(TUNA_BOTTLES, { signal: AbortSignal.timeout(20_000) });
@@ -303,6 +305,22 @@ export async function resolveCloudflared({ home, onPhase = () => {}, signal } =
303
305
  for (const bin of candidates) {
304
306
  try {
305
307
  await access(bin);
308
+ // Linux:识别并丢弃 Homebrew bottle 坏缓存(issue #22)——其 ELF 解释器是
309
+ // @@HOMEBREW_PREFIX@@ 占位符,直接 spawn 报 ENOENT;读文件头(解释器路径在
310
+ // ELF 头部附近)即可识别,命中则删掉走重新下载
311
+ if (os === 'linux') {
312
+ try {
313
+ const fd = await open(bin, 'r');
314
+ const head = Buffer.alloc(8192);
315
+ await fd.read(head, 0, 8192, 0);
316
+ await fd.close();
317
+ if (head.includes('@@HOMEBREW_PREFIX@@')) {
318
+ await rm(bin, { force: true }).catch(() => {});
319
+ console.warn('dsh-pocket: discarding unusable Homebrew-bottle cloudflared cache | 丢弃不可用的 Homebrew bottle 缓存,重新下载');
320
+ continue;
321
+ }
322
+ } catch { /* 读失败按正常缓存处理 */ }
323
+ }
306
324
  return bin; // 缓存命中,秒开
307
325
  } catch { /* 继续找下一个 */ }
308
326
  }
package/package.json CHANGED
@@ -76,5 +76,5 @@
76
76
  "access": "public",
77
77
  "registry": "https://registry.npmjs.org/"
78
78
  },
79
- "version": "1.7.3"
79
+ "version": "1.7.5"
80
80
  }