@bankr/cli 0.3.38 → 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.
@@ -3,6 +3,7 @@ import { confirm, password, checkbox } from "@inquirer/prompts";
3
3
  import { isAddress } from "viem";
4
4
  import { getCreatorFees, getBeneficiaryFeesStreaming, getTokenCreatorFees, getUserInfo, claimTokenLaunchFees, } from "../lib/api.js";
5
5
  import { emitCESP } from "../lib/cesp/engine.js";
6
+ import { CHAIN_LABELS } from "../lib/chains.js";
6
7
  import { requireApiKey } from "../lib/config.js";
7
8
  import { txExplorer } from "../lib/explorer.js";
8
9
  import * as output from "../lib/output.js";
@@ -129,7 +130,7 @@ function drawChart(dailyData) {
129
130
  const xAxisPadding = " ".repeat(14);
130
131
  if (dailyData.length > 0) {
131
132
  const first = dailyData[0].date.slice(5);
132
- const last = dailyData[dailyData.length - 1].date.slice(5);
133
+ const last = dailyData.at(-1).date.slice(5);
133
134
  const mid = dailyData[Math.floor(dailyData.length / 2)].date.slice(5);
134
135
  const gap1 = Math.floor(CHART_WIDTH / 2) - first.length;
135
136
  const gap2 = CHART_WIDTH - Math.floor(CHART_WIDTH / 2) - mid.length;
@@ -653,12 +654,21 @@ export async function feesClaimWalletCommand(address, opts) {
653
654
  ],
654
655
  },
655
656
  ];
656
- // Robinhood-chain launches can't be claimed here: this flow signs and
657
- // broadcasts on Base only, and a Base tx to a robinhood initializer is a
658
- // gas-burning no-op. Skip them loudly.
659
- const baseChainTokens = selectedTokens.filter((t) => (t.chain ?? "base") === "base");
660
- if (baseChainTokens.length < selectedTokens.length) {
661
- output.warn(`${selectedTokens.length - baseChainTokens.length} Robinhood Chain token(s) skipped — CLI claiming supports Base only for now. Claim at https://bankr.bot/launches instead.`);
657
+ // Launches on other chains can't be claimed here: this flow signs and
658
+ // broadcasts on Base only, and a Base tx to another chain's initializer is a
659
+ // gas-burning no-op. Skip them loudly, linking each token page's claim flow.
660
+ const chainOf = (t) => t.chain ?? "base";
661
+ const baseChainTokens = selectedTokens.filter((t) => chainOf(t) === "base");
662
+ const skippedTokens = selectedTokens.filter((t) => chainOf(t) !== "base");
663
+ if (skippedTokens.length > 0) {
664
+ const names = [...new Set(skippedTokens.map(chainOf))].map((chain) => CHAIN_LABELS[chain] ?? chain);
665
+ const skippedChains = names.length > 1
666
+ ? `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`
667
+ : names[0];
668
+ output.warn(`${skippedTokens.length} ${skippedChains} token(s) skipped — CLI claiming supports Base only for now. Claim them on each token's page instead:`);
669
+ for (const t of skippedTokens) {
670
+ output.dim(` ${t.symbol}: https://bankr.bot/terminal/trade?out=${t.tokenAddress}&chain=${chainOf(t)}`);
671
+ }
662
672
  }
663
673
  // Filter to tokens with initializer (required for local tx building)
664
674
  const buildableTokens = baseChainTokens.filter((t) => t.initializer);
@@ -38,6 +38,15 @@ const GATEWAY_MODELS = [
38
38
  input: IMAGE_INPUT,
39
39
  cost: { input: 10.0, output: 50.0, cacheRead: 1.0, cacheWrite: 12.5 },
40
40
  },
41
+ {
42
+ id: "claude-opus-5.5",
43
+ name: "Claude Opus 5.5",
44
+ owned_by: "anthropic",
45
+ contextWindow: 1000000,
46
+ maxTokens: 128000,
47
+ input: IMAGE_INPUT,
48
+ cost: { input: 4.0, output: 20.0, cacheRead: 0.2, cacheWrite: 5.0 },
49
+ },
41
50
  {
42
51
  id: "claude-opus-5",
43
52
  name: "Claude Opus 5",
@@ -83,6 +92,15 @@ const GATEWAY_MODELS = [
83
92
  input: IMAGE_INPUT,
84
93
  cost: { input: 5.0, output: 25.0, cacheRead: 0.5, cacheWrite: 6.25 },
85
94
  },
95
+ {
96
+ id: "claude-sonnet-5.5",
97
+ name: "Claude Sonnet 5.5",
98
+ owned_by: "anthropic",
99
+ contextWindow: 1000000,
100
+ maxTokens: 128000,
101
+ input: IMAGE_INPUT,
102
+ cost: { input: 2.0, output: 10.0, cacheRead: 0.2, cacheWrite: 2.5 },
103
+ },
86
104
  {
87
105
  id: "claude-sonnet-5",
88
106
  name: "Claude Sonnet 5",
@@ -182,6 +200,12 @@ const GATEWAY_MODELS = [
182
200
  maxTokens: 65536,
183
201
  input: IMAGE_INPUT,
184
202
  cost: { input: 1.25, output: 10.0, cacheRead: 0.125, cacheWrite: 0.375 },
203
+ // Google retires the 2.5 line on 2026-10-20 (published on every Vertex
204
+ // endpoint). Without this the offline catalog advertises it as active.
205
+ deprecation: {
206
+ replaced_by: "gemini-3.1-pro",
207
+ removed_at: "2026-10-20T00:00:00.000Z",
208
+ },
185
209
  },
186
210
  {
187
211
  id: "gemini-2.5-flash",
@@ -191,6 +215,10 @@ const GATEWAY_MODELS = [
191
215
  maxTokens: 65535,
192
216
  input: IMAGE_INPUT,
193
217
  cost: { input: 0.3, output: 2.5, cacheRead: 0.03, cacheWrite: 0.0833 },
218
+ deprecation: {
219
+ replaced_by: "gemini-3.8-flash",
220
+ removed_at: "2026-10-20T00:00:00.000Z",
221
+ },
194
222
  },
195
223
  // Gemma
196
224
  {
@@ -243,6 +271,24 @@ const GATEWAY_MODELS = [
243
271
  input: IMAGE_INPUT,
244
272
  cost: { input: 10.0, output: 50.0, cacheRead: 1.0, cacheWrite: 12.5 },
245
273
  },
274
+ {
275
+ id: "gpt-6-sol",
276
+ name: "GPT 6 Sol",
277
+ owned_by: "openai",
278
+ contextWindow: 1050000,
279
+ maxTokens: 128000,
280
+ input: IMAGE_INPUT,
281
+ cost: { input: 2.0, output: 10.0, cacheRead: 0.2, cacheWrite: 2.5 },
282
+ },
283
+ {
284
+ id: "gpt-6-luna",
285
+ name: "GPT 6 Luna",
286
+ owned_by: "openai",
287
+ contextWindow: 1050000,
288
+ maxTokens: 128000,
289
+ input: IMAGE_INPUT,
290
+ cost: { input: 0.1, output: 0.5, cacheRead: 0.01, cacheWrite: 0.125 },
291
+ },
246
292
  {
247
293
  id: "gpt-5.6-sol",
248
294
  name: "GPT 5.6 Sol",
@@ -355,6 +401,15 @@ const GATEWAY_MODELS = [
355
401
  cost: { input: 0.05, output: 0.4, cacheRead: 0.005, cacheWrite: 0 },
356
402
  },
357
403
  // Other providers
404
+ {
405
+ id: "grok-4.7",
406
+ name: "Grok 4.7",
407
+ owned_by: "x-ai",
408
+ contextWindow: 500000,
409
+ maxTokens: 500000,
410
+ input: IMAGE_INPUT,
411
+ cost: { input: 2.0, output: 6.0, cacheRead: 0.5, cacheWrite: 0 },
412
+ },
358
413
  {
359
414
  id: "grok-4.6",
360
415
  name: "Grok 4.6",
@@ -604,6 +659,15 @@ const GATEWAY_MODELS = [
604
659
  input: TEXT_INPUT,
605
660
  cost: { input: 0.6, output: 2.4, cacheRead: 0.06, cacheWrite: 0 },
606
661
  },
662
+ {
663
+ id: "glm-5.3-flashx",
664
+ name: "GLM-5.3 FlashX",
665
+ owned_by: "z-ai",
666
+ contextWindow: 1048576,
667
+ maxTokens: 131072,
668
+ input: IMAGE_INPUT,
669
+ cost: { input: 0.37, output: 1.25, cacheRead: 0.075, cacheWrite: 0 },
670
+ },
607
671
  {
608
672
  id: "glm-5.3-flash",
609
673
  name: "GLM-5.3 Flash",
@@ -11,6 +11,37 @@ export declare class MfaStepUpError extends Error {
11
11
  /** Keyed on `code`, never on message text, so a server wording change can't
12
12
  * silently turn this back into the opaque failure. */
13
13
  export declare function mfaStepUpErrorFrom(body: unknown): MfaStepUpError | null;
14
+ /** Any 5xx: a gateway page (ALB/CloudFront 502/503/504) the API never saw, or
15
+ * an API catch-all such as a DB blip. Idempotent callers retry it; the API's
16
+ * own message, when it sent one, is kept for the final report. */
17
+ export declare class TransientHttpError extends Error {
18
+ readonly status: number;
19
+ constructor(status: number, apiMessage?: string);
20
+ }
21
+ /** True for a request the API never answered: undici's `fetch failed`, a
22
+ * socket-level code, or a gateway error page. An API refusal is not one. */
23
+ export declare function isTransientNetworkError(err: unknown, depth?: number): boolean;
24
+ /**
25
+ * Retry a login step on network blips only. The OTP is already spent by the
26
+ * time the wallet step runs, so a dropped connection here used to cost the
27
+ * user a fresh code; three quick attempts ride out the transient case.
28
+ */
29
+ export declare function withNetworkRetry<T>(run: () => Promise<T>, opts?: {
30
+ attempts?: number;
31
+ baseDelayMs?: number;
32
+ sleep?: (ms: number) => Promise<void>;
33
+ }): Promise<T>;
34
+ /** Key names are unique per user, so the default carries the time to the
35
+ * second: a scripted re-login seconds after a dropped mint response (which
36
+ * already created the key) must not collide with the earlier default. */
37
+ export declare function defaultCliKeyName(now?: Date): string;
38
+ /** Headless login (`--code` / `--ni`) accepts the Terms of Service as part of
39
+ * logging in, so the one-time code isn't burned on a missing flag. Interactive
40
+ * login returns undefined to defer to the prompt. */
41
+ export declare function resolveTermsAcceptance(opts: {
42
+ headless: boolean;
43
+ acceptTerms?: boolean;
44
+ }): boolean | undefined;
14
45
  /** The web app that fronts an API URL: `BANKR_WEB_URL` when set (local dev,
15
46
  * staging), bankr.bot for the official API, else the API host with its
16
47
  * `api.`/`api-` prefix dropped. */
@@ -41,6 +72,13 @@ export declare function resolveKeyPermissionOverrides(opts: {
41
72
  readOnly?: boolean;
42
73
  agentApiEnabled?: boolean;
43
74
  };
75
+ /**
76
+ * Parse a login-gated response. The body is read as text first so a gateway
77
+ * HTML error page becomes a `TransientHttpError` instead of a `SyntaxError`
78
+ * from `res.json()`. Every 5xx is transient (callers that opted into the
79
+ * retry replay it; others just report it); a 4xx keeps the API's message.
80
+ */
81
+ export declare function readGatedResponse<T>(res: Response, label: string): Promise<T>;
44
82
  declare const CHALLENGE_STATUSES: readonly ["pending", "verified", "expired", "abandoned", "failed"];
45
83
  export type ChallengeStatus = (typeof CHALLENGE_STATUSES)[number];
46
84
  /**
@@ -35,6 +35,77 @@ export function mfaStepUpErrorFrom(body) {
35
35
  return null;
36
36
  return new MfaStepUpError(typeof context === "string" ? context : "unknown");
37
37
  }
38
+ const NETWORK_ERROR_CODES = new Set([
39
+ "ECONNRESET",
40
+ "ECONNREFUSED",
41
+ "ETIMEDOUT",
42
+ "ENOTFOUND",
43
+ "EAI_AGAIN",
44
+ "EPIPE",
45
+ "UND_ERR_SOCKET",
46
+ "UND_ERR_CONNECT_TIMEOUT",
47
+ ]);
48
+ /** Any 5xx: a gateway page (ALB/CloudFront 502/503/504) the API never saw, or
49
+ * an API catch-all such as a DB blip. Idempotent callers retry it; the API's
50
+ * own message, when it sent one, is kept for the final report. */
51
+ export class TransientHttpError extends Error {
52
+ constructor(status, apiMessage) {
53
+ super(apiMessage ?? `Bankr API unavailable (${status})`);
54
+ this.status = status;
55
+ this.name = "TransientHttpError";
56
+ }
57
+ }
58
+ /** True for a request the API never answered: undici's `fetch failed`, a
59
+ * socket-level code, or a gateway error page. An API refusal is not one. */
60
+ export function isTransientNetworkError(err, depth = 0) {
61
+ if (depth > 5)
62
+ return false;
63
+ if (!(err instanceof Error) || err instanceof MfaStepUpError)
64
+ return false;
65
+ if (err instanceof TransientHttpError)
66
+ return true;
67
+ // undici: "fetch failed" when no connection, "terminated" on a mid-body drop.
68
+ if (err instanceof TypeError &&
69
+ (err.message === "fetch failed" || err.message === "terminated")) {
70
+ return true;
71
+ }
72
+ const code = err.code;
73
+ if (typeof code === "string" && NETWORK_ERROR_CODES.has(code))
74
+ return true;
75
+ return isTransientNetworkError(err.cause, depth + 1);
76
+ }
77
+ /**
78
+ * Retry a login step on network blips only. The OTP is already spent by the
79
+ * time the wallet step runs, so a dropped connection here used to cost the
80
+ * user a fresh code; three quick attempts ride out the transient case.
81
+ */
82
+ export async function withNetworkRetry(run, opts = {}) {
83
+ const { attempts = 3, baseDelayMs = 1000, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), } = opts;
84
+ for (let attempt = 1;; attempt++) {
85
+ try {
86
+ return await run();
87
+ }
88
+ catch (err) {
89
+ if (attempt >= attempts || !isTransientNetworkError(err))
90
+ throw err;
91
+ await sleep(baseDelayMs * 2 ** (attempt - 1));
92
+ }
93
+ }
94
+ }
95
+ /** Key names are unique per user, so the default carries the time to the
96
+ * second: a scripted re-login seconds after a dropped mint response (which
97
+ * already created the key) must not collide with the earlier default. */
98
+ export function defaultCliKeyName(now = new Date()) {
99
+ const iso = now.toISOString();
100
+ return `CLI-${iso.slice(0, 10)}-${iso.slice(11, 19).replace(/:/g, "")}`;
101
+ }
102
+ /** Headless login (`--code` / `--ni`) accepts the Terms of Service as part of
103
+ * logging in, so the one-time code isn't burned on a missing flag. Interactive
104
+ * login returns undefined to defer to the prompt. */
105
+ export function resolveTermsAcceptance(opts) {
106
+ // `--accept-terms` is set-only (no `--no-accept-terms`), so `||` is safe.
107
+ return opts.acceptTerms || opts.headless || undefined;
108
+ }
38
109
  /** The web app that fronts an API URL: `BANKR_WEB_URL` when set (local dev,
39
110
  * staging), bankr.bot for the official API, else the API host with its
40
111
  * `api.`/`api-` prefix dropped. */
@@ -111,7 +182,9 @@ export function resolveKeyPermissionOverrides(opts) {
111
182
  return overrides;
112
183
  }
113
184
  async function verifyPrivyOtp(privyConfig, email, code) {
114
- const res = await fetch("https://auth.privy.io/api/v1/passwordless/authenticate", {
185
+ // Retry only a request that never got an answer. If Privy did answer and
186
+ // the socket dropped, the replay reads as INVALID_CODE: no worse than today.
187
+ const res = await withNetworkRetry(() => fetch("https://auth.privy.io/api/v1/passwordless/authenticate", {
115
188
  method: "POST",
116
189
  headers: {
117
190
  "Content-Type": "application/json",
@@ -119,7 +192,7 @@ async function verifyPrivyOtp(privyConfig, email, code) {
119
192
  "privy-client-id": privyConfig.privyClientId,
120
193
  },
121
194
  body: JSON.stringify({ email, code, mode: "login-or-sign-up" }),
122
- });
195
+ }));
123
196
  if (!res.ok) {
124
197
  if (INVALID_CODE_STATUSES.includes(res.status)) {
125
198
  throw new Error("INVALID_CODE");
@@ -131,30 +204,49 @@ async function verifyPrivyOtp(privyConfig, email, code) {
131
204
  }
132
205
  return (await res.json());
133
206
  }
207
+ /**
208
+ * Parse a login-gated response. The body is read as text first so a gateway
209
+ * HTML error page becomes a `TransientHttpError` instead of a `SyntaxError`
210
+ * from `res.json()`. Every 5xx is transient (callers that opted into the
211
+ * retry replay it; others just report it); a 4xx keeps the API's message.
212
+ */
213
+ export async function readGatedResponse(res, label) {
214
+ const text = await res.text();
215
+ let body;
216
+ try {
217
+ body = JSON.parse(text);
218
+ }
219
+ catch {
220
+ body = undefined;
221
+ }
222
+ const isJsonObject = !!body && typeof body === "object";
223
+ const { message, error } = (isJsonObject ? body : {});
224
+ const apiMessage = (typeof message === "string" && message) ||
225
+ (typeof error === "string" && error) ||
226
+ undefined;
227
+ if (!res.ok) {
228
+ if (res.status >= 500)
229
+ throw new TransientHttpError(res.status, apiMessage);
230
+ throw (mfaStepUpErrorFrom(body) ??
231
+ new Error(apiMessage ?? `${label} (${res.status})`));
232
+ }
233
+ if (!isJsonObject)
234
+ throw new Error(`${label}: unexpected response`);
235
+ return body;
236
+ }
134
237
  async function callGenerateWallet(apiUrl, tokens) {
135
238
  const res = await fetch(`${apiUrl}/cli/generate-wallet`, {
136
239
  method: "POST",
137
240
  headers: authHeaders(tokens),
138
241
  });
139
- const body = (await res.json());
140
- if (!res.ok) {
141
- throw (mfaStepUpErrorFrom(body) ??
142
- new Error(body.message ||
143
- body.error ||
144
- `Wallet generation failed (${res.status})`));
145
- }
146
- return body;
242
+ return readGatedResponse(res, "Wallet generation failed");
147
243
  }
148
244
  async function callAcceptTerms(apiUrl, tokens) {
149
245
  const res = await fetch(`${apiUrl}/user/accept-terms`, {
150
246
  method: "POST",
151
247
  headers: authHeaders(tokens),
152
248
  });
153
- if (!res.ok) {
154
- const body = (await res.json().catch(() => ({})));
155
- throw (mfaStepUpErrorFrom(body) ??
156
- new Error(body.message || body.error || `Accept terms failed (${res.status})`));
157
- }
249
+ await readGatedResponse(res, "Accept terms failed");
158
250
  }
159
251
  async function callGenerateApiKey(apiUrl, tokens, opts) {
160
252
  const res = await fetch(`${apiUrl}/api-keys`, {
@@ -162,14 +254,9 @@ async function callGenerateApiKey(apiUrl, tokens, opts) {
162
254
  headers: authHeaders(tokens),
163
255
  body: JSON.stringify(opts),
164
256
  });
165
- const body = (await res.json());
166
- if (!res.ok) {
167
- throw (mfaStepUpErrorFrom(body) ??
168
- new Error(body.message ||
169
- body.error ||
170
- `API key generation failed (${res.status})`));
171
- }
172
- return body;
257
+ // Parsed through the same seam so a gateway page reports cleanly; the mint
258
+ // itself is still never retried (see runGatedStep).
259
+ return readGatedResponse(res, "API key generation failed");
173
260
  }
174
261
  // ── Out-of-band MFA approval ─────────────────────────────────────────
175
262
  // Mirrors `MFA_CHALLENGE_STATUSES` in @bankr/shared (the CLI can't import it).
@@ -370,13 +457,18 @@ async function authenticateWithEmail(apiUrl, providedEmail, providedCode) {
370
457
  /**
371
458
  * Run a login-gated call; on the MFA step-up envelope, take the browser
372
459
  * approval hop once and retry. A second refusal, or an approval that expires
373
- * or fails, exits with the browser-key guidance.
460
+ * or fails, exits with the browser-key guidance. Network blips are retried
461
+ * only where the caller opts in, so a step is never replayed by default: a key
462
+ * mint whose response was dropped has already consumed its unique name.
374
463
  */
375
- async function runGatedStep(apiUrl, tokens, label, run) {
464
+ async function runGatedStep(apiUrl, tokens, label, run, opts = {}) {
465
+ const { retryOnNetworkBlip = false } = opts;
376
466
  for (const retry of [false, true]) {
377
467
  const spin = output.spinner(label);
378
468
  try {
379
- const result = await run();
469
+ const result = retryOnNetworkBlip
470
+ ? await withNetworkRetry(run)
471
+ : await run();
380
472
  spin.stop();
381
473
  return result;
382
474
  }
@@ -416,8 +508,9 @@ function exitWithApiKeyGuidance(apiUrl) {
416
508
  async function emailLoginFlow(apiUrl, opts) {
417
509
  // Step 1: Authenticate via email OTP
418
510
  const { tokens } = await authenticateWithEmail(apiUrl, opts.email, opts.code);
419
- // Step 2: Generate/resolve wallet
420
- const wallet = await runGatedStep(apiUrl, tokens, "Setting up wallet...", () => callGenerateWallet(apiUrl, tokens));
511
+ // Step 2: Resolve (or create) the wallet. The label stays neutral because
512
+ // whether the user is new only comes back in the response.
513
+ const wallet = await runGatedStep(apiUrl, tokens, "Loading wallet...", () => callGenerateWallet(apiUrl, tokens), { retryOnNetworkBlip: true });
421
514
  // Show wallet info
422
515
  output.blank();
423
516
  if (wallet.isNewUser) {
@@ -437,23 +530,21 @@ async function emailLoginFlow(apiUrl, opts) {
437
530
  output.blank();
438
531
  output.dim(" Please review our Terms of Service: https://bankr.bot/terms");
439
532
  output.blank();
440
- const accepted = opts.acceptTerms ??
441
- (headless
442
- ? false
443
- : await confirm({
444
- message: "Do you accept the Terms of Service?",
445
- default: false,
446
- theme: output.bankrTheme,
447
- }));
533
+ const accepted = resolveTermsAcceptance({ headless, acceptTerms: opts.acceptTerms }) ??
534
+ (await confirm({
535
+ message: "Do you accept the Terms of Service?",
536
+ default: false,
537
+ theme: output.bankrTheme,
538
+ }));
448
539
  if (!accepted) {
449
540
  output.fatal("Terms must be accepted to continue.");
450
541
  }
451
- await runGatedStep(apiUrl, tokens, "Accepting terms...", () => callAcceptTerms(apiUrl, tokens));
542
+ await runGatedStep(apiUrl, tokens, "Accepting terms...", () => callAcceptTerms(apiUrl, tokens), { retryOnNetworkBlip: true });
452
543
  output.success("Terms accepted");
453
544
  }
454
545
  // Step 4: Generate API key
455
546
  output.blank();
456
- const defaultKeyName = `CLI-${new Date().toISOString().slice(0, 10)}`;
547
+ const defaultKeyName = defaultCliKeyName();
457
548
  const keyName = opts.keyName ??
458
549
  (headless
459
550
  ? defaultKeyName
@@ -1,7 +1,7 @@
1
- export declare function profileViewCommand(opts: {
1
+ export declare function projectViewCommand(opts: {
2
2
  json?: boolean;
3
3
  }): Promise<void>;
4
- export declare function profileCreateCommand(opts: {
4
+ export declare function projectCreateCommand(opts: {
5
5
  name?: string;
6
6
  description?: string;
7
7
  token?: string;
@@ -9,7 +9,7 @@ export declare function profileCreateCommand(opts: {
9
9
  website?: string;
10
10
  json?: boolean;
11
11
  }): Promise<void>;
12
- export declare function profileUpdateCommand(opts: {
12
+ export declare function projectUpdateCommand(opts: {
13
13
  slug?: string;
14
14
  name?: string;
15
15
  description?: string;
@@ -18,13 +18,13 @@ export declare function profileUpdateCommand(opts: {
18
18
  website?: string;
19
19
  json?: boolean;
20
20
  }): Promise<void>;
21
- export declare function profileDeleteCommand(opts: {
21
+ export declare function projectDeleteCommand(opts: {
22
22
  slug?: string;
23
23
  }): Promise<void>;
24
- export declare function profileAddUpdateCommand(opts: {
24
+ export declare function projectAddUpdateCommand(opts: {
25
25
  slug?: string;
26
26
  title?: string;
27
27
  content?: string;
28
28
  json?: boolean;
29
29
  }): Promise<void>;
30
- //# sourceMappingURL=profile.d.ts.map
30
+ //# sourceMappingURL=project.d.ts.map