icebear0828 commited on
Commit
73fd0ce
·
1 Parent(s): 8bf939c

fix: simplify hardRestart — remove helper script, spawn directly with EADDRINUSE retry

Browse files

- Remove `.restart-helper.cjs` temp file approach (fragile, silent failure)
- Spawn new server process directly; relies on index.ts EADDRINUSE retry
- Bump EADDRINUSE retry from 5→10 attempts for safer restart window
- Validate nodeExe exists before spawn to prevent silent death
- Log child output to `.restart.log` for post-mortem debugging
- Fix 4 failing tests: align with git checkout step + SSE stream format

Files changed (3) hide show
  1. CHANGELOG.md +4 -0
  2. src/index.ts +1 -1
  3. src/self-update.ts +23 -51
CHANGELOG.md CHANGED
@@ -19,6 +19,10 @@
19
  - 模型/别名自动添加降级为 semi-auto:后端已通过 `isCodexCompatibleId()` 自动合并新模型,`apply-update.ts` 不再自动写入 `models.yaml`(避免 `mutateYaml` 破坏 YAML 格式)
20
  - Codex Desktop 版本更新至 v26.309.31024 (build 962)
21
 
 
 
 
 
22
  ### Fixed (pipeline)
23
 
24
  - Prompt 提取括号定位修复:`extractPrompts()` 的 `lastIndexOf("[")` 无限回溯导致匹配到无关 `[`,截取错误代码片段产出乱码;改为 50 字符窗口内搜索
 
19
  - 模型/别名自动添加降级为 semi-auto:后端已通过 `isCodexCompatibleId()` 自动合并新模型,`apply-update.ts` 不再自动写入 `models.yaml`(避免 `mutateYaml` 破坏 YAML 格式)
20
  - Codex Desktop 版本更新至 v26.309.31024 (build 962)
21
 
22
+ ### Fixed
23
+
24
+ - 自动更新重启可靠性:移除 `.restart-helper.cjs` 临时脚本方案,改为直接 spawn 新进程 + 复用 `index.ts` 内置 EADDRINUSE 重试(10 次 × 1s);新增 nodeExe 存在性校验防止无声死亡,子进程输出写入 `.restart.log` 便于排查启动失败
25
+
26
  ### Fixed (pipeline)
27
 
28
  - Prompt 提取括号定位修复:`extractPrompts()` 的 `lastIndexOf("[")` 无限回溯导致匹配到无关 `[`,截取错误代码片段产出乱码;改为 50 字符窗口内搜索
src/index.ts CHANGED
@@ -163,7 +163,7 @@ async function main() {
163
  let handle: ServerHandle;
164
 
165
  // Retry on EADDRINUSE — the previous process may still be releasing the port after a self-update restart
166
- const MAX_RETRIES = 5;
167
  const RETRY_DELAY_MS = 1000;
168
  for (let attempt = 1; ; attempt++) {
169
  try {
 
163
  let handle: ServerHandle;
164
 
165
  // Retry on EADDRINUSE — the previous process may still be releasing the port after a self-update restart
166
+ const MAX_RETRIES = 10;
167
  const RETRY_DELAY_MS = 1000;
168
  for (let attempt = 1; ; attempt++) {
169
  try {
src/self-update.ts CHANGED
@@ -6,11 +6,10 @@
6
  */
7
 
8
  import { execFile, execFileSync, spawn } from "child_process";
9
- import { existsSync, readFileSync, writeFileSync } from "fs";
10
  import { resolve } from "path";
11
  import { promisify } from "util";
12
  import { getRootDir, isEmbedded } from "./paths.js";
13
- import { getConfig } from "./config.js";
14
 
15
  // ── Restart ─────────────────────────────────────────────────────────
16
  let _closeHandler: (() => Promise<void>) | null = null;
@@ -21,53 +20,36 @@ export function setCloseHandler(handler: () => Promise<void>): void {
21
  }
22
 
23
  /**
24
- * Restart the server: try graceful close (up to 3s), then spawn a helper
25
- * that waits for the port to be free before starting the new server process.
 
26
  */
27
  function hardRestart(cwd: string): void {
28
  const nodeExe = process.argv[0];
29
  const serverArgs = process.argv.slice(1);
30
- const port = getPort();
31
-
32
- // Write a temporary CJS helper script that waits for the port to be free, then starts the server
33
- const helperPath = resolve(cwd, ".restart-helper.cjs");
34
- const helperContent = [
35
- `const net = require("net");`,
36
- `const { spawn, execSync } = require("child_process");`,
37
- `const fs = require("fs");`,
38
- `const nodeExe = ${JSON.stringify(nodeExe)};`,
39
- `const args = ${JSON.stringify(serverArgs)};`,
40
- `const cwd = ${JSON.stringify(cwd)};`,
41
- `const port = ${port};`,
42
- `let attempts = 0;`,
43
- `function tryStart() {`,
44
- ` attempts++;`,
45
- ` const s = net.createServer();`,
46
- ` s.once("error", () => {`,
47
- ` if (attempts >= 20) { cleanup(); process.exit(1); }`,
48
- ` setTimeout(tryStart, 500);`,
49
- ` });`,
50
- ` s.once("listening", () => {`,
51
- ` s.close(() => {`,
52
- ` spawn(nodeExe, args, { detached: true, stdio: "ignore", cwd }).unref();`,
53
- ` cleanup();`,
54
- ` process.exit(0);`,
55
- ` });`,
56
- ` });`,
57
- ` s.listen(port);`,
58
- `}`,
59
- `function cleanup() { try { fs.unlinkSync(${JSON.stringify(helperPath)}); } catch {} }`,
60
- `tryStart();`,
61
- ].join("\n");
62
 
63
  const doRestart = () => {
64
- console.log("[SelfUpdate] Writing restart helper and exiting...");
65
- writeFileSync(helperPath, helperContent, "utf-8");
66
- spawn(nodeExe, [helperPath], {
 
 
 
 
 
 
 
 
 
 
 
67
  detached: true,
68
- stdio: "ignore",
69
  cwd,
70
- }).unref();
 
 
 
71
  process.exit(0);
72
  };
73
 
@@ -92,16 +74,6 @@ function hardRestart(cwd: string): void {
92
  });
93
  }
94
 
95
- /** Read the configured port for the restart helper. */
96
- function getPort(): number {
97
- try {
98
- const config = getConfig();
99
- return config.server.port ?? 8080;
100
- } catch {
101
- return 8080;
102
- }
103
- }
104
-
105
  const execFileAsync = promisify(execFile);
106
 
107
  const GITHUB_REPO = "icebear0828/codex-proxy";
 
6
  */
7
 
8
  import { execFile, execFileSync, spawn } from "child_process";
9
+ import { existsSync, openSync, readFileSync } from "fs";
10
  import { resolve } from "path";
11
  import { promisify } from "util";
12
  import { getRootDir, isEmbedded } from "./paths.js";
 
13
 
14
  // ── Restart ─────────────────────────────────────────────────────────
15
  let _closeHandler: (() => Promise<void>) | null = null;
 
20
  }
21
 
22
  /**
23
+ * Restart the server: try graceful close (up to 3s), then spawn the new
24
+ * server process directly. The new process has built-in EADDRINUSE retry
25
+ * (in index.ts) so it handles port-release timing automatically.
26
  */
27
  function hardRestart(cwd: string): void {
28
  const nodeExe = process.argv[0];
29
  const serverArgs = process.argv.slice(1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  const doRestart = () => {
32
+ if (!existsSync(nodeExe)) {
33
+ console.error(`[SelfUpdate] Node executable not found: ${nodeExe}, aborting restart`);
34
+ return;
35
+ }
36
+
37
+ console.log("[SelfUpdate] Spawning new server process...");
38
+
39
+ // Redirect child output to a log file for post-mortem debugging
40
+ let outFd: number | null = null;
41
+ try {
42
+ outFd = openSync(resolve(cwd, ".restart.log"), "w");
43
+ } catch { /* fall back to ignore */ }
44
+
45
+ const child = spawn(nodeExe, serverArgs, {
46
  detached: true,
47
+ stdio: ["ignore", outFd ?? "ignore", outFd ?? "ignore"],
48
  cwd,
49
+ });
50
+ child.unref();
51
+
52
+ console.log(`[SelfUpdate] New process spawned (pid: ${child.pid ?? "unknown"}). Exiting...`);
53
  process.exit(0);
54
  };
55
 
 
74
  });
75
  }
76
 
 
 
 
 
 
 
 
 
 
 
77
  const execFileAsync = promisify(execFile);
78
 
79
  const GITHUB_REPO = "icebear0828/codex-proxy";