@bankr/cli 0.3.1 → 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,}.+)$/);
@@ -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")
@@ -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,
@@ -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,
@@ -25,7 +25,16 @@ function feeTypeLabel(type) {
25
25
  return "Wallet";
26
26
  }
27
27
  }
28
+ async function promptOrSkip(message) {
29
+ if (output.isNonInteractive())
30
+ return "";
31
+ return input({ message, theme: output.bankrTheme });
32
+ }
28
33
  async function collectInteractive(options) {
34
+ if (output.isNonInteractive() && !options.name) {
35
+ output.error("--name is required in non-interactive mode");
36
+ process.exit(1);
37
+ }
29
38
  const tokenName = options.name ??
30
39
  (await input({
31
40
  message: "What do you want to call your token?",
@@ -33,32 +42,17 @@ async function collectInteractive(options) {
33
42
  required: true,
34
43
  }));
35
44
  const tokenSymbol = options.symbol ??
36
- (await input({
37
- message: "Token symbol (press Enter to auto-generate from name):",
38
- theme: output.bankrTheme,
39
- }));
45
+ (await promptOrSkip("Token symbol (press Enter to auto-generate from name):"));
40
46
  const imageUrl = options.image ??
41
- (await input({
42
- message: "Image URL for your token (press Enter to skip):",
43
- theme: output.bankrTheme,
44
- }));
47
+ (await promptOrSkip("Image URL for your token (press Enter to skip):"));
45
48
  const tweetUrl = options.tweet ??
46
- (await input({
47
- message: "Associate with a tweet? Paste URL (press Enter to skip):",
48
- theme: output.bankrTheme,
49
- }));
49
+ (await promptOrSkip("Associate with a tweet? Paste URL (press Enter to skip):"));
50
50
  const websiteUrl = options.website ??
51
- (await input({
52
- message: "Project website URL (press Enter to skip):",
53
- theme: output.bankrTheme,
54
- }));
51
+ (await promptOrSkip("Project website URL (press Enter to skip):"));
55
52
  let feeRecipient = options.fee ??
56
- (await input({
57
- message: "Direct fees to someone? Enter username, ENS, or address (press Enter to skip):",
58
- theme: output.bankrTheme,
59
- }));
53
+ (await promptOrSkip("Direct fees to someone? Enter username, ENS, or address (press Enter to skip):"));
60
54
  let feeRecipientType = parseFeeType(options.feeType);
61
- if (feeRecipient && !options.feeType) {
55
+ if (feeRecipient && !options.feeType && !output.isNonInteractive()) {
62
56
  feeRecipientType = await select({
63
57
  message: "What type of identifier is this?",
64
58
  choices: [
@@ -108,8 +102,7 @@ export async function launchCommand(options) {
108
102
  output.keyValue("Mode", "Simulation (dry run)");
109
103
  }
110
104
  console.log();
111
- // Confirm unless --yes
112
- if (!options.yes) {
105
+ if (!options.yes && !output.isNonInteractive()) {
113
106
  const proceed = await confirm({
114
107
  message: options.simulate
115
108
  ? "Simulate this token launch?"