@bankr/cli 0.1.3 → 0.2.0
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/dist/cli.js +379 -133
- package/dist/commands/balances.d.ts +1 -0
- package/dist/commands/balances.js +7 -32
- package/dist/commands/llm.js +47 -3
- package/dist/commands/login.d.ts +5 -0
- package/dist/commands/login.js +139 -33
- package/dist/commands/portfolio.d.ts +9 -0
- package/dist/commands/portfolio.js +134 -0
- package/dist/commands/tokens.d.ts +7 -0
- package/dist/commands/tokens.js +60 -0
- package/dist/commands/transfer.d.ts +8 -0
- package/dist/commands/transfer.js +80 -0
- package/dist/commands/update.d.ts +4 -0
- package/dist/commands/update.js +116 -0
- package/dist/lib/api.d.ts +59 -2
- package/dist/lib/api.js +34 -8
- package/dist/lib/chains.d.ts +5 -0
- package/dist/lib/chains.js +35 -0
- package/dist/lib/output.d.ts +2 -0
- package/dist/lib/output.js +17 -0
- package/package.json +1 -1
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import * as output from "../lib/output.js";
|
|
4
|
+
const PACKAGE_NAME = "@bankr/cli";
|
|
5
|
+
/**
|
|
6
|
+
* Compare two semver version strings.
|
|
7
|
+
* Returns -1 if a < b, 0 if equal, 1 if a > b.
|
|
8
|
+
*/
|
|
9
|
+
function compareSemver(a, b) {
|
|
10
|
+
const partsA = a.split(".").map(Number);
|
|
11
|
+
const partsB = b.split(".").map(Number);
|
|
12
|
+
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
|
13
|
+
const numA = partsA[i] ?? 0;
|
|
14
|
+
const numB = partsB[i] ?? 0;
|
|
15
|
+
if (numA < numB)
|
|
16
|
+
return -1;
|
|
17
|
+
if (numA > numB)
|
|
18
|
+
return 1;
|
|
19
|
+
}
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
async function fetchLatestVersion() {
|
|
23
|
+
const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, {
|
|
24
|
+
signal: AbortSignal.timeout(10000),
|
|
25
|
+
});
|
|
26
|
+
if (!res.ok) {
|
|
27
|
+
throw new Error(`Failed to check for updates (HTTP ${res.status})`);
|
|
28
|
+
}
|
|
29
|
+
const data = (await res.json());
|
|
30
|
+
return data.version;
|
|
31
|
+
}
|
|
32
|
+
function getCurrentVersion() {
|
|
33
|
+
const pkg = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf-8"));
|
|
34
|
+
return pkg.version;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Detect which package manager installed the CLI globally.
|
|
38
|
+
* Falls back to npm if we can't determine.
|
|
39
|
+
*/
|
|
40
|
+
function detectPackageManager() {
|
|
41
|
+
try {
|
|
42
|
+
const execPath = process.argv[1] ?? "";
|
|
43
|
+
// Check if running under bun
|
|
44
|
+
if (execPath.includes("/.bun/") || "bun" in process.versions) {
|
|
45
|
+
return "bun";
|
|
46
|
+
}
|
|
47
|
+
// Check if running under pnpm
|
|
48
|
+
if (execPath.includes("/pnpm/") || execPath.includes("/.pnpm/")) {
|
|
49
|
+
return "pnpm";
|
|
50
|
+
}
|
|
51
|
+
// Check if running under yarn
|
|
52
|
+
if (execPath.includes("/yarn/") || execPath.includes("/.yarn/")) {
|
|
53
|
+
return "yarn";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// ignore detection errors
|
|
58
|
+
}
|
|
59
|
+
return "npm";
|
|
60
|
+
}
|
|
61
|
+
function getInstallCommand(pm) {
|
|
62
|
+
switch (pm) {
|
|
63
|
+
case "bun":
|
|
64
|
+
return `bun install -g ${PACKAGE_NAME}@latest`;
|
|
65
|
+
case "pnpm":
|
|
66
|
+
return `pnpm add -g ${PACKAGE_NAME}@latest`;
|
|
67
|
+
case "yarn":
|
|
68
|
+
return `yarn global add ${PACKAGE_NAME}@latest`;
|
|
69
|
+
default:
|
|
70
|
+
return `npm install -g ${PACKAGE_NAME}@latest`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export async function updateCommand(opts) {
|
|
74
|
+
const current = getCurrentVersion();
|
|
75
|
+
const spin = output.spinner("Checking for updates...");
|
|
76
|
+
let latest;
|
|
77
|
+
try {
|
|
78
|
+
latest = await fetchLatestVersion();
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
spin.fail();
|
|
82
|
+
output.error(err.message);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
const cmp = compareSemver(current, latest);
|
|
86
|
+
if (cmp === 0) {
|
|
87
|
+
spin.succeed(`Already on the latest version (${current})`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (cmp > 0) {
|
|
91
|
+
spin.succeed(`You're ahead of latest (${current} > ${latest}). No update needed.`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
spin.succeed(`Update available: ${current} → ${latest}`);
|
|
95
|
+
if (opts.check) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const pm = detectPackageManager();
|
|
99
|
+
const cmd = getInstallCommand(pm);
|
|
100
|
+
output.blank();
|
|
101
|
+
output.info(`Updating via ${pm}...`);
|
|
102
|
+
output.dim(` $ ${cmd}`);
|
|
103
|
+
output.blank();
|
|
104
|
+
try {
|
|
105
|
+
execSync(cmd, { stdio: "inherit" });
|
|
106
|
+
output.blank();
|
|
107
|
+
output.success(`Updated to ${PACKAGE_NAME}@${latest}`);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
output.blank();
|
|
111
|
+
output.error("Update failed. Try running manually:");
|
|
112
|
+
output.dim(` $ ${cmd}`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=update.js.map
|
package/dist/lib/api.d.ts
CHANGED
|
@@ -61,7 +61,29 @@ export interface TokenBalanceInfo {
|
|
|
61
61
|
imgUrl: string;
|
|
62
62
|
decimals: number;
|
|
63
63
|
};
|
|
64
|
+
pnl?: {
|
|
65
|
+
realizedPnl: number;
|
|
66
|
+
unrealizedPnl: number;
|
|
67
|
+
totalPnl: number;
|
|
68
|
+
averageEntryPrice: number;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export interface NftInfo {
|
|
73
|
+
name?: string;
|
|
74
|
+
tokenId: string;
|
|
75
|
+
collection?: {
|
|
76
|
+
name: string;
|
|
77
|
+
address: string;
|
|
78
|
+
nftStandard?: string;
|
|
64
79
|
};
|
|
80
|
+
chain: string;
|
|
81
|
+
chainName?: string;
|
|
82
|
+
image?: {
|
|
83
|
+
thumbnail?: string;
|
|
84
|
+
original?: string;
|
|
85
|
+
};
|
|
86
|
+
openSeaUrl?: string;
|
|
65
87
|
}
|
|
66
88
|
export interface ChainBalanceInfo {
|
|
67
89
|
nativeBalance: string;
|
|
@@ -69,13 +91,23 @@ export interface ChainBalanceInfo {
|
|
|
69
91
|
tokenBalances: TokenBalanceInfo[];
|
|
70
92
|
total: string;
|
|
71
93
|
}
|
|
72
|
-
export interface
|
|
94
|
+
export interface PortfolioResponse {
|
|
73
95
|
success: boolean;
|
|
74
96
|
evmAddress: string;
|
|
75
97
|
solAddress?: string;
|
|
76
98
|
balances: Record<string, ChainBalanceInfo>;
|
|
99
|
+
nfts?: NftInfo[];
|
|
100
|
+
}
|
|
101
|
+
export interface PortfolioOptions {
|
|
102
|
+
chains?: string[];
|
|
103
|
+
showLowValueTokens?: boolean;
|
|
104
|
+
include?: string[];
|
|
77
105
|
}
|
|
78
|
-
export declare function
|
|
106
|
+
export declare function getPortfolio(opts?: PortfolioOptions): Promise<PortfolioResponse>;
|
|
107
|
+
/** @deprecated Use PortfolioResponse instead */
|
|
108
|
+
export type BalancesResponse = PortfolioResponse;
|
|
109
|
+
/** @deprecated Use getPortfolio instead */
|
|
110
|
+
export declare const getBalances: (chains?: string[], showLowValueTokens?: boolean) => Promise<PortfolioResponse>;
|
|
79
111
|
export declare function pollJob(jobId: string, opts?: {
|
|
80
112
|
interval?: number;
|
|
81
113
|
maxAttempts?: number;
|
|
@@ -324,5 +356,30 @@ export declare function addProjectUpdate(data: {
|
|
|
324
356
|
title: string;
|
|
325
357
|
content: string;
|
|
326
358
|
}): Promise<AgentProfileResponse>;
|
|
359
|
+
export interface TokenSearchResult {
|
|
360
|
+
address: string;
|
|
361
|
+
symbol: string;
|
|
362
|
+
name: string;
|
|
363
|
+
decimals: number;
|
|
364
|
+
logoURI?: string;
|
|
365
|
+
priceUsd?: number;
|
|
366
|
+
}
|
|
367
|
+
export interface TokenSearchResponse {
|
|
368
|
+
tokens: TokenSearchResult[];
|
|
369
|
+
}
|
|
370
|
+
export declare function searchTokens(query: string, chainId?: number): Promise<TokenSearchResponse>;
|
|
371
|
+
export interface TransferRequest {
|
|
372
|
+
tokenAddress: string;
|
|
373
|
+
recipientAddress: string;
|
|
374
|
+
amount: string;
|
|
375
|
+
isNativeToken: boolean;
|
|
376
|
+
chain?: string;
|
|
377
|
+
}
|
|
378
|
+
export interface TransferResponse {
|
|
379
|
+
success: boolean;
|
|
380
|
+
txHash?: string;
|
|
381
|
+
error?: string;
|
|
382
|
+
}
|
|
383
|
+
export declare function transfer(request: TransferRequest): Promise<TransferResponse>;
|
|
327
384
|
export declare function buildPublicClaimTxs(beneficiaryAddress: string, tokenAddresses: string[]): Promise<BuildClaimResponse>;
|
|
328
385
|
//# sourceMappingURL=api.d.ts.map
|
package/dist/lib/api.js
CHANGED
|
@@ -62,18 +62,25 @@ export async function validateApiKey() {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
export async function getUserInfo() {
|
|
65
|
-
const res = await fetch(`${getApiUrl()}/
|
|
65
|
+
const res = await fetch(`${getApiUrl()}/wallet/me`, {
|
|
66
66
|
headers: authHeaders(),
|
|
67
67
|
});
|
|
68
68
|
return handleResponse(res);
|
|
69
69
|
}
|
|
70
|
-
export async function
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
export async function getPortfolio(opts = {}) {
|
|
71
|
+
const query = new URLSearchParams();
|
|
72
|
+
if (opts.chains?.length)
|
|
73
|
+
query.set("chains", opts.chains.join(","));
|
|
74
|
+
if (opts.showLowValueTokens)
|
|
75
|
+
query.set("showLowValueTokens", "true");
|
|
76
|
+
if (opts.include?.length)
|
|
77
|
+
query.set("include", opts.include.join(","));
|
|
78
|
+
const qs = query.toString();
|
|
79
|
+
const res = await fetch(`${getApiUrl()}/wallet/portfolio${qs ? `?${qs}` : ""}`, { headers: authHeaders() });
|
|
75
80
|
return handleResponse(res);
|
|
76
81
|
}
|
|
82
|
+
/** @deprecated Use getPortfolio instead */
|
|
83
|
+
export const getBalances = (chains, showLowValueTokens) => getPortfolio({ chains, showLowValueTokens });
|
|
77
84
|
export async function pollJob(jobId, opts = {}) {
|
|
78
85
|
const { interval = 2000, maxAttempts = 150, onStatus } = opts;
|
|
79
86
|
let attempts = 0;
|
|
@@ -89,7 +96,7 @@ export async function pollJob(jobId, opts = {}) {
|
|
|
89
96
|
throw new Error(`Polling timed out after ${maxAttempts} attempts`);
|
|
90
97
|
}
|
|
91
98
|
export async function sign(request) {
|
|
92
|
-
const res = await fetch(`${getApiUrl()}/
|
|
99
|
+
const res = await fetch(`${getApiUrl()}/wallet/sign`, {
|
|
93
100
|
method: "POST",
|
|
94
101
|
headers: authHeaders(),
|
|
95
102
|
body: JSON.stringify(request),
|
|
@@ -97,7 +104,7 @@ export async function sign(request) {
|
|
|
97
104
|
return handleResponse(res);
|
|
98
105
|
}
|
|
99
106
|
export async function submit(request) {
|
|
100
|
-
const res = await fetch(`${getApiUrl()}/
|
|
107
|
+
const res = await fetch(`${getApiUrl()}/wallet/submit`, {
|
|
101
108
|
method: "POST",
|
|
102
109
|
headers: authHeaders(),
|
|
103
110
|
body: JSON.stringify(request),
|
|
@@ -254,6 +261,25 @@ export async function addProjectUpdate(data) {
|
|
|
254
261
|
});
|
|
255
262
|
return handleResponse(res);
|
|
256
263
|
}
|
|
264
|
+
export async function searchTokens(query, chainId) {
|
|
265
|
+
const params = new URLSearchParams({ query });
|
|
266
|
+
if (chainId)
|
|
267
|
+
params.set("chainId", String(chainId));
|
|
268
|
+
const res = await fetch(`${getApiUrl()}/tokens/search?${params}`, {
|
|
269
|
+
headers: {
|
|
270
|
+
"User-Agent": CLI_USER_AGENT,
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
return handleResponse(res);
|
|
274
|
+
}
|
|
275
|
+
export async function transfer(request) {
|
|
276
|
+
const res = await fetch(`${getApiUrl()}/wallet/transfer`, {
|
|
277
|
+
method: "POST",
|
|
278
|
+
headers: authHeaders(),
|
|
279
|
+
body: JSON.stringify(request),
|
|
280
|
+
});
|
|
281
|
+
return handleResponse(res);
|
|
282
|
+
}
|
|
257
283
|
export async function buildPublicClaimTxs(beneficiaryAddress, tokenAddresses) {
|
|
258
284
|
const res = await fetch(`${getApiUrl()}/public/doppler/build-claim`, {
|
|
259
285
|
method: "POST",
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const CHAIN_LABELS = {
|
|
2
|
+
base: "Base",
|
|
3
|
+
polygon: "Polygon",
|
|
4
|
+
mainnet: "Ethereum",
|
|
5
|
+
unichain: "Unichain",
|
|
6
|
+
worldchain: "World Chain",
|
|
7
|
+
arbitrum: "Arbitrum",
|
|
8
|
+
solana: "Solana",
|
|
9
|
+
};
|
|
10
|
+
export const VALID_CHAINS = new Set([
|
|
11
|
+
"base",
|
|
12
|
+
"polygon",
|
|
13
|
+
"mainnet",
|
|
14
|
+
"unichain",
|
|
15
|
+
"worldchain",
|
|
16
|
+
"arbitrum",
|
|
17
|
+
"solana",
|
|
18
|
+
]);
|
|
19
|
+
export const CHAIN_IDS = {
|
|
20
|
+
base: 8453,
|
|
21
|
+
mainnet: 1,
|
|
22
|
+
polygon: 137,
|
|
23
|
+
unichain: 130,
|
|
24
|
+
worldchain: 480,
|
|
25
|
+
arbitrum: 42161,
|
|
26
|
+
};
|
|
27
|
+
export const NATIVE_SYMBOLS = {
|
|
28
|
+
base: "ETH",
|
|
29
|
+
mainnet: "ETH",
|
|
30
|
+
unichain: "ETH",
|
|
31
|
+
worldchain: "ETH",
|
|
32
|
+
arbitrum: "ETH",
|
|
33
|
+
polygon: "POL",
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=chains.js.map
|
package/dist/lib/output.d.ts
CHANGED
|
@@ -29,5 +29,7 @@ export declare function keyValue(key: string, value: string): void;
|
|
|
29
29
|
export declare function spinner(text: string): Ora;
|
|
30
30
|
export declare function maskApiKey(key: string): string;
|
|
31
31
|
export declare function formatDuration(ms: number): string;
|
|
32
|
+
export declare function formatUsd(value: string | number): string;
|
|
33
|
+
export declare function formatBalance(value: string | number, decimals?: number): string;
|
|
32
34
|
export declare function formatStatus(status: string): string;
|
|
33
35
|
//# sourceMappingURL=output.d.ts.map
|
package/dist/lib/output.js
CHANGED
|
@@ -66,6 +66,23 @@ export function formatDuration(ms) {
|
|
|
66
66
|
return `${ms}ms`;
|
|
67
67
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
68
68
|
}
|
|
69
|
+
export function formatUsd(value) {
|
|
70
|
+
const num = typeof value === "string" ? parseFloat(value) : value;
|
|
71
|
+
if (isNaN(num) || num === 0)
|
|
72
|
+
return chalk.dim("$0.00");
|
|
73
|
+
return `$${num.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
74
|
+
}
|
|
75
|
+
export function formatBalance(value, decimals = 6) {
|
|
76
|
+
const num = typeof value === "string" ? parseFloat(value) : value;
|
|
77
|
+
if (isNaN(num) || num === 0)
|
|
78
|
+
return "0";
|
|
79
|
+
if (num < 0.000001)
|
|
80
|
+
return "<0.000001";
|
|
81
|
+
return num.toLocaleString("en-US", {
|
|
82
|
+
minimumFractionDigits: 0,
|
|
83
|
+
maximumFractionDigits: decimals,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
69
86
|
export function formatStatus(status) {
|
|
70
87
|
switch (status) {
|
|
71
88
|
case "completed":
|