## 十六、方案 A 落地:launch/status 自动携带 dsh launch token(2026-09-08 19:30) **决策**:用户选方案 A(小改上游,普通用户无需手动拿 token 即可打开聊天界面)。 **改动(3 文件,已 git commit 29c8907,本地分叉 patch)**: | 文件 | 改动 | |------|------| | `src/supervisor/spawner.ts` | `Instance` 接口加 `launchToken?: string` 字段 | | `src/supervisor/orchestrator.ts` | ① `trackChild` 监听子进程 stdout,正则 `dsh web: http://127.0.0.1:\d+/\?token=([A-Za-z0-9_-]+)` 解析 token 存入 instance;② 新增 `waitForLaunchToken()`(轮询最多 20s 等 token 出现,崩溃/停止即返回);③ `launch()`/`restartMain()` 返回前等待 token | | `src/web/routes/dsh.ts` | `dshUrl()` 支持可选 token 参数;launch/restart/status 返回的 `url` 均拼接 `?token=`(status 从 `main.launchToken` 取) | **验证结果(全链路)**: | 步骤 | 结果 | |------|------| | launch 返回 | `{launchToken, url: https://admin.dsh.alotbuy.com/?token=...}` ✅ | | 打开带 token url(带 sid,-L 跟随) | 200,24233B SPA,自动 303 种 dsh-auth cookie ✅ | | 之后裸开 `/` | 200 ✅ | | `/api/llm/listProviders` | `ok:true, deepseek-official` ✅ | **构建/部署**:`npm run build`(tsc)+ `systemctl restart dshs`。TS 坑:`RegExp.exec` 返回 `null` 非 `undefined`,判空须 `m !== null && m[1] !== undefined`。 **admin API Key 已配置**:从旧 `/opt/dsh/.env` 取出 `sk-81c...` 经 `/api/me/keys` 注入(name=deepseek-main,AES-GCM 加密入库,自动触发实例重启生效)。**KEY 为每用户独立**——每个用户需在桌面"管理密钥"各自添加;无全局通用 key(原容器 .env 统一 key 模型已废弃)。