Every PORT fallback in the tree now says 9000. There were three answers to one question, each defensible where it was written and none of them visible from the others: server.tsx and 19 sidecars 5000 a default from before there was an installer user-instance.ts 9010 what scripts/setup-old/setup.sh really wrote .env.example 9000 what we told people to write 5000 goes first because macOS binds it — AirPlay Receiver has owned it since Monterey, so a dev server there fails to bind or gets shadowed by something that answers. All 22 sites moved together, which is the point. Changing the app alone would have turned a consistent-but-wrong default into a split one: the app on 9000 while nineteen sidecars still dialled 5000. 9010 was the interesting one. It was the only value that ever matched a real machine, because it is what the old installer wrote — and it was in the single file whose disagreement would have broken chat alone, with nothing else looking wrong. Its own comment records the same bug being fixed once already, within the file, by a change that left it disagreeing with everything outside it. Note what these defaults actually are: the sidecars bind nothing. user-instance.ts has no listener at all — it builds ws:// and http:// URLs that both address the app's single listener. So every one of these numbers is a guess at where the app is, for a value that .env always supplies. Worth removing rather than aligning, which is a separate change. Not typechecked (empty node_modules, frozen installs). Every edited file parses under `bun build --no-bundle`; the pm2 profile loads. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
86 lines
4.2 KiB
JavaScript
86 lines
4.2 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
|
|
*/
|
|
// The directory holding the platform's package.json, found by walking up from this file. Independent of
|
|
// where in the tree this config is kept, and of where pm2 was invoked from.
|
|
function repoRoot() {
|
|
const { existsSync, readFileSync } = require('node:fs');
|
|
const { dirname, join } = require('node:path');
|
|
let dir = __dirname;
|
|
for (;;) {
|
|
const manifest = join(dir, 'package.json');
|
|
if (existsSync(manifest)) {
|
|
try {
|
|
if (JSON.parse(readFileSync(manifest, 'utf8')).name === 'officer') return dir;
|
|
} catch {
|
|
// Unparseable is not ours; keep walking.
|
|
}
|
|
}
|
|
const up = dirname(dir);
|
|
if (up === dir) throw new Error("ecosystem.profile.cjs: could not find the platform's package.json above " + __dirname);
|
|
dir = up;
|
|
}
|
|
}
|
|
|
|
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 the default PORT with no POSTGRES_URL.
|
|
//
|
|
// It also decides where the install is. src/servers/data-path.ts derives OFFICER_ROOT as the PARENT of
|
|
// the working directory, and data/, capabilities/ and dockers/ hang off that — so a wrong cwd does not
|
|
// fail, it relocates the whole install. `assertInstallLayout` is the boot check that catches it.
|
|
//
|
|
// This was `__dirname`, with a comment asserting "__dirname is the repo root — this file sits beside
|
|
// ecosystem.config.cjs". That stopped being true the moment these files were moved into
|
|
// ecosystem-files/, and nothing said so. Found by walking up to the package.json instead, which is
|
|
// true wherever this file ends up living.
|
|
return { apps: include.map((name) => ({ ...byName.get(name), cwd: repoRoot() })) };
|
|
}
|
|
|
|
module.exports = { defineProfile };
|