























@@ -15,6 +15,7 @@ import { enableConsoleCapture } from "../logging.js";
1515import type { PluginManifestCommandAliasRegistry } from "../plugins/manifest-command-aliases.js";
1616import { resolveManifestCommandAliasOwner } from "../plugins/manifest-command-aliases.runtime.js";
1717import { hasMemoryRuntime } from "../plugins/memory-state.js";
18+import { createCliProgress } from "./progress.js";
1819import { maybeWarnAboutDebugProxyCoverage } from "../proxy-capture/coverage.js";
1920import {
2021finalizeDebugProxyCapture,
@@ -248,7 +249,25 @@ export async function runCli(argv: string[] = process.argv) {
248249return;
249250}
250251const { runCrestodian } = await import("../crestodian/crestodian.js");
251-await runCrestodian();
252+const progress = createCliProgress({
253+label: "Starting Crestodian…",
254+indeterminate: true,
255+delayMs: 0,
256+fallback: "none",
257+});
258+let progressStopped = false;
259+const stopProgress = () => {
260+if (progressStopped) {
261+return;
262+}
263+progressStopped = true;
264+progress.done();
265+};
266+try {
267+await runCrestodian({ onReady: stopProgress });
268+} finally {
269+stopProgress();
270+}
252271return;
253272}
254273@@ -268,95 +287,116 @@ export async function runCli(argv: string[] = process.argv) {
268287return;
269288}
270289271-// Capture all console output into structured logs while keeping stdout/stderr behavior.
272-enableConsoleCapture();
290+const startupProgress = createCliProgress({
291+label: "Loading OpenClaw CLI…",
292+indeterminate: true,
293+delayMs: 0,
294+fallback: "none",
295+});
296+let startupProgressStopped = false;
297+const stopStartupProgress = () => {
298+if (startupProgressStopped) {
299+return;
300+}
301+startupProgressStopped = true;
302+startupProgress.done();
303+};
304+305+try {
306+// Capture all console output into structured logs while keeping stdout/stderr behavior.
307+enableConsoleCapture();
273308274-const [
275-{ buildProgram },
276-{ runFatalErrorHooks },
277-{ installUnhandledRejectionHandler },
278-{ restoreTerminalState },
279-] = await Promise.all([
280-import("./program.js"),
281-import("../infra/fatal-error-hooks.js"),
282-import("../infra/unhandled-rejections.js"),
283-import("../terminal/restore.js"),
284-]);
285-const program = buildProgram();
309+ const [
310+ { buildProgram },
311+ { runFatalErrorHooks },
312+ { installUnhandledRejectionHandler },
313+ { restoreTerminalState },
314+ ] = await Promise.all([
315+ import("./program.js"),
316+ import("../infra/fatal-error-hooks.js"),
317+ import("../infra/unhandled-rejections.js"),
318+ import("../terminal/restore.js"),
319+ ]);
320+ const program = buildProgram();
286321287-// Global error handlers to prevent silent crashes from unhandled rejections/exceptions.
288-// These log the error and exit gracefully instead of crashing without trace.
289-installUnhandledRejectionHandler();
322+ // Global error handlers to prevent silent crashes from unhandled rejections/exceptions.
323+ // These log the error and exit gracefully instead of crashing without trace.
324+ installUnhandledRejectionHandler();
290325291-process.on("uncaughtException", (error) => {
292-console.error("[openclaw] Uncaught exception:", formatUncaughtError(error));
293-for (const message of runFatalErrorHooks({ reason: "uncaught_exception", error })) {
294-console.error("[openclaw]", message);
295-}
296-restoreTerminalState("uncaught exception", { resumeStdinIfPaused: false });
297-process.exit(1);
298-});
326+ process.on("uncaughtException", (error) => {
327+ console.error("[openclaw] Uncaught exception:", formatUncaughtError(error));
328+ for (const message of runFatalErrorHooks({ reason: "uncaught_exception", error })) {
329+ console.error("[openclaw]", message);
330+ }
331+ restoreTerminalState("uncaught exception", { resumeStdinIfPaused: false });
332+ process.exit(1);
333+ });
299334300-const parseArgv = rewriteUpdateFlagArgv(normalizedArgv);
301-const invocation = resolveCliArgvInvocation(parseArgv);
302-// Register the primary command (builtin or subcli) so help and command parsing
303-// are correct even with lazy command registration.
304-const { primary } = invocation;
305-if (primary && shouldRegisterPrimaryCommandOnly(parseArgv)) {
306-const { getProgramContext } = await import("./program/program-context.js");
307-const ctx = getProgramContext(program);
308-if (ctx) {
309-const { registerCoreCliByName } = await import("./program/command-registry.js");
310-await registerCoreCliByName(program, ctx, primary, parseArgv);
335+const parseArgv = rewriteUpdateFlagArgv(normalizedArgv);
336+const invocation = resolveCliArgvInvocation(parseArgv);
337+// Register the primary command (builtin or subcli) so help and command parsing
338+// are correct even with lazy command registration.
339+const { primary } = invocation;
340+if (primary && shouldRegisterPrimaryCommandOnly(parseArgv)) {
341+const { getProgramContext } = await import("./program/program-context.js");
342+const ctx = getProgramContext(program);
343+if (ctx) {
344+const { registerCoreCliByName } = await import("./program/command-registry.js");
345+await registerCoreCliByName(program, ctx, primary, parseArgv);
346+}
347+const { registerSubCliByName } = await import("./program/register.subclis.js");
348+await registerSubCliByName(program, primary);
311349}
312-const { registerSubCliByName } = await import("./program/register.subclis.js");
313-await registerSubCliByName(program, primary);
314-}
315350316-const hasBuiltinPrimary =
317-primary !== null &&
318-program.commands.some(
319-(command) => command.name() === primary || command.aliases().includes(primary),
320-);
321-const shouldSkipPluginRegistration = shouldSkipPluginCommandRegistration({
322-argv: parseArgv,
323- primary,
324- hasBuiltinPrimary,
325-});
326-if (!shouldSkipPluginRegistration) {
327-// Register plugin CLI commands before parsing
328-const { registerPluginCliCommandsFromValidatedConfig } = await import("../plugins/cli.js");
329-const config = await registerPluginCliCommandsFromValidatedConfig(
330-program,
331-undefined,
332-undefined,
333-{
334-mode: "lazy",
335- primary,
336-},
337-);
338-if (config) {
339-if (
340-primary &&
341-!program.commands.some(
342-(command) => command.name() === primary || command.aliases().includes(primary),
343-)
344-) {
345-const missingPluginCommandMessage = resolveMissingPluginCommandMessage(primary, config);
346-if (missingPluginCommandMessage) {
347-throw new Error(missingPluginCommandMessage);
351+const hasBuiltinPrimary =
352+primary !== null &&
353+program.commands.some(
354+(command) => command.name() === primary || command.aliases().includes(primary),
355+);
356+const shouldSkipPluginRegistration = shouldSkipPluginCommandRegistration({
357+argv: parseArgv,
358+ primary,
359+ hasBuiltinPrimary,
360+});
361+if (!shouldSkipPluginRegistration) {
362+// Register plugin CLI commands before parsing
363+const { registerPluginCliCommandsFromValidatedConfig } = await import("../plugins/cli.js");
364+const config = await registerPluginCliCommandsFromValidatedConfig(
365+program,
366+undefined,
367+undefined,
368+{
369+mode: "lazy",
370+ primary,
371+},
372+);
373+if (config) {
374+if (
375+primary &&
376+!program.commands.some(
377+(command) => command.name() === primary || command.aliases().includes(primary),
378+)
379+) {
380+const missingPluginCommandMessage = resolveMissingPluginCommandMessage(primary, config);
381+if (missingPluginCommandMessage) {
382+throw new Error(missingPluginCommandMessage);
383+}
348384}
349385}
350386}
351-}
352387353-try {
354-await program.parseAsync(parseArgv);
355-} catch (error) {
356-if (!(error instanceof CommanderError)) {
357-throw error;
388+stopStartupProgress();
389+390+try {
391+await program.parseAsync(parseArgv);
392+} catch (error) {
393+if (!(error instanceof CommanderError)) {
394+throw error;
395+}
396+process.exitCode = error.exitCode;
358397}
359-process.exitCode = error.exitCode;
398+} finally {
399+stopStartupProgress();
360400}
361401} finally {
362402await closeCliMemoryManagers();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。