memory-gateway-sync 0.9.0 → 0.11.0
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/index.js +36 -22
- package/package.json +1 -1
- package/src/index.ts +42 -20
package/index.js
CHANGED
|
@@ -4,12 +4,18 @@ import fs from "node:fs";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import crypto from "node:crypto";
|
|
6
6
|
import os from "node:os";
|
|
7
|
+
var registered = false;
|
|
7
8
|
var index_default = definePluginEntry({
|
|
8
9
|
id: "memory-gateway-sync",
|
|
9
10
|
name: "Memory Gateway Sync",
|
|
10
11
|
description: "fs.watch \u76D1\u542C\u591A workspace \u7684 MEMORY \u6587\u4EF6\u53D8\u5316\uFF0C\u53CC\u5199\u5230\u5916\u90E8 Gateway\uFF08\u652F\u6301 peers/groups/knowledge \u5B50\u76EE\u5F55\uFF09",
|
|
11
12
|
kind: "generic",
|
|
12
13
|
register(api) {
|
|
14
|
+
if (registered) {
|
|
15
|
+
api.logger.info("memory-gateway-sync: \u5DF2\u6CE8\u518C\uFF0C\u8DF3\u8FC7\u91CD\u590D\u52A0\u8F7D");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
registered = true;
|
|
13
19
|
const cfg = api.pluginConfig ?? {};
|
|
14
20
|
const gatewayUrl = cfg.gatewayUrl || process.env.MEMORY_SYNC_GATEWAY_URL || "";
|
|
15
21
|
const gatewayToken = cfg.gatewayToken || process.env.MEMORY_SYNC_GATEWAY_TOKEN || "";
|
|
@@ -81,6 +87,17 @@ var index_default = definePluginEntry({
|
|
|
81
87
|
return;
|
|
82
88
|
}
|
|
83
89
|
const relativePath = path.relative(ws.dir, absPath);
|
|
90
|
+
if (relativePath === "MEMORY.md" && content.trim().length > 0) {
|
|
91
|
+
try {
|
|
92
|
+
await fs.promises.writeFile(absPath, "", "utf-8");
|
|
93
|
+
const emptyHash = crypto.createHash("md5").update("").digest("hex");
|
|
94
|
+
lastSyncedHash.set(absPath, emptyHash);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
api.logger.warn(
|
|
97
|
+
`memory-gateway-sync: [${ws.agentId}] 清空 MEMORY.md 失败: ${String(err)}`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
84
101
|
let userId = null;
|
|
85
102
|
const parts = relativePath.replace(/\\/g, "/").split("/");
|
|
86
103
|
if (relativePath === "MEMORY.md" || parts[0] === "memory" && parts[1] === "owner") {
|
|
@@ -98,9 +115,10 @@ var index_default = definePluginEntry({
|
|
|
98
115
|
eventType,
|
|
99
116
|
syncedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
100
117
|
};
|
|
118
|
+
let syncOk = false;
|
|
101
119
|
try {
|
|
102
120
|
const controller = new AbortController();
|
|
103
|
-
const timeout = setTimeout(() => controller.abort(),
|
|
121
|
+
const timeout = setTimeout(() => controller.abort(), 12e4);
|
|
104
122
|
const res = await fetch(gatewayUrl, {
|
|
105
123
|
method: "POST",
|
|
106
124
|
headers: {
|
|
@@ -112,31 +130,11 @@ var index_default = definePluginEntry({
|
|
|
112
130
|
});
|
|
113
131
|
clearTimeout(timeout);
|
|
114
132
|
if (res.ok) {
|
|
133
|
+
syncOk = true;
|
|
115
134
|
lastSyncedHash.set(absPath, contentHash);
|
|
116
135
|
api.logger.info?.(
|
|
117
136
|
`memory-gateway-sync: [${ws.agentId}] \u540C\u6B65\u6210\u529F ${relativePath} (${content.length} chars)`
|
|
118
137
|
);
|
|
119
|
-
if (relativePath === "MEMORY.md" && content.trim().length > 0) {
|
|
120
|
-
try {
|
|
121
|
-
const backupPath = absPath + ".bak";
|
|
122
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
123
|
-
const section = `
|
|
124
|
-
<!-- backup ${timestamp} -->
|
|
125
|
-
${content}
|
|
126
|
-
`;
|
|
127
|
-
await fs.promises.appendFile(backupPath, section, "utf-8");
|
|
128
|
-
await fs.promises.writeFile(absPath, "", "utf-8");
|
|
129
|
-
const emptyHash = crypto.createHash("md5").update("").digest("hex");
|
|
130
|
-
lastSyncedHash.set(absPath, emptyHash);
|
|
131
|
-
api.logger.info?.(
|
|
132
|
-
`memory-gateway-sync: [${ws.agentId}] MEMORY.md \u5DF2\u5907\u4EFD\u5230 .bak \u5E76\u6E05\u7A7A`
|
|
133
|
-
);
|
|
134
|
-
} catch (err) {
|
|
135
|
-
api.logger.warn(
|
|
136
|
-
`memory-gateway-sync: [${ws.agentId}] \u5907\u4EFD/\u6E05\u7A7A MEMORY.md \u5931\u8D25: ${String(err)}`
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
138
|
} else {
|
|
141
139
|
const body = await res.text().catch(() => "");
|
|
142
140
|
api.logger.warn(
|
|
@@ -148,6 +146,21 @@ ${content}
|
|
|
148
146
|
`memory-gateway-sync: [${ws.agentId}] POST \u5931\u8D25 ${relativePath}: ${String(err)}`
|
|
149
147
|
);
|
|
150
148
|
}
|
|
149
|
+
if (relativePath === "MEMORY.md" && content.trim().length > 0) {
|
|
150
|
+
try {
|
|
151
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
152
|
+
const section = `\n<!-- backup ${timestamp} synced=${syncOk} -->\n${content}\n`;
|
|
153
|
+
const backupPath = syncOk ? absPath + ".bak" : absPath + ".failed";
|
|
154
|
+
await fs.promises.appendFile(backupPath, section, "utf-8");
|
|
155
|
+
api.logger.info?.(
|
|
156
|
+
`memory-gateway-sync: [${ws.agentId}] MEMORY.md \u5DF2\u5907\u4EFD\u5230 ${syncOk ? ".bak" : ".failed"}`
|
|
157
|
+
);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
api.logger.warn(
|
|
160
|
+
`memory-gateway-sync: [${ws.agentId}] \u5907\u4EFD/\u6E05\u7A7A MEMORY.md \u5931\u8D25: ${String(err)}`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
151
164
|
};
|
|
152
165
|
const scheduleSync = (absPath, eventType) => {
|
|
153
166
|
const existing = debounceTimers.get(absPath);
|
|
@@ -174,6 +187,7 @@ ${content}
|
|
|
174
187
|
api.logger.info?.(
|
|
175
188
|
`memory-gateway-sync: [${wsAgentId}] \u5F00\u59CB\u76D1\u542C MEMORY.md`
|
|
176
189
|
);
|
|
190
|
+
scheduleSync(memoryMdPath, "startup");
|
|
177
191
|
} catch (err) {
|
|
178
192
|
api.logger.warn(
|
|
179
193
|
`memory-gateway-sync: [${wsAgentId}] \u65E0\u6CD5\u76D1\u542C MEMORY.md: ${String(err)}`
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -29,6 +29,9 @@ interface WorkspaceContext {
|
|
|
29
29
|
agentId: string;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// ── 防重复加载 ───────────────────────────────────────────────────────────────
|
|
33
|
+
let registered = false;
|
|
34
|
+
|
|
32
35
|
// ── 插件入口 ──────────────────────────────────────────────────────────────────
|
|
33
36
|
|
|
34
37
|
export default definePluginEntry({
|
|
@@ -39,6 +42,11 @@ export default definePluginEntry({
|
|
|
39
42
|
kind: "generic",
|
|
40
43
|
|
|
41
44
|
register(api) {
|
|
45
|
+
if (registered) {
|
|
46
|
+
api.logger.info("memory-gateway-sync: 已注册,跳过重复加载");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
registered = true;
|
|
42
50
|
const cfg = (api.pluginConfig ?? {}) as Partial<PluginConfig>;
|
|
43
51
|
const gatewayUrl = cfg.gatewayUrl || process.env.MEMORY_SYNC_GATEWAY_URL || "";
|
|
44
52
|
const gatewayToken = cfg.gatewayToken || process.env.MEMORY_SYNC_GATEWAY_TOKEN || "";
|
|
@@ -142,6 +150,19 @@ export default definePluginEntry({
|
|
|
142
150
|
|
|
143
151
|
const relativePath = path.relative(ws.dir, absPath);
|
|
144
152
|
|
|
153
|
+
// MEMORY.md 立即清空源文件,防止数据泄露
|
|
154
|
+
if (relativePath === "MEMORY.md" && content.trim().length > 0) {
|
|
155
|
+
try {
|
|
156
|
+
await fs.promises.writeFile(absPath, "", "utf-8");
|
|
157
|
+
const emptyHash = crypto.createHash("md5").update("").digest("hex");
|
|
158
|
+
lastSyncedHash.set(absPath, emptyHash);
|
|
159
|
+
} catch (err) {
|
|
160
|
+
api.logger.warn(
|
|
161
|
+
`memory-gateway-sync: [${ws.agentId}] 清空 MEMORY.md 失败: ${String(err)}`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
145
166
|
// 根据路径解析 userId
|
|
146
167
|
// MEMORY.md / memory/owner/* → 环境变量 owner_claw_user_id
|
|
147
168
|
// memory/peers/{peerId}/* → 路径中的 peerId
|
|
@@ -165,9 +186,10 @@ export default definePluginEntry({
|
|
|
165
186
|
syncedAt: new Date().toISOString(),
|
|
166
187
|
};
|
|
167
188
|
|
|
189
|
+
let syncOk = false;
|
|
168
190
|
try {
|
|
169
191
|
const controller = new AbortController();
|
|
170
|
-
const timeout = setTimeout(() => controller.abort(),
|
|
192
|
+
const timeout = setTimeout(() => controller.abort(), 120000);
|
|
171
193
|
|
|
172
194
|
const res = await fetch(gatewayUrl, {
|
|
173
195
|
method: "POST",
|
|
@@ -182,29 +204,11 @@ export default definePluginEntry({
|
|
|
182
204
|
clearTimeout(timeout);
|
|
183
205
|
|
|
184
206
|
if (res.ok) {
|
|
207
|
+
syncOk = true;
|
|
185
208
|
lastSyncedHash.set(absPath, contentHash);
|
|
186
209
|
api.logger.info?.(
|
|
187
210
|
`memory-gateway-sync: [${ws.agentId}] 同步成功 ${relativePath} (${content.length} chars)`
|
|
188
211
|
);
|
|
189
|
-
|
|
190
|
-
if (relativePath === "MEMORY.md" && content.trim().length > 0) {
|
|
191
|
-
try {
|
|
192
|
-
const backupPath = absPath + ".bak";
|
|
193
|
-
const timestamp = new Date().toISOString();
|
|
194
|
-
const section = `\n<!-- backup ${timestamp} -->\n${content}\n`;
|
|
195
|
-
await fs.promises.appendFile(backupPath, section, "utf-8");
|
|
196
|
-
await fs.promises.writeFile(absPath, "", "utf-8");
|
|
197
|
-
const emptyHash = crypto.createHash("md5").update("").digest("hex");
|
|
198
|
-
lastSyncedHash.set(absPath, emptyHash);
|
|
199
|
-
api.logger.info?.(
|
|
200
|
-
`memory-gateway-sync: [${ws.agentId}] MEMORY.md 已备份到 .bak 并清空`
|
|
201
|
-
);
|
|
202
|
-
} catch (err) {
|
|
203
|
-
api.logger.warn(
|
|
204
|
-
`memory-gateway-sync: [${ws.agentId}] 备份/清空 MEMORY.md 失败: ${String(err)}`
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
212
|
} else {
|
|
209
213
|
const body = await res.text().catch(() => "");
|
|
210
214
|
api.logger.warn(
|
|
@@ -216,6 +220,22 @@ export default definePluginEntry({
|
|
|
216
220
|
`memory-gateway-sync: [${ws.agentId}] POST 失败 ${relativePath}: ${String(err)}`
|
|
217
221
|
);
|
|
218
222
|
}
|
|
223
|
+
|
|
224
|
+
if (relativePath === "MEMORY.md" && content.trim().length > 0) {
|
|
225
|
+
try {
|
|
226
|
+
const timestamp = new Date().toISOString();
|
|
227
|
+
const section = `\n<!-- backup ${timestamp} synced=${syncOk} -->\n${content}\n`;
|
|
228
|
+
const backupPath = syncOk ? absPath + ".bak" : absPath + ".failed";
|
|
229
|
+
await fs.promises.appendFile(backupPath, section, "utf-8");
|
|
230
|
+
api.logger.info?.(
|
|
231
|
+
`memory-gateway-sync: [${ws.agentId}] MEMORY.md 已备份到 ${syncOk ? ".bak" : ".failed"}`
|
|
232
|
+
);
|
|
233
|
+
} catch (err) {
|
|
234
|
+
api.logger.warn(
|
|
235
|
+
`memory-gateway-sync: [${ws.agentId}] 备份 MEMORY.md 失败: ${String(err)}`
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
219
239
|
};
|
|
220
240
|
|
|
221
241
|
// ── 防抖调度 ─────────────────────────────────────────────────────
|
|
@@ -252,6 +272,8 @@ export default definePluginEntry({
|
|
|
252
272
|
api.logger.info?.(
|
|
253
273
|
`memory-gateway-sync: [${wsAgentId}] 开始监听 MEMORY.md`
|
|
254
274
|
);
|
|
275
|
+
// 启动时检查已有内容
|
|
276
|
+
scheduleSync(memoryMdPath, "startup");
|
|
255
277
|
} catch (err) {
|
|
256
278
|
api.logger.warn(
|
|
257
279
|
`memory-gateway-sync: [${wsAgentId}] 无法监听 MEMORY.md: ${String(err)}`
|