@lovable.dev/sdk 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/client.ts
2
+ import createClient from "openapi-fetch";
3
+
1
4
  // src/types.ts
2
5
  var ApiError = class extends Error {
3
6
  status;
@@ -23,10 +26,29 @@ function normalizeBaseUrl(url) {
23
26
  }
24
27
  return normalized;
25
28
  }
29
+ var errorMiddleware = {
30
+ async onResponse({ response }) {
31
+ if (response.ok) return;
32
+ let errorBody;
33
+ try {
34
+ errorBody = await response.clone().json();
35
+ } catch {
36
+ }
37
+ const message = buildErrorMessage(errorBody, response.status, response.statusText);
38
+ const type = errorBody?.type ?? errorBody?.title;
39
+ const detail = errorBody?.detail ?? errorBody?.details;
40
+ throw new ApiError(response.status, message, type, detail, errorBody?.props);
41
+ }
42
+ };
43
+ function buildErrorMessage(body, status, statusText) {
44
+ return body?.message || body?.title || (statusText ? `HTTP ${status}: ${statusText}` : `HTTP ${status}`);
45
+ }
26
46
  var LovableClient = class {
27
47
  authHeaders;
28
48
  baseUrl;
29
49
  extraHeaders;
50
+ clientSource;
51
+ typedClient;
30
52
  constructor(options) {
31
53
  const hasApiKey = !!options.apiKey;
32
54
  const hasBearerToken = !!options.bearerToken;
@@ -39,10 +61,33 @@ var LovableClient = class {
39
61
  this.authHeaders = hasApiKey ? { "Lovable-API-Key": options.apiKey } : { Authorization: `Bearer ${options.bearerToken}` };
40
62
  this.baseUrl = normalizeBaseUrl(options.baseUrl);
41
63
  this.extraHeaders = options.headers ?? {};
64
+ this.clientSource = options.clientSource ?? "sdk";
65
+ this.typedClient = createClient({
66
+ baseUrl: this.baseUrl,
67
+ headers: {
68
+ "X-Client-Source": this.clientSource,
69
+ ...this.authHeaders,
70
+ ...this.extraHeaders,
71
+ Accept: "application/json"
72
+ }
73
+ });
74
+ this.typedClient.use(errorMiddleware);
42
75
  }
43
- async rawRequest(method, path, body) {
76
+ /**
77
+ * Type-safe access to any documented API route, driven by the auto-generated
78
+ * OpenAPI schema. Path / query params and request bodies are checked at compile
79
+ * time; calling an unknown path is a type error.
80
+ *
81
+ * Non-2xx responses throw `ApiError`; on success, `data` is set on the result.
82
+ */
83
+ get typed() {
84
+ return this.typedClient;
85
+ }
86
+ async rawRequest(method, path, body, init) {
44
87
  const url = `${this.baseUrl}${path}`;
45
88
  const headers = {
89
+ "X-Client-Source": this.clientSource,
90
+ ...init?.headers,
46
91
  ...this.authHeaders,
47
92
  ...this.extraHeaders,
48
93
  Accept: "application/json"
@@ -53,7 +98,8 @@ var LovableClient = class {
53
98
  const response = await fetch(url, {
54
99
  method,
55
100
  headers,
56
- body: body !== void 0 ? JSON.stringify(body) : void 0
101
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
102
+ signal: init?.signal
57
103
  });
58
104
  if (!response.ok) {
59
105
  let errorBody;
@@ -61,7 +107,7 @@ var LovableClient = class {
61
107
  errorBody = await response.json();
62
108
  } catch {
63
109
  }
64
- const message = errorBody?.message ?? errorBody?.title ?? `HTTP ${response.status}: ${response.statusText}`;
110
+ const message = buildErrorMessage(errorBody, response.status, response.statusText);
65
111
  const type = errorBody?.type ?? errorBody?.title;
66
112
  const detail = errorBody?.detail ?? errorBody?.details;
67
113
  throw new ApiError(response.status, message, type, detail, errorBody?.props);
@@ -84,7 +130,8 @@ var LovableClient = class {
84
130
  * Useful for validating an API key and discovering workspace IDs.
85
131
  */
86
132
  async me() {
87
- return this.request("GET", "/v1/me");
133
+ const { data } = await this.typed.GET("/v1/me");
134
+ return { ...data, workspaces: data.workspaces ?? [] };
88
135
  }
89
136
  /**
90
137
  * List all workspaces the authenticated user has access to
@@ -134,9 +181,11 @@ var LovableClient = class {
134
181
  }
135
182
  const body = {
136
183
  description: options.description,
137
- visibility: options.visibility ?? "private",
138
184
  template_project_id: options.templateProjectId
139
185
  };
186
+ if (options.visibility) {
187
+ body.visibility = options.visibility;
188
+ }
140
189
  if (options.techStack) {
141
190
  body.tech_stack = options.techStack;
142
191
  }
@@ -275,19 +324,6 @@ var LovableClient = class {
275
324
  async queryDatabase(projectId, sql) {
276
325
  return this.request("POST", `/v1/projects/${projectId}/database/query`, { sql });
277
326
  }
278
- /**
279
- * Get database connection info for a project.
280
- *
281
- * Returns host, port, user, password, database name, and full connection string
282
- * that can be used with any PostgreSQL client (psql, pgAdmin, etc.).
283
- * The database must be enabled first (see enableDatabase).
284
- *
285
- * @param projectId - The project ID
286
- * @returns Database connection details
287
- */
288
- async getDatabaseConnectionInfo(projectId) {
289
- return this.request("GET", `/v1/projects/${projectId}/database/connection-info`);
290
- }
291
327
  // ---------------------------------------------------------------------------
292
328
  // Messages
293
329
  // ---------------------------------------------------------------------------
@@ -298,6 +334,17 @@ var LovableClient = class {
298
334
  async getMessage(projectId, messageId) {
299
335
  return this.request("GET", `/v1/projects/${projectId}/messages/${messageId}`);
300
336
  }
337
+ /**
338
+ * List recent messages in a project, newest first. Use `before` (a message ID
339
+ * from a prior page) to paginate backwards through history.
340
+ */
341
+ async listMessages(projectId, params) {
342
+ const qs = new URLSearchParams();
343
+ if (params?.limit !== void 0) qs.set("limit", String(params.limit));
344
+ if (params?.before) qs.set("before", params.before);
345
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
346
+ return this.request("GET", `/v1/projects/${projectId}/messages${suffix}`);
347
+ }
301
348
  /**
302
349
  * Poll for message completion. Waits until the AI response reaches a terminal
303
350
  * status (completed, stopped) or the timeout expires.
@@ -386,7 +433,10 @@ var LovableClient = class {
386
433
  // ---------------------------------------------------------------------------
387
434
  /** Get workspace knowledge (custom instructions for the AI agent). */
388
435
  async getWorkspaceKnowledge(workspaceId) {
389
- return this.request("GET", `/v1/workspaces/${workspaceId}/knowledge`);
436
+ const { data } = await this.typed.GET("/v1/workspaces/{workspace_id}/knowledge", {
437
+ params: { path: { workspace_id: workspaceId } }
438
+ });
439
+ return data;
390
440
  }
391
441
  /** Set workspace knowledge. Max 10,000 characters. */
392
442
  async setWorkspaceKnowledge(workspaceId, content) {
@@ -394,7 +444,10 @@ var LovableClient = class {
394
444
  }
395
445
  /** Get project knowledge (custom instructions for the AI agent). */
396
446
  async getProjectKnowledge(projectId) {
397
- return this.request("GET", `/v1/projects/${projectId}/knowledge`);
447
+ const { data } = await this.typed.GET("/v1/projects/{project_id}/knowledge", {
448
+ params: { path: { project_id: projectId } }
449
+ });
450
+ return data;
398
451
  }
399
452
  /** Set project knowledge. Max 10,000 characters. */
400
453
  async setProjectKnowledge(projectId, content) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/client.ts"],"sourcesContent":["// Type definitions for the Lovable SDK\n// These are self-contained and don't require external dependencies\n\nexport type ProjectVisibility = \"draft\" | \"private\" | \"public\";\nexport type MemberRole = \"admin\" | \"collaborator\" | \"invited\" | \"member\" | \"none\" | \"owner\" | \"viewer\";\nexport type ProjectStatus = \"completed\" | \"in_progress\" | \"failed\";\n\nexport interface MeWorkspace {\n id: string;\n name: string;\n role: string;\n}\n\nexport interface MeResponse {\n id: string;\n email: string;\n name: string;\n workspaces: MeWorkspace[];\n}\n\nexport interface WorkspaceMembership {\n email: string;\n invited_at?: string;\n joined_at?: string;\n monthly_credit_limit: number | null;\n project_access?: Record<string, { access_level?: string }>;\n role: MemberRole;\n user_id: string;\n workspace_id: string;\n}\n\nexport interface WorkspaceWithMembership {\n id: string;\n name: string;\n description?: string;\n image_url?: string;\n owner_id?: string;\n is_personal?: boolean;\n plan?: string;\n plan_type?: string;\n num_projects: number;\n num_seats?: number;\n membership: WorkspaceMembership;\n created_at: string;\n updated_at: string;\n deleted_at?: string;\n credits_granted: number;\n credits_used: number;\n daily_credits_limit: number;\n daily_credits_used: number;\n billing_period_credits_limit: number;\n billing_period_credits_used: number;\n billing_period_start_date?: string;\n billing_period_end_date?: string;\n rollover_credits_limit: number;\n rollover_credits_used: number;\n topup_credits_limit: number;\n topup_credits_used: number;\n total_credits_used: number;\n subscription_status?:\n | \"active\"\n | \"canceled\"\n | \"incomplete_expired\"\n | \"incomplete\"\n | \"past_due\"\n | \"paused\"\n | \"trialing\"\n | \"unpaid\";\n referral_code?: string;\n short_referral_code?: string;\n referral_count: number;\n followers_count: number;\n default_project_visibility?: ProjectVisibility;\n default_project_publish_visibility?: \"private\" | \"public\";\n mcp_enabled?: boolean;\n}\n\nexport interface WorkspaceMembershipResponse {\n user_id: string;\n username: string;\n display_name?: string;\n email?: string;\n role: MemberRole;\n invited_at?: string;\n joined_at?: string;\n monthly_credit_limit?: number;\n total_credits_used?: number;\n total_credits_used_in_billing_period?: number;\n project_access?: Record<string, { access_level?: string }>;\n}\n\n/**\n * Project response from the API.\n *\n * The v1 endpoints (getProject, createProject) return a lean subset:\n * id, workspace_id, name, display_name, description, status, visibility,\n * is_published, url. Other fields are available from list endpoints.\n */\nexport interface ProjectResponse {\n id: string;\n name?: string;\n display_name?: string;\n description?: string;\n tech_stack?: string;\n status?: string;\n visibility?: ProjectVisibility;\n publish_visibility?: \"private\" | \"public\";\n is_published?: boolean;\n /** Present in create project response when initial_message was provided */\n message_id?: string;\n is_starred?: boolean;\n is_template?: boolean;\n is_github?: boolean;\n is_supabase_enabled?: boolean;\n url?: string;\n og_image_url?: string;\n latest_screenshot_url?: string;\n user_id?: string;\n user_display_name?: string;\n user_photo_url?: string;\n created_at?: string;\n created_by?: string;\n updated_at?: string;\n deleted_at?: string;\n last_edited_at?: string;\n last_viewed_at?: string;\n published_at?: string;\n workspace_id?: string;\n folder_id?: string;\n category?: string;\n edit_count?: number;\n gen_count?: number;\n user_message_count?: number;\n remix_count?: number;\n remixed_from_project_id?: string;\n template_project_id?: string;\n credit_total?: number;\n custom_instructions?: string;\n main_branch?: string;\n latest_commit_sha?: string;\n github_repo_name?: string;\n github_repo_id?: number;\n deployment_target?: string;\n active_deployment_job_id?: string;\n environments_enabled?: boolean;\n hide_badge?: boolean | null;\n featured?: boolean;\n featured_at?: string;\n feature_rank?: number;\n feature_source?: string;\n}\n\nexport interface UnscopedFile {\n file_id: string;\n type: \"user_upload\";\n file_name?: string;\n mime_type?: string;\n}\n\nexport interface FileInput {\n name: string;\n data: Blob | ArrayBuffer | Uint8Array;\n type: string;\n}\n\n/**\n * Configuration to use a custom OpenAI-compatible model as the main agent.\n */\nexport interface CustomModelConfig {\n /** Base URL of the OpenAI-compatible API (e.g. \"https://my-vllm.example.com/v1\") */\n endpoint: string;\n /** API key for the custom endpoint */\n apiKey: string;\n /** Model identifier sent in the request (e.g. \"my-org/my-model-id\") */\n modelName: string;\n}\n\nexport interface ChatRequest {\n id: string;\n message: string;\n chat_only: boolean;\n headless: boolean;\n ai_message_id?: string;\n intent?: string;\n model?: string;\n temperature?: number;\n current_page?: string;\n view?: string;\n view_description?: string;\n prev_session_id?: string;\n is_creation?: boolean;\n files?: UnscopedFile[];\n custom_model_endpoint?: string;\n custom_model_api_key?: string;\n custom_model_name?: string;\n custom_model_disable_race?: boolean;\n}\n\nexport interface CreateProjectBody {\n description: string;\n tech_stack?: string;\n visibility?: ProjectVisibility;\n template_project_id?: string;\n initial_message?: string;\n files?: UnscopedFile[];\n selected_libraries?: { project_id: string }[];\n category?: string;\n project_type?: string;\n prompt_name?: string;\n selected_theme?: string;\n env_vars?: Record<string, string>;\n metadata?: Record<string, unknown>;\n}\n\nexport interface AddUserToWorkspaceInputBody {\n email: string;\n role?: MemberRole;\n}\n\nexport interface GetWorkspacesResponse {\n workspaces: WorkspaceWithMembership[] | null;\n}\n\nexport interface GetWorkspaceProjectsResponse {\n projects: ProjectResponse[] | null;\n}\n\nexport interface CreateProjectOptions {\n description: string;\n techStack?: string;\n visibility?: ProjectVisibility;\n templateProjectId?: string;\n initialMessage?: string;\n /** Files to upload and attach to the initial message. The SDK handles uploading. */\n files?: (File | FileInput)[];\n /** Pre-uploaded file references to attach to the initial message. Skips upload. */\n uploadedFiles?: UnscopedFile[];\n /** Design system library projects to connect to the new project. */\n selectedLibraries?: { project_id: string }[];\n}\n\nexport interface InviteCollaboratorOptions {\n email: string;\n role?: MemberRole;\n}\n\n/**\n * Controls prompt cache continuation behavior for a message.\n *\n * - `\"force\"` — skip cache TTL and token/criteria checks (force continuation)\n * - `\"fresh_build\"` — force a full prompt rebuild from scratch\n * - `\"allow_expired_cache\"` — skip cache TTL check but respect token/criteria limits\n *\n * Omit for default behavior (5-min TTL, token and criteria checks apply).\n */\nexport type ContinuationOverride = \"force\" | \"fresh_build\" | \"allow_expired_cache\";\n\nexport interface ChatMessageOptions {\n message: string;\n /** Files to upload and attach. The SDK handles uploading them first. */\n files?: (File | FileInput)[];\n /** Pre-uploaded file references (already uploaded via getFileUploadUrl). Skips upload. */\n uploadedFiles?: UnscopedFile[];\n /** Enable plan mode: the agent discusses and plans without editing code. */\n planMode?: boolean;\n customModel?: CustomModelConfig;\n /**\n * When true, the server may skip staggered inference racing for that request if your\n * custom model endpoint supports it; otherwise the request fails with a validation error.\n * API key authentication only. Sent as `custom_model_disable_race` in the JSON body.\n */\n customModelDisableRace?: boolean;\n /**\n * Override continuation behavior for this message.\n * Controls whether the agent reuses the prompt cache or rebuilds from scratch.\n */\n continuation?: ContinuationOverride;\n}\n\nexport interface LovableClientOptions {\n /** API key for authentication (mutually exclusive with bearerToken) */\n apiKey?: string;\n /** Bearer token for OAuth authentication (mutually exclusive with apiKey) */\n bearerToken?: string;\n baseUrl?: string;\n /** Additional headers to include on every request */\n headers?: Record<string, string>;\n}\n\nexport interface LovableError extends Error {\n status: number;\n type?: string;\n detail?: string;\n}\n\nexport interface WaitOptions {\n pollInterval?: number;\n timeout?: number;\n onProgress?: (project: ProjectResponse) => void;\n}\n\nexport interface DeploymentResponse {\n status: string;\n deployment_id?: string;\n url?: string;\n}\n\nexport interface ChatResponse {\n content: string;\n previewUrl: string;\n messageId: string;\n}\n\nexport interface ChatResponseOptions {\n timeout?: number;\n}\n\n// --- Trace types ---\n\nexport type TracePurpose = \"main_agent\" | \"codebase_rag\" | \"knowledge_rag\" | \"review\";\n\nexport interface TraceSpan {\n span_id: string;\n root_span_id?: string;\n span_parents?: unknown;\n span_name?: string;\n span_type?: string;\n purpose?: string;\n subpurpose?: string;\n created?: string;\n input?: unknown;\n output?: unknown;\n error?: unknown;\n metadata?: unknown;\n metrics?: unknown;\n scores?: unknown;\n tags?: unknown;\n}\n\nexport interface MessageTracesResponse {\n message_id: string;\n braintrust_span_id: string;\n root_span_id: string;\n response_message_id?: string;\n spans: TraceSpan[];\n}\n\nexport interface GetMessageTracesOptions {\n purposes?: TracePurpose[];\n}\n\nexport interface TraceQuery {\n projectId: string;\n messageId: string;\n}\n\nexport interface BatchTracesResult {\n traces: Map<string, MessageTracesResponse>;\n errors: Map<string, Error>;\n}\n\n// --- On-demand review replay (`/v1/_dev/.../reviews/replay`) — typed in SDK only ---\n\nexport interface ReplayReviewsItemInput {\n /** Assistant (AI) message id for the turn to score */\n response_message_id: string;\n /** Optional per-item reviewer types */\n reviewer_types?: string[] | null;\n}\n\nexport interface ReplayReviewsRequestBody {\n items: ReplayReviewsItemInput[];\n /** Defaults to project_success_v3 and user_sentiment when omitted */\n reviewer_types?: string[] | null;\n /** Parallel items (default 4, max 16) */\n concurrency?: number;\n}\n\nexport interface ReviewerRunResult {\n reviewer_type: string;\n score?: number;\n message?: string;\n model?: string;\n published_rubric_rows: number;\n error?: string;\n}\n\nexport interface ReplayOutcome {\n response_message_id: string;\n user_message_id: string;\n results: ReviewerRunResult[] | null;\n}\n\nexport interface BatchItemOutcome {\n response_message_id: string;\n ok: boolean;\n error?: string;\n replay?: ReplayOutcome;\n}\n\nexport interface BatchReplayReviewsOutcome {\n items: BatchItemOutcome[];\n}\n\nexport interface ReplayReviewsResponse {\n results: BatchReplayReviewsOutcome;\n}\n\n// --- Remix types ---\n\nexport type RemixJobStatus = \"unknown\" | \"preparing\" | \"running\" | \"completed\" | \"error\";\n\nexport type RemixJobStep =\n | \"starting\"\n | \"creating_new_project\"\n | \"restoring_supabase\"\n | \"copying_history\"\n | \"preparing_repository\"\n | \"remixing_integration\"\n | \"pushing_repository\"\n | \"finalizing\"\n | \"completed\";\n\nexport interface RemixJobStepInfo {\n step: RemixJobStep;\n integration_name?: string;\n status?: string;\n}\n\nexport type RemixMode = \"before\" | \"including\";\n\nexport interface RemixProjectOptions {\n workspaceId: string;\n messageId?: string;\n remixMode?: RemixMode;\n includeHistory?: boolean;\n includeCustomKnowledge?: boolean;\n initialMessage?: string;\n projectName?: string;\n skipInitialRemixMessage?: boolean;\n skipIntegrations?: boolean;\n}\n\nexport interface RemixInitBody {\n workspace_id: string;\n message_id?: string;\n remix_mode?: RemixMode;\n include_history?: boolean;\n include_custom_knowledge?: boolean;\n initial_message?: ChatRequest;\n project_name?: string;\n skip_initial_remix_message?: boolean;\n skip_integrations?: boolean;\n}\n\nexport interface RemixInitResponse {\n job_id: string;\n}\n\nexport interface RemixProgressResult {\n project_id: string;\n}\n\nexport interface RemixProgressResponse {\n status: RemixJobStatus;\n step?: RemixJobStepInfo;\n error_message?: string;\n result?: RemixProgressResult;\n}\n\nexport interface RemixResult {\n projectId: string;\n}\n\nexport interface RemixWaitOptions {\n pollInterval?: number;\n timeout?: number;\n onProgress?: (status: RemixJobStatus, step?: RemixJobStepInfo) => void;\n}\n\n// --- Database types ---\n\nexport interface DatabaseStatus {\n enabled: boolean;\n stack?: string;\n}\n\nexport interface EnableDatabaseResult {\n enabled: boolean;\n stack: string;\n}\n\nexport interface DatabaseQueryResult {\n rows: Record<string, unknown>[] | null;\n}\n\nexport interface DatabaseConnectionInfo {\n host: string;\n port: string;\n user: string;\n password: string;\n database: string;\n connection_string: string;\n api_url: string;\n}\n\n// --- API Error ---\n\nexport class ApiError extends Error {\n readonly status: number;\n readonly type?: string;\n readonly detail?: string;\n readonly props?: Record<string, unknown>;\n\n constructor(status: number, message: string, type?: string, detail?: string, props?: Record<string, unknown>) {\n super(message);\n this.status = status;\n this.type = type;\n this.detail = detail;\n this.props = props;\n }\n}\n\n// --- Send message response ---\n\nexport interface SendMessageResponse {\n message_id: string;\n status: string;\n}\n\n// --- Get message response ---\n\nexport interface GetMessageResponse {\n message_id: string;\n role: string;\n content: string;\n status: string;\n created_at: string;\n edit_id?: string;\n commit_sha?: string;\n summary?: string;\n cost_credits?: number;\n response?: {\n message_id: string;\n status: string;\n content: string;\n edit_id?: string;\n commit_sha?: string;\n summary?: string;\n cost_credits?: number;\n };\n queue_position?: number;\n queue_paused?: boolean;\n queue_pause_reason?: string;\n}\n\nexport interface MessageCompletionResult {\n status: \"completed\" | \"timeout\" | \"error\";\n message_id: string;\n content: string;\n edit_id?: string;\n commit_sha?: string;\n summary?: string;\n cost_credits?: number;\n error?: string;\n}\n\nexport interface MessageCompletionOptions {\n /** Time between polls in ms (default: 3000) */\n pollInterval?: number;\n /** Maximum time to wait in ms (default: 600000 = 10 minutes) */\n timeout?: number;\n}\n\n// --- Knowledge ---\n\nexport interface KnowledgeResponse {\n content: string;\n}\n\n// --- File upload ---\n\nexport interface FileUploadUrlResponse {\n url: string;\n file_id: string;\n}\n\n// --- Git types ---\n\nexport interface DiffLine {\n type: string;\n content: string;\n}\n\nexport interface DiffHunk {\n oldStart: number;\n oldCount: number;\n newStart: number;\n newCount: number;\n lines: DiffLine[];\n}\n\nexport interface DiffEntry {\n action: string;\n file_path: string;\n original_file_path?: string;\n file_type?: string;\n is_image: boolean;\n is_incomplete?: boolean;\n hunks?: DiffHunk[];\n}\n\nexport interface GitDiffResponse {\n diffs: DiffEntry[];\n error?: string;\n}\n\nexport interface GitFileEntry {\n path: string;\n size: number;\n binary: boolean;\n}\n\nexport interface GitFilesResponse {\n files: GitFileEntry[];\n ref: string;\n}\n\n// --- Edits ---\n\nexport interface EditSummary {\n id: string;\n type: string;\n commit_sha?: string;\n commit_message?: string;\n status: string;\n created_at: string;\n}\n\nexport interface EditsResponse {\n edits: EditSummary[];\n has_more: boolean;\n}\n\n// --- List projects (expanded) ---\n\nexport interface ListProjectItem extends ProjectResponse {\n user_id?: string;\n is_template?: boolean;\n is_starred?: boolean;\n created_at?: string;\n updated_at?: string;\n last_edited_at?: string;\n last_viewed_at?: string;\n edit_count?: number;\n remix_count?: number;\n}\n\nexport interface ListProjectsResponse {\n projects: ListProjectItem[];\n total: number;\n has_more: boolean;\n}\n\nexport interface ListProjectsOptions {\n query?: string;\n visibility?: string;\n publish_status?: string;\n folder_id?: string;\n user_id?: string;\n sort_by?: string;\n sort_order?: string;\n viewed_by_me?: boolean;\n limit?: number;\n offset?: number;\n cursor?: string;\n}\n\n// --- Library & template projects ---\n\nexport interface LibraryProjectResponse {\n id: string;\n name: string | null;\n description: string;\n updated_at: string;\n}\n\nexport interface TemplateProjectResponse {\n id: string;\n name: string | null;\n description: string;\n updated_at: string;\n}\n\n// --- MCP servers ---\n\nexport interface ConnectorResponse {\n id: string;\n name: string;\n url?: string;\n auth_type: string;\n connector_id?: string;\n is_connected: boolean;\n}\n\nexport interface AvailableConnectorEntry {\n id: string;\n display_name: string;\n summary: string;\n category: string;\n requires_custom_url?: boolean;\n documentation_url?: string;\n}\n\nexport interface AddConnectorBody {\n name: string;\n url: string;\n auth_type: \"none\" | \"bearer_token\";\n connector_id?: string;\n token?: string;\n}\n\n// --- Connectors ---\n\nexport interface StandardConnectorItem {\n id: string;\n display_name: string;\n short_description: string;\n categories: string[];\n is_enabled_for_workspace: boolean;\n auth_type: string;\n logo_id?: string;\n documentation_url?: string;\n connection_scope?: string;\n}\n\nexport interface SeamlessConnectorItem {\n id: string;\n display_name: string;\n short_description: string;\n categories: string[];\n is_enabled_for_workspace: boolean;\n logo_id?: string;\n documentation_url?: string;\n}\n\nexport interface MCPConnectorItem {\n id: string;\n display_name: string;\n summary: string;\n status: string;\n is_enabled_for_workspace: boolean;\n is_connected: boolean;\n connector_id?: string;\n documentation_url?: string;\n}\n\nexport interface ConnectionItem {\n id: string;\n connector_id: string;\n display_name: string;\n is_managed: boolean;\n status: string;\n created_at: string;\n updated_at: string;\n}\n\n// --- Analytics ---\n\nexport interface TimeSeriesDataPoint {\n date: string;\n value: number;\n}\n\nexport interface TimeSeriesData {\n total: number;\n label: string;\n data: TimeSeriesDataPoint[];\n}\n\nexport interface ListDataPoint {\n label: string;\n value: number;\n}\n\nexport interface ListData {\n label: string;\n data: ListDataPoint[];\n}\n\nexport interface ProjectAnalyticsResponse {\n timeSeries: {\n visitors: TimeSeriesData;\n pageviews: TimeSeriesData;\n pageviewsPerVisit: TimeSeriesData;\n sessionDuration: TimeSeriesData;\n bounceRate: TimeSeriesData;\n };\n lists: {\n page: ListData;\n source: ListData;\n device: ListData;\n country: ListData;\n };\n}\n\nexport interface TrendDataPoint {\n time: string;\n visits: number;\n}\n\nexport interface ProjectAnalyticsTrendResponse {\n data: TrendDataPoint[];\n currentVisitors: number;\n}\n","import type {\n LovableClientOptions,\n CreateProjectOptions,\n InviteCollaboratorOptions,\n ChatMessageOptions,\n WaitOptions,\n WorkspaceWithMembership,\n ProjectResponse,\n WorkspaceMembershipResponse,\n CreateProjectBody,\n AddUserToWorkspaceInputBody,\n GetWorkspacesResponse,\n DeploymentResponse,\n ChatResponse,\n ChatResponseOptions,\n UnscopedFile,\n FileInput,\n RemixProjectOptions,\n RemixInitBody,\n RemixInitResponse,\n RemixProgressResponse,\n RemixResult,\n RemixWaitOptions,\n MessageTracesResponse,\n GetMessageTracesOptions,\n TraceQuery,\n BatchTracesResult,\n ReplayReviewsRequestBody,\n ReplayReviewsResponse,\n MeResponse,\n DatabaseStatus,\n EnableDatabaseResult,\n DatabaseQueryResult,\n DatabaseConnectionInfo,\n SendMessageResponse,\n GetMessageResponse,\n MessageCompletionResult,\n MessageCompletionOptions,\n KnowledgeResponse,\n FileUploadUrlResponse,\n GitDiffResponse,\n GitFilesResponse,\n EditsResponse,\n ListProjectsResponse,\n ListProjectsOptions,\n ConnectorResponse,\n AvailableConnectorEntry,\n AddConnectorBody,\n StandardConnectorItem,\n SeamlessConnectorItem,\n MCPConnectorItem,\n ConnectionItem,\n ProjectAnalyticsResponse,\n ProjectAnalyticsTrendResponse,\n LibraryProjectResponse,\n TemplateProjectResponse,\n} from \"./types.js\";\n\nimport { ApiError } from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.lovable.dev\";\n\nfunction normalizeBaseUrl(url: string | undefined): string {\n if (!url) return DEFAULT_BASE_URL;\n\n const normalized = url.replace(/\\/$/, \"\");\n\n if (!normalized.startsWith(\"http://\") && !normalized.startsWith(\"https://\")) {\n throw new Error(`baseUrl must include a protocol (http:// or https://). Got: \"${url}\"`);\n }\n\n return normalized;\n}\n\nexport class LovableClient {\n private readonly authHeaders: Record<string, string>;\n private readonly baseUrl: string;\n private readonly extraHeaders: Record<string, string>;\n\n constructor(options: LovableClientOptions) {\n const hasApiKey = !!options.apiKey;\n const hasBearerToken = !!options.bearerToken;\n\n if (!hasApiKey && !hasBearerToken) {\n throw new Error(\"Either apiKey or bearerToken is required\");\n }\n if (hasApiKey && hasBearerToken) {\n throw new Error(\"Provide either apiKey or bearerToken, not both\");\n }\n\n this.authHeaders = hasApiKey\n ? { \"Lovable-API-Key\": options.apiKey! }\n : { Authorization: `Bearer ${options.bearerToken}` };\n\n this.baseUrl = normalizeBaseUrl(options.baseUrl);\n this.extraHeaders = options.headers ?? {};\n }\n\n private async rawRequest(method: string, path: string, body?: unknown): Promise<Response> {\n const url = `${this.baseUrl}${path}`;\n\n const headers: Record<string, string> = {\n ...this.authHeaders,\n ...this.extraHeaders,\n Accept: \"application/json\",\n };\n if (body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n let errorBody:\n | {\n type?: string;\n title?: string;\n message?: string;\n detail?: string;\n details?: string;\n props?: Record<string, unknown>;\n }\n | undefined;\n try {\n errorBody = (await response.json()) as typeof errorBody;\n } catch {\n // Ignore JSON parse errors\n }\n\n const message = errorBody?.message ?? errorBody?.title ?? `HTTP ${response.status}: ${response.statusText}`;\n const type = errorBody?.type ?? errorBody?.title;\n const detail = errorBody?.detail ?? errorBody?.details;\n throw new ApiError(response.status, message, type, detail, errorBody?.props);\n }\n\n return response;\n }\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const response = await this.rawRequest(method, path, body);\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return response.json() as Promise<T>;\n }\n\n private async requestText(method: string, path: string): Promise<string> {\n const response = await this.rawRequest(method, path);\n return response.text();\n }\n\n /**\n * Get the current authenticated user and their workspaces.\n * Useful for validating an API key and discovering workspace IDs.\n */\n async me(): Promise<MeResponse> {\n return this.request<MeResponse>(\"GET\", \"/v1/me\");\n }\n\n /**\n * List all workspaces the authenticated user has access to\n */\n async listWorkspaces(): Promise<WorkspaceWithMembership[]> {\n const response = await this.request<GetWorkspacesResponse>(\"GET\", \"/v1/workspaces\");\n return response.workspaces ?? [];\n }\n\n /**\n * Get a specific workspace by ID\n */\n async getWorkspace(workspaceId: string): Promise<WorkspaceWithMembership> {\n const response = await this.request<{ workspace: WorkspaceWithMembership }>(\"GET\", `/v1/workspaces/${workspaceId}`);\n return response.workspace;\n }\n\n /**\n * List projects in a workspace.\n * Supports full-text search, filtering by visibility/publish status/folder/creator,\n * and pagination via offset or cursor.\n */\n async listProjects(workspaceId: string, options?: ListProjectsOptions): Promise<ListProjectsResponse> {\n const params = new URLSearchParams();\n if (options?.query) params.set(\"q\", options.query);\n if (options?.visibility) params.set(\"visibility\", options.visibility);\n if (options?.publish_status) params.set(\"publish_status\", options.publish_status);\n if (options?.folder_id) params.set(\"folder_id\", options.folder_id);\n if (options?.user_id) params.set(\"user_id\", options.user_id);\n if (options?.sort_by) params.set(\"sort_by\", options.sort_by);\n if (options?.sort_order) params.set(\"sort_order\", options.sort_order);\n if (options?.viewed_by_me) params.set(\"viewed_by_me\", \"true\");\n if (options?.limit !== undefined) params.set(\"limit\", String(options.limit));\n if (options?.offset !== undefined) params.set(\"offset\", String(options.offset));\n if (options?.cursor) params.set(\"cursor\", options.cursor);\n\n const query = params.toString();\n const path = `/v1/workspaces/${workspaceId}/projects${query ? `?${query}` : \"\"}`;\n\n return this.request<ListProjectsResponse>(\"GET\", path);\n }\n\n /**\n * Create a new project in a workspace\n */\n async createProject(workspaceId: string, options: CreateProjectOptions): Promise<ProjectResponse> {\n let fileRefs: UnscopedFile[] | undefined;\n if (options.uploadedFiles?.length) {\n fileRefs = options.uploadedFiles;\n } else if (options.files?.length) {\n fileRefs = await this.uploadFiles(options.files);\n }\n\n const body: CreateProjectBody = {\n description: options.description,\n visibility: options.visibility ?? \"private\",\n template_project_id: options.templateProjectId,\n };\n if (options.techStack) {\n body.tech_stack = options.techStack;\n }\n if (options.selectedLibraries?.length) {\n body.selected_libraries = options.selectedLibraries;\n }\n\n if (options.initialMessage) {\n body.initial_message = options.initialMessage;\n }\n if (fileRefs?.length) {\n body.files = fileRefs;\n }\n\n const project = await this.request<ProjectResponse>(\"POST\", `/v1/workspaces/${workspaceId}/projects`, body);\n\n return project;\n }\n\n /**\n * Send a chat message to a project.\n *\n * The API accepts the message and processes it in the background.\n * Returns the message ID and status. Use `waitForMessageCompletion()`\n * to poll for the AI response, or `waitForResponse()` for SSE streaming.\n */\n async chat(projectId: string, options: ChatMessageOptions): Promise<SendMessageResponse> {\n let fileRefs: UnscopedFile[] | undefined;\n if (options.uploadedFiles?.length) {\n fileRefs = options.uploadedFiles;\n } else if (options.files?.length) {\n fileRefs = await this.uploadFiles(options.files);\n }\n\n const body: {\n message: string;\n files?: UnscopedFile[];\n plan_mode?: boolean;\n custom_model_endpoint?: string;\n custom_model_api_key?: string;\n custom_model_name?: string;\n custom_model_disable_race?: boolean;\n continuation?: string;\n } = {\n message: options.message,\n };\n if (fileRefs) {\n body.files = fileRefs;\n }\n if (options.planMode) {\n body.plan_mode = true;\n }\n if (options.customModel) {\n body.custom_model_endpoint = options.customModel.endpoint;\n body.custom_model_api_key = options.customModel.apiKey;\n body.custom_model_name = options.customModel.modelName;\n }\n if (options.customModelDisableRace) {\n body.custom_model_disable_race = true;\n }\n if (options.continuation) {\n body.continuation = options.continuation;\n }\n\n return this.request<SendMessageResponse>(\"POST\", `/v1/projects/${projectId}/messages`, body);\n }\n\n /**\n * Invite a user to a workspace as a collaborator\n */\n async inviteCollaborator(\n workspaceId: string,\n options: InviteCollaboratorOptions,\n ): Promise<WorkspaceMembershipResponse> {\n const body: AddUserToWorkspaceInputBody = {\n email: options.email,\n role: options.role ?? \"member\",\n };\n\n return this.request<WorkspaceMembershipResponse>(\"POST\", `/workspaces/${workspaceId}/memberships`, body);\n }\n\n /**\n * List members of a workspace\n */\n async listWorkspaceMembers(workspaceId: string): Promise<WorkspaceMembershipResponse[]> {\n const response = await this.request<{\n memberships: WorkspaceMembershipResponse[] | null;\n }>(\"GET\", `/workspaces/${workspaceId}/memberships`);\n return response.memberships ?? [];\n }\n\n /**\n * Remove a member from a workspace\n */\n async removeWorkspaceMember(workspaceId: string, userId: string): Promise<void> {\n await this.request<void>(\"DELETE\", `/workspaces/${workspaceId}/memberships/${userId}`);\n }\n\n /**\n * Get project details by ID\n */\n async getProject(projectId: string): Promise<ProjectResponse> {\n return this.request<ProjectResponse>(\"GET\", `/v1/projects/${projectId}`);\n }\n\n /**\n * Get the preview URL for a project.\n *\n * The preview URL is available once the project reaches \"completed\" status.\n * This URL allows viewing the project in development mode.\n *\n * @param projectId - The project ID\n * @returns The preview URL\n */\n getPreviewUrl(projectId: string): string {\n return `https://id-preview--${projectId}.lovable.app`;\n }\n\n /**\n * Get the published URL for a project (if published).\n *\n * Returns the public URL if the project has been published, or null if not.\n *\n * @param projectId - The project ID\n * @returns The published URL or null if not published\n */\n async getPublishedUrl(projectId: string): Promise<string | null> {\n const project = await this.getProject(projectId);\n return project.is_published && project.url ? project.url : null;\n }\n\n /**\n * Get the cloud database status for a project.\n *\n * @param projectId - The project ID\n * @returns Whether the database is enabled and which stack is used\n */\n async getDatabaseStatus(projectId: string): Promise<DatabaseStatus> {\n return this.request<DatabaseStatus>(\"GET\", `/v1/projects/${projectId}/database`);\n }\n\n /**\n * Enable (provision) a cloud database for a project.\n *\n * This triggers database provisioning which takes 30-60 seconds.\n * The call blocks until provisioning completes.\n *\n * @param projectId - The project ID\n * @returns The database status after enablement\n */\n async enableDatabase(projectId: string): Promise<EnableDatabaseResult> {\n return this.request<EnableDatabaseResult>(\"POST\", `/v1/projects/${projectId}/database/enable`);\n }\n\n /**\n * Execute a SQL query against the project's cloud database.\n *\n * Supports SELECT, INSERT, UPDATE, DELETE, and DDL statements.\n * The database must be enabled first (see enableDatabase).\n *\n * @param projectId - The project ID\n * @param sql - SQL query to execute\n * @returns Query result rows as JSON objects\n */\n async queryDatabase(projectId: string, sql: string): Promise<DatabaseQueryResult> {\n return this.request<DatabaseQueryResult>(\"POST\", `/v1/projects/${projectId}/database/query`, { sql });\n }\n\n /**\n * Get database connection info for a project.\n *\n * Returns host, port, user, password, database name, and full connection string\n * that can be used with any PostgreSQL client (psql, pgAdmin, etc.).\n * The database must be enabled first (see enableDatabase).\n *\n * @param projectId - The project ID\n * @returns Database connection details\n */\n async getDatabaseConnectionInfo(projectId: string): Promise<DatabaseConnectionInfo> {\n return this.request<DatabaseConnectionInfo>(\"GET\", `/v1/projects/${projectId}/database/connection-info`);\n }\n\n // ---------------------------------------------------------------------------\n // Messages\n // ---------------------------------------------------------------------------\n\n /**\n * Get a message by ID. Returns the message content, status, and (for user messages)\n * the AI response if available. Use to poll for completion after `chat()`.\n */\n async getMessage(projectId: string, messageId: string): Promise<GetMessageResponse> {\n return this.request<GetMessageResponse>(\"GET\", `/v1/projects/${projectId}/messages/${messageId}`);\n }\n\n /**\n * Poll for message completion. Waits until the AI response reaches a terminal\n * status (completed, stopped) or the timeout expires.\n *\n * Handles edge cases: 404 grace period for race conditions between queue\n * dequeue and event creation, and queue pause detection.\n */\n async waitForMessageCompletion(\n projectId: string,\n messageId: string,\n options?: MessageCompletionOptions,\n ): Promise<MessageCompletionResult> {\n const pollInterval = options?.pollInterval ?? 3000;\n const timeout = options?.timeout ?? 600_000;\n const deadline = Date.now() + timeout;\n let notFoundSince: number | null = null;\n const notFoundGraceMs = 15_000;\n\n while (Date.now() < deadline) {\n try {\n const msg = await this.getMessage(projectId, messageId);\n notFoundSince = null;\n\n if (msg.status === \"queued\") {\n if (msg.queue_paused && msg.queue_pause_reason !== \"hitl_tool\") {\n return {\n status: \"error\",\n message_id: messageId,\n content: \"\",\n error:\n `Message is queued (position ${msg.queue_position ?? \"unknown\"}) but the queue is paused` +\n (msg.queue_pause_reason ? ` (reason: ${msg.queue_pause_reason})` : \"\") +\n `. Unpause the queue in the Lovable editor, or use wait=false to return immediately.`,\n };\n }\n await sleep(pollInterval);\n continue;\n }\n\n const ai = msg.response;\n if (ai) {\n if (ai.status === \"completed\" || ai.status === \"stopped\") {\n return {\n status: ai.status === \"completed\" ? \"completed\" : \"error\",\n message_id: ai.message_id,\n content: ai.content,\n edit_id: ai.edit_id,\n commit_sha: ai.commit_sha,\n summary: ai.summary,\n cost_credits: ai.cost_credits,\n };\n }\n }\n\n if (msg.role === \"assistant\") {\n if (msg.status === \"completed\" || msg.status === \"stopped\") {\n return {\n status: msg.status === \"completed\" ? \"completed\" : \"error\",\n message_id: msg.message_id,\n content: msg.content,\n edit_id: msg.edit_id,\n commit_sha: msg.commit_sha,\n summary: msg.summary,\n cost_credits: msg.cost_credits,\n };\n }\n }\n\n await sleep(pollInterval);\n } catch (err) {\n if (err instanceof ApiError && err.status === 404) {\n if (notFoundSince === null) {\n notFoundSince = Date.now();\n }\n if (Date.now() - notFoundSince < notFoundGraceMs) {\n await sleep(pollInterval);\n continue;\n }\n return {\n status: \"error\",\n message_id: messageId,\n content: \"\",\n error: \"Message not found. It may have been deleted from the queue.\",\n };\n }\n await sleep(pollInterval);\n }\n }\n\n return {\n status: \"timeout\",\n message_id: messageId,\n content: \"\",\n error: `Agent did not finish within ${timeout / 1000}s`,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Knowledge\n // ---------------------------------------------------------------------------\n\n /** Get workspace knowledge (custom instructions for the AI agent). */\n async getWorkspaceKnowledge(workspaceId: string): Promise<KnowledgeResponse> {\n return this.request<KnowledgeResponse>(\"GET\", `/v1/workspaces/${workspaceId}/knowledge`);\n }\n\n /** Set workspace knowledge. Max 10,000 characters. */\n async setWorkspaceKnowledge(workspaceId: string, content: string): Promise<KnowledgeResponse> {\n return this.request<KnowledgeResponse>(\"PUT\", `/v1/workspaces/${workspaceId}/knowledge`, { content });\n }\n\n /** Get project knowledge (custom instructions for the AI agent). */\n async getProjectKnowledge(projectId: string): Promise<KnowledgeResponse> {\n return this.request<KnowledgeResponse>(\"GET\", `/v1/projects/${projectId}/knowledge`);\n }\n\n /** Set project knowledge. Max 10,000 characters. */\n async setProjectKnowledge(projectId: string, content: string): Promise<KnowledgeResponse> {\n return this.request<KnowledgeResponse>(\"PUT\", `/v1/projects/${projectId}/knowledge`, { content });\n }\n\n // ---------------------------------------------------------------------------\n // Git operations\n // ---------------------------------------------------------------------------\n\n /**\n * Get the structured diff for a message or commit.\n * Pass `messageId` to get the diff for a specific AI message,\n * or `sha` for a specific commit.\n */\n async getDiff(\n projectId: string,\n params: { messageId?: string; sha?: string; baseSha?: string },\n ): Promise<GitDiffResponse> {\n const qs = new URLSearchParams();\n if (params.messageId) qs.set(\"message_id\", params.messageId);\n if (params.sha) qs.set(\"sha\", params.sha);\n if (params.baseSha) qs.set(\"base_sha\", params.baseSha);\n return this.request<GitDiffResponse>(\"GET\", `/v1/projects/${projectId}/git/diff?${qs.toString()}`);\n }\n\n /** List all files in a project at a specific git ref. */\n async listFiles(projectId: string, ref: string): Promise<GitFilesResponse> {\n const qs = new URLSearchParams({ ref });\n return this.request<GitFilesResponse>(\"GET\", `/v1/projects/${projectId}/git/files?${qs.toString()}`);\n }\n\n /** Read the raw content of a single file at a specific git ref. Returns text. */\n async readFile(projectId: string, path: string, ref: string): Promise<string> {\n const qs = new URLSearchParams({ path, ref });\n return this.requestText(\"GET\", `/v1/projects/${projectId}/git/file?${qs.toString()}`);\n }\n\n // ---------------------------------------------------------------------------\n // Edits\n // ---------------------------------------------------------------------------\n\n /** List the edit history of a project. */\n async listEdits(projectId: string, params?: { limit?: number; before?: string }): Promise<EditsResponse> {\n const qs = new URLSearchParams();\n if (params?.limit !== undefined) qs.set(\"limit\", String(params.limit));\n if (params?.before) qs.set(\"before\", params.before);\n const suffix = qs.toString() ? `?${qs.toString()}` : \"\";\n return this.request<EditsResponse>(\"GET\", `/v1/projects/${projectId}/edits${suffix}`);\n }\n\n // ---------------------------------------------------------------------------\n // File upload\n // ---------------------------------------------------------------------------\n\n /** Get a presigned URL for uploading a file. Returns the upload URL and file ID. */\n async getFileUploadUrl(params: { file_name: string; content_type?: string }): Promise<FileUploadUrlResponse> {\n return this.request<FileUploadUrlResponse>(\"POST\", \"/v1/files/upload-url\", params);\n }\n\n // ---------------------------------------------------------------------------\n // Visibility\n // ---------------------------------------------------------------------------\n\n /** Set a project's visibility (draft, private, or public). */\n async setProjectVisibility(projectId: string, visibility: string): Promise<{ visibility: string }> {\n return this.request<{ visibility: string }>(\"PUT\", `/v1/projects/${projectId}/visibility`, { visibility });\n }\n\n /** Set a folder's visibility (personal or workspace). */\n async setFolderVisibility(\n workspaceId: string,\n folderId: string,\n visibility: string,\n ): Promise<{ visibility: string }> {\n return this.request<{ visibility: string }>(\"PUT\", `/v1/workspaces/${workspaceId}/folders/${folderId}/visibility`, {\n visibility,\n });\n }\n\n // ---------------------------------------------------------------------------\n // Library & template projects\n // ---------------------------------------------------------------------------\n\n /** List available design system library projects in a workspace. */\n async listLibraryProjects(workspaceId: string): Promise<{ libraries: LibraryProjectResponse[] }> {\n return this.request<{ libraries: LibraryProjectResponse[] }>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/available-library-projects`,\n );\n }\n\n /** List available template projects in a workspace. */\n async listTemplateProjects(workspaceId: string): Promise<{ templates: TemplateProjectResponse[] }> {\n return this.request<{ templates: TemplateProjectResponse[] }>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/available-template-projects`,\n );\n }\n\n // ---------------------------------------------------------------------------\n // Connectors (MCP servers)\n // ---------------------------------------------------------------------------\n\n /** List all connectors in a workspace. */\n async listConnectors(workspaceId: string): Promise<{ connectors: ConnectorResponse[] }> {\n return this.request<{ connectors: ConnectorResponse[] }>(\"GET\", `/v1/workspaces/${workspaceId}/connectors`);\n }\n\n /** Add a connector to a workspace. The server URL is tested before saving. */\n async addConnector(workspaceId: string, body: AddConnectorBody): Promise<ConnectorResponse> {\n return this.request<ConnectorResponse>(\"POST\", `/v1/workspaces/${workspaceId}/connectors`, body);\n }\n\n /** Remove a connector from a workspace. */\n async removeConnector(workspaceId: string, connectorId: string): Promise<{ success: boolean }> {\n return this.request<{ success: boolean }>(\"DELETE\", `/v1/workspaces/${workspaceId}/connectors/${connectorId}`);\n }\n\n /** Browse available connector templates. */\n async listAvailableConnectors(workspaceId: string): Promise<{ catalog: AvailableConnectorEntry[] }> {\n return this.request<{ catalog: AvailableConnectorEntry[] }>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/available-connectors`,\n );\n }\n\n // ---------------------------------------------------------------------------\n // Connectors\n // ---------------------------------------------------------------------------\n\n /** List standard (OAuth-based) connectors in a workspace. */\n async listStandardConnectors(workspaceId: string): Promise<{ connectors: StandardConnectorItem[] }> {\n return this.request<{ connectors: StandardConnectorItem[] }>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/connectors/standard`,\n );\n }\n\n /** List seamless (zero-config) connectors in a workspace. */\n async listSeamlessConnectors(workspaceId: string): Promise<{ connectors: SeamlessConnectorItem[] }> {\n return this.request<{ connectors: SeamlessConnectorItem[] }>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/connectors/seamless`,\n );\n }\n\n /** List MCP connectors in a workspace. */\n async listMCPConnectors(workspaceId: string): Promise<{ connectors: MCPConnectorItem[]; custom_enabled: boolean }> {\n return this.request<{ connectors: MCPConnectorItem[]; custom_enabled: boolean }>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/connectors/mcp`,\n );\n }\n\n /** List authenticated connections (accounts) in a workspace. */\n async listConnections(\n workspaceId: string,\n params?: { connector_id?: string },\n ): Promise<{ connections: ConnectionItem[] }> {\n const qs = new URLSearchParams();\n if (params?.connector_id) qs.set(\"connector_id\", params.connector_id);\n const suffix = qs.toString() ? `?${qs.toString()}` : \"\";\n return this.request<{ connections: ConnectionItem[] }>(\"GET\", `/v1/workspaces/${workspaceId}/connections${suffix}`);\n }\n\n // ---------------------------------------------------------------------------\n // Analytics\n // ---------------------------------------------------------------------------\n\n /** Get historical analytics for a published project. */\n async getProjectAnalytics(\n projectId: string,\n params: { startDate: string; endDate: string; granularity?: string },\n ): Promise<ProjectAnalyticsResponse> {\n const qs = new URLSearchParams({\n startDate: params.startDate,\n endDate: params.endDate,\n });\n if (params.granularity) qs.set(\"granularity\", params.granularity);\n return this.request<ProjectAnalyticsResponse>(\"GET\", `/v1/projects/${projectId}/analytics?${qs.toString()}`);\n }\n\n /** Get real-time visitor trend for a published project. */\n async getProjectAnalyticsTrend(projectId: string): Promise<ProjectAnalyticsTrendResponse> {\n return this.request<ProjectAnalyticsTrendResponse>(\"GET\", `/v1/projects/${projectId}/analytics/trend`);\n }\n\n /**\n * Publish a project.\n *\n * This triggers a deployment which makes the project publicly accessible.\n * The deployment runs asynchronously - use waitForProjectPublished() to wait for completion.\n *\n * @param projectId - The project ID to publish\n * @param options.name - Optional custom slug for the published URL\n * @returns Deployment info including deployment ID\n */\n async publish(projectId: string, options?: { name?: string }): Promise<DeploymentResponse> {\n return this.request<DeploymentResponse>(\"POST\", `/v1/projects/${projectId}/deployments`, {\n name: options?.name,\n });\n }\n\n /**\n * Remix (fork) an existing project, optionally at a specific message point in time.\n *\n * When `messageId` is provided, the remix captures the project state as it was\n * just before that message was processed (default). Set `remixMode: \"including\"`\n * to include the message and its AI response in the remix.\n * Without `messageId`, the full current state is remixed.\n *\n * @param sourceProjectId - The project to remix from\n * @param options.workspaceId - Target workspace for the new project\n * @param options.messageId - Optional message ID to snapshot at\n * @param options.remixMode - \"before\" (default): state before the message; \"including\": state after the message and its AI response\n * @param options.includeHistory - Whether to preserve chat history (default: false)\n * @param options.includeCustomKnowledge - Whether to copy custom instructions (default: false)\n * @param options.initialMessage - Optional initial message to send after remix\n * @returns The remix job ID for polling progress\n */\n async remixProject(sourceProjectId: string, options: RemixProjectOptions): Promise<string> {\n const body: RemixInitBody = {\n workspace_id: options.workspaceId,\n include_history: options.includeHistory,\n include_custom_knowledge: options.includeCustomKnowledge,\n project_name: options.projectName,\n skip_initial_remix_message: options.skipInitialRemixMessage,\n skip_integrations: options.skipIntegrations,\n };\n\n if (options.messageId) {\n body.message_id = options.messageId;\n body.remix_mode = options.remixMode ?? \"before\";\n }\n\n if (options.initialMessage) {\n body.initial_message = {\n id: crypto.randomUUID(),\n message: options.initialMessage,\n chat_only: false,\n headless: true,\n };\n }\n\n const response = await this.request<RemixInitResponse>(\"POST\", `/v1/projects/${sourceProjectId}/remix/init`, body);\n return response.job_id;\n }\n\n /**\n * Wait for a remix operation to complete.\n *\n * Polls the remix progress endpoint until the job reaches \"completed\" or \"error\" status.\n *\n * @param sourceProjectId - The source project ID (used for the progress endpoint)\n * @param jobId - The job ID returned by `remixProject()`\n * @param options.pollInterval - Time between polls in ms (default: 2000)\n * @param options.timeout - Maximum time to wait in ms (default: 300000 = 5 minutes)\n * @param options.onProgress - Optional callback for status/step updates\n * @returns The new project ID\n * @throws Error if the remix fails or timeout is reached\n */\n async waitForRemix(sourceProjectId: string, jobId: string, options?: RemixWaitOptions): Promise<RemixResult> {\n const pollInterval = options?.pollInterval ?? 2000;\n const timeout = options?.timeout ?? 300000;\n const startTime = Date.now();\n\n while (true) {\n const progress = await this.request<RemixProgressResponse>(\n \"GET\",\n `/v1/projects/${sourceProjectId}/remix/progress?job_id=${encodeURIComponent(jobId)}`,\n );\n\n options?.onProgress?.(progress.status, progress.step);\n\n if (progress.status === \"completed\" && progress.result) {\n return { projectId: progress.result.project_id };\n }\n\n if (progress.status === \"error\") {\n throw new Error(progress.error_message ?? \"Remix failed\");\n }\n\n if (Date.now() - startTime > timeout) {\n throw new Error(`Timeout waiting for remix of project ${sourceProjectId}`);\n }\n\n await sleep(pollInterval);\n }\n }\n\n /**\n * Wait for a project to reach \"completed\" status.\n *\n * Projects start in \"in_progress\" status while being created/built.\n * This method polls until the status becomes \"completed\" or \"failed\".\n * A successful completion means the project's preview is ready to view.\n *\n * @param projectId - The project ID to wait for\n * @param options.pollInterval - Time between polls in ms (default: 2000)\n * @param options.timeout - Maximum time to wait in ms (default: 300000 = 5 minutes)\n * @param options.onProgress - Optional callback for status updates\n * @returns The completed project\n * @throws Error if project fails or timeout is reached\n */\n async waitForProjectReady(projectId: string, options?: WaitOptions): Promise<ProjectResponse> {\n const pollInterval = options?.pollInterval ?? 2000;\n const timeout = options?.timeout ?? 300000;\n const startTime = Date.now();\n\n while (true) {\n const project = await this.getProject(projectId);\n options?.onProgress?.(project);\n\n if (project.status === \"completed\") {\n return project;\n }\n\n if (project.status === \"failed\") {\n throw new Error(`Project ${projectId} failed to build`);\n }\n\n if (Date.now() - startTime > timeout) {\n throw new Error(`Timeout waiting for project ${projectId} to be ready`);\n }\n\n await sleep(pollInterval);\n }\n }\n\n /**\n * Wait for the AI response to a chat message.\n *\n * Connects to the project's message stream (SSE) and accumulates the\n * response content until the message is complete. Returns the full\n * response text along with the project's preview URL.\n *\n * Use this after `chat()` or after `createProject()` with `initialMessage`.\n *\n * @param projectId - The project ID to listen for\n * @param options.timeout - Maximum time to wait in ms (default: 300000 = 5 minutes)\n * @returns The AI response content and preview URL\n * @throws Error if the stream fails or timeout is reached\n */\n async waitForResponse(projectId: string, options?: ChatResponseOptions): Promise<ChatResponse> {\n const timeout = options?.timeout ?? 300000;\n const url = `${this.baseUrl}/v1/projects/${projectId}/messages/stream`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n headers: {\n ...this.authHeaders,\n ...this.extraHeaders,\n },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new ApiError(response.status, `Failed to connect to message stream: HTTP ${response.status}`);\n }\n\n if (!response.body) {\n throw new Error(\"Response body is not readable\");\n }\n\n const result = await this.consumeSSEStream(response.body);\n return {\n content: result.content,\n messageId: result.messageId,\n previewUrl: this.getPreviewUrl(projectId),\n };\n } catch (err) {\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new Error(`Timeout waiting for response on project ${projectId}`);\n }\n throw err;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Developer/experimental APIs. These are not part of the stable v1 surface\n * and may change without notice.\n */\n get _dev() {\n return {\n /**\n * Fetch Braintrust traces for a specific chat message.\n *\n * Returns the trace spans associated with the AI response message.\n * The messageId is available from the ChatResponse returned by waitForResponse().\n *\n * Use the `purposes` option to filter which span types are returned.\n * When a purpose has multiple spans (e.g. main_agent across turns),\n * only the last span is returned — it contains the full accumulated context.\n *\n * @param projectId - The project ID\n * @param messageId - The AI message ID (from ChatResponse.messageId)\n * @param options.purposes - Filter spans by purpose (e.g. [\"main_agent\", \"knowledge_rag\"])\n * @returns The trace data including filtered spans\n */\n getMessageTraces: async (\n projectId: string,\n messageId: string,\n options?: GetMessageTracesOptions,\n ): Promise<MessageTracesResponse> => {\n const params = new URLSearchParams();\n if (options?.purposes?.length) {\n params.set(\"purposes\", options.purposes.join(\",\"));\n }\n const query = params.toString();\n const path = `/v1/_dev/projects/${projectId}/messages/${messageId}/traces${query ? `?${query}` : \"\"}`;\n return this.request<MessageTracesResponse>(\"GET\", path);\n },\n\n /**\n * Fetch traces for multiple messages across projects in parallel.\n *\n * Fires concurrent requests (up to `concurrency` at a time) and collects\n * results. Failed requests are captured in `errors` instead of throwing.\n *\n * @param queries - Array of { projectId, messageId } to fetch\n * @param options.purposes - Filter spans by purpose (applied to all queries)\n * @param options.concurrency - Max parallel requests (default: 5)\n * @returns Object with `traces` map (keyed by messageId) and `errors` map\n */\n getMessageTracesBatch: async (\n queries: TraceQuery[],\n options?: GetMessageTracesOptions & { concurrency?: number },\n ): Promise<BatchTracesResult> => {\n const concurrency = options?.concurrency ?? 5;\n const traces = new Map<string, MessageTracesResponse>();\n const errors = new Map<string, Error>();\n const purposeOpts = options?.purposes ? { purposes: options.purposes } : undefined;\n\n const pending = [...queries];\n const executing = new Set<Promise<void>>();\n\n for (const query of pending) {\n const task = this._dev\n .getMessageTraces(query.projectId, query.messageId, purposeOpts)\n .then((result) => {\n traces.set(query.messageId, result);\n })\n .catch((err) => {\n errors.set(query.messageId, err instanceof Error ? err : new Error(String(err)));\n })\n .finally(() => {\n executing.delete(task);\n });\n\n executing.add(task);\n\n if (executing.size >= concurrency) {\n await Promise.race(executing);\n }\n }\n\n await Promise.all(executing);\n\n return { traces, errors };\n },\n\n /**\n * Re-run project reviewers (e.g. project_success_v3, user_sentiment) for past assistant\n * messages. Requires an API key with project write access.\n */\n replayReviews: async (projectId: string, body: ReplayReviewsRequestBody): Promise<ReplayReviewsResponse> => {\n return this.request<ReplayReviewsResponse>(\"POST\", `/v1/_dev/projects/${projectId}/reviews/replay`, body);\n },\n };\n }\n\n private isFileInput(file: File | FileInput): file is FileInput {\n return \"data\" in file;\n }\n\n private async uploadFile(file: File | FileInput): Promise<UnscopedFile> {\n const fileName = this.isFileInput(file) ? file.name : file.name;\n const mimeType = this.isFileInput(file) ? file.type : file.type;\n const body = this.isFileInput(file) ? file.data : file;\n\n const { url, file_id: objectPath } = await this.request<{ url: string; file_id: string }>(\n \"POST\",\n \"/v1/files/upload-url\",\n {\n file_name: fileName,\n content_type: mimeType,\n },\n );\n\n const uploadResponse = await fetch(url, {\n method: \"PUT\",\n body,\n headers: { \"Content-Type\": mimeType },\n });\n if (!uploadResponse.ok) {\n throw new Error(`File upload failed for \"${fileName}\": HTTP ${uploadResponse.status}`);\n }\n\n return { file_id: objectPath, type: \"user_upload\", file_name: fileName, mime_type: mimeType };\n }\n\n private async uploadFiles(files: (File | FileInput)[]): Promise<UnscopedFile[]> {\n return Promise.all(files.map((file) => this.uploadFile(file)));\n }\n\n private async consumeSSEStream(body: ReadableStream<Uint8Array>): Promise<{ content: string; messageId: string }> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let content = \"\";\n let messageId = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // SSE events are separated by double newlines\n const parts = buffer.split(\"\\n\\n\");\n buffer = parts.pop() ?? \"\";\n\n for (const part of parts) {\n if (!part.trim()) continue;\n\n const lines = part.split(\"\\n\");\n let eventType = \"\";\n let eventData = \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"event: \")) {\n eventType = line.slice(7);\n } else if (line.startsWith(\"data: \")) {\n eventData = line.slice(6);\n }\n }\n\n if (eventType === \"message\" && eventData) {\n try {\n const data = JSON.parse(eventData);\n if (typeof data.content === \"string\") {\n content += data.content;\n }\n if (typeof data.message_id === \"string\" && data.message_id) {\n messageId = data.message_id;\n }\n if (data.is_final) {\n return { content, messageId };\n }\n } catch {\n // Skip non-JSON data lines\n }\n }\n\n if (eventType === \"error\") {\n let detail = \"Stream error from server\";\n try {\n const data = JSON.parse(eventData);\n if (data.message) detail = data.message;\n } catch {\n // use default message\n }\n throw new Error(detail);\n }\n }\n }\n } finally {\n void reader.cancel();\n }\n\n return { content, messageId };\n }\n\n /**\n * Wait for a project to be published (deployed).\n *\n * This method polls until the project has `is_published: true` and a `url`.\n *\n * @param projectId - The project ID to wait for\n * @param options.pollInterval - Time between polls in ms (default: 3000)\n * @param options.timeout - Maximum time to wait in ms (default: 600000 = 10 minutes)\n * @param options.onProgress - Optional callback for status updates\n * @returns The published project with URL\n * @throws Error if timeout is reached\n */\n async waitForProjectPublished(projectId: string, options?: WaitOptions): Promise<ProjectResponse> {\n const pollInterval = options?.pollInterval ?? 3000;\n const timeout = options?.timeout ?? 600000;\n const startTime = Date.now();\n\n while (true) {\n const project = await this.getProject(projectId);\n options?.onProgress?.(project);\n\n if (project.is_published && project.url) {\n return project;\n }\n\n if (project.status === \"failed\") {\n throw new Error(`Project ${projectId} failed to build`);\n }\n\n if (Date.now() - startTime > timeout) {\n throw new Error(`Timeout waiting for project ${projectId} to be published`);\n }\n\n await sleep(pollInterval);\n }\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/client.ts","../src/types.ts"],"sourcesContent":["import type { Client, Middleware } from \"openapi-fetch\";\n\nimport createClient from \"openapi-fetch\";\n\nimport type { paths } from \"./generated/paths.js\";\nimport type {\n LovableClientOptions,\n CreateProjectOptions,\n InviteCollaboratorOptions,\n ChatMessageOptions,\n WaitOptions,\n WorkspaceWithMembership,\n ProjectResponse,\n WorkspaceMembershipResponse,\n CreateProjectBody,\n AddUserToWorkspaceInputBody,\n GetWorkspacesResponse,\n DeploymentResponse,\n ChatResponse,\n ChatResponseOptions,\n UnscopedFile,\n FileInput,\n RemixProjectOptions,\n RemixInitBody,\n RemixInitResponse,\n RemixProgressResponse,\n RemixResult,\n RemixWaitOptions,\n MessageTracesResponse,\n GetMessageTracesOptions,\n TraceQuery,\n BatchTracesResult,\n ReplayReviewsRequestBody,\n ReplayReviewsResponse,\n MeResponse,\n DatabaseStatus,\n EnableDatabaseResult,\n DatabaseQueryResult,\n SendMessageResponse,\n GetMessageResponse,\n ListMessagesResponse,\n MessageCompletionResult,\n MessageCompletionOptions,\n KnowledgeResponse,\n FileUploadUrlResponse,\n GitDiffResponse,\n GitFilesResponse,\n EditsResponse,\n ListProjectsResponse,\n ListProjectsOptions,\n ConnectorResponse,\n AvailableConnectorEntry,\n AddConnectorBody,\n StandardConnectorItem,\n SeamlessConnectorItem,\n MCPConnectorItem,\n ConnectionItem,\n ProjectAnalyticsResponse,\n ProjectAnalyticsTrendResponse,\n LibraryProjectResponse,\n TemplateProjectResponse,\n} from \"./types.js\";\n\nimport { ApiError } from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.lovable.dev\";\n\nfunction normalizeBaseUrl(url: string | undefined): string {\n if (!url) return DEFAULT_BASE_URL;\n\n const normalized = url.replace(/\\/$/, \"\");\n\n if (!normalized.startsWith(\"http://\") && !normalized.startsWith(\"https://\")) {\n throw new Error(`baseUrl must include a protocol (http:// or https://). Got: \"${url}\"`);\n }\n\n return normalized;\n}\n\n/**\n * openapi-fetch middleware that converts non-2xx Response objects into the\n * SDK's ApiError, preserving the existing error contract (status/type/detail/\n * props) used by every other method in this client.\n */\nconst errorMiddleware: Middleware = {\n async onResponse({ response }) {\n if (response.ok) return;\n let errorBody:\n | {\n type?: string;\n title?: string;\n message?: string;\n detail?: string;\n details?: string;\n props?: Record<string, unknown>;\n }\n | undefined;\n try {\n errorBody = (await response.clone().json()) as typeof errorBody;\n } catch {\n // Ignore JSON parse errors\n }\n const message = buildErrorMessage(errorBody, response.status, response.statusText);\n const type = errorBody?.type ?? errorBody?.title;\n const detail = errorBody?.detail ?? errorBody?.details;\n throw new ApiError(response.status, message, type, detail, errorBody?.props);\n },\n};\n\n// HTTP/2 leaves response.statusText empty, and some Go API endpoints return\n// `{message: \"\"}` / `{title: \"\"}` bodies on errors. `??` would preserve the\n// empty string and produce an ApiError with no usable message; consumers\n// downstream (MCP, logs, UI) then render an unlabeled failure.\nfunction buildErrorMessage(\n body: { message?: string; title?: string } | undefined,\n status: number,\n statusText: string,\n): string {\n return body?.message || body?.title || (statusText ? `HTTP ${status}: ${statusText}` : `HTTP ${status}`);\n}\n\nexport class LovableClient {\n private readonly authHeaders: Record<string, string>;\n private readonly baseUrl: string;\n private readonly extraHeaders: Record<string, string>;\n private readonly clientSource: string;\n private readonly typedClient: Client<paths>;\n\n constructor(options: LovableClientOptions) {\n const hasApiKey = !!options.apiKey;\n const hasBearerToken = !!options.bearerToken;\n\n if (!hasApiKey && !hasBearerToken) {\n throw new Error(\"Either apiKey or bearerToken is required\");\n }\n if (hasApiKey && hasBearerToken) {\n throw new Error(\"Provide either apiKey or bearerToken, not both\");\n }\n\n this.authHeaders = hasApiKey\n ? { \"Lovable-API-Key\": options.apiKey! }\n : { Authorization: `Bearer ${options.bearerToken}` };\n\n this.baseUrl = normalizeBaseUrl(options.baseUrl);\n this.extraHeaders = options.headers ?? {};\n this.clientSource = options.clientSource ?? \"sdk\";\n\n this.typedClient = createClient<paths>({\n baseUrl: this.baseUrl,\n headers: {\n \"X-Client-Source\": this.clientSource,\n ...this.authHeaders,\n ...this.extraHeaders,\n Accept: \"application/json\",\n },\n });\n this.typedClient.use(errorMiddleware);\n }\n\n /**\n * Type-safe access to any documented API route, driven by the auto-generated\n * OpenAPI schema. Path / query params and request bodies are checked at compile\n * time; calling an unknown path is a type error.\n *\n * Non-2xx responses throw `ApiError`; on success, `data` is set on the result.\n */\n get typed(): Client<paths> {\n return this.typedClient;\n }\n\n private async rawRequest(\n method: string,\n path: string,\n body?: unknown,\n init?: { headers?: Record<string, string>; signal?: AbortSignal },\n ): Promise<Response> {\n const url = `${this.baseUrl}${path}`;\n\n // init.headers can ADD per-request headers but must NOT override built-in\n // auth/client identification — spread it first so authHeaders win.\n const headers: Record<string, string> = {\n \"X-Client-Source\": this.clientSource,\n ...init?.headers,\n ...this.authHeaders,\n ...this.extraHeaders,\n Accept: \"application/json\",\n };\n if (body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n signal: init?.signal,\n });\n\n if (!response.ok) {\n let errorBody:\n | {\n type?: string;\n title?: string;\n message?: string;\n detail?: string;\n details?: string;\n props?: Record<string, unknown>;\n }\n | undefined;\n try {\n errorBody = (await response.json()) as typeof errorBody;\n } catch {\n // Ignore JSON parse errors\n }\n\n const message = buildErrorMessage(errorBody, response.status, response.statusText);\n const type = errorBody?.type ?? errorBody?.title;\n const detail = errorBody?.detail ?? errorBody?.details;\n throw new ApiError(response.status, message, type, detail, errorBody?.props);\n }\n\n return response;\n }\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const response = await this.rawRequest(method, path, body);\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return response.json() as Promise<T>;\n }\n\n private async requestText(method: string, path: string): Promise<string> {\n const response = await this.rawRequest(method, path);\n return response.text();\n }\n\n /**\n * Get the current authenticated user and their workspaces.\n * Useful for validating an API key and discovering workspace IDs.\n */\n async me(): Promise<MeResponse> {\n const { data } = await this.typed.GET(\"/v1/me\");\n return { ...data!, workspaces: data!.workspaces ?? [] };\n }\n\n /**\n * List all workspaces the authenticated user has access to\n */\n async listWorkspaces(): Promise<WorkspaceWithMembership[]> {\n const response = await this.request<GetWorkspacesResponse>(\"GET\", \"/v1/workspaces\");\n return response.workspaces ?? [];\n }\n\n /**\n * Get a specific workspace by ID\n */\n async getWorkspace(workspaceId: string): Promise<WorkspaceWithMembership> {\n const response = await this.request<{ workspace: WorkspaceWithMembership }>(\"GET\", `/v1/workspaces/${workspaceId}`);\n return response.workspace;\n }\n\n /**\n * List projects in a workspace.\n * Supports full-text search, filtering by visibility/publish status/folder/creator,\n * and pagination via offset or cursor.\n */\n async listProjects(workspaceId: string, options?: ListProjectsOptions): Promise<ListProjectsResponse> {\n const params = new URLSearchParams();\n if (options?.query) params.set(\"q\", options.query);\n if (options?.visibility) params.set(\"visibility\", options.visibility);\n if (options?.publish_status) params.set(\"publish_status\", options.publish_status);\n if (options?.folder_id) params.set(\"folder_id\", options.folder_id);\n if (options?.user_id) params.set(\"user_id\", options.user_id);\n if (options?.sort_by) params.set(\"sort_by\", options.sort_by);\n if (options?.sort_order) params.set(\"sort_order\", options.sort_order);\n if (options?.viewed_by_me) params.set(\"viewed_by_me\", \"true\");\n if (options?.limit !== undefined) params.set(\"limit\", String(options.limit));\n if (options?.offset !== undefined) params.set(\"offset\", String(options.offset));\n if (options?.cursor) params.set(\"cursor\", options.cursor);\n\n const query = params.toString();\n const path = `/v1/workspaces/${workspaceId}/projects${query ? `?${query}` : \"\"}`;\n\n return this.request<ListProjectsResponse>(\"GET\", path);\n }\n\n /**\n * Create a new project in a workspace\n */\n async createProject(workspaceId: string, options: CreateProjectOptions): Promise<ProjectResponse> {\n let fileRefs: UnscopedFile[] | undefined;\n if (options.uploadedFiles?.length) {\n fileRefs = options.uploadedFiles;\n } else if (options.files?.length) {\n fileRefs = await this.uploadFiles(options.files);\n }\n\n const body: CreateProjectBody = {\n description: options.description,\n template_project_id: options.templateProjectId,\n };\n if (options.visibility) {\n body.visibility = options.visibility;\n }\n if (options.techStack) {\n body.tech_stack = options.techStack;\n }\n if (options.selectedLibraries?.length) {\n body.selected_libraries = options.selectedLibraries;\n }\n\n if (options.initialMessage) {\n body.initial_message = options.initialMessage;\n }\n if (fileRefs?.length) {\n body.files = fileRefs;\n }\n\n const project = await this.request<ProjectResponse>(\"POST\", `/v1/workspaces/${workspaceId}/projects`, body);\n\n return project;\n }\n\n /**\n * Send a chat message to a project.\n *\n * The API accepts the message and processes it in the background.\n * Returns the message ID and status. Use `waitForMessageCompletion()`\n * to poll for the AI response, or `waitForResponse()` for SSE streaming.\n */\n async chat(projectId: string, options: ChatMessageOptions): Promise<SendMessageResponse> {\n let fileRefs: UnscopedFile[] | undefined;\n if (options.uploadedFiles?.length) {\n fileRefs = options.uploadedFiles;\n } else if (options.files?.length) {\n fileRefs = await this.uploadFiles(options.files);\n }\n\n const body: {\n message: string;\n files?: UnscopedFile[];\n plan_mode?: boolean;\n custom_model_endpoint?: string;\n custom_model_api_key?: string;\n custom_model_name?: string;\n custom_model_disable_race?: boolean;\n continuation?: string;\n } = {\n message: options.message,\n };\n if (fileRefs) {\n body.files = fileRefs;\n }\n if (options.planMode) {\n body.plan_mode = true;\n }\n if (options.customModel) {\n body.custom_model_endpoint = options.customModel.endpoint;\n body.custom_model_api_key = options.customModel.apiKey;\n body.custom_model_name = options.customModel.modelName;\n }\n if (options.customModelDisableRace) {\n body.custom_model_disable_race = true;\n }\n if (options.continuation) {\n body.continuation = options.continuation;\n }\n\n return this.request<SendMessageResponse>(\"POST\", `/v1/projects/${projectId}/messages`, body);\n }\n\n /**\n * Invite a user to a workspace as a collaborator\n */\n async inviteCollaborator(\n workspaceId: string,\n options: InviteCollaboratorOptions,\n ): Promise<WorkspaceMembershipResponse> {\n const body: AddUserToWorkspaceInputBody = {\n email: options.email,\n role: options.role ?? \"member\",\n };\n\n return this.request<WorkspaceMembershipResponse>(\"POST\", `/workspaces/${workspaceId}/memberships`, body);\n }\n\n /**\n * List members of a workspace\n */\n async listWorkspaceMembers(workspaceId: string): Promise<WorkspaceMembershipResponse[]> {\n const response = await this.request<{\n memberships: WorkspaceMembershipResponse[] | null;\n }>(\"GET\", `/workspaces/${workspaceId}/memberships`);\n return response.memberships ?? [];\n }\n\n /**\n * Remove a member from a workspace\n */\n async removeWorkspaceMember(workspaceId: string, userId: string): Promise<void> {\n await this.request<void>(\"DELETE\", `/workspaces/${workspaceId}/memberships/${userId}`);\n }\n\n /**\n * Get project details by ID\n */\n async getProject(projectId: string): Promise<ProjectResponse> {\n return this.request<ProjectResponse>(\"GET\", `/v1/projects/${projectId}`);\n }\n\n /**\n * Get the preview URL for a project.\n *\n * The preview URL is available once the project reaches \"completed\" status.\n * This URL allows viewing the project in development mode.\n *\n * @param projectId - The project ID\n * @returns The preview URL\n */\n getPreviewUrl(projectId: string): string {\n return `https://id-preview--${projectId}.lovable.app`;\n }\n\n /**\n * Get the published URL for a project (if published).\n *\n * Returns the public URL if the project has been published, or null if not.\n *\n * @param projectId - The project ID\n * @returns The published URL or null if not published\n */\n async getPublishedUrl(projectId: string): Promise<string | null> {\n const project = await this.getProject(projectId);\n return project.is_published && project.url ? project.url : null;\n }\n\n /**\n * Get the cloud database status for a project.\n *\n * @param projectId - The project ID\n * @returns Whether the database is enabled and which stack is used\n */\n async getDatabaseStatus(projectId: string): Promise<DatabaseStatus> {\n return this.request<DatabaseStatus>(\"GET\", `/v1/projects/${projectId}/database`);\n }\n\n /**\n * Enable (provision) a cloud database for a project.\n *\n * This triggers database provisioning which takes 30-60 seconds.\n * The call blocks until provisioning completes.\n *\n * @param projectId - The project ID\n * @returns The database status after enablement\n */\n async enableDatabase(projectId: string): Promise<EnableDatabaseResult> {\n return this.request<EnableDatabaseResult>(\"POST\", `/v1/projects/${projectId}/database/enable`);\n }\n\n /**\n * Execute a SQL query against the project's cloud database.\n *\n * Supports SELECT, INSERT, UPDATE, DELETE, and DDL statements.\n * The database must be enabled first (see enableDatabase).\n *\n * @param projectId - The project ID\n * @param sql - SQL query to execute\n * @returns Query result rows as JSON objects\n */\n async queryDatabase(projectId: string, sql: string): Promise<DatabaseQueryResult> {\n return this.request<DatabaseQueryResult>(\"POST\", `/v1/projects/${projectId}/database/query`, { sql });\n }\n\n // ---------------------------------------------------------------------------\n // Messages\n // ---------------------------------------------------------------------------\n\n /**\n * Get a message by ID. Returns the message content, status, and (for user messages)\n * the AI response if available. Use to poll for completion after `chat()`.\n */\n async getMessage(projectId: string, messageId: string): Promise<GetMessageResponse> {\n return this.request<GetMessageResponse>(\"GET\", `/v1/projects/${projectId}/messages/${messageId}`);\n }\n\n /**\n * List recent messages in a project, newest first. Use `before` (a message ID\n * from a prior page) to paginate backwards through history.\n */\n async listMessages(projectId: string, params?: { limit?: number; before?: string }): Promise<ListMessagesResponse> {\n const qs = new URLSearchParams();\n if (params?.limit !== undefined) qs.set(\"limit\", String(params.limit));\n if (params?.before) qs.set(\"before\", params.before);\n const suffix = qs.toString() ? `?${qs.toString()}` : \"\";\n return this.request<ListMessagesResponse>(\"GET\", `/v1/projects/${projectId}/messages${suffix}`);\n }\n\n /**\n * Poll for message completion. Waits until the AI response reaches a terminal\n * status (completed, stopped) or the timeout expires.\n *\n * Handles edge cases: 404 grace period for race conditions between queue\n * dequeue and event creation, and queue pause detection.\n */\n async waitForMessageCompletion(\n projectId: string,\n messageId: string,\n options?: MessageCompletionOptions,\n ): Promise<MessageCompletionResult> {\n const pollInterval = options?.pollInterval ?? 3000;\n const timeout = options?.timeout ?? 600_000;\n const deadline = Date.now() + timeout;\n let notFoundSince: number | null = null;\n const notFoundGraceMs = 15_000;\n\n while (Date.now() < deadline) {\n try {\n const msg = await this.getMessage(projectId, messageId);\n notFoundSince = null;\n\n if (msg.status === \"queued\") {\n if (msg.queue_paused && msg.queue_pause_reason !== \"hitl_tool\") {\n return {\n status: \"error\",\n message_id: messageId,\n content: \"\",\n error:\n `Message is queued (position ${msg.queue_position ?? \"unknown\"}) but the queue is paused` +\n (msg.queue_pause_reason ? ` (reason: ${msg.queue_pause_reason})` : \"\") +\n `. Unpause the queue in the Lovable editor, or use wait=false to return immediately.`,\n };\n }\n await sleep(pollInterval);\n continue;\n }\n\n const ai = msg.response;\n if (ai) {\n if (ai.status === \"completed\" || ai.status === \"stopped\") {\n return {\n status: ai.status === \"completed\" ? \"completed\" : \"error\",\n message_id: ai.message_id,\n content: ai.content,\n edit_id: ai.edit_id,\n commit_sha: ai.commit_sha,\n summary: ai.summary,\n cost_credits: ai.cost_credits,\n };\n }\n }\n\n if (msg.role === \"assistant\") {\n if (msg.status === \"completed\" || msg.status === \"stopped\") {\n return {\n status: msg.status === \"completed\" ? \"completed\" : \"error\",\n message_id: msg.message_id,\n content: msg.content,\n edit_id: msg.edit_id,\n commit_sha: msg.commit_sha,\n summary: msg.summary,\n cost_credits: msg.cost_credits,\n };\n }\n }\n\n await sleep(pollInterval);\n } catch (err) {\n if (err instanceof ApiError && err.status === 404) {\n if (notFoundSince === null) {\n notFoundSince = Date.now();\n }\n if (Date.now() - notFoundSince < notFoundGraceMs) {\n await sleep(pollInterval);\n continue;\n }\n return {\n status: \"error\",\n message_id: messageId,\n content: \"\",\n error: \"Message not found. It may have been deleted from the queue.\",\n };\n }\n await sleep(pollInterval);\n }\n }\n\n return {\n status: \"timeout\",\n message_id: messageId,\n content: \"\",\n error: `Agent did not finish within ${timeout / 1000}s`,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Knowledge\n // ---------------------------------------------------------------------------\n\n /** Get workspace knowledge (custom instructions for the AI agent). */\n async getWorkspaceKnowledge(workspaceId: string): Promise<KnowledgeResponse> {\n const { data } = await this.typed.GET(\"/v1/workspaces/{workspace_id}/knowledge\", {\n params: { path: { workspace_id: workspaceId } },\n });\n return data!;\n }\n\n /** Set workspace knowledge. Max 10,000 characters. */\n async setWorkspaceKnowledge(workspaceId: string, content: string): Promise<KnowledgeResponse> {\n return this.request<KnowledgeResponse>(\"PUT\", `/v1/workspaces/${workspaceId}/knowledge`, { content });\n }\n\n /** Get project knowledge (custom instructions for the AI agent). */\n async getProjectKnowledge(projectId: string): Promise<KnowledgeResponse> {\n const { data } = await this.typed.GET(\"/v1/projects/{project_id}/knowledge\", {\n params: { path: { project_id: projectId } },\n });\n return data!;\n }\n\n /** Set project knowledge. Max 10,000 characters. */\n async setProjectKnowledge(projectId: string, content: string): Promise<KnowledgeResponse> {\n return this.request<KnowledgeResponse>(\"PUT\", `/v1/projects/${projectId}/knowledge`, { content });\n }\n\n // ---------------------------------------------------------------------------\n // Git operations\n // ---------------------------------------------------------------------------\n\n /**\n * Get the structured diff for a message or commit.\n * Pass `messageId` to get the diff for a specific AI message,\n * or `sha` for a specific commit.\n */\n async getDiff(\n projectId: string,\n params: { messageId?: string; sha?: string; baseSha?: string },\n ): Promise<GitDiffResponse> {\n const qs = new URLSearchParams();\n if (params.messageId) qs.set(\"message_id\", params.messageId);\n if (params.sha) qs.set(\"sha\", params.sha);\n if (params.baseSha) qs.set(\"base_sha\", params.baseSha);\n return this.request<GitDiffResponse>(\"GET\", `/v1/projects/${projectId}/git/diff?${qs.toString()}`);\n }\n\n /** List all files in a project at a specific git ref. */\n async listFiles(projectId: string, ref: string): Promise<GitFilesResponse> {\n const qs = new URLSearchParams({ ref });\n return this.request<GitFilesResponse>(\"GET\", `/v1/projects/${projectId}/git/files?${qs.toString()}`);\n }\n\n /** Read the raw content of a single file at a specific git ref. Returns text. */\n async readFile(projectId: string, path: string, ref: string): Promise<string> {\n const qs = new URLSearchParams({ path, ref });\n return this.requestText(\"GET\", `/v1/projects/${projectId}/git/file?${qs.toString()}`);\n }\n\n // ---------------------------------------------------------------------------\n // Edits\n // ---------------------------------------------------------------------------\n\n /** List the edit history of a project. */\n async listEdits(projectId: string, params?: { limit?: number; before?: string }): Promise<EditsResponse> {\n const qs = new URLSearchParams();\n if (params?.limit !== undefined) qs.set(\"limit\", String(params.limit));\n if (params?.before) qs.set(\"before\", params.before);\n const suffix = qs.toString() ? `?${qs.toString()}` : \"\";\n return this.request<EditsResponse>(\"GET\", `/v1/projects/${projectId}/edits${suffix}`);\n }\n\n // ---------------------------------------------------------------------------\n // File upload\n // ---------------------------------------------------------------------------\n\n /** Get a presigned URL for uploading a file. Returns the upload URL and file ID. */\n async getFileUploadUrl(params: { file_name: string; content_type?: string }): Promise<FileUploadUrlResponse> {\n return this.request<FileUploadUrlResponse>(\"POST\", \"/v1/files/upload-url\", params);\n }\n\n // ---------------------------------------------------------------------------\n // Visibility\n // ---------------------------------------------------------------------------\n\n /** Set a project's visibility (draft, private, or public). */\n async setProjectVisibility(projectId: string, visibility: string): Promise<{ visibility: string }> {\n return this.request<{ visibility: string }>(\"PUT\", `/v1/projects/${projectId}/visibility`, { visibility });\n }\n\n /** Set a folder's visibility (personal or workspace). */\n async setFolderVisibility(\n workspaceId: string,\n folderId: string,\n visibility: string,\n ): Promise<{ visibility: string }> {\n return this.request<{ visibility: string }>(\"PUT\", `/v1/workspaces/${workspaceId}/folders/${folderId}/visibility`, {\n visibility,\n });\n }\n\n // ---------------------------------------------------------------------------\n // Library & template projects\n // ---------------------------------------------------------------------------\n\n /** List available design system library projects in a workspace. */\n async listLibraryProjects(workspaceId: string): Promise<{ libraries: LibraryProjectResponse[] }> {\n return this.request<{ libraries: LibraryProjectResponse[] }>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/available-library-projects`,\n );\n }\n\n /** List available template projects in a workspace. */\n async listTemplateProjects(workspaceId: string): Promise<{ templates: TemplateProjectResponse[] }> {\n return this.request<{ templates: TemplateProjectResponse[] }>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/available-template-projects`,\n );\n }\n\n // ---------------------------------------------------------------------------\n // Connectors (MCP servers)\n // ---------------------------------------------------------------------------\n\n /** List all connectors in a workspace. */\n async listConnectors(workspaceId: string): Promise<{ connectors: ConnectorResponse[] }> {\n return this.request<{ connectors: ConnectorResponse[] }>(\"GET\", `/v1/workspaces/${workspaceId}/connectors`);\n }\n\n /** Add a connector to a workspace. The server URL is tested before saving. */\n async addConnector(workspaceId: string, body: AddConnectorBody): Promise<ConnectorResponse> {\n return this.request<ConnectorResponse>(\"POST\", `/v1/workspaces/${workspaceId}/connectors`, body);\n }\n\n /** Remove a connector from a workspace. */\n async removeConnector(workspaceId: string, connectorId: string): Promise<{ success: boolean }> {\n return this.request<{ success: boolean }>(\"DELETE\", `/v1/workspaces/${workspaceId}/connectors/${connectorId}`);\n }\n\n /** Browse available connector templates. */\n async listAvailableConnectors(workspaceId: string): Promise<{ catalog: AvailableConnectorEntry[] }> {\n return this.request<{ catalog: AvailableConnectorEntry[] }>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/available-connectors`,\n );\n }\n\n // ---------------------------------------------------------------------------\n // Connectors\n // ---------------------------------------------------------------------------\n\n /** List standard (OAuth-based) connectors in a workspace. */\n async listStandardConnectors(workspaceId: string): Promise<{ connectors: StandardConnectorItem[] }> {\n return this.request<{ connectors: StandardConnectorItem[] }>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/connectors/standard`,\n );\n }\n\n /** List seamless (zero-config) connectors in a workspace. */\n async listSeamlessConnectors(workspaceId: string): Promise<{ connectors: SeamlessConnectorItem[] }> {\n return this.request<{ connectors: SeamlessConnectorItem[] }>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/connectors/seamless`,\n );\n }\n\n /** List MCP connectors in a workspace. */\n async listMCPConnectors(workspaceId: string): Promise<{ connectors: MCPConnectorItem[]; custom_enabled: boolean }> {\n return this.request<{ connectors: MCPConnectorItem[]; custom_enabled: boolean }>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/connectors/mcp`,\n );\n }\n\n /** List authenticated connections (accounts) in a workspace. */\n async listConnections(\n workspaceId: string,\n params?: { connector_id?: string },\n ): Promise<{ connections: ConnectionItem[] }> {\n const qs = new URLSearchParams();\n if (params?.connector_id) qs.set(\"connector_id\", params.connector_id);\n const suffix = qs.toString() ? `?${qs.toString()}` : \"\";\n return this.request<{ connections: ConnectionItem[] }>(\"GET\", `/v1/workspaces/${workspaceId}/connections${suffix}`);\n }\n\n // ---------------------------------------------------------------------------\n // Analytics\n // ---------------------------------------------------------------------------\n\n /** Get historical analytics for a published project. */\n async getProjectAnalytics(\n projectId: string,\n params: { startDate: string; endDate: string; granularity?: string },\n ): Promise<ProjectAnalyticsResponse> {\n const qs = new URLSearchParams({\n startDate: params.startDate,\n endDate: params.endDate,\n });\n if (params.granularity) qs.set(\"granularity\", params.granularity);\n return this.request<ProjectAnalyticsResponse>(\"GET\", `/v1/projects/${projectId}/analytics?${qs.toString()}`);\n }\n\n /** Get real-time visitor trend for a published project. */\n async getProjectAnalyticsTrend(projectId: string): Promise<ProjectAnalyticsTrendResponse> {\n return this.request<ProjectAnalyticsTrendResponse>(\"GET\", `/v1/projects/${projectId}/analytics/trend`);\n }\n\n /**\n * Publish a project.\n *\n * This triggers a deployment which makes the project publicly accessible.\n * The deployment runs asynchronously - use waitForProjectPublished() to wait for completion.\n *\n * @param projectId - The project ID to publish\n * @param options.name - Optional custom slug for the published URL\n * @returns Deployment info including deployment ID\n */\n async publish(projectId: string, options?: { name?: string }): Promise<DeploymentResponse> {\n return this.request<DeploymentResponse>(\"POST\", `/v1/projects/${projectId}/deployments`, {\n name: options?.name,\n });\n }\n\n /**\n * Remix (fork) an existing project, optionally at a specific message point in time.\n *\n * When `messageId` is provided, the remix captures the project state as it was\n * just before that message was processed (default). Set `remixMode: \"including\"`\n * to include the message and its AI response in the remix.\n * Without `messageId`, the full current state is remixed.\n *\n * @param sourceProjectId - The project to remix from\n * @param options.workspaceId - Target workspace for the new project\n * @param options.messageId - Optional message ID to snapshot at\n * @param options.remixMode - \"before\" (default): state before the message; \"including\": state after the message and its AI response\n * @param options.includeHistory - Whether to preserve chat history (default: false)\n * @param options.includeCustomKnowledge - Whether to copy custom instructions (default: false)\n * @param options.initialMessage - Optional initial message to send after remix\n * @returns The remix job ID for polling progress\n */\n async remixProject(sourceProjectId: string, options: RemixProjectOptions): Promise<string> {\n const body: RemixInitBody = {\n workspace_id: options.workspaceId,\n include_history: options.includeHistory,\n include_custom_knowledge: options.includeCustomKnowledge,\n project_name: options.projectName,\n skip_initial_remix_message: options.skipInitialRemixMessage,\n skip_integrations: options.skipIntegrations,\n };\n\n if (options.messageId) {\n body.message_id = options.messageId;\n body.remix_mode = options.remixMode ?? \"before\";\n }\n\n if (options.initialMessage) {\n body.initial_message = {\n id: crypto.randomUUID(),\n message: options.initialMessage,\n chat_only: false,\n headless: true,\n };\n }\n\n const response = await this.request<RemixInitResponse>(\"POST\", `/v1/projects/${sourceProjectId}/remix/init`, body);\n return response.job_id;\n }\n\n /**\n * Wait for a remix operation to complete.\n *\n * Polls the remix progress endpoint until the job reaches \"completed\" or \"error\" status.\n *\n * @param sourceProjectId - The source project ID (used for the progress endpoint)\n * @param jobId - The job ID returned by `remixProject()`\n * @param options.pollInterval - Time between polls in ms (default: 2000)\n * @param options.timeout - Maximum time to wait in ms (default: 300000 = 5 minutes)\n * @param options.onProgress - Optional callback for status/step updates\n * @returns The new project ID\n * @throws Error if the remix fails or timeout is reached\n */\n async waitForRemix(sourceProjectId: string, jobId: string, options?: RemixWaitOptions): Promise<RemixResult> {\n const pollInterval = options?.pollInterval ?? 2000;\n const timeout = options?.timeout ?? 300000;\n const startTime = Date.now();\n\n while (true) {\n const progress = await this.request<RemixProgressResponse>(\n \"GET\",\n `/v1/projects/${sourceProjectId}/remix/progress?job_id=${encodeURIComponent(jobId)}`,\n );\n\n options?.onProgress?.(progress.status, progress.step);\n\n if (progress.status === \"completed\" && progress.result) {\n return { projectId: progress.result.project_id };\n }\n\n if (progress.status === \"error\") {\n throw new Error(progress.error_message ?? \"Remix failed\");\n }\n\n if (Date.now() - startTime > timeout) {\n throw new Error(`Timeout waiting for remix of project ${sourceProjectId}`);\n }\n\n await sleep(pollInterval);\n }\n }\n\n /**\n * Wait for a project to reach \"completed\" status.\n *\n * Projects start in \"in_progress\" status while being created/built.\n * This method polls until the status becomes \"completed\" or \"failed\".\n * A successful completion means the project's preview is ready to view.\n *\n * @param projectId - The project ID to wait for\n * @param options.pollInterval - Time between polls in ms (default: 2000)\n * @param options.timeout - Maximum time to wait in ms (default: 300000 = 5 minutes)\n * @param options.onProgress - Optional callback for status updates\n * @returns The completed project\n * @throws Error if project fails or timeout is reached\n */\n async waitForProjectReady(projectId: string, options?: WaitOptions): Promise<ProjectResponse> {\n const pollInterval = options?.pollInterval ?? 2000;\n const timeout = options?.timeout ?? 300000;\n const startTime = Date.now();\n\n while (true) {\n const project = await this.getProject(projectId);\n options?.onProgress?.(project);\n\n if (project.status === \"completed\") {\n return project;\n }\n\n if (project.status === \"failed\") {\n throw new Error(`Project ${projectId} failed to build`);\n }\n\n if (Date.now() - startTime > timeout) {\n throw new Error(`Timeout waiting for project ${projectId} to be ready`);\n }\n\n await sleep(pollInterval);\n }\n }\n\n /**\n * Wait for the AI response to a chat message.\n *\n * Connects to the project's message stream (SSE) and accumulates the\n * response content until the message is complete. Returns the full\n * response text along with the project's preview URL.\n *\n * Use this after `chat()` or after `createProject()` with `initialMessage`.\n *\n * @param projectId - The project ID to listen for\n * @param options.timeout - Maximum time to wait in ms (default: 300000 = 5 minutes)\n * @returns The AI response content and preview URL\n * @throws Error if the stream fails or timeout is reached\n */\n async waitForResponse(projectId: string, options?: ChatResponseOptions): Promise<ChatResponse> {\n const timeout = options?.timeout ?? 300000;\n const url = `${this.baseUrl}/v1/projects/${projectId}/messages/stream`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n headers: {\n ...this.authHeaders,\n ...this.extraHeaders,\n },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new ApiError(response.status, `Failed to connect to message stream: HTTP ${response.status}`);\n }\n\n if (!response.body) {\n throw new Error(\"Response body is not readable\");\n }\n\n const result = await this.consumeSSEStream(response.body);\n return {\n content: result.content,\n messageId: result.messageId,\n previewUrl: this.getPreviewUrl(projectId),\n };\n } catch (err) {\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new Error(`Timeout waiting for response on project ${projectId}`);\n }\n throw err;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Developer/experimental APIs. These are not part of the stable v1 surface\n * and may change without notice.\n */\n get _dev() {\n return {\n /**\n * Fetch Braintrust traces for a specific chat message.\n *\n * Returns the trace spans associated with the AI response message.\n * The messageId is available from the ChatResponse returned by waitForResponse().\n *\n * Use the `purposes` option to filter which span types are returned.\n * When a purpose has multiple spans (e.g. main_agent across turns),\n * only the last span is returned — it contains the full accumulated context.\n *\n * @param projectId - The project ID\n * @param messageId - The AI message ID (from ChatResponse.messageId)\n * @param options.purposes - Filter spans by purpose (e.g. [\"main_agent\", \"knowledge_rag\"])\n * @returns The trace data including filtered spans\n */\n getMessageTraces: async (\n projectId: string,\n messageId: string,\n options?: GetMessageTracesOptions,\n ): Promise<MessageTracesResponse> => {\n const params = new URLSearchParams();\n if (options?.purposes?.length) {\n params.set(\"purposes\", options.purposes.join(\",\"));\n }\n const query = params.toString();\n const path = `/v1/_dev/projects/${projectId}/messages/${messageId}/traces${query ? `?${query}` : \"\"}`;\n return this.request<MessageTracesResponse>(\"GET\", path);\n },\n\n /**\n * Fetch traces for multiple messages across projects in parallel.\n *\n * Fires concurrent requests (up to `concurrency` at a time) and collects\n * results. Failed requests are captured in `errors` instead of throwing.\n *\n * @param queries - Array of { projectId, messageId } to fetch\n * @param options.purposes - Filter spans by purpose (applied to all queries)\n * @param options.concurrency - Max parallel requests (default: 5)\n * @returns Object with `traces` map (keyed by messageId) and `errors` map\n */\n getMessageTracesBatch: async (\n queries: TraceQuery[],\n options?: GetMessageTracesOptions & { concurrency?: number },\n ): Promise<BatchTracesResult> => {\n const concurrency = options?.concurrency ?? 5;\n const traces = new Map<string, MessageTracesResponse>();\n const errors = new Map<string, Error>();\n const purposeOpts = options?.purposes ? { purposes: options.purposes } : undefined;\n\n const pending = [...queries];\n const executing = new Set<Promise<void>>();\n\n for (const query of pending) {\n const task = this._dev\n .getMessageTraces(query.projectId, query.messageId, purposeOpts)\n .then((result) => {\n traces.set(query.messageId, result);\n })\n .catch((err) => {\n errors.set(query.messageId, err instanceof Error ? err : new Error(String(err)));\n })\n .finally(() => {\n executing.delete(task);\n });\n\n executing.add(task);\n\n if (executing.size >= concurrency) {\n await Promise.race(executing);\n }\n }\n\n await Promise.all(executing);\n\n return { traces, errors };\n },\n\n /**\n * Re-run project reviewers (e.g. project_success_v3, user_sentiment) for past assistant\n * messages. Requires an API key with project write access.\n */\n replayReviews: async (projectId: string, body: ReplayReviewsRequestBody): Promise<ReplayReviewsResponse> => {\n return this.request<ReplayReviewsResponse>(\"POST\", `/v1/_dev/projects/${projectId}/reviews/replay`, body);\n },\n };\n }\n\n private isFileInput(file: File | FileInput): file is FileInput {\n return \"data\" in file;\n }\n\n private async uploadFile(file: File | FileInput): Promise<UnscopedFile> {\n const fileName = this.isFileInput(file) ? file.name : file.name;\n const mimeType = this.isFileInput(file) ? file.type : file.type;\n const body = this.isFileInput(file) ? file.data : file;\n\n const { url, file_id: objectPath } = await this.request<{ url: string; file_id: string }>(\n \"POST\",\n \"/v1/files/upload-url\",\n {\n file_name: fileName,\n content_type: mimeType,\n },\n );\n\n const uploadResponse = await fetch(url, {\n method: \"PUT\",\n body,\n headers: { \"Content-Type\": mimeType },\n });\n if (!uploadResponse.ok) {\n throw new Error(`File upload failed for \"${fileName}\": HTTP ${uploadResponse.status}`);\n }\n\n return { file_id: objectPath, type: \"user_upload\", file_name: fileName, mime_type: mimeType };\n }\n\n private async uploadFiles(files: (File | FileInput)[]): Promise<UnscopedFile[]> {\n return Promise.all(files.map((file) => this.uploadFile(file)));\n }\n\n private async consumeSSEStream(body: ReadableStream<Uint8Array>): Promise<{ content: string; messageId: string }> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let content = \"\";\n let messageId = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // SSE events are separated by double newlines\n const parts = buffer.split(\"\\n\\n\");\n buffer = parts.pop() ?? \"\";\n\n for (const part of parts) {\n if (!part.trim()) continue;\n\n const lines = part.split(\"\\n\");\n let eventType = \"\";\n let eventData = \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"event: \")) {\n eventType = line.slice(7);\n } else if (line.startsWith(\"data: \")) {\n eventData = line.slice(6);\n }\n }\n\n if (eventType === \"message\" && eventData) {\n try {\n const data = JSON.parse(eventData);\n if (typeof data.content === \"string\") {\n content += data.content;\n }\n if (typeof data.message_id === \"string\" && data.message_id) {\n messageId = data.message_id;\n }\n if (data.is_final) {\n return { content, messageId };\n }\n } catch {\n // Skip non-JSON data lines\n }\n }\n\n if (eventType === \"error\") {\n let detail = \"Stream error from server\";\n try {\n const data = JSON.parse(eventData);\n if (data.message) detail = data.message;\n } catch {\n // use default message\n }\n throw new Error(detail);\n }\n }\n }\n } finally {\n void reader.cancel();\n }\n\n return { content, messageId };\n }\n\n /**\n * Wait for a project to be published (deployed).\n *\n * This method polls until the project has `is_published: true` and a `url`.\n *\n * @param projectId - The project ID to wait for\n * @param options.pollInterval - Time between polls in ms (default: 3000)\n * @param options.timeout - Maximum time to wait in ms (default: 600000 = 10 minutes)\n * @param options.onProgress - Optional callback for status updates\n * @returns The published project with URL\n * @throws Error if timeout is reached\n */\n async waitForProjectPublished(projectId: string, options?: WaitOptions): Promise<ProjectResponse> {\n const pollInterval = options?.pollInterval ?? 3000;\n const timeout = options?.timeout ?? 600000;\n const startTime = Date.now();\n\n while (true) {\n const project = await this.getProject(projectId);\n options?.onProgress?.(project);\n\n if (project.is_published && project.url) {\n return project;\n }\n\n if (project.status === \"failed\") {\n throw new Error(`Project ${projectId} failed to build`);\n }\n\n if (Date.now() - startTime > timeout) {\n throw new Error(`Timeout waiting for project ${projectId} to be published`);\n }\n\n await sleep(pollInterval);\n }\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","// Type definitions for the Lovable SDK\n// These are self-contained and don't require external dependencies\n\nexport type ProjectVisibility = \"draft\" | \"private\" | \"public\";\nexport type MemberRole = \"admin\" | \"collaborator\" | \"invited\" | \"member\" | \"none\" | \"owner\" | \"viewer\";\nexport type ProjectStatus = \"completed\" | \"in_progress\" | \"failed\";\n\nexport interface MeWorkspace {\n id: string;\n name: string;\n role: string;\n}\n\nexport interface MeResponse {\n id: string;\n email: string;\n name: string;\n workspaces: MeWorkspace[];\n}\n\nexport interface WorkspaceMembership {\n email: string;\n invited_at?: string;\n joined_at?: string;\n monthly_credit_limit: number | null;\n project_access?: Record<string, { access_level?: string }>;\n role: MemberRole;\n user_id: string;\n workspace_id: string;\n}\n\nexport interface WorkspaceWithMembership {\n id: string;\n name: string;\n description?: string;\n image_url?: string;\n owner_id?: string;\n is_personal?: boolean;\n plan?: string;\n plan_type?: string;\n num_projects: number;\n num_seats?: number;\n membership: WorkspaceMembership;\n created_at: string;\n updated_at: string;\n deleted_at?: string;\n credits_granted: number;\n credits_used: number;\n daily_credits_limit: number;\n daily_credits_used: number;\n billing_period_credits_limit: number;\n billing_period_credits_used: number;\n billing_period_start_date?: string;\n billing_period_end_date?: string;\n rollover_credits_limit: number;\n rollover_credits_used: number;\n topup_credits_limit: number;\n topup_credits_used: number;\n total_credits_used: number;\n subscription_status?:\n | \"active\"\n | \"canceled\"\n | \"incomplete_expired\"\n | \"incomplete\"\n | \"past_due\"\n | \"paused\"\n | \"trialing\"\n | \"unpaid\";\n referral_code?: string;\n short_referral_code?: string;\n referral_count: number;\n followers_count: number;\n default_project_visibility?: ProjectVisibility;\n default_project_publish_visibility?: \"private\" | \"public\";\n mcp_enabled?: boolean;\n}\n\nexport interface WorkspaceMembershipResponse {\n user_id: string;\n username: string;\n display_name?: string;\n email?: string;\n role: MemberRole;\n invited_at?: string;\n joined_at?: string;\n monthly_credit_limit?: number;\n total_credits_used?: number;\n total_credits_used_in_billing_period?: number;\n project_access?: Record<string, { access_level?: string }>;\n}\n\n/**\n * Project response from the API.\n *\n * The v1 endpoints (getProject, createProject) return a lean subset:\n * id, workspace_id, name, display_name, description, status, visibility,\n * is_published, url. Other fields are available from list endpoints.\n */\nexport interface ProjectResponse {\n id: string;\n name?: string;\n display_name?: string;\n description?: string;\n tech_stack?: string;\n status?: string;\n visibility?: ProjectVisibility;\n publish_visibility?: \"private\" | \"public\";\n is_published?: boolean;\n /** Present in create project response when initial_message was provided */\n message_id?: string;\n is_starred?: boolean;\n is_template?: boolean;\n is_github?: boolean;\n is_supabase_enabled?: boolean;\n url?: string;\n og_image_url?: string;\n latest_screenshot_url?: string;\n user_id?: string;\n user_display_name?: string;\n user_photo_url?: string;\n created_at?: string;\n created_by?: string;\n updated_at?: string;\n deleted_at?: string;\n last_edited_at?: string;\n last_viewed_at?: string;\n published_at?: string;\n workspace_id?: string;\n folder_id?: string;\n category?: string;\n edit_count?: number;\n gen_count?: number;\n user_message_count?: number;\n remix_count?: number;\n remixed_from_project_id?: string;\n template_project_id?: string;\n credit_total?: number;\n custom_instructions?: string;\n main_branch?: string;\n latest_commit_sha?: string;\n github_repo_name?: string;\n github_repo_id?: number;\n deployment_target?: string;\n active_deployment_job_id?: string;\n environments_enabled?: boolean;\n hide_badge?: boolean | null;\n featured?: boolean;\n featured_at?: string;\n feature_rank?: number;\n feature_source?: string;\n}\n\nexport interface UnscopedFile {\n file_id: string;\n type: \"user_upload\";\n file_name?: string;\n mime_type?: string;\n}\n\nexport interface FileInput {\n name: string;\n data: Blob | ArrayBuffer | Uint8Array;\n type: string;\n}\n\n/**\n * Configuration to use a custom OpenAI-compatible model as the main agent.\n */\nexport interface CustomModelConfig {\n /** Base URL of the OpenAI-compatible API (e.g. \"https://my-vllm.example.com/v1\") */\n endpoint: string;\n /** API key for the custom endpoint */\n apiKey: string;\n /** Model identifier sent in the request (e.g. \"my-org/my-model-id\") */\n modelName: string;\n}\n\nexport interface ChatRequest {\n id: string;\n message: string;\n chat_only: boolean;\n headless: boolean;\n ai_message_id?: string;\n intent?: string;\n model?: string;\n temperature?: number;\n current_page?: string;\n view?: string;\n view_description?: string;\n prev_session_id?: string;\n is_creation?: boolean;\n files?: UnscopedFile[];\n custom_model_endpoint?: string;\n custom_model_api_key?: string;\n custom_model_name?: string;\n custom_model_disable_race?: boolean;\n}\n\nexport interface CreateProjectBody {\n description: string;\n tech_stack?: string;\n visibility?: ProjectVisibility;\n template_project_id?: string;\n initial_message?: string;\n files?: UnscopedFile[];\n selected_libraries?: { project_id: string }[];\n category?: string;\n project_type?: string;\n prompt_name?: string;\n selected_theme?: string;\n env_vars?: Record<string, string>;\n metadata?: Record<string, unknown>;\n}\n\nexport interface AddUserToWorkspaceInputBody {\n email: string;\n role?: MemberRole;\n}\n\nexport interface GetWorkspacesResponse {\n workspaces: WorkspaceWithMembership[] | null;\n}\n\nexport interface GetWorkspaceProjectsResponse {\n projects: ProjectResponse[] | null;\n}\n\nexport interface CreateProjectOptions {\n description: string;\n techStack?: string;\n visibility?: ProjectVisibility;\n templateProjectId?: string;\n initialMessage?: string;\n /** Files to upload and attach to the initial message. The SDK handles uploading. */\n files?: (File | FileInput)[];\n /** Pre-uploaded file references to attach to the initial message. Skips upload. */\n uploadedFiles?: UnscopedFile[];\n /** Design system library projects to connect to the new project. */\n selectedLibraries?: { project_id: string }[];\n}\n\nexport interface InviteCollaboratorOptions {\n email: string;\n role?: MemberRole;\n}\n\n/**\n * Controls prompt cache continuation behavior for a message.\n *\n * - `\"force\"` — skip cache TTL and token/criteria checks (force continuation)\n * - `\"fresh_build\"` — force a full prompt rebuild from scratch\n * - `\"allow_expired_cache\"` — skip cache TTL check but respect token/criteria limits\n *\n * Omit for default behavior (5-min TTL, token and criteria checks apply).\n */\nexport type ContinuationOverride = \"force\" | \"fresh_build\" | \"allow_expired_cache\";\n\nexport interface ChatMessageOptions {\n message: string;\n /** Files to upload and attach. The SDK handles uploading them first. */\n files?: (File | FileInput)[];\n /** Pre-uploaded file references (already uploaded via getFileUploadUrl). Skips upload. */\n uploadedFiles?: UnscopedFile[];\n /** Enable plan mode: the agent discusses and plans without editing code. */\n planMode?: boolean;\n customModel?: CustomModelConfig;\n /**\n * When true, the server may skip staggered inference racing for that request if your\n * custom model endpoint supports it; otherwise the request fails with a validation error.\n * API key authentication only. Sent as `custom_model_disable_race` in the JSON body.\n */\n customModelDisableRace?: boolean;\n /**\n * Override continuation behavior for this message.\n * Controls whether the agent reuses the prompt cache or rebuilds from scratch.\n */\n continuation?: ContinuationOverride;\n}\n\nexport interface LovableClientOptions {\n /** API key for authentication (mutually exclusive with bearerToken) */\n apiKey?: string;\n /** Bearer token for OAuth authentication (mutually exclusive with apiKey) */\n bearerToken?: string;\n baseUrl?: string;\n /** Additional headers to include on every request */\n headers?: Record<string, string>;\n /**\n * Identifier for the originating client, sent as the `X-Client-Source` header.\n * Used by the API to tag audit logs and observability with the message origin.\n * Defaults to `\"sdk\"`. The Go API allowlist accepts `\"mcp\"`, `\"sdk\"`, `\"cli\"`.\n */\n clientSource?: string;\n}\n\nexport interface LovableError extends Error {\n status: number;\n type?: string;\n detail?: string;\n}\n\nexport interface WaitOptions {\n pollInterval?: number;\n timeout?: number;\n onProgress?: (project: ProjectResponse) => void;\n}\n\nexport interface DeploymentResponse {\n status: string;\n deployment_id?: string;\n url?: string;\n}\n\nexport interface ChatResponse {\n content: string;\n previewUrl: string;\n messageId: string;\n}\n\nexport interface ChatResponseOptions {\n timeout?: number;\n}\n\n// --- Trace types ---\n\nexport type TracePurpose = \"main_agent\" | \"codebase_rag\" | \"knowledge_rag\" | \"review\";\n\nexport interface TraceSpan {\n span_id: string;\n root_span_id?: string;\n span_parents?: unknown;\n span_name?: string;\n span_type?: string;\n purpose?: string;\n subpurpose?: string;\n created?: string;\n input?: unknown;\n output?: unknown;\n error?: unknown;\n metadata?: unknown;\n metrics?: unknown;\n scores?: unknown;\n tags?: unknown;\n}\n\nexport interface MessageTracesResponse {\n message_id: string;\n braintrust_span_id: string;\n root_span_id: string;\n response_message_id?: string;\n spans: TraceSpan[];\n}\n\nexport interface GetMessageTracesOptions {\n purposes?: TracePurpose[];\n}\n\nexport interface TraceQuery {\n projectId: string;\n messageId: string;\n}\n\nexport interface BatchTracesResult {\n traces: Map<string, MessageTracesResponse>;\n errors: Map<string, Error>;\n}\n\n// --- On-demand review replay (`/v1/_dev/.../reviews/replay`) — typed in SDK only ---\n\nexport interface ReplayReviewsItemInput {\n /** Assistant (AI) message id for the turn to score */\n response_message_id: string;\n /** Optional per-item reviewer types */\n reviewer_types?: string[] | null;\n}\n\nexport interface ReplayReviewsRequestBody {\n items: ReplayReviewsItemInput[];\n /** Defaults to project_success_v3 and user_sentiment when omitted */\n reviewer_types?: string[] | null;\n /** Parallel items (default 4, max 16) */\n concurrency?: number;\n}\n\nexport interface ReviewerRunResult {\n reviewer_type: string;\n score?: number;\n message?: string;\n model?: string;\n published_rubric_rows: number;\n error?: string;\n}\n\nexport interface ReplayOutcome {\n response_message_id: string;\n user_message_id: string;\n results: ReviewerRunResult[] | null;\n}\n\nexport interface BatchItemOutcome {\n response_message_id: string;\n ok: boolean;\n error?: string;\n replay?: ReplayOutcome;\n}\n\nexport interface BatchReplayReviewsOutcome {\n items: BatchItemOutcome[];\n}\n\nexport interface ReplayReviewsResponse {\n results: BatchReplayReviewsOutcome;\n}\n\n// --- Remix types ---\n\nexport type RemixJobStatus = \"unknown\" | \"preparing\" | \"running\" | \"completed\" | \"error\";\n\nexport type RemixJobStep =\n | \"starting\"\n | \"creating_new_project\"\n | \"restoring_supabase\"\n | \"copying_history\"\n | \"preparing_repository\"\n | \"remixing_integration\"\n | \"pushing_repository\"\n | \"finalizing\"\n | \"completed\";\n\nexport interface RemixJobStepInfo {\n step: RemixJobStep;\n integration_name?: string;\n status?: string;\n}\n\nexport type RemixMode = \"before\" | \"including\";\n\nexport interface RemixProjectOptions {\n workspaceId: string;\n messageId?: string;\n remixMode?: RemixMode;\n includeHistory?: boolean;\n includeCustomKnowledge?: boolean;\n initialMessage?: string;\n projectName?: string;\n skipInitialRemixMessage?: boolean;\n skipIntegrations?: boolean;\n}\n\nexport interface RemixInitBody {\n workspace_id: string;\n message_id?: string;\n remix_mode?: RemixMode;\n include_history?: boolean;\n include_custom_knowledge?: boolean;\n initial_message?: ChatRequest;\n project_name?: string;\n skip_initial_remix_message?: boolean;\n skip_integrations?: boolean;\n}\n\nexport interface RemixInitResponse {\n job_id: string;\n}\n\nexport interface RemixProgressResult {\n project_id: string;\n}\n\nexport interface RemixProgressResponse {\n status: RemixJobStatus;\n step?: RemixJobStepInfo;\n error_message?: string;\n result?: RemixProgressResult;\n}\n\nexport interface RemixResult {\n projectId: string;\n}\n\nexport interface RemixWaitOptions {\n pollInterval?: number;\n timeout?: number;\n onProgress?: (status: RemixJobStatus, step?: RemixJobStepInfo) => void;\n}\n\n// --- Database types ---\n\nexport interface DatabaseStatus {\n enabled: boolean;\n stack?: string;\n}\n\nexport interface EnableDatabaseResult {\n enabled: boolean;\n stack: string;\n}\n\nexport interface DatabaseQueryResult {\n rows: Record<string, unknown>[] | null;\n}\n\n// --- API Error ---\n\nexport class ApiError extends Error {\n readonly status: number;\n readonly type?: string;\n readonly detail?: string;\n readonly props?: Record<string, unknown>;\n\n constructor(status: number, message: string, type?: string, detail?: string, props?: Record<string, unknown>) {\n super(message);\n this.status = status;\n this.type = type;\n this.detail = detail;\n this.props = props;\n }\n}\n\n// --- Send message response ---\n\nexport interface SendMessageResponse {\n message_id: string;\n status: string;\n}\n\n// --- Get message response ---\n\nexport interface GetMessageResponse {\n message_id: string;\n role: string;\n content: string;\n status: string;\n created_at: string;\n edit_id?: string;\n commit_sha?: string;\n summary?: string;\n cost_credits?: number;\n response?: {\n message_id: string;\n status: string;\n content: string;\n edit_id?: string;\n commit_sha?: string;\n summary?: string;\n cost_credits?: number;\n };\n queue_position?: number;\n queue_paused?: boolean;\n queue_pause_reason?: string;\n}\n\n// --- List messages response ---\n\nexport interface MessageSummary {\n message_id: string;\n role: string;\n content: string;\n status: string;\n created_at: string;\n edit_id?: string;\n}\n\nexport interface ListMessagesResponse {\n messages: MessageSummary[];\n has_more: boolean;\n}\n\nexport interface MessageCompletionResult {\n status: \"completed\" | \"timeout\" | \"error\";\n message_id: string;\n content: string;\n edit_id?: string;\n commit_sha?: string;\n summary?: string;\n cost_credits?: number;\n error?: string;\n}\n\nexport interface MessageCompletionOptions {\n /** Time between polls in ms (default: 3000) */\n pollInterval?: number;\n /** Maximum time to wait in ms (default: 600000 = 10 minutes) */\n timeout?: number;\n}\n\n// --- Knowledge ---\n\nexport interface KnowledgeResponse {\n content: string;\n}\n\n// --- File upload ---\n\nexport interface FileUploadUrlResponse {\n url: string;\n file_id: string;\n}\n\n// --- Git types ---\n\nexport interface DiffLine {\n type: string;\n content: string;\n}\n\nexport interface DiffHunk {\n oldStart: number;\n oldCount: number;\n newStart: number;\n newCount: number;\n lines: DiffLine[];\n}\n\nexport interface DiffEntry {\n action: string;\n file_path: string;\n original_file_path?: string;\n file_type?: string;\n is_image: boolean;\n is_incomplete?: boolean;\n hunks?: DiffHunk[];\n}\n\nexport interface GitDiffResponse {\n diffs: DiffEntry[];\n error?: string;\n}\n\nexport interface GitFileEntry {\n path: string;\n size: number;\n binary: boolean;\n}\n\nexport interface GitFilesResponse {\n files: GitFileEntry[];\n ref: string;\n}\n\n// --- Edits ---\n\nexport interface EditSummary {\n id: string;\n type: string;\n commit_sha?: string;\n commit_message?: string;\n status: string;\n created_at: string;\n}\n\nexport interface EditsResponse {\n edits: EditSummary[];\n has_more: boolean;\n}\n\n// --- List projects (expanded) ---\n\nexport interface ListProjectItem extends ProjectResponse {\n user_id?: string;\n is_template?: boolean;\n is_starred?: boolean;\n created_at?: string;\n updated_at?: string;\n last_edited_at?: string;\n last_viewed_at?: string;\n edit_count?: number;\n remix_count?: number;\n}\n\nexport interface ListProjectsResponse {\n projects: ListProjectItem[];\n total: number;\n has_more: boolean;\n}\n\nexport interface ListProjectsOptions {\n query?: string;\n visibility?: string;\n publish_status?: string;\n folder_id?: string;\n user_id?: string;\n sort_by?: string;\n sort_order?: string;\n viewed_by_me?: boolean;\n limit?: number;\n offset?: number;\n cursor?: string;\n}\n\n// --- Library & template projects ---\n\nexport interface LibraryProjectResponse {\n id: string;\n name: string | null;\n description: string;\n updated_at: string;\n}\n\nexport interface TemplateProjectResponse {\n id: string;\n name: string | null;\n description: string;\n updated_at: string;\n}\n\n// --- MCP servers ---\n\nexport interface ConnectorResponse {\n id: string;\n name: string;\n url?: string;\n auth_type: string;\n connector_id?: string;\n is_connected: boolean;\n}\n\nexport interface AvailableConnectorEntry {\n id: string;\n display_name: string;\n summary: string;\n category: string;\n requires_custom_url?: boolean;\n documentation_url?: string;\n}\n\nexport interface AddConnectorBody {\n name: string;\n url: string;\n auth_type: \"none\" | \"bearer_token\";\n connector_id?: string;\n token?: string;\n}\n\n// --- Connectors ---\n\nexport interface StandardConnectorItem {\n id: string;\n display_name: string;\n short_description: string;\n categories: string[];\n is_enabled_for_workspace: boolean;\n auth_type: string;\n logo_id?: string;\n documentation_url?: string;\n connection_scope?: string;\n}\n\nexport interface SeamlessConnectorItem {\n id: string;\n display_name: string;\n short_description: string;\n categories: string[];\n is_enabled_for_workspace: boolean;\n logo_id?: string;\n documentation_url?: string;\n}\n\nexport interface MCPConnectorItem {\n id: string;\n display_name: string;\n summary: string;\n status: string;\n is_enabled_for_workspace: boolean;\n is_connected: boolean;\n connector_id?: string;\n documentation_url?: string;\n}\n\nexport interface ConnectionItem {\n id: string;\n connector_id: string;\n display_name: string;\n is_managed: boolean;\n status: string;\n created_at: string;\n updated_at: string;\n}\n\n// --- Analytics ---\n\nexport interface TimeSeriesDataPoint {\n date: string;\n value: number;\n}\n\nexport interface TimeSeriesData {\n total: number;\n label: string;\n data: TimeSeriesDataPoint[];\n}\n\nexport interface ListDataPoint {\n label: string;\n value: number;\n}\n\nexport interface ListData {\n label: string;\n data: ListDataPoint[];\n}\n\nexport interface ProjectAnalyticsResponse {\n timeSeries: {\n visitors: TimeSeriesData;\n pageviews: TimeSeriesData;\n pageviewsPerVisit: TimeSeriesData;\n sessionDuration: TimeSeriesData;\n bounceRate: TimeSeriesData;\n };\n lists: {\n page: ListData;\n source: ListData;\n device: ListData;\n country: ListData;\n };\n}\n\nexport interface TrendDataPoint {\n time: string;\n visits: number;\n}\n\nexport interface ProjectAnalyticsTrendResponse {\n data: TrendDataPoint[];\n currentVisitors: number;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lovable.dev/sdk",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "TypeScript SDK for the Lovable API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,10 +15,15 @@
15
15
  "files": [
16
16
  "dist"
17
17
  ],
18
+ "dependencies": {
19
+ "openapi-fetch": "^0.14.0"
20
+ },
18
21
  "devDependencies": {
22
+ "openapi-typescript": "^7.10.1",
19
23
  "tsup": "^8.5.0",
20
24
  "typescript": "^5.9.3",
21
- "vitest": "^4.1.0"
25
+ "vitest": "^4.1.5",
26
+ "@lovable/api-utils": "1.0.0"
22
27
  },
23
28
  "publishConfig": {
24
29
  "access": "public"
@@ -31,8 +36,9 @@
31
36
  "typescript"
32
37
  ],
33
38
  "scripts": {
34
- "build": "tsup",
35
- "typecheck": "tsgo --noEmit",
39
+ "generate-paths": "node scripts/generate-public-paths.mjs",
40
+ "build": "pnpm run generate-paths && tsup",
41
+ "typecheck": "pnpm run generate-paths && tsgo --noEmit",
36
42
  "test": "vitest",
37
43
  "test:replay-reviews": "node scripts/test-replay-reviews.mjs",
38
44
  "publish:npm": "pnpm publish --access public"