Skip to content

Commit 3608028

Browse files
committed
refactor: remove remaining contract path leaks
1 parent 97e1437 commit 3608028

11 files changed

Lines changed: 190 additions & 124 deletions

src/agents/models-config.providers.static.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import path from "node:path";
22
import { pathToFileURL } from "node:url";
3-
import {
4-
listBundledPluginMetadata,
5-
resolveBundledPluginPublicSurfacePath,
6-
} from "../plugins/bundled-plugin-metadata.js";
3+
import { listBundledPluginMetadata } from "../plugins/bundled-plugin-metadata.js";
4+
import { resolveBundledPluginPublicSurfacePath } from "../plugins/public-surface-runtime.js";
75

86
const PROVIDER_CATALOG_ARTIFACT_BASENAME = "provider-catalog.js";
97
const DEFAULT_PROVIDER_CATALOG_ROOT = path.resolve(import.meta.dirname, "../..");

src/channels/plugins/bundled.shape-guard.test.ts

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import os from "node:os";
33
import path from "node:path";
44
import { afterEach, describe, expect, it, vi } from "vitest";
55
import { importFreshModule } from "../../../test/helpers/import-fresh.ts";
6+
import { loadPluginManifestRegistry } from "../../plugins/manifest-registry.js";
67

78
afterEach(() => {
89
vi.doUnmock("../../plugins/discovery.js");
@@ -12,6 +13,10 @@ afterEach(() => {
1213
});
1314

1415
describe("bundled channel entry shape guards", () => {
16+
const bundledPluginRoots = loadPluginManifestRegistry({ cache: true, config: {} })
17+
.plugins.filter((plugin) => plugin.origin === "bundled")
18+
.map((plugin) => plugin.rootDir);
19+
1520
it("treats missing bundled discovery results as empty", async () => {
1621
vi.doMock("../../plugins/discovery.js", () => ({
1722
discoverOpenClawPlugins: () => ({
@@ -35,14 +40,9 @@ describe("bundled channel entry shape guards", () => {
3540
expect(bundled.listBundledChannelSetupPlugins()).toEqual([]);
3641
});
3742
it("keeps channel entrypoints on the dedicated entry-contract SDK surface", () => {
38-
const extensionRoot = path.resolve("extensions");
3943
const offenders: string[] = [];
4044

41-
for (const extensionId of fs.readdirSync(extensionRoot)) {
42-
const extensionDir = path.join(extensionRoot, extensionId);
43-
if (!fs.statSync(extensionDir).isDirectory()) {
44-
continue;
45-
}
45+
for (const extensionDir of bundledPluginRoots) {
4646
for (const relativePath of ["index.ts", "channel-entry.ts", "setup-entry.ts"]) {
4747
const filePath = path.join(extensionDir, relativePath);
4848
if (!fs.existsSync(filePath)) {
@@ -69,14 +69,9 @@ describe("bundled channel entry shape guards", () => {
6969
});
7070

7171
it("keeps bundled channel entrypoints free of static src imports", () => {
72-
const extensionRoot = path.resolve("extensions");
7372
const offenders: string[] = [];
7473

75-
for (const extensionId of fs.readdirSync(extensionRoot)) {
76-
const extensionDir = path.join(extensionRoot, extensionId);
77-
if (!fs.statSync(extensionDir).isDirectory()) {
78-
continue;
79-
}
74+
for (const extensionDir of bundledPluginRoots) {
8075
for (const relativePath of ["index.ts", "channel-entry.ts", "setup-entry.ts"]) {
8176
const filePath = path.join(extensionDir, relativePath);
8277
if (!fs.existsSync(filePath)) {
@@ -99,14 +94,9 @@ describe("bundled channel entry shape guards", () => {
9994
});
10095

10196
it("keeps channel implementations off the broad core SDK surface", () => {
102-
const extensionRoot = path.resolve("extensions");
10397
const offenders: string[] = [];
10498

105-
for (const extensionId of fs.readdirSync(extensionRoot)) {
106-
const extensionDir = path.join(extensionRoot, extensionId);
107-
if (!fs.statSync(extensionDir).isDirectory()) {
108-
continue;
109-
}
99+
for (const extensionDir of bundledPluginRoots) {
110100
for (const relativePath of ["src/channel.ts", "src/plugin.ts"]) {
111101
const filePath = path.join(extensionDir, relativePath);
112102
if (!fs.existsSync(filePath)) {

src/channels/plugins/contracts/registry-session-binding.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
sessionBindingContractChannelIds,
1414
type SessionBindingContractChannelId,
1515
} from "./manifest.js";
16+
import { importBundledChannelContractArtifact } from "./runtime-artifacts.js";
1617
import "./registry.js";
1718

1819
type SessionBindingContractEntry = {
@@ -35,18 +36,14 @@ const matrixSessionBindingAuth = {
3536
accessToken: "token",
3637
} as const;
3738

38-
function buildBundledPluginModuleId(pluginId: string, artifactBasename: string): string {
39-
return ["..", "..", "..", "..", "extensions", pluginId, artifactBasename].join("/");
40-
}
41-
4239
async function getContractApi<T extends Record<string, unknown>>(pluginId: string): Promise<T> {
4340
const existing = contractApiPromises.get(pluginId);
4441
if (existing) {
4542
return (await existing) as T;
4643
}
47-
const next = import(buildBundledPluginModuleId(pluginId, "contract-api.js"));
44+
const next = importBundledChannelContractArtifact<T>(pluginId, "contract-api");
4845
contractApiPromises.set(pluginId, next);
49-
return (await next) as T;
46+
return await next;
5047
}
5148

5249
function expectResolvedSessionBinding(params: {

src/channels/plugins/contracts/registry.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import type { OpenClawConfig } from "../../../config/config.js";
33
import { listBundledChannelPlugins, setBundledChannelRuntime } from "../bundled.js";
44
import type { ChannelPlugin } from "../types.js";
55
import { channelPluginSurfaceKeys, type ChannelPluginSurface } from "./manifest.js";
6-
7-
function buildBundledPluginModuleId(pluginId: string, artifactBasename: string): string {
8-
return ["..", "..", "..", "..", "extensions", pluginId, artifactBasename].join("/");
9-
}
6+
import {
7+
importBundledChannelContractArtifact,
8+
resolveBundledChannelContractArtifactUrl,
9+
} from "./runtime-artifacts.js";
1010

1111
type SurfaceContractEntry = {
1212
id: string;
@@ -45,7 +45,11 @@ const sendMessageMatrixMock = vi.hoisted(() =>
4545
})),
4646
);
4747

48-
const lineContractApi = await import(buildBundledPluginModuleId("line", "contract-api.js"));
48+
const lineContractApi = await importBundledChannelContractArtifact<{
49+
listLineAccountIds: () => string[];
50+
resolveDefaultLineAccountId: (cfg: OpenClawConfig) => string | undefined;
51+
resolveLineAccount: (params: { cfg: OpenClawConfig; accountId?: string }) => unknown;
52+
}>("line", "contract-api");
4953

5054
setBundledChannelRuntime("line", {
5155
channel: {
@@ -58,8 +62,11 @@ setBundledChannelRuntime("line", {
5862
},
5963
} as never);
6064

61-
vi.mock(buildBundledPluginModuleId("matrix", "runtime-api.js"), async () => {
62-
const matrixRuntimeApiModuleId = buildBundledPluginModuleId("matrix", "runtime-api.js");
65+
vi.mock(resolveBundledChannelContractArtifactUrl("matrix", "runtime-api"), async () => {
66+
const matrixRuntimeApiModuleId = resolveBundledChannelContractArtifactUrl(
67+
"matrix",
68+
"runtime-api",
69+
);
6370
const actual = await vi.importActual(matrixRuntimeApiModuleId);
6471
return {
6572
...actual,
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { pathToFileURL } from "node:url";
2+
import {
3+
resolvePluginRuntimeModulePath,
4+
resolvePluginRuntimeRecord,
5+
} from "../../../plugins/runtime/runtime-plugin-boundary.js";
6+
7+
export function resolveBundledChannelContractArtifactUrl(
8+
pluginId: string,
9+
entryBaseName: string,
10+
): string {
11+
const record = resolvePluginRuntimeRecord(pluginId, () => {
12+
throw new Error(`missing bundled channel plugin '${pluginId}'`);
13+
});
14+
if (!record) {
15+
throw new Error(`missing bundled channel plugin '${pluginId}'`);
16+
}
17+
const modulePath = resolvePluginRuntimeModulePath(record, entryBaseName, () => {
18+
throw new Error(`missing ${entryBaseName} for bundled channel plugin '${pluginId}'`);
19+
});
20+
if (!modulePath) {
21+
throw new Error(`missing ${entryBaseName} for bundled channel plugin '${pluginId}'`);
22+
}
23+
return pathToFileURL(modulePath).href;
24+
}
25+
26+
export async function importBundledChannelContractArtifact<T extends object>(
27+
pluginId: string,
28+
entryBaseName: string,
29+
): Promise<T> {
30+
return (await import(resolveBundledChannelContractArtifactUrl(pluginId, entryBaseName))) as T;
31+
}

src/gateway/server.startup-matrix-migration.integration.test.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import path from "node:path";
22
import { describe, expect, it, vi } from "vitest";
3-
import { clearBundledPluginMetadataCache } from "../plugins/bundled-plugin-metadata.js";
43
import { clearPluginDiscoveryCache } from "../plugins/discovery.js";
54
import { clearPluginManifestRegistryCache } from "../plugins/manifest-registry.js";
65

@@ -25,7 +24,6 @@ describe("gateway startup channel maintenance wiring", () => {
2524
const previousSkipChannels = process.env.OPENCLAW_SKIP_CHANNELS;
2625
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = path.resolve(process.cwd(), "extensions");
2726
process.env.OPENCLAW_SKIP_CHANNELS = "0";
28-
clearBundledPluginMetadataCache();
2927
clearPluginDiscoveryCache();
3028
clearPluginManifestRegistryCache();
3129
runChannelPluginStartupMaintenanceMock.mockClear();
@@ -70,7 +68,6 @@ describe("gateway startup channel maintenance wiring", () => {
7068
} else {
7169
process.env.OPENCLAW_SKIP_CHANNELS = previousSkipChannels;
7270
}
73-
clearBundledPluginMetadataCache();
7471
clearPluginDiscoveryCache();
7572
clearPluginManifestRegistryCache();
7673
}

src/plugin-sdk/facade-runtime.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { getRuntimeConfigSnapshot } from "../config/runtime-snapshot.js";
99
import type { OpenClawConfig } from "../config/types.js";
1010
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
1111
import { resolveBundledPluginsDir } from "../plugins/bundled-dir.js";
12-
import { resolveBundledPluginPublicSurfacePath } from "../plugins/bundled-plugin-metadata.js";
1312
import {
1413
createPluginActivationSource,
1514
normalizePluginsConfig,
@@ -19,6 +18,7 @@ import {
1918
loadPluginManifestRegistry,
2019
type PluginManifestRecord,
2120
} from "../plugins/manifest-registry.js";
21+
import { resolveBundledPluginPublicSurfacePath } from "../plugins/public-surface-runtime.js";
2222
import {
2323
buildPluginLoaderAliasMap,
2424
buildPluginLoaderJitiOptions,

src/plugins/bundled-plugin-metadata.ts

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { fileURLToPath } from "node:url";
44
import { createJiti } from "jiti";
55
import { buildChannelConfigSchema } from "../channels/plugins/config-schema.js";
66
import type { ChannelConfigRuntimeSchema } from "../channels/plugins/types.plugin.js";
7-
import { resolveBundledPluginsDir } from "./bundled-dir.js";
87
import {
98
getPackageManifestMetadata,
109
loadPluginManifest,
@@ -521,58 +520,3 @@ export function resolveBundledPluginGeneratedPath(
521520
}
522521
return null;
523522
}
524-
525-
export function resolveBundledPluginPublicSurfacePath(params: {
526-
rootDir: string;
527-
dirName: string;
528-
artifactBasename: string;
529-
env?: NodeJS.ProcessEnv;
530-
bundledPluginsDir?: string;
531-
}): string | null {
532-
const artifactBasename = params.artifactBasename.replace(/^\.\//u, "");
533-
if (!artifactBasename) {
534-
return null;
535-
}
536-
537-
const explicitBundledPluginsDir =
538-
params.bundledPluginsDir ?? resolveBundledPluginsDir(params.env ?? process.env);
539-
if (explicitBundledPluginsDir) {
540-
const explicitPluginDir = path.resolve(explicitBundledPluginsDir, params.dirName);
541-
const explicitBuiltCandidate = path.join(explicitPluginDir, artifactBasename);
542-
if (fs.existsSync(explicitBuiltCandidate)) {
543-
return explicitBuiltCandidate;
544-
}
545-
546-
const sourceBaseName = artifactBasename.replace(/\.js$/u, "");
547-
for (const ext of PUBLIC_SURFACE_SOURCE_EXTENSIONS) {
548-
const sourceCandidate = path.join(explicitPluginDir, `${sourceBaseName}${ext}`);
549-
if (fs.existsSync(sourceCandidate)) {
550-
return sourceCandidate;
551-
}
552-
}
553-
}
554-
555-
for (const candidate of [
556-
path.resolve(params.rootDir, "dist", "extensions", params.dirName, artifactBasename),
557-
path.resolve(params.rootDir, "dist-runtime", "extensions", params.dirName, artifactBasename),
558-
]) {
559-
if (fs.existsSync(candidate)) {
560-
return candidate;
561-
}
562-
}
563-
564-
const sourceBaseName = artifactBasename.replace(/\.js$/u, "");
565-
for (const ext of PUBLIC_SURFACE_SOURCE_EXTENSIONS) {
566-
const sourceCandidate = path.resolve(
567-
params.rootDir,
568-
"extensions",
569-
params.dirName,
570-
`${sourceBaseName}${ext}`,
571-
);
572-
if (fs.existsSync(sourceCandidate)) {
573-
return sourceCandidate;
574-
}
575-
}
576-
577-
return null;
578-
}

src/plugins/contracts/plugin-sdk-index.bundle.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,26 @@ import { createRequire } from "node:module";
33
import path from "node:path";
44
import { pathToFileURL } from "node:url";
55
import { describe, expect, it } from "vitest";
6-
import { bundledPluginFile } from "../../../test/helpers/bundled-plugin-paths.js";
76
import { buildPluginSdkEntrySources, pluginSdkEntrypoints } from "../../plugin-sdk/entrypoints.js";
7+
import { loadPluginManifestRegistry } from "../manifest-registry.js";
88

99
const require = createRequire(import.meta.url);
1010
const tsdownModuleUrl = pathToFileURL(require.resolve("tsdown")).href;
1111
const bundledRepresentativeEntrypoints = ["matrix-runtime-heavy"] as const;
12+
const bundledPluginRoots = new Map(
13+
loadPluginManifestRegistry({ cache: true, config: {} })
14+
.plugins.filter((plugin) => plugin.origin === "bundled")
15+
.map((plugin) => [plugin.id, plugin.rootDir] as const),
16+
);
17+
18+
function bundledPluginFile(pluginId: string, relativePath: string): string {
19+
const rootDir = bundledPluginRoots.get(pluginId);
20+
if (!rootDir) {
21+
throw new Error(`missing bundled plugin root for ${pluginId}`);
22+
}
23+
return path.join(rootDir, relativePath);
24+
}
25+
1226
const matrixRuntimeCoverageEntries = {
1327
"matrix-runtime-sdk": bundledPluginFile("matrix", "src/matrix/sdk.ts"),
1428
} as const;

0 commit comments

Comments
 (0)