memory-gateway-sync 0.8.0 → 0.10.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 +38 -24
- package/openclaw.plugin.json +0 -1
- package/package.json +1 -1
- package/src/index.ts +43 -23
package/index.js
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
|
+
// memory-gateway-sync/src/index.ts
|
|
1
2
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import crypto from "node:crypto";
|
|
5
6
|
import os from "node:os";
|
|
7
|
+
var registered = false;
|
|
6
8
|
var index_default = definePluginEntry({
|
|
7
9
|
id: "memory-gateway-sync",
|
|
8
10
|
name: "Memory Gateway Sync",
|
|
9
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",
|
|
10
12
|
kind: "generic",
|
|
11
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;
|
|
12
19
|
const cfg = api.pluginConfig ?? {};
|
|
13
|
-
const gatewayUrl = cfg.gatewayUrl;
|
|
14
|
-
const gatewayToken = cfg.gatewayToken;
|
|
20
|
+
const gatewayUrl = cfg.gatewayUrl || process.env.MEMORY_SYNC_GATEWAY_URL || "";
|
|
21
|
+
const gatewayToken = cfg.gatewayToken || process.env.MEMORY_SYNC_GATEWAY_TOKEN || "";
|
|
15
22
|
const debounceMs = cfg.debounceMs ?? 1500;
|
|
16
23
|
const defaultAgentId = cfg.agentId ?? "default";
|
|
17
24
|
const ownerClawUserId = (() => {
|
|
@@ -80,6 +87,17 @@ var index_default = definePluginEntry({
|
|
|
80
87
|
return;
|
|
81
88
|
}
|
|
82
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
|
+
}
|
|
83
101
|
let userId = null;
|
|
84
102
|
const parts = relativePath.replace(/\\/g, "/").split("/");
|
|
85
103
|
if (relativePath === "MEMORY.md" || parts[0] === "memory" && parts[1] === "owner") {
|
|
@@ -97,9 +115,10 @@ var index_default = definePluginEntry({
|
|
|
97
115
|
eventType,
|
|
98
116
|
syncedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
99
117
|
};
|
|
118
|
+
let syncOk = false;
|
|
100
119
|
try {
|
|
101
120
|
const controller = new AbortController();
|
|
102
|
-
const timeout = setTimeout(() => controller.abort(),
|
|
121
|
+
const timeout = setTimeout(() => controller.abort(), 12e4);
|
|
103
122
|
const res = await fetch(gatewayUrl, {
|
|
104
123
|
method: "POST",
|
|
105
124
|
headers: {
|
|
@@ -111,31 +130,11 @@ var index_default = definePluginEntry({
|
|
|
111
130
|
});
|
|
112
131
|
clearTimeout(timeout);
|
|
113
132
|
if (res.ok) {
|
|
133
|
+
syncOk = true;
|
|
114
134
|
lastSyncedHash.set(absPath, contentHash);
|
|
115
135
|
api.logger.info?.(
|
|
116
136
|
`memory-gateway-sync: [${ws.agentId}] \u540C\u6B65\u6210\u529F ${relativePath} (${content.length} chars)`
|
|
117
137
|
);
|
|
118
|
-
if (relativePath === "MEMORY.md" && content.trim().length > 0) {
|
|
119
|
-
try {
|
|
120
|
-
const backupPath = absPath + ".bak";
|
|
121
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
122
|
-
const section = `
|
|
123
|
-
<!-- backup ${timestamp} -->
|
|
124
|
-
${content}
|
|
125
|
-
`;
|
|
126
|
-
await fs.promises.appendFile(backupPath, section, "utf-8");
|
|
127
|
-
await fs.promises.writeFile(absPath, "", "utf-8");
|
|
128
|
-
const emptyHash = crypto.createHash("md5").update("").digest("hex");
|
|
129
|
-
lastSyncedHash.set(absPath, emptyHash);
|
|
130
|
-
api.logger.info?.(
|
|
131
|
-
`memory-gateway-sync: [${ws.agentId}] MEMORY.md \u5DF2\u5907\u4EFD\u5230 .bak \u5E76\u6E05\u7A7A`
|
|
132
|
-
);
|
|
133
|
-
} catch (err) {
|
|
134
|
-
api.logger.warn(
|
|
135
|
-
`memory-gateway-sync: [${ws.agentId}] \u5907\u4EFD/\u6E05\u7A7A MEMORY.md \u5931\u8D25: ${String(err)}`
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
138
|
} else {
|
|
140
139
|
const body = await res.text().catch(() => "");
|
|
141
140
|
api.logger.warn(
|
|
@@ -147,6 +146,21 @@ ${content}
|
|
|
147
146
|
`memory-gateway-sync: [${ws.agentId}] POST \u5931\u8D25 ${relativePath}: ${String(err)}`
|
|
148
147
|
);
|
|
149
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
|
+
}
|
|
150
164
|
};
|
|
151
165
|
const scheduleSync = (absPath, eventType) => {
|
|
152
166
|
const existing = debounceTimers.get(absPath);
|
package/openclaw.plugin.json
CHANGED
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,9 +42,14 @@ export default definePluginEntry({
|
|
|
39
42
|
kind: "generic",
|
|
40
43
|
|
|
41
44
|
register(api) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
if (registered) {
|
|
46
|
+
api.logger.info("memory-gateway-sync: 已注册,跳过重复加载");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
registered = true;
|
|
50
|
+
const cfg = (api.pluginConfig ?? {}) as Partial<PluginConfig>;
|
|
51
|
+
const gatewayUrl = cfg.gatewayUrl || process.env.MEMORY_SYNC_GATEWAY_URL || "";
|
|
52
|
+
const gatewayToken = cfg.gatewayToken || process.env.MEMORY_SYNC_GATEWAY_TOKEN || "";
|
|
45
53
|
const debounceMs = cfg.debounceMs ?? 1500;
|
|
46
54
|
const defaultAgentId = cfg.agentId ?? "default";
|
|
47
55
|
const ownerClawUserId = (() => {
|
|
@@ -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
|
// ── 防抖调度 ─────────────────────────────────────────────────────
|