openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
224 lines (223 loc) • 7.3 kB
JavaScript
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { n as ok, t as err } from "./result-BQGgYouL.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { s as sanitizeHostExecEnv } from "./host-env-security-BAvlDaJf.js";
import { o as renderSystemLaunchDaemonOwnershipShellProbe, p as resolveLaunchAgentLabel } from "./launchd-system-BlHvFe-Q.js";
import { r as renderPosixRestartLogSetup } from "./restart-logs-uOTpYNS7.js";
import path from "node:path";
import { spawn } from "node:child_process";
import os from "node:os";
//#region src/daemon/launchd-restart-handoff.ts
/** Detached macOS launchd restart handoff for restarting from inside the service. */
const RELOAD_BOOTOUT_WAIT_DELAY_SECONDS = 1;
const RELOAD_BOOTOUT_WAIT_COUNT = 35;
const RELOAD_BOOTSTRAP_RETRY_COUNT = 15;
function resolveGuiDomain() {
if (typeof process.getuid !== "function") return "gui/501";
return `gui/${process.getuid()}`;
}
function collectStringEnvOverrides(env) {
const overrides = Object.fromEntries(Object.entries(env ?? {}).filter((entry) => typeof entry[1] === "string"));
return Object.keys(overrides).length > 0 ? overrides : void 0;
}
function collectRestartLogEnv(env) {
const source = {
...process.env,
...env
};
return {
HOME: source.HOME,
USERPROFILE: source.USERPROFILE,
OPENCLAW_STATE_DIR: source.OPENCLAW_STATE_DIR,
OPENCLAW_PROFILE: source.OPENCLAW_PROFILE
};
}
function resolveLaunchdRestartTarget(env = process.env) {
const domain = resolveGuiDomain();
const label = resolveLaunchAgentLabel(env);
const home = normalizeOptionalString(env.HOME) || os.homedir();
return {
domain,
label,
plistPath: path.join(home, "Library", "LaunchAgents", `${label}.plist`),
serviceTarget: `${domain}/${label}`
};
}
function buildLaunchdRestartScript(mode, restartLogEnv, label) {
const waitForCallerPid = `wait_pid="$4"
${renderPosixRestartLogSetup(restartLogEnv)}
printf '[%s] openclaw restart attempt source=handoff mode=${mode} target=%s pid=%s interactive=0\\n' "$(date -u +%FT%TZ)" "$service_target" "$wait_pid" >&2
if [ -n "$wait_pid" ] && [ "$wait_pid" -gt 1 ] 2>/dev/null; then
while kill -0 "$wait_pid" >/dev/null 2>&1; do
sleep 0.1
done
fi
`;
const systemOwnershipGuard = `${renderSystemLaunchDaemonOwnershipShellProbe(label)}
if [ -n "$openclaw_system_launchd_conflict" ]; then
printf '[%s] openclaw restart blocked source=handoff mode=${mode} reason=%s interactive=0\n' "$(date -u +%FT%TZ)" "$openclaw_system_launchd_detail" >&2
exit 78
fi
`;
if (mode === "park") return `service_target="$1"
domain="$2"
plist_path="$3"
${waitForCallerPid}
status=0
if launchctl bootout "$service_target"; then
status=0
else
status=$?
fi
if [ "$status" -eq 0 ]; then
printf '[%s] openclaw service park done source=handoff interactive=0\\n' "$(date -u +%FT%TZ)" >&2
else
printf '[%s] openclaw service park failed source=handoff status=%s interactive=0\\n' "$(date -u +%FT%TZ)" "$status" >&2
fi
exit "$status"
`;
if (mode === "kickstart") return `service_target="$1"
domain="$2"
plist_path="$3"
${waitForCallerPid}
${systemOwnershipGuard}
status=0
launchctl enable "$service_target"
if launchctl kickstart -k "$service_target"; then
status=0
else
status=$?
if launchctl bootstrap "$domain" "$plist_path"; then
status=0
else
launchctl kickstart -k "$service_target"
status=$?
fi
fi
if [ "$status" -eq 0 ]; then
printf '[%s] openclaw restart done source=handoff mode=${mode} interactive=0\\n' "$(date -u +%FT%TZ)" >&2
else
printf '[%s] openclaw restart failed source=handoff mode=${mode} status=%s interactive=0\\n' "$(date -u +%FT%TZ)" "$status" >&2
fi
exit "$status"
`;
if (mode === "reload") return `service_target="$1"
domain="$2"
plist_path="$3"
${waitForCallerPid}
${systemOwnershipGuard}
status=0
launchctl enable "$service_target"
launchctl bootout "$service_target" >/dev/null 2>&1 || true
${`bootout_wait_count="${RELOAD_BOOTOUT_WAIT_COUNT}"
while [ "$bootout_wait_count" -gt 0 ]; do
if ! launchctl print "$service_target" >/dev/null 2>&1; then
break
fi
bootout_wait_count=$((bootout_wait_count - 1))
sleep ${RELOAD_BOOTOUT_WAIT_DELAY_SECONDS}
done
`}
${`bootstrap_retry_count="${RELOAD_BOOTSTRAP_RETRY_COUNT}"
while :; do
if launchctl bootstrap "$domain" "$plist_path"; then
status=0
break
else
# Capture inside the else: after a completed if with a false condition,
# $? is 0, which would let exhausted retries report a successful restart.
status=$?
fi
if launchctl print "$service_target" >/dev/null 2>&1; then
if launchctl kickstart -k "$service_target"; then
status=0
break
else
# The pending bootout can finish between print and kickstart. Keep
# retrying bootstrap if that check-then-act race deregisters the label.
status=$?
fi
fi
bootstrap_retry_count=$((bootstrap_retry_count - 1))
if [ "$bootstrap_retry_count" -le 0 ]; then
break
fi
sleep ${RELOAD_BOOTOUT_WAIT_DELAY_SECONDS}
done
`}
if [ "$status" -eq 0 ]; then
printf '[%s] openclaw restart done source=handoff mode=${mode} interactive=0\\n' "$(date -u +%FT%TZ)" >&2
else
printf '[%s] openclaw restart failed source=handoff mode=${mode} status=%s interactive=0\\n' "$(date -u +%FT%TZ)" "$status" >&2
fi
exit "$status"
`;
return `service_target="$1"
domain="$2"
plist_path="$3"
${waitForCallerPid}
${systemOwnershipGuard}
status=0
launchctl enable "$service_target"
if launchctl kickstart "$service_target"; then
status=0
else
status=$?
if launchctl bootstrap "$domain" "$plist_path"; then
status=0
else
launchctl kickstart "$service_target"
status=$?
fi
fi
if [ "$status" -eq 0 ]; then
printf '[%s] openclaw restart done source=handoff mode=${mode} interactive=0\\n' "$(date -u +%FT%TZ)" >&2
else
printf '[%s] openclaw restart failed source=handoff mode=${mode} status=%s interactive=0\\n' "$(date -u +%FT%TZ)" "$status" >&2
fi
exit "$status"
`;
}
function scheduleDetachedLaunchdHandoff(params) {
const target = resolveLaunchdRestartTarget(params.env);
const waitForPid = typeof params.waitForPid === "number" && Number.isFinite(params.waitForPid) ? Math.floor(params.waitForPid) : 0;
const restartLogEnv = collectRestartLogEnv(params.env);
const restartEnv = sanitizeHostExecEnv({
baseEnv: process.env,
overrides: collectStringEnvOverrides(params.env)
});
try {
const child = spawn("/bin/sh", [
"-c",
buildLaunchdRestartScript(params.mode, restartLogEnv, target.label),
"openclaw-launchd-restart-handoff",
target.serviceTarget,
target.domain,
target.plistPath,
String(waitForPid)
], {
detached: true,
stdio: "ignore",
env: restartEnv
});
const spawned = new Promise((resolve) => {
child.once("spawn", () => resolve(true));
child.once("error", () => resolve(false));
});
child.unref();
return ok(spawned);
} catch (error) {
return err(formatErrorMessage(error));
}
}
function scheduleDetachedLaunchdRestartHandoff(params) {
return scheduleDetachedLaunchdHandoff(params);
}
function scheduleDetachedLaunchdMaintenancePark(params) {
return scheduleDetachedLaunchdHandoff({
...params,
mode: "park"
});
}
//#endregion
export { scheduleDetachedLaunchdRestartHandoff as n, scheduleDetachedLaunchdMaintenancePark as t };