fix(control): ship dev-pm2 launcher outside ignored scripts path

Move launcher to packages/server/dev-pm2.mjs (scripts/dev-* is gitignored)
and fix repo root resolution. Improve Ops pause/resume button disabled styles.

Co-authored-by: Nasyarobby Putra <nasyarobby@gmail.com>
This commit is contained in:
Cursor Agent
2026-08-19 10:55:56 +00:00
co-authored by nsrb
parent 4e6c336ae8
commit 8590fce3f8
2 changed files with 115 additions and 1 deletions
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env node
/**
* Start Vite (:8500) + control (:8600). Control connects to PM2 and starts HTTP/workers.
*
* Usage: pnpm dev:pm2
*/
import { spawn } from "node:child_process";
import path from "node:path";
import { fileURLToPath } from "node:url";
import net from "node:net";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const children = [];
function run(command, args, opts = {}) {
const child = spawn(command, args, {
cwd: root,
stdio: "inherit",
env: {
...process.env,
JFLOW_CORS_ORIGIN: process.env.JFLOW_CORS_ORIGIN ?? "http://localhost:8500",
JFLOW_CONTROL_PORT: process.env.JFLOW_CONTROL_PORT ?? "8600",
PORT: process.env.JFLOW_HTTP_PORT ?? "8700",
...opts.env,
},
shell: false,
});
children.push(child);
child.on("exit", (code, signal) => {
if (shuttingDown) return;
console.error(`[dev:pm2] ${command} ${args.join(" ")} exited (${code ?? signal})`);
shutdown(code ?? 1);
});
return child;
}
let shuttingDown = false;
async function redisReachable() {
const url = process.env.REDIS_URL || "redis://127.0.0.1:6379";
let host = "127.0.0.1";
let port = 6379;
try {
const u = new URL(url);
host = u.hostname || host;
port = Number(u.port || 6379);
} catch {
// keep defaults
}
return new Promise((resolve) => {
const socket = net.connect({ host, port });
socket.setTimeout(1500);
socket.on("connect", () => {
socket.destroy();
resolve(true);
});
socket.on("timeout", () => {
socket.destroy();
resolve(false);
});
socket.on("error", () => resolve(false));
});
}
async function shutdown(code = 0) {
if (shuttingDown) return;
shuttingDown = true;
for (const child of children) {
if (!child.killed) child.kill("SIGTERM");
}
// Best-effort: stop jflow apps so the next run is clean
try {
const stop = spawn(
process.platform === "win32" ? "npx.cmd" : "npx",
["pm2", "delete", "jflow-http", "jflow-worker"],
{ cwd: root, stdio: "ignore", shell: false },
);
await new Promise((r) => stop.on("exit", r));
} catch {
// ignore
}
process.exit(code);
}
process.on("SIGINT", () => void shutdown(0));
process.on("SIGTERM", () => void shutdown(0));
if (!(await redisReachable())) {
console.error(
"[dev:pm2] Redis is not reachable. Start Redis (default redis://127.0.0.1:6379) and retry.",
);
process.exit(1);
}
console.log("[dev:pm2] starting control on :8600 (PM2 will start HTTP :8700 + workers)");
run(process.execPath, [path.join(root, "packages/server/control.js")], {
env: {
JFLOW_CONTROL_PORT: "8600",
},
});
// Give control a moment to migrate + spawn before Vite opens
await new Promise((r) => setTimeout(r, 1500));
console.log("[dev:pm2] starting Vite on :8500");
run(
process.platform === "win32" ? "pnpm.cmd" : "pnpm",
["--filter", "@jerapah-flow/web", "dev"],
{
env: {
// vite.config reads nothing; port is set in vite.config.js
},
},
);
+1 -1
View File
@@ -6,7 +6,7 @@
"main": "runner.js",
"scripts": {
"dev": "node --watch runner.js",
"dev:pm2": "node scripts/dev-pm2.mjs",
"dev:pm2": "node dev-pm2.mjs",
"start": "node runner.js",
"start:api": "node server.js",
"start:worker": "node worker.js",