@clanker-chain/clanker-cli 2026.9.8 → 2026.9.12-1
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/bin/clanker.mjs +240 -22
- package/lib/clanker-identity-abi.mjs +18 -0
- package/lib/doctor.mjs +100 -8
- package/lib/fund.mjs +287 -0
- package/lib/identity-query.mjs +1 -1
- package/lib/mint-budget.mjs +237 -0
- package/lib/openclaw-wire.mjs +43 -2
- package/lib/operator-key.mjs +57 -0
- package/lib/pair.mjs +252 -0
- package/lib/resolve.mjs +10 -6
- package/lib/setup.mjs +240 -42
- package/package.json +5 -2
package/lib/setup.mjs
CHANGED
|
@@ -3,7 +3,14 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { stdin as input } from "node:process";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
chmodSync,
|
|
8
|
+
copyFileSync,
|
|
9
|
+
existsSync,
|
|
10
|
+
mkdirSync,
|
|
11
|
+
} from "node:fs";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import { basename, join } from "node:path";
|
|
7
14
|
import { getAddress } from "viem";
|
|
8
15
|
import * as clack from "@clack/prompts";
|
|
9
16
|
import {
|
|
@@ -33,8 +40,12 @@ import {
|
|
|
33
40
|
exportFoundryKey,
|
|
34
41
|
resolveFoundryAddress,
|
|
35
42
|
} from "./foundry.mjs";
|
|
43
|
+
import {
|
|
44
|
+
consumerFundHints,
|
|
45
|
+
generateOperatorKeyFile,
|
|
46
|
+
} from "./operator-key.mjs";
|
|
47
|
+
import { wireOpenClawMqtt } from "./openclaw-wire.mjs";
|
|
36
48
|
import { c, nextHint } from "./ui.mjs";
|
|
37
|
-
import { join } from "node:path";
|
|
38
49
|
|
|
39
50
|
/**
|
|
40
51
|
* @param {string[]} argv
|
|
@@ -52,6 +63,8 @@ export function parseSetupFlags(argv) {
|
|
|
52
63
|
let skipChainCheck = false;
|
|
53
64
|
let foundryAccount = null;
|
|
54
65
|
let exportKey = false;
|
|
66
|
+
let generateKey = false;
|
|
67
|
+
let botKey = null;
|
|
55
68
|
|
|
56
69
|
for (let i = 0; i < argv.length; i += 1) {
|
|
57
70
|
const a = argv[i];
|
|
@@ -62,7 +75,9 @@ export function parseSetupFlags(argv) {
|
|
|
62
75
|
else if (a === "--key-env" && argv[i + 1]) keyEnv = argv[++i];
|
|
63
76
|
else if (a === "--from-block" && argv[i + 1]) fromBlock = BigInt(argv[++i]);
|
|
64
77
|
else if (a === "--foundry-account" && argv[i + 1]) foundryAccount = argv[++i];
|
|
78
|
+
else if (a === "--bot-key" && argv[i + 1]) botKey = argv[++i];
|
|
65
79
|
else if (a === "--export-key") exportKey = true;
|
|
80
|
+
else if (a === "--generate-key") generateKey = true;
|
|
66
81
|
else if (a === "--force") force = true;
|
|
67
82
|
else if (a === "--yes" || a === "-y") yes = true;
|
|
68
83
|
else if (a === "--skip-key") skipKey = true;
|
|
@@ -82,6 +97,8 @@ export function parseSetupFlags(argv) {
|
|
|
82
97
|
skipChainCheck,
|
|
83
98
|
foundryAccount,
|
|
84
99
|
exportKey,
|
|
100
|
+
generateKey,
|
|
101
|
+
botKey,
|
|
85
102
|
};
|
|
86
103
|
}
|
|
87
104
|
|
|
@@ -155,6 +172,39 @@ export function buildKeyPointer({ keyFile, keyEnv, skipKey, env = process.env })
|
|
|
155
172
|
return null;
|
|
156
173
|
}
|
|
157
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Copy a downloaded bot key into ~/.openclaw/keys and wire channels.mqtt.
|
|
177
|
+
* @param {string} botKeyPath
|
|
178
|
+
* @param {{
|
|
179
|
+
* operatorLabel: string,
|
|
180
|
+
* network: { rpc: string, registry: string|null, brokerUrl?: string, mqttAuthServiceUrl?: string },
|
|
181
|
+
* openclawHome?: string,
|
|
182
|
+
* }} opts
|
|
183
|
+
*/
|
|
184
|
+
export function attachBotKeyFile(botKeyPath, opts) {
|
|
185
|
+
if (!existsSync(botKeyPath)) {
|
|
186
|
+
throw new Error(`Bot key file not found: ${botKeyPath}`);
|
|
187
|
+
}
|
|
188
|
+
const botId = basename(botKeyPath).replace(/\.key$/i, "");
|
|
189
|
+
if (!botId || botId.includes("/") || botId.includes("..")) {
|
|
190
|
+
throw new Error(`Invalid bot key basename: ${botKeyPath}`);
|
|
191
|
+
}
|
|
192
|
+
const openclawHome = opts.openclawHome ?? join(homedir(), ".openclaw");
|
|
193
|
+
const destDir = join(openclawHome, "keys");
|
|
194
|
+
mkdirSync(destDir, { recursive: true });
|
|
195
|
+
const dest = join(destDir, `${botId}.key`);
|
|
196
|
+
copyFileSync(botKeyPath, dest);
|
|
197
|
+
chmodSync(dest, 0o600);
|
|
198
|
+
const wired = wireOpenClawMqtt({
|
|
199
|
+
botId,
|
|
200
|
+
operatorId: opts.operatorLabel,
|
|
201
|
+
network: opts.network,
|
|
202
|
+
keyPath: dest,
|
|
203
|
+
openclawHome,
|
|
204
|
+
});
|
|
205
|
+
return { botId, keyPath: dest, openclawPath: wired.path };
|
|
206
|
+
}
|
|
207
|
+
|
|
158
208
|
/**
|
|
159
209
|
* Apply setup choices to disk.
|
|
160
210
|
*/
|
|
@@ -203,7 +253,7 @@ export async function runSetupNonInteractive(argv, opts = {}) {
|
|
|
203
253
|
if (!flags.preset || !PRESETS[flags.preset]) {
|
|
204
254
|
throw new Error(
|
|
205
255
|
"Non-interactive setup requires --preset sepolia|local (stdin is not a TTY). " +
|
|
206
|
-
"Also pass --operator <label> and --address
|
|
256
|
+
"Also pass --operator <label> and --generate-key (or --address / --key-file)",
|
|
207
257
|
);
|
|
208
258
|
}
|
|
209
259
|
if (!flags.operator) {
|
|
@@ -218,6 +268,23 @@ export async function runSetupNonInteractive(argv, opts = {}) {
|
|
|
218
268
|
inheritStdio: false,
|
|
219
269
|
};
|
|
220
270
|
|
|
271
|
+
if (flags.generateKey) {
|
|
272
|
+
if (flags.skipKey) {
|
|
273
|
+
throw new Error("Cannot combine --generate-key with --skip-key");
|
|
274
|
+
}
|
|
275
|
+
const dest = keyFile || defaultOperatorKeyPath(home);
|
|
276
|
+
const created = generateOperatorKeyFile(dest, {
|
|
277
|
+
force: flags.force,
|
|
278
|
+
});
|
|
279
|
+
keyFile = created.path;
|
|
280
|
+
if (address && getAddress(address) !== getAddress(created.address)) {
|
|
281
|
+
throw new Error(
|
|
282
|
+
`Generated key address ${created.address} does not match --address ${getAddress(address)}`,
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
address = created.address;
|
|
286
|
+
}
|
|
287
|
+
|
|
221
288
|
if (flags.foundryAccount) {
|
|
222
289
|
if (!address) {
|
|
223
290
|
address = resolveFoundryAddress(flags.foundryAccount, castOpts);
|
|
@@ -238,7 +305,7 @@ export async function runSetupNonInteractive(argv, opts = {}) {
|
|
|
238
305
|
if (!address && env.OPERATOR_PRIVATE_KEY) address = addressFromEnv(env);
|
|
239
306
|
if (!address) {
|
|
240
307
|
throw new Error(
|
|
241
|
-
"Non-interactive setup requires --address 0x…, --key-file, OPERATOR_PRIVATE_KEY, or --foundry-account",
|
|
308
|
+
"Non-interactive setup requires --generate-key, --address 0x…, --key-file, OPERATOR_PRIVATE_KEY, or --foundry-account",
|
|
242
309
|
);
|
|
243
310
|
}
|
|
244
311
|
|
|
@@ -275,7 +342,7 @@ export async function runSetupNonInteractive(argv, opts = {}) {
|
|
|
275
342
|
}
|
|
276
343
|
}
|
|
277
344
|
|
|
278
|
-
|
|
345
|
+
const result = applySetup(
|
|
279
346
|
{
|
|
280
347
|
preset: flags.preset,
|
|
281
348
|
force: flags.force || flags.yes,
|
|
@@ -287,6 +354,34 @@ export async function runSetupNonInteractive(argv, opts = {}) {
|
|
|
287
354
|
},
|
|
288
355
|
{ home, env },
|
|
289
356
|
);
|
|
357
|
+
return finishSetupResult(result, flags, { ...opts, quiet: true });
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* @param {object} result
|
|
362
|
+
* @param {{ botKey?: string|null, operator?: string|null }} flags
|
|
363
|
+
* @param {{ openclawHome?: string, quiet?: boolean }} [opts]
|
|
364
|
+
*/
|
|
365
|
+
async function finishSetupResult(result, flags, opts = {}) {
|
|
366
|
+
if (flags.botKey) {
|
|
367
|
+
const attached = attachBotKeyFile(flags.botKey, {
|
|
368
|
+
operatorLabel: result.operator?.label ?? flags.operator,
|
|
369
|
+
network: {
|
|
370
|
+
rpc: result.config.chainRpcUrl,
|
|
371
|
+
registry: result.config.registryAddress,
|
|
372
|
+
brokerUrl: result.config.brokerUrl,
|
|
373
|
+
mqttAuthServiceUrl: result.config.mqttAuthServiceUrl,
|
|
374
|
+
},
|
|
375
|
+
openclawHome: opts.openclawHome,
|
|
376
|
+
});
|
|
377
|
+
result.botKey = attached;
|
|
378
|
+
if (!opts.quiet) {
|
|
379
|
+
clack.log.success(
|
|
380
|
+
`Bot key → ${attached.keyPath}; wired ${attached.openclawPath}`,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return result;
|
|
290
385
|
}
|
|
291
386
|
|
|
292
387
|
/**
|
|
@@ -305,9 +400,13 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
305
400
|
});
|
|
306
401
|
|
|
307
402
|
clack.intro(c.bold("clanker setup"));
|
|
308
|
-
clack.log.step(
|
|
403
|
+
clack.log.step(
|
|
404
|
+
"Sets up your operator identity (org account) and network for OpenClaw bots.",
|
|
405
|
+
);
|
|
309
406
|
clack.log.message(
|
|
310
|
-
c.dim(
|
|
407
|
+
c.dim(
|
|
408
|
+
"Attach an existing owner address (/join or any 0x), or create ~/.clanker/op.key. Mint needs test ETH later.",
|
|
409
|
+
),
|
|
311
410
|
);
|
|
312
411
|
clack.log.message(c.dim(`Profile: ${home}`));
|
|
313
412
|
console.log("");
|
|
@@ -366,6 +465,28 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
366
465
|
|
|
367
466
|
let address = flags.address ?? null;
|
|
368
467
|
let foundryAccountUsed = null;
|
|
468
|
+
let generatedKeyFile = null;
|
|
469
|
+
if (!address && flags.generateKey) {
|
|
470
|
+
const dest = flags.keyFile || defaultOperatorKeyPath(home);
|
|
471
|
+
let forceGen = flags.force;
|
|
472
|
+
if (existsSync(dest) && !forceGen) {
|
|
473
|
+
forceGen = cancelIf(
|
|
474
|
+
await clack.confirm({
|
|
475
|
+
message: `Overwrite existing ${dest}?`,
|
|
476
|
+
initialValue: false,
|
|
477
|
+
}),
|
|
478
|
+
);
|
|
479
|
+
if (!forceGen) {
|
|
480
|
+
clack.cancel("Aborted.");
|
|
481
|
+
process.exit(0);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
const created = generateOperatorKeyFile(dest, { force: true });
|
|
485
|
+
generatedKeyFile = created.path;
|
|
486
|
+
address = created.address;
|
|
487
|
+
clack.log.success(`Created operator key at ${created.path}`);
|
|
488
|
+
clack.log.info(`Your operator address: ${created.address}`);
|
|
489
|
+
}
|
|
369
490
|
if (!address && flags.keyFile) {
|
|
370
491
|
address = addressFromKeyFile(flags.keyFile);
|
|
371
492
|
clack.log.info(`Address from --key-file: ${address}`);
|
|
@@ -388,23 +509,87 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
388
509
|
);
|
|
389
510
|
if (useOp) address = hints.operator.owner;
|
|
390
511
|
}
|
|
391
|
-
if (!address
|
|
512
|
+
if (!address) {
|
|
392
513
|
const options = [
|
|
393
|
-
|
|
394
|
-
value:
|
|
395
|
-
label:
|
|
396
|
-
hint: "
|
|
397
|
-
}
|
|
398
|
-
{
|
|
514
|
+
{
|
|
515
|
+
value: "__paste__",
|
|
516
|
+
label: "I already have an owner address",
|
|
517
|
+
hint: "/join or any 0x — usually read-only",
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
value: "__generate__",
|
|
521
|
+
label: "Create a new operator key for me",
|
|
522
|
+
hint: "writes ~/.clanker/op.key",
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
value: "__keyfile__",
|
|
526
|
+
label: "Use an existing key file…",
|
|
527
|
+
hint: "path to a 0x private key file",
|
|
528
|
+
},
|
|
399
529
|
];
|
|
530
|
+
if (hints.foundryAccounts.length) {
|
|
531
|
+
for (const n of hints.foundryAccounts) {
|
|
532
|
+
options.push({
|
|
533
|
+
value: n,
|
|
534
|
+
label: `Foundry: ${n}`,
|
|
535
|
+
hint: "advanced — cast wallet",
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
} else {
|
|
539
|
+
options.push({
|
|
540
|
+
value: "__foundry_missing__",
|
|
541
|
+
label: "Foundry account…",
|
|
542
|
+
hint: "advanced — install Foundry first",
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
|
|
400
546
|
const pick = cancelIf(
|
|
401
547
|
await clack.select({
|
|
402
|
-
message: "
|
|
548
|
+
message: "How do you want to set your operator identity?",
|
|
403
549
|
options,
|
|
404
|
-
initialValue:
|
|
550
|
+
initialValue: "__paste__",
|
|
405
551
|
}),
|
|
406
552
|
);
|
|
407
|
-
|
|
553
|
+
|
|
554
|
+
let attachReadOnly = false;
|
|
555
|
+
if (pick === "__generate__") {
|
|
556
|
+
const dest = defaultOperatorKeyPath(home);
|
|
557
|
+
let forceGen = flags.force;
|
|
558
|
+
if (existsSync(dest) && !forceGen) {
|
|
559
|
+
forceGen = cancelIf(
|
|
560
|
+
await clack.confirm({
|
|
561
|
+
message: `Overwrite existing ${dest}?`,
|
|
562
|
+
initialValue: false,
|
|
563
|
+
}),
|
|
564
|
+
);
|
|
565
|
+
if (!forceGen) {
|
|
566
|
+
clack.cancel("Aborted.");
|
|
567
|
+
process.exit(0);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
const created = generateOperatorKeyFile(dest, { force: true });
|
|
571
|
+
generatedKeyFile = created.path;
|
|
572
|
+
address = created.address;
|
|
573
|
+
clack.log.success(`Created operator key at ${created.path}`);
|
|
574
|
+
clack.log.info(`Your operator address: ${created.address}`);
|
|
575
|
+
} else if (pick === "__keyfile__") {
|
|
576
|
+
const path = cancelIf(
|
|
577
|
+
await clack.text({
|
|
578
|
+
message: "Path to operator key file",
|
|
579
|
+
placeholder: join(home, "op.key"),
|
|
580
|
+
validate: (v) =>
|
|
581
|
+
v && String(v).trim() && existsSync(String(v).trim())
|
|
582
|
+
? undefined
|
|
583
|
+
: "File not found",
|
|
584
|
+
}),
|
|
585
|
+
);
|
|
586
|
+
generatedKeyFile = String(path).trim();
|
|
587
|
+
address = addressFromKeyFile(generatedKeyFile);
|
|
588
|
+
clack.log.info(`Address from key file: ${address}`);
|
|
589
|
+
} else if (pick === "__foundry_missing__") {
|
|
590
|
+
clack.log.warn(
|
|
591
|
+
"Foundry (cast) is not available. Install https://book.getfoundry.sh/ or choose Create a new operator key.",
|
|
592
|
+
);
|
|
408
593
|
address = cancelIf(
|
|
409
594
|
await clack.text({
|
|
410
595
|
message: "Operator owner address",
|
|
@@ -413,6 +598,17 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
413
598
|
/^0x[0-9a-fA-F]{40}$/.test(v || "") ? undefined : "Need 0x + 40 hex",
|
|
414
599
|
}),
|
|
415
600
|
);
|
|
601
|
+
attachReadOnly = true;
|
|
602
|
+
} else if (pick === "__paste__") {
|
|
603
|
+
address = cancelIf(
|
|
604
|
+
await clack.text({
|
|
605
|
+
message: "Owner address (from /join or any EOA)",
|
|
606
|
+
placeholder: "0x…",
|
|
607
|
+
validate: (v) =>
|
|
608
|
+
/^0x[0-9a-fA-F]{40}$/.test(v || "") ? undefined : "Need 0x + 40 hex",
|
|
609
|
+
}),
|
|
610
|
+
);
|
|
611
|
+
attachReadOnly = true;
|
|
416
612
|
} else {
|
|
417
613
|
foundryAccountUsed = pick;
|
|
418
614
|
try {
|
|
@@ -435,16 +631,9 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
435
631
|
);
|
|
436
632
|
}
|
|
437
633
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
await clack.text({
|
|
442
|
-
message: "Operator owner address",
|
|
443
|
-
placeholder: "0x…",
|
|
444
|
-
validate: (v) =>
|
|
445
|
-
/^0x[0-9a-fA-F]{40}$/.test(v || "") ? undefined : "Need 0x + 40 hex",
|
|
446
|
-
}),
|
|
447
|
-
);
|
|
634
|
+
if (attachReadOnly) {
|
|
635
|
+
flags.skipKey = true;
|
|
636
|
+
}
|
|
448
637
|
}
|
|
449
638
|
address = getAddress(address);
|
|
450
639
|
|
|
@@ -483,7 +672,7 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
483
672
|
throw err;
|
|
484
673
|
}
|
|
485
674
|
|
|
486
|
-
let keyFile = flags.keyFile;
|
|
675
|
+
let keyFile = flags.keyFile || generatedKeyFile;
|
|
487
676
|
let keyEnv = flags.keyEnv;
|
|
488
677
|
let skipKey = flags.skipKey;
|
|
489
678
|
if (!skipKey && !keyFile && !keyEnv) {
|
|
@@ -575,11 +764,23 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
575
764
|
{ home, env },
|
|
576
765
|
);
|
|
577
766
|
|
|
767
|
+
await finishSetupResult(result, { ...flags, operator: label }, opts);
|
|
768
|
+
|
|
578
769
|
clack.outro(c.green(`Wrote ${result.configPath}\nWrote ${result.operatorPath}`));
|
|
579
770
|
if (!key) {
|
|
580
771
|
nextHint([
|
|
772
|
+
"clanker fund",
|
|
581
773
|
"clanker whoami",
|
|
582
|
-
"
|
|
774
|
+
"mint/pair/rotate need a signing key or stay on /join",
|
|
775
|
+
]);
|
|
776
|
+
} else if (preset === "sepolia" && generatedKeyFile) {
|
|
777
|
+
nextHint(consumerFundHints({ address, label }));
|
|
778
|
+
} else if (preset === "sepolia") {
|
|
779
|
+
nextHint([
|
|
780
|
+
"clanker fund",
|
|
781
|
+
"clanker doctor",
|
|
782
|
+
"clanker whoami",
|
|
783
|
+
`clanker bot mint <label>`,
|
|
583
784
|
]);
|
|
584
785
|
} else {
|
|
585
786
|
nextHint(["clanker whoami", `clanker bot mint <label>`]);
|
|
@@ -593,23 +794,20 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
593
794
|
export async function runSetup(argv, opts = {}) {
|
|
594
795
|
const flags = parseSetupFlags(argv);
|
|
595
796
|
const isTTY = opts.isTTY ?? Boolean(input.isTTY);
|
|
797
|
+
const hasIdentitySource = Boolean(
|
|
798
|
+
flags.address ||
|
|
799
|
+
flags.keyFile ||
|
|
800
|
+
flags.generateKey ||
|
|
801
|
+
flags.foundryAccount ||
|
|
802
|
+
opts.env?.OPERATOR_PRIVATE_KEY ||
|
|
803
|
+
process.env.OPERATOR_PRIVATE_KEY,
|
|
804
|
+
);
|
|
596
805
|
|
|
597
|
-
if (
|
|
598
|
-
!isTTY ||
|
|
599
|
-
(flags.yes && flags.preset && flags.operator && (flags.address || flags.keyFile))
|
|
600
|
-
) {
|
|
806
|
+
if (!isTTY || (flags.yes && flags.preset && flags.operator && hasIdentitySource)) {
|
|
601
807
|
if (!isTTY && !(flags.preset && flags.operator)) {
|
|
602
808
|
return runSetupNonInteractive(argv, opts);
|
|
603
809
|
}
|
|
604
|
-
if (
|
|
605
|
-
flags.yes &&
|
|
606
|
-
flags.preset &&
|
|
607
|
-
flags.operator &&
|
|
608
|
-
(flags.address ||
|
|
609
|
-
flags.keyFile ||
|
|
610
|
-
opts.env?.OPERATOR_PRIVATE_KEY ||
|
|
611
|
-
process.env.OPERATOR_PRIVATE_KEY)
|
|
612
|
-
) {
|
|
810
|
+
if (flags.yes && flags.preset && flags.operator && hasIdentitySource) {
|
|
613
811
|
return runSetupNonInteractive(argv, opts);
|
|
614
812
|
}
|
|
615
813
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clanker-chain/clanker-cli",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.12-1",
|
|
4
4
|
"description": "CLI for wiring clanker-chain identity and MQTT into OpenClaw and other agentic stacks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@clack/prompts": "^1.8.0",
|
|
27
27
|
"picocolors": "^1.1.1",
|
|
28
|
-
"viem": "^2.
|
|
28
|
+
"viem": "^2.56.3"
|
|
29
|
+
},
|
|
30
|
+
"overrides": {
|
|
31
|
+
"ws": "^8.21.0"
|
|
29
32
|
}
|
|
30
33
|
}
|