better-dsh 0.2.5-a → 0.2.5-c

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 (54) hide show
  1. package/THIRD_PARTY_NOTICES.md +49 -0
  2. package/docs/50_test-reports/2026-10-02-better-dsh-schemastery/344/276/235/350/265/226/345/243/260/346/230/216/344/277/256/345/244/215/345/256/236/346/265/213/346/212/245/345/221/212.md +62 -0
  3. package/docs/50_test-reports/2026-10-03-better-dsh-tool-surface-repl-dvc-browser-ast-lsp-4999/345/256/236/346/265/213/346/212/245/345/221/212.md +185 -0
  4. package/docs/50_test-reports/2026-10-03-physical-import/344/270/216llm-completion-ast/347/233/270/345/257/271/350/267/257/345/276/204/344/277/256/345/244/215/345/256/236/346/265/213/346/212/245/345/221/212.md +101 -0
  5. package/docs/50_test-reports/2026-10-03-pnpm11/345/271/264/351/276/204/351/227/250/351/224/201/346/226/207/344/273/266/347/233/270/344/275/215/345/256/236/350/257/201.md +54 -0
  6. package/docs/50_test-reports/2026-10-03-remote-BYO-PTY-label/351/207/215/346/236/204/347/254/254/344/270/200/344/272/272/347/247/260/345/256/236/346/265/213/346/212/245/345/221/212.md +447 -0
  7. package/docs/50_test-reports/2026-10-03-schemastery-cosmokit/346/272/220/347/240/201/345/206/205/350/201/224/345/256/236/346/265/213/346/212/245/345/221/212.md +63 -0
  8. package/docs/50_test-reports/2026-10-03-zero-npm-runtime-deps/345/256/236/346/265/213/346/212/245/345/221/212.md +82 -0
  9. package/docs/specs/remote-pty-label/spec.md +126 -0
  10. package/docs/specs/zero-npm-runtime-deps/spec.md +85 -0
  11. package/lib/BrowserWebSocketTransport-FbPg_VFT.js +39 -0
  12. package/lib/BrowserWebSocketTransport-Q5SUMk7_.js +4 -0
  13. package/lib/LaunchOptions-DlBKKKLC.js +4 -0
  14. package/lib/LaunchOptions-RNO1YIoJ.js +17 -0
  15. package/lib/NodeWebSocketTransport-BdrEPUFe.js +3629 -0
  16. package/lib/NodeWebSocketTransport-DyU2tm5L.js +4 -0
  17. package/lib/bidi-D0POLRy7.js +16106 -0
  18. package/lib/chunk-DQxbwPpZ.js +30 -0
  19. package/lib/compaction/index.d.ts +3 -3
  20. package/lib/compaction/index.js +4 -4
  21. package/lib/failover/index.d.ts +3 -3
  22. package/lib/failover/index.js +4 -4
  23. package/lib/fs-aware/sandbox-plugin.js +20 -20
  24. package/lib/index-DZQNwO7f.d.ts +208 -0
  25. package/lib/index-browser-rqlXyoed.js +21061 -0
  26. package/lib/index.d.ts +4 -4
  27. package/lib/index.js +397 -149
  28. package/lib/kernel-bridge.py +172 -0
  29. package/lib/kernel-env.js +1 -1
  30. package/lib/lib-C8_jXvwA.js +894 -0
  31. package/lib/{lsp-server-registry-946ysdqg.js → lsp-server-registry-C05QLdw0.js} +13 -13
  32. package/lib/{lsp-server-registry-XU6aHSqZ.js → lsp-server-registry-LUjrrLxk.js} +1 -1
  33. package/lib/main-7c69ae95.js +1432 -0
  34. package/lib/main-Dlv0Jm8U.js +3 -0
  35. package/lib/mobile/plugin.d.ts +4 -4
  36. package/lib/mobile/plugin.js +10 -10
  37. package/lib/puppeteer-core-BIIJGWPE.js +1136 -0
  38. package/lib/py-sdk.js +1 -1
  39. package/lib/remote/plugin.d.ts +3 -3
  40. package/lib/remote/plugin.js +148 -53
  41. package/lib/url-schemes/index.d.ts +11 -11
  42. package/lib/url-schemes/index.js +4274 -308
  43. package/lib/util-DgR7HQUl.js +2953 -0
  44. package/lib/web-password.d.ts +7 -7
  45. package/lib/web-password.js +2 -2
  46. package/lib/{web-trust-BO66y72w.js → web-trust-B27wcZEw.js} +2 -2
  47. package/lib/{web-trust-BihHCelF.d.ts → web-trust-CL9P6jVe.d.ts} +7 -7
  48. package/lib/web-trust.d.ts +1 -1
  49. package/lib/web-trust.js +2 -1
  50. package/lib/{wrap-Bzdimp4p.js → wrap-0VA0uFx8.js} +54 -51
  51. package/package.json +9 -21
  52. /package/lib/{kernel-env-hxaihi9C.js → kernel-env-BLPZE-I3.js} +0 -0
  53. /package/lib/{native-capture-x_Hos2e8.js → native-capture-BEByDXYM.js} +0 -0
  54. /package/lib/{py-sdk-Chvy92MB.js → py-sdk-D7klhpWb.js} +0 -0
@@ -0,0 +1,126 @@
1
+ # remote-pty-label Specification
2
+
3
+ ## Purpose
4
+
5
+ `remote` 工具的持久化 BYO-PTY 通道从「`spawn` 命令原文即会话键」改为「`target` 选择器寻址的具名会话」。旧实现的池键就是整条 spawn 文本,于是「同一条命令」与「同一个会话」被绑死:改一个 token 就**静默**新开一个 shell(cwd/env 全丢、零提示),roster 又拿整整一条 shell 命令当会话名。本规格固定新契约——`byop:<label>` 是唯一入口、label 由调用方(模型)自选并因此成为复用入口、`spawn` 降格为该 label 首次创建时的伴生参数——并据此定义创建/复用/冲突三分支、失败面,以及裸名候选表的末位新增项。
6
+
7
+ ## Requirements
8
+
9
+ ### Requirement: 单入口寻址
10
+
11
+ `target` SHALL 是 `remote` 工具的唯一分流入口。以 `byop:` 开头的 target SHALL 被解析为 PTY 选择器,冒号之后的全部内容为 label。`spawn` SHALL 仅在与 `byop:<label>` target 同时出现时合法——它不再是独立入口。
12
+
13
+ #### Scenario: spawn 不能独立成调用
14
+
15
+ - **WHEN** 调用只给 `spawn`(无 target,或 target 不是 `byop:` 选择器)
16
+ - **THEN** 系统返回 `[E_PARAMS]` 并在消息内给出正确形状 `{ target: "byop:<label>", spawn: "…", cmd: "…" }`,且不创建、不触碰任何会话
17
+
18
+ #### Scenario: label 字符集
19
+
20
+ - **WHEN** label 不匹配 `^[A-Za-z0-9][A-Za-z0-9._-]*$`(空 label、含空白、含 `:` 等)
21
+ - **THEN** 系统返回 `[E_BAD_LABEL]` 并在消息内给出合法形状,且不触碰会话池
22
+
23
+ #### Scenario: PTY 选择器锁定 mode
24
+
25
+ - **WHEN** `byop:<label>` target 与显式 `mode:"oneshot"` 同时出现
26
+ - **THEN** 系统返回 `[E_BAD_MODE]`,而不是静默降级或静默改写 mode
27
+
28
+ ### Requirement: label 即复用入口,命名空间按 agent 隔离
29
+
30
+ PTY 会话 SHALL 以 `` `${agentSessionKey}|p:${label}` `` 为池键。`spawn` 原文 SHALL 作为创建来源(origin)随会话保存,仅用于同 label 重复给 spawn 时的比对。label 首次创建之后,仅给 `{ target: "byop:<label>", cmd }` SHALL 复用该会话,cwd/env 存活。
31
+
32
+ #### Scenario: 创建后仅凭 label 复用
33
+
34
+ - **WHEN** 先调用 `{target:"byop:dev3", spawn:"bash", cmd:"cd /tmp && export MARK=live"}`,再调用 `{target:"byop:dev3", cmd:'echo "$MARK" && pwd'}`
35
+ - **THEN** 第二次调用在**同一个** shell 内执行,输出含 `live`、cwd 为 `/tmp`,且结果带 `session: "byop:dev3"`
36
+
37
+ #### Scenario: 两个 agent 的同名 label 互不可见
38
+
39
+ - **WHEN** agent A 创建了 `byop:dev3`,agent B 以同一 label 且不给 spawn 调用
40
+ - **THEN** B 得到 `[E_NO_SESSION]`,A 的会话不受任何影响
41
+
42
+ ### Requirement: 同 label 重复 spawn 的三分支
43
+
44
+ 对已存在的 label 再次给 `spawn` 时,系统 SHALL 将其与创建来源比对:相同即复用并在结果内附教学提示;不同即拒绝,且 SHALL NOT 执行任何命令。
45
+
46
+ #### Scenario: 同一 spawn → 复用 + 提示
47
+
48
+ - **WHEN** 对活着的 `byop:dev3` 再次传入与创建时逐字相同的 `spawn`
49
+ - **THEN** 调用在既有会话内执行,结果的 `notice` 说明该 PTY 是持久化的、后续可省略 `spawn`(只写 label)
50
+
51
+ #### Scenario: 不同 spawn → 冲突,什么都不跑
52
+
53
+ - **WHEN** 对活着的 `byop:dev3` 传入与创建时不同的 `spawn`
54
+ - **THEN** 系统返回 `[E_LABEL_CONFLICT]`,消息内含既有 spawn 原文与「丢掉 spawn 复用它 / 换一个 label 新建」两条出路,**cmd 不执行**,既有会话状态不变
55
+
56
+ ### Requirement: 无会话面 fail-loud 且不保留 spawn
57
+
58
+ label 从未创建、或已被 idle TTL(默认 600s)回收时,不带 `spawn` 的调用 SHALL 返回 `[E_NO_SESSION]`。spawn 命令 SHALL NOT 被跨会话保留(无墓碑、无自动重建)。
59
+
60
+ #### Scenario: 回收后必须重贴 spawn
61
+
62
+ - **WHEN** `byop:dev3` 因 idle TTL 出池后调用 `{target:"byop:dev3", cmd:"echo hi"}`
63
+ - **THEN** 系统返回 `[E_NO_SESSION]`,消息内给出带 `spawn` 的重建形状,并说明 spawn 未被保留
64
+
65
+ ### Requirement: 初始化失败不留任何东西
66
+
67
+ 首建命令未能初始化(`[E_SESSION_DIED]` / `[E_SESSION_START]`)时,该 PTY 会话 SHALL 出池:label 与 TTY SHALL NOT 被建立——这里从来没有过一个可用会话。**已 ready 过**的会话死亡 SHALL 留在池内(那是 reconnect 语义),不受本需求约束。
68
+
69
+ #### Scenario: 失败的 spawn 之后 label 仍然自由
70
+
71
+ - **WHEN** `{target:"byop:x", spawn:"…", cmd:"…"}` 的首建命令未能初始化
72
+ - **THEN** 随后不带 spawn 的复用得到 `[E_NO_SESSION]`(而非重跑冻结的 argv 再失败一次),且带**任意** spawn 的调用 SHALL 正常新建——不得被创建来源比对拦成 `[E_LABEL_CONFLICT]`
73
+
74
+ #### Scenario: 已 ready 的会话死亡仍可重连
75
+
76
+ - **WHEN** 一个曾经初始化成功的 `byop:<label>` shell 死亡后被复用
77
+ - **THEN** 该会话留在池内、按原创建命令重连并附 reconnect 提示
78
+ ### Requirement: 状态面与 roster 以 label 呈现
79
+
80
+ `{target:"byop:<label>"}`(无 cmd)SHALL 只读池内状态、零拨号。roster SHALL 以 `byop:<label>` 命名会话,SHALL NOT 泄露 spawn 命令。
81
+
82
+ #### Scenario: 探测不存在的 label
83
+
84
+ - **WHEN** 对无会话的 `byop:<label>` 做 status 调用
85
+ - **THEN** 返回 `session: none — no live PTY under this label`(不是 transport target 的 "dials on first exec",也不报错)
86
+
87
+ #### Scenario: roster 只给 label
88
+
89
+ - **WHEN** 调用方自己以 `UNIQUESPAWNMARKER bash` 创建了 `byop:dev3`
90
+ - **THEN** roster 包含 `live pty sessions: byop:dev3`,且文本中不出现 `UNIQUESPAWNMARKER`
91
+
92
+ ### Requirement: roster 的活性面按 agent session 作用域
93
+
94
+ 「live pty sessions」段 SHALL 只列调用方 agent session 自己的会话(池键前缀 = 调用方 sessionKey)。`ssh hosts` / `docker containers` / `incus containers` 三段名字候选 SHALL 保持全局可见。理由:名字候选是**静态公共配置**(谁都能 `ssh dev4`),活会话却是**操作环境**——复用别的 agent 留下的 shell 等于继承未知状态,而两个 agent 各开一条线的代价可忽略。BYO-PTY 天然是活的,因此自然落进这个 scope,无需另立规则。
95
+
96
+ #### Scenario: 两个 agent 各自只看见自己的活会话
97
+
98
+ - **WHEN** agent `a1` 建了 `byop:mine`、agent `a2` 建了 `byop:theirs`,随后各自调 `{}`
99
+ - **THEN** `a1` 的 roster 含 `byop:mine` 且不含 `byop:theirs`(反之亦然),而两边 `ssh hosts` / `docker containers` / `incus containers` 三段内容相同
100
+ ### Requirement: 模型面不得泄露池内部键
101
+
102
+ 面向模型的文本(roster、status、错误消息)SHALL 只出现模型面名字(`byop:<label>`、裸 transport target),SHALL NOT 出现池键(`` `${agentSessionKey}|p:<label>` `` / `|t:<target>`)。key→名字 SHALL 是单一定义(`displayKey`),roster 与错误面共用同一条规则。
103
+
104
+ #### Scenario: 初始化失败的错误消息
105
+
106
+ - **WHEN** agent `sess-abc` 的 `byop:x` 初始化失败
107
+ - **THEN** 错误消息含 `'byop:x'`,且不含 `sess-abc`、不含 `|p:`
108
+
109
+ ### Requirement: 裸名候选表末位新增 PTY label
110
+
111
+ 裸名解析顺序 SHALL 保持 ssh config 字面 host → docker → incus **一位不挪**,并在这三段之后、ssh 兜底之前追加「池内活 PTY label」候选。该候选 SHALL 仅在调用面未显式指定 `mode:"oneshot"` 时参与匹配。命中顺序即优先级:同名冲突时更靠前的来源胜出。
112
+
113
+ #### Scenario: 无重名时裸 label 可达(懒复用)
114
+
115
+ - **WHEN** 池内有 `byop:lab`,ssh config / docker / incus 均无 `lab`,且调用 `{target:"lab", cmd:"…"}`(未指定 mode)
116
+ - **THEN** 调用落在该 PTY 会话上(mode 为 `pty`),结果 `session` 为 `"byop:lab"`
117
+
118
+ #### Scenario: 重名时 ssh host 优先
119
+
120
+ - **WHEN** ssh config 存在 `Host lab`,同时池内有 `byop:lab`
121
+ - **THEN** 裸名 `lab` 仍解析为 ssh transport(现有优先级逐位不变);要进 PTY 通道必须显式写 `byop:lab`
122
+
123
+ #### Scenario: 显式 oneshot 不吃 PTY 候选
124
+
125
+ - **WHEN** 调用显式带 `mode:"oneshot"` 且 target 为裸名
126
+ - **THEN** 该调用不走 PTY 末位候选,裸名在 `mode:"oneshot"` 下的旧行为逐位保留
@@ -0,0 +1,85 @@
1
+ # better-dsh 归零 npm 运行时依赖(zero-npm-runtime-deps)
2
+
3
+ 状态:**已完成**(round 1–4 全部落地并实测;发布待 user 放行) | 规格路径:`docs/specs/zero-npm-runtime-deps/spec.md`(仓库根 docs;`better-dsh/docs/` 是 `prebuild` 由 `../docs` 重建的产物,别写那里)
4
+
5
+ ## 0. 动机(实证)
6
+
7
+ 一次 DSH UI Plugin Manager 安装的真实日志:
8
+
9
+ ```
10
+ + better-dsh 0.2.5-a
11
+ Packages: +15 -71
12
+ node_modules/zeromq install$ node ./script/install.js ← 我们的 REPL 原生依赖,cmake 构建
13
+ Done in 3.7s using pnpm v11.7.0
14
+ [WARN] GET …/@oven/bun-linux-aarch64-android/-/…-1.4.2.tgz error (UND_ERR_SOCKET). Will retry in 10 seconds.
15
+ dsh: pnpm printed nothing for 600000ms and was terminated
16
+ ```
17
+
18
+ 1. **卡住的东西不是 better-dsh 的**:`bun@1.4.2`(12 个平台包,各 ~86 MB)来自同一 profile 的 `super-dsh@0.1.9`。DSH 把插件 profile 设计成**一个共享 pnpm 工程**,于是"装插件 X" = "重解整个 profile"(`Packages: +15 -71` 是指纹)。
19
+ 2. **失败形态是超时、不是构建错误**:Plugin Manager 对每条捕获输出的 pnpm 运行挂 600000 ms 静默上限(`packages/boot/plugin-manager/src/index.ts:184`),超时杀树并打印 `operations.ts:453` 那句;真正的错误只是一条被吞掉的 WARN。
20
+ 3. **我们自己也在这条链上**:`zeromq@6.6.0` 是硬 `dependencies`,其 install 脚本用 cmake-ts 现场编译;`src/kernel.ts:20` 是**顶层静态 import**,该原生模块一旦拿不到,**整个插件加载死**(不只是 REPL)。`docs/specs/kernel-provisioning` 的三级自愈只覆盖 Python venv。
21
+
22
+ user 裁决(2026-10-03):**不要 declare 任何运行时依赖,不要 postinstall,不要碰 Node 包管理器**;二进制由我们自己的代码在 spin-up 自检与首次使用 lazy 时自装;装不上就那个组件不可用,其他组件照常。
23
+
24
+ ## 1. 目标态(硬契约)— ✅ 已达成
25
+
26
+ - `package.json`:`dependencies` / `optionalDependencies` / `scripts.postinstall` **全部不存在**;60 条 `peerDependencies` 保留(harness 契约,由 loader 安装域供给)。真实安装器实测 `Packages: +1`(只加自己)。
27
+ - 运行时需要的一切只有两条出路:**构建期进产物**(tsdown `noExternal` 内联;资产随 `files`)或**运行期自装到插件自管目录**(不调用 npm/pnpm/npx)。
28
+ - 供给触发点只剩:**守护进程 spin-up 自检** + **首次使用 lazy**。
29
+ - **fail-open 硬要求**:任何一项失败只让该组件不可用,插件整体加载与其余组件零影响。
30
+
31
+ ## 2. 逐依赖处置与进度
32
+
33
+ | 依赖 | 用户 | 处置 | 状态 |
34
+ |---|---|---|---|
35
+ | `diff` | hashline `edit-diff.js` | tsdown `noExternal` 内联 | ✅ 已内联(`//#region node_modules/diff/...` 实证),已从 `dependencies` 移除 |
36
+ | `xxhash-wasm` | hashline `hash-assign.js` | 同上(wasm 在 esm 构建里已内联为 `Uint8Array`,无外部资产) | ✅ 已内联,已移除 |
37
+ | `file-type` | hashline `file-view.js`(A/B 实测:该调用点在**改动前就已被 tree-shake**,产物里只剩一条裸 `import "file-type"`) | 直接移除;内联与否不影响运行时 | ✅ 已移除 |
38
+ | `use-sync-external-store` | client 半 | client 构建早已 `noExternal` 内联(产物零引用实证) | ✅ 已移除 |
39
+ | `puppeteer-core` | `dvc://browser`(`await import()`) | 构建期内联(`lib/puppeteer-core-*.js`,206 导出);实测拉起系统 Chrome 成功;保留 vendor 自装兜底 | ✅ 已移除声明 |
40
+ | `zeromq` | REPL `src/kernel.ts:20` 静态 import | 删除;传输层改 venv 内 Python bridge | ✅ 已删除(`dependencies` 现只剩 `puppeteer-core`;Node 产物零 zeromq 引用) |
41
+ | `@oh-my-pi/pi-natives-<platform>` | AST(`natives-loader.ts` 已 fail-open) | `ensurePiNatives()` 首用自装到 `<packageRoot>/.vendor`;加载器 vendor 精确查找 + 锚点有界 | ✅ 已移除声明,实测通过 |
42
+
43
+ `scripts.postinstall`:✅ 早已不存在(0.2.2-a 起零 lifecycle script)——本次要消灭的是**依赖带进来的 install script**(zeromq)。
44
+
45
+ ## 3. REPL 传输层重写(round 2 已落地,待 4999 复测)
46
+
47
+ 现状:Node 是 Jupyter 客户端,用 `Dealer`/`Subscriber` 直连 ipykernel 的 shell/iopub/control(`src/kernel.ts:249-344`)。
48
+
49
+ 目标:
50
+
51
+ ```
52
+ Node(TS) ──stdio JSON lines──► bridge.py(venv python + pyzmq/jupyter_client)──ZMQ──► ipykernel
53
+ ```
54
+
55
+ - `pyzmq` / `jupyter_client` 由 `ipykernel` 传递依赖保证在场(实证:`.venv-kernel/lib/python3.11/site-packages/zmq`)。
56
+ - 保留语义:cell 串行、iopub 流式、control 中断 + SIGALRM 升级、超时/dispose 时序、comm(host 请求桥)。
57
+ - Node 侧不得再出现 zeromq/原生 socket;venv 未就绪时 `dashr-repl` 行**不抛**,标为未就绪。
58
+
59
+ ## 4. 供给阶梯(改造后)
60
+
61
+ 1. **守护进程 spin-up 自检**(主路径;`resolveKernelEnv` 已有,新增 bridge 可启动检查)。
62
+ 2. **首次使用 lazy**(兜底)。
63
+ 3. ~~postinstall~~ 不存在。
64
+
65
+ ## 5. 验收(对齐"改动点同类"红线)
66
+
67
+ - 清单零 `dependencies`/`optionalDependencies`/`postinstall`;`npm pack` 后 tarball 自足。
68
+ - 断网/无 npm:`dsh plugin add <tarball>` 成功、插件加载成功、REPL 由 venv 自足跑通一次真实 `eval`。
69
+ - 无 zeromq 时:插件加载正常、其余组件全可用、只有 REPL 在缺 venv 时明确报"未就绪"。
70
+ - 4999 第一人称实测 + 报告落 `docs/50_test-reports/`。
71
+ - 发布仍需 user 明确放行。
72
+
73
+ ## 6. 已知风险
74
+
75
+ - `puppeteer-core` 打包兼容性(动态 require / `import.meta`)未验。
76
+ - Python bridge 多一跳后的中断/流式时序(SIGALRM 升级路径最敏感)。
77
+ - 自装代码自担:版本钉死、sha512 校验、平台探测、并发去重、代理/离线行为。
78
+
79
+ ## 7. 进展日志
80
+
81
+ - **round 1(2026-10-03)**:`tsdown.config.ts` 增 `BUNDLED_RUNTIME` + `noExternal`(含 `file-type` 的纯 JS 闭包:`@borewit/text-codec`/`@tokenizer/*`/`debug`/`ieee754`/`strtok3`/`token-types`/`uint8array-extras`);`package.json` 的 `dependencies` 从 6 项降到 2 项(只剩 `puppeteer-core`、`zeromq`)。验证:`npm run build` 通过;产物内联 `diff` + `xxhash-wasm`,除 `puppeteer-core`/`zeromq` 外无任何外部包引用;全量单测 **649 passed / 1 skipped**。
82
+ - 顺手发现:`better-dsh/docs/` 是构建产物(`scripts/copy-docs.mjs` 会 `rmSync + cpSync ../docs`),规格必须写在仓库根 `docs/`。
83
+ - **round 4(2026-10-03)**:清单归零并接线自装器。`vendor.ts` 增 `VENDOR_ROOT = <packageRoot>/.vendor`;`natives-loader` 增 `ensurePiNatives()`(首用 lazy;显式 anchor 永不 provision)+ vendor 精确查找;`ast-device` 两处调用点 `await nativesOrThrow()`;`browser-device.loadPuppeteer()` 失败时走 vendor。删 `dependencies` + `optionalDependencies`(顺带修掉 round 2 我引入的重复 `puppeteer-core` 键)。实测:**`Packages: +1`**;puppeteer 内联后成功 `launch()` 系统 Chrome(`BUNDLED_PUPPETEER_OK`);`dsh headless` 让 agent 走 `dvc://ast_grep` → `totalMatches 3 / files 2 / searched 189`,同时 `<plugin>/.vendor` 出现 314MB pi-natives,而 **profile 树里无 `@oh-my-pi`**(证明自装生效);全量单测 **649 passed / 1 skipped**。报告:`docs/50_test-reports/2026-10-03-zero-npm-runtime-deps实测报告.md`。
84
+ - **round 3(2026-10-03)**:① 新传输最后一条未验路径打通:`/tmp/smoke-comm.mts` 走通 host-request comm(内核 `create_comm('dashr.host')` → iopub → Node → **control** 通道 `comm_msg` 回包 → 内核 control-thread handler 解 future),cell 打印 `HOSTREPLY {'pong': True, ...}`。② **4999 第一人称实测**:新 tarball 装进 rig 的 web + headless profile(物理树里 **`zeromq` 已消失**),`dsh headless` 真实 session:`eval` 跑出 `REPL_OK 42`,同一 Python session 内 `await tool.bash({...})` 拿回 `HOST_OK\n`(宿主参数校验失败路径也顺带被触发),启动日志零错误、composed tree 含 `dashr-repl`。③ 新增 `src/vendor.ts`——零依赖自装器(registry packument → sha512/shasum 校验 → 自写 tar+pax 解包 → `createRequire` 解析入口 → 递归依赖闭包 + os/cpu/libc 平台过滤 → staging+rename 原子落盘)。实测:provision `@oh-my-pi/pi-natives-linux-x64@18.0.6`(2×163MB `.node`)并 **dlopen 成功**(exports `astGrep`/`astEdit`/…),二次调用 reuse。④ 注意:`better-dsh-0.2.5*.tgz` 是**入库的** rig 交付物,本轮为实测重新打包覆盖。
85
+ - **round 2(2026-10-03)**:REPL 传输层换成 venv 内 Python bridge。新增 `src/kernel-bridge.py`(stdio JSON lines ↔ pyzmq/jupyter_client;`default=str` 序列化,避免 header 的 `datetime` 打死 pump 线程)与 `src/kernel-transport.ts`(spawn/readline/`waitFor`/`onExit`);`kernel.ts` 删掉 `import { Dealer, Subscriber } from 'zeromq'`、`DELIM`/`encode`/`decode`/socket 字段/iopub pump,改由 `handleTransportMessage` 路由。`scripts/copy-kernel-bridge.mjs` 把 .py 拷进 `lib/`(`files: [lib]` 覆盖),`build` 链尾追加。验证:`tsc --noEmit` 0 错;全量产物零 `zeromq`;真实 ipykernel 冒烟通过(kernel_info ✅ / `print("SMOKE",6*7)` → `"SMOKE 42\n"` ✅ / 到 idle ✅ / **KERNEL_BOOTSTRAP 单元执行无错** ✅ / interrupt ✅ / dispose ✅)。⚠ 尚未经新传输验证的路径:host-request comm(内核侧 `dashr.host` → Node `serveHostRequest` → control 通道 comm_msg 回包),留给 4999 实测。
@@ -0,0 +1,39 @@
1
+ import { o as debugCatchError } from "./util-DgR7HQUl.js";
2
+
3
+ //#region node_modules/puppeteer-core/lib/puppeteer/common/BrowserWebSocketTransport.js
4
+ /**
5
+ * @internal
6
+ */
7
+ var BrowserWebSocketTransport = class BrowserWebSocketTransport {
8
+ static create(url) {
9
+ return new Promise((resolve, reject) => {
10
+ const ws = new WebSocket(url);
11
+ ws.addEventListener("open", () => {
12
+ return resolve(new BrowserWebSocketTransport(ws));
13
+ });
14
+ ws.addEventListener("error", reject);
15
+ });
16
+ }
17
+ #ws;
18
+ onmessage;
19
+ onclose;
20
+ constructor(ws) {
21
+ this.#ws = ws;
22
+ this.#ws.addEventListener("message", (event) => {
23
+ if (this.onmessage) this.onmessage.call(null, event.data);
24
+ });
25
+ this.#ws.addEventListener("close", () => {
26
+ if (this.onclose) this.onclose.call(null);
27
+ });
28
+ this.#ws.addEventListener("error", debugCatchError);
29
+ }
30
+ send(message) {
31
+ this.#ws.send(message);
32
+ }
33
+ close() {
34
+ this.#ws.close();
35
+ }
36
+ };
37
+
38
+ //#endregion
39
+ export { BrowserWebSocketTransport as t };
@@ -0,0 +1,4 @@
1
+ import "./util-DgR7HQUl.js";
2
+ import { t as BrowserWebSocketTransport } from "./BrowserWebSocketTransport-FbPg_VFT.js";
3
+
4
+ export { BrowserWebSocketTransport };
@@ -0,0 +1,4 @@
1
+ import "./main-7c69ae95.js";
2
+ import { t as convertPuppeteerChannelToBrowsersChannel } from "./LaunchOptions-RNO1YIoJ.js";
3
+
4
+ export { convertPuppeteerChannelToBrowsersChannel };
@@ -0,0 +1,17 @@
1
+ import { b as ChromeReleaseChannel } from "./main-7c69ae95.js";
2
+
3
+ //#region node_modules/puppeteer-core/lib/puppeteer/node/LaunchOptions.js
4
+ /**
5
+ * @internal
6
+ */
7
+ function convertPuppeteerChannelToBrowsersChannel(channel) {
8
+ switch (channel) {
9
+ case "chrome": return ChromeReleaseChannel.STABLE;
10
+ case "chrome-dev": return ChromeReleaseChannel.DEV;
11
+ case "chrome-beta": return ChromeReleaseChannel.BETA;
12
+ case "chrome-canary": return ChromeReleaseChannel.CANARY;
13
+ }
14
+ }
15
+
16
+ //#endregion
17
+ export { convertPuppeteerChannelToBrowsersChannel as t };