Skip to content

Commit 8806ef8

Browse files
committed
refactor: remove remaining channel and gateway boundary leaks
1 parent 5cadf06 commit 8806ef8

12 files changed

Lines changed: 344 additions & 343 deletions

src/channels/plugins/contracts/channel-import-guardrails.test.ts

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,24 @@ import { dirname, resolve } from "node:path";
33
import { fileURLToPath } from "node:url";
44
import { describe, expect, it } from "vitest";
55
import { classifyBundledExtensionSourcePath } from "../../../../scripts/lib/extension-source-classifier.mjs";
6-
import {
7-
BUNDLED_PLUGIN_PATH_PREFIX,
8-
BUNDLED_PLUGIN_ROOT_DIR,
9-
bundledPluginFile,
10-
} from "../../../../test/helpers/bundled-plugin-paths.js";
6+
import { loadPluginManifestRegistry } from "../../../plugins/manifest-registry.js";
117
import { GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES } from "../../../plugins/public-artifacts.js";
128

139
const ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
1410
const REPO_ROOT = resolve(ROOT_DIR, "..");
1511
const ALLOWED_EXTENSION_PUBLIC_SURFACES = new Set(GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES);
1612
ALLOWED_EXTENSION_PUBLIC_SURFACES.add("test-api.js");
17-
const BUNDLED_EXTENSION_IDS = readdirSync(resolve(REPO_ROOT, "extensions"), { withFileTypes: true })
18-
.filter((entry) => entry.isDirectory() && entry.name !== "shared")
19-
.map((entry) => entry.name)
20-
.toSorted((left, right) => right.length - left.length);
13+
const BUNDLED_PLUGIN_ROOT_DIR = "extensions";
14+
const bundledPluginRecords = loadPluginManifestRegistry({
15+
cache: true,
16+
config: {},
17+
}).plugins.filter((plugin) => plugin.origin === "bundled");
18+
const bundledPluginRoots = new Map(
19+
bundledPluginRecords.map((plugin) => [plugin.id, plugin.rootDir] as const),
20+
);
21+
const BUNDLED_EXTENSION_IDS = [...bundledPluginRoots.keys()].toSorted(
22+
(left, right) => right.length - left.length,
23+
);
2124
const GUARDED_CHANNEL_EXTENSIONS = new Set([
2225
"bluebubbles",
2326
"discord",
@@ -44,6 +47,14 @@ const GUARDED_CHANNEL_EXTENSIONS = new Set([
4447
// Shared config validation intentionally consumes this curated Telegram contract.
4548
const ALLOWED_CORE_CHANNEL_SDK_SUBPATHS = new Set(["telegram-command-config"]);
4649

50+
function bundledPluginFile(pluginId: string, relativePath: string): string {
51+
const rootDir = bundledPluginRoots.get(pluginId);
52+
if (!rootDir) {
53+
throw new Error(`missing bundled plugin root for ${pluginId}`);
54+
}
55+
return normalizePath(resolve(rootDir, relativePath));
56+
}
57+
4758
type GuardedSource = {
4859
path: string;
4960
forbiddenPatterns: RegExp[];
@@ -320,18 +331,18 @@ function readSetupBarrelImportBlock(path: string): string {
320331
}
321332

322333
function collectExtensionSourceFiles(): string[] {
323-
const extensionsDir = normalizePath(resolve(ROOT_DIR, "..", "extensions"));
324-
const sharedExtensionsDir = normalizePath(resolve(extensionsDir, "shared"));
325-
extensionSourceFilesCache = collectSourceFiles(extensionSourceFilesCache, {
326-
rootDir: resolve(ROOT_DIR, "..", "extensions"),
327-
shouldSkipPath: (normalizedFullPath) =>
328-
normalizedFullPath.includes(sharedExtensionsDir) ||
329-
normalizedFullPath.includes(`${extensionsDir}/shared/`),
330-
shouldSkipEntry: ({ entryName, normalizedFullPath }) =>
331-
classifyBundledExtensionSourcePath(normalizedFullPath).isTestLike ||
332-
entryName === "api.ts" ||
333-
entryName === "runtime-api.ts",
334-
});
334+
if (extensionSourceFilesCache) {
335+
return extensionSourceFilesCache;
336+
}
337+
extensionSourceFilesCache = bundledPluginRecords.flatMap((plugin) =>
338+
collectSourceFiles(undefined, {
339+
rootDir: plugin.rootDir,
340+
shouldSkipEntry: ({ entryName, normalizedFullPath }) =>
341+
classifyBundledExtensionSourcePath(normalizedFullPath).isTestLike ||
342+
entryName === "api.ts" ||
343+
entryName === "runtime-api.ts",
344+
}),
345+
);
335346
return extensionSourceFilesCache;
336347
}
337348

@@ -361,8 +372,12 @@ function collectCoreSourceFiles(): string[] {
361372

362373
function collectExtensionFiles(extensionId: string): string[] {
363374
const cached = extensionFilesCache.get(extensionId);
375+
const rootDir = bundledPluginRoots.get(extensionId);
376+
if (!rootDir) {
377+
return [];
378+
}
364379
const files = collectSourceFiles(cached, {
365-
rootDir: resolve(ROOT_DIR, "..", "extensions", extensionId),
380+
rootDir,
366381
shouldSkipEntry: ({ entryName, normalizedFullPath }) =>
367382
classifyBundledExtensionSourcePath(normalizedFullPath).isTestLike ||
368383
entryName === "runtime-api.ts",
@@ -406,7 +421,7 @@ function getSourceAnalysis(path: string): SourceAnalysis {
406421
text,
407422
importSpecifiers,
408423
extensionImports: importSpecifiers.filter((specifier) =>
409-
specifier.includes(BUNDLED_PLUGIN_PATH_PREFIX),
424+
specifier.includes(`/${BUNDLED_PLUGIN_ROOT_DIR}/`),
410425
),
411426
} satisfies SourceAnalysis;
412427
sourceAnalysisCache.set(fullPath, analysis);

src/channels/plugins/session-conversation.bundled-fallback.test.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,6 @@ vi.mock("../../plugin-sdk/facade-runtime.js", async () => {
2727
};
2828
});
2929

30-
vi.mock("../../plugins/bundled-plugin-metadata.js", async () => {
31-
const actual = await vi.importActual<typeof import("../../plugins/bundled-plugin-metadata.js")>(
32-
"../../plugins/bundled-plugin-metadata.js",
33-
);
34-
return {
35-
...actual,
36-
resolveBundledPluginPublicSurfacePath: ({ dirName }: { dirName: string }) =>
37-
dirName === fallbackState.activeDirName ? `/tmp/${dirName}/session-key-api.js` : null,
38-
};
39-
});
40-
4130
import { resolveSessionConversationRef } from "./session-conversation.js";
4231

4332
describe("session conversation bundled fallback", () => {

src/channels/plugins/session-conversation.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import { fileURLToPath } from "node:url";
21
import { tryLoadActivatedBundledPluginPublicSurfaceModuleSync } from "../../plugin-sdk/facade-runtime.js";
3-
import { resolveBundledPluginsDir } from "../../plugins/bundled-dir.js";
4-
import { resolveBundledPluginPublicSurfacePath } from "../../plugins/bundled-plugin-metadata.js";
52
import {
63
parseRawSessionConversationRef,
74
parseThreadSessionSuffix,
@@ -47,7 +44,6 @@ type BundledSessionKeyModule = {
4744
) => SessionConversationHookResult | null;
4845
};
4946

50-
const OPENCLAW_PACKAGE_ROOT = fileURLToPath(new URL("../../..", import.meta.url));
5147
const SESSION_KEY_API_ARTIFACT_BASENAME = "session-key-api.js";
5248

5349
type NormalizedSessionConversationResolution = ResolvedSessionConversation & {
@@ -140,16 +136,6 @@ function resolveBundledSessionConversationFallback(params: {
140136
rawId: string;
141137
}): NormalizedSessionConversationResolution | null {
142138
const dirName = normalizeResolvedChannel(params.channel);
143-
if (
144-
!resolveBundledPluginPublicSurfacePath({
145-
rootDir: OPENCLAW_PACKAGE_ROOT,
146-
bundledPluginsDir: resolveBundledPluginsDir(),
147-
dirName,
148-
artifactBasename: SESSION_KEY_API_ARTIFACT_BASENAME,
149-
})
150-
) {
151-
return null;
152-
}
153139
let resolveSessionConversation: BundledSessionKeyModule["resolveSessionConversation"];
154140
try {
155141
resolveSessionConversation =

src/gateway/server.agent.gateway-server-agent.mocks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { vi } from "vitest";
22
import { createEmptyPluginRegistry, type PluginRegistry } from "../plugins/registry.js";
33
import { setActivePluginRegistry } from "../plugins/runtime.js";
4-
import { setTestPluginRegistry } from "./test-helpers.mocks.js";
4+
import { setTestPluginRegistry } from "./test-helpers.plugin-registry.js";
55

66
export const registryState: { registry: PluginRegistry } = {
77
registry: createEmptyPluginRegistry(),

src/gateway/server.chat.gateway-server-chat.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
withGatewayServer,
1919
writeSessionStore,
2020
} from "./test-helpers.js";
21-
import { agentCommand } from "./test-helpers.mocks.js";
21+
import { agentCommand } from "./test-helpers.runtime-state.js";
2222
import { installConnectedControlUiServerSuite } from "./test-with-server.js";
2323

2424
installGatewayTestHooks({ scope: "suite" });

src/gateway/server.preauth-hardening.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { MAX_PREAUTH_PAYLOAD_BYTES } from "./server-constants.js";
66
import { attachGatewayUpgradeHandler, createGatewayHttpServer } from "./server-http.js";
77
import { createPreauthConnectionBudget } from "./server/preauth-connection-budget.js";
88
import type { GatewayWsClient } from "./server/ws-types.js";
9-
import { testState } from "./test-helpers.mocks.js";
9+
import { testState } from "./test-helpers.runtime-state.js";
1010
import {
1111
createGatewaySuiteHarness,
1212
installGatewayTestHooks,

src/gateway/session-message-events.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { afterEach, describe, expect, test, vi } from "vitest";
55
import { appendAssistantMessageToSessionTranscript } from "../config/sessions/transcript.js";
66
import { emitSessionLifecycleEvent } from "../sessions/session-lifecycle-events.js";
77
import { emitSessionTranscriptUpdate } from "../sessions/transcript-events.js";
8-
import { testState } from "./test-helpers.mocks.js";
8+
import { testState } from "./test-helpers.runtime-state.js";
99
import {
1010
connectOk,
1111
createGatewaySuiteHarness,

src/gateway/sessions-history-http.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import os from "node:os";
33
import path from "node:path";
44
import { afterEach, describe, expect, test } from "vitest";
55
import { appendAssistantMessageToSessionTranscript } from "../config/sessions/transcript.js";
6-
import { testState } from "./test-helpers.mocks.js";
6+
import { testState } from "./test-helpers.runtime-state.js";
77
import {
88
connectReq,
99
createGatewaySuiteHarness,

0 commit comments

Comments
 (0)