refactor: extract shared install and embedding utilities

This commit is contained in:
Peter Steinberger
2026-02-18 04:48:30 +00:00
parent 4d3403b7ac
commit 8a9fddedc9
7 changed files with 247 additions and 257 deletions

View File

@@ -1,5 +1,4 @@
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { MANIFEST_KEY } from "../compat/legacy-names.js";
import {
@@ -15,8 +14,12 @@ import {
safeDirName,
unscopedPackageName,
} from "../infra/install-safe-path.js";
import {
packNpmSpecToArchive,
resolveArchiveSourcePath,
withTempDir,
} from "../infra/install-source-utils.js";
import { validateRegistryNpmSpec } from "../infra/npm-registry-spec.js";
import { runCommandWithTimeout } from "../process/exec.js";
import { extensionUsesSkippedScannerPath, isPathInside } from "../security/scan-paths.js";
import * as skillScanner from "../security/skill-scanner.js";
import { CONFIG_DIR, resolveUserPath } from "../utils.js";
@@ -298,18 +301,13 @@ export async function installPluginFromArchive(params: {
const logger = params.logger ?? defaultLogger;
const timeoutMs = params.timeoutMs ?? 120_000;
const mode = params.mode ?? "install";
const archivePath = resolveUserPath(params.archivePath);
if (!(await fileExists(archivePath))) {
return { ok: false, error: `archive not found: ${archivePath}` };
const archivePathResult = await resolveArchiveSourcePath(params.archivePath);
if (!archivePathResult.ok) {
return archivePathResult;
}
const archivePath = archivePathResult.path;
if (!resolveArchiveKind(archivePath)) {
return { ok: false, error: `unsupported archive: ${archivePath}` };
}
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-plugin-"));
try {
return await withTempDir("openclaw-plugin-", async (tmpDir) => {
const extractDir = path.join(tmpDir, "extract");
await fs.mkdir(extractDir, { recursive: true });
@@ -341,9 +339,7 @@ export async function installPluginFromArchive(params: {
dryRun: params.dryRun,
expectedPluginId: params.expectedPluginId,
});
} finally {
await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => undefined);
}
});
}
export async function installPluginFromDir(params: {
@@ -433,36 +429,19 @@ export async function installPluginFromNpmSpec(params: {
return { ok: false, error: specError };
}
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-npm-pack-"));
try {
return await withTempDir("openclaw-npm-pack-", async (tmpDir) => {
logger.info?.(`Downloading ${spec}`);
const res = await runCommandWithTimeout(["npm", "pack", spec, "--ignore-scripts"], {
timeoutMs: Math.max(timeoutMs, 300_000),
const packedResult = await packNpmSpecToArchive({
spec,
timeoutMs,
cwd: tmpDir,
env: {
COREPACK_ENABLE_DOWNLOAD_PROMPT: "0",
NPM_CONFIG_IGNORE_SCRIPTS: "true",
},
});
if (res.code !== 0) {
return {
ok: false,
error: `npm pack failed: ${res.stderr.trim() || res.stdout.trim()}`,
};
if (!packedResult.ok) {
return packedResult;
}
const packed = (res.stdout || "")
.split("\n")
.map((l) => l.trim())
.filter(Boolean)
.pop();
if (!packed) {
return { ok: false, error: "npm pack produced no archive" };
}
const archivePath = path.join(tmpDir, packed);
return await installPluginFromArchive({
archivePath,
archivePath: packedResult.archivePath,
extensionsDir: params.extensionsDir,
timeoutMs,
logger,
@@ -470,9 +449,7 @@ export async function installPluginFromNpmSpec(params: {
dryRun,
expectedPluginId,
});
} finally {
await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => undefined);
}
});
}
export async function installPluginFromPath(params: {