import type { ArtifactContentChunk, ArtifactDownloadReferenceResponse } from "../api/types"; export async function downloadArtifactReference( reference: ArtifactDownloadReferenceResponse, readContent: (artifactId: string, offset: number, limit?: number) => Promise, onProgress?: (progress: number) => void ) { const chunks: ArrayBuffer[] = []; let offset = 0; onProgress?.(0); while (offset < reference.sizeBytes) { const chunk = await readContent(reference.artifactId, offset, reference.chunkSizeBytes); chunks.push(chunk.payload); offset += chunk.payload.byteLength; onProgress?.(Math.min(100, Math.round((offset / reference.sizeBytes) * 100))); if (chunk.payload.byteLength === 0) { break; } } openArtifactBlob(reference, chunks); onProgress?.(100); } export function openArtifactBlob(reference: ArtifactDownloadReferenceResponse, chunks: ArrayBuffer[]) { if (typeof document === "undefined" || typeof URL === "undefined") { return; } const blob = new Blob(chunks, { type: reference.contentType }); const url = URL.createObjectURL(blob); const anchor = document.createElement("a"); anchor.href = url; anchor.download = safeArtifactFilename(reference.filename); anchor.rel = "noopener"; document.body.append(anchor); anchor.click(); anchor.remove(); URL.revokeObjectURL(url); } export function safeArtifactFilename(filename: string): string { const cleaned = filename.replace(/[\\/]/g, "").trim(); return cleaned || "artifact.bin"; } export function safeArtifactError(error: unknown): string { const message = error instanceof Error ? error.message : "制品传输失败"; return message.replace(/\/Users\/[^\s]+/g, "[path]").replace(/Bearer\s+[^\s]+/gi, "[token]").replace(/sk-[A-Za-z0-9_-]+/g, "[secret]"); }