131 lines
2.9 KiB
TypeScript
131 lines
2.9 KiB
TypeScript
import { spawn, type ChildProcess } from "node:child_process";
|
|
import { setTimeout as delay } from "node:timers/promises";
|
|
|
|
const children: ChildProcess[] = [];
|
|
|
|
function run(command: string, args: string[]) {
|
|
const child = spawn(command, args, {
|
|
stdio: "inherit",
|
|
shell: false,
|
|
env: {
|
|
...process.env,
|
|
PORT: process.env.PORT ?? "43210",
|
|
BOSS_DATA_FILE: process.env.BOSS_DATA_FILE ?? ".boss-data/demo-store.json",
|
|
},
|
|
});
|
|
child.on("exit", (code, signal) => {
|
|
console.log(`[demo] child exited`, { code, signal, command, args: args.join(" ") });
|
|
if (code && code !== 0 && signal !== "SIGINT" && signal !== "SIGTERM") {
|
|
shutdown("SIGTERM");
|
|
}
|
|
});
|
|
children.push(child);
|
|
return child;
|
|
}
|
|
|
|
async function waitForHealth(url: string) {
|
|
for (let attempt = 0; attempt < 30; attempt += 1) {
|
|
try {
|
|
const response = await fetch(`${url}/api/health`);
|
|
if (response.ok) {
|
|
return;
|
|
}
|
|
} catch {
|
|
// wait and retry
|
|
}
|
|
await delay(1_000);
|
|
}
|
|
|
|
throw new Error(`Server did not become healthy: ${url}`);
|
|
}
|
|
|
|
async function isHealthy(url: string) {
|
|
try {
|
|
const response = await fetch(`${url}/api/health`);
|
|
if (!response.ok) {
|
|
return false;
|
|
}
|
|
const payload = await response.json();
|
|
return payload?.status === "ok";
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function shutdown(signal: NodeJS.Signals) {
|
|
for (const child of children) {
|
|
if (!child.killed) {
|
|
child.kill(signal);
|
|
}
|
|
}
|
|
process.exit(0);
|
|
}
|
|
|
|
async function main() {
|
|
const serverUrl = `http://127.0.0.1:${process.env.PORT ?? "43210"}`;
|
|
const hasExisting = await isHealthy(serverUrl);
|
|
if (!hasExisting) {
|
|
run(process.execPath, ["./node_modules/tsx/dist/cli.mjs", "src/server.ts"]);
|
|
await waitForHealth(serverUrl);
|
|
} else {
|
|
console.log(`Boss server already running at ${serverUrl}, reusing it.`);
|
|
}
|
|
|
|
run(process.execPath, [
|
|
"./node_modules/tsx/dist/cli.mjs",
|
|
"src/worker.ts",
|
|
"--name",
|
|
"win-a",
|
|
"--os",
|
|
"windows",
|
|
"--capability",
|
|
"terminal",
|
|
"--capability",
|
|
"browser",
|
|
"--server",
|
|
serverUrl,
|
|
]);
|
|
run(process.execPath, [
|
|
"./node_modules/tsx/dist/cli.mjs",
|
|
"src/worker.ts",
|
|
"--name",
|
|
"win-b",
|
|
"--os",
|
|
"windows",
|
|
"--capability",
|
|
"terminal",
|
|
"--capability",
|
|
"test",
|
|
"--server",
|
|
serverUrl,
|
|
]);
|
|
run(process.execPath, [
|
|
"./node_modules/tsx/dist/cli.mjs",
|
|
"src/worker.ts",
|
|
"--name",
|
|
"mac-a",
|
|
"--os",
|
|
"macos",
|
|
"--capability",
|
|
"terminal",
|
|
"--capability",
|
|
"browser",
|
|
"--capability",
|
|
"test",
|
|
"--server",
|
|
serverUrl,
|
|
]);
|
|
|
|
console.log(`Boss demo running at ${serverUrl}`);
|
|
console.log("Press Ctrl+C to stop server and workers.");
|
|
await new Promise(() => {});
|
|
}
|
|
|
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
|
|
main().catch((error) => {
|
|
console.error(error);
|
|
shutdown("SIGTERM");
|
|
});
|