mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-02 15:21:34 +00:00
* Install ClawHub packages for new Claw agents * test(claws): cover skill and plugin installation * docs(claws): document package artifacts * fix(claws): bind package trust preflight * fix(claws): bind package capability consent * fix(claws): satisfy package type guards * test(claws): update package installer fixtures * test(claws): complete package plan fixtures * test(claws): model complete ClawHub results * fix(claws): serialize plugin ownership changes * fix(claws): cover legacy plugin lifecycle updates * fix(claws): preserve legacy ClawHub update selectors * test(claws): type lifecycle lease mock * refactor(claws): isolate plugin update lifecycle * fix(claws): declare managed installer coordination * test(claws): preserve partial collision result * test(claws): clean installer temp dirs --------- Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
117 lines
3.8 KiB
TypeScript
117 lines
3.8 KiB
TypeScript
import {
|
|
resolvePluginInstallRequestContext,
|
|
type PluginInstallRequestContext,
|
|
} from "../cli/plugin-install-config-policy.js";
|
|
import type { PluginInstallRecord } from "../config/types.plugins.js";
|
|
import { parseClawHubPluginSpec } from "../infra/clawhub-spec.js";
|
|
import { loadInstalledPluginIndexInstallRecords } from "./installed-plugin-index-records.js";
|
|
|
|
type PluginInstallPreflightResult =
|
|
| { ok: true; action: "install"; request: PluginInstallRequestContext }
|
|
| {
|
|
ok: true;
|
|
action: "reuse";
|
|
request: PluginInstallRequestContext;
|
|
installedId: string;
|
|
installedVersion: string;
|
|
installedIntegrity?: string;
|
|
installedAt?: string;
|
|
}
|
|
| {
|
|
ok: false;
|
|
code: "plugin_version_conflict";
|
|
request: PluginInstallRequestContext;
|
|
installedVersion: string;
|
|
expectedVersion: string;
|
|
}
|
|
| { ok: false; code: "invalid_plugin_spec"; error: string };
|
|
|
|
type InstalledClawHubPluginResolution =
|
|
| { status: "missing" }
|
|
| { status: "ambiguous"; pluginIds: string[] }
|
|
| {
|
|
status: "found";
|
|
pluginId: string;
|
|
record: PluginInstallRecord;
|
|
installedVersion?: string;
|
|
};
|
|
|
|
/** Resolves one installed plugin by its stable ClawHub package identity. */
|
|
export async function resolveInstalledClawHubPlugin(params: {
|
|
clawhubPackage: string;
|
|
loadInstallRecords?: typeof loadInstalledPluginIndexInstallRecords;
|
|
}): Promise<InstalledClawHubPluginResolution> {
|
|
const records = await (params.loadInstallRecords ?? loadInstalledPluginIndexInstallRecords)();
|
|
const matches = Object.entries(records).filter(
|
|
([, record]) =>
|
|
(record.clawhubPackage ??
|
|
parseClawHubPluginSpec(record.spec ?? "")?.name ??
|
|
parseClawHubPluginSpec(record.resolvedSpec ?? "")?.name) === params.clawhubPackage,
|
|
);
|
|
if (matches.length === 0) {
|
|
return { status: "missing" };
|
|
}
|
|
if (matches.length > 1) {
|
|
return { status: "ambiguous", pluginIds: matches.map(([pluginId]) => pluginId).toSorted() };
|
|
}
|
|
const match = matches[0];
|
|
if (!match) {
|
|
return { status: "missing" };
|
|
}
|
|
const [pluginId, record] = match;
|
|
return {
|
|
status: "found",
|
|
pluginId,
|
|
record,
|
|
installedVersion: record.resolvedVersion ?? record.version,
|
|
};
|
|
}
|
|
|
|
export async function preflightPluginInstall(params: {
|
|
clawhubPackage: string;
|
|
rawSpec: string;
|
|
expectedVersion: string;
|
|
marketplace?: string;
|
|
loadInstallRecords?: typeof loadInstalledPluginIndexInstallRecords;
|
|
}): Promise<PluginInstallPreflightResult> {
|
|
const resolved = resolvePluginInstallRequestContext({
|
|
rawSpec: params.rawSpec,
|
|
...(params.marketplace ? { marketplace: params.marketplace } : {}),
|
|
installKind: "plugin",
|
|
});
|
|
if (!resolved.ok) {
|
|
return { ok: false, code: "invalid_plugin_spec", error: resolved.error };
|
|
}
|
|
|
|
const records = await (params.loadInstallRecords ?? loadInstalledPluginIndexInstallRecords)();
|
|
const installedEntry = Object.entries(records).find(
|
|
([, record]) =>
|
|
(record.clawhubPackage ?? parseClawHubPluginSpec(record.spec ?? "")?.name) ===
|
|
params.clawhubPackage,
|
|
);
|
|
const installedId = installedEntry?.[0];
|
|
const installed = installedEntry?.[1];
|
|
const installedVersion = installed?.resolvedVersion ?? installed?.version;
|
|
if (!installedVersion || !installedId) {
|
|
return { ok: true, action: "install", request: resolved.request };
|
|
}
|
|
if (installedVersion === params.expectedVersion) {
|
|
return {
|
|
ok: true,
|
|
action: "reuse",
|
|
request: resolved.request,
|
|
installedId,
|
|
installedVersion,
|
|
...(installed?.integrity ? { installedIntegrity: installed.integrity } : {}),
|
|
...(installed?.installedAt ? { installedAt: installed.installedAt } : {}),
|
|
};
|
|
}
|
|
return {
|
|
ok: false,
|
|
code: "plugin_version_conflict",
|
|
request: resolved.request,
|
|
installedVersion,
|
|
expectedVersion: params.expectedVersion,
|
|
};
|
|
}
|