add gen:ecosystem script to rebuild ecosystem.config.cjs from the database
bun gen:ecosystem writes the whole file from three sources: CORE_PROCESSES read directly out of scripts/setup/officer-setup/lib/services.sh (shelled out to bash, so there is exactly one list rather than a TS copy to keep in sync by hand), marketplace plugins that are installed AND enabled in Postgres and actually ship a sidecar/, and app-store services that are installed AND enabled, using each catalogue entry's `process` name against the src/servers/sidecar/<id>/index.ts convention. Extracted `pluginPm2Entry` (a pure DiscoveredPlugin -> Pm2App) out of addPluginToEcosystem so the live install/uninstall path and this from-scratch rebuild share one definition of what a plugin's entry looks like, rather than two that can drift apart. addPluginToEcosystem's own behavior is unchanged. Complements, not replaces, the existing incremental add/remove in servers/plugins/ecosystem.ts — that edits the live file at install time; this regenerates the whole thing, for a fresh clone, drift, or restoring from a backup. Found while testing against this machine's real file, worth flagging rather than silently working around: officer-marketplace (the marketplace.officer.dev process, src/servers/marketplace/server.tsx) is in this machine's ecosystem.config.cjs by hand and is modeled by neither the plugin system nor the app-store catalogue, so this script does not know about it and would drop its entry on a real run. Left alone rather than guessed at. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,7 @@ import type { DiscoveredPlugin } from './manifest';
|
||||
const ECOSYSTEM_PATH = join(PLATFORM_DIR, 'ecosystem.config.cjs');
|
||||
|
||||
/** One PM2 app entry. Matches what `services.sh` emits for the core processes. */
|
||||
type Pm2App = { name: string; script: string; args?: string; cwd: string; watch: boolean };
|
||||
export type Pm2App = { name: string; script: string; args?: string; cwd: string; watch: boolean };
|
||||
|
||||
/**
|
||||
* The header to write when there is no file to take one from.
|
||||
@@ -91,13 +91,15 @@ function writeEcosystemApps(apps: Pm2App[]): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the plugin's sidecar has an entry, replacing one that is already there.
|
||||
* The PM2 entry a plugin's sidecar would get, or null when it has none — most plugins won't.
|
||||
*
|
||||
* Idempotent, because re-installing is how a plugin is upgraded and the script path may have moved.
|
||||
* Returns false when the plugin has no sidecar at all, which is not a failure — most plugins won't.
|
||||
* Pure, deliberately: `addPluginToEcosystem` below is the read-modify-write that touches the file for a
|
||||
* live install, and `scripts/gen-ecosystem.ts` is the other caller, rebuilding the whole file from what
|
||||
* the database says is installed and enabled. Neither should duplicate the other's idea of what a
|
||||
* plugin's entry looks like.
|
||||
*/
|
||||
export function addPluginToEcosystem(plugin: DiscoveredPlugin): boolean {
|
||||
if (!plugin.sidecar) return false;
|
||||
export function pluginPm2Entry(plugin: DiscoveredPlugin): Pm2App | null {
|
||||
if (!plugin.sidecar) return null;
|
||||
|
||||
const name = pluginProcessName(plugin.appName);
|
||||
// Relative to the platform, because `cwd` is pinned to it and an absolute path would break the moment
|
||||
@@ -106,12 +108,22 @@ export function addPluginToEcosystem(plugin: DiscoveredPlugin): boolean {
|
||||
? plugin.sidecar.script.slice(PLATFORM_DIR.length + 1)
|
||||
: plugin.sidecar.script;
|
||||
|
||||
const entry: Pm2App =
|
||||
plugin.sidecar.runtime === 'node'
|
||||
? { name, script: 'node', args: script, cwd: PLATFORM_DIR, watch: false }
|
||||
: { name, script: 'bun', args: `run ${script}`, cwd: PLATFORM_DIR, watch: false };
|
||||
return plugin.sidecar.runtime === 'node'
|
||||
? { name, script: 'node', args: script, cwd: PLATFORM_DIR, watch: false }
|
||||
: { name, script: 'bun', args: `run ${script}`, cwd: PLATFORM_DIR, watch: false };
|
||||
}
|
||||
|
||||
const apps = readEcosystemApps().filter((a) => a.name !== name);
|
||||
/**
|
||||
* Ensure the plugin's sidecar has an entry, replacing one that is already there.
|
||||
*
|
||||
* Idempotent, because re-installing is how a plugin is upgraded and the script path may have moved.
|
||||
* Returns false when the plugin has no sidecar at all, which is not a failure — most plugins won't.
|
||||
*/
|
||||
export function addPluginToEcosystem(plugin: DiscoveredPlugin): boolean {
|
||||
const entry = pluginPm2Entry(plugin);
|
||||
if (!entry) return false;
|
||||
|
||||
const apps = readEcosystemApps().filter((a) => a.name !== entry.name);
|
||||
apps.push(entry);
|
||||
writeEcosystemApps(apps);
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user