@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.
@@ -1,28 +1,38 @@
1
- import { transfer, searchTokens } from "../lib/api.js";
1
+ import { transfer, resolveRecipient } from "../lib/api.js";
2
2
  import * as output from "../lib/output.js";
3
- import { NATIVE_SYMBOLS, CHAIN_IDS } from "../lib/chains.js";
3
+ import { NATIVE_SYMBOLS } from "../lib/chains.js";
4
4
  import { isAddress } from "viem";
5
- /**
6
- * Resolve a token symbol or address to a contract address.
7
- * If it's already a valid address, return as-is. Otherwise search by symbol.
8
- */
9
- async function resolveToken(tokenInput, chain) {
10
- if (isAddress(tokenInput)) {
11
- return { address: tokenInput, symbol: tokenInput };
5
+ import { ZERO_ADDRESS, resolveTokenSymbol } from "../lib/tokens.js";
6
+ function looksLikeEnsName(value) {
7
+ const v = value.trim().toLowerCase();
8
+ return v.endsWith(".eth") || v.endsWith(".cb.id");
9
+ }
10
+ async function resolveTransferRecipient(raw) {
11
+ const value = raw.trim();
12
+ if (isAddress(value)) {
13
+ return { address: value, label: value };
14
+ }
15
+ if (!looksLikeEnsName(value)) {
16
+ output.error(`Invalid recipient "${value}". Expected a 0x address or an ENS name (e.g. vitalik.eth, name.base.eth, name.cb.id).`);
17
+ process.exit(1);
12
18
  }
13
- const chainId = CHAIN_IDS[chain];
14
- const res = await searchTokens(tokenInput, chainId);
15
- // Exact symbol match (case-insensitive) to avoid resolving to wrong token
16
- const inputUpper = tokenInput.toUpperCase();
17
- const exactMatch = res.tokens.find((t) => t.symbol.toUpperCase() === inputUpper);
18
- if (!exactMatch) {
19
- const available = res.tokens
20
- .slice(0, 5)
21
- .map((t) => `${t.symbol} (${t.address})`)
22
- .join(", ");
23
- throw new Error(`No exact match for "${tokenInput}" on ${chain}. Did you mean: ${available || "no results"}`);
19
+ const spin = output.spinner(`Resolving ENS name "${value}"...`);
20
+ try {
21
+ const resolved = await resolveRecipient(value, "ens");
22
+ if (!resolved.resolved || !resolved.address) {
23
+ spin.fail(`Could not resolve "${value}": ${resolved.error ?? "unknown error"}`);
24
+ process.exit(1);
25
+ }
26
+ spin.succeed(`Resolved ${value} → ${resolved.address}`);
27
+ return {
28
+ address: resolved.address,
29
+ label: `${value} (${resolved.address})`,
30
+ };
31
+ }
32
+ catch (err) {
33
+ spin.fail(`ENS resolution failed for "${value}": ${err.message}`);
34
+ process.exit(1);
24
35
  }
25
- return { address: exactMatch.address, symbol: exactMatch.symbol };
26
36
  }
27
37
  export async function transferCommand(opts) {
28
38
  const isNative = opts.native ?? false;
@@ -31,16 +41,19 @@ export async function transferCommand(opts) {
31
41
  output.error("Token required for ERC20 transfers. Use --token <symbol or address> or --native.");
32
42
  process.exit(1);
33
43
  }
44
+ const recipient = await resolveTransferRecipient(opts.to);
45
+ const recipientAddress = recipient.address;
46
+ const recipientLabel = recipient.label;
34
47
  let tokenAddress;
35
48
  let tokenLabel;
36
49
  if (isNative) {
37
- tokenAddress = "0x0000000000000000000000000000000000000000";
50
+ tokenAddress = ZERO_ADDRESS;
38
51
  tokenLabel = NATIVE_SYMBOLS[chain] ?? "ETH";
39
52
  }
40
53
  else {
41
54
  const spin = output.spinner(`Resolving token "${opts.token}"...`);
42
55
  try {
43
- const resolved = await resolveToken(opts.token, chain);
56
+ const resolved = await resolveTokenSymbol(opts.token, chain);
44
57
  tokenAddress = resolved.address;
45
58
  tokenLabel = resolved.symbol;
46
59
  if (tokenAddress !== opts.token) {
@@ -56,11 +69,11 @@ export async function transferCommand(opts) {
56
69
  }
57
70
  }
58
71
  const chainLabel = chain !== "base" ? ` on ${chain}` : "";
59
- const spin = output.spinner(`Transferring ${opts.amount} ${tokenLabel} to ${opts.to}${chainLabel}...`);
72
+ const spin = output.spinner(`Transferring ${opts.amount} ${tokenLabel} to ${recipientLabel}${chainLabel}...`);
60
73
  try {
61
74
  const result = await transfer({
62
75
  tokenAddress,
63
- recipientAddress: opts.to,
76
+ recipientAddress,
64
77
  amount: opts.amount,
65
78
  isNativeToken: isNative,
66
79
  chain,
@@ -17,7 +17,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
17
17
  import { join } from "node:path";
18
18
  import { confirm } from "@inquirer/prompts";
19
19
  import * as output from "../lib/output.js";
20
- import { getApiUrl, requireApiKey, readConfig, CLI_USER_AGENT, } from "../lib/config.js";
20
+ import { getApiUrl, requireApiKey, CLI_USER_AGENT } from "../lib/config.js";
21
21
  // ── Config file ─────────────────────────────────────────────────────────
22
22
  const CONFIG_FILENAME = "bankr.webhooks.json";
23
23
  const WEBHOOKS_DIR = "webhooks";
@@ -46,16 +46,11 @@ function saveConfigFile(projectRoot, config) {
46
46
  writeFileSync(join(projectRoot, CONFIG_FILENAME), JSON.stringify(config, null, 2) + "\n");
47
47
  }
48
48
  function authHeaders() {
49
- const headers = {
49
+ return {
50
50
  "X-API-Key": requireApiKey(),
51
51
  "Content-Type": "application/json",
52
52
  "User-Agent": CLI_USER_AGENT,
53
53
  };
54
- const config = readConfig();
55
- if (config.partnerKey) {
56
- headers["X-Partner-Key"] = config.partnerKey;
57
- }
58
- return headers;
59
54
  }
60
55
  async function handleResponse(res) {
61
56
  if (!res.ok) {
@@ -20,7 +20,7 @@ import { join } from "node:path";
20
20
  import chalk from "chalk";
21
21
  import { input, select, confirm } from "@inquirer/prompts";
22
22
  import * as output from "../lib/output.js";
23
- import { getApiUrl, requireApiKey, readConfig, CLI_USER_AGENT, } from "../lib/config.js";
23
+ import { getApiUrl, requireApiKey, CLI_USER_AGENT } from "../lib/config.js";
24
24
  // ── Config file helpers ─────────────────────────────────────────────────
25
25
  const CONFIG_FILENAME = "bankr.x402.json";
26
26
  function findProjectRoot() {
@@ -50,16 +50,11 @@ function saveConfigFile(projectRoot, config) {
50
50
  }
51
51
  // ── API helpers ─────────────────────────────────────────────────────────
52
52
  function authHeaders() {
53
- const headers = {
53
+ return {
54
54
  "X-API-Key": requireApiKey(),
55
55
  "Content-Type": "application/json",
56
56
  "User-Agent": CLI_USER_AGENT,
57
57
  };
58
- const config = readConfig();
59
- if (config.partnerKey) {
60
- headers["X-Partner-Key"] = config.partnerKey;
61
- }
62
- return headers;
63
58
  }
64
59
  async function handleResponse(res) {
65
60
  if (!res.ok) {
@@ -279,10 +274,7 @@ export async function x402ConfigureCommand(name) {
279
274
  const currency = "USDC";
280
275
  const network = await select({
281
276
  message: "Network:",
282
- choices: [
283
- { name: "Base", value: "base" },
284
- { name: "Base Sepolia (testnet)", value: "base-sepolia" },
285
- ],
277
+ choices: [{ name: "Base", value: "base" }],
286
278
  default: existing.network ?? "base",
287
279
  theme: output.bankrTheme,
288
280
  });
@@ -697,62 +689,78 @@ function resolveSchemas(schema) {
697
689
  const outputSchema = schema.output;
698
690
  return { input: inputSchema, output: outputSchema };
699
691
  }
700
- function printServiceFormatted(svc) {
692
+ /**
693
+ * Builds the verbose multi-line card for a service. Returns the rendered
694
+ * string. Pass `rank` and `total` to render a `#N of M` badge in the header
695
+ * (used by the search command so reading top-to-bottom shows ascending rank
696
+ * with the top match at the bottom).
697
+ */
698
+ function formatServiceCard(svc, rank, total) {
701
699
  const route = svc.routes[0];
702
700
  const methods = route?.methods?.filter((m) => m !== "*").join(", ") || "ANY";
703
701
  const url = svc.url ?? `https://x402.bankr.bot/${svc.slug}`;
704
702
  const isGet = methods === "GET";
705
- // ── Header ──
706
- console.log(` ${output.fmt.brandBold(svc.name)}`);
707
- if (svc.description) {
708
- console.log(` ${chalk.dim(svc.description)}`);
709
- }
710
- console.log("");
703
+ const lines = [];
704
+ // ── Header (with rank badge) ──
705
+ const rankBadge = rank !== undefined
706
+ ? `${chalk.bold(`#${rank}${total ? ` of ${total}` : ""}`)} `
707
+ : "";
708
+ const scoreBadge = svc.score !== undefined
709
+ ? ` ${chalk.dim(`(score ${svc.score.toFixed(2)})`)}`
710
+ : "";
711
+ lines.push(` ${rankBadge}${output.fmt.brandBold(svc.name)}${scoreBadge}`);
712
+ if (svc.description)
713
+ lines.push(` ${chalk.dim(svc.description)}`);
714
+ lines.push("");
711
715
  // ── Metadata ──
712
- console.log(` ${chalk.dim("URL")} ${url}`);
713
- console.log(` ${chalk.dim("Method")} ${chalk.bold(methods)}`);
714
- console.log(` ${chalk.dim("Price")} ${chalk.green(`$${route?.price ?? "?"} ${route?.currency ?? "USDC"}`)}`);
715
- console.log(` ${chalk.dim("Network")} ${route?.network ?? "base"}`);
716
+ lines.push(` ${chalk.dim("URL")} ${url}`);
717
+ lines.push(` ${chalk.dim("Method")} ${chalk.bold(methods)}`);
718
+ lines.push(` ${chalk.dim("Price")} ${chalk.green(`$${route?.price ?? "?"} ${route?.currency ?? "USDC"}`)}`);
719
+ lines.push(` ${chalk.dim("Network")} ${route?.network ?? "base"}`);
716
720
  // ── Schema ──
717
721
  const { input: inputSchema, output: outputSchema } = resolveSchemas(route?.schema);
718
722
  if (inputSchema || outputSchema) {
719
- console.log("");
723
+ lines.push("");
720
724
  if (inputSchema) {
721
725
  const label = isGet ? "Input (query params)" : "Input (JSON body)";
722
- console.log(` ${chalk.bold(label)}`);
726
+ lines.push(` ${chalk.bold(label)}`);
723
727
  if (isJsonSchema(inputSchema)) {
724
728
  for (const line of renderJsonSchemaProps(inputSchema)) {
725
- console.log(line);
729
+ lines.push(line);
726
730
  }
727
731
  }
728
732
  else if (typeof inputSchema === "object" && inputSchema !== null) {
729
733
  for (const line of renderLegacySchema(inputSchema)) {
730
- console.log(line);
734
+ lines.push(line);
731
735
  }
732
736
  }
733
737
  }
734
738
  if (outputSchema) {
735
- console.log("");
736
- console.log(` ${chalk.bold("Output")}`);
739
+ lines.push("");
740
+ lines.push(` ${chalk.bold("Output")}`);
737
741
  if (isJsonSchema(outputSchema)) {
738
742
  for (const line of renderJsonSchemaProps(outputSchema)) {
739
- console.log(line);
743
+ lines.push(line);
740
744
  }
741
745
  }
742
746
  else if (typeof outputSchema === "object" && outputSchema !== null) {
743
747
  for (const line of renderLegacySchema(outputSchema)) {
744
- console.log(line);
748
+ lines.push(line);
745
749
  }
746
750
  }
747
751
  }
748
752
  }
749
753
  // ── Tags ──
750
754
  if (svc.tags?.length) {
751
- console.log("");
752
- console.log(` ${svc.tags.map((t) => chalk.dim(`#${t}`)).join(" ")}`);
755
+ lines.push("");
756
+ lines.push(` ${svc.tags.map((t) => chalk.dim(`#${t}`)).join(" ")}`);
753
757
  }
754
- console.log(` ${chalk.dim("─".repeat(60))}`);
755
- console.log("");
758
+ lines.push(` ${chalk.dim("─".repeat(60))}`);
759
+ lines.push("");
760
+ return lines.join("\n");
761
+ }
762
+ function printServiceFormatted(svc, rank, total) {
763
+ console.log(formatServiceCard(svc, rank, total));
756
764
  }
757
765
  // ── Schema ────────────────────────────────────────────────────────────
758
766
  export async function x402SchemaCommand(url, opts = {}) {
@@ -1144,8 +1152,7 @@ export async function x402CallCommand(url, opts = {}) {
1144
1152
  resolvedPrice = parseFloat(schema.price);
1145
1153
  }
1146
1154
  }
1147
- // Confirm payment unless --yes
1148
- if (!opts.yes && !opts.interactive) {
1155
+ if (!opts.yes && !opts.interactive && !output.isNonInteractive()) {
1149
1156
  const priceDisplay = resolvedPrice !== null
1150
1157
  ? chalk.green(`$${resolvedPrice} USDC`)
1151
1158
  : chalk.green(`up to $${maxPaymentUsd} USDC`);
@@ -1235,7 +1242,7 @@ export async function x402SearchCommand(queryParts, opts = {}) {
1235
1242
  }
1236
1243
  const spin = output.spinner("Searching services...");
1237
1244
  try {
1238
- const params = new URLSearchParams({ q: query, limit: "10" });
1245
+ const params = new URLSearchParams({ q: query, limit: "50" });
1239
1246
  const res = await fetch(`${getApiUrl()}/x402/endpoints/discover?${params}`);
1240
1247
  const result = await handleResponse(res);
1241
1248
  spin.stop();
@@ -1246,16 +1253,25 @@ export async function x402SearchCommand(queryParts, opts = {}) {
1246
1253
  output.info(`No services found for "${query}"`);
1247
1254
  return;
1248
1255
  }
1249
- // Raw mode: dump JSON and exit
1256
+ // Raw mode: dump JSON and exit (preserves natural rank order for AI
1257
+ // agents and pipelines — most relevant first).
1250
1258
  if (opts.raw) {
1251
1259
  console.log(JSON.stringify(services, null, 2));
1252
1260
  return;
1253
1261
  }
1262
+ // Print results in REVERSE rank order so the cursor parks on the most
1263
+ // relevant result (rank #1) at the bottom of the buffer. Each card has
1264
+ // a `#N of M` badge so the rank is always visible regardless of which
1265
+ // direction you read.
1254
1266
  console.log();
1255
- console.log(` ${chalk.dim(`Found ${services.length} service(s) for`)} "${query}"`);
1267
+ console.log(` ${chalk.dim(`Found ${services.length} service(s) for`)} "${query}" ${chalk.dim("(scrolling order: lowest rank first, top match at the bottom)")}`);
1256
1268
  console.log(` ${chalk.dim("─".repeat(60))}`);
1257
- for (const svc of services) {
1258
- printServiceFormatted(svc);
1269
+ console.log();
1270
+ const total = services.length;
1271
+ // Iterate in reverse: start with the lowest-ranked result, end on rank #1.
1272
+ for (let i = total - 1; i >= 0; i--) {
1273
+ const svc = services[i];
1274
+ printServiceFormatted(svc, i + 1, total);
1259
1275
  }
1260
1276
  }
1261
1277
  catch (err) {
package/dist/lib/api.d.ts CHANGED
@@ -209,7 +209,6 @@ export interface DeployTokenResponse {
209
209
  tokenAddress: string;
210
210
  poolId: string;
211
211
  txHash?: string;
212
- activityId: string;
213
212
  chain: string;
214
213
  simulated?: boolean;
215
214
  feeDistribution: Record<string, {
@@ -292,15 +291,39 @@ export interface StreamProgress {
292
291
  */
293
292
  export declare function getBeneficiaryFeesStreaming(address: string, onProgress: (progress: StreamProgress) => void): Promise<BeneficiaryFeesResponse>;
294
293
  /**
295
- * Fetch creator fee data for a specific token address. Unauthenticated —
296
- * looks up the token's creator beneficiary and returns their fee data.
294
+ * Fetch creator fee data for a specific token address. Unauthenticated.
295
+ *
296
+ * Uses the unified `/token-launches/:addr/fees` endpoint (added as part of
297
+ * the token-launches API consolidation epic). The legacy alias
298
+ * `GET /public/doppler/token-fees/:addr` still works but emits `Deprecation`
299
+ * and `Sunset` headers.
297
300
  */
298
301
  export declare function getTokenCreatorFees(tokenAddress: string, days?: number): Promise<CreatorFeesResponse>;
299
302
  /**
300
303
  * Build a Doppler fee claim transaction. Requires authentication.
301
304
  * The returned transaction data should be submitted via `submit()`.
305
+ *
306
+ * @deprecated Use `claimTokenLaunchFees` instead — it builds, signs, and
307
+ * broadcasts in a single HTTP call. Kept here so older CLI scripts continue
308
+ * to work through the deprecation window.
302
309
  */
303
310
  export declare function buildDopplerClaimTx(tokenAddress: string): Promise<DopplerClaimTxResponse>;
311
+ export interface ClaimTokenLaunchFeesResponse {
312
+ transactionHash?: string;
313
+ status?: string;
314
+ signer?: string;
315
+ chainId: number;
316
+ description: string;
317
+ blockNumber?: string;
318
+ gasUsed?: string;
319
+ }
320
+ /**
321
+ * Claim trading fees for a Bankr-launched token. Auto-detects Doppler vs
322
+ * Clanker and executes (build + sign + broadcast) in a single call.
323
+ *
324
+ * Replaces the two-step `buildDopplerClaimTx` + `submit` flow.
325
+ */
326
+ export declare function claimTokenLaunchFees(tokenAddress: string): Promise<ClaimTokenLaunchFeesResponse>;
304
327
  export interface BuildClaimTx {
305
328
  tokenAddress: string;
306
329
  tokenName: string;
@@ -399,6 +422,49 @@ export interface TransferResponse {
399
422
  error?: string;
400
423
  }
401
424
  export declare function transfer(request: TransferRequest): Promise<TransferResponse>;
425
+ export interface SwapQuoteRequest {
426
+ fromChain: string;
427
+ fromToken: string;
428
+ toChain: string;
429
+ toToken: string;
430
+ amount: string;
431
+ }
432
+ export interface SwapQuoteSide {
433
+ chain: string;
434
+ token: string;
435
+ amount: string;
436
+ formattedAmount: string;
437
+ symbol: string;
438
+ decimals: number;
439
+ usdValue: string;
440
+ }
441
+ export interface SwapQuoteResponse {
442
+ from: SwapQuoteSide;
443
+ to: SwapQuoteSide;
444
+ minBuyAmount: string;
445
+ }
446
+ export interface SwapExecuteRequest extends SwapQuoteRequest {
447
+ minBuyAmount: string;
448
+ }
449
+ export interface SwapExecuteResponse {
450
+ success: boolean;
451
+ hash?: string;
452
+ error?: string;
453
+ amountReceived?: number;
454
+ amountReceivedRaw?: string;
455
+ amountSold?: number;
456
+ amountSoldRaw?: string;
457
+ }
458
+ export declare function getSwapQuote(request: SwapQuoteRequest): Promise<SwapQuoteResponse>;
459
+ export declare function executeSwap(request: SwapExecuteRequest): Promise<SwapExecuteResponse>;
460
+ export type RecipientType = "address" | "ens" | "twitter" | "farcaster";
461
+ export interface ResolveRecipientResponse {
462
+ resolved: boolean;
463
+ address?: string;
464
+ displayName?: string;
465
+ error?: string;
466
+ }
467
+ export declare function resolveRecipient(value: string, type: RecipientType): Promise<ResolveRecipientResponse>;
402
468
  export interface X402PayRequest {
403
469
  url: string;
404
470
  method?: string;
package/dist/lib/api.js CHANGED
@@ -1,4 +1,4 @@
1
- import { CLI_USER_AGENT, getApiUrl, readConfig, requireApiKey, } from "./config.js";
1
+ import { CLI_USER_AGENT, getApiUrl, requireApiKey } from "./config.js";
2
2
  const TERMINAL_STATUSES = [
3
3
  "completed",
4
4
  "failed",
@@ -8,16 +8,11 @@ function isTerminalStatus(status) {
8
8
  return TERMINAL_STATUSES.includes(status);
9
9
  }
10
10
  function authHeaders() {
11
- const headers = {
11
+ return {
12
12
  "X-API-Key": requireApiKey(),
13
13
  "Content-Type": "application/json",
14
14
  "User-Agent": CLI_USER_AGENT,
15
15
  };
16
- const config = readConfig();
17
- if (config.partnerKey) {
18
- headers["X-Partner-Key"] = config.partnerKey;
19
- }
20
- return headers;
21
16
  }
22
17
  async function handleResponse(res) {
23
18
  if (!res.ok) {
@@ -219,12 +214,16 @@ export async function getBeneficiaryFeesStreaming(address, onProgress) {
219
214
  return result;
220
215
  }
221
216
  /**
222
- * Fetch creator fee data for a specific token address. Unauthenticated —
223
- * looks up the token's creator beneficiary and returns their fee data.
217
+ * Fetch creator fee data for a specific token address. Unauthenticated.
218
+ *
219
+ * Uses the unified `/token-launches/:addr/fees` endpoint (added as part of
220
+ * the token-launches API consolidation epic). The legacy alias
221
+ * `GET /public/doppler/token-fees/:addr` still works but emits `Deprecation`
222
+ * and `Sunset` headers.
224
223
  */
225
224
  export async function getTokenCreatorFees(tokenAddress, days) {
226
225
  const params = days ? `?days=${days}` : "";
227
- const res = await fetch(`${getApiUrl()}/public/doppler/token-fees/${tokenAddress}${params}`, {
226
+ const res = await fetch(`${getApiUrl()}/token-launches/${tokenAddress}/fees${params}`, {
228
227
  headers: {
229
228
  "User-Agent": CLI_USER_AGENT,
230
229
  },
@@ -234,6 +233,10 @@ export async function getTokenCreatorFees(tokenAddress, days) {
234
233
  /**
235
234
  * Build a Doppler fee claim transaction. Requires authentication.
236
235
  * The returned transaction data should be submitted via `submit()`.
236
+ *
237
+ * @deprecated Use `claimTokenLaunchFees` instead — it builds, signs, and
238
+ * broadcasts in a single HTTP call. Kept here so older CLI scripts continue
239
+ * to work through the deprecation window.
237
240
  */
238
241
  export async function buildDopplerClaimTx(tokenAddress) {
239
242
  const res = await fetch(`${getApiUrl()}/agent/doppler/claim`, {
@@ -243,6 +246,20 @@ export async function buildDopplerClaimTx(tokenAddress) {
243
246
  });
244
247
  return handleResponse(res);
245
248
  }
249
+ /**
250
+ * Claim trading fees for a Bankr-launched token. Auto-detects Doppler vs
251
+ * Clanker and executes (build + sign + broadcast) in a single call.
252
+ *
253
+ * Replaces the two-step `buildDopplerClaimTx` + `submit` flow.
254
+ */
255
+ export async function claimTokenLaunchFees(tokenAddress) {
256
+ const res = await fetch(`${getApiUrl()}/token-launches/${tokenAddress}/fees/claim`, {
257
+ method: "POST",
258
+ headers: authHeaders(),
259
+ body: JSON.stringify({}),
260
+ });
261
+ return handleResponse(res);
262
+ }
246
263
  export async function getOwnProfile() {
247
264
  const res = await fetch(`${getApiUrl()}/agent/profile`, {
248
265
  headers: authHeaders(),
@@ -299,6 +316,31 @@ export async function transfer(request) {
299
316
  });
300
317
  return handleResponse(res);
301
318
  }
319
+ export async function getSwapQuote(request) {
320
+ const res = await fetch(`${getApiUrl()}/wallet/swap-quote`, {
321
+ method: "POST",
322
+ headers: authHeaders(),
323
+ body: JSON.stringify(request),
324
+ });
325
+ return handleResponse(res);
326
+ }
327
+ export async function executeSwap(request) {
328
+ const res = await fetch(`${getApiUrl()}/wallet/swap`, {
329
+ method: "POST",
330
+ headers: authHeaders(),
331
+ body: JSON.stringify(request),
332
+ });
333
+ return handleResponse(res);
334
+ }
335
+ export async function resolveRecipient(value, type) {
336
+ const params = new URLSearchParams({ value, type });
337
+ const res = await fetch(`${getApiUrl()}/addresses/resolve?${params}`, {
338
+ headers: {
339
+ "User-Agent": CLI_USER_AGENT,
340
+ },
341
+ });
342
+ return handleResponse(res);
343
+ }
302
344
  export async function x402Pay(request) {
303
345
  const res = await fetch(`${getApiUrl()}/wallet/x402-pay`, {
304
346
  method: "POST",
@@ -5,7 +5,6 @@ export interface BankrConfig {
5
5
  llmKey?: string;
6
6
  llmUrl?: string;
7
7
  lastThreadId?: string;
8
- partnerKey?: string;
9
8
  sound?: SoundConfig;
10
9
  }
11
10
  export declare function setConfigPath(path: string): void;
@@ -44,5 +44,6 @@ export interface RemediationStep {
44
44
  * Used when the API returns a `remediation` array in error responses.
45
45
  */
46
46
  export declare function remediation(steps: RemediationStep[]): void;
47
+ export declare function isNonInteractive(): boolean;
47
48
  export declare function formatStatus(status: string): string;
48
49
  //# sourceMappingURL=output.d.ts.map
@@ -105,6 +105,9 @@ export function remediation(steps) {
105
105
  }
106
106
  console.log();
107
107
  }
108
+ export function isNonInteractive() {
109
+ return process.env.BANKR_NOT_INTERACTIVE === "1";
110
+ }
108
111
  export function formatStatus(status) {
109
112
  switch (status) {
110
113
  case "completed":
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Native-token sentinel (zero address). The API detects + sanitizes native
3
+ * tokens from this, so callers send it instead of a real contract address.
4
+ */
5
+ export declare const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
6
+ /** Shorten an address for display, e.g. `0x8335…2913`. */
7
+ export declare function shortenAddress(address: string): string;
8
+ /**
9
+ * Resolve a token symbol or 0x address to `{ address, symbol }` on a chain:
10
+ * - a valid 0x address is used as-is (symbol shown shortened for display),
11
+ * - otherwise an exact, case-insensitive symbol match is looked up via
12
+ * `/tokens/search`, throwing a "Did you mean" suggestion list on no match.
13
+ *
14
+ * Native-token handling is caller-specific (a `--native` flag for transfer, a
15
+ * native-ticker/zero-address short-circuit for swap), so it lives in the command.
16
+ */
17
+ export declare function resolveTokenSymbol(tokenInput: string, chain: string): Promise<{
18
+ address: string;
19
+ symbol: string;
20
+ }>;
21
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1,40 @@
1
+ import { isAddress } from "viem";
2
+ import { searchTokens } from "./api.js";
3
+ import { CHAIN_IDS } from "./chains.js";
4
+ /**
5
+ * Native-token sentinel (zero address). The API detects + sanitizes native
6
+ * tokens from this, so callers send it instead of a real contract address.
7
+ */
8
+ export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
9
+ /** Shorten an address for display, e.g. `0x8335…2913`. */
10
+ export function shortenAddress(address) {
11
+ return `${address.slice(0, 6)}…${address.slice(-4)}`;
12
+ }
13
+ /**
14
+ * Resolve a token symbol or 0x address to `{ address, symbol }` on a chain:
15
+ * - a valid 0x address is used as-is (symbol shown shortened for display),
16
+ * - otherwise an exact, case-insensitive symbol match is looked up via
17
+ * `/tokens/search`, throwing a "Did you mean" suggestion list on no match.
18
+ *
19
+ * Native-token handling is caller-specific (a `--native` flag for transfer, a
20
+ * native-ticker/zero-address short-circuit for swap), so it lives in the command.
21
+ */
22
+ export async function resolveTokenSymbol(tokenInput, chain) {
23
+ const input = tokenInput.trim();
24
+ if (isAddress(input)) {
25
+ return { address: input, symbol: shortenAddress(input) };
26
+ }
27
+ const chainId = CHAIN_IDS[chain];
28
+ const res = await searchTokens(input, chainId);
29
+ const inputUpper = input.toUpperCase();
30
+ const exactMatch = res.tokens.find((t) => t.symbol.toUpperCase() === inputUpper);
31
+ if (!exactMatch) {
32
+ const available = res.tokens
33
+ .slice(0, 5)
34
+ .map((t) => `${t.symbol} (${t.address})`)
35
+ .join(", ");
36
+ throw new Error(`No exact match for "${input}" on ${chain}. Did you mean: ${available || "no results"}`);
37
+ }
38
+ return { address: exactMatch.address, symbol: exactMatch.symbol };
39
+ }
40
+ //# sourceMappingURL=tokens.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bankr/cli",
3
- "version": "0.2.15",
3
+ "version": "0.3.8",
4
4
  "description": "Official CLI for the Bankr AI agent platform",
5
5
  "type": "module",
6
6
  "bin": {
@@ -40,11 +40,11 @@
40
40
  "url": "https://discord.gg/bankr"
41
41
  },
42
42
  "dependencies": {
43
- "@inquirer/prompts": "^8.2.0",
44
- "chalk": "^5.3.0",
45
- "commander": "^12.1.0",
46
- "open": "^10.1.0",
47
- "ora": "^8.1.0",
43
+ "@inquirer/prompts": "^8.4.2",
44
+ "chalk": "^5.6.2",
45
+ "commander": "^14.0.3",
46
+ "open": "^11.0.0",
47
+ "ora": "^9.4.0",
48
48
  "viem": "^2.37.2"
49
49
  }
50
50
  }