memory-gateway-sync 0.14.0 → 0.14.2
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 +57 -37
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/index.ts +72 -42
package/index.js
CHANGED
|
@@ -225,6 +225,42 @@ var index_default = definePluginEntry({
|
|
|
225
225
|
`memory-gateway-sync: [${wsAgentId}] workspace 注册完成 → ${wsDir}`
|
|
226
226
|
);
|
|
227
227
|
};
|
|
228
|
+
const scanTenantWorkspaces = async (tenantDir, userId) => {
|
|
229
|
+
try {
|
|
230
|
+
const entries = await fs.promises.readdir(tenantDir, { withFileTypes: true });
|
|
231
|
+
for (const entry of entries) {
|
|
232
|
+
if (!entry.isDirectory()) continue;
|
|
233
|
+
const name = entry.name;
|
|
234
|
+
if (name !== "workspace" && !name.startsWith("workspace-")) continue;
|
|
235
|
+
const wsDir = path.join(tenantDir, name);
|
|
236
|
+
const wsAgentId = resolveAgentId(name);
|
|
237
|
+
await registerWorkspace(wsDir, wsAgentId, userId);
|
|
238
|
+
}
|
|
239
|
+
} catch {
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
const watchTenantWorkspacesDir = (tenantDir, userId) => {
|
|
243
|
+
try {
|
|
244
|
+
fs.watch(tenantDir, async (_eventType, filename) => {
|
|
245
|
+
if (!filename) return;
|
|
246
|
+
if (filename !== "workspace" && !filename.startsWith("workspace-")) return;
|
|
247
|
+
const wsDir = path.join(tenantDir, filename);
|
|
248
|
+
try {
|
|
249
|
+
const stat = await fs.promises.stat(wsDir);
|
|
250
|
+
if (!stat.isDirectory()) return;
|
|
251
|
+
const realDir = await fs.promises.realpath(wsDir).catch(() => wsDir);
|
|
252
|
+
if (registeredRealPaths.has(realDir)) return;
|
|
253
|
+
const wsAgentId = resolveAgentId(filename);
|
|
254
|
+
api.logger.info(
|
|
255
|
+
`memory-gateway-sync: [tenants] 发现新 workspace → ${userId}/${filename} (agentId=${wsAgentId})`
|
|
256
|
+
);
|
|
257
|
+
await registerWorkspace(wsDir, wsAgentId, userId);
|
|
258
|
+
} catch {
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
} catch {
|
|
262
|
+
}
|
|
263
|
+
};
|
|
228
264
|
const scanWorkspaces = async () => {
|
|
229
265
|
try {
|
|
230
266
|
const entries = await fs.promises.readdir(openclawDir, {
|
|
@@ -233,6 +269,13 @@ var index_default = definePluginEntry({
|
|
|
233
269
|
for (const entry of entries) {
|
|
234
270
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
235
271
|
const name = entry.name;
|
|
272
|
+
if (name.startsWith("user-")) {
|
|
273
|
+
const tenantDir = path.join(openclawDir, name);
|
|
274
|
+
const userId = name;
|
|
275
|
+
await scanTenantWorkspaces(tenantDir, userId);
|
|
276
|
+
watchTenantWorkspacesDir(tenantDir, userId);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
236
279
|
if (name !== "workspace" && !name.startsWith("workspace-")) continue;
|
|
237
280
|
const wsDir = path.join(openclawDir, name);
|
|
238
281
|
if (entry.isSymbolicLink()) {
|
|
@@ -278,6 +321,20 @@ var index_default = definePluginEntry({
|
|
|
278
321
|
try {
|
|
279
322
|
fs.watch(dir, async (eventType, filename) => {
|
|
280
323
|
if (!filename) return;
|
|
324
|
+
if (dir === openclawDir && filename.startsWith("user-")) {
|
|
325
|
+
const tenantDir = path.join(openclawDir, filename);
|
|
326
|
+
try {
|
|
327
|
+
const stat = await fs.promises.stat(tenantDir);
|
|
328
|
+
if (!stat.isDirectory()) return;
|
|
329
|
+
api.logger.info(
|
|
330
|
+
`memory-gateway-sync: 发现新 tenant → ${filename}`
|
|
331
|
+
);
|
|
332
|
+
await scanTenantWorkspaces(tenantDir, filename);
|
|
333
|
+
watchTenantWorkspacesDir(tenantDir, filename);
|
|
334
|
+
} catch {
|
|
335
|
+
}
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
281
338
|
if (filename !== "workspace" && !filename.startsWith("workspace-")) {
|
|
282
339
|
return;
|
|
283
340
|
}
|
|
@@ -306,43 +363,6 @@ var index_default = definePluginEntry({
|
|
|
306
363
|
);
|
|
307
364
|
}
|
|
308
365
|
};
|
|
309
|
-
|
|
310
|
-
// ── Tenants 模式 ──
|
|
311
|
-
|
|
312
|
-
const scanTenantWorkspaces = async (tenantDir, userId) => {
|
|
313
|
-
const workspacesDir = path.join(tenantDir, "workspaces");
|
|
314
|
-
try {
|
|
315
|
-
const entries = await fs.promises.readdir(workspacesDir, { withFileTypes: true });
|
|
316
|
-
for (const entry of entries) {
|
|
317
|
-
if (!entry.isDirectory()) continue;
|
|
318
|
-
const agentId = entry.name;
|
|
319
|
-
const wsDir = path.join(workspacesDir, agentId);
|
|
320
|
-
await registerWorkspace(wsDir, agentId, userId);
|
|
321
|
-
}
|
|
322
|
-
} catch {
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
const watchTenantWorkspacesDir = (tenantDir, userId) => {
|
|
326
|
-
const workspacesDir = path.join(tenantDir, "workspaces");
|
|
327
|
-
try {
|
|
328
|
-
fs.watch(workspacesDir, async (_eventType, filename) => {
|
|
329
|
-
if (!filename) return;
|
|
330
|
-
const wsDir = path.join(workspacesDir, filename);
|
|
331
|
-
try {
|
|
332
|
-
const stat = await fs.promises.stat(wsDir);
|
|
333
|
-
if (!stat.isDirectory()) return;
|
|
334
|
-
const realDir = await fs.promises.realpath(wsDir).catch(() => wsDir);
|
|
335
|
-
if (registeredRealPaths.has(realDir)) return;
|
|
336
|
-
api.logger.info(
|
|
337
|
-
`memory-gateway-sync: [tenants] 发现新 workspace → ${userId}/${filename}`
|
|
338
|
-
);
|
|
339
|
-
await registerWorkspace(wsDir, filename, userId);
|
|
340
|
-
} catch {
|
|
341
|
-
}
|
|
342
|
-
});
|
|
343
|
-
} catch {
|
|
344
|
-
}
|
|
345
|
-
};
|
|
346
366
|
const scanTenants = async () => {
|
|
347
367
|
try {
|
|
348
368
|
const entries = await fs.promises.readdir(tenantsDir, { withFileTypes: true });
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -295,10 +295,53 @@ export default definePluginEntry({
|
|
|
295
295
|
);
|
|
296
296
|
};
|
|
297
297
|
|
|
298
|
+
// ── Tenants 模式:扫描 {tenantsDir}/{userId}/workspace-{name}/ ──
|
|
299
|
+
|
|
300
|
+
const scanTenantWorkspaces = async (tenantDir: string, userId: string): Promise<void> => {
|
|
301
|
+
try {
|
|
302
|
+
const entries = await fs.promises.readdir(tenantDir, { withFileTypes: true });
|
|
303
|
+
for (const entry of entries) {
|
|
304
|
+
if (!entry.isDirectory()) continue;
|
|
305
|
+
const name = entry.name;
|
|
306
|
+
if (name !== "workspace" && !name.startsWith("workspace-")) continue;
|
|
307
|
+
const wsDir = path.join(tenantDir, name);
|
|
308
|
+
const wsAgentId = resolveAgentId(name);
|
|
309
|
+
await registerWorkspace(wsDir, wsAgentId, userId);
|
|
310
|
+
}
|
|
311
|
+
} catch {
|
|
312
|
+
// tenant 目录不存在或不可读,跳过
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const watchTenantWorkspacesDir = (tenantDir: string, userId: string): void => {
|
|
317
|
+
try {
|
|
318
|
+
fs.watch(tenantDir, async (_eventType, filename) => {
|
|
319
|
+
if (!filename) return;
|
|
320
|
+
if (filename !== "workspace" && !filename.startsWith("workspace-")) return;
|
|
321
|
+
const wsDir = path.join(tenantDir, filename);
|
|
322
|
+
try {
|
|
323
|
+
const stat = await fs.promises.stat(wsDir);
|
|
324
|
+
if (!stat.isDirectory()) return;
|
|
325
|
+
const realDir = await fs.promises.realpath(wsDir).catch(() => wsDir);
|
|
326
|
+
if (registeredRealPaths.has(realDir)) return;
|
|
327
|
+
const wsAgentId = resolveAgentId(filename);
|
|
328
|
+
api.logger.info(
|
|
329
|
+
`memory-gateway-sync: [tenants] 发现新 workspace → ${userId}/${filename} (agentId=${wsAgentId})`
|
|
330
|
+
);
|
|
331
|
+
await registerWorkspace(wsDir, wsAgentId, userId);
|
|
332
|
+
} catch {
|
|
333
|
+
// 目录不存在或已删除
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
} catch {
|
|
337
|
+
// tenant 目录无法监听
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
298
341
|
// ── 扫描所有 workspace 目录 ──────────────────────────────────────
|
|
299
342
|
|
|
300
343
|
const scanWorkspaces = async (): Promise<void> => {
|
|
301
|
-
// 1. 扫描 openclawDir(处理 symlink
|
|
344
|
+
// 1. 扫描 openclawDir(处理 symlink、物理目录、user-* 子目录)
|
|
302
345
|
try {
|
|
303
346
|
const entries = await fs.promises.readdir(openclawDir, {
|
|
304
347
|
withFileTypes: true,
|
|
@@ -306,6 +349,16 @@ export default definePluginEntry({
|
|
|
306
349
|
for (const entry of entries) {
|
|
307
350
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
308
351
|
const name = entry.name;
|
|
352
|
+
|
|
353
|
+
// 自动发现 user-* 子目录,作为 tenant 处理
|
|
354
|
+
if (name.startsWith("user-")) {
|
|
355
|
+
const tenantDir = path.join(openclawDir, name);
|
|
356
|
+
const userId = name;
|
|
357
|
+
await scanTenantWorkspaces(tenantDir, userId);
|
|
358
|
+
watchTenantWorkspacesDir(tenantDir, userId);
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
|
|
309
362
|
if (name !== "workspace" && !name.startsWith("workspace-")) continue;
|
|
310
363
|
|
|
311
364
|
const wsDir = path.join(openclawDir, name);
|
|
@@ -360,6 +413,24 @@ export default definePluginEntry({
|
|
|
360
413
|
try {
|
|
361
414
|
fs.watch(dir, async (eventType, filename) => {
|
|
362
415
|
if (!filename) return;
|
|
416
|
+
|
|
417
|
+
// 动态感知新 user-* 子目录
|
|
418
|
+
if (dir === openclawDir && filename.startsWith("user-")) {
|
|
419
|
+
const tenantDir = path.join(openclawDir, filename);
|
|
420
|
+
try {
|
|
421
|
+
const stat = await fs.promises.stat(tenantDir);
|
|
422
|
+
if (!stat.isDirectory()) return;
|
|
423
|
+
api.logger.info(
|
|
424
|
+
`memory-gateway-sync: 发现新 tenant → ${filename}`
|
|
425
|
+
);
|
|
426
|
+
await scanTenantWorkspaces(tenantDir, filename);
|
|
427
|
+
watchTenantWorkspacesDir(tenantDir, filename);
|
|
428
|
+
} catch {
|
|
429
|
+
// 目录不存在或已删除
|
|
430
|
+
}
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
|
|
363
434
|
if (
|
|
364
435
|
filename !== "workspace" &&
|
|
365
436
|
!filename.startsWith("workspace-")
|
|
@@ -399,47 +470,6 @@ export default definePluginEntry({
|
|
|
399
470
|
}
|
|
400
471
|
};
|
|
401
472
|
|
|
402
|
-
// ── Tenants 模式:扫描 {tenantsDir}/{userId}/workspaces/{agentId}/ ──
|
|
403
|
-
|
|
404
|
-
const scanTenantWorkspaces = async (tenantDir: string, userId: string): Promise<void> => {
|
|
405
|
-
const workspacesDir = path.join(tenantDir, "workspaces");
|
|
406
|
-
try {
|
|
407
|
-
const entries = await fs.promises.readdir(workspacesDir, { withFileTypes: true });
|
|
408
|
-
for (const entry of entries) {
|
|
409
|
-
if (!entry.isDirectory()) continue;
|
|
410
|
-
const agentId = entry.name;
|
|
411
|
-
const wsDir = path.join(workspacesDir, agentId);
|
|
412
|
-
await registerWorkspace(wsDir, agentId, userId);
|
|
413
|
-
}
|
|
414
|
-
} catch {
|
|
415
|
-
// workspaces 目录不存在,跳过
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
const watchTenantWorkspacesDir = (tenantDir: string, userId: string): void => {
|
|
420
|
-
const workspacesDir = path.join(tenantDir, "workspaces");
|
|
421
|
-
try {
|
|
422
|
-
fs.watch(workspacesDir, async (_eventType, filename) => {
|
|
423
|
-
if (!filename) return;
|
|
424
|
-
const wsDir = path.join(workspacesDir, filename);
|
|
425
|
-
try {
|
|
426
|
-
const stat = await fs.promises.stat(wsDir);
|
|
427
|
-
if (!stat.isDirectory()) return;
|
|
428
|
-
const realDir = await fs.promises.realpath(wsDir).catch(() => wsDir);
|
|
429
|
-
if (registeredRealPaths.has(realDir)) return;
|
|
430
|
-
api.logger.info(
|
|
431
|
-
`memory-gateway-sync: [tenants] 发现新 workspace → ${userId}/${filename}`
|
|
432
|
-
);
|
|
433
|
-
await registerWorkspace(wsDir, filename, userId);
|
|
434
|
-
} catch {
|
|
435
|
-
// 目录不存在或已删除
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
} catch {
|
|
439
|
-
// workspaces 目录不存在,无法监听
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
|
-
|
|
443
473
|
const scanTenants = async (): Promise<void> => {
|
|
444
474
|
try {
|
|
445
475
|
const entries = await fs.promises.readdir(tenantsDir, { withFileTypes: true });
|