






















@@ -265,6 +265,14 @@ describe("runGatewayUpdate", () => {
265265}
266266}
267267268+async function writeGatewayEntrypoint(pkgRoot: string) {
269+const entrypoint = path.join(pkgRoot, "dist", "index.js");
270+await fs.mkdir(path.dirname(entrypoint), { recursive: true });
271+await fs.writeFile(entrypoint, "export {};\n", "utf-8");
272+await writePackageDistInventory(pkgRoot);
273+return entrypoint;
274+}
275+268276async function createGlobalPackageFixture(rootDir: string) {
269277const nodeModules = path.join(rootDir, "node_modules");
270278const pkgRoot = path.join(nodeModules, "openclaw");
@@ -1456,6 +1464,87 @@ describe("runGatewayUpdate", () => {
14561464);
14571465});
145814661467+it("runs doctor after global npm updates before reporting success", async () => {
1468+const nodeModules = path.join(tempDir, "node_modules");
1469+const pkgRoot = path.join(nodeModules, "openclaw");
1470+await seedGlobalPackageRoot(pkgRoot);
1471+1472+let doctorEnv: NodeJS.ProcessEnv | undefined;
1473+const { calls, runCommand } = createGlobalInstallHarness({
1474+ pkgRoot,
1475+npmRootOutput: nodeModules,
1476+installCommand: "npm i -g openclaw@latest --no-fund --no-audit --loglevel=error",
1477+onInstall: async () => {
1478+await writeGlobalPackageVersion(pkgRoot);
1479+await writeGatewayEntrypoint(pkgRoot);
1480+},
1481+});
1482+const doctorNodePath = await resolveStableNodePath(process.execPath);
1483+const doctorCommand = `${doctorNodePath} ${path.join(
1484+ pkgRoot,
1485+ "dist",
1486+ "index.js",
1487+ )} doctor --non-interactive --fix`;
1488+const runCommandWithDoctor = async (argv: string[], options?: { env?: NodeJS.ProcessEnv }) => {
1489+const key = argv.join(" ");
1490+if (key === doctorCommand) {
1491+calls.push(key);
1492+doctorEnv = options?.env;
1493+return { stdout: "doctor repaired config", stderr: "", code: 0 };
1494+}
1495+return runCommand(argv, options);
1496+};
1497+1498+const result = await runWithCommand(runCommandWithDoctor, { cwd: pkgRoot });
1499+1500+expect(result.status).toBe("ok");
1501+expect(calls).toContain(doctorCommand);
1502+expect(result.steps.map((step) => step.name)).toContain("openclaw doctor");
1503+expect(doctorEnv?.OPENCLAW_UPDATE_IN_PROGRESS).toBe("1");
1504+expect(doctorEnv?.OPENCLAW_UPDATE_PARENT_SUPPORTS_DOCTOR_CONFIG_WRITE).toBe("1");
1505+});
1506+1507+it("fails global npm updates when post-update doctor fails", async () => {
1508+const nodeModules = path.join(tempDir, "node_modules");
1509+const pkgRoot = path.join(nodeModules, "openclaw");
1510+await seedGlobalPackageRoot(pkgRoot);
1511+1512+const { calls, runCommand } = createGlobalInstallHarness({
1513+ pkgRoot,
1514+npmRootOutput: nodeModules,
1515+installCommand: "npm i -g openclaw@latest --no-fund --no-audit --loglevel=error",
1516+onInstall: async () => {
1517+await writeGlobalPackageVersion(pkgRoot);
1518+await writeGatewayEntrypoint(pkgRoot);
1519+},
1520+});
1521+const doctorNodePath = await resolveStableNodePath(process.execPath);
1522+const doctorCommand = `${doctorNodePath} ${path.join(
1523+ pkgRoot,
1524+ "dist",
1525+ "index.js",
1526+ )} doctor --non-interactive --fix`;
1527+const runCommandWithDoctor = async (argv: string[], options?: { env?: NodeJS.ProcessEnv }) => {
1528+const key = argv.join(" ");
1529+if (key === doctorCommand) {
1530+calls.push(key);
1531+return { stdout: "", stderr: "doctor refused migration", code: 1 };
1532+}
1533+return runCommand(argv, options);
1534+};
1535+1536+const result = await runWithCommand(runCommandWithDoctor, { cwd: pkgRoot });
1537+1538+expect(result.status).toBe("error");
1539+expect(result.reason).toBe("doctor-failed");
1540+expect(calls).toContain(doctorCommand);
1541+expect(result.steps.at(-1)).toMatchObject({
1542+name: "openclaw doctor",
1543+exitCode: 1,
1544+stderrTail: "doctor refused migration",
1545+});
1546+});
1547+14591548it("falls back to global npm update when git is missing from PATH", async () => {
14601549const { nodeModules, pkgRoot } = await createGlobalPackageFixture(tempDir);
14611550const { calls, runCommand } = createGlobalInstallHarness({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。