Files
workbuddy_skills/dsh-local-env/SKILL.md
T

90 lines
13 KiB
Markdown
Raw Normal View History

---
name: dsh-local-env
description: 「**在 Windows 本机把官方 dsh 跑起来 / 本机开发环境**」的总入口 —— 覆盖两半:① **本机跑起官方 dsh 并端到端取证**(web 实例 `apps/cli` 的 `dsh` CLI:起实例 / 查回环监听 / token+cookie jar 取 200 / 探测 Windows 沙箱后端;桌面壳 `apps/desktop`:源码构建、Electron 参数、CDP 真跑取证、**离线可用**验证、自研 client 插件真跑)② **环境引导 / 搬迁 / 改名**(「换电脑了」「改了工作区路径」「迁移到新环境」「规则会不会丢」「新环境还没配好」;以及**改名/搬家后的路径残留清理**)。⚠️ **边界**:本技能管**本机源码/开发态**;「**服务器上**单个用户实例打不开 / OOM / 崩溃重启」⇒ `dsh-diagnose`;「**服务器实例里**插件没 UI / 没生效」⇒ `dsh-diagnose`。核心 = 两形态先选对入口 + **Node 22** + **监听 ≠ 就绪** + **判据写窄=假红** + 换环境的"**规则齐备优先**"顺序 + **路径残留分类要细到「行」**。判据实体在主干,**全文在 `references/`(2 档)**。
version: 1.0.3
updated_at: 2026-09-30
last_change: 【2026-09-30 · 三改】§1 新增「🔴 **headless 起法:普通 node 直起 `apps/desktop-host` + 计划任务常驻**」——`@deepseek-ai/dsh-desktop-host` **src/lib 零 `electron` 引用**、官方壳自己就是把它当独立 node 子进程 spawn(`host-process.ts:188-190`)⇒ **可无窗口直起**;给出 argv 四段(`[2]`=runtimeDir、`[3]`=**`$DSH_HOME/profiles/desktop`**、`[4]`=primaryRuntime、`[5]/[6]`=pnpm/nodeBin)+ 必需 env(含 🔴 `CODEBUDDY_SAFE_DELETE_BULK_THRESHOLD` 不调高会"起了但口不开且零报错");**durable 只能靠计划任务**(`detached` 因 Job 不可逃逸=`err=5` 无效);⚠️ 更正一条半对说法「没有口令也不妨碍先起来」—— 口令缺失确实不阻止 `listen`,但 `workbuddy` 模式**先发现后监听 fail-closed**,**发现失败 ⇒ 永不监听**;§6c.4 四枪判据 + §6c.5 两条新坑(子进程 env 要**枚举删**;`Get-CimInstance -match` 数进程会被**探针自己的命令行**污染)。 (2026-09-29 · 二改)§1 新增「🔴 **「装上了」≠「口会开」—— 自研插件的 opt-in 回环口**」:`mount-*-into-profile.mjs verify` 的 `rc=0` 是**假绿**(绿只判 bundles+junction、opt-in 开关仅打印);分诊三步(合成插件树探针 → 插件自己的诊断路由 404/401 → 才查运行期);「**开机一次性发现 + fail-closed 永不重试**」+「**Electron 单实例锁让"再拉一份"无效**」这对组合会让故障**永远修不好** ⇒ 入口必须带**幂等闸**。详情档已补 §6。 【2026-09-29】§1 新增「**无人值守轮里只跑到骨架落盘**」姿势(启动器末尾 `spawn Electron` 常驻 ⇒ 用 `DSH_DESKTOP_PRIMARY_RUNTIME_DIR` 不存在路径当哨兵、在 `initProfile` 之后自停;挂载由 `DEVKIT_MOUNT_PLUGINS=1`/`DEVKIT_SKIP_*` 控制)。【2026-09-28】**由 `dsh-desktop-dev-shell`(v1.3.1)+ `dsh-env-bootstrap`(v1.2.0)合并而成**(两个原名退役)。按 `dsh-knowledge-upkeep §10`「主干 + 详情档」形态:判据实体留主干、全文下沉 `references/`(**内容守恒:逐行 0 丢失**,含原 frontmatter 变更历史)。合并理由:两者都是「**本机环境**」域 —— 一个管"把 dsh 跑起来并取证",一个管"换环境/改路径后规则与残留"。⚠️ 并按对账结论处置了版本分叉:`desktop-dev-shell` 本机版为**超集**(比文档库多 §11.6–11.8 等 131 行)⇒ 取**本机版**;`env-bootstrap` 本机版为**超集**(多 §3 迁移清理 67 行,文档库的"设计红线/自检"是其真子集)⇒ 取**本机版**。⛔ 未删任何判据。
agent_created: true
---
# dsh-local-env — Windows 本机 dsh 环境总入口
> ## 🔴 第 0 步:先分诊
>
> | 情形 | 去哪 |
> |---|---|
> | 要在**本机跑起 dsh**(web 实例 / 桌面壳)并取证 | **§1** |
> | 改了插件后**界面没变化** / 壳起不来 / pnpm 卡住 | **§1** |
> | **换电脑 / 改工作区路径 / 迁移 / 改名** | **§2** |
> | 改名后发现还有地方指着旧路径 | **§2 → 路径残留清理(分类细到「行」)** |
> | **服务器上**实例崩了 / OOM / 插件没 UI | ⛔ 不属本技能 ⇒ `dsh-diagnose` |
---
## 1. 本机跑起官方 dsh 并取证
**先选对入口(两个形态别混)**:**web 实例**(`apps/cli` 的 `dsh` CLI)|**桌面壳**(`apps/desktop`,需源码构建,可挂自研 client 插件做真跑验证)。
🔴 **三条必须先知道的判断**:
1. **Node 22**(版本不对会以各种"像工程问题"的方式失败);
2. 🔴 **「监听 ≠ 就绪」** —— 端口在听 **不代表** 实例能服务(实测:监听起来了但仍是 401/白屏)⇒ 判据必须做到"**能取到 200**"这一层;
3. 🔴 **判据写窄=假红** —— 会把**本来正确的产品行为**判成故障,然后你去改对的东西。⚠️ 同类:**陈旧写锁**会让实例**永久起不来**。
**依赖安装**:pnpm 在本机"卡在 added ~100"有**真根因**(不是慢)⇒ 见详情档。
**启动**:⛔ **不要用 `pnpm run dev:desktop`**;Electron 启动参数与 `--no-sandbox` 判据见详情档。
**改插件后必须重启壳**(插件树不热加载)。
**🔴 无人值守轮里"只跑到骨架落盘"的姿势(2026-09-29 实测 · 省一整类事故)**:dev 启动器末尾会 `spawn Electron` 并**常驻** ⇒ 自动化轮里跑它只有两条坏路 —— 放进**会话后台任务**(每轮输出唤醒宿主会话)或留一个无人关的窗口(残留)。
✅ 正解 = **跑同一份启动器代码**(同 tsx / 同 `DEVKIT_*` 开关),拿一个**排在校验序列后面的必需项**当哨兵 —— 桌面 0.1.7 启动器的 `DSH_DESKTOP_PRIMARY_RUNTIME_DIR` 校验发生在 `initProfile` **之后** ⇒ 指向不存在的路径即可**骨架落盘即自停**(`exit=1`、报错文本就是那一条,属预期)。零窗口、零 `electron.exe` 残留。
⚠️ 配套:挂什么插件由 `DEVKIT_MOUNT_PLUGINS=1` + `DEVKIT_SKIP_*` 精确控制(缺省 = 官方初始状态,一个都不挂)。
**🔴 「装上了」≠「口会开」—— 自研插件的 opt-in 回环口(2026-09-29 实测 · 一整轮排查换来的)**:
- 症状:profile 里包挂好、插件**自己的路由也能命中**,**但那个回环口就是不 LISTENING**。
- 🔴 **`mount-*-into-profile.mjs verify` 的 `rc=0` 是假绿** —— 它只判 `bundles + junction`,**opt-in 开关只在输出里打印**。判"口会不会开"**只能看口有没有 LISTENING**。
- **最快分诊(⛔ 先别重启客户端)**:① 用官方 `loadProfileDirectory()` + `composeEntries()` 打印**合成后**的插件树,确认那条 `config` **真的落到插件**(⛔ **别**调 `loadProfile()` —— 它会写 profile 目录;另注意 `cordis.patch.yml` 的 id 定向覆盖要能命中 bundle 自己 insert 的 id);② 打插件**自己的**诊断路由区分 **404 = 没装载 / 401 = 已装载**;③ 前两步都正常,才去怀疑运行期(动态发现失败 / `listen` 失败)。
- **一个"永远修不好"的陷阱**:模块若在**开机一次性**做上游动态发现且 **fail-closed 永不重试**,那一次失败 ⇒ 该客户端**整条生命周期**都不开;而守护进程"再拉一份"会被 **Electron 单实例锁**顶掉(新进程秒退,日志一片 `address already in use` + `exited: 0` —— **看着像起不来,其实头一份活得好好的**)⇒ **启动入口必须带幂等闸**:口在听/正在启动(宽限期)⇒ **零副作用早退**(⛔ 别为"发现不必启动"而先把 runtimeDir 重铺一遍);旧客户端活着但口不通 ⇒ **先按命令级甄别停旧的、再拉新的**。
- ⚠️ **模块自己的日志可能进不了客户端日志**(`grep` 零命中)⇒ 失败原因**离线不可见**;⇒ 入口侧要**自己落一行判定**(实测做法:客户端日志里出现 `device-access=ok(...)`)。
- 细节(探针脚本、读数、判据脚本)⇒ 详情档 §「opt-in 回环口」。
**🔴 headless 起法(要"无窗口"或要"活过会话"时用这条 · 2026-09-30 实测)**:那个口所在的 **DSH host 本身可以用普通 node 直起**(`apps/desktop-host` src/lib **零 `electron` 引用**,官方壳也只是把它当独立 node 子进程 spawn)⇒ **不弹窗口、不依赖 Electron**;要**跨会话常驻**则只能交给 **Windows 计划任务**(`detached` 在本机因 Job 不可逃逸而无效)。⚠️ 两条必记:① argv `[3]` 是 **`$DSH_HOME/profiles/desktop`**(⛔ 不是 runtimeDir)② 少了 `CODEBUDDY_SAFE_DELETE_BULK_THRESHOLD` 会变成「**进程起了、口永远不开、且零报错**」。⚠️ 另更正一条半对说法:**口令缺失不阻止 `listen`,但"网关发现失败"会**(`workbuddy` 模式 = 先发现后监听,fail-closed ⇒ 永不监听)。
📂 **全文 ⇒ 详情档 §6c**(argv/env 对照表、计划任务动作形状、`schtasks` 被拦只能经脚本 spawn、UTF-16 必带 BOM、四枪判据、两条新坑)。
**真跑取证(怎么拿到硬证据)**:用 **CDP** 连真窗口。⚠️ 选 CDP 目标时 **DevTools 自己也是一个 `type:'page'`** ⇒ 别选错。
**「离线可用」怎么验**:⛔ **别用"没人监听的端口"**造断网(那验不出东西);要用黑洞服务造"挂起" + **有界超时** + 如实状态。
⚠️ **造假的 `ctx` 有个必踩的语义坑**(假 ctx 与真 ctx 的语义差异 ⇒ 会让你得出错误结论)。
**两条本机环境坑**:`safe-delete` 护栏(**删 `node_modules` 前必读**);⚠️ Windows 上 `taskkill /F` 会被 MSYS **路径改写**成 `taskkill 'F:/'` ⇒ 报"无效参数"。
📂 **全文 ⇒ `references/00-本机跑起来与取证.md`**(含两形态完整命令与判据、pnpm 卡安装真根因、Electron 参数实测、CDP 取证配方、离线可用验证、`safe-delete` 护栏、自研 client bundle 必须 lazy-CJS 封包、真 host 的 RPC 调用(token→cookie + 信封口径)、桌面快捷方式链路)
---
## 2. 环境引导 / 搬迁 / 改名后的路径残留
**换环境的正确顺序**:🔴 **把"规则齐备"放在第一步**(先确认工作区常驻规则带着走,再谈别的)。
**工具**:`references/dsh-env-bootstrap/resident-rules.py` —— `--check`(校验关键规则齐备,**默认只报不改**)/ `--inject`(标记块内替换,首次需 `--init`)/ `--env-check`(路径与 hooks 命令自检)/ `--snapshot`(由权威重生成快照)。
🔴 **设计红线**:**权威方向单向**(规则文件为权威,快照为副本)⇒ ⛔ 别把它用成"第二真相源"。
🔴 **路径残留清理:分类要细到「行」,⛔ 不能只到「文件」**(血的教训):
- **第一层|文件分两类**:**活载体**(会被读取并据以行动:状态脚本 / 规则文件头部的路径声明 / 可执行脚本 / 当前工作线入口与接续包)⇒ **改写**;**历史档案**(记录过去某一棒做过什么:日志 · 归档 · tmp · 交付物 · 历史草案)⇒ ⛔ **不动**。
- 🔴 **第二层(最易漏、代价最大)|同一个"活载体"文件里,也有**不能改的行**:① **「污染源」记录本身**(改了就变成"它们写成了正确路径"⇒ **事故记录自毁**)② **`旧 A → 新 B` 配对里的 A 侧**(改了就成 `新 → 新`,脚本参数变重复)。
- **两类「改不动」的残留**:⛔ 别硬改、⛔ 别猜。
- **改名必须同步的「机制层」两处**:⛔ 漏了会**静默失效**。
📂 **全文 ⇒ `references/01-环境引导与迁移.md`**(含脚本完整参数、换环境顺序、分类细则与三类"不能改的行"、改不动的残留清单、机制层两处、三条可复跑验收)
📂 **快照 ⇒ `references/dsh-env-bootstrap/常驻规则-快照.md`** | **脚本 ⇒ `references/dsh-env-bootstrap/resident-rules.py`**
---
## 3. 详情档索引(跨档引用按此表定位)
| 档 | 覆盖的原技能 | 原章节 |
|---|---|---|
| `references/00-本机跑起来与取证.md` | `dsh-desktop-dev-shell`(**v1.3.1 本机超集版**,全文) | §0 为什么存在 / §1 两形态 / §2 桌面壳三判断 / §3 pnpm 卡安装 / §4 启动 / §5 Electron 参数 / §6 真跑验证 / **§6b opt-in 回环口(09-29 新增)** / **§6c headless 主机 + 计划任务常驻(09-30 新增)** / §7 本机环境坑 / §8 壳里打开不是登录页(**不是 bug**)/ §9 `safe-delete` 护栏 / §10 纪律 / §11 web 实例形态(含 **§11.6–11.8 本机独有**) |
| `references/01-环境引导与迁移.md` | `dsh-env-bootstrap`(**v1.2.0 本机超集版**,全文) | §0 为什么存在 / §1 命令 / §2 换环境顺序 / §3 迁移·改名后的路径残留清理(含 §3.1–§3.5)/ §4 设计红线 / §5 自检 |
| `references/dsh-env-bootstrap/` | 原附属档 | `常驻规则-快照.md` · `resident-rules.py` |
🔴 **两个原名已退役**(`dsh-desktop-dev-shell` / `dsh-env-bootstrap`)⇒ 别处见到按本技能对应档读。
⚠️ **边界**:本技能管「**本机**」;服务器侧的实例与插件 ⇒ `dsh-diagnose`。
⚠️ **已并入的版本分叉**:文档库侧旧版比本机少 §11.6–11.8(本机为超集)⇒ 已按**本机版**定稿,⛔ 别用旧副本反向覆盖。