naro-cli 0.22.0 → 0.24.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/README.md +58 -0
- package/dist/args.d.ts +238 -0
- package/dist/{chunk-MBSO3K6L.js → chunk-XJIWWZ2T.js} +2264 -372
- package/dist/commands/confirm.d.ts +12 -0
- package/dist/commands/db.d.ts +32 -0
- package/dist/commands/migration.d.ts +78 -0
- package/dist/context.d.ts +21 -0
- package/dist/data-environment.d.ts +36 -0
- package/dist/deploy.d.ts +1 -0
- package/dist/deploy.js +5 -1
- package/dist/env-services.d.ts +44 -0
- package/dist/functions/deploy.d.ts +70 -0
- package/dist/functions/retry.d.ts +16 -0
- package/dist/index.js +1743 -721
- package/dist/version.d.ts +6 -0
- package/package.json +11 -5
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CommandContext } from "../context";
|
|
2
|
+
/**
|
|
3
|
+
* Whether a question could be put to anyone: not with `--json`, and not
|
|
4
|
+
* without a terminal. Only `--yes` answers it then (`confirmOrAbort`).
|
|
5
|
+
*/
|
|
6
|
+
export declare function cannotAsk(context: CommandContext): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Gate for destructive actions. Throws instead of silently returning so a
|
|
9
|
+
* declined/unavailable confirmation is a real failure (exit 1, structured
|
|
10
|
+
* JSON) — CI must never read "nothing happened" as success.
|
|
11
|
+
*/
|
|
12
|
+
export declare function confirmOrAbort(context: CommandContext, question: string): Promise<void>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CommandContext } from "../context";
|
|
2
|
+
/** How `db query` sees stdin — injectable so tests never touch the real one. */
|
|
3
|
+
export interface DbQueryDeps {
|
|
4
|
+
stdinIsTty(): boolean;
|
|
5
|
+
readStdin(): Promise<string>;
|
|
6
|
+
}
|
|
7
|
+
/** `512 B`, `1.5 KB`, `2.0 GB` — binary sizes, as `db dump` prints the file it wrote. */
|
|
8
|
+
export declare function humanSize(bytes: number): string;
|
|
9
|
+
/**
|
|
10
|
+
* `10 GB`, `100 KB`, `12.29 KB` — Cloudflare states D1's limits in decimal
|
|
11
|
+
* units, so `db info` prints them that way (PR D). Two decimals, rounded; a
|
|
12
|
+
* value that rounds to 1000 moves to the next unit (`999,995 B` is `1 MB`,
|
|
13
|
+
* not `1000 KB` — user review 3).
|
|
14
|
+
*/
|
|
15
|
+
export declare function decimalSize(bytes: number): string;
|
|
16
|
+
/**
|
|
17
|
+
* A database's size on the `Size:` and `Storage:` lines. From 1 GB, decimal
|
|
18
|
+
* gigabytes FLOORED to two decimals: the 8 GB warning and the 10 GB limit are
|
|
19
|
+
* `>=` comparisons, and rounding showed `10.00 GB / 10 GB` for 9,996,000,000
|
|
20
|
+
* bytes that still took writes, and `8.00 GB` with no warning (user review
|
|
21
|
+
* 3). Under 1 GB, decimalSize — a new database is `12.29 KB`, not `0.00 GB`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function storageSize(bytes: number): string;
|
|
24
|
+
/** `naro db info` — provider, status, size, table count and the limits. */
|
|
25
|
+
export declare function runDbInfo(context: CommandContext): Promise<void>;
|
|
26
|
+
/** `naro db enable` — idempotent: creates the D1 if the project has none. */
|
|
27
|
+
export declare function runDbEnable(context: CommandContext): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* `naro db query <sql> | --file <path> | (stdin)` — runs the batch through the
|
|
30
|
+
* control plane, which classifies it, checks the role and executes it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function runDbQuery(context: CommandContext, deps?: DbQueryDeps): Promise<void>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type MigrationInfo, type NaroClient } from "@next-deploy/sdk";
|
|
2
|
+
import type { CommandContext } from "../context";
|
|
3
|
+
import type { DbQueryDeps } from "./db";
|
|
4
|
+
/** `--dir` wins; otherwise `<--root>/migrations` (or `./migrations`). */
|
|
5
|
+
export declare function migrationsDir(context: CommandContext): string;
|
|
6
|
+
/** `relative()` returns "" for the cwd itself; `migration list` prints "." instead. */
|
|
7
|
+
export declare function relativeToCwd(context: CommandContext, path: string): string;
|
|
8
|
+
/** The ledger, or [] while the project has no database yet — a push creates it. */
|
|
9
|
+
export declare function appliedMigrations(client: NaroClient, projectId: string): Promise<MigrationInfo[]>;
|
|
10
|
+
export declare function printable(text: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Whether `printable` shows the text as it is. Text a command takes back —
|
|
13
|
+
* a CREATE POLICY statement, a condition — must not be printed with `?` in
|
|
14
|
+
* a character's place: pasted back, it would say something else (PR #74
|
|
15
|
+
* 리뷰 L2). What is not printable is not printed; `--json` has every byte.
|
|
16
|
+
*/
|
|
17
|
+
export declare function isPrintable(text: string): boolean;
|
|
18
|
+
export declare const UNSHOWN_WARNING_LINE = "(a line of this warning has characters the terminal cannot show; run with --json to see it exactly)";
|
|
19
|
+
/**
|
|
20
|
+
* A server warning as terminal lines: `⚠ ` and its first line, then each
|
|
21
|
+
* further line indented. A warning can carry SQL to paste back — the
|
|
22
|
+
* policies a table rebuild took, one statement a line (P2-7b) — so a line
|
|
23
|
+
* break starts a new line rather than becoming a `?`; every other control
|
|
24
|
+
* character does in the first line. A further line it cannot show as it is
|
|
25
|
+
* is not printed at all — pasted back with a `?` in a character's place, it
|
|
26
|
+
* would say something else (as `naro policy list` holds a CREATE POLICY to,
|
|
27
|
+
* PR #74 리뷰 L2) — and `--json` has it exactly.
|
|
28
|
+
*/
|
|
29
|
+
export declare function warningLines(message: string): string[];
|
|
30
|
+
/** Something said about one migration file: a server warning, a refusal. */
|
|
31
|
+
export interface MigrationMessage {
|
|
32
|
+
migration: string;
|
|
33
|
+
message: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* What the control plane would refuse in each file before it runs any of
|
|
37
|
+
* it (P2-7b) — a policy statement its parser cannot read, DISABLE ROW LEVEL
|
|
38
|
+
* SECURITY, GRANT, REVOKE — said the way the server says it: its own
|
|
39
|
+
* planner over its own split. A file the split cannot read is left to the
|
|
40
|
+
* server, which says why. The guards (is the table there, is the policy)
|
|
41
|
+
* need the database and are not run.
|
|
42
|
+
*/
|
|
43
|
+
export declare function policyProblems(dir: string, names: string[]): MigrationMessage[];
|
|
44
|
+
/**
|
|
45
|
+
* `naro migration new <name>` — creates `migrations/NNNN_<name>.sql` and
|
|
46
|
+
* never overwrites an existing file. No network: the file is all this writes.
|
|
47
|
+
*/
|
|
48
|
+
export declare function runMigrationNew(context: CommandContext, deps?: DbQueryDeps): Promise<void>;
|
|
49
|
+
/** `naro migration list` — local files against the `d1_migrations` ledger. */
|
|
50
|
+
export declare function runMigrationList(context: CommandContext): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Applies `names` from `dir` in order and answers with the ones that went
|
|
53
|
+
* through, and what the server warned about each (P2-7b: what their
|
|
54
|
+
* policies need — on stderr as `⚠` lines as they come). Each file is
|
|
55
|
+
* applied all-or-nothing (D1 runs it as one transaction); a failure stops
|
|
56
|
+
* the run and the files before it stay applied. Shared with `db reset`,
|
|
57
|
+
* which re-applies the same way after clearing the ledger — the apply loop
|
|
58
|
+
* exists once.
|
|
59
|
+
*/
|
|
60
|
+
export declare function applyMigrationFiles(context: CommandContext, client: NaroClient, projectId: string, dir: string, names: string[]): Promise<{
|
|
61
|
+
applied: string[];
|
|
62
|
+
warnings: MigrationMessage[];
|
|
63
|
+
}>;
|
|
64
|
+
/**
|
|
65
|
+
* `naro migration repair <name> --status applied|reverted` — edits the
|
|
66
|
+
* `d1_migrations` ledger and nothing else. It never runs the migration's SQL:
|
|
67
|
+
* this is for a ledger that stopped matching the database (a record step that
|
|
68
|
+
* failed after the file ran, a `db pull` baseline being taken back). To run
|
|
69
|
+
* SQL, use `db push`.
|
|
70
|
+
*/
|
|
71
|
+
export declare function runMigrationRepair(context: CommandContext): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* `naro migration up` (= `naro db push`) — applies every pending migration in
|
|
74
|
+
* order. Each file is applied all-or-nothing (D1 runs it as one transaction);
|
|
75
|
+
* a failure rolls that file back and stops the run, and the files before it
|
|
76
|
+
* stay applied (see the ledger via `migration list`).
|
|
77
|
+
*/
|
|
78
|
+
export declare function runMigrationUp(context: CommandContext): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { NaroClient, type ProjectLink, type ResolvedAuth } from "@next-deploy/sdk";
|
|
2
|
+
import type { GlobalFlags } from "./args";
|
|
3
|
+
import { Output } from "./output";
|
|
4
|
+
import { type Prompter } from "./prompt";
|
|
5
|
+
export interface CommandContext {
|
|
6
|
+
cwd: string;
|
|
7
|
+
flags: GlobalFlags;
|
|
8
|
+
/** Positional args after the (sub)command, e.g. the KEY in `env add KEY`. */
|
|
9
|
+
positionals: string[];
|
|
10
|
+
output: Output;
|
|
11
|
+
prompter: Prompter;
|
|
12
|
+
/** Returns the first `count` positionals or fails with a usage message. */
|
|
13
|
+
positionalOrThrow(count: number, usage: string): string[];
|
|
14
|
+
/** Lazily authenticates; interactive login only in a TTY without --json/--yes. */
|
|
15
|
+
auth(): Promise<ResolvedAuth>;
|
|
16
|
+
client(): Promise<NaroClient>;
|
|
17
|
+
/** Resolves the target project id: --project flag > .naro/project.json. */
|
|
18
|
+
resolveProjectId(): Promise<string>;
|
|
19
|
+
projectLink(): ProjectLink | null;
|
|
20
|
+
}
|
|
21
|
+
export declare function createContext(flags: GlobalFlags, positionals?: string[]): CommandContext;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type NaroClient } from "@next-deploy/sdk";
|
|
2
|
+
import type { GlobalFlags } from "./args";
|
|
3
|
+
import type { CommandContext } from "./context";
|
|
4
|
+
export declare const DATA_ENV_MESSAGES: {
|
|
5
|
+
readonly name: "--env must be production or an environment name.";
|
|
6
|
+
readonly preview: "--env preview has no data of its own: preview deployments use production's. Drop --env, or name an environment.";
|
|
7
|
+
readonly projectWide: (command: string) => string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* The environment a data command acts on, or null for production. Throws for
|
|
11
|
+
* a value that names no environment — `preview`, an empty one, one that is no
|
|
12
|
+
* slug — before any request; whether the environment exists is the control
|
|
13
|
+
* plane's to answer (`ENVIRONMENT_NOT_FOUND`).
|
|
14
|
+
*/
|
|
15
|
+
export declare function dataEnvironment(flags: Pick<GlobalFlags, "env">): string | null;
|
|
16
|
+
/** The command's client, acting on the data `--env` names. */
|
|
17
|
+
export declare function dataClient(context: Pick<CommandContext, "client" | "flags">): Promise<NaroClient>;
|
|
18
|
+
/** What a prompt and a result line call the database a command acts on. */
|
|
19
|
+
export declare function databaseName(environment: string | null): string;
|
|
20
|
+
/**
|
|
21
|
+
* The command that puts the backend (back) on: `naro backend enable` for
|
|
22
|
+
* production's, and for an environment `naro environments provision <slug>` —
|
|
23
|
+
* backend enable is project-wide and the router refuses it under `--env`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function enableBackendCommand(environment: string | null): string;
|
|
26
|
+
/** ` --env <name>` for a hint that names a command; nothing for production. */
|
|
27
|
+
export declare function envFlag(environment: string | null): string;
|
|
28
|
+
/**
|
|
29
|
+
* A hint, or a message the CLI prints, as the environment's: every command
|
|
30
|
+
* it names acts on that environment (`environmentGuidance`, PR #343 review
|
|
31
|
+
* 2) — a data command takes ` --env <slug>`, a backend repair becomes
|
|
32
|
+
* `naro environments provision <slug>`, and the backend's switch is said to
|
|
33
|
+
* be production's, which every environment follows. Production's text
|
|
34
|
+
* passes through byte for byte.
|
|
35
|
+
*/
|
|
36
|
+
export declare function forEnvironment(text: string, environment: string | null): string;
|
package/dist/deploy.d.ts
CHANGED
|
@@ -112,3 +112,4 @@ export interface DeployResult {
|
|
|
112
112
|
* Shared by `naro deploy` and the MCP `deploy` tool.
|
|
113
113
|
*/
|
|
114
114
|
export declare function deployDirectory(sourceDir: string, options: DeployOptions, log: (message: string) => void): Promise<DeployResult>;
|
|
115
|
+
export { assertFunctionsNode, deployFunctions, type DeployedFunction, type DeployFunctionsOptions, type DeployFunctionsResult, } from "./functions/deploy";
|
package/dist/deploy.js
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
assertFunctionsNode,
|
|
3
4
|
deployDirectory,
|
|
5
|
+
deployFunctions,
|
|
4
6
|
eventsObserver,
|
|
5
7
|
localDependencies,
|
|
6
8
|
parseDotenv,
|
|
7
9
|
readProjectEnv,
|
|
8
10
|
slugify,
|
|
9
11
|
workingTreeNotices
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-XJIWWZ2T.js";
|
|
11
13
|
import "./chunk-K3NQKI34.js";
|
|
12
14
|
export {
|
|
15
|
+
assertFunctionsNode,
|
|
13
16
|
deployDirectory,
|
|
17
|
+
deployFunctions,
|
|
14
18
|
eventsObserver,
|
|
15
19
|
localDependencies,
|
|
16
20
|
parseDotenv,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which outside service the env keys a deploy is missing belong to, and where
|
|
3
|
+
* that service keeps their values.
|
|
4
|
+
*
|
|
5
|
+
* The deploy already works out the names it needs by scanning the source
|
|
6
|
+
* (`scanEnvReferences`), but a list of bare names only helps someone who
|
|
7
|
+
* already knows where each value comes from. The first thing anyone moving an
|
|
8
|
+
* existing app here hits is exactly that gap: the app talks to a Supabase
|
|
9
|
+
* project that is already running, and the deploy asks for
|
|
10
|
+
* `NEXT_PUBLIC_SUPABASE_URL` without saying it is asking for something they
|
|
11
|
+
* have in another browser tab.
|
|
12
|
+
*
|
|
13
|
+
* This only names the service and the screen its values are on. Nothing here
|
|
14
|
+
* reaches Supabase's API, stores a token or creates anything there — the
|
|
15
|
+
* values still arrive the way every other env var does, from `.env.local`
|
|
16
|
+
* with the user's consent or from `naro env add`.
|
|
17
|
+
*/
|
|
18
|
+
/** The key without that prefix. The longest match wins, so order is free. */
|
|
19
|
+
export declare function bareEnvKey(key: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* The given keys that name a Supabase project, in the order given.
|
|
22
|
+
*
|
|
23
|
+
* Everything Supabase hands a project is `SUPABASE_*`: the project URL, the
|
|
24
|
+
* publishable and secret keys, the legacy `anon` and `service_role` keys, the
|
|
25
|
+
* JWT secret, the account's access token, the database password. Matching the
|
|
26
|
+
* prefix rather than a fixed list keeps a name we have not seen — Supabase
|
|
27
|
+
* renamed its keys once already — from dropping out. A name that merely
|
|
28
|
+
* contains the word (`MY_SUPABASE_URL`) is not one of theirs.
|
|
29
|
+
*
|
|
30
|
+
* Belonging to Supabase is not the same as being on one of the screens
|
|
31
|
+
* `envServiceHints` knows; that function decides which of these it can place.
|
|
32
|
+
*/
|
|
33
|
+
export declare function supabaseKeys(keys: readonly string[]): string[];
|
|
34
|
+
/**
|
|
35
|
+
* Lines naming the service the missing keys belong to, to print under the
|
|
36
|
+
* "env var(s) needed" line. Empty when nothing is recognised, which is the
|
|
37
|
+
* common case — this adds to that line, it never replaces it.
|
|
38
|
+
*
|
|
39
|
+
* Adding a second service means another `supabaseKeys`-shaped filter and
|
|
40
|
+
* another branch here. It is deliberately not a table of services yet: the
|
|
41
|
+
* useful half of each entry is the screen its values are on, and that is
|
|
42
|
+
* prose we can only write for a dashboard we have actually checked.
|
|
43
|
+
*/
|
|
44
|
+
export declare function envServiceHints(keys: readonly string[]): string[];
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { bootCheck, bundleFunction } from "@next-deploy/functions-bundler";
|
|
2
|
+
import { type FunctionUploadTarget, type NaroClient } from "@next-deploy/sdk";
|
|
3
|
+
export declare const PER_FUNCTION_CODES: ReadonlySet<string>;
|
|
4
|
+
export interface DeploySteps {
|
|
5
|
+
bundle: typeof bundleFunction;
|
|
6
|
+
boot: typeof bootCheck;
|
|
7
|
+
}
|
|
8
|
+
export interface DeployFunctionsOptions {
|
|
9
|
+
client: NaroClient;
|
|
10
|
+
projectId: string;
|
|
11
|
+
/** Where relative --root, --dir and --import-map start. */
|
|
12
|
+
cwd: string;
|
|
13
|
+
root?: string | undefined;
|
|
14
|
+
dir?: string | undefined;
|
|
15
|
+
/** Empty or absent: every function in the folder. */
|
|
16
|
+
names?: readonly string[] | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Wins over function.json either way: the CLI's `--no-verify-jwt` passes
|
|
19
|
+
* false, naro-mcp's `verifyJwt` passes what it was given — true is never
|
|
20
|
+
* dropped for function.json's false (PR #353 review).
|
|
21
|
+
*/
|
|
22
|
+
verifyJwt?: boolean | undefined;
|
|
23
|
+
importMap?: string | undefined;
|
|
24
|
+
skipCheck?: boolean | undefined;
|
|
25
|
+
cliVersion?: string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Tries after FUNCTIONS_RATE_LIMITED (3 by default, up to 300 s each).
|
|
28
|
+
* naro-mcp passes 0: one tool call must end within two minutes (plan Q6),
|
|
29
|
+
* so it hands the error and its retryAfter back instead (final review A-M4).
|
|
30
|
+
*/
|
|
31
|
+
rateLimitRetries?: number | undefined;
|
|
32
|
+
/** Tries after FUNCTION_BUSY (3 by default, 5 s each); naro-mcp passes 0. */
|
|
33
|
+
busyRetries?: number | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* The whole run's limit — naro-mcp passes 120 s, so one tool call ends
|
|
36
|
+
* within about two minutes after the bundle (plan Q6, PR #353 review). The
|
|
37
|
+
* secrets list, the boot check, making the version, the PUT and the deploy
|
|
38
|
+
* each get only what is left (never more than their own limit), and none
|
|
39
|
+
* starts once it is reached. Absent: each step's own limit, as for the CLI.
|
|
40
|
+
*/
|
|
41
|
+
deadlineMs?: number | undefined;
|
|
42
|
+
/** Tests move the clock; `Date.now` by default. */
|
|
43
|
+
now?: (() => number) | undefined;
|
|
44
|
+
log?: ((line: string) => void) | undefined;
|
|
45
|
+
/** The presigned PUT's fetch; the global one by default. */
|
|
46
|
+
fetch?: typeof fetch | undefined;
|
|
47
|
+
sleep?: ((ms: number) => Promise<void>) | undefined;
|
|
48
|
+
/** Tests stand in for esbuild and workerd. */
|
|
49
|
+
steps?: DeploySteps | undefined;
|
|
50
|
+
/** `process.versions.node` by default. */
|
|
51
|
+
nodeVersion?: string | undefined;
|
|
52
|
+
}
|
|
53
|
+
export interface DeployedFunction {
|
|
54
|
+
name: string;
|
|
55
|
+
version: number | null;
|
|
56
|
+
url: string;
|
|
57
|
+
verifyJwt: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface DeployFunctionsResult {
|
|
60
|
+
deployed: DeployedFunction[];
|
|
61
|
+
}
|
|
62
|
+
export declare function assertFunctionsNode(version?: string): void;
|
|
63
|
+
/**
|
|
64
|
+
* The presigned PUT's time limit: as long as the URL still works (60 seconds
|
|
65
|
+
* at least) plus 30 seconds, so a hung connection ends instead of waiting
|
|
66
|
+
* for the OS; 15 minutes when the answer has no usable `expiresAt`.
|
|
67
|
+
*/
|
|
68
|
+
export declare function uploadTimeoutMs(upload: FunctionUploadTarget, now?: number): number;
|
|
69
|
+
export declare const deadlineReached: (name: string, deadlineMs: number) => string;
|
|
70
|
+
export declare function deployFunctions(options: DeployFunctionsOptions): Promise<DeployFunctionsResult>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type NaroError } from "@next-deploy/sdk";
|
|
2
|
+
export declare const FUNCTION_RETRIES = 3;
|
|
3
|
+
export declare const FUNCTION_BUSY_WAIT_MS = 5000;
|
|
4
|
+
export declare const FUNCTION_RATE_LIMIT_DEFAULT_SECONDS = 60;
|
|
5
|
+
export declare const FUNCTION_RATE_LIMIT_MAX_SECONDS = 300;
|
|
6
|
+
export declare function defaultSleep(ms: number): Promise<void>;
|
|
7
|
+
/** `details.retryAfter` rounded up, at most 300; 60 when it is missing. */
|
|
8
|
+
export declare function retryAfterSeconds(error: NaroError): number;
|
|
9
|
+
export declare function withFunctionRetries<T>(op: () => Promise<T>, name: string, io: {
|
|
10
|
+
sleep: (ms: number) => Promise<void>;
|
|
11
|
+
log: (line: string) => void;
|
|
12
|
+
/** Tries after FUNCTIONS_RATE_LIMITED: 3 by default; naro-mcp passes 0. */
|
|
13
|
+
rateLimitRetries?: number | undefined;
|
|
14
|
+
/** Tries after FUNCTION_BUSY: 3 by default; naro-mcp passes 0. */
|
|
15
|
+
busyRetries?: number | undefined;
|
|
16
|
+
}): Promise<T>;
|