@duyquangnvx/agent-cli 0.2.1 → 0.4.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/CHANGELOG.md +71 -0
- package/dist/abort.d.ts +2 -0
- package/dist/abort.js +11 -0
- package/dist/abort.js.map +1 -0
- package/dist/agent-cli.d.ts +17 -0
- package/dist/agent-cli.js +28 -0
- package/dist/agent-cli.js.map +1 -0
- package/dist/agent.d.ts +4 -0
- package/dist/agent.js +14 -0
- package/dist/agent.js.map +1 -0
- package/dist/built-ins.d.ts +1 -2
- package/dist/built-ins.js +0 -10
- package/dist/built-ins.js.map +1 -1
- package/dist/clock.d.ts +10 -0
- package/dist/clock.js +35 -0
- package/dist/clock.js.map +1 -0
- package/dist/command-spec.js +12 -1
- package/dist/command-spec.js.map +1 -1
- package/dist/confirm.d.ts +2 -0
- package/dist/confirm.js +4 -2
- package/dist/confirm.js.map +1 -1
- package/dist/cost.d.ts +9 -0
- package/dist/cost.js +16 -0
- package/dist/cost.js.map +1 -0
- package/dist/define.d.ts +145 -32
- package/dist/define.js +9 -2
- package/dist/define.js.map +1 -1
- package/dist/doctor-rows.d.ts +2 -2
- package/dist/doctor-rows.js +45 -5
- package/dist/doctor-rows.js.map +1 -1
- package/dist/doctor.d.ts +2 -1
- package/dist/doctor.js +31 -29
- package/dist/doctor.js.map +1 -1
- package/dist/errno.d.ts +15 -0
- package/dist/errno.js +50 -0
- package/dist/errno.js.map +1 -0
- package/dist/error-table.d.ts +34 -0
- package/dist/error-table.js +10 -0
- package/dist/error-table.js.map +1 -0
- package/dist/errors.d.ts +17 -12
- package/dist/errors.js +59 -38
- package/dist/errors.js.map +1 -1
- package/dist/execute.d.ts +7 -3
- package/dist/execute.js +38 -5
- package/dist/execute.js.map +1 -1
- package/dist/exit-classes.d.ts +31 -0
- package/dist/exit-classes.js +108 -0
- package/dist/exit-classes.js.map +1 -0
- package/dist/files.d.ts +31 -0
- package/dist/files.js +95 -0
- package/dist/files.js.map +1 -0
- package/dist/flags.d.ts +21 -3
- package/dist/flags.js +41 -4
- package/dist/flags.js.map +1 -1
- package/dist/global-install.d.ts +1 -0
- package/dist/global-install.js +2 -2
- package/dist/global-install.js.map +1 -1
- package/dist/help-json.d.ts +2 -1
- package/dist/help-json.js +18 -2
- package/dist/help-json.js.map +1 -1
- package/dist/help.d.ts +4 -2
- package/dist/help.js +62 -6
- package/dist/help.js.map +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/input.d.ts +4 -0
- package/dist/input.js +24 -2
- package/dist/input.js.map +1 -1
- package/dist/interrupts.d.ts +17 -3
- package/dist/interrupts.js +43 -25
- package/dist/interrupts.js.map +1 -1
- package/dist/invocation.d.ts +7 -2
- package/dist/invocation.js +11 -3
- package/dist/invocation.js.map +1 -1
- package/dist/list.d.ts +1 -0
- package/dist/list.js +4 -2
- package/dist/list.js.map +1 -1
- package/dist/npm.d.ts +5 -0
- package/dist/npm.js +19 -17
- package/dist/npm.js.map +1 -1
- package/dist/output.d.ts +18 -4
- package/dist/output.js +65 -23
- package/dist/output.js.map +1 -1
- package/dist/outputs.d.ts +11 -0
- package/dist/outputs.js +22 -0
- package/dist/outputs.js.map +1 -0
- package/dist/package-root.d.ts +6 -0
- package/dist/package-root.js +17 -0
- package/dist/package-root.js.map +1 -0
- package/dist/parse-input.d.ts +2 -0
- package/dist/parse-input.js +41 -8
- package/dist/parse-input.js.map +1 -1
- package/dist/planned.d.ts +6 -0
- package/dist/planned.js +5 -0
- package/dist/planned.js.map +1 -0
- package/dist/project.d.ts +4 -0
- package/dist/project.js +48 -0
- package/dist/project.js.map +1 -0
- package/dist/route.d.ts +2 -0
- package/dist/route.js +2 -1
- package/dist/route.js.map +1 -1
- package/dist/run-cli.d.ts +24 -5
- package/dist/run-cli.js +126 -18
- package/dist/run-cli.js.map +1 -1
- package/dist/run-process.js +6 -3
- package/dist/run-process.js.map +1 -1
- package/dist/secrets.d.ts +19 -0
- package/dist/secrets.js +32 -0
- package/dist/secrets.js.map +1 -0
- package/dist/spawn.d.ts +29 -0
- package/dist/spawn.js +79 -0
- package/dist/spawn.js.map +1 -0
- package/dist/spill.d.ts +16 -0
- package/dist/spill.js +65 -0
- package/dist/spill.js.map +1 -0
- package/dist/style.js +5 -12
- package/dist/style.js.map +1 -1
- package/dist/tail.d.ts +2 -0
- package/dist/tail.js +5 -0
- package/dist/tail.js.map +1 -0
- package/dist/tools.d.ts +6 -0
- package/dist/tools.js +69 -0
- package/dist/tools.js.map +1 -0
- package/dist/update.js +7 -7
- package/dist/update.js.map +1 -1
- package/dist/usage.d.ts +3 -0
- package/dist/usage.js +6 -3
- package/dist/usage.js.map +1 -1
- package/package.json +1 -1
package/dist/define.d.ts
CHANGED
|
@@ -1,83 +1,186 @@
|
|
|
1
1
|
import type * as z from "zod";
|
|
2
|
+
import type { Clock } from "./clock.js";
|
|
3
|
+
import type { ErrorTable } from "./error-table.js";
|
|
4
|
+
import type { Files } from "./files.js";
|
|
2
5
|
import { type StandardFlagName } from "./flags.js";
|
|
3
6
|
import type { Page, PageInput, RowSchema } from "./list.js";
|
|
7
|
+
import type { Secret } from "./secrets.js";
|
|
8
|
+
import type { ContextSpawn } from "./spawn.js";
|
|
4
9
|
import type { Style } from "./style.js";
|
|
5
10
|
export type Env = Readonly<Record<string, string | undefined>>;
|
|
6
11
|
export interface Example {
|
|
7
12
|
readonly command: string;
|
|
8
13
|
readonly note: string;
|
|
9
14
|
}
|
|
10
|
-
export interface
|
|
15
|
+
export interface PollOptions {
|
|
16
|
+
readonly intervalMs: number;
|
|
17
|
+
}
|
|
18
|
+
export interface Context<Ports = unknown, Settings = unknown> {
|
|
11
19
|
readonly cwd: string;
|
|
12
20
|
readonly env: Env;
|
|
21
|
+
/** The folder holding this CLI's `package.json`, for reading files shipped in the package. */
|
|
22
|
+
readonly packageRoot: string;
|
|
13
23
|
/** Writes a plain line to stderr, for long work; it never reaches stdout. */
|
|
14
24
|
progress(message: string): void;
|
|
15
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Aborts when the run is interrupted (Ctrl-C) or hits the Command's run deadline; pass it to
|
|
27
|
+
* `fetch` and child processes.
|
|
28
|
+
*/
|
|
16
29
|
readonly signal: AbortSignal;
|
|
17
30
|
/** Returns a signal that also aborts, with `DEADLINE_EXCEEDED`, once `ms` have passed. */
|
|
18
31
|
deadline(ms: number): AbortSignal;
|
|
19
|
-
/**
|
|
20
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Runs a step that must not stop halfway: the run waits for it even when stopped, and then
|
|
34
|
+
* prints `finishing <name>` on stderr.
|
|
35
|
+
*/
|
|
36
|
+
runToEnd<T>(name: string, work: () => Promise<T>): Promise<T>;
|
|
37
|
+
/**
|
|
38
|
+
* Records the data done so far, in the shape `render` takes; the last call wins. A run that then
|
|
39
|
+
* fails, hits a deadline or is interrupted prints it; a returned value replaces it.
|
|
40
|
+
*/
|
|
41
|
+
deliver(data: unknown): void;
|
|
42
|
+
/** The outside systems the CLI declared with `agentCli<Ports>()`. */
|
|
43
|
+
readonly ports: Ports;
|
|
44
|
+
/**
|
|
45
|
+
* Reads, lists, removes and atomically writes files and folders, paths resolved against `cwd`; a
|
|
46
|
+
* failure fails the run with `PERMISSION_DENIED`, `DISK_FULL`, `NOT_FOUND` or `ALREADY_EXISTS`.
|
|
47
|
+
*/
|
|
48
|
+
readonly files: Files;
|
|
49
|
+
/**
|
|
50
|
+
* Runs a child process, killed when the run is interrupted; `check: true` fails the run with
|
|
51
|
+
* `TOOL_FAILED` and the tail of its stderr when it exits non-zero.
|
|
52
|
+
*/
|
|
53
|
+
readonly spawn: ContextSpawn;
|
|
54
|
+
/** Deadlines, `poll` and the grace period after an interrupt run on it. */
|
|
55
|
+
readonly clock: Clock;
|
|
56
|
+
/**
|
|
57
|
+
* Calls `check` until it returns a value other than `undefined`, sleeping `intervalMs` between
|
|
58
|
+
* calls; it stops when `ctx.signal` aborts.
|
|
59
|
+
*/
|
|
60
|
+
poll<T>(check: () => T | undefined | Promise<T | undefined>, options: PollOptions): Promise<T>;
|
|
61
|
+
/** The Project file found by walking up from `cwd`, parsed by the CLI's schema. */
|
|
62
|
+
readonly project: Project<Settings> | undefined;
|
|
63
|
+
/** Reads `name` from the env, then from `$XDG_CONFIG_HOME/<cli>/.env`, with where it came from. */
|
|
64
|
+
secret(name: string): Secret | undefined;
|
|
65
|
+
/** Whether the caller passed `--force`; always false for a Command without it. */
|
|
66
|
+
readonly force: boolean;
|
|
67
|
+
}
|
|
68
|
+
/** The slice of `Context` the ports factory receives, which exists before the ports do. */
|
|
69
|
+
export type PortsBase = Pick<Context, "env" | "cwd" | "packageRoot" | "secret" | "spawn" | "files" | "clock" | "signal">;
|
|
70
|
+
export interface Project<Settings = unknown> {
|
|
71
|
+
/** The folder holding the Project file. */
|
|
72
|
+
readonly root: string;
|
|
73
|
+
readonly settings: Settings;
|
|
74
|
+
}
|
|
75
|
+
/** Names the Project file and the schema its JSON must match. */
|
|
76
|
+
export interface ProjectFile<Settings = unknown> {
|
|
77
|
+
readonly file: string;
|
|
78
|
+
readonly schema: z.ZodType<Settings>;
|
|
21
79
|
}
|
|
22
80
|
export type InputSchema = z.ZodObject;
|
|
81
|
+
export type NoPorts = Record<never, never>;
|
|
23
82
|
export type NoInput = z.ZodObject<Record<never, never>>;
|
|
24
83
|
type OutputOf<Schema extends InputSchema> = keyof Schema["shape"] extends never ? unknown : z.output<Schema>;
|
|
25
84
|
type Flatten<T> = {
|
|
26
85
|
[K in keyof T]: T[K];
|
|
27
86
|
};
|
|
28
87
|
export type Input<Args extends InputSchema, Options extends InputSchema> = Flatten<OutputOf<Args> & OutputOf<Options>>;
|
|
88
|
+
/** A program a Command runs, looked up on `PATH` before the Command starts. */
|
|
89
|
+
export interface Tool {
|
|
90
|
+
readonly name: string;
|
|
91
|
+
/** The command that installs it, shown when it is missing. */
|
|
92
|
+
readonly install: string;
|
|
93
|
+
}
|
|
94
|
+
/** Bounds a whole run, so a long run stops before the caller's own timeout and can be continued. */
|
|
95
|
+
export interface RunDeadline<In> {
|
|
96
|
+
readonly ms: number;
|
|
97
|
+
/** The command line that continues the work, given as the hint of the run's `DEADLINE_EXCEEDED`. */
|
|
98
|
+
resume(input: In): string;
|
|
99
|
+
}
|
|
29
100
|
interface CommandBase<Args extends InputSchema, Options extends InputSchema> {
|
|
30
101
|
readonly name: string;
|
|
31
102
|
readonly description: string;
|
|
32
103
|
readonly examples: readonly Example[];
|
|
104
|
+
/** Missing ones fail the run with `TOOL_MISSING` before `plan`, `run` or `start`. */
|
|
105
|
+
readonly tools?: readonly Tool[];
|
|
33
106
|
readonly args?: Args;
|
|
34
107
|
readonly options?: Options;
|
|
108
|
+
/** At `ms`, `ctx.signal` aborts and the run ends with `DEADLINE_EXCEEDED` (exit 75). */
|
|
109
|
+
readonly deadline?: RunDeadline<Input<Args, Options>>;
|
|
35
110
|
}
|
|
36
111
|
interface UnpagedCommand<Args extends InputSchema, Options extends InputSchema> extends CommandBase<Args, Options> {
|
|
37
112
|
readonly output?: z.ZodType;
|
|
38
113
|
readonly rows?: never;
|
|
39
114
|
}
|
|
40
|
-
export interface ReadCommand<Args extends InputSchema, Options extends InputSchema, Data> extends UnpagedCommand<Args, Options> {
|
|
115
|
+
export interface ReadCommand<Args extends InputSchema, Options extends InputSchema, Data, Ports = unknown, Settings = unknown> extends UnpagedCommand<Args, Options> {
|
|
41
116
|
readonly effect: "read";
|
|
42
|
-
run(input: Input<Args, Options>, ctx: Context): Data | Promise<Data>;
|
|
117
|
+
run(input: Input<Args, Options>, ctx: Context<Ports, Settings>): Data | Promise<Data>;
|
|
43
118
|
render(data: Data, style: Style): string;
|
|
44
119
|
}
|
|
45
120
|
/** A read Command whose data is a page of rows; the Runtime adds `--limit`, `--cursor` and `--fields`. */
|
|
46
|
-
export interface ListCommand<Args extends InputSchema, Options extends InputSchema, Row extends RowSchema> extends CommandBase<Args, Options> {
|
|
121
|
+
export interface ListCommand<Args extends InputSchema, Options extends InputSchema, Row extends RowSchema, Ports = unknown, Settings = unknown> extends CommandBase<Args, Options> {
|
|
47
122
|
readonly effect: "read";
|
|
48
123
|
/** The schema of one row; the page's output schema is derived from it. */
|
|
49
124
|
readonly rows: Row;
|
|
50
125
|
readonly output?: never;
|
|
51
|
-
run(input: Flatten<OutputOf<Args> & OutputOf<Options> & PageInput>, ctx: Context): Page<z.output<Row>> | Promise<Page<z.output<Row>>>;
|
|
126
|
+
run(input: Flatten<OutputOf<Args> & OutputOf<Options> & PageInput>, ctx: Context<Ports, Settings>): Page<z.output<Row>> | Promise<Page<z.output<Row>>>;
|
|
52
127
|
render(page: Page<z.output<Row>>, style: Style): string;
|
|
53
128
|
}
|
|
54
129
|
/** A Command that changes something: `plan` has no side effects, `apply` performs the Plan. */
|
|
55
|
-
interface PlannedCommand<Args extends InputSchema, Options extends InputSchema, Plan, Data> extends UnpagedCommand<Args, Options> {
|
|
56
|
-
plan(input: Input<Args, Options>, ctx: Context): Plan | Promise<Plan>;
|
|
57
|
-
apply(plan: Plan, ctx: Context): Data | Promise<Data>;
|
|
130
|
+
interface PlannedCommand<Args extends InputSchema, Options extends InputSchema, Plan, Data, Ports, Settings> extends UnpagedCommand<Args, Options> {
|
|
131
|
+
plan(input: Input<Args, Options>, ctx: Context<Ports, Settings>): Plan | Promise<Plan>;
|
|
132
|
+
apply(plan: Plan, ctx: Context<Ports, Settings>): Data | Promise<Data>;
|
|
58
133
|
render(data: Data, style: Style): string;
|
|
59
134
|
renderPlan(plan: Plan, style: Style): string;
|
|
60
|
-
|
|
61
|
-
|
|
135
|
+
/**
|
|
136
|
+
* A sentence stating what the Plan costs, or undefined when it is cheap. Declaring it adds
|
|
137
|
+
* `--yes`, which a run must pass whenever it returns a sentence.
|
|
138
|
+
*/
|
|
139
|
+
cost?(plan: Plan): string | undefined;
|
|
140
|
+
/**
|
|
141
|
+
* The files the Plan writes, relative to `ctx.cwd`. Declaring it adds `--force`, without which
|
|
142
|
+
* a run refuses with `OUTPUT_EXISTS` when any of them exists.
|
|
143
|
+
*/
|
|
144
|
+
outputs?(plan: Plan): readonly string[];
|
|
145
|
+
/**
|
|
146
|
+
* What `--force` replaces for this Command, shown as the flag's help. Declaring it adds
|
|
147
|
+
* `--force`, which `plan` and `apply` read as `ctx.force`.
|
|
148
|
+
*/
|
|
149
|
+
readonly force?: string;
|
|
150
|
+
}
|
|
151
|
+
export interface WriteCommand<Args extends InputSchema, Options extends InputSchema, Plan, Data, Ports = unknown, Settings = unknown> extends PlannedCommand<Args, Options, Plan, Data, Ports, Settings> {
|
|
62
152
|
readonly effect: "write";
|
|
63
153
|
}
|
|
64
|
-
export interface DestructiveCommand<Args extends InputSchema, Options extends InputSchema, Plan, Data> extends PlannedCommand<Args, Options, Plan, Data> {
|
|
154
|
+
export interface DestructiveCommand<Args extends InputSchema, Options extends InputSchema, Plan, Data, Ports = unknown, Settings = unknown> extends PlannedCommand<Args, Options, Plan, Data, Ports, Settings> {
|
|
65
155
|
readonly effect: "destructive";
|
|
66
156
|
/** The value the caller must repeat as `--confirm`, naming the target the Plan changes. */
|
|
67
157
|
confirm(plan: Plan): string;
|
|
68
158
|
}
|
|
69
|
-
|
|
70
|
-
export interface
|
|
71
|
-
readonly
|
|
159
|
+
/** A Command that keeps running, such as a preview server, until Ctrl-C or SIGTERM stops it. */
|
|
160
|
+
export interface ServeCommand<Args extends InputSchema, Options extends InputSchema, Data, Ports = unknown, Settings = unknown> extends UnpagedCommand<Args, Options> {
|
|
161
|
+
readonly effect: "serve";
|
|
162
|
+
/** Starts the work and returns its data, which prints at once; stop the work on `ctx.signal`. */
|
|
163
|
+
start(input: Input<Args, Options>, ctx: Context<Ports, Settings>): Data | Promise<Data>;
|
|
164
|
+
render(data: Data, style: Style): string;
|
|
165
|
+
}
|
|
166
|
+
export type Command<Ports = unknown, Settings = unknown> = ReadCommand<InputSchema, InputSchema, unknown, Ports, Settings> | ListCommand<InputSchema, InputSchema, RowSchema, Ports, Settings> | WriteCommand<InputSchema, InputSchema, unknown, unknown, Ports, Settings> | DestructiveCommand<InputSchema, InputSchema, unknown, unknown, Ports, Settings> | ServeCommand<InputSchema, InputSchema, unknown, Ports, Settings>;
|
|
167
|
+
export interface Commands<Ports = unknown, Settings = unknown> {
|
|
168
|
+
readonly [key: string]: Command<Ports, Settings> | Commands<Ports, Settings>;
|
|
72
169
|
}
|
|
73
|
-
interface CliBase {
|
|
170
|
+
interface CliBase<Ports, Settings> {
|
|
74
171
|
readonly name: string;
|
|
75
172
|
readonly version: string;
|
|
76
|
-
/** The commit the build was made from, stamped by the bundler; `--version` prints it. */
|
|
173
|
+
/** The commit the build was made from and when, stamped by the bundler; `--version` prints it. */
|
|
77
174
|
readonly build?: {
|
|
78
175
|
readonly sha: string;
|
|
79
176
|
readonly dirty: boolean;
|
|
177
|
+
readonly builtAt: string;
|
|
80
178
|
};
|
|
179
|
+
/** Every code the CLI throws; a code missing here exits 70, as a bug. */
|
|
180
|
+
readonly errors?: ErrorTable;
|
|
181
|
+
/** Builds the real ports when a run first reads `ctx.ports`; `runCli`'s `ports` replaces them in a test. */
|
|
182
|
+
readonly ports?: (base: PortsBase) => Ports;
|
|
183
|
+
readonly project?: ProjectFile<Settings>;
|
|
81
184
|
}
|
|
82
185
|
/** Where an Agent CLI is published and how it is installed; `doctor` and `update` read it. */
|
|
83
186
|
export interface GlobalInstall {
|
|
@@ -92,14 +195,18 @@ export interface DoctorFinding {
|
|
|
92
195
|
readonly fix?: string;
|
|
93
196
|
}
|
|
94
197
|
/** A row the CLI adds to `doctor`, for a system it depends on. */
|
|
95
|
-
export interface DoctorCheck {
|
|
198
|
+
export interface DoctorCheck<Ports = unknown, Settings = unknown> {
|
|
96
199
|
readonly name: string;
|
|
97
|
-
/**
|
|
98
|
-
|
|
200
|
+
/** How long the row may take before it is reported as a problem; 10 s when absent. */
|
|
201
|
+
readonly deadlineMs?: number;
|
|
202
|
+
/** A problem in an advisory row shows but never fails `doctor`. */
|
|
203
|
+
readonly advisory?: boolean;
|
|
204
|
+
/** `ctx.signal` aborts at the row's deadline. A thrown error is reported as this row's problem. */
|
|
205
|
+
run(ctx: Context<Ports, Settings>): DoctorFinding | Promise<DoctorFinding>;
|
|
99
206
|
}
|
|
100
207
|
/** An Agent CLI whose Commands are reached by name, as in `tool voices list`. */
|
|
101
|
-
export interface CommandTreeCli extends CliBase {
|
|
102
|
-
readonly commands: Commands
|
|
208
|
+
export interface CommandTreeCli<Ports = unknown, Settings = unknown> extends CliBase<Ports, Settings> {
|
|
209
|
+
readonly commands: Commands<Ports, Settings>;
|
|
103
210
|
readonly command?: never;
|
|
104
211
|
/** Absent for a CLI that is not published: `doctor` then checks no install or registry. */
|
|
105
212
|
readonly install?: GlobalInstall;
|
|
@@ -107,14 +214,14 @@ export interface CommandTreeCli extends CliBase {
|
|
|
107
214
|
readonly engines?: {
|
|
108
215
|
readonly node?: string;
|
|
109
216
|
};
|
|
110
|
-
readonly doctor?: readonly DoctorCheck[];
|
|
217
|
+
readonly doctor?: readonly DoctorCheck<Ports, Settings>[];
|
|
111
218
|
}
|
|
112
219
|
/** An Agent CLI that is one Command, run with no Command name, as in `tool <args>`. */
|
|
113
|
-
export interface SingleCommandCli extends CliBase {
|
|
114
|
-
readonly command: Command
|
|
220
|
+
export interface SingleCommandCli<Ports = unknown, Settings = unknown> extends CliBase<Ports, Settings> {
|
|
221
|
+
readonly command: Command<Ports, Settings>;
|
|
115
222
|
readonly commands?: never;
|
|
116
223
|
}
|
|
117
|
-
export type Cli = CommandTreeCli | SingleCommandCli
|
|
224
|
+
export type Cli<Ports = unknown, Settings = unknown> = CommandTreeCli<Ports, Settings> | SingleCommandCli<Ports, Settings>;
|
|
118
225
|
type Kebab<Key extends string> = Key extends `${infer Head}${infer Tail}` ? `${Head extends Lowercase<Head> ? Head : `-${Lowercase<Head>}`}${Kebab<Tail>}` : Key;
|
|
119
226
|
type Taken<Schema extends InputSchema> = Extract<Kebab<Extract<keyof Schema["shape"], string>>, StandardFlagName>;
|
|
120
227
|
type Unreserved<Schema extends InputSchema> = [Taken<Schema>] extends [never] ? unknown : {
|
|
@@ -124,9 +231,15 @@ interface WithoutStandardFlags<Args extends InputSchema, Options extends InputSc
|
|
|
124
231
|
readonly args?: Unreserved<Args>;
|
|
125
232
|
readonly options?: Unreserved<Options>;
|
|
126
233
|
}
|
|
127
|
-
|
|
128
|
-
export
|
|
129
|
-
|
|
130
|
-
|
|
234
|
+
/** Declares a Command whose `ctx.ports` is the CLI's `Ports`. */
|
|
235
|
+
export interface DefineCommand<Ports, Settings = unknown> {
|
|
236
|
+
<Row extends RowSchema, Args extends InputSchema = NoInput, Options extends InputSchema = NoInput>(command: ListCommand<Args, Options, Row, Ports, Settings> & WithoutStandardFlags<Args, Options>): ListCommand<Args, Options, Row, Ports, Settings>;
|
|
237
|
+
<Data, Args extends InputSchema = NoInput, Options extends InputSchema = NoInput>(command: ReadCommand<Args, Options, Data, Ports, Settings> & WithoutStandardFlags<Args, Options>): ReadCommand<Args, Options, Data, Ports, Settings>;
|
|
238
|
+
<Data, Args extends InputSchema = NoInput, Options extends InputSchema = NoInput>(command: ServeCommand<Args, Options, Data, Ports, Settings> & WithoutStandardFlags<Args, Options>): ServeCommand<Args, Options, Data, Ports, Settings>;
|
|
239
|
+
<Plan, Data, Args extends InputSchema = NoInput, Options extends InputSchema = NoInput>(command: WriteCommand<Args, Options, Plan, Data, Ports, Settings> & WithoutStandardFlags<Args, Options>): WriteCommand<Args, Options, Plan, Data, Ports, Settings>;
|
|
240
|
+
<Plan, Data, Args extends InputSchema = NoInput, Options extends InputSchema = NoInput>(command: DestructiveCommand<Args, Options, Plan, Data, Ports, Settings> & WithoutStandardFlags<Args, Options>): DestructiveCommand<Args, Options, Plan, Data, Ports, Settings>;
|
|
241
|
+
}
|
|
242
|
+
export declare function defineCommandFor<Ports, Settings>(): DefineCommand<Ports, Settings>;
|
|
243
|
+
export declare const defineCommand: DefineCommand<unknown>;
|
|
131
244
|
export declare function isCommand(node: Command | Commands): node is Command;
|
|
132
245
|
export {};
|
package/dist/define.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import { checkNoStandardFlags } from "./flags.js";
|
|
2
|
-
|
|
1
|
+
import { checkForce, checkNoStandardFlags } from "./flags.js";
|
|
2
|
+
import { checkArrayArgLast } from "./input.js";
|
|
3
|
+
function checkedCommand(command) {
|
|
3
4
|
checkNoStandardFlags(command);
|
|
5
|
+
checkForce(command);
|
|
6
|
+
checkArrayArgLast(command.name, command.args);
|
|
4
7
|
return command;
|
|
5
8
|
}
|
|
9
|
+
export function defineCommandFor() {
|
|
10
|
+
return checkedCommand;
|
|
11
|
+
}
|
|
12
|
+
export const defineCommand = defineCommandFor();
|
|
6
13
|
// A group's values are always objects, so a string `effect` can only belong to a Command.
|
|
7
14
|
export function isCommand(node) {
|
|
8
15
|
return typeof node.effect === "string";
|
package/dist/define.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAyB,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA+V/C,SAAS,cAAc,CAA2B,OAAiB;IACjE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,UAAU,CAAC,OAAO,CAAC,CAAC;IACpB,iBAAiB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAA2B,gBAAgB,EAAE,CAAC;AAExE,0FAA0F;AAC1F,MAAM,UAAU,SAAS,CAAC,IAAwB;IAChD,OAAO,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC;AACzC,CAAC"}
|
package/dist/doctor-rows.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { CommandTreeCli, DoctorCheck } from "./define.js";
|
|
2
|
-
/**
|
|
3
|
-
export declare function
|
|
2
|
+
/** Every row `doctor` reports, in order: the fixed rows around the CLI's own and its tools'. */
|
|
3
|
+
export declare function doctorChecks(cli: CommandTreeCli, bin: string | undefined): DoctorCheck[];
|
package/dist/doctor-rows.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
1
2
|
import { CliError } from "./errors.js";
|
|
2
|
-
import { globalInstallOf } from "./global-install.js";
|
|
3
|
+
import { globalInstallOf, NOT_GLOBAL_INSTALL } from "./global-install.js";
|
|
3
4
|
import { installCommandLine, latestVersion, npmContext } from "./npm.js";
|
|
5
|
+
import { versionLine } from "./output.js";
|
|
6
|
+
import { PROJECT_INVALID, projectOf } from "./project.js";
|
|
4
7
|
import { engineMinimum, meetsMinimum, standingOf } from "./semver.js";
|
|
5
8
|
import { skillCopies, skillUpdateCommand } from "./skills.js";
|
|
9
|
+
import { toolRows } from "./tools.js";
|
|
6
10
|
function ok(message) {
|
|
7
11
|
return { state: "ok", message };
|
|
8
12
|
}
|
|
13
|
+
function buildRow(cli) {
|
|
14
|
+
return { name: "build", run: () => ok(versionLine(cli)) };
|
|
15
|
+
}
|
|
9
16
|
function nodeRow(cli) {
|
|
10
17
|
return {
|
|
11
18
|
name: "node",
|
|
@@ -34,9 +41,32 @@ function nodeRow(cli) {
|
|
|
34
41
|
},
|
|
35
42
|
};
|
|
36
43
|
}
|
|
44
|
+
function projectRow(projectFile) {
|
|
45
|
+
const { file } = projectFile;
|
|
46
|
+
return {
|
|
47
|
+
name: "project",
|
|
48
|
+
run: (ctx) => {
|
|
49
|
+
let project;
|
|
50
|
+
try {
|
|
51
|
+
project = projectOf(projectFile, ctx.cwd);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (error instanceof CliError && error.code === PROJECT_INVALID) {
|
|
55
|
+
return { state: "problem", message: error.message };
|
|
56
|
+
}
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
return ok(project === undefined
|
|
60
|
+
? `none found: no ${file} at or above ${ctx.cwd}`
|
|
61
|
+
: join(project.root, file));
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// A source run or an unreachable registry never stops a Command working, so these two rows are advisory.
|
|
37
66
|
function installRow(cli, bin) {
|
|
38
67
|
return {
|
|
39
68
|
name: "install",
|
|
69
|
+
advisory: true,
|
|
40
70
|
run: async (ctx) => {
|
|
41
71
|
const { install } = cli;
|
|
42
72
|
if (install === undefined) {
|
|
@@ -47,7 +77,7 @@ function installRow(cli, bin) {
|
|
|
47
77
|
return ok(`a global npm install under ${copy.prefix}`);
|
|
48
78
|
}
|
|
49
79
|
catch (error) {
|
|
50
|
-
if (error instanceof CliError && error.code ===
|
|
80
|
+
if (error instanceof CliError && error.code === NOT_GLOBAL_INSTALL) {
|
|
51
81
|
return { state: "problem", message: error.message, fix: installCommandLine(install) };
|
|
52
82
|
}
|
|
53
83
|
throw error;
|
|
@@ -58,6 +88,7 @@ function installRow(cli, bin) {
|
|
|
58
88
|
function registryRow(cli) {
|
|
59
89
|
return {
|
|
60
90
|
name: "registry",
|
|
91
|
+
advisory: true,
|
|
61
92
|
run: async (ctx) => {
|
|
62
93
|
const { install } = cli;
|
|
63
94
|
if (install === undefined) {
|
|
@@ -108,8 +139,17 @@ function skillRow(cli) {
|
|
|
108
139
|
},
|
|
109
140
|
};
|
|
110
141
|
}
|
|
111
|
-
/**
|
|
112
|
-
export function
|
|
113
|
-
return [
|
|
142
|
+
/** Every row `doctor` reports, in order: the fixed rows around the CLI's own and its tools'. */
|
|
143
|
+
export function doctorChecks(cli, bin) {
|
|
144
|
+
return [
|
|
145
|
+
buildRow(cli),
|
|
146
|
+
nodeRow(cli),
|
|
147
|
+
...(cli.project === undefined ? [] : [projectRow(cli.project)]),
|
|
148
|
+
...(cli.doctor ?? []),
|
|
149
|
+
...toolRows(cli.commands),
|
|
150
|
+
installRow(cli, bin),
|
|
151
|
+
registryRow(cli),
|
|
152
|
+
skillRow(cli),
|
|
153
|
+
];
|
|
114
154
|
}
|
|
115
155
|
//# sourceMappingURL=doctor-rows.js.map
|
package/dist/doctor-rows.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor-rows.js","sourceRoot":"","sources":["../src/doctor-rows.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"doctor-rows.js","sourceRoot":"","sources":["../src/doctor-rows.ts"],"names":[],"mappings":"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"}
|
package/dist/doctor.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { type Command, type CommandTreeCli } from "./define.js";
|
|
2
|
-
|
|
2
|
+
export declare const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
|
|
3
|
+
/** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
|
|
3
4
|
export declare function doctorCommand(cli: CommandTreeCli, bin: string | undefined): Command;
|
package/dist/doctor.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
|
+
import { untilAborted } from "./abort.js";
|
|
2
3
|
import { defineCommand, } from "./define.js";
|
|
3
|
-
import {
|
|
4
|
-
import { CliError,
|
|
5
|
-
const
|
|
4
|
+
import { doctorChecks } from "./doctor-rows.js";
|
|
5
|
+
import { CliError, messageOf } from "./errors.js";
|
|
6
|
+
export const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
|
|
7
|
+
const DEFAULT_DEADLINE_MS = 10_000;
|
|
6
8
|
const MS_PER_SECOND = 1000;
|
|
7
9
|
const MIN_LABEL_WIDTH = 8;
|
|
8
10
|
const rowSchema = z.object({
|
|
@@ -15,20 +17,11 @@ const reportSchema = z.object({
|
|
|
15
17
|
rows: z.array(rowSchema),
|
|
16
18
|
next: z.array(z.string()),
|
|
17
19
|
});
|
|
18
|
-
function
|
|
19
|
-
return new Promise((_resolve, reject) => {
|
|
20
|
-
if (signal.aborted) {
|
|
21
|
-
reject(signal.reason);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
signal.addEventListener("abort", () => reject(signal.reason), { once: true });
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
function problemOf(check, error, { signal, deadline }) {
|
|
20
|
+
function problemOf(check, error, { signal, deadline, ms }) {
|
|
28
21
|
if (!signal.aborted && deadline.aborted && error === deadline.reason) {
|
|
29
22
|
return {
|
|
30
23
|
state: "problem",
|
|
31
|
-
message: `${check.name} did not answer within ${
|
|
24
|
+
message: `${check.name} did not answer within ${ms / MS_PER_SECOND} s`,
|
|
32
25
|
};
|
|
33
26
|
}
|
|
34
27
|
if (error instanceof CliError) {
|
|
@@ -36,20 +29,25 @@ function problemOf(check, error, { signal, deadline }) {
|
|
|
36
29
|
}
|
|
37
30
|
return { state: "problem", message: `the ${check.name} row hit a bug: ${messageOf(error)}` };
|
|
38
31
|
}
|
|
32
|
+
// Not a spread, which would read the lazy `ctx.ports` for every row.
|
|
33
|
+
function withSignal(ctx, signal) {
|
|
34
|
+
return Object.create(ctx, { signal: { value: signal } });
|
|
35
|
+
}
|
|
39
36
|
// Racing the deadline ends a row whose check ignores its signal.
|
|
40
37
|
async function rowOf(check, ctx) {
|
|
41
|
-
const
|
|
38
|
+
const ms = check.deadlineMs ?? DEFAULT_DEADLINE_MS;
|
|
39
|
+
const signal = ctx.deadline(ms);
|
|
42
40
|
let finding;
|
|
43
41
|
try {
|
|
44
42
|
finding = await Promise.race([
|
|
45
|
-
Promise.resolve().then(() => check.run(
|
|
43
|
+
Promise.resolve().then(() => check.run(withSignal(ctx, signal))),
|
|
46
44
|
untilAborted(signal),
|
|
47
45
|
]);
|
|
48
46
|
}
|
|
49
47
|
catch (error) {
|
|
50
|
-
finding = problemOf(check, error, { signal: ctx.signal, deadline: signal });
|
|
48
|
+
finding = problemOf(check, error, { signal: ctx.signal, deadline: signal, ms });
|
|
51
49
|
}
|
|
52
|
-
return { check: check.name, ...finding };
|
|
50
|
+
return { row: { check: check.name, ...finding }, advisory: check.advisory === true };
|
|
53
51
|
}
|
|
54
52
|
function nextOf(rows) {
|
|
55
53
|
return [...new Set(rows.flatMap(({ fix }) => (fix === undefined ? [] : [fix])))];
|
|
@@ -63,18 +61,16 @@ function render({ rows, next }, style) {
|
|
|
63
61
|
});
|
|
64
62
|
return [...lines, ...next.map((fix) => `${"next".padEnd(width)}${fix}`)].join("\n");
|
|
65
63
|
}
|
|
66
|
-
function failure(cliName, report) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
code: "DOCTOR_PROBLEMS",
|
|
64
|
+
function failure(cliName, problems, report) {
|
|
65
|
+
return new CliError({
|
|
66
|
+
code: DOCTOR_PROBLEMS,
|
|
70
67
|
message: `${cliName} doctor found ${problems.length === 1 ? "a problem" : `${problems.length} problems`}: ${problems.join(", ")}`,
|
|
71
68
|
hint: report.next.length === 0
|
|
72
69
|
? "each problem row says what is wrong"
|
|
73
70
|
: "run the commands under next, then run doctor again",
|
|
74
|
-
|
|
75
|
-
}, report);
|
|
71
|
+
});
|
|
76
72
|
}
|
|
77
|
-
/** The built-in `doctor`: the fixed rows
|
|
73
|
+
/** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
|
|
78
74
|
export function doctorCommand(cli, bin) {
|
|
79
75
|
return defineCommand({
|
|
80
76
|
name: "doctor",
|
|
@@ -86,11 +82,17 @@ export function doctorCommand(cli, bin) {
|
|
|
86
82
|
effect: "read",
|
|
87
83
|
output: reportSchema,
|
|
88
84
|
run: async (_input, ctx) => {
|
|
89
|
-
const
|
|
90
|
-
|
|
85
|
+
const checked = await Promise.all(doctorChecks(cli, bin).map((check) => rowOf(check, ctx)));
|
|
86
|
+
// Rows cut short by an interrupt report it as their problem, which no row has.
|
|
87
|
+
ctx.signal.throwIfAborted();
|
|
88
|
+
const rows = checked.map(({ row }) => row);
|
|
91
89
|
const report = { rows, next: nextOf(rows) };
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
const problems = checked
|
|
91
|
+
.filter(({ row, advisory }) => row.state === "problem" && !advisory)
|
|
92
|
+
.map(({ row }) => row.check);
|
|
93
|
+
if (problems.length > 0) {
|
|
94
|
+
ctx.deliver(report);
|
|
95
|
+
throw failure(cli.name, problems, report);
|
|
94
96
|
}
|
|
95
97
|
return report;
|
|
96
98
|
},
|
package/dist/doctor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,EAML,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"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"}
|
package/dist/errno.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Env } from "./define.js";
|
|
2
|
+
export declare const PERMISSION_DENIED = "PERMISSION_DENIED";
|
|
3
|
+
export declare const DISK_FULL = "DISK_FULL";
|
|
4
|
+
export declare const NOT_FOUND = "NOT_FOUND";
|
|
5
|
+
export declare const ALREADY_EXISTS = "ALREADY_EXISTS";
|
|
6
|
+
export interface ErrnoContext {
|
|
7
|
+
readonly path: string;
|
|
8
|
+
/** Whether the failed access read `path` or wrote it; a sandbox grants the two separately. */
|
|
9
|
+
readonly access: "read" | "write";
|
|
10
|
+
/** The caller's command line, ready to paste. */
|
|
11
|
+
readonly callerLine: string;
|
|
12
|
+
readonly env: Env;
|
|
13
|
+
}
|
|
14
|
+
/** The error a file system failure on `path` stands for; any other error comes back unchanged. */
|
|
15
|
+
export declare function classifyErrno(error: unknown, context: ErrnoContext): unknown;
|
package/dist/errno.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { isAgent } from "./agent.js";
|
|
2
|
+
import { CliError, errnoOf } from "./errors.js";
|
|
3
|
+
export const PERMISSION_DENIED = "PERMISSION_DENIED";
|
|
4
|
+
export const DISK_FULL = "DISK_FULL";
|
|
5
|
+
export const NOT_FOUND = "NOT_FOUND";
|
|
6
|
+
export const ALREADY_EXISTS = "ALREADY_EXISTS";
|
|
7
|
+
// The same errnos happen with no sandbox at all (a root-owned folder), so only a detected agent is
|
|
8
|
+
// pointed at its sandbox.
|
|
9
|
+
function permissionHint({ path, access, callerLine, env }) {
|
|
10
|
+
if (!isAgent(env))
|
|
11
|
+
return `check the permissions of ${path} and its folder`;
|
|
12
|
+
const allow = access === "read"
|
|
13
|
+
? `to allow reading ${path} in the sandbox`
|
|
14
|
+
: `to allow ${path} in the sandbox's write list`;
|
|
15
|
+
return `your sandbox may block it: ask your user to run \`! ${callerLine}\`, or ${allow}`;
|
|
16
|
+
}
|
|
17
|
+
/** The error a file system failure on `path` stands for; any other error comes back unchanged. */
|
|
18
|
+
export function classifyErrno(error, context) {
|
|
19
|
+
const { path } = context;
|
|
20
|
+
switch (errnoOf(error)) {
|
|
21
|
+
case "EACCES":
|
|
22
|
+
case "EPERM":
|
|
23
|
+
return new CliError({
|
|
24
|
+
code: PERMISSION_DENIED,
|
|
25
|
+
message: `permission denied: ${path}`,
|
|
26
|
+
hint: permissionHint(context),
|
|
27
|
+
}, { cause: error });
|
|
28
|
+
case "ENOSPC":
|
|
29
|
+
return new CliError({
|
|
30
|
+
code: DISK_FULL,
|
|
31
|
+
message: `no space left on the disk holding ${path}`,
|
|
32
|
+
hint: "free space on that disk, then run the same command again",
|
|
33
|
+
}, { cause: error });
|
|
34
|
+
case "ENOENT":
|
|
35
|
+
return new CliError({
|
|
36
|
+
code: NOT_FOUND,
|
|
37
|
+
message: `not found: ${path}`,
|
|
38
|
+
hint: `check that ${path} and its folder exist`,
|
|
39
|
+
}, { cause: error });
|
|
40
|
+
case "EEXIST":
|
|
41
|
+
return new CliError({
|
|
42
|
+
code: ALREADY_EXISTS,
|
|
43
|
+
message: `already exists: ${path}`,
|
|
44
|
+
hint: `pass another path, or remove ${path}`,
|
|
45
|
+
}, { cause: error });
|
|
46
|
+
default:
|
|
47
|
+
return error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=errno.js.map
|