The updater was previously attempting to restart the service using the
installed codebase, which could be in an inconsistent state during the
update process. This caused the service to stall when the updater
deleted its own files before the restart could complete.
Changes:
- restart-helper.ts: new module that writes a platform-specific restart
script to os.tmpdir() before the update begins (Linux systemd, macOS
launchctl, Windows schtasks).
- update-command.ts: prepares the restart script before installing, then
uses it for service restart instead of the standard runDaemonRestart.
- restart-helper.test.ts: 12 tests covering all platforms, custom
profiles, error cases, and shell injection safety.
Review feedback addressed:
- Use spawn(detached: true) + unref() so restart script survives parent
process termination (Greptile).
- Shell-escape profile values using single-quote wrapping to prevent
injection via OPENCLAW_PROFILE (Greptile).
- Reject unsafe batch characters on Windows.
- Self-cleanup: scripts delete themselves after execution (Copilot).
- Add tests for write failures and custom profiles (Copilot).
Fixes#17225