import type { ProblemMarkdownFrontMatter } from '../types/problem.js'; import type { TestCaseResult } from '../types/testCaseResult.js'; interface BaseCommandTestCase { id: string; input?: string; fileInputPath?: string; } type CommandJudgeCaseResult = Pick; export interface CommandRunResult { stdin: string; stdout: string; stderr: string; status: number | undefined; timeSeconds: number; memoryBytes: number; outputLimitExceeded?: boolean; } interface CommandJudgeContext { timeLimitSeconds: number; outputLimitLength: number; problemMarkdownFrontMatter: Pick; } export interface CommandJudgeLimits { buildTimeoutSeconds: number; maxOutputLength: number; } export interface CommandJudgePresetOptions { /** * Set to false when the custom runner builds the submitted source itself. * Defaults to true. */ buildSubmission?: boolean; limits?: CommandJudgeLimits; runTimeoutSeconds?: number; readTestCases?: (problemDir: string) => Promise; /** * Runs as the trusted harness user with the submission's `cwd`. Fixture files it creates there * must be written with `createDirectoryWithoutFollowingSymlinks`/`writeFileWithoutFollowingSymlinks` * (both exported): a submission can plant a symlink at a fixture path, and a plain `fs.writeFile` * would follow it into a file only the harness can write. */ resolveInput?: (context: { testCase: TTestCase; cwd: string; env: NodeJS.ProcessEnv; }) => Promise | string; runCommand?: (context: { testCase: TTestCase; /** * The command to run. Under `EXERCODE_SANDBOX_USER` delegation it is already wrapped so it * executes as the sandbox user; spawn it as given (with the supplied `env`) instead of * reconstructing it, or the submission runs as the trusted harness user. * * Its direct child is then a root-owned `sudo` whose descendants belong to the sandbox user, so * the handler cannot signal them: enforce `timeLimitSeconds` with `startSandboxTimeoutWatchdog` * and `killSandboxUserProcesses` (both exported) rather than `child.kill()` or an outer * `timeout`. The preset terminates leftover sandbox processes after the handler returns. */ command: readonly [string, ...string[]]; stdin: string; cwd: string; env: NodeJS.ProcessEnv; timeLimitSeconds: number; }) => Promise | TRunResult; test?: (context: { testCase: TTestCase; runResult: TRunResult; outputFiles: NonNullable; context: CommandJudgeContext; }) => Promise> | Partial | undefined; } /** * A preset function for judging by executable command. * * Keep problem-specific logic in `resolveInput` and `test`. * * @example * Create `judge.ts`: * ```ts * import { commandJudgePreset } from '@exercode/problem-utils/presets/command'; * import { DecisionCode } from '@exercode/problem-utils'; * * await commandJudgePreset(import.meta.dirname, { * readTestCases: async () => [ * { id: '01', input: '1 2' }, * ], * test: ({ runResult }) => { * return runResult.stdout.trim() === '3' * ? { decisionCode: DecisionCode.ACCEPTED } * : { decisionCode: DecisionCode.WRONG_ANSWER }; * }, * }); * ``` * * Run with the required parameters: * ```bash * bun judge.ts model_answers/python '{ "language": "python" }' * ``` * * Run without a cwd argument to judge each `/model_answers/*` directory * for debugging. A prominent banner is printed for each run. * ```bash * bun judge.ts * ``` */ export declare function commandJudgePreset(problemDir: string, options: CommandJudgePresetOptions): Promise; export {};