@exercode/problem-utils 3.1.1 → 3.2.1

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 (35) hide show
  1. package/README.md +6 -1
  2. package/dist/helpers/runCommandInTemporaryPackageManagerProject.cjs +1 -1
  3. package/dist/helpers/runCommandInTemporaryPackageManagerProject.cjs.map +1 -1
  4. package/dist/helpers/runCommandInTemporaryPackageManagerProject.d.ts +2 -0
  5. package/dist/helpers/runCommandInTemporaryPackageManagerProject.js +1 -1
  6. package/dist/helpers/runCommandInTemporaryPackageManagerProject.js.map +1 -1
  7. package/dist/helpers/spawnWithLimits.cjs +1 -1
  8. package/dist/helpers/spawnWithLimits.cjs.map +1 -1
  9. package/dist/helpers/spawnWithLimits.d.ts +14 -3
  10. package/dist/helpers/spawnWithLimits.js +1 -1
  11. package/dist/helpers/spawnWithLimits.js.map +1 -1
  12. package/dist/helpers/spawnWithTimeout.cjs +2 -2
  13. package/dist/helpers/spawnWithTimeout.cjs.map +1 -1
  14. package/dist/helpers/spawnWithTimeout.d.ts +2 -0
  15. package/dist/helpers/spawnWithTimeout.js +2 -2
  16. package/dist/helpers/spawnWithTimeout.js.map +1 -1
  17. package/dist/presets/command.cjs +1 -1
  18. package/dist/presets/command.cjs.map +1 -1
  19. package/dist/presets/command.d.ts +2 -0
  20. package/dist/presets/command.js +1 -1
  21. package/dist/presets/command.js.map +1 -1
  22. package/dist/presets/llm.cjs +1 -1
  23. package/dist/presets/llm.cjs.map +1 -1
  24. package/dist/presets/llm.js +1 -1
  25. package/dist/presets/llm.js.map +1 -1
  26. package/dist/presets/stdio.cjs +1 -1
  27. package/dist/presets/stdio.cjs.map +1 -1
  28. package/dist/presets/stdio.js +1 -1
  29. package/dist/presets/stdio.js.map +1 -1
  30. package/dist/types/testCaseResult.cjs +1 -1
  31. package/dist/types/testCaseResult.cjs.map +1 -1
  32. package/dist/types/testCaseResult.d.ts +1 -0
  33. package/dist/types/testCaseResult.js +1 -1
  34. package/dist/types/testCaseResult.js.map +1 -1
  35. package/package.json +3 -3
@@ -1,4 +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.stderr}${o.stderr&&!o.stderr.endsWith(`
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
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,memoryBytes:o.memoryBytes,outputLimitExceeded:o.outputLimitExceeded}}export{n as spawnWithTimeout};
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
4
  //# sourceMappingURL=spawnWithTimeout.js.map
@@ -1 +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 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 const stderr = result.timeCommandMessage\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 memoryBytes: result.memoryBytes,\n outputLimitExceeded: result.outputLimitExceeded,\n };\n}\n"],"mappings":"mFAWA,eAAsB,EACpB,EACA,EACA,EACA,EAQC,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,EAGK,EAAS,EAAO,mBAClB,GAAG,EAAO,SAAS,EAAO,QAAU,CAAC,EAAO,OAAO,SAAS;CAAI,EAAI;EAAO,KAAK,EAAO,mBAAmB,IAC1G,EAAO,OAEX,MAAO,CACL,OAAQ,EAAO,OACf,SACA,OAAQ,EAAO,UAAY,EAAO,oBAAsB,EAAI,EAAO,OACnE,YAAa,EAAO,SAAW,EAAiB,KAAO,EAAO,YAC9D,YAAa,EAAO,YACpB,oBAAqB,EAAO,mBAC9B,CACF"}
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";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,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,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.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,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,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;
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","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 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 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 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.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 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 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
+ {"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":"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"}
@@ -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{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,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,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.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,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,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};
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 { 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 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 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 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.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 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 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
+ {"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,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/parseArgs.cjs"),r=require("../helpers/readTestCases.cjs"),i=require("../types/decisionCode.cjs");let a=require("node:fs");a=e.__toESM(a,1);let o=require("node:path");o=e.__toESM(o,1);let s=require("zod"),c=require("@ai-sdk/amazon-bedrock"),l=require("@ai-sdk/google"),u=require("@ai-sdk/openai"),d=require("@ai-sdk/xai"),f=require("ai");const p={bedrock:c.bedrock,google:l.google,openai:u.openai,xai:d.xai},m=s.z.object({model:s.z.string().min(1)});async function h(e,s){let c=n.parseArgs(process.argv);if(!c.cwd)throw Error(`cwd argument required`);let l=m.parse(c.params),u=await r.readTestCases(o.default.join(e,`test_cases`)),d=await a.default.promises.readFile(o.default.join(c.cwd,`prompt.txt`),`utf8`);for(let e of u){let n=Date.now();try{let{text:r}=await(0,f.generateText)({model:g(l.model),prompt:s.buildPrompt?.({prompt:d,testCase:e})??d.replaceAll(`{input}`,e.input??``)}),a=Date.now(),o={testCaseId:e.id,decisionCode:i.DecisionCode.ACCEPTED,stdin:e.input,stdout:r,timeSeconds:(a-n)/1e3,...await s.test({testCase:e,result:{output:r}})};if(t.printTestCaseResult(o),o.decisionCode!==i.DecisionCode.ACCEPTED)break}catch(r){let a=Date.now();t.printTestCaseResult({testCaseId:e.id,decisionCode:i.DecisionCode.RUNTIME_ERROR,stdin:e.input,stderr:r instanceof Error?r.message:String(r),timeSeconds:(a-n)/1e3});break}}}function g(e){let[t,n]=e.split(`/`);if(!t||!n)throw Error(`bad model: ${e}`);let r=p[t]?.(n);if(!r)throw Error(`model not found: ${e}`);return r}exports.llmJudgePreset=h;
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/decisionCode.cjs");let a=require("node:fs");a=e.__toESM(a,1);let o=require("node:path");o=e.__toESM(o,1);let s=require("zod"),c=require("@ai-sdk/amazon-bedrock"),l=require("@ai-sdk/google"),u=require("@ai-sdk/openai"),d=require("@ai-sdk/xai"),f=require("ai");const p={bedrock:c.bedrock,google:l.google,openai:u.openai,xai:d.xai},m=s.z.object({model:s.z.string().min(1)});async function h(e,s){let c=n.parseArgs(process.argv);if(!c.cwd)throw Error(`cwd argument required`);let l=m.parse(c.params),u=await r.readTestCases(o.default.join(e,`test_cases`)),d=await a.default.promises.readFile(o.default.join(c.cwd,`prompt.txt`),`utf8`);for(let e of u){let n=Date.now();try{let{text:r}=await(0,f.generateText)({model:_(l.model),...g(s.buildPrompt?.({prompt:d,testCase:e})??d.replaceAll(`{input}`,e.input??``))}),a=Date.now(),o={testCaseId:e.id,decisionCode:i.DecisionCode.ACCEPTED,stdin:e.input,stdout:r,timeSeconds:(a-n)/1e3,...await s.test({testCase:e,result:{output:r}})};if(t.printTestCaseResult(o),o.decisionCode!==i.DecisionCode.ACCEPTED)break}catch(r){let a=Date.now();t.printTestCaseResult({testCaseId:e.id,decisionCode:i.DecisionCode.RUNTIME_ERROR,stdin:e.input,stderr:r instanceof Error?r.message:String(r),timeSeconds:(a-n)/1e3});break}}}function g(e){return typeof e==`string`?{prompt:e}:{messages:e,allowSystemInMessages:!0}}function _(e){let[t,n]=e.split(`/`);if(!t||!n)throw Error(`bad model: ${e}`);let r=p[t]?.(n);if(!r)throw Error(`model not found: ${e}`);return r}exports.llmJudgePreset=h;
2
2
  //# sourceMappingURL=llm.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"llm.cjs","names":["z","parseArgs","readTestCases","path","fs","generateText","DecisionCode"],"sources":["../../src/presets/llm.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { bedrock } from '@ai-sdk/amazon-bedrock';\nimport { google } from '@ai-sdk/google';\nimport { openai } from '@ai-sdk/openai';\nimport { xai } from '@ai-sdk/xai';\nimport type { LanguageModel, ModelMessage } from 'ai';\nimport { generateText } from 'ai';\nimport { z } from 'zod';\n\nimport { parseArgs } from '../helpers/parseArgs.js';\nimport { printTestCaseResult } from '../helpers/printTestCaseResult.js';\nimport { readTestCases } from '../helpers/readTestCases.js';\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nconst PROMPT_FILENAME = 'prompt.txt';\n\nconst providerByName: Record<string, typeof bedrock | typeof google | typeof openai | typeof xai> = {\n // requires `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`\n bedrock,\n // requires `GOOGLE_GENERATIVE_AI_API_KEY`\n google,\n // requires `OPENAI_API_KEY`\n openai,\n // requires `XAI_API_KEY`\n xai,\n} as const;\n\nconst judgeParamsSchema = z.object({\n model: z.string().min(1),\n});\n\n/** A `test_cases/` entry; the preset uses `input` for the prompt and hands everything to `test`. */\ninterface LlmTestCase {\n id: string;\n input?: string;\n output?: string;\n /** Present when `<id>.fin/` exists; the preset does not copy it anywhere (there is no program run). */\n fileInputPath?: string;\n /** Present when `<id>.fout/` exists, for `test` to read. */\n fileOutputPath?: string;\n}\n\ninterface LlmJudgePresetOptions {\n buildPrompt?: (context: { prompt: string; testCase: LlmTestCase }) => string | ModelMessage[];\n test: (context: {\n testCase: LlmTestCase;\n result: { output: string };\n }) => Partial<TestCaseResult> | Promise<Partial<TestCaseResult>>;\n}\n\n/**\n * A preset judge function for running and testing a user prompt in LLM.\n *\n * @example\n * Create `judge.ts`:\n * ```ts\n * import { llmJudgePreset } from '@exercode/problem-utils/presets/llm';\n * import { DecisionCode } from '@exercode/problem-utils';\n *\n * await llmJudgePreset(import.meta.dirname, {\n * test: (context) {\n * return { decisionCode: context.result.output ? DecisionCode.ACCEPTED : DecisionCode.WRONG_ANSWER };\n * }\n * });\n * ```\n *\n * Run with the required parameters:\n * ```bash\n * bun judge.ts model_answers/java '{ \"model\": \"gemini-2.5-flash-lite\" }'\n * ```\n */\nexport async function llmJudgePreset(problemDir: string, options: LlmJudgePresetOptions): Promise<void> {\n const args = parseArgs(process.argv);\n if (!args.cwd) throw new Error('cwd argument required');\n const params = judgeParamsSchema.parse(args.params);\n\n const testCases = await readTestCases(path.join(problemDir, 'test_cases'));\n\n const prompt = await fs.promises.readFile(path.join(args.cwd, PROMPT_FILENAME), 'utf8');\n\n for (const testCase of testCases) {\n const startTimeMilliseconds = Date.now();\n try {\n const { text } = await generateText({\n model: toLanguageModel(params.model),\n prompt: options.buildPrompt?.({ prompt, testCase }) ?? prompt.replaceAll('{input}', testCase.input ?? ''),\n });\n\n const stopTimeMilliseconds = Date.now();\n\n const testCaseResult = {\n testCaseId: testCase.id,\n decisionCode: DecisionCode.ACCEPTED,\n stdin: testCase.input,\n stdout: text,\n timeSeconds: (stopTimeMilliseconds - startTimeMilliseconds) / 1000,\n ...(await options.test({ testCase, result: { output: text } })),\n };\n\n printTestCaseResult(testCaseResult);\n\n if (testCaseResult.decisionCode !== DecisionCode.ACCEPTED) break;\n } catch (error) {\n const stopTimeMilliseconds = Date.now();\n\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stdin: testCase.input,\n stderr: error instanceof Error ? error.message : String(error),\n timeSeconds: (stopTimeMilliseconds - startTimeMilliseconds) / 1000,\n });\n\n break;\n }\n }\n}\n\nfunction toLanguageModel(model: string): LanguageModel {\n const [providerId, modelId] = model.split('/');\n if (!providerId || !modelId) throw new Error(`bad model: ${model}`);\n const languageModel = providerByName[providerId]?.(modelId);\n if (!languageModel) throw new Error(`model not found: ${model}`);\n return languageModel;\n}\n"],"mappings":"4hBAiBA,MAEM,EAA8F,CAElG,QAAA,EAAA,QAEA,OAAA,EAAA,OAEA,OAAA,EAAA,OAEA,IAAA,EAAA,GACF,EAEM,EAAoBA,EAAAA,EAAE,OAAO,CACjC,MAAOA,EAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CACzB,CAAC,EA0CD,eAAsB,EAAe,EAAoB,EAA+C,CACtG,IAAM,EAAOC,EAAAA,UAAU,QAAQ,IAAI,EACnC,GAAI,CAAC,EAAK,IAAK,MAAU,MAAM,uBAAuB,EACtD,IAAM,EAAS,EAAkB,MAAM,EAAK,MAAM,EAE5C,EAAY,MAAMC,EAAAA,cAAcC,EAAAA,QAAK,KAAK,EAAY,YAAY,CAAC,EAEnE,EAAS,MAAMC,EAAAA,QAAG,SAAS,SAASD,EAAAA,QAAK,KAAK,EAAK,IAAK,YAAe,EAAG,MAAM,EAEtF,IAAK,IAAM,KAAY,EAAW,CAChC,IAAM,EAAwB,KAAK,IAAI,EACvC,GAAI,CACF,GAAM,CAAE,QAAS,MAAA,EAAME,EAAAA,aAAAA,CAAa,CAClC,MAAO,EAAgB,EAAO,KAAK,EACnC,OAAQ,EAAQ,cAAc,CAAE,SAAQ,UAAS,CAAC,GAAK,EAAO,WAAW,UAAW,EAAS,OAAS,EAAE,CAC1G,CAAC,EAEK,EAAuB,KAAK,IAAI,EAEhC,EAAiB,CACrB,WAAY,EAAS,GACrB,aAAcC,EAAAA,aAAa,SAC3B,MAAO,EAAS,MAChB,OAAQ,EACR,aAAc,EAAuB,GAAyB,IAC9D,GAAI,MAAM,EAAQ,KAAK,CAAE,WAAU,OAAQ,CAAE,OAAQ,CAAK,CAAE,CAAC,CAC/D,EAIA,GAFA,EAAA,oBAAoB,CAAc,EAE9B,EAAe,eAAiBA,EAAAA,aAAa,SAAU,KAC7D,OAAS,EAAO,CACd,IAAM,EAAuB,KAAK,IAAI,EAEtC,EAAA,oBAAoB,CAClB,WAAY,EAAS,GACrB,aAAcA,EAAAA,aAAa,cAC3B,MAAO,EAAS,MAChB,OAAQ,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EAC7D,aAAc,EAAuB,GAAyB,GAChE,CAAC,EAED,KACF,CACF,CACF,CAEA,SAAS,EAAgB,EAA8B,CACrD,GAAM,CAAC,EAAY,GAAW,EAAM,MAAM,GAAG,EAC7C,GAAI,CAAC,GAAc,CAAC,EAAS,MAAU,MAAM,cAAc,GAAO,EAClE,IAAM,EAAgB,EAAe,EAAW,GAAG,CAAO,EAC1D,GAAI,CAAC,EAAe,MAAU,MAAM,oBAAoB,GAAO,EAC/D,OAAO,CACT"}
1
+ {"version":3,"file":"llm.cjs","names":["z","parseArgs","readTestCases","path","fs","generateText","DecisionCode"],"sources":["../../src/presets/llm.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { bedrock } from '@ai-sdk/amazon-bedrock';\nimport { google } from '@ai-sdk/google';\nimport { openai } from '@ai-sdk/openai';\nimport { xai } from '@ai-sdk/xai';\nimport type { LanguageModel, ModelMessage } from 'ai';\nimport { generateText } from 'ai';\nimport { z } from 'zod';\n\nimport { parseArgs } from '../helpers/parseArgs.js';\nimport { printTestCaseResult } from '../helpers/printTestCaseResult.js';\nimport { readTestCases } from '../helpers/readTestCases.js';\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nconst PROMPT_FILENAME = 'prompt.txt';\n\nconst providerByName: Record<string, typeof bedrock | typeof google | typeof openai | typeof xai> = {\n // requires `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`\n bedrock,\n // requires `GOOGLE_GENERATIVE_AI_API_KEY`\n google,\n // requires `OPENAI_API_KEY`\n openai,\n // requires `XAI_API_KEY`\n xai,\n} as const;\n\nconst judgeParamsSchema = z.object({\n model: z.string().min(1),\n});\n\n/** A `test_cases/` entry; the preset uses `input` for the prompt and hands everything to `test`. */\ninterface LlmTestCase {\n id: string;\n input?: string;\n output?: string;\n /** Present when `<id>.fin/` exists; the preset does not copy it anywhere (there is no program run). */\n fileInputPath?: string;\n /** Present when `<id>.fout/` exists, for `test` to read. */\n fileOutputPath?: string;\n}\n\ninterface LlmJudgePresetOptions {\n buildPrompt?: (context: { prompt: string; testCase: LlmTestCase }) => string | ModelMessage[];\n test: (context: {\n testCase: LlmTestCase;\n result: { output: string };\n }) => Partial<TestCaseResult> | Promise<Partial<TestCaseResult>>;\n}\n\n/**\n * A preset judge function for running and testing a user prompt in LLM.\n *\n * @example\n * Create `judge.ts`:\n * ```ts\n * import { llmJudgePreset } from '@exercode/problem-utils/presets/llm';\n * import { DecisionCode } from '@exercode/problem-utils';\n *\n * await llmJudgePreset(import.meta.dirname, {\n * test: (context) {\n * return { decisionCode: context.result.output ? DecisionCode.ACCEPTED : DecisionCode.WRONG_ANSWER };\n * }\n * });\n * ```\n *\n * Run with the required parameters:\n * ```bash\n * bun judge.ts model_answers/java '{ \"model\": \"gemini-2.5-flash-lite\" }'\n * ```\n */\nexport async function llmJudgePreset(problemDir: string, options: LlmJudgePresetOptions): Promise<void> {\n const args = parseArgs(process.argv);\n if (!args.cwd) throw new Error('cwd argument required');\n const params = judgeParamsSchema.parse(args.params);\n\n const testCases = await readTestCases(path.join(problemDir, 'test_cases'));\n\n const prompt = await fs.promises.readFile(path.join(args.cwd, PROMPT_FILENAME), 'utf8');\n\n for (const testCase of testCases) {\n const startTimeMilliseconds = Date.now();\n try {\n const { text } = await generateText({\n model: toLanguageModel(params.model),\n ...toPromptOptions(\n options.buildPrompt?.({ prompt, testCase }) ?? prompt.replaceAll('{input}', testCase.input ?? '')\n ),\n });\n\n const stopTimeMilliseconds = Date.now();\n\n const testCaseResult = {\n testCaseId: testCase.id,\n decisionCode: DecisionCode.ACCEPTED,\n stdin: testCase.input,\n stdout: text,\n timeSeconds: (stopTimeMilliseconds - startTimeMilliseconds) / 1000,\n ...(await options.test({ testCase, result: { output: text } })),\n };\n\n printTestCaseResult(testCaseResult);\n\n if (testCaseResult.decisionCode !== DecisionCode.ACCEPTED) break;\n } catch (error) {\n const stopTimeMilliseconds = Date.now();\n\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stdin: testCase.input,\n stderr: error instanceof Error ? error.message : String(error),\n timeSeconds: (stopTimeMilliseconds - startTimeMilliseconds) / 1000,\n });\n\n break;\n }\n }\n}\n\n/** `ai` rejects system messages inside `messages` unless told that a `buildPrompt` may place them there. */\nfunction toPromptOptions(\n built: string | ModelMessage[]\n): { prompt: string } | { messages: ModelMessage[]; allowSystemInMessages: true } {\n return typeof built === 'string' ? { prompt: built } : { messages: built, allowSystemInMessages: true };\n}\n\nfunction toLanguageModel(model: string): LanguageModel {\n const [providerId, modelId] = model.split('/');\n if (!providerId || !modelId) throw new Error(`bad model: ${model}`);\n const languageModel = providerByName[providerId]?.(modelId);\n if (!languageModel) throw new Error(`model not found: ${model}`);\n return languageModel;\n}\n"],"mappings":"4hBAiBA,MAEM,EAA8F,CAElG,QAAA,EAAA,QAEA,OAAA,EAAA,OAEA,OAAA,EAAA,OAEA,IAAA,EAAA,GACF,EAEM,EAAoBA,EAAAA,EAAE,OAAO,CACjC,MAAOA,EAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CACzB,CAAC,EA0CD,eAAsB,EAAe,EAAoB,EAA+C,CACtG,IAAM,EAAOC,EAAAA,UAAU,QAAQ,IAAI,EACnC,GAAI,CAAC,EAAK,IAAK,MAAU,MAAM,uBAAuB,EACtD,IAAM,EAAS,EAAkB,MAAM,EAAK,MAAM,EAE5C,EAAY,MAAMC,EAAAA,cAAcC,EAAAA,QAAK,KAAK,EAAY,YAAY,CAAC,EAEnE,EAAS,MAAMC,EAAAA,QAAG,SAAS,SAASD,EAAAA,QAAK,KAAK,EAAK,IAAK,YAAe,EAAG,MAAM,EAEtF,IAAK,IAAM,KAAY,EAAW,CAChC,IAAM,EAAwB,KAAK,IAAI,EACvC,GAAI,CACF,GAAM,CAAE,QAAS,MAAA,EAAME,EAAAA,aAAAA,CAAa,CAClC,MAAO,EAAgB,EAAO,KAAK,EACnC,GAAG,EACD,EAAQ,cAAc,CAAE,SAAQ,UAAS,CAAC,GAAK,EAAO,WAAW,UAAW,EAAS,OAAS,EAAE,CAClG,CACF,CAAC,EAEK,EAAuB,KAAK,IAAI,EAEhC,EAAiB,CACrB,WAAY,EAAS,GACrB,aAAcC,EAAAA,aAAa,SAC3B,MAAO,EAAS,MAChB,OAAQ,EACR,aAAc,EAAuB,GAAyB,IAC9D,GAAI,MAAM,EAAQ,KAAK,CAAE,WAAU,OAAQ,CAAE,OAAQ,CAAK,CAAE,CAAC,CAC/D,EAIA,GAFA,EAAA,oBAAoB,CAAc,EAE9B,EAAe,eAAiBA,EAAAA,aAAa,SAAU,KAC7D,OAAS,EAAO,CACd,IAAM,EAAuB,KAAK,IAAI,EAEtC,EAAA,oBAAoB,CAClB,WAAY,EAAS,GACrB,aAAcA,EAAAA,aAAa,cAC3B,MAAO,EAAS,MAChB,OAAQ,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EAC7D,aAAc,EAAuB,GAAyB,GAChE,CAAC,EAED,KACF,CACF,CACF,CAGA,SAAS,EACP,EACgF,CAChF,OAAO,OAAO,GAAU,SAAW,CAAE,OAAQ,CAAM,EAAI,CAAE,SAAU,EAAO,sBAAuB,EAAK,CACxG,CAEA,SAAS,EAAgB,EAA8B,CACrD,GAAM,CAAC,EAAY,GAAW,EAAM,MAAM,GAAG,EAC7C,GAAI,CAAC,GAAc,CAAC,EAAS,MAAU,MAAM,cAAc,GAAO,EAClE,IAAM,EAAgB,EAAe,EAAW,GAAG,CAAO,EAC1D,GAAI,CAAC,EAAe,MAAU,MAAM,oBAAoB,GAAO,EAC/D,OAAO,CACT"}
@@ -1,2 +1,2 @@
1
- import{printTestCaseResult as e}from"../helpers/printTestCaseResult.js";import{parseArgs as t}from"../helpers/parseArgs.js";import{readTestCases as n}from"../helpers/readTestCases.js";import{DecisionCode as r}from"../types/decisionCode.js";import i from"node:fs";import a from"node:path";import{z as o}from"zod";import{bedrock as s}from"@ai-sdk/amazon-bedrock";import{google as c}from"@ai-sdk/google";import{openai as l}from"@ai-sdk/openai";import{xai as u}from"@ai-sdk/xai";import{generateText as d}from"ai";const f={bedrock:s,google:c,openai:l,xai:u},p=o.object({model:o.string().min(1)});async function m(o,s){let c=t(process.argv);if(!c.cwd)throw Error(`cwd argument required`);let l=p.parse(c.params),u=await n(a.join(o,`test_cases`)),f=await i.promises.readFile(a.join(c.cwd,`prompt.txt`),`utf8`);for(let t of u){let n=Date.now();try{let{text:i}=await d({model:h(l.model),prompt:s.buildPrompt?.({prompt:f,testCase:t})??f.replaceAll(`{input}`,t.input??``)}),a=Date.now(),o={testCaseId:t.id,decisionCode:r.ACCEPTED,stdin:t.input,stdout:i,timeSeconds:(a-n)/1e3,...await s.test({testCase:t,result:{output:i}})};if(e(o),o.decisionCode!==r.ACCEPTED)break}catch(i){let a=Date.now();e({testCaseId:t.id,decisionCode:r.RUNTIME_ERROR,stdin:t.input,stderr:i instanceof Error?i.message:String(i),timeSeconds:(a-n)/1e3});break}}}function h(e){let[t,n]=e.split(`/`);if(!t||!n)throw Error(`bad model: ${e}`);let r=f[t]?.(n);if(!r)throw Error(`model not found: ${e}`);return r}export{m as llmJudgePreset};
1
+ import{printTestCaseResult as e}from"../helpers/printTestCaseResult.js";import{parseArgs as t}from"../helpers/parseArgs.js";import{readTestCases as n}from"../helpers/readTestCases.js";import{DecisionCode as r}from"../types/decisionCode.js";import i from"node:fs";import a from"node:path";import{z as o}from"zod";import{bedrock as s}from"@ai-sdk/amazon-bedrock";import{google as c}from"@ai-sdk/google";import{openai as l}from"@ai-sdk/openai";import{xai as u}from"@ai-sdk/xai";import{generateText as d}from"ai";const f={bedrock:s,google:c,openai:l,xai:u},p=o.object({model:o.string().min(1)});async function m(o,s){let c=t(process.argv);if(!c.cwd)throw Error(`cwd argument required`);let l=p.parse(c.params),u=await n(a.join(o,`test_cases`)),f=await i.promises.readFile(a.join(c.cwd,`prompt.txt`),`utf8`);for(let t of u){let n=Date.now();try{let{text:i}=await d({model:g(l.model),...h(s.buildPrompt?.({prompt:f,testCase:t})??f.replaceAll(`{input}`,t.input??``))}),a=Date.now(),o={testCaseId:t.id,decisionCode:r.ACCEPTED,stdin:t.input,stdout:i,timeSeconds:(a-n)/1e3,...await s.test({testCase:t,result:{output:i}})};if(e(o),o.decisionCode!==r.ACCEPTED)break}catch(i){let a=Date.now();e({testCaseId:t.id,decisionCode:r.RUNTIME_ERROR,stdin:t.input,stderr:i instanceof Error?i.message:String(i),timeSeconds:(a-n)/1e3});break}}}function h(e){return typeof e==`string`?{prompt:e}:{messages:e,allowSystemInMessages:!0}}function g(e){let[t,n]=e.split(`/`);if(!t||!n)throw Error(`bad model: ${e}`);let r=f[t]?.(n);if(!r)throw Error(`model not found: ${e}`);return r}export{m as llmJudgePreset};
2
2
  //# sourceMappingURL=llm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"llm.js","names":[],"sources":["../../src/presets/llm.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { bedrock } from '@ai-sdk/amazon-bedrock';\nimport { google } from '@ai-sdk/google';\nimport { openai } from '@ai-sdk/openai';\nimport { xai } from '@ai-sdk/xai';\nimport type { LanguageModel, ModelMessage } from 'ai';\nimport { generateText } from 'ai';\nimport { z } from 'zod';\n\nimport { parseArgs } from '../helpers/parseArgs.js';\nimport { printTestCaseResult } from '../helpers/printTestCaseResult.js';\nimport { readTestCases } from '../helpers/readTestCases.js';\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nconst PROMPT_FILENAME = 'prompt.txt';\n\nconst providerByName: Record<string, typeof bedrock | typeof google | typeof openai | typeof xai> = {\n // requires `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`\n bedrock,\n // requires `GOOGLE_GENERATIVE_AI_API_KEY`\n google,\n // requires `OPENAI_API_KEY`\n openai,\n // requires `XAI_API_KEY`\n xai,\n} as const;\n\nconst judgeParamsSchema = z.object({\n model: z.string().min(1),\n});\n\n/** A `test_cases/` entry; the preset uses `input` for the prompt and hands everything to `test`. */\ninterface LlmTestCase {\n id: string;\n input?: string;\n output?: string;\n /** Present when `<id>.fin/` exists; the preset does not copy it anywhere (there is no program run). */\n fileInputPath?: string;\n /** Present when `<id>.fout/` exists, for `test` to read. */\n fileOutputPath?: string;\n}\n\ninterface LlmJudgePresetOptions {\n buildPrompt?: (context: { prompt: string; testCase: LlmTestCase }) => string | ModelMessage[];\n test: (context: {\n testCase: LlmTestCase;\n result: { output: string };\n }) => Partial<TestCaseResult> | Promise<Partial<TestCaseResult>>;\n}\n\n/**\n * A preset judge function for running and testing a user prompt in LLM.\n *\n * @example\n * Create `judge.ts`:\n * ```ts\n * import { llmJudgePreset } from '@exercode/problem-utils/presets/llm';\n * import { DecisionCode } from '@exercode/problem-utils';\n *\n * await llmJudgePreset(import.meta.dirname, {\n * test: (context) {\n * return { decisionCode: context.result.output ? DecisionCode.ACCEPTED : DecisionCode.WRONG_ANSWER };\n * }\n * });\n * ```\n *\n * Run with the required parameters:\n * ```bash\n * bun judge.ts model_answers/java '{ \"model\": \"gemini-2.5-flash-lite\" }'\n * ```\n */\nexport async function llmJudgePreset(problemDir: string, options: LlmJudgePresetOptions): Promise<void> {\n const args = parseArgs(process.argv);\n if (!args.cwd) throw new Error('cwd argument required');\n const params = judgeParamsSchema.parse(args.params);\n\n const testCases = await readTestCases(path.join(problemDir, 'test_cases'));\n\n const prompt = await fs.promises.readFile(path.join(args.cwd, PROMPT_FILENAME), 'utf8');\n\n for (const testCase of testCases) {\n const startTimeMilliseconds = Date.now();\n try {\n const { text } = await generateText({\n model: toLanguageModel(params.model),\n prompt: options.buildPrompt?.({ prompt, testCase }) ?? prompt.replaceAll('{input}', testCase.input ?? ''),\n });\n\n const stopTimeMilliseconds = Date.now();\n\n const testCaseResult = {\n testCaseId: testCase.id,\n decisionCode: DecisionCode.ACCEPTED,\n stdin: testCase.input,\n stdout: text,\n timeSeconds: (stopTimeMilliseconds - startTimeMilliseconds) / 1000,\n ...(await options.test({ testCase, result: { output: text } })),\n };\n\n printTestCaseResult(testCaseResult);\n\n if (testCaseResult.decisionCode !== DecisionCode.ACCEPTED) break;\n } catch (error) {\n const stopTimeMilliseconds = Date.now();\n\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stdin: testCase.input,\n stderr: error instanceof Error ? error.message : String(error),\n timeSeconds: (stopTimeMilliseconds - startTimeMilliseconds) / 1000,\n });\n\n break;\n }\n }\n}\n\nfunction toLanguageModel(model: string): LanguageModel {\n const [providerId, modelId] = model.split('/');\n if (!providerId || !modelId) throw new Error(`bad model: ${model}`);\n const languageModel = providerByName[providerId]?.(modelId);\n if (!languageModel) throw new Error(`model not found: ${model}`);\n return languageModel;\n}\n"],"mappings":"6fAiBA,MAEM,EAA8F,CAElG,UAEA,SAEA,SAEA,KACF,EAEM,EAAoB,EAAE,OAAO,CACjC,MAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CACzB,CAAC,EA0CD,eAAsB,EAAe,EAAoB,EAA+C,CACtG,IAAM,EAAO,EAAU,QAAQ,IAAI,EACnC,GAAI,CAAC,EAAK,IAAK,MAAU,MAAM,uBAAuB,EACtD,IAAM,EAAS,EAAkB,MAAM,EAAK,MAAM,EAE5C,EAAY,MAAM,EAAc,EAAK,KAAK,EAAY,YAAY,CAAC,EAEnE,EAAS,MAAM,EAAG,SAAS,SAAS,EAAK,KAAK,EAAK,IAAK,YAAe,EAAG,MAAM,EAEtF,IAAK,IAAM,KAAY,EAAW,CAChC,IAAM,EAAwB,KAAK,IAAI,EACvC,GAAI,CACF,GAAM,CAAE,QAAS,MAAM,EAAa,CAClC,MAAO,EAAgB,EAAO,KAAK,EACnC,OAAQ,EAAQ,cAAc,CAAE,SAAQ,UAAS,CAAC,GAAK,EAAO,WAAW,UAAW,EAAS,OAAS,EAAE,CAC1G,CAAC,EAEK,EAAuB,KAAK,IAAI,EAEhC,EAAiB,CACrB,WAAY,EAAS,GACrB,aAAc,EAAa,SAC3B,MAAO,EAAS,MAChB,OAAQ,EACR,aAAc,EAAuB,GAAyB,IAC9D,GAAI,MAAM,EAAQ,KAAK,CAAE,WAAU,OAAQ,CAAE,OAAQ,CAAK,CAAE,CAAC,CAC/D,EAIA,GAFA,EAAoB,CAAc,EAE9B,EAAe,eAAiB,EAAa,SAAU,KAC7D,OAAS,EAAO,CACd,IAAM,EAAuB,KAAK,IAAI,EAEtC,EAAoB,CAClB,WAAY,EAAS,GACrB,aAAc,EAAa,cAC3B,MAAO,EAAS,MAChB,OAAQ,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EAC7D,aAAc,EAAuB,GAAyB,GAChE,CAAC,EAED,KACF,CACF,CACF,CAEA,SAAS,EAAgB,EAA8B,CACrD,GAAM,CAAC,EAAY,GAAW,EAAM,MAAM,GAAG,EAC7C,GAAI,CAAC,GAAc,CAAC,EAAS,MAAU,MAAM,cAAc,GAAO,EAClE,IAAM,EAAgB,EAAe,EAAW,GAAG,CAAO,EAC1D,GAAI,CAAC,EAAe,MAAU,MAAM,oBAAoB,GAAO,EAC/D,OAAO,CACT"}
1
+ {"version":3,"file":"llm.js","names":[],"sources":["../../src/presets/llm.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { bedrock } from '@ai-sdk/amazon-bedrock';\nimport { google } from '@ai-sdk/google';\nimport { openai } from '@ai-sdk/openai';\nimport { xai } from '@ai-sdk/xai';\nimport type { LanguageModel, ModelMessage } from 'ai';\nimport { generateText } from 'ai';\nimport { z } from 'zod';\n\nimport { parseArgs } from '../helpers/parseArgs.js';\nimport { printTestCaseResult } from '../helpers/printTestCaseResult.js';\nimport { readTestCases } from '../helpers/readTestCases.js';\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nconst PROMPT_FILENAME = 'prompt.txt';\n\nconst providerByName: Record<string, typeof bedrock | typeof google | typeof openai | typeof xai> = {\n // requires `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`\n bedrock,\n // requires `GOOGLE_GENERATIVE_AI_API_KEY`\n google,\n // requires `OPENAI_API_KEY`\n openai,\n // requires `XAI_API_KEY`\n xai,\n} as const;\n\nconst judgeParamsSchema = z.object({\n model: z.string().min(1),\n});\n\n/** A `test_cases/` entry; the preset uses `input` for the prompt and hands everything to `test`. */\ninterface LlmTestCase {\n id: string;\n input?: string;\n output?: string;\n /** Present when `<id>.fin/` exists; the preset does not copy it anywhere (there is no program run). */\n fileInputPath?: string;\n /** Present when `<id>.fout/` exists, for `test` to read. */\n fileOutputPath?: string;\n}\n\ninterface LlmJudgePresetOptions {\n buildPrompt?: (context: { prompt: string; testCase: LlmTestCase }) => string | ModelMessage[];\n test: (context: {\n testCase: LlmTestCase;\n result: { output: string };\n }) => Partial<TestCaseResult> | Promise<Partial<TestCaseResult>>;\n}\n\n/**\n * A preset judge function for running and testing a user prompt in LLM.\n *\n * @example\n * Create `judge.ts`:\n * ```ts\n * import { llmJudgePreset } from '@exercode/problem-utils/presets/llm';\n * import { DecisionCode } from '@exercode/problem-utils';\n *\n * await llmJudgePreset(import.meta.dirname, {\n * test: (context) {\n * return { decisionCode: context.result.output ? DecisionCode.ACCEPTED : DecisionCode.WRONG_ANSWER };\n * }\n * });\n * ```\n *\n * Run with the required parameters:\n * ```bash\n * bun judge.ts model_answers/java '{ \"model\": \"gemini-2.5-flash-lite\" }'\n * ```\n */\nexport async function llmJudgePreset(problemDir: string, options: LlmJudgePresetOptions): Promise<void> {\n const args = parseArgs(process.argv);\n if (!args.cwd) throw new Error('cwd argument required');\n const params = judgeParamsSchema.parse(args.params);\n\n const testCases = await readTestCases(path.join(problemDir, 'test_cases'));\n\n const prompt = await fs.promises.readFile(path.join(args.cwd, PROMPT_FILENAME), 'utf8');\n\n for (const testCase of testCases) {\n const startTimeMilliseconds = Date.now();\n try {\n const { text } = await generateText({\n model: toLanguageModel(params.model),\n ...toPromptOptions(\n options.buildPrompt?.({ prompt, testCase }) ?? prompt.replaceAll('{input}', testCase.input ?? '')\n ),\n });\n\n const stopTimeMilliseconds = Date.now();\n\n const testCaseResult = {\n testCaseId: testCase.id,\n decisionCode: DecisionCode.ACCEPTED,\n stdin: testCase.input,\n stdout: text,\n timeSeconds: (stopTimeMilliseconds - startTimeMilliseconds) / 1000,\n ...(await options.test({ testCase, result: { output: text } })),\n };\n\n printTestCaseResult(testCaseResult);\n\n if (testCaseResult.decisionCode !== DecisionCode.ACCEPTED) break;\n } catch (error) {\n const stopTimeMilliseconds = Date.now();\n\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stdin: testCase.input,\n stderr: error instanceof Error ? error.message : String(error),\n timeSeconds: (stopTimeMilliseconds - startTimeMilliseconds) / 1000,\n });\n\n break;\n }\n }\n}\n\n/** `ai` rejects system messages inside `messages` unless told that a `buildPrompt` may place them there. */\nfunction toPromptOptions(\n built: string | ModelMessage[]\n): { prompt: string } | { messages: ModelMessage[]; allowSystemInMessages: true } {\n return typeof built === 'string' ? { prompt: built } : { messages: built, allowSystemInMessages: true };\n}\n\nfunction toLanguageModel(model: string): LanguageModel {\n const [providerId, modelId] = model.split('/');\n if (!providerId || !modelId) throw new Error(`bad model: ${model}`);\n const languageModel = providerByName[providerId]?.(modelId);\n if (!languageModel) throw new Error(`model not found: ${model}`);\n return languageModel;\n}\n"],"mappings":"6fAiBA,MAEM,EAA8F,CAElG,UAEA,SAEA,SAEA,KACF,EAEM,EAAoB,EAAE,OAAO,CACjC,MAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CACzB,CAAC,EA0CD,eAAsB,EAAe,EAAoB,EAA+C,CACtG,IAAM,EAAO,EAAU,QAAQ,IAAI,EACnC,GAAI,CAAC,EAAK,IAAK,MAAU,MAAM,uBAAuB,EACtD,IAAM,EAAS,EAAkB,MAAM,EAAK,MAAM,EAE5C,EAAY,MAAM,EAAc,EAAK,KAAK,EAAY,YAAY,CAAC,EAEnE,EAAS,MAAM,EAAG,SAAS,SAAS,EAAK,KAAK,EAAK,IAAK,YAAe,EAAG,MAAM,EAEtF,IAAK,IAAM,KAAY,EAAW,CAChC,IAAM,EAAwB,KAAK,IAAI,EACvC,GAAI,CACF,GAAM,CAAE,QAAS,MAAM,EAAa,CAClC,MAAO,EAAgB,EAAO,KAAK,EACnC,GAAG,EACD,EAAQ,cAAc,CAAE,SAAQ,UAAS,CAAC,GAAK,EAAO,WAAW,UAAW,EAAS,OAAS,EAAE,CAClG,CACF,CAAC,EAEK,EAAuB,KAAK,IAAI,EAEhC,EAAiB,CACrB,WAAY,EAAS,GACrB,aAAc,EAAa,SAC3B,MAAO,EAAS,MAChB,OAAQ,EACR,aAAc,EAAuB,GAAyB,IAC9D,GAAI,MAAM,EAAQ,KAAK,CAAE,WAAU,OAAQ,CAAE,OAAQ,CAAK,CAAE,CAAC,CAC/D,EAIA,GAFA,EAAoB,CAAc,EAE9B,EAAe,eAAiB,EAAa,SAAU,KAC7D,OAAS,EAAO,CACd,IAAM,EAAuB,KAAK,IAAI,EAEtC,EAAoB,CAClB,WAAY,EAAS,GACrB,aAAc,EAAa,cAC3B,MAAO,EAAS,MAChB,OAAQ,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EAC7D,aAAc,EAAuB,GAAyB,GAChE,CAAC,EAED,KACF,CACF,CACF,CAGA,SAAS,EACP,EACgF,CAChF,OAAO,OAAO,GAAU,SAAW,CAAE,OAAQ,CAAM,EAAI,CAAE,SAAU,EAAO,sBAAuB,EAAK,CACxG,CAEA,SAAS,EAAgB,EAA8B,CACrD,GAAM,CAAC,EAAY,GAAW,EAAM,MAAM,GAAG,EAC7C,GAAI,CAAC,GAAc,CAAC,EAAS,MAAU,MAAM,cAAc,GAAO,EAClE,IAAM,EAAgB,EAAe,EAAW,GAAG,CAAO,EAC1D,GAAI,CAAC,EAAe,MAAU,MAAM,oBAAoB,GAAO,EAC/D,OAAO,CACT"}
@@ -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/temporaryProblemDirCopy.cjs"),o=require("../helpers/findLanguageDefinitionByPath.cjs"),s=require("../helpers/readProblemMarkdownFrontMatter.cjs"),c=require("../helpers/stdioJudgeRules.cjs"),l=require("../types/decisionCode.cjs"),u=require("../helpers/cleanWorkingDirectory.cjs"),d=require("../helpers/copyTestCaseFileInput.cjs"),f=require("../helpers/findEntryPointFile.cjs"),p=require("../helpers/judgeByStaticAnalysis.cjs"),m=require("../helpers/readOutputFiles.cjs"),h=require("../helpers/spawnWithTimeout.cjs");let g=require("node:fs");g=e.__toESM(g,1);let _=require("node:path");_=e.__toESM(_,1);let v=require("zod");const y=v.z.object({language:v.z.union([v.z.string(),v.z.array(v.z.string())]).optional()}),b=y.extend({stdin:v.z.string().optional()});async function x(e){let a=r.parseArgs(process.argv);if(!a.cwd)throw Error(`cwd argument required`);let c=y.parse(a.params),g=await s.readProblemMarkdownFrontMatter(e),v=await i.readTestCases(_.default.join(e,`test_cases`));if(!s.judgesTestCasesWithoutExpectations(g)){for(let e of v)if(e.output===void 0&&!await S(e.fileOutputPath))throw Error(`test case ${e.id} needs an expected output (${e.id}.out or a non-empty ${e.id}.fout/)`)}let b=await p.judgeByStaticAnalysis(a.cwd,g);if(b){t.printTestCaseResult({testCaseId:v[0]?.id??`prebuild`,...b});return}let x=await f.findEntryPointFile(a.cwd,c.language);if(!x){t.printTestCaseResult({testCaseId:v[0]?.id??`prebuild`,decisionCode:l.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${c.language?`: language: ${c.language}`:``}`});return}let C=o.findLanguageDefinitionByPath(x);if(!C){t.printTestCaseResult({testCaseId:v[0]?.id??`prebuild`,decisionCode:l.DecisionCode.WRONG_ANSWER,stderr:`unsupported language`});return}let w={...process.env,CI:``,FORCE_COLOR:`0`},T;if(C.prebuild)try{await C.prebuild(a.cwd),T=await f.findEntryPointFile(a.cwd,c.language)}catch(e){console.error(`prebuild error`,e),t.printTestCaseResult({testCaseId:v[0]?.id??`prebuild`,decisionCode:l.DecisionCode.BUILD_ERROR,stderr:e instanceof Error?e.message:String(e)});return}let E=T??x;if(C.buildCommand){let e=C.buildCommand(E),n=await h.spawnWithTimeout(e[0],e.slice(1),{cwd:a.cwd,env:w},10),r=l.DecisionCode.ACCEPTED;if(n.status===0?n.timeSeconds>10?r=l.DecisionCode.BUILD_TIME_LIMIT_EXCEEDED:(n.stdout.length>5e4||n.stderr.length>5e4)&&(r=l.DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED):r=l.DecisionCode.BUILD_ERROR,r!==l.DecisionCode.ACCEPTED){t.printTestCaseResult({testCaseId:v[0]?.id??`build`,decisionCode:r,exitStatus:n.status??void 0,stdout:n.stdout.slice(0,5e4)||void 0,stderr:n.stderr.slice(0,5e4)||void 0,timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes});return}}let D=await u.snapshotWorkingDirectory(a.cwd);v.length===0&&t.printTestCaseResult({testCaseId:`default`,decisionCode:l.DecisionCode.ACCEPTED});for(let e of v){v.shared?.fileInputPath&&await d.copyTestCaseFileInput(v.shared.fileInputPath,a.cwd),e.fileInputPath&&await d.copyTestCaseFileInput(e.fileInputPath,a.cwd);let r=typeof g.timeLimitMs==`number`?g.timeLimitMs/1e3:2,i=C.command(E),o=await h.spawnWithTimeout(i[0],i.slice(1),{cwd:a.cwd,stdin:e.input,env:w},r),s=await m.readOutputFiles(a.cwd,g.requiredOutputFilePaths??[]),c=l.DecisionCode.ACCEPTED,f;if(o.status!==0)c=l.DecisionCode.RUNTIME_ERROR;else if(o.timeSeconds>r)c=l.DecisionCode.TIME_LIMIT_EXCEEDED;else if(o.memoryBytes>(g.memoryLimitByte??1/0))c=l.DecisionCode.MEMORY_LIMIT_EXCEEDED;else if(o.stdout.length>5e4||o.stderr.length>5e4)c=l.DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED;else if(s.length<(g.requiredOutputFilePaths?.length??0))c=l.DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR;else try{let t=await n.judgeAgainstExpectations({stdout:o.stdout,expectedStdout:e.output,fileOutputPath:e.fileOutputPath,cwd:a.cwd,outputFiles:s});t.matches||(c=l.DecisionCode.WRONG_ANSWER),s=t.outputFiles}catch(e){c=l.DecisionCode.RUNTIME_ERROR,f=e instanceof Error?e.message:String(e)}if(t.printTestCaseResult({testCaseId:e.id,decisionCode:c,exitStatus:o.status??void 0,stdin:e.input,stdout:o.stdout.slice(0,5e4)||void 0,stderr:(f??o.stderr).slice(0,5e4)||void 0,timeSeconds:o.timeSeconds,memoryBytes:o.memoryBytes,outputFiles:s.length>0?s:void 0}),await u.cleanWorkingDirectory(a.cwd,D),c!==l.DecisionCode.ACCEPTED)break}}async function S(e){return e!==void 0&&(await g.default.promises.readdir(e,{withFileTypes:!0,recursive:!0})).some(e=>e.isFile())}async function C(e){let t=r.parseArgs(process.argv);if(!t.cwd)throw Error(`cwd argument required`);let n=b.parse(t.params),i,o=()=>{i!==void 0&&a.forciblyRemoveDirectorySync(i),process.exit(1)};process.once(`SIGINT`,o),process.once(`SIGTERM`,o);try{await w(e,(await a.copyProblemDirToTemporaryRoot(t.cwd,{onTempRootCreated:e=>i=e})).copiedProblemDir,n)}finally{i!==void 0&&await a.forciblyRemoveDirectory(i),process.off(`SIGINT`,o),process.off(`SIGTERM`,o)}}async function w(e,n,r){let a=await s.readProblemMarkdownFrontMatter(e),u=await f.findEntryPointFile(n,r.language);if(!u){t.printTestCaseResult({testCaseId:`prebuild`,decisionCode:l.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${r.language?`: language: ${r.language}`:``}`});return}let p=o.findLanguageDefinitionByPath(u);if(!p){t.printTestCaseResult({testCaseId:`prebuild`,decisionCode:l.DecisionCode.WRONG_ANSWER,stderr:`unsupported language`});return}let g={...process.env,CI:``,FORCE_COLOR:`0`},v;if(p.prebuild)try{await p.prebuild(n),v=await f.findEntryPointFile(n,r.language)}catch(e){console.error(`prebuild error`,e),t.printTestCaseResult({testCaseId:`prebuild`,decisionCode:l.DecisionCode.BUILD_ERROR,stderr:e instanceof Error?e.message:String(e)});return}let y=v??u;if(p.buildCommand){let e=p.buildCommand(y),r=await h.spawnWithTimeout(e[0],e.slice(1),{cwd:n,env:g},10),i=l.DecisionCode.ACCEPTED;if(r.status===0?r.timeSeconds>10?i=l.DecisionCode.BUILD_TIME_LIMIT_EXCEEDED:(r.stdout.length>5e4||r.stderr.length>5e4)&&(i=l.DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED):i=l.DecisionCode.BUILD_ERROR,i!==l.DecisionCode.ACCEPTED){t.printTestCaseResult({testCaseId:`build`,decisionCode:i,exitStatus:r.status??void 0,stdout:r.stdout.slice(0,5e4)||void 0,stderr:r.stderr.slice(0,5e4)||void 0,timeSeconds:r.timeSeconds,memoryBytes:r.memoryBytes});return}}let b=await i.readTestCases(_.default.join(e,`test_cases`));b.shared?.fileInputPath&&await d.copyTestCaseFileInput(b.shared.fileInputPath,n);let x=b.find(e=>c.EXAMPLE_TEST_CASE_ID_PATTERN.test(e.id)&&e.fileInputPath)?.fileInputPath;x?await d.copyTestCaseFileInput(x,n):b.some(e=>e.fileInputPath)&&console.error(`debug: no example test case has input files, so the program runs without them`);let S=Math.max(10,(a.timeLimitMs??0)/1e3),C=p.command(y),w=await h.spawnWithTimeout(C[0],C.slice(1),{cwd:n,stdin:r.stdin,env:g},S),T=await m.readOutputFiles(n,a.requiredOutputFilePaths??[]),E=l.DecisionCode.ACCEPTED;w.status===0?w.timeSeconds>S?E=l.DecisionCode.TIME_LIMIT_EXCEEDED:w.memoryBytes>(a.memoryLimitByte??1/0)?E=l.DecisionCode.MEMORY_LIMIT_EXCEEDED:w.stdout.length>5e4||w.stderr.length>5e4?E=l.DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED:T.length<(a.requiredOutputFilePaths?.length??0)&&(E=l.DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR):E=l.DecisionCode.RUNTIME_ERROR,t.printTestCaseResult({testCaseId:`debug`,decisionCode:E,exitStatus:w.status??void 0,stdin:r.stdin,stdout:w.stdout.slice(0,5e4)||void 0,stderr:w.stderr.slice(0,5e4)||void 0,timeSeconds:w.timeSeconds,memoryBytes:w.memoryBytes,outputFiles:T.length>0?T:void 0})}exports.stdioDebugPreset=C,exports.stdioJudgePreset=x;
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/temporaryProblemDirCopy.cjs"),o=require("../helpers/findLanguageDefinitionByPath.cjs"),s=require("../helpers/readProblemMarkdownFrontMatter.cjs"),c=require("../helpers/stdioJudgeRules.cjs"),l=require("../types/decisionCode.cjs"),u=require("../helpers/cleanWorkingDirectory.cjs"),d=require("../helpers/copyTestCaseFileInput.cjs"),f=require("../helpers/findEntryPointFile.cjs"),p=require("../helpers/judgeByStaticAnalysis.cjs"),m=require("../helpers/readOutputFiles.cjs"),h=require("../helpers/spawnWithTimeout.cjs");let g=require("node:fs");g=e.__toESM(g,1);let _=require("node:path");_=e.__toESM(_,1);let v=require("zod");const y=v.z.object({language:v.z.union([v.z.string(),v.z.array(v.z.string())]).optional()}),b=y.extend({stdin:v.z.string().optional()});async function x(e){let a=r.parseArgs(process.argv);if(!a.cwd)throw Error(`cwd argument required`);let c=y.parse(a.params),g=await s.readProblemMarkdownFrontMatter(e),v=await i.readTestCases(_.default.join(e,`test_cases`));if(!s.judgesTestCasesWithoutExpectations(g)){for(let e of v)if(e.output===void 0&&!await S(e.fileOutputPath))throw Error(`test case ${e.id} needs an expected output (${e.id}.out or a non-empty ${e.id}.fout/)`)}let b=await p.judgeByStaticAnalysis(a.cwd,g);if(b){t.printTestCaseResult({testCaseId:v[0]?.id??`prebuild`,...b});return}let x=await f.findEntryPointFile(a.cwd,c.language);if(!x){t.printTestCaseResult({testCaseId:v[0]?.id??`prebuild`,decisionCode:l.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${c.language?`: language: ${c.language}`:``}`});return}let C=o.findLanguageDefinitionByPath(x);if(!C){t.printTestCaseResult({testCaseId:v[0]?.id??`prebuild`,decisionCode:l.DecisionCode.WRONG_ANSWER,stderr:`unsupported language`});return}let w={...process.env,CI:``,FORCE_COLOR:`0`},T;if(C.prebuild)try{await C.prebuild(a.cwd),T=await f.findEntryPointFile(a.cwd,c.language)}catch(e){console.error(`prebuild error`,e),t.printTestCaseResult({testCaseId:v[0]?.id??`prebuild`,decisionCode:l.DecisionCode.BUILD_ERROR,stderr:e instanceof Error?e.message:String(e)});return}let E=T??x;if(C.buildCommand){let e=C.buildCommand(E),n=await h.spawnWithTimeout(e[0],e.slice(1),{cwd:a.cwd,env:w},10),r=l.DecisionCode.ACCEPTED;if(n.status===0?n.timeSeconds>10?r=l.DecisionCode.BUILD_TIME_LIMIT_EXCEEDED:(n.stdout.length>5e4||n.stderr.length>5e4)&&(r=l.DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED):r=l.DecisionCode.BUILD_ERROR,r!==l.DecisionCode.ACCEPTED){t.printTestCaseResult({testCaseId:v[0]?.id??`build`,decisionCode:r,exitStatus:n.status??void 0,stdout:n.stdout.slice(0,5e4)||void 0,stderr:n.stderr.slice(0,5e4)||void 0,timeSeconds:n.timeSeconds,cpuTimeSeconds:n.cpuTimeSeconds,memoryBytes:n.memoryBytes});return}}let D=await u.snapshotWorkingDirectory(a.cwd);v.length===0&&t.printTestCaseResult({testCaseId:`default`,decisionCode:l.DecisionCode.ACCEPTED});for(let e of v){v.shared?.fileInputPath&&await d.copyTestCaseFileInput(v.shared.fileInputPath,a.cwd),e.fileInputPath&&await d.copyTestCaseFileInput(e.fileInputPath,a.cwd);let r=typeof g.timeLimitMs==`number`?g.timeLimitMs/1e3:2,i=C.command(E),o=await h.spawnWithTimeout(i[0],i.slice(1),{cwd:a.cwd,stdin:e.input,env:w},r),s=await m.readOutputFiles(a.cwd,g.requiredOutputFilePaths??[]),c=l.DecisionCode.ACCEPTED,f;if(o.status!==0)c=l.DecisionCode.RUNTIME_ERROR;else if(o.timeSeconds>r)c=l.DecisionCode.TIME_LIMIT_EXCEEDED;else if(o.memoryBytes>(g.memoryLimitByte??1/0))c=l.DecisionCode.MEMORY_LIMIT_EXCEEDED;else if(o.stdout.length>5e4||o.stderr.length>5e4)c=l.DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED;else if(s.length<(g.requiredOutputFilePaths?.length??0))c=l.DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR;else try{let t=await n.judgeAgainstExpectations({stdout:o.stdout,expectedStdout:e.output,fileOutputPath:e.fileOutputPath,cwd:a.cwd,outputFiles:s});t.matches||(c=l.DecisionCode.WRONG_ANSWER),s=t.outputFiles}catch(e){c=l.DecisionCode.RUNTIME_ERROR,f=e instanceof Error?e.message:String(e)}if(t.printTestCaseResult({testCaseId:e.id,decisionCode:c,exitStatus:o.status??void 0,stdin:e.input,stdout:o.stdout.slice(0,5e4)||void 0,stderr:(f??o.stderr).slice(0,5e4)||void 0,timeSeconds:o.timeSeconds,cpuTimeSeconds:o.cpuTimeSeconds,memoryBytes:o.memoryBytes,outputFiles:s.length>0?s:void 0}),await u.cleanWorkingDirectory(a.cwd,D),c!==l.DecisionCode.ACCEPTED)break}}async function S(e){return e!==void 0&&(await g.default.promises.readdir(e,{withFileTypes:!0,recursive:!0})).some(e=>e.isFile())}async function C(e){let t=r.parseArgs(process.argv);if(!t.cwd)throw Error(`cwd argument required`);let n=b.parse(t.params),i,o=()=>{i!==void 0&&a.forciblyRemoveDirectorySync(i),process.exit(1)};process.once(`SIGINT`,o),process.once(`SIGTERM`,o);try{await w(e,(await a.copyProblemDirToTemporaryRoot(t.cwd,{onTempRootCreated:e=>i=e})).copiedProblemDir,n)}finally{i!==void 0&&await a.forciblyRemoveDirectory(i),process.off(`SIGINT`,o),process.off(`SIGTERM`,o)}}async function w(e,n,r){let a=await s.readProblemMarkdownFrontMatter(e),u=await f.findEntryPointFile(n,r.language);if(!u){t.printTestCaseResult({testCaseId:`prebuild`,decisionCode:l.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${r.language?`: language: ${r.language}`:``}`});return}let p=o.findLanguageDefinitionByPath(u);if(!p){t.printTestCaseResult({testCaseId:`prebuild`,decisionCode:l.DecisionCode.WRONG_ANSWER,stderr:`unsupported language`});return}let g={...process.env,CI:``,FORCE_COLOR:`0`},v;if(p.prebuild)try{await p.prebuild(n),v=await f.findEntryPointFile(n,r.language)}catch(e){console.error(`prebuild error`,e),t.printTestCaseResult({testCaseId:`prebuild`,decisionCode:l.DecisionCode.BUILD_ERROR,stderr:e instanceof Error?e.message:String(e)});return}let y=v??u;if(p.buildCommand){let e=p.buildCommand(y),r=await h.spawnWithTimeout(e[0],e.slice(1),{cwd:n,env:g},10),i=l.DecisionCode.ACCEPTED;if(r.status===0?r.timeSeconds>10?i=l.DecisionCode.BUILD_TIME_LIMIT_EXCEEDED:(r.stdout.length>5e4||r.stderr.length>5e4)&&(i=l.DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED):i=l.DecisionCode.BUILD_ERROR,i!==l.DecisionCode.ACCEPTED){t.printTestCaseResult({testCaseId:`build`,decisionCode:i,exitStatus:r.status??void 0,stdout:r.stdout.slice(0,5e4)||void 0,stderr:r.stderr.slice(0,5e4)||void 0,timeSeconds:r.timeSeconds,cpuTimeSeconds:r.cpuTimeSeconds,memoryBytes:r.memoryBytes});return}}let b=await i.readTestCases(_.default.join(e,`test_cases`));b.shared?.fileInputPath&&await d.copyTestCaseFileInput(b.shared.fileInputPath,n);let x=b.find(e=>c.EXAMPLE_TEST_CASE_ID_PATTERN.test(e.id)&&e.fileInputPath)?.fileInputPath;x?await d.copyTestCaseFileInput(x,n):b.some(e=>e.fileInputPath)&&console.error(`debug: no example test case has input files, so the program runs without them`);let S=Math.max(10,(a.timeLimitMs??0)/1e3),C=p.command(y),w=await h.spawnWithTimeout(C[0],C.slice(1),{cwd:n,stdin:r.stdin,env:g},S),T=await m.readOutputFiles(n,a.requiredOutputFilePaths??[]),E=l.DecisionCode.ACCEPTED;w.status===0?w.timeSeconds>S?E=l.DecisionCode.TIME_LIMIT_EXCEEDED:w.memoryBytes>(a.memoryLimitByte??1/0)?E=l.DecisionCode.MEMORY_LIMIT_EXCEEDED:w.stdout.length>5e4||w.stderr.length>5e4?E=l.DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED:T.length<(a.requiredOutputFilePaths?.length??0)&&(E=l.DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR):E=l.DecisionCode.RUNTIME_ERROR,t.printTestCaseResult({testCaseId:`debug`,decisionCode:E,exitStatus:w.status??void 0,stdin:r.stdin,stdout:w.stdout.slice(0,5e4)||void 0,stderr:w.stderr.slice(0,5e4)||void 0,timeSeconds:w.timeSeconds,cpuTimeSeconds:w.cpuTimeSeconds,memoryBytes:w.memoryBytes,outputFiles:T.length>0?T:void 0})}exports.stdioDebugPreset=C,exports.stdioJudgePreset=x;
2
2
  //# sourceMappingURL=stdio.cjs.map