UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

122 lines (121 loc) 5.2 kB
import { f as withPluginCache, i as createPluginCache } from "./plugin-cache-DGWspMEc.js"; import { c as isRecord } from "./record-coerce-DItp3I4t.js"; import { r as defaultRuntime } from "./runtime-CF2WjnNZ.js"; import { w as root } from "./fs-safe-B6pvPGnf.js"; import { r as loadPluginManifest, s as resolvePackageExtensionEntries } from "./manifest-ByRdkf9X.js"; import { r as readPluginControlUiAssets } from "./control-ui-assets-CtXpBttK.js"; import { l as buildPluginBundle, r as collectPluginsValidationResult } from "./plugins-authoring-command-B5ijz98k.js"; import { createRequire } from "node:module"; import path from "node:path"; import fs from "node:fs/promises"; import os from "node:os"; import { createHash } from "node:crypto"; import { create } from "tar"; //#region src/cli/plugins-feature-artifact.ts /** Produce one reviewable import: bundled code, immutable UI assets, no install scripts. */ async function packFeaturePlugin(opts) { const rootDir = await fs.realpath(path.resolve(opts.root ?? process.cwd())); const validation = await collectPluginsValidationResult({ root: rootDir }); if (!validation.valid) throw new Error(validation.errors.join("\n")); const source = await root(rootDir, { symlinks: "reject", hardlinks: "reject" }); const packageManifest = await source.readJson("package.json"); if (!isRecord(packageManifest) || !isRecord(packageManifest.openclaw)) throw new Error("Plugin package metadata is missing. Run openclaw plugins build."); const extensions = resolvePackageExtensionEntries(packageManifest); if (extensions.status !== "ok" || extensions.entries.length !== 1) throw new Error("Plugin artifacts require exactly one backend entrypoint."); const entry = extensions.entries[0]; const loaded = withPluginCache(createPluginCache(), () => loadPluginManifest(rootDir, false)); if (!loaded.ok) throw new Error(loaded.error); const builder = createRequire(path.join(rootDir, "package.json"))("esbuild"); const files = await buildPluginBundle(builder, { absWorkingDir: rootDir, entryPoints: [entry], outfile: "dist/index.js", platform: "node", target: "node22", external: ["openclaw", "openclaw/*"] }); const pluginId = loaded.manifest.id; const outputPath = path.resolve(opts.out ?? path.join(rootDir, `${pluginId}.tgz`)); if (!/\.(?:tgz|tar\.gz)$/u.test(outputPath)) throw new Error("Plugin artifact output must end in .tgz or .tar.gz."); const staging = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-plugin-pack-")); try { await fs.mkdir(path.join(staging, "package")); const destination = await root(path.join(staging, "package"), { mkdir: true, symlinks: "reject", hardlinks: "reject" }); await destination.ensureRoot(); const { controlUi: _source, ...openclaw } = packageManifest.openclaw; const packedPackage = { name: packageManifest.name, version: packageManifest.version, type: "module", ...typeof packageManifest.description === "string" ? { description: packageManifest.description } : {}, ...typeof packageManifest.license === "string" ? { license: packageManifest.license } : {}, ...isRecord(packageManifest.peerDependencies) && typeof packageManifest.peerDependencies.openclaw === "string" ? { peerDependencies: { openclaw: packageManifest.peerDependencies.openclaw } } : {}, openclaw: { ...openclaw, extensions: ["./dist/index.js"] } }; await destination.create("package.json", `${JSON.stringify(packedPackage, null, 2)}\n`); await destination.create("openclaw.plugin.json", await source.readBytes("openclaw.plugin.json")); await destination.mkdir("dist"); await destination.create("dist/index.js", Buffer.from(files[0].contents)); const controlUi = loaded.manifest.controlUi; if (controlUi) { const { directory, assets } = await readPluginControlUiAssets(rootDir, controlUi); for (const [name, asset] of assets) { const relativePath = path.posix.join(directory, name); await destination.mkdir(path.posix.dirname(relativePath)); await destination.create(relativePath, asset.body); } } const archive = path.join(staging, "plugin.tgz"); await create({ cwd: staging, file: archive, gzip: true, portable: true, noMtime: true, strict: true }, ["package"]); const bytes = await fs.readFile(archive); if (bytes.length > 33554432) throw new Error("Plugin artifacts must be at most 32 MiB."); await fs.writeFile(outputPath, bytes, { flag: "wx", mode: 384 }); const sha256 = createHash("sha256").update(bytes).digest("hex"); return { path: outputPath, sha256, pluginId, bytes: bytes.length, activation: { action: "plugin_activate_artifact", path: outputPath, sha256 } }; } finally { await fs.rm(staging, { recursive: true, force: true }); } } async function runPluginsPackCommand(opts) { const receipt = await packFeaturePlugin(opts); if (opts.json) { defaultRuntime.writeJson(receipt); return; } defaultRuntime.log(`Packed ${receipt.pluginId}: ${receipt.path}\nSHA256: ${receipt.sha256}\nUse plugin_activate_artifact with this path and digest to request activation approval.`); } //#endregion export { runPluginsPackCommand };