mirror of
https://github.com/morluto/rea.git
synced 2026-11-14 10:43:30 +00:00
refactor(cli): run one-shot commands through one shared owner (#10405)
* refactor(cli): run one-shot commands through one shared owner scripts/rea.mjs and the test CLI session worker each implemented the one-shot command sequence: argument normalization, output admission, configuration errors, version documents, the Amp scope refusal, and result delivery. The worker copy had drifted: it never rendered formatted version reports or the Amp refusal. Move the sequence into src/cliOneShot.ts. Both callers supply a host for their streams, exit and resource ownership. The launcher keeps closed-pipe status, worker shutdown and process exit; the session worker captures output per command. The cliOutput bundle entry is superseded and removed. The dispatcher test now replaces only the command runtime, so output admission runs production code. A new parity test compares the file session with a fresh process. * test(cli): bundle the one-shot entry in the runtime bundle fixture
This commit is contained in:
1 parent
f1e250ee76
commit
bddbd80afb
11 files changed
+333
-244
No files matched your search
@@ -10,8 +10,8 @@ import {
|
||||
encodeCliSessionFrame,
|
||||
} from "./cliCommandSessionFrames.mjs";
|
||||
|
||||
// One-shot command sequence matches scripts/rea.mjs. Output is captured per
|
||||
// command so this process can serve the next invocation without restarting.
|
||||
// Commands run through the same one-shot owner as scripts/rea.mjs. Output is
|
||||
// captured per command so this process can serve the next invocation.
|
||||
const packageRoot = resolve(
|
||||
dirname(fileURLToPath(import.meta.url)),
|
||||
"../../..",
|
||||
@@ -23,9 +23,7 @@ const { default: packageJson } = await import(
|
||||
|
||||
const runtimeFiles = [
|
||||
"dist/cli-runtime/cli.js",
|
||||
"dist/cli-runtime/cliOutput.js",
|
||||
"dist/cli-runtime/cliBuiltinArguments.js",
|
||||
"dist/cli-runtime/cliResultOutput.js",
|
||||
"dist/cli-runtime/cliOneShot.js",
|
||||
];
|
||||
|
||||
prepareCliProcessEnvironment(
|
||||
@@ -40,21 +38,9 @@ if (!(await compiledRuntimeExists(runtimeFiles))) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { closeCliResources, createCli } =
|
||||
await import("../../../dist/cli-runtime/cli.js");
|
||||
const { createCliResultOutput } =
|
||||
await import("../../../dist/cli-runtime/cliResultOutput.js");
|
||||
const {
|
||||
cliCompletionShell,
|
||||
normalizeCliArguments,
|
||||
splitIncurBuiltinArguments,
|
||||
} = await import("../../../dist/cli-runtime/cliBuiltinArguments.js");
|
||||
const {
|
||||
cliConfigurationError,
|
||||
renderCliOutputArgumentError,
|
||||
renderEmptyFilteredCliOutput,
|
||||
parseCliOutputArguments,
|
||||
} = await import("../../../dist/cli-runtime/cliOutput.js");
|
||||
const { closeCliResources } = await import("../../../dist/cli-runtime/cli.js");
|
||||
const { runOneShotCli } =
|
||||
await import("../../../dist/cli-runtime/cliOneShot.js");
|
||||
|
||||
const writeFrame = (value) =>
|
||||
new Promise((resolveWrite, rejectWrite) => {
|
||||
@@ -181,57 +167,45 @@ async function runOneShot(args) {
|
||||
const previousExitCode = process.exitCode;
|
||||
process.exitCode = undefined;
|
||||
let explicitExit;
|
||||
let exitOutput;
|
||||
try {
|
||||
const isShellCompletion = cliCompletionShell(process.env) !== undefined;
|
||||
const cliArgs = isShellCompletion ? args : normalizeCliArguments(args);
|
||||
const outputOptions = parseCliOutputArguments(
|
||||
isShellCompletion ? [] : cliArgs,
|
||||
);
|
||||
const outputArguments = outputOptions.validation;
|
||||
const configurationError = cliConfigurationError(process.env);
|
||||
if (
|
||||
configurationError !== undefined &&
|
||||
(isShellCompletion ||
|
||||
splitIncurBuiltinArguments(cliArgs).command[0] !== "doctor")
|
||||
) {
|
||||
process.stderr.write(`${configurationError}\n`);
|
||||
process.exitCode = 1;
|
||||
} else if (!outputArguments.ok) {
|
||||
process.stdout.write(renderCliOutputArgumentError(outputArguments));
|
||||
process.exitCode = 1;
|
||||
} else {
|
||||
let wroteOutput = false;
|
||||
const resultOutput = createCliResultOutput(outputOptions, destination);
|
||||
try {
|
||||
await createCli(process.env, resultOutput).serve(cliArgs, {
|
||||
stdout: (output) => {
|
||||
if (resultOutput?.handled) {
|
||||
if (resultOutput.failed) process.stderr.write(output);
|
||||
return;
|
||||
}
|
||||
if (output.length > 0) wroteOutput = true;
|
||||
if (resultOutput?.captureSdkOutput(output)) return;
|
||||
process.stdout.write(output);
|
||||
},
|
||||
exit: (code) => {
|
||||
explicitExit = code;
|
||||
},
|
||||
});
|
||||
} catch (cause) {
|
||||
if (wroteOutput) throw cause;
|
||||
const recovered = await resultOutput?.recoverFormattingFailure(cause);
|
||||
if (!recovered) throw cause;
|
||||
process.exitCode = 1;
|
||||
}
|
||||
if (!wroteOutput && !resultOutput?.handled) {
|
||||
const empty = renderEmptyFilteredCliOutput(outputOptions);
|
||||
if (empty) process.stdout.write(empty);
|
||||
}
|
||||
await resultOutput?.finishSdkOutput(
|
||||
explicitExit ??
|
||||
(typeof process.exitCode === "number" ? process.exitCode : 0),
|
||||
);
|
||||
}
|
||||
await runOneShotCli(args, {
|
||||
environment: process.env,
|
||||
runtime: {
|
||||
packageName: packageJson.name,
|
||||
packageVersion: packageJson.version,
|
||||
versions: process.versions,
|
||||
platform: process.platform,
|
||||
architecture: process.arch,
|
||||
},
|
||||
stdoutIsTerminal: destination.isTTY,
|
||||
resultDestination: destination,
|
||||
// The session frames own this process's stdout; a command's reader
|
||||
// cannot close it.
|
||||
closedStdoutStatus: 1,
|
||||
writeStdout: (text) => {
|
||||
process.stdout.write(text);
|
||||
},
|
||||
writeStderr: (text) => {
|
||||
process.stderr.write(text);
|
||||
},
|
||||
stdoutClosed: () => false,
|
||||
exitCode: () =>
|
||||
typeof process.exitCode === "number" ? process.exitCode : 0,
|
||||
setExitCode: (code) => {
|
||||
process.exitCode = code;
|
||||
},
|
||||
// The session closes CLI resources once, after its last command.
|
||||
ownCliResources: () => {},
|
||||
exitRequested: () => exitOutput !== undefined,
|
||||
// This process serves the next command; record the status and finish
|
||||
// the pending result in place of exiting.
|
||||
exit: (code, finishOutput) => {
|
||||
explicitExit = code;
|
||||
exitOutput = finishOutput(code);
|
||||
},
|
||||
});
|
||||
await exitOutput;
|
||||
} finally {
|
||||
process.stdout.write = originalStdoutWrite;
|
||||
process.stderr.write = originalStderrWrite;
|
||||
|
||||
Reference in new issue
Block a user