dsh-zen-remote 1.1.11 → 1.1.13

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.
Files changed (85) hide show
  1. package/AGENTS.md +24 -0
  2. package/README.md +7 -3
  3. package/README.zh-CN.md +7 -3
  4. package/docs/interface.md +5 -2
  5. package/dsh-push.mjs +25 -1
  6. package/lan-gate.mjs +16 -1
  7. package/lib/client.js +684 -43
  8. package/lib/lan-gate-server.cjs +107 -4
  9. package/lib/types/client/MobileHome.d.ts +7 -2
  10. package/lib/types/client/MobileHome.d.ts.map +1 -1
  11. package/lib/types/client/MobileSessionInfo.d.ts +2 -2
  12. package/lib/types/client/MobileSessionInfo.d.ts.map +1 -1
  13. package/lib/types/client/compat/store.d.ts +62 -0
  14. package/lib/types/client/compat/store.d.ts.map +1 -0
  15. package/lib/types/client/compat/types.d.ts +84 -0
  16. package/lib/types/client/compat/types.d.ts.map +1 -0
  17. package/lib/types/client/debug.d.ts +1 -1
  18. package/lib/types/client/debug.d.ts.map +1 -1
  19. package/lib/types/client/effects/aionui-compat.d.ts +1 -1
  20. package/lib/types/client/effects/aionui-compat.d.ts.map +1 -1
  21. package/lib/types/client/effects/gestures.d.ts +1 -1
  22. package/lib/types/client/effects/gestures.d.ts.map +1 -1
  23. package/lib/types/client/effects/header-status.d.ts +1 -1
  24. package/lib/types/client/effects/header-status.d.ts.map +1 -1
  25. package/lib/types/client/effects/keyboard-avoid.d.ts +1 -1
  26. package/lib/types/client/effects/keyboard-avoid.d.ts.map +1 -1
  27. package/lib/types/client/effects/keyboard-guard.d.ts +1 -1
  28. package/lib/types/client/effects/keyboard-guard.d.ts.map +1 -1
  29. package/lib/types/client/effects/modal-back.d.ts +1 -1
  30. package/lib/types/client/effects/modal-back.d.ts.map +1 -1
  31. package/lib/types/client/effects/model-sheet-extras.d.ts +33 -0
  32. package/lib/types/client/effects/model-sheet-extras.d.ts.map +1 -0
  33. package/lib/types/client/effects/native-trigger-overlay.d.ts +1 -1
  34. package/lib/types/client/effects/native-trigger-overlay.d.ts.map +1 -1
  35. package/lib/types/client/effects/phone-chrome.d.ts +1 -1
  36. package/lib/types/client/effects/phone-chrome.d.ts.map +1 -1
  37. package/lib/types/client/effects/turn-fold.d.ts +1 -1
  38. package/lib/types/client/effects/turn-fold.d.ts.map +1 -1
  39. package/lib/types/client/effects/welcome-notice.d.ts +1 -1
  40. package/lib/types/client/effects/welcome-notice.d.ts.map +1 -1
  41. package/lib/types/client/effects/workbench-ref-close.d.ts +1 -1
  42. package/lib/types/client/effects/workbench-ref-close.d.ts.map +1 -1
  43. package/lib/types/client/index.d.ts +1 -1
  44. package/lib/types/client/index.d.ts.map +1 -1
  45. package/lib/types/client/nav-store.d.ts +2 -2
  46. package/lib/types/client/nav-store.d.ts.map +1 -1
  47. package/lib/types/client/session-dot.d.ts +11 -3
  48. package/lib/types/client/session-dot.d.ts.map +1 -1
  49. package/lib/types/client/styles/compat.css.d.ts +1 -1
  50. package/lib/types/client/styles/compat.css.d.ts.map +1 -1
  51. package/lib/types/client/styles/composer.css.d.ts +1 -1
  52. package/lib/types/client/styles/composer.css.d.ts.map +1 -1
  53. package/lib/types/client/styles/info.css.d.ts +1 -1
  54. package/lib/types/client/styles/info.css.d.ts.map +1 -1
  55. package/package.json +49 -26
  56. package/pwa/app.css +47 -7
  57. package/scripts/check-client-externals.mjs +48 -0
  58. package/scripts/check-compat-store.mjs +113 -0
  59. package/scripts/check-host-shadow.mjs +145 -0
  60. package/scripts/check-info-sheet-stacking.mjs +55 -0
  61. package/src/client/MobileAttachButton.tsx +1 -1
  62. package/src/client/MobileHome.tsx +33 -7
  63. package/src/client/MobileSessionInfo.tsx +6 -4
  64. package/src/client/chips-store.ts +1 -1
  65. package/src/client/compat/store.ts +205 -0
  66. package/src/client/compat/types.ts +115 -0
  67. package/src/client/debug.ts +1 -1
  68. package/src/client/effects/aionui-compat.ts +1 -1
  69. package/src/client/effects/gestures.ts +1 -1
  70. package/src/client/effects/header-status.ts +22 -3
  71. package/src/client/effects/keyboard-avoid.ts +1 -1
  72. package/src/client/effects/keyboard-guard.ts +1 -1
  73. package/src/client/effects/modal-back.ts +1 -1
  74. package/src/client/effects/model-sheet-extras.ts +159 -0
  75. package/src/client/effects/native-trigger-overlay.ts +1 -1
  76. package/src/client/effects/phone-chrome.ts +1 -1
  77. package/src/client/effects/turn-fold.ts +1 -1
  78. package/src/client/effects/welcome-notice.ts +1 -1
  79. package/src/client/effects/workbench-ref-close.ts +1 -1
  80. package/src/client/index.tsx +70 -6
  81. package/src/client/nav-store.ts +2 -2
  82. package/src/client/session-dot.ts +13 -4
  83. package/src/client/styles/compat.css.ts +43 -11
  84. package/src/client/styles/composer.css.ts +184 -9
  85. package/src/client/styles/info.css.ts +21 -0
package/AGENTS.md CHANGED
@@ -40,6 +40,30 @@ release 徽章和 profile 依赖示例由 `scripts/sync-doc-version.mjs` 按 `pa
40
40
  把测试挂掉。文档里的锚点变了就更新那个脚本:找不到标记它直接非零退出,不会
41
41
  默默通过。
42
42
 
43
+ **发版**:全部由 tag 驱动,本地只做一步。
44
+
45
+ ```sh
46
+ npm version patch # 改 package.json + 同步两份 README + 建 commit 和 v* tag
47
+ git push --follow-tags # 推 tag 才是真正的触发器
48
+ ```
49
+
50
+ 推上去之后 `.github/workflows/publish.yml` 依次做:装依赖 → `pnpm build` →
51
+ **`git diff --exit-code -- lib`**(入库产物必须与重新构建的结果一致,挡住
52
+ 「改了 src 忘了 build 就打 tag」)→ `pnpm verify` → `pnpm test` →
53
+ tag 名与 `package.json` 版本一致性 → `pnpm publish`(npm Trusted Publishing,
54
+ OIDC 无令牌,provenance 自动生成)→ **建 GitHub Release**。
55
+
56
+ Release 那步刻意排在 npm 之后:它宣告的是「这个版本已经发出去了」,npm 失败就
57
+ 不该留下一个指向不存在版本的发布页。发布说明用 `--generate-notes` 按上一个 tag
58
+ 以来的提交自动生成,所以**提交信息就是 changelog**,不另外维护文件。这一步可重入
59
+ (先 `gh release view` 查在不在,在就跳过),重跑失败的 workflow 不会因为
60
+ Release 已存在而挂掉。
61
+
62
+ 需要人工配置的只有一处:npmjs.com 的包设置里登记可信发布者
63
+ `KyoMio/dsh-zen-remote` + 文件名 `publish.yml`。GitHub Release 那步用的是
64
+ workflow 自带的 `GITHUB_TOKEN`,不需要额外密钥——但 job 的 `permissions` 里
65
+ `contents` 必须是 `write`(原来是 `read`)。
66
+
43
67
  ## 深度文档
44
68
 
45
69
  | 文件 | 内容 |
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  <p align="center">
5
5
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-0B7285?style=flat-square" alt="MIT"></a>
6
- <img src="https://img.shields.io/badge/release-v1.1.11-5B4CF0?style=flat-square" alt="v1.1.11">
6
+ <img src="https://img.shields.io/badge/release-v1.1.13-5B4CF0?style=flat-square" alt="v1.1.13">
7
7
  <img src="https://img.shields.io/badge/DSH-Web%20Profile-5B4CF0?style=flat-square" alt="DSH Web Profile">
8
8
  </p>
9
9
 
@@ -29,7 +29,11 @@ dsh plugin add dsh-zen-remote
29
29
 
30
30
  Restart `dsh web` afterwards. The mobile UI and the gateway both come up — there is no config line to hand-write.
31
31
 
32
- > Compatibility: developed and tested against DSH `0.1.1-rc.2` (web profile); last verified 2026-08-22.
32
+ > **Compatibility one build, both runtimes.** Verified live on DSH `0.1.1-rc.2` and `0.1.2-rc.1` (web profile), 2026-09-04.
33
+ >
34
+ > 0.1.2 removed `@deepseek-ai/dsh-client-runtime` and put the web UI behind a signed cookie, which would have broken the mobile UI and locked the phone out of the gateway. The plugin carries its own store engine instead of importing that package, probes for the services 0.1.2 moved (`uiWorkspace`, `uiSession`) and falls back to the 0.1.1 shapes when they are absent, reads a turn's events through either `session.events` or `session.snapshotEvents()`, and lets the gateway do the browser-token handshake on the phone's behalf — all of it inert on 0.1.1, where the token endpoint does not exist.
35
+ >
36
+ > One thing to know on 0.1.2: the web UI now rejects requests whose `Host` is not trusted. Reaching DSH through this plugin's gateway is unaffected (the gateway presents every request as `127.0.0.1`), but pointing a browser straight at `http://<LAN-IP>:3080` returns 403 unless you start `dsh web --trusted-host <host>`.
33
37
 
34
38
  To uninstall: `dsh plugin remove dsh-zen-remote` (or delete the two lines from your profile's `dependencies` and `bundles`) and restart `dsh web`. To also wipe the pairing data, delete `~/.dsh/lan-gate-state.json` and `~/.dsh/lan-gate.config.json`.
35
39
 
@@ -41,7 +45,7 @@ Edit `~/.dsh/profiles/web/package.json` by hand — one line under `dependencies
41
45
  ```jsonc
42
46
  {
43
47
  "dependencies": {
44
- "dsh-zen-remote": "^1.1.11" // for local development: "link:/path/to/dsh-zen-remote"
48
+ "dsh-zen-remote": "^1.1.13" // for local development: "link:/path/to/dsh-zen-remote"
45
49
  },
46
50
  "dsh": { "profile": { "bundles": [
47
51
  "@deepseek-ai/dsh-base",
package/README.zh-CN.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  <p align="center">
5
5
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-0B7285?style=flat-square" alt="MIT"></a>
6
- <img src="https://img.shields.io/badge/release-v1.1.11-5B4CF0?style=flat-square" alt="v1.1.11">
6
+ <img src="https://img.shields.io/badge/release-v1.1.13-5B4CF0?style=flat-square" alt="v1.1.13">
7
7
  <img src="https://img.shields.io/badge/DSH-Web%20Profile-5B4CF0?style=flat-square" alt="DSH Web Profile">
8
8
  </p>
9
9
 
@@ -29,7 +29,11 @@ dsh plugin add dsh-zen-remote
29
29
 
30
30
  装完重启 `dsh web`,手机界面与网关一起生效,不需要再手写任何配置行。
31
31
 
32
- > 兼容性:在 DSH `0.1.1-rc.2`(web profile)上开发并实测,最后验证 2026-08-22
32
+ > **兼容性——同一份产物,两个运行时。** DSH `0.1.1-rc.2` 与 `0.1.2-rc.1`(web profile)上均实机验证,2026-09-04
33
+ >
34
+ > 0.1.2 删掉了 `@deepseek-ai/dsh-client-runtime`,并把网页界面挪到签名 cookie 之后——照原样会让手机界面加载失败、手机经网关也进不来。本插件改为自带 store 实现而不 import 那个包;对 0.1.2 搬了家的服务(`uiWorkspace`、`uiSession`)按存在探测、取不到就走 0.1.1 的老路;回合事件 `session.events` 和 `session.snapshotEvents()` 两种读法都认;浏览器 token 交换由网关代手机完成——这些在 0.1.1 上全部惰性,因为那边根本没有 token 端点。
35
+ >
36
+ > 0.1.2 上有一点要知道:网页界面现在会拒绝 `Host` 不受信的请求。经本插件网关访问不受影响(网关把每个请求都以 `127.0.0.1` 的身份递上去),但拿浏览器直接开 `http://<局域网IP>:3080` 会得到 403,除非启动时加 `dsh web --trusted-host <host>`。
33
37
 
34
38
  卸载:`dsh plugin remove dsh-zen-remote`(或从 profile 的 `dependencies` 与 `bundles` 里删掉那两行),重启 `dsh web` 即恢复原状;要清掉配对数据再删 `~/.dsh/lan-gate-state.json` 与 `~/.dsh/lan-gate.config.json`。
35
39
 
@@ -41,7 +45,7 @@ dsh plugin add dsh-zen-remote
41
45
  ```jsonc
42
46
  {
43
47
  "dependencies": {
44
- "dsh-zen-remote": "^1.1.11" // 本地开发换成 "link:/path/to/dsh-zen-remote"
48
+ "dsh-zen-remote": "^1.1.13" // 本地开发换成 "link:/path/to/dsh-zen-remote"
45
49
  },
46
50
  "dsh": { "profile": { "bundles": [
47
51
  "@deepseek-ai/dsh-base",
package/docs/interface.md CHANGED
@@ -119,7 +119,10 @@ composer 最左的回形针打开的是**手机本地**的文件选择器(iOS
119
119
 
120
120
  **修复**
121
121
 
122
- - 适配 dsh-vision-router(2.0.1):composer 识图开关在手机端压成 28px 圆形
122
+ - composer 行里第三方插件的入口(`conversation.input.right`)在手机端整体移进模型弹层,
123
+ 与「模型」「推理等级」并列成行——行是不换行的,模型名是唯一能让宽度的东西,
124
+ 订阅插件的速度 chip(带文字约 70px)加上识图开关会把它挤没(2026-09-06 用户报)
125
+ - 适配 dsh-vision-router(2.0.1 / 2.1.1):composer 识图开关在手机端压成 28px 圆形
123
126
  图标钮(眼睛为 CSS mask 图标,aria-label / aria-pressed 照旧),不再把
124
127
  模型名挤出 composer 行;桌面端保持插件原样(见兼容插件清单)。
125
128
  - 适配 DSH `0.1.1`:活动 chip 点击失效(点了退回信息卡)。三处上游变化同时命中
@@ -248,7 +251,7 @@ composer 最左的回形针打开的是**手机本地**的文件选择器(iOS
248
251
  - [@opendsh/dsh-plugin-scheduled-tasks](https://github.com/Ceelog/dsh-plugins)(定时任务,主屏 chips 行自动收割入口)——本机实测 **0.2.3**
249
252
  - dsh-at-file(@文件引用;它和本插件的附件 chip 读的是同一份草稿 token,手机端会为一个上传文件同时画出缩略图和文件名两条,故隐藏它 `.dsh-uploads/` 下那几行,其余 @ 引用不动;桌面端不隐藏——那边本插件的 chip 是关掉的,它是唯一的呈现)——本机实测 **0.6.7**
250
253
  - [@ace-zone/dsh-market](https://www.npmjs.com/package/@ace-zone/dsh-market)(插件市场;它的弹窗顶栏在手机上放不下,关闭的 × 被挤出面板外,故隐藏标语/版本号/官网链接三个装饰位并让标题省略号收缩,语言切换和 × 保留;认插件自己渲染的 `.dshm-ver[title="@ace-zone/dsh-market"]` 版本片,同名的其他"市场"插件不会被误伤)——本机实测 **0.1.66**
251
- - [dsh-vision-router](https://github.com/ysr666/dsh-vision-router)(视觉桥,composer 行内的识图开关胶囊;手机端压成 28px 圆形图标钮——原胶囊 👁+文字+激活 ✓ 约 90px 定宽,而手机 composer 行不换行、模型座位是唯一可收缩项,会被它挤没;三条文字 span 全部隐藏,改由 CSS mask 画一只 currentColor 眼睛,开合状态仍走插件自己的品牌描边/着色与 `aria-pressed`,可访问名走按钮自带的 aria-label)——本机实测 **2.0.1**
254
+ - [dsh-vision-router](https://github.com/ysr666/dsh-vision-router)(视觉桥,composer 行内的识图开关胶囊;手机端压成 28px 圆形图标钮——原胶囊 👁+文字+激活 ✓ 约 90px 定宽,而手机 composer 行不换行、模型座位是唯一可收缩项,会被它挤没;按钮里只留一只 16px 眼睛:span 一律隐藏;2.1.x 起插件自带 svg 图标,只保留第一个 svg、用兄弟选择器隐掉其后的(当前是激活态的对钩),我们自己的 `::before` mask 眼睛降级为 `:has(svg)` 兜底、只给 2.0.x 的文字写法用;开合状态仍走插件自己的品牌描边/着色与 `aria-pressed`,可访问名走按钮自带的 aria-label)——本机实测 **2.0.1** 与 **2.1.1**。2.1.1 把 👁/✓ 从文字 span 换成 svg,只按 2.0.x 写的规则会渲染出**两只眼**且对钩无人隐藏(2026-09-04 用户报);回归由 `test/vision-router-compat-css.test.cjs` 守着
252
255
  - [dsh-web-ui 全家桶](https://www.npmjs.com/package/@linxin666/dsh-web-ui-all)(文件树 / 预览 / 任务看板 / SSH / 宠物 / 会话统计 / 远程配对 / 设置)——沿用上游兼容规则,本次未扩展
253
256
 
254
257
  ## 安装
package/dsh-push.mjs CHANGED
@@ -180,6 +180,30 @@ export function pendingQuestionText(rawArguments) {
180
180
  } catch { return '' }
181
181
  }
182
182
 
183
+ /**
184
+ * Session event log, read compatibly across both DSH versions: 0.1.1 exposes
185
+ * `session.events` as an array getter; 0.1.2 replaced it with the
186
+ * `session.snapshotEvents()` method. When neither is available, return [] so
187
+ * turnSummary walks its own "not an array → ''" path.
188
+ * @param session - the session object, possibly undefined.
189
+ * @returns the event array.
190
+ */
191
+ export function sessionEvents(session) {
192
+ if (session && Array.isArray(session.events)) return session.events
193
+ if (session && typeof session.snapshotEvents === 'function') {
194
+ // ponytail: snapshotEvents() with no argument copies the WHOLE event log
195
+ // on every call. That is acceptable here — this branch only runs when
196
+ // INCLUDE_SUMMARY is on (dsh-push.mjs:53, off by default) and at most once
197
+ // per turn end. Slicing would need the turn's start seq, and the
198
+ // agent/turn-stopping payload carries none: its emitter (0.1.2
199
+ // dsh-agent-loop) sends only `{ turn, signal }` plus the injected
200
+ // `agent` — a turn NUMBER, not an event-log seq, and the two are not the
201
+ // same thing. If a future payload adds a start seq, pass it as fromSeq.
202
+ return session.snapshotEvents()
203
+ }
204
+ return []
205
+ }
206
+
183
207
  const skip = (reason) => ({ shouldNotify: false, title: '', body: '', reason })
184
208
 
185
209
  /**
@@ -420,7 +444,7 @@ export function apply(ctx) {
420
444
  fire({
421
445
  kind: 'turn-end',
422
446
  delegationDepth: header && header.delegationDepth,
423
- summary: INCLUDE_SUMMARY ? turnSummary(session && session.events, payload && payload.turn) : ''
447
+ summary: INCLUDE_SUMMARY ? turnSummary(sessionEvents(session), payload && payload.turn) : ''
424
448
  })
425
449
  }
426
450
  for (const event of EVENTS) {
package/lan-gate.mjs CHANGED
@@ -9,7 +9,10 @@ import { fileURLToPath } from 'node:url'
9
9
  import { dirname, join } from 'node:path'
10
10
 
11
11
  export const name = 'dsh-zen-remote-gateway'
12
- export const inject = ['subprocess']
12
+ // `connection` (both DSH versions provide it — unlike 0.1.2-only services such
13
+ // as uiWorkspace) lets the host side read the browser-auth token URL, so
14
+ // adding it here does not stop the plugin activating on 0.1.1.
15
+ export const inject = ['subprocess', 'connection']
13
16
 
14
17
  const here = dirname(fileURLToPath(import.meta.url))
15
18
  const serverFile = join(here, 'lib', 'lan-gate-server.cjs')
@@ -39,10 +42,22 @@ export function apply(ctx, config) {
39
42
 
40
43
  const start = async () => {
41
44
  try {
45
+ // 0.1.2 起浏览器要签名 cookie 才能进;token 只有 `GET /?token=` 一处收。
46
+ // authenticatedUrl 是 connection 服务的公开方法,0.1.1 没有它——用它是不是
47
+ // 函数来判断跑在哪一版,比看版本号可靠。只把这个 URL 交给子进程,密钥一律
48
+ // 不传。目标端口与网关子进程的解析一致:显式 env > cordis config > 3080。
49
+ const targetPort = Number(process.env.LAN_GATE_TARGET_PORT || 3080)
50
+ const tokenUrl = ctx.connection && typeof ctx.connection.authenticatedUrl === 'function'
51
+ ? ctx.connection.authenticatedUrl('http://127.0.0.1:' + targetPort)
52
+ : undefined
42
53
  const nodePath = await ctx.subprocess.resolveExecutable('node')
54
+ const env = { ...process.env }
55
+ if (tokenUrl !== undefined) env['LAN_GATE_UPSTREAM_TOKEN_URL'] = tokenUrl
56
+ else delete env['LAN_GATE_UPSTREAM_TOKEN_URL'] // 0.1.1 模式:变量不能残留
43
57
  handle = ctx.subprocess.spawn({
44
58
  argv: [nodePath, serverFile],
45
59
  cwd: here,
60
+ env,
46
61
  stdio: {
47
62
  stdin: 'ignore',
48
63
  stdout: { maxBytes: 131072 },