mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:10:43 +00:00
fix(plugins): build package-local npm runtimes
This commit is contained in:
@@ -31,7 +31,7 @@ function isManifestlessBundledRuntimeSupportPackage(params) {
|
||||
return params.topLevelPublicSurfaceEntries.length > 0;
|
||||
}
|
||||
|
||||
function collectPluginSourceEntries(packageJson) {
|
||||
export function collectPluginSourceEntries(packageJson) {
|
||||
let packageEntries = Array.isArray(packageJson?.openclaw?.extensions)
|
||||
? packageJson.openclaw.extensions.filter(
|
||||
(entry) => typeof entry === "string" && entry.trim().length > 0,
|
||||
@@ -48,7 +48,7 @@ function collectPluginSourceEntries(packageJson) {
|
||||
return packageEntries.length > 0 ? packageEntries : ["./index.ts"];
|
||||
}
|
||||
|
||||
function collectTopLevelPublicSurfaceEntries(pluginDir) {
|
||||
export function collectTopLevelPublicSurfaceEntries(pluginDir) {
|
||||
if (!fs.existsSync(pluginDir)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -1,44 +1,24 @@
|
||||
[
|
||||
"dist/extensions/acpx/runtime-api.js",
|
||||
"dist/extensions/bluebubbles/runtime-api.js",
|
||||
"dist/extensions/browser/runtime-api.js",
|
||||
"dist/extensions/copilot-proxy/runtime-api.js",
|
||||
"dist/extensions/diffs/runtime-api.js",
|
||||
"dist/extensions/discord/runtime-api.js",
|
||||
"dist/extensions/discord/runtime-setter-api.js",
|
||||
"dist/extensions/feishu/runtime-api.js",
|
||||
"dist/extensions/google/runtime-api.js",
|
||||
"dist/extensions/googlechat/runtime-api.js",
|
||||
"dist/extensions/imessage/runtime-api.js",
|
||||
"dist/extensions/irc/runtime-api.js",
|
||||
"dist/extensions/line/runtime-api.js",
|
||||
"dist/extensions/lmstudio/runtime-api.js",
|
||||
"dist/extensions/lobster/runtime-api.js",
|
||||
"dist/extensions/matrix/helper-api.js",
|
||||
"dist/extensions/matrix/runtime-api.js",
|
||||
"dist/extensions/matrix/runtime-setter-api.js",
|
||||
"dist/extensions/matrix/thread-bindings-runtime.js",
|
||||
"dist/extensions/mattermost/runtime-api.js",
|
||||
"dist/extensions/memory-core/runtime-api.js",
|
||||
"dist/extensions/msteams/runtime-api.js",
|
||||
"dist/extensions/nextcloud-talk/runtime-api.js",
|
||||
"dist/extensions/nostr/runtime-api.js",
|
||||
"dist/extensions/ollama/runtime-api.js",
|
||||
"dist/extensions/open-prose/runtime-api.js",
|
||||
"dist/extensions/qqbot/runtime-api.js",
|
||||
"dist/extensions/signal/runtime-api.js",
|
||||
"dist/extensions/slack/runtime-api.js",
|
||||
"dist/extensions/slack/runtime-setter-api.js",
|
||||
"dist/extensions/telegram/runtime-api.js",
|
||||
"dist/extensions/telegram/runtime-setter-api.js",
|
||||
"dist/extensions/tlon/runtime-api.js",
|
||||
"dist/extensions/tokenjuice/runtime-api.js",
|
||||
"dist/extensions/twitch/runtime-api.js",
|
||||
"dist/extensions/voice-call/runtime-api.js",
|
||||
"dist/extensions/webhooks/runtime-api.js",
|
||||
"dist/extensions/whatsapp/light-runtime-api.js",
|
||||
"dist/extensions/whatsapp/runtime-api.js",
|
||||
"dist/extensions/zai/runtime-api.js",
|
||||
"dist/extensions/zalo/runtime-api.js",
|
||||
"dist/extensions/zalouser/runtime-api.js"
|
||||
"dist/extensions/zai/runtime-api.js"
|
||||
]
|
||||
|
||||
@@ -3,6 +3,7 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import JSON5 from "json5";
|
||||
import { resolvePluginNpmRuntimeBuildPlan } from "./plugin-npm-runtime-build.mjs";
|
||||
|
||||
const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA_PATH =
|
||||
"src/config/bundled-channel-config-metadata.generated.ts";
|
||||
@@ -19,6 +20,103 @@ function resolvePackageDir(repoRoot, packageDir) {
|
||||
return path.isAbsolute(packageDir) ? packageDir : path.resolve(repoRoot, packageDir);
|
||||
}
|
||||
|
||||
function resolvePackageJsonPath(packageDir) {
|
||||
return path.join(packageDir, "package.json");
|
||||
}
|
||||
|
||||
function packageRelativePathExists(packageDir, relativePath) {
|
||||
return fs.existsSync(path.join(packageDir, relativePath));
|
||||
}
|
||||
|
||||
function mergePackageFiles(packageDir, files) {
|
||||
const merged = new Set(
|
||||
Array.isArray(files) ? files.filter((entry) => typeof entry === "string") : [],
|
||||
);
|
||||
merged.add("dist/**");
|
||||
if (packageRelativePathExists(packageDir, "openclaw.plugin.json")) {
|
||||
merged.add("openclaw.plugin.json");
|
||||
}
|
||||
if (packageRelativePathExists(packageDir, "README.md")) {
|
||||
merged.add("README.md");
|
||||
}
|
||||
if (packageRelativePathExists(packageDir, "SKILL.md")) {
|
||||
merged.add("SKILL.md");
|
||||
}
|
||||
if (packageRelativePathExists(packageDir, "skills")) {
|
||||
merged.add("skills/**");
|
||||
}
|
||||
return [...merged];
|
||||
}
|
||||
|
||||
function listRuntimeBuildOutputs(plan) {
|
||||
return Object.keys(plan.entry)
|
||||
.map((entryKey) => `./dist/${entryKey}.js`)
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
function assertPluginNpmRuntimeBuildExists(plan) {
|
||||
const missing = listRuntimeBuildOutputs(plan).filter(
|
||||
(runtimePath) => !packageRelativePathExists(plan.packageDir, runtimePath.replace(/^\.\//u, "")),
|
||||
);
|
||||
if (missing.length > 0) {
|
||||
throw new Error(
|
||||
[
|
||||
`package-local plugin runtime is missing for ${plan.pluginDir}: ${missing.join(", ")}`,
|
||||
`Run node scripts/lib/plugin-npm-runtime-build.mjs ${path.relative(plan.repoRoot, plan.packageDir) || plan.packageDir} before publishing ${plan.packageJson.name ?? plan.pluginDir}.`,
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveAugmentedPluginNpmPackageJson(params) {
|
||||
const repoRoot = path.resolve(params.repoRoot ?? ".");
|
||||
const packageDir = resolvePackageDir(repoRoot, params.packageDir);
|
||||
const packageJsonPath = resolvePackageJsonPath(packageDir);
|
||||
if (!fs.existsSync(packageJsonPath)) {
|
||||
return {
|
||||
packageJsonPath,
|
||||
packageDir,
|
||||
repoRoot,
|
||||
changed: false,
|
||||
packageJson: undefined,
|
||||
reason: "missing-package-json",
|
||||
};
|
||||
}
|
||||
|
||||
const plan = resolvePluginNpmRuntimeBuildPlan({ repoRoot, packageDir });
|
||||
if (!plan) {
|
||||
return {
|
||||
packageJsonPath,
|
||||
packageDir,
|
||||
repoRoot,
|
||||
changed: false,
|
||||
packageJson: undefined,
|
||||
reason: "no-runtime-build",
|
||||
};
|
||||
}
|
||||
assertPluginNpmRuntimeBuildExists(plan);
|
||||
|
||||
const packageJson = {
|
||||
...plan.packageJson,
|
||||
files: mergePackageFiles(packageDir, plan.packageJson.files),
|
||||
openclaw: {
|
||||
...plan.packageJson.openclaw,
|
||||
runtimeExtensions: plan.runtimeExtensions,
|
||||
...(plan.runtimeSetupEntry ? { runtimeSetupEntry: plan.runtimeSetupEntry } : {}),
|
||||
},
|
||||
};
|
||||
const changed = JSON.stringify(packageJson) !== JSON.stringify(plan.packageJson);
|
||||
return {
|
||||
packageJsonPath,
|
||||
packageDir,
|
||||
repoRoot,
|
||||
changed,
|
||||
packageJson,
|
||||
pluginDir: plan.pluginDir,
|
||||
reason: changed ? "package-local-runtime" : "unchanged",
|
||||
};
|
||||
}
|
||||
|
||||
function readGeneratedBundledChannelConfigs(repoRoot) {
|
||||
const metadataPath = path.join(repoRoot, GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA_PATH);
|
||||
if (!fs.existsSync(metadataPath)) {
|
||||
@@ -133,34 +231,63 @@ export function resolveAugmentedPluginNpmManifest(params) {
|
||||
export function withAugmentedPluginNpmManifestForPackage(params, callback) {
|
||||
const repoRoot = path.resolve(params.repoRoot ?? ".");
|
||||
const packageDir = resolvePackageDir(repoRoot, params.packageDir);
|
||||
const resolved = resolveAugmentedPluginNpmManifest({
|
||||
const resolvedManifest = resolveAugmentedPluginNpmManifest({
|
||||
repoRoot,
|
||||
packageDir,
|
||||
});
|
||||
const resolvedPackageJson = resolveAugmentedPluginNpmPackageJson({
|
||||
repoRoot,
|
||||
packageDir,
|
||||
});
|
||||
|
||||
if (!resolved.changed || !resolved.manifest) {
|
||||
if (
|
||||
(!resolvedManifest.changed || !resolvedManifest.manifest) &&
|
||||
(!resolvedPackageJson.changed || !resolvedPackageJson.packageJson)
|
||||
) {
|
||||
return callback({
|
||||
...resolved,
|
||||
...resolvedManifest,
|
||||
packageDir,
|
||||
repoRoot,
|
||||
applied: false,
|
||||
packageJsonApplied: false,
|
||||
});
|
||||
}
|
||||
|
||||
const originalManifest = fs.readFileSync(resolved.manifestPath, "utf8");
|
||||
console.error(
|
||||
`[plugin-npm-publish] overlaying generated channel config metadata for ${resolved.pluginId}`,
|
||||
);
|
||||
writeJsonFile(resolved.manifestPath, resolved.manifest);
|
||||
const originalManifest =
|
||||
resolvedManifest.changed && resolvedManifest.manifest
|
||||
? fs.readFileSync(resolvedManifest.manifestPath, "utf8")
|
||||
: undefined;
|
||||
const originalPackageJson =
|
||||
resolvedPackageJson.changed && resolvedPackageJson.packageJson
|
||||
? fs.readFileSync(resolvedPackageJson.packageJsonPath, "utf8")
|
||||
: undefined;
|
||||
if (resolvedManifest.changed && resolvedManifest.manifest) {
|
||||
console.error(
|
||||
`[plugin-npm-publish] overlaying generated channel config metadata for ${resolvedManifest.pluginId}`,
|
||||
);
|
||||
writeJsonFile(resolvedManifest.manifestPath, resolvedManifest.manifest);
|
||||
}
|
||||
if (resolvedPackageJson.changed && resolvedPackageJson.packageJson) {
|
||||
console.error(
|
||||
`[plugin-npm-publish] overlaying package-local runtime metadata for ${resolvedPackageJson.pluginDir}`,
|
||||
);
|
||||
writeJsonFile(resolvedPackageJson.packageJsonPath, resolvedPackageJson.packageJson);
|
||||
}
|
||||
try {
|
||||
return callback({
|
||||
...resolved,
|
||||
...resolvedManifest,
|
||||
packageDir,
|
||||
repoRoot,
|
||||
applied: true,
|
||||
applied: resolvedManifest.changed && Boolean(resolvedManifest.manifest),
|
||||
packageJsonApplied: resolvedPackageJson.changed && Boolean(resolvedPackageJson.packageJson),
|
||||
});
|
||||
} finally {
|
||||
fs.writeFileSync(resolved.manifestPath, originalManifest, "utf8");
|
||||
if (originalManifest !== undefined) {
|
||||
fs.writeFileSync(resolvedManifest.manifestPath, originalManifest, "utf8");
|
||||
}
|
||||
if (originalPackageJson !== undefined) {
|
||||
fs.writeFileSync(resolvedPackageJson.packageJsonPath, originalPackageJson, "utf8");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
172
scripts/lib/plugin-npm-runtime-build.mjs
Normal file
172
scripts/lib/plugin-npm-runtime-build.mjs
Normal file
@@ -0,0 +1,172 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { build } from "tsdown";
|
||||
import {
|
||||
collectPluginSourceEntries,
|
||||
collectTopLevelPublicSurfaceEntries,
|
||||
} from "./bundled-plugin-build-entries.mjs";
|
||||
import { copyStaticExtensionAssetsForPackage } from "./static-extension-assets.mjs";
|
||||
|
||||
const env = {
|
||||
NODE_ENV: "production",
|
||||
};
|
||||
|
||||
function readJsonFile(filePath) {
|
||||
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
||||
}
|
||||
|
||||
function normalizePackageEntry(value) {
|
||||
return typeof value === "string" ? value.trim().replaceAll("\\", "/") : "";
|
||||
}
|
||||
|
||||
function isTypeScriptEntry(entry) {
|
||||
return /\.(?:c|m)?ts$/u.test(entry);
|
||||
}
|
||||
|
||||
function toPackageRuntimeEntry(entry) {
|
||||
const normalized = normalizePackageEntry(entry).replace(/^\.\//u, "");
|
||||
return `./dist/${normalized.replace(/\.[^.]+$/u, ".js")}`;
|
||||
}
|
||||
|
||||
function collectExternalDependencyNames(packageJson) {
|
||||
return new Set(
|
||||
[
|
||||
...Object.keys(packageJson.dependencies ?? {}),
|
||||
...Object.keys(packageJson.peerDependencies ?? {}),
|
||||
...Object.keys(packageJson.optionalDependencies ?? {}),
|
||||
].filter(Boolean),
|
||||
);
|
||||
}
|
||||
|
||||
function createNeverBundleDependencyMatcher(packageJson) {
|
||||
const externalDependencies = collectExternalDependencyNames(packageJson);
|
||||
return (id) => {
|
||||
if (id === "openclaw" || id.startsWith("openclaw/")) {
|
||||
return true;
|
||||
}
|
||||
for (const dependency of externalDependencies) {
|
||||
if (id === dependency || id.startsWith(`${dependency}/`)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
function packageEntryKey(entry) {
|
||||
return normalizePackageEntry(entry)
|
||||
.replace(/^\.\//u, "")
|
||||
.replace(/\.[^.]+$/u, "");
|
||||
}
|
||||
|
||||
function resolvePackageDir(repoRoot, packageDir) {
|
||||
return path.isAbsolute(packageDir) ? packageDir : path.resolve(repoRoot, packageDir);
|
||||
}
|
||||
|
||||
export function resolvePluginNpmRuntimeBuildPlan(params) {
|
||||
const repoRoot = path.resolve(params.repoRoot ?? ".");
|
||||
const packageDir = resolvePackageDir(repoRoot, params.packageDir);
|
||||
const packageJsonPath = path.join(packageDir, "package.json");
|
||||
if (!fs.existsSync(packageJsonPath)) {
|
||||
return null;
|
||||
}
|
||||
const packageJson = readJsonFile(packageJsonPath);
|
||||
if (packageJson.openclaw?.release?.publishToNpm !== true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const packageEntries = collectPluginSourceEntries(packageJson).map(normalizePackageEntry);
|
||||
const requiresRuntimeBuild = packageEntries.some(isTypeScriptEntry);
|
||||
if (!requiresRuntimeBuild) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const pluginDir = path.basename(packageDir);
|
||||
const sourceEntries = [
|
||||
...new Set([
|
||||
...packageEntries,
|
||||
...collectTopLevelPublicSurfaceEntries(packageDir).map(normalizePackageEntry),
|
||||
]),
|
||||
].filter(Boolean);
|
||||
const entry = Object.fromEntries(
|
||||
sourceEntries.map((sourceEntry) => [
|
||||
packageEntryKey(sourceEntry),
|
||||
path.join(packageDir, sourceEntry.replace(/^\.\//u, "")),
|
||||
]),
|
||||
);
|
||||
|
||||
return {
|
||||
repoRoot,
|
||||
packageDir,
|
||||
pluginDir,
|
||||
packageJson,
|
||||
sourceEntries,
|
||||
entry,
|
||||
outDir: path.join(packageDir, "dist"),
|
||||
runtimeExtensions: (Array.isArray(packageJson.openclaw?.extensions)
|
||||
? packageJson.openclaw.extensions
|
||||
: []
|
||||
)
|
||||
.map(normalizePackageEntry)
|
||||
.filter(Boolean)
|
||||
.map(toPackageRuntimeEntry),
|
||||
runtimeSetupEntry: normalizePackageEntry(packageJson.openclaw?.setupEntry)
|
||||
? toPackageRuntimeEntry(packageJson.openclaw.setupEntry)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export async function buildPluginNpmRuntime(params) {
|
||||
const plan = resolvePluginNpmRuntimeBuildPlan(params);
|
||||
if (!plan) {
|
||||
return null;
|
||||
}
|
||||
|
||||
fs.rmSync(plan.outDir, { recursive: true, force: true });
|
||||
await build({
|
||||
clean: false,
|
||||
config: false,
|
||||
dts: false,
|
||||
deps: {
|
||||
neverBundle: createNeverBundleDependencyMatcher(plan.packageJson),
|
||||
},
|
||||
entry: plan.entry,
|
||||
env,
|
||||
fixedExtension: false,
|
||||
logLevel: params.logLevel ?? "info",
|
||||
outDir: plan.outDir,
|
||||
platform: "node",
|
||||
});
|
||||
const copiedStaticAssets = copyStaticExtensionAssetsForPackage({
|
||||
rootDir: plan.repoRoot,
|
||||
pluginDir: plan.pluginDir,
|
||||
});
|
||||
return {
|
||||
...plan,
|
||||
copiedStaticAssets,
|
||||
};
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const packageDir = argv[0];
|
||||
if (!packageDir) {
|
||||
throw new Error("usage: node scripts/lib/plugin-npm-runtime-build.mjs <package-dir>");
|
||||
}
|
||||
return { packageDir };
|
||||
}
|
||||
|
||||
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
||||
try {
|
||||
const { packageDir } = parseArgs(process.argv.slice(2));
|
||||
const result = await buildPluginNpmRuntime({ packageDir });
|
||||
if (result) {
|
||||
console.error(
|
||||
`[plugin-npm-runtime-build] built ${result.pluginDir} runtime (${result.sourceEntries.length} entries)`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error instanceof Error ? error.message : String(error));
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
77
scripts/lib/static-extension-assets.mjs
Normal file
77
scripts/lib/static-extension-assets.mjs
Normal file
@@ -0,0 +1,77 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
/**
|
||||
* Static, non-transpiled runtime assets referenced by built extension code.
|
||||
*
|
||||
* `dest` is the root-package dist path. Package-local runtime builds rewrite it
|
||||
* under the plugin package's own dist directory.
|
||||
*/
|
||||
export const STATIC_EXTENSION_ASSETS = [
|
||||
{
|
||||
src: "extensions/acpx/src/runtime-internals/mcp-proxy.mjs",
|
||||
dest: "dist/extensions/acpx/mcp-proxy.mjs",
|
||||
},
|
||||
{
|
||||
src: "extensions/acpx/src/runtime-internals/error-format.mjs",
|
||||
dest: "dist/extensions/acpx/error-format.mjs",
|
||||
},
|
||||
{
|
||||
src: "extensions/acpx/src/runtime-internals/mcp-command-line.mjs",
|
||||
dest: "dist/extensions/acpx/mcp-command-line.mjs",
|
||||
},
|
||||
{
|
||||
src: "extensions/diffs/assets/viewer-runtime.js",
|
||||
dest: "dist/extensions/diffs/assets/viewer-runtime.js",
|
||||
},
|
||||
];
|
||||
|
||||
export function listStaticExtensionAssetOutputs(params = {}) {
|
||||
const assets = params.assets ?? STATIC_EXTENSION_ASSETS;
|
||||
return assets
|
||||
.map(({ dest }) => dest.replace(/\\/g, "/"))
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
export function copyStaticExtensionAssets(params = {}) {
|
||||
const rootDir = params.rootDir ?? process.cwd();
|
||||
const assets = params.assets ?? STATIC_EXTENSION_ASSETS;
|
||||
const fsImpl = params.fs ?? fs;
|
||||
const warn = params.warn ?? console.warn;
|
||||
for (const { src, dest } of assets) {
|
||||
const srcPath = path.join(rootDir, src);
|
||||
const destPath = path.join(rootDir, dest);
|
||||
if (fsImpl.existsSync(srcPath)) {
|
||||
fsImpl.mkdirSync(path.dirname(destPath), { recursive: true });
|
||||
fsImpl.copyFileSync(srcPath, destPath);
|
||||
} else {
|
||||
warn(`[runtime-postbuild] static asset not found, skipping: ${src}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function copyStaticExtensionAssetsForPackage(params) {
|
||||
const rootDir = params.rootDir ?? process.cwd();
|
||||
const assets = params.assets ?? STATIC_EXTENSION_ASSETS;
|
||||
const fsImpl = params.fs ?? fs;
|
||||
const packagePrefix = `extensions/${params.pluginDir}/`;
|
||||
const rootDistPrefix = `dist/extensions/${params.pluginDir}/`;
|
||||
const copied = [];
|
||||
for (const { src, dest } of assets) {
|
||||
const normalizedSrc = src.replaceAll("\\", "/");
|
||||
const normalizedDest = dest.replaceAll("\\", "/");
|
||||
if (!normalizedSrc.startsWith(packagePrefix) || !normalizedDest.startsWith(rootDistPrefix)) {
|
||||
continue;
|
||||
}
|
||||
const srcPath = path.join(rootDir, src);
|
||||
if (!fsImpl.existsSync(srcPath)) {
|
||||
continue;
|
||||
}
|
||||
const packageRelativeDest = normalizedDest.slice(rootDistPrefix.length);
|
||||
const destPath = path.join(rootDir, packagePrefix, "dist", packageRelativeDest);
|
||||
fsImpl.mkdirSync(path.dirname(destPath), { recursive: true });
|
||||
fsImpl.copyFileSync(srcPath, destPath);
|
||||
copied.push(`dist/${packageRelativeDest}`);
|
||||
}
|
||||
return copied.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
@@ -75,6 +75,15 @@ log "Resolved mirror dist-tags: ${mirror_dist_tags_csv:-<none>}"
|
||||
log "Mirror dist-tag auth source: ${mirror_auth_source}"
|
||||
log "Mirror dist-tag auth requirement: ${mirror_auth_requirement}"
|
||||
|
||||
build_package_runtime() {
|
||||
if [[ "${OPENCLAW_PLUGIN_NPM_RUNTIME_BUILD:-1}" == "0" || "${OPENCLAW_PLUGIN_NPM_RUNTIME_BUILD:-1}" == "false" ]]; then
|
||||
log "Package-local runtime build: skipped"
|
||||
return
|
||||
fi
|
||||
log "Package-local runtime build: ${package_dir}"
|
||||
node scripts/lib/plugin-npm-runtime-build.mjs "${package_dir}"
|
||||
}
|
||||
|
||||
mirror_auth_token=""
|
||||
case "${mirror_auth_source}" in
|
||||
node-auth-token)
|
||||
@@ -122,6 +131,8 @@ if [[ "${mode}" == "--dry-run" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
build_package_runtime
|
||||
|
||||
if [[ "${mode}" == "--pack-dry-run" ]]; then
|
||||
node scripts/lib/plugin-npm-package-manifest.mjs --run "${package_dir}" -- \
|
||||
npm pack --dry-run --json --ignore-scripts
|
||||
|
||||
@@ -4,10 +4,16 @@ import { performance } from "node:perf_hooks";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import { copyBundledPluginMetadata } from "./copy-bundled-plugin-metadata.mjs";
|
||||
import { copyPluginSdkRootAlias } from "./copy-plugin-sdk-root-alias.mjs";
|
||||
import {
|
||||
copyStaticExtensionAssets,
|
||||
listStaticExtensionAssetOutputs,
|
||||
} from "./lib/static-extension-assets.mjs";
|
||||
import { writeTextFileIfChanged } from "./runtime-postbuild-shared.mjs";
|
||||
import { stageBundledPluginRuntime } from "./stage-bundled-plugin-runtime.mjs";
|
||||
import { writeOfficialChannelCatalog } from "./write-official-channel-catalog.mjs";
|
||||
|
||||
export { copyStaticExtensionAssets, listStaticExtensionAssetOutputs };
|
||||
|
||||
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const ROOT_RUNTIME_ALIAS_PATTERN = /^(?<base>.+\.(?:runtime|contract))-[A-Za-z0-9_-]+\.js$/u;
|
||||
const LEGACY_CLI_EXIT_COMPAT_CHUNKS = [
|
||||
@@ -21,60 +27,6 @@ const LEGACY_CLI_EXIT_COMPAT_CHUNKS = [
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Copy static (non-transpiled) runtime assets that are referenced by their
|
||||
* source-relative path inside bundled extension code.
|
||||
*
|
||||
* Each entry: { src: repo-root-relative source, dest: dist-relative dest }
|
||||
*/
|
||||
const STATIC_EXTENSION_ASSETS = [
|
||||
// acpx MCP proxy — co-deployed alongside the acpx index bundle so that
|
||||
// `path.resolve(dirname(import.meta.url), "mcp-proxy.mjs")` resolves correctly
|
||||
// at runtime from the built ACPX extension directory.
|
||||
{
|
||||
src: "extensions/acpx/src/runtime-internals/mcp-proxy.mjs",
|
||||
dest: "dist/extensions/acpx/mcp-proxy.mjs",
|
||||
},
|
||||
{
|
||||
src: "extensions/acpx/src/runtime-internals/error-format.mjs",
|
||||
dest: "dist/extensions/acpx/error-format.mjs",
|
||||
},
|
||||
{
|
||||
src: "extensions/acpx/src/runtime-internals/mcp-command-line.mjs",
|
||||
dest: "dist/extensions/acpx/mcp-command-line.mjs",
|
||||
},
|
||||
// diffs viewer runtime bundle — co-deployed inside the plugin package so the
|
||||
// built bundle can resolve `./assets/viewer-runtime.js` from dist.
|
||||
{
|
||||
src: "extensions/diffs/assets/viewer-runtime.js",
|
||||
dest: "dist/extensions/diffs/assets/viewer-runtime.js",
|
||||
},
|
||||
];
|
||||
|
||||
export function listStaticExtensionAssetOutputs(params = {}) {
|
||||
const assets = params.assets ?? STATIC_EXTENSION_ASSETS;
|
||||
return assets
|
||||
.map(({ dest }) => dest.replace(/\\/g, "/"))
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
export function copyStaticExtensionAssets(params = {}) {
|
||||
const rootDir = params.rootDir ?? ROOT;
|
||||
const assets = params.assets ?? STATIC_EXTENSION_ASSETS;
|
||||
const fsImpl = params.fs ?? fs;
|
||||
const warn = params.warn ?? console.warn;
|
||||
for (const { src, dest } of assets) {
|
||||
const srcPath = path.join(rootDir, src);
|
||||
const destPath = path.join(rootDir, dest);
|
||||
if (fsImpl.existsSync(srcPath)) {
|
||||
fsImpl.mkdirSync(path.dirname(destPath), { recursive: true });
|
||||
fsImpl.copyFileSync(srcPath, destPath);
|
||||
} else {
|
||||
warn(`[runtime-postbuild] static asset not found, skipping: ${src}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function writeStableRootRuntimeAliases(params = {}) {
|
||||
const rootDir = params.rootDir ?? ROOT;
|
||||
const distDir = path.join(rootDir, "dist");
|
||||
@@ -126,7 +78,12 @@ export function runRuntimePostBuild(params = {}) {
|
||||
runPhase("bundled plugin runtime overlay", () => stageBundledPluginRuntime(params));
|
||||
runPhase("stable root runtime aliases", () => writeStableRootRuntimeAliases(params));
|
||||
runPhase("legacy CLI exit compat chunks", () => writeLegacyCliExitCompatChunks(params));
|
||||
runPhase("static extension assets", () => copyStaticExtensionAssets(params));
|
||||
runPhase("static extension assets", () =>
|
||||
copyStaticExtensionAssets({
|
||||
rootDir: ROOT,
|
||||
...params,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
||||
|
||||
Reference in New Issue
Block a user