{"version":3,"file":"guiCommand.cjs","names":["os","z","parseArgs","readProblemMarkdownFrontMatter","judgeByStaticAnalysis","findEntryPointFile","DecisionCode","findLanguageDefinitionByPath","runCustomRunner","getSandboxUserEnvOverrides","snapshotWorkingDirectory","copyTestCaseFileInput","wrapCommandWithSandboxUser","cleanWorkingDirectory","readOutputFiles","sandboxUserName","path","languageIdToDefinition","spawnSyncWithTimeout","readFileTestCases","TIMEOUT_COMMAND","startSandboxTimeoutWatchdog","childProcess","wait","getTrustedHelperEnv","fs"],"sources":["../../src/presets/guiCommand.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { setTimeout as wait } from 'node:timers/promises';\n\nimport { z } from 'zod';\n\nimport { cleanWorkingDirectory, snapshotWorkingDirectory } from '../helpers/cleanWorkingDirectory.js';\nimport { copyTestCaseFileInput } from '../helpers/copyTestCaseFileInput.js';\nimport { findEntryPointFile } from '../helpers/findEntryPointFile.js';\nimport { findLanguageDefinitionByPath } from '../helpers/findLanguageDefinitionByPath.js';\nimport { judgeByStaticAnalysis } from '../helpers/judgeByStaticAnalysis.js';\nimport { parseArgs } from '../helpers/parseArgs.js';\nimport { printTestCaseResult } from '../helpers/printTestCaseResult.js';\nimport { readOutputFiles } from '../helpers/readOutputFiles.js';\nimport { readProblemMarkdownFrontMatter } from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases as readFileTestCases } from '../helpers/readTestCases.js';\nimport { runCustomRunner } from '../helpers/runCustomRunner.js';\nimport {\n  getSandboxUserEnvOverrides,\n  getTrustedHelperEnv,\n  killSandboxUserProcesses,\n  makeAccessibleToSandboxUser,\n  sandboxUserName,\n  startSandboxTimeoutWatchdog,\n  TIMEOUT_COMMAND,\n  wrapCommandWithSandboxUser,\n} from '../helpers/sandboxUser.js';\nimport { spawnSyncWithTimeout } from '../helpers/spawnSyncWithTimeout.js';\nimport { DecisionCode } from '../types/decisionCode.js';\nimport { languageIdToDefinition } from '../types/language.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nconst BUILD_TIMEOUT_SECONDS = 10;\nconst JUDGE_DEFAULT_TIMEOUT_SECONDS = 5;\nconst SCREENSHOT_WAIT_SECONDS = 0.3;\nconst XVFB_STARTUP_WAIT_SECONDS = 0.3;\nconst XVFB_SHUTDOWN_WAIT_SECONDS = 0.1;\nconst PROCESS_SHUTDOWN_WAIT_SECONDS = 0.2;\nconst STOP_DETECTION_THRESHOLD = 5;\nconst TIME_COMMAND = [os.platform() === 'darwin' ? 'gtime' : '/usr/bin/time', '--format', '%e %M'] as const;\n\nconst judgeParamsSchema = z.object({\n  language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\ninterface BaseGuiTestCase {\n  id: string;\n  input?: string;\n  fileInputPath?: string;\n}\n\nexport interface GuiScreenshotFile {\n  path: string;\n  data: string;\n  encoding: 'base64';\n}\n\nexport interface GuiCommandRunResult {\n  stdin: string;\n  stdout: string;\n  stderr: string;\n  status: number | undefined;\n  timeSeconds: number;\n  memoryBytes: number;\n  screenshots: GuiScreenshotFile[];\n  stopReason: 'process_exit' | 'stable_screenshot' | 'timeout';\n}\n\ninterface GuiJudgeContext {\n  timeLimitSeconds: number;\n  problemMarkdownFrontMatter: Pick<ProblemMarkdownFrontMatter, 'memoryLimitByte' | 'requiredOutputFilePaths'>;\n}\n\ntype GuiJudgeCaseResult = Pick<\n  TestCaseResult,\n  'decisionCode' | 'feedbackMarkdown' | 'stderr' | 'stdout' | 'outputFiles'\n>;\n\nexport interface GuiCommandJudgePresetOptions<TTestCase extends BaseGuiTestCase = BaseGuiTestCase> {\n  mainFilePath?: string;\n  runTimeoutSeconds?: number;\n  screenshotWaitSeconds?: number;\n  stopDetectionThreshold?: number;\n  readTestCases?: (problemDir: string) => Promise<readonly TTestCase[]>;\n  prepare?: (context: {\n    cwd: string;\n    /**\n     * Build the submission with it. Under `EXERCODE_SANDBOX_USER` delegation it already carries the\n     * sandbox user's overrides, but the handler must still wrap whatever it spawns with\n     * `wrapCommandWithSandboxUser` (both exported): a build runs the submission's own scripts\n     * (`package.json` lifecycle scripts, `build.gradle`, `build.rs`), which as the trusted harness\n     * user could read the problem's test cases and rewrite the harness. The preset terminates\n     * leftover sandbox processes after the handler returns.\n     */\n    env: NodeJS.ProcessEnv;\n    mainFilePath: string;\n    problemMarkdownFrontMatter: ProblemMarkdownFrontMatter;\n  }) => Promise<Partial<GuiJudgeCaseResult> | undefined> | Partial<GuiJudgeCaseResult> | undefined;\n  /**\n   * Runs as the trusted harness user with the submission's `cwd`. Fixture files it creates there\n   * must be written with `createDirectoryWithoutFollowingSymlinks`/`writeFileWithoutFollowingSymlinks`\n   * (both exported): a submission can plant a symlink at a fixture path, and a plain `fs.writeFile`\n   * would follow it into a file only the harness can write.\n   */\n  resolveInput?: (context: { testCase: TTestCase; cwd: string; env: NodeJS.ProcessEnv }) => Promise<string> | string;\n  command?: (context: {\n    testCase: TTestCase;\n    cwd: string;\n    env: NodeJS.ProcessEnv;\n    mainFilePath: string;\n  }) => Promise<readonly [string, ...string[]]> | readonly [string, ...string[]];\n  runCommand?: (context: {\n    testCase: TTestCase;\n    /**\n     * The command to run. Under `EXERCODE_SANDBOX_USER` delegation it is already wrapped so it\n     * executes as the sandbox user; spawn it as given (with the supplied `env`) instead of\n     * reconstructing it, or the submission runs as the trusted harness user.\n     *\n     * Its direct child is then a root-owned `sudo` whose descendants belong to the sandbox user, so\n     * the handler cannot signal them: enforce `timeLimitSeconds` with `startSandboxTimeoutWatchdog`\n     * and `killSandboxUserProcesses` (both exported) rather than `child.kill()` or an outer\n     * `timeout`. The preset terminates leftover sandbox processes after the handler returns.\n     */\n    command: readonly [string, ...string[]];\n    stdin: string;\n    cwd: string;\n    env: NodeJS.ProcessEnv;\n    timeLimitSeconds: number;\n    screenshotWaitSeconds: number;\n    stopDetectionThreshold: number;\n  }) => Promise<GuiCommandRunResult> | GuiCommandRunResult;\n  test: (context: {\n    testCase: TTestCase;\n    runResult: GuiCommandRunResult;\n    outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n    context: GuiJudgeContext;\n  }) => Promise<Partial<GuiJudgeCaseResult>> | Partial<GuiJudgeCaseResult>;\n}\n\n/**\n * A preset function for judging GUI programs by collecting screenshots while the program runs.\n *\n * Keep problem-specific logic in `prepare`, `command`, and `test`.\n *\n * @example\n * Create `judge.ts`:\n * ```ts\n * import { DecisionCode } from '@exercode/problem-utils';\n * import { guiCommandJudgePreset } from '@exercode/problem-utils/presets/guiCommand';\n *\n * await guiCommandJudgePreset(import.meta.dirname, {\n *   mainFilePath: 'Main.java',\n *   readTestCases: async () => [{ id: 'default' }],\n *   test: ({ runResult }) => {\n *     return runResult.screenshots.length > 0\n *       ? { decisionCode: DecisionCode.ACCEPTED }\n *       : { decisionCode: DecisionCode.WRONG_ANSWER };\n *   },\n * });\n * ```\n */\nexport async function guiCommandJudgePreset<TTestCase extends BaseGuiTestCase = BaseGuiTestCase>(\n  problemDir: string,\n  options: GuiCommandJudgePresetOptions<TTestCase>\n): Promise<void> {\n  const args = parseArgs(process.argv);\n  if (!args.cwd) throw new Error('cwd argument required');\n  const params = judgeParamsSchema.parse(args.params);\n  const submissionDir = args.cwd;\n\n  // The sandboxed submission must read its sources and write build/run outputs in its directory.\n  makeAccessibleToSandboxUser(submissionDir);\n\n  const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n  const configuredTestCases = await (options.readTestCases ?? readGuiTestCases<TTestCase>)(problemDir);\n  const testCases =\n    configuredTestCases.length > 0 ? configuredTestCases : ([{ id: 'default' }] as unknown as readonly TTestCase[]);\n  const prebuildTestCaseId = testCases[0]?.id ?? 'prebuild';\n\n  const staticAnalysisResult = await judgeByStaticAnalysis(args.cwd, problemMarkdownFrontMatter);\n  if (staticAnalysisResult) {\n    printTestCaseResult({ testCaseId: prebuildTestCaseId, ...staticAnalysisResult });\n    return;\n  }\n\n  const initialMainFilePath = options.mainFilePath ?? (await findEntryPointFile(args.cwd, params.language));\n  if (!initialMainFilePath) {\n    printTestCaseResult({\n      testCaseId: prebuildTestCaseId,\n      decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n      stderr: options.mainFilePath\n        ? `required main file not found: ${options.mainFilePath}`\n        : `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n    });\n    return;\n  }\n\n  const languageDefinition = findLanguageDefinitionByPath(initialMainFilePath);\n  if (!languageDefinition) {\n    printTestCaseResult({\n      testCaseId: prebuildTestCaseId,\n      decisionCode: DecisionCode.WRONG_ANSWER,\n      stderr: 'unsupported language',\n    });\n    return;\n  }\n\n  const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n  let resolvedMainFilePath = await resolveMainFilePath({\n    cwd: args.cwd,\n    language: params.language,\n    configuredMainFilePath: options.mainFilePath,\n  });\n  if (languageDefinition.prebuild) {\n    try {\n      await languageDefinition.prebuild(args.cwd);\n      const prebuiltMainFilePath = await resolveMainFilePath({\n        cwd: args.cwd,\n        language: params.language ?? inferLanguageIdsByPath(initialMainFilePath),\n        configuredMainFilePath: options.mainFilePath,\n        allowConfiguredPathFallback: true,\n      });\n      if (prebuiltMainFilePath) resolvedMainFilePath = prebuiltMainFilePath;\n    } catch (error) {\n      printTestCaseResult({\n        testCaseId: prebuildTestCaseId,\n        decisionCode: DecisionCode.BUILD_ERROR,\n        stderr: errorToMessage(error),\n      });\n      return;\n    }\n  }\n  if (!resolvedMainFilePath) {\n    printTestCaseResult({\n      testCaseId: prebuildTestCaseId,\n      decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n      stderr: options.mainFilePath\n        ? `required main file not found: ${options.mainFilePath}`\n        : `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n    });\n    return;\n  }\n\n  let customPrepareResult: Partial<GuiJudgeCaseResult> | undefined;\n  if (options.prepare) {\n    try {\n      customPrepareResult = await runCustomRunner(() =>\n        options.prepare?.({\n          cwd: submissionDir,\n          env: { ...env, ...getSandboxUserEnvOverrides(env) },\n          mainFilePath: resolvedMainFilePath,\n          problemMarkdownFrontMatter,\n        })\n      );\n    } catch (error) {\n      // Like the `prebuild` step above: report the failed build rather than letting the throw (from\n      // the handler itself, or from the fail-closed sandbox sweep around it) end the run resultless.\n      printTestCaseResult({\n        testCaseId: prebuildTestCaseId,\n        decisionCode: DecisionCode.BUILD_ERROR,\n        stderr: errorToMessage(error),\n      });\n      return;\n    }\n  }\n  const prepareResult =\n    customPrepareResult ??\n    runDefaultPrepare({\n      cwd: args.cwd,\n      env,\n      mainFilePath: resolvedMainFilePath,\n      languageDefinition,\n    });\n  if (prepareResult) {\n    printTestCaseResult({\n      testCaseId: prebuildTestCaseId,\n      decisionCode: prepareResult.decisionCode ?? DecisionCode.BUILD_ERROR,\n      feedbackMarkdown: prepareResult.feedbackMarkdown,\n      stderr: prepareResult.stderr,\n      stdout: prepareResult.stdout,\n      outputFiles: prepareResult.outputFiles,\n    });\n    return;\n  }\n\n  const cwdSnapshot = await snapshotWorkingDirectory(args.cwd);\n  let displayServer: Awaited<ReturnType<typeof ensureDisplayServer>> | undefined;\n  let currentTestCaseId = prebuildTestCaseId;\n  let currentStdin: string | undefined;\n  try {\n    displayServer = options.runCommand ? undefined : await ensureDisplayServer();\n    const sharedFileInputPath = (configuredTestCases as { shared?: { fileInputPath?: string } }).shared?.fileInputPath;\n    for (const testCase of testCases) {\n      currentTestCaseId = testCase.id;\n      if (sharedFileInputPath) await copyTestCaseFileInput(sharedFileInputPath, args.cwd);\n      if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, args.cwd);\n\n      const timeLimitSeconds =\n        typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n          ? problemMarkdownFrontMatter.timeLimitMs / 1000\n          : (options.runTimeoutSeconds ?? JUDGE_DEFAULT_TIMEOUT_SECONDS);\n\n      const runEnv = displayServer ? { ...env, DISPLAY: displayServer.display } : env;\n      const stdin = (await options.resolveInput?.({ testCase, cwd: args.cwd, env: runEnv })) ?? testCase.input ?? '';\n      currentStdin = stdin;\n      const command =\n        (await options.command?.({ testCase, cwd: args.cwd, env: runEnv, mainFilePath: resolvedMainFilePath })) ??\n        languageDefinition.command(resolvedMainFilePath);\n\n      let runResult: GuiCommandRunResult;\n      try {\n        runResult = options.runCommand\n          ? await runCustomRunner(\n              () =>\n                // Hand custom runners a command that is already sandbox-wrapped, and the matching\n                // environment: they spawn it themselves, so an unwrapped command would run the\n                // submission as the trusted harness user and defeat the delegation boundary.\n                options.runCommand?.({\n                  testCase,\n                  command: wrapCommandWithSandboxUser(command),\n                  stdin,\n                  cwd: submissionDir,\n                  env: { ...runEnv, ...getSandboxUserEnvOverrides(runEnv) },\n                  timeLimitSeconds,\n                  screenshotWaitSeconds: options.screenshotWaitSeconds ?? SCREENSHOT_WAIT_SECONDS,\n                  stopDetectionThreshold: options.stopDetectionThreshold ?? STOP_DETECTION_THRESHOLD,\n                }) as Promise<GuiCommandRunResult>\n            )\n          : await spawnGuiProgram({\n              command,\n              stdin,\n              cwd: args.cwd,\n              env: runEnv,\n              timeLimitSeconds,\n              screenshotWaitSeconds: options.screenshotWaitSeconds ?? SCREENSHOT_WAIT_SECONDS,\n              stopDetectionThreshold: options.stopDetectionThreshold ?? STOP_DETECTION_THRESHOLD,\n            });\n      } catch (error) {\n        printTestCaseResult({\n          testCaseId: testCase.id,\n          decisionCode: DecisionCode.RUNTIME_ERROR,\n          stdin,\n          stderr: errorToMessage(error),\n        });\n        await cleanWorkingDirectory(args.cwd, cwdSnapshot);\n        return;\n      }\n\n      const outputFiles = await readOutputFiles(args.cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n      const judgeContext: GuiJudgeContext = {\n        timeLimitSeconds,\n        problemMarkdownFrontMatter: {\n          memoryLimitByte: problemMarkdownFrontMatter.memoryLimitByte,\n          requiredOutputFilePaths: problemMarkdownFrontMatter.requiredOutputFilePaths,\n        },\n      };\n      const baseJudgeResult = evaluateGuiRunResult({ runResult, outputFiles, context: judgeContext });\n      let judgeResult = baseJudgeResult;\n      if (baseJudgeResult.decisionCode === DecisionCode.ACCEPTED) {\n        try {\n          const extendedJudgeResult = await options.test({\n            testCase,\n            runResult,\n            outputFiles,\n            context: judgeContext,\n          });\n          judgeResult = {\n            decisionCode: extendedJudgeResult.decisionCode ?? baseJudgeResult.decisionCode,\n            feedbackMarkdown: extendedJudgeResult.feedbackMarkdown,\n            stderr: extendedJudgeResult.stderr,\n            stdout: extendedJudgeResult.stdout,\n            outputFiles: extendedJudgeResult.outputFiles,\n          };\n        } catch (error) {\n          judgeResult = {\n            decisionCode: DecisionCode.RUNTIME_ERROR,\n            stderr: errorToMessage(error),\n          };\n        }\n      }\n\n      const decisionCode = judgeResult.decisionCode ?? DecisionCode.ACCEPTED;\n      const stdout = judgeResult.stdout ?? runResult.stdout;\n      const stderr = judgeResult.stderr ?? runResult.stderr;\n      printTestCaseResult({\n        testCaseId: testCase.id,\n        decisionCode,\n        exitStatus: runResult.status,\n        stdin: runResult.stdin || undefined,\n        stdout: stdout || undefined,\n        stderr: stderr || undefined,\n        timeSeconds: runResult.timeSeconds,\n        memoryBytes: runResult.memoryBytes,\n        feedbackMarkdown: judgeResult.feedbackMarkdown,\n        outputFiles: judgeResult.outputFiles ?? (outputFiles.length > 0 ? outputFiles : undefined),\n      });\n\n      await cleanWorkingDirectory(args.cwd, cwdSnapshot);\n      if (decisionCode !== DecisionCode.ACCEPTED) break;\n    }\n  } catch (error) {\n    printTestCaseResult({\n      testCaseId: currentTestCaseId,\n      decisionCode: DecisionCode.RUNTIME_ERROR,\n      stdin: currentStdin,\n      stderr: errorToMessage(error),\n    });\n    await cleanWorkingDirectory(args.cwd, cwdSnapshot);\n  } finally {\n    try {\n      // Sweep any sandbox processes the run left behind (e.g. a SIGTERM-ignoring forked child) so\n      // they cannot race the harness's output reads or survive into the next test case/request.\n      if (sandboxUserName) killSandboxUserProcesses();\n    } finally {\n      // Must run even when the sweep fails closed, or Xvfb keeps holding its display number and\n      // later GUI requests exhaust the `:90`–`:99` range.\n      await displayServer?.dispose();\n    }\n  }\n}\n\nasync function resolveMainFilePath(context: {\n  cwd: string;\n  language?: string | string[];\n  configuredMainFilePath?: string;\n  allowConfiguredPathFallback?: boolean;\n}): Promise<string | undefined> {\n  if (context.configuredMainFilePath) {\n    const resolvedPath = path.join(context.cwd, context.configuredMainFilePath);\n    if (await pathExists(resolvedPath)) return context.configuredMainFilePath;\n    if (!context.allowConfiguredPathFallback) return undefined;\n  }\n\n  return await findEntryPointFile(context.cwd, context.language);\n}\n\nfunction inferLanguageIdsByPath(filePath: string): string[] | undefined {\n  const languageIds = Object.entries(languageIdToDefinition)\n    .filter(([, definition]) => definition.fileExtensions.some((ext) => filePath.endsWith(ext)))\n    .map(([languageId]) => languageId);\n  return languageIds.length > 0 ? languageIds : undefined;\n}\n\nfunction runDefaultPrepare(context: {\n  cwd: string;\n  env: NodeJS.ProcessEnv;\n  mainFilePath: string;\n  languageDefinition: NonNullable<ReturnType<typeof findLanguageDefinitionByPath>>;\n}): Partial<GuiJudgeCaseResult> | undefined {\n  const buildCommand = context.languageDefinition.buildCommand?.(context.mainFilePath);\n  if (!buildCommand) return undefined;\n\n  const buildResult = spawnSyncWithTimeout(\n    buildCommand[0],\n    buildCommand.slice(1),\n    { cwd: context.cwd, encoding: 'utf8', env: context.env },\n    BUILD_TIMEOUT_SECONDS\n  );\n\n  if (buildResult.timeSeconds > BUILD_TIMEOUT_SECONDS) {\n    return {\n      decisionCode: DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,\n      stderr: buildResult.stderr || undefined,\n    };\n  }\n\n  if (buildResult.status !== 0) {\n    return {\n      decisionCode: DecisionCode.BUILD_ERROR,\n      stderr: buildResult.stderr || buildResult.stdout || undefined,\n    };\n  }\n\n  return undefined;\n}\n\nfunction evaluateGuiRunResult(context: {\n  runResult: GuiCommandRunResult;\n  outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n  context: GuiJudgeContext;\n}): Partial<GuiJudgeCaseResult> {\n  if (context.runResult.stopReason === 'timeout') {\n    return {\n      decisionCode: DecisionCode.TIME_LIMIT_EXCEEDED,\n      stderr: context.runResult.stderr,\n    };\n  }\n\n  if (context.runResult.status !== 0) {\n    return {\n      decisionCode: DecisionCode.RUNTIME_ERROR,\n      stderr: context.runResult.stderr,\n    };\n  }\n\n  if (\n    context.runResult.memoryBytes >\n    (context.context.problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)\n  ) {\n    return {\n      decisionCode: DecisionCode.MEMORY_LIMIT_EXCEEDED,\n      stderr: context.runResult.stderr,\n    };\n  }\n\n  const requiredOutputFilesCount = context.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0;\n  if (context.outputFiles.length < requiredOutputFilesCount) {\n    return {\n      decisionCode: DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR,\n    };\n  }\n\n  return { decisionCode: DecisionCode.ACCEPTED };\n}\n\nasync function readGuiTestCases<TTestCase extends BaseGuiTestCase>(problemDir: string): Promise<readonly TTestCase[]> {\n  return (await readFileTestCases(path.join(problemDir, 'test_cases'))) as unknown as readonly TTestCase[];\n}\n\nasync function spawnGuiProgram(context: {\n  command: readonly [string, ...string[]];\n  stdin: string;\n  cwd: string;\n  env: NodeJS.ProcessEnv;\n  timeLimitSeconds: number;\n  screenshotWaitSeconds: number;\n  stopDetectionThreshold: number;\n}): Promise<GuiCommandRunResult> {\n  const wrappedCommand = wrapCommandWithSandboxUser([\n    TIMEOUT_COMMAND,\n    context.timeLimitSeconds.toFixed(3),\n    ...TIME_COMMAND,\n    ...context.command,\n  ]);\n  // A submission can signal its own sandboxed `timeout` and then wedge the event loop (e.g.\n  // XGrabServer makes the synchronous `xwininfo`/`maim` block), so a harness-owned watchdog\n  // force-kills the sandbox user at the deadline; killing the client also releases its X grab.\n  // Armed before the submission starts: a submission that exhausts the PID cgroup would otherwise\n  // prevent the watchdog from spawning at all.\n  const watchdog = startSandboxTimeoutWatchdog(context.timeLimitSeconds);\n  let child: childProcess.ChildProcessWithoutNullStreams;\n  try {\n    child = childProcess.spawn(wrappedCommand[0], wrappedCommand.slice(1), {\n      cwd: context.cwd,\n      env: { ...context.env, ...getSandboxUserEnvOverrides(context.env) },\n      detached: process.platform !== 'win32',\n      stdio: ['pipe', 'pipe', 'pipe'],\n    });\n  } catch (error) {\n    watchdog.cancel();\n    throw error;\n  }\n\n  child.stdout.setEncoding('utf8');\n  child.stderr.setEncoding('utf8');\n\n  let stdout = '';\n  let stderr = '';\n  let exitCode: number | undefined;\n  let spawnError: Error | undefined;\n  let stopReason: GuiCommandRunResult['stopReason'] = 'process_exit';\n  let submissionStopped = false;\n  const screenshotSignaturesHistory: string[][] = [];\n  let screenshots: GuiScreenshotFile[] = [];\n  const startTimeSeconds = Date.now() / 1000;\n  let sampledMemoryBytes = 0;\n  child.stdout.on('data', (chunk: string) => {\n    stdout += chunk;\n  });\n  child.stderr.on('data', (chunk: string) => {\n    stderr += chunk;\n  });\n  child.on('error', (error) => {\n    spawnError = error;\n    exitCode = 1;\n  });\n  // A child that exits before the input is fully written makes the write fail; without a listener\n  // that EPIPE would be an unhandled stream error terminating the harness mid-run.\n  child.stdin.on('error', () => {\n    // The submission simply stopped reading its input; the exit handling below reports the result.\n  });\n  child.on('close', (code, signal) => {\n    if (code === 124) {\n      stopReason = 'timeout';\n      exitCode = 0;\n      return;\n    }\n    if (signal) {\n      exitCode = 1;\n      stderr = stderr\n        ? `${stderr}\\nprocess terminated by signal: ${signal}`\n        : `process terminated by signal: ${signal}`;\n      return;\n    }\n    exitCode = code ?? 1;\n  });\n\n  // Everything below must run under the `finally` that decides the watchdog's fate: it is cancelled\n  // only once the submission is demonstrably stopped. When a helper throws first — `takeScreenshots`\n  // rethrows spawn failures, which a submission can provoke by exhausting PIDs — the watchdog stays\n  // armed on purpose, since nothing else would end the submission. The cost is that a detached\n  // watchdog can outlive this harness and SIGKILL sandbox processes of a request that starts within\n  // its remaining deadline, which is the same trade-off the package-manager runner accepts.\n  try {\n    if (context.stdin) child.stdin.write(context.stdin);\n    child.stdin.end();\n\n    while (exitCode === undefined) {\n      await wait(context.screenshotWaitSeconds * 1000);\n      sampledMemoryBytes = Math.max(sampledMemoryBytes, readProcessGroupMemoryBytes(child.pid));\n      const currentScreenshots = takeScreenshots(context.env.DISPLAY);\n      screenshots = currentScreenshots.toSorted((a, b) => a.data.length - b.data.length);\n\n      if (screenshots.length > 0) {\n        const screenshotSignatures = screenshots.map((file) => file.data).toSorted();\n        screenshotSignaturesHistory.unshift(screenshotSignatures);\n        screenshotSignaturesHistory.length = Math.min(\n          screenshotSignaturesHistory.length,\n          context.stopDetectionThreshold\n        );\n        if (\n          screenshotSignaturesHistory.length === context.stopDetectionThreshold &&\n          screenshotSignaturesHistory.every(\n            (files) =>\n              files.length === screenshotSignatures.length &&\n              files.every((file, index) => file === screenshotSignatures[index])\n          )\n        ) {\n          stopReason = 'stable_screenshot';\n          exitCode = 0;\n          break;\n        }\n      }\n\n      if (Date.now() / 1000 - startTimeSeconds > context.timeLimitSeconds) {\n        stopReason = 'timeout';\n        exitCode = 0;\n        break;\n      }\n    }\n\n    if (stopReason !== 'process_exit' && child.exitCode === null) {\n      child.removeAllListeners('close');\n      child.removeAllListeners('error');\n    }\n    // A submission can kill its own same-UID `timeout` and be stopped by the watchdog instead,\n    // which surfaces as a SIGKILL rather than a deadline. Report that as the timeout it is, not as\n    // the runtime error the signal would otherwise imply — but only when the child's fate is what\n    // decided the verdict. A `stable_screenshot` (or the loop's own `timeout`) was already decided\n    // from the screenshots, so the watchdog must not overwrite it.\n    const watchdogFired = stopReason === 'process_exit' && watchdog.fired();\n    await stopProcess(child);\n    submissionStopped = true;\n    if (spawnError) throw spawnError;\n    const {\n      memoryBytes,\n      stderr: normalizedStderr,\n      timeSeconds,\n    } = parseTimedStderr(stderr, startTimeSeconds, sampledMemoryBytes);\n\n    return {\n      stdin: context.stdin,\n      stdout: stdout.trimEnd(),\n      stderr: normalizedStderr,\n      status: watchdogFired ? 0 : exitCode,\n      timeSeconds,\n      memoryBytes,\n      screenshots,\n      stopReason: watchdogFired ? 'timeout' : stopReason,\n    };\n  } finally {\n    // Only once the submission is demonstrably stopped: if termination itself failed (or never ran\n    // because a helper threw), the watchdog is the only thing left able to end it, and cancelling\n    // would leave it running unchecked.\n    if (submissionStopped) watchdog.cancel();\n  }\n}\n\nfunction takeScreenshots(display: string | undefined): GuiScreenshotFile[] {\n  // These helpers run as the trusted harness user, so they must not be resolved through a\n  // submission-influenced `PATH` (see `getTrustedHelperEnv`).\n  const env = getTrustedHelperEnv(display ? { DISPLAY: display } : { DISPLAY: process.env.DISPLAY });\n  const xwininfo = childProcess.spawnSync('xwininfo', ['-root', '-tree'], { encoding: 'utf8', env });\n  if (xwininfo.error) throw xwininfo.error;\n  if (xwininfo.status !== 0 || !xwininfo.stdout) return [];\n\n  const screenshots: GuiScreenshotFile[] = [];\n  for (const windowId of extractTopLevelWindowIds(xwininfo.stdout)) {\n    const screenshot = childProcess.spawnSync('maim', ['-i', windowId], { env });\n    if (screenshot.error) throw screenshot.error;\n    if (screenshot.status !== 0 || screenshot.stdout.length === 0) continue;\n\n    const windowNameResult = childProcess.spawnSync('xdotool', ['getwindowname', windowId], { encoding: 'utf8', env });\n    if (windowNameResult.error) throw windowNameResult.error;\n    const windowName = windowNameResult.stdout.trim().replaceAll(/[\\s/]/g, '_');\n\n    screenshots.push({\n      path: `${windowName || 'window'}_${windowId}.png`,\n      data: screenshot.stdout.toString('base64'),\n      encoding: 'base64',\n    });\n  }\n\n  return screenshots;\n}\n\nfunction extractTopLevelWindowIds(stdout: string): string[] {\n  const windowIds: string[] = [];\n  const lines = stdout.split('\\n');\n  for (const line of lines) {\n    if (line.includes('Root window id:') || line.includes('Parent window id:') || line.includes('()')) continue;\n\n    const match = /^\\s{5}(0x[\\da-f]+) /.exec(line);\n    if (!match?.[1]) continue;\n    windowIds.push(Number.parseInt(match[1], 16).toString());\n  }\n  return windowIds;\n}\n\nfunction parseTimedStderr(\n  stderr: string,\n  startTimeSeconds: number,\n  sampledMemoryBytes: number\n): Pick<GuiCommandRunResult, 'stderr' | 'timeSeconds' | 'memoryBytes'> {\n  const match = /(?:^|\\n)(\\d+\\.\\d+) (\\d+)\\s*$/.exec(stderr);\n  const normalizedStderr = match ? stderr.slice(0, match.index).trimEnd() : stderr.trimEnd();\n  const parsedMemoryBytes = Number(match?.[2]) * 1024 || 0;\n  return {\n    stderr: normalizedStderr,\n    timeSeconds: Number(match?.[1]) || Date.now() / 1000 - startTimeSeconds,\n    memoryBytes: Math.max(parsedMemoryBytes, sampledMemoryBytes),\n  };\n}\n\nasync function ensureDisplayServer(): Promise<{ display: string; dispose: () => Promise<void> }> {\n  if (process.platform !== 'linux') {\n    throw new Error('GUI screenshot capture is supported only on Linux.');\n  }\n\n  for (let displayNumber = 90; displayNumber < 100; displayNumber++) {\n    const display = `:${displayNumber}`;\n    let spawnError: Error | undefined;\n    const xvfb = childProcess.spawn('Xvfb', [display, '-screen', '0', '1280x1024x24', '-ac'], {\n      stdio: 'ignore',\n      // Runs as the trusted harness user, so pin the lookup to system paths.\n      env: getTrustedHelperEnv(),\n    });\n    xvfb.on('error', (error) => {\n      spawnError = error;\n    });\n\n    await wait(XVFB_STARTUP_WAIT_SECONDS * 1000);\n    if (spawnError) throw spawnError;\n    if (xvfb.exitCode !== null) continue;\n\n    return {\n      display,\n      dispose: async () => {\n        if (!xvfb.killed) {\n          xvfb.kill('SIGTERM');\n          await wait(XVFB_SHUTDOWN_WAIT_SECONDS * 1000);\n          if (xvfb.exitCode === null) xvfb.kill('SIGKILL');\n        }\n      },\n    };\n  }\n\n  throw new Error('Xvfb could not be started.');\n}\n\nasync function stopProcess(child: childProcess.ChildProcess): Promise<void> {\n  if (!child.pid) return;\n  // The current user cannot signal the root-owned sudo wrapper nor the sandbox user's processes, so\n  // go through sudo. SIGKILL is unconditional after the grace period: `child.exitCode` reflects only\n  // the sudo wrapper, so a daemonized child that ignored SIGTERM would survive if we gated on it.\n  if (sandboxUserName) {\n    killSandboxUserProcesses(['TERM']);\n    await wait(PROCESS_SHUTDOWN_WAIT_SECONDS * 1000);\n    killSandboxUserProcesses(['KILL']);\n    return;\n  }\n  killProcessGroup(child.pid, 'SIGTERM');\n  await wait(PROCESS_SHUTDOWN_WAIT_SECONDS * 1000);\n  if (child.exitCode === null) killProcessGroup(child.pid, 'SIGKILL');\n}\n\nfunction readProcessGroupMemoryBytes(processGroupId: number | undefined): number {\n  if (!processGroupId || process.platform !== 'linux') return 0;\n\n  // The delegation contract requires sudoers `!use_pty`, so sudo execs the command in place and it\n  // stays in the detached child's process group; `--pgroup` therefore still captures it there.\n  const result = childProcess.spawnSync('ps', ['-o', 'rss=', '--no-headers', '--pgroup', String(processGroupId)], {\n    encoding: 'utf8',\n    // Runs as the trusted harness user, so pin the lookup to system paths.\n    env: getTrustedHelperEnv(),\n  });\n  if (result.error || result.status !== 0 || !result.stdout) return 0;\n\n  return result.stdout\n    .split('\\n')\n    .map((line) => Number(line.trim()))\n    .filter((value) => Number.isFinite(value) && value > 0)\n    .reduce((sum, value) => sum + value * 1024, 0);\n}\n\nfunction killProcessGroup(processGroupId: number, signal: NodeJS.Signals): void {\n  try {\n    if (process.platform !== 'win32') {\n      process.kill(-processGroupId, signal);\n      return;\n    }\n  } catch {\n    // The process group may already be gone. Fall back to the direct PID below.\n  }\n\n  try {\n    process.kill(processGroupId, signal);\n  } catch {\n    // The direct child may also already be gone.\n  }\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n  try {\n    await fs.access(filePath);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\nfunction errorToMessage(error: unknown): string {\n  return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":"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"}