mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-26 07:57:40 +00:00
refactor(plugins): centralize before_install context shaping
This commit is contained in:
@@ -27,7 +27,7 @@ function addBeforeInstallHook(
|
||||
}
|
||||
|
||||
const stubCtx: PluginHookBeforeInstallContext = {
|
||||
source: "openclaw-workspace",
|
||||
origin: "openclaw-workspace",
|
||||
targetType: "skill",
|
||||
requestKind: "skill-install",
|
||||
};
|
||||
@@ -37,7 +37,7 @@ const stubEvent: PluginHookBeforeInstallEvent = {
|
||||
targetType: "skill",
|
||||
sourcePath: "/tmp/demo-skill",
|
||||
sourcePathKind: "directory",
|
||||
source: "openclaw-workspace",
|
||||
origin: "openclaw-workspace",
|
||||
request: {
|
||||
kind: "skill-install",
|
||||
mode: "install",
|
||||
|
||||
53
src/plugins/install-policy-context.ts
Normal file
53
src/plugins/install-policy-context.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import type {
|
||||
PluginHookBeforeInstallBuiltinScan,
|
||||
PluginHookBeforeInstallContext,
|
||||
PluginHookBeforeInstallEvent,
|
||||
PluginHookBeforeInstallPlugin,
|
||||
PluginHookBeforeInstallRequest,
|
||||
PluginHookBeforeInstallSkill,
|
||||
PluginInstallSourcePathKind,
|
||||
PluginInstallTargetType,
|
||||
} from "./types.js";
|
||||
|
||||
/**
|
||||
* Centralized builder for the public before_install hook contract.
|
||||
*
|
||||
* Keep all payload shaping here so partner feedback lands in one place instead
|
||||
* of drifting across individual install codepaths.
|
||||
*/
|
||||
export type BeforeInstallHookPayloadParams = {
|
||||
targetType: PluginInstallTargetType;
|
||||
targetName: string;
|
||||
origin?: string;
|
||||
sourcePath: string;
|
||||
sourcePathKind: PluginInstallSourcePathKind;
|
||||
request: PluginHookBeforeInstallRequest;
|
||||
builtinScan: PluginHookBeforeInstallBuiltinScan;
|
||||
skill?: PluginHookBeforeInstallSkill;
|
||||
plugin?: PluginHookBeforeInstallPlugin;
|
||||
};
|
||||
|
||||
export function createBeforeInstallHookPayload(params: BeforeInstallHookPayloadParams): {
|
||||
ctx: PluginHookBeforeInstallContext;
|
||||
event: PluginHookBeforeInstallEvent;
|
||||
} {
|
||||
const event: PluginHookBeforeInstallEvent = {
|
||||
targetType: params.targetType,
|
||||
targetName: params.targetName,
|
||||
sourcePath: params.sourcePath,
|
||||
sourcePathKind: params.sourcePathKind,
|
||||
...(params.origin ? { origin: params.origin } : {}),
|
||||
request: params.request,
|
||||
builtinScan: params.builtinScan,
|
||||
...(params.skill ? { skill: params.skill } : {}),
|
||||
...(params.plugin ? { plugin: params.plugin } : {}),
|
||||
};
|
||||
|
||||
const ctx: PluginHookBeforeInstallContext = {
|
||||
targetType: params.targetType,
|
||||
requestKind: params.request.kind,
|
||||
...(params.origin ? { origin: params.origin } : {}),
|
||||
};
|
||||
|
||||
return { event, ctx };
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import path from "node:path";
|
||||
import { extensionUsesSkippedScannerPath, isPathInside } from "../security/scan-paths.js";
|
||||
import { scanDirectoryWithSummary } from "../security/skill-scanner.js";
|
||||
import { getGlobalHookRunner } from "./hook-runner-global.js";
|
||||
import { createBeforeInstallHookPayload } from "./install-policy-context.js";
|
||||
|
||||
type InstallScanLogger = {
|
||||
warn?: (message: string) => void;
|
||||
@@ -131,7 +132,7 @@ async function scanFileTarget(params: {
|
||||
async function runBeforeInstallHook(params: {
|
||||
logger: InstallScanLogger;
|
||||
installLabel: string;
|
||||
source: string;
|
||||
origin: string;
|
||||
sourcePath: string;
|
||||
sourcePathKind: "file" | "directory";
|
||||
targetName: string;
|
||||
@@ -173,28 +174,22 @@ async function runBeforeInstallHook(params: {
|
||||
}
|
||||
|
||||
try {
|
||||
const hookResult = await hookRunner.runBeforeInstall(
|
||||
{
|
||||
targetName: params.targetName,
|
||||
targetType: params.targetType,
|
||||
source: params.source,
|
||||
sourcePath: params.sourcePath,
|
||||
sourcePathKind: params.sourcePathKind,
|
||||
request: {
|
||||
kind: params.requestKind,
|
||||
mode: params.requestMode,
|
||||
...(params.requestedSpecifier ? { requestedSpecifier: params.requestedSpecifier } : {}),
|
||||
},
|
||||
builtinScan: params.builtinScan,
|
||||
...(params.skill ? { skill: params.skill } : {}),
|
||||
...(params.plugin ? { plugin: params.plugin } : {}),
|
||||
const { event, ctx } = createBeforeInstallHookPayload({
|
||||
targetName: params.targetName,
|
||||
targetType: params.targetType,
|
||||
origin: params.origin,
|
||||
sourcePath: params.sourcePath,
|
||||
sourcePathKind: params.sourcePathKind,
|
||||
request: {
|
||||
kind: params.requestKind,
|
||||
mode: params.requestMode,
|
||||
...(params.requestedSpecifier ? { requestedSpecifier: params.requestedSpecifier } : {}),
|
||||
},
|
||||
{
|
||||
source: params.source,
|
||||
targetType: params.targetType,
|
||||
requestKind: params.requestKind,
|
||||
},
|
||||
);
|
||||
builtinScan: params.builtinScan,
|
||||
...(params.skill ? { skill: params.skill } : {}),
|
||||
...(params.plugin ? { plugin: params.plugin } : {}),
|
||||
});
|
||||
const hookResult = await hookRunner.runBeforeInstall(event, ctx);
|
||||
if (hookResult?.block) {
|
||||
const reason = hookResult.blockReason || "Installation blocked by plugin hook";
|
||||
params.logger.warn?.(`WARNING: ${params.installLabel} blocked by plugin hook: ${reason}`);
|
||||
@@ -237,7 +232,7 @@ export async function scanBundleInstallSourceRuntime(params: {
|
||||
return await runBeforeInstallHook({
|
||||
logger: params.logger,
|
||||
installLabel: `Bundle "${params.pluginId}" installation`,
|
||||
source: "plugin-bundle",
|
||||
origin: "plugin-bundle",
|
||||
sourcePath: params.sourceDir,
|
||||
sourcePathKind: "directory",
|
||||
targetName: params.pluginId,
|
||||
@@ -297,7 +292,7 @@ export async function scanPackageInstallSourceRuntime(params: {
|
||||
return await runBeforeInstallHook({
|
||||
logger: params.logger,
|
||||
installLabel: `Plugin "${params.pluginId}" installation`,
|
||||
source: "plugin-package",
|
||||
origin: "plugin-package",
|
||||
sourcePath: params.packageDir,
|
||||
sourcePathKind: "directory",
|
||||
targetName: params.pluginId,
|
||||
@@ -336,7 +331,7 @@ export async function scanFileInstallSourceRuntime(params: {
|
||||
return await runBeforeInstallHook({
|
||||
logger: params.logger,
|
||||
installLabel: `Plugin file "${params.pluginId}" installation`,
|
||||
source: "plugin-file",
|
||||
origin: "plugin-file",
|
||||
sourcePath: params.filePath,
|
||||
sourcePathKind: "file",
|
||||
targetName: params.pluginId,
|
||||
|
||||
@@ -777,7 +777,7 @@ describe("installPluginFromArchive", () => {
|
||||
expect(handler.mock.calls[0]?.[0]).toMatchObject({
|
||||
targetName: "hook-findings-plugin",
|
||||
targetType: "plugin",
|
||||
source: "plugin-package",
|
||||
origin: "plugin-package",
|
||||
sourcePath: pluginDir,
|
||||
sourcePathKind: "directory",
|
||||
request: {
|
||||
@@ -797,7 +797,7 @@ describe("installPluginFromArchive", () => {
|
||||
},
|
||||
});
|
||||
expect(handler.mock.calls[0]?.[1]).toEqual({
|
||||
source: "plugin-package",
|
||||
origin: "plugin-package",
|
||||
targetType: "plugin",
|
||||
requestKind: "plugin-dir",
|
||||
});
|
||||
@@ -840,7 +840,7 @@ describe("installPluginFromArchive", () => {
|
||||
expect(handler.mock.calls[0]?.[0]).toMatchObject({
|
||||
targetName: "dangerous-blocked-plugin",
|
||||
targetType: "plugin",
|
||||
source: "plugin-package",
|
||||
origin: "plugin-package",
|
||||
request: {
|
||||
kind: "plugin-dir",
|
||||
mode: "install",
|
||||
@@ -1202,7 +1202,7 @@ describe("installPluginFromPath", () => {
|
||||
expect(handler.mock.calls[0]?.[0]).toMatchObject({
|
||||
targetName: "payload",
|
||||
targetType: "plugin",
|
||||
source: "plugin-file",
|
||||
origin: "plugin-file",
|
||||
sourcePath,
|
||||
sourcePathKind: "file",
|
||||
request: {
|
||||
@@ -1220,7 +1220,7 @@ describe("installPluginFromPath", () => {
|
||||
},
|
||||
});
|
||||
expect(handler.mock.calls[0]?.[1]).toEqual({
|
||||
source: "plugin-file",
|
||||
origin: "plugin-file",
|
||||
targetType: "plugin",
|
||||
requestKind: "plugin-file",
|
||||
});
|
||||
|
||||
@@ -2416,8 +2416,8 @@ export type PluginHookBeforeInstallContext = {
|
||||
targetType: PluginInstallTargetType;
|
||||
/** Original install entrypoint/provenance. */
|
||||
requestKind: PluginInstallRequestKind;
|
||||
/** Origin of the install target (e.g. "openclaw-bundled", "plugin-package"). */
|
||||
source?: string;
|
||||
/** Normalized origin of the install target (e.g. "openclaw-bundled", "plugin-package"). */
|
||||
origin?: string;
|
||||
};
|
||||
|
||||
export type PluginHookBeforeInstallEvent = {
|
||||
@@ -2429,8 +2429,8 @@ export type PluginHookBeforeInstallEvent = {
|
||||
sourcePath: string;
|
||||
/** Whether the install target content is a file or directory. */
|
||||
sourcePathKind: PluginInstallSourcePathKind;
|
||||
/** Origin of the install target (e.g. "openclaw-bundled", "plugin-package"). */
|
||||
source?: string;
|
||||
/** Normalized origin of the install target (e.g. "openclaw-bundled", "plugin-package"). */
|
||||
origin?: string;
|
||||
/** Install request provenance and caller mode. */
|
||||
request: PluginHookBeforeInstallRequest;
|
||||
/** Structured result of the built-in scanner. */
|
||||
|
||||
Reference in New Issue
Block a user