cec48832be
- redeploy-bridged.sh now refuses (not warns) a supervised restart when its computed log path disagrees with the loaded plist's StandardOutPath — otherwise every post-restart check reads the wrong file and can report a clean restart while the daemon crash-loops. The check is a pure, testable function; the script gained a source-for-test guard so it can be exercised without installing the agent or touching launchd. - a failed 'launchctl load' after a successful 'unload' now retries once and, on ultimate failure, tells the operator the agent is stopped AND disabled plus the exact recovery command, instead of leaving that silently worse than the pre-redeploy state. - the plist documents honestly that the crash loop launchd retries is unbounded (ThrottleInterval only paces it), and what actually stops it. - fixed the requiredSecretEnvVars javadoc: the auth.tokenEnv startup throw is ~370 lines below its call site, not a few lines above it, and only fires in auth.mode: token.
128 lines
6.8 KiB
Plaintext
128 lines
6.8 KiB
Plaintext
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<!--
|
|
CB-504 / CB-594 — launchd agent for bridged (macOS).
|
|
|
|
This is the real supervision target today: the dogfooded daemon runs on macOS, where there is
|
|
no systemd. A systemd unit ships alongside (deploy/bridged.service) for the Linux gateways
|
|
CB-308 introduces.
|
|
|
|
Install:
|
|
cp deploy/dev.ltms.bridged.plist ~/Library/LaunchAgents/
|
|
launchctl load -w ~/Library/LaunchAgents/dev.ltms.bridged.plist
|
|
launchctl list | grep bridged
|
|
|
|
The paths below are already filled in for this host (resolved 2026-08-16 from
|
|
`/usr/libexec/java_home`... except that reported the system Applet-plugin JVM, not the jenv-
|
|
managed JDK 25 actually used to build/run bridged, so JAVA_HOME here is the real one:
|
|
`JENV_VERSION=25.0.3 java -XshowSettings:properties -version 2>&1 | grep java.home`; `which mvn`;
|
|
`echo $HOME`). If this file is copied to a different host, re-resolve all three paths and check
|
|
no placeholder path is left behind; scripts/redeploy-bridged.sh's check mode does not (and
|
|
cannot) check this file for you.
|
|
|
|
CB-594 — launchd cannot run a login shell (see the PATH comment on EnvironmentVariables below,
|
|
and scripts/bridged-launchd-wrapper.sh for the fix): ProgramArguments below execs THAT wrapper,
|
|
not java directly, so WORKER_GITEA_TOKEN and AI_GATEWAY_TOKEN still get sourced from
|
|
${SHARED_ENV}/tools/secrets.sh even though launchd itself never sources anything.
|
|
|
|
Note on ordering: launchd has no "start after herdr" primitive for user agents, and neither
|
|
does systemd in a way that survives a socket appearing late. bridged retries the herdr socket
|
|
on startup instead, so an agent that comes up before herdr converges rather than dying — that
|
|
retry is the actual fix; KeepAlive below is the backstop.
|
|
|
|
CB-594 — KeepAlive vs. scripts/redeploy-bridged.sh: a bare SIGTERM makes this JVM exit 143 even
|
|
with its shutdown hook running to completion (measured, see the CB-594 report), which
|
|
SuccessfulExit:false below reads as a crash and races to restart the OLD jar. The redeploy
|
|
script now detects a loaded agent and uses `launchctl unload`/`load` instead of a raw kill, so
|
|
only one supervisor ever touches the process at a time — read that script's own output on a
|
|
redeploy for the confirmation.
|
|
-->
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>Label</key>
|
|
<string>dev.ltms.bridged</string>
|
|
|
|
<key>ProgramArguments</key>
|
|
<array>
|
|
<string>/Users/dai.ha/LTMS/claude-bridge/scripts/bridged-launchd-wrapper.sh</string>
|
|
<string>/Users/dai.ha/Softwares/jdks/jdk-25.0.3.jdk/Contents/Home/bin/java</string>
|
|
<string>-jar</string>
|
|
<string>/Users/dai.ha/LTMS/claude-bridge/bridged/target/bridged.jar</string>
|
|
<string>bridged.yaml</string>
|
|
</array>
|
|
|
|
<!-- Config path in ProgramArguments is relative, so the working directory must be the module. -->
|
|
<key>WorkingDirectory</key>
|
|
<string>/Users/dai.ha/LTMS/claude-bridge/bridged</string>
|
|
|
|
<key>EnvironmentVariables</key>
|
|
<dict>
|
|
<key>JAVA_HOME</key>
|
|
<string>/Users/dai.ha/Softwares/jdks/jdk-25.0.3.jdk/Contents/Home</string>
|
|
<key>HERDR_SOCKET_PATH</key>
|
|
<string>/Users/dai.ha/.config/herdr/herdr.sock</string>
|
|
<!--
|
|
PATH matters more than it looks (CB-511): bridged propagates its own PATH to every worker
|
|
it spawns, so this line decides whether the fleet can run a build at all. launchd does NOT
|
|
source .zprofile/.zshrc, so without this the daemon (and therefore every worker) gets a
|
|
bare /usr/bin:/bin and no JDK or Maven. Keep the toolchain entries first.
|
|
-->
|
|
<key>PATH</key>
|
|
<string>/Users/dai.ha/Softwares/jdks/jdk-25.0.3.jdk/Contents/Home/bin:/Users/dai.ha/Softwares/apache-maven/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
|
<!--
|
|
Worker/API tokens are NOT set here: this file is committed. CB-594 —
|
|
scripts/bridged-launchd-wrapper.sh (named in ProgramArguments above) is what supplies
|
|
them, by execing a login shell that sources ${SHARED_ENV}/tools/secrets.sh before the
|
|
daemon itself starts. bridged also reads the API token from the env var named by
|
|
auth.tokenEnv (default BRIDGED_API_TOKEN) and only in auth.mode: token — the wrapper
|
|
covers that one too, since it is the same login shell.
|
|
-->
|
|
</dict>
|
|
|
|
<key>RunAtLoad</key>
|
|
<true/>
|
|
|
|
<!--
|
|
CB-600 — read this before assuming ThrottleInterval bounds anything. It paces restarts to at
|
|
most one per 10s; it does NOT cap how many times launchd retries. If bridged fails fast on
|
|
every start — a bad bridged.yaml, for example auth.mode: token with the token env var unset,
|
|
which throws in main() before the daemon ever binds a port — launchd restarts it forever,
|
|
once every 10s, until a human intervenes. LaunchAgents have no "give up after N attempts"
|
|
primitive, so this is not something a config change here can fix.
|
|
|
|
That loop stops only two ways: (1) `launchctl unload -w ~/Library/LaunchAgents/dev.ltms.bridged.plist`,
|
|
or (2) the underlying cause gets fixed, so the process starts successfully and stays up (no
|
|
more exits to restart). scripts/redeploy-bridged.sh does not add a third way — it does not
|
|
make bridged self-disable on a config error, on purpose: a fail-fast exit path that
|
|
sometimes decides "this is unrecoverable, stop trying" is one more thing that can misfire,
|
|
and a wrongly self-disabled daemon needs the exact same manual `launchctl load -w` recovery
|
|
this comment already names — so it buys nothing an operator watching for the crash loop
|
|
doesn't already have, at the cost of a new way to be silently down. Watch for it with
|
|
`launchctl list dev.ltms.bridged` (a high restart count) or by tailing bridged.out for the
|
|
same startup error repeating every ~10s.
|
|
-->
|
|
<key>KeepAlive</key>
|
|
<dict>
|
|
<key>SuccessfulExit</key>
|
|
<false/>
|
|
</dict>
|
|
<key>ThrottleInterval</key>
|
|
<integer>10</integer>
|
|
|
|
<!--
|
|
CB-594 — same file scripts/redeploy-bridged.sh already tails ($BRIDGED/bridged.out), and both
|
|
streams point at it, not two separate log files: the script's fresh-line / ERROR-count checks
|
|
after a restart read this one path regardless of whether launchd or the script started the
|
|
process, and a stdout/stderr split would make half of what happens during a launchd-driven
|
|
restart invisible to it.
|
|
-->
|
|
<key>StandardOutPath</key>
|
|
<string>/Users/dai.ha/LTMS/claude-bridge/bridged/bridged.out</string>
|
|
<key>StandardErrorPath</key>
|
|
<string>/Users/dai.ha/LTMS/claude-bridge/bridged/bridged.out</string>
|
|
|
|
<key>ProcessType</key>
|
|
<string>Background</string>
|
|
</dict>
|
|
</plist>
|