remnote-bridge 0.1.12 → 0.1.13

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.
@@ -128,7 +128,7 @@ Rem 有两个**独立维度**的类型:
128
128
  |:-----|:-----|:-----|
129
129
  | 创建 Portal | `edit-tree` | 新增行 `<!--portal refs:id1,id2-->` |
130
130
  | 删除 Portal | `edit-tree` | 从大纲中移除 Portal 行(与删除普通行相同) |
131
- | 修改引用列表(增删引用的 Rem) | `edit-rem` | str_replace 简化 JSON 中的 `portalDirectlyIncludedRem` 数组 |
131
+ | 修改引用列表(增删引用的 Rem) | `edit-rem` | 直接修改 changes 中的 `portalDirectlyIncludedRem` 数组 |
132
132
  | 移动 Portal(换父节点/位置) | `edit-tree` | 与移动普通行相同 |
133
133
  | 读取 Portal | `read-rem` | 自动输出 8 字段简化 JSON |
134
134
 
@@ -288,7 +288,7 @@ remnote-bridge disconnect --instance work
288
288
 
289
289
  | 命令 | 功能 | 前置条件 | 安全机制 | 详细文档 |
290
290
  |:-----|:-----|:---------|:---------|:---------|
291
- | `edit-rem` | str_replace 编辑 Rem JSON 字段 | 先 `read-rem` | 三道防线 | `edit-rem.md` |
291
+ | `edit-rem` | 直接修改 Rem 属性字段 | 先 `read-rem` | 两道防线 + 字段白名单 | `edit-rem.md` |
292
292
  | `edit-tree` | str_replace 编辑树结构 | 先 `read-tree` | 三道防线 + diff | `edit-tree.md` |
293
293
 
294
294
  ### 4.2 探索决策指南
@@ -410,7 +410,7 @@ Agent 需要根据用户意图选择正确的读取命令:
410
410
  ```bash
411
411
  read-rem kLrIOHJLyMd8Y2lyA --fields text,type
412
412
  read-tree kLrIOHJLyMd8Y2lyA --depth 2
413
- edit-rem kLrIOHJLyMd8Y2lyA --old-str '"concept"' --new-str '"descriptor"'
413
+ edit-rem kLrIOHJLyMd8Y2lyA --changes '{"type":"descriptor"}'
414
414
  ```
415
415
 
416
416
  - 位置参数 = remId 或关键词
@@ -422,7 +422,7 @@ edit-rem kLrIOHJLyMd8Y2lyA --old-str '"concept"' --new-str '"descriptor"'
422
422
  ```bash
423
423
  read-rem --json '{"remId":"kLrIOHJLyMd8Y2lyA","fields":["text","type"]}'
424
424
  read-tree --json '{"remId":"kLrIOHJLyMd8Y2lyA","depth":2}'
425
- edit-rem --json '{"remId":"kLrIOHJLyMd8Y2lyA","oldStr":"\"concept\"","newStr":"\"descriptor\""}'
425
+ edit-rem --json '{"remId":"kLrIOHJLyMd8Y2lyA","changes":{"type":"descriptor"}}'
426
426
  ```
427
427
 
428
428
  - **位置参数 = JSON 字符串**,所有参数打包在 JSON 对象中
@@ -571,7 +571,7 @@ Portal:portalType [R], portalDirectlyIncludedRem [Portal-W]
571
571
  { "i": "a", "onlyAudio": true, "url": "..." } // 音频
572
572
  ```
573
573
 
574
- > 在 RemObject 格式化 JSON 中,数组内对象展开为多行。构造 edit-rem 的 oldStr/newStr 必须用多行格式。
574
+ > 在 RemObject 格式化 JSON 中,数组内对象展开为多行。构造 edit-tree 的 oldStr/newStr 或 edit-rem 的 changes 中 RichText 值时,需注意多行格式。
575
575
 
576
576
  **⚠️ highlightColor vs h — 两种完全不同的高亮**:
577
577
 
@@ -582,7 +582,7 @@ Portal:portalType [R], portalDirectlyIncludedRem [Portal-W]
582
582
 
583
583
  `h` 颜色值:0=无, 1=Red, 2=Orange, 3=Yellow, 4=Green, 5=Purple, 6=Blue, 7=Gray, 8=Brown, 9=Pink。
584
584
 
585
- **序列化确定性**:RichText 对象内部按 key 字母序排列(`sortRichTextKeys()`)。`_id` 的 `_`(U+005F)排在所有小写字母之前。这对 edit-rem 的 str_replace 和并发检测至关重要。
585
+ **序列化确定性**:RichText 对象内部按 key 字母序排列(`sortRichTextKeys()`)。`_id` 的 `_`(U+005F)排在所有小写字母之前。这对乐观并发检测和 edit-tree 的 str_replace 至关重要。
586
586
 
587
587
  ---
588
588
 
@@ -690,7 +690,7 @@ read-tree / read-globe / read-context 的输出核心是 Markdown 大纲文本
690
690
 
691
691
  | 前缀 | 用途 | 写入命令 |
692
692
  |:-----|:-----|:---------|
693
- | `rem:{remId}` | RemObject JSON | read-rem |
693
+ | `rem:{remId}` | RemObject 对象 | read-rem |
694
694
  | `tree:{remId}` | Markdown 大纲 | read-tree |
695
695
  | `tree-depth:{remId}` 等 | read-tree 参数 | read-tree |
696
696
 
@@ -698,9 +698,9 @@ read-tree / read-globe / read-context 的输出核心是 Markdown 大纲文本
698
698
  - disconnect 关闭 daemon 时缓存自动消失
699
699
  - **没有 TTL**——三道防线的并发检测已能捕获所有陈旧数据
700
700
 
701
- ### 9.2 三道防线
701
+ ### 9.2 安全防线
702
702
 
703
- `edit-rem` 和 `edit-tree` 使用 str_replace 语义编辑数据。为防止数据损坏,实施三道防线:
703
+ `edit-rem` 和 `edit-tree` 编辑数据时,实施多道防线防止数据损坏:
704
704
 
705
705
  #### 防线 1:缓存存在性检查
706
706
 
@@ -718,7 +718,16 @@ edit 时重新从 SDK 读取最新数据 → 与缓存严格比较
718
718
 
719
719
  如果 Rem 在 read 之后被外部修改(用户在 RemNote UI 中编辑、其他 Agent 修改等),数据不一致时拒绝编辑,**且不更新缓存**——迫使 Agent 重新 read。
720
720
 
721
- #### 防线 3:str_replace 精确匹配
721
+ #### edit-rem 防线 3:字段白名单校验
722
+
723
+ ```
724
+ changes 中的字段必须在 RW 白名单内,枚举值必须合法
725
+ ```
726
+
727
+ - 只读字段(R / R-F)写入时**警告跳过**,不阻断其他字段
728
+ - 枚举值非法时**报错拒绝**(如 `type: "invalid"`)
729
+
730
+ #### edit-tree 防线 3:str_replace 精确匹配
722
731
 
723
732
  ```
724
733
  oldStr 必须在目标文本中恰好匹配 1 次
@@ -733,7 +742,8 @@ oldStr 必须在目标文本中恰好匹配 1 次
733
742
  | 场景 | 缓存行为 |
734
743
  |:-----|:---------|
735
744
  | 写入全部成功 | 从 SDK 重新读取最新状态 → **更新缓存** |
736
- | 防线拒绝 | **不更新缓存**(迫使 Agent 重新 read) |
745
+ | 防线拒绝(缓存缺失 / 并发冲突) | **不更新缓存**(迫使 Agent 重新 read) |
746
+ | 枚举值非法 | **报错拒绝**,不更新缓存 |
737
747
  | 部分写入失败 | **不更新缓存** |
738
748
 
739
749
  ---
@@ -854,13 +864,12 @@ Agent 遇到错误时的诊断和恢复指南:
854
864
  | has not been read yet | 未先执行 read-rem / read-tree | 执行对应 read 命令后重试 |
855
865
  | has been modified since last read | Rem 在 read 和 edit 之间被外部修改 | 重新执行 read 获取最新状态后重试 |
856
866
 
857
- ### str_replace 错误
867
+ ### edit-tree str_replace 错误
858
868
 
859
869
  | 错误 | 原因 | 恢复 |
860
870
  |:-----|:-----|:-----|
861
- | old_str not found | oldStr 在目标文本中不存在 | 检查 oldStr 是否精确匹配(含引号、空格、换行) |
871
+ | old_str not found | oldStr 在目标文本中不存在 | 检查 oldStr 是否精确匹配(含空格、换行、缩进) |
862
872
  | old_str matches N locations | oldStr 匹配到多个位置 | 扩大 oldStr 范围,包含更多上下文以唯一定位 |
863
- | invalid JSON | 替换后的文本不是合法 JSON | 检查 newStr 的引号、逗号、括号完整性 |
864
873
 
865
874
  ### edit-tree 专用错误
866
875
 
@@ -422,7 +422,7 @@ localUpdatedAt, lastPracticed
422
422
  | `tags` | `rem.addTag()` / `rem.removeTag()` | **Diff 机制**:对比当前 vs 目标,增删差异项。必须列出完整目标数组,缺少的会被删除 |
423
423
  | `sources` | `rem.addSource()` / `rem.removeSource()` | **Diff 机制**:同 tags |
424
424
  | `positionAmongstSiblings` | `rem.setParent(parent, position)` | 与 `parent` 联动(见下方说明) |
425
- | `portalDirectlyIncludedRem` | `rem.addToPortal()` / `rem.removeFromPortal()` | string[] | **Portal-W Diff 机制**:仅 type=portal 时可修改。对比当前 vs 目标数组,逐项增删。调用方向:在被引用 Rem 上调用,参数是 Portal Rem |
425
+ | `portalDirectlyIncludedRem` | `rem.addToPortal()` / `rem.removeFromPortal()` | **Portal-W Diff 机制**:仅 type=portal 时可修改。对比当前 vs 目标数组,逐项增删 |
426
426
 
427
427
  ### parent + positionAmongstSiblings 联动
428
428
 
@@ -434,7 +434,7 @@ localUpdatedAt, lastPracticed
434
434
  | 只有 `parent` 变更 | `setParent(newParent)` 不带 position(保持末尾) |
435
435
  | 只有 `positionAmongstSiblings` 变更 | 获取当前 parent → `setParent(currentParent, newPosition)` |
436
436
 
437
- **应在同一次 str_replace 中同时修改这两个字段。**
437
+ **应在同一次 edit-rem 的 changes 中同时传入这两个字段。**
438
438
 
439
439
  ---
440
440
 
@@ -561,7 +561,7 @@ RemObject 中的 `text` 和 `backText` 字段使用 RichText 格式——一个
561
561
 
562
562
  ### 序列化确定性
563
563
 
564
- RichText 对象元素内部按 **key 字母序排列**(Plugin 端 `sortRichTextKeys()` 处理),确保同一内容的序列化 JSON 始终一致。这对 `edit-rem` 的 str_replace 和乐观并发检测至关重要。
564
+ RichText 对象元素内部按 **key 字母序排列**(Plugin 端 `sortRichTextKeys()` 处理),确保同一内容的序列化 JSON 始终一致。这对乐观并发检测至关重要。
565
565
 
566
566
  - `_`(下划线)在 Unicode 中排在所有小写字母之前(`_` = U+005F,`a` = U+0061),所以 `_id` 总是排在第一位
567
567
  - 排序由 `Object.keys().sort()` 决定,即 JavaScript 默认的 Unicode 字典序
@@ -589,11 +589,11 @@ RichText 对象元素内部按 **key 字母序排列**(Plugin 端 `sortRichTex
589
589
  |------|------|
590
590
  | 读取成功 | 完整 JSON 写入缓存 `cache.set('rem:' + remId, fullJson)` |
591
591
  | 已有缓存 | 覆盖旧缓存,返回 `cacheOverridden` 元数据 |
592
- | 缓存用途 | 供 `edit-rem` 的三道防线使用(存在性检查 + 乐观并发检测 + str_replace) |
592
+ | 缓存用途 | 供 `edit-rem` 的防线检查使用(存在性检查 + 乐观并发检测) |
593
593
  | 缓存存储 | daemon 内存中的 LRU 缓存(最大 200 条目) |
594
594
  | 缓存清空 | daemon 关闭时自动消失 |
595
595
 
596
- **重要**:缓存存储的是 **完整 RemObject**(含 R-F 字段),不受 `--fields` / `--full` 选项影响。字段过滤仅作用于返回给 CLI 的输出。
596
+ **重要**:缓存存储的是 **完整 RemObject 对象**(含 R-F 字段),不受 `--fields` / `--full` 选项影响。字段过滤仅作用于返回给 CLI 的输出。
597
597
 
598
598
  ---
599
599
 
@@ -56,7 +56,7 @@ remnote-bridge search <query> [--limit <N>]
56
56
  ### JSON 模式
57
57
 
58
58
  ```bash
59
- remnote-bridge search --json '{"query":"机器学习","numResults":10}'
59
+ remnote-bridge search --json '{"query":"机器学习","limit":10}'
60
60
  ```
61
61
 
62
62
  ---
@@ -66,7 +66,7 @@ remnote-bridge search --json '{"query":"机器学习","numResults":10}'
66
66
  | 字段 | 类型 | 必需 | 说明 |
67
67
  |------|------|:----:|------|
68
68
  | `query` | string | 是 | 搜索关键词(不能为空) |
69
- | `numResults` | number | 否 | 结果数量上限(默认 20) |
69
+ | `limit` | number | 否 | 结果数量上限(默认 20) |
70
70
 
71
71
  ---
72
72
 
@@ -159,7 +159,7 @@ remnote-bridge search --json '{"query":"机器学习","numResults":10}'
159
159
  ## 内部流程
160
160
 
161
161
  ```
162
- 1. CLI 解析参数(query, numResults
162
+ 1. CLI 解析参数(query, limit
163
163
  2. 检查配置:addons.remnote-rag.enabled
164
164
  ├─ 未启用 → 跳到步骤 4
165
165
  └─ 已启用 → 继续步骤 3
@@ -250,6 +250,6 @@ RemNote 的 Web 版搜索索引按单字符拆分非空格语言的文本。搜
250
250
  ## 注意事项
251
251
 
252
252
  - 搜索结果的 `text` 字段是 Markdown 格式的单行文本(多行换行符已替换为空格)
253
- - `totalFound` 等于 `results.length`,即实际返回的数量(受 `numResults` 限制)
253
+ - `totalFound` 等于 `results.length`,即实际返回的数量(受 `limit` 限制)
254
254
  - 搜索不会触发缓存写入——search 结果不进入 RemCache
255
255
  - 如需获取某个搜索结果的完整属性,需对其 `remId` 执行 `read-rem`
@@ -83,22 +83,21 @@ remnote-bridge --json setup
83
83
 
84
84
  ## AI Agent 使用流程
85
85
 
86
- setup 会弹出 Chrome 窗口,用户需要完成两件事:登录 RemNote + 配置 dev plugin
86
+ setup 会弹出 Chrome 窗口,用户只需**登录 RemNote**,然后彻底退出 Chrome
87
+
88
+ setup 只负责保存登录凭证——配置 dev plugin 是 connect 之后的事(见 `connect.md` 标准模式说明)。
87
89
 
88
90
  ### 交互步骤
89
91
 
90
92
  1. 调用 `setup`
91
93
  2. **立即告知用户**:
92
- > 已打开 Chrome 浏览器。请完成以下操作:
93
- > 1. 登录 RemNote
94
- > 2. 在 RemNote 中配置开发插件:点击左下角插件图标 → 开发你的插件 → 输入 connect 输出的 Plugin 服务地址(如 `http://localhost:29101`)
95
- > 3. 完成后彻底退出 Chrome(macOS 请按 Cmd+Q,仅关窗口不够)
94
+ > 已打开 Chrome 浏览器。请登录 RemNote,完成后彻底退出 Chrome(macOS 请按 Cmd+Q,仅关窗口不够)
96
95
  3. 等待 `setup` 命令返回(阻塞式,超时 600 秒)
97
96
  4. 收到成功 → 继续执行 `connect --headless`
98
97
 
99
98
  ### setup 之后
100
99
 
101
- `setup` 只需执行一次。登录凭证和 plugin 配置都已保存,之后每次只需 `connect --headless` 即可自动连接,无需用户操作。
100
+ `setup` 只需执行一次。登录凭证已保存,之后每次只需 `connect --headless` 即可自动连接,无需用户操作。
102
101
 
103
102
  如果后续 headless 模式下 Plugin 始终不连接,可能是 RemNote 登录 session 过期,需重新 setup(删除 `~/.remnote-bridge/chrome-profile/.setup-done` 后重新执行)。
104
103