npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lucasxingg/dsh-file-attach

v0.1.2

Published

Drag-and-drop PDF, Office, images, and text/code files into DSH conversations. The host extracts (and OCRs) them into the prompt; attach_* tools cover notebook cells, PDF-page OCR, image describe, and save.

Readme

dsh-file-attach

CI GitHub release License: MIT DSH plugin

dsh-file-attach 是一个由宿主端和网页端组成的 DSH 文件附件插件。它接受 配置大小限制内的任意非空文件,把支持的文档提取为模型可读文本,并将原文件 保存在会话工作区之外。所选模型明确支持图片输入时,PNG、JPEG、WebP 和 GIF 会进入 DSH 原生视觉通道;否则插件会上传图片并执行 OCR,还可附加一段文本描述。

不支持的二进制格式仍然可以上传,并可通过工具保存到工作区,但放入提示词的 提取结果只会说明“没有可用的文本提取器”。

插件增加了什么

  • 全页面拖放、图片粘贴、多文件选择器,以及 DSH / 菜单中的 上传文件… 入口。
  • 中英文标签、状态文本和上传错误通知。
  • 1 MiB 顺序分块上传、汇总进度和“正在提取”状态。
  • 与输入框同宽的附件栏,显示由插件上传的文件、大小,以及跟随输入框里蓝色 文件名的移除按钮。
  • 文本/代码、PDF、DOCX、XLSX、PPTX、Jupyter Notebook 和栅格图片提取器。
  • 根据当前模型能力,在 DSH 原生视觉与插件 OCR/文本提取之间自动路由。
  • 对用户隐藏的大段上下文:模型收到完整提取内容,对话界面只显示简短附件头。
  • 4 个模型工具:读取 Notebook 单元、对 PDF 单页 OCR、描述图片、保存原文件。
  • 按会话隔离的文件库、文件名和路径净化、上传限制、残缺上传清理及持久化提取 结果查询接口。

环境要求与安装

  • Node.js 22.13 或更高版本。
  • 带 Cordis 宿主和网页客户端插件系统的 DSH。

从 npm 安装到 web profile:

npx @deepseek-ai/dsh plugin --profile web add --allow-build=tesseract.js @lucasxingg/dsh-file-attach

包尚未发布到 npm 时,改从 GitHub 安装:

npx @deepseek-ai/dsh plugin --profile web add --allow-build=tesseract.js github:LucasXingg/dsh-file-attach

pnpm 10+ 会拦截依赖的构建脚本。OCR 用到的 tesseract.js 带有构建脚本,因此不加 --allow-build=tesseract.js 的第一次安装会以 ERR_PNPM_IGNORED_BUILDS 失败。 这不是安装成功。

如果已经遇到该错误,在 profile 里允许该脚本后再执行一次:

# ~/.dsh/profiles/web/pnpm-workspace.yaml
allowBuilds:
  tesseract.js: true

不要运行 add dsh-file-attach。那个未加 scope 的 npm 包名属于另一个插件 ([email protected]),不会提供拖放上传或 /attach。

也可以从 GitHub Releases 下载并解压,或克隆本仓库并在目录内执行 npm ci,然后把该目录加到 profile:

npx @deepseek-ai/dsh plugin --profile web add /path/to/dsh-file-attach

安装后重启 dsh web 并刷新浏览器;已运行的 GUI 不会热安装此插件。 cordis.patch.yml 会添加一条 file-attach 配置,npm 包同时暴露宿主入口 lib/index.js 和生成后的浏览器入口 lib/client.js。

添加文件的方式

| 入口 | 实际行为 | |---|---| | 拖放 | 捕获阶段的全页面监听器会接管所有文件拖放。没有活动会话时,它仍会阻止浏览器默认行为,仅在控制台警告,然后丢弃文件,不显示 toast。 | | 粘贴 | 只有剪贴板中至少含一张受支持的栅格图片时插件才接管粘贴;这次粘贴中的所有文件都会被路由,纯文本也会追加到草稿。只有非图片文件的粘贴交给 DSH 自身处理。 | | / 菜单 | 选择 上传文件… 打开没有文件类型过滤器的多文件选择器。 | | 附件栏 添加 按钮 | 打开同一个选择器。附件栏只有在一次插件上传开始后才出现,因此第一个附件请用拖放、粘贴或 / 菜单添加。 | | /attach <id> | 复用已知附件引用。同一页面生命周期内可以提交内存中缓存的提取内容;刷新后因文件名和大小元数据未持久化,只会提交降级的 ID 提示。 |

在插件上传路径中,客户端和宿主都会拒绝空文件及超过 maxFileBytes 的文件; 原生视觉图片的准入则由 DSH 自身负责。宿主只接受活动会话的插件上传。输入框 忙碌时(阶段既不是 plain 也不是 claimed),插件会提示稍后再试,而不会 开始上传。

同一批中走插件路径的文件会各自独立开始,客户端不会排队,也不会预先检查 maxConcurrentUploads。附件栏把所有进行中文件合并为一个汇总进度条。每个 文件发送最后一个请求时,标签会切换为提取状态,进度暂时使用约 95% 的估算值。 提取完成后,插件最多进行两次带草稿版本保护的尝试,把蓝色文件名插到草稿末尾 (就是输入框里可用退格键删掉的那个文件名)。如果插入失败,客户端会显示错误; 已完成文件仍保留在文件库中,附件栏会留下一条可关闭、但输入框里没有蓝色文件 名的记录。

已完成附件的 × 会从输入框里删掉对应的蓝色文件名;原文件仍留在文件库。 附件栏中的已完成条目跟随输入框:用退格键删掉那个文件名,或发送消息(草稿被 清空)后,该文件会从输入框上方消失。进行中的上传会继续显示,直到完成或失败。

图片路由

只有 PNG、JPEG、WebP 和 GIF 被识别为栅格图片。

  1. 客户端向宿主查询输入框当前选择的 provider/model。
  2. 宿主解析对应的模型元数据。
  3. 只有 inputModalities 明确包含 image 时才返回 visual: true。
  4. 视觉模型的图片交给 DSH 原生输入框图片栏。
  5. 模型能力未知、查询失败或纯文本模型都会回退到插件上传、OCR 和可选描述。
  6. 如果 DSH 原生图片 API 拒绝该批图片,也会回退到插件上传路径。

走原生视觉路径的图片不会进入插件文件库,没有 12 位附件 ID,不会显示在插件 附件栏中,也不能用于 attach_* 工具。混合选择时,图片走原生视觉,其他文件 仍由插件上传。

提取格式支持

| 输入 | 识别方式 | 发送给模型的内容 | |---|---|---| | 文本和源码 | text/*,或 txt、md、csv、json、html、css、js、ts、py、go、rs、java、c/cpp、sh、yaml、toml、sql 等已知扩展名 | UTF-8 文本;无效字节序列会被替换。 | | PDF | .pdf 或 application/pdf | 合并后的文本层;没有文本层时返回提示,引导模型调用 attach_pdf_ocr_page。 | | Word | .docx 或 DOCX MIME | Mammoth 提取的纯文本;不支持旧版 .doc。 | | Excel | .xlsx 或 XLSX MIME | 工作表标题和以制表符分隔的单元格值;不支持旧版 .xls。 | | PowerPoint | .pptx 或 PPTX MIME | 从幻灯片 XML 提取的标题和文本;不支持旧版 .ppt。 | | Jupyter Notebook | .ipynb | 每个单元的类型和源码;初始提取只保留每个单元非 display 文本输出的最后两行。 | | 栅格图片 | 按 MIME 或扩展名识别 PNG/JPEG/WebP/GIF | 可探测的格式/尺寸、Tesseract OCR,以及可选的简短 LLM 描述。仅在原生视觉不可用或失败时使用。 | | 其他二进制 | 以上之外的格式 | 一条包含不支持扩展名和 MIME 的说明;原文件仍可由 attach_save 保存。 |

每个提取结果最多 maxExtractChars 个字符。发生截断时,文本和持久化元数据都会 标记。提取失败不会丢弃已经上传完成的文件:插件会保留原文件和错误提取结果, 模型仍可调用 attach_save。

图片 OCR 与自动描述

OCR worker 会复用;ocrLanguages 改变时会重建。explainImages: true 时, 插件还会尝试使用第一个可用 LLM provider 和 model 生成一次简短描述。自动描述 同时依赖 llm 和 attachments 服务;服务缺失、模型信息缺失、超时或调用失败 都不会让上传失败,已有 OCR 结果仍会保留。

模型和用户分别看到什么

输入框里的蓝色文件名只是给模型的短引用,可以放在提示词的任何位置,提取内容 不会在那里展开。发送时,宿主会把这些文件的提取结果全部接到用户提示词后面:

Please compare [attached file "a.pdf" (2.4 MB) id=a1b2c3d4e5f6] with [attached file "b.txt" (10 B) id=b2c3d4e5f6a1] thanks
----- extracted content -----
[attached file "a.pdf" (2.4 MB) id=a1b2c3d4e5f6]
…a.pdf 的提取文本…
----- end -----
----- extracted content -----
[attached file "b.txt" (10 B) id=b2c3d4e5f6a1]
…b.txt 的提取文本…
----- end -----

输入框上方的文件列表就是这些会进到末尾提取区的文件。浏览器会从对话界面去掉 提取区块;即使对话把同一条气泡拆成多个节点,或折叠了标记周围的换行,也同样 移除。用户只看到提示词和蓝色文件名。底层提交消息仍包含完整提取内容。提示词 中不会出现文件库路径。

12 位小写十六进制 id 是所有工具首选的附件标识。文件名也可以使用,但仅当 当前会话文件库中恰好有一个同名上传时有效;存在同名文件时必须使用 ID。

附件元数据只保存在浏览器内存中。页面刷新后,输入框里恢复的文件名可能拿不 回原文件名和大小,因此会降级为:

[attached file id=a1b2c3d4e5f6 — extraction unavailable after reload; use attach_* tools with id a1b2c3d4e5f6]

原文件和 extract.json 仍保存在文件库。宿主仍会从文件库把提取内容接到提示 词后面,工具也能通过 ID 找到原文件。

模型工具

4 个工具都返回文本;业务错误会作为工具错误返回。它们只能操作当前会话中由 本插件上传到文件库的文件。

attach_notebook_output

返回一个完整的 Jupyter 单元:序号/类型、源码、全部文本输出、错误/traceback, 以及被省略的非文本 MIME 数据类型与字节数。

{"id":"a1b2c3d4e5f6","cell":0}
  • id:附件 ID,或唯一的 Notebook 文件名。
  • cell:从 0 开始的索引;非整数或越界会报错。
  • 附件文件名必须以 .ipynb 结尾。

attach_pdf_ocr_page

以 2 倍缩放栅格化一个 PDF 页面,再使用 ocrLanguages 执行 OCR。

{"id":"a1b2c3d4e5f6","page":1}
  • id:附件 ID,或唯一的 PDF 文件名。
  • page:从 1 开始的正整数页码。
  • 附件文件名必须以 .pdf 结尾。
  • 没有识别出文本时返回 (no OCR text on page N)。

attach_describe_image

使用自定义提示词启动 describeProvider 配置的子代理。

{"id":"a1b2c3d4e5f6","prompt":"列出界面中所有可见文字"}
  • id:附件 ID,或唯一文件名。
  • prompt:必填且不能为空。
  • 需要 DSH 子代理服务。附件服务可用时会把原始字节作为图片传给子代理;否则 子代理只会收到文件名和提示词。
  • 实现并不强制检查图片扩展名,但该工具的预期输入是 PNG/JPEG/WebP/GIF。
  • 返回子代理文本、(empty description),或子代理未正常完成时的工具错误。

attach_save

把未经修改的原文件从文件库复制到会话工作区。

{"id":"a1b2c3d4e5f6","path":"docs/report.pdf"}
  • id:附件 ID,或唯一原文件名。
  • path:相对于会话工作区的目标路径。
  • 自动创建父目录。
  • 解析到工作区根目录本身或工作区之外的路径会被拒绝。
  • 已存在的目标文件会被覆盖。
  • 返回规范化后的工作区相对路径。

宿主 HTTP 接口

这些接口用于配套网页客户端,不是独立的公网远程 API。

| 方法与路径 | 用途 | |---|---| | POST /api/dsh-file-attach/upload | 新建或继续分块上传;校验活动会话、元数据、限制、分块 header 和最终字节数;完成后提取并持久化。 | | POST /api/dsh-file-attach/abort | 按上传 ID 尽力删除进行中的上传目录。 | | GET /api/dsh-file-attach/extract | 返回活动会话中有效上传 ID 对应的 extract.json。 | | GET /api/dsh-file-attach/config | 返回客户端可见限制和支持的栅格 MIME 类型。 | | GET /api/dsh-file-attach/vision | 判断当前/所选会话模型是否明确支持图片输入。 |

上传元数据通过 x-session-id、x-file-name、x-file-type、 x-file-size、x-chunk-index、x-chunk-count 传递;第一块之后还会发送 x-upload-id。宿主会串行写入同一上传的分块。单个请求 body 的上限为 maxFileBytes + 1 MiB。

配置接口只返回 maxFileBytes、maxFilesPerMessage、 maxConcurrentUploads、vaultDir、maxExtractChars、explainImages、 ocrLanguages 和栅格 MIME 列表;不会返回 explainTimeoutMs、 describeProvider 或固定的客户端分块大小。

配置

编辑 cordis.patch.yml 中 file-attach 行的 config:

| 配置项 | 默认值 | 实际作用 | |---|---:|---| | maxFileBytes | 52428800(50 MiB) | 单文件限制,客户端和宿主都会检查。 | | maxFilesPerMessage | 20 | 会返回给客户端,但当前客户端尚未执行单消息文件数量限制。 | | maxConcurrentUploads | 20 | 宿主中同时打开的上传会话上限;新的首块超过上限时返回 HTTP 429。 | | vaultDir | file-attach | $DSH_HOME(或 ~/.dsh)下保存原文件和提取结果的子目录。 | | maxExtractChars | 80000 | 每个初始提取结果的字符上限。 | | explainImages | true | 在插件图片路径上启用尽力而为的自动 LLM 描述。 | | ocrLanguages | eng+chi_sim | 图片和 PDF 单页 OCR 使用的 Tesseract 语言 ID。 | | explainTimeoutMs | 30000 | 自动图片描述的中止超时。 | | describeProvider | spawn | attach_describe_image 启动子代理时使用的 provider。 |

上传分块大小固定为 1 MiB,不是可配置项。

存储、清理与安全

插件上传存储在:

$DSH_HOME/file-attach/<净化后的会话 ID>/<12 位 ID>/
├── <净化后的原文件名>
└── extract.json

未设置 $DSH_HOME 时使用 ~/.dsh;vaultDir 会替换其中的 file-attach。文件名只保留 basename,移除控制字符和前导点,去除两端空格, 并限制为 200 个字符;会话 ID 也会被压缩成一个安全路径段。

主动中止或失败的残缺上传会尽力删除。无人继续的残缺上传 10 分钟后过期,宿主 每分钟扫描一次。已完成上传不会自动清理:从输入框或附件栏移除引用不会删除原 文件。插件卸载时,浏览器会中止活动上传,宿主会清理仍打开的上传会话。

这些接口与网页应用同源;上传、提取和视觉查询会检查活动会话,但没有单独的 Bearer 凭据。中止和配置接口不要求会话 header。除非前方有认证反向代理,否则 请只把 DSH 绑定到 127.0.0.1。详见 SECURITY.md。

当前限制

  • 浏览器附件元数据和附件栏状态不会跨页面刷新持久化。
  • 刷新后的 /attach <id> 只会生成 ID 提示,不能把完整提取内容重新注入提示词。
  • 当前客户端公开了 maxFilesPerMessage,但没有实际执行该限制。
  • 已完成的文件库文件没有保留期限或自动垃圾回收策略。
  • 扫描 PDF 不会在上传时自动 OCR;需要逐页调用 attach_pdf_ocr_page。
  • 原生视觉图片绕过插件文件库,因此不能用于插件工具。
  • 自动图片描述使用第一个可用 provider/model,不一定是当前对话所选模型。
  • 全页面拖放监听器会把任何文件拖放交给当前活动会话。
  • 没有活动会话时,拖放仍会被接管并丢弃,只写控制台警告;已定义的本地化 noSession toast 当前没有使用。
  • 输入框里的蓝色文件名总是追加到草稿末尾,不会插入当前光标位置。
  • 旧版 Office 格式(.doc、.xls、.ppt)没有提取器。
  • SVG、BMP、TIFF 等非 PNG/JPEG/WebP/GIF 图片不会走 OCR 或原生视觉;它们按 文本检测规则或不支持的二进制文件处理。
  • OCR 依赖可用的 Tesseract 语言数据;自动化测试会替换 OCR 和 PDF 栅格化后端, 不会测试真实语言包下载。

开发

npm ci
npm test
npm run build:client
git diff --exit-code -- lib/client.js
npm pack --dry-run

src/client-core.js 和 src/client-app.js 是由 scripts/build-client.mjs 组装的普通脚本。修改任一源码后,都要提交重新生成的 lib/client.js。CI 会在 Node.js 22 和 24 上运行测试,并检查生成文件和发布包。

项目结构:

lib/index.js              宿主路由、限制、生命周期和工具注册
lib/extract.js            文本、Office、Notebook、PDF、图片和 OCR 提取
lib/ingest.js             准入、净化、文件库路径和模型文本
lib/tools.js              attach_* 工具定义
lib/vision.js             当前模型视觉能力解析
lib/client.js             生成后的浏览器 bundle
src/client-core.js        纯浏览器辅助逻辑
src/client-app.js         浏览器插件和界面行为
scripts/build-client.mjs  浏览器 bundle 组装
test/                     node:test 测试

发布与贡献

npm 包名是 @lucasxingg/dsh-file-attach(未加 scope 的 dsh-file-attach 已被占用)。项目遵循语义化版本,变更记录见 CHANGELOG.md。

发布全部由 .github/workflows/release.yml 完成。先把改动写进 CHANGELOG 的 ## [Unreleased],然后在 Actions → Release → Run workflow 选择版本递增方式运行:工作流会运行测试、 校验生成的浏览器 bundle、递增版本号、把 Unreleased 条目归入带日期的新版本、 提交并打 tag、发布到 npm,并带上打包好的 tarball 创建 GitHub Release。也可以 自己推送 v* tag,此时直接发布已提交的版本、跳过递增。两条路径都可以重跑: npm 上已存在该版本时会跳过发布,GitHub Release 已存在时只更新附件。Unreleased 为空会让本次运行失败——没有记录任何改动的发布是失误。

npm 鉴权只需一次性配置,二选一:

  • Trusted publishing(推荐,仓库里不保存任何凭据)。在 npmjs.com 打开该包 → Settings → Trusted publisher → GitHub Actions,仓库填 LucasXingg/dsh-file-attach,工作流文件名填 release.yml,environment 留空。 这种方式会自动附带 provenance。所有字段大小写敏感且必须完全一致,npm 只在发布 时校验,配置不匹配时发布步骤只会报一个 404。
  • 仓库 secret NPM_TOKEN,值为对该包有发布权限的 granular automation token。 只有该 secret 存在时工作流才会使用它,并自行传入 --provenance。

Run workflow 这条路径会推送发布提交,因此要求 PR 的分支保护会拒绝它;这种情况 下改用推 tag 发布。仍然可以在以 npm 用户 lucasxingg 登录的机器上手动发布:

npm whoami   # 必须输出 lucasxingg
npm test
npm run build:client
npm publish --access public

开发规范见 CONTRIBUTING.md,私下报告安全问题见 SECURITY.md,许可条款见 LICENSE。