@alfe.ai/openclaw-sync 0.0.16 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.cjs +45 -63
- package/dist/cli/index.js +44 -62
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +4 -10
- package/dist/index.d.cts +955 -202
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +955 -202
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/plugin.d.cts +16 -18
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.ts +16 -18
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin2.cjs +118 -109
- package/dist/plugin2.js +117 -108
- package/dist/plugin2.js.map +1 -1
- package/dist/sync-engine.cjs +248 -372
- package/dist/sync-engine.js +207 -348
- package/dist/sync-engine.js.map +1 -1
- package/package.json +2 -1
- package/dist/ignore.cjs +0 -120
- package/dist/ignore.js +0 -74
- package/dist/ignore.js.map +0 -1
package/dist/plugin2.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { f as isInitialized, m as resolveSyncConfig, t as createSyncEngine } from "./sync-engine.js";
|
|
1
|
+
import { o as loadIgnorePatterns, s as shouldIgnore, t as createSyncEngine } from "./sync-engine.js";
|
|
3
2
|
import { createRequire } from "node:module";
|
|
4
|
-
import { dirname, join, normalize, relative, sep } from "node:path";
|
|
5
|
-
import { DEFAULT_SOCKET_PATH, DEFAULT_WORKSPACE_PATH, resolveConfig } from "@alfe.ai/config";
|
|
6
3
|
import { mkdir, rm, unlink, writeFile } from "node:fs/promises";
|
|
4
|
+
import { dirname, join, normalize, relative, sep } from "node:path";
|
|
7
5
|
import { watch } from "chokidar";
|
|
6
|
+
import { DEFAULT_SOCKET_PATH, DEFAULT_WORKSPACE_PATH, configExists, resolveConfig } from "@alfe.ai/config";
|
|
7
|
+
import { AgentApiClient } from "@alfe.ai/agent-api-client";
|
|
8
8
|
//#region src/watcher.ts
|
|
9
9
|
/**
|
|
10
10
|
* AlfeSync watcher — recursive file watcher with debounce and ignore support.
|
|
@@ -73,36 +73,23 @@ async function startWatcher(options) {
|
|
|
73
73
|
//#endregion
|
|
74
74
|
//#region src/shared-sync.ts
|
|
75
75
|
/**
|
|
76
|
-
* Shared file sync
|
|
76
|
+
* Shared file sync — mirrors org/team/project files to a `shared/`
|
|
77
|
+
* directory in the agent's workspace, organised by scope:
|
|
77
78
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
79
|
+
* shared/org/<files…>
|
|
80
|
+
* shared/teams/<scopeId>/<files…>
|
|
81
|
+
* shared/projects/<scopeId>/<files…>
|
|
80
82
|
*
|
|
81
|
-
*
|
|
83
|
+
* Backed by the agent self-service API (`AgentApiClient.sharedListFiles`,
|
|
84
|
+
* `AgentApiClient.sharedDownloadUrl`).
|
|
82
85
|
*/
|
|
83
86
|
const MAX_SHARED_FILE_SIZE = 100 * 1024 * 1024;
|
|
84
|
-
/**
|
|
87
|
+
/** Throw if `resolvedPath` would escape `baseDir`. */
|
|
85
88
|
function assertContained(baseDir, resolvedPath) {
|
|
86
89
|
const normalizedBase = normalize(baseDir) + sep;
|
|
87
90
|
const normalizedPath = normalize(resolvedPath);
|
|
88
91
|
if (!normalizedPath.startsWith(normalizedBase) && normalizedPath !== normalize(baseDir)) throw new Error(`Path traversal blocked: ${resolvedPath} escapes ${baseDir}`);
|
|
89
92
|
}
|
|
90
|
-
async function fetchJson(url, token) {
|
|
91
|
-
const response = await fetch(url, { headers: {
|
|
92
|
-
"Authorization": `Bearer ${token}`,
|
|
93
|
-
"Content-Type": "application/json"
|
|
94
|
-
} });
|
|
95
|
-
if (!response.ok) {
|
|
96
|
-
let errorBody = "";
|
|
97
|
-
try {
|
|
98
|
-
errorBody = await response.text();
|
|
99
|
-
} catch {
|
|
100
|
-
errorBody = "(unable to read error body)";
|
|
101
|
-
}
|
|
102
|
-
throw new Error(`HTTP ${String(response.status)}: ${errorBody}`);
|
|
103
|
-
}
|
|
104
|
-
return response.json();
|
|
105
|
-
}
|
|
106
93
|
function createSharedSyncEngine(config, log) {
|
|
107
94
|
let activeScopes = [];
|
|
108
95
|
const sharedDir = join(config.workspacePath, "shared");
|
|
@@ -110,16 +97,14 @@ function createSharedSyncEngine(config, log) {
|
|
|
110
97
|
if (scope.scopeType === "org") return join(sharedDir, "org");
|
|
111
98
|
return join(sharedDir, scope.scopeType === "team" ? "teams" : "projects", scope.scopeId);
|
|
112
99
|
}
|
|
113
|
-
function apiBase() {
|
|
114
|
-
return `${config.apiUrl}/agents/org`;
|
|
115
|
-
}
|
|
116
|
-
async function listRemoteFiles(scope) {
|
|
117
|
-
return (await fetchJson(`${apiBase()}/files/${scope.scopeType}/${scope.scopeId}`, config.token)).files;
|
|
118
|
-
}
|
|
119
100
|
async function downloadFile(scope, filePath, localPath) {
|
|
120
101
|
assertContained(scopeDir(scope), localPath);
|
|
121
|
-
const
|
|
122
|
-
|
|
102
|
+
const { downloadUrl } = await config.client.sharedDownloadUrl({
|
|
103
|
+
scope: scope.scopeType,
|
|
104
|
+
scopeId: scope.scopeId,
|
|
105
|
+
filePath
|
|
106
|
+
});
|
|
107
|
+
const response = await fetch(downloadUrl);
|
|
123
108
|
if (!response.ok) throw new Error(`Download failed: HTTP ${String(response.status)}`);
|
|
124
109
|
const contentLength = parseInt(response.headers.get("content-length") ?? "0", 10);
|
|
125
110
|
if (contentLength > MAX_SHARED_FILE_SIZE) throw new Error(`File too large: ${String(contentLength)} bytes exceeds ${String(MAX_SHARED_FILE_SIZE)} limit`);
|
|
@@ -132,8 +117,11 @@ function createSharedSyncEngine(config, log) {
|
|
|
132
117
|
const dir = scopeDir(scope);
|
|
133
118
|
await mkdir(dir, { recursive: true });
|
|
134
119
|
try {
|
|
135
|
-
const
|
|
136
|
-
|
|
120
|
+
const { files } = await config.client.sharedListFiles({
|
|
121
|
+
scope: scope.scopeType,
|
|
122
|
+
scopeId: scope.scopeId
|
|
123
|
+
});
|
|
124
|
+
for (const file of files) {
|
|
137
125
|
const localPath = join(dir, file.filePath);
|
|
138
126
|
try {
|
|
139
127
|
await downloadFile(scope, file.filePath, localPath);
|
|
@@ -251,16 +239,19 @@ function createSharedSyncEngine(config, log) {
|
|
|
251
239
|
/**
|
|
252
240
|
* @alfe.ai/openclaw-sync — OpenClaw Sync plugin.
|
|
253
241
|
*
|
|
254
|
-
* Wraps the
|
|
255
|
-
*
|
|
256
|
-
*
|
|
242
|
+
* Wraps the sync engine as a lifecycle-managed integration. Same shape as
|
|
243
|
+
* @alfe.ai/openclaw-memory-cloud / -secrets / -google: one AgentApiClient
|
|
244
|
+
* is constructed in `activate()` and reused for every API call. The agent's
|
|
245
|
+
* identity is resolved server-side from the API key — the plugin never
|
|
246
|
+
* touches `/auth/validate`, never plumbs an `agentId` around, never writes
|
|
247
|
+
* a `.alfesync/` directory.
|
|
257
248
|
*
|
|
258
249
|
* Lifecycle:
|
|
259
|
-
* - activate(api): start
|
|
260
|
-
* - deactivate(api): stop
|
|
261
|
-
* - configure(api, config):
|
|
250
|
+
* - activate(api): construct client, start sync engine + watcher
|
|
251
|
+
* - deactivate(api): stop watcher, drop relay connections, clean up
|
|
252
|
+
* - configure(api, config): swap schedule/scope at runtime
|
|
262
253
|
*
|
|
263
|
-
* Registers
|
|
254
|
+
* Registers gateway RPC methods: `sync.now`, `sync.status`.
|
|
264
255
|
*/
|
|
265
256
|
const pkg = createRequire(import.meta.url)("../package.json");
|
|
266
257
|
const SYNC_CAPABILITIES = [
|
|
@@ -271,6 +262,8 @@ const SYNC_CAPABILITIES = [
|
|
|
271
262
|
const SYNC_RELAY_RECONNECT_BASE_MS = 1e3;
|
|
272
263
|
const SYNC_RELAY_RECONNECT_MAX_MS = 3e4;
|
|
273
264
|
const SYNC_RELAY_DEBOUNCE_MS = 500;
|
|
265
|
+
let client = null;
|
|
266
|
+
let agentId = null;
|
|
274
267
|
let syncEngine = null;
|
|
275
268
|
let sharedSyncEngine = null;
|
|
276
269
|
let stopWatcher = null;
|
|
@@ -323,12 +316,11 @@ function setupSchedule(schedule, log) {
|
|
|
323
316
|
}
|
|
324
317
|
async function connectToDaemon(socketPath, log) {
|
|
325
318
|
try {
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
client.on("connected", () => {
|
|
319
|
+
const ipc = new (await (import("@alfe.ai/openclaw"))).IPCClient(socketPath, log);
|
|
320
|
+
ipc.on("connected", () => {
|
|
329
321
|
(async () => {
|
|
330
322
|
log.info("Connected to Alfe daemon — registering sync capabilities...");
|
|
331
|
-
const response = await
|
|
323
|
+
const response = await ipc.request("capability.register", {
|
|
332
324
|
plugin: "@alfe.ai/openclaw-sync",
|
|
333
325
|
capabilities: [...SYNC_CAPABILITIES]
|
|
334
326
|
});
|
|
@@ -336,11 +328,11 @@ async function connectToDaemon(socketPath, log) {
|
|
|
336
328
|
else log.warn(`Failed to register sync capabilities: ${response.error?.message ?? "unknown"}`);
|
|
337
329
|
})();
|
|
338
330
|
});
|
|
339
|
-
|
|
331
|
+
ipc.on("disconnected", (...args) => {
|
|
340
332
|
const reason = typeof args[0] === "string" ? args[0] : String(args[0]);
|
|
341
333
|
log.warn(`Disconnected from Alfe daemon: ${reason}`);
|
|
342
334
|
});
|
|
343
|
-
|
|
335
|
+
ipc.on("message", (...args) => {
|
|
344
336
|
const msg = args[0];
|
|
345
337
|
if (msg?.type === "SYNC_NOW" || msg?.command === "SYNC_NOW") {
|
|
346
338
|
log.info("Received SYNC_NOW command — triggering immediate sync...");
|
|
@@ -371,12 +363,12 @@ async function connectToDaemon(socketPath, log) {
|
|
|
371
363
|
}
|
|
372
364
|
}
|
|
373
365
|
});
|
|
374
|
-
|
|
366
|
+
ipc.on("error", (...args) => {
|
|
375
367
|
const err = args[0];
|
|
376
368
|
log.debug(`Daemon IPC error: ${err instanceof Error ? err.message : String(err)}`);
|
|
377
369
|
});
|
|
378
|
-
|
|
379
|
-
return
|
|
370
|
+
ipc.start();
|
|
371
|
+
return ipc;
|
|
380
372
|
} catch {
|
|
381
373
|
log.info("Alfe daemon not available — Sync plugin running standalone");
|
|
382
374
|
return null;
|
|
@@ -428,7 +420,7 @@ async function processPendingNotifications(log) {
|
|
|
428
420
|
}
|
|
429
421
|
}
|
|
430
422
|
}
|
|
431
|
-
async function connectToSyncRelay(relayUrl, token,
|
|
423
|
+
async function connectToSyncRelay(relayUrl, token, agentIdForSubscribe, log) {
|
|
432
424
|
try {
|
|
433
425
|
const { default: WebSocket } = await import("ws");
|
|
434
426
|
const ws = new WebSocket(`${relayUrl}?token=${encodeURIComponent(token)}`);
|
|
@@ -437,7 +429,7 @@ async function connectToSyncRelay(relayUrl, token, agentId, log) {
|
|
|
437
429
|
syncRelayReconnectAttempt = 0;
|
|
438
430
|
ws.send(JSON.stringify({
|
|
439
431
|
type: "SUBSCRIBE",
|
|
440
|
-
agentId
|
|
432
|
+
agentId: agentIdForSubscribe
|
|
441
433
|
}));
|
|
442
434
|
});
|
|
443
435
|
ws.on("message", (data) => {
|
|
@@ -450,7 +442,7 @@ async function connectToSyncRelay(relayUrl, token, agentId, log) {
|
|
|
450
442
|
switch (message.type) {
|
|
451
443
|
case "SUBSCRIBE_ACK":
|
|
452
444
|
if (message.status === "ok") {
|
|
453
|
-
log.info(`Subscribed to sync notifications for agent ${message.agentId ??
|
|
445
|
+
log.info(`Subscribed to sync notifications for agent ${message.agentId ?? agentIdForSubscribe}`);
|
|
454
446
|
const sharedEngine = sharedSyncEngine;
|
|
455
447
|
if (sharedEngine) (async () => {
|
|
456
448
|
try {
|
|
@@ -468,9 +460,7 @@ async function connectToSyncRelay(relayUrl, token, agentId, log) {
|
|
|
468
460
|
eventType: message.eventType === "deleted" ? "deleted" : "created"
|
|
469
461
|
});
|
|
470
462
|
clearSyncRelayDebounce();
|
|
471
|
-
syncRelayDebounceTimer = setTimeout(() =>
|
|
472
|
-
processPendingNotifications(log);
|
|
473
|
-
}, SYNC_RELAY_DEBOUNCE_MS);
|
|
463
|
+
syncRelayDebounceTimer = setTimeout(() => void processPendingNotifications(log), SYNC_RELAY_DEBOUNCE_MS);
|
|
474
464
|
break;
|
|
475
465
|
case "PING":
|
|
476
466
|
try {
|
|
@@ -482,7 +472,7 @@ async function connectToSyncRelay(relayUrl, token, agentId, log) {
|
|
|
482
472
|
ws.on("close", (code) => {
|
|
483
473
|
log.info(`Sync Relay disconnected (code=${String(code)})`);
|
|
484
474
|
syncRelayWs = null;
|
|
485
|
-
scheduleSyncRelayReconnect(relayUrl, token,
|
|
475
|
+
scheduleSyncRelayReconnect(relayUrl, token, agentIdForSubscribe, log);
|
|
486
476
|
});
|
|
487
477
|
ws.on("error", (err) => {
|
|
488
478
|
log.debug(`Sync Relay error: ${err.message}`);
|
|
@@ -490,18 +480,18 @@ async function connectToSyncRelay(relayUrl, token, agentId, log) {
|
|
|
490
480
|
return ws;
|
|
491
481
|
} catch (err) {
|
|
492
482
|
log.debug(`Failed to connect to Sync Relay: ${err instanceof Error ? err.message : String(err)}`);
|
|
493
|
-
scheduleSyncRelayReconnect(relayUrl, token,
|
|
483
|
+
scheduleSyncRelayReconnect(relayUrl, token, agentIdForSubscribe, log);
|
|
494
484
|
return null;
|
|
495
485
|
}
|
|
496
486
|
}
|
|
497
|
-
function scheduleSyncRelayReconnect(relayUrl, token,
|
|
487
|
+
function scheduleSyncRelayReconnect(relayUrl, token, agentIdForSubscribe, log) {
|
|
498
488
|
clearSyncRelayReconnect();
|
|
499
489
|
const delay = Math.min(SYNC_RELAY_RECONNECT_BASE_MS * Math.pow(2, syncRelayReconnectAttempt), SYNC_RELAY_RECONNECT_MAX_MS);
|
|
500
490
|
syncRelayReconnectAttempt++;
|
|
501
491
|
log.debug(`Reconnecting to Sync Relay in ${String(delay)}ms (attempt ${String(syncRelayReconnectAttempt)})`);
|
|
502
492
|
syncRelayReconnectTimer = setTimeout(() => {
|
|
503
493
|
(async () => {
|
|
504
|
-
syncRelayWs = await connectToSyncRelay(relayUrl, token,
|
|
494
|
+
syncRelayWs = await connectToSyncRelay(relayUrl, token, agentIdForSubscribe, log);
|
|
505
495
|
})();
|
|
506
496
|
}, delay);
|
|
507
497
|
}
|
|
@@ -517,6 +507,12 @@ function disconnectSyncRelay() {
|
|
|
517
507
|
syncRelayWs = null;
|
|
518
508
|
}
|
|
519
509
|
}
|
|
510
|
+
function deriveRelayUrl(apiUrl) {
|
|
511
|
+
if (apiUrl.includes("dev.alfe.ai")) return "wss://sync.dev.alfe.ai/ws";
|
|
512
|
+
if (apiUrl.includes("demo.alfe.ai")) return "wss://sync.demo.alfe.ai/ws";
|
|
513
|
+
if (apiUrl.includes("test.alfe.ai")) return "wss://sync.test.alfe.ai/ws";
|
|
514
|
+
return "wss://sync.alfe.ai/ws";
|
|
515
|
+
}
|
|
520
516
|
const plugin = {
|
|
521
517
|
id: "@alfe.ai/openclaw-sync",
|
|
522
518
|
name: "Alfe Sync Plugin",
|
|
@@ -548,14 +544,27 @@ const plugin = {
|
|
|
548
544
|
log.info(`Sync scope: ${syncScope.join(", ")}`);
|
|
549
545
|
log.info(`Sync schedule: ${syncSchedule}`);
|
|
550
546
|
log.info(`Workspace: ${workspacePath}`);
|
|
551
|
-
if (
|
|
552
|
-
|
|
553
|
-
|
|
547
|
+
if (!configExists()) {
|
|
548
|
+
log.info("Sync skipped — no Alfe config found. Run `alfe login` to enable.");
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
let syncCfg;
|
|
552
|
+
try {
|
|
553
|
+
syncCfg = resolveConfig();
|
|
554
554
|
} catch (err) {
|
|
555
|
-
log.warn(`
|
|
555
|
+
log.warn(`Sync skipped — failed to resolve credentials from ~/.alfe/config.toml: ${err instanceof Error ? err.message : String(err)}`);
|
|
556
|
+
return;
|
|
556
557
|
}
|
|
557
|
-
|
|
558
|
-
|
|
558
|
+
client = new AgentApiClient({
|
|
559
|
+
apiKey: syncCfg.apiKey,
|
|
560
|
+
apiUrl: syncCfg.apiUrl
|
|
561
|
+
});
|
|
562
|
+
syncEngine = createSyncEngine({
|
|
563
|
+
workspacePath,
|
|
564
|
+
client
|
|
565
|
+
});
|
|
566
|
+
log.info("Sync engine initialized");
|
|
567
|
+
if (syncSchedule === "realtime") try {
|
|
559
568
|
stopWatcher = await startWatcher({
|
|
560
569
|
workspacePath,
|
|
561
570
|
debounceMs: 2e3,
|
|
@@ -573,28 +582,29 @@ const plugin = {
|
|
|
573
582
|
} catch (err) {
|
|
574
583
|
log.warn(`Failed to start file watcher: ${err instanceof Error ? err.message : String(err)}`);
|
|
575
584
|
}
|
|
576
|
-
|
|
585
|
+
else setupSchedule(syncSchedule, log);
|
|
577
586
|
daemonIpcClient = await connectToDaemon(socketPath, log);
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
|
|
587
|
+
let registered = null;
|
|
588
|
+
try {
|
|
589
|
+
registered = (await client.syncRegister()).agent;
|
|
590
|
+
agentId = registered.agentId;
|
|
591
|
+
} catch (err) {
|
|
592
|
+
log.warn(`Sync register failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
593
|
+
}
|
|
594
|
+
if (registered) {
|
|
595
|
+
try {
|
|
596
|
+
syncRelayWs = await connectToSyncRelay(pluginConfig.syncRelayUrl ?? deriveRelayUrl(syncCfg.apiUrl), syncCfg.apiKey, registered.agentId, log);
|
|
597
|
+
} catch (err) {
|
|
598
|
+
log.debug(`Sync Relay connection skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
599
|
+
}
|
|
600
|
+
if (pluginConfig.sharedSync !== false) try {
|
|
601
|
+
sharedSyncEngine = createSharedSyncEngine({
|
|
602
|
+
workspacePath,
|
|
603
|
+
client
|
|
604
|
+
}, log);
|
|
605
|
+
log.info("Shared sync engine created — waiting for SHARED_SCOPES from gateway");
|
|
606
|
+
} catch (err) {
|
|
607
|
+
log.debug(`Shared sync engine skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
598
608
|
}
|
|
599
609
|
}
|
|
600
610
|
};
|
|
@@ -620,6 +630,8 @@ const plugin = {
|
|
|
620
630
|
}
|
|
621
631
|
daemonIpcClient = null;
|
|
622
632
|
}
|
|
633
|
+
client = null;
|
|
634
|
+
agentId = null;
|
|
623
635
|
syncEngine = null;
|
|
624
636
|
sharedSyncEngine = null;
|
|
625
637
|
lastSyncResult = null;
|
|
@@ -630,7 +642,7 @@ const plugin = {
|
|
|
630
642
|
api.registerGatewayMethod("sync.now", async () => {
|
|
631
643
|
if (!syncEngine) return {
|
|
632
644
|
ok: false,
|
|
633
|
-
error: "Sync engine not initialized — run
|
|
645
|
+
error: "Sync engine not initialized — run `alfe login`"
|
|
634
646
|
};
|
|
635
647
|
try {
|
|
636
648
|
lastSyncResult = await syncEngine.fullSync({ quiet: true });
|
|
@@ -646,20 +658,19 @@ const plugin = {
|
|
|
646
658
|
}
|
|
647
659
|
});
|
|
648
660
|
log.info("Registered gateway RPC method: sync.now");
|
|
649
|
-
api.registerGatewayMethod("sync.status", () => {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
});
|
|
661
|
+
api.registerGatewayMethod("sync.status", () => Promise.resolve({
|
|
662
|
+
ok: true,
|
|
663
|
+
initialized: !!syncEngine,
|
|
664
|
+
agentId,
|
|
665
|
+
schedule: currentConfig.syncSchedule ?? "daily",
|
|
666
|
+
scope: currentConfig.syncScope ?? [
|
|
667
|
+
"config",
|
|
668
|
+
"conversations",
|
|
669
|
+
"memory"
|
|
670
|
+
],
|
|
671
|
+
lastResult: lastSyncResult,
|
|
672
|
+
watcherActive: !!stopWatcher
|
|
673
|
+
}));
|
|
663
674
|
log.info("Registered gateway RPC method: sync.status");
|
|
664
675
|
}
|
|
665
676
|
if (api.registerService) api.registerService({
|
|
@@ -696,6 +707,8 @@ const plugin = {
|
|
|
696
707
|
}
|
|
697
708
|
daemonIpcClient = null;
|
|
698
709
|
}
|
|
710
|
+
client = null;
|
|
711
|
+
agentId = null;
|
|
699
712
|
syncEngine = null;
|
|
700
713
|
sharedSyncEngine = null;
|
|
701
714
|
lastSyncResult = null;
|
|
@@ -711,12 +724,8 @@ const plugin = {
|
|
|
711
724
|
};
|
|
712
725
|
if (config.syncSchedule) {
|
|
713
726
|
if (config.syncSchedule === "realtime" && syncEngine && !stopWatcher) {
|
|
714
|
-
let cfgForWorkspace = null;
|
|
715
|
-
try {
|
|
716
|
-
cfgForWorkspace = resolveConfig();
|
|
717
|
-
} catch {}
|
|
718
727
|
stopWatcher = await startWatcher({
|
|
719
|
-
workspacePath: currentConfig.workspacePath ??
|
|
728
|
+
workspacePath: currentConfig.workspacePath ?? syncEngine.workspacePath,
|
|
720
729
|
debounceMs: 2e3,
|
|
721
730
|
onChanges: async (paths) => {
|
|
722
731
|
if (!syncEngine) return;
|