Skip to content
dsh.fish
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

Source