import { spawnSync } from "node:child_process"; import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs"; import os from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; import type { EmitContext } from "../commander.ts"; /** * Lazy-fetch of the web dashboard for npm consumers. * * The published npm package ships the CLI + coord engine but deliberately * excludes `web/` (ADR 0001/0003: keep the core lean for CLI-only users). The * dashboard also imports harnery's `src/` directly (see `web/next.config.ts`), * so it can't be lifted out as a standalone folder. Instead, the first time * `harn web up` runs from an install without a local `web/`, we clone the * harnery repo at the *matching version tag* into a cache dir and install the * web app's deps. Subsequent runs reuse the cache. * * The only thing `web/` pulls from `src/` is `harnery/core/journal` (a single, * dependency-free module), so the install is `web/` deps only: no root install, * no Playwright browser download. */ const DEFAULT_REPO_URL = "https://github.com/ryanjkelly/harnery.git"; /** harnery's own package root: src/commands/ -> ../.. ; valid in both src/ and dist/. */ function packageRoot(): string { const here = path.dirname(fileURLToPath(import.meta.url)); return path.resolve(here, "..", ".."); } function readPkg(): { version: string; repoUrl: string } { try { const pkg = JSON.parse(readFileSync(path.join(packageRoot(), "package.json"), "utf8")); const raw = typeof pkg.repository === "string" ? pkg.repository : (pkg.repository?.url ?? ""); const repoUrl = raw.replace(/^git\+/, "") || DEFAULT_REPO_URL; return { version: typeof pkg.version === "string" ? pkg.version : "0.0.0", repoUrl }; } catch { return { version: "0.0.0", repoUrl: DEFAULT_REPO_URL }; } } /** The git ref to fetch: HARNERY_WEB_REF override, else the version tag (`v`). */ function resolveRef(version: string): string { return process.env.HARNERY_WEB_REF || `v${version}`; } /** Per-ref cache dir under the XDG cache (matches harnery's `~/.cache/harnery/...`). */ function webCacheRoot(ref: string): string { const base = process.env.XDG_CACHE_HOME || path.join(os.homedir(), ".cache"); const safeRef = ref.replace(/[^\w.-]/g, "_"); return path.join(base, "harnery", "web", safeRef); } /** bun if available (preferred), else npm. Both support ` install` and ` run