@exercode/problem-utils 3.2.0 → 3.2.2
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.
- package/README.md +1 -1
- package/dist/helpers/judgeByStaticAnalysis.cjs +5 -5
- package/dist/helpers/judgeByStaticAnalysis.cjs.map +1 -1
- package/dist/helpers/judgeByStaticAnalysis.js +5 -5
- package/dist/helpers/judgeByStaticAnalysis.js.map +1 -1
- package/dist/presets/llm.cjs +1 -1
- package/dist/presets/llm.cjs.map +1 -1
- package/dist/presets/llm.js +1 -1
- package/dist/presets/llm.js.map +1 -1
- package/dist/presets/stdio.cjs +1 -1
- package/dist/presets/stdio.cjs.map +1 -1
- package/dist/presets/stdio.d.ts +5 -1
- package/dist/presets/stdio.js +1 -1
- package/dist/presets/stdio.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -78,7 +78,7 @@ How a missing expectation is treated depends on the harness:
|
|
|
78
78
|
- `commandJudgePreset` without a `test` option checks whatever expectations exist, and a test case with neither only has to run within the limits. A `test` option replaces that comparison; it receives `testCase.output`, `testCase.fileOutputPath` and `cwd` and can call the exported `judgeAgainstExpectations` (or `compareStdoutAsSpaceSeparatedTokens` and `compareExpectedOutputFiles`). A custom `readTestCases` may return any test case type with `id` (plus optional `input` and `fileInputPath`); the default verdict judges any case that exposes a string `output` or `fileOutputPath`, which the default reader's `CommandTestCase` does.
|
|
79
79
|
- `guiCommandJudgePreset` passes the expectations to the problem's `test`, which decides everything.
|
|
80
80
|
- `llmJudgePreset` runs no program, so it copies no `.fin/`; it hands `<id>.in` as the prompt input and the whole entry (including `fileOutputPath`) to the problem's `test`.
|
|
81
|
-
- `stdioDebugPreset` copies the answer directory to a temporary directory and builds and runs it there together with `_shared.fin/` and the first example case's `.fin/` (the case's files win; hidden cases' inputs are never handed to learner code), so the answer directory is left untouched (its `node_modules`, and those of its ancestors, are linked into the copy rather than duplicated); files the program writes are reported only through `requiredOutputFilePaths`.
|
|
81
|
+
- `stdioDebugPreset` by default copies the answer directory to a temporary directory and builds and runs it there together with `_shared.fin/` and the first example case's `.fin/` (the case's files win; hidden cases' inputs are never handed to learner code), so the answer directory is left untouched (its `node_modules`, and those of its ancestors, are linked into the copy rather than duplicated); files the program writes are reported only through `requiredOutputFilePaths`. Callers that already own a disposable answer directory can pass `{ disposableWorkingDirectory: true }` as the second argument to build and run directly there; they must remove it themselves, including when the harness is terminated.
|
|
82
82
|
- `evaluationJudgePreset` does not use `test_cases/`.
|
|
83
83
|
|
|
84
84
|
`readTestCases` is exported for harnesses that enumerate `test_cases/` themselves.
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
"use strict";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./removeCommentsInSourceCode.cjs"),n=require("../types/problem.cjs"),r=require("./findLanguageDefinitionByPath.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);async function s(e,s){let l=new Set,
|
|
1
|
+
"use strict";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./removeCommentsInSourceCode.cjs"),n=require("../types/problem.cjs"),r=require("./findLanguageDefinitionByPath.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);async function s(e,s){let l=!!s.forbiddenRegExpsInCode?.length||!!s.forbiddenTextsInCode?.length||!!s.requiredRegExpsInCode?.length;if(!l&&!s.requiredSubmissionFilePaths?.length)return;let u=new Set,d=[];for(let n of await a.default.promises.readdir(e,{withFileTypes:!0,recursive:!0})){if(!n.isFile())continue;let i=o.default.relative(e,o.default.join(n.parentPath,n.name));if(u.add(i),!l)continue;let s=r.findLanguageDefinitionByPath(n.name);if(!s)continue;let c=await a.default.promises.readFile(o.default.join(n.parentPath,n.name),`utf8`);c.includes(`�`)||d.push({path:i,data:s.grammer?t.removeCommentsInSourceCode(s.grammer,c):c})}if(s.requiredSubmissionFilePaths){let e=s.requiredSubmissionFilePaths.filter(e=>!u.has(e)).toSorted();if(e.length>0)return{decisionCode:i.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,feedbackMarkdown:`ファイルが不足しています。
|
|
2
2
|
次のファイルを追加してから再度提出してください。
|
|
3
3
|
|
|
4
4
|
${e.map(e=>`- \`${e}\``).join(`
|
|
5
5
|
`)}
|
|
6
|
-
`}}let
|
|
6
|
+
`}}let f=[];for(let e of s.forbiddenRegExpsInCode??[]){let{pattern:t,message:r}=n.normalizeCodeRule(e),i=new RegExp(t,`g`),a=d.flatMap(e=>[...e.data.matchAll(i)].map(t=>({path:e.path,match:t[0]})));a.length>0&&f.push({label:r??`禁止パターン \`${i.toString()}\``,matches:a})}for(let e of s.forbiddenTextsInCode??[]){let{pattern:t,message:r}=n.normalizeCodeRule(e),i=d.filter(e=>e.data.includes(t)).map(e=>({path:e.path,match:t}));i.length>0&&f.push({label:r??`禁止文字列 \`${t}\``,matches:i})}if(f.length>0)return{decisionCode:i.DecisionCode.FORBIDDEN_PATTERNS_IN_CODE_ERROR,feedbackMarkdown:`ソースコード中に禁止された文字列が含まれています。
|
|
7
7
|
ソースコードを修正してから再度提出してください。
|
|
8
8
|
|
|
9
|
-
${
|
|
9
|
+
${f.map(c).join(`
|
|
10
10
|
`)}
|
|
11
|
-
`};let
|
|
11
|
+
`};let p=[];for(let e of s.requiredRegExpsInCode??[]){let{pattern:t,message:r}=n.normalizeCodeRule(e),i=new RegExp(t);d.some(e=>i.test(e.data))||p.push(r??`\`${i.toString()}\``)}if(p.length>0)return{decisionCode:i.DecisionCode.REQUIRED_PATTERNS_IN_CODE_ERROR,feedbackMarkdown:`ソースコード中に必要な文字列が含まれていません。
|
|
12
12
|
ソースコードを修正してから再度提出してください。
|
|
13
13
|
|
|
14
|
-
${
|
|
14
|
+
${p.map(e=>`- ${e}`).join(`
|
|
15
15
|
`)}
|
|
16
16
|
`}}function c(e){let t=[...new Set(e.matches.map(({path:e,match:t})=>` - \`${e}\`: \`${t}\``))];return`- ${e.label}\n${t.join(`
|
|
17
17
|
`)}`}exports.judgeByStaticAnalysis=s;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"judgeByStaticAnalysis.cjs","names":["fs","path","findLanguageDefinitionByPath","removeCommentsInSourceCode","DecisionCode","normalizeCodeRule"],"sources":["../../src/helpers/judgeByStaticAnalysis.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport { normalizeCodeRule } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nimport { findLanguageDefinitionByPath } from './findLanguageDefinitionByPath.js';\nimport { removeCommentsInSourceCode } from './removeCommentsInSourceCode.js';\n\ninterface ForbiddenRuleViolation {\n /** Learner-facing description of the rule; the raw pattern when the problem declares no message. */\n label: string;\n matches: { path: string; match: string }[];\n}\n\nexport async function judgeByStaticAnalysis(\n cwd: string,\n problemMarkdownFrontMatterLike: Pick<\n ProblemMarkdownFrontMatter,\n 'forbiddenRegExpsInCode' | 'forbiddenTextsInCode' | 'requiredRegExpsInCode' | 'requiredSubmissionFilePaths'\n >\n): Promise<Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown'> | undefined> {\n const filePathSet = new Set<string>();\n const sourceCodeWithoutCommentFiles: { path: string; data: string }[] = [];\n\n for (const dirent of await fs.promises.readdir(cwd, { withFileTypes: true, recursive: true })) {\n if (!dirent.isFile()) continue;\n\n const relativePath = path.relative(cwd, path.join(dirent.parentPath, dirent.name));\n filePathSet.add(relativePath);\n\n const text = await fs.promises.readFile(path.join(dirent.parentPath, dirent.name), 'utf8');\n const isBinary = text.includes('\\uFFFD');\n if (isBinary) continue;\n\n
|
|
1
|
+
{"version":3,"file":"judgeByStaticAnalysis.cjs","names":["fs","path","findLanguageDefinitionByPath","removeCommentsInSourceCode","DecisionCode","normalizeCodeRule"],"sources":["../../src/helpers/judgeByStaticAnalysis.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport { normalizeCodeRule } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nimport { findLanguageDefinitionByPath } from './findLanguageDefinitionByPath.js';\nimport { removeCommentsInSourceCode } from './removeCommentsInSourceCode.js';\n\ninterface ForbiddenRuleViolation {\n /** Learner-facing description of the rule; the raw pattern when the problem declares no message. */\n label: string;\n matches: { path: string; match: string }[];\n}\n\nexport async function judgeByStaticAnalysis(\n cwd: string,\n problemMarkdownFrontMatterLike: Pick<\n ProblemMarkdownFrontMatter,\n 'forbiddenRegExpsInCode' | 'forbiddenTextsInCode' | 'requiredRegExpsInCode' | 'requiredSubmissionFilePaths'\n >\n): Promise<Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown'> | undefined> {\n const needsSourceCode =\n !!problemMarkdownFrontMatterLike.forbiddenRegExpsInCode?.length ||\n !!problemMarkdownFrontMatterLike.forbiddenTextsInCode?.length ||\n !!problemMarkdownFrontMatterLike.requiredRegExpsInCode?.length;\n if (!needsSourceCode && !problemMarkdownFrontMatterLike.requiredSubmissionFilePaths?.length) return;\n\n const filePathSet = new Set<string>();\n const sourceCodeWithoutCommentFiles: { path: string; data: string }[] = [];\n\n for (const dirent of await fs.promises.readdir(cwd, { withFileTypes: true, recursive: true })) {\n if (!dirent.isFile()) continue;\n\n const relativePath = path.relative(cwd, path.join(dirent.parentPath, dirent.name));\n filePathSet.add(relativePath);\n\n if (!needsSourceCode) continue;\n const languageDefinition = findLanguageDefinitionByPath(dirent.name);\n if (!languageDefinition) continue;\n\n const text = await fs.promises.readFile(path.join(dirent.parentPath, dirent.name), 'utf8');\n const isBinary = text.includes('\\uFFFD');\n if (isBinary) continue;\n\n sourceCodeWithoutCommentFiles.push({\n path: relativePath,\n data: languageDefinition.grammer ? removeCommentsInSourceCode(languageDefinition.grammer, text) : text,\n });\n }\n\n if (problemMarkdownFrontMatterLike.requiredSubmissionFilePaths) {\n const missingFilePaths = problemMarkdownFrontMatterLike.requiredSubmissionFilePaths\n .filter((p) => !filePathSet.has(p))\n .toSorted();\n\n if (missingFilePaths.length > 0) {\n return {\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n feedbackMarkdown: `ファイルが不足しています。\n次のファイルを追加してから再度提出してください。\n\n${missingFilePaths.map((p) => `- \\`${p}\\``).join('\\n')}\n`,\n };\n }\n }\n\n const violations: ForbiddenRuleViolation[] = [];\n\n for (const rule of problemMarkdownFrontMatterLike.forbiddenRegExpsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const re = new RegExp(pattern, 'g');\n const matches = sourceCodeWithoutCommentFiles.flatMap((file) =>\n [...file.data.matchAll(re)].map((match) => ({ path: file.path, match: match[0] }))\n );\n if (matches.length > 0) violations.push({ label: message ?? `禁止パターン \\`${re.toString()}\\``, matches });\n }\n for (const rule of problemMarkdownFrontMatterLike.forbiddenTextsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const matches = sourceCodeWithoutCommentFiles\n .filter((file) => file.data.includes(pattern))\n .map((file) => ({ path: file.path, match: pattern }));\n if (matches.length > 0) violations.push({ label: message ?? `禁止文字列 \\`${pattern}\\``, matches });\n }\n\n if (violations.length > 0) {\n return {\n decisionCode: DecisionCode.FORBIDDEN_PATTERNS_IN_CODE_ERROR,\n feedbackMarkdown: `ソースコード中に禁止された文字列が含まれています。\nソースコードを修正してから再度提出してください。\n\n${violations.map(formatForbiddenRuleViolation).join('\\n')}\n`,\n };\n }\n\n const missingRequiredLabels: string[] = [];\n\n for (const rule of problemMarkdownFrontMatterLike.requiredRegExpsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const re = new RegExp(pattern);\n const isFound = sourceCodeWithoutCommentFiles.some((f) => re.test(f.data));\n if (!isFound) missingRequiredLabels.push(message ?? `\\`${re.toString()}\\``);\n }\n\n if (missingRequiredLabels.length > 0) {\n return {\n decisionCode: DecisionCode.REQUIRED_PATTERNS_IN_CODE_ERROR,\n feedbackMarkdown: `ソースコード中に必要な文字列が含まれていません。\nソースコードを修正してから再度提出してください。\n\n${missingRequiredLabels.map((label) => `- ${label}`).join('\\n')}\n`,\n };\n }\n\n return;\n}\n\nfunction formatForbiddenRuleViolation(violation: ForbiddenRuleViolation): string {\n const locations = [...new Set(violation.matches.map(({ path, match }) => ` - \\`${path}\\`: \\`${match}\\``))];\n return `- ${violation.label}\\n${locations.join('\\n')}`;\n}\n"],"mappings":"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"}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import{removeCommentsInSourceCode as e}from"./removeCommentsInSourceCode.js";import{normalizeCodeRule as t}from"../types/problem.js";import{findLanguageDefinitionByPath as n}from"./findLanguageDefinitionByPath.js";import{DecisionCode as r}from"../types/decisionCode.js";import i from"node:fs";import a from"node:path";async function o(o,c){let l=new Set,
|
|
1
|
+
import{removeCommentsInSourceCode as e}from"./removeCommentsInSourceCode.js";import{normalizeCodeRule as t}from"../types/problem.js";import{findLanguageDefinitionByPath as n}from"./findLanguageDefinitionByPath.js";import{DecisionCode as r}from"../types/decisionCode.js";import i from"node:fs";import a from"node:path";async function o(o,c){let l=!!c.forbiddenRegExpsInCode?.length||!!c.forbiddenTextsInCode?.length||!!c.requiredRegExpsInCode?.length;if(!l&&!c.requiredSubmissionFilePaths?.length)return;let u=new Set,d=[];for(let t of await i.promises.readdir(o,{withFileTypes:!0,recursive:!0})){if(!t.isFile())continue;let r=a.relative(o,a.join(t.parentPath,t.name));if(u.add(r),!l)continue;let s=n(t.name);if(!s)continue;let c=await i.promises.readFile(a.join(t.parentPath,t.name),`utf8`);c.includes(`�`)||d.push({path:r,data:s.grammer?e(s.grammer,c):c})}if(c.requiredSubmissionFilePaths){let e=c.requiredSubmissionFilePaths.filter(e=>!u.has(e)).toSorted();if(e.length>0)return{decisionCode:r.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,feedbackMarkdown:`ファイルが不足しています。
|
|
2
2
|
次のファイルを追加してから再度提出してください。
|
|
3
3
|
|
|
4
4
|
${e.map(e=>`- \`${e}\``).join(`
|
|
5
5
|
`)}
|
|
6
|
-
`}}let
|
|
6
|
+
`}}let f=[];for(let e of c.forbiddenRegExpsInCode??[]){let{pattern:n,message:r}=t(e),i=new RegExp(n,`g`),a=d.flatMap(e=>[...e.data.matchAll(i)].map(t=>({path:e.path,match:t[0]})));a.length>0&&f.push({label:r??`禁止パターン \`${i.toString()}\``,matches:a})}for(let e of c.forbiddenTextsInCode??[]){let{pattern:n,message:r}=t(e),i=d.filter(e=>e.data.includes(n)).map(e=>({path:e.path,match:n}));i.length>0&&f.push({label:r??`禁止文字列 \`${n}\``,matches:i})}if(f.length>0)return{decisionCode:r.FORBIDDEN_PATTERNS_IN_CODE_ERROR,feedbackMarkdown:`ソースコード中に禁止された文字列が含まれています。
|
|
7
7
|
ソースコードを修正してから再度提出してください。
|
|
8
8
|
|
|
9
|
-
${
|
|
9
|
+
${f.map(s).join(`
|
|
10
10
|
`)}
|
|
11
|
-
`};let
|
|
11
|
+
`};let p=[];for(let e of c.requiredRegExpsInCode??[]){let{pattern:n,message:r}=t(e),i=new RegExp(n);d.some(e=>i.test(e.data))||p.push(r??`\`${i.toString()}\``)}if(p.length>0)return{decisionCode:r.REQUIRED_PATTERNS_IN_CODE_ERROR,feedbackMarkdown:`ソースコード中に必要な文字列が含まれていません。
|
|
12
12
|
ソースコードを修正してから再度提出してください。
|
|
13
13
|
|
|
14
|
-
${
|
|
14
|
+
${p.map(e=>`- ${e}`).join(`
|
|
15
15
|
`)}
|
|
16
16
|
`}}function s(e){let t=[...new Set(e.matches.map(({path:e,match:t})=>` - \`${e}\`: \`${t}\``))];return`- ${e.label}\n${t.join(`
|
|
17
17
|
`)}`}export{o as judgeByStaticAnalysis};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"judgeByStaticAnalysis.js","names":[],"sources":["../../src/helpers/judgeByStaticAnalysis.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport { normalizeCodeRule } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nimport { findLanguageDefinitionByPath } from './findLanguageDefinitionByPath.js';\nimport { removeCommentsInSourceCode } from './removeCommentsInSourceCode.js';\n\ninterface ForbiddenRuleViolation {\n /** Learner-facing description of the rule; the raw pattern when the problem declares no message. */\n label: string;\n matches: { path: string; match: string }[];\n}\n\nexport async function judgeByStaticAnalysis(\n cwd: string,\n problemMarkdownFrontMatterLike: Pick<\n ProblemMarkdownFrontMatter,\n 'forbiddenRegExpsInCode' | 'forbiddenTextsInCode' | 'requiredRegExpsInCode' | 'requiredSubmissionFilePaths'\n >\n): Promise<Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown'> | undefined> {\n const filePathSet = new Set<string>();\n const sourceCodeWithoutCommentFiles: { path: string; data: string }[] = [];\n\n for (const dirent of await fs.promises.readdir(cwd, { withFileTypes: true, recursive: true })) {\n if (!dirent.isFile()) continue;\n\n const relativePath = path.relative(cwd, path.join(dirent.parentPath, dirent.name));\n filePathSet.add(relativePath);\n\n const text = await fs.promises.readFile(path.join(dirent.parentPath, dirent.name), 'utf8');\n const isBinary = text.includes('\\uFFFD');\n if (isBinary) continue;\n\n
|
|
1
|
+
{"version":3,"file":"judgeByStaticAnalysis.js","names":[],"sources":["../../src/helpers/judgeByStaticAnalysis.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport { normalizeCodeRule } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nimport { findLanguageDefinitionByPath } from './findLanguageDefinitionByPath.js';\nimport { removeCommentsInSourceCode } from './removeCommentsInSourceCode.js';\n\ninterface ForbiddenRuleViolation {\n /** Learner-facing description of the rule; the raw pattern when the problem declares no message. */\n label: string;\n matches: { path: string; match: string }[];\n}\n\nexport async function judgeByStaticAnalysis(\n cwd: string,\n problemMarkdownFrontMatterLike: Pick<\n ProblemMarkdownFrontMatter,\n 'forbiddenRegExpsInCode' | 'forbiddenTextsInCode' | 'requiredRegExpsInCode' | 'requiredSubmissionFilePaths'\n >\n): Promise<Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown'> | undefined> {\n const needsSourceCode =\n !!problemMarkdownFrontMatterLike.forbiddenRegExpsInCode?.length ||\n !!problemMarkdownFrontMatterLike.forbiddenTextsInCode?.length ||\n !!problemMarkdownFrontMatterLike.requiredRegExpsInCode?.length;\n if (!needsSourceCode && !problemMarkdownFrontMatterLike.requiredSubmissionFilePaths?.length) return;\n\n const filePathSet = new Set<string>();\n const sourceCodeWithoutCommentFiles: { path: string; data: string }[] = [];\n\n for (const dirent of await fs.promises.readdir(cwd, { withFileTypes: true, recursive: true })) {\n if (!dirent.isFile()) continue;\n\n const relativePath = path.relative(cwd, path.join(dirent.parentPath, dirent.name));\n filePathSet.add(relativePath);\n\n if (!needsSourceCode) continue;\n const languageDefinition = findLanguageDefinitionByPath(dirent.name);\n if (!languageDefinition) continue;\n\n const text = await fs.promises.readFile(path.join(dirent.parentPath, dirent.name), 'utf8');\n const isBinary = text.includes('\\uFFFD');\n if (isBinary) continue;\n\n sourceCodeWithoutCommentFiles.push({\n path: relativePath,\n data: languageDefinition.grammer ? removeCommentsInSourceCode(languageDefinition.grammer, text) : text,\n });\n }\n\n if (problemMarkdownFrontMatterLike.requiredSubmissionFilePaths) {\n const missingFilePaths = problemMarkdownFrontMatterLike.requiredSubmissionFilePaths\n .filter((p) => !filePathSet.has(p))\n .toSorted();\n\n if (missingFilePaths.length > 0) {\n return {\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n feedbackMarkdown: `ファイルが不足しています。\n次のファイルを追加してから再度提出してください。\n\n${missingFilePaths.map((p) => `- \\`${p}\\``).join('\\n')}\n`,\n };\n }\n }\n\n const violations: ForbiddenRuleViolation[] = [];\n\n for (const rule of problemMarkdownFrontMatterLike.forbiddenRegExpsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const re = new RegExp(pattern, 'g');\n const matches = sourceCodeWithoutCommentFiles.flatMap((file) =>\n [...file.data.matchAll(re)].map((match) => ({ path: file.path, match: match[0] }))\n );\n if (matches.length > 0) violations.push({ label: message ?? `禁止パターン \\`${re.toString()}\\``, matches });\n }\n for (const rule of problemMarkdownFrontMatterLike.forbiddenTextsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const matches = sourceCodeWithoutCommentFiles\n .filter((file) => file.data.includes(pattern))\n .map((file) => ({ path: file.path, match: pattern }));\n if (matches.length > 0) violations.push({ label: message ?? `禁止文字列 \\`${pattern}\\``, matches });\n }\n\n if (violations.length > 0) {\n return {\n decisionCode: DecisionCode.FORBIDDEN_PATTERNS_IN_CODE_ERROR,\n feedbackMarkdown: `ソースコード中に禁止された文字列が含まれています。\nソースコードを修正してから再度提出してください。\n\n${violations.map(formatForbiddenRuleViolation).join('\\n')}\n`,\n };\n }\n\n const missingRequiredLabels: string[] = [];\n\n for (const rule of problemMarkdownFrontMatterLike.requiredRegExpsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const re = new RegExp(pattern);\n const isFound = sourceCodeWithoutCommentFiles.some((f) => re.test(f.data));\n if (!isFound) missingRequiredLabels.push(message ?? `\\`${re.toString()}\\``);\n }\n\n if (missingRequiredLabels.length > 0) {\n return {\n decisionCode: DecisionCode.REQUIRED_PATTERNS_IN_CODE_ERROR,\n feedbackMarkdown: `ソースコード中に必要な文字列が含まれていません。\nソースコードを修正してから再度提出してください。\n\n${missingRequiredLabels.map((label) => `- ${label}`).join('\\n')}\n`,\n };\n }\n\n return;\n}\n\nfunction formatForbiddenRuleViolation(violation: ForbiddenRuleViolation): string {\n const locations = [...new Set(violation.matches.map(({ path, match }) => ` - \\`${path}\\`: \\`${match}\\``))];\n return `- ${violation.label}\\n${locations.join('\\n')}`;\n}\n"],"mappings":"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"}
|
package/dist/presets/llm.cjs
CHANGED
|
@@ -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:
|
|
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
|
package/dist/presets/llm.cjs.map
CHANGED
|
@@ -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
|
|
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"}
|
package/dist/presets/llm.js
CHANGED
|
@@ -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:
|
|
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
|
package/dist/presets/llm.js.map
CHANGED
|
@@ -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
|
|
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"}
|
package/dist/presets/stdio.cjs
CHANGED
|
@@ -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,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
|
|
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,t={}){let n=r.parseArgs(process.argv);if(!n.cwd)throw Error(`cwd argument required`);let i=b.parse(n.params);if(t.disposableWorkingDirectory){await w(e,n.cwd,i);return}let o,s=()=>{o!==void 0&&a.forciblyRemoveDirectorySync(o),process.exit(1)};process.once(`SIGINT`,s),process.once(`SIGTERM`,s);try{await w(e,(await a.copyProblemDirToTemporaryRoot(n.cwd,{onTempRootCreated:e=>o=e})).copiedProblemDir,i)}finally{o!==void 0&&await a.forciblyRemoveDirectory(o),process.off(`SIGINT`,s),process.off(`SIGTERM`,s)}}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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stdio.cjs","names":["z","parseArgs","readProblemMarkdownFrontMatter","readTestCases","path","judgesTestCasesWithoutExpectations","judgeByStaticAnalysis","findEntryPointFile","DecisionCode","findLanguageDefinitionByPath","spawnWithTimeout","snapshotWorkingDirectory","copyTestCaseFileInput","readOutputFiles","judgeAgainstExpectations","cleanWorkingDirectory","fs","copyProblemDirToTemporaryRoot","forciblyRemoveDirectory","EXAMPLE_TEST_CASE_ID_PATTERN"],"sources":["../../src/presets/stdio.ts"],"sourcesContent":["import fs from 'node:fs';\nimport 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 {\n judgesTestCasesWithoutExpectations,\n readProblemMarkdownFrontMatter,\n} from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases } from '../helpers/readTestCases.js';\nimport { spawnWithTimeout } from '../helpers/spawnWithTimeout.js';\nimport { EXAMPLE_TEST_CASE_ID_PATTERN, MAX_STDOUT_LENGTH } from '../helpers/stdioJudgeRules.js';\nimport {\n copyProblemDirToTemporaryRoot,\n forciblyRemoveDirectory,\n forciblyRemoveDirectorySync,\n} from '../helpers/temporaryProblemDirCopy.js';\nimport { DecisionCode } from '../types/decisionCode.js';\n\nconst BUILD_TIMEOUT_SECONDS = 10;\nconst JUDGE_DEFAULT_TIMEOUT_SECONDS = 2;\nconst DEBUG_DEFAULT_TIMEOUT_SECONDS = 10;\n\nconst judgeParamsSchema = z.object({\n language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\nconst debugParamsSchema = judgeParamsSchema.extend({\n stdin: z.string().optional(),\n});\n\ntype DebugParams = z.infer<typeof debugParamsSchema>;\n\n/**\n * A preset judge function using stdin and stdout as test cases.\n *\n * A standard stdio problem must NOT commit a `judge.ts` that only calls this preset: the Exercode\n * server applies this preset automatically when `judge.ts` is absent, and committed copies would\n * drift from the server's defaults.\n *\n * @example\n * Run in a problem directory without `judge.ts`:\n * ```bash\n * bun x exercode-problem judge model_answers/java\n * ```\n *\n * Run with the optional parameters:\n * ```bash\n * bun x exercode-problem judge model_answers/java '{ \"language\": \"javascript\" }'\n * ```\n */\nexport async function stdioJudgePreset(problemDir: string): 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 problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n const testCases = await readTestCases(path.join(problemDir, 'test_cases'));\n\n // Without an expectation, a case would accept any run; only custom harnesses may judge input-only\n // cases. A problem judged by manual scoring or the presence of required output files has an\n // expectation of its own. Checked before the static analysis so that an authoring error surfaces\n // regardless of the submission.\n if (!judgesTestCasesWithoutExpectations(problemMarkdownFrontMatter)) {\n for (const testCase of testCases) {\n if (testCase.output === undefined && !(await hasExpectedFiles(testCase.fileOutputPath))) {\n throw new Error(\n `test case ${testCase.id} needs an expected output (${testCase.id}.out or a non-empty ${testCase.id}.fout/)`\n );\n }\n }\n }\n\n const staticAnalysisTestCaseResult = await judgeByStaticAnalysis(args.cwd, problemMarkdownFrontMatter);\n if (staticAnalysisTestCaseResult) {\n printTestCaseResult({ testCaseId: testCases[0]?.id ?? 'prebuild', ...staticAnalysisTestCaseResult });\n return;\n }\n\n const originalMainFilePath = await findEntryPointFile(args.cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'prebuild',\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return;\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'prebuild',\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return;\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let prebuiltMainFilePath: string | undefined;\n\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(args.cwd);\n prebuiltMainFilePath = await findEntryPointFile(args.cwd, params.language);\n } catch (error) {\n console.error('prebuild error', error);\n\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'prebuild',\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return;\n }\n }\n\n const mainFilePath = prebuiltMainFilePath ?? originalMainFilePath;\n\n if (languageDefinition.buildCommand) {\n const buildCommand = languageDefinition.buildCommand(mainFilePath);\n\n // The build reports the submission's failures in its result; what `spawnWithTimeout` throws is\n // the judge's own failure and ends the run without a verdict.\n const buildSpawnResult = await spawnWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd: args.cwd, env },\n BUILD_TIMEOUT_SECONDS\n );\n\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n\n if (buildSpawnResult.status !== 0) {\n decisionCode = DecisionCode.BUILD_ERROR;\n } else if (buildSpawnResult.timeSeconds > BUILD_TIMEOUT_SECONDS) {\n decisionCode = DecisionCode.BUILD_TIME_LIMIT_EXCEEDED;\n } else if (\n buildSpawnResult.stdout.length > MAX_STDOUT_LENGTH ||\n buildSpawnResult.stderr.length > MAX_STDOUT_LENGTH\n ) {\n decisionCode = DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED;\n }\n\n if (decisionCode !== DecisionCode.ACCEPTED) {\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'build',\n decisionCode,\n exitStatus: buildSpawnResult.status ?? undefined,\n stdout: buildSpawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: buildSpawnResult.stderr.slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: buildSpawnResult.timeSeconds,\n cpuTimeSeconds: buildSpawnResult.cpuTimeSeconds,\n memoryBytes: buildSpawnResult.memoryBytes,\n });\n return;\n }\n }\n\n const cwdSnapshot = await snapshotWorkingDirectory(args.cwd);\n\n if (testCases.length === 0) {\n printTestCaseResult({ testCaseId: 'default', decisionCode: DecisionCode.ACCEPTED });\n }\n\n for (const testCase of testCases) {\n // prepare test case\n if (testCases.shared?.fileInputPath) await copyTestCaseFileInput(testCases.shared.fileInputPath, args.cwd);\n if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, args.cwd);\n\n // run\n const timeoutSeconds =\n typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n ? problemMarkdownFrontMatter.timeLimitMs / 1000\n : JUDGE_DEFAULT_TIMEOUT_SECONDS;\n\n const command = languageDefinition.command(mainFilePath);\n\n const spawnResult = await spawnWithTimeout(\n command[0],\n command.slice(1),\n { cwd: args.cwd, stdin: testCase.input, env },\n timeoutSeconds\n );\n\n let outputFiles = await readOutputFiles(args.cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n\n // calculate decision\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n let judgementError: string | undefined;\n\n if (spawnResult.status !== 0) {\n decisionCode = DecisionCode.RUNTIME_ERROR;\n } else if (spawnResult.timeSeconds > timeoutSeconds) {\n decisionCode = DecisionCode.TIME_LIMIT_EXCEEDED;\n } else if (spawnResult.memoryBytes > (problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)) {\n decisionCode = DecisionCode.MEMORY_LIMIT_EXCEEDED;\n } else if (spawnResult.stdout.length > MAX_STDOUT_LENGTH || spawnResult.stderr.length > MAX_STDOUT_LENGTH) {\n decisionCode = DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED;\n } else if (outputFiles.length < (problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0)) {\n decisionCode = DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR;\n } else {\n try {\n const judgement = await judgeAgainstExpectations({\n stdout: spawnResult.stdout,\n expectedStdout: testCase.output,\n fileOutputPath: testCase.fileOutputPath,\n cwd: args.cwd,\n outputFiles,\n });\n if (!judgement.matches) decisionCode = DecisionCode.WRONG_ANSWER;\n outputFiles = judgement.outputFiles;\n } catch (error) {\n // An authoring error (e.g. an oversized expected file) is reported per case, like the command preset does.\n decisionCode = DecisionCode.RUNTIME_ERROR;\n judgementError = error instanceof Error ? error.message : String(error);\n }\n }\n\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode,\n exitStatus: spawnResult.status ?? undefined,\n stdin: testCase.input,\n stdout: spawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: (judgementError ?? spawnResult.stderr).slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n\n // clean up\n await cleanWorkingDirectory(args.cwd, cwdSnapshot);\n\n if (decisionCode !== DecisionCode.ACCEPTED) break;\n }\n}\n\nasync function hasExpectedFiles(fileOutputPath: string | undefined): Promise<boolean> {\n if (fileOutputPath === undefined) return false;\n const dirents = await fs.promises.readdir(fileOutputPath, { withFileTypes: true, recursive: true });\n return dirents.some((dirent) => dirent.isFile());\n}\n\n/**\n * A preset debug function using stdin and stdout as test cases. The answer directory is copied to a\n * temporary directory where it is built and run together with `_shared.fin/` and the first example\n * case's `.fin/`; files the program writes are reported only through `requiredOutputFilePaths`.\n *\n * A standard stdio problem must NOT commit a `debug.ts` that only calls this preset: the Exercode\n * server applies this preset automatically when `debug.ts` is absent, and committed copies would\n * drift from the server's defaults.\n *\n * @example\n * Run in a problem directory without `debug.ts`:\n * ```bash\n * bun x exercode-problem debug model_answers/java '{ \"stdin\": \"1 2\" }'\n * ```\n */\nexport async function stdioDebugPreset(problemDir: string): Promise<void> {\n const args = parseArgs(process.argv);\n if (!args.cwd) throw new Error('cwd argument required');\n const params = debugParamsSchema.parse(args.params);\n\n // Everything (build, input files, run) happens in a disposable copy of the answer directory, so\n // the developer's files are never touched and whatever the submission leaves behind goes with it.\n // The copy keeps the directory hierarchy and links every ancestor `node_modules`, so packages the\n // answer resolves from its parents still resolve.\n // A terminated debug run must not leave the copy behind; `finally` does not run on a signal, so\n // the handlers learn the root as soon as it is created and stay until it is removed.\n let tempRoot: string | undefined;\n const removeOnSignal = (): void => {\n if (tempRoot !== undefined) forciblyRemoveDirectorySync(tempRoot);\n process.exit(1);\n };\n process.once('SIGINT', removeOnSignal);\n process.once('SIGTERM', removeOnSignal);\n try {\n const copy = await copyProblemDirToTemporaryRoot(args.cwd, { onTempRootCreated: (root) => (tempRoot = root) });\n await debugInTemporaryCopy(problemDir, copy.copiedProblemDir, params);\n } finally {\n if (tempRoot !== undefined) await forciblyRemoveDirectory(tempRoot);\n process.off('SIGINT', removeOnSignal);\n process.off('SIGTERM', removeOnSignal);\n }\n}\n\nasync function debugInTemporaryCopy(problemDir: string, cwd: string, params: DebugParams): Promise<void> {\n const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n\n const originalMainFilePath = await findEntryPointFile(cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: 'prebuild',\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return;\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: 'prebuild',\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return;\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let prebuiltMainFilePath: string | undefined;\n\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(cwd);\n prebuiltMainFilePath = await findEntryPointFile(cwd, params.language);\n } catch (error) {\n console.error('prebuild error', error);\n\n printTestCaseResult({\n testCaseId: 'prebuild',\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return;\n }\n }\n\n const mainFilePath = prebuiltMainFilePath ?? originalMainFilePath;\n\n if (languageDefinition.buildCommand) {\n const buildCommand = languageDefinition.buildCommand(mainFilePath);\n\n // The build reports the submission's failures in its result; what `spawnWithTimeout` throws is\n // the judge's own failure and ends the run without a verdict.\n const buildSpawnResult = await spawnWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd, env },\n BUILD_TIMEOUT_SECONDS\n );\n\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n\n if (buildSpawnResult.status !== 0) {\n decisionCode = DecisionCode.BUILD_ERROR;\n } else if (buildSpawnResult.timeSeconds > BUILD_TIMEOUT_SECONDS) {\n decisionCode = DecisionCode.BUILD_TIME_LIMIT_EXCEEDED;\n } else if (\n buildSpawnResult.stdout.length > MAX_STDOUT_LENGTH ||\n buildSpawnResult.stderr.length > MAX_STDOUT_LENGTH\n ) {\n decisionCode = DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED;\n }\n\n if (decisionCode !== DecisionCode.ACCEPTED) {\n printTestCaseResult({\n testCaseId: 'build',\n decisionCode,\n exitStatus: buildSpawnResult.status ?? undefined,\n stdout: buildSpawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: buildSpawnResult.stderr.slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: buildSpawnResult.timeSeconds,\n cpuTimeSeconds: buildSpawnResult.cpuTimeSeconds,\n memoryBytes: buildSpawnResult.memoryBytes,\n });\n return;\n }\n }\n\n // A debug run has no test case of its own: it gets the shared input files and the `.fin/` of the\n // first example case (the cases a learner may see), placed the way the judge does. Hidden cases'\n // input files are never handed to learner code.\n const testCases = await readTestCases(path.join(problemDir, 'test_cases'));\n if (testCases.shared?.fileInputPath) await copyTestCaseFileInput(testCases.shared.fileInputPath, cwd);\n const exampleFileInputPath = testCases.find(\n (testCase) => EXAMPLE_TEST_CASE_ID_PATTERN.test(testCase.id) && testCase.fileInputPath\n )?.fileInputPath;\n if (exampleFileInputPath) {\n await copyTestCaseFileInput(exampleFileInputPath, cwd);\n } else if (testCases.some((testCase) => testCase.fileInputPath)) {\n console.error('debug: no example test case has input files, so the program runs without them');\n }\n\n const timeoutSeconds = Math.max(DEBUG_DEFAULT_TIMEOUT_SECONDS, (problemMarkdownFrontMatter.timeLimitMs ?? 0) / 1000);\n\n const command = languageDefinition.command(mainFilePath);\n\n const spawnResult = await spawnWithTimeout(\n command[0],\n command.slice(1),\n { cwd, stdin: params.stdin, env },\n timeoutSeconds\n );\n\n const outputFiles = await readOutputFiles(cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n\n if (spawnResult.status !== 0) {\n decisionCode = DecisionCode.RUNTIME_ERROR;\n } else if (spawnResult.timeSeconds > timeoutSeconds) {\n decisionCode = DecisionCode.TIME_LIMIT_EXCEEDED;\n } else if (spawnResult.memoryBytes > (problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)) {\n decisionCode = DecisionCode.MEMORY_LIMIT_EXCEEDED;\n } else if (spawnResult.stdout.length > MAX_STDOUT_LENGTH || spawnResult.stderr.length > MAX_STDOUT_LENGTH) {\n decisionCode = DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED;\n } else if (outputFiles.length < (problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0)) {\n decisionCode = DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR;\n }\n\n printTestCaseResult({\n testCaseId: 'debug',\n decisionCode,\n exitStatus: spawnResult.status ?? undefined,\n stdin: params.stdin,\n stdout: spawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: spawnResult.stderr.slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stdio.cjs","names":["z","parseArgs","readProblemMarkdownFrontMatter","readTestCases","path","judgesTestCasesWithoutExpectations","judgeByStaticAnalysis","findEntryPointFile","DecisionCode","findLanguageDefinitionByPath","spawnWithTimeout","snapshotWorkingDirectory","copyTestCaseFileInput","readOutputFiles","judgeAgainstExpectations","cleanWorkingDirectory","fs","copyProblemDirToTemporaryRoot","forciblyRemoveDirectory","EXAMPLE_TEST_CASE_ID_PATTERN"],"sources":["../../src/presets/stdio.ts"],"sourcesContent":["import fs from 'node:fs';\nimport 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 {\n judgesTestCasesWithoutExpectations,\n readProblemMarkdownFrontMatter,\n} from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases } from '../helpers/readTestCases.js';\nimport { spawnWithTimeout } from '../helpers/spawnWithTimeout.js';\nimport { EXAMPLE_TEST_CASE_ID_PATTERN, MAX_STDOUT_LENGTH } from '../helpers/stdioJudgeRules.js';\nimport {\n copyProblemDirToTemporaryRoot,\n forciblyRemoveDirectory,\n forciblyRemoveDirectorySync,\n} from '../helpers/temporaryProblemDirCopy.js';\nimport { DecisionCode } from '../types/decisionCode.js';\n\nconst BUILD_TIMEOUT_SECONDS = 10;\nconst JUDGE_DEFAULT_TIMEOUT_SECONDS = 2;\nconst DEBUG_DEFAULT_TIMEOUT_SECONDS = 10;\n\nconst judgeParamsSchema = z.object({\n language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\nconst debugParamsSchema = judgeParamsSchema.extend({\n stdin: z.string().optional(),\n});\n\ntype DebugParams = z.infer<typeof debugParamsSchema>;\n\n/**\n * A preset judge function using stdin and stdout as test cases.\n *\n * A standard stdio problem must NOT commit a `judge.ts` that only calls this preset: the Exercode\n * server applies this preset automatically when `judge.ts` is absent, and committed copies would\n * drift from the server's defaults.\n *\n * @example\n * Run in a problem directory without `judge.ts`:\n * ```bash\n * bun x exercode-problem judge model_answers/java\n * ```\n *\n * Run with the optional parameters:\n * ```bash\n * bun x exercode-problem judge model_answers/java '{ \"language\": \"javascript\" }'\n * ```\n */\nexport async function stdioJudgePreset(problemDir: string): 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 problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n const testCases = await readTestCases(path.join(problemDir, 'test_cases'));\n\n // Without an expectation, a case would accept any run; only custom harnesses may judge input-only\n // cases. A problem judged by manual scoring or the presence of required output files has an\n // expectation of its own. Checked before the static analysis so that an authoring error surfaces\n // regardless of the submission.\n if (!judgesTestCasesWithoutExpectations(problemMarkdownFrontMatter)) {\n for (const testCase of testCases) {\n if (testCase.output === undefined && !(await hasExpectedFiles(testCase.fileOutputPath))) {\n throw new Error(\n `test case ${testCase.id} needs an expected output (${testCase.id}.out or a non-empty ${testCase.id}.fout/)`\n );\n }\n }\n }\n\n const staticAnalysisTestCaseResult = await judgeByStaticAnalysis(args.cwd, problemMarkdownFrontMatter);\n if (staticAnalysisTestCaseResult) {\n printTestCaseResult({ testCaseId: testCases[0]?.id ?? 'prebuild', ...staticAnalysisTestCaseResult });\n return;\n }\n\n const originalMainFilePath = await findEntryPointFile(args.cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'prebuild',\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return;\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'prebuild',\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return;\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let prebuiltMainFilePath: string | undefined;\n\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(args.cwd);\n prebuiltMainFilePath = await findEntryPointFile(args.cwd, params.language);\n } catch (error) {\n console.error('prebuild error', error);\n\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'prebuild',\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return;\n }\n }\n\n const mainFilePath = prebuiltMainFilePath ?? originalMainFilePath;\n\n if (languageDefinition.buildCommand) {\n const buildCommand = languageDefinition.buildCommand(mainFilePath);\n\n // The build reports the submission's failures in its result; what `spawnWithTimeout` throws is\n // the judge's own failure and ends the run without a verdict.\n const buildSpawnResult = await spawnWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd: args.cwd, env },\n BUILD_TIMEOUT_SECONDS\n );\n\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n\n if (buildSpawnResult.status !== 0) {\n decisionCode = DecisionCode.BUILD_ERROR;\n } else if (buildSpawnResult.timeSeconds > BUILD_TIMEOUT_SECONDS) {\n decisionCode = DecisionCode.BUILD_TIME_LIMIT_EXCEEDED;\n } else if (\n buildSpawnResult.stdout.length > MAX_STDOUT_LENGTH ||\n buildSpawnResult.stderr.length > MAX_STDOUT_LENGTH\n ) {\n decisionCode = DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED;\n }\n\n if (decisionCode !== DecisionCode.ACCEPTED) {\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'build',\n decisionCode,\n exitStatus: buildSpawnResult.status ?? undefined,\n stdout: buildSpawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: buildSpawnResult.stderr.slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: buildSpawnResult.timeSeconds,\n cpuTimeSeconds: buildSpawnResult.cpuTimeSeconds,\n memoryBytes: buildSpawnResult.memoryBytes,\n });\n return;\n }\n }\n\n const cwdSnapshot = await snapshotWorkingDirectory(args.cwd);\n\n if (testCases.length === 0) {\n printTestCaseResult({ testCaseId: 'default', decisionCode: DecisionCode.ACCEPTED });\n }\n\n for (const testCase of testCases) {\n // prepare test case\n if (testCases.shared?.fileInputPath) await copyTestCaseFileInput(testCases.shared.fileInputPath, args.cwd);\n if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, args.cwd);\n\n // run\n const timeoutSeconds =\n typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n ? problemMarkdownFrontMatter.timeLimitMs / 1000\n : JUDGE_DEFAULT_TIMEOUT_SECONDS;\n\n const command = languageDefinition.command(mainFilePath);\n\n const spawnResult = await spawnWithTimeout(\n command[0],\n command.slice(1),\n { cwd: args.cwd, stdin: testCase.input, env },\n timeoutSeconds\n );\n\n let outputFiles = await readOutputFiles(args.cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n\n // calculate decision\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n let judgementError: string | undefined;\n\n if (spawnResult.status !== 0) {\n decisionCode = DecisionCode.RUNTIME_ERROR;\n } else if (spawnResult.timeSeconds > timeoutSeconds) {\n decisionCode = DecisionCode.TIME_LIMIT_EXCEEDED;\n } else if (spawnResult.memoryBytes > (problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)) {\n decisionCode = DecisionCode.MEMORY_LIMIT_EXCEEDED;\n } else if (spawnResult.stdout.length > MAX_STDOUT_LENGTH || spawnResult.stderr.length > MAX_STDOUT_LENGTH) {\n decisionCode = DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED;\n } else if (outputFiles.length < (problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0)) {\n decisionCode = DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR;\n } else {\n try {\n const judgement = await judgeAgainstExpectations({\n stdout: spawnResult.stdout,\n expectedStdout: testCase.output,\n fileOutputPath: testCase.fileOutputPath,\n cwd: args.cwd,\n outputFiles,\n });\n if (!judgement.matches) decisionCode = DecisionCode.WRONG_ANSWER;\n outputFiles = judgement.outputFiles;\n } catch (error) {\n // An authoring error (e.g. an oversized expected file) is reported per case, like the command preset does.\n decisionCode = DecisionCode.RUNTIME_ERROR;\n judgementError = error instanceof Error ? error.message : String(error);\n }\n }\n\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode,\n exitStatus: spawnResult.status ?? undefined,\n stdin: testCase.input,\n stdout: spawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: (judgementError ?? spawnResult.stderr).slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n\n // clean up\n await cleanWorkingDirectory(args.cwd, cwdSnapshot);\n\n if (decisionCode !== DecisionCode.ACCEPTED) break;\n }\n}\n\nasync function hasExpectedFiles(fileOutputPath: string | undefined): Promise<boolean> {\n if (fileOutputPath === undefined) return false;\n const dirents = await fs.promises.readdir(fileOutputPath, { withFileTypes: true, recursive: true });\n return dirents.some((dirent) => dirent.isFile());\n}\n\n/**\n * A preset debug function using stdin and stdout as test cases. The answer directory is copied to a\n * temporary directory where it is built and run together with `_shared.fin/` and the first example\n * case's `.fin/`; files the program writes are reported only through `requiredOutputFilePaths`.\n * Callers that own and remove the working directory can set `disposableWorkingDirectory` to run\n * directly there; they are responsible for cleanup even when the harness is terminated.\n *\n * A standard stdio problem must NOT commit a `debug.ts` that only calls this preset: the Exercode\n * server applies this preset automatically when `debug.ts` is absent, and committed copies would\n * drift from the server's defaults.\n *\n * @example\n * Run in a problem directory without `debug.ts`:\n * ```bash\n * bun x exercode-problem debug model_answers/java '{ \"stdin\": \"1 2\" }'\n * ```\n */\nexport async function stdioDebugPreset(\n problemDir: string,\n options: { disposableWorkingDirectory?: boolean } = {}\n): Promise<void> {\n const args = parseArgs(process.argv);\n if (!args.cwd) throw new Error('cwd argument required');\n const params = debugParamsSchema.parse(args.params);\n\n // The judge server already owns and removes its per-request answer directory.\n if (options.disposableWorkingDirectory) {\n await debugInWorkingDirectory(problemDir, args.cwd, params);\n return;\n }\n\n // Everything (build, input files, run) happens in a disposable copy of the answer directory, so\n // the developer's files are never touched and whatever the submission leaves behind goes with it.\n // The copy keeps the directory hierarchy and links every ancestor `node_modules`, so packages the\n // answer resolves from its parents still resolve.\n // A terminated debug run must not leave the copy behind; `finally` does not run on a signal, so\n // the handlers learn the root as soon as it is created and stay until it is removed.\n let tempRoot: string | undefined;\n const removeOnSignal = (): void => {\n if (tempRoot !== undefined) forciblyRemoveDirectorySync(tempRoot);\n process.exit(1);\n };\n process.once('SIGINT', removeOnSignal);\n process.once('SIGTERM', removeOnSignal);\n try {\n const copy = await copyProblemDirToTemporaryRoot(args.cwd, { onTempRootCreated: (root) => (tempRoot = root) });\n await debugInWorkingDirectory(problemDir, copy.copiedProblemDir, params);\n } finally {\n if (tempRoot !== undefined) await forciblyRemoveDirectory(tempRoot);\n process.off('SIGINT', removeOnSignal);\n process.off('SIGTERM', removeOnSignal);\n }\n}\n\nasync function debugInWorkingDirectory(problemDir: string, cwd: string, params: DebugParams): Promise<void> {\n const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n\n const originalMainFilePath = await findEntryPointFile(cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: 'prebuild',\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return;\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: 'prebuild',\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return;\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let prebuiltMainFilePath: string | undefined;\n\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(cwd);\n prebuiltMainFilePath = await findEntryPointFile(cwd, params.language);\n } catch (error) {\n console.error('prebuild error', error);\n\n printTestCaseResult({\n testCaseId: 'prebuild',\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return;\n }\n }\n\n const mainFilePath = prebuiltMainFilePath ?? originalMainFilePath;\n\n if (languageDefinition.buildCommand) {\n const buildCommand = languageDefinition.buildCommand(mainFilePath);\n\n // The build reports the submission's failures in its result; what `spawnWithTimeout` throws is\n // the judge's own failure and ends the run without a verdict.\n const buildSpawnResult = await spawnWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd, env },\n BUILD_TIMEOUT_SECONDS\n );\n\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n\n if (buildSpawnResult.status !== 0) {\n decisionCode = DecisionCode.BUILD_ERROR;\n } else if (buildSpawnResult.timeSeconds > BUILD_TIMEOUT_SECONDS) {\n decisionCode = DecisionCode.BUILD_TIME_LIMIT_EXCEEDED;\n } else if (\n buildSpawnResult.stdout.length > MAX_STDOUT_LENGTH ||\n buildSpawnResult.stderr.length > MAX_STDOUT_LENGTH\n ) {\n decisionCode = DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED;\n }\n\n if (decisionCode !== DecisionCode.ACCEPTED) {\n printTestCaseResult({\n testCaseId: 'build',\n decisionCode,\n exitStatus: buildSpawnResult.status ?? undefined,\n stdout: buildSpawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: buildSpawnResult.stderr.slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: buildSpawnResult.timeSeconds,\n cpuTimeSeconds: buildSpawnResult.cpuTimeSeconds,\n memoryBytes: buildSpawnResult.memoryBytes,\n });\n return;\n }\n }\n\n // A debug run has no test case of its own: it gets the shared input files and the `.fin/` of the\n // first example case (the cases a learner may see), placed the way the judge does. Hidden cases'\n // input files are never handed to learner code.\n const testCases = await readTestCases(path.join(problemDir, 'test_cases'));\n if (testCases.shared?.fileInputPath) await copyTestCaseFileInput(testCases.shared.fileInputPath, cwd);\n const exampleFileInputPath = testCases.find(\n (testCase) => EXAMPLE_TEST_CASE_ID_PATTERN.test(testCase.id) && testCase.fileInputPath\n )?.fileInputPath;\n if (exampleFileInputPath) {\n await copyTestCaseFileInput(exampleFileInputPath, cwd);\n } else if (testCases.some((testCase) => testCase.fileInputPath)) {\n console.error('debug: no example test case has input files, so the program runs without them');\n }\n\n const timeoutSeconds = Math.max(DEBUG_DEFAULT_TIMEOUT_SECONDS, (problemMarkdownFrontMatter.timeLimitMs ?? 0) / 1000);\n\n const command = languageDefinition.command(mainFilePath);\n\n const spawnResult = await spawnWithTimeout(\n command[0],\n command.slice(1),\n { cwd, stdin: params.stdin, env },\n timeoutSeconds\n );\n\n const outputFiles = await readOutputFiles(cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n\n if (spawnResult.status !== 0) {\n decisionCode = DecisionCode.RUNTIME_ERROR;\n } else if (spawnResult.timeSeconds > timeoutSeconds) {\n decisionCode = DecisionCode.TIME_LIMIT_EXCEEDED;\n } else if (spawnResult.memoryBytes > (problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)) {\n decisionCode = DecisionCode.MEMORY_LIMIT_EXCEEDED;\n } else if (spawnResult.stdout.length > MAX_STDOUT_LENGTH || spawnResult.stderr.length > MAX_STDOUT_LENGTH) {\n decisionCode = DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED;\n } else if (outputFiles.length < (problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0)) {\n decisionCode = DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR;\n }\n\n printTestCaseResult({\n testCaseId: 'debug',\n decisionCode,\n exitStatus: spawnResult.status ?? undefined,\n stdin: params.stdin,\n stdout: spawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: spawnResult.stderr.slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n}\n"],"mappings":"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"}
|
package/dist/presets/stdio.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export declare function stdioJudgePreset(problemDir: string): Promise<void>;
|
|
|
21
21
|
* A preset debug function using stdin and stdout as test cases. The answer directory is copied to a
|
|
22
22
|
* temporary directory where it is built and run together with `_shared.fin/` and the first example
|
|
23
23
|
* case's `.fin/`; files the program writes are reported only through `requiredOutputFilePaths`.
|
|
24
|
+
* Callers that own and remove the working directory can set `disposableWorkingDirectory` to run
|
|
25
|
+
* directly there; they are responsible for cleanup even when the harness is terminated.
|
|
24
26
|
*
|
|
25
27
|
* A standard stdio problem must NOT commit a `debug.ts` that only calls this preset: the Exercode
|
|
26
28
|
* server applies this preset automatically when `debug.ts` is absent, and committed copies would
|
|
@@ -32,4 +34,6 @@ export declare function stdioJudgePreset(problemDir: string): Promise<void>;
|
|
|
32
34
|
* bun x exercode-problem debug model_answers/java '{ "stdin": "1 2" }'
|
|
33
35
|
* ```
|
|
34
36
|
*/
|
|
35
|
-
export declare function stdioDebugPreset(problemDir: string
|
|
37
|
+
export declare function stdioDebugPreset(problemDir: string, options?: {
|
|
38
|
+
disposableWorkingDirectory?: boolean;
|
|
39
|
+
}): Promise<void>;
|
package/dist/presets/stdio.js
CHANGED
|
@@ -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{copyProblemDirToTemporaryRoot as i,forciblyRemoveDirectory as a,forciblyRemoveDirectorySync as o}from"../helpers/temporaryProblemDirCopy.js";import{findLanguageDefinitionByPath as s}from"../helpers/findLanguageDefinitionByPath.js";import{judgesTestCasesWithoutExpectations as c,readProblemMarkdownFrontMatter as l}from"../helpers/readProblemMarkdownFrontMatter.js";import{EXAMPLE_TEST_CASE_ID_PATTERN as u}from"../helpers/stdioJudgeRules.js";import{DecisionCode as d}from"../types/decisionCode.js";import{cleanWorkingDirectory as f,snapshotWorkingDirectory as p}from"../helpers/cleanWorkingDirectory.js";import{copyTestCaseFileInput as m}from"../helpers/copyTestCaseFileInput.js";import{findEntryPointFile as h}from"../helpers/findEntryPointFile.js";import{judgeByStaticAnalysis as g}from"../helpers/judgeByStaticAnalysis.js";import{readOutputFiles as _}from"../helpers/readOutputFiles.js";import{spawnWithTimeout as v}from"../helpers/spawnWithTimeout.js";import y from"node:fs";import b from"node:path";import{z as x}from"zod";const S=x.object({language:x.union([x.string(),x.array(x.string())]).optional()}),C=S.extend({stdin:x.string().optional()});async function w(i){let a=n(process.argv);if(!a.cwd)throw Error(`cwd argument required`);let o=S.parse(a.params),u=await l(i),y=await r(b.join(i,`test_cases`));if(!c(u)){for(let e of y)if(e.output===void 0&&!await T(e.fileOutputPath))throw Error(`test case ${e.id} needs an expected output (${e.id}.out or a non-empty ${e.id}.fout/)`)}let x=await g(a.cwd,u);if(x){e({testCaseId:y[0]?.id??`prebuild`,...x});return}let C=await h(a.cwd,o.language);if(!C){e({testCaseId:y[0]?.id??`prebuild`,decisionCode:d.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${o.language?`: language: ${o.language}`:``}`});return}let w=s(C);if(!w){e({testCaseId:y[0]?.id??`prebuild`,decisionCode:d.WRONG_ANSWER,stderr:`unsupported language`});return}let E={...process.env,CI:``,FORCE_COLOR:`0`},D;if(w.prebuild)try{await w.prebuild(a.cwd),D=await h(a.cwd,o.language)}catch(t){console.error(`prebuild error`,t),e({testCaseId:y[0]?.id??`prebuild`,decisionCode:d.BUILD_ERROR,stderr:t instanceof Error?t.message:String(t)});return}let O=D??C;if(w.buildCommand){let t=w.buildCommand(O),n=await v(t[0],t.slice(1),{cwd:a.cwd,env:E},10),r=d.ACCEPTED;if(n.status===0?n.timeSeconds>10?r=d.BUILD_TIME_LIMIT_EXCEEDED:(n.stdout.length>5e4||n.stderr.length>5e4)&&(r=d.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED):r=d.BUILD_ERROR,r!==d.ACCEPTED){e({testCaseId:y[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 k=await p(a.cwd);y.length===0&&e({testCaseId:`default`,decisionCode:d.ACCEPTED});for(let n of y){y.shared?.fileInputPath&&await m(y.shared.fileInputPath,a.cwd),n.fileInputPath&&await m(n.fileInputPath,a.cwd);let r=typeof u.timeLimitMs==`number`?u.timeLimitMs/1e3:2,i=w.command(O),o=await v(i[0],i.slice(1),{cwd:a.cwd,stdin:n.input,env:E},r),s=await _(a.cwd,u.requiredOutputFilePaths??[]),c=d.ACCEPTED,l;if(o.status!==0)c=d.RUNTIME_ERROR;else if(o.timeSeconds>r)c=d.TIME_LIMIT_EXCEEDED;else if(o.memoryBytes>(u.memoryLimitByte??1/0))c=d.MEMORY_LIMIT_EXCEEDED;else if(o.stdout.length>5e4||o.stderr.length>5e4)c=d.OUTPUT_SIZE_LIMIT_EXCEEDED;else if(s.length<(u.requiredOutputFilePaths?.length??0))c=d.MISSING_REQUIRED_OUTPUT_FILE_ERROR;else try{let e=await t({stdout:o.stdout,expectedStdout:n.output,fileOutputPath:n.fileOutputPath,cwd:a.cwd,outputFiles:s});e.matches||(c=d.WRONG_ANSWER),s=e.outputFiles}catch(e){c=d.RUNTIME_ERROR,l=e instanceof Error?e.message:String(e)}if(e({testCaseId:n.id,decisionCode:c,exitStatus:o.status??void 0,stdin:n.input,stdout:o.stdout.slice(0,5e4)||void 0,stderr:(l??o.stderr).slice(0,5e4)||void 0,timeSeconds:o.timeSeconds,cpuTimeSeconds:o.cpuTimeSeconds,memoryBytes:o.memoryBytes,outputFiles:s.length>0?s:void 0}),await f(a.cwd,k),c!==d.ACCEPTED)break}}async function T(e){return e!==void 0&&(await y.promises.readdir(e,{withFileTypes:!0,recursive:!0})).some(e=>e.isFile())}async function E(e){let
|
|
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{copyProblemDirToTemporaryRoot as i,forciblyRemoveDirectory as a,forciblyRemoveDirectorySync as o}from"../helpers/temporaryProblemDirCopy.js";import{findLanguageDefinitionByPath as s}from"../helpers/findLanguageDefinitionByPath.js";import{judgesTestCasesWithoutExpectations as c,readProblemMarkdownFrontMatter as l}from"../helpers/readProblemMarkdownFrontMatter.js";import{EXAMPLE_TEST_CASE_ID_PATTERN as u}from"../helpers/stdioJudgeRules.js";import{DecisionCode as d}from"../types/decisionCode.js";import{cleanWorkingDirectory as f,snapshotWorkingDirectory as p}from"../helpers/cleanWorkingDirectory.js";import{copyTestCaseFileInput as m}from"../helpers/copyTestCaseFileInput.js";import{findEntryPointFile as h}from"../helpers/findEntryPointFile.js";import{judgeByStaticAnalysis as g}from"../helpers/judgeByStaticAnalysis.js";import{readOutputFiles as _}from"../helpers/readOutputFiles.js";import{spawnWithTimeout as v}from"../helpers/spawnWithTimeout.js";import y from"node:fs";import b from"node:path";import{z as x}from"zod";const S=x.object({language:x.union([x.string(),x.array(x.string())]).optional()}),C=S.extend({stdin:x.string().optional()});async function w(i){let a=n(process.argv);if(!a.cwd)throw Error(`cwd argument required`);let o=S.parse(a.params),u=await l(i),y=await r(b.join(i,`test_cases`));if(!c(u)){for(let e of y)if(e.output===void 0&&!await T(e.fileOutputPath))throw Error(`test case ${e.id} needs an expected output (${e.id}.out or a non-empty ${e.id}.fout/)`)}let x=await g(a.cwd,u);if(x){e({testCaseId:y[0]?.id??`prebuild`,...x});return}let C=await h(a.cwd,o.language);if(!C){e({testCaseId:y[0]?.id??`prebuild`,decisionCode:d.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${o.language?`: language: ${o.language}`:``}`});return}let w=s(C);if(!w){e({testCaseId:y[0]?.id??`prebuild`,decisionCode:d.WRONG_ANSWER,stderr:`unsupported language`});return}let E={...process.env,CI:``,FORCE_COLOR:`0`},D;if(w.prebuild)try{await w.prebuild(a.cwd),D=await h(a.cwd,o.language)}catch(t){console.error(`prebuild error`,t),e({testCaseId:y[0]?.id??`prebuild`,decisionCode:d.BUILD_ERROR,stderr:t instanceof Error?t.message:String(t)});return}let O=D??C;if(w.buildCommand){let t=w.buildCommand(O),n=await v(t[0],t.slice(1),{cwd:a.cwd,env:E},10),r=d.ACCEPTED;if(n.status===0?n.timeSeconds>10?r=d.BUILD_TIME_LIMIT_EXCEEDED:(n.stdout.length>5e4||n.stderr.length>5e4)&&(r=d.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED):r=d.BUILD_ERROR,r!==d.ACCEPTED){e({testCaseId:y[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 k=await p(a.cwd);y.length===0&&e({testCaseId:`default`,decisionCode:d.ACCEPTED});for(let n of y){y.shared?.fileInputPath&&await m(y.shared.fileInputPath,a.cwd),n.fileInputPath&&await m(n.fileInputPath,a.cwd);let r=typeof u.timeLimitMs==`number`?u.timeLimitMs/1e3:2,i=w.command(O),o=await v(i[0],i.slice(1),{cwd:a.cwd,stdin:n.input,env:E},r),s=await _(a.cwd,u.requiredOutputFilePaths??[]),c=d.ACCEPTED,l;if(o.status!==0)c=d.RUNTIME_ERROR;else if(o.timeSeconds>r)c=d.TIME_LIMIT_EXCEEDED;else if(o.memoryBytes>(u.memoryLimitByte??1/0))c=d.MEMORY_LIMIT_EXCEEDED;else if(o.stdout.length>5e4||o.stderr.length>5e4)c=d.OUTPUT_SIZE_LIMIT_EXCEEDED;else if(s.length<(u.requiredOutputFilePaths?.length??0))c=d.MISSING_REQUIRED_OUTPUT_FILE_ERROR;else try{let e=await t({stdout:o.stdout,expectedStdout:n.output,fileOutputPath:n.fileOutputPath,cwd:a.cwd,outputFiles:s});e.matches||(c=d.WRONG_ANSWER),s=e.outputFiles}catch(e){c=d.RUNTIME_ERROR,l=e instanceof Error?e.message:String(e)}if(e({testCaseId:n.id,decisionCode:c,exitStatus:o.status??void 0,stdin:n.input,stdout:o.stdout.slice(0,5e4)||void 0,stderr:(l??o.stderr).slice(0,5e4)||void 0,timeSeconds:o.timeSeconds,cpuTimeSeconds:o.cpuTimeSeconds,memoryBytes:o.memoryBytes,outputFiles:s.length>0?s:void 0}),await f(a.cwd,k),c!==d.ACCEPTED)break}}async function T(e){return e!==void 0&&(await y.promises.readdir(e,{withFileTypes:!0,recursive:!0})).some(e=>e.isFile())}async function E(e,t={}){let r=n(process.argv);if(!r.cwd)throw Error(`cwd argument required`);let s=C.parse(r.params);if(t.disposableWorkingDirectory){await D(e,r.cwd,s);return}let c,l=()=>{c!==void 0&&o(c),process.exit(1)};process.once(`SIGINT`,l),process.once(`SIGTERM`,l);try{await D(e,(await i(r.cwd,{onTempRootCreated:e=>c=e})).copiedProblemDir,s)}finally{c!==void 0&&await a(c),process.off(`SIGINT`,l),process.off(`SIGTERM`,l)}}async function D(t,n,i){let a=await l(t),o=await h(n,i.language);if(!o){e({testCaseId:`prebuild`,decisionCode:d.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${i.language?`: language: ${i.language}`:``}`});return}let c=s(o);if(!c){e({testCaseId:`prebuild`,decisionCode:d.WRONG_ANSWER,stderr:`unsupported language`});return}let f={...process.env,CI:``,FORCE_COLOR:`0`},p;if(c.prebuild)try{await c.prebuild(n),p=await h(n,i.language)}catch(t){console.error(`prebuild error`,t),e({testCaseId:`prebuild`,decisionCode:d.BUILD_ERROR,stderr:t instanceof Error?t.message:String(t)});return}let g=p??o;if(c.buildCommand){let t=c.buildCommand(g),r=await v(t[0],t.slice(1),{cwd:n,env:f},10),i=d.ACCEPTED;if(r.status===0?r.timeSeconds>10?i=d.BUILD_TIME_LIMIT_EXCEEDED:(r.stdout.length>5e4||r.stderr.length>5e4)&&(i=d.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED):i=d.BUILD_ERROR,i!==d.ACCEPTED){e({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 y=await r(b.join(t,`test_cases`));y.shared?.fileInputPath&&await m(y.shared.fileInputPath,n);let x=y.find(e=>u.test(e.id)&&e.fileInputPath)?.fileInputPath;x?await m(x,n):y.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=c.command(g),w=await v(C[0],C.slice(1),{cwd:n,stdin:i.stdin,env:f},S),T=await _(n,a.requiredOutputFilePaths??[]),E=d.ACCEPTED;w.status===0?w.timeSeconds>S?E=d.TIME_LIMIT_EXCEEDED:w.memoryBytes>(a.memoryLimitByte??1/0)?E=d.MEMORY_LIMIT_EXCEEDED:w.stdout.length>5e4||w.stderr.length>5e4?E=d.OUTPUT_SIZE_LIMIT_EXCEEDED:T.length<(a.requiredOutputFilePaths?.length??0)&&(E=d.MISSING_REQUIRED_OUTPUT_FILE_ERROR):E=d.RUNTIME_ERROR,e({testCaseId:`debug`,decisionCode:E,exitStatus:w.status??void 0,stdin:i.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})}export{E as stdioDebugPreset,w as stdioJudgePreset};
|
|
2
2
|
//# sourceMappingURL=stdio.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stdio.js","names":[],"sources":["../../src/presets/stdio.ts"],"sourcesContent":["import fs from 'node:fs';\nimport 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 {\n judgesTestCasesWithoutExpectations,\n readProblemMarkdownFrontMatter,\n} from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases } from '../helpers/readTestCases.js';\nimport { spawnWithTimeout } from '../helpers/spawnWithTimeout.js';\nimport { EXAMPLE_TEST_CASE_ID_PATTERN, MAX_STDOUT_LENGTH } from '../helpers/stdioJudgeRules.js';\nimport {\n copyProblemDirToTemporaryRoot,\n forciblyRemoveDirectory,\n forciblyRemoveDirectorySync,\n} from '../helpers/temporaryProblemDirCopy.js';\nimport { DecisionCode } from '../types/decisionCode.js';\n\nconst BUILD_TIMEOUT_SECONDS = 10;\nconst JUDGE_DEFAULT_TIMEOUT_SECONDS = 2;\nconst DEBUG_DEFAULT_TIMEOUT_SECONDS = 10;\n\nconst judgeParamsSchema = z.object({\n language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\nconst debugParamsSchema = judgeParamsSchema.extend({\n stdin: z.string().optional(),\n});\n\ntype DebugParams = z.infer<typeof debugParamsSchema>;\n\n/**\n * A preset judge function using stdin and stdout as test cases.\n *\n * A standard stdio problem must NOT commit a `judge.ts` that only calls this preset: the Exercode\n * server applies this preset automatically when `judge.ts` is absent, and committed copies would\n * drift from the server's defaults.\n *\n * @example\n * Run in a problem directory without `judge.ts`:\n * ```bash\n * bun x exercode-problem judge model_answers/java\n * ```\n *\n * Run with the optional parameters:\n * ```bash\n * bun x exercode-problem judge model_answers/java '{ \"language\": \"javascript\" }'\n * ```\n */\nexport async function stdioJudgePreset(problemDir: string): 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 problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n const testCases = await readTestCases(path.join(problemDir, 'test_cases'));\n\n // Without an expectation, a case would accept any run; only custom harnesses may judge input-only\n // cases. A problem judged by manual scoring or the presence of required output files has an\n // expectation of its own. Checked before the static analysis so that an authoring error surfaces\n // regardless of the submission.\n if (!judgesTestCasesWithoutExpectations(problemMarkdownFrontMatter)) {\n for (const testCase of testCases) {\n if (testCase.output === undefined && !(await hasExpectedFiles(testCase.fileOutputPath))) {\n throw new Error(\n `test case ${testCase.id} needs an expected output (${testCase.id}.out or a non-empty ${testCase.id}.fout/)`\n );\n }\n }\n }\n\n const staticAnalysisTestCaseResult = await judgeByStaticAnalysis(args.cwd, problemMarkdownFrontMatter);\n if (staticAnalysisTestCaseResult) {\n printTestCaseResult({ testCaseId: testCases[0]?.id ?? 'prebuild', ...staticAnalysisTestCaseResult });\n return;\n }\n\n const originalMainFilePath = await findEntryPointFile(args.cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'prebuild',\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return;\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'prebuild',\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return;\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let prebuiltMainFilePath: string | undefined;\n\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(args.cwd);\n prebuiltMainFilePath = await findEntryPointFile(args.cwd, params.language);\n } catch (error) {\n console.error('prebuild error', error);\n\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'prebuild',\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return;\n }\n }\n\n const mainFilePath = prebuiltMainFilePath ?? originalMainFilePath;\n\n if (languageDefinition.buildCommand) {\n const buildCommand = languageDefinition.buildCommand(mainFilePath);\n\n // The build reports the submission's failures in its result; what `spawnWithTimeout` throws is\n // the judge's own failure and ends the run without a verdict.\n const buildSpawnResult = await spawnWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd: args.cwd, env },\n BUILD_TIMEOUT_SECONDS\n );\n\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n\n if (buildSpawnResult.status !== 0) {\n decisionCode = DecisionCode.BUILD_ERROR;\n } else if (buildSpawnResult.timeSeconds > BUILD_TIMEOUT_SECONDS) {\n decisionCode = DecisionCode.BUILD_TIME_LIMIT_EXCEEDED;\n } else if (\n buildSpawnResult.stdout.length > MAX_STDOUT_LENGTH ||\n buildSpawnResult.stderr.length > MAX_STDOUT_LENGTH\n ) {\n decisionCode = DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED;\n }\n\n if (decisionCode !== DecisionCode.ACCEPTED) {\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'build',\n decisionCode,\n exitStatus: buildSpawnResult.status ?? undefined,\n stdout: buildSpawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: buildSpawnResult.stderr.slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: buildSpawnResult.timeSeconds,\n cpuTimeSeconds: buildSpawnResult.cpuTimeSeconds,\n memoryBytes: buildSpawnResult.memoryBytes,\n });\n return;\n }\n }\n\n const cwdSnapshot = await snapshotWorkingDirectory(args.cwd);\n\n if (testCases.length === 0) {\n printTestCaseResult({ testCaseId: 'default', decisionCode: DecisionCode.ACCEPTED });\n }\n\n for (const testCase of testCases) {\n // prepare test case\n if (testCases.shared?.fileInputPath) await copyTestCaseFileInput(testCases.shared.fileInputPath, args.cwd);\n if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, args.cwd);\n\n // run\n const timeoutSeconds =\n typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n ? problemMarkdownFrontMatter.timeLimitMs / 1000\n : JUDGE_DEFAULT_TIMEOUT_SECONDS;\n\n const command = languageDefinition.command(mainFilePath);\n\n const spawnResult = await spawnWithTimeout(\n command[0],\n command.slice(1),\n { cwd: args.cwd, stdin: testCase.input, env },\n timeoutSeconds\n );\n\n let outputFiles = await readOutputFiles(args.cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n\n // calculate decision\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n let judgementError: string | undefined;\n\n if (spawnResult.status !== 0) {\n decisionCode = DecisionCode.RUNTIME_ERROR;\n } else if (spawnResult.timeSeconds > timeoutSeconds) {\n decisionCode = DecisionCode.TIME_LIMIT_EXCEEDED;\n } else if (spawnResult.memoryBytes > (problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)) {\n decisionCode = DecisionCode.MEMORY_LIMIT_EXCEEDED;\n } else if (spawnResult.stdout.length > MAX_STDOUT_LENGTH || spawnResult.stderr.length > MAX_STDOUT_LENGTH) {\n decisionCode = DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED;\n } else if (outputFiles.length < (problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0)) {\n decisionCode = DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR;\n } else {\n try {\n const judgement = await judgeAgainstExpectations({\n stdout: spawnResult.stdout,\n expectedStdout: testCase.output,\n fileOutputPath: testCase.fileOutputPath,\n cwd: args.cwd,\n outputFiles,\n });\n if (!judgement.matches) decisionCode = DecisionCode.WRONG_ANSWER;\n outputFiles = judgement.outputFiles;\n } catch (error) {\n // An authoring error (e.g. an oversized expected file) is reported per case, like the command preset does.\n decisionCode = DecisionCode.RUNTIME_ERROR;\n judgementError = error instanceof Error ? error.message : String(error);\n }\n }\n\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode,\n exitStatus: spawnResult.status ?? undefined,\n stdin: testCase.input,\n stdout: spawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: (judgementError ?? spawnResult.stderr).slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n\n // clean up\n await cleanWorkingDirectory(args.cwd, cwdSnapshot);\n\n if (decisionCode !== DecisionCode.ACCEPTED) break;\n }\n}\n\nasync function hasExpectedFiles(fileOutputPath: string | undefined): Promise<boolean> {\n if (fileOutputPath === undefined) return false;\n const dirents = await fs.promises.readdir(fileOutputPath, { withFileTypes: true, recursive: true });\n return dirents.some((dirent) => dirent.isFile());\n}\n\n/**\n * A preset debug function using stdin and stdout as test cases. The answer directory is copied to a\n * temporary directory where it is built and run together with `_shared.fin/` and the first example\n * case's `.fin/`; files the program writes are reported only through `requiredOutputFilePaths`.\n *\n * A standard stdio problem must NOT commit a `debug.ts` that only calls this preset: the Exercode\n * server applies this preset automatically when `debug.ts` is absent, and committed copies would\n * drift from the server's defaults.\n *\n * @example\n * Run in a problem directory without `debug.ts`:\n * ```bash\n * bun x exercode-problem debug model_answers/java '{ \"stdin\": \"1 2\" }'\n * ```\n */\nexport async function stdioDebugPreset(problemDir: string): Promise<void> {\n const args = parseArgs(process.argv);\n if (!args.cwd) throw new Error('cwd argument required');\n const params = debugParamsSchema.parse(args.params);\n\n // Everything (build, input files, run) happens in a disposable copy of the answer directory, so\n // the developer's files are never touched and whatever the submission leaves behind goes with it.\n // The copy keeps the directory hierarchy and links every ancestor `node_modules`, so packages the\n // answer resolves from its parents still resolve.\n // A terminated debug run must not leave the copy behind; `finally` does not run on a signal, so\n // the handlers learn the root as soon as it is created and stay until it is removed.\n let tempRoot: string | undefined;\n const removeOnSignal = (): void => {\n if (tempRoot !== undefined) forciblyRemoveDirectorySync(tempRoot);\n process.exit(1);\n };\n process.once('SIGINT', removeOnSignal);\n process.once('SIGTERM', removeOnSignal);\n try {\n const copy = await copyProblemDirToTemporaryRoot(args.cwd, { onTempRootCreated: (root) => (tempRoot = root) });\n await debugInTemporaryCopy(problemDir, copy.copiedProblemDir, params);\n } finally {\n if (tempRoot !== undefined) await forciblyRemoveDirectory(tempRoot);\n process.off('SIGINT', removeOnSignal);\n process.off('SIGTERM', removeOnSignal);\n }\n}\n\nasync function debugInTemporaryCopy(problemDir: string, cwd: string, params: DebugParams): Promise<void> {\n const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n\n const originalMainFilePath = await findEntryPointFile(cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: 'prebuild',\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return;\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: 'prebuild',\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return;\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let prebuiltMainFilePath: string | undefined;\n\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(cwd);\n prebuiltMainFilePath = await findEntryPointFile(cwd, params.language);\n } catch (error) {\n console.error('prebuild error', error);\n\n printTestCaseResult({\n testCaseId: 'prebuild',\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return;\n }\n }\n\n const mainFilePath = prebuiltMainFilePath ?? originalMainFilePath;\n\n if (languageDefinition.buildCommand) {\n const buildCommand = languageDefinition.buildCommand(mainFilePath);\n\n // The build reports the submission's failures in its result; what `spawnWithTimeout` throws is\n // the judge's own failure and ends the run without a verdict.\n const buildSpawnResult = await spawnWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd, env },\n BUILD_TIMEOUT_SECONDS\n );\n\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n\n if (buildSpawnResult.status !== 0) {\n decisionCode = DecisionCode.BUILD_ERROR;\n } else if (buildSpawnResult.timeSeconds > BUILD_TIMEOUT_SECONDS) {\n decisionCode = DecisionCode.BUILD_TIME_LIMIT_EXCEEDED;\n } else if (\n buildSpawnResult.stdout.length > MAX_STDOUT_LENGTH ||\n buildSpawnResult.stderr.length > MAX_STDOUT_LENGTH\n ) {\n decisionCode = DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED;\n }\n\n if (decisionCode !== DecisionCode.ACCEPTED) {\n printTestCaseResult({\n testCaseId: 'build',\n decisionCode,\n exitStatus: buildSpawnResult.status ?? undefined,\n stdout: buildSpawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: buildSpawnResult.stderr.slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: buildSpawnResult.timeSeconds,\n cpuTimeSeconds: buildSpawnResult.cpuTimeSeconds,\n memoryBytes: buildSpawnResult.memoryBytes,\n });\n return;\n }\n }\n\n // A debug run has no test case of its own: it gets the shared input files and the `.fin/` of the\n // first example case (the cases a learner may see), placed the way the judge does. Hidden cases'\n // input files are never handed to learner code.\n const testCases = await readTestCases(path.join(problemDir, 'test_cases'));\n if (testCases.shared?.fileInputPath) await copyTestCaseFileInput(testCases.shared.fileInputPath, cwd);\n const exampleFileInputPath = testCases.find(\n (testCase) => EXAMPLE_TEST_CASE_ID_PATTERN.test(testCase.id) && testCase.fileInputPath\n )?.fileInputPath;\n if (exampleFileInputPath) {\n await copyTestCaseFileInput(exampleFileInputPath, cwd);\n } else if (testCases.some((testCase) => testCase.fileInputPath)) {\n console.error('debug: no example test case has input files, so the program runs without them');\n }\n\n const timeoutSeconds = Math.max(DEBUG_DEFAULT_TIMEOUT_SECONDS, (problemMarkdownFrontMatter.timeLimitMs ?? 0) / 1000);\n\n const command = languageDefinition.command(mainFilePath);\n\n const spawnResult = await spawnWithTimeout(\n command[0],\n command.slice(1),\n { cwd, stdin: params.stdin, env },\n timeoutSeconds\n );\n\n const outputFiles = await readOutputFiles(cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n\n if (spawnResult.status !== 0) {\n decisionCode = DecisionCode.RUNTIME_ERROR;\n } else if (spawnResult.timeSeconds > timeoutSeconds) {\n decisionCode = DecisionCode.TIME_LIMIT_EXCEEDED;\n } else if (spawnResult.memoryBytes > (problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)) {\n decisionCode = DecisionCode.MEMORY_LIMIT_EXCEEDED;\n } else if (spawnResult.stdout.length > MAX_STDOUT_LENGTH || spawnResult.stderr.length > MAX_STDOUT_LENGTH) {\n decisionCode = DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED;\n } else if (outputFiles.length < (problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0)) {\n decisionCode = DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR;\n }\n\n printTestCaseResult({\n testCaseId: 'debug',\n decisionCode,\n exitStatus: spawnResult.status ?? undefined,\n stdin: params.stdin,\n stdout: spawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: spawnResult.stderr.slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stdio.js","names":[],"sources":["../../src/presets/stdio.ts"],"sourcesContent":["import fs from 'node:fs';\nimport 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 {\n judgesTestCasesWithoutExpectations,\n readProblemMarkdownFrontMatter,\n} from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases } from '../helpers/readTestCases.js';\nimport { spawnWithTimeout } from '../helpers/spawnWithTimeout.js';\nimport { EXAMPLE_TEST_CASE_ID_PATTERN, MAX_STDOUT_LENGTH } from '../helpers/stdioJudgeRules.js';\nimport {\n copyProblemDirToTemporaryRoot,\n forciblyRemoveDirectory,\n forciblyRemoveDirectorySync,\n} from '../helpers/temporaryProblemDirCopy.js';\nimport { DecisionCode } from '../types/decisionCode.js';\n\nconst BUILD_TIMEOUT_SECONDS = 10;\nconst JUDGE_DEFAULT_TIMEOUT_SECONDS = 2;\nconst DEBUG_DEFAULT_TIMEOUT_SECONDS = 10;\n\nconst judgeParamsSchema = z.object({\n language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\nconst debugParamsSchema = judgeParamsSchema.extend({\n stdin: z.string().optional(),\n});\n\ntype DebugParams = z.infer<typeof debugParamsSchema>;\n\n/**\n * A preset judge function using stdin and stdout as test cases.\n *\n * A standard stdio problem must NOT commit a `judge.ts` that only calls this preset: the Exercode\n * server applies this preset automatically when `judge.ts` is absent, and committed copies would\n * drift from the server's defaults.\n *\n * @example\n * Run in a problem directory without `judge.ts`:\n * ```bash\n * bun x exercode-problem judge model_answers/java\n * ```\n *\n * Run with the optional parameters:\n * ```bash\n * bun x exercode-problem judge model_answers/java '{ \"language\": \"javascript\" }'\n * ```\n */\nexport async function stdioJudgePreset(problemDir: string): 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 problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n const testCases = await readTestCases(path.join(problemDir, 'test_cases'));\n\n // Without an expectation, a case would accept any run; only custom harnesses may judge input-only\n // cases. A problem judged by manual scoring or the presence of required output files has an\n // expectation of its own. Checked before the static analysis so that an authoring error surfaces\n // regardless of the submission.\n if (!judgesTestCasesWithoutExpectations(problemMarkdownFrontMatter)) {\n for (const testCase of testCases) {\n if (testCase.output === undefined && !(await hasExpectedFiles(testCase.fileOutputPath))) {\n throw new Error(\n `test case ${testCase.id} needs an expected output (${testCase.id}.out or a non-empty ${testCase.id}.fout/)`\n );\n }\n }\n }\n\n const staticAnalysisTestCaseResult = await judgeByStaticAnalysis(args.cwd, problemMarkdownFrontMatter);\n if (staticAnalysisTestCaseResult) {\n printTestCaseResult({ testCaseId: testCases[0]?.id ?? 'prebuild', ...staticAnalysisTestCaseResult });\n return;\n }\n\n const originalMainFilePath = await findEntryPointFile(args.cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'prebuild',\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return;\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'prebuild',\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return;\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let prebuiltMainFilePath: string | undefined;\n\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(args.cwd);\n prebuiltMainFilePath = await findEntryPointFile(args.cwd, params.language);\n } catch (error) {\n console.error('prebuild error', error);\n\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'prebuild',\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return;\n }\n }\n\n const mainFilePath = prebuiltMainFilePath ?? originalMainFilePath;\n\n if (languageDefinition.buildCommand) {\n const buildCommand = languageDefinition.buildCommand(mainFilePath);\n\n // The build reports the submission's failures in its result; what `spawnWithTimeout` throws is\n // the judge's own failure and ends the run without a verdict.\n const buildSpawnResult = await spawnWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd: args.cwd, env },\n BUILD_TIMEOUT_SECONDS\n );\n\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n\n if (buildSpawnResult.status !== 0) {\n decisionCode = DecisionCode.BUILD_ERROR;\n } else if (buildSpawnResult.timeSeconds > BUILD_TIMEOUT_SECONDS) {\n decisionCode = DecisionCode.BUILD_TIME_LIMIT_EXCEEDED;\n } else if (\n buildSpawnResult.stdout.length > MAX_STDOUT_LENGTH ||\n buildSpawnResult.stderr.length > MAX_STDOUT_LENGTH\n ) {\n decisionCode = DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED;\n }\n\n if (decisionCode !== DecisionCode.ACCEPTED) {\n printTestCaseResult({\n testCaseId: testCases[0]?.id ?? 'build',\n decisionCode,\n exitStatus: buildSpawnResult.status ?? undefined,\n stdout: buildSpawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: buildSpawnResult.stderr.slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: buildSpawnResult.timeSeconds,\n cpuTimeSeconds: buildSpawnResult.cpuTimeSeconds,\n memoryBytes: buildSpawnResult.memoryBytes,\n });\n return;\n }\n }\n\n const cwdSnapshot = await snapshotWorkingDirectory(args.cwd);\n\n if (testCases.length === 0) {\n printTestCaseResult({ testCaseId: 'default', decisionCode: DecisionCode.ACCEPTED });\n }\n\n for (const testCase of testCases) {\n // prepare test case\n if (testCases.shared?.fileInputPath) await copyTestCaseFileInput(testCases.shared.fileInputPath, args.cwd);\n if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, args.cwd);\n\n // run\n const timeoutSeconds =\n typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n ? problemMarkdownFrontMatter.timeLimitMs / 1000\n : JUDGE_DEFAULT_TIMEOUT_SECONDS;\n\n const command = languageDefinition.command(mainFilePath);\n\n const spawnResult = await spawnWithTimeout(\n command[0],\n command.slice(1),\n { cwd: args.cwd, stdin: testCase.input, env },\n timeoutSeconds\n );\n\n let outputFiles = await readOutputFiles(args.cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n\n // calculate decision\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n let judgementError: string | undefined;\n\n if (spawnResult.status !== 0) {\n decisionCode = DecisionCode.RUNTIME_ERROR;\n } else if (spawnResult.timeSeconds > timeoutSeconds) {\n decisionCode = DecisionCode.TIME_LIMIT_EXCEEDED;\n } else if (spawnResult.memoryBytes > (problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)) {\n decisionCode = DecisionCode.MEMORY_LIMIT_EXCEEDED;\n } else if (spawnResult.stdout.length > MAX_STDOUT_LENGTH || spawnResult.stderr.length > MAX_STDOUT_LENGTH) {\n decisionCode = DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED;\n } else if (outputFiles.length < (problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0)) {\n decisionCode = DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR;\n } else {\n try {\n const judgement = await judgeAgainstExpectations({\n stdout: spawnResult.stdout,\n expectedStdout: testCase.output,\n fileOutputPath: testCase.fileOutputPath,\n cwd: args.cwd,\n outputFiles,\n });\n if (!judgement.matches) decisionCode = DecisionCode.WRONG_ANSWER;\n outputFiles = judgement.outputFiles;\n } catch (error) {\n // An authoring error (e.g. an oversized expected file) is reported per case, like the command preset does.\n decisionCode = DecisionCode.RUNTIME_ERROR;\n judgementError = error instanceof Error ? error.message : String(error);\n }\n }\n\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode,\n exitStatus: spawnResult.status ?? undefined,\n stdin: testCase.input,\n stdout: spawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: (judgementError ?? spawnResult.stderr).slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n\n // clean up\n await cleanWorkingDirectory(args.cwd, cwdSnapshot);\n\n if (decisionCode !== DecisionCode.ACCEPTED) break;\n }\n}\n\nasync function hasExpectedFiles(fileOutputPath: string | undefined): Promise<boolean> {\n if (fileOutputPath === undefined) return false;\n const dirents = await fs.promises.readdir(fileOutputPath, { withFileTypes: true, recursive: true });\n return dirents.some((dirent) => dirent.isFile());\n}\n\n/**\n * A preset debug function using stdin and stdout as test cases. The answer directory is copied to a\n * temporary directory where it is built and run together with `_shared.fin/` and the first example\n * case's `.fin/`; files the program writes are reported only through `requiredOutputFilePaths`.\n * Callers that own and remove the working directory can set `disposableWorkingDirectory` to run\n * directly there; they are responsible for cleanup even when the harness is terminated.\n *\n * A standard stdio problem must NOT commit a `debug.ts` that only calls this preset: the Exercode\n * server applies this preset automatically when `debug.ts` is absent, and committed copies would\n * drift from the server's defaults.\n *\n * @example\n * Run in a problem directory without `debug.ts`:\n * ```bash\n * bun x exercode-problem debug model_answers/java '{ \"stdin\": \"1 2\" }'\n * ```\n */\nexport async function stdioDebugPreset(\n problemDir: string,\n options: { disposableWorkingDirectory?: boolean } = {}\n): Promise<void> {\n const args = parseArgs(process.argv);\n if (!args.cwd) throw new Error('cwd argument required');\n const params = debugParamsSchema.parse(args.params);\n\n // The judge server already owns and removes its per-request answer directory.\n if (options.disposableWorkingDirectory) {\n await debugInWorkingDirectory(problemDir, args.cwd, params);\n return;\n }\n\n // Everything (build, input files, run) happens in a disposable copy of the answer directory, so\n // the developer's files are never touched and whatever the submission leaves behind goes with it.\n // The copy keeps the directory hierarchy and links every ancestor `node_modules`, so packages the\n // answer resolves from its parents still resolve.\n // A terminated debug run must not leave the copy behind; `finally` does not run on a signal, so\n // the handlers learn the root as soon as it is created and stay until it is removed.\n let tempRoot: string | undefined;\n const removeOnSignal = (): void => {\n if (tempRoot !== undefined) forciblyRemoveDirectorySync(tempRoot);\n process.exit(1);\n };\n process.once('SIGINT', removeOnSignal);\n process.once('SIGTERM', removeOnSignal);\n try {\n const copy = await copyProblemDirToTemporaryRoot(args.cwd, { onTempRootCreated: (root) => (tempRoot = root) });\n await debugInWorkingDirectory(problemDir, copy.copiedProblemDir, params);\n } finally {\n if (tempRoot !== undefined) await forciblyRemoveDirectory(tempRoot);\n process.off('SIGINT', removeOnSignal);\n process.off('SIGTERM', removeOnSignal);\n }\n}\n\nasync function debugInWorkingDirectory(problemDir: string, cwd: string, params: DebugParams): Promise<void> {\n const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n\n const originalMainFilePath = await findEntryPointFile(cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: 'prebuild',\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return;\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: 'prebuild',\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return;\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let prebuiltMainFilePath: string | undefined;\n\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(cwd);\n prebuiltMainFilePath = await findEntryPointFile(cwd, params.language);\n } catch (error) {\n console.error('prebuild error', error);\n\n printTestCaseResult({\n testCaseId: 'prebuild',\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return;\n }\n }\n\n const mainFilePath = prebuiltMainFilePath ?? originalMainFilePath;\n\n if (languageDefinition.buildCommand) {\n const buildCommand = languageDefinition.buildCommand(mainFilePath);\n\n // The build reports the submission's failures in its result; what `spawnWithTimeout` throws is\n // the judge's own failure and ends the run without a verdict.\n const buildSpawnResult = await spawnWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd, env },\n BUILD_TIMEOUT_SECONDS\n );\n\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n\n if (buildSpawnResult.status !== 0) {\n decisionCode = DecisionCode.BUILD_ERROR;\n } else if (buildSpawnResult.timeSeconds > BUILD_TIMEOUT_SECONDS) {\n decisionCode = DecisionCode.BUILD_TIME_LIMIT_EXCEEDED;\n } else if (\n buildSpawnResult.stdout.length > MAX_STDOUT_LENGTH ||\n buildSpawnResult.stderr.length > MAX_STDOUT_LENGTH\n ) {\n decisionCode = DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED;\n }\n\n if (decisionCode !== DecisionCode.ACCEPTED) {\n printTestCaseResult({\n testCaseId: 'build',\n decisionCode,\n exitStatus: buildSpawnResult.status ?? undefined,\n stdout: buildSpawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: buildSpawnResult.stderr.slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: buildSpawnResult.timeSeconds,\n cpuTimeSeconds: buildSpawnResult.cpuTimeSeconds,\n memoryBytes: buildSpawnResult.memoryBytes,\n });\n return;\n }\n }\n\n // A debug run has no test case of its own: it gets the shared input files and the `.fin/` of the\n // first example case (the cases a learner may see), placed the way the judge does. Hidden cases'\n // input files are never handed to learner code.\n const testCases = await readTestCases(path.join(problemDir, 'test_cases'));\n if (testCases.shared?.fileInputPath) await copyTestCaseFileInput(testCases.shared.fileInputPath, cwd);\n const exampleFileInputPath = testCases.find(\n (testCase) => EXAMPLE_TEST_CASE_ID_PATTERN.test(testCase.id) && testCase.fileInputPath\n )?.fileInputPath;\n if (exampleFileInputPath) {\n await copyTestCaseFileInput(exampleFileInputPath, cwd);\n } else if (testCases.some((testCase) => testCase.fileInputPath)) {\n console.error('debug: no example test case has input files, so the program runs without them');\n }\n\n const timeoutSeconds = Math.max(DEBUG_DEFAULT_TIMEOUT_SECONDS, (problemMarkdownFrontMatter.timeLimitMs ?? 0) / 1000);\n\n const command = languageDefinition.command(mainFilePath);\n\n const spawnResult = await spawnWithTimeout(\n command[0],\n command.slice(1),\n { cwd, stdin: params.stdin, env },\n timeoutSeconds\n );\n\n const outputFiles = await readOutputFiles(cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n\n let decisionCode: DecisionCode = DecisionCode.ACCEPTED;\n\n if (spawnResult.status !== 0) {\n decisionCode = DecisionCode.RUNTIME_ERROR;\n } else if (spawnResult.timeSeconds > timeoutSeconds) {\n decisionCode = DecisionCode.TIME_LIMIT_EXCEEDED;\n } else if (spawnResult.memoryBytes > (problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)) {\n decisionCode = DecisionCode.MEMORY_LIMIT_EXCEEDED;\n } else if (spawnResult.stdout.length > MAX_STDOUT_LENGTH || spawnResult.stderr.length > MAX_STDOUT_LENGTH) {\n decisionCode = DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED;\n } else if (outputFiles.length < (problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0)) {\n decisionCode = DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR;\n }\n\n printTestCaseResult({\n testCaseId: 'debug',\n decisionCode,\n exitStatus: spawnResult.status ?? undefined,\n stdin: params.stdin,\n stdout: spawnResult.stdout.slice(0, MAX_STDOUT_LENGTH) || undefined,\n stderr: spawnResult.stderr.slice(0, MAX_STDOUT_LENGTH) || undefined,\n timeSeconds: spawnResult.timeSeconds,\n cpuTimeSeconds: spawnResult.cpuTimeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n}\n"],"mappings":"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"}
|