Renamed for parity now that Linux has a light profile too: scripts/setup_mac.sh -> scripts/setup_mac_light.sh ecosystem.mac.config.cjs -> ecosystem.mac.light.config.cjs The macOS process list was still a hand-copied subset, which is the shape that broke it: written 2026-07-28, within days it was running the Anthropic proxy under the name officer-claude with nothing spawning `claude`, and pointing at a pty entry point that had moved. Both silent. It now declares names and reasons and reads script/args from ecosystem.config.cjs, so a launch change on the host reaches it for free. The include/exclude checks moved into ecosystem.profile.cjs rather than being copied into the second profile — duplicating the guard rails would have repeated the mistake they exist to catch. Both profiles were re-tested against a mutated host ecosystem: renaming an included app and adding an unclassified sidecar each throw in both, and an unmodified host loads five apps in both. Kept as two files rather than collapsed into one, even though they currently produce identical output. The exclusions do not mean the same thing: on macOS officer-vnc CANNOT run, there being no Xorg; on a Linux light install it could run fine and you have chosen not to. Merging them would lose that, and they diverge the moment one profile gains something the other cannot have. setup_mac_light.sh's verification loop now reads app names with node instead of grepping for `name:` — the derived profile has no literal keys, so the grep would have silently listed no services at all, which reads the same as a healthy install with nothing configured. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
57 lines
2.8 KiB
JavaScript
57 lines
2.8 KiB
JavaScript
// Shared machinery for the pm2 install profiles (ecosystem.light.config.cjs,
|
|
// ecosystem.mac.light.config.cjs).
|
|
//
|
|
// A profile is a SUBSET of ecosystem.config.cjs, declared as names plus reasons. It never restates how
|
|
// a process is launched — `script` and `args` are read from the host file at load — because a
|
|
// hand-copied process list is exactly what failed here: the macOS list was written on 2026-07-28 and
|
|
// within days was starting a sidecar that had been split in two and pointing at a pty entry point that
|
|
// had moved. Neither failure said anything; the processes simply did not come up.
|
|
//
|
|
// So the rule is: ecosystem.config.cjs is the only place a launch command is written down, and a
|
|
// profile only decides which of them to run.
|
|
//
|
|
// Two consistency checks, both of which turn a silent breakage into a loud one at load:
|
|
// 1. a name the profile INCLUDES that the host no longer defines — the app was renamed or removed
|
|
// 2. an app the host defines that the profile neither includes nor excludes — a new sidecar, which
|
|
// must be classified deliberately rather than defaulting to absent because nobody noticed
|
|
//
|
|
// The second is the one that matters over time. Without it, every sidecar added to the host silently
|
|
// stays out of every profile, and the profiles quietly stop meaning what their comments claim.
|
|
|
|
/**
|
|
* @param {object} spec
|
|
* @param {string} spec.file this profile's filename, for error messages
|
|
* @param {string[]} spec.include app names to run, in start order
|
|
* @param {Record<string,string>} spec.excluded app name → why it is not in this profile
|
|
*/
|
|
function defineProfile({ file, include, excluded }) {
|
|
const full = require('./ecosystem.config.cjs');
|
|
const byName = new Map(full.apps.map((app) => [app.name, app]));
|
|
|
|
const missing = include.filter((name) => !byName.has(name));
|
|
if (missing.length) {
|
|
throw new Error(
|
|
`${file}: ${missing.join(', ')} not found in ecosystem.config.cjs — the app was renamed or ` +
|
|
`removed. Update this profile's include list.`,
|
|
);
|
|
}
|
|
|
|
const unclassified = full.apps
|
|
.map((app) => app.name)
|
|
.filter((name) => !include.includes(name) && !(name in excluded));
|
|
if (unclassified.length) {
|
|
throw new Error(
|
|
`${file}: ${unclassified.join(', ')} is in ecosystem.config.cjs but neither included nor ` +
|
|
`excluded here. Add it to the include list, or to the excluded map with a reason.`,
|
|
);
|
|
}
|
|
|
|
// `cwd` is pinned because Bun auto-loads .env from the working directory (and the pty sidecar does
|
|
// `import 'dotenv/config'`). Without it, starting pm2 from anywhere but the repo root silently falls
|
|
// back to PORT=5000 with no POSTGRES_URL. __dirname is the repo root — this file sits beside
|
|
// ecosystem.config.cjs.
|
|
return { apps: include.map((name) => ({ ...byName.get(name), cwd: __dirname })) };
|
|
}
|
|
|
|
module.exports = { defineProfile };
|