@storacha/clawracha 0.1.14 → 0.1.16
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 +103 -5
- package/dist/sync.d.ts +3 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +11 -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;AAwJ7B,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,45 @@ 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
|
+
const envRaw = process.env.OPENCLAW_GATEWAY_PORT?.trim() ||
|
|
43
|
+
process.env.CLAWDBOT_GATEWAY_PORT?.trim();
|
|
44
|
+
if (envRaw) {
|
|
45
|
+
const parsed = Number.parseInt(envRaw, 10);
|
|
46
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
47
|
+
port = parsed;
|
|
48
|
+
}
|
|
49
|
+
else if (typeof gatewayConfig.port === "number" &&
|
|
50
|
+
Number.isFinite(gatewayConfig.port) &&
|
|
51
|
+
gatewayConfig.port > 0) {
|
|
52
|
+
port = gatewayConfig.port;
|
|
53
|
+
}
|
|
54
|
+
const url = `http://127.0.0.1:${port}/api/channels/clawracha/workspace-update`;
|
|
55
|
+
const headers = {
|
|
56
|
+
"Content-Type": "application/json",
|
|
57
|
+
};
|
|
58
|
+
// Auth: /api/channels/ routes get gateway auth middleware
|
|
59
|
+
const token = gatewayConfig.auth?.token ?? gatewayConfig.auth?.password;
|
|
60
|
+
if (token) {
|
|
61
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const res = await fetch(url, {
|
|
65
|
+
method: "POST",
|
|
66
|
+
headers,
|
|
67
|
+
body: JSON.stringify({ agentId, workspace }),
|
|
68
|
+
});
|
|
69
|
+
if (!res.ok) {
|
|
70
|
+
console.warn(`Warning: Failed to notify gateway (${res.status}). Restart the gateway to pick up changes.`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
console.warn(`Warning: Could not reach gateway at ${url}: ${err.message}. Restart the gateway to pick up changes.`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
32
77
|
async function saveDeviceConfig(workspace, config) {
|
|
33
78
|
const configDir = path.join(workspace, ".storacha");
|
|
34
79
|
await fs.mkdir(configDir, { recursive: true });
|
|
@@ -72,6 +117,41 @@ async function startWorkspaceSync(workspace, agentId, pluginConfig, initialAdd,
|
|
|
72
117
|
export default function plugin(api) {
|
|
73
118
|
const pluginConfig = (api.pluginConfig ?? {});
|
|
74
119
|
// --- Background service: one syncer per agent workspace ---
|
|
120
|
+
api.registerHttpHandler(async (req, res) => {
|
|
121
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
122
|
+
// Only handle /api/channels/clawracha/workspace-update
|
|
123
|
+
if (!url.pathname.startsWith("/api/channels/clawracha/workspace-update")) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
// only handle post requests
|
|
127
|
+
if (req.method !== "POST") {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
const body = await consumeJson(req);
|
|
131
|
+
const paramsResult = UpdateParams.safeParse(body);
|
|
132
|
+
if (paramsResult.success === false) {
|
|
133
|
+
res.statusCode = 400;
|
|
134
|
+
res.end(JSON.stringify({
|
|
135
|
+
error: "Invalid parameters",
|
|
136
|
+
details: paramsResult.error,
|
|
137
|
+
}));
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
const updateParams = paramsResult.data;
|
|
141
|
+
const sync = activeSyncers.get(updateParams.workspace);
|
|
142
|
+
if (sync) {
|
|
143
|
+
// stop active sync engine if present
|
|
144
|
+
// waiting for any active syncs to flush.
|
|
145
|
+
await sync.watcher.stop();
|
|
146
|
+
await sync.watcher.forceFlush();
|
|
147
|
+
await sync.engine.stop();
|
|
148
|
+
}
|
|
149
|
+
const newSync = await startWorkspaceSync(updateParams.workspace, updateParams.agentId, pluginConfig, false, api.logger);
|
|
150
|
+
if (newSync) {
|
|
151
|
+
activeSyncers.set(updateParams.workspace, newSync);
|
|
152
|
+
}
|
|
153
|
+
return true;
|
|
154
|
+
});
|
|
75
155
|
api.registerService({
|
|
76
156
|
id: "storacha-sync",
|
|
77
157
|
async start(ctx) {
|
|
@@ -102,8 +182,9 @@ export default function plugin(api) {
|
|
|
102
182
|
},
|
|
103
183
|
async stop(ctx) {
|
|
104
184
|
for (const [workspace, sync] of activeSyncers) {
|
|
105
|
-
sync.engine.stop();
|
|
106
185
|
await sync.watcher.stop();
|
|
186
|
+
await sync.watcher.forceFlush();
|
|
187
|
+
await sync.engine.stop();
|
|
107
188
|
ctx.logger.info(`[${sync.agentId}] Stopped syncing: ${workspace}`);
|
|
108
189
|
}
|
|
109
190
|
activeSyncers.clear();
|
|
@@ -257,7 +338,17 @@ export default function plugin(api) {
|
|
|
257
338
|
if (!sync) {
|
|
258
339
|
throw new Error("Failed to start sync engine");
|
|
259
340
|
}
|
|
260
|
-
|
|
341
|
+
// Wait for initial files to flush through, then stop the watcher
|
|
342
|
+
await sync.watcher.waitForReady();
|
|
343
|
+
await sync.watcher.stop();
|
|
344
|
+
// force out changes then stop the engine, which will wait for last
|
|
345
|
+
// sync to complete
|
|
346
|
+
await sync.watcher.forceFlush();
|
|
347
|
+
await sync.engine.stop();
|
|
348
|
+
// post to the endpoint to tell the service to start syncing this space
|
|
349
|
+
if (config.gateway) {
|
|
350
|
+
await requestWorkspaceUpdate(workspace, agentId, config.gateway);
|
|
351
|
+
}
|
|
261
352
|
const agent = Agent.parse(deviceConfig.agentKey);
|
|
262
353
|
console.log(`🔥 Storacha workspace ready for ${agentId}!`);
|
|
263
354
|
console.log(`Agent DID: ${agent.did()}`);
|
|
@@ -309,9 +400,16 @@ export default function plugin(api) {
|
|
|
309
400
|
// Pull remote state before watcher starts
|
|
310
401
|
const sync = await startWorkspaceSync(workspace, agentId, pluginConfig, false, console);
|
|
311
402
|
let pullCount = 0;
|
|
312
|
-
if (sync) {
|
|
313
|
-
|
|
314
|
-
|
|
403
|
+
if (!sync) {
|
|
404
|
+
throw new Error("Failed to start sync engine");
|
|
405
|
+
}
|
|
406
|
+
pullCount = await sync.engine.pullRemote();
|
|
407
|
+
await sync.watcher.stop();
|
|
408
|
+
await sync.watcher.forceFlush();
|
|
409
|
+
await sync.engine.stop();
|
|
410
|
+
// post to the endpoint to tell the service to start syncing this space
|
|
411
|
+
if (config.gateway) {
|
|
412
|
+
await requestWorkspaceUpdate(workspace, agentId, config.gateway);
|
|
315
413
|
}
|
|
316
414
|
const agent = Agent.parse(deviceConfig.agentKey);
|
|
317
415
|
console.log(`🔥 Joined existing Storacha workspace for ${agentId}!`);
|
package/dist/sync.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export declare class SyncEngine {
|
|
|
18
18
|
private pendingOps;
|
|
19
19
|
private carFile;
|
|
20
20
|
private lastSync;
|
|
21
|
+
private syncLock;
|
|
21
22
|
constructor(workspace: string);
|
|
22
23
|
/**
|
|
23
24
|
* Initialize sync engine with device config.
|
|
@@ -37,6 +38,7 @@ export declare class SyncEngine {
|
|
|
37
38
|
* Execute sync: generate revision, publish, upload, apply remote changes.
|
|
38
39
|
*/
|
|
39
40
|
sync(): Promise<void>;
|
|
41
|
+
private _syncInner;
|
|
40
42
|
/**
|
|
41
43
|
* Create CAR and upload to Storacha.
|
|
42
44
|
*/
|
|
@@ -52,7 +54,7 @@ export declare class SyncEngine {
|
|
|
52
54
|
/**
|
|
53
55
|
* Mark the engine as stopped.
|
|
54
56
|
*/
|
|
55
|
-
stop(): void
|
|
57
|
+
stop(): Promise<void>;
|
|
56
58
|
/**
|
|
57
59
|
* Pull all remote state and write to local filesystem.
|
|
58
60
|
* Used by join to overwrite local with remote before watcher starts.
|
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;
|
|
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
|
@@ -26,6 +26,7 @@ export class SyncEngine {
|
|
|
26
26
|
pendingOps = [];
|
|
27
27
|
carFile = null;
|
|
28
28
|
lastSync = null;
|
|
29
|
+
syncLock = Promise.resolve();
|
|
29
30
|
constructor(workspace) {
|
|
30
31
|
this.workspace = workspace;
|
|
31
32
|
this.blocks = createWorkspaceBlockstore(workspace);
|
|
@@ -95,6 +96,11 @@ export class SyncEngine {
|
|
|
95
96
|
* Execute sync: generate revision, publish, upload, apply remote changes.
|
|
96
97
|
*/
|
|
97
98
|
async sync() {
|
|
99
|
+
const op = this.syncLock.then(() => this._syncInner());
|
|
100
|
+
this.syncLock = op.catch(() => { }); // heal chain so subsequent syncs run
|
|
101
|
+
return op;
|
|
102
|
+
}
|
|
103
|
+
async _syncInner() {
|
|
98
104
|
const { name } = this.requireRunning();
|
|
99
105
|
const beforeEntries = await this.getPailEntries();
|
|
100
106
|
if (this.pendingOps.length === 0) {
|
|
@@ -193,8 +199,11 @@ export class SyncEngine {
|
|
|
193
199
|
/**
|
|
194
200
|
* Mark the engine as stopped.
|
|
195
201
|
*/
|
|
196
|
-
stop() {
|
|
197
|
-
this.
|
|
202
|
+
async stop() {
|
|
203
|
+
this.syncLock = this.syncLock.then(() => {
|
|
204
|
+
this.state = { running: false };
|
|
205
|
+
});
|
|
206
|
+
return this.syncLock;
|
|
198
207
|
}
|
|
199
208
|
/**
|
|
200
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.16",
|
|
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",
|