Files
openclaw/src/index.ts
Peter Steinberger 74af7163e1 perf(startup): short-circuit dist-internal ESM resolution with explicit module format (#128541)
Node's ESM resolver runs getPackageScopeConfig() for every file: URL to pick
a module format, and each call re-materializes package.json — re-parsing
openclaw's 318-key exports map (~93µs/call, verified on Node 26.5.0). Across
the ~17k module edges of a gateway boot that costs 1.3-2.6s CPU, showing up
in profiles as package_json_reader.get (self time) or attributed inclusively
under any registered resolve hook.

Install a Module.registerHooks resolve fast path from the main-module entry
points that resolves relative "./*.js" imports inside dist/ by URL join with
format "module", skipping scope detection entirely. Behavior-identical: all
145 dist package.json files declare type module and dist contains no
CJS-syntax .js files, so Node would conclude the same format. require()
resolutions, bare specifiers, non-.js targets, out-of-dist parents, and
dist-escaping ../ chains defer to default resolution; source checkouts and
runtimes without registerHooks (Bun) are untouched.

Measured (interleaved same-dist A/B, isolated OPENCLAW_STATE_DIR, macOS,
Node 26.5.0): gateway boot user CPU 5.2s -> 2.9s, self-reported listening
time 3.3-6.6s -> stable 2.1s.
2026-08-23 23:20:19 -07:00

172 lines
6.0 KiB
JavaScript

#!/usr/bin/env node
// Re-exports the OpenClaw CLI entry point for package execution.
// Package executable entrypoint that forwards to the CLI bootstrap.
import process from "node:process";
import { fileURLToPath } from "node:url";
import {
formatCliFailureLines,
formatCliJsonFailure,
isExpectedCliError,
} from "./cli/failure-output.js";
import { isJsonOutputModeActive } from "./cli/json-output-mode.js";
import { runCliWithExitFinalization } from "./cli/one-shot-exit.js";
import { installDistEsmResolveFastPath } from "./entry.esm-resolve-fast-path.js";
import { tryHandleRootVersionFastPath } from "./entry.version-fast-path.js";
import { formatUncaughtError } from "./infra/errors.js";
import { runFatalErrorHooks } from "./infra/fatal-error-hooks.js";
import { isMainModule } from "./infra/is-main.js";
import {
installUnhandledRejectionHandler,
isBenignUncaughtExceptionError,
isUncaughtExceptionHandled,
} from "./infra/unhandled-rejections.js";
type LegacyCliDeps = {
runCli: (
argv: string[],
options?: {
retainConsoleRoutingUntilProcessExit?: boolean;
},
) => Promise<void>;
};
type LibraryExports = typeof import("./library.js");
// These bindings are populated only for library consumers. The CLI entry stays
// on the lean path and must not read them while running as main.
export let applyTemplate: LibraryExports["applyTemplate"];
export let createDefaultDeps: LibraryExports["createDefaultDeps"];
export let deriveSessionKey: LibraryExports["deriveSessionKey"];
export let describePortOwner: LibraryExports["describePortOwner"];
export let ensureBinary: LibraryExports["ensureBinary"];
export let ensurePortAvailable: LibraryExports["ensurePortAvailable"];
export let getReplyFromConfig: LibraryExports["getReplyFromConfig"];
export let handlePortError: LibraryExports["handlePortError"];
export let loadConfig: LibraryExports["loadConfig"];
/** @deprecated Use SQLite-backed session APIs. Scheduled for removal after 2026-10-12. */
export let loadSessionStore: LibraryExports["loadSessionStore"];
export let monitorWebChannel: LibraryExports["monitorWebChannel"];
export let normalizeE164: LibraryExports["normalizeE164"];
export let PortInUseError: LibraryExports["PortInUseError"];
export let promptYesNo: LibraryExports["promptYesNo"];
export let resolveSessionKey: LibraryExports["resolveSessionKey"];
export let resolveStorePath: LibraryExports["resolveStorePath"];
export let runCommandWithTimeout: LibraryExports["runCommandWithTimeout"];
export let runExec: LibraryExports["runExec"];
/** @deprecated Use SQLite-backed session APIs. Scheduled for removal after 2026-10-12. */
export let saveSessionStore: LibraryExports["saveSessionStore"];
export let waitForever: LibraryExports["waitForever"];
async function loadLegacyCliDeps(): Promise<LegacyCliDeps> {
const { runCli } = await import("./cli/run-main.js");
return { runCli };
}
// Legacy direct file entrypoint only. Package root exports now live in library.ts.
export async function runLegacyCliEntry(
argv: string[] = process.argv,
deps?: LegacyCliDeps,
options?: {
retainConsoleRoutingUntilProcessExit?: boolean;
},
): Promise<void> {
const { runCli } = deps ?? (await loadLegacyCliDeps());
await runCli(argv, options);
}
const isMain = isMainModule({
currentFile: fileURLToPath(import.meta.url),
});
if (isMain) {
installDistEsmResolveFastPath(import.meta.url);
}
const handledRootVersion = isMain && tryHandleRootVersionFastPath(process.argv);
if (!isMain) {
({
applyTemplate,
createDefaultDeps,
deriveSessionKey,
describePortOwner,
ensureBinary,
ensurePortAvailable,
getReplyFromConfig,
handlePortError,
loadConfig,
loadSessionStore,
monitorWebChannel,
normalizeE164,
PortInUseError,
promptYesNo,
resolveSessionKey,
resolveStorePath,
runCommandWithTimeout,
runExec,
saveSessionStore,
waitForever,
} = await import("./library.js"));
}
if (isMain && !handledRootVersion) {
const { defaultRuntime, restoreRuntimeTerminalState } = await import("./runtime.js");
// Global error handlers to prevent silent crashes from unhandled rejections/exceptions.
// These log the error and exit gracefully instead of crashing without trace.
installUnhandledRejectionHandler();
process.on("uncaughtException", (error) => {
if (isUncaughtExceptionHandled(error)) {
return;
}
if (isBenignUncaughtExceptionError(error)) {
console.warn(
"[openclaw] Non-fatal uncaught exception (continuing):",
formatUncaughtError(error),
);
return;
}
if (isJsonOutputModeActive(process.argv)) {
defaultRuntime.writeJson(formatCliJsonFailure(error));
}
for (const line of formatCliFailureLines({
title: "OpenClaw hit an unexpected runtime error.",
error,
argv: process.argv,
})) {
console.error(line);
}
for (const message of runFatalErrorHooks({ reason: "uncaught_exception", error })) {
console.error("[openclaw]", message);
}
restoreRuntimeTerminalState("uncaught exception", { resumeStdinIfPaused: false });
process.exit(1);
});
void runCliWithExitFinalization({
run: async () =>
await runLegacyCliEntry(process.argv, undefined, {
// Finalizers and process-exit hooks can still emit diagnostics after runCli settles.
retainConsoleRoutingUntilProcessExit: true,
}),
onError: (err) => {
if (isJsonOutputModeActive(process.argv)) {
defaultRuntime.writeJson(formatCliJsonFailure(err));
}
for (const line of formatCliFailureLines({
title: "The CLI command failed.",
error: err,
argv: process.argv,
})) {
console.error(line);
}
if (!isExpectedCliError(err)) {
for (const message of runFatalErrorHooks({ reason: "legacy_cli_failure", error: err })) {
console.error("[openclaw]", message);
}
}
restoreRuntimeTerminalState("legacy cli failure", { resumeStdinIfPaused: false });
process.exitCode = 1;
},
});
}