From 37ff7c3103ae91e83094b73498a3eb9c2124ab09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Tue, 4 Aug 2026 12:31:06 +0000 Subject: [PATCH] mac: bring the laptop ecosystem back in step with the architecture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ecosystem.mac.config.cjs was written on 2026-07-28 and broken within days by two changes it never caught up with, leaving two of its four processes dead: - it ran `officer-claude` against src/servers/sidecar/claude/index.ts. That file is the Anthropic credential proxy; the process that actually spawns `claude` is user-instance.ts, which was never started. Chat had a credential holder and nothing driving it. Split into officer-anthropic-proxy + officer-agent to match the host, and retired the `officer-claude` name that caused it. - officer-pty pointed at src/servers/api/terminal/pty-sidecar.mjs, which no longer exists; the sidecar moved to src/servers/sidecar/pty/index.mjs. The terminal could not start at all. No startup ordering is needed between the proxy and the agent: the agent reads the proxy secret from disk and, when it is not there yet, warns and re-reads before the next spawn. Five sidecars have been added since the file was written (memos, caldav, photos, notify, wallet). All are excluded, and every exclusion is now listed with its reason so the next reader can tell "not applicable on macOS" from "forgotten" — which is the failure this commit is fixing. setup_mac.sh needed no path corrections; its verification loop derives the process names from the ecosystem file, so it picks the new list up on its own. Two gaps closed there: - it called `pm2 save` without `pm2 startup`, writing a process list that nothing reads — a reboot left the machine with nothing running. Added the launchd equivalent behind SETUP_BOOT, optional because a laptop is not a server, and non-fatal because pm2's launchd integration can want a prompt. - the closing "not installed on macOS" note listed four omissions when there are now fourteen. Both files parse and every ecosystem entry point resolves to a file that exists. The launchd path is the one thing not verified here — it cannot be exercised from the Linux host. Co-Authored-By: Claude Opus 5 (1M context) --- ecosystem.mac.config.cjs | 58 ++++++++++++++++++++++++++++++++-------- scripts/setup_mac.sh | 21 ++++++++++++++- 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/ecosystem.mac.config.cjs b/ecosystem.mac.config.cjs index a6bd235f..4927d7b3 100644 --- a/ecosystem.mac.config.cjs +++ b/ecosystem.mac.config.cjs @@ -1,18 +1,40 @@ // macOS process list — the laptop subset of ecosystem.config.cjs. // -// Only what a Mac install can actually run and what the laptop workflow needs (file browser + -// Claude/opencode chat + terminal). Deliberately omitted: -// officer-vnc — mirrors an Xorg :0 with x11vnc; there is no Xorg on macOS. -// officer-email — needs the mbsync/IMAP stack that setup_mac.sh does not install. -// officer-music — the indexer is ffprobe-driven and works, but it is not part of the laptop -// workflow and a full ~/Music index is an expensive thing to start by default. +// Only what a Mac install can actually run and what the laptop workflow needs: the file browser, the +// terminal, and Claude/opencode chat. Everything else on the Linux host is either tied to Xorg, tied to +// a container the laptop does not run, or holds material that has no business on a laptop. +// +// KEEP THIS IN STEP WITH ecosystem.config.cjs. It was written on 2026-07-28 and was broken by two +// architecture changes within days, which is the failure mode to watch for: +// - `officer-claude` was split into `officer-anthropic-proxy` (holds the credential) and +// `officer-agent` (spawns `claude`). This file ran only the first under the old name, so chat had a +// credential holder and nothing driving it. +// - the pty sidecar moved to src/servers/sidecar/pty/index.mjs. This file still pointed at +// src/servers/api/terminal/pty-sidecar.mjs, which no longer exists, so the terminal never started. +// +// Deliberately omitted, and why — so the next person can tell "not applicable" from "forgotten": +// officer-vnc mirrors an Xorg display with x11vnc; macOS has no Xorg to mirror. +// officer-email needs the mbsync/IMAP stack that setup_mac.sh does not install. +// officer-music the ffprobe indexer works, but a full ~/Music index is an expensive thing to +// start by default and it is not part of the laptop workflow. +// officer-vault a reverse proxy to a self-hosted Vaultwarden container. +// officer-slskd supervises the slskd daemon. +// officer-headscale fronts a headscale server. +// officer-transmission fronts a transmission daemon. +// officer-invoiceshelf fronts an InvoiceShelf container. +// officer-memos needs an owner-configured Memos instance URL and token. +// officer-photos needs an owner-configured Immich instance URL and API key. +// officer-caldav supervises Radicale, which setup_mac.sh does not install. +// officer-notify the outbound notifier; its producers are the queue and the email/agent +// sidecars, so on a laptop there is nothing to notify about. +// officer-wallet holds seed and node credentials. Not on a laptop. // // Start with: pm2 startOrRestart ecosystem.mac.config.cjs // The Linux host keeps using ecosystem.config.cjs; neither file references the other. // -// `cwd` is pinned on every app because Bun auto-loads .env from the working directory (and -// pty-sidecar.mjs does `import 'dotenv/config'`). Without it, starting pm2 from anywhere other than -// the repo root silently falls back to PORT=5000 with no POSTGRES_URL. +// `cwd` is pinned on every app because Bun auto-loads .env from the working directory (and the pty +// sidecar does `import 'dotenv/config'`). Without it, starting pm2 from anywhere other than the repo +// root silently falls back to PORT=5000 with no POSTGRES_URL. const cwd = __dirname; @@ -25,13 +47,25 @@ module.exports = { cwd, watch: false, }, + // Holds the proxy secret and forwards to api.anthropic.com. Does NOT run agents — the old + // `officer-claude` name covered both jobs and is exactly how this file ended up with half of chat. { - name: 'officer-claude', + name: 'officer-anthropic-proxy', script: 'bun', args: 'run src/servers/sidecar/claude/index.ts', cwd, watch: false, }, + // The process that actually spawns `claude`. Resolves the owner from the database and the proxy + // secret from disk, so it needs no startup ordering against the proxy above: if the secret is not + // written yet it warns and re-reads before the next spawn. + { + name: 'officer-agent', + script: 'bun', + args: 'run src/servers/sidecar/claude/user-instance.ts', + cwd, + watch: false, + }, { name: 'officer-opencode', script: 'bun', @@ -39,10 +73,12 @@ module.exports = { cwd, watch: false, }, + // node, not bun — same as the Linux host. The pty sidecar is a .mjs that binds node-pty's native + // addon, which is built against node's ABI. { name: 'officer-pty', script: 'node', - args: 'src/servers/api/terminal/pty-sidecar.mjs', + args: 'src/servers/sidecar/pty/index.mjs', cwd, watch: false, }, diff --git a/scripts/setup_mac.sh b/scripts/setup_mac.sh index b4c76900..ac18e07c 100755 --- a/scripts/setup_mac.sh +++ b/scripts/setup_mac.sh @@ -442,6 +442,23 @@ elif confirm SETUP_SERVICES "Start Officer and its sidecars with pm2?" y; then note_failure "pm2 could not start the services — check 'pm2 logs'" fi if pm2 save >/dev/null 2>&1; then ok "process list saved"; else warn "pm2 save failed"; fi + + # `pm2 save` on its own writes a process list that nothing ever reads. The Linux setup pairs it with + # `pm2 startup`, which installs the boot unit that resurrects that list; without the pair, a reboot + # silently leaves the machine with nothing running. On macOS the equivalent is a launchd agent rather + # than a systemd unit. Kept optional because a laptop is not a server — you may not want the whole + # stack coming back at every login — and non-fatal, because pm2's launchd integration can want an + # elevated prompt that a scripted run should not force. + if confirm SETUP_BOOT "Start Officer automatically at login (pm2 + launchd)?" y; then + if pm2 startup launchd -u "$(whoami)" --hp "$HOME" >/dev/null 2>&1; then + pm2 save >/dev/null 2>&1 + ok "services will start at login" + else + warn "Could not install the launchd agent — run: pm2 startup (and follow its instructions)" + fi + else + skip "login startup (declined — 'pm2 startup' enables it later)" + fi else skip "services (declined)" fi @@ -504,7 +521,9 @@ echo "Notes:" echo " • Open ${ENV_PUBLIC_URL:-http://localhost:9010} — the first-run screen creates the owner account" echo " • Logs: pm2 logs Restart: pm2 restart ecosystem.mac.config.cjs" echo " • Postgres must be running before the services start, or db:push and boot will fail" -echo " • Not installed on macOS: VNC desktop, email sync, music indexer, cliamp audio" +echo " • Not run on macOS: VNC desktop, email sync, music indexer, cliamp audio, and the sidecars" +echo " that front a container or an external service — vault, slskd, headscale, transmission," +echo " invoiceshelf, memos, photos, caldav, notify, wallet. See ecosystem.mac.config.cjs." echo " • Pin a specific Claude CLI with CLAUDE_BIN=/path/to/claude in .env if you need to" echo " • Re-run any single step with e.g. SETUP_OPENCODE=1 bash scripts/setup_mac.sh" echo ""