@lovable.dev/sdk 0.0.1

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/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # @lovable/sdk
2
+
3
+ TypeScript SDK for the Lovable API.
4
+
5
+ Currently in preview.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @lovable/sdk
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```typescript
16
+ import { LovableClient } from "@lovable/sdk";
17
+
18
+ const client = new LovableClient({
19
+ apiKey: "lov_your-api-key",
20
+ });
21
+
22
+ // List workspaces
23
+ const workspaces = await client.listWorkspaces();
24
+ console.log(workspaces);
25
+
26
+ // Create a project
27
+ const project = await client.createProject(workspaces[0].id, {
28
+ description: "A todo app with authentication",
29
+ visibility: "private",
30
+ initialMessage: "Create a simple todo app with user authentication",
31
+ });
32
+ console.log("Created project:", project.id);
33
+
34
+ // Wait for project to be ready (status = "completed")
35
+ const readyProject = await client.waitForProjectReady(project.id, {
36
+ onProgress: (p) => console.log(`Status: ${p.status}`),
37
+ });
38
+
39
+ // Send a chat message to an existing project
40
+ await client.chat(readyProject.id, {
41
+ message: "Add a dark mode toggle to the app",
42
+ });
43
+
44
+ // Wait for project to be published/deployed
45
+ const published = await client.waitForProjectPublished(project.id);
46
+ console.log("Live at:", published.url);
47
+
48
+ // Invite a collaborator to a workspace
49
+ await client.inviteCollaborator(workspaces[0].id, {
50
+ email: "collaborator@example.com",
51
+ role: "member",
52
+ });
53
+ ```
54
+
55
+ ## API Reference
56
+
57
+ ### `LovableClient`
58
+
59
+ #### Constructor
60
+
61
+ ```typescript
62
+ new LovableClient(options: LovableClientOptions)
63
+ ```
64
+
65
+ - `apiKey` (required): Your Lovable API key
66
+ - `baseUrl` (optional): Override the default API base URL
67
+
68
+ #### Methods
69
+
70
+ ##### `listWorkspaces(): Promise<WorkspaceWithMembership[]>`
71
+
72
+ List all workspaces the authenticated user has access to.
73
+
74
+ ##### `getWorkspace(workspaceId: string): Promise<WorkspaceWithMembership>`
75
+
76
+ Get a specific workspace by ID.
77
+
78
+ ##### `listProjects(workspaceId: string, options?): Promise<ProjectResponse[]>`
79
+
80
+ List projects in a workspace.
81
+
82
+ Options:
83
+
84
+ - `limit` (optional): Maximum number of projects to return
85
+ - `visibility` (optional): Filter by visibility (`"all"` | `"personal"` | `"public"` | `"workspace"`)
86
+
87
+ ##### `createProject(workspaceId: string, options): Promise<ProjectResponse>`
88
+
89
+ Create a new project in a workspace.
90
+
91
+ Options:
92
+
93
+ - `description` (required): Project description
94
+ - `techStack` (optional): Technology stack (e.g., `"react"`)
95
+ - `visibility` (optional): Project visibility (`"draft"` | `"private"` | `"public"`)
96
+ - `templateProjectId` (optional): ID of a template project to clone
97
+ - `initialMessage` (optional): Initial chat message to send to the AI agent
98
+
99
+ ##### `chat(projectId: string, options): Promise<void>`
100
+
101
+ Send a chat message to a project's AI agent.
102
+
103
+ Options:
104
+
105
+ - `message` (required): The message to send
106
+ - `chatOnly` (optional): If true, only chat without making code changes
107
+
108
+ Note: This is an asynchronous operation. The API accepts the message and processes it in the background.
109
+
110
+ ##### `inviteCollaborator(workspaceId: string, options): Promise<WorkspaceMembershipResponse>`
111
+
112
+ Invite a user to a workspace.
113
+
114
+ Options:
115
+
116
+ - `email` (required): Email address of the user to invite
117
+ - `role` (optional): Role to assign (`"admin"` | `"collaborator"` | `"member"` | `"viewer"`)
118
+
119
+ ##### `listWorkspaceMembers(workspaceId: string): Promise<WorkspaceMembershipResponse[]>`
120
+
121
+ List all members of a workspace.
122
+
123
+ ##### `removeWorkspaceMember(workspaceId: string, userId: string): Promise<void>`
124
+
125
+ Remove a member from a workspace.
126
+
127
+ ##### `getProject(projectId: string): Promise<ProjectResponse>`
128
+
129
+ Get project details by ID.
130
+
131
+ ##### `waitForProjectReady(projectId: string, options?): Promise<ProjectResponse>`
132
+
133
+ Wait for a project to reach "completed" status. Projects start in "in_progress" status while being created/built.
134
+
135
+ Options:
136
+
137
+ - `pollInterval` (optional): Time between polls in ms (default: 2000)
138
+ - `timeout` (optional): Maximum time to wait in ms (default: 300000 = 5 minutes)
139
+ - `onProgress` (optional): Callback for status updates
140
+
141
+ Throws an error if the project fails or timeout is reached.
142
+
143
+ ##### `waitForProjectPublished(projectId: string, options?): Promise<ProjectResponse>`
144
+
145
+ Wait for a project to be published (deployed) and have a live URL.
146
+
147
+ Options:
148
+
149
+ - `pollInterval` (optional): Time between polls in ms (default: 3000)
150
+ - `timeout` (optional): Maximum time to wait in ms (default: 600000 = 10 minutes)
151
+ - `onProgress` (optional): Callback for status updates
152
+
153
+ Throws an error if timeout is reached.
154
+
155
+ ## Types
156
+
157
+ The SDK exports TypeScript types for all API responses. See `src/types.ts` for the full list.
158
+
159
+ ```typescript
160
+ import type {
161
+ WorkspaceWithMembership,
162
+ ProjectResponse,
163
+ CreateProjectOptions,
164
+ // ... etc
165
+ } from "@lovable/sdk";
166
+ ```
@@ -0,0 +1,295 @@
1
+ type ProjectVisibility = "draft" | "private" | "public";
2
+ type MemberRole = "admin" | "collaborator" | "invited" | "member" | "none" | "owner" | "viewer";
3
+ type ProjectStatus = "completed" | "in_progress" | "failed";
4
+ interface WorkspaceMembership {
5
+ email: string;
6
+ invited_at?: string;
7
+ joined_at?: string;
8
+ monthly_credit_limit: number | null;
9
+ project_access?: Record<string, {
10
+ access_level?: string;
11
+ }>;
12
+ role: MemberRole;
13
+ user_id: string;
14
+ workspace_id: string;
15
+ }
16
+ interface WorkspaceWithMembership {
17
+ id: string;
18
+ name: string;
19
+ description?: string;
20
+ image_url?: string;
21
+ owner_id?: string;
22
+ is_personal?: boolean;
23
+ plan?: string;
24
+ plan_type?: string;
25
+ num_projects: number;
26
+ num_seats?: number;
27
+ membership: WorkspaceMembership;
28
+ created_at: string;
29
+ updated_at: string;
30
+ deleted_at?: string;
31
+ credits_granted: number;
32
+ credits_used: number;
33
+ daily_credits_limit: number;
34
+ daily_credits_used: number;
35
+ billing_period_credits_limit: number;
36
+ billing_period_credits_used: number;
37
+ billing_period_start_date?: string;
38
+ billing_period_end_date?: string;
39
+ rollover_credits_limit: number;
40
+ rollover_credits_used: number;
41
+ topup_credits_limit: number;
42
+ topup_credits_used: number;
43
+ total_credits_used: number;
44
+ subscription_status?: "active" | "canceled" | "incomplete_expired" | "incomplete" | "past_due" | "paused" | "trialing" | "unpaid";
45
+ referral_code?: string;
46
+ short_referral_code?: string;
47
+ referral_count: number;
48
+ followers_count: number;
49
+ default_project_visibility?: ProjectVisibility;
50
+ default_project_publish_visibility?: "private" | "public";
51
+ mcp_enabled?: boolean;
52
+ }
53
+ interface WorkspaceMembershipResponse {
54
+ user_id: string;
55
+ username: string;
56
+ display_name?: string;
57
+ email?: string;
58
+ role: MemberRole;
59
+ invited_at?: string;
60
+ joined_at?: string;
61
+ monthly_credit_limit?: number;
62
+ total_credits_used?: number;
63
+ total_credits_used_in_billing_period?: number;
64
+ project_access?: Record<string, {
65
+ access_level?: string;
66
+ }>;
67
+ }
68
+ interface ProjectResponse {
69
+ id: string;
70
+ name?: string;
71
+ display_name?: string;
72
+ description: string;
73
+ tech_stack: string;
74
+ status: string;
75
+ visibility?: ProjectVisibility;
76
+ publish_visibility?: "private" | "public";
77
+ is_published: boolean;
78
+ is_starred?: boolean;
79
+ is_template?: boolean;
80
+ is_github?: boolean;
81
+ is_supabase_enabled?: boolean;
82
+ url?: string;
83
+ og_image_url?: string;
84
+ latest_screenshot_url?: string;
85
+ user_id: string;
86
+ user_display_name?: string;
87
+ user_photo_url?: string;
88
+ created_at: string;
89
+ created_by?: string;
90
+ updated_at: string;
91
+ deleted_at?: string;
92
+ last_edited_at?: string;
93
+ last_viewed_at?: string;
94
+ published_at?: string;
95
+ workspace_id?: string;
96
+ folder_id?: string;
97
+ category?: string;
98
+ edit_count?: number;
99
+ gen_count?: number;
100
+ user_message_count?: number;
101
+ remix_count: number;
102
+ remixed_from_project_id?: string;
103
+ template_project_id?: string;
104
+ credit_total?: number;
105
+ custom_instructions?: string;
106
+ main_branch?: string;
107
+ latest_commit_sha?: string;
108
+ github_repo_name?: string;
109
+ github_repo_id?: number;
110
+ deployment_target?: string;
111
+ active_deployment_job_id?: string;
112
+ environments_enabled?: boolean;
113
+ hide_badge: boolean | null;
114
+ featured?: boolean;
115
+ featured_at?: string;
116
+ feature_rank?: number;
117
+ feature_source?: string;
118
+ }
119
+ interface ChatRequest {
120
+ id: string;
121
+ message: string;
122
+ chat_only: boolean;
123
+ headless: boolean;
124
+ ai_message_id?: string;
125
+ intent?: string;
126
+ model?: string;
127
+ temperature?: number;
128
+ current_page?: string;
129
+ view?: string;
130
+ view_description?: string;
131
+ prev_session_id?: string;
132
+ is_creation?: boolean;
133
+ }
134
+ interface CreateProjectBody {
135
+ description: string;
136
+ tech_stack: string;
137
+ visibility?: ProjectVisibility;
138
+ template_project_id?: string;
139
+ initial_message?: ChatRequest;
140
+ category?: string;
141
+ project_type?: string;
142
+ prompt_name?: string;
143
+ selected_theme?: string;
144
+ env_vars?: Record<string, string>;
145
+ metadata?: Record<string, unknown>;
146
+ }
147
+ interface AddUserToWorkspaceInputBody {
148
+ email: string;
149
+ role?: MemberRole;
150
+ }
151
+ interface CreateProjectOptions {
152
+ description: string;
153
+ techStack?: string;
154
+ visibility?: ProjectVisibility;
155
+ templateProjectId?: string;
156
+ initialMessage?: string;
157
+ }
158
+ interface InviteCollaboratorOptions {
159
+ email: string;
160
+ role?: MemberRole;
161
+ }
162
+ interface ChatMessageOptions {
163
+ message: string;
164
+ chatOnly?: boolean;
165
+ }
166
+ interface LovableClientOptions {
167
+ apiKey: string;
168
+ baseUrl?: string;
169
+ }
170
+ interface LovableError extends Error {
171
+ status: number;
172
+ type?: string;
173
+ detail?: string;
174
+ }
175
+ interface WaitOptions {
176
+ pollInterval?: number;
177
+ timeout?: number;
178
+ onProgress?: (project: ProjectResponse) => void;
179
+ }
180
+ interface DeploymentResponse {
181
+ status: string;
182
+ deployment_id?: string;
183
+ url?: string;
184
+ }
185
+
186
+ declare class LovableClient {
187
+ private readonly apiKey;
188
+ private readonly baseUrl;
189
+ constructor(options: LovableClientOptions);
190
+ private request;
191
+ /**
192
+ * List all workspaces the authenticated user has access to
193
+ */
194
+ listWorkspaces(): Promise<WorkspaceWithMembership[]>;
195
+ /**
196
+ * Get a specific workspace by ID
197
+ */
198
+ getWorkspace(workspaceId: string): Promise<WorkspaceWithMembership>;
199
+ /**
200
+ * List projects in a workspace
201
+ */
202
+ listProjects(workspaceId: string, options?: {
203
+ limit?: number;
204
+ visibility?: "all" | "personal" | "public" | "workspace";
205
+ }): Promise<ProjectResponse[]>;
206
+ /**
207
+ * Create a new project in a workspace
208
+ */
209
+ createProject(workspaceId: string, options: CreateProjectOptions): Promise<ProjectResponse>;
210
+ /**
211
+ * Send a chat message to a project
212
+ *
213
+ * Note: This sends a message to the project's AI agent. The response is
214
+ * asynchronous - the API accepts the message and processes it in the background.
215
+ */
216
+ chat(projectId: string, options: ChatMessageOptions): Promise<void>;
217
+ /**
218
+ * Invite a user to a workspace as a collaborator
219
+ */
220
+ inviteCollaborator(workspaceId: string, options: InviteCollaboratorOptions): Promise<WorkspaceMembershipResponse>;
221
+ /**
222
+ * List members of a workspace
223
+ */
224
+ listWorkspaceMembers(workspaceId: string): Promise<WorkspaceMembershipResponse[]>;
225
+ /**
226
+ * Remove a member from a workspace
227
+ */
228
+ removeWorkspaceMember(workspaceId: string, userId: string): Promise<void>;
229
+ /**
230
+ * Get project details by ID
231
+ */
232
+ getProject(projectId: string): Promise<ProjectResponse>;
233
+ /**
234
+ * Get the preview URL for a project.
235
+ *
236
+ * The preview URL is available once the project reaches "completed" status.
237
+ * This URL allows viewing the project in development mode.
238
+ *
239
+ * @param projectId - The project ID
240
+ * @returns The preview URL
241
+ */
242
+ getPreviewUrl(projectId: string): string;
243
+ /**
244
+ * Get the published URL for a project (if published).
245
+ *
246
+ * Returns the public URL if the project has been published, or null if not.
247
+ *
248
+ * @param projectId - The project ID
249
+ * @returns The published URL or null if not published
250
+ */
251
+ getPublishedUrl(projectId: string): Promise<string | null>;
252
+ /**
253
+ * Publish a project.
254
+ *
255
+ * This triggers a deployment which makes the project publicly accessible.
256
+ * The deployment runs asynchronously - use waitForProjectPublished() to wait for completion.
257
+ *
258
+ * @param projectId - The project ID to publish
259
+ * @param options.name - Optional custom slug for the published URL
260
+ * @returns Deployment info including deployment ID
261
+ */
262
+ publish(projectId: string, options?: {
263
+ name?: string;
264
+ }): Promise<DeploymentResponse>;
265
+ /**
266
+ * Wait for a project to reach "completed" status.
267
+ *
268
+ * Projects start in "in_progress" status while being created/built.
269
+ * This method polls until the status becomes "completed" or "failed".
270
+ * A successful completion means the project's preview is ready to view.
271
+ *
272
+ * @param projectId - The project ID to wait for
273
+ * @param options.pollInterval - Time between polls in ms (default: 2000)
274
+ * @param options.timeout - Maximum time to wait in ms (default: 300000 = 5 minutes)
275
+ * @param options.onProgress - Optional callback for status updates
276
+ * @returns The completed project
277
+ * @throws Error if project fails or timeout is reached
278
+ */
279
+ waitForProjectReady(projectId: string, options?: WaitOptions): Promise<ProjectResponse>;
280
+ /**
281
+ * Wait for a project to be published (deployed).
282
+ *
283
+ * This method polls until the project has `is_published: true` and a `url`.
284
+ *
285
+ * @param projectId - The project ID to wait for
286
+ * @param options.pollInterval - Time between polls in ms (default: 3000)
287
+ * @param options.timeout - Maximum time to wait in ms (default: 600000 = 10 minutes)
288
+ * @param options.onProgress - Optional callback for status updates
289
+ * @returns The published project with URL
290
+ * @throws Error if timeout is reached
291
+ */
292
+ waitForProjectPublished(projectId: string, options?: WaitOptions): Promise<ProjectResponse>;
293
+ }
294
+
295
+ export { type AddUserToWorkspaceInputBody, type ChatMessageOptions, type ChatRequest, type CreateProjectBody, type CreateProjectOptions, type DeploymentResponse, type InviteCollaboratorOptions, LovableClient, type LovableClientOptions, type LovableError, type MemberRole, type ProjectResponse, type ProjectStatus, type ProjectVisibility, type WaitOptions, type WorkspaceMembership, type WorkspaceMembershipResponse, type WorkspaceWithMembership };
package/dist/index.js ADDED
@@ -0,0 +1,246 @@
1
+ // src/client.ts
2
+ var DEFAULT_BASE_URL = "https://api.lovable.dev";
3
+ function normalizeBaseUrl(url) {
4
+ if (!url) return DEFAULT_BASE_URL;
5
+ let normalized = url.replace(/\/$/, "");
6
+ if (!normalized.startsWith("http://") && !normalized.startsWith("https://")) {
7
+ const isLocalhost = normalized.startsWith("localhost") || normalized.startsWith("127.0.0.1");
8
+ normalized = isLocalhost ? `http://${normalized}` : `https://${normalized}`;
9
+ }
10
+ return normalized;
11
+ }
12
+ var LovableClient = class {
13
+ apiKey;
14
+ baseUrl;
15
+ constructor(options) {
16
+ if (!options.apiKey) {
17
+ throw new Error("API key is required");
18
+ }
19
+ this.apiKey = options.apiKey;
20
+ this.baseUrl = normalizeBaseUrl(options.baseUrl);
21
+ }
22
+ async request(method, path, body) {
23
+ const url = `${this.baseUrl}${path}`;
24
+ const headers = {
25
+ "Lovable-API-Key": this.apiKey,
26
+ "Content-Type": "application/json"
27
+ };
28
+ const response = await fetch(url, {
29
+ method,
30
+ headers,
31
+ body: body ? JSON.stringify(body) : void 0
32
+ });
33
+ if (!response.ok) {
34
+ let errorBody;
35
+ try {
36
+ errorBody = await response.json();
37
+ } catch {
38
+ }
39
+ const error = new Error(errorBody?.title ?? `HTTP ${response.status}: ${response.statusText}`);
40
+ error.status = response.status;
41
+ error.type = errorBody?.title;
42
+ error.detail = errorBody?.detail;
43
+ throw error;
44
+ }
45
+ if (response.status === 204) {
46
+ return void 0;
47
+ }
48
+ return response.json();
49
+ }
50
+ /**
51
+ * List all workspaces the authenticated user has access to
52
+ */
53
+ async listWorkspaces() {
54
+ const response = await this.request("GET", "/user/workspaces");
55
+ return response.workspaces ?? [];
56
+ }
57
+ /**
58
+ * Get a specific workspace by ID
59
+ */
60
+ async getWorkspace(workspaceId) {
61
+ return this.request("GET", `/user/workspaces/${workspaceId}`);
62
+ }
63
+ /**
64
+ * List projects in a workspace
65
+ */
66
+ async listProjects(workspaceId, options) {
67
+ const params = new URLSearchParams();
68
+ if (options?.limit) params.set("limit", options.limit.toString());
69
+ if (options?.visibility) params.set("visibility", options.visibility);
70
+ const query = params.toString();
71
+ const path = `/workspaces/${workspaceId}/projects${query ? `?${query}` : ""}`;
72
+ const response = await this.request("GET", path);
73
+ return response.projects ?? [];
74
+ }
75
+ /**
76
+ * Create a new project in a workspace
77
+ */
78
+ async createProject(workspaceId, options) {
79
+ const body = {
80
+ description: options.description,
81
+ tech_stack: options.techStack ?? "",
82
+ visibility: options.visibility ?? "private",
83
+ template_project_id: options.templateProjectId
84
+ };
85
+ if (options.initialMessage) {
86
+ body.initial_message = {
87
+ id: crypto.randomUUID(),
88
+ message: options.initialMessage,
89
+ chat_only: false,
90
+ headless: true
91
+ };
92
+ }
93
+ return this.request("POST", `/workspaces/${workspaceId}/projects`, body);
94
+ }
95
+ /**
96
+ * Send a chat message to a project
97
+ *
98
+ * Note: This sends a message to the project's AI agent. The response is
99
+ * asynchronous - the API accepts the message and processes it in the background.
100
+ */
101
+ async chat(projectId, options) {
102
+ const body = {
103
+ id: crypto.randomUUID(),
104
+ message: options.message,
105
+ chat_only: options.chatOnly ?? false,
106
+ headless: true
107
+ };
108
+ await this.request("POST", `/projects/${projectId}/chat`, body);
109
+ }
110
+ /**
111
+ * Invite a user to a workspace as a collaborator
112
+ */
113
+ async inviteCollaborator(workspaceId, options) {
114
+ const body = {
115
+ email: options.email,
116
+ role: options.role ?? "member"
117
+ };
118
+ return this.request("POST", `/workspaces/${workspaceId}/memberships`, body);
119
+ }
120
+ /**
121
+ * List members of a workspace
122
+ */
123
+ async listWorkspaceMembers(workspaceId) {
124
+ const response = await this.request("GET", `/workspaces/${workspaceId}/memberships`);
125
+ return response.memberships ?? [];
126
+ }
127
+ /**
128
+ * Remove a member from a workspace
129
+ */
130
+ async removeWorkspaceMember(workspaceId, userId) {
131
+ await this.request("DELETE", `/workspaces/${workspaceId}/memberships/${userId}`);
132
+ }
133
+ /**
134
+ * Get project details by ID
135
+ */
136
+ async getProject(projectId) {
137
+ return this.request("GET", `/projects/${projectId}/details`);
138
+ }
139
+ /**
140
+ * Get the preview URL for a project.
141
+ *
142
+ * The preview URL is available once the project reaches "completed" status.
143
+ * This URL allows viewing the project in development mode.
144
+ *
145
+ * @param projectId - The project ID
146
+ * @returns The preview URL
147
+ */
148
+ getPreviewUrl(projectId) {
149
+ return `https://id-preview--${projectId}.lovable.app`;
150
+ }
151
+ /**
152
+ * Get the published URL for a project (if published).
153
+ *
154
+ * Returns the public URL if the project has been published, or null if not.
155
+ *
156
+ * @param projectId - The project ID
157
+ * @returns The published URL or null if not published
158
+ */
159
+ async getPublishedUrl(projectId) {
160
+ const project = await this.getProject(projectId);
161
+ return project.is_published && project.url ? project.url : null;
162
+ }
163
+ /**
164
+ * Publish a project.
165
+ *
166
+ * This triggers a deployment which makes the project publicly accessible.
167
+ * The deployment runs asynchronously - use waitForProjectPublished() to wait for completion.
168
+ *
169
+ * @param projectId - The project ID to publish
170
+ * @param options.name - Optional custom slug for the published URL
171
+ * @returns Deployment info including deployment ID
172
+ */
173
+ async publish(projectId, options) {
174
+ return this.request("POST", `/projects/${projectId}/deployments`, {
175
+ name: options?.name
176
+ });
177
+ }
178
+ /**
179
+ * Wait for a project to reach "completed" status.
180
+ *
181
+ * Projects start in "in_progress" status while being created/built.
182
+ * This method polls until the status becomes "completed" or "failed".
183
+ * A successful completion means the project's preview is ready to view.
184
+ *
185
+ * @param projectId - The project ID to wait for
186
+ * @param options.pollInterval - Time between polls in ms (default: 2000)
187
+ * @param options.timeout - Maximum time to wait in ms (default: 300000 = 5 minutes)
188
+ * @param options.onProgress - Optional callback for status updates
189
+ * @returns The completed project
190
+ * @throws Error if project fails or timeout is reached
191
+ */
192
+ async waitForProjectReady(projectId, options) {
193
+ const pollInterval = options?.pollInterval ?? 2e3;
194
+ const timeout = options?.timeout ?? 3e5;
195
+ const startTime = Date.now();
196
+ while (true) {
197
+ const project = await this.getProject(projectId);
198
+ options?.onProgress?.(project);
199
+ if (project.status === "completed") {
200
+ return project;
201
+ }
202
+ if (project.status === "failed") {
203
+ throw new Error(`Project ${projectId} failed to build`);
204
+ }
205
+ if (Date.now() - startTime > timeout) {
206
+ throw new Error(`Timeout waiting for project ${projectId} to be ready`);
207
+ }
208
+ await sleep(pollInterval);
209
+ }
210
+ }
211
+ /**
212
+ * Wait for a project to be published (deployed).
213
+ *
214
+ * This method polls until the project has `is_published: true` and a `url`.
215
+ *
216
+ * @param projectId - The project ID to wait for
217
+ * @param options.pollInterval - Time between polls in ms (default: 3000)
218
+ * @param options.timeout - Maximum time to wait in ms (default: 600000 = 10 minutes)
219
+ * @param options.onProgress - Optional callback for status updates
220
+ * @returns The published project with URL
221
+ * @throws Error if timeout is reached
222
+ */
223
+ async waitForProjectPublished(projectId, options) {
224
+ const pollInterval = options?.pollInterval ?? 3e3;
225
+ const timeout = options?.timeout ?? 6e5;
226
+ const startTime = Date.now();
227
+ while (true) {
228
+ const project = await this.getProject(projectId);
229
+ options?.onProgress?.(project);
230
+ if (project.is_published && project.url) {
231
+ return project;
232
+ }
233
+ if (Date.now() - startTime > timeout) {
234
+ throw new Error(`Timeout waiting for project ${projectId} to be published`);
235
+ }
236
+ await sleep(pollInterval);
237
+ }
238
+ }
239
+ };
240
+ function sleep(ms) {
241
+ return new Promise((resolve) => setTimeout(resolve, ms));
242
+ }
243
+ export {
244
+ LovableClient
245
+ };
246
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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 ChatRequest,\n AddUserToWorkspaceInputBody,\n GetWorkspacesResponse,\n GetWorkspaceProjectsResponse,\n DeploymentResponse,\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 let normalized = url.replace(/\\/$/, \"\");\n\n // Add http:// for localhost URLs without protocol\n if (!normalized.startsWith(\"http://\") && !normalized.startsWith(\"https://\")) {\n const isLocalhost = normalized.startsWith(\"localhost\") || normalized.startsWith(\"127.0.0.1\");\n normalized = isLocalhost ? `http://${normalized}` : `https://${normalized}`;\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 * List all workspaces the authenticated user has access to\n */\n async listWorkspaces(): Promise<WorkspaceWithMembership[]> {\n const response = await this.request<GetWorkspacesResponse>(\"GET\", \"/user/workspaces\");\n return response.workspaces ?? [];\n }\n\n /**\n * Get a specific workspace by ID\n */\n async getWorkspace(workspaceId: string): Promise<WorkspaceWithMembership> {\n return this.request<WorkspaceWithMembership>(\"GET\", `/user/workspaces/${workspaceId}`);\n }\n\n /**\n * List projects in a workspace\n */\n async listProjects(\n workspaceId: string,\n options?: { limit?: number; visibility?: \"all\" | \"personal\" | \"public\" | \"workspace\" },\n ): Promise<ProjectResponse[]> {\n const params = new URLSearchParams();\n if (options?.limit) params.set(\"limit\", options.limit.toString());\n if (options?.visibility) params.set(\"visibility\", options.visibility);\n\n const query = params.toString();\n const path = `/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 const body: CreateProjectBody = {\n description: options.description,\n tech_stack: options.techStack ?? \"\",\n visibility: options.visibility ?? \"private\",\n template_project_id: options.templateProjectId,\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 return this.request<ProjectResponse>(\"POST\", `/workspaces/${workspaceId}/projects`, body);\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 const body: ChatRequest = {\n id: crypto.randomUUID(),\n message: options.message,\n chat_only: options.chatOnly ?? false,\n headless: true,\n };\n\n await this.request<void>(\"POST\", `/projects/${projectId}/chat`, 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\", `/projects/${projectId}/details`);\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 * 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\", `/projects/${projectId}/deployments`, {\n name: options?.name,\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 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 (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":";AAkBA,IAAM,mBAAmB;AAEzB,SAAS,iBAAiB,KAAiC;AACzD,MAAI,CAAC,IAAK,QAAO;AAEjB,MAAI,aAAa,IAAI,QAAQ,OAAO,EAAE;AAGtC,MAAI,CAAC,WAAW,WAAW,SAAS,KAAK,CAAC,WAAW,WAAW,UAAU,GAAG;AAC3E,UAAM,cAAc,WAAW,WAAW,WAAW,KAAK,WAAW,WAAW,WAAW;AAC3F,iBAAa,cAAc,UAAU,UAAU,KAAK,WAAW,UAAU;AAAA,EAC3E;AAEA,SAAO;AACT;AAEO,IAAM,gBAAN,MAAoB;AAAA,EACR;AAAA,EACA;AAAA,EAEjB,YAAY,SAA+B;AACzC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACvC;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,UAAU,iBAAiB,QAAQ,OAAO;AAAA,EACjD;AAAA,EAEA,MAAc,QAAW,QAAgB,MAAc,MAA4B;AACjF,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAElC,UAAM,UAAkC;AAAA,MACtC,mBAAmB,KAAK;AAAA,MACxB,gBAAgB;AAAA,IAClB;AAEA,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC;AAAA,MACA;AAAA,MACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,UAAI;AACJ,UAAI;AACF,oBAAa,MAAM,SAAS,KAAK;AAAA,MACnC,QAAQ;AAAA,MAER;AAEA,YAAM,QAAQ,IAAI,MAAM,WAAW,SAAS,QAAQ,SAAS,MAAM,KAAK,SAAS,UAAU,EAAE;AAC7F,YAAM,SAAS,SAAS;AACxB,YAAM,OAAO,WAAW;AACxB,YAAM,SAAS,WAAW;AAC1B,YAAM;AAAA,IACR;AAEA,QAAI,SAAS,WAAW,KAAK;AAC3B,aAAO;AAAA,IACT;AAEA,WAAO,SAAS,KAAK;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAqD;AACzD,UAAM,WAAW,MAAM,KAAK,QAA+B,OAAO,kBAAkB;AACpF,WAAO,SAAS,cAAc,CAAC;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,aAAuD;AACxE,WAAO,KAAK,QAAiC,OAAO,oBAAoB,WAAW,EAAE;AAAA,EACvF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aACJ,aACA,SAC4B;AAC5B,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,SAAS,MAAO,QAAO,IAAI,SAAS,QAAQ,MAAM,SAAS,CAAC;AAChE,QAAI,SAAS,WAAY,QAAO,IAAI,cAAc,QAAQ,UAAU;AAEpE,UAAM,QAAQ,OAAO,SAAS;AAC9B,UAAM,OAAO,eAAe,WAAW,YAAY,QAAQ,IAAI,KAAK,KAAK,EAAE;AAE3E,UAAM,WAAW,MAAM,KAAK,QAAsC,OAAO,IAAI;AAC7E,WAAO,SAAS,YAAY,CAAC;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,aAAqB,SAAyD;AAChG,UAAM,OAA0B;AAAA,MAC9B,aAAa,QAAQ;AAAA,MACrB,YAAY,QAAQ,aAAa;AAAA,MACjC,YAAY,QAAQ,cAAc;AAAA,MAClC,qBAAqB,QAAQ;AAAA,IAC/B;AAEA,QAAI,QAAQ,gBAAgB;AAC1B,WAAK,kBAAkB;AAAA,QACrB,IAAI,OAAO,WAAW;AAAA,QACtB,SAAS,QAAQ;AAAA,QACjB,WAAW;AAAA,QACX,UAAU;AAAA,MACZ;AAAA,IACF;AAEA,WAAO,KAAK,QAAyB,QAAQ,eAAe,WAAW,aAAa,IAAI;AAAA,EAC1F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,KAAK,WAAmB,SAA4C;AACxE,UAAM,OAAoB;AAAA,MACxB,IAAI,OAAO,WAAW;AAAA,MACtB,SAAS,QAAQ;AAAA,MACjB,WAAW,QAAQ,YAAY;AAAA,MAC/B,UAAU;AAAA,IACZ;AAEA,UAAM,KAAK,QAAc,QAAQ,aAAa,SAAS,SAAS,IAAI;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBACJ,aACA,SACsC;AACtC,UAAM,OAAoC;AAAA,MACxC,OAAO,QAAQ;AAAA,MACf,MAAM,QAAQ,QAAQ;AAAA,IACxB;AAEA,WAAO,KAAK,QAAqC,QAAQ,eAAe,WAAW,gBAAgB,IAAI;AAAA,EACzG;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,qBAAqB,aAA6D;AACtF,UAAM,WAAW,MAAM,KAAK,QAEzB,OAAO,eAAe,WAAW,cAAc;AAClD,WAAO,SAAS,eAAe,CAAC;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,sBAAsB,aAAqB,QAA+B;AAC9E,UAAM,KAAK,QAAc,UAAU,eAAe,WAAW,gBAAgB,MAAM,EAAE;AAAA,EACvF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,WAA6C;AAC5D,WAAO,KAAK,QAAyB,OAAO,aAAa,SAAS,UAAU;AAAA,EAC9E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,cAAc,WAA2B;AACvC,WAAO,uBAAuB,SAAS;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,gBAAgB,WAA2C;AAC/D,UAAM,UAAU,MAAM,KAAK,WAAW,SAAS;AAC/C,WAAO,QAAQ,gBAAgB,QAAQ,MAAM,QAAQ,MAAM;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,QAAQ,WAAmB,SAA0D;AACzF,WAAO,KAAK,QAA4B,QAAQ,aAAa,SAAS,gBAAgB;AAAA,MACpF,MAAM,SAAS;AAAA,IACjB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,oBAAoB,WAAmB,SAAiD;AAC5F,UAAM,eAAe,SAAS,gBAAgB;AAC9C,UAAM,UAAU,SAAS,WAAW;AACpC,UAAM,YAAY,KAAK,IAAI;AAE3B,WAAO,MAAM;AACX,YAAM,UAAU,MAAM,KAAK,WAAW,SAAS;AAC/C,eAAS,aAAa,OAAO;AAE7B,UAAI,QAAQ,WAAW,aAAa;AAClC,eAAO;AAAA,MACT;AAEA,UAAI,QAAQ,WAAW,UAAU;AAC/B,cAAM,IAAI,MAAM,WAAW,SAAS,kBAAkB;AAAA,MACxD;AAEA,UAAI,KAAK,IAAI,IAAI,YAAY,SAAS;AACpC,cAAM,IAAI,MAAM,+BAA+B,SAAS,cAAc;AAAA,MACxE;AAEA,YAAM,MAAM,YAAY;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,wBAAwB,WAAmB,SAAiD;AAChG,UAAM,eAAe,SAAS,gBAAgB;AAC9C,UAAM,UAAU,SAAS,WAAW;AACpC,UAAM,YAAY,KAAK,IAAI;AAE3B,WAAO,MAAM;AACX,YAAM,UAAU,MAAM,KAAK,WAAW,SAAS;AAC/C,eAAS,aAAa,OAAO;AAE7B,UAAI,QAAQ,gBAAgB,QAAQ,KAAK;AACvC,eAAO;AAAA,MACT;AAEA,UAAI,KAAK,IAAI,IAAI,YAAY,SAAS;AACpC,cAAM,IAAI,MAAM,+BAA+B,SAAS,kBAAkB;AAAA,MAC5E;AAEA,YAAM,MAAM,YAAY;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;","names":[]}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@lovable.dev/sdk",
3
+ "version": "0.0.1",
4
+ "description": "TypeScript SDK for the Lovable API",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "devDependencies": {
19
+ "tsup": "^8.5.0",
20
+ "typescript": "^5.9.3",
21
+ "vitest": "^3.2.4"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/lovable-dev/lovable.git",
29
+ "directory": "packages/lov-sdk"
30
+ },
31
+ "license": "MIT",
32
+ "keywords": [
33
+ "lovable",
34
+ "sdk",
35
+ "api",
36
+ "typescript"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsup",
40
+ "typecheck": "tsc --noEmit",
41
+ "test": "vitest",
42
+ "publish:npm": "pnpm publish --access public"
43
+ }
44
+ }