import { api } from "./client"; import type { CompanyArtifact, CompanyArtifactGroupBy, CompanyArtifactMediaKind, CompanyArtifactsResponse, } from "@paperclipai/shared"; export type { CompanyArtifact, CompanyArtifactGroup, CompanyArtifactGroupBy as ArtifactGroupBy, CompanyArtifactMediaKind as ArtifactMediaKind, CompanyArtifactsResponse, CompanyArtifactSource as ArtifactSource, } from "@paperclipai/shared"; /** * Company-level Artifacts client (PAP-10359). * * Talks to the company-scoped artifacts projection endpoint * (`GET /api/companies/:companyId/artifacts`) defined by the approved * Artifacts plan (PAP-10353). The endpoint flattens agent-produced issue * documents, direct attachments, and `artifact` work products into a single * card-ready list so the UI never has to stitch issue-specific endpoints * together. * * The `CompanyArtifact` shape is imported from `@paperclipai/shared` so the * frontend and server stay synchronized as the contract evolves. */ export type ArtifactKindFilter = Exclude | "all"; export interface ListArtifactsParams { kind?: ArtifactKindFilter; projectId?: string; q?: string; /** Grouping mode. `none` (default) returns the flat artifact grid. */ groupBy?: CompanyArtifactGroupBy; /** When grouping, selects a single stack to expand into its artifacts. */ groupIssueId?: string; limit?: number; cursor?: string; } function buildArtifactsQuery(params?: ListArtifactsParams): string { const search = new URLSearchParams(); if (params?.kind && params.kind !== "all") search.set("kind", params.kind); if (params?.projectId) search.set("projectId", params.projectId); if (params?.q) search.set("q", params.q); if (params?.groupBy && params.groupBy !== "none") search.set("groupBy", params.groupBy); if (params?.groupIssueId) search.set("groupIssueId", params.groupIssueId); if (params?.limit != null) search.set("limit", String(params.limit)); if (params?.cursor) search.set("cursor", params.cursor); const qs = search.toString(); return qs ? `?${qs}` : ""; } /** * Normalize the endpoint response. The contract is an envelope * (`{ artifacts, groups?, selectedGroup?, nextCursor }`), but we also tolerate a * bare array so the page keeps working if the backend ships the simpler shape. */ function normalizeArtifactsResponse( raw: CompanyArtifactsResponse | CompanyArtifact[], ): CompanyArtifactsResponse { if (Array.isArray(raw)) { return { artifacts: raw, nextCursor: null }; } return { artifacts: raw.artifacts ?? [], groups: raw.groups, selectedGroup: raw.selectedGroup, nextCursor: raw.nextCursor ?? null, }; } export const artifactsApi = { list: async (companyId: string, params?: ListArtifactsParams): Promise => { const raw = await api.get( `/companies/${companyId}/artifacts${buildArtifactsQuery(params)}`, ); return normalizeArtifactsResponse(raw); }, };