openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
179 lines (178 loc) • 7.55 kB
JavaScript
import { m as loadPluginRegistrySnapshotWithMetadata } from "./plugin-registry-ChcJPHWl.js";
import { i as loadPluginMetadataSnapshot } from "./plugin-metadata-snapshot-Ctk9Oarq.js";
import { i as getRuntimeConfig } from "./io-Gi7-pyU-.js";
import "./config-C9RxTsn1.js";
import "./registry-CYsBNH12.js";
import { I as createEmptyPluginRegistry } from "./runtime-B2ROiq5l.js";
import fs from "node:fs";
import path from "node:path";
//#region src/plugins/status-snapshot-dependencies.ts
/** Builds dependency install status for plugin status snapshots without importing discovery. */
function dependencyPathSegments(name) {
const segments = name.split("/");
if (segments.length === 1 && segments[0]) return [segments[0]];
if (segments.length === 2 && segments[0]?.startsWith("@") && segments[1]) return segments;
return null;
}
function findDependencyPackageDir(params) {
const segments = dependencyPathSegments(params.name);
if (!segments) return;
let current = path.resolve(params.fromDir);
while (true) {
const candidate = path.join(current, "node_modules", ...segments);
if (fs.existsSync(candidate)) return candidate;
const parent = path.dirname(current);
if (parent === current) return;
current = parent;
}
}
function buildDependencyEntries(params) {
return Object.entries(params.dependencies).toSorted(([left], [right]) => left.localeCompare(right)).map(([name, spec]) => {
const resolvedPath = params.rootDir ? findDependencyPackageDir({
fromDir: params.rootDir,
name
}) : void 0;
const entry = {
name,
spec,
installed: resolvedPath !== void 0,
optional: params.optional
};
if (resolvedPath) entry.resolvedPath = resolvedPath;
return entry;
});
}
/** Resolves required and optional dependency status for one plugin root. */
function buildSnapshotPluginDependencyStatus(params) {
const dependencies = buildDependencyEntries({
rootDir: params.rootDir,
dependencies: params.dependencies ?? {},
optional: false
});
const optionalDependencies = buildDependencyEntries({
rootDir: params.rootDir,
dependencies: params.optionalDependencies ?? {},
optional: true
});
const missing = dependencies.filter((entry) => !entry.installed).map((entry) => entry.name);
const missingOptional = optionalDependencies.filter((entry) => !entry.installed).map((entry) => entry.name);
const requiredInstalled = missing.length === 0;
const optionalInstalled = missingOptional.length === 0;
return {
hasDependencies: dependencies.length > 0 || optionalDependencies.length > 0,
installed: requiredInstalled,
requiredInstalled,
optionalInstalled,
missing,
missingOptional,
dependencies,
optionalDependencies
};
}
//#endregion
//#region src/plugins/status-snapshot.ts
/** Builds plugin status reports from persisted metadata without importing full plugin runtimes. */
function isPluginLifecycleTraceEnabled() {
const raw = process.env.OPENCLAW_PLUGIN_LIFECYCLE_TRACE?.trim().toLowerCase();
return raw === "1" || raw === "true" || raw === "yes";
}
function formatTraceValue(value) {
if (typeof value === "number" || typeof value === "boolean") return String(value);
return JSON.stringify(value);
}
function tracePluginLifecyclePhase(phase, fn, details) {
if (!isPluginLifecycleTraceEnabled()) return fn();
const start = process.hrtime.bigint();
let status;
try {
const result = fn();
status = "ok";
return result;
} catch (error) {
status = "error";
throw error;
} finally {
const elapsedMs = Number(process.hrtime.bigint() - start) / 1e6;
const detailText = Object.entries(details ?? {}).filter((entry) => entry[1] !== void 0).map(([key, value]) => `${key}=${formatTraceValue(value)}`).join(" ");
const suffix = detailText ? ` ${detailText}` : "";
console.error(`[plugins:lifecycle] phase=${JSON.stringify(phase)} ms=${elapsedMs.toFixed(2)} status=${status ?? "error"}${suffix}`);
}
}
function buildPluginRecordFromInstalledIndex(plugin, manifest) {
const format = plugin.format ?? manifest?.format ?? "openclaw";
const bundleFormat = plugin.bundleFormat ?? manifest?.bundleFormat;
return {
id: plugin.pluginId,
name: manifest?.name ?? plugin.packageName ?? plugin.pluginId,
...plugin.packageVersion || manifest?.version ? { version: plugin.packageVersion ?? manifest?.version } : {},
...manifest?.description ? { description: manifest.description } : {},
format,
...bundleFormat ? { bundleFormat } : {},
...manifest?.kind ? { kind: manifest.kind } : {},
source: plugin.source ?? plugin.manifestPath,
rootDir: plugin.rootDir,
origin: plugin.origin,
enabled: plugin.enabled,
compat: plugin.compat,
syntheticAuthRefs: [...plugin.syntheticAuthRefs ?? manifest?.syntheticAuthRefs ?? []],
status: plugin.enabled ? "loaded" : "disabled",
toolNames: [],
hookNames: [],
channelIds: [...manifest?.channels ?? []],
cliBackendIds: [...manifest?.cliBackends ?? [], ...manifest?.setup?.cliBackends ?? []],
providerIds: [...manifest?.providers ?? []],
embeddingProviderIds: [...manifest?.contracts?.embeddingProviders ?? []],
speechProviderIds: [...manifest?.contracts?.speechProviders ?? []],
realtimeTranscriptionProviderIds: [...manifest?.contracts?.realtimeTranscriptionProviders ?? []],
realtimeVoiceProviderIds: [...manifest?.contracts?.realtimeVoiceProviders ?? []],
mediaUnderstandingProviderIds: [...manifest?.contracts?.mediaUnderstandingProviders ?? []],
transcriptSourceProviderIds: [...manifest?.contracts?.transcriptSourceProviders ?? []],
imageGenerationProviderIds: [...manifest?.contracts?.imageGenerationProviders ?? []],
videoGenerationProviderIds: [...manifest?.contracts?.videoGenerationProviders ?? []],
musicGenerationProviderIds: [...manifest?.contracts?.musicGenerationProviders ?? []],
webFetchProviderIds: [...manifest?.contracts?.webFetchProviders ?? []],
webSearchProviderIds: [...manifest?.contracts?.webSearchProviders ?? []],
migrationProviderIds: [...manifest?.contracts?.migrationProviders ?? []],
memoryEmbeddingProviderIds: [...manifest?.contracts?.memoryEmbeddingProviders ?? []],
agentHarnessIds: [],
cliCommands: [],
services: [],
gatewayDiscoveryServiceIds: [],
commands: [...manifest?.commandAliases?.map((alias) => alias.name) ?? []],
httpRoutes: 0,
hookCount: 0,
configSchema: false,
contracts: manifest?.contracts,
dependencyStatus: buildSnapshotPluginDependencyStatus({
rootDir: plugin.rootDir,
dependencies: manifest?.packageDependencies,
optionalDependencies: manifest?.packageOptionalDependencies
})
};
}
/** Resolves the best available plugin registry snapshot and annotates dependency status. */
function buildPluginRegistrySnapshotReport(params) {
const config = params?.config ?? getRuntimeConfig();
const result = tracePluginLifecyclePhase("plugin registry snapshot", () => loadPluginRegistrySnapshotWithMetadata({
config,
env: params?.env,
workspaceDir: params?.workspaceDir
}), { surface: "status" });
const env = params?.env ?? process.env;
const manifestByPluginId = loadPluginMetadataSnapshot({
index: result.snapshot,
config,
env,
workspaceDir: params?.workspaceDir
}).byPluginId;
return {
workspaceDir: params?.workspaceDir,
...createEmptyPluginRegistry(),
plugins: result.snapshot.plugins.map((plugin) => buildPluginRecordFromInstalledIndex(plugin, manifestByPluginId.get(plugin.pluginId))),
diagnostics: [...result.snapshot.diagnostics],
registrySource: result.source,
registryDiagnostics: result.diagnostics
};
}
//#endregion
export { buildPluginRegistrySnapshotReport as t };