mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:50:42 +00:00
feat(plugins): report dependency install state
This commit is contained in:
@@ -6,6 +6,8 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Changes
|
||||
|
||||
- Plugins/CLI: include package dependency install state in `openclaw plugins list --json` so scripts can spot missing plugin dependencies without runtime-loading plugins.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Status: show the `openai-codex` OAuth profile for `openai/gpt-*` sessions running through the native Codex runtime instead of reporting auth as unknown. (#76197) Thanks @mbelinky.
|
||||
|
||||
@@ -237,11 +237,17 @@ openclaw plugins search <query> --json
|
||||
Switch from the table view to per-plugin detail lines with source/origin/version/activation metadata.
|
||||
</ParamField>
|
||||
<ParamField path="--json" type="boolean">
|
||||
Machine-readable inventory plus registry diagnostics.
|
||||
Machine-readable inventory plus registry diagnostics and package dependency install state.
|
||||
</ParamField>
|
||||
|
||||
<Note>
|
||||
`plugins list` reads the persisted local plugin registry first, with a manifest-only derived fallback when the registry is missing or invalid. It is useful for checking whether a plugin is installed, enabled, and visible to cold startup planning, but it is not a live runtime probe of an already-running Gateway process. After changing plugin code, enablement, hook policy, or `plugins.load.paths`, restart the Gateway that serves the channel before expecting new `register(api)` code or hooks to run. For remote/container deployments, verify you are restarting the actual `openclaw gateway run` child, not only a wrapper process.
|
||||
|
||||
`plugins list --json` includes each plugin's `dependencyStatus` from `package.json`
|
||||
`dependencies` and `optionalDependencies`. OpenClaw checks whether those package
|
||||
names are present along the plugin's normal Node `node_modules` lookup path; it
|
||||
does not import plugin runtime code, run a package manager, or repair missing
|
||||
dependencies.
|
||||
</Note>
|
||||
|
||||
`plugins search` is a remote ClawHub catalog lookup. It does not inspect local
|
||||
|
||||
@@ -120,6 +120,8 @@ installed under OpenClaw's managed plugin roots. npm dependencies may be hoisted
|
||||
within OpenClaw's managed npm root; install/update scans that managed root before
|
||||
trust and uninstall removes npm-managed packages through npm. External plugins
|
||||
and custom load paths must still be installed through `openclaw plugins install`.
|
||||
Use `openclaw plugins list --json` to see the static `dependencyStatus` for each
|
||||
visible plugin without importing runtime code or repairing dependencies.
|
||||
See [Plugin dependency resolution](/plugins/dependency-resolution) for the
|
||||
install-time lifecycle.
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ import { formatPosixMode, isPathInside, safeRealpathSync, safeStatSync } from ".
|
||||
import { tracePluginLifecyclePhase } from "./plugin-lifecycle-trace.js";
|
||||
import type { PluginOrigin } from "./plugin-origin.types.js";
|
||||
import { resolvePluginSourceRoots } from "./roots.js";
|
||||
import {
|
||||
normalizePluginDependencySpecs,
|
||||
type PluginDependencySpecMap,
|
||||
} from "./status-dependencies.js";
|
||||
|
||||
const EXTENSION_EXTS = new Set([".ts", ".js", ".mts", ".cts", ".mjs", ".cjs"]);
|
||||
const SCANNED_DIRECTORY_IGNORE_NAMES = new Set([
|
||||
@@ -61,6 +65,8 @@ export type PluginCandidate = {
|
||||
packageDescription?: string;
|
||||
packageDir?: string;
|
||||
packageManifest?: OpenClawPackageManifest;
|
||||
packageDependencies?: PluginDependencySpecMap;
|
||||
packageOptionalDependencies?: PluginDependencySpecMap;
|
||||
bundledManifest?: PluginManifest;
|
||||
bundledManifestPath?: string;
|
||||
};
|
||||
@@ -494,6 +500,10 @@ function addCandidate(params: {
|
||||
}
|
||||
params.seen.add(resolved);
|
||||
const manifest = params.manifest ?? null;
|
||||
const packageDependencies = normalizePluginDependencySpecs({
|
||||
dependencies: manifest?.dependencies,
|
||||
optionalDependencies: manifest?.optionalDependencies,
|
||||
});
|
||||
params.candidates.push({
|
||||
idHint: params.idHint,
|
||||
source: resolved,
|
||||
@@ -508,6 +518,8 @@ function addCandidate(params: {
|
||||
packageDescription: normalizeOptionalString(manifest?.description),
|
||||
packageDir: params.packageDir,
|
||||
packageManifest: getPackageManifestMetadata(manifest ?? undefined),
|
||||
packageDependencies: packageDependencies.dependencies,
|
||||
packageOptionalDependencies: packageDependencies.optionalDependencies,
|
||||
bundledManifest: params.bundledManifest,
|
||||
bundledManifestPath: params.bundledManifestPath,
|
||||
});
|
||||
@@ -518,6 +530,7 @@ function discoverBundleInRoot(params: {
|
||||
origin: PluginOrigin;
|
||||
ownershipUid?: number | null;
|
||||
workspaceDir?: string;
|
||||
manifest?: PackageManifest | null;
|
||||
candidates: PluginCandidate[];
|
||||
diagnostics: PluginDiagnostic[];
|
||||
seen: Set<string>;
|
||||
@@ -554,6 +567,8 @@ function discoverBundleInRoot(params: {
|
||||
bundleFormat,
|
||||
ownershipUid: params.ownershipUid,
|
||||
workspaceDir: params.workspaceDir,
|
||||
manifest: params.manifest,
|
||||
packageDir: params.rootDir,
|
||||
realpathCache: params.realpathCache,
|
||||
});
|
||||
return "added";
|
||||
@@ -683,6 +698,7 @@ function discoverInDirectory(params: {
|
||||
origin: params.origin,
|
||||
ownershipUid: params.ownershipUid,
|
||||
workspaceDir: params.workspaceDir,
|
||||
manifest,
|
||||
candidates: params.candidates,
|
||||
diagnostics: params.diagnostics,
|
||||
seen: params.seen,
|
||||
@@ -882,6 +898,7 @@ function discoverFromPath(params: {
|
||||
origin: params.origin,
|
||||
ownershipUid: params.ownershipUid,
|
||||
workspaceDir: params.workspaceDir,
|
||||
manifest,
|
||||
candidates: params.candidates,
|
||||
diagnostics: params.diagnostics,
|
||||
seen: params.seen,
|
||||
|
||||
@@ -14,6 +14,10 @@ import {
|
||||
type PackageManifest,
|
||||
} from "./manifest.js";
|
||||
import { tracePluginLifecyclePhase } from "./plugin-lifecycle-trace.js";
|
||||
import {
|
||||
normalizePluginDependencySpecs,
|
||||
type PluginDependencySpecMap,
|
||||
} from "./status-dependencies.js";
|
||||
|
||||
function resolvePackageJsonPath(record: InstalledPluginIndexRecord): string | undefined {
|
||||
if (!record.packageJson?.path) {
|
||||
@@ -101,9 +105,11 @@ function resolveFallbackPluginSource(record: InstalledPluginIndexRecord): string
|
||||
return path.join(rootDir, DEFAULT_PLUGIN_ENTRY_CANDIDATES[0]);
|
||||
}
|
||||
|
||||
function resolveInstalledPackageManifest(
|
||||
record: InstalledPluginIndexRecord,
|
||||
): OpenClawPackageManifest | undefined {
|
||||
function resolveInstalledPackageMetadata(record: InstalledPluginIndexRecord): {
|
||||
packageManifest?: OpenClawPackageManifest;
|
||||
packageDependencies?: PluginDependencySpecMap;
|
||||
packageOptionalDependencies?: PluginDependencySpecMap;
|
||||
} {
|
||||
const fallbackPackageManifest = record.packageChannel
|
||||
? {
|
||||
channel: record.packageChannel,
|
||||
@@ -114,17 +120,25 @@ function resolveInstalledPackageManifest(
|
||||
? path.resolve(rootDir, record.packageJson.path)
|
||||
: undefined;
|
||||
if (!packageJsonPath) {
|
||||
return fallbackPackageManifest;
|
||||
return fallbackPackageManifest ? { packageManifest: fallbackPackageManifest } : {};
|
||||
}
|
||||
const relative = path.relative(rootDir, packageJsonPath);
|
||||
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
||||
return fallbackPackageManifest;
|
||||
return fallbackPackageManifest ? { packageManifest: fallbackPackageManifest } : {};
|
||||
}
|
||||
try {
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")) as PackageManifest;
|
||||
const packageManifest = getPackageManifestMetadata(packageJson);
|
||||
const dependencies = normalizePluginDependencySpecs({
|
||||
dependencies: packageJson.dependencies,
|
||||
optionalDependencies: packageJson.optionalDependencies,
|
||||
});
|
||||
if (!packageManifest) {
|
||||
return fallbackPackageManifest;
|
||||
return {
|
||||
...(fallbackPackageManifest ? { packageManifest: fallbackPackageManifest } : {}),
|
||||
packageDependencies: dependencies.dependencies,
|
||||
packageOptionalDependencies: dependencies.optionalDependencies,
|
||||
};
|
||||
}
|
||||
const channel =
|
||||
record.packageChannel || packageManifest.channel
|
||||
@@ -134,17 +148,21 @@ function resolveInstalledPackageManifest(
|
||||
}
|
||||
: undefined;
|
||||
return {
|
||||
...packageManifest,
|
||||
...(channel ? { channel } : {}),
|
||||
packageManifest: {
|
||||
...packageManifest,
|
||||
...(channel ? { channel } : {}),
|
||||
},
|
||||
packageDependencies: dependencies.dependencies,
|
||||
packageOptionalDependencies: dependencies.optionalDependencies,
|
||||
};
|
||||
} catch {
|
||||
return fallbackPackageManifest;
|
||||
return fallbackPackageManifest ? { packageManifest: fallbackPackageManifest } : {};
|
||||
}
|
||||
}
|
||||
|
||||
function toPluginCandidate(record: InstalledPluginIndexRecord): PluginCandidate {
|
||||
const rootDir = resolveInstalledPluginRootDir(record);
|
||||
const packageManifest = resolveInstalledPackageManifest(record);
|
||||
const packageMetadata = resolveInstalledPackageMetadata(record);
|
||||
return {
|
||||
idHint: record.pluginId,
|
||||
source: record.source ?? resolveFallbackPluginSource(record),
|
||||
@@ -155,7 +173,15 @@ function toPluginCandidate(record: InstalledPluginIndexRecord): PluginCandidate
|
||||
...(record.bundleFormat ? { bundleFormat: record.bundleFormat } : {}),
|
||||
...(record.packageName ? { packageName: record.packageName } : {}),
|
||||
...(record.packageVersion ? { packageVersion: record.packageVersion } : {}),
|
||||
...(packageManifest ? { packageManifest } : {}),
|
||||
...(packageMetadata.packageManifest
|
||||
? { packageManifest: packageMetadata.packageManifest }
|
||||
: {}),
|
||||
...(packageMetadata.packageDependencies
|
||||
? { packageDependencies: packageMetadata.packageDependencies }
|
||||
: {}),
|
||||
...(packageMetadata.packageOptionalDependencies
|
||||
? { packageOptionalDependencies: packageMetadata.packageOptionalDependencies }
|
||||
: {}),
|
||||
packageDir: rootDir,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ import { checkMinHostVersion } from "./min-host-version.js";
|
||||
import { isPathInside, safeRealpathSync } from "./path-safety.js";
|
||||
import type { PluginKind } from "./plugin-kind.types.js";
|
||||
import type { PluginOrigin } from "./plugin-origin.types.js";
|
||||
import type { PluginDependencySpecMap } from "./status-dependencies.js";
|
||||
|
||||
/**
|
||||
* Resolve a plugin source path, falling back from .ts to .js when the
|
||||
@@ -130,6 +131,8 @@ export type PluginManifestRecord = {
|
||||
activation?: PluginManifestActivation;
|
||||
setup?: PluginManifestSetup;
|
||||
packageManifest?: OpenClawPackageManifest;
|
||||
packageDependencies?: PluginDependencySpecMap;
|
||||
packageOptionalDependencies?: PluginDependencySpecMap;
|
||||
packageChannel?: PluginPackageChannel;
|
||||
packageInstall?: PluginPackageInstall;
|
||||
qaRunners?: PluginManifestQaRunner[];
|
||||
@@ -316,6 +319,8 @@ function buildRecord(params: {
|
||||
activation: params.manifest.activation,
|
||||
setup: params.manifest.setup,
|
||||
packageManifest: params.candidate.packageManifest,
|
||||
packageDependencies: params.candidate.packageDependencies,
|
||||
packageOptionalDependencies: params.candidate.packageOptionalDependencies,
|
||||
packageChannel: params.candidate.packageManifest?.channel,
|
||||
packageInstall: params.candidate.packageManifest?.install,
|
||||
qaRunners: params.manifest.qaRunners,
|
||||
@@ -385,6 +390,8 @@ function buildBundleRecord(params: {
|
||||
packageVersion: params.candidate.packageVersion,
|
||||
packageDescription: params.candidate.packageDescription,
|
||||
packageManifest: params.candidate.packageManifest,
|
||||
packageDependencies: params.candidate.packageDependencies,
|
||||
packageOptionalDependencies: params.candidate.packageOptionalDependencies,
|
||||
packageChannel: params.candidate.packageManifest?.channel,
|
||||
packageInstall: params.candidate.packageManifest?.install,
|
||||
format: "bundle",
|
||||
|
||||
@@ -1737,6 +1737,8 @@ export type PackageManifest = {
|
||||
name?: string;
|
||||
version?: string;
|
||||
description?: string;
|
||||
dependencies?: Record<string, string>;
|
||||
optionalDependencies?: Record<string, string>;
|
||||
} & Partial<Record<ManifestKey, OpenClawPackageManifest>>;
|
||||
|
||||
export function getPackageManifestMetadata(
|
||||
|
||||
@@ -30,6 +30,7 @@ import type { PluginManifestContracts } from "./manifest.js";
|
||||
import type { MemoryEmbeddingProviderAdapter } from "./memory-embedding-providers.js";
|
||||
import type { PluginKind } from "./plugin-kind.types.js";
|
||||
import type { PluginRuntime } from "./runtime/types.js";
|
||||
import type { PluginDependencyStatus } from "./status-dependencies.js";
|
||||
import type {
|
||||
CliBackendPlugin,
|
||||
ImageGenerationProviderPlugin,
|
||||
@@ -377,6 +378,7 @@ export type PluginRecord = {
|
||||
configJsonSchema?: JsonSchemaObject;
|
||||
contracts?: PluginManifestContracts;
|
||||
memorySlotSelected?: boolean;
|
||||
dependencyStatus?: PluginDependencyStatus;
|
||||
};
|
||||
|
||||
export type PluginRegistry = {
|
||||
|
||||
135
src/plugins/status-dependencies.ts
Normal file
135
src/plugins/status-dependencies.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
export type PluginDependencySpecMap = Record<string, string>;
|
||||
|
||||
export type PluginDependencyEntry = {
|
||||
name: string;
|
||||
spec: string;
|
||||
installed: boolean;
|
||||
optional: boolean;
|
||||
resolvedPath?: string;
|
||||
};
|
||||
|
||||
export type PluginDependencyStatus = {
|
||||
hasDependencies: boolean;
|
||||
installed: boolean;
|
||||
requiredInstalled: boolean;
|
||||
optionalInstalled: boolean;
|
||||
missing: string[];
|
||||
missingOptional: string[];
|
||||
dependencies: PluginDependencyEntry[];
|
||||
optionalDependencies: PluginDependencyEntry[];
|
||||
};
|
||||
|
||||
function normalizeDependencyMap(raw: unknown): PluginDependencySpecMap {
|
||||
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
||||
return {};
|
||||
}
|
||||
const normalized: PluginDependencySpecMap = {};
|
||||
for (const [name, spec] of Object.entries(raw)) {
|
||||
const normalizedName = name.trim();
|
||||
if (!normalizedName || typeof spec !== "string" || !spec.trim()) {
|
||||
continue;
|
||||
}
|
||||
normalized[normalizedName] = spec.trim();
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export function normalizePluginDependencySpecs(params: {
|
||||
dependencies?: unknown;
|
||||
optionalDependencies?: unknown;
|
||||
}): {
|
||||
dependencies: PluginDependencySpecMap;
|
||||
optionalDependencies: PluginDependencySpecMap;
|
||||
} {
|
||||
return {
|
||||
dependencies: normalizeDependencyMap(params.dependencies),
|
||||
optionalDependencies: normalizeDependencyMap(params.optionalDependencies),
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
return {
|
||||
name,
|
||||
spec,
|
||||
installed: resolvedPath !== undefined,
|
||||
optional: params.optional,
|
||||
...(resolvedPath ? { resolvedPath } : {}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function buildPluginDependencyStatus(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,
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { refreshPluginRegistry } from "./plugin-registry.js";
|
||||
import { buildPluginRegistrySnapshotReport, buildPluginSnapshotReport } from "./status.js";
|
||||
@@ -73,6 +74,69 @@ describe("buildPluginRegistrySnapshotReport", () => {
|
||||
expect(isColdPluginRuntimeLoaded(fixture)).toBe(false);
|
||||
});
|
||||
|
||||
it("reports package dependency install state without importing plugin runtime", () => {
|
||||
const rootDir = makeTempDir();
|
||||
const fixture = createColdPluginFixture({
|
||||
rootDir,
|
||||
pluginId: "dependency-demo",
|
||||
packageJson: {
|
||||
dependencies: {
|
||||
"missing-required": "1.0.0",
|
||||
"present-required": "1.0.0",
|
||||
},
|
||||
optionalDependencies: {
|
||||
"missing-optional": "1.0.0",
|
||||
},
|
||||
},
|
||||
manifest: {
|
||||
id: "dependency-demo",
|
||||
name: "Dependency Demo",
|
||||
},
|
||||
});
|
||||
fs.mkdirSync(path.join(rootDir, "node_modules", "present-required"), { recursive: true });
|
||||
|
||||
const report = buildPluginRegistrySnapshotReport({
|
||||
config: {
|
||||
plugins: {
|
||||
load: { paths: [fixture.rootDir] },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const plugin = report.plugins.find((entry) => entry.id === "dependency-demo");
|
||||
expect(plugin?.dependencyStatus).toMatchObject({
|
||||
hasDependencies: true,
|
||||
installed: false,
|
||||
requiredInstalled: false,
|
||||
optionalInstalled: false,
|
||||
missing: ["missing-required"],
|
||||
missingOptional: ["missing-optional"],
|
||||
dependencies: [
|
||||
{
|
||||
name: "missing-required",
|
||||
spec: "1.0.0",
|
||||
installed: false,
|
||||
optional: false,
|
||||
},
|
||||
{
|
||||
name: "present-required",
|
||||
spec: "1.0.0",
|
||||
installed: true,
|
||||
optional: false,
|
||||
},
|
||||
],
|
||||
optionalDependencies: [
|
||||
{
|
||||
name: "missing-optional",
|
||||
spec: "1.0.0",
|
||||
installed: false,
|
||||
optional: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(isColdPluginRuntimeLoaded(fixture)).toBe(false);
|
||||
});
|
||||
|
||||
it("replays persisted list metadata without importing plugin runtime", async () => {
|
||||
const fixture = createColdPluginFixture({
|
||||
rootDir: makeTempDir(),
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
resolvePluginRuntimeLoadContext,
|
||||
} from "./runtime/load-context.js";
|
||||
import { loadPluginMetadataRegistrySnapshot } from "./runtime/metadata-registry-loader.js";
|
||||
import { buildPluginDependencyStatus } from "./status-dependencies.js";
|
||||
import type { PluginHookName, PluginLogger } from "./types.js";
|
||||
|
||||
export type PluginStatusReport = PluginRegistry & {
|
||||
@@ -212,6 +213,11 @@ function buildPluginRecordFromInstalledIndex(
|
||||
hookCount: 0,
|
||||
configSchema: false,
|
||||
contracts: {},
|
||||
dependencyStatus: buildPluginDependencyStatus({
|
||||
rootDir: plugin.rootDir,
|
||||
dependencies: manifest?.packageDependencies,
|
||||
optionalDependencies: manifest?.packageOptionalDependencies,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -363,6 +369,14 @@ function buildPluginReport(
|
||||
Object.assign({}, plugin, {
|
||||
imported: plugin.format !== `bundle` && importedPluginIds.has(plugin.id),
|
||||
version: resolveReportedPluginVersion(plugin, params?.env),
|
||||
dependencyStatus:
|
||||
plugin.dependencyStatus ??
|
||||
buildPluginDependencyStatus({
|
||||
rootDir: plugin.rootDir,
|
||||
dependencies: metadataSnapshot?.byPluginId.get(plugin.id)?.packageDependencies,
|
||||
optionalDependencies: metadataSnapshot?.byPluginId.get(plugin.id)
|
||||
?.packageOptionalDependencies,
|
||||
}),
|
||||
}),
|
||||
),
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@ type ColdPluginFixtureOptions = {
|
||||
pluginId?: string;
|
||||
packageName?: string;
|
||||
packageVersion?: string;
|
||||
packageJson?: Record<string, unknown>;
|
||||
providerId?: string;
|
||||
channelId?: string;
|
||||
authChoiceId?: string;
|
||||
@@ -37,6 +38,7 @@ export function createColdPluginFixture(options: ColdPluginFixtureOptions): Cold
|
||||
{
|
||||
name: options.packageName ?? "@example/openclaw-cold-control-plane",
|
||||
version: options.packageVersion ?? "1.0.0",
|
||||
...options.packageJson,
|
||||
openclaw: { extensions: ["./index.cjs"] },
|
||||
},
|
||||
null,
|
||||
|
||||
Reference in New Issue
Block a user