# @onthewayli/dsh-plugin-path-completion [English](README.md) | 中文 为 DeepSeek Harness 的 Web 输入框提供 Codex 风格的 `@路径` 文件补全,以**树外(out-of-tree)插件**形式交付:在输入框输入 `@` 并继续输入时,触发菜单会列出当前会话项目目录中匹配的文件与目录——上下键、Enter、Esc 由 Harness 自带的输入触发菜单处理选择——选中的路径以 `@./相对路径`(含空格时为 `@"路径 带空格"`)落进草稿,发送时由 Harness 的提示词侧 `@路径` 展开读取成文件内容。 本插件把两侧放在同一个包里: - **宿主半**(`lib/index.js`)在 Harness webserver 上注册一个 HTTP 路由 `/path-completion/search`,返回有界、排序后的候选列表。它**从不在网络上传文件内容**——只传名称、类型与查询形态的路径。 - **浏览器半**(`lib/client.js`)在共享的输入触发管道(`ctx.inputTriggers`)上注册一个 `@` 来源,因此本包不含任何 UI 代码、React 或 CSS:候选由管道现有菜单渲染。 ## 安装 ```sh # 从 registry 安装(已发布的包) dsh plugin --profile web add -w @onthewayli/dsh-plugin-path-completion # 从本仓库的 checkout 安装 dsh plugin --profile web add -w file:/path/to/dsh-plugin-path-completion # 从 tarball 安装 npm pack && dsh plugin --profile web add -w ./onthewayli-dsh-plugin-path-completion-0.1.0.tgz # 从 git 仓库安装(需要 profile 的 allowBuilds 授权;请钉住 commit) dsh plugin --profile web add -w github:OnTheWay111/dsh-plugin-path-completion# ``` `dsh plugin` 会在 `~/.dsh/profiles/web` 里转发给 pnpm;由于本包声明了 `dsh.bundle`,它会被自动追加进 profile 的 `dsh.profile.bundles`,其 `cordis.patch.yml` 则插入自己的加载器行。之后需要**重启 Web 服务**:客户端模块系统按包名缓存元数据且进程内不过期,因此插件集合的变化要在重启后才生效。 卸载:`dsh plugin --profile web remove -w @onthewayli/dsh-plugin-path-completion`。 ## 配置 可在 `~/.dsh/profiles/web/cordis.patch.yml` 里为该行添加可选配置: ```yaml - id: path-completion config: # 候选搜索上限(此处为默认值)。 maxResults: 50 # 一次响应携带的候选数 maxVisited: 20000 # 一次查询最多访问的目录项数 budgetMs: 400 # 一次查询遍历的墙钟预算 ignoreDirs: [node_modules, .git, dist, build, ...] # 永不进入的目录 # 除回环外还允许调用该路由的裸 host[:port] 授权(局域网场景使用)。 trustedHosts: [] ``` ## 信任模型 只有以 GUI 自身来源抵达该路由的请求才会被应答: - `Host` 必须是回环授权(`127.0.0.1`、`localhost`、`[::1]`)或 `trustedHosts` 中的一项; - 携带 `Origin` 的请求,其授权必须与 `Host` 一致; - `Sec-Fetch-Site: cross-site` 一律拒绝。 因此,通过局域网地址访问浏览器时,在该授权被写入 `trustedHosts` 之前不会出现补全。该路由是只读的,只返回名称,绝不暴露文件内容。 ## 兼容性 | 插件 | Harness | Node | 依赖面 | |---|---|---|---| | 0.1.0 | 0.1.0-rc.5 或 0.1.x 系列中更新的版本(源码 checkout 或已发布的 `dsh` 均可) | >= 22.19 | 宿主侧 `ctx.webServer.register`;浏览器侧 `inputTriggers` 客户端服务及其 `PickOutcome.text` 分支;以及 Harness 自带的提示词侧 `@路径` 展开,负责在发送时读取被选中的文件 | 已验证的安装形态:`file:` 目录、预构建 tarball、以及基于源码 checkout 的 Harness profile——三者启动后 boot manifest 中都带有本插件的客户端条目,其路由均可正常应答。 升级可能使其失效的耦合事实: - Harness 的客户端与宿主**同版本发布且没有 wire 协议版本号**,因此 Harness 升级后可能需要重建本插件。两侧对它使用的接口做**结构化类型**声明(`src/index.ts`、`src/client/index.ts`),就是为了让不匹配在运行时以具名错误暴露,而不是在构建期对着未必安装的包报错。 - `@deepseek-ai/cordis` 是 peer 依赖(`>=4.0.0 <5.0.0-0`):插件由安装实例自身的 Cordis 加载。除此之外不 import 任何官方包——协作一律走 Cordis 服务。 - 浏览器半在 `inputTriggers` 缺失时抛出具名错误,而不是静默什么都不做。 - 菜单的分组标题取自 Harness 的 `slash.menu` 词条表,树外插件无法扩展它;因此该分组会以本来源的原始名(`files`)显示。 ## 已知限制 - 选中**目录**会插入 `@./dir/` 并关闭菜单;下一次按键会以该目录的子项重新打开它。Harness 只在输入时重新识别触发符,不在程序化插入时识别,因此"选中目录立即展开下一层"需要上游提供钩子。 - `@` 同时会列出 Harness 的会话引用(子会话),位于各自的分组中;因此只输入一个片段时,文件名与会话名会并排出现。 - 候选来源:相对查询以会话项目目录为基准,`~/…` 以操作系统主目录为基准,`/…` 使用绝对路径。忽略列表用于保持大仓库的响应速度,因此只存在于 `node_modules` 之类的名字必须逐层进入才会被列出。 ## 发布 三种分发形式都可用,按用户便利度递增: | 形式 | 发布动作 | 用户安装 | 是否需要构建授权 | |---|---|---|---| | npm registry | `npm publish`(或你的私有源) | `dsh plugin --profile web add -w @onthewayli/dsh-plugin-path-completion` | 不需要(预构建的 `lib/` 随 tarball 发布) | | tarball | `npm pack` | `dsh plugin --profile web add -w ./onthewayli-dsh-plugin-path-completion-0.1.0.tgz` | 不需要 | | git 仓库 | 推送仓库 | `dsh plugin --profile web add -w github:you/repo` | 需要——在 profile 的 `pnpm-workspace.yaml` 里把该包加入 `allowBuilds` 之前,pnpm 会拒绝运行 git 依赖的 `prepare`;请钉住 commit | `files` 列出了 `lib/`、`cordis.patch.yml` 与两份 README,`prepublishOnly`/`prepare` 负责重建产物——若没有这份显式的 `files` 清单,`lib/` 会从 tarball 中消失(它被 gitignore 了),装出来的包将无法加载。发布前用 `npm pack --dry-run` 核对:清单里必须出现 `lib/index.js` 与 `lib/client.js`。 请以自己的名义发布:`@deepseek-ai/` 作用域属于 Harness 项目。发布前先确认名字可用(`npm view version`),因为 registry 上已经出现了不少插件包。 npm 现在要求发布请求必须满足 2FA:要么带上验证器动态码(`npm publish --otp= --access public`),要么(脚本化/CI 发布)使用**启用 "Bypass two-factor authentication" 的 granular access token**(npmjs.com → Access Tokens → Generate New Token → Granular)。普通的经典 `npm_` token 发布时会被 403 拒绝。 ## 开发 ```sh # 需要 tsdown 与 typescript;可把 node_modules 指向某份 Harness checkout(或正常安装它们): # ln -s ../deepseek-harness/node_modules node_modules # # 被软链的 node_modules 携带的是按安装时架构下载的 rolldown 原生绑定(如 darwin-x64), # 构建/打包须使用同架构且 >= 22.19 的 Node(tsdown 要求 ^22.18 || >=24); # 本机对应的是 nvm 中 22.23.2 的 x64 构建: # nvm use 22.23.2 && node_modules/.bin/tsdown node_modules/.bin/tsdown # 构建 lib/index.js 与 lib/client.js node --experimental-strip-types --test tests/plugin.test.ts # 自包含测试,无第三方依赖 ``` `dsh plugin add` 会把本包的一份**拷贝**装进 profile(pnpm 对 `file:` 的处理方式),所以已安装的 profile 不会自动感知重建结果:构建后要重新执行一次 `add`,并重启服务(客户端包元数据按名称在进程内缓存)。 浏览器 bundle 必须保持 Harness 客户端插件的形态:CommonJS 主体包在 `window.__ModuleLoader__.load({ id, factory })` 里,平台模块(React、cordis、共享 UI 包)留作外部依赖,其余全部内联——树外 bundle 不能以值的形式 import 另一个插件的客户端模块。