把印象笔记 / Yinxiang Biji接入 DSH —— 通过官方 EDAM API 完整读写笔记、笔记本、标签与附件, 并提供侧边栏面板(浏览 / 搜索 / 编辑 / 导出)。
这是 DSH 插件 + CLI 的双形态包:CLI 可以立刻用,插件提供 HTTP 路由与 Web UI。
本项目是第三方独立开发的,与印象笔记 / Evernote 官方没有任何隶属、赞助或背书关系。 「印象笔记」「Evernote」及其图形标识是各自权利人的注册商标。本项目默认使用原创图标, 不包含任何厂商商标素材。详见 LICENSE 的商标条款。
This is an unofficial third-party integration. Not affiliated with, endorsed by, or sponsored by Evernote Corporation or 印象笔记.
| 路径 | 结论 |
|---|---|
| 官方 EDAM API(本插件) | ✅ 功能 100%,合规,稳定 |
本地 .exb 数据库 |
✅ 只读可靠(145 条笔记实测可读),❌ 写入会与云端同步冲突 |
客户端 COM 接口(yxapi.YinXiangBiJi) |
接口完整(CreateNote / CreateNotebook / Search …),但没有已认证会话,调用返回 0x80040205 |
| 浏览器自动化 | 可行但脆弱,依赖登录态 |
| ENEX 文件导入 | ✅ 适合批量,但半自动 |
官方开发者门户 dev.yinxiang.com 可用(旧域名 open.yinxiang.com 已废弃)。
不需要申请应用、不需要 OAuth。
-
点 Create a developer token
-
页面上会出现两个值:
Developer Token : S=s33:U=...:H=... NoteStore URL : https://app.yinxiang.com/shard/sXX/notestore -
写进 token 文件
%USERPROFILE%\.dsh\yinxiang-token.json:{ "token": "S=s33:U=...:H=...", "noteStoreUrl": "https://app.yinxiang.com/shard/sXX/notestore", "shardId": "s33", "china": true }
注意:令牌有有效期(页面上会显示,通常是 7 天),过期后回同一个页面重新生成即可。 令牌等同于账号密码权限,不要外泄。
- 打开 https://dev.yinxiang.com → 申请 API Key
- 回调地址填:
http://localhost:8765/oauth/callback - 配置并授权:
cd dsh-yinxiang-plugin
node bin\yinxiang.mjs config set consumerKey <你的 key>
node bin\yinxiang.mjs config set consumerSecret <你的 secret>
node bin\yinxiang.mjs auth浏览器打开印象笔记授权页 → 点「授权」→ token 自动保存。
node bin\yinxiang.mjs status
# 授权方式应为「开发者令牌」或「OAuth」
node bin\yinxiang.mjs user
# 显示账号 ID、会员状态、shard
node bin\yinxiang.mjs notebooks
# 列出全部笔记本(含 stack 分组)node bin\yinxiang.mjs status # 看配置与授权状态
node bin\yinxiang.mjs user # 账号信息
node bin\yinxiang.mjs notebooks # 笔记本列表
node bin\yinxiang.mjs tags # 标签列表
node bin\yinxiang.mjs search "GSOT" # 搜索
node bin\yinxiang.mjs read <guid> # 读笔记(正文转纯文本,并列出附件)
node bin\yinxiang.mjs read <guid> --raw # 原始 ENML
node bin\yinxiang.mjs create --title "标题" --content "正文" --notebook "GSOT世界设定集" --tags "设定,待整理"
node bin\yinxiang.mjs update <guid> --content "新正文"
node bin\yinxiang.mjs trash <guid>
node bin\yinxiang.mjs export "GSOT" --out .\yinxiang-export # 默认 Markdown
node bin\yinxiang.mjs export "GSOT" --out D:\备份 --format enex # 官方格式(能导回印象笔记)
node bin\yinxiang.mjs export "GSOT" --out D:\备份 --format html # 自包含网页
node bin\yinxiang.mjs export "GSOT" --out D:\备份 --format json # 结构化数据
node bin\yinxiang.mjs export "GSOT" --out D:\备份 --format all # 五种全出全部命令加 --json 可以得到机器可读输出。
node bin\yinxiang.mjs usage # 近 1 小时调用次数、估算上限、预警等级
node bin\yinxiang.mjs usage --json # 机器可读
node bin\yinxiang.mjs usage --reset # 清零(重新学习估算上限)# 给已有笔记追加附件(可多次 --file)
node bin\yinxiang.mjs attach <noteGuid> --file "图片.png" --file "说明.txt"
# 下载某个附件
node bin\yinxiang.mjs download <resourceGuid> --out D:\附件
# 下载一条笔记的全部附件
node bin\yinxiang.mjs download --note <noteGuid> --out D:\附件attach 会同时把 <en-media> 插进正文,否则附件在客户端里「存在但看不见」。
create 支持可重复的 --file,MIME 按扩展名自动推断:
node bin\yinxiang.mjs create `
--title "带附件的笔记" `
--content "下面是图片和 PDF。" `
--file "E:\Pictures\diagram.png" `
--file "E:\Docs\spec.pdf" `
--file "E:\Docs\notes.txt"- 图片(png / jpg / gif / webp …)会在正文里内嵌显示;
- 其它文件作为附件卡片挂在笔记末尾;
- 创建成功后会打印每个附件的文件名、MIME、大小和 MD5,
read也能看到同样的信息,方便和本地文件核对; - 单条笔记上限:免费账号 25 MB、付费账号 200 MB(正文 + 附件总和)。超限时会在上传前直接报错,提示压缩或拆分为多条笔记。
export 支持五种格式,用 --format 指定:
| 格式 | 产物 | 保真度 | 附件 | 能导回印象笔记 | 适合 |
|---|---|---|---|---|---|
markdown |
.md + .assets/ 旁挂附件 |
中 | 旁挂文件 | ❌ | 编辑、喂给 DSH |
enex |
单个 .enex |
100% | 内嵌 base64 | ✅ | 备份、迁移 |
html |
每条一个 .html + index.html |
高 | 内嵌 data URI | ❌ | 存档、分享、打印 |
json |
单个 .json |
100%(数据) | base64 | ❌ | 程序处理、做索引 |
txt |
.txt |
低 | 只列清单 | ❌ | 快速预览、全文搜索 |
# 按笔记本过滤
node bin\yinxiang.mjs export "" --notebook "GSOT世界设定集" --out D:\导出
# 限制条数
node bin\yinxiang.mjs export "莫雷" --out D:\导出 --limit 10
# 不下载附件(快,适合先看清单)
node bin\yinxiang.mjs export "GSOT" --out D:\导出 --format json --no-resources
# 不按笔记本分目录(全部平铺)
node bin\yinxiang.mjs export "GSOT" --out D:\导出 --flat
# 机器可读输出
node bin\yinxiang.mjs export "GSOT" --out D:\导出 --format json --json- ENEX 格式没有笔记本槽位 —— 导出后导入回来,所有笔记会进默认笔记本。要保留归属,建议同时导出
json做 sidecar(里面记了每条笔记的 notebook 和 tags)。 - ENEX 不存 guid —— 同一批笔记「导出 → 导入」会产生重复副本。它是备份/迁移格式,不是同步格式。
dsh plugin --profile desktop add link:<克隆后的绝对路径>
dsh默认不在 PATH 时,可用完整入口:<DeepSeekHarness 安装目录>\resources\runtime\cli\bin\dsh.cmd执行后 DSH 一般会热挂载插件,无需重启;若路由没生效,重启一次 DSH 即可。
装完重启 DSH。插件会挂上这些路由:
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /yinxiang/status |
配置与授权状态 |
| GET | /yinxiang/user |
账号信息 |
| GET | /yinxiang/notebooks |
笔记本列表 |
| GET | /yinxiang/tags |
标签列表 |
| GET | /yinxiang/search?q=&limit= |
搜索笔记 |
| GET | /yinxiang/note?guid=&format= |
读笔记(json / markdown / text / enml) |
| POST | /yinxiang/create |
{ title, content, notebook?, tags?, files? };files 支持 [{ path }](服务端读本地文件)或 [{ name, mime?, base64 }],请求体上限 40 MB |
| POST | /yinxiang/update |
{ guid, title?, content?, notebook?, tags? } |
| POST | /yinxiang/trash |
{ guid } |
| POST | /yinxiang/logout |
清除本地 token |
带附件的调用示例(base64 走请求体,适合远程调用方):
POST /yinxiang/create
{
"title": "带附件的笔记",
"content": "正文",
"files": [
{ "name": "diagram.png", "mime": "image/png", "base64": "iVBORw0KGgo..." }
]
}注意:/yinxiang/create 的请求体上限放宽到 40 MB(其余 POST 路由仍是 64 KB);
超过免费账号 25 MB 的笔记上限时会返回 413 + NOTE_TOO_LARGE。
卸载:
dsh plugin --profile desktop remove dsh-yinxiang- API Key 与 token 只存在本机
%USERPROFILE%\.dsh\下,不写工作区、不进对话上下文。 - 所有 HTTP 路由都做同源信任校验:
Host必须是 loopback 或部署配置的trustedHosts,Origin必须与Host一致,跨站请求一律 403。 - 写入操作全部走官方 API,与客户端同步链路一致,不会造成数据冲突。
- 附件只读用户显式指定的文件:CLI 的
--file、或/yinxiang/create里的files[].path;插件不扫描目录、不读取其它文件。 - 插件不读本地
.exb数据库、不驱动客户端进程、不注入任何 DLL。
dsh-yinxiang/
├── bin/yinxiang.mjs CLI 入口
├── lib/index.js 插件宿主(10 条路由)
├── src/
│ ├── api.js EDAM API 封装(async/await + 附件 + ENML 工具)
│ ├── oauth.js OAuth 1.0a 授权流程
│ ├── renew.js 开发者令牌自动续期(Puppeteer)
│ └── config.js 配置解析 / token 存取
├── test/
│ ├── fixtures.mjs 离线生成 PNG / PDF / TXT 测试附件
│ ├── attachment-roundtrip.mjs 附件上传→读回→下载→删除 全链路
│ └── plugin-route.mjs 路由层的访问控制 / 参数校验 / files 数组
├── tools/
│ ├── build.mjs client 产物构建(拼接,无打包器)
│ ├── preview.mjs 生成静态预览页(假数据,真实 CSS)
│ └── import-official-logo.mjs 从本机客户端提取官方图标(已 gitignore)
├── cordis.patch.yml 挂载声明
├── dsh-plugin.json 插件清单与权限声明
└── package.json
跑测试(会真实读写印象笔记,测试笔记自动删除):
npm test # 路由层 + 附件往返
node test\plugin-route.mjs # 只跑路由层(会建 2 条测试笔记)
node test\attachment-roundtrip.mjs # 只跑附件往返(会建 1 条测试笔记)装上之后有两处入口,都是可选的(宿主没装对应插件时自动跳过,不影响 CLI 和其它功能):
在右侧边栏的 tab 栏里会出现一个「印象笔记」页:
| 区域 | 功能 |
|---|---|
| 工具条 | 搜索框(防抖 320ms)、刷新、新建笔记、导出、管理 |
| 笔记本筛选 | 横向滚动的胶囊,点击过滤;后面跟条数 |
| 笔记列表 | 标题 + 日期 + 笔记本 + 标签胶囊,点开进预览 |
| 分页 | 首次拉 100 条,底部「还有 N 条」按钮按页追加(每次 +100,上限 2000) |
| 自动刷新 | tab 从隐藏变可见时静默重拉,切回来就能看到最新数据 |
| 预览 | 正文(Markdown 渲染)、笔记本/时间/标签、附件(图片内联、其它给下载链接)、编辑与删除 |
| 新建/编辑 | 标题、笔记本下拉、标签(逗号分隔)、正文(Markdown / 纯文本) |
| 导出对话框 | 六种格式胶囊 + 输出目录 + 「包含附件」「不按笔记本分目录」两个开关 |
| 用量徽标 | 工具条上的小仪表,显示近 1 小时调用量 / 估算占比;点开看详情 |
| 笔记本/标签管理 | 页签切换、带条目计数、逐条重命名 / 删除、底部新建 |
会话头右侧工具区会多一个文档图标,点一下直接打开上面那个 tab。
位置 conversation.session.header.utilities,order: 6(在系统项与现有第三方项之间)。
npm run build # 把 src/client/*.js 拼成 lib/client.js
npm run test:client # 产物加载 + 组件渲染(29 + 21 项断言)
npm test # 全部测试
node tools/preview.mjs out.html # 生成一个静态预览页(假数据,真实 CSS)默认图标是原创设计(正脸大象,描边风格,与 DSH 其它图标统一),不含任何厂商商标图形。
如果你只在本机个人使用、想换成官方图标:
npm run logo:official它会从你本机安装的印象笔记客户端里提取 Evernote.svg,生成
src/client/08-logo.official.js(已被 .gitignore 排除),然后:
npm run build构建会自动优先使用官方版。移除该文件后重新构建即回到原创图标。
⚠️ 不要把08-logo.official.js或含官方图标的lib/client.js提交到仓库。 商标图形随代码分发属于另一回事,个人使用与公开发布的边界在这里。
宿主路由改动会随 DSH 重启生效;client 包的改动必须重启 DSH 才会加载(DSH 在启动时读 package.json 的 dsh.client)。
| 项 | 状态 |
|---|---|
| 源码许可 | MIT(见 LICENSE) |
| 默认图标 | 原创,无商标风险 |
| 官方图标 | 仅本机可选,已被 .gitignore 排除,不建议随仓库分发 |
| 项目名「yinxiang」 | 指示性使用,说明连接的服务,不构成商标使用 |
| 依赖许可 | evernote@2.0.5 为 Apache-2.0,与本项目 MIT 兼容 |
| 用户凭据 | 只存本地 ~/.dsh/,不在仓库内 |
印象笔记不提供「剩余配额」查询接口 —— 我查过 SDK 的全部结构定义:
User.accounting只有上传容量(uploadLimit/uploadLimitEnd),没有请求次数- 所有 struct 里都找不到 rateLimit 字段
rateLimitDuration只在已经超限的错误异常里出现
所以这里做的是本地估算,不是真实配额:
| 环节 | 做法 |
|---|---|
| 记账 | 所有 API 调用都经过 api.js 的 call(),在那里记一笔 |
| 学习 | 撞到限流时,把「本窗口内的调用数」记成一个上限样本 |
| 估算 | 取样本的最小值(最保守);样本不足 2 个时标注「未校准」 |
| 预警 | 达到估算上限的 60% / 80% / 95% 分别给 warn / high / critical |
| 展示 | 工具条上的用量徽标 + 超阈值时的预警条 + 点开的详情卡片 |
窗口:滚动 1 小时(实测限流窗口约 30~60 分钟)。
统计文件:~/.dsh/yinxiang-usage.json,可用环境变量 YINXIANG_USAGE_FILE 覆盖。
命令行:
node bin\yinxiang.mjs usage # 看当前用量
node bin\yinxiang.mjs usage --json # 机器可读
node bin\yinxiang.mjs usage --reset # 清零统计局限(要说清楚):
- 第一次撞限流之前,无法给出任何百分比,只能显示原始调用数
- 如果印象笔记的限额是按操作类型分开算的(search / getNote 各有配额), 本地总量估算会偏保守 —— 但保守不会误报「安全」
- 换账号 / 换网络环境后,样本可能不再适用 —— 建议用
--reset重新学习
印象笔记对 API 调用频率有配额。超了会返回 EDAM 错误码 19(RATE_LIMIT_REACHED),
并附带 rateLimitDuration 表示还要等多少秒(实测可达 30 分钟量级)。
本插件已经处理:
describeError()把它翻译成中文,带上具体等待时长 (例:「请求太频繁,印象笔记限流了 —— 请等 28 分钟 再试(还剩 1647 秒)」)call()抛出code: 'RATE_LIMITED'并带rateLimitDuration/retryAfterMs- HTTP 路由把限流映射成 429,前端可据此做倒计时
什么时候容易触发:
- 短时间内大量跑测试(压测、反复往返)
- 面板里快速连续搜索不同关键词
缓解办法:
- 搜索有 320ms 防抖,正常使用不会触发
- 自动化测试要注意节奏,别连着跑几轮全量往返
- 命中了就等,没有别的办法(配额是账号级的)
- 一次只能授权一个账号(token 文件单份)。
update不能追加附件:只改标题 / 正文 / 笔记本 / 标签;要给已有笔记加附件需走create新建。- HTTP 附件上传上限 40 MB(base64 编码后),对应免费账号 25 MB / 付费 200 MB 的笔记上限;超大文件请用 CLI 的
--file(同样受笔记上限约束)。 - 导出为串行拉取,大账号建议用
--limit分批。 - 开发者令牌 7 天过期,
renew受 Chrome profile 独占锁限制,需先关掉浏览器。 - 依赖 SDK 的
getRequestToken/getAccessToken回调风格,已在src/oauth.js统一成 Promise。
MIT