diff --git a/src/daemon/launchd-plist.ts b/src/daemon/launchd-plist.ts
index b292ff45974..37448cdcebf 100644
--- a/src/daemon/launchd-plist.ts
+++ b/src/daemon/launchd-plist.ts
@@ -1,5 +1,10 @@
import fs from "node:fs/promises";
+// launchd applies ThrottleInterval to any rapid relaunch, including
+// intentional gateway restarts. Keep it low so CLI restarts and forced
+// reinstalls do not stall for a full minute.
+export const LAUNCH_AGENT_THROTTLE_INTERVAL_SECONDS = 1;
+
const plistEscape = (value: string): string =>
value
.replaceAll("&", "&")
@@ -106,5 +111,5 @@ export function buildLaunchAgentPlist({
? `\n Comment\n ${plistEscape(comment.trim())}`
: "";
const envXml = renderEnvDict(environment);
- return `\n\n\n \n Label\n ${plistEscape(label)}\n ${commentXml}\n RunAtLoad\n \n KeepAlive\n \n ThrottleInterval\n 60\n ProgramArguments\n ${argsXml}\n \n ${workingDirXml}\n StandardOutPath\n ${plistEscape(stdoutPath)}\n StandardErrorPath\n ${plistEscape(stderrPath)}${envXml}\n \n\n`;
+ return `\n\n\n \n Label\n ${plistEscape(label)}\n ${commentXml}\n RunAtLoad\n \n KeepAlive\n \n ThrottleInterval\n ${LAUNCH_AGENT_THROTTLE_INTERVAL_SECONDS}\n ProgramArguments\n ${argsXml}\n \n ${workingDirXml}\n StandardOutPath\n ${plistEscape(stdoutPath)}\n StandardErrorPath\n ${plistEscape(stderrPath)}${envXml}\n \n\n`;
}
diff --git a/src/daemon/launchd.test.ts b/src/daemon/launchd.test.ts
index 85c7a3350e9..6cf31dc5ce5 100644
--- a/src/daemon/launchd.test.ts
+++ b/src/daemon/launchd.test.ts
@@ -1,5 +1,6 @@
import { PassThrough } from "node:stream";
import { beforeEach, describe, expect, it, vi } from "vitest";
+import { LAUNCH_AGENT_THROTTLE_INTERVAL_SECONDS } from "./launchd-plist.js";
import {
installLaunchAgent,
isLaunchAgentListed,
@@ -199,7 +200,7 @@ describe("launchd install", () => {
expect(plist).toContain("");
expect(plist).not.toContain("SuccessfulExit");
expect(plist).toContain("ThrottleInterval");
- expect(plist).toContain("60");
+ expect(plist).toContain(`${LAUNCH_AGENT_THROTTLE_INTERVAL_SECONDS}`);
});
it("restarts LaunchAgent with bootout-bootstrap-kickstart order", async () => {