import { gzipSync } from "node:zlib"; import type { FeedbackTraceBundle } from "@paperclipai/shared"; import type { Config } from "../config.js"; const DEFAULT_FEEDBACK_EXPORT_BACKEND_URL = "https://telemetry.paperclip.ing"; 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 { const baseUrl = config.feedbackExportBackendUrl?.trim() || DEFAULT_FEEDBACK_EXPORT_BACKEND_URL; 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 requestBody = JSON.stringify({ objectKey, exportedAt: exportedAt.toISOString(), bundle, }); const response = await fetch(endpoint, { method: "POST", headers: { "content-type": "application/json", ...(token ? { authorization: `Bearer ${token}` } : {}), }, body: JSON.stringify({ encoding: "gzip+base64+json", payload: gzipSync(requestBody).toString("base64"), }), }); 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, }; }, }; }