hacksh 0.6.0 → 0.8.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 +43 -1
- package/dist/index.d.ts +123 -98
- package/dist/index.js +469 -266
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,6 +3,8 @@ import { inspect, parseArgs as parseArgs$1, stripVTControlCharacters, styleText
|
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { emitKeypressEvents } from "node:readline";
|
|
5
5
|
import { PassThrough } from "node:stream";
|
|
6
|
+
import { readFile, realpath } from "node:fs/promises";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
6
8
|
//#region src/options.ts
|
|
7
9
|
function kebabCase(value) {
|
|
8
10
|
return value.replaceAll(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
@@ -643,6 +645,395 @@ async function parseArgs(command, argv, options) {
|
|
|
643
645
|
return (await parseCommandArgs(command, argv, tokenizeCommandArgs(command, argv), options?.env, envPath)).args;
|
|
644
646
|
}
|
|
645
647
|
//#endregion
|
|
648
|
+
//#region src/terminal.ts
|
|
649
|
+
var TerminalLock = class {
|
|
650
|
+
#owner;
|
|
651
|
+
get owner() {
|
|
652
|
+
return this.#owner;
|
|
653
|
+
}
|
|
654
|
+
acquire(owner) {
|
|
655
|
+
if (this.#owner !== void 0) return false;
|
|
656
|
+
this.#owner = owner;
|
|
657
|
+
return true;
|
|
658
|
+
}
|
|
659
|
+
release() {
|
|
660
|
+
this.#owner = void 0;
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
//#endregion
|
|
664
|
+
//#region src/exec.ts
|
|
665
|
+
const OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
666
|
+
"cwd",
|
|
667
|
+
"env",
|
|
668
|
+
"input",
|
|
669
|
+
"reject",
|
|
670
|
+
"timeout",
|
|
671
|
+
"signal",
|
|
672
|
+
"maxBuffer",
|
|
673
|
+
"forceKillAfterDelay",
|
|
674
|
+
"stdin",
|
|
675
|
+
"stdout",
|
|
676
|
+
"stderr",
|
|
677
|
+
"interactive",
|
|
678
|
+
"display",
|
|
679
|
+
"redact"
|
|
680
|
+
]);
|
|
681
|
+
const MAX_DELAY = 2147483647;
|
|
682
|
+
const REFUSAL_MESSAGE = {
|
|
683
|
+
"json-output": "Command cannot use the terminal in JSON output mode",
|
|
684
|
+
"non-interactive": "Command requires an interactive terminal",
|
|
685
|
+
overlapping: "Command cannot use the terminal while it is in use"
|
|
686
|
+
};
|
|
687
|
+
function isRecord(value) {
|
|
688
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
689
|
+
}
|
|
690
|
+
function assertNoNul(value, label) {
|
|
691
|
+
if (value.includes("\0")) throw new TypeError(`${label} must not contain NUL characters`);
|
|
692
|
+
}
|
|
693
|
+
function readInteger(value, label, minimum, maximum) {
|
|
694
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < minimum || value > maximum) throw new TypeError(`${label} must be an integer from ${minimum} to ${maximum}`);
|
|
695
|
+
return value;
|
|
696
|
+
}
|
|
697
|
+
function readEnum(value, label, allowed) {
|
|
698
|
+
if (typeof value === "string" && allowed.includes(value)) return value;
|
|
699
|
+
throw new TypeError(`Exec option \`${label}\` must be ${allowed.map((item) => JSON.stringify(item)).join(" or ")}`);
|
|
700
|
+
}
|
|
701
|
+
function readBoolean(value, label) {
|
|
702
|
+
if (typeof value !== "boolean") throw new TypeError(`Exec option \`${label}\` must be a boolean`);
|
|
703
|
+
return value;
|
|
704
|
+
}
|
|
705
|
+
function readRedact(value) {
|
|
706
|
+
if (!Array.isArray(value)) throw new TypeError("Exec option `redact` must be an array");
|
|
707
|
+
for (const item of value) {
|
|
708
|
+
if (typeof item !== "string" || item === "") throw new TypeError("Exec option `redact` must contain only non-empty strings");
|
|
709
|
+
assertNoNul(item, "Exec option `redact`");
|
|
710
|
+
}
|
|
711
|
+
return value;
|
|
712
|
+
}
|
|
713
|
+
function mergeEnvironment(base, overrides) {
|
|
714
|
+
if (overrides === void 0 && base === void 0) return void 0;
|
|
715
|
+
if (overrides !== void 0 && !isRecord(overrides)) throw new TypeError("Exec option `env` must be an object");
|
|
716
|
+
const merged = {};
|
|
717
|
+
for (const [key, value] of Object.entries(base ?? process.env)) if (value !== void 0) merged[key] = value;
|
|
718
|
+
for (const [key, value] of Object.entries(overrides ?? {})) {
|
|
719
|
+
if (key === "" || key.includes("=") || key.includes("\0")) throw new TypeError(`Invalid environment variable name ${JSON.stringify(key)}`);
|
|
720
|
+
if (value === void 0) {
|
|
721
|
+
delete merged[key];
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
724
|
+
if (typeof value !== "string") throw new TypeError(`Environment variable ${key} must be a string or undefined`);
|
|
725
|
+
assertNoNul(value, `Environment variable ${key}`);
|
|
726
|
+
merged[key] = value;
|
|
727
|
+
}
|
|
728
|
+
return merged;
|
|
729
|
+
}
|
|
730
|
+
function redactValue(value, secrets) {
|
|
731
|
+
let redacted = value;
|
|
732
|
+
for (const secret of [...secrets].sort((left, right) => right.length - left.length)) redacted = redacted.split(secret).join("***");
|
|
733
|
+
return redacted;
|
|
734
|
+
}
|
|
735
|
+
function redactCommand(command, secrets) {
|
|
736
|
+
if (secrets.length === 0) return command;
|
|
737
|
+
return {
|
|
738
|
+
file: redactValue(command.file, secrets),
|
|
739
|
+
args: command.args.map((arg) => redactValue(arg, secrets)),
|
|
740
|
+
cwd: command.cwd === void 0 ? void 0 : redactValue(command.cwd, secrets)
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
function redactError(error, secrets) {
|
|
744
|
+
const source = error;
|
|
745
|
+
const redacted = new Error(redactValue(error.message, secrets));
|
|
746
|
+
if (typeof error.stack === "string") redacted.stack = redactValue(error.stack, secrets);
|
|
747
|
+
if (source.code !== void 0) redacted.code = source.code;
|
|
748
|
+
if (source.errno !== void 0) redacted.errno = source.errno;
|
|
749
|
+
if (typeof source.syscall === "string") redacted.syscall = redactValue(source.syscall, secrets);
|
|
750
|
+
if (typeof source.path === "string") redacted.path = redactValue(source.path, secrets);
|
|
751
|
+
if (Array.isArray(source.spawnargs)) redacted.spawnargs = source.spawnargs.map((arg) => typeof arg === "string" ? redactValue(arg, secrets) : arg);
|
|
752
|
+
return redacted;
|
|
753
|
+
}
|
|
754
|
+
function observedError(plan, error) {
|
|
755
|
+
return plan.redact.length === 0 ? error : redactError(error, plan.redact);
|
|
756
|
+
}
|
|
757
|
+
function parsePlan(file, args, options, baseEnv) {
|
|
758
|
+
if (typeof file !== "string" || file === "") throw new TypeError("Exec file must be a non-empty string");
|
|
759
|
+
assertNoNul(file, "Exec file");
|
|
760
|
+
const argv = args === void 0 ? [] : args;
|
|
761
|
+
if (!Array.isArray(argv) || !argv.every((arg) => typeof arg === "string")) throw new TypeError("Exec args must be an array of strings");
|
|
762
|
+
for (const arg of argv) assertNoNul(arg, "Exec args");
|
|
763
|
+
const raw = options === void 0 ? {} : options;
|
|
764
|
+
if (!isRecord(raw)) throw new TypeError("Exec options must be an object");
|
|
765
|
+
for (const key of Object.keys(raw)) if (!OPTION_KEYS.has(key)) throw new TypeError(`Unknown exec option \`${key}\``);
|
|
766
|
+
const { cwd, input, reject, timeout, signal, maxBuffer, forceKillAfterDelay } = raw;
|
|
767
|
+
if (cwd !== void 0) {
|
|
768
|
+
if (typeof cwd !== "string" || cwd === "") throw new TypeError("Exec option `cwd` must be a non-empty string");
|
|
769
|
+
assertNoNul(cwd, "Exec option `cwd`");
|
|
770
|
+
}
|
|
771
|
+
if (input !== void 0 && typeof input !== "string") throw new TypeError("Exec option `input` must be a string");
|
|
772
|
+
if (reject !== void 0 && typeof reject !== "boolean") throw new TypeError("Exec option `reject` must be a boolean");
|
|
773
|
+
if (signal !== void 0 && !(signal instanceof AbortSignal)) throw new TypeError("Exec option `signal` must be an AbortSignal");
|
|
774
|
+
const stdin = raw.stdin === void 0 ? "pipe" : readEnum(raw.stdin, "stdin", ["pipe", "inherit"]);
|
|
775
|
+
const stdout = raw.stdout === void 0 ? "pipe" : readEnum(raw.stdout, "stdout", ["pipe", "inherit"]);
|
|
776
|
+
const stderr = raw.stderr === void 0 ? "pipe" : readEnum(raw.stderr, "stderr", ["pipe", "inherit"]);
|
|
777
|
+
if (input !== void 0 && stdin === "inherit") throw new TypeError("Exec option `input` cannot be combined with stdin inherit");
|
|
778
|
+
const command = {
|
|
779
|
+
file,
|
|
780
|
+
args: [...argv],
|
|
781
|
+
cwd
|
|
782
|
+
};
|
|
783
|
+
const redact = raw.redact === void 0 ? [] : readRedact(raw.redact);
|
|
784
|
+
return {
|
|
785
|
+
command,
|
|
786
|
+
reported: redactCommand(command, redact),
|
|
787
|
+
env: mergeEnvironment(baseEnv, raw.env),
|
|
788
|
+
input,
|
|
789
|
+
reject: reject ?? true,
|
|
790
|
+
timeout: timeout === void 0 ? void 0 : readInteger(timeout, "Exec option `timeout`", 1, MAX_DELAY),
|
|
791
|
+
signal,
|
|
792
|
+
maxBuffer: maxBuffer === void 0 ? 1e8 : readInteger(maxBuffer, "Exec option `maxBuffer`", 1, Number.MAX_SAFE_INTEGER),
|
|
793
|
+
forceKillAfterDelay: forceKillAfterDelay === void 0 ? 5e3 : readInteger(forceKillAfterDelay, "Exec option `forceKillAfterDelay`", 0, MAX_DELAY),
|
|
794
|
+
stdin,
|
|
795
|
+
stdout,
|
|
796
|
+
stderr,
|
|
797
|
+
interactive: raw.interactive === void 0 ? false : readBoolean(raw.interactive, "interactive"),
|
|
798
|
+
display: raw.display === void 0 ? true : readBoolean(raw.display, "display"),
|
|
799
|
+
redact
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
function stripFinalNewline(text) {
|
|
803
|
+
if (text.endsWith("\r\n")) return text.slice(0, -2);
|
|
804
|
+
if (text.endsWith("\n")) return text.slice(0, -1);
|
|
805
|
+
return text;
|
|
806
|
+
}
|
|
807
|
+
function collect(stream, limit, onLimit) {
|
|
808
|
+
const chunks = [];
|
|
809
|
+
let length = 0;
|
|
810
|
+
let exceeded = false;
|
|
811
|
+
stream.on("data", (chunk) => {
|
|
812
|
+
if (exceeded) return;
|
|
813
|
+
const remaining = limit - length;
|
|
814
|
+
if (chunk.length <= remaining) {
|
|
815
|
+
chunks.push(chunk);
|
|
816
|
+
length += chunk.length;
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
chunks.push(chunk.subarray(0, remaining));
|
|
820
|
+
length = limit;
|
|
821
|
+
exceeded = true;
|
|
822
|
+
onLimit();
|
|
823
|
+
});
|
|
824
|
+
return () => stripFinalNewline(Buffer.concat(chunks).toString("utf8"));
|
|
825
|
+
}
|
|
826
|
+
function toResult(command, outcome, stdout, stderr) {
|
|
827
|
+
const failed = {
|
|
828
|
+
status: "failed",
|
|
829
|
+
command,
|
|
830
|
+
stdout,
|
|
831
|
+
stderr
|
|
832
|
+
};
|
|
833
|
+
if (outcome.cause) return {
|
|
834
|
+
...failed,
|
|
835
|
+
reason: outcome.cause
|
|
836
|
+
};
|
|
837
|
+
if (outcome.spawnError) return {
|
|
838
|
+
...failed,
|
|
839
|
+
reason: "spawn",
|
|
840
|
+
error: outcome.spawnError
|
|
841
|
+
};
|
|
842
|
+
const { code, signal } = outcome.exit ?? {
|
|
843
|
+
code: null,
|
|
844
|
+
signal: null
|
|
845
|
+
};
|
|
846
|
+
if (signal !== null) return {
|
|
847
|
+
...failed,
|
|
848
|
+
reason: "signal",
|
|
849
|
+
signal
|
|
850
|
+
};
|
|
851
|
+
if (code !== 0) return {
|
|
852
|
+
...failed,
|
|
853
|
+
reason: "exit-code",
|
|
854
|
+
exitCode: code ?? 1
|
|
855
|
+
};
|
|
856
|
+
return {
|
|
857
|
+
status: "succeeded",
|
|
858
|
+
command,
|
|
859
|
+
stdout,
|
|
860
|
+
stderr,
|
|
861
|
+
exitCode: 0
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
function isSpawnErrno(error) {
|
|
865
|
+
return error instanceof Error && "syscall" in error;
|
|
866
|
+
}
|
|
867
|
+
function commandStdio(plan) {
|
|
868
|
+
return {
|
|
869
|
+
stdin: plan.stdin === "inherit" ? "inherit" : plan.input === void 0 ? "ignore" : "input",
|
|
870
|
+
stdout: plan.stdout,
|
|
871
|
+
stderr: plan.stderr
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
function usesTerminal(plan, stdio) {
|
|
875
|
+
return plan.interactive || stdio.stdin === "inherit" || stdio.stdout === "inherit" || stdio.stderr === "inherit";
|
|
876
|
+
}
|
|
877
|
+
function startChild(plan) {
|
|
878
|
+
const stdio = commandStdio(plan);
|
|
879
|
+
try {
|
|
880
|
+
return spawn(plan.command.file, plan.command.args, {
|
|
881
|
+
cwd: plan.command.cwd,
|
|
882
|
+
env: plan.env,
|
|
883
|
+
shell: false,
|
|
884
|
+
windowsHide: true,
|
|
885
|
+
stdio: [
|
|
886
|
+
stdio.stdin === "inherit" ? "inherit" : stdio.stdin === "input" ? "pipe" : "ignore",
|
|
887
|
+
stdio.stdout,
|
|
888
|
+
stdio.stderr
|
|
889
|
+
]
|
|
890
|
+
});
|
|
891
|
+
} catch (error) {
|
|
892
|
+
if (isSpawnErrno(error)) return error;
|
|
893
|
+
throw error;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
function runPlan(plan) {
|
|
897
|
+
const outcome = {
|
|
898
|
+
cause: void 0,
|
|
899
|
+
spawnError: void 0,
|
|
900
|
+
exit: void 0
|
|
901
|
+
};
|
|
902
|
+
if (plan.signal?.aborted) {
|
|
903
|
+
outcome.cause = "aborted";
|
|
904
|
+
return Promise.resolve(toResult(plan.reported, outcome, "", ""));
|
|
905
|
+
}
|
|
906
|
+
const child = startChild(plan);
|
|
907
|
+
if (child instanceof Error) {
|
|
908
|
+
outcome.spawnError = observedError(plan, child);
|
|
909
|
+
return Promise.resolve(toResult(plan.reported, outcome, "", ""));
|
|
910
|
+
}
|
|
911
|
+
return new Promise((resolve) => {
|
|
912
|
+
const stdout = child.stdout;
|
|
913
|
+
const stderr = child.stderr;
|
|
914
|
+
let settled = false;
|
|
915
|
+
let killTimer;
|
|
916
|
+
const settle = () => {
|
|
917
|
+
if (settled) return;
|
|
918
|
+
settled = true;
|
|
919
|
+
clearTimeout(timeoutTimer);
|
|
920
|
+
clearTimeout(killTimer);
|
|
921
|
+
plan.signal?.removeEventListener("abort", onAbort);
|
|
922
|
+
resolve(toResult(plan.reported, outcome, readStdout(), readStderr()));
|
|
923
|
+
};
|
|
924
|
+
const release = () => {
|
|
925
|
+
stdout?.destroy();
|
|
926
|
+
stderr?.destroy();
|
|
927
|
+
settle();
|
|
928
|
+
};
|
|
929
|
+
const terminate = (cause) => {
|
|
930
|
+
if (settled || outcome.cause) return;
|
|
931
|
+
outcome.cause = cause;
|
|
932
|
+
if (outcome.exit) {
|
|
933
|
+
release();
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
child.kill("SIGTERM");
|
|
937
|
+
killTimer = setTimeout(() => child.kill("SIGKILL"), plan.forceKillAfterDelay);
|
|
938
|
+
};
|
|
939
|
+
const onAbort = () => terminate("aborted");
|
|
940
|
+
const readStdout = stdout ? collect(stdout, plan.maxBuffer, () => terminate("output-limit")) : () => "";
|
|
941
|
+
const readStderr = stderr ? collect(stderr, plan.maxBuffer, () => terminate("output-limit")) : () => "";
|
|
942
|
+
const timeoutTimer = plan.timeout === void 0 ? void 0 : setTimeout(() => terminate("timeout"), plan.timeout);
|
|
943
|
+
plan.signal?.addEventListener("abort", onAbort, { once: true });
|
|
944
|
+
child.on("error", (error) => {
|
|
945
|
+
if (child.pid !== void 0) return;
|
|
946
|
+
outcome.spawnError = observedError(plan, error);
|
|
947
|
+
release();
|
|
948
|
+
});
|
|
949
|
+
child.on("exit", (code, signal) => {
|
|
950
|
+
outcome.exit = {
|
|
951
|
+
code,
|
|
952
|
+
signal
|
|
953
|
+
};
|
|
954
|
+
clearTimeout(killTimer);
|
|
955
|
+
if (outcome.cause) release();
|
|
956
|
+
});
|
|
957
|
+
child.on("close", settle);
|
|
958
|
+
if (child.stdin) {
|
|
959
|
+
child.stdin.on("error", () => void 0);
|
|
960
|
+
child.stdin.end(plan.input);
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
function formatArgument(value) {
|
|
965
|
+
return value === "" || /[\s"'\\$`;|&<>*?()[\]{}~#!]/.test(value) ? JSON.stringify(value) : value;
|
|
966
|
+
}
|
|
967
|
+
function formatExecCommand(command) {
|
|
968
|
+
return [command.file, ...command.args].map(formatArgument).join(" ");
|
|
969
|
+
}
|
|
970
|
+
function failureMessage(plan, result) {
|
|
971
|
+
const display = formatExecCommand(plan.reported);
|
|
972
|
+
switch (result.reason) {
|
|
973
|
+
case "exit-code": return `Command failed with exit code ${result.exitCode}: ${display}`;
|
|
974
|
+
case "signal": return `Command was terminated by ${result.signal}: ${display}`;
|
|
975
|
+
case "spawn": return `Command could not be started (${result.error.code ?? result.error.message}): ${display}`;
|
|
976
|
+
case "timeout": return `Command timed out after ${plan.timeout} ms: ${display}`;
|
|
977
|
+
case "aborted": return `Command was aborted: ${display}`;
|
|
978
|
+
case "output-limit": return `Command output exceeded ${plan.maxBuffer} bytes: ${display}`;
|
|
979
|
+
case "json-output":
|
|
980
|
+
case "non-interactive":
|
|
981
|
+
case "overlapping": return `${REFUSAL_MESSAGE[result.reason]}: ${display}`;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
function emptyFailure(plan, reason) {
|
|
985
|
+
return {
|
|
986
|
+
status: "failed",
|
|
987
|
+
command: plan.reported,
|
|
988
|
+
stdout: "",
|
|
989
|
+
stderr: "",
|
|
990
|
+
reason
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
function complete(plan, result) {
|
|
994
|
+
if (result.status === "failed" && plan.reject) throw new HackshExecError(failureMessage(plan, result), result, result.reason === "spawn" ? { cause: result.error } : void 0);
|
|
995
|
+
return result;
|
|
996
|
+
}
|
|
997
|
+
function blockedReason(plan, context, stdio) {
|
|
998
|
+
const inherited = stdio.stdin === "inherit" || stdio.stdout === "inherit" || stdio.stderr === "inherit";
|
|
999
|
+
if (context.mode.format === "json" && inherited) return "json-output";
|
|
1000
|
+
if (plan.interactive && !context.mode.interactive) return "non-interactive";
|
|
1001
|
+
}
|
|
1002
|
+
async function execute(file, args, options, context) {
|
|
1003
|
+
const plan = parsePlan(file, args, options, context.env);
|
|
1004
|
+
if (plan.signal?.aborted) return complete(plan, emptyFailure(plan, "aborted"));
|
|
1005
|
+
const stdio = commandStdio(plan);
|
|
1006
|
+
const blocked = blockedReason(plan, context, stdio);
|
|
1007
|
+
if (blocked) return complete(plan, emptyFailure(plan, blocked));
|
|
1008
|
+
const holdTerminal = usesTerminal(plan, stdio);
|
|
1009
|
+
if (holdTerminal && !context.terminal.acquire("exec")) return complete(plan, emptyFailure(plan, "overlapping"));
|
|
1010
|
+
try {
|
|
1011
|
+
if (plan.display) {
|
|
1012
|
+
const { file, args, cwd } = plan.reported;
|
|
1013
|
+
await context.print({
|
|
1014
|
+
type: "exec",
|
|
1015
|
+
phase: "start",
|
|
1016
|
+
command: cwd === void 0 ? {
|
|
1017
|
+
file,
|
|
1018
|
+
args
|
|
1019
|
+
} : {
|
|
1020
|
+
file,
|
|
1021
|
+
args,
|
|
1022
|
+
cwd
|
|
1023
|
+
},
|
|
1024
|
+
stdio,
|
|
1025
|
+
interactive: plan.interactive
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
return complete(plan, await runPlan(plan));
|
|
1029
|
+
} finally {
|
|
1030
|
+
if (holdTerminal) context.terminal.release();
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
function createExec(context) {
|
|
1034
|
+
return ((file, args, options) => execute(file, args, options, context));
|
|
1035
|
+
}
|
|
1036
|
+
//#endregion
|
|
646
1037
|
//#region src/serialization.ts
|
|
647
1038
|
function safeString(value) {
|
|
648
1039
|
try {
|
|
@@ -771,7 +1162,7 @@ function isErrorLevel(level) {
|
|
|
771
1162
|
return level === "warning" || level === "error" || level === "fatal";
|
|
772
1163
|
}
|
|
773
1164
|
function eventStream(event) {
|
|
774
|
-
if (event.type === "error") return "stderr";
|
|
1165
|
+
if (event.type === "error" || event.type === "exec") return "stderr";
|
|
775
1166
|
if (event.type === "usage") return event.stream;
|
|
776
1167
|
if (event.type === "log" && isErrorLevel(event.record.level)) return "stderr";
|
|
777
1168
|
return "stdout";
|
|
@@ -783,6 +1174,7 @@ function formatTextEvent(event) {
|
|
|
783
1174
|
case "error": return event.error.kind === "usage" || event.error.kind === "validation" ? event.error.message : `Error: ${event.error.message}`;
|
|
784
1175
|
case "log": return event.record.message;
|
|
785
1176
|
case "prompt": return event.prompt.message;
|
|
1177
|
+
case "exec": return `$ ${formatExecCommand(event.command)}`;
|
|
786
1178
|
}
|
|
787
1179
|
}
|
|
788
1180
|
/** Undecorated console compatibility adapter; automatic output uses the selected format printer. */
|
|
@@ -872,7 +1264,8 @@ const unicodeSymbols = {
|
|
|
872
1264
|
usage: "◆",
|
|
873
1265
|
version: "●",
|
|
874
1266
|
errorEvent: "■",
|
|
875
|
-
prompt: "?"
|
|
1267
|
+
prompt: "?",
|
|
1268
|
+
exec: "$"
|
|
876
1269
|
}
|
|
877
1270
|
};
|
|
878
1271
|
const asciiSymbols = {
|
|
@@ -889,7 +1282,8 @@ const asciiSymbols = {
|
|
|
889
1282
|
usage: "?",
|
|
890
1283
|
version: "i",
|
|
891
1284
|
errorEvent: "x",
|
|
892
|
-
prompt: "?"
|
|
1285
|
+
prompt: "?",
|
|
1286
|
+
exec: "$"
|
|
893
1287
|
}
|
|
894
1288
|
};
|
|
895
1289
|
const markStyles = {
|
|
@@ -908,7 +1302,8 @@ const markStyles = {
|
|
|
908
1302
|
usage: "cyan",
|
|
909
1303
|
version: "blue",
|
|
910
1304
|
errorEvent: "red",
|
|
911
|
-
prompt: "cyan"
|
|
1305
|
+
prompt: "cyan",
|
|
1306
|
+
exec: "gray"
|
|
912
1307
|
};
|
|
913
1308
|
function safelyInspect(value) {
|
|
914
1309
|
if (typeof value === "string") return value;
|
|
@@ -955,6 +1350,7 @@ function eventMark(event) {
|
|
|
955
1350
|
if (event.type === "version") return "version";
|
|
956
1351
|
if (event.type === "error") return "errorEvent";
|
|
957
1352
|
if (event.type === "prompt") return event.prompt.status === "submitted" ? "success" : "prompt";
|
|
1353
|
+
if (event.type === "exec") return "exec";
|
|
958
1354
|
return event.record.kind === "message" ? event.record.level : event.record.kind;
|
|
959
1355
|
}
|
|
960
1356
|
function eventLines(event) {
|
|
@@ -976,6 +1372,11 @@ function eventLines(event) {
|
|
|
976
1372
|
if (prompt.error) lines.push(prompt.error);
|
|
977
1373
|
return lines;
|
|
978
1374
|
}
|
|
1375
|
+
case "exec": {
|
|
1376
|
+
const lines = [formatExecCommand(event.command)];
|
|
1377
|
+
if (event.command.cwd !== void 0) lines.push(...propertyLines({ cwd: event.command.cwd }));
|
|
1378
|
+
return lines;
|
|
1379
|
+
}
|
|
979
1380
|
}
|
|
980
1381
|
}
|
|
981
1382
|
function colorize(enabled, style, text) {
|
|
@@ -1059,253 +1460,6 @@ function createPrettyPrinter(options) {
|
|
|
1059
1460
|
} };
|
|
1060
1461
|
}
|
|
1061
1462
|
//#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
1463
|
//#region src/prompts.ts
|
|
1310
1464
|
function details$1(kind, message, choices) {
|
|
1311
1465
|
let normalizedMessage;
|
|
@@ -1335,11 +1489,12 @@ var PromptController = class {
|
|
|
1335
1489
|
#inputValue;
|
|
1336
1490
|
#printEvent;
|
|
1337
1491
|
#bufferedKeys = [];
|
|
1338
|
-
#
|
|
1492
|
+
#terminal;
|
|
1339
1493
|
constructor(options) {
|
|
1340
1494
|
this.#interactive = options.interactive;
|
|
1341
1495
|
this.#inputValue = options.input;
|
|
1342
1496
|
this.#printEvent = (event) => options.print(event);
|
|
1497
|
+
this.#terminal = options.terminal;
|
|
1343
1498
|
}
|
|
1344
1499
|
async #print(state, prompt) {
|
|
1345
1500
|
try {
|
|
@@ -1353,9 +1508,8 @@ var PromptController = class {
|
|
|
1353
1508
|
}
|
|
1354
1509
|
async #run(prompt, initial, accept, update, submitted, closed) {
|
|
1355
1510
|
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
1511
|
const input = asInput(this.#inputValue, prompt);
|
|
1358
|
-
this.#
|
|
1512
|
+
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
1513
|
let originallyFlowing = false;
|
|
1360
1514
|
let rawChanged = false;
|
|
1361
1515
|
let outcome;
|
|
@@ -1478,7 +1632,7 @@ var PromptController = class {
|
|
|
1478
1632
|
try {
|
|
1479
1633
|
await this.#print(closed(), prompt);
|
|
1480
1634
|
} catch {} finally {
|
|
1481
|
-
this.#
|
|
1635
|
+
this.#terminal.release();
|
|
1482
1636
|
}
|
|
1483
1637
|
throw failure;
|
|
1484
1638
|
};
|
|
@@ -1493,7 +1647,7 @@ var PromptController = class {
|
|
|
1493
1647
|
} catch {}
|
|
1494
1648
|
throw error;
|
|
1495
1649
|
} finally {
|
|
1496
|
-
this.#
|
|
1650
|
+
this.#terminal.release();
|
|
1497
1651
|
}
|
|
1498
1652
|
}
|
|
1499
1653
|
async select(options) {
|
|
@@ -1640,11 +1794,44 @@ function createLogger(printer, category, options) {
|
|
|
1640
1794
|
const prompts = new PromptController({
|
|
1641
1795
|
interactive: options.interactive,
|
|
1642
1796
|
input: options.input,
|
|
1643
|
-
print: (event) => options.printPrompt(event)
|
|
1797
|
+
print: (event) => options.printPrompt(event),
|
|
1798
|
+
terminal: options.terminal
|
|
1644
1799
|
});
|
|
1645
1800
|
return createBoundLogger(printer, [...category], {}, prompts);
|
|
1646
1801
|
}
|
|
1647
1802
|
//#endregion
|
|
1803
|
+
//#region src/package-version.ts
|
|
1804
|
+
async function findPackageVersion(entry) {
|
|
1805
|
+
if (!entry) return void 0;
|
|
1806
|
+
let directory;
|
|
1807
|
+
try {
|
|
1808
|
+
directory = dirname(await realpath(entry));
|
|
1809
|
+
} catch {
|
|
1810
|
+
return;
|
|
1811
|
+
}
|
|
1812
|
+
for (;;) {
|
|
1813
|
+
const version = await readPackageVersion(join(directory, "package.json"));
|
|
1814
|
+
if (version !== void 0) return version;
|
|
1815
|
+
const parent = dirname(directory);
|
|
1816
|
+
if (parent === directory) return void 0;
|
|
1817
|
+
directory = parent;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
async function readPackageVersion(path) {
|
|
1821
|
+
let text;
|
|
1822
|
+
try {
|
|
1823
|
+
text = await readFile(path, "utf8");
|
|
1824
|
+
} catch {
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
try {
|
|
1828
|
+
const { version } = JSON.parse(text);
|
|
1829
|
+
return typeof version === "string" ? version : void 0;
|
|
1830
|
+
} catch {
|
|
1831
|
+
return;
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
//#endregion
|
|
1648
1835
|
//#region src/output.ts
|
|
1649
1836
|
function consoleWriter(method, stream) {
|
|
1650
1837
|
return {
|
|
@@ -1865,13 +2052,14 @@ function resolveCommand(root, rawArgs) {
|
|
|
1865
2052
|
remaining = remaining.slice(index + 1);
|
|
1866
2053
|
}
|
|
1867
2054
|
}
|
|
1868
|
-
async function executeCommand(root, argv, displayUsage, printer, loggerOptions,
|
|
1869
|
-
const env =
|
|
2055
|
+
async function executeCommand(root, argv, displayUsage, printer, loggerOptions, execContext, fallbackVersion = async () => void 0) {
|
|
2056
|
+
const env = execContext.env ?? process.env;
|
|
1870
2057
|
const resolved = resolveCommand(root, argv);
|
|
1871
2058
|
if (resolved.segments[0]?.tokenized.version) {
|
|
2059
|
+
const version = root.version ?? await fallbackVersion() ?? "";
|
|
1872
2060
|
await printer.print({
|
|
1873
2061
|
type: "version",
|
|
1874
|
-
version
|
|
2062
|
+
version
|
|
1875
2063
|
});
|
|
1876
2064
|
return;
|
|
1877
2065
|
}
|
|
@@ -1888,7 +2076,7 @@ async function executeCommand(root, argv, displayUsage, printer, loggerOptions,
|
|
|
1888
2076
|
cmd: resolved.command,
|
|
1889
2077
|
rawArgs: resolved.rawArgs,
|
|
1890
2078
|
logger: createLogger(printer, category, loggerOptions),
|
|
1891
|
-
exec: createExec(
|
|
2079
|
+
exec: createExec(execContext)
|
|
1892
2080
|
});
|
|
1893
2081
|
}
|
|
1894
2082
|
function printUsage(displayUsage, command, printer, stream) {
|
|
@@ -1909,8 +2097,21 @@ function prepareOutput(options, env) {
|
|
|
1909
2097
|
});
|
|
1910
2098
|
return {
|
|
1911
2099
|
...createPrintQueue(selectOutputPrinter(options, options.printer, mode)),
|
|
2100
|
+
format: mode.format,
|
|
1912
2101
|
interactive: mode.interactive,
|
|
1913
|
-
input
|
|
2102
|
+
input,
|
|
2103
|
+
terminal: new TerminalLock()
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2106
|
+
function invocationExec(output, env) {
|
|
2107
|
+
return {
|
|
2108
|
+
env,
|
|
2109
|
+
mode: {
|
|
2110
|
+
format: output.format,
|
|
2111
|
+
interactive: output.interactive
|
|
2112
|
+
},
|
|
2113
|
+
print: (event) => output.printer.print(event),
|
|
2114
|
+
terminal: output.terminal
|
|
1914
2115
|
};
|
|
1915
2116
|
}
|
|
1916
2117
|
async function runCommand(root, argv, options = {}) {
|
|
@@ -1919,8 +2120,9 @@ async function runCommand(root, argv, options = {}) {
|
|
|
1919
2120
|
return await executeCommand(root, argv, options.showUsage ?? showUsage, output.printer, {
|
|
1920
2121
|
interactive: output.interactive,
|
|
1921
2122
|
input: output.input,
|
|
1922
|
-
printPrompt: (event) => output.printChecked(event)
|
|
1923
|
-
|
|
2123
|
+
printPrompt: (event) => output.printChecked(event),
|
|
2124
|
+
terminal: output.terminal
|
|
2125
|
+
}, invocationExec(output, options.env));
|
|
1924
2126
|
} finally {
|
|
1925
2127
|
await output.drain();
|
|
1926
2128
|
}
|
|
@@ -1962,8 +2164,9 @@ async function runMain(command, options = {}) {
|
|
|
1962
2164
|
await executeCommand(command, options.argv ?? process.argv.slice(2), displayUsage, output.printer, {
|
|
1963
2165
|
interactive: output.interactive,
|
|
1964
2166
|
input: output.input,
|
|
1965
|
-
printPrompt: (event) => output.printChecked(event)
|
|
1966
|
-
|
|
2167
|
+
printPrompt: (event) => output.printChecked(event),
|
|
2168
|
+
terminal: output.terminal
|
|
2169
|
+
}, invocationExec(output, options.env), () => findPackageVersion(process.argv[1]));
|
|
1967
2170
|
} catch (error) {
|
|
1968
2171
|
if (options.onError) {
|
|
1969
2172
|
await options.onError(error);
|