memory-gateway-sync 0.5.0 → 0.6.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/package.json +1 -1
- package/src/index.ts +38 -0
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import crypto from "node:crypto";
|
|
4
5
|
import os from "node:os";
|
|
5
6
|
|
|
6
7
|
// ── 类型 ──────────────────────────────────��───────────────────────────────────
|
|
@@ -15,6 +16,7 @@ interface PluginConfig {
|
|
|
15
16
|
interface IngestPayload {
|
|
16
17
|
path: string;
|
|
17
18
|
content: string;
|
|
19
|
+
release_name: string | null;
|
|
18
20
|
agentId: string;
|
|
19
21
|
userId: string | null;
|
|
20
22
|
workspaceDir: string;
|
|
@@ -68,6 +70,16 @@ export default definePluginEntry({
|
|
|
68
70
|
ReturnType<typeof setTimeout>
|
|
69
71
|
>();
|
|
70
72
|
|
|
73
|
+
// 内容哈希 Map:key=文件绝对路径,value=上次同步内容的 MD5,用于去重
|
|
74
|
+
const lastSyncedHash = new Map<string, string>();
|
|
75
|
+
|
|
76
|
+
const parseReleaseName = (content: string): string | null => {
|
|
77
|
+
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
78
|
+
if (!match) return null;
|
|
79
|
+
const nameMatch = match[1].match(/^name:\s*(.+)$/m);
|
|
80
|
+
return nameMatch ? nameMatch[1].trim() : null;
|
|
81
|
+
};
|
|
82
|
+
|
|
71
83
|
// 已注册的 workspace 集合(避免重复注册)
|
|
72
84
|
const registeredWorkspaces = new Map<string, WorkspaceContext>();
|
|
73
85
|
|
|
@@ -118,6 +130,11 @@ export default definePluginEntry({
|
|
|
118
130
|
return;
|
|
119
131
|
}
|
|
120
132
|
|
|
133
|
+
const contentHash = crypto.createHash("md5").update(content).digest("hex");
|
|
134
|
+
if (lastSyncedHash.get(absPath) === contentHash) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
121
138
|
const relativePath = path.relative(ws.dir, absPath);
|
|
122
139
|
|
|
123
140
|
// 根据路径解析 userId
|
|
@@ -135,6 +152,7 @@ export default definePluginEntry({
|
|
|
135
152
|
const payload: IngestPayload = {
|
|
136
153
|
path: relativePath,
|
|
137
154
|
content,
|
|
155
|
+
release_name: parseReleaseName(content),
|
|
138
156
|
agentId: ws.agentId,
|
|
139
157
|
userId,
|
|
140
158
|
workspaceDir: ws.dir,
|
|
@@ -159,9 +177,29 @@ export default definePluginEntry({
|
|
|
159
177
|
clearTimeout(timeout);
|
|
160
178
|
|
|
161
179
|
if (res.ok) {
|
|
180
|
+
lastSyncedHash.set(absPath, contentHash);
|
|
162
181
|
api.logger.info?.(
|
|
163
182
|
`memory-gateway-sync: [${ws.agentId}] 同步成功 ${relativePath} (${content.length} chars)`
|
|
164
183
|
);
|
|
184
|
+
|
|
185
|
+
if (relativePath === "MEMORY.md" && content.trim().length > 0) {
|
|
186
|
+
try {
|
|
187
|
+
const backupPath = absPath + ".bak";
|
|
188
|
+
const timestamp = new Date().toISOString();
|
|
189
|
+
const section = `\n<!-- backup ${timestamp} -->\n${content}\n`;
|
|
190
|
+
await fs.promises.appendFile(backupPath, section, "utf-8");
|
|
191
|
+
await fs.promises.writeFile(absPath, "", "utf-8");
|
|
192
|
+
const emptyHash = crypto.createHash("md5").update("").digest("hex");
|
|
193
|
+
lastSyncedHash.set(absPath, emptyHash);
|
|
194
|
+
api.logger.info?.(
|
|
195
|
+
`memory-gateway-sync: [${ws.agentId}] MEMORY.md 已备份到 .bak 并清空`
|
|
196
|
+
);
|
|
197
|
+
} catch (err) {
|
|
198
|
+
api.logger.warn(
|
|
199
|
+
`memory-gateway-sync: [${ws.agentId}] 备份/清空 MEMORY.md 失败: ${String(err)}`
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
165
203
|
} else {
|
|
166
204
|
const body = await res.text().catch(() => "");
|
|
167
205
|
api.logger.warn(
|