# v0.0 探针实测报告 > 目的:设计文档把「沙箱 host 半边的能力边界」列为**风险 1**——它的结论决定 v0.1/v0.2 的估时与设计是否成立。 > 本报告全部是**实测结果**,不是推断。实测环境:DSH 0.2.0-rc.2,Node v24.18.1,darwin/arm64。 --- ## 一、四个问题的答案 | 探针问题 | 结论 | 实测证据 | |---|---|---| | **1. 能不能跑子进程(git)?** | ✅ **可以** | `git version 2.39.5 (Apple Git-154)`;`which git` → `/usr/bin/git`;子 node 进程 `child-node-ok` | | **2. 能读写哪些路径?** | ✅ **全部可读写** | session cwd、tmpdir、DSH_HOME、session cwd 子目录——四处 `mkdir/write/read/cleanup` 全 ok | | **3. 能不能 serve HTTP 页面?** | ✅ **能力存在**(有前提,见下) | `web` profile 挂了 `@deepseek-ai/dsh-host-webserver`;headless 没有;注册代码路径已在 mock ctx 下跑通 | | **4. exec / ctx 上有哪些能力?** | ✅ 见第三节 | 拿到完整形状 | **对 HTTP 的补充说明**:`webServer` 是**可选依赖**——只有挂了 `host-webserver` 的 profile(web / desktop)才有,headless 没有。 实测 headless 下 `ctx.get('webServer')` 返回 `undefined`,注册被正确跳过,插件照常工作。 → **独立看板页面这条设计成立**,但需要 web profile;v0.2 做 UI 时要在这个 profile 里冒烟测试。 --- ## 二、三个必须修正设计的地方(探针的主要价值) ### 修正 1:可选依赖要用 `ctx.get()`,不能用 try/catch 包 `ctx.X` `ctx` 是**受 `inject` 约束的代理**。访问一个没有 inject 的服务会**直接抛错**: ``` cannot get property "webServer" without inject ``` 也就是说,`ctx.webServer` 这种写法在没 inject 时不是返回 `undefined`,而是抛异常——`try/catch` 只能兜住症状,兜不住设计问题(而且会让"忘记 inject"变成难查的静默降级)。 官方文档(cordis 教程第 3 章「可选依赖」)给了正确写法: ```js // inject 用于硬性依赖;可选依赖不 inject,在使用处探测 const greeter = ctx.get('greeter') // 没提供方时是 undefined greeter?.greet('maybe') ?? 'no greeter' ``` **已据此修改插件**:`inject: ['tools']`(硬依赖),`webServer` 走 `ctx.get('webServer')?.register(...)`。 ### 修正 2:拿不到 `session.cwd` `exec.agent.session` 存在、能取到 `sessionId`,但 **`session.cwd` 是 `null`**。 v0.1 的 worktree 路径**不能依赖它**。下一步要从 `session.header` 或 `agent.options` 找真实来源。 (注意:`process.cwd()` 在 headless 下是启动目录 `/private/tmp`,不等于会话工作目录,也不能用。) ### 修正 3:服务名是 `subagents`(复数) `ctx.get('subagent')` → `absent`;`ctx.get('subagents')` → `object`。设计文档里写错了,已改。 --- ## 三、ctx / exec 能力清单(实测) ### `ctx.get(name)` 探测结果(headless profile) | 服务 | 结果 | |---|---| | `tools` | object ✅ | | `sessions` | object ✅ | | `subagents` | object ✅ | | `storage` | object ✅ | | `llm` | object ✅ | | `systemPrompt` | object ✅ | | `webServer` | **absent**(本 profile 未挂) | | `subagent` / `agent` | absent(名字写错 / 不存在) | ### `exec` 形状 ``` execKeys: agent, arguments, callId, concludeTurn, deferContext, name, rootCallId, signal, token agentKeys: ctx, dispatch, id, inbox, loopCtx, options, phase, scope, session, systemPrompt, … session: header, log, derived, toolHistoryProjection, … 以及 id ``` 可用:`exec.agent.session.id`(会话 id)、`exec.signal`(中止信号)、`exec.callId`。 --- ## 四、开发回路的两个硬约束(踩过才知道) ### 约束 A:插件放在 profile 树之外时,裸导入解析不到 `--patch` 用绝对路径能挂上**入口文件**,但 Node 解析该文件自己的 `import '@deepseek-ai/dsh-tools'` 时,是**按插件自身位置**向上找 `node_modules`——插件在 profile 树外就找不到。 实测报错: ``` failed to import Cannot find package '@deepseek-ai/dsh-tools' imported from /Users/.../dsh-parallel-dev/src/host/index.js ``` (而且这条错误在启动阶段可能被吞掉——官方文档提醒过:模块解析失败走 logger,可能早于 console 导出器就绪。) **采用的做法**:把包同步进 `~/.dsh/profiles//node_modules/`(`dev-sync.sh`),裸导入就能沿目录树向上找到 `profiles/node_modules`。**这与团队最终的 `dsh plugin add` 安装方式一致,没有开发/生产差异。** ### 约束 B:不要碰用户正在用的 profile 开发用独立 profile `pdevdev`(从 headless 派生),通过 `--patch` 叠加层挂载。用户正在跑的 `web`(127.0.0.1:19387)完全不受影响。 --- ## 五、环境变量有继承污染(要记住的坑) headless 会话里读到的环境变量: ``` DSH_PROFILE=desktop ← 不是本会话的 profile DSH_WEB_URL=http://127.0.0.1:19387 ← 用户正在跑的 GUI DSH_SESSION_ID=session-78a07a9b-… ← 不是本会话的 id(本会话是 session-9e72dd67-…) DSH_PROFILE_DIR=/Users/…/profiles/desktop ``` 这些是**从用户正在运行的桌面应用继承下来的**。 → **插件不能盲信 `DSH_*` 环境变量**,真实信息要从 `ctx` / `exec` 取。这条直接写进 v0.1 的实现约定。 --- ## 六、意外收获:本机有 DSH 源码检出 `~/Documents/projects/deepseek-harness/` 是一份完整检出,带**官方插件开发文档**: | 路径 | 内容 | |---|---| | `docs/cordis-tutorial/` | 7 章插件框架教程(中英双语)—— 插件形态、生命周期、服务与注入、事件、配置、组合与 HMR、进入 harness | | `docs/user/develop/basic/` | 第一个插件 / 开发一个工具 / 插件配置 / 发布 | | `docs/cookbook/adding-a-tool.zh.md` | 工具的深入参考:嵌套 schema、规范值、后台工作、策略钩子、PTC、UI 卡片 | | `packages/bundle/*/cordis.patch.yml` | 各 profile 层的真实组合,可对照读懂依赖关系 | **后续优先查它,不要再逆向 asar。** 这次先在 asar 里挖了半天,其实官方文档一直躺在本地。 --- ## 七、探针的结论 **风险 1 解除,设计基本成立。** 三处修正(可选依赖写法、cwd 来源、服务名)都是局部调整,不影响 v0.1 / v0.2 的骨架和估时。 **可以进 v0.1**:`pdev_preflight` / `pdev_task_create`+`pdev_task_update` / `pdev_verify_record`。 已就绪的开发回路: ```bash ./dev-sync.sh pdevdev dsh --profile pdevdev --patch ./dev-mount.yml "" ```