

























@@ -1,4 +1,4 @@
1-import { existsSync } from "node:fs";
1+import { existsSync, readdirSync, readFileSync } from "node:fs";
22import fs from "node:fs/promises";
33import path from "node:path";
44import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared";
@@ -76,16 +76,20 @@ export function resolveQaBundledPluginSourceDir(params: { repoRoot: string; plug
7676path.join(params.repoRoot, "extensions", params.pluginId),
7777];
7878const existingCandidates = candidates.filter((candidate) => existsSync(candidate));
79-if (existingCandidates.length === 0) {
79+const manifestCandidates = findQaBundledPluginDirsByManifestId(params);
80+const allCandidates = [...existingCandidates, ...manifestCandidates].filter(
81+(candidate, index, all) => all.indexOf(candidate) === index,
82+);
83+if (allCandidates.length === 0) {
8084return null;
8185}
82-const cliMetadataCandidate = existingCandidates.find((candidate) =>
86+const cliMetadataCandidate = allCandidates.find((candidate) =>
8387QA_CLI_METADATA_ENTRY_BASENAMES.some((basename) => existsSync(path.join(candidate, basename))),
8488);
8589if (cliMetadataCandidate) {
8690return cliMetadataCandidate;
8791}
88-return existingCandidates[0] ?? null;
92+return allCandidates[0] ?? null;
8993}
90949195function resolveQaBundledPluginScanRoots(repoRoot: string) {
@@ -96,6 +100,37 @@ function resolveQaBundledPluginScanRoots(repoRoot: string) {
96100].filter((candidate, index, all) => existsSync(candidate) && all.indexOf(candidate) === index);
97101}
98102103+function readQaBundledManifestId(manifestPath: string): string | null {
104+try {
105+const parsed = JSON.parse(readFileSync(manifestPath, "utf8")) as { id?: unknown };
106+return typeof parsed.id === "string" ? parsed.id.trim() || null : null;
107+} catch {
108+return null;
109+}
110+}
111+112+function findQaBundledPluginDirsByManifestId(params: {
113+repoRoot: string;
114+pluginId: string;
115+}): string[] {
116+const candidates: string[] = [];
117+for (const sourceRoot of resolveQaBundledPluginScanRoots(params.repoRoot)) {
118+for (const entry of readdirSync(sourceRoot, { withFileTypes: true }).toSorted((left, right) =>
119+left.name.localeCompare(right.name),
120+)) {
121+if (!entry.isDirectory()) {
122+continue;
123+}
124+const candidate = path.join(sourceRoot, entry.name);
125+const manifestId = readQaBundledManifestId(path.join(candidate, "openclaw.plugin.json"));
126+if (manifestId === params.pluginId) {
127+candidates.push(candidate);
128+}
129+}
130+}
131+return candidates;
132+}
133+99134export async function resolveQaOwnerPluginIdsForProviderIds(params: {
100135repoRoot: string;
101136providerIds: readonly string[];
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。