@bankr/cli 0.1.0-beta.9 → 0.1.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.
Files changed (64) hide show
  1. package/README.md +18 -0
  2. package/dist/cli.js +200 -3
  3. package/dist/commands/balances.d.ts +5 -0
  4. package/dist/commands/balances.js +113 -0
  5. package/dist/commands/fees.d.ts +18 -0
  6. package/dist/commands/fees.js +793 -0
  7. package/dist/commands/launch.d.ts +13 -0
  8. package/dist/commands/launch.js +174 -0
  9. package/dist/commands/llm.d.ts +11 -0
  10. package/dist/commands/llm.js +319 -3
  11. package/dist/commands/login.d.ts +3 -0
  12. package/dist/commands/login.js +94 -4
  13. package/dist/commands/profile.d.ts +26 -0
  14. package/dist/commands/profile.js +183 -0
  15. package/dist/commands/prompt.js +5 -0
  16. package/dist/commands/sign.js +3 -0
  17. package/dist/commands/sounds.d.ts +12 -0
  18. package/dist/commands/sounds.js +262 -0
  19. package/dist/commands/submit.js +14 -4
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.js +1 -1
  22. package/dist/lib/api.d.ts +213 -0
  23. package/dist/lib/api.js +177 -3
  24. package/dist/lib/cesp/engine.d.ts +13 -0
  25. package/dist/lib/cesp/engine.js +132 -0
  26. package/dist/lib/cesp/player.d.ts +6 -0
  27. package/dist/lib/cesp/player.js +50 -0
  28. package/dist/lib/cesp/types.d.ts +38 -0
  29. package/dist/lib/cesp/types.js +2 -0
  30. package/dist/lib/config.d.ts +4 -0
  31. package/dist/lib/config.js +1 -0
  32. package/package.json +4 -2
  33. package/dist/cli.d.ts.map +0 -1
  34. package/dist/cli.js.map +0 -1
  35. package/dist/commands/cancel.d.ts.map +0 -1
  36. package/dist/commands/cancel.js.map +0 -1
  37. package/dist/commands/capabilities.d.ts.map +0 -1
  38. package/dist/commands/capabilities.js.map +0 -1
  39. package/dist/commands/config.d.ts.map +0 -1
  40. package/dist/commands/config.js.map +0 -1
  41. package/dist/commands/llm.d.ts.map +0 -1
  42. package/dist/commands/llm.js.map +0 -1
  43. package/dist/commands/login.d.ts.map +0 -1
  44. package/dist/commands/login.js.map +0 -1
  45. package/dist/commands/logout.d.ts.map +0 -1
  46. package/dist/commands/logout.js.map +0 -1
  47. package/dist/commands/prompt.d.ts.map +0 -1
  48. package/dist/commands/prompt.js.map +0 -1
  49. package/dist/commands/sign.d.ts.map +0 -1
  50. package/dist/commands/sign.js.map +0 -1
  51. package/dist/commands/status.d.ts.map +0 -1
  52. package/dist/commands/status.js.map +0 -1
  53. package/dist/commands/submit.d.ts.map +0 -1
  54. package/dist/commands/submit.js.map +0 -1
  55. package/dist/commands/whoami.d.ts.map +0 -1
  56. package/dist/commands/whoami.js.map +0 -1
  57. package/dist/index.d.ts.map +0 -1
  58. package/dist/index.js.map +0 -1
  59. package/dist/lib/api.d.ts.map +0 -1
  60. package/dist/lib/api.js.map +0 -1
  61. package/dist/lib/config.d.ts.map +0 -1
  62. package/dist/lib/config.js.map +0 -1
  63. package/dist/lib/output.d.ts.map +0 -1
  64. package/dist/lib/output.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import { confirm, input, select } from "@inquirer/prompts";
2
2
  import open from "open";
3
- import { DEFAULT_API_URL, getApiUrl, getConfigPath, readConfig, writeConfig, } from "../lib/config.js";
3
+ import { CLI_USER_AGENT, DEFAULT_API_URL, getApiUrl, getConfigPath, readConfig, writeConfig, } from "../lib/config.js";
4
4
  import * as output from "../lib/output.js";
5
5
  const DEFAULT_DASHBOARD_URL = "https://bankr.bot/api";
6
6
  const MAX_OTP_ATTEMPTS = 3;
@@ -16,7 +16,9 @@ function deriveDashboardUrl(apiUrl) {
16
16
  return (apiUrl.replace(/\/+$/, "").replace("api.", "").replace("api-", "") + "/api");
17
17
  }
18
18
  async function fetchPrivyConfig(apiUrl) {
19
- const res = await fetch(`${apiUrl}/cli/config`);
19
+ const res = await fetch(`${apiUrl}/cli/config`, {
20
+ headers: { "User-Agent": CLI_USER_AGENT },
21
+ });
20
22
  if (!res.ok) {
21
23
  throw new Error(`Failed to fetch auth config (${res.status})`);
22
24
  }
@@ -66,6 +68,7 @@ async function callGenerateWallet(apiUrl, identityToken) {
66
68
  method: "POST",
67
69
  headers: {
68
70
  "Content-Type": "application/json",
71
+ "User-Agent": CLI_USER_AGENT,
69
72
  "privy-id-token": identityToken,
70
73
  },
71
74
  });
@@ -80,6 +83,7 @@ async function callAcceptTerms(apiUrl, identityToken) {
80
83
  method: "POST",
81
84
  headers: {
82
85
  "Content-Type": "application/json",
86
+ "User-Agent": CLI_USER_AGENT,
83
87
  "privy-id-token": identityToken,
84
88
  },
85
89
  });
@@ -93,6 +97,7 @@ async function callGenerateApiKey(apiUrl, identityToken, opts) {
93
97
  method: "POST",
94
98
  headers: {
95
99
  "Content-Type": "application/json",
100
+ "User-Agent": CLI_USER_AGENT,
96
101
  "privy-id-token": identityToken,
97
102
  },
98
103
  body: JSON.stringify(opts),
@@ -288,12 +293,77 @@ async function emailLoginFlow(apiUrl, opts) {
288
293
  output.dim(" Manage keys at bankr.bot/api");
289
294
  return apiKeyResult.apiKey;
290
295
  }
296
+ // ── SIWE login flow ─────────────────────────────────────────────────
297
+ async function siweLoginFlow(apiUrl, opts) {
298
+ // Dynamic import to avoid loading viem for non-SIWE flows
299
+ const { privateKeyToAccount } = await import("viem/accounts");
300
+ const account = privateKeyToAccount(opts.privateKey);
301
+ // 1. Fetch nonce
302
+ const nonceSpinner = output.spinner("Fetching SIWE nonce...");
303
+ const nonceRes = await fetch(`${apiUrl}/cli/siwe/nonce`, {
304
+ headers: { "User-Agent": CLI_USER_AGENT },
305
+ });
306
+ if (!nonceRes.ok) {
307
+ nonceSpinner.fail("Failed to fetch nonce");
308
+ fatal(`Nonce request failed (${nonceRes.status})`);
309
+ }
310
+ const { nonce } = (await nonceRes.json());
311
+ nonceSpinner.succeed("Nonce received");
312
+ // 2. Create and sign SIWE message
313
+ const domain = new URL(apiUrl).host;
314
+ const message = [
315
+ `${domain} wants you to sign in with your Ethereum account:`,
316
+ account.address,
317
+ "",
318
+ "Sign in to Bankr",
319
+ "",
320
+ `URI: ${apiUrl}/cli/siwe/verify`,
321
+ `Version: 1`,
322
+ `Chain ID: 1`,
323
+ `Nonce: ${nonce}`,
324
+ `Issued At: ${new Date().toISOString()}`,
325
+ ].join("\n");
326
+ const signSpinner = output.spinner("Signing SIWE message...");
327
+ const signature = await account.signMessage({ message });
328
+ signSpinner.succeed("Message signed");
329
+ // 3. Verify with API
330
+ const verifySpinner = output.spinner("Verifying and creating wallet...");
331
+ const verifyRes = await fetch(`${apiUrl}/cli/siwe/verify`, {
332
+ method: "POST",
333
+ headers: {
334
+ "Content-Type": "application/json",
335
+ "User-Agent": CLI_USER_AGENT,
336
+ },
337
+ body: JSON.stringify({
338
+ message,
339
+ signature,
340
+ partnerApiKey: opts.partnerKey,
341
+ keyName: opts.keyName ?? `SIWE-${new Date().toISOString().slice(0, 10)}`,
342
+ readOnly: opts.readOnly ?? false,
343
+ }),
344
+ });
345
+ if (!verifyRes.ok) {
346
+ verifySpinner.fail("SIWE verification failed");
347
+ const body = (await verifyRes.json().catch(() => ({})));
348
+ fatal(body.message || `Verification failed (${verifyRes.status})`);
349
+ }
350
+ const result = (await verifyRes.json());
351
+ verifySpinner.succeed("Wallet created");
352
+ output.blank();
353
+ output.success("SIWE authentication successful");
354
+ output.dim(` EVM: ${result.walletAddress}`);
355
+ if (result.solAddress) {
356
+ output.dim(` SOL: ${result.solAddress}`);
357
+ }
358
+ output.dim(` Mode: ${result.readOnly ? "read-only" : "read-write"}`);
359
+ return { apiKey: result.apiKey, walletAddress: result.walletAddress };
360
+ }
291
361
  // ── API key validation helper ────────────────────────────────────────
292
362
  async function validateApiKey(apiUrl, apiKey) {
293
363
  const spin = output.spinner("Validating Bankr API key...");
294
364
  try {
295
365
  const res = await fetch(`${apiUrl}/agent/me`, {
296
- headers: { "X-API-Key": apiKey },
366
+ headers: { "X-API-Key": apiKey, "User-Agent": CLI_USER_AGENT },
297
367
  });
298
368
  if (res.ok) {
299
369
  spin.succeed("Bankr API key validated successfully");
@@ -337,7 +407,8 @@ export async function loginCommand(opts) {
337
407
  if (resolvedApiUrl !== DEFAULT_API_URL) {
338
408
  const nonInteractive = !!(opts.apiKey ||
339
409
  opts.url ||
340
- opts.email !== undefined);
410
+ opts.email !== undefined ||
411
+ opts.siwe);
341
412
  if (nonInteractive) {
342
413
  config.apiUrl = resolvedApiUrl;
343
414
  }
@@ -373,6 +444,25 @@ export async function loginCommand(opts) {
373
444
  output.success(`Credentials saved to ${getConfigPath()}`);
374
445
  output.dim(`Bankr API URL: ${apiUrl}`);
375
446
  }
447
+ // --siwe: SIWE login flow (headless agent onboarding)
448
+ if (opts.siwe) {
449
+ if (!opts.privateKey) {
450
+ fatal("--private-key is required with --siwe");
451
+ }
452
+ const { apiKey } = await siweLoginFlow(apiUrl, {
453
+ privateKey: opts.privateKey,
454
+ partnerKey: opts.partnerKey,
455
+ keyName: opts.keyName,
456
+ readOnly: !opts.readWrite,
457
+ });
458
+ config.apiKey = apiKey;
459
+ if (opts.partnerKey) {
460
+ config.partnerKey = opts.partnerKey;
461
+ }
462
+ writeConfig(config);
463
+ output.success(`Credentials saved to ${getConfigPath()}`);
464
+ return;
465
+ }
376
466
  // --url: print the dashboard URL and exit
377
467
  if (opts.url) {
378
468
  const dashboardUrl = deriveDashboardUrl(apiUrl);
@@ -0,0 +1,26 @@
1
+ export declare function profileViewCommand(opts: {
2
+ json?: boolean;
3
+ }): Promise<void>;
4
+ export declare function profileCreateCommand(opts: {
5
+ name?: string;
6
+ description?: string;
7
+ token?: string;
8
+ image?: string;
9
+ website?: string;
10
+ json?: boolean;
11
+ }): Promise<void>;
12
+ export declare function profileUpdateCommand(opts: {
13
+ name?: string;
14
+ description?: string;
15
+ token?: string;
16
+ image?: string;
17
+ website?: string;
18
+ json?: boolean;
19
+ }): Promise<void>;
20
+ export declare function profileDeleteCommand(): Promise<void>;
21
+ export declare function profileAddUpdateCommand(opts: {
22
+ title?: string;
23
+ content?: string;
24
+ json?: boolean;
25
+ }): Promise<void>;
26
+ //# sourceMappingURL=profile.d.ts.map
@@ -0,0 +1,183 @@
1
+ import chalk from "chalk";
2
+ import { input, confirm } from "@inquirer/prompts";
3
+ import { getOwnProfile, createProfile, updateProfile, deleteProfile, addProjectUpdate, } from "../lib/api.js";
4
+ import * as output from "../lib/output.js";
5
+ const BRAND = chalk.hex("#FF613D");
6
+ const DIM = chalk.dim;
7
+ function printProfile(profile, json) {
8
+ if (json) {
9
+ console.log(JSON.stringify(profile, null, 2));
10
+ return;
11
+ }
12
+ console.log();
13
+ console.log(` ${BRAND.bold(profile.projectName)}`);
14
+ console.log(` ${DIM("slug:")} ${profile.slug}`);
15
+ console.log(` ${DIM("approved:")} ${profile.approved ? chalk.green("yes") : chalk.yellow("no")}`);
16
+ if (profile.description) {
17
+ console.log(` ${DIM("description:")} ${profile.description}`);
18
+ }
19
+ if (profile.tokenSymbol) {
20
+ console.log(` ${DIM("token:")} $${profile.tokenSymbol} (${profile.tokenAddress})`);
21
+ }
22
+ if (profile.marketCapUsd) {
23
+ console.log(` ${DIM("market cap:")} $${profile.marketCapUsd.toFixed(0)}`);
24
+ }
25
+ if (profile.weeklyRevenueWeth) {
26
+ console.log(` ${DIM("weekly revenue:")} ${profile.weeklyRevenueWeth} WETH`);
27
+ }
28
+ if (profile.twitterUsername) {
29
+ console.log(` ${DIM("twitter:")} @${profile.twitterUsername}`);
30
+ }
31
+ if (profile.website) {
32
+ console.log(` ${DIM("website:")} ${profile.website}`);
33
+ }
34
+ if (profile.teamMembers.length > 0) {
35
+ console.log(` ${DIM("team:")} ${profile.teamMembers.length} members`);
36
+ }
37
+ if (profile.products.length > 0) {
38
+ console.log(` ${DIM("products:")} ${profile.products.length}`);
39
+ }
40
+ if (profile.projectUpdates.length > 0) {
41
+ console.log(` ${DIM("updates:")} ${profile.projectUpdates.length}`);
42
+ }
43
+ console.log();
44
+ }
45
+ export async function profileViewCommand(opts) {
46
+ try {
47
+ const profile = await getOwnProfile();
48
+ printProfile(profile, !!opts.json);
49
+ }
50
+ catch (err) {
51
+ const msg = err.message;
52
+ if (msg.includes("404")) {
53
+ output.info("No profile found. Create one with: bankr profile create");
54
+ }
55
+ else {
56
+ output.error(msg);
57
+ }
58
+ }
59
+ }
60
+ export async function profileCreateCommand(opts) {
61
+ try {
62
+ let projectName = opts.name;
63
+ if (!projectName) {
64
+ projectName = await input({
65
+ message: "Project name:",
66
+ theme: output.bankrTheme,
67
+ required: true,
68
+ });
69
+ }
70
+ const data = { projectName };
71
+ if (opts.description)
72
+ data.description = opts.description;
73
+ if (opts.token)
74
+ data.tokenAddress = opts.token;
75
+ if (opts.image)
76
+ data.profileImageUrl = opts.image;
77
+ if (opts.website)
78
+ data.website = opts.website;
79
+ // Interactive prompts for missing optional fields
80
+ if (!opts.json && !opts.description) {
81
+ const desc = await input({
82
+ message: "Description (optional):",
83
+ theme: output.bankrTheme,
84
+ });
85
+ if (desc)
86
+ data.description = desc;
87
+ }
88
+ if (!opts.json && !opts.token) {
89
+ const token = await input({
90
+ message: "Token address (optional):",
91
+ theme: output.bankrTheme,
92
+ });
93
+ if (token)
94
+ data.tokenAddress = token;
95
+ }
96
+ if (!opts.json && !opts.website) {
97
+ const website = await input({
98
+ message: "Website URL (optional):",
99
+ theme: output.bankrTheme,
100
+ });
101
+ if (website)
102
+ data.website = website;
103
+ }
104
+ const profile = await createProfile(data);
105
+ output.success("Profile created!");
106
+ printProfile(profile, !!opts.json);
107
+ }
108
+ catch (err) {
109
+ output.error(err.message);
110
+ }
111
+ }
112
+ export async function profileUpdateCommand(opts) {
113
+ try {
114
+ const data = {};
115
+ if (opts.name)
116
+ data.projectName = opts.name;
117
+ if (opts.description)
118
+ data.description = opts.description;
119
+ if (opts.token)
120
+ data.tokenAddress = opts.token;
121
+ if (opts.image)
122
+ data.profileImageUrl = opts.image;
123
+ if (opts.website)
124
+ data.website = opts.website;
125
+ if (Object.keys(data).length === 0) {
126
+ output.error("No fields to update. Use flags: --name, --description, --token, --image, --website");
127
+ return;
128
+ }
129
+ const profile = await updateProfile(data);
130
+ output.success("Profile updated!");
131
+ printProfile(profile, !!opts.json);
132
+ }
133
+ catch (err) {
134
+ output.error(err.message);
135
+ }
136
+ }
137
+ export async function profileDeleteCommand() {
138
+ try {
139
+ const yes = await confirm({
140
+ message: "Are you sure you want to delete your profile?",
141
+ default: false,
142
+ theme: output.bankrTheme,
143
+ });
144
+ if (!yes) {
145
+ output.info("Cancelled.");
146
+ return;
147
+ }
148
+ await deleteProfile();
149
+ output.success("Profile deleted.");
150
+ }
151
+ catch (err) {
152
+ output.error(err.message);
153
+ }
154
+ }
155
+ export async function profileAddUpdateCommand(opts) {
156
+ try {
157
+ let title = opts.title;
158
+ let content = opts.content;
159
+ if (!title) {
160
+ title = await input({
161
+ message: "Update title:",
162
+ theme: output.bankrTheme,
163
+ required: true,
164
+ });
165
+ }
166
+ if (!content) {
167
+ content = await input({
168
+ message: "Update content:",
169
+ theme: output.bankrTheme,
170
+ required: true,
171
+ });
172
+ }
173
+ const profile = await addProjectUpdate({ title, content });
174
+ output.success("Project update added!");
175
+ if (opts.json) {
176
+ console.log(JSON.stringify(profile, null, 2));
177
+ }
178
+ }
179
+ catch (err) {
180
+ output.error(err.message);
181
+ }
182
+ }
183
+ //# sourceMappingURL=profile.js.map
@@ -1,4 +1,5 @@
1
1
  import { pollJob, submitPrompt } from "../lib/api.js";
2
+ import { emitCESP } from "../lib/cesp/engine.js";
2
3
  import { getLastThreadId, requireApiKey, setLastThreadId, } from "../lib/config.js";
3
4
  import * as output from "../lib/output.js";
4
5
  export async function promptCommand(text, options = {}) {
@@ -23,6 +24,7 @@ export async function promptCommand(text, options = {}) {
23
24
  jobId = res.jobId;
24
25
  resolvedThreadId = res.threadId;
25
26
  spin.succeed("Prompt submitted");
27
+ emitCESP("task.acknowledge");
26
28
  output.label("Job ID", jobId);
27
29
  if (resolvedThreadId) {
28
30
  output.label("Thread ID", resolvedThreadId);
@@ -52,6 +54,7 @@ export async function promptCommand(text, options = {}) {
52
54
  if (result.status === "completed") {
53
55
  const elapsed = output.formatDuration(result.processingTime ?? Date.now() - startTime);
54
56
  spin.succeed(`Completed in ${elapsed} (${jobId})`);
57
+ emitCESP("task.complete");
55
58
  console.log();
56
59
  if (result.response) {
57
60
  console.log(result.response);
@@ -63,6 +66,7 @@ export async function promptCommand(text, options = {}) {
63
66
  }
64
67
  else if (result.status === "failed") {
65
68
  spin.fail(`Job failed (${jobId})`);
69
+ emitCESP("task.error");
66
70
  if (result.error) {
67
71
  output.error(result.error);
68
72
  }
@@ -74,6 +78,7 @@ export async function promptCommand(text, options = {}) {
74
78
  }
75
79
  catch (err) {
76
80
  spin.fail("Error while polling");
81
+ emitCESP("resource.limit");
77
82
  output.error(err.message);
78
83
  process.exit(1);
79
84
  }
@@ -1,4 +1,5 @@
1
1
  import { sign } from "../lib/api.js";
2
+ import { emitCESP } from "../lib/cesp/engine.js";
2
3
  import * as output from "../lib/output.js";
3
4
  export async function signCommand(opts) {
4
5
  const request = {
@@ -53,6 +54,7 @@ export async function signCommand(opts) {
53
54
  process.exit(1);
54
55
  }
55
56
  output.success("Signed successfully");
57
+ emitCESP("task.complete");
56
58
  console.log();
57
59
  output.keyValue("Signature", result.signature || "");
58
60
  output.keyValue("Signer", result.signer);
@@ -60,6 +62,7 @@ export async function signCommand(opts) {
60
62
  }
61
63
  catch (err) {
62
64
  spinner.stop();
65
+ emitCESP("task.error");
63
66
  output.error(err.message);
64
67
  process.exit(1);
65
68
  }
@@ -0,0 +1,12 @@
1
+ export declare function soundsStatusCommand(): Promise<void>;
2
+ export declare function soundsEnableCommand(): Promise<void>;
3
+ export declare function soundsDisableCommand(): Promise<void>;
4
+ export declare function soundsListCommand(): Promise<void>;
5
+ export declare function soundsUseCommand(packName: string): Promise<void>;
6
+ export declare function soundsVolumeCommand(level?: string): Promise<void>;
7
+ export declare function soundsMuteCommand(): Promise<void>;
8
+ export declare function soundsUnmuteCommand(): Promise<void>;
9
+ export declare function soundsTestCommand(category?: string): Promise<void>;
10
+ export declare function soundsInstallCommand(packName: string): Promise<void>;
11
+ export declare function soundsSearchCommand(query?: string): Promise<void>;
12
+ //# sourceMappingURL=sounds.d.ts.map