scripts/agent-release.mjs
Rama main · réplica de lectura del repositorio original.
import { createHash } from "node:crypto";
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import {
agentReleasePlatforms,
validateAgentRelease,
} from "../app/src/lib/agent-release.mjs";
const root = resolve(import.meta.dirname, "..");
export function manifestForBinaries({
version,
repository,
goVersion,
metadata,
binaries,
}) {
if (metadata.version !== version)
throw new Error(
"Agent build version does not match the application version.",
);
const assets = agentReleasePlatforms.map((platform) => {
const body = binaries.get(platform.filename);
// Verify the executable platform, not merely a filename supplied by a caller.
if (
!body ||
body.length < 64 ||
body.subarray(0, 4).toString("hex") !== "7f454c46" ||
body[4] !== 2 ||
body[5] !== 1 ||
![0, 3].includes(body[7]) ||
body.readUInt16LE(16) !== 2 ||
body.readUInt16LE(18) !== (platform.arch === "amd64" ? 62 : 183)
)
throw new Error(`Invalid ELF platform: ${platform.filename}`);
return {
...platform,
size: body.length,
sha256: createHash("sha256").update(body).digest("hex"),
url: `https://github.com/${repository}/releases/download/v${version}/${platform.filename}`,
};
});
return validateAgentRelease({
schemaVersion: 1,
version,
repository,
goVersion,
protocolVersion: metadata.protocolVersion,
capabilities: metadata.capabilities,
assets,
});
}
function run(command, args, extra = {}) {
const result = spawnSync(command, args, {
cwd: root,
env: { ...process.env, ...extra },
encoding: "utf8",
});
if (result.error) throw result.error;
if (result.status !== 0)
throw new Error(`${command} failed: ${result.stderr}`);
return result.stdout;
}
export function prepareAgentRelease({
directory = resolve(root, "bin"),
manifestPath = resolve(root, "app/agent-release.json"),
} = {}) {
const { version, repository } = JSON.parse(
readFileSync(resolve(root, "app/package.json"), "utf8"),
);
const repositoryUrl =
typeof repository === "string" ? repository : repository?.url;
const repositoryName = /github\.com[/:]([^/]+\/[^/]+?)(?:\.git)?$/.exec(
repositoryUrl ?? "",
)?.[1];
if (!repositoryName)
throw new Error("Application package must identify its GitHub repository.");
mkdirSync(directory, { recursive: true });
for (const platform of agentReleasePlatforms)
run(
process.execPath,
["scripts/build-agent.mjs", resolve(directory, platform.filename)],
{ GOOS: platform.os, GOARCH: platform.arch },
);
const native = resolve(root, ".tmp/agent-release/native-agent");
run(process.execPath, ["scripts/build-agent.mjs", native], {
GOOS: process.platform === "darwin" ? "darwin" : process.platform,
GOARCH: process.arch === "arm64" ? "arm64" : "amd64",
});
const metadata = JSON.parse(run(native, ["version", "--json"]));
const goVersion = run("go", ["env", "GOVERSION"]).trim();
const binaries = new Map(
agentReleasePlatforms.map(({ filename }) => [
filename,
readFileSync(resolve(directory, filename)),
]),
);
const manifest = manifestForBinaries({
version,
repository: repositoryName,
goVersion,
metadata,
binaries,
});
mkdirSync(dirname(manifestPath), { recursive: true });
const json = JSON.stringify(manifest, null, 2) + "\n";
writeFileSync(manifestPath, json);
writeFileSync(resolve(directory, "agent-release.json"), json);
writeFileSync(
resolve(directory, "SHA256SUMS"),
manifest.assets
.map((asset) => `${asset.sha256} ${asset.filename}`)
.join("\n") + "\n",
);
console.log(
`Prepared agent ${version}: Linux amd64/arm64 and verified manifest.`,
);
return manifest;
}
if (
process.argv[1] &&
resolve(process.argv[1]) === fileURLToPath(import.meta.url)
) {
const args = process.argv.slice(2);
if (args.length > 2)
throw new Error(
"Usage: node scripts/agent-release.mjs [output-directory] [manifest-path]",
);
prepareAgentRelease({
...(args[0] ? { directory: resolve(args[0]) } : {}),
...(args[1] ? { manifestPath: resolve(args[1]) } : {}),
});
}