File size: 3,855 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/**
 * Live Dashboard WebSocket Server β€” Startup Script
 *
 * This script starts the live dashboard WebSocket server as a separate
 * process alongside the Next.js app. Run it with:
 *
 *   node scripts/start-ws-server.mjs
 *
 * Environment variables:
 *   LIVE_WS_PORT       β€” WebSocket server port (default: 20129)
 *   LIVE_WS_HOST       β€” WebSocket server host (default: 127.0.0.1)
 *   OMNIROUTE_DISABLE_LIVE_WS β€” Set to "1" or "true" to disable
 */

import { spawnSync } from "node:child_process";
import { fileURLToPath, pathToFileURL } from "node:url";
import { dirname, join } from "node:path";

const BOOTSTRAPPED_ENV = "OMNIROUTE_LIVE_WS_BOOTSTRAPPED";

/**
 * Package root for the launcher = the directory above `scripts/`, where
 * `package.json` and `tsconfig.json` (which defines the `@/*` path aliases)
 * live. Derived from the script URL so it is correct no matter where the
 * process was launched from.
 */
export function resolvePackageRoot(scriptUrl) {
  return join(dirname(fileURLToPath(scriptUrl)), "..");
}

/**
 * Build the re-spawn spec for the bootstrap stage. The sidecar is launched with
 * `node --import tsx <self>`; #4055: without `cwd` pinned to the package root, a
 * launch from outside the package dir (global npm / homebrew, or a
 * systemd/launchd unit started from $HOME) fails with
 * `ERR_MODULE_NOT_FOUND: Cannot find package 'tsx'`, and even from the package
 * dir tsx cannot resolve the tsconfig `@/*` aliases. Pinning `cwd` to the
 * package root fixes both β€” tsx and tsconfig are both discovered there.
 */
export function buildSidecarSpawn(scriptUrl, env = process.env) {
  return {
    command: process.execPath,
    args: ["--import", "tsx", fileURLToPath(scriptUrl)],
    options: {
      // #4055: pin cwd to the package root so `node --import tsx` resolves the
      // `tsx` package and the tsconfig `@/*` aliases regardless of where the
      // process manager (npm-global / homebrew / systemd / launchd) launched us.
      cwd: resolvePackageRoot(scriptUrl),
      stdio: "inherit",
      env: {
        ...env,
        [BOOTSTRAPPED_ENV]: "1",
        // Prevent liveServer.ts from auto-starting on import; this script owns
        // process startup so errors propagate to the supervisor/CLI caller.
        OMNIROUTE_ENABLE_LIVE_WS: "0",
      },
    },
  };
}

async function main() {
  if (
    process.env.OMNIROUTE_DISABLE_LIVE_WS === "1" ||
    process.env.OMNIROUTE_DISABLE_LIVE_WS === "true"
  ) {
    console.log("[LiveWS] Disabled via OMNIROUTE_DISABLE_LIVE_WS");
    process.exit(0);
  }

  if (process.env[BOOTSTRAPPED_ENV] !== "1") {
    const { command, args, options } = buildSidecarSpawn(import.meta.url);
    const result = spawnSync(command, args, options);

    if (result.signal) {
      process.kill(process.pid, result.signal);
    }

    process.exit(result.status ?? 1);
  }

  const { startLiveDashboardServer } = await import("../src/server/ws/liveServer.ts");

  const port = parseInt(process.env.LIVE_WS_PORT || "20129", 10);
  const host = process.env.LIVE_WS_HOST || "127.0.0.1";

  console.log(`[LiveWS] Starting dashboard WebSocket server on ${host}:${port}...`);

  try {
    await startLiveDashboardServer(port, host);
    console.log(`[LiveWS] Dashboard WebSocket server listening on ws://${host}:${port}`);
    console.log("[LiveWS] Connect via: ws://localhost:%d?token=<api-key>", port);
    console.log("[LiveWS] Channels: requests, combo, credentials");
  } catch (err) {
    console.error("[LiveWS] Failed to start:", err);
    process.exit(1);
  }
}

// Only run when invoked as the entry point β€” importing this module (e.g. from a
// unit test exercising the spawn-spec helpers) must not spawn or exit.
const invokedPath = process.argv[1] ? pathToFileURL(process.argv[1]).href : "";
if (import.meta.url === invokedPath) {
  await main();
}