Back to home

AFAP

dsh-input-file-ref

输入框文件引用插件:在 DeepSeek Harness Web GUI 输入框里像 Kimicode/Cursor 那样,输入 @ 弹出可搜索、可逐级下钻的文件选择面板,选中后把 @相对路径 回填进输入框(保留 @、末尾一空格),让模型按路径读取该文件。

Stars
1
Language
JavaScript
Created
Aug 16, 2026
Updated
Aug 16, 2026

Introduction

输入框文件引用插件 · Input File Reference

中文 · English

在会话输入框输入 @,像 Kimicode / Claude Code / Cursor 一样弹出当前工作目录的文件选择面板,选中后把 @相对路径 回填进输入框,模型即可按路径读取该文件内容。DeepSeek Harness Web GUI 输入框新增一个"文件"@ 引用源:可搜索、可逐级下钻、与输入框同宽的选择面板,回填保留 @ 并自带末尾空格(自动停止再次触发选择)。

License: MIT


1. 它解决了什么问题

dsh 输入框已有 @ 触发管线(dsh-client-ui-input-trigger),但只支持技能(skill)和子代理(subagent)两类引用源。想引用工作目录里的某个文件时,没有对应的 @ 源。

本插件新增一个可搜索、可逐级下钻的文件选择面板替换默认窄菜单,数据由宿主半部提供:

dsh-input-file-ref (node half, lib/index.js — 零外部依赖,仅 node 内置)
  GET /api/input-file-list?sessionId=…&dir=<rel>     — 浏览模式:单层列目录
  GET /api/input-file-list?sessionId=…&query=<text>  — 搜索模式:递归 + 排除忽略目录
    ├─ 定位会话日志($DSH_HOME/sessions 目录树)
    ├─ 读日志首条 session 记录 → 得到会话 cwd(绝不信任客户端传来的路径)
    └─ 目录/文件相对路径 + 大小 + mtime(不读取文件内容)
          │
          ▼  (浏览器 fetch,同源)
  文件选择面板 (client half, conversation.input.overlay) ── 顶部搜索栏 + 逐级下钻
    ├─ 与输入框同宽;默认只列 50 条,输入更多关键字显示更多
    ├─ 文件夹可一级级进入(含 .git / node_modules / target;搜索会排除它们)
    └─ 选中 → 保留 @ 并回填全路径(末尾一空格)──发送──► 模型读取该文件

只读:本插件不修改任何文件。宿主半部只读取会话日志头部与文件系统元信息(路径/大小/mtime),不读、不写、不删除任何文件内容。

2. 功能特性

  • ✅ 输入 @ 弹出与输入框同宽的选择面板,顶部带搜索栏。
  • ✅ 顶部搜索栏:输入关键字即时递归搜索文件(大小写不敏感子串匹配;继续输入显示更多结果)。
  • 逐级下钻文件夹:点文件夹一层层进入;.gitnode_modulestarget 等目录支持一级级往下选,但不出现在搜索结果里(性能更好)。
  • ✅ 文件行显示相对全路径,过长时中间用 省略
  • ✅ 默认只展示 50 条;输入更多关键字后逐步放宽到 100 / 200 / 500,减轻展示压力。
  • ✅ 键盘(↑/↓/Enter/Esc)与鼠标均可选择。
  • ✅ 选中后,保留 @ 并把文件相对全路径以普通文本回填到输入框(末尾一个空格,示例 @src/main.ts ,可直接编辑、删除、连续引用多个文件)。末尾空格让 @ 不再触发选择,可继续正常输入。
  • ✅ 发送后,消息中带有所引用的相对路径文本,模型可用自己的 fs 工具读取(沙箱以会话 cwd 为根)。
  • ✅ 未选择工作目录的会话:面板显示提示,不崩溃。
  • ✅ 与现有 @ 技能/子代理引用共存。
  • ✅ 中英双语界面文案,跟随界面语言。
  • ✅ 只读、零外部依赖、浏览器信任围栏(防 DNS 重绑定 / 跨站)。

MVP 暂不支持:不内联文件内容到消息;不做文件预览/语法高亮;不做多选批量。

效果预览

快速筛选文件(搜索 + 50 条上限,输入更多显示更多)选中文件回填(@相对路径 保留 @ + 末尾空格)
快速筛选文件选中文件回填

3. 目录结构

dsh-input-file-ref/           # 仓库根 = npm 包根
├── package.json              # dsh.bundle.patch + dsh.client(浏览器端声明)+ exports["./client"]
├── cordis.patch.yml          # 组合行:inject webRuntime + trustedHosts 配置
├── LICENSE                   # MIT
├── .gitignore
└── lib/
    ├── index.js              # 宿主半部:/api/input-file-list(浏览 dir / 搜索 query)+ cwd 解析(零依赖)
    └── client.js             # 浏览器 bundle:'@' 文件源 + 自定义文件选择面板(insert + codec 路径)

4. 快速开始

一键安装(GitHub):

dsh plugin --profile web add github:AFAP/dsh-input-file-ref

然后重启 dsh web 生效。

安装后插件位于 $DSH_HOME\profiles\web\node_modules\dsh-input-file-ref(pnpm 从 GitHub 克隆),与源码仓库位置无关。

升级:

dsh plugin --profile web update dsh-input-file-ref

卸载:

dsh plugin --profile web remove dsh-input-file-ref

从源码目录手动安装(等价验证用)

dsh plugin --profile web add "D:\path\to\dsh-input-file-ref"

验证是否加载成功

新建/打开一个已选定工作目录的会话 → 在输入框输入 @ → 出现"文件"分组的文件列表即可。

5. 使用

  1. 选定工作目录:会话需要有工作目录(cwd)。
  2. 在输入框输入 @ → 弹出与输入框同宽的文件选择面板。
  3. 顶层 = 浏览当前目录:直接看到当前目录的文件与子文件夹;点文件夹可逐级进入(.gitnode_modulestarget 等也会显示,可一级级往下选)。
  4. 顶部搜索栏(也可直接在输入框 @ 后继续输入):输入关键字即时递归搜索整个 cwd;默认只显示 50 条,多输入几个字符会显示更多(逐步放宽到 100 / 200 / 500)。
  5. 返回上级:当前进入某文件夹时,列表顶部有"↑ 上级目录",或按 Backspace 返回。
  6. 用 ↑/↓ + Enter(或鼠标点击 📄 文件行)选择文件 → 保留 @ 并把文件相对全路径以普通文本回填进输入框(末尾一个空格),例如 @src/main.ts
  7. 发送后,消息中带有所引用的相对路径,模型可用 fs 工具按该路径读取内容。
  8. 删除/修改:回填的是普通文本,直接当作普通文字编辑或删除即可;末尾空格会让 @ 停止触发选择;可连续引用多个文件(每次输入空格 + @ 重新打开选择)。

未选择工作目录的会话:输入 @ 时面板显示"当前会话没有工作目录"提示,不会崩溃。搜索时 .gitnode_modulestarget 等目录里的文件不会出现(但可以逐级进入选择)。

6. API 速查

# 浏览模式:列出 dir(子路径,""=cwd)下的单层目录与文件
GET /api/input-file-list?sessionId=<sessionId>[&dir=<relative-dir>]

# 搜索模式:递归搜索整个 cwd(排除 .git/node_modules/target 等忽略目录)
GET /api/input-file-list?sessionId=<sessionId>...&query=<text>
  • 需要浏览器信任围栏通过(loopback / trustedHosts + 同源校验),否则 403
  • 非法/未知 sessionId 返回 404(错误,而非任意路径内容)。
  • 缺失 sessionId 返回 400
  • 非法 dir(含 ..、绝对路径、反斜杠、盘符)返回 400;目录不存在返回 404

浏览模式响应示例(dir=src):

{
  "mode": "browse",
  "cwd": "D:/workspace/MyProject",
  "dir": "src",
  "noCwd": false,
  "truncated": false,
  "dirs": ["util"],
  "files": [{ "path": "src/main.ts", "size": 2384, "mtime": 1719300000000 }]
}

搜索模式响应示例(query=main):

{
  "mode": "search",
  "cwd": "D:/workspace/MyProject",
  "noCwd": false,
  "truncated": false,
  "files": [{ "path": "src/main.ts", "size": 2384, "mtime": 1719300000000 }]
}

无工作目录的会话:

{ "noCwd": true, "files": [] }

7. 配置项

默认值说明
trustedHosts来自 webRuntime(loopback + LAN + --trusted-host浏览器信任围栏的非回环授权主机列表。
ignore.gitnode_modulestarget.venv 等(见下)搜索模式下跳过的目录名列表(浏览下钻仍可进入)。默认忽略 .gitnode_modules.venvvenv__pycache__.next.nuxtdistbuildtarget.idea.vscode.DS_Store

ignore 可在组合层覆盖:在 cordis.patch.ymlconfig 下追加即可(默认已内置,通常无需改动)。

8. 日志与排错

现象排查方向
输入 @ 无反应或无文件面板确认 dsh web 已重启、插件是否加载;技能/子代理的 @ 分组是否仍在(共存正常)。
面板一直"正在加载…"打开开发者工具 Network,检查 /api/input-file-list 状态;403=信任围栏拦截、404=会话未找到。
面板提示"当前会话没有工作目录"该会话未设置 cwd;请先选择工作目录。
搜索结果不全 / 找不到某些文件它们可能在 .git/node_modules/target 等忽略目录里;请用浏览模式一级级进入选择。
只显示了 50 条这是默认上限;输入更多关键字即可显示更多结果。

9. 安全与合规(务必阅读)

  • 只读:本插件不修改、不删除任何文件;宿主只读取会话日志头部与文件系统元信息,不读取文件内容。
  • 路径红线:cwd 由会话持久化头部解析(会话创建时的绝对路径),绝不信任客户端传来的任意路径sessionId 在拼路径前做单段转义,杜绝目录穿越。
  • 浏览器信任围栏/api/input-file-list 仅接受 loopback 或声明 trustedHosts 的同源请求,拒绝 sec-fetch-site: cross-site 与 Origin 不同的请求,防 DNS 重绑定与跨站。
  • 信息最小化:仅返回相对路径、文件名、大小、mtime;不返回绝对路径,不返回文件内容。
  • 会话定位仅限本机:文件列表只服务本机会话日志,不对外暴露任意目录浏览能力。
  • repo 不携带私密信息:无密钥、无本机绝对路径(文档用占位符 <your-project>)。

10. FAQ

  • Q:插件会读取文件内容吗? A:不会。宿主只列元信息;选中的相对路径以文本回填,模型在收到后用自己的 fs 工具读取内容(沙箱以会话 cwd 为根)。
  • Q:可以引用多个文件吗? A:可以。重复打开 @ 面板选择,每次回填一个保留 @ 的相对路径(末尾一空格),可连续引用多个。
  • Q:搜索时找不到 .gitnode_modulestarget 里的文件? A:这些忽略目录默认不进搜索结果(性能考虑)。请在浏览模式下点击文件夹一层层进入,即可看到并选中它们里面的文件。

11. 开发与构建

纯 JS 无构建步骤(无需 GitHub Actions workflow)。分层:

  • lib/index.js:宿主半部,零外部依赖(仅 node: 内置),因此无论以 git / registry / file: / link: 哪种方式安装都不会出现模块解析失败。
  • lib/client.js:浏览器 bundle(经典脚本,window.__ModuleLoader__.load),注册 @ 文件引用源 + 自定义文件选择面板(conversation.input.overlay)。

12. 相关文档

  • 输入框引用契约:@deepseek-ai/dsh-client-ui-input-trigger/lib/types/types.d.ts
  • 参照实现:技能 /@deepseek-ai/dsh-client-ui-skill、子代理 @@deepseek-ai/dsh-client-ui-subagent、exact 路由范例 dsh-token-usage

13. License

MIT © AFAP