mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 19:01:25 +00:00
CLI: restore terminal state on exit
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
import type { OnboardOptions } from "./onboard-types.js";
|
import type { OnboardOptions } from "./onboard-types.js";
|
||||||
import { defaultRuntime } from "../runtime.js";
|
import { defaultRuntime } from "../runtime.js";
|
||||||
|
import { restoreTerminalState } from "../terminal/restore.js";
|
||||||
import { createClackPrompter } from "../wizard/clack-prompter.js";
|
import { createClackPrompter } from "../wizard/clack-prompter.js";
|
||||||
import { runOnboardingWizard } from "../wizard/onboarding.js";
|
import { runOnboardingWizard } from "../wizard/onboarding.js";
|
||||||
import { WizardCancelledError } from "../wizard/prompts.js";
|
import { WizardCancelledError } from "../wizard/prompts.js";
|
||||||
@@ -18,5 +19,7 @@ export async function runInteractiveOnboarding(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
|
} finally {
|
||||||
|
restoreTerminalState("onboarding finish");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { clearActiveProgressLine } from "./terminal/progress-line.js";
|
import { clearActiveProgressLine } from "./terminal/progress-line.js";
|
||||||
|
import { restoreTerminalState } from "./terminal/restore.js";
|
||||||
|
|
||||||
export type RuntimeEnv = {
|
export type RuntimeEnv = {
|
||||||
log: typeof console.log;
|
log: typeof console.log;
|
||||||
@@ -16,6 +17,7 @@ export const defaultRuntime: RuntimeEnv = {
|
|||||||
console.error(...args);
|
console.error(...args);
|
||||||
},
|
},
|
||||||
exit: (code) => {
|
exit: (code) => {
|
||||||
|
restoreTerminalState("runtime exit");
|
||||||
process.exit(code);
|
process.exit(code);
|
||||||
throw new Error("unreachable"); // satisfies tests when mocked
|
throw new Error("unreachable"); // satisfies tests when mocked
|
||||||
},
|
},
|
||||||
|
|||||||
49
src/terminal/restore.ts
Normal file
49
src/terminal/restore.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { clearActiveProgressLine } from "./progress-line.js";
|
||||||
|
|
||||||
|
const RESET_SEQUENCE = "\x1b[0m\x1b[?25h\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l\x1b[?2004l";
|
||||||
|
|
||||||
|
function reportRestoreFailure(scope: string, err: unknown, reason?: string): void {
|
||||||
|
const suffix = reason ? ` (${reason})` : "";
|
||||||
|
const message = `[terminal] restore ${scope} failed${suffix}: ${String(err)}`;
|
||||||
|
try {
|
||||||
|
process.stderr.write(`${message}\n`);
|
||||||
|
} catch (writeErr) {
|
||||||
|
try {
|
||||||
|
console.error(`[terminal] restore reporting failed${suffix}: ${String(writeErr)}`);
|
||||||
|
} catch (consoleErr) {
|
||||||
|
throw consoleErr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function restoreTerminalState(reason?: string): void {
|
||||||
|
try {
|
||||||
|
clearActiveProgressLine();
|
||||||
|
} catch (err) {
|
||||||
|
reportRestoreFailure("progress line", err, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
const stdin = process.stdin;
|
||||||
|
if (stdin.isTTY && typeof stdin.setRawMode === "function") {
|
||||||
|
try {
|
||||||
|
stdin.setRawMode(false);
|
||||||
|
} catch (err) {
|
||||||
|
reportRestoreFailure("raw mode", err, reason);
|
||||||
|
}
|
||||||
|
if (typeof stdin.isPaused === "function" && stdin.isPaused()) {
|
||||||
|
try {
|
||||||
|
stdin.resume();
|
||||||
|
} catch (err) {
|
||||||
|
reportRestoreFailure("stdin resume", err, reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.stdout.isTTY) {
|
||||||
|
try {
|
||||||
|
process.stdout.write(RESET_SEQUENCE);
|
||||||
|
} catch (err) {
|
||||||
|
reportRestoreFailure("stdout reset", err, reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user