10
September 16, 2026 · View on GitHub
1. 仓库与依赖策略
- 独立仓库(本目录或其 git 初始化),以 pnpm workspace 管理;上游
deepseek-ai/deepseek-harness作为 git 子模块或 pnpm 固定依赖 (推荐:子模块 + 锁 tag,理由见 ADR-005) - 所有与上游的耦合点收敛在极少文件(
apps层语义):shell/ipc-fetch.ts(载波)、shell/protocol.ts(协议/资产面)、shell/compat.ts(兼容层)、bundle/desktop/cordis.patch.yml(装配) - 镜像但不出包:上游源码检出只读;desktop 项目自身源码按
04-architecture.md §2布局(shell/、renderer/、compat/、bundle/、packages/)
2. 构建链路
① 自绘 renderer build(主面) vite build(renderer/)→ resources/ui [主面不依赖上游 dist]
② 兼容面 dist build(跟随 tag) pnpm run build (apps/web) → apps/web/dist → resources/legacy [由脚本产出,冻结基线]
③ shell 构建 electron-builder / 多步:main+preload 单包
④ desktop 插件构建(每包) tsdown(与官方同 preset 对齐)→ lib/ + client.js(若有 client 半)
⑤ 运行时组装 resources/(ui + legacy + deps)-> 打进包;deps 以 pnpm --prod 冻结清单
⑥ 开发工具 scripts/dsh-forge-dev(主面 vite dev server + shell --dev 直连)
- 开发模式(主面):
renderer/跑 vite dev server → shell--dev加载 dev URL(HMR 全量); 兼容面:--dev直读上游apps/web/dist(watch 由上游pnpm run dev:web提供;桌面兼容窗口复用client-hmr行, 注意:官方 web-app 默认 disabledhmr(TODO 注释),兼容面同样先禁后按需开) - 生产模式:
resources/ui+resources/legacy内嵌,零外部路径依赖
3. 运行方式(开发期)
pnpm --filter @lansi-ai/dsh-shell start -- --dev --profile desktop # 或直接 electron .
dsh --help 等价物:startup args: --serve={port} | --user-data-dir | --no-retry
- 多实例:单实例锁;调试可用
--user-data-dir隔离
4. 测试体系
| 层 | 工具 | 覆盖 |
|---|---|---|
| 单测 | vitest(对齐上游) | forge-api schema、IPC 信封编解码、host 插件 effect 清理 |
| 组件 | vitest + fixture | 自绘面组件(对话流/时间线/命令面板)与 forge-client-* 插件(无宿主) |
| 集成 | 内存 profile + InProcessApiClient | 桥→apiProxy→session 全链(对齐上游 carrier 测试思路) |
| 兼容层 | vitest + e2e | desktopRoutes 映射、SSE→帧、旧插件 host 半零改动(/rules/*、/terminal/run) |
| e2e | Playwright + _electron | 主面:启动→对话→命令面板→托盘→通知→协议唤起→崩溃恢复;兼容窗口:旧插件回归 |
| 静态 | oxlint / tsc 双程序(host/client 聚合,对齐上游 tsconfig 双 aggregate) | 方向纪律、类型安全 |
- 关键测试文件计划:
shell/test/ipc-fetch.spec.ts(象限完整性)、bundle/test/patch-invariants.spec.ts(desktop patch 与上游 web-app 行集合的差集校验)、packages/forge-client-*/test/*.spec.tsx
5. 与上游同步节奏
- 每 rc:
npm run upstream:check判上游新版(判据源 = GitHub releases),node scripts/upstream.cjs assess <version>评估拴合面破坏性,判定 safe 才npm run upstream:auto(自动 bump + install + typecheck/lint/build + 迁移登记;台账仍需人工同步,见workflow.md场景 D) - 强制门禁:耦合面文件的 diff 必须人工 review 后合入;
dsh--version基线校验进 CI - 破坏性变更登记:
docs/adr/adr-005.md挂的表 +docs/11-risks.md更新
6. 调试技巧
- 协议诊断:官方 envelope tap(
subscribeEnvelopes)→ 桌面版桥两端各留开关(--debug-wire) - Host 状态:
--dump-config等价物(boot前renderConfigDump)验证 desktop patch 合成结果;plugin-inventory行在 UI 侧可见桌面插件装载状态 - renderer:Electron devtools(仅
--dev开放);?fixture模式无宿主调试 UI - Windows 特例:toast/托盘 dev 行为差异(见 07§10);子进程闪窗根因在上游
dsh-subprocess-local(不修,规避或打补丁 upstream patch 到子模块 overlay)
7. CI(建议)
- 分支:main(可发布)→ rc/*(预览构建)→ ci 门禁:lint + typecheck + unit + integration + e2e(Win runner) + patch 差集 + 上游基线检查
- 产物:CI 产出安装包 + SHA256SUMS + 更新描述符(M4 起)
- 平台矩阵:Win x64 必跑;mac/linux 延后(M5)
8. 编码约定(对齐官方仓库纪律)
- 双面插件命名与
dsh.client声明、inject纪律同官方(见 dsh-terminal 先例) - 客户端包禁止 import 宿主运行时(只 type-import /client 子路径);跨包 value 协作走服务
- 错误用开放 code 字符串 + zod schema(对齐
RpcErrorDetailsMap思路) - 文档即成品的一部分:改动必须同步本文档集与 ADR
9. 发版流程(release 脚本)
一条命令完成「门禁 → 版本号 → commit/tag →(可选)打包与推送」,实现于 scripts/release.cjs(npm 入口 npm run release)。
# ① 演练:只打印将执行的命令,零写入
npm run release -- 0.1.1-alpha.6 --dry-run
# ② 本地发版:跑门禁 + bump + commit/tag,打印待推命令(不推)
npm run release -- 0.1.1-alpha.6
# ③ 完整发版:本地出 Windows 包并推送,触发 CI 双平台构建
npm run release -- 0.1.1-alpha.6 --local --push
--是必需的(npm 参数透传分隔符)。亦可直接调用node scripts/release.cjs <version> [选项]。
9.1 选项
| 选项 | 作用 |
|---|---|
<version> | 必填;合法语义化版本,且必须高于当前版本 |
--local | 本地打包(npm run dist → 产物名对齐 → 生成 SHA256SUMS) |
--clean | 打包前清理 release/ 中非目标版本的旧产物(需配合 --local) |
--push | 真实推送 main 与 v<version> tag,并 ls-remote 回验(坑 44) |
--skip-gates | 跳过 typecheck/lint/test/build 门禁(仅调试用) |
--dry-run | 只打印将执行的命令,不写文件 / 不提交 / 不打包 |
9.2 前置条件(不满足即中止)
- 工作区干净、当前分支为
main、v<version>tag 本地与远程均不存在、不落后origin/main(允许领先——未推的功能提交会随发版一并推送) --local涉及npm run dist,沙箱内必失败,需授权沙箱外运行(见坑 0 / 38 / 42)
9.3 与 CI 的衔接
- 推送 tag 后由
.github/workflows/release-win.yml/release-mac.yml在云端构建双平台产物并上传至v<version>Release(版本号含预发布段时自动打 pre-release) - 两个 workflow 上传前均调用
scripts/align-release-assets.cjs,把产物名对齐latest.yml的path(坑 41 根治,防自动更新 404) - 渠道描述符:同一脚本还会生成
latest.yml → rc.yml、latest-mac.yml → rc-mac.yml的逐字节副本,两条链的上传清单都必须带上(应用内「预发布渠道」固定请求rc.yml;缺它时正式版装机检查更新会直接抛 404,回退latest.yml只在allowPrerelease=true时成立 —— 详见 坑 75) - 发布命名约束:
rc渠道按 tag 的预发布段匹配版本(electron-updater的hrefChannel === currentChannel),故预发布必须用-rc.N;用-alpha.N的版本对应用内更新不可见。另外:正式版装机无法升到预发布版(allowPrerelease由当前版本推导),跨过去要手动装一次预发布包 - 发布后核验:匿名
HEAD https://github.com/lansi-ai/dsh-forge/releases/download/<tag>/<latest.yml 的 path>应返回 200(勿带 Authorization,公开端点带 token 反被 401);预发布版本额外核验rc.yml同样 200