mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:10:44 +00:00
fix: launch Windows startup gateway directly
This commit is contained in:
@@ -47,7 +47,7 @@ BUILD_LOCK_DIR="${TMPDIR:-/tmp}/openclaw-parallels-build.lock"
|
||||
TIMEOUT_INSTALL_SITE_S=420
|
||||
TIMEOUT_INSTALL_TGZ_S=420
|
||||
TIMEOUT_INSTALL_REGISTRY_S=420
|
||||
TIMEOUT_UPDATE_DEV_S=300
|
||||
TIMEOUT_UPDATE_DEV_S="${OPENCLAW_PARALLELS_MACOS_UPDATE_DEV_TIMEOUT_S:-600}"
|
||||
TIMEOUT_VERIFY_S=60
|
||||
TIMEOUT_ONBOARD_S=180
|
||||
TIMEOUT_GATEWAY_S=180
|
||||
|
||||
@@ -41,7 +41,7 @@ BUILD_LOCK_DIR="${TMPDIR:-/tmp}/openclaw-parallels-build.lock"
|
||||
TIMEOUT_SNAPSHOT_S=240
|
||||
TIMEOUT_GIT_SETUP_S=1200
|
||||
TIMEOUT_INSTALL_S=420
|
||||
TIMEOUT_UPDATE_S=300
|
||||
TIMEOUT_UPDATE_S="${OPENCLAW_PARALLELS_WINDOWS_UPDATE_TIMEOUT_S:-1800}"
|
||||
TIMEOUT_UPDATE_POLL_GRACE_S=60
|
||||
TIMEOUT_VERIFY_S=120
|
||||
TIMEOUT_ONBOARD_S=600
|
||||
|
||||
@@ -2,7 +2,6 @@ import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { PassThrough } from "node:stream";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { quoteCmdScriptArg } from "./cmd-argv.js";
|
||||
import "./test-helpers/schtasks-base-mocks.js";
|
||||
import {
|
||||
inspectPortUsage,
|
||||
@@ -91,11 +90,25 @@ async function writeStartupFallbackEntry(env: Record<string, string>) {
|
||||
return startupEntryPath;
|
||||
}
|
||||
|
||||
function expectStartupFallbackSpawn(env: Record<string, string>) {
|
||||
expect(spawn).toHaveBeenCalledWith(
|
||||
"cmd.exe",
|
||||
["/d", "/s", "/c", quoteCmdScriptArg(resolveTaskScriptPath(env))],
|
||||
expect.objectContaining({ detached: true, stdio: "ignore", windowsHide: true }),
|
||||
function expectStartupFallbackSpawn() {
|
||||
expect(spawn).toHaveBeenCalled();
|
||||
const calls = spawn.mock.calls as unknown as Array<
|
||||
[string, readonly string[], Record<string, unknown>]
|
||||
>;
|
||||
const lastCall = calls[calls.length - 1];
|
||||
if (!lastCall) {
|
||||
throw new Error("expected gateway launch spawn call");
|
||||
}
|
||||
const [executable, args, options] = lastCall;
|
||||
expect(executable).not.toBe("cmd.exe");
|
||||
expect(args).toEqual(expect.arrayContaining(["--port", "18789"]));
|
||||
expect(options).toEqual(
|
||||
expect.objectContaining({
|
||||
detached: true,
|
||||
env: expect.objectContaining({ OPENCLAW_GATEWAY_PORT: "18789" }),
|
||||
stdio: "ignore",
|
||||
windowsHide: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -197,11 +210,7 @@ describe("Windows startup fallback", () => {
|
||||
expect(result.scriptPath).toBe(resolveTaskScriptPath(env));
|
||||
expect(startupScript).toContain('start "" /min cmd.exe /d /c');
|
||||
expect(startupScript).toContain("gateway.cmd");
|
||||
expect(spawn).toHaveBeenCalledWith(
|
||||
"cmd.exe",
|
||||
["/d", "/s", "/c", quoteCmdScriptArg(resolveTaskScriptPath(env))],
|
||||
expect.objectContaining({ detached: true, stdio: "ignore", windowsHide: true }),
|
||||
);
|
||||
expectStartupFallbackSpawn();
|
||||
expect(childUnref).toHaveBeenCalled();
|
||||
expect(printed).toContain("Installed Windows login item");
|
||||
});
|
||||
@@ -216,7 +225,7 @@ describe("Windows startup fallback", () => {
|
||||
await installGatewayScheduledTask(env);
|
||||
|
||||
await expect(fs.access(resolveStartupEntryPath(env))).resolves.toBeUndefined();
|
||||
expectStartupFallbackSpawn(env);
|
||||
expectStartupFallbackSpawn();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -231,18 +240,18 @@ describe("Windows startup fallback", () => {
|
||||
await installGatewayScheduledTask(env);
|
||||
|
||||
await expect(fs.access(resolveStartupEntryPath(env))).resolves.toBeUndefined();
|
||||
expectStartupFallbackSpawn(env);
|
||||
expectStartupFallbackSpawn();
|
||||
});
|
||||
});
|
||||
|
||||
it("launches the task script directly when schtasks /Run is accepted but never starts the task", async () => {
|
||||
it("launches through the Startup-style launcher when schtasks /Run is accepted but never starts the task", async () => {
|
||||
await withWindowsEnv("openclaw-win-startup-", async ({ env }) => {
|
||||
fastForwardTaskStartWait();
|
||||
addAcceptedRunNeverStartsResponses();
|
||||
|
||||
await installGatewayScheduledTask(env);
|
||||
|
||||
expectStartupFallbackSpawn(env);
|
||||
expectStartupFallbackSpawn();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -388,6 +397,7 @@ describe("Windows startup fallback", () => {
|
||||
{ code: 0, stdout: "", stderr: "" },
|
||||
{ code: 1, stdout: "", stderr: "not found" },
|
||||
]);
|
||||
await writeGatewayScript(env);
|
||||
await writeStartupFallbackEntry(env);
|
||||
inspectPortUsage.mockResolvedValue({
|
||||
port: 18789,
|
||||
@@ -401,7 +411,7 @@ describe("Windows startup fallback", () => {
|
||||
outcome: "completed",
|
||||
});
|
||||
expectGatewayTermination(5151);
|
||||
expectStartupFallbackSpawn(env);
|
||||
expectStartupFallbackSpawn();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -432,7 +442,7 @@ describe("Windows startup fallback", () => {
|
||||
outcome: "completed",
|
||||
});
|
||||
|
||||
expectStartupFallbackSpawn(env);
|
||||
expectStartupFallbackSpawn();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -270,6 +270,10 @@ function buildTaskScript({
|
||||
return `${lines.join("\r\n")}\r\n`;
|
||||
}
|
||||
|
||||
function renderStartupLaunchCommand(scriptPath: string): string {
|
||||
return `start "" /min cmd.exe /d /c ${quoteCmdScriptArg(scriptPath)}`;
|
||||
}
|
||||
|
||||
function buildStartupLauncherScript(params: { description?: string; scriptPath: string }): string {
|
||||
const lines = ["@echo off"];
|
||||
const trimmedDescription = params.description?.trim();
|
||||
@@ -277,7 +281,7 @@ function buildStartupLauncherScript(params: { description?: string; scriptPath:
|
||||
assertNoCmdLineBreak(trimmedDescription, "Startup launcher description");
|
||||
lines.push(`rem ${trimmedDescription}`);
|
||||
}
|
||||
lines.push(`start "" /min cmd.exe /d /c ${quoteCmdScriptArg(params.scriptPath)}`);
|
||||
lines.push(renderStartupLaunchCommand(params.scriptPath));
|
||||
return `${lines.join("\r\n")}\r\n`;
|
||||
}
|
||||
|
||||
@@ -309,8 +313,26 @@ async function isRegisteredScheduledTask(env: GatewayServiceEnv): Promise<boolea
|
||||
return res.code === 0;
|
||||
}
|
||||
|
||||
function launchFallbackTaskScript(scriptPath: string): void {
|
||||
const child = spawn("cmd.exe", ["/d", "/s", "/c", quoteCmdScriptArg(scriptPath)], {
|
||||
async function launchFallbackTaskScript(env: GatewayServiceEnv): Promise<void> {
|
||||
const scriptPath = resolveTaskScriptPath(env);
|
||||
const command = await readScheduledTaskCommand(env);
|
||||
if (command?.programArguments.length) {
|
||||
const [executable, ...args] = command.programArguments;
|
||||
const child = spawn(executable, args, {
|
||||
cwd: command.workingDirectory || undefined,
|
||||
detached: true,
|
||||
env: {
|
||||
...process.env,
|
||||
...command.environment,
|
||||
},
|
||||
stdio: "ignore",
|
||||
windowsHide: true,
|
||||
});
|
||||
child.unref();
|
||||
return;
|
||||
}
|
||||
|
||||
const child = spawn("cmd.exe", ["/d", "/c", scriptPath], {
|
||||
detached: true,
|
||||
stdio: "ignore",
|
||||
windowsHide: true,
|
||||
@@ -563,7 +585,7 @@ async function restartStartupEntry(
|
||||
if (typeof runtime.pid === "number" && runtime.pid > 0) {
|
||||
await terminateGatewayProcessTree(runtime.pid, 300);
|
||||
}
|
||||
launchFallbackTaskScript(resolveTaskScriptPath(env));
|
||||
await launchFallbackTaskScript(env);
|
||||
stdout.write(`${formatLine("Restarted Windows login item", resolveTaskName(env))}\n`);
|
||||
return { outcome: "completed" };
|
||||
}
|
||||
@@ -784,7 +806,7 @@ async function runScheduledTaskOrThrow(params: {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
launchFallbackTaskScript(params.scriptPath);
|
||||
await launchFallbackTaskScript(params.env);
|
||||
}
|
||||
|
||||
async function activateScheduledTask(params: {
|
||||
@@ -831,7 +853,7 @@ async function activateScheduledTask(params: {
|
||||
scriptPath: params.scriptPath,
|
||||
});
|
||||
await fs.writeFile(startupEntryPath, launcher, "utf8");
|
||||
launchFallbackTaskScript(params.scriptPath);
|
||||
await launchFallbackTaskScript(params.env);
|
||||
writeFormattedLines(
|
||||
params.stdout,
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user