diff --git a/src/server/index.ts b/src/server/index.ts index 6c7e53f062..29409df1b6 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -501,6 +501,7 @@ function inspectStartupOwnership( currentHomes: ReturnType | null, statePaths: readonly string[] | null, windowsTaskListingCache?: ReturnType, + skipWindowsTaskListing = false, ): OwnershipInspection { try { if (currentHomes === null || statePaths === null) { @@ -510,9 +511,19 @@ function inspectStartupOwnership( }; } if (deps.inspectNativeCodexOwnership) { - return deps.inspectNativeCodexOwnership({ currentHomes, statePaths, windowsTaskListingCache }); + return deps.inspectNativeCodexOwnership({ + currentHomes, + statePaths, + windowsTaskListingCache, + skipWindowsTaskListing, + }); } - return inspectNativeCodexOwnership({ currentHomes, statePaths, windowsTaskListingCache }); + return inspectNativeCodexOwnership({ + currentHomes, + statePaths, + windowsTaskListingCache, + skipWindowsTaskListing, + }); } catch { return { ownership: "unknown", @@ -825,7 +836,9 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server> - & Pick, + & Pick, ): { registered: "present" | "absent" | "unknown"; registeredXml: string; @@ -659,6 +661,9 @@ function probeWindowsTaskRegistration( if (queried.status !== null && SCHTASKS_TASK_NOT_FOUND_EN.test(queryText)) { return { registered: "absent", registeredXml: "" }; } + if (deps.skipWindowsTaskListing) { + return { registered: "unknown", registeredXml: "" }; + } const runListing = () => deps.runRaw( schtasks, diff --git a/tests/codex-service-manager-probe-hardening.test.ts b/tests/codex-service-manager-probe-hardening.test.ts index 6044a389c5..670b5c3b5a 100644 --- a/tests/codex-service-manager-probe-hardening.test.ts +++ b/tests/codex-service-manager-probe-hardening.test.ts @@ -212,6 +212,29 @@ describe("Windows ownership probe hardening regressions", () => { expect(result.kind).toBe("unknown"); }); + test("a latency-sensitive ownership probe does not enumerate scheduled tasks", () => { + let fullListings = 0; + const result = inspectServiceManagerInstallation({ + platform: "win32", + home, + configDir, + windowsLocale: "zh-CN", + skipWindowsTaskListing: true, + runRaw: (file, args) => { + if (file.toLowerCase().endsWith("sc.exe")) return raw(1, "", "1060"); + if (args.includes("/xml")) { + return { status: 1, stdout: Buffer.alloc(0), stderr: GBK_TASK_NOT_FOUND, timedOut: false, spawnFailed: false }; + } + if (args.includes("/fo")) fullListings += 1; + return raw(0, ""); + }, + winswStatus: () => "nonexistent", + }); + + expect(result.kind).toBe("unknown"); + expect(fullListings).toBe(0); + }); + test("one startup keeps two targeted queries but shares one unchanged full listing (#2923)", async () => { const codexHome = join(home, "codex"); mkdirSync(codexHome, { recursive: true }); diff --git a/tests/native-profile-startup.test.ts b/tests/native-profile-startup.test.ts index 921796a6fd..8703743526 100644 --- a/tests/native-profile-startup.test.ts +++ b/tests/native-profile-startup.test.ts @@ -759,12 +759,14 @@ describe("an unknown service-ownership fence is retryable (#2108)", () => { const scopes: Array<{ currentHomes?: { codexHome: string; opencodexHome: string }; statePaths?: readonly string[]; + skipWindowsTaskListing?: boolean; }> = []; const server = startServer(0, { inspectNativeCodexOwnership: (scope = {}) => { scopes.push({ currentHomes: scope.currentHomes ? { ...scope.currentHomes } : undefined, statePaths: scope.statePaths ? [...scope.statePaths] : undefined, + skipWindowsTaskListing: scope.skipWindowsTaskListing, }); return { ownership: answer, reason: "pinned startup test" }; }, @@ -791,7 +793,12 @@ describe("an unknown service-ownership fence is retryable (#2108)", () => { opencodexHome: f.configDir, }); expect(firstScope.statePaths?.[0]).toBe(join(f.configDir, "service-state.json")); - for (const scope of scopes.slice(1)) expect(scope).toEqual(firstScope); + expect(scopes.slice(0, 2).every(scope => scope.skipWindowsTaskListing === false)).toBe(true); + expect(scopes.slice(2).every(scope => scope.skipWindowsTaskListing === true)).toBe(true); + for (const scope of scopes.slice(1)) { + expect(scope.currentHomes).toEqual(firstScope.currentHomes); + expect(scope.statePaths).toEqual(firstScope.statePaths); + } finishRecovery(); expect(await waitForNativeMainStartupGate()).toEqual({