@bankr/cli 0.3.13 → 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 +28 -7
- package/dist/commands/balances.js +2 -1
- package/dist/commands/cancel.js +2 -1
- package/dist/commands/fees.js +65 -40
- package/dist/commands/launch.d.ts +4 -0
- package/dist/commands/launch.js +42 -5
- package/dist/commands/llm.d.ts +2 -0
- package/dist/commands/llm.js +245 -43
- 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 +6 -2
- package/dist/commands/whoami.js +3 -2
- package/dist/commands/x402.d.ts +3 -0
- package/dist/commands/x402.js +63 -19
- package/dist/lib/api.d.ts +13 -1
- package/dist/lib/api.js +32 -41
- 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
|
@@ -2,6 +2,7 @@ import chalk from "chalk";
|
|
|
2
2
|
import { getPortfolio, } from "../lib/api.js";
|
|
3
3
|
import * as output from "../lib/output.js";
|
|
4
4
|
import { formatUsd, formatBalance } from "../lib/output.js";
|
|
5
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
5
6
|
import { CHAIN_LABELS, VALID_CHAINS } from "../lib/chains.js";
|
|
6
7
|
const BRAND = chalk.hex("#FF613D");
|
|
7
8
|
const DIM = chalk.dim;
|
|
@@ -78,7 +79,7 @@ export async function portfolioCommand(opts) {
|
|
|
78
79
|
spin.succeed("Portfolio loaded");
|
|
79
80
|
}
|
|
80
81
|
catch (err) {
|
|
81
|
-
spin.fail(`Failed to fetch portfolio: ${err
|
|
82
|
+
spin.fail(`Failed to fetch portfolio: ${getErrorMessage(err)}`);
|
|
82
83
|
process.exit(1);
|
|
83
84
|
}
|
|
84
85
|
// JSON output mode
|
|
@@ -109,7 +110,7 @@ export async function portfolioCommand(opts) {
|
|
|
109
110
|
printChainBalances(chain, chainData, !!includePnl);
|
|
110
111
|
}
|
|
111
112
|
// Print NFTs
|
|
112
|
-
if (includeNfts && data.nfts
|
|
113
|
+
if (includeNfts && data.nfts?.length) {
|
|
113
114
|
console.log();
|
|
114
115
|
console.log();
|
|
115
116
|
console.log(` ${BRAND.bold("NFTs")} ${DIM(`${data.nfts.length} item(s)`)}`);
|
package/dist/commands/profile.js
CHANGED
|
@@ -2,6 +2,7 @@ import chalk from "chalk";
|
|
|
2
2
|
import { input, confirm, select } from "@inquirer/prompts";
|
|
3
3
|
import { getOwnProfiles, createProfile, updateProfile, deleteProfile, addProjectUpdate, } from "../lib/api.js";
|
|
4
4
|
import * as output from "../lib/output.js";
|
|
5
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
5
6
|
const BRAND = chalk.hex("#FF613D");
|
|
6
7
|
const DIM = chalk.dim;
|
|
7
8
|
function printProfile(profile, json) {
|
|
@@ -74,7 +75,7 @@ export async function profileViewCommand(opts) {
|
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
catch (err) {
|
|
77
|
-
const msg = err
|
|
78
|
+
const msg = getErrorMessage(err);
|
|
78
79
|
if (msg.includes("404")) {
|
|
79
80
|
output.info("No profiles found. Create one with: bankr profile create");
|
|
80
81
|
}
|
|
@@ -132,7 +133,7 @@ export async function profileCreateCommand(opts) {
|
|
|
132
133
|
printProfile(profile, !!opts.json);
|
|
133
134
|
}
|
|
134
135
|
catch (err) {
|
|
135
|
-
output.error(err
|
|
136
|
+
output.error(getErrorMessage(err));
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
139
|
export async function profileUpdateCommand(opts) {
|
|
@@ -163,7 +164,7 @@ export async function profileUpdateCommand(opts) {
|
|
|
163
164
|
printProfile(profile, !!opts.json);
|
|
164
165
|
}
|
|
165
166
|
catch (err) {
|
|
166
|
-
output.error(err
|
|
167
|
+
output.error(getErrorMessage(err));
|
|
167
168
|
}
|
|
168
169
|
}
|
|
169
170
|
export async function profileDeleteCommand(opts) {
|
|
@@ -187,7 +188,7 @@ export async function profileDeleteCommand(opts) {
|
|
|
187
188
|
output.success("Profile deleted.");
|
|
188
189
|
}
|
|
189
190
|
catch (err) {
|
|
190
|
-
output.error(err
|
|
191
|
+
output.error(getErrorMessage(err));
|
|
191
192
|
}
|
|
192
193
|
}
|
|
193
194
|
export async function profileAddUpdateCommand(opts) {
|
|
@@ -221,7 +222,7 @@ export async function profileAddUpdateCommand(opts) {
|
|
|
221
222
|
}
|
|
222
223
|
}
|
|
223
224
|
catch (err) {
|
|
224
|
-
output.error(err
|
|
225
|
+
output.error(getErrorMessage(err));
|
|
225
226
|
}
|
|
226
227
|
}
|
|
227
228
|
//# sourceMappingURL=profile.js.map
|
package/dist/commands/prompt.js
CHANGED
|
@@ -2,11 +2,13 @@ import { ApiError, pollJob, submitPrompt } from "../lib/api.js";
|
|
|
2
2
|
import { emitCESP } from "../lib/cesp/engine.js";
|
|
3
3
|
import { getLastThreadId, requireApiKey, setLastThreadId, } from "../lib/config.js";
|
|
4
4
|
import * as output from "../lib/output.js";
|
|
5
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
5
6
|
/** Valid Max Mode model IDs — hardcoded because CLI is standalone (no monorepo imports).
|
|
6
7
|
* Must be manually kept in sync with models in the LLM gateway DB. */
|
|
7
8
|
const VALID_MAX_MODE_MODELS = new Set([
|
|
8
9
|
// Claude (Vertex AI)
|
|
9
10
|
"claude-fable-5",
|
|
11
|
+
"claude-opus-5",
|
|
10
12
|
"claude-opus-4.8",
|
|
11
13
|
"claude-opus-4.7",
|
|
12
14
|
"claude-opus-4.6",
|
|
@@ -15,28 +17,39 @@ const VALID_MAX_MODE_MODELS = new Set([
|
|
|
15
17
|
"claude-sonnet-4.6",
|
|
16
18
|
"claude-sonnet-4.5",
|
|
17
19
|
"claude-haiku-4.5",
|
|
18
|
-
// Gemini (Vertex AI)
|
|
20
|
+
// Gemini (Vertex AI) — gemini-3-pro deliberately absent: removed 2026-03-09,
|
|
21
|
+
// both provider slugs retired. Don't re-add on a sync pass.
|
|
22
|
+
"gemini-3.6-flash",
|
|
23
|
+
"gemini-3.5-flash-lite",
|
|
19
24
|
"gemini-3.1-pro",
|
|
20
|
-
"gemini-3-pro",
|
|
21
25
|
"gemini-2.5-pro",
|
|
22
26
|
// OpenAI (OpenRouter)
|
|
27
|
+
"gpt-5.6-sol",
|
|
28
|
+
"gpt-5.6-terra",
|
|
29
|
+
"gpt-5.6-luna",
|
|
30
|
+
"gpt-5.5",
|
|
23
31
|
"gpt-5.4",
|
|
24
32
|
"gpt-5.4-mini",
|
|
25
33
|
"gpt-5.2",
|
|
26
34
|
"gpt-5-mini",
|
|
27
35
|
// Grok (OpenRouter)
|
|
28
|
-
"grok-4.
|
|
36
|
+
"grok-4.6",
|
|
37
|
+
"grok-4.5",
|
|
38
|
+
"grok-4.3",
|
|
29
39
|
// DeepSeek (OpenRouter)
|
|
30
40
|
"deepseek-v3.2",
|
|
31
41
|
// Qwen (OpenRouter)
|
|
32
42
|
"qwen3-coder",
|
|
43
|
+
"qwen3.8-max",
|
|
33
44
|
"qwen3.7-max",
|
|
34
45
|
"qwen3.7-plus",
|
|
46
|
+
"qwen3.7-flash",
|
|
35
47
|
"qwen3.5-plus",
|
|
36
48
|
// MiniMax (direct)
|
|
37
49
|
"minimax-m2.5",
|
|
38
50
|
"minimax-m2.7",
|
|
39
51
|
// Other (OpenRouter)
|
|
52
|
+
"kimi-k3",
|
|
40
53
|
"kimi-k2.7-code",
|
|
41
54
|
"kimi-k2.6",
|
|
42
55
|
"kimi-k2.5",
|
|
@@ -95,7 +108,7 @@ export async function promptCommand(text, options = {}) {
|
|
|
95
108
|
}
|
|
96
109
|
}
|
|
97
110
|
else {
|
|
98
|
-
output.error(err
|
|
111
|
+
output.error(getErrorMessage(err));
|
|
99
112
|
}
|
|
100
113
|
process.exit(1);
|
|
101
114
|
}
|
|
@@ -121,7 +134,7 @@ export async function promptCommand(text, options = {}) {
|
|
|
121
134
|
if (result.response) {
|
|
122
135
|
console.log(result.response);
|
|
123
136
|
}
|
|
124
|
-
if (result.transactions
|
|
137
|
+
if (result.transactions?.length) {
|
|
125
138
|
console.log();
|
|
126
139
|
output.dim(`${result.transactions.length} transaction(s) returned. Use the web app to review and execute.`);
|
|
127
140
|
}
|
|
@@ -141,7 +154,7 @@ export async function promptCommand(text, options = {}) {
|
|
|
141
154
|
catch (err) {
|
|
142
155
|
spin.fail("Error while polling");
|
|
143
156
|
emitCESP("resource.limit");
|
|
144
|
-
output.error(err
|
|
157
|
+
output.error(getErrorMessage(err));
|
|
145
158
|
process.exit(1);
|
|
146
159
|
}
|
|
147
160
|
}
|
package/dist/commands/sign.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { sign } from "../lib/api.js";
|
|
2
2
|
import { emitCESP } from "../lib/cesp/engine.js";
|
|
3
3
|
import * as output from "../lib/output.js";
|
|
4
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
4
5
|
export async function signCommand(opts) {
|
|
5
6
|
const request = {
|
|
6
7
|
signatureType: opts.type,
|
|
@@ -63,7 +64,7 @@ export async function signCommand(opts) {
|
|
|
63
64
|
catch (err) {
|
|
64
65
|
spinner.stop();
|
|
65
66
|
emitCESP("task.error");
|
|
66
|
-
output.error(err
|
|
67
|
+
output.error(getErrorMessage(err));
|
|
67
68
|
process.exit(1);
|
|
68
69
|
}
|
|
69
70
|
}
|
package/dist/commands/sounds.js
CHANGED
|
@@ -6,6 +6,7 @@ import { tmpdir } from "node:os";
|
|
|
6
6
|
import { readConfig, writeConfig } from "../lib/config.js";
|
|
7
7
|
import { DEFAULT_SOUND_CONFIG, emitCESP, getPacksDir, listInstalledPacks, } from "../lib/cesp/engine.js";
|
|
8
8
|
import * as output from "../lib/output.js";
|
|
9
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
9
10
|
const execFileAsync = promisify(execFile);
|
|
10
11
|
const REGISTRY_URL = "https://peonping.github.io/registry/index.json";
|
|
11
12
|
function getSoundConfig() {
|
|
@@ -164,7 +165,7 @@ export async function soundsInstallCommand(packName) {
|
|
|
164
165
|
}
|
|
165
166
|
catch (err) {
|
|
166
167
|
spin.fail("Failed to fetch registry");
|
|
167
|
-
output.error(err
|
|
168
|
+
output.error(getErrorMessage(err));
|
|
168
169
|
process.exit(1);
|
|
169
170
|
}
|
|
170
171
|
// Download and extract
|
|
@@ -218,7 +219,7 @@ export async function soundsInstallCommand(packName) {
|
|
|
218
219
|
}
|
|
219
220
|
catch (err) {
|
|
220
221
|
spin2.fail("Install failed");
|
|
221
|
-
output.error(err
|
|
222
|
+
output.error(getErrorMessage(err));
|
|
222
223
|
process.exit(1);
|
|
223
224
|
}
|
|
224
225
|
}
|
|
@@ -232,7 +233,7 @@ export async function soundsSearchCommand(query) {
|
|
|
232
233
|
}
|
|
233
234
|
catch (err) {
|
|
234
235
|
spin.fail("Failed to fetch registry");
|
|
235
|
-
output.error(err
|
|
236
|
+
output.error(getErrorMessage(err));
|
|
236
237
|
process.exit(1);
|
|
237
238
|
}
|
|
238
239
|
// Filter by query if provided
|
package/dist/commands/status.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getJobStatus } from "../lib/api.js";
|
|
2
2
|
import { requireApiKey } from "../lib/config.js";
|
|
3
3
|
import * as output from "../lib/output.js";
|
|
4
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
4
5
|
export async function statusCommand(jobId) {
|
|
5
6
|
requireApiKey();
|
|
6
7
|
const spin = output.spinner("Fetching job status...");
|
|
@@ -31,14 +32,14 @@ export async function statusCommand(jobId) {
|
|
|
31
32
|
console.log();
|
|
32
33
|
output.error(status.error);
|
|
33
34
|
}
|
|
34
|
-
if (status.transactions
|
|
35
|
+
if (status.transactions?.length) {
|
|
35
36
|
console.log();
|
|
36
37
|
output.dim(`${status.transactions.length} transaction(s) attached.`);
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
catch (err) {
|
|
40
41
|
spin.fail("Failed to fetch job status");
|
|
41
|
-
output.error(err
|
|
42
|
+
output.error(getErrorMessage(err));
|
|
42
43
|
process.exit(1);
|
|
43
44
|
}
|
|
44
45
|
}
|
package/dist/commands/submit.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { submit } from "../lib/api.js";
|
|
2
2
|
import { emitCESP } from "../lib/cesp/engine.js";
|
|
3
3
|
import * as output from "../lib/output.js";
|
|
4
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
4
5
|
function displayResult(result) {
|
|
5
6
|
if (!result.success) {
|
|
6
7
|
emitCESP("task.error");
|
|
@@ -73,7 +74,7 @@ export async function submitCommand(opts) {
|
|
|
73
74
|
}
|
|
74
75
|
catch (err) {
|
|
75
76
|
spinner.stop();
|
|
76
|
-
output.error(err
|
|
77
|
+
output.error(getErrorMessage(err));
|
|
77
78
|
process.exit(1);
|
|
78
79
|
}
|
|
79
80
|
}
|
|
@@ -127,7 +128,7 @@ export async function submitJsonCommand(json, opts) {
|
|
|
127
128
|
}
|
|
128
129
|
catch (err) {
|
|
129
130
|
spinner.stop();
|
|
130
|
-
output.error(err
|
|
131
|
+
output.error(getErrorMessage(err));
|
|
131
132
|
process.exit(1);
|
|
132
133
|
}
|
|
133
134
|
}
|
package/dist/commands/swap.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { getSwapQuote, executeSwap } from "../lib/api.js";
|
|
2
2
|
import * as output from "../lib/output.js";
|
|
3
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
3
4
|
import { NATIVE_SYMBOLS, VALID_CHAINS } from "../lib/chains.js";
|
|
4
|
-
import {
|
|
5
|
+
import { zeroAddress } from "viem";
|
|
6
|
+
import { resolveTokenSymbol } from "../lib/tokens.js";
|
|
5
7
|
/**
|
|
6
8
|
* Resolve a token symbol or address to a contract address for swapping.
|
|
7
9
|
* - The chain's native ticker (ETH/POL/BNB) or the zero address → native, sent
|
|
@@ -12,10 +14,10 @@ async function resolveSwapToken(tokenInput, chain) {
|
|
|
12
14
|
const input = tokenInput.trim();
|
|
13
15
|
const nativeSymbol = NATIVE_SYMBOLS[chain] ?? "ETH";
|
|
14
16
|
const upper = input.toUpperCase();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
// viem's zeroAddress is already lowercase, so compare against the lowercased input.
|
|
18
|
+
const isNative = upper === nativeSymbol.toUpperCase() || input.toLowerCase() === zeroAddress;
|
|
17
19
|
if (isNative) {
|
|
18
|
-
return { address:
|
|
20
|
+
return { address: zeroAddress, symbol: nativeSymbol };
|
|
19
21
|
}
|
|
20
22
|
return resolveTokenSymbol(input, chain);
|
|
21
23
|
}
|
|
@@ -45,7 +47,7 @@ export async function swapCommand(opts) {
|
|
|
45
47
|
resolveSpin.stop();
|
|
46
48
|
}
|
|
47
49
|
catch (err) {
|
|
48
|
-
resolveSpin.fail(err
|
|
50
|
+
resolveSpin.fail(getErrorMessage(err));
|
|
49
51
|
process.exit(1);
|
|
50
52
|
}
|
|
51
53
|
// Quote
|
|
@@ -62,7 +64,7 @@ export async function swapCommand(opts) {
|
|
|
62
64
|
quoteSpin.stop();
|
|
63
65
|
}
|
|
64
66
|
catch (err) {
|
|
65
|
-
quoteSpin.fail(`Quote failed: ${err
|
|
67
|
+
quoteSpin.fail(`Quote failed: ${getErrorMessage(err)}`);
|
|
66
68
|
process.exit(1);
|
|
67
69
|
}
|
|
68
70
|
output.blank();
|
|
@@ -100,7 +102,7 @@ export async function swapCommand(opts) {
|
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
104
|
catch (err) {
|
|
103
|
-
swapSpin.fail(`Swap failed: ${err
|
|
105
|
+
swapSpin.fail(`Swap failed: ${getErrorMessage(err)}`);
|
|
104
106
|
process.exit(1);
|
|
105
107
|
}
|
|
106
108
|
}
|
package/dist/commands/tokens.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { searchTokens } from "../lib/api.js";
|
|
2
2
|
import * as output from "../lib/output.js";
|
|
3
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
3
4
|
export async function tokensSearchCommand(query, opts) {
|
|
4
5
|
const spin = output.spinner(`Searching tokens for "${query}"...`);
|
|
5
6
|
try {
|
|
@@ -22,7 +23,7 @@ export async function tokensSearchCommand(query, opts) {
|
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
catch (err) {
|
|
25
|
-
spin.fail(`Search failed: ${err
|
|
26
|
+
spin.fail(`Search failed: ${getErrorMessage(err)}`);
|
|
26
27
|
process.exit(1);
|
|
27
28
|
}
|
|
28
29
|
}
|
|
@@ -53,7 +54,7 @@ export async function tokensInfoCommand(address, opts) {
|
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
catch (err) {
|
|
56
|
-
spin.fail(`Failed to fetch token info: ${err
|
|
57
|
+
spin.fail(`Failed to fetch token info: ${getErrorMessage(err)}`);
|
|
57
58
|
process.exit(1);
|
|
58
59
|
}
|
|
59
60
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { transfer, resolveRecipient } from "../lib/api.js";
|
|
2
2
|
import * as output from "../lib/output.js";
|
|
3
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
3
4
|
import { NATIVE_SYMBOLS } from "../lib/chains.js";
|
|
4
|
-
import { isAddress } from "viem";
|
|
5
|
-
import {
|
|
5
|
+
import { isAddress, zeroAddress } from "viem";
|
|
6
|
+
import { resolveTokenSymbol } from "../lib/tokens.js";
|
|
6
7
|
function looksLikeEnsName(value) {
|
|
7
8
|
const v = value.trim().toLowerCase();
|
|
8
9
|
return v.endsWith(".eth") || v.endsWith(".cb.id");
|
|
@@ -30,7 +31,7 @@ async function resolveTransferRecipient(raw) {
|
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
33
|
catch (err) {
|
|
33
|
-
spin.fail(`ENS resolution failed for "${value}": ${err
|
|
34
|
+
spin.fail(`ENS resolution failed for "${value}": ${getErrorMessage(err)}`);
|
|
34
35
|
process.exit(1);
|
|
35
36
|
}
|
|
36
37
|
}
|
|
@@ -47,7 +48,8 @@ export async function transferCommand(opts) {
|
|
|
47
48
|
let tokenAddress;
|
|
48
49
|
let tokenLabel;
|
|
49
50
|
if (isNative) {
|
|
50
|
-
|
|
51
|
+
// Zero address = native-token sentinel; the API detects + sanitizes it.
|
|
52
|
+
tokenAddress = zeroAddress;
|
|
51
53
|
tokenLabel = NATIVE_SYMBOLS[chain] ?? "ETH";
|
|
52
54
|
}
|
|
53
55
|
else {
|
|
@@ -64,7 +66,7 @@ export async function transferCommand(opts) {
|
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
catch (err) {
|
|
67
|
-
spin.fail(err
|
|
69
|
+
spin.fail(getErrorMessage(err));
|
|
68
70
|
process.exit(1);
|
|
69
71
|
}
|
|
70
72
|
}
|
|
@@ -86,7 +88,7 @@ export async function transferCommand(opts) {
|
|
|
86
88
|
output.label("Tx Hash", result.txHash ?? "unknown");
|
|
87
89
|
}
|
|
88
90
|
catch (err) {
|
|
89
|
-
spin.fail(`Transfer failed: ${err
|
|
91
|
+
spin.fail(`Transfer failed: ${getErrorMessage(err)}`);
|
|
90
92
|
process.exit(1);
|
|
91
93
|
}
|
|
92
94
|
}
|
package/dist/commands/update.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { execSync } from "node:child_process";
|
|
3
3
|
import * as output from "../lib/output.js";
|
|
4
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
4
5
|
const PACKAGE_NAME = "@bankr/cli";
|
|
5
6
|
/**
|
|
6
7
|
* Compare two semver version strings.
|
|
@@ -79,7 +80,7 @@ export async function updateCommand(opts) {
|
|
|
79
80
|
}
|
|
80
81
|
catch (err) {
|
|
81
82
|
spin.fail();
|
|
82
|
-
output.error(err
|
|
83
|
+
output.error(getErrorMessage(err));
|
|
83
84
|
process.exit(1);
|
|
84
85
|
}
|
|
85
86
|
const cmp = compareSemver(current, latest);
|
|
@@ -508,7 +508,9 @@ export async function webhooksListCommand() {
|
|
|
508
508
|
const status = w.status === "active"
|
|
509
509
|
? output.fmt.success("active")
|
|
510
510
|
: output.fmt.warn(w.status);
|
|
511
|
-
const mode = w.readOnly
|
|
511
|
+
const mode = w.readOnly
|
|
512
|
+
? output.fmt.dim("read-only")
|
|
513
|
+
: output.fmt.warn("writes");
|
|
512
514
|
console.log(` ${output.fmt.brand(w.name)} ${status} v${w.version} ${mode} ${w.totalInvocations} invocations`);
|
|
513
515
|
if (w.description)
|
|
514
516
|
output.dim(` ${w.description}`);
|
|
@@ -564,7 +566,9 @@ export async function webhooksDeleteCommand(name) {
|
|
|
564
566
|
export async function webhooksLogsCommand(name) {
|
|
565
567
|
const spin = output.spinner(`Fetching invocations for ${name}...`);
|
|
566
568
|
try {
|
|
567
|
-
const res = await fetch(`${getApiUrl()}/webhooks/${name}/logs?limit=25`, {
|
|
569
|
+
const res = await fetch(`${getApiUrl()}/webhooks/${name}/logs?limit=25`, {
|
|
570
|
+
headers: authHeaders(),
|
|
571
|
+
});
|
|
568
572
|
const result = await handleResponse(res);
|
|
569
573
|
spin.stop();
|
|
570
574
|
if (result.logs.length === 0) {
|
package/dist/commands/whoami.js
CHANGED
|
@@ -2,6 +2,7 @@ import { existsSync } from "fs";
|
|
|
2
2
|
import { getApiKey, getApiUrl, getConfigPath, isCustomApiUrl, } from "../lib/config.js";
|
|
3
3
|
import { getUserInfo } from "../lib/api.js";
|
|
4
4
|
import * as output from "../lib/output.js";
|
|
5
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
5
6
|
export async function whoamiCommand() {
|
|
6
7
|
const apiKey = getApiKey();
|
|
7
8
|
const apiUrl = getApiUrl();
|
|
@@ -35,7 +36,7 @@ export async function whoamiCommand() {
|
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
catch (err) {
|
|
38
|
-
spin.fail(`Could not reach Bankr API: ${err
|
|
39
|
+
spin.fail(`Could not reach Bankr API: ${getErrorMessage(err)}`);
|
|
39
40
|
return;
|
|
40
41
|
}
|
|
41
42
|
// Fetch user profile info
|
|
@@ -69,7 +70,7 @@ export async function whoamiCommand() {
|
|
|
69
70
|
output.label("Score", `${score}${rankStr}`);
|
|
70
71
|
}
|
|
71
72
|
catch (err) {
|
|
72
|
-
profileSpin.fail(`Could not fetch account info: ${err
|
|
73
|
+
profileSpin.fail(`Could not fetch account info: ${getErrorMessage(err)}`);
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
//# sourceMappingURL=whoami.js.map
|
package/dist/commands/x402.d.ts
CHANGED
|
@@ -62,6 +62,9 @@ export declare function x402EnvUnsetCommand(key: string): Promise<void>;
|
|
|
62
62
|
export declare function x402SchemaCommand(url: string, opts?: {
|
|
63
63
|
raw?: boolean;
|
|
64
64
|
}): Promise<void>;
|
|
65
|
+
export declare function checkAdvertisedPrice(advertisedUsd: number | null, callerCapUsd: number): string | null;
|
|
66
|
+
export declare function parseAdvertisedPrice(price: string, currency: string): number | null;
|
|
67
|
+
export declare function outboundMaxPayment(callerCapUsd: number, advertisedUsd: number | null): number;
|
|
65
68
|
export declare function x402CallCommand(url: string, opts?: {
|
|
66
69
|
method?: string;
|
|
67
70
|
body?: string;
|
package/dist/commands/x402.js
CHANGED
|
@@ -27,6 +27,12 @@ const CONFIG_FILENAME = "bankr.x402.json";
|
|
|
27
27
|
function findProjectRoot() {
|
|
28
28
|
return process.cwd();
|
|
29
29
|
}
|
|
30
|
+
// Config values come from user-edited JSON cast without validation, so fields
|
|
31
|
+
// feeding select() defaults are plain strings; narrow them to the offered
|
|
32
|
+
// choices (select's default must match the literal union of choice values).
|
|
33
|
+
function pickDefault(value, allowed) {
|
|
34
|
+
return allowed.find((v) => v === value);
|
|
35
|
+
}
|
|
30
36
|
function loadConfigFile(projectRoot) {
|
|
31
37
|
const candidates = [
|
|
32
38
|
join(projectRoot, CONFIG_FILENAME),
|
|
@@ -238,9 +244,7 @@ export default async function handler(req: Request): Promise<Response> {
|
|
|
238
244
|
};
|
|
239
245
|
// Add to config
|
|
240
246
|
let config = loadConfigFile(root);
|
|
241
|
-
|
|
242
|
-
config = { services: {} };
|
|
243
|
-
}
|
|
247
|
+
config || (config = { services: {} });
|
|
244
248
|
config.services[name] = {
|
|
245
249
|
price: "0.001",
|
|
246
250
|
description: `${name} service`,
|
|
@@ -258,9 +262,7 @@ export default async function handler(req: Request): Promise<Response> {
|
|
|
258
262
|
export async function x402ConfigureCommand(name) {
|
|
259
263
|
const root = findProjectRoot();
|
|
260
264
|
let config = loadConfigFile(root);
|
|
261
|
-
|
|
262
|
-
config = { services: {} };
|
|
263
|
-
}
|
|
265
|
+
config || (config = { services: {} });
|
|
264
266
|
const existing = config.services[name] ?? { price: "0.001" };
|
|
265
267
|
const description = await input({
|
|
266
268
|
message: "Service description:",
|
|
@@ -273,12 +275,14 @@ export async function x402ConfigureCommand(name) {
|
|
|
273
275
|
theme: output.bankrTheme,
|
|
274
276
|
});
|
|
275
277
|
const currency = "USDC";
|
|
278
|
+
const networkDefault = pickDefault(existing.network, ["base"]) ?? "base";
|
|
276
279
|
const network = await select({
|
|
277
280
|
message: "Network:",
|
|
278
281
|
choices: [{ name: "Base", value: "base" }],
|
|
279
|
-
default:
|
|
282
|
+
default: networkDefault,
|
|
280
283
|
theme: output.bankrTheme,
|
|
281
284
|
});
|
|
285
|
+
const methodDefault = pickDefault(existing.methods?.[0], ["GET", "POST", "*"]) ?? "GET";
|
|
282
286
|
const method = await select({
|
|
283
287
|
message: "HTTP method:",
|
|
284
288
|
choices: [
|
|
@@ -286,7 +290,7 @@ export async function x402ConfigureCommand(name) {
|
|
|
286
290
|
{ name: "POST — JSON body", value: "POST" },
|
|
287
291
|
{ name: "Any — accept all methods", value: "*" },
|
|
288
292
|
],
|
|
289
|
-
default:
|
|
293
|
+
default: methodDefault,
|
|
290
294
|
theme: output.bankrTheme,
|
|
291
295
|
});
|
|
292
296
|
const paymentScheme = await select({
|
|
@@ -328,9 +332,7 @@ export async function x402DeployCommand(name) {
|
|
|
328
332
|
}
|
|
329
333
|
// Load or bootstrap config
|
|
330
334
|
let config = loadConfigFile(root);
|
|
331
|
-
|
|
332
|
-
config = { services: {} };
|
|
333
|
-
}
|
|
335
|
+
config || (config = { services: {} });
|
|
334
336
|
// Discover services
|
|
335
337
|
const { readdirSync, statSync } = await import("node:fs");
|
|
336
338
|
const entries = readdirSync(x402Dir);
|
|
@@ -999,6 +1001,32 @@ async function fetchSchemaForInteractive(url) {
|
|
|
999
1001
|
return null;
|
|
1000
1002
|
}
|
|
1001
1003
|
// ── Call ──────────────────────────────────────────────────────────────
|
|
1004
|
+
// The caller's --max-payment is the only value ever sent as the payment cap;
|
|
1005
|
+
// an endpoint-advertised price may never raise it — fail closed instead.
|
|
1006
|
+
// Exported for tests.
|
|
1007
|
+
export function checkAdvertisedPrice(advertisedUsd, callerCapUsd) {
|
|
1008
|
+
if (advertisedUsd !== null && advertisedUsd > callerCapUsd) {
|
|
1009
|
+
return `Endpoint requires $${advertisedUsd} but --max-payment is $${callerCapUsd}. Re-run with a higher --max-payment (up to 10) to allow it.`;
|
|
1010
|
+
}
|
|
1011
|
+
return null;
|
|
1012
|
+
}
|
|
1013
|
+
// Advertised prices that are non-positive, unparseable, or token-denominated
|
|
1014
|
+
// (non-USD currency) are treated as unknown: only a USD value can safely feed
|
|
1015
|
+
// the USD cap clamp or the fail-closed comparison. Exported for tests.
|
|
1016
|
+
export function parseAdvertisedPrice(price, currency) {
|
|
1017
|
+
const upper = currency.toUpperCase();
|
|
1018
|
+
if (upper !== "USDC" && upper !== "USD")
|
|
1019
|
+
return null;
|
|
1020
|
+
const parsed = parseFloat(price);
|
|
1021
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
1022
|
+
}
|
|
1023
|
+
// The cap we send: never above the caller's --max-payment, and never above the
|
|
1024
|
+
// advertised price the user was shown and confirmed. Exported for tests.
|
|
1025
|
+
export function outboundMaxPayment(callerCapUsd, advertisedUsd) {
|
|
1026
|
+
return advertisedUsd !== null
|
|
1027
|
+
? Math.min(advertisedUsd, callerCapUsd)
|
|
1028
|
+
: callerCapUsd;
|
|
1029
|
+
}
|
|
1002
1030
|
export async function x402CallCommand(url, opts = {}) {
|
|
1003
1031
|
if (!url) {
|
|
1004
1032
|
output.error("Usage: bankr x402 call <url> [--method POST] [--body '{}']");
|
|
@@ -1011,7 +1039,8 @@ export async function x402CallCommand(url, opts = {}) {
|
|
|
1011
1039
|
output.error("--max-payment must be between 0 and 10 (USD)");
|
|
1012
1040
|
process.exit(1);
|
|
1013
1041
|
}
|
|
1014
|
-
//
|
|
1042
|
+
// Endpoint-advertised price. Shown at confirm time; the outbound cap is
|
|
1043
|
+
// clamped to min(advertised, caller's maxPaymentUsd) — see outboundMaxPayment.
|
|
1015
1044
|
let resolvedPrice = null;
|
|
1016
1045
|
if (opts.interactive) {
|
|
1017
1046
|
const schema = await fetchSchemaForInteractive(url);
|
|
@@ -1025,7 +1054,12 @@ export async function x402CallCommand(url, opts = {}) {
|
|
|
1025
1054
|
console.log(` ${chalk.dim("Price")} ${chalk.green(`$${schema.price} ${schema.currency}`)}`);
|
|
1026
1055
|
console.log();
|
|
1027
1056
|
// Use the endpoint's actual price and preferred method
|
|
1028
|
-
resolvedPrice =
|
|
1057
|
+
resolvedPrice = parseAdvertisedPrice(schema.price, schema.currency);
|
|
1058
|
+
const capError = checkAdvertisedPrice(resolvedPrice, maxPaymentUsd);
|
|
1059
|
+
if (capError) {
|
|
1060
|
+
output.error(capError);
|
|
1061
|
+
process.exit(1);
|
|
1062
|
+
}
|
|
1029
1063
|
if (schema.methods.length > 0) {
|
|
1030
1064
|
method = schema.methods.includes("POST") ? "POST" : schema.methods[0];
|
|
1031
1065
|
}
|
|
@@ -1041,7 +1075,7 @@ export async function x402CallCommand(url, opts = {}) {
|
|
|
1041
1075
|
: `${key} ${chalk.dim(`(${typedProp.type}, optional)`)}`;
|
|
1042
1076
|
const hint = typedProp.description ?? "";
|
|
1043
1077
|
// Enum fields → select prompt
|
|
1044
|
-
if (typedProp.enum
|
|
1078
|
+
if (typedProp.enum?.length) {
|
|
1045
1079
|
const selected = await select({
|
|
1046
1080
|
message: `${label}${hint ? ` — ${chalk.dim(hint)}` : ""}`,
|
|
1047
1081
|
choices: typedProp.enum.map((v) => ({
|
|
@@ -1098,6 +1132,7 @@ export async function x402CallCommand(url, opts = {}) {
|
|
|
1098
1132
|
// No schema available — fallback: prompt for method and body
|
|
1099
1133
|
output.warn("No schema found for this endpoint. Falling back to manual input.");
|
|
1100
1134
|
console.log();
|
|
1135
|
+
const manualMethodDefault = pickDefault(method, ["GET", "POST", "PUT", "DELETE"]) ?? "GET";
|
|
1101
1136
|
method = await select({
|
|
1102
1137
|
message: "HTTP method",
|
|
1103
1138
|
choices: [
|
|
@@ -1106,7 +1141,7 @@ export async function x402CallCommand(url, opts = {}) {
|
|
|
1106
1141
|
{ name: "PUT", value: "PUT" },
|
|
1107
1142
|
{ name: "DELETE", value: "DELETE" },
|
|
1108
1143
|
],
|
|
1109
|
-
default:
|
|
1144
|
+
default: manualMethodDefault,
|
|
1110
1145
|
theme: output.bankrTheme,
|
|
1111
1146
|
});
|
|
1112
1147
|
if (method === "POST" || method === "PUT") {
|
|
@@ -1144,13 +1179,22 @@ export async function x402CallCommand(url, opts = {}) {
|
|
|
1144
1179
|
}
|
|
1145
1180
|
console.log();
|
|
1146
1181
|
}
|
|
1147
|
-
//
|
|
1148
|
-
|
|
1182
|
+
// Probe the endpoint to show the actual price before confirming. Skipped in
|
|
1183
|
+
// non-interactive mode, where the confirm is suppressed — --ni matches --yes.
|
|
1184
|
+
if (!opts.interactive &&
|
|
1185
|
+
!opts.yes &&
|
|
1186
|
+
!output.isNonInteractive() &&
|
|
1187
|
+
resolvedPrice === null) {
|
|
1149
1188
|
const spin = output.spinner("Checking endpoint price...");
|
|
1150
1189
|
const schema = await fetchSchemaForInteractive(url);
|
|
1151
1190
|
spin.stop();
|
|
1152
1191
|
if (schema) {
|
|
1153
|
-
resolvedPrice =
|
|
1192
|
+
resolvedPrice = parseAdvertisedPrice(schema.price, schema.currency);
|
|
1193
|
+
const capError = checkAdvertisedPrice(resolvedPrice, maxPaymentUsd);
|
|
1194
|
+
if (capError) {
|
|
1195
|
+
output.error(capError);
|
|
1196
|
+
process.exit(1);
|
|
1197
|
+
}
|
|
1154
1198
|
}
|
|
1155
1199
|
}
|
|
1156
1200
|
if (!opts.yes && !opts.interactive && !output.isNonInteractive()) {
|
|
@@ -1185,7 +1229,7 @@ export async function x402CallCommand(url, opts = {}) {
|
|
|
1185
1229
|
url,
|
|
1186
1230
|
method,
|
|
1187
1231
|
body: requestBody,
|
|
1188
|
-
maxPaymentUsd:
|
|
1232
|
+
maxPaymentUsd: outboundMaxPayment(maxPaymentUsd, resolvedPrice),
|
|
1189
1233
|
}),
|
|
1190
1234
|
});
|
|
1191
1235
|
const result = await handleResponse(res);
|