From 9803ef406c80418aec5e95c114af87546af8091f Mon Sep 17 00:00:00 2001 From: Dotta Date: Tue, 11 Aug 2026 17:45:30 +0000 Subject: [PATCH] feat(repositories): UI API client for connection flow + clone credentials Add begin/complete connection, searchable paginated discovery, multi-select import, and clone-credential resolution methods bound to the new server contract and shared types. Co-Authored-By: Paperclip --- ui/src/api/repositories.ts | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/ui/src/api/repositories.ts b/ui/src/api/repositories.ts index f4d33b35074..ca56585bcde 100644 --- a/ui/src/api/repositories.ts +++ b/ui/src/api/repositories.ts @@ -1,16 +1,42 @@ import type { AgentRepositoryGrantEntry, + BeginRepositoryConnectionResult, + CompleteRepositoryConnectionResult, CreateManualRepository, CreateRepositoryConnection, EffectiveRepositoryAccess, + ImportRepositoriesResult, ProjectRepositoryEntry, Repository, RepositoryCatalogItem, RepositoryConnection, + RepositoryDiscoveryPage, UpdateRepository, } from "@paperclipai/shared"; import { api } from "./client"; +export interface RepositoryCloneCredential { + username: string; + token: string; + authenticatedCloneUrl: string; + expiresAt: string; +} + +export interface RepositoryDiscoveryQuery { + query?: string | null; + cursor?: string | null; + pageSize?: number; +} + +function discoveryQueryString(opts: RepositoryDiscoveryQuery = {}): string { + const params = new URLSearchParams(); + if (opts.query) params.set("query", opts.query); + if (opts.cursor) params.set("cursor", opts.cursor); + if (typeof opts.pageSize === "number") params.set("pageSize", String(opts.pageSize)); + const qs = params.toString(); + return qs ? `?${qs}` : ""; +} + export const repositoriesApi = { list: (companyId: string, opts: { includeArchived?: boolean } = {}) => api.get( @@ -43,6 +69,36 @@ export const repositoriesApi = { `/repository-connections/${encodeURIComponent(connectionId)}`, ), + // ---- Guided provider connection flow (GitHub.com + other providers) ---- + beginConnection: (companyId: string, provider: string, input: { redirectPath?: string | null } = {}) => + api.post( + `/companies/${encodeURIComponent(companyId)}/repository-connections/${encodeURIComponent(provider)}/install`, + input, + ), + completeConnection: ( + companyId: string, + provider: string, + input: { state: string; installationId: string }, + ) => + api.post( + `/companies/${encodeURIComponent(companyId)}/repository-connections/${encodeURIComponent(provider)}/callback`, + input, + ), + discoverConnectionRepositories: (connectionId: string, opts: RepositoryDiscoveryQuery = {}) => + api.get( + `/repository-connections/${encodeURIComponent(connectionId)}/discover${discoveryQueryString(opts)}`, + ), + importConnectionRepositories: (connectionId: string, providerRepositoryIds: string[]) => + api.post( + `/repository-connections/${encodeURIComponent(connectionId)}/import`, + { providerRepositoryIds }, + ), + resolveCloneCredential: (repositoryId: string) => + api.post( + `/repositories/${encodeURIComponent(repositoryId)}/clone-credential`, + {}, + ), + listProjectRepositories: (projectId: string) => api.get(`/projects/${encodeURIComponent(projectId)}/repositories`), attachProjectRepository: (projectId: string, repositoryId: string, displayOrder = 0) =>