dsh-artifact-linker

uitested: passnpmMIT

Turns every file artifact the agent names in chat into a clickable entry — and refuses to paint one that does not resolve. Hover resolves asynchronously, and only a file that really exists becomes a hand pointer with a dashed blue outline (30 s TTL cache, so repeated hovers cost nothing); confirmed

下载量 / Downloads
2,310
依赖体积 / node_modules
457.31 MB
测试时间 / Tested
2026-11-06
DSH 版本 / Version
0.2.2-rc.1

Install

dsh plugin --profile web add dsh-artifact-linker

Sandbox test results

StageResult
1 install✓
2 list✓
3 mount✓
4 boot—

Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).

README

dsh-artifact-linker · 产物路径联动器

让 DSH 交付的每一个产物,都能点一下就到位——不用再「复制路径 → 打开文件管理器 → 贴到地址栏 → 回车」。

[English](README.en.md) · 简体中文 · [安装](#安装) · [交互](#交互) · [配置](#配置) · [边界](#它不做什么) · [排查](#排查)


它解决什么

DSH 里预览器已经能开图片、代码、pdf、office。但插件、脚本、Fluent 出图、打包产物、临时导出这些文件,正文里只给一个名字,想打开它得走完四步机械劳动:

看到 "结果.xlsx" → 复制路径 → Win+E → 地址栏输入 → 回车 → 在一堆同级文件里找那一个

dsh-artifact-linker 把这条路压成 0 步:正文里的文件名本身就是入口。

它做两件互相咬合的事:

半边 · 做什么

宿主(提示词层) · 常驻注入一条「产物路径铁律」,要求模型把产物写成完整绝对路径、而给用户看的正文只写文件名——路径有据可查,正文又不被长串路径污染

客户端(渲染层) · 正文里的产物名变成可点目标:左键在右侧边栏预览(对齐官方交付卡片)、右键八项菜单(在侧栏打开 / 在浮窗中打开 / 在文件夹中显示 / 用默认程序打开 / 另存为 / 复制文件 / 复制文件路径 / 添加到对话框)。零常驻 UI、零扫描、零定时器

第 1 条管「模型把路径写对」,第 2 条管「用户点得动」。缺前者,路径经常是猜的、点开是死链;缺后者,路径再对也要人去复制。

长什么样

悬停到产物名上,会出现 Codex 风格的蓝色虚线框——这是唯一的视觉提示,正文一个字都不会被改写。右键弹出完整菜单:

想看实操录屏?点这里看效果视频(0.4.4 右键菜单演示)。


交互

正文里点一个文件名(或绝对路径):

操作 · 结果

左键 · 在 DSH 右侧边栏打开预览(和官方交付卡片同一条通道;完整绝对路径 0 次解析直通,侧栏认领不了的类型自动退回系统默认程序打开)

Ctrl / Shift / Cmd + 左键 · 用系统默认关联程序打开(Excel、PS、播放器……)

Alt + 左键 · 复制完整绝对路径到剪贴板

右键产物名 · 插件菜单,共八项(见下表)

右键非产物文字 · 完全放行,宿主原生菜单照常出现

悬停 · 只有确认文件真的存在才变手型、才画虚线

文件不存在 / 解析不到 · 静默放行或一次性提示,绝不假装成功

右键菜单

菜单项 · 作用

在侧栏打开 · 在 DSH 内置右侧边栏里打开,和聊天里点文件链接是同一条通道。0.4.0 起对所有文件类型一律显示这一项,不再按扩展名筛:能认领的查看器就渲染(PDF / Word / Excel / PowerPoint / Markdown / 纯文本 / CSV / 图片,装了 video-preview / audio-preview 等插件后还有音视频),认领不了的宿主会退回纯文本或默认程序。副作用是极少数类型点下去可能只得到一句「侧边栏打不开」——这是刻意接受的代价,为的是以后侧边栏补了新能力,本插件零改动自动跟上

在浮窗中打开 · 在一个独立浮窗里打开预览(增强项;浏览器或宿主拦截弹窗时改用「在侧栏打开」)

用默认程序打开 · 交给系统关联(资源管理器 / Excel / 播放器……)

在文件夹中显示 · 打开所在目录并选中该文件

另存为… · 复制到你指定的位置

复制文件 · 把文件本身放进剪贴板(CF_HDROP),可以直接粘到资源管理器、邮件、聊天窗口

复制文件路径 · 把真实绝对路径放进剪贴板

添加到对话框 · 把路径插入输入框,供下一条消息使用


路径怎么解析(三源兜底)

模型嘴里的话不可全信,插件按顺序试:

1. /workspace/<...> —— DSH 工作区的虚拟根,映射到真实 workspace 目录;

2. 原生绝对路径 —— E:\proj\out\a.xlsx 直接用(盘符和 UNC 都认);

3. 会话相对路径 —— 以会话 workspace 为基准拼接;

4. 各 workspace 根 —— 遍历所有已知的 profile / 会话 workspace,不只当前那一个;

5. 裸文件名 —— 深浅递归搜同名文件(跳过 node_modules/.git/dist/build/ 等),按「最近修改优先」+「目录深度浅优先」排序:

  • 命中 1 个 → 直接用;
  • 命中 多个 → 返回候选列表交给用户,绝不猜(猜错比不猜更糟);
  • 命中 0 个 → 若 allowOutside: true,再在磁盘上按名字做一次浅层搜索。

提到时若「提及吞掉了前面几个词」(结果#1_速度云图.png 只剩 #1_速度云图.png),会从左逐段收缩重试直到能命中。

缓存两层:stat 10 秒、同名索引 30 秒——避免重复打满磁盘,同时刚删掉的文件也能很快翻成「已不存在」。


安装

# 1. 放进 DSH 的本地插件目录
#    Windows: %USERPROFILE%\.dsh\local-plugins\dsh-artifact-linker

# 2. profile 里声明依赖(<profile> 换成 desktop / web / xiufu)
#    在 %USERPROFILE%\.dsh\profiles\<profile>\package.json 的 dependencies 里加:
#      "dsh-artifact-linker": "file:~/.dsh/local-plugins/dsh-artifact-linker"

# 3. 完全重启 DSH(客户端 bundle 不随 Ctrl+R 重载)
⚠️ file: 依赖的坑:file: / link: 指向会在 profile 重装依赖时把目录内容按快照恢复,本地改动可能被回滚。长期使用建议 npm pack 成 tarball 后用 file: 指向 tgz,或直接发到 npm。

装好后确认:

GET http://127.0.0.1:<port>/dsh-artifact-linker/info

正常应返回插件名、版本、workspace、以及 injectRules / clientChips / allowOutside 三个开关的实际生效值(这一条是排障时唯一可信的地方——别信 README,信 /info)。


配置

在 profile 的 cordis.patch.yml 里给本插件条目加 config(不要在 profile 里手写第二段 insert,同一 entry id 插两行会 duplicate loader entry id):

- id: dsh-artifact-linker
  name: dsh-artifact-linker
  config:
    injectRules: true    # 常驻 prompt 铁律
    clientChips: true    # 是否接管正文点击(false = 只留提示词注入,不动鼠标)
    allowOutside: true   # 是否允许解析 workspace 之外的路径

开关 · 默认 · 置 false / true 的效果

injectRules · true · false = 不注入 prompt 段,模型不再被要求输出绝对路径

clientChips · true · false = 纯注入模式,不接管任何鼠标事件

allowOutside · true · false = 只允许打开 workspace 内的文件

hoverDelayMs · 40 · 悬停判定的节流间隔(毫秒,上限 1000)。0 = 不节流;调大 = 更省 IO,但手型/高亮更迟钝

hoverDelayMs 说明:0.4.2 及更早把这条写死成 150ms,鼠标划过一行文字时高亮会明显慢半拍(150ms ≈ 9 帧),而且判定用的是「排程那一刻」的坐标,不是判定那一刻的坐标,所以还会出现落点不准。0.4.3 起改为可配、默认 40ms(≈2.5 帧,肉眼已跟手),并且每次 mousemove 都会刷新坐标,判定时读最新值。

allowOutside 要特别说一句:产物经常落在项目盘(E:\…、D:\…)而不是 DSH 的 workspace 里,所以默认放开。但它同时是安全闸——挡的是「模型说个 C:\Windows\System32\xxx.exe 就帮你点开」这类情况。自己一个人用保持 true 更顺手;给别人用、或者接入不可信模型,改成 false。


它不做什么

这几条是刻意的,不是没做完:

1. 不改任何 DOM 节点。正文由 React 渲染,给文本包一层 <span> 就是移动 React 的节点,重渲染时整段白屏。本插件全程只读,可点性是「捕获期 click + 光标位置反查」实现的。

2. 不接管 markdown 图片与链接语法。![](path) 一旦被改写成芯片,图就不显示了。

3. 不联网、零遥测。全部路由只在 127.0.0.1 回环可用,外部来源一律 403。

4. 不 spawn shell。reveal/open 走 execFile(cmd, args) 参数数组,路径里的 & | ; %VAR% 全是字面量,没有命令注入面。

5. 不猜多候选路径。搜到多个同名文件时把选择权还给人。

6. 不保证文件存在。库里的文件随时可能被删、被移走、被别的进程占用——所以每次打开前都重新 stat,点开发现没了就明说「已不存在」,不会装作没事。

7. 不给不存在的文件画芯片。正文里长得像文件名的片段很多(日志摘录、示例路径、报错原文),只看「形状」就画成可点芯片,点开只会得到一句「没解析到」——那比根本不认识更糟。所以判定加了存在性闸门:悬停时先异步 resolve,只有文件真的在才变手型、画蓝色虚线;确认不是产物的就当普通文字,右键直接放行宿主原生菜单。解析结果进 30 秒 TTL 缓存,反复悬停不重复请求。


排查

症状 · 原因与处置

正文文件名不可点 · ① clientChips 被置 false;② 扩展名是可执行体(exe/msi/dll/sys/so 等,这是唯一仍刻意不认的一类,见 lib/client.js 的 EXEC_EXT);③ 客户端 bundle 没重载——Ctrl+R 不重载客户端 bundle,必须完全重启 DSH

点开提示「解析不到绝对路径」 · 裸文件名不在 workspace 内,且 allowOutside: false。先确认文件真实路径,再决定是否放宽

点开提示「文件已不存在」 · 路径是真的,文件被删/移了。清单里该行会打上删除线

点产物名没反应 · ① 文件不在候选 base 下(访问宿主 /info 看 bases);② 直接看 /resolve?p=<名字> 返回的 reason。0.4.0 起不再有扩展名白名单,任何字母或数字开头的扩展名都会被识别为候选(7z/3mf 这类数字开头的也算),误报由「必须先真实解析到磁盘文件」这道存在性闸门兜掉

右键没出本插件菜单 · 该处文字没解析成文件(同上);对非产物右键本来就不接管,出的是宿主原生菜单。0.3.3 起会先确认文件存在——不存在的片段既不画虚线也不接管右键

日志里出现 N 份相同输出 · 热重载堆叠了多个实例。Ctrl+R 无效,必须完全重启 DSH;同时确认 window.__dshArtifactLinkerV1.dispose() 守卫在


目录

dsh-artifact-linker/
├── package.json          # dsh.bundle + dsh.client(两个都要,缺 bundle 装不上)
├── cordis.patch.yml      # 插件注册补丁(entry id = 包名,唯一)
├── lib/
│   ├── index.js          # 宿主:

…(truncated)

Rendered from the README published on npm. Formatted only, not edited.

Versions

Published on npm::13,最后更新 2026-11-05

Links

GitHub · Homepage · npm

Same category