check / check (push) Canceled after 0s
Every line quak prints on stderr now goes through one log, in src/cli-log.ts, at one of six levels. The default, info, prints what quak printed before. -v adds each retry and the start and end of each refresh and ML data fetch; -vv adds each request with its result. -q prints only fatal lines, and is refused with -v; both are program options. Every non-zero exit logs one fatal line, so a run that exits 1 because files failed says how many and where to look. Every line reporting a failure is at error. --json changes stdout only, so backup and --json runs log their progress on stderr. withRetry and ApiClient report retries and requests through new callbacks. Model: opus-5-5
51 lines
1.7 KiB
TypeScript
51 lines
1.7 KiB
TypeScript
// Runs one CLI command for `bin/quak.ts` and exits with its code.
|
|
|
|
import type { Writable } from "node:stream";
|
|
import { ApiError } from "./api/client.js";
|
|
import type { Log } from "./cli-log.js";
|
|
import { describeError } from "./errors.js";
|
|
|
|
// Run a command and exit with its code once stdout/stderr have drained.
|
|
// Exiting before the drain can truncate piped output, and the library can keep
|
|
// the event loop alive after a command returns, so a plain return could hang.
|
|
// An error the command throws is logged as one fatal `quak: MESSAGE` line,
|
|
// without the stack trace, and exits 1. MESSAGE is the error as
|
|
// `describeError` gives it, so it carries the reason Node's `fetch` puts on
|
|
// `cause`. A 401 from the server means it no longer accepts the saved session:
|
|
// that logs one fatal line saying to log in again and exits 3, as a missing or
|
|
// corrupt session file does.
|
|
export const run = async (
|
|
command: Promise<number>,
|
|
stdout: Writable,
|
|
stderr: Writable,
|
|
log: Log,
|
|
exit: (code: number) => void,
|
|
): Promise<void> => {
|
|
let code: number;
|
|
try {
|
|
code = await command;
|
|
} catch (err) {
|
|
if (err instanceof ApiError && err.status === 401) {
|
|
log(
|
|
"fatal",
|
|
`quak: the saved session is no longer valid; run "quak login"`,
|
|
);
|
|
code = 3;
|
|
} else {
|
|
log("fatal", `quak: ${describeError(err)}`);
|
|
code = 1;
|
|
}
|
|
}
|
|
const pending = [stdout, stderr].filter((s) => s.writableLength > 0);
|
|
if (pending.length === 0) {
|
|
exit(code);
|
|
return;
|
|
}
|
|
let remaining = pending.length;
|
|
for (const s of pending) {
|
|
s.once("drain", () => {
|
|
if (--remaining === 0) exit(code);
|
|
});
|
|
}
|
|
};
|