@alfe.ai/openclaw-sync 0.3.4 → 0.3.6
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/index.d.cts +946 -702
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +946 -702
- package/dist/index.d.ts.map +1 -1
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin2.cjs +136 -156
- package/dist/plugin2.js +136 -156
- package/dist/plugin2.js.map +1 -1
- package/package.json +4 -3
package/dist/plugin2.cjs
CHANGED
|
@@ -5,6 +5,7 @@ let node_path = require("node:path");
|
|
|
5
5
|
let chokidar = require("chokidar");
|
|
6
6
|
let node_module = require("node:module");
|
|
7
7
|
let _alfe_ai_config = require("@alfe.ai/config");
|
|
8
|
+
let _alfe_ai_openclaw_plugin_kit = require("@alfe.ai/openclaw-plugin-kit");
|
|
8
9
|
let _alfe_ai_agent_api_client = require("@alfe.ai/agent-api-client");
|
|
9
10
|
//#region src/watcher.ts
|
|
10
11
|
/**
|
|
@@ -301,6 +302,7 @@ const SYNC_CAPABILITIES = [
|
|
|
301
302
|
"sync.pull",
|
|
302
303
|
"sync.fullSync"
|
|
303
304
|
];
|
|
305
|
+
const SYNC_ACTIVATION_KEY = (0, _alfe_ai_openclaw_plugin_kit.getActivationKey)("sync");
|
|
304
306
|
const SYNC_RELAY_RECONNECT_BASE_MS = 1e3;
|
|
305
307
|
const SYNC_RELAY_RECONNECT_MAX_MS = 3e4;
|
|
306
308
|
const SYNC_RELAY_DEBOUNCE_MS = 500;
|
|
@@ -358,64 +360,34 @@ function setupSchedule(schedule, log) {
|
|
|
358
360
|
}, intervalMs);
|
|
359
361
|
scheduledInterval.unref();
|
|
360
362
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
363
|
+
function handleDaemonMessage(msg, log) {
|
|
364
|
+
if (msg.type === "SYNC_NOW" || msg.command === "SYNC_NOW") {
|
|
365
|
+
log.info("Received SYNC_NOW command — triggering immediate sync...");
|
|
366
|
+
if (syncEngine) {
|
|
367
|
+
const engine = syncEngine;
|
|
365
368
|
(async () => {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if (response.ok) log.info("Sync capabilities registered with daemon");
|
|
372
|
-
else log.warn(`Failed to register sync capabilities: ${response.error?.message ?? "unknown"}`);
|
|
373
|
-
})();
|
|
374
|
-
});
|
|
375
|
-
ipc.on("disconnected", (...args) => {
|
|
376
|
-
const reason = typeof args[0] === "string" ? args[0] : String(args[0]);
|
|
377
|
-
log.warn(`Disconnected from Alfe daemon: ${reason}`);
|
|
378
|
-
});
|
|
379
|
-
ipc.on("message", (...args) => {
|
|
380
|
-
const msg = args[0];
|
|
381
|
-
if (msg?.type === "SYNC_NOW" || msg?.command === "SYNC_NOW") {
|
|
382
|
-
log.info("Received SYNC_NOW command — triggering immediate sync...");
|
|
383
|
-
if (syncEngine) {
|
|
384
|
-
const engine = syncEngine;
|
|
385
|
-
(async () => {
|
|
386
|
-
try {
|
|
387
|
-
lastSyncResult = await engine.fullSync({ quiet: true });
|
|
388
|
-
log.info(`SYNC_NOW complete: ${String(lastSyncResult.pushed)} pushed, ${String(lastSyncResult.pulled)} pulled`);
|
|
389
|
-
} catch (err) {
|
|
390
|
-
log.error(`SYNC_NOW failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
391
|
-
}
|
|
392
|
-
})();
|
|
369
|
+
try {
|
|
370
|
+
lastSyncResult = await engine.fullSync({ quiet: true });
|
|
371
|
+
log.info(`SYNC_NOW complete: ${String(lastSyncResult.pushed)} pushed, ${String(lastSyncResult.pulled)} pulled`);
|
|
372
|
+
} catch (err) {
|
|
373
|
+
log.error(`SYNC_NOW failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
393
374
|
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
})
|
|
375
|
+
})();
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (msg.type === "SHARED_SCOPES") {
|
|
379
|
+
const scopes = msg.scopes;
|
|
380
|
+
const engine = sharedSyncEngine;
|
|
381
|
+
if (scopes && engine) {
|
|
382
|
+
log.info(`Received SHARED_SCOPES update: ${String(scopes.length)} scope(s)`);
|
|
383
|
+
(async () => {
|
|
384
|
+
try {
|
|
385
|
+
await engine.updateScopes(scopes);
|
|
386
|
+
} catch (err) {
|
|
387
|
+
log.error(`SHARED_SCOPES update failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
407
388
|
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
ipc.on("error", (...args) => {
|
|
411
|
-
const err = args[0];
|
|
412
|
-
log.debug(`Daemon IPC error: ${err instanceof Error ? err.message : String(err)}`);
|
|
413
|
-
});
|
|
414
|
-
ipc.start();
|
|
415
|
-
return ipc;
|
|
416
|
-
} catch {
|
|
417
|
-
log.info("Alfe daemon not available — Sync plugin running standalone");
|
|
418
|
-
return null;
|
|
389
|
+
})();
|
|
390
|
+
}
|
|
419
391
|
}
|
|
420
392
|
}
|
|
421
393
|
function clearSyncRelayReconnect() {
|
|
@@ -581,118 +553,123 @@ const plugin = {
|
|
|
581
553
|
];
|
|
582
554
|
const syncSchedule = pluginConfig.syncSchedule ?? "realtime";
|
|
583
555
|
const socketPath = pluginConfig.socketPath ?? alfeConfig?.socketPath ?? _alfe_ai_config.DEFAULT_SOCKET_PATH;
|
|
584
|
-
const startSyncService =
|
|
585
|
-
|
|
586
|
-
log.
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
return;
|
|
597
|
-
}
|
|
598
|
-
let syncCfg;
|
|
599
|
-
try {
|
|
600
|
-
syncCfg = (0, _alfe_ai_config.resolveConfig)();
|
|
601
|
-
} catch (err) {
|
|
602
|
-
log.warn(`Sync skipped — failed to resolve credentials from ~/.alfe/config.toml: ${err instanceof Error ? err.message : String(err)}`);
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
|
-
client = new _alfe_ai_agent_api_client.AgentApiClient({
|
|
606
|
-
apiKey: syncCfg.apiKey,
|
|
607
|
-
apiUrl: syncCfg.apiUrl
|
|
608
|
-
});
|
|
609
|
-
syncEngine = require_sync_engine.createSyncEngine({
|
|
610
|
-
workspacePath,
|
|
611
|
-
client,
|
|
612
|
-
runtime
|
|
613
|
-
});
|
|
614
|
-
log.info("Sync engine initialized");
|
|
615
|
-
if (syncSchedule === "realtime") {
|
|
556
|
+
const startSyncService = () => {
|
|
557
|
+
(0, _alfe_ai_openclaw_plugin_kit.guardedStart)(SYNC_ACTIVATION_KEY, log, async () => {
|
|
558
|
+
log.info("Alfe Sync plugin activating...");
|
|
559
|
+
log.info(`Sync scope: ${syncScope.join(", ")}`);
|
|
560
|
+
log.info(`Sync schedule: ${syncSchedule}`);
|
|
561
|
+
log.info(`Workspace: ${workspacePath}`);
|
|
562
|
+
if (!(0, _alfe_ai_config.configExists)()) {
|
|
563
|
+
log.info("Sync skipped — no Alfe config found. Run `alfe login` to enable.");
|
|
564
|
+
(0, _alfe_ai_openclaw_plugin_kit.resetActivation)(SYNC_ACTIVATION_KEY);
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
let syncCfg;
|
|
616
568
|
try {
|
|
617
|
-
|
|
618
|
-
if (lastSyncResult.conflicts > 0) log.info(`Initial workspace reconcile complete — ${String(lastSyncResult.conflicts)} diverged local file(s) handled (see RECOVERY-*.md in the agent workspace)`);
|
|
619
|
-
else log.info("Initial workspace reconcile complete");
|
|
569
|
+
syncCfg = (0, _alfe_ai_config.resolveConfig)();
|
|
620
570
|
} catch (err) {
|
|
621
|
-
log.warn(`
|
|
571
|
+
log.warn(`Sync skipped — failed to resolve credentials from ~/.alfe/config.toml: ${err instanceof Error ? err.message : String(err)}`);
|
|
572
|
+
(0, _alfe_ai_openclaw_plugin_kit.resetActivation)(SYNC_ACTIVATION_KEY);
|
|
573
|
+
return;
|
|
622
574
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
log.warn(`Ignored-file prune failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
575
|
+
client = new _alfe_ai_agent_api_client.AgentApiClient({
|
|
576
|
+
apiKey: syncCfg.apiKey,
|
|
577
|
+
apiUrl: syncCfg.apiUrl
|
|
627
578
|
});
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
579
|
+
syncEngine = require_sync_engine.createSyncEngine({
|
|
580
|
+
workspacePath,
|
|
581
|
+
client,
|
|
582
|
+
runtime
|
|
583
|
+
});
|
|
584
|
+
log.info("Sync engine initialized");
|
|
585
|
+
if (syncSchedule === "realtime") {
|
|
586
|
+
try {
|
|
587
|
+
lastSyncResult = await syncEngine.firstRunReconcile({ quiet: true });
|
|
588
|
+
if (lastSyncResult.conflicts > 0) log.info(`Initial workspace reconcile complete — ${String(lastSyncResult.conflicts)} diverged local file(s) handled (see RECOVERY-*.md in the agent workspace)`);
|
|
589
|
+
else log.info("Initial workspace reconcile complete");
|
|
590
|
+
} catch (err) {
|
|
591
|
+
log.warn(`Initial workspace reconcile failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
592
|
+
}
|
|
593
|
+
syncEngine.pruneIgnored({ quiet: true }).then((pruned) => {
|
|
594
|
+
if (pruned.pushed > 0) log.info(`Pruned ${String(pruned.pushed)} ignored file(s) from cloud`);
|
|
595
|
+
}).catch((err) => {
|
|
596
|
+
log.warn(`Ignored-file prune failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
597
|
+
});
|
|
598
|
+
deleteBrake = createDeleteBrake();
|
|
599
|
+
try {
|
|
600
|
+
stopWatcher = await startWatcher({
|
|
601
|
+
workspacePath,
|
|
602
|
+
runtime,
|
|
603
|
+
debounceMs: 2e3,
|
|
604
|
+
onChanges: async (paths) => {
|
|
605
|
+
if (!syncEngine) return;
|
|
606
|
+
const existing = [];
|
|
607
|
+
const missing = [];
|
|
608
|
+
for (const p of paths) if ((0, node_fs.existsSync)((0, node_path.join)(workspacePath, p))) existing.push(p);
|
|
609
|
+
else missing.push(p);
|
|
610
|
+
log.debug(`Realtime sync: ${String(existing.length)} upload(s), ${String(missing.length)} delete(s)`);
|
|
611
|
+
if (existing.length > 0) try {
|
|
612
|
+
lastSyncResult = await syncEngine.push(existing, { quiet: true });
|
|
658
613
|
} catch (err) {
|
|
659
|
-
log.error(`Realtime
|
|
614
|
+
log.error(`Realtime push failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
615
|
+
}
|
|
616
|
+
if (missing.length > 0) {
|
|
617
|
+
const artifactDeletes = missing.filter((p) => require_sync_engine.isRecoveryArtifact(p));
|
|
618
|
+
const regularDeletes = missing.filter((p) => !require_sync_engine.isRecoveryArtifact(p));
|
|
619
|
+
const manifest = await require_sync_engine.readManifest(workspacePath);
|
|
620
|
+
const manifestSize = Object.keys(manifest.files).length;
|
|
621
|
+
let toDelete = missing;
|
|
622
|
+
if (deleteBrake && !deleteBrake.check(regularDeletes.length, manifestSize)) {
|
|
623
|
+
log.error(`Sync delete brake tripped: ${String(deleteBrake.windowSum() + regularDeletes.length)} deletes in last 60s vs manifest size ${String(manifestSize)} (threshold 30%). Refusing batch — investigate the workspace state.`);
|
|
624
|
+
toDelete = artifactDeletes;
|
|
625
|
+
}
|
|
626
|
+
if (toDelete.length > 0) try {
|
|
627
|
+
lastSyncResult = await syncEngine.pushDeletes(toDelete, { quiet: true });
|
|
628
|
+
} catch (err) {
|
|
629
|
+
log.error(`Realtime delete failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
630
|
+
}
|
|
660
631
|
}
|
|
661
632
|
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
if (registered) {
|
|
633
|
+
});
|
|
634
|
+
log.info("File watcher started for realtime sync");
|
|
635
|
+
} catch (err) {
|
|
636
|
+
log.warn(`Failed to start file watcher: ${err instanceof Error ? err.message : String(err)}`);
|
|
637
|
+
}
|
|
638
|
+
} else setupSchedule(syncSchedule, log);
|
|
639
|
+
daemonIpcClient = await (0, _alfe_ai_openclaw_plugin_kit.connectToDaemon)(socketPath, log, {
|
|
640
|
+
pluginId: "@alfe.ai/openclaw-sync",
|
|
641
|
+
capabilities: SYNC_CAPABILITIES,
|
|
642
|
+
onMessage: (msg) => {
|
|
643
|
+
handleDaemonMessage(msg, log);
|
|
644
|
+
},
|
|
645
|
+
standaloneNote: "Alfe daemon not available — Sync plugin running standalone"
|
|
646
|
+
});
|
|
647
|
+
let registered = null;
|
|
678
648
|
try {
|
|
679
|
-
|
|
649
|
+
registered = (await client.syncRegister()).agent;
|
|
650
|
+
agentId = registered.agentId;
|
|
680
651
|
} catch (err) {
|
|
681
|
-
log.
|
|
652
|
+
log.warn(`Sync register failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
682
653
|
}
|
|
683
|
-
if (
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
654
|
+
if (registered) {
|
|
655
|
+
try {
|
|
656
|
+
syncRelayWs = await connectToSyncRelay(pluginConfig.syncRelayUrl ?? deriveRelayUrl(syncCfg.apiUrl), syncCfg.apiKey, registered.agentId, log);
|
|
657
|
+
} catch (err) {
|
|
658
|
+
log.debug(`Sync Relay connection skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
659
|
+
}
|
|
660
|
+
if (pluginConfig.sharedSync !== false) try {
|
|
661
|
+
sharedSyncEngine = createSharedSyncEngine({
|
|
662
|
+
workspacePath,
|
|
663
|
+
client
|
|
664
|
+
}, log);
|
|
665
|
+
log.info("Shared sync engine created — waiting for SHARED_SCOPES from gateway");
|
|
666
|
+
} catch (err) {
|
|
667
|
+
log.debug(`Shared sync engine skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
668
|
+
}
|
|
691
669
|
}
|
|
692
|
-
}
|
|
670
|
+
});
|
|
693
671
|
};
|
|
694
672
|
const stopSyncService = async () => {
|
|
695
|
-
globalThis.__alfeSyncPluginActivated = false;
|
|
696
673
|
clearSchedule();
|
|
697
674
|
disconnectSyncRelay();
|
|
698
675
|
if (stopWatcher) {
|
|
@@ -720,6 +697,7 @@ const plugin = {
|
|
|
720
697
|
sharedSyncEngine = null;
|
|
721
698
|
lastSyncResult = null;
|
|
722
699
|
currentConfig = {};
|
|
700
|
+
(0, _alfe_ai_openclaw_plugin_kit.resetActivation)(SYNC_ACTIVATION_KEY);
|
|
723
701
|
log.info("Alfe Sync plugin deactivated");
|
|
724
702
|
};
|
|
725
703
|
if (typeof api.registerGatewayMethod === "function") {
|
|
@@ -759,13 +737,14 @@ const plugin = {
|
|
|
759
737
|
}
|
|
760
738
|
api.registerService({
|
|
761
739
|
id: "alfe-sync-engine",
|
|
762
|
-
start: () =>
|
|
740
|
+
start: () => {
|
|
741
|
+
startSyncService();
|
|
742
|
+
},
|
|
763
743
|
stop: () => stopSyncService()
|
|
764
744
|
});
|
|
765
745
|
log.info("Alfe Sync plugin activated");
|
|
766
746
|
},
|
|
767
747
|
async deactivate(api) {
|
|
768
|
-
globalThis.__alfeSyncPluginActivated = false;
|
|
769
748
|
const log = api.logger;
|
|
770
749
|
log.info("Alfe Sync plugin deactivating...");
|
|
771
750
|
clearSchedule();
|
|
@@ -794,6 +773,7 @@ const plugin = {
|
|
|
794
773
|
sharedSyncEngine = null;
|
|
795
774
|
lastSyncResult = null;
|
|
796
775
|
currentConfig = {};
|
|
776
|
+
(0, _alfe_ai_openclaw_plugin_kit.resetActivation)(SYNC_ACTIVATION_KEY);
|
|
797
777
|
log.info("Alfe Sync plugin deactivated");
|
|
798
778
|
},
|
|
799
779
|
async configure(api, config) {
|