@@ -92,8 +92,9 @@ function resolveStartupEntryPath(env: GatewayServiceEnv, extension?: "cmd" | "vb
9292
9393function resolveStartupEntryPaths ( env : GatewayServiceEnv ) : string [ ] {
9494 const primaryPath = resolveStartupEntryPath ( env ) ;
95- const legacyCmdPath = resolveStartupEntryPath ( env , "cmd" ) ;
96- return uniqueStrings ( [ primaryPath , legacyCmdPath ] ) ;
95+ const cmdPath = resolveStartupEntryPath ( env , "cmd" ) ;
96+ const hiddenPath = resolveStartupEntryPath ( env , "vbs" ) ;
97+ return uniqueStrings ( [ primaryPath , cmdPath , hiddenPath ] ) ;
9798}
9899
99100// `/TR` is parsed by schtasks itself, while the generated `gateway.cmd` line is parsed by cmd.exe.
@@ -462,6 +463,18 @@ async function isStartupEntryInstalled(env: GatewayServiceEnv): Promise<boolean>
462463 return false ;
463464}
464465
466+ async function removeStartupEntries (
467+ env : GatewayServiceEnv ,
468+ stdout ?: NodeJS . WritableStream ,
469+ ) : Promise < void > {
470+ for ( const startupEntryPath of resolveStartupEntryPaths ( env ) ) {
471+ try {
472+ await fs . unlink ( startupEntryPath ) ;
473+ stdout ?. write ( `${ formatLine ( "Removed Windows login item" , startupEntryPath ) } \n` ) ;
474+ } catch { }
475+ }
476+ }
477+
465478async function isRegisteredScheduledTask ( env : GatewayServiceEnv ) : Promise < boolean > {
466479 const taskName = resolveTaskName ( env ) ;
467480 const res = await execSchtasks ( [ "/Query" , "/TN" , taskName ] ) . catch ( ( ) => ( {
@@ -995,11 +1008,14 @@ async function updateExistingScheduledTask(params: {
9951008 } finally {
9961009 await fs . rm ( path . dirname ( upgradeXmlPath ) , { recursive : true , force : true } ) . catch ( ( ) => { } ) ;
9971010 }
998- await runScheduledTaskOrThrow ( {
1011+ const runResult = await runScheduledTaskOrThrow ( {
9991012 taskName : params . taskName ,
10001013 env : params . env ,
10011014 scriptPath : params . scriptPath ,
10021015 } ) ;
1016+ if ( runResult . taskStarted ) {
1017+ await removeStartupEntries ( params . env , params . stdout ) ;
1018+ }
10031019 writeFormattedLines (
10041020 params . stdout ,
10051021 [
@@ -1014,15 +1030,21 @@ async function updateExistingScheduledTask(params: {
10141030async function shouldFallbackScheduledTaskLaunch ( params : {
10151031 env : GatewayServiceEnv ;
10161032 scriptPath : string ;
1017- } ) : Promise < boolean > {
1033+ } ) : Promise < { shouldLaunchFallback : boolean ; taskStarted : boolean } > {
10181034 const readLaunchObservation = async ( ) : Promise < {
1019- state : "running" | "not-yet-run" | "other" ;
1035+ state : "confirmed-running" | " running" | "not-yet-run" | "other" ;
10201036 signature : string ;
10211037 } > => {
10221038 const runtime = await readScheduledTaskRuntime ( params . env ) . catch ( ( ) => null ) ;
10231039 if ( runtime ?. status === "running" ) {
1040+ const normalizedResult = normalizeTaskResultCode ( runtime . lastRunResult ) ;
1041+ const state = normalizedResult
1042+ ? RUNNING_RESULT_CODES . has ( normalizedResult )
1043+ ? "confirmed-running"
1044+ : "running"
1045+ : "running" ;
10241046 return {
1025- state : "running" ,
1047+ state,
10261048 signature : [ runtime . state , runtime . lastRunTime , runtime . lastRunResult , runtime . detail ]
10271049 . filter ( Boolean )
10281050 . join ( "|" ) ,
@@ -1104,39 +1126,54 @@ async function shouldFallbackScheduledTaskLaunch(params: {
11041126 } ;
11051127
11061128 const initial = await readLaunchObservation ( ) ;
1129+ if ( initial . state === "confirmed-running" ) {
1130+ return { shouldLaunchFallback : false , taskStarted : true } ;
1131+ }
11071132 if ( initial . state !== "not-yet-run" ) {
1108- return false ;
1133+ return { shouldLaunchFallback : false , taskStarted : false } ;
11091134 }
11101135
11111136 const deadline = Date . now ( ) + SCHEDULED_TASK_FALLBACK_TIMEOUT_MS ;
11121137 while ( Date . now ( ) < deadline ) {
11131138 await sleep ( SCHEDULED_TASK_FALLBACK_POLL_MS ) ;
11141139 const current = await readLaunchObservation ( ) ;
1140+ if ( current . state === "confirmed-running" ) {
1141+ return { shouldLaunchFallback : false , taskStarted : true } ;
1142+ }
1143+ if ( current . state === "running" ) {
1144+ return { shouldLaunchFallback : false , taskStarted : false } ;
1145+ }
11151146 if ( current . state !== "not-yet-run" ) {
1116- return false ;
1147+ return { shouldLaunchFallback : false , taskStarted : false } ;
11171148 }
11181149 if ( current . signature !== initial . signature ) {
1119- return false ;
1150+ return { shouldLaunchFallback : false , taskStarted : false } ;
11201151 }
11211152 }
1122- return ! ( await hasLaunchEvidence ( ) ) ;
1153+ return {
1154+ shouldLaunchFallback : ! ( await hasLaunchEvidence ( ) ) ,
1155+ taskStarted : false ,
1156+ } ;
11231157}
11241158
11251159async function runScheduledTaskOrThrow ( params : {
11261160 taskName : string ;
11271161 env : GatewayServiceEnv ;
11281162 scriptPath : string ;
1129- } ) : Promise < void > {
1163+ } ) : Promise < { fallbackLaunched : boolean ; taskStarted : boolean } > {
11301164 const run = await execSchtasks ( [ "/Run" , "/TN" , params . taskName ] ) ;
11311165 if ( run . code !== 0 ) {
11321166 throw new Error ( `schtasks run failed: ${ run . stderr || run . stdout } ` . trim ( ) ) ;
11331167 }
1134- if (
1135- ! ( await shouldFallbackScheduledTaskLaunch ( { env : params . env , scriptPath : params . scriptPath } ) )
1136- ) {
1137- return ;
1168+ const launchResult = await shouldFallbackScheduledTaskLaunch ( {
1169+ env : params . env ,
1170+ scriptPath : params . scriptPath ,
1171+ } ) ;
1172+ if ( ! launchResult . shouldLaunchFallback ) {
1173+ return { fallbackLaunched : false , taskStarted : launchResult . taskStarted } ;
11381174 }
11391175 await launchFallbackTaskScript ( params . env ) ;
1176+ return { fallbackLaunched : true , taskStarted : false } ;
11401177}
11411178
11421179async function activateScheduledTask ( params : {
@@ -1211,11 +1248,14 @@ async function activateScheduledTask(params: {
12111248 throw new Error ( `schtasks create failed: ${ detail } ` . trim ( ) ) ;
12121249 }
12131250
1214- await runScheduledTaskOrThrow ( {
1251+ const runResult = await runScheduledTaskOrThrow ( {
12151252 taskName,
12161253 env : params . env ,
12171254 scriptPath : params . scriptPath ,
12181255 } ) ;
1256+ if ( runResult . taskStarted ) {
1257+ await removeStartupEntries ( params . env , params . stdout ) ;
1258+ }
12191259 // Ensure we don't end up writing to a clack spinner line (wizards show progress without a newline).
12201260 writeFormattedLines (
12211261 params . stdout ,
@@ -1252,12 +1292,7 @@ export async function uninstallScheduledTask({
12521292 await execSchtasks ( [ "/Delete" , "/F" , "/TN" , taskName ] ) ;
12531293 }
12541294
1255- for ( const startupEntryPath of resolveStartupEntryPaths ( env ) ) {
1256- try {
1257- await fs . unlink ( startupEntryPath ) ;
1258- stdout . write ( `${ formatLine ( "Removed Windows login item" , startupEntryPath ) } \n` ) ;
1259- } catch { }
1260- }
1295+ await removeStartupEntries ( env , stdout ) ;
12611296
12621297 const scriptPath = resolveTaskScriptPath ( env ) ;
12631298 const launcherPath = resolveTaskLauncherScriptPath ( env , scriptPath ) ;
0 commit comments