UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

252 lines (251 loc) 13.6 kB
import { c as resolveUserPath } from "./home-dir-BPhrG-aM.js"; import "./utils-P__uGsPB.js"; import { n as registerPluginMetadataProcessMemoLifecycleClear } from "./plugin-metadata-lifecycle-C6m-q5Au.js"; import { l as normalizePluginsConfig, u as resolveEffectiveEnableState } from "./config-state-BkU1frVq.js"; import { r as resolveInstalledPluginIndexInstallOwner, t as isInstalledPluginIndexInstallOwnerAmbiguous } from "./installed-plugin-index-install-owner-Bd-Byre8.js"; import { r as loadInstalledPluginIndexInstallRecords } from "./installed-plugin-index-record-reader-SXWwf_BU.js"; import { i as isTrustedOfficialPluginInstallRecord } from "./official-external-install-records-DClkub0e.js"; import { c as resolvePluginMetadataSnapshot } from "./plugin-metadata-snapshot-w-4EjxI4.js"; import { n as resolvePluginControlPlaneWorkspace } from "./control-plane-workspace-BWhgt2gz.js"; import { t as createInstalledPluginOwnershipResolver } from "./installed-plugin-package-ownership-DGTVMT4h.js"; import { t as ManagedPluginLifecycleError } from "./management-lifecycle-error-BlJhejU6.js"; import { c as writePersistedInstalledPluginIndexInstallRecordsWithLease } from "./installed-plugin-index-records-C06Xozeq.js"; import { t as withPluginLifecycleLease } from "./plugin-lifecycle-lease-BtAFH872.js"; import { c as resolvePluginInstallRecordIntegrity, i as diffDeclaredSurfaceWidening, r as computeDeclaredSurfaceHash, s as resolveAcceptedSurfaceCurrent, t as buildPluginCapabilityConsentReview, u as resolvePluginPackageDeclaredSurface } from "./capability-summary-BLe-gZ01.js"; import { n as resolvePluginArtifactDeclaredSurface, t as inspectPluginCapabilityArtifact } from "./capability-artifact-ss_enFeU.js"; import path from "node:path"; //#region src/plugins/capability-consent.ts /** Preserve caller control-flow failures across installers that normalize exceptions. */ function capturePluginCapabilityConsentHandlerErrors(handler) { let failure; return { onCapabilityConsent: handler ? async (review) => { try { return await handler(review); } catch (error) { failure = { error }; throw error; } } : void 0, rethrowCallbackError: () => { if (failure) throw failure.error; } }; } const pendingPluginCapabilityReviews = /* @__PURE__ */ new Map(); registerPluginMetadataProcessMemoLifecycleClear(() => { pendingPluginCapabilityReviews.clear(); }); function resolvePendingPluginCapabilityReview(pluginId) { return pendingPluginCapabilityReviews.get(pluginId); } function acceptManagedPluginDeclaredSurface(record, declared) { const integrity = resolvePluginInstallRecordIntegrity(record)?.integrity; const accepted = { ...record, acceptedSurface: declared, acceptedSurfaceHash: computeDeclaredSurfaceHash(declared), acceptedSurfaceAt: (/* @__PURE__ */ new Date()).toISOString() }; delete accepted.acceptedSurfaceIntegrity; if (integrity) accepted.acceptedSurfaceIntegrity = integrity; return accepted; } function throwManagedPluginCapabilityConsentRequired(review) { pendingPluginCapabilityReviews.delete(review.pluginId); pendingPluginCapabilityReviews.set(review.pluginId, review); if (pendingPluginCapabilityReviews.size > 32) { const oldest = pendingPluginCapabilityReviews.keys().next().value; if (oldest !== void 0) pendingPluginCapabilityReviews.delete(oldest); } throw new ManagedPluginLifecycleError(`Plugin "${review.pluginId}" requires capability consent. Use openclaw plugins install or openclaw plugins enable with --accept-capabilities, then retry.`, { capabilityConsent: { pluginId: review.pluginId, reviewToken: review.reviewToken, ...review.widened ? { widened: review.widened } : {}, ...review.acceptedAt ? { acceptedAt: review.acceptedAt } : {} } }); } /** Enforce and durably acknowledge consent before an installed plugin is enabled. */ async function resolvePluginCapabilityConsent(params) { const env = params.env ?? process.env; return await withPluginLifecycleLease({ env }, async (lease) => { const workspace = resolvePluginControlPlaneWorkspace({ config: params.config, env }); const metadata = params.metadata ?? resolvePluginMetadataSnapshot({ allowCurrent: false, config: params.config, env, ...workspace.workspaceDir !== void 0 ? { workspaceDir: workspace.workspaceDir } : {} }); const pluginId = metadata.normalizePluginId(params.pluginId); const plugin = metadata.index.plugins.find((candidate) => candidate.pluginId === pluginId); if (!plugin || plugin.origin === "bundled") return; if (!resolveInstalledPluginIndexInstallOwner(plugin) && !isInstalledPluginIndexInstallOwnerAmbiguous(plugin) && !Object.hasOwn(metadata.index.installRecords, pluginId)) return; const ownership = createInstalledPluginOwnershipResolver(metadata.index, env).resolvePackage(pluginId); if (!ownership.ok) throw new ManagedPluginLifecycleError(ownership.error); const { installOwner, installRecord } = ownership.value; const manifest = metadata.byPluginId.get(pluginId); if (!manifest) throw new ManagedPluginLifecycleError(`Plugin "${pluginId}" has no installed manifest.`); if (manifest.trustedOfficialInstall) { pendingPluginCapabilityReviews.delete(pluginId); return; } const declared = resolvePluginPackageDeclaredSurface(ownership.value, metadata.byPluginId); if (!declared) throw new ManagedPluginLifecycleError(`Plugin package "${installOwner}" has incomplete manifest metadata.`); const review = buildPluginCapabilityConsentReview({ pluginId, manifest, record: installRecord, config: params.config, declared }); if (resolveAcceptedSurfaceCurrent(installRecord, declared)) { pendingPluginCapabilityReviews.delete(pluginId); return; } const acknowledgment = params.acknowledge ?? await params.onCapabilityConsent?.(review); if (!acknowledgment) throwManagedPluginCapabilityConsentRequired(review); await params.beforePersistentEffect?.(); const records = await loadInstalledPluginIndexInstallRecords({ env }); const persistedRecord = records[installOwner]; if (!persistedRecord?.installPath) throw new ManagedPluginLifecycleError(`Plugin "${pluginId}" no longer has a verifiable installed package record.`); const currentDeclared = resolvePluginArtifactDeclaredSurface(persistedRecord.installPath, env, { config: params.config }); const currentReview = buildPluginCapabilityConsentReview({ pluginId, manifest, record: persistedRecord, config: params.config, declared: currentDeclared }); if (acknowledgment.reviewToken !== currentReview.reviewToken) throwManagedPluginCapabilityConsentRequired(currentReview); await writePersistedInstalledPluginIndexInstallRecordsWithLease({ ...records, [installOwner]: acceptManagedPluginDeclaredSurface(persistedRecord, currentDeclared) }, { env, config: params.config, lease }); pendingPluginCapabilityReviews.delete(pluginId); }); } async function resolvePluginArtifactCapabilityConsent(params) { const artifactContext = { config: params.config, currentArtifactDir: params.currentArtifactDir }; const { declared, manifest } = inspectPluginCapabilityArtifact(params.artifactDir, params.env, artifactContext); const isOfficialArtifact = (artifactManifest) => params.sourceRecord !== void 0 && isTrustedOfficialPluginInstallRecord({ pluginId: params.pluginId, packageName: artifactManifest?.packageName, record: params.sourceRecord }); const official = isOfficialArtifact(manifest); const review = buildPluginCapabilityConsentReview({ pluginId: params.pluginId, manifest: manifest ?? { name: params.pluginId }, record: params.sourceRecord ?? params.record, config: params.config, declared, ...params.previousDeclared ? { previousDeclared: params.previousDeclared } : {} }); let acceptanceCurrent = false; if (params.mode === "update" && params.previousDeclared) { const { hasWidening } = diffDeclaredSurfaceWidening(params.previousDeclared, declared); const priorAcceptanceCurrent = params.previousRecord !== void 0 && resolveAcceptedSurfaceCurrent(params.previousRecord, params.previousDeclared) && resolvePluginInstallRecordIntegrity(params.previousRecord) !== void 0; acceptanceCurrent = !hasWidening && priorAcceptanceCurrent; } const acknowledgment = official || !params.enabled || acceptanceCurrent ? { reviewToken: review.reviewToken } : params.acknowledgeCapabilities ?? await params.onCapabilityConsent?.(review); if (acknowledgment) await params.beforePersistentEffect?.(); const { declared: finalDeclared, manifest: finalManifest } = inspectPluginCapabilityArtifact(params.artifactDir, params.env, artifactContext); const finalToken = computeDeclaredSurfaceHash(finalDeclared); if (!acknowledgment || acknowledgment.reviewToken !== finalToken || official && !isOfficialArtifact(finalManifest)) return throwManagedPluginCapabilityConsentRequired(finalToken === review.reviewToken ? review : buildPluginCapabilityConsentReview({ pluginId: params.pluginId, manifest: finalManifest ?? { name: params.pluginId }, record: params.sourceRecord ?? params.record, config: params.config, declared: finalDeclared, ...params.previousDeclared ? { previousDeclared: params.previousDeclared } : {} })); pendingPluginCapabilityReviews.delete(params.pluginId); return !official && (params.enabled || acceptanceCurrent) ? finalDeclared : void 0; } /** Bind artifact consent to verified staged bytes and carry acceptance into the record commit. */ function createManagedPluginArtifactConsentHandler(params) { const previousDeclaredByOwner = /* @__PURE__ */ new Map(); for (const [installOwner, record] of Object.entries(params.previousRecords ?? {})) if (record.installPath) try { previousDeclaredByOwner.set(installOwner, resolvePluginArtifactDeclaredSurface(record.installPath, params.env, { config: params.config })); } catch {} const pendingAcceptedSurfaces = /* @__PURE__ */ new Map(); return { onBeforePluginArtifactCommit: async (artifact) => { pendingAcceptedSurfaces.clear(); const matchingOwners = Object.entries(params.previousRecords ?? {}).filter(([installOwner, record]) => installOwner === artifact.pluginId || installOwner === params.previousPluginOwners?.get(artifact.pluginId) || Boolean(artifact.currentArtifactDir && record.installPath && path.resolve(resolveUserPath(artifact.currentArtifactDir, params.env)) === path.resolve(resolveUserPath(record.installPath, params.env)))); if (matchingOwners.length > 1) throw new ManagedPluginLifecycleError(`Plugin "${artifact.pluginId}" matches multiple installed package owners.`); const [installOwner, previousRecord] = matchingOwners[0] ?? []; const previousDeclared = installOwner ? previousDeclaredByOwner.get(installOwner) : void 0; const declared = await resolvePluginArtifactCapabilityConsent({ config: params.config, env: params.env, pluginId: artifact.pluginId, artifactDir: artifact.stagedArtifactDir, currentArtifactDir: previousRecord?.installPath ?? artifact.currentArtifactDir, record: { source: params.source, installPath: artifact.stagedArtifactDir, ...params.spec ? { spec: params.spec } : {}, ...params.expectedIntegrity ? { integrity: params.expectedIntegrity } : {} }, sourceRecord: artifact.sourceRecord, acknowledgeCapabilities: params.acknowledgeCapabilities, onCapabilityConsent: params.onCapabilityConsent, beforePersistentEffect: params.beforePersistentEffect, ...previousRecord ? { previousRecord } : {}, ...previousDeclared ? { previousDeclared } : {}, mode: artifact.mode, enabled: !params.updatingPluginIds?.length || params.updatingPluginIds.some((id) => resolveEffectiveEnableState({ id, origin: "global", config: normalizePluginsConfig(params.config.plugins), rootConfig: params.config }).enabled) }); pendingAcceptedSurfaces.set(artifact.pluginId, declared); }, applyAcceptedSurface: (pluginId, record) => { const declared = pendingAcceptedSurfaces.get(pluginId); if (!pendingAcceptedSurfaces.has(pluginId)) throw new ManagedPluginLifecycleError(`Plugin "${pluginId}" did not expose its verified artifact for capability review.`); return declared ? acceptManagedPluginDeclaredSurface(record, declared) : record; } }; } /** Prepare the same package-owned consent history for every managed installer. */ async function prepareManagedPluginArtifactConsentHandler(params) { const env = params.env ?? process.env; const previousRecords = params.previousRecords ?? await loadInstalledPluginIndexInstallRecords({ env }); const workspace = resolvePluginControlPlaneWorkspace({ config: params.config, env }); const metadata = Object.keys(previousRecords).length > 0 ? resolvePluginMetadataSnapshot({ allowCurrent: false, config: params.config, env, ...workspace.workspaceDir !== void 0 ? { workspaceDir: workspace.workspaceDir } : {} }) : void 0; const previousPluginOwners = /* @__PURE__ */ new Map(); for (const plugin of metadata?.index.plugins ?? []) { const owner = resolveInstalledPluginIndexInstallOwner(plugin); if (owner) previousPluginOwners.set(plugin.pluginId, owner); } return createManagedPluginArtifactConsentHandler({ ...params, env, previousRecords, previousPluginOwners }); } //#endregion export { resolvePluginCapabilityConsent as a, resolvePendingPluginCapabilityReview as i, createManagedPluginArtifactConsentHandler as n, prepareManagedPluginArtifactConsentHandler as r, capturePluginCapabilityConsentHandlerErrors as t };