remnote-bridge 0.1.11 → 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.
- package/dist/cli/addon/addon-manager.js +163 -0
- package/dist/cli/addon/registry.js +24 -0
- package/dist/cli/commands/addon.js +149 -0
- package/dist/cli/commands/clean.js +121 -52
- package/dist/cli/commands/connect.js +72 -33
- package/dist/cli/commands/disconnect.js +19 -19
- package/dist/cli/commands/edit-rem.js +8 -36
- package/dist/cli/commands/edit-tree.js +3 -20
- package/dist/cli/commands/health.js +19 -18
- package/dist/cli/commands/read-context.js +3 -20
- package/dist/cli/commands/read-globe.js +3 -20
- package/dist/cli/commands/read-rem.js +6 -32
- package/dist/cli/commands/read-tree.js +3 -20
- package/dist/cli/commands/search.js +97 -21
- package/dist/cli/config.js +148 -72
- package/dist/cli/daemon/daemon.js +104 -24
- package/dist/cli/daemon/dev-server.js +9 -1
- package/dist/cli/daemon/pid.js +36 -22
- package/dist/cli/daemon/registry.js +160 -0
- package/dist/cli/daemon/send-request.js +11 -11
- package/dist/cli/daemon/static-server.js +97 -34
- package/dist/cli/handlers/edit-handler.js +49 -140
- package/dist/cli/handlers/read-handler.js +9 -9
- package/dist/cli/handlers/rem-cache.js +10 -5
- package/dist/cli/handlers/tree-parser.js +16 -9
- package/dist/cli/main.js +67 -19
- package/dist/cli/protocol.js +18 -4
- package/dist/cli/server/config-server.js +280 -14
- package/dist/cli/server/ws-server.js +93 -44
- package/dist/cli/utils/output.js +29 -0
- package/dist/mcp/format.js +43 -0
- package/dist/mcp/index.js +0 -55
- package/dist/mcp/instructions.js +424 -216
- package/dist/mcp/resources/edit-rem-guide.js +37 -158
- package/dist/mcp/resources/edit-tree-guide.js +1 -1
- package/dist/mcp/resources/error-reference.js +9 -13
- package/dist/mcp/resources/rem-object-fields.js +6 -6
- package/dist/mcp/tools/edit-tools.js +69 -8
- package/dist/mcp/tools/infra-tools.js +44 -8
- package/dist/mcp/tools/read-tools.js +136 -20
- package/package.json +2 -2
- package/remnote-plugin/dist/bridge_widget-sandbox.js +17 -17
- package/remnote-plugin/dist/bridge_widget.js +17 -17
- package/remnote-plugin/dist/index-sandbox.js +31 -31
- package/remnote-plugin/dist/index.js +31 -31
- package/remnote-plugin/dist/manifest.json +1 -1
- package/remnote-plugin/package.json +1 -1
- package/remnote-plugin/public/manifest.json +1 -1
- package/remnote-plugin/src/bridge/multi-connection-manager.ts +151 -0
- package/remnote-plugin/src/bridge/websocket-client.ts +62 -16
- package/remnote-plugin/src/services/index.ts +0 -8
- package/remnote-plugin/src/services/read-rem.ts +1 -9
- package/remnote-plugin/src/services/search.ts +13 -10
- package/remnote-plugin/src/settings.ts +9 -7
- package/remnote-plugin/src/utils/index.ts +0 -5
- package/remnote-plugin/src/widgets/bridge_widget.tsx +105 -20
- package/remnote-plugin/src/widgets/index.tsx +41 -44
- package/remnote-plugin/webpack.config.js +35 -0
- package/skills/remnote-bridge/SKILL.md +45 -40
- package/skills/remnote-bridge/instructions/addon.md +134 -0
- package/skills/remnote-bridge/instructions/clean.md +110 -0
- package/skills/remnote-bridge/instructions/connect.md +80 -37
- package/skills/remnote-bridge/instructions/disconnect.md +22 -9
- package/skills/remnote-bridge/instructions/edit-rem.md +113 -327
- package/skills/remnote-bridge/instructions/health.md +23 -13
- package/skills/remnote-bridge/instructions/install-skill.md +58 -0
- package/skills/remnote-bridge/instructions/overall.md +99 -35
- package/skills/remnote-bridge/instructions/read-rem.md +15 -15
- package/skills/remnote-bridge/instructions/search.md +77 -18
- package/skills/remnote-bridge/instructions/setup.md +5 -6
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const EDIT_REM_GUIDE_CONTENT = `
|
|
2
2
|
# edit_rem 操作指南
|
|
3
3
|
|
|
4
|
-
edit_rem
|
|
4
|
+
edit_rem 直接修改单个 Rem 的属性字段。通过 changes 对象指定要修改的字段及新值。
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -9,149 +9,70 @@ edit_rem 通过 str_replace 语义修改单个 Rem 的属性。操作对象是 \
|
|
|
9
9
|
|
|
10
10
|
必须先 \\\`read_rem\\\` 同一个 remId,建立缓存后才能 \\\`edit_rem\\\`。跳过会触发防线 1 错误。
|
|
11
11
|
|
|
12
|
-
工作流:\\\`read_rem\\\` →
|
|
12
|
+
工作流:\\\`read_rem\\\` → 查看属性 → \\\`edit_rem\\\`(changes 对象)。
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## changes 对象
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
传入 {字段名: 新值} 格式的对象。支持 21 个可写字段,只读和未知字段会产生警告但不阻断。
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
{
|
|
24
|
-
"id": "kLrIOHJLyMd8Y2lyA",
|
|
25
|
-
"text": [
|
|
26
|
-
"Hello World"
|
|
27
|
-
],
|
|
28
|
-
"backText": null,
|
|
29
|
-
"type": "concept",
|
|
30
|
-
"highlightColor": null,
|
|
31
|
-
"isTodo": false
|
|
32
|
-
}
|
|
33
|
-
\\\`\\\`\\\`
|
|
34
|
-
|
|
35
|
-
### 匹配规则
|
|
36
|
-
|
|
37
|
-
- oldStr 必须在 JSON 文本中**恰好匹配 1 次**(0 次=未找到,>1 次=多匹配,均报错)
|
|
38
|
-
- 大小写敏感,精确匹配
|
|
39
|
-
- oldStr 建议包含字段名 + 值,避免匹配到 text 内容中的同名字符串
|
|
40
|
-
|
|
41
|
-
### 替换后校验
|
|
42
|
-
|
|
43
|
-
替换后的文本必须是合法 JSON,否则报 "invalid JSON" 错误。
|
|
20
|
+
示例:
|
|
21
|
+
- 修改类型:\\\`{type: "concept"}\\\`
|
|
22
|
+
- 设置高亮:\\\`{highlightColor: "Yellow"}\\\`
|
|
23
|
+
- 批量修改:\\\`{type: "concept", highlightColor: "Yellow", fontSize: "H1"}\\\`
|
|
44
24
|
|
|
45
25
|
---
|
|
46
26
|
|
|
47
|
-
##
|
|
27
|
+
## 两道防线
|
|
48
28
|
|
|
49
29
|
| 防线 | 检查内容 | 失败时 |
|
|
50
30
|
|:-----|:---------|:-------|
|
|
51
31
|
| 1. 缓存存在 | 是否已 read_rem | 报 "has not been read yet" → 先 read_rem |
|
|
52
32
|
| 2. 并发检测 | 当前 Rem 是否被外部修改 | 报 "has been modified since last read" → 重新 read_rem |
|
|
53
|
-
| 3. 精确匹配 | oldStr 匹配次数 | 0 次或多次 → 调整 oldStr 使其唯一 |
|
|
54
33
|
|
|
55
|
-
防线 2 的关键:edit 时会从 Plugin
|
|
34
|
+
防线 2 的关键:edit 时会从 Plugin 重新读取最新数据与缓存比较。如果不一致,拒绝编辑并**不更新缓存**,迫使你重新 read_rem。
|
|
56
35
|
|
|
57
36
|
---
|
|
58
37
|
|
|
59
|
-
## RichText
|
|
38
|
+
## RichText 编辑
|
|
60
39
|
|
|
61
|
-
\\\`text\\\` 和 \\\`backText\\\` 字段使用 RichText
|
|
40
|
+
\\\`text\\\` 和 \\\`backText\\\` 字段使用 RichText 格式。传入完整的 RichText 数组作为新值。
|
|
62
41
|
|
|
63
42
|
### 关键排序规则
|
|
64
43
|
|
|
65
44
|
- \\\`_id\\\`(U+005F)排在所有小写字母之前,所以 \\\`_id\\\` 总是第一个 key
|
|
66
45
|
- 示例排序:\\\`_id\\\` < \\\`b\\\` < \\\`cId\\\` < \\\`h\\\` < \\\`i\\\` < \\\`iUrl\\\` < \\\`text\\\`
|
|
67
46
|
|
|
68
|
-
### 示例 1
|
|
47
|
+
### 示例 1:设置纯文本
|
|
69
48
|
|
|
70
|
-
read_rem 返回:
|
|
71
|
-
|
|
72
|
-
\\\`\\\`\\\`json
|
|
73
|
-
"text": [
|
|
74
|
-
"普通标题"
|
|
75
|
-
],
|
|
76
49
|
\\\`\\\`\\\`
|
|
77
|
-
|
|
78
|
-
edit_rem 调用:
|
|
79
|
-
|
|
80
|
-
\\\`\\\`\\\`
|
|
81
|
-
oldStr: "text": [\\n "普通标题"\\n ]
|
|
82
|
-
newStr: "text": [\\n {\\n "b": true,\\n "i": "m",\\n "text": "粗体标题"\\n }\\n ]
|
|
50
|
+
changes: {text: ["新标题"]}
|
|
83
51
|
\\\`\\\`\\\`
|
|
84
52
|
|
|
85
|
-
|
|
53
|
+
### 示例 2:设置粗体
|
|
86
54
|
|
|
87
|
-
\\\`\\\`\\\`json
|
|
88
|
-
"text": [
|
|
89
|
-
{
|
|
90
|
-
"b": true,
|
|
91
|
-
"i": "m",
|
|
92
|
-
"text": "粗体标题"
|
|
93
|
-
}
|
|
94
|
-
],
|
|
95
55
|
\\\`\\\`\\\`
|
|
96
|
-
|
|
97
|
-
### 示例 2:纯文本 → 部分超链接
|
|
98
|
-
|
|
56
|
+
changes: {text: [{"b": true, "i": "m", "text": "粗体标题"}]}
|
|
99
57
|
\\\`\\\`\\\`
|
|
100
|
-
oldStr: "text": [\\n "点击访问官网"\\n ]
|
|
101
|
-
newStr: "text": [\\n "点击",\\n {\\n "i": "m",\\n "iUrl": "https://remnote.com",\\n "text": "访问官网"\\n }\\n ]
|
|
102
|
-
\\\`\\\`\\\`
|
|
103
|
-
|
|
104
|
-
### 示例 3:修改引用旁的文本
|
|
105
58
|
|
|
106
|
-
|
|
59
|
+
### 示例 3:设置超链接
|
|
107
60
|
|
|
108
|
-
\\\`\\\`\\\`json
|
|
109
|
-
"text": [
|
|
110
|
-
"参考 ",
|
|
111
|
-
{
|
|
112
|
-
"_id": "abc123",
|
|
113
|
-
"i": "q"
|
|
114
|
-
},
|
|
115
|
-
" 的内容"
|
|
116
|
-
],
|
|
117
61
|
\\\`\\\`\\\`
|
|
118
|
-
|
|
119
|
-
只改文字部分(纯字符串可直接匹配):
|
|
120
|
-
|
|
121
|
-
\\\`\\\`\\\`
|
|
122
|
-
oldStr: " 的内容"
|
|
123
|
-
newStr: " 的详细说明"
|
|
124
|
-
\\\`\\\`\\\`
|
|
125
|
-
|
|
126
|
-
如果 " 的内容" 出现多次导致多匹配,加上下文:
|
|
127
|
-
|
|
128
|
-
\\\`\\\`\\\`
|
|
129
|
-
oldStr: "q"\\n },\\n " 的内容"
|
|
130
|
-
newStr: "q"\\n },\\n " 的详细说明"
|
|
62
|
+
changes: {text: ["点击", {"i": "m", "iUrl": "https://remnote.com", "text": "访问官网"}]}
|
|
131
63
|
\\\`\\\`\\\`
|
|
132
64
|
|
|
133
65
|
### 示例 4:添加完形填空
|
|
134
66
|
|
|
135
67
|
\\\`\\\`\\\`
|
|
136
|
-
|
|
137
|
-
newStr: "text": [\\n "光合作用需要",\\n {\\n "cId": "cloze1",\\n "i": "m",\\n "text": "阳光"\\n }\\n ]
|
|
138
|
-
\\\`\\\`\\\`
|
|
139
|
-
|
|
140
|
-
### 示例 5:修改简单属性
|
|
141
|
-
|
|
142
|
-
\\\`\\\`\\\`
|
|
143
|
-
oldStr: "type": "default"
|
|
144
|
-
newStr: "type": "concept"
|
|
68
|
+
changes: {text: ["光合作用需要", {"cId": "cloze1", "i": "m", "text": "阳光"}]}
|
|
145
69
|
\\\`\\\`\\\`
|
|
146
70
|
|
|
147
|
-
|
|
148
|
-
oldStr: "highlightColor": null
|
|
149
|
-
newStr: "highlightColor": "Red"
|
|
150
|
-
\\\`\\\`\\\`
|
|
71
|
+
### 示例 5:设置 backText
|
|
151
72
|
|
|
152
73
|
\\\`\\\`\\\`
|
|
153
|
-
|
|
154
|
-
|
|
74
|
+
changes: {backText: ["背面答案"]}
|
|
75
|
+
changes: {backText: null} // 清除背面
|
|
155
76
|
\\\`\\\`\\\`
|
|
156
77
|
|
|
157
78
|
---
|
|
@@ -167,68 +88,15 @@ newStr: "practiceDirection": "both"
|
|
|
167
88
|
|
|
168
89
|
---
|
|
169
90
|
|
|
170
|
-
## 常见错误
|
|
171
|
-
|
|
172
|
-
| 错误 | 原因 | 解决 |
|
|
173
|
-
|:-----|:-----|:-----|
|
|
174
|
-
| key 顺序错 | 写 \\\`{"text":"xx","i":"m"}\\\` 但实际是 \\\`{"i":"m","text":"xx"}\\\` | 按字母序排列 key |
|
|
175
|
-
| 缩进不匹配 | 空格数不对 | 仔细对照 read_rem 返回的缩进 |
|
|
176
|
-
| 混淆 highlightColor 和 h | 前者字符串 \\\`"Red"\\\`,后者数字 \\\`1\\\` | 参考上方对比表 |
|
|
177
|
-
| 漏 onlyAudio | \\\`i:"a"\\\` 的 \\\`onlyAudio\\\` 是必填 | true=音频,false=视频 |
|
|
178
|
-
| JSON 语法错 | 引号、逗号、括号不完整 | 检查替换边界 |
|
|
179
|
-
| Portal oldStr 不匹配 | Portal 编辑在简化 JSON 上匹配,不是完整 JSON | 检查 oldStr 是否匹配 9 字段简化 JSON |
|
|
180
|
-
|
|
181
|
-
---
|
|
182
|
-
|
|
183
91
|
## Portal 编辑
|
|
184
92
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
**edit_rem 只能修改 Portal 的引用列表和位置属性。创建 Portal 和删除 Portal 请使用 \\\`edit_tree\\\`。**
|
|
93
|
+
Portal(type=portal)的引用列表通过 \\\`portalDirectlyIncludedRem\\\` 字段修改:
|
|
188
94
|
|
|
189
|
-
### 操作目标:简化 JSON
|
|
190
|
-
|
|
191
|
-
Portal 的 str_replace 在 **9 字段简化 JSON** 上执行(而非完整 51 字段):
|
|
192
|
-
|
|
193
|
-
\\\`\\\`\\\`json
|
|
194
|
-
{
|
|
195
|
-
"id": "abc123",
|
|
196
|
-
"type": "portal",
|
|
197
|
-
"portalType": "portal",
|
|
198
|
-
"portalDirectlyIncludedRem": ["remId1", "remId2"],
|
|
199
|
-
"parent": "parentId",
|
|
200
|
-
"positionAmongstSiblings": 3,
|
|
201
|
-
"children": ["remId1", "remId2"],
|
|
202
|
-
"createdAt": 1709000000000,
|
|
203
|
-
"updatedAt": 1709000000000
|
|
204
|
-
}
|
|
205
95
|
\\\`\\\`\\\`
|
|
206
|
-
|
|
207
|
-
### 可写字段
|
|
208
|
-
|
|
209
|
-
| 字段 | 写入方式 |
|
|
210
|
-
|:-----|:---------|
|
|
211
|
-
| \\\`portalDirectlyIncludedRem\\\` | diff 数组 → addToPortal / removeFromPortal |
|
|
212
|
-
| \\\`parent\\\` | setParent() |
|
|
213
|
-
| \\\`positionAmongstSiblings\\\` | setParent(parent, position) |
|
|
214
|
-
|
|
215
|
-
其余字段(id、type、portalType、children、createdAt、updatedAt)为只读,修改只产生警告。
|
|
216
|
-
|
|
217
|
-
### 示例
|
|
218
|
-
|
|
219
|
-
添加引用:
|
|
220
|
-
|
|
221
|
-
\\\`\\\`\\\`
|
|
222
|
-
oldStr: "portalDirectlyIncludedRem": ["remId1", "remId2"]
|
|
223
|
-
newStr: "portalDirectlyIncludedRem": ["remId1", "remId2", "remId3"]
|
|
96
|
+
changes: {portalDirectlyIncludedRem: ["remId1", "remId2", "newRemId3"]}
|
|
224
97
|
\\\`\\\`\\\`
|
|
225
98
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
\\\`\\\`\\\`
|
|
229
|
-
oldStr: "portalDirectlyIncludedRem": ["remId1", "remId2"]
|
|
230
|
-
newStr: "portalDirectlyIncludedRem": ["remId1"]
|
|
231
|
-
\\\`\\\`\\\`
|
|
99
|
+
创建和删除 Portal 请使用 \\\`edit_tree\\\`。
|
|
232
100
|
|
|
233
101
|
---
|
|
234
102
|
|
|
@@ -238,8 +106,19 @@ newStr: "portalDirectlyIncludedRem": ["remId1"]
|
|
|
238
106
|
|:-----|:-----|
|
|
239
107
|
| 写入成功 | 从 Plugin 重新读取最新状态 → 覆盖缓存 |
|
|
240
108
|
| 防线 2 拒绝 | **不更新**缓存(迫使重新 read_rem) |
|
|
241
|
-
| 防线 3 拒绝 | 缓存不变(可调整 oldStr 重试) |
|
|
242
109
|
| 部分写入失败 | **不更新**缓存(迫使重新 read_rem) |
|
|
243
110
|
|
|
244
111
|
写入成功后**永远从 Plugin 重新读取**,不做本地推导,保证缓存与 SDK 状态完全同步。
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 常见错误
|
|
116
|
+
|
|
117
|
+
| 错误 | 原因 | 解决 |
|
|
118
|
+
|:-----|:-----|:-----|
|
|
119
|
+
| 字段值类型错误 | 如 type 传了无效值 | 检查枚举类型速查表 |
|
|
120
|
+
| 混淆 highlightColor 和 h | 前者字符串 \\\`"Red"\\\`,后者数字 \\\`1\\\` | 参考上方对比表 |
|
|
121
|
+
| 漏 onlyAudio | \\\`i:"a"\\\` 的 \\\`onlyAudio\\\` 是必填 | true=音频,false=视频 |
|
|
122
|
+
| 只读字段被忽略 | 修改了只读字段 | 产生警告但不阻断,检查字段权限 |
|
|
123
|
+
| Portal 字段修改无效 | 非 Portal 类型的 Rem 修改 portalDirectlyIncludedRem | 仅 type=portal 的 Rem 支持该字段 |
|
|
245
124
|
`;
|
|
@@ -253,5 +253,5 @@ Portal 新增行与已有 Portal 行的区别:
|
|
|
253
253
|
|
|
254
254
|
删除 Portal 与删除普通行相同——从 newStr 中移除该 Portal 行即可。
|
|
255
255
|
|
|
256
|
-
**注意**:修改已有 Portal 的引用列表(增删引用的 Rem)请使用 \\\`edit_rem\\\`,通过
|
|
256
|
+
**注意**:修改已有 Portal 的引用列表(增删引用的 Rem)请使用 \\\`edit_rem\\\`,通过 changes 传入新的 \\\`portalDirectlyIncludedRem\\\` 数组。
|
|
257
257
|
`;
|
|
@@ -34,13 +34,11 @@ export const ERROR_REFERENCE_CONTENT = `
|
|
|
34
34
|
|
|
35
35
|
**关键**:防线 2 失败时**不会自动更新缓存**,必须手动重新 read。
|
|
36
36
|
|
|
37
|
-
### 防线 3:str_replace
|
|
37
|
+
### 防线 3:str_replace 精确匹配(仅 edit_tree)
|
|
38
38
|
|
|
39
39
|
| 错误信息 | 触发命令 | 恢复操作 |
|
|
40
40
|
|:---------|:---------|:---------|
|
|
41
|
-
| old_str not found in the serialized JSON of rem {remId} | edit_rem | 检查 oldStr 是否精确匹配(含引号、空格、换行) |
|
|
42
41
|
| old_str not found in the tree outline of {remId} | edit_tree | 检查 oldStr 是否精确匹配缓存大纲 |
|
|
43
|
-
| old_str matches {N} locations in rem {remId}. Make old_str more specific to match exactly once. | edit_rem | 扩大 oldStr 范围,包含更多上下文以唯一定位 |
|
|
44
42
|
| old_str matches {N} locations in the tree outline of {remId}. Make old_str more specific to match exactly once. | edit_tree | 扩大 oldStr 范围 |
|
|
45
43
|
|
|
46
44
|
---
|
|
@@ -49,13 +47,12 @@ export const ERROR_REFERENCE_CONTENT = `
|
|
|
49
47
|
|
|
50
48
|
| 错误信息 | 原因 | 恢复操作 |
|
|
51
49
|
|:---------|:-----|:---------|
|
|
52
|
-
| The replacement produced invalid JSON. Check your new_str for syntax errors. | 替换后文本不是合法 JSON | 检查 newStr 的引号、逗号、括号完整性 |
|
|
53
50
|
| Failed to update field 'type': Portal 不可通过 setType() 设置 | 尝试将 type 设为 portal | Portal 只能通过 SDK 专用 API 创建,不可通过 edit_rem 设置 |
|
|
54
51
|
| Failed to update field '{field}': ... | SDK setter 调用失败 | 检查字段值是否在允许范围内 |
|
|
52
|
+
| Invalid value for '{field}': ... | 字段值不符合约束 | 检查字段值类型和允许范围(参考枚举类型速查表) |
|
|
55
53
|
| Field '{fieldName}' is read-only and was ignored | 修改了只读字段 | **警告**(非阻断),该字段不可修改 |
|
|
54
|
+
| Field '{fieldName}' is unknown and was ignored | changes 中包含不存在的字段名 | **警告**(非阻断),检查字段名拼写 |
|
|
56
55
|
| Setting 'todoStatus' without 'isTodo: true' may have no effect | todoStatus 非 null 但 isTodo=false | 先将 isTodo 设为 true |
|
|
57
|
-
| old_str not found in the simplified Portal JSON of rem {remId} | Portal 编辑时 oldStr 在 9 字段简化 JSON 中不匹配 | 检查 oldStr 是否匹配 Portal 简化 JSON 格式(9 字段:id、type、portalType、portalDirectlyIncludedRem、parent、positionAmongstSiblings、children、createdAt、updatedAt) |
|
|
58
|
-
| old_str matches {N} locations in Portal rem {remId}. Make old_str more specific to match exactly once. | Portal 编辑时 oldStr 在简化 JSON 中匹配多处 | 扩大 oldStr 范围以唯一定位 |
|
|
59
56
|
|
|
60
57
|
---
|
|
61
58
|
|
|
@@ -109,16 +106,15 @@ export const ERROR_REFERENCE_CONTENT = `
|
|
|
109
106
|
├─ "has been modified since last read"
|
|
110
107
|
│ └─ 重新执行 read → 然后重试 edit
|
|
111
108
|
│
|
|
112
|
-
├─ "old_str not found"
|
|
113
|
-
│ ├─
|
|
114
|
-
│
|
|
115
|
-
│ └─ 重新 read 确认当前内容
|
|
109
|
+
├─ "old_str not found"(edit_tree)
|
|
110
|
+
│ ├─ 检查空格、换行是否精确匹配
|
|
111
|
+
│ └─ 重新 read_tree 确认当前内容
|
|
116
112
|
│
|
|
117
|
-
├─ "old_str matches N locations"
|
|
113
|
+
├─ "old_str matches N locations"(edit_tree)
|
|
118
114
|
│ └─ 扩大 oldStr,包含更多上下文
|
|
119
115
|
│
|
|
120
|
-
├─ "
|
|
121
|
-
│ └─
|
|
116
|
+
├─ "Invalid value for" / "Field ... is unknown"(edit_rem)
|
|
117
|
+
│ └─ 检查字段名拼写和值类型
|
|
122
118
|
│
|
|
123
119
|
├─ "Content modification not allowed"
|
|
124
120
|
│ └─ 改用 edit_rem 修改内容
|
|
@@ -11,8 +11,8 @@ RemObject 是本项目对 RemNote Rem 的标准化表示,包含 51 个字段
|
|
|
11
11
|
|:-----|:-----|:-----|:---------|
|
|
12
12
|
| RW | 可读可写 | 20 | 默认输出 |
|
|
13
13
|
| Portal-W | Portal 专用可写 | 1 | 默认输出(Portal 简化模式) |
|
|
14
|
-
| R | 只读 |
|
|
15
|
-
| R-F | 只读低频 |
|
|
14
|
+
| R | 只读 | 13 | 默认输出 |
|
|
15
|
+
| R-F | 只读低频 | 18 | 仅 \\\`--full\\\` 输出 |
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -41,7 +41,7 @@ RemObject 是本项目对 RemNote Rem 的标准化表示,包含 51 个字段
|
|
|
41
41
|
| 字段 | 类型 | 权限 | 说明 |
|
|
42
42
|
|------|------|:----:|------|
|
|
43
43
|
| \\\`parent\\\` | \\\`string \\| null\\\` | RW | 父 Rem ID。null=顶级。UI:Rem 从原位置消失,出现在新父级下 |
|
|
44
|
-
| \\\`children\\\` | \\\`string[]\\\` | R | 子 Rem ID 有序数组 |
|
|
44
|
+
| \\\`children\\\` | \\\`string[]\\\` | R-F | 子 Rem ID 有序数组 |
|
|
45
45
|
|
|
46
46
|
## 格式 / 显示
|
|
47
47
|
|
|
@@ -215,7 +215,7 @@ for id in currentSet:
|
|
|
215
215
|
|
|
216
216
|
## 只读字段列表
|
|
217
217
|
|
|
218
|
-
以下 30 个字段在
|
|
218
|
+
以下 30 个字段在 changes 中出现时,**只产生警告,不执行写入**:
|
|
219
219
|
|
|
220
220
|
\\\`\\\`\\\`
|
|
221
221
|
id,
|
|
@@ -360,7 +360,7 @@ RemObject 中的 \\\`text\\\` 和 \\\`backText\\\` 字段使用 RichText 格式
|
|
|
360
360
|
{ "i": "a", "onlyAudio": true, "url": "..." } // 音频
|
|
361
361
|
\\\`\\\`\\\`
|
|
362
362
|
|
|
363
|
-
> 在 RemObject 格式化 JSON 中,数组内对象会展开为多行(每个 key 一行)。构造 \\\`
|
|
363
|
+
> 在 RemObject 格式化 JSON 中,数组内对象会展开为多行(每个 key 一行)。构造 \\\`edit_tree\\\` 的 oldStr/newStr 或 \\\`edit_rem\\\` 的 changes 中 RichText 值时,必须使用正确的多行格式。
|
|
364
364
|
|
|
365
365
|
### highlightColor(Rem 级别)vs h(RichText 行内)
|
|
366
366
|
|
|
@@ -371,5 +371,5 @@ RemObject 中的 \\\`text\\\` 和 \\\`backText\\\` 字段使用 RichText 格式
|
|
|
371
371
|
|
|
372
372
|
### 序列化确定性
|
|
373
373
|
|
|
374
|
-
RichText 对象元素内部按 **key 字母序排列**(Plugin 端 \\\`sortRichTextKeys()\\\` 处理),确保同一内容的序列化 JSON 始终一致。\\\`_id\\\` 中的 \\\`_\\\`(U+005F)排在所有小写字母(\\\`a\\\`=U+0061)之前,所以 \\\`_id\\\` 总是第一个 key
|
|
374
|
+
RichText 对象元素内部按 **key 字母序排列**(Plugin 端 \\\`sortRichTextKeys()\\\` 处理),确保同一内容的序列化 JSON 始终一致。\\\`_id\\\` 中的 \\\`_\\\`(U+005F)排在所有小写字母(\\\`a\\\`=U+0061)之前,所以 \\\`_id\\\` 总是第一个 key。这对乐观并发检测至关重要。
|
|
375
375
|
`;
|
|
@@ -3,25 +3,54 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { callCli } from '../daemon-client.js';
|
|
6
|
+
import { formatDataJson } from '../format.js';
|
|
6
7
|
export function registerEditTools(server) {
|
|
7
8
|
// -------------------------------------------------------------------------
|
|
8
9
|
// edit_rem
|
|
9
10
|
// -------------------------------------------------------------------------
|
|
10
11
|
server.addTool({
|
|
11
12
|
name: 'edit_rem',
|
|
12
|
-
description: '
|
|
13
|
+
description: '直接修改单个 Rem 的属性字段。通过 changes 对象传入 {字段名: 新值}。' +
|
|
14
|
+
'\\n\\n适用场景:修改文本、类型、标题级别、高亮色、Todo 状态、practiceDirection、Portal 引用列表等属性。' +
|
|
15
|
+
'\\n不适合修改子树结构(增删移动子节点用 edit_tree)。' +
|
|
16
|
+
'\\n\\n前置条件:必须先对同一个 remId 调用 read_rem 建立缓存,否则被防线 1 拒绝。' +
|
|
17
|
+
'\\n工作流:read_rem → 查看属性 → edit_rem(changes) → 写入成功后自动从 Plugin 重新读取并更新缓存。' +
|
|
18
|
+
'\\n\\n操作指南:' +
|
|
19
|
+
'\\n- changes 对象的键=字段名,值=新值。21 个可写字段:text, backText, type, isDocument, parent, fontSize, highlightColor, isTodo, todoStatus, isCode, isQuote, isListItem, isCardItem, isSlot, isProperty, enablePractice, practiceDirection, tags, sources, positionAmongstSiblings, portalDirectlyIncludedRem' +
|
|
20
|
+
'\\n- RichText 编辑(text/backText):传完整 RichText 数组,元素为纯字符串或格式化对象如 {"i":"m","text":"...","b":true}。backText 传 null 可清除背面' +
|
|
21
|
+
'\\n- tags/sources 使用 diff 机制:传入目标 ID 数组,系统自动计算增删差异' +
|
|
22
|
+
'\\n- portalDirectlyIncludedRem:传入目标 ID 数组(仅 type=portal 的 Rem 可修改),系统自动 diff' +
|
|
23
|
+
'\\n- parent + positionAmongstSiblings 联动:通过同一个 SDK 调用写入,可单独或同时修改' +
|
|
24
|
+
'\\n\\n枚举约束:' +
|
|
25
|
+
'\\n- type: concept / descriptor / default(portal 不可设置)' +
|
|
26
|
+
'\\n- practiceDirection: forward / backward / both / none' +
|
|
27
|
+
'\\n- highlightColor: Red / Orange / Yellow / Green / Blue / Purple / Gray / Brown / Pink / null(清除)' +
|
|
28
|
+
'\\n- fontSize: H1 / H2 / H3 / null(恢复普通)' +
|
|
29
|
+
'\\n- todoStatus: Finished / Unfinished / null(需先 isTodo=true)' +
|
|
30
|
+
'\\n\\nhighlightColor vs RichText h 字段:两者完全独立。highlightColor 是整行背景色(字符串如 "Red");h 是 RichText 元素内部的行内荧光底色(数字:1=Red, 2=Orange, 3=Yellow, 4=Green, 5=Purple, 6=Blue, 7=Gray, 8=Brown, 9=Pink)。' +
|
|
31
|
+
'\\n\\n输出格式:JSON 对象,包含 changes(已写入的字段名数组)和 warnings(只读/未知字段警告数组)。' +
|
|
32
|
+
'\\n\\n两道防线:' +
|
|
33
|
+
'\\n1. 缓存存在检查——未先 read_rem 则报 "has not been read yet"' +
|
|
34
|
+
'\\n2. 并发检测——edit 时重新从 Plugin 读取并对比缓存,不一致则报 "has been modified since last read"。防线拒绝/部分失败时不更新缓存,迫使重新 read_rem' +
|
|
35
|
+
'\\n\\n常见错误:' +
|
|
36
|
+
'\\n- "has not been read yet" → 先 read_rem' +
|
|
37
|
+
'\\n- "has been modified since last read" → 重新 read_rem' +
|
|
38
|
+
'\\n- "Invalid value for \'field\'" → 检查枚举合法值' +
|
|
39
|
+
'\\n- "Field \'...\' is read-only/unknown and was ignored" → 警告不阻断' +
|
|
40
|
+
'\\n\\n关联工具:read_rem(前置读取)、edit_tree(子树结构编辑)',
|
|
13
41
|
parameters: z.object({
|
|
14
42
|
remId: z.string().describe('目标 Rem 的 ID'),
|
|
15
|
-
|
|
16
|
-
|
|
43
|
+
changes: z.record(z.unknown()).describe('要修改的字段及新值。键=字段名,值=新值。' +
|
|
44
|
+
'支持字段:text, backText, type, isDocument, parent, fontSize, highlightColor, ' +
|
|
45
|
+
'isTodo, todoStatus, isCode, isQuote, isListItem, isCardItem, isSlot, isProperty, ' +
|
|
46
|
+
'enablePractice, practiceDirection, tags, sources, positionAmongstSiblings, portalDirectlyIncludedRem。'),
|
|
17
47
|
}),
|
|
18
48
|
execute: async (args) => {
|
|
19
49
|
const response = await callCli('edit-rem', {
|
|
20
50
|
remId: args.remId,
|
|
21
|
-
|
|
22
|
-
newStr: args.newStr,
|
|
51
|
+
changes: args.changes,
|
|
23
52
|
});
|
|
24
|
-
return
|
|
53
|
+
return formatDataJson(response);
|
|
25
54
|
},
|
|
26
55
|
});
|
|
27
56
|
// -------------------------------------------------------------------------
|
|
@@ -29,7 +58,39 @@ export function registerEditTools(server) {
|
|
|
29
58
|
// -------------------------------------------------------------------------
|
|
30
59
|
server.addTool({
|
|
31
60
|
name: 'edit_tree',
|
|
32
|
-
description: '通过 str_replace 对 Markdown
|
|
61
|
+
description: '通过 str_replace 对 Markdown 大纲进行结构编辑(行级增/删/移/重排),禁止修改已有行的文字内容。' +
|
|
62
|
+
'\\n\\n适用场景:新增子节点、删除节点、移动节点到不同父节点、重排同级节点顺序。' +
|
|
63
|
+
'\\n不适合修改已有行的文本内容(用 edit_rem)。' +
|
|
64
|
+
'\\n\\n前置条件:必须先对同一个 remId 调用 read_tree 建立缓存,否则被防线拒绝。' +
|
|
65
|
+
'\\noldStr 必须精确匹配 read_tree 返回的大纲缓存(含缩进、空格、换行),且只匹配 1 次。' +
|
|
66
|
+
'\\n工作流:read_tree → 查看大纲 → edit_tree(oldStr, newStr) → 成功后自动 re-read 更新缓存(可连续 edit)。' +
|
|
67
|
+
'\\n\\n四种操作:' +
|
|
68
|
+
'\\n1. 新增:在 newStr 中插入无 remId 注释的新行。缩进决定父子关系(每级 2 空格)' +
|
|
69
|
+
'\\n2. 删除:从 newStr 中移除带 remId 的行。必须同时删除所有可见子行' +
|
|
70
|
+
'\\n3. 移动:改变行的缩进或位置(换父节点)' +
|
|
71
|
+
'\\n4. 重排:调换同级行的顺序' +
|
|
72
|
+
'\\n执行顺序:Create → Move → Reorder → Delete' +
|
|
73
|
+
'\\n\\n新增行格式:' +
|
|
74
|
+
'\\n- Markdown 前缀:# / ## / ### / - [ ] / - [x] / `代码` / ---' +
|
|
75
|
+
'\\n- 箭头分隔符(闪卡):→ ← ↔(单行)、↓ ↑ ↕(多行,带 backText 或子节点为答案)' +
|
|
76
|
+
'\\n- 元数据注释(可选):<!--type:concept--> <!--doc--> <!--tag:Name(id)--> 可组合' +
|
|
77
|
+
'\\n- Portal 创建:<!--portal refs:id1,id2--> 或 <!--portal-->(空 Portal)' +
|
|
78
|
+
'\\n\\n⚠️ 插入位置红线:新行必须插在目标层级所有兄弟的末尾,不能插在父 Rem 和它的 children 之间,否则触发 children_captured 错误。如需"创建新父节点并移入已有 children",必须分两次 edit_tree 完成。' +
|
|
79
|
+
'\\n\\n六种禁止操作:' +
|
|
80
|
+
'\\n- content_modified:修改已有行内容 → 改用 edit_rem' +
|
|
81
|
+
'\\n- root_modified:修改/删除根节点' +
|
|
82
|
+
'\\n- folded_delete:删除有隐藏子节点(children:N)的行 → 用更大 depth 重新 read_tree' +
|
|
83
|
+
'\\n- orphan_detected:删父不删子 → 必须一起删' +
|
|
84
|
+
'\\n- elided_modified:修改省略占位符 → 用更大参数重新 read_tree' +
|
|
85
|
+
'\\n- children_captured:新行劫持已有子节点 → 把新行插到兄弟末尾' +
|
|
86
|
+
'\\n\\n输出格式:JSON 对象,包含 operations 数组(每项有 type: create/delete/move/reorder 及相关字段)。' +
|
|
87
|
+
'\\n\\n缓存行为:成功后自动 re-read 更新缓存;失败则不更新缓存,迫使重新 read_tree。' +
|
|
88
|
+
'\\n\\n常见错误:' +
|
|
89
|
+
'\\n- "old_str not found" → 检查精确匹配(含缩进换行)' +
|
|
90
|
+
'\\n- "old_str matches N locations" → 扩大 oldStr 包含更多上下文使其唯一' +
|
|
91
|
+
'\\n- "Content modification not allowed" → 改用 edit_rem 修改内容' +
|
|
92
|
+
'\\n- "children_captured" → 新行插到兄弟末尾,不要插在父 Rem 和 children 之间' +
|
|
93
|
+
'\\n\\n关联工具:read_tree(前置读取大纲)、edit_rem(修改单个 Rem 属性/内容)',
|
|
33
94
|
parameters: z.object({
|
|
34
95
|
remId: z.string().describe('根 Rem 的 ID(与 read_tree 时相同)'),
|
|
35
96
|
oldStr: z.string().describe('要替换的大纲片段(必须精确匹配缓存中的内容,且恰好匹配 1 次)'),
|
|
@@ -41,7 +102,7 @@ export function registerEditTools(server) {
|
|
|
41
102
|
oldStr: args.oldStr,
|
|
42
103
|
newStr: args.newStr,
|
|
43
104
|
});
|
|
44
|
-
return
|
|
105
|
+
return formatDataJson(response);
|
|
45
106
|
},
|
|
46
107
|
});
|
|
47
108
|
}
|
|
@@ -3,17 +3,18 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { callCli } from '../daemon-client.js';
|
|
6
|
+
import { formatDataJson } from '../format.js';
|
|
6
7
|
export function registerInfraTools(server) {
|
|
7
8
|
// -------------------------------------------------------------------------
|
|
8
9
|
// setup
|
|
9
10
|
// -------------------------------------------------------------------------
|
|
10
11
|
server.addTool({
|
|
11
12
|
name: 'setup',
|
|
12
|
-
description: '启动 Chrome 浏览器让用户登录 RemNote,保存登录凭证到本地 profile。这是使用 headless 模式(connect --headless)的前置步骤。\n\n调用后 Chrome 窗口会弹出,你需要立即告知用户:"已打开 Chrome,请在浏览器中登录 RemNote
|
|
13
|
+
description: '启动 Chrome 浏览器让用户登录 RemNote,保存登录凭证到本地 profile。这是使用 headless 模式(connect --headless)的前置步骤。\n\nsetup 只负责登录——配置 dev plugin 是 connect 之后的事。\n\n调用后 Chrome 窗口会弹出,你需要立即告知用户:"已打开 Chrome,请在浏览器中登录 RemNote,完成后用 Cmd+Q(macOS)或关闭窗口彻底退出 Chrome"。此工具会阻塞等待用户关闭 Chrome 后返回结果。\n\n幂等:已完成 setup 后重复调用返回 alreadyDone: true。\n超时:600 秒(用户可能需要较长时间完成登录、2FA 验证等)。\n前置条件:需要桌面环境(GUI),无 GUI 环境会报错。\n关联工具:connect(启动会话,传 headless 参数实现无人值守连接)',
|
|
13
14
|
parameters: z.object({}),
|
|
14
15
|
execute: async () => {
|
|
15
16
|
const response = await callCli('setup', undefined, { timeoutMs: 600_000 });
|
|
16
|
-
return
|
|
17
|
+
return formatDataJson(response);
|
|
17
18
|
},
|
|
18
19
|
});
|
|
19
20
|
// -------------------------------------------------------------------------
|
|
@@ -21,7 +22,7 @@ export function registerInfraTools(server) {
|
|
|
21
22
|
// -------------------------------------------------------------------------
|
|
22
23
|
server.addTool({
|
|
23
24
|
name: 'connect',
|
|
24
|
-
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\n输出:返回 JSON,关键字段 ok、alreadyRunning
|
|
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\n输出:返回 JSON,关键字段 ok、alreadyRunning、instance、slotIndex、pid、wsPort、headless。\n幂等:重复调用不会启动多个 daemon。daemon 默认 30 分钟无活动自动关闭。\n关联工具:setup(headless 前置)、disconnect(结束会话)、health(检查状态)',
|
|
25
26
|
parameters: z.object({
|
|
26
27
|
headless: z.boolean().optional().describe('启用 headless 模式:自动启动 Chrome 加载 Plugin(需先 setup)'),
|
|
27
28
|
}),
|
|
@@ -33,7 +34,7 @@ export function registerInfraTools(server) {
|
|
|
33
34
|
timeoutMs: 90_000,
|
|
34
35
|
flags: flags.length > 0 ? flags : undefined,
|
|
35
36
|
});
|
|
36
|
-
return
|
|
37
|
+
return formatDataJson(response);
|
|
37
38
|
},
|
|
38
39
|
});
|
|
39
40
|
// -------------------------------------------------------------------------
|
|
@@ -41,11 +42,11 @@ export function registerInfraTools(server) {
|
|
|
41
42
|
// -------------------------------------------------------------------------
|
|
42
43
|
server.addTool({
|
|
43
44
|
name: 'disconnect',
|
|
44
|
-
description: '停止守护进程,释放所有端口、清空内存缓存、结束当前会话。\n\n适用场景:\n- 操作完成后主动释放资源\n- 需要重置连接状态(例如排查问题时先 disconnect 再 connect)\n\n输出:返回 JSON,关键字段 ok、wasRunning(之前是否在运行)、pid。\n幂等:daemon 未运行时调用也返回 ok: true。\n所有缓存随 daemon 退出清空,下次 connect 后需重新 read。\n关联工具:connect(启动会话)、health(检查状态)',
|
|
45
|
+
description: '停止守护进程,释放所有端口、清空内存缓存、结束当前会话。\n\n适用场景:\n- 操作完成后主动释放资源\n- 需要重置连接状态(例如排查问题时先 disconnect 再 connect)\n\n输出:返回 JSON,关键字段 ok、wasRunning(之前是否在运行)、instance、pid。\n幂等:daemon 未运行时调用也返回 ok: true。\n所有缓存随 daemon 退出清空,下次 connect 后需重新 read。\n关联工具:connect(启动会话)、health(检查状态)',
|
|
45
46
|
parameters: z.object({}),
|
|
46
47
|
execute: async () => {
|
|
47
48
|
const response = await callCli('disconnect');
|
|
48
|
-
return
|
|
49
|
+
return formatDataJson(response);
|
|
49
50
|
},
|
|
50
51
|
});
|
|
51
52
|
// -------------------------------------------------------------------------
|
|
@@ -53,7 +54,7 @@ export function registerInfraTools(server) {
|
|
|
53
54
|
// -------------------------------------------------------------------------
|
|
54
55
|
server.addTool({
|
|
55
56
|
name: 'health',
|
|
56
|
-
description: '检查系统三层状态(daemon 守护进程 / Plugin 连接 / SDK 就绪),用于诊断连接问题。\n\n适用场景:\n- 业务命令失败时,首先调用 health 定位故障层级\n- 执行 connect 后确认通道是否完全就绪\n- 长时间未操作后,检查 daemon 是否仍在运行\n\n输出:返回 JSON,关键字段 ok(三层是否全部健康)、daemon.running、plugin.connected、sdk.ready、timeoutRemaining。headless 模式下额外包含 headless 对象。\n三层有严格依赖:daemon 运行 → Plugin 连接 → SDK 就绪。\nok 为 false 时:daemon 未运行则 connect;Plugin 未连接则确认 RemNote 已打开(或使用 headless 模式);SDK 未就绪则等待重试。\n\n--diagnose 模式(headless 专用):截图 + 详细状态 + console 错误 + 排查建议。\n--reload 模式(headless 专用):重载 headless Chrome 页面。\n\n只读不写,不改变任何状态(--reload 除外)。\n关联工具:connect(启动)、disconnect(结束)',
|
|
57
|
+
description: '检查系统三层状态(daemon 守护进程 / Plugin 连接 / SDK 就绪),用于诊断连接问题。\n\n适用场景:\n- 业务命令失败时,首先调用 health 定位故障层级\n- 执行 connect 后确认通道是否完全就绪\n- 长时间未操作后,检查 daemon 是否仍在运行\n\n输出:返回 JSON,关键字段 ok(三层是否全部健康)、instance、slotIndex、daemon.running、plugin.connected、sdk.ready、timeoutRemaining。headless 模式下额外包含 headless 对象。\n三层有严格依赖:daemon 运行 → Plugin 连接 → SDK 就绪。\nok 为 false 时:daemon 未运行则 connect;Plugin 未连接则确认 RemNote 已打开(或使用 headless 模式);SDK 未就绪则等待重试。\n\n--diagnose 模式(headless 专用):截图 + 详细状态 + console 错误 + 排查建议。\n--reload 模式(headless 专用):重载 headless Chrome 页面。\n\n只读不写,不改变任何状态(--reload 除外)。\n关联工具:connect(启动)、disconnect(结束)',
|
|
57
58
|
parameters: z.object({
|
|
58
59
|
diagnose: z.boolean().optional().describe('诊断 headless Chrome(截图 + 状态 + console 错误)'),
|
|
59
60
|
reload: z.boolean().optional().describe('重载 headless Chrome 页面'),
|
|
@@ -67,7 +68,42 @@ export function registerInfraTools(server) {
|
|
|
67
68
|
const response = await callCli('health', undefined, {
|
|
68
69
|
flags: flags.length > 0 ? flags : undefined,
|
|
69
70
|
});
|
|
70
|
-
return
|
|
71
|
+
return formatDataJson(response);
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
// -------------------------------------------------------------------------
|
|
75
|
+
// addon
|
|
76
|
+
// -------------------------------------------------------------------------
|
|
77
|
+
server.addTool({
|
|
78
|
+
name: 'addon',
|
|
79
|
+
description: '管理增强项目(addon):查看状态、安装、卸载。\n\n增强项目是独立安装的可选组件,扩展核心功能(如 remnote-rag 语义搜索)。\n\n操作:\n- action="list":查看所有可用增强项目的状态(已安装/未安装、已启用/已禁用、缺失配置)\n- action="install":安装并启用指定增强项目(name 必填)\n- action="uninstall":卸载指定增强项目(name 必填,可选 purge 清理数据目录)\n\n安装后需在配置页面中填写必需的配置项(如 API Key)。\n不需要 daemon 运行。',
|
|
80
|
+
parameters: z.object({
|
|
81
|
+
action: z.enum(['list', 'install', 'uninstall']).describe('操作类型'),
|
|
82
|
+
name: z.string().optional().describe('增强项目名称(install/uninstall 时必填)'),
|
|
83
|
+
purge: z.boolean().optional().describe('卸载时清理数据目录(仅 uninstall 有效)'),
|
|
84
|
+
}),
|
|
85
|
+
execute: async (args) => {
|
|
86
|
+
const flags = [args.action];
|
|
87
|
+
if (args.name)
|
|
88
|
+
flags.push(args.name);
|
|
89
|
+
if (args.purge)
|
|
90
|
+
flags.push('--purge');
|
|
91
|
+
const response = await callCli('addon', undefined, {
|
|
92
|
+
flags,
|
|
93
|
+
});
|
|
94
|
+
return formatDataJson(response);
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
// -------------------------------------------------------------------------
|
|
98
|
+
// clean
|
|
99
|
+
// -------------------------------------------------------------------------
|
|
100
|
+
server.addTool({
|
|
101
|
+
name: 'clean',
|
|
102
|
+
description: '清理所有 daemon 进程、PID 文件、日志、注册表和 addon 数据。\n\n适用场景:\n- daemon 进程残留,disconnect 无法正常停止时\n- 端口被占用,多次启动失败后\n- 注册表损坏需要重置\n- 完整重装,清除所有状态从零开始\n\n⚠️ 破坏性操作——会停止所有 daemon、清空所有缓存和注册表。执行后需重新 connect。\n不需要 daemon 运行。',
|
|
103
|
+
parameters: z.object({}),
|
|
104
|
+
execute: async () => {
|
|
105
|
+
const response = await callCli('clean');
|
|
106
|
+
return formatDataJson(response);
|
|
71
107
|
},
|
|
72
108
|
});
|
|
73
109
|
}
|