@bankr/cli 0.3.1 → 0.3.11

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
@@ -109,19 +109,19 @@ Launch Claude Code / OpenCode routed through the Bankr gateway:
109
109
 
110
110
  ```bash
111
111
  # Claude Code (auto-translates dotted model IDs to the Anthropic wire format)
112
- bankr llm claude --model claude-opus-4-7
112
+ bankr llm claude --model claude-opus-4-8
113
113
 
114
- # Or use dotted form — bankr llm claude rewrites it to claude-opus-4-7
115
- bankr llm claude --model claude-opus-4.7
114
+ # Or use dotted form — bankr llm claude rewrites it to claude-opus-4-8
115
+ bankr llm claude --model claude-opus-4.8
116
116
 
117
117
  # OpenCode (use the bankr/ prefix with the gateway-canonical dotted form)
118
- bankr llm opencode -m bankr/claude-opus-4.7
118
+ bankr llm opencode -m bankr/claude-opus-4.8
119
119
  ```
120
120
 
121
121
  **Claude Code model format**: Claude Code's `--model` flag expects
122
- Anthropic-style dashed IDs (`claude-opus-4-7`, `claude-sonnet-4-6`,
122
+ Anthropic-style dashed IDs (`claude-opus-4-8`, `claude-sonnet-4-6`,
123
123
  `claude-haiku-4-5`). If you pass the gateway-canonical dotted form
124
- (`claude-opus-4.7`), Claude Code silently falls back to its default —
124
+ (`claude-opus-4.8`), Claude Code silently falls back to its default —
125
125
  the flag appears honored but the real request is a different model.
126
126
 
127
127
  `bankr llm claude` translates dotted → dashed for you. If you use older
@@ -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,}.+)$/);
@@ -86,19 +82,27 @@ program
86
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")
@@ -383,6 +418,7 @@ profileCmd
383
418
  profileCmd
384
419
  .command("update")
385
420
  .description("Update your agent profile")
421
+ .option("--slug <slug>", "Project slug (required if multiple profiles)")
386
422
  .option("--name <name>", "Project name")
387
423
  .option("--description <text>", "Project description")
388
424
  .option("--token <address>", "Token contract address")
@@ -391,6 +427,7 @@ profileCmd
391
427
  .option("--json", "Output raw JSON")
392
428
  .action(async (opts) => {
393
429
  await profileUpdateCommand({
430
+ slug: opts.slug,
394
431
  name: opts.name,
395
432
  description: opts.description,
396
433
  token: opts.token,
@@ -402,20 +439,26 @@ profileCmd
402
439
  profileCmd
403
440
  .command("delete")
404
441
  .description("Delete your agent profile")
405
- .action(profileDeleteCommand);
442
+ .option("--slug <slug>", "Project slug (required if multiple profiles)")
443
+ .action(async (opts) => {
444
+ await profileDeleteCommand({ slug: opts.slug });
445
+ });
406
446
  profileCmd
407
447
  .command("add-update")
408
448
  .description("Add a project update to your profile")
449
+ .option("--slug <slug>", "Project slug (required if multiple profiles)")
409
450
  .option("--title <title>", "Update title")
410
451
  .option("--content <text>", "Update content")
411
452
  .option("--json", "Output raw JSON")
412
453
  .action(async (opts) => {
413
454
  await profileAddUpdateCommand({
455
+ slug: opts.slug,
414
456
  title: opts.title,
415
457
  content: opts.content,
416
458
  json: opts.json,
417
459
  });
418
460
  });
461
+ program.commandsGroup("LLM");
419
462
  const llmCmd = program
420
463
  .command("llm")
421
464
  .description("LLM Gateway tools")
@@ -425,6 +468,7 @@ const llmCmd = program
425
468
  llmCmd
426
469
  .command("models")
427
470
  .description("List available models")
471
+ .option("--private", "Show only models that support private (:private) inference")
428
472
  .action(modelsCommand);
429
473
  const llmCreditsCmd = llmCmd
430
474
  .command("credits")
@@ -487,6 +531,7 @@ llmCmd
487
531
  await opencodeCommand(args);
488
532
  });
489
533
  // Token search and info
534
+ program.commandsGroup("Tokens");
490
535
  const tokensCmd = program
491
536
  .command("tokens")
492
537
  .description("Token search and info")
@@ -507,62 +552,7 @@ tokensCmd
507
552
  .action(async (address, opts) => {
508
553
  await tokensInfoCommand(address, opts);
509
554
  });
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);
555
+ program.commandsGroup("Account");
566
556
  program
567
557
  .command("logout")
568
558
  .description("Clear stored credentials")
@@ -641,6 +631,7 @@ profileAliasCmd
641
631
  });
642
632
  profileAliasCmd
643
633
  .command("update")
634
+ .option("--slug <slug>", "Project slug")
644
635
  .option("--name <name>", "Project name")
645
636
  .option("--description <text>", "Project description")
646
637
  .option("--token <address>", "Token contract address")
@@ -650,6 +641,7 @@ profileAliasCmd
650
641
  .action(async (opts) => {
651
642
  deprecatedCmd("profile update", "agent profile update");
652
643
  await profileUpdateCommand({
644
+ slug: opts.slug,
653
645
  name: opts.name,
654
646
  description: opts.description,
655
647
  token: opts.token,
@@ -658,18 +650,23 @@ profileAliasCmd
658
650
  json: opts.json,
659
651
  });
660
652
  });
661
- profileAliasCmd.command("delete").action(async () => {
653
+ profileAliasCmd
654
+ .command("delete")
655
+ .option("--slug <slug>", "Project slug")
656
+ .action(async (opts) => {
662
657
  deprecatedCmd("profile delete", "agent profile delete");
663
- await profileDeleteCommand();
658
+ await profileDeleteCommand({ slug: opts.slug });
664
659
  });
665
660
  profileAliasCmd
666
661
  .command("add-update")
662
+ .option("--slug <slug>", "Project slug")
667
663
  .option("--title <title>", "Update title")
668
664
  .option("--content <text>", "Update content")
669
665
  .option("--json", "Output raw JSON")
670
666
  .action(async (opts) => {
671
667
  deprecatedCmd("profile add-update", "agent profile add-update");
672
668
  await profileAddUpdateCommand({
669
+ slug: opts.slug,
673
670
  title: opts.title,
674
671
  content: opts.content,
675
672
  json: opts.json,
@@ -736,14 +733,8 @@ submitAliasCmd
736
733
  description: opts.description,
737
734
  });
738
735
  });
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
736
  // ── File Storage ────────────────────────────────────────────────────────
737
+ program.commandsGroup("Protocol");
747
738
  const filesCmd = program
748
739
  .command("files")
749
740
  .description("Manage your file storage")
@@ -814,6 +805,7 @@ filesCmd
814
805
  .description("Show file metadata")
815
806
  .action(filesInfoCommand);
816
807
  // ── Bankr Club ────────────────────────────────────────────────────────
808
+ program.commandsGroup("Account");
817
809
  const clubCmd = program
818
810
  .command("club")
819
811
  .description("View Bankr Club membership, sign up, or cancel")
@@ -824,8 +816,9 @@ clubCmd
824
816
  .action(clubStatusCommand);
825
817
  clubCmd
826
818
  .command("signup")
827
- .description("Sign up for Bankr Club (pays in $BNKR from your wallet)")
819
+ .description("Sign up for Bankr Club (default: USDC; --token BNKR | ETH | <0x...>)")
828
820
  .option("--yearly", "Subscribe yearly instead of monthly")
821
+ .option("--token <symbolOrAddress>", "Payment token: USDC (default), BNKR, ETH, or a 0x... Base address")
829
822
  .option("-y, --yes", "Skip confirmation prompt")
830
823
  .action(clubSignupCommand);
831
824
  clubCmd
@@ -834,6 +827,7 @@ clubCmd
834
827
  .option("-y, --yes", "Skip confirmation prompt")
835
828
  .action(clubCancelCommand);
836
829
  // ── x402 Endpoint Hosting ─────────────────────────────────────────────
830
+ program.commandsGroup("Protocol");
837
831
  const x402Cmd = program
838
832
  .command("x402")
839
833
  .description("Deploy and manage x402 paid API endpoints");
@@ -889,6 +883,7 @@ x402EnvCmd
889
883
  .description("Remove an env var")
890
884
  .action(x402EnvUnsetCommand);
891
885
  // ── User Webhooks ─────────────────────────────────────────────────────
886
+ program.commandsGroup("Protocol");
892
887
  const webhooksCmd = program
893
888
  .command("webhooks")
894
889
  .description("Deploy and manage user webhooks that trigger the Bankr agent on external events");
@@ -960,6 +955,70 @@ x402Cmd
960
955
  .option("-y, --yes", "Skip payment confirmation")
961
956
  .option("--raw", "Output raw JSON (unformatted)")
962
957
  .action(async (url, opts) => x402CallCommand(url, opts));
958
+ // ── Settings ────────────────────────────────────────────────────────────
959
+ program.commandsGroup("Settings");
960
+ const configCmd = program
961
+ .command("config")
962
+ .description("Manage CLI configuration");
963
+ configCmd
964
+ .command("get [key]")
965
+ .description("Get config value(s). Keys: apiKey, apiUrl, llmKey, llmUrl")
966
+ .action(configGetCommand);
967
+ configCmd
968
+ .command("set <key> <value>")
969
+ .description("Set a config value. Keys: apiKey, apiUrl, llmKey, llmUrl")
970
+ .action(configSetCommand);
971
+ const soundsCmd = program
972
+ .command("sounds")
973
+ .description("Manage CESP sound packs")
974
+ .action(soundsStatusCommand);
975
+ soundsCmd
976
+ .command("enable")
977
+ .description("Enable sounds")
978
+ .action(soundsEnableCommand);
979
+ soundsCmd
980
+ .command("disable")
981
+ .description("Disable sounds")
982
+ .action(soundsDisableCommand);
983
+ soundsCmd
984
+ .command("install <pack>")
985
+ .description("Install a sound pack from the CESP registry")
986
+ .action(soundsInstallCommand);
987
+ soundsCmd
988
+ .command("search [query]")
989
+ .description("Search available packs in the CESP registry")
990
+ .action(soundsSearchCommand);
991
+ soundsCmd
992
+ .command("list")
993
+ .description("List installed sound packs")
994
+ .action(soundsListCommand);
995
+ soundsCmd
996
+ .command("use <pack>")
997
+ .description("Set active sound pack")
998
+ .action(soundsUseCommand);
999
+ soundsCmd
1000
+ .command("volume [level]")
1001
+ .description("Get or set volume (0.0-1.0)")
1002
+ .action(soundsVolumeCommand);
1003
+ soundsCmd
1004
+ .command("mute")
1005
+ .description("Mute all sounds")
1006
+ .action(soundsMuteCommand);
1007
+ soundsCmd
1008
+ .command("unmute")
1009
+ .description("Unmute sounds")
1010
+ .action(soundsUnmuteCommand);
1011
+ soundsCmd
1012
+ .command("test [category]")
1013
+ .description("Play a test sound")
1014
+ .action(soundsTestCommand);
1015
+ program
1016
+ .command("update")
1017
+ .description("Update the Bankr CLI to the latest version")
1018
+ .option("--check", "Check for updates without installing")
1019
+ .action(async (opts) => {
1020
+ await updateCommand({ check: opts.check });
1021
+ });
963
1022
  // Default: treat unrecognized arguments as a prompt
964
1023
  program
965
1024
  .arguments("[text...]")
@@ -968,6 +1027,10 @@ program
968
1027
  .option("-m, --model <model>", "Use Max Mode with a specific model (e.g. claude-opus-4.6)")
969
1028
  .action(async (text, opts) => {
970
1029
  if (text.length === 0) {
1030
+ if (output.isNonInteractive()) {
1031
+ output.error('No prompt provided. Pass it as an argument: bankr "your prompt"');
1032
+ process.exit(1);
1033
+ }
971
1034
  program.help();
972
1035
  return;
973
1036
  }
@@ -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,
@@ -386,7 +386,7 @@ export async function feesCommand(address, opts) {
386
386
  export async function feesClaimCommand(tokenAddress, opts) {
387
387
  requireApiKey();
388
388
  // Confirm — we send one request that builds, signs, and broadcasts.
389
- if (!opts.yes) {
389
+ if (!opts.yes && !output.isNonInteractive()) {
390
390
  console.log();
391
391
  output.keyValue("Token", tokenAddress);
392
392
  output.keyValue("Action", "Claim trading fees");
@@ -452,6 +452,10 @@ export async function feesClaimWalletCommand(address, opts) {
452
452
  if (address && !isAddress(address)) {
453
453
  address = undefined;
454
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
+ }
455
459
  // Load .env from cwd if present (no dependency needed)
456
460
  try {
457
461
  const { readFileSync, existsSync } = await import("node:fs");
@@ -492,6 +496,10 @@ export async function feesClaimWalletCommand(address, opts) {
492
496
  process.exit(1);
493
497
  }
494
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
+ }
495
503
  privateKey = await password({
496
504
  message: "Enter private key for this wallet (0x...):",
497
505
  mask: "*",
@@ -680,7 +688,7 @@ export async function feesClaimWalletCommand(address, opts) {
680
688
  console.log(` ${DIM("To:")} ${tx.to} ${DIM("Gas:")} ~${gasCostEth.toFixed(6)} ETH`);
681
689
  console.log();
682
690
  }
683
- if (!opts.yes) {
691
+ if (!opts.yes && !output.isNonInteractive()) {
684
692
  const proceed = await confirm({
685
693
  message: `Submit ${readyTxs.length} claim transaction${readyTxs.length !== 1 ? "s" : ""}?`,
686
694
  default: true,