Install ClawHub packages for new Claw agents (#102228)

* 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>
This commit is contained in:
Gio Della-Libera
2026-07-21 18:55:10 -07:00
committed by GitHub
parent bc43b3eda5
commit ba467fbd3e
40 changed files with 3139 additions and 199 deletions
+66 -28
View File
@@ -45,6 +45,8 @@ import {
import { resolveCatalogOfficialExternalInstallPlan } from "../plugins/official-external-install-trust.js";
import { tracePluginLifecyclePhaseAsync } from "../plugins/plugin-lifecycle-trace.js";
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
import { markClawPackageIndependentlyOwned } from "../state/claw-package-adoption.js";
import { withClawPackageLifecycleLease } from "../state/claw-package-lifecycle-lease.js";
import { resolveUserPath, shortenHomePath } from "../utils.js";
import { resolveClawHubRiskAcknowledgementCliOptions } from "./clawhub-risk-acknowledgement.js";
import { formatCliCommand } from "./command-format.js";
@@ -759,12 +761,15 @@ export async function runPluginInstallCommand(params: {
raw: string;
opts: InstallSafetyOverrides & {
acknowledgeClawHubRisk?: boolean;
expectedIntegrity?: string;
expectedPluginId?: string;
force?: boolean;
link?: boolean;
pin?: boolean;
marketplace?: string;
};
invalidateRuntimeCache?: boolean;
clawManaged?: boolean;
runtime?: RuntimeEnv;
}) {
assertConfigWriteAllowedInCurrentMode();
@@ -1215,36 +1220,69 @@ export async function runPluginInstallCommand(params: {
}
if (clawhubSpec) {
const result = await installPluginFromClawHub({
...safetyOverrides,
...resolveClawHubRiskAcknowledgementCliOptions({
acknowledgeClawHubRisk: opts.acknowledgeClawHubRisk,
action: "installing",
}),
mode: installMode,
spec: raw,
extensionsDir,
logger: createPluginInstallLogger(runtime),
});
if (!result.ok) {
if (!isClawHubBlockedCliFailure(result)) {
runtime.error(result.error);
}
return runtime.exit(1);
}
await persistPluginInstall({
snapshot,
pluginId: result.pluginId,
install: {
...buildClawHubPluginInstallRecordFields(result.clawhub),
const installFromClawHub = async (
installSnapshot = snapshot,
installSafetyOverrides = safetyOverrides,
) => {
const result = await installPluginFromClawHub({
...installSafetyOverrides,
...resolveClawHubRiskAcknowledgementCliOptions({
acknowledgeClawHubRisk: opts.acknowledgeClawHubRisk,
action: "installing",
}),
mode: installMode,
spec: raw,
installPath: result.targetDir,
...(opts.expectedIntegrity ? { expectedIntegrity: opts.expectedIntegrity } : {}),
...(opts.expectedPluginId ? { expectedPluginId: opts.expectedPluginId } : {}),
extensionsDir,
logger: createPluginInstallLogger(runtime),
});
if (!result.ok) {
if (!isClawHubBlockedCliFailure(result)) {
runtime.error(result.error);
}
return runtime.exit(1);
}
await persistPluginInstall({
snapshot: installSnapshot,
pluginId: result.pluginId,
install: {
...buildClawHubPluginInstallRecordFields(result.clawhub),
spec: raw,
installPath: result.targetDir,
},
invalidateRuntimeCache,
runtime,
});
if (!params.clawManaged && result.clawhub.version) {
markClawPackageIndependentlyOwned({
kind: "plugin",
source: "clawhub",
ref: result.clawhub.clawhubPackage,
version: result.clawhub.version,
});
}
};
if (params.clawManaged) {
return await installFromClawHub();
}
return await withClawPackageLifecycleLease(
{ kind: "plugin", source: "clawhub", ref: clawhubSpec.name },
async () => {
const leasedSnapshot = await loadConfigForInstall(request).catch((error: unknown) => {
runtime.error(formatErrorMessage(error));
return null;
});
if (!leasedSnapshot) {
return runtime.exit(1);
}
return await installFromClawHub(
leasedSnapshot,
resolveInstallSafetyOverrides({ ...opts, config: leasedSnapshot.config }),
);
},
invalidateRuntimeCache,
runtime,
});
return;
);
}
const trustedNpmInstall = resolveOpenClawTrustedNpmPackageInstall(raw);