@bankr/cli 0.3.12 → 0.3.23
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 +28 -10
- package/dist/cli.js +31 -8
- package/dist/commands/balances.js +2 -1
- package/dist/commands/cancel.js +2 -1
- package/dist/commands/club.js +4 -3
- package/dist/commands/fees.js +83 -45
- package/dist/commands/files.js +14 -13
- package/dist/commands/launch.d.ts +5 -0
- package/dist/commands/launch.js +76 -7
- package/dist/commands/llm.d.ts +2 -0
- package/dist/commands/llm.js +264 -44
- package/dist/commands/login.d.ts +14 -0
- package/dist/commands/login.js +41 -30
- package/dist/commands/portfolio.js +3 -2
- package/dist/commands/profile.js +6 -5
- package/dist/commands/prompt.js +19 -6
- package/dist/commands/sign.js +2 -1
- package/dist/commands/sounds.js +4 -3
- package/dist/commands/status.js +3 -2
- package/dist/commands/submit.js +3 -2
- package/dist/commands/swap.js +9 -7
- package/dist/commands/tokens.js +3 -2
- package/dist/commands/transfer.js +8 -6
- package/dist/commands/update.js +2 -1
- package/dist/commands/webhooks.js +15 -10
- package/dist/commands/whoami.js +3 -2
- package/dist/commands/x402.d.ts +3 -0
- package/dist/commands/x402.js +75 -30
- package/dist/lib/api.d.ts +18 -1
- package/dist/lib/api.js +32 -41
- package/dist/lib/errors.d.ts +7 -0
- package/dist/lib/errors.js +9 -0
- package/dist/lib/explorer.d.ts +7 -0
- package/dist/lib/explorer.js +14 -0
- package/dist/lib/output.d.ts +1 -0
- package/dist/lib/output.js +6 -0
- package/dist/lib/passthrough.d.ts +21 -0
- package/dist/lib/passthrough.js +26 -0
- package/dist/lib/tokens.d.ts +0 -5
- package/dist/lib/tokens.js +0 -5
- package/package.json +1 -1
package/dist/commands/launch.js
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
import { confirm, input, select } from "@inquirer/prompts";
|
|
2
|
-
import { deployToken } from "../lib/api.js";
|
|
2
|
+
import { deployToken, } 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
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
6
7
|
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";
|
|
11
|
+
}
|
|
12
|
+
function parseChain(value) {
|
|
13
|
+
if (!value)
|
|
14
|
+
return undefined;
|
|
15
|
+
const lower = value.toLowerCase();
|
|
16
|
+
if (VALID_CHAINS.includes(lower))
|
|
17
|
+
return lower;
|
|
18
|
+
// Launching is irreversible and chain-selecting — a mistyped chain must be
|
|
19
|
+
// 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);
|
|
22
|
+
}
|
|
7
23
|
function parseFeeType(value) {
|
|
8
24
|
if (!value)
|
|
9
25
|
return "x";
|
|
@@ -45,6 +61,49 @@ async function collectInteractive(options) {
|
|
|
45
61
|
(await promptOrSkip("Token symbol (press Enter to auto-generate from name):"));
|
|
46
62
|
const imageUrl = options.image ??
|
|
47
63
|
(await promptOrSkip("Image URL for your token (press Enter to skip):"));
|
|
64
|
+
// Chain: --chain flag wins; otherwise interactive select (default Base).
|
|
65
|
+
// Non-interactive runs without --chain launch on Base.
|
|
66
|
+
let chain = parseChain(options.chain);
|
|
67
|
+
if (!chain && !output.isNonInteractive()) {
|
|
68
|
+
chain = await select({
|
|
69
|
+
message: "Which chain do you want to launch on?",
|
|
70
|
+
choices: [
|
|
71
|
+
{ name: "Base", value: "base" },
|
|
72
|
+
{ name: "Robinhood Chain", value: "robinhood" },
|
|
73
|
+
],
|
|
74
|
+
default: "base",
|
|
75
|
+
theme: output.bankrTheme,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
chain ?? (chain = "base");
|
|
79
|
+
// Vesting: --no-vesting wins; otherwise interactive select (default: keep
|
|
80
|
+
// the 15% creator vesting). Non-interactive runs keep vesting on.
|
|
81
|
+
let disableVesting = options.vesting === false;
|
|
82
|
+
if (!disableVesting && !output.isNonInteractive()) {
|
|
83
|
+
disableVesting = await select({
|
|
84
|
+
message: "Creator vesting? By default 15% of supply vests to the fee recipient over 1 year (30-day cliff).",
|
|
85
|
+
choices: [
|
|
86
|
+
{ name: "15% vesting (default)", value: false },
|
|
87
|
+
{ name: "No vesting — sell 100% into the pool", value: true },
|
|
88
|
+
],
|
|
89
|
+
default: false,
|
|
90
|
+
theme: output.bankrTheme,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// Fee collection: --quote-only-fees wins; otherwise interactive select
|
|
94
|
+
// (default: in-kind). Non-interactive runs keep the in-kind default.
|
|
95
|
+
let quoteOnlyFees = options.quoteOnlyFees === true;
|
|
96
|
+
if (!quoteOnlyFees && !output.isNonInteractive()) {
|
|
97
|
+
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).",
|
|
99
|
+
choices: [
|
|
100
|
+
{ name: "Both tokens (default)", value: false },
|
|
101
|
+
{ name: "Quote token only", value: true },
|
|
102
|
+
],
|
|
103
|
+
default: false,
|
|
104
|
+
theme: output.bankrTheme,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
48
107
|
const tweetUrl = options.tweet ??
|
|
49
108
|
(await promptOrSkip("Associate with a tweet? Paste URL (press Enter to skip):"));
|
|
50
109
|
const websiteUrl = options.website ??
|
|
@@ -64,17 +123,18 @@ async function collectInteractive(options) {
|
|
|
64
123
|
theme: output.bankrTheme,
|
|
65
124
|
});
|
|
66
125
|
}
|
|
67
|
-
|
|
68
|
-
feeRecipient = "";
|
|
69
|
-
}
|
|
126
|
+
feeRecipient || (feeRecipient = "");
|
|
70
127
|
return {
|
|
71
128
|
tokenName: tokenName.trim(),
|
|
72
129
|
tokenSymbol: tokenSymbol.trim(),
|
|
73
130
|
imageUrl: imageUrl.trim(),
|
|
131
|
+
chain,
|
|
74
132
|
tweetUrl: tweetUrl.trim(),
|
|
75
133
|
websiteUrl: websiteUrl.trim(),
|
|
76
134
|
feeRecipient: feeRecipient.trim(),
|
|
77
135
|
feeRecipientType,
|
|
136
|
+
disableVesting,
|
|
137
|
+
quoteOnlyFees,
|
|
78
138
|
};
|
|
79
139
|
}
|
|
80
140
|
export async function launchCommand(options) {
|
|
@@ -97,7 +157,13 @@ export async function launchCommand(options) {
|
|
|
97
157
|
output.keyValue("Fee recipient", state.feeRecipient
|
|
98
158
|
? `${state.feeRecipient} (${feeTypeLabel(state.feeRecipientType)})`
|
|
99
159
|
: "(self)");
|
|
100
|
-
output.keyValue("Chain",
|
|
160
|
+
output.keyValue("Chain", chainLabel(state.chain));
|
|
161
|
+
output.keyValue("Vesting", state.disableVesting
|
|
162
|
+
? "Off (100% of supply sold into the pool)"
|
|
163
|
+
: "15% to fee recipient over 1 year (default)");
|
|
164
|
+
output.keyValue("Fee collection", state.quoteOnlyFees
|
|
165
|
+
? "Quote token only (e.g. WETH)"
|
|
166
|
+
: "Both tokens (default)");
|
|
101
167
|
if (options.simulate) {
|
|
102
168
|
output.keyValue("Mode", "Simulation (dry run)");
|
|
103
169
|
}
|
|
@@ -123,12 +189,15 @@ export async function launchCommand(options) {
|
|
|
123
189
|
tokenName: state.tokenName,
|
|
124
190
|
tokenSymbol: state.tokenSymbol || undefined,
|
|
125
191
|
image: state.imageUrl || undefined,
|
|
192
|
+
chain: state.chain,
|
|
126
193
|
tweetUrl: state.tweetUrl || undefined,
|
|
127
194
|
websiteUrl: state.websiteUrl || undefined,
|
|
128
195
|
feeRecipient: state.feeRecipient
|
|
129
196
|
? { type: state.feeRecipientType, value: state.feeRecipient }
|
|
130
197
|
: undefined,
|
|
131
198
|
simulateOnly: options.simulate,
|
|
199
|
+
disableVesting: state.disableVesting || undefined,
|
|
200
|
+
quoteOnlyFees: state.quoteOnlyFees || undefined,
|
|
132
201
|
});
|
|
133
202
|
const elapsed = output.formatDuration(Date.now() - startTime);
|
|
134
203
|
spin.succeed(result.simulated
|
|
@@ -155,12 +224,12 @@ export async function launchCommand(options) {
|
|
|
155
224
|
}
|
|
156
225
|
}
|
|
157
226
|
console.log();
|
|
158
|
-
output.dim(`View token: https://bankr.bot/
|
|
227
|
+
output.dim(`View token: https://bankr.bot/terminal/trade?out=${result.tokenAddress}&chain=${result.chain}`);
|
|
159
228
|
}
|
|
160
229
|
catch (err) {
|
|
161
230
|
spin.fail("Token launch failed");
|
|
162
231
|
emitCESP("task.error");
|
|
163
|
-
output.error(err
|
|
232
|
+
output.error(getErrorMessage(err));
|
|
164
233
|
process.exit(1);
|
|
165
234
|
}
|
|
166
235
|
}
|
package/dist/commands/llm.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare function modelsCommand(opts?: {
|
|
2
2
|
private?: boolean;
|
|
3
|
+
zdr?: boolean;
|
|
3
4
|
}): Promise<void>;
|
|
4
5
|
export declare function creditsCommand(): Promise<void>;
|
|
5
6
|
export declare function creditsAddCommand(amount: string, opts: {
|
|
@@ -15,6 +16,7 @@ export declare function creditsAutoCommand(opts: {
|
|
|
15
16
|
}): Promise<void>;
|
|
16
17
|
export declare function setupOpenclawCommand(opts: {
|
|
17
18
|
install?: boolean;
|
|
19
|
+
images?: boolean;
|
|
18
20
|
}): Promise<void>;
|
|
19
21
|
export declare function setupOpenCodeCommand(opts: {
|
|
20
22
|
install?: boolean;
|
package/dist/commands/llm.js
CHANGED
|
@@ -5,7 +5,13 @@ 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 { getErrorMessage } from "../lib/errors.js";
|
|
9
|
+
import { isHelpRequest } from "../lib/passthrough.js";
|
|
8
10
|
const ERR_LLM_NOT_ENABLED = "LLM Gateway not enabled on this API key. Enable at bankr.bot/api-keys";
|
|
11
|
+
/** True when the model serves chat completions (image-only models don't). */
|
|
12
|
+
function supportsChat(m) {
|
|
13
|
+
return (m.output ?? ["text"]).includes("text");
|
|
14
|
+
}
|
|
9
15
|
/**
|
|
10
16
|
* Fallback model catalog — used when the gateway is unreachable or unauthenticated.
|
|
11
17
|
* resolveModels() fetches live data from GET /v1/models; this list is the offline safety net.
|
|
@@ -14,6 +20,15 @@ const IMAGE_INPUT = ["text", "image"];
|
|
|
14
20
|
const TEXT_INPUT = ["text"];
|
|
15
21
|
const GATEWAY_MODELS = [
|
|
16
22
|
// Claude
|
|
23
|
+
{
|
|
24
|
+
id: "claude-fable-5",
|
|
25
|
+
name: "Claude Fable 5",
|
|
26
|
+
owned_by: "anthropic",
|
|
27
|
+
contextWindow: 1000000,
|
|
28
|
+
maxTokens: 128000,
|
|
29
|
+
input: IMAGE_INPUT,
|
|
30
|
+
cost: { input: 10.0, output: 50.0, cacheRead: 1.0, cacheWrite: 12.5 },
|
|
31
|
+
},
|
|
17
32
|
{
|
|
18
33
|
id: "claude-opus-4.8",
|
|
19
34
|
name: "Claude Opus 4.8",
|
|
@@ -78,6 +93,24 @@ const GATEWAY_MODELS = [
|
|
|
78
93
|
cost: { input: 1.0, output: 5.0, cacheRead: 0.1, cacheWrite: 1.25 },
|
|
79
94
|
},
|
|
80
95
|
// Gemini
|
|
96
|
+
{
|
|
97
|
+
id: "gemini-3.6-flash",
|
|
98
|
+
name: "Gemini 3.6 Flash",
|
|
99
|
+
owned_by: "google",
|
|
100
|
+
contextWindow: 1048576,
|
|
101
|
+
maxTokens: 65536,
|
|
102
|
+
input: IMAGE_INPUT,
|
|
103
|
+
cost: { input: 1.5, output: 7.5, cacheRead: 0.15, cacheWrite: 0.08333 },
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "gemini-3.5-flash-lite",
|
|
107
|
+
name: "Gemini 3.5 Flash Lite",
|
|
108
|
+
owned_by: "google",
|
|
109
|
+
contextWindow: 1048576,
|
|
110
|
+
maxTokens: 65536,
|
|
111
|
+
input: IMAGE_INPUT,
|
|
112
|
+
cost: { input: 0.3, output: 2.5, cacheRead: 0.03, cacheWrite: 0.08333 },
|
|
113
|
+
},
|
|
81
114
|
{
|
|
82
115
|
id: "gemini-3.1-pro",
|
|
83
116
|
name: "Gemini 3.1 Pro",
|
|
@@ -124,6 +157,27 @@ const GATEWAY_MODELS = [
|
|
|
124
157
|
cost: { input: 0.3, output: 2.5, cacheRead: 0.03, cacheWrite: 0.0833 },
|
|
125
158
|
},
|
|
126
159
|
// OpenAI
|
|
160
|
+
{
|
|
161
|
+
id: "gpt-image-2",
|
|
162
|
+
name: "GPT Image 2",
|
|
163
|
+
owned_by: "openai",
|
|
164
|
+
contextWindow: 32000,
|
|
165
|
+
maxTokens: 16384,
|
|
166
|
+
input: TEXT_INPUT,
|
|
167
|
+
output: ["image"],
|
|
168
|
+
// Image model — served via /v1/images/generations; output rate is per
|
|
169
|
+
// million image tokens.
|
|
170
|
+
cost: { input: 5.0, output: 30.0, cacheRead: 0, cacheWrite: 0 },
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
id: "gpt-5.5",
|
|
174
|
+
name: "GPT 5.5",
|
|
175
|
+
owned_by: "openai",
|
|
176
|
+
contextWindow: 1050000,
|
|
177
|
+
maxTokens: 128000,
|
|
178
|
+
input: IMAGE_INPUT,
|
|
179
|
+
cost: { input: 5.0, output: 30.0, cacheRead: 0.5, cacheWrite: 0 },
|
|
180
|
+
},
|
|
127
181
|
{
|
|
128
182
|
id: "gpt-5.4",
|
|
129
183
|
name: "GPT 5.4",
|
|
@@ -260,6 +314,15 @@ const GATEWAY_MODELS = [
|
|
|
260
314
|
input: TEXT_INPUT,
|
|
261
315
|
cost: { input: 0.1, output: 0.4, cacheRead: 0, cacheWrite: 0 },
|
|
262
316
|
},
|
|
317
|
+
{
|
|
318
|
+
id: "kimi-k3",
|
|
319
|
+
name: "Kimi K3",
|
|
320
|
+
owned_by: "moonshotai",
|
|
321
|
+
contextWindow: 1048576,
|
|
322
|
+
maxTokens: 131072,
|
|
323
|
+
input: IMAGE_INPUT,
|
|
324
|
+
cost: { input: 3.0, output: 15.0, cacheRead: 0.3, cacheWrite: 0 },
|
|
325
|
+
},
|
|
263
326
|
{
|
|
264
327
|
id: "kimi-k2.7-code",
|
|
265
328
|
name: "Kimi K2.7 Code",
|
|
@@ -355,6 +418,9 @@ async function resolveModels() {
|
|
|
355
418
|
contextWindow: m.context_window ?? fallback?.contextWindow ?? 128000,
|
|
356
419
|
maxTokens: m.max_output_tokens ?? fallback?.maxTokens ?? 32768,
|
|
357
420
|
input: m.input_modalities ?? fallback?.input ?? TEXT_INPUT,
|
|
421
|
+
...((m.output_modalities ?? fallback?.output) && {
|
|
422
|
+
output: m.output_modalities ?? fallback?.output,
|
|
423
|
+
}),
|
|
358
424
|
cost: m.pricing
|
|
359
425
|
? {
|
|
360
426
|
input: m.pricing.input,
|
|
@@ -370,6 +436,7 @@ async function resolveModels() {
|
|
|
370
436
|
}),
|
|
371
437
|
...(m.deprecation && { deprecation: m.deprecation }),
|
|
372
438
|
...(m.private && { private: true }),
|
|
439
|
+
...(m.zdr && { zdr: true }),
|
|
373
440
|
};
|
|
374
441
|
});
|
|
375
442
|
return { models, live: true };
|
|
@@ -408,46 +475,142 @@ function installConfigFile(configFile, toolName, merge) {
|
|
|
408
475
|
output.success(`Bankr provider installed to ${configFile}`);
|
|
409
476
|
}
|
|
410
477
|
/* ───────────────────────────── bankr llm models ───────────────────────────── */
|
|
478
|
+
/**
|
|
479
|
+
* The tier filters. Both flags read the gateway's own capability flags from
|
|
480
|
+
* /v1/models, so they track the live registry rather than a hardcoded list.
|
|
481
|
+
*/
|
|
482
|
+
const TIER_VIEWS = {
|
|
483
|
+
private: {
|
|
484
|
+
title: "Bankr LLM Gateway — Private Models",
|
|
485
|
+
match: (m) => m.private === true,
|
|
486
|
+
empty: "No models currently offer a private (TEE) compute environment.",
|
|
487
|
+
countSuffix: " support :private",
|
|
488
|
+
},
|
|
489
|
+
zdr: {
|
|
490
|
+
title: "Bankr LLM Gateway — Zero-Retention Models",
|
|
491
|
+
match: (m) => m.zdr === true,
|
|
492
|
+
empty: "No models currently guarantee zero data retention.",
|
|
493
|
+
countSuffix: " support :zdr",
|
|
494
|
+
},
|
|
495
|
+
};
|
|
411
496
|
export async function modelsCommand(opts = {}) {
|
|
412
497
|
const spin = output.spinner("Fetching models…");
|
|
413
498
|
const { models, live } = await resolveModels();
|
|
414
499
|
spin.stop();
|
|
415
|
-
//
|
|
416
|
-
//
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
500
|
+
// `--private` wins when both are passed: private is the narrower set (every
|
|
501
|
+
// private model is also ZDR), so it's the more specific of the two requests.
|
|
502
|
+
const view = opts.private
|
|
503
|
+
? TIER_VIEWS.private
|
|
504
|
+
: opts.zdr
|
|
505
|
+
? TIER_VIEWS.zdr
|
|
506
|
+
: undefined;
|
|
507
|
+
const shown = view ? models.filter(view.match) : models;
|
|
508
|
+
output.brandBold(view?.title ?? "Bankr LLM Gateway — Available Models");
|
|
422
509
|
console.log();
|
|
423
|
-
if (
|
|
510
|
+
if (view && shown.length === 0) {
|
|
424
511
|
output.warn(live
|
|
425
|
-
?
|
|
426
|
-
: "
|
|
512
|
+
? view.empty
|
|
513
|
+
: "Tier status needs a live gateway — set BANKR_LLM_KEY (and BANKR_LLM_URL).");
|
|
427
514
|
return;
|
|
428
515
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
516
|
+
// Widths are sized to the longest real value (`minimax-m2.7-highspeed` /
|
|
517
|
+
// `MiniMax M2.7 Highspeed` are both 22), keeping the table under ~90 columns
|
|
518
|
+
// once the tier list is included.
|
|
519
|
+
const COL = { id: 23, name: 23, provider: 11, privacy: 28 };
|
|
520
|
+
const isImageGen = (m) => m.output?.includes("image") === true;
|
|
521
|
+
/**
|
|
522
|
+
* Every tier this model can serve, not just the strongest.
|
|
523
|
+
*
|
|
524
|
+
* The tiers nest, so a private model is also reachable at zdr and standard —
|
|
525
|
+
* listing only the top one made the ladder look like three separate offers
|
|
526
|
+
* and hid that `glm-5.2` (no suffix) is a perfectly valid standard request.
|
|
527
|
+
* Read straight off the flags rather than inferring `zdr` from `private`, so
|
|
528
|
+
* the column reports what the gateway actually advertises.
|
|
529
|
+
*/
|
|
530
|
+
const supportedTiers = (m) => {
|
|
531
|
+
// Each tier is painted individually. Colouring the joined string instead
|
|
532
|
+
// made "standard" grey on a standard-only row and green on a ZDR one — the
|
|
533
|
+
// same word changing colour purely because of what followed it.
|
|
534
|
+
const tiers = [
|
|
535
|
+
// The baseline, on every row, so it recedes rather than competing.
|
|
536
|
+
{ label: "standard", paint: output.fmt.dim },
|
|
537
|
+
];
|
|
538
|
+
if (m.zdr)
|
|
539
|
+
tiers.push({ label: "zdr", paint: output.fmt.success });
|
|
540
|
+
if (m.private) {
|
|
541
|
+
// Distinct from zdr: they are two rungs, not one capability.
|
|
542
|
+
tiers.push({ label: "private (tee)", paint: output.fmt.magic });
|
|
543
|
+
}
|
|
544
|
+
return tiers;
|
|
545
|
+
};
|
|
546
|
+
/**
|
|
547
|
+
* Pad from the PLAIN text — ANSI escapes have zero display width but count
|
|
548
|
+
* toward `.length`, so padding the painted string misaligns every coloured
|
|
549
|
+
* row against the header.
|
|
550
|
+
*/
|
|
551
|
+
const tierCell = (m) => {
|
|
552
|
+
const tiers = supportedTiers(m);
|
|
553
|
+
const plain = tiers.map((t) => t.label).join(", ");
|
|
554
|
+
const painted = tiers
|
|
555
|
+
.map((t) => t.paint(t.label))
|
|
556
|
+
.join(output.fmt.dim(", "));
|
|
557
|
+
return painted + " ".repeat(Math.max(0, COL.privacy - plain.length));
|
|
558
|
+
};
|
|
559
|
+
/**
|
|
560
|
+
* Everything that is NOT a privacy tier. Kept in its own column because
|
|
561
|
+
* sharing one cell forced an `else if` chain where a deprecated ZDR model
|
|
562
|
+
* showed only "deprecated" and its tier silently disappeared.
|
|
563
|
+
*/
|
|
564
|
+
const modelNotes = (m) => {
|
|
565
|
+
const parts = [];
|
|
435
566
|
if (m.deprecation) {
|
|
436
|
-
|
|
567
|
+
parts.push(m.deprecation.replaced_by
|
|
437
568
|
? `deprecated → ${m.deprecation.replaced_by}`
|
|
438
|
-
: "deprecated";
|
|
569
|
+
: "deprecated");
|
|
439
570
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
571
|
+
if (isImageGen(m))
|
|
572
|
+
parts.push("Image gen (/v1/images/generations)");
|
|
573
|
+
return parts.join(" · ");
|
|
574
|
+
};
|
|
575
|
+
const showNotes = shown.some((m) => modelNotes(m) !== "");
|
|
576
|
+
const line = (cells) => console.log(` ${cells.join(" ")}`.trimEnd());
|
|
577
|
+
line([
|
|
578
|
+
output.fmt.brandBold("Model ID".padEnd(COL.id)),
|
|
579
|
+
output.fmt.brandBold("Name".padEnd(COL.name)),
|
|
580
|
+
output.fmt.brandBold("Provider".padEnd(COL.provider)),
|
|
581
|
+
output.fmt.brandBold("Privacy Tier".padEnd(COL.privacy)),
|
|
582
|
+
...(showNotes ? [output.fmt.brandBold("Notes")] : []),
|
|
583
|
+
]);
|
|
584
|
+
// Measured, not guessed: a fixed fudge factor for the last column left the
|
|
585
|
+
// rule hanging well past the header once the tier list widened it.
|
|
586
|
+
const notesWidth = showNotes
|
|
587
|
+
? Math.max(...shown.map((m) => modelNotes(m).length))
|
|
588
|
+
: 0;
|
|
589
|
+
const rule = COL.id +
|
|
590
|
+
COL.name +
|
|
591
|
+
COL.provider +
|
|
592
|
+
COL.privacy +
|
|
593
|
+
3 +
|
|
594
|
+
(showNotes ? notesWidth + 1 : 0);
|
|
595
|
+
console.log(output.fmt.dim(` ${"─".repeat(rule)}`));
|
|
596
|
+
for (const m of shown) {
|
|
597
|
+
line([
|
|
598
|
+
output.fmt.brand(m.id.padEnd(COL.id)),
|
|
599
|
+
m.name.padEnd(COL.name),
|
|
600
|
+
output.fmt.dim(m.owned_by.padEnd(COL.provider)),
|
|
601
|
+
tierCell(m),
|
|
602
|
+
...(showNotes ? [output.fmt.dim(modelNotes(m))] : []),
|
|
603
|
+
]);
|
|
444
604
|
}
|
|
445
605
|
console.log();
|
|
446
606
|
output.dim(` Gateway: ${getLlmUrl()}`);
|
|
447
|
-
output.dim(` ${shown.length} model${shown.length === 1 ? "" : "s"}${
|
|
448
|
-
if (!
|
|
607
|
+
output.dim(` ${shown.length} model${shown.length === 1 ? "" : "s"}${view?.countSuffix ?? " available"}${live ? "" : " (cached)"}`);
|
|
608
|
+
if (!view && shown.some((m) => m.private)) {
|
|
449
609
|
output.dim(" Private (TEE) — append :private to the model id (e.g. glm-5.2:private). List: bankr llm models --private");
|
|
450
610
|
}
|
|
611
|
+
if (!view && shown.some((m) => m.zdr)) {
|
|
612
|
+
output.dim(" Zero data retention — append :zdr to the model id (e.g. glm-5.2:zdr), or turn it on account-wide under Settings in the web terminal. List: bankr llm models --zdr");
|
|
613
|
+
}
|
|
451
614
|
}
|
|
452
615
|
/* ─────────────────────── bankr llm credits ─────────────────────────────────── */
|
|
453
616
|
export async function creditsCommand() {
|
|
@@ -489,7 +652,7 @@ export async function creditsCommand() {
|
|
|
489
652
|
}
|
|
490
653
|
catch (err) {
|
|
491
654
|
spin.stop();
|
|
492
|
-
output.error(`Failed to fetch credits: ${err
|
|
655
|
+
output.error(`Failed to fetch credits: ${getErrorMessage(err)}`);
|
|
493
656
|
process.exit(1);
|
|
494
657
|
}
|
|
495
658
|
}
|
|
@@ -542,7 +705,7 @@ function resolveTokenAcrossChains(input, balances) {
|
|
|
542
705
|
symbol: nativeSym,
|
|
543
706
|
name: nativeSym,
|
|
544
707
|
decimals: 18,
|
|
545
|
-
balance:
|
|
708
|
+
balance: chainBal.nativeBalance || "0",
|
|
546
709
|
balanceUsd: nativeUsd,
|
|
547
710
|
});
|
|
548
711
|
}
|
|
@@ -609,7 +772,7 @@ export async function creditsAddCommand(amount, opts) {
|
|
|
609
772
|
catch (err) {
|
|
610
773
|
balSpin.stop();
|
|
611
774
|
// Non-fatal — proceed without balance display
|
|
612
|
-
output.dim(` (Could not fetch balance: ${err
|
|
775
|
+
output.dim(` (Could not fetch balance: ${getErrorMessage(err)})`);
|
|
613
776
|
}
|
|
614
777
|
}
|
|
615
778
|
// When --token is passed, the chain holding the highest USD balance of
|
|
@@ -624,7 +787,7 @@ export async function creditsAddCommand(amount, opts) {
|
|
|
624
787
|
}
|
|
625
788
|
catch (err) {
|
|
626
789
|
resolveSpin.stop();
|
|
627
|
-
output.warn(`Could not fetch balances: ${err
|
|
790
|
+
output.warn(`Could not fetch balances: ${getErrorMessage(err)}`);
|
|
628
791
|
}
|
|
629
792
|
if (!resolved) {
|
|
630
793
|
output.error(`Token "${opts.token}" not found in your wallet on any supported chain.`);
|
|
@@ -702,7 +865,7 @@ export async function creditsAddCommand(amount, opts) {
|
|
|
702
865
|
}
|
|
703
866
|
catch (err) {
|
|
704
867
|
spin.stop();
|
|
705
|
-
output.error(`Failed to add credits: ${err
|
|
868
|
+
output.error(`Failed to add credits: ${getErrorMessage(err)}`);
|
|
706
869
|
process.exit(1);
|
|
707
870
|
}
|
|
708
871
|
}
|
|
@@ -764,7 +927,7 @@ export async function creditsAutoCommand(opts) {
|
|
|
764
927
|
}
|
|
765
928
|
catch (err) {
|
|
766
929
|
spin.stop();
|
|
767
|
-
output.error(`Failed to fetch config: ${err
|
|
930
|
+
output.error(`Failed to fetch config: ${getErrorMessage(err)}`);
|
|
768
931
|
process.exit(1);
|
|
769
932
|
}
|
|
770
933
|
return;
|
|
@@ -889,7 +1052,7 @@ export async function creditsAutoCommand(opts) {
|
|
|
889
1052
|
}
|
|
890
1053
|
catch (err) {
|
|
891
1054
|
spin.stop();
|
|
892
|
-
output.error(`Failed to update config: ${err
|
|
1055
|
+
output.error(`Failed to update config: ${getErrorMessage(err)}`);
|
|
893
1056
|
process.exit(1);
|
|
894
1057
|
}
|
|
895
1058
|
}
|
|
@@ -898,13 +1061,31 @@ export async function setupOpenclawCommand(opts) {
|
|
|
898
1061
|
const llmKey = getLlmKey();
|
|
899
1062
|
const llmUrl = getLlmUrl();
|
|
900
1063
|
const { models } = await resolveModels();
|
|
901
|
-
|
|
1064
|
+
// Coding-tool config lists chat models only — image-only models can't chat.
|
|
1065
|
+
const activeModels = models.filter((m) => !m.deprecation && supportsChat(m));
|
|
1066
|
+
// --images routes OpenClaw's built-in image_generate tool through the gateway.
|
|
1067
|
+
// That tool only uses OpenClaw's known image providers (not custom ones like
|
|
1068
|
+
// `bankr`), so enabling it takes over the `openai` provider slot. OpenClaw's
|
|
1069
|
+
// OpenAI provider expects a `/v1`-suffixed base; normalize so a llmUrl that
|
|
1070
|
+
// already ends in /v1 (or a trailing slash) doesn't produce `/v1/v1`.
|
|
1071
|
+
const imageBaseUrl = `${llmUrl.replace(/\/+$/, "").replace(/\/v1$/, "")}/v1`;
|
|
1072
|
+
const imageProvider = opts.images
|
|
1073
|
+
? { baseUrl: imageBaseUrl, apiKey: llmKey ?? "${BANKR_API_KEY}" }
|
|
1074
|
+
: null;
|
|
1075
|
+
const imageGenerationModel = opts.images
|
|
1076
|
+
? { primary: "openai/gpt-image-2", timeoutMs: 180000 }
|
|
1077
|
+
: null;
|
|
1078
|
+
const warnImagesSlot = () => {
|
|
1079
|
+
if (opts.images)
|
|
1080
|
+
output.warn("--images points OpenClaw's `openai` provider at the gateway; image_generate (and any OpenAI-format request) now routes through Bankr. Skip --images if you use real OpenAI in OpenClaw.");
|
|
1081
|
+
};
|
|
902
1082
|
const providerConfig = {
|
|
903
1083
|
baseUrl: llmUrl,
|
|
904
1084
|
apiKey: llmKey ?? "${BANKR_API_KEY}",
|
|
905
1085
|
api: "openai-completions",
|
|
906
|
-
//
|
|
907
|
-
//
|
|
1086
|
+
// Models get a twin entry per privacy tier they support, so users can pick
|
|
1087
|
+
// one from the model dropdown in tools that expose no other configuration.
|
|
1088
|
+
// Base first, then weakest tier to strongest.
|
|
908
1089
|
models: activeModels.flatMap((m) => {
|
|
909
1090
|
const base = {
|
|
910
1091
|
id: m.id,
|
|
@@ -915,12 +1096,15 @@ export async function setupOpenclawCommand(opts) {
|
|
|
915
1096
|
maxTokens: m.maxTokens,
|
|
916
1097
|
cost: m.cost,
|
|
917
1098
|
};
|
|
918
|
-
return
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
{ ...base, id: `${m.id}:
|
|
922
|
-
|
|
923
|
-
|
|
1099
|
+
return [
|
|
1100
|
+
base,
|
|
1101
|
+
...(m.zdr
|
|
1102
|
+
? [{ ...base, id: `${m.id}:zdr`, name: `${m.name} (ZDR)` }]
|
|
1103
|
+
: []),
|
|
1104
|
+
...(m.private
|
|
1105
|
+
? [{ ...base, id: `${m.id}:private`, name: `${m.name} (Private)` }]
|
|
1106
|
+
: []),
|
|
1107
|
+
];
|
|
924
1108
|
}),
|
|
925
1109
|
};
|
|
926
1110
|
if (opts.install) {
|
|
@@ -929,17 +1113,43 @@ export async function setupOpenclawCommand(opts) {
|
|
|
929
1113
|
const models = existing.models ?? {};
|
|
930
1114
|
const providers = models.providers ?? {};
|
|
931
1115
|
providers.bankr = providerConfig;
|
|
1116
|
+
if (imageProvider) {
|
|
1117
|
+
providers.openai = {
|
|
1118
|
+
...(providers.openai ?? {}),
|
|
1119
|
+
...imageProvider,
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
932
1122
|
models.providers = providers;
|
|
933
1123
|
existing.models = models;
|
|
1124
|
+
if (imageGenerationModel) {
|
|
1125
|
+
const agents = existing.agents ?? {};
|
|
1126
|
+
const defaults = agents.defaults ?? {};
|
|
1127
|
+
defaults.imageGenerationModel = imageGenerationModel;
|
|
1128
|
+
agents.defaults = defaults;
|
|
1129
|
+
existing.agents = agents;
|
|
1130
|
+
}
|
|
934
1131
|
});
|
|
935
1132
|
warnIfNoKey(llmKey);
|
|
1133
|
+
warnImagesSlot();
|
|
936
1134
|
return;
|
|
937
1135
|
}
|
|
938
|
-
|
|
1136
|
+
const printed = {
|
|
1137
|
+
models: {
|
|
1138
|
+
providers: {
|
|
1139
|
+
bankr: providerConfig,
|
|
1140
|
+
...(imageProvider ? { openai: imageProvider } : {}),
|
|
1141
|
+
},
|
|
1142
|
+
},
|
|
1143
|
+
...(imageGenerationModel
|
|
1144
|
+
? { agents: { defaults: { imageGenerationModel } } }
|
|
1145
|
+
: {}),
|
|
1146
|
+
};
|
|
1147
|
+
console.log(JSON.stringify(printed, null, 2));
|
|
939
1148
|
console.log();
|
|
940
1149
|
if (!llmKey) {
|
|
941
1150
|
output.warn("No API key set. Replace ${BANKR_API_KEY} with your key or run: bankr login");
|
|
942
1151
|
}
|
|
1152
|
+
warnImagesSlot();
|
|
943
1153
|
output.dim("Add to ~/.openclaw/openclaw.json or run: bankr llm setup openclaw --install");
|
|
944
1154
|
}
|
|
945
1155
|
/* ──────────────────────── bankr llm setup opencode ─────────────────────────── */
|
|
@@ -947,11 +1157,15 @@ export async function setupOpenCodeCommand(opts) {
|
|
|
947
1157
|
const llmKey = getLlmKey();
|
|
948
1158
|
const llmUrl = getLlmUrl();
|
|
949
1159
|
const { models } = await resolveModels();
|
|
950
|
-
|
|
1160
|
+
// Coding-tool config lists chat models only — image-only models can't chat.
|
|
1161
|
+
const activeModels = models.filter((m) => !m.deprecation && supportsChat(m));
|
|
951
1162
|
const modelsObj = {};
|
|
952
1163
|
for (const m of activeModels) {
|
|
953
1164
|
modelsObj[m.id] = { name: m.name };
|
|
954
|
-
//
|
|
1165
|
+
// One variant per privacy tier the model supports, weakest to strongest.
|
|
1166
|
+
if (m.zdr) {
|
|
1167
|
+
modelsObj[`${m.id}:zdr`] = { name: `${m.name} (ZDR)` };
|
|
1168
|
+
}
|
|
955
1169
|
if (m.private) {
|
|
956
1170
|
modelsObj[`${m.id}:private`] = { name: `${m.name} (Private)` };
|
|
957
1171
|
}
|
|
@@ -997,7 +1211,7 @@ export async function setupCursorCommand() {
|
|
|
997
1211
|
const { models } = await resolveModels();
|
|
998
1212
|
const activeModels = models.filter((m) => !m.deprecation);
|
|
999
1213
|
// Pick one model per provider as recommended examples
|
|
1000
|
-
const recommendedIds = ["claude-
|
|
1214
|
+
const recommendedIds = ["claude-fable-5", "gemini-3.1-pro", "gpt-5.5"];
|
|
1001
1215
|
const recommended = recommendedIds.filter((id) => activeModels.some((m) => m.id === id));
|
|
1002
1216
|
output.brandBold("Cursor — Bankr LLM Gateway");
|
|
1003
1217
|
console.log();
|
|
@@ -1104,6 +1318,9 @@ function translateClaudeModelArgs(args) {
|
|
|
1104
1318
|
return out;
|
|
1105
1319
|
}
|
|
1106
1320
|
export async function claudeCommand(args) {
|
|
1321
|
+
if (isHelpRequest(args)) {
|
|
1322
|
+
return launchTool("claude", args, {}, "https://docs.anthropic.com/en/docs/claude-code");
|
|
1323
|
+
}
|
|
1107
1324
|
const llmKey = requireAuth();
|
|
1108
1325
|
const llmUrl = getLlmUrl();
|
|
1109
1326
|
const translated = translateClaudeModelArgs(args);
|
|
@@ -1112,6 +1329,9 @@ export async function claudeCommand(args) {
|
|
|
1112
1329
|
}
|
|
1113
1330
|
/* ────────────────────────── bankr llm opencode ───────────────────────────── */
|
|
1114
1331
|
export async function opencodeCommand(args) {
|
|
1332
|
+
if (isHelpRequest(args)) {
|
|
1333
|
+
return launchTool("opencode", args, {}, "https://opencode.ai");
|
|
1334
|
+
}
|
|
1115
1335
|
const llmKey = requireAuth();
|
|
1116
1336
|
const llmUrl = getLlmUrl();
|
|
1117
1337
|
const configFile = join(homedir(), ".config", "opencode", "opencode.json");
|
package/dist/commands/login.d.ts
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Both Privy credentials minted by the OTP exchange. The identity token alone
|
|
3
|
+
* authenticates, but only the ACCESS token carries the session `sid` claim —
|
|
4
|
+
* so sending it too is what makes a CLI login show up in Active Sessions and
|
|
5
|
+
* be killable by "log out this device". Keep them paired; never send just one.
|
|
6
|
+
*/
|
|
7
|
+
interface CliAuthTokens {
|
|
8
|
+
identityToken: string;
|
|
9
|
+
accessToken: string;
|
|
10
|
+
}
|
|
11
|
+
/** The auth headers every authenticated CLI call sends — one place, so a new
|
|
12
|
+
* endpoint can't silently omit the access token and go unrevocable again. */
|
|
13
|
+
export declare function authHeaders(tokens: CliAuthTokens): Record<string, string>;
|
|
1
14
|
export declare function loginCommand(opts: {
|
|
2
15
|
apiUrl?: string;
|
|
3
16
|
apiKey?: string;
|
|
@@ -16,4 +29,5 @@ export declare function loginCommand(opts: {
|
|
|
16
29
|
allowedIps?: string;
|
|
17
30
|
allowedRecipients?: string;
|
|
18
31
|
}): Promise<void>;
|
|
32
|
+
export {};
|
|
19
33
|
//# sourceMappingURL=login.d.ts.map
|