UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

670 lines (669 loc) 29.3 kB
import { t as isTruthyEnvValue } from "./env-Di49gJwo.js"; import { o as isRecord } from "./record-coerce-DHZ4bFlT.js"; import { p as resolveIsNixMode } from "./paths-mvMm5bYV.js"; import { n as appendRegularFileSync } from "./regular-file-BD2zl6_l.js"; import { p as resolveUserPath } from "./utils-CCC-BEJH.js"; import { o as resolveCompatibilityHostVersion } from "./version-Crcn9X9T.js"; import "./logger-Brhy0cvC.js"; import { i as normalizeProviderId } from "./provider-id-Dq06Bcx6.js"; import { t as loadPluginManifestRegistry } from "./manifest-registry-BywZIeAq.js"; import { a as resolveDefaultPluginNpmDir, l as resolvePluginNpmProjectsDir } from "./install-paths-D-sCLR1I.js"; import { d as serializePluginIdScope, p as resolvePluginControlPlaneFingerprint, r as getCurrentPluginMetadataSnapshot, u as normalizePluginIdScope } from "./current-plugin-metadata-snapshot-CfA_n2Nc.js"; import { n as registerPluginMetadataProcessMemoLifecycleClear } from "./plugin-metadata-lifecycle-C3dWg4tn.js"; import { g as hashJson, m as resolveInstalledPluginIndexPolicyHash } from "./installed-plugin-index-BSYm7o5l.js"; import { n as resolveInstalledManifestRegistryIndexFingerprint, t as loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed-DSyeeqEt.js"; import { g as createPluginRegistryIdNormalizer, m as loadPluginRegistrySnapshotWithMetadata } from "./plugin-registry-ChcJPHWl.js"; import { i as readPersistedInstalledPluginIndexSync } from "./installed-plugin-index-store-DyTBte5L.js"; import { t as isDiagnosticFlagEnabled } from "./diagnostic-flags-4mY6zjJI.js"; import fs, { mkdirSync } from "node:fs"; import path, { dirname } from "node:path"; import { randomUUID } from "node:crypto"; import { AsyncLocalStorage } from "node:async_hooks"; import { performance as performance$1 } from "node:perf_hooks"; //#region src/infra/diagnostics-timeline.ts const OPENCLAW_DIAGNOSTICS_TIMELINE_SCHEMA_VERSION = "openclaw.diagnostics.v1"; let warnedAboutTimelineWrite = false; const createdTimelineDirs = /* @__PURE__ */ new Set(); const activeDiagnosticsTimelineSpan = new AsyncLocalStorage(); function resolveDiagnosticsTimelineOptions(options = {}) { return { env: options.env ?? process.env, ...options.config ? { config: options.config } : {} }; } /** Returns true when diagnostics flags and a JSONL output path both allow timeline writes. */ function isDiagnosticsTimelineEnabled(options = {}) { const { config, env } = resolveDiagnosticsTimelineOptions(options); return (isDiagnosticFlagEnabled("timeline", config, env) || isDiagnosticFlagEnabled("diagnostics.timeline", config, env) || isTruthyEnvValue(env.OPENCLAW_DIAGNOSTICS)) && typeof env.OPENCLAW_DIAGNOSTICS_TIMELINE_PATH === "string" && env.OPENCLAW_DIAGNOSTICS_TIMELINE_PATH.trim().length > 0; } function normalizeNumber(value) { if (typeof value !== "number" || !Number.isFinite(value)) return; return Math.max(0, Math.round(value * 1e3) / 1e3); } function normalizeAttributes(attributes) { if (!attributes) return; const normalized = {}; for (const [key, value] of Object.entries(attributes)) { if (typeof value === "number") { if (Number.isFinite(value)) normalized[key] = normalizeNumber(value) ?? 0; continue; } if (typeof value === "string" || typeof value === "boolean" || value === null) normalized[key] = value; } return Object.keys(normalized).length > 0 ? normalized : void 0; } function serializeTimelineEvent(event, env) { const normalized = { schemaVersion: OPENCLAW_DIAGNOSTICS_TIMELINE_SCHEMA_VERSION, type: event.type, timestamp: event.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(), name: event.name, ...env.OPENCLAW_DIAGNOSTICS_RUN_ID ? { runId: env.OPENCLAW_DIAGNOSTICS_RUN_ID } : {}, ...env.OPENCLAW_DIAGNOSTICS_ENV ? { envName: env.OPENCLAW_DIAGNOSTICS_ENV } : {}, pid: process.pid, ...event.runId ? { runId: event.runId } : {}, ...event.envName ? { envName: event.envName } : {}, ...typeof event.pid === "number" ? { pid: event.pid } : {}, ...event.phase ? { phase: event.phase } : {}, ...event.spanId ? { spanId: event.spanId } : {}, ...event.parentSpanId ? { parentSpanId: event.parentSpanId } : {}, ...typeof event.durationMs === "number" ? { durationMs: normalizeNumber(event.durationMs) } : {}, ...event.errorName ? { errorName: event.errorName } : {}, ...event.errorMessage ? { errorMessage: event.errorMessage } : {}, ...typeof event.p50Ms === "number" ? { p50Ms: normalizeNumber(event.p50Ms) } : {}, ...typeof event.p95Ms === "number" ? { p95Ms: normalizeNumber(event.p95Ms) } : {}, ...typeof event.p99Ms === "number" ? { p99Ms: normalizeNumber(event.p99Ms) } : {}, ...typeof event.maxMs === "number" ? { maxMs: normalizeNumber(event.maxMs) } : {}, ...event.activeSpanName ? { activeSpanName: event.activeSpanName } : {}, ...event.provider ? { provider: event.provider } : {}, ...event.operation ? { operation: event.operation } : {}, ...typeof event.ok === "boolean" ? { ok: event.ok } : {}, ...event.command ? { command: event.command } : {}, ...event.exitCode !== void 0 ? { exitCode: event.exitCode } : {}, ...event.signal !== void 0 ? { signal: event.signal } : {}, ...normalizeAttributes(event.attributes) ? { attributes: normalizeAttributes(event.attributes) } : {} }; return `${JSON.stringify(normalized)}\n`; } /** Appends one normalized diagnostics timeline event to the configured JSONL file. */ function emitDiagnosticsTimelineEvent(event, options = {}) { const { env } = resolveDiagnosticsTimelineOptions(options); if (!isDiagnosticsTimelineEnabled(options)) return; const path = env.OPENCLAW_DIAGNOSTICS_TIMELINE_PATH?.trim(); if (!path) return; const line = serializeTimelineEvent(event, env); try { const dir = dirname(path); if (!createdTimelineDirs.has(dir)) { mkdirSync(dir, { recursive: true }); createdTimelineDirs.add(dir); } appendRegularFileSync({ filePath: path, content: line }); } catch (error) { if (!warnedAboutTimelineWrite) { warnedAboutTimelineWrite = true; process.stderr.write(`[diagnostics] failed to write timeline event: ${String(error)}\n`); } } } /** Returns the currently active span so callers can preserve parentage across memoized work. */ function getActiveDiagnosticsTimelineSpan() { return activeDiagnosticsTimelineSpan.getStore(); } function startDiagnosticsTimelineSpan(name, options) { const env = options.env ?? process.env; if (!isDiagnosticsTimelineEnabled({ config: options.config, env })) return; const activeSpan = getActiveDiagnosticsTimelineSpan(); const phase = options.phase ?? activeSpan?.phase; const parentSpanId = options.parentSpanId ?? activeSpan?.spanId; const span = { name, env, ...options.config ? { config: options.config } : {}, spanId: randomUUID(), startedAt: performance$1.now(), ...phase ? { phase } : {}, ...parentSpanId ? { parentSpanId } : {}, ...options.attributes ? { attributes: options.attributes } : {}, ...options.omitErrorMessage ? { omitErrorMessage: true } : {} }; emitDiagnosticsTimelineEvent({ type: "span.start", name: span.name, phase: span.phase, spanId: span.spanId, parentSpanId: span.parentSpanId, attributes: span.attributes }, { config: span.config, env: span.env }); return span; } function runInDiagnosticsTimelineSpan(span, run) { return activeDiagnosticsTimelineSpan.run({ name: span.name, ...span.phase ? { phase: span.phase } : {}, spanId: span.spanId, ...span.parentSpanId ? { parentSpanId: span.parentSpanId } : {}, ...span.attributes ? { attributes: span.attributes } : {} }, run); } function emitFinishedDiagnosticsTimelineSpan(span) { emitDiagnosticsTimelineEvent({ type: "span.end", name: span.name, phase: span.phase, spanId: span.spanId, parentSpanId: span.parentSpanId, durationMs: performance$1.now() - span.startedAt, attributes: span.attributes }, { config: span.config, env: span.env }); } function emitFailedDiagnosticsTimelineSpan(span, error) { emitDiagnosticsTimelineEvent({ type: "span.error", name: span.name, phase: span.phase, spanId: span.spanId, parentSpanId: span.parentSpanId, durationMs: performance$1.now() - span.startedAt, attributes: span.attributes, errorName: error instanceof Error ? error.name : typeof error, ...span.omitErrorMessage ? {} : { errorMessage: error instanceof Error ? error.message : String(error) } }, { config: span.config, env: span.env }); } /** Measures async work as a start/end timeline span, emitting an error span before rethrowing. */ async function measureDiagnosticsTimelineSpan(name, run, options = {}) { const span = startDiagnosticsTimelineSpan(name, options); if (!span) return await run(); try { const result = await runInDiagnosticsTimelineSpan(span, () => run()); emitFinishedDiagnosticsTimelineSpan(span); return result; } catch (error) { emitFailedDiagnosticsTimelineSpan(span, error); throw error; } } /** Measures sync work as a start/end timeline span, emitting an error span before rethrowing. */ function measureDiagnosticsTimelineSpanSync(name, run, options = {}) { const span = startDiagnosticsTimelineSpan(name, options); if (!span) return run(); try { const result = runInDiagnosticsTimelineSpan(span, run); emitFinishedDiagnosticsTimelineSpan(span); return result; } catch (error) { emitFailedDiagnosticsTimelineSpan(span, error); throw error; } } //#endregion //#region src/plugins/plugin-metadata-snapshot.ts const MAX_PLUGIN_METADATA_SNAPSHOT_MEMOS = 8; let pluginMetadataSnapshotMemos = []; function clearLoadPluginMetadataSnapshotMemo() { pluginMetadataSnapshotMemos = []; } registerPluginMetadataProcessMemoLifecycleClear(clearLoadPluginMetadataSnapshotMemo); const MEMO_RELEVANT_ENV_KEYS = [ "APPDATA", "HOME", "OPENCLAW_BUNDLED_PLUGINS_DIR", "OPENCLAW_COMPATIBILITY_HOST_VERSION", "OPENCLAW_CONFIG_PATH", "OPENCLAW_DISABLE_BUNDLED_PLUGINS", "OPENCLAW_DISABLE_BUNDLED_SOURCE_OVERLAYS", "OPENCLAW_DISABLE_PERSISTED_PLUGIN_REGISTRY", "OPENCLAW_HOME", "OPENCLAW_NIX_MODE", "OPENCLAW_STATE_DIR", "USERPROFILE", "XDG_CONFIG_HOME" ]; function fileFingerprint(filePath) { try { const stat = fs.statSync(filePath, { bigint: true }); return [ filePath, stat.isFile() ? "file" : stat.isDirectory() ? "dir" : "other", stat.size.toString(), stat.mtimeNs.toString(), stat.ctimeNs.toString() ]; } catch { return [filePath, "missing"]; } } function directoryChildPackageJsonFingerprint(directoryPath) { let entries; try { entries = fs.readdirSync(directoryPath, { withFileTypes: true }); } catch { return [directoryPath, "missing"]; } return [directoryPath, ...entries.filter((entry) => entry.isDirectory()).toSorted((a, b) => a.name.localeCompare(b.name)).map((entry) => fileFingerprint(path.join(directoryPath, entry.name, "package.json")))]; } function stableMemoValue(value) { if (Array.isArray(value)) return value.map(stableMemoValue); if (!isRecord(value)) return value; return Object.fromEntries(Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, stableMemoValue(entry)])); } function pickMemoRelevantEnv(env) { return Object.fromEntries(MEMO_RELEVANT_ENV_KEYS.flatMap((key) => { const value = env[key]; return value === void 0 ? [] : [[key, value]]; })); } function resolvePluginMetadataSnapshotMemoEnvFingerprint(env) { return hashJson(pickMemoRelevantEnv(env)); } function throwReadonlyPluginMetadataMutation() { throw new TypeError("Plugin metadata snapshots are immutable"); } function freezeSnapshotValue(value, seen = /* @__PURE__ */ new WeakSet()) { if (!value || typeof value !== "object") return value; if (seen.has(value)) return value; seen.add(value); if (value instanceof Map) { for (const [key, entry] of value) { freezeSnapshotValue(key, seen); freezeSnapshotValue(entry, seen); } Object.defineProperties(value, { clear: { value: throwReadonlyPluginMetadataMutation }, delete: { value: throwReadonlyPluginMetadataMutation }, set: { value: throwReadonlyPluginMetadataMutation } }); return Object.freeze(value); } if (value instanceof Set) { for (const entry of value) freezeSnapshotValue(entry, seen); Object.defineProperties(value, { add: { value: throwReadonlyPluginMetadataMutation }, clear: { value: throwReadonlyPluginMetadataMutation }, delete: { value: throwReadonlyPluginMetadataMutation } }); return Object.freeze(value); } for (const entry of Object.values(value)) freezeSnapshotValue(entry, seen); return Object.freeze(value); } function freezePluginMetadataSnapshot(snapshot) { return freezeSnapshotValue(snapshot); } function resolvePersistedRegistryFastMemoFingerprint(params) { const disabledByEnv = params.env.OPENCLAW_DISABLE_PERSISTED_PLUGIN_REGISTRY?.trim().toLowerCase(); if (params.preferPersisted === false || Boolean(disabledByEnv) && disabledByEnv !== "0" && disabledByEnv !== "false" && disabledByEnv !== "no") return { disabled: true }; const npmRoot = params.stateDir ? path.join(params.stateDir, "npm") : resolveDefaultPluginNpmDir(params.env); return { index: hashJson(stableMemoValue(readPersistedInstalledPluginIndexSync({ env: params.env, ...params.stateDir ? { stateDir: params.stateDir } : {} })) ?? null), npmPackageJson: fileFingerprint(path.join(npmRoot, "package.json")), npmProjectPackageJsons: directoryChildPackageJsonFingerprint(resolvePluginNpmProjectsDir(npmRoot)) }; } function resolvePersistedRegistryMemoContextHash(params) { return hashJson({ env: pickMemoRelevantEnv(params.env), fastFingerprint: params.fastFingerprint, preferPersisted: params.preferPersisted ?? null, stateDir: params.stateDir ?? null }); } function resolvePersistedRegistryMemoLookupContextHash(params) { return hashJson({ env: pickMemoRelevantEnv(params.env), preferPersisted: params.preferPersisted ?? null, stateDir: params.stateDir ?? null }); } function resolvePersistedRegistryMemoState(params) { const fastFingerprint = resolvePersistedRegistryFastMemoFingerprint(params); const fastHash = hashJson(fastFingerprint); const contextHash = resolvePersistedRegistryMemoContextHash({ ...params, fastFingerprint }); if (isRecord(fastFingerprint) && fastFingerprint.disabled === true) return { contextHash, fastHash, fingerprint: fastFingerprint }; const index = readPersistedInstalledPluginIndexSync({ env: params.env, ...params.stateDir ? { stateDir: params.stateDir } : {} }); return { contextHash, fastHash, fingerprint: { ...fastFingerprint, indexHash: hashJson(stableMemoValue(index) ?? null) } }; } function resolvePersistedRegistryMemoStateForLookup(params, memos) { const lookupContextHash = resolvePersistedRegistryMemoLookupContextHash(params); for (const memo of memos) if (memo.lookupContextHash === lookupContextHash && memo.registryState) return memo.registryState; const fastFingerprint = resolvePersistedRegistryFastMemoFingerprint(params); const fastHash = hashJson(fastFingerprint); const contextHash = resolvePersistedRegistryMemoContextHash({ ...params, fastFingerprint }); for (const memo of memos) { const registryState = memo.registryState; if (registryState && registryState.contextHash === contextHash && registryState.fastHash === fastHash) return registryState; } return resolvePersistedRegistryMemoState(params); } function resolveProvidedIndexMemoState(index) { const fingerprint = { providedIndex: resolveInstalledManifestRegistryIndexFingerprint(index) }; const fingerprintHash = hashJson(fingerprint); return { contextHash: fingerprintHash, fastHash: fingerprintHash, fingerprint }; } function findPluginMetadataSnapshotMemo(key) { const index = pluginMetadataSnapshotMemos.findIndex((memo) => memo.key === key); if (index === -1) return; const [memo] = pluginMetadataSnapshotMemos.splice(index, 1); if (!memo) return; pluginMetadataSnapshotMemos.unshift(memo); return memo; } function rememberPluginMetadataSnapshotMemo(memo) { pluginMetadataSnapshotMemos = [memo, ...pluginMetadataSnapshotMemos.filter((existing) => existing.key !== memo.key)].slice(0, MAX_PLUGIN_METADATA_SNAPSHOT_MEMOS); } function computePluginMetadataSnapshotMemoKey(params) { const { params: snapshotParams, registryState } = params; const env = snapshotParams.env ?? process.env; const indexFingerprint = snapshotParams.index ? resolveInstalledManifestRegistryIndexFingerprint(snapshotParams.index) : void 0; return hashJson({ controlPlane: resolvePluginControlPlaneFingerprint({ config: snapshotParams.config, env, workspaceDir: snapshotParams.workspaceDir, policyHash: resolveInstalledPluginIndexPolicyHash(snapshotParams.config), ...indexFingerprint ? { inventoryFingerprint: indexFingerprint } : {} }), cwd: process.cwd(), env: pickMemoRelevantEnv(env), index: indexFingerprint ?? null, pathPolicy: { compatibilityHostVersion: resolveCompatibilityHostVersion(env), nixMode: resolveIsNixMode(env) }, pluginIds: serializePluginIdScope(normalizePluginIdScope(snapshotParams.pluginIds)), pluginIdScopeKey: snapshotParams.pluginIdScope?.key ?? null, preferPersisted: snapshotParams.preferPersisted ?? null, registry: registryState.fingerprint, stateDir: snapshotParams.stateDir ? resolveUserPath(snapshotParams.stateDir, env) : null, workspaceDir: snapshotParams.workspaceDir ?? null }); } function resolvePluginMetadataControlPlaneFingerprint(params) { return resolvePluginControlPlaneFingerprint(params); } function indexesMatch(left, right) { if (!left || !right) return true; return resolveInstalledManifestRegistryIndexFingerprint(left) === resolveInstalledManifestRegistryIndexFingerprint(right); } function cloneSnapshotInput(value) { return value && typeof value === "object" ? structuredClone(value) : value; } function normalizeInstalledPluginIndex(index) { return { version: index.version ?? 1, hostContractVersion: index.hostContractVersion ?? "", compatRegistryVersion: index.compatRegistryVersion ?? "", migrationVersion: index.migrationVersion ?? 1, policyHash: index.policyHash ?? "", generatedAtMs: index.generatedAtMs ?? 0, installRecords: cloneSnapshotInput(index.installRecords ?? {}), plugins: (index.plugins ?? []).map(cloneSnapshotInput), diagnostics: (index.diagnostics ?? []).map(cloneSnapshotInput), ...index.warning ? { warning: index.warning } : {}, ...index.refreshReason ? { refreshReason: index.refreshReason } : {} }; } function resolvePluginMetadataSnapshotPluginIds(params) { const direct = normalizePluginIdScope(params.params.pluginIds); if (direct !== void 0) return direct; return normalizePluginIdScope(params.params.pluginIdScope?.resolve({ index: params.index })); } function isPluginMetadataSnapshotCompatible(params) { const env = params.env ?? process.env; const requestedPluginIds = normalizePluginIdScope(params.pluginIds); const snapshotPluginIds = normalizePluginIdScope(params.snapshot.pluginIds); return (snapshotPluginIds === void 0 || params.allowScopedSnapshot === true || requestedPluginIds !== void 0 && serializePluginIdScope(snapshotPluginIds) === serializePluginIdScope(requestedPluginIds)) && params.snapshot.policyHash === resolveInstalledPluginIndexPolicyHash(params.config) && (!params.snapshot.configFingerprint || params.snapshot.configFingerprint === resolvePluginMetadataControlPlaneFingerprint({ config: params.config, env, index: params.index ?? params.snapshot.index, policyHash: params.snapshot.policyHash, workspaceDir: params.workspaceDir })) && (params.snapshot.workspaceDir ?? "") === (params.workspaceDir ?? "") && indexesMatch(params.snapshot.index, params.index); } function appendOwner(owners, ownedId, pluginId) { const existing = owners.get(ownedId); if (existing) { if (existing.includes(pluginId)) return; existing.push(pluginId); return; } owners.set(ownedId, [pluginId]); } function freezeOwnerMap(owners) { return new Map([...owners.entries()].map(([ownedId, pluginIds]) => [ownedId, Object.freeze([...pluginIds])])); } function buildPluginMetadataOwnerMaps(plugins) { const channels = /* @__PURE__ */ new Map(); const channelConfigs = /* @__PURE__ */ new Map(); const providers = /* @__PURE__ */ new Map(); const modelCatalogProviders = /* @__PURE__ */ new Map(); const cliBackends = /* @__PURE__ */ new Map(); const setupProviders = /* @__PURE__ */ new Map(); const commandAliases = /* @__PURE__ */ new Map(); const contracts = /* @__PURE__ */ new Map(); for (const plugin of plugins) { for (const channelId of plugin.channels ?? []) appendOwner(channels, channelId, plugin.id); for (const channelId of Object.keys(plugin.channelConfigs ?? {})) appendOwner(channelConfigs, channelId, plugin.id); for (const providerId of plugin.providers ?? []) appendOwner(providers, providerId, plugin.id); for (const [rawAlias, target] of Object.entries(plugin.providerAuthAliases ?? {})) { const alias = normalizeProviderId(rawAlias); const targetProvider = normalizeProviderId(target); if (alias && targetProvider && (plugin.providers ?? []).some((providerId) => normalizeProviderId(providerId) === targetProvider)) appendOwner(providers, alias, plugin.id); } for (const providerId of Object.keys(plugin.modelCatalog?.providers ?? {})) appendOwner(modelCatalogProviders, providerId, plugin.id); for (const providerId of Object.keys(plugin.modelCatalog?.aliases ?? {})) appendOwner(modelCatalogProviders, providerId, plugin.id); for (const cliBackendId of plugin.cliBackends ?? []) appendOwner(cliBackends, cliBackendId, plugin.id); for (const cliBackendId of plugin.setup?.cliBackends ?? []) appendOwner(cliBackends, cliBackendId, plugin.id); for (const setupProvider of plugin.setup?.providers ?? []) appendOwner(setupProviders, setupProvider.id, plugin.id); for (const commandAlias of plugin.commandAliases ?? []) appendOwner(commandAliases, commandAlias.name, plugin.id); for (const [contract, values] of Object.entries(plugin.contracts ?? {})) if (Array.isArray(values) && values.length > 0) appendOwner(contracts, contract, plugin.id); } return { channels: freezeOwnerMap(channels), channelConfigs: freezeOwnerMap(channelConfigs), providers: freezeOwnerMap(providers), modelCatalogProviders: freezeOwnerMap(modelCatalogProviders), cliBackends: freezeOwnerMap(cliBackends), setupProviders: freezeOwnerMap(setupProviders), commandAliases: freezeOwnerMap(commandAliases), contracts: freezeOwnerMap(contracts) }; } function listPluginOriginsFromMetadataSnapshot(snapshot) { return new Map(snapshot.plugins.map((record) => [record.id, record.origin])); } function loadPluginMetadataSnapshot(params) { const activeTimelineSpan = getActiveDiagnosticsTimelineSpan(); const env = params.env ?? process.env; const registryState = params.index ? resolveProvidedIndexMemoState(params.index) : resolvePersistedRegistryMemoStateForLookup({ env, ...params.stateDir ? { stateDir: resolveUserPath(params.stateDir, env) } : {}, ...params.preferPersisted !== void 0 ? { preferPersisted: params.preferPersisted } : {} }, pluginMetadataSnapshotMemos); const memoKey = computePluginMetadataSnapshotMemoKey({ params, registryState }); const memo = findPluginMetadataSnapshotMemo(memoKey); if (memo?.key === memoKey) return measureDiagnosticsTimelineSpanSync("plugins.metadata.scan", () => memo.snapshot, { phase: activeTimelineSpan?.phase ?? "startup", config: params.config, env: params.env, attributes: { cacheHit: true, hasWorkspaceDir: params.workspaceDir !== void 0, hasInstalledIndex: params.index !== void 0 } }); const result = measureDiagnosticsTimelineSpanSync("plugins.metadata.scan", () => loadPluginMetadataSnapshotImpl(params), { phase: activeTimelineSpan?.phase ?? "startup", config: params.config, env: params.env, attributes: { hasWorkspaceDir: params.workspaceDir !== void 0, hasInstalledIndex: params.index !== void 0 } }); const snapshot = freezePluginMetadataSnapshot(result.snapshot); if (canMemoizePluginMetadataSnapshotResult(result)) rememberPluginMetadataSnapshotMemo({ key: memoKey, lookupContextHash: resolvePersistedRegistryMemoLookupContextHash({ env, ...params.stateDir ? { stateDir: resolveUserPath(params.stateDir, env) } : {}, ...params.preferPersisted !== void 0 ? { preferPersisted: params.preferPersisted } : {} }), registryState, snapshot }); return snapshot; } function canMemoizePluginMetadataSnapshotResult(result) { const snapshot = result.snapshot; const hasCompleteSnapshotShape = Array.isArray(snapshot.plugins) && Array.isArray(snapshot.diagnostics) && Array.isArray(snapshot.registryDiagnostics) && Array.isArray(snapshot.manifestRegistry.plugins) && Array.isArray(snapshot.manifestRegistry.diagnostics) && Array.isArray(snapshot.index.plugins) && Array.isArray(snapshot.index.diagnostics); const hasPluginMetadata = snapshot.plugins.length > 0 || snapshot.index.plugins.length > 0; return hasCompleteSnapshotShape && hasPluginMetadata; } function resolvePluginMetadataSnapshot(params) { if (params.allowCurrent !== false && params.stateDir === void 0 && params.preferPersisted !== false) { const current = getCurrentPluginMetadataSnapshot({ config: params.config, env: params.env, ...params.pluginIds !== void 0 ? { pluginIds: params.pluginIds } : {}, ...params.pluginIdScope !== void 0 ? { pluginIdScope: params.pluginIdScope } : {}, ...params.workspaceDir !== void 0 ? { workspaceDir: params.workspaceDir } : {}, ...params.allowWorkspaceScopedCurrent === true ? { allowWorkspaceScopedSnapshot: true } : {} }); if (!current) return loadPluginMetadataSnapshot(params); if (!params.index) return current; if (isPluginMetadataSnapshotCompatible({ snapshot: current, config: params.config, env: params.env, allowScopedSnapshot: params.pluginIds !== void 0 || params.pluginIdScope !== void 0, workspaceDir: params.workspaceDir ?? (params.allowWorkspaceScopedCurrent === true ? current.workspaceDir : void 0), index: params.index })) return current; } return loadPluginMetadataSnapshot(params); } function loadPluginMetadataSnapshotImpl(params) { const totalStartedAt = performance.now(); const registryStartedAt = performance.now(); const registryResult = loadPluginRegistrySnapshotWithMetadata({ config: params.config, workspaceDir: params.workspaceDir, ...params.stateDir ? { stateDir: params.stateDir } : {}, env: params.env, ...params.preferPersisted !== void 0 ? { preferPersisted: params.preferPersisted } : {}, ...params.index ? { index: params.index } : {} }) ?? { source: "derived", snapshot: { plugins: [] }, diagnostics: [] }; const registrySnapshotMs = performance.now() - registryStartedAt; const index = normalizeInstalledPluginIndex(registryResult.snapshot); const pluginIds = resolvePluginMetadataSnapshotPluginIds({ params, index }); const manifestStartedAt = performance.now(); const manifestRegistry = index.plugins.length === 0 ? loadPluginManifestRegistry({ config: params.config, workspaceDir: params.workspaceDir, env: params.env, diagnostics: [...index.diagnostics], installRecords: index.installRecords }) : loadPluginManifestRegistryForInstalledIndex({ index, config: params.config, workspaceDir: params.workspaceDir, env: params.env, ...pluginIds !== void 0 ? { pluginIds } : {}, includeDisabled: true }); const manifestRegistryMs = performance.now() - manifestStartedAt; const normalizePluginId = createPluginRegistryIdNormalizer(index, { manifestRegistry }); const byPluginId = new Map(manifestRegistry.plugins.map((plugin) => [plugin.id, plugin])); const ownerMapsStartedAt = performance.now(); const owners = buildPluginMetadataOwnerMaps(manifestRegistry.plugins); const ownerMapsMs = performance.now() - ownerMapsStartedAt; const totalMs = performance.now() - totalStartedAt; return { registrySource: registryResult.source, snapshot: { policyHash: index.policyHash, registrySource: registryResult.source, configFingerprint: resolvePluginMetadataControlPlaneFingerprint({ config: params.config, env: params.env, index, policyHash: index.policyHash, workspaceDir: params.workspaceDir }), ...pluginIds !== void 0 ? { pluginIds } : {}, ...params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}, index, registryDiagnostics: registryResult.diagnostics, manifestRegistry, plugins: manifestRegistry.plugins, diagnostics: manifestRegistry.diagnostics, byPluginId, normalizePluginId, owners, metrics: { registrySnapshotMs, manifestRegistryMs, ownerMapsMs, totalMs, indexPluginCount: index.plugins.length, manifestPluginCount: manifestRegistry.plugins.length }, discovery: registryResult.discovery } }; } //#endregion export { resolvePluginMetadataSnapshot as a, isDiagnosticsTimelineEnabled as c, loadPluginMetadataSnapshot as i, measureDiagnosticsTimelineSpan as l, isPluginMetadataSnapshotCompatible as n, resolvePluginMetadataSnapshotMemoEnvFingerprint as o, listPluginOriginsFromMetadataSnapshot as r, emitDiagnosticsTimelineEvent as s, clearLoadPluginMetadataSnapshotMemo as t, measureDiagnosticsTimelineSpanSync as u };