@@ -100,9 +100,10 @@ function resolveStartupEntryPath(env: GatewayServiceEnv, extension?: "cmd" | "vb
100100function resolveStartupEntryPaths ( env : GatewayServiceEnv ) : string [ ] {
101101 const primaryPath = resolveStartupEntryPath ( env ) ;
102102 const legacyCmdPath = resolveStartupEntryPath ( env , "cmd" ) ;
103- // Hidden VBS launchers supersede cmd launchers, but uninstall must remove the
104- // legacy cmd path from older installs too.
105- return uniqueStrings ( [ primaryPath , legacyCmdPath ] ) ;
103+ const hiddenLauncherPath = resolveStartupEntryPath ( env , "vbs" ) ;
104+ // Hidden VBS launchers supersede cmd launchers, but lifecycle operations must
105+ // discover both variants even when the caller env lacks the persisted marker.
106+ return uniqueStrings ( [ primaryPath , legacyCmdPath , hiddenLauncherPath ] ) ;
106107}
107108
108109// `/TR` is parsed by schtasks itself, while the generated `gateway.cmd` line is parsed by cmd.exe.
@@ -923,6 +924,70 @@ async function restartStartupEntry(
923924 return { outcome : "completed" } ;
924925}
925926
927+ const CALLER_OWNED_SERVICE_IDENTITY_KEYS = [
928+ "OPENCLAW_LAUNCHD_LABEL" ,
929+ "OPENCLAW_SYSTEMD_UNIT" ,
930+ "OPENCLAW_WINDOWS_TASK_NAME" ,
931+ ] as const ;
932+
933+ function resolveScheduledTaskRenderEnv (
934+ env : GatewayServiceEnv ,
935+ environment : GatewayServiceEnv | undefined ,
936+ ) : GatewayServiceEnv {
937+ if ( ! environment ) {
938+ return env ;
939+ }
940+ const merged = { ...env , ...environment } ;
941+ for ( const key of CALLER_OWNED_SERVICE_IDENTITY_KEYS ) {
942+ const value = env [ key ] ?. trim ( ) ;
943+ if ( value ) {
944+ merged [ key ] = value ;
945+ }
946+ }
947+ return merged ;
948+ }
949+
950+ function resolveScheduledTaskScriptEnvironment (
951+ taskEnv : GatewayServiceEnv ,
952+ environment : GatewayServiceEnv | undefined ,
953+ ) : GatewayServiceEnv | undefined {
954+ const scriptEnv = environment ? { ...environment } : { } ;
955+ for ( const key of CALLER_OWNED_SERVICE_IDENTITY_KEYS ) {
956+ const value = taskEnv [ key ] ?. trim ( ) ;
957+ if ( value ) {
958+ scriptEnv [ key ] = value ;
959+ }
960+ }
961+ return Object . keys ( scriptEnv ) . length > 0 ? scriptEnv : undefined ;
962+ }
963+
964+ const SCHEDULED_TASK_ACTIVATION_KEYS = [
965+ "OPENCLAW_WINDOWS_TASK_HIDDEN_LAUNCHER" ,
966+ "OPENCLAW_TASK_SCRIPT_NAME" ,
967+ "OPENCLAW_TASK_SCRIPT" ,
968+ "OPENCLAW_SERVICE_KIND" ,
969+ "OPENCLAW_GATEWAY_PORT" ,
970+ "OPENCLAW_STATE_DIR" ,
971+ "OPENCLAW_PROFILE" ,
972+ ] as const ;
973+
974+ function resolveScheduledTaskActivationEnv (
975+ env : GatewayServiceEnv ,
976+ environment : GatewayServiceEnv | undefined ,
977+ ) : GatewayServiceEnv {
978+ if ( ! environment ) {
979+ return env ;
980+ }
981+ const activationEnv = { ...env } ;
982+ for ( const key of SCHEDULED_TASK_ACTIVATION_KEYS ) {
983+ const value = environment [ key ] ;
984+ if ( value !== undefined ) {
985+ activationEnv [ key ] = value ;
986+ }
987+ }
988+ return activationEnv ;
989+ }
990+
926991async function writeScheduledTaskScript ( {
927992 env,
928993 programArguments,
@@ -933,17 +998,24 @@ async function writeScheduledTaskScript({
933998 scriptPath : string ;
934999 taskLaunchPath : string ;
9351000 taskDescription : string ;
1001+ taskEnv : GatewayServiceEnv ;
9361002} > {
9371003 await assertSchtasksAvailable ( ) . catch ( ( ) => undefined ) ;
938- const scriptPath = resolveTaskScriptPath ( env ) ;
939- const taskLaunchPath = resolveTaskLauncherScriptPath ( env , scriptPath ) ;
1004+ const taskEnv = resolveScheduledTaskRenderEnv ( env , environment ) ;
1005+ const scriptPath = resolveTaskScriptPath ( taskEnv ) ;
1006+ const taskLaunchPath = resolveTaskLauncherScriptPath ( taskEnv , scriptPath ) ;
9401007 await fs . mkdir ( path . dirname ( scriptPath ) , { recursive : true } ) ;
941- const taskDescription = resolveGatewayServiceDescription ( { env, environment, description } ) ;
1008+ const taskDescription = resolveGatewayServiceDescription ( {
1009+ env : taskEnv ,
1010+ environment,
1011+ description,
1012+ } ) ;
1013+ const scriptEnvironment = resolveScheduledTaskScriptEnvironment ( taskEnv , environment ) ;
9421014 const script = buildTaskScript ( {
9431015 description : taskDescription ,
9441016 programArguments,
9451017 workingDirectory,
946- environment,
1018+ environment : scriptEnvironment ,
9471019 } ) ;
9481020 await fs . writeFile ( scriptPath , script , "utf8" ) ;
9491021 if ( taskLaunchPath !== scriptPath ) {
@@ -953,7 +1025,7 @@ async function writeScheduledTaskScript({
9531025 } ) ;
9541026 await fs . writeFile ( taskLaunchPath , launcher , "utf8" ) ;
9551027 }
956- return { scriptPath, taskLaunchPath, taskDescription } ;
1028+ return { scriptPath, taskLaunchPath, taskDescription, taskEnv } ;
9571029}
9581030
9591031export async function stageScheduledTask ( {
@@ -1243,7 +1315,7 @@ export async function installScheduledTask(
12431315) : Promise < { scriptPath : string } > {
12441316 const staged = await writeScheduledTaskScript ( args ) ;
12451317 await activateScheduledTask ( {
1246- env : args . env ,
1318+ env : resolveScheduledTaskActivationEnv ( args . env , args . environment ) ,
12471319 stdout : args . stdout ,
12481320 scriptPath : staged . scriptPath ,
12491321 taskLaunchPath : staged . taskLaunchPath ,
@@ -1271,8 +1343,15 @@ export async function uninstallScheduledTask({
12711343 }
12721344
12731345 const scriptPath = resolveTaskScriptPath ( env ) ;
1274- const launcherPath = resolveTaskLauncherScriptPath ( env , scriptPath ) ;
1275- if ( launcherPath !== scriptPath ) {
1346+ const parsedScriptPath = path . parse ( scriptPath ) ;
1347+ const launcherPaths = uniqueStrings ( [
1348+ resolveTaskLauncherScriptPath ( env , scriptPath ) ,
1349+ path . join ( parsedScriptPath . dir , `${ parsedScriptPath . name } .vbs` ) ,
1350+ ] ) ;
1351+ for ( const launcherPath of launcherPaths ) {
1352+ if ( launcherPath === scriptPath ) {
1353+ continue ;
1354+ }
12761355 try {
12771356 await fs . unlink ( launcherPath ) ;
12781357 stdout . write ( `${ formatLine ( "Removed task launcher" , launcherPath ) } \n` ) ;
0 commit comments