lumnisai 0.1.0

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.cjs ADDED
@@ -0,0 +1,1110 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ const DEFAULT_BASE_URL = "https://api.lumnis.ai";
6
+ const DEFAULT_POLL_INTERVAL_MS = 2e3;
7
+ const LONG_POLL_TIMEOUT_S = 10;
8
+ const DEFAULT_TIMEOUT_MS = 3e4;
9
+ const DEFAULT_MAX_RETRIES = 3;
10
+ const DEFAULT_BACKOFF_FACTOR = 0.5;
11
+
12
+ class ExternalAPIKeysResource {
13
+ constructor(http) {
14
+ this.http = http;
15
+ }
16
+ /**
17
+ * Store a new external API key (encrypted)
18
+ */
19
+ async store(data) {
20
+ return this.http.post("/external-api-keys", data);
21
+ }
22
+ /**
23
+ * Create a new external API key (alias for store)
24
+ */
25
+ async create(data) {
26
+ return this.store(data);
27
+ }
28
+ /**
29
+ * List all configured external API keys (metadata only, no key values)
30
+ */
31
+ async list() {
32
+ return this.http.get("/external-api-keys");
33
+ }
34
+ /**
35
+ * Get details for a specific external API key (no key value)
36
+ */
37
+ async get(keyId) {
38
+ return this.http.get(`/external-api-keys/${keyId}`);
39
+ }
40
+ /**
41
+ * Delete an external API key for a specific provider
42
+ */
43
+ async delete(provider) {
44
+ return this.http.delete(`/external-api-keys/${provider}`);
45
+ }
46
+ /**
47
+ * Get the current API key mode
48
+ */
49
+ async getMode() {
50
+ return this.http.get("/external-api-keys/mode");
51
+ }
52
+ /**
53
+ * Update the API key mode
54
+ */
55
+ async updateMode(data) {
56
+ return this.http.patch("/external-api-keys/mode", data);
57
+ }
58
+ /**
59
+ * Set the API key mode (alias for updateMode)
60
+ */
61
+ async setMode(data) {
62
+ return this.updateMode(data);
63
+ }
64
+ }
65
+
66
+ class FilesResource {
67
+ constructor(http) {
68
+ this.http = http;
69
+ }
70
+ /**
71
+ * Upload a new file for processing
72
+ * @param file - The file to upload (File or Blob)
73
+ * @param options - Upload options
74
+ * @param options.scope - File access scope (user or tenant)
75
+ * @param options.userId - User ID or email (required for user-scoped files)
76
+ * @param options.tags - Comma-separated tags for categorization
77
+ * @param options.duplicateHandling - How to handle duplicate filenames
78
+ */
79
+ async upload(file, options) {
80
+ const formData = new FormData();
81
+ formData.append("file", file);
82
+ formData.append("scope", options.scope);
83
+ if (options.userId)
84
+ formData.append("user_id", options.userId);
85
+ if (options.tags)
86
+ formData.append("tags", options.tags);
87
+ if (options.duplicateHandling)
88
+ formData.append("duplicate_handling", options.duplicateHandling);
89
+ return this.http.post("/files/upload", formData);
90
+ }
91
+ /**
92
+ * Upload multiple files at once
93
+ */
94
+ async bulkUpload(files, options) {
95
+ const formData = new FormData();
96
+ for (const file of files)
97
+ formData.append("files", file);
98
+ formData.append("scope", options.scope);
99
+ if (options.userId)
100
+ formData.append("user_id", options.userId);
101
+ if (options.tags)
102
+ formData.append("tags", options.tags);
103
+ return this.http.post("/files/bulk-upload", formData);
104
+ }
105
+ /**
106
+ * Get file metadata by ID
107
+ */
108
+ async get(fileId, userId) {
109
+ const params = userId ? { user_id: userId } : void 0;
110
+ return this.http.get(`/files/${fileId}`, { params });
111
+ }
112
+ /**
113
+ * List files with optional filters
114
+ */
115
+ async list(params) {
116
+ return this.http.get("/files", { params });
117
+ }
118
+ /**
119
+ * Get file content
120
+ */
121
+ async getContent(fileId, options) {
122
+ const params = {
123
+ content_type: options?.contentType,
124
+ start_line: options?.startLine,
125
+ end_line: options?.endLine,
126
+ user_id: options?.userId
127
+ };
128
+ return this.http.get(`/files/${fileId}/content`, { params });
129
+ }
130
+ /**
131
+ * Download original file
132
+ * Returns a redirect URL for blob storage or file content directly
133
+ */
134
+ async download(fileId, userId) {
135
+ const params = userId ? { user_id: userId } : void 0;
136
+ return this.http.request(`/files/${fileId}/download`, {
137
+ method: "GET",
138
+ params
139
+ });
140
+ }
141
+ /**
142
+ * Update file access scope
143
+ */
144
+ async updateScope(fileId, data) {
145
+ return this.http.patch(`/files/${fileId}/scope`, data);
146
+ }
147
+ /**
148
+ * Delete a file
149
+ */
150
+ async delete(fileId, options) {
151
+ const params = {
152
+ hard_delete: options?.hardDelete ?? true,
153
+ user_id: options?.userId
154
+ };
155
+ return this.http.delete(`/files/${fileId}`, { params });
156
+ }
157
+ /**
158
+ * Delete multiple files at once
159
+ */
160
+ async bulkDelete(data, options) {
161
+ const params = {
162
+ hard_delete: options?.hardDelete ?? true,
163
+ user_id: options?.userId
164
+ };
165
+ return this.http.request("/files/bulk", {
166
+ method: "DELETE",
167
+ body: data,
168
+ params
169
+ });
170
+ }
171
+ /**
172
+ * Get file processing status
173
+ */
174
+ async getStatus(fileId, userId) {
175
+ const params = userId ? { user_id: userId } : void 0;
176
+ return this.http.get(`/files/${fileId}/status`, { params });
177
+ }
178
+ /**
179
+ * Semantic search across files
180
+ */
181
+ async search(request) {
182
+ return this.http.post("/files/search", request);
183
+ }
184
+ /**
185
+ * Get file statistics for the tenant
186
+ */
187
+ async getStatistics() {
188
+ return this.http.get("/files/statistics");
189
+ }
190
+ }
191
+
192
+ class IntegrationsResource {
193
+ constructor(http) {
194
+ this.http = http;
195
+ }
196
+ /**
197
+ * Start an OAuth connection flow for a user
198
+ */
199
+ async initiateConnection(data) {
200
+ const requestData = {
201
+ ...data,
202
+ appName: data.appName.toUpperCase()
203
+ };
204
+ return this.http.post("/integrations/connections/initiate", requestData);
205
+ }
206
+ /**
207
+ * Check the status of a specific connection
208
+ */
209
+ async getConnectionStatus(userId, appName) {
210
+ return this.http.get(
211
+ `/integrations/connections/${encodeURIComponent(userId)}/${appName.toUpperCase()}`
212
+ );
213
+ }
214
+ /**
215
+ * Get all connections for a user
216
+ */
217
+ async getUserConnections(userId, appFilter) {
218
+ const params = new URLSearchParams();
219
+ if (appFilter)
220
+ params.append("app_filter", appFilter);
221
+ const query = params.toString() ? `?${params.toString()}` : "";
222
+ return this.http.get(
223
+ `/integrations/connections/${encodeURIComponent(userId)}${query}`
224
+ );
225
+ }
226
+ /**
227
+ * Get available tools for a user based on connections
228
+ */
229
+ async getTools(data) {
230
+ const requestData = {
231
+ ...data,
232
+ appFilter: data.appFilter?.map((app) => app.toUpperCase())
233
+ };
234
+ return this.http.post("/integrations/tools", requestData);
235
+ }
236
+ /**
237
+ * Disconnect a user from an external app
238
+ */
239
+ async disconnect(data) {
240
+ const requestData = {
241
+ ...data,
242
+ appName: data.appName.toUpperCase()
243
+ };
244
+ return this.http.post("/integrations/connections/disconnect", requestData);
245
+ }
246
+ /**
247
+ * Handle OAuth callback (for custom implementations)
248
+ */
249
+ async handleCallback(data) {
250
+ return this.http.post("/integrations/connections/callback", data);
251
+ }
252
+ /**
253
+ * List apps enabled for the tenant
254
+ */
255
+ async listApps(params) {
256
+ const urlParams = new URLSearchParams();
257
+ if (params?.includeAvailable)
258
+ urlParams.append("include_available", "true");
259
+ const query = urlParams.toString() ? `?${urlParams.toString()}` : "";
260
+ return this.http.get(`/integrations/apps${query}`);
261
+ }
262
+ /**
263
+ * Check if a specific app is enabled
264
+ */
265
+ async checkAppEnabled(appName) {
266
+ return this.http.get(
267
+ `/integrations/apps/${appName.toUpperCase()}/enabled`
268
+ );
269
+ }
270
+ /**
271
+ * Enable or disable an app for the tenant
272
+ */
273
+ async updateAppStatus(appName, enabled) {
274
+ return this.http.put(
275
+ `/integrations/apps/${appName.toUpperCase()}?enabled=${enabled}`
276
+ );
277
+ }
278
+ /**
279
+ * Get required fields for non-OAuth authentication (future)
280
+ */
281
+ async getNonOAuthRequiredFields(appName, authScheme) {
282
+ const params = new URLSearchParams({ auth_scheme: authScheme });
283
+ return this.http.get(
284
+ `/integrations/non-oauth/required-fields/${appName.toUpperCase()}?${params.toString()}`
285
+ );
286
+ }
287
+ // Aliases for backward compatibility with client methods
288
+ async isAppEnabled(appName) {
289
+ return this.checkAppEnabled(appName);
290
+ }
291
+ async setAppEnabled(appName, data) {
292
+ return this.updateAppStatus(appName, data.enabled);
293
+ }
294
+ async listConnections(userId, params) {
295
+ return this.getUserConnections(userId, params?.appFilter);
296
+ }
297
+ }
298
+
299
+ class MCPServersResource {
300
+ constructor(http) {
301
+ this.http = http;
302
+ }
303
+ /**
304
+ * Create a new MCP server configuration
305
+ */
306
+ async create(data) {
307
+ return this.http.post("/mcp-servers", data);
308
+ }
309
+ /**
310
+ * List MCP server configurations
311
+ */
312
+ async list(params) {
313
+ const queryParams = new URLSearchParams();
314
+ if (params?.scope)
315
+ queryParams.append("scope", params.scope);
316
+ if (params?.userIdentifier)
317
+ queryParams.append("user_identifier", params.userIdentifier);
318
+ if (params?.isActive !== void 0)
319
+ queryParams.append("is_active", params.isActive.toString());
320
+ if (params?.skip)
321
+ queryParams.append("skip", params.skip.toString());
322
+ if (params?.limit)
323
+ queryParams.append("limit", params.limit.toString());
324
+ const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
325
+ return this.http.get(`/mcp-servers${query}`);
326
+ }
327
+ /**
328
+ * Get a specific MCP server configuration
329
+ */
330
+ async get(serverId) {
331
+ return this.http.get(`/mcp-servers/${serverId}`);
332
+ }
333
+ /**
334
+ * Update an MCP server configuration
335
+ */
336
+ async update(serverId, data) {
337
+ return this.http.patch(`/mcp-servers/${serverId}`, data);
338
+ }
339
+ /**
340
+ * Delete an MCP server configuration
341
+ */
342
+ async delete(serverId) {
343
+ await this.http.delete(`/mcp-servers/${serverId}`);
344
+ }
345
+ /**
346
+ * List tools provided by an MCP server
347
+ * Note: Currently returns empty list, tool indexing coming in Phase 1
348
+ */
349
+ async listTools(serverId) {
350
+ return this.http.get(`/mcp-servers/${serverId}/tools`);
351
+ }
352
+ /**
353
+ * Test connection to an MCP server
354
+ * Note: Currently returns placeholder, testing coming in Phase 1
355
+ */
356
+ async testConnection(serverId) {
357
+ return this.http.post(`/mcp-servers/${serverId}/test`);
358
+ }
359
+ /**
360
+ * Test an MCP server configuration before saving
361
+ * Validates that the server can be connected to without creating a permanent configuration
362
+ */
363
+ async testConfig(config) {
364
+ return this.http.post("/mcp-servers/test", config);
365
+ }
366
+ }
367
+
368
+ class ModelPreferencesResource {
369
+ constructor(http) {
370
+ this.http = http;
371
+ }
372
+ /**
373
+ * Get all model preferences for the tenant
374
+ */
375
+ async get(includeDefaults = true) {
376
+ const params = new URLSearchParams();
377
+ params.append("include_defaults", includeDefaults.toString());
378
+ return this.http.get(`/model-preferences?${params.toString()}`);
379
+ }
380
+ /**
381
+ * Update multiple model preferences in a single request
382
+ */
383
+ async updateBulk(data) {
384
+ return this.http.put("/model-preferences", data);
385
+ }
386
+ /**
387
+ * Update a specific model type preference
388
+ */
389
+ async update(modelType, data) {
390
+ return this.http.patch(`/model-preferences/${modelType}`, data);
391
+ }
392
+ /**
393
+ * Delete a model preference to revert to system default
394
+ */
395
+ async delete(modelType) {
396
+ await this.http.delete(`/model-preferences/${modelType}`);
397
+ }
398
+ /**
399
+ * Check which models are available based on API key configuration
400
+ */
401
+ async checkAvailability(models) {
402
+ return this.http.post("/model-preferences/check-availability", models);
403
+ }
404
+ /**
405
+ * List all model preferences (alias for get)
406
+ */
407
+ async list(params) {
408
+ return this.get(params?.includeDefaults);
409
+ }
410
+ }
411
+
412
+ class LumnisError extends Error {
413
+ code;
414
+ statusCode;
415
+ details;
416
+ requestId;
417
+ constructor(message, options = {}) {
418
+ super(message);
419
+ this.name = "LumnisError";
420
+ this.code = options.code || "UNKNOWN_ERROR";
421
+ this.statusCode = options.statusCode;
422
+ this.details = options.details;
423
+ this.requestId = options.requestId;
424
+ Error.captureStackTrace(this, this.constructor);
425
+ }
426
+ }
427
+ class AuthenticationError extends LumnisError {
428
+ constructor(message, options = {}) {
429
+ super(message, { ...options, statusCode: options.statusCode || 401 });
430
+ this.name = "AuthenticationError";
431
+ }
432
+ }
433
+ class ValidationError extends LumnisError {
434
+ constructor(message, options = {}) {
435
+ super(message, { ...options, statusCode: options.statusCode || 400 });
436
+ this.name = "ValidationError";
437
+ }
438
+ }
439
+ class NotFoundError extends LumnisError {
440
+ constructor(message, options = {}) {
441
+ super(message, { ...options, statusCode: 404 });
442
+ this.name = "NotFoundError";
443
+ }
444
+ }
445
+ class RateLimitError extends LumnisError {
446
+ retryAfter;
447
+ constructor(options = {}) {
448
+ super("Rate limit exceeded", { ...options, statusCode: 429 });
449
+ this.name = "RateLimitError";
450
+ this.retryAfter = options.retryAfter;
451
+ }
452
+ }
453
+ class InternalServerError extends LumnisError {
454
+ constructor(message, options = {}) {
455
+ super(message, { ...options, statusCode: 500 });
456
+ this.name = "InternalServerError";
457
+ }
458
+ }
459
+ class LocalFileNotSupportedError extends ValidationError {
460
+ constructor(filePath) {
461
+ super(
462
+ `Local file paths are not supported yet: ${filePath}. Please wait for the artifact upload API or use artifact IDs.`,
463
+ { code: "LOCAL_FILE_NOT_SUPPORTED" }
464
+ );
465
+ this.name = "LocalFileNotSupportedError";
466
+ }
467
+ }
468
+
469
+ class ResponsesResource {
470
+ constructor(http) {
471
+ this.http = http;
472
+ }
473
+ _validateFileReference(uri) {
474
+ if (uri.startsWith("artifact_"))
475
+ return;
476
+ try {
477
+ const parsed = new URL(uri);
478
+ const allowedSchemes = [
479
+ "http",
480
+ "https",
481
+ "s3",
482
+ "gs",
483
+ "gcs",
484
+ "file",
485
+ "ftp",
486
+ "ftps",
487
+ "blob",
488
+ "data"
489
+ ];
490
+ if (allowedSchemes.includes(parsed.protocol.replace(":", ""))) {
491
+ if ((parsed.protocol === "http:" || parsed.protocol === "https \u043F\u0440\u043E\u0442\u043E\u043A\u043E\u043B:") && !parsed.hostname)
492
+ throw new LocalFileNotSupportedError(uri);
493
+ return;
494
+ } else {
495
+ throw new LocalFileNotSupportedError(uri);
496
+ }
497
+ } catch (error) {
498
+ if (error instanceof LocalFileNotSupportedError)
499
+ throw error;
500
+ }
501
+ const isLocalPath = uri.startsWith("/") || uri.startsWith("./") || uri.startsWith("../") || /^[a-z]:/i.test(uri) || uri.startsWith("\\\\") || uri.split("/").length === 1 && uri.includes(".") && !uri.startsWith("artifact_");
502
+ if (isLocalPath)
503
+ throw new LocalFileNotSupportedError(uri);
504
+ }
505
+ /**
506
+ * Create a new response request for asynchronous processing
507
+ */
508
+ async create(request) {
509
+ if (request.files) {
510
+ for (const file of request.files)
511
+ this._validateFileReference(file.uri);
512
+ }
513
+ return this.http.post("/responses", request);
514
+ }
515
+ /**
516
+ * Get response status and content
517
+ * @param responseId - The response ID
518
+ * @param options - Optional parameters
519
+ * @param options.wait - Wait time in seconds for long-polling
520
+ */
521
+ async get(responseId, options) {
522
+ return this.http.get(`/responses/${responseId}`, { params: options });
523
+ }
524
+ /**
525
+ * List responses with optional filtering
526
+ * @param params - Optional filter parameters
527
+ * @param params.userId - Filter by user ID
528
+ * @param params.status - Filter by response status
529
+ * @param params.startDate - Filter responses created after this date
530
+ * @param params.endDate - Filter responses created before this date
531
+ * @param params.limit - Maximum number of responses to return
532
+ * @param params.offset - Number of responses to skip for pagination
533
+ */
534
+ async list(params) {
535
+ const queryParams = {};
536
+ if (params?.userId)
537
+ queryParams.user_id = params.userId;
538
+ if (params?.status)
539
+ queryParams.status = params.status;
540
+ if (params?.startDate)
541
+ queryParams.start_date = params.startDate;
542
+ if (params?.endDate)
543
+ queryParams.end_date = params.endDate;
544
+ if (params?.limit)
545
+ queryParams.limit = params.limit;
546
+ if (params?.offset)
547
+ queryParams.offset = params.offset;
548
+ return this.http.get("/responses", { params: queryParams });
549
+ }
550
+ /**
551
+ * Cancel a queued or in-progress response
552
+ */
553
+ async cancel(responseId) {
554
+ return this.http.post(`/responses/${responseId}/cancel`);
555
+ }
556
+ /**
557
+ * List artifacts generated by a response
558
+ */
559
+ async listArtifacts(responseId, params) {
560
+ return this.http.get(`/responses/${responseId}/artifacts`, { params });
561
+ }
562
+ }
563
+
564
+ class TenantInfoResource {
565
+ constructor(http) {
566
+ this.http = http;
567
+ }
568
+ /**
569
+ * Get detailed information about a tenant
570
+ * Users can only access information about their own tenant
571
+ */
572
+ async get(tenantId) {
573
+ return this.http.get(`/tenants/${tenantId}`);
574
+ }
575
+ }
576
+
577
+ class ThreadsResource {
578
+ constructor(http) {
579
+ this.http = http;
580
+ }
581
+ /**
582
+ * Create a new thread
583
+ */
584
+ async create(params) {
585
+ return this.http.post("/threads", params);
586
+ }
587
+ /**
588
+ * List threads for the authenticated tenant
589
+ */
590
+ async list(params) {
591
+ return this.http.get("/threads", { params });
592
+ }
593
+ /**
594
+ * Get detailed information about a specific thread
595
+ */
596
+ async get(threadId) {
597
+ return this.http.get(`/threads/${encodeURIComponent(threadId)}`);
598
+ }
599
+ /**
600
+ * Get all responses associated with a specific thread
601
+ */
602
+ async getResponses(threadId, params) {
603
+ return this.http.get(`/threads/${encodeURIComponent(threadId)}/responses`, { params });
604
+ }
605
+ /**
606
+ * Update thread metadata
607
+ */
608
+ async update(threadId, data) {
609
+ return this.http.patch(`/threads/${encodeURIComponent(threadId)}`, data);
610
+ }
611
+ /**
612
+ * Delete a thread and all its associated responses
613
+ */
614
+ async delete(threadId) {
615
+ await this.http.delete(`/threads/${encodeURIComponent(threadId)}`);
616
+ }
617
+ }
618
+
619
+ class UsersResource {
620
+ constructor(http) {
621
+ this.http = http;
622
+ }
623
+ /**
624
+ * Create a new user within the tenant
625
+ * Returns existing user with 200 OK if user with same email already exists
626
+ */
627
+ async create(data) {
628
+ return this.http.post("/users", data);
629
+ }
630
+ /**
631
+ * List all users in the tenant with pagination
632
+ */
633
+ async list(params) {
634
+ const queryParams = new URLSearchParams();
635
+ if (params?.page)
636
+ queryParams.append("page", params.page.toString());
637
+ if (params?.pageSize)
638
+ queryParams.append("page_size", params.pageSize.toString());
639
+ const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
640
+ return this.http.get(`/users${query}`);
641
+ }
642
+ /**
643
+ * Get user details by ID or email
644
+ */
645
+ async get(userIdentifier) {
646
+ return this.http.get(`/users/${encodeURIComponent(userIdentifier)}`);
647
+ }
648
+ /**
649
+ * Update user information by ID or email
650
+ */
651
+ async update(userIdentifier, data) {
652
+ return this.http.put(`/users/${encodeURIComponent(userIdentifier)}`, data);
653
+ }
654
+ /**
655
+ * Delete (deactivate) a user by ID or email
656
+ */
657
+ async delete(userIdentifier) {
658
+ return this.http.delete(`/users/${encodeURIComponent(userIdentifier)}`);
659
+ }
660
+ /**
661
+ * Get all AI responses generated for a specific user
662
+ */
663
+ async getResponses(userIdentifier, params) {
664
+ const queryParams = new URLSearchParams();
665
+ if (params?.page)
666
+ queryParams.append("page", params.page.toString());
667
+ if (params?.pageSize)
668
+ queryParams.append("page_size", params.pageSize.toString());
669
+ const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
670
+ return this.http.get(`/users/${encodeURIComponent(userIdentifier)}/responses${query}`);
671
+ }
672
+ /**
673
+ * Get all conversation threads for a specific user
674
+ */
675
+ async getThreads(userIdentifier, params) {
676
+ const queryParams = new URLSearchParams();
677
+ if (params?.page)
678
+ queryParams.append("page", params.page.toString());
679
+ if (params?.pageSize)
680
+ queryParams.append("page_size", params.pageSize.toString());
681
+ const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
682
+ return this.http.get(`/users/${encodeURIComponent(userIdentifier)}/threads${query}`);
683
+ }
684
+ }
685
+
686
+ function toCamel(s) {
687
+ return s.replace(/([-_][a-z])/gi, ($1) => {
688
+ return $1.toUpperCase().replace("-", "").replace("_", "");
689
+ });
690
+ }
691
+ function toSnake(s) {
692
+ return s.replace(/[A-Z]/g, (letter, index) => {
693
+ return index === 0 ? letter.toLowerCase() : `_${letter.toLowerCase()}`;
694
+ });
695
+ }
696
+ function convertCase(obj, converter) {
697
+ if (Array.isArray(obj)) {
698
+ return obj.map((v) => convertCase(v, converter));
699
+ } else if (obj !== null && typeof obj === "object") {
700
+ return Object.keys(obj).reduce((acc, key) => {
701
+ acc[converter(key)] = convertCase(obj[key], converter);
702
+ return acc;
703
+ }, {});
704
+ }
705
+ return obj;
706
+ }
707
+ function toCamelCase(obj) {
708
+ return convertCase(obj, toCamel);
709
+ }
710
+ function toSnakeCase(obj) {
711
+ return convertCase(obj, toSnake);
712
+ }
713
+
714
+ class Http {
715
+ options;
716
+ constructor(options) {
717
+ this.options = {
718
+ baseUrl: options.baseUrl.replace(/\/$/, ""),
719
+ apiPrefix: options.apiPrefix || "",
720
+ headers: options.headers || {},
721
+ timeoutMs: options.timeoutMs || DEFAULT_TIMEOUT_MS,
722
+ maxRetries: options.maxRetries ?? DEFAULT_MAX_RETRIES,
723
+ backoffFactor: options.backoffFactor || DEFAULT_BACKOFF_FACTOR
724
+ };
725
+ }
726
+ async _handleResponse(response) {
727
+ const requestId = response.headers.get("x-request-id");
728
+ if (response.ok) {
729
+ if (response.status === 204)
730
+ return null;
731
+ const contentType = response.headers.get("content-type") || "";
732
+ if (contentType.includes("application/json")) {
733
+ const json = await response.json();
734
+ return toCamelCase(json);
735
+ }
736
+ return await response.text();
737
+ }
738
+ let detail = { raw: await response.text() };
739
+ try {
740
+ if (response.headers.get("content-type")?.includes("application/json"))
741
+ detail = JSON.parse(detail.raw);
742
+ } catch {
743
+ }
744
+ const errorMsg = detail?.error?.message || `Server error: ${response.status}`;
745
+ switch (response.status) {
746
+ case 401:
747
+ throw new AuthenticationError("Invalid or missing API key", { requestId, statusCode: 401, details: detail });
748
+ case 403:
749
+ throw new AuthenticationError("Forbidden - insufficient permissions", { requestId, statusCode: 403, details: detail });
750
+ case 404:
751
+ throw new NotFoundError("Resource not found", { requestId, statusCode: 404, details: detail });
752
+ case 429: {
753
+ const retryAfter = response.headers.get("Retry-After");
754
+ throw new RateLimitError({ requestId, statusCode: 429, details: detail, retryAfter });
755
+ }
756
+ default:
757
+ if (response.status >= 400 && response.status < 500)
758
+ throw new ValidationError(errorMsg, { requestId, statusCode: response.status, details: detail });
759
+ throw new LumnisError(`Server error: ${response.status}`, { requestId, statusCode: response.status, details: detail });
760
+ }
761
+ }
762
+ async request(path, init = {}) {
763
+ const { body, params, idempotencyKey: idempotencyKeyOption, ...fetchOptions } = init;
764
+ const method = fetchOptions.method || "GET";
765
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
766
+ const fullPath = this.options.apiPrefix ? `${this.options.apiPrefix}${normalizedPath}` : normalizedPath;
767
+ const url = new URL(this.options.baseUrl + fullPath);
768
+ if (params) {
769
+ for (const [key, value] of Object.entries(params)) {
770
+ if (value !== void 0 && value !== null)
771
+ url.searchParams.append(key, String(value));
772
+ }
773
+ }
774
+ const idempotencyKey = idempotencyKeyOption || (method !== "GET" ? crypto.randomUUID() : void 0);
775
+ const headers = {
776
+ "content-type": "application/json",
777
+ ...this.options.headers,
778
+ ...fetchOptions.headers,
779
+ ...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}
780
+ };
781
+ const isIdempotent = method === "GET" || method === "DELETE" || !!idempotencyKey;
782
+ const maxAttempts = isIdempotent ? this.options.maxRetries + 1 : 1;
783
+ let lastError;
784
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
785
+ const controller = new AbortController();
786
+ const timeout = setTimeout(() => controller.abort(), this.options.timeoutMs);
787
+ try {
788
+ const response = await fetch(url.toString(), {
789
+ ...fetchOptions,
790
+ method,
791
+ headers,
792
+ body: body ? JSON.stringify(toSnakeCase(body)) : void 0,
793
+ signal: controller.signal
794
+ });
795
+ return await this._handleResponse(response);
796
+ } catch (error) {
797
+ lastError = error;
798
+ if (error instanceof RateLimitError) {
799
+ const backoff = backoffMs(attempt, error.retryAfter);
800
+ await sleep(backoff);
801
+ continue;
802
+ }
803
+ if (error instanceof LumnisError && (error.statusCode ?? 0) < 500)
804
+ throw error;
805
+ if (attempt < maxAttempts - 1) {
806
+ const backoff = backoffMs(attempt);
807
+ await sleep(backoff);
808
+ }
809
+ } finally {
810
+ clearTimeout(timeout);
811
+ }
812
+ }
813
+ throw lastError || new LumnisError("Request failed after all retries", {});
814
+ }
815
+ get(path, options) {
816
+ return this.request(path, { ...options, method: "GET" });
817
+ }
818
+ post(path, body, options) {
819
+ return this.request(path, { ...options, method: "POST", body });
820
+ }
821
+ put(path, body, options) {
822
+ return this.request(path, { ...options, method: "PUT", body });
823
+ }
824
+ patch(path, body, options) {
825
+ return this.request(path, { ...options, method: "PATCH", body });
826
+ }
827
+ delete(path, options) {
828
+ return this.request(path, { ...options, method: "DELETE" });
829
+ }
830
+ async warnTenantScope() {
831
+ console.warn(
832
+ "Using TENANT scope bypasses user isolation. Ensure this is intentional and follows security best practices."
833
+ );
834
+ }
835
+ }
836
+ function sleep(ms) {
837
+ return new Promise((resolve) => setTimeout(resolve, ms));
838
+ }
839
+ function backoffMs(attempt, retryAfter = null) {
840
+ if (retryAfter) {
841
+ const retryAfterMs = Number(retryAfter) * 1e3;
842
+ if (!Number.isNaN(retryAfterMs))
843
+ return retryAfterMs;
844
+ }
845
+ const jitter = Math.random() * 500;
846
+ return Math.min(1e3 * 2 ** attempt, 1e4) + jitter;
847
+ }
848
+
849
+ class LumnisClient {
850
+ http;
851
+ tenantId;
852
+ // Resources
853
+ threads;
854
+ responses;
855
+ users;
856
+ files;
857
+ tenantInfo;
858
+ externalApiKeys;
859
+ integrations;
860
+ modelPreferences;
861
+ mcpServers;
862
+ _scopedUserId;
863
+ _defaultScope;
864
+ constructor(options = {}) {
865
+ const apiKey = options.apiKey || typeof process !== "undefined" && process.env?.LUMNISAI_API_KEY;
866
+ if (!apiKey) {
867
+ throw new Error("API key is required. Provide it via options.apiKey or set LUMNISAI_API_KEY environment variable.");
868
+ }
869
+ this.tenantId = options.tenantId || typeof process !== "undefined" && process.env?.LUMNISAI_TENANT_ID || void 0;
870
+ const baseUrl = options.baseUrl || typeof process !== "undefined" && process.env?.LUMNISAI_BASE_URL || DEFAULT_BASE_URL;
871
+ this._defaultScope = options.scope || "tenant";
872
+ this._scopedUserId = options._scopedUserId;
873
+ this.http = new Http({
874
+ baseUrl,
875
+ apiPrefix: "/v1",
876
+ headers: {
877
+ "X-API-Key": apiKey
878
+ },
879
+ timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
880
+ maxRetries: options.maxRetries ?? DEFAULT_MAX_RETRIES
881
+ });
882
+ this.threads = new ThreadsResource(this.http);
883
+ this.responses = new ResponsesResource(this.http);
884
+ this.users = new UsersResource(this.http);
885
+ this.files = new FilesResource(this.http);
886
+ this.tenantInfo = new TenantInfoResource(this.http);
887
+ this.externalApiKeys = new ExternalAPIKeysResource(this.http);
888
+ this.integrations = new IntegrationsResource(this.http);
889
+ this.modelPreferences = new ModelPreferencesResource(this.http);
890
+ this.mcpServers = new MCPServersResource(this.http);
891
+ }
892
+ forUser(userId) {
893
+ return new LumnisClient({
894
+ apiKey: this.http.options.headers["X-API-Key"],
895
+ tenantId: this.tenantId,
896
+ baseUrl: this.http.options.baseUrl,
897
+ timeoutMs: this.http.options.timeoutMs,
898
+ maxRetries: this.http.options.maxRetries,
899
+ scope: "user",
900
+ _scopedUserId: userId
901
+ });
902
+ }
903
+ async invoke(messages, options) {
904
+ const { stream = false, showProgress = true, ...restOptions } = options || {};
905
+ if (stream) {
906
+ if (showProgress)
907
+ console.warn("showProgress is not supported in streaming mode.");
908
+ return this._invokeStream(messages, restOptions);
909
+ } else {
910
+ let progressCallback;
911
+ if (showProgress)
912
+ progressCallback = createSimpleProgressCallback();
913
+ return this._invokeAndWait(messages, restOptions, progressCallback);
914
+ }
915
+ }
916
+ async *_invokeStream(messages, options) {
917
+ const response = await this._createResponse(messages, options);
918
+ console.log(`Response ID: ${response.responseId}`);
919
+ let lastMessageCount = 0;
920
+ while (true) {
921
+ const current = await this.responses.get(response.responseId, { wait: LONG_POLL_TIMEOUT_S });
922
+ const currentMessageCount = current.progress?.length || 0;
923
+ if (currentMessageCount > lastMessageCount && current.progress) {
924
+ for (let i = lastMessageCount; i < currentMessageCount; i++)
925
+ yield current.progress[i];
926
+ lastMessageCount = currentMessageCount;
927
+ }
928
+ if (current.status === "succeeded" || current.status === "failed" || current.status === "cancelled") {
929
+ if (current.status === "succeeded" && current.outputText) {
930
+ const progressEntry = {
931
+ ts: current.completedAt || (/* @__PURE__ */ new Date()).toISOString(),
932
+ state: "completed",
933
+ message: "Task completed successfully",
934
+ outputText: current.outputText
935
+ };
936
+ yield progressEntry;
937
+ }
938
+ break;
939
+ }
940
+ }
941
+ }
942
+ async _invokeAndWait(messages, options, progressCallback) {
943
+ const { pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, maxWaitMs = 3e5, ...createOptions } = options || {};
944
+ const response = await this._createResponse(messages, createOptions);
945
+ if (!progressCallback)
946
+ console.log(`Response ID: ${response.responseId}`);
947
+ const startTime = Date.now();
948
+ while (true) {
949
+ const current = await this.responses.get(response.responseId, { wait: LONG_POLL_TIMEOUT_S });
950
+ if (progressCallback)
951
+ progressCallback(current);
952
+ if (current.status === "succeeded" || current.status === "failed" || current.status === "cancelled")
953
+ return current;
954
+ if (Date.now() - startTime > maxWaitMs)
955
+ throw new Error(`Response ${response.responseId} timed out after ${maxWaitMs}ms`);
956
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
957
+ }
958
+ }
959
+ async _createResponse(messages, options) {
960
+ const { scope: optionScope, userId: optionUserId, ...restOptions } = options;
961
+ const effectiveUserId = optionUserId || this._scopedUserId;
962
+ let scope = optionScope || this._defaultScope;
963
+ if (effectiveUserId && scope === "tenant")
964
+ scope = "user";
965
+ if (scope === "user" && !effectiveUserId)
966
+ throw new Error("user_id is required for user scope");
967
+ if (scope === "tenant")
968
+ await this.http.warnTenantScope();
969
+ let messageArray;
970
+ if (typeof messages === "string")
971
+ messageArray = [{ role: "user", content: messages }];
972
+ else if (Array.isArray(messages))
973
+ messageArray = messages;
974
+ else
975
+ messageArray = [messages];
976
+ return this.responses.create({
977
+ messages: messageArray,
978
+ userId: effectiveUserId,
979
+ ...restOptions
980
+ });
981
+ }
982
+ // Thread methods
983
+ async listThreads(params) {
984
+ return this.threads.list(params);
985
+ }
986
+ async createThread(params) {
987
+ return this.threads.create(params);
988
+ }
989
+ async getThread(threadId) {
990
+ return this.threads.get(threadId);
991
+ }
992
+ async deleteThread(threadId) {
993
+ return this.threads.delete(threadId);
994
+ }
995
+ // User methods
996
+ async createUser(params) {
997
+ return this.users.create(params);
998
+ }
999
+ async getUser(userId) {
1000
+ return this.users.get(userId);
1001
+ }
1002
+ async updateUser(userId, params) {
1003
+ return this.users.update(userId, params);
1004
+ }
1005
+ async deleteUser(userId) {
1006
+ return this.users.delete(userId);
1007
+ }
1008
+ async listUsers(params) {
1009
+ return this.users.list(params);
1010
+ }
1011
+ // External API Key methods
1012
+ async addApiKey(params) {
1013
+ return this.externalApiKeys.create(params);
1014
+ }
1015
+ async listApiKeys() {
1016
+ return this.externalApiKeys.list();
1017
+ }
1018
+ async getApiKey(keyId) {
1019
+ return this.externalApiKeys.get(keyId);
1020
+ }
1021
+ async deleteApiKey(provider) {
1022
+ return this.externalApiKeys.delete(provider);
1023
+ }
1024
+ async getApiKeyMode() {
1025
+ return this.externalApiKeys.getMode();
1026
+ }
1027
+ async setApiKeyMode(mode) {
1028
+ return this.externalApiKeys.setMode(mode);
1029
+ }
1030
+ // Integration methods
1031
+ async listApps(params) {
1032
+ return this.integrations.listApps(params);
1033
+ }
1034
+ async isAppEnabled(appName) {
1035
+ return this.integrations.isAppEnabled(appName);
1036
+ }
1037
+ async setAppEnabled(appName, enabled) {
1038
+ return this.integrations.setAppEnabled(appName, { enabled });
1039
+ }
1040
+ async initiateConnection(params) {
1041
+ return this.integrations.initiateConnection(params);
1042
+ }
1043
+ async getConnectionStatus(userId, appName) {
1044
+ return this.integrations.getConnectionStatus(userId, appName);
1045
+ }
1046
+ async listConnections(userId, params) {
1047
+ return this.integrations.listConnections(userId, params);
1048
+ }
1049
+ async getIntegrationTools(userId, params) {
1050
+ return this.integrations.getTools({ userId, appFilter: params?.appFilter });
1051
+ }
1052
+ // Model Preference methods
1053
+ async getModelPreferences(params) {
1054
+ return this.modelPreferences.list(params);
1055
+ }
1056
+ async updateModelPreferences(params) {
1057
+ return this.modelPreferences.updateBulk(params);
1058
+ }
1059
+ // MCP Server methods
1060
+ async createMcpServer(params) {
1061
+ return this.mcpServers.create(params);
1062
+ }
1063
+ async getMcpServer(serverId) {
1064
+ return this.mcpServers.get(serverId);
1065
+ }
1066
+ async listMcpServers(params) {
1067
+ return this.mcpServers.list(params);
1068
+ }
1069
+ async updateMcpServer(serverId, params) {
1070
+ return this.mcpServers.update(serverId, params);
1071
+ }
1072
+ async deleteMcpServer(serverId) {
1073
+ return this.mcpServers.delete(serverId);
1074
+ }
1075
+ async listMcpServerTools(serverId) {
1076
+ return this.mcpServers.listTools(serverId);
1077
+ }
1078
+ async testMcpServer(serverId) {
1079
+ return this.mcpServers.testConnection(serverId);
1080
+ }
1081
+ }
1082
+ function createSimpleProgressCallback() {
1083
+ let lastStatus;
1084
+ const seenMessages = /* @__PURE__ */ new Set();
1085
+ return (response) => {
1086
+ if (response.status !== lastStatus) {
1087
+ console.log(`Status: ${response.status}`);
1088
+ lastStatus = response.status;
1089
+ }
1090
+ if (response.progress) {
1091
+ for (const entry of response.progress) {
1092
+ const messageKey = `${entry.state}:${entry.message}`;
1093
+ if (!seenMessages.has(messageKey)) {
1094
+ console.log(`${entry.state.toUpperCase()}: ${entry.message}`);
1095
+ seenMessages.add(messageKey);
1096
+ }
1097
+ }
1098
+ }
1099
+ };
1100
+ }
1101
+
1102
+ exports.AuthenticationError = AuthenticationError;
1103
+ exports.InternalServerError = InternalServerError;
1104
+ exports.LocalFileNotSupportedError = LocalFileNotSupportedError;
1105
+ exports.LumnisClient = LumnisClient;
1106
+ exports.LumnisError = LumnisError;
1107
+ exports.NotFoundError = NotFoundError;
1108
+ exports.RateLimitError = RateLimitError;
1109
+ exports.ValidationError = ValidationError;
1110
+ exports.default = LumnisClient;