Files
hyperframes/packages/cli/src/commands/doctor.ts
T
Vance IngallsandClaude Opus 4.6 9f8e5ba5a1 initial code (#2)
* feat: initial code port from hyperframes-internal

Port all OSS-ready packages from the internal monorepo:
- @hyperframes/core — shared types, HTML generation, GSAP utilities, runtime
- @hyperframes/cli — CLI for creating, previewing, and rendering compositions
- @hyperframes/engine — framework-agnostic rendering engine (BeginFrame + FFmpeg)
- @hyperframes/producer — video rendering pipeline (Puppeteer + FFmpeg)
- @hyperframes/ui-player — browser-based video player component
- @hyperframes/studio — composition editor (React frontend + Hono backend)

Includes regression test suite with Docker-based test harness.

All HeyGen-internal references, deployment infrastructure, and
proprietary assets have been removed. Package names migrated
from @app/* to @hyperframes/*.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: scrub internal codenames and stale references from OSS port

- Replace static.heygen.ai runtime URLs in test fixtures
- Remove internal CDN publish script (publish-hyperframe-runtime.ts)
- Replace sandbox-studio, sandbox-interceptor, __magicEditRuntime
  with neutral names (studio, hyperframe-runtime, __hyperframeRuntime)
- Fix stale Vault API / localhost references in docs
- Remove broken deprecated_studio link

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove remaining internal codenames and stale references

- Delete stale producer README.md and PIPELINE.md (referenced nonexistent files)
- Replace "Cerberus" codename with "HyperFrames" in test design reviews
- Replace magic-edit postMessage identifiers with hf-preview/hf-parent
- Rename debug-magic-edit-timeline.ts to debug-timeline.ts
- Replace "Motion Cut" with "HyperFrames" in Timeline comments
- Fix studio/CLI references to nonexistent archive package
  (use local data/projects/ dir, stub render proxy)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 22:43:56 -07:00

132 lines
3.5 KiB
TypeScript

import { defineCommand } from "citty";
import { execSync } from "node:child_process";
import { existsSync } from "node:fs";
import { c } from "../ui/colors.js";
import { findBrowser } from "../browser/manager.js";
import { findFFmpeg } from "../browser/ffmpeg.js";
interface Check {
name: string;
run: () => CheckResult | Promise<CheckResult>;
}
interface CheckResult {
ok: boolean;
detail: string;
hint?: string;
}
function checkFFmpeg(): CheckResult {
const path = findFFmpeg();
if (path) {
try {
const version = execSync("ffmpeg -version", { encoding: "utf-8", timeout: 5000 })
.split("\n")[0] ?? "";
return { ok: true, detail: version.trim() };
} catch {
return { ok: true, detail: path };
}
}
return {
ok: false,
detail: "Not found",
hint: process.platform === "darwin" ? "brew install ffmpeg" : "sudo apt install ffmpeg",
};
}
function checkFFprobe(): CheckResult {
try {
const result = execSync("which ffprobe", { encoding: "utf-8", timeout: 5000 }).trim();
return { ok: true, detail: result };
} catch {
return {
ok: false,
detail: "Not found",
hint: "Installed with ffmpeg",
};
}
}
async function checkChrome(): Promise<CheckResult> {
const info = await findBrowser();
if (info) {
return { ok: true, detail: `${info.source}: ${info.executablePath}` };
}
return {
ok: false,
detail: "Not found",
hint: "Run: npx hyperframes browser ensure",
};
}
function checkDocker(): CheckResult {
try {
const version = execSync("docker --version", { encoding: "utf-8", timeout: 5000 }).trim();
return { ok: true, detail: version };
} catch {
return {
ok: false,
detail: "Not found",
hint: "https://docs.docker.com/get-docker/",
};
}
}
function checkDockerRunning(): CheckResult {
try {
execSync("docker info", { stdio: "pipe", timeout: 5000 });
return { ok: true, detail: "Running" };
} catch {
return {
ok: false,
detail: "Not running",
hint: "Start Docker Desktop or run: sudo systemctl start docker",
};
}
}
function checkNode(): CheckResult {
return { ok: true, detail: `${process.version} (${process.platform} ${process.arch})` };
}
export default defineCommand({
meta: { name: "doctor", description: "Check system dependencies and environment" },
args: {},
async run() {
console.log();
console.log(c.bold("hyperframes doctor"));
console.log();
const checks: Check[] = [
{ name: "Node.js", run: checkNode },
{ name: "FFmpeg", run: checkFFmpeg },
{ name: "FFprobe", run: checkFFprobe },
{ name: "Chrome", run: checkChrome },
{ name: "Docker", run: checkDocker },
{ name: "Docker running", run: checkDockerRunning },
];
let allOk = true;
for (const check of checks) {
const result = await check.run();
const icon = result.ok ? c.success("\u2713") : c.error("\u2717");
const name = check.name.padEnd(16);
console.log(` ${icon} ${c.bold(name)} ${result.ok ? c.dim(result.detail) : c.error(result.detail)}`);
if (!result.ok && result.hint) {
console.log(` ${" ".repeat(19)}${c.accent(result.hint)}`);
}
if (!result.ok) allOk = false;
}
console.log();
if (allOk) {
console.log(` ${c.success("\u25C7")} ${c.success("All checks passed")}`);
} else {
console.log(` ${c.warn("\u25C7")} ${c.warn("Some checks failed — see hints above")}`);
}
console.log();
},
});