remnote-bridge 0.1.14 → 0.1.16

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.
package/README.md CHANGED
@@ -292,6 +292,12 @@ remnote-bridge addon uninstall remnote-rag --purge
292
292
 
293
293
  ## Changelog
294
294
 
295
+ ### 0.1.15 (2026-03-18)
296
+
297
+ - **Defense-2 false positive fix** — Removed `.sort()` from ID arrays in RemObject serialization that caused spurious concurrency conflicts in edit-rem
298
+ - **Documentation overhaul** — Strengthened headless mode warnings, added anti-hallucination guardrails for Plugin loading instructions, restructured edit-tree template/fallback dual-mode docs
299
+ - **Skill ↔ MCP doc sync** — Aligned connect, edit-tree, overall instructions across Skill and MCP layers
300
+
295
301
  ### 0.1.14 (2026-03-18)
296
302
 
297
303
  - **read-rem-in-tree** — New command: fetch subtree outline + all Rem objects in one call (batch read for bulk edits)
package/README.zh-CN.md CHANGED
@@ -292,6 +292,12 @@ remnote-bridge addon uninstall remnote-rag --purge
292
292
 
293
293
  ## Changelog
294
294
 
295
+ ### 0.1.15 (2026-03-18)
296
+
297
+ - **防线 2 误判修复** — 移除 RemObject 序列化中 ID 数组的 `.sort()`,消除 edit-rem 并发检测的假阳性
298
+ - **文档全面优化** — 加强 headless 模式警告、新增 Plugin 加载防幻觉红线、重组 edit-tree 模板/回退双模式文档
299
+ - **Skill ↔ MCP 文档同步** — connect、edit-tree、overall 说明在 Skill 和 MCP 层全面对齐
300
+
295
301
  ### 0.1.14 (2026-03-18)
296
302
 
297
303
  - **read-rem-in-tree** — 新命令:一次调用获取子树大纲 + 所有 Rem 对象(批量读取,用于批量编辑场景)
@@ -33,7 +33,12 @@ export async function editTreeCommand(remId, options) {
33
33
  return;
34
34
  }
35
35
  if (json) {
36
- jsonOutput({ ok: true, command: 'edit-tree', operations: data.operations });
36
+ jsonOutput({
37
+ ok: true,
38
+ command: 'edit-tree',
39
+ operations: data.operations,
40
+ ...(data.templateWarnings?.length && { templateWarnings: data.templateWarnings }),
41
+ });
37
42
  }
38
43
  else {
39
44
  if (data.operations.length === 0) {
@@ -46,5 +51,11 @@ export async function editTreeCommand(remId, options) {
46
51
  console.log(` - ${o.type}: ${JSON.stringify(o)}`);
47
52
  }
48
53
  }
54
+ if (data.templateWarnings?.length) {
55
+ console.log('⚠ 模板/前缀警告:');
56
+ for (const w of data.templateWarnings) {
57
+ console.log(` - ${w}`);
58
+ }
59
+ }
49
60
  }
50
61
  }
@@ -63,6 +63,9 @@ export class EditHandler {
63
63
  const currentJson = JSON.stringify(currentRemObject, null, 2);
64
64
  const cachedJson = JSON.stringify(cachedObj, null, 2);
65
65
  if (currentJson !== cachedJson) {
66
+ // 诊断日志:打出具体哪些字段不同,帮助定位并发误判
67
+ const diff = diffFields(cachedObj, currentRemObject);
68
+ console.error(`[defense-2] Rem ${remId} conflict detected. Changed fields: ${diff.join(', ') || '(JSON differs but no top-level field diff — possible nested change)'}`);
66
69
  // 不更新缓存 — 迫使 AI re-read
67
70
  throw new Error(`Rem ${remId} has been modified since last read. Please read it again before editing.`);
68
71
  }
@@ -126,3 +129,14 @@ export class EditHandler {
126
129
  };
127
130
  }
128
131
  }
132
+ /** 比较两个 RemObject 的顶层字段,返回值不同的 key 列表 */
133
+ function diffFields(cached, current) {
134
+ const allKeys = new Set([...Object.keys(cached), ...Object.keys(current)]);
135
+ const changed = [];
136
+ for (const key of allKeys) {
137
+ if (JSON.stringify(cached[key]) !== JSON.stringify(current[key])) {
138
+ changed.push(key);
139
+ }
140
+ }
141
+ return changed;
142
+ }
@@ -172,7 +172,9 @@ export class TreeEditHandler {
172
172
  else {
173
173
  // ── 普通 Rem 创建路径 ──
174
174
  // 解析 Markdown 前缀 + 箭头分隔符 → 属性
175
- const { cleanContent, powerups, backText, practiceDirection } = parsePowerupPrefix(op.content);
175
+ const { cleanContent, powerups, backText, practiceDirection, warnings: prefixWarnings } = parsePowerupPrefix(op.content);
176
+ if (prefixWarnings?.length)
177
+ templateWarnings.push(...prefixWarnings);
176
178
  const createResult = await this.forwardToPlugin('create_rem', {
177
179
  content: cleanContent,
178
180
  parentId,
@@ -141,6 +141,7 @@ export function parseMetadata(metadataStr) {
141
141
  */
142
142
  export function parsePowerupPrefix(rawContent) {
143
143
  const powerups = {};
144
+ const warnings = [];
144
145
  if (rawContent === '---') {
145
146
  return { cleanContent: '', powerups: { addPowerup: 'dv' } };
146
147
  }
@@ -168,6 +169,20 @@ export function parsePowerupPrefix(rawContent) {
168
169
  powerups.isTodo = true;
169
170
  content = content.slice(6);
170
171
  }
172
+ // Quote(引用块)
173
+ if (content.startsWith('> ')) {
174
+ powerups.isQuote = true;
175
+ content = content.slice(2);
176
+ }
177
+ // ListItem(有序列表)— 容错 1-9 开头,归一化为 isListItem
178
+ const listItemMatch = content.match(/^([1-9])\. /);
179
+ if (listItemMatch) {
180
+ powerups.isListItem = true;
181
+ content = content.slice(listItemMatch[0].length);
182
+ if (listItemMatch[1] !== '1') {
183
+ warnings.push(`有序列表前缀 "${listItemMatch[0]}" 已归一化为 isListItem(RemNote 自动编号,请统一使用 "1. ")`);
184
+ }
185
+ }
171
186
  // Code
172
187
  if (content.startsWith('`') && content.endsWith('`') && content.length >= 2) {
173
188
  powerups.isCode = true;
@@ -231,6 +246,8 @@ export function parsePowerupPrefix(rawContent) {
231
246
  result.practiceDirection = practiceDirection;
232
247
  if (isMultiline !== undefined)
233
248
  result.isMultiline = isMultiline;
249
+ if (warnings.length > 0)
250
+ result.warnings = warnings;
234
251
  return result;
235
252
  }
236
253
  // ────────────────────────── Multiline 检测 ──────────────────────────
@@ -119,12 +119,20 @@ RemNote 格式设置(标题、高亮、代码等)底层通过 Powerup 机制
119
119
 
120
120
  所有操作都依赖一个活跃的会话(= 守护进程的生命周期)。
121
121
 
122
- ### 标准模式(需要用户配合)
122
+ > **⚠️ 关于 Plugin 加载方式(防幻觉红线)**:
123
+ > - 本插件是**开发者插件**,通过 RemNote「开发你的插件」功能加载本地 URL
124
+ > - **禁止**告诉用户"去插件市场/商店搜索安装"——本插件**不在 RemNote 插件市场中**
125
+ > - **禁止**告诉用户"Settings → Plugins"——这个路径不存在
126
+ > - **禁止**编造不存在的安装流程——严格按照下方步骤引导用户
127
+
128
+ ### 标准模式(默认,推荐)
129
+
130
+ **标准模式是日常使用的推荐方式**。用户在自己的浏览器中打开 RemNote 并加载 Plugin,Agent 可以通过 \`read_context\` 感知用户正在浏览的页面和焦点位置,实现真正的协作。
123
131
 
124
132
  \`\`\`
125
133
  connect → 启动 daemon(幂等)
126
134
 
127
- ⚠️ 用户操作:确保 RemNote 中已加载插件(见下方说明)
135
+ ⚠️ 引导用户加载 Plugin(见下方 ★ 标记的步骤)
128
136
 
129
137
  health → 确认三层就绪(daemon / Plugin / SDK)
130
138
 
@@ -133,11 +141,34 @@ health → 确认三层就绪(daemon / Plugin / SDK)
133
141
  disconnect → 关闭 daemon,清空所有缓存
134
142
  \`\`\`
135
143
 
136
- ### Headless 模式(自动连接)
144
+ ### 标准模式:connect 后引导用户加载 Plugin(核心步骤)
145
+
146
+ \`connect\`(不传 headless)成功只意味着 daemon 和 Plugin 服务已启动,**Plugin 并未自动连接**。你必须引导用户完成以下操作:
147
+
148
+ **首次使用**(RemNote 从未加载过此插件):
149
+ 1. 打开 RemNote 桌面端或网页端
150
+ 2. 点击左侧边栏底部的**插件图标**(拼图形状)
151
+ 3. 点击「**开发你的插件**」(Develop Your Plugin)
152
+ 4. 在输入框中填入 connect 输出的 **Plugin 服务地址**(如 \`http://localhost:29101\`)
153
+ 5. 等待插件加载完成
154
+
155
+ **非首次使用**(之前已加载过此插件):
156
+ - 只需**刷新 RemNote 页面**即可(浏览器 F5 或 Cmd+R)
157
+
158
+ **你必须**:执行 \`connect\` 后,**立即**将上述步骤告知用户,**禁止**跳过此步直接调用业务命令。引导用户完成后,用 \`health\` 确认三层就绪再继续。
159
+
160
+ ### Headless 模式(特殊场景,不推荐日常使用)
137
161
 
138
162
  通过 setup(一次性)+ headless Chrome 实现自动连接,后续 connect 无需用户介入。
139
163
 
140
- **⚠️ 模式选择建议**:日常使用推荐**标准模式**。Headless 模式下 Chrome 在后台运行,**无法感知用户正在 RemNote 中浏览和操作的界面**(\`read_context\` 返回的是 headless Chrome 的上下文,而非用户的浏览器)。只有在全自动化场景才建议使用 Headless 模式。
164
+ **⚠️ 不推荐日常使用**。Headless Chrome 是后台独立实例,**会丢失用户上下文**:\`read_context\` 返回的是 headless Chrome 的上下文,不是用户浏览器的。Agent 无法感知用户正在浏览和操作的页面,协作体验大打折扣。
165
+
166
+ **仅在以下场景使用 headless**:
167
+ - 用户明确要求在**服务器/无 GUI 环境**中运行
168
+ - 用户明确表示**不想参与操作**,希望全自动化(CI/CD、定时任务、批量处理等)
169
+ - 用户自己不在 RemNote 前面,不需要与 Agent 协作浏览
170
+
171
+ **默认始终使用标准模式**,除非用户主动要求 headless。
141
172
 
142
173
  #### 首次使用(setup)
143
174
 
@@ -169,22 +200,6 @@ disconnect → 关闭 daemon + headless Chrome,清空所有缓存,清除 hea
169
200
  - \`health(reload=true)\`:重载 headless Chrome 页面
170
201
  - Plugin 始终不连接 → 可能登录 session 过期,需重新 setup
171
202
 
172
- ### ⚠️ 标准模式:connect 后需要用户配合(重要)
173
-
174
- \`connect\`(不传 headless)成功只意味着 daemon 和 Plugin 服务已启动,**Plugin 并未自动连接**。
175
-
176
- **首次使用**(RemNote 从未加载过此插件):
177
- 1. 打开 RemNote 桌面端或网页端
178
- 2. 点击左侧边栏底部的插件图标(拼图形状)
179
- 3. 点击「开发你的插件」(Develop Your Plugin)
180
- 4. 在输入框中填入 connect 输出的 Plugin 服务地址(如 \`http://localhost:29101\`)
181
- 5. 等待插件加载完成
182
-
183
- **非首次使用**(之前已加载过此插件):
184
- - 只需**刷新 RemNote 页面**即可(浏览器 F5 或 Cmd+R)
185
-
186
- **你必须**:执行 \`connect\` 后,**立即告知用户需要完成上述操作**,不要直接调用业务命令。引导用户完成后,用 \`health\` 确认三层就绪再继续。
187
-
188
203
  ### Windows 注意事项
189
204
 
190
205
  - 默认模式秒级启动(预构建 plugin)
@@ -311,7 +326,9 @@ changes: { "type": "concept", "highlightColor": "Yellow", "fontSize": "H1" }
311
326
 
312
327
  新增行(无 remId 注释的行)支持以下格式:
313
328
 
314
- **Markdown 前缀**:\`# \` \`## \` \`### \` \`- [ ] \` \`- [x] \` \\\`code\\\` \`---\`
329
+ **Markdown 前缀**:\`# \` \`## \` \`### \` \`- [ ] \` \`- [x] \` \`> \`(引用块) \`1. \`(有序列表) \\\`code\\\` \`---\`
330
+
331
+ > **⚠️ 有序列表必须用 \`1. \` 前缀(Lazy Numbering)**:RemNote 有序列表采用 Lazy Numbering——所有列表项统一写 \`1. \`,RemNote 按层级自动编号(1./2./3./A./B./I./II.)。不要手动编号(如 \`2. \` \`3. \`)。\`2. \`~\`9. \` 会被容错处理(归一化为 isListItem 并返回 templateWarnings 警告),\`10. \` 及以上不会被识别为有序列表。
315
332
 
316
333
  **箭头分隔符**:
317
334
  - 单行:\`→\`(forward)\`←\`(backward)\`↔\`(both)——格式 \`text → backText\`
@@ -326,59 +343,39 @@ changes: { "type": "concept", "highlightColor": "Yellow", "fontSize": "H1" }
326
343
  - \`<!--portal refs:id1,id2-->\`(创建并引用指定 Rem)
327
344
  - \`<!--portal-->\`(创建空 Portal)
328
345
 
329
- #### str_replace 构造示例
346
+ #### 行引用模板 \`{{remId}}\`
330
347
 
331
- **示例 1:在末尾新增行**
332
- \`\`\`
333
- oldStr:
334
- 最后一个兄弟 <!--idZ-->
335
- newStr:
336
- 最后一个兄弟 <!--idZ-->
337
- 新增行
338
- \`\`\`
348
+ 已有行支持两种写法:**模板模式**(推荐)和**完整匹配模式**(回退)。
339
349
 
340
- **示例 2:删除一个带子节点的行(必须一起删)**
341
- \`\`\`
342
- oldStr:
343
- 子节点 A <!--idA-->
344
- 孙节点 A1 <!--idA1-->
345
- 子节点 B <!--idB-->
346
- newStr:
347
- 子节点 B <!--idB-->
348
- \`\`\`
350
+ **模板模式**(优先使用):用 \`{{remId}}\` 引用已有行,系统自动展开为完整内容(不含缩进)。节省 token、减少复制错误。
351
+ **完整匹配模式**(回退):直接从大纲复制完整行内容(含 \`<!--remId 元数据-->\`)。
349
352
 
350
- **示例 3:创建多行闪卡**
351
- \`\`\`
352
- oldStr:
353
- 子节点 A <!--idA-->
354
- newStr:
355
- 什么是线性回归? ↓
356
- 一种基本的回归分析方法
357
- 假设因变量与自变量呈线性关系
358
- 子节点 A <!--idA-->
359
- \`\`\`
353
+ **策略:优先模板,连续失败则回退**。如果模板模式连续 2+ 次因 ID 错误导致 \`old_str not found\`,说明当前上下文不足以准确引用 ID——立即切换到完整匹配模式(从最新大纲复制完整行内容),不要反复重试模板。
360
354
 
361
- #### 行引用模板 \`{{remId}}\`
355
+ **模板模式示例**:
356
+ \`\`\`
357
+ # 重排两个节点
358
+ oldStr: " {{id1_1}}\\n {{id1_2}}"
359
+ newStr: " {{id1_2}}\\n {{id1_1}}"
362
360
 
363
- oldStr/newStr 中使用 \`{{remId}}\` 引用缓存大纲中已有行的完整内容(不含缩进)。系统在 str_replace 前自动展开。
361
+ # 删除带子节点的行
362
+ oldStr: " {{idA}}\\n {{idA1}}\\n {{idB}}"
363
+ newStr: " {{idB}}"
364
364
 
365
- **优势**:避免抄写完整行内容(remId、元数据标记),减少 token 浪费和复制错误。
365
+ # 末尾新增行(新增行手动写,已有行用模板)
366
+ oldStr: " {{idZ}}"
367
+ newStr: " 新增行\\n {{idZ}}"
368
+ \`\`\`
366
369
 
367
- **示例:重排两个节点**
370
+ **完整匹配模式示例**(回退时使用):
368
371
  \`\`\`
369
- // 不用模板:
370
372
  oldStr: " 动态数组 <!--id1_1 type:concept-->\\n 静态数组 <!--id1_2 type:concept-->"
371
373
  newStr: " 静态数组 <!--id1_2 type:concept-->\\n 动态数组 <!--id1_1 type:concept-->"
372
-
373
- // 用模板:
374
- oldStr: " {{id1_1}}\\n {{id1_2}}"
375
- newStr: " {{id1_2}}\\n {{id1_1}}"
376
374
  \`\`\`
377
375
 
378
- **规则**:
376
+ **模板规则**:
379
377
  - \`{{remId}}\` 展开为**不含缩进**的完整行内容,缩进由你控制
380
- - 只匹配纯字母数字(\`[a-zA-Z0-9]+\`),与 RemNote cloze 语法 \`{{text}}\` 不冲突(cloze 含中文/空格/标点,不会被匹配)
381
- - 匹配到但不在缓存大纲中的 \`{{xxx}}\` 原样保留(可能是 cloze),并输出 warning
378
+ - 只匹配纯字母数字(\`[a-zA-Z0-9]+\`),与 RemNote cloze 语法 \`{{text}}\` 不冲突
382
379
  - 新增行不能用 \`{{}}\`(新增行没有 remId)
383
380
  - 可以混用:部分行用 \`{{id}}\`,部分行手动写
384
381
 
@@ -387,15 +384,13 @@ newStr: " {{id1_2}}\\n {{id1_1}}"
387
384
  在有子节点的 Rem 和其 children 之间插入新行,会导致新行"劫持"已有 children:
388
385
 
389
386
  \`\`\`
390
- 错误:插在父 Rem 和 children 之间
391
- oldStr: 水分子 <!--idA-->
392
- newStr: 水分子 ↓ <!--idA-->
393
- 新行 children 会变成新行的子节点!
387
+ 错误(模板):
388
+ oldStr: " {{idA}}" newStr: " {{idA}}\\n 新行" ← idA 有子节点,新行劫持 children!
389
+ 错误(完整匹配):
390
+ oldStr: " 水分子 ↓ <!--idA-->" newStr: " 水分子 ↓ <!--idA-->\\n 新行" 同理
394
391
 
395
392
  ✅ 正确:插在末尾
396
- oldStr: 最后一个兄弟 <!--idZ-->
397
- newStr: 最后一个兄弟 <!--idZ-->
398
- 新行
393
+ oldStr: " {{idZ}}" newStr: " {{idZ}}\\n 新行"
399
394
  \`\`\`
400
395
 
401
396
  #### 创建新节点并移动已有 children
@@ -427,7 +422,7 @@ newStr: 最后一个兄弟 <!--idZ-->
427
422
 
428
423
  \`health\` 默认查询所有活跃实例的三层状态(daemon / Plugin / SDK),返回 \`instances\` 数组。有 \`--instance\` 或 \`--headless\` 时只查询指定实例。每个实例的 \`plugin.isTwin\` 标记是否为孪生连接。
429
424
 
430
- 故障定位:无活跃实例 → \`connect\`;Plugin 未连接 → 引导用户操作 RemNote(或使用 headless 模式);SDK 未就绪 → 等待重试。
425
+ 故障定位:无活跃实例 → \`connect\`;Plugin 未连接 → 引导用户操作 RemNote(标准模式下刷新页面或首次加载插件);SDK 未就绪 → 等待重试。
431
426
 
432
427
  ### 场景 H:管理增强项目
433
428
 
@@ -509,7 +504,7 @@ tags, sources, positionAmongstSiblings, portalDirectlyIncludedRem
509
504
  \`\`\`
510
505
 
511
506
  - 缩进:每级 2 空格
512
- - 前缀:\`# \`(H1)、\`## \`(H2)、\`### \`(H3)、\`- [ ] \`(待办)、\`- [x] \`(已完成)、\\\`...\\\`(代码)、\`---\`(分隔线)
507
+ - 前缀:\`# \`(H1)、\`## \`(H2)、\`### \`(H3)、\`- [ ] \`(待办)、\`- [x] \`(已完成)、\`> \`(引用块)、\`1. \`(有序列表)、\\\`...\\\`(代码)、\`---\`(分隔线)
513
508
 
514
509
  ### 元数据标记
515
510
 
@@ -544,7 +539,7 @@ tags, sources, positionAmongstSiblings, portalDirectlyIncludedRem
544
539
  新增行(无 remId 注释的行)在 newStr 中出现时,会被创建为新的 Rem。格式选项:
545
540
 
546
541
  - 纯文本行:\`新内容\`
547
- - 带前缀:\`# 新标题\`、\`- [ ] 新待办\`
542
+ - 带前缀:\`# 新标题\`、\`- [ ] 新待办\`、\`> 引用内容\`、\`1. 列表项\`
548
543
  - 带箭头:\`问题 → 答案\`、\`概念 ↔ 定义\`、\`题目 ↓\`
549
544
  - 带元数据注释(metadata-only,无 remId):\`新行 <!--type:concept doc-->\`
550
545
  - Portal 行:\`<!--portal refs:id1,id2-->\`
@@ -643,6 +638,7 @@ tags, sources, positionAmongstSiblings, portalDirectlyIncludedRem
643
638
 
644
639
  ### 诊断决策树
645
640
 
641
+
646
642
  \`\`\`
647
643
  命令报错
648
644
  ├─ "守护进程未运行" → connect 未执行或 daemon 已超时 → 执行 connect
@@ -71,7 +71,8 @@ export function registerEditTools(server) {
71
71
  '\\n4. 重排:调换同级行的顺序' +
72
72
  '\\n执行顺序:Create → Move → Reorder → Delete' +
73
73
  '\\n\\n新增行格式:' +
74
- '\\n- Markdown 前缀:# / ## / ### / - [ ] / - [x] / `代码` / ---' +
74
+ '\\n- Markdown 前缀:# / ## / ### / - [ ] / - [x] / > / 1. / `代码` / ---' +
75
+ '\\n ⚠️ 有序列表必须用 `1. `(Lazy Numbering):RemNote 自动编号,不要写 `2. ` `3. ` 等。`2.`~`9.` 会被容错归一化并返回 templateWarnings 警告,`10.` 及以上不识别为列表。' +
75
76
  '\\n- 箭头分隔符(闪卡):→ ← ↔(单行)、↓ ↑ ↕(多行,带 backText 或子节点为答案)' +
76
77
  '\\n- 元数据注释(可选):<!--type:concept--> <!--doc--> <!--tag:Name(id)--> 可组合' +
77
78
  '\\n- Portal 创建:<!--portal refs:id1,id2--> 或 <!--portal-->(空 Portal)' +
@@ -22,7 +22,7 @@ export function registerInfraTools(server) {
22
22
  // -------------------------------------------------------------------------
23
23
  server.addTool({
24
24
  name: 'connect',
25
- description: '启动守护进程(daemon),建立 CLI 与 RemNote Plugin 之间的通信通道。这是所有业务命令(read_rem、edit_rem、search 等)的前置步骤。\n\n适用场景:\n- 开始一次 RemNote 操作会话前,必须先调用此工具\n- 不确定 daemon 是否在运行时,也可安全调用(幂等)\n\n两种模式:\n- 标准模式(默认):启动 daemon 后需要用户在 RemNote 中手动加载 Plugin\n- Headless 模式(headless=true):自动启动 headless Chrome 加载 Plugin,无需用户操作。需先完成 setup(保存登录凭证)\n\nHeadless 模式会话持久化:connect(headless=true) 后,MCP Server 自动记住 headless 状态,后续所有工具调用(health、read_rem、edit_rem 等)自动路由到 headless 实例,无需额外参数。disconnect 或 clean 后自动清除。\n\n输出:返回 JSON,关键字段 ok、alreadyRunning、instance、slotIndex、pid、wsPort、headless。\n幂等:重复调用不会启动多个 daemon。daemon 默认 30 分钟无活动自动关闭。\n关联工具:setup(headless 前置)、disconnect(结束会话)、health(检查状态)',
25
+ description: '启动守护进程(daemon),建立 CLI 与 RemNote Plugin 之间的通信通道。这是所有业务命令(read_rem、edit_rem、search 等)的前置步骤。\n\n适用场景:\n- 开始一次 RemNote 操作会话前,必须先调用此工具\n- 不确定 daemon 是否在运行时,也可安全调用(幂等)\n\n两种模式:\n- 标准模式(默认,推荐):启动 daemon 后用户在自己的浏览器中加载 Plugin。优势:Agent 可通过 read_context 感知用户正在浏览的页面,实现协作\n- Headless 模式(headless=true,不推荐日常使用):自动启动 headless Chrome,无需用户操作。⚠️ 会丢失用户上下文(read_context 返回 headless 实例的上下文,不是用户浏览器的)。仅在以下场景使用:用户明确要求在服务器/无 GUI 环境运行、用户不想参与操作(全自动化)、用户不在 RemNote 前面。需先完成 setup\n\n⚠️ 标准模式 Plugin 加载(connect 后必须引导用户完成):\n本插件是开发者插件,不在 RemNote 插件市场中,禁止告诉用户去市场搜索安装。正确步骤:\n- 首次:RemNote → 左下角插件图标 → 「开发你的插件」→ 填入 Plugin 服务地址(如 http://localhost:29101)\n- 非首次:刷新 RemNote 页面即可\n引导用户完成后用 health 确认三层就绪再执行业务命令。\n\nHeadless 模式会话持久化:connect(headless=true) 后,MCP Server 自动记住 headless 状态,后续所有工具调用自动路由到 headless 实例。disconnect 或 clean 后自动清除。\n\n输出:返回 JSON,关键字段 ok、alreadyRunning、instance、slotIndex、pid、wsPort、headless。\n幂等:重复调用不会启动多个 daemon。daemon 默认 30 分钟无活动自动关闭。\n关联工具:setup(headless 前置)、disconnect(结束会话)、health(检查状态)',
26
26
  parameters: z.object({
27
27
  headless: z.boolean().optional().describe('启用 headless 模式:自动启动 Chrome 加载 Plugin(需先 setup)'),
28
28
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remnote-bridge",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "RemNote 自动化桥接工具集:CLI + MCP Server + Plugin",
5
5
  "type": "module",
6
6
  "bin": {