diff --git a/scripts/gen-ecosystem.ts b/scripts/gen-ecosystem.ts index 03beb2cd..dee2eeab 100644 --- a/scripts/gen-ecosystem.ts +++ b/scripts/gen-ecosystem.ts @@ -60,9 +60,34 @@ function coreProcesses(): Pm2App[] { const [name, script, args] = line.split('|'); if (!name || !script) return fail(`unreadable CORE_PROCESSES entry: "${line}"`); return { name, script, args, cwd: PLATFORM_DIR, watch: false }; - }); + }) + .filter((app) => !DISABLED.has(app.name)); } +/* + Core processes this host does not want, from `OFFICER_DISABLED_SIDECARS` in `.env`. + + A core process is core because the platform's own features depend on it — which is why the list is a + fixed array and not a setting. But "the platform depends on it" is not "every host runs it": fofos is a + 1.9 GB machine serving a website, and `officer-opencode` sat there costing 456 MB of that for a harness + nobody on it uses. It froze hard twice, with no OOM line in the journal — which is what memory + exhaustion looks like when there is not enough headroom left to kill something cleanly. + + Deliberately NOT a database setting. This file is regenerated during a plugin install, so a host that + has dropped a process needs it to stay dropped across one. `.env` is read at generation time and is + per-host, which is exactly the scope of the decision. + + Naming something the platform genuinely needs will break that host — `officer` itself, or + `officer-pty` if anyone uses a terminal. Not validated, because which processes are safe to drop + depends on what the host is for, and this script cannot know that. +*/ +const DISABLED = new Set( + (process.env.OFFICER_DISABLED_SIDECARS ?? '') + .split(',') + .map((entry) => entry.trim()) + .filter(Boolean), +); + /** Installed AND enabled AND actually ships a sidecar — all three, or it gets no entry. */ async function pluginEntries(): Promise { const [installs, { plugins }] = await Promise.all([listPluginInstalls(), discoverPlugins()]);