



















@@ -1,5 +1,6 @@
1-import { readFileSync, readdirSync } from "node:fs";
2-import { dirname, resolve } from "node:path";
1+import { spawnSync } from "node:child_process";
2+import fs, { readFileSync } from "node:fs";
3+import { dirname, relative, resolve } from "node:path";
34import { fileURLToPath } from "node:url";
45import type {
56BaseProbeResult as ContractBaseProbeResult,
@@ -22,7 +23,7 @@ import type {
2223} from "openclaw/plugin-sdk/core";
2324import * as providerEntrySdk from "openclaw/plugin-sdk/provider-entry";
2425import ts from "typescript";
25-import { describe, expect, expectTypeOf, it } from "vitest";
26+import { describe, expect, expectTypeOf, it, vi } from "vitest";
2627import type { ChannelMessageActionContext } from "../../channels/plugins/types.js";
2728import type {
2829BaseProbeResult,
@@ -325,12 +326,44 @@ function expectNamedExportParity(params: BrowserHelperExportParityContract) {
325326]);
326327}
327328329+function listTrackedRepoTsFiles(dir: string): string[] | null {
330+const relativeDir = relative(REPO_ROOT, dir).split(/[\\/]+/u).join("/");
331+if (!relativeDir || relativeDir.startsWith("..")) {
332+return null;
333+}
334+const result = spawnSync("git", ["ls-files", "--", relativeDir], {
335+cwd: REPO_ROOT,
336+encoding: "utf8",
337+stdio: ["ignore", "pipe", "ignore"],
338+});
339+if (result.status !== 0) {
340+return null;
341+}
342+return result.stdout
343+.split("\n")
344+.map((line) => line.trim().replaceAll("\\", "/"))
345+.filter(
346+(line) =>
347+line.endsWith(".ts") &&
348+!line.includes("/dist/") &&
349+!line.includes("/node_modules/"),
350+)
351+.map((line) => resolve(REPO_ROOT, ...line.split("/")))
352+.toSorted();
353+}
354+328355function listRepoTsFiles(dir: string): string[] {
329356const cached = repoTsFilesCache.get(dir);
330-if (cached) {
357+if (cached !== undefined) {
331358return cached;
332359}
333-const entries = readdirSync(dir, { withFileTypes: true });
360+const trackedFiles = listTrackedRepoTsFiles(dir);
361+if (trackedFiles) {
362+repoTsFilesCache.set(dir, trackedFiles);
363+return trackedFiles;
364+}
365+366+const entries = fs.readdirSync(dir, { withFileTypes: true });
334367const files = entries.flatMap((entry) => {
335368const absolute = resolve(dir, entry.name);
336369if (entry.isDirectory()) {
@@ -791,6 +824,21 @@ describe("plugin-sdk subpath exports", () => {
791824expect(violations).toStrictEqual([]);
792825});
793826827+it("lists repo source candidates from git without walking SDK boundary roots", () => {
828+const readDir = vi.spyOn(fs, "readdirSync");
829+try {
830+repoTsFilesCache.clear();
831+const files = listRepoTsFiles(resolve(REPO_ROOT, "src"));
832+833+expect(files.length).toBeGreaterThan(0);
834+expect(files.every((file) => file.endsWith(".ts"))).toBe(true);
835+expect(readDir).not.toHaveBeenCalled();
836+} finally {
837+readDir.mockRestore();
838+repoTsFilesCache.clear();
839+}
840+});
841+794842it("keeps the deprecated channel-runtime shim unused in repo imports", () => {
795843const matches = findRepoFilesContaining({
796844roots: [
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。