Fix CLI resource cleanup and auth flow

This commit is contained in:
Jared Miller 2026-01-28 11:50:51 -05:00
parent 54bc458b7d
commit 508a1c9837
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
2 changed files with 44 additions and 28 deletions

View file

@ -60,7 +60,9 @@ async function main() {
let pty: IPty | null = null; let pty: IPty | null = null;
let ws: WebSocket | null = null; let ws: WebSocket | null = null;
let isExiting = false; let isExiting = false;
let isAuthenticated = false;
let reconnectTimer: Timer | null = null; let reconnectTimer: Timer | null = null;
const disposables: Array<{ dispose: () => void }> = [];
const cleanup = () => { const cleanup = () => {
if (isExiting) return; if (isExiting) return;
@ -69,6 +71,12 @@ async function main() {
if (reconnectTimer) { if (reconnectTimer) {
clearTimeout(reconnectTimer); clearTimeout(reconnectTimer);
} }
for (const d of disposables) {
d.dispose();
}
if (process.stdin.isTTY) {
process.stdin.setRawMode(false);
}
if (pty) { if (pty) {
pty.kill(); pty.kill();
} }
@ -102,19 +110,28 @@ async function main() {
} }
}); });
// Forward PTY output to stdout // Forward PTY output to stdout AND WebSocket (if authenticated)
disposables.push(
pty.onData((data: string) => { pty.onData((data: string) => {
process.stdout.write(data); process.stdout.write(data);
});
if (ws && ws.readyState === WebSocket.OPEN && isAuthenticated) {
const msg: ClientMessage = { type: "output", data };
ws.send(JSON.stringify(msg));
}
}),
);
// Handle PTY exit // Handle PTY exit
disposables.push(
pty.onExit((event) => { pty.onExit((event) => {
if (ws && !isExiting) { if (ws && !isExiting) {
const msg: ClientMessage = { type: "exit", code: event.exitCode }; const msg: ClientMessage = { type: "exit", code: event.exitCode };
ws.send(JSON.stringify(msg)); ws.send(JSON.stringify(msg));
} }
cleanup(); cleanup();
}); }),
);
// Handle terminal resize // Handle terminal resize
process.stdout.on("resize", () => { process.stdout.on("resize", () => {
@ -154,15 +171,20 @@ async function main() {
}; };
ws.onmessage = (event) => { ws.onmessage = (event) => {
if (!pty) return;
try { try {
const msg: ServerMessage = JSON.parse(event.data); const msg: ServerMessage = JSON.parse(event.data);
if (msg.type === "input") { if (msg.type === "authenticated") {
isAuthenticated = true;
console.debug(`Authenticated with session ID: ${msg.session_id}`);
} else if (msg.type === "input") {
if (pty) {
pty.write(msg.data); pty.write(msg.data);
}
} else if (msg.type === "resize") { } else if (msg.type === "resize") {
if (pty) {
pty.resize(msg.cols, msg.rows); pty.resize(msg.cols, msg.rows);
}
} else if (msg.type === "ping") { } else if (msg.type === "ping") {
// Acknowledge ping (keep-alive) // Acknowledge ping (keep-alive)
} }
@ -171,13 +193,16 @@ async function main() {
} }
}; };
ws.onerror = () => { ws.onerror = (event) => {
console.error("WebSocket error"); console.error("WebSocket error:", event);
}; };
ws.onclose = () => { ws.onclose = () => {
if (isExiting) return; if (isExiting) return;
// Reset auth flag on disconnect
isAuthenticated = false;
// Try to reconnect after 2 seconds // Try to reconnect after 2 seconds
reconnectTimer = setTimeout(() => { reconnectTimer = setTimeout(() => {
console.debug("Reconnecting to server..."); console.debug("Reconnecting to server...");
@ -186,16 +211,6 @@ async function main() {
}; };
}; };
// Forward PTY output to server
if (pty) {
pty.onData((data: string) => {
if (ws && ws.readyState === WebSocket.OPEN) {
const msg: ClientMessage = { type: "output", data };
ws.send(JSON.stringify(msg));
}
});
}
connect(); connect();
} }

View file

@ -45,6 +45,7 @@ export type ClientMessage =
| { type: "prompt"; session_id: number; prompt_text: string }; | { type: "prompt"; session_id: number; prompt_text: string };
export type ServerMessage = export type ServerMessage =
| { type: "authenticated"; session_id: number }
| { type: "input"; data: string } | { type: "input"; data: string }
| { type: "resize"; cols: number; rows: number } | { type: "resize"; cols: number; rows: number }
| { type: "ping" }; | { type: "ping" };