@alfe.ai/openclaw-sync 0.3.7 → 0.3.8

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.
@@ -44,6 +44,16 @@ shared/**
44
44
  **/Crashpad/**
45
45
  **/ShaderCache/**
46
46
  **/component_crx_cache/**
47
+ # Alfe headless-browser profile (packages/openclaw-remote points Chromium's
48
+ # userDataDir at `<workspace>/.alfe-browser-profile`). Same live-WAL-SQLite
49
+ # corruption hazard as the runtime-home `browser/` dir — Cookies / History /
50
+ # Web Data / Login Data are live databases — plus hundreds of high-churn cache
51
+ # files that exhaust the inotify watch limit. The `**/Cache/**` entries above
52
+ # only drop the profile's sub-caches; this drops the WHOLE profile (incl. the
53
+ # live SQLite at its root), which is ephemeral runtime state with zero
54
+ # cross-rebuild restore value (the browser relaunches fresh). `**/`-anchored so
55
+ # it matches wherever the profile is created under the workspace.
56
+ **/.alfe-browser-profile/**
47
57
  **/__pycache__/**
48
58
  **/*.pyc
49
59
  **/*.log
package/dist/plugin.d.cts CHANGED
@@ -42,8 +42,6 @@ interface PluginApi {
42
42
  interface SyncPluginConfig {
43
43
  /** Workspace path to sync. Defaults to the resolved Alfe workspace. */
44
44
  workspacePath?: string;
45
- /** Sync scope — which data categories to sync. */
46
- syncScope?: ('config' | 'conversations' | 'memory')[];
47
45
  /** Sync schedule — how often to auto-sync. */
48
46
  syncSchedule?: 'realtime' | 'hourly' | 'daily' | 'weekly';
49
47
  /** IPC socket path override. */
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.cts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;AAkEsB;;;;;AAWA;;;;;;;;;;AAqCtB,UAhDU,YAAA,CAgDuB;EA2jB3B,IAAA,CAAA,GAAA,EAoWL,MAAA,CAAA,EAAA,IAAA;EAAA,IAAA,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;OA7Ve,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;OAwQQ,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;;UAn3Bd,oBAAA,CA25Ba;QAAmB,EA15BhC,MA05BgC,CAAA,MAAA,EAAA,OAAA,CAAA;cAAgB,CAAA,EAAA,MAAA;EAAA,QAAA,EAAA,MAAA;UAv5BhD;;UAGA,SAAA;UACA;;WAEC;wDAC6C;;;iBAGvC,gCAAgC;iBAChC,gCAAgC;;;UA0BhC,gBAAA;;;;;;;;;;;;;;cA2jBX;;;;;gBAOU;kBAwQQ,YAAS;iBAwCV,mBAAmB,mBAAgB"}
1
+ {"version":3,"file":"plugin.d.cts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;AAkEsB;;;;;AAWA;;;;;;;;;;AAqCtB,UAhDU,YAAA,CAgDuB;EAyjB3B,IAAA,CAAA,GAAA,EAiWL,MAAA,CAAA,EAAA,IAAA;EAAA,IAAA,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;OA1Ve,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;OAyQQ,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;;UAl3Bd,oBAAA,CA05Ba;QAAmB,EAz5BhC,MAy5BgC,CAAA,MAAA,EAAA,OAAA,CAAA;cAAgB,CAAA,EAAA,MAAA;EAAA,QAAA,EAAA,MAAA;UAt5BhD;;UAGA,SAAA;UACA;;WAEC;wDAC6C;;;iBAGvC,gCAAgC;iBAChC,gCAAgC;;;UA0BhC,gBAAA;;;;;;;;;;;;cAyjBX;;;;;gBAOU;kBAyQQ,YAAS;iBAwCV,mBAAmB,mBAAgB"}
package/dist/plugin.d.ts CHANGED
@@ -42,8 +42,6 @@ interface PluginApi {
42
42
  interface SyncPluginConfig {
43
43
  /** Workspace path to sync. Defaults to the resolved Alfe workspace. */
44
44
  workspacePath?: string;
45
- /** Sync scope — which data categories to sync. */
46
- syncScope?: ('config' | 'conversations' | 'memory')[];
47
45
  /** Sync schedule — how often to auto-sync. */
48
46
  syncSchedule?: 'realtime' | 'hourly' | 'daily' | 'weekly';
49
47
  /** IPC socket path override. */
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;AAkEsB;;;;;AAWA;;;;;;;;;;AAqCtB,UAhDU,YAAA,CAgDuB;EA2jB3B,IAAA,CAAA,GAAA,EAoWL,MAAA,CAAA,EAAA,IAAA;EAAA,IAAA,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;OA7Ve,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;OAwQQ,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;;UAn3Bd,oBAAA,CA25Ba;QAAmB,EA15BhC,MA05BgC,CAAA,MAAA,EAAA,OAAA,CAAA;cAAgB,CAAA,EAAA,MAAA;EAAA,QAAA,EAAA,MAAA;UAv5BhD;;UAGA,SAAA;UACA;;WAEC;wDAC6C;;;iBAGvC,gCAAgC;iBAChC,gCAAgC;;;UA0BhC,gBAAA;;;;;;;;;;;;;;cA2jBX;;;;;gBAOU;kBAwQQ,YAAS;iBAwCV,mBAAmB,mBAAgB"}
1
+ {"version":3,"file":"plugin.d.ts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;AAkEsB;;;;;AAWA;;;;;;;;;;AAqCtB,UAhDU,YAAA,CAgDuB;EAyjB3B,IAAA,CAAA,GAAA,EAiWL,MAAA,CAAA,EAAA,IAAA;EAAA,IAAA,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;OA1Ve,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;OAyQQ,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;;UAl3Bd,oBAAA,CA05Ba;QAAmB,EAz5BhC,MAy5BgC,CAAA,MAAA,EAAA,OAAA,CAAA;cAAgB,CAAA,EAAA,MAAA;EAAA,QAAA,EAAA,MAAA;UAt5BhD;;UAGA,SAAA;UACA;;WAEC;wDAC6C;;;iBAGvC,gCAAgC;iBAChC,gCAAgC;;;UA0BhC,gBAAA;;;;;;;;;;;;cAyjBX;;;;;gBAOU;kBAyQQ,YAAS;iBAwCV,mBAAmB,mBAAgB"}
package/dist/plugin2.cjs CHANGED
@@ -811,17 +811,11 @@ const plugin = {
811
811
  } catch {}
812
812
  const workspacePath = pluginConfig.workspacePath ?? alfeConfig?.workspacePath ?? _alfe_ai_config.DEFAULT_WORKSPACE_PATH;
813
813
  const runtime = alfeConfig?.runtime ?? "openclaw";
814
- const syncScope = pluginConfig.syncScope ?? [
815
- "config",
816
- "conversations",
817
- "memory"
818
- ];
819
814
  const syncSchedule = pluginConfig.syncSchedule ?? "realtime";
820
815
  const socketPath = pluginConfig.socketPath ?? alfeConfig?.socketPath ?? _alfe_ai_config.DEFAULT_SOCKET_PATH;
821
816
  const startSyncService = () => {
822
817
  (0, _alfe_ai_openclaw_plugin_kit.guardedStart)(SYNC_ACTIVATION_KEY, log, async () => {
823
818
  log.info("Alfe Sync plugin activating...");
824
- log.info(`Sync scope: ${syncScope.join(", ")}`);
825
819
  log.info(`Sync schedule: ${syncSchedule}`);
826
820
  log.info(`Workspace: ${workspacePath}`);
827
821
  if (!(0, _alfe_ai_config.configExists)()) {
@@ -972,11 +966,6 @@ const plugin = {
972
966
  initialized: !!syncEngine,
973
967
  agentId,
974
968
  schedule: currentConfig.syncSchedule ?? "realtime",
975
- scope: currentConfig.syncScope ?? [
976
- "config",
977
- "conversations",
978
- "memory"
979
- ],
980
969
  lastResult: lastSyncResult,
981
970
  watcherActive: !!stopWatcher
982
971
  }));
@@ -1062,7 +1051,6 @@ const plugin = {
1062
1051
  setupSchedule(config.syncSchedule, log);
1063
1052
  }
1064
1053
  }
1065
- if (config.syncScope) log.info(`Updated sync scope: ${config.syncScope.join(", ")}`);
1066
1054
  log.info("Alfe Sync plugin reconfigured");
1067
1055
  }
1068
1056
  };
package/dist/plugin2.js CHANGED
@@ -811,17 +811,11 @@ const plugin = {
811
811
  } catch {}
812
812
  const workspacePath = pluginConfig.workspacePath ?? alfeConfig?.workspacePath ?? DEFAULT_WORKSPACE_PATH;
813
813
  const runtime = alfeConfig?.runtime ?? "openclaw";
814
- const syncScope = pluginConfig.syncScope ?? [
815
- "config",
816
- "conversations",
817
- "memory"
818
- ];
819
814
  const syncSchedule = pluginConfig.syncSchedule ?? "realtime";
820
815
  const socketPath = pluginConfig.socketPath ?? alfeConfig?.socketPath ?? DEFAULT_SOCKET_PATH;
821
816
  const startSyncService = () => {
822
817
  guardedStart(SYNC_ACTIVATION_KEY, log, async () => {
823
818
  log.info("Alfe Sync plugin activating...");
824
- log.info(`Sync scope: ${syncScope.join(", ")}`);
825
819
  log.info(`Sync schedule: ${syncSchedule}`);
826
820
  log.info(`Workspace: ${workspacePath}`);
827
821
  if (!configExists()) {
@@ -972,11 +966,6 @@ const plugin = {
972
966
  initialized: !!syncEngine,
973
967
  agentId,
974
968
  schedule: currentConfig.syncSchedule ?? "realtime",
975
- scope: currentConfig.syncScope ?? [
976
- "config",
977
- "conversations",
978
- "memory"
979
- ],
980
969
  lastResult: lastSyncResult,
981
970
  watcherActive: !!stopWatcher
982
971
  }));
@@ -1062,7 +1051,6 @@ const plugin = {
1062
1051
  setupSchedule(config.syncSchedule, log);
1063
1052
  }
1064
1053
  }
1065
- if (config.syncScope) log.info(`Updated sync scope: ${config.syncScope.join(", ")}`);
1066
1054
  log.info("Alfe Sync plugin reconfigured");
1067
1055
  }
1068
1056
  };
@@ -1 +1 @@
1
- {"version":3,"file":"plugin2.js","names":["resolveAlfeConfig","alfeConfigExists"],"sources":["../src/watcher.ts","../src/shared-sync.ts","../src/delete-brake.ts","../src/plugin.ts"],"sourcesContent":["/**\n * AlfeSync watcher — recursive file watcher with debounce and ignore support.\n *\n * Uses chokidar to watch the workspace root, debounces per-file changes\n * by 2 seconds, and emits batches of changed paths.\n */\n\nimport { watch } from \"chokidar\";\nimport { relative } from \"node:path\";\nimport { createLogger } from \"@auriclabs/logger\";\nimport { loadIgnorePatterns, shouldIgnore, shouldIgnoreDir } from \"./ignore.js\";\n\nconst WORKSPACE_IGNORE_FILE = \".alfesyncignore\";\n\nconst log = createLogger(\"SyncWatcher\");\n\nexport interface WatcherOptions {\n workspacePath: string;\n /**\n * Active agent runtime — selects the per-runtime default ignore set so the\n * watcher never descends into runtime-internal dirs (openclaw `npm/`,\n * hermes `.venv/`, …). Default: \"openclaw\".\n */\n runtime?: string;\n /** Debounce delay per file in milliseconds. Default: 2000 */\n debounceMs?: number;\n /** Callback invoked with batches of changed relative paths */\n onChanges: (paths: string[]) => void | Promise<void>;\n}\n\n/**\n * Start watching a workspace for file changes.\n *\n * Returns a cleanup function to stop watching.\n */\nexport async function startWatcher(\n options: WatcherOptions,\n): Promise<() => Promise<void>> {\n const { workspacePath, runtime = \"openclaw\", debounceMs = 2000, onChanges } = options;\n let ignorePatterns = await loadIgnorePatterns(workspacePath, runtime);\n let stopped = false;\n\n // Pending changes map: relativePath → timeout handle\n const pending = new Map<string, ReturnType<typeof setTimeout>>();\n // Batch accumulator for flushing\n let batchPaths = new Set<string>();\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n function scheduleBatch() {\n if (flushTimer) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n if (batchPaths.size === 0) return;\n\n const paths = [...batchPaths];\n batchPaths = new Set();\n void Promise.resolve(onChanges(paths)).catch((error: unknown) => {\n log.error({ err: error }, \"Sync watcher change handler failed\");\n });\n }, debounceMs);\n }\n\n function queueChange(relativePath: string) {\n // Skip ignored files\n if (shouldIgnore(relativePath, ignorePatterns)) return;\n\n // Clear existing timer for this file\n const existingTimer = pending.get(relativePath);\n if (existingTimer) clearTimeout(existingTimer);\n\n // Debounce: wait before adding to batch\n const timer = setTimeout(() => {\n pending.delete(relativePath);\n batchPaths.add(relativePath);\n scheduleBatch();\n }, debounceMs);\n\n pending.set(relativePath, timer);\n }\n\n const watcher = watch(workspacePath, {\n persistent: true,\n ignoreInitial: true,\n followSymlinks: false,\n depth: undefined, // unlimited depth\n // chokidar 4 dropped glob support in `ignored` — a string-glob array is\n // treated as literal paths and matches nothing, so the watcher would\n // create an inotify watch on every dir (incl. node_modules / the runtime's\n // npm cache) and exhaust the OS watch limit → ENOSPC crash-loop. Use a\n // predicate driven by the SAME ignore engine as event filtering; returning\n // true for a directory stops chokidar descending into it.\n // chokidar invokes this for files AND directories, twice per entry: first\n // with no stats (path check), then with stats before it actually watches.\n // Match ignored files directly; prune ignored directories (whose contents\n // are ignored) via the sentinel-child probe. When stats say it's a FILE,\n // skip the dir probe so a file literally named like an ignored dir (e.g. an\n // extensionless `dist`) isn't wrongly ignored — keeps parity with the walk,\n // which only dir-prunes real directories.\n ignored: (p: string, stats?: { isDirectory(): boolean }) => {\n const rel = relative(workspacePath, p).replace(/\\\\/g, \"/\");\n if (rel === \"\") return false; // never ignore the workspace root\n if (shouldIgnore(rel, ignorePatterns)) return true;\n if (stats && !stats.isDirectory()) return false;\n return shouldIgnoreDir(rel, ignorePatterns);\n },\n });\n\n // Reloads are serialized so a rapid editor save sequence cannot publish an\n // older ruleset after a newer one. Re-subscribing the root is important:\n // changing only the predicate would not make chokidar descend into a\n // directory that the previous rules pruned at startup.\n let ignoreReloadTail = Promise.resolve();\n\n function handleChange(absolutePath: string) {\n // Posix-normalise so the globs (which are posix-style) match on Windows\n // self-hosted agents too — parity with the `ignored` predicate below.\n const relativePath = relative(workspacePath, absolutePath).replace(/\\\\/g, \"/\");\n\n if (relativePath === WORKSPACE_IGNORE_FILE) {\n ignoreReloadTail = ignoreReloadTail\n .then(async () => {\n const nextRules = await loadIgnorePatterns(workspacePath, runtime);\n if (stopped) return;\n ignorePatterns = nextRules;\n watcher.unwatch(workspacePath);\n watcher.add(workspacePath);\n queueChange(relativePath);\n })\n .catch((error: unknown) => {\n log.error({ err: error }, \"Failed to reload sync ignore rules\");\n });\n return;\n }\n\n queueChange(relativePath);\n }\n\n watcher.on(\"add\", handleChange);\n watcher.on(\"change\", handleChange);\n watcher.on(\"unlink\", handleChange);\n watcher.on(\"error\", (error) => {\n log.error({ err: error }, \"Sync watcher failed\");\n });\n\n // Return cleanup function\n return async () => {\n stopped = true;\n\n // Clear all pending timers\n for (const timer of pending.values()) {\n clearTimeout(timer);\n }\n pending.clear();\n\n if (flushTimer) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n batchPaths.clear();\n\n await ignoreReloadTail;\n await watcher.close();\n };\n}\n","/**\n * Shared file sync — mirrors org/team/project files to a `shared/`\n * directory in the agent's workspace, organised by scope:\n *\n * shared/org/<files…>\n * shared/teams/<scopeId>/<files…>\n * shared/projects/<scopeId>/<files…>\n *\n * Backed by the agent self-service API (`AgentApiClient.sharedListFiles`,\n * `AgentApiClient.sharedDownloadUrl`).\n */\n\nimport { join, dirname, isAbsolute, normalize, sep } from \"node:path\";\nimport { lstat, mkdir, readdir, rmdir, writeFile, unlink, rm } from \"node:fs/promises\";\nimport type { AgentApiClient, SharedFileEntry } from \"@alfe.ai/agent-api-client\";\n\nconst MAX_SHARED_FILE_SIZE = 100 * 1024 * 1024; // 100 MB\nconst SHARED_LIST_PAGE_SIZE = 500;\nconst MAX_SHARED_LIST_PAGES = 100;\n\nfunction validateScope(scope: SharedScope): SharedScope {\n const candidate = scope as Partial<SharedScope> | null;\n if (\n candidate === null ||\n (candidate.scopeType !== \"org\" &&\n candidate.scopeType !== \"team\" &&\n candidate.scopeType !== \"project\") ||\n typeof candidate.scopeId !== \"string\" ||\n candidate.scopeId.length === 0 ||\n candidate.scopeId.length > 512 ||\n candidate.scopeId === \".\" ||\n candidate.scopeId === \"..\" ||\n candidate.scopeId.includes(\"/\") ||\n candidate.scopeId.includes(\"\\\\\") ||\n candidate.scopeId.includes(\"\\0\") ||\n typeof candidate.name !== \"string\" ||\n candidate.name.length > 512\n ) {\n throw new Error(\"Invalid shared sync scope\");\n }\n return candidate as SharedScope;\n}\n\nfunction validateSharedFilePath(filePath: string): string {\n if (\n filePath.length === 0 ||\n filePath.length > 1024 ||\n isAbsolute(filePath) ||\n filePath.includes(\"\\\\\") ||\n filePath.includes(\"\\0\")\n ) {\n throw new Error(\"Invalid shared sync file path\");\n }\n const segments = filePath.split(\"/\");\n if (segments.some((segment) => segment === \"\" || segment === \".\" || segment === \"..\")) {\n throw new Error(\"Invalid shared sync file path\");\n }\n return segments.join(\"/\");\n}\n\n/** Throw if `resolvedPath` would escape `baseDir`. */\nfunction assertContained(baseDir: string, resolvedPath: string): void {\n const normalizedBase = normalize(baseDir) + sep;\n const normalizedPath = normalize(resolvedPath);\n if (\n !normalizedPath.startsWith(normalizedBase) &&\n normalizedPath !== normalize(baseDir)\n ) {\n throw new Error(`Path traversal blocked: ${resolvedPath} escapes ${baseDir}`);\n }\n}\n\nasync function assertNoSymlinkTraversal(\n baseDir: string,\n relativePath: string,\n): Promise<string> {\n const canonical = validateSharedFilePath(relativePath);\n const resolvedPath = join(baseDir, canonical);\n assertContained(baseDir, resolvedPath);\n let current = normalize(baseDir);\n for (const segment of canonical.split(\"/\")) {\n current = join(current, segment);\n try {\n const entry = await lstat(current);\n if (entry.isSymbolicLink()) throw new Error(\"Shared sync path crosses a symbolic link\");\n } catch (error) {\n if (\n error instanceof Error &&\n \"code\" in error &&\n (error as NodeJS.ErrnoException).code === \"ENOENT\"\n ) {\n break;\n }\n throw error;\n }\n }\n return resolvedPath;\n}\n\nexport interface SharedScope {\n scopeType: \"team\" | \"project\" | \"org\";\n scopeId: string;\n name: string;\n}\n\nexport interface SharedSyncConfig {\n workspacePath: string;\n client: AgentApiClient;\n}\n\ninterface PluginLogger {\n info(msg: string): void;\n warn(msg: string): void;\n error(msg: string): void;\n debug(msg: string): void;\n}\n\nexport interface SharedSyncEngine {\n initialize(scopes: SharedScope[]): Promise<void>;\n updateScopes(scopes: SharedScope[]): Promise<void>;\n handleNotification(filePath: string, eventType: \"created\" | \"deleted\"): Promise<void>;\n fullSync(): Promise<void>;\n getScopes(): SharedScope[];\n}\n\nexport function createSharedSyncEngine(\n config: SharedSyncConfig,\n log: PluginLogger,\n): SharedSyncEngine {\n let activeScopes: SharedScope[] = [];\n const sharedDir = join(config.workspacePath, \"shared\");\n\n function scopeDir(scope: SharedScope): string {\n validateScope(scope);\n if (scope.scopeType === \"org\") return join(sharedDir, \"org\");\n const plural = scope.scopeType === \"team\" ? \"teams\" : \"projects\";\n return join(sharedDir, plural, scope.scopeId);\n }\n\n async function downloadFile(\n scope: SharedScope,\n filePath: string,\n localPath: string,\n ): Promise<void> {\n const canonicalPath = validateSharedFilePath(filePath);\n const safeLocalPath = await assertNoSymlinkTraversal(scopeDir(scope), canonicalPath);\n if (safeLocalPath !== normalize(localPath)) {\n throw new Error(\"Shared sync local path does not match its canonical path\");\n }\n\n const { downloadUrl } = await config.client.sharedDownloadUrl({\n scope: scope.scopeType,\n scopeId: scope.scopeId,\n filePath: canonicalPath,\n });\n\n // Presigned URL → S3 directly (raw fetch is the right tool here).\n const response = await fetch(downloadUrl);\n if (!response.ok) {\n throw new Error(`Download failed: HTTP ${String(response.status)}`);\n }\n\n const contentLength = parseInt(\n response.headers.get(\"content-length\") ?? \"0\",\n 10,\n );\n if (contentLength > MAX_SHARED_FILE_SIZE) {\n throw new Error(\n `File too large: ${String(contentLength)} bytes exceeds ${String(MAX_SHARED_FILE_SIZE)} limit`,\n );\n }\n\n const buffer = Buffer.from(await response.arrayBuffer());\n if (buffer.length > MAX_SHARED_FILE_SIZE) {\n throw new Error(`Downloaded file exceeds size limit: ${String(buffer.length)} bytes`);\n }\n\n await mkdir(dirname(localPath), { recursive: true });\n await writeFile(localPath, buffer);\n }\n\n async function listRemoteFiles(scope: SharedScope): Promise<SharedFileEntry[]> {\n const files = new Map<string, SharedFileEntry>();\n const seenCursors = new Set<string>();\n let cursor: string | undefined;\n\n for (let page = 0; page < MAX_SHARED_LIST_PAGES; page++) {\n const response = await config.client.sharedListFiles({\n scope: scope.scopeType,\n scopeId: scope.scopeId,\n limit: SHARED_LIST_PAGE_SIZE,\n ...(cursor ? { cursor } : {}),\n });\n for (const file of response.files) {\n const filePath = validateSharedFilePath(file.filePath);\n files.set(filePath, { ...file, filePath });\n }\n\n if (!response.nextCursor) {\n const paths = new Set(files.keys());\n for (const filePath of paths) {\n const segments = filePath.split(\"/\");\n for (let index = 1; index < segments.length; index++) {\n if (paths.has(segments.slice(0, index).join(\"/\"))) {\n throw new Error(\"Shared file listing contains a file/directory collision\");\n }\n }\n }\n return [...files.values()];\n }\n if (seenCursors.has(response.nextCursor)) {\n throw new Error(\"Shared file listing returned a repeated cursor\");\n }\n seenCursors.add(response.nextCursor);\n cursor = response.nextCursor;\n }\n\n throw new Error(\n `Shared file listing exceeded ${String(MAX_SHARED_LIST_PAGES)} pages`,\n );\n }\n\n async function pruneLocalMirror(\n dir: string,\n remotePaths: ReadonlySet<string>,\n ): Promise<void> {\n const remoteDirectories = new Set<string>();\n for (const filePath of remotePaths) {\n const segments = filePath.split(\"/\");\n for (let index = 1; index < segments.length; index++) {\n remoteDirectories.add(segments.slice(0, index).join(\"/\"));\n }\n }\n\n const walk = async (currentDir: string, prefix: string): Promise<void> => {\n const entries = await readdir(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;\n const fullPath = join(currentDir, entry.name);\n let canonicalPath: string | null = null;\n try {\n canonicalPath = validateSharedFilePath(relativePath);\n } catch {\n // Invalid local entries cannot be part of the canonical mirror.\n }\n\n if (entry.isSymbolicLink()) {\n await unlink(fullPath);\n log.warn(`Shared sync: removed symbolic link ${relativePath}`);\n continue;\n }\n\n if (entry.isDirectory()) {\n if (!canonicalPath) {\n await rm(fullPath, { recursive: true, force: true });\n continue;\n }\n await walk(fullPath, canonicalPath);\n if (!remoteDirectories.has(canonicalPath)) {\n try {\n await rmdir(fullPath);\n } catch (error) {\n if (\n !(error instanceof Error &&\n \"code\" in error &&\n ((error as NodeJS.ErrnoException).code === \"ENOENT\" ||\n (error as NodeJS.ErrnoException).code === \"ENOTEMPTY\"))\n ) {\n throw error;\n }\n }\n }\n continue;\n }\n\n if (!canonicalPath || !remotePaths.has(canonicalPath)) {\n await unlink(fullPath);\n log.debug(`Shared sync: pruned stale local file ${relativePath}`);\n }\n }\n };\n\n await walk(dir, \"\");\n }\n\n async function syncScope(scope: SharedScope): Promise<void> {\n const dir = scopeDir(scope);\n await mkdir(dir, { recursive: true });\n\n const files = await listRemoteFiles(scope);\n const remotePaths = new Set(files.map((file) => file.filePath));\n await pruneLocalMirror(dir, remotePaths);\n\n for (const file of files) {\n try {\n const filePath = validateSharedFilePath(file.filePath);\n const localPath = join(dir, filePath);\n await downloadFile(scope, filePath, localPath);\n log.debug(\n `Shared sync: downloaded ${scope.scopeType}/${scope.scopeId}/${file.filePath}`,\n );\n } catch (err: unknown) {\n log.error(\n `Shared sync: failed to download ${file.filePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n function parseScopedPath(\n filePath: string,\n ): { scope: SharedScope; relativePath: string } | null {\n if (filePath.length > 2048 || filePath.includes(\"\\\\\") || filePath.includes(\"\\0\")) {\n return null;\n }\n\n const orgMatch = /^shared\\/org\\/(.+)$/.exec(filePath);\n if (orgMatch) {\n const scope = activeScopes.find((s) => s.scopeType === \"org\");\n if (scope) {\n try {\n return { scope, relativePath: validateSharedFilePath(orgMatch[1]) };\n } catch {\n return null;\n }\n }\n }\n\n const teamMatch = /^shared\\/teams\\/([^/]+)\\/(.+)$/.exec(filePath);\n if (teamMatch) {\n const scope = activeScopes.find(\n (s) => s.scopeType === \"team\" && s.scopeId === teamMatch[1],\n );\n if (scope) {\n try {\n return { scope, relativePath: validateSharedFilePath(teamMatch[2]) };\n } catch {\n return null;\n }\n }\n }\n\n const projectMatch = /^shared\\/projects\\/([^/]+)\\/(.+)$/.exec(filePath);\n if (projectMatch) {\n const scope = activeScopes.find(\n (s) => s.scopeType === \"project\" && s.scopeId === projectMatch[1],\n );\n if (scope) {\n try {\n return { scope, relativePath: validateSharedFilePath(projectMatch[2]) };\n } catch {\n return null;\n }\n }\n }\n\n return null;\n }\n\n return {\n async initialize(scopes: SharedScope[]): Promise<void> {\n activeScopes = scopes.map(validateScope);\n log.info(`Shared sync: initializing with ${String(scopes.length)} scope(s)`);\n await mkdir(sharedDir, { recursive: true });\n for (const scope of scopes) {\n await syncScope(scope);\n }\n log.info(\"Shared sync: initialization complete\");\n },\n\n async updateScopes(newScopes: SharedScope[]): Promise<void> {\n const validatedScopes = newScopes.map(validateScope);\n const oldIds = new Set(activeScopes.map((s) => `${s.scopeType}:${s.scopeId}`));\n const newIds = new Set(validatedScopes.map((s) => `${s.scopeType}:${s.scopeId}`));\n\n for (const scope of activeScopes) {\n const key = `${scope.scopeType}:${scope.scopeId}`;\n if (!newIds.has(key)) {\n const dir = scopeDir(scope);\n try {\n await rm(dir, { recursive: true, force: true });\n log.info(`Shared sync: removed scope directory ${dir}`);\n } catch (err: unknown) {\n log.warn(\n `Shared sync: failed to remove ${dir}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n for (const scope of validatedScopes) {\n const key = `${scope.scopeType}:${scope.scopeId}`;\n if (!oldIds.has(key)) {\n log.info(\n `Shared sync: new scope ${scope.scopeType}/${scope.scopeId} — syncing files`,\n );\n await syncScope(scope);\n }\n }\n\n activeScopes = [...validatedScopes];\n },\n\n async handleNotification(\n filePath: string,\n eventType: \"created\" | \"deleted\",\n ): Promise<void> {\n const parsed = parseScopedPath(filePath);\n if (!parsed) {\n log.debug(`Shared sync: ignoring notification for unknown path: ${filePath}`);\n return;\n }\n\n const dir = scopeDir(parsed.scope);\n const localPath = await assertNoSymlinkTraversal(dir, parsed.relativePath).catch(() => null);\n\n if (!localPath) {\n log.warn(`Shared sync: path traversal blocked for ${filePath}`);\n return;\n }\n\n if (eventType === \"deleted\") {\n try {\n await unlink(localPath);\n log.debug(`Shared sync: deleted ${filePath}`);\n } catch {\n // idempotent — file may not exist locally on out-of-order events\n }\n return;\n }\n\n try {\n await downloadFile(parsed.scope, parsed.relativePath, localPath);\n log.debug(`Shared sync: pulled ${filePath}`);\n } catch (err: unknown) {\n log.error(\n `Shared sync: failed to pull ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n },\n\n async fullSync(): Promise<void> {\n log.info(`Shared sync: full sync across ${String(activeScopes.length)} scope(s)`);\n for (const scope of activeScopes) {\n await syncScope(scope);\n }\n },\n\n getScopes(): SharedScope[] {\n return [...activeScopes];\n },\n };\n}\n","// Rolling-window rate brake for cloud delete propagation.\n//\n// The watcher's onChanges callback emits batches every ~2 s. A per-batch\n// brake (`if batch / manifestSize > 30%`) is trivial to slip on a real\n// `rm -rf` because chokidar dribbles deletes out 5-10 at a time. Instead\n// we sum deletes across the last 60 s and trip when the cumulative count\n// exceeds 30% of the manifest. Legitimate single-file deletes via the\n// agent stay nowhere near the threshold; a workspace unmount or\n// catastrophic delete rapidly accumulates and is stopped.\n//\n// Manifest size of 0 means the agent has nothing in the cloud yet —\n// can't be a disaster, so always allow.\n//\n// In-memory only. A plugin restart resets the window, which is acceptable:\n// a restart already implies operator awareness.\n\nconst DEFAULT_WINDOW_MS = 60_000;\nconst DEFAULT_THRESHOLD_RATIO = 0.3;\n\nexport interface DeleteBrakeOptions {\n windowMs?: number;\n thresholdRatio?: number;\n now?: () => number;\n}\n\nexport interface DeleteBrake {\n // Returns true if the proposed delete count is safe to proceed (and\n // records it). Returns false if combining it with the existing rolling\n // window would exceed the threshold — caller must not perform the\n // delete in that case.\n check(deleteCount: number, manifestSize: number): boolean;\n // Current sum of deletes within the active window (after expiry).\n windowSum(): number;\n}\n\nexport function createDeleteBrake(opts: DeleteBrakeOptions = {}): DeleteBrake {\n const windowMs = opts.windowMs ?? DEFAULT_WINDOW_MS;\n const thresholdRatio = opts.thresholdRatio ?? DEFAULT_THRESHOLD_RATIO;\n const now = opts.now ?? (() => Date.now());\n\n const records: { ts: number; count: number }[] = [];\n\n function expire(): void {\n const cutoff = now() - windowMs;\n while (records.length > 0 && records[0].ts < cutoff) records.shift();\n }\n\n function sum(): number {\n return records.reduce((acc, r) => acc + r.count, 0);\n }\n\n return {\n check(deleteCount, manifestSize) {\n if (deleteCount <= 0) return true;\n expire();\n // Fresh agent (no manifest entries) — nothing to lose, always allow.\n if (manifestSize === 0) {\n records.push({ ts: now(), count: deleteCount });\n return true;\n }\n const projected = sum() + deleteCount;\n if (projected / manifestSize > thresholdRatio) return false;\n records.push({ ts: now(), count: deleteCount });\n return true;\n },\n windowSum() {\n expire();\n return sum();\n },\n };\n}\n","/**\n * @alfe.ai/openclaw-sync — OpenClaw Sync plugin.\n *\n * Wraps the sync engine as a lifecycle-managed integration. Same shape as\n * @alfe.ai/openclaw-memory-cloud / -secrets / -google: one AgentApiClient\n * is constructed in `activate()` and reused for every API call. The agent's\n * identity is resolved server-side from the API key — the plugin never\n * touches `/auth/validate`, never plumbs an `agentId` around, never writes\n * a `.alfesync/` directory.\n *\n * Lifecycle:\n * - activate(api): construct client, start sync engine + watcher\n * - deactivate(api): stop watcher, drop relay connections, clean up\n * - configure(api, config): swap schedule/scope at runtime\n *\n * Registers gateway RPC methods: `sync.now`, `sync.status`.\n */\n\nimport { createRequire } from 'node:module';\nimport {\n resolveConfig as resolveAlfeConfig,\n configExists as alfeConfigExists,\n DEFAULT_SOCKET_PATH,\n DEFAULT_WORKSPACE_PATH,\n deriveServiceWsUrl,\n type ResolvedConfig as AlfeResolvedConfig,\n} from '@alfe.ai/config';\nimport {\n connectToDaemon,\n getActivationKey,\n guardedStart,\n resetActivation,\n type IpcClient,\n} from '@alfe.ai/openclaw-plugin-kit';\nimport { AgentApiClient, type SyncAgentInfo } from '@alfe.ai/agent-api-client';\n\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport {\n createSyncEngine,\n isRecoveryArtifact,\n type SyncEngine,\n type SyncResult,\n} from './sync-engine.js';\nimport { startWatcher } from './watcher.js';\nimport { createSharedSyncEngine, type SharedSyncEngine, type SharedScope } from './shared-sync.js';\nimport { createDeleteBrake, type DeleteBrake } from './delete-brake.js';\nimport { readManifest } from './manifest.js';\nimport { validatePrivateRelativePath } from './path-contract.js';\n\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json') as { version: string };\n\n// ── Constants ───────────────────────────────────────────────\nconst SYNC_CAPABILITIES = ['sync.push', 'sync.pull', 'sync.fullSync'] as const;\nconst SYNC_ACTIVATION_KEY = getActivationKey('sync');\nconst SYNC_RELAY_RECONNECT_BASE_MS = 1000;\nconst SYNC_RELAY_RECONNECT_MAX_MS = 30000;\nconst SYNC_RELAY_DEBOUNCE_MS = 500;\nconst SYNC_RELAY_MAX_MESSAGE_BYTES = 64 * 1024;\nconst SYNC_RELAY_HANDSHAKE_TIMEOUT_MS = 10_000;\nconst REMOTE_DELETE_SUPPRESSION_MS = 30_000;\n\n// ── Types ───────────────────────────────────────────────────\n\ninterface PluginLogger {\n info(msg: string): void;\n warn(msg: string): void;\n error(msg: string): void;\n debug(msg: string): void;\n}\n\ninterface PluginServiceContext {\n config: Record<string, unknown>;\n workspaceDir?: string;\n stateDir: string;\n logger: PluginLogger;\n}\n\ninterface PluginApi {\n logger: PluginLogger;\n registrationMode?: 'full' | 'setup-only' | 'setup-runtime' | 'cli-metadata';\n config?: SyncPluginConfig;\n registerGatewayMethod?: (name: string, handler: () => Promise<unknown>) => void;\n registerService(service: {\n id: string;\n start: (ctx: PluginServiceContext) => void | Promise<void>;\n stop?: (ctx: PluginServiceContext) => void | Promise<void>;\n }): void;\n}\n\ninterface SyncRelaySocket {\n on(event: string, handler: (...args: never[]) => void): void;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n}\n\ntype RelayMessage =\n | { type: 'PING' }\n | {\n type: 'SUBSCRIBE_ACK';\n status: 'ok' | 'error';\n agentId: string;\n message?: string;\n }\n | {\n type: 'FILE_CHANGED';\n agentId: string;\n filePath: string;\n etag?: string;\n eventType: 'created' | 'deleted';\n };\n\nexport interface SyncPluginConfig {\n /** Workspace path to sync. Defaults to the resolved Alfe workspace. */\n workspacePath?: string;\n /** Sync scope — which data categories to sync. */\n syncScope?: ('config' | 'conversations' | 'memory')[];\n /** Sync schedule — how often to auto-sync. */\n syncSchedule?: 'realtime' | 'hourly' | 'daily' | 'weekly';\n /** IPC socket path override. */\n socketPath?: string;\n /** Sync relay URL override (default: derived from apiUrl). */\n syncRelayUrl?: string;\n /** Enable shared file sync for org/team/project scopes. Default: true. */\n sharedSync?: boolean;\n}\n\n// ── Plugin State ────────────────────────────────────────────\n\nlet client: AgentApiClient | null = null;\nlet agentId: string | null = null;\nlet syncEngine: SyncEngine | null = null;\nlet sharedSyncEngine: SharedSyncEngine | null = null;\nlet stopWatcher: (() => Promise<void>) | null = null;\nlet deleteBrake: DeleteBrake | null = null;\nlet daemonIpcClient: IpcClient | null = null;\nlet scheduledInterval: ReturnType<typeof setInterval> | null = null;\nlet currentConfig: SyncPluginConfig = {};\nlet lastSyncResult: SyncResult | null = null;\nlet syncRelayWs: SyncRelaySocket | null = null;\nlet syncRelayReconnectTimer: ReturnType<typeof setTimeout> | null = null;\nlet syncRelayReconnectAttempt = 0;\nlet syncRelayDebounceTimer: ReturnType<typeof setTimeout> | null = null;\nlet syncRelayGeneration = 0;\nlet syncRelayActive = false;\nlet syncOperationTail: Promise<void> = Promise.resolve();\nconst syncRelayPendingPaths = new Map<\n string,\n { etag?: string; eventType: 'created' | 'deleted' }\n>();\nconst remoteDeleteSuppressions = new Map<string, number>();\n\nfunction enqueueSyncOperation<T>(operation: () => Promise<T>): Promise<T> {\n const result = syncOperationTail.then(operation, operation);\n syncOperationTail = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n}\n\nfunction parseRelayMessage(value: unknown): RelayMessage | null {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return null;\n const candidate = value as Record<string, unknown>;\n\n if (candidate.type === 'PING') return { type: 'PING' };\n\n if (\n candidate.type === 'SUBSCRIBE_ACK' &&\n (candidate.status === 'ok' || candidate.status === 'error') &&\n typeof candidate.agentId === 'string' &&\n candidate.agentId.length > 0 &&\n candidate.agentId.length <= 512 &&\n (candidate.message === undefined ||\n (typeof candidate.message === 'string' && candidate.message.length <= 1024))\n ) {\n return {\n type: 'SUBSCRIBE_ACK',\n status: candidate.status,\n agentId: candidate.agentId,\n ...(typeof candidate.message === 'string' ? { message: candidate.message } : {}),\n };\n }\n\n if (\n candidate.type === 'FILE_CHANGED' &&\n typeof candidate.agentId === 'string' &&\n candidate.agentId.length > 0 &&\n candidate.agentId.length <= 512 &&\n typeof candidate.filePath === 'string' &&\n (candidate.eventType === 'created' || candidate.eventType === 'deleted') &&\n (candidate.etag === undefined ||\n (typeof candidate.etag === 'string' && candidate.etag.length <= 256))\n ) {\n try {\n const sharedSegments = candidate.filePath.split('/');\n const isCanonicalSharedPath =\n sharedSegments[0] === 'shared' &&\n sharedSegments.length >= 3 &&\n !sharedSegments.some(\n (segment) => segment === '' || segment === '.' || segment === '..',\n );\n const filePath = isCanonicalSharedPath\n ? candidate.filePath\n : validatePrivateRelativePath(candidate.filePath);\n if (\n filePath.length > 1024 ||\n filePath.includes('\\0') ||\n filePath.includes('\\\\') ||\n ((filePath === 'shared' || filePath.startsWith('shared/')) && !isCanonicalSharedPath)\n ) {\n return null;\n }\n return {\n type: 'FILE_CHANGED',\n agentId: candidate.agentId,\n filePath,\n eventType: candidate.eventType,\n ...(typeof candidate.etag === 'string' ? { etag: candidate.etag } : {}),\n };\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\nfunction parseSharedScopes(value: unknown): SharedScope[] | null {\n if (!Array.isArray(value) || value.length > 1000) return null;\n const scopes: SharedScope[] = [];\n for (const item of value) {\n if (typeof item !== 'object' || item === null || Array.isArray(item)) return null;\n const candidate = item as Record<string, unknown>;\n if (\n (candidate.scopeType !== 'org' &&\n candidate.scopeType !== 'team' &&\n candidate.scopeType !== 'project') ||\n typeof candidate.scopeId !== 'string' ||\n candidate.scopeId.length === 0 ||\n candidate.scopeId.length > 512 ||\n candidate.scopeId === '.' ||\n candidate.scopeId === '..' ||\n candidate.scopeId.includes('/') ||\n candidate.scopeId.includes('\\\\') ||\n candidate.scopeId.includes('\\0') ||\n typeof candidate.name !== 'string' ||\n candidate.name.length > 512\n ) {\n return null;\n }\n scopes.push({\n scopeType: candidate.scopeType,\n scopeId: candidate.scopeId,\n name: candidate.name,\n });\n }\n return scopes;\n}\n\nasync function runFullSync(\n engine: SyncEngine,\n log: PluginLogger,\n label: string,\n): Promise<SyncResult | null> {\n return enqueueSyncOperation(async () => {\n if (syncEngine !== engine) return null;\n try {\n const result = await engine.fullSync({ quiet: true });\n lastSyncResult = result;\n log.info(\n `${label} complete: ${String(result.pushed)} pushed, ${String(result.pulled)} pulled`,\n );\n return result;\n } catch (err: unknown) {\n log.error(`${label} failed: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n });\n}\n\nasync function handleRealtimeChanges(\n paths: string[],\n workspacePath: string,\n log: PluginLogger,\n): Promise<void> {\n const engine = syncEngine;\n const brake = deleteBrake;\n if (!engine || !brake) return;\n\n await enqueueSyncOperation(async () => {\n if (syncEngine !== engine || deleteBrake !== brake) return;\n const existing: string[] = [];\n const missing: string[] = [];\n const now = Date.now();\n\n for (const [suppressedPath, expiresAt] of remoteDeleteSuppressions) {\n if (expiresAt <= now) remoteDeleteSuppressions.delete(suppressedPath);\n }\n\n for (const candidate of paths) {\n let path: string;\n try {\n path = validatePrivateRelativePath(candidate);\n } catch {\n log.warn('Realtime sync ignored an invalid workspace path');\n continue;\n }\n\n if (existsSync(join(workspacePath, path))) {\n remoteDeleteSuppressions.delete(path);\n existing.push(path);\n } else if ((remoteDeleteSuppressions.get(path) ?? 0) > now) {\n remoteDeleteSuppressions.delete(path);\n } else {\n missing.push(path);\n }\n }\n\n log.debug(\n `Realtime sync: ${String(existing.length)} upload(s), ${String(missing.length)} delete(s)`,\n );\n if (existing.length > 0) {\n try {\n lastSyncResult = await engine.push(existing, { quiet: true });\n } catch (err: unknown) {\n log.error(`Realtime push failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n if (missing.length === 0) return;\n\n const artifactDeletes = missing.filter((path) => isRecoveryArtifact(path));\n const regularDeletes = missing.filter((path) => !isRecoveryArtifact(path));\n const manifest = await readManifest(workspacePath);\n const manifestSize = Object.keys(manifest.files).length;\n let toDelete = missing;\n if (!brake.check(regularDeletes.length, manifestSize)) {\n log.error(\n `Sync delete brake tripped: ${String(brake.windowSum() + regularDeletes.length)} ` +\n `deletes in last 60s vs manifest size ${String(manifestSize)} ` +\n `(threshold 30%). Refusing batch — investigate the workspace state.`,\n );\n toDelete = artifactDeletes;\n }\n if (toDelete.length > 0) {\n try {\n lastSyncResult = await engine.pushDeletes(toDelete, { quiet: true });\n } catch (err: unknown) {\n log.error(`Realtime delete failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n });\n}\n\n// ── Schedule Helpers ────────────────────────────────────────\n\nconst SCHEDULE_INTERVALS_MS: Record<string, number> = {\n hourly: 60 * 60 * 1000,\n daily: 24 * 60 * 60 * 1000,\n weekly: 7 * 24 * 60 * 60 * 1000,\n};\n\nfunction clearSchedule() {\n if (scheduledInterval) {\n clearInterval(scheduledInterval);\n scheduledInterval = null;\n }\n}\n\nfunction setupSchedule(schedule: string, log: PluginLogger) {\n clearSchedule();\n if (schedule === 'realtime') {\n log.info('Sync schedule: realtime (file watcher active)');\n return;\n }\n\n const intervalMs = SCHEDULE_INTERVALS_MS[schedule];\n if (!intervalMs) {\n log.warn(`Unknown sync schedule: ${schedule}, defaulting to hourly`);\n setupSchedule('hourly', log);\n return;\n }\n\n log.info(`Sync schedule: ${schedule} (every ${String(intervalMs / 1000)}s)`);\n scheduledInterval = setInterval(() => {\n if (!syncEngine) return;\n const engine = syncEngine;\n log.info(`Scheduled sync (${schedule}) starting...`);\n void runFullSync(engine, log, 'Scheduled sync');\n }, intervalMs);\n scheduledInterval.unref();\n}\n\n// ── Daemon IPC ──────────────────────────────────────────────\n// (connection via @alfe.ai/openclaw-plugin-kit's connectToDaemon; the\n// SYNC_NOW / SHARED_SCOPES message handling stays here)\n\nfunction handleDaemonMessage(msg: Record<string, unknown>, log: PluginLogger): void {\n if (msg.type === 'SYNC_NOW' || msg.command === 'SYNC_NOW') {\n log.info('Received SYNC_NOW command — triggering immediate sync...');\n if (syncEngine) {\n const engine = syncEngine;\n void runFullSync(engine, log, 'SYNC_NOW');\n }\n }\n\n if (msg.type === 'SHARED_SCOPES') {\n const scopes = parseSharedScopes(msg.scopes);\n const engine = sharedSyncEngine;\n if (!scopes) {\n log.warn('Ignored invalid SHARED_SCOPES payload from daemon');\n } else if (engine) {\n log.info(`Received SHARED_SCOPES update: ${String(scopes.length)} scope(s)`);\n void enqueueSyncOperation(async () => {\n if (sharedSyncEngine !== engine) return;\n try {\n await engine.updateScopes(scopes);\n } catch (err: unknown) {\n log.error(\n `SHARED_SCOPES update failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n });\n }\n }\n}\n\n// ── Sync Relay Connection ───────────────────────────────────\n\nfunction clearSyncRelayReconnect() {\n if (syncRelayReconnectTimer) {\n clearTimeout(syncRelayReconnectTimer);\n syncRelayReconnectTimer = null;\n }\n}\n\nfunction clearSyncRelayDebounce() {\n if (syncRelayDebounceTimer) {\n clearTimeout(syncRelayDebounceTimer);\n syncRelayDebounceTimer = null;\n }\n}\n\nasync function processPendingNotifications(log: PluginLogger) {\n if (syncRelayPendingPaths.size === 0) return;\n\n const entries = new Map(syncRelayPendingPaths);\n syncRelayPendingPaths.clear();\n\n const sharedEntries = new Map<string, { etag?: string; eventType: 'created' | 'deleted' }>();\n const privateEntries = new Map<string, { etag?: string; eventType: 'created' | 'deleted' }>();\n\n for (const [filePath, info] of entries) {\n if (filePath.startsWith('shared/')) {\n sharedEntries.set(filePath, info);\n } else {\n privateEntries.set(filePath, info);\n }\n }\n\n if (sharedEntries.size > 0 && sharedSyncEngine) {\n for (const [filePath, info] of sharedEntries) {\n try {\n await sharedSyncEngine.handleNotification(filePath, info.eventType);\n } catch (err: unknown) {\n log.error(\n `Shared sync relay: failed ${info.eventType} ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n if (privateEntries.size > 0 && syncEngine) {\n const engine = syncEngine;\n const toPull: string[] = [];\n const toDelete: string[] = [];\n\n for (const [filePath, info] of privateEntries) {\n if (info.eventType === 'deleted') {\n toDelete.push(filePath);\n } else {\n toPull.push(filePath);\n }\n }\n\n for (const filePath of toDelete) {\n try {\n remoteDeleteSuppressions.set(filePath, Date.now() + REMOTE_DELETE_SUPPRESSION_MS);\n await engine.removeLocalFile(filePath, { quiet: true });\n log.debug(`Sync relay: deleted ${filePath}`);\n } catch (err: unknown) {\n log.error(\n `Sync relay: failed to delete ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n if (toPull.length > 0) {\n try {\n const result = await engine.pullFiles(toPull, { quiet: true });\n if (result.pulled > 0) log.info(`Sync relay: pulled ${String(result.pulled)} file(s)`);\n if (result.errors > 0) log.warn(`Sync relay: ${String(result.errors)} pull error(s)`);\n } catch (err: unknown) {\n log.error(`Sync relay: pull failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n}\n\nasync function connectToSyncRelay(\n relayUrl: string,\n token: string,\n agentIdForSubscribe: string,\n log: PluginLogger,\n generation: number,\n): Promise<SyncRelaySocket | null> {\n try {\n const { default: WebSocket } = await import('ws');\n if (!syncRelayActive || generation !== syncRelayGeneration) return null;\n\n const ws = new WebSocket(relayUrl, {\n headers: { Authorization: `Bearer ${token}` },\n handshakeTimeout: SYNC_RELAY_HANDSHAKE_TIMEOUT_MS,\n maxPayload: SYNC_RELAY_MAX_MESSAGE_BYTES,\n });\n\n ws.on('open', () => {\n if (!syncRelayActive || generation !== syncRelayGeneration) {\n ws.close(1000, 'Stale sync relay connection');\n return;\n }\n log.info('Connected to Sync Relay');\n syncRelayReconnectAttempt = 0;\n ws.send(JSON.stringify({ type: 'SUBSCRIBE', agentId: agentIdForSubscribe }));\n });\n\n ws.on('message', (data: Buffer | string) => {\n if (!syncRelayActive || generation !== syncRelayGeneration) return;\n const raw = data.toString();\n if (Buffer.byteLength(raw) > SYNC_RELAY_MAX_MESSAGE_BYTES) {\n ws.close(1009, 'Sync relay message too large');\n return;\n }\n let value: unknown;\n try {\n value = JSON.parse(raw) as unknown;\n } catch {\n return;\n }\n const message = parseRelayMessage(value);\n if (!message) return;\n\n switch (message.type) {\n case 'SUBSCRIBE_ACK':\n if (message.agentId !== agentIdForSubscribe) return;\n if (message.status === 'ok') {\n log.info(`Subscribed to sync notifications for agent ${message.agentId}`);\n const sharedEngine = sharedSyncEngine;\n if (sharedEngine) {\n void enqueueSyncOperation(async () => {\n if (sharedSyncEngine !== sharedEngine) return;\n try {\n await sharedEngine.fullSync();\n log.info('Shared sync: reconnect full sync complete');\n } catch (err: unknown) {\n log.error(\n `Shared sync: reconnect full sync failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n });\n }\n } else {\n log.warn(`Sync relay subscribe failed: ${message.message ?? 'unknown'}`);\n }\n break;\n\n case 'FILE_CHANGED':\n if (message.agentId !== agentIdForSubscribe) return;\n syncRelayPendingPaths.set(message.filePath, {\n etag: message.etag,\n eventType: message.eventType,\n });\n clearSyncRelayDebounce();\n syncRelayDebounceTimer = setTimeout(\n () => {\n void enqueueSyncOperation(() => processPendingNotifications(log));\n },\n SYNC_RELAY_DEBOUNCE_MS,\n );\n syncRelayDebounceTimer.unref();\n break;\n\n case 'PING':\n try {\n ws.send(JSON.stringify({ type: 'PONG' }));\n } catch {\n // ignore\n }\n break;\n }\n });\n\n ws.on('close', (code: number) => {\n if (syncRelayWs === ws) syncRelayWs = null;\n if (!syncRelayActive || generation !== syncRelayGeneration) return;\n log.info(`Sync Relay disconnected (code=${String(code)})`);\n scheduleSyncRelayReconnect(relayUrl, token, agentIdForSubscribe, log, generation);\n });\n\n ws.on('error', (err: Error) => {\n log.debug(`Sync Relay error: ${err.message}`);\n });\n\n return ws as SyncRelaySocket;\n } catch (err: unknown) {\n log.debug(`Failed to connect to Sync Relay: ${err instanceof Error ? err.message : String(err)}`);\n if (syncRelayActive && generation === syncRelayGeneration) {\n scheduleSyncRelayReconnect(relayUrl, token, agentIdForSubscribe, log, generation);\n }\n return null;\n }\n}\n\nfunction scheduleSyncRelayReconnect(\n relayUrl: string,\n token: string,\n agentIdForSubscribe: string,\n log: PluginLogger,\n generation: number,\n) {\n if (!syncRelayActive || generation !== syncRelayGeneration) return;\n clearSyncRelayReconnect();\n const delay = Math.min(\n SYNC_RELAY_RECONNECT_BASE_MS * Math.pow(2, syncRelayReconnectAttempt),\n SYNC_RELAY_RECONNECT_MAX_MS,\n );\n syncRelayReconnectAttempt++;\n log.debug(\n `Reconnecting to Sync Relay in ${String(delay)}ms (attempt ${String(syncRelayReconnectAttempt)})`,\n );\n\n syncRelayReconnectTimer = setTimeout(() => {\n void (async () => {\n const socket = await connectToSyncRelay(\n relayUrl,\n token,\n agentIdForSubscribe,\n log,\n generation,\n );\n if (syncRelayActive && generation === syncRelayGeneration) {\n syncRelayWs = socket;\n } else if (socket) {\n socket.close(1000, 'Stale sync relay connection');\n }\n })();\n }, delay);\n syncRelayReconnectTimer.unref();\n}\n\nfunction disconnectSyncRelay() {\n syncRelayActive = false;\n syncRelayGeneration++;\n clearSyncRelayReconnect();\n clearSyncRelayDebounce();\n syncRelayPendingPaths.clear();\n remoteDeleteSuppressions.clear();\n\n if (syncRelayWs) {\n try {\n if (agentId) {\n syncRelayWs.send(JSON.stringify({ type: 'UNSUBSCRIBE', agentId }));\n }\n syncRelayWs.close(1000, 'Plugin deactivating');\n } catch {\n // ignore\n }\n syncRelayWs = null;\n }\n}\n\n// ── Plugin Definition ───────────────────────────────────────\n\nconst plugin = {\n id: '@alfe.ai/openclaw-sync',\n name: 'Alfe Sync Plugin',\n description:\n 'Back up agent configuration, conversations, and memory to the cloud with scheduled or real-time sync.',\n version: pkg.version,\n\n activate(api: PluginApi) {\n const log = api.logger;\n\n const pluginConfig: SyncPluginConfig = api.config ?? {};\n currentConfig = pluginConfig;\n\n let alfeConfig: { workspacePath: string; socketPath: string; runtime: string } | null = null;\n try {\n alfeConfig = resolveAlfeConfig();\n } catch {\n // alfe login hasn't run; logged below\n }\n\n const workspacePath =\n pluginConfig.workspacePath ?? alfeConfig?.workspacePath ?? DEFAULT_WORKSPACE_PATH;\n // Active runtime selects the per-runtime default ignore set (openclaw npm/\n // extensions/ plugins/, hermes .venv/ hermes-agent/) so the watcher never\n // descends into runtime-internal dirs. Falls back to openclaw.\n const runtime = alfeConfig?.runtime ?? 'openclaw';\n\n const syncScope = pluginConfig.syncScope ?? ['config', 'conversations', 'memory'];\n // Default to 'realtime' when no schedule is configured. The integration's\n // user-config schema marks sync_schedule as required, so an unset value\n // here means the framework didn't deliver the user's choice (manifest\n // template missing, applyConfig errored, etc.). 'daily' would leave a\n // managed agent silent for 24 hours after activation; 'realtime' fails\n // open into the safest mode — initial push runs immediately and the\n // watcher streams ongoing changes.\n const syncSchedule = pluginConfig.syncSchedule ?? 'realtime';\n const socketPath =\n pluginConfig.socketPath ?? alfeConfig?.socketPath ?? DEFAULT_SOCKET_PATH;\n\n const startSyncService = () => {\n guardedStart(SYNC_ACTIVATION_KEY, log, async () => {\n\n log.info('Alfe Sync plugin activating...');\n log.info(`Sync scope: ${syncScope.join(', ')}`);\n log.info(`Sync schedule: ${syncSchedule}`);\n log.info(`Workspace: ${workspacePath}`);\n\n // Sync needs `~/.alfe/config.toml` (the existing `alfe login` config).\n // Identity (agentId / tenantId) is resolved server-side from the API\n // key on every AgentApiClient call.\n if (!alfeConfigExists()) {\n log.info('Sync skipped — no Alfe config found. Run `alfe login` to enable.');\n // Reset so a later activate() retries once `alfe login` has run.\n resetActivation(SYNC_ACTIVATION_KEY);\n return;\n }\n\n let syncCfg: AlfeResolvedConfig;\n try {\n syncCfg = resolveAlfeConfig();\n } catch (err: unknown) {\n log.warn(\n `Sync skipped — failed to resolve credentials from ~/.alfe/config.toml: ${err instanceof Error ? err.message : String(err)}`,\n );\n resetActivation(SYNC_ACTIVATION_KEY);\n return;\n }\n\n client = new AgentApiClient({ apiKey: syncCfg.apiKey, apiUrl: syncCfg.apiUrl });\n syncEngine = createSyncEngine({ workspacePath, client, runtime });\n log.info('Sync engine initialized');\n\n // Realtime mode → first-run reconcile of pre-existing files (chokidar\n // ignoreInitial means the watcher only emits for post-activation\n // changes; without this the workspace tree never reaches the cloud\n // unless a file is touched). This RECONCILES with cloud state rather\n // than blindly pushing: on a from-scratch VM rebuild the local\n // manifest is gone and `alfe setup` has just re-seeded the persona\n // templates, so a blind push(undefined) would upload those fresh seeds\n // OVER the agent's edited cloud copy and lose its edits. firstRunReconcile\n // restores cloud → local (cloud wins) when the cloud already holds\n // state, and only push-seeds files that are genuinely new locally. A\n // brand-new agent (empty cloud) still gets the push-only seed. See\n // sync-engine.ts firstRunReconcile for the fresh-vs-rebuild rationale.\n if (syncSchedule === 'realtime') {\n try {\n lastSyncResult = await syncEngine.firstRunReconcile({ quiet: true });\n if (lastSyncResult.conflicts > 0) {\n // `conflicts` counts every diverged local file the recovery\n // handled (sidecar written, identical sidecar already present,\n // or over the preservation size cap) — see RECOVERY-*.md for\n // the per-file breakdown.\n log.info(\n `Initial workspace reconcile complete — ${String(lastSyncResult.conflicts)} diverged local file(s) handled (see RECOVERY-*.md in the agent workspace)`,\n );\n } else {\n log.info('Initial workspace reconcile complete');\n }\n } catch (err: unknown) {\n log.warn(`Initial workspace reconcile failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n // Self-heal: purge cloud files that a pre-fix ignore engine uploaded\n // but the current per-runtime ignore set excludes (node_modules, the\n // openclaw npm cache, …). Bounded to ignored paths (capped per pass),\n // so legit files are never touched. Fire-and-forget so a large drain\n // can't stall watcher startup — the watcher already ignores these\n // paths, so they won't reappear, and the remainder clears next start.\n const engineForPrune = syncEngine;\n void enqueueSyncOperation(async () => {\n if (syncEngine !== engineForPrune) return null;\n return engineForPrune.pruneIgnored({ quiet: true });\n })\n .then((pruned) => {\n if (pruned && pruned.pushed > 0) {\n log.info(`Pruned ${String(pruned.pushed)} ignored file(s) from cloud`);\n }\n })\n .catch((err: unknown) => {\n log.warn(`Ignored-file prune failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n deleteBrake = createDeleteBrake();\n try {\n stopWatcher = await startWatcher({\n workspacePath,\n runtime,\n debounceMs: 2000,\n onChanges: (paths) => handleRealtimeChanges(paths, workspacePath, log),\n });\n log.info('File watcher started for realtime sync');\n } catch (err: unknown) {\n log.warn(`Failed to start file watcher: ${err instanceof Error ? err.message : String(err)}`);\n }\n } else {\n setupSchedule(syncSchedule, log);\n }\n\n // Daemon IPC (for SYNC_NOW + SHARED_SCOPES messages)\n daemonIpcClient = await connectToDaemon(socketPath, log, {\n pluginId: '@alfe.ai/openclaw-sync',\n capabilities: SYNC_CAPABILITIES,\n onMessage: (msg) => { handleDaemonMessage(msg, log); },\n standaloneNote: 'Alfe daemon not available — Sync plugin running standalone',\n });\n\n // Register with the sync service to materialize the SyncAgent record\n // and learn our own agentId for relay subscription.\n let registered: SyncAgentInfo | null = null;\n try {\n const result = await client.syncRegister();\n registered = result.agent;\n agentId = registered.agentId;\n } catch (err: unknown) {\n log.warn(`Sync register failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n // Sync relay (real-time file change notifications)\n if (registered) {\n // Create the shared engine before opening the relay. A fast\n // SUBSCRIBE_ACK triggers a shared full sync, so constructing it after\n // the socket introduced a race where the initial refresh was skipped.\n if (pluginConfig.sharedSync !== false) {\n try {\n sharedSyncEngine = createSharedSyncEngine({ workspacePath, client }, log);\n log.info('Shared sync engine created — waiting for SHARED_SCOPES from gateway');\n } catch (err: unknown) {\n log.debug(\n `Shared sync engine skipped: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n try {\n const relayUrl =\n pluginConfig.syncRelayUrl ?? deriveServiceWsUrl(syncCfg.apiUrl, 'sync');\n syncRelayActive = true;\n const relayGeneration = ++syncRelayGeneration;\n syncRelayWs = await connectToSyncRelay(\n relayUrl,\n syncCfg.apiKey,\n registered.agentId,\n log,\n relayGeneration,\n );\n } catch (err: unknown) {\n log.debug(\n `Sync Relay connection skipped: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n }\n\n });\n };\n\n const stopSyncService = async () => {\n // Stop side effects BEFORE resetting the activation flag (§E ordering).\n clearSchedule();\n disconnectSyncRelay();\n deleteBrake = null;\n\n if (stopWatcher) {\n try {\n await stopWatcher();\n log.info('File watcher stopped');\n } catch (err: unknown) {\n log.debug(`Error stopping watcher: ${err instanceof Error ? err.message : String(err)}`);\n }\n stopWatcher = null;\n }\n\n if (daemonIpcClient) {\n try {\n daemonIpcClient.stop();\n log.info('Disconnected from Alfe daemon');\n } catch (err: unknown) {\n log.debug(`Error disconnecting from daemon: ${err instanceof Error ? err.message : String(err)}`);\n }\n daemonIpcClient = null;\n }\n\n client = null;\n agentId = null;\n syncEngine = null;\n sharedSyncEngine = null;\n lastSyncResult = null;\n currentConfig = {};\n\n resetActivation(SYNC_ACTIVATION_KEY);\n log.info('Alfe Sync plugin deactivated');\n };\n\n if (typeof api.registerGatewayMethod === 'function') {\n api.registerGatewayMethod('sync.now', async () => {\n if (!syncEngine) {\n return { ok: false, error: 'Sync engine not initialized — run `alfe login`' };\n }\n try {\n const engine = syncEngine;\n const result = await runFullSync(engine, log, 'sync.now');\n return result\n ? { ok: true, result }\n : { ok: false, error: 'Sync did not complete' };\n } catch (err: unknown) {\n return { ok: false, error: err instanceof Error ? err.message : String(err) };\n }\n });\n log.info('Registered gateway RPC method: sync.now');\n\n api.registerGatewayMethod('sync.status', () =>\n Promise.resolve({\n ok: true,\n initialized: !!syncEngine,\n agentId,\n schedule: currentConfig.syncSchedule ?? 'realtime',\n scope: currentConfig.syncScope ?? ['config', 'conversations', 'memory'],\n lastResult: lastSyncResult,\n watcherActive: !!stopWatcher,\n }),\n );\n log.info('Registered gateway RPC method: sync.status');\n }\n\n api.registerService({\n id: 'alfe-sync-engine',\n start: () => { startSyncService(); },\n stop: () => stopSyncService(),\n });\n\n log.info('Alfe Sync plugin activated');\n },\n\n async deactivate(api: PluginApi) {\n const log = api.logger;\n log.info('Alfe Sync plugin deactivating...');\n\n // Stop side effects BEFORE resetting the activation flag (§E ordering).\n clearSchedule();\n disconnectSyncRelay();\n deleteBrake = null;\n\n if (stopWatcher) {\n try {\n await stopWatcher();\n log.info('File watcher stopped');\n } catch (err: unknown) {\n log.debug(`Error stopping watcher: ${err instanceof Error ? err.message : String(err)}`);\n }\n stopWatcher = null;\n }\n\n if (daemonIpcClient) {\n try {\n daemonIpcClient.stop();\n log.info('Disconnected from Alfe daemon');\n } catch (err: unknown) {\n log.debug(`Error disconnecting from daemon: ${err instanceof Error ? err.message : String(err)}`);\n }\n daemonIpcClient = null;\n }\n\n client = null;\n agentId = null;\n syncEngine = null;\n sharedSyncEngine = null;\n lastSyncResult = null;\n currentConfig = {};\n\n resetActivation(SYNC_ACTIVATION_KEY);\n log.info('Alfe Sync plugin deactivated');\n },\n\n async configure(api: PluginApi, config: SyncPluginConfig) {\n const log = api.logger;\n log.info('Reconfiguring Alfe Sync plugin...');\n currentConfig = { ...currentConfig, ...config };\n\n if (config.syncSchedule) {\n if (config.syncSchedule === 'realtime' && syncEngine && !stopWatcher) {\n clearSchedule();\n const engine = syncEngine;\n try {\n const reconciled = await enqueueSyncOperation(async () => {\n if (syncEngine !== engine) return null;\n return engine.firstRunReconcile({ quiet: true });\n });\n if (reconciled) lastSyncResult = reconciled;\n } catch (err: unknown) {\n log.warn(\n `Realtime reconfigure reconcile failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n const workspacePath = currentConfig.workspacePath ?? syncEngine.workspacePath;\n deleteBrake = createDeleteBrake();\n stopWatcher = await startWatcher({\n workspacePath,\n runtime: syncEngine.runtime,\n debounceMs: 2000,\n onChanges: (paths) => handleRealtimeChanges(paths, workspacePath, log),\n });\n log.info('Switched to realtime sync');\n } else if (config.syncSchedule !== 'realtime') {\n deleteBrake = null;\n if (stopWatcher) {\n await stopWatcher();\n stopWatcher = null;\n }\n setupSchedule(config.syncSchedule, log);\n }\n }\n\n if (config.syncScope) {\n log.info(`Updated sync scope: ${config.syncScope.join(', ')}`);\n }\n\n log.info('Alfe Sync plugin reconfigured');\n },\n};\n\nexport default plugin;\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin2.js","names":["resolveAlfeConfig","alfeConfigExists"],"sources":["../src/watcher.ts","../src/shared-sync.ts","../src/delete-brake.ts","../src/plugin.ts"],"sourcesContent":["/**\n * AlfeSync watcher — recursive file watcher with debounce and ignore support.\n *\n * Uses chokidar to watch the workspace root, debounces per-file changes\n * by 2 seconds, and emits batches of changed paths.\n */\n\nimport { watch } from \"chokidar\";\nimport { relative } from \"node:path\";\nimport { createLogger } from \"@auriclabs/logger\";\nimport { loadIgnorePatterns, shouldIgnore, shouldIgnoreDir } from \"./ignore.js\";\n\nconst WORKSPACE_IGNORE_FILE = \".alfesyncignore\";\n\nconst log = createLogger(\"SyncWatcher\");\n\nexport interface WatcherOptions {\n workspacePath: string;\n /**\n * Active agent runtime — selects the per-runtime default ignore set so the\n * watcher never descends into runtime-internal dirs (openclaw `npm/`,\n * hermes `.venv/`, …). Default: \"openclaw\".\n */\n runtime?: string;\n /** Debounce delay per file in milliseconds. Default: 2000 */\n debounceMs?: number;\n /** Callback invoked with batches of changed relative paths */\n onChanges: (paths: string[]) => void | Promise<void>;\n}\n\n/**\n * Start watching a workspace for file changes.\n *\n * Returns a cleanup function to stop watching.\n */\nexport async function startWatcher(\n options: WatcherOptions,\n): Promise<() => Promise<void>> {\n const { workspacePath, runtime = \"openclaw\", debounceMs = 2000, onChanges } = options;\n let ignorePatterns = await loadIgnorePatterns(workspacePath, runtime);\n let stopped = false;\n\n // Pending changes map: relativePath → timeout handle\n const pending = new Map<string, ReturnType<typeof setTimeout>>();\n // Batch accumulator for flushing\n let batchPaths = new Set<string>();\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n function scheduleBatch() {\n if (flushTimer) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n if (batchPaths.size === 0) return;\n\n const paths = [...batchPaths];\n batchPaths = new Set();\n void Promise.resolve(onChanges(paths)).catch((error: unknown) => {\n log.error({ err: error }, \"Sync watcher change handler failed\");\n });\n }, debounceMs);\n }\n\n function queueChange(relativePath: string) {\n // Skip ignored files\n if (shouldIgnore(relativePath, ignorePatterns)) return;\n\n // Clear existing timer for this file\n const existingTimer = pending.get(relativePath);\n if (existingTimer) clearTimeout(existingTimer);\n\n // Debounce: wait before adding to batch\n const timer = setTimeout(() => {\n pending.delete(relativePath);\n batchPaths.add(relativePath);\n scheduleBatch();\n }, debounceMs);\n\n pending.set(relativePath, timer);\n }\n\n const watcher = watch(workspacePath, {\n persistent: true,\n ignoreInitial: true,\n followSymlinks: false,\n depth: undefined, // unlimited depth\n // chokidar 4 dropped glob support in `ignored` — a string-glob array is\n // treated as literal paths and matches nothing, so the watcher would\n // create an inotify watch on every dir (incl. node_modules / the runtime's\n // npm cache) and exhaust the OS watch limit → ENOSPC crash-loop. Use a\n // predicate driven by the SAME ignore engine as event filtering; returning\n // true for a directory stops chokidar descending into it.\n // chokidar invokes this for files AND directories, twice per entry: first\n // with no stats (path check), then with stats before it actually watches.\n // Match ignored files directly; prune ignored directories (whose contents\n // are ignored) via the sentinel-child probe. When stats say it's a FILE,\n // skip the dir probe so a file literally named like an ignored dir (e.g. an\n // extensionless `dist`) isn't wrongly ignored — keeps parity with the walk,\n // which only dir-prunes real directories.\n ignored: (p: string, stats?: { isDirectory(): boolean }) => {\n const rel = relative(workspacePath, p).replace(/\\\\/g, \"/\");\n if (rel === \"\") return false; // never ignore the workspace root\n if (shouldIgnore(rel, ignorePatterns)) return true;\n if (stats && !stats.isDirectory()) return false;\n return shouldIgnoreDir(rel, ignorePatterns);\n },\n });\n\n // Reloads are serialized so a rapid editor save sequence cannot publish an\n // older ruleset after a newer one. Re-subscribing the root is important:\n // changing only the predicate would not make chokidar descend into a\n // directory that the previous rules pruned at startup.\n let ignoreReloadTail = Promise.resolve();\n\n function handleChange(absolutePath: string) {\n // Posix-normalise so the globs (which are posix-style) match on Windows\n // self-hosted agents too — parity with the `ignored` predicate below.\n const relativePath = relative(workspacePath, absolutePath).replace(/\\\\/g, \"/\");\n\n if (relativePath === WORKSPACE_IGNORE_FILE) {\n ignoreReloadTail = ignoreReloadTail\n .then(async () => {\n const nextRules = await loadIgnorePatterns(workspacePath, runtime);\n if (stopped) return;\n ignorePatterns = nextRules;\n watcher.unwatch(workspacePath);\n watcher.add(workspacePath);\n queueChange(relativePath);\n })\n .catch((error: unknown) => {\n log.error({ err: error }, \"Failed to reload sync ignore rules\");\n });\n return;\n }\n\n queueChange(relativePath);\n }\n\n watcher.on(\"add\", handleChange);\n watcher.on(\"change\", handleChange);\n watcher.on(\"unlink\", handleChange);\n watcher.on(\"error\", (error) => {\n log.error({ err: error }, \"Sync watcher failed\");\n });\n\n // Return cleanup function\n return async () => {\n stopped = true;\n\n // Clear all pending timers\n for (const timer of pending.values()) {\n clearTimeout(timer);\n }\n pending.clear();\n\n if (flushTimer) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n batchPaths.clear();\n\n await ignoreReloadTail;\n await watcher.close();\n };\n}\n","/**\n * Shared file sync — mirrors org/team/project files to a `shared/`\n * directory in the agent's workspace, organised by scope:\n *\n * shared/org/<files…>\n * shared/teams/<scopeId>/<files…>\n * shared/projects/<scopeId>/<files…>\n *\n * Backed by the agent self-service API (`AgentApiClient.sharedListFiles`,\n * `AgentApiClient.sharedDownloadUrl`).\n */\n\nimport { join, dirname, isAbsolute, normalize, sep } from \"node:path\";\nimport { lstat, mkdir, readdir, rmdir, writeFile, unlink, rm } from \"node:fs/promises\";\nimport type { AgentApiClient, SharedFileEntry } from \"@alfe.ai/agent-api-client\";\n\nconst MAX_SHARED_FILE_SIZE = 100 * 1024 * 1024; // 100 MB\nconst SHARED_LIST_PAGE_SIZE = 500;\nconst MAX_SHARED_LIST_PAGES = 100;\n\nfunction validateScope(scope: SharedScope): SharedScope {\n const candidate = scope as Partial<SharedScope> | null;\n if (\n candidate === null ||\n (candidate.scopeType !== \"org\" &&\n candidate.scopeType !== \"team\" &&\n candidate.scopeType !== \"project\") ||\n typeof candidate.scopeId !== \"string\" ||\n candidate.scopeId.length === 0 ||\n candidate.scopeId.length > 512 ||\n candidate.scopeId === \".\" ||\n candidate.scopeId === \"..\" ||\n candidate.scopeId.includes(\"/\") ||\n candidate.scopeId.includes(\"\\\\\") ||\n candidate.scopeId.includes(\"\\0\") ||\n typeof candidate.name !== \"string\" ||\n candidate.name.length > 512\n ) {\n throw new Error(\"Invalid shared sync scope\");\n }\n return candidate as SharedScope;\n}\n\nfunction validateSharedFilePath(filePath: string): string {\n if (\n filePath.length === 0 ||\n filePath.length > 1024 ||\n isAbsolute(filePath) ||\n filePath.includes(\"\\\\\") ||\n filePath.includes(\"\\0\")\n ) {\n throw new Error(\"Invalid shared sync file path\");\n }\n const segments = filePath.split(\"/\");\n if (segments.some((segment) => segment === \"\" || segment === \".\" || segment === \"..\")) {\n throw new Error(\"Invalid shared sync file path\");\n }\n return segments.join(\"/\");\n}\n\n/** Throw if `resolvedPath` would escape `baseDir`. */\nfunction assertContained(baseDir: string, resolvedPath: string): void {\n const normalizedBase = normalize(baseDir) + sep;\n const normalizedPath = normalize(resolvedPath);\n if (\n !normalizedPath.startsWith(normalizedBase) &&\n normalizedPath !== normalize(baseDir)\n ) {\n throw new Error(`Path traversal blocked: ${resolvedPath} escapes ${baseDir}`);\n }\n}\n\nasync function assertNoSymlinkTraversal(\n baseDir: string,\n relativePath: string,\n): Promise<string> {\n const canonical = validateSharedFilePath(relativePath);\n const resolvedPath = join(baseDir, canonical);\n assertContained(baseDir, resolvedPath);\n let current = normalize(baseDir);\n for (const segment of canonical.split(\"/\")) {\n current = join(current, segment);\n try {\n const entry = await lstat(current);\n if (entry.isSymbolicLink()) throw new Error(\"Shared sync path crosses a symbolic link\");\n } catch (error) {\n if (\n error instanceof Error &&\n \"code\" in error &&\n (error as NodeJS.ErrnoException).code === \"ENOENT\"\n ) {\n break;\n }\n throw error;\n }\n }\n return resolvedPath;\n}\n\nexport interface SharedScope {\n scopeType: \"team\" | \"project\" | \"org\";\n scopeId: string;\n name: string;\n}\n\nexport interface SharedSyncConfig {\n workspacePath: string;\n client: AgentApiClient;\n}\n\ninterface PluginLogger {\n info(msg: string): void;\n warn(msg: string): void;\n error(msg: string): void;\n debug(msg: string): void;\n}\n\nexport interface SharedSyncEngine {\n initialize(scopes: SharedScope[]): Promise<void>;\n updateScopes(scopes: SharedScope[]): Promise<void>;\n handleNotification(filePath: string, eventType: \"created\" | \"deleted\"): Promise<void>;\n fullSync(): Promise<void>;\n getScopes(): SharedScope[];\n}\n\nexport function createSharedSyncEngine(\n config: SharedSyncConfig,\n log: PluginLogger,\n): SharedSyncEngine {\n let activeScopes: SharedScope[] = [];\n const sharedDir = join(config.workspacePath, \"shared\");\n\n function scopeDir(scope: SharedScope): string {\n validateScope(scope);\n if (scope.scopeType === \"org\") return join(sharedDir, \"org\");\n const plural = scope.scopeType === \"team\" ? \"teams\" : \"projects\";\n return join(sharedDir, plural, scope.scopeId);\n }\n\n async function downloadFile(\n scope: SharedScope,\n filePath: string,\n localPath: string,\n ): Promise<void> {\n const canonicalPath = validateSharedFilePath(filePath);\n const safeLocalPath = await assertNoSymlinkTraversal(scopeDir(scope), canonicalPath);\n if (safeLocalPath !== normalize(localPath)) {\n throw new Error(\"Shared sync local path does not match its canonical path\");\n }\n\n const { downloadUrl } = await config.client.sharedDownloadUrl({\n scope: scope.scopeType,\n scopeId: scope.scopeId,\n filePath: canonicalPath,\n });\n\n // Presigned URL → S3 directly (raw fetch is the right tool here).\n const response = await fetch(downloadUrl);\n if (!response.ok) {\n throw new Error(`Download failed: HTTP ${String(response.status)}`);\n }\n\n const contentLength = parseInt(\n response.headers.get(\"content-length\") ?? \"0\",\n 10,\n );\n if (contentLength > MAX_SHARED_FILE_SIZE) {\n throw new Error(\n `File too large: ${String(contentLength)} bytes exceeds ${String(MAX_SHARED_FILE_SIZE)} limit`,\n );\n }\n\n const buffer = Buffer.from(await response.arrayBuffer());\n if (buffer.length > MAX_SHARED_FILE_SIZE) {\n throw new Error(`Downloaded file exceeds size limit: ${String(buffer.length)} bytes`);\n }\n\n await mkdir(dirname(localPath), { recursive: true });\n await writeFile(localPath, buffer);\n }\n\n async function listRemoteFiles(scope: SharedScope): Promise<SharedFileEntry[]> {\n const files = new Map<string, SharedFileEntry>();\n const seenCursors = new Set<string>();\n let cursor: string | undefined;\n\n for (let page = 0; page < MAX_SHARED_LIST_PAGES; page++) {\n const response = await config.client.sharedListFiles({\n scope: scope.scopeType,\n scopeId: scope.scopeId,\n limit: SHARED_LIST_PAGE_SIZE,\n ...(cursor ? { cursor } : {}),\n });\n for (const file of response.files) {\n const filePath = validateSharedFilePath(file.filePath);\n files.set(filePath, { ...file, filePath });\n }\n\n if (!response.nextCursor) {\n const paths = new Set(files.keys());\n for (const filePath of paths) {\n const segments = filePath.split(\"/\");\n for (let index = 1; index < segments.length; index++) {\n if (paths.has(segments.slice(0, index).join(\"/\"))) {\n throw new Error(\"Shared file listing contains a file/directory collision\");\n }\n }\n }\n return [...files.values()];\n }\n if (seenCursors.has(response.nextCursor)) {\n throw new Error(\"Shared file listing returned a repeated cursor\");\n }\n seenCursors.add(response.nextCursor);\n cursor = response.nextCursor;\n }\n\n throw new Error(\n `Shared file listing exceeded ${String(MAX_SHARED_LIST_PAGES)} pages`,\n );\n }\n\n async function pruneLocalMirror(\n dir: string,\n remotePaths: ReadonlySet<string>,\n ): Promise<void> {\n const remoteDirectories = new Set<string>();\n for (const filePath of remotePaths) {\n const segments = filePath.split(\"/\");\n for (let index = 1; index < segments.length; index++) {\n remoteDirectories.add(segments.slice(0, index).join(\"/\"));\n }\n }\n\n const walk = async (currentDir: string, prefix: string): Promise<void> => {\n const entries = await readdir(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;\n const fullPath = join(currentDir, entry.name);\n let canonicalPath: string | null = null;\n try {\n canonicalPath = validateSharedFilePath(relativePath);\n } catch {\n // Invalid local entries cannot be part of the canonical mirror.\n }\n\n if (entry.isSymbolicLink()) {\n await unlink(fullPath);\n log.warn(`Shared sync: removed symbolic link ${relativePath}`);\n continue;\n }\n\n if (entry.isDirectory()) {\n if (!canonicalPath) {\n await rm(fullPath, { recursive: true, force: true });\n continue;\n }\n await walk(fullPath, canonicalPath);\n if (!remoteDirectories.has(canonicalPath)) {\n try {\n await rmdir(fullPath);\n } catch (error) {\n if (\n !(error instanceof Error &&\n \"code\" in error &&\n ((error as NodeJS.ErrnoException).code === \"ENOENT\" ||\n (error as NodeJS.ErrnoException).code === \"ENOTEMPTY\"))\n ) {\n throw error;\n }\n }\n }\n continue;\n }\n\n if (!canonicalPath || !remotePaths.has(canonicalPath)) {\n await unlink(fullPath);\n log.debug(`Shared sync: pruned stale local file ${relativePath}`);\n }\n }\n };\n\n await walk(dir, \"\");\n }\n\n async function syncScope(scope: SharedScope): Promise<void> {\n const dir = scopeDir(scope);\n await mkdir(dir, { recursive: true });\n\n const files = await listRemoteFiles(scope);\n const remotePaths = new Set(files.map((file) => file.filePath));\n await pruneLocalMirror(dir, remotePaths);\n\n for (const file of files) {\n try {\n const filePath = validateSharedFilePath(file.filePath);\n const localPath = join(dir, filePath);\n await downloadFile(scope, filePath, localPath);\n log.debug(\n `Shared sync: downloaded ${scope.scopeType}/${scope.scopeId}/${file.filePath}`,\n );\n } catch (err: unknown) {\n log.error(\n `Shared sync: failed to download ${file.filePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n function parseScopedPath(\n filePath: string,\n ): { scope: SharedScope; relativePath: string } | null {\n if (filePath.length > 2048 || filePath.includes(\"\\\\\") || filePath.includes(\"\\0\")) {\n return null;\n }\n\n const orgMatch = /^shared\\/org\\/(.+)$/.exec(filePath);\n if (orgMatch) {\n const scope = activeScopes.find((s) => s.scopeType === \"org\");\n if (scope) {\n try {\n return { scope, relativePath: validateSharedFilePath(orgMatch[1]) };\n } catch {\n return null;\n }\n }\n }\n\n const teamMatch = /^shared\\/teams\\/([^/]+)\\/(.+)$/.exec(filePath);\n if (teamMatch) {\n const scope = activeScopes.find(\n (s) => s.scopeType === \"team\" && s.scopeId === teamMatch[1],\n );\n if (scope) {\n try {\n return { scope, relativePath: validateSharedFilePath(teamMatch[2]) };\n } catch {\n return null;\n }\n }\n }\n\n const projectMatch = /^shared\\/projects\\/([^/]+)\\/(.+)$/.exec(filePath);\n if (projectMatch) {\n const scope = activeScopes.find(\n (s) => s.scopeType === \"project\" && s.scopeId === projectMatch[1],\n );\n if (scope) {\n try {\n return { scope, relativePath: validateSharedFilePath(projectMatch[2]) };\n } catch {\n return null;\n }\n }\n }\n\n return null;\n }\n\n return {\n async initialize(scopes: SharedScope[]): Promise<void> {\n activeScopes = scopes.map(validateScope);\n log.info(`Shared sync: initializing with ${String(scopes.length)} scope(s)`);\n await mkdir(sharedDir, { recursive: true });\n for (const scope of scopes) {\n await syncScope(scope);\n }\n log.info(\"Shared sync: initialization complete\");\n },\n\n async updateScopes(newScopes: SharedScope[]): Promise<void> {\n const validatedScopes = newScopes.map(validateScope);\n const oldIds = new Set(activeScopes.map((s) => `${s.scopeType}:${s.scopeId}`));\n const newIds = new Set(validatedScopes.map((s) => `${s.scopeType}:${s.scopeId}`));\n\n for (const scope of activeScopes) {\n const key = `${scope.scopeType}:${scope.scopeId}`;\n if (!newIds.has(key)) {\n const dir = scopeDir(scope);\n try {\n await rm(dir, { recursive: true, force: true });\n log.info(`Shared sync: removed scope directory ${dir}`);\n } catch (err: unknown) {\n log.warn(\n `Shared sync: failed to remove ${dir}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n for (const scope of validatedScopes) {\n const key = `${scope.scopeType}:${scope.scopeId}`;\n if (!oldIds.has(key)) {\n log.info(\n `Shared sync: new scope ${scope.scopeType}/${scope.scopeId} — syncing files`,\n );\n await syncScope(scope);\n }\n }\n\n activeScopes = [...validatedScopes];\n },\n\n async handleNotification(\n filePath: string,\n eventType: \"created\" | \"deleted\",\n ): Promise<void> {\n const parsed = parseScopedPath(filePath);\n if (!parsed) {\n log.debug(`Shared sync: ignoring notification for unknown path: ${filePath}`);\n return;\n }\n\n const dir = scopeDir(parsed.scope);\n const localPath = await assertNoSymlinkTraversal(dir, parsed.relativePath).catch(() => null);\n\n if (!localPath) {\n log.warn(`Shared sync: path traversal blocked for ${filePath}`);\n return;\n }\n\n if (eventType === \"deleted\") {\n try {\n await unlink(localPath);\n log.debug(`Shared sync: deleted ${filePath}`);\n } catch {\n // idempotent — file may not exist locally on out-of-order events\n }\n return;\n }\n\n try {\n await downloadFile(parsed.scope, parsed.relativePath, localPath);\n log.debug(`Shared sync: pulled ${filePath}`);\n } catch (err: unknown) {\n log.error(\n `Shared sync: failed to pull ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n },\n\n async fullSync(): Promise<void> {\n log.info(`Shared sync: full sync across ${String(activeScopes.length)} scope(s)`);\n for (const scope of activeScopes) {\n await syncScope(scope);\n }\n },\n\n getScopes(): SharedScope[] {\n return [...activeScopes];\n },\n };\n}\n","// Rolling-window rate brake for cloud delete propagation.\n//\n// The watcher's onChanges callback emits batches every ~2 s. A per-batch\n// brake (`if batch / manifestSize > 30%`) is trivial to slip on a real\n// `rm -rf` because chokidar dribbles deletes out 5-10 at a time. Instead\n// we sum deletes across the last 60 s and trip when the cumulative count\n// exceeds 30% of the manifest. Legitimate single-file deletes via the\n// agent stay nowhere near the threshold; a workspace unmount or\n// catastrophic delete rapidly accumulates and is stopped.\n//\n// Manifest size of 0 means the agent has nothing in the cloud yet —\n// can't be a disaster, so always allow.\n//\n// In-memory only. A plugin restart resets the window, which is acceptable:\n// a restart already implies operator awareness.\n\nconst DEFAULT_WINDOW_MS = 60_000;\nconst DEFAULT_THRESHOLD_RATIO = 0.3;\n\nexport interface DeleteBrakeOptions {\n windowMs?: number;\n thresholdRatio?: number;\n now?: () => number;\n}\n\nexport interface DeleteBrake {\n // Returns true if the proposed delete count is safe to proceed (and\n // records it). Returns false if combining it with the existing rolling\n // window would exceed the threshold — caller must not perform the\n // delete in that case.\n check(deleteCount: number, manifestSize: number): boolean;\n // Current sum of deletes within the active window (after expiry).\n windowSum(): number;\n}\n\nexport function createDeleteBrake(opts: DeleteBrakeOptions = {}): DeleteBrake {\n const windowMs = opts.windowMs ?? DEFAULT_WINDOW_MS;\n const thresholdRatio = opts.thresholdRatio ?? DEFAULT_THRESHOLD_RATIO;\n const now = opts.now ?? (() => Date.now());\n\n const records: { ts: number; count: number }[] = [];\n\n function expire(): void {\n const cutoff = now() - windowMs;\n while (records.length > 0 && records[0].ts < cutoff) records.shift();\n }\n\n function sum(): number {\n return records.reduce((acc, r) => acc + r.count, 0);\n }\n\n return {\n check(deleteCount, manifestSize) {\n if (deleteCount <= 0) return true;\n expire();\n // Fresh agent (no manifest entries) — nothing to lose, always allow.\n if (manifestSize === 0) {\n records.push({ ts: now(), count: deleteCount });\n return true;\n }\n const projected = sum() + deleteCount;\n if (projected / manifestSize > thresholdRatio) return false;\n records.push({ ts: now(), count: deleteCount });\n return true;\n },\n windowSum() {\n expire();\n return sum();\n },\n };\n}\n","/**\n * @alfe.ai/openclaw-sync — OpenClaw Sync plugin.\n *\n * Wraps the sync engine as a lifecycle-managed integration. Same shape as\n * @alfe.ai/openclaw-memory-cloud / -secrets / -google: one AgentApiClient\n * is constructed in `activate()` and reused for every API call. The agent's\n * identity is resolved server-side from the API key — the plugin never\n * touches `/auth/validate`, never plumbs an `agentId` around, never writes\n * a `.alfesync/` directory.\n *\n * Lifecycle:\n * - activate(api): construct client, start sync engine + watcher\n * - deactivate(api): stop watcher, drop relay connections, clean up\n * - configure(api, config): swap schedule/scope at runtime\n *\n * Registers gateway RPC methods: `sync.now`, `sync.status`.\n */\n\nimport { createRequire } from 'node:module';\nimport {\n resolveConfig as resolveAlfeConfig,\n configExists as alfeConfigExists,\n DEFAULT_SOCKET_PATH,\n DEFAULT_WORKSPACE_PATH,\n deriveServiceWsUrl,\n type ResolvedConfig as AlfeResolvedConfig,\n} from '@alfe.ai/config';\nimport {\n connectToDaemon,\n getActivationKey,\n guardedStart,\n resetActivation,\n type IpcClient,\n} from '@alfe.ai/openclaw-plugin-kit';\nimport { AgentApiClient, type SyncAgentInfo } from '@alfe.ai/agent-api-client';\n\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport {\n createSyncEngine,\n isRecoveryArtifact,\n type SyncEngine,\n type SyncResult,\n} from './sync-engine.js';\nimport { startWatcher } from './watcher.js';\nimport { createSharedSyncEngine, type SharedSyncEngine, type SharedScope } from './shared-sync.js';\nimport { createDeleteBrake, type DeleteBrake } from './delete-brake.js';\nimport { readManifest } from './manifest.js';\nimport { validatePrivateRelativePath } from './path-contract.js';\n\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json') as { version: string };\n\n// ── Constants ───────────────────────────────────────────────\nconst SYNC_CAPABILITIES = ['sync.push', 'sync.pull', 'sync.fullSync'] as const;\nconst SYNC_ACTIVATION_KEY = getActivationKey('sync');\nconst SYNC_RELAY_RECONNECT_BASE_MS = 1000;\nconst SYNC_RELAY_RECONNECT_MAX_MS = 30000;\nconst SYNC_RELAY_DEBOUNCE_MS = 500;\nconst SYNC_RELAY_MAX_MESSAGE_BYTES = 64 * 1024;\nconst SYNC_RELAY_HANDSHAKE_TIMEOUT_MS = 10_000;\nconst REMOTE_DELETE_SUPPRESSION_MS = 30_000;\n\n// ── Types ───────────────────────────────────────────────────\n\ninterface PluginLogger {\n info(msg: string): void;\n warn(msg: string): void;\n error(msg: string): void;\n debug(msg: string): void;\n}\n\ninterface PluginServiceContext {\n config: Record<string, unknown>;\n workspaceDir?: string;\n stateDir: string;\n logger: PluginLogger;\n}\n\ninterface PluginApi {\n logger: PluginLogger;\n registrationMode?: 'full' | 'setup-only' | 'setup-runtime' | 'cli-metadata';\n config?: SyncPluginConfig;\n registerGatewayMethod?: (name: string, handler: () => Promise<unknown>) => void;\n registerService(service: {\n id: string;\n start: (ctx: PluginServiceContext) => void | Promise<void>;\n stop?: (ctx: PluginServiceContext) => void | Promise<void>;\n }): void;\n}\n\ninterface SyncRelaySocket {\n on(event: string, handler: (...args: never[]) => void): void;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n}\n\ntype RelayMessage =\n | { type: 'PING' }\n | {\n type: 'SUBSCRIBE_ACK';\n status: 'ok' | 'error';\n agentId: string;\n message?: string;\n }\n | {\n type: 'FILE_CHANGED';\n agentId: string;\n filePath: string;\n etag?: string;\n eventType: 'created' | 'deleted';\n };\n\nexport interface SyncPluginConfig {\n /** Workspace path to sync. Defaults to the resolved Alfe workspace. */\n workspacePath?: string;\n /** Sync schedule — how often to auto-sync. */\n syncSchedule?: 'realtime' | 'hourly' | 'daily' | 'weekly';\n /** IPC socket path override. */\n socketPath?: string;\n /** Sync relay URL override (default: derived from apiUrl). */\n syncRelayUrl?: string;\n /** Enable shared file sync for org/team/project scopes. Default: true. */\n sharedSync?: boolean;\n}\n\n// ── Plugin State ────────────────────────────────────────────\n\nlet client: AgentApiClient | null = null;\nlet agentId: string | null = null;\nlet syncEngine: SyncEngine | null = null;\nlet sharedSyncEngine: SharedSyncEngine | null = null;\nlet stopWatcher: (() => Promise<void>) | null = null;\nlet deleteBrake: DeleteBrake | null = null;\nlet daemonIpcClient: IpcClient | null = null;\nlet scheduledInterval: ReturnType<typeof setInterval> | null = null;\nlet currentConfig: SyncPluginConfig = {};\nlet lastSyncResult: SyncResult | null = null;\nlet syncRelayWs: SyncRelaySocket | null = null;\nlet syncRelayReconnectTimer: ReturnType<typeof setTimeout> | null = null;\nlet syncRelayReconnectAttempt = 0;\nlet syncRelayDebounceTimer: ReturnType<typeof setTimeout> | null = null;\nlet syncRelayGeneration = 0;\nlet syncRelayActive = false;\nlet syncOperationTail: Promise<void> = Promise.resolve();\nconst syncRelayPendingPaths = new Map<\n string,\n { etag?: string; eventType: 'created' | 'deleted' }\n>();\nconst remoteDeleteSuppressions = new Map<string, number>();\n\nfunction enqueueSyncOperation<T>(operation: () => Promise<T>): Promise<T> {\n const result = syncOperationTail.then(operation, operation);\n syncOperationTail = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n}\n\nfunction parseRelayMessage(value: unknown): RelayMessage | null {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return null;\n const candidate = value as Record<string, unknown>;\n\n if (candidate.type === 'PING') return { type: 'PING' };\n\n if (\n candidate.type === 'SUBSCRIBE_ACK' &&\n (candidate.status === 'ok' || candidate.status === 'error') &&\n typeof candidate.agentId === 'string' &&\n candidate.agentId.length > 0 &&\n candidate.agentId.length <= 512 &&\n (candidate.message === undefined ||\n (typeof candidate.message === 'string' && candidate.message.length <= 1024))\n ) {\n return {\n type: 'SUBSCRIBE_ACK',\n status: candidate.status,\n agentId: candidate.agentId,\n ...(typeof candidate.message === 'string' ? { message: candidate.message } : {}),\n };\n }\n\n if (\n candidate.type === 'FILE_CHANGED' &&\n typeof candidate.agentId === 'string' &&\n candidate.agentId.length > 0 &&\n candidate.agentId.length <= 512 &&\n typeof candidate.filePath === 'string' &&\n (candidate.eventType === 'created' || candidate.eventType === 'deleted') &&\n (candidate.etag === undefined ||\n (typeof candidate.etag === 'string' && candidate.etag.length <= 256))\n ) {\n try {\n const sharedSegments = candidate.filePath.split('/');\n const isCanonicalSharedPath =\n sharedSegments[0] === 'shared' &&\n sharedSegments.length >= 3 &&\n !sharedSegments.some(\n (segment) => segment === '' || segment === '.' || segment === '..',\n );\n const filePath = isCanonicalSharedPath\n ? candidate.filePath\n : validatePrivateRelativePath(candidate.filePath);\n if (\n filePath.length > 1024 ||\n filePath.includes('\\0') ||\n filePath.includes('\\\\') ||\n ((filePath === 'shared' || filePath.startsWith('shared/')) && !isCanonicalSharedPath)\n ) {\n return null;\n }\n return {\n type: 'FILE_CHANGED',\n agentId: candidate.agentId,\n filePath,\n eventType: candidate.eventType,\n ...(typeof candidate.etag === 'string' ? { etag: candidate.etag } : {}),\n };\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\nfunction parseSharedScopes(value: unknown): SharedScope[] | null {\n if (!Array.isArray(value) || value.length > 1000) return null;\n const scopes: SharedScope[] = [];\n for (const item of value) {\n if (typeof item !== 'object' || item === null || Array.isArray(item)) return null;\n const candidate = item as Record<string, unknown>;\n if (\n (candidate.scopeType !== 'org' &&\n candidate.scopeType !== 'team' &&\n candidate.scopeType !== 'project') ||\n typeof candidate.scopeId !== 'string' ||\n candidate.scopeId.length === 0 ||\n candidate.scopeId.length > 512 ||\n candidate.scopeId === '.' ||\n candidate.scopeId === '..' ||\n candidate.scopeId.includes('/') ||\n candidate.scopeId.includes('\\\\') ||\n candidate.scopeId.includes('\\0') ||\n typeof candidate.name !== 'string' ||\n candidate.name.length > 512\n ) {\n return null;\n }\n scopes.push({\n scopeType: candidate.scopeType,\n scopeId: candidate.scopeId,\n name: candidate.name,\n });\n }\n return scopes;\n}\n\nasync function runFullSync(\n engine: SyncEngine,\n log: PluginLogger,\n label: string,\n): Promise<SyncResult | null> {\n return enqueueSyncOperation(async () => {\n if (syncEngine !== engine) return null;\n try {\n const result = await engine.fullSync({ quiet: true });\n lastSyncResult = result;\n log.info(\n `${label} complete: ${String(result.pushed)} pushed, ${String(result.pulled)} pulled`,\n );\n return result;\n } catch (err: unknown) {\n log.error(`${label} failed: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n });\n}\n\nasync function handleRealtimeChanges(\n paths: string[],\n workspacePath: string,\n log: PluginLogger,\n): Promise<void> {\n const engine = syncEngine;\n const brake = deleteBrake;\n if (!engine || !brake) return;\n\n await enqueueSyncOperation(async () => {\n if (syncEngine !== engine || deleteBrake !== brake) return;\n const existing: string[] = [];\n const missing: string[] = [];\n const now = Date.now();\n\n for (const [suppressedPath, expiresAt] of remoteDeleteSuppressions) {\n if (expiresAt <= now) remoteDeleteSuppressions.delete(suppressedPath);\n }\n\n for (const candidate of paths) {\n let path: string;\n try {\n path = validatePrivateRelativePath(candidate);\n } catch {\n log.warn('Realtime sync ignored an invalid workspace path');\n continue;\n }\n\n if (existsSync(join(workspacePath, path))) {\n remoteDeleteSuppressions.delete(path);\n existing.push(path);\n } else if ((remoteDeleteSuppressions.get(path) ?? 0) > now) {\n remoteDeleteSuppressions.delete(path);\n } else {\n missing.push(path);\n }\n }\n\n log.debug(\n `Realtime sync: ${String(existing.length)} upload(s), ${String(missing.length)} delete(s)`,\n );\n if (existing.length > 0) {\n try {\n lastSyncResult = await engine.push(existing, { quiet: true });\n } catch (err: unknown) {\n log.error(`Realtime push failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n if (missing.length === 0) return;\n\n const artifactDeletes = missing.filter((path) => isRecoveryArtifact(path));\n const regularDeletes = missing.filter((path) => !isRecoveryArtifact(path));\n const manifest = await readManifest(workspacePath);\n const manifestSize = Object.keys(manifest.files).length;\n let toDelete = missing;\n if (!brake.check(regularDeletes.length, manifestSize)) {\n log.error(\n `Sync delete brake tripped: ${String(brake.windowSum() + regularDeletes.length)} ` +\n `deletes in last 60s vs manifest size ${String(manifestSize)} ` +\n `(threshold 30%). Refusing batch — investigate the workspace state.`,\n );\n toDelete = artifactDeletes;\n }\n if (toDelete.length > 0) {\n try {\n lastSyncResult = await engine.pushDeletes(toDelete, { quiet: true });\n } catch (err: unknown) {\n log.error(`Realtime delete failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n });\n}\n\n// ── Schedule Helpers ────────────────────────────────────────\n\nconst SCHEDULE_INTERVALS_MS: Record<string, number> = {\n hourly: 60 * 60 * 1000,\n daily: 24 * 60 * 60 * 1000,\n weekly: 7 * 24 * 60 * 60 * 1000,\n};\n\nfunction clearSchedule() {\n if (scheduledInterval) {\n clearInterval(scheduledInterval);\n scheduledInterval = null;\n }\n}\n\nfunction setupSchedule(schedule: string, log: PluginLogger) {\n clearSchedule();\n if (schedule === 'realtime') {\n log.info('Sync schedule: realtime (file watcher active)');\n return;\n }\n\n const intervalMs = SCHEDULE_INTERVALS_MS[schedule];\n if (!intervalMs) {\n log.warn(`Unknown sync schedule: ${schedule}, defaulting to hourly`);\n setupSchedule('hourly', log);\n return;\n }\n\n log.info(`Sync schedule: ${schedule} (every ${String(intervalMs / 1000)}s)`);\n scheduledInterval = setInterval(() => {\n if (!syncEngine) return;\n const engine = syncEngine;\n log.info(`Scheduled sync (${schedule}) starting...`);\n void runFullSync(engine, log, 'Scheduled sync');\n }, intervalMs);\n scheduledInterval.unref();\n}\n\n// ── Daemon IPC ──────────────────────────────────────────────\n// (connection via @alfe.ai/openclaw-plugin-kit's connectToDaemon; the\n// SYNC_NOW / SHARED_SCOPES message handling stays here)\n\nfunction handleDaemonMessage(msg: Record<string, unknown>, log: PluginLogger): void {\n if (msg.type === 'SYNC_NOW' || msg.command === 'SYNC_NOW') {\n log.info('Received SYNC_NOW command — triggering immediate sync...');\n if (syncEngine) {\n const engine = syncEngine;\n void runFullSync(engine, log, 'SYNC_NOW');\n }\n }\n\n if (msg.type === 'SHARED_SCOPES') {\n const scopes = parseSharedScopes(msg.scopes);\n const engine = sharedSyncEngine;\n if (!scopes) {\n log.warn('Ignored invalid SHARED_SCOPES payload from daemon');\n } else if (engine) {\n log.info(`Received SHARED_SCOPES update: ${String(scopes.length)} scope(s)`);\n void enqueueSyncOperation(async () => {\n if (sharedSyncEngine !== engine) return;\n try {\n await engine.updateScopes(scopes);\n } catch (err: unknown) {\n log.error(\n `SHARED_SCOPES update failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n });\n }\n }\n}\n\n// ── Sync Relay Connection ───────────────────────────────────\n\nfunction clearSyncRelayReconnect() {\n if (syncRelayReconnectTimer) {\n clearTimeout(syncRelayReconnectTimer);\n syncRelayReconnectTimer = null;\n }\n}\n\nfunction clearSyncRelayDebounce() {\n if (syncRelayDebounceTimer) {\n clearTimeout(syncRelayDebounceTimer);\n syncRelayDebounceTimer = null;\n }\n}\n\nasync function processPendingNotifications(log: PluginLogger) {\n if (syncRelayPendingPaths.size === 0) return;\n\n const entries = new Map(syncRelayPendingPaths);\n syncRelayPendingPaths.clear();\n\n const sharedEntries = new Map<string, { etag?: string; eventType: 'created' | 'deleted' }>();\n const privateEntries = new Map<string, { etag?: string; eventType: 'created' | 'deleted' }>();\n\n for (const [filePath, info] of entries) {\n if (filePath.startsWith('shared/')) {\n sharedEntries.set(filePath, info);\n } else {\n privateEntries.set(filePath, info);\n }\n }\n\n if (sharedEntries.size > 0 && sharedSyncEngine) {\n for (const [filePath, info] of sharedEntries) {\n try {\n await sharedSyncEngine.handleNotification(filePath, info.eventType);\n } catch (err: unknown) {\n log.error(\n `Shared sync relay: failed ${info.eventType} ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n if (privateEntries.size > 0 && syncEngine) {\n const engine = syncEngine;\n const toPull: string[] = [];\n const toDelete: string[] = [];\n\n for (const [filePath, info] of privateEntries) {\n if (info.eventType === 'deleted') {\n toDelete.push(filePath);\n } else {\n toPull.push(filePath);\n }\n }\n\n for (const filePath of toDelete) {\n try {\n remoteDeleteSuppressions.set(filePath, Date.now() + REMOTE_DELETE_SUPPRESSION_MS);\n await engine.removeLocalFile(filePath, { quiet: true });\n log.debug(`Sync relay: deleted ${filePath}`);\n } catch (err: unknown) {\n log.error(\n `Sync relay: failed to delete ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n if (toPull.length > 0) {\n try {\n const result = await engine.pullFiles(toPull, { quiet: true });\n if (result.pulled > 0) log.info(`Sync relay: pulled ${String(result.pulled)} file(s)`);\n if (result.errors > 0) log.warn(`Sync relay: ${String(result.errors)} pull error(s)`);\n } catch (err: unknown) {\n log.error(`Sync relay: pull failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n}\n\nasync function connectToSyncRelay(\n relayUrl: string,\n token: string,\n agentIdForSubscribe: string,\n log: PluginLogger,\n generation: number,\n): Promise<SyncRelaySocket | null> {\n try {\n const { default: WebSocket } = await import('ws');\n if (!syncRelayActive || generation !== syncRelayGeneration) return null;\n\n const ws = new WebSocket(relayUrl, {\n headers: { Authorization: `Bearer ${token}` },\n handshakeTimeout: SYNC_RELAY_HANDSHAKE_TIMEOUT_MS,\n maxPayload: SYNC_RELAY_MAX_MESSAGE_BYTES,\n });\n\n ws.on('open', () => {\n if (!syncRelayActive || generation !== syncRelayGeneration) {\n ws.close(1000, 'Stale sync relay connection');\n return;\n }\n log.info('Connected to Sync Relay');\n syncRelayReconnectAttempt = 0;\n ws.send(JSON.stringify({ type: 'SUBSCRIBE', agentId: agentIdForSubscribe }));\n });\n\n ws.on('message', (data: Buffer | string) => {\n if (!syncRelayActive || generation !== syncRelayGeneration) return;\n const raw = data.toString();\n if (Buffer.byteLength(raw) > SYNC_RELAY_MAX_MESSAGE_BYTES) {\n ws.close(1009, 'Sync relay message too large');\n return;\n }\n let value: unknown;\n try {\n value = JSON.parse(raw) as unknown;\n } catch {\n return;\n }\n const message = parseRelayMessage(value);\n if (!message) return;\n\n switch (message.type) {\n case 'SUBSCRIBE_ACK':\n if (message.agentId !== agentIdForSubscribe) return;\n if (message.status === 'ok') {\n log.info(`Subscribed to sync notifications for agent ${message.agentId}`);\n const sharedEngine = sharedSyncEngine;\n if (sharedEngine) {\n void enqueueSyncOperation(async () => {\n if (sharedSyncEngine !== sharedEngine) return;\n try {\n await sharedEngine.fullSync();\n log.info('Shared sync: reconnect full sync complete');\n } catch (err: unknown) {\n log.error(\n `Shared sync: reconnect full sync failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n });\n }\n } else {\n log.warn(`Sync relay subscribe failed: ${message.message ?? 'unknown'}`);\n }\n break;\n\n case 'FILE_CHANGED':\n if (message.agentId !== agentIdForSubscribe) return;\n syncRelayPendingPaths.set(message.filePath, {\n etag: message.etag,\n eventType: message.eventType,\n });\n clearSyncRelayDebounce();\n syncRelayDebounceTimer = setTimeout(\n () => {\n void enqueueSyncOperation(() => processPendingNotifications(log));\n },\n SYNC_RELAY_DEBOUNCE_MS,\n );\n syncRelayDebounceTimer.unref();\n break;\n\n case 'PING':\n try {\n ws.send(JSON.stringify({ type: 'PONG' }));\n } catch {\n // ignore\n }\n break;\n }\n });\n\n ws.on('close', (code: number) => {\n if (syncRelayWs === ws) syncRelayWs = null;\n if (!syncRelayActive || generation !== syncRelayGeneration) return;\n log.info(`Sync Relay disconnected (code=${String(code)})`);\n scheduleSyncRelayReconnect(relayUrl, token, agentIdForSubscribe, log, generation);\n });\n\n ws.on('error', (err: Error) => {\n log.debug(`Sync Relay error: ${err.message}`);\n });\n\n return ws as SyncRelaySocket;\n } catch (err: unknown) {\n log.debug(`Failed to connect to Sync Relay: ${err instanceof Error ? err.message : String(err)}`);\n if (syncRelayActive && generation === syncRelayGeneration) {\n scheduleSyncRelayReconnect(relayUrl, token, agentIdForSubscribe, log, generation);\n }\n return null;\n }\n}\n\nfunction scheduleSyncRelayReconnect(\n relayUrl: string,\n token: string,\n agentIdForSubscribe: string,\n log: PluginLogger,\n generation: number,\n) {\n if (!syncRelayActive || generation !== syncRelayGeneration) return;\n clearSyncRelayReconnect();\n const delay = Math.min(\n SYNC_RELAY_RECONNECT_BASE_MS * Math.pow(2, syncRelayReconnectAttempt),\n SYNC_RELAY_RECONNECT_MAX_MS,\n );\n syncRelayReconnectAttempt++;\n log.debug(\n `Reconnecting to Sync Relay in ${String(delay)}ms (attempt ${String(syncRelayReconnectAttempt)})`,\n );\n\n syncRelayReconnectTimer = setTimeout(() => {\n void (async () => {\n const socket = await connectToSyncRelay(\n relayUrl,\n token,\n agentIdForSubscribe,\n log,\n generation,\n );\n if (syncRelayActive && generation === syncRelayGeneration) {\n syncRelayWs = socket;\n } else if (socket) {\n socket.close(1000, 'Stale sync relay connection');\n }\n })();\n }, delay);\n syncRelayReconnectTimer.unref();\n}\n\nfunction disconnectSyncRelay() {\n syncRelayActive = false;\n syncRelayGeneration++;\n clearSyncRelayReconnect();\n clearSyncRelayDebounce();\n syncRelayPendingPaths.clear();\n remoteDeleteSuppressions.clear();\n\n if (syncRelayWs) {\n try {\n if (agentId) {\n syncRelayWs.send(JSON.stringify({ type: 'UNSUBSCRIBE', agentId }));\n }\n syncRelayWs.close(1000, 'Plugin deactivating');\n } catch {\n // ignore\n }\n syncRelayWs = null;\n }\n}\n\n// ── Plugin Definition ───────────────────────────────────────\n\nconst plugin = {\n id: '@alfe.ai/openclaw-sync',\n name: 'Alfe Sync Plugin',\n description:\n 'Back up agent configuration, conversations, and memory to the cloud with scheduled or real-time sync.',\n version: pkg.version,\n\n activate(api: PluginApi) {\n const log = api.logger;\n\n const pluginConfig: SyncPluginConfig = api.config ?? {};\n currentConfig = pluginConfig;\n\n let alfeConfig: { workspacePath: string; socketPath: string; runtime: string } | null = null;\n try {\n alfeConfig = resolveAlfeConfig();\n } catch {\n // alfe login hasn't run; logged below\n }\n\n const workspacePath =\n pluginConfig.workspacePath ?? alfeConfig?.workspacePath ?? DEFAULT_WORKSPACE_PATH;\n // Active runtime selects the per-runtime default ignore set (openclaw npm/\n // extensions/ plugins/, hermes .venv/ hermes-agent/) so the watcher never\n // descends into runtime-internal dirs. Falls back to openclaw.\n const runtime = alfeConfig?.runtime ?? 'openclaw';\n\n // Default to 'realtime' when no schedule is configured. The integration's\n // user-config schema marks sync_schedule as required, so an unset value\n // here means the framework didn't deliver the user's choice (manifest\n // template missing, applyConfig errored, etc.). 'daily' would leave a\n // managed agent silent for 24 hours after activation; 'realtime' fails\n // open into the safest mode — initial push runs immediately and the\n // watcher streams ongoing changes.\n const syncSchedule = pluginConfig.syncSchedule ?? 'realtime';\n const socketPath =\n pluginConfig.socketPath ?? alfeConfig?.socketPath ?? DEFAULT_SOCKET_PATH;\n\n const startSyncService = () => {\n guardedStart(SYNC_ACTIVATION_KEY, log, async () => {\n\n log.info('Alfe Sync plugin activating...');\n // Sync is whole-home always: the watcher syncs all of the runtime HOME\n // (~/.openclaw) minus the ignore set. The ignore-list is the single\n // control surface for what syncs — there is deliberately no \"scope\"\n // option (removed 2026-08-14; see packages/openclaw-sync/DEVELOPING.md).\n log.info(`Sync schedule: ${syncSchedule}`);\n log.info(`Workspace: ${workspacePath}`);\n\n // Sync needs `~/.alfe/config.toml` (the existing `alfe login` config).\n // Identity (agentId / tenantId) is resolved server-side from the API\n // key on every AgentApiClient call.\n if (!alfeConfigExists()) {\n log.info('Sync skipped — no Alfe config found. Run `alfe login` to enable.');\n // Reset so a later activate() retries once `alfe login` has run.\n resetActivation(SYNC_ACTIVATION_KEY);\n return;\n }\n\n let syncCfg: AlfeResolvedConfig;\n try {\n syncCfg = resolveAlfeConfig();\n } catch (err: unknown) {\n log.warn(\n `Sync skipped — failed to resolve credentials from ~/.alfe/config.toml: ${err instanceof Error ? err.message : String(err)}`,\n );\n resetActivation(SYNC_ACTIVATION_KEY);\n return;\n }\n\n client = new AgentApiClient({ apiKey: syncCfg.apiKey, apiUrl: syncCfg.apiUrl });\n syncEngine = createSyncEngine({ workspacePath, client, runtime });\n log.info('Sync engine initialized');\n\n // Realtime mode → first-run reconcile of pre-existing files (chokidar\n // ignoreInitial means the watcher only emits for post-activation\n // changes; without this the workspace tree never reaches the cloud\n // unless a file is touched). This RECONCILES with cloud state rather\n // than blindly pushing: on a from-scratch VM rebuild the local\n // manifest is gone and `alfe setup` has just re-seeded the persona\n // templates, so a blind push(undefined) would upload those fresh seeds\n // OVER the agent's edited cloud copy and lose its edits. firstRunReconcile\n // restores cloud → local (cloud wins) when the cloud already holds\n // state, and only push-seeds files that are genuinely new locally. A\n // brand-new agent (empty cloud) still gets the push-only seed. See\n // sync-engine.ts firstRunReconcile for the fresh-vs-rebuild rationale.\n if (syncSchedule === 'realtime') {\n try {\n lastSyncResult = await syncEngine.firstRunReconcile({ quiet: true });\n if (lastSyncResult.conflicts > 0) {\n // `conflicts` counts every diverged local file the recovery\n // handled (sidecar written, identical sidecar already present,\n // or over the preservation size cap) — see RECOVERY-*.md for\n // the per-file breakdown.\n log.info(\n `Initial workspace reconcile complete — ${String(lastSyncResult.conflicts)} diverged local file(s) handled (see RECOVERY-*.md in the agent workspace)`,\n );\n } else {\n log.info('Initial workspace reconcile complete');\n }\n } catch (err: unknown) {\n log.warn(`Initial workspace reconcile failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n // Self-heal: purge cloud files that a pre-fix ignore engine uploaded\n // but the current per-runtime ignore set excludes (node_modules, the\n // openclaw npm cache, …). Bounded to ignored paths (capped per pass),\n // so legit files are never touched. Fire-and-forget so a large drain\n // can't stall watcher startup — the watcher already ignores these\n // paths, so they won't reappear, and the remainder clears next start.\n const engineForPrune = syncEngine;\n void enqueueSyncOperation(async () => {\n if (syncEngine !== engineForPrune) return null;\n return engineForPrune.pruneIgnored({ quiet: true });\n })\n .then((pruned) => {\n if (pruned && pruned.pushed > 0) {\n log.info(`Pruned ${String(pruned.pushed)} ignored file(s) from cloud`);\n }\n })\n .catch((err: unknown) => {\n log.warn(`Ignored-file prune failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n deleteBrake = createDeleteBrake();\n try {\n stopWatcher = await startWatcher({\n workspacePath,\n runtime,\n debounceMs: 2000,\n onChanges: (paths) => handleRealtimeChanges(paths, workspacePath, log),\n });\n log.info('File watcher started for realtime sync');\n } catch (err: unknown) {\n log.warn(`Failed to start file watcher: ${err instanceof Error ? err.message : String(err)}`);\n }\n } else {\n setupSchedule(syncSchedule, log);\n }\n\n // Daemon IPC (for SYNC_NOW + SHARED_SCOPES messages)\n daemonIpcClient = await connectToDaemon(socketPath, log, {\n pluginId: '@alfe.ai/openclaw-sync',\n capabilities: SYNC_CAPABILITIES,\n onMessage: (msg) => { handleDaemonMessage(msg, log); },\n standaloneNote: 'Alfe daemon not available — Sync plugin running standalone',\n });\n\n // Register with the sync service to materialize the SyncAgent record\n // and learn our own agentId for relay subscription.\n let registered: SyncAgentInfo | null = null;\n try {\n const result = await client.syncRegister();\n registered = result.agent;\n agentId = registered.agentId;\n } catch (err: unknown) {\n log.warn(`Sync register failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n // Sync relay (real-time file change notifications)\n if (registered) {\n // Create the shared engine before opening the relay. A fast\n // SUBSCRIBE_ACK triggers a shared full sync, so constructing it after\n // the socket introduced a race where the initial refresh was skipped.\n if (pluginConfig.sharedSync !== false) {\n try {\n sharedSyncEngine = createSharedSyncEngine({ workspacePath, client }, log);\n log.info('Shared sync engine created — waiting for SHARED_SCOPES from gateway');\n } catch (err: unknown) {\n log.debug(\n `Shared sync engine skipped: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n try {\n const relayUrl =\n pluginConfig.syncRelayUrl ?? deriveServiceWsUrl(syncCfg.apiUrl, 'sync');\n syncRelayActive = true;\n const relayGeneration = ++syncRelayGeneration;\n syncRelayWs = await connectToSyncRelay(\n relayUrl,\n syncCfg.apiKey,\n registered.agentId,\n log,\n relayGeneration,\n );\n } catch (err: unknown) {\n log.debug(\n `Sync Relay connection skipped: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n }\n\n });\n };\n\n const stopSyncService = async () => {\n // Stop side effects BEFORE resetting the activation flag (§E ordering).\n clearSchedule();\n disconnectSyncRelay();\n deleteBrake = null;\n\n if (stopWatcher) {\n try {\n await stopWatcher();\n log.info('File watcher stopped');\n } catch (err: unknown) {\n log.debug(`Error stopping watcher: ${err instanceof Error ? err.message : String(err)}`);\n }\n stopWatcher = null;\n }\n\n if (daemonIpcClient) {\n try {\n daemonIpcClient.stop();\n log.info('Disconnected from Alfe daemon');\n } catch (err: unknown) {\n log.debug(`Error disconnecting from daemon: ${err instanceof Error ? err.message : String(err)}`);\n }\n daemonIpcClient = null;\n }\n\n client = null;\n agentId = null;\n syncEngine = null;\n sharedSyncEngine = null;\n lastSyncResult = null;\n currentConfig = {};\n\n resetActivation(SYNC_ACTIVATION_KEY);\n log.info('Alfe Sync plugin deactivated');\n };\n\n if (typeof api.registerGatewayMethod === 'function') {\n api.registerGatewayMethod('sync.now', async () => {\n if (!syncEngine) {\n return { ok: false, error: 'Sync engine not initialized — run `alfe login`' };\n }\n try {\n const engine = syncEngine;\n const result = await runFullSync(engine, log, 'sync.now');\n return result\n ? { ok: true, result }\n : { ok: false, error: 'Sync did not complete' };\n } catch (err: unknown) {\n return { ok: false, error: err instanceof Error ? err.message : String(err) };\n }\n });\n log.info('Registered gateway RPC method: sync.now');\n\n api.registerGatewayMethod('sync.status', () =>\n Promise.resolve({\n ok: true,\n initialized: !!syncEngine,\n agentId,\n schedule: currentConfig.syncSchedule ?? 'realtime',\n lastResult: lastSyncResult,\n watcherActive: !!stopWatcher,\n }),\n );\n log.info('Registered gateway RPC method: sync.status');\n }\n\n api.registerService({\n id: 'alfe-sync-engine',\n start: () => { startSyncService(); },\n stop: () => stopSyncService(),\n });\n\n log.info('Alfe Sync plugin activated');\n },\n\n async deactivate(api: PluginApi) {\n const log = api.logger;\n log.info('Alfe Sync plugin deactivating...');\n\n // Stop side effects BEFORE resetting the activation flag (§E ordering).\n clearSchedule();\n disconnectSyncRelay();\n deleteBrake = null;\n\n if (stopWatcher) {\n try {\n await stopWatcher();\n log.info('File watcher stopped');\n } catch (err: unknown) {\n log.debug(`Error stopping watcher: ${err instanceof Error ? err.message : String(err)}`);\n }\n stopWatcher = null;\n }\n\n if (daemonIpcClient) {\n try {\n daemonIpcClient.stop();\n log.info('Disconnected from Alfe daemon');\n } catch (err: unknown) {\n log.debug(`Error disconnecting from daemon: ${err instanceof Error ? err.message : String(err)}`);\n }\n daemonIpcClient = null;\n }\n\n client = null;\n agentId = null;\n syncEngine = null;\n sharedSyncEngine = null;\n lastSyncResult = null;\n currentConfig = {};\n\n resetActivation(SYNC_ACTIVATION_KEY);\n log.info('Alfe Sync plugin deactivated');\n },\n\n async configure(api: PluginApi, config: SyncPluginConfig) {\n const log = api.logger;\n log.info('Reconfiguring Alfe Sync plugin...');\n currentConfig = { ...currentConfig, ...config };\n\n if (config.syncSchedule) {\n if (config.syncSchedule === 'realtime' && syncEngine && !stopWatcher) {\n clearSchedule();\n const engine = syncEngine;\n try {\n const reconciled = await enqueueSyncOperation(async () => {\n if (syncEngine !== engine) return null;\n return engine.firstRunReconcile({ quiet: true });\n });\n if (reconciled) lastSyncResult = reconciled;\n } catch (err: unknown) {\n log.warn(\n `Realtime reconfigure reconcile failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n const workspacePath = currentConfig.workspacePath ?? syncEngine.workspacePath;\n deleteBrake = createDeleteBrake();\n stopWatcher = await startWatcher({\n workspacePath,\n runtime: syncEngine.runtime,\n debounceMs: 2000,\n onChanges: (paths) => handleRealtimeChanges(paths, workspacePath, log),\n });\n log.info('Switched to realtime sync');\n } else if (config.syncSchedule !== 'realtime') {\n deleteBrake = null;\n if (stopWatcher) {\n await stopWatcher();\n stopWatcher = null;\n }\n setupSchedule(config.syncSchedule, log);\n }\n }\n\n log.info('Alfe Sync plugin reconfigured');\n },\n};\n\nexport default plugin;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/openclaw-sync",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "AlfeSync — agent workspace backup and sync skill for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",