Files
ai-agent/worker/make_plist.py
Gabriel Vidal 7ac7fe6c17 feat(worker): generic Mac installer (cwd + account prompts) and opt-in autoRoute per worker
The worker installer no longer assumes the Orus work seat. It asks for the
default working dir — the directory it is run from, confirmed with `y`, or a
typed path (taken as is without a terminal) — and for the hub-side account,
proposed from `claude auth status` (a gmail login → personal, else work).
`--update` keeps the installed agent's cwd and account. Remote Control stays
off on every worker.

Pairing a personal Mac on `personal` would have taken every personal spawn
while it was awake (for_account), so workers now carry an `autoRoute` flag:
off at pairing (the lab stays the runner; the worker only gets what the
composer's Runs-on chip pins to it), toggled from the Settings → Workers card
or the pairing dialog. Records from before the flag keep auto-routing.

Docs genericized (worker README, CLAUDE.md Workers, notify SKILL, GOAL).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 16:41:26 +02:00

48 lines
1.9 KiB
Python

"""Write the worker's launchd agent plist (called by install-macos.sh, which
passes everything through the environment).
KeepAlive restarts the worker if it dies — including at login, when Tailscale
may not be up yet and binding its address fails for the first few seconds.
AbandonProcessGroup keeps launchd from killing in-flight `claude -p` runs when
the worker restarts: they're daemons (their own session), and a restarted
worker re-discovers them from its pidfiles, exactly like the host sidecar
under systemd's KillMode=process."""
import os
import plistlib
e = os.environ
logs = e["LOGS"]
plist = {
"Label": e["LABEL"],
"ProgramArguments": [e["PY"], os.path.join(e["SIDECAR"], "sidecar.py")],
"WorkingDirectory": e["SIDECAR"],
"RunAtLoad": True,
"KeepAlive": True,
"ThrottleInterval": 10,
"AbandonProcessGroup": True,
"ProcessType": "Interactive",
"StandardOutPath": os.path.join(logs, "worker.log"),
"StandardErrorPath": os.path.join(logs, "worker.log"),
"EnvironmentVariables": {
"HOME": os.path.expanduser("~"),
"PATH": e["RUN_PATH"],
"SIDECAR_BIND": e["BIND"],
"SIDECAR_PORT": e["PORT"],
"SIDECAR_CWD": e["CWD"],
# This Mac's own `claude` login (Keychain) is the only account here;
# the id is what the hub calls it (`personal`, or `work` on a work
# seat). Remote Control stays off on every worker: org-disabled on a
# work seat (and a work run must never list itself there anyway), and
# a worker run is already watched live by the hub.
"SIDECAR_DEFAULT_ACCOUNT": e["ACCOUNT"],
"SIDECAR_REMOTE_CONTROL": "0",
"SIDECAR_LOG_DIR": os.path.join(logs, "runs"),
"CLAUDE_BIN": e["CLAUDE_BIN"],
"WORKER_STATE_DIR": e["STATE"],
"PYTHONUNBUFFERED": "1",
},
}
with open(e["PLIST"], "wb") as f:
plistlib.dump(plist, f)