


























@@ -31,6 +31,7 @@ export type {
31313232const TEXT_PREVIEW_BYTES = 12 * 1024;
3333const ARTIFACT_VIEW_CONCURRENCY = 8;
34+const REPO_ROOT_ARTIFACT_PATH_PREFIX = "<repo-root>/";
34353536const UX_MATRIX_PRODUCER_FILES = [
3637{ key: "commands", path: "commands.txt", previewKind: "text" },
@@ -171,6 +172,13 @@ function isExplicitRepoRootArtifactPath(raw: string): boolean {
171172return normalized.startsWith(".artifacts/");
172173}
173174175+function repoRootTokenArtifactPath(raw: string): string | null {
176+const normalized = raw.split(/[\\/]+/u).join("/");
177+return normalized.startsWith(REPO_ROOT_ARTIFACT_PATH_PREFIX)
178+ ? normalized.slice(REPO_ROOT_ARTIFACT_PATH_PREFIX.length)
179+ : null;
180+}
181+174182// Resolve an artifact path against pre-resolved roots without re-reading the evidence file.
175183// Returns null when the path is missing or escapes both roots; callers map that to an error.
176184async function resolveArtifactFileWithinRoots(params: {
@@ -182,8 +190,13 @@ async function resolveArtifactFileWithinRoots(params: {
182190if (!raw) {
183191return null;
184192}
185-const candidates = path.isAbsolute(raw) ? [raw] : [path.resolve(params.evidenceDir, raw)];
186-if (!path.isAbsolute(raw) && isExplicitRepoRootArtifactPath(raw)) {
193+const tokenPath = repoRootTokenArtifactPath(raw);
194+const candidates = tokenPath
195+ ? [path.resolve(params.repoRoot, tokenPath)]
196+ : path.isAbsolute(raw)
197+ ? [raw]
198+ : [path.resolve(params.evidenceDir, raw)];
199+if (!tokenPath && !path.isAbsolute(raw) && isExplicitRepoRootArtifactPath(raw)) {
187200candidates.push(path.resolve(params.repoRoot, raw));
188201}
189202for (const candidate of candidates) {
@@ -345,6 +358,7 @@ async function buildArtifactView(params: {
345358allowedArtifactFiles: ReadonlySet<string>;
346359artifact: QaEvidenceArtifact;
347360evidenceDir: string;
361+extraRoots: readonly string[];
348362hrefEvidencePath: string;
349363repoRoot: string;
350364}): Promise<QaEvidenceArtifactView> {
@@ -354,6 +368,16 @@ async function buildArtifactView(params: {
354368evidenceDir: params.evidenceDir,
355369repoRoot: params.repoRoot,
356370}).catch(() => null);
371+const realFileRepoPath =
372+realFile && isInside(params.repoRoot, realFile)
373+ ? toRepoRelativePath(params.repoRoot, realFile)
374+ : null;
375+const displayPath =
376+realFileRepoPath ??
377+sanitizeGalleryText(params.artifact.path, {
378+extraRoots: params.extraRoots,
379+repoRoot: params.repoRoot,
380+});
357381if (!realFile || !params.allowedArtifactFiles.has(realFile)) {
358382return {
359383exists: false,
@@ -363,18 +387,22 @@ async function buildArtifactView(params: {
363387href: null,
364388kind: params.artifact.kind,
365389 mediaKind,
366-path: params.artifact.path,
390+path: displayPath,
367391preview: null,
368392source: params.artifact.source,
369393};
370394}
395+const hrefArtifactPath =
396+realFileRepoPath && path.isAbsolute(params.artifact.path)
397+ ? `${REPO_ROOT_ARTIFACT_PATH_PREFIX}${realFileRepoPath}`
398+ : params.artifact.path;
371399return {
372400exists: true,
373401error: null,
374-href: artifactHref(params.hrefEvidencePath, params.artifact.path),
402+href: artifactHref(params.hrefEvidencePath, hrefArtifactPath),
375403kind: params.artifact.kind,
376404 mediaKind,
377-path: params.artifact.path,
405+path: displayPath,
378406preview: await readPreview(realFile, mediaKind).catch(
379407(error: unknown) => `Preview unavailable: ${formatErrorMessage(error)}`,
380408),
@@ -701,6 +729,7 @@ export async function buildQaEvidenceGalleryModel(params: {
701729 allowedArtifactFiles,
702730 artifact,
703731 evidenceDir,
732+extraRoots: [requestedRepoRoot],
704733 hrefEvidencePath,
705734 repoRoot,
706735}),
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。