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 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 });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "memory-gateway-sync",
3
- "version": "0.12.0",
3
+ "version": "0.14.2",
4
4
  "name": "Memory Gateway Sync",
5
5
  "description": "监听 MEMORY.md / memory/*.md 文件变化,双写到外部 Gateway",
6
6
  "kind": "generic",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memory-gateway-sync",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "description": "Memory 双写插件:fs.watch 感知变化后同步到外部 Gateway",
5
5
  "type": "module",
6
6
  "main": "index.js",
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 });