hololake-system-architecture/product-source/hololake-native-desktop/system-integrations/codex-host-bridge/scripts/install.mjs

130 lines
4.5 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
import crypto from "node:crypto";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
function parseArgs(argv) {
const values = {};
for (let index = 0; index < argv.length; index += 2) {
if (!argv[index]?.startsWith("--") || argv[index + 1] === undefined) {
throw new Error("INVALID_ARGUMENTS");
}
values[argv[index].slice(2)] = argv[index + 1];
}
return values;
}
function sha256(file) {
return crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
}
function shellQuote(value) {
return `'${String(value).replaceAll("'", `'"'"'`)}'`;
}
function pinnedCommand(file, hash, environment = {}) {
const assignments = Object.entries(environment)
.map(([key, value]) => `${key}=${shellQuote(value)}`)
.join(" ");
const body = [
`printf \"%s %s\\n\" \"${hash}\" ${shellQuote(file)}`,
"/usr/bin/shasum -a 256 -c - >/dev/null",
"exit 97",
`exec /usr/local/bin/node ${shellQuote(file)}`,
];
return `${assignments ? `/usr/bin/env ${assignments} ` : ""}/bin/sh -c ${shellQuote(`${body[0]} | ${body[1]} || ${body[2]}; ${body[3]}`)}`;
}
function atomicWrite(file, value) {
fs.mkdirSync(path.dirname(file), { recursive: true });
const temporary = `${file}.${process.pid}.tmp`;
fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, {
encoding: "utf8",
mode: 0o600,
});
fs.renameSync(temporary, file);
}
const args = parseArgs(process.argv.slice(2));
const codexHome = process.env.CODEX_HOME ?? path.join(os.homedir(), ".codex");
const hooksPath = path.join(codexHome, "hooks.json");
const installRoot = path.join(codexHome, "runtime", "guanghu-codex-host-bridge", "v1");
const packageRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
const runtimeSource = path.join(packageRoot, "runtime");
const runtimeFiles = [
"source-controller-gate.mjs",
"current-controller-guard.mjs",
"write-lease.mjs",
];
fs.mkdirSync(installRoot, { recursive: true });
for (const file of runtimeFiles) {
fs.copyFileSync(path.join(runtimeSource, file), path.join(installRoot, file));
fs.chmodSync(path.join(installRoot, file), 0o700);
}
let config = { hooks: {} };
if (fs.existsSync(hooksPath)) config = JSON.parse(fs.readFileSync(hooksPath, "utf8"));
config.hooks ??= {};
const environment = {
GH_HUMAN_SOURCE_ID: args["subject-id"] ?? "HUMAN-LOCAL",
GH_HUMAN_SOURCE_NAME: args["subject-name"] ?? "LOCAL_HUMAN",
GH_HUMAN_SOURCE_ROLE: args["subject-role"] ?? "HUMAN_HOST_ANCHOR",
GH_CODEX_CARRIER_ROLE: args["carrier-role"] ?? "CODEX_EXECUTION_CARRIER",
GH_HUMAN_MEMORY_ANCHOR: process.env.GH_HUMAN_MEMORY_ANCHOR ?? "CURRENT_DIRECT_HUMAN_MESSAGE",
};
const definitions = {
UserPromptSubmit: {
matcher: ".*",
hooks: [{
type: "command",
command: pinnedCommand(
path.join(installRoot, "source-controller-gate.mjs"),
sha256(path.join(installRoot, "source-controller-gate.mjs")),
environment,
),
timeout: 10,
additionalContextLimit: 1536,
statusMessage: "GuangHu: attach direct-human source envelope and advance controller epoch",
}],
},
PreToolUse: {
matcher: ".*",
hooks: [{
type: "command",
command: pinnedCommand(
path.join(installRoot, "current-controller-guard.mjs"),
sha256(path.join(installRoot, "current-controller-guard.mjs")),
),
timeout: 5,
additionalContextLimit: 1024,
statusMessage: "GuangHu: verify current controller epoch and one-shot write lease",
}],
},
};
for (const [event, definition] of Object.entries(definitions)) {
const existing = Array.isArray(config.hooks[event]) ? config.hooks[event] : [];
config.hooks[event] = [
...existing.filter((entry) => !JSON.stringify(entry).includes("guanghu-codex-host-bridge")),
definition,
];
}
atomicWrite(hooksPath, config);
atomicWrite(path.join(installRoot, "installation-receipt.json"), {
schema: "guanghu.codex-host-bridge-installation/v1",
package_id: "GH-CODEX-HOST-BRIDGE-v1",
installed_at: new Date().toISOString(),
codex_home: codexHome,
installed_files: runtimeFiles.map((file) => ({
path: path.join(installRoot, file),
sha256: sha256(path.join(installRoot, file)),
})),
runtime_state_in_repository: false,
trust_state: "REQUIRES_VISIBLE_CODEX_REVIEW",
});
process.stdout.write(`${JSON.stringify({
decision: "INSTALLED_REVIEW_REQUIRED",
hooks_path: hooksPath,
install_root: installRoot,
}, null, 2)}\n`);