openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,346 lines • 65.6 kB
JavaScript
import { A as PluginDependencyLinkError, c as getPluginCache } from "./plugin-cache-CfBhQFSN.mjs";
import { t as hasErrnoCode } from "./errno-FR4CRyya.mjs";
import { r as isPathInside } from "./path-guards-CH7noY0Z.mjs";
import { n as isArtifactPreservingStateRead } from "./artifact-preserving-state-reads-BQnQLAJR.mjs";
import "./openclaw-state-db-readonly-ywwvuxdQ.mjs";
import { r as captureOpenClawStateWorkerContext } from "./openclaw-state-worker-context-BvvgY6-t.mjs";
import { t as _usingCtx } from "./usingCtx-BhiZp_rS.mjs";
import { i as isPluginSourceCaptureRetained, s as retainPluginNativeCapturePath, t as createPluginNativeCaptureRoot } from "./plugin-source-capture-directory-BTDwXfw8.mjs";
import { n as resolveInstalledPluginIndexStateDatabaseOptions } from "./installed-plugin-index-store-path-CUbUiRdU.mjs";
import { c as snapshotOwners, l as startNativeAdmissionPublication, n as nativeAdmissionStateFor, o as retainNativePath } from "./plugin-native-admission-state-DZQSA6iK.mjs";
import { A as packageName, B as pluginSourceIdentityChangedOnlyByCtime, C as createPluginDependencyLookup, D as findPluginCapturedPackage, E as createPluginSourceCapture, F as verifyPluginSourceInputs, H as createPluginCaptureResolver, I as copyPluginSourceFile, L as hashPluginSourceFile, N as pluginSourceInputIdentity, O as importTargetNames, P as readPluginSourceDirectory, R as isPluginNativeExecutable, S as capturePluginPackageMetadata, T as createPluginPackageMetadataCapture, V as pluginSourceStatIdentity, _ as pluginNativeNamespaceIsCurrent, a as createPluginSourceFacts, b as capturePluginDependencies, c as createPluginNativeReferenceValidator, d as assertPluginNativeNamespaceHost, f as capturePluginNativeDirectoryAliases, g as pluginNativeNamespaceDirectory, h as pluginNativeNamespaceBoundary, i as createPluginGenerationSourceLookup, j as resolvePluginModulePackageRoot, k as isPluginPackageFile, l as linkPluginNativeReference, m as finishPluginNativeNamespace, n as createPluginGenerationCapture, p as capturePluginNativeNamespace, r as createPluginGenerationModuleLookup, s as admitPluginNativeRecoveryReference, t as assertPluginSourceRootCurrent, v as pluginNativeNamespaceMemberPath, w as createPluginNativeDependencyScopes, x as capturePluginModuleSource, y as pluginNativeNamespaceMemberRelativePath, z as isPluginSourceEntry } from "./plugin-generation-source-lookup-BCzbJZNI.mjs";
import { isBuiltin } from "node:module";
import fs from "node:fs";
import { fileURLToPath, pathToFileURL } from "node:url";
import path from "node:path";
import { isDeepStrictEqual } from "node:util";
import { createHash, randomUUID } from "node:crypto";
import { createRootFileCopyBatchSync } from "@openclaw/fs-safe/advanced";
import { moduleResolve } from "import-meta-resolve";
import { parse as parse$1 } from "acorn";
//#region src/plugins/plugin-generation-file-capture.ts
/** Publish only verified dependency links; real nested installs keep their own bytes. */
function linkPluginGenerationDependency(link, target) {
const entry = fs.lstatSync(link, { throwIfNoEntry: false });
if (entry && !entry.isSymbolicLink()) return false;
if (entry && path.resolve(path.dirname(link), fs.readlinkSync(link)) === target && fs.statSync(link, { throwIfNoEntry: false })) return false;
fs.mkdirSync(path.dirname(link), {
recursive: true,
mode: 448
});
if (process.platform === "win32") {
if (entry) {
if (!fs.lstatSync(link).isSymbolicLink()) throw new Error(`Dependency link became a real entry: ${link}`);
fs.unlinkSync(link);
}
fs.symlinkSync(target, link, "junction");
} else {
const staged = `${link}.${randomUUID()}.tmp`;
try {
fs.symlinkSync(path.relative(path.dirname(link), target), staged, "junction");
fs.renameSync(staged, link);
} finally {
fs.rmSync(staged, { force: true });
}
}
if (!fs.lstatSync(link).isSymbolicLink() || fs.realpathSync(link) !== fs.realpathSync(target)) throw new Error(`Dependency link does not resolve to its captured target: ${link}`);
if (entry) process.emitWarning(`Replacing plugin dependency link ${link}`);
return true;
}
function createPluginSourceLinkCapture() {
const links = /* @__PURE__ */ new Set();
return {
defer(filename, root, source = filename) {
if (!fs.lstatSync(filename).isSymbolicLink() || isPathInside(root, fs.realpathSync(filename))) return false;
links.add(source);
return true;
},
contains: (filename) => [...links].some((link) => isPathInside(link, filename))
};
}
/** Capture each selected package file once, retaining its source facts and ordered receipt. */
function createPluginGenerationFileCapture({ boundary, destination, directory, outputRoot, capturedPaths, originalSources, hardlinkedSources, sourceCapture, sourceLinks, deferExternalLinks, nativeAdmission, receipt, retained, sourceFacts, onPackageMetadata }) {
const { inputs, pendingInputs, additions } = sourceCapture;
const ancestors = /* @__PURE__ */ new Set();
const copy = (source, target, copyFile) => {
if (capturedPaths.get(path.resolve(source)) === target) return;
const prepared = nativeAdmission.resolvePreparedSource(source);
const input = prepared?.path ?? source;
const inputBoundary = prepared?.boundary ?? boundary;
const real = fs.realpathSync(input);
const retainedNative = nativeAdmission.isRetainedReference(source, real);
if (!isPathInside(inputBoundary, real) && !retainedNative) throw new Error(`Plugin source link leaves its package: ${path.relative(boundary, source)}. Declare shared code as a package dependency.`);
if (!prepared && outputRoot && isPathInside(outputRoot, real)) return;
const stat = fs.statSync(real, { bigint: true });
const captured = capturedPaths.get(real);
const recordContent = (contentHash, sizeBytes = 0, native = false, identity = pluginSourceInputIdentity(stat), admittedBoundary = inputBoundary) => {
if (sourceFacts) {
const original = fs.realpathSync(source);
sourceFacts.set(path.relative(directory, target), {
source: path.resolve(source),
input: {
identity: pluginSourceInputIdentity(fs.statSync(original, { bigint: true })),
contentHash: stat.isDirectory() ? readPluginSourceDirectory(original).contentHash : contentHash,
sizeBytes,
directory: stat.isDirectory(),
boundary: isPathInside(boundary, original) ? boundary : path.dirname(original)
}
});
}
if (!captured) {
inputs.set(real, {
identity,
contentHash,
sizeBytes,
directory: stat.isDirectory(),
boundary: admittedBoundary,
...native ? { native: true } : {}
});
pendingInputs.add(real);
}
};
capturedPaths.set(path.resolve(source), target);
originalSources.set(target, path.resolve(source));
capturedPaths.set(target, target);
if (!capturedPaths.has(real)) capturedPaths.set(real, target);
receipt.marker(`${stat.isDirectory() ? "directory" : "file"}\0${path.relative(destination, target)}\0`);
if (stat.isDirectory()) {
if (ancestors.has(real)) throw new Error(`Plugin source contains a directory cycle: ${source}`);
ancestors.add(real);
fs.mkdirSync(target, {
recursive: true,
mode: 448
});
const { names, contentHash } = readPluginSourceDirectory(real);
recordContent(contentHash);
for (const name of names) if (!(deferExternalLinks && !nativeAdmission.isRetainedReference(path.join(source, name)) && sourceLinks.defer(path.join(input, name), inputBoundary, path.join(source, name)))) copy(path.join(source, name), path.join(target, name), copyFile);
ancestors.delete(real);
} else if (stat.isFile()) {
if (stat.nlink > 1n) hardlinkedSources.add(target);
fs.mkdirSync(path.dirname(target), {
recursive: true,
mode: 448
});
additions.add(target);
const retainedFile = retained?.files.get(path.relative(directory, target));
if (retainedFile && retainedFile.source !== path.resolve(source)) throw new Error("Plugin retained source layout changed during admission");
const retainedReference = retained?.source.native?.references.has(target);
if (retainedReference) fs.unlinkSync(target);
const native = nativeAdmission.materialize(real, inputBoundary, target, stat, source);
let copiedContent;
if (native) nativeAdmission.reconcileSourceInputs(inputs);
else if (retainedFile && !retainedReference) {
const retainedInput = retainedFile.input;
copiedContent = {
contentHash: retainedInput.contentHash,
sizeBytes: retainedInput.sizeBytes,
sourceIdentity: pluginSourceInputIdentity(stat)
};
} else if (captured) copiedContent = copyPluginSourceFile(captured, directory, target, {
hashCopiedContent: true,
copyFile,
preserveSourceMode: true
});
else {
copiedContent = copyPluginSourceFile(real, inputBoundary, target, {
hashCopiedContent: true,
copyFile
});
const identity = pluginSourceInputIdentity(stat);
if (copiedContent && copiedContent.sourceIdentity !== identity && !pluginSourceIdentityChangedOnlyByCtime(identity, copiedContent.sourceIdentity)) throw new Error("Plugin source changed while preparing its reload; retry after the edit finishes.");
}
receipt.file({
target: native?.path ?? target,
boundary: native?.boundary ?? directory,
sizeBytes: Number(stat.size),
native: native !== void 0,
prepared: native?.content ?? copiedContent,
onContent: (content) => {
native?.record(content);
recordContent(content.contentHash, content.sizeBytes, native !== void 0, native?.sourceIdentity ?? (captured ? void 0 : copiedContent?.sourceIdentity), native?.sourceBoundary ?? inputBoundary);
}
});
if (path.basename(target) === "package.json") onPackageMetadata(source, target);
} else throw new Error(`Plugin build input is not a regular file: ${source}`);
};
return (source, target) => {
try {
var _usingCtx$1 = _usingCtx();
const batch = _usingCtx$1.u(createRootFileCopyBatchSync());
return copy(source, target, batch.copyFile.bind(batch));
} catch (_) {
_usingCtx$1.e = _;
} finally {
_usingCtx$1.d();
}
};
}
//#endregion
//#region src/plugins/plugin-generation-receipt.ts
/** Reuse the initial raw-byte receipt only when its complete ordered inputs still match. */
function createPluginGenerationReceipt(prepared) {
const events = [];
let finished;
const publishContent = (file, content) => {
if (content.sizeBytes !== file.sizeBytes) throw new Error("Plugin source changed while preparing its reload; retry after the edit finishes.");
if (!file.content) {
file.content = content;
file.onContent(content);
}
};
const readContent = (file, receipt, expected = file.content) => {
const content = hashPluginSourceFile(file.target, file.boundary, receipt, file.native ? file.content : void 0);
if (!file.native && expected && content.contentHash !== expected.contentHash) throw new Error("Plugin source changed while preparing its reload; retry after the edit finishes.");
publishContent(file, content);
};
const inputSignature = () => {
if (events.some((event) => typeof event !== "string" && !event.content)) return;
const signature = createHash("sha256");
for (const event of events) signature.update(JSON.stringify(typeof event === "string" ? ["marker", event] : [
"file",
event.sizeBytes,
event.content.contentHash
]));
return signature.digest("hex");
};
return {
marker(framed) {
if (!finished) events.push(framed);
},
file(params) {
const { prepared: content, ...input } = params;
const file = input;
if (content) {
if (finished && !file.native) readContent(file, void 0, content);
else publishContent(file, content);
} else if (!file.native || finished) readContent(file);
if (!finished) events.push(file);
},
finish() {
if (finished) return finished;
const signature = inputSignature();
const receipt = signature !== void 0 && prepared?.signature === signature ? void 0 : createHash("sha256");
for (const event of events) if (typeof event === "string") receipt?.update(event);
else if (receipt || !event.content || !event.native) readContent(event, receipt);
finished = {
signature: signature ?? inputSignature(),
sourceDigest: receipt ? receipt.digest("hex") : prepared.sourceDigest
};
events.length = 0;
return finished;
}
};
}
//#endregion
//#region src/plugins/plugin-source-admission-store.ts
/** Deferred publication retains its original store and authority through settlement. */
function createPluginSourceAdmissionPublisher(options = {}) {
if (isArtifactPreservingStateRead()) return;
let context;
try {
context = captureOpenClawStateWorkerContext(resolveInstalledPluginIndexStateDatabaseOptions(options));
} catch (error) {
return async () => {
throw error;
};
}
return async (publication) => {
if (isArtifactPreservingStateRead()) return false;
const prepared = structuredClone(publication);
const { runOpenClawStateWorkerOperation } = await import("./openclaw-state-worker-store-x5r3Lkjl.mjs");
return await runOpenClawStateWorkerOperation(context, (scope) => scope.execute({
type: "plugins.metadata.sourceAdmission.publish",
input: prepared
}), { existingOnly: true }) ?? false;
};
}
//#endregion
//#region src/plugins/plugin-native-admission.ts
const nativeSourceMembers = /* @__PURE__ */ new WeakMap();
function nativeMemberForSource(namespace, source) {
const relative = path.relative(namespace.sourceDirectory, source);
if (namespace.members[relative]?.source === source) return relative;
let members = nativeSourceMembers.get(namespace);
if (!members) {
members = /* @__PURE__ */ new Map();
for (const [key, { source: memberSource }] of Object.entries(namespace.members)) if (!members.has(memberSource)) members.set(memberSource, key);
nativeSourceMembers.set(namespace, members);
}
return members.get(source);
}
function createNativeRecovery(receipt, references, namespaces, directories, roots) {
const custody = {};
const paths = [...references.values()].map((fact) => fact.capturedPath);
paths.push(...[...namespaces.keys()].map((root) => path.join(root, "content")));
const pins = [...new Set(paths)].map(retainPluginNativeCapturePath);
for (const root of roots) snapshotOwners.get(root).add(custody);
let disposed = false;
const release = () => {
if (disposed) return [];
disposed = true;
for (const pin of pins) pin();
return roots.filter((root) => {
const owners = snapshotOwners.get(root);
owners.delete(custody);
return owners.size === 0;
});
};
return {
receipt: { ...receipt },
references,
namespaces,
directories,
fork(relocate = (source) => source) {
if (disposed) throw new Error("Plugin native recovery has been disposed");
return createNativeRecovery(receipt, new Map([...references].map(([source, fact]) => [relocate(source), { ...fact }])), structuredClone(namespaces), new Map([...directories].map(([source, namespace]) => [relocate(source), namespace])), roots);
},
retain(cache) {
if (disposed) throw new Error("Plugin native recovery has been disposed");
const state = nativeAdmissionStateFor(cache);
for (const [key, namespace] of namespaces) {
state.namespaces.set(key, namespace);
retainNativePath(state, path.join(key, "content"));
}
for (const root of roots) {
state.roots.add(root);
snapshotOwners.get(root).add(state);
}
for (const fact of references.values()) retainNativePath(state, fact.capturedPath);
},
dispose() {
for (const root of release()) root.dispose();
},
async disposeAsync() {
await Promise.all(release().map((root) => root.disposeAsync()));
}
};
}
function isNativeArtifact(source, boundary, stat, known) {
return /\.(?:node|so(?:\.\d+)*|dylib|dll|exe|bin)$/i.test(source) || (stat.mode & 73n) !== 0n && path.extname(source) === "" && (known || isPluginNativeExecutable(source, boundary));
}
/** Admission is shared below registration; each generation still owns its module graph. */
function createPluginNativeAdmission(rootDir, directory, entryFile, recovery, outputRoot) {
recovery?.retain(getPluginCache());
const state = nativeAdmissionStateFor();
const key = `${path.resolve(rootDir)}\0${entryFile ? path.resolve(entryFile) : ""}`;
const owner = state.owners.get(path.resolve(rootDir));
const publishAdmission = owner && !state.artifactPreservingReadOnly ? createPluginSourceAdmissionPublisher({ stateDir: state.publicationStateDir }) : void 0;
const prepared = recovery?.receipt ?? state.receipts.get(key);
const selected = /* @__PURE__ */ new Map();
const priorNamespaces = /* @__PURE__ */ new Set();
const files = /* @__PURE__ */ new Map();
const targets = /* @__PURE__ */ new Map();
const hardlinkedTargets = /* @__PURE__ */ new Set();
const pendingTargets = /* @__PURE__ */ new Set();
let assertReference = createPluginNativeReferenceValidator(directory, owner?.pluginId ?? rootDir);
const recoveredFiles = /* @__PURE__ */ new Map();
let hostRoot;
let finalReceipt;
const namespaces = () => [...new Set(selected.values())];
for (const [alias, id] of recovery?.directories ?? []) {
const namespace = state.namespaces.get(id);
if (namespace) selected.set(alias, namespace);
}
const namespaceFor = (source) => [...selected].toSorted(([left], [right]) => right.length - left.length).find(([alias]) => isPathInside(alias, source));
const resolvePreparedSource = (source) => {
const file = files.get(path.resolve(source));
if (file) return {
path: file.capturedPath,
boundary: pluginNativeNamespaceBoundary(state.namespaces.get(file.namespace))
};
if (!recovery) for (const namespace of namespaces()) {
const relative = nativeMemberForSource(namespace, source);
if (relative !== void 0) return {
path: pluginNativeNamespaceMemberPath(namespace, relative),
boundary: pluginNativeNamespaceBoundary(namespace)
};
}
const found = namespaceFor(path.resolve(source));
return found ? {
path: path.join(pluginNativeNamespaceDirectory(found[1]), path.relative(found[0], path.resolve(source))),
boundary: pluginNativeNamespaceBoundary(found[1])
} : void 0;
};
const sourceForPrepared = (filename) => {
for (const [alias, namespace] of selected) {
const root = pluginNativeNamespaceDirectory(namespace);
if (isPathInside(root, filename)) {
const relative = path.relative(root, filename);
return !recovery && namespace.members[relative]?.source || path.join(alias, relative);
}
}
};
const createNamespace = (sourceDirectory, boundary, managed, previous, retainedRoot) => {
const root = createPluginNativeCaptureRoot(state.captureStorage.stateDir, state.captureStorage.placement);
state.roots.add(root);
snapshotOwners.set(root, /* @__PURE__ */ new Set([state]));
const { fact, changed } = capturePluginNativeNamespace({
sourceDirectory,
boundary,
managed,
copyOnly: state.captureStorage.placement === "temporary",
previous,
retainedRoot,
managedRoots: [...state.managedRoots.keys()],
capturedRoot: root.directory,
outputRoot,
inspectionRoots: [.../* @__PURE__ */ new Set([boundary, ...owner ? [owner.rootDir] : []])],
inspectedFiles: [
owner?.source,
owner?.setupSource,
entryFile
].filter((file) => Boolean(file))
});
for (const namespace of state.namespaces.values()) for (const [relative, member] of Object.entries(namespace.members)) {
const identity = changed.get(member.source);
const sourceChanged = identity !== void 0 && pluginSourceIdentityChangedOnlyByCtime(member.sourceIdentity, identity);
const captureChanged = identity !== void 0 && pluginSourceIdentityChangedOnlyByCtime(member.capturedIdentity, identity);
if (!identity || !sourceChanged && !captureChanged) continue;
if (!namespace.referenceRoot && namespace !== previous && !namespaces().includes(namespace) && !priorNamespaces.has(namespace) && !isPluginSourceCaptureRetained(namespace.capturedRoot) && !fs.lstatSync(namespace.capturedRoot, { throwIfNoEntry: false })) break;
if (namespace !== previous || !captureChanged) {
const capturedHash = hashPluginSourceFile(pluginNativeNamespaceMemberPath(namespace, relative), pluginNativeNamespaceBoundary(namespace)).contentHash;
if (member.contentHash && capturedHash !== member.contentHash || sourceChanged && hashPluginSourceFile(member.source, path.dirname(member.source)).contentHash !== capturedHash) throw new Error("Native plugin companion changed during admission");
member.contentHash ??= capturedHash;
}
if (sourceChanged) member.sourceIdentity = identity;
if (captureChanged) member.capturedIdentity = identity;
}
state.namespaces.set(root.directory, fact);
return fact;
};
const publish = () => {
if (!finalReceipt || files.size === 0) return;
for (const [source, fact] of files) {
state.files.set(source, fact);
retainNativePath(state, fact.capturedPath);
retainNativePath(state, path.join(fact.namespace, "content"));
}
const used = new Set([...files.values()].map((fact) => fact.namespace));
const next = structuredClone({
...finalReceipt,
nativeArtifacts: Object.fromEntries(files),
nativeNamespaces: Object.fromEntries([...used].map((id) => [id, state.namespaces.get(id)]))
});
const unchanged = isDeepStrictEqual(state.receipts.get(key), next);
state.receipts.set(key, next);
if (!owner || !publishAdmission) return;
if (unchanged) {
startNativeAdmissionPublication(state, key);
return;
}
const roots = [...state.roots].filter((root) => used.has(root.directory));
const publication = () => publishAdmission({
pluginId: owner.pluginId,
rootDir: owner.rootDir,
installRecordHash: owner.installRecordHash,
key,
receipt: next
}).then((accepted) => {
if (accepted) {
for (const root of roots) root.commit();
if (state.publications.get(key) === publication) state.publications.delete(key);
}
}).catch((error) => process.emitWarning(`Plugin source receipt was not recorded: ${String(error)}`));
state.publications.set(key, publication);
startNativeAdmissionPublication(state, key);
};
const refreshReference = (fact, input, target) => {
const namespace = state.namespaces.get(fact.namespace);
const relative = pluginNativeNamespaceMemberRelativePath(namespace, fact.capturedPath);
const member = namespace.members[relative];
const linked = {
...fact,
sourceIdentity: pluginSourceStatIdentity(fs.statSync(input, { bigint: true }))
};
if (linkPluginNativeReference(input, target, linked) === "hardlink") hardlinkedTargets.add(target);
else hardlinkedTargets.delete(target);
const oldIdentity = member.capturedIdentity;
member.capturedIdentity = linked.capturedIdentity;
if (member.sourceIdentity === oldIdentity) member.sourceIdentity = linked.capturedIdentity;
fact.sourceIdentity = member.sourceIdentity;
fact.capturedIdentity = linked.capturedIdentity;
pendingTargets.add(target);
return linked.sourceIdentity;
};
const assertReferenceNamespaces = (references = pendingTargets) => {
for (const target of references) {
if (!hardlinkedTargets.has(target)) continue;
const fact = files.get(targets.get(target));
assertReference(target, fact, state.namespaces.get(fact.namespace), hostRoot);
}
};
const linkHost = (selectedHost) => {
hostRoot = fs.realpathSync(selectedHost);
for (const namespace of namespaces()) {
if (namespace.referenceRoot) {
assertPluginNativeNamespaceHost(namespace, hostRoot, rootDir);
continue;
}
const link = path.join(namespace.capturedRoot, "node_modules", "openclaw");
const present = fs.lstatSync(link, { throwIfNoEntry: false });
if (present && fs.realpathSync(link) !== hostRoot) {
const replacement = createNamespace(namespace.sourceDirectory, namespace.sourceDirectory, namespace.managed, namespace);
priorNamespaces.add(namespace);
fs.mkdirSync(path.join(replacement.capturedRoot, "node_modules"), {
recursive: true,
mode: 448
});
fs.symlinkSync(hostRoot, path.join(replacement.capturedRoot, "node_modules", "openclaw"), "junction");
for (const [alias, selectedNamespace] of selected) if (selectedNamespace === namespace) selected.set(alias, replacement);
for (const [source, fact] of files) {
if (fact.namespace !== namespace.capturedRoot) continue;
const relative = pluginNativeNamespaceMemberRelativePath(namespace, fact.capturedPath);
const member = replacement.members[relative];
files.set(source, {
...fact,
namespace: replacement.capturedRoot,
capturedPath: path.join(pluginNativeNamespaceDirectory(replacement), relative),
capturedIdentity: member.capturedIdentity,
sourceIdentity: member.sourceIdentity
});
}
for (const [target, source] of targets) {
const fact = files.get(source);
if (fact.namespace !== replacement.capturedRoot) continue;
fs.unlinkSync(target);
refreshReference(fact, fact.capturedPath, target);
}
} else if (!present) {
fs.mkdirSync(path.dirname(link), {
recursive: true,
mode: 448
});
try {
fs.symlinkSync(hostRoot, link, "junction");
} catch (error) {
if (!hasErrnoCode(error, "EEXIST")) throw error;
if (fs.realpathSync(link) !== hostRoot) return linkHost(hostRoot);
}
}
}
};
return {
prepared,
resolvePreparedSource,
sourceForPrepared,
reconcileSourceInputs(inputs) {
for (const namespace of /* @__PURE__ */ new Set([...priorNamespaces, ...namespaces()])) for (const [relative, member] of Object.entries(namespace.members)) {
if (member.sizeBytes === void 0) continue;
const capturedPath = pluginNativeNamespaceMemberPath(namespace, relative);
for (const [source, identity] of [[member.source, member.sourceIdentity], [capturedPath, member.capturedIdentity]]) {
const input = inputs.get(source);
if (!input || input.identity === identity) continue;
if (!pluginSourceIdentityChangedOnlyByCtime(input.identity, identity)) throw new Error("Plugin source changed during native namespace admission");
member.contentHash ??= hashPluginSourceFile(capturedPath, pluginNativeNamespaceBoundary(namespace)).contentHash;
if (input.contentHash !== member.contentHash) throw new Error("Plugin source changed during native namespace admission");
input.identity = identity;
}
}
},
captureRecovery(receipt, packageRoots) {
const references = new Map([...targets].map(([target, source]) => [target, { ...files.get(source) }]));
const used = new Set([...references.values()].map((fact) => fact.namespace));
return createNativeRecovery(receipt, references, new Map([...used].map((id) => [id, structuredClone(state.namespaces.get(id))])), capturePluginNativeDirectoryAliases(selected, packageRoots), [...state.roots].filter((root) => used.has(root.directory)));
},
isRetainedReference(source, real) {
const fact = recovery?.references.get(path.resolve(source));
if (!fact) return false;
const canonical = real ?? fs.realpathSync(source);
const admitted = admitPluginNativeRecoveryReference(canonical, fact, recoveredFiles.get(canonical));
if (!admitted) return false;
recoveredFiles.set(canonical, admitted);
return true;
},
materialize(source, boundary, target, stat, logicalSource = sourceForPrepared(source) ?? source) {
const recovered = recoveredFiles.get(source);
const resolvedSource = sourceForPrepared(source) ?? source;
const known = files.get(logicalSource) ?? state.files.get(logicalSource) ?? recovered;
if (owner?.source === logicalSource || owner?.setupSource === logicalSource || entryFile === logicalSource) return;
if (!isNativeArtifact(source, boundary, stat, Boolean(known))) return;
const found = namespaceFor(resolvedSource);
let namespace = recovered ? state.namespaces.get(recovered.namespace) : namespaces().find((candidate) => nativeMemberForSource(candidate, resolvedSource) !== void 0) ?? found?.[1];
let alias = found?.[0] ?? path.dirname(resolvedSource);
if (!namespace) {
const tree = [...state.managedRoots].find(([root]) => isPathInside(root, resolvedSource));
const managed = Boolean(tree);
const admittedBoundary = tree?.[0] ?? boundary;
const packageRoot = resolvePluginModulePackageRoot(resolvedSource);
alias = isPathInside(admittedBoundary, packageRoot) ? packageRoot : path.dirname(resolvedSource);
const preferred = known && state.namespaces.get(known.namespace);
namespace = [.../* @__PURE__ */ new Set([...preferred ? [preferred] : [], ...[...state.namespaces.values()].toReversed()])].find((candidate) => {
if (!isPathInside(candidate.sourceDirectory, alias) || candidate.managed !== managed || candidate.referenceRoot !== void 0 && (tree?.[1] !== "retained-npm" || candidate.referenceRoot !== tree[0])) return false;
try {
return pluginNativeNamespaceIsCurrent(candidate, admittedBoundary, outputRoot);
} catch {
return false;
}
});
namespace ??= createNamespace(alias, admittedBoundary, managed, void 0, tree?.[1] === "retained-npm" ? tree[0] : void 0);
alias = namespace.sourceDirectory;
selected.set(alias, namespace);
} else if (recovered) alias = [...selected].find(([, candidate]) => candidate === namespace)?.[0] ?? alias;
const relative = recovered ? pluginNativeNamespaceMemberRelativePath(namespace, recovered.capturedPath) : nativeMemberForSource(namespace, resolvedSource) ?? path.relative(alias, resolvedSource);
const member = namespace.members[relative];
if (!member || member.sizeBytes === void 0) throw new Error("Native plugin artifact is outside its admitted directory");
const fact = {
sourceIdentity: member.sourceIdentity,
contentHash: member.contentHash ?? "",
sizeBytes: member.sizeBytes,
namespace: namespace.capturedRoot,
capturedPath: pluginNativeNamespaceMemberPath(namespace, relative),
capturedIdentity: member.capturedIdentity
};
const inputIdentity = refreshReference(fact, source, target);
files.set(logicalSource, fact);
targets.set(target, logicalSource);
return {
fact,
path: fact.capturedPath,
boundary: pluginNativeNamespaceBoundary(namespace),
sourceIdentity: inputIdentity,
sourceBoundary: path.dirname(source),
content: fact.contentHash ? {
contentHash: fact.contentHash,
sizeBytes: fact.sizeBytes
} : void 0,
record(content) {
Object.assign(fact, content);
Object.assign(member, content);
for (const other of namespaces()) for (const sibling of Object.values(other.members)) {
if (sibling.sizeBytes !== content.sizeBytes || sibling.contentHash) continue;
if (sibling.source === member.source && sibling.sourceIdentity === member.sourceIdentity || sibling.capturedIdentity.split(":").slice(0, 2).join(":") === member.capturedIdentity.split(":").slice(0, 2).join(":")) Object.assign(sibling, content);
}
}
};
},
finish(receipt) {
if (!pendingTargets.size) return;
for (const namespace of namespaces()) finishPluginNativeNamespace(namespace);
finalReceipt = receipt;
if (hostRoot) linkHost(hostRoot);
assertReferenceNamespaces();
publish();
pendingTargets.clear();
},
linkHost(selectedHost) {
assertReference = createPluginNativeReferenceValidator(directory, owner?.pluginId ?? rootDir);
linkHost(selectedHost);
assertReferenceNamespaces(hardlinkedTargets);
publish();
pendingTargets.clear();
return new Map([...files].map(([source, fact]) => [source, fact.sourceIdentity]));
}
};
}
//#endregion
//#region src/plugins/plugin-native-resolution.ts
function getPluginBunConditions(requireMode) {
const conditions = /* @__PURE__ */ new Set([
"bun",
"node",
"module-sync",
requireMode ? "require" : "import"
]);
if (!process.execArgv.includes("--no-addons")) conditions.add("node-addons");
for (let index = 0; index < process.execArgv.length; index += 1) {
const argument = process.execArgv[index];
if (argument === "--conditions") {
const condition = process.execArgv[index + 1];
if (condition && !condition.startsWith("-")) {
conditions.add(condition);
index += 1;
}
} else if (argument?.startsWith("--conditions=")) conditions.add(argument.slice(13));
}
return conditions;
}
function createPluginNativeImportPattern(imports) {
const importKeys = imports && typeof imports === "object" ? Object.keys(imports) : [];
return (specifier) => !importKeys.includes(specifier) && importKeys.some((key) => {
const star = key.indexOf("*");
return star >= 0 && star < key.length - 1 && key.lastIndexOf("*") === star && specifier.startsWith(key.slice(0, star)) && specifier.endsWith(key.slice(star + 1));
});
}
//#endregion
//#region src/plugins/plugin-source-references.ts
function capturedPluginModuleUrl(filename, specifier, conditions) {
const url = pathToFileURL(filename);
if (!conditions.includes("require") && (specifier.startsWith(".") || specifier.startsWith("file:") || path.isAbsolute(specifier))) {
const requested = new URL(specifier, url);
url.search = requested.search;
url.hash = requested.hash;
}
return url;
}
function isPackageMapError(error, code) {
return error instanceof Error && "code" in error && error.code === code;
}
/** Package metadata selects a target before its deferred body has been captured. */
function resolvePluginPackageMapTarget(specifier, importer, conditions) {
let selected;
try {
selected = moduleResolve(specifier, pathToFileURL(importer), new Set(conditions));
} catch (error) {
if (conditions.includes("bun") && isPackageMapError(error, "ERR_INVALID_PACKAGE_TARGET")) return;
if (!isPackageMapError(error, "ERR_MODULE_NOT_FOUND")) throw error;
if (typeof error.url !== "string") return;
selected = new URL(error.url);
}
return selected.protocol === "file:" ? selected : void 0;
}
/** Missing physical inputs stay absent without poisoning another condition's selected target. */
function createPluginPackageMapReferences() {
const missingTargets = /* @__PURE__ */ new Set();
const recordMissingTarget = (filename) => {
missingTargets.add(path.resolve(filename));
};
return {
recordMissingTarget,
hasMissingTarget: (filename) => missingTargets.has(path.resolve(filename)),
resolveReference(specifier, importer, conditions) {
try {
return moduleResolve(specifier, pathToFileURL(importer), new Set(conditions)).href;
} catch (error) {
if (isPackageMapError(error, "ERR_MODULE_NOT_FOUND") && typeof error.url === "string") {
const target = new URL(error.url);
if (target.protocol === "file:") recordMissingTarget(fileURLToPath(target));
}
return;
}
}
};
}
function staticString(node) {
if ((node?.type === "Literal" || node?.type === "StringLiteral") && typeof node.value === "string") return node.value;
if (node?.type === "TemplateLiteral" && node.expressions?.length === 0) return node.quasis?.[0]?.value.cooked ?? void 0;
}
/** Whether this expression receives a value, directly or through a destructuring pattern. */
function isAssignmentTarget(target) {
let child = target;
for (let parent = target.parentPath; parent?.node; parent = parent.parentPath) {
switch (parent.node.type) {
case "AssignmentExpression":
case "AssignmentPattern":
case "ForInStatement":
case "ForOfStatement": return child.key === "left";
case "UpdateExpression": return true;
case "ObjectProperty":
if (child.key !== "value") return false;
break;
case "ArrayPattern":
case "ObjectPattern":
case "RestElement": break;
default: return false;
}
child = parent;
}
return false;
}
function unwrapReferenceArgument(input) {
let argument = input;
while (argument?.isTSAsExpression() || argument?.isTSTypeAssertion() || argument?.isTSNonNullExpression() || argument?.isTSSatisfiesExpression() || argument?.isTSInstantiationExpression()) argument = argument.get("expression");
return argument;
}
/** Inspect authored TypeScript syntax before Jiti rewrites module operations. */
function inspectPluginTypeScriptExecutionFacts(source, sourceText, resolver) {
let hasComputedImport = false;
const staticImports = /* @__PURE__ */ new Map();
const recordStaticImport = (specifier, sideEffect) => {
staticImports.set(specifier, (staticImports.get(specifier) ?? false) || sideEffect);
};
resolver.transform({
source: sourceText,
filename: source,
ts: true,
async: true,
babel: { plugins: [{ pre(file) {
file.path.traverse({
"ImportDeclaration|ExportNamedDeclaration|ExportAllDeclaration"(declaration) {
const specifier = staticString(declaration.node.source);
if (specifier !== void 0) recordStaticImport(specifier, declaration.isImportDeclaration() && declaration.node.specifiers.length === 0);
},
ImportExpression(expression) {
if (staticString(expression.get("source").node) === void 0) hasComputedImport = true;
},
CallExpression(call) {
const args = call.get("arguments");
if (call.get("callee").node?.type === "Import" && staticString(unwrapReferenceArgument(args[0])?.node) === void 0) hasComputedImport = true;
}
});
} }] }
});
return {
hasComputedImport,
staticImports: [...staticImports].map(([specifier, sideEffect]) => ({
specifier,
sideEffect
}))
};
}
/** Read the native factory binding before Jiti rewrites modules and import.meta. */
function isCurrentFileRequire(call) {
const callee = call.get("callee");
const reference = callee.isMemberExpression() && !callee.node.computed && callee.get("property").isIdentifier({ name: "resolve" }) ? callee.get("object") : callee;
let init = reference;
if (reference.isIdentifier() && reference.node.name) {
const binding = reference.scope.getBinding(reference.node.name);
if (!binding?.constant || !binding.path.isVariableDeclarator()) return false;
init = binding.path.get("init");
}
if (!init.isCallExpression()) return false;
const args = init.get("arguments");
const anchor = args.length === 1 ? args[0] : void 0;
if (!anchor || !(anchor.isMemberExpression() && !anchor.node.computed && anchor.get("object").node?.type === "MetaProperty" && anchor.matchesPattern("import.meta.url") || anchor.node?.type === "Identifier" && anchor.node.name === "__filename" && !anchor.scope.getBinding("__filename"))) return false;
const factory = init.get("callee");
return ["module", "node:module"].some((source) => factory.referencesImport(source, "createRequire") || factory.isMemberExpression() && !factory.node.computed && factory.get("property").isIdentifier({ name: "createRequire" }) && factory.get("object").referencesImport(source, "default"));
}
const TRANSFORMED_REFERENCE_NAMES = /* @__PURE__ */ new Set([
"createRequire",
"URL",
"readFile",
"readFileSync",
"createReadStream",
"join",
"resolve",
"require",
"jitiImport",
"jitiESMResolve",
"__dirname"
]);
function* sourceChildren(node) {
for (const value of Object.values(node)) for (const child of Array.isArray(value) ? value : [value]) if (child && typeof child === "object" && "type" in child) yield child;
}
function parseNativePluginJavaScript(source, sourceText) {
if (!/\.[cm]?js$/.test(source)) return;
let tree;
try {
tree = parse$1(sourceText, {
ecmaVersion: "latest",
sourceType: "module",
allowAwaitOutsideFunction: true
});
} catch (error) {
if (error instanceof SyntaxError) return;
throw error;
}
const needsTransform = (node, exportedDeclaration = false) => {
if (node.type === "MetaProperty") return true;
if (node.type === "VariableDeclaration" && (node.kind === "using" || node.kind === "await using")) return true;
if ((node.type === "ForInStatement" || node.type === "ForOfStatement") && node.left.type !== "VariableDeclaration") return true;
const exported = node.type === "ExportSpecifier" || node.type === "ExportAllDeclaration" ? node.exported : void 0;
if ((exported?.type === "Identifier" ? exported.name : staticString(exported)) === "__esModule" || exportedDeclaration && node.type === "Identifier" && node.name === "__esModule") return true;
if (node.type === "Identifier" && (node.name === "require" || node.name === "__dirname")) return true;
if (node.type === "ImportExpression" && (node.source.type !== "Literal" || typeof node.source.value !== "string" || node.options != null)) return true;
if (node.type === "ImportDeclaration") {
if (node.source.value === "module" || node.source.value === "node:module") return true;
for (const specifier of node.specifiers) {
const imported = specifier.type === "ImportSpecifier" ? specifier.imported.type === "Identifier" ? specifier.imported.name : staticString(specifier.imported) : void 0;
if (TRANSFORMED_REFERENCE_NAMES.has(specifier.local.name) || imported !== void 0 && TRANSFORMED_REFERENCE_NAMES.has(imported)) return true;
}
}
for (const child of sourceChildren(node)) if (needsTransform(child, exportedDeclaration || node.type === "ExportNamedDeclaration" && child === node.declaration)) return true;
return false;
};
return needsTransform(tree) ? void 0 : tree;
}
function parseTransformedPluginSource(source, code) {
try {
return parse$1(code, {
ecmaVersion: "latest",
allowImportExportEverywhere: true,
allowAwaitOutsideFunction: true,
allowReturnOutsideFunction: true
});
} catch (error) {
if (!(error instanceof SyntaxError)) throw error;
throw new SyntaxError(`${source}: could not parse transformed source: ${error.message}`, { cause: error });
}
}
/** Visit literal module and explicit asset inputs without evaluating plugin code. */
function visitPluginSourceReferences(source, sourceText, resolver, visitReference) {
const authoredStaticImports = /* @__PURE__ */ new Set();
const visitDirectoryAsset = (name, parts) => {
if ((name === "join" || name === "resolve") && parts.length > 0 && parts.every((part) => part !== void 0) && (name === "join" || !parts.some((part) => path.isAbsolute(part)))) visitReference(path.join(".", ...parts), "asset");
};
const tree = parseNativePluginJavaScript(source, sourceText) ?? parseTransformedPluginSource(source, resolver.transform({
source: sourceText,
filename: source,
ts: /\.[cm]?tsx?$/.test(source),
async: true,
babel: { plugins: [{
pre(file) {
file.path.traverse({
"ImportDeclaration|ExportNamedDeclaration"(declaration) {
const node = declaration.node;
if (node.source && (node.type === "ImportDeclaration" ? node.importKind : node.exportKind) !== "type" && node.specifiers.length > 0 && node.specifiers.every((specifier) => specifier.type === "ImportSpecifier" && specifier.importKind === "type" || specifier.type === "ExportSpecifier" && specifier.exportKind === "type")) authoredStaticImports.add(node.source.value);
},
MemberExpression(member) {
if (member.get("object").node?.type === "MetaProperty" && isAssignmentTarget(member)) member.replaceWith({
type: "Identifier",
name: "importMetaTarget"
});
},
CallExpression(call) {
const args = call.get("arguments");
if (unwrapReferenceArgument(args[0])?.matchesPattern("import.meta.dirname")) {
const callee = call.get("callee");
const member = callee.isMemberExpression() ? callee.get("property") : callee;
const name = ["join", "resolve"].find((method) => ["path", "node:path"].some((moduleName) => callee.referencesImport(moduleName, method))) ?? (member.isIdentifier() ? member.node.name : void 0) ?? "";
visitDirectoryAsset(name, args.slice(1).map((arg) => staticString(unwrapReferenceArgument(arg)?.node)));
}
const specifier = staticString(unwrapReferenceArgument(args.length === 1 ? args[0] : void 0)?.node);
if (specifier !== void 0 && isCurrentFileRequire(call)) visitReference(specifier, "require");
}
});
},
visitor: { "ImportDeclaration|ExportNamedDeclaration|ExportAllDeclaration"(declaration) {
if (declaration.node.source) authoredStaticImports.add(declaration.node.source.value);
} }
}] }
}));
const staticImports = /* @__PURE__ */ new Set();
for (const statement of tree.body) if ((statement.type === "ImportDeclaration" || statement.type === "ExportNamedDeclaration" || statement.type === "ExportAllDeclaration") && statement.source) {
const reference = staticString(statement.source);
if (reference !== void 0) {
staticImports.add(reference);
authoredStaticImports.add(reference);
}
}
for (const reference of staticImports) visitReference(reference, "import");
const visit = (node) => {
if (node.type === "ImportExpression") {
const reference = staticString(node.source);
if (reference !== void 0) visitReference(reference, "import");
}
if (node.type === "CallExpression" || node.type === "NewExpression") {
const { callee: call, arguments: args } = node;
const callee = call.type === "SequenceExpression" ? call.expressions.at(-1) : call;
const member = callee.type === "MemberExpression" ? callee.property : callee;
const name = member.type === "Identifier" ? member.name : "";
const module = callee.type === "Identifier" ? [
"require",
"jitiImport",
"jitiESMResolve"
].includes(name) : callee.type === "MemberExpression" && callee.object.type === "Identifier" && callee.object.name === "require" && name === "resolve";
const asset = node.type === "NewExpression" && name === "URL" || [
"readFile",
"readFileSync",
"createReadStream"
].includes(name);
const reference = staticString(args[0]);
if ((module || asset) && reference !== void 0) visitReference(reference, module ? name === "require" || name === "resolve" ? "require" : "import" : "asset");
if (callee.type === "MemberExpression" && args[0]?.type === "Identifier" && args[0].name === "__dirname") visitDirectoryAsset(name, args.slice(1).map(staticString));
}
for (const child of sourceChildren(node)) visit(child);
};
visit(tree);
return authoredStaticImports;
}
//#endregion
//#region src/plugins/plugin-generation-artifact.ts
/** Capture selective entries and whole dependencies without replacing earlier file bytes. */
const capturePluginGenerationArtifact = createPluginGenerationCapture(createPluginGenerationArtifact);
function createPluginGenerationArtifact(rootDir, entryFile, execute, moduleSource, nativeRecovery, dependencyLookupBoundary, retained, captureForCustody = false) {
const sourceCapture = retained?.source.sourceCapture ?? createPluginSourceCapture();
const directory = sourceCapture.directory;
const packages = /* @__PURE__ */ new Map();
const capturedPaths = /* @__PURE__ */ new Map();
const originalSources = /* @__PURE__ */ new Map();
const hardlinkedSources = /* @__PURE__ */ new Set();
const nativeAdmission = createPluginNativeAdmission(rootDir, directory, entryFile, nativeRecovery, sourceCapture.outputRoot);
const metadataCapture = createPluginPackageMetadataCapture({
sourceForCaptured: (filename) => originalSources.get(filename),
packageForFile: (filename) => packageForFile(filename),
isRetainedReference: nativeAdmission.isRetainedReference,
resolveSource: nativeAdmission.resolvePreparedSource
});
const sourceAliases = {};
const sourceFacts = createPluginSourceFacts(rootDir, entryFile, dependencyLookupBoundary, captureForCustody);
const receipt = createPluginGenerationReceipt(nativeAdmission.prepared);
const { inputs, pendingInputs, additions, capture: acquireSources, assertModuleAvailable } = sourceCapture;
const captureAdmitted = (run) => {
const acquired = execute ? execute(() => acquireSources(run)) : acquireSources(run);
nativeAdmission.finish(receipt.finish());
return acquired;
};
const moduleCaptures = /* @__PURE__ */ new Map();
const resolution = createPluginCaptureResolver();
const resolveDependency = sourceFacts.resolveDependency;
const copyPackage = (root, entry, metadataOnly = false, executableEntry = false) => {
const boundary = root;
const outputRoot = sourceCapture.outputRoot && isPathInside(boundary, sourceCapture.outputRoot) ? sourceCapture.outputRoot : void 0;
const existing = packages.get(root);
if (existing) {
if (!metadataOnly) existing.materialize(executableEntry ? entry : void 0);
return existing.destination;
}
const packageMap = createPluginPackageMapReferences();
const packageId = `package-${packages.size}`;
const moduleRoot = path.join(directory, packageId, "node_modules");
const parentName = path.basename(path.dirname(boundary));
const sourceModuleRoot = parentName.startsWith("@") ? path.dirname(path.dirname(boundary)) : path.dirname(boundary);
const destination = path.join(moduleRoot, parentName.startsWith("@") ? parentName : "", path.basename(boundary));
sourceAliases[root] = destination;
receipt.marker(`${packageId}\0`);
const owner = {
destination,
capturedRoot: destination,
sourceRoot: boundary,
links: /* @__PURE__ */ new Set(),
state: "metadata",
captureTarget(filename) {
const source = path.join(boundary, path.relative(destination, filename));
if (!capturedPaths.has(source) && !packageMap.hasMissingTarget(source) && fs.statSync(source, { throwIfNoEntry: false })?.isFile() && (isPathInside(boundary, fs.realpathSync(source)) || nativeAdmission.isRetainedReference(source))) {
copy(source, filename);
scopes.captureMetadata(path.dirname(source));
}
},
materialize(selectedEntry) {
if (typeof owner.state === "object") throw owner.state.error;
if (owner.state === "body" && !selectedEntry) return;
owner.state = selectedEntry && owner.state !== "body" ? "entry" : "body";
try {
if (selectedEntry) captureFile(path.resolve(selectedEntry));
else copy(root, destination);
captureDependencies();
} catch (error) {
owner.state = { error };
throw error;
}
}
};
packages.set(root, owner);
const sourceLinks = createPluginSourceLinkCapture();
const copy = createPluginGenerationFileCapture({
boundary,
destination,
directory,
outputRoot,
capturedPaths,
originalSources,
hardlinkedSources,
sourceCapture,
sourceLinks,
deferExternalLinks: Boolean(execute),
nativeAdmission,
receipt,
retained,
sourceFacts: sourceFacts.files,
onPackageMetadata(source, target) {
metadataCapture.record(target, (manifest) => {
for (const alias of importTargetNames(manifest.imports)) {
if (alias === "openclaw" || alias === "@openclaw/plugin-sdk") continue;
const dependency = resolveDependency(alias, source);
if (dependency) linkDependency(alias, dependency, true);
}
});
}
});
const linkDependency = (name, dependency, captureMetadataOnly = false) => {
const captured = copyPackage(dependency.root, void 0, captureMetadataOnly);
const lookupDirectory = isPluginPackageFile(boundary, dependency.lookupDirectory) ? path.join(destination, path.relative(boundary, dependency.lookupDirectory)) : path.join(dependency.lookupDirectory, "node_modules") === sourceModuleRoot ? path.dirname(moduleRoot) : destination;
const link = path.join(lookupDirectory, "node_modules", name);
const dependencyPackage = packages.get(dependency.root);
dependencyPackage.links.add(link);
try {
if (linkPluginGenerationDependency(link, captured)) additions.add(link);
} catch (cause) {
const error = new PluginDependencyLinkError(`Cannot prepare dependency ${name} for plugin ${rootDir}; check filesystem permissions and retry.`, { cause });
dependencyPackage.state = { error };
additions.add(link);
throw error;
}
};
const scopes = metadataCapture.createScope({
root,
destination,
boundary,
copy,
hasSource: (source) => capturedPaths.has(source),
recordMissingMetadata: sourceFacts.recordMissingFile
});
const references = /* @__PURE__ */ new Map();
const getNativeScope = createPluginNativeDependencyScopes(resolveDependency, (name, dependency) => linkDependency(name, dependency, true));
const scannedDirectories = /* @__PURE__ */ new Set();
const captureFile = (source) => {
const existingSource = capturedPaths.get(path.resolve(source));
if (existingSource) assertModuleAvailable(existingSource);
if (existingSource && (!/\.[cm]?[jt]sx?$/.test(source) || moduleCaptures.has(existingSource))) return;
const target = existingSource ?? path.join(destination, path.relative(root, source));
if (!existingSource) {
const prepared = nativeAdmission.resolvePreparedSource(source);
const real = fs.realpathSync(prepared?.path ?? source);
if (!isPathInside(prepared?.boundary ?? boundary, real) && !nativeAdmission.isRetainedReference(source, real)) throw new Error("Standalone plugin input leaves its source directory");
if (!prepared && outputRoot && isPathInside(outputRoot, real)) return;
if (fs.statSync(real).isDirectory()) {
sourceFacts.recordDirectory(source);
if (scannedDirectories.has(real)) throw new Error("Standalone plugin input contains a directory cycle");
scannedDirectories.add(real);
for (const name of fs.readdirSync(real).toSorted()) if (isPluginSourceEntry(name)) captureFile(path.join(source, name));
scannedDirectories.delete(real);
return;
}
copy(source, target);
}
if (!/\.[cm]?[jt]sx?$/.test(source)) return;
const scope = scopes.resolve(path.dirname(source));
const prepareDependency = createPluginDependencyLookup(source, scope?.manifest, resolveDependency, linkDependency);
const resolver = resolution.get(source);
const captureReference = (reference, kind, conditions) => {
const module = kind !== "asset";
const importUrl = kind === "import" && reference.startsWith(".") ? new URL(reference, pathToFileURL(source)) : void 0;
const value = importUrl ? `./${path.relative(path.dirname(source), fileURLToPath(importUrl))}` : module && reference.startsWith("file:") ? fileURLToPath(reference) : reference;
const resolve = (specifier) => {
const resolved = resolution.resolve(source, specifier, conditions ?? [
"node",
"module-sync",
kind === "require" ? "require" : "import"
]);
if (!resolved?.startsWith("file:")) return resolved;
const url = new URL(resolved);
const filename = fileURLToPath(url);
const captured = moduleSource?.(filename) ?? filename;
url.pathname = pathToFileURL(originalSources.get(captured) ?? nativeAdmission.sourceForPrepared(captured) ?? captured).pathname;
return url.href;
};
const addDependency = (name, importer = source) => {
const imports = references.get(importer) ?? /* @__PURE__ */ new Set();
imports.add(name);
references.set(importer, imports);
};
if (module && !value.startsWith(".") && !path.isAbsolute(value)) {
if (isBuiltin(value)) return;
const name = packageName(value);
const self = scope?.manifest.exports != null && scope.manifest.name === name;
const resolved = value.startsWith("#") || self ? packageMap.resolveReference(value, source, conditions ?? [
"node",
"module-sync",
kind
]) : resolve(value);
const input = resolved?.startsWith("file:") ? fileURLToPath(resolved) : resolved;
if (resolver.options.tsconfigPaths && name !== "openclaw" && name !== "@openclaw/plugin-sdk" && resolved?.startsWith("file:") && input) {
if (isPluginPackageFile(boundary, input) && (capturedPaths.has(path.resolve(input)) || isPluginPackageFile(boundary, fs.realpathSync(input)))) {
captureFile(input);
return input;
}
if (!isPathInside(resolveDependency(name, source)?.root ?? boundary, input)) return conditions && execute ? captureExecutableFile(input) : null;
}
if (!value.startsWith("#") && !self) {
if (conditions && !resolved) return;
addDependency(name);
return resolved?.startsWith("file:") ? input : void 0;
}
if (!input || isBuiltin(input)) return;
let external = false;
if (!self && scope) for (const alias of scope.aliases) {
const dependency = resolveDependency(alias, scope.source);
if (dependency && isPathInside(dependency.root, input)) {
if (conditions || !execute) addDependency(alias, scope.source);
external = true;
}
}
if (!external) captureFile(input);
return input;
}
const requested = path.resolve(path.dirname(source), value);
const lexicalBoundary = entry && !executableEntry ? path.resolve(rootDir) : root;
const local = module && path.isAbsolute(value) && isPathInside(lexicalBoundary, requested) ? path.join(boundary, path.relative(lexicalBoundary, requested)) : requested;
if (module && !isPathInside(boundary, local)) {
if (!conditions || !execute) return null;
const selected = resolve(local);
if (!selected?.startsWith("file:")) return;
return captureExecutableFile(fileURLToPath(selected));
}
if (!value || !module && path.isAbsolute(value) || !isPathInside(boundary, local) || local === boundary) return;
if (module && path.isAbsolute(value) && path.relative(boundary, local).split(path.sep).includes("node_modules")) return;
const fromCopy = owner.state === "body" && !sourceLinks.contains(local);
const moduleRequest = nativeAdmission.resolvePreparedSource(local)?.path ?? (fromCopy ? path.join(destination, path.relative(root, local)) : local);
const resolved = module ? resolve(moduleRequest) : void 0;
if (module && !resolved) return;
const input = resolved?.startsWith("file:") ? fileURLToPath(resolved) : resolved ?? local;
const capturedInput = capturedPaths.has(path.resolve(input));
const preparedInput = nativeAdmission.resolvePreparedSource(input);
if (capturedInput || fs.existsSync(preparedInput?.path ?? input)) {
if (module && execute && !capturedInput && !preparedInput && !isPathInside(boundary, fs.realpathSync(input))) return conditions ? captureExecutableFile(input) : null;
captureFile(input);
if (module && path.isAbsolute(value)) capturedPaths.set(requested, capturedPaths.get(path.resolve(input)));
return input;
}
if (!module) sourceFacts.recordMissingFile(local);
};
const observed = /* @__PURE__ */ new Map();
const captureObservedReference = (reference, kind, conditions) => {
const key = `${kind}\0${reference}`;
if (!observed.has(key) || conditions && execute && observed.get(key) === null) {
observed.set(key, captureReference(reference, kind, conditions));
if (kind !== "asset" && observed.get(key) !== null) sourceFacts.recordModuleLookup(source, reference, resolution, conditions ?? [
"node",
"module-sync",
kind
]);
}
return observed.get(key) ?? void 0;
};
const captureModule = (specifier, conditions) => {
const inputFilename = specifier.startsWith("file:") ? fileURLToPath(specifier) : path.isAbsolute(specifier) ? specifier : void 0;
const known = inputFilename && capturedPaths.get(path.resolve(inputFilename));
if (execute && known) {
assertModuleAvailable(known);
return { target: capturedPluginModuleUrl(known, specifier, conditions) };
}
const name = packageName(specifier);
const self = scope?.manifest.exports != null && scope.manifest.name === name;
const bare = !specifier.startsWith(".") && !path.isAbsolute(specifier) && !specifier.startsWith("file:");
if (resolver.options.tsconfigPaths && bare && !self && !specifier.startsWith("#")) {
const mapped = captureObservedReference(specifier, conditions.includes("require") ? "require" : "import", conditions);
const captured = mapped && capturedPaths.get(path.resolve(mapped));
if (captured) {
captureDependencies();
assertModuleAvailable(captured);
return { target: pathToFileURL(captured) };
}
}
const dependencyPrepared = prepareDependency(specifier);
if (typeof dependencyPrepared === "boolean") return dependencyPrepared ? { retryNative: true } : void 0;
if (dependencyPrepared === "package-map") {
const selected = resolvePluginPackageMapTarget(specifier, target, conditions);
if (!selected) return;
const filename = fileURLToPath(selected);
if (isPluginPackageFile(destination, filename)) {
const original = path.join(boundary, path.relative(destination, filename));
if (packageMap.hasMissingTarget(original)) return;
if (!capturedPaths.has(original) && !fs.existsSync(original)) {
packageMap.recordMissingTarget(original);
return;
}
captureFile(original);
} else packageForFile(filename)?.materialize();
captureDependencies();
return { retryNative: true };
}
const observedSource = captureObservedReference(specifier, conditions.includes("require") ? "require" : "import", conditions);
captureDependencies();
const captured = observedSource ? capturedPaths.get(path.resolve(observedSource)) : void 0;
if (!captured) return;
assertModuleAvailable(captured);
return { target: capturedPluginModuleUrl(captured, specifier, conditions) };
};
const moduleCapture = {
isNativeImportPattern: createPluginNativeImportPattern(scope?.manifest.imports),
isRequireReference: (specifier) => observed.has(`require\0${specifier}`) && !observed.has(`import\0${specifier}`),
prepareDependency,
nativeScope: getNativeScope(source, scope?.manifest),
capture: captureModule
};
moduleCaptures.set(target, moduleCapture);
if (entry && !executableEntry || process.versions.bun) moduleCapture.staticImports = visitPluginSourceReferences(source, fs.readFileSync(target, "utf8"), resolver, entry && !executableEntry ? captureObservedReference : (reference, kind) => observed.set(`${kind}\0${reference}`, null));
};
const captureDependencies = () => {
const manifestPath = path.join(root, "package.json");
if (!entry && !capturedPaths.has(manifestPath)) return;
const manifest = capturePluginDependencies({
root,
manifestFile: entry ? void 0 : path.join(destination, "package.json"),
references,
resolve: resolveDependency,
capture: linkDependency
});
if (!entry) metadataCapture.setManifest(path.join(destination, "package.json"), manifest);
};
if (metadataOnly) {
const manifest = capturePluginPackageMetadata(root, destination, copy, nativeAdmission.isRetainedReference, nativeAdmission.resolvePreparedSource, sourceFacts.recordFileProbe);
metadataCapture.setManifest(path.join(destination, "package.json"), manifest ?? null);
} else owner.materialize(entry);
return destination;
};
const captureExecutableFile = (filename) => execute?.(() => capturePluginModuleSource(filename, (root, source) => copyPackage(root, source, false, true)));
const packageForFile = (filename) => findPluginCapturedPackage(packages, filename, directory)?.owner;
try {
const sourceRoot = fs.realpathSync(rootDir);
const entry = entryFile ? fs.realpathSync(entryFile) : void 0;
const root = copyPackage(sourceRoot, entry);
sourceAliases[path.resolve(rootDir)] = root;
if (entry && entryFile) {
const alias = path.join(sourceRoot, path.relative(path.resolve(rootDir), path.resolve(entryFile)));
capturedPaths.set(alias, capturedPaths.get(entry));
}
const assertSourceCurrent = () => {
assertPluginSourceRootCurrent({
rootDir,
sourceRoot,
entryFile,
entry
});
nativeAdmission.reconcileSourceInputs(inputs);
verifyPluginSourceInputs(inputs, inputs.keys());
};
const initialReceipt = receipt.finish();
assertSourceCurrent();
nativeAdmission.finish(initialReceipt);
pendingInputs.clear();
additions.clear();
const captures = [
moduleCaptures,
hardlinkedSources,
metadataCapture,
packages,
resolution
];
const clearCaptures = () => captures.forEach((capture) => capture.clear());
const sourceLookup = createPluginGenerationSourceLookup({
rootDir,
sourceRoot,
capturedRoot: root,
boundaryRoot: directory,
capturedPaths,
hardlinkedSources,
assertModuleAvailable,
captureNativeRecovery: () => nativeAdmission.captureRecovery(initialReceipt, sourceAliases)
});
return {
sourceRoot,
rootDir: root,
sourceAliases,
linkHost: (hostRoot) => {
sourceCapture.linkHost(hostRoot);
for (const [source, identity] of nativeAdmission.linkHost(hostRoot)) {
const input = inputs.get(source);
if (input?.native) input.identity = identity;
}
nativeAdmission.reconcileSourceInputs(inputs);
},
sourceDigest: initialReceipt.sourceDigest,
...sourceLookup,
retainSourceCustody: () => sourceFacts.captureCustody({
sourceRoot,
entry,
sourceDigest: initialReceipt.sourceDigest,
capture: sourceLookup.captureRecoverySource
}),
assertSourceCurrent,
...createPluginGenerationModuleLookup({
capturedPaths,
originalSources,
moduleCaptures,
metadataCapture,
assertModuleAvailable,
captureAdmitted,
captureExecutableFile,
executable: Boolean(execute),
packages,
directory
}),
dispose: () => {
(retained?.source ?? sourceCapture).dispose();
clearCaptures();
},
disposeAsync: () => (retained?.source ?? sourceCapture).disposeAsync().then(clearCaptures)
};
} catch (error) {
(retained?.source ?? sourceCapture).dispose();
throw error;
}
}
//#endregion
export { getPluginBunConditions as a, visitPluginSourceReferences as i, inspectPluginTypeScriptExecutionFacts as n, resolvePluginPackageMapTarget as r, capturePluginGenerationArtifact as t };