mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 04:47:03 -06:00
perf(plugins): list from installed metadata (#117900)
This commit is contained in:
committed by
GitHub
parent
f5c985d92c
commit
76ec39556e
@@ -44,7 +44,8 @@ export async function runPluginsListCommand(
|
||||
runtime: RuntimeEnv = defaultRuntime,
|
||||
): Promise<void> {
|
||||
const { buildPluginRegistrySnapshotReport } = await import("../plugins/status-snapshot.js");
|
||||
const cfg = getRuntimeConfig();
|
||||
// The inventory projector owns plugin metadata validation from the installed index.
|
||||
const cfg = getRuntimeConfig({ skipPluginValidation: true });
|
||||
const report = buildPluginRegistrySnapshotReport({
|
||||
config: cfg,
|
||||
...(opts.json ? { logger: quietPluginJsonLogger } : {}),
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
// The default plugin inventory route must render manifest facts without executing plugin code.
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterAll, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import {
|
||||
createColdPluginFixture,
|
||||
isColdPluginRuntimeLoaded,
|
||||
} from "../plugins/test-helpers/cold-plugin-fixtures.js";
|
||||
|
||||
const testState = vi.hoisted(() => ({
|
||||
config: {} as OpenClawConfig,
|
||||
loadedModules: new Set<string>(),
|
||||
logs: [] as string[],
|
||||
}));
|
||||
|
||||
vi.mock("./command-execution-startup.js", () => ({
|
||||
applyCliExecutionStartupPresentation: vi.fn(async () => {}),
|
||||
ensureCliExecutionBootstrap: vi.fn(async () => {}),
|
||||
resolveCliExecutionStartupContext: vi.fn(() => ({
|
||||
startupPolicy: { loadPlugins: false, suppressDoctorStdout: true },
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("../config/config.js", () => ({
|
||||
getRuntimeConfig: () => testState.config,
|
||||
}));
|
||||
|
||||
vi.mock("../runtime.js", () => ({
|
||||
defaultRuntime: {
|
||||
error: vi.fn(),
|
||||
exit: vi.fn(),
|
||||
log: (...args: unknown[]) => testState.logs.push(args.map(String).join(" ")),
|
||||
writeJson: vi.fn(),
|
||||
writeStdout: vi.fn(),
|
||||
},
|
||||
writeRuntimeJson: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./plugins-cli.js", () => {
|
||||
testState.loadedModules.add("plugins-cli");
|
||||
return { registerPluginsCli: vi.fn() };
|
||||
});
|
||||
|
||||
vi.mock("../plugins/loader-module-runtime.js", () => {
|
||||
testState.loadedModules.add("plugin-module-runtime");
|
||||
return {};
|
||||
});
|
||||
|
||||
import { tryRouteCli } from "./route.js";
|
||||
|
||||
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-plugins-list-route-"));
|
||||
|
||||
afterAll(() => {
|
||||
fs.rmSync(tempRoot, { recursive: true, force: true });
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
it("renders the default list from metadata without loading plugin modules", async () => {
|
||||
const pluginRoot = path.join(tempRoot, "route-demo");
|
||||
fs.mkdirSync(pluginRoot, { recursive: true });
|
||||
const fixture = createColdPluginFixture({
|
||||
rootDir: pluginRoot,
|
||||
pluginId: "route-demo",
|
||||
packageName: "@example/route-demo",
|
||||
packageVersion: "4.2.0",
|
||||
manifest: {
|
||||
name: "Route Demo",
|
||||
description: "Prepared inventory metadata",
|
||||
},
|
||||
});
|
||||
const bundledRoot = path.join(tempRoot, "bundled");
|
||||
fs.mkdirSync(bundledRoot, { recursive: true });
|
||||
vi.stubEnv("OPENCLAW_BUNDLED_PLUGINS_DIR", bundledRoot);
|
||||
vi.stubEnv("OPENCLAW_DISABLE_BUNDLED_PLUGINS", "1");
|
||||
vi.stubEnv("OPENCLAW_HOME", path.join(tempRoot, "home"));
|
||||
vi.stubEnv("OPENCLAW_STATE_DIR", path.join(tempRoot, "state"));
|
||||
testState.config = {
|
||||
plugins: {
|
||||
load: { paths: [pluginRoot] },
|
||||
entries: { "route-demo": { enabled: true } },
|
||||
},
|
||||
};
|
||||
|
||||
await expect(tryRouteCli(["node", "openclaw", "plugins", "list"])).resolves.toBe(true);
|
||||
|
||||
const output = testState.logs.join("\n");
|
||||
expect(output).toContain("Route Demo");
|
||||
expect(output).toContain("route-demo");
|
||||
expect(output).toContain("Prepared inventory metadata");
|
||||
expect(output).toContain("4.2.0");
|
||||
expect(isColdPluginRuntimeLoaded(fixture)).toBe(false);
|
||||
expect(testState.loadedModules).not.toContain("plugin-module-runtime");
|
||||
expect(testState.loadedModules).not.toContain("plugins-cli");
|
||||
});
|
||||
@@ -449,11 +449,8 @@ export function parseChannelsStatusRouteArgs(argv: string[]) {
|
||||
};
|
||||
}
|
||||
|
||||
/** Parse JSON-only `openclaw plugins list` flags for plugin inventory output. */
|
||||
/** Parse `openclaw plugins list` flags for the metadata-only inventory path. */
|
||||
export function parsePluginsListRouteArgs(argv: string[]) {
|
||||
if (!hasFlag(argv, "--json")) {
|
||||
return null;
|
||||
}
|
||||
const positionals = getRoutedCommandPositionals(argv, {
|
||||
commandPath: ["plugins", "list"],
|
||||
booleanFlags: ["--json", "--enabled", "--verbose"],
|
||||
@@ -462,7 +459,7 @@ export function parsePluginsListRouteArgs(argv: string[]) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
json: true as const,
|
||||
json: hasFlag(argv, "--json"),
|
||||
enabled: hasFlag(argv, "--enabled"),
|
||||
verbose: hasFlag(argv, "--verbose"),
|
||||
};
|
||||
|
||||
@@ -169,27 +169,41 @@ describe("program routes", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("routes plugins list JSON without importing the full plugins CLI", async () => {
|
||||
const route = expectRoute(["plugins", "list"]);
|
||||
expect(route.loadPlugins).toBeUndefined();
|
||||
expect(route.canRun?.(["node", "openclaw", "plugins", "list"])).toBe(false);
|
||||
it.each([
|
||||
{ label: "default", flags: [], options: { json: false, enabled: false, verbose: false } },
|
||||
{
|
||||
label: "enabled",
|
||||
flags: ["--enabled"],
|
||||
options: { json: false, enabled: true, verbose: false },
|
||||
},
|
||||
{
|
||||
label: "verbose",
|
||||
flags: ["--verbose"],
|
||||
options: { json: false, enabled: false, verbose: true },
|
||||
},
|
||||
{
|
||||
label: "JSON",
|
||||
flags: ["--json", "--enabled", "--verbose"],
|
||||
options: { json: true, enabled: true, verbose: true },
|
||||
},
|
||||
])(
|
||||
"routes plugins list $label without importing the full plugins CLI",
|
||||
async ({ flags, options }) => {
|
||||
const route = expectRoute(["plugins", "list"]);
|
||||
expect(route.loadPlugins).toBeUndefined();
|
||||
expect(route.canRun?.(["node", "openclaw", "plugins", "list", ...flags])).toBe(true);
|
||||
|
||||
await expect(
|
||||
route.run(["node", "openclaw", "plugins", "list", "--json", "--enabled", "--verbose"]),
|
||||
).resolves.toBe(true);
|
||||
await expect(route.run(["node", "openclaw", "plugins", "list", ...flags])).resolves.toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
expect(runPluginsListCommandMock).toHaveBeenCalledWith(
|
||||
{ json: true, enabled: true, verbose: true },
|
||||
defaultRuntime,
|
||||
);
|
||||
expect(pluginsCliLoadedMock).not.toHaveBeenCalled();
|
||||
});
|
||||
expect(runPluginsListCommandMock).toHaveBeenCalledWith(options, defaultRuntime);
|
||||
expect(pluginsCliLoadedMock).not.toHaveBeenCalled();
|
||||
},
|
||||
);
|
||||
|
||||
it("returns false for plugins list JSON route with unsupported arguments", async () => {
|
||||
await expectRunFalse(
|
||||
["plugins", "list"],
|
||||
["node", "openclaw", "plugins", "list", "--json", "--wat"],
|
||||
);
|
||||
it("returns false for plugins list route with unsupported arguments", async () => {
|
||||
await expectRunFalse(["plugins", "list"], ["node", "openclaw", "plugins", "list", "--wat"]);
|
||||
});
|
||||
|
||||
it("matches gateway status route without plugin preload", () => {
|
||||
|
||||
Reference in New Issue
Block a user