Files
dev/hanzo-node/postinstall.js
T
zeekay c6a95b97db refactor: rename code-* binary references to dev-* everywhere
Release workflow already builds dev-* binaries. Update npm packages
(dev-cli, hanzo-node) and preview-build workflow to reference dev-*
directly, eliminating the download-and-rename indirection.

Removed stale dev-cli/.pack directory.
Cleaned code-* assets from v0.6.83-v0.6.85 GitHub releases.
2026-03-29 18:20:56 -07:00

292 lines
7.7 KiB
JavaScript

// postinstall.js - Download and extract platform-specific binary for hanzo-node
import { platform as nodePlatform, arch as nodeArch } from "os";
import path from "path";
import { fileURLToPath } from "url";
import {
existsSync,
mkdirSync,
copyFileSync,
chmodSync,
readFileSync,
unlinkSync,
createWriteStream,
} from "fs";
import { execSync } from "child_process";
import { get as httpsGet } from "https";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const { platform, arch } = process;
let targetTriple = null;
switch (platform) {
case "linux":
case "android":
switch (arch) {
case "x64":
targetTriple = "x86_64-unknown-linux-musl";
break;
case "arm64":
targetTriple = "aarch64-unknown-linux-musl";
break;
default:
break;
}
break;
case "darwin":
switch (arch) {
case "x64":
targetTriple = "x86_64-apple-darwin";
break;
case "arm64":
targetTriple = "aarch64-apple-darwin";
break;
default:
break;
}
break;
case "win32":
switch (arch) {
case "x64":
targetTriple = "x86_64-pc-windows-msvc.exe";
break;
default:
break;
}
break;
default:
break;
}
const getPlatformPackageName = () => {
const plt = nodePlatform();
const cpu = nodeArch();
if (plt === "win32") return "hanzo-node-win32-x64";
if (plt === "darwin" && cpu === "arm64") return "hanzo-node-darwin-arm64";
if (plt === "darwin" && cpu === "x64") return "hanzo-node-darwin-x64";
if (plt === "linux" && cpu === "x64") return "hanzo-node-linux-x64-musl";
if (plt === "linux" && cpu === "arm64") return "hanzo-node-linux-arm64-musl";
return null;
};
const getCacheDir = (version) => {
const plt = nodePlatform();
const home = process.env.HOME || process.env.USERPROFILE || "";
let base = "";
if (plt === "win32") {
base = process.env.LOCALAPPDATA || path.join(home, "AppData", "Local");
} else if (plt === "darwin") {
base = path.join(home, "Library", "Caches");
} else {
base = process.env.XDG_CACHE_HOME || path.join(home, ".cache");
}
const dir = path.join(base, "hanzo", "node", version);
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
return dir;
};
const httpsDownload = (url, dest) =>
new Promise((resolve, reject) => {
const req = httpsGet(url, (res) => {
const status = res.statusCode || 0;
if (status >= 300 && status < 400 && res.headers.location) {
return resolve(httpsDownload(res.headers.location, dest));
}
if (status !== 200) {
return reject(new Error(`HTTP ${status}`));
}
const out = createWriteStream(dest);
res.pipe(out);
out.on("finish", () => out.close(resolve));
out.on("error", (e) => {
try {
unlinkSync(dest);
} catch {}
reject(e);
});
});
req.on("error", (e) => {
try {
unlinkSync(dest);
} catch {}
reject(e);
});
req.setTimeout(120000, () => {
req.destroy(new Error("download timed out"));
});
});
export async function runPostinstall(opts = {}) {
const { invokedByRuntime = false, skipGlobalAlias = false } = opts;
if (!targetTriple) {
if (!invokedByRuntime) {
console.log(
`hanzo-node: unsupported platform ${platform}/${arch}, skipping postinstall`,
);
}
return;
}
const binDir = path.join(__dirname, "bin");
if (!existsSync(binDir)) mkdirSync(binDir, { recursive: true });
const binaryPath = path.join(binDir, `hanzo-${targetTriple}`);
const legacyBinaryPath = path.join(binDir, `dev-${targetTriple}`);
// Already present?
if (existsSync(binaryPath) || existsSync(legacyBinaryPath)) {
if (!invokedByRuntime) {
console.log("hanzo-node: binary already present");
}
return;
}
// Try platform package first
const pkgName = getPlatformPackageName();
if (pkgName) {
try {
const req = (await import("module")).createRequire(import.meta.url);
const pkgJson = req.resolve(`${pkgName}/package.json`);
const pkgDir = path.dirname(pkgJson);
const src = path.join(pkgDir, "bin", `hanzo-${targetTriple}`);
if (existsSync(src)) {
copyFileSync(src, binaryPath);
if (platform !== "win32") {
try {
chmodSync(binaryPath, 0o755);
} catch {}
}
if (!invokedByRuntime) {
console.log(`hanzo-node: installed from ${pkgName}`);
}
return;
}
} catch (e) {
// Fall through to download
}
}
// Download from GitHub release
try {
const pkg = JSON.parse(
readFileSync(path.join(__dirname, "package.json"), "utf8"),
);
const version = pkg.version;
const isWin = platform === "win32";
const binaryName = `dev-${targetTriple}`;
const archiveName = isWin
? `${binaryName}.zip`
: (() => {
try {
execSync("zstd --version", { stdio: "ignore", shell: true });
return `${binaryName}.zst`;
} catch {
return `${binaryName}.tar.gz`;
}
})();
const url = `https://github.com/hanzoai/dev/releases/download/v${version}/${archiveName}`;
if (!invokedByRuntime) {
console.log(`hanzo-node: downloading ${archiveName}...`);
}
const tmp = path.join(binDir, `.${archiveName}.part`);
await httpsDownload(url, tmp);
if (isWin) {
const sysRoot =
process.env.SystemRoot || process.env.windir || "C:\\Windows";
const psFull = path.join(
sysRoot,
"System32",
"WindowsPowerShell",
"v1.0",
"powershell.exe",
);
const psCmd = `Expand-Archive -Path '${tmp}' -DestinationPath '${binDir}' -Force`;
let ok = false;
try {
execSync(`"${psFull}" -NoProfile -NonInteractive -Command "${psCmd}"`, {
stdio: "ignore",
});
ok = true;
} catch {}
if (!ok) {
try {
execSync(
`powershell -NoProfile -NonInteractive -Command "${psCmd}"`,
{ stdio: "ignore" },
);
ok = true;
} catch {}
}
if (!ok) {
try {
execSync(`pwsh -NoProfile -NonInteractive -Command "${psCmd}"`, {
stdio: "ignore",
});
ok = true;
} catch {}
}
if (!ok) {
execSync(`tar -xf "${tmp}" -C "${binDir}"`, {
stdio: "ignore",
shell: true,
});
}
try {
unlinkSync(tmp);
} catch {}
} else {
if (archiveName.endsWith(".zst")) {
execSync(`zstd -d '${tmp}' -o '${binaryPath}'`, {
stdio: "ignore",
shell: true,
});
try {
unlinkSync(tmp);
} catch {}
} else {
execSync(`tar -xzf '${tmp}' -C '${binDir}'`, {
stdio: "ignore",
shell: true,
});
try {
unlinkSync(tmp);
} catch {}
}
try {
chmodSync(binaryPath, 0o755);
} catch {}
}
// Cache for future use
const cacheDir = getCacheDir(version);
const cachePath = path.join(cacheDir, `hanzo-${targetTriple}`);
try {
copyFileSync(binaryPath, cachePath);
} catch {}
if (!invokedByRuntime) {
console.log("hanzo-node: installation complete");
}
} catch (e) {
if (!invokedByRuntime) {
console.error(`hanzo-node: postinstall failed: ${e.message}`);
console.error(
"You can try running the CLI directly, which will attempt to bootstrap the binary.",
);
}
throw e;
}
}
// Run if invoked directly
if (process.argv[1] === fileURLToPath(import.meta.url)) {
runPostinstall().catch(() => process.exit(0)); // Don't fail npm install
}