#!/usr/bin/env node import { realpathSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { downloadFromManifest } from "./download.ts"; import { runSessionStartHook } from "./hook.ts"; import { runBootstrapWorker } from "./worker.ts"; export { bootstrapLocks, detectInstallFlow, detectInstallFlowDetailed, detectInstallFlowForTest, detectInstallFlowFromEnvironment, INSTALL_SNAPSHOT_FILENAME, resolveBootstrapLockPath, resolveBootstrapStatePath, resolveCodexHome, } from "./environment.ts"; export type { BootstrapLockHandle, BootstrapLocksOptions, CodexHomeResolution, CodexHomeSource, ConfigSourceSignal, DetectInstallFlowFromEnvironmentOptions, DetectInstallFlowOptions, InstallFlow, InstallFlowDetection, ResolveCodexHomeOptions, } from "./environment.ts"; export { BOOTSTRAP_RESTART_NOTICE, executeSessionStartHook, runSessionStartHook } from "./hook.ts"; export { runSgProvision, SG_FORCE_PROVISION_ENV_KEY, SG_PROVISION_COMPONENT, sgProvisionDestination, } from "./provision.ts"; export type { ResolvePreexistingSgOptions, SgProvisionSeams } from "./provision.ts"; export { GIT_BASH_INSTALL_HINT, runWorkerSetup, SETUP_MARKETPLACE_NAME, SETUP_PLUGIN_NAME } from "./setup.ts"; export type { WorkerSetupOptions } from "./setup.ts"; export type { SessionStartAction, SessionStartHookOptions, SessionStartHookResult, WorkerSpawnInvocation, } from "./hook.ts"; export { appendBootstrapLog, BOOTSTRAP_DOCTOR_HINT, defaultWorkerSteps, parseBootstrapState, parseWorkerFlags, readBootstrapState, readPluginVersion, resolvePluginDataRoot, runBootstrapWorker, } from "./worker.ts"; export type { DefaultWorkerStepsSeams, BootstrapDegradedEntry, BootstrapRunStatus, BootstrapState, BootstrapStepOutcome, BootstrapWorkerContext, BootstrapWorkerFlags, BootstrapWorkerResult, BootstrapWorkerSkipReason, BootstrapWorkerStep, RunBootstrapWorkerOptions, } from "./worker.ts"; const TOP_LEVEL_HELP = "Usage:\n omo-bootstrap hook session-start\n omo-bootstrap worker [--codex-home ] [--once] [--only ] [--manifest-dir ]\n omo-bootstrap download \n omo-bootstrap help | --help | -h\n"; async function runDownloadCommand(args: readonly string[]): Promise { const [manifestName, platformKey, destinationDir] = args; if (manifestName === undefined || platformKey === undefined || destinationDir === undefined) { process.stderr.write(`[omo-bootstrap] download requires \n${TOP_LEVEL_HELP}`); return 1; } try { const destination = await downloadFromManifest({ destinationDir, manifestName, platformKey }); process.stdout.write(`OK:${destination}\n`); return 0; } catch (error) { process.stderr.write(`[omo-bootstrap] download failed: ${error instanceof Error ? error.message : String(error)}\n`); return 1; } } async function runWorkerCommand(args: readonly string[]): Promise { let result; try { result = await runBootstrapWorker({ argv: args, env: process.env }); } catch (error) { const message = error instanceof Error ? error.message : String(error); if (/flag/.test(message)) { process.stderr.write(`[omo-bootstrap] ${message}\n${TOP_LEVEL_HELP}`); return 1; } // Runtime worker failures must never surface as non-zero exits; the // degraded ledger in state.json is the error channel. process.stderr.write(`[omo-bootstrap] worker error: ${message}\n`); return 0; } process.stdout.write( result.ran ? `[omo-bootstrap] worker finished: ${result.status}\n` : `[omo-bootstrap] worker skipped: ${result.reason}\n`, ); return 0; } async function main(): Promise { const argv = process.argv.slice(2); const command = argv[0]; if (command === undefined || command === "help" || command === "--help" || command === "-h") { process.stdout.write(TOP_LEVEL_HELP); return 0; } if (command === "hook" && argv[1] === "session-start") { return runSessionStartHook({ env: process.env, stdin: process.stdin }); } if (command === "worker") { return runWorkerCommand(argv.slice(1)); } if (command === "download") { return runDownloadCommand(argv.slice(1)); } process.stderr.write(`[omo-bootstrap] unknown command: ${argv.join(" ")}\n${TOP_LEVEL_HELP}`); return 1; } function isProcessEntry(): boolean { const entry = process.argv[1]; if (entry === undefined) return false; try { return realpathSync(entry) === realpathSync(fileURLToPath(import.meta.url)); } catch { return false; } } if (isProcessEntry()) { main() .then((code) => { process.exit(code); }) .catch((error: unknown) => { // The SessionStart hook path must never fail the session: log and exit 0. process.stderr.write(`[omo-bootstrap] ${error instanceof Error ? error.message : String(error)}\n`); process.exit(0); }); }