@storacha/clawracha 0.1.15 → 0.1.17
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 +96 -5
- package/dist/sync.d.ts +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +5 -2
- package/dist/watcher.d.ts +5 -0
- package/dist/watcher.d.ts.map +1 -1
- package/dist/watcher.js +13 -0
- package/package.json +3 -2
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;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EACV,iBAAiB,EAIlB,MAAM,qBAAqB,CAAC;AAkJ7B,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,GAAG,EAAE,iBAAiB,QAsmBpD"}
|
package/dist/plugin.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import * as fs from "node:fs/promises";
|
|
10
10
|
import * as path from "node:path";
|
|
11
|
+
import { json as consumeJson } from "stream/consumers";
|
|
11
12
|
import { SyncEngine } from "./sync.js";
|
|
12
13
|
import { FileWatcher } from "./watcher.js";
|
|
13
14
|
import { createStorachaClient } from "./utils/client.js";
|
|
@@ -15,8 +16,13 @@ import { decodeDelegation, encodeDelegation, readDelegationArg, } from "./utils/
|
|
|
15
16
|
import { resolveAgentWorkspace, getAgentIds } from "./utils/workspace.js";
|
|
16
17
|
import { Agent, Name } from "@storacha/ucn/pail";
|
|
17
18
|
import { extract } from "@storacha/client/delegation";
|
|
19
|
+
import * as z from "zod";
|
|
18
20
|
const activeSyncers = new Map();
|
|
19
21
|
// --- Config helpers ---
|
|
22
|
+
const UpdateParams = z.object({
|
|
23
|
+
agentId: z.string(),
|
|
24
|
+
workspace: z.string(),
|
|
25
|
+
});
|
|
20
26
|
async function loadDeviceConfig(workspace) {
|
|
21
27
|
const configPath = path.join(workspace, ".storacha", "config.json");
|
|
22
28
|
try {
|
|
@@ -29,6 +35,38 @@ async function loadDeviceConfig(workspace) {
|
|
|
29
35
|
throw err;
|
|
30
36
|
}
|
|
31
37
|
}
|
|
38
|
+
async function requestWorkspaceUpdate(workspace, agentId, gatewayConfig) {
|
|
39
|
+
// Mirror resolveGatewayPort from openclaw core
|
|
40
|
+
const DEFAULT_GATEWAY_PORT = 18789;
|
|
41
|
+
let port = DEFAULT_GATEWAY_PORT;
|
|
42
|
+
if (typeof gatewayConfig.port === "number" &&
|
|
43
|
+
Number.isFinite(gatewayConfig.port) &&
|
|
44
|
+
gatewayConfig.port > 0) {
|
|
45
|
+
port = gatewayConfig.port;
|
|
46
|
+
}
|
|
47
|
+
const url = `http://127.0.0.1:${port}/api/channels/clawracha/workspace-update`;
|
|
48
|
+
const headers = {
|
|
49
|
+
"Content-Type": "application/json",
|
|
50
|
+
};
|
|
51
|
+
// Auth: /api/channels/ routes get gateway auth middleware
|
|
52
|
+
const token = gatewayConfig.auth?.token ?? gatewayConfig.auth?.password;
|
|
53
|
+
if (token) {
|
|
54
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const res = await fetch(url, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers,
|
|
60
|
+
body: JSON.stringify({ agentId, workspace }),
|
|
61
|
+
});
|
|
62
|
+
if (!res.ok) {
|
|
63
|
+
console.warn(`Warning: Failed to notify gateway (${res.status}). Restart the gateway to pick up changes.`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
console.warn(`Warning: Could not reach gateway at ${url}: ${err.message}. Restart the gateway to pick up changes.`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
32
70
|
async function saveDeviceConfig(workspace, config) {
|
|
33
71
|
const configDir = path.join(workspace, ".storacha");
|
|
34
72
|
await fs.mkdir(configDir, { recursive: true });
|
|
@@ -72,6 +110,41 @@ async function startWorkspaceSync(workspace, agentId, pluginConfig, initialAdd,
|
|
|
72
110
|
export default function plugin(api) {
|
|
73
111
|
const pluginConfig = (api.pluginConfig ?? {});
|
|
74
112
|
// --- Background service: one syncer per agent workspace ---
|
|
113
|
+
api.registerHttpHandler(async (req, res) => {
|
|
114
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
115
|
+
// Only handle /api/channels/clawracha/workspace-update
|
|
116
|
+
if (!url.pathname.startsWith("/api/channels/clawracha/workspace-update")) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
// only handle post requests
|
|
120
|
+
if (req.method !== "POST") {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
const body = await consumeJson(req);
|
|
124
|
+
const paramsResult = UpdateParams.safeParse(body);
|
|
125
|
+
if (paramsResult.success === false) {
|
|
126
|
+
res.statusCode = 400;
|
|
127
|
+
res.end(JSON.stringify({
|
|
128
|
+
error: "Invalid parameters",
|
|
129
|
+
details: paramsResult.error,
|
|
130
|
+
}));
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
const updateParams = paramsResult.data;
|
|
134
|
+
const sync = activeSyncers.get(updateParams.workspace);
|
|
135
|
+
if (sync) {
|
|
136
|
+
// stop active sync engine if present
|
|
137
|
+
// waiting for any active syncs to flush.
|
|
138
|
+
await sync.watcher.stop();
|
|
139
|
+
await sync.watcher.forceFlush();
|
|
140
|
+
await sync.engine.stop();
|
|
141
|
+
}
|
|
142
|
+
const newSync = await startWorkspaceSync(updateParams.workspace, updateParams.agentId, pluginConfig, false, api.logger);
|
|
143
|
+
if (newSync) {
|
|
144
|
+
activeSyncers.set(updateParams.workspace, newSync);
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
});
|
|
75
148
|
api.registerService({
|
|
76
149
|
id: "storacha-sync",
|
|
77
150
|
async start(ctx) {
|
|
@@ -102,8 +175,9 @@ export default function plugin(api) {
|
|
|
102
175
|
},
|
|
103
176
|
async stop(ctx) {
|
|
104
177
|
for (const [workspace, sync] of activeSyncers) {
|
|
105
|
-
sync.engine.stop();
|
|
106
178
|
await sync.watcher.stop();
|
|
179
|
+
await sync.watcher.forceFlush();
|
|
180
|
+
await sync.engine.stop();
|
|
107
181
|
ctx.logger.info(`[${sync.agentId}] Stopped syncing: ${workspace}`);
|
|
108
182
|
}
|
|
109
183
|
activeSyncers.clear();
|
|
@@ -257,7 +331,17 @@ export default function plugin(api) {
|
|
|
257
331
|
if (!sync) {
|
|
258
332
|
throw new Error("Failed to start sync engine");
|
|
259
333
|
}
|
|
260
|
-
|
|
334
|
+
// Wait for initial files to flush through, then stop the watcher
|
|
335
|
+
await sync.watcher.waitForReady();
|
|
336
|
+
await sync.watcher.stop();
|
|
337
|
+
// force out changes then stop the engine, which will wait for last
|
|
338
|
+
// sync to complete
|
|
339
|
+
await sync.watcher.forceFlush();
|
|
340
|
+
await sync.engine.stop();
|
|
341
|
+
// post to the endpoint to tell the service to start syncing this space
|
|
342
|
+
if (config.gateway) {
|
|
343
|
+
await requestWorkspaceUpdate(workspace, agentId, config.gateway);
|
|
344
|
+
}
|
|
261
345
|
const agent = Agent.parse(deviceConfig.agentKey);
|
|
262
346
|
console.log(`🔥 Storacha workspace ready for ${agentId}!`);
|
|
263
347
|
console.log(`Agent DID: ${agent.did()}`);
|
|
@@ -309,9 +393,16 @@ export default function plugin(api) {
|
|
|
309
393
|
// Pull remote state before watcher starts
|
|
310
394
|
const sync = await startWorkspaceSync(workspace, agentId, pluginConfig, false, console);
|
|
311
395
|
let pullCount = 0;
|
|
312
|
-
if (sync) {
|
|
313
|
-
|
|
314
|
-
|
|
396
|
+
if (!sync) {
|
|
397
|
+
throw new Error("Failed to start sync engine");
|
|
398
|
+
}
|
|
399
|
+
pullCount = await sync.engine.pullRemote();
|
|
400
|
+
await sync.watcher.stop();
|
|
401
|
+
await sync.watcher.forceFlush();
|
|
402
|
+
await sync.engine.stop();
|
|
403
|
+
// post to the endpoint to tell the service to start syncing this space
|
|
404
|
+
if (config.gateway) {
|
|
405
|
+
await requestWorkspaceUpdate(workspace, agentId, config.gateway);
|
|
315
406
|
}
|
|
316
407
|
const agent = Agent.parse(deviceConfig.agentKey);
|
|
317
408
|
console.log(`🔥 Joined existing Storacha workspace for ${agentId}!`);
|
package/dist/sync.d.ts
CHANGED
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;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;IACvC,OAAO,CAAC,QAAQ,CAAoC;gBAExC,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;YAMb,UAAU;IAyDxB;;OAEG;YACW,iBAAiB;IAe/B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;IAc5C;;OAEG;YACW,kBAAkB;IAUhC;;OAEG;
|
|
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;IACvC,OAAO,CAAC,QAAQ,CAAoC;gBAExC,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;YAMb,UAAU;IAyDxB;;OAEG;YACW,iBAAiB;IAe/B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;IAc5C;;OAEG;YACW,kBAAkB;IAUhC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAO3B;;;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;IAkBI,MAAM,IAAI,OAAO,CAAC,SAAS,CAAC;IAW5B,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;YAM5B,WAAW;CAK1B"}
|
package/dist/sync.js
CHANGED
|
@@ -199,8 +199,11 @@ export class SyncEngine {
|
|
|
199
199
|
/**
|
|
200
200
|
* Mark the engine as stopped.
|
|
201
201
|
*/
|
|
202
|
-
stop() {
|
|
203
|
-
this.
|
|
202
|
+
async stop() {
|
|
203
|
+
this.syncLock = this.syncLock.then(() => {
|
|
204
|
+
this.state = { running: false };
|
|
205
|
+
});
|
|
206
|
+
return this.syncLock;
|
|
204
207
|
}
|
|
205
208
|
/**
|
|
206
209
|
* Pull all remote state and write to local filesystem.
|
package/dist/watcher.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export declare class FileWatcher {
|
|
|
16
16
|
private watcher;
|
|
17
17
|
private pendingChanges;
|
|
18
18
|
private debounceTimer;
|
|
19
|
+
private readyPromise;
|
|
19
20
|
private options;
|
|
20
21
|
private debounceMs;
|
|
21
22
|
constructor(options: WatcherOptions);
|
|
@@ -23,6 +24,10 @@ export declare class FileWatcher {
|
|
|
23
24
|
* Start watching the workspace
|
|
24
25
|
*/
|
|
25
26
|
start(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Wait for chokidar to finish its initial scan
|
|
29
|
+
*/
|
|
30
|
+
waitForReady(): Promise<void>;
|
|
26
31
|
/**
|
|
27
32
|
* Stop watching
|
|
28
33
|
*/
|
package/dist/watcher.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAErE,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,UAAU,CAAS;gBAEf,OAAO,EAAE,cAAc;IAKnC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAErE,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,UAAU,CAAS;gBAEf,OAAO,EAAE,cAAc;IAKnC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuC5B;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAOnC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B;;OAEG;IACH,OAAO,CAAC,YAAY;IAmBpB;;OAEG;YACW,KAAK;IAanB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAOlC"}
|
package/dist/watcher.js
CHANGED
|
@@ -11,6 +11,7 @@ export class FileWatcher {
|
|
|
11
11
|
watcher = null;
|
|
12
12
|
pendingChanges = new Map();
|
|
13
13
|
debounceTimer = null;
|
|
14
|
+
readyPromise = null;
|
|
14
15
|
options;
|
|
15
16
|
debounceMs;
|
|
16
17
|
constructor(options) {
|
|
@@ -43,12 +44,24 @@ export class FileWatcher {
|
|
|
43
44
|
pollInterval: 100,
|
|
44
45
|
},
|
|
45
46
|
});
|
|
47
|
+
this.readyPromise = new Promise((resolve) => {
|
|
48
|
+
this.watcher.on("ready", () => resolve());
|
|
49
|
+
});
|
|
46
50
|
this.watcher
|
|
47
51
|
.on("add", (filePath) => this.handleChange("add", filePath))
|
|
48
52
|
.on("change", (filePath) => this.handleChange("change", filePath))
|
|
49
53
|
.on("unlink", (filePath) => this.handleChange("unlink", filePath))
|
|
50
54
|
.on("error", (err) => console.error("Watcher error:", err));
|
|
51
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Wait for chokidar to finish its initial scan
|
|
58
|
+
*/
|
|
59
|
+
async waitForReady() {
|
|
60
|
+
if (!this.readyPromise) {
|
|
61
|
+
throw new Error("Watcher not started");
|
|
62
|
+
}
|
|
63
|
+
return this.readyPromise;
|
|
64
|
+
}
|
|
52
65
|
/**
|
|
53
66
|
* Stop watching
|
|
54
67
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storacha/clawracha",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "OpenClaw plugin for Storacha workspace sync via UCN Pail",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -36,7 +36,8 @@
|
|
|
36
36
|
"@web3-storage/pail": "0.6.3-rc.3",
|
|
37
37
|
"carstream": "^2.3.0",
|
|
38
38
|
"chokidar": "^3.6.0",
|
|
39
|
-
"multiformats": "^13.0.0"
|
|
39
|
+
"multiformats": "^13.0.0",
|
|
40
|
+
"zod": "^4.3.6"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@ipld/unixfs": "^3.0.0",
|