openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
233 lines (232 loc) • 12.8 kB
JavaScript
import { r as defaultRuntime } from "./runtime-CF2WjnNZ.js";
import { l as readConfigFileSnapshotForWrite } from "./io.runtime-B9iJRs3w.js";
import { m as shortenHomePath } from "./utils-P__uGsPB.js";
import { c as tracePluginLifecyclePhaseAsync, s as tracePluginLifecyclePhase } from "./discovery-D_VDsZuY.js";
import { a as resolveDefaultPluginExtensionsDir } from "./install-paths-ChnxikBv.js";
import { t as parseClawHubPluginSpec } from "./clawhub-spec-Er3Np6VI.js";
import { n as assertConfigWriteAllowedInCurrentMode } from "./nix-mode-write-guard-uwcnAyQN.js";
import { r as replaceConfigFile } from "./mutate-ZNN4iFCn.js";
import "./config-Cs0XXL3x.js";
import { r as theme } from "./theme-vjDs9tao.js";
import { r as withClawPackageLifecycleLease } from "./claw-package-lifecycle-lease-CH70IHcU.js";
import { t as withPluginLifecycleLease } from "./plugin-lifecycle-lease-BtAFH872.js";
//#region src/cli/plugins-uninstall-command.ts
function isPromptInputClosedError(error, PromptInputClosedError) {
return error instanceof PromptInputClosedError;
}
async function runPluginUninstallCommand(id, opts = {}, runtime = defaultRuntime) {
if (opts.dryRun) return await runPluginUninstallCommandUnlocked(id, opts, runtime);
assertConfigWriteAllowedInCurrentMode();
if (!opts.force) return await runPluginUninstallCommandUnlocked(id, opts, runtime);
return await withPluginLifecycleLease({}, async () => await runPluginUninstallCommandUnlocked(id, opts, runtime));
}
async function runPluginUninstallCommandUnlocked(id, opts, runtime, skipPreview = false) {
if (!opts.dryRun) assertConfigWriteAllowedInCurrentMode();
const { loadInstalledPluginIndex } = await import("./installed-plugin-index-CMAssyN0.js");
const { createInstalledPluginIndexScopeLookup } = await import("./installed-plugin-index-scope-lookup-DA_DML2r.js");
const { createInstalledPluginOwnershipResolver } = await import("./installed-plugin-package-ownership-Bp7Honoy.js");
const { loadInstalledPluginIndexInstallRecords, removePluginInstallRecordFromRecords, withoutPluginInstallRecords, withPluginInstallRecords } = await import("./installed-plugin-index-records-DGtSxIvi.js");
const { buildPluginSnapshotReport } = await import("./status-CrqpxX2s.js");
const { applyPluginUninstallDirectoryRemoval, formatUninstallActionLabels, formatUninstallSlotResetPreview, planPluginUninstall, pluginUninstallTargetExists, resolveUninstallChannelConfigKeys, UNINSTALL_ACTION_LABELS } = await import("./uninstall-DftaKZK5.js");
const { prepareConfigForDisabledPluginSet, recordPluginPackageUninstallPlan } = await import("./uninstall-package-plan-C8ZyNlqU.js");
const { commitPluginInstallRecordsWithConfig } = await import("./install-record-commit-DVoT_zjZ.js");
const { selectInstallMutationWriteOptions } = await import("./install-persistence-CdvjoJeG.js");
const { refreshPluginRegistryAfterConfigMutation } = await import("./registry-refresh-x5mIxBEc.js");
const { resolvePluginUninstallId } = await import("./plugins-uninstall-selection-BhULrPH7.js");
const { PromptInputClosedError, promptYesNo } = await import("./prompt-BMrl28g7.js");
const prepared = await tracePluginLifecyclePhaseAsync("config read", () => readConfigFileSnapshotForWrite(), { command: "uninstall" });
const { snapshot } = prepared;
const mutationWriteOptions = selectInstallMutationWriteOptions(prepared.writeOptions);
const sourceConfig = snapshot.sourceConfig ?? snapshot.config;
const installRecords = await tracePluginLifecyclePhaseAsync("install records load", () => loadInstalledPluginIndexInstallRecords(), { command: "uninstall" });
const cfg = withPluginInstallRecords(sourceConfig, installRecords);
const installedIndex = loadInstalledPluginIndex({
config: cfg,
installRecords
});
const report = tracePluginLifecyclePhase("plugin registry snapshot", () => buildPluginSnapshotReport({ config: cfg }), { command: "uninstall" });
const extensionsDir = resolveDefaultPluginExtensionsDir();
const keepFiles = Boolean(opts.keepFiles || opts.keepConfig);
if (opts.keepConfig) runtime.log(theme.warn("`--keep-config` is deprecated, use `--keep-files`."));
const selection = resolvePluginUninstallId({
rawId: id,
config: cfg,
plugins: report.plugins
});
if (!selection.ok) {
runtime.error(selection.error);
runtime.exit(1);
return;
}
const { plugin } = selection.value;
const requestedPluginId = selection.value.pluginId;
const ownership = createInstalledPluginOwnershipResolver(installedIndex).resolveLifecycle(requestedPluginId);
if (!ownership.ok) {
runtime.error(ownership.error);
runtime.exit(1);
return;
}
const { installOwner: pluginId, pluginIds: ownedPluginIds } = ownership.value;
const policyPluginIds = ownedPluginIds.length > 0 ? ownedPluginIds : [pluginId];
let channelIds;
if (ownedPluginIds.length === 1 && ownedPluginIds[0] === requestedPluginId) channelIds = plugin?.channelIds;
else if (ownedPluginIds.length > 0) channelIds = [...new Set(ownedPluginIds.flatMap((entryId) => report.plugins.find((entry) => entry.id === entryId)?.channelIds ?? []))];
else if (createInstalledPluginIndexScopeLookup(installedIndex).hasChannelContributionOwners([pluginId])) channelIds = [];
const initialPlan = planPluginUninstall(recordPluginPackageUninstallPlan({
config: cfg,
pluginId,
...channelIds !== void 0 ? { channelIds } : {},
deleteFiles: !keepFiles,
extensionsDir
}, {
runtimePluginIds: policyPluginIds,
runtimeLoadPaths: ownedPluginIds.flatMap((entryId) => report.plugins.find((entry) => entry.id === entryId)?.source ?? [])
}));
if (!initialPlan.ok) {
if (plugin) runtime.error(`Plugin "${pluginId}" is not managed by plugins config/install records and cannot be uninstalled.`);
else runtime.error(initialPlan.error);
runtime.exit(1);
return;
}
let plan = initialPlan;
const hasInstall = Object.hasOwn(cfg.plugins?.installs ?? {}, pluginId);
const preview = [];
if (plan.actions.entry) preview.push(UNINSTALL_ACTION_LABELS.entry);
if (plan.actions.install) preview.push(UNINSTALL_ACTION_LABELS.install);
if (plan.actions.allowlist) preview.push(UNINSTALL_ACTION_LABELS.allowlist);
if (plan.actions.denylist) preview.push(UNINSTALL_ACTION_LABELS.denylist);
if (plan.actions.loadPath) preview.push(UNINSTALL_ACTION_LABELS.loadPath);
if (plan.actions.memorySlot) preview.push(formatUninstallSlotResetPreview("memory"));
if (plan.actions.contextEngineSlot) preview.push(formatUninstallSlotResetPreview("contextEngine"));
const channels = cfg.channels;
if (plan.actions.channelConfig && hasInstall && channels) {
for (const key of resolveUninstallChannelConfigKeys(pluginId, { channelIds })) if (Object.hasOwn(channels, key)) preview.push(`${UNINSTALL_ACTION_LABELS.channelConfig} (channels.${key})`);
}
if (plan.directoryRemoval) preview.push(`directory: ${shortenHomePath(plan.directoryRemoval.target)}`);
if (!skipPreview) {
const pluginName = plugin?.name || pluginId;
runtime.log(`Plugin: ${theme.command(pluginName)}${pluginName !== pluginId ? theme.muted(` (${pluginId})`) : ""}`);
if (ownedPluginIds.length > 1 || requestedPluginId !== pluginId) runtime.log(`Package owner: ${theme.command(pluginId)}; all entries will be removed: ${ownedPluginIds.join(", ")}`);
runtime.log(`Will remove: ${preview.length > 0 ? preview.join(", ") : "(nothing)"}`);
const { collectClawPluginUninstallWarnings } = await import("./uninstall-claw-references-CtX2poGK.js");
for (const warning of collectClawPluginUninstallWarnings({
pluginId,
installRecord: cfg.plugins?.installs?.[pluginId]
})) runtime.log(theme.warn(warning));
}
let nextConfig = withoutPluginInstallRecords(plan.config);
if (opts.dryRun) {
runtime.log(theme.muted("Dry run, no changes made."));
return;
}
if (!opts.force) {
let confirmed;
try {
confirmed = await promptYesNo(ownedPluginIds.length > 1 ? `Uninstall plugin package "${pluginId}" and all entries?` : `Uninstall plugin "${pluginId}"?`);
} catch (error) {
if (isPromptInputClosedError(error, PromptInputClosedError)) {
runtime.error("Error: plugins uninstall requires confirmation input. Re-run in an interactive TTY or pass --force.");
runtime.exit(1);
return;
}
throw error;
}
if (!confirmed) {
runtime.log("Cancelled.");
return;
}
return await withPluginLifecycleLease({}, async () => await runPluginUninstallCommandUnlocked(id, {
...opts,
force: true
}, runtime, true));
}
const uninstall = async () => {
const guardedWriteOptions = (writeOptions) => opts.beforePersistentApply ? {
...writeOptions,
assertConfigPathForWrite: () => {
writeOptions.assertConfigPathForWrite?.();
opts.beforePersistentApply?.();
}
} : writeOptions;
let finalBaseHash = snapshot.hash;
let finalWriteOptions = mutationWriteOptions;
let directoryResult = {
directoryRemoved: false,
warnings: []
};
if (plan.directoryRemoval) {
const disabledConfig = prepareConfigForDisabledPluginSet(sourceConfig, policyPluginIds);
finalBaseHash = (await tracePluginLifecyclePhaseAsync("config disable", () => replaceConfigFile({
nextConfig: disabledConfig,
...snapshot.hash !== void 0 ? { baseHash: snapshot.hash } : {},
writeOptions: {
...guardedWriteOptions(mutationWriteOptions),
afterWrite: { mode: "auto" }
}
}), { command: "uninstall" }))?.persistedHash ?? snapshot.hash;
opts.beforePersistentApply?.();
directoryResult = await applyPluginUninstallDirectoryRemoval(plan.directoryRemoval);
for (const warning of directoryResult.warnings) runtime.log(theme.warn(warning));
if (pluginUninstallTargetExists(plan.directoryRemoval.target)) throw new Error(`Failed to remove plugin directory ${shortenHomePath(plan.directoryRemoval.target)}; the plugin remains disabled and tracked so uninstall can be retried.`);
const refreshedPrepared = await tracePluginLifecyclePhaseAsync("config reread", () => readConfigFileSnapshotForWrite(), { command: "uninstall" });
const refreshedSnapshot = refreshedPrepared.snapshot;
const refreshedSourceConfig = refreshedSnapshot.sourceConfig ?? refreshedSnapshot.config;
const refreshedPlan = planPluginUninstall(recordPluginPackageUninstallPlan({
config: withPluginInstallRecords(refreshedSourceConfig, installRecords),
pluginId,
...channelIds !== void 0 ? { channelIds } : {},
deleteFiles: true,
extensionsDir
}, {
runtimePluginIds: policyPluginIds,
runtimeLoadPaths: ownedPluginIds.flatMap((entryId) => report.plugins.find((entry) => entry.id === entryId)?.source ?? [])
}));
if (!refreshedPlan.ok) throw new Error(refreshedPlan.error);
plan = refreshedPlan;
nextConfig = withoutPluginInstallRecords(plan.config);
finalBaseHash = refreshedSnapshot.hash;
finalWriteOptions = selectInstallMutationWriteOptions(refreshedPrepared.writeOptions);
}
const nextInstallRecords = removePluginInstallRecordFromRecords(installRecords, pluginId);
await tracePluginLifecyclePhaseAsync("config mutation", () => commitPluginInstallRecordsWithConfig({
previousInstallRecords: installRecords,
nextInstallRecords,
nextConfig,
...finalBaseHash !== void 0 ? { baseHash: finalBaseHash } : {},
writeOptions: {
...guardedWriteOptions(finalWriteOptions),
allowConfigSizeDrop: true,
afterWrite: {
mode: "restart",
reason: "plugin source changed"
}
}
}), { command: "uninstall" });
if (!plan.directoryRemoval) directoryResult = await applyPluginUninstallDirectoryRemoval(null);
await refreshPluginRegistryAfterConfigMutation({
config: nextConfig,
reason: "source-changed",
installRecords: nextInstallRecords,
invalidateRuntimeCache: opts.invalidateRuntimeCache,
traceCommand: "uninstall",
logger: { warn: (message) => runtime.log(theme.warn(message)) }
});
const removed = formatUninstallActionLabels({
...plan.actions,
directory: directoryResult.directoryRemoved
});
const uninstalledSubject = ownedPluginIds.length > 1 || requestedPluginId !== pluginId ? `plugin package "${pluginId}" and entries ${ownedPluginIds.join(", ")}` : `plugin "${pluginId}"`;
runtime.log(`Uninstalled ${uninstalledSubject}. Removed: ${removed.length > 0 ? removed.join(", ") : "nothing"}.`);
runtime.log("Restart the gateway to apply changes.");
};
const installRecord = cfg.plugins?.installs?.[pluginId];
const clawhubPackage = installRecord?.source === "clawhub" ? installRecord.clawhubPackage ?? parseClawHubPluginSpec(installRecord.spec ?? "")?.name : void 0;
if (opts.clawManaged || !clawhubPackage) return await uninstall();
await withClawPackageLifecycleLease({
kind: "plugin",
source: "clawhub",
ref: clawhubPackage
}, uninstall, { required: true });
}
//#endregion
export { runPluginUninstallCommand as t };