RSI-Harness / scripts /build-binary.ts
Zihan Tan
Sync full source tree with github.com/CosmosMind-ai/RSI-Harness@33c4f8d
a861001
Raw History Blame Contribute Delete
5.08 kB
import { spawnSync } from "node:child_process";
import {
chmodSync,
copyFileSync,
cpSync,
mkdirSync,
rmSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import { homedir } from "node:os";
import { basename, dirname, join, resolve } from "node:path";
const executableName = process.platform === "win32" ? "rsih.exe" : "rsih";
const outputDirectory = resolve("dist");
const outputPath = join(outputDirectory, executableName);
mkdirSync(outputDirectory, { recursive: true });
const build = spawnSync(
"bun",
["build", "src/cli.ts", "--compile", "--outfile", outputPath],
{
cwd: process.cwd(),
stdio: "inherit",
},
);
if (build.error?.code === "ENOENT") {
throw new Error(
"Bun is required to build the standalone binary. Install Bun or use the npm-linked CLI.",
);
}
if (build.status !== 0) {
process.exitCode = build.status ?? 1;
} else {
if (process.platform !== "win32") chmodSync(outputPath, 0o755);
copyFileSync(resolve("package.json"), join(outputDirectory, "package.json"));
copyFileSync(resolve("README.md"), join(outputDirectory, "README.md"));
// No CHANGELOG.md ships with the binary. Pi resolves the changelog next to
// the package.json it reads, so pairing Pi's release notes with RSIH's
// version makes every Pi release look unseen and replays "What's New" on
// every start; a missing file reads as no entries, which is what we want.
// Built-in Genomes. `resolveHarnessGenome` looks for `<PI_PACKAGE_DIR>/genomes`
// after the project and user directories, so `--genome paperlab` and
// `--genome harness-rsi` work from any cwd without an install step.
// Clear the target first so a Genome removed from config/genomes does not
// keep shipping from earlier builds.
rmSync(join(outputDirectory, "genomes"), { recursive: true, force: true });
cpSync(resolve("config", "genomes"), join(outputDirectory, "genomes"), {
recursive: true,
});
cpSync(
resolve(
"node_modules",
"@earendil-works",
"pi-coding-agent",
"docs",
),
join(outputDirectory, "docs"),
{ recursive: true },
);
cpSync(
resolve(
"node_modules",
"@earendil-works",
"pi-coding-agent",
"examples",
),
join(outputDirectory, "examples"),
{ recursive: true },
);
cpSync(
resolve(
"node_modules",
"@earendil-works",
"pi-coding-agent",
"dist",
"modes",
"interactive",
"theme",
),
join(outputDirectory, "theme"),
{ recursive: true },
);
cpSync(
resolve(
"node_modules",
"@earendil-works",
"pi-coding-agent",
"dist",
"modes",
"interactive",
"assets",
),
join(outputDirectory, "assets"),
{ recursive: true },
);
cpSync(
resolve(
"node_modules",
"@earendil-works",
"pi-coding-agent",
"dist",
"core",
"export-html",
),
join(outputDirectory, "export-html"),
{ recursive: true },
);
console.log(`Built ${outputPath}`);
if (process.argv.includes("--install")) install(outputPath);
}
/**
* Install the built binary together with everything it resolves relative to
* itself.
*
* The binary is not self-sufficient: `PI_PACKAGE_DIR` is the directory the
* executable sits in, and that is where it looks for package.json (the version),
* the built-in Genome seeds, themes and Pi's own assets. Copying just the
* executable onto the PATH produces an rsih that reports version 0.0.0 and
* cannot find `paperlab` or `harness-rsi` at all. So the whole payload goes to a
* library directory and only a link goes on the PATH.
*/
function install(outputPath) {
const binDirectory = resolve(
process.env.RSIH_INSTALL_DIR ?? join(homedir(), ".local", "bin"),
);
const libDirectory = resolve(
process.env.RSIH_LIB_DIR ?? join(homedir(), ".local", "lib", "rsih"),
);
const executable = basename(outputPath);
mkdirSync(libDirectory, { recursive: true });
cpSync(dirname(outputPath), libDirectory, { recursive: true, force: true });
const target = join(libDirectory, executable);
if (process.platform !== "win32") chmodSync(target, 0o755);
console.log(`Installed payload to ${libDirectory}`);
if (process.platform === "win32") {
// Symlinks need elevation on Windows, so the payload directory itself goes
// on the PATH.
console.log(`Add ${libDirectory} to your PATH.`);
return;
}
mkdirSync(binDirectory, { recursive: true });
const link = join(binDirectory, executable);
rmSync(link, { force: true });
symlinkSync(target, link);
console.log(`Linked ${link}`);
// `gee` is `rsih --genome harness-rsi`. It cannot be a link, because the
// binary would have no way to know which name invoked it.
const gee = join(binDirectory, "gee");
rmSync(gee, { force: true });
writeFileSync(
gee,
`#!/bin/sh\n# Generated by rsih. Equivalent to: rsih --genome harness-rsi\nexec ${JSON.stringify(link)} --genome harness-rsi "$@"\n`,
"utf8",
);
chmodSync(gee, 0o755);
console.log(`Wrote ${gee}`);
}