# DeepSeek Harness Desktop / CLI 接入

## 两个安装入口

### SearchBoost TUI

运行 `search-boost`，在默认平铺首页选择首次配置向导，或「管理 Agent 接入 → 安装 / 卸载」（文件夹模式先进入「安装与接入」）。交互安装选中 DSH 且未指定 profile/surface 时，会进入 **Desktop / CLI / All** 选择页；即使 Desktop 未被自动发现，也可以手动选择 Desktop。卸载仍只在发现 Desktop、未指定 profile/surface 时提供选择页。

- **Desktop**：`$DSH_HOME/profiles/desktop`，通过桌面安装附带的命令操作。现有持久 SearchBoost 安装使用绝对本地包路径，避免再下载一份；临时 `_npx` 缓存不能用作持久链接，改用当前精确 npm 版本。
- **CLI**：安装默认 `web` profile，`--profile` 可指定其他 CLI profile。与 Desktop 一样优先复用当前持久包路径，包括从本地 tarball 安装到 `node_modules` 的开发包，不以同版本 npm 包替换它。卸载只处理选中的 CLI profile，或所有已登记 SearchBoost 的 CLI profiles。
- **All**：分别管理 Desktop 与 CLI，分别报告成功/失败；一个失败不会掩盖另一个的结果。

选择安装 Desktop 后，还有两种方式：

- **自动安装（默认）**：保留注册表 / 默认目录 / PATH 探查，调用 Desktop 自带的 launcher 和 pnpm。启动 Desktop 一次初始化后，完全退出（包括托盘）再安装。
- **本地目录接入**：由用户在运行中的 Desktop「插件 → 添加插件」粘贴当前 SearchBoost 包目录。所有其他目标（包括 CLI、Grok 原生插件及其确认流程）执行结束后，最后单独显示完整绝对路径、步骤、等待状态和检测的 Desktop profile。不缩写成 `~`，不提供 `cli.mjs` 或 profile 目录作为安装来源。

安装时 Desktop 排在最后；其他目标失败也不会跳过它。卸载顺序和已确认范围不受影响。`--yes` / 非交互命令行仍使用原自动安装接口，不启动需要用户操作的等待流程。

本地方式每秒只读检查 profile 中的本地依赖来源、安装包版本 / bundle 元数据及当前发布文件内容。包写锁、包进程记录或未恢复的事务存在时不判完成；两个连续稳定结果后再确认。Desktop 的应用锁允许存在，因为应用必须运行。若能找到内置 launcher，还会通过 Desktop 自有运行时只读验证实际解析来源；失败或遮蔽副本不会降级为成功。同一个安装指纹的失败探针最多 3 次，采用封顶 30 秒的递增退避；仍失败会提前报告“运行时验证未通过、接入未完成”，不反复启动探针直到整个 15 分钟期限。已知失败后 launcher 消失也不降级为成功，需检查运行时/来源后手动重试。找不到 launcher 时仅报告「保存的本地安装已核对，运行时解析/加载未验证」，不声称插件已经加载。

已安装但禁用也算完成保存的安装，会明确提醒启用。若明确指定 `--enable-dsh-bundle`，此方式等待用户在 Desktop 保存启用选择，不由 SearchBoost 修改运行中的 profile。现有同源、同版本、同载荷安装可以直接通过检查；旧版本、其他来源及同版本旧代码不能通过。实际生效仍以 Desktop 显示 / 重启为准。

Esc / Ctrl+C 或关闭输入仅停止本次等待；15 分钟超时也会标记 Desktop「未完成」，保留其他目标的结果，不报告整次安装成功。取消等待不会取消 Desktop 正在进行的安装。可以检查 Desktop 后再次运行接入以重新检测。dry-run 只预览步骤和目录，不等待、不执行宿主命令、不修改 Desktop profile。

本地链接必须指向持久目录。通过 `_npx` 临时缓存运行时，选择本地方式会在目标安装前拒绝，要求先把当前版本安装到持久位置后重试；不把缓存路径注册到 Desktop，不以同版本注册表下载替换当前代码。

命令行等价入口（自动方式）：

```sh
search-boost install -t dsh --dsh-surface desktop -y
search-boost install -t dsh --dsh-surface cli --profile web -y
search-boost install -t dsh --dsh-surface all -y
# 明确启用此前已安装但禁用的 bundle（不带此参数则保留禁用）：
search-boost install -t dsh --profile web --enable-dsh-bundle -y
search-boost uninstall -t dsh --dsh-surface desktop -y
search-boost uninstall -t dsh --dsh-surface cli -y
search-boost uninstall -t dsh --dsh-surface all -y
search-boost print dsh --profile desktop
```

不指定 surface 的非交互安装保持旧行为：默认 CLI `web`；`--profile desktop` 明确选择 Desktop。未限定 profile/surface 的卸载处理所有已有 DSH 登记。安装和删除均不删除 profile、会话、用户 patch、其他插件或 SearchBoost 凭据。

### Desktop 内添加插件

在 **插件 → 添加插件** 输入：

```text
search-boost
```

Desktop 自带的 pnpm 从所选 npm registry 解析默认发布版本，安装到 Desktop profile；下载和安装不依赖系统 npm/pnpm。若要指定版本，可输入 `search-boost@<version>`。完成后选择启用，按宿主提示重启。

已安装 SearchBoost 且要直接复用其代码时，在同一输入框粘贴 **SearchBoost 包根目录的绝对路径**，不是 `cli.mjs` 文件，也不是另一个 profile 目录。`search-boost print dsh --profile desktop` 会显示当前包路径。本地链接依赖该目录持续存在；不要把会删除的项目依赖目录或临时 dlx 缓存当作持久安装。删除/移动来源后需重新接入；npm 包名入口的独立副本不依赖这个外部目录。

包名入口不会自动查找或安装全局 npm SearchBoost。它可能安装独立代码副本；同一操作系统用户、相同环境覆盖下，两种入口仍共用 `~/.search-boost` 的配置、密钥和开关。Windows 与 WSL 是不同的宿主环境，不能据 Linux HOME 推断 Windows 配置路径；请在目标 Desktop 所在的系统运行安装命令。

## Desktop 的所有权与检测

Desktop 独占自己的 profile 和包管理状态。先运行应用一次初始化 profile，然后 **完全退出应用（包括系统托盘）**，再从 SearchBoost 安装/删除/更新它的插件。

SearchBoost 只读取 profile/安装路径，不为检测启动 Desktop，也不创建 Desktop profile。原生平台发现位置包括：

- Windows：先只读查询当前用户 / 整机卸载注册表（包括 `WOW6432Node`）中 DeepSeek Harness 的安装记录，使用 `InstallLocation`，缺少时从 `DisplayIcon` / `UninstallString` 提取安装目录，识别其他盘符、中文及带空格的自定义目录。仅提取路径，不执行注册表中的命令。自动发现只接受本地盘符绝对目录，不探查 UNC 网络共享；未知变量仍拒绝，字面 `%` 保留。登记目录优先于默认目录，且要求同级应用可执行文件存在；不存在的启动器会跳过。原始查询结果（含失败）缓存 10 秒，路径变量与文件存在性每次重新检查，避免每个状态行启动 PowerShell，同时允许随后安装/移动的 Desktop 被重新发现。再查用户 `LOCALAPPDATA/Programs/DeepSeek Harness/resources/runtime/cli/bin/dsh.cmd` 及可用的 Program Files 路径。注册表查询超时、被拒绝或不可用时，仍继续默认目录和 PATH 发现，不修改注册表。
- macOS：`/Applications/DeepSeek Harness.app/Contents/Resources/runtime/cli/bin/dsh` 和用户 `~/Applications`。
- PATH 中指向 Desktop `resources/runtime/cli/bin` 的命令；macOS 登记的符号链接也可解析。
- 未登记的便携/移动安装，或注册表不可读时，可设置 `SEARCH_BOOST_DSH_DESKTOP_COMMAND` 为 Desktop 自带命令的绝对路径。此覆盖优先于所有自动发现；设置后不查询注册表，路径无效时不会改用其他安装。

`desktop` 是保留 profile，不能通过 CLI surface 操作。CLI 别名目录或单独 manifest 通过符号链接、junction、硬链接指向 Desktop 时，也会在宿主执行/备份之前拒绝；链接形式的 canonical Desktop profile 仍会正常发现。路径身份使用 realpath 及可用的设备/inode，不宣称能识别所有容器/overlay 映射。

缺少 Desktop 命令、未初始化的 profile 或现存应用锁会阻止实际操作，**不会回退到 npm DSH，也不会擅自删除锁**。一个陈旧锁也不会由 SearchBoost 自动接管；先检查宿主状态。

若 PATH 实际选中的 `dsh` 就是 Desktop 自带 launcher，普通 CLI profiles 也直接使用它及内置 pnpm，不要求系统 pnpm/npm。仅仅检测到另一个 Desktop 安装，不会替换 PATH 上优先选中的独立 CLI。

## 安装验证与禁用状态

SearchBoost 管理的事务安装要求**所选宿主自身**提供 `@deepseek-ai/dsh-plugin-manager/operations`、`dsh-atomic-write` 和官方 bundle resolver；已按 DSH `0.2.0-rc.2` 的接口验证。旧 `0.1.5-rc.3` 缺少事务 API，不能继续使用此前的非事务安装路径。缺少 API 时会在包管理/备份前明确拒绝，请自行升级对应 CLI 或 Desktop 宿主；SearchBoost 不自动升级宿主、不切换到另一个宿主，也不回退到无回滚安装。版本号本身不能代替 API 检查。选择 All 时，旧 CLI 缺少 API 会单独报告 CLI 失败，新版 Desktop 仍继续使用自己的宿主完成安装；更新 Desktop 不等于更新独立 CLI。

安装检查、profile/升级发现、显式启用与卸载验证共同识别 `dependencies`、`devDependencies` 和 `optionalDependencies`。即使可选依赖处于禁用状态，也不会漏扫或把仍保留的依赖误报已删除；旧名称的可选依赖同样参与升级迁移。

退出码为 0、profile 中有新包，都不足以证明宿主会加载新代码。官方 bundle 解析器先查 DSH 安装目录，再查 profile；安装目录旁的旧 SearchBoost 可能遮蔽 profile 的新版。

SearchBoost 在选定 launcher 的运行时调用它自带的 `resolveBundleDir`，检查实际解析路径、精确版本、bundle 元数据和 adapter 文件，并要求实际路径与刚安装的载荷具有相同 realpath。成功时显示路径和版本；旧包、同版本但不同目录的宿主遮蔽副本、缺失解析器或未返回探针结果，都不能报验证成功。复制型安装还会校验当前包声明的发布文件内容与运行时 manifest 字段，不能只凭同一个 `0.2.4-beta.4` 版本号认定代码相同。冲突错误列出预期和实际路径/版本；请明确移除或更新宿主旁的冲突包后重试，SearchBoost 不自动改写宿主安装目录。

Desktop 验证复用官方 launcher 指向的应用二进制及 ASAR carrier，以 `ELECTRON_RUN_AS_NODE=1` 和显式 `--import` 运行一次性探针，不依赖打包 Electron 受限的 `NODE_OPTIONS`。非标准 Desktop launcher 布局会拒绝验证；覆盖变量仍须指向官方 bundled launcher。探针不会启动 profile 或导入 SearchBoost 工具。这是**下次启动的来源验证**，不是证明已有进程已热更新；代码变化仍需重启宿主。

持久本地链接依赖来源目录继续存在，CLI 与 Desktop 都一样。临时 `_npx` 缓存仍不能成为持久链接；精确版本 npm 替换只有在载荷内容与当前运行包一致时才通过，否则明确报未验证，并要求安装当前 tarball 到持久位置或发布新版本。修改代码后的 npm 发布必须使用新版本号，不复用已经发布的版本。

已存在但禁用的 bundle 更新后，报告“已安装并验证，但禁用”，仍算安装成功，不自动启用。可在宿主插件管理器启用，或明确使用 `--enable-dsh-bundle`；后者通过宿主的 manifest 锁与原子写入 API 修改选择，并在启用前解析实际 bundle patch。其他 bundle 的顺序、用户配置与 patch 保留。

## 更新与删除

TUI 管理 Agent 接入 → 刷新（选择精确范围），或 `search-boost refresh`（全部已有接入），发现已登记 SearchBoost 的 DSH profiles，并逐个选择其拥有者：Desktop 使用 bundled command，CLI 沿用普通 DSH / npm-exec 路径。不升级 Desktop 本身，不改变其他 profile 的宿主运行时。

更新同时验证依赖来源、profile 载荷与宿主实际解析的路径/版本及 adapter 文件；保留 bundle 顺序、启用/禁用选择和用户 patch。旧适配器依赖只在新包验证后清理。缺失命令或应用锁在 profile 备份/写入之前阻止同步；同一次刷新仍可能已完成其他接入，需按结果解决阻塞后重试同步；不自动更新全局 SearchBoost 软件包。

卸载在宿主命令成功后重新读取 manifest，拒绝“退出码为 0 但仍登记”的假成功。对于 optional 安装启用后遗留的 bundle 登记，确认所有直接依赖字段和 profile 包链接均已移除，再通过所属宿主的官方 manifest 锁和 `saveManifest` 只清除本插件登记，重新验证。不删除应用锁，不修复宿主全局副本。卸载不移除共享的 SearchBoost 安装和配置。

安装在所选宿主运行时中调用官方 `runProfilePnpm`，将 profile 文件、完整 `node_modules` 私有备份、包管理动作、载荷/解析器校验和恢复置于同一个官方 manifest 写锁内。Desktop 使用其 Electron 与 bundled pnpm；不改用系统 npm/pnpm。校验失败时恢复原文件和模块树，避免同版本旧 npm 载荷留在启用的 profile 中。

成功安装删除大体积备份；失败备份保留在 `$SEARCH_BOOST_HOME/backups/dsh-install-<UUID>`，`meta.json` 标明 profile、目录、版本、宿主 anchor，`files.json` 保存配置原字节。操作开始前写入 profile 的 `.search-boost-install-pending.json`。断电、强杀或 15 分钟外层超时可能阻止恢复完成；安装、启用、更新和卸载发现标记都会拒绝新操作，不声称已经回滚。官方 `.plugin-manager/run.json` 尚存在时亦先拒绝快照/写入，避免前一次 pnpm 进程仍在改动模块树。先停止仍存活的包管理进程、退出宿主，再根据标记/备份恢复文件与模块树，确认后再处理标记。不要直接删锁或标记后重试。自动故障恢复不等于抗断电的文件系统事务。

宿主原始 stdout/stderr 可能含认证信息，不复制进 SearchBoost TUI 日志/异常；失败报告退出码和操作状态。需要详细诊断时，在本机直接运行相同宿主命令，分享输出前先检查秘密。

## 搜索层并发写入

共享搜索层设置使用每次独有的私有临时文件和跨进程写锁，迁移与原子替换位于同一临界区。并发修改按写锁顺序保存，后完成的写入生效；锁等待最长 5 秒，超时明确报告冲突。失败写入清理自己的临时文件与锁，不破坏旧配置。此修复不改变插件启用状态按 profile 独立管理的规则。

## 验证与限制

```sh
npm run test:dsh-desktop
npm run test:install
npm run test:isolation
```

全部测试先加载 `scripts/isolate-tests.mjs`。Desktop 绝对路径发现额外使用指向测试目录的缺失命令覆盖，不能绕过 PATH 防护执行真实桌面版命令。测试覆盖本地接入的双语选择、Desktop 最后执行（含 Grok）、只读等待、稳定状态/来源/载荷校验、自动接口保留、取消/超时与终端恢复、dry-run 和运行时未验证说明，以及 Windows 自定义目录与注册表元数据解析、失效登记、默认目录回退、显式覆盖优先、非 Windows 不探查注册表、旧独立 CLI 失败时 Desktop 仍成功、双宿主安装/删除、旧版及同版本副本遮蔽、可选依赖发现/重装/卸载/旧名升级、同版本本地新包与缓存载荷校验、搜索层跨进程并发和失败写入清理、禁用成功状态与 TUI/非交互 CLI 显式启用、坏 patch 与锁拒绝启用、Desktop PATH 无系统 npm/pnpm、旧名升级、错误退出与假成功、缺失解析器/命令、dry-run、保留配置和诊断输出不泄密。模拟调用者文件及源码树由隔离门禁做内容快照。

手动等待不使用 Clack 0.10 的 spinner：它的输入拦截会在 Escape / Ctrl+C 时直接退出整个进程。等待页用状态变化日志和独立可清理的输入监听，保证能输出未完成汇总，并恢复终端模式。

Desktop 探针测试使用 Node 模拟应用二进制、目录模拟 ASAR，并测试清空 NODE_OPTIONS 后仍可验证；这些不是 Electron 真机或真实 ASAR 解析测试。这些模拟宿主回归不代表真实 Desktop 已加载新插件。Windows/macOS 命令执行需对应平台 CI / 真机确认。DSH 开发预览版接口仍可能变化。

## 设计依据

核实于 2026-09-30 官方 master：

- [Desktop 所有权与 bundled command](https://github.com/deepseek-ai/deepseek-harness/blob/master/apps/desktop/README.md#bundled-command-runtime)
- [Desktop 路径与应用锁](https://github.com/deepseek-ai/deepseek-harness/blob/master/apps/desktop/src/paths.ts)
- [Web/Desktop 添加插件界面](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-plugin-manager/README.md#installing-a-bundle)
- [插件管理、npm 与本地路径来源](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/boot/plugin-manager/README.md)
- [CLI 插件命令](https://github.com/deepseek-ai/deepseek-harness/blob/master/apps/cli/reference/README.md#plugin-management)
- [官方 installation-first bundle 解析](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/boot/app-boot/src/profile.ts)
- [Desktop carrier 与内置 pnpm](https://github.com/deepseek-ai/deepseek-harness/blob/master/apps/desktop-host/src/cli.ts)
- [Electron NODE_OPTIONS 限制](https://www.electronjs.org/docs/latest/api/environment-variables#node_options)

采用宿主原生入口，不增加全局 npm 引导层、安装生命周期脚本或另一套插件市场协议。
