























@@ -1,6 +1,7 @@
1+import { spawnSync } from "node:child_process";
12import fs from "node:fs";
23import path from "node:path";
3-import { describe, expect, it } from "vitest";
4+import { describe, expect, it, vi } from "vitest";
4556const REPO_ROOT = path.resolve(import.meta.dirname, "../..");
67const SCAN_ROOTS = ["src", "packages", "extensions"] as const;
@@ -11,7 +12,57 @@ function isSourceFile(filePath: string): boolean {
1112return filePath.endsWith(".ts") && !filePath.endsWith(".test.ts") && !filePath.endsWith(".d.ts");
1213}
131414-function walk(dir: string): string[] {
15+function listSourceFiles(dir: string): string[] {
16+const externalFiles = listExternalSourceFiles(dir);
17+if (externalFiles) {
18+return externalFiles;
19+}
20+return walkSourceFiles(dir);
21+}
22+23+function listExternalSourceFiles(dir: string): string[] | null {
24+const repoPath = toRepoPath(dir);
25+return listGitSourceFiles(repoPath) ?? listFindSourceFiles(dir);
26+}
27+28+function listGitSourceFiles(repoPath: string): string[] | null {
29+const result = spawnSync("git", ["ls-files", "--", repoPath], {
30+cwd: REPO_ROOT,
31+encoding: "utf8",
32+maxBuffer: 1024 * 1024 * 8,
33+stdio: ["ignore", "pipe", "ignore"],
34+});
35+if (result.status !== 0) {
36+return null;
37+}
38+return result.stdout
39+.split("\n")
40+.map((line) => line.trim())
41+.filter((line) => line.length > 0)
42+.map((filePath) => path.join(REPO_ROOT, filePath))
43+.filter(isSourceFile)
44+.toSorted();
45+}
46+47+function listFindSourceFiles(dir: string): string[] | null {
48+const result = spawnSync("find", [dir, "-type", "f", "-name", "*.ts"], {
49+cwd: REPO_ROOT,
50+encoding: "utf8",
51+maxBuffer: 1024 * 1024 * 8,
52+stdio: ["ignore", "pipe", "ignore"],
53+});
54+if (result.status !== 0) {
55+return null;
56+}
57+return result.stdout
58+.split("\n")
59+.map((line) => line.trim())
60+.filter((line) => line.length > 0)
61+.filter(isSourceFile)
62+.toSorted();
63+}
64+65+function walkSourceFiles(dir: string): string[] {
1566const entries = fs.readdirSync(dir, { withFileTypes: true });
1667const files: string[] = [];
1768for (const entry of entries) {
@@ -20,7 +71,7 @@ function walk(dir: string): string[] {
2071if (entry.name === "node_modules" || entry.name === "dist") {
2172continue;
2273}
23-files.push(...walk(fullPath));
74+files.push(...walkSourceFiles(fullPath));
2475continue;
2576}
2677if (entry.isFile() && isSourceFile(fullPath)) {
@@ -35,8 +86,21 @@ function toRepoPath(filePath: string): string {
3586}
36873788describe("fs-safe import boundary", () => {
89+it("lists source files without scanning boundary roots in-process", () => {
90+const readDir = vi.spyOn(fs, "readdirSync");
91+try {
92+const files = SCAN_ROOTS.flatMap((root) => listSourceFiles(path.join(REPO_ROOT, root)));
93+94+expect(files.length).toBeGreaterThan(0);
95+expect(files.every(isSourceFile)).toBe(true);
96+expect(readDir).not.toHaveBeenCalled();
97+} finally {
98+readDir.mockRestore();
99+}
100+});
101+38102it("keeps direct fs-safe imports behind OpenClaw policy wrappers", () => {
39-const violations = SCAN_ROOTS.flatMap((root) => walk(path.join(REPO_ROOT, root)))
103+const violations = SCAN_ROOTS.flatMap((root) => listSourceFiles(path.join(REPO_ROOT, root)))
40104.map(toRepoPath)
41105.filter((filePath) => {
42106if (ALLOWED_PREFIXES.some((prefix) => filePath.startsWith(prefix))) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。