@lovable.dev/sdk 0.1.1 → 0.1.3
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 +70 -0
- package/dist/index.d.ts +120 -1
- package/dist/index.js +59 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -79,6 +79,38 @@ await client.chat(projectId, { message: "Add dark mode" });
|
|
|
79
79
|
const response = await client.waitForResponse(projectId);
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
+
### Remixing with agent state
|
|
83
|
+
|
|
84
|
+
Enable `includeAgentState` to carry over the agent's continuation state to the remixed project, allowing prompt cache reuse on the first follow-up message.
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
const jobId = await client.remixProject("source-project-id", {
|
|
88
|
+
workspaceId: "target-workspace-id",
|
|
89
|
+
messageId: "message-id-to-snapshot-at",
|
|
90
|
+
includeHistory: true,
|
|
91
|
+
includeCustomKnowledge: true,
|
|
92
|
+
includeAgentState: true,
|
|
93
|
+
});
|
|
94
|
+
const { projectId } = await client.waitForRemix("source-project-id", jobId);
|
|
95
|
+
|
|
96
|
+
await client.chat(projectId, {
|
|
97
|
+
message: "Add dark mode",
|
|
98
|
+
continuation: "allow_expired_cache",
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Requires `includeHistory`, `includeCustomKnowledge`, and API-key auth.
|
|
103
|
+
|
|
104
|
+
### Continuation override
|
|
105
|
+
|
|
106
|
+
The `continuation` option on `chat()` overrides prompt cache reuse behavior. API-key auth only.
|
|
107
|
+
|
|
108
|
+
| Value | Effect |
|
|
109
|
+
| --- | --- |
|
|
110
|
+
| `"force"` | Skip all checks, force cache reuse |
|
|
111
|
+
| `"fresh_build"` | Force full prompt rebuild |
|
|
112
|
+
| `"allow_expired_cache"` | Skip expiry check only |
|
|
113
|
+
|
|
82
114
|
### Using a custom model
|
|
83
115
|
|
|
84
116
|
You can route the main agent to a custom OpenAI-compatible endpoint for eval and RL workflows:
|
|
@@ -124,6 +156,19 @@ for (const [messageId, error] of result.errors) {
|
|
|
124
156
|
}
|
|
125
157
|
```
|
|
126
158
|
|
|
159
|
+
### Replaying reviewers (`_dev`)
|
|
160
|
+
|
|
161
|
+
Re-run quality reviewers (e.g. `project_success_v3`, `user_sentiment`) for past assistant messages. Requires an API key with project write access; not part of the stable v1 OpenAPI bundle (types live in this SDK).
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const out = await client._dev.replayReviews(project.id, {
|
|
165
|
+
items: [{ response_message_id: "aimsg_..." }],
|
|
166
|
+
reviewer_types: ["project_success_v3", "user_sentiment"],
|
|
167
|
+
concurrency: 4,
|
|
168
|
+
});
|
|
169
|
+
console.log(out.results.items);
|
|
170
|
+
```
|
|
171
|
+
|
|
127
172
|
## API Reference
|
|
128
173
|
|
|
129
174
|
### `LovableClient`
|
|
@@ -179,6 +224,7 @@ Options:
|
|
|
179
224
|
- `files` (optional): Array of files to attach (browser `File` objects or `FileInput` objects)
|
|
180
225
|
- `chatOnly` (optional): If true, only chat without making code changes
|
|
181
226
|
- `customModel` (optional): Route the main agent to a custom OpenAI-compatible endpoint (see `CustomModelConfig`)
|
|
227
|
+
- `continuation` (optional): Override prompt cache continuation behavior (`"force"` | `"fresh_build"` | `"allow_expired_cache"`). API-key auth only. See [Controlling prompt cache continuation](#controlling-prompt-cache-continuation)
|
|
182
228
|
|
|
183
229
|
Note: This is an asynchronous operation. The API accepts the message and processes it in the background. Use `waitForResponse()` to wait for the AI's reply.
|
|
184
230
|
|
|
@@ -261,8 +307,10 @@ Options:
|
|
|
261
307
|
- `remixMode` (optional): `"before"` (default) captures state before the message; `"including"` captures state after the message and its AI response
|
|
262
308
|
- `includeHistory` (optional, default: `false`): Whether to preserve chat history
|
|
263
309
|
- `includeCustomKnowledge` (optional, default: `false`): Whether to copy custom instructions/knowledge
|
|
310
|
+
- `includeAgentState` (optional, default: `false`): Copy agent continuation state so the prompt cache can be reused after remix. Requires `includeHistory: true`, `includeCustomKnowledge: true`, and API-key auth. See [Remixing with agent state](#remixing-with-agent-state-prompt-cache-continuation)
|
|
264
311
|
- `initialMessage` (optional): Initial chat message to send after remix completes
|
|
265
312
|
- `skipInitialRemixMessage` (optional, default: `false`): When true, suppresses the default "I've successfully remixed this project" message
|
|
313
|
+
- `skipIntegrations` (optional, default: `false`): When true, skips copying integrations to the remixed project
|
|
266
314
|
|
|
267
315
|
##### `waitForRemix(sourceProjectId: string, jobId: string, options?): Promise<RemixResult>`
|
|
268
316
|
|
|
@@ -314,6 +362,14 @@ Returns:
|
|
|
314
362
|
- `traces`: Map of messageId → `MessageTracesResponse`
|
|
315
363
|
- `errors`: Map of messageId → `Error` (for failed requests)
|
|
316
364
|
|
|
365
|
+
##### `_dev.replayReviews(projectId: string, body): Promise<ReplayReviewsResponse>`
|
|
366
|
+
|
|
367
|
+
Re-run reviewers for one or more assistant (`response`) messages. Requires an API key with project write access.
|
|
368
|
+
|
|
369
|
+
- `body.items`: Each entry must include `response_message_id` (assistant message id). Optional per-item `reviewer_types`.
|
|
370
|
+
- `body.reviewer_types` (optional): Global default when an item omits `reviewer_types`.
|
|
371
|
+
- `body.concurrency` (optional): Parallel items (default 4, max 16).
|
|
372
|
+
|
|
317
373
|
##### `inviteCollaborator(workspaceId: string, options): Promise<WorkspaceMembershipResponse>`
|
|
318
374
|
|
|
319
375
|
Invite a user to a workspace.
|
|
@@ -345,8 +401,10 @@ import type {
|
|
|
345
401
|
ProjectResponse,
|
|
346
402
|
CreateProjectOptions,
|
|
347
403
|
ChatResponse,
|
|
404
|
+
ContinuationOverride,
|
|
348
405
|
CustomModelConfig,
|
|
349
406
|
FileInput,
|
|
407
|
+
RemixProjectOptions,
|
|
350
408
|
TracePurpose,
|
|
351
409
|
TraceSpan,
|
|
352
410
|
MessageTracesResponse,
|
|
@@ -378,6 +436,18 @@ interface CustomModelConfig {
|
|
|
378
436
|
}
|
|
379
437
|
```
|
|
380
438
|
|
|
439
|
+
### `ContinuationOverride`
|
|
440
|
+
|
|
441
|
+
Controls prompt cache continuation behavior for a message:
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
type ContinuationOverride = "force" | "fresh_build" | "allow_expired_cache";
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
- `"force"` — skip all continuation checks (force continuation)
|
|
448
|
+
- `"fresh_build"` — force a full prompt rebuild from scratch
|
|
449
|
+
- `"allow_expired_cache"` — skip cache expiry check but respect other continuation checks
|
|
450
|
+
|
|
381
451
|
### `TracePurpose`
|
|
382
452
|
|
|
383
453
|
Available trace span purposes:
|
package/dist/index.d.ts
CHANGED
|
@@ -206,11 +206,26 @@ interface InviteCollaboratorOptions {
|
|
|
206
206
|
email: string;
|
|
207
207
|
role?: MemberRole;
|
|
208
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Controls prompt cache continuation behavior for a message.
|
|
211
|
+
*
|
|
212
|
+
* - `"force"` — skip cache TTL and token/criteria checks (force continuation)
|
|
213
|
+
* - `"fresh_build"` — force a full prompt rebuild from scratch
|
|
214
|
+
* - `"allow_expired_cache"` — skip cache TTL check but respect token/criteria limits
|
|
215
|
+
*
|
|
216
|
+
* Omit for default behavior (5-min TTL, token and criteria checks apply).
|
|
217
|
+
*/
|
|
218
|
+
type ContinuationOverride = "force" | "fresh_build" | "allow_expired_cache";
|
|
209
219
|
interface ChatMessageOptions {
|
|
210
220
|
message: string;
|
|
211
221
|
files?: (File | FileInput)[];
|
|
212
222
|
chatOnly?: boolean;
|
|
213
223
|
customModel?: CustomModelConfig;
|
|
224
|
+
/**
|
|
225
|
+
* Override continuation behavior for this message.
|
|
226
|
+
* Controls whether the agent reuses the prompt cache or rebuilds from scratch.
|
|
227
|
+
*/
|
|
228
|
+
continuation?: ContinuationOverride;
|
|
214
229
|
}
|
|
215
230
|
interface LovableClientOptions {
|
|
216
231
|
apiKey: string;
|
|
@@ -275,6 +290,44 @@ interface BatchTracesResult {
|
|
|
275
290
|
traces: Map<string, MessageTracesResponse>;
|
|
276
291
|
errors: Map<string, Error>;
|
|
277
292
|
}
|
|
293
|
+
interface ReplayReviewsItemInput {
|
|
294
|
+
/** Assistant (AI) message id for the turn to score */
|
|
295
|
+
response_message_id: string;
|
|
296
|
+
/** Optional per-item reviewer types */
|
|
297
|
+
reviewer_types?: string[] | null;
|
|
298
|
+
}
|
|
299
|
+
interface ReplayReviewsRequestBody {
|
|
300
|
+
items: ReplayReviewsItemInput[];
|
|
301
|
+
/** Defaults to project_success_v3 and user_sentiment when omitted */
|
|
302
|
+
reviewer_types?: string[] | null;
|
|
303
|
+
/** Parallel items (default 4, max 16) */
|
|
304
|
+
concurrency?: number;
|
|
305
|
+
}
|
|
306
|
+
interface ReviewerRunResult {
|
|
307
|
+
reviewer_type: string;
|
|
308
|
+
score?: number;
|
|
309
|
+
message?: string;
|
|
310
|
+
model?: string;
|
|
311
|
+
published_rubric_rows: number;
|
|
312
|
+
error?: string;
|
|
313
|
+
}
|
|
314
|
+
interface ReplayOutcome {
|
|
315
|
+
response_message_id: string;
|
|
316
|
+
user_message_id: string;
|
|
317
|
+
results: ReviewerRunResult[] | null;
|
|
318
|
+
}
|
|
319
|
+
interface BatchItemOutcome {
|
|
320
|
+
response_message_id: string;
|
|
321
|
+
ok: boolean;
|
|
322
|
+
error?: string;
|
|
323
|
+
replay?: ReplayOutcome;
|
|
324
|
+
}
|
|
325
|
+
interface BatchReplayReviewsOutcome {
|
|
326
|
+
items: BatchItemOutcome[];
|
|
327
|
+
}
|
|
328
|
+
interface ReplayReviewsResponse {
|
|
329
|
+
results: BatchReplayReviewsOutcome;
|
|
330
|
+
}
|
|
278
331
|
type RemixJobStatus = "unknown" | "preparing" | "running" | "completed" | "error";
|
|
279
332
|
type RemixJobStep = "starting" | "creating_new_project" | "remixing_integration" | "finalizing" | "completed";
|
|
280
333
|
interface RemixJobStepInfo {
|
|
@@ -292,6 +345,8 @@ interface RemixProjectOptions {
|
|
|
292
345
|
initialMessage?: string;
|
|
293
346
|
skipInitialRemixMessage?: boolean;
|
|
294
347
|
skipIntegrations?: boolean;
|
|
348
|
+
/** Copy agent continuation state so prompt cache can be reused after remix. Requires includeHistory. API-key auth only. */
|
|
349
|
+
includeAgentState?: boolean;
|
|
295
350
|
}
|
|
296
351
|
interface RemixResult {
|
|
297
352
|
projectId: string;
|
|
@@ -301,6 +356,26 @@ interface RemixWaitOptions {
|
|
|
301
356
|
timeout?: number;
|
|
302
357
|
onProgress?: (status: RemixJobStatus, step?: RemixJobStepInfo) => void;
|
|
303
358
|
}
|
|
359
|
+
interface DatabaseStatus {
|
|
360
|
+
enabled: boolean;
|
|
361
|
+
stack?: string;
|
|
362
|
+
}
|
|
363
|
+
interface EnableDatabaseResult {
|
|
364
|
+
enabled: boolean;
|
|
365
|
+
stack: string;
|
|
366
|
+
}
|
|
367
|
+
interface DatabaseQueryResult {
|
|
368
|
+
rows: Record<string, unknown>[] | null;
|
|
369
|
+
}
|
|
370
|
+
interface DatabaseConnectionInfo {
|
|
371
|
+
host: string;
|
|
372
|
+
port: string;
|
|
373
|
+
user: string;
|
|
374
|
+
password: string;
|
|
375
|
+
database: string;
|
|
376
|
+
connection_string: string;
|
|
377
|
+
api_url: string;
|
|
378
|
+
}
|
|
304
379
|
|
|
305
380
|
declare class LovableClient {
|
|
306
381
|
private readonly apiKey;
|
|
@@ -374,6 +449,45 @@ declare class LovableClient {
|
|
|
374
449
|
* @returns The published URL or null if not published
|
|
375
450
|
*/
|
|
376
451
|
getPublishedUrl(projectId: string): Promise<string | null>;
|
|
452
|
+
/**
|
|
453
|
+
* Get the cloud database status for a project.
|
|
454
|
+
*
|
|
455
|
+
* @param projectId - The project ID
|
|
456
|
+
* @returns Whether the database is enabled and which stack is used
|
|
457
|
+
*/
|
|
458
|
+
getDatabaseStatus(projectId: string): Promise<DatabaseStatus>;
|
|
459
|
+
/**
|
|
460
|
+
* Enable (provision) a cloud database for a project.
|
|
461
|
+
*
|
|
462
|
+
* This triggers database provisioning which takes 30-60 seconds.
|
|
463
|
+
* The call blocks until provisioning completes.
|
|
464
|
+
*
|
|
465
|
+
* @param projectId - The project ID
|
|
466
|
+
* @returns The database status after enablement
|
|
467
|
+
*/
|
|
468
|
+
enableDatabase(projectId: string): Promise<EnableDatabaseResult>;
|
|
469
|
+
/**
|
|
470
|
+
* Execute a SQL query against the project's cloud database.
|
|
471
|
+
*
|
|
472
|
+
* Supports SELECT, INSERT, UPDATE, DELETE, and DDL statements.
|
|
473
|
+
* The database must be enabled first (see enableDatabase).
|
|
474
|
+
*
|
|
475
|
+
* @param projectId - The project ID
|
|
476
|
+
* @param sql - SQL query to execute
|
|
477
|
+
* @returns Query result rows as JSON objects
|
|
478
|
+
*/
|
|
479
|
+
queryDatabase(projectId: string, sql: string): Promise<DatabaseQueryResult>;
|
|
480
|
+
/**
|
|
481
|
+
* Get database connection info for a project.
|
|
482
|
+
*
|
|
483
|
+
* Returns host, port, user, password, database name, and full connection string
|
|
484
|
+
* that can be used with any PostgreSQL client (psql, pgAdmin, etc.).
|
|
485
|
+
* The database must be enabled first (see enableDatabase).
|
|
486
|
+
*
|
|
487
|
+
* @param projectId - The project ID
|
|
488
|
+
* @returns Database connection details
|
|
489
|
+
*/
|
|
490
|
+
getDatabaseConnectionInfo(projectId: string): Promise<DatabaseConnectionInfo>;
|
|
377
491
|
/**
|
|
378
492
|
* Publish a project.
|
|
379
493
|
*
|
|
@@ -484,6 +598,11 @@ declare class LovableClient {
|
|
|
484
598
|
getMessageTracesBatch: (queries: TraceQuery[], options?: GetMessageTracesOptions & {
|
|
485
599
|
concurrency?: number;
|
|
486
600
|
}) => Promise<BatchTracesResult>;
|
|
601
|
+
/**
|
|
602
|
+
* Re-run project reviewers (e.g. project_success_v3, user_sentiment) for past assistant
|
|
603
|
+
* messages. Requires an API key with project write access.
|
|
604
|
+
*/
|
|
605
|
+
replayReviews: (projectId: string, body: ReplayReviewsRequestBody) => Promise<ReplayReviewsResponse>;
|
|
487
606
|
};
|
|
488
607
|
private isFileInput;
|
|
489
608
|
private uploadFile;
|
|
@@ -504,4 +623,4 @@ declare class LovableClient {
|
|
|
504
623
|
waitForProjectPublished(projectId: string, options?: WaitOptions): Promise<ProjectResponse>;
|
|
505
624
|
}
|
|
506
625
|
|
|
507
|
-
export { type AddUserToWorkspaceInputBody, type BatchTracesResult, type ChatMessageOptions, type ChatRequest, type ChatResponse, type ChatResponseOptions, type CreateProjectBody, type CreateProjectOptions, type CustomModelConfig, type DeploymentResponse, 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 TracePurpose, type TraceQuery, type TraceSpan, type UnscopedFile, type WaitOptions, type WorkspaceMembership, type WorkspaceMembershipResponse, type WorkspaceWithMembership };
|
|
626
|
+
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 };
|
package/dist/index.js
CHANGED
|
@@ -135,6 +135,9 @@ var LovableClient = class {
|
|
|
135
135
|
body.custom_model_api_key = options.customModel.apiKey;
|
|
136
136
|
body.custom_model_name = options.customModel.modelName;
|
|
137
137
|
}
|
|
138
|
+
if (options.continuation) {
|
|
139
|
+
body.continuation = options.continuation;
|
|
140
|
+
}
|
|
138
141
|
await this.request("POST", `/v1/projects/${projectId}/messages`, body);
|
|
139
142
|
}
|
|
140
143
|
/**
|
|
@@ -190,6 +193,53 @@ var LovableClient = class {
|
|
|
190
193
|
const project = await this.getProject(projectId);
|
|
191
194
|
return project.is_published && project.url ? project.url : null;
|
|
192
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Get the cloud database status for a project.
|
|
198
|
+
*
|
|
199
|
+
* @param projectId - The project ID
|
|
200
|
+
* @returns Whether the database is enabled and which stack is used
|
|
201
|
+
*/
|
|
202
|
+
async getDatabaseStatus(projectId) {
|
|
203
|
+
return this.request("GET", `/v1/projects/${projectId}/database`);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Enable (provision) a cloud database for a project.
|
|
207
|
+
*
|
|
208
|
+
* This triggers database provisioning which takes 30-60 seconds.
|
|
209
|
+
* The call blocks until provisioning completes.
|
|
210
|
+
*
|
|
211
|
+
* @param projectId - The project ID
|
|
212
|
+
* @returns The database status after enablement
|
|
213
|
+
*/
|
|
214
|
+
async enableDatabase(projectId) {
|
|
215
|
+
return this.request("POST", `/v1/projects/${projectId}/database/enable`);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Execute a SQL query against the project's cloud database.
|
|
219
|
+
*
|
|
220
|
+
* Supports SELECT, INSERT, UPDATE, DELETE, and DDL statements.
|
|
221
|
+
* The database must be enabled first (see enableDatabase).
|
|
222
|
+
*
|
|
223
|
+
* @param projectId - The project ID
|
|
224
|
+
* @param sql - SQL query to execute
|
|
225
|
+
* @returns Query result rows as JSON objects
|
|
226
|
+
*/
|
|
227
|
+
async queryDatabase(projectId, sql) {
|
|
228
|
+
return this.request("POST", `/v1/projects/${projectId}/database/query`, { sql });
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Get database connection info for a project.
|
|
232
|
+
*
|
|
233
|
+
* Returns host, port, user, password, database name, and full connection string
|
|
234
|
+
* that can be used with any PostgreSQL client (psql, pgAdmin, etc.).
|
|
235
|
+
* The database must be enabled first (see enableDatabase).
|
|
236
|
+
*
|
|
237
|
+
* @param projectId - The project ID
|
|
238
|
+
* @returns Database connection details
|
|
239
|
+
*/
|
|
240
|
+
async getDatabaseConnectionInfo(projectId) {
|
|
241
|
+
return this.request("GET", `/v1/projects/${projectId}/database/connection-info`);
|
|
242
|
+
}
|
|
193
243
|
/**
|
|
194
244
|
* Publish a project.
|
|
195
245
|
*
|
|
@@ -228,7 +278,8 @@ var LovableClient = class {
|
|
|
228
278
|
include_history: options.includeHistory,
|
|
229
279
|
include_custom_knowledge: options.includeCustomKnowledge,
|
|
230
280
|
skip_initial_remix_message: options.skipInitialRemixMessage,
|
|
231
|
-
skip_integrations: options.skipIntegrations
|
|
281
|
+
skip_integrations: options.skipIntegrations,
|
|
282
|
+
include_agent_state: options.includeAgentState
|
|
232
283
|
};
|
|
233
284
|
if (options.messageId) {
|
|
234
285
|
body.message_id = options.messageId;
|
|
@@ -425,6 +476,13 @@ var LovableClient = class {
|
|
|
425
476
|
}
|
|
426
477
|
await Promise.all(executing);
|
|
427
478
|
return { traces, errors };
|
|
479
|
+
},
|
|
480
|
+
/**
|
|
481
|
+
* Re-run project reviewers (e.g. project_success_v3, user_sentiment) for past assistant
|
|
482
|
+
* messages. Requires an API key with project write access.
|
|
483
|
+
*/
|
|
484
|
+
replayReviews: async (projectId, body) => {
|
|
485
|
+
return this.request("POST", `/v1/_dev/projects/${projectId}/reviews/replay`, body);
|
|
428
486
|
}
|
|
429
487
|
};
|
|
430
488
|
}
|
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 ChatRequest,\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 MeResponse,\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 } = {\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\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 * 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 };\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\n private isFileInput(file: File | FileInput): file is FileInput {\n return \"data\" in file;\n }\n\n private async uploadFile(file: File | FileInput): Promise<UnscopedFile> {\n const fileName = this.isFileInput(file) ? file.name : file.name;\n const mimeType = this.isFileInput(file) ? file.type : file.type;\n const body = this.isFileInput(file) ? file.data : file;\n\n const { url, file_id: objectPath } = await this.request<{ url: string; file_id: string }>(\n \"POST\",\n \"/v1/files/upload-url\",\n {\n file_name: fileName,\n content_type: mimeType,\n },\n );\n\n const uploadResponse = await fetch(url, {\n method: \"PUT\",\n body,\n headers: { \"Content-Type\": mimeType },\n });\n if (!uploadResponse.ok) {\n throw new Error(`File upload failed for \"${fileName}\": HTTP ${uploadResponse.status}`);\n }\n\n return { file_id: objectPath, type: \"user_upload\", file_name: fileName, mime_type: mimeType };\n }\n\n private async uploadFiles(files: (File | FileInput)[]): Promise<UnscopedFile[]> {\n return Promise.all(files.map((file) => this.uploadFile(file)));\n }\n\n private async consumeSSEStream(body: ReadableStream<Uint8Array>): Promise<{ content: string; messageId: string }> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let content = \"\";\n let messageId = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // SSE events are separated by double newlines\n const parts = buffer.split(\"\\n\\n\");\n buffer = parts.pop() ?? \"\";\n\n for (const part of parts) {\n if (!part.trim()) continue;\n\n const lines = part.split(\"\\n\");\n let eventType = \"\";\n let eventData = \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"event: \")) {\n eventType = line.slice(7);\n } else if (line.startsWith(\"data: \")) {\n eventData = line.slice(6);\n }\n }\n\n if (eventType === \"message\" && eventData) {\n try {\n const data = JSON.parse(eventData);\n if (typeof data.content === \"string\") {\n content += data.content;\n }\n if (typeof data.message_id === \"string\" && data.message_id) {\n messageId = data.message_id;\n }\n if (data.is_final) {\n return { content, messageId };\n }\n } catch {\n // Skip non-JSON data lines\n }\n }\n\n if (eventType === \"error\") {\n let detail = \"Stream error from server\";\n try {\n const data = JSON.parse(eventData);\n if (data.message) detail = data.message;\n } catch {\n // use default message\n }\n throw new Error(detail);\n }\n }\n }\n } finally {\n void reader.cancel();\n }\n\n return { content, messageId };\n }\n\n /**\n * Wait for a project to be published (deployed).\n *\n * This method polls until the project has `is_published: true` and a `url`.\n *\n * @param projectId - The project ID to wait for\n * @param options.pollInterval - Time between polls in ms (default: 3000)\n * @param options.timeout - Maximum time to wait in ms (default: 600000 = 10 minutes)\n * @param options.onProgress - Optional callback for status updates\n * @returns The published project with URL\n * @throws Error if timeout is reached\n */\n async waitForProjectPublished(projectId: string, options?: WaitOptions): Promise<ProjectResponse> {\n const pollInterval = options?.pollInterval ?? 3000;\n const timeout = options?.timeout ?? 600000;\n const startTime = Date.now();\n\n while (true) {\n const project = await this.getProject(projectId);\n options?.onProgress?.(project);\n\n if (project.is_published && project.url) {\n return project;\n }\n\n if (project.status === \"failed\") {\n throw new Error(`Project ${projectId} failed to build`);\n }\n\n if (Date.now() - startTime > timeout) {\n throw new Error(`Timeout waiting for project ${projectId} to be published`);\n }\n\n await sleep(pollInterval);\n }\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"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 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.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":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lovable.dev/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "TypeScript SDK for the Lovable API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"tsup": "^8.5.0",
|
|
20
20
|
"typescript": "^5.9.3",
|
|
21
|
-
"vitest": "^
|
|
21
|
+
"vitest": "^4.1.0"
|
|
22
22
|
},
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"build": "tsup",
|
|
35
35
|
"typecheck": "tsgo --noEmit",
|
|
36
36
|
"test": "vitest",
|
|
37
|
+
"test:replay-reviews": "node scripts/test-replay-reviews.mjs",
|
|
37
38
|
"publish:npm": "pnpm publish --access public"
|
|
38
39
|
}
|
|
39
40
|
}
|