fix: scope plugin inspect runtime loading

This commit is contained in:
Shakker
2026-04-28 10:42:17 +01:00
parent 1bd4b7ac4d
commit 7727e102a5
4 changed files with 56 additions and 9 deletions

View File

@@ -37,6 +37,7 @@ Docs: https://docs.openclaw.ai
- Plugins/startup: precompute bundled runtime mirror fingerprints before taking the mirror lock and keep Docker bundled plugin runtime deps/mirrors in a Docker-managed volume instead of the Windows/WSL config bind mount, so cold starts avoid slow host-volume mirror writes. Fixes #73339. Thanks @1yihui.
- Channels/LINE: persist inbound image, video, audio, and file downloads in `~/.openclaw/media/inbound/` instead of temporary files so agents can still read LINE media after `/tmp` cleanup. Fixes #73370. Thanks @hijirii and @wenxu007.
- CLI/plugins: keep bundled plugin installs out of `plugins.load.paths` while preserving install records, so install/inspect/doctor loops no longer warn about the current bundled plugin directory. Thanks @vincentkoc.
- CLI/plugins: scope `plugins inspect <id>` runtime loading to the matched plugin so single-plugin inspection does not load every plugin before checking the target. Thanks @shakkernerd.
- CLI/plugins: remove managed copied-path plugin directories during uninstall and plan uninstall from metadata instead of runtime-loading plugins, so plugin lifecycle commands avoid unnecessary bundled runtime-deps work. Thanks @shakkernerd.
- Cron tool: infer the creating session's agentId for `cron.add` jobs when `agentId` is omitted or passed as undefined, keeping scheduled agentTurn jobs routed to the session agent; #40571 identified the guard bug and supplied the focused regression coverage. Thanks @ChanningYul.
- Cron/Telegram: add `--thread-id` to `openclaw cron add` and `openclaw cron edit`, preserving Telegram forum topic delivery targets across scheduled announcements. Carries forward #51581, #60373, and #60890. Thanks @ChunHao-dev.

View File

@@ -4,10 +4,12 @@ import {
buildPluginDiagnosticsReport,
buildPluginInspectReport,
buildPluginRegistrySnapshotReport,
buildPluginSnapshotReport,
inspectPluginRegistry,
resetPluginsCliTestState,
refreshPluginRegistry,
runPluginsCommand,
runtimeErrors,
runtimeLogs,
} from "./plugins-cli-test-helpers.js";
@@ -119,6 +121,10 @@ describe("plugins cli list", () => {
});
it("shows conversation-access hook policy in inspect output", async () => {
buildPluginSnapshotReport.mockReturnValue({
plugins: [createPluginRecord({ id: "openclaw-mem0", name: "Mem0" })],
diagnostics: [],
});
buildPluginInspectReport.mockReturnValue({
workspaceDir: "/workspace",
plugin: createPluginRecord({ id: "openclaw-mem0", name: "Mem0" }),
@@ -150,7 +156,26 @@ describe("plugins cli list", () => {
await runPluginsCommand(["plugins", "inspect", "openclaw-mem0"]);
expect(buildPluginDiagnosticsReport).toHaveBeenCalledWith({
config: {},
onlyPluginIds: ["openclaw-mem0"],
});
expect(runtimeLogs.join("\n")).toContain("Policy");
expect(runtimeLogs.join("\n")).toContain("allowConversationAccess: true");
});
it("does not runtime-load plugins when inspect target is missing", async () => {
buildPluginSnapshotReport.mockReturnValue({
plugins: [],
diagnostics: [],
});
await expect(runPluginsCommand(["plugins", "inspect", "missing-plugin"])).rejects.toThrow(
"__exit__:1",
);
expect(buildPluginSnapshotReport).toHaveBeenCalledWith({ config: {} });
expect(buildPluginDiagnosticsReport).not.toHaveBeenCalled();
expect(runtimeErrors.at(-1)).toContain("Plugin not found: missing-plugin");
});
});

View File

@@ -259,24 +259,26 @@ export function registerPluginsCli(program: Command) {
buildAllPluginInspectReports,
buildPluginDiagnosticsReport,
buildPluginInspectReport,
buildPluginSnapshotReport,
formatPluginCompatibilityNotice,
} = await import("../plugins/status.js");
const { loadInstalledPluginIndexInstallRecords } =
await import("../plugins/installed-plugin-index-records.js");
const cfg = getRuntimeConfig();
const installRecords = await loadInstalledPluginIndexInstallRecords();
const report = buildPluginDiagnosticsReport({
config: cfg,
...(opts.json ? { logger: quietPluginJsonLogger } : {}),
});
const loggerParams = opts.json ? { logger: quietPluginJsonLogger } : {};
if (opts.all) {
if (id) {
defaultRuntime.error("Pass either a plugin id or --all, not both.");
return defaultRuntime.exit(1);
}
const report = buildPluginDiagnosticsReport({
config: cfg,
...loggerParams,
});
const inspectAll = buildAllPluginInspectReports({
config: cfg,
...(opts.json ? { logger: quietPluginJsonLogger } : {}),
...loggerParams,
report,
});
const inspectAllWithInstall = inspectAll.map((inspect) => ({
@@ -342,10 +344,26 @@ export function registerPluginsCli(program: Command) {
return defaultRuntime.exit(1);
}
const inspect = buildPluginInspectReport({
id,
const snapshotReport = buildPluginSnapshotReport({
config: cfg,
...(opts.json ? { logger: quietPluginJsonLogger } : {}),
...loggerParams,
});
const targetPlugin = snapshotReport.plugins.find(
(entry) => entry.id === id || entry.name === id,
);
if (!targetPlugin) {
defaultRuntime.error(`Plugin not found: ${id}`);
return defaultRuntime.exit(1);
}
const report = buildPluginDiagnosticsReport({
config: cfg,
...loggerParams,
onlyPluginIds: [targetPlugin.id],
});
const inspect = buildPluginInspectReport({
id: targetPlugin.id,
config: cfg,
...loggerParams,
report,
});
if (!inspect) {

View File

@@ -161,6 +161,7 @@ function resolveReportedPluginVersion(
type PluginReportParams = {
config?: OpenClawConfig;
effectiveOnly?: boolean;
onlyPluginIds?: readonly string[];
workspaceDir?: string;
/** Use an explicit env when plugin roots should resolve independently from process.env. */
env?: NodeJS.ProcessEnv;
@@ -295,7 +296,9 @@ function buildPluginReport(
workspaceDir,
env: params?.env ?? process.env,
})
: undefined;
: params?.onlyPluginIds === undefined
? undefined
: [...params.onlyPluginIds];
const registry = loadModules
? loadOpenClawPlugins(