diff --git a/scripts/release-check.ts b/scripts/release-check.ts index 7fd2c7fb806..f3cf9ae33c4 100755 --- a/scripts/release-check.ts +++ b/scripts/release-check.ts @@ -482,20 +482,25 @@ function runPackedTaskRegistryControlRuntimeSmoke(packageRoot: string): void { if (!existsSync(runtimePath)) { throw new Error("release-check: packed task-registry control runtime is missing."); } - const source = ` -const runtime = await import(${JSON.stringify(pathToFileURL(runtimePath).href)}); -if (typeof runtime.getAcpSessionManager !== "function") { - throw new Error("missing getAcpSessionManager export"); -} -if (typeof runtime.killSubagentRunAdmin !== "function") { - throw new Error("missing killSubagentRunAdmin export"); -} -`; - execFileSync(process.execPath, ["--input-type=module", "--eval", source], { - cwd: packageRoot, - stdio: "inherit", - env: createPackedCliSmokeEnv(process.env), - }); + const source = [ + 'const dynamicImport = Function("specifier", "return im" + "port(specifier)");', + "const runtime = await dynamicImport(process.argv[1]);", + 'if (typeof runtime.getAcpSessionManager !== "function") {', + ' throw new Error("missing getAcpSessionManager export");', + "}", + 'if (typeof runtime.killSubagentRunAdmin !== "function") {', + ' throw new Error("missing killSubagentRunAdmin export");', + "}", + ].join("\n"); + execFileSync( + process.execPath, + ["--input-type=module", "--eval", source, pathToFileURL(runtimePath).href], + { + cwd: packageRoot, + stdio: "inherit", + env: createPackedCliSmokeEnv(process.env), + }, + ); } function runPackedCliSmoke(params: { diff --git a/src/plugins/sdk-alias.test.ts b/src/plugins/sdk-alias.test.ts index f3f77fe0dd3..dd292b398f7 100644 --- a/src/plugins/sdk-alias.test.ts +++ b/src/plugins/sdk-alias.test.ts @@ -682,6 +682,29 @@ describe("plugin sdk alias helpers", () => { ); }); + it("adds the private qa-channel api alias when private qa is enabled", () => { + const fixture = createPluginSdkAliasFixture(); + const qaChannelApiPath = path.join(fixture.root, "extensions", "qa-channel", "api.ts"); + mkdirSafeDir(path.dirname(qaChannelApiPath)); + fs.writeFileSync(qaChannelApiPath, "export const qaChannelApi = true;\n", "utf-8"); + const sourcePluginEntry = writePluginEntry( + fixture.root, + bundledPluginFile("qa-lab", "src/runtime-api.ts"), + ); + + const publicAliases = withEnv({ OPENCLAW_ENABLE_PRIVATE_QA_CLI: undefined }, () => + buildPluginLoaderAliasMap(sourcePluginEntry), + ); + const privateAliases = withEnv({ OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1" }, () => + buildPluginLoaderAliasMap(sourcePluginEntry), + ); + + expect(publicAliases["@openclaw/qa-channel/api.js"]).toBeUndefined(); + expect(fs.realpathSync(privateAliases["@openclaw/qa-channel/api.js"] ?? "")).toBe( + fs.realpathSync(qaChannelApiPath), + ); + }); + it("applies explicit dist resolution to plugin-sdk subpath aliases too", () => { const { fixture, distRootAlias, distChannelRuntimePath } = createPluginSdkAliasTargetFixture(); const sourcePluginEntry = writePluginEntry( diff --git a/src/plugins/sdk-alias.ts b/src/plugins/sdk-alias.ts index 212537d55f1..a3844e5971a 100644 --- a/src/plugins/sdk-alias.ts +++ b/src/plugins/sdk-alias.ts @@ -310,6 +310,20 @@ function shouldIncludePrivateLocalOnlyPluginSdkSubpaths() { return process.env.OPENCLAW_ENABLE_PRIVATE_QA_CLI === "1"; } +function resolvePrivateQaChannelApiAlias( + params: LoaderModuleResolveParams & { modulePath: string }, +) { + if (!shouldIncludePrivateLocalOnlyPluginSdkSubpaths()) { + return null; + } + const packageRoot = resolveLoaderPluginSdkPackageRoot(params); + if (!packageRoot) { + return null; + } + const candidate = path.join(packageRoot, "extensions", "qa-channel", "api.ts"); + return fs.existsSync(candidate) ? candidate : null; +} + function hasPluginSdkSubpathArtifact(packageRoot: string, subpath: string) { const distPath = path.join(packageRoot, "dist", "plugin-sdk", `${subpath}.js`); if (isUsableDistPluginSdkArtifact(distPath)) { @@ -622,10 +636,21 @@ export function buildPluginLoaderAliasMap( pluginSdkResolution, }); const extensionApiAlias = resolveExtensionApiAlias({ modulePath, pluginSdkResolution }); + const privateQaChannelApiAlias = resolvePrivateQaChannelApiAlias({ + modulePath, + argv1, + moduleUrl, + pluginSdkResolution, + }); const result: Record = { ...(extensionApiAlias ? { "openclaw/extension-api": normalizeJitiAliasTargetPath(extensionApiAlias) } : {}), + ...(privateQaChannelApiAlias + ? { + "@openclaw/qa-channel/api.js": normalizeJitiAliasTargetPath(privateQaChannelApiAlias), + } + : {}), ...(pluginSdkAlias ? Object.fromEntries( PLUGIN_SDK_PACKAGE_NAMES.map((packageName) => [