scripts: add a light install profile
OFFICER_PROFILE=light installs the same thing the macOS build does, on Linux: the file browser, the terminal, and Claude/opencode chat. It skips the archive extras, the sudoers entry and auto-suspend disabling, Go, Rust, PulseAudio, cliamp, Neovim, the shell tooling and yt-dlp, brings up Postgres alone of the five Docker services, and starts ecosystem.light.config.cjs. Unset or `full` behaves exactly as before. The profile changes which processes start, not which code ships — every API route stays mounted, so features whose sidecars are absent report themselves unavailable rather than disappearing. ecosystem.light.config.cjs DERIVES its apps from ecosystem.config.cjs rather than copying them, because the hand-copied Mac list was broken within days of being written by a sidecar split in two and a pty entry point that moved, and both failures were silent. Here a script/args change on the host propagates for free, and two consistency checks turn the silent cases loud: - a name the profile needs that the host no longer defines throws at load - an app added to the host that is in neither the include list nor the annotated exclusion list throws, so a new sidecar cannot default to "not in the profile" without someone deciding Both were tested against a mutated copy of the host ecosystem: renaming officer-agent and adding an unclassified sidecar each throw, and the unmodified file loads five apps. The verification block now reads app names with node instead of grepping for `name:` — the derived file has no literal keys to match, so a grep would have silently verified nothing — and skips the checks for tools the profile did not install, so a clean light run does not report Go and cliamp as missing. Full-profile behaviour is unchanged by construction: every guard wraps the original code in an else branch. The preamble was tested across unset, full, light and an invalid value. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
// Linux light profile — the same process set as the Mac laptop build, on a Linux host.
|
||||
//
|
||||
// For a machine that should run the platform without the self-hosted estate around it: the file
|
||||
// browser, the terminal, and Claude/opencode chat. Everything else in ecosystem.config.cjs either
|
||||
// fronts a container, supervises a daemon, needs an owner-configured external service, or holds
|
||||
// material a small install has no business holding.
|
||||
//
|
||||
// DERIVED, NOT COPIED — and that is the whole point. ecosystem.mac.config.cjs was a hand-copied
|
||||
// process list, and within days of being written it was running a sidecar that had been split in two
|
||||
// and pointing at a pty entry point that had moved. Both failures were silent. Here the entry points
|
||||
// come from ecosystem.config.cjs, so a `script`/`args` change on the host reaches this profile for
|
||||
// free, and a REMOVED or RENAMED app throws at load instead of quietly starting nothing.
|
||||
//
|
||||
// To change what the light profile runs, edit LIGHT_APPS. To change how an app is launched, edit
|
||||
// ecosystem.config.cjs and both profiles follow.
|
||||
//
|
||||
// Start with: pm2 startOrRestart ecosystem.light.config.cjs
|
||||
|
||||
const full = require('./ecosystem.config.cjs');
|
||||
|
||||
const LIGHT_APPS = [
|
||||
'officer', // the app itself: SPA, /api, websockets
|
||||
'officer-anthropic-proxy', // holds the Anthropic credential, forwards upstream
|
||||
'officer-agent', // spawns `claude` — chat is dead without it
|
||||
'officer-opencode', // the alternative agent
|
||||
'officer-pty', // the terminal
|
||||
];
|
||||
|
||||
// Everything in ecosystem.config.cjs that is deliberately NOT here, with the reason. Kept as data so
|
||||
// the two lists can be checked against each other below: an app that is in neither is a mistake, and
|
||||
// saying so at load beats discovering it when a feature silently does nothing.
|
||||
const EXCLUDED = {
|
||||
'officer-vnc': 'mirrors an Xorg display with x11vnc; a light install has no desktop to mirror',
|
||||
'officer-email': 'needs the mbsync/IMAP stack the light profile does not install',
|
||||
'officer-music': 'the ffprobe indexer works, but a full library index is not a light-install concern',
|
||||
'officer-vault': 'reverse-proxies 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 the light profile does not install',
|
||||
'officer-notify': 'its producers are the queue and the email/agent sidecars; nothing to notify about',
|
||||
'officer-wallet': 'holds seed and node credentials',
|
||||
};
|
||||
|
||||
const byName = new Map(full.apps.map((app) => [app.name, app]));
|
||||
|
||||
// A name in LIGHT_APPS that the host no longer defines is the exact failure that broke the Mac file.
|
||||
// Fail loudly at load rather than start a short list and look healthy.
|
||||
const missing = LIGHT_APPS.filter((name) => !byName.has(name));
|
||||
if (missing.length) {
|
||||
throw new Error(
|
||||
`ecosystem.light.config.cjs: ${missing.join(', ')} not found in ecosystem.config.cjs — ` +
|
||||
`the app was renamed or removed. Update LIGHT_APPS.`,
|
||||
);
|
||||
}
|
||||
|
||||
// And an app added to the host that nobody has classified: it belongs in LIGHT_APPS or in EXCLUDED.
|
||||
// Without this, a new sidecar silently defaults to "not in the light profile" and nobody decides.
|
||||
const unclassified = full.apps.map((app) => app.name).filter((name) => !LIGHT_APPS.includes(name) && !(name in EXCLUDED));
|
||||
if (unclassified.length) {
|
||||
throw new Error(
|
||||
`ecosystem.light.config.cjs: ${unclassified.join(', ')} is in ecosystem.config.cjs but neither ` +
|
||||
`included nor excluded here. Add it to LIGHT_APPS or to EXCLUDED 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.
|
||||
module.exports = {
|
||||
apps: LIGHT_APPS.map((name) => ({ ...byName.get(name), cwd: __dirname })),
|
||||
};
|
||||
Reference in New Issue
Block a user