remnote-bridge 0.1.10 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/cli/addon/addon-manager.js +163 -0
  2. package/dist/cli/addon/registry.js +24 -0
  3. package/dist/cli/commands/addon.js +149 -0
  4. package/dist/cli/commands/clean.js +121 -52
  5. package/dist/cli/commands/connect.js +72 -33
  6. package/dist/cli/commands/disconnect.js +19 -19
  7. package/dist/cli/commands/edit-rem.js +3 -31
  8. package/dist/cli/commands/edit-tree.js +3 -20
  9. package/dist/cli/commands/health.js +19 -18
  10. package/dist/cli/commands/read-context.js +8 -23
  11. package/dist/cli/commands/read-globe.js +3 -20
  12. package/dist/cli/commands/read-rem.js +3 -31
  13. package/dist/cli/commands/read-tree.js +3 -20
  14. package/dist/cli/commands/search.js +97 -21
  15. package/dist/cli/config.js +148 -72
  16. package/dist/cli/daemon/daemon.js +104 -24
  17. package/dist/cli/daemon/dev-server.js +9 -1
  18. package/dist/cli/daemon/pid.js +36 -22
  19. package/dist/cli/daemon/registry.js +160 -0
  20. package/dist/cli/daemon/send-request.js +11 -11
  21. package/dist/cli/daemon/static-server.js +97 -34
  22. package/dist/cli/handlers/context-read-handler.js +5 -3
  23. package/dist/cli/handlers/read-handler.js +4 -3
  24. package/dist/cli/handlers/tree-parser.js +16 -9
  25. package/dist/cli/main.js +51 -9
  26. package/dist/cli/protocol.js +18 -4
  27. package/dist/cli/server/config-server.js +280 -14
  28. package/dist/cli/server/ws-server.js +93 -44
  29. package/dist/cli/utils/output.js +29 -0
  30. package/dist/mcp/instructions.js +103 -10
  31. package/dist/mcp/resources/edit-rem-guide.js +3 -4
  32. package/dist/mcp/resources/error-reference.js +5 -3
  33. package/dist/mcp/resources/rem-object-fields.js +3 -3
  34. package/dist/mcp/tools/infra-tools.js +54 -6
  35. package/dist/mcp/tools/read-tools.js +16 -3
  36. package/package.json +2 -2
  37. package/remnote-plugin/dist/bridge_widget-sandbox.js +17 -17
  38. package/remnote-plugin/dist/bridge_widget.js +17 -17
  39. package/remnote-plugin/dist/index-sandbox.js +31 -31
  40. package/remnote-plugin/dist/index.js +31 -31
  41. package/remnote-plugin/dist/manifest.json +1 -1
  42. package/remnote-plugin/package.json +1 -1
  43. package/remnote-plugin/public/manifest.json +1 -1
  44. package/remnote-plugin/src/bridge/message-router.ts +1 -1
  45. package/remnote-plugin/src/bridge/multi-connection-manager.ts +151 -0
  46. package/remnote-plugin/src/bridge/websocket-client.ts +62 -16
  47. package/remnote-plugin/src/services/index.ts +0 -8
  48. package/remnote-plugin/src/services/read-context.ts +13 -4
  49. package/remnote-plugin/src/services/read-rem.ts +1 -9
  50. package/remnote-plugin/src/services/search.ts +13 -10
  51. package/remnote-plugin/src/settings.ts +9 -7
  52. package/remnote-plugin/src/utils/index.ts +0 -5
  53. package/remnote-plugin/src/widgets/bridge_widget.tsx +105 -20
  54. package/remnote-plugin/src/widgets/index.tsx +41 -44
  55. package/remnote-plugin/webpack.config.js +35 -0
  56. package/skills/remnote-bridge/SKILL.md +14 -9
  57. package/skills/remnote-bridge/instructions/addon.md +134 -0
  58. package/skills/remnote-bridge/instructions/clean.md +110 -0
  59. package/skills/remnote-bridge/instructions/connect.md +80 -37
  60. package/skills/remnote-bridge/instructions/disconnect.md +22 -9
  61. package/skills/remnote-bridge/instructions/edit-rem.md +37 -9
  62. package/skills/remnote-bridge/instructions/health.md +23 -13
  63. package/skills/remnote-bridge/instructions/install-skill.md +58 -0
  64. package/skills/remnote-bridge/instructions/overall.md +76 -21
  65. package/skills/remnote-bridge/instructions/read-context.md +34 -8
  66. package/skills/remnote-bridge/instructions/read-rem.md +10 -10
  67. package/skills/remnote-bridge/instructions/search.md +73 -14
  68. package/skills/remnote-bridge/instructions/setup.md +1 -1
@@ -130,7 +130,7 @@ Rem 有两个**独立维度**的类型:
130
130
  | 删除 Portal | `edit-tree` | 从大纲中移除 Portal 行(与删除普通行相同) |
131
131
  | 修改引用列表(增删引用的 Rem) | `edit-rem` | str_replace 简化 JSON 中的 `portalDirectlyIncludedRem` 数组 |
132
132
  | 移动 Portal(换父节点/位置) | `edit-tree` | 与移动普通行相同 |
133
- | 读取 Portal | `read-rem` | 自动输出 9 字段简化 JSON |
133
+ | 读取 Portal | `read-rem` | 自动输出 8 字段简化 JSON |
134
134
 
135
135
  ### 2.6 Powerup 机制简述
136
136
 
@@ -199,19 +199,53 @@ health → 确认三层就绪 → 会话可用
199
199
  disconnect → daemon 关闭 → 会话结束,缓存清空
200
200
  ```
201
201
 
202
- > **重要**:`connect` 成功只意味着 daemon 已启动,Plugin 并未自动连接。首次使用需用户在 RemNote「开发你的插件」中填入 `http://localhost:8080`;非首次只需刷新 RemNote 页面。必须引导用户完成此步后再用 `health` 确认就绪。
202
+ > **重要**:`connect` 成功只意味着 daemon 已启动,Plugin 并未自动连接。首次使用需用户在 RemNote「开发你的插件」中填入对应的 Plugin 服务地址;非首次只需刷新 RemNote 页面。必须引导用户完成此步后再用 `health` 确认就绪。
203
203
 
204
- `connect` 启动三个服务:
204
+ `connect` 启动三个服务,端口由槽位自动分配:
205
205
 
206
- | 服务 | 默认端口 | 用途 |
207
- |:-----|:---------|:-----|
208
- | WS Server | 3002 | CLI ↔ daemon ↔ Plugin 通信 |
209
- | Plugin 服务 | 8080 | 加载 Plugin 到 RemNote(默认静态服务器,`--dev` 时为 webpack-dev-server) |
210
- | ConfigServer | 3003 | HTTP 配置界面 |
206
+ | 服务 | 槽位 0 端口 | 用途 |
207
+ |:-----|:-----------|:-----|
208
+ | WS Server | 29100 | CLI ↔ daemon ↔ Plugin 通信 |
209
+ | Plugin 服务 | 29101 | 加载 Plugin 到 RemNote(默认静态服务器,`--dev` 时为 webpack-dev-server) |
210
+ | ConfigServer | 29102 | HTTP 配置界面 |
211
211
 
212
212
  超时机制:daemon 默认 **30 分钟无活动**自动关闭。每次收到 CLI 请求时重置计时器。
213
213
 
214
- ### 3.3 健康状态
214
+ ### 3.3 多实例支持
215
+
216
+ 系统支持最多 **4 个并发实例**,每个实例连接不同的 RemNote 知识库。
217
+
218
+ ```bash
219
+ # 启动默认实例
220
+ remnote-bridge connect
221
+
222
+ # 启动额外实例
223
+ remnote-bridge connect --instance work
224
+ remnote-bridge connect --instance personal
225
+
226
+ # 查看指定实例状态
227
+ remnote-bridge health --instance work
228
+
229
+ # 停止指定实例
230
+ remnote-bridge disconnect --instance work
231
+ ```
232
+
233
+ **槽位分配**:每个实例启动时自动占用第一个空闲槽位,端口固定分配:
234
+
235
+ | 槽位 | WS 端口 | Plugin 服务端口 | 配置端口 |
236
+ |:-----|:--------|:---------------|:---------|
237
+ | 0 | 29100 | 29101 | 29102 |
238
+ | 1 | 29110 | 29111 | 29112 |
239
+ | 2 | 29120 | 29121 | 29122 |
240
+ | 3 | 29130 | 29131 | 29132 |
241
+
242
+ **实例名解析优先级**:CLI `--instance` > 环境变量 `REMNOTE_BRIDGE_INSTANCE` > 默认值 `default`。
243
+
244
+ **Plugin 自动发现**:Plugin 启动后通过 `/api/discovery` 获取其孪生 daemon 的连接信息(WS 端口、槽位索引等),自动建立连接。一个 Plugin 可同时连接最多 4 个 daemon。
245
+
246
+ **业务命令的实例选择**:`read-rem`、`edit-rem` 等业务命令自动路由到 `--instance` 指定的实例(默认 `default`)。各实例的缓存相互独立。
247
+
248
+ ### 3.4 健康状态
215
249
 
216
250
  `health` 命令检查三层状态:
217
251
 
@@ -233,9 +267,12 @@ disconnect → daemon 关闭 → 会话结束,缓存清空
233
267
 
234
268
  | 命令 | 功能 | 需要 Plugin | 详细文档 |
235
269
  |:-----|:-----|:------------|:---------|
236
- | `connect` | 启动守护进程 | 否 | `connect.md` |
237
- | `health` | 检查系统状态 | 否 | `health.md` |
238
- | `disconnect` | 停止守护进程 | 否 | `disconnect.md` |
270
+ | `connect` | 启动守护进程(支持 `--instance`) | 否 | `connect.md` |
271
+ | `health` | 检查系统状态(支持 `--instance`) | 否 | `health.md` |
272
+ | `disconnect` | 停止守护进程(支持 `--instance`) | 否 | `disconnect.md` |
273
+ | `addon` | 管理增强项目(list/install/uninstall) | 否 | `addon.md` |
274
+ | `clean` | 清理所有进程、缓存和注册表 | 否 | `clean.md` |
275
+ | `install-skill` | 安装 Skill 到 Agent 环境 | 否 | `install-skill.md` |
239
276
 
240
277
  #### 读取命令
241
278
 
@@ -454,8 +491,8 @@ RemObject 是本项目对 RemNote Rem 的标准化表示,包含 51 个字段
454
491
  | 标记 | 含义 | 数量 | 输出条件 |
455
492
  |:-----|:-----|:-----|:---------|
456
493
  | RW | 可读可写 | 20 | 默认输出 |
457
- | R | 只读 | 14 | 默认输出 |
458
- | R-F | 只读低频 | 17 | 仅 `--full` 输出 |
494
+ | R | 只读 | 13 | 默认输出 |
495
+ | R-F | 只读低频 | 18 | 仅 `--full` 输出 |
459
496
 
460
497
  ### 7.2 核心字段速览
461
498
 
@@ -463,7 +500,7 @@ RemObject 是本项目对 RemNote Rem 的标准化表示,包含 51 个字段
463
500
  标识: id
464
501
  内容: text [RW], backText [RW]
465
502
  类型: type [RW], isDocument [RW]
466
- 结构: parent [RW], children [R]
503
+ 结构: parent [RW], children [R-F]
467
504
  格式: fontSize [RW], highlightColor [RW]
468
505
  状态: isTodo [RW], todoStatus [RW], isCode [RW], isQuote [RW],
469
506
  isListItem [RW], isCardItem [RW], isSlot [RW], isProperty [RW]
@@ -536,7 +573,14 @@ Portal:portalType [R], portalDirectlyIncludedRem [Portal-W]
536
573
 
537
574
  > 在 RemObject 格式化 JSON 中,数组内对象展开为多行。构造 edit-rem 的 oldStr/newStr 必须用多行格式。
538
575
 
539
- **highlightColor vs h**:`highlightColor` RemObject 顶层字段(字符串如 `"Red"`),`h` 是 RichText 行内格式标记(数字 0-9)。两者独立。
576
+ **⚠️ highlightColor vs h 两种完全不同的高亮**:
577
+
578
+ | 属性 | 位置 | 值类型 | 效果 |
579
+ |:-----|:-----|:-------|:-----|
580
+ | `highlightColor` | RemObject 顶层字段 | 字符串 `"Yellow"`/`"Red"` 等或 `null` | 整行背景色(左侧彩色竖条) |
581
+ | `h` | RichText 元素内部 | 数字 0-9 | 文字片段荧光底色 |
582
+
583
+ `h` 颜色值:0=无, 1=Red, 2=Orange, 3=Yellow, 4=Green, 5=Purple, 6=Blue, 7=Gray, 8=Brown, 9=Pink。
540
584
 
541
585
  **序列化确定性**:RichText 对象内部按 key 字母序排列(`sortRichTextKeys()`)。`_id` 的 `_`(U+005F)排在所有小写字母之前。这对 edit-rem 的 str_replace 和并发检测至关重要。
542
586
 
@@ -751,13 +795,10 @@ RemNote 的格式设置通过 Powerup 机制实现,会向 Rem 注入隐藏的
751
795
 
752
796
  ## 12. 配置系统
753
797
 
754
- 配置文件位于项目根目录:`.remnote-bridge.json`
798
+ 配置文件位于全局目录:`~/.remnote-bridge/config.json`(所有实例共享)。
755
799
 
756
800
  ```json
757
801
  {
758
- "wsPort": 3002,
759
- "devServerPort": 8080,
760
- "configPort": 3003,
761
802
  "daemonTimeoutMinutes": 30,
762
803
  "defaults": {
763
804
  "maxNodes": 200,
@@ -776,7 +817,21 @@ RemNote 的格式设置通过 Powerup 机制实现,会向 Rem 注入隐藏的
776
817
  ```
777
818
 
778
819
  - 文件不存在时使用全部默认值
779
- - 三个端口不允许冲突
820
+ - 端口由槽位自动分配(`~/.remnote-bridge/slots.json`),通常无需手动配置
821
+
822
+ ### 多实例文件布局
823
+
824
+ ```
825
+ ~/.remnote-bridge/
826
+ ├── config.json — 全局配置(所有实例共享)
827
+ ├── slots.json — 槽位端口定义(自动生成)
828
+ ├── registry.json — 实例→槽位映射(运行时维护)
829
+ └── instances/
830
+ ├── 0.pid / 0.log — 槽位 0 的 PID 和日志
831
+ ├── 1.pid / 1.log — 槽位 1
832
+ ├── 2.pid / 2.log — 槽位 2
833
+ └── 3.pid / 3.log — 槽位 3
834
+ ```
780
835
 
781
836
  ---
782
837
 
@@ -27,7 +27,7 @@
27
27
  ### 人类模式
28
28
 
29
29
  ```bash
30
- remnote-bridge read-context [--mode <mode>] [--ancestor-levels <N>] [--depth <N>] [--max-nodes <N>] [--max-siblings <N>]
30
+ remnote-bridge read-context [--mode <mode>] [--ancestor-levels <N>] [--depth <N>] [--max-nodes <N>] [--max-siblings <N>] [--focus-rem-id <remId>]
31
31
  ```
32
32
 
33
33
  | 参数/选项 | 类型 | 必需 | 说明 |
@@ -37,6 +37,7 @@ remnote-bridge read-context [--mode <mode>] [--ancestor-levels <N>] [--depth <N>
37
37
  | `--depth <N>` | integer | 否 | 展开深度(默认 3,仅 page 模式) |
38
38
  | `--max-nodes <N>` | integer | 否 | 全局节点上限(默认 200) |
39
39
  | `--max-siblings <N>` | integer | 否 | 每个父节点下展示的 children 上限(默认 20) |
40
+ | `--focus-rem-id <remId>` | string | 否 | 指定鱼眼中心 Rem ID(仅 focus 模式,默认使用当前焦点) |
40
41
 
41
42
  Focus 模式输出示例:
42
43
 
@@ -73,6 +74,7 @@ Page 模式输出示例:
73
74
 
74
75
  ```bash
75
76
  remnote-bridge read-context --json '{"mode":"focus","ancestorLevels":3,"maxNodes":100}'
77
+ remnote-bridge read-context --json '{"mode":"focus","focusRemId":"kLrIOHJLyMd8Y2lyA","ancestorLevels":2}'
76
78
  remnote-bridge read-context --json '{"mode":"page","depth":5,"maxSiblings":10}'
77
79
  ```
78
80
 
@@ -87,6 +89,7 @@ remnote-bridge read-context --json '{"mode":"page","depth":5,"maxSiblings":10}'
87
89
  | `depth` | number | 否 | 展开深度(默认 3,仅 page 模式) |
88
90
  | `maxNodes` | number | 否 | 全局节点上限(默认 200) |
89
91
  | `maxSiblings` | number | 否 | 每个父节点下展示的 children 上限(默认 20) |
92
+ | `focusRemId` | string | 否 | 指定鱼眼中心 Rem ID(仅 focus 模式,默认使用当前焦点) |
90
93
 
91
94
  ---
92
95
 
@@ -124,7 +127,7 @@ remnote-bridge read-context --json '{"mode":"page","depth":5,"maxSiblings":10}'
124
127
  }
125
128
  ```
126
129
 
127
- ### 无焦点 Rem(focus 模式)
130
+ ### 无焦点 Rem(focus 模式,未指定 focusRemId)
128
131
 
129
132
  ```json
130
133
  {
@@ -135,6 +138,28 @@ remnote-bridge read-context --json '{"mode":"page","depth":5,"maxSiblings":10}'
135
138
  }
136
139
  ```
137
140
 
141
+ ### focusRemId 指定的 Rem 不存在
142
+
143
+ ```json
144
+ {
145
+ "ok": false,
146
+ "command": "read-context",
147
+ "error": "指定的 Rem 不存在: kLrIOHJLyMd8Y2lyA",
148
+ "timestamp": "2026-03-07T10:00:00.000Z"
149
+ }
150
+ ```
151
+
152
+ ### page 模式下误传 focusRemId
153
+
154
+ ```json
155
+ {
156
+ "ok": false,
157
+ "command": "read-context",
158
+ "error": "focusRemId 仅在 focus 模式下有效,page 模式下请勿指定",
159
+ "timestamp": "2026-03-07T10:00:00.000Z"
160
+ }
161
+ ```
162
+
138
163
  ### 无打开的页面(page 模式)
139
164
 
140
165
  ```json
@@ -182,15 +207,15 @@ remnote-bridge read-context --json '{"mode":"page","depth":5,"maxSiblings":10}'
182
207
  ## 内部流程
183
208
 
184
209
  ```
185
- 1. CLI 解析参数(mode, ancestorLevels, depth, maxNodes, maxSiblings)
210
+ 1. CLI 解析参数(mode, ancestorLevels, depth, maxNodes, maxSiblings, focusRemId
186
211
  2. sendRequest → WS → daemon
187
212
  3. daemon ContextReadHandler:
188
213
  ├─ 合并配置默认值(config.ts)
189
- └─ forwardToPlugin('read_context', { mode, ancestorLevels, depth, maxNodes, maxSiblings })
214
+ └─ forwardToPlugin('read_context', { mode, ancestorLevels, depth, maxNodes, maxSiblings, focusRemId })
190
215
  4. Plugin 端 readContext() 分支:
191
216
 
192
217
  ├─ mode === 'focus' → readContextFocus():
193
- │ ├─ plugin.focus.getFocusedRem() → 获取焦点 Rem
218
+ │ ├─ focusRemId ? plugin.rem.findOne(focusRemId) : plugin.focus.getFocusedRem() → 获取焦点 Rem
194
219
  │ ├─ 向上追溯 ancestorLevels 层 → ancestorPath[]
195
220
  │ ├─ buildBreadcrumb(plugin, focusRem) → 面包屑路径
196
221
  │ ├─ 从最顶层祖先开始递归 buildFisheyeNode():
@@ -274,8 +299,8 @@ focus 模式的核心特点是**渐进式展开**——离焦点越近,展开
274
299
 
275
300
  | 维度 | focus 模式 | page 模式 |
276
301
  |:-----|:----------|:----------|
277
- | 触发点 | 当前焦点 Rem(光标位置) | 当前打开的页面 Rem |
278
- | SDK 入口 | `plugin.focus.getFocusedRem()` | `plugin.window.getFocusedPaneId()` → `getOpenPaneRemId()` |
302
+ | 触发点 | 当前焦点 Rem 或指定 `focusRemId` | 当前打开的页面 Rem |
303
+ | SDK 入口 | `focusRemId` ? `plugin.rem.findOne()` : `plugin.focus.getFocusedRem()` | `plugin.window.getFocusedPaneId()` → `getOpenPaneRemId()` |
279
304
  | 深度参数 | `ancestorLevels`(向上几层) | `depth`(向下几层) |
280
305
  | 展开策略 | 鱼眼(焦点完全展开,周围递减) | 均匀展开(全树同深度控制) |
281
306
  | 大纲头注释 | `<!-- path: ... -->` + `<!-- focus: Name (id) -->` | `<!-- page: Name -->` + `<!-- path: ... -->` |
@@ -345,7 +370,8 @@ focus 模式的核心特点是**渐进式展开**——离焦点越近,展开
345
370
  ## 注意事项
346
371
 
347
372
  - read-context **不缓存**结果——每次调用都重新从 SDK 获取最新数据
348
- - focus 模式需要用户在 RemNote 中有焦点 Rem(光标需在某个 Rem 上)
373
+ - focus 模式需要用户在 RemNote 中有焦点 Rem(光标需在某个 Rem 上),或通过 `focusRemId` 指定任意 Rem 作为鱼眼中心
374
+ - `focusRemId` 仅在 focus 模式下有效,page 模式下传入会报错
349
375
  - page 模式需要 RemNote 中有打开的页面
350
376
  - 输出使用 `createMinimalSerializableRem` 序列化,不包含 backText、practiceDirection 等详细信息
351
377
  - Powerup 过滤是硬编码的,无法通过参数关闭
@@ -9,7 +9,7 @@
9
9
  `read-rem` 通过 Rem ID 读取一个 Rem 的所有可获取属性,返回标准化的 RemObject。读取结果会被缓存在 daemon 内存中,供后续 `edit-rem` 使用。
10
10
 
11
11
  核心能力:
12
- - 返回 51 个字段的完整 Rem 数据(默认 34 个,Portal 简化 9 个,`--full` 时 51 个)
12
+ - 返回 51 个字段的完整 Rem 数据(默认 33 个,Portal 简化 8 个,`--full` 时 51 个)
13
13
  - 支持 `--fields` 指定字段子集
14
14
  - 支持 Powerup 噪音过滤(默认过滤)
15
15
  - 自动缓存,为 `edit-rem` 建立编辑基础
@@ -76,12 +76,11 @@ remnote-bridge read-rem --json '{"remId":"kLrIOHJLyMd8Y2lyA"}'
76
76
  "type": "concept",
77
77
  "isDocument": false,
78
78
  "parent": "parentRemId",
79
- "children": ["childId1", "childId2"],
80
79
  "fontSize": null,
81
80
  "highlightColor": null,
82
81
  "isTodo": false,
83
82
  "todoStatus": null,
84
- "...": "(共 34 个字段,--full 时 51 个)"
83
+ "...": "(共 33 个字段,--full 时 51 个)"
85
84
  },
86
85
  "timestamp": "2026-03-06T10:00:00.000Z"
87
86
  }
@@ -159,8 +158,8 @@ remnote-bridge read-rem --json '{"remId":"kLrIOHJLyMd8Y2lyA"}'
159
158
  ├─ 字段过滤:
160
159
  │ ├─ --full → 返回全部 51 字段
161
160
  │ ├─ --fields → 返回指定字段 + id
162
- │ ├─ type=portal → Portal 简化模式(返回 9 个关键字段)
163
- │ └─ 默认 → 排除 R-F 字段(返回 34 字段)
161
+ │ ├─ type=portal → Portal 简化模式(返回 8 个关键字段)
162
+ │ └─ 默认 → 排除 R-F 字段(返回 33 字段)
164
163
  └─ 附加 _cacheOverridden 元数据(若之前有缓存)
165
164
  4. CLI 格式化输出(人类模式 pretty-print / JSON 模式单行)
166
165
  ```
@@ -172,8 +171,8 @@ remnote-bridge read-rem --json '{"remId":"kLrIOHJLyMd8Y2lyA"}'
172
171
  RemObject 共 51 个字段,按读写权限分为三类:
173
172
 
174
173
  - **RW**(20 个):可读可写,SDK 有对应的 setter
175
- - **R**(14 个):只读,默认输出
176
- - **R-F**(17 个):只读,仅 `--full` 模式输出(低频 / 可由其他字段推导)
174
+ - **R**(13 个):只读,默认输出
175
+ - **R-F**(18 个):只读,仅 `--full` 模式输出(低频 / 可由其他字段推导)
177
176
 
178
177
  ### 核心标识
179
178
 
@@ -200,7 +199,7 @@ RemObject 共 51 个字段,按读写权限分为三类:
200
199
  | 字段 | 类型 | 权限 | 说明 |
201
200
  |------|------|:----:|------|
202
201
  | `parent` | `string \| null` | RW | 父 Rem ID。null=顶级。UI:Rem 从原位置消失,出现在新父级下 |
203
- | `children` | `string[]` | R | 子 Rem ID 有序数组 |
202
+ | `children` | `string[]` | R-F | 子 Rem ID 有序数组 |
204
203
 
205
204
  ### 格式 / 显示
206
205
 
@@ -377,14 +376,15 @@ text | number | date | checkbox | single_select | multi_select | url | image | t
377
376
 
378
377
  | 模式 | 输出字段数 | 说明 |
379
378
  |------|:----------:|------|
380
- | 默认 | 34 | RW + R 字段,覆盖常用场景 |
381
- | Portal 简化 | 9 | type=portal 时自动使用(id、type、portalType、portalDirectlyIncludedRem、parent、positionAmongstSiblings、children、createdAt、updatedAt)。`--full` / `--fields` 可覆盖 |
379
+ | 默认 | 33 | RW + R 字段,覆盖常用场景 |
380
+ | Portal 简化 | 8 | type=portal 时自动使用(id、type、portalType、portalDirectlyIncludedRem、parent、positionAmongstSiblings、createdAt、updatedAt)。`--full` / `--fields` 可覆盖 |
382
381
  | `--full` | 51 | 全部字段(含 R-F 低频字段) |
383
382
  | `--fields` | 自选 + id | 仅返回指定字段(始终包含 id) |
384
383
 
385
384
  ### R-F 字段列表(默认不输出,`--full` 时输出)
386
385
 
387
386
  ```
387
+ children,
388
388
  isPowerup, isPowerupEnum, isPowerupProperty, isPowerupPropertyListItem, isPowerupSlot,
389
389
  deepRemsBeingReferenced,
390
390
  ancestorTagRem, descendantTagRem,
@@ -1,12 +1,26 @@
1
1
  # search
2
2
 
3
- > 在知识库中按文本搜索 Rem,返回匹配结果列表。
3
+ > 在知识库中搜索 Rem,返回匹配结果列表。支持 SDK 全文搜索和 RAG 语义搜索。
4
4
 
5
5
  ---
6
6
 
7
7
  ## 功能
8
8
 
9
- `search` 调用 RemNote SDK 的全文搜索 API(`plugin.search.search()`),在当前知识库中搜索包含关键词的 Rem,返回匹配结果的 ID、文本和文档标记。
9
+ `search` 在当前知识库中搜索包含关键词的 Rem。搜索方式由配置驱动:
10
+
11
+ - **RAG 语义搜索**:在 `~/.remnote-bridge/config.json` 中启用 `addons.remnote-rag`(`enabled: true`),并在 `~/.remnote-bridge/addons/remnote-rag/config.json` 中配置 API Key,系统自动使用语义向量搜索(中文支持更好)
12
+ - **SDK 全文搜索**:addon 未启用、未安装、或调用失败时自动降级
13
+
14
+ 安装与管理 addon:`remnote-bridge addon install remnote-rag` / `remnote-bridge addon list`。
15
+
16
+ ### 搜索来源(source 字段)
17
+
18
+ | source | 说明 | 返回字段 |
19
+ |--------|------|----------|
20
+ | `"rag"` | 语义向量搜索(remnote-rag 已启用且可用) | remId, text, backText, ancestorPath, type, isDocument, tags, score |
21
+ | `"sdk"` | SDK 全文搜索(默认降级) | remId, text, isDocument |
22
+
23
+ 降级场景:addon 未启用、remnote-rag 未安装、向量库未索引、子进程超时(10s)、返回异常。
10
24
 
11
25
  ---
12
26
 
@@ -58,7 +72,33 @@ remnote-bridge search --json '{"query":"机器学习","numResults":10}'
58
72
 
59
73
  ## JSON 输出
60
74
 
61
- ### 成功
75
+ ### 成功(RAG 模式)
76
+
77
+ ```json
78
+ {
79
+ "ok": true,
80
+ "command": "search",
81
+ "data": {
82
+ "query": "机器学习",
83
+ "results": [
84
+ {
85
+ "remId": "kLrIOHJLyMd8Y2lyA",
86
+ "text": "支持向量机的核心思想",
87
+ "backText": "最大化分类间隔",
88
+ "ancestorPath": ["机器学习", "监督学习"],
89
+ "type": "concept",
90
+ "isDocument": false,
91
+ "tags": ["统计学习"],
92
+ "score": 0.87
93
+ }
94
+ ],
95
+ "totalFound": 1,
96
+ "source": "rag"
97
+ }
98
+ }
99
+ ```
100
+
101
+ ### 成功(SDK 降级模式)
62
102
 
63
103
  ```json
64
104
  {
@@ -70,7 +110,8 @@ remnote-bridge search --json '{"query":"机器学习","numResults":10}'
70
110
  { "remId": "kLrIOHJLyMd8Y2lyA", "text": "机器学习笔记", "isDocument": true },
71
111
  { "remId": "abc123def456", "text": "监督学习与机器学习的关系", "isDocument": false }
72
112
  ],
73
- "totalFound": 2
113
+ "totalFound": 2,
114
+ "source": "sdk"
74
115
  },
75
116
  "timestamp": "2026-03-06T10:00:00.000Z"
76
117
  }
@@ -119,29 +160,47 @@ remnote-bridge search --json '{"query":"机器学习","numResults":10}'
119
160
 
120
161
  ```
121
162
  1. CLI 解析参数(query, numResults)
122
- 2. sendRequest → WS → daemon → forwardToPlugin('search', { query, numResults })
123
- 3. Plugin 端:
124
- ├─ plugin.search.search([query], undefined, { numResults })
125
- ├─ 遍历结果 Rem:
126
- ├─ plugin.richText.toMarkdown(rem.text) Markdown 文本
127
- ├─ 换行符替换为空格(保持单行)
128
- │ └─ rem.isDocument() → 是否文档
129
- └─ 返回 { query, results, totalFound }
130
- 4. CLI 格式化输出
163
+ 2. 检查配置:addons.remnote-rag.enabled
164
+ ├─ 未启用 → 跳到步骤 4
165
+ └─ 已启用 继续步骤 3
166
+ 3. 尝试 RAG 搜索:
167
+ ├─ execFile('remnote-rag', ['search', '--json', payload])
168
+ ├─ remnote-rag 从 ~/.remnote-bridge/addons/remnote-rag/config.json 读取配置
169
+ ├─ 超时 10s
170
+ ├─ 成功 返回 RAG 结果(source: "rag")
171
+ └─ 失败 → 继续到步骤 4(静默降级)
172
+ 4. 降级到 SDK 搜索:
173
+ ├─ sendRequest → WS → daemon → forwardToPlugin('search', ...)
174
+ ├─ Plugin 端调用 plugin.search.search()
175
+ └─ 返回 SDK 结果(source: "sdk")
176
+ 5. CLI 格式化输出
131
177
  ```
132
178
 
133
179
  ---
134
180
 
135
181
  ## 结果字段说明
136
182
 
183
+ ### 通用字段(两种模式都有)
184
+
137
185
  | 字段 | 类型 | 说明 |
138
186
  |------|------|------|
139
187
  | `query` | string | 原始搜索关键词 |
140
188
  | `results` | array | 结果数组 |
141
189
  | `results[].remId` | string | 匹配 Rem 的 ID |
142
- | `results[].text` | string | Rem 正面文本(Markdown 格式,单行) |
190
+ | `results[].text` | string | Rem 正面文本 |
143
191
  | `results[].isDocument` | boolean | 是否为文档页面 |
144
192
  | `totalFound` | number | 返回的结果数量 |
193
+ | `source` | string | 搜索来源:`"rag"` 或 `"sdk"` |
194
+
195
+ ### RAG 模式额外字段
196
+
197
+ | 字段 | 类型 | 说明 |
198
+ |------|------|------|
199
+ | `results[].backText` | string \| null | Rem 背面文本 |
200
+ | `results[].ancestorPath` | string[] | 祖先路径名称数组(从根到父级) |
201
+ | `results[].type` | string | Rem 类型:`"concept"` / `"descriptor"` / `"default"` |
202
+ | `results[].tags` | string[] | 标签名称数组 |
203
+ | `results[].score` | number | 语义相关性分数(0-1,越高越相关) |
145
204
 
146
205
  ---
147
206
 
@@ -91,7 +91,7 @@ setup 会弹出 Chrome 窗口,用户需要完成两件事:登录 RemNote +
91
91
  2. **立即告知用户**:
92
92
  > 已打开 Chrome 浏览器。请完成以下操作:
93
93
  > 1. 登录 RemNote
94
- > 2. 在 RemNote 中配置开发插件:点击左下角插件图标 → 开发你的插件 → 输入 `http://localhost:8080`
94
+ > 2. 在 RemNote 中配置开发插件:点击左下角插件图标 → 开发你的插件 → 输入 connect 输出的 Plugin 服务地址(如 `http://localhost:29101`)
95
95
  > 3. 完成后彻底退出 Chrome(macOS 请按 Cmd+Q,仅关窗口不够)
96
96
  3. 等待 `setup` 命令返回(阻塞式,超时 600 秒)
97
97
  4. 收到成功 → 继续执行 `connect --headless`