fix(plugins): share source public surface resolver

This commit is contained in:
Vincent Koc
2026-04-14 17:33:15 +01:00
parent f12d6bf3bb
commit 9843a4f1fc
4 changed files with 70 additions and 66 deletions

View File

@@ -10,8 +10,7 @@ import {
type PluginJitiLoaderFactory,
} from "../plugins/jiti-loader-cache.js";
import {
PUBLIC_SURFACE_SOURCE_EXTENSIONS,
normalizeBundledPluginArtifactSubpath,
resolveBundledPluginSourcePublicSurfacePath,
resolveBundledPluginPublicSurfacePath,
} from "../plugins/public-surface-runtime.js";
import { resolveLoaderPackageRoot } from "../plugins/sdk-alias.js";
@@ -58,24 +57,6 @@ function createFacadeResolutionKey(params: { dirName: string; artifactBasename:
return `${params.dirName}::${params.artifactBasename}::${bundledPluginsDir ? path.resolve(bundledPluginsDir) : "<default>"}`;
}
function resolveSourceFirstPublicSurfacePath(params: {
bundledPluginsDir?: string;
dirName: string;
artifactBasename: string;
}): string | null {
const artifactBasename = normalizeBundledPluginArtifactSubpath(params.artifactBasename);
const sourceBaseName = artifactBasename.replace(/\.js$/u, "");
const sourceRoot =
params.bundledPluginsDir ?? path.resolve(getOpenClawPackageRoot(), "extensions");
for (const ext of PUBLIC_SURFACE_SOURCE_EXTENSIONS) {
const candidate = path.resolve(sourceRoot, params.dirName, `${sourceBaseName}${ext}`);
if (fs.existsSync(candidate)) {
return candidate;
}
}
return null;
}
function resolveFacadeModuleLocationUncached(params: {
dirName: string;
artifactBasename: string;
@@ -84,11 +65,10 @@ function resolveFacadeModuleLocationUncached(params: {
const preferSource = !CURRENT_MODULE_PATH.includes(`${path.sep}dist${path.sep}`);
if (preferSource) {
const modulePath =
resolveSourceFirstPublicSurfacePath({
resolveBundledPluginSourcePublicSurfacePath({
...params,
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
sourceRoot: bundledPluginsDir ?? path.resolve(getOpenClawPackageRoot(), "extensions"),
}) ??
resolveSourceFirstPublicSurfacePath(params) ??
resolveBundledPluginPublicSurfacePath({
rootDir: getOpenClawPackageRoot(),
...(bundledPluginsDir ? { bundledPluginsDir } : {}),

View File

@@ -7,6 +7,7 @@ import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
import {
PUBLIC_SURFACE_SOURCE_EXTENSIONS,
normalizeBundledPluginArtifactSubpath,
resolveBundledPluginSourcePublicSurfacePath,
resolveBundledPluginPublicSurfacePath,
} from "../plugins/public-surface-runtime.js";
import { resolveLoaderPackageRoot } from "../plugins/sdk-alias.js";
@@ -42,23 +43,6 @@ function createFacadeResolutionKey(params: { dirName: string; artifactBasename:
return `${params.dirName}::${params.artifactBasename}::${bundledPluginsDir ? path.resolve(bundledPluginsDir) : "<default>"}`;
}
function resolveSourceFirstPublicSurfacePath(params: {
bundledPluginsDir?: string;
dirName: string;
artifactBasename: string;
}): string | null {
const artifactBasename = normalizeBundledPluginArtifactSubpath(params.artifactBasename);
const sourceBaseName = artifactBasename.replace(/\.js$/u, "");
const sourceRoot = params.bundledPluginsDir ?? path.resolve(OPENCLAW_PACKAGE_ROOT, "extensions");
for (const ext of PUBLIC_SURFACE_SOURCE_EXTENSIONS) {
const candidate = path.resolve(sourceRoot, params.dirName, `${sourceBaseName}${ext}`);
if (fs.existsSync(candidate)) {
return candidate;
}
}
return null;
}
function resolveRegistryPluginModuleLocationFromRegistry(params: {
registry: readonly Pick<PluginManifestRecord, "id" | "rootDir" | "channels">[];
dirName: string;
@@ -108,11 +92,10 @@ function resolveFacadeModuleLocationUncached(params: {
const preferSource = !CURRENT_MODULE_PATH.includes(`${path.sep}dist${path.sep}`);
if (preferSource) {
const modulePath =
resolveSourceFirstPublicSurfacePath({
resolveBundledPluginSourcePublicSurfacePath({
...params,
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
sourceRoot: bundledPluginsDir ?? path.resolve(OPENCLAW_PACKAGE_ROOT, "extensions"),
}) ??
resolveSourceFirstPublicSurfacePath(params) ??
resolveBundledPluginPublicSurfacePath({
rootDir: OPENCLAW_PACKAGE_ROOT,
...(bundledPluginsDir ? { bundledPluginsDir } : {}),

View File

@@ -1,9 +1,27 @@
import { describe, expect, it } from "vitest";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import {
PUBLIC_SURFACE_SOURCE_EXTENSIONS,
normalizeBundledPluginArtifactSubpath,
resolveBundledPluginSourcePublicSurfacePath,
} from "./public-surface-runtime.js";
const tempDirs: string[] = [];
afterEach(() => {
for (const tempDir of tempDirs.splice(0)) {
fs.rmSync(tempDir, { recursive: true, force: true });
}
});
function createTempDir(): string {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-public-surface-runtime-"));
tempDirs.push(tempDir);
return tempDir;
}
describe("bundled plugin public surface runtime", () => {
it("exports the canonical public surface source extension list", () => {
expect(PUBLIC_SURFACE_SOURCE_EXTENSIONS).toEqual([
@@ -16,6 +34,21 @@ describe("bundled plugin public surface runtime", () => {
]);
});
it("resolves source public surfaces from the shared extension list", () => {
const sourceRoot = createTempDir();
const modulePath = path.join(sourceRoot, "demo", "api.mts");
fs.mkdirSync(path.dirname(modulePath), { recursive: true });
fs.writeFileSync(modulePath, "export {};\n", "utf8");
expect(
resolveBundledPluginSourcePublicSurfacePath({
sourceRoot,
dirName: "demo",
artifactBasename: "api.js",
}),
).toBe(modulePath);
});
it("allows plugin-local nested artifact paths", () => {
expect(normalizeBundledPluginArtifactSubpath("src/outbound-adapter.js")).toBe(
"src/outbound-adapter.js",

View File

@@ -38,6 +38,26 @@ export function normalizeBundledPluginArtifactSubpath(artifactBasename: string):
return normalized;
}
export function resolveBundledPluginSourcePublicSurfacePath(params: {
sourceRoot: string;
dirName: string;
artifactBasename: string;
}): string | null {
const artifactBasename = normalizeBundledPluginArtifactSubpath(params.artifactBasename);
const sourceBaseName = artifactBasename.replace(/\.js$/u, "");
for (const ext of PUBLIC_SURFACE_SOURCE_EXTENSIONS) {
const sourceCandidate = path.resolve(
params.sourceRoot,
params.dirName,
`${sourceBaseName}${ext}`,
);
if (fs.existsSync(sourceCandidate)) {
return sourceCandidate;
}
}
return null;
}
export function resolveBundledPluginPublicSurfacePath(params: {
rootDir: string;
dirName: string;
@@ -55,14 +75,11 @@ export function resolveBundledPluginPublicSurfacePath(params: {
if (fs.existsSync(explicitBuiltCandidate)) {
return explicitBuiltCandidate;
}
const sourceBaseName = artifactBasename.replace(/\.js$/u, "");
for (const ext of PUBLIC_SURFACE_SOURCE_EXTENSIONS) {
const sourceCandidate = path.join(explicitPluginDir, `${sourceBaseName}${ext}`);
if (fs.existsSync(sourceCandidate)) {
return sourceCandidate;
}
}
return resolveBundledPluginSourcePublicSurfacePath({
sourceRoot: explicitBundledPluginsDir,
dirName: params.dirName,
artifactBasename,
});
}
for (const candidate of [
@@ -74,18 +91,9 @@ export function resolveBundledPluginPublicSurfacePath(params: {
}
}
const sourceBaseName = artifactBasename.replace(/\.js$/u, "");
for (const ext of PUBLIC_SURFACE_SOURCE_EXTENSIONS) {
const sourceCandidate = path.resolve(
params.rootDir,
"extensions",
params.dirName,
`${sourceBaseName}${ext}`,
);
if (fs.existsSync(sourceCandidate)) {
return sourceCandidate;
}
}
return null;
return resolveBundledPluginSourcePublicSurfacePath({
sourceRoot: path.resolve(params.rootDir, "extensions"),
dirName: params.dirName,
artifactBasename,
});
}