@bankr/cli 0.3.37 → 0.3.43

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.
@@ -1,61 +1,61 @@
1
1
  import chalk from "chalk";
2
2
  import { input, confirm, select } from "@inquirer/prompts";
3
- import { getOwnProfiles, createProfile, updateProfile, deleteProfile, addProjectUpdate, } from "../lib/api.js";
3
+ import { getOwnProjects, createProject, updateProject, deleteProject, addProjectUpdate, } from "../lib/api.js";
4
4
  import * as output from "../lib/output.js";
5
5
  import { getErrorMessage } from "../lib/errors.js";
6
6
  const BRAND = chalk.hex("#FF613D");
7
7
  const DIM = chalk.dim;
8
- function printProfile(profile, json) {
8
+ function printProject(project, json) {
9
9
  if (json) {
10
- console.log(JSON.stringify(profile, null, 2));
10
+ console.log(JSON.stringify(project, null, 2));
11
11
  return;
12
12
  }
13
13
  console.log();
14
- console.log(` ${BRAND.bold(profile.projectName)}`);
15
- console.log(` ${DIM("slug:")} ${profile.slug}`);
16
- console.log(` ${DIM("approved:")} ${profile.approved ? chalk.green("yes") : chalk.yellow("no")}`);
17
- if (profile.description) {
18
- console.log(` ${DIM("description:")} ${profile.description}`);
14
+ console.log(` ${BRAND.bold(project.projectName)}`);
15
+ console.log(` ${DIM("slug:")} ${project.slug}`);
16
+ console.log(` ${DIM("approved:")} ${project.approved ? chalk.green("yes") : chalk.yellow("no")}`);
17
+ if (project.description) {
18
+ console.log(` ${DIM("description:")} ${project.description}`);
19
19
  }
20
- if (profile.tokenSymbol) {
21
- console.log(` ${DIM("token:")} $${profile.tokenSymbol} (${profile.tokenAddress})`);
20
+ if (project.tokenSymbol) {
21
+ console.log(` ${DIM("token:")} $${project.tokenSymbol} (${project.tokenAddress})`);
22
22
  }
23
- if (profile.marketCapUsd) {
24
- console.log(` ${DIM("market cap:")} $${profile.marketCapUsd.toFixed(0)}`);
23
+ if (project.marketCapUsd) {
24
+ console.log(` ${DIM("market cap:")} $${project.marketCapUsd.toFixed(0)}`);
25
25
  }
26
- if (profile.weeklyRevenueWeth) {
27
- console.log(` ${DIM("weekly revenue:")} ${profile.weeklyRevenueWeth} WETH`);
26
+ if (project.weeklyRevenueWeth) {
27
+ console.log(` ${DIM("weekly revenue:")} ${project.weeklyRevenueWeth} WETH`);
28
28
  }
29
- if (profile.twitterUsername) {
30
- console.log(` ${DIM("twitter:")} @${profile.twitterUsername}`);
29
+ if (project.twitterUsername) {
30
+ console.log(` ${DIM("twitter:")} @${project.twitterUsername}`);
31
31
  }
32
- if (profile.website) {
33
- console.log(` ${DIM("website:")} ${profile.website}`);
32
+ if (project.website) {
33
+ console.log(` ${DIM("website:")} ${project.website}`);
34
34
  }
35
- if (profile.teamMembers.length > 0) {
36
- console.log(` ${DIM("team:")} ${profile.teamMembers.length} members`);
35
+ if (project.teamMembers.length > 0) {
36
+ console.log(` ${DIM("team:")} ${project.teamMembers.length} members`);
37
37
  }
38
- if (profile.products.length > 0) {
39
- console.log(` ${DIM("products:")} ${profile.products.length}`);
38
+ if (project.products.length > 0) {
39
+ console.log(` ${DIM("products:")} ${project.products.length}`);
40
40
  }
41
- if (profile.projectUpdates.length > 0) {
42
- console.log(` ${DIM("updates:")} ${profile.projectUpdates.length}`);
41
+ if (project.projectUpdates.length > 0) {
42
+ console.log(` ${DIM("updates:")} ${project.projectUpdates.length}`);
43
43
  }
44
44
  console.log();
45
45
  }
46
46
  /**
47
- * Resolve which profile to operate on.
48
- * If --slug is provided, use it. Otherwise, if there's exactly one profile, use that.
49
- * If multiple profiles exist, prompt the user to pick.
47
+ * Resolve which project to operate on.
48
+ * If --slug is provided, use it. Otherwise, if there's exactly one project, use that.
49
+ * If multiple projects exist, prompt the user to pick.
50
50
  */
51
- async function resolveSlug(profiles, slugOpt) {
51
+ async function resolveSlug(projects, slugOpt) {
52
52
  if (slugOpt)
53
53
  return slugOpt;
54
- if (profiles.length === 1)
55
- return profiles[0].slug;
54
+ if (projects.length === 1)
55
+ return projects[0].slug;
56
56
  const choice = await select({
57
57
  message: "Which project?",
58
- choices: profiles.map((p) => ({
58
+ choices: projects.map((p) => ({
59
59
  name: `${p.projectName} (${p.slug})`,
60
60
  value: p.slug,
61
61
  })),
@@ -63,28 +63,28 @@ async function resolveSlug(profiles, slugOpt) {
63
63
  });
64
64
  return choice;
65
65
  }
66
- export async function profileViewCommand(opts) {
66
+ export async function projectViewCommand(opts) {
67
67
  try {
68
- const profiles = await getOwnProfiles();
69
- if (!profiles.length) {
70
- output.info("No profiles found. Create one with: bankr profile create");
68
+ const projects = await getOwnProjects();
69
+ if (!projects.length) {
70
+ output.info("No projects found. Create one with: bankr project create");
71
71
  return;
72
72
  }
73
- for (const profile of profiles) {
74
- printProfile(profile, !!opts.json);
73
+ for (const project of projects) {
74
+ printProject(project, !!opts.json);
75
75
  }
76
76
  }
77
77
  catch (err) {
78
78
  const msg = getErrorMessage(err);
79
79
  if (msg.includes("404")) {
80
- output.info("No profiles found. Create one with: bankr profile create");
80
+ output.info("No projects found. Create one with: bankr project create");
81
81
  }
82
82
  else {
83
83
  output.error(msg);
84
84
  }
85
85
  }
86
86
  }
87
- export async function profileCreateCommand(opts) {
87
+ export async function projectCreateCommand(opts) {
88
88
  try {
89
89
  let projectName = opts.name;
90
90
  if (!projectName) {
@@ -128,15 +128,15 @@ export async function profileCreateCommand(opts) {
128
128
  if (website)
129
129
  data.website = website;
130
130
  }
131
- const profile = await createProfile(data);
132
- output.success("Profile created!");
133
- printProfile(profile, !!opts.json);
131
+ const project = await createProject(data);
132
+ output.success("Project created!");
133
+ printProject(project, !!opts.json);
134
134
  }
135
135
  catch (err) {
136
136
  output.error(getErrorMessage(err));
137
137
  }
138
138
  }
139
- export async function profileUpdateCommand(opts) {
139
+ export async function projectUpdateCommand(opts) {
140
140
  try {
141
141
  const data = {};
142
142
  if (opts.name)
@@ -153,28 +153,28 @@ export async function profileUpdateCommand(opts) {
153
153
  output.error("No fields to update. Use flags: --name, --description, --token, --image, --website");
154
154
  return;
155
155
  }
156
- const profiles = await getOwnProfiles();
157
- if (!profiles.length) {
158
- output.error("No profiles found. Create one first with: bankr profile create");
156
+ const projects = await getOwnProjects();
157
+ if (!projects.length) {
158
+ output.error("No projects found. Create one first with: bankr project create");
159
159
  return;
160
160
  }
161
- const slug = await resolveSlug(profiles, opts.slug);
162
- const profile = await updateProfile(slug, data);
163
- output.success("Profile updated!");
164
- printProfile(profile, !!opts.json);
161
+ const slug = await resolveSlug(projects, opts.slug);
162
+ const project = await updateProject(slug, data);
163
+ output.success("Project updated!");
164
+ printProject(project, !!opts.json);
165
165
  }
166
166
  catch (err) {
167
167
  output.error(getErrorMessage(err));
168
168
  }
169
169
  }
170
- export async function profileDeleteCommand(opts) {
170
+ export async function projectDeleteCommand(opts) {
171
171
  try {
172
- const profiles = await getOwnProfiles();
173
- if (!profiles.length) {
174
- output.error("No profiles found.");
172
+ const projects = await getOwnProjects();
173
+ if (!projects.length) {
174
+ output.error("No projects found.");
175
175
  return;
176
176
  }
177
- const slug = await resolveSlug(profiles, opts.slug);
177
+ const slug = await resolveSlug(projects, opts.slug);
178
178
  const yes = await confirm({
179
179
  message: `Are you sure you want to delete "${slug}"?`,
180
180
  default: false,
@@ -184,14 +184,14 @@ export async function profileDeleteCommand(opts) {
184
184
  output.info("Cancelled.");
185
185
  return;
186
186
  }
187
- await deleteProfile(slug);
188
- output.success("Profile deleted.");
187
+ await deleteProject(slug);
188
+ output.success("Project deleted.");
189
189
  }
190
190
  catch (err) {
191
191
  output.error(getErrorMessage(err));
192
192
  }
193
193
  }
194
- export async function profileAddUpdateCommand(opts) {
194
+ export async function projectAddUpdateCommand(opts) {
195
195
  try {
196
196
  let title = opts.title;
197
197
  let content = opts.content;
@@ -209,20 +209,20 @@ export async function profileAddUpdateCommand(opts) {
209
209
  required: true,
210
210
  });
211
211
  }
212
- const profiles = await getOwnProfiles();
213
- if (!profiles.length) {
214
- output.error("No profiles found. Create one first with: bankr profile create");
212
+ const projects = await getOwnProjects();
213
+ if (!projects.length) {
214
+ output.error("No projects found. Create one first with: bankr project create");
215
215
  return;
216
216
  }
217
- const slug = await resolveSlug(profiles, opts.slug);
218
- const profile = await addProjectUpdate(slug, { title, content });
217
+ const slug = await resolveSlug(projects, opts.slug);
218
+ const project = await addProjectUpdate(slug, { title, content });
219
219
  output.success("Project update added!");
220
220
  if (opts.json) {
221
- console.log(JSON.stringify(profile, null, 2));
221
+ console.log(JSON.stringify(project, null, 2));
222
222
  }
223
223
  }
224
224
  catch (err) {
225
225
  output.error(getErrorMessage(err));
226
226
  }
227
227
  }
228
- //# sourceMappingURL=profile.js.map
228
+ //# sourceMappingURL=project.js.map
@@ -1,3 +1,14 @@
1
+ /** Valid Max Mode model IDs — hardcoded because CLI is standalone (no monorepo imports).
2
+ * Must be manually kept in sync with models in the LLM gateway DB.
3
+ *
4
+ * Only the two newest releases of each vendor tier are listed, and only the
5
+ * frontier / flagship / balanced ranks — matching what the web picker shows.
6
+ * Older generations and `light` lines still run on the gateway, but the CLI
7
+ * stops steering people onto them. Superseding a model here means DROPPING
8
+ * the one it pushes out of the tier, not just adding the new id. */
9
+ export declare const VALID_MAX_MODE_MODELS: Set<string>;
10
+ /** The `-m, --model` help example; prompt.test.ts fails if the set drops it. */
11
+ export declare const MAX_MODE_MODEL_EXAMPLE = "claude-opus-5.5";
1
12
  export interface PromptOptions {
2
13
  thread?: string;
3
14
  continue?: boolean;
@@ -3,6 +3,7 @@ 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
5
  import { getErrorMessage } from "../lib/errors.js";
6
+ import { createStatusTrail } from "../lib/statusUpdates.js";
6
7
  /** Valid Max Mode model IDs — hardcoded because CLI is standalone (no monorepo imports).
7
8
  * Must be manually kept in sync with models in the LLM gateway DB.
8
9
  *
@@ -11,31 +12,32 @@ import { getErrorMessage } from "../lib/errors.js";
11
12
  * Older generations and `light` lines still run on the gateway, but the CLI
12
13
  * stops steering people onto them. Superseding a model here means DROPPING
13
14
  * the one it pushes out of the tier, not just adding the new id. */
14
- const VALID_MAX_MODE_MODELS = new Set([
15
+ export const VALID_MAX_MODE_MODELS = new Set([
15
16
  // Claude (Vertex AI)
16
17
  "claude-fable-5.1",
17
18
  "claude-fable-5",
19
+ "claude-opus-5.5",
18
20
  "claude-opus-5",
19
- "claude-opus-4.8",
21
+ "claude-sonnet-5.5",
20
22
  "claude-sonnet-5",
21
- "claude-sonnet-4.6",
22
23
  // Gemini (Vertex AI) — gemini-3-pro deliberately absent: removed 2026-03-09,
23
24
  // both provider slugs retired. The Flash Lite tier likewise: 3.5 and 3.1
24
- // deprecated 2026-09-09 (replaced by gemini-3.8-flash). Don't re-add on a
25
- // sync pass.
25
+ // deprecated 2026-09-09 (replaced by gemini-3.8-flash). gemini-2.5-pro too,
26
+ // as of 2026-09-19: Google retires the whole 2.5 line on 2026-10-20, so the
27
+ // `gemini-pro` tier shows only 3.1 Pro. Don't re-add on a sync pass.
26
28
  "gemini-3.8-flash",
27
29
  "gemini-3.7-flash",
28
30
  "gemini-3.1-pro",
29
- "gemini-2.5-pro",
30
31
  // OpenAI (OpenRouter)
31
32
  "gpt-6-astra",
33
+ "gpt-6-sol",
32
34
  "gpt-5.6-sol",
33
- "gpt-5.5",
34
35
  "gpt-5.6-terra",
36
+ "gpt-5.4-mini",
35
37
  "gpt-5.2-codex",
36
38
  // Grok (OpenRouter)
39
+ "grok-4.7",
37
40
  "grok-4.6",
38
- "grok-4.5",
39
41
  // DeepSeek (OpenRouter) — v4.1-flash is its own tier at `balanced`
40
42
  // ($0.30/$1.20, 3.3x under the V4 Pro card), not the `light` flash line.
41
43
  "deepseek-v4-pro-0813",
@@ -56,8 +58,11 @@ const VALID_MAX_MODE_MODELS = new Set([
56
58
  "kimi-k2.6",
57
59
  "glm-5.2",
58
60
  "glm-5.3",
61
+ "glm-5.3-flashx",
59
62
  "glm-5-turbo",
60
63
  ]);
64
+ /** The `-m, --model` help example; prompt.test.ts fails if the set drops it. */
65
+ export const MAX_MODE_MODEL_EXAMPLE = "claude-opus-5.5";
61
66
  export async function promptCommand(text, options = {}) {
62
67
  requireApiKey();
63
68
  // Resolve thread ID from flags
@@ -114,11 +119,15 @@ export async function promptCommand(text, options = {}) {
114
119
  }
115
120
  try {
116
121
  const startTime = Date.now();
122
+ // ora writes to stderr, so a piped consumer still gets only the final
123
+ // response on stdout.
124
+ const renderTrail = createStatusTrail(spin);
117
125
  const result = await pollJob(jobId, {
118
126
  interval: 2000,
119
- onStatus: () => {
120
- const elapsed = output.formatDuration(Date.now() - startTime);
121
- spin.text = `Processing... (${elapsed})`;
127
+ onStatus: (job) => {
128
+ // Set the text first so the restarted spinner shows the current elapsed.
129
+ spin.text = `Processing... (${output.formatDuration(Date.now() - startTime)})`;
130
+ renderTrail(job.statusUpdates);
122
131
  },
123
132
  });
124
133
  // Save thread ID from the final result (or from initial response)
@@ -1,2 +1,6 @@
1
- export declare function statusCommand(jobId: string): Promise<void>;
1
+ export interface StatusOptions {
2
+ /** Poll until the job reaches a terminal state, streaming progress as it arrives. */
3
+ wait?: boolean;
4
+ }
5
+ export declare function statusCommand(jobId: string, options?: StatusOptions): Promise<void>;
2
6
  //# sourceMappingURL=status.d.ts.map
@@ -1,12 +1,49 @@
1
- import { getJobStatus } from "../lib/api.js";
1
+ import { getJobStatus, pollJob, PollTimeoutError, } from "../lib/api.js";
2
2
  import { requireApiKey } from "../lib/config.js";
3
3
  import * as output from "../lib/output.js";
4
- export async function statusCommand(jobId) {
5
- requireApiKey();
6
- const spin = output.spinner("Fetching job status...");
4
+ import { createStatusTrail, newStatusMessages } from "../lib/statusUpdates.js";
5
+ /**
6
+ * Follows a running job, streaming its progress above `spin`. `--wait` attaches
7
+ * to a job that may already be minutes old, so running out the poll budget is a
8
+ * normal outcome: report the last state seen rather than failing the command.
9
+ */
10
+ async function followJob(jobId, spin) {
11
+ const startTime = Date.now();
12
+ const renderTrail = createStatusTrail(spin);
13
+ let lastSeen;
14
+ const elapsed = () => output.formatDuration(Date.now() - startTime);
7
15
  try {
8
- const status = await getJobStatus(jobId);
16
+ const status = await pollJob(jobId, {
17
+ interval: 2000,
18
+ onStatus: (job) => {
19
+ lastSeen = job;
20
+ // Set the text first so the restarted spinner shows the current elapsed.
21
+ spin.text = `Waiting for job... (${elapsed()})`;
22
+ renderTrail(job.statusUpdates);
23
+ },
24
+ });
9
25
  spin.stop();
26
+ return status;
27
+ }
28
+ catch (err) {
29
+ if (!(err instanceof PollTimeoutError) || !lastSeen)
30
+ throw err;
31
+ spin.warn(`Still running after ${elapsed()}; re-run to keep following`);
32
+ return lastSeen;
33
+ }
34
+ }
35
+ export async function statusCommand(jobId, options = {}) {
36
+ requireApiKey();
37
+ const spin = output.spinner(options.wait ? "Waiting for job..." : "Fetching job status...");
38
+ try {
39
+ let status;
40
+ if (options.wait) {
41
+ status = await followJob(jobId, spin);
42
+ }
43
+ else {
44
+ status = await getJobStatus(jobId);
45
+ spin.stop();
46
+ }
10
47
  output.label("Job", status.jobId);
11
48
  output.label("Status", output.formatStatus(status.status));
12
49
  output.label("Prompt", `"${status.prompt}"`);
@@ -23,6 +60,18 @@ export async function statusCommand(jobId) {
23
60
  if (status.processingTime) {
24
61
  output.label("Duration", output.formatDuration(status.processingTime));
25
62
  }
63
+ // With --wait the trail already streamed above the spinner; reprinting it
64
+ // here would show every line twice.
65
+ const progress = options.wait
66
+ ? []
67
+ : newStatusMessages(status.statusUpdates, 0);
68
+ if (progress.length) {
69
+ output.blank();
70
+ output.brandBold("Progress:");
71
+ for (const message of progress) {
72
+ output.dim(` · ${message}`);
73
+ }
74
+ }
26
75
  if (status.response) {
27
76
  console.log();
28
77
  console.log(status.response);
@@ -1,7 +1,7 @@
1
- import { readFileSync } from "node:fs";
2
1
  import { execSync } from "node:child_process";
3
2
  import * as output from "../lib/output.js";
4
3
  import { getErrorMessage } from "../lib/errors.js";
4
+ import { CLI_VERSION } from "../lib/config.js";
5
5
  const PACKAGE_NAME = "@bankr/cli";
6
6
  /**
7
7
  * Compare two semver version strings.
@@ -31,8 +31,7 @@ async function fetchLatestVersion() {
31
31
  return data.version;
32
32
  }
33
33
  function getCurrentVersion() {
34
- const pkg = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf-8"));
35
- return pkg.version;
34
+ return CLI_VERSION;
36
35
  }
37
36
  /**
38
37
  * Detect which package manager installed the CLI globally.
@@ -869,7 +869,7 @@ async function fetchSchemaForInteractive(url) {
869
869
  // Strategy 2: Discover API (already deployed, searches by service name)
870
870
  if (isBankr && parts.length >= 2) {
871
871
  try {
872
- const serviceName = parts[parts.length - 1];
872
+ const serviceName = parts.at(-1);
873
873
  const discoverRes = await fetch(`${getApiUrl()}/x402/endpoints/discover?${new URLSearchParams({ q: serviceName, limit: "5" })}`);
874
874
  if (discoverRes.ok) {
875
875
  const discoverData = (await discoverRes.json());
@@ -931,7 +931,7 @@ async function fetchSchemaForInteractive(url) {
931
931
  const amountAtomic = BigInt(acceptAmount);
932
932
  const amountUsd = Number(amountAtomic) / 1000000;
933
933
  // Extract service name from URL path
934
- const serviceName = parts[parts.length - 1] ?? "endpoint";
934
+ const serviceName = parts.at(-1) ?? "endpoint";
935
935
  return {
936
936
  name: serviceName,
937
937
  description: accept?.description ?? "",
package/dist/lib/api.d.ts CHANGED
@@ -26,6 +26,12 @@ export interface PromptResponse {
26
26
  status: string;
27
27
  message: string;
28
28
  }
29
+ /** One agent progress line, as the API stores it on the job. */
30
+ export interface JobStatusUpdate {
31
+ message: string;
32
+ /** ISO 8601 — a Date on the server, serialized over the wire. */
33
+ timestamp: string;
34
+ }
29
35
  export interface JobStatusResponse {
30
36
  success: boolean;
31
37
  jobId: string;
@@ -34,6 +40,8 @@ export interface JobStatusResponse {
34
40
  prompt: string;
35
41
  createdAt: string;
36
42
  processingTime?: number;
43
+ /** Progress the agent emitted during the run; grows as the job advances. */
44
+ statusUpdates?: JobStatusUpdate[];
37
45
  response?: string;
38
46
  error?: string;
39
47
  startedAt?: string;
@@ -66,6 +74,11 @@ export declare function submitPrompt(prompt: string, threadId?: string, maxMode?
66
74
  }): Promise<PromptResponse>;
67
75
  export declare function getJobStatus(jobId: string): Promise<JobStatusResponse>;
68
76
  export declare function cancelJob(jobId: string): Promise<JobStatusResponse>;
77
+ /**
78
+ * Answers "can this key read the account", not "is the API up": `/wallet/me`
79
+ * is key-authenticated, so invalid/revoked/IP-blocked keys (401/403), keys with
80
+ * no active wallet (401/404/banned/suspended) and API errors all yield false.
81
+ */
69
82
  export declare function validateApiKey(): Promise<boolean>;
70
83
  export declare function getUserInfo(): Promise<UserInfoResponse>;
71
84
  export interface TokenBalanceInfo {
@@ -132,6 +145,14 @@ export declare function getPortfolio(opts?: PortfolioOptions): Promise<Portfolio
132
145
  export type BalancesResponse = PortfolioResponse;
133
146
  /** @deprecated Use getPortfolio instead */
134
147
  export declare const getBalances: (chains?: string[], showLowValueTokens?: boolean) => Promise<PortfolioResponse>;
148
+ /**
149
+ * The wait ran out, not the job — every fetch succeeded and the job is still
150
+ * running. Typed so callers can tell it apart from a real request failure.
151
+ */
152
+ export declare class PollTimeoutError extends Error {
153
+ readonly attempts: number;
154
+ constructor(attempts: number);
155
+ }
135
156
  export declare function pollJob(jobId: string, opts?: {
136
157
  interval?: number;
137
158
  maxAttempts?: number;
@@ -406,7 +427,7 @@ export interface BuildClaimResponse {
406
427
  * Unauthenticated -- calls the public endpoint. The CLI signs and
407
428
  * broadcasts locally.
408
429
  */
409
- export interface AgentProfileResponse {
430
+ export interface ProjectResponse {
410
431
  id: string;
411
432
  slug: string;
412
433
  projectName: string;
@@ -447,18 +468,18 @@ export interface AgentProfileResponse {
447
468
  isPublished?: boolean;
448
469
  createdAt: string;
449
470
  }
450
- export declare function getOwnProfiles(): Promise<AgentProfileResponse[]>;
451
- /** @deprecated Use getOwnProfiles() — returns first profile for backward compat */
452
- export declare function getOwnProfile(): Promise<AgentProfileResponse>;
453
- export declare function createProfile(data: Record<string, unknown>): Promise<AgentProfileResponse>;
454
- export declare function updateProfile(slug: string, data: Record<string, unknown>): Promise<AgentProfileResponse>;
455
- export declare function deleteProfile(slug: string): Promise<{
471
+ export declare function getOwnProjects(): Promise<ProjectResponse[]>;
472
+ /** @deprecated Use getOwnProjects() — returns first project for backward compat */
473
+ export declare function getOwnProject(): Promise<ProjectResponse>;
474
+ export declare function createProject(data: Record<string, unknown>): Promise<ProjectResponse>;
475
+ export declare function updateProject(slug: string, data: Record<string, unknown>): Promise<ProjectResponse>;
476
+ export declare function deleteProject(slug: string): Promise<{
456
477
  success: boolean;
457
478
  }>;
458
479
  export declare function addProjectUpdate(slug: string, data: {
459
480
  title: string;
460
481
  content: string;
461
- }): Promise<AgentProfileResponse>;
482
+ }): Promise<ProjectResponse>;
462
483
  export interface TokenSearchResult {
463
484
  address: string;
464
485
  symbol: string;
package/dist/lib/api.js CHANGED
@@ -72,11 +72,14 @@ export async function cancelJob(jobId) {
72
72
  });
73
73
  return handleResponse(res);
74
74
  }
75
+ /**
76
+ * Answers "can this key read the account", not "is the API up": `/wallet/me`
77
+ * is key-authenticated, so invalid/revoked/IP-blocked keys (401/403), keys with
78
+ * no active wallet (401/404/banned/suspended) and API errors all yield false.
79
+ */
75
80
  export async function validateApiKey() {
76
81
  try {
77
- const res = await fetch(`${getApiUrl()}/_health`, {
78
- headers: { "X-API-Key": requireApiKey(), "User-Agent": CLI_USER_AGENT },
79
- });
82
+ const res = await authedFetch(`${getApiUrl()}/wallet/me`);
80
83
  return res.ok;
81
84
  }
82
85
  catch {
@@ -101,6 +104,17 @@ export async function getPortfolio(opts = {}) {
101
104
  }
102
105
  /** @deprecated Use getPortfolio instead */
103
106
  export const getBalances = (chains, showLowValueTokens) => getPortfolio({ chains, showLowValueTokens });
107
+ /**
108
+ * The wait ran out, not the job — every fetch succeeded and the job is still
109
+ * running. Typed so callers can tell it apart from a real request failure.
110
+ */
111
+ export class PollTimeoutError extends Error {
112
+ constructor(attempts) {
113
+ super(`Polling timed out after ${attempts} attempts`);
114
+ this.attempts = attempts;
115
+ this.name = "PollTimeoutError";
116
+ }
117
+ }
104
118
  export async function pollJob(jobId, opts = {}) {
105
119
  const { interval = 2000, maxAttempts = 150, onStatus } = opts;
106
120
  let attempts = 0;
@@ -113,7 +127,7 @@ export async function pollJob(jobId, opts = {}) {
113
127
  await new Promise((resolve) => setTimeout(resolve, interval));
114
128
  attempts++;
115
129
  }
116
- throw new Error(`Polling timed out after ${maxAttempts} attempts`);
130
+ throw new PollTimeoutError(maxAttempts);
117
131
  }
118
132
  export async function sign(request) {
119
133
  const res = await authedFetch(`${getApiUrl()}/wallet/sign`, {
@@ -269,30 +283,30 @@ export async function claimTokenLaunchFees(tokenAddress) {
269
283
  });
270
284
  return handleResponse(res);
271
285
  }
272
- export async function getOwnProfiles() {
286
+ export async function getOwnProjects() {
273
287
  const res = await authedFetch(`${getApiUrl()}/agent/profile?multi=true`);
274
288
  return handleResponse(res);
275
289
  }
276
- /** @deprecated Use getOwnProfiles() — returns first profile for backward compat */
277
- export async function getOwnProfile() {
278
- const profiles = await getOwnProfiles();
279
- return profiles[0];
290
+ /** @deprecated Use getOwnProjects() — returns first project for backward compat */
291
+ export async function getOwnProject() {
292
+ const projects = await getOwnProjects();
293
+ return projects[0];
280
294
  }
281
- export async function createProfile(data) {
295
+ export async function createProject(data) {
282
296
  const res = await authedFetch(`${getApiUrl()}/agent/profile?multi=true`, {
283
297
  method: "POST",
284
298
  body: JSON.stringify(data),
285
299
  });
286
300
  return handleResponse(res);
287
301
  }
288
- export async function updateProfile(slug, data) {
302
+ export async function updateProject(slug, data) {
289
303
  const res = await authedFetch(`${getApiUrl()}/agent/profile/${encodeURIComponent(slug)}`, {
290
304
  method: "PUT",
291
305
  body: JSON.stringify(data),
292
306
  });
293
307
  return handleResponse(res);
294
308
  }
295
- export async function deleteProfile(slug) {
309
+ export async function deleteProject(slug) {
296
310
  const res = await authedFetch(`${getApiUrl()}/agent/profile/${encodeURIComponent(slug)}`, {
297
311
  method: "DELETE",
298
312
  });
@@ -11,7 +11,8 @@ export declare function setConfigPath(path: string): void;
11
11
  export declare function getConfigPath(): string;
12
12
  export declare const DEFAULT_API_URL = "https://api.bankr.bot";
13
13
  export declare const DEFAULT_LLM_URL = "https://llm.bankr.bot";
14
- export declare const CLI_USER_AGENT = "bankr-cli/0.1";
14
+ export declare const CLI_VERSION: string;
15
+ export declare const CLI_USER_AGENT: string;
15
16
  export declare function readConfig(): BankrConfig;
16
17
  export declare function writeConfig(config: BankrConfig): void;
17
18
  export declare function clearConfig(): void;
@@ -1,5 +1,5 @@
1
1
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync, } from "fs";
2
- import { homedir } from "os";
2
+ import { homedir, platform, release } from "os";
3
3
  import { dirname, join } from "path";
4
4
  let configPathOverride;
5
5
  const DEFAULT_CONFIG_FILE = join(homedir(), ".bankr", "config.json");
@@ -11,7 +11,23 @@ export function getConfigPath() {
11
11
  }
12
12
  export const DEFAULT_API_URL = "https://api.bankr.bot";
13
13
  export const DEFAULT_LLM_URL = "https://llm.bankr.bot";
14
- export const CLI_USER_AGENT = "bankr-cli/0.1";
14
+ export const CLI_VERSION = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf-8")).version;
15
+ /** Browser-style platform token so the API's shared user-agent parser (which
16
+ * clamps to known families) names the OS on an out-of-band approval screen
17
+ * as "Bankr CLI · macOS". Never the hostname or username. */
18
+ function platformToken() {
19
+ switch (platform()) {
20
+ case "darwin":
21
+ return "Macintosh";
22
+ case "win32":
23
+ return "Windows NT";
24
+ case "linux":
25
+ return "Linux";
26
+ default:
27
+ return platform();
28
+ }
29
+ }
30
+ export const CLI_USER_AGENT = `bankr-cli/${CLI_VERSION} (${platformToken()}; ${platform()} ${release()})`;
15
31
  function ensureConfigDir() {
16
32
  const configDir = dirname(getConfigPath());
17
33
  if (!existsSync(configDir)) {