@exercode/problem-utils 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +6 -1
  2. package/dist/cli/runSingleHarness.js.map +1 -1
  3. package/dist/helpers/runCommandInTemporaryPackageManagerProject.cjs +1 -1
  4. package/dist/helpers/runCommandInTemporaryPackageManagerProject.cjs.map +1 -1
  5. package/dist/helpers/runCommandInTemporaryPackageManagerProject.d.ts +2 -0
  6. package/dist/helpers/runCommandInTemporaryPackageManagerProject.js +1 -1
  7. package/dist/helpers/runCommandInTemporaryPackageManagerProject.js.map +1 -1
  8. package/dist/helpers/runHarnessProcess.cjs.map +1 -1
  9. package/dist/helpers/runHarnessProcess.d.ts +4 -1
  10. package/dist/helpers/runHarnessProcess.js.map +1 -1
  11. package/dist/helpers/spawnWithLimits.cjs +2 -0
  12. package/dist/helpers/spawnWithLimits.cjs.map +1 -0
  13. package/dist/helpers/spawnWithLimits.d.ts +38 -0
  14. package/dist/helpers/spawnWithLimits.js +2 -0
  15. package/dist/helpers/spawnWithLimits.js.map +1 -0
  16. package/dist/helpers/spawnWithTimeout.cjs +4 -0
  17. package/dist/helpers/spawnWithTimeout.cjs.map +1 -0
  18. package/dist/helpers/spawnWithTimeout.d.ts +21 -0
  19. package/dist/helpers/spawnWithTimeout.js +4 -0
  20. package/dist/helpers/spawnWithTimeout.js.map +1 -0
  21. package/dist/helpers/temporaryProblemDirCopy.cjs +1 -1
  22. package/dist/helpers/temporaryProblemDirCopy.js +1 -1
  23. package/dist/presets/command.cjs +1 -1
  24. package/dist/presets/command.cjs.map +1 -1
  25. package/dist/presets/command.d.ts +2 -0
  26. package/dist/presets/command.js +1 -1
  27. package/dist/presets/command.js.map +1 -1
  28. package/dist/presets/guiCommand.cjs +2 -2
  29. package/dist/presets/guiCommand.cjs.map +1 -1
  30. package/dist/presets/guiCommand.js +2 -2
  31. package/dist/presets/guiCommand.js.map +1 -1
  32. package/dist/presets/stdio.cjs +1 -1
  33. package/dist/presets/stdio.cjs.map +1 -1
  34. package/dist/presets/stdio.js +1 -1
  35. package/dist/presets/stdio.js.map +1 -1
  36. package/dist/types/testCaseResult.cjs +1 -1
  37. package/dist/types/testCaseResult.cjs.map +1 -1
  38. package/dist/types/testCaseResult.d.ts +1 -0
  39. package/dist/types/testCaseResult.js +1 -1
  40. package/dist/types/testCaseResult.js.map +1 -1
  41. package/package.json +3 -3
  42. package/dist/helpers/spawnSyncWithTimeout.cjs +0 -2
  43. package/dist/helpers/spawnSyncWithTimeout.cjs.map +0 -1
  44. package/dist/helpers/spawnSyncWithTimeout.d.ts +0 -5
  45. package/dist/helpers/spawnSyncWithTimeout.js +0 -2
  46. package/dist/helpers/spawnSyncWithTimeout.js.map +0 -1
@@ -0,0 +1,2 @@
1
+ import e from"node:path";import t from"node:fs/promises";import n from"node:os";import r from"node:child_process";const i=1e3,a=g(),o=a!==void 0,s=new Set;process.once(`exit`,()=>{for(let e of s)u(e,`SIGKILL`)});async function c(e,t){let n=a===void 0?void 0:await d(`exercode-time-`);try{let o=process.platform!==`win32`,c=o?[`timeout`,`--foreground`,`-k`,`1`,String(Math.max(t.timeLimitSeconds,.001)),...e]:e,d=a===void 0?c:[...a,`--output=/dev/fd/3`,...c],m=o?t.timeLimitSeconds*1e3+2*i:t.timeLimitSeconds*1e3,h=performance.now(),g=r.spawn(d[0],d.slice(1),{cwd:t.cwd,detached:o,env:t.env,stdio:n===void 0?[`pipe`,`pipe`,`pipe`]:[`pipe`,`pipe`,`pipe`,n.fd]});s.add(g);let _=[],v=[],y=0,b=!1,x=!1,S=0,C,w=(e,n)=>{if(y>=t.outputLimitBytes){n.byteLength>0&&(x=!0,u(g,`SIGKILL`));return}let r=t.outputLimitBytes-y,i=n.byteLength>r?n.subarray(0,r):n;e.push(i),y+=i.byteLength,n.byteLength>r&&(x=!0,u(g,`SIGKILL`))};g.stdout.on(`data`,e=>w(_,e)),g.stderr.on(`data`,e=>w(v,e));let T=setTimeout(()=>{b=!0,u(g,`SIGTERM`)},m),E=setTimeout(()=>{b&&u(g,`SIGKILL`)},m+i);E.unref();let{status:D,signal:O}=await new Promise((e,n)=>{let r=!1,a,o,s=(t,i)=>{if(!r){if(r=!0,clearTimeout(o),a){n(a);return}e({status:t??void 0,signal:i??void 0})}},c=e=>{if(!r){if(g.pid===void 0){C=e.message,s(void 0,void 0);return}a=e,u(g,`SIGKILL`)}};g.on(`error`,c),g.stdin.on(`error`,e=>{e.code!==`EPIPE`&&c(e)}),g.on(`exit`,(e,t)=>{clearTimeout(T),clearTimeout(E),S=(performance.now()-h)/1e3,u(g,`SIGKILL`),o=setTimeout(()=>{g.stdout.destroy(),g.stderr.destroy(),s(e,t)},i)}),g.on(`close`,s),g.stdin.end(t.stdin)}).finally(()=>{clearTimeout(T),clearTimeout(E),s.delete(g)}),k=n===void 0?void 0:p(await f(n,4096));return{stdout:Buffer.concat(_).toString(),stderr:C??Buffer.concat(v).toString(),status:D,signal:O,timeSeconds:k?.timeSeconds||S,cpuTimeSeconds:k?.cpuTimeSeconds??0,memoryBytes:k?.memoryBytes??0,timeCommandMessage:k?.message,timedOut:b||l(D,S,t.timeLimitSeconds),outputLimitExceeded:x}}finally{await n?.close()}}function l(e,t,n){return e===124?t>=n:e===137&&t>=n+i/1e3}function u(e,t){if(e.pid!==void 0)try{if(process.platform===`win32`){e.kill(t);return}process.kill(-e.pid,t)}catch(e){if(!h(e,`ESRCH`)&&!h(e,`EPERM`))throw e}}async function d(r){let i=await t.mkdtemp(e.join(n.tmpdir(),r));try{return await t.open(e.join(i,`record`),`wx+`,384)}finally{await t.rm(i,{recursive:!0,force:!0})}}async function f(e,t){let{size:n}=await e.stat(),r=Math.min(n,t),{buffer:i}=await e.read(Buffer.alloc(r),0,r,n-r);return i.toString()}function p(e){let t=/(?:^|\n)(\d+(?:[.,]\d+)?) (\d+(?:[.,]\d+)?) (\d+(?:[.,]\d+)?) (\d+)\s*$/.exec(e);if(t)return{timeSeconds:m(t[1]),cpuTimeSeconds:m(t[2])+m(t[3]),memoryBytes:Number(t[4])*1024,message:e.slice(0,t.index).trim()||void 0}}function m(e){return Number(e.replace(`,`,`.`))}function h(e,t){return typeof e==`object`&&!!e&&`code`in e&&e.code===t}function g(){let e=n.platform()===`darwin`?`gtime`:`/usr/bin/time`,t=r.spawnSync(e,[`--version`],{stdio:`ignore`});if(!(t.error||t.status!==0))return[e,`--format`,`%e %U %S %M`]}export{o as isTimeCommandAvailable,c as spawnWithLimits};
2
+ //# sourceMappingURL=spawnWithLimits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawnWithLimits.js","names":[],"sources":["../../src/helpers/spawnWithLimits.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport interface SpawnWithLimitsResult {\n stdout: string;\n stderr: string;\n status: number | undefined;\n signal: NodeJS.Signals | undefined;\n /** Wall time measured by GNU time, or measured here until the command exited when it produced none. */\n timeSeconds: number;\n /**\n * User plus system CPU time of the command and the descendants it waited for, measured by GNU\n * time, or 0 when it produced no measurement. A run `timeout` ended is measured too, so a caller\n * can tell a program that used up its time limit from one that waited for the CPU.\n */\n cpuTimeSeconds: number;\n /**\n * Peak resident set size measured by GNU time, or 0 when it produced no measurement. GNU time\n * measures its child `timeout` and the descendants it waited for, so the value is at least\n * `timeout`'s own footprint (about 1 MiB), which only shows for the smallest native programs.\n */\n memoryBytes: number;\n /** The note GNU time adds when the command exits abnormally, e.g. `Command terminated by signal 11`. */\n timeCommandMessage: string | undefined;\n timedOut: boolean;\n outputLimitExceeded: boolean;\n}\n\nconst killGracePeriodMilliseconds = 1000;\nconst MIN_TIMEOUT_SECONDS = 0.001;\n// GNU `timeout` exits with 124 after ending a run at its limit, or with 128 + SIGKILL when the run\n// ignored its SIGTERM and it had to be killed after the grace period.\nconst TIMEOUT_EXIT_STATUS = 124;\nconst KILLED_AFTER_TIMEOUT_EXIT_STATUS = 137;\n// GNU time writes its record to a file this process opened and unlinked at once, handed down as fd 3\n// of the command and reopened by GNU time through `/dev/fd`: the command runs as the same OS user,\n// so a record reachable by path could be swapped for a fake or for a FIFO that blocks the read.\nconst TIME_OUTPUT_FD = 3;\n// The command inherits that descriptor too, so only the tail that can hold GNU time's record is read.\nconst TIME_OUTPUT_TAIL_BYTES = 4096;\nconst timeCommand = resolveTimeCommand();\n\n/** Whether GNU time is available, i.e. whether `timeSeconds`, `cpuTimeSeconds`, and `memoryBytes` are measured at all. */\nexport const isTimeCommandAvailable = timeCommand !== undefined;\n\n// The commands run in their own sessions, so they outlive this process unless it ends them itself\n// on the way out (e.g. a preset's SIGINT handler calling `process.exit`).\nconst liveSubprocesses = new Set<childProcess.ChildProcess>();\nprocess.once('exit', () => {\n for (const subprocess of liveSubprocesses) killSubprocessGroup(subprocess, 'SIGKILL');\n});\n\n/**\n * Runs a command in its own process group with `stdin` piped in, killing the whole group once it\n * exceeds the time or output limit, and reports the wall time, CPU time, and peak memory measured\n * by GNU time.\n */\nexport async function spawnWithLimits(\n command: readonly [string, ...string[]],\n context: {\n cwd: string;\n env: NodeJS.ProcessEnv;\n outputLimitBytes: number;\n stdin: string;\n timeLimitSeconds: number;\n }\n): Promise<SpawnWithLimitsResult> {\n const timeOutput = timeCommand === undefined ? undefined : await openUnlinkedFile('exercode-time-');\n try {\n const detached = process.platform !== 'win32';\n // The command runs in its own session so the group kill below cannot hit this process, but that\n // also puts it out of reach of whoever kills this process. GNU `timeout` keeps the run bounded\n // in that case, and it is what normally ends a run at its limit: GNU time wraps it, so a run\n // that hit the limit still gets its CPU time recorded (a group kill from here would take GNU\n // time down with the command), and `--foreground` keeps the command in this group so the kill\n // below still reaches every descendant. The timers below only back `timeout` up. Should this\n // process die first, `timeout` ends the command alone (`--foreground` signals no descendant),\n // an accepted gap: the judge server sweeps every process of the request's sandbox user anyway.\n const boundedCommand: readonly [string, ...string[]] = detached\n ? [\n 'timeout',\n '--foreground',\n '-k',\n String(killGracePeriodMilliseconds / 1000),\n // A zero duration would disable `timeout`; the front matter allows a zero time limit.\n String(Math.max(context.timeLimitSeconds, MIN_TIMEOUT_SECONDS)),\n ...command,\n ]\n : command;\n const spawnedCommand: readonly [string, ...string[]] =\n timeCommand === undefined\n ? boundedCommand\n : [...timeCommand, `--output=/dev/fd/${TIME_OUTPUT_FD}`, ...boundedCommand];\n // `timeout` sends SIGKILL a grace period after its SIGTERM; the timers here fire once even that\n // should have ended the run.\n const timeLimitMilliseconds = detached\n ? context.timeLimitSeconds * 1000 + 2 * killGracePeriodMilliseconds\n : context.timeLimitSeconds * 1000;\n // Monotonic: a wall-clock step must not turn a run `timeout` ended into one that finished in time.\n const startTimeMilliseconds = performance.now();\n // The standard streams are always pipes; the type cannot express that with the extra entry.\n const subprocess = childProcess.spawn(spawnedCommand[0], spawnedCommand.slice(1), {\n cwd: context.cwd,\n detached,\n env: context.env,\n stdio: timeOutput === undefined ? ['pipe', 'pipe', 'pipe'] : ['pipe', 'pipe', 'pipe', timeOutput.fd],\n }) as childProcess.ChildProcessWithoutNullStreams;\n liveSubprocesses.add(subprocess);\n\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let outputBytes = 0;\n let timedOut = false;\n let outputLimitExceeded = false;\n let wallTimeSeconds = 0;\n let spawnErrorMessage: string | undefined;\n\n const appendOutputChunk = (chunks: Buffer[], chunk: Buffer): void => {\n if (outputBytes >= context.outputLimitBytes) {\n if (chunk.byteLength > 0) {\n outputLimitExceeded = true;\n killSubprocessGroup(subprocess, 'SIGKILL');\n }\n return;\n }\n\n const remainingBytes = context.outputLimitBytes - outputBytes;\n const appendedChunk = chunk.byteLength > remainingBytes ? chunk.subarray(0, remainingBytes) : chunk;\n chunks.push(appendedChunk);\n outputBytes += appendedChunk.byteLength;\n\n if (chunk.byteLength > remainingBytes) {\n outputLimitExceeded = true;\n killSubprocessGroup(subprocess, 'SIGKILL');\n }\n };\n\n subprocess.stdout.on('data', (chunk: Buffer) => appendOutputChunk(stdoutChunks, chunk));\n subprocess.stderr.on('data', (chunk: Buffer) => appendOutputChunk(stderrChunks, chunk));\n\n const timeout = setTimeout(() => {\n timedOut = true;\n killSubprocessGroup(subprocess, 'SIGTERM');\n }, timeLimitMilliseconds);\n const killTimeout = setTimeout(() => {\n if (timedOut) killSubprocessGroup(subprocess, 'SIGKILL');\n }, timeLimitMilliseconds + killGracePeriodMilliseconds);\n killTimeout.unref();\n\n const { status, signal } = await new Promise<{ status: number | undefined; signal: NodeJS.Signals | undefined }>(\n (resolve, reject) => {\n let settled = false;\n let pendingError: Error | undefined;\n let closeTimeout: NodeJS.Timeout | undefined;\n const settle = (code: number | null | undefined, exitSignal: NodeJS.Signals | null | undefined): void => {\n if (settled) return;\n settled = true;\n clearTimeout(closeTimeout);\n if (pendingError) {\n reject(pendingError);\n return;\n }\n resolve({ status: code ?? undefined, signal: exitSignal ?? undefined });\n };\n const failAfterClose = (error: Error): void => {\n if (settled) return;\n if (subprocess.pid === undefined) {\n // The spawn itself failed, i.e. the head of the chain (GNU time, or `timeout` without\n // it, or the command itself on Windows) or the cwd is missing. A missing judged command\n // is reported as status 127 by the wrapper instead. Report this like a run that produced\n // only this message.\n spawnErrorMessage = error.message;\n settle(undefined, undefined);\n return;\n }\n pendingError = error;\n killSubprocessGroup(subprocess, 'SIGKILL');\n };\n subprocess.on('error', failAfterClose);\n subprocess.stdin.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code !== 'EPIPE') failAfterClose(error);\n });\n subprocess.on('exit', (code, exitSignal) => {\n // The command is gone, so the limit timers must not fire during the grace period below. A\n // descendant that inherited the pipes (e.g. `sleep 1000 &`) would keep them open and hold\n // back 'close' until it ends: kill what is left of the group. A descendant that moved to\n // its own session survives that, so stop waiting for the pipes after the grace period and\n // keep the output read so far.\n clearTimeout(timeout);\n clearTimeout(killTimeout);\n wallTimeSeconds = (performance.now() - startTimeMilliseconds) / 1000;\n killSubprocessGroup(subprocess, 'SIGKILL');\n closeTimeout = setTimeout(() => {\n subprocess.stdout.destroy();\n subprocess.stderr.destroy();\n settle(code, exitSignal);\n }, killGracePeriodMilliseconds);\n });\n subprocess.on('close', settle);\n subprocess.stdin.end(context.stdin);\n }\n ).finally(() => {\n clearTimeout(timeout);\n clearTimeout(killTimeout);\n liveSubprocesses.delete(subprocess);\n });\n\n const timeResult =\n timeOutput === undefined ? undefined : parseTimeOutput(await readTail(timeOutput, TIME_OUTPUT_TAIL_BYTES));\n\n return {\n stdout: Buffer.concat(stdoutChunks).toString(),\n stderr: spawnErrorMessage ?? Buffer.concat(stderrChunks).toString(),\n status,\n signal,\n // GNU time rounds a fast run to 0.00; the wall time until 'exit' stands in for it (never the\n // grace period spent on the pipes afterwards).\n timeSeconds: timeResult?.timeSeconds || wallTimeSeconds,\n cpuTimeSeconds: timeResult?.cpuTimeSeconds ?? 0,\n memoryBytes: timeResult?.memoryBytes ?? 0,\n timeCommandMessage: timeResult?.message,\n timedOut: timedOut || endedByTimeout(status, wallTimeSeconds, context.timeLimitSeconds),\n outputLimitExceeded,\n };\n } finally {\n await timeOutput?.close();\n }\n}\n\n/**\n * Whether `timeout` ended the run: 124 says it did so at the limit, and 137 that the run ignored its\n * SIGTERM and had to be killed after the grace period. A program can exit with either status on its\n * own, so each is trusted only once the wall time reached the moment `timeout` would have produced it.\n */\nfunction endedByTimeout(status: number | undefined, wallTimeSeconds: number, timeLimitSeconds: number): boolean {\n if (status === TIMEOUT_EXIT_STATUS) return wallTimeSeconds >= timeLimitSeconds;\n if (status === KILLED_AFTER_TIMEOUT_EXIT_STATUS) {\n return wallTimeSeconds >= timeLimitSeconds + killGracePeriodMilliseconds / 1000;\n }\n return false;\n}\n\nfunction killSubprocessGroup(subprocess: childProcess.ChildProcess, signal: NodeJS.Signals): void {\n if (subprocess.pid === undefined) return;\n\n try {\n if (process.platform === 'win32') {\n subprocess.kill(signal);\n return;\n }\n process.kill(-subprocess.pid, signal);\n } catch (error) {\n if (!isErrorWithCode(error, 'ESRCH') && !isErrorWithCode(error, 'EPERM')) throw error;\n }\n}\n\n// The file is unlinked before it is returned, so nothing can reach it by path anymore.\nasync function openUnlinkedFile(prefix: string): Promise<fs.FileHandle> {\n const directory = await fs.mkdtemp(path.join(os.tmpdir(), prefix));\n try {\n return await fs.open(path.join(directory, 'record'), 'wx+', 0o600);\n } finally {\n await fs.rm(directory, { recursive: true, force: true });\n }\n}\n\nasync function readTail(file: fs.FileHandle, maxBytes: number): Promise<string> {\n const { size } = await file.stat();\n const length = Math.min(size, maxBytes);\n const { buffer } = await file.read(Buffer.alloc(length), 0, length, size - length);\n return buffer.toString();\n}\n\n// Empty when the command was killed before GNU time wrote its record.\nfunction parseTimeOutput(\n content: string\n): { timeSeconds: number; cpuTimeSeconds: number; memoryBytes: number; message: string | undefined } | undefined {\n const match = /(?:^|\\n)(\\d+(?:[.,]\\d+)?) (\\d+(?:[.,]\\d+)?) (\\d+(?:[.,]\\d+)?) (\\d+)\\s*$/.exec(content);\n if (!match) return undefined;\n\n return {\n timeSeconds: parseSeconds(match[1]!),\n cpuTimeSeconds: parseSeconds(match[2]!) + parseSeconds(match[3]!),\n memoryBytes: Number(match[4]) * 1024,\n message: content.slice(0, match.index).trim() || undefined,\n };\n}\n\n// GNU time formats its seconds with the locale's decimal separator.\nfunction parseSeconds(text: string): number {\n return Number(text.replace(',', '.'));\n}\n\nfunction isErrorWithCode(error: unknown, code: string): boolean {\n return typeof error === 'object' && error !== null && 'code' in error && (error as { code: unknown }).code === code;\n}\n\nfunction resolveTimeCommand(): readonly [string, ...string[]] | undefined {\n const command = os.platform() === 'darwin' ? 'gtime' : '/usr/bin/time';\n const result = childProcess.spawnSync(command, ['--version'], { stdio: 'ignore' });\n if (result.error || result.status !== 0) return undefined;\n\n // Wall seconds, user CPU seconds, system CPU seconds, peak resident set size in KiB.\n return [command, '--format', '%e %U %S %M'];\n}\n"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ "use strict";const e=require("./spawnWithLimits.cjs");async function t(t,n,r,i){if(!e.isTimeCommandAvailable)throw Error(`GNU time (gtime on macOS, /usr/bin/time on Linux) is required.`);let a=await e.spawnWithLimits([t,...n],{cwd:r.cwd,env:r.env,outputLimitBytes:8388608,stdin:r.stdin??``,timeLimitSeconds:i}),o=a.timeCommandMessage&&!a.timedOut?`${a.stderr}${a.stderr&&!a.stderr.endsWith(`
2
+ `)?`
3
+ `:``}${a.timeCommandMessage}\n`:a.stderr;return{stdout:a.stdout,stderr:o,status:a.timedOut||a.outputLimitExceeded?0:a.status,timeSeconds:a.timedOut?i+.001:a.timeSeconds,cpuTimeSeconds:a.cpuTimeSeconds,memoryBytes:a.memoryBytes,outputLimitExceeded:a.outputLimitExceeded}}exports.spawnWithTimeout=t;
4
+ //# sourceMappingURL=spawnWithTimeout.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawnWithTimeout.cjs","names":["isTimeCommandAvailable","spawnWithLimits"],"sources":["../../src/helpers/spawnWithTimeout.ts"],"sourcesContent":["import { isTimeCommandAvailable, spawnWithLimits } from './spawnWithLimits.js';\n\nconst OUTPUT_LIMIT_BYTES = 8 * 1024 * 1024;\n\n/**\n * Runs a submission-derived command under a time limit and an output cap. A run that hits a limit is\n * reported like a normal exit (status 0), with `timeSeconds` just above the limit or the output\n * truncated at the cap, so callers judge it by the limit it hit. A missing command is reported by\n * GNU time as status 127; a spawn that fails outright (e.g. a missing cwd) is reported with no\n * status and the error as stderr; a failure on the judge's side throws.\n */\nexport async function spawnWithTimeout(\n command: string,\n args: readonly string[],\n context: { cwd: string; env: NodeJS.ProcessEnv; stdin?: string },\n timeoutSeconds: number\n): Promise<{\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n /** See `SpawnWithLimitsResult.cpuTimeSeconds`. */\n cpuTimeSeconds: number;\n memoryBytes: number;\n outputLimitExceeded: boolean;\n}> {\n // Without GNU time's measurements, the memory limit would silently never apply and the reported\n // time would be this process's wall clock rather than the program's.\n if (!isTimeCommandAvailable) throw new Error('GNU time (gtime on macOS, /usr/bin/time on Linux) is required.');\n\n const result = await spawnWithLimits([command, ...args], {\n cwd: context.cwd,\n env: context.env,\n outputLimitBytes: OUTPUT_LIMIT_BYTES,\n stdin: context.stdin ?? '',\n timeLimitSeconds: timeoutSeconds,\n });\n\n // Keep GNU time's note about an abnormal exit (e.g. a segmentation fault) visible to the learner;\n // the note about `timeout`'s own exit status after ending the run says nothing to them.\n const stderr =\n result.timeCommandMessage && !result.timedOut\n ? `${result.stderr}${result.stderr && !result.stderr.endsWith('\\n') ? '\\n' : ''}${result.timeCommandMessage}\\n`\n : result.stderr;\n\n return {\n stdout: result.stdout,\n stderr,\n status: result.timedOut || result.outputLimitExceeded ? 0 : result.status,\n timeSeconds: result.timedOut ? timeoutSeconds + 1e-3 : result.timeSeconds,\n cpuTimeSeconds: result.cpuTimeSeconds,\n memoryBytes: result.memoryBytes,\n outputLimitExceeded: result.outputLimitExceeded,\n };\n}\n"],"mappings":"sDAWA,eAAsB,EACpB,EACA,EACA,EACA,EAUC,CAGD,GAAI,CAACA,EAAAA,uBAAwB,MAAU,MAAM,gEAAgE,EAE7G,IAAM,EAAS,MAAMC,EAAAA,gBAAgB,CAAC,EAAS,GAAG,CAAI,EAAG,CACvD,IAAK,EAAQ,IACb,IAAK,EAAQ,IACb,iBAAkB,QAClB,MAAO,EAAQ,OAAS,GACxB,iBAAkB,CACpB,CAAC,EAIK,EACJ,EAAO,oBAAsB,CAAC,EAAO,SACjC,GAAG,EAAO,SAAS,EAAO,QAAU,CAAC,EAAO,OAAO,SAAS;CAAI,EAAI;EAAO,KAAK,EAAO,mBAAmB,IAC1G,EAAO,OAEb,MAAO,CACL,OAAQ,EAAO,OACf,SACA,OAAQ,EAAO,UAAY,EAAO,oBAAsB,EAAI,EAAO,OACnE,YAAa,EAAO,SAAW,EAAiB,KAAO,EAAO,YAC9D,eAAgB,EAAO,eACvB,YAAa,EAAO,YACpB,oBAAqB,EAAO,mBAC9B,CACF"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Runs a submission-derived command under a time limit and an output cap. A run that hits a limit is
3
+ * reported like a normal exit (status 0), with `timeSeconds` just above the limit or the output
4
+ * truncated at the cap, so callers judge it by the limit it hit. A missing command is reported by
5
+ * GNU time as status 127; a spawn that fails outright (e.g. a missing cwd) is reported with no
6
+ * status and the error as stderr; a failure on the judge's side throws.
7
+ */
8
+ export declare function spawnWithTimeout(command: string, args: readonly string[], context: {
9
+ cwd: string;
10
+ env: NodeJS.ProcessEnv;
11
+ stdin?: string;
12
+ }, timeoutSeconds: number): Promise<{
13
+ stdout: string;
14
+ stderr: string;
15
+ status: number | undefined;
16
+ timeSeconds: number;
17
+ /** See `SpawnWithLimitsResult.cpuTimeSeconds`. */
18
+ cpuTimeSeconds: number;
19
+ memoryBytes: number;
20
+ outputLimitExceeded: boolean;
21
+ }>;
@@ -0,0 +1,4 @@
1
+ import{isTimeCommandAvailable as e,spawnWithLimits as t}from"./spawnWithLimits.js";async function n(n,r,i,a){if(!e)throw Error(`GNU time (gtime on macOS, /usr/bin/time on Linux) is required.`);let o=await t([n,...r],{cwd:i.cwd,env:i.env,outputLimitBytes:8388608,stdin:i.stdin??``,timeLimitSeconds:a}),s=o.timeCommandMessage&&!o.timedOut?`${o.stderr}${o.stderr&&!o.stderr.endsWith(`
2
+ `)?`
3
+ `:``}${o.timeCommandMessage}\n`:o.stderr;return{stdout:o.stdout,stderr:s,status:o.timedOut||o.outputLimitExceeded?0:o.status,timeSeconds:o.timedOut?a+.001:o.timeSeconds,cpuTimeSeconds:o.cpuTimeSeconds,memoryBytes:o.memoryBytes,outputLimitExceeded:o.outputLimitExceeded}}export{n as spawnWithTimeout};
4
+ //# sourceMappingURL=spawnWithTimeout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawnWithTimeout.js","names":[],"sources":["../../src/helpers/spawnWithTimeout.ts"],"sourcesContent":["import { isTimeCommandAvailable, spawnWithLimits } from './spawnWithLimits.js';\n\nconst OUTPUT_LIMIT_BYTES = 8 * 1024 * 1024;\n\n/**\n * Runs a submission-derived command under a time limit and an output cap. A run that hits a limit is\n * reported like a normal exit (status 0), with `timeSeconds` just above the limit or the output\n * truncated at the cap, so callers judge it by the limit it hit. A missing command is reported by\n * GNU time as status 127; a spawn that fails outright (e.g. a missing cwd) is reported with no\n * status and the error as stderr; a failure on the judge's side throws.\n */\nexport async function spawnWithTimeout(\n command: string,\n args: readonly string[],\n context: { cwd: string; env: NodeJS.ProcessEnv; stdin?: string },\n timeoutSeconds: number\n): Promise<{\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n /** See `SpawnWithLimitsResult.cpuTimeSeconds`. */\n cpuTimeSeconds: number;\n memoryBytes: number;\n outputLimitExceeded: boolean;\n}> {\n // Without GNU time's measurements, the memory limit would silently never apply and the reported\n // time would be this process's wall clock rather than the program's.\n if (!isTimeCommandAvailable) throw new Error('GNU time (gtime on macOS, /usr/bin/time on Linux) is required.');\n\n const result = await spawnWithLimits([command, ...args], {\n cwd: context.cwd,\n env: context.env,\n outputLimitBytes: OUTPUT_LIMIT_BYTES,\n stdin: context.stdin ?? '',\n timeLimitSeconds: timeoutSeconds,\n });\n\n // Keep GNU time's note about an abnormal exit (e.g. a segmentation fault) visible to the learner;\n // the note about `timeout`'s own exit status after ending the run says nothing to them.\n const stderr =\n result.timeCommandMessage && !result.timedOut\n ? `${result.stderr}${result.stderr && !result.stderr.endsWith('\\n') ? '\\n' : ''}${result.timeCommandMessage}\\n`\n : result.stderr;\n\n return {\n stdout: result.stdout,\n stderr,\n status: result.timedOut || result.outputLimitExceeded ? 0 : result.status,\n timeSeconds: result.timedOut ? timeoutSeconds + 1e-3 : result.timeSeconds,\n cpuTimeSeconds: result.cpuTimeSeconds,\n memoryBytes: result.memoryBytes,\n outputLimitExceeded: result.outputLimitExceeded,\n };\n}\n"],"mappings":"mFAWA,eAAsB,EACpB,EACA,EACA,EACA,EAUC,CAGD,GAAI,CAAC,EAAwB,MAAU,MAAM,gEAAgE,EAE7G,IAAM,EAAS,MAAM,EAAgB,CAAC,EAAS,GAAG,CAAI,EAAG,CACvD,IAAK,EAAQ,IACb,IAAK,EAAQ,IACb,iBAAkB,QAClB,MAAO,EAAQ,OAAS,GACxB,iBAAkB,CACpB,CAAC,EAIK,EACJ,EAAO,oBAAsB,CAAC,EAAO,SACjC,GAAG,EAAO,SAAS,EAAO,QAAU,CAAC,EAAO,OAAO,SAAS;CAAI,EAAI;EAAO,KAAK,EAAO,mBAAmB,IAC1G,EAAO,OAEb,MAAO,CACL,OAAQ,EAAO,OACf,SACA,OAAQ,EAAO,UAAY,EAAO,oBAAsB,EAAI,EAAO,OACnE,YAAa,EAAO,SAAW,EAAiB,KAAO,EAAO,YAC9D,eAAgB,EAAO,eACvB,YAAa,EAAO,YACpB,oBAAqB,EAAO,mBAC9B,CACF"}
@@ -1,2 +1,2 @@
1
- "use strict";const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:path");n=e.__toESM(n,1);let r=require("node:child_process");r=e.__toESM(r,1);let i=require("node:os");i=e.__toESM(i,1);async function a(e,r={}){let a=await t.default.promises.mkdtemp(n.default.join(i.default.tmpdir(),`problem-utils-isolation_`));r.onTempRootCreated?.(a);try{let r=n.default.resolve(e),i=n.default.join(a,f(r));return await t.default.promises.mkdir(n.default.dirname(i),{recursive:!0}),await t.default.promises.cp(r,i,{recursive:!0,filter:l,verbatimSymlinks:!0}),await d(a,r),{tempRoot:a,copiedProblemDir:i}}catch(e){throw await o(a),e}}async function o(e){try{return await t.default.promises.rm(e,{recursive:!0,force:!0}),!0}catch{c(e);try{return await t.default.promises.rm(e,{recursive:!0,force:!0}),!0}catch{return!1}}}function s(e){try{return t.default.rmSync(e,{recursive:!0,force:!0}),!0}catch{c(e);try{return t.default.rmSync(e,{recursive:!0,force:!0}),!0}catch{return!1}}}function c(e){process.platform!==`win32`&&r.default.spawnSync(`chmod`,[`-R`,`u+rwX`,e])}async function l(e){if(!u(e))return!1;if((await t.default.promises.lstat(e)).isSymbolicLink()&&n.default.isAbsolute(await t.default.promises.readlink(e)))throw Error(`${e} is an absolute symlink, which would escape the temporary copy; use a relative symlink`);return!0}function u(e){let t=n.default.basename(e);return t!==`node_modules`&&t!==`.git`}async function d(e,r){let i=n.default.resolve(r);for(;;){let r=n.default.join(i,`node_modules`);if(t.default.existsSync(r)){let a=n.default.join(e,f(i),`node_modules`);try{await t.default.promises.symlink(r,a,process.platform===`win32`?`junction`:`dir`)}catch{}}let a=n.default.dirname(i);if(a===i)break;i=a}}function f(e){let{root:t}=n.default.parse(e);return n.default.relative(t,e)}exports.copyProblemDirToTemporaryRoot=a,exports.forciblyRemoveDirectory=o,exports.forciblyRemoveDirectorySync=s;
1
+ "use strict";const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:path");n=e.__toESM(n,1);let r=require("node:os");r=e.__toESM(r,1);let i=require("node:child_process");i=e.__toESM(i,1);async function a(e,i={}){let a=await t.default.promises.mkdtemp(n.default.join(r.default.tmpdir(),`problem-utils-isolation_`));i.onTempRootCreated?.(a);try{let r=n.default.resolve(e),i=n.default.join(a,f(r));return await t.default.promises.mkdir(n.default.dirname(i),{recursive:!0}),await t.default.promises.cp(r,i,{recursive:!0,filter:l,verbatimSymlinks:!0}),await d(a,r),{tempRoot:a,copiedProblemDir:i}}catch(e){throw await o(a),e}}async function o(e){try{return await t.default.promises.rm(e,{recursive:!0,force:!0}),!0}catch{c(e);try{return await t.default.promises.rm(e,{recursive:!0,force:!0}),!0}catch{return!1}}}function s(e){try{return t.default.rmSync(e,{recursive:!0,force:!0}),!0}catch{c(e);try{return t.default.rmSync(e,{recursive:!0,force:!0}),!0}catch{return!1}}}function c(e){process.platform!==`win32`&&i.default.spawnSync(`chmod`,[`-R`,`u+rwX`,e])}async function l(e){if(!u(e))return!1;if((await t.default.promises.lstat(e)).isSymbolicLink()&&n.default.isAbsolute(await t.default.promises.readlink(e)))throw Error(`${e} is an absolute symlink, which would escape the temporary copy; use a relative symlink`);return!0}function u(e){let t=n.default.basename(e);return t!==`node_modules`&&t!==`.git`}async function d(e,r){let i=n.default.resolve(r);for(;;){let r=n.default.join(i,`node_modules`);if(t.default.existsSync(r)){let a=n.default.join(e,f(i),`node_modules`);try{await t.default.promises.symlink(r,a,process.platform===`win32`?`junction`:`dir`)}catch{}}let a=n.default.dirname(i);if(a===i)break;i=a}}function f(e){let{root:t}=n.default.parse(e);return n.default.relative(t,e)}exports.copyProblemDirToTemporaryRoot=a,exports.forciblyRemoveDirectory=o,exports.forciblyRemoveDirectorySync=s;
2
2
  //# sourceMappingURL=temporaryProblemDirCopy.cjs.map
@@ -1,2 +1,2 @@
1
- import e from"node:fs";import t from"node:path";import n from"node:child_process";import r from"node:os";async function i(n,i={}){let o=await e.promises.mkdtemp(t.join(r.tmpdir(),`problem-utils-isolation_`));i.onTempRootCreated?.(o);try{let r=t.resolve(n),i=t.join(o,d(r));return await e.promises.mkdir(t.dirname(i),{recursive:!0}),await e.promises.cp(r,i,{recursive:!0,filter:c,verbatimSymlinks:!0}),await u(o,r),{tempRoot:o,copiedProblemDir:i}}catch(e){throw await a(o),e}}async function a(t){try{return await e.promises.rm(t,{recursive:!0,force:!0}),!0}catch{s(t);try{return await e.promises.rm(t,{recursive:!0,force:!0}),!0}catch{return!1}}}function o(t){try{return e.rmSync(t,{recursive:!0,force:!0}),!0}catch{s(t);try{return e.rmSync(t,{recursive:!0,force:!0}),!0}catch{return!1}}}function s(e){process.platform!==`win32`&&n.spawnSync(`chmod`,[`-R`,`u+rwX`,e])}async function c(n){if(!l(n))return!1;if((await e.promises.lstat(n)).isSymbolicLink()&&t.isAbsolute(await e.promises.readlink(n)))throw Error(`${n} is an absolute symlink, which would escape the temporary copy; use a relative symlink`);return!0}function l(e){let n=t.basename(e);return n!==`node_modules`&&n!==`.git`}async function u(n,r){let i=t.resolve(r);for(;;){let r=t.join(i,`node_modules`);if(e.existsSync(r)){let a=t.join(n,d(i),`node_modules`);try{await e.promises.symlink(r,a,process.platform===`win32`?`junction`:`dir`)}catch{}}let a=t.dirname(i);if(a===i)break;i=a}}function d(e){let{root:n}=t.parse(e);return t.relative(n,e)}export{i as copyProblemDirToTemporaryRoot,a as forciblyRemoveDirectory,o as forciblyRemoveDirectorySync};
1
+ import e from"node:fs";import t from"node:path";import n from"node:os";import r from"node:child_process";async function i(r,i={}){let o=await e.promises.mkdtemp(t.join(n.tmpdir(),`problem-utils-isolation_`));i.onTempRootCreated?.(o);try{let n=t.resolve(r),i=t.join(o,d(n));return await e.promises.mkdir(t.dirname(i),{recursive:!0}),await e.promises.cp(n,i,{recursive:!0,filter:c,verbatimSymlinks:!0}),await u(o,n),{tempRoot:o,copiedProblemDir:i}}catch(e){throw await a(o),e}}async function a(t){try{return await e.promises.rm(t,{recursive:!0,force:!0}),!0}catch{s(t);try{return await e.promises.rm(t,{recursive:!0,force:!0}),!0}catch{return!1}}}function o(t){try{return e.rmSync(t,{recursive:!0,force:!0}),!0}catch{s(t);try{return e.rmSync(t,{recursive:!0,force:!0}),!0}catch{return!1}}}function s(e){process.platform!==`win32`&&r.spawnSync(`chmod`,[`-R`,`u+rwX`,e])}async function c(n){if(!l(n))return!1;if((await e.promises.lstat(n)).isSymbolicLink()&&t.isAbsolute(await e.promises.readlink(n)))throw Error(`${n} is an absolute symlink, which would escape the temporary copy; use a relative symlink`);return!0}function l(e){let n=t.basename(e);return n!==`node_modules`&&n!==`.git`}async function u(n,r){let i=t.resolve(r);for(;;){let r=t.join(i,`node_modules`);if(e.existsSync(r)){let a=t.join(n,d(i),`node_modules`);try{await e.promises.symlink(r,a,process.platform===`win32`?`junction`:`dir`)}catch{}}let a=t.dirname(i);if(a===i)break;i=a}}function d(e){let{root:n}=t.parse(e);return t.relative(n,e)}export{i as copyProblemDirToTemporaryRoot,a as forciblyRemoveDirectory,o as forciblyRemoveDirectorySync};
2
2
  //# sourceMappingURL=temporaryProblemDirCopy.js.map
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../helpers/printTestCaseResult.cjs"),n=require("../helpers/compareExpectedOutputFiles.cjs"),r=require("../helpers/parseArgs.cjs"),i=require("../helpers/readTestCases.cjs"),a=require("../helpers/findLanguageDefinitionByPath.cjs"),o=require("../helpers/readProblemMarkdownFrontMatter.cjs"),s=require("../types/decisionCode.cjs"),c=require("../helpers/cleanWorkingDirectory.cjs"),l=require("../helpers/copyTestCaseFileInput.cjs"),u=require("../helpers/findEntryPointFile.cjs"),d=require("../helpers/judgeByStaticAnalysis.cjs"),f=require("../helpers/readOutputFiles.cjs"),p=require("../helpers/runIsolationCheckInDebugMode.cjs"),m=require("../helpers/resolveCwds.cjs"),h=require("../helpers/spawnSyncWithTimeout.cjs");let g=require("node:path");g=e.__toESM(g,1);let _=require("zod");const v=_.z.object({language:_.z.union([_.z.string(),_.z.array(_.z.string())]).optional()});async function y(e,t={}){let n=r.parseArgs(process.argv),i=v.parse(n.params),{cwds:a,isDebugMode:s}=await m.resolveCwds(e,n.cwd),c=await o.readProblemMarkdownFrontMatter(e),l=await(t.readTestCases??w)(e),u={buildTimeoutSeconds:t.limits?.buildTimeoutSeconds??10,maxOutputLength:t.limits?.maxOutputLength??5e4},d=typeof c.timeLimitMs==`number`?c.timeLimitMs/1e3:t.runTimeoutSeconds??2,f={problemMarkdownFrontMatter:c,testCases:l,limits:u,timeLimitSeconds:d};if(s){let t=(u.buildTimeoutSeconds+l.length*d)*1e3;if(!await p.passesIsolationCheckInDebugMode(e,a,i,{expectedMaxDurationMs:t})){process.exitCode=1;return}}for(let n of a){s&&m.printDebugCwdBanner(e,n);let r=await b(n.cwd,i,f,t);s&&!x(n,r)&&(process.exitCode=1,m.printDebugExpectationFailureBanner(e,n))}}async function b(e,n,r,i){let{problemMarkdownFrontMatter:o,testCases:p,limits:m,timeLimitSeconds:h}=r,g=p[0]?.id??`prebuild`,_=await d.judgeByStaticAnalysis(e,o);if(_)return t.printTestCaseResult({testCaseId:g,..._}),{allAccepted:!1};let v=await u.findEntryPointFile(e,n.language);if(!v)return t.printTestCaseResult({testCaseId:g,decisionCode:s.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${n.language?`: language: ${n.language}`:``}`}),{allAccepted:!1};let y=a.findLanguageDefinitionByPath(v);if(!y)return t.printTestCaseResult({testCaseId:g,decisionCode:s.DecisionCode.WRONG_ANSWER,stderr:`unsupported language`}),{allAccepted:!1};let b={...process.env,CI:``,FORCE_COLOR:`0`},x=v;if(i.buildSubmission!==!1){if(y.prebuild)try{await y.prebuild(e);let t=await u.findEntryPointFile(e,n.language);t&&(x=t)}catch(e){return t.printTestCaseResult({testCaseId:g,decisionCode:s.DecisionCode.BUILD_ERROR,stderr:e instanceof Error?e.message:String(e)}),{allAccepted:!1}}let r=y.buildCommand?.(x);if(r){let n=S(r,{cwd:e,env:b,testCaseId:g,limits:m});if(n)return t.printTestCaseResult(n),{allAccepted:!1}}}let w=await c.snapshotWorkingDirectory(e);if(p.length===0)return t.printTestCaseResult({testCaseId:`default`,decisionCode:s.DecisionCode.ACCEPTED}),{allAccepted:!0};let O=p.shared?.fileInputPath;for(let n of p){O&&await l.copyTestCaseFileInput(O,e),n.fileInputPath&&await l.copyTestCaseFileInput(n.fileInputPath,e);let r=y.command(x),a=n.input??``,u;try{i.resolveInput&&(a=await i.resolveInput({testCase:n,cwd:e,env:b})),u=i.runCommand?await i.runCommand({testCase:n,command:r,stdin:a,cwd:e,env:b,timeLimitSeconds:h}):E(r,{stdin:a,cwd:e,env:b,timeLimitSeconds:h})}catch(r){return t.printTestCaseResult({testCaseId:n.id,decisionCode:s.DecisionCode.RUNTIME_ERROR,stdin:a,stderr:C(r)}),await c.cleanWorkingDirectory(e,w),{allAccepted:!1}}let d=await f.readOutputFiles(e,o.requiredOutputFilePaths??[]),p={timeLimitSeconds:h,outputLimitLength:m.maxOutputLength,problemMarkdownFrontMatter:{memoryLimitByte:o.memoryLimitByte,requiredOutputFilePaths:o.requiredOutputFilePaths}},g=D({runResult:u,outputFiles:d,context:p}),_=g;if(g.decisionCode===s.DecisionCode.ACCEPTED)try{let t=i.test?await i.test({testCase:n,runResult:u,outputFiles:d,cwd:e,context:p}):await T({testCase:n,runResult:u,outputFiles:d,cwd:e});t&&(_={decisionCode:t.decisionCode??g.decisionCode,feedbackMarkdown:t.feedbackMarkdown,stderr:t.stderr})}catch(e){_={decisionCode:s.DecisionCode.RUNTIME_ERROR,stderr:C(e)}}if(t.printTestCaseResult({testCaseId:n.id,..._,exitStatus:u.status,stdin:u.stdin,stdout:u.stdout.slice(0,m.maxOutputLength)||void 0,stderr:(_.stderr??u.stderr).slice(0,m.maxOutputLength)||void 0,timeSeconds:u.timeSeconds,memoryBytes:u.memoryBytes,outputFiles:d.length>0?d:void 0}),await c.cleanWorkingDirectory(e,w),_.decisionCode!==s.DecisionCode.ACCEPTED)return{allAccepted:!1}}return{allAccepted:!0}}function x(e,t){return t.allAccepted===(e.expectedResult===`accepted`)}function S(e,t){let n=h.spawnSyncWithTimeout(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,env:t.env},t.limits.buildTimeoutSeconds),r=n.status??void 0;if(n.status!==0)return{testCaseId:t.testCaseId,decisionCode:s.DecisionCode.BUILD_ERROR,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.timeSeconds>t.limits.buildTimeoutSeconds)return{testCaseId:t.testCaseId,decisionCode:s.DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.stdout.length>t.limits.maxOutputLength||n.stderr.length>t.limits.maxOutputLength)return{testCaseId:t.testCaseId,decisionCode:s.DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function C(e){return e instanceof Error?e.message:String(e)}async function w(e){return await i.readTestCases(g.default.join(e,`test_cases`))}async function T(e){let{testCase:t,runResult:r,outputFiles:i,cwd:a}=e,o=await n.judgeAgainstExpectations({stdout:r.stdout,expectedStdout:typeof t.output==`string`?t.output:void 0,fileOutputPath:typeof t.fileOutputPath==`string`?t.fileOutputPath:void 0,cwd:a,outputFiles:i});return i.splice(0,i.length,...o.outputFiles),o.matches?{}:{decisionCode:s.DecisionCode.WRONG_ANSWER}}function E(e,t){let n=h.spawnSyncWithTimeout(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,input:t.stdin,env:t.env},t.timeLimitSeconds);return{stdin:t.stdin,stdout:n.stdout,stderr:n.stderr,status:n.status??void 0,timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function D(e){if(e.runResult.status!==0)return{decisionCode:s.DecisionCode.RUNTIME_ERROR,stderr:e.runResult.stderr};if(e.runResult.timeSeconds>e.context.timeLimitSeconds)return{decisionCode:s.DecisionCode.TIME_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.memoryBytes>(e.context.problemMarkdownFrontMatter.memoryLimitByte??1/0))return{decisionCode:s.DecisionCode.MEMORY_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.outputLimitExceeded||e.runResult.stdout.length>e.context.outputLimitLength||e.runResult.stderr.length>e.context.outputLimitLength)return{decisionCode:s.DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED,stderr:e.runResult.stderr};let t=e.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length??0;return e.outputFiles.length<t?{decisionCode:s.DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR}:{decisionCode:s.DecisionCode.ACCEPTED}}exports.commandJudgePreset=y;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../helpers/printTestCaseResult.cjs"),n=require("../helpers/compareExpectedOutputFiles.cjs"),r=require("../helpers/parseArgs.cjs"),i=require("../helpers/readTestCases.cjs"),a=require("../helpers/findLanguageDefinitionByPath.cjs"),o=require("../helpers/readProblemMarkdownFrontMatter.cjs"),s=require("../types/decisionCode.cjs"),c=require("../helpers/cleanWorkingDirectory.cjs"),l=require("../helpers/copyTestCaseFileInput.cjs"),u=require("../helpers/findEntryPointFile.cjs"),d=require("../helpers/judgeByStaticAnalysis.cjs"),f=require("../helpers/readOutputFiles.cjs"),p=require("../helpers/runIsolationCheckInDebugMode.cjs"),m=require("../helpers/resolveCwds.cjs"),h=require("../helpers/spawnWithTimeout.cjs");let g=require("node:path");g=e.__toESM(g,1);let _=require("zod");const v=_.z.object({language:_.z.union([_.z.string(),_.z.array(_.z.string())]).optional()});async function y(e,t={}){let n=r.parseArgs(process.argv),i=v.parse(n.params),{cwds:a,isDebugMode:s}=await m.resolveCwds(e,n.cwd),c=await o.readProblemMarkdownFrontMatter(e),l=await(t.readTestCases??w)(e),u={buildTimeoutSeconds:t.limits?.buildTimeoutSeconds??10,maxOutputLength:t.limits?.maxOutputLength??5e4},d=typeof c.timeLimitMs==`number`?c.timeLimitMs/1e3:t.runTimeoutSeconds??2,f={problemMarkdownFrontMatter:c,testCases:l,limits:u,timeLimitSeconds:d};if(s){let t=(u.buildTimeoutSeconds+l.length*d)*1e3;if(!await p.passesIsolationCheckInDebugMode(e,a,i,{expectedMaxDurationMs:t})){process.exitCode=1;return}}for(let n of a){s&&m.printDebugCwdBanner(e,n);let r=await b(n.cwd,i,f,t);s&&!x(n,r)&&(process.exitCode=1,m.printDebugExpectationFailureBanner(e,n))}}async function b(e,n,r,i){let{problemMarkdownFrontMatter:o,testCases:p,limits:m,timeLimitSeconds:h}=r,g=p[0]?.id??`prebuild`,_=await d.judgeByStaticAnalysis(e,o);if(_)return t.printTestCaseResult({testCaseId:g,..._}),{allAccepted:!1};let v=await u.findEntryPointFile(e,n.language);if(!v)return t.printTestCaseResult({testCaseId:g,decisionCode:s.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${n.language?`: language: ${n.language}`:``}`}),{allAccepted:!1};let y=a.findLanguageDefinitionByPath(v);if(!y)return t.printTestCaseResult({testCaseId:g,decisionCode:s.DecisionCode.WRONG_ANSWER,stderr:`unsupported language`}),{allAccepted:!1};let b={...process.env,CI:``,FORCE_COLOR:`0`},x=v;if(i.buildSubmission!==!1){if(y.prebuild)try{await y.prebuild(e);let t=await u.findEntryPointFile(e,n.language);t&&(x=t)}catch(e){return t.printTestCaseResult({testCaseId:g,decisionCode:s.DecisionCode.BUILD_ERROR,stderr:e instanceof Error?e.message:String(e)}),{allAccepted:!1}}let r=y.buildCommand?.(x);if(r){let n=await S(r,{cwd:e,env:b,testCaseId:g,limits:m});if(n)return t.printTestCaseResult(n),{allAccepted:!1}}}let w=await c.snapshotWorkingDirectory(e);if(p.length===0)return t.printTestCaseResult({testCaseId:`default`,decisionCode:s.DecisionCode.ACCEPTED}),{allAccepted:!0};let O=p.shared?.fileInputPath;for(let n of p){O&&await l.copyTestCaseFileInput(O,e),n.fileInputPath&&await l.copyTestCaseFileInput(n.fileInputPath,e);let r=y.command(x),a=n.input??``,u;try{i.resolveInput&&(a=await i.resolveInput({testCase:n,cwd:e,env:b})),i.runCommand&&(u=await i.runCommand({testCase:n,command:r,stdin:a,cwd:e,env:b,timeLimitSeconds:h}))}catch(r){return t.printTestCaseResult({testCaseId:n.id,decisionCode:s.DecisionCode.RUNTIME_ERROR,stdin:a,stderr:C(r)}),await c.cleanWorkingDirectory(e,w),{allAccepted:!1}}let d=u??await E(r,{stdin:a,cwd:e,env:b,timeLimitSeconds:h}),p=await f.readOutputFiles(e,o.requiredOutputFilePaths??[]),g={timeLimitSeconds:h,outputLimitLength:m.maxOutputLength,problemMarkdownFrontMatter:{memoryLimitByte:o.memoryLimitByte,requiredOutputFilePaths:o.requiredOutputFilePaths}},_=D({runResult:d,outputFiles:p,context:g}),v=_;if(_.decisionCode===s.DecisionCode.ACCEPTED)try{let t=i.test?await i.test({testCase:n,runResult:d,outputFiles:p,cwd:e,context:g}):await T({testCase:n,runResult:d,outputFiles:p,cwd:e});t&&(v={decisionCode:t.decisionCode??_.decisionCode,feedbackMarkdown:t.feedbackMarkdown,stderr:t.stderr})}catch(e){v={decisionCode:s.DecisionCode.RUNTIME_ERROR,stderr:C(e)}}if(t.printTestCaseResult({testCaseId:n.id,...v,exitStatus:d.status,stdin:d.stdin,stdout:d.stdout.slice(0,m.maxOutputLength)||void 0,stderr:(v.stderr??d.stderr).slice(0,m.maxOutputLength)||void 0,timeSeconds:d.timeSeconds,cpuTimeSeconds:d.cpuTimeSeconds,memoryBytes:d.memoryBytes,outputFiles:p.length>0?p:void 0}),await c.cleanWorkingDirectory(e,w),v.decisionCode!==s.DecisionCode.ACCEPTED)return{allAccepted:!1}}return{allAccepted:!0}}function x(e,t){return t.allAccepted===(e.expectedResult===`accepted`)}async function S(e,t){let n=await h.spawnWithTimeout(e[0],e.slice(1),{cwd:t.cwd,env:t.env},t.limits.buildTimeoutSeconds),r=n.status??void 0;if(n.status!==0)return{testCaseId:t.testCaseId,decisionCode:s.DecisionCode.BUILD_ERROR,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,cpuTimeSeconds:n.cpuTimeSeconds,memoryBytes:n.memoryBytes};if(n.timeSeconds>t.limits.buildTimeoutSeconds)return{testCaseId:t.testCaseId,decisionCode:s.DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,cpuTimeSeconds:n.cpuTimeSeconds,memoryBytes:n.memoryBytes};if(n.outputLimitExceeded||n.stdout.length>t.limits.maxOutputLength||n.stderr.length>t.limits.maxOutputLength)return{testCaseId:t.testCaseId,decisionCode:s.DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,cpuTimeSeconds:n.cpuTimeSeconds,memoryBytes:n.memoryBytes}}function C(e){return e instanceof Error?e.message:String(e)}async function w(e){return await i.readTestCases(g.default.join(e,`test_cases`))}async function T(e){let{testCase:t,runResult:r,outputFiles:i,cwd:a}=e,o=await n.judgeAgainstExpectations({stdout:r.stdout,expectedStdout:typeof t.output==`string`?t.output:void 0,fileOutputPath:typeof t.fileOutputPath==`string`?t.fileOutputPath:void 0,cwd:a,outputFiles:i});return i.splice(0,i.length,...o.outputFiles),o.matches?{}:{decisionCode:s.DecisionCode.WRONG_ANSWER}}async function E(e,t){let n=await h.spawnWithTimeout(e[0],e.slice(1),{cwd:t.cwd,stdin:t.stdin,env:t.env},t.timeLimitSeconds);return{stdin:t.stdin,stdout:n.stdout,stderr:n.stderr,status:n.status??void 0,timeSeconds:n.timeSeconds,cpuTimeSeconds:n.cpuTimeSeconds,memoryBytes:n.memoryBytes,outputLimitExceeded:n.outputLimitExceeded}}function D(e){if(e.runResult.status!==0)return{decisionCode:s.DecisionCode.RUNTIME_ERROR,stderr:e.runResult.stderr};if(e.runResult.timeSeconds>e.context.timeLimitSeconds)return{decisionCode:s.DecisionCode.TIME_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.memoryBytes>(e.context.problemMarkdownFrontMatter.memoryLimitByte??1/0))return{decisionCode:s.DecisionCode.MEMORY_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.outputLimitExceeded||e.runResult.stdout.length>e.context.outputLimitLength||e.runResult.stderr.length>e.context.outputLimitLength)return{decisionCode:s.DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED,stderr:e.runResult.stderr};let t=e.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length??0;return e.outputFiles.length<t?{decisionCode:s.DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR}:{decisionCode:s.DecisionCode.ACCEPTED}}exports.commandJudgePreset=y;
2
2
  //# sourceMappingURL=command.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"command.cjs","names":["z","parseArgs","resolveCwds","readProblemMarkdownFrontMatter","passesIsolationCheckInDebugMode","judgeByStaticAnalysis","findEntryPointFile","DecisionCode","findLanguageDefinitionByPath","snapshotWorkingDirectory","copyTestCaseFileInput","cleanWorkingDirectory","readOutputFiles","spawnSyncWithTimeout","readFileTestCases","path","judgeAgainstExpectations"],"sources":["../../src/presets/command.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { z } from 'zod';\n\nimport { cleanWorkingDirectory, snapshotWorkingDirectory } from '../helpers/cleanWorkingDirectory.js';\nimport { judgeAgainstExpectations } from '../helpers/compareExpectedOutputFiles.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 { passesIsolationCheckInDebugMode } from '../helpers/runIsolationCheckInDebugMode.js';\nimport { readProblemMarkdownFrontMatter } from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases as readFileTestCases } from '../helpers/readTestCases.js';\nimport {\n printDebugCwdBanner,\n printDebugExpectationFailureBanner,\n resolveCwds,\n type ResolvedCwd,\n} from '../helpers/resolveCwds.js';\nimport { spawnSyncWithTimeout } from '../helpers/spawnSyncWithTimeout.js';\nimport { DecisionCode } from '../types/decisionCode.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 = 2;\nconst MAX_STDOUT_LENGTH = 50_000;\n\nconst judgeParamsSchema = z.object({\n language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\ntype JudgeParams = z.infer<typeof judgeParamsSchema>;\n\n/** What every command test case needs; a custom `readTestCases` may add any fields of its own. */\ninterface BaseCommandTestCase {\n id: string;\n /** Standard input (`test_cases/<id>.in`). */\n input?: string;\n /** Directory copied into the working directory before the run (`test_cases/<id>.fin/`). */\n fileInputPath?: string;\n}\n\n/** A test case read from `test_cases/` by the default reader. */\nexport interface CommandTestCase extends BaseCommandTestCase {\n /** Expected standard output (`test_cases/<id>.out`). */\n output?: string;\n /** Directory of expected output files compared after the run (`test_cases/<id>.fout/`). */\n fileOutputPath?: string;\n}\n\ntype CommandJudgeCaseResult = Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown' | 'stderr'>;\n\nexport interface CommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n outputLimitExceeded?: boolean;\n}\n\ninterface CommandJudgeContext {\n timeLimitSeconds: number;\n outputLimitLength: number;\n problemMarkdownFrontMatter: Pick<ProblemMarkdownFrontMatter, 'memoryLimitByte' | 'requiredOutputFilePaths'>;\n}\n\nexport interface CommandJudgeLimits {\n buildTimeoutSeconds: number;\n maxOutputLength: number;\n}\n\ninterface ResolvedCommandProblem<TTestCase extends BaseCommandTestCase> {\n problemMarkdownFrontMatter: ProblemMarkdownFrontMatter;\n testCases: readonly TTestCase[];\n limits: CommandJudgeLimits;\n timeLimitSeconds: number;\n}\n\nexport interface CommandJudgePresetOptions<\n TTestCase extends BaseCommandTestCase = CommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n> {\n /**\n * Set to false when the custom runner builds the submitted source itself.\n * Defaults to true.\n */\n buildSubmission?: boolean;\n limits?: CommandJudgeLimits;\n /** The per-case time limit used only when `problem.md` declares no `timeLimitMs`. */\n runTimeoutSeconds?: number;\n readTestCases?: (problemDir: string) => Promise<readonly TTestCase[]>;\n resolveInput?: (context: { testCase: TTestCase; cwd: string; env: NodeJS.ProcessEnv }) => Promise<string> | string;\n runCommand?: (context: {\n testCase: TTestCase;\n command: readonly [string, ...string[]];\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }) => Promise<TRunResult> | TRunResult;\n /**\n * Decides the verdict of a run that passed the limit checks. It replaces the default comparison\n * of `testCase.output` (space-separated tokens) and `testCase.fileOutputPath` (see\n * `compareStdoutAsSpaceSeparatedTokens` and `compareExpectedOutputFiles`, both exported), so\n * call those yourself when the test case ships `.out` / `.fout` expectations to keep.\n * `outputFiles` is the array printed with the result and may be edited in place.\n */\n test?: (context: {\n testCase: TTestCase;\n runResult: TRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n /** The submission's working directory, e.g. for `compareExpectedOutputFiles(cwd, testCase.fileOutputPath)`. */\n cwd: string;\n context: CommandJudgeContext;\n }) => Promise<Partial<CommandJudgeCaseResult>> | Partial<CommandJudgeCaseResult> | undefined;\n}\n\n/**\n * A preset function for judging by executable command.\n *\n * Without options, test cases come from `test_cases/` (`.in`, `.out`, `.fin/`, `.fout/`) and a run\n * within the limits is accepted when its stdout matches `.out` and its files match `.fout/`; a\n * case without either expectation only has to run within the limits.\n * Keep problem-specific logic in `resolveInput` and `test`.\n *\n * @example\n * Create `judge.ts` that judges `test_cases/` like the default stdio harness, with the command\n * preset's debug mode:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n *\n * await commandJudgePreset(import.meta.dirname);\n * ```\n *\n * @example\n * Create `judge.ts` with an own verdict:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n * import { DecisionCode } from '@exercode/problem-utils';\n *\n * await commandJudgePreset(import.meta.dirname, {\n * readTestCases: async () => [\n * { id: '01', input: '1 2' },\n * ],\n * test: ({ runResult }) => {\n * return runResult.stdout.trim() === '3'\n * ? { decisionCode: DecisionCode.ACCEPTED }\n * : { decisionCode: DecisionCode.WRONG_ANSWER };\n * },\n * });\n * ```\n *\n * Run with the required parameters:\n * ```bash\n * bun judge.ts model_answers/python '{ \"language\": \"python\" }'\n * ```\n *\n * Run without a cwd argument to judge each `<problemDir>/model_answers/*` directory\n * for debugging. A prominent banner is printed for each run.\n * ```bash\n * bun judge.ts\n * ```\n */\nexport async function commandJudgePreset<\n TTestCase extends BaseCommandTestCase = CommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(problemDir: string, options: CommandJudgePresetOptions<TTestCase, TRunResult> = {}): Promise<void> {\n const args = parseArgs(process.argv);\n const params = judgeParamsSchema.parse(args.params);\n\n const { cwds, isDebugMode } = await resolveCwds(problemDir, args.cwd);\n const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n const testCases = await (options.readTestCases ?? readCommandTestCases)(problemDir);\n const limits = {\n buildTimeoutSeconds: options.limits?.buildTimeoutSeconds ?? BUILD_TIMEOUT_SECONDS,\n maxOutputLength: options.limits?.maxOutputLength ?? MAX_STDOUT_LENGTH,\n };\n const timeLimitSeconds =\n typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n ? problemMarkdownFrontMatter.timeLimitMs / 1000\n : (options.runTimeoutSeconds ?? JUDGE_DEFAULT_TIMEOUT_SECONDS);\n const problem: ResolvedCommandProblem<TTestCase> = {\n problemMarkdownFrontMatter,\n testCases,\n limits,\n timeLimitSeconds,\n };\n\n if (isDebugMode) {\n const expectedMaxDurationMs = (limits.buildTimeoutSeconds + testCases.length * timeLimitSeconds) * 1000;\n if (!(await passesIsolationCheckInDebugMode(problemDir, cwds, params, { expectedMaxDurationMs }))) {\n process.exitCode = 1;\n return;\n }\n }\n\n for (const resolvedCwd of cwds) {\n if (isDebugMode) printDebugCwdBanner(problemDir, resolvedCwd);\n const result = await runCommandJudgeForCwd<TTestCase, TRunResult>(resolvedCwd.cwd, params, problem, options);\n if (isDebugMode && !matchesExpectedResult(resolvedCwd, result)) {\n process.exitCode = 1;\n printDebugExpectationFailureBanner(problemDir, resolvedCwd);\n }\n }\n}\n\nasync function runCommandJudgeForCwd<\n TTestCase extends BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(\n cwd: string,\n params: JudgeParams,\n problem: ResolvedCommandProblem<TTestCase>,\n options: CommandJudgePresetOptions<TTestCase, TRunResult>\n): Promise<{ allAccepted: boolean }> {\n const { problemMarkdownFrontMatter, testCases, limits, timeLimitSeconds } = problem;\n const prebuildTestCaseId = testCases[0]?.id ?? 'prebuild';\n\n const staticAnalysisResult = await judgeByStaticAnalysis(cwd, problemMarkdownFrontMatter);\n if (staticAnalysisResult) {\n printTestCaseResult({ testCaseId: prebuildTestCaseId, ...staticAnalysisResult });\n return { allAccepted: false };\n }\n\n const originalMainFilePath = await findEntryPointFile(cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return { allAccepted: false };\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return { allAccepted: false };\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let mainFilePath = originalMainFilePath;\n if (options.buildSubmission !== false) {\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(cwd);\n const prebuiltMainFilePath = await findEntryPointFile(cwd, params.language);\n if (prebuiltMainFilePath) mainFilePath = prebuiltMainFilePath;\n } catch (error) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return { allAccepted: false };\n }\n }\n\n const buildCommand = languageDefinition.buildCommand?.(mainFilePath);\n if (buildCommand) {\n const buildResult = runBuild(buildCommand, {\n cwd,\n env,\n testCaseId: prebuildTestCaseId,\n limits,\n });\n if (buildResult) {\n printTestCaseResult(buildResult);\n return { allAccepted: false };\n }\n }\n }\n\n const cwdSnapshot = await snapshotWorkingDirectory(cwd);\n\n if (testCases.length === 0) {\n printTestCaseResult({ testCaseId: 'default', decisionCode: DecisionCode.ACCEPTED });\n return { allAccepted: true };\n }\n\n const sharedFileInputPath = (testCases as { shared?: { fileInputPath?: string } }).shared?.fileInputPath;\n\n for (const testCase of testCases) {\n if (sharedFileInputPath) await copyTestCaseFileInput(sharedFileInputPath, cwd);\n if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, cwd);\n\n const command = languageDefinition.command(mainFilePath);\n let stdin = testCase.input ?? '';\n let runResult: TRunResult;\n try {\n if (options.resolveInput) {\n stdin = await options.resolveInput({ testCase, cwd, env });\n }\n\n runResult = options.runCommand\n ? await options.runCommand({ testCase, command, stdin, cwd, env, timeLimitSeconds })\n : (runCommand(command, {\n stdin,\n cwd,\n env,\n timeLimitSeconds,\n }) as TRunResult);\n } catch (error) {\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stdin,\n stderr: errorToMessage(error),\n });\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n return { allAccepted: false };\n }\n\n const outputFiles = await readOutputFiles(cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n const judgeContext: CommandJudgeContext = {\n timeLimitSeconds,\n outputLimitLength: limits.maxOutputLength,\n problemMarkdownFrontMatter: {\n memoryLimitByte: problemMarkdownFrontMatter.memoryLimitByte,\n requiredOutputFilePaths: problemMarkdownFrontMatter.requiredOutputFilePaths,\n },\n };\n const baseJudgeResult = evaluateByLimits({ runResult, outputFiles, context: judgeContext });\n let judgeResult = baseJudgeResult;\n if (baseJudgeResult.decisionCode === DecisionCode.ACCEPTED) {\n try {\n const extendedJudgeResult = options.test\n ? await options.test({ testCase, runResult, outputFiles, cwd, context: judgeContext })\n : await compareWithExpectedOutputs({ testCase, runResult, outputFiles, cwd });\n if (extendedJudgeResult) {\n judgeResult = {\n decisionCode: extendedJudgeResult.decisionCode ?? baseJudgeResult.decisionCode,\n feedbackMarkdown: extendedJudgeResult.feedbackMarkdown,\n stderr: extendedJudgeResult.stderr,\n };\n }\n } catch (error) {\n judgeResult = {\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stderr: errorToMessage(error),\n };\n }\n }\n\n printTestCaseResult({\n testCaseId: testCase.id,\n ...judgeResult,\n exitStatus: runResult.status,\n stdin: runResult.stdin,\n stdout: runResult.stdout.slice(0, limits.maxOutputLength) || undefined,\n stderr: (judgeResult.stderr ?? runResult.stderr).slice(0, limits.maxOutputLength) || undefined,\n timeSeconds: runResult.timeSeconds,\n memoryBytes: runResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n if (judgeResult.decisionCode !== DecisionCode.ACCEPTED) return { allAccepted: false };\n }\n\n return { allAccepted: true };\n}\n\nfunction matchesExpectedResult(resolvedCwd: ResolvedCwd, result: { allAccepted: boolean }): boolean {\n return result.allAccepted === (resolvedCwd.expectedResult === 'accepted');\n}\n\nfunction runBuild(\n buildCommand: readonly [string, ...string[]],\n context: { cwd: string; env: NodeJS.ProcessEnv; testCaseId: string; limits: CommandJudgeLimits }\n): (Omit<TestCaseResult, 'testCaseId'> & { testCaseId: string }) | undefined {\n const spawnResult = spawnSyncWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd: context.cwd, encoding: 'utf8', env: context.env },\n context.limits.buildTimeoutSeconds\n );\n const exitStatus = spawnResult.status ?? undefined;\n\n if (spawnResult.status !== 0) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (spawnResult.timeSeconds > context.limits.buildTimeoutSeconds) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (\n spawnResult.stdout.length > context.limits.maxOutputLength ||\n spawnResult.stderr.length > context.limits.maxOutputLength\n ) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n return;\n}\n\nfunction errorToMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nasync function readCommandTestCases<TTestCase extends BaseCommandTestCase = CommandTestCase>(\n problemDir: string\n): Promise<readonly TTestCase[]> {\n // The default reader yields the base shape; a narrower TTestCase must come from `options.readTestCases`.\n return (await readFileTestCases(path.join(problemDir, 'test_cases'))) as unknown as readonly TTestCase[];\n}\n\n/** The default verdict: a case exposing a string `output` / `fileOutputPath` is judged against it. */\nasync function compareWithExpectedOutputs(context: {\n testCase: BaseCommandTestCase & Partial<CommandTestCase>;\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n cwd: string;\n}): Promise<Partial<CommandJudgeCaseResult>> {\n const { testCase, runResult, outputFiles, cwd } = context;\n const judgement = await judgeAgainstExpectations({\n stdout: runResult.stdout,\n expectedStdout: typeof testCase.output === 'string' ? testCase.output : undefined,\n fileOutputPath: typeof testCase.fileOutputPath === 'string' ? testCase.fileOutputPath : undefined,\n cwd,\n outputFiles,\n });\n outputFiles.splice(0, outputFiles.length, ...judgement.outputFiles);\n return judgement.matches ? {} : { decisionCode: DecisionCode.WRONG_ANSWER };\n}\n\nfunction runCommand(\n command: readonly [string, ...string[]],\n context: {\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }\n): CommandRunResult {\n const spawnResult = spawnSyncWithTimeout(\n command[0],\n command.slice(1),\n { cwd: context.cwd, encoding: 'utf8', input: context.stdin, env: context.env },\n context.timeLimitSeconds\n );\n\n return {\n stdin: context.stdin,\n stdout: spawnResult.stdout,\n stderr: spawnResult.stderr,\n status: spawnResult.status ?? undefined,\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n}\n\nfunction evaluateByLimits(context: {\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n}): CommandJudgeCaseResult {\n if (context.runResult.status !== 0) {\n return { decisionCode: DecisionCode.RUNTIME_ERROR, stderr: context.runResult.stderr };\n }\n\n if (context.runResult.timeSeconds > context.context.timeLimitSeconds) {\n return { decisionCode: DecisionCode.TIME_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.memoryBytes >\n (context.context.problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)\n ) {\n return { decisionCode: DecisionCode.MEMORY_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.outputLimitExceeded ||\n context.runResult.stdout.length > context.context.outputLimitLength ||\n context.runResult.stderr.length > context.context.outputLimitLength\n ) {\n return { decisionCode: DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n const requiredOutputFileCount = context.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0;\n if (context.outputFiles.length < requiredOutputFileCount) {\n return { decisionCode: DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR };\n }\n\n return { decisionCode: DecisionCode.ACCEPTED };\n}\n"],"mappings":"25BA2BA,MAIM,EAAoBA,EAAAA,EAAE,OAAO,CACjC,SAAUA,EAAAA,EAAE,MAAM,CAACA,EAAAA,EAAE,OAAO,EAAGA,EAAAA,EAAE,MAAMA,EAAAA,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAChE,CAAC,EAwID,eAAsB,EAGpB,EAAoB,EAA4D,CAAC,EAAkB,CACnG,IAAM,EAAOC,EAAAA,UAAU,QAAQ,IAAI,EAC7B,EAAS,EAAkB,MAAM,EAAK,MAAM,EAE5C,CAAE,OAAM,eAAgB,MAAMC,EAAAA,YAAY,EAAY,EAAK,GAAG,EAC9D,EAA6B,MAAMC,EAAAA,+BAA+B,CAAU,EAC5E,EAAY,MAAO,EAAQ,eAAiB,EAAA,CAAsB,CAAU,EAC5E,EAAS,CACb,oBAAqB,EAAQ,QAAQ,qBAAuB,GAC5D,gBAAiB,EAAQ,QAAQ,iBAAmB,GACtD,EACM,EACJ,OAAO,EAA2B,aAAgB,SAC9C,EAA2B,YAAc,IACxC,EAAQ,mBAAqB,EAC9B,EAA6C,CACjD,6BACA,YACA,SACA,kBACF,EAEA,GAAI,EAAa,CACf,IAAM,GAAyB,EAAO,oBAAsB,EAAU,OAAS,GAAoB,IACnG,GAAI,CAAE,MAAMC,EAAAA,gCAAgC,EAAY,EAAM,EAAQ,CAAE,uBAAsB,CAAC,EAAI,CACjG,QAAQ,SAAW,EACnB,MACF,CACF,CAEA,IAAK,IAAM,KAAe,EAAM,CAC1B,GAAa,EAAA,oBAAoB,EAAY,CAAW,EAC5D,IAAM,EAAS,MAAM,EAA6C,EAAY,IAAK,EAAQ,EAAS,CAAO,EACvG,GAAe,CAAC,EAAsB,EAAa,CAAM,IAC3D,QAAQ,SAAW,EACnB,EAAA,mCAAmC,EAAY,CAAW,EAE9D,CACF,CAEA,eAAe,EAIb,EACA,EACA,EACA,EACmC,CACnC,GAAM,CAAE,6BAA4B,YAAW,SAAQ,oBAAqB,EACtE,EAAqB,EAAU,EAAE,EAAE,IAAM,WAEzC,EAAuB,MAAMC,EAAAA,sBAAsB,EAAK,CAA0B,EACxF,GAAI,EAEF,OADA,EAAA,oBAAoB,CAAE,WAAY,EAAoB,GAAG,CAAqB,CAAC,EACxE,CAAE,YAAa,EAAM,EAG9B,IAAM,EAAuB,MAAMC,EAAAA,mBAAmB,EAAK,EAAO,QAAQ,EAC1E,GAAI,CAAC,EAMH,OALA,EAAA,oBAAoB,CAClB,WAAY,EACZ,aAAcC,EAAAA,aAAa,uCAC3B,OAAQ,sBAAsB,EAAO,SAAW,eAAe,EAAO,WAAa,IACrF,CAAC,EACM,CAAE,YAAa,EAAM,EAG9B,IAAM,EAAqBC,EAAAA,6BAA6B,CAAoB,EAC5E,GAAI,CAAC,EAMH,OALA,EAAA,oBAAoB,CAClB,WAAY,EACZ,aAAcD,EAAAA,aAAa,aAC3B,OAAQ,sBACV,CAAC,EACM,CAAE,YAAa,EAAM,EAI9B,IAAM,EAAM,CAAE,GAAG,QAAQ,IAAK,GAAI,GAAI,YAAa,GAAI,EAEnD,EAAe,EACnB,GAAI,EAAQ,kBAAoB,GAAO,CACrC,GAAI,EAAmB,SACrB,GAAI,CACF,MAAM,EAAmB,SAAS,CAAG,EACrC,IAAM,EAAuB,MAAMD,EAAAA,mBAAmB,EAAK,EAAO,QAAQ,EACtE,IAAsB,EAAe,EAC3C,OAAS,EAAO,CAMd,OALA,EAAA,oBAAoB,CAClB,WAAY,EACZ,aAAcC,EAAAA,aAAa,YAC3B,OAAQ,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,CAC/D,CAAC,EACM,CAAE,YAAa,EAAM,CAC9B,CAGF,IAAM,EAAe,EAAmB,eAAe,CAAY,EACnE,GAAI,EAAc,CAChB,IAAM,EAAc,EAAS,EAAc,CACzC,MACA,MACA,WAAY,EACZ,QACF,CAAC,EACD,GAAI,EAEF,OADA,EAAA,oBAAoB,CAAW,EACxB,CAAE,YAAa,EAAM,CAEhC,CACF,CAEA,IAAM,EAAc,MAAME,EAAAA,yBAAyB,CAAG,EAEtD,GAAI,EAAU,SAAW,EAEvB,OADA,EAAA,oBAAoB,CAAE,WAAY,UAAW,aAAcF,EAAAA,aAAa,QAAS,CAAC,EAC3E,CAAE,YAAa,EAAK,EAG7B,IAAM,EAAuB,EAAsD,QAAQ,cAE3F,IAAK,IAAM,KAAY,EAAW,CAC5B,GAAqB,MAAMG,EAAAA,sBAAsB,EAAqB,CAAG,EACzE,EAAS,eAAe,MAAMA,EAAAA,sBAAsB,EAAS,cAAe,CAAG,EAEnF,IAAM,EAAU,EAAmB,QAAQ,CAAY,EACnD,EAAQ,EAAS,OAAS,GAC1B,EACJ,GAAI,CACE,EAAQ,eACV,EAAQ,MAAM,EAAQ,aAAa,CAAE,WAAU,MAAK,KAAI,CAAC,GAG3D,EAAY,EAAQ,WAChB,MAAM,EAAQ,WAAW,CAAE,WAAU,UAAS,QAAO,MAAK,MAAK,kBAAiB,CAAC,EAChF,EAAW,EAAS,CACnB,QACA,MACA,MACA,kBACF,CAAC,CACP,OAAS,EAAO,CAQd,OAPA,EAAA,oBAAoB,CAClB,WAAY,EAAS,GACrB,aAAcH,EAAAA,aAAa,cAC3B,QACA,OAAQ,EAAe,CAAK,CAC9B,CAAC,EACD,MAAMI,EAAAA,sBAAsB,EAAK,CAAW,EACrC,CAAE,YAAa,EAAM,CAC9B,CAEA,IAAM,EAAc,MAAMC,EAAAA,gBAAgB,EAAK,EAA2B,yBAA2B,CAAC,CAAC,EACjG,EAAoC,CACxC,mBACA,kBAAmB,EAAO,gBAC1B,2BAA4B,CAC1B,gBAAiB,EAA2B,gBAC5C,wBAAyB,EAA2B,uBACtD,CACF,EACM,EAAkB,EAAiB,CAAE,YAAW,cAAa,QAAS,CAAa,CAAC,EACtF,EAAc,EAClB,GAAI,EAAgB,eAAiBL,EAAAA,aAAa,SAChD,GAAI,CACF,IAAM,EAAsB,EAAQ,KAChC,MAAM,EAAQ,KAAK,CAAE,WAAU,YAAW,cAAa,MAAK,QAAS,CAAa,CAAC,EACnF,MAAM,EAA2B,CAAE,WAAU,YAAW,cAAa,KAAI,CAAC,EAC1E,IACF,EAAc,CACZ,aAAc,EAAoB,cAAgB,EAAgB,aAClE,iBAAkB,EAAoB,iBACtC,OAAQ,EAAoB,MAC9B,EAEJ,OAAS,EAAO,CACd,EAAc,CACZ,aAAcA,EAAAA,aAAa,cAC3B,OAAQ,EAAe,CAAK,CAC9B,CACF,CAgBF,GAbA,EAAA,oBAAoB,CAClB,WAAY,EAAS,GACrB,GAAG,EACH,WAAY,EAAU,OACtB,MAAO,EAAU,MACjB,OAAQ,EAAU,OAAO,MAAM,EAAG,EAAO,eAAe,GAAK,IAAA,GAC7D,QAAS,EAAY,QAAU,EAAU,OAAA,CAAQ,MAAM,EAAG,EAAO,eAAe,GAAK,IAAA,GACrF,YAAa,EAAU,YACvB,YAAa,EAAU,YACvB,YAAa,EAAY,OAAS,EAAI,EAAc,IAAA,EACtD,CAAC,EAED,MAAMI,EAAAA,sBAAsB,EAAK,CAAW,EACxC,EAAY,eAAiBJ,EAAAA,aAAa,SAAU,MAAO,CAAE,YAAa,EAAM,CACtF,CAEA,MAAO,CAAE,YAAa,EAAK,CAC7B,CAEA,SAAS,EAAsB,EAA0B,EAA2C,CAClG,OAAO,EAAO,eAAiB,EAAY,iBAAmB,WAChE,CAEA,SAAS,EACP,EACA,EAC2E,CAC3E,IAAM,EAAcM,EAAAA,qBAClB,EAAa,GACb,EAAa,MAAM,CAAC,EACpB,CAAE,IAAK,EAAQ,IAAK,SAAU,OAAQ,IAAK,EAAQ,GAAI,EACvD,EAAQ,OAAO,mBACjB,EACM,EAAa,EAAY,QAAU,IAAA,GAEzC,GAAI,EAAY,SAAW,EACzB,MAAO,CACL,WAAY,EAAQ,WACpB,aAAcN,EAAAA,aAAa,YAC3B,aACA,OAAQ,EAAY,OAAO,MAAM,EAAG,EAAQ,OAAO,eAAe,EAClE,OAAQ,EAAY,OAAO,MAAM,EAAG,EAAQ,OAAO,eAAe,EAClE,YAAa,EAAY,YACzB,YAAa,EAAY,WAC3B,EAGF,GAAI,EAAY,YAAc,EAAQ,OAAO,oBAC3C,MAAO,CACL,WAAY,EAAQ,WACpB,aAAcA,EAAAA,aAAa,0BAC3B,aACA,OAAQ,EAAY,OAAO,MAAM,EAAG,EAAQ,OAAO,eAAe,EAClE,OAAQ,EAAY,OAAO,MAAM,EAAG,EAAQ,OAAO,eAAe,EAClE,YAAa,EAAY,YACzB,YAAa,EAAY,WAC3B,EAGF,GACE,EAAY,OAAO,OAAS,EAAQ,OAAO,iBAC3C,EAAY,OAAO,OAAS,EAAQ,OAAO,gBAE3C,MAAO,CACL,WAAY,EAAQ,WACpB,aAAcA,EAAAA,aAAa,iCAC3B,aACA,OAAQ,EAAY,OAAO,MAAM,EAAG,EAAQ,OAAO,eAAe,EAClE,OAAQ,EAAY,OAAO,MAAM,EAAG,EAAQ,OAAO,eAAe,EAClE,YAAa,EAAY,YACzB,YAAa,EAAY,WAC3B,CAIJ,CAEA,SAAS,EAAe,EAAwB,CAC9C,OAAO,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,CAC9D,CAEA,eAAe,EACb,EAC+B,CAE/B,OAAQ,MAAMO,EAAAA,cAAkBC,EAAAA,QAAK,KAAK,EAAY,YAAY,CAAC,CACrE,CAGA,eAAe,EAA2B,EAKG,CAC3C,GAAM,CAAE,WAAU,YAAW,cAAa,OAAQ,EAC5C,EAAY,MAAMC,EAAAA,yBAAyB,CAC/C,OAAQ,EAAU,OAClB,eAAgB,OAAO,EAAS,QAAW,SAAW,EAAS,OAAS,IAAA,GACxE,eAAgB,OAAO,EAAS,gBAAmB,SAAW,EAAS,eAAiB,IAAA,GACxF,MACA,aACF,CAAC,EAED,OADA,EAAY,OAAO,EAAG,EAAY,OAAQ,GAAG,EAAU,WAAW,EAC3D,EAAU,QAAU,CAAC,EAAI,CAAE,aAAcT,EAAAA,aAAa,YAAa,CAC5E,CAEA,SAAS,EACP,EACA,EAMkB,CAClB,IAAM,EAAcM,EAAAA,qBAClB,EAAQ,GACR,EAAQ,MAAM,CAAC,EACf,CAAE,IAAK,EAAQ,IAAK,SAAU,OAAQ,MAAO,EAAQ,MAAO,IAAK,EAAQ,GAAI,EAC7E,EAAQ,gBACV,EAEA,MAAO,CACL,MAAO,EAAQ,MACf,OAAQ,EAAY,OACpB,OAAQ,EAAY,OACpB,OAAQ,EAAY,QAAU,IAAA,GAC9B,YAAa,EAAY,YACzB,YAAa,EAAY,WAC3B,CACF,CAEA,SAAS,EAAiB,EAIC,CACzB,GAAI,EAAQ,UAAU,SAAW,EAC/B,MAAO,CAAE,aAAcN,EAAAA,aAAa,cAAe,OAAQ,EAAQ,UAAU,MAAO,EAGtF,GAAI,EAAQ,UAAU,YAAc,EAAQ,QAAQ,iBAClD,MAAO,CAAE,aAAcA,EAAAA,aAAa,oBAAqB,OAAQ,EAAQ,UAAU,MAAO,EAG5F,GACE,EAAQ,UAAU,aACjB,EAAQ,QAAQ,2BAA2B,iBAAmB,KAE/D,MAAO,CAAE,aAAcA,EAAAA,aAAa,sBAAuB,OAAQ,EAAQ,UAAU,MAAO,EAG9F,GACE,EAAQ,UAAU,qBAClB,EAAQ,UAAU,OAAO,OAAS,EAAQ,QAAQ,mBAClD,EAAQ,UAAU,OAAO,OAAS,EAAQ,QAAQ,kBAElD,MAAO,CAAE,aAAcA,EAAAA,aAAa,2BAA4B,OAAQ,EAAQ,UAAU,MAAO,EAGnG,IAAM,EAA0B,EAAQ,QAAQ,2BAA2B,yBAAyB,QAAU,EAK9G,OAJI,EAAQ,YAAY,OAAS,EACxB,CAAE,aAAcA,EAAAA,aAAa,kCAAmC,EAGlE,CAAE,aAAcA,EAAAA,aAAa,QAAS,CAC/C"}
1
+ {"version":3,"file":"command.cjs","names":["z","parseArgs","resolveCwds","readProblemMarkdownFrontMatter","passesIsolationCheckInDebugMode","judgeByStaticAnalysis","findEntryPointFile","DecisionCode","findLanguageDefinitionByPath","snapshotWorkingDirectory","copyTestCaseFileInput","cleanWorkingDirectory","readOutputFiles","spawnWithTimeout","readFileTestCases","path","judgeAgainstExpectations"],"sources":["../../src/presets/command.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { z } from 'zod';\n\nimport { cleanWorkingDirectory, snapshotWorkingDirectory } from '../helpers/cleanWorkingDirectory.js';\nimport { judgeAgainstExpectations } from '../helpers/compareExpectedOutputFiles.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 { passesIsolationCheckInDebugMode } from '../helpers/runIsolationCheckInDebugMode.js';\nimport { readProblemMarkdownFrontMatter } from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases as readFileTestCases } from '../helpers/readTestCases.js';\nimport {\n printDebugCwdBanner,\n printDebugExpectationFailureBanner,\n resolveCwds,\n type ResolvedCwd,\n} from '../helpers/resolveCwds.js';\nimport { spawnWithTimeout } from '../helpers/spawnWithTimeout.js';\nimport { DecisionCode } from '../types/decisionCode.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 = 2;\nconst MAX_STDOUT_LENGTH = 50_000;\n\nconst judgeParamsSchema = z.object({\n language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\ntype JudgeParams = z.infer<typeof judgeParamsSchema>;\n\n/** What every command test case needs; a custom `readTestCases` may add any fields of its own. */\ninterface BaseCommandTestCase {\n id: string;\n /** Standard input (`test_cases/<id>.in`). */\n input?: string;\n /** Directory copied into the working directory before the run (`test_cases/<id>.fin/`). */\n fileInputPath?: string;\n}\n\n/** A test case read from `test_cases/` by the default reader. */\nexport interface CommandTestCase extends BaseCommandTestCase {\n /** Expected standard output (`test_cases/<id>.out`). */\n output?: string;\n /** Directory of expected output files compared after the run (`test_cases/<id>.fout/`). */\n fileOutputPath?: string;\n}\n\ntype CommandJudgeCaseResult = Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown' | 'stderr'>;\n\nexport interface CommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n /** See `TestCaseResult.cpuTimeSeconds`; a custom `runCommand` may leave it out. */\n cpuTimeSeconds?: number;\n memoryBytes: number;\n outputLimitExceeded?: boolean;\n}\n\ninterface CommandJudgeContext {\n timeLimitSeconds: number;\n outputLimitLength: number;\n problemMarkdownFrontMatter: Pick<ProblemMarkdownFrontMatter, 'memoryLimitByte' | 'requiredOutputFilePaths'>;\n}\n\nexport interface CommandJudgeLimits {\n buildTimeoutSeconds: number;\n maxOutputLength: number;\n}\n\ninterface ResolvedCommandProblem<TTestCase extends BaseCommandTestCase> {\n problemMarkdownFrontMatter: ProblemMarkdownFrontMatter;\n testCases: readonly TTestCase[];\n limits: CommandJudgeLimits;\n timeLimitSeconds: number;\n}\n\nexport interface CommandJudgePresetOptions<\n TTestCase extends BaseCommandTestCase = CommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n> {\n /**\n * Set to false when the custom runner builds the submitted source itself.\n * Defaults to true.\n */\n buildSubmission?: boolean;\n limits?: CommandJudgeLimits;\n /** The per-case time limit used only when `problem.md` declares no `timeLimitMs`. */\n runTimeoutSeconds?: number;\n readTestCases?: (problemDir: string) => Promise<readonly TTestCase[]>;\n resolveInput?: (context: { testCase: TTestCase; cwd: string; env: NodeJS.ProcessEnv }) => Promise<string> | string;\n runCommand?: (context: {\n testCase: TTestCase;\n command: readonly [string, ...string[]];\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }) => Promise<TRunResult> | TRunResult;\n /**\n * Decides the verdict of a run that passed the limit checks. It replaces the default comparison\n * of `testCase.output` (space-separated tokens) and `testCase.fileOutputPath` (see\n * `compareStdoutAsSpaceSeparatedTokens` and `compareExpectedOutputFiles`, both exported), so\n * call those yourself when the test case ships `.out` / `.fout` expectations to keep.\n * `outputFiles` is the array printed with the result and may be edited in place.\n */\n test?: (context: {\n testCase: TTestCase;\n runResult: TRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n /** The submission's working directory, e.g. for `compareExpectedOutputFiles(cwd, testCase.fileOutputPath)`. */\n cwd: string;\n context: CommandJudgeContext;\n }) => Promise<Partial<CommandJudgeCaseResult>> | Partial<CommandJudgeCaseResult> | undefined;\n}\n\n/**\n * A preset function for judging by executable command.\n *\n * Without options, test cases come from `test_cases/` (`.in`, `.out`, `.fin/`, `.fout/`) and a run\n * within the limits is accepted when its stdout matches `.out` and its files match `.fout/`; a\n * case without either expectation only has to run within the limits.\n * Keep problem-specific logic in `resolveInput` and `test`.\n *\n * @example\n * Create `judge.ts` that judges `test_cases/` like the default stdio harness, with the command\n * preset's debug mode:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n *\n * await commandJudgePreset(import.meta.dirname);\n * ```\n *\n * @example\n * Create `judge.ts` with an own verdict:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n * import { DecisionCode } from '@exercode/problem-utils';\n *\n * await commandJudgePreset(import.meta.dirname, {\n * readTestCases: async () => [\n * { id: '01', input: '1 2' },\n * ],\n * test: ({ runResult }) => {\n * return runResult.stdout.trim() === '3'\n * ? { decisionCode: DecisionCode.ACCEPTED }\n * : { decisionCode: DecisionCode.WRONG_ANSWER };\n * },\n * });\n * ```\n *\n * Run with the required parameters:\n * ```bash\n * bun judge.ts model_answers/python '{ \"language\": \"python\" }'\n * ```\n *\n * Run without a cwd argument to judge each `<problemDir>/model_answers/*` directory\n * for debugging. A prominent banner is printed for each run.\n * ```bash\n * bun judge.ts\n * ```\n */\nexport async function commandJudgePreset<\n TTestCase extends BaseCommandTestCase = CommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(problemDir: string, options: CommandJudgePresetOptions<TTestCase, TRunResult> = {}): Promise<void> {\n const args = parseArgs(process.argv);\n const params = judgeParamsSchema.parse(args.params);\n\n const { cwds, isDebugMode } = await resolveCwds(problemDir, args.cwd);\n const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n const testCases = await (options.readTestCases ?? readCommandTestCases)(problemDir);\n const limits = {\n buildTimeoutSeconds: options.limits?.buildTimeoutSeconds ?? BUILD_TIMEOUT_SECONDS,\n maxOutputLength: options.limits?.maxOutputLength ?? MAX_STDOUT_LENGTH,\n };\n const timeLimitSeconds =\n typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n ? problemMarkdownFrontMatter.timeLimitMs / 1000\n : (options.runTimeoutSeconds ?? JUDGE_DEFAULT_TIMEOUT_SECONDS);\n const problem: ResolvedCommandProblem<TTestCase> = {\n problemMarkdownFrontMatter,\n testCases,\n limits,\n timeLimitSeconds,\n };\n\n if (isDebugMode) {\n const expectedMaxDurationMs = (limits.buildTimeoutSeconds + testCases.length * timeLimitSeconds) * 1000;\n if (!(await passesIsolationCheckInDebugMode(problemDir, cwds, params, { expectedMaxDurationMs }))) {\n process.exitCode = 1;\n return;\n }\n }\n\n for (const resolvedCwd of cwds) {\n if (isDebugMode) printDebugCwdBanner(problemDir, resolvedCwd);\n const result = await runCommandJudgeForCwd<TTestCase, TRunResult>(resolvedCwd.cwd, params, problem, options);\n if (isDebugMode && !matchesExpectedResult(resolvedCwd, result)) {\n process.exitCode = 1;\n printDebugExpectationFailureBanner(problemDir, resolvedCwd);\n }\n }\n}\n\nasync function runCommandJudgeForCwd<\n TTestCase extends BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(\n cwd: string,\n params: JudgeParams,\n problem: ResolvedCommandProblem<TTestCase>,\n options: CommandJudgePresetOptions<TTestCase, TRunResult>\n): Promise<{ allAccepted: boolean }> {\n const { problemMarkdownFrontMatter, testCases, limits, timeLimitSeconds } = problem;\n const prebuildTestCaseId = testCases[0]?.id ?? 'prebuild';\n\n const staticAnalysisResult = await judgeByStaticAnalysis(cwd, problemMarkdownFrontMatter);\n if (staticAnalysisResult) {\n printTestCaseResult({ testCaseId: prebuildTestCaseId, ...staticAnalysisResult });\n return { allAccepted: false };\n }\n\n const originalMainFilePath = await findEntryPointFile(cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return { allAccepted: false };\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return { allAccepted: false };\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let mainFilePath = originalMainFilePath;\n if (options.buildSubmission !== false) {\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(cwd);\n const prebuiltMainFilePath = await findEntryPointFile(cwd, params.language);\n if (prebuiltMainFilePath) mainFilePath = prebuiltMainFilePath;\n } catch (error) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return { allAccepted: false };\n }\n }\n\n const buildCommand = languageDefinition.buildCommand?.(mainFilePath);\n if (buildCommand) {\n const buildResult = await runBuild(buildCommand, {\n cwd,\n env,\n testCaseId: prebuildTestCaseId,\n limits,\n });\n if (buildResult) {\n printTestCaseResult(buildResult);\n return { allAccepted: false };\n }\n }\n }\n\n const cwdSnapshot = await snapshotWorkingDirectory(cwd);\n\n if (testCases.length === 0) {\n printTestCaseResult({ testCaseId: 'default', decisionCode: DecisionCode.ACCEPTED });\n return { allAccepted: true };\n }\n\n const sharedFileInputPath = (testCases as { shared?: { fileInputPath?: string } }).shared?.fileInputPath;\n\n for (const testCase of testCases) {\n if (sharedFileInputPath) await copyTestCaseFileInput(sharedFileInputPath, cwd);\n if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, cwd);\n\n const command = languageDefinition.command(mainFilePath);\n let stdin = testCase.input ?? '';\n let customRunResult: TRunResult | undefined;\n try {\n if (options.resolveInput) {\n stdin = await options.resolveInput({ testCase, cwd, env });\n }\n\n if (options.runCommand) {\n customRunResult = await options.runCommand({ testCase, command, stdin, cwd, env, timeLimitSeconds });\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(cwd, cwdSnapshot);\n return { allAccepted: false };\n }\n\n // The default runner reports the submission's failures in its result; what it throws is the\n // judge's own failure and ends the run without a verdict.\n const runResult =\n customRunResult ?? ((await runCommand(command, { stdin, cwd, env, timeLimitSeconds })) as TRunResult);\n\n const outputFiles = await readOutputFiles(cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n const judgeContext: CommandJudgeContext = {\n timeLimitSeconds,\n outputLimitLength: limits.maxOutputLength,\n problemMarkdownFrontMatter: {\n memoryLimitByte: problemMarkdownFrontMatter.memoryLimitByte,\n requiredOutputFilePaths: problemMarkdownFrontMatter.requiredOutputFilePaths,\n },\n };\n const baseJudgeResult = evaluateByLimits({ runResult, outputFiles, context: judgeContext });\n let judgeResult = baseJudgeResult;\n if (baseJudgeResult.decisionCode === DecisionCode.ACCEPTED) {\n try {\n const extendedJudgeResult = options.test\n ? await options.test({ testCase, runResult, outputFiles, cwd, context: judgeContext })\n : await compareWithExpectedOutputs({ testCase, runResult, outputFiles, cwd });\n if (extendedJudgeResult) {\n judgeResult = {\n decisionCode: extendedJudgeResult.decisionCode ?? baseJudgeResult.decisionCode,\n feedbackMarkdown: extendedJudgeResult.feedbackMarkdown,\n stderr: extendedJudgeResult.stderr,\n };\n }\n } catch (error) {\n judgeResult = {\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stderr: errorToMessage(error),\n };\n }\n }\n\n printTestCaseResult({\n testCaseId: testCase.id,\n ...judgeResult,\n exitStatus: runResult.status,\n stdin: runResult.stdin,\n stdout: runResult.stdout.slice(0, limits.maxOutputLength) || undefined,\n stderr: (judgeResult.stderr ?? runResult.stderr).slice(0, limits.maxOutputLength) || undefined,\n timeSeconds: runResult.timeSeconds,\n cpuTimeSeconds: runResult.cpuTimeSeconds,\n memoryBytes: runResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n if (judgeResult.decisionCode !== DecisionCode.ACCEPTED) return { allAccepted: false };\n }\n\n return { allAccepted: true };\n}\n\nfunction matchesExpectedResult(resolvedCwd: ResolvedCwd, result: { allAccepted: boolean }): boolean {\n return result.allAccepted === (resolvedCwd.expectedResult === 'accepted');\n}\n\nasync function runBuild(\n buildCommand: readonly [string, ...string[]],\n context: { cwd: string; env: NodeJS.ProcessEnv; testCaseId: string; limits: CommandJudgeLimits }\n): Promise<(Omit<TestCaseResult, 'testCaseId'> & { testCaseId: string }) | undefined> {\n const spawnResult = await spawnWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd: context.cwd, env: context.env },\n context.limits.buildTimeoutSeconds\n );\n const exitStatus = spawnResult.status ?? undefined;\n\n if (spawnResult.status !== 0) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (spawnResult.timeSeconds > context.limits.buildTimeoutSeconds) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (\n spawnResult.outputLimitExceeded ||\n spawnResult.stdout.length > context.limits.maxOutputLength ||\n spawnResult.stderr.length > context.limits.maxOutputLength\n ) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n return;\n}\n\nfunction errorToMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nasync function readCommandTestCases<TTestCase extends BaseCommandTestCase = CommandTestCase>(\n problemDir: string\n): Promise<readonly TTestCase[]> {\n // The default reader yields the base shape; a narrower TTestCase must come from `options.readTestCases`.\n return (await readFileTestCases(path.join(problemDir, 'test_cases'))) as unknown as readonly TTestCase[];\n}\n\n/** The default verdict: a case exposing a string `output` / `fileOutputPath` is judged against it. */\nasync function compareWithExpectedOutputs(context: {\n testCase: BaseCommandTestCase & Partial<CommandTestCase>;\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n cwd: string;\n}): Promise<Partial<CommandJudgeCaseResult>> {\n const { testCase, runResult, outputFiles, cwd } = context;\n const judgement = await judgeAgainstExpectations({\n stdout: runResult.stdout,\n expectedStdout: typeof testCase.output === 'string' ? testCase.output : undefined,\n fileOutputPath: typeof testCase.fileOutputPath === 'string' ? testCase.fileOutputPath : undefined,\n cwd,\n outputFiles,\n });\n outputFiles.splice(0, outputFiles.length, ...judgement.outputFiles);\n return judgement.matches ? {} : { decisionCode: DecisionCode.WRONG_ANSWER };\n}\n\nasync function runCommand(\n command: readonly [string, ...string[]],\n context: {\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }\n): Promise<CommandRunResult> {\n const spawnResult = await spawnWithTimeout(\n command[0],\n command.slice(1),\n { cwd: context.cwd, stdin: context.stdin, env: context.env },\n context.timeLimitSeconds\n );\n\n return {\n stdin: context.stdin,\n stdout: spawnResult.stdout,\n stderr: spawnResult.stderr,\n status: spawnResult.status ?? undefined,\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n outputLimitExceeded: spawnResult.outputLimitExceeded,\n };\n}\n\nfunction evaluateByLimits(context: {\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n}): CommandJudgeCaseResult {\n if (context.runResult.status !== 0) {\n return { decisionCode: DecisionCode.RUNTIME_ERROR, stderr: context.runResult.stderr };\n }\n\n if (context.runResult.timeSeconds > context.context.timeLimitSeconds) {\n return { decisionCode: DecisionCode.TIME_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.memoryBytes >\n (context.context.problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)\n ) {\n return { decisionCode: DecisionCode.MEMORY_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.outputLimitExceeded ||\n context.runResult.stdout.length > context.context.outputLimitLength ||\n context.runResult.stderr.length > context.context.outputLimitLength\n ) {\n return { decisionCode: DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n const requiredOutputFileCount = context.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0;\n if (context.outputFiles.length < requiredOutputFileCount) {\n return { decisionCode: DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR };\n }\n\n return { decisionCode: DecisionCode.ACCEPTED };\n}\n"],"mappings":"u5BA2BA,MAIM,EAAoBA,EAAAA,EAAE,OAAO,CACjC,SAAUA,EAAAA,EAAE,MAAM,CAACA,EAAAA,EAAE,OAAO,EAAGA,EAAAA,EAAE,MAAMA,EAAAA,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAChE,CAAC,EA0ID,eAAsB,EAGpB,EAAoB,EAA4D,CAAC,EAAkB,CACnG,IAAM,EAAOC,EAAAA,UAAU,QAAQ,IAAI,EAC7B,EAAS,EAAkB,MAAM,EAAK,MAAM,EAE5C,CAAE,OAAM,eAAgB,MAAMC,EAAAA,YAAY,EAAY,EAAK,GAAG,EAC9D,EAA6B,MAAMC,EAAAA,+BAA+B,CAAU,EAC5E,EAAY,MAAO,EAAQ,eAAiB,EAAA,CAAsB,CAAU,EAC5E,EAAS,CACb,oBAAqB,EAAQ,QAAQ,qBAAuB,GAC5D,gBAAiB,EAAQ,QAAQ,iBAAmB,GACtD,EACM,EACJ,OAAO,EAA2B,aAAgB,SAC9C,EAA2B,YAAc,IACxC,EAAQ,mBAAqB,EAC9B,EAA6C,CACjD,6BACA,YACA,SACA,kBACF,EAEA,GAAI,EAAa,CACf,IAAM,GAAyB,EAAO,oBAAsB,EAAU,OAAS,GAAoB,IACnG,GAAI,CAAE,MAAMC,EAAAA,gCAAgC,EAAY,EAAM,EAAQ,CAAE,uBAAsB,CAAC,EAAI,CACjG,QAAQ,SAAW,EACnB,MACF,CACF,CAEA,IAAK,IAAM,KAAe,EAAM,CAC1B,GAAa,EAAA,oBAAoB,EAAY,CAAW,EAC5D,IAAM,EAAS,MAAM,EAA6C,EAAY,IAAK,EAAQ,EAAS,CAAO,EACvG,GAAe,CAAC,EAAsB,EAAa,CAAM,IAC3D,QAAQ,SAAW,EACnB,EAAA,mCAAmC,EAAY,CAAW,EAE9D,CACF,CAEA,eAAe,EAIb,EACA,EACA,EACA,EACmC,CACnC,GAAM,CAAE,6BAA4B,YAAW,SAAQ,oBAAqB,EACtE,EAAqB,EAAU,EAAE,EAAE,IAAM,WAEzC,EAAuB,MAAMC,EAAAA,sBAAsB,EAAK,CAA0B,EACxF,GAAI,EAEF,OADA,EAAA,oBAAoB,CAAE,WAAY,EAAoB,GAAG,CAAqB,CAAC,EACxE,CAAE,YAAa,EAAM,EAG9B,IAAM,EAAuB,MAAMC,EAAAA,mBAAmB,EAAK,EAAO,QAAQ,EAC1E,GAAI,CAAC,EAMH,OALA,EAAA,oBAAoB,CAClB,WAAY,EACZ,aAAcC,EAAAA,aAAa,uCAC3B,OAAQ,sBAAsB,EAAO,SAAW,eAAe,EAAO,WAAa,IACrF,CAAC,EACM,CAAE,YAAa,EAAM,EAG9B,IAAM,EAAqBC,EAAAA,6BAA6B,CAAoB,EAC5E,GAAI,CAAC,EAMH,OALA,EAAA,oBAAoB,CAClB,WAAY,EACZ,aAAcD,EAAAA,aAAa,aAC3B,OAAQ,sBACV,CAAC,EACM,CAAE,YAAa,EAAM,EAI9B,IAAM,EAAM,CAAE,GAAG,QAAQ,IAAK,GAAI,GAAI,YAAa,GAAI,EAEnD,EAAe,EACnB,GAAI,EAAQ,kBAAoB,GAAO,CACrC,GAAI,EAAmB,SACrB,GAAI,CACF,MAAM,EAAmB,SAAS,CAAG,EACrC,IAAM,EAAuB,MAAMD,EAAAA,mBAAmB,EAAK,EAAO,QAAQ,EACtE,IAAsB,EAAe,EAC3C,OAAS,EAAO,CAMd,OALA,EAAA,oBAAoB,CAClB,WAAY,EACZ,aAAcC,EAAAA,aAAa,YAC3B,OAAQ,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,CAC/D,CAAC,EACM,CAAE,YAAa,EAAM,CAC9B,CAGF,IAAM,EAAe,EAAmB,eAAe,CAAY,EACnE,GAAI,EAAc,CAChB,IAAM,EAAc,MAAM,EAAS,EAAc,CAC/C,MACA,MACA,WAAY,EACZ,QACF,CAAC,EACD,GAAI,EAEF,OADA,EAAA,oBAAoB,CAAW,EACxB,CAAE,YAAa,EAAM,CAEhC,CACF,CAEA,IAAM,EAAc,MAAME,EAAAA,yBAAyB,CAAG,EAEtD,GAAI,EAAU,SAAW,EAEvB,OADA,EAAA,oBAAoB,CAAE,WAAY,UAAW,aAAcF,EAAAA,aAAa,QAAS,CAAC,EAC3E,CAAE,YAAa,EAAK,EAG7B,IAAM,EAAuB,EAAsD,QAAQ,cAE3F,IAAK,IAAM,KAAY,EAAW,CAC5B,GAAqB,MAAMG,EAAAA,sBAAsB,EAAqB,CAAG,EACzE,EAAS,eAAe,MAAMA,EAAAA,sBAAsB,EAAS,cAAe,CAAG,EAEnF,IAAM,EAAU,EAAmB,QAAQ,CAAY,EACnD,EAAQ,EAAS,OAAS,GAC1B,EACJ,GAAI,CACE,EAAQ,eACV,EAAQ,MAAM,EAAQ,aAAa,CAAE,WAAU,MAAK,KAAI,CAAC,GAGvD,EAAQ,aACV,EAAkB,MAAM,EAAQ,WAAW,CAAE,WAAU,UAAS,QAAO,MAAK,MAAK,kBAAiB,CAAC,EAEvG,OAAS,EAAO,CAQd,OAPA,EAAA,oBAAoB,CAClB,WAAY,EAAS,GACrB,aAAcH,EAAAA,aAAa,cAC3B,QACA,OAAQ,EAAe,CAAK,CAC9B,CAAC,EACD,MAAMI,EAAAA,sBAAsB,EAAK,CAAW,EACrC,CAAE,YAAa,EAAM,CAC9B,CAIA,IAAM,EACJ,GAAqB,MAAM,EAAW,EAAS,CAAE,QAAO,MAAK,MAAK,kBAAiB,CAAC,EAEhF,EAAc,MAAMC,EAAAA,gBAAgB,EAAK,EAA2B,yBAA2B,CAAC,CAAC,EACjG,EAAoC,CACxC,mBACA,kBAAmB,EAAO,gBAC1B,2BAA4B,CAC1B,gBAAiB,EAA2B,gBAC5C,wBAAyB,EAA2B,uBACtD,CACF,EACM,EAAkB,EAAiB,CAAE,YAAW,cAAa,QAAS,CAAa,CAAC,EACtF,EAAc,EAClB,GAAI,EAAgB,eAAiBL,EAAAA,aAAa,SAChD,GAAI,CACF,IAAM,EAAsB,EAAQ,KAChC,MAAM,EAAQ,KAAK,CAAE,WAAU,YAAW,cAAa,MAAK,QAAS,CAAa,CAAC,EACnF,MAAM,EAA2B,CAAE,WAAU,YAAW,cAAa,KAAI,CAAC,EAC1E,IACF,EAAc,CACZ,aAAc,EAAoB,cAAgB,EAAgB,aAClE,iBAAkB,EAAoB,iBACtC,OAAQ,EAAoB,MAC9B,EAEJ,OAAS,EAAO,CACd,EAAc,CACZ,aAAcA,EAAAA,aAAa,cAC3B,OAAQ,EAAe,CAAK,CAC9B,CACF,CAiBF,GAdA,EAAA,oBAAoB,CAClB,WAAY,EAAS,GACrB,GAAG,EACH,WAAY,EAAU,OACtB,MAAO,EAAU,MACjB,OAAQ,EAAU,OAAO,MAAM,EAAG,EAAO,eAAe,GAAK,IAAA,GAC7D,QAAS,EAAY,QAAU,EAAU,OAAA,CAAQ,MAAM,EAAG,EAAO,eAAe,GAAK,IAAA,GACrF,YAAa,EAAU,YACvB,eAAgB,EAAU,eAC1B,YAAa,EAAU,YACvB,YAAa,EAAY,OAAS,EAAI,EAAc,IAAA,EACtD,CAAC,EAED,MAAMI,EAAAA,sBAAsB,EAAK,CAAW,EACxC,EAAY,eAAiBJ,EAAAA,aAAa,SAAU,MAAO,CAAE,YAAa,EAAM,CACtF,CAEA,MAAO,CAAE,YAAa,EAAK,CAC7B,CAEA,SAAS,EAAsB,EAA0B,EAA2C,CAClG,OAAO,EAAO,eAAiB,EAAY,iBAAmB,WAChE,CAEA,eAAe,EACb,EACA,EACoF,CACpF,IAAM,EAAc,MAAMM,EAAAA,iBACxB,EAAa,GACb,EAAa,MAAM,CAAC,EACpB,CAAE,IAAK,EAAQ,IAAK,IAAK,EAAQ,GAAI,EACrC,EAAQ,OAAO,mBACjB,EACM,EAAa,EAAY,QAAU,IAAA,GAEzC,GAAI,EAAY,SAAW,EACzB,MAAO,CACL,WAAY,EAAQ,WACpB,aAAcN,EAAAA,aAAa,YAC3B,aACA,OAAQ,EAAY,OAAO,MAAM,EAAG,EAAQ,OAAO,eAAe,EAClE,OAAQ,EAAY,OAAO,MAAM,EAAG,EAAQ,OAAO,eAAe,EAClE,YAAa,EAAY,YACzB,eAAgB,EAAY,eAC5B,YAAa,EAAY,WAC3B,EAGF,GAAI,EAAY,YAAc,EAAQ,OAAO,oBAC3C,MAAO,CACL,WAAY,EAAQ,WACpB,aAAcA,EAAAA,aAAa,0BAC3B,aACA,OAAQ,EAAY,OAAO,MAAM,EAAG,EAAQ,OAAO,eAAe,EAClE,OAAQ,EAAY,OAAO,MAAM,EAAG,EAAQ,OAAO,eAAe,EAClE,YAAa,EAAY,YACzB,eAAgB,EAAY,eAC5B,YAAa,EAAY,WAC3B,EAGF,GACE,EAAY,qBACZ,EAAY,OAAO,OAAS,EAAQ,OAAO,iBAC3C,EAAY,OAAO,OAAS,EAAQ,OAAO,gBAE3C,MAAO,CACL,WAAY,EAAQ,WACpB,aAAcA,EAAAA,aAAa,iCAC3B,aACA,OAAQ,EAAY,OAAO,MAAM,EAAG,EAAQ,OAAO,eAAe,EAClE,OAAQ,EAAY,OAAO,MAAM,EAAG,EAAQ,OAAO,eAAe,EAClE,YAAa,EAAY,YACzB,eAAgB,EAAY,eAC5B,YAAa,EAAY,WAC3B,CAIJ,CAEA,SAAS,EAAe,EAAwB,CAC9C,OAAO,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,CAC9D,CAEA,eAAe,EACb,EAC+B,CAE/B,OAAQ,MAAMO,EAAAA,cAAkBC,EAAAA,QAAK,KAAK,EAAY,YAAY,CAAC,CACrE,CAGA,eAAe,EAA2B,EAKG,CAC3C,GAAM,CAAE,WAAU,YAAW,cAAa,OAAQ,EAC5C,EAAY,MAAMC,EAAAA,yBAAyB,CAC/C,OAAQ,EAAU,OAClB,eAAgB,OAAO,EAAS,QAAW,SAAW,EAAS,OAAS,IAAA,GACxE,eAAgB,OAAO,EAAS,gBAAmB,SAAW,EAAS,eAAiB,IAAA,GACxF,MACA,aACF,CAAC,EAED,OADA,EAAY,OAAO,EAAG,EAAY,OAAQ,GAAG,EAAU,WAAW,EAC3D,EAAU,QAAU,CAAC,EAAI,CAAE,aAAcT,EAAAA,aAAa,YAAa,CAC5E,CAEA,eAAe,EACb,EACA,EAM2B,CAC3B,IAAM,EAAc,MAAMM,EAAAA,iBACxB,EAAQ,GACR,EAAQ,MAAM,CAAC,EACf,CAAE,IAAK,EAAQ,IAAK,MAAO,EAAQ,MAAO,IAAK,EAAQ,GAAI,EAC3D,EAAQ,gBACV,EAEA,MAAO,CACL,MAAO,EAAQ,MACf,OAAQ,EAAY,OACpB,OAAQ,EAAY,OACpB,OAAQ,EAAY,QAAU,IAAA,GAC9B,YAAa,EAAY,YACzB,eAAgB,EAAY,eAC5B,YAAa,EAAY,YACzB,oBAAqB,EAAY,mBACnC,CACF,CAEA,SAAS,EAAiB,EAIC,CACzB,GAAI,EAAQ,UAAU,SAAW,EAC/B,MAAO,CAAE,aAAcN,EAAAA,aAAa,cAAe,OAAQ,EAAQ,UAAU,MAAO,EAGtF,GAAI,EAAQ,UAAU,YAAc,EAAQ,QAAQ,iBAClD,MAAO,CAAE,aAAcA,EAAAA,aAAa,oBAAqB,OAAQ,EAAQ,UAAU,MAAO,EAG5F,GACE,EAAQ,UAAU,aACjB,EAAQ,QAAQ,2BAA2B,iBAAmB,KAE/D,MAAO,CAAE,aAAcA,EAAAA,aAAa,sBAAuB,OAAQ,EAAQ,UAAU,MAAO,EAG9F,GACE,EAAQ,UAAU,qBAClB,EAAQ,UAAU,OAAO,OAAS,EAAQ,QAAQ,mBAClD,EAAQ,UAAU,OAAO,OAAS,EAAQ,QAAQ,kBAElD,MAAO,CAAE,aAAcA,EAAAA,aAAa,2BAA4B,OAAQ,EAAQ,UAAU,MAAO,EAGnG,IAAM,EAA0B,EAAQ,QAAQ,2BAA2B,yBAAyB,QAAU,EAK9G,OAJI,EAAQ,YAAY,OAAS,EACxB,CAAE,aAAcA,EAAAA,aAAa,kCAAmC,EAGlE,CAAE,aAAcA,EAAAA,aAAa,QAAS,CAC/C"}
@@ -22,6 +22,8 @@ export interface CommandRunResult {
22
22
  stderr: string;
23
23
  status: number | undefined;
24
24
  timeSeconds: number;
25
+ /** See `TestCaseResult.cpuTimeSeconds`; a custom `runCommand` may leave it out. */
26
+ cpuTimeSeconds?: number;
25
27
  memoryBytes: number;
26
28
  outputLimitExceeded?: boolean;
27
29
  }
@@ -1,2 +1,2 @@
1
- import{printTestCaseResult as e}from"../helpers/printTestCaseResult.js";import{judgeAgainstExpectations as t}from"../helpers/compareExpectedOutputFiles.js";import{parseArgs as n}from"../helpers/parseArgs.js";import{readTestCases as r}from"../helpers/readTestCases.js";import{findLanguageDefinitionByPath as i}from"../helpers/findLanguageDefinitionByPath.js";import{readProblemMarkdownFrontMatter as a}from"../helpers/readProblemMarkdownFrontMatter.js";import{DecisionCode as o}from"../types/decisionCode.js";import{cleanWorkingDirectory as s,snapshotWorkingDirectory as c}from"../helpers/cleanWorkingDirectory.js";import{copyTestCaseFileInput as l}from"../helpers/copyTestCaseFileInput.js";import{findEntryPointFile as u}from"../helpers/findEntryPointFile.js";import{judgeByStaticAnalysis as d}from"../helpers/judgeByStaticAnalysis.js";import{readOutputFiles as f}from"../helpers/readOutputFiles.js";import{passesIsolationCheckInDebugMode as p}from"../helpers/runIsolationCheckInDebugMode.js";import{printDebugCwdBanner as m,printDebugExpectationFailureBanner as h,resolveCwds as g}from"../helpers/resolveCwds.js";import{spawnSyncWithTimeout as _}from"../helpers/spawnSyncWithTimeout.js";import v from"node:path";import{z as y}from"zod";const b=y.object({language:y.union([y.string(),y.array(y.string())]).optional()});async function x(e,t={}){let r=n(process.argv),i=b.parse(r.params),{cwds:o,isDebugMode:s}=await g(e,r.cwd),c=await a(e),l=await(t.readTestCases??E)(e),u={buildTimeoutSeconds:t.limits?.buildTimeoutSeconds??10,maxOutputLength:t.limits?.maxOutputLength??5e4},d=typeof c.timeLimitMs==`number`?c.timeLimitMs/1e3:t.runTimeoutSeconds??2,f={problemMarkdownFrontMatter:c,testCases:l,limits:u,timeLimitSeconds:d};if(s){let t=(u.buildTimeoutSeconds+l.length*d)*1e3;if(!await p(e,o,i,{expectedMaxDurationMs:t})){process.exitCode=1;return}}for(let n of o){s&&m(e,n);let r=await S(n.cwd,i,f,t);s&&!C(n,r)&&(process.exitCode=1,h(e,n))}}async function S(t,n,r,a){let{problemMarkdownFrontMatter:p,testCases:m,limits:h,timeLimitSeconds:g}=r,_=m[0]?.id??`prebuild`,v=await d(t,p);if(v)return e({testCaseId:_,...v}),{allAccepted:!1};let y=await u(t,n.language);if(!y)return e({testCaseId:_,decisionCode:o.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${n.language?`: language: ${n.language}`:``}`}),{allAccepted:!1};let b=i(y);if(!b)return e({testCaseId:_,decisionCode:o.WRONG_ANSWER,stderr:`unsupported language`}),{allAccepted:!1};let x={...process.env,CI:``,FORCE_COLOR:`0`},S=y;if(a.buildSubmission!==!1){if(b.prebuild)try{await b.prebuild(t);let e=await u(t,n.language);e&&(S=e)}catch(t){return e({testCaseId:_,decisionCode:o.BUILD_ERROR,stderr:t instanceof Error?t.message:String(t)}),{allAccepted:!1}}let r=b.buildCommand?.(S);if(r){let n=w(r,{cwd:t,env:x,testCaseId:_,limits:h});if(n)return e(n),{allAccepted:!1}}}let C=await c(t);if(m.length===0)return e({testCaseId:`default`,decisionCode:o.ACCEPTED}),{allAccepted:!0};let E=m.shared?.fileInputPath;for(let n of m){E&&await l(E,t),n.fileInputPath&&await l(n.fileInputPath,t);let r=b.command(S),i=n.input??``,c;try{a.resolveInput&&(i=await a.resolveInput({testCase:n,cwd:t,env:x})),c=a.runCommand?await a.runCommand({testCase:n,command:r,stdin:i,cwd:t,env:x,timeLimitSeconds:g}):O(r,{stdin:i,cwd:t,env:x,timeLimitSeconds:g})}catch(r){return e({testCaseId:n.id,decisionCode:o.RUNTIME_ERROR,stdin:i,stderr:T(r)}),await s(t,C),{allAccepted:!1}}let u=await f(t,p.requiredOutputFilePaths??[]),d={timeLimitSeconds:g,outputLimitLength:h.maxOutputLength,problemMarkdownFrontMatter:{memoryLimitByte:p.memoryLimitByte,requiredOutputFilePaths:p.requiredOutputFilePaths}},m=k({runResult:c,outputFiles:u,context:d}),_=m;if(m.decisionCode===o.ACCEPTED)try{let e=a.test?await a.test({testCase:n,runResult:c,outputFiles:u,cwd:t,context:d}):await D({testCase:n,runResult:c,outputFiles:u,cwd:t});e&&(_={decisionCode:e.decisionCode??m.decisionCode,feedbackMarkdown:e.feedbackMarkdown,stderr:e.stderr})}catch(e){_={decisionCode:o.RUNTIME_ERROR,stderr:T(e)}}if(e({testCaseId:n.id,..._,exitStatus:c.status,stdin:c.stdin,stdout:c.stdout.slice(0,h.maxOutputLength)||void 0,stderr:(_.stderr??c.stderr).slice(0,h.maxOutputLength)||void 0,timeSeconds:c.timeSeconds,memoryBytes:c.memoryBytes,outputFiles:u.length>0?u:void 0}),await s(t,C),_.decisionCode!==o.ACCEPTED)return{allAccepted:!1}}return{allAccepted:!0}}function C(e,t){return t.allAccepted===(e.expectedResult===`accepted`)}function w(e,t){let n=_(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,env:t.env},t.limits.buildTimeoutSeconds),r=n.status??void 0;if(n.status!==0)return{testCaseId:t.testCaseId,decisionCode:o.BUILD_ERROR,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.timeSeconds>t.limits.buildTimeoutSeconds)return{testCaseId:t.testCaseId,decisionCode:o.BUILD_TIME_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.stdout.length>t.limits.maxOutputLength||n.stderr.length>t.limits.maxOutputLength)return{testCaseId:t.testCaseId,decisionCode:o.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function T(e){return e instanceof Error?e.message:String(e)}async function E(e){return await r(v.join(e,`test_cases`))}async function D(e){let{testCase:n,runResult:r,outputFiles:i,cwd:a}=e,s=await t({stdout:r.stdout,expectedStdout:typeof n.output==`string`?n.output:void 0,fileOutputPath:typeof n.fileOutputPath==`string`?n.fileOutputPath:void 0,cwd:a,outputFiles:i});return i.splice(0,i.length,...s.outputFiles),s.matches?{}:{decisionCode:o.WRONG_ANSWER}}function O(e,t){let n=_(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,input:t.stdin,env:t.env},t.timeLimitSeconds);return{stdin:t.stdin,stdout:n.stdout,stderr:n.stderr,status:n.status??void 0,timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function k(e){if(e.runResult.status!==0)return{decisionCode:o.RUNTIME_ERROR,stderr:e.runResult.stderr};if(e.runResult.timeSeconds>e.context.timeLimitSeconds)return{decisionCode:o.TIME_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.memoryBytes>(e.context.problemMarkdownFrontMatter.memoryLimitByte??1/0))return{decisionCode:o.MEMORY_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.outputLimitExceeded||e.runResult.stdout.length>e.context.outputLimitLength||e.runResult.stderr.length>e.context.outputLimitLength)return{decisionCode:o.OUTPUT_SIZE_LIMIT_EXCEEDED,stderr:e.runResult.stderr};let t=e.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length??0;return e.outputFiles.length<t?{decisionCode:o.MISSING_REQUIRED_OUTPUT_FILE_ERROR}:{decisionCode:o.ACCEPTED}}export{x as commandJudgePreset};
1
+ import{printTestCaseResult as e}from"../helpers/printTestCaseResult.js";import{judgeAgainstExpectations as t}from"../helpers/compareExpectedOutputFiles.js";import{parseArgs as n}from"../helpers/parseArgs.js";import{readTestCases as r}from"../helpers/readTestCases.js";import{findLanguageDefinitionByPath as i}from"../helpers/findLanguageDefinitionByPath.js";import{readProblemMarkdownFrontMatter as a}from"../helpers/readProblemMarkdownFrontMatter.js";import{DecisionCode as o}from"../types/decisionCode.js";import{cleanWorkingDirectory as s,snapshotWorkingDirectory as c}from"../helpers/cleanWorkingDirectory.js";import{copyTestCaseFileInput as l}from"../helpers/copyTestCaseFileInput.js";import{findEntryPointFile as u}from"../helpers/findEntryPointFile.js";import{judgeByStaticAnalysis as d}from"../helpers/judgeByStaticAnalysis.js";import{readOutputFiles as f}from"../helpers/readOutputFiles.js";import{passesIsolationCheckInDebugMode as p}from"../helpers/runIsolationCheckInDebugMode.js";import{printDebugCwdBanner as m,printDebugExpectationFailureBanner as h,resolveCwds as g}from"../helpers/resolveCwds.js";import{spawnWithTimeout as _}from"../helpers/spawnWithTimeout.js";import v from"node:path";import{z as y}from"zod";const b=y.object({language:y.union([y.string(),y.array(y.string())]).optional()});async function x(e,t={}){let r=n(process.argv),i=b.parse(r.params),{cwds:o,isDebugMode:s}=await g(e,r.cwd),c=await a(e),l=await(t.readTestCases??E)(e),u={buildTimeoutSeconds:t.limits?.buildTimeoutSeconds??10,maxOutputLength:t.limits?.maxOutputLength??5e4},d=typeof c.timeLimitMs==`number`?c.timeLimitMs/1e3:t.runTimeoutSeconds??2,f={problemMarkdownFrontMatter:c,testCases:l,limits:u,timeLimitSeconds:d};if(s){let t=(u.buildTimeoutSeconds+l.length*d)*1e3;if(!await p(e,o,i,{expectedMaxDurationMs:t})){process.exitCode=1;return}}for(let n of o){s&&m(e,n);let r=await S(n.cwd,i,f,t);s&&!C(n,r)&&(process.exitCode=1,h(e,n))}}async function S(t,n,r,a){let{problemMarkdownFrontMatter:p,testCases:m,limits:h,timeLimitSeconds:g}=r,_=m[0]?.id??`prebuild`,v=await d(t,p);if(v)return e({testCaseId:_,...v}),{allAccepted:!1};let y=await u(t,n.language);if(!y)return e({testCaseId:_,decisionCode:o.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${n.language?`: language: ${n.language}`:``}`}),{allAccepted:!1};let b=i(y);if(!b)return e({testCaseId:_,decisionCode:o.WRONG_ANSWER,stderr:`unsupported language`}),{allAccepted:!1};let x={...process.env,CI:``,FORCE_COLOR:`0`},S=y;if(a.buildSubmission!==!1){if(b.prebuild)try{await b.prebuild(t);let e=await u(t,n.language);e&&(S=e)}catch(t){return e({testCaseId:_,decisionCode:o.BUILD_ERROR,stderr:t instanceof Error?t.message:String(t)}),{allAccepted:!1}}let r=b.buildCommand?.(S);if(r){let n=await w(r,{cwd:t,env:x,testCaseId:_,limits:h});if(n)return e(n),{allAccepted:!1}}}let C=await c(t);if(m.length===0)return e({testCaseId:`default`,decisionCode:o.ACCEPTED}),{allAccepted:!0};let E=m.shared?.fileInputPath;for(let n of m){E&&await l(E,t),n.fileInputPath&&await l(n.fileInputPath,t);let r=b.command(S),i=n.input??``,c;try{a.resolveInput&&(i=await a.resolveInput({testCase:n,cwd:t,env:x})),a.runCommand&&(c=await a.runCommand({testCase:n,command:r,stdin:i,cwd:t,env:x,timeLimitSeconds:g}))}catch(r){return e({testCaseId:n.id,decisionCode:o.RUNTIME_ERROR,stdin:i,stderr:T(r)}),await s(t,C),{allAccepted:!1}}let u=c??await O(r,{stdin:i,cwd:t,env:x,timeLimitSeconds:g}),d=await f(t,p.requiredOutputFilePaths??[]),m={timeLimitSeconds:g,outputLimitLength:h.maxOutputLength,problemMarkdownFrontMatter:{memoryLimitByte:p.memoryLimitByte,requiredOutputFilePaths:p.requiredOutputFilePaths}},_=k({runResult:u,outputFiles:d,context:m}),v=_;if(_.decisionCode===o.ACCEPTED)try{let e=a.test?await a.test({testCase:n,runResult:u,outputFiles:d,cwd:t,context:m}):await D({testCase:n,runResult:u,outputFiles:d,cwd:t});e&&(v={decisionCode:e.decisionCode??_.decisionCode,feedbackMarkdown:e.feedbackMarkdown,stderr:e.stderr})}catch(e){v={decisionCode:o.RUNTIME_ERROR,stderr:T(e)}}if(e({testCaseId:n.id,...v,exitStatus:u.status,stdin:u.stdin,stdout:u.stdout.slice(0,h.maxOutputLength)||void 0,stderr:(v.stderr??u.stderr).slice(0,h.maxOutputLength)||void 0,timeSeconds:u.timeSeconds,cpuTimeSeconds:u.cpuTimeSeconds,memoryBytes:u.memoryBytes,outputFiles:d.length>0?d:void 0}),await s(t,C),v.decisionCode!==o.ACCEPTED)return{allAccepted:!1}}return{allAccepted:!0}}function C(e,t){return t.allAccepted===(e.expectedResult===`accepted`)}async function w(e,t){let n=await _(e[0],e.slice(1),{cwd:t.cwd,env:t.env},t.limits.buildTimeoutSeconds),r=n.status??void 0;if(n.status!==0)return{testCaseId:t.testCaseId,decisionCode:o.BUILD_ERROR,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,cpuTimeSeconds:n.cpuTimeSeconds,memoryBytes:n.memoryBytes};if(n.timeSeconds>t.limits.buildTimeoutSeconds)return{testCaseId:t.testCaseId,decisionCode:o.BUILD_TIME_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,cpuTimeSeconds:n.cpuTimeSeconds,memoryBytes:n.memoryBytes};if(n.outputLimitExceeded||n.stdout.length>t.limits.maxOutputLength||n.stderr.length>t.limits.maxOutputLength)return{testCaseId:t.testCaseId,decisionCode:o.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,cpuTimeSeconds:n.cpuTimeSeconds,memoryBytes:n.memoryBytes}}function T(e){return e instanceof Error?e.message:String(e)}async function E(e){return await r(v.join(e,`test_cases`))}async function D(e){let{testCase:n,runResult:r,outputFiles:i,cwd:a}=e,s=await t({stdout:r.stdout,expectedStdout:typeof n.output==`string`?n.output:void 0,fileOutputPath:typeof n.fileOutputPath==`string`?n.fileOutputPath:void 0,cwd:a,outputFiles:i});return i.splice(0,i.length,...s.outputFiles),s.matches?{}:{decisionCode:o.WRONG_ANSWER}}async function O(e,t){let n=await _(e[0],e.slice(1),{cwd:t.cwd,stdin:t.stdin,env:t.env},t.timeLimitSeconds);return{stdin:t.stdin,stdout:n.stdout,stderr:n.stderr,status:n.status??void 0,timeSeconds:n.timeSeconds,cpuTimeSeconds:n.cpuTimeSeconds,memoryBytes:n.memoryBytes,outputLimitExceeded:n.outputLimitExceeded}}function k(e){if(e.runResult.status!==0)return{decisionCode:o.RUNTIME_ERROR,stderr:e.runResult.stderr};if(e.runResult.timeSeconds>e.context.timeLimitSeconds)return{decisionCode:o.TIME_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.memoryBytes>(e.context.problemMarkdownFrontMatter.memoryLimitByte??1/0))return{decisionCode:o.MEMORY_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.outputLimitExceeded||e.runResult.stdout.length>e.context.outputLimitLength||e.runResult.stderr.length>e.context.outputLimitLength)return{decisionCode:o.OUTPUT_SIZE_LIMIT_EXCEEDED,stderr:e.runResult.stderr};let t=e.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length??0;return e.outputFiles.length<t?{decisionCode:o.MISSING_REQUIRED_OUTPUT_FILE_ERROR}:{decisionCode:o.ACCEPTED}}export{x as commandJudgePreset};
2
2
  //# sourceMappingURL=command.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"command.js","names":["readFileTestCases"],"sources":["../../src/presets/command.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { z } from 'zod';\n\nimport { cleanWorkingDirectory, snapshotWorkingDirectory } from '../helpers/cleanWorkingDirectory.js';\nimport { judgeAgainstExpectations } from '../helpers/compareExpectedOutputFiles.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 { passesIsolationCheckInDebugMode } from '../helpers/runIsolationCheckInDebugMode.js';\nimport { readProblemMarkdownFrontMatter } from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases as readFileTestCases } from '../helpers/readTestCases.js';\nimport {\n printDebugCwdBanner,\n printDebugExpectationFailureBanner,\n resolveCwds,\n type ResolvedCwd,\n} from '../helpers/resolveCwds.js';\nimport { spawnSyncWithTimeout } from '../helpers/spawnSyncWithTimeout.js';\nimport { DecisionCode } from '../types/decisionCode.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 = 2;\nconst MAX_STDOUT_LENGTH = 50_000;\n\nconst judgeParamsSchema = z.object({\n language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\ntype JudgeParams = z.infer<typeof judgeParamsSchema>;\n\n/** What every command test case needs; a custom `readTestCases` may add any fields of its own. */\ninterface BaseCommandTestCase {\n id: string;\n /** Standard input (`test_cases/<id>.in`). */\n input?: string;\n /** Directory copied into the working directory before the run (`test_cases/<id>.fin/`). */\n fileInputPath?: string;\n}\n\n/** A test case read from `test_cases/` by the default reader. */\nexport interface CommandTestCase extends BaseCommandTestCase {\n /** Expected standard output (`test_cases/<id>.out`). */\n output?: string;\n /** Directory of expected output files compared after the run (`test_cases/<id>.fout/`). */\n fileOutputPath?: string;\n}\n\ntype CommandJudgeCaseResult = Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown' | 'stderr'>;\n\nexport interface CommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n outputLimitExceeded?: boolean;\n}\n\ninterface CommandJudgeContext {\n timeLimitSeconds: number;\n outputLimitLength: number;\n problemMarkdownFrontMatter: Pick<ProblemMarkdownFrontMatter, 'memoryLimitByte' | 'requiredOutputFilePaths'>;\n}\n\nexport interface CommandJudgeLimits {\n buildTimeoutSeconds: number;\n maxOutputLength: number;\n}\n\ninterface ResolvedCommandProblem<TTestCase extends BaseCommandTestCase> {\n problemMarkdownFrontMatter: ProblemMarkdownFrontMatter;\n testCases: readonly TTestCase[];\n limits: CommandJudgeLimits;\n timeLimitSeconds: number;\n}\n\nexport interface CommandJudgePresetOptions<\n TTestCase extends BaseCommandTestCase = CommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n> {\n /**\n * Set to false when the custom runner builds the submitted source itself.\n * Defaults to true.\n */\n buildSubmission?: boolean;\n limits?: CommandJudgeLimits;\n /** The per-case time limit used only when `problem.md` declares no `timeLimitMs`. */\n runTimeoutSeconds?: number;\n readTestCases?: (problemDir: string) => Promise<readonly TTestCase[]>;\n resolveInput?: (context: { testCase: TTestCase; cwd: string; env: NodeJS.ProcessEnv }) => Promise<string> | string;\n runCommand?: (context: {\n testCase: TTestCase;\n command: readonly [string, ...string[]];\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }) => Promise<TRunResult> | TRunResult;\n /**\n * Decides the verdict of a run that passed the limit checks. It replaces the default comparison\n * of `testCase.output` (space-separated tokens) and `testCase.fileOutputPath` (see\n * `compareStdoutAsSpaceSeparatedTokens` and `compareExpectedOutputFiles`, both exported), so\n * call those yourself when the test case ships `.out` / `.fout` expectations to keep.\n * `outputFiles` is the array printed with the result and may be edited in place.\n */\n test?: (context: {\n testCase: TTestCase;\n runResult: TRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n /** The submission's working directory, e.g. for `compareExpectedOutputFiles(cwd, testCase.fileOutputPath)`. */\n cwd: string;\n context: CommandJudgeContext;\n }) => Promise<Partial<CommandJudgeCaseResult>> | Partial<CommandJudgeCaseResult> | undefined;\n}\n\n/**\n * A preset function for judging by executable command.\n *\n * Without options, test cases come from `test_cases/` (`.in`, `.out`, `.fin/`, `.fout/`) and a run\n * within the limits is accepted when its stdout matches `.out` and its files match `.fout/`; a\n * case without either expectation only has to run within the limits.\n * Keep problem-specific logic in `resolveInput` and `test`.\n *\n * @example\n * Create `judge.ts` that judges `test_cases/` like the default stdio harness, with the command\n * preset's debug mode:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n *\n * await commandJudgePreset(import.meta.dirname);\n * ```\n *\n * @example\n * Create `judge.ts` with an own verdict:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n * import { DecisionCode } from '@exercode/problem-utils';\n *\n * await commandJudgePreset(import.meta.dirname, {\n * readTestCases: async () => [\n * { id: '01', input: '1 2' },\n * ],\n * test: ({ runResult }) => {\n * return runResult.stdout.trim() === '3'\n * ? { decisionCode: DecisionCode.ACCEPTED }\n * : { decisionCode: DecisionCode.WRONG_ANSWER };\n * },\n * });\n * ```\n *\n * Run with the required parameters:\n * ```bash\n * bun judge.ts model_answers/python '{ \"language\": \"python\" }'\n * ```\n *\n * Run without a cwd argument to judge each `<problemDir>/model_answers/*` directory\n * for debugging. A prominent banner is printed for each run.\n * ```bash\n * bun judge.ts\n * ```\n */\nexport async function commandJudgePreset<\n TTestCase extends BaseCommandTestCase = CommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(problemDir: string, options: CommandJudgePresetOptions<TTestCase, TRunResult> = {}): Promise<void> {\n const args = parseArgs(process.argv);\n const params = judgeParamsSchema.parse(args.params);\n\n const { cwds, isDebugMode } = await resolveCwds(problemDir, args.cwd);\n const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n const testCases = await (options.readTestCases ?? readCommandTestCases)(problemDir);\n const limits = {\n buildTimeoutSeconds: options.limits?.buildTimeoutSeconds ?? BUILD_TIMEOUT_SECONDS,\n maxOutputLength: options.limits?.maxOutputLength ?? MAX_STDOUT_LENGTH,\n };\n const timeLimitSeconds =\n typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n ? problemMarkdownFrontMatter.timeLimitMs / 1000\n : (options.runTimeoutSeconds ?? JUDGE_DEFAULT_TIMEOUT_SECONDS);\n const problem: ResolvedCommandProblem<TTestCase> = {\n problemMarkdownFrontMatter,\n testCases,\n limits,\n timeLimitSeconds,\n };\n\n if (isDebugMode) {\n const expectedMaxDurationMs = (limits.buildTimeoutSeconds + testCases.length * timeLimitSeconds) * 1000;\n if (!(await passesIsolationCheckInDebugMode(problemDir, cwds, params, { expectedMaxDurationMs }))) {\n process.exitCode = 1;\n return;\n }\n }\n\n for (const resolvedCwd of cwds) {\n if (isDebugMode) printDebugCwdBanner(problemDir, resolvedCwd);\n const result = await runCommandJudgeForCwd<TTestCase, TRunResult>(resolvedCwd.cwd, params, problem, options);\n if (isDebugMode && !matchesExpectedResult(resolvedCwd, result)) {\n process.exitCode = 1;\n printDebugExpectationFailureBanner(problemDir, resolvedCwd);\n }\n }\n}\n\nasync function runCommandJudgeForCwd<\n TTestCase extends BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(\n cwd: string,\n params: JudgeParams,\n problem: ResolvedCommandProblem<TTestCase>,\n options: CommandJudgePresetOptions<TTestCase, TRunResult>\n): Promise<{ allAccepted: boolean }> {\n const { problemMarkdownFrontMatter, testCases, limits, timeLimitSeconds } = problem;\n const prebuildTestCaseId = testCases[0]?.id ?? 'prebuild';\n\n const staticAnalysisResult = await judgeByStaticAnalysis(cwd, problemMarkdownFrontMatter);\n if (staticAnalysisResult) {\n printTestCaseResult({ testCaseId: prebuildTestCaseId, ...staticAnalysisResult });\n return { allAccepted: false };\n }\n\n const originalMainFilePath = await findEntryPointFile(cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return { allAccepted: false };\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return { allAccepted: false };\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let mainFilePath = originalMainFilePath;\n if (options.buildSubmission !== false) {\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(cwd);\n const prebuiltMainFilePath = await findEntryPointFile(cwd, params.language);\n if (prebuiltMainFilePath) mainFilePath = prebuiltMainFilePath;\n } catch (error) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return { allAccepted: false };\n }\n }\n\n const buildCommand = languageDefinition.buildCommand?.(mainFilePath);\n if (buildCommand) {\n const buildResult = runBuild(buildCommand, {\n cwd,\n env,\n testCaseId: prebuildTestCaseId,\n limits,\n });\n if (buildResult) {\n printTestCaseResult(buildResult);\n return { allAccepted: false };\n }\n }\n }\n\n const cwdSnapshot = await snapshotWorkingDirectory(cwd);\n\n if (testCases.length === 0) {\n printTestCaseResult({ testCaseId: 'default', decisionCode: DecisionCode.ACCEPTED });\n return { allAccepted: true };\n }\n\n const sharedFileInputPath = (testCases as { shared?: { fileInputPath?: string } }).shared?.fileInputPath;\n\n for (const testCase of testCases) {\n if (sharedFileInputPath) await copyTestCaseFileInput(sharedFileInputPath, cwd);\n if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, cwd);\n\n const command = languageDefinition.command(mainFilePath);\n let stdin = testCase.input ?? '';\n let runResult: TRunResult;\n try {\n if (options.resolveInput) {\n stdin = await options.resolveInput({ testCase, cwd, env });\n }\n\n runResult = options.runCommand\n ? await options.runCommand({ testCase, command, stdin, cwd, env, timeLimitSeconds })\n : (runCommand(command, {\n stdin,\n cwd,\n env,\n timeLimitSeconds,\n }) as TRunResult);\n } catch (error) {\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stdin,\n stderr: errorToMessage(error),\n });\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n return { allAccepted: false };\n }\n\n const outputFiles = await readOutputFiles(cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n const judgeContext: CommandJudgeContext = {\n timeLimitSeconds,\n outputLimitLength: limits.maxOutputLength,\n problemMarkdownFrontMatter: {\n memoryLimitByte: problemMarkdownFrontMatter.memoryLimitByte,\n requiredOutputFilePaths: problemMarkdownFrontMatter.requiredOutputFilePaths,\n },\n };\n const baseJudgeResult = evaluateByLimits({ runResult, outputFiles, context: judgeContext });\n let judgeResult = baseJudgeResult;\n if (baseJudgeResult.decisionCode === DecisionCode.ACCEPTED) {\n try {\n const extendedJudgeResult = options.test\n ? await options.test({ testCase, runResult, outputFiles, cwd, context: judgeContext })\n : await compareWithExpectedOutputs({ testCase, runResult, outputFiles, cwd });\n if (extendedJudgeResult) {\n judgeResult = {\n decisionCode: extendedJudgeResult.decisionCode ?? baseJudgeResult.decisionCode,\n feedbackMarkdown: extendedJudgeResult.feedbackMarkdown,\n stderr: extendedJudgeResult.stderr,\n };\n }\n } catch (error) {\n judgeResult = {\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stderr: errorToMessage(error),\n };\n }\n }\n\n printTestCaseResult({\n testCaseId: testCase.id,\n ...judgeResult,\n exitStatus: runResult.status,\n stdin: runResult.stdin,\n stdout: runResult.stdout.slice(0, limits.maxOutputLength) || undefined,\n stderr: (judgeResult.stderr ?? runResult.stderr).slice(0, limits.maxOutputLength) || undefined,\n timeSeconds: runResult.timeSeconds,\n memoryBytes: runResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n if (judgeResult.decisionCode !== DecisionCode.ACCEPTED) return { allAccepted: false };\n }\n\n return { allAccepted: true };\n}\n\nfunction matchesExpectedResult(resolvedCwd: ResolvedCwd, result: { allAccepted: boolean }): boolean {\n return result.allAccepted === (resolvedCwd.expectedResult === 'accepted');\n}\n\nfunction runBuild(\n buildCommand: readonly [string, ...string[]],\n context: { cwd: string; env: NodeJS.ProcessEnv; testCaseId: string; limits: CommandJudgeLimits }\n): (Omit<TestCaseResult, 'testCaseId'> & { testCaseId: string }) | undefined {\n const spawnResult = spawnSyncWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd: context.cwd, encoding: 'utf8', env: context.env },\n context.limits.buildTimeoutSeconds\n );\n const exitStatus = spawnResult.status ?? undefined;\n\n if (spawnResult.status !== 0) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (spawnResult.timeSeconds > context.limits.buildTimeoutSeconds) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (\n spawnResult.stdout.length > context.limits.maxOutputLength ||\n spawnResult.stderr.length > context.limits.maxOutputLength\n ) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n return;\n}\n\nfunction errorToMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nasync function readCommandTestCases<TTestCase extends BaseCommandTestCase = CommandTestCase>(\n problemDir: string\n): Promise<readonly TTestCase[]> {\n // The default reader yields the base shape; a narrower TTestCase must come from `options.readTestCases`.\n return (await readFileTestCases(path.join(problemDir, 'test_cases'))) as unknown as readonly TTestCase[];\n}\n\n/** The default verdict: a case exposing a string `output` / `fileOutputPath` is judged against it. */\nasync function compareWithExpectedOutputs(context: {\n testCase: BaseCommandTestCase & Partial<CommandTestCase>;\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n cwd: string;\n}): Promise<Partial<CommandJudgeCaseResult>> {\n const { testCase, runResult, outputFiles, cwd } = context;\n const judgement = await judgeAgainstExpectations({\n stdout: runResult.stdout,\n expectedStdout: typeof testCase.output === 'string' ? testCase.output : undefined,\n fileOutputPath: typeof testCase.fileOutputPath === 'string' ? testCase.fileOutputPath : undefined,\n cwd,\n outputFiles,\n });\n outputFiles.splice(0, outputFiles.length, ...judgement.outputFiles);\n return judgement.matches ? {} : { decisionCode: DecisionCode.WRONG_ANSWER };\n}\n\nfunction runCommand(\n command: readonly [string, ...string[]],\n context: {\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }\n): CommandRunResult {\n const spawnResult = spawnSyncWithTimeout(\n command[0],\n command.slice(1),\n { cwd: context.cwd, encoding: 'utf8', input: context.stdin, env: context.env },\n context.timeLimitSeconds\n );\n\n return {\n stdin: context.stdin,\n stdout: spawnResult.stdout,\n stderr: spawnResult.stderr,\n status: spawnResult.status ?? undefined,\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n}\n\nfunction evaluateByLimits(context: {\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n}): CommandJudgeCaseResult {\n if (context.runResult.status !== 0) {\n return { decisionCode: DecisionCode.RUNTIME_ERROR, stderr: context.runResult.stderr };\n }\n\n if (context.runResult.timeSeconds > context.context.timeLimitSeconds) {\n return { decisionCode: DecisionCode.TIME_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.memoryBytes >\n (context.context.problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)\n ) {\n return { decisionCode: DecisionCode.MEMORY_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.outputLimitExceeded ||\n context.runResult.stdout.length > context.context.outputLimitLength ||\n context.runResult.stderr.length > context.context.outputLimitLength\n ) {\n return { decisionCode: DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n const requiredOutputFileCount = context.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0;\n if (context.outputFiles.length < requiredOutputFileCount) {\n return { decisionCode: DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR };\n }\n\n return { decisionCode: DecisionCode.ACCEPTED };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"command.js","names":["readFileTestCases"],"sources":["../../src/presets/command.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { z } from 'zod';\n\nimport { cleanWorkingDirectory, snapshotWorkingDirectory } from '../helpers/cleanWorkingDirectory.js';\nimport { judgeAgainstExpectations } from '../helpers/compareExpectedOutputFiles.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 { passesIsolationCheckInDebugMode } from '../helpers/runIsolationCheckInDebugMode.js';\nimport { readProblemMarkdownFrontMatter } from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases as readFileTestCases } from '../helpers/readTestCases.js';\nimport {\n printDebugCwdBanner,\n printDebugExpectationFailureBanner,\n resolveCwds,\n type ResolvedCwd,\n} from '../helpers/resolveCwds.js';\nimport { spawnWithTimeout } from '../helpers/spawnWithTimeout.js';\nimport { DecisionCode } from '../types/decisionCode.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 = 2;\nconst MAX_STDOUT_LENGTH = 50_000;\n\nconst judgeParamsSchema = z.object({\n language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\ntype JudgeParams = z.infer<typeof judgeParamsSchema>;\n\n/** What every command test case needs; a custom `readTestCases` may add any fields of its own. */\ninterface BaseCommandTestCase {\n id: string;\n /** Standard input (`test_cases/<id>.in`). */\n input?: string;\n /** Directory copied into the working directory before the run (`test_cases/<id>.fin/`). */\n fileInputPath?: string;\n}\n\n/** A test case read from `test_cases/` by the default reader. */\nexport interface CommandTestCase extends BaseCommandTestCase {\n /** Expected standard output (`test_cases/<id>.out`). */\n output?: string;\n /** Directory of expected output files compared after the run (`test_cases/<id>.fout/`). */\n fileOutputPath?: string;\n}\n\ntype CommandJudgeCaseResult = Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown' | 'stderr'>;\n\nexport interface CommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n /** See `TestCaseResult.cpuTimeSeconds`; a custom `runCommand` may leave it out. */\n cpuTimeSeconds?: number;\n memoryBytes: number;\n outputLimitExceeded?: boolean;\n}\n\ninterface CommandJudgeContext {\n timeLimitSeconds: number;\n outputLimitLength: number;\n problemMarkdownFrontMatter: Pick<ProblemMarkdownFrontMatter, 'memoryLimitByte' | 'requiredOutputFilePaths'>;\n}\n\nexport interface CommandJudgeLimits {\n buildTimeoutSeconds: number;\n maxOutputLength: number;\n}\n\ninterface ResolvedCommandProblem<TTestCase extends BaseCommandTestCase> {\n problemMarkdownFrontMatter: ProblemMarkdownFrontMatter;\n testCases: readonly TTestCase[];\n limits: CommandJudgeLimits;\n timeLimitSeconds: number;\n}\n\nexport interface CommandJudgePresetOptions<\n TTestCase extends BaseCommandTestCase = CommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n> {\n /**\n * Set to false when the custom runner builds the submitted source itself.\n * Defaults to true.\n */\n buildSubmission?: boolean;\n limits?: CommandJudgeLimits;\n /** The per-case time limit used only when `problem.md` declares no `timeLimitMs`. */\n runTimeoutSeconds?: number;\n readTestCases?: (problemDir: string) => Promise<readonly TTestCase[]>;\n resolveInput?: (context: { testCase: TTestCase; cwd: string; env: NodeJS.ProcessEnv }) => Promise<string> | string;\n runCommand?: (context: {\n testCase: TTestCase;\n command: readonly [string, ...string[]];\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }) => Promise<TRunResult> | TRunResult;\n /**\n * Decides the verdict of a run that passed the limit checks. It replaces the default comparison\n * of `testCase.output` (space-separated tokens) and `testCase.fileOutputPath` (see\n * `compareStdoutAsSpaceSeparatedTokens` and `compareExpectedOutputFiles`, both exported), so\n * call those yourself when the test case ships `.out` / `.fout` expectations to keep.\n * `outputFiles` is the array printed with the result and may be edited in place.\n */\n test?: (context: {\n testCase: TTestCase;\n runResult: TRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n /** The submission's working directory, e.g. for `compareExpectedOutputFiles(cwd, testCase.fileOutputPath)`. */\n cwd: string;\n context: CommandJudgeContext;\n }) => Promise<Partial<CommandJudgeCaseResult>> | Partial<CommandJudgeCaseResult> | undefined;\n}\n\n/**\n * A preset function for judging by executable command.\n *\n * Without options, test cases come from `test_cases/` (`.in`, `.out`, `.fin/`, `.fout/`) and a run\n * within the limits is accepted when its stdout matches `.out` and its files match `.fout/`; a\n * case without either expectation only has to run within the limits.\n * Keep problem-specific logic in `resolveInput` and `test`.\n *\n * @example\n * Create `judge.ts` that judges `test_cases/` like the default stdio harness, with the command\n * preset's debug mode:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n *\n * await commandJudgePreset(import.meta.dirname);\n * ```\n *\n * @example\n * Create `judge.ts` with an own verdict:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n * import { DecisionCode } from '@exercode/problem-utils';\n *\n * await commandJudgePreset(import.meta.dirname, {\n * readTestCases: async () => [\n * { id: '01', input: '1 2' },\n * ],\n * test: ({ runResult }) => {\n * return runResult.stdout.trim() === '3'\n * ? { decisionCode: DecisionCode.ACCEPTED }\n * : { decisionCode: DecisionCode.WRONG_ANSWER };\n * },\n * });\n * ```\n *\n * Run with the required parameters:\n * ```bash\n * bun judge.ts model_answers/python '{ \"language\": \"python\" }'\n * ```\n *\n * Run without a cwd argument to judge each `<problemDir>/model_answers/*` directory\n * for debugging. A prominent banner is printed for each run.\n * ```bash\n * bun judge.ts\n * ```\n */\nexport async function commandJudgePreset<\n TTestCase extends BaseCommandTestCase = CommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(problemDir: string, options: CommandJudgePresetOptions<TTestCase, TRunResult> = {}): Promise<void> {\n const args = parseArgs(process.argv);\n const params = judgeParamsSchema.parse(args.params);\n\n const { cwds, isDebugMode } = await resolveCwds(problemDir, args.cwd);\n const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n const testCases = await (options.readTestCases ?? readCommandTestCases)(problemDir);\n const limits = {\n buildTimeoutSeconds: options.limits?.buildTimeoutSeconds ?? BUILD_TIMEOUT_SECONDS,\n maxOutputLength: options.limits?.maxOutputLength ?? MAX_STDOUT_LENGTH,\n };\n const timeLimitSeconds =\n typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n ? problemMarkdownFrontMatter.timeLimitMs / 1000\n : (options.runTimeoutSeconds ?? JUDGE_DEFAULT_TIMEOUT_SECONDS);\n const problem: ResolvedCommandProblem<TTestCase> = {\n problemMarkdownFrontMatter,\n testCases,\n limits,\n timeLimitSeconds,\n };\n\n if (isDebugMode) {\n const expectedMaxDurationMs = (limits.buildTimeoutSeconds + testCases.length * timeLimitSeconds) * 1000;\n if (!(await passesIsolationCheckInDebugMode(problemDir, cwds, params, { expectedMaxDurationMs }))) {\n process.exitCode = 1;\n return;\n }\n }\n\n for (const resolvedCwd of cwds) {\n if (isDebugMode) printDebugCwdBanner(problemDir, resolvedCwd);\n const result = await runCommandJudgeForCwd<TTestCase, TRunResult>(resolvedCwd.cwd, params, problem, options);\n if (isDebugMode && !matchesExpectedResult(resolvedCwd, result)) {\n process.exitCode = 1;\n printDebugExpectationFailureBanner(problemDir, resolvedCwd);\n }\n }\n}\n\nasync function runCommandJudgeForCwd<\n TTestCase extends BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(\n cwd: string,\n params: JudgeParams,\n problem: ResolvedCommandProblem<TTestCase>,\n options: CommandJudgePresetOptions<TTestCase, TRunResult>\n): Promise<{ allAccepted: boolean }> {\n const { problemMarkdownFrontMatter, testCases, limits, timeLimitSeconds } = problem;\n const prebuildTestCaseId = testCases[0]?.id ?? 'prebuild';\n\n const staticAnalysisResult = await judgeByStaticAnalysis(cwd, problemMarkdownFrontMatter);\n if (staticAnalysisResult) {\n printTestCaseResult({ testCaseId: prebuildTestCaseId, ...staticAnalysisResult });\n return { allAccepted: false };\n }\n\n const originalMainFilePath = await findEntryPointFile(cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return { allAccepted: false };\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return { allAccepted: false };\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let mainFilePath = originalMainFilePath;\n if (options.buildSubmission !== false) {\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(cwd);\n const prebuiltMainFilePath = await findEntryPointFile(cwd, params.language);\n if (prebuiltMainFilePath) mainFilePath = prebuiltMainFilePath;\n } catch (error) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return { allAccepted: false };\n }\n }\n\n const buildCommand = languageDefinition.buildCommand?.(mainFilePath);\n if (buildCommand) {\n const buildResult = await runBuild(buildCommand, {\n cwd,\n env,\n testCaseId: prebuildTestCaseId,\n limits,\n });\n if (buildResult) {\n printTestCaseResult(buildResult);\n return { allAccepted: false };\n }\n }\n }\n\n const cwdSnapshot = await snapshotWorkingDirectory(cwd);\n\n if (testCases.length === 0) {\n printTestCaseResult({ testCaseId: 'default', decisionCode: DecisionCode.ACCEPTED });\n return { allAccepted: true };\n }\n\n const sharedFileInputPath = (testCases as { shared?: { fileInputPath?: string } }).shared?.fileInputPath;\n\n for (const testCase of testCases) {\n if (sharedFileInputPath) await copyTestCaseFileInput(sharedFileInputPath, cwd);\n if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, cwd);\n\n const command = languageDefinition.command(mainFilePath);\n let stdin = testCase.input ?? '';\n let customRunResult: TRunResult | undefined;\n try {\n if (options.resolveInput) {\n stdin = await options.resolveInput({ testCase, cwd, env });\n }\n\n if (options.runCommand) {\n customRunResult = await options.runCommand({ testCase, command, stdin, cwd, env, timeLimitSeconds });\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(cwd, cwdSnapshot);\n return { allAccepted: false };\n }\n\n // The default runner reports the submission's failures in its result; what it throws is the\n // judge's own failure and ends the run without a verdict.\n const runResult =\n customRunResult ?? ((await runCommand(command, { stdin, cwd, env, timeLimitSeconds })) as TRunResult);\n\n const outputFiles = await readOutputFiles(cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n const judgeContext: CommandJudgeContext = {\n timeLimitSeconds,\n outputLimitLength: limits.maxOutputLength,\n problemMarkdownFrontMatter: {\n memoryLimitByte: problemMarkdownFrontMatter.memoryLimitByte,\n requiredOutputFilePaths: problemMarkdownFrontMatter.requiredOutputFilePaths,\n },\n };\n const baseJudgeResult = evaluateByLimits({ runResult, outputFiles, context: judgeContext });\n let judgeResult = baseJudgeResult;\n if (baseJudgeResult.decisionCode === DecisionCode.ACCEPTED) {\n try {\n const extendedJudgeResult = options.test\n ? await options.test({ testCase, runResult, outputFiles, cwd, context: judgeContext })\n : await compareWithExpectedOutputs({ testCase, runResult, outputFiles, cwd });\n if (extendedJudgeResult) {\n judgeResult = {\n decisionCode: extendedJudgeResult.decisionCode ?? baseJudgeResult.decisionCode,\n feedbackMarkdown: extendedJudgeResult.feedbackMarkdown,\n stderr: extendedJudgeResult.stderr,\n };\n }\n } catch (error) {\n judgeResult = {\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stderr: errorToMessage(error),\n };\n }\n }\n\n printTestCaseResult({\n testCaseId: testCase.id,\n ...judgeResult,\n exitStatus: runResult.status,\n stdin: runResult.stdin,\n stdout: runResult.stdout.slice(0, limits.maxOutputLength) || undefined,\n stderr: (judgeResult.stderr ?? runResult.stderr).slice(0, limits.maxOutputLength) || undefined,\n timeSeconds: runResult.timeSeconds,\n cpuTimeSeconds: runResult.cpuTimeSeconds,\n memoryBytes: runResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n if (judgeResult.decisionCode !== DecisionCode.ACCEPTED) return { allAccepted: false };\n }\n\n return { allAccepted: true };\n}\n\nfunction matchesExpectedResult(resolvedCwd: ResolvedCwd, result: { allAccepted: boolean }): boolean {\n return result.allAccepted === (resolvedCwd.expectedResult === 'accepted');\n}\n\nasync function runBuild(\n buildCommand: readonly [string, ...string[]],\n context: { cwd: string; env: NodeJS.ProcessEnv; testCaseId: string; limits: CommandJudgeLimits }\n): Promise<(Omit<TestCaseResult, 'testCaseId'> & { testCaseId: string }) | undefined> {\n const spawnResult = await spawnWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd: context.cwd, env: context.env },\n context.limits.buildTimeoutSeconds\n );\n const exitStatus = spawnResult.status ?? undefined;\n\n if (spawnResult.status !== 0) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (spawnResult.timeSeconds > context.limits.buildTimeoutSeconds) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (\n spawnResult.outputLimitExceeded ||\n spawnResult.stdout.length > context.limits.maxOutputLength ||\n spawnResult.stderr.length > context.limits.maxOutputLength\n ) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n return;\n}\n\nfunction errorToMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nasync function readCommandTestCases<TTestCase extends BaseCommandTestCase = CommandTestCase>(\n problemDir: string\n): Promise<readonly TTestCase[]> {\n // The default reader yields the base shape; a narrower TTestCase must come from `options.readTestCases`.\n return (await readFileTestCases(path.join(problemDir, 'test_cases'))) as unknown as readonly TTestCase[];\n}\n\n/** The default verdict: a case exposing a string `output` / `fileOutputPath` is judged against it. */\nasync function compareWithExpectedOutputs(context: {\n testCase: BaseCommandTestCase & Partial<CommandTestCase>;\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n cwd: string;\n}): Promise<Partial<CommandJudgeCaseResult>> {\n const { testCase, runResult, outputFiles, cwd } = context;\n const judgement = await judgeAgainstExpectations({\n stdout: runResult.stdout,\n expectedStdout: typeof testCase.output === 'string' ? testCase.output : undefined,\n fileOutputPath: typeof testCase.fileOutputPath === 'string' ? testCase.fileOutputPath : undefined,\n cwd,\n outputFiles,\n });\n outputFiles.splice(0, outputFiles.length, ...judgement.outputFiles);\n return judgement.matches ? {} : { decisionCode: DecisionCode.WRONG_ANSWER };\n}\n\nasync function runCommand(\n command: readonly [string, ...string[]],\n context: {\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }\n): Promise<CommandRunResult> {\n const spawnResult = await spawnWithTimeout(\n command[0],\n command.slice(1),\n { cwd: context.cwd, stdin: context.stdin, env: context.env },\n context.timeLimitSeconds\n );\n\n return {\n stdin: context.stdin,\n stdout: spawnResult.stdout,\n stderr: spawnResult.stderr,\n status: spawnResult.status ?? undefined,\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n outputLimitExceeded: spawnResult.outputLimitExceeded,\n };\n}\n\nfunction evaluateByLimits(context: {\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n}): CommandJudgeCaseResult {\n if (context.runResult.status !== 0) {\n return { decisionCode: DecisionCode.RUNTIME_ERROR, stderr: context.runResult.stderr };\n }\n\n if (context.runResult.timeSeconds > context.context.timeLimitSeconds) {\n return { decisionCode: DecisionCode.TIME_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.memoryBytes >\n (context.context.problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)\n ) {\n return { decisionCode: DecisionCode.MEMORY_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.outputLimitExceeded ||\n context.runResult.stdout.length > context.context.outputLimitLength ||\n context.runResult.stderr.length > context.context.outputLimitLength\n ) {\n return { decisionCode: DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n const requiredOutputFileCount = context.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0;\n if (context.outputFiles.length < requiredOutputFileCount) {\n return { decisionCode: DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR };\n }\n\n return { decisionCode: DecisionCode.ACCEPTED };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../helpers/printTestCaseResult.cjs"),n=require("../helpers/parseArgs.cjs"),r=require("../helpers/readTestCases.cjs"),i=require("../types/language.cjs"),a=require("../helpers/findLanguageDefinitionByPath.cjs"),o=require("../helpers/readProblemMarkdownFrontMatter.cjs"),s=require("../types/decisionCode.cjs"),c=require("../helpers/cleanWorkingDirectory.cjs"),l=require("../helpers/copyTestCaseFileInput.cjs"),u=require("../helpers/findEntryPointFile.cjs"),d=require("../helpers/judgeByStaticAnalysis.cjs"),f=require("../helpers/readOutputFiles.cjs"),p=require("../helpers/spawnSyncWithTimeout.cjs");let m=require("node:path");m=e.__toESM(m,1);let h=require("zod"),g=require("node:fs/promises");g=e.__toESM(g,1);let _=require("node:child_process");_=e.__toESM(_,1);let v=require("node:os");v=e.__toESM(v,1);let y=require("node:timers/promises");const b=.3,x=[v.default.platform()===`darwin`?`gtime`:`/usr/bin/time`,`--format`,`%e %M`],S=h.z.object({language:h.z.union([h.z.string(),h.z.array(h.z.string())]).optional()});async function C(e,r){let i=n.parseArgs(process.argv);if(!i.cwd)throw Error(`cwd argument required`);let p=S.parse(i.params),m=i.cwd,h=await o.readProblemMarkdownFrontMatter(e),g=await(r.readTestCases??O)(e),_=g.length>0?g:[{id:`default`}],v=_[0]?.id??`prebuild`,y=await d.judgeByStaticAnalysis(i.cwd,h);if(y){t.printTestCaseResult({testCaseId:v,...y});return}let x=r.mainFilePath??await u.findEntryPointFile(i.cwd,p.language);if(!x){t.printTestCaseResult({testCaseId:v,decisionCode:s.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:r.mainFilePath?`required main file not found: ${r.mainFilePath}`:`main file not found${p.language?`: language: ${p.language}`:``}`});return}let C=a.findLanguageDefinitionByPath(x);if(!C){t.printTestCaseResult({testCaseId:v,decisionCode:s.DecisionCode.WRONG_ANSWER,stderr:`unsupported language`});return}let A={...process.env,CI:``,FORCE_COLOR:`0`},j=await w({cwd:i.cwd,language:p.language,configuredMainFilePath:r.mainFilePath});if(C.prebuild)try{await C.prebuild(i.cwd);let e=await w({cwd:i.cwd,language:p.language??T(x),configuredMainFilePath:r.mainFilePath,allowConfiguredPathFallback:!0});e&&(j=e)}catch(e){t.printTestCaseResult({testCaseId:v,decisionCode:s.DecisionCode.BUILD_ERROR,stderr:R(e)});return}if(!j){t.printTestCaseResult({testCaseId:v,decisionCode:s.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:r.mainFilePath?`required main file not found: ${r.mainFilePath}`:`main file not found${p.language?`: language: ${p.language}`:``}`});return}let M;if(r.prepare)try{M=await r.prepare({cwd:m,env:A,mainFilePath:j,problemMarkdownFrontMatter:h})}catch(e){t.printTestCaseResult({testCaseId:v,decisionCode:s.DecisionCode.BUILD_ERROR,stderr:R(e)});return}let P=M??E({cwd:i.cwd,env:A,mainFilePath:j,languageDefinition:C});if(P){t.printTestCaseResult({testCaseId:v,decisionCode:P.decisionCode??s.DecisionCode.BUILD_ERROR,feedbackMarkdown:P.feedbackMarkdown,stderr:P.stderr,stdout:P.stdout,outputFiles:P.outputFiles});return}let F=await c.snapshotWorkingDirectory(i.cwd),I,L=v,z;try{I=r.runCommand?void 0:await N();let e=g.shared?.fileInputPath;for(let n of _){L=n.id,e&&await l.copyTestCaseFileInput(e,i.cwd),n.fileInputPath&&await l.copyTestCaseFileInput(n.fileInputPath,i.cwd);let a=typeof h.timeLimitMs==`number`?h.timeLimitMs/1e3:r.runTimeoutSeconds??5,o=I?{...A,DISPLAY:I.display}:A,u=await r.resolveInput?.({testCase:n,cwd:i.cwd,env:o})??n.input??``;z=u;let d=await r.command?.({testCase:n,cwd:i.cwd,env:o,mainFilePath:j})??C.command(j),p;try{p=r.runCommand?await r.runCommand({testCase:n,command:d,stdin:u,cwd:m,env:o,timeLimitSeconds:a,screenshotWaitSeconds:r.screenshotWaitSeconds??b,stopDetectionThreshold:r.stopDetectionThreshold??5}):await k({command:d,stdin:u,cwd:i.cwd,env:o,timeLimitSeconds:a,screenshotWaitSeconds:r.screenshotWaitSeconds??b,stopDetectionThreshold:r.stopDetectionThreshold??5})}catch(e){t.printTestCaseResult({testCaseId:n.id,decisionCode:s.DecisionCode.RUNTIME_ERROR,stdin:u,stderr:R(e)}),await c.cleanWorkingDirectory(i.cwd,F);return}let g=await f.readOutputFiles(i.cwd,h.requiredOutputFilePaths??[]),_={timeLimitSeconds:a,problemMarkdownFrontMatter:{memoryLimitByte:h.memoryLimitByte,requiredOutputFilePaths:h.requiredOutputFilePaths}},v=D({runResult:p,outputFiles:g,context:_}),y=v;if(v.decisionCode===s.DecisionCode.ACCEPTED)try{let e=await r.test({testCase:n,runResult:p,outputFiles:g,cwd:i.cwd,context:_});y={decisionCode:e.decisionCode??v.decisionCode,feedbackMarkdown:e.feedbackMarkdown,stderr:e.stderr,stdout:e.stdout,outputFiles:e.outputFiles}}catch(e){y={decisionCode:s.DecisionCode.RUNTIME_ERROR,stderr:R(e)}}let x=y.decisionCode??s.DecisionCode.ACCEPTED,S=y.stdout??p.stdout,w=y.stderr??p.stderr;if(t.printTestCaseResult({testCaseId:n.id,decisionCode:x,exitStatus:p.status,stdin:p.stdin||void 0,stdout:S||void 0,stderr:w||void 0,timeSeconds:p.timeSeconds,memoryBytes:p.memoryBytes,feedbackMarkdown:y.feedbackMarkdown,outputFiles:y.outputFiles??(g.length>0?g:void 0)}),await c.cleanWorkingDirectory(i.cwd,F),x!==s.DecisionCode.ACCEPTED)break}}catch(e){t.printTestCaseResult({testCaseId:L,decisionCode:s.DecisionCode.RUNTIME_ERROR,stdin:z,stderr:R(e)}),await c.cleanWorkingDirectory(i.cwd,F)}finally{await I?.dispose()}}async function w(e){if(e.configuredMainFilePath){if(await L(m.default.join(e.cwd,e.configuredMainFilePath)))return e.configuredMainFilePath;if(!e.allowConfiguredPathFallback)return}return await u.findEntryPointFile(e.cwd,e.language)}function T(e){let t=Object.entries(i.languageIdToDefinition).filter(([,t])=>t.fileExtensions.some(t=>e.endsWith(t))).map(([e])=>e);return t.length>0?t:void 0}function E(e){let t=e.languageDefinition.buildCommand?.(e.mainFilePath);if(!t)return;let n=p.spawnSyncWithTimeout(t[0],t.slice(1),{cwd:e.cwd,encoding:`utf8`,env:e.env},10);if(n.timeSeconds>10)return{decisionCode:s.DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,stderr:n.stderr||void 0};if(n.status!==0)return{decisionCode:s.DecisionCode.BUILD_ERROR,stderr:n.stderr||n.stdout||void 0}}function D(e){if(e.runResult.stopReason===`timeout`)return{decisionCode:s.DecisionCode.TIME_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.status!==0)return{decisionCode:s.DecisionCode.RUNTIME_ERROR,stderr:e.runResult.stderr};if(e.runResult.memoryBytes>(e.context.problemMarkdownFrontMatter.memoryLimitByte??1/0))return{decisionCode:s.DecisionCode.MEMORY_LIMIT_EXCEEDED,stderr:e.runResult.stderr};let t=e.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length??0;return e.outputFiles.length<t?{decisionCode:s.DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR}:{decisionCode:s.DecisionCode.ACCEPTED}}async function O(e){return await r.readTestCases(m.default.join(e,`test_cases`))}async function k(e){let t=_.default.spawn(`timeout`,[e.timeLimitSeconds.toFixed(3),...x,...e.command],{cwd:e.cwd,env:e.env,detached:process.platform!==`win32`,stdio:[`pipe`,`pipe`,`pipe`]});t.stdout.setEncoding(`utf8`),t.stderr.setEncoding(`utf8`);let n=``,r=``,i,a,o=`process_exit`,s=[],c=[],l=Date.now()/1e3,u=0;t.stdout.on(`data`,e=>{n+=e}),t.stderr.on(`data`,e=>{r+=e}),t.on(`error`,e=>{a=e,i=1}),t.stdin.on(`error`,()=>{}),t.on(`close`,(e,t)=>{if(e===124){o=`timeout`,i=0;return}if(t){i=1,r=r?`${r}\nprocess terminated by signal: ${t}`:`process terminated by signal: ${t}`;return}i=e??1});try{for(e.stdin&&t.stdin.write(e.stdin),t.stdin.end();i===void 0;){if(await(0,y.setTimeout)(e.screenshotWaitSeconds*1e3),u=Math.max(u,F(t.pid)),c=A(e.env.DISPLAY).toSorted((e,t)=>e.data.length-t.data.length),c.length>0){let t=c.map(e=>e.data).toSorted();if(s.unshift(t),s.length=Math.min(s.length,e.stopDetectionThreshold),s.length===e.stopDetectionThreshold&&s.every(e=>e.length===t.length&&e.every((e,n)=>e===t[n]))){o=`stable_screenshot`,i=0;break}}if(Date.now()/1e3-l>e.timeLimitSeconds){o=`timeout`,i=0;break}}o!==`process_exit`&&t.exitCode===null&&(t.removeAllListeners(`close`),t.removeAllListeners(`error`))}finally{await P(t)}if(a)throw a;let{memoryBytes:d,stderr:f,timeSeconds:p}=M(r,l,u);return{stdin:e.stdin,stdout:n.trimEnd(),stderr:f,status:i,timeSeconds:p,memoryBytes:d,screenshots:c,stopReason:o}}function A(e){let t={...process.env,DISPLAY:e??process.env.DISPLAY},n=_.default.spawnSync(`xwininfo`,[`-root`,`-tree`],{encoding:`utf8`,env:t});if(n.error)throw n.error;if(n.status!==0||!n.stdout)return[];let r=[];for(let e of j(n.stdout)){let n=_.default.spawnSync(`maim`,[`-i`,e],{env:t});if(n.error)throw n.error;if(n.status!==0||n.stdout.length===0)continue;let i=_.default.spawnSync(`xdotool`,[`getwindowname`,e],{encoding:`utf8`,env:t});if(i.error)throw i.error;let a=i.stdout.trim().replaceAll(/[\s/]/g,`_`);r.push({path:`${a||`window`}_${e}.png`,data:n.stdout.toString(`base64`),encoding:`base64`})}return r}function j(e){let t=[],n=e.split(`
2
- `);for(let e of n){if(e.includes(`Root window id:`)||e.includes(`Parent window id:`)||e.includes(`()`))continue;let n=/^\s{5}(0x[\da-f]+) /.exec(e);n?.[1]&&t.push(Number.parseInt(n[1],16).toString())}return t}function M(e,t,n){let r=/(?:^|\n)(\d+\.\d+) (\d+)\s*$/.exec(e),i=r?e.slice(0,r.index).trimEnd():e.trimEnd(),a=Number(r?.[2])*1024||0;return{stderr:i,timeSeconds:Number(r?.[1])||Date.now()/1e3-t,memoryBytes:Math.max(a,n)}}async function N(){if(process.platform!==`linux`)throw Error(`GUI screenshot capture is supported only on Linux.`);for(let e=90;e<100;e++){let t=`:${e}`,n,r=_.default.spawn(`Xvfb`,[t,`-screen`,`0`,`1280x1024x24`,`-ac`],{stdio:`ignore`});if(r.on(`error`,e=>{n=e}),await(0,y.setTimeout)(300),n)throw n;if(r.exitCode===null)return{display:t,dispose:async()=>{r.killed||(r.kill(`SIGTERM`),await(0,y.setTimeout)(100),r.exitCode===null&&r.kill(`SIGKILL`))}}}throw Error(`Xvfb could not be started.`)}async function P(e){e.pid&&(I(e.pid,`SIGTERM`),await(0,y.setTimeout)(200),e.exitCode===null&&I(e.pid,`SIGKILL`))}function F(e){if(!e||process.platform!==`linux`)return 0;let t=_.default.spawnSync(`ps`,[`-o`,`rss=`,`--no-headers`,`--pgroup`,String(e)],{encoding:`utf8`});return t.error||t.status!==0||!t.stdout?0:t.stdout.split(`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../helpers/printTestCaseResult.cjs"),n=require("../helpers/parseArgs.cjs"),r=require("../helpers/readTestCases.cjs"),i=require("../types/language.cjs"),a=require("../helpers/findLanguageDefinitionByPath.cjs"),o=require("../helpers/readProblemMarkdownFrontMatter.cjs");require("../helpers/stdioJudgeRules.cjs");const s=require("../types/decisionCode.cjs"),c=require("../helpers/cleanWorkingDirectory.cjs"),l=require("../helpers/copyTestCaseFileInput.cjs"),u=require("../helpers/findEntryPointFile.cjs"),d=require("../helpers/judgeByStaticAnalysis.cjs"),f=require("../helpers/readOutputFiles.cjs"),p=require("../helpers/spawnWithTimeout.cjs");let m=require("node:path");m=e.__toESM(m,1);let h=require("zod"),g=require("node:fs/promises");g=e.__toESM(g,1);let _=require("node:os");_=e.__toESM(_,1);let v=require("node:child_process");v=e.__toESM(v,1);let y=require("node:timers/promises");const b=.3,x=[_.default.platform()===`darwin`?`gtime`:`/usr/bin/time`,`--format`,`%e %M`],S=h.z.object({language:h.z.union([h.z.string(),h.z.array(h.z.string())]).optional()});async function C(e,r){let i=n.parseArgs(process.argv);if(!i.cwd)throw Error(`cwd argument required`);let p=S.parse(i.params),m=i.cwd,h=await o.readProblemMarkdownFrontMatter(e),g=await(r.readTestCases??O)(e),_=g.length>0?g:[{id:`default`}],v=_[0]?.id??`prebuild`,y=await d.judgeByStaticAnalysis(i.cwd,h);if(y){t.printTestCaseResult({testCaseId:v,...y});return}let x=r.mainFilePath??await u.findEntryPointFile(i.cwd,p.language);if(!x){t.printTestCaseResult({testCaseId:v,decisionCode:s.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:r.mainFilePath?`required main file not found: ${r.mainFilePath}`:`main file not found${p.language?`: language: ${p.language}`:``}`});return}let C=a.findLanguageDefinitionByPath(x);if(!C){t.printTestCaseResult({testCaseId:v,decisionCode:s.DecisionCode.WRONG_ANSWER,stderr:`unsupported language`});return}let A={...process.env,CI:``,FORCE_COLOR:`0`},j=await w({cwd:i.cwd,language:p.language,configuredMainFilePath:r.mainFilePath});if(C.prebuild)try{await C.prebuild(i.cwd);let e=await w({cwd:i.cwd,language:p.language??T(x),configuredMainFilePath:r.mainFilePath,allowConfiguredPathFallback:!0});e&&(j=e)}catch(e){t.printTestCaseResult({testCaseId:v,decisionCode:s.DecisionCode.BUILD_ERROR,stderr:R(e)});return}if(!j){t.printTestCaseResult({testCaseId:v,decisionCode:s.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:r.mainFilePath?`required main file not found: ${r.mainFilePath}`:`main file not found${p.language?`: language: ${p.language}`:``}`});return}let M;if(r.prepare)try{M=await r.prepare({cwd:m,env:A,mainFilePath:j,problemMarkdownFrontMatter:h})}catch(e){t.printTestCaseResult({testCaseId:v,decisionCode:s.DecisionCode.BUILD_ERROR,stderr:R(e)});return}let P=M??await E({cwd:i.cwd,env:A,mainFilePath:j,languageDefinition:C});if(P){t.printTestCaseResult({testCaseId:v,decisionCode:P.decisionCode??s.DecisionCode.BUILD_ERROR,feedbackMarkdown:P.feedbackMarkdown,stderr:P.stderr,stdout:P.stdout,outputFiles:P.outputFiles});return}let F=await c.snapshotWorkingDirectory(i.cwd),I,L=v,z;try{I=r.runCommand?void 0:await N();let e=g.shared?.fileInputPath;for(let n of _){L=n.id,e&&await l.copyTestCaseFileInput(e,i.cwd),n.fileInputPath&&await l.copyTestCaseFileInput(n.fileInputPath,i.cwd);let a=typeof h.timeLimitMs==`number`?h.timeLimitMs/1e3:r.runTimeoutSeconds??5,o=I?{...A,DISPLAY:I.display}:A,u=await r.resolveInput?.({testCase:n,cwd:i.cwd,env:o})??n.input??``;z=u;let d=await r.command?.({testCase:n,cwd:i.cwd,env:o,mainFilePath:j})??C.command(j),p;try{p=r.runCommand?await r.runCommand({testCase:n,command:d,stdin:u,cwd:m,env:o,timeLimitSeconds:a,screenshotWaitSeconds:r.screenshotWaitSeconds??b,stopDetectionThreshold:r.stopDetectionThreshold??5}):await k({command:d,stdin:u,cwd:i.cwd,env:o,timeLimitSeconds:a,screenshotWaitSeconds:r.screenshotWaitSeconds??b,stopDetectionThreshold:r.stopDetectionThreshold??5})}catch(e){t.printTestCaseResult({testCaseId:n.id,decisionCode:s.DecisionCode.RUNTIME_ERROR,stdin:u,stderr:R(e)}),await c.cleanWorkingDirectory(i.cwd,F);return}let g=await f.readOutputFiles(i.cwd,h.requiredOutputFilePaths??[]),_={timeLimitSeconds:a,problemMarkdownFrontMatter:{memoryLimitByte:h.memoryLimitByte,requiredOutputFilePaths:h.requiredOutputFilePaths}},v=D({runResult:p,outputFiles:g,context:_}),y=v;if(v.decisionCode===s.DecisionCode.ACCEPTED)try{let e=await r.test({testCase:n,runResult:p,outputFiles:g,cwd:i.cwd,context:_});y={decisionCode:e.decisionCode??v.decisionCode,feedbackMarkdown:e.feedbackMarkdown,stderr:e.stderr,stdout:e.stdout,outputFiles:e.outputFiles}}catch(e){y={decisionCode:s.DecisionCode.RUNTIME_ERROR,stderr:R(e)}}let x=y.decisionCode??s.DecisionCode.ACCEPTED,S=y.stdout??p.stdout,w=y.stderr??p.stderr;if(t.printTestCaseResult({testCaseId:n.id,decisionCode:x,exitStatus:p.status,stdin:p.stdin||void 0,stdout:S||void 0,stderr:w||void 0,timeSeconds:p.timeSeconds,memoryBytes:p.memoryBytes,feedbackMarkdown:y.feedbackMarkdown,outputFiles:y.outputFiles??(g.length>0?g:void 0)}),await c.cleanWorkingDirectory(i.cwd,F),x!==s.DecisionCode.ACCEPTED)break}}catch(e){t.printTestCaseResult({testCaseId:L,decisionCode:s.DecisionCode.RUNTIME_ERROR,stdin:z,stderr:R(e)}),await c.cleanWorkingDirectory(i.cwd,F)}finally{await I?.dispose()}}async function w(e){if(e.configuredMainFilePath){if(await L(m.default.join(e.cwd,e.configuredMainFilePath)))return e.configuredMainFilePath;if(!e.allowConfiguredPathFallback)return}return await u.findEntryPointFile(e.cwd,e.language)}function T(e){let t=Object.entries(i.languageIdToDefinition).filter(([,t])=>t.fileExtensions.some(t=>e.endsWith(t))).map(([e])=>e);return t.length>0?t:void 0}async function E(e){let t=e.languageDefinition.buildCommand?.(e.mainFilePath);if(!t)return;let n=await p.spawnWithTimeout(t[0],t.slice(1),{cwd:e.cwd,env:e.env},10),r=(n.stderr||n.stdout).slice(0,5e4)||void 0;if(n.timeSeconds>10)return{decisionCode:s.DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,stderr:r};if(n.status!==0)return{decisionCode:s.DecisionCode.BUILD_ERROR,stderr:r};if(n.outputLimitExceeded)return{decisionCode:s.DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,stderr:r}}function D(e){if(e.runResult.stopReason===`timeout`)return{decisionCode:s.DecisionCode.TIME_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.status!==0)return{decisionCode:s.DecisionCode.RUNTIME_ERROR,stderr:e.runResult.stderr};if(e.runResult.memoryBytes>(e.context.problemMarkdownFrontMatter.memoryLimitByte??1/0))return{decisionCode:s.DecisionCode.MEMORY_LIMIT_EXCEEDED,stderr:e.runResult.stderr};let t=e.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length??0;return e.outputFiles.length<t?{decisionCode:s.DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR}:{decisionCode:s.DecisionCode.ACCEPTED}}async function O(e){return await r.readTestCases(m.default.join(e,`test_cases`))}async function k(e){let t=v.default.spawn(`timeout`,[e.timeLimitSeconds.toFixed(3),...x,...e.command],{cwd:e.cwd,env:e.env,detached:process.platform!==`win32`,stdio:[`pipe`,`pipe`,`pipe`]});t.stdout.setEncoding(`utf8`),t.stderr.setEncoding(`utf8`);let n=``,r=``,i,a,o=`process_exit`,s=[],c=[],l=Date.now()/1e3,u=0;t.stdout.on(`data`,e=>{n+=e}),t.stderr.on(`data`,e=>{r+=e}),t.on(`error`,e=>{a=e,i=1}),t.stdin.on(`error`,()=>{}),t.on(`close`,(e,t)=>{if(e===124){o=`timeout`,i=0;return}if(t){i=1,r=r?`${r}\nprocess terminated by signal: ${t}`:`process terminated by signal: ${t}`;return}i=e??1});try{for(e.stdin&&t.stdin.write(e.stdin),t.stdin.end();i===void 0;){if(await(0,y.setTimeout)(e.screenshotWaitSeconds*1e3),u=Math.max(u,F(t.pid)),c=A(e.env.DISPLAY).toSorted((e,t)=>e.data.length-t.data.length),c.length>0){let t=c.map(e=>e.data).toSorted();if(s.unshift(t),s.length=Math.min(s.length,e.stopDetectionThreshold),s.length===e.stopDetectionThreshold&&s.every(e=>e.length===t.length&&e.every((e,n)=>e===t[n]))){o=`stable_screenshot`,i=0;break}}if(Date.now()/1e3-l>e.timeLimitSeconds){o=`timeout`,i=0;break}}o!==`process_exit`&&t.exitCode===null&&(t.removeAllListeners(`close`),t.removeAllListeners(`error`))}finally{await P(t)}if(a)throw a;let{memoryBytes:d,stderr:f,timeSeconds:p}=M(r,l,u);return{stdin:e.stdin,stdout:n.trimEnd(),stderr:f,status:i,timeSeconds:p,memoryBytes:d,screenshots:c,stopReason:o}}function A(e){let t={...process.env,DISPLAY:e??process.env.DISPLAY},n=v.default.spawnSync(`xwininfo`,[`-root`,`-tree`],{encoding:`utf8`,env:t});if(n.error)throw n.error;if(n.status!==0||!n.stdout)return[];let r=[];for(let e of j(n.stdout)){let n=v.default.spawnSync(`maim`,[`-i`,e],{env:t});if(n.error)throw n.error;if(n.status!==0||n.stdout.length===0)continue;let i=v.default.spawnSync(`xdotool`,[`getwindowname`,e],{encoding:`utf8`,env:t});if(i.error)throw i.error;let a=i.stdout.trim().replaceAll(/[\s/]/g,`_`);r.push({path:`${a||`window`}_${e}.png`,data:n.stdout.toString(`base64`),encoding:`base64`})}return r}function j(e){let t=[],n=e.split(`
2
+ `);for(let e of n){if(e.includes(`Root window id:`)||e.includes(`Parent window id:`)||e.includes(`()`))continue;let n=/^\s{5}(0x[\da-f]+) /.exec(e);n?.[1]&&t.push(Number.parseInt(n[1],16).toString())}return t}function M(e,t,n){let r=/(?:^|\n)(\d+\.\d+) (\d+)\s*$/.exec(e),i=r?e.slice(0,r.index).trimEnd():e.trimEnd(),a=Number(r?.[2])*1024||0;return{stderr:i,timeSeconds:Number(r?.[1])||Date.now()/1e3-t,memoryBytes:Math.max(a,n)}}async function N(){if(process.platform!==`linux`)throw Error(`GUI screenshot capture is supported only on Linux.`);for(let e=90;e<100;e++){let t=`:${e}`,n,r=v.default.spawn(`Xvfb`,[t,`-screen`,`0`,`1280x1024x24`,`-ac`],{stdio:`ignore`});if(r.on(`error`,e=>{n=e}),await(0,y.setTimeout)(300),n)throw n;if(r.exitCode===null)return{display:t,dispose:async()=>{r.killed||(r.kill(`SIGTERM`),await(0,y.setTimeout)(100),r.exitCode===null&&r.kill(`SIGKILL`))}}}throw Error(`Xvfb could not be started.`)}async function P(e){e.pid&&(I(e.pid,`SIGTERM`),await(0,y.setTimeout)(200),e.exitCode===null&&I(e.pid,`SIGKILL`))}function F(e){if(!e||process.platform!==`linux`)return 0;let t=v.default.spawnSync(`ps`,[`-o`,`rss=`,`--no-headers`,`--pgroup`,String(e)],{encoding:`utf8`});return t.error||t.status!==0||!t.stdout?0:t.stdout.split(`
3
3
  `).map(e=>Number(e.trim())).filter(e=>Number.isFinite(e)&&e>0).reduce((e,t)=>e+t*1024,0)}function I(e,t){try{if(process.platform!==`win32`){process.kill(-e,t);return}}catch{}try{process.kill(e,t)}catch{}}async function L(e){try{return await g.default.access(e),!0}catch{return!1}}function R(e){return e instanceof Error?e.message:String(e)}exports.guiCommandJudgePreset=C;
4
4
  //# sourceMappingURL=guiCommand.cjs.map