mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-28 09:33:06 +00:00
fix(channels): harden bundled runtime sidecar resolution
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { resolveOpenClawPackageRootSync } from "../../infra/openclaw-root.js";
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
import type {
|
||||
BundledChannelEntryContract,
|
||||
@@ -21,7 +21,12 @@ type GeneratedBundledChannelEntry = {
|
||||
};
|
||||
|
||||
const log = createSubsystemLogger("channels");
|
||||
const OPENCLAW_PACKAGE_ROOT = path.resolve(fileURLToPath(new URL("../../..", import.meta.url)));
|
||||
const OPENCLAW_PACKAGE_ROOT =
|
||||
resolveOpenClawPackageRootSync({
|
||||
cwd: process.cwd(),
|
||||
moduleUrl: import.meta.url,
|
||||
argv1: process.argv[1],
|
||||
}) ?? process.cwd();
|
||||
|
||||
function resolveChannelPluginModuleEntry(
|
||||
moduleExport: unknown,
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { PluginRuntime } from "../plugins/runtime/types.js";
|
||||
import {
|
||||
buildPluginLoaderAliasMap,
|
||||
buildPluginLoaderJitiOptions,
|
||||
resolveLoaderPackageRoot,
|
||||
shouldPreferNativeJiti,
|
||||
} from "../plugins/sdk-alias.js";
|
||||
import type { AnyAgentTool, OpenClawPluginApi, PluginCommandContext } from "../plugins/types.js";
|
||||
@@ -85,6 +86,82 @@ function resolveEntryBoundaryRoot(importMetaUrl: string): string {
|
||||
return path.dirname(fileURLToPath(importMetaUrl));
|
||||
}
|
||||
|
||||
type BundledEntryModuleCandidate = {
|
||||
path: string;
|
||||
boundaryRoot: string;
|
||||
};
|
||||
|
||||
function addBundledEntryCandidates(
|
||||
candidates: BundledEntryModuleCandidate[],
|
||||
basePath: string,
|
||||
boundaryRoot: string,
|
||||
): void {
|
||||
for (const candidate of resolveSpecifierCandidates(basePath)) {
|
||||
if (
|
||||
candidates.some((entry) => entry.path === candidate && entry.boundaryRoot === boundaryRoot)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
candidates.push({ path: candidate, boundaryRoot });
|
||||
}
|
||||
}
|
||||
|
||||
function resolveBundledEntryModuleCandidates(
|
||||
importMetaUrl: string,
|
||||
specifier: string,
|
||||
): BundledEntryModuleCandidate[] {
|
||||
const importerPath = fileURLToPath(importMetaUrl);
|
||||
const importerDir = path.dirname(importerPath);
|
||||
const boundaryRoot = resolveEntryBoundaryRoot(importMetaUrl);
|
||||
const candidates: BundledEntryModuleCandidate[] = [];
|
||||
const primaryResolved = path.resolve(importerDir, specifier);
|
||||
addBundledEntryCandidates(candidates, primaryResolved, boundaryRoot);
|
||||
|
||||
const sourceRelativeSpecifier = specifier.replace(/^\.\/src\//u, "./");
|
||||
if (sourceRelativeSpecifier !== specifier) {
|
||||
addBundledEntryCandidates(
|
||||
candidates,
|
||||
path.resolve(importerDir, sourceRelativeSpecifier),
|
||||
boundaryRoot,
|
||||
);
|
||||
}
|
||||
|
||||
const packageRoot = resolveLoaderPackageRoot({
|
||||
modulePath: importerPath,
|
||||
moduleUrl: importMetaUrl,
|
||||
cwd: importerDir,
|
||||
argv1: process.argv[1],
|
||||
});
|
||||
if (!packageRoot) {
|
||||
return candidates;
|
||||
}
|
||||
|
||||
const distExtensionsRoot = path.join(packageRoot, "dist", "extensions") + path.sep;
|
||||
if (!importerPath.startsWith(distExtensionsRoot)) {
|
||||
return candidates;
|
||||
}
|
||||
|
||||
const pluginDirName = path.basename(importerDir);
|
||||
const sourcePluginRoot = path.join(packageRoot, "extensions", pluginDirName);
|
||||
if (sourcePluginRoot === boundaryRoot) {
|
||||
return candidates;
|
||||
}
|
||||
|
||||
addBundledEntryCandidates(
|
||||
candidates,
|
||||
path.resolve(sourcePluginRoot, specifier),
|
||||
sourcePluginRoot,
|
||||
);
|
||||
if (sourceRelativeSpecifier !== specifier) {
|
||||
addBundledEntryCandidates(
|
||||
candidates,
|
||||
path.resolve(sourcePluginRoot, sourceRelativeSpecifier),
|
||||
sourcePluginRoot,
|
||||
);
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
function formatBundledEntryUnknownError(error: unknown): string {
|
||||
if (typeof error === "string") {
|
||||
return error;
|
||||
@@ -120,31 +197,58 @@ function formatBundledEntryModuleOpenFailure(params: {
|
||||
}
|
||||
|
||||
function resolveBundledEntryModulePath(importMetaUrl: string, specifier: string): string {
|
||||
const importerPath = fileURLToPath(importMetaUrl);
|
||||
const resolved = path.resolve(path.dirname(importerPath), specifier);
|
||||
const boundaryRoot = resolveEntryBoundaryRoot(importMetaUrl);
|
||||
const candidate =
|
||||
resolveSpecifierCandidates(resolved).find((entry) => fs.existsSync(entry)) ?? resolved;
|
||||
const opened = openBoundaryFileSync({
|
||||
absolutePath: candidate,
|
||||
rootPath: boundaryRoot,
|
||||
boundaryLabel: "plugin root",
|
||||
rejectHardlinks: false,
|
||||
skipLexicalRootCheck: true,
|
||||
});
|
||||
if (!opened.ok) {
|
||||
const candidates = resolveBundledEntryModuleCandidates(importMetaUrl, specifier);
|
||||
const fallbackCandidate = candidates[0] ?? {
|
||||
path: path.resolve(path.dirname(fileURLToPath(importMetaUrl)), specifier),
|
||||
boundaryRoot: resolveEntryBoundaryRoot(importMetaUrl),
|
||||
};
|
||||
|
||||
let firstFailure: {
|
||||
candidate: BundledEntryModuleCandidate;
|
||||
failure: Extract<ReturnType<typeof openBoundaryFileSync>, { ok: false }>;
|
||||
} | null = null;
|
||||
|
||||
for (const candidate of candidates) {
|
||||
const opened = openBoundaryFileSync({
|
||||
absolutePath: candidate.path,
|
||||
rootPath: candidate.boundaryRoot,
|
||||
boundaryLabel: "plugin root",
|
||||
rejectHardlinks: false,
|
||||
skipLexicalRootCheck: true,
|
||||
});
|
||||
if (opened.ok) {
|
||||
fs.closeSync(opened.fd);
|
||||
return opened.path;
|
||||
}
|
||||
firstFailure ??= { candidate, failure: opened };
|
||||
}
|
||||
|
||||
const failure = firstFailure;
|
||||
if (!failure) {
|
||||
throw new Error(
|
||||
formatBundledEntryModuleOpenFailure({
|
||||
importMetaUrl,
|
||||
specifier,
|
||||
resolvedPath: candidate,
|
||||
boundaryRoot,
|
||||
failure: opened,
|
||||
resolvedPath: fallbackCandidate.path,
|
||||
boundaryRoot: fallbackCandidate.boundaryRoot,
|
||||
failure: {
|
||||
ok: false,
|
||||
reason: "path",
|
||||
error: new Error(`ENOENT: no such file or directory, lstat '${fallbackCandidate.path}'`),
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
fs.closeSync(opened.fd);
|
||||
return opened.path;
|
||||
|
||||
throw new Error(
|
||||
formatBundledEntryModuleOpenFailure({
|
||||
importMetaUrl,
|
||||
specifier,
|
||||
resolvedPath: failure.candidate.path,
|
||||
boundaryRoot: failure.candidate.boundaryRoot,
|
||||
failure: failure.failure,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function getJiti(modulePath: string) {
|
||||
|
||||
Reference in New Issue
Block a user