


























@@ -1,8 +1,9 @@
1-import { existsSync, readdirSync, readFileSync } from "node:fs";
1+import { spawnSync } from "node:child_process";
2+import fs, { existsSync, readFileSync } from "node:fs";
23import path from "node:path";
34import { fileURLToPath } from "node:url";
45import { bundledPluginFile } from "openclaw/plugin-sdk/test-fixtures";
5-import { describe, expect, it } from "vitest";
6+import { describe, expect, it, vi } from "vitest";
6778const thisFilePath = fileURLToPath(import.meta.url);
89const thisDir = path.dirname(thisFilePath);
@@ -18,9 +19,65 @@ function readRepoFile(relativePath: string): string {
1819return readFileSync(absolute, "utf8");
1920}
202122+function listGitFiles(pathspecs: string[]): string[] | null {
23+const result = spawnSync("git", ["ls-files", "--", ...pathspecs], {
24+cwd: repoRoot,
25+encoding: "utf8",
26+maxBuffer: 1024 * 1024,
27+stdio: ["ignore", "pipe", "ignore"],
28+});
29+if (result.status !== 0) {
30+return null;
31+}
32+return result.stdout
33+.split("\n")
34+.map((line) => line.trim())
35+.filter((line) => line.length > 0)
36+.map(toPosix)
37+.toSorted();
38+}
39+40+function listFindFiles(args: string[]): string[] | null {
41+const result = spawnSync("find", args, {
42+cwd: repoRoot,
43+encoding: "utf8",
44+maxBuffer: 1024 * 1024,
45+stdio: ["ignore", "pipe", "ignore"],
46+});
47+if (result.status !== 0) {
48+return null;
49+}
50+return result.stdout
51+.split("\n")
52+.map((line) => line.trim())
53+.filter((line) => line.length > 0)
54+.map((file) => path.relative(repoRoot, file))
55+.map(toPosix)
56+.toSorted();
57+}
58+2159function listCoreOutboundEntryFiles(): string[] {
60+const externalFiles =
61+listGitFiles(["src/channels/plugins/outbound/*.ts"]) ??
62+listFindFiles([
63+path.join(repoRoot, "src/channels/plugins/outbound"),
64+"-maxdepth",
65+"1",
66+"-type",
67+"f",
68+"-name",
69+"*.ts",
70+]);
71+if (externalFiles) {
72+return externalFiles
73+.filter((file) => !file.endsWith(".test.ts"))
74+.map(toPosix)
75+.toSorted();
76+}
77+2278const outboundDir = path.join(repoRoot, "src/channels/plugins/outbound");
23-return readdirSync(outboundDir)
79+return fs
80+.readdirSync(outboundDir)
2481.filter((name) => name.endsWith(".ts") && !name.endsWith(".test.ts"))
2582.map((name) => toPosix(path.join("src/channels/plugins/outbound", name)))
2683.toSorted();
@@ -30,11 +87,25 @@ function listExtensionFiles(): {
3087adapterEntrypoints: string[];
3188inlineChannelEntrypoints: string[];
3289} {
90+const externalFiles =
91+listGitFiles(["extensions/*/src/outbound.ts", "extensions/*/src/channel.ts"]) ??
92+listFindFiles([
93+path.join(repoRoot, "extensions"),
94+"-path",
95+"*/src/outbound.ts",
96+"-o",
97+"-path",
98+"*/src/channel.ts",
99+]);
100+if (externalFiles) {
101+return partitionExtensionEntrypoints(externalFiles);
102+}
103+33104const extensionsRoot = path.join(repoRoot, "extensions");
34105const adapterEntrypoints: string[] = [];
35106const inlineChannelEntrypoints: string[] = [];
3610737-for (const entry of readdirSync(extensionsRoot, { withFileTypes: true })) {
108+for (const entry of fs.readdirSync(extensionsRoot, { withFileTypes: true })) {
38109if (!entry.isDirectory()) {
39110continue;
40111}
@@ -60,6 +131,33 @@ function listExtensionFiles(): {
60131};
61132}
62133134+function partitionExtensionEntrypoints(files: string[]): {
135+adapterEntrypoints: string[];
136+inlineChannelEntrypoints: string[];
137+} {
138+const adapterEntrypoints: string[] = [];
139+const inlineChannelEntrypoints: string[] = [];
140+141+for (const file of files) {
142+if (file.endsWith("/src/outbound.ts")) {
143+adapterEntrypoints.push(file);
144+continue;
145+}
146+if (!file.endsWith("/src/channel.ts")) {
147+continue;
148+}
149+const source = readRepoFile(file);
150+if (/\boutbound\s*:\s*\{/.test(source)) {
151+inlineChannelEntrypoints.push(file);
152+}
153+}
154+155+return {
156+adapterEntrypoints: adapterEntrypoints.toSorted(),
157+inlineChannelEntrypoints: inlineChannelEntrypoints.toSorted(),
158+};
159+}
160+63161function listHighRiskRuntimeCfgFiles(): string[] {
64162return [
65163bundledPluginFile("telegram", "src/action-runtime.ts"),
@@ -163,6 +261,25 @@ function extractOutboundBlock(source: string, file: string): string {
163261}
164262165263describe("outbound cfg-threading guard", () => {
264+it("lists outbound entrypoints without scanning directories in-process", () => {
265+const readDir = vi.spyOn(fs, "readdirSync");
266+try {
267+const coreAdapterFiles = listCoreOutboundEntryFiles();
268+const extensionFiles = listExtensionFiles();
269+270+expect(coreAdapterFiles.length).toBeGreaterThan(0);
271+expect(extensionFiles.adapterEntrypoints.length).toBeGreaterThan(0);
272+expect(
273+coreAdapterFiles.every((file) =>
274+file.startsWith("src/channels/plugins/outbound/") && file.endsWith(".ts"),
275+),
276+).toBe(true);
277+expect(readDir).not.toHaveBeenCalled();
278+} finally {
279+readDir.mockRestore();
280+}
281+});
282+166283it("keeps outbound adapter entrypoints free of getRuntimeConfig calls", () => {
167284const coreAdapterFiles = listCoreOutboundEntryFiles();
168285const extensionAdapterFiles = listExtensionFiles().adapterEntrypoints;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。