diff --git a/tests/runner-e2e/chat-catalog-domains/runtime-provider-cleanup.ts b/tests/runner-e2e/chat-catalog-domains/runtime-provider-cleanup.ts index 1a63913a639..d6979db307d 100644 --- a/tests/runner-e2e/chat-catalog-domains/runtime-provider-cleanup.ts +++ b/tests/runner-e2e/chat-catalog-domains/runtime-provider-cleanup.ts @@ -38,7 +38,9 @@ async function providerIdentity(pid: number): Promise { if (!startedAt) throw new Error("missing start identity"); return { pid, parentPid: Number(parent), uid: Number(uid), startedAt, executable: canonical, digest: createHash("sha256").update(await readFile(canonical)).digest("hex"), appServer: /(?:^|\s)app-server(?=\s|$)/.test(command), - openCodeServe: /^opencode/.test(path.basename(canonical)) && args.includes("serve"), proxyScript: proxy, proxyDigest }; + // `opencode serve` runs either as its own binary or as node running the opencode launcher script. + openCodeServe: args.includes("serve") && (/^opencode/.test(path.basename(canonical)) + || args.slice(1, args.indexOf("serve")).some(arg => path.isAbsolute(arg) && /^opencode(?:\.[cm]?js)?$/.test(path.basename(arg)))), proxyScript: proxy, proxyDigest }; } catch { throw new Error("Provider cleanup fixture could not qualify the exact owned process; private diagnostics withheld"); } } /** The single qualifying provider among the verified runner's descendants (depth <= 4). The @@ -47,7 +49,7 @@ async function providerIdentity(pid: number): Promise { async function soleProviderDescendant(ctx: ChatCatalogContext, runnerPid: number, kind: "codex" | "opencode"): Promise<{ pid: number; parentPid: number } | undefined> { const rows = (await promisify(execFile)("/bin/ps", ["-A", "-o", "pid=,ppid="], { timeout: 5_000, maxBuffer: 1_048_576 })).stdout .split("\n").map(line => line.trim().split(/\s+/).map(Number)).filter(([pid, parent]) => Number.isSafeInteger(pid) && Number.isSafeInteger(parent)); - const seen: Array<{ depth: number; command: string | null; argCount: number | null; scripts: string[]; executable: string | null; proxy: boolean; appServer: boolean; openCodeServe: boolean }> = []; + const seen: Array<{ depth: number; command: string | null; argCount: number | null; scripts: string[]; hasServe: boolean; executable: string | null; proxy: boolean; appServer: boolean; openCodeServe: boolean }> = []; const providers: Array<{ pid: number; parentPid: number }> = []; let frontier = [runnerPid]; for (let depth = 1; depth <= 4 && frontier.length; depth += 1) { @@ -56,7 +58,7 @@ async function soleProviderDescendant(ctx: ChatCatalogContext, runnerPid: number const identity = await providerIdentity(pid).catch(() => undefined); const args = await promisify(execFile)("/bin/ps", ["-p", String(pid), "-o", "args="], { timeout: 5_000, maxBuffer: 65_536 }).then(out => out.stdout.trim().split(/\s+/), () => null); const command = await promisify(execFile)("/bin/ps", ["-p", String(pid), "-o", "comm="], { timeout: 5_000, maxBuffer: 65_536 }).then(out => path.basename(out.stdout.trim()) || null, () => null); - seen.push({ depth, command, argCount: args?.length ?? null, scripts: (args ?? []).filter(arg => /\.(?:c|m)?js$/.test(arg)).map(arg => path.basename(arg)), executable: identity ? path.basename(identity.executable) : null, proxy: !!identity?.proxyScript, appServer: !!identity?.appServer, openCodeServe: !!identity?.openCodeServe }); + seen.push({ depth, command, argCount: args?.length ?? null, scripts: (args ?? []).filter(arg => path.isAbsolute(arg)).map(arg => path.basename(arg)).slice(0, 8), hasServe: !!args?.includes("serve"), executable: identity ? path.basename(identity.executable) : null, proxy: !!identity?.proxyScript, appServer: !!identity?.appServer, openCodeServe: !!identity?.openCodeServe }); if (identity && (kind === "opencode" ? !!identity.proxyScript || identity.openCodeServe : identity.appServer)) providers.push({ pid, parentPid: parent }); } frontier = next.map(row => row.pid);