hacksh 0.5.4 → 0.7.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 +90 -1
- package/dist/index.d.ts +133 -55
- package/dist/index.js +450 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -311,6 +311,94 @@ Physical rows are calculated from explicit newlines, ANSI-free Unicode display w
|
|
|
311
311
|
`stdout.columns`, including terminal wrapping. When a custom output writer does not expose a
|
|
312
312
|
positive `columns` value, the renderer conservatively counts explicit lines only.
|
|
313
313
|
|
|
314
|
+
## External commands
|
|
315
|
+
|
|
316
|
+
Every `run` context has an `exec` function that starts an executable with an argument array. It
|
|
317
|
+
never uses a shell, so spaces, quotes, `$`, `;`, `|`, and `*` in arguments reach the child
|
|
318
|
+
unchanged. By default it resolves with the captured output and rejects with `HackshExecError`
|
|
319
|
+
when the command fails. Pass `reject: false` to receive failures as results instead.
|
|
320
|
+
|
|
321
|
+
```ts
|
|
322
|
+
const sync = defineCommand({
|
|
323
|
+
name: "sync",
|
|
324
|
+
async run({ exec, logger }) {
|
|
325
|
+
const { stdout: branch } = await exec("git", ["branch", "--show-current"]);
|
|
326
|
+
const upstream = await exec("git", ["rev-parse", "--abbrev-ref", "@{upstream}"], {
|
|
327
|
+
reject: false,
|
|
328
|
+
});
|
|
329
|
+
if (upstream.status === "failed") {
|
|
330
|
+
logger.warn("{branch} has no upstream", { branch });
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
await exec("git", ["pull", "--ff-only"], { cwd: "/path/to/repo", timeout: 60_000 });
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Results have `status` `"succeeded"` or `"failed"`, the `command` (`file`, `args`, `cwd`), and
|
|
339
|
+
UTF-8 `stdout` and `stderr` with one final newline removed. A failure's `reason` is `exit-code`
|
|
340
|
+
(with `exitCode`), `signal` (a signal the child received from elsewhere, with `signal`), `spawn`
|
|
341
|
+
(the executable or cwd could not be used, with `error`), or one of `timeout`, `aborted`, and
|
|
342
|
+
`output-limit` when hacksh stopped the child. The return type follows `reject`: omitted or `true`
|
|
343
|
+
gives `ExecSuccess`, while `false` or a `boolean` variable gives `ExecResult`, which must be
|
|
344
|
+
narrowed before reading `exitCode`.
|
|
345
|
+
|
|
346
|
+
Options are `cwd`, `env`, `input`, `reject`, `timeout` (milliseconds), `signal` (an
|
|
347
|
+
`AbortSignal`), `maxBuffer` (bytes per stream, default 100,000,000), and `forceKillAfterDelay`
|
|
348
|
+
(milliseconds, default 5,000). `input` is written to stdin and closed; otherwise stdin is empty.
|
|
349
|
+
Output beyond `maxBuffer` is truncated to that many bytes. On timeout, abort, or output limit,
|
|
350
|
+
hacksh sends `SIGTERM` to the child it started and `SIGKILL` after `forceKillAfterDelay`; it does
|
|
351
|
+
not stop grandchildren, and it does not wait for them to close inherited pipes. Invalid arguments
|
|
352
|
+
or options reject with `TypeError` without starting anything, even with `reject: false`.
|
|
353
|
+
|
|
354
|
+
The child's environment starts from the invocation's `env` option on `runCommand` or `runMain`, or
|
|
355
|
+
from `process.env` when that is absent. The `env` option on `exec` sets variables and removes those
|
|
356
|
+
set to `undefined`. Concurrent invocations with different `env` options do not share settings.
|
|
357
|
+
`HackshExecError` messages include the command and the reason but never the child's output; read
|
|
358
|
+
`error.result` for the full failure, which error summaries and JSON error events omit.
|
|
359
|
+
|
|
360
|
+
Before starting a child, `exec` prints an `exec` event through the invocation's printer, on stderr
|
|
361
|
+
in both formats. Pretty output shows `$` followed by the command line and, when set, its `cwd`. The
|
|
362
|
+
JSON event has `phase: "start"`, `command` (`file`, `args`, and `cwd` when set), `stdio`, and
|
|
363
|
+
`interactive`; it never includes `env` or `input`. Pass `display: false` to hide one call. Pass
|
|
364
|
+
`redact` with secret values to replace every occurrence in the file, arguments, and cwd with `***`
|
|
365
|
+
in the event, the result's `command`, the `HackshExecError` message, and a `spawn` failure's
|
|
366
|
+
`error`. The child still receives the real values, and its captured output is returned unchanged.
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
await exec("git", ["worktree", "add", path, branch], { cwd: repository });
|
|
370
|
+
await exec("gh", ["auth", "login", "--with-token"], { input: token, display: false });
|
|
371
|
+
await exec("curl", ["-H", `Authorization: Bearer ${token}`, url], { redact: [token] });
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Child output is captured by default and never written to the terminal by hacksh. Set `stdin`,
|
|
375
|
+
`stdout`, or `stderr` to `"inherit"` to hand that stream of the parent process to the child; an
|
|
376
|
+
inherited stream bypasses the printer and any `stdout`/`stderr` writers given to `runMain` or
|
|
377
|
+
`runCommand`, and its result field is `""`. `input` cannot be combined with `stdin: "inherit"`.
|
|
378
|
+
Set `interactive: true` when the child talks to the user through the terminal, including tools
|
|
379
|
+
that open `/dev/tty` themselves; hacksh cannot detect that, and it does not create a PTY.
|
|
380
|
+
|
|
381
|
+
```ts
|
|
382
|
+
// Select a row: the table goes to stdin, the selection comes back on stdout.
|
|
383
|
+
const { stdout: picked } = await exec("fzf", [], { input: table, interactive: true });
|
|
384
|
+
// Hand the whole terminal to a long-running command.
|
|
385
|
+
await exec("gh", ["run", "watch", id], { stdin: "inherit", stdout: "inherit", stderr: "inherit" });
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
These calls fail before starting the child, without an `exec` event, and follow `reject` like any
|
|
389
|
+
other failure: `json-output` when a stream is inherited in JSON mode, `non-interactive` when
|
|
390
|
+
`interactive: true` is used outside an interactive mode (the same rule as prompts), and
|
|
391
|
+
`overlapping` when a call that uses the terminal (interactive or inheriting a stream) starts while
|
|
392
|
+
a prompt or another such call is active. A prompt started during such a call fails with
|
|
393
|
+
`HackshPromptError` reason `overlapping`. Captured calls can run alongside prompts. Signals such
|
|
394
|
+
as Ctrl-C reach the whole foreground process group; hacksh does not forward them or manage jobs.
|
|
395
|
+
|
|
396
|
+
On Deno, `exec` needs `--allow-run` and unscoped `--allow-env`, because Deno's `node:child_process`
|
|
397
|
+
reads the whole environment to start a process.
|
|
398
|
+
|
|
399
|
+
To test a command without starting processes, call its `run` with a context whose `exec` is your own
|
|
400
|
+
function of type `Exec`.
|
|
401
|
+
|
|
314
402
|
## Output formats
|
|
315
403
|
|
|
316
404
|
`runMain` and `runCommand` resolve one output mode before command routing, parsing, or execution.
|
|
@@ -338,7 +426,8 @@ capabilities. A stream without those readable operations fails with an `input-er
|
|
|
338
426
|
is attempted.
|
|
339
427
|
|
|
340
428
|
JSON output contains exactly one `PrintEvent` object per line. Help and version events and
|
|
341
|
-
trace/debug/info logs go to stdout; errors
|
|
429
|
+
trace/debug/info logs go to stdout; errors, warning/error/fatal logs, and `exec` events go to
|
|
430
|
+
stderr. Usage attached
|
|
342
431
|
to an error retains its `stderr` stream. Every log level is emitted by default. JSON strings have
|
|
343
432
|
terminal control sequences removed, and the printer never serializes executable command or Zod
|
|
344
433
|
schema objects.
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,12 @@ declare class HackshPromptError extends Error {
|
|
|
26
26
|
readonly prompt: PromptErrorDetails;
|
|
27
27
|
constructor(reason: HackshPromptErrorReason, prompt: PromptErrorDetails, message: string, options?: ErrorOptions);
|
|
28
28
|
}
|
|
29
|
+
/** An external command that did not succeed; `result` is not enumerable so error summaries omit child output. */
|
|
30
|
+
declare class HackshExecError extends Error {
|
|
31
|
+
#private;
|
|
32
|
+
constructor(message: string, result: ExecFailure, options?: ErrorOptions);
|
|
33
|
+
get result(): ExecFailure;
|
|
34
|
+
}
|
|
29
35
|
type HackshErrorKind = "error" | "prompt" | "usage" | "validation" | "thrown";
|
|
30
36
|
interface ValidationIssueSummary {
|
|
31
37
|
arg: string;
|
|
@@ -45,6 +51,60 @@ interface HackshErrorSummary {
|
|
|
45
51
|
value?: unknown;
|
|
46
52
|
}
|
|
47
53
|
//#endregion
|
|
54
|
+
//#region src/prompts.d.ts
|
|
55
|
+
interface SelectOption<T extends string> {
|
|
56
|
+
value: T;
|
|
57
|
+
label?: string;
|
|
58
|
+
hint?: string;
|
|
59
|
+
}
|
|
60
|
+
interface SelectOptions<T extends string> {
|
|
61
|
+
message: string;
|
|
62
|
+
options: readonly SelectOption<T>[];
|
|
63
|
+
initialValue?: T;
|
|
64
|
+
}
|
|
65
|
+
interface ConfirmOptions {
|
|
66
|
+
message: string;
|
|
67
|
+
initialValue?: boolean;
|
|
68
|
+
}
|
|
69
|
+
interface TextOptions {
|
|
70
|
+
message: string;
|
|
71
|
+
placeholder?: string;
|
|
72
|
+
validate?(value: string): string | undefined;
|
|
73
|
+
}
|
|
74
|
+
interface Prompter {
|
|
75
|
+
select<const T extends string>(options: SelectOptions<T>): Promise<T>;
|
|
76
|
+
confirm(options: ConfirmOptions): Promise<boolean>;
|
|
77
|
+
text(options: TextOptions): Promise<string>;
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/logger.d.ts
|
|
81
|
+
type LogLevel = "trace" | "debug" | "info" | "warning" | "error" | "fatal";
|
|
82
|
+
type LogKind = "message" | "step" | "success";
|
|
83
|
+
type LogProperties = Record<string, unknown>;
|
|
84
|
+
/** A structured message emitted by a command's logger. */
|
|
85
|
+
interface LogRecord {
|
|
86
|
+
level: LogLevel;
|
|
87
|
+
kind: LogKind;
|
|
88
|
+
category: readonly string[];
|
|
89
|
+
message: string;
|
|
90
|
+
rawMessage: string;
|
|
91
|
+
properties: LogProperties;
|
|
92
|
+
timestamp: number;
|
|
93
|
+
}
|
|
94
|
+
interface Logger extends Prompter {
|
|
95
|
+
readonly category: readonly string[];
|
|
96
|
+
trace(message: string, properties?: LogProperties): void;
|
|
97
|
+
debug(message: string, properties?: LogProperties): void;
|
|
98
|
+
info(message: string, properties?: LogProperties): void;
|
|
99
|
+
warn(message: string, properties?: LogProperties): void;
|
|
100
|
+
error(message: string | Error, properties?: LogProperties): void;
|
|
101
|
+
fatal(message: string | Error, properties?: LogProperties): void;
|
|
102
|
+
step(message: string, properties?: LogProperties): void;
|
|
103
|
+
success(message: string, properties?: LogProperties): void;
|
|
104
|
+
with(properties: LogProperties): Logger;
|
|
105
|
+
getChild(category: string): Logger;
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
48
108
|
//#region src/output.d.ts
|
|
49
109
|
interface OutputOptions {
|
|
50
110
|
output?: OutputPreference;
|
|
@@ -175,8 +235,21 @@ interface PromptPrintEvent {
|
|
|
175
235
|
type: "prompt";
|
|
176
236
|
prompt: PromptState;
|
|
177
237
|
}
|
|
238
|
+
interface ExecPrintEvent {
|
|
239
|
+
type: "exec";
|
|
240
|
+
phase: "start";
|
|
241
|
+
command: Omit<ExecCommand, "cwd"> & {
|
|
242
|
+
cwd?: string;
|
|
243
|
+
};
|
|
244
|
+
stdio: {
|
|
245
|
+
stdin: "ignore" | "input" | "inherit";
|
|
246
|
+
stdout: "pipe" | "inherit";
|
|
247
|
+
stderr: "pipe" | "inherit";
|
|
248
|
+
};
|
|
249
|
+
interactive: boolean;
|
|
250
|
+
}
|
|
178
251
|
/** A semantic, command- and schema-free output value emitted by hacksh. */
|
|
179
|
-
type PrintEvent = UsagePrintEvent | VersionPrintEvent | ErrorPrintEvent | LogPrintEvent | PromptPrintEvent;
|
|
252
|
+
type PrintEvent = UsagePrintEvent | VersionPrintEvent | ErrorPrintEvent | LogPrintEvent | PromptPrintEvent | ExecPrintEvent;
|
|
180
253
|
/** Receives every output event produced by one command invocation. */
|
|
181
254
|
interface Printer {
|
|
182
255
|
print(event: PrintEvent): void | Promise<void>;
|
|
@@ -213,59 +286,63 @@ declare function createStreamPrinter(options: StreamPrinterOptions): Printer;
|
|
|
213
286
|
/** Create a one-semantic-event-per-line JSON printer. */
|
|
214
287
|
declare function createJsonPrinter(options?: JsonPrinterOptions): Printer;
|
|
215
288
|
//#endregion
|
|
216
|
-
//#region src/
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
289
|
+
//#region src/exec.d.ts
|
|
290
|
+
type Environment = Readonly<Record<string, string | undefined>>;
|
|
291
|
+
interface ExecOptions {
|
|
292
|
+
cwd?: string;
|
|
293
|
+
env?: Environment;
|
|
294
|
+
input?: string;
|
|
295
|
+
reject?: boolean;
|
|
296
|
+
timeout?: number;
|
|
297
|
+
signal?: AbortSignal;
|
|
298
|
+
maxBuffer?: number;
|
|
299
|
+
forceKillAfterDelay?: number;
|
|
300
|
+
stdin?: "pipe" | "inherit";
|
|
301
|
+
stdout?: "pipe" | "inherit";
|
|
302
|
+
stderr?: "pipe" | "inherit";
|
|
303
|
+
interactive?: boolean;
|
|
304
|
+
display?: boolean;
|
|
305
|
+
redact?: readonly string[];
|
|
306
|
+
}
|
|
307
|
+
interface ExecCommand {
|
|
308
|
+
file: string;
|
|
309
|
+
args: readonly string[];
|
|
310
|
+
cwd: string | undefined;
|
|
311
|
+
}
|
|
312
|
+
interface ExecSuccess {
|
|
313
|
+
status: "succeeded";
|
|
314
|
+
command: ExecCommand;
|
|
315
|
+
stdout: string;
|
|
316
|
+
stderr: string;
|
|
317
|
+
exitCode: 0;
|
|
318
|
+
}
|
|
319
|
+
type ExecFailure = {
|
|
320
|
+
status: "failed";
|
|
321
|
+
command: ExecCommand;
|
|
322
|
+
stdout: string;
|
|
323
|
+
stderr: string;
|
|
324
|
+
} & ({
|
|
325
|
+
reason: "exit-code";
|
|
326
|
+
exitCode: number;
|
|
327
|
+
} | {
|
|
328
|
+
reason: "signal";
|
|
329
|
+
signal: string;
|
|
330
|
+
} | {
|
|
331
|
+
reason: "spawn";
|
|
332
|
+
error: Error;
|
|
333
|
+
} | {
|
|
334
|
+
reason: "timeout" | "aborted" | "output-limit";
|
|
335
|
+
} | {
|
|
336
|
+
reason: "json-output" | "non-interactive" | "overlapping";
|
|
337
|
+
});
|
|
338
|
+
type ExecFailureReason = ExecFailure["reason"];
|
|
339
|
+
type ExecResult = ExecSuccess | ExecFailure;
|
|
340
|
+
type ExecReturn<O extends ExecOptions> = "reject" extends keyof O ? false extends O["reject"] ? ExecResult : ExecSuccess : ExecSuccess;
|
|
341
|
+
type RejectInheritedInput<O> = O extends {
|
|
342
|
+
input: string;
|
|
343
|
+
stdin: "inherit";
|
|
344
|
+
} ? never : unknown;
|
|
345
|
+
type Exec = <const O extends ExecOptions = {}>(file: string, args?: readonly string[], options?: O & { [K in Exclude<keyof O, keyof ExecOptions>]: never; } & RejectInheritedInput<O>) => Promise<ExecReturn<O>>;
|
|
269
346
|
//#endregion
|
|
270
347
|
//#region src/schema.d.ts
|
|
271
348
|
type ArgsSchema = $ZodObject;
|
|
@@ -289,6 +366,7 @@ interface CommandContext<S extends ArgsSchema> {
|
|
|
289
366
|
passthrough: string[];
|
|
290
367
|
extraArgs: string[];
|
|
291
368
|
logger: Logger;
|
|
369
|
+
exec: Exec;
|
|
292
370
|
}
|
|
293
371
|
interface CommandDef<S extends ArgsSchema = EmptyArgs> {
|
|
294
372
|
name?: string;
|
|
@@ -337,5 +415,5 @@ interface RunMainOptions extends OutputOptions {
|
|
|
337
415
|
declare function runMain<S extends ArgsSchema>(command: CommandDef<S>, options?: RunMainOptions): Promise<void>;
|
|
338
416
|
declare function createMain<S extends ArgsSchema>(command: CommandDef<S>, options?: RunMainOptions): (argv?: string[]) => Promise<void>;
|
|
339
417
|
//#endregion
|
|
340
|
-
export { type ArgOptions, type ArgOptionsMap, type CommandContext, type CommandDef, type ConfirmOptions, type ConfirmPromptState, type ErrorPrintEvent, type HackshErrorKind, type HackshErrorSummary, type HackshIssue, HackshPromptError, type HackshPromptErrorReason, HackshUsageError, HackshValidationError, type JsonPrinterOptions, type LogKind, type LogLevel, type LogPrintEvent, type LogProperties, type LogRecord, type Logger, type OutputFormat, type OutputMode, type OutputOptions, type OutputPreference, type OutputWriter, type PrettyPrinterOptions, type PrintEvent, type PrintStream, type Printer, type PromptChoiceState, type PromptErrorDetails, type PromptKind, type PromptPrintEvent, type PromptState, type PromptStatus, type Prompter, type ResolveOutputModeOptions, type RunCommandOptions, type RunMainOptions, type SelectOption, type SelectOptions, type SelectPromptState, type ShowUsageCallback, type ShowUsageOptions, type StreamPrinterOptions, type TerminalInput, type TerminalOutput, type TextOptions, type TextPromptState, type UsageArgumentSummary, type UsageCommandSummary, type UsageOptionSummary, type UsageOutput, type UsagePrintEvent, type UsageSummary, type UsageWriter, type ValidationIssueSummary, type VersionPrintEvent, consolePrinter, createJsonPrinter, createMain, createPrettyPrinter, createStreamPrinter, defineCommand, parseArgs, renderUsage, resolveOutputMode, runCommand, runMain, showUsage, summarizeUsage };
|
|
418
|
+
export { type ArgOptions, type ArgOptionsMap, type CommandContext, type CommandDef, type ConfirmOptions, type ConfirmPromptState, type ErrorPrintEvent, type Exec, type ExecCommand, type ExecFailure, type ExecFailureReason, type ExecOptions, type ExecPrintEvent, type ExecResult, type ExecReturn, type ExecSuccess, type HackshErrorKind, type HackshErrorSummary, HackshExecError, type HackshIssue, HackshPromptError, type HackshPromptErrorReason, HackshUsageError, HackshValidationError, type JsonPrinterOptions, type LogKind, type LogLevel, type LogPrintEvent, type LogProperties, type LogRecord, type Logger, type OutputFormat, type OutputMode, type OutputOptions, type OutputPreference, type OutputWriter, type PrettyPrinterOptions, type PrintEvent, type PrintStream, type Printer, type PromptChoiceState, type PromptErrorDetails, type PromptKind, type PromptPrintEvent, type PromptState, type PromptStatus, type Prompter, type ResolveOutputModeOptions, type RunCommandOptions, type RunMainOptions, type SelectOption, type SelectOptions, type SelectPromptState, type ShowUsageCallback, type ShowUsageOptions, type StreamPrinterOptions, type TerminalInput, type TerminalOutput, type TextOptions, type TextPromptState, type UsageArgumentSummary, type UsageCommandSummary, type UsageOptionSummary, type UsageOutput, type UsagePrintEvent, type UsageSummary, type UsageWriter, type ValidationIssueSummary, type VersionPrintEvent, consolePrinter, createJsonPrinter, createMain, createPrettyPrinter, createStreamPrinter, defineCommand, parseArgs, renderUsage, resolveOutputMode, runCommand, runMain, showUsage, summarizeUsage };
|
|
341
419
|
//# sourceMappingURL=index.d.ts.map
|