@clanker-chain/clanker-cli 2026.9.8-2 → 2026.9.8

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 CHANGED
@@ -26,8 +26,6 @@ import { resolveForRead, resolveOperatorKey, resolveReadIdentity } from "../lib/
26
26
  import { runSetup } from "../lib/setup.mjs";
27
27
  import { runDoctor } from "../lib/doctor.mjs";
28
28
  import {
29
- botIdentityCard,
30
- botIdentityJson,
31
29
  hubConnectChecklist,
32
30
  wireOpenClawMqtt,
33
31
  } from "../lib/openclaw-wire.mjs";
@@ -617,30 +615,18 @@ async function main() {
617
615
  keyPath: result.key_path,
618
616
  });
619
617
  if (hasFlag(flags, "--json")) {
620
- printJson({
621
- ...result,
622
- openclaw: wire,
623
- bot_identity: botIdentityJson({
624
- botId: botLabel,
625
- keyPath: result.key_path,
626
- openclawConfigPath: wire.path,
627
- }),
628
- });
618
+ printJson({ ...result, openclaw: wire });
629
619
  } else {
630
620
  console.log(c.green(`Minted bot ${botLabel} under ${operatorLabel}`));
631
621
  console.log(`botKey: ${result.bot_key}`);
622
+ console.log(`key file: ${result.key_path}`);
623
+ console.log(`also: ${result.clanker_key_path}`);
632
624
  console.log(`tx: ${result.tx}`);
633
- console.log(c.dim(`also: ${result.clanker_key_path}`));
634
- console.log("");
635
- for (const line of botIdentityCard({
636
- botId: botLabel,
637
- operatorId: operatorLabel,
638
- keyPath: result.key_path,
639
- openclawPath: wire.path,
640
- created: wire.created,
641
- })) {
642
- console.log(line);
643
- }
625
+ console.log(
626
+ wire.created
627
+ ? c.green(`Wrote ${wire.path}`)
628
+ : c.green(`Updated channels.mqtt in ${wire.path}`),
629
+ );
644
630
  nextHint(
645
631
  hubConnectChecklist({
646
632
  botId: botLabel,
@@ -94,7 +94,6 @@ export function wireOpenClawMqtt(opts) {
94
94
  export function hubConnectChecklist(opts) {
95
95
  const pin = opts.pluginPin ?? OPENCLAW_PLUGIN_PIN;
96
96
  return [
97
- "Your bot login is already wired — install plugins and restart OpenClaw.",
98
97
  `openclaw plugins install @clanker-chain/mqtt-channel-plugin@${pin}`,
99
98
  `openclaw plugins install @clanker-chain/mqtt-tools@${pin}`,
100
99
  "Enable plugin ids mqtt + mqtt-tools (already set in openclaw.json if we wired it)",
@@ -103,43 +102,3 @@ export function hubConnectChecklist(opts) {
103
102
  "DM openclaw.france.prod-1 to smoke the mesh",
104
103
  ];
105
104
  }
106
-
107
- /**
108
- * Plain-language card: what the bot uses vs operator mint key.
109
- * @param {{
110
- * botId: string,
111
- * operatorId: string,
112
- * keyPath: string,
113
- * openclawPath: string,
114
- * created?: boolean,
115
- * }} opts
116
- * @returns {string[]}
117
- */
118
- export function botIdentityCard(opts) {
119
- const openclawState = opts.created
120
- ? `${opts.openclawPath} (channels.mqtt created)`
121
- : `${opts.openclawPath} (channels.mqtt updated)`;
122
- return [
123
- "Bot identity (what OpenClaw uses to CONNECT):",
124
- `bot id: ${opts.botId}`,
125
- `operator: ${opts.operatorId}`,
126
- `bot key: ${opts.keyPath}`,
127
- `openclaw: ${openclawState}`,
128
- "Do not give the bot ~/.clanker/op.key — that key is only for mint/transfer.",
129
- ];
130
- }
131
-
132
- /**
133
- * Structured bot_identity for --json mint output.
134
- * @param {{ botId: string, keyPath: string, openclawConfigPath: string }} opts
135
- */
136
- export function botIdentityJson(opts) {
137
- return {
138
- botId: opts.botId,
139
- keyPath: opts.keyPath,
140
- openclawConfigPath: opts.openclawConfigPath,
141
- warnOperatorKey:
142
- "Do not give the bot ~/.clanker/op.key — that key is only for mint/transfer.",
143
- };
144
- }
145
-
package/lib/setup.mjs CHANGED
@@ -33,11 +33,6 @@ import {
33
33
  exportFoundryKey,
34
34
  resolveFoundryAddress,
35
35
  } from "./foundry.mjs";
36
- import {
37
- BASE_SEPOLIA_FAUCET_URL,
38
- consumerFundHints,
39
- generateOperatorKeyFile,
40
- } from "./operator-key.mjs";
41
36
  import { c, nextHint } from "./ui.mjs";
42
37
  import { join } from "node:path";
43
38
 
@@ -57,7 +52,6 @@ export function parseSetupFlags(argv) {
57
52
  let skipChainCheck = false;
58
53
  let foundryAccount = null;
59
54
  let exportKey = false;
60
- let generateKey = false;
61
55
 
62
56
  for (let i = 0; i < argv.length; i += 1) {
63
57
  const a = argv[i];
@@ -69,7 +63,6 @@ export function parseSetupFlags(argv) {
69
63
  else if (a === "--from-block" && argv[i + 1]) fromBlock = BigInt(argv[++i]);
70
64
  else if (a === "--foundry-account" && argv[i + 1]) foundryAccount = argv[++i];
71
65
  else if (a === "--export-key") exportKey = true;
72
- else if (a === "--generate-key") generateKey = true;
73
66
  else if (a === "--force") force = true;
74
67
  else if (a === "--yes" || a === "-y") yes = true;
75
68
  else if (a === "--skip-key") skipKey = true;
@@ -89,7 +82,6 @@ export function parseSetupFlags(argv) {
89
82
  skipChainCheck,
90
83
  foundryAccount,
91
84
  exportKey,
92
- generateKey,
93
85
  };
94
86
  }
95
87
 
@@ -211,7 +203,7 @@ export async function runSetupNonInteractive(argv, opts = {}) {
211
203
  if (!flags.preset || !PRESETS[flags.preset]) {
212
204
  throw new Error(
213
205
  "Non-interactive setup requires --preset sepolia|local (stdin is not a TTY). " +
214
- "Also pass --operator <label> and --generate-key (or --address / --key-file)",
206
+ "Also pass --operator <label> and --address 0x…",
215
207
  );
216
208
  }
217
209
  if (!flags.operator) {
@@ -226,23 +218,6 @@ export async function runSetupNonInteractive(argv, opts = {}) {
226
218
  inheritStdio: false,
227
219
  };
228
220
 
229
- if (flags.generateKey) {
230
- if (flags.skipKey) {
231
- throw new Error("Cannot combine --generate-key with --skip-key");
232
- }
233
- const dest = keyFile || defaultOperatorKeyPath(home);
234
- const created = generateOperatorKeyFile(dest, {
235
- force: flags.force,
236
- });
237
- keyFile = created.path;
238
- if (address && getAddress(address) !== getAddress(created.address)) {
239
- throw new Error(
240
- `Generated key address ${created.address} does not match --address ${getAddress(address)}`,
241
- );
242
- }
243
- address = created.address;
244
- }
245
-
246
221
  if (flags.foundryAccount) {
247
222
  if (!address) {
248
223
  address = resolveFoundryAddress(flags.foundryAccount, castOpts);
@@ -263,7 +238,7 @@ export async function runSetupNonInteractive(argv, opts = {}) {
263
238
  if (!address && env.OPERATOR_PRIVATE_KEY) address = addressFromEnv(env);
264
239
  if (!address) {
265
240
  throw new Error(
266
- "Non-interactive setup requires --generate-key, --address 0x…, --key-file, OPERATOR_PRIVATE_KEY, or --foundry-account",
241
+ "Non-interactive setup requires --address 0x…, --key-file, OPERATOR_PRIVATE_KEY, or --foundry-account",
267
242
  );
268
243
  }
269
244
 
@@ -330,13 +305,9 @@ export async function runSetupInteractive(argv, opts = {}) {
330
305
  });
331
306
 
332
307
  clack.intro(c.bold("clanker setup"));
333
- clack.log.step(
334
- "Sets up your operator identity (org account) and network for OpenClaw bots.",
335
- );
308
+ clack.log.step("Creates ~/.clanker/config.json (network) and operator.json (identity).");
336
309
  clack.log.message(
337
- c.dim(
338
- "New here? Create a key file — no wallet app or Foundry required. Mint needs a little test ETH later.",
339
- ),
310
+ c.dim("whoami works from owner without a key; mint/revoke need a key pointer later."),
340
311
  );
341
312
  clack.log.message(c.dim(`Profile: ${home}`));
342
313
  console.log("");
@@ -395,28 +366,6 @@ export async function runSetupInteractive(argv, opts = {}) {
395
366
 
396
367
  let address = flags.address ?? null;
397
368
  let foundryAccountUsed = null;
398
- let generatedKeyFile = null;
399
- if (!address && flags.generateKey) {
400
- const dest = flags.keyFile || defaultOperatorKeyPath(home);
401
- let forceGen = flags.force;
402
- if (existsSync(dest) && !forceGen) {
403
- forceGen = cancelIf(
404
- await clack.confirm({
405
- message: `Overwrite existing ${dest}?`,
406
- initialValue: false,
407
- }),
408
- );
409
- if (!forceGen) {
410
- clack.cancel("Aborted.");
411
- process.exit(0);
412
- }
413
- }
414
- const created = generateOperatorKeyFile(dest, { force: true });
415
- generatedKeyFile = created.path;
416
- address = created.address;
417
- clack.log.success(`Created operator key at ${created.path}`);
418
- clack.log.info(`Your operator address: ${created.address}`);
419
- }
420
369
  if (!address && flags.keyFile) {
421
370
  address = addressFromKeyFile(flags.keyFile);
422
371
  clack.log.info(`Address from --key-file: ${address}`);
@@ -439,95 +388,23 @@ export async function runSetupInteractive(argv, opts = {}) {
439
388
  );
440
389
  if (useOp) address = hints.operator.owner;
441
390
  }
442
- if (!address) {
391
+ if (!address && hints.foundryAccounts.length) {
443
392
  const options = [
444
- {
445
- value: "__generate__",
446
- label: "Create a new operator key for me",
447
- hint: "writes ~/.clanker/op.key (recommended)",
448
- },
449
- {
450
- value: "__keyfile__",
451
- label: "Use an existing key file…",
452
- hint: "path to a 0x private key file",
453
- },
393
+ ...hints.foundryAccounts.map((n) => ({
394
+ value: n,
395
+ label: n,
396
+ hint: "resolve via cast wallet address",
397
+ })),
398
+ { value: "__paste__", label: "Paste an address…", hint: "0x…" },
454
399
  ];
455
- if (hints.foundryAccounts.length) {
456
- for (const n of hints.foundryAccounts) {
457
- options.push({
458
- value: n,
459
- label: `Foundry: ${n}`,
460
- hint: "advanced — cast wallet",
461
- });
462
- }
463
- } else {
464
- options.push({
465
- value: "__foundry_missing__",
466
- label: "Foundry account…",
467
- hint: "advanced — install Foundry first",
468
- });
469
- }
470
- options.push({
471
- value: "__paste__",
472
- label: "Paste an address…",
473
- hint: "read-only unless you add a key later",
474
- });
475
-
476
400
  const pick = cancelIf(
477
401
  await clack.select({
478
- message: "How do you want to set your operator identity?",
402
+ message: "Operator owner source",
479
403
  options,
480
- initialValue: "__generate__",
404
+ initialValue: hints.foundryAccounts[0],
481
405
  }),
482
406
  );
483
-
484
- if (pick === "__generate__") {
485
- const dest = defaultOperatorKeyPath(home);
486
- let forceGen = flags.force;
487
- if (existsSync(dest) && !forceGen) {
488
- forceGen = cancelIf(
489
- await clack.confirm({
490
- message: `Overwrite existing ${dest}?`,
491
- initialValue: false,
492
- }),
493
- );
494
- if (!forceGen) {
495
- clack.cancel("Aborted.");
496
- process.exit(0);
497
- }
498
- }
499
- const created = generateOperatorKeyFile(dest, { force: true });
500
- generatedKeyFile = created.path;
501
- address = created.address;
502
- clack.log.success(`Created operator key at ${created.path}`);
503
- clack.log.info(`Your operator address: ${created.address}`);
504
- } else if (pick === "__keyfile__") {
505
- const path = cancelIf(
506
- await clack.text({
507
- message: "Path to operator key file",
508
- placeholder: join(home, "op.key"),
509
- validate: (v) =>
510
- v && String(v).trim() && existsSync(String(v).trim())
511
- ? undefined
512
- : "File not found",
513
- }),
514
- );
515
- generatedKeyFile = String(path).trim();
516
- address = addressFromKeyFile(generatedKeyFile);
517
- clack.log.info(`Address from key file: ${address}`);
518
- } else if (pick === "__foundry_missing__") {
519
- clack.log.warn(
520
- "Foundry (cast) is not available. Install https://book.getfoundry.sh/ or choose Create a new operator key.",
521
- );
522
- address = cancelIf(
523
- await clack.text({
524
- message: "Operator owner address",
525
- placeholder: "0x…",
526
- validate: (v) =>
527
- /^0x[0-9a-fA-F]{40}$/.test(v || "") ? undefined : "Need 0x + 40 hex",
528
- }),
529
- );
530
- } else if (pick === "__paste__") {
407
+ if (pick === "__paste__") {
531
408
  address = cancelIf(
532
409
  await clack.text({
533
410
  message: "Operator owner address",
@@ -559,6 +436,16 @@ export async function runSetupInteractive(argv, opts = {}) {
559
436
  }
560
437
  }
561
438
  }
439
+ if (!address) {
440
+ address = cancelIf(
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
+ );
448
+ }
562
449
  address = getAddress(address);
563
450
 
564
451
  let label = flags.operator || hints.operator?.label || null;
@@ -596,7 +483,7 @@ export async function runSetupInteractive(argv, opts = {}) {
596
483
  throw err;
597
484
  }
598
485
 
599
- let keyFile = flags.keyFile || generatedKeyFile;
486
+ let keyFile = flags.keyFile;
600
487
  let keyEnv = flags.keyEnv;
601
488
  let skipKey = flags.skipKey;
602
489
  if (!skipKey && !keyFile && !keyEnv) {
@@ -692,16 +579,7 @@ export async function runSetupInteractive(argv, opts = {}) {
692
579
  if (!key) {
693
580
  nextHint([
694
581
  "clanker whoami",
695
- "clanker setup choose Create a new operator key when you need mint",
696
- ]);
697
- } else if (preset === "sepolia" && generatedKeyFile) {
698
- nextHint(consumerFundHints({ address, label }));
699
- } else if (preset === "sepolia") {
700
- nextHint([
701
- `If this address needs test ETH: ${BASE_SEPOLIA_FAUCET_URL}`,
702
- "clanker doctor",
703
- "clanker whoami",
704
- `clanker bot mint <label>`,
582
+ "clanker setup --key-file ~/.clanker/op.key --force # when you need mint",
705
583
  ]);
706
584
  } else {
707
585
  nextHint(["clanker whoami", `clanker bot mint <label>`]);
@@ -715,20 +593,23 @@ export async function runSetupInteractive(argv, opts = {}) {
715
593
  export async function runSetup(argv, opts = {}) {
716
594
  const flags = parseSetupFlags(argv);
717
595
  const isTTY = opts.isTTY ?? Boolean(input.isTTY);
718
- const hasIdentitySource = Boolean(
719
- flags.address ||
720
- flags.keyFile ||
721
- flags.generateKey ||
722
- flags.foundryAccount ||
723
- opts.env?.OPERATOR_PRIVATE_KEY ||
724
- process.env.OPERATOR_PRIVATE_KEY,
725
- );
726
596
 
727
- if (!isTTY || (flags.yes && flags.preset && flags.operator && hasIdentitySource)) {
597
+ if (
598
+ !isTTY ||
599
+ (flags.yes && flags.preset && flags.operator && (flags.address || flags.keyFile))
600
+ ) {
728
601
  if (!isTTY && !(flags.preset && flags.operator)) {
729
602
  return runSetupNonInteractive(argv, opts);
730
603
  }
731
- if (flags.yes && flags.preset && flags.operator && hasIdentitySource) {
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
+ ) {
732
613
  return runSetupNonInteractive(argv, opts);
733
614
  }
734
615
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clanker-chain/clanker-cli",
3
- "version": "2026.9.8-2",
3
+ "version": "2026.9.8",
4
4
  "description": "CLI for wiring clanker-chain identity and MQTT into OpenClaw and other agentic stacks.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,50 +0,0 @@
1
- /**
2
- * Consumer-path operator key helpers (generate local op.key).
3
- * Never logs private keys.
4
- */
5
-
6
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
7
- import { dirname } from "node:path";
8
- import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
9
- import { defaultOperatorKeyPath } from "./foundry.mjs";
10
-
11
- export { defaultOperatorKeyPath };
12
-
13
- /** Coinbase Developer Platform faucet UI (Base Sepolia). */
14
- export const BASE_SEPOLIA_FAUCET_URL =
15
- "https://portal.cdp.coinbase.com/products/faucet";
16
-
17
- /**
18
- * Generate a new secp256k1 key and write it to destPath (mode 0o600).
19
- * Does not print the key.
20
- *
21
- * @param {string} destPath
22
- * @param {{ force?: boolean }} [opts]
23
- * @returns {{ path: string, address: string }}
24
- */
25
- export function generateOperatorKeyFile(destPath, opts = {}) {
26
- if (existsSync(destPath) && !opts.force) {
27
- throw new Error(
28
- `Key file already exists: ${destPath} (pass --force to overwrite)`,
29
- );
30
- }
31
- const key = generatePrivateKey();
32
- const address = privateKeyToAccount(key).address;
33
- mkdirSync(dirname(destPath), { recursive: true });
34
- writeFileSync(destPath, `${key}\n`, { mode: 0o600 });
35
- return { path: destPath, address };
36
- }
37
-
38
- /**
39
- * Plain-language next steps after creating an operator key on Sepolia.
40
- * @param {{ address: string, label?: string }} opts
41
- */
42
- export function consumerFundHints(opts) {
43
- const mintLabel = opts.label ? ` ${opts.label}` : "";
44
- return [
45
- `Your operator address is ${opts.address} — fund it with Base Sepolia ETH: ${BASE_SEPOLIA_FAUCET_URL}`,
46
- "clanker doctor",
47
- `clanker operator mint${mintLabel} --yes`,
48
- "clanker bot mint <bot_label> --yes",
49
- ];
50
- }