@lovable.dev/sdk 0.1.4 → 0.1.5

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.d.ts CHANGED
@@ -93,6 +93,8 @@ interface ProjectResponse {
93
93
  visibility?: ProjectVisibility;
94
94
  publish_visibility?: "private" | "public";
95
95
  is_published?: boolean;
96
+ /** Present in create project response when initial_message was provided */
97
+ message_id?: string;
96
98
  is_starred?: boolean;
97
99
  is_template?: boolean;
98
100
  is_github?: boolean;
@@ -182,6 +184,10 @@ interface CreateProjectBody {
182
184
  visibility?: ProjectVisibility;
183
185
  template_project_id?: string;
184
186
  initial_message?: string;
187
+ files?: UnscopedFile[];
188
+ selected_libraries?: {
189
+ project_id: string;
190
+ }[];
185
191
  category?: string;
186
192
  project_type?: string;
187
193
  prompt_name?: string;
@@ -199,7 +205,14 @@ interface CreateProjectOptions {
199
205
  visibility?: ProjectVisibility;
200
206
  templateProjectId?: string;
201
207
  initialMessage?: string;
208
+ /** Files to upload and attach to the initial message. The SDK handles uploading. */
202
209
  files?: (File | FileInput)[];
210
+ /** Pre-uploaded file references to attach to the initial message. Skips upload. */
211
+ uploadedFiles?: UnscopedFile[];
212
+ /** Design system library projects to connect to the new project. */
213
+ selectedLibraries?: {
214
+ project_id: string;
215
+ }[];
203
216
  }
204
217
  interface InviteCollaboratorOptions {
205
218
  email: string;
@@ -217,8 +230,12 @@ interface InviteCollaboratorOptions {
217
230
  type ContinuationOverride = "force" | "fresh_build" | "allow_expired_cache";
218
231
  interface ChatMessageOptions {
219
232
  message: string;
233
+ /** Files to upload and attach. The SDK handles uploading them first. */
220
234
  files?: (File | FileInput)[];
221
- chatOnly?: boolean;
235
+ /** Pre-uploaded file references (already uploaded via getFileUploadUrl). Skips upload. */
236
+ uploadedFiles?: UnscopedFile[];
237
+ /** Enable plan mode: the agent discusses and plans without editing code. */
238
+ planMode?: boolean;
222
239
  customModel?: CustomModelConfig;
223
240
  /**
224
241
  * When true, the server may skip staggered inference racing for that request if your
@@ -233,8 +250,13 @@ interface ChatMessageOptions {
233
250
  continuation?: ContinuationOverride;
234
251
  }
235
252
  interface LovableClientOptions {
236
- apiKey: string;
253
+ /** API key for authentication (mutually exclusive with bearerToken) */
254
+ apiKey?: string;
255
+ /** Bearer token for OAuth authentication (mutually exclusive with apiKey) */
256
+ bearerToken?: string;
237
257
  baseUrl?: string;
258
+ /** Additional headers to include on every request */
259
+ headers?: Record<string, string>;
238
260
  }
239
261
  interface LovableError extends Error {
240
262
  status: number;
@@ -348,6 +370,7 @@ interface RemixProjectOptions {
348
370
  includeHistory?: boolean;
349
371
  includeCustomKnowledge?: boolean;
350
372
  initialMessage?: string;
373
+ projectName?: string;
351
374
  skipInitialRemixMessage?: boolean;
352
375
  skipIntegrations?: boolean;
353
376
  /** Copy agent continuation state so prompt cache can be reused after remix. Requires includeHistory. API-key auth only. */
@@ -381,12 +404,259 @@ interface DatabaseConnectionInfo {
381
404
  connection_string: string;
382
405
  api_url: string;
383
406
  }
407
+ declare class ApiError extends Error {
408
+ readonly status: number;
409
+ readonly type?: string;
410
+ readonly detail?: string;
411
+ constructor(status: number, message: string, type?: string, detail?: string);
412
+ }
413
+ interface SendMessageResponse {
414
+ message_id: string;
415
+ status: string;
416
+ }
417
+ interface GetMessageResponse {
418
+ message_id: string;
419
+ role: string;
420
+ content: string;
421
+ status: string;
422
+ created_at: string;
423
+ edit_id?: string;
424
+ commit_sha?: string;
425
+ summary?: string;
426
+ cost_credits?: number;
427
+ response?: {
428
+ message_id: string;
429
+ status: string;
430
+ content: string;
431
+ edit_id?: string;
432
+ commit_sha?: string;
433
+ summary?: string;
434
+ cost_credits?: number;
435
+ };
436
+ queue_position?: number;
437
+ queue_paused?: boolean;
438
+ queue_pause_reason?: string;
439
+ }
440
+ interface MessageCompletionResult {
441
+ status: "completed" | "timeout" | "error";
442
+ message_id: string;
443
+ content: string;
444
+ edit_id?: string;
445
+ commit_sha?: string;
446
+ summary?: string;
447
+ cost_credits?: number;
448
+ error?: string;
449
+ }
450
+ interface MessageCompletionOptions {
451
+ /** Time between polls in ms (default: 3000) */
452
+ pollInterval?: number;
453
+ /** Maximum time to wait in ms (default: 600000 = 10 minutes) */
454
+ timeout?: number;
455
+ }
456
+ interface KnowledgeResponse {
457
+ content: string;
458
+ }
459
+ interface FileUploadUrlResponse {
460
+ url: string;
461
+ file_id: string;
462
+ }
463
+ interface DiffLine {
464
+ type: string;
465
+ content: string;
466
+ }
467
+ interface DiffHunk {
468
+ oldStart: number;
469
+ oldCount: number;
470
+ newStart: number;
471
+ newCount: number;
472
+ lines: DiffLine[];
473
+ }
474
+ interface DiffEntry {
475
+ action: string;
476
+ file_path: string;
477
+ original_file_path?: string;
478
+ file_type?: string;
479
+ is_image: boolean;
480
+ is_incomplete?: boolean;
481
+ hunks?: DiffHunk[];
482
+ }
483
+ interface GitDiffResponse {
484
+ diffs: DiffEntry[];
485
+ error?: string;
486
+ }
487
+ interface GitFileEntry {
488
+ path: string;
489
+ size: number;
490
+ binary: boolean;
491
+ }
492
+ interface GitFilesResponse {
493
+ files: GitFileEntry[];
494
+ ref: string;
495
+ }
496
+ interface EditSummary {
497
+ id: string;
498
+ type: string;
499
+ commit_sha?: string;
500
+ commit_message?: string;
501
+ status: string;
502
+ created_at: string;
503
+ }
504
+ interface EditsResponse {
505
+ edits: EditSummary[];
506
+ has_more: boolean;
507
+ }
508
+ interface ListProjectItem extends ProjectResponse {
509
+ user_id?: string;
510
+ is_template?: boolean;
511
+ is_starred?: boolean;
512
+ created_at?: string;
513
+ updated_at?: string;
514
+ last_edited_at?: string;
515
+ last_viewed_at?: string;
516
+ edit_count?: number;
517
+ remix_count?: number;
518
+ }
519
+ interface ListProjectsResponse {
520
+ projects: ListProjectItem[];
521
+ total: number;
522
+ has_more: boolean;
523
+ }
524
+ interface ListProjectsOptions {
525
+ query?: string;
526
+ visibility?: string;
527
+ publish_status?: string;
528
+ folder_id?: string;
529
+ user_id?: string;
530
+ sort_by?: string;
531
+ sort_order?: string;
532
+ viewed_by_me?: boolean;
533
+ limit?: number;
534
+ offset?: number;
535
+ cursor?: string;
536
+ }
537
+ interface LibraryProjectResponse {
538
+ id: string;
539
+ name: string | null;
540
+ description: string;
541
+ updated_at: string;
542
+ }
543
+ interface TemplateProjectResponse {
544
+ id: string;
545
+ name: string | null;
546
+ description: string;
547
+ updated_at: string;
548
+ }
549
+ interface MCPServerResponse {
550
+ id: string;
551
+ name: string;
552
+ url?: string;
553
+ auth_type: string;
554
+ connector_id?: string;
555
+ is_connected: boolean;
556
+ }
557
+ interface MCPCatalogEntry {
558
+ id: string;
559
+ display_name: string;
560
+ summary: string;
561
+ category: string;
562
+ requires_custom_url?: boolean;
563
+ documentation_url?: string;
564
+ }
565
+ interface AddMCPServerBody {
566
+ name: string;
567
+ url: string;
568
+ auth_type: "none" | "bearer_token";
569
+ connector_id?: string;
570
+ token?: string;
571
+ }
572
+ interface StandardConnectorItem {
573
+ id: string;
574
+ display_name: string;
575
+ short_description: string;
576
+ categories: string[];
577
+ is_enabled_for_workspace: boolean;
578
+ auth_type: string;
579
+ logo_id?: string;
580
+ documentation_url?: string;
581
+ connection_scope?: string;
582
+ }
583
+ interface SeamlessConnectorItem {
584
+ id: string;
585
+ display_name: string;
586
+ short_description: string;
587
+ categories: string[];
588
+ is_enabled_for_workspace: boolean;
589
+ logo_id?: string;
590
+ documentation_url?: string;
591
+ }
592
+ interface MCPConnectorItem {
593
+ id: string;
594
+ display_name: string;
595
+ summary: string;
596
+ status: string;
597
+ is_enabled_for_workspace: boolean;
598
+ is_connected: boolean;
599
+ connector_id?: string;
600
+ documentation_url?: string;
601
+ }
602
+ interface ConnectionItem {
603
+ id: string;
604
+ connector_id: string;
605
+ display_name: string;
606
+ is_managed: boolean;
607
+ status: string;
608
+ created_at: string;
609
+ updated_at: string;
610
+ }
611
+ interface TimeSeriesDataPoint {
612
+ date: string;
613
+ value: number;
614
+ }
615
+ interface TimeSeriesData {
616
+ total: number;
617
+ label: string;
618
+ data: TimeSeriesDataPoint[];
619
+ }
620
+ interface ListDataPoint {
621
+ label: string;
622
+ value: number;
623
+ }
624
+ interface ListData {
625
+ label: string;
626
+ data: ListDataPoint[];
627
+ }
628
+ interface ProjectAnalyticsResponse {
629
+ timeSeries: {
630
+ visitors: TimeSeriesData;
631
+ pageviews: TimeSeriesData;
632
+ pageviewsPerVisit: TimeSeriesData;
633
+ sessionDuration: TimeSeriesData;
634
+ bounceRate: TimeSeriesData;
635
+ };
636
+ lists: {
637
+ page: ListData;
638
+ source: ListData;
639
+ device: ListData;
640
+ country: ListData;
641
+ };
642
+ }
643
+ interface TrendDataPoint {
644
+ time: string;
645
+ visits: number;
646
+ }
647
+ interface ProjectAnalyticsTrendResponse {
648
+ data: TrendDataPoint[];
649
+ currentVisitors: number;
650
+ }
384
651
 
385
652
  declare class LovableClient {
386
- private readonly apiKey;
653
+ private readonly authHeaders;
387
654
  private readonly baseUrl;
655
+ private readonly extraHeaders;
388
656
  constructor(options: LovableClientOptions);
657
+ private rawRequest;
389
658
  private request;
659
+ private requestText;
390
660
  /**
391
661
  * Get the current authenticated user and their workspaces.
392
662
  * Useful for validating an API key and discovering workspace IDs.
@@ -401,24 +671,23 @@ declare class LovableClient {
401
671
  */
402
672
  getWorkspace(workspaceId: string): Promise<WorkspaceWithMembership>;
403
673
  /**
404
- * List projects in a workspace
674
+ * List projects in a workspace.
675
+ * Supports full-text search, filtering by visibility/publish status/folder/creator,
676
+ * and pagination via offset or cursor.
405
677
  */
406
- listProjects(workspaceId: string, options?: {
407
- limit?: number;
408
- cursor?: string;
409
- visibility?: "all" | "draft" | "personal" | "private" | "public" | "workspace";
410
- }): Promise<ProjectResponse[]>;
678
+ listProjects(workspaceId: string, options?: ListProjectsOptions): Promise<ListProjectsResponse>;
411
679
  /**
412
680
  * Create a new project in a workspace
413
681
  */
414
682
  createProject(workspaceId: string, options: CreateProjectOptions): Promise<ProjectResponse>;
415
683
  /**
416
- * Send a chat message to a project
684
+ * Send a chat message to a project.
417
685
  *
418
- * Note: This sends a message to the project's AI agent. The response is
419
- * asynchronous - the API accepts the message and processes it in the background.
686
+ * The API accepts the message and processes it in the background.
687
+ * Returns the message ID and status. Use `waitForMessageCompletion()`
688
+ * to poll for the AI response, or `waitForResponse()` for SSE streaming.
420
689
  */
421
- chat(projectId: string, options: ChatMessageOptions): Promise<void>;
690
+ chat(projectId: string, options: ChatMessageOptions): Promise<SendMessageResponse>;
422
691
  /**
423
692
  * Invite a user to a workspace as a collaborator
424
693
  */
@@ -493,6 +762,108 @@ declare class LovableClient {
493
762
  * @returns Database connection details
494
763
  */
495
764
  getDatabaseConnectionInfo(projectId: string): Promise<DatabaseConnectionInfo>;
765
+ /**
766
+ * Get a message by ID. Returns the message content, status, and (for user messages)
767
+ * the AI response if available. Use to poll for completion after `chat()`.
768
+ */
769
+ getMessage(projectId: string, messageId: string): Promise<GetMessageResponse>;
770
+ /**
771
+ * Poll for message completion. Waits until the AI response reaches a terminal
772
+ * status (completed, stopped) or the timeout expires.
773
+ *
774
+ * Handles edge cases: 404 grace period for race conditions between queue
775
+ * dequeue and event creation, and queue pause detection.
776
+ */
777
+ waitForMessageCompletion(projectId: string, messageId: string, options?: MessageCompletionOptions): Promise<MessageCompletionResult>;
778
+ /** Get workspace knowledge (custom instructions for the AI agent). */
779
+ getWorkspaceKnowledge(workspaceId: string): Promise<KnowledgeResponse>;
780
+ /** Set workspace knowledge. Max 10,000 characters. */
781
+ setWorkspaceKnowledge(workspaceId: string, content: string): Promise<KnowledgeResponse>;
782
+ /** Get project knowledge (custom instructions for the AI agent). */
783
+ getProjectKnowledge(projectId: string): Promise<KnowledgeResponse>;
784
+ /** Set project knowledge. Max 10,000 characters. */
785
+ setProjectKnowledge(projectId: string, content: string): Promise<KnowledgeResponse>;
786
+ /**
787
+ * Get the structured diff for a message or commit.
788
+ * Pass `messageId` to get the diff for a specific AI message,
789
+ * or `sha` for a specific commit.
790
+ */
791
+ getDiff(projectId: string, params: {
792
+ messageId?: string;
793
+ sha?: string;
794
+ baseSha?: string;
795
+ }): Promise<GitDiffResponse>;
796
+ /** List all files in a project at a specific git ref. */
797
+ listFiles(projectId: string, ref: string): Promise<GitFilesResponse>;
798
+ /** Read the raw content of a single file at a specific git ref. Returns text. */
799
+ readFile(projectId: string, path: string, ref: string): Promise<string>;
800
+ /** List the edit history of a project. */
801
+ listEdits(projectId: string, params?: {
802
+ limit?: number;
803
+ before?: string;
804
+ }): Promise<EditsResponse>;
805
+ /** Get a presigned URL for uploading a file. Returns the upload URL and file ID. */
806
+ getFileUploadUrl(params: {
807
+ file_name: string;
808
+ content_type?: string;
809
+ }): Promise<FileUploadUrlResponse>;
810
+ /** Set a project's visibility (draft, private, or public). */
811
+ setProjectVisibility(projectId: string, visibility: string): Promise<{
812
+ visibility: string;
813
+ }>;
814
+ /** Set a folder's visibility (personal or workspace). */
815
+ setFolderVisibility(workspaceId: string, folderId: string, visibility: string): Promise<{
816
+ visibility: string;
817
+ }>;
818
+ /** List available design system library projects in a workspace. */
819
+ listLibraryProjects(workspaceId: string): Promise<{
820
+ libraries: LibraryProjectResponse[];
821
+ }>;
822
+ /** List available template projects in a workspace. */
823
+ listTemplateProjects(workspaceId: string): Promise<{
824
+ templates: TemplateProjectResponse[];
825
+ }>;
826
+ /** List all MCP servers connected to a workspace. */
827
+ listMCPServers(workspaceId: string): Promise<{
828
+ servers: MCPServerResponse[];
829
+ }>;
830
+ /** Add an MCP server to a workspace. The server URL is tested before saving. */
831
+ addMCPServer(workspaceId: string, body: AddMCPServerBody): Promise<MCPServerResponse>;
832
+ /** Remove an MCP server from a workspace. */
833
+ removeMCPServer(workspaceId: string, serverId: string): Promise<{
834
+ success: boolean;
835
+ }>;
836
+ /** Browse available MCP server templates (catalog). */
837
+ listMCPCatalog(workspaceId: string): Promise<{
838
+ catalog: MCPCatalogEntry[];
839
+ }>;
840
+ /** List standard (OAuth-based) connectors in a workspace. */
841
+ listStandardConnectors(workspaceId: string): Promise<{
842
+ connectors: StandardConnectorItem[];
843
+ }>;
844
+ /** List seamless (zero-config) connectors in a workspace. */
845
+ listSeamlessConnectors(workspaceId: string): Promise<{
846
+ connectors: SeamlessConnectorItem[];
847
+ }>;
848
+ /** List MCP connectors in a workspace. */
849
+ listMCPConnectors(workspaceId: string): Promise<{
850
+ connectors: MCPConnectorItem[];
851
+ custom_enabled: boolean;
852
+ }>;
853
+ /** List authenticated connections (accounts) in a workspace. */
854
+ listConnections(workspaceId: string, params?: {
855
+ connector_id?: string;
856
+ }): Promise<{
857
+ connections: ConnectionItem[];
858
+ }>;
859
+ /** Get historical analytics for a published project. */
860
+ getProjectAnalytics(projectId: string, params: {
861
+ startDate: string;
862
+ endDate: string;
863
+ granularity?: string;
864
+ }): Promise<ProjectAnalyticsResponse>;
865
+ /** Get real-time visitor trend for a published project. */
866
+ getProjectAnalyticsTrend(projectId: string): Promise<ProjectAnalyticsTrendResponse>;
496
867
  /**
497
868
  * Publish a project.
498
869
  *
@@ -628,4 +999,4 @@ declare class LovableClient {
628
999
  waitForProjectPublished(projectId: string, options?: WaitOptions): Promise<ProjectResponse>;
629
1000
  }
630
1001
 
631
- export { type AddUserToWorkspaceInputBody, type BatchItemOutcome, type BatchReplayReviewsOutcome, type BatchTracesResult, type ChatMessageOptions, type ChatRequest, type ChatResponse, type ChatResponseOptions, type ContinuationOverride, type CreateProjectBody, type CreateProjectOptions, type CustomModelConfig, type DatabaseConnectionInfo, type DatabaseQueryResult, type DatabaseStatus, type DeploymentResponse, type EnableDatabaseResult, type FileInput, type GetMessageTracesOptions, type InviteCollaboratorOptions, LovableClient, type LovableClientOptions, type LovableError, type MeResponse, type MeWorkspace, type MemberRole, type MessageTracesResponse, type ProjectResponse, type ProjectStatus, type ProjectVisibility, type RemixJobStatus, type RemixJobStep, type RemixJobStepInfo, type RemixMode, type RemixProjectOptions, type RemixResult, type RemixWaitOptions, type ReplayOutcome, type ReplayReviewsItemInput, type ReplayReviewsRequestBody, type ReplayReviewsResponse, type ReviewerRunResult, type TracePurpose, type TraceQuery, type TraceSpan, type UnscopedFile, type WaitOptions, type WorkspaceMembership, type WorkspaceMembershipResponse, type WorkspaceWithMembership };
1002
+ export { type AddMCPServerBody, type AddUserToWorkspaceInputBody, ApiError, type BatchItemOutcome, type BatchReplayReviewsOutcome, type BatchTracesResult, type ChatMessageOptions, type ChatRequest, type ChatResponse, type ChatResponseOptions, type ConnectionItem, type ContinuationOverride, type CreateProjectBody, type CreateProjectOptions, type CustomModelConfig, type DatabaseConnectionInfo, type DatabaseQueryResult, type DatabaseStatus, type DeploymentResponse, type DiffEntry, type DiffHunk, type DiffLine, type EditSummary, type EditsResponse, type EnableDatabaseResult, type FileInput, type FileUploadUrlResponse, type GetMessageResponse, type GetMessageTracesOptions, type GitDiffResponse, type GitFileEntry, type GitFilesResponse, type InviteCollaboratorOptions, type KnowledgeResponse, type LibraryProjectResponse, type ListData, type ListDataPoint, type ListProjectItem, type ListProjectsOptions, type ListProjectsResponse, LovableClient, type LovableClientOptions, type LovableError, type MCPCatalogEntry, type MCPConnectorItem, type MCPServerResponse, type MeResponse, type MeWorkspace, type MemberRole, type MessageCompletionOptions, type MessageCompletionResult, type MessageTracesResponse, type ProjectAnalyticsResponse, type ProjectAnalyticsTrendResponse, type ProjectResponse, type ProjectStatus, type ProjectVisibility, type RemixJobStatus, type RemixJobStep, type RemixJobStepInfo, type RemixMode, type RemixProjectOptions, type RemixResult, type RemixWaitOptions, type ReplayOutcome, type ReplayReviewsItemInput, type ReplayReviewsRequestBody, type ReplayReviewsResponse, type ReviewerRunResult, type SeamlessConnectorItem, type SendMessageResponse, type StandardConnectorItem, type TemplateProjectResponse, type TimeSeriesData, type TimeSeriesDataPoint, type TracePurpose, type TraceQuery, type TraceSpan, type TrendDataPoint, type UnscopedFile, type WaitOptions, type WorkspaceMembership, type WorkspaceMembershipResponse, type WorkspaceWithMembership };
package/dist/index.js CHANGED
@@ -1,3 +1,16 @@
1
+ // src/types.ts
2
+ var ApiError = class extends Error {
3
+ status;
4
+ type;
5
+ detail;
6
+ constructor(status, message, type, detail) {
7
+ super(message);
8
+ this.status = status;
9
+ this.type = type;
10
+ this.detail = detail;
11
+ }
12
+ };
13
+
1
14
  // src/client.ts
2
15
  var DEFAULT_BASE_URL = "https://api.lovable.dev";
3
16
  function normalizeBaseUrl(url) {
@@ -9,25 +22,36 @@ function normalizeBaseUrl(url) {
9
22
  return normalized;
10
23
  }
11
24
  var LovableClient = class {
12
- apiKey;
25
+ authHeaders;
13
26
  baseUrl;
27
+ extraHeaders;
14
28
  constructor(options) {
15
- if (!options.apiKey) {
16
- throw new Error("API key is required");
29
+ const hasApiKey = !!options.apiKey;
30
+ const hasBearerToken = !!options.bearerToken;
31
+ if (!hasApiKey && !hasBearerToken) {
32
+ throw new Error("Either apiKey or bearerToken is required");
33
+ }
34
+ if (hasApiKey && hasBearerToken) {
35
+ throw new Error("Provide either apiKey or bearerToken, not both");
17
36
  }
18
- this.apiKey = options.apiKey;
37
+ this.authHeaders = hasApiKey ? { "Lovable-API-Key": options.apiKey } : { Authorization: `Bearer ${options.bearerToken}` };
19
38
  this.baseUrl = normalizeBaseUrl(options.baseUrl);
39
+ this.extraHeaders = options.headers ?? {};
20
40
  }
21
- async request(method, path, body) {
41
+ async rawRequest(method, path, body) {
22
42
  const url = `${this.baseUrl}${path}`;
23
43
  const headers = {
24
- "Lovable-API-Key": this.apiKey,
25
- "Content-Type": "application/json"
44
+ ...this.authHeaders,
45
+ ...this.extraHeaders,
46
+ Accept: "application/json"
26
47
  };
48
+ if (body !== void 0) {
49
+ headers["Content-Type"] = "application/json";
50
+ }
27
51
  const response = await fetch(url, {
28
52
  method,
29
53
  headers,
30
- body: body ? JSON.stringify(body) : void 0
54
+ body: body !== void 0 ? JSON.stringify(body) : void 0
31
55
  });
32
56
  if (!response.ok) {
33
57
  let errorBody;
@@ -35,17 +59,22 @@ var LovableClient = class {
35
59
  errorBody = await response.json();
36
60
  } catch {
37
61
  }
38
- const error = new Error(errorBody?.title ?? `HTTP ${response.status}: ${response.statusText}`);
39
- error.status = response.status;
40
- error.type = errorBody?.title;
41
- error.detail = errorBody?.detail;
42
- throw error;
62
+ const message = errorBody?.message ?? errorBody?.title ?? `HTTP ${response.status}: ${response.statusText}`;
63
+ throw new ApiError(response.status, message, errorBody?.title, errorBody?.detail);
43
64
  }
65
+ return response;
66
+ }
67
+ async request(method, path, body) {
68
+ const response = await this.rawRequest(method, path, body);
44
69
  if (response.status === 204) {
45
70
  return void 0;
46
71
  }
47
72
  return response.json();
48
73
  }
74
+ async requestText(method, path) {
75
+ const response = await this.rawRequest(method, path);
76
+ return response.text();
77
+ }
49
78
  /**
50
79
  * Get the current authenticated user and their workspaces.
51
80
  * Useful for validating an API key and discovering workspace IDs.
@@ -68,25 +97,36 @@ var LovableClient = class {
68
97
  return response.workspace;
69
98
  }
70
99
  /**
71
- * List projects in a workspace
100
+ * List projects in a workspace.
101
+ * Supports full-text search, filtering by visibility/publish status/folder/creator,
102
+ * and pagination via offset or cursor.
72
103
  */
73
104
  async listProjects(workspaceId, options) {
74
105
  const params = new URLSearchParams();
75
- if (options?.limit) params.set("limit", options.limit.toString());
76
- if (options?.cursor) params.set("cursor", options.cursor);
106
+ if (options?.query) params.set("q", options.query);
77
107
  if (options?.visibility) params.set("visibility", options.visibility);
108
+ if (options?.publish_status) params.set("publish_status", options.publish_status);
109
+ if (options?.folder_id) params.set("folder_id", options.folder_id);
110
+ if (options?.user_id) params.set("user_id", options.user_id);
111
+ if (options?.sort_by) params.set("sort_by", options.sort_by);
112
+ if (options?.sort_order) params.set("sort_order", options.sort_order);
113
+ if (options?.viewed_by_me) params.set("viewed_by_me", "true");
114
+ if (options?.limit !== void 0) params.set("limit", String(options.limit));
115
+ if (options?.offset !== void 0) params.set("offset", String(options.offset));
116
+ if (options?.cursor) params.set("cursor", options.cursor);
78
117
  const query = params.toString();
79
118
  const path = `/v1/workspaces/${workspaceId}/projects${query ? `?${query}` : ""}`;
80
- const response = await this.request("GET", path);
81
- return response.projects ?? [];
119
+ return this.request("GET", path);
82
120
  }
83
121
  /**
84
122
  * Create a new project in a workspace
85
123
  */
86
124
  async createProject(workspaceId, options) {
87
- let uploadedFiles;
88
- if (options.files?.length) {
89
- uploadedFiles = await this.uploadFiles(options.files);
125
+ let fileRefs;
126
+ if (options.uploadedFiles?.length) {
127
+ fileRefs = options.uploadedFiles;
128
+ } else if (options.files?.length) {
129
+ fileRefs = await this.uploadFiles(options.files);
90
130
  }
91
131
  const body = {
92
132
  description: options.description,
@@ -96,39 +136,40 @@ var LovableClient = class {
96
136
  if (options.techStack) {
97
137
  body.tech_stack = options.techStack;
98
138
  }
99
- const hasFiles = uploadedFiles && uploadedFiles.length > 0;
100
- if (options.initialMessage && !hasFiles) {
139
+ if (options.selectedLibraries?.length) {
140
+ body.selected_libraries = options.selectedLibraries;
141
+ }
142
+ if (options.initialMessage) {
101
143
  body.initial_message = options.initialMessage;
102
144
  }
103
- const project = await this.request("POST", `/v1/workspaces/${workspaceId}/projects`, body);
104
- if (hasFiles && options.initialMessage) {
105
- const chatBody = {
106
- message: options.initialMessage,
107
- files: uploadedFiles
108
- };
109
- await this.request("POST", `/v1/projects/${project.id}/messages`, chatBody);
145
+ if (fileRefs?.length) {
146
+ body.files = fileRefs;
110
147
  }
148
+ const project = await this.request("POST", `/v1/workspaces/${workspaceId}/projects`, body);
111
149
  return project;
112
150
  }
113
151
  /**
114
- * Send a chat message to a project
152
+ * Send a chat message to a project.
115
153
  *
116
- * Note: This sends a message to the project's AI agent. The response is
117
- * asynchronous - the API accepts the message and processes it in the background.
154
+ * The API accepts the message and processes it in the background.
155
+ * Returns the message ID and status. Use `waitForMessageCompletion()`
156
+ * to poll for the AI response, or `waitForResponse()` for SSE streaming.
118
157
  */
119
158
  async chat(projectId, options) {
120
- let uploadedFiles;
121
- if (options.files?.length) {
122
- uploadedFiles = await this.uploadFiles(options.files);
159
+ let fileRefs;
160
+ if (options.uploadedFiles?.length) {
161
+ fileRefs = options.uploadedFiles;
162
+ } else if (options.files?.length) {
163
+ fileRefs = await this.uploadFiles(options.files);
123
164
  }
124
165
  const body = {
125
166
  message: options.message
126
167
  };
127
- if (uploadedFiles) {
128
- body.files = uploadedFiles;
168
+ if (fileRefs) {
169
+ body.files = fileRefs;
129
170
  }
130
- if (options.chatOnly) {
131
- body.chat_only = true;
171
+ if (options.planMode) {
172
+ body.plan_mode = true;
132
173
  }
133
174
  if (options.customModel) {
134
175
  body.custom_model_endpoint = options.customModel.endpoint;
@@ -141,7 +182,7 @@ var LovableClient = class {
141
182
  if (options.continuation) {
142
183
  body.continuation = options.continuation;
143
184
  }
144
- await this.request("POST", `/v1/projects/${projectId}/messages`, body);
185
+ return this.request("POST", `/v1/projects/${projectId}/messages`, body);
145
186
  }
146
187
  /**
147
188
  * Invite a user to a workspace as a collaborator
@@ -243,6 +284,257 @@ var LovableClient = class {
243
284
  async getDatabaseConnectionInfo(projectId) {
244
285
  return this.request("GET", `/v1/projects/${projectId}/database/connection-info`);
245
286
  }
287
+ // ---------------------------------------------------------------------------
288
+ // Messages
289
+ // ---------------------------------------------------------------------------
290
+ /**
291
+ * Get a message by ID. Returns the message content, status, and (for user messages)
292
+ * the AI response if available. Use to poll for completion after `chat()`.
293
+ */
294
+ async getMessage(projectId, messageId) {
295
+ return this.request("GET", `/v1/projects/${projectId}/messages/${messageId}`);
296
+ }
297
+ /**
298
+ * Poll for message completion. Waits until the AI response reaches a terminal
299
+ * status (completed, stopped) or the timeout expires.
300
+ *
301
+ * Handles edge cases: 404 grace period for race conditions between queue
302
+ * dequeue and event creation, and queue pause detection.
303
+ */
304
+ async waitForMessageCompletion(projectId, messageId, options) {
305
+ const pollInterval = options?.pollInterval ?? 3e3;
306
+ const timeout = options?.timeout ?? 6e5;
307
+ const deadline = Date.now() + timeout;
308
+ let notFoundSince = null;
309
+ const notFoundGraceMs = 15e3;
310
+ while (Date.now() < deadline) {
311
+ try {
312
+ const msg = await this.getMessage(projectId, messageId);
313
+ notFoundSince = null;
314
+ if (msg.status === "queued") {
315
+ if (msg.queue_paused && msg.queue_pause_reason !== "hitl_tool") {
316
+ return {
317
+ status: "error",
318
+ message_id: messageId,
319
+ content: "",
320
+ error: `Message is queued (position ${msg.queue_position ?? "unknown"}) but the queue is paused` + (msg.queue_pause_reason ? ` (reason: ${msg.queue_pause_reason})` : "") + `. Unpause the queue in the Lovable editor, or use wait=false to return immediately.`
321
+ };
322
+ }
323
+ await sleep(pollInterval);
324
+ continue;
325
+ }
326
+ const ai = msg.response;
327
+ if (ai) {
328
+ if (ai.status === "completed" || ai.status === "stopped") {
329
+ return {
330
+ status: ai.status === "completed" ? "completed" : "error",
331
+ message_id: ai.message_id,
332
+ content: ai.content,
333
+ edit_id: ai.edit_id,
334
+ commit_sha: ai.commit_sha,
335
+ summary: ai.summary,
336
+ cost_credits: ai.cost_credits
337
+ };
338
+ }
339
+ }
340
+ if (msg.role === "assistant") {
341
+ if (msg.status === "completed" || msg.status === "stopped") {
342
+ return {
343
+ status: msg.status === "completed" ? "completed" : "error",
344
+ message_id: msg.message_id,
345
+ content: msg.content,
346
+ edit_id: msg.edit_id,
347
+ commit_sha: msg.commit_sha,
348
+ summary: msg.summary,
349
+ cost_credits: msg.cost_credits
350
+ };
351
+ }
352
+ }
353
+ await sleep(pollInterval);
354
+ } catch (err) {
355
+ if (err instanceof ApiError && err.status === 404) {
356
+ if (notFoundSince === null) {
357
+ notFoundSince = Date.now();
358
+ }
359
+ if (Date.now() - notFoundSince < notFoundGraceMs) {
360
+ await sleep(pollInterval);
361
+ continue;
362
+ }
363
+ return {
364
+ status: "error",
365
+ message_id: messageId,
366
+ content: "",
367
+ error: "Message not found. It may have been deleted from the queue."
368
+ };
369
+ }
370
+ await sleep(pollInterval);
371
+ }
372
+ }
373
+ return {
374
+ status: "timeout",
375
+ message_id: messageId,
376
+ content: "",
377
+ error: `Agent did not finish within ${timeout / 1e3}s`
378
+ };
379
+ }
380
+ // ---------------------------------------------------------------------------
381
+ // Knowledge
382
+ // ---------------------------------------------------------------------------
383
+ /** Get workspace knowledge (custom instructions for the AI agent). */
384
+ async getWorkspaceKnowledge(workspaceId) {
385
+ return this.request("GET", `/v1/workspaces/${workspaceId}/knowledge`);
386
+ }
387
+ /** Set workspace knowledge. Max 10,000 characters. */
388
+ async setWorkspaceKnowledge(workspaceId, content) {
389
+ return this.request("PUT", `/v1/workspaces/${workspaceId}/knowledge`, { content });
390
+ }
391
+ /** Get project knowledge (custom instructions for the AI agent). */
392
+ async getProjectKnowledge(projectId) {
393
+ return this.request("GET", `/v1/projects/${projectId}/knowledge`);
394
+ }
395
+ /** Set project knowledge. Max 10,000 characters. */
396
+ async setProjectKnowledge(projectId, content) {
397
+ return this.request("PUT", `/v1/projects/${projectId}/knowledge`, { content });
398
+ }
399
+ // ---------------------------------------------------------------------------
400
+ // Git operations
401
+ // ---------------------------------------------------------------------------
402
+ /**
403
+ * Get the structured diff for a message or commit.
404
+ * Pass `messageId` to get the diff for a specific AI message,
405
+ * or `sha` for a specific commit.
406
+ */
407
+ async getDiff(projectId, params) {
408
+ const qs = new URLSearchParams();
409
+ if (params.messageId) qs.set("message_id", params.messageId);
410
+ if (params.sha) qs.set("sha", params.sha);
411
+ if (params.baseSha) qs.set("base_sha", params.baseSha);
412
+ return this.request("GET", `/v1/projects/${projectId}/git/diff?${qs.toString()}`);
413
+ }
414
+ /** List all files in a project at a specific git ref. */
415
+ async listFiles(projectId, ref) {
416
+ const qs = new URLSearchParams({ ref });
417
+ return this.request("GET", `/v1/projects/${projectId}/git/files?${qs.toString()}`);
418
+ }
419
+ /** Read the raw content of a single file at a specific git ref. Returns text. */
420
+ async readFile(projectId, path, ref) {
421
+ const qs = new URLSearchParams({ path, ref });
422
+ return this.requestText("GET", `/v1/projects/${projectId}/git/file?${qs.toString()}`);
423
+ }
424
+ // ---------------------------------------------------------------------------
425
+ // Edits
426
+ // ---------------------------------------------------------------------------
427
+ /** List the edit history of a project. */
428
+ async listEdits(projectId, params) {
429
+ const qs = new URLSearchParams();
430
+ if (params?.limit !== void 0) qs.set("limit", String(params.limit));
431
+ if (params?.before) qs.set("before", params.before);
432
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
433
+ return this.request("GET", `/v1/projects/${projectId}/edits${suffix}`);
434
+ }
435
+ // ---------------------------------------------------------------------------
436
+ // File upload
437
+ // ---------------------------------------------------------------------------
438
+ /** Get a presigned URL for uploading a file. Returns the upload URL and file ID. */
439
+ async getFileUploadUrl(params) {
440
+ return this.request("POST", "/v1/files/upload-url", params);
441
+ }
442
+ // ---------------------------------------------------------------------------
443
+ // Visibility
444
+ // ---------------------------------------------------------------------------
445
+ /** Set a project's visibility (draft, private, or public). */
446
+ async setProjectVisibility(projectId, visibility) {
447
+ return this.request("PUT", `/v1/projects/${projectId}/visibility`, { visibility });
448
+ }
449
+ /** Set a folder's visibility (personal or workspace). */
450
+ async setFolderVisibility(workspaceId, folderId, visibility) {
451
+ return this.request("PUT", `/v1/workspaces/${workspaceId}/folders/${folderId}/visibility`, {
452
+ visibility
453
+ });
454
+ }
455
+ // ---------------------------------------------------------------------------
456
+ // Library & template projects
457
+ // ---------------------------------------------------------------------------
458
+ /** List available design system library projects in a workspace. */
459
+ async listLibraryProjects(workspaceId) {
460
+ return this.request(
461
+ "GET",
462
+ `/v1/workspaces/${workspaceId}/available-library-projects`
463
+ );
464
+ }
465
+ /** List available template projects in a workspace. */
466
+ async listTemplateProjects(workspaceId) {
467
+ return this.request(
468
+ "GET",
469
+ `/v1/workspaces/${workspaceId}/available-template-projects`
470
+ );
471
+ }
472
+ // ---------------------------------------------------------------------------
473
+ // MCP servers
474
+ // ---------------------------------------------------------------------------
475
+ /** List all MCP servers connected to a workspace. */
476
+ async listMCPServers(workspaceId) {
477
+ return this.request("GET", `/v1/workspaces/${workspaceId}/mcp-servers`);
478
+ }
479
+ /** Add an MCP server to a workspace. The server URL is tested before saving. */
480
+ async addMCPServer(workspaceId, body) {
481
+ return this.request("POST", `/v1/workspaces/${workspaceId}/mcp-servers`, body);
482
+ }
483
+ /** Remove an MCP server from a workspace. */
484
+ async removeMCPServer(workspaceId, serverId) {
485
+ return this.request("DELETE", `/v1/workspaces/${workspaceId}/mcp-servers/${serverId}`);
486
+ }
487
+ /** Browse available MCP server templates (catalog). */
488
+ async listMCPCatalog(workspaceId) {
489
+ return this.request("GET", `/v1/workspaces/${workspaceId}/mcp-catalog`);
490
+ }
491
+ // ---------------------------------------------------------------------------
492
+ // Connectors
493
+ // ---------------------------------------------------------------------------
494
+ /** List standard (OAuth-based) connectors in a workspace. */
495
+ async listStandardConnectors(workspaceId) {
496
+ return this.request(
497
+ "GET",
498
+ `/v1/workspaces/${workspaceId}/connectors/standard`
499
+ );
500
+ }
501
+ /** List seamless (zero-config) connectors in a workspace. */
502
+ async listSeamlessConnectors(workspaceId) {
503
+ return this.request(
504
+ "GET",
505
+ `/v1/workspaces/${workspaceId}/connectors/seamless`
506
+ );
507
+ }
508
+ /** List MCP connectors in a workspace. */
509
+ async listMCPConnectors(workspaceId) {
510
+ return this.request(
511
+ "GET",
512
+ `/v1/workspaces/${workspaceId}/connectors/mcp`
513
+ );
514
+ }
515
+ /** List authenticated connections (accounts) in a workspace. */
516
+ async listConnections(workspaceId, params) {
517
+ const qs = new URLSearchParams();
518
+ if (params?.connector_id) qs.set("connector_id", params.connector_id);
519
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
520
+ return this.request("GET", `/v1/workspaces/${workspaceId}/connections${suffix}`);
521
+ }
522
+ // ---------------------------------------------------------------------------
523
+ // Analytics
524
+ // ---------------------------------------------------------------------------
525
+ /** Get historical analytics for a published project. */
526
+ async getProjectAnalytics(projectId, params) {
527
+ const qs = new URLSearchParams({
528
+ startDate: params.startDate,
529
+ endDate: params.endDate
530
+ });
531
+ if (params.granularity) qs.set("granularity", params.granularity);
532
+ return this.request("GET", `/v1/projects/${projectId}/analytics?${qs.toString()}`);
533
+ }
534
+ /** Get real-time visitor trend for a published project. */
535
+ async getProjectAnalyticsTrend(projectId) {
536
+ return this.request("GET", `/v1/projects/${projectId}/analytics/trend`);
537
+ }
246
538
  /**
247
539
  * Publish a project.
248
540
  *
@@ -280,6 +572,7 @@ var LovableClient = class {
280
572
  workspace_id: options.workspaceId,
281
573
  include_history: options.includeHistory,
282
574
  include_custom_knowledge: options.includeCustomKnowledge,
575
+ project_name: options.projectName,
283
576
  skip_initial_remix_message: options.skipInitialRemixMessage,
284
577
  skip_integrations: options.skipIntegrations,
285
578
  include_agent_state: options.includeAgentState
@@ -389,14 +682,13 @@ var LovableClient = class {
389
682
  try {
390
683
  const response = await fetch(url, {
391
684
  headers: {
392
- "Lovable-API-Key": this.apiKey
685
+ ...this.authHeaders,
686
+ ...this.extraHeaders
393
687
  },
394
688
  signal: controller.signal
395
689
  });
396
690
  if (!response.ok) {
397
- const error = new Error(`Failed to connect to message stream: HTTP ${response.status}`);
398
- error.status = response.status;
399
- throw error;
691
+ throw new ApiError(response.status, `Failed to connect to message stream: HTTP ${response.status}`);
400
692
  }
401
693
  if (!response.body) {
402
694
  throw new Error("Response body is not readable");
@@ -609,6 +901,7 @@ function sleep(ms) {
609
901
  return new Promise((resolve) => setTimeout(resolve, ms));
610
902
  }
611
903
  export {
904
+ ApiError,
612
905
  LovableClient
613
906
  };
614
907
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import type {\n LovableClientOptions,\n LovableError,\n CreateProjectOptions,\n InviteCollaboratorOptions,\n ChatMessageOptions,\n WaitOptions,\n WorkspaceWithMembership,\n ProjectResponse,\n WorkspaceMembershipResponse,\n CreateProjectBody,\n AddUserToWorkspaceInputBody,\n GetWorkspacesResponse,\n GetWorkspaceProjectsResponse,\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} 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 apiKey: string;\n private readonly baseUrl: string;\n\n constructor(options: LovableClientOptions) {\n if (!options.apiKey) {\n throw new Error(\"API key is required\");\n }\n this.apiKey = options.apiKey;\n this.baseUrl = normalizeBaseUrl(options.baseUrl);\n }\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n\n const headers: Record<string, string> = {\n \"Lovable-API-Key\": this.apiKey,\n \"Content-Type\": \"application/json\",\n };\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n let errorBody: { title?: string; detail?: string } | undefined;\n try {\n errorBody = (await response.json()) as { title?: string; detail?: string };\n } catch {\n // Ignore JSON parse errors\n }\n\n const error = new Error(errorBody?.title ?? `HTTP ${response.status}: ${response.statusText}`) as LovableError;\n error.status = response.status;\n error.type = errorBody?.title;\n error.detail = errorBody?.detail;\n throw error;\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return response.json() as Promise<T>;\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 */\n async listProjects(\n workspaceId: string,\n options?: {\n limit?: number;\n cursor?: string;\n visibility?: \"all\" | \"draft\" | \"personal\" | \"private\" | \"public\" | \"workspace\";\n },\n ): Promise<ProjectResponse[]> {\n const params = new URLSearchParams();\n if (options?.limit) params.set(\"limit\", options.limit.toString());\n if (options?.cursor) params.set(\"cursor\", options.cursor);\n if (options?.visibility) params.set(\"visibility\", options.visibility);\n\n const query = params.toString();\n const path = `/v1/workspaces/${workspaceId}/projects${query ? `?${query}` : \"\"}`;\n\n const response = await this.request<GetWorkspaceProjectsResponse>(\"GET\", path);\n return response.projects ?? [];\n }\n\n /**\n * Create a new project in a workspace\n */\n async createProject(workspaceId: string, options: CreateProjectOptions): Promise<ProjectResponse> {\n let uploadedFiles: UnscopedFile[] | undefined;\n if (options.files?.length) {\n uploadedFiles = 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\n // The v1 create endpoint only accepts initial_message as a plain string,\n // so when files are attached we send the message via chat() after creation\n // to include the file references.\n const hasFiles = uploadedFiles && uploadedFiles.length > 0;\n if (options.initialMessage && !hasFiles) {\n body.initial_message = options.initialMessage;\n }\n\n const project = await this.request<ProjectResponse>(\"POST\", `/v1/workspaces/${workspaceId}/projects`, body);\n\n if (hasFiles && options.initialMessage) {\n const chatBody: { message: string; files?: UnscopedFile[] } = {\n message: options.initialMessage,\n files: uploadedFiles,\n };\n await this.request<void>(\"POST\", `/v1/projects/${project.id}/messages`, chatBody);\n }\n\n return project;\n }\n\n /**\n * Send a chat message to a project\n *\n * Note: This sends a message to the project's AI agent. The response is\n * asynchronous - the API accepts the message and processes it in the background.\n */\n async chat(projectId: string, options: ChatMessageOptions): Promise<void> {\n let uploadedFiles: UnscopedFile[] | undefined;\n if (options.files?.length) {\n uploadedFiles = await this.uploadFiles(options.files);\n }\n\n const body: {\n message: string;\n files?: UnscopedFile[];\n chat_only?: 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 (uploadedFiles) {\n body.files = uploadedFiles;\n }\n if (options.chatOnly) {\n body.chat_only = 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 await this.request<void>(\"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 * 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 skip_initial_remix_message: options.skipInitialRemixMessage,\n skip_integrations: options.skipIntegrations,\n include_agent_state: options.includeAgentState,\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\", `/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 `/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 \"Lovable-API-Key\": this.apiKey,\n },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const error = new Error(`Failed to connect to message stream: HTTP ${response.status}`) as LovableError;\n error.status = response.status;\n throw error;\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/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 = \"starting\" | \"creating_new_project\" | \"remixing_integration\" | \"finalizing\" | \"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 /** Copy agent continuation state so prompt cache can be reused after remix. Requires includeHistory. API-key auth only. */\n includeAgentState?: 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 include_agent_state?: 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\n constructor(status: number, message: string, type?: string, detail?: string) {\n super(message);\n this.status = status;\n this.type = type;\n this.detail = detail;\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 MCPServerResponse {\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 MCPCatalogEntry {\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 AddMCPServerBody {\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 MCPServerResponse,\n MCPCatalogEntry,\n AddMCPServerBody,\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: { title?: string; message?: string; detail?: string } | undefined;\n try {\n errorBody = (await response.json()) as { title?: string; message?: string; detail?: string };\n } catch {\n // Ignore JSON parse errors\n }\n\n const message = errorBody?.message ?? errorBody?.title ?? `HTTP ${response.status}: ${response.statusText}`;\n throw new ApiError(response.status, message, errorBody?.title, errorBody?.detail);\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 // MCP servers\n // ---------------------------------------------------------------------------\n\n /** List all MCP servers connected to a workspace. */\n async listMCPServers(workspaceId: string): Promise<{ servers: MCPServerResponse[] }> {\n return this.request<{ servers: MCPServerResponse[] }>(\"GET\", `/v1/workspaces/${workspaceId}/mcp-servers`);\n }\n\n /** Add an MCP server to a workspace. The server URL is tested before saving. */\n async addMCPServer(workspaceId: string, body: AddMCPServerBody): Promise<MCPServerResponse> {\n return this.request<MCPServerResponse>(\"POST\", `/v1/workspaces/${workspaceId}/mcp-servers`, body);\n }\n\n /** Remove an MCP server from a workspace. */\n async removeMCPServer(workspaceId: string, serverId: string): Promise<{ success: boolean }> {\n return this.request<{ success: boolean }>(\"DELETE\", `/v1/workspaces/${workspaceId}/mcp-servers/${serverId}`);\n }\n\n /** Browse available MCP server templates (catalog). */\n async listMCPCatalog(workspaceId: string): Promise<{ catalog: MCPCatalogEntry[] }> {\n return this.request<{ catalog: MCPCatalogEntry[] }>(\"GET\", `/v1/workspaces/${workspaceId}/mcp-catalog`);\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 include_agent_state: options.includeAgentState,\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\", `/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 `/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":[]}
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@lovable.dev/sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "TypeScript SDK for the Lovable API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
8
- "types": "dist/index.d.ts",
8
+ "types": "src/index.ts",
9
9
  "exports": {
10
10
  ".": {
11
- "import": "./dist/index.js",
12
- "types": "./dist/index.d.ts"
11
+ "types": "./src/index.ts",
12
+ "import": "./dist/index.js"
13
13
  }
14
14
  },
15
15
  "files": [