The workbench's server API lives under web/app/api/. Every route runs on the
Node runtime (runtime = "nodejs") and is dynamic (dynamic = "force-dynamic").
Field names below are transcribed from each route's source and its top-of-file
contract comment; the typed client in web/lib/api.ts mirrors them.
All request bodies are JSON objects. On a bad request a route replies with a
non-2xx status and a body of { "error": string }; malformed JSON is a 400.
| Route | Method(s) | Purpose |
|---|---|---|
/api/run |
POST | Compile a source and run it once. |
/api/test |
POST | Compile once, run against many cases, return AC/WA/TLE/RE/CE. |
/api/stress |
POST | Compare a solution to a brute force on generated inputs. |
/api/samples |
POST | Parse a pasted statement into sample tests and limits. |
/api/problems |
GET / POST / DELETE | CRUD, duplicate, export and import for saved problems. |
/api/config |
GET / DELETE | Toolchain facts and limits; clear the compile cache. |
/api/template |
GET / POST | List / fetch the bundled starter templates. |
/api/problem-text |
GET / POST | Load / save a problem statement (problem.txt) in src/. |
/api/solution |
GET / POST | Load / save a solution (solution.cpp) in src/. |
/api/run, /api/test and /api/stress accept the same option fields:
| Field | Type | Default | Notes |
|---|---|---|---|
std |
string | gnu++17 |
Validated; unknown standards fall back to the default. |
timeLimitMs |
number | 5000 |
Clamped to [100, 60000]. |
compilerFlags |
string[] | [] |
Validated against an allowlist; rejected flags are reported. |
checker |
string | lines |
lines, tokens or float (see architecture.md). |
epsilon |
number | 1e-6 |
Float checker tolerance, clamped to [0, 1]. |
Every compile reports the same compile object:
{
"ok": true,
"stderr": "",
"ms": 912,
"cached": false,
"diagnostics": [
{
"severity": "error",
"line": 5,
"column": 9,
"message": "'x' was not declared in this scope"
}
],
"rejectedFlags": [{ "flag": "-o", "reason": "flag family is not allowed" }]
}ms is 0 and cached is true when the binary was served from the compile
cache. diagnostics[].line is null for diagnostics that point at another
file (for example the bundled shim). Sources larger than 1 MiB are refused
with error: "source-too-large"; compilation is killed after 30 s.
Compile a single C++ source and run it once.
Request:
{
"code": "#include <bits/stdc++.h> ...",
"input": "2 3\n",
"timeLimitMs": 2000,
"std": "gnu++17",
"compilerFlags": ["-Wall"]
}stdin is accepted as an alias for input. code is required.
Response (200):
{
"ok": true,
"verdict": "OK",
"stdout": "5\n",
"stderr": "",
"exitCode": 0,
"signal": null,
"timedOut": false,
"timeMs": 3.2,
"truncated": false,
"compiler": "clang++",
"compile": {
"ok": true,
"stderr": "",
"ms": 912,
"cached": false,
"diagnostics": [],
"rejectedFlags": []
}
}verdict is OK, CE (compile failed; compile.stderr holds the text),
RE (non-zero exit, signal or spawn failure) or TLE (killed at the time
limit). timeMs is wall-clock time measured in Node. truncated is set when
stdout or stderr hit the 4 MiB cap.
Compile once and run against many cases.
Request:
{
"code": "...",
"tests": [{ "input": "2 3\n", "expected": "5\n" }],
"checker": "tokens",
"stopOnFirstFailure": false
}code and a non-empty tests array (at most 200 cases) are required.
Response (200):
{
"ok": true,
"compiler": "clang++",
"compile": { "...": "..." },
"checker": "tokens",
"summary": {
"total": 2,
"passed": 2,
"failed": 0,
"skipped": 0,
"verdict": "AC",
"maxTimeMs": 4.1,
"totalTimeMs": 7.9
},
"results": [
{
"index": 0,
"verdict": "AC",
"input": "2 3\n",
"expected": "5\n",
"actual": "5\n",
"stderr": "",
"exitCode": 0,
"signal": null,
"timeMs": 3.8,
"truncated": false,
"presentationOnly": false,
"diff": []
}
]
}Per-case verdict is AC, WA, TLE, RE, CE (every case, when the
compile fails) or SKIPPED (after a failure with stopOnFirstFailure).
summary.verdict is the worst verdict in CE > RE > TLE > WA > AC order.
diff lists only the differing lines (capped at 200) as
{ line, expected, actual, same }, with null for a missing side.
presentationOnly is true for a WA under the lines checker whose tokens
match.
Compile three sources and search for a counter-example.
Request:
{
"solution": "...",
"brute": "...",
"generator": "...",
"iterations": 200,
"seedBase": 1,
"timeLimitMs": 2000,
"checker": "lines"
}All three sources are required. iterations is clamped to [1, 5000]; the
generator receives seedBase + i as argv[1].
Response (200):
{
"ok": true,
"failed": true,
"iterationsRun": 7,
"elapsedMs": 412,
"deadlineHit": false,
"stats": { "maxSolutionMs": 5.2, "maxBruteMs": 4.9, "avgSolutionMs": 3.1 },
"compile": {
"solution": {
"ok": true,
"stderr": "",
"ms": 900,
"cached": false,
"diagnostics": []
},
"brute": { "...": "..." },
"generator": { "...": "..." }
},
"firstFailure": {
"iteration": 7,
"seed": 7,
"reason": "mismatch",
"input": "615 892\n",
"solutionOutput": "1506\n",
"bruteOutput": "1507\n",
"diff": [{ "line": 1, "expected": "1507", "actual": "1506", "same": false }]
},
"message": "Found a counter-example on iteration 7."
}ok means the sources compiled and the search ran; failed means a
counter-example was found. reason is one of mismatch, solution-error,
brute-error, solution-tle, brute-tle, generator-error; the error
variants also carry solutionStderr, bruteStderr or generatorStderr. A
compile failure returns ok: false with the offending compile.*.stderr
populated. The request is bounded by a 60 s budget; deadlineHit says when
that cut the search short.
Parse a pasted statement.
Request: { "statement": "A. Team\ntime limit per test\n2 seconds\n...Examples\nInput\n...\nOutput\n..." }
Response (200):
{
"title": "A. Team",
"timeLimitMs": 2000,
"memoryLimitMb": 256,
"tests": [{ "input": "3\n1 1 0\n1 1 1\n1 0 0\n", "expected": "2\n" }]
}Headings are matched case-insensitively (Examples, Example, Sample 1,
Input, Output, Sample Input N, Sample Output N, inputCopy); a
Note heading ends the examples. A statement without samples yields
tests: [].
Saved problems are JSON files under web/data/problems/<slug>.json (or
$CF_DATA_DIR/problems). slug is derived from name (lowercase,
non-alphanumerics become -) and is the stable key; saving with the same
name updates the record. Writes are atomic.
Types:
TestCase = { input: string; expected: string }
ProblemSettings= { std?, timeLimitMs?, compilerFlags?, checker?, epsilon? }
Problem = { name, slug, code, statement, tests: TestCase[], stdin,
brute, generator, settings: ProblemSettings | null,
createdAt, updatedAt }
ProblemSummary = { name, slug, testCount, updatedAt }| Request | Response |
|---|---|
GET /api/problems |
{ problems: ProblemSummary[] } newest first |
GET /api/problems?name=<name or slug> |
{ problem: Problem } or 404 |
GET /api/problems?export=1 |
{ version: 1, exportedAt, problems: Problem[] } |
POST /api/problems { name, code?, statement?, tests?, stdin?, brute?, generator?, settings? } |
{ ok, problem } (upsert; omitted fields keep their stored value) |
POST /api/problems { op: "rename", from, to } |
{ ok, problem } or 404 |
POST /api/problems { op: "duplicate", from, to } |
{ ok, problem } or 404 |
POST /api/problems { op: "import", problems: Problem[], overwrite? } |
{ ok, imported, skipped } |
DELETE /api/problems?name=<name or slug> |
{ ok: true, deleted: boolean } |
Names are limited to 120 characters. Import skips problems that already exist
unless overwrite is true, and tolerates records written by older versions.
GET /api/config returns facts the UI shows in Settings:
{
"version": "1.0.0",
"platform": "darwin",
"compiler": "clang++",
"compilerVersion": "Apple clang version 16.0.0 (clang-1600.0.26.4)",
"includeDir": "/path/to/cf/include",
"defaults": {
"std": "gnu++17",
"timeLimitMs": 5000,
"maxTimeLimitMs": 60000,
"checker": "lines"
},
"limits": {
"maxSourceBytes": 1048576,
"maxInputBytes": 4194304,
"maxOutputBytes": 4194304,
"compileTimeoutMs": 30000
},
"cache": { "entries": 3 },
"startProblem": null,
"problemsDir": null
}startProblem and problemsDir echo CF_START_PROBLEM / CF_PROBLEMS_DIR
as set by cf serve. DELETE /api/config?cache=1 drops every cached binary
and returns { ok: true, cleared: <count> }.
GET /api/templatereturns{ "templates": ["dp", "graph", "math"] }, the.cppfiles under the repositorytemplates/directory.POST /api/templatewith{ "name": "dp" }returns{ stdout, stderr, exitCode, content };stdoutandcontentboth hold the template text (stdoutis kept for backward compatibility). A miss returnsexitCode: 1and an error instderr.
These read and write problem.txt / solution.cpp under src/<problem>/
(or $CF_PROBLEMS_DIR/<problem>/) for CLI interoperability.
GET /api/problem-text?problem=<name>returns{ "text": string }(empty when absent);POST /api/problem-textwith{ "problem", "text" }returns{ "success": true }.GET /api/solution?problem=<name>returns{ "code": string };POST /api/solutionwith{ "problem", "code" }returns{ "success": true }.
problem must be a single path segment of letters, digits, _, - and .
that does not start with a dot; anything else (including .. and path
separators) is rejected with 400. Bodies are capped at 1 MiB.