Files
adolf/test/scripts/bundled-plugin-build-entries.test.ts
alvis bedb527145
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Vendor OpenClaw source as Adolf fork baseline
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11),
free to diverge. Tree copied sans upstream .git; upstream remote added for
future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19.
Preserves docs/ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
2026-07-05 09:36:54 +00:00

304 lines
11 KiB
TypeScript

// Bundled Plugin Build Entries tests cover bundled plugin build entries script behavior.
import fs from "node:fs";
import path from "node:path";
import { describe, expect, it } from "vitest";
import {
collectRootPackageExcludedExtensionDirs,
listBundledPluginBuildEntries,
listBundledPluginPackArtifacts,
} from "../../scripts/lib/bundled-plugin-build-entries.mjs";
import { expectNoNodeFsScans } from "../../src/test-utils/fs-scan-assertions.js";
function expectNoPrefixMatches(values: string[], prefix: string) {
expect(values.filter((value) => value.startsWith(prefix))).toEqual([]);
}
function expectSomePrefixMatch(values: string[], prefix: string) {
expect(values.filter((value) => value.startsWith(prefix))).not.toEqual([]);
}
function pickEntries(entries: Record<string, string>, keys: readonly string[]) {
return Object.fromEntries(keys.map((key) => [key, entries[key]]));
}
describe("bundled plugin build entries", () => {
const bundledChannelEntrySources = ["index.ts", "channel-entry.ts", "setup-entry.ts"];
const forEachBundledChannelEntry = (
visit: (params: { entryPath: string; entry: string; pluginId: string }) => void,
) => {
for (const dirent of fs.readdirSync("extensions", { withFileTypes: true })) {
if (!dirent.isDirectory()) {
continue;
}
for (const sourceEntry of bundledChannelEntrySources) {
const entryPath = path.join("extensions", dirent.name, sourceEntry);
if (!fs.existsSync(entryPath)) {
continue;
}
visit({
entryPath,
entry: fs.readFileSync(entryPath, "utf8"),
pluginId: dirent.name,
});
}
}
};
it("includes manifest-less runtime core support packages in dist build entries", () => {
const entries = listBundledPluginBuildEntries();
const expectedEntries = {
"extensions/image-generation-core/api": "extensions/image-generation-core/api.ts",
"extensions/image-generation-core/runtime-api":
"extensions/image-generation-core/runtime-api.ts",
"extensions/media-understanding-core/runtime-api":
"extensions/media-understanding-core/runtime-api.ts",
};
expect(pickEntries(entries, Object.keys(expectedEntries))).toStrictEqual(expectedEntries);
});
it("keeps the Matrix packaged runtime shim in bundled plugin build entries", () => {
const entries = listBundledPluginBuildEntries();
const expectedEntries = {
"extensions/matrix/plugin-entry.handlers.runtime":
"extensions/matrix/plugin-entry.handlers.runtime.ts",
};
expect(pickEntries(entries, Object.keys(expectedEntries))).toStrictEqual(expectedEntries);
});
it("filters bundled plugin build entries for bounded script lanes", () => {
const entries = listBundledPluginBuildEntries({
env: {
...process.env,
OPENCLAW_BUNDLED_PLUGIN_BUILD_IDS: "active-memory,acpx",
},
});
const entryKeys = Object.keys(entries);
expect(entryKeys).toEqual(expect.arrayContaining(["extensions/acpx/index"]));
expect(entryKeys.every((entry) => /^extensions\/(?:acpx|active-memory)\//u.test(entry))).toBe(
true,
);
});
it("rejects unknown bounded bundled plugin build ids", () => {
expect(() =>
listBundledPluginBuildEntries({
env: {
...process.env,
OPENCLAW_BUNDLED_PLUGIN_BUILD_IDS: "missing-plugin",
},
}),
).toThrow(
"OPENCLAW_BUNDLED_PLUGIN_BUILD_IDS references unknown bundled plugin id(s): missing-plugin",
);
});
it("keeps the Telegram ingress worker out of bundled plugin public-surface entries", () => {
const entries = listBundledPluginBuildEntries();
expect(entries["extensions/telegram/telegram-ingress-worker.runtime"]).toBeUndefined();
});
it("keeps top-level bundled plugin test helpers out of public-surface entries", () => {
const entries = listBundledPluginBuildEntries();
expect(entries["extensions/browser/test-support"]).toBeUndefined();
expect(entries["extensions/comfy/test-helpers"]).toBeUndefined();
expect(entries["extensions/minimax/provider-http.test-helpers"]).toBeUndefined();
});
it("discovers repo plugin build entries without directory scans", () => {
const payload = expectNoNodeFsScans<{
artifacts: number;
entries: number;
}>(
`
const build = await import("./scripts/lib/bundled-plugin-build-entries.mjs");
const entries = build.listBundledPluginBuildEntries();
const artifacts = build.listBundledPluginPackArtifacts();
return {
artifacts: artifacts.length,
entries: Object.keys(entries).length,
};
`,
{ counters: ["readdirSync"] },
);
expect(payload.entries).toBeGreaterThan(0);
expect(payload.artifacts).toBeGreaterThan(0);
});
it("packs runtime core support packages without requiring plugin manifests", () => {
const artifacts = listBundledPluginPackArtifacts();
expect(artifacts).toContain("dist/extensions/image-generation-core/package.json");
expect(artifacts).toContain("dist/extensions/image-generation-core/runtime-api.js");
expect(artifacts).not.toContain("dist/extensions/image-generation-core/openclaw.plugin.json");
expect(artifacts).toContain("dist/extensions/media-understanding-core/runtime-api.js");
expect(artifacts).not.toContain(
"dist/extensions/media-understanding-core/openclaw.plugin.json",
);
});
it("packs the Matrix packaged runtime shim", () => {
const artifacts = listBundledPluginPackArtifacts({ includeRootPackageExcludedDirs: true });
expect(artifacts).toContain("dist/extensions/matrix/plugin-entry.handlers.runtime.js");
});
it("keeps private QA bundles out of required npm pack artifacts", () => {
const artifacts = listBundledPluginPackArtifacts();
expectNoPrefixMatches(artifacts, "dist/extensions/qa-channel/");
expectNoPrefixMatches(artifacts, "dist/extensions/qa-lab/");
expectNoPrefixMatches(artifacts, "dist/extensions/qa-matrix/");
});
it("keeps explicitly downloadable plugins out of bundled package artifacts", () => {
const entries = listBundledPluginBuildEntries();
const artifacts = listBundledPluginPackArtifacts();
for (const pluginId of ["acpx", "googlechat", "line"]) {
expectSomePrefixMatch(Object.keys(entries), `extensions/${pluginId}/`);
expectNoPrefixMatches(artifacts, `dist/extensions/${pluginId}/`);
}
for (const pluginId of ["qqbot", "whatsapp"]) {
expectNoPrefixMatches(Object.keys(entries), `extensions/${pluginId}/`);
expectNoPrefixMatches(artifacts, `dist/extensions/${pluginId}/`);
}
});
it("keeps external-only providers out of bundled dist entries", () => {
const entries = listBundledPluginBuildEntries();
const artifacts = listBundledPluginPackArtifacts();
for (const pluginId of ["amazon-bedrock", "amazon-bedrock-mantle", "anthropic-vertex"]) {
expectNoPrefixMatches(Object.keys(entries), `extensions/${pluginId}/`);
expectNoPrefixMatches(artifacts, `dist/extensions/${pluginId}/`);
}
});
it("keeps externalized runtime-dependency plugins out of bundled dist entries", () => {
const entries = listBundledPluginBuildEntries();
const artifacts = listBundledPluginPackArtifacts();
for (const pluginId of ["copilot", "openshell", "slack", "tokenjuice"]) {
expectNoPrefixMatches(Object.keys(entries), `extensions/${pluginId}/`);
expectNoPrefixMatches(artifacts, `dist/extensions/${pluginId}/`);
}
});
it("keeps Cohere bundled through the externalization transition", () => {
const artifacts = listBundledPluginPackArtifacts();
expect(artifacts).toContain("dist/extensions/cohere/index.js");
expect(artifacts).toContain("dist/extensions/cohere/openclaw.plugin.json");
expect(artifacts).toContain("dist/extensions/cohere/package.json");
});
it("keeps bundled channel secret contracts on packed top-level sidecars", () => {
const artifacts = listBundledPluginPackArtifacts();
const excludedPackageDirs = collectRootPackageExcludedExtensionDirs();
const offenders: string[] = [];
const secretBackedPluginIds = new Set<string>();
forEachBundledChannelEntry(({ entryPath, entry, pluginId }) => {
if (!entry.includes('exportName: "channelSecrets"')) {
return;
}
secretBackedPluginIds.add(pluginId);
if (entry.includes("./src/secret-contract.js")) {
offenders.push(entryPath);
}
expect(entry).toContain('specifier: "./secret-contract-api.js"');
});
expect(offenders).toStrictEqual([]);
for (const pluginId of [...secretBackedPluginIds].toSorted()) {
if (excludedPackageDirs.has(pluginId)) {
continue;
}
const secretApiPath = path.join("extensions", pluginId, "secret-contract-api.ts");
expect(fs.readFileSync(secretApiPath, "utf8")).toContain("channelSecrets");
expect(artifacts).toContain(`dist/extensions/${pluginId}/secret-contract-api.js`);
}
});
it("keeps dedicated channel contract exports off broad contract-api sidecars", () => {
const duplicateExportMarkersByArtifact = {
"directory-contract-api.ts": [
"DirectoryContractPlugin",
"DirectoryGroupsFromConfig",
"DirectoryPeersFromConfig",
],
"doctor-contract-api.ts": [
"legacyConfigRules",
"normalizeCompatibilityConfig",
"stateMigrations",
],
"secret-contract-api.ts": [
"channelSecrets",
"collectRuntimeConfigAssignments",
"secretTargetRegistryEntries",
],
"security-audit-contract-api.ts": ["SecurityAuditFindings"],
"security-contract-api.ts": [
"collectUnsupportedSecretRefConfigCandidates",
"unsupportedSecretRefSurfacePatterns",
],
"session-binding-contract-api.ts": [
"ConversationBindingManager",
"ThreadBindingManager",
"ThreadBindingsForTests",
"setMatrixRuntime",
],
} as const;
const offenders: string[] = [];
for (const dirent of fs.readdirSync("extensions", { withFileTypes: true })) {
if (!dirent.isDirectory()) {
continue;
}
const contractApiPath = path.join("extensions", dirent.name, "contract-api.ts");
if (!fs.existsSync(contractApiPath)) {
continue;
}
const contractApi = fs.readFileSync(contractApiPath, "utf8");
for (const [artifact, markers] of Object.entries(duplicateExportMarkersByArtifact)) {
if (!fs.existsSync(path.join("extensions", dirent.name, artifact))) {
continue;
}
for (const marker of markers) {
if (contractApi.includes(marker)) {
offenders.push(`${contractApiPath} duplicates ${artifact}: ${marker}`);
}
}
}
}
expect(offenders).toStrictEqual([]);
});
it("keeps bundled channel entry metadata on packed top-level sidecars", () => {
const offenders: string[] = [];
forEachBundledChannelEntry(({ entryPath, entry }) => {
if (
!entry.includes("defineBundledChannelEntry") &&
!entry.includes("defineBundledChannelSetupEntry")
) {
return;
}
if (/specifier:\s*["']\.\/src\//u.test(entry)) {
offenders.push(entryPath);
}
});
expect(offenders).toStrictEqual([]);
});
});