Implementation:Openclaw Openclaw RunDaemonRestart
RunDaemonRestart
This page documents the implementation of Gateway Restart in the OpenClaw gateway. It covers the runDaemonRestart function for programmatic service restart and the scripts/restart-mac.sh script for full macOS development restart cycles.
Source Locations
| Function/Script | File | Lines |
|---|---|---|
runDaemonRestart |
src/cli/daemon-cli/lifecycle.ts |
L239-319 |
restart-mac.sh |
scripts/restart-mac.sh |
L1-270 |
runDaemonRestart
Signature
export async function runDaemonRestart(opts: DaemonLifecycleOptions = {}): Promise<boolean>
Purpose
Restarts the gateway service via the OS service manager. Returns true if the restart succeeded, false if the service was not loaded. Throws or exits the process on check or restart failures.
Parameters
type DaemonLifecycleOptions = {
json?: boolean;
};
| Option | Type | Description |
|---|---|---|
json |
boolean (optional) |
When true, emit structured JSON output instead of human-readable text. Default: false.
|
Behavior
Step 1: Setup
- Resolves output mode: if
jsonistrue, creates a null writer to suppress stdout text and prepares a JSON emitter; otherwise usesprocess.stdout. - Defines a
failhelper that either emits a JSON error payload or writes to stderr, then exits with code 1.
Step 2: Service state check
const service = resolveGatewayService();
let loaded = false;
try {
loaded = await service.isLoaded({ env: process.env });
} catch (err) {
fail(`Gateway service check failed: ${String(err)}`);
return false;
}
Resolves the platform-appropriate gateway service and checks whether it is loaded. On check failure, reports the error and exits.
Step 3: Not loaded handling
If the service is not loaded:
- Generates start hints via
renderGatewayServiceStartHints(). - On Linux, additionally checks
isSystemdUserServiceAvailable()and adds systemd-specific hints (including WSL-aware guidance). - Emits a JSON payload (if JSON mode) or prints "Gateway service not loaded" with hints.
- Returns
false.
Step 4: Restart
If the service is loaded:
try {
await service.restart({ env: process.env, stdout });
let restarted = true;
try {
restarted = await service.isLoaded({ env: process.env });
} catch {
restarted = true;
}
emit({
ok: true,
result: "restarted",
service: buildDaemonServiceSnapshot(service, restarted),
});
return true;
} catch (err) {
const hints = renderGatewayServiceStartHints();
fail(`Gateway restart failed: ${String(err)}`, hints);
return false;
}
- Calls
service.restart()which delegates to the platform service manager (launchd or systemd). - After restart, verifies the service is still loaded (detecting crash-on-start).
- Emits a success JSON payload or returns
true. - On failure, reports the error with hints and exits.
Return Value
| Value | Meaning |
|---|---|
true |
Restart succeeded; the service is running. |
false |
The service was not loaded (not an error; the caller should decide how to proceed). |
The function may also exit the process (via defaultRuntime.exit(1)) on service check or restart failures.
JSON Output Schema
When json: true, the function emits payloads via emitDaemonActionJson:
// Success
{
action: "restart",
ok: true,
result: "restarted",
service: {
label: string, // e.g., "ai.openclaw.mac"
loaded: boolean,
loadedText: string, // e.g., "loaded"
notLoadedText: string // e.g., "not loaded"
}
}
// Not loaded
{
action: "restart",
ok: true,
result: "not-loaded",
message: "Gateway service not loaded.",
hints: string[],
service: { ... }
}
// Failure
{
action: "restart",
ok: false,
error: "Gateway restart failed: ...",
hints: string[]
}
Related Lifecycle Functions
The same file (src/cli/daemon-cli/lifecycle.ts) contains sibling functions that share the same pattern:
| Function | Lines | Purpose |
|---|---|---|
runDaemonUninstall |
L11-81 | Stops and uninstalls the gateway service. |
runDaemonStart |
L83-163 | Starts the gateway service (restarts if already loaded). |
runDaemonStop |
L165-232 | Stops the gateway service. |
runDaemonRestart |
L239-319 | Restarts the gateway service (documented above). |
All four functions follow the same structure: resolve service, check loaded state, perform action, verify post-state, emit result.
scripts/restart-mac.sh
Purpose
A comprehensive macOS development restart script that performs a full kill-rebuild-package-relaunch cycle. Used during development when changes span both the TypeScript gateway and the Swift macOS app.
Usage
scripts/restart-mac.sh [--wait] [--no-sign] [--sign] [--attach-only|--no-attach-only]
| Flag | Description |
|---|---|
--wait |
Wait for another running restart to complete instead of exiting. |
--no-sign |
Force ad-hoc code signing (fastest for development). |
--sign |
Force proper code signing (fails if no signing key is available). |
--attach-only |
Launch the app with --attach-only (skip launchd install). Default.
|
--no-attach-only |
Launch the app without the attach-only override. |
Behavior
1. Lock acquisition -- Uses a directory-based lock (/tmp/openclaw-restart-<hash>) to prevent concurrent restart runs. The lock holder's PID is recorded; stale locks from dead processes are automatically cleaned.
2. Kill all instances -- Iterates up to 10 times, killing all OpenClaw processes matching app bundle, debug build, local build, and release build patterns. Waits 300ms between attempts.
3. Stop LaunchAgent -- Runs launchctl bootout gui/$UID/ai.openclaw.mac.
4. Canvas A2UI bundle -- Runs pnpm canvas:a2ui:bundle to bundle the Canvas A2UI assets.
5. Clean and rebuild -- Removes the Swift build cache and runs swift build -q --product OpenClaw.
6. Code signing resolution -- Auto-detects signing keys via security find-identity -p codesigning. Falls back to ad-hoc signing if no keys are found.
7. Package -- Runs scripts/package-mac-app.sh to create the app bundle.
8. Unsigned flow (optional) -- When unsigned:
- Writes a
disable-launchagentmarker to prevent the app from managing the LaunchAgent. - Installs the gateway daemon via
node openclaw.mjs daemon install --force --runtime node. - Restarts the gateway daemon.
- Verifies the gateway port is listening via
lsof.
9. Launch -- Opens the app bundle via /usr/bin/open with a sanitized environment (env -i with only essential variables: HOME, USER, LOGNAME, TMPDIR, PATH, LANG).
10. Verification -- Waits 1.5 seconds and checks for the running process via pgrep.
Environment Variables
| Variable | Description |
|---|---|
OPENCLAW_APP_BUNDLE |
Override the app bundle path (default: auto-detected from /Applications or dist/).
|
OPENCLAW_GATEWAY_WAIT_SECONDS |
Wait time before gateway port check in unsigned mode (default: 0). |
OPENCLAW_RESTART_LOG |
Log file path (default: /tmp/openclaw-restart.log).
|
Dependencies
| Module | Purpose |
|---|---|
src/daemon/service.ts |
resolveGatewayService for platform-specific service abstraction.
|
src/daemon/systemd.ts |
isSystemdUserServiceAvailable for Linux systemd detection.
|
src/daemon/systemd-hints.ts |
renderSystemdUnavailableHints for Linux-specific guidance.
|
src/infra/wsl.ts |
isWSL for Windows Subsystem for Linux detection.
|
src/cli/daemon-cli/response.ts |
buildDaemonServiceSnapshot, createNullWriter, emitDaemonActionJson.
|
src/cli/daemon-cli/shared.ts |
renderGatewayServiceStartHints for start hint generation.
|