// Phase 0 · Lesson 01 — Dev Environment verifier (TypeScript port). // Probes node version + presence of git, python3, cargo, deno; mirrors verify.py. // Refs: https://nodejs.org/api/process.html https://nodejs.org/api/child_process.html import { execFileSync } from "node:child_process"; import process from "node:process"; type ProbeFn = () => { ok: boolean; detail?: string }; type Probe = { name: string; required: boolean; run: ProbeFn; }; function whichVersion(cmd: string, args: string[] = ["--version"]): ReturnType { // execFile (not exec) avoids a shell, so user PATH lookups can't be re-interpreted. try { const out = execFileSync(cmd, args, { stdio: ["ignore", "pipe", "ignore"], encoding: "utf8", timeout: 4000, }); return { ok: true, detail: out.trim().split("\n")[0] }; } catch { return { ok: false }; } } const PROBES: Probe[] = [ { name: "Node.js 20+", required: true, run: () => { const major = Number.parseInt(process.versions.node.split(".")[0]!, 10); return { ok: major >= 20, detail: `v${process.versions.node}` }; }, }, { name: "TypeScript runner (tsx)", required: false, run: () => whichVersion("npx", ["-y", "tsx", "--version"]), }, { name: "Git", required: true, run: () => whichVersion("git"), }, { name: "Python 3.10+", required: true, run: () => { const probe = whichVersion("python3"); if (!probe.ok || !probe.detail) return probe; // Detail looks like "Python 3.11.7"; pull major.minor. const match = probe.detail.match(/(\d+)\.(\d+)/); if (!match) return { ok: false, detail: probe.detail }; const [major, minor] = [Number(match[1]), Number(match[2])]; const ok = major > 3 || (major === 3 && minor >= 10); return { ok, detail: probe.detail }; }, }, { name: "Rust (cargo)", required: false, run: () => whichVersion("cargo"), }, { name: "Deno", required: false, run: () => whichVersion("deno"), }, ]; function run(): number { process.stdout.write("\n=== AI Engineering from Scratch — Environment Check ===\n\n"); let requiredPassed = 0; let requiredTotal = 0; for (const probe of PROBES) { const result = probe.run(); const tag = result.ok ? "PASS" : "FAIL"; const detail = result.detail ? ` (${result.detail})` : ""; const flag = probe.required ? "" : " [optional]"; process.stdout.write(` [${tag}] ${probe.name}${detail}${flag}\n`); if (probe.required) { requiredTotal += 1; if (result.ok) requiredPassed += 1; } } process.stdout.write(`\nResult: ${requiredPassed}/${requiredTotal} required checks passed\n`); if (requiredPassed === requiredTotal) { process.stdout.write("\nYou're ready. Start with Phase 1.\n\n"); return 0; } process.stdout.write("\nFix the failed required checks above, then re-run.\n\n"); return 1; } process.exit(run());