




















@@ -79,9 +79,19 @@ function sanitizeWindowsFilename(value: string): string {
7979return value.replace(/[<>:"/\\|?*]/g, "_").replace(/\p{Cc}/gu, "_");
8080}
818182-function resolveStartupEntryPath(env: GatewayServiceEnv): string {
82+function resolveStartupEntryPath(env: GatewayServiceEnv, extension?: "cmd" | "vbs"): string {
8383const taskName = resolveTaskName(env);
84-return path.join(resolveWindowsStartupDir(env), `${sanitizeWindowsFilename(taskName)}.cmd`);
84+const entryExtension = extension ?? (shouldUseHiddenWindowsTaskLauncher(env) ? "vbs" : "cmd");
85+return path.join(
86+resolveWindowsStartupDir(env),
87+`${sanitizeWindowsFilename(taskName)}.${entryExtension}`,
88+);
89+}
90+91+function resolveStartupEntryPaths(env: GatewayServiceEnv): string[] {
92+const primaryPath = resolveStartupEntryPath(env);
93+const legacyCmdPath = resolveStartupEntryPath(env, "cmd");
94+return Array.from(new Set([primaryPath, legacyCmdPath]));
8595}
86968797// `/TR` is parsed by schtasks itself, while the generated `gateway.cmd` line is parsed by cmd.exe.
@@ -108,6 +118,19 @@ function resolveTaskUser(env: GatewayServiceEnv): string | null {
108118return username;
109119}
110120121+function shouldUseHiddenWindowsTaskLauncher(env: GatewayServiceEnv): boolean {
122+const value = normalizeLowercaseStringOrEmpty(env.OPENCLAW_WINDOWS_TASK_HIDDEN_LAUNCHER);
123+return value === "1" || value === "true" || value === "yes";
124+}
125+126+function resolveTaskLauncherScriptPath(env: GatewayServiceEnv, scriptPath: string): string {
127+if (!shouldUseHiddenWindowsTaskLauncher(env)) {
128+return scriptPath;
129+}
130+const parsed = path.parse(scriptPath);
131+return path.join(parsed.dir, `${parsed.name}.vbs`);
132+}
133+111134export async function readScheduledTaskCommand(
112135env: GatewayServiceEnv,
113136): Promise<GatewayServiceCommandConfig | null> {
@@ -292,6 +315,27 @@ function buildStartupLauncherScript(params: { description?: string; scriptPath:
292315return `${lines.join("\r\n")}\r\n`;
293316}
294317318+function quoteVbsString(value: string): string {
319+return `"${value.replace(/"/g, '""')}"`;
320+}
321+322+function quoteVbsRunCommand(scriptPath: string): string {
323+return quoteVbsString(`"${scriptPath}"`);
324+}
325+326+function buildHiddenLauncherScript(params: { description?: string; scriptPath: string }): string {
327+const lines = [];
328+const trimmedDescription = params.description?.trim();
329+if (trimmedDescription) {
330+assertNoCmdLineBreak(trimmedDescription, "Hidden launcher description");
331+lines.push(`' ${trimmedDescription}`);
332+}
333+lines.push(
334+`CreateObject("WScript.Shell").Run ${quoteVbsRunCommand(params.scriptPath)}, 0, False`,
335+);
336+return `${lines.join("\r\n")}\r\n`;
337+}
338+295339async function assertSchtasksAvailable() {
296340const res = await execSchtasks(["/Query"]);
297341if (res.code === 0) {
@@ -302,12 +346,13 @@ async function assertSchtasksAvailable() {
302346}
303347304348async function isStartupEntryInstalled(env: GatewayServiceEnv): Promise<boolean> {
305-try {
306-await fs.access(resolveStartupEntryPath(env));
307-return true;
308-} catch {
309-return false;
349+for (const startupEntryPath of resolveStartupEntryPaths(env)) {
350+try {
351+ await fs.access(startupEntryPath);
352+ return true;
353+} catch {}
310354}
355+return false;
311356}
312357313358async function isRegisteredScheduledTask(env: GatewayServiceEnv): Promise<boolean> {
@@ -605,10 +650,12 @@ async function writeScheduledTaskScript({
605650 description,
606651}: Omit<GatewayServiceInstallArgs, "stdout">): Promise<{
607652scriptPath: string;
653+taskLaunchPath: string;
608654taskDescription: string;
609655}> {
610656await assertSchtasksAvailable().catch(() => undefined);
611657const scriptPath = resolveTaskScriptPath(env);
658+const taskLaunchPath = resolveTaskLauncherScriptPath(env, scriptPath);
612659await fs.mkdir(path.dirname(scriptPath), { recursive: true });
613660const taskDescription = resolveGatewayServiceDescription({ env, environment, description });
614661const script = buildTaskScript({
@@ -618,7 +665,14 @@ async function writeScheduledTaskScript({
618665 environment,
619666});
620667await fs.writeFile(scriptPath, script, "utf8");
621-return { scriptPath, taskDescription };
668+if (taskLaunchPath !== scriptPath) {
669+const launcher = buildHiddenLauncherScript({
670+description: taskDescription,
671+ scriptPath,
672+});
673+await fs.writeFile(taskLaunchPath, launcher, "utf8");
674+}
675+return { scriptPath, taskLaunchPath, taskDescription };
622676}
623677624678export async function stageScheduledTask({
@@ -636,7 +690,7 @@ async function updateExistingScheduledTask(params: {
636690env: GatewayServiceEnv;
637691stdout: NodeJS.WritableStream;
638692taskName: string;
639-quotedScript: string;
693+quotedLaunchPath: string;
640694scriptPath: string;
641695}): Promise<boolean> {
642696if (!(await isRegisteredScheduledTask(params.env))) {
@@ -647,7 +701,7 @@ async function updateExistingScheduledTask(params: {
647701"/TN",
648702params.taskName,
649703"/TR",
650-params.quotedScript,
704+params.quotedLaunchPath,
651705]);
652706if (change.code !== 0) {
653707return false;
@@ -820,14 +874,15 @@ async function activateScheduledTask(params: {
820874env: GatewayServiceEnv;
821875stdout: NodeJS.WritableStream;
822876scriptPath: string;
877+taskLaunchPath: string;
823878description?: string;
824879}) {
825880const taskDescription = params.description ?? "OpenClaw Gateway";
826881827882const taskName = resolveTaskName(params.env);
828-const quotedScript = quoteSchtasksArg(params.scriptPath);
883+const quotedLaunchPath = quoteSchtasksArg(params.taskLaunchPath);
829884830-if (await updateExistingScheduledTask({ ...params, taskName, quotedScript })) {
885+if (await updateExistingScheduledTask({ ...params, taskName, quotedLaunchPath })) {
831886return;
832887}
833888@@ -841,11 +896,12 @@ async function activateScheduledTask(params: {
841896"/TN",
842897taskName,
843898"/TR",
844-quotedScript,
899+quotedLaunchPath,
845900];
846901const taskUser = resolveTaskUser(params.env);
902+const taskUserArgs = taskUser ? ["/RU", taskUser, "/NP", "/IT"] : [];
847903let create = await execSchtasks(
848-taskUser ? [...baseArgs, "/RU", taskUser, "/NP", "/IT"] : baseArgs,
904+taskUserArgs.length > 0 ? [...baseArgs, ...taskUserArgs] : baseArgs,
849905);
850906if (create.code !== 0 && taskUser) {
851907create = await execSchtasks(baseArgs);
@@ -855,10 +911,15 @@ async function activateScheduledTask(params: {
855911if (shouldFallbackToStartupEntry({ code: create.code, detail })) {
856912const startupEntryPath = resolveStartupEntryPath(params.env);
857913await fs.mkdir(path.dirname(startupEntryPath), { recursive: true });
858-const launcher = buildStartupLauncherScript({
859-description: taskDescription,
860-scriptPath: params.scriptPath,
861-});
914+const launcher = shouldUseHiddenWindowsTaskLauncher(params.env)
915+ ? buildHiddenLauncherScript({
916+description: taskDescription,
917+scriptPath: params.scriptPath,
918+})
919+ : buildStartupLauncherScript({
920+description: taskDescription,
921+scriptPath: params.scriptPath,
922+});
862923await fs.writeFile(startupEntryPath, launcher, "utf8");
863924await launchFallbackTaskScript(params.env);
864925writeFormattedLines(
@@ -898,6 +959,7 @@ export async function installScheduledTask(
898959env: args.env,
899960stdout: args.stdout,
900961scriptPath: staged.scriptPath,
962+taskLaunchPath: staged.taskLaunchPath,
901963description: staged.taskDescription,
902964});
903965return { scriptPath: staged.scriptPath };
@@ -914,13 +976,21 @@ export async function uninstallScheduledTask({
914976await execSchtasks(["/Delete", "/F", "/TN", taskName]);
915977}
916978917-const startupEntryPath = resolveStartupEntryPath(env);
918-try {
919-await fs.unlink(startupEntryPath);
920-stdout.write(`${formatLine("Removed Windows login item", startupEntryPath)}\n`);
921-} catch {}
979+for (const startupEntryPath of resolveStartupEntryPaths(env)) {
980+try {
981+await fs.unlink(startupEntryPath);
982+stdout.write(`${formatLine("Removed Windows login item", startupEntryPath)}\n`);
983+} catch {}
984+}
922985923986const scriptPath = resolveTaskScriptPath(env);
987+const launcherPath = resolveTaskLauncherScriptPath(env, scriptPath);
988+if (launcherPath !== scriptPath) {
989+try {
990+await fs.unlink(launcherPath);
991+stdout.write(`${formatLine("Removed task launcher", launcherPath)}\n`);
992+} catch {}
993+}
924994try {
925995await fs.unlink(scriptPath);
926996stdout.write(`${formatLine("Removed task script", scriptPath)}\n`);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。