Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions apps/desktop/src/main/lib/terminal-host/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const SOCKET_PATH = join(SUPERSET_HOME_DIR, "terminal-host.sock");
const TOKEN_PATH = join(SUPERSET_HOME_DIR, "terminal-host.token");
const PID_PATH = join(SUPERSET_HOME_DIR, "terminal-host.pid");
const SPAWN_LOCK_PATH = join(SUPERSET_HOME_DIR, "terminal-host.spawn.lock");
const SCRIPT_MTIME_PATH = join(SUPERSET_HOME_DIR, "terminal-host.mtime");

// Connection timeouts
const CONNECT_TIMEOUT_MS = 5000;
Expand Down Expand Up @@ -295,6 +296,18 @@ export class TerminalHostClient extends EventEmitter {
*/
private async connectAndAuthenticate(): Promise<void> {
for (let attempt = 0; attempt < 2; attempt++) {
if (attempt === 0 && process.env.NODE_ENV === "development") {
if (this.isDaemonScriptStale()) {
if (DEBUG_CLIENT) {
console.log(
"[TerminalHostClient] Daemon script rebuilt, restarting...",
);
}
this.killDaemonFromPidFile();
await this.waitForDaemonShutdown();
}
}

let controlConnected = await this.tryConnectControl();
if (!controlConnected) {
await this.spawnDaemon();
Expand Down Expand Up @@ -354,6 +367,47 @@ export class TerminalHostClient extends EventEmitter {
throw new Error("Failed to connect after protocol upgrade");
}

/**
* Check if the daemon script has been rebuilt since the daemon was spawned.
* Only used in development mode to detect stale daemons.
*/
private isDaemonScriptStale(): boolean {
try {
if (!existsSync(SCRIPT_MTIME_PATH)) {
return false; // No mtime file = first run or manual cleanup
}

const savedMtime = readFileSync(SCRIPT_MTIME_PATH, "utf-8").trim();
const scriptPath = this.getDaemonScriptPath();

if (!existsSync(scriptPath)) {
return false;
}

const currentMtime = statSync(scriptPath).mtimeMs.toString();
return savedMtime !== currentMtime;
} catch {
return false; // On error, don't restart
}
}

/**
* Save the daemon script's mtime to detect rebuilds.
*/
private saveDaemonScriptMtime(): void {
try {
const scriptPath = this.getDaemonScriptPath();
if (!existsSync(scriptPath)) {
return;
}

const mtime = statSync(scriptPath).mtimeMs.toString();
writeFileSync(SCRIPT_MTIME_PATH, mtime, { mode: 0o600 });
} catch {
// Best-effort
}
}

private killDaemonFromPidFile(): void {
if (!existsSync(PID_PATH)) return;

Expand Down Expand Up @@ -1087,6 +1141,11 @@ export class TerminalHostClient extends EventEmitter {
}
await this.waitForDaemon();

// In development mode, save the script mtime to detect rebuilds
if (process.env.NODE_ENV === "development") {
this.saveDaemonScriptMtime();
}

if (DEBUG_CLIENT) {
console.log("[TerminalHostClient] Daemon started successfully");
}
Expand Down
Loading