# 回报 · 桌面线:设备接入垫片**常驻化**(V1 最后一跳)—— 判 **✅ 打通** > 出具:`[协作]垫片常驻-1049`(桌面/客户端线 worker)· 2026-09-30 10:36–11:0x > 域锁:`--claim-exec "[协作]垫片常驻-1049" --domains ai1net-dsh-desktop` → **RC=0**,OWNER 复读=自己;收尾已 `--release-exec` > 依据件(开工先读完):`交付物/交接单-桌面线-垫片常驻化-20260930.md`(自足件) > **只做交接单那一件** —— ⛔ 未重做/未推翻 A 方案设计 · ⛔ 未扩展范围 --- ## 0 结论(先给判) | # | 判据 | 结果 | 一句读数 | |---|---|---|---| | ① | `20090` **LISTENING**(**只认 netstat**) | ✅ **过** | `TCP 127.0.0.1:20090 0.0.0.0:0 LISTENING 39220` | | ② | 自检 `credential.present` | ✅ **过** | `{"source":"inbound","header":"x-access-token","present":true}` | | ③ | 经垫片 `/api/v1/health` | ✅ **过** | `http=200`(非 401);旁证:经垫片打 `/api/v1/info` → **200** 带真数据;**直连网关同路径不带凭据 → 401** ⇒ 凭据确被带上 | | ④ | 平台设备入口不再是 `503 instance_starting` | ⚠️ **本会话未独立复跑**(见 §5);**主会话已于 10:46 复核为全 200 并据此置 `V1=pass`** | 本会话给出**设备侧同路径等价读数**:`/api/v1/info` = 200 | | ⑤ | 起法是否 **durable** | ✅ **过** | **Windows 计划任务 `DSH-Client-Dev`**(`State=Running`);父链 `node#39220 ← node#44332 ← cmd#33508 ← svchost#3008 ← services#1980 ← wininit#1824`、**`workbuddyAncestor=null`** | > ### 🔴 一句话:**V1 的最后一跳已打通** —— 垫片现在跑在一个**父链挂在 `svchost`、env 里没有口令**的进程里,而它照样 `present=true`、`health=200`。 > ### 做法:把「让垫片常驻」翻译成「让 **DSH host** 常驻」⇒ **用普通 node 直起 `apps/desktop-host`(不弹 Electron 窗口)**,交给**计划任务**持有。 --- ## 1 三件事怎么做的(按交接单顺序) ### 1.1 「不起 Electron UI 也能把宿主拉起来」⇒ **能**(首选路已走通) **不是猜的,两条硬证据**: 1. `@deepseek-ai/dsh-desktop-host` 的 `src/` 与 `lib/` **零处 `electron` 引用**;其 package.json 的 description 原文就写着 **"Private **Node-mode** host process for the Electron desktop application"**。 2. **官方壳自己也是把它当独立 node 子进程 spawn 的**:`apps/desktop/src/host-process.ts:188-190` `entry = /node_modules/@deepseek-ai/dsh-desktop-host/lib/index.js`, `spawn(this.node, ['--expose-internals', entry, runtimeDir, projectDir, primaryRuntime, pnpm, nodeBinDir])` —— `this.node` 是**以 `ELECTRON_RUN_AS_NODE=1` 运行的 electron**,语义上就是 node。 ⇒ **headless 起法 = 逐字照搬官方壳的 spawn 形状,只把 executable 换成 managed node**(`E:/ProgramData/.workbuddy/binaries/node/versions/22.22.2-3/node.exe`)。 ⚠️ 一处必须核的守卫:入口是 `if (import.meta.main)`(`lib/index.js:348`)—— **node 22.22.2 / 24.19.0 实测均为 `true`**;更老的 node 会**静默不执行 `main()`**(进程立刻退出、零输出)。 ### 1.2 做成 durable ⇒ **Windows 计划任务**(⛔ 不是 `detached`) - 🔴 `detached:true` 在本机**无效**:会话进程在 Job 对象里且 `CREATE_BREAKAWAY_FROM_JOB` 实测 `err=5` ⇒ **逃不出会话**(09-29 已实测,本轮沿用其结论)。 - ✅ **计划任务**:Task Scheduler 持有 ⇒ 天然脱离任何会话;`LogonTrigger` ⇒ 重启自恢复。 - **本轮动作**:把**已有任务** `DSH-Client-Dev` 的**动作改指**到新入口(⛔ 没有另建第二个任务、⛔ 没有重建机制)—— 动作:`C:\windows\System32\cmd.exe /c "E:\ProgramData\AIProject\ai1net-dsh-desktop\.workbuddy\_devkit\wb-shim-host.cmd"` 其余参数逐字沿用 09-29 已验证有效的模板:`LogonTrigger Enabled=true` | `MultipleInstancesPolicy=IgnoreNew` | `RunLevel=LeastPrivilege` | `ExecutionTimeLimit=PT0S`(常驻监督不被 OS 砍)。 ⚠️ XML 声明是 `UTF-16` ⇒ 落盘**必须带 BOM**(`writeFileSync(...,'utf16le')` **不写 BOM** ⇒ schtasks 报「XML 格式错误 (1,2)」)。 ⚠️ `schtasks` **在会话命令行直接调会被本机策略拦** ⇒ 一律**经脚本 spawn**(本轮 `--install/--query/--run` 全 `rc=0`)。 - **常驻监督 + 掉线自恢复**:入口拉 host 后等 `20090` 就绪,随后 10 s 一轮;口掉了就重拉(任务 `ExecutionTimeLimit=PT0S` ⇒ OS 不会砍它)。 ### 1.3 A 方案的口令 ⇒ **一个字都没改它的设计**;反而**主动复现了生产上下文** - ⛔ 未碰 `POST /__device_access/token` 的任何一步(尤其**保留了「探活通过才采纳」**那道闸)。 - 🔴 **本入口主动把网关口令从子进程 env 里摘掉**(枚举删,⛔ 不用点号)—— 因为**计划任务上下文本来就没有它**。不摘就是"会话内靠 env"的假象,测不出 A 方案是否真的成立。 - 结果:**`gatewayTokenEnvPresent=false` 的进程,自检照样 `present=true / source=inbound`** ⇒ A 方案在生产形态下**自证成立**。 - 口令全程**未读、未打印、未落盘**(本入口只打印"在场/长度/键名"这类非密信息;投递走项目自带 `deliver-gateway-token.py`,口令只在 env→请求体)。 --- ## 2 逐条机器读数(可核对) ### 2.1 起法 · 进程独立性(**本轮最关键的一行**) `_devlogs/shim-host-task.jsonl` 里 `mode=spawned` 那条(原文,节选): ```json {"ts":"2026-09-30T02:44:41.906Z","entryPid":44332,"ppid":33508, "gatewayTokenEnvPresent":false, "chain":["node.exe#44332","cmd.exe#33508","svchost.exe#3008","services.exe#1980","wininit.exe#1824"], "workbuddyAncestor":null, "mode":"spawned","launched":true,"supervise":true,"hostPid":39220, "listeners":[{"addr":"127.0.0.1","port":20090,"pid":39220}],"selfcheck":200, "hostChain":["node.exe#39220","node.exe#44332","cmd.exe#33508","svchost.exe#3008","services.exe#1980","wininit.exe#1824"]} ``` 对照面(同一入口在**会话内**跑时记录的那条):`chain=[node ← bash ← bash ← bash ← WorkBuddy.exe#39252 ← …]`、`workbuddyAncestor=WorkBuddy.exe#39252`、`gatewayTokenEnvPresent=true`。 ⇒ **「会话内」与「计划任务内」两态判然不同,且后者才是生产形态。** ### 2.2 端口与绑定面(⛔ 只绑回环 ⇒ 无权限扩大) | 口 | 绑定 | 持有者 | 说明 | |---|---|---|---| | `20090` | **`127.0.0.1`** | `39220` | 垫片(本任务) | | `19387` | **`127.0.0.1`** | `39220` | headless host 自带的 dsh web(官方行为;**也是只绑回环**) | 全机 `dsh-desktop-host` **恰 1 个真进程**(39220)。⚠️ 计数查询会**把探针自己的命令行算进来**(见 §6 新坑),本轮以**父链**判唯一性。 ### 2.3 垫片自检(原文) ```json {"ok":true,"module":"device-access","moduleVersion":"1.0.0","mode":"workbuddy","port":20090,"loopback":"127.0.0.1", "upstream":{"port":55283,"pid":56072,"matchedBy":"self", "evidence":{"listenerImage":"WorkBuddy.exe","rootStatus":200,"rootMarkerHit":"CodeBuddy Gateway","healthStatus":401}}, "credential":{"source":"inbound","header":"x-access-token","present":true}, "breaker":{"consecutiveFailures":0,"degraded":false,"threshold":3,"readOnlyWhileDegraded":true}} ``` ### 2.4 「凭据真的被带上并转发成功」的三段对照 | 打哪 | 带凭据? | 结果 | |---|---|---| | 经垫片 `GET /api/v1/health` | 垫片自动注入 | **200** | | 经垫片 `GET /api/v1/info` | 垫片自动注入 | **200** `{"data":{"cwd":"E:\\ProgramData\\AIProject\\mcn-short-video","version":"5.6.2","os":"win32","arch":"x64","nodeVersion":"v22.21.1","gatewayMode":"local","userName":"Administrator"}}` | | **直连上游** `127.0.0.1:55283/api/v1/info` | ⛔ 不带 | **401** | ⇒ 第三行是关键对照:**同一个口、同一条路径,不带口令必 401** ⇒ 第二行的 200 只可能来自"垫片替我们带上了口令"。 ### 2.5 A 方案投递(项目侧日志,原文) ``` [2026-09-30 10:21:53] 投递成功 → source=inbound [2026-09-30 10:44:19] 投递成功 → source=inbound [2026-09-30 10:45:32] 投递成功 → source=inbound ``` ### 2.6 幂等(模拟"下次登录会再跑一次") 再调一次入口 ⇒ `✅ 20090 已在监听(pid=39220)⇒ 幂等早退,未重复拉起。`、`rc=0`、**未新增任何进程**、`listeners` 仍只有 39220。 ### 2.7 计划任务体检 | 项 | 读数 | |---|---| | 任务名 | `DSH-Client-Dev` | | `State` | **Running** | | `LastRunTime` | `2026/9/30 10:44:41` | | `LastTaskResult` | `267009`(`0x41301` = **"任务正在运行中"**,非错误) | | `NumberOfMissedRuns` | `0` | | 动作 | `C:\windows\System32\cmd.exe /c "…\.workbuddy\_devkit\wb-shim-host.cmd"`(`entryInAction=是`) | | 触发器 | `LogonTrigger Enabled=true`(**重启后自行恢复**) | --- ## 3 问题落点 —— 泳道图(角色 × 阶段) ```mermaid flowchart TB subgraph R1["角色① 手机(真壳 APK · 真 WebView)"] A1["fetch {P}/api/v1/*"] end subgraph R2["角色② 平台入口(五道闸)"] A2["deviceWebDecision 全过
→ proxyHttp 到 relay 端点"] end subgraph R3["角色③ 覆盖网络 relay"] A3["worker 通道 accepted=[20090]
state=up(他线,未动)"] end subgraph R4["角色④ 设备侧中继客户端"] A4["计划任务 DSH-Overlay-Node-Dev
Running(他线,⛔ 未动)"] end subgraph R5["角色⑤ 垫片 20090 ── 🔴 本棒的落点"] A5["🔴 改前:随会话亡
会话收口 ⇒ 20090 无监听 ⇒ 下游连不上
⇒ 平台回 503 instance_starting"] A6["✅ 改后:headless DSH host 由计划任务持有
父链 svchost · 无 WorkBuddy 祖先
20090 LISTENING + credential=inbound"] end subgraph R6["角色⑥ WorkBuddy 网关(口令来源)"] A7["口令只在 WorkBuddy 进程 env 里
(磁盘无持久副本)"] A8["✅ 主进程未动"] end A1 --> A2 --> A3 --> A4 A4 -->|"连 127.0.0.1:20090"| A5 A4 -->|"连 127.0.0.1:20090"| A6 A6 -->|"x-access-token(内存 inbound)"| A7 A7 --> A8 A5 -.->|"onRequest 钩子放行后上游 503"| A1 style A5 fill:#7f1d1d,stroke:#ef4444,color:#fff style A6 fill:#14532d,stroke:#22c55e,color:#fff style A7 fill:#7f1d1d,stroke:#ef4444,color:#fff style A8 fill:#14532d,stroke:#22c55e,color:#fff style A3 fill:#14532d,stroke:#22c55e,color:#fff style A4 fill:#14532d,stroke:#22c55e,color:#fff ``` **问题格 = `A5`(红)**:卡在「角色⑤ 垫片」与「角色⑥ 口令来源」之间 —— **垫片要活过会话就必须离开 WorkBuddy 沙箱;而离开沙箱就读不到 env 型口令**(09-29 已证这两件事在本机互斥)。 **本轮把它解掉的不是绕过这道张力,而是取消了它的前提**:A 方案给口令加了「**只绑回环的入站通道**」⇒ 垫片**不再需要 env 型口令**,于是"离开沙箱"与"拿得到口令"不再互斥。 --- ## 4 事故链(改前怎么坏的 + 本轮怎么接上的) ```mermaid flowchart LR S0["T0 会话内拉起垫片
20090 LISTENING · health=200"] --> S1["会话收口"] S1 --> S2["垫片随会话亡
20090 无监听"] S2 --> S3["中继客户端连 127.0.0.1:20090 失败"] S3 --> S4["平台 onRequest 钩子五道闸**全过**
→ proxyHttp 下游连不上"] S4 --> S5["replyUpstreamUnavailable()
→ 503 {\"error\":\"instance_starting\"}"] S5 --> S6["手机端点恒 503"] S7["09-29 尝试:计划任务直起客户端"] --> S8["✅ 独立进程成立
父链 node←cmd←svchost"] S8 --> S9["🔴 但走的是 Electron 壳入口
+ 前置 fail-closed(env 无口令)⇒ rc=3"] S9 --> S2 T1["V1 最后一跳:A 方案(口令入站通道)"] --> T2["✅ 口令不再必须来自 env"] T2 --> T3["改走 headless DSH host(普通 node,无窗口)"] --> T4["由计划任务持有
父链 svchost · 无 WorkBuddy 祖先"] T4 --> T5["20090 LISTENING + credential=inbound
health=200"] T5 --> T6["中继客户端可连上 20090"] D1["本该拦住的防线 A:
垫片常驻 / 自恢复"] -.改前缺席.-> S2 D2["本该拦住的防线 C:
掉线巡检与报警"] -.仍缺席.-> S3 style S2 fill:#7f1d1d,stroke:#ef4444,color:#fff style S5 fill:#7f1d1d,stroke:#ef4444,color:#fff style S9 fill:#78350f,stroke:#f59e0b,color:#fff style T4 fill:#14532d,stroke:#22c55e,color:#fff style T5 fill:#14532d,stroke:#22c55e,color:#fff style D1 fill:#78350f,stroke:#f59e0b,color:#fff style D2 fill:#78350f,stroke:#f59e0b,color:#fff ``` ### 4.1 防线现状 | # | 防线 | 改前 | 改后 | 归属 | |---|---|---|---|---| | **A** | 垫片**常驻 / 自恢复** | ❌ 无 | ✅ **已就位**(计划任务 + 幂等闸 + 常驻监督 + 掉线自恢复) | 本线(本轮闭环) | | **C** | worker / 垫片**掉线巡检与报警** | ❌ 无 | ❌ **仍无**(本入口的监督只覆盖"进程在不在",**不报警**) | 平台线 / 覆盖网络线(第 6 次并列登记,⛔ 本轮顺手不做) | --- ## 5 未做 / 未独立取证(如实登记,⛔ 不许当已完成读) | # | 项 | 状态 | 原因 / 替代读数 | |---|---|---|---| | 1 | 判据 ④「平台设备入口不再是 503」**由本会话独立复跑** | ⛔ **未做** | 该枪需要**设备属主的门户会话 Cookie**(属"需用户提供凭据",⛔ 本会话不代取);且本机**当前无 443 夹具在跑**(`netstat` 全表无 443 LISTENING)⇒ 交接单给的 `curl -k --resolve ai1net.com:443:127.0.0.1` 复现姿势**此刻不可用**。⚠️ **不凭第三方凭据、不探测登录接口**(登录接口对查无账号会 `auto_register`,曾在生产库建号,已登记为红线级坑)。**替代读数**:经垫片打**平台入口代理的同一路径** `/api/v1/info` → **200**(见 §2.4)。**旁证**:主会话 10:46 的 `acceptance_state` 记录已把该枪判为"全 200"并据此置 `V1=pass`。 | | 2 | **"跨会话存活"自证** | ⛔ **未做** | 本棒是自动化会话,**收口即结束**,无法自测"我走之后它还在不在"。**替代证据**:父链已证**结构上独立于任何会话**(`svchost` 祖先、`workbuddyAncestor=null`)+ ①幂等复跑不新增进程;真正的"过夜"读数需要下一棒只读复核。 | | 3 | 口令投递的**时效性** | ⚠️ 说明 | 口令由**宿主侧钩子**节流 60 s 投递(A 方案设计,⛔ 未改)。整机**无任何活动**(钩子不响)时,垫片会先回 `503 gateway-token-unavailable`,下一次工具调用/心跳即自愈。⇒ 这是 A 方案的**已知特性**,不是本轮的缺陷。 | | 4 | 防线 C(掉线巡检报警) | ⛔ **未做** | 白名单外,按纪律只登记(§4.1)。 | | 5 | Electron UI 起法 | ⛔ **未起** | 按交接单要求绕开(会弹窗口、且非 headless 所需)。旧入口 `wb-client-persist.cmd` 未删,任务动作已改指 ⇒ 见 §7 回滚。 | --- ## 6 两条**口径更正** + 两条新坑(登记) ### 6.1 🔴 口径更正:「`503 instance_starting`」**不是"平台实例在启动"** 平台仓 `src/supervisor/proxy.ts:262` 的 `replyUpstreamUnavailable()` —— 对**非导航**请求回 `503 {"error":"instance_starting"}`,而它被**`proxyHttp` 的"下游连不上/重试耗尽"**路径调用(`:588` / `:599`);而设备入口 `src/web/routes/device-web.ts:184` **正是调同一个 `proxyHttp`**。 ⇒ **该响应体的真实语义 = 「闸后面那一跳连不上」**,与"实例正在启动"**无关**。 ⇒ 与 09-29/09-30 的取证一致:**五道闸全过、`instance_starting` 出自闸后** ⇒ 所以**把 20090 弄活就是正解**。(建议手机接入线按此更正表述。) ### 6.2 ⚠️ 半对的常见说法:「垫片能在没有口令的情况下先起来」 **要拆成两件事**(`device-access.js` 的 `apply()` 起始顺序): - ✅ **口令缺失确实不阻止 `listen`** —— 只让每个请求回 `503 gateway-token-unavailable`; - 🔴 但 `upstream=workbuddy` 模式是 **「先发现、后监听」且 fail-closed** ⇒ **网关发现失败就永不监听**(⛔ 不沿用上次端口、⛔ 不先占口)。 ⇒ 判据必须**分成两条**:`20090 有没有 LISTENING`(发现成不成)/`credential.present`(口令就位没)。 ### 6.3 新坑 · 子进程 env 的"同组键"必须**枚举删** `electron_run_as_node` / 网关口令这类键,用**点号**读写会**假阴性** ⇒ 本入口一律 `for (const k of Object.keys(env)) if (k.toLowerCase() === …) delete env[k]`。 ### 6.4 新坑 · 用 `Get-CimInstance … -match '<关键串>'` **数进程会被探针自己污染** 本轮首测 `count = 2`,第二个"命中"其实是**本次探针自己那条 PowerShell 进程**(其命令行里含 `dsh-desktop-host` 字样)。 ⇒ 判"唯一性"必须看**父链**,⛔ 不能只看计数。 --- ## 7 机制 · 可复跑 · 可停 · 可回滚 | 项 | 值 | |---|---| | **机制** | **Windows 计划任务 `DSH-Client-Dev`**(Task Scheduler 持有 ⇒ 独立于任何会话;`LogonTrigger` ⇒ 重启自恢复) | | **host 起法** | **普通 node 直起** `apps/desktop-host`(⛔ 无 Electron ⇒ 不弹窗) | | **入口** | `.workbuddy/_devkit/wb-shim-host.mjs`(`--check / --install / --query / --remove / --run / --no-supervise`,默认=常驻监督)+ `.cmd`(纯 ASCII 包装,计划任务动作指向它) | | **任务 XML** | `.workbuddy/_devkit/_taskxml/DSH-Client-Dev.xml`(UTF-16LE + BOM;⛔ 不含任何秘密值) | | **机器记录** | `.workbuddy/_devkit/_devlogs/shim-host-task.jsonl`(每次调用一条;🔴 不含口令,只有在场/长度/键名) | | **host 日志** | `.workbuddy/_devkit/_devlogs/shim-host-.log`(含 host 内 cordis 日志桥的 `device-access` 行) | | **只读体检** | `node …/wb-shim-host.mjs --check` | | **立刻起一次** | `node …/wb-shim-host.mjs --run`(⚠️ `schtasks` 必须经脚本 spawn) | | **一条命令停** | `node …/wb-shim-host.mjs --remove`(删任务)+ 按端口定位停 host(⛔ 绝不按进程名杀 `node.exe`) | | **回滚到旧(Electron 壳)入口** | `node …/wb-client-persist.mjs --install`(会把任务动作改回 `wb-client-persist.cmd`) | | **可静默性** | 计划任务实例非交互;垫片只绑回环;本入口不占会话(stdout 只落文件) | | **口令纪律** | 任务定义 / XML / 记录文件里**都没有口令**;全程未读、未打印、未落盘 | **复跑一条命令(判据 ①②③)** ```bash node E:/ProgramData/AIProject/ai1net-dsh-desktop/.workbuddy/_devkit/wb-shim-host.mjs --check netstat -ano | grep -E "TCP\s+127\.0\.0\.1:20090\s" | grep -i LISTENING # ① 唯一判据 curl -s --noproxy '*' http://127.0.0.1:20090/__device_access/selfcheck # ② present:true curl -s --noproxy '*' -o /dev/null -w '%{http_code}\n' http://127.0.0.1:20090/api/v1/health # ③ 期望 200 ``` **判据 ④ 的复跑(需**设备属主的门户会话**,⛔ 本线不代取)** ```bash # 前提:把 换成设备属主本人的门户会话 Cookie(⛔ 不要探测登录接口:查无账号会 auto_register) curl -k -s -o /dev/null -w '%{http_code}\n' \ -H 'Cookie: sid=' \ 'https://ai1net.com/u/bdf89014-c66f-4060-a2df-995d8c18a951/desk/d-bdf89014-c66f-4060-a2df-995d8c18a951-626dc095/api/v1/info' # 期望:200(设备侧已自证:经垫片同路径 = 200)。⛔ 不要再期望 503 instance_starting。 ``` --- ## 8 边界自检(逐条) - ✅ ⛔ **未改 A 方案的通道设计**(尤其**未去掉"探活通过才采纳"**);⛔ **未让口令落盘**(未改回 C 方案)。 - ✅ ⛔ **未动看板 `127.0.0.1:8788`** —— 收尾复核仍在听、pid 仍 `19160`。 - ✅ ⛔ **未动中继客户端 `DSH-Overlay-Node-Dev`** —— 收尾复核 `State=Running`(未重启、未改其定义);本轮只**只读**读过它的 XML 作模板。 - ✅ ⛔ **未起 Electron UI**、⛔ 未弹任何窗口。 - ✅ **权限只收窄**:垫片 `20090` 与 host 自带 web `19387` **都只绑 `127.0.0.1`**;未开任何对外口、未改防火墙。 - ✅ **未碰**官方树(`E:\github\dsh-desktop-0.1.7rc2` 只读;未改其任何文件)· 平台仓(`D:\github\dsh_shenxian` 只读)· 生产服务器 47。 - ✅ **未 commit / 未 push** 任何仓库;⛔ 未启用退役任务 `DSH-Worker-Dev`。 - ✅ **WorkBuddy 未受影响**:未 kill / 未 restart / 未改其配置;主进程 `WorkBuddy.exe#3176` 与网关 pid `56072` 全程在场。 - ✅ **口令全程未读、未打印、未落盘**(新记录文件只有"在场/长度/键名"这类非密信息)。 - ⚠️ 一处**如实登记**:`WorkBuddy.exe` 现为 **15 个进程**(09-29 记录的基线是 13)。本轮**未杀任何进程**,差值与轮内多会话/自动化并行有关,⛔ 不作因果判断。 --- ## 9 本轮产出 / 复跑件 | 文件 | 用途 | |---|---| | `.workbuddy/_devkit/wb-shim-host.mjs` | **新增**:垫片的 **headless 常驻入口**(普通 node 直起 host + 计划任务管理 + 常驻监督 + 自恢复;幂等闸;⛔ 只打印口令的在场/长度/键名) | | `.workbuddy/_devkit/wb-shim-host.cmd` | **新增**:计划任务动作的 ASCII 包装 | | `.workbuddy/_devkit/_taskxml/DSH-Client-Dev.xml` | **更新**:任务动作改指新入口(UTF-16LE + BOM;⛔ 无秘密值) | | `.workbuddy/_devkit/_devlogs/shim-host-task.jsonl` · `shim-host-*.log` | **新增**:机器记录 + host 日志(含"计划任务上下文 `envPresent=false`、父链 svchost"的铁证) | | 计划任务 `DSH-Client-Dev` | **改指**(`State=Running`;登陆时触发) | | `交付物/回报-桌面线-垫片常驻化-20260930.md` | **新增**(本件) | | 技能 `dsh-local-env`(用户级) | **更新**:新增详情档 **§6c「headless 主机 + 计划任务常驻」**+ 主干 §1 一行指针(v1.0.2 → **v1.0.3**) | **技能**:本轮加载 **`dsh-local-env`**(命中:任务正是"在 Windows 本机把官方 dsh 跑起来并取证")—— 其 §6b 覆盖了"opt-in 回环口",本轮把**起法与常驻**这一缺口补成 **§6c** 并回写主干。 另:`agent-operating-rules`(作业总规矩)与 `multi-session-collab`(抢锁/收口)按既有常驻纪律执行,未逐条加载。 --- ## 10 给主会话的一句话 **V1 的最后一跳已打通**,且**起法已挂在 Windows 计划任务上**(不是任何会话)—— `20090` LISTENING + `credential.source=inbound / present=true` + 经垫片 `health=200` + **父链 `… ← svchost`、`workbuddyAncestor=null`** 四条齐了。 唯一**留给你的**是判据 ④ 那道"门户会话 Cookie"的枪(本线不代取凭据):设备侧同路径已自证 200,平台侧一旦复跑应不再是 `503 instance_starting`。