Polyman Documentation - v2.3.3
    Preparing search index...

    Polyman Documentation - v2.3.3

    Complete Technical Reference for Polyman CLI Tool

    A TypeScript-based CLI tool for Codeforces problem setters that automates problem preparation workflows including test generation, validation, solution verification, and checker integration.


    1. Architecture Overview
    2. Core Components
    3. Type System
    4. Action Layer
    5. Helper Modules
    6. Execution Engine
    7. Formatter System
    8. Configuration Schema
    9. Compilation Pipeline
    10. Validation System
    11. Solution Testing
    12. Checker Integration
    13. Generator System
    14. Error Handling
    15. Polygon Integration
    16. File Structure
    17. Development Guide
    18. API Reference
    19. Implementation Notes

    Polyman follows a layered architecture:

    ┌─────────────────────────────────────┐
    CLI Layer (cli.ts) │
    Commander.js command parsing
    └─────────────────────────────────────┘

    ┌─────────────────────────────────────┐
    Action Layer (actions.ts) │
    High-level workflow orchestration
    └─────────────────────────────────────┘

    ┌─────────────────────────────────────┐
    Helper Layer (helpers/) │
    Domain-specific logic modules
    └─────────────────────────────────────┘

    ┌─────────────────────────────────────┐
    Execution Engine (executor.ts) │
    Process management & I/O
    └─────────────────────────────────────┘

    ┌─────────────────────────────────────┐
    Formatter (formatter.ts) │
    Terminal output styling
    └─────────────────────────────────────┘

    The CLI entry point is src/cli.ts. It uses Commander.js to parse argv and delegate to action functions in src/actions.ts. See GUIDE.md - CLI Commands Reference for user-facing command descriptions, or read src/cli.ts for the wiring.


    All TypeScript types live in src/types.d.ts. The key shapes are ConfigFile (the on-disk Config.json schema), LocalSolution, LocalGenerator, LocalChecker, LocalValidator, LocalTestset, plus SolutionTag and the source-type unions for C++/Java/Python. See src/types.d.ts for the full definitions and JSDoc, or GUIDE.md - Configuration Reference for usage.


    Action functions in src/actions.ts orchestrate workflows by composing helpers. Each action is structured as a sequence of independent step… calls (see src/steps.ts) which compile, run, and verify components. See GUIDE.md - CLI Commands Reference for what each action does from the user perspective.


    Each helper module owns a single domain. Function-level documentation is on TypeDoc; this section lists the responsibility of each module so contributors know where to look.

    • src/helpers/utils.ts — Cross-cutting utilities: config file reading, directory management, C++ and Java compilation, error logging, and the shared exit/throw helpers used by every action.
    • src/helpers/generator.ts — Generator compilation and test file production. Drives Polygon-format scripts: parses, resolves to concrete test indices, then runs each generator (or copies each manual test) into the testset's output directory.
    • src/helpers/validator.ts — Validator compilation, validation of generated test inputs, and validator self-testing against validator_tests.json.
    • src/helpers/checker.ts — Checker compilation (custom or standard from assets/checkers/), checker invocation, and checker self-testing. Owns the verdict mapping from checker stdout to CheckerVerdict.
    • src/helpers/solution.ts — Solution compilation, execution against a testset, and verdict comparison. Responsible for enforcing the solution-tag contract (e.g. WA solutions must produce WA on at least one test).
    • src/helpers/create-template.ts — Materialises the bundled template/ tree into a new problem directory when polyman new runs.
    • src/helpers/testlib-download.ts — Fetches testlib.h from the upstream GitHub repo for the polyman download-testlib command.
    • src/helpers/testset.ts — Testset and group resolution: given filters from CLI options, returns the matching testsets, groups, and indices.
    • src/helpers/script-parser.ts — Parses Polygon-format generation scripts (gen [args] > N|$|{indices}, FreeMarker comments and <#list> loops, <#-- @group X --> headers) and resolves them, together with manualTests[], into a flat list of test-production instructions with concrete indices.
    • src/helpers/remote/ — Polygon integration. pulling.ts and pushing.ts implement the per-component download/upload steps; utils.ts handles credentials, line-ending normalization, and problem-id extraction; viewer.ts formats polyman remote view output.

    For function-level docs see TypeDoc at https://hamzahassanain.github.io/polyman/.


    Low-level process execution with timeout and memory limit support.

    Executes command with resource limits.

    Parameters:

    • command: Executable path or command
    • args: Command-line arguments
    • timeoutMs: Maximum execution time
    • memoryLimitMB: Maximum memory usage

    Returns:

    interface ExecutionResult {
    stdout: string;
    stderr: string;
    exitCode: number;
    timedOut: boolean;
    memoryExceeded: boolean;
    }

    Platform Support:

    • Linux: Uses ulimit for memory limiting
    • macOS: Memory limiting not supported (warning shown)
    • Windows: Memory limiting not supported (warning shown)

    Implementation:

    // Linux memory limiting
    if (process.platform === 'linux' && memoryLimitMB > 0) {
    const memoryLimitKB = memoryLimitMB * 1024;
    command = `ulimit -v ${memoryLimitKB} && ${command}`;
    }

    // Spawn process with timeout
    const child = spawn(command, args, { shell: true });
    const timeout = setTimeout(() => {
    child.kill();
    timedOut = true;
    }, timeoutMs);

    Terminal output styling with Codeforces theme.

    Colors:

    • Primary: #1E88E5 (blue)
    • Error: #FF6B6B (red)
    • Success: #4CAF50 (green)
    • Warning: #FFC107 (yellow)

    section(title: string)

    Prints section header with box.

    ╭─────────────────────────────────╮
    │ 📁 SECTION TITLE
    ╰─────────────────────────────────╯

    step(stepNumber: number, title: string)

    Prints numbered step header.

    ╭─ STEP 1: Creating Directory
    

    stepComplete(message: string)

    Prints step completion (╰─ ✓ Done).

    Other methods: success, error, warning, info, log, successBox, errorBox.

    • primary(text: string): Blue color
    • highlight(text: string): Cyan color
    • dim(text: string): Dimmed text
    • successIcon(): ✓ icon
    • errorIcon(): ✗ icon
    • warningIcon(): ⚠ icon
    • infoIcon(): ℹ icon

    The on-disk Config.json schema is described in GUIDE.md - Configuration File Reference for users, and defined as TypeScript interfaces in src/types.d.ts (ConfigFile).


    C++:

    g++ -o output source.cpp
    

    Java:

    javac source.java
    java ClassName < input.txt > output.txt

    Python:

    python3 source.py < input.txt > output.txt
    
    1. Detect language from file extension
    2. Call appropriate compiler function:
      • .cppcompileCPP
      • .javacompileJava
      • .py → Return interpreter command
    3. Handle compilation errors
    4. Return executable path or command

    The validator is a C++ program built against testlib that reads a test input on stdin and exits 0 for VALID or 3 for INVALID (any other code is an error). The validation workflow compiles the validator once and runs it over each test file in the requested testset, logging per-test results. Validator self-tests live in validator/validator_tests.json as {stdin, expectedVerdict} pairs and are exercised by polyman test validator to verify the validator agrees with its own contract before being used on generated tests.


    Each solution is compiled, then run against every test in the active testset under the configured time and memory limits. The runner inspects the first line of the captured output for the sentinel verdicts Time Limit Exceeded, Memory Limit Exceeded, and Runtime Error; otherwise it invokes the checker on the (input, output, answer) triple to obtain OK/WA/PE. Aggregated VerdictTracker flags (didWA, didTLE, didMLE, didRTE) are then matched against the solution's declared SolutionTag so that, e.g., a WA solution must fail at least one test and a MA solution must pass them all.


    Standard checkers ship in assets/checkers/*.cpp (vendored testlib checkers — do not modify). Run polyman list checkers for the runtime list, or see GUIDE.md - Standard Checkers.

    Location: checker/chk.cpp

    Interface:

    int main(int argc, char* argv[]) {
      registerTestlibCmd(argc, argv);
    
      // Read input
      // Read output (ouf)
      // Read answer (ans)
    
      // Compare and return verdict:
      // quitf(_ok, "Correct");
      // quitf(_wa, "Wrong Answer");
      // quitf(_pe, "Presentation Error");
    }
    

    Command:

    ./checker <input_file> <output_file> <answer_file>
    

    Output Parsing:

    • First word determines verdict
    • ok/OK → OK
    • wrong/WA → WA
    • presentation/PE → PE

    Generators are testlib C++ programs that take parameters as positional argv[] and write the test content to stdout. The pipeline is:

    1. Parsescript-parser.ts reads the testset's generatorScript.script (or scriptFile) as Codeforces-Polygon syntax: each non-comment line is gen-name [args...] > target where target is N, $ (smallest unused), or {1-3,7} (multi-output). FreeMarker <#-- comments --> and <#list a..b as i> ... ${i} ... </#list> blocks are supported, and <#-- @group <name> --> headers tag every following line.
    2. Resolvescript-parser.ts:resolveTests merges parsed lines with the testset's manualTests[], assigning concrete Polygon indices to every $. Manual indices and explicit indices reserve slots; $ walks the smallest free integer. Duplicate indices are an error.
    3. Compile + rungenerator.ts:executeResolvedTests compiles each referenced generator once and, per resolved test, either copies the manual .in file into testsets/<name>/test<index>.txt, runs the generator with stdout redirected to that file, or (for > {indices} lines) runs the generator with cwd set to the testset directory and verifies it wrote the promised files itself.

    Generator names with extensions (gen.exe, gen.cpp) are rejected — Polygon strips them on the server side, so polyman keeps the same constraint.


    Polyman provides comprehensive error handling with formatted output and proper exit codes.

    1. Catch Error: In action or helper function
    2. Log Error: Via logError
    3. Handle Error:
      • Exit: logErrorAndExit
      • Throw: logErrorAndThrow

    Configuration Errors:

    • Config.json not found
    • Invalid JSON
    • Missing required fields
    • No main-correct solution

    Compilation Errors:

    • Source file not found
    • Compilation failed
    • Invalid syntax

    Execution Errors:

    • TLE (timeout)
    • MLE (memory exceeded)
    • RTE (runtime error)
    • Exit code ≠ 0

    Validation Errors:

    • Test file not found
    • Validator failed
    • Invalid test input

    Checker Errors:

    • Checker compilation failed
    • Unexpected verdict format
    • Answer file not found

    Polyman provides comprehensive integration with the Codeforces Polygon system through a type-safe TypeScript SDK and remote operations.

    Location: src/polygon.ts

    The Polygon SDK is a complete TypeScript implementation of the Polygon API v1, providing type-safe methods for all Polygon operations.

    class PolygonSDK {
    private apiKey: string;
    private apiSecret: string;
    private baseUrl: string;

    constructor(config: PolygonConfig) {
    this.apiKey = config.apiKey;
    this.apiSecret = config.apiSecret;
    this.baseUrl = config.baseUrl || 'https://polygon.codeforces.com/api';
    }

    // 54+ API methods for complete Polygon integration
    }

    The SDK exposes 50+ methods grouped under Problem Management, Statements, Solutions, Checker & Validator, Generators, Tests, Metadata, Packages, and Contests. See src/polygon.ts for the full method signatures and JSDoc, or TypeDoc for the rendered API.

    Polygon API uses SHA-512 signature-based authentication.

    1. API Credentials:

      {
      apiKey: 'your-api-key',
      apiSecret: 'your-api-secret'
      }
    2. Request Signing:

      // Generate 6-character random prefix
      const rand = generateRandomString(6);

      // Sort parameters alphabetically
      const sortedParams = Object.keys(params).sort();

      // Build signature string
      const sigString = `${rand}/${methodName}?${paramString}#${apiSecret}`;

      // Compute SHA-512 hash
      const hash = crypto.createHash('sha512').update(sigString).digest('hex');

      // Final signature
      const apiSig = rand + hash;
    3. Request Parameters:

      • apiKey - Your API key
      • time - Current Unix timestamp
      • apiSig - Computed signature
      • Method-specific parameters
    4. API Call:

      POST https://polygon.codeforces.com/api/{method}
      Content-Type: application/x-www-form-urlencoded

      apiKey=xxx&time=xxx&apiSig=xxx&problemId=xxx&...
    • API credentials stored in ~/.polyman/credentials.json
    • File permissions set to 600 (owner read/write only)
    • Credentials never logged or displayed
    • Signature includes timestamp to prevent replay attacks
    • Each request has unique random prefix
    User Command

    polyman remote pull 123456 ./my-problem

    remotePullProblemAction

    ┌────────────────────────────────────┐
    Step 1: Read Credentials
    │ - Load from ~/.polyman/
    └────────────────────────────────────┘

    ┌────────────────────────────────────┐
    Step 2: Initialize SDK
    │ - Create PolygonSDK instance
    │ - Configure authentication
    └────────────────────────────────────┘

    ┌────────────────────────────────────┐
    Step 3: Fetch Problem Info
    │ - Get limits, I/O files
    │ - Get problem metadata
    └────────────────────────────────────┘

    ┌────────────────────────────────────┐
    Step 4: Create Directory
    │ - Create problem directory
    │ - Create subdirectories
    └────────────────────────────────────┘

    ┌────────────────────────────────────┐
    Step 5: Download Components
    │ ┌──────────────────────────────┐ │
    │ │ Solutions (parallel) │ │
    │ │ Checker + tests │ │
    │ │ Validator + tests │ │
    │ │ Generators │ │
    │ │ Statements (all languages) │ │
    │ │ Tests (parallel fetch) │ │
    │ │ Metadata │ │
    │ └──────────────────────────────┘ │
    │ - Normalize line endings (→ Unix) │
    └────────────────────────────────────┘

    ┌────────────────────────────────────┐
    Step 6: Generate Config.json
    │ - Build complete configuration
    │ - Include all metadata
    └────────────────────────────────────┘

    Success! Problem ready for local work
    User Command

    polyman remote push . ./my-problem

    remotePushProblemAction

    ┌────────────────────────────────────┐
    Step 1: Read Credentials
    └────────────────────────────────────┘

    ┌────────────────────────────────────┐
    Step 2: Initialize SDK
    └────────────────────────────────────┘

    ┌────────────────────────────────────┐
    Step 3: Read Config.json
    │ - Get problem ID
    │ - Get all configurations
    └────────────────────────────────────┘

    ┌────────────────────────────────────┐
    Step 4: Update Problem Info
    │ - Upload time/memory limits
    │ - Upload I/O file settings
    └────────────────────────────────────┘

    ┌────────────────────────────────────┐
    Step 5-11: Upload Components
    │ ┌──────────────────────────────┐ │
    │ │ Solutions with tags │ │
    │ │ Checker + set active │ │
    │ │ Validator + tests + set │ │
    │ │ Generators │ │
    │ │ Statements (all languages) │ │
    │ │ Metadata │ │
    │ │ Testsets: │ │
    │ │ - Clear existing │ │
    │ │ - Enable groups │ │
    │ │ - Upload tests (parallel) │ │
    │ │ - Upload script │ │
    │ └──────────────────────────────┘ │
    │ - Normalize line endings (→ Win) │
    └────────────────────────────────────┘

    Success! Changes uploaded to Polygon
    (Don't forget to commit!)

    Parallel Test Operations:

    Both pull and push operations fetch/upload manual tests in parallel for significant performance improvements:

    Pull (Parallel Fetch):

    // Fetch test metadata without inputs (fast)
    const tests = await sdk.getTests(problemId, testsetName, true);
    const manualTests = tests.filter(t => t.manual);

    // Fetch all inputs in parallel
    const promises = manualTests.map(test =>
    sdk.getTestInput(problemId, testsetName, test.index)
    );
    const results = await Promise.all(promises);

    Push (Parallel Upload):

    // Create upload promises for all manual tests
    const promises = manualTests.map(test =>
    sdk.saveTest(problemId, testsetName, index, input, options)
    );

    // Upload all in parallel
    await Promise.all(promises);

    Performance Impact:

    • 50 manual tests: ~50 seconds sequential → ~2 seconds parallel
    • 100 manual tests: ~100 seconds sequential → ~3 seconds parallel

    See GUIDE.md - Directory Structure for the canonical layout of a Polyman problem directory and the bundled template tree.


    git clone https://github.com/HamzaHassanain/polyman.git
    cd polyman
    npm install
    npm run build
    npm link

    Entry Points:

    • src/cli.ts - CLI commands
    • src/actions.ts - Action functions

    Core Logic:

    • src/helpers/ - Domain modules
    • src/executor.ts - Process execution
    • src/formatter.ts - Output formatting

    Types:

    • src/types.d.ts - TypeScript type definitions

    Assets:

    • assets/checkers/ - Standard checkers
    • template/ - Problem template

    New Command:

    1. Add action function in src/actions.ts
    2. Register command in src/cli.ts
    3. Document in README and DOCUMENTATION

    New Helper Module:

    1. Create file in src/helpers/
    2. Export functions
    3. Import in src/actions.ts

    New Solution Type:

    1. Add type to src/types.d.ts
    2. Handle in getExpectedCheckerVerdict
    3. Update verdict validation in src/helpers/solution.ts
    # Lint code
    npm run lint

    # Build
    npm run build

    # Manual testing
    polyman new test-problem
    cd test-problem
    # Test commands...

    See eslint.config.js for linting rules.

    Key Rules:

    • TypeScript strict mode
    • Prettier integration
    • No unused variables (except prefixed with _)
    • No explicit any (warning)

    TypeDoc generates the full API reference: https://hamzahassanain.github.io/polyman/. Key entry points: src/cli.ts (commands), src/actions.ts (orchestration), src/polygon.ts (Polygon SDK), src/types.d.ts (types).


    Important considerations for development, deployment, and platform-specific behavior.

    • Parallel Execution: Currently sequential; could parallelize test generation and solution execution
    • Caching: Compiled executables are not cached between runs
    • Memory Limiting: Only supported on Linux via ulimit

    Linux:

    • Full support for memory limiting
    • Recommended platform

    macOS:

    • Memory limiting not supported
    • Warning shown when memory limit specified

    Windows:

    • Memory limiting not supported
    • Requires MinGW or WSL for C++ compilation
    • Command Injection: Uses spawn with shell: true; sanitize user input if accepting external configs
    • File System: Direct file operations; validate paths to prevent directory traversal
    • Parallel test execution
    • Compiled executable caching
    • Windows native memory limiting
    • Progress bars for long operations
    • Config validation schema
    • Interactive mode


    MIT License - See LICENCE file for details.


    This technical documentation is auto-generated alongside TypeDoc API documentation. For the latest version, visit the online documentation.