返回 DeepSeek-Reasonix
dev-desktop.mjs
根目录 / desktop / scripts / dev-desktop.mjs
1 #!/usr/bin/env node
2 import { spawn } from "node:child_process";
3 import { mkdir } from "node:fs/promises";
4 import { dirname, resolve } from "node:path";
5 import { fileURLToPath } from "node:url";
6
7 const desktopRoot = dirname(dirname(fileURLToPath(import.meta.url)));
8 const pnpm = process.platform === "win32" ? "pnpm.cmd" : "pnpm";
9 const serviceName = process.platform === "win32"
10 ? "reasonix-desktop-service.exe"
11 : "reasonix-desktop-service";
12 const servicePath = resolve(desktopRoot, "build/bin", serviceName);
13 const port = process.env.REASONIX_DESKTOP_VITE_PORT || "5173";
14 const devUrl = process.env.REASONIX_ELECTRON_DEV_URL || `http://127.0.0.1:${port}`;
15
16 if (process.argv.includes("--help")) {
17 console.log("Build the Reasonix service, start Vite, and launch the Electron development shell.");
18 console.log("Usage: pnpm dev:desktop");
19 process.exit(0);
20 }
21
22 function start(command, args, env = process.env) {
23 return spawn(command, args, { cwd: desktopRoot, env, stdio: "inherit" });
24 }
25
26 function waitForExit(child, label) {
27 return new Promise((resolveExit, rejectExit) => {
28 child.once("error", rejectExit);
29 child.once("exit", (code, signal) => {
30 if (code === 0) resolveExit();
31 else rejectExit(new Error(`${label} exited with ${code ?? signal ?? "an unknown error"}`));
32 });
33 });
34 }
35
36 async function servesReasonix(url) {
37 try {
38 const response = await fetch(url);
39 if (!response.ok) return false;
40 const html = await response.text();
41 return /<title>\s*Reasonix\s*<\/title>/i.test(html);
42 } catch {
43 return false;
44 }
45 }
46
47 async function waitForVite(frontend) {
48 const deadline = Date.now() + 30_000;
49 while (Date.now() < deadline) {
50 if (frontend.exitCode !== null || frontend.signalCode) {
51 throw new Error(`Vite exited with ${frontend.exitCode ?? frontend.signalCode}`);
52 }
53 if (await servesReasonix(devUrl)) return;
54 await new Promise(resolveWait => setTimeout(resolveWait, 150));
55 }
56 throw new Error(`Vite did not become ready at ${devUrl}`);
57 }
58
59 let frontend;
60 let electron;
61 let exiting = false;
62
63 function stopChildren() {
64 if (electron?.exitCode === null) electron.kill("SIGTERM");
65 if (frontend?.exitCode === null) frontend.kill("SIGTERM");
66 }
67
68 function fail(error) {
69 console.error(error instanceof Error ? error.message : error);
70 stopChildren();
71 process.exitCode = 1;
72 }
73
74 for (const signal of ["SIGINT", "SIGTERM"]) {
75 process.once(signal, () => {
76 exiting = true;
77 stopChildren();
78 });
79 }
80
81 try {
82 await mkdir(dirname(servicePath), { recursive: true });
83 console.log("==> Building Reasonix desktop service");
84 await waitForExit(start("go", ["build", "-o", servicePath, "."]), "Go build");
85
86 if (await servesReasonix(devUrl)) {
87 console.log(`==> Reusing Reasonix Vite server at ${devUrl}`);
88 } else {
89 console.log(`==> Starting Reasonix Vite server at ${devUrl}`);
90 frontend = start(pnpm, ["--dir", "frontend", "dev", "--host", "127.0.0.1", "--port", port, "--strictPort"]);
91 await waitForVite(frontend);
92 }
93
94 console.log("==> Launching Reasonix Electron development shell");
95 electron = start(pnpm, ["--dir", "electron", "dev"], {
96 ...process.env,
97 REASONIX_DESKTOP_SERVICE: servicePath,
98 REASONIX_ELECTRON_DEV_URL: devUrl,
99 });
100 const electronExit = waitForExit(electron, "Electron");
101 const frontendExit = frontend
102 ? waitForExit(frontend, "Vite").then(() => { throw new Error("Vite stopped before Electron"); })
103 : new Promise(() => {});
104 await Promise.race([electronExit, frontendExit]);
105 if (frontend?.exitCode === null) frontend.kill("SIGTERM");
106 } catch (error) {
107 if (!exiting) fail(error);
108 }
109
109 lines Plain Text