@lovable.dev/sdk 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/client.ts
2
+ import createClient from "openapi-fetch";
3
+
1
4
  // src/types.ts
2
5
  var ApiError = class extends Error {
3
6
  status;
@@ -23,10 +26,29 @@ function normalizeBaseUrl(url) {
23
26
  }
24
27
  return normalized;
25
28
  }
29
+ var errorMiddleware = {
30
+ async onResponse({ response }) {
31
+ if (response.ok) return;
32
+ let errorBody;
33
+ try {
34
+ errorBody = await response.clone().json();
35
+ } catch {
36
+ }
37
+ const message = buildErrorMessage(errorBody, response.status, response.statusText);
38
+ const type = errorBody?.type ?? errorBody?.title;
39
+ const detail = errorBody?.detail ?? errorBody?.details;
40
+ throw new ApiError(response.status, message, type, detail, errorBody?.props);
41
+ }
42
+ };
43
+ function buildErrorMessage(body, status, statusText) {
44
+ return body?.message || body?.title || (statusText ? `HTTP ${status}: ${statusText}` : `HTTP ${status}`);
45
+ }
26
46
  var LovableClient = class {
27
47
  authHeaders;
28
48
  baseUrl;
29
49
  extraHeaders;
50
+ clientSource;
51
+ typedClient;
30
52
  constructor(options) {
31
53
  const hasApiKey = !!options.apiKey;
32
54
  const hasBearerToken = !!options.bearerToken;
@@ -39,10 +61,33 @@ var LovableClient = class {
39
61
  this.authHeaders = hasApiKey ? { "Lovable-API-Key": options.apiKey } : { Authorization: `Bearer ${options.bearerToken}` };
40
62
  this.baseUrl = normalizeBaseUrl(options.baseUrl);
41
63
  this.extraHeaders = options.headers ?? {};
64
+ this.clientSource = options.clientSource ?? "sdk";
65
+ this.typedClient = createClient({
66
+ baseUrl: this.baseUrl,
67
+ headers: {
68
+ "X-Client-Source": this.clientSource,
69
+ ...this.authHeaders,
70
+ ...this.extraHeaders,
71
+ Accept: "application/json"
72
+ }
73
+ });
74
+ this.typedClient.use(errorMiddleware);
42
75
  }
43
- async rawRequest(method, path, body) {
76
+ /**
77
+ * Type-safe access to any documented API route, driven by the auto-generated
78
+ * OpenAPI schema. Path / query params and request bodies are checked at compile
79
+ * time; calling an unknown path is a type error.
80
+ *
81
+ * Non-2xx responses throw `ApiError`; on success, `data` is set on the result.
82
+ */
83
+ get typed() {
84
+ return this.typedClient;
85
+ }
86
+ async rawRequest(method, path, body, init) {
44
87
  const url = `${this.baseUrl}${path}`;
45
88
  const headers = {
89
+ "X-Client-Source": this.clientSource,
90
+ ...init?.headers,
46
91
  ...this.authHeaders,
47
92
  ...this.extraHeaders,
48
93
  Accept: "application/json"
@@ -53,7 +98,8 @@ var LovableClient = class {
53
98
  const response = await fetch(url, {
54
99
  method,
55
100
  headers,
56
- body: body !== void 0 ? JSON.stringify(body) : void 0
101
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
102
+ signal: init?.signal
57
103
  });
58
104
  if (!response.ok) {
59
105
  let errorBody;
@@ -61,7 +107,7 @@ var LovableClient = class {
61
107
  errorBody = await response.json();
62
108
  } catch {
63
109
  }
64
- const message = errorBody?.message ?? errorBody?.title ?? `HTTP ${response.status}: ${response.statusText}`;
110
+ const message = buildErrorMessage(errorBody, response.status, response.statusText);
65
111
  const type = errorBody?.type ?? errorBody?.title;
66
112
  const detail = errorBody?.detail ?? errorBody?.details;
67
113
  throw new ApiError(response.status, message, type, detail, errorBody?.props);
@@ -84,7 +130,8 @@ var LovableClient = class {
84
130
  * Useful for validating an API key and discovering workspace IDs.
85
131
  */
86
132
  async me() {
87
- return this.request("GET", "/v1/me");
133
+ const { data } = await this.typed.GET("/v1/me");
134
+ return { ...data, workspaces: data.workspaces ?? [] };
88
135
  }
89
136
  /**
90
137
  * List all workspaces the authenticated user has access to
@@ -134,9 +181,11 @@ var LovableClient = class {
134
181
  }
135
182
  const body = {
136
183
  description: options.description,
137
- visibility: options.visibility ?? "private",
138
184
  template_project_id: options.templateProjectId
139
185
  };
186
+ if (options.visibility) {
187
+ body.visibility = options.visibility;
188
+ }
140
189
  if (options.techStack) {
141
190
  body.tech_stack = options.techStack;
142
191
  }
@@ -275,19 +324,6 @@ var LovableClient = class {
275
324
  async queryDatabase(projectId, sql) {
276
325
  return this.request("POST", `/v1/projects/${projectId}/database/query`, { sql });
277
326
  }
278
- /**
279
- * Get database connection info for a project.
280
- *
281
- * Returns host, port, user, password, database name, and full connection string
282
- * that can be used with any PostgreSQL client (psql, pgAdmin, etc.).
283
- * The database must be enabled first (see enableDatabase).
284
- *
285
- * @param projectId - The project ID
286
- * @returns Database connection details
287
- */
288
- async getDatabaseConnectionInfo(projectId) {
289
- return this.request("GET", `/v1/projects/${projectId}/database/connection-info`);
290
- }
291
327
  // ---------------------------------------------------------------------------
292
328
  // Messages
293
329
  // ---------------------------------------------------------------------------
@@ -298,6 +334,17 @@ var LovableClient = class {
298
334
  async getMessage(projectId, messageId) {
299
335
  return this.request("GET", `/v1/projects/${projectId}/messages/${messageId}`);
300
336
  }
337
+ /**
338
+ * List recent messages in a project, newest first. Use `before` (a message ID
339
+ * from a prior page) to paginate backwards through history.
340
+ */
341
+ async listMessages(projectId, params) {
342
+ const qs = new URLSearchParams();
343
+ if (params?.limit !== void 0) qs.set("limit", String(params.limit));
344
+ if (params?.before) qs.set("before", params.before);
345
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
346
+ return this.request("GET", `/v1/projects/${projectId}/messages${suffix}`);
347
+ }
301
348
  /**
302
349
  * Poll for message completion. Waits until the AI response reaches a terminal
303
350
  * status (completed, stopped) or the timeout expires.
@@ -386,15 +433,76 @@ var LovableClient = class {
386
433
  // ---------------------------------------------------------------------------
387
434
  /** Get workspace knowledge (custom instructions for the AI agent). */
388
435
  async getWorkspaceKnowledge(workspaceId) {
389
- return this.request("GET", `/v1/workspaces/${workspaceId}/knowledge`);
436
+ const { data } = await this.typed.GET("/v1/workspaces/{workspace_id}/knowledge", {
437
+ params: { path: { workspace_id: workspaceId } }
438
+ });
439
+ return data;
390
440
  }
391
441
  /** Set workspace knowledge. Max 10,000 characters. */
392
442
  async setWorkspaceKnowledge(workspaceId, content) {
393
443
  return this.request("PUT", `/v1/workspaces/${workspaceId}/knowledge`, { content });
394
444
  }
445
+ // ---------------------------------------------------------------------------
446
+ // Workspace skills
447
+ // ---------------------------------------------------------------------------
448
+ /** List workspace skills. */
449
+ async listWorkspaceSkills(workspaceId, options) {
450
+ const qs = new URLSearchParams();
451
+ if (options?.includeMarkdown) qs.set("include_markdown", "true");
452
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
453
+ return this.request("GET", `/v1/workspaces/${workspaceId}/skills${suffix}`);
454
+ }
455
+ /** Get a single workspace skill, including SKILL.md contents. */
456
+ async getWorkspaceSkill(workspaceId, skillName) {
457
+ return this.request(
458
+ "GET",
459
+ `/v1/workspaces/${workspaceId}/skills/${encodeURIComponent(skillName)}`
460
+ );
461
+ }
462
+ /** Create a workspace skill from full SKILL.md markdown. */
463
+ async createWorkspaceSkill(workspaceId, skillName, markdown) {
464
+ return this.request(
465
+ "POST",
466
+ `/v1/workspaces/${workspaceId}/skills/${encodeURIComponent(skillName)}`,
467
+ { markdown }
468
+ );
469
+ }
470
+ /** Update a workspace skill by replacing its SKILL.md markdown. */
471
+ async updateWorkspaceSkill(workspaceId, skillName, markdown) {
472
+ return this.request(
473
+ "PUT",
474
+ `/v1/workspaces/${workspaceId}/skills/${encodeURIComponent(skillName)}`,
475
+ { markdown }
476
+ );
477
+ }
478
+ /** Delete a workspace skill. */
479
+ async deleteWorkspaceSkill(workspaceId, skillName) {
480
+ return this.request(
481
+ "DELETE",
482
+ `/v1/workspaces/${workspaceId}/skills/${encodeURIComponent(skillName)}`
483
+ );
484
+ }
485
+ // ---------------------------------------------------------------------------
486
+ // Project skills
487
+ // ---------------------------------------------------------------------------
488
+ /** List project skills, including whether each skill is enabled. */
489
+ async listProjectSkills(projectId) {
490
+ return this.request("GET", `/v1/projects/${projectId}/skills`);
491
+ }
492
+ /** Enable or disable a project skill without removing it from the project repo. */
493
+ async setProjectSkillEnabled(projectId, skillName, enabled) {
494
+ return this.request(
495
+ "PUT",
496
+ `/v1/projects/${projectId}/skills/${encodeURIComponent(skillName)}/enabled`,
497
+ { enabled }
498
+ );
499
+ }
395
500
  /** Get project knowledge (custom instructions for the AI agent). */
396
501
  async getProjectKnowledge(projectId) {
397
- return this.request("GET", `/v1/projects/${projectId}/knowledge`);
502
+ const { data } = await this.typed.GET("/v1/projects/{project_id}/knowledge", {
503
+ params: { path: { project_id: projectId } }
504
+ });
505
+ return data;
398
506
  }
399
507
  /** Set project knowledge. Max 10,000 characters. */
400
508
  async setProjectKnowledge(projectId, content) {