@bankr/cli 0.3.23 → 0.3.35

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,15 +1,21 @@
1
- import { confirm, input, select } from "@inquirer/prompts";
2
- import { deployToken, } from "../lib/api.js";
1
+ import { confirm, input, search, select } from "@inquirer/prompts";
2
+ import { deployToken, getLaunchQuoteTokens, } from "../lib/api.js";
3
3
  import { emitCESP } from "../lib/cesp/engine.js";
4
4
  import { requireApiKey } from "../lib/config.js";
5
5
  import * as output from "../lib/output.js";
6
6
  import { getErrorMessage } from "../lib/errors.js";
7
+ import { quoteTokenAvailabilityNote, quoteTokenDeployFields, resolveQuoteToken, } from "../lib/quoteTokens.js";
7
8
  const VALID_FEE_TYPES = ["x", "farcaster", "ens", "wallet"];
8
- const VALID_CHAINS = ["base", "robinhood"];
9
- function chainLabel(chain) {
10
- return chain === "robinhood" ? "Robinhood Chain" : "Base";
9
+ const VALID_CHAINS = ["base", "robinhood", "arbitrum"];
10
+ const CHAIN_LABELS = {
11
+ base: "Base",
12
+ robinhood: "Robinhood Chain",
13
+ arbitrum: "Arbitrum",
14
+ };
15
+ export function launchChainLabel(chain) {
16
+ return CHAIN_LABELS[chain];
11
17
  }
12
- function parseChain(value) {
18
+ export function parseLaunchChain(value) {
13
19
  if (!value)
14
20
  return undefined;
15
21
  const lower = value.toLowerCase();
@@ -17,8 +23,7 @@ function parseChain(value) {
17
23
  return lower;
18
24
  // Launching is irreversible and chain-selecting — a mistyped chain must be
19
25
  // a hard usage error, never a silent fallback to Base.
20
- output.error(`Unknown chain "${value}". Valid chains: ${VALID_CHAINS.join(", ")}.`);
21
- process.exit(1);
26
+ output.fatal(`Unknown chain "${value}". Valid chains: ${VALID_CHAINS.join(", ")}.`);
22
27
  }
23
28
  function parseFeeType(value) {
24
29
  if (!value)
@@ -29,6 +34,60 @@ function parseFeeType(value) {
29
34
  output.warn(`Unknown fee type "${value}". Valid types: ${VALID_FEE_TYPES.join(", ")}. Defaulting to "x".`);
30
35
  return "x";
31
36
  }
37
+ function quoteTokenChoiceName(option) {
38
+ const note = quoteTokenAvailabilityNote(option);
39
+ const base = option.isDefault
40
+ ? `${option.symbol} (default)`
41
+ : `${option.symbol} — ${option.name}`;
42
+ return note ? `${base} [${note}]` : base;
43
+ }
44
+ /**
45
+ * Resolve the quote token for the pool: --quote wins; otherwise the wizard
46
+ * offers the chain's list. Non-interactive runs without --quote keep the
47
+ * chain default and never hit the network.
48
+ */
49
+ async function collectQuoteToken(chain, requested) {
50
+ if (!requested && output.isNonInteractive())
51
+ return undefined;
52
+ let listing;
53
+ try {
54
+ listing = await getLaunchQuoteTokens(chain);
55
+ }
56
+ catch (err) {
57
+ if (requested) {
58
+ output.fatal(`Could not load quote tokens for ${launchChainLabel(chain)}: ${getErrorMessage(err)}`);
59
+ }
60
+ output.warn(`Could not load quote tokens for ${launchChainLabel(chain)}; pairing with the chain default.`);
61
+ return undefined;
62
+ }
63
+ const { provider, quoteTokens } = listing;
64
+ if (requested) {
65
+ const option = resolveQuoteToken(quoteTokens, requested);
66
+ if (!option) {
67
+ output.fatal(`"${requested}" is not a quote token you can pair with on ${launchChainLabel(chain)}. Run "bankr launch quotes --chain ${chain}" to see the list.`);
68
+ }
69
+ return option.isDefault ? undefined : { option, provider };
70
+ }
71
+ // Nothing to choose from — the chain only offers its default.
72
+ if (quoteTokens.length <= 1)
73
+ return undefined;
74
+ const option = await search({
75
+ message: "Quote token to pair the pool with? (type to filter)",
76
+ source: (term) => {
77
+ const key = (term ?? "").trim().toLowerCase().replace(/^\$/, "");
78
+ return quoteTokens
79
+ .filter((candidate) => !key ||
80
+ candidate.symbol.toLowerCase().includes(key) ||
81
+ candidate.name.toLowerCase().includes(key))
82
+ .map((candidate) => ({
83
+ name: quoteTokenChoiceName(candidate),
84
+ value: candidate,
85
+ }));
86
+ },
87
+ theme: output.bankrTheme,
88
+ });
89
+ return option.isDefault ? undefined : { option, provider };
90
+ }
32
91
  function feeTypeLabel(type) {
33
92
  switch (type) {
34
93
  case "x":
@@ -48,8 +107,7 @@ async function promptOrSkip(message) {
48
107
  }
49
108
  async function collectInteractive(options) {
50
109
  if (output.isNonInteractive() && !options.name) {
51
- output.error("--name is required in non-interactive mode");
52
- process.exit(1);
110
+ output.fatal("--name is required in non-interactive mode");
53
111
  }
54
112
  const tokenName = options.name ??
55
113
  (await input({
@@ -63,19 +121,21 @@ async function collectInteractive(options) {
63
121
  (await promptOrSkip("Image URL for your token (press Enter to skip):"));
64
122
  // Chain: --chain flag wins; otherwise interactive select (default Base).
65
123
  // Non-interactive runs without --chain launch on Base.
66
- let chain = parseChain(options.chain);
124
+ let chain = parseLaunchChain(options.chain);
67
125
  if (!chain && !output.isNonInteractive()) {
68
126
  chain = await select({
69
127
  message: "Which chain do you want to launch on?",
70
- choices: [
71
- { name: "Base", value: "base" },
72
- { name: "Robinhood Chain", value: "robinhood" },
73
- ],
128
+ choices: VALID_CHAINS.map((value) => ({
129
+ name: launchChainLabel(value),
130
+ value,
131
+ })),
74
132
  default: "base",
75
133
  theme: output.bankrTheme,
76
134
  });
77
135
  }
78
136
  chain ?? (chain = "base");
137
+ const quoteToken = await collectQuoteToken(chain, options.quote);
138
+ const quoteSymbol = quoteToken?.option.symbol ?? "WETH";
79
139
  // Vesting: --no-vesting wins; otherwise interactive select (default: keep
80
140
  // the 15% creator vesting). Non-interactive runs keep vesting on.
81
141
  let disableVesting = options.vesting === false;
@@ -95,7 +155,7 @@ async function collectInteractive(options) {
95
155
  let quoteOnlyFees = options.quoteOnlyFees === true;
96
156
  if (!quoteOnlyFees && !output.isNonInteractive()) {
97
157
  quoteOnlyFees = await select({
98
- message: "Fee collection? By default creator fees accrue as a mix of your token and the pool's quote token (e.g. WETH).",
158
+ message: `Fee collection? By default creator fees accrue as a mix of your token and the pool's quote token (${quoteSymbol}).`,
99
159
  choices: [
100
160
  { name: "Both tokens (default)", value: false },
101
161
  { name: "Quote token only", value: true },
@@ -129,6 +189,7 @@ async function collectInteractive(options) {
129
189
  tokenSymbol: tokenSymbol.trim(),
130
190
  imageUrl: imageUrl.trim(),
131
191
  chain,
192
+ quoteToken,
132
193
  tweetUrl: tweetUrl.trim(),
133
194
  websiteUrl: websiteUrl.trim(),
134
195
  feeRecipient: feeRecipient.trim(),
@@ -142,8 +203,7 @@ export async function launchCommand(options) {
142
203
  // Collect token details
143
204
  const state = await collectInteractive(options);
144
205
  if (!state.tokenName) {
145
- output.error("Token name is required.");
146
- process.exit(1);
206
+ output.fatal("Token name is required.");
147
207
  }
148
208
  // Display summary
149
209
  console.log();
@@ -157,17 +217,32 @@ export async function launchCommand(options) {
157
217
  output.keyValue("Fee recipient", state.feeRecipient
158
218
  ? `${state.feeRecipient} (${feeTypeLabel(state.feeRecipientType)})`
159
219
  : "(self)");
160
- output.keyValue("Chain", chainLabel(state.chain));
220
+ output.keyValue("Chain", launchChainLabel(state.chain));
221
+ const quoteSymbol = state.quoteToken?.option.symbol ?? "WETH";
222
+ output.keyValue("Quote token", state.quoteToken
223
+ ? `${quoteSymbol} — ${state.quoteToken.option.name} (${state.quoteToken.option.address})`
224
+ : "WETH (default)");
161
225
  output.keyValue("Vesting", state.disableVesting
162
226
  ? "Off (100% of supply sold into the pool)"
163
227
  : "15% to fee recipient over 1 year (default)");
164
228
  output.keyValue("Fee collection", state.quoteOnlyFees
165
- ? "Quote token only (e.g. WETH)"
229
+ ? `Quote token only (${quoteSymbol})`
166
230
  : "Both tokens (default)");
167
231
  if (options.simulate) {
168
232
  output.keyValue("Mode", "Simulation (dry run)");
169
233
  }
170
234
  console.log();
235
+ const quoteNote = state.quoteToken
236
+ ? quoteTokenAvailabilityNote(state.quoteToken.option)
237
+ : undefined;
238
+ if (quoteNote === "thin liquidity") {
239
+ output.warn(`${quoteSymbol} has thin liquidity right now — the pool may be hard to trade until more arrives.`);
240
+ console.log();
241
+ }
242
+ else if (quoteNote) {
243
+ output.warn(`${quoteSymbol} is ${quoteNote}; the API may refuse this pairing until it is live.`);
244
+ console.log();
245
+ }
171
246
  if (!options.yes && !output.isNonInteractive()) {
172
247
  const proceed = await confirm({
173
248
  message: options.simulate
@@ -198,6 +273,8 @@ export async function launchCommand(options) {
198
273
  simulateOnly: options.simulate,
199
274
  disableVesting: state.disableVesting || undefined,
200
275
  quoteOnlyFees: state.quoteOnlyFees || undefined,
276
+ ...(state.quoteToken &&
277
+ quoteTokenDeployFields(state.quoteToken.option, state.quoteToken.provider)),
201
278
  });
202
279
  const elapsed = output.formatDuration(Date.now() - startTime);
203
280
  spin.succeed(result.simulated
@@ -229,8 +306,7 @@ export async function launchCommand(options) {
229
306
  catch (err) {
230
307
  spin.fail("Token launch failed");
231
308
  emitCESP("task.error");
232
- output.error(getErrorMessage(err));
233
- process.exit(1);
309
+ output.fatal(getErrorMessage(err));
234
310
  }
235
311
  }
236
312
  //# sourceMappingURL=launch.js.map
@@ -0,0 +1,7 @@
1
+ export interface LaunchQuotesOptions {
2
+ chain?: string;
3
+ json?: boolean;
4
+ }
5
+ /** `bankr launch quotes` — the quote tokens a launch on a chain can pair with. */
6
+ export declare function launchQuotesCommand(opts: LaunchQuotesOptions): Promise<void>;
7
+ //# sourceMappingURL=launchQuotes.d.ts.map
@@ -0,0 +1,49 @@
1
+ import { getLaunchQuoteTokens } from "../lib/api.js";
2
+ import * as output from "../lib/output.js";
3
+ import { getErrorMessage } from "../lib/errors.js";
4
+ import { quoteTokenAvailabilityNote, quoteTokenGroupLabel, } from "../lib/quoteTokens.js";
5
+ import { launchChainLabel, parseLaunchChain } from "./launch.js";
6
+ const GROUP_ORDER = [
7
+ "Default",
8
+ "Majors",
9
+ "More tokens",
10
+ "Stocks",
11
+ ];
12
+ /** `bankr launch quotes` — the quote tokens a launch on a chain can pair with. */
13
+ export async function launchQuotesCommand(opts) {
14
+ const chain = parseLaunchChain(opts.chain) ?? "base";
15
+ if (opts.json) {
16
+ try {
17
+ console.log(JSON.stringify(await getLaunchQuoteTokens(chain), null, 2));
18
+ }
19
+ catch (err) {
20
+ output.fatal(getErrorMessage(err));
21
+ }
22
+ return;
23
+ }
24
+ const spin = output.spinner(`Loading quote tokens for ${launchChainLabel(chain)}...`);
25
+ let listing;
26
+ try {
27
+ listing = await getLaunchQuoteTokens(chain);
28
+ }
29
+ catch (err) {
30
+ output.failWith(spin, "Could not load quote tokens", err);
31
+ }
32
+ spin.succeed(`${listing.quoteTokens.length} quote token(s) on ${launchChainLabel(chain)}`);
33
+ const symbolWidth = Math.max(...listing.quoteTokens.map((option) => option.symbol.length), 4);
34
+ for (const group of GROUP_ORDER) {
35
+ const members = listing.quoteTokens.filter((option) => quoteTokenGroupLabel(option) === group);
36
+ if (members.length === 0)
37
+ continue;
38
+ console.log();
39
+ output.brandBold(` ${group} (${members.length})`);
40
+ for (const option of members) {
41
+ const note = quoteTokenAvailabilityNote(option);
42
+ const label = `${option.symbol.padEnd(symbolWidth)} ${option.name}`;
43
+ console.log(` ${label} ${output.fmt.dim(option.address)}${note ? ` ${output.fmt.dim(`[${note}]`)}` : ""}`);
44
+ }
45
+ }
46
+ console.log();
47
+ output.dim(`Pair a launch with one: bankr launch --chain ${chain} --quote <symbol or address>`);
48
+ }
49
+ //# sourceMappingURL=launchQuotes.js.map
@@ -23,6 +23,14 @@ export declare function setupOpenCodeCommand(opts: {
23
23
  }): Promise<void>;
24
24
  export declare function setupCursorCommand(): Promise<void>;
25
25
  export declare function setupClaudeCodeCommand(): Promise<void>;
26
+ /**
27
+ * Rewrite gateway-canonical Claude IDs (dotted, e.g. `claude-opus-4.8`) to
28
+ * the wire format Claude Code expects (dashed, e.g. `claude-opus-4-8`).
29
+ * Otherwise Claude Code silently falls back to its default model — the
30
+ * `--model` flag appears honored but the real request is a different
31
+ * model. Any suffix (e.g. `[1m]` context tier) is preserved.
32
+ */
33
+ export declare function toAnthropicModelId(model: string): string;
26
34
  export declare function claudeCommand(args: string[]): Promise<void>;
27
35
  export declare function opencodeCommand(args: string[]): Promise<void>;
28
36
  //# sourceMappingURL=llm.d.ts.map