diff --git a/CHANGELOG.md b/CHANGELOG.md index cb1a65591..4ee9094b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - macOS packaging: move rpath config into swift build for reliability (#69) — thanks @petter-b - macOS: prioritize main bundle for device resources to prevent crash (#73) — thanks @petter-b - Chat UI: clear composer input immediately and allow clear while editing to prevent duplicate sends (#72) — thanks @hrdwdmrbl +- Restart: use systemd on Linux (and report actual restart method) instead of always launchctl. - Docs: add manual OAuth setup for remote/headless deployments (#67) — thanks @wstock - Docs/agent tools: clarify that browser `wait` should be avoided by default and used only in exceptional cases. - Browser tools: `upload` supports auto-click refs, direct `inputRef`/`element` file inputs, and emits input/change after `setFiles` so JS-heavy sites pick up attachments. diff --git a/src/auto-reply/reply.ts b/src/auto-reply/reply.ts index 6d14eaa89..91701392d 100644 --- a/src/auto-reply/reply.ts +++ b/src/auto-reply/reply.ts @@ -886,10 +886,10 @@ export async function getReplyFromConfig( cleanupTyping(); return undefined; } - triggerClawdisRestart(); + const restartMethod = triggerClawdisRestart(); cleanupTyping(); return { - text: "⚙️ Restarting clawdis via launchctl; give me a few seconds to come back online.", + text: `⚙️ Restarting clawdis via ${restartMethod}; give me a few seconds to come back online.`, }; } diff --git a/src/infra/restart.ts b/src/infra/restart.ts index c7b3b703d..b39c98819 100644 --- a/src/infra/restart.ts +++ b/src/infra/restart.ts @@ -1,22 +1,33 @@ -import { spawn } from "node:child_process"; +import { spawnSync } from "node:child_process"; const DEFAULT_LAUNCHD_LABEL = "com.steipete.clawdis"; +const DEFAULT_SYSTEMD_UNIT = "clawdis-gateway.service"; -export function triggerClawdisRestart(): void { +export function triggerClawdisRestart(): "launchctl" | "systemd" | "supervisor" { if (process.platform !== "darwin") { - return; + if (process.platform === "linux") { + const unit = process.env.CLAWDIS_SYSTEMD_UNIT || DEFAULT_SYSTEMD_UNIT; + const userRestart = spawnSync("systemctl", ["--user", "restart", unit], { + stdio: "ignore", + }); + if (!userRestart.error && userRestart.status === 0) { + return "systemd"; + } + const systemRestart = spawnSync("systemctl", ["restart", unit], { + stdio: "ignore", + }); + if (!systemRestart.error && systemRestart.status === 0) { + return "systemd"; + } + return "systemd"; + } + return "supervisor"; } const label = process.env.CLAWDIS_LAUNCHD_LABEL || DEFAULT_LAUNCHD_LABEL; const uid = typeof process.getuid === "function" ? process.getuid() : undefined; const target = uid !== undefined ? `gui/${uid}/${label}` : label; - const child = spawn("launchctl", ["kickstart", "-k", target], { - detached: true, - stdio: "ignore", - }); - child.on("error", () => { - // Best-effort restart; ignore failures (e.g. missing launchctl, invalid label). - }); - child.unref(); + spawnSync("launchctl", ["kickstart", "-k", target], { stdio: "ignore" }); + return "launchctl"; }