hacksh 0.6.0 → 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 +38 -1
- package/dist/index.d.ts +123 -98
- package/dist/index.js +433 -265
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -643,6 +643,395 @@ async function parseArgs(command, argv, options) {
|
|
|
643
643
|
return (await parseCommandArgs(command, argv, tokenizeCommandArgs(command, argv), options?.env, envPath)).args;
|
|
644
644
|
}
|
|
645
645
|
//#endregion
|
|
646
|
+
//#region src/terminal.ts
|
|
647
|
+
var TerminalLock = class {
|
|
648
|
+
#owner;
|
|
649
|
+
get owner() {
|
|
650
|
+
return this.#owner;
|
|
651
|
+
}
|
|
652
|
+
acquire(owner) {
|
|
653
|
+
if (this.#owner !== void 0) return false;
|
|
654
|
+
this.#owner = owner;
|
|
655
|
+
return true;
|
|
656
|
+
}
|
|
657
|
+
release() {
|
|
658
|
+
this.#owner = void 0;
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
//#endregion
|
|
662
|
+
//#region src/exec.ts
|
|
663
|
+
const OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
664
|
+
"cwd",
|
|
665
|
+
"env",
|
|
666
|
+
"input",
|
|
667
|
+
"reject",
|
|
668
|
+
"timeout",
|
|
669
|
+
"signal",
|
|
670
|
+
"maxBuffer",
|
|
671
|
+
"forceKillAfterDelay",
|
|
672
|
+
"stdin",
|
|
673
|
+
"stdout",
|
|
674
|
+
"stderr",
|
|
675
|
+
"interactive",
|
|
676
|
+
"display",
|
|
677
|
+
"redact"
|
|
678
|
+
]);
|
|
679
|
+
const MAX_DELAY = 2147483647;
|
|
680
|
+
const REFUSAL_MESSAGE = {
|
|
681
|
+
"json-output": "Command cannot use the terminal in JSON output mode",
|
|
682
|
+
"non-interactive": "Command requires an interactive terminal",
|
|
683
|
+
overlapping: "Command cannot use the terminal while it is in use"
|
|
684
|
+
};
|
|
685
|
+
function isRecord(value) {
|
|
686
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
687
|
+
}
|
|
688
|
+
function assertNoNul(value, label) {
|
|
689
|
+
if (value.includes("\0")) throw new TypeError(`${label} must not contain NUL characters`);
|
|
690
|
+
}
|
|
691
|
+
function readInteger(value, label, minimum, maximum) {
|
|
692
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < minimum || value > maximum) throw new TypeError(`${label} must be an integer from ${minimum} to ${maximum}`);
|
|
693
|
+
return value;
|
|
694
|
+
}
|
|
695
|
+
function readEnum(value, label, allowed) {
|
|
696
|
+
if (typeof value === "string" && allowed.includes(value)) return value;
|
|
697
|
+
throw new TypeError(`Exec option \`${label}\` must be ${allowed.map((item) => JSON.stringify(item)).join(" or ")}`);
|
|
698
|
+
}
|
|
699
|
+
function readBoolean(value, label) {
|
|
700
|
+
if (typeof value !== "boolean") throw new TypeError(`Exec option \`${label}\` must be a boolean`);
|
|
701
|
+
return value;
|
|
702
|
+
}
|
|
703
|
+
function readRedact(value) {
|
|
704
|
+
if (!Array.isArray(value)) throw new TypeError("Exec option `redact` must be an array");
|
|
705
|
+
for (const item of value) {
|
|
706
|
+
if (typeof item !== "string" || item === "") throw new TypeError("Exec option `redact` must contain only non-empty strings");
|
|
707
|
+
assertNoNul(item, "Exec option `redact`");
|
|
708
|
+
}
|
|
709
|
+
return value;
|
|
710
|
+
}
|
|
711
|
+
function mergeEnvironment(base, overrides) {
|
|
712
|
+
if (overrides === void 0 && base === void 0) return void 0;
|
|
713
|
+
if (overrides !== void 0 && !isRecord(overrides)) throw new TypeError("Exec option `env` must be an object");
|
|
714
|
+
const merged = {};
|
|
715
|
+
for (const [key, value] of Object.entries(base ?? process.env)) if (value !== void 0) merged[key] = value;
|
|
716
|
+
for (const [key, value] of Object.entries(overrides ?? {})) {
|
|
717
|
+
if (key === "" || key.includes("=") || key.includes("\0")) throw new TypeError(`Invalid environment variable name ${JSON.stringify(key)}`);
|
|
718
|
+
if (value === void 0) {
|
|
719
|
+
delete merged[key];
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
if (typeof value !== "string") throw new TypeError(`Environment variable ${key} must be a string or undefined`);
|
|
723
|
+
assertNoNul(value, `Environment variable ${key}`);
|
|
724
|
+
merged[key] = value;
|
|
725
|
+
}
|
|
726
|
+
return merged;
|
|
727
|
+
}
|
|
728
|
+
function redactValue(value, secrets) {
|
|
729
|
+
let redacted = value;
|
|
730
|
+
for (const secret of [...secrets].sort((left, right) => right.length - left.length)) redacted = redacted.split(secret).join("***");
|
|
731
|
+
return redacted;
|
|
732
|
+
}
|
|
733
|
+
function redactCommand(command, secrets) {
|
|
734
|
+
if (secrets.length === 0) return command;
|
|
735
|
+
return {
|
|
736
|
+
file: redactValue(command.file, secrets),
|
|
737
|
+
args: command.args.map((arg) => redactValue(arg, secrets)),
|
|
738
|
+
cwd: command.cwd === void 0 ? void 0 : redactValue(command.cwd, secrets)
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
function redactError(error, secrets) {
|
|
742
|
+
const source = error;
|
|
743
|
+
const redacted = new Error(redactValue(error.message, secrets));
|
|
744
|
+
if (typeof error.stack === "string") redacted.stack = redactValue(error.stack, secrets);
|
|
745
|
+
if (source.code !== void 0) redacted.code = source.code;
|
|
746
|
+
if (source.errno !== void 0) redacted.errno = source.errno;
|
|
747
|
+
if (typeof source.syscall === "string") redacted.syscall = redactValue(source.syscall, secrets);
|
|
748
|
+
if (typeof source.path === "string") redacted.path = redactValue(source.path, secrets);
|
|
749
|
+
if (Array.isArray(source.spawnargs)) redacted.spawnargs = source.spawnargs.map((arg) => typeof arg === "string" ? redactValue(arg, secrets) : arg);
|
|
750
|
+
return redacted;
|
|
751
|
+
}
|
|
752
|
+
function observedError(plan, error) {
|
|
753
|
+
return plan.redact.length === 0 ? error : redactError(error, plan.redact);
|
|
754
|
+
}
|
|
755
|
+
function parsePlan(file, args, options, baseEnv) {
|
|
756
|
+
if (typeof file !== "string" || file === "") throw new TypeError("Exec file must be a non-empty string");
|
|
757
|
+
assertNoNul(file, "Exec file");
|
|
758
|
+
const argv = args === void 0 ? [] : args;
|
|
759
|
+
if (!Array.isArray(argv) || !argv.every((arg) => typeof arg === "string")) throw new TypeError("Exec args must be an array of strings");
|
|
760
|
+
for (const arg of argv) assertNoNul(arg, "Exec args");
|
|
761
|
+
const raw = options === void 0 ? {} : options;
|
|
762
|
+
if (!isRecord(raw)) throw new TypeError("Exec options must be an object");
|
|
763
|
+
for (const key of Object.keys(raw)) if (!OPTION_KEYS.has(key)) throw new TypeError(`Unknown exec option \`${key}\``);
|
|
764
|
+
const { cwd, input, reject, timeout, signal, maxBuffer, forceKillAfterDelay } = raw;
|
|
765
|
+
if (cwd !== void 0) {
|
|
766
|
+
if (typeof cwd !== "string" || cwd === "") throw new TypeError("Exec option `cwd` must be a non-empty string");
|
|
767
|
+
assertNoNul(cwd, "Exec option `cwd`");
|
|
768
|
+
}
|
|
769
|
+
if (input !== void 0 && typeof input !== "string") throw new TypeError("Exec option `input` must be a string");
|
|
770
|
+
if (reject !== void 0 && typeof reject !== "boolean") throw new TypeError("Exec option `reject` must be a boolean");
|
|
771
|
+
if (signal !== void 0 && !(signal instanceof AbortSignal)) throw new TypeError("Exec option `signal` must be an AbortSignal");
|
|
772
|
+
const stdin = raw.stdin === void 0 ? "pipe" : readEnum(raw.stdin, "stdin", ["pipe", "inherit"]);
|
|
773
|
+
const stdout = raw.stdout === void 0 ? "pipe" : readEnum(raw.stdout, "stdout", ["pipe", "inherit"]);
|
|
774
|
+
const stderr = raw.stderr === void 0 ? "pipe" : readEnum(raw.stderr, "stderr", ["pipe", "inherit"]);
|
|
775
|
+
if (input !== void 0 && stdin === "inherit") throw new TypeError("Exec option `input` cannot be combined with stdin inherit");
|
|
776
|
+
const command = {
|
|
777
|
+
file,
|
|
778
|
+
args: [...argv],
|
|
779
|
+
cwd
|
|
780
|
+
};
|
|
781
|
+
const redact = raw.redact === void 0 ? [] : readRedact(raw.redact);
|
|
782
|
+
return {
|
|
783
|
+
command,
|
|
784
|
+
reported: redactCommand(command, redact),
|
|
785
|
+
env: mergeEnvironment(baseEnv, raw.env),
|
|
786
|
+
input,
|
|
787
|
+
reject: reject ?? true,
|
|
788
|
+
timeout: timeout === void 0 ? void 0 : readInteger(timeout, "Exec option `timeout`", 1, MAX_DELAY),
|
|
789
|
+
signal,
|
|
790
|
+
maxBuffer: maxBuffer === void 0 ? 1e8 : readInteger(maxBuffer, "Exec option `maxBuffer`", 1, Number.MAX_SAFE_INTEGER),
|
|
791
|
+
forceKillAfterDelay: forceKillAfterDelay === void 0 ? 5e3 : readInteger(forceKillAfterDelay, "Exec option `forceKillAfterDelay`", 0, MAX_DELAY),
|
|
792
|
+
stdin,
|
|
793
|
+
stdout,
|
|
794
|
+
stderr,
|
|
795
|
+
interactive: raw.interactive === void 0 ? false : readBoolean(raw.interactive, "interactive"),
|
|
796
|
+
display: raw.display === void 0 ? true : readBoolean(raw.display, "display"),
|
|
797
|
+
redact
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
function stripFinalNewline(text) {
|
|
801
|
+
if (text.endsWith("\r\n")) return text.slice(0, -2);
|
|
802
|
+
if (text.endsWith("\n")) return text.slice(0, -1);
|
|
803
|
+
return text;
|
|
804
|
+
}
|
|
805
|
+
function collect(stream, limit, onLimit) {
|
|
806
|
+
const chunks = [];
|
|
807
|
+
let length = 0;
|
|
808
|
+
let exceeded = false;
|
|
809
|
+
stream.on("data", (chunk) => {
|
|
810
|
+
if (exceeded) return;
|
|
811
|
+
const remaining = limit - length;
|
|
812
|
+
if (chunk.length <= remaining) {
|
|
813
|
+
chunks.push(chunk);
|
|
814
|
+
length += chunk.length;
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
chunks.push(chunk.subarray(0, remaining));
|
|
818
|
+
length = limit;
|
|
819
|
+
exceeded = true;
|
|
820
|
+
onLimit();
|
|
821
|
+
});
|
|
822
|
+
return () => stripFinalNewline(Buffer.concat(chunks).toString("utf8"));
|
|
823
|
+
}
|
|
824
|
+
function toResult(command, outcome, stdout, stderr) {
|
|
825
|
+
const failed = {
|
|
826
|
+
status: "failed",
|
|
827
|
+
command,
|
|
828
|
+
stdout,
|
|
829
|
+
stderr
|
|
830
|
+
};
|
|
831
|
+
if (outcome.cause) return {
|
|
832
|
+
...failed,
|
|
833
|
+
reason: outcome.cause
|
|
834
|
+
};
|
|
835
|
+
if (outcome.spawnError) return {
|
|
836
|
+
...failed,
|
|
837
|
+
reason: "spawn",
|
|
838
|
+
error: outcome.spawnError
|
|
839
|
+
};
|
|
840
|
+
const { code, signal } = outcome.exit ?? {
|
|
841
|
+
code: null,
|
|
842
|
+
signal: null
|
|
843
|
+
};
|
|
844
|
+
if (signal !== null) return {
|
|
845
|
+
...failed,
|
|
846
|
+
reason: "signal",
|
|
847
|
+
signal
|
|
848
|
+
};
|
|
849
|
+
if (code !== 0) return {
|
|
850
|
+
...failed,
|
|
851
|
+
reason: "exit-code",
|
|
852
|
+
exitCode: code ?? 1
|
|
853
|
+
};
|
|
854
|
+
return {
|
|
855
|
+
status: "succeeded",
|
|
856
|
+
command,
|
|
857
|
+
stdout,
|
|
858
|
+
stderr,
|
|
859
|
+
exitCode: 0
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
function isSpawnErrno(error) {
|
|
863
|
+
return error instanceof Error && "syscall" in error;
|
|
864
|
+
}
|
|
865
|
+
function commandStdio(plan) {
|
|
866
|
+
return {
|
|
867
|
+
stdin: plan.stdin === "inherit" ? "inherit" : plan.input === void 0 ? "ignore" : "input",
|
|
868
|
+
stdout: plan.stdout,
|
|
869
|
+
stderr: plan.stderr
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
function usesTerminal(plan, stdio) {
|
|
873
|
+
return plan.interactive || stdio.stdin === "inherit" || stdio.stdout === "inherit" || stdio.stderr === "inherit";
|
|
874
|
+
}
|
|
875
|
+
function startChild(plan) {
|
|
876
|
+
const stdio = commandStdio(plan);
|
|
877
|
+
try {
|
|
878
|
+
return spawn(plan.command.file, plan.command.args, {
|
|
879
|
+
cwd: plan.command.cwd,
|
|
880
|
+
env: plan.env,
|
|
881
|
+
shell: false,
|
|
882
|
+
windowsHide: true,
|
|
883
|
+
stdio: [
|
|
884
|
+
stdio.stdin === "inherit" ? "inherit" : stdio.stdin === "input" ? "pipe" : "ignore",
|
|
885
|
+
stdio.stdout,
|
|
886
|
+
stdio.stderr
|
|
887
|
+
]
|
|
888
|
+
});
|
|
889
|
+
} catch (error) {
|
|
890
|
+
if (isSpawnErrno(error)) return error;
|
|
891
|
+
throw error;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
function runPlan(plan) {
|
|
895
|
+
const outcome = {
|
|
896
|
+
cause: void 0,
|
|
897
|
+
spawnError: void 0,
|
|
898
|
+
exit: void 0
|
|
899
|
+
};
|
|
900
|
+
if (plan.signal?.aborted) {
|
|
901
|
+
outcome.cause = "aborted";
|
|
902
|
+
return Promise.resolve(toResult(plan.reported, outcome, "", ""));
|
|
903
|
+
}
|
|
904
|
+
const child = startChild(plan);
|
|
905
|
+
if (child instanceof Error) {
|
|
906
|
+
outcome.spawnError = observedError(plan, child);
|
|
907
|
+
return Promise.resolve(toResult(plan.reported, outcome, "", ""));
|
|
908
|
+
}
|
|
909
|
+
return new Promise((resolve) => {
|
|
910
|
+
const stdout = child.stdout;
|
|
911
|
+
const stderr = child.stderr;
|
|
912
|
+
let settled = false;
|
|
913
|
+
let killTimer;
|
|
914
|
+
const settle = () => {
|
|
915
|
+
if (settled) return;
|
|
916
|
+
settled = true;
|
|
917
|
+
clearTimeout(timeoutTimer);
|
|
918
|
+
clearTimeout(killTimer);
|
|
919
|
+
plan.signal?.removeEventListener("abort", onAbort);
|
|
920
|
+
resolve(toResult(plan.reported, outcome, readStdout(), readStderr()));
|
|
921
|
+
};
|
|
922
|
+
const release = () => {
|
|
923
|
+
stdout?.destroy();
|
|
924
|
+
stderr?.destroy();
|
|
925
|
+
settle();
|
|
926
|
+
};
|
|
927
|
+
const terminate = (cause) => {
|
|
928
|
+
if (settled || outcome.cause) return;
|
|
929
|
+
outcome.cause = cause;
|
|
930
|
+
if (outcome.exit) {
|
|
931
|
+
release();
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
child.kill("SIGTERM");
|
|
935
|
+
killTimer = setTimeout(() => child.kill("SIGKILL"), plan.forceKillAfterDelay);
|
|
936
|
+
};
|
|
937
|
+
const onAbort = () => terminate("aborted");
|
|
938
|
+
const readStdout = stdout ? collect(stdout, plan.maxBuffer, () => terminate("output-limit")) : () => "";
|
|
939
|
+
const readStderr = stderr ? collect(stderr, plan.maxBuffer, () => terminate("output-limit")) : () => "";
|
|
940
|
+
const timeoutTimer = plan.timeout === void 0 ? void 0 : setTimeout(() => terminate("timeout"), plan.timeout);
|
|
941
|
+
plan.signal?.addEventListener("abort", onAbort, { once: true });
|
|
942
|
+
child.on("error", (error) => {
|
|
943
|
+
if (child.pid !== void 0) return;
|
|
944
|
+
outcome.spawnError = observedError(plan, error);
|
|
945
|
+
release();
|
|
946
|
+
});
|
|
947
|
+
child.on("exit", (code, signal) => {
|
|
948
|
+
outcome.exit = {
|
|
949
|
+
code,
|
|
950
|
+
signal
|
|
951
|
+
};
|
|
952
|
+
clearTimeout(killTimer);
|
|
953
|
+
if (outcome.cause) release();
|
|
954
|
+
});
|
|
955
|
+
child.on("close", settle);
|
|
956
|
+
if (child.stdin) {
|
|
957
|
+
child.stdin.on("error", () => void 0);
|
|
958
|
+
child.stdin.end(plan.input);
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
function formatArgument(value) {
|
|
963
|
+
return value === "" || /[\s"'\\$`;|&<>*?()[\]{}~#!]/.test(value) ? JSON.stringify(value) : value;
|
|
964
|
+
}
|
|
965
|
+
function formatExecCommand(command) {
|
|
966
|
+
return [command.file, ...command.args].map(formatArgument).join(" ");
|
|
967
|
+
}
|
|
968
|
+
function failureMessage(plan, result) {
|
|
969
|
+
const display = formatExecCommand(plan.reported);
|
|
970
|
+
switch (result.reason) {
|
|
971
|
+
case "exit-code": return `Command failed with exit code ${result.exitCode}: ${display}`;
|
|
972
|
+
case "signal": return `Command was terminated by ${result.signal}: ${display}`;
|
|
973
|
+
case "spawn": return `Command could not be started (${result.error.code ?? result.error.message}): ${display}`;
|
|
974
|
+
case "timeout": return `Command timed out after ${plan.timeout} ms: ${display}`;
|
|
975
|
+
case "aborted": return `Command was aborted: ${display}`;
|
|
976
|
+
case "output-limit": return `Command output exceeded ${plan.maxBuffer} bytes: ${display}`;
|
|
977
|
+
case "json-output":
|
|
978
|
+
case "non-interactive":
|
|
979
|
+
case "overlapping": return `${REFUSAL_MESSAGE[result.reason]}: ${display}`;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
function emptyFailure(plan, reason) {
|
|
983
|
+
return {
|
|
984
|
+
status: "failed",
|
|
985
|
+
command: plan.reported,
|
|
986
|
+
stdout: "",
|
|
987
|
+
stderr: "",
|
|
988
|
+
reason
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
function complete(plan, result) {
|
|
992
|
+
if (result.status === "failed" && plan.reject) throw new HackshExecError(failureMessage(plan, result), result, result.reason === "spawn" ? { cause: result.error } : void 0);
|
|
993
|
+
return result;
|
|
994
|
+
}
|
|
995
|
+
function blockedReason(plan, context, stdio) {
|
|
996
|
+
const inherited = stdio.stdin === "inherit" || stdio.stdout === "inherit" || stdio.stderr === "inherit";
|
|
997
|
+
if (context.mode.format === "json" && inherited) return "json-output";
|
|
998
|
+
if (plan.interactive && !context.mode.interactive) return "non-interactive";
|
|
999
|
+
}
|
|
1000
|
+
async function execute(file, args, options, context) {
|
|
1001
|
+
const plan = parsePlan(file, args, options, context.env);
|
|
1002
|
+
if (plan.signal?.aborted) return complete(plan, emptyFailure(plan, "aborted"));
|
|
1003
|
+
const stdio = commandStdio(plan);
|
|
1004
|
+
const blocked = blockedReason(plan, context, stdio);
|
|
1005
|
+
if (blocked) return complete(plan, emptyFailure(plan, blocked));
|
|
1006
|
+
const holdTerminal = usesTerminal(plan, stdio);
|
|
1007
|
+
if (holdTerminal && !context.terminal.acquire("exec")) return complete(plan, emptyFailure(plan, "overlapping"));
|
|
1008
|
+
try {
|
|
1009
|
+
if (plan.display) {
|
|
1010
|
+
const { file, args, cwd } = plan.reported;
|
|
1011
|
+
await context.print({
|
|
1012
|
+
type: "exec",
|
|
1013
|
+
phase: "start",
|
|
1014
|
+
command: cwd === void 0 ? {
|
|
1015
|
+
file,
|
|
1016
|
+
args
|
|
1017
|
+
} : {
|
|
1018
|
+
file,
|
|
1019
|
+
args,
|
|
1020
|
+
cwd
|
|
1021
|
+
},
|
|
1022
|
+
stdio,
|
|
1023
|
+
interactive: plan.interactive
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
return complete(plan, await runPlan(plan));
|
|
1027
|
+
} finally {
|
|
1028
|
+
if (holdTerminal) context.terminal.release();
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
function createExec(context) {
|
|
1032
|
+
return ((file, args, options) => execute(file, args, options, context));
|
|
1033
|
+
}
|
|
1034
|
+
//#endregion
|
|
646
1035
|
//#region src/serialization.ts
|
|
647
1036
|
function safeString(value) {
|
|
648
1037
|
try {
|
|
@@ -771,7 +1160,7 @@ function isErrorLevel(level) {
|
|
|
771
1160
|
return level === "warning" || level === "error" || level === "fatal";
|
|
772
1161
|
}
|
|
773
1162
|
function eventStream(event) {
|
|
774
|
-
if (event.type === "error") return "stderr";
|
|
1163
|
+
if (event.type === "error" || event.type === "exec") return "stderr";
|
|
775
1164
|
if (event.type === "usage") return event.stream;
|
|
776
1165
|
if (event.type === "log" && isErrorLevel(event.record.level)) return "stderr";
|
|
777
1166
|
return "stdout";
|
|
@@ -783,6 +1172,7 @@ function formatTextEvent(event) {
|
|
|
783
1172
|
case "error": return event.error.kind === "usage" || event.error.kind === "validation" ? event.error.message : `Error: ${event.error.message}`;
|
|
784
1173
|
case "log": return event.record.message;
|
|
785
1174
|
case "prompt": return event.prompt.message;
|
|
1175
|
+
case "exec": return `$ ${formatExecCommand(event.command)}`;
|
|
786
1176
|
}
|
|
787
1177
|
}
|
|
788
1178
|
/** Undecorated console compatibility adapter; automatic output uses the selected format printer. */
|
|
@@ -872,7 +1262,8 @@ const unicodeSymbols = {
|
|
|
872
1262
|
usage: "◆",
|
|
873
1263
|
version: "●",
|
|
874
1264
|
errorEvent: "■",
|
|
875
|
-
prompt: "?"
|
|
1265
|
+
prompt: "?",
|
|
1266
|
+
exec: "$"
|
|
876
1267
|
}
|
|
877
1268
|
};
|
|
878
1269
|
const asciiSymbols = {
|
|
@@ -889,7 +1280,8 @@ const asciiSymbols = {
|
|
|
889
1280
|
usage: "?",
|
|
890
1281
|
version: "i",
|
|
891
1282
|
errorEvent: "x",
|
|
892
|
-
prompt: "?"
|
|
1283
|
+
prompt: "?",
|
|
1284
|
+
exec: "$"
|
|
893
1285
|
}
|
|
894
1286
|
};
|
|
895
1287
|
const markStyles = {
|
|
@@ -908,7 +1300,8 @@ const markStyles = {
|
|
|
908
1300
|
usage: "cyan",
|
|
909
1301
|
version: "blue",
|
|
910
1302
|
errorEvent: "red",
|
|
911
|
-
prompt: "cyan"
|
|
1303
|
+
prompt: "cyan",
|
|
1304
|
+
exec: "gray"
|
|
912
1305
|
};
|
|
913
1306
|
function safelyInspect(value) {
|
|
914
1307
|
if (typeof value === "string") return value;
|
|
@@ -955,6 +1348,7 @@ function eventMark(event) {
|
|
|
955
1348
|
if (event.type === "version") return "version";
|
|
956
1349
|
if (event.type === "error") return "errorEvent";
|
|
957
1350
|
if (event.type === "prompt") return event.prompt.status === "submitted" ? "success" : "prompt";
|
|
1351
|
+
if (event.type === "exec") return "exec";
|
|
958
1352
|
return event.record.kind === "message" ? event.record.level : event.record.kind;
|
|
959
1353
|
}
|
|
960
1354
|
function eventLines(event) {
|
|
@@ -976,6 +1370,11 @@ function eventLines(event) {
|
|
|
976
1370
|
if (prompt.error) lines.push(prompt.error);
|
|
977
1371
|
return lines;
|
|
978
1372
|
}
|
|
1373
|
+
case "exec": {
|
|
1374
|
+
const lines = [formatExecCommand(event.command)];
|
|
1375
|
+
if (event.command.cwd !== void 0) lines.push(...propertyLines({ cwd: event.command.cwd }));
|
|
1376
|
+
return lines;
|
|
1377
|
+
}
|
|
979
1378
|
}
|
|
980
1379
|
}
|
|
981
1380
|
function colorize(enabled, style, text) {
|
|
@@ -1059,253 +1458,6 @@ function createPrettyPrinter(options) {
|
|
|
1059
1458
|
} };
|
|
1060
1459
|
}
|
|
1061
1460
|
//#endregion
|
|
1062
|
-
//#region src/exec.ts
|
|
1063
|
-
const OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
1064
|
-
"cwd",
|
|
1065
|
-
"env",
|
|
1066
|
-
"input",
|
|
1067
|
-
"reject",
|
|
1068
|
-
"timeout",
|
|
1069
|
-
"signal",
|
|
1070
|
-
"maxBuffer",
|
|
1071
|
-
"forceKillAfterDelay"
|
|
1072
|
-
]);
|
|
1073
|
-
const MAX_DELAY = 2147483647;
|
|
1074
|
-
function isRecord(value) {
|
|
1075
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1076
|
-
}
|
|
1077
|
-
function assertNoNul(value, label) {
|
|
1078
|
-
if (value.includes("\0")) throw new TypeError(`${label} must not contain NUL characters`);
|
|
1079
|
-
}
|
|
1080
|
-
function readInteger(value, label, minimum, maximum) {
|
|
1081
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < minimum || value > maximum) throw new TypeError(`${label} must be an integer from ${minimum} to ${maximum}`);
|
|
1082
|
-
return value;
|
|
1083
|
-
}
|
|
1084
|
-
function mergeEnvironment(base, overrides) {
|
|
1085
|
-
if (overrides === void 0 && base === void 0) return void 0;
|
|
1086
|
-
if (overrides !== void 0 && !isRecord(overrides)) throw new TypeError("Exec option `env` must be an object");
|
|
1087
|
-
const merged = {};
|
|
1088
|
-
for (const [key, value] of Object.entries(base ?? process.env)) if (value !== void 0) merged[key] = value;
|
|
1089
|
-
for (const [key, value] of Object.entries(overrides ?? {})) {
|
|
1090
|
-
if (key === "" || key.includes("=") || key.includes("\0")) throw new TypeError(`Invalid environment variable name ${JSON.stringify(key)}`);
|
|
1091
|
-
if (value === void 0) {
|
|
1092
|
-
delete merged[key];
|
|
1093
|
-
continue;
|
|
1094
|
-
}
|
|
1095
|
-
if (typeof value !== "string") throw new TypeError(`Environment variable ${key} must be a string or undefined`);
|
|
1096
|
-
assertNoNul(value, `Environment variable ${key}`);
|
|
1097
|
-
merged[key] = value;
|
|
1098
|
-
}
|
|
1099
|
-
return merged;
|
|
1100
|
-
}
|
|
1101
|
-
function parsePlan(file, args, options, baseEnv) {
|
|
1102
|
-
if (typeof file !== "string" || file === "") throw new TypeError("Exec file must be a non-empty string");
|
|
1103
|
-
assertNoNul(file, "Exec file");
|
|
1104
|
-
const argv = args === void 0 ? [] : args;
|
|
1105
|
-
if (!Array.isArray(argv) || !argv.every((arg) => typeof arg === "string")) throw new TypeError("Exec args must be an array of strings");
|
|
1106
|
-
for (const arg of argv) assertNoNul(arg, "Exec args");
|
|
1107
|
-
const raw = options === void 0 ? {} : options;
|
|
1108
|
-
if (!isRecord(raw)) throw new TypeError("Exec options must be an object");
|
|
1109
|
-
for (const key of Object.keys(raw)) if (!OPTION_KEYS.has(key)) throw new TypeError(`Unknown exec option \`${key}\``);
|
|
1110
|
-
const { cwd, input, reject, timeout, signal, maxBuffer, forceKillAfterDelay } = raw;
|
|
1111
|
-
if (cwd !== void 0) {
|
|
1112
|
-
if (typeof cwd !== "string" || cwd === "") throw new TypeError("Exec option `cwd` must be a non-empty string");
|
|
1113
|
-
assertNoNul(cwd, "Exec option `cwd`");
|
|
1114
|
-
}
|
|
1115
|
-
if (input !== void 0 && typeof input !== "string") throw new TypeError("Exec option `input` must be a string");
|
|
1116
|
-
if (reject !== void 0 && typeof reject !== "boolean") throw new TypeError("Exec option `reject` must be a boolean");
|
|
1117
|
-
if (signal !== void 0 && !(signal instanceof AbortSignal)) throw new TypeError("Exec option `signal` must be an AbortSignal");
|
|
1118
|
-
return {
|
|
1119
|
-
command: {
|
|
1120
|
-
file,
|
|
1121
|
-
args: [...argv],
|
|
1122
|
-
cwd
|
|
1123
|
-
},
|
|
1124
|
-
env: mergeEnvironment(baseEnv, raw.env),
|
|
1125
|
-
input,
|
|
1126
|
-
reject: reject ?? true,
|
|
1127
|
-
timeout: timeout === void 0 ? void 0 : readInteger(timeout, "Exec option `timeout`", 1, MAX_DELAY),
|
|
1128
|
-
signal,
|
|
1129
|
-
maxBuffer: maxBuffer === void 0 ? 1e8 : readInteger(maxBuffer, "Exec option `maxBuffer`", 1, Number.MAX_SAFE_INTEGER),
|
|
1130
|
-
forceKillAfterDelay: forceKillAfterDelay === void 0 ? 5e3 : readInteger(forceKillAfterDelay, "Exec option `forceKillAfterDelay`", 0, MAX_DELAY)
|
|
1131
|
-
};
|
|
1132
|
-
}
|
|
1133
|
-
function stripFinalNewline(text) {
|
|
1134
|
-
if (text.endsWith("\r\n")) return text.slice(0, -2);
|
|
1135
|
-
if (text.endsWith("\n")) return text.slice(0, -1);
|
|
1136
|
-
return text;
|
|
1137
|
-
}
|
|
1138
|
-
function collect(stream, limit, onLimit) {
|
|
1139
|
-
const chunks = [];
|
|
1140
|
-
let length = 0;
|
|
1141
|
-
let exceeded = false;
|
|
1142
|
-
stream.on("data", (chunk) => {
|
|
1143
|
-
if (exceeded) return;
|
|
1144
|
-
const remaining = limit - length;
|
|
1145
|
-
if (chunk.length <= remaining) {
|
|
1146
|
-
chunks.push(chunk);
|
|
1147
|
-
length += chunk.length;
|
|
1148
|
-
return;
|
|
1149
|
-
}
|
|
1150
|
-
chunks.push(chunk.subarray(0, remaining));
|
|
1151
|
-
length = limit;
|
|
1152
|
-
exceeded = true;
|
|
1153
|
-
onLimit();
|
|
1154
|
-
});
|
|
1155
|
-
return () => stripFinalNewline(Buffer.concat(chunks).toString("utf8"));
|
|
1156
|
-
}
|
|
1157
|
-
function toResult(command, outcome, stdout, stderr) {
|
|
1158
|
-
const failed = {
|
|
1159
|
-
status: "failed",
|
|
1160
|
-
command,
|
|
1161
|
-
stdout,
|
|
1162
|
-
stderr
|
|
1163
|
-
};
|
|
1164
|
-
if (outcome.cause) return {
|
|
1165
|
-
...failed,
|
|
1166
|
-
reason: outcome.cause
|
|
1167
|
-
};
|
|
1168
|
-
if (outcome.spawnError) return {
|
|
1169
|
-
...failed,
|
|
1170
|
-
reason: "spawn",
|
|
1171
|
-
error: outcome.spawnError
|
|
1172
|
-
};
|
|
1173
|
-
const { code, signal } = outcome.exit ?? {
|
|
1174
|
-
code: null,
|
|
1175
|
-
signal: null
|
|
1176
|
-
};
|
|
1177
|
-
if (signal !== null) return {
|
|
1178
|
-
...failed,
|
|
1179
|
-
reason: "signal",
|
|
1180
|
-
signal
|
|
1181
|
-
};
|
|
1182
|
-
if (code !== 0) return {
|
|
1183
|
-
...failed,
|
|
1184
|
-
reason: "exit-code",
|
|
1185
|
-
exitCode: code ?? 1
|
|
1186
|
-
};
|
|
1187
|
-
return {
|
|
1188
|
-
status: "succeeded",
|
|
1189
|
-
command,
|
|
1190
|
-
stdout,
|
|
1191
|
-
stderr,
|
|
1192
|
-
exitCode: 0
|
|
1193
|
-
};
|
|
1194
|
-
}
|
|
1195
|
-
function isSpawnErrno(error) {
|
|
1196
|
-
return error instanceof Error && "syscall" in error;
|
|
1197
|
-
}
|
|
1198
|
-
function startChild(plan) {
|
|
1199
|
-
try {
|
|
1200
|
-
return spawn(plan.command.file, plan.command.args, {
|
|
1201
|
-
cwd: plan.command.cwd,
|
|
1202
|
-
env: plan.env,
|
|
1203
|
-
shell: false,
|
|
1204
|
-
windowsHide: true,
|
|
1205
|
-
stdio: [
|
|
1206
|
-
plan.input === void 0 ? "ignore" : "pipe",
|
|
1207
|
-
"pipe",
|
|
1208
|
-
"pipe"
|
|
1209
|
-
]
|
|
1210
|
-
});
|
|
1211
|
-
} catch (error) {
|
|
1212
|
-
if (isSpawnErrno(error)) return error;
|
|
1213
|
-
throw error;
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
function runPlan(plan) {
|
|
1217
|
-
const outcome = {
|
|
1218
|
-
cause: void 0,
|
|
1219
|
-
spawnError: void 0,
|
|
1220
|
-
exit: void 0
|
|
1221
|
-
};
|
|
1222
|
-
if (plan.signal?.aborted) {
|
|
1223
|
-
outcome.cause = "aborted";
|
|
1224
|
-
return Promise.resolve(toResult(plan.command, outcome, "", ""));
|
|
1225
|
-
}
|
|
1226
|
-
const child = startChild(plan);
|
|
1227
|
-
if (child instanceof Error) {
|
|
1228
|
-
outcome.spawnError = child;
|
|
1229
|
-
return Promise.resolve(toResult(plan.command, outcome, "", ""));
|
|
1230
|
-
}
|
|
1231
|
-
return new Promise((resolve) => {
|
|
1232
|
-
const stdout = child.stdout;
|
|
1233
|
-
const stderr = child.stderr;
|
|
1234
|
-
let settled = false;
|
|
1235
|
-
let killTimer;
|
|
1236
|
-
const settle = () => {
|
|
1237
|
-
if (settled) return;
|
|
1238
|
-
settled = true;
|
|
1239
|
-
clearTimeout(timeoutTimer);
|
|
1240
|
-
clearTimeout(killTimer);
|
|
1241
|
-
plan.signal?.removeEventListener("abort", onAbort);
|
|
1242
|
-
resolve(toResult(plan.command, outcome, readStdout(), readStderr()));
|
|
1243
|
-
};
|
|
1244
|
-
const release = () => {
|
|
1245
|
-
stdout.destroy();
|
|
1246
|
-
stderr.destroy();
|
|
1247
|
-
settle();
|
|
1248
|
-
};
|
|
1249
|
-
const terminate = (cause) => {
|
|
1250
|
-
if (settled || outcome.cause) return;
|
|
1251
|
-
outcome.cause = cause;
|
|
1252
|
-
if (outcome.exit) {
|
|
1253
|
-
release();
|
|
1254
|
-
return;
|
|
1255
|
-
}
|
|
1256
|
-
child.kill("SIGTERM");
|
|
1257
|
-
killTimer = setTimeout(() => child.kill("SIGKILL"), plan.forceKillAfterDelay);
|
|
1258
|
-
};
|
|
1259
|
-
const onAbort = () => terminate("aborted");
|
|
1260
|
-
const readStdout = collect(stdout, plan.maxBuffer, () => terminate("output-limit"));
|
|
1261
|
-
const readStderr = collect(stderr, plan.maxBuffer, () => terminate("output-limit"));
|
|
1262
|
-
const timeoutTimer = plan.timeout === void 0 ? void 0 : setTimeout(() => terminate("timeout"), plan.timeout);
|
|
1263
|
-
plan.signal?.addEventListener("abort", onAbort, { once: true });
|
|
1264
|
-
child.on("error", (error) => {
|
|
1265
|
-
if (child.pid !== void 0) return;
|
|
1266
|
-
outcome.spawnError = error;
|
|
1267
|
-
release();
|
|
1268
|
-
});
|
|
1269
|
-
child.on("exit", (code, signal) => {
|
|
1270
|
-
outcome.exit = {
|
|
1271
|
-
code,
|
|
1272
|
-
signal
|
|
1273
|
-
};
|
|
1274
|
-
clearTimeout(killTimer);
|
|
1275
|
-
if (outcome.cause) release();
|
|
1276
|
-
});
|
|
1277
|
-
child.on("close", settle);
|
|
1278
|
-
if (child.stdin) {
|
|
1279
|
-
child.stdin.on("error", () => void 0);
|
|
1280
|
-
child.stdin.end(plan.input);
|
|
1281
|
-
}
|
|
1282
|
-
});
|
|
1283
|
-
}
|
|
1284
|
-
function formatArgument(value) {
|
|
1285
|
-
return value === "" || /[\s"'\\$`;|&<>*?()[\]{}~#!]/.test(value) ? JSON.stringify(value) : value;
|
|
1286
|
-
}
|
|
1287
|
-
function failureMessage(plan, result) {
|
|
1288
|
-
const display = [plan.command.file, ...plan.command.args].map(formatArgument).join(" ");
|
|
1289
|
-
switch (result.reason) {
|
|
1290
|
-
case "exit-code": return `Command failed with exit code ${result.exitCode}: ${display}`;
|
|
1291
|
-
case "signal": return `Command was terminated by ${result.signal}: ${display}`;
|
|
1292
|
-
case "spawn": return `Command could not be started (${result.error.code ?? result.error.message}): ${display}`;
|
|
1293
|
-
case "timeout": return `Command timed out after ${plan.timeout} ms: ${display}`;
|
|
1294
|
-
case "aborted": return `Command was aborted: ${display}`;
|
|
1295
|
-
case "output-limit": return `Command output exceeded ${plan.maxBuffer} bytes: ${display}`;
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
async function execute(file, args, options, baseEnv) {
|
|
1299
|
-
const plan = parsePlan(file, args, options, baseEnv);
|
|
1300
|
-
const result = await runPlan(plan);
|
|
1301
|
-
if (result.status === "failed" && plan.reject) throw new HackshExecError(failureMessage(plan, result), result, result.reason === "spawn" ? { cause: result.error } : void 0);
|
|
1302
|
-
return result;
|
|
1303
|
-
}
|
|
1304
|
-
/** Bind exec to an invocation environment; `undefined` lets children inherit `process.env`. */
|
|
1305
|
-
function createExec(baseEnv) {
|
|
1306
|
-
return ((file, args, options) => execute(file, args, options, baseEnv));
|
|
1307
|
-
}
|
|
1308
|
-
//#endregion
|
|
1309
1461
|
//#region src/prompts.ts
|
|
1310
1462
|
function details$1(kind, message, choices) {
|
|
1311
1463
|
let normalizedMessage;
|
|
@@ -1335,11 +1487,12 @@ var PromptController = class {
|
|
|
1335
1487
|
#inputValue;
|
|
1336
1488
|
#printEvent;
|
|
1337
1489
|
#bufferedKeys = [];
|
|
1338
|
-
#
|
|
1490
|
+
#terminal;
|
|
1339
1491
|
constructor(options) {
|
|
1340
1492
|
this.#interactive = options.interactive;
|
|
1341
1493
|
this.#inputValue = options.input;
|
|
1342
1494
|
this.#printEvent = (event) => options.print(event);
|
|
1495
|
+
this.#terminal = options.terminal;
|
|
1343
1496
|
}
|
|
1344
1497
|
async #print(state, prompt) {
|
|
1345
1498
|
try {
|
|
@@ -1353,9 +1506,8 @@ var PromptController = class {
|
|
|
1353
1506
|
}
|
|
1354
1507
|
async #run(prompt, initial, accept, update, submitted, closed) {
|
|
1355
1508
|
if (!this.#interactive) throw promptError("non-interactive", prompt, `Cannot request ${prompt.kind} input in non-interactive mode: ${prompt.message}`);
|
|
1356
|
-
if (this.#active) throw promptError("overlapping", prompt, "Another prompt is already active");
|
|
1357
1509
|
const input = asInput(this.#inputValue, prompt);
|
|
1358
|
-
this.#
|
|
1510
|
+
if (!this.#terminal.acquire("prompt")) throw promptError("overlapping", prompt, this.#terminal.owner === "exec" ? "A command is using the terminal" : "Another prompt is already active");
|
|
1359
1511
|
let originallyFlowing = false;
|
|
1360
1512
|
let rawChanged = false;
|
|
1361
1513
|
let outcome;
|
|
@@ -1478,7 +1630,7 @@ var PromptController = class {
|
|
|
1478
1630
|
try {
|
|
1479
1631
|
await this.#print(closed(), prompt);
|
|
1480
1632
|
} catch {} finally {
|
|
1481
|
-
this.#
|
|
1633
|
+
this.#terminal.release();
|
|
1482
1634
|
}
|
|
1483
1635
|
throw failure;
|
|
1484
1636
|
};
|
|
@@ -1493,7 +1645,7 @@ var PromptController = class {
|
|
|
1493
1645
|
} catch {}
|
|
1494
1646
|
throw error;
|
|
1495
1647
|
} finally {
|
|
1496
|
-
this.#
|
|
1648
|
+
this.#terminal.release();
|
|
1497
1649
|
}
|
|
1498
1650
|
}
|
|
1499
1651
|
async select(options) {
|
|
@@ -1640,7 +1792,8 @@ function createLogger(printer, category, options) {
|
|
|
1640
1792
|
const prompts = new PromptController({
|
|
1641
1793
|
interactive: options.interactive,
|
|
1642
1794
|
input: options.input,
|
|
1643
|
-
print: (event) => options.printPrompt(event)
|
|
1795
|
+
print: (event) => options.printPrompt(event),
|
|
1796
|
+
terminal: options.terminal
|
|
1644
1797
|
});
|
|
1645
1798
|
return createBoundLogger(printer, [...category], {}, prompts);
|
|
1646
1799
|
}
|
|
@@ -1865,8 +2018,8 @@ function resolveCommand(root, rawArgs) {
|
|
|
1865
2018
|
remaining = remaining.slice(index + 1);
|
|
1866
2019
|
}
|
|
1867
2020
|
}
|
|
1868
|
-
async function executeCommand(root, argv, displayUsage, printer, loggerOptions,
|
|
1869
|
-
const env =
|
|
2021
|
+
async function executeCommand(root, argv, displayUsage, printer, loggerOptions, execContext) {
|
|
2022
|
+
const env = execContext.env ?? process.env;
|
|
1870
2023
|
const resolved = resolveCommand(root, argv);
|
|
1871
2024
|
if (resolved.segments[0]?.tokenized.version) {
|
|
1872
2025
|
await printer.print({
|
|
@@ -1888,7 +2041,7 @@ async function executeCommand(root, argv, displayUsage, printer, loggerOptions,
|
|
|
1888
2041
|
cmd: resolved.command,
|
|
1889
2042
|
rawArgs: resolved.rawArgs,
|
|
1890
2043
|
logger: createLogger(printer, category, loggerOptions),
|
|
1891
|
-
exec: createExec(
|
|
2044
|
+
exec: createExec(execContext)
|
|
1892
2045
|
});
|
|
1893
2046
|
}
|
|
1894
2047
|
function printUsage(displayUsage, command, printer, stream) {
|
|
@@ -1909,8 +2062,21 @@ function prepareOutput(options, env) {
|
|
|
1909
2062
|
});
|
|
1910
2063
|
return {
|
|
1911
2064
|
...createPrintQueue(selectOutputPrinter(options, options.printer, mode)),
|
|
2065
|
+
format: mode.format,
|
|
1912
2066
|
interactive: mode.interactive,
|
|
1913
|
-
input
|
|
2067
|
+
input,
|
|
2068
|
+
terminal: new TerminalLock()
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
function invocationExec(output, env) {
|
|
2072
|
+
return {
|
|
2073
|
+
env,
|
|
2074
|
+
mode: {
|
|
2075
|
+
format: output.format,
|
|
2076
|
+
interactive: output.interactive
|
|
2077
|
+
},
|
|
2078
|
+
print: (event) => output.printer.print(event),
|
|
2079
|
+
terminal: output.terminal
|
|
1914
2080
|
};
|
|
1915
2081
|
}
|
|
1916
2082
|
async function runCommand(root, argv, options = {}) {
|
|
@@ -1919,8 +2085,9 @@ async function runCommand(root, argv, options = {}) {
|
|
|
1919
2085
|
return await executeCommand(root, argv, options.showUsage ?? showUsage, output.printer, {
|
|
1920
2086
|
interactive: output.interactive,
|
|
1921
2087
|
input: output.input,
|
|
1922
|
-
printPrompt: (event) => output.printChecked(event)
|
|
1923
|
-
|
|
2088
|
+
printPrompt: (event) => output.printChecked(event),
|
|
2089
|
+
terminal: output.terminal
|
|
2090
|
+
}, invocationExec(output, options.env));
|
|
1924
2091
|
} finally {
|
|
1925
2092
|
await output.drain();
|
|
1926
2093
|
}
|
|
@@ -1962,8 +2129,9 @@ async function runMain(command, options = {}) {
|
|
|
1962
2129
|
await executeCommand(command, options.argv ?? process.argv.slice(2), displayUsage, output.printer, {
|
|
1963
2130
|
interactive: output.interactive,
|
|
1964
2131
|
input: output.input,
|
|
1965
|
-
printPrompt: (event) => output.printChecked(event)
|
|
1966
|
-
|
|
2132
|
+
printPrompt: (event) => output.printChecked(event),
|
|
2133
|
+
terminal: output.terminal
|
|
2134
|
+
}, invocationExec(output, options.env));
|
|
1967
2135
|
} catch (error) {
|
|
1968
2136
|
if (options.onError) {
|
|
1969
2137
|
await options.onError(error);
|