mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 19:00:45 +00:00
fix: align private QA runner roots
This commit is contained in:
@@ -144,7 +144,10 @@ function getFacadeBoundaryResolvedConfig() {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
function getFacadeManifestRegistry(params: { cacheKey: string }): readonly PluginManifestRecord[] {
|
||||
function getFacadeManifestRegistry(params: {
|
||||
cacheKey: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): readonly PluginManifestRecord[] {
|
||||
const cached = cachedManifestRegistryByKey.get(params.cacheKey);
|
||||
if (cached) {
|
||||
return cached;
|
||||
@@ -152,6 +155,7 @@ function getFacadeManifestRegistry(params: { cacheKey: string }): readonly Plugi
|
||||
const loaded = loadPluginManifestRegistry({
|
||||
config: getFacadeBoundaryResolvedConfig().config,
|
||||
cache: true,
|
||||
...(params.env ? { env: params.env } : {}),
|
||||
}).plugins;
|
||||
cachedManifestRegistryByKey.set(params.cacheKey, loaded);
|
||||
return loaded;
|
||||
@@ -161,8 +165,12 @@ export function resolveRegistryPluginModuleLocation(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
resolutionKey: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): FacadeModuleLocation | null {
|
||||
const registry = getFacadeManifestRegistry({ cacheKey: params.resolutionKey });
|
||||
const registry = getFacadeManifestRegistry({
|
||||
cacheKey: params.resolutionKey,
|
||||
...(params.env ? { env: params.env } : {}),
|
||||
});
|
||||
type RegistryRecord = (typeof registry)[number];
|
||||
const tiers: Array<(plugin: RegistryRecord) => boolean> = [
|
||||
(plugin) => plugin.id === params.dirName,
|
||||
@@ -229,6 +237,7 @@ function resolveBundledMetadataManifestRecord(params: {
|
||||
artifactBasename: string;
|
||||
location: FacadeModuleLocation | null;
|
||||
sourceExtensionsRoot: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): FacadePluginManifestLike | null {
|
||||
if (!params.location) {
|
||||
return null;
|
||||
@@ -247,7 +256,7 @@ function resolveBundledMetadataManifestRecord(params: {
|
||||
resolvedDirName,
|
||||
});
|
||||
}
|
||||
const bundledPluginsDir = resolveBundledPluginsDir();
|
||||
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
|
||||
if (!bundledPluginsDir) {
|
||||
return null;
|
||||
}
|
||||
@@ -275,6 +284,7 @@ function resolveBundledPluginManifestRecord(params: {
|
||||
location: FacadeModuleLocation | null;
|
||||
sourceExtensionsRoot: string;
|
||||
resolutionKey: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): FacadePluginManifestLike | null {
|
||||
if (cachedFacadeManifestRecordsByKey.has(params.resolutionKey)) {
|
||||
return cachedFacadeManifestRecordsByKey.get(params.resolutionKey) ?? null;
|
||||
@@ -286,7 +296,10 @@ function resolveBundledPluginManifestRecord(params: {
|
||||
return metadataRecord;
|
||||
}
|
||||
|
||||
const registry = getFacadeManifestRegistry({ cacheKey: params.resolutionKey });
|
||||
const registry = getFacadeManifestRegistry({
|
||||
cacheKey: params.resolutionKey,
|
||||
...(params.env ? { env: params.env } : {}),
|
||||
});
|
||||
const resolved =
|
||||
(params.location
|
||||
? registry.find((plugin) => {
|
||||
@@ -312,6 +325,7 @@ export function resolveTrackedFacadePluginId(params: {
|
||||
location: FacadeModuleLocation | null;
|
||||
sourceExtensionsRoot: string;
|
||||
resolutionKey: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): string {
|
||||
return resolveBundledPluginManifestRecord(params)?.id ?? params.dirName;
|
||||
}
|
||||
@@ -322,6 +336,7 @@ export function resolveBundledPluginPublicSurfaceAccess(params: {
|
||||
location: FacadeModuleLocation | null;
|
||||
sourceExtensionsRoot: string;
|
||||
resolutionKey: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): { allowed: boolean; pluginId?: string; reason?: string } {
|
||||
const cached = cachedFacadePublicSurfaceAccessByKey.get(params.resolutionKey);
|
||||
if (cached) {
|
||||
@@ -410,6 +425,7 @@ export function resolveActivatedBundledPluginPublicSurfaceAccessOrThrow(params:
|
||||
location: FacadeModuleLocation | null;
|
||||
sourceExtensionsRoot: string;
|
||||
resolutionKey: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}) {
|
||||
const access = resolveBundledPluginPublicSurfaceAccess(params);
|
||||
if (!access.allowed) {
|
||||
|
||||
@@ -52,16 +52,21 @@ function getOpenClawPackageRoot() {
|
||||
return cachedOpenClawPackageRoot;
|
||||
}
|
||||
|
||||
function createFacadeResolutionKey(params: { dirName: string; artifactBasename: string }): string {
|
||||
const bundledPluginsDir = resolveBundledPluginsDir();
|
||||
function createFacadeResolutionKey(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): string {
|
||||
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
|
||||
return `${params.dirName}::${params.artifactBasename}::${bundledPluginsDir ? path.resolve(bundledPluginsDir) : "<default>"}`;
|
||||
}
|
||||
|
||||
function resolveFacadeModuleLocationUncached(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): { modulePath: string; boundaryRoot: string } | null {
|
||||
const bundledPluginsDir = resolveBundledPluginsDir();
|
||||
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
|
||||
const preferSource = !CURRENT_MODULE_PATH.includes(`${path.sep}dist${path.sep}`);
|
||||
if (preferSource) {
|
||||
const modulePath =
|
||||
@@ -71,6 +76,7 @@ function resolveFacadeModuleLocationUncached(params: {
|
||||
}) ??
|
||||
resolveBundledPluginPublicSurfacePath({
|
||||
rootDir: getOpenClawPackageRoot(),
|
||||
env: params.env,
|
||||
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
|
||||
dirName: params.dirName,
|
||||
artifactBasename: params.artifactBasename,
|
||||
@@ -88,6 +94,7 @@ function resolveFacadeModuleLocationUncached(params: {
|
||||
}
|
||||
const modulePath = resolveBundledPluginPublicSurfacePath({
|
||||
rootDir: getOpenClawPackageRoot(),
|
||||
env: params.env,
|
||||
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
|
||||
dirName: params.dirName,
|
||||
artifactBasename: params.artifactBasename,
|
||||
@@ -107,6 +114,7 @@ function resolveFacadeModuleLocationUncached(params: {
|
||||
function resolveFacadeModuleLocation(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): { modulePath: string; boundaryRoot: string } | null {
|
||||
const key = createFacadeResolutionKey(params);
|
||||
if (cachedFacadeModuleLocationsByKey.has(key)) {
|
||||
@@ -253,6 +261,7 @@ export function loadBundledPluginPublicSurfaceModuleSync<T extends object>(param
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
trackedPluginId?: string | (() => string);
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): T {
|
||||
const location = resolveFacadeModuleLocation(params);
|
||||
if (!location) {
|
||||
|
||||
@@ -42,8 +42,12 @@ const cachedFacadeModuleLocationsByKey = new Map<
|
||||
} | null
|
||||
>();
|
||||
|
||||
function createFacadeResolutionKey(params: { dirName: string; artifactBasename: string }): string {
|
||||
const bundledPluginsDir = resolveBundledPluginsDir();
|
||||
function createFacadeResolutionKey(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): string {
|
||||
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
|
||||
return `${params.dirName}::${params.artifactBasename}::${bundledPluginsDir ? path.resolve(bundledPluginsDir) : "<default>"}`;
|
||||
}
|
||||
|
||||
@@ -81,6 +85,7 @@ function resolveRegistryPluginModuleLocationFromRegistry(params: {
|
||||
function resolveRegistryPluginModuleLocation(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): { modulePath: string; boundaryRoot: string } | null {
|
||||
return loadFacadeActivationCheckRuntime().resolveRegistryPluginModuleLocation({
|
||||
...params,
|
||||
@@ -91,8 +96,9 @@ function resolveRegistryPluginModuleLocation(params: {
|
||||
function resolveFacadeModuleLocationUncached(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): { modulePath: string; boundaryRoot: string } | null {
|
||||
const bundledPluginsDir = resolveBundledPluginsDir();
|
||||
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
|
||||
const preferSource = !CURRENT_MODULE_PATH.includes(`${path.sep}dist${path.sep}`);
|
||||
if (preferSource) {
|
||||
const modulePath =
|
||||
@@ -102,6 +108,7 @@ function resolveFacadeModuleLocationUncached(params: {
|
||||
}) ??
|
||||
resolveBundledPluginPublicSurfacePath({
|
||||
rootDir: OPENCLAW_PACKAGE_ROOT,
|
||||
env: params.env,
|
||||
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
|
||||
dirName: params.dirName,
|
||||
artifactBasename: params.artifactBasename,
|
||||
@@ -119,6 +126,7 @@ function resolveFacadeModuleLocationUncached(params: {
|
||||
}
|
||||
const modulePath = resolveBundledPluginPublicSurfacePath({
|
||||
rootDir: OPENCLAW_PACKAGE_ROOT,
|
||||
env: params.env,
|
||||
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
|
||||
dirName: params.dirName,
|
||||
artifactBasename: params.artifactBasename,
|
||||
@@ -138,6 +146,7 @@ function resolveFacadeModuleLocationUncached(params: {
|
||||
function resolveFacadeModuleLocation(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): { modulePath: string; boundaryRoot: string } | null {
|
||||
const key = createFacadeResolutionKey(params);
|
||||
if (cachedFacadeModuleLocationsByKey.has(key)) {
|
||||
@@ -151,6 +160,7 @@ function resolveFacadeModuleLocation(params: {
|
||||
type BundledPluginPublicSurfaceParams = {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
};
|
||||
|
||||
type FacadeActivationCheckRuntimeModule = typeof import("./facade-activation-check.runtime.js");
|
||||
@@ -252,6 +262,7 @@ export function loadBundledPluginPublicSurfaceModuleSync<T extends object>(
|
||||
export function canLoadActivatedBundledPluginPublicSurface(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): boolean {
|
||||
return loadFacadeActivationCheckRuntime().resolveBundledPluginPublicSurfaceAccess(
|
||||
buildFacadeActivationCheckParams(params),
|
||||
@@ -261,6 +272,7 @@ export function canLoadActivatedBundledPluginPublicSurface(params: {
|
||||
export function loadActivatedBundledPluginPublicSurfaceModuleSync<T extends object>(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): T {
|
||||
loadFacadeActivationCheckRuntime().resolveActivatedBundledPluginPublicSurfaceAccessOrThrow(
|
||||
buildFacadeActivationCheckParams(params),
|
||||
@@ -271,6 +283,7 @@ export function loadActivatedBundledPluginPublicSurfaceModuleSync<T extends obje
|
||||
export function tryLoadActivatedBundledPluginPublicSurfaceModuleSync<T extends object>(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): T | null {
|
||||
const access = loadFacadeActivationCheckRuntime().resolveBundledPluginPublicSurfaceAccess(
|
||||
buildFacadeActivationCheckParams(params),
|
||||
|
||||
@@ -72,6 +72,34 @@ describe("plugin-sdk qa-runner-runtime", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the source bundled tree for qa-lab runtime loading in private qa mode", async () => {
|
||||
const sourceRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-qa-runtime-root-"));
|
||||
tempDirs.push(sourceRoot);
|
||||
fs.mkdirSync(path.join(sourceRoot, "src"), { recursive: true });
|
||||
fs.mkdirSync(path.join(sourceRoot, "extensions"), { recursive: true });
|
||||
fs.writeFileSync(path.join(sourceRoot, ".git"), "gitdir: /tmp/mock\n", "utf8");
|
||||
process.env.OPENCLAW_ENABLE_PRIVATE_QA_CLI = "1";
|
||||
resolveOpenClawPackageRootSync.mockReturnValue(sourceRoot);
|
||||
|
||||
const runtimeSurface = {
|
||||
defaultQaRuntimeModelForMode: vi.fn(),
|
||||
startQaLiveLaneGateway: vi.fn(),
|
||||
};
|
||||
loadBundledPluginPublicSurfaceModuleSync.mockReturnValue(runtimeSurface);
|
||||
|
||||
const module = await import("./qa-runner-runtime.js");
|
||||
|
||||
expect(module.loadQaRuntimeModule()).toBe(runtimeSurface);
|
||||
expect(loadBundledPluginPublicSurfaceModuleSync).toHaveBeenCalledWith({
|
||||
dirName: "qa-lab",
|
||||
artifactBasename: "runtime-api.js",
|
||||
env: expect.objectContaining({
|
||||
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1",
|
||||
OPENCLAW_BUNDLED_PLUGINS_DIR: path.join(sourceRoot, "extensions"),
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it("reports the qa runtime as unavailable when the qa-lab surface is missing", async () => {
|
||||
loadBundledPluginPublicSurfaceModuleSync.mockImplementation(() => {
|
||||
throw new Error("Unable to resolve bundled plugin public surface qa-lab/runtime-api.js");
|
||||
@@ -194,6 +222,14 @@ describe("plugin-sdk qa-runner-runtime", () => {
|
||||
OPENCLAW_BUNDLED_PLUGINS_DIR: path.join(sourceRoot, "extensions"),
|
||||
}),
|
||||
});
|
||||
expect(loadBundledPluginPublicSurfaceModuleSync).toHaveBeenCalledWith({
|
||||
dirName: "qa-matrix",
|
||||
artifactBasename: "runtime-api.js",
|
||||
env: expect.objectContaining({
|
||||
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1",
|
||||
OPENCLAW_BUNDLED_PLUGINS_DIR: path.join(sourceRoot, "extensions"),
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it("fails fast when two plugins declare the same qa runner command", async () => {
|
||||
|
||||
@@ -56,9 +56,11 @@ function isMissingQaRuntimeError(error: unknown) {
|
||||
}
|
||||
|
||||
export function loadQaRuntimeModule(): QaRuntimeSurface {
|
||||
const env = resolvePrivateQaRunnerEnv();
|
||||
return loadBundledPluginPublicSurfaceModuleSync<QaRuntimeSurface>({
|
||||
dirName: ["qa", "lab"].join("-"),
|
||||
artifactBasename: ["runtime-api", "js"].join("."),
|
||||
...(env ? { env } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -74,7 +76,7 @@ export function isQaRuntimeAvailable(): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
function resolvePrivateQaRunnerManifestEnv(
|
||||
function resolvePrivateQaRunnerEnv(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): NodeJS.ProcessEnv | undefined {
|
||||
if (env.OPENCLAW_ENABLE_PRIVATE_QA_CLI !== "1") {
|
||||
@@ -102,12 +104,13 @@ function resolvePrivateQaRunnerManifestEnv(
|
||||
};
|
||||
}
|
||||
|
||||
function listDeclaredQaRunnerPlugins(): Array<
|
||||
function listDeclaredQaRunnerPlugins(
|
||||
env: NodeJS.ProcessEnv | undefined = resolvePrivateQaRunnerEnv(),
|
||||
): Array<
|
||||
PluginManifestRecord & {
|
||||
qaRunners: NonNullable<PluginManifestRecord["qaRunners"]>;
|
||||
}
|
||||
> {
|
||||
const env = resolvePrivateQaRunnerManifestEnv();
|
||||
return loadPluginManifestRegistry({ cache: true, ...(env ? { env } : {}) })
|
||||
.plugins.filter(
|
||||
(
|
||||
@@ -145,24 +148,30 @@ function indexRuntimeRegistrations(
|
||||
return registrationByCommandName;
|
||||
}
|
||||
|
||||
function loadQaRunnerRuntimeSurface(plugin: PluginManifestRecord): QaRunnerRuntimeSurface | null {
|
||||
function loadQaRunnerRuntimeSurface(
|
||||
plugin: PluginManifestRecord,
|
||||
env?: NodeJS.ProcessEnv,
|
||||
): QaRunnerRuntimeSurface | null {
|
||||
if (plugin.origin === "bundled") {
|
||||
return loadBundledPluginPublicSurfaceModuleSync<QaRunnerRuntimeSurface>({
|
||||
dirName: plugin.id,
|
||||
artifactBasename: "runtime-api.js",
|
||||
...(env ? { env } : {}),
|
||||
});
|
||||
}
|
||||
return tryLoadActivatedBundledPluginPublicSurfaceModuleSync<QaRunnerRuntimeSurface>({
|
||||
dirName: plugin.id,
|
||||
artifactBasename: "runtime-api.js",
|
||||
...(env ? { env } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
export function listQaRunnerCliContributions(): readonly QaRunnerCliContribution[] {
|
||||
const env = resolvePrivateQaRunnerEnv();
|
||||
const contributions = new Map<string, QaRunnerCliContribution>();
|
||||
|
||||
for (const plugin of listDeclaredQaRunnerPlugins()) {
|
||||
const runtimeSurface = loadQaRunnerRuntimeSurface(plugin);
|
||||
for (const plugin of listDeclaredQaRunnerPlugins(env)) {
|
||||
const runtimeSurface = loadQaRunnerRuntimeSurface(plugin, env);
|
||||
const runtimeRegistrationByCommandName = runtimeSurface
|
||||
? indexRuntimeRegistrations(plugin.id, runtimeSurface)
|
||||
: null;
|
||||
|
||||
Reference in New Issue
Block a user