diff --git a/openclaw.mjs b/openclaw.mjs index 432ee961fb0..01a74302112 100755 --- a/openclaw.mjs +++ b/openclaw.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +import { readFileSync } from "node:fs"; import { access } from "node:fs/promises"; import module from "node:module"; import { fileURLToPath } from "node:url"; @@ -85,8 +86,6 @@ const installProcessWarningFilter = async () => { } }; -await installProcessWarningFilter(); - const tryImport = async (specifier) => { try { await import(specifier); @@ -126,10 +125,56 @@ const buildMissingEntryErrorMessage = async () => { return lines.join("\n"); }; -if (await tryImport("./dist/entry.js")) { - // OK -} else if (await tryImport("./dist/entry.mjs")) { +const isBareRootHelpInvocation = (argv) => + argv.length === 3 && (argv[2] === "--help" || argv[2] === "-h"); + +const loadPrecomputedRootHelpText = () => { + try { + const raw = readFileSync(new URL("./dist/cli-startup-metadata.json", import.meta.url), "utf8"); + const parsed = JSON.parse(raw); + return typeof parsed?.rootHelpText === "string" && parsed.rootHelpText.length > 0 + ? parsed.rootHelpText + : null; + } catch { + return null; + } +}; + +const tryOutputBareRootHelp = async () => { + if (!isBareRootHelpInvocation(process.argv)) { + return false; + } + const precomputed = loadPrecomputedRootHelpText(); + if (precomputed) { + process.stdout.write(precomputed); + return true; + } + for (const specifier of ["./dist/cli/program/root-help.js", "./dist/cli/program/root-help.mjs"]) { + try { + const mod = await import(specifier); + if (typeof mod.outputRootHelp === "function") { + mod.outputRootHelp(); + return true; + } + } catch (err) { + if (isDirectModuleNotFoundError(err, specifier)) { + continue; + } + throw err; + } + } + return false; +}; + +if (await tryOutputBareRootHelp()) { // OK } else { - throw new Error(await buildMissingEntryErrorMessage()); + await installProcessWarningFilter(); + if (await tryImport("./dist/entry.js")) { + // OK + } else if (await tryImport("./dist/entry.mjs")) { + // OK + } else { + throw new Error(await buildMissingEntryErrorMessage()); + } } diff --git a/scripts/write-cli-startup-metadata.ts b/scripts/write-cli-startup-metadata.ts index 9f52b0fced3..67648c38646 100644 --- a/scripts/write-cli-startup-metadata.ts +++ b/scripts/write-cli-startup-metadata.ts @@ -1,6 +1,7 @@ import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; +import { renderRootHelpText } from "../src/cli/program/root-help.ts"; function dedupe(values: string[]): string[] { const seen = new Set(); @@ -77,6 +78,7 @@ function readBundledChannelCatalogIds(): string[] { const catalog = readBundledChannelCatalogIds(); const channelOptions = dedupe([...CORE_CHANNEL_ORDER, ...catalog]); +const rootHelpText = renderRootHelpText(); mkdirSync(distDir, { recursive: true }); writeFileSync( @@ -85,6 +87,7 @@ writeFileSync( { generatedBy: "scripts/write-cli-startup-metadata.ts", channelOptions, + rootHelpText, }, null, 2, diff --git a/src/cli/program/root-help.ts b/src/cli/program/root-help.ts index 500dbe3b039..f62930f24f9 100644 --- a/src/cli/program/root-help.ts +++ b/src/cli/program/root-help.ts @@ -23,7 +23,23 @@ function buildRootHelpProgram(): Command { return program; } -export function outputRootHelp(): void { +export function renderRootHelpText(): string { const program = buildRootHelpProgram(); - program.outputHelp(); + let output = ""; + const originalWrite = process.stdout.write.bind(process.stdout); + const captureWrite: typeof process.stdout.write = ((chunk: string | Uint8Array) => { + output += String(chunk); + return true; + }) as typeof process.stdout.write; + process.stdout.write = captureWrite; + try { + program.outputHelp(); + } finally { + process.stdout.write = originalWrite; + } + return output; +} + +export function outputRootHelp(): void { + process.stdout.write(renderRootHelpText()); }