Bundle
dsh-baidu-ocr
Baidu cloud OCR (PaddleOCR-VL + Unlimited-OCR) for DeepSeek Harness Web: drag images/PDFs in, OCR to markdown, write results as local files.
- Source
- pipiwolve
- License
- MIT
- Updated
- Updated 6 days ago
Readme
# dsh-baidu-ocr
百度云 OCR 的 DeepSeek Harness Web 插件(bundle)。把图片 / PDF / 办公文档拖进页面,用百度云 OCR 识别为 Markdown,并把结果写成**本地文件**。
- **PaddleOCR-VL**(同步):千帆平台 `qianfan.baidubce.com` 的通用文字识别,返回 `markdown.text`,支持版面分析、图表识别、方向/畸变矫正。
- **Unlimited-OCR**(异步):`aip.baidubce.com` 的文档解析,公式 LaTeX、表格 HTML、多栏版面智能合并。
> 一句话:一个 key、两个引擎、拖入即识别、结果落盘、既能 GUI 也能当模型工具用。
---
## 目录
- [它是什么](#它是什么)
- [为什么这样设计](#为什么这样设计)
- [架构](#架构)
- [能力一览](#能力一览)
- [安装](#安装)
- [配置 API Key](#配置-api-key)
- [使用](#使用)
- [输出](#输出)
- [目录结构](#目录结构)
- [安全](#安全)
- [开发与调试](#开发与调试)
- [FAQ](#faq)
- [路线图](#路线图)
---
## 它是什么
这是一个符合 DeepSeek Harness **bundle 规范**的插件包,安装后同时提供:
| 界面 | 形态 | 说明 |
|---|---|---|
| **拖入面板** | 浏览器右下角浮窗 | 拖入文件 → 自动解析真实本地路径 → 选引擎 → 识别 → 结果卡片预览 |
| **模型工具** | `baidu_ocr` 工具 | 对话里直接 `用 baidu_ocr 识别 /path/to/img.png`,Agent 可自动调用 |
| **设置卡片** | 设置 → 插件 → 百度 OCR | 在 GUI 里填写 API key(不回显、不落日志) |
它解决的核心痛点:**浏览器无法把「拖进来的文件」直接交给本地 OCR 引擎**——浏览器只给你 `File` 对象(没有真实路径),而 DSH 的工具需要真实路径来读文件。本插件在 Host 端读真实路径、调 API、写结果文件,在 Client 端把拖入的文件解析回真实路径。
---
## 为什么这样设计
这是经过验证后收敛出来的架构,几个关键取舍:
1. **零 `@deepseek-ai` 依赖**:社区 bundle 会被 `dsh plugin add` 装进 profile 的 `node_modules`,那里**没有** DSH 内部包。因此 host 只用 Node 内置(`node:fs`/`node:path` + 原生 `fetch`),client 端拖入面板用**纯 DOM**(不 import React),只有设置卡片用 platform seed 里的 `react`。
2. **原生 `fetch` 而非 Python**:bundle 的 host 运行在真实 Node 进程里(不同于动态插件的受限沙箱),可以直接 `fetch` 调百度 API。相比早期的 Python 脚本方案,零外部依赖、更可分发。
3. **Client ↔ Host 走 HTTP 路由**(而非动态插件的 `host.call`):bundle 没有动态插件那种「Package 私有 RPC」,标准做法是 host 注册 `webServer` 路由、client `fetch` 调用。这是 dsh-drag-and-drop / modlens 等社区 bundle 的同一模式。
4. **key 永不回传浏览器**:设置页只读到 `hasKey` 布尔值,真值只存在于 `${DSH_HOME:-~/.dsh}/baidu-ocr.json`(`0600` 权限),提交时留空 = 保留原值。参照 modlens 的安全模型。
---
## 架构
```
┌─────────────────────────────────────────────────────────────────┐
│ Host (lib/index.js, Node 进程, inject: [tools, webServer]) │
│ │
│ baidu_ocr 工具 ──┐ │
│ ├─ resolveKey(ctx) ── 设置文件 > credentials > env
│ │ │
│ /baidu-ocr/run ──┼─ runOcr(path, engine, key) │
│ (client 面板用) │ ├─ paddleocr() → qianfan 同步 POST │
│ │ └─ unlimited() → aip 提交→轮询→下载 │
│ │ └─ writeResults() → ocr_output/*.md/.json │
│ │ │
│ /baidu-ocr/config ─ writeSettingsFile() → ${DSH_HOME:-~/.dsh}/baidu-ocr.json │
│ (设置页用) readSettingsFile() → { hasKey } (不回显 key) │
└─────────────────────────────────────────────────────────────────┘
│ HTTP (同源 fetch)
┌─────────────────────────────────────────────────────────────────┐
│ Client (lib/client.js, window.__ModuleLoader__) │
│ │
│ 拖入面板(纯 DOM): │
│ drag/drop → 解析 file:// URI → 真实路径 chip → 选引擎 │
│ → POST /baidu-ocr/run → 结果卡片 (markdown 预览 + 文件路径) │
│ · 可拖动 / 可收起 / FAB 跟随 composer 上方(不挡发送按钮) │
│ │
│ 设置卡片(React, settings.plugin.item): │
│ GET /baidu-ocr/config → hasKey 状态 │
│ POST → 写 key(留空 = 保留) │
└─────────────────────────────────────────────────────────────────┘
```
---
## 能力一览
### 双引擎
| 引擎 | 接口 | 模式 | 适用 | 输出 |
|---|---|---|---|---|
| `paddleocr` | `qianfan.baidubce.com/v2/ocr/paddleocr` | 同步 | 图片、PDF 通用识别 | `markdown.text` |
| `unlimited` | `aip.baidubce.com/.../unlimited-ocr-parser` | 异步(提交→轮询→下载) | 文档解析、公式、表格、多栏 | `markdown_url` 内容 |
引擎选择:
- `auto`(默认):按扩展名自动选 —— 图片(jpg/png/bmp/tif…)→ `paddleocr`;办公文档/PDF(pdf/ofd/doc/docx/ppt/pptx…)→ `unlimited`;
- 也可在面板下拉框或工具参数里显式指定。
### 支持格式
| 类型 | 扩展名 |
|---|---|
| 图片 | `.jpg .jpeg .png .bmp .tif .tiff` |
| 版式文档 | `.pdf .ofd` |
| 流式文档 | `.doc .docx .txt .wps .ppt .pptx` |
---
## 安装
```sh
dsh plugin --profile web add <本仓库路径或 git url>
```
> `dsh plugin` 是 pnpm 转发器:它把包加进 profile 的依赖,然后扫描声明了 `dsh.bundle.patch` 的包,自动 reconcile 进 `dsh.profile.bundles` 层栈。无需手改 config。
然后**重启 Web UI 并刷新浏览器**:
```sh
dsh web --host 127.0.0.1 --port 3080 --no-open
```
> 注意:请用与当前运行实例**同一个** `dsh` 二进制重启(如果你机器上有多个 dsh 版本,旧版可能不认 `--no-open`)。
安装后:
- Host 注册 `baidu_ocr` 工具 + `/baidu-ocr/run` + `/baidu-ocr/config` 路由;
- Client 注册右下角拖入面板 + 设置页「百度 OCR」卡片;
- 3080 直连与 5173 皮肤壳(iframe 代理 `/plugins`)都会自动加载,无需手动配置。
---
## 配置 API Key
三种来源,优先级从高到低:
1. **设置页**(推荐):设置 → 插件 → 百度 OCR,填 key 保存 → 写入 `${DSH_HOME:-~/.dsh}/baidu-ocr.json`;
2. **credentials 服务**:`ctx.get('credentials').resolve('BAIDU_OCR_KEY')`;
3. **环境变量**:`export BAIDU_OCR_KEY="bce-v3/ALTAK-.../..."`。
key 格式为百度 **BCE IAM API Key**(`bce-v3/ALTAK-.../...`),对两个引擎的 Bearer 鉴权都有效。
获取:https://console.bce.baidu.com/iam/#/iam/accesslist
---
## 使用
### 方式一:拖入
1. 从 Finder(或文件管理器)拖文件到页面任意位置,全屏出现「松开以添加文件到 OCR」提示;
2. 右下角「百度 OCR」面板弹出,文件变成可删除的 chip(hover 显示完整路径);
3. 选引擎(自动 / PaddleOCR-VL / Unlimited-OCR),点「识别 N 个文件」;
4. 结果卡片显示 markdown 预览 + 结果文件路径,`.md`/`.json` 落到源文件旁。
面板可**拖动标题栏**移动、**收起**成 `OCR` 圆钮(悬浮在输入框上方,不挡发送按钮)、支持粘贴绝对路径手动添加。
### 方式二:模型工具
在对话里直接说:
```
用 baidu_ocr 识别 /absolute/path/to/image.png
```
工具返回预览 + 文件路径;要拿完整文本,再让 Agent 用 `read` 读 `markdownFile`。
---
## 输出
每个文件在 `<源目录>/ocr_output/` 下产出两个文件:
- `<文件名>.md` — Markdown 识别结果(含公式 LaTeX、表格 HTML);
- `<文件名>.json` — 元数据:
```json
{
"engine": "paddleocr",
"source": "/abs/path/to/image.png",
"markdownFile": "/abs/path/to/ocr_output/image.md",
"charCount": 1234,
"requestId": "as-xxx",
"generatedAt": "2026-08-24T08:00:00.000Z"
}
```
---
## 目录结构
```
dsh-baidu-ocr/
├── package.json # dsh.bundle.patch + dsh.client 声明、exports、files
├── cordis.patch.yml # insert 插件行的 bundle patch
├── lib/
│ ├── index.js # host:baidu_ocr 工具 + /run + /config + 双引擎(原生 fetch,零依赖)
│ └── client.js # client:拖入面板(纯 DOM)+ 设置卡片(React)
├── test/
│ └── index.test.js # fence / 路径校验等纯逻辑的单元测试(node --test)
├── .github/workflows/ci.yml # CI:语法检查 + 单元测试
└── README.md
```
---
## 安全
- **key 不回显、不落日志**:设置页只拿到 `hasKey` 布尔值;真值存 `${DSH_HOME:-~/.dsh}/baidu-ocr.json`(`0600` 权限),会话日志里不出现。
- **跨站写防护(CSRF fence)**:`/baidu-ocr/config` 与 `/baidu-ocr/run` 复刻 DSH 自身 `/api` 的信任模型——副作用请求只接受 `application/json` 的 POST,并校验 `Origin` 与请求 Host 同源;恶意网页的跨站请求会被强制进入本服务永不应答的 CORS preflight。只读的 GET 也做 Origin 校验。
- **OCR 目标路径校验**:`/run` 只接受**绝对路径**、扩展名在白名单(图片 / PDF / 办公文档)、且确认为普通文件的目标,防止任意本地文件被上传到百度云(路径遍历 / 数据外带加固)。`unlimited` 引擎的结果下载只允许 `https://` 的 markdown_url。
- **结果文件写源目录**:`.md`/`.json` 写到源文件旁的 `ocr_output/`,可预期、可追溯。
- **零依赖**:host 只用 Node 内置,client 拖入面板纯 DOM,攻击面最小。
> 安全模型参照:DSH 的 `/api` 代理在 `dsh-host-apiproxy` 中以「仅接受 `application/json`」实现跨站写 fence;bundle 路由注册在 `webServer`(loopback),浏览器与 host 同源。
---
## 开发与调试
### 验证 host 逻辑
```sh
# 起临时实例(独立端口,不打扰正式 3080)
node /path/to/dsh/lib/bin.js web --host 127.0.0.1 --port 3090 --no-open
# 探测 config 路由(GET 不回显 key,POST 写 key)
curl http://127.0.0.1:3090/baidu-ocr/config
curl -X POST http://127.0.0.1:3090/baidu-ocr/config \
-H "content-type: application/json" -d '{"apiKey":"bce-v3/..."}'
# 跑一次 OCR
curl -X POST http://127.0.0.1:3090/baidu-ocr/run \
-H "content-type: application/json" -d '{"path":"/tmp/test.png","engine":"auto"}'
```
### 改 client 后生效
- 有 `pnpm run dev:web`(HMR watcher):client 改动热更新;
- 无 watcher:改 `lib/client.js` 后,`serveBundle` 实时读磁盘,**浏览器硬刷新(Cmd+Shift+R)即可**;若要 rev 缓存键也更新,重启 `dsh web`。
### 语法校验
```sh
node --check lib/index.js
node --check lib/client.js
```
### 单元测试
```sh
npm test
```
覆盖 host 侧的 CSRF fence 与目标路径校验等纯逻辑(`test/index.test.js`)。CI(`.github/workflows/ci.yml`)在每次 push 自动运行语法检查与测试。
---
## FAQ
**Q:为什么拖入能拿到真实路径?**
A:浏览器对拖入文件只暴露 `File` 对象,但文件管理器会带 `text/uri-list`(`file://` URI)。client 端解析这些 URI 还原成本地绝对路径(POSIX / Windows 盘符 / UNC),再交给 host 读文件。这复用了 dsh-drag-and-drop 的定位思路。
**Q:两个引擎的 key 一样吗?**
A:是。同一个 BCE IAM API Key(`bce-v3/ALTAK-...`)对 PaddleOCR-VL(千帆 Bearer)和 Unlimited-OCR(aip Bearer)都有效,一个 key 通吃。
**Q:结果能直接「拖出」到任意文件夹吗?**
A:浏览器标准不支持「拖出写文件」,但 DSH 是本地 Web UI,host 直接写本地文件更可靠——结果落在源文件旁 `ocr_output/`,会话里展示预览 + 路径。
**Q:为什么 5173 皮肤壳也能看到?**
A:皮肤壳是 `<iframe>` 代理 `/plugins`、`/api` 到 3080,bundle 的 client 走静态路由 `/plugins/dsh-baidu-ocr/client.js`,两个视图都代理到了,无需重复配置。
---
## 路线图
- [ ] 批量目录识别 + 并发限流(QPS 控制)
- [ ] 结果文件路径做成可再拖入的 chip
- [ ] 更多引擎(PP-OCRv6 通用文字识别)
- [ ] 打包成 npm 包发布(当前为本地 `link:` 安装)
---
## License
MIT
Install
dsh plugin --profile web add github:pipiwolve/dsh-baidu-ocr#a3c687cc0eacd39fa37658c24b913cebe038388f
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-baidu-ocr from the hub