fix(packaging): recognize owner-restricted plugin aliases

This commit is contained in:
Vincent Koc
2026-08-06 07:15:40 +02:00
parent aacbcaacc8
commit a90c8d81df
3 changed files with 67 additions and 13 deletions
+1 -1
View File
@@ -33,7 +33,7 @@ export type PluginNpmRuntimeBuildPlan = {
};
/** Resolve the package-local runtime build plan for one publishable plugin package. */
export function resolvePluginNpmRuntimeBuildPlan(params: unknown): PluginNpmRuntimeBuildPlan | null;
/** List built host SDK imports that are absent from the OpenClaw package exports. */
/** List built host SDK imports unavailable through package exports or owner-restricted aliases. */
export function listMissingPluginNpmRuntimeHostExports(plan: PluginNpmRuntimeBuildPlan): string[];
/** Build package-local runtime files and static assets for one plugin package. */
export function buildPluginNpmRuntime(params: unknown): Promise<
+15 -3
View File
@@ -93,6 +93,10 @@ function createNeverBundleDependencyMatcher(packageJson) {
const HOST_PLUGIN_SDK_IMPORT_RE =
/(?:\bfrom\s+|\bimport\s*(?:\(\s*)?|\b(?:require|_+require\d*)\(\s*)["'](openclaw\/plugin-sdk\/[^"']+)["']/gu;
const OWNER_RESTRICTED_HOST_IMPORTS_BY_PACKAGE_NAME = new Map([
["@openclaw/codex", new Set(["openclaw/plugin-sdk/agent-harness-tool-authority-runtime"])],
["@openclaw/copilot", new Set(["openclaw/plugin-sdk/agent-harness-tool-authority-runtime"])],
]);
function listRuntimeJavaScriptFiles(rootDir) {
if (!fs.existsSync(rootDir)) {
@@ -111,8 +115,8 @@ function listRuntimeJavaScriptFiles(rootDir) {
}
/**
* List host SDK imports emitted by a built plugin runtime but absent from package exports.
* @param {{ repoRoot: string; outDir: string }} plan
* List host SDK imports emitted by a built plugin runtime but unavailable to its package owner.
* @param {{ repoRoot: string; outDir: string; packageJson?: { name?: unknown } }} plan
*/
export function listMissingPluginNpmRuntimeHostExports(plan) {
const hostImports = new Set();
@@ -131,8 +135,16 @@ export function listMissingPluginNpmRuntimeHostExports(plan) {
const hostPackageJson = readJsonFile(path.join(plan.repoRoot, "package.json"));
const hostExports = new Set(Object.keys(hostPackageJson.exports ?? {}));
const packageName =
typeof plan.packageJson?.name === "string" ? plan.packageJson.name.trim() : "";
const ownerRestrictedHostImports =
OWNER_RESTRICTED_HOST_IMPORTS_BY_PACKAGE_NAME.get(packageName) ?? new Set();
return [...hostImports]
.filter((specifier) => !hostExports.has(specifier.replace(/^openclaw/u, ".")))
.filter(
(specifier) =>
!hostExports.has(specifier.replace(/^openclaw/u, ".")) &&
!ownerRestrictedHostImports.has(specifier),
)
.toSorted((left, right) => left.localeCompare(right));
}
+51 -9
View File
@@ -240,16 +240,19 @@ describe("plugin npm runtime build planning", () => {
expect(plan.runtimeBuildOutputs).toContain("./dist/setup-api.js");
});
it("keeps published Codex runtime imports resolvable from the host package", async () => {
const result = await buildPluginNpmRuntime({
repoRoot,
packageDir: "extensions/codex",
logLevel: "silent",
});
const plan = expectPluginNpmRuntimeBuildPlan(result);
it.each(["codex", "copilot"])(
"keeps published %s runtime imports resolvable from the host package",
async (pluginId) => {
const result = await buildPluginNpmRuntime({
repoRoot,
packageDir: `extensions/${pluginId}`,
logLevel: "silent",
});
const plan = expectPluginNpmRuntimeBuildPlan(result);
expect(listMissingPluginNpmRuntimeHostExports(plan)).toEqual([]);
});
expect(listMissingPluginNpmRuntimeHostExports(plan)).toEqual([]);
},
);
it("detects unresolved side-effect host imports in built plugin runtimes", () => {
const outDir = tempDirs.make("openclaw-plugin-runtime-host-import-");
@@ -275,6 +278,45 @@ describe("plugin npm runtime build planning", () => {
]);
});
it("allows private harness authority imports only for their official package owners", () => {
const outDir = tempDirs.make("openclaw-plugin-runtime-private-owner-import-");
writeFileSync(
path.join(outDir, "index.js"),
[
'import "openclaw/plugin-sdk/agent-harness-tool-authority-runtime";',
'import "openclaw/plugin-sdk/not-owner-restricted";',
"",
].join("\n"),
);
const plan = expectPluginNpmRuntimeBuildPlan(
resolvePluginNpmRuntimeBuildPlan({
repoRoot,
packageDir: path.join(repoRoot, "extensions", "codex"),
}),
);
expect(listMissingPluginNpmRuntimeHostExports({ ...plan, outDir })).toEqual([
"openclaw/plugin-sdk/not-owner-restricted",
]);
expect(
listMissingPluginNpmRuntimeHostExports({
...plan,
outDir,
packageJson: { ...plan.packageJson, name: "@openclaw/copilot" },
}),
).toEqual(["openclaw/plugin-sdk/not-owner-restricted"]);
expect(
listMissingPluginNpmRuntimeHostExports({
...plan,
outDir,
packageJson: { ...plan.packageJson, name: "@openclaw/demo" },
}),
).toEqual([
"openclaw/plugin-sdk/agent-harness-tool-authority-runtime",
"openclaw/plugin-sdk/not-owner-restricted",
]);
});
it("does not require host metadata when the runtime has no host imports", () => {
const syntheticRepoRoot = tempDirs.make("openclaw-plugin-runtime-synthetic-repo-");
const outDir = tempDirs.make("openclaw-plugin-runtime-no-host-import-");