

























@@ -1,6 +1,5 @@
11import { spawn, spawnSync } from "node:child_process";
22import { once } from "node:events";
3-import { watch } from "node:fs";
43import fs from "node:fs/promises";
54import path from "node:path";
65import { afterEach, describe, expect, it } from "vitest";
@@ -38,51 +37,18 @@ async function addCompileCacheProbe(fixtureRoot: string): Promise<void> {
3837);
3938}
403941-async function waitForFile(filePath: string, timeoutMs: number): Promise<string> {
42-try {
43-return await fs.readFile(filePath, "utf8");
44-} catch {
45-// Wait below.
40+async function waitForJsonFile<T>(filePath: string, timeoutMs: number): Promise<T> {
41+const deadline = Date.now() + timeoutMs;
42+let lastError: unknown;
43+while (Date.now() <= deadline) {
44+try {
45+return JSON.parse(await fs.readFile(filePath, "utf8")) as T;
46+} catch (error) {
47+lastError = error;
48+await new Promise((resolve) => setTimeout(resolve, 25));
49+}
4650}
47-48-const signal = AbortSignal.timeout(timeoutMs);
49-return await new Promise<string>((resolve, reject) => {
50-let settled = false;
51-let watcher: ReturnType<typeof watch> | undefined;
52-const fileName = path.basename(filePath);
53-54-const cleanup = () => {
55-if (settled) {
56-return;
57-}
58-settled = true;
59-watcher?.close();
60-};
61-const tryRead = async () => {
62-try {
63-const content = await fs.readFile(filePath, "utf8");
64-cleanup();
65-resolve(content);
66-} catch {
67-// Keep watching until the deadline aborts.
68-}
69-};
70-71-signal.addEventListener(
72-"abort",
73-() => {
74-cleanup();
75-reject(new Error(`timed out waiting for ${filePath}`));
76-},
77-{ once: true },
78-);
79-watcher = watch(path.dirname(filePath), { signal }, (_event, changedFileName) => {
80-if (!changedFileName || changedFileName.toString() === fileName) {
81-void tryRead();
82-}
83-});
84-void tryRead();
85-});
51+throw new Error(`timed out waiting for parseable JSON in ${filePath}`, { cause: lastError });
8652}
87538854async function waitForProcessExit(
@@ -274,7 +240,7 @@ describe("openclaw launcher", () => {
274240let respawnChildPid: number | undefined;
275241276242try {
277-const childInfo = JSON.parse(await waitForFile(childInfoPath, 5000)) as { pid: number };
243+const childInfo = await waitForJsonFile<{ pid: number }>(childInfoPath, 5000);
278244respawnChildPid = childInfo.pid;
279245280246launcher.kill("SIGTERM");
@@ -325,7 +291,7 @@ describe("openclaw launcher", () => {
325291let respawnChildPid: number | undefined;
326292327293try {
328-const childInfo = JSON.parse(await waitForFile(childInfoPath, 5000)) as { pid: number };
294+const childInfo = await waitForJsonFile<{ pid: number }>(childInfoPath, 5000);
329295respawnChildPid = childInfo.pid;
330296331297launcher.kill("SIGTERM");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。