openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
747 lines (746 loc) • 28.8 kB
JavaScript
import { y as uniqueStrings } from "./string-normalization-DsCfAx8q.js";
import { r as defaultRuntime } from "./runtime-CF2WjnNZ.js";
import { m as shortenHomePath } from "./utils-P__uGsPB.js";
import { t as formatCliCommand } from "./command-format-C7YfyMTd.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { c as tracePluginLifecyclePhaseAsync } from "./discovery-D_VDsZuY.js";
import { t as parseClawHubPluginSpec } from "./clawhub-spec-Er3Np6VI.js";
import { I as CLAWHUB_INSTALL_ERROR_CODE } from "./official-external-plugin-catalog-Dzu7dwBN.js";
import { t as PLUGIN_INSTALL_ERROR_CODE } from "./install-types-DY_kphq4.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 { h as resolveArchiveKind } from "./archive-BfjFIxAJ.js";
import { t as buildNpmResolutionFields } from "./install-source-utils-DPJygB4h.js";
import { r as withClawPackageLifecycleLease } from "./claw-package-lifecycle-lease-CH70IHcU.js";
import { a as resolvePackageDirInstallTransaction, i as requestDeferredPackageDirInstall } from "./install-package-dir-kJK8m3kQ.js";
import { a as reportClawHubPluginInstallTelemetry } from "./clawhub-packages-DzRBkN2F.js";
import { t as markClawPackageIndependentlyOwned } from "./claw-package-adoption-Ba25vCgq.js";
import { t as findBundledPluginSource } from "./bundled-sources-CU4qjQGy.js";
import { n as formatNonClawHubInstallWarning, t as NON_CLAWHUB_INSTALL_FORCE_FLAG } from "./install-provenance-W0Pf0RSv.js";
import { t as withPluginLifecycleLease } from "./plugin-lifecycle-lease-BtAFH872.js";
import { r as promptYesNo } from "./prompt-BvBir9Yc.js";
import { n as resolvePluginCapabilityConsentCliOptions } from "./plugin-capability-consent-HWTOHYVK.js";
import "./install-BezM8CtS.js";
import { n as resolveBundledInstallPlanForNpmFailure, r as resolvePluginInstallSourcePlan } from "./plugin-install-plan-5wcyV5nl.js";
import { n as installHooksFromNpmSpec, r as installHooksFromPath } from "./install-7XIb3_ne.js";
import { i as formatPluginInstallWithHookFallbackError, n as createPluginInstallLogger, r as enableInternalHookEntries, t as createHookPackInstallLogger } from "./plugins-command-helpers-X8Lgc-nc.js";
import { t as installManagedPluginSource } from "./management-install-CPAJwGGP.js";
import "./clawhub-DgZbJqg3.js";
import { r as resolveMarketplaceInstallShortcut } from "./marketplace-ClbNhrdY.js";
import { r as resolvePluginInstallRequestContext } from "./plugin-install-config-policy-BKE_FN73.js";
import { t as resolveClawHubInstallConfirmation } from "./clawhub-install-confirmation-D-TCZ_VU.js";
import { t as resolveInstallPolicyWarningAcknowledgementCliOptions } from "./install-policy-warning-acknowledgement-DNm6cyVK.js";
import { n as resolveFullyBlockedConfigMutationReason, t as loadConfigForInstall } from "./plugins-install-config-_OMLBGf-.js";
import { t as stageHookInstall } from "./install-record-transaction-BSZea7FD.js";
import fs from "node:fs";
//#region src/cli/non-clawhub-install-acknowledgement.ts
function canPromptForNonClawHubInstall() {
return process.stdin.isTTY && process.stdout.isTTY;
}
async function confirmNonClawHubInstall(params) {
const warning = formatNonClawHubInstallWarning({
sourceClass: params.sourceClass,
spec: params.spec
});
if (params.acknowledged) {
params.runtime.log(theme.warn(warning));
return true;
}
if (canPromptForNonClawHubInstall()) {
params.runtime.log(theme.warn(warning));
return await promptYesNo("Install this non-ClawHub plugin source?");
}
params.runtime.error(`${warning}\nInstall cancelled; rerun with ${NON_CLAWHUB_INSTALL_FORCE_FLAG} after reviewing the source.`);
return false;
}
//#endregion
//#region src/cli/hook-install-persistence.ts
async function persistHookPackInstall(params) {
const runtime = params.runtime ?? defaultRuntime;
return await withPluginLifecycleLease({}, async (lease) => {
const assertPersistentApply = () => {
lease.assertOwned();
params.snapshot.writeOptions.assertConfigPathForWrite?.();
params.beforePersistentApply?.();
};
const next = enableInternalHookEntries(params.snapshot.config, params.hooks);
const transaction = await stageHookInstall({
update: {
hookId: params.hookPackId,
hooks: params.hooks,
...params.install
},
payloadTransaction: params.payloadTransaction,
lease,
beforePersistentApply: assertPersistentApply
});
try {
await replaceConfigFile({
nextConfig: next,
baseHash: params.snapshot.baseHash,
writeOptions: {
...params.snapshot.writeOptions,
assertConfigPathForWrite: assertPersistentApply
}
});
} catch (error) {
try {
await transaction.rollback();
} catch (rollbackError) {
throw new AggregateError([error, rollbackError], "Hook install config rollback failed", { cause: rollbackError });
}
throw error;
}
await transaction.commit();
runtime.log(params.successMessage ?? `Installed hook pack: ${params.hookPackId}`);
runtime.log("Hook install/link config can activate immediately in hybrid mode; code-only updates and reload mode off need a Gateway restart.");
return next;
});
}
//#endregion
//#region src/cli/npm-resolution.ts
/** Build the npm section of a plugin install record. */
function buildNpmInstallRecordFields(params) {
return {
source: "npm",
spec: params.spec,
installPath: params.installPath,
version: params.version,
...buildNpmResolutionFields(params.resolution)
};
}
/** CLI adapter for npm install-record pinning with styled warning output. */
function resolvePinnedNpmInstallRecordForCli(rawSpec, pin, installPath, version, resolution, log, warnFormat) {
const resolvedSpec = resolution?.resolvedSpec;
const recordSpec = pin && resolvedSpec ? resolvedSpec : rawSpec;
if (pin) {
if (resolvedSpec) log(`Pinned npm install record to ${resolvedSpec}.`);
else log(warnFormat("Could not resolve exact npm version for --pin; storing original npm spec."));
}
return buildNpmInstallRecordFields({
spec: recordSpec,
installPath,
version,
resolution
});
}
//#endregion
//#region src/cli/plugins-install-hook-fallback.ts
function resolveInstallSafetyOverrides(overrides) {
return {
config: overrides.config,
dangerouslyForceUnsafeInstall: overrides.dangerouslyForceUnsafeInstall,
onInstallPolicyWarning: overrides.onInstallPolicyWarning,
trustedSourceLinkedOfficialInstall: overrides.trustedSourceLinkedOfficialInstall
};
}
async function probeHookPackFromNpmSpec(params) {
try {
return await installHooksFromNpmSpec(params);
} catch (error) {
return {
ok: false,
error: formatErrorMessage(error)
};
}
}
async function probeHookPackFromPath(params) {
try {
return await installHooksFromPath(params);
} catch (error) {
return {
ok: false,
error: formatErrorMessage(error)
};
}
}
function isTerminalPluginInstallFailure(code) {
return code === PLUGIN_INSTALL_ERROR_CODE.SECURITY_SCAN_BLOCKED || code === PLUGIN_INSTALL_ERROR_CODE.SECURITY_SCAN_FAILED || code === PLUGIN_INSTALL_ERROR_CODE.RELEASE_COHORT_UNAVAILABLE || code === PLUGIN_INSTALL_ERROR_CODE.UNSUPPORTED_PLAIN_FILE_PLUGIN;
}
async function tryInstallHookPackFromLocalPath(params) {
if (params.snapshot.hookMutation.mode === "blocked") return {
ok: false,
error: params.snapshot.hookMutation.reason
};
if (params.link) {
if (!fs.statSync(params.resolvedPath).isDirectory()) return {
ok: false,
error: "Linked hook pack paths must be directories."
};
const probe = await installHooksFromPath({
...resolveInstallSafetyOverrides(params.safetyOverrides ?? {}),
path: params.resolvedPath,
dryRun: true,
...params.expectedPackageKind ? { expectedPackageKind: params.expectedPackageKind } : {}
});
if (!probe.ok) return probe;
const existing = params.snapshot.config.hooks?.internal?.load?.extraDirs ?? [];
const merged = uniqueStrings([...existing, params.resolvedPath]);
await persistHookPackInstall({
snapshot: {
...params.snapshot,
config: {
...params.snapshot.config,
hooks: {
...params.snapshot.config.hooks,
internal: {
...params.snapshot.config.hooks?.internal,
enabled: true,
load: {
...params.snapshot.config.hooks?.internal?.load,
extraDirs: merged
}
}
}
}
},
hookPackId: probe.hookPackId,
hooks: probe.hooks,
install: {
source: "path",
sourcePath: params.resolvedPath,
installPath: params.resolvedPath,
version: probe.version
},
successMessage: `Linked hook pack path: ${shortenHomePath(params.resolvedPath)}`,
runtime: params.runtime,
beforePersistentApply: params.beforePersistentApply
});
return { ok: true };
}
const result = await installHooksFromPath(requestDeferredPackageDirInstall({
...resolveInstallSafetyOverrides(params.safetyOverrides ?? {}),
path: params.resolvedPath,
mode: params.installMode,
...params.expectedPackageKind ? { expectedPackageKind: params.expectedPackageKind } : {},
logger: createHookPackInstallLogger(params.runtime),
beforePersistentApply: params.beforePersistentApply
}, params.assertOwned));
if (!result.ok) return result;
const source = resolveArchiveKind(params.resolvedPath) ? "archive" : "path";
await persistHookPackInstall({
snapshot: params.snapshot,
hookPackId: result.hookPackId,
hooks: result.hooks,
install: {
source,
sourcePath: params.resolvedPath,
installPath: result.targetDir,
version: result.version
},
runtime: params.runtime,
beforePersistentApply: params.beforePersistentApply,
payloadTransaction: resolvePackageDirInstallTransaction(result)
});
return { ok: true };
}
async function tryInstallHookPackFromNpmSpec(params) {
if (params.snapshot.hookMutation.mode === "blocked") return {
ok: false,
error: params.snapshot.hookMutation.reason
};
const result = await installHooksFromNpmSpec(requestDeferredPackageDirInstall({
...resolveInstallSafetyOverrides(params.safetyOverrides ?? {}),
config: params.snapshot.config,
spec: params.spec,
mode: params.installMode,
...params.expectedIntegrity ? { expectedIntegrity: params.expectedIntegrity } : {},
...params.expectedPackageKind ? { expectedPackageKind: params.expectedPackageKind } : {},
logger: createHookPackInstallLogger(params.runtime),
beforePersistentApply: params.beforePersistentApply
}, params.assertOwned));
if (!result.ok) return result;
const pinMessages = [];
const installRecord = resolvePinnedNpmInstallRecordForCli(params.spec, Boolean(params.pin), result.targetDir, result.version, result.npmResolution, (message) => pinMessages.push(message), theme.warn);
await persistHookPackInstall({
snapshot: params.snapshot,
hookPackId: result.hookPackId,
hooks: result.hooks,
install: installRecord,
runtime: params.runtime,
beforePersistentApply: params.beforePersistentApply,
payloadTransaction: resolvePackageDirInstallTransaction(result)
});
for (const message of pinMessages) (params.runtime ?? defaultRuntime).log(message);
return { ok: true };
}
/** Preserve npm plugin and hook ownership without executing a blocked mutation. */
async function tryInstallPluginOrHookPackFromNpmSpec(params) {
const runtime = params.runtime ?? defaultRuntime;
const installContext = {
snapshot: params.snapshot,
runtime,
invalidateRuntimeCache: params.invalidateRuntimeCache,
beforePersistentApply: params.beforePersistentApply,
assertOwned: params.assertOwned
};
const fullyBlockedReason = resolveFullyBlockedConfigMutationReason(params.snapshot);
if (fullyBlockedReason) {
runtime.error(fullyBlockedReason);
return { ok: false };
}
if (params.snapshot.pluginMutation.mode === "blocked" || params.snapshot.hookMutation.mode === "blocked") {
const hookProbe = await probeHookPackFromNpmSpec({
...resolveInstallSafetyOverrides(params.safetyOverrides),
config: params.snapshot.config,
spec: params.spec,
mode: params.installMode,
inspection: "package-kind",
...params.expectedIntegrity ? { expectedIntegrity: params.expectedIntegrity } : {},
logger: createHookPackInstallLogger(params.runtime)
});
if (hookProbe.ok && hookProbe.packageKind === "hook-only") {
if (params.snapshot.hookMutation.mode === "blocked") {
runtime.error(params.snapshot.hookMutation.reason);
return { ok: false };
}
const hookFallback = await tryInstallHookPackFromNpmSpec({
...installContext,
installMode: params.installMode,
spec: params.spec,
safetyOverrides: params.safetyOverrides,
pin: params.pin,
expectedIntegrity: hookProbe.npmResolution?.integrity ?? params.expectedIntegrity,
expectedPackageKind: "hook-only"
});
if (hookFallback.ok) return { ok: true };
runtime.error(hookFallback.error);
return { ok: false };
}
if (params.snapshot.pluginMutation.mode === "blocked") {
runtime.error(params.snapshot.pluginMutation.reason);
return { ok: false };
}
}
const result = await installManagedPluginSource({
request: params.officialRequest ?? {
source: "npm",
spec: params.spec,
mode: params.installMode,
pin: params.pin,
...params.expectedPluginId ? { expectedPluginId: params.expectedPluginId } : {},
...params.expectedIntegrity ? { expectedIntegrity: params.expectedIntegrity } : {},
...params.trustedSourceLinkedOfficialInstall ? { trustedSourceLinkedOfficialInstall: true } : {}
},
...installContext,
...params.capabilityConsent,
safetyOverrides: params.safetyOverrides,
logger: createPluginInstallLogger(params.runtime)
});
if (!result.ok) {
if (result.installSource?.source === "clawhub" || params.officialRequest && result.code !== PLUGIN_INSTALL_ERROR_CODE.MISSING_OPENCLAW_EXTENSIONS || isTerminalPluginInstallFailure(result.code)) {
runtime.error(result.error);
return { ok: false };
}
if (params.allowBundledFallback) {
const bundledFallbackPlan = resolveBundledInstallPlanForNpmFailure({
rawSpec: params.spec,
code: result.code,
findBundledSource: (lookup) => findBundledPluginSource({ lookup })
});
if (bundledFallbackPlan) {
const bundledResult = await installManagedPluginSource({
request: {
source: "bundled",
rawSpec: params.spec,
bundledSource: bundledFallbackPlan.bundledSource,
warning: bundledFallbackPlan.warning
},
...installContext
});
if (!bundledResult.ok) {
runtime.error(bundledResult.error);
return { ok: false };
}
return { ok: true };
}
}
const hookFallback = await tryInstallHookPackFromNpmSpec({
...installContext,
installMode: params.installMode,
spec: params.spec,
safetyOverrides: params.safetyOverrides,
pin: params.pin,
expectedIntegrity: params.expectedIntegrity
});
if (hookFallback.ok) return { ok: true };
runtime.error(formatPluginInstallWithHookFallbackError(result.error, hookFallback));
return { ok: false };
}
if (params.pin) {
const resolvedSpec = result.npmResolution?.resolvedSpec;
runtime.log(resolvedSpec ? `Pinned npm install record to ${resolvedSpec}.` : theme.warn("Could not resolve exact npm version for --pin; storing original npm spec."));
}
return { ok: true };
}
//#endregion
//#region src/cli/plugins-install-preflight.ts
function resolveMarketplaceOptionError(opts) {
if (opts.link) return `--link is not supported with --marketplace. Remove --link, or install a local path with ${formatCliCommand(`openclaw plugins install --link <path> ${NON_CLAWHUB_INSTALL_FORCE_FLAG}`)}.`;
if (opts.pin) return `--pin is not supported with --marketplace. Use ${formatCliCommand(`openclaw plugins install <plugin> --marketplace <name> ${NON_CLAWHUB_INSTALL_FORCE_FLAG}`)} without --pin.`;
return null;
}
function resolveSourceOptionError(opts, sourcePlan) {
if (sourcePlan.request.source === "git" && opts.link) return `--link is not supported with git: installs. Use ${formatCliCommand(`openclaw plugins install git:<repo>@<ref> ${NON_CLAWHUB_INSTALL_FORCE_FLAG}`)} for Git installs or ${formatCliCommand(`openclaw plugins install --link <path> ${NON_CLAWHUB_INSTALL_FORCE_FLAG}`)} for local paths.`;
if (sourcePlan.request.source === "git" && opts.pin) return `--pin is not supported with git: installs. Pin the ref in the spec instead, for example ${formatCliCommand(`openclaw plugins install git:<repo>@<ref> ${NON_CLAWHUB_INSTALL_FORCE_FLAG}`)}.`;
if (opts.pin && sourcePlan.request.source !== "npm" && sourcePlan.request.source !== "official" && sourcePlan.request.source !== "bundled") return "--pin is only supported with npm registry installs.";
if (opts.link && sourcePlan.request.source !== "local") return `--link requires a local path. Run ${formatCliCommand(`openclaw plugins install --link <path> ${NON_CLAWHUB_INSTALL_FORCE_FLAG}`)}.`;
return null;
}
/** Complete source and option validation before acquiring the persistent lifecycle lease. */
async function resolvePluginInstallPreflight(params) {
if (!params.raw.trim()) return {
ok: false,
error: "Plugin install source must not be empty."
};
if (params.opts.marketplace !== void 0 && !params.opts.marketplace.trim()) return {
ok: false,
error: "--marketplace requires a non-empty source."
};
const installMode = params.opts.force && !params.opts.link ? "update" : "install";
let raw = params.raw;
let marketplace = params.opts.marketplace;
let sourcePlan = null;
if (marketplace === void 0) {
const shorthand = await tracePluginLifecyclePhaseAsync("marketplace shortcut resolution", () => resolveMarketplaceInstallShortcut(raw), { command: "install" });
if (shorthand?.ok === false) return {
ok: false,
error: shorthand.error
};
if (shorthand?.ok) {
raw = shorthand.plugin;
marketplace = shorthand.marketplaceSource;
} else {
const planned = resolvePluginInstallSourcePlan({
raw,
mode: installMode,
link: params.opts.link,
pin: params.opts.pin
});
if (!planned.ok) return planned;
sourcePlan = planned;
}
}
const opts = {
...params.opts,
marketplace
};
const optionError = marketplace ? resolveMarketplaceOptionError(opts) : sourcePlan ? resolveSourceOptionError(opts, sourcePlan) : "Plugin install source could not be resolved.";
if (optionError) return {
ok: false,
error: optionError
};
const requestResolution = resolvePluginInstallRequestContext({
rawSpec: raw,
marketplace
});
if (!requestResolution.ok) return requestResolution;
const source = sourcePlan?.request.source;
const request = source && [
"npm-pack",
"git",
"clawhub",
"bundled",
"official"
].includes(source) ? {
...requestResolution.request,
installKind: "plugin"
} : requestResolution.request;
if (marketplace) return {
ok: true,
raw,
opts,
installMode,
request,
marketplace,
sourcePlan: null
};
if (!sourcePlan) return {
ok: false,
error: "Plugin install source could not be resolved."
};
return {
ok: true,
raw,
opts,
installMode,
request,
sourcePlan
};
}
//#endregion
//#region src/cli/plugins-install-command.ts
const DEPRECATED_DANGEROUS_FORCE_UNSAFE_INSTALL_WARNING = "--dangerously-force-unsafe-install is deprecated and no longer affects plugin installs because built-in install-time dangerous-code scanning has been removed. Configure security.installPolicy for operator-owned install decisions.";
function isClawHubBlockedCliFailure(result) {
return result.code === CLAWHUB_INSTALL_ERROR_CODE.CLAWHUB_DOWNLOAD_BLOCKED && typeof result.warning === "string" && result.warning.trim().length > 0;
}
/** Validate install intent before opening the SQLite-backed plugin lifecycle lease. */
async function runPluginInstallCommand(params) {
assertConfigWriteAllowedInCurrentMode();
const runtime = params.runtime ?? defaultRuntime;
const preflight = await resolvePluginInstallPreflight(params);
if (!preflight.ok) {
runtime.error(preflight.error);
return runtime.exit(1);
}
return await withPluginLifecycleLease({}, async (lease) => await runPluginInstallCommandUnlocked({
...params,
beforePersistentApply: () => {
lease.assertOwned();
params.beforePersistentApply?.();
}
}, preflight, lease.assertOwned.bind(lease)));
}
async function runPluginInstallCommandUnlocked(params, preflight, assertOwned) {
assertConfigWriteAllowedInCurrentMode();
const runtime = params.runtime ?? defaultRuntime;
const { raw, opts, installMode, request } = preflight;
if (opts.dangerouslyForceUnsafeInstall) runtime.log(theme.warn(DEPRECATED_DANGEROUS_FORCE_UNSAFE_INSTALL_WARNING));
const snapshot = await loadConfigForInstall(request).catch((error) => {
runtime.error(formatErrorMessage(error));
return null;
});
if (!snapshot) return runtime.exit(1);
const installContext = {
snapshot,
runtime,
invalidateRuntimeCache: params.invalidateRuntimeCache ?? true,
beforePersistentApply: params.beforePersistentApply,
assertOwned
};
const safetyOverrides = resolveInstallSafetyOverrides({
...opts,
config: snapshot.config,
...resolveInstallPolicyWarningAcknowledgementCliOptions({
acknowledgeInstallPolicyWarning: opts.acknowledgeInstallPolicyWarning,
allowPrompt: params.allowInstallPolicyWarningPrompt,
dangerouslyForceUnsafeInstall: opts.dangerouslyForceUnsafeInstall
})
});
const capabilityConsent = resolvePluginCapabilityConsentCliOptions({
acceptCapabilities: opts.acceptCapabilities,
action: "install",
runtime
});
const acknowledgeNonClawHubSource = async (sourceClass, spec) => await confirmNonClawHubInstall({
acknowledged: opts.force,
runtime,
sourceClass,
spec
});
if (preflight.sourcePlan === null) {
if (!await acknowledgeNonClawHubSource("marketplace", `${raw} from ${preflight.marketplace}`)) return runtime.exit(1);
const result = await installManagedPluginSource({
request: {
source: "marketplace",
marketplace: preflight.marketplace,
plugin: raw,
mode: installMode
},
...installContext,
...capabilityConsent,
safetyOverrides,
logger: createPluginInstallLogger(runtime)
});
if (!result.ok) {
if (!isClawHubBlockedCliFailure(result)) runtime.error(result.error);
return runtime.exit(1);
}
return;
}
const { sourcePlan } = preflight;
if (sourcePlan.acknowledgement && !await acknowledgeNonClawHubSource(sourcePlan.acknowledgement.sourceClass, sourcePlan.acknowledgement.spec)) return runtime.exit(1);
const sourceRequest = sourcePlan.request;
switch (sourceRequest.source) {
case "local": {
const resolved = sourceRequest.path;
if (sourceRequest.link) sourceRequest.successMessage = `Linked plugin path: ${shortenHomePath(resolved)}`;
const fullyBlockedReason = resolveFullyBlockedConfigMutationReason(snapshot);
if (fullyBlockedReason) {
runtime.error(fullyBlockedReason);
return runtime.exit(1);
}
if (snapshot.pluginMutation.mode === "blocked" || snapshot.hookMutation.mode === "blocked") {
const hookProbe = await probeHookPackFromPath({
...safetyOverrides,
path: resolved,
mode: installMode,
inspection: "package-kind"
});
if (hookProbe.ok && hookProbe.packageKind === "hook-only") {
if (snapshot.hookMutation.mode === "blocked") {
runtime.error(snapshot.hookMutation.reason);
return runtime.exit(1);
}
const hookFallback = await tryInstallHookPackFromLocalPath({
...installContext,
installMode,
resolvedPath: resolved,
safetyOverrides,
...opts.link ? { link: true } : {},
expectedPackageKind: "hook-only"
});
if (hookFallback.ok) return;
runtime.error(hookFallback.error);
return runtime.exit(1);
}
if (snapshot.pluginMutation.mode === "blocked") {
runtime.error(snapshot.pluginMutation.reason);
return runtime.exit(1);
}
}
const result = await installManagedPluginSource({
request: sourceRequest,
...installContext,
...capabilityConsent,
safetyOverrides,
logger: createPluginInstallLogger(runtime)
});
if (result.ok) return;
if (isTerminalPluginInstallFailure(result.code)) {
runtime.error(result.error);
return runtime.exit(1);
}
const hookFallback = await tryInstallHookPackFromLocalPath({
...installContext,
installMode,
resolvedPath: resolved,
safetyOverrides,
...sourceRequest.link ? { link: true } : {}
});
if (hookFallback.ok) return;
runtime.error(formatPluginInstallWithHookFallbackError(result.error, hookFallback));
return runtime.exit(1);
}
case "marketplace":
case "npm-pack":
case "git": {
const result = await installManagedPluginSource({
request: sourceRequest,
...installContext,
...capabilityConsent,
safetyOverrides,
logger: createPluginInstallLogger(runtime)
});
if (!result.ok) {
runtime.error(result.error);
return runtime.exit(1);
}
return;
}
case "bundled": {
const result = await tracePluginLifecyclePhaseAsync("install execution", () => installManagedPluginSource({
request: sourceRequest,
...installContext
}), {
command: "install",
source: "bundled",
pluginId: sourceRequest.bundledSource.pluginId
});
if (!result.ok) {
runtime.error(result.error);
return runtime.exit(1);
}
return;
}
case "official": {
const primary = sourceRequest.installSources?.[0];
if (primary?.source === "clawhub") {
const result = await installManagedPluginSource({
request: sourceRequest,
...installContext,
...capabilityConsent,
safetyOverrides,
logger: createPluginInstallLogger(runtime)
});
if (!result.ok) {
runtime.error(result.error);
return runtime.exit(1);
}
return;
}
if (!(await tryInstallPluginOrHookPackFromNpmSpec({
...installContext,
installMode,
spec: sourceRequest.spec,
pin: sourceRequest.pin,
safetyOverrides,
capabilityConsent,
allowBundledFallback: false,
expectedPluginId: sourceRequest.pluginId,
expectedIntegrity: primary?.expectedIntegrity ?? sourceRequest.expectedIntegrity,
trustedSourceLinkedOfficialInstall: true,
officialRequest: sourceRequest
})).ok) return runtime.exit(1);
return;
}
case "clawhub": {
const installFromClawHub = async (installSnapshot = snapshot, installSafetyOverrides = safetyOverrides) => {
const result = await installManagedPluginSource({
...installContext,
request: {
...sourceRequest,
...opts.expectedIntegrity ? { expectedIntegrity: opts.expectedIntegrity } : {},
...opts.expectedPluginId ? { expectedPluginId: opts.expectedPluginId } : {},
confirmInstall: resolveClawHubInstallConfirmation()
},
snapshot: installSnapshot,
...capabilityConsent,
safetyOverrides: installSafetyOverrides,
logger: createPluginInstallLogger(runtime)
});
if (!result.ok) {
if (!isClawHubBlockedCliFailure(result)) runtime.error(result.error);
return runtime.exit(1);
}
if (!result.clawhub) {
runtime.error("ClawHub plugin install completed without source metadata.");
return runtime.exit(1);
}
if (!params.clawManaged && result.clawhub.version) markClawPackageIndependentlyOwned({
kind: "plugin",
source: "clawhub",
ref: result.clawhub.clawhubPackage,
version: result.clawhub.version
});
await reportClawHubPluginInstallTelemetry({
baseUrl: result.clawhub.clawhubUrl,
packageName: result.clawhub.clawhubPackage,
version: result.clawhub.version
}).catch(() => void 0);
};
if (params.clawManaged) return await installFromClawHub();
return await withClawPackageLifecycleLease({
kind: "plugin",
source: "clawhub",
ref: parseClawHubPluginSpec(sourceRequest.spec)?.name ?? sourceRequest.spec
}, async () => {
const leasedSnapshot = await loadConfigForInstall(request).catch((error) => {
runtime.error(formatErrorMessage(error));
return null;
});
if (!leasedSnapshot) return runtime.exit(1);
return await installFromClawHub(leasedSnapshot, resolveInstallSafetyOverrides({
...safetyOverrides,
config: leasedSnapshot.config
}));
});
}
case "npm": if (!(await tryInstallPluginOrHookPackFromNpmSpec({
...installContext,
installMode,
spec: sourceRequest.spec,
pin: sourceRequest.pin,
safetyOverrides,
capabilityConsent,
allowBundledFallback: sourceRequest.allowBundledFallback ?? false,
expectedPluginId: sourceRequest.expectedPluginId,
expectedIntegrity: sourceRequest.expectedIntegrity,
trustedSourceLinkedOfficialInstall: sourceRequest.trustedSourceLinkedOfficialInstall
})).ok) return runtime.exit(1);
}
}
//#endregion
export { runPluginInstallCommand as t };