Files
openclaw/src/plugins/loader.activation.test-utils.ts
Peter Steinberger d8f2645c2c refactor(memory-wiki): move compiled cache to plugin state (#110167)
* feat(plugin-sdk): prepare memory prompts asynchronously

* refactor(memory-wiki): move compiled cache to plugin state

* fix(plugin-sdk): export memory prompt preparer type

* refactor(plugin-sdk): keep prompt preparer type private

* test(memory-wiki): isolate compiled cache state

* chore(memory-wiki): satisfy changed gates

* fix(memory): preserve prepared prompts across context engines

* docs(plugin-sdk): refresh API baseline

* fix(memory): revalidate prepared prompt ownership

* fix(codex): propagate sandboxed memory context

* test(memory-wiki): seed durable vault identity

* refactor(memory-wiki): keep cache lifecycle internal

* fix(memory-wiki): harden compiled cache publication

* style(memory-wiki): format cache lifecycle changes

* test(memory-wiki): support extension test lib target

* style(memory-wiki): remove redundant catch

* refactor(memory-wiki): keep vault generation helper private
2026-07-18 03:05:03 +01:00

1658 lines
54 KiB
TypeScript

// Imported by loader.test.ts to keep its mocked suite in one Vitest module graph.
import fs from "node:fs";
import path from "node:path";
import { afterAll, afterEach, describe, expect, it } from "vitest";
import { getContextEngineRegistration } from "../context-engine/registry.js";
import { withEnv } from "../test-utils/env.js";
import { getCompactionProvider } from "./compaction-provider.js";
import { writePersistedInstalledPluginIndexInstallRecordsSync } from "./installed-plugin-index-records.js";
import { loadOpenClawPlugins } from "./loader.js";
import {
EMPTY_PLUGIN_SCHEMA,
makeTempDir,
mkdirSafe,
useNoBundledPlugins,
writePlugin,
} from "./loader.test-fixtures.js";
import {
countMatching,
writeWorkspacePlugin,
withStateDir,
loadRegistryFromSinglePlugin,
loadRegistryFromAllowedPlugins,
runRegistryScenarios,
runSinglePluginRegistryScenarios,
loadRegistryFromScenarioPlugins,
expectRegisteredHttpRoute,
expectDuplicateRegistrationResult,
expectRegistryErrorDiagnostic,
expectDiagnosticContaining,
expectNoDiagnosticContaining,
createErrorLogger,
expectCacheMissThenHit,
globalAfterEach0,
globalAfterAll1,
} from "./loader.test-harness.js";
import {
listMemoryPromptPreparations,
listMemoryPromptSupplements,
} from "./memory-state.test-fixtures.js";
import type { PluginSdkResolutionPreference } from "./sdk-alias.js";
afterEach(globalAfterEach0);
afterAll(globalAfterAll1);
describe("loadOpenClawPlugins", () => {
it.each([
{
name: "does not reuse cached registries when env-resolved install paths change",
setup: () => {
useNoBundledPlugins();
const openclawHome = makeTempDir();
const ignoredHome = makeTempDir();
const stateDir = makeTempDir();
const pluginDir = path.join(openclawHome, "plugins", "tracked-install-cache");
mkdirSafe(pluginDir);
const plugin = writePlugin({
id: "tracked-install-cache",
dir: pluginDir,
filename: "index.cjs",
body: `module.exports = { id: "tracked-install-cache", register() {} };`,
});
writePersistedInstalledPluginIndexInstallRecordsSync(
{
"tracked-install-cache": {
source: "path" as const,
installPath: "~/plugins/tracked-install-cache",
sourcePath: "~/plugins/tracked-install-cache",
},
},
{ stateDir },
);
const options = {
config: {
plugins: {
load: { paths: [plugin.file] },
allow: ["tracked-install-cache"],
},
},
};
const secondHome = makeTempDir();
return {
loadFirst: () =>
loadOpenClawPlugins({
...options,
env: {
...process.env,
OPENCLAW_HOME: openclawHome,
HOME: ignoredHome,
OPENCLAW_STATE_DIR: stateDir,
OPENCLAW_BUNDLED_PLUGINS_DIR: "/nonexistent/bundled/plugins",
},
}),
loadVariant: () =>
loadOpenClawPlugins({
...options,
env: {
...process.env,
OPENCLAW_HOME: secondHome,
HOME: ignoredHome,
OPENCLAW_STATE_DIR: stateDir,
OPENCLAW_BUNDLED_PLUGINS_DIR: "/nonexistent/bundled/plugins",
},
}),
};
},
},
{
name: "does not reuse cached registries across different plugin SDK resolution preferences",
setup: () => {
useNoBundledPlugins();
const plugin = writePlugin({
id: "cache-sdk-resolution",
filename: "cache-sdk-resolution.cjs",
body: `module.exports = { id: "cache-sdk-resolution", register() {} };`,
});
const options = {
workspaceDir: plugin.dir,
config: {
plugins: {
allow: ["cache-sdk-resolution"],
load: {
paths: [plugin.file],
},
},
},
};
return {
loadFirst: () => loadOpenClawPlugins(options),
loadVariant: () =>
loadOpenClawPlugins({
...options,
pluginSdkResolution: "workspace" as PluginSdkResolutionPreference,
}),
};
},
},
{
name: "does not reuse cached registries across gateway subagent binding modes",
setup: () => {
useNoBundledPlugins();
const plugin = writePlugin({
id: "cache-gateway-shared",
filename: "cache-gateway-shared.cjs",
body: `module.exports = { id: "cache-gateway-shared", register() {} };`,
});
const options = {
workspaceDir: plugin.dir,
config: {
plugins: {
allow: ["cache-gateway-shared"],
load: {
paths: [plugin.file],
},
},
},
};
return {
loadFirst: () => loadOpenClawPlugins(options),
loadVariant: () =>
loadOpenClawPlugins({
...options,
runtimeOptions: {
allowGatewaySubagentBinding: true,
},
}),
};
},
},
])("$name", ({ setup }) => {
expectCacheMissThenHit(setup());
});
it("normalizes bundled plugin env overrides against the provided env", () => {
const bundledDir = makeTempDir();
const homeDir = path.dirname(bundledDir);
const override = `~/${path.basename(bundledDir)}`;
const plugin = writePlugin({
id: "tilde-bundled",
dir: path.join(bundledDir, "tilde-bundled"),
filename: "index.cjs",
body: `module.exports = { id: "tilde-bundled", register() {} };`,
});
const registry = loadOpenClawPlugins({
env: {
...process.env,
HOME: homeDir,
OPENCLAW_HOME: undefined,
OPENCLAW_BUNDLED_PLUGINS_DIR: override,
},
config: {
plugins: {
allow: ["tilde-bundled"],
entries: {
"tilde-bundled": { enabled: true },
},
},
},
});
expect(
fs.realpathSync(registry.plugins.find((entry) => entry.id === "tilde-bundled")?.source ?? ""),
).toBe(fs.realpathSync(plugin.file));
});
it("prefers OPENCLAW_HOME over HOME for env-expanded load paths", () => {
const ignoredHome = makeTempDir();
const openclawHome = makeTempDir();
const stateDir = makeTempDir();
const bundledDir = makeTempDir();
const plugin = writePlugin({
id: "openclaw-home-demo",
dir: path.join(openclawHome, "plugins", "openclaw-home-demo"),
filename: "index.cjs",
body: `module.exports = { id: "openclaw-home-demo", register() {} };`,
});
const registry = loadOpenClawPlugins({
env: {
...process.env,
HOME: ignoredHome,
OPENCLAW_HOME: openclawHome,
OPENCLAW_STATE_DIR: stateDir,
OPENCLAW_BUNDLED_PLUGINS_DIR: bundledDir,
},
config: {
plugins: {
allow: ["openclaw-home-demo"],
entries: {
"openclaw-home-demo": { enabled: true },
},
load: {
paths: ["~/plugins/openclaw-home-demo"],
},
},
},
});
expect(
fs.realpathSync(
registry.plugins.find((entry) => entry.id === "openclaw-home-demo")?.source ?? "",
),
).toBe(fs.realpathSync(plugin.file));
});
it("loads plugins when source and root differ only by realpath alias", () => {
useNoBundledPlugins();
const plugin = writePlugin({
id: "alias-safe",
filename: "alias-safe.cjs",
body: `module.exports = { id: "alias-safe", register() {} };`,
});
const realRoot = fs.realpathSync(plugin.dir);
if (realRoot === plugin.dir) {
return;
}
const registry = loadRegistryFromSinglePlugin({
plugin,
pluginConfig: {
allow: ["alias-safe"],
},
});
const loaded = registry.plugins.find((entry) => entry.id === "alias-safe");
expect(loaded?.status).toBe("loaded");
});
it("denylist disables plugins even if allowed", () => {
useNoBundledPlugins();
const plugin = writePlugin({
id: "blocked",
body: `module.exports = { id: "blocked", register() {} };`,
});
const registry = loadRegistryFromSinglePlugin({
plugin,
pluginConfig: {
allow: ["blocked"],
deny: ["blocked"],
},
});
const blocked = registry.plugins.find((entry) => entry.id === "blocked");
expect(blocked?.status).toBe("disabled");
});
it("fails fast on invalid plugin config", () => {
useNoBundledPlugins();
const plugin = writePlugin({
id: "configurable",
filename: "configurable.cjs",
body: `module.exports = { id: "configurable", register() {} };`,
});
const registry = loadRegistryFromSinglePlugin({
plugin,
pluginConfig: {
entries: {
configurable: {
config: "nope" as unknown as Record<string, unknown>,
},
},
},
});
const configurable = registry.plugins.find((entry) => entry.id === "configurable");
expect(configurable?.status).toBe("error");
expectDiagnosticContaining({
registry,
level: "error",
pluginId: "configurable",
message: "invalid config",
});
});
it("repairs incomplete registered channel metadata before storing registry entries", () => {
useNoBundledPlugins();
const plugin = writePlugin({
id: "channel-meta-repair",
filename: "channel-meta-repair.cjs",
body: `module.exports = { id: "channel-meta-repair", register(api) {
api.registerChannel({
plugin: {
id: "telegram",
meta: {
id: "telegram"
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => [],
resolveAccount: () => ({ accountId: "default" })
},
outbound: { deliveryMode: "direct" }
}
});
} };`,
});
const registry = loadRegistryFromSinglePlugin({
plugin,
pluginConfig: {
allow: ["channel-meta-repair"],
},
});
const telegram = registry.channels.find((entry) => entry.plugin.id === "telegram")?.plugin;
expect(telegram?.meta.id).toBe("telegram");
expect(telegram?.meta.label).toBe("Telegram");
expect(telegram?.meta.docsPath).toBe("/channels/telegram");
expectDiagnosticContaining({
registry,
level: "warn",
message:
'channel "telegram" registered incomplete metadata; filled missing label, selectionLabel, docsPath, blurb',
});
});
it("throws when strict plugin loading sees plugin errors", () => {
useNoBundledPlugins();
const plugin = writePlugin({
id: "configurable",
filename: "configurable.cjs",
body: `module.exports = { id: "configurable", register() {} };`,
});
expect(() =>
loadOpenClawPlugins({
cache: false,
throwOnLoadError: true,
config: {
plugins: {
enabled: true,
load: { paths: [plugin.file] },
allow: ["configurable"],
entries: {
configurable: {
enabled: true,
config: "nope" as unknown as Record<string, unknown>,
},
},
},
},
}),
).toThrow("plugin load failed: configurable: invalid config: <root>: must be object");
});
it("fails when plugin export id mismatches manifest id", () => {
useNoBundledPlugins();
const plugin = writePlugin({
id: "manifest-id",
filename: "manifest-id.cjs",
body: `module.exports = { id: "export-id", register() {} };`,
});
const registry = loadRegistryFromSinglePlugin({
plugin,
pluginConfig: {
allow: ["manifest-id"],
},
});
const loaded = registry.plugins.find((entry) => entry.id === "manifest-id");
expect(loaded?.status).toBe("error");
expect(loaded?.error).toBe(
'plugin id mismatch (config uses "manifest-id", export uses "export-id")',
);
expectDiagnosticContaining({
registry,
level: "error",
pluginId: "manifest-id",
message: 'plugin id mismatch (config uses "manifest-id", export uses "export-id")',
});
});
it("can include plugin export shape when register is missing", () => {
useNoBundledPlugins();
const plugin = writePlugin({
id: "missing-register-shape",
filename: "missing-register-shape.cjs",
body: `module.exports = { default: { default: { id: "missing-register-shape" } } };`,
});
const registry = withEnv({ OPENCLAW_PLUGIN_LOAD_DEBUG: "1" }, () =>
loadRegistryFromSinglePlugin({
plugin,
pluginConfig: {
allow: ["missing-register-shape"],
},
}),
);
const loaded = registry.plugins.find((entry) => entry.id === "missing-register-shape");
expect(loaded?.status).toBe("error");
expect(loaded?.error).toContain("plugin export missing register/activate");
expect(loaded?.error).toContain("module shape:");
expect(loaded?.error).toContain("export:object keys=default");
expect(loaded?.error).toContain("export.default:object keys=default");
});
it.each([
{
id: "wrong-channel-entry",
kind: "bundled-channel-entry",
error: "bundled channel entry requires setup-runtime loader",
},
{
id: "wrong-channel-setup-entry",
kind: "bundled-channel-setup-entry",
error: "bundled channel setup entry requires setup-runtime loader",
},
])("reports $kind loaded through the legacy plugin loader", ({ id, kind, error }) => {
useNoBundledPlugins();
const plugin = writePlugin({
id,
filename: `${id}.cjs`,
body: `module.exports = { id: ${JSON.stringify(id)}, kind: ${JSON.stringify(kind)} };`,
});
const errors: string[] = [];
const registry = loadRegistryFromSinglePlugin({
plugin,
pluginConfig: {
allow: [id],
},
options: {
logger: createErrorLogger(errors),
},
});
const loaded = registry.plugins.find((entry) => entry.id === id);
expect(loaded?.status).toBe("error");
expect(loaded?.error).toBe(error);
expectRegistryErrorDiagnostic({ registry, pluginId: id, message: error });
expect(errors).toEqual([
`[plugins] ${id} ${error}; ensure plugin is loaded via bundled channel discovery, not legacy plugin loader`,
]);
});
it("handles single-plugin channel, context engine, and cli validation", () => {
useNoBundledPlugins();
const scenarios = [
{
label: "registers channel plugins",
pluginId: "channel-demo",
body: `module.exports = { id: "channel-demo", register(api) {
api.registerChannel({
plugin: {
id: "demo",
meta: {
id: "demo",
label: "Demo",
selectionLabel: "Demo",
docsPath: "/channels/demo",
blurb: "demo channel"
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => [],
resolveAccount: () => ({ accountId: "default" })
},
outbound: { deliveryMode: "direct" }
}
});
} };`,
assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
const channel = registry.channels.find((entry) => entry.plugin.id === "demo");
expect(channel?.plugin.id).toBe("demo");
},
},
{
label: "updates duplicate channel ids during same-plugin registration",
pluginId: "channel-dup",
body: `module.exports = { id: "channel-dup", register(api) {
api.registerChannel({
plugin: {
id: "demo",
meta: {
id: "demo",
label: "Demo Override",
selectionLabel: "Demo Override",
docsPath: "/channels/demo-override",
blurb: "override"
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => [],
resolveAccount: () => ({ accountId: "default" })
},
outbound: { deliveryMode: "direct" }
}
});
api.registerChannel({
plugin: {
id: "demo",
meta: {
id: "demo",
label: "Demo Duplicate",
selectionLabel: "Demo Duplicate",
docsPath: "/channels/demo-duplicate",
blurb: "duplicate"
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => [],
resolveAccount: () => ({ accountId: "default" })
},
outbound: { deliveryMode: "direct" }
}
});
} };`,
assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
expect(countMatching(registry.channels, (entry) => entry.plugin.id === "demo")).toBe(1);
expect(
registry.channels.find((entry) => entry.plugin.id === "demo")?.plugin.meta?.label,
).toBe("Demo Duplicate");
},
},
{
label: "rejects malformed plugin context engine registration",
pluginId: "context-engine-malformed",
body: `module.exports = { id: "context-engine-malformed", register(api) {
api.registerContextEngine({ id: "broken-context" });
} };`,
assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
expectRegistryErrorDiagnostic({
registry,
pluginId: "context-engine-malformed",
message: "context engine registration missing id",
});
expect(getContextEngineRegistration("broken-context")).toBeUndefined();
},
},
{
label: "rejects plugin context engine ids reserved by core",
pluginId: "context-engine-core-collision",
body: `module.exports = { id: "context-engine-core-collision", register(api) {
api.registerContextEngine("legacy", () => ({}));
} };`,
assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
expectRegistryErrorDiagnostic({
registry,
pluginId: "context-engine-core-collision",
message: "context engine id reserved by core: legacy",
});
},
},
{
label: "rejects malformed compaction provider registration",
pluginId: "compaction-provider-malformed",
body: `module.exports = { id: "compaction-provider-malformed", register(api) {
api.registerCompactionProvider({ id: "broken-compaction", label: "Broken" });
} };`,
assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
expectRegistryErrorDiagnostic({
registry,
pluginId: "compaction-provider-malformed",
message: 'compaction provider "broken-compaction" registration missing summarize',
});
expect(getCompactionProvider("broken-compaction")).toBeUndefined();
},
},
{
label: "rejects malformed memory prompt supplement registration",
pluginId: "memory-prompt-supplement-malformed",
body: `module.exports = { id: "memory-prompt-supplement-malformed", register(api) {
api.registerMemoryPromptSupplement({ id: "broken-memory-prompt" });
} };`,
assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
expectRegistryErrorDiagnostic({
registry,
pluginId: "memory-prompt-supplement-malformed",
message: "memory prompt supplement registration missing builder",
});
expect(listMemoryPromptSupplements()).toStrictEqual([]);
},
},
{
label: "rejects malformed memory prompt preparation registration",
pluginId: "memory-prompt-preparation-malformed",
body: `module.exports = { id: "memory-prompt-preparation-malformed", register(api) {
api.registerMemoryPromptPreparation({ id: "broken-memory-prompt" });
} };`,
assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
expectRegistryErrorDiagnostic({
registry,
pluginId: "memory-prompt-preparation-malformed",
message: "memory prompt preparation registration missing prepare function",
});
expect(listMemoryPromptPreparations()).toStrictEqual([]);
},
},
{
label: "requires plugin CLI registrars to declare explicit command roots",
pluginId: "cli-missing-metadata",
body: `module.exports = { id: "cli-missing-metadata", register(api) {
api.registerCli(() => {});
} };`,
assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
expect(registry.cliRegistrars).toHaveLength(0);
expectRegistryErrorDiagnostic({
registry,
pluginId: "cli-missing-metadata",
message: "cli registration missing explicit commands metadata",
});
},
},
{
label: "registers node feature CLI commands under nodes",
pluginId: "node-cli-feature",
body: `module.exports = { id: "node-cli-feature", register(api) {
api.registerNodeCliFeature(() => {}, {
descriptors: [
{
name: "demo-node",
description: "Demo node feature",
hasSubcommands: true,
},
],
});
} };`,
assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
expect(registry.cliRegistrars).toHaveLength(1);
expect(registry.cliRegistrars[0]?.parentPath).toEqual(["nodes"]);
expect(registry.cliRegistrars[0]?.commands).toEqual(["demo-node"]);
expect(registry.cliRegistrars[0]?.descriptors).toEqual([
{
name: "demo-node",
description: "Demo node feature",
hasSubcommands: true,
},
]);
},
},
] as const;
runSinglePluginRegistryScenarios(scenarios);
});
it("registers plugin http routes", () => {
useNoBundledPlugins();
const scenarios = [
{
label: "defaults exact match",
pluginId: "http-route-demo",
routeOptions:
'{ path: "/demo", auth: "gateway", handler: async (_req, res) => { res.statusCode = 200; res.end("ok"); } }',
expectedPath: "/demo",
expectedAuth: "gateway",
expectedMatch: "exact",
assert: expectRegisteredHttpRoute,
},
{
label: "keeps explicit auth and match options",
pluginId: "http-demo",
routeOptions:
'{ path: "/webhook", auth: "plugin", match: "prefix", handler: async () => false }',
expectedPath: "/webhook",
expectedAuth: "plugin",
expectedMatch: "prefix",
assert: expectRegisteredHttpRoute,
},
] as const;
runSinglePluginRegistryScenarios(
scenarios.map((scenario) =>
Object.assign({}, scenario, {
body: `module.exports = { id: "${scenario.pluginId}", register(api) {
api.registerHttpRoute(${scenario.routeOptions});
} };`,
}),
),
);
});
it("rejects duplicate plugin registrations", () => {
useNoBundledPlugins();
const scenarios = [
{
label: "plugin-visible hook names",
ownerA: "hook-owner-a",
ownerB: "hook-owner-b",
buildBody: (ownerId: string) => `module.exports = { id: "${ownerId}", register(api) {
api.registerHook("gateway:startup", () => {}, { name: "shared-hook" });
} };`,
selectCount: (registry: ReturnType<typeof loadOpenClawPlugins>) =>
countMatching(registry.hooks, (entry) => entry.entry.hook.name === "shared-hook"),
duplicateMessage: "hook already registered: shared-hook (hook-owner-a)",
assert: expectDuplicateRegistrationResult,
},
{
label: "plugin service ids",
ownerA: "service-owner-a",
ownerB: "service-owner-b",
buildBody: (ownerId: string) => `module.exports = { id: "${ownerId}", register(api) {
api.registerService({ id: "shared-service", start() {} });
} };`,
selectCount: (registry: ReturnType<typeof loadOpenClawPlugins>) =>
countMatching(registry.services, (entry) => entry.service.id === "shared-service"),
duplicateMessage: "service already registered: shared-service (service-owner-a)",
assert: expectDuplicateRegistrationResult,
},
{
label: "gateway discovery service ids",
ownerA: "discovery-owner-a",
ownerB: "discovery-owner-b",
buildBody: (ownerId: string) => `module.exports = { id: "${ownerId}", register(api) {
api.registerGatewayDiscoveryService({ id: "shared-discovery", advertise() {} });
} };`,
selectCount: (registry: ReturnType<typeof loadOpenClawPlugins>) =>
registry.gatewayDiscoveryServices.filter(
(entry) => entry.service.id === "shared-discovery",
).length,
duplicateMessage:
"gateway discovery service already registered: shared-discovery (discovery-owner-a)",
assertPrimaryOwner: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
expect(
registry.plugins.find((entry) => entry.id === "discovery-owner-a")
?.gatewayDiscoveryServiceIds,
).toEqual(["shared-discovery"]);
},
assert: expectDuplicateRegistrationResult,
},
{
label: "plugin context engine ids",
ownerA: "context-engine-owner-a",
ownerB: "context-engine-owner-b",
buildBody: (ownerId: string) => `module.exports = { id: "${ownerId}", register(api) {
api.registerContextEngine("shared-context-engine-loader-test", () => ({}));
} };`,
selectCount: () => 1,
duplicateMessage:
"context engine already registered: shared-context-engine-loader-test (plugin:context-engine-owner-a)",
assertPrimaryOwner: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
expect(
registry.plugins.find((entry) => entry.id === "context-engine-owner-a")
?.contextEngineIds,
).toEqual(["shared-context-engine-loader-test"]);
},
assert: expectDuplicateRegistrationResult,
},
{
label: "plugin CLI command roots",
ownerA: "cli-owner-a",
ownerB: "cli-owner-b",
buildBody: (ownerId: string) => `module.exports = { id: "${ownerId}", register(api) {
api.registerCli(() => {}, { commands: ["shared-cli"] });
} };`,
selectCount: (registry: ReturnType<typeof loadOpenClawPlugins>) =>
registry.cliRegistrars.length,
duplicateMessage: "cli command already registered: shared-cli (cli-owner-a)",
assertPrimaryOwner: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
expect(registry.cliRegistrars[0]?.pluginId).toBe("cli-owner-a");
},
assert: expectDuplicateRegistrationResult,
},
] as const;
runRegistryScenarios(scenarios, (scenario) => {
const first = writePlugin({
id: scenario.ownerA,
filename: `${scenario.ownerA}.cjs`,
body: scenario.buildBody(scenario.ownerA),
});
const second = writePlugin({
id: scenario.ownerB,
filename: `${scenario.ownerB}.cjs`,
body: scenario.buildBody(scenario.ownerB),
});
return loadRegistryFromAllowedPlugins([first, second]);
});
});
it("allows the same plugin to register the same service id twice", () => {
useNoBundledPlugins();
const plugin = writePlugin({
id: "service-owner-self",
filename: "service-owner-self.cjs",
body: `module.exports = { id: "service-owner-self", register(api) {
api.registerService({ id: "shared-service", start() {} });
api.registerService({ id: "shared-service", start() {} });
} };`,
});
const registry = loadRegistryFromSinglePlugin({
plugin,
pluginConfig: {
allow: ["service-owner-self"],
},
});
expect(countMatching(registry.services, (entry) => entry.service.id === "shared-service")).toBe(
1,
);
expectNoDiagnosticContaining({
registry,
message: "service already registered: shared-service",
});
});
it("tracks regular services and gateway discovery services separately", () => {
useNoBundledPlugins();
const plugin = writePlugin({
id: "split-service-owner",
filename: "split-service-owner.cjs",
body: `module.exports = { id: "split-service-owner", register(api) {
api.registerService({ id: "shared-service", start() {} });
api.registerGatewayDiscoveryService({ id: "shared-service", advertise() {} });
} };`,
});
const registry = loadRegistryFromSinglePlugin({
plugin,
pluginConfig: {
allow: ["split-service-owner"],
},
});
const record = registry.plugins.find((entry) => entry.id === "split-service-owner");
expect(record?.services).toEqual(["shared-service"]);
expect(record?.gatewayDiscoveryServiceIds).toEqual(["shared-service"]);
expect(registry.services).toHaveLength(1);
expect(registry.gatewayDiscoveryServices).toHaveLength(1);
expect(registry.diagnostics).toStrictEqual([]);
});
it("rewrites removed registerHttpHandler failures into migration diagnostics", () => {
useNoBundledPlugins();
const plugin = writePlugin({
id: "http-handler-legacy",
filename: "http-handler-legacy.cjs",
body: `module.exports = { id: "http-handler-legacy", register(api) {
api.registerHttpHandler({ path: "/legacy", handler: async () => true });
} };`,
});
const errors: string[] = [];
const registry = loadRegistryFromSinglePlugin({
plugin,
pluginConfig: {
allow: ["http-handler-legacy"],
},
options: {
logger: createErrorLogger(errors),
},
});
const loaded = registry.plugins.find((entry) => entry.id === "http-handler-legacy");
expect(loaded?.status).toBe("error");
expect(loaded?.error).toContain("api.registerHttpHandler(...) was removed");
expect(loaded?.error).toContain("api.registerHttpRoute(...)");
expect(loaded?.error).toContain("registerPluginHttpRoute(...)");
expectDiagnosticContaining({
registry,
message: "api.registerHttpHandler(...) was removed",
});
expect(
errors.some((message) => message.includes("api.registerHttpHandler(...) was removed")),
).toBe(true);
});
it("does not rewrite unrelated registerHttpHandler helper failures", () => {
useNoBundledPlugins();
const plugin = writePlugin({
id: "http-handler-local-helper",
filename: "http-handler-local-helper.cjs",
body: `module.exports = { id: "http-handler-local-helper", register() {
const registerHttpHandler = undefined;
registerHttpHandler();
} };`,
});
const registry = loadRegistryFromSinglePlugin({
plugin,
pluginConfig: {
allow: ["http-handler-local-helper"],
},
});
const loaded = registry.plugins.find((entry) => entry.id === "http-handler-local-helper");
expect(loaded?.status).toBe("error");
expect(loaded?.error).not.toContain("api.registerHttpHandler(...) was removed");
});
it("enforces plugin http route validation and conflict rules", () => {
useNoBundledPlugins();
const scenarios = [
{
label: "missing auth is rejected",
buildPlugins: () => [
writePlugin({
id: "http-route-missing-auth",
filename: "http-route-missing-auth.cjs",
body: `module.exports = { id: "http-route-missing-auth", register(api) {
api.registerHttpRoute({ path: "/demo", handler: async () => true });
} };`,
}),
],
assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
expect(
registry.httpRoutes.find((entry) => entry.pluginId === "http-route-missing-auth"),
).toBeUndefined();
expectDiagnosticContaining({
registry,
message: "http route registration missing or invalid auth",
});
},
},
{
label: "same plugin can implicitly replace its own route",
buildPlugins: () => [
writePlugin({
id: "http-route-replace-self",
filename: "http-route-replace-self.cjs",
body: `module.exports = { id: "http-route-replace-self", register(api) {
api.registerHttpRoute({ path: "/demo", auth: "plugin", handler: async () => false });
api.registerHttpRoute({ path: "/demo", auth: "plugin", handler: async () => true });
} };`,
}),
],
assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
const routes = registry.httpRoutes.filter(
(entry) => entry.pluginId === "http-route-replace-self",
);
expect(routes).toHaveLength(1);
expect(routes[0]?.path).toBe("/demo");
expect(registry.diagnostics).toStrictEqual([]);
},
},
{
label: "cross-plugin replaceExisting is rejected",
buildPlugins: () => [
writePlugin({
id: "http-route-owner-a",
filename: "http-route-owner-a.cjs",
body: `module.exports = { id: "http-route-owner-a", register(api) {
api.registerHttpRoute({ path: "/demo", auth: "plugin", handler: async () => false });
} };`,
}),
writePlugin({
id: "http-route-owner-b",
filename: "http-route-owner-b.cjs",
body: `module.exports = { id: "http-route-owner-b", register(api) {
api.registerHttpRoute({ path: "/demo", auth: "plugin", replaceExisting: true, handler: async () => true });
} };`,
}),
],
assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
const route = registry.httpRoutes.find((entry) => entry.path === "/demo");
expect(route?.pluginId).toBe("http-route-owner-a");
expectDiagnosticContaining({
registry,
message: "http route replacement rejected",
});
},
},
{
label: "mixed-auth overlaps are rejected",
buildPlugins: () => [
writePlugin({
id: "http-route-overlap",
filename: "http-route-overlap.cjs",
body: `module.exports = { id: "http-route-overlap", register(api) {
api.registerHttpRoute({ path: "/plugin/secure", auth: "gateway", match: "prefix", handler: async () => true });
api.registerHttpRoute({ path: "/plugin/secure/report", auth: "plugin", match: "exact", handler: async () => true });
} };`,
}),
],
assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
const routes = registry.httpRoutes.filter(
(entry) => entry.pluginId === "http-route-overlap",
);
expect(routes).toHaveLength(1);
expect(routes[0]?.path).toBe("/plugin/secure");
expectDiagnosticContaining({
registry,
message: "http route overlap rejected",
});
},
},
{
label: "same-auth overlaps are allowed",
buildPlugins: () => [
writePlugin({
id: "http-route-overlap-same-auth",
filename: "http-route-overlap-same-auth.cjs",
body: `module.exports = { id: "http-route-overlap-same-auth", register(api) {
api.registerHttpRoute({ path: "/plugin/public", auth: "plugin", match: "prefix", handler: async () => true });
api.registerHttpRoute({ path: "/plugin/public/report", auth: "plugin", match: "exact", handler: async () => true });
} };`,
}),
],
assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
const routes = registry.httpRoutes.filter(
(entry) => entry.pluginId === "http-route-overlap-same-auth",
);
expect(routes).toHaveLength(2);
expect(registry.diagnostics).toStrictEqual([]);
},
},
] as const;
runRegistryScenarios(scenarios, (scenario) =>
loadRegistryFromScenarioPlugins(scenario.buildPlugins()),
);
});
it("respects explicit disable in config", () => {
useNoBundledPlugins();
const plugin = writePlugin({
id: "config-disable",
body: `module.exports = { id: "config-disable", register() {} };`,
});
const registry = loadOpenClawPlugins({
cache: false,
config: {
plugins: {
load: { paths: [plugin.file] },
entries: {
"config-disable": { enabled: false },
},
},
},
});
const disabled = registry.plugins.find((entry) => entry.id === "config-disable");
expect(disabled?.status).toBe("disabled");
});
it("loads bundled channel entries through nested default export wrappers", () => {
useNoBundledPlugins();
const pluginDir = makeTempDir();
const fullMarker = path.join(pluginDir, "full-loaded.txt");
fs.writeFileSync(
path.join(pluginDir, "package.json"),
JSON.stringify(
{
name: "@openclaw/nested-default-channel",
openclaw: {
extensions: ["./index.cjs"],
},
},
null,
2,
),
"utf-8",
);
fs.writeFileSync(
path.join(pluginDir, "openclaw.plugin.json"),
JSON.stringify(
{
id: "nested-default-channel",
configSchema: EMPTY_PLUGIN_SCHEMA,
channels: ["nested-default-channel"],
},
null,
2,
),
"utf-8",
);
fs.writeFileSync(
path.join(pluginDir, "index.cjs"),
`module.exports = {
default: {
default: {
id: "nested-default-channel",
kind: "bundled-channel-entry",
name: "Nested Default Channel",
description: "interop-wrapped bundled channel entry",
register(api) {
require("node:fs").writeFileSync(${JSON.stringify(fullMarker)}, "loaded", "utf-8");
api.registerChannel({
plugin: {
id: "nested-default-channel",
meta: {
id: "nested-default-channel",
label: "Nested Default Channel",
selectionLabel: "Nested Default Channel",
docsPath: "/channels/nested-default-channel",
blurb: "interop-wrapped bundled channel entry",
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => ["default"],
resolveAccount: () => ({ accountId: "default", token: "configured" }),
},
outbound: { deliveryMode: "direct" },
},
});
},
},
},
};`,
"utf-8",
);
const registry = loadOpenClawPlugins({
cache: false,
config: {
channels: {
"nested-default-channel": {
enabled: true,
token: "configured",
},
},
plugins: {
load: { paths: [pluginDir] },
allow: ["nested-default-channel"],
},
},
});
expect(fs.existsSync(fullMarker)).toBe(true);
expect(registry.plugins.find((entry) => entry.id === "nested-default-channel")?.status).toBe(
"loaded",
);
expect(registry.channels.map((entry) => entry.plugin.id)).toContain("nested-default-channel");
});
it("does not treat manifest channel ids as scoped plugin id matches", () => {
useNoBundledPlugins();
const target = writePlugin({
id: "target-plugin",
filename: "target-plugin.cjs",
body: `module.exports = { id: "target-plugin", register() {} };`,
});
const unrelated = writePlugin({
id: "unrelated-plugin",
filename: "unrelated-plugin.cjs",
body: `module.exports = { id: "unrelated-plugin", register() { throw new Error("unrelated plugin should not load"); } };`,
});
fs.writeFileSync(
path.join(unrelated.dir, "openclaw.plugin.json"),
JSON.stringify(
{
id: "unrelated-plugin",
configSchema: EMPTY_PLUGIN_SCHEMA,
channels: ["target-plugin"],
},
null,
2,
),
"utf-8",
);
const registry = loadOpenClawPlugins({
cache: false,
config: {
plugins: {
load: { paths: [target.file, unrelated.file] },
allow: ["target-plugin", "unrelated-plugin"],
entries: {
"target-plugin": { enabled: true },
"unrelated-plugin": { enabled: true },
},
},
},
onlyPluginIds: ["target-plugin"],
});
expect(registry.plugins.map((entry) => entry.id)).toEqual(["target-plugin"]);
});
it("does not setup-load an explicitly disabled channel plugin even when the caller scopes to it", () => {
useNoBundledPlugins();
const marker = path.join(makeTempDir(), "lazy-channel-imported.txt");
const plugin = writePlugin({
id: "lazy-channel-plugin",
filename: "lazy-channel.cjs",
body: `require("node:fs").writeFileSync(${JSON.stringify(marker)}, "loaded", "utf-8");
module.exports = {
id: "lazy-channel-plugin",
register(api) {
api.registerChannel({
plugin: {
id: "lazy-channel",
meta: {
id: "lazy-channel",
label: "Lazy Channel",
selectionLabel: "Lazy Channel",
docsPath: "/channels/lazy-channel",
blurb: "lazy test channel",
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => [],
resolveAccount: () => ({ accountId: "default" }),
},
outbound: { deliveryMode: "direct" },
},
});
},
};`,
});
fs.writeFileSync(
path.join(plugin.dir, "openclaw.plugin.json"),
JSON.stringify(
{
id: "lazy-channel-plugin",
configSchema: EMPTY_PLUGIN_SCHEMA,
channels: ["lazy-channel"],
},
null,
2,
),
"utf-8",
);
const config = {
plugins: {
load: { paths: [plugin.file] },
allow: ["lazy-channel-plugin"],
entries: {
"lazy-channel-plugin": { enabled: false },
},
},
};
const registry = loadOpenClawPlugins({
cache: false,
config,
});
expect(fs.existsSync(marker)).toBe(false);
expect(registry.channelSetups).toHaveLength(0);
expect(registry.plugins.find((entry) => entry.id === "lazy-channel-plugin")?.status).toBe(
"disabled",
);
const broadSetupRegistry = loadOpenClawPlugins({
cache: false,
config,
includeSetupOnlyChannelPlugins: true,
});
expect(fs.existsSync(marker)).toBe(false);
expect(broadSetupRegistry.channelSetups).toHaveLength(0);
expect(broadSetupRegistry.channels).toHaveLength(0);
expect(
broadSetupRegistry.plugins.find((entry) => entry.id === "lazy-channel-plugin")?.status,
).toBe("disabled");
const scopedSetupRegistry = loadOpenClawPlugins({
cache: false,
config,
includeSetupOnlyChannelPlugins: true,
onlyPluginIds: ["lazy-channel-plugin"],
});
expect(fs.existsSync(marker)).toBe(false);
expect(scopedSetupRegistry.channelSetups).toHaveLength(0);
expect(scopedSetupRegistry.channels).toHaveLength(0);
expect(
scopedSetupRegistry.plugins.find((entry) => entry.id === "lazy-channel-plugin")?.status,
).toBe("disabled");
});
it("blocks untrusted setup-only workspace channel plugins when explicitly scoped", () => {
useNoBundledPlugins();
const marker = path.join(makeTempDir(), "workspace-setup-only-loaded.txt");
const { workspaceDir, workspacePluginDir } = writeWorkspacePlugin({
id: "workspace-shadow",
body: `require("node:fs").writeFileSync(${JSON.stringify(marker)}, "loaded", "utf-8");
module.exports = {
id: "workspace-shadow",
register(api) {
api.registerChannel({
plugin: {
id: "workspace-shadow",
meta: {
id: "workspace-shadow",
label: "Workspace Shadow",
selectionLabel: "Workspace Shadow",
docsPath: "/channels/workspace-shadow",
blurb: "workspace shadow",
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => [],
resolveAccount: () => undefined,
},
outbound: { deliveryMode: "direct" },
},
});
},
};`,
});
fs.writeFileSync(
path.join(workspacePluginDir, "openclaw.plugin.json"),
JSON.stringify(
{
id: "workspace-shadow",
configSchema: EMPTY_PLUGIN_SCHEMA,
channels: ["workspace-shadow"],
},
null,
2,
),
"utf-8",
);
const registry = loadOpenClawPlugins({
cache: false,
workspaceDir,
includeSetupOnlyChannelPlugins: true,
forceSetupOnlyChannelPlugins: true,
onlyPluginIds: ["workspace-shadow"],
config: {
plugins: {
enabled: true,
},
},
});
expect(fs.existsSync(marker)).toBe(false);
expect(registry.channelSetups).toHaveLength(0);
expect(registry.channels).toHaveLength(0);
expect(registry.plugins.find((entry) => entry.id === "workspace-shadow")).toMatchObject({
status: "disabled",
error: "workspace plugin (disabled by default)",
});
});
it("keeps trusted setup-only workspace channel plugins available when explicitly scoped", () => {
useNoBundledPlugins();
const marker = path.join(makeTempDir(), "trusted-workspace-setup-only-loaded.txt");
const { workspaceDir, workspacePluginDir } = writeWorkspacePlugin({
id: "trusted-workspace-shadow",
body: `require("node:fs").writeFileSync(${JSON.stringify(marker)}, "loaded", "utf-8");
module.exports = {
id: "trusted-workspace-shadow",
register(api) {
api.registerChannel({
plugin: {
id: "telegram",
meta: {
id: "telegram",
label: "Trusted Workspace Telegram",
selectionLabel: "Trusted Workspace Telegram",
docsPath: "/channels/telegram",
blurb: "trusted workspace telegram",
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => [],
resolveAccount: () => ({ accountId: "default" }),
},
outbound: { deliveryMode: "direct" },
},
});
},
};`,
});
fs.writeFileSync(
path.join(workspacePluginDir, "openclaw.plugin.json"),
JSON.stringify(
{
id: "trusted-workspace-shadow",
configSchema: EMPTY_PLUGIN_SCHEMA,
channels: ["telegram"],
},
null,
2,
),
"utf-8",
);
const registry = loadOpenClawPlugins({
cache: false,
workspaceDir,
includeSetupOnlyChannelPlugins: true,
forceSetupOnlyChannelPlugins: true,
onlyPluginIds: ["trusted-workspace-shadow"],
config: {
plugins: {
enabled: true,
allow: ["trusted-workspace-shadow"],
},
},
});
expect(fs.existsSync(marker)).toBe(true);
expect(registry.channelSetups.map((entry) => entry.plugin.meta.label)).toEqual([
"Trusted Workspace Telegram",
]);
expect(registry.channels).toHaveLength(0);
expect(registry.plugins.find((entry) => entry.id === "trusted-workspace-shadow")?.status).toBe(
"loaded",
);
});
it("does not setup-load an untrusted config-origin channel plugin when the caller scopes to it", () => {
useNoBundledPlugins();
const marker = path.join(makeTempDir(), "untrusted-load-path-channel-imported.txt");
const plugin = writePlugin({
id: "untrusted-load-path-channel",
filename: "untrusted-load-path-channel.cjs",
body: `require("node:fs").writeFileSync(${JSON.stringify(marker)}, "loaded", "utf-8");
module.exports = {
id: "untrusted-load-path-channel",
register(api) {
api.registerChannel({
plugin: {
id: "untrusted-load-path-channel",
meta: {
id: "untrusted-load-path-channel",
label: "Untrusted Load Path Channel",
selectionLabel: "Untrusted Load Path Channel",
docsPath: "/channels/untrusted-load-path-channel",
blurb: "untrusted load-path setup gate",
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => [],
resolveAccount: () => ({ accountId: "default" }),
},
outbound: { deliveryMode: "direct" },
},
});
},
};`,
});
fs.writeFileSync(
path.join(plugin.dir, "openclaw.plugin.json"),
JSON.stringify(
{
id: "untrusted-load-path-channel",
configSchema: EMPTY_PLUGIN_SCHEMA,
channels: ["untrusted-load-path-channel"],
},
null,
2,
),
"utf-8",
);
const scopedSetupRegistry = loadOpenClawPlugins({
cache: false,
config: {
plugins: {
load: { paths: [plugin.file] },
},
},
includeSetupOnlyChannelPlugins: true,
onlyPluginIds: ["untrusted-load-path-channel"],
});
expect(fs.existsSync(marker)).toBe(false);
expect(scopedSetupRegistry.channelSetups).toHaveLength(0);
expect(scopedSetupRegistry.channels).toHaveLength(0);
expect(
scopedSetupRegistry.plugins.find((entry) => entry.id === "untrusted-load-path-channel")
?.status,
).toBe("disabled");
});
it("does not setup-load a denylisted config-origin channel plugin even when explicitly allowed", () => {
useNoBundledPlugins();
const marker = path.join(makeTempDir(), "denylisted-load-path-channel-imported.txt");
const plugin = writePlugin({
id: "denylisted-load-path-channel",
filename: "denylisted-load-path-channel.cjs",
body: `require("node:fs").writeFileSync(${JSON.stringify(marker)}, "loaded", "utf-8");
module.exports = {
id: "denylisted-load-path-channel",
register(api) {
api.registerChannel({
plugin: {
id: "denylisted-load-path-channel",
meta: {
id: "denylisted-load-path-channel",
label: "Denylisted Load Path Channel",
selectionLabel: "Denylisted Load Path Channel",
docsPath: "/channels/denylisted-load-path-channel",
blurb: "denylisted load-path setup gate",
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => [],
resolveAccount: () => ({ accountId: "default" }),
},
outbound: { deliveryMode: "direct" },
},
});
},
};`,
});
fs.writeFileSync(
path.join(plugin.dir, "openclaw.plugin.json"),
JSON.stringify(
{
id: "denylisted-load-path-channel",
configSchema: EMPTY_PLUGIN_SCHEMA,
channels: ["denylisted-load-path-channel"],
},
null,
2,
),
"utf-8",
);
const scopedSetupRegistry = loadOpenClawPlugins({
cache: false,
config: {
plugins: {
load: { paths: [plugin.file] },
allow: ["denylisted-load-path-channel"],
deny: ["denylisted-load-path-channel"],
},
},
includeSetupOnlyChannelPlugins: true,
onlyPluginIds: ["denylisted-load-path-channel"],
});
expect(fs.existsSync(marker)).toBe(false);
expect(scopedSetupRegistry.channelSetups).toHaveLength(0);
expect(scopedSetupRegistry.channels).toHaveLength(0);
expect(
scopedSetupRegistry.plugins.find((entry) => entry.id === "denylisted-load-path-channel")
?.status,
).toBe("disabled");
});
it("does not setup-load an untrusted global channel plugin when the caller scopes to it", () => {
useNoBundledPlugins();
const marker = path.join(makeTempDir(), "untrusted-global-channel-imported.txt");
withStateDir((stateDir) => {
const globalDir = path.join(stateDir, "extensions", "untrusted-global-channel");
mkdirSafe(globalDir);
fs.writeFileSync(
path.join(globalDir, "index.cjs"),
`require("node:fs").writeFileSync(${JSON.stringify(marker)}, "loaded", "utf-8");
module.exports = {
id: "untrusted-global-channel",
register(api) {
api.registerChannel({
plugin: {
id: "untrusted-global-channel",
meta: {
id: "untrusted-global-channel",
label: "Untrusted Global Channel",
selectionLabel: "Untrusted Global Channel",
docsPath: "/channels/untrusted-global-channel",
blurb: "untrusted global setup gate",
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => [],
resolveAccount: () => ({ accountId: "default" }),
},
outbound: { deliveryMode: "direct" },
},
});
},
};`,
"utf-8",
);
fs.writeFileSync(
path.join(globalDir, "openclaw.plugin.json"),
JSON.stringify(
{
id: "untrusted-global-channel",
configSchema: EMPTY_PLUGIN_SCHEMA,
channels: ["untrusted-global-channel"],
},
null,
2,
),
"utf-8",
);
fs.writeFileSync(
path.join(globalDir, "package.json"),
JSON.stringify(
{
name: "@openclaw/untrusted-global-channel",
version: "0.0.0-test",
main: "./index.cjs",
openclaw: {
extensions: ["./index.cjs"],
},
},
null,
2,
),
"utf-8",
);
const scopedSetupRegistry = loadOpenClawPlugins({
cache: false,
config: {
plugins: {
enabled: true,
},
},
includeSetupOnlyChannelPlugins: true,
onlyPluginIds: ["untrusted-global-channel"],
});
expect(fs.existsSync(marker)).toBe(false);
expect(scopedSetupRegistry.channelSetups).toHaveLength(0);
expect(scopedSetupRegistry.channels).toHaveLength(0);
expect(
scopedSetupRegistry.plugins.find((entry) => entry.id === "untrusted-global-channel")
?.status,
).toBe("disabled");
});
});
});
/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */