MCPcopy Create free account
hub / github.com/ForestHubAI/edge-agents / openDev

Function openDev

ts/workflow-cli/cli/open.ts:77–128  ·  view source on GitHub ↗

* DEV: spawn Vite as a child process (NOT via createServer in-process — Vite's * config loader fights with the tsx loader powering this CLI), wait for the dev * server, then open the browser. Narrows the bridge's allowlist to the bound * file's directory via FH_BUILDER_ALLOW_ROOT.

(resolvedFile?: string, allowRoot?: string)

Source from the content-addressed store, hash-verified

75 * file's directory via FH_BUILDER_ALLOW_ROOT.
76 */
77async function openDev(resolvedFile?: string, allowRoot?: string): Promise<void> {
78 const here = path.dirname(fileURLToPath(import.meta.url));
79 const appRoot = path.resolve(here, "..");
80
81 const env: NodeJS.ProcessEnv = { ...process.env };
82 if (allowRoot) env.FH_BUILDER_ALLOW_ROOT = allowRoot;
83
84 const port = 5173;
85 const url = resolvedFile
86 ? `http://localhost:${port}/?file=${encodeURIComponent(resolvedFile)}`
87 : `http://localhost:${port}/`;
88
89 const viteBin = locateViteBin(appRoot);
90 // --no-open: this CLI is the sole opener — it builds the `?file=` URL and
91 // opens it below. Without this, vite.config's `server.open: true` would also
92 // open a tab, at the bare URL (no ?file=), so you'd get two tabs and the
93 // wrong one focused. (CLI flag overrides config; `dev` still auto-opens.)
94 const vite = spawn(viteBin, ["--port", String(port), "--host", "127.0.0.1", "--strictPort", "--no-open"], {
95 cwd: appRoot,
96 env,
97 stdio: "inherit",
98 shell: process.platform === "win32", // .cmd shims need a shell on Windows
99 });
100
101 vite.on("error", (err) => {
102 process.stderr.write(`Failed to start vite: ${err.message}\n`);
103 process.exit(1);
104 });
105
106 // Poll until the port answers, then open the browser. Bounded so we don't
107 // hang forever if vite never starts.
108 void (async () => {
109 const ok = await waitForPort(port, 15000);
110 if (!ok) {
111 process.stderr.write("Vite didn't come up within 15s.\n");
112 return;
113 }
114 process.stdout.write(`\nfh-workflow running at ${url}\n`);
115 if (resolvedFile) process.stdout.write(`Bound to ${resolvedFile}\n`);
116 process.stdout.write("Press Ctrl+C to stop.\n\n");
117 openInBrowser(url);
118 })();
119
120 // Keep our process alive until vite exits (stdio is inherited, so Ctrl+C
121 // goes straight to vite which then exits, then we exit).
122 await new Promise<void>((resolve) => {
123 vite.on("exit", (code) => {
124 process.exit(code ?? 0);
125 resolve();
126 });
127 });
128}
129
130function locateViteBin(appRoot: string): string {
131 const cmd = process.platform === "win32" ? "vite.cmd" : "vite";

Callers 1

openCommandFunction · 0.85

Calls 4

locateViteBinFunction · 0.85
waitForPortFunction · 0.85
openInBrowserFunction · 0.85
writeMethod · 0.80

Tested by

no test coverage detected