@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.
@@ -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?"
@@ -1,4 +1,6 @@
1
- export declare function modelsCommand(): Promise<void>;
1
+ export declare function modelsCommand(opts?: {
2
+ private?: boolean;
3
+ }): Promise<void>;
2
4
  export declare function creditsCommand(): Promise<void>;
3
5
  export declare function creditsAddCommand(amount: string, opts: {
4
6
  token?: string;
@@ -5,8 +5,7 @@ import { spawn } from "child_process";
5
5
  import { CLI_USER_AGENT, getApiUrl, getLlmKey, getLlmUrl, requireApiKey, } from "../lib/config.js";
6
6
  import { getBalances } from "../lib/api.js";
7
7
  import * as output from "../lib/output.js";
8
- import { bankrTheme } from "../lib/output.js";
9
- const ERR_LLM_NOT_ENABLED = "LLM Gateway not enabled on this API key. Enable at bankr.bot/api";
8
+ const ERR_LLM_NOT_ENABLED = "LLM Gateway not enabled on this API key. Enable at bankr.bot/api-keys";
10
9
  /**
11
10
  * Fallback model catalog — used when the gateway is unreachable or unauthenticated.
12
11
  * resolveModels() fetches live data from GET /v1/models; this list is the offline safety net.
@@ -15,6 +14,24 @@ const IMAGE_INPUT = ["text", "image"];
15
14
  const TEXT_INPUT = ["text"];
16
15
  const GATEWAY_MODELS = [
17
16
  // Claude
17
+ {
18
+ id: "claude-opus-4.8",
19
+ name: "Claude Opus 4.8",
20
+ owned_by: "anthropic",
21
+ contextWindow: 1000000,
22
+ maxTokens: 128000,
23
+ input: IMAGE_INPUT,
24
+ cost: { input: 5.0, output: 25.0, cacheRead: 0.5, cacheWrite: 6.25 },
25
+ },
26
+ {
27
+ id: "claude-opus-4.7",
28
+ name: "Claude Opus 4.7",
29
+ owned_by: "anthropic",
30
+ contextWindow: 1000000,
31
+ maxTokens: 128000,
32
+ input: IMAGE_INPUT,
33
+ cost: { input: 5.0, output: 25.0, cacheRead: 0.5, cacheWrite: 6.25 },
34
+ },
18
35
  {
19
36
  id: "claude-opus-4.6",
20
37
  name: "Claude Opus 4.6",
@@ -171,6 +188,15 @@ const GATEWAY_MODELS = [
171
188
  cost: { input: 0.05, output: 0.4, cacheRead: 0.005, cacheWrite: 0 },
172
189
  },
173
190
  // Other providers
191
+ {
192
+ id: "grok-4.3",
193
+ name: "Grok 4.3",
194
+ owned_by: "x-ai",
195
+ contextWindow: 1000000,
196
+ maxTokens: 1000000,
197
+ input: IMAGE_INPUT,
198
+ cost: { input: 1.25, output: 2.5, cacheRead: 0.2, cacheWrite: 0 },
199
+ },
174
200
  {
175
201
  id: "grok-4.1-fast",
176
202
  name: "Grok 4.1 Fast",
@@ -198,6 +224,24 @@ const GATEWAY_MODELS = [
198
224
  input: TEXT_INPUT,
199
225
  cost: { input: 0.12, output: 0.75, cacheRead: 0.06, cacheWrite: 0 },
200
226
  },
227
+ {
228
+ id: "qwen3.7-plus",
229
+ name: "Qwen3.7 Plus",
230
+ owned_by: "qwen",
231
+ contextWindow: 1000000,
232
+ maxTokens: 65536,
233
+ input: TEXT_INPUT,
234
+ cost: { input: 0.4, output: 1.6, cacheRead: 0.04, cacheWrite: 0 },
235
+ },
236
+ {
237
+ id: "qwen3.6-flash",
238
+ name: "Qwen3.6 Flash",
239
+ owned_by: "qwen",
240
+ contextWindow: 1000000,
241
+ maxTokens: 65536,
242
+ input: TEXT_INPUT,
243
+ cost: { input: 0.25, output: 1.5, cacheRead: 0, cacheWrite: 0 },
244
+ },
201
245
  {
202
246
  id: "qwen3.5-plus",
203
247
  name: "Qwen3.5 Plus",
@@ -216,6 +260,15 @@ const GATEWAY_MODELS = [
216
260
  input: TEXT_INPUT,
217
261
  cost: { input: 0.1, output: 0.4, cacheRead: 0, cacheWrite: 0 },
218
262
  },
263
+ {
264
+ id: "kimi-k2.7-code",
265
+ name: "Kimi K2.7 Code",
266
+ owned_by: "moonshotai",
267
+ contextWindow: 262144,
268
+ maxTokens: 262144,
269
+ input: TEXT_INPUT,
270
+ cost: { input: 0.95, output: 4.0, cacheRead: 0.19, cacheWrite: 0 },
271
+ },
219
272
  {
220
273
  id: "kimi-k2.6",
221
274
  name: "Kimi K2.6",
@@ -316,6 +369,7 @@ async function resolveModels() {
316
369
  cacheWrite: 0,
317
370
  }),
318
371
  ...(m.deprecation && { deprecation: m.deprecation }),
372
+ ...(m.private && { private: true }),
319
373
  };
320
374
  });
321
375
  return { models, live: true };
@@ -354,28 +408,46 @@ function installConfigFile(configFile, toolName, merge) {
354
408
  output.success(`Bankr provider installed to ${configFile}`);
355
409
  }
356
410
  /* ───────────────────────────── bankr llm models ───────────────────────────── */
357
- export async function modelsCommand() {
411
+ export async function modelsCommand(opts = {}) {
358
412
  const spin = output.spinner("Fetching models…");
359
413
  const { models, live } = await resolveModels();
360
414
  spin.stop();
361
- output.brandBold("Bankr LLM Gateway — Available Models");
415
+ // `private` is the gateway's own "supports :private" flag from
416
+ // /v1/models (driven by each model's NEAR slot + NEARAI_API_KEY), so this
417
+ // tracks the live registry rather than a hardcoded CLI list.
418
+ const shown = opts.private ? models.filter((m) => m.private) : models;
419
+ output.brandBold(opts.private
420
+ ? "Bankr LLM Gateway — Private Models"
421
+ : "Bankr LLM Gateway — Available Models");
362
422
  console.log();
423
+ if (opts.private && shown.length === 0) {
424
+ output.warn(live
425
+ ? "No models currently offer a private (TEE) compute environment."
426
+ : "Private status needs a live gateway — set BANKR_LLM_KEY (and BANKR_LLM_URL).");
427
+ return;
428
+ }
363
429
  const COL = { id: 24, name: 24, provider: 12 };
364
- const hasDeprecated = models.some((m) => m.deprecation);
365
- console.log(` ${output.fmt.brandBold("Model ID".padEnd(COL.id))} ${output.fmt.brandBold("Name".padEnd(COL.name))} ${output.fmt.brandBold("Provider".padEnd(COL.provider))}${hasDeprecated ? ` ${output.fmt.brandBold("Notes")}` : ""}`);
366
- console.log(output.fmt.dim(` ${"─".repeat(COL.id + COL.name + COL.provider + (hasDeprecated ? 24 : 2))}`));
367
- for (const m of models) {
430
+ const hasNotes = shown.some((m) => m.deprecation || m.private);
431
+ console.log(` ${output.fmt.brandBold("Model ID".padEnd(COL.id))} ${output.fmt.brandBold("Name".padEnd(COL.name))} ${output.fmt.brandBold("Provider".padEnd(COL.provider))}${hasNotes ? ` ${output.fmt.brandBold("Notes")}` : ""}`);
432
+ console.log(output.fmt.dim(` ${"─".repeat(COL.id + COL.name + COL.provider + (hasNotes ? 24 : 2))}`));
433
+ for (const m of shown) {
368
434
  let notes = "";
369
435
  if (m.deprecation) {
370
436
  notes = m.deprecation.replaced_by
371
437
  ? `deprecated → ${m.deprecation.replaced_by}`
372
438
  : "deprecated";
373
439
  }
440
+ else if (m.private) {
441
+ notes = "Private (TEE)";
442
+ }
374
443
  console.log(` ${output.fmt.brand(m.id.padEnd(COL.id))} ${m.name.padEnd(COL.name)} ${output.fmt.dim(m.owned_by.padEnd(COL.provider))} ${notes ? output.fmt.dim(notes) : ""}`);
375
444
  }
376
445
  console.log();
377
446
  output.dim(` Gateway: ${getLlmUrl()}`);
378
- output.dim(` ${models.length} models available${live ? "" : " (cached)"}`);
447
+ output.dim(` ${shown.length} model${shown.length === 1 ? "" : "s"}${opts.private ? " support :private" : " available"}${live ? "" : " (cached)"}`);
448
+ if (!opts.private && shown.some((m) => m.private)) {
449
+ output.dim(" Private (TEE) — append :private to the model id (e.g. glm-5.2:private). List: bankr llm models --private");
450
+ }
379
451
  }
380
452
  /* ─────────────────────── bankr llm credits ─────────────────────────────────── */
381
453
  export async function creditsCommand() {
@@ -422,6 +494,89 @@ export async function creditsCommand() {
422
494
  }
423
495
  }
424
496
  /* ─────────────────────── bankr llm credits add ──────────────────────────── */
497
+ // Keep in sync with packages/shared/src/chain-config-base/chainConfigBase.ts.
498
+ // CLI is standalone-published and cannot depend on @bankr/shared.
499
+ const TOPUP_CHAINS = ["base", "polygon", "mainnet", "arbitrum", "bnb"];
500
+ const CHAIN_DISPLAY_NAME = {
501
+ base: "Base",
502
+ polygon: "Polygon",
503
+ mainnet: "Ethereum",
504
+ arbitrum: "Arbitrum",
505
+ bnb: "BNB Chain",
506
+ };
507
+ // Polygon's native POL is at a precompile, not the 0xeee placeholder.
508
+ const NATIVE_TOKEN_ADDRESS_BY_CHAIN = {
509
+ base: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
510
+ polygon: "0x0000000000000000000000000000000000001010",
511
+ mainnet: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
512
+ arbitrum: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
513
+ bnb: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
514
+ };
515
+ const BLOCK_EXPLORER_URL = {
516
+ base: "https://basescan.org",
517
+ polygon: "https://polygonscan.com",
518
+ mainnet: "https://etherscan.io",
519
+ arbitrum: "https://arbiscan.io",
520
+ bnb: "https://bscscan.com",
521
+ };
522
+ /**
523
+ * Resolve user input (symbol or 0x address) to the (chain, token) it should
524
+ * pay with. Scans every supported chain and picks the highest USD balance,
525
+ * so the user never has to think about chains. Returns null if nothing matches.
526
+ */
527
+ function resolveTokenAcrossChains(input, balances) {
528
+ const isAddress = input.startsWith("0x");
529
+ const sym = isAddress ? null : input.toUpperCase();
530
+ const candidates = [];
531
+ for (const chain of TOPUP_CHAINS) {
532
+ const chainBal = balances[chain];
533
+ if (!chainBal)
534
+ continue;
535
+ if (!isAddress) {
536
+ const nativeUsd = parseFloat(chainBal.nativeUsd || "0");
537
+ const nativeSym = nativeSymbolFor(chain);
538
+ if (sym === nativeSym && nativeUsd > 0) {
539
+ candidates.push({
540
+ chain,
541
+ address: NATIVE_TOKEN_ADDRESS_BY_CHAIN[chain],
542
+ symbol: nativeSym,
543
+ name: nativeSym,
544
+ decimals: 18,
545
+ balance: parseFloat(chainBal.nativeBalance || "0"),
546
+ balanceUsd: nativeUsd,
547
+ });
548
+ }
549
+ }
550
+ for (const tb of chainBal.tokenBalances) {
551
+ const matchesSym = sym != null && tb.token.baseToken.symbol.toUpperCase() === sym;
552
+ const matchesAddr = isAddress &&
553
+ tb.token.baseToken.address.toLowerCase() === input.toLowerCase();
554
+ if (!matchesSym && !matchesAddr)
555
+ continue;
556
+ candidates.push({
557
+ chain,
558
+ address: tb.token.baseToken.address,
559
+ symbol: tb.token.baseToken.symbol,
560
+ name: tb.token.baseToken.name,
561
+ decimals: tb.token.baseToken.decimals,
562
+ balance: tb.token.balance,
563
+ balanceUsd: tb.token.balanceUSD,
564
+ imageUrl: tb.token.baseToken.imgUrl,
565
+ });
566
+ }
567
+ }
568
+ if (candidates.length === 0)
569
+ return null;
570
+ candidates.sort((a, b) => b.balanceUsd - a.balanceUsd);
571
+ return candidates[0];
572
+ }
573
+ function nativeSymbolFor(chain) {
574
+ if (chain === "polygon")
575
+ return "POL";
576
+ if (chain === "bnb")
577
+ return "BNB";
578
+ return "ETH";
579
+ }
425
580
  export async function creditsAddCommand(amount, opts) {
426
581
  const apiKey = requireApiKey();
427
582
  const apiUrl = getApiUrl();
@@ -433,9 +588,9 @@ export async function creditsAddCommand(amount, opts) {
433
588
  output.error("Amount must be between $1 and $1,000");
434
589
  process.exit(1);
435
590
  }
436
- // Fetch current balance for display (skip when --yes since user won't see it)
591
+ // Fetch current balance for display (skip when --yes/--not-interactive since user won't see it)
437
592
  let currentBalance = "unknown";
438
- if (llmKey && !opts.yes) {
593
+ if (llmKey && !opts.yes && !output.isNonInteractive()) {
439
594
  const balSpin = output.spinner("Fetching balance…");
440
595
  try {
441
596
  const balRes = await fetch(`${llmUrl}/v1/credits`, {
@@ -457,18 +612,41 @@ export async function creditsAddCommand(amount, opts) {
457
612
  output.dim(` (Could not fetch balance: ${err.message})`);
458
613
  }
459
614
  }
615
+ // When --token is passed, the chain holding the highest USD balance of
616
+ // that symbol/address wins. Default (no --token) is Base USDC.
617
+ let resolved = null;
618
+ if (opts.token) {
619
+ const resolveSpin = output.spinner("Resolving token…");
620
+ try {
621
+ const balData = await getBalances([...TOPUP_CHAINS]);
622
+ resolved = resolveTokenAcrossChains(opts.token, balData.balances);
623
+ resolveSpin.stop();
624
+ }
625
+ catch (err) {
626
+ resolveSpin.stop();
627
+ output.warn(`Could not fetch balances: ${err.message}`);
628
+ }
629
+ if (!resolved) {
630
+ output.error(`Token "${opts.token}" not found in your wallet on any supported chain.`);
631
+ process.exit(1);
632
+ }
633
+ }
634
+ const targetChain = resolved?.chain ?? "base";
635
+ const sourceTokenLabel = resolved
636
+ ? `${resolved.symbol} on ${CHAIN_DISPLAY_NAME[targetChain]}`
637
+ : `USDC on ${CHAIN_DISPLAY_NAME.base} (default)`;
460
638
  // Show confirmation
461
639
  console.log();
462
640
  output.label("Add Credits", `$${amountUsd.toFixed(2)}`);
463
- output.keyValue("Source token", opts.token ?? "USDC (default)");
641
+ output.keyValue("Pay with", sourceTokenLabel);
464
642
  output.keyValue("Current balance", currentBalance);
465
643
  console.log();
466
- if (!opts.yes) {
644
+ if (!opts.yes && !output.isNonInteractive()) {
467
645
  const { confirm } = await import("@inquirer/prompts");
468
646
  const proceed = await confirm({
469
647
  message: "Proceed?",
470
648
  default: true,
471
- theme: bankrTheme,
649
+ theme: output.bankrTheme,
472
650
  });
473
651
  if (!proceed) {
474
652
  output.dim("Cancelled.");
@@ -477,9 +655,12 @@ export async function creditsAddCommand(amount, opts) {
477
655
  }
478
656
  const spin = output.spinner("Adding credits…");
479
657
  try {
480
- const body = { amountUsd };
481
- if (opts.token) {
482
- body.sourceToken = opts.token;
658
+ const body = {
659
+ amountUsd,
660
+ chain: targetChain,
661
+ };
662
+ if (resolved) {
663
+ body.sourceToken = resolved.address;
483
664
  }
484
665
  const res = await fetch(`${apiUrl}/llm/credits/topup`, {
485
666
  method: "POST",
@@ -516,7 +697,7 @@ export async function creditsAddCommand(amount, opts) {
516
697
  output.success(`Added $${result.creditsGranted.toFixed(2)} credits`);
517
698
  output.label("New Balance", `$${result.newBalance.toFixed(2)}`);
518
699
  if (result.txHash) {
519
- output.keyValue("Transaction", `https://basescan.org/tx/${result.txHash}`);
700
+ output.keyValue("Transaction", `${BLOCK_EXPLORER_URL[targetChain]}/tx/${result.txHash}`);
520
701
  }
521
702
  }
522
703
  catch (err) {
@@ -632,45 +813,27 @@ export async function creditsAutoCommand(opts) {
632
813
  process.exit(1);
633
814
  }
634
815
  const resolved = [];
635
- // Fetch balances to resolve both symbols and addresses
816
+ // Highest USD balance wins when the same symbol exists on multiple chains.
636
817
  const spin = output.spinner("Resolving tokens…");
637
- let baseTokens = [];
638
- let hasNativeEth = false;
818
+ let balances = {};
639
819
  try {
640
- const balData = await getBalances(["base"]);
641
- const baseChain = balData.balances.base;
642
- baseTokens = baseChain?.tokenBalances ?? [];
643
- hasNativeEth = !!baseChain && parseFloat(baseChain.nativeBalance) > 0;
820
+ const balData = await getBalances([...TOPUP_CHAINS]);
821
+ balances = balData.balances;
644
822
  }
645
823
  catch {
646
824
  output.warn("Could not fetch balances for token resolution");
647
825
  }
648
826
  spin.stop();
649
827
  for (const input of inputs) {
650
- const isAddress = input.startsWith("0x");
651
- const sym = isAddress ? null : input.toUpperCase();
652
- // Native ETH
653
- if (sym === "ETH" && hasNativeEth) {
828
+ const hit = resolveTokenAcrossChains(input, balances);
829
+ if (hit) {
654
830
  resolved.push({
655
- address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
656
- chain: "base",
657
- symbol: "ETH",
658
- name: "Ethereum",
659
- decimals: 18,
660
- });
661
- continue;
662
- }
663
- const tb = baseTokens.find((t) => isAddress
664
- ? t.token.baseToken.address.toLowerCase() === input.toLowerCase()
665
- : t.token.baseToken.symbol.toUpperCase() === sym);
666
- if (tb) {
667
- resolved.push({
668
- address: tb.token.baseToken.address,
669
- chain: "base",
670
- symbol: tb.token.baseToken.symbol,
671
- name: tb.token.baseToken.name,
672
- decimals: tb.token.baseToken.decimals,
673
- imageUrl: tb.token.baseToken.imgUrl,
831
+ address: hit.address,
832
+ chain: hit.chain,
833
+ symbol: hit.symbol,
834
+ name: hit.name,
835
+ decimals: hit.decimals,
836
+ imageUrl: hit.imageUrl,
674
837
  });
675
838
  }
676
839
  else {
@@ -740,15 +903,25 @@ export async function setupOpenclawCommand(opts) {
740
903
  baseUrl: llmUrl,
741
904
  apiKey: llmKey ?? "${BANKR_API_KEY}",
742
905
  api: "openai-completions",
743
- models: activeModels.map((m) => ({
744
- id: m.id,
745
- name: m.name,
746
- ...(m.owned_by === "anthropic" ? { api: "anthropic-messages" } : {}),
747
- input: m.input,
748
- contextWindow: m.contextWindow,
749
- maxTokens: m.maxTokens,
750
- cost: m.cost,
751
- })),
906
+ // Private models get both entries: the plain id and a `:private`
907
+ // twin that routes through the TEE compute environment — so users can pick either.
908
+ models: activeModels.flatMap((m) => {
909
+ const base = {
910
+ id: m.id,
911
+ name: m.name,
912
+ ...(m.owned_by === "anthropic" ? { api: "anthropic-messages" } : {}),
913
+ input: m.input,
914
+ contextWindow: m.contextWindow,
915
+ maxTokens: m.maxTokens,
916
+ cost: m.cost,
917
+ };
918
+ return m.private
919
+ ? [
920
+ base,
921
+ { ...base, id: `${m.id}:private`, name: `${m.name} (Private)` },
922
+ ]
923
+ : [base];
924
+ }),
752
925
  };
753
926
  if (opts.install) {
754
927
  const configFile = join(homedir(), ".openclaw", "openclaw.json");
@@ -778,6 +951,10 @@ export async function setupOpenCodeCommand(opts) {
778
951
  const modelsObj = {};
779
952
  for (const m of activeModels) {
780
953
  modelsObj[m.id] = { name: m.name };
954
+ // Private models also expose a `:private` variant (TEE compute environment).
955
+ if (m.private) {
956
+ modelsObj[`${m.id}:private`] = { name: `${m.name} (Private)` };
957
+ }
781
958
  }
782
959
  const bankrProvider = {
783
960
  npm: "@ai-sdk/openai-compatible",
@@ -810,7 +987,7 @@ export async function setupOpenCodeCommand(opts) {
810
987
  output.warn("No API key set. Set BANKR_API_KEY env var or run: bankr login");
811
988
  }
812
989
  output.dim("Add to ~/.config/opencode/opencode.json or run: bankr llm setup opencode --install");
813
- output.dim("Use bankr/ prefix for models: opencode -m bankr/claude-opus-4.6");
990
+ output.dim("Use bankr/ prefix for models: opencode -m bankr/claude-opus-4.8");
814
991
  }
815
992
  /* ──────────────────────── bankr llm setup cursor ──────────────────────────── */
816
993
  export async function setupCursorCommand() {
@@ -820,7 +997,7 @@ export async function setupCursorCommand() {
820
997
  const { models } = await resolveModels();
821
998
  const activeModels = models.filter((m) => !m.deprecation);
822
999
  // Pick one model per provider as recommended examples
823
- const recommendedIds = ["claude-opus-4.6", "gemini-3.1-pro", "gpt-5.2"];
1000
+ const recommendedIds = ["claude-opus-4.8", "gemini-3.1-pro", "gpt-5.2"];
824
1001
  const recommended = recommendedIds.filter((id) => activeModels.some((m) => m.id === id));
825
1002
  output.brandBold("Cursor — Bankr LLM Gateway");
826
1003
  console.log();
@@ -859,8 +1036,8 @@ export async function setupClaudeCodeCommand() {
859
1036
  output.dim("Or launch directly: bankr llm claude");
860
1037
  console.log();
861
1038
  output.info("Model format:");
862
- output.dim(" Claude Code accepts Anthropic-style IDs (dashed): claude-opus-4-7, claude-sonnet-4-6.");
863
- output.dim(" `bankr llm claude` auto-translates the dotted form (claude-opus-4.7) for you.");
1039
+ output.dim(" Claude Code accepts Anthropic-style IDs (dashed): claude-opus-4-8, claude-sonnet-4-6.");
1040
+ output.dim(" `bankr llm claude` auto-translates the dotted form (claude-opus-4.8) for you.");
864
1041
  }
865
1042
  /* ─────────────────────── Shared launcher helpers ────────────────────────── */
866
1043
  function requireAuth() {
@@ -901,8 +1078,8 @@ function fileContains(filePath, search) {
901
1078
  }
902
1079
  /* ─────────────────────────── bankr llm claude ─────────────────────────────── */
903
1080
  /**
904
- * Rewrite gateway-canonical Claude IDs (dotted, e.g. `claude-opus-4.7`) to
905
- * the wire format Claude Code expects (dashed, e.g. `claude-opus-4-7`).
1081
+ * Rewrite gateway-canonical Claude IDs (dotted, e.g. `claude-opus-4.8`) to
1082
+ * the wire format Claude Code expects (dashed, e.g. `claude-opus-4-8`).
906
1083
  * Otherwise Claude Code silently falls back to its default model — the
907
1084
  * `--model` flag appears honored but the real request is a different
908
1085
  * model. Any suffix (e.g. `[1m]` context tier) is preserved.
@@ -13,7 +13,6 @@ export declare function loginCommand(opts: {
13
13
  url?: boolean;
14
14
  siwe?: boolean;
15
15
  privateKey?: string;
16
- partnerKey?: string;
17
16
  allowedIps?: string;
18
17
  allowedRecipients?: string;
19
18
  }): Promise<void>;