mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-20 13:41:30 +00:00
fix(plugin-sdk): add bundled entry error context
(cherry picked from commit 6099405ba1e8b98caa92cce4487808d212dc3544)
This commit is contained in:
45
src/plugin-sdk/channel-entry-contract.test.ts
Normal file
45
src/plugin-sdk/channel-entry-contract.test.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { loadBundledEntryExportSync } from "./channel-entry-contract.js";
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
afterEach(() => {
|
||||
for (const dir of tempDirs.splice(0)) {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
describe("loadBundledEntryExportSync", () => {
|
||||
it("includes importer and resolved path context when a bundled sidecar is missing", () => {
|
||||
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-channel-entry-contract-"));
|
||||
tempDirs.push(tempRoot);
|
||||
|
||||
const pluginRoot = path.join(tempRoot, "dist", "extensions", "telegram");
|
||||
fs.mkdirSync(pluginRoot, { recursive: true });
|
||||
|
||||
const importerPath = path.join(pluginRoot, "index.js");
|
||||
fs.writeFileSync(importerPath, "export default {};\n", "utf8");
|
||||
|
||||
let thrown: unknown;
|
||||
try {
|
||||
loadBundledEntryExportSync(pathToFileURL(importerPath).href, {
|
||||
specifier: "./src/secret-contract.js",
|
||||
});
|
||||
} catch (error) {
|
||||
thrown = error;
|
||||
}
|
||||
|
||||
expect(thrown).toBeInstanceOf(Error);
|
||||
const message = (thrown as Error).message;
|
||||
expect(message).toContain('bundled plugin entry "./src/secret-contract.js" failed to open');
|
||||
expect(message).toContain(`from "${importerPath}"`);
|
||||
expect(message).toContain(`resolved "${path.join(pluginRoot, "src", "secret-contract.js")}"`);
|
||||
expect(message).toContain(`plugin root "${pluginRoot}"`);
|
||||
expect(message).toContain('reason "path"');
|
||||
expect(message).toContain("ENOENT");
|
||||
});
|
||||
});
|
||||
@@ -85,6 +85,40 @@ function resolveEntryBoundaryRoot(importMetaUrl: string): string {
|
||||
return path.dirname(fileURLToPath(importMetaUrl));
|
||||
}
|
||||
|
||||
function formatBundledEntryUnknownError(error: unknown): string {
|
||||
if (typeof error === "string") {
|
||||
return error;
|
||||
}
|
||||
if (error === undefined) {
|
||||
return "boundary validation failed";
|
||||
}
|
||||
try {
|
||||
return JSON.stringify(error);
|
||||
} catch {
|
||||
return "non-serializable error";
|
||||
}
|
||||
}
|
||||
|
||||
function formatBundledEntryModuleOpenFailure(params: {
|
||||
importMetaUrl: string;
|
||||
specifier: string;
|
||||
resolvedPath: string;
|
||||
boundaryRoot: string;
|
||||
failure: Extract<ReturnType<typeof openBoundaryFileSync>, { ok: false }>;
|
||||
}): string {
|
||||
const importerPath = fileURLToPath(params.importMetaUrl);
|
||||
const errorDetail =
|
||||
params.failure.error instanceof Error
|
||||
? params.failure.error.message
|
||||
: formatBundledEntryUnknownError(params.failure.error);
|
||||
return [
|
||||
`bundled plugin entry "${params.specifier}" failed to open`,
|
||||
`from "${importerPath}"`,
|
||||
`(resolved "${params.resolvedPath}", plugin root "${params.boundaryRoot}",`,
|
||||
`reason "${params.failure.reason}"): ${errorDetail}`,
|
||||
].join(" ");
|
||||
}
|
||||
|
||||
function resolveBundledEntryModulePath(importMetaUrl: string, specifier: string): string {
|
||||
const importerPath = fileURLToPath(importMetaUrl);
|
||||
const resolved = path.resolve(path.dirname(importerPath), specifier);
|
||||
@@ -99,7 +133,15 @@ function resolveBundledEntryModulePath(importMetaUrl: string, specifier: string)
|
||||
skipLexicalRootCheck: true,
|
||||
});
|
||||
if (!opened.ok) {
|
||||
throw new Error(`plugin entry path escapes plugin root: ${specifier}`);
|
||||
throw new Error(
|
||||
formatBundledEntryModuleOpenFailure({
|
||||
importMetaUrl,
|
||||
specifier,
|
||||
resolvedPath: candidate,
|
||||
boundaryRoot,
|
||||
failure: opened,
|
||||
}),
|
||||
);
|
||||
}
|
||||
fs.closeSync(opened.fd);
|
||||
return opened.path;
|
||||
|
||||
Reference in New Issue
Block a user