@bankr/cli 0.2.15 → 0.3.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/README.md CHANGED
@@ -159,7 +159,7 @@ Environment variables override stored values:
159
159
 
160
160
  ## API Key
161
161
 
162
- Generate an API key at [bankr.bot/api](https://bankr.bot/api). Keys start with `bk_` and require Agent API access to be enabled.
162
+ Generate an API key at [bankr.bot/api-keys](https://bankr.bot/api-keys). Keys start with `bk_` and require Agent API access to be enabled.
163
163
 
164
164
  ## License
165
165
 
package/dist/cli.js CHANGED
@@ -5,6 +5,7 @@ import { input } from "@inquirer/prompts";
5
5
  import { balancesCommand } from "./commands/balances.js";
6
6
  import { portfolioCommand } from "./commands/portfolio.js";
7
7
  import { transferCommand } from "./commands/transfer.js";
8
+ import { swapCommand } from "./commands/swap.js";
8
9
  import { skillsCommand } from "./commands/capabilities.js";
9
10
  import { cancelCommand } from "./commands/cancel.js";
10
11
  import { configGetCommand, configSetCommand } from "./commands/config.js";
@@ -48,6 +49,10 @@ async function readPromptInput() {
48
49
  }
49
50
  return text;
50
51
  }
52
+ if (output.isNonInteractive()) {
53
+ output.error('No prompt provided. Pass it as an argument: bankr agent "your prompt"');
54
+ process.exit(1);
55
+ }
51
56
  // Interactive input — shell expansion does not apply here
52
57
  const text = await input({
53
58
  message: "Enter your prompt:",
@@ -61,15 +66,6 @@ function colorizeHelp(text) {
61
66
  return text
62
67
  .split("\n")
63
68
  .map((line) => {
64
- // "Usage: ..." line
65
- if (line.startsWith("Usage:"))
66
- return output.fmt.brandBold(line);
67
- // Section headers: "Options:" and "Commands:"
68
- if (/^(Options|Commands):/.test(line))
69
- return output.fmt.brandBold(line);
70
- // Description line (standalone, no leading whitespace, no colon)
71
- if (line === "Bankr AI agent CLI")
72
- return output.fmt.brandBold(line);
73
69
  // Command/option rows: leading whitespace then content
74
70
  if (/^\s{2,}\S/.test(line)) {
75
71
  const match = line.match(/^(\s+)([\w<>[\].\-,/ ]+?)(\s{2,}.+)$/);
@@ -83,22 +79,30 @@ function colorizeHelp(text) {
83
79
  }
84
80
  program
85
81
  .name("bankr")
86
- .description("Bankr AI agent CLI\n\n Docs: https://docs.bankr.bot\n Skills: https://docs.bankr.bot/openclaw/installation")
82
+ .description("Bankr AI agent CLI\n\n Docs: https://docs.bankr.bot\n Skills: https://docs.bankr.bot/skills/overview")
87
83
  .version(pkg.version, "-v, --version", "Show CLI version")
88
84
  .helpOption("-h, --help", "Display help")
89
- .addHelpCommand("help [command]", "Display help for command")
85
+ .helpCommand(false)
90
86
  .option("--config <path>", "Path to config file (default: ~/.bankr/config.json)")
87
+ .option("--ni, --not-interactive", "Non-interactive mode: skip confirmation prompts (implies --yes) and fail fast on required input. Also enabled by BANKR_NOT_INTERACTIVE=1")
88
+ .configureHelp({
89
+ styleTitle: (str) => output.fmt.brandBold(str),
90
+ })
91
91
  .configureOutput({
92
92
  writeOut: (str) => process.stdout.write(colorizeHelp(str)),
93
93
  writeErr: (str) => process.stderr.write(colorizeHelp(str)),
94
94
  })
95
95
  .hook("preAction", (thisCommand) => {
96
- const configPath = thisCommand.opts().config;
97
- if (configPath) {
98
- setConfigPath(configPath);
96
+ const opts = thisCommand.opts();
97
+ if (opts.notInteractive) {
98
+ process.env.BANKR_NOT_INTERACTIVE = "1";
99
+ }
100
+ if (opts.config) {
101
+ setConfigPath(opts.config);
99
102
  }
100
103
  });
101
104
  // ── Core commands ───────────────────────────────────────────────────────
105
+ program.commandsGroup("Account");
102
106
  const loginCmd = program
103
107
  .command("login")
104
108
  .description("Authenticate with the Bankr API")
@@ -120,10 +124,12 @@ loginCmd
120
124
  .option("--code <otp>", "OTP code (step 2 of headless login)")
121
125
  .option("--accept-terms", "Accept Terms of Service (skips prompt)")
122
126
  .option("--key-name <name>", "API key name (default: CLI)")
127
+ .optionsGroup("API Permissions")
123
128
  .option("--read-write", "Disable read-only mode (allow transactions)")
124
129
  .option("--no-wallet-api", "Disable Wallet API (enabled by default)")
125
130
  .option("--no-token-launch", "Disable Token Launch API (enabled by default)")
126
131
  .option("--llm", "Enable LLM gateway on the API key")
132
+ .optionsGroup("Security")
127
133
  .option("--allowed-ips <ips>", "Comma-separated IP/CIDR allowlist, e.g. 1.2.3.4,10.0.0.0/24")
128
134
  .option("--allowed-recipients <addresses>", "Comma-separated EVM/Solana addresses (agent will only send funds to these)")
129
135
  .action(async (address, opts, cmd) => {
@@ -147,11 +153,12 @@ loginCmd
147
153
  .command("siwe")
148
154
  .description("Sign in with Ethereum wallet (SIWE, for headless agents)")
149
155
  .requiredOption("--private-key <key>", "Ethereum private key (0x...)")
150
- .option("--partner-key <key>", "Partner API key for fee attribution")
151
156
  .option("--key-name <name>", "API key name (default: SIWE-<date>)")
157
+ .optionsGroup("API Permissions")
152
158
  .option("--read-write", "Disable read-only mode (allow transactions)")
153
159
  .option("--no-wallet-api", "Disable Wallet API (enabled by default)")
154
160
  .option("--no-token-launch", "Disable Token Launch API (enabled by default)")
161
+ .optionsGroup("Security")
155
162
  .option("--allowed-ips <ips>", "Comma-separated IP/CIDR allowlist, e.g. 1.2.3.4,10.0.0.0/24")
156
163
  .option("--allowed-recipients <addresses>", "Comma-separated EVM/Solana addresses (agent will only send funds to these)")
157
164
  .action(async (opts, cmd) => {
@@ -160,7 +167,6 @@ loginCmd
160
167
  apiUrl: parentOpts.apiUrl,
161
168
  siwe: true,
162
169
  privateKey: opts.privateKey,
163
- partnerKey: opts.partnerKey,
164
170
  keyName: opts.keyName,
165
171
  readWrite: opts.readWrite,
166
172
  walletApi: opts.walletApi,
@@ -174,6 +180,7 @@ program
174
180
  .description("Show wallet info and account details")
175
181
  .action(whoamiCommand);
176
182
  // Wallet commands (non-agentic operations)
183
+ program.commandsGroup("Wallet");
177
184
  const walletCmd = program
178
185
  .command("wallet")
179
186
  .description("Wallet info, portfolio, transfer, sign, and submit")
@@ -209,6 +216,23 @@ walletCmd
209
216
  chain: opts.chain,
210
217
  });
211
218
  });
219
+ walletCmd
220
+ .command("swap")
221
+ .description("Swap tokens on an EVM chain (same-chain)")
222
+ .requiredOption("--from <symbol or address>", "Token to sell: symbol (ETH, USDC) or contract address")
223
+ .requiredOption("--to <symbol or address>", "Token to buy: symbol (ETH, USDC) or contract address")
224
+ .requiredOption("--amount <amount>", "Amount of --from to sell (human-readable)")
225
+ .option("--chain <chain>", "EVM chain: base, polygon, mainnet, unichain, worldchain, arbitrum, bnb (default: base)")
226
+ .option("--quote-only", "Print the quote without executing the swap")
227
+ .action(async (opts) => {
228
+ await swapCommand({
229
+ from: opts.from,
230
+ to: opts.to,
231
+ amount: opts.amount,
232
+ chain: opts.chain,
233
+ quoteOnly: opts.quoteOnly,
234
+ });
235
+ });
212
236
  walletCmd
213
237
  .command("sign")
214
238
  .description("Sign messages, typed data, or transactions")
@@ -268,6 +292,7 @@ walletSubmitCmd
268
292
  });
269
293
  });
270
294
  // Agent commands (agentic operations that use LLM)
295
+ program.commandsGroup("Agent");
271
296
  const agentCmd = program
272
297
  .command("agent [text...]")
273
298
  .description("AI agent commands (prompt, status, cancel, profile, skills)")
@@ -278,10 +303,14 @@ const agentCmd = program
278
303
  opts.model ?? (opts.model = cmd.parent?.opts()?.model);
279
304
  const joined = text.join(" ").trim();
280
305
  if (!joined) {
306
+ if (output.isNonInteractive()) {
307
+ output.error('No prompt provided. Pass it as an argument: bankr agent "your prompt"');
308
+ process.exit(1);
309
+ }
281
310
  agentCmd.help();
282
311
  return;
283
312
  }
284
- await promptCommand(joined || (await readPromptInput()), opts);
313
+ await promptCommand(joined, opts);
285
314
  });
286
315
  agentCmd
287
316
  .command("prompt [text...]")
@@ -308,16 +337,21 @@ agentCmd
308
337
  .command("skills")
309
338
  .description("Show all Bankr AI agent skills with examples")
310
339
  .action(skillsCommand);
340
+ program.commandsGroup("Tokens");
311
341
  program
312
342
  .command("launch")
313
343
  .description("Launch a token on Base (interactive wizard)")
344
+ .optionsGroup("Token")
314
345
  .option("--name <name>", "Token name")
315
346
  .option("--symbol <symbol>", "Token symbol (default: auto-generated from name)")
316
347
  .option("--image <url>", "Token image URL")
348
+ .optionsGroup("Links")
317
349
  .option("--tweet <url>", "Associated tweet URL")
318
350
  .option("--website <url>", "Project website URL")
351
+ .optionsGroup("Fees")
319
352
  .option("--fee <recipient>", "Fee recipient (username, ENS, or address)")
320
353
  .option("--fee-type <type>", "Fee recipient type: x, farcaster, ens, wallet (default: x)")
354
+ .optionsGroup("")
321
355
  .option("-y, --yes", "Skip confirmation prompt")
322
356
  .option("--simulate", "Simulate the deploy without broadcasting a transaction")
323
357
  .action(async (opts) => {
@@ -333,6 +367,7 @@ program
333
367
  simulate: opts.simulate,
334
368
  });
335
369
  });
370
+ program.commandsGroup("Tokens");
336
371
  const feesCmd = program
337
372
  .command("fees [address]")
338
373
  .description("View and claim creator token fee earnings")
@@ -416,6 +451,7 @@ profileCmd
416
451
  json: opts.json,
417
452
  });
418
453
  });
454
+ program.commandsGroup("LLM");
419
455
  const llmCmd = program
420
456
  .command("llm")
421
457
  .description("LLM Gateway tools")
@@ -487,6 +523,7 @@ llmCmd
487
523
  await opencodeCommand(args);
488
524
  });
489
525
  // Token search and info
526
+ program.commandsGroup("Tokens");
490
527
  const tokensCmd = program
491
528
  .command("tokens")
492
529
  .description("Token search and info")
@@ -507,62 +544,7 @@ tokensCmd
507
544
  .action(async (address, opts) => {
508
545
  await tokensInfoCommand(address, opts);
509
546
  });
510
- // ── Utility commands ────────────────────────────────────────────────────
511
- const configCmd = program
512
- .command("config")
513
- .description("Manage CLI configuration");
514
- configCmd
515
- .command("get [key]")
516
- .description("Get config value(s). Keys: apiKey, apiUrl, llmKey, llmUrl")
517
- .action(configGetCommand);
518
- configCmd
519
- .command("set <key> <value>")
520
- .description("Set a config value. Keys: apiKey, apiUrl, llmKey, llmUrl")
521
- .action(configSetCommand);
522
- const soundsCmd = program
523
- .command("sounds")
524
- .description("Manage CESP sound packs")
525
- .action(soundsStatusCommand);
526
- soundsCmd
527
- .command("enable")
528
- .description("Enable sounds")
529
- .action(soundsEnableCommand);
530
- soundsCmd
531
- .command("disable")
532
- .description("Disable sounds")
533
- .action(soundsDisableCommand);
534
- soundsCmd
535
- .command("install <pack>")
536
- .description("Install a sound pack from the CESP registry")
537
- .action(soundsInstallCommand);
538
- soundsCmd
539
- .command("search [query]")
540
- .description("Search available packs in the CESP registry")
541
- .action(soundsSearchCommand);
542
- soundsCmd
543
- .command("list")
544
- .description("List installed sound packs")
545
- .action(soundsListCommand);
546
- soundsCmd
547
- .command("use <pack>")
548
- .description("Set active sound pack")
549
- .action(soundsUseCommand);
550
- soundsCmd
551
- .command("volume [level]")
552
- .description("Get or set volume (0.0-1.0)")
553
- .action(soundsVolumeCommand);
554
- soundsCmd
555
- .command("mute")
556
- .description("Mute all sounds")
557
- .action(soundsMuteCommand);
558
- soundsCmd
559
- .command("unmute")
560
- .description("Unmute sounds")
561
- .action(soundsUnmuteCommand);
562
- soundsCmd
563
- .command("test [category]")
564
- .description("Play a test sound")
565
- .action(soundsTestCommand);
547
+ program.commandsGroup("Account");
566
548
  program
567
549
  .command("logout")
568
550
  .description("Clear stored credentials")
@@ -736,14 +718,8 @@ submitAliasCmd
736
718
  description: opts.description,
737
719
  });
738
720
  });
739
- program
740
- .command("update")
741
- .description("Update the Bankr CLI to the latest version")
742
- .option("--check", "Check for updates without installing")
743
- .action(async (opts) => {
744
- await updateCommand({ check: opts.check });
745
- });
746
721
  // ── File Storage ────────────────────────────────────────────────────────
722
+ program.commandsGroup("Protocol");
747
723
  const filesCmd = program
748
724
  .command("files")
749
725
  .description("Manage your file storage")
@@ -814,6 +790,7 @@ filesCmd
814
790
  .description("Show file metadata")
815
791
  .action(filesInfoCommand);
816
792
  // ── Bankr Club ────────────────────────────────────────────────────────
793
+ program.commandsGroup("Account");
817
794
  const clubCmd = program
818
795
  .command("club")
819
796
  .description("View Bankr Club membership, sign up, or cancel")
@@ -824,8 +801,9 @@ clubCmd
824
801
  .action(clubStatusCommand);
825
802
  clubCmd
826
803
  .command("signup")
827
- .description("Sign up for Bankr Club (pays in $BNKR from your wallet)")
804
+ .description("Sign up for Bankr Club (default: USDC; --token BNKR | ETH | <0x...>)")
828
805
  .option("--yearly", "Subscribe yearly instead of monthly")
806
+ .option("--token <symbolOrAddress>", "Payment token: USDC (default), BNKR, ETH, or a 0x... Base address")
829
807
  .option("-y, --yes", "Skip confirmation prompt")
830
808
  .action(clubSignupCommand);
831
809
  clubCmd
@@ -834,6 +812,7 @@ clubCmd
834
812
  .option("-y, --yes", "Skip confirmation prompt")
835
813
  .action(clubCancelCommand);
836
814
  // ── x402 Endpoint Hosting ─────────────────────────────────────────────
815
+ program.commandsGroup("Protocol");
837
816
  const x402Cmd = program
838
817
  .command("x402")
839
818
  .description("Deploy and manage x402 paid API endpoints");
@@ -889,6 +868,7 @@ x402EnvCmd
889
868
  .description("Remove an env var")
890
869
  .action(x402EnvUnsetCommand);
891
870
  // ── User Webhooks ─────────────────────────────────────────────────────
871
+ program.commandsGroup("Protocol");
892
872
  const webhooksCmd = program
893
873
  .command("webhooks")
894
874
  .description("Deploy and manage user webhooks that trigger the Bankr agent on external events");
@@ -960,6 +940,70 @@ x402Cmd
960
940
  .option("-y, --yes", "Skip payment confirmation")
961
941
  .option("--raw", "Output raw JSON (unformatted)")
962
942
  .action(async (url, opts) => x402CallCommand(url, opts));
943
+ // ── Settings ────────────────────────────────────────────────────────────
944
+ program.commandsGroup("Settings");
945
+ const configCmd = program
946
+ .command("config")
947
+ .description("Manage CLI configuration");
948
+ configCmd
949
+ .command("get [key]")
950
+ .description("Get config value(s). Keys: apiKey, apiUrl, llmKey, llmUrl")
951
+ .action(configGetCommand);
952
+ configCmd
953
+ .command("set <key> <value>")
954
+ .description("Set a config value. Keys: apiKey, apiUrl, llmKey, llmUrl")
955
+ .action(configSetCommand);
956
+ const soundsCmd = program
957
+ .command("sounds")
958
+ .description("Manage CESP sound packs")
959
+ .action(soundsStatusCommand);
960
+ soundsCmd
961
+ .command("enable")
962
+ .description("Enable sounds")
963
+ .action(soundsEnableCommand);
964
+ soundsCmd
965
+ .command("disable")
966
+ .description("Disable sounds")
967
+ .action(soundsDisableCommand);
968
+ soundsCmd
969
+ .command("install <pack>")
970
+ .description("Install a sound pack from the CESP registry")
971
+ .action(soundsInstallCommand);
972
+ soundsCmd
973
+ .command("search [query]")
974
+ .description("Search available packs in the CESP registry")
975
+ .action(soundsSearchCommand);
976
+ soundsCmd
977
+ .command("list")
978
+ .description("List installed sound packs")
979
+ .action(soundsListCommand);
980
+ soundsCmd
981
+ .command("use <pack>")
982
+ .description("Set active sound pack")
983
+ .action(soundsUseCommand);
984
+ soundsCmd
985
+ .command("volume [level]")
986
+ .description("Get or set volume (0.0-1.0)")
987
+ .action(soundsVolumeCommand);
988
+ soundsCmd
989
+ .command("mute")
990
+ .description("Mute all sounds")
991
+ .action(soundsMuteCommand);
992
+ soundsCmd
993
+ .command("unmute")
994
+ .description("Unmute sounds")
995
+ .action(soundsUnmuteCommand);
996
+ soundsCmd
997
+ .command("test [category]")
998
+ .description("Play a test sound")
999
+ .action(soundsTestCommand);
1000
+ program
1001
+ .command("update")
1002
+ .description("Update the Bankr CLI to the latest version")
1003
+ .option("--check", "Check for updates without installing")
1004
+ .action(async (opts) => {
1005
+ await updateCommand({ check: opts.check });
1006
+ });
963
1007
  // Default: treat unrecognized arguments as a prompt
964
1008
  program
965
1009
  .arguments("[text...]")
@@ -968,6 +1012,10 @@ program
968
1012
  .option("-m, --model <model>", "Use Max Mode with a specific model (e.g. claude-opus-4.6)")
969
1013
  .action(async (text, opts) => {
970
1014
  if (text.length === 0) {
1015
+ if (output.isNonInteractive()) {
1016
+ output.error('No prompt provided. Pass it as an argument: bankr "your prompt"');
1017
+ process.exit(1);
1018
+ }
971
1019
  program.help();
972
1020
  return;
973
1021
  }
@@ -2,6 +2,7 @@ export declare function clubStatusCommand(): Promise<void>;
2
2
  export declare function clubSignupCommand(opts: {
3
3
  yearly?: boolean;
4
4
  yes?: boolean;
5
+ token?: string;
5
6
  }): Promise<void>;
6
7
  export declare function clubCancelCommand(opts: {
7
8
  yes?: boolean;
@@ -8,6 +8,33 @@ function authHeaders() {
8
8
  "User-Agent": CLI_USER_AGENT,
9
9
  };
10
10
  }
11
+ // Common Base tokens accepted as `--token` shortcuts. Lowercase to bypass
12
+ // viem's strict EIP-55 checksum on the API's isAddress() check; the route
13
+ // resolves to lowercase server-side anyway. Inlined here (not imported
14
+ // from the monorepo) because @bankr/cli is published standalone.
15
+ const TOKEN_ALIASES = {
16
+ USDC: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
17
+ BNKR: "0x22af33fe49fd1fa80c7149773dde5890d3c76f3b",
18
+ // Native-asset sentinel address used by 0x and most aggregators.
19
+ ETH: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
20
+ };
21
+ const ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
22
+ /**
23
+ * Resolve a user-supplied `--token` value to a Base ERC-20 address.
24
+ * Accepts a known symbol (USDC | BNKR | ETH, case-insensitive) or any
25
+ * 0x-prefixed 40-char address. Returns null for invalid input.
26
+ */
27
+ function resolvePaymentToken(input) {
28
+ if (!input)
29
+ return null;
30
+ const normalized = input.trim();
31
+ const upper = normalized.toUpperCase();
32
+ if (TOKEN_ALIASES[upper])
33
+ return TOKEN_ALIASES[upper];
34
+ if (ADDRESS_RE.test(normalized))
35
+ return normalized;
36
+ return null;
37
+ }
11
38
  async function fetchStatus() {
12
39
  const res = await fetch(`${getApiUrl()}/bankr-club/`, {
13
40
  headers: authHeaders(),
@@ -63,8 +90,20 @@ export async function clubSignupCommand(opts) {
63
90
  output.warn("You already have an active Bankr Club subscription.");
64
91
  return;
65
92
  }
93
+ let tokenAddress;
94
+ if (opts.token) {
95
+ const resolved = resolvePaymentToken(opts.token);
96
+ if (!resolved) {
97
+ output.error(`Unknown token "${opts.token}". Use one of USDC, BNKR, ETH, or pass a 0x-prefixed Base address.`);
98
+ process.exit(1);
99
+ }
100
+ tokenAddress = resolved;
101
+ }
66
102
  const yearly = !!opts.yearly;
67
- const quoteRes = await fetch(`${getApiUrl()}/bankr-club/quote?yearly=${yearly}`, { headers: authHeaders() });
103
+ // Reused for /quote and /sign-up so both endpoints agree on the token.
104
+ // The API rejects sign-up with `quote-token-mismatch` if they diverge.
105
+ const tokenParam = tokenAddress ? `&token=${tokenAddress}` : "";
106
+ const quoteRes = await fetch(`${getApiUrl()}/bankr-club/quote?yearly=${yearly}${tokenParam}`, { headers: authHeaders() });
68
107
  if (!quoteRes.ok) {
69
108
  const body = await quoteRes
70
109
  .json()
@@ -72,13 +111,15 @@ export async function clubSignupCommand(opts) {
72
111
  throw new Error(body.error || quoteRes.statusText);
73
112
  }
74
113
  const quote = (await quoteRes.json());
114
+ const symbol = quote.tokenSymbol ?? "USDC";
75
115
  output.label("Plan", yearly ? "Yearly" : "Monthly");
76
- output.label("Price", `${output.formatUsd(quote.usdPrice)} (in $BNKR)`);
77
- output.label("Cost", `${quote.priceInBankr} BNKR`);
116
+ output.label("Price", `${output.formatUsd(quote.usdPrice)} (in ${symbol})`);
117
+ output.label("Cost", `${quote.priceInToken} ${symbol}`);
78
118
  output.dim(`Quote expires ${new Date(quote.expiresAt * 1000).toLocaleString()}.`);
79
119
  output.blank();
80
- output.dim("Payment is deducted from the $BNKR balance in your Bankr wallet.");
120
+ output.dim(`Payment is deducted from your ${symbol} balance in your Bankr wallet.`);
81
121
  const ok = opts.yes ||
122
+ output.isNonInteractive() ||
82
123
  (await confirm({
83
124
  message: "Confirm subscription?",
84
125
  default: false,
@@ -89,7 +130,7 @@ export async function clubSignupCommand(opts) {
89
130
  return;
90
131
  }
91
132
  const spinner = output.spinner("Signing up for Bankr Club...");
92
- const signUpRes = await fetch(`${getApiUrl()}/bankr-club/sign-up?yearly=${yearly}`, { headers: authHeaders() });
133
+ const signUpRes = await fetch(`${getApiUrl()}/bankr-club/sign-up?yearly=${yearly}${tokenParam}`, { headers: authHeaders() });
93
134
  spinner.stop();
94
135
  if (!signUpRes.ok) {
95
136
  const body = await signUpRes
@@ -121,6 +162,7 @@ export async function clubCancelCommand(opts) {
121
162
  return;
122
163
  }
123
164
  const ok = opts.yes ||
165
+ output.isNonInteractive() ||
124
166
  (await confirm({
125
167
  message: "Cancel Bankr Club? You'll keep access until the current billing period ends.",
126
168
  default: false,
@@ -1,7 +1,7 @@
1
1
  import chalk from "chalk";
2
2
  import { confirm, password, checkbox } from "@inquirer/prompts";
3
3
  import { isAddress } from "viem";
4
- import { getCreatorFees, getBeneficiaryFeesStreaming, getTokenCreatorFees, getUserInfo, buildDopplerClaimTx, submit, } from "../lib/api.js";
4
+ import { getCreatorFees, getBeneficiaryFeesStreaming, getTokenCreatorFees, getUserInfo, claimTokenLaunchFees, } from "../lib/api.js";
5
5
  import { emitCESP } from "../lib/cesp/engine.js";
6
6
  import { requireApiKey } from "../lib/config.js";
7
7
  import * as output from "../lib/output.js";
@@ -385,28 +385,14 @@ export async function feesCommand(address, opts) {
385
385
  }
386
386
  export async function feesClaimCommand(tokenAddress, opts) {
387
387
  requireApiKey();
388
- // Step 1: Build claim transaction
389
- const buildSpin = output.spinner("Building claim transaction...");
390
- let claimTx;
391
- try {
392
- claimTx = await buildDopplerClaimTx(tokenAddress);
393
- buildSpin.succeed(claimTx.description);
394
- }
395
- catch (err) {
396
- buildSpin.fail("Failed to build claim transaction");
397
- emitCESP("task.error");
398
- output.error(err.message);
399
- process.exit(1);
400
- }
401
- // Step 2: Confirm
402
- if (!opts.yes) {
388
+ // Confirm — we send one request that builds, signs, and broadcasts.
389
+ if (!opts.yes && !output.isNonInteractive()) {
403
390
  console.log();
404
- output.keyValue("To", claimTx.to);
405
- output.keyValue("Chain ID", String(claimTx.chainId));
406
- output.keyValue("Action", claimTx.description);
391
+ output.keyValue("Token", tokenAddress);
392
+ output.keyValue("Action", "Claim trading fees");
407
393
  console.log();
408
394
  const proceed = await confirm({
409
- message: "Submit this claim transaction?",
395
+ message: "Submit claim transaction?",
410
396
  default: true,
411
397
  theme: output.bankrTheme,
412
398
  });
@@ -415,43 +401,43 @@ export async function feesClaimCommand(tokenAddress, opts) {
415
401
  return;
416
402
  }
417
403
  }
418
- // Step 3: Submit via /agent/submit
419
- const submitSpin = output.spinner("Submitting claim transaction...");
404
+ const spin = output.spinner("Claiming fees...");
420
405
  const startTime = Date.now();
421
406
  try {
422
- const result = await submit({
423
- transaction: {
424
- to: claimTx.to,
425
- chainId: claimTx.chainId,
426
- data: claimTx.data,
427
- },
428
- description: claimTx.description,
429
- waitForConfirmation: true,
430
- });
407
+ const result = await claimTokenLaunchFees(tokenAddress);
431
408
  const elapsed = output.formatDuration(Date.now() - startTime);
432
- if (result.success) {
433
- submitSpin.succeed(`Fees claimed in ${elapsed}`);
409
+ const ok = result.status === "success";
410
+ if (ok) {
411
+ spin.succeed(`Fees claimed in ${elapsed}`);
434
412
  emitCESP("task.complete");
435
- console.log();
436
- if (result.transactionHash) {
437
- output.keyValue("Transaction", result.transactionHash);
438
- }
439
- if (result.status) {
440
- output.keyValue("Status", output.formatStatus(result.status === "success" ? "completed" : result.status));
441
- }
442
- output.keyValue("Signer", result.signer);
443
- console.log();
444
- output.dim(`View on Basescan: https://basescan.org/tx/${result.transactionHash}`);
445
413
  }
446
414
  else {
447
- submitSpin.fail(`Claim failed after ${elapsed}`);
415
+ spin.fail(`Claim failed after ${elapsed}`);
448
416
  emitCESP("task.error");
449
- output.error(result.error || "Transaction failed");
417
+ }
418
+ console.log();
419
+ if (result.description) {
420
+ output.keyValue("Action", result.description);
421
+ }
422
+ if (result.transactionHash) {
423
+ output.keyValue("Transaction", result.transactionHash);
424
+ }
425
+ if (result.status) {
426
+ output.keyValue("Status", output.formatStatus(result.status === "success" ? "completed" : result.status));
427
+ }
428
+ if (result.signer) {
429
+ output.keyValue("Signer", result.signer);
430
+ }
431
+ console.log();
432
+ if (result.transactionHash) {
433
+ output.dim(`View on Basescan: https://basescan.org/tx/${result.transactionHash}`);
434
+ }
435
+ if (!ok) {
450
436
  process.exit(1);
451
437
  }
452
438
  }
453
439
  catch (err) {
454
- submitSpin.fail("Claim submission failed");
440
+ spin.fail("Claim submission failed");
455
441
  emitCESP("task.error");
456
442
  output.error(err.message);
457
443
  process.exit(1);
@@ -466,6 +452,10 @@ export async function feesClaimWalletCommand(address, opts) {
466
452
  if (address && !isAddress(address)) {
467
453
  address = undefined;
468
454
  }
455
+ if (output.isNonInteractive() && !opts.all) {
456
+ output.error("Use --all to select all tokens in non-interactive mode, or run interactively to choose");
457
+ process.exit(1);
458
+ }
469
459
  // Load .env from cwd if present (no dependency needed)
470
460
  try {
471
461
  const { readFileSync, existsSync } = await import("node:fs");
@@ -506,6 +496,10 @@ export async function feesClaimWalletCommand(address, opts) {
506
496
  process.exit(1);
507
497
  }
508
498
  else {
499
+ if (output.isNonInteractive()) {
500
+ output.error("Private key required in non-interactive mode. Use --private-key or set BANKR_PRIVATE_KEY");
501
+ process.exit(1);
502
+ }
509
503
  privateKey = await password({
510
504
  message: "Enter private key for this wallet (0x...):",
511
505
  mask: "*",
@@ -694,7 +688,7 @@ export async function feesClaimWalletCommand(address, opts) {
694
688
  console.log(` ${DIM("To:")} ${tx.to} ${DIM("Gas:")} ~${gasCostEth.toFixed(6)} ETH`);
695
689
  console.log();
696
690
  }
697
- if (!opts.yes) {
691
+ if (!opts.yes && !output.isNonInteractive()) {
698
692
  const proceed = await confirm({
699
693
  message: `Submit ${readyTxs.length} claim transaction${readyTxs.length !== 1 ? "s" : ""}?`,
700
694
  default: true,