perf(status): defer heavy startup loading

This commit is contained in:
Peter Steinberger
2026-03-15 18:20:37 -07:00
parent 9c89a74f84
commit 83ee5c0328
11 changed files with 334 additions and 16 deletions

View File

@@ -190,6 +190,19 @@ describe("registerPreActionHooks", () => {
});
it("applies --json stdout suppression only for explicit JSON output commands", async () => {
await runPreAction({
parseArgv: ["status"],
processArgv: ["node", "openclaw", "status", "--json"],
});
expect(ensureConfigReadyMock).toHaveBeenCalledWith({
runtime: runtimeMock,
commandPath: ["status"],
suppressDoctorStdout: true,
});
expect(ensurePluginRegistryLoadedMock).not.toHaveBeenCalled();
vi.clearAllMocks();
await runPreAction({
parseArgv: ["update", "status", "--json"],
processArgv: ["node", "openclaw", "update", "status", "--json"],
@@ -200,6 +213,7 @@ describe("registerPreActionHooks", () => {
commandPath: ["update", "status"],
suppressDoctorStdout: true,
});
expect(ensurePluginRegistryLoadedMock).not.toHaveBeenCalled();
vi.clearAllMocks();
await runPreAction({

View File

@@ -71,6 +71,16 @@ function resolvePluginRegistryScope(commandPath: string[]): "channels" | "all" {
return commandPath[0] === "status" || commandPath[0] === "health" ? "channels" : "all";
}
function shouldLoadPluginsForCommand(commandPath: string[], argv: string[]): boolean {
if (!PLUGIN_REQUIRED_COMMANDS.has(commandPath[0])) {
return false;
}
if ((commandPath[0] === "status" || commandPath[0] === "health") && hasFlag(argv, "--json")) {
return false;
}
return true;
}
function getRootCommand(command: Command): Command {
let current = command;
while (current.parent) {
@@ -138,7 +148,7 @@ export function registerPreActionHooks(program: Command, programVersion: string)
...(suppressDoctorStdout ? { suppressDoctorStdout: true } : {}),
});
// Load plugins for commands that need channel access
if (PLUGIN_REQUIRED_COMMANDS.has(commandPath[0])) {
if (shouldLoadPluginsForCommand(commandPath, argv)) {
const { ensurePluginRegistryLoaded } = await loadPluginRegistryModule();
ensurePluginRegistryLoaded({ scope: resolvePluginRegistryScope(commandPath) });
}

View File

@@ -34,9 +34,9 @@ const routeHealth: RouteSpec = {
const routeStatus: RouteSpec = {
match: (path) => path[0] === "status",
// Status runs security audit with channel checks in both text and JSON output,
// so plugin registry must be ready for consistent findings.
loadPlugins: true,
// `status --json` can defer channel plugin loading until config/env inspection
// proves it is needed, which keeps the fast-path startup lightweight.
loadPlugins: (argv) => !hasFlag(argv, "--json"),
run: async (argv) => {
const json = hasFlag(argv, "--json");
const deep = hasFlag(argv, "--deep");