From 2e7caba5572abfb58188e89558f2ab939696550d Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Tue, 16 Jun 2026 13:56:59 +0800 Subject: [PATCH] refactor(plugins): reuse dependency status core --- src/plugins/status-snapshot-dependencies.ts | 99 --------------------- src/plugins/status-snapshot.ts | 4 +- 2 files changed, 2 insertions(+), 101 deletions(-) delete mode 100644 src/plugins/status-snapshot-dependencies.ts diff --git a/src/plugins/status-snapshot-dependencies.ts b/src/plugins/status-snapshot-dependencies.ts deleted file mode 100644 index bff76d9e518b..000000000000 --- a/src/plugins/status-snapshot-dependencies.ts +++ /dev/null @@ -1,99 +0,0 @@ -/** Builds dependency install status for plugin status snapshots without importing discovery. */ -import fs from "node:fs"; -import path from "node:path"; -import type { - PluginDependencyEntry, - PluginDependencySpecMap, - PluginDependencyStatus, -} from "./status-dependencies.js"; - -// Keep this helper separate from status-dependencies.ts. That module is also -// imported by plugin discovery, and the CLI startup bundle otherwise pulls the -// full discovery chunk for `plugins list --json`. -function dependencyPathSegments(name: string): string[] | null { - 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: { fromDir: string; name: string }): string | undefined { - const segments = dependencyPathSegments(params.name); - if (!segments) { - return undefined; - } - 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 undefined; - } - current = parent; - } -} - -function buildDependencyEntries(params: { - rootDir: string | undefined; - dependencies: PluginDependencySpecMap; - optional: boolean; -}): PluginDependencyEntry[] { - return Object.entries(params.dependencies) - .toSorted(([left], [right]) => left.localeCompare(right)) - .map(([name, spec]) => { - const resolvedPath = params.rootDir - ? findDependencyPackageDir({ fromDir: params.rootDir, name }) - : undefined; - const entry: PluginDependencyEntry = { - name, - spec, - installed: resolvedPath !== undefined, - optional: params.optional, - }; - if (resolvedPath) { - entry.resolvedPath = resolvedPath; - } - return entry; - }); -} - -/** Resolves required and optional dependency status for one plugin root. */ -export function buildSnapshotPluginDependencyStatus(params: { - rootDir?: string; - dependencies?: PluginDependencySpecMap; - optionalDependencies?: PluginDependencySpecMap; -}): PluginDependencyStatus { - 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, - }; -} diff --git a/src/plugins/status-snapshot.ts b/src/plugins/status-snapshot.ts index 962b4b67b105..e420e2d69f5b 100644 --- a/src/plugins/status-snapshot.ts +++ b/src/plugins/status-snapshot.ts @@ -8,7 +8,7 @@ import { type PluginRegistrySnapshotSource, } from "./plugin-registry.js"; import { createEmptyPluginRegistry, type PluginRecord, type PluginRegistry } from "./registry.js"; -import { buildSnapshotPluginDependencyStatus } from "./status-snapshot-dependencies.js"; +import { buildPluginDependencyStatus } from "./status-dependencies-core.js"; import type { PluginLogger } from "./types.js"; /** Control-plane plugin status shape used by `openclaw plugins status` style surfaces. */ @@ -118,7 +118,7 @@ function buildPluginRecordFromInstalledIndex( hookCount: 0, configSchema: false, contracts: manifest?.contracts, - dependencyStatus: buildSnapshotPluginDependencyStatus({ + dependencyStatus: buildPluginDependencyStatus({ rootDir: plugin.rootDir, dependencies: manifest?.packageDependencies, optionalDependencies: manifest?.packageOptionalDependencies,