import { spawn } from "node:child_process"; import { randomUUID } from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { quoteCmdScriptArg } from "../daemon/cmd-argv.js"; import { resolveGatewayWindowsTaskName } from "../daemon/constants.js"; import type { RestartAttempt } from "./restart.js"; import { resolvePreferredOpenClawTmpDir } from "./tmp-openclaw-dir.js"; const TASK_RESTART_RETRY_LIMIT = 12; const TASK_RESTART_RETRY_DELAY_SEC = 1; function resolveWindowsTaskName(env: NodeJS.ProcessEnv): string { const override = env.OPENCLAW_WINDOWS_TASK_NAME?.trim(); if (override) { return override; } return resolveGatewayWindowsTaskName(env.OPENCLAW_PROFILE); } function buildScheduledTaskRestartScript(taskName: string): string { const quotedTaskName = quoteCmdScriptArg(taskName); return [ "@echo off", "setlocal", "set /a attempts=0", ":retry", `timeout /t ${TASK_RESTART_RETRY_DELAY_SEC} /nobreak >nul`, "set /a attempts+=1", `schtasks /Run /TN ${quotedTaskName} >nul 2>&1`, "if not errorlevel 1 goto cleanup", `if %attempts% GEQ ${TASK_RESTART_RETRY_LIMIT} goto cleanup`, "goto retry", ":cleanup", 'del "%~f0" >nul 2>&1', ].join("\r\n"); } export function relaunchGatewayScheduledTask(env: NodeJS.ProcessEnv = process.env): RestartAttempt { const taskName = resolveWindowsTaskName(env); const scriptPath = path.join( resolvePreferredOpenClawTmpDir(), `openclaw-schtasks-restart-${randomUUID()}.cmd`, ); const quotedScriptPath = quoteCmdScriptArg(scriptPath); try { fs.writeFileSync(scriptPath, `${buildScheduledTaskRestartScript(taskName)}\r\n`, "utf8"); const child = spawn("cmd.exe", ["/d", "/s", "/c", quotedScriptPath], { detached: true, stdio: "ignore", windowsHide: true, }); child.unref(); return { ok: true, method: "schtasks", tried: [`schtasks /Run /TN "${taskName}"`, `cmd.exe /d /s /c ${quotedScriptPath}`], }; } catch (err) { try { fs.unlinkSync(scriptPath); } catch { // Best-effort cleanup; keep the original restart failure. } return { ok: false, method: "schtasks", detail: err instanceof Error ? err.message : String(err), tried: [`schtasks /Run /TN "${taskName}"`], }; } }