feat(HAI-071): add Bun single-binary compile support for CLI
- Create build script (packages/cli/build.ts) using Bun.build compile target - Update asset resolution in dashboard server for bundled context - Add npm scripts to wire up the build process - Add build-exe tests to verify compiled binary output - Fix pre-existing build errors in engine and core packages - Document build and compile workflow in README
This commit is contained in:
88
packages/cli/build.ts
Normal file
88
packages/cli/build.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* Bun compile build script for the `hai` CLI.
|
||||
*
|
||||
* Produces a single self-contained executable at packages/cli/dist/hai
|
||||
* with the dashboard client assets co-located at packages/cli/dist/client/.
|
||||
*
|
||||
* Usage:
|
||||
* bun run build.ts
|
||||
*
|
||||
* Prerequisites:
|
||||
* - `pnpm build` must have been run first (dashboard client + tsc)
|
||||
* - Bun >= 1.0
|
||||
*/
|
||||
|
||||
import { join, dirname } from "node:path";
|
||||
import { cpSync, mkdirSync, existsSync, rmSync } from "node:fs";
|
||||
|
||||
const cliRoot = dirname(new URL(import.meta.url).pathname);
|
||||
const workspaceRoot = join(cliRoot, "..", "..");
|
||||
const outDir = join(cliRoot, "dist");
|
||||
const outBinary = join(outDir, process.platform === "win32" ? "hai.exe" : "hai");
|
||||
const dashboardClientSrc = join(workspaceRoot, "packages", "dashboard", "dist", "client");
|
||||
const dashboardClientDest = join(outDir, "client");
|
||||
|
||||
// ── Validate prerequisites ────────────────────────────────────────────
|
||||
if (!existsSync(dashboardClientSrc)) {
|
||||
console.error(
|
||||
`ERROR: Dashboard client not built. Expected: ${dashboardClientSrc}\n` +
|
||||
`Run 'pnpm build' first to build all packages.`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// ── Clean previous output ─────────────────────────────────────────────
|
||||
if (existsSync(outBinary)) rmSync(outBinary);
|
||||
if (existsSync(dashboardClientDest)) rmSync(dashboardClientDest, { recursive: true });
|
||||
|
||||
// ── Copy dashboard client assets alongside output ─────────────────────
|
||||
// Express.static requires a real filesystem directory, so we co-locate
|
||||
// the pre-built SPA next to the binary rather than embedding blobs.
|
||||
console.log("Copying dashboard client assets...");
|
||||
mkdirSync(dashboardClientDest, { recursive: true });
|
||||
cpSync(dashboardClientSrc, dashboardClientDest, { recursive: true });
|
||||
console.log(` → ${dashboardClientDest}`);
|
||||
|
||||
// ── Compile the CLI binary ────────────────────────────────────────────
|
||||
console.log("Compiling hai executable...");
|
||||
|
||||
const entryPoint = join(cliRoot, "src", "bin.ts");
|
||||
|
||||
const proc = Bun.spawnSync({
|
||||
cmd: [
|
||||
"bun", "build",
|
||||
"--compile",
|
||||
entryPoint,
|
||||
"--outfile", outBinary,
|
||||
"--target", "bun",
|
||||
// Minify for smaller binary
|
||||
"--minify",
|
||||
],
|
||||
cwd: workspaceRoot,
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
env: {
|
||||
...process.env,
|
||||
// Ensure workspace resolution works
|
||||
NODE_PATH: join(workspaceRoot, "node_modules"),
|
||||
},
|
||||
});
|
||||
|
||||
if (proc.exitCode !== 0) {
|
||||
console.error(`\nBun compile failed with exit code ${proc.exitCode}`);
|
||||
process.exit(proc.exitCode ?? 1);
|
||||
}
|
||||
|
||||
// ── Write a minimal package.json next to the binary ───────────────────
|
||||
// Some bundled dependencies (e.g. express) probe for package.json at
|
||||
// runtime. Provide a minimal one so the binary can self-resolve.
|
||||
import { writeFileSync } from "node:fs";
|
||||
writeFileSync(
|
||||
join(outDir, "package.json"),
|
||||
JSON.stringify({ name: "hai", version: "0.1.0", type: "module" }, null, 2) + "\n",
|
||||
);
|
||||
|
||||
console.log(`\n✓ Built: ${outBinary}`);
|
||||
console.log(` Assets: ${dashboardClientDest}`);
|
||||
console.log(`\nRun with: ${outBinary} --help`);
|
||||
Reference in New Issue
Block a user