diff --git a/cp-agent/cp_agent/api/v1/projects.py b/cp-agent/cp_agent/api/v1/projects.py index e1f06d0..26a9ae2 100644 --- a/cp-agent/cp_agent/api/v1/projects.py +++ b/cp-agent/cp_agent/api/v1/projects.py @@ -21,12 +21,15 @@ MigrationRequest, MigrationResponse, ProjectCreateRequest, + SwitchCommitRequest, + SwitchCommitResponse, ) from cp_agent.utils.agent_helpers import get_agent from cp_agent.utils.project_download import create_project_zip from cp_agent.utils.project_paths import find_project_paths from cp_agent.utils.project_summary import generate_project_summary from cp_agent.utils.snapshot import create_snapshot +from cp_agent.utils.runner_client import RunnerClient, ErrorModel from cp_agent.utils.supabase_utils import SupabaseUtil router = APIRouter() @@ -579,6 +582,95 @@ async def download_project( ) +@router.post( + "/{project_id}/switch-commit", + response_model=SwitchCommitResponse, + status_code=status.HTTP_200_OK, +) +async def switch_commit( + project_id: str, + request: SwitchCommitRequest, +) -> SwitchCommitResponse: + """Switch the project's working directory to a specific git commit. + + Args: + project_id: The ID of the project. + request: Request containing the commit hash. + + Returns: + SwitchCommitResponse: Indicates success or failure. + + Raises: + HTTPException: 404 if project doesn't exist. + HTTPException: 500 if switching commit fails. + """ + # Check project exists (optional, runner might handle this) + project_manager = get_project_manager() + project = await project_manager.get_project(project_id) + if not project: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Project {project_id} not found", + ) + + runner_client: RunnerClient = RunnerClient() + + try: + logger.info( + f"Attempting to switch project {project_id} to commit {request.commit_hash}" + ) + # Call the runner using the updated client method + runner_response = await runner_client.switch_commit( + project_id=project_id, commit_hash=request.commit_hash + ) + + if isinstance(runner_response, ErrorModel): + logger.error(f"Runner failed to switch commit for project {project_id}") + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to switch commit", + ) + + logger.info( + f"Successfully switched project {project_id} to commit {request.commit_hash}" + ) + + # Add memory item and compact memory + try: + agent = await get_agent(project_id) + memory_message = ( + "The project state has been reverted to an earlier point in the development history. " + "IMPORTANT: Any file contents previously accessed or stored in memory are now STALE. " + "You must re-read ALL files before working with them. " + ) + await agent.message_manager.add_memory_item(memory_message) + logger.info(f"Added revert memory item for project {project_id}") + # Trigger compaction + await agent.message_manager.compact_memory() + logger.info(f"Triggered memory compaction for project {project_id}") + except Exception as agent_err: + # Log agent-related errors but don't fail the API call + logger.warning( + f"Error during agent memory update/compaction for project {project_id} after commit switch: {agent_err}" + ) + + return SwitchCommitResponse( + message="Commit switch successful", + success=True, + ) + + except HTTPException as http_exc: + raise http_exc + except Exception as e: + logger.exception( + f"Unexpected error switching commit for project {project_id}: {e}" + ) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"An unexpected error occurred: {str(e)}", + ) + + def _cleanup_zip_file(zip_path: str) -> None: """Clean up the temporary zip file after it has been sent to the client. diff --git a/cp-agent/cp_agent/schemas/projects.py b/cp-agent/cp_agent/schemas/projects.py index afc0215..14408db 100644 --- a/cp-agent/cp_agent/schemas/projects.py +++ b/cp-agent/cp_agent/schemas/projects.py @@ -51,3 +51,16 @@ class ListProjectPathsResponse(BaseModel): """Response model for listing project paths.""" paths: List[str] + + +class SwitchCommitRequest(BaseModel): + """Request model for switching git commit.""" + + commit_hash: str = Field(..., description="The git commit hash to switch to") + + +class SwitchCommitResponse(BaseModel): + """Response model for switching git commit.""" + + message: str = Field(..., description="Success or error message") + success: bool = Field(..., description="Whether the switch was successful") diff --git a/cp-agent/cp_agent/utils/runner_client.py b/cp-agent/cp_agent/utils/runner_client.py index 2d2fa48..30d886f 100644 --- a/cp-agent/cp_agent/utils/runner_client.py +++ b/cp-agent/cp_agent/utils/runner_client.py @@ -1,6 +1,9 @@ """Client for interacting with cp-runner API.""" from cp_agent.config import settings +from cp_agent.generated.api.git import ( + switch_commit as switch_commit_api, +) from cp_agent.generated.api.projects import add_package as add_package_api from cp_agent.generated.api.projects import check_build_errors from cp_agent.generated.api.projects import lint_project as lint_project_api @@ -17,11 +20,18 @@ from cp_agent.generated.models.project_operation_response_body import ( ProjectOperationResponseBody, ) +from cp_agent.generated.models.switch_commit_request_body import ( + SwitchCommitRequestBody, +) +from cp_agent.generated.models.switch_commit_response_body import ( + SwitchCommitResponseBody, +) ResponseType = ErrorModel | ProjectOperationResponseBody BuildErrorType = ErrorModel | BuildErrorResponseBody LintResponseType = ErrorModel | LintResponseBody AddPackageResponseType = ErrorModel | AddPackageResponseBody +SwitchCommitResponseType = ErrorModel | SwitchCommitResponseBody # Added type alias class RunnerClient: @@ -87,3 +97,22 @@ async def add_package( return response except Exception as e: raise RuntimeError(f"Failed to install package: {e}") + + async def switch_commit( + self, project_id: str, commit_hash: str + ) -> SwitchCommitResponseType: + """Switch the project's working directory to a specific commit via the runner.""" + try: + request_body = SwitchCommitRequestBody( + project_id=project_id, commit_hash=commit_hash + ) + response = await switch_commit_api.asyncio( + client=self.client, body=request_body + ) + + if not response: + raise RuntimeError("No response received from runner") + + return response + except Exception as e: + raise RuntimeError(f"Failed to switch commit for project {project_id}: {e}") diff --git a/cp-webapp/src/components/snapshot-view/context.tsx b/cp-webapp/src/components/snapshot-view/context.tsx index eec97f4..1d3e639 100644 --- a/cp-webapp/src/components/snapshot-view/context.tsx +++ b/cp-webapp/src/components/snapshot-view/context.tsx @@ -2,7 +2,7 @@ import React, { createContext, useContext, useState, ReactNode } from 'react'; import { useMutation } from '@tanstack/react-query'; -import { switchCommitMutation } from '@/generated/runner/@tanstack/react-query.gen'; +import { switchCommitApiV1ProjectsProjectIdSwitchCommitPostMutation } from '@/generated/agent/@tanstack/react-query.gen'; import { useToast } from '@/hooks/use-toast'; import { useProject } from '@/context'; @@ -35,7 +35,9 @@ export function SnapshotProvider({ children }: { children?: ReactNode }) { const { toast } = useToast(); - const switchCommitMutate = useMutation(switchCommitMutation()); + const switchCommitMutate = useMutation( + switchCommitApiV1ProjectsProjectIdSwitchCommitPostMutation() + ); const triggerRefresh = () => { setRefreshTrigger(prev => prev + 1); @@ -47,8 +49,10 @@ export function SnapshotProvider({ children }: { children?: ReactNode }) { try { setSwitchingCommit(true); await switchCommitMutate.mutateAsync({ - body: { + path: { project_id: projectId, + }, + body: { commit_hash: hash, }, }); diff --git a/cp-webapp/src/generated/agent/@tanstack/react-query.gen.ts b/cp-webapp/src/generated/agent/@tanstack/react-query.gen.ts index e8b6ef1..dc404ce 100644 --- a/cp-webapp/src/generated/agent/@tanstack/react-query.gen.ts +++ b/cp-webapp/src/generated/agent/@tanstack/react-query.gen.ts @@ -2,8 +2,8 @@ import type { Options } from '@hey-api/client-fetch'; import { queryOptions, type UseMutationOptions } from '@tanstack/react-query'; -import type { ChatApiV1ChatPostData, ChatApiV1ChatPostError, CreateProjectApiV1ProjectsPostData, CreateProjectApiV1ProjectsPostError, CreateProjectApiV1ProjectsPostResponse, IntegrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabasePostData, IntegrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabasePostError, IntegrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabasePostResponse, SetProjectSecretApiV1ProjectsProjectIdSecretsPostData, SetProjectSecretApiV1ProjectsProjectIdSecretsPostError, SetProjectSecretApiV1ProjectsProjectIdSecretsPostResponse, UploadAssetsApiV1ProjectsProjectIdUploadPostData, UploadAssetsApiV1ProjectsProjectIdUploadPostError, UploadAssetsApiV1ProjectsProjectIdUploadPostResponse, GenerateSummaryApiV1ProjectsProjectIdGenerateSummaryPostData, GenerateSummaryApiV1ProjectsProjectIdGenerateSummaryPostError, GenerateSummaryApiV1ProjectsProjectIdGenerateSummaryPostResponse, ExecuteMigrationApiV1ProjectsProjectIdMigrationsPostData, ExecuteMigrationApiV1ProjectsProjectIdMigrationsPostError, ExecuteMigrationApiV1ProjectsProjectIdMigrationsPostResponse, ListProjectPathsApiV1ProjectsProjectIdPathsGetData, DownloadProjectApiV1ProjectsProjectIdDownloadGetData, RootGetData, HealthCheckHealthGetData } from '../types.gen'; -import { chatApiV1ChatPost, createProjectApiV1ProjectsPost, integrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabasePost, setProjectSecretApiV1ProjectsProjectIdSecretsPost, uploadAssetsApiV1ProjectsProjectIdUploadPost, generateSummaryApiV1ProjectsProjectIdGenerateSummaryPost, executeMigrationApiV1ProjectsProjectIdMigrationsPost, listProjectPathsApiV1ProjectsProjectIdPathsGet, downloadProjectApiV1ProjectsProjectIdDownloadGet, rootGet, healthCheckHealthGet, client } from '../sdk.gen'; +import type { ChatApiV1ChatPostData, ChatApiV1ChatPostError, HeartbeatApiV1HeartbeatPostData, HeartbeatApiV1HeartbeatPostError, HeartbeatApiV1HeartbeatPostResponse, CreateProjectApiV1ProjectsPostData, CreateProjectApiV1ProjectsPostError, CreateProjectApiV1ProjectsPostResponse, IntegrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabasePostData, IntegrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabasePostError, IntegrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabasePostResponse, SetProjectSecretApiV1ProjectsProjectIdSecretsPostData, SetProjectSecretApiV1ProjectsProjectIdSecretsPostError, SetProjectSecretApiV1ProjectsProjectIdSecretsPostResponse, UploadAssetsApiV1ProjectsProjectIdUploadPostData, UploadAssetsApiV1ProjectsProjectIdUploadPostError, UploadAssetsApiV1ProjectsProjectIdUploadPostResponse, GenerateSummaryApiV1ProjectsProjectIdGenerateSummaryPostData, GenerateSummaryApiV1ProjectsProjectIdGenerateSummaryPostError, GenerateSummaryApiV1ProjectsProjectIdGenerateSummaryPostResponse, ExecuteMigrationApiV1ProjectsProjectIdMigrationsPostData, ExecuteMigrationApiV1ProjectsProjectIdMigrationsPostError, ExecuteMigrationApiV1ProjectsProjectIdMigrationsPostResponse, ListProjectPathsApiV1ProjectsProjectIdPathsGetData, DownloadProjectApiV1ProjectsProjectIdDownloadGetData, SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostData, SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostError, SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostResponse, CreateSubdomainApiV1ProjectsProjectIdSubdomainPutData, CreateSubdomainApiV1ProjectsProjectIdSubdomainPutError, CreateSubdomainApiV1ProjectsProjectIdSubdomainPutResponse, EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutData, EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutError, EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutResponse, DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteData, DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteError, DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteResponse, GetProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetData, PublishProjectApiV1ProjectsProjectIdPublishPostData, PublishProjectApiV1ProjectsProjectIdPublishPostError, PublishProjectApiV1ProjectsProjectIdPublishPostResponse, GetDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetData, GetCreditsApiV1SubscriptionsCreditsGetData, RootGetData, HealthCheckHealthGetData, MetricsMetricsGetData } from '../types.gen'; +import { chatApiV1ChatPost, heartbeatApiV1HeartbeatPost, createProjectApiV1ProjectsPost, integrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabasePost, setProjectSecretApiV1ProjectsProjectIdSecretsPost, uploadAssetsApiV1ProjectsProjectIdUploadPost, generateSummaryApiV1ProjectsProjectIdGenerateSummaryPost, executeMigrationApiV1ProjectsProjectIdMigrationsPost, listProjectPathsApiV1ProjectsProjectIdPathsGet, downloadProjectApiV1ProjectsProjectIdDownloadGet, switchCommitApiV1ProjectsProjectIdSwitchCommitPost, createSubdomainApiV1ProjectsProjectIdSubdomainPut, editProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPut, deleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDelete, getProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGet, publishProjectApiV1ProjectsProjectIdPublishPost, getDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGet, getCreditsApiV1SubscriptionsCreditsGet, rootGet, healthCheckHealthGet, metricsMetricsGet, client } from '../sdk.gen'; type QueryKey = [ Pick & { @@ -65,6 +65,39 @@ export const chatApiV1ChatPostMutation = (options?: Partial) => [ + createQueryKey('heartbeatApiV1HeartbeatPost', options) +]; + +export const heartbeatApiV1HeartbeatPostOptions = (options?: Options) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await heartbeatApiV1HeartbeatPost({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: heartbeatApiV1HeartbeatPostQueryKey(options) + }); +}; + +export const heartbeatApiV1HeartbeatPostMutation = (options?: Partial>) => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (localOptions) => { + const { data } = await heartbeatApiV1HeartbeatPost({ + ...options, + ...localOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; +}; + export const createProjectApiV1ProjectsPostQueryKey = (options: Options) => [ createQueryKey('createProjectApiV1ProjectsPost', options) ]; @@ -301,6 +334,171 @@ export const downloadProjectApiV1ProjectsProjectIdDownloadGetOptions = (options: }); }; +export const switchCommitApiV1ProjectsProjectIdSwitchCommitPostQueryKey = (options: Options) => [ + createQueryKey('switchCommitApiV1ProjectsProjectIdSwitchCommitPost', options) +]; + +export const switchCommitApiV1ProjectsProjectIdSwitchCommitPostOptions = (options: Options) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await switchCommitApiV1ProjectsProjectIdSwitchCommitPost({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: switchCommitApiV1ProjectsProjectIdSwitchCommitPostQueryKey(options) + }); +}; + +export const switchCommitApiV1ProjectsProjectIdSwitchCommitPostMutation = (options?: Partial>) => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (localOptions) => { + const { data } = await switchCommitApiV1ProjectsProjectIdSwitchCommitPost({ + ...options, + ...localOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; +}; + +export const createSubdomainApiV1ProjectsProjectIdSubdomainPutMutation = (options?: Partial>) => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (localOptions) => { + const { data } = await createSubdomainApiV1ProjectsProjectIdSubdomainPut({ + ...options, + ...localOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; +}; + +export const editProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutMutation = (options?: Partial>) => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (localOptions) => { + const { data } = await editProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPut({ + ...options, + ...localOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; +}; + +export const deleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteMutation = (options?: Partial>) => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (localOptions) => { + const { data } = await deleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDelete({ + ...options, + ...localOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; +}; + +export const getProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetQueryKey = (options: Options) => [ + createQueryKey('getProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGet', options) +]; + +export const getProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetOptions = (options: Options) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGet({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: getProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetQueryKey(options) + }); +}; + +export const publishProjectApiV1ProjectsProjectIdPublishPostQueryKey = (options: Options) => [ + createQueryKey('publishProjectApiV1ProjectsProjectIdPublishPost', options) +]; + +export const publishProjectApiV1ProjectsProjectIdPublishPostOptions = (options: Options) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await publishProjectApiV1ProjectsProjectIdPublishPost({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: publishProjectApiV1ProjectsProjectIdPublishPostQueryKey(options) + }); +}; + +export const publishProjectApiV1ProjectsProjectIdPublishPostMutation = (options?: Partial>) => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (localOptions) => { + const { data } = await publishProjectApiV1ProjectsProjectIdPublishPost({ + ...options, + ...localOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; +}; + +export const getDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetQueryKey = (options: Options) => [ + createQueryKey('getDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGet', options) +]; + +export const getDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetOptions = (options: Options) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGet({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: getDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetQueryKey(options) + }); +}; + +export const getCreditsApiV1SubscriptionsCreditsGetQueryKey = (options?: Options) => [ + createQueryKey('getCreditsApiV1SubscriptionsCreditsGet', options) +]; + +export const getCreditsApiV1SubscriptionsCreditsGetOptions = (options?: Options) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await getCreditsApiV1SubscriptionsCreditsGet({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: getCreditsApiV1SubscriptionsCreditsGetQueryKey(options) + }); +}; + export const rootGetQueryKey = (options?: Options) => [ createQueryKey('rootGet', options) ]; @@ -337,4 +535,23 @@ export const healthCheckHealthGetOptions = (options?: Options) => [ + createQueryKey('metricsMetricsGet', options) +]; + +export const metricsMetricsGetOptions = (options?: Options) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await metricsMetricsGet({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: metricsMetricsGetQueryKey(options) + }); }; \ No newline at end of file diff --git a/cp-webapp/src/generated/agent/sdk.gen.ts b/cp-webapp/src/generated/agent/sdk.gen.ts index 5f40524..2d8dbe6 100644 --- a/cp-webapp/src/generated/agent/sdk.gen.ts +++ b/cp-webapp/src/generated/agent/sdk.gen.ts @@ -1,7 +1,7 @@ // This file is auto-generated by @hey-api/openapi-ts import { createClient, createConfig, type Options, formDataBodySerializer } from '@hey-api/client-fetch'; -import type { ChatApiV1ChatPostData, ChatApiV1ChatPostError, CreateProjectApiV1ProjectsPostData, CreateProjectApiV1ProjectsPostResponse, CreateProjectApiV1ProjectsPostError, IntegrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabasePostData, IntegrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabasePostResponse, IntegrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabasePostError, SetProjectSecretApiV1ProjectsProjectIdSecretsPostData, SetProjectSecretApiV1ProjectsProjectIdSecretsPostResponse, SetProjectSecretApiV1ProjectsProjectIdSecretsPostError, UploadAssetsApiV1ProjectsProjectIdUploadPostData, UploadAssetsApiV1ProjectsProjectIdUploadPostResponse, UploadAssetsApiV1ProjectsProjectIdUploadPostError, GenerateSummaryApiV1ProjectsProjectIdGenerateSummaryPostData, GenerateSummaryApiV1ProjectsProjectIdGenerateSummaryPostResponse, GenerateSummaryApiV1ProjectsProjectIdGenerateSummaryPostError, ExecuteMigrationApiV1ProjectsProjectIdMigrationsPostData, ExecuteMigrationApiV1ProjectsProjectIdMigrationsPostResponse, ExecuteMigrationApiV1ProjectsProjectIdMigrationsPostError, ListProjectPathsApiV1ProjectsProjectIdPathsGetData, ListProjectPathsApiV1ProjectsProjectIdPathsGetResponse, ListProjectPathsApiV1ProjectsProjectIdPathsGetError, DownloadProjectApiV1ProjectsProjectIdDownloadGetData, DownloadProjectApiV1ProjectsProjectIdDownloadGetError, RootGetData, RootGetResponse, HealthCheckHealthGetData, HealthCheckHealthGetResponse } from './types.gen'; +import type { ChatApiV1ChatPostData, ChatApiV1ChatPostError, HeartbeatApiV1HeartbeatPostData, HeartbeatApiV1HeartbeatPostResponse, HeartbeatApiV1HeartbeatPostError, CreateProjectApiV1ProjectsPostData, CreateProjectApiV1ProjectsPostResponse, CreateProjectApiV1ProjectsPostError, IntegrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabasePostData, IntegrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabasePostResponse, IntegrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabasePostError, SetProjectSecretApiV1ProjectsProjectIdSecretsPostData, SetProjectSecretApiV1ProjectsProjectIdSecretsPostResponse, SetProjectSecretApiV1ProjectsProjectIdSecretsPostError, UploadAssetsApiV1ProjectsProjectIdUploadPostData, UploadAssetsApiV1ProjectsProjectIdUploadPostResponse, UploadAssetsApiV1ProjectsProjectIdUploadPostError, GenerateSummaryApiV1ProjectsProjectIdGenerateSummaryPostData, GenerateSummaryApiV1ProjectsProjectIdGenerateSummaryPostResponse, GenerateSummaryApiV1ProjectsProjectIdGenerateSummaryPostError, ExecuteMigrationApiV1ProjectsProjectIdMigrationsPostData, ExecuteMigrationApiV1ProjectsProjectIdMigrationsPostResponse, ExecuteMigrationApiV1ProjectsProjectIdMigrationsPostError, ListProjectPathsApiV1ProjectsProjectIdPathsGetData, ListProjectPathsApiV1ProjectsProjectIdPathsGetResponse, ListProjectPathsApiV1ProjectsProjectIdPathsGetError, DownloadProjectApiV1ProjectsProjectIdDownloadGetData, DownloadProjectApiV1ProjectsProjectIdDownloadGetError, SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostData, SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostResponse, SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostError, CreateSubdomainApiV1ProjectsProjectIdSubdomainPutData, CreateSubdomainApiV1ProjectsProjectIdSubdomainPutResponse, CreateSubdomainApiV1ProjectsProjectIdSubdomainPutError, EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutData, EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutResponse, EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutError, DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteData, DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteResponse, DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteError, GetProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetData, GetProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetResponse, GetProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetError, PublishProjectApiV1ProjectsProjectIdPublishPostData, PublishProjectApiV1ProjectsProjectIdPublishPostResponse, PublishProjectApiV1ProjectsProjectIdPublishPostError, GetDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetData, GetDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetResponse, GetDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetError, GetCreditsApiV1SubscriptionsCreditsGetData, GetCreditsApiV1SubscriptionsCreditsGetResponse, RootGetData, RootGetResponse, HealthCheckHealthGetData, HealthCheckHealthGetResponse, MetricsMetricsGetData } from './types.gen'; export const client = createClient(createConfig()); @@ -11,12 +11,20 @@ export const client = createClient(createConfig()); * * Args: * request: The chat request containing MessageContent and project info + * user: Optional authenticated user, required in hosted mode + * _: Dependency that verifies the user owns the project * * Returns: * A streaming response of SSE events */ export const chatApiV1ChatPost = (options: Options) => { return (options?.client ?? client).post({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], url: '/api/v1/chat/', ...options, headers: { @@ -26,22 +34,41 @@ export const chatApiV1ChatPost = (options: }); }; +/** + * Heartbeat + * Record heartbeat for project. + */ +export const heartbeatApiV1HeartbeatPost = (options?: Options) => { + return (options?.client ?? client).post({ + url: '/api/v1/heartbeat/', + ...options + }); +}; + /** * Create Project * Create a new project. * * Args: * request: Project creation request containing project_id and optional name + * user: Optional authenticated user, required in hosted mode * * Returns: * Project: The created project details * * Raises: * HTTPException: 400 if project ID is invalid + * HTTPException: 403 if user authentication is required but missing * HTTPException: 500 if project creation fails */ export const createProjectApiV1ProjectsPost = (options: Options) => { return (options?.client ?? client).post({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], url: '/api/v1/projects/', ...options, headers: { @@ -57,16 +84,26 @@ export const createProjectApiV1ProjectsPost = (options: Options) => { return (options?.client ?? client).post({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], url: '/api/v1/projects/{project_id}/integrate-supabase', ...options }); @@ -79,16 +116,25 @@ export const integrateSupabaseWithProjectApiV1ProjectsProjectIdIntegrateSupabase * Args: * project_id: The ID of the project * request: Secret request containing name and value + * user: Optional authenticated user, required in hosted mode + * _: Dependency that verifies the user owns the project * * Returns: * Dict with success message * * Raises: * HTTPException: 404 if project doesn't exist + * HTTPException: 403 if user authentication is required but missing * HTTPException: 500 if setting the secret fails */ export const setProjectSecretApiV1ProjectsProjectIdSecretsPost = (options: Options) => { return (options?.client ?? client).post({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], url: '/api/v1/projects/{project_id}/secrets', ...options, headers: { @@ -105,6 +151,7 @@ export const setProjectSecretApiV1ProjectsProjectIdSecretsPost = (options: Options) => { return (options?.client ?? client).post({ ...formDataBodySerializer, + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], url: '/api/v1/projects/{project_id}/upload', ...options, headers: { @@ -131,6 +184,8 @@ export const uploadAssetsApiV1ProjectsProjectIdUploadPost = (options: Options) => { return (options?.client ?? client).post({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], url: '/api/v1/projects/{project_id}/generate-summary', ...options, headers: { @@ -157,6 +218,8 @@ export const generateSummaryApiV1ProjectsProjectIdGenerateSummaryPost = (options: Options) => { return (options?.client ?? client).post({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], url: '/api/v1/projects/{project_id}/migrations', ...options, headers: { @@ -183,6 +253,7 @@ export const executeMigrationApiV1ProjectsProjectIdMigrationsPost = (options: Options) => { return (options?.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], url: '/api/v1/projects/{project_id}/paths', ...options }); @@ -205,6 +282,7 @@ export const listProjectPathsApiV1ProjectsProjectIdPathsGet = (options: Options) => { return (options?.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], url: '/api/v1/projects/{project_id}/download', ...options }); }; +/** + * Switch Commit + * Switch the project's working directory to a specific git commit. + * + * Args: + * project_id: The ID of the project. + * request: Request containing the commit hash. + * _: Dependency that verifies the user owns the project. + * + * Returns: + * SwitchCommitResponse: Indicates success or failure. + * + * Raises: + * HTTPException: 404 if project doesn't exist. + * HTTPException: 500 if switching commit fails. + */ +export const switchCommitApiV1ProjectsProjectIdSwitchCommitPost = (options: Options) => { + return (options?.client ?? client).post({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/switch-commit', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); +}; + +/** + * Create Subdomain + * Create a project's custom subdomain. + * + * Creates a production domain for the project if none exists yet. + * If a production subdomain already exists, returns an error. + * + * Args: + * project_id: The ID of the project + * request: The subdomain update request + * user: The authenticated user + * _: Dependency that verifies the user owns the project + * + * Returns: + * SubdomainUpdateResponse: Response containing the subdomain info + * + * Raises: + * HTTPException: If subdomain is invalid or production subdomain already exists + */ +export const createSubdomainApiV1ProjectsProjectIdSubdomainPut = (options: Options) => { + return (options?.client ?? client).put({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/subdomain', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); +}; + +/** + * Edit Project Subdomain + * Edit a project's existing production subdomain. + * + * This will rename the subdomain in the database and update the Cloudflare KV mapping. + * Original content in R2 will remain unchanged. + * + * Args: + * project_id: The ID of the project + * request: The subdomain edit request containing the current and new subdomain + * user: The authenticated user + * _: Dependency that verifies the user owns the project + * + * Returns: + * SubdomainUpdateResponse: Response containing the updated subdomain info + * + * Raises: + * HTTPException: If subdomain is invalid, doesn't exist, or the new name is already taken + */ +export const editProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPut = (options: Options) => { + return (options?.client ?? client).put({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/subdomain/edit', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); +}; + +/** + * Delete Subdomain + * Delete a project's production subdomain and its associated resources. + * + * This will: + * 1. Delete the subdomain record from the database + * 2. Delete the Cloudflare KV mapping + * 3. Create an audit record for later R2 content cleanup + * + * Args: + * project_id: The ID of the project + * domain: The full domain name to delete (e.g., my-project.yourdomain.com) + * _: Dependency that verifies the user owns the project + * + * Returns: + * SubdomainDeleteResponse: Response confirming deletion + * + * Raises: + * HTTPException: If the domain doesn't exist or deletion fails + */ +export const deleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDelete = (options: Options) => { + return (options?.client ?? client).delete({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/subdomain/{domain}', + ...options + }); +}; + +/** + * Get Project Publish History + * Get the publish history for a project. + * + * This returns the site_domains records which contain the publishing info. + * + * Args: + * project_id: The ID of the project + * _: Dependency that verifies the user owns the project + * + * Returns: + * List of site domains with publish history information + */ +export const getProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGet = (options: Options) => { + return (options?.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/publish-history', + ...options + }); +}; + +/** + * Publish Project + * Publish a project to selected domains. + * + * This endpoint builds the project and deploys it to the selected domains. + * Each domain gets created/updated and published in a single operation. + * + * Args: + * project_id: The ID of the project + * request: The publish request containing domain names to publish to + * user: Authenticated user making the request + * _: Dependency that verifies the user owns the project + * + * Returns: + * PublishResponse: Details of the publishing operation + * + * Raises: + * HTTPException: 404 if project doesn't exist + * HTTPException: 400 if no domains specified + * HTTPException: 500 if publishing fails or SSL configuration fails + */ +export const publishProjectApiV1ProjectsProjectIdPublishPost = (options: Options) => { + return (options?.client ?? client).post({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/publish', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); +}; + +/** + * Get Domain Status + * Get the status of a custom domain's SSL configuration. + * + * This endpoint only works for custom domains, not subdomains. + * It will check the SSL status and verification status in Cloudflare. + * + * Args: + * project_id: The ID of the project + * domain: The domain name to check + * _: Dependency that verifies the user owns the project + * + * Returns: + * Dict with domain status information + * + * Raises: + * HTTPException: 400 if domain is not a custom domain + * HTTPException: 404 if domain not found + * HTTPException: 500 if status check fails + */ +export const getDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGet = (options: Options) => { + return (options?.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/domain-status/{domain}', + ...options + }); +}; + +/** + * Get Credits + */ +export const getCreditsApiV1SubscriptionsCreditsGet = (options?: Options) => { + return (options?.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/subscriptions/credits', + ...options + }); +}; + /** * Root * Root endpoint. @@ -240,4 +575,15 @@ export const healthCheckHealthGet = (optio url: '/health', ...options }); +}; + +/** + * Metrics + * Prometheus metrics endpoint. + */ +export const metricsMetricsGet = (options?: Options) => { + return (options?.client ?? client).get({ + url: '/metrics', + ...options + }); }; \ No newline at end of file diff --git a/cp-webapp/src/generated/agent/types.gen.ts b/cp-webapp/src/generated/agent/types.gen.ts index d593061..a9ed4e4 100644 --- a/cp-webapp/src/generated/agent/types.gen.ts +++ b/cp-webapp/src/generated/agent/types.gen.ts @@ -19,6 +19,21 @@ export type ChatRequest = { context: RequestContext; }; +export type CreditBalance = { + /** + * Type of credit (e.g., 'free', 'promotional', 'purchased') + */ + credit_type: string; + /** + * Number of credits remaining for this credit type + */ + credits_remaining: number; + /** + * Expiration date of the credits, if applicable + */ + valid_until?: string | null; +}; + /** * Request model for generating project summary. */ @@ -43,6 +58,21 @@ export type GenerateSummaryResponse = { description: string; }; +export type GetCreditsResponse = { + /** + * Number of free credits available to the user + */ + free_credits: number; + /** + * Detailed breakdown of different credit balances + */ + credit_balances: Array; + /** + * Total number of credits available across all credit types + */ + total_credits: number; +}; + export type HttpValidationError = { detail?: Array; }; @@ -116,6 +146,39 @@ export type ProjectCreateRequest = { name?: string | null; }; +/** + * Individual item in a project's publishing history. + */ +export type PublishHistoryItem = { + id: string; + domain_name: string; + domain_type: string; + is_prod: boolean; + published_at: string; + publish_count: number; +}; + +/** + * Request model for publishing to selected domains. + */ +export type PublishRequest = { + /** + * List of domain names to publish to + */ + domain_names?: Array; +}; + +/** + * Response model for publishing a project. + */ +export type PublishResponse = { + message: string; + publishedDomains: Array; + results: Array<{ + [key: string]: unknown; + }>; +}; + /** * Context information for chat requests. */ @@ -128,6 +191,62 @@ export type SecretRequest = { value: string; }; +/** + * Response after deleting a subdomain. + */ +export type SubdomainDeleteResponse = { + message: string; + domain: string; +}; + +/** + * Request to edit an existing subdomain. + */ +export type SubdomainEditRequest = { + current_domain: string; + new_subdomain: string; +}; + +/** + * Request model for updating a project's custom subdomain. + */ +export type SubdomainUpdateRequest = { + subdomain: string; +}; + +/** + * Response model for updating a project's subdomain. + */ +export type SubdomainUpdateResponse = { + message: string; + subdomain: string; + domain: string; +}; + +/** + * Request model for switching git commit. + */ +export type SwitchCommitRequest = { + /** + * The git commit hash to switch to + */ + commit_hash: string; +}; + +/** + * Response model for switching git commit. + */ +export type SwitchCommitResponse = { + /** + * Success or error message + */ + message: string; + /** + * Whether the switch was successful + */ + success: boolean; +}; + export type TextBlock = { type: string; text: string; @@ -162,6 +281,36 @@ export type ChatApiV1ChatPostResponses = { 200: unknown; }; +export type HeartbeatApiV1HeartbeatPostData = { + body?: never; + headers?: { + 'X-Project-ID'?: string | null; + }; + path?: never; + query?: never; + url: '/api/v1/heartbeat/'; +}; + +export type HeartbeatApiV1HeartbeatPostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type HeartbeatApiV1HeartbeatPostError = HeartbeatApiV1HeartbeatPostErrors[keyof HeartbeatApiV1HeartbeatPostErrors]; + +export type HeartbeatApiV1HeartbeatPostResponses = { + /** + * Successful Response + */ + 200: { + [key: string]: unknown; + }; +}; + +export type HeartbeatApiV1HeartbeatPostResponse = HeartbeatApiV1HeartbeatPostResponses[keyof HeartbeatApiV1HeartbeatPostResponses]; + export type CreateProjectApiV1ProjectsPostData = { body: ProjectCreateRequest; path?: never; @@ -380,6 +529,215 @@ export type DownloadProjectApiV1ProjectsProjectIdDownloadGetResponses = { 200: unknown; }; +export type SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostData = { + body: SwitchCommitRequest; + path: { + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/switch-commit'; +}; + +export type SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostError = SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostErrors[keyof SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostErrors]; + +export type SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostResponses = { + /** + * Successful Response + */ + 200: SwitchCommitResponse; +}; + +export type SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostResponse = SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostResponses[keyof SwitchCommitApiV1ProjectsProjectIdSwitchCommitPostResponses]; + +export type CreateSubdomainApiV1ProjectsProjectIdSubdomainPutData = { + body: SubdomainUpdateRequest; + path: { + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/subdomain'; +}; + +export type CreateSubdomainApiV1ProjectsProjectIdSubdomainPutErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CreateSubdomainApiV1ProjectsProjectIdSubdomainPutError = CreateSubdomainApiV1ProjectsProjectIdSubdomainPutErrors[keyof CreateSubdomainApiV1ProjectsProjectIdSubdomainPutErrors]; + +export type CreateSubdomainApiV1ProjectsProjectIdSubdomainPutResponses = { + /** + * Successful Response + */ + 200: SubdomainUpdateResponse; +}; + +export type CreateSubdomainApiV1ProjectsProjectIdSubdomainPutResponse = CreateSubdomainApiV1ProjectsProjectIdSubdomainPutResponses[keyof CreateSubdomainApiV1ProjectsProjectIdSubdomainPutResponses]; + +export type EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutData = { + body: SubdomainEditRequest; + path: { + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/subdomain/edit'; +}; + +export type EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutError = EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutErrors[keyof EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutErrors]; + +export type EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutResponses = { + /** + * Successful Response + */ + 200: SubdomainUpdateResponse; +}; + +export type EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutResponse = EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutResponses[keyof EditProjectSubdomainApiV1ProjectsProjectIdSubdomainEditPutResponses]; + +export type DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteData = { + body?: never; + path: { + project_id: string; + domain: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/subdomain/{domain}'; +}; + +export type DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteError = DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteErrors[keyof DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteErrors]; + +export type DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteResponses = { + /** + * Successful Response + */ + 200: SubdomainDeleteResponse; +}; + +export type DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteResponse = DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteResponses[keyof DeleteSubdomainApiV1ProjectsProjectIdSubdomainDomainDeleteResponses]; + +export type GetProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetData = { + body?: never; + path: { + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/publish-history'; +}; + +export type GetProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetError = GetProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetErrors[keyof GetProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetErrors]; + +export type GetProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetResponses = { + /** + * Successful Response + */ + 200: Array; +}; + +export type GetProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetResponse = GetProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetResponses[keyof GetProjectPublishHistoryApiV1ProjectsProjectIdPublishHistoryGetResponses]; + +export type PublishProjectApiV1ProjectsProjectIdPublishPostData = { + body: PublishRequest; + path: { + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/publish'; +}; + +export type PublishProjectApiV1ProjectsProjectIdPublishPostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type PublishProjectApiV1ProjectsProjectIdPublishPostError = PublishProjectApiV1ProjectsProjectIdPublishPostErrors[keyof PublishProjectApiV1ProjectsProjectIdPublishPostErrors]; + +export type PublishProjectApiV1ProjectsProjectIdPublishPostResponses = { + /** + * Successful Response + */ + 200: PublishResponse; +}; + +export type PublishProjectApiV1ProjectsProjectIdPublishPostResponse = PublishProjectApiV1ProjectsProjectIdPublishPostResponses[keyof PublishProjectApiV1ProjectsProjectIdPublishPostResponses]; + +export type GetDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetData = { + body?: never; + path: { + project_id: string; + domain: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/domain-status/{domain}'; +}; + +export type GetDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetError = GetDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetErrors[keyof GetDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetErrors]; + +export type GetDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetResponses = { + /** + * Successful Response + */ + 200: { + [key: string]: unknown; + }; +}; + +export type GetDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetResponse = GetDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetResponses[keyof GetDomainStatusApiV1ProjectsProjectIdDomainStatusDomainGetResponses]; + +export type GetCreditsApiV1SubscriptionsCreditsGetData = { + body?: never; + path?: never; + query?: never; + url: '/api/v1/subscriptions/credits'; +}; + +export type GetCreditsApiV1SubscriptionsCreditsGetResponses = { + /** + * Successful Response + */ + 200: GetCreditsResponse; +}; + +export type GetCreditsApiV1SubscriptionsCreditsGetResponse = GetCreditsApiV1SubscriptionsCreditsGetResponses[keyof GetCreditsApiV1SubscriptionsCreditsGetResponses]; + export type RootGetData = { body?: never; path?: never; @@ -414,4 +772,18 @@ export type HealthCheckHealthGetResponses = { }; }; -export type HealthCheckHealthGetResponse = HealthCheckHealthGetResponses[keyof HealthCheckHealthGetResponses]; \ No newline at end of file +export type HealthCheckHealthGetResponse = HealthCheckHealthGetResponses[keyof HealthCheckHealthGetResponses]; + +export type MetricsMetricsGetData = { + body?: never; + path?: never; + query?: never; + url: '/metrics'; +}; + +export type MetricsMetricsGetResponses = { + /** + * Successful Response + */ + 200: unknown; +}; \ No newline at end of file diff --git a/cp-webapp/src/generated/runner/@tanstack/react-query.gen.ts b/cp-webapp/src/generated/runner/@tanstack/react-query.gen.ts index 02d9e8d..7a2673f 100644 --- a/cp-webapp/src/generated/runner/@tanstack/react-query.gen.ts +++ b/cp-webapp/src/generated/runner/@tanstack/react-query.gen.ts @@ -2,8 +2,8 @@ import type { Options } from '@hey-api/client-fetch'; import { queryOptions, infiniteQueryOptions, type InfiniteData, type UseMutationOptions } from '@tanstack/react-query'; -import type { CheckPreviewData, GetFileContentData, GetFileTreeData, GetCommitsData, GetCommitsError, GetCommitsResponse, GetCommitDiffData, GetFileDiffData, SwitchCommitData, SwitchCommitError, SwitchCommitResponse, HealthData, AddPackageData, AddPackageError, AddPackageResponse, CheckBuildErrorsData, CheckBuildErrorsError, CheckBuildErrorsResponse, LintProjectData, LintProjectError, LintProjectResponse, StartProjectData, StartProjectError, StartProjectResponse, StopProjectData, StopProjectError, StopProjectResponse } from '../types.gen'; -import { checkPreview, getFileContent, getFileTree, getCommits, getCommitDiff, getFileDiff, switchCommit, health, addPackage, checkBuildErrors, lintProject, startProject, stopProject, client } from '../sdk.gen'; +import type { CheckPreviewData, GetFileContentData, GetFileTreeData, GetCommitsData, GetCommitsError, GetCommitsResponse, GetCommitDiffData, GetFileDiffData, SwitchCommitData, SwitchCommitError, SwitchCommitResponse, HealthData, AddPackageData, AddPackageError, AddPackageResponse, BuildSiteData, BuildSiteError, BuildSiteResponse, CheckBuildErrorsData, CheckBuildErrorsError, CheckBuildErrorsResponse, LintProjectData, LintProjectError, LintProjectResponse, StartProjectData, StartProjectError, StartProjectResponse, StopProjectData, StopProjectError, StopProjectResponse } from '../types.gen'; +import { checkPreview, getFileContent, getFileTree, getCommits, getCommitDiff, getFileDiff, switchCommit, health, addPackage, buildSite, checkBuildErrors, lintProject, startProject, stopProject, client } from '../sdk.gen'; type QueryKey = [ Pick & { @@ -288,6 +288,39 @@ export const addPackageMutation = (options?: Partial>) = return mutationOptions; }; +export const buildSiteQueryKey = (options: Options) => [ + createQueryKey('buildSite', options) +]; + +export const buildSiteOptions = (options: Options) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await buildSite({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: buildSiteQueryKey(options) + }); +}; + +export const buildSiteMutation = (options?: Partial>) => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (localOptions) => { + const { data } = await buildSite({ + ...options, + ...localOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; +}; + export const checkBuildErrorsQueryKey = (options: Options) => [ createQueryKey('checkBuildErrors', options) ]; diff --git a/cp-webapp/src/generated/runner/sdk.gen.ts b/cp-webapp/src/generated/runner/sdk.gen.ts index ecad59e..18d6580 100644 --- a/cp-webapp/src/generated/runner/sdk.gen.ts +++ b/cp-webapp/src/generated/runner/sdk.gen.ts @@ -1,7 +1,7 @@ // This file is auto-generated by @hey-api/openapi-ts import { createClient, createConfig, type Options } from '@hey-api/client-fetch'; -import type { CheckPreviewData, CheckPreviewResponse, CheckPreviewError, GetFileContentData, GetFileContentResponse, GetFileContentError, GetFileTreeData, GetFileTreeResponse, GetFileTreeError, GetCommitsData, GetCommitsResponse, GetCommitsError, GetCommitDiffData, GetCommitDiffResponse, GetCommitDiffError, GetFileDiffData, GetFileDiffResponse, GetFileDiffError, SwitchCommitData, SwitchCommitResponse, SwitchCommitError, HealthData, HealthResponse, HealthError, AddPackageData, AddPackageResponse, AddPackageError, CheckBuildErrorsData, CheckBuildErrorsResponse, CheckBuildErrorsError, LintProjectData, LintProjectResponse, LintProjectError, StartProjectData, StartProjectResponse, StartProjectError, StopProjectData, StopProjectResponse, StopProjectError } from './types.gen'; +import type { CheckPreviewData, CheckPreviewResponse, CheckPreviewError, GetFileContentData, GetFileContentResponse, GetFileContentError, GetFileTreeData, GetFileTreeResponse, GetFileTreeError, GetCommitsData, GetCommitsResponse, GetCommitsError, GetCommitDiffData, GetCommitDiffResponse, GetCommitDiffError, GetFileDiffData, GetFileDiffResponse, GetFileDiffError, SwitchCommitData, SwitchCommitResponse, SwitchCommitError, HealthData, HealthResponse, HealthError, AddPackageData, AddPackageResponse, AddPackageError, BuildSiteData, BuildSiteResponse, BuildSiteError, CheckBuildErrorsData, CheckBuildErrorsResponse, CheckBuildErrorsError, LintProjectData, LintProjectResponse, LintProjectError, StartProjectData, StartProjectResponse, StartProjectError, StopProjectData, StopProjectResponse, StopProjectError } from './types.gen'; export const client = createClient(createConfig()); @@ -112,6 +112,21 @@ export const addPackage = (options: Option }); }; +/** + * Build website + * Build a website project and return the output directory + */ +export const buildSite = (options: Options) => { + return (options?.client ?? client).post({ + url: '/projects/build-site', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); +}; + /** * Check build errors * Check for build errors in a project diff --git a/cp-webapp/src/generated/runner/types.gen.ts b/cp-webapp/src/generated/runner/types.gen.ts index 18b6c50..d797918 100644 --- a/cp-webapp/src/generated/runner/types.gen.ts +++ b/cp-webapp/src/generated/runner/types.gen.ts @@ -49,6 +49,36 @@ export type BuildErrorResponseBody = { message: string; }; +export type BuildSiteRequestBody = { + /** + * A URL to the JSON Schema for this object. + */ + readonly $schema?: string; + /** + * ID of the project to build + */ + project_id: string; +}; + +export type BuildSiteResponseBody = { + /** + * A URL to the JSON Schema for this object. + */ + readonly $schema?: string; + /** + * Build output or error message + */ + message: string; + /** + * Path to the build output directory if successful + */ + output_dir: string; + /** + * Whether the build was successful + */ + success: boolean; +}; + export type CheckPreviewResponseBody = { /** * A URL to the JSON Schema for this object. @@ -618,6 +648,35 @@ export type AddPackageResponses = { export type AddPackageResponse = AddPackageResponses[keyof AddPackageResponses]; +export type BuildSiteData = { + body: BuildSiteRequestBody; + path?: never; + query?: never; + url: '/projects/build-site'; +}; + +export type BuildSiteErrors = { + /** + * Unprocessable Entity + */ + 422: ErrorModel; + /** + * Internal Server Error + */ + 500: ErrorModel; +}; + +export type BuildSiteError = BuildSiteErrors[keyof BuildSiteErrors]; + +export type BuildSiteResponses = { + /** + * OK + */ + 200: BuildSiteResponseBody; +}; + +export type BuildSiteResponse = BuildSiteResponses[keyof BuildSiteResponses]; + export type CheckBuildErrorsData = { body: ProjectOperationRequestBody; path?: never;