@storacha/clawracha 0.1.2 → 0.1.3
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/plugin.d.ts.map +1 -1
- package/dist/plugin.js +14 -10
- package/dist/sync.d.ts +15 -12
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +40 -32
- package/package.json +1 -1
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,qBAAqB,CAAC;AAgH7B,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,GAAG,EAAE,iBAAiB,QAojBpD"}
|
package/dist/plugin.js
CHANGED
|
@@ -40,8 +40,7 @@ async function startWorkspaceSync(workspace, agentId, pluginConfig, logger) {
|
|
|
40
40
|
if (!deviceConfig || !deviceConfig.setupComplete) {
|
|
41
41
|
return null;
|
|
42
42
|
}
|
|
43
|
-
const
|
|
44
|
-
const engine = new SyncEngine(storachaClient, workspace);
|
|
43
|
+
const engine = new SyncEngine(workspace);
|
|
45
44
|
await engine.init(deviceConfig);
|
|
46
45
|
const watcher = new FileWatcher({
|
|
47
46
|
workspace,
|
|
@@ -263,8 +262,7 @@ export default function plugin(api) {
|
|
|
263
262
|
deviceConfig.setupComplete = true;
|
|
264
263
|
await saveDeviceConfig(workspace, deviceConfig);
|
|
265
264
|
// Initial upload: scan all existing workspace files and sync to Storacha
|
|
266
|
-
const
|
|
267
|
-
const engine = new SyncEngine(storachaClient, workspace);
|
|
265
|
+
const engine = new SyncEngine(workspace);
|
|
268
266
|
await engine.init(deviceConfig);
|
|
269
267
|
const userIgnored = await readIgnoreFile(workspace);
|
|
270
268
|
const ignorePatterns = [
|
|
@@ -296,6 +294,7 @@ export default function plugin(api) {
|
|
|
296
294
|
console.log("\nRestart the gateway to start syncing: `openclaw gateway restart`");
|
|
297
295
|
console.log(`\nTo add another device, run \`openclaw clawracha grant <their-DID> --agent ${agentId}\` here,`);
|
|
298
296
|
console.log(`then \`openclaw clawracha join <upload> <name> --agent <id>\` on the other device.`);
|
|
297
|
+
console.log("\nSync is now active (no gateway restart needed).");
|
|
299
298
|
}
|
|
300
299
|
catch (err) {
|
|
301
300
|
console.error(`Error: ${err.message}`);
|
|
@@ -335,8 +334,7 @@ export default function plugin(api) {
|
|
|
335
334
|
await saveDeviceConfig(workspace, deviceConfig);
|
|
336
335
|
// Pull remote state before watcher starts
|
|
337
336
|
let pullCount = 0;
|
|
338
|
-
const
|
|
339
|
-
const engine = new SyncEngine(storachaClient, workspace);
|
|
337
|
+
const engine = new SyncEngine(workspace);
|
|
340
338
|
await engine.init(deviceConfig);
|
|
341
339
|
pullCount = await engine.pullRemote();
|
|
342
340
|
// Save name archive after pull
|
|
@@ -486,10 +484,16 @@ export default function plugin(api) {
|
|
|
486
484
|
console.log(`Not set up. Run \`openclaw clawracha init --agent ${agentId}\` first.`);
|
|
487
485
|
return;
|
|
488
486
|
}
|
|
489
|
-
//
|
|
490
|
-
|
|
491
|
-
const
|
|
492
|
-
|
|
487
|
+
// Use active syncer if available, otherwise spin up temporary engine
|
|
488
|
+
let engine;
|
|
489
|
+
const activeSync = activeSyncers.get(workspace);
|
|
490
|
+
if (activeSync) {
|
|
491
|
+
engine = activeSync.engine;
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
engine = new SyncEngine(workspace);
|
|
495
|
+
await engine.init(deviceConfig);
|
|
496
|
+
}
|
|
493
497
|
const state = await engine.inspect();
|
|
494
498
|
console.log(`🔥 Storacha Inspect [${agentId}]`);
|
|
495
499
|
console.log(`Workspace: ${workspace}`);
|
package/dist/sync.d.ts
CHANGED
|
@@ -10,40 +10,43 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { SyncState, FileChange, DeviceConfig } from "./types/index.js";
|
|
12
12
|
import { type PailEntries } from "./utils/differ.js";
|
|
13
|
-
import { Client } from "@storacha/client";
|
|
14
13
|
export declare class SyncEngine {
|
|
15
14
|
private workspace;
|
|
16
15
|
private blocks;
|
|
17
|
-
private
|
|
16
|
+
private state;
|
|
18
17
|
private current;
|
|
19
18
|
private pendingOps;
|
|
20
19
|
private carFile;
|
|
21
|
-
private running;
|
|
22
20
|
private lastSync;
|
|
23
|
-
|
|
24
|
-
constructor(storachaClient: Client, workspace: string);
|
|
21
|
+
constructor(workspace: string);
|
|
25
22
|
/**
|
|
26
|
-
* Initialize sync engine with device config
|
|
23
|
+
* Initialize sync engine with device config.
|
|
24
|
+
* Creates the Storacha client and resolves the UCN name.
|
|
27
25
|
*/
|
|
28
26
|
init(config: DeviceConfig): Promise<void>;
|
|
29
27
|
/**
|
|
30
|
-
*
|
|
28
|
+
* Require running state or throw.
|
|
29
|
+
*/
|
|
30
|
+
private requireRunning;
|
|
31
|
+
/**
|
|
32
|
+
* Process a batch of file changes.
|
|
33
|
+
* Can be called even when not running (accumulates pending ops).
|
|
31
34
|
*/
|
|
32
35
|
processChanges(changes: FileChange[]): Promise<void>;
|
|
33
36
|
/**
|
|
34
|
-
* Execute sync: generate revision, publish, upload, apply remote changes
|
|
37
|
+
* Execute sync: generate revision, publish, upload, apply remote changes.
|
|
35
38
|
*/
|
|
36
39
|
sync(): Promise<void>;
|
|
37
40
|
/**
|
|
38
|
-
* Create CAR and upload to Storacha
|
|
41
|
+
* Create CAR and upload to Storacha.
|
|
39
42
|
*/
|
|
40
43
|
private possiblyUploadCAR;
|
|
41
44
|
/**
|
|
42
|
-
* Get current pail entries as map
|
|
45
|
+
* Get current pail entries as map.
|
|
43
46
|
*/
|
|
44
47
|
getPailEntries(): Promise<PailEntries>;
|
|
45
48
|
/**
|
|
46
|
-
* Apply remote changes to local filesystem
|
|
49
|
+
* Apply remote changes to local filesystem.
|
|
47
50
|
*/
|
|
48
51
|
private applyRemoteChanges;
|
|
49
52
|
/**
|
|
@@ -52,7 +55,7 @@ export declare class SyncEngine {
|
|
|
52
55
|
stop(): void;
|
|
53
56
|
/**
|
|
54
57
|
* Pull all remote state and write to local filesystem.
|
|
55
|
-
* Used by
|
|
58
|
+
* Used by join to overwrite local with remote before watcher starts.
|
|
56
59
|
*/
|
|
57
60
|
pullRemote(): Promise<number>;
|
|
58
61
|
/**
|
package/dist/sync.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,OAAO,KAAK,EACV,SAAS,EACT,UAAU,EAEV,YAAY,EACb,MAAM,kBAAkB,CAAC;AAS1B,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,OAAO,KAAK,EACV,SAAS,EACT,UAAU,EAEV,YAAY,EACb,MAAM,kBAAkB,CAAC;AAS1B,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAWxE,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,QAAQ,CAAuB;gBAE3B,SAAS,EAAE,MAAM;IAK7B;;;OAGG;IACG,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAoC/C;;OAEG;IACH,OAAO,CAAC,cAAc;IAOtB;;;OAGG;IACG,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAe1D;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgD3B;;OAEG;YACW,iBAAiB;IAe/B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;IAc5C;;OAEG;YACW,kBAAkB;IAUhC;;OAEG;IACH,IAAI,IAAI,IAAI;IAIZ;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IA0BnC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC;QACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QACpB,SAAS,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAC/B,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,UAAU,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAC5D,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC;IAiBI,MAAM,IAAI,OAAO,CAAC,SAAS,CAAC;IAW5B,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;YAM5B,WAAW;CAK1B"}
|
package/dist/sync.js
CHANGED
|
@@ -16,31 +16,32 @@ import { applyRemoteChanges } from "./handlers/remote.js";
|
|
|
16
16
|
import { processChanges } from "./handlers/process.js";
|
|
17
17
|
import { diffRemoteChanges } from "./utils/differ.js";
|
|
18
18
|
import { makeTempCar } from "./utils/tempcar.js";
|
|
19
|
+
import { createStorachaClient } from "./utils/client.js";
|
|
19
20
|
import { decodeDelegation, encodeDelegation } from "./utils/delegation.js";
|
|
20
21
|
export class SyncEngine {
|
|
21
22
|
workspace;
|
|
22
23
|
blocks;
|
|
23
|
-
|
|
24
|
+
state = { running: false };
|
|
24
25
|
current = null;
|
|
25
26
|
pendingOps = [];
|
|
26
27
|
carFile = null;
|
|
27
|
-
running = false;
|
|
28
28
|
lastSync = null;
|
|
29
|
-
|
|
30
|
-
constructor(storachaClient, workspace) {
|
|
31
|
-
this.storachaClient = storachaClient;
|
|
29
|
+
constructor(workspace) {
|
|
32
30
|
this.workspace = workspace;
|
|
33
31
|
this.blocks = createWorkspaceBlockstore(workspace);
|
|
34
32
|
}
|
|
35
33
|
/**
|
|
36
|
-
* Initialize sync engine with device config
|
|
34
|
+
* Initialize sync engine with device config.
|
|
35
|
+
* Creates the Storacha client and resolves the UCN name.
|
|
37
36
|
*/
|
|
38
37
|
async init(config) {
|
|
38
|
+
const storachaClient = await createStorachaClient(config);
|
|
39
39
|
const agent = Agent.parse(config.agentKey);
|
|
40
|
+
let name;
|
|
40
41
|
if (config.nameArchive) {
|
|
41
42
|
// Restore from previously saved archive (has full state)
|
|
42
43
|
const archiveBytes = decodeDelegation(config.nameArchive);
|
|
43
|
-
|
|
44
|
+
name = await Name.extract(agent, archiveBytes);
|
|
44
45
|
}
|
|
45
46
|
else if (config.nameDelegation) {
|
|
46
47
|
// Reconstruct from delegation (granted by another device)
|
|
@@ -49,14 +50,15 @@ export class SyncEngine {
|
|
|
49
50
|
const { ok: delegation } = await extract(nameBytes);
|
|
50
51
|
if (!delegation)
|
|
51
52
|
throw new Error("Failed to extract name delegation");
|
|
52
|
-
|
|
53
|
+
name = Name.from(agent, [delegation]);
|
|
53
54
|
}
|
|
54
55
|
else {
|
|
55
56
|
// First device — create a new name
|
|
56
|
-
|
|
57
|
+
name = await Name.create(agent);
|
|
57
58
|
}
|
|
59
|
+
this.state = { running: true, name, storachaClient };
|
|
58
60
|
try {
|
|
59
|
-
const result = await Revision.resolve(this.blocks,
|
|
61
|
+
const result = await Revision.resolve(this.blocks, name);
|
|
60
62
|
this.current = result.value;
|
|
61
63
|
await this.storeBlocks(result.additions);
|
|
62
64
|
}
|
|
@@ -68,10 +70,19 @@ export class SyncEngine {
|
|
|
68
70
|
throw err;
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
|
-
this.running = true;
|
|
72
73
|
}
|
|
73
74
|
/**
|
|
74
|
-
*
|
|
75
|
+
* Require running state or throw.
|
|
76
|
+
*/
|
|
77
|
+
requireRunning() {
|
|
78
|
+
if (!this.state.running) {
|
|
79
|
+
throw new Error("Sync engine not initialized");
|
|
80
|
+
}
|
|
81
|
+
return this.state;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Process a batch of file changes.
|
|
85
|
+
* Can be called even when not running (accumulates pending ops).
|
|
75
86
|
*/
|
|
76
87
|
async processChanges(changes) {
|
|
77
88
|
if (!this.carFile) {
|
|
@@ -81,18 +92,16 @@ export class SyncEngine {
|
|
|
81
92
|
this.pendingOps.push(...pendingOps);
|
|
82
93
|
}
|
|
83
94
|
/**
|
|
84
|
-
* Execute sync: generate revision, publish, upload, apply remote changes
|
|
95
|
+
* Execute sync: generate revision, publish, upload, apply remote changes.
|
|
85
96
|
*/
|
|
86
97
|
async sync() {
|
|
87
|
-
|
|
88
|
-
throw new Error("Sync engine not initialized");
|
|
89
|
-
}
|
|
98
|
+
const { name } = this.requireRunning();
|
|
90
99
|
const beforeEntries = await this.getPailEntries();
|
|
91
100
|
if (this.pendingOps.length > 0) {
|
|
92
101
|
if (!this.carFile) {
|
|
93
102
|
throw new Error("CAR file not initialized");
|
|
94
103
|
}
|
|
95
|
-
const result = await applyPendingOps(this.blocks,
|
|
104
|
+
const result = await applyPendingOps(this.blocks, name, this.current, this.pendingOps);
|
|
96
105
|
this.current = result.current;
|
|
97
106
|
for (const block of result.revisionBlocks) {
|
|
98
107
|
await this.carFile.put(block);
|
|
@@ -102,7 +111,7 @@ export class SyncEngine {
|
|
|
102
111
|
else {
|
|
103
112
|
// No pending ops — just pull remote
|
|
104
113
|
try {
|
|
105
|
-
const result = await Revision.resolve(this.blocks,
|
|
114
|
+
const result = await Revision.resolve(this.blocks, name, {
|
|
106
115
|
base: this.current ?? undefined,
|
|
107
116
|
});
|
|
108
117
|
await this.storeBlocks(result.additions);
|
|
@@ -125,15 +134,16 @@ export class SyncEngine {
|
|
|
125
134
|
this.lastSync = Date.now();
|
|
126
135
|
}
|
|
127
136
|
/**
|
|
128
|
-
* Create CAR and upload to Storacha
|
|
137
|
+
* Create CAR and upload to Storacha.
|
|
129
138
|
*/
|
|
130
139
|
async possiblyUploadCAR() {
|
|
131
140
|
if (this.carFile) {
|
|
141
|
+
const { storachaClient } = this.requireRunning();
|
|
132
142
|
const readableCar = await this.carFile.switchToReadable();
|
|
133
143
|
this.carFile = null;
|
|
134
144
|
if (readableCar) {
|
|
135
145
|
try {
|
|
136
|
-
await
|
|
146
|
+
await storachaClient.uploadCAR(readableCar.readable);
|
|
137
147
|
}
|
|
138
148
|
finally {
|
|
139
149
|
await readableCar.cleanup();
|
|
@@ -142,7 +152,7 @@ export class SyncEngine {
|
|
|
142
152
|
}
|
|
143
153
|
}
|
|
144
154
|
/**
|
|
145
|
-
* Get current pail entries as map
|
|
155
|
+
* Get current pail entries as map.
|
|
146
156
|
*/
|
|
147
157
|
async getPailEntries() {
|
|
148
158
|
const entries = new Map();
|
|
@@ -156,7 +166,7 @@ export class SyncEngine {
|
|
|
156
166
|
return entries;
|
|
157
167
|
}
|
|
158
168
|
/**
|
|
159
|
-
* Apply remote changes to local filesystem
|
|
169
|
+
* Apply remote changes to local filesystem.
|
|
160
170
|
*/
|
|
161
171
|
async applyRemoteChanges(changedPaths, entries) {
|
|
162
172
|
await applyRemoteChanges(changedPaths, entries, this.workspace, {
|
|
@@ -168,17 +178,16 @@ export class SyncEngine {
|
|
|
168
178
|
* Mark the engine as stopped.
|
|
169
179
|
*/
|
|
170
180
|
stop() {
|
|
171
|
-
this.
|
|
181
|
+
this.state = { running: false };
|
|
172
182
|
}
|
|
173
183
|
/**
|
|
174
184
|
* Pull all remote state and write to local filesystem.
|
|
175
|
-
* Used by
|
|
185
|
+
* Used by join to overwrite local with remote before watcher starts.
|
|
176
186
|
*/
|
|
177
187
|
async pullRemote() {
|
|
178
|
-
|
|
179
|
-
throw new Error("Sync engine not initialized");
|
|
188
|
+
const { name } = this.requireRunning();
|
|
180
189
|
try {
|
|
181
|
-
const result = await Revision.resolve(this.blocks,
|
|
190
|
+
const result = await Revision.resolve(this.blocks, name, {
|
|
182
191
|
base: this.current ?? undefined,
|
|
183
192
|
});
|
|
184
193
|
await this.storeBlocks(result.additions);
|
|
@@ -215,13 +224,13 @@ export class SyncEngine {
|
|
|
215
224
|
key: op.key,
|
|
216
225
|
value: op.value?.toString(),
|
|
217
226
|
})),
|
|
218
|
-
running: this.running,
|
|
227
|
+
running: this.state.running,
|
|
219
228
|
};
|
|
220
229
|
}
|
|
221
230
|
async status() {
|
|
222
231
|
const entries = await this.getPailEntries();
|
|
223
232
|
return {
|
|
224
|
-
running: this.running,
|
|
233
|
+
running: this.state.running,
|
|
225
234
|
lastSync: this.lastSync,
|
|
226
235
|
root: this.current?.root ?? null,
|
|
227
236
|
entryCount: entries.size,
|
|
@@ -229,9 +238,8 @@ export class SyncEngine {
|
|
|
229
238
|
};
|
|
230
239
|
}
|
|
231
240
|
async exportNameArchive() {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const bytes = await this.name.archive();
|
|
241
|
+
const { name } = this.requireRunning();
|
|
242
|
+
const bytes = await name.archive();
|
|
235
243
|
return encodeDelegation(bytes);
|
|
236
244
|
}
|
|
237
245
|
async storeBlocks(blocks) {
|