import { Command } from "commander"; import type { Mock } from "vitest"; import { vi } from "vitest"; import { getRuntimeConfig } from "../config/config.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { PluginInstallRecord } from "../config/types.plugins.js"; import { createEmptyUninstallActions } from "../plugins/uninstall.js"; import { createCliRuntimeCapture } from "./test-runtime-capture.js"; type UnknownMock = Mock<(...args: unknown[]) => unknown>; type AsyncUnknownMock = Mock<(...args: unknown[]) => Promise>; type LoadConfigFn = (typeof import("../config/config.js"))["loadConfig"]; type ParseClawHubPluginSpecFn = (typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"]; type InstallPluginFromMarketplaceFn = (typeof import("../plugins/marketplace.js"))["installPluginFromMarketplace"]; type ListMarketplacePluginsFn = (typeof import("../plugins/marketplace.js"))["listMarketplacePlugins"]; type ResolveMarketplaceInstallShortcutFn = (typeof import("../plugins/marketplace.js"))["resolveMarketplaceInstallShortcut"]; type PluginInstallRecordMap = Record; let mockInstalledPluginIndexInstallRecords: PluginInstallRecordMap = {}; function clonePluginInstallRecords(records: PluginInstallRecordMap): PluginInstallRecordMap { return structuredClone(records); } // oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves mock call and result types. function invokeMock(mock: unknown, ...args: TArgs): TResult { return (mock as (...args: TArgs) => TResult)(...args); } export const loadConfig: Mock = vi.fn(() => ({}) as OpenClawConfig); export const readConfigFileSnapshot: AsyncUnknownMock = vi.fn(); export const writeConfigFile: AsyncUnknownMock = vi.fn(async () => undefined); export const replaceConfigFile: AsyncUnknownMock = vi.fn( async (params: { nextConfig: OpenClawConfig }) => await writeConfigFile(params.nextConfig), ) as AsyncUnknownMock; export const resolveStateDir: Mock<() => string> = vi.fn(() => "/tmp/openclaw-state"); export const installPluginFromMarketplace: Mock = vi.fn(); export const listMarketplacePlugins: Mock = vi.fn(); export const resolveMarketplaceInstallShortcut: Mock = vi.fn(); export const enablePluginInConfig: UnknownMock = vi.fn(); export const recordPluginInstall: UnknownMock = vi.fn(); export const loadInstalledPluginIndexInstallRecords: AsyncUnknownMock = vi.fn(async () => clonePluginInstallRecords(mockInstalledPluginIndexInstallRecords), ); export const writePersistedInstalledPluginIndexInstallRecords: AsyncUnknownMock = vi.fn( async (records: unknown) => { mockInstalledPluginIndexInstallRecords = clonePluginInstallRecords( (records ?? {}) as PluginInstallRecordMap, ); }, ); export const clearPluginManifestRegistryCache: UnknownMock = vi.fn(); export const loadPluginManifestRegistry: UnknownMock = vi.fn(); export const buildPluginSnapshotReport: UnknownMock = vi.fn(); export const buildPluginRegistrySnapshotReport: UnknownMock = vi.fn(); export const buildPluginInspectReport: UnknownMock = vi.fn(); export const buildAllPluginInspectReports: UnknownMock = vi.fn(); export const buildPluginDiagnosticsReport: UnknownMock = vi.fn(); export const buildPluginCompatibilityNotices: UnknownMock = vi.fn(); export const inspectPluginRegistry: AsyncUnknownMock = vi.fn(); export const refreshPluginRegistry: AsyncUnknownMock = vi.fn(); export const applyExclusiveSlotSelection: UnknownMock = vi.fn(); export const planPluginUninstall: UnknownMock = vi.fn(); export const applyPluginUninstallDirectoryRemoval: AsyncUnknownMock = vi.fn(); export const uninstallPlugin: AsyncUnknownMock = vi.fn(); export const updateNpmInstalledPlugins: AsyncUnknownMock = vi.fn(); export const updateNpmInstalledHookPacks: AsyncUnknownMock = vi.fn(); export const promptYesNo: AsyncUnknownMock = vi.fn(); export const installPluginFromNpmSpec: AsyncUnknownMock = vi.fn(); export const installPluginFromPath: AsyncUnknownMock = vi.fn(); export const installPluginFromClawHub: AsyncUnknownMock = vi.fn(); export const parseClawHubPluginSpec: Mock = vi.fn(); export const installHooksFromNpmSpec: AsyncUnknownMock = vi.fn(); export const installHooksFromPath: AsyncUnknownMock = vi.fn(); export const recordHookInstall: UnknownMock = vi.fn(); const { defaultRuntime, runtimeLogs, runtimeErrors, resetRuntimeCapture } = createCliRuntimeCapture(); export { runtimeErrors, runtimeLogs }; export function setInstalledPluginIndexInstallRecords(records: PluginInstallRecordMap): void { mockInstalledPluginIndexInstallRecords = clonePluginInstallRecords(records); } function restoreRuntimeCaptureMocks() { defaultRuntime.log.mockReset(); defaultRuntime.log.mockImplementation((...args: unknown[]) => { runtimeLogs.push(args.map((value) => String(value)).join(" ")); }); defaultRuntime.error.mockReset(); defaultRuntime.error.mockImplementation((...args: unknown[]) => { runtimeErrors.push(args.map((value) => String(value)).join(" ")); }); defaultRuntime.writeStdout.mockReset(); defaultRuntime.writeStdout.mockImplementation((value: string) => { defaultRuntime.log(value.endsWith("\n") ? value.slice(0, -1) : value); }); defaultRuntime.writeJson.mockReset(); defaultRuntime.writeJson.mockImplementation((value: unknown, space = 2) => { defaultRuntime.log(JSON.stringify(value, null, space > 0 ? space : undefined)); }); defaultRuntime.exit.mockReset(); defaultRuntime.exit.mockImplementation((code: number) => { throw new Error(`__exit__:${code}`); }); } vi.mock("../runtime.js", () => ({ defaultRuntime, })); vi.mock("../config/config.js", () => ({ getRuntimeConfig: () => loadConfig(), loadConfig: () => loadConfig(), readConfigFileSnapshot: (( ...args: Parameters<(typeof import("../config/config.js"))["readConfigFileSnapshot"]> ) => invokeMock< Parameters<(typeof import("../config/config.js"))["readConfigFileSnapshot"]>, ReturnType<(typeof import("../config/config.js"))["readConfigFileSnapshot"]> >( readConfigFileSnapshot, ...args, )) as (typeof import("../config/config.js"))["readConfigFileSnapshot"], writeConfigFile: ((config: OpenClawConfig) => invokeMock< [OpenClawConfig], ReturnType<(typeof import("../config/config.js"))["writeConfigFile"]> >(writeConfigFile, config)) as (typeof import("../config/config.js"))["writeConfigFile"], replaceConfigFile: (( params: Parameters<(typeof import("../config/config.js"))["replaceConfigFile"]>[0], ) => invokeMock< [Parameters<(typeof import("../config/config.js"))["replaceConfigFile"]>[0]], ReturnType<(typeof import("../config/config.js"))["replaceConfigFile"]> >(replaceConfigFile, params)) as (typeof import("../config/config.js"))["replaceConfigFile"], })); vi.mock("../config/paths.js", () => ({ resolveStateDir: () => resolveStateDir(), })); vi.mock("../plugins/marketplace.js", () => ({ installPluginFromMarketplace: ((...args: Parameters) => installPluginFromMarketplace(...args)) as InstallPluginFromMarketplaceFn, listMarketplacePlugins: ((...args: Parameters) => listMarketplacePlugins(...args)) as ListMarketplacePluginsFn, resolveMarketplaceInstallShortcut: ((...args: Parameters) => resolveMarketplaceInstallShortcut(...args)) as ResolveMarketplaceInstallShortcutFn, })); vi.mock("../plugins/enable.js", () => ({ enablePluginInConfig: ((cfg: OpenClawConfig, pluginId: string) => invokeMock<[OpenClawConfig, string], unknown>( enablePluginInConfig, cfg, pluginId, )) as (typeof import("../plugins/enable.js"))["enablePluginInConfig"], })); vi.mock("../plugins/installs.js", () => ({ recordPluginInstall: (( ...args: Parameters<(typeof import("../plugins/installs.js"))["recordPluginInstall"]> ) => invokeMock< Parameters<(typeof import("../plugins/installs.js"))["recordPluginInstall"]>, ReturnType<(typeof import("../plugins/installs.js"))["recordPluginInstall"]> >( recordPluginInstall, ...args, )) as (typeof import("../plugins/installs.js"))["recordPluginInstall"], })); vi.mock("../plugins/installed-plugin-index-records.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, loadInstalledPluginIndexInstallRecords: ((...args: unknown[]) => invokeMock(loadInstalledPluginIndexInstallRecords, ...args)) as ( ...args: unknown[] ) => unknown, writePersistedInstalledPluginIndexInstallRecords: ((...args: unknown[]) => invokeMock( writePersistedInstalledPluginIndexInstallRecords, ...args, )) as (...args: unknown[]) => unknown, recordPluginInstallInRecords: ( records: Record, update: { pluginId: string; installedAt?: string } & Record, ) => { const { pluginId, ...record } = update; return { ...records, [pluginId]: { ...(records[pluginId] as Record | undefined), ...record, installedAt: update.installedAt ?? "2026-04-25T00:00:00.000Z", }, }; }, }; }); vi.mock("../plugins/manifest-registry.js", () => ({ clearPluginManifestRegistryCache: () => clearPluginManifestRegistryCache(), loadPluginManifestRegistry: ((...args: unknown[]) => invokeMock(loadPluginManifestRegistry, ...args)) as ( ...args: unknown[] ) => unknown, })); vi.mock("../plugins/status.js", () => ({ buildPluginSnapshotReport: (( ...args: Parameters<(typeof import("../plugins/status.js"))["buildPluginSnapshotReport"]> ) => invokeMock< Parameters<(typeof import("../plugins/status.js"))["buildPluginSnapshotReport"]>, ReturnType<(typeof import("../plugins/status.js"))["buildPluginSnapshotReport"]> >( buildPluginSnapshotReport, ...args, )) as (typeof import("../plugins/status.js"))["buildPluginSnapshotReport"], buildPluginRegistrySnapshotReport: (( ...args: Parameters< (typeof import("../plugins/status.js"))["buildPluginRegistrySnapshotReport"] > ) => invokeMock< Parameters<(typeof import("../plugins/status.js"))["buildPluginRegistrySnapshotReport"]>, ReturnType<(typeof import("../plugins/status.js"))["buildPluginRegistrySnapshotReport"]> >( buildPluginRegistrySnapshotReport, ...args, )) as (typeof import("../plugins/status.js"))["buildPluginRegistrySnapshotReport"], buildPluginInspectReport: (( ...args: Parameters<(typeof import("../plugins/status.js"))["buildPluginInspectReport"]> ) => invokeMock< Parameters<(typeof import("../plugins/status.js"))["buildPluginInspectReport"]>, ReturnType<(typeof import("../plugins/status.js"))["buildPluginInspectReport"]> >( buildPluginInspectReport, ...args, )) as (typeof import("../plugins/status.js"))["buildPluginInspectReport"], buildAllPluginInspectReports: (( ...args: Parameters<(typeof import("../plugins/status.js"))["buildAllPluginInspectReports"]> ) => invokeMock< Parameters<(typeof import("../plugins/status.js"))["buildAllPluginInspectReports"]>, ReturnType<(typeof import("../plugins/status.js"))["buildAllPluginInspectReports"]> >( buildAllPluginInspectReports, ...args, )) as (typeof import("../plugins/status.js"))["buildAllPluginInspectReports"], buildPluginDiagnosticsReport: (( ...args: Parameters<(typeof import("../plugins/status.js"))["buildPluginDiagnosticsReport"]> ) => invokeMock< Parameters<(typeof import("../plugins/status.js"))["buildPluginDiagnosticsReport"]>, ReturnType<(typeof import("../plugins/status.js"))["buildPluginDiagnosticsReport"]> >( buildPluginDiagnosticsReport, ...args, )) as (typeof import("../plugins/status.js"))["buildPluginDiagnosticsReport"], buildPluginCompatibilityNotices: (( ...args: Parameters<(typeof import("../plugins/status.js"))["buildPluginCompatibilityNotices"]> ) => invokeMock< Parameters<(typeof import("../plugins/status.js"))["buildPluginCompatibilityNotices"]>, ReturnType<(typeof import("../plugins/status.js"))["buildPluginCompatibilityNotices"]> >( buildPluginCompatibilityNotices, ...args, )) as (typeof import("../plugins/status.js"))["buildPluginCompatibilityNotices"], formatPluginCompatibilityNotice: (entry: { message: string }) => entry.message, })); vi.mock("../plugins/plugin-registry.js", () => ({ loadPluginManifestRegistryForPluginRegistry: () => ({ diagnostics: [], plugins: [] }), inspectPluginRegistry: (( ...args: Parameters<(typeof import("../plugins/plugin-registry.js"))["inspectPluginRegistry"]> ) => invokeMock< Parameters<(typeof import("../plugins/plugin-registry.js"))["inspectPluginRegistry"]>, ReturnType<(typeof import("../plugins/plugin-registry.js"))["inspectPluginRegistry"]> >( inspectPluginRegistry, ...args, )) as (typeof import("../plugins/plugin-registry.js"))["inspectPluginRegistry"], refreshPluginRegistry: (( ...args: Parameters<(typeof import("../plugins/plugin-registry.js"))["refreshPluginRegistry"]> ) => invokeMock< Parameters<(typeof import("../plugins/plugin-registry.js"))["refreshPluginRegistry"]>, ReturnType<(typeof import("../plugins/plugin-registry.js"))["refreshPluginRegistry"]> >( refreshPluginRegistry, ...args, )) as (typeof import("../plugins/plugin-registry.js"))["refreshPluginRegistry"], })); vi.mock("../plugins/slots.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, applyExclusiveSlotSelection: (( params: Parameters<(typeof import("../plugins/slots.js"))["applyExclusiveSlotSelection"]>[0], ) => invokeMock< [Parameters<(typeof import("../plugins/slots.js"))["applyExclusiveSlotSelection"]>[0]], ReturnType<(typeof import("../plugins/slots.js"))["applyExclusiveSlotSelection"]> >( applyExclusiveSlotSelection, params, )) as (typeof import("../plugins/slots.js"))["applyExclusiveSlotSelection"], }; }); vi.mock("../plugins/uninstall.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, planPluginUninstall: (( ...args: Parameters<(typeof import("../plugins/uninstall.js"))["planPluginUninstall"]> ) => invokeMock< Parameters<(typeof import("../plugins/uninstall.js"))["planPluginUninstall"]>, ReturnType<(typeof import("../plugins/uninstall.js"))["planPluginUninstall"]> >( planPluginUninstall, ...args, )) as (typeof import("../plugins/uninstall.js"))["planPluginUninstall"], applyPluginUninstallDirectoryRemoval: (( ...args: Parameters< (typeof import("../plugins/uninstall.js"))["applyPluginUninstallDirectoryRemoval"] > ) => invokeMock< Parameters< (typeof import("../plugins/uninstall.js"))["applyPluginUninstallDirectoryRemoval"] >, ReturnType< (typeof import("../plugins/uninstall.js"))["applyPluginUninstallDirectoryRemoval"] > >( applyPluginUninstallDirectoryRemoval, ...args, )) as (typeof import("../plugins/uninstall.js"))["applyPluginUninstallDirectoryRemoval"], uninstallPlugin: (( ...args: Parameters<(typeof import("../plugins/uninstall.js"))["uninstallPlugin"]> ) => invokeMock< Parameters<(typeof import("../plugins/uninstall.js"))["uninstallPlugin"]>, ReturnType<(typeof import("../plugins/uninstall.js"))["uninstallPlugin"]> >(uninstallPlugin, ...args)) as (typeof import("../plugins/uninstall.js"))["uninstallPlugin"], resolveUninstallDirectoryTarget: ({ installRecord, }: { installRecord?: { installPath?: string; sourcePath?: string }; }) => installRecord?.installPath ?? installRecord?.sourcePath ?? null, }; }); vi.mock("../plugins/update.js", () => ({ updateNpmInstalledPlugins: (( ...args: Parameters<(typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"]> ) => invokeMock< Parameters<(typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"]>, ReturnType<(typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"]> >( updateNpmInstalledPlugins, ...args, )) as (typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"], })); vi.mock("../hooks/update.js", () => ({ updateNpmInstalledHookPacks: (( ...args: Parameters<(typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"]> ) => invokeMock< Parameters<(typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"]>, ReturnType<(typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"]> >( updateNpmInstalledHookPacks, ...args, )) as (typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"], })); vi.mock("./prompt.js", () => ({ promptYesNo: ((...args: Parameters<(typeof import("./prompt.js"))["promptYesNo"]>) => invokeMock< Parameters<(typeof import("./prompt.js"))["promptYesNo"]>, ReturnType<(typeof import("./prompt.js"))["promptYesNo"]> >(promptYesNo, ...args)) as (typeof import("./prompt.js"))["promptYesNo"], })); vi.mock("../plugins/install.js", () => ({ PLUGIN_INSTALL_ERROR_CODE: { NPM_PACKAGE_NOT_FOUND: "npm_package_not_found", SECURITY_SCAN_BLOCKED: "security_scan_blocked", SECURITY_SCAN_FAILED: "security_scan_failed", }, installPluginFromNpmSpec: (( ...args: Parameters<(typeof import("../plugins/install.js"))["installPluginFromNpmSpec"]> ) => invokeMock< Parameters<(typeof import("../plugins/install.js"))["installPluginFromNpmSpec"]>, ReturnType<(typeof import("../plugins/install.js"))["installPluginFromNpmSpec"]> >( installPluginFromNpmSpec, ...args, )) as (typeof import("../plugins/install.js"))["installPluginFromNpmSpec"], installPluginFromPath: (( ...args: Parameters<(typeof import("../plugins/install.js"))["installPluginFromPath"]> ) => invokeMock< Parameters<(typeof import("../plugins/install.js"))["installPluginFromPath"]>, ReturnType<(typeof import("../plugins/install.js"))["installPluginFromPath"]> >( installPluginFromPath, ...args, )) as (typeof import("../plugins/install.js"))["installPluginFromPath"], })); vi.mock("../hooks/install.js", () => ({ installHooksFromNpmSpec: (( ...args: Parameters<(typeof import("../hooks/install.js"))["installHooksFromNpmSpec"]> ) => invokeMock< Parameters<(typeof import("../hooks/install.js"))["installHooksFromNpmSpec"]>, ReturnType<(typeof import("../hooks/install.js"))["installHooksFromNpmSpec"]> >( installHooksFromNpmSpec, ...args, )) as (typeof import("../hooks/install.js"))["installHooksFromNpmSpec"], installHooksFromPath: (( ...args: Parameters<(typeof import("../hooks/install.js"))["installHooksFromPath"]> ) => invokeMock< Parameters<(typeof import("../hooks/install.js"))["installHooksFromPath"]>, ReturnType<(typeof import("../hooks/install.js"))["installHooksFromPath"]> >( installHooksFromPath, ...args, )) as (typeof import("../hooks/install.js"))["installHooksFromPath"], resolveHookInstallDir: (hookId: string) => `/tmp/hooks/${hookId}`, })); vi.mock("../hooks/installs.js", () => ({ recordHookInstall: (( ...args: Parameters<(typeof import("../hooks/installs.js"))["recordHookInstall"]> ) => invokeMock< Parameters<(typeof import("../hooks/installs.js"))["recordHookInstall"]>, ReturnType<(typeof import("../hooks/installs.js"))["recordHookInstall"]> >(recordHookInstall, ...args)) as (typeof import("../hooks/installs.js"))["recordHookInstall"], })); vi.mock("../plugins/clawhub.js", () => ({ CLAWHUB_INSTALL_ERROR_CODE: { PACKAGE_NOT_FOUND: "package_not_found", VERSION_NOT_FOUND: "version_not_found", }, installPluginFromClawHub: (( ...args: Parameters<(typeof import("../plugins/clawhub.js"))["installPluginFromClawHub"]> ) => invokeMock< Parameters<(typeof import("../plugins/clawhub.js"))["installPluginFromClawHub"]>, ReturnType<(typeof import("../plugins/clawhub.js"))["installPluginFromClawHub"]> >( installPluginFromClawHub, ...args, )) as (typeof import("../plugins/clawhub.js"))["installPluginFromClawHub"], })); vi.mock("../infra/clawhub.js", () => ({ parseClawHubPluginSpec: (( ...args: Parameters<(typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"]> ) => invokeMock< Parameters<(typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"]>, ReturnType<(typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"]> >( parseClawHubPluginSpec, ...args, )) as (typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"], })); const { registerPluginsCli } = await import("./plugins-cli.js"); export { registerPluginsCli }; export async function runPluginsCommand(argv: string[]) { const program = new Command(); program.exitOverride(); vi.resetModules(); const { registerPluginsCli: registerPluginsCliFresh } = await import("./plugins-cli.js"); registerPluginsCliFresh(program); return await program.parseAsync(argv, { from: "user" }); } export function resetPluginsCliTestState() { resetRuntimeCapture(); restoreRuntimeCaptureMocks(); loadConfig.mockReset(); readConfigFileSnapshot.mockReset(); writeConfigFile.mockReset(); replaceConfigFile.mockReset(); resolveStateDir.mockReset(); installPluginFromMarketplace.mockReset(); listMarketplacePlugins.mockReset(); resolveMarketplaceInstallShortcut.mockReset(); enablePluginInConfig.mockReset(); recordPluginInstall.mockReset(); mockInstalledPluginIndexInstallRecords = {}; loadInstalledPluginIndexInstallRecords.mockReset(); writePersistedInstalledPluginIndexInstallRecords.mockReset(); clearPluginManifestRegistryCache.mockReset(); loadPluginManifestRegistry.mockReset(); buildPluginSnapshotReport.mockReset(); buildPluginRegistrySnapshotReport.mockReset(); buildPluginInspectReport.mockReset(); buildPluginDiagnosticsReport.mockReset(); buildPluginCompatibilityNotices.mockReset(); inspectPluginRegistry.mockReset(); refreshPluginRegistry.mockReset(); applyExclusiveSlotSelection.mockReset(); planPluginUninstall.mockReset(); applyPluginUninstallDirectoryRemoval.mockReset(); uninstallPlugin.mockReset(); updateNpmInstalledPlugins.mockReset(); updateNpmInstalledHookPacks.mockReset(); promptYesNo.mockReset(); installPluginFromNpmSpec.mockReset(); installPluginFromPath.mockReset(); installPluginFromClawHub.mockReset(); parseClawHubPluginSpec.mockReset(); installHooksFromNpmSpec.mockReset(); installHooksFromPath.mockReset(); recordHookInstall.mockReset(); loadConfig.mockReturnValue({} as OpenClawConfig); readConfigFileSnapshot.mockImplementation(async () => { const config = getRuntimeConfig(); return { path: "/tmp/openclaw-config.json5", exists: true, raw: "{}", parsed: config, resolved: config, sourceConfig: config, runtimeConfig: config, valid: true, config, hash: "mock", issues: [], warnings: [], legacyIssues: [], }; }); writeConfigFile.mockResolvedValue(undefined); replaceConfigFile.mockImplementation( (async (params: { nextConfig: OpenClawConfig }) => await writeConfigFile(params.nextConfig)) as (...args: unknown[]) => Promise, ); resolveStateDir.mockReturnValue("/tmp/openclaw-state"); resolveMarketplaceInstallShortcut.mockResolvedValue(null); installPluginFromMarketplace.mockResolvedValue({ ok: false, error: "marketplace install failed", }); enablePluginInConfig.mockImplementation(((cfg: OpenClawConfig) => ({ config: cfg })) as ( ...args: unknown[] ) => unknown); recordPluginInstall.mockImplementation( ((cfg: OpenClawConfig) => cfg) as (...args: unknown[]) => unknown, ); loadInstalledPluginIndexInstallRecords.mockImplementation(async () => clonePluginInstallRecords(mockInstalledPluginIndexInstallRecords), ); writePersistedInstalledPluginIndexInstallRecords.mockImplementation(async (records: unknown) => { mockInstalledPluginIndexInstallRecords = clonePluginInstallRecords( (records ?? {}) as PluginInstallRecordMap, ); }); loadPluginManifestRegistry.mockReturnValue({ plugins: [], diagnostics: [], }); const defaultPluginReport = { plugins: [], diagnostics: [], }; buildPluginSnapshotReport.mockReturnValue(defaultPluginReport); buildPluginRegistrySnapshotReport.mockReturnValue({ ...defaultPluginReport, registrySource: "derived", registryDiagnostics: [], }); buildPluginDiagnosticsReport.mockReturnValue(defaultPluginReport); buildPluginCompatibilityNotices.mockReturnValue([]); const defaultRegistryIndex = { version: 1, hostContractVersion: "2026.4.25", compatRegistryVersion: "compat-v1", migrationVersion: 1, policyHash: "policy-v1", generatedAtMs: 1777118400000, plugins: [], diagnostics: [], }; inspectPluginRegistry.mockResolvedValue({ state: "fresh", refreshReasons: [], persisted: defaultRegistryIndex, current: defaultRegistryIndex, }); refreshPluginRegistry.mockResolvedValue(defaultRegistryIndex); applyExclusiveSlotSelection.mockImplementation((({ config }: { config: OpenClawConfig }) => ({ config, warnings: [], })) as (...args: unknown[]) => unknown); planPluginUninstall.mockImplementation((({ config, pluginId, }: { config: OpenClawConfig; pluginId: string; }) => ({ ok: true, config, pluginId, actions: createEmptyUninstallActions(), directoryRemoval: null, })) as (...args: unknown[]) => unknown); applyPluginUninstallDirectoryRemoval.mockResolvedValue({ directoryRemoved: false, warnings: [], }); uninstallPlugin.mockResolvedValue({ ok: true, config: {} as OpenClawConfig, warnings: [], actions: createEmptyUninstallActions(), }); updateNpmInstalledPlugins.mockResolvedValue({ outcomes: [], changed: false, config: {} as OpenClawConfig, }); updateNpmInstalledHookPacks.mockResolvedValue({ outcomes: [], changed: false, config: {} as OpenClawConfig, }); promptYesNo.mockResolvedValue(true); installPluginFromPath.mockResolvedValue({ ok: false, error: "path install disabled in test" }); installPluginFromNpmSpec.mockResolvedValue({ ok: false, error: "npm install disabled in test", }); installPluginFromClawHub.mockResolvedValue({ ok: false, error: "clawhub install disabled in test", }); parseClawHubPluginSpec.mockReturnValue(null); installHooksFromPath.mockResolvedValue({ ok: false, error: "hook path install disabled in test", }); installHooksFromNpmSpec.mockResolvedValue({ ok: false, error: "hook npm install disabled in test", }); recordHookInstall.mockImplementation( ((cfg: OpenClawConfig) => cfg) as (...args: unknown[]) => unknown, ); }