import type { FeedbackTraceBundle } from "@paperclipai/shared"; import type { Config } from "../config.js"; function buildFeedbackShareObjectKey(bundle: FeedbackTraceBundle, exportedAt: Date) { const year = String(exportedAt.getUTCFullYear()); const month = String(exportedAt.getUTCMonth() + 1).padStart(2, "0"); const day = String(exportedAt.getUTCDate()).padStart(2, "0"); return `feedback-traces/${bundle.companyId}/${year}/${month}/${day}/${bundle.exportId ?? bundle.traceId}.json`; } export interface FeedbackTraceShareClient { uploadTraceBundle(bundle: FeedbackTraceBundle): Promise<{ objectKey: string }>; } export function createFeedbackTraceShareClientFromConfig( config: Pick, ): FeedbackTraceShareClient | null { const baseUrl = config.feedbackExportBackendUrl?.trim(); if (!baseUrl) return null; const token = config.feedbackExportBackendToken?.trim(); const endpoint = new URL("/feedback-traces", baseUrl).toString(); return { async uploadTraceBundle(bundle) { const exportedAt = new Date(); const objectKey = buildFeedbackShareObjectKey(bundle, exportedAt); const response = await fetch(endpoint, { method: "POST", headers: { "content-type": "application/json", ...(token ? { authorization: `Bearer ${token}` } : {}), }, body: JSON.stringify({ objectKey, exportedAt: exportedAt.toISOString(), bundle, }), }); if (!response.ok) { const detail = await response.text().catch(() => ""); throw new Error(detail.trim() || `Feedback trace upload failed with HTTP ${response.status}`); } const payload = await response.json().catch(() => null) as { objectKey?: unknown } | null; return { objectKey: typeof payload?.objectKey === "string" && payload.objectKey.trim().length > 0 ? payload.objectKey : objectKey, }; }, }; }