@omnikit-ai/sdk 2.0.10 → 2.2.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.js CHANGED
@@ -435,6 +435,53 @@ var LiveVoiceSessionImpl = class {
435
435
  }
436
436
  };
437
437
 
438
+ // src/connectors.ts
439
+ function createConnectorsModule(makeRequest, appId, baseUrl, getServiceToken) {
440
+ return {
441
+ async getAccessToken(connectorType) {
442
+ const serviceToken = getServiceToken();
443
+ if (!serviceToken) {
444
+ throw new Error(
445
+ "Service token is required to get connector access token. This method is only available in backend functions. Make sure you created the client with a serviceToken."
446
+ );
447
+ }
448
+ return makeRequest(
449
+ `${baseUrl}/apps/${appId}/connectors/${connectorType}/access-token`,
450
+ "GET",
451
+ null,
452
+ { useServiceToken: true }
453
+ );
454
+ },
455
+ async isConnected(connectorType) {
456
+ try {
457
+ const response = await makeRequest(
458
+ `${baseUrl}/apps/${appId}/connectors/${connectorType}`,
459
+ "GET"
460
+ );
461
+ return response?.connector?.status === "connected";
462
+ } catch {
463
+ return false;
464
+ }
465
+ },
466
+ async getStatus(connectorType) {
467
+ try {
468
+ const response = await makeRequest(
469
+ `${baseUrl}/apps/${appId}/connectors/${connectorType}`,
470
+ "GET"
471
+ );
472
+ return {
473
+ success: true,
474
+ connector: response?.connector
475
+ };
476
+ } catch (error) {
477
+ return {
478
+ success: false
479
+ };
480
+ }
481
+ }
482
+ };
483
+ }
484
+
438
485
  // src/client.ts
439
486
  var LLM_MODEL_MAP = {
440
487
  // Gemini 2.5 models
@@ -758,13 +805,13 @@ var APIClient = class {
758
805
  return this.createAuthProxy();
759
806
  }
760
807
  /**
761
- * Lazy getter for service role operations
762
- * Only available when serviceToken is provided
808
+ * Service-level operations (elevated privileges).
809
+ * Only available when serviceToken is provided (e.g., via createServerClient).
763
810
  */
764
- get asServiceRole() {
811
+ get service() {
765
812
  if (!this._serviceToken) {
766
813
  throw new OmnikitError(
767
- "Service token is required to use asServiceRole. Provide serviceToken in config.",
814
+ "Service token is required. Use createServerClient(req) in backend functions.",
768
815
  403,
769
816
  "SERVICE_TOKEN_REQUIRED"
770
817
  );
@@ -777,12 +824,51 @@ var APIClient = class {
777
824
  // Service role collections
778
825
  services: this.createServicesProxy(true),
779
826
  // Service role services (new flat structure)
780
- integrations: this.createIntegrationsProxy(true)
827
+ integrations: this.createIntegrationsProxy(true),
781
828
  // Service role integrations (legacy)
829
+ connectors: this.connectors
830
+ // Connectors module (requires service token)
782
831
  // Note: auth not available in service role for security
783
832
  };
784
833
  return this._asServiceRole;
785
834
  }
835
+ /**
836
+ * @deprecated Use service instead
837
+ */
838
+ get asServiceRole() {
839
+ return this.service;
840
+ }
841
+ /**
842
+ * Lazy getter for connectors module
843
+ * Like Base44's connectors - provides access to external service tokens
844
+ *
845
+ * SECURITY: getAccessToken requires service token authentication.
846
+ * Only use in backend functions, not frontend code.
847
+ *
848
+ * @example
849
+ * ```typescript
850
+ * // In a backend function
851
+ * const { access_token } = await omnikit.connectors.getAccessToken('slack');
852
+ *
853
+ * // Make direct Slack API call
854
+ * const response = await fetch('https://slack.com/api/chat.postMessage', {
855
+ * method: 'POST',
856
+ * headers: { Authorization: `Bearer ${access_token}` },
857
+ * body: JSON.stringify({ channel: '#general', text: 'Hello!' })
858
+ * });
859
+ * ```
860
+ */
861
+ get connectors() {
862
+ if (!this._connectors) {
863
+ this._connectors = createConnectorsModule(
864
+ this.makeRequest.bind(this),
865
+ this.appId,
866
+ this.baseUrl,
867
+ () => this._serviceToken
868
+ );
869
+ }
870
+ return this._connectors;
871
+ }
786
872
  /**
787
873
  * Create auth proxy that auto-initializes
788
874
  */
@@ -2236,12 +2322,298 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
2236
2322
  function createClient(config) {
2237
2323
  return new APIClient(config);
2238
2324
  }
2325
+ function createServerClient(request) {
2326
+ const getHeader = (name) => {
2327
+ if (typeof request.headers.get === "function") {
2328
+ return request.headers.get(name);
2329
+ }
2330
+ return request.headers[name] || request.headers[name.toLowerCase()] || null;
2331
+ };
2332
+ const authHeader = getHeader("Authorization") || getHeader("authorization");
2333
+ const userToken = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
2334
+ const serviceToken = getHeader("X-Omnikit-Service-Authorization") || getHeader("x-omnikit-service-authorization");
2335
+ const appId = getHeader("X-Omnikit-App-Id") || getHeader("x-omnikit-app-id");
2336
+ const serverUrl = getHeader("X-Omnikit-Server-Url") || getHeader("x-omnikit-server-url") || "https://omnikit.ai/api";
2337
+ if (!appId) {
2338
+ throw new OmnikitError(
2339
+ "X-Omnikit-App-Id header is required. This function should be invoked through the Omnikit platform.",
2340
+ 400,
2341
+ "MISSING_APP_ID"
2342
+ );
2343
+ }
2344
+ return createClient({
2345
+ appId,
2346
+ serverUrl,
2347
+ token: userToken || void 0,
2348
+ serviceToken: serviceToken || void 0,
2349
+ autoInitAuth: false
2350
+ // Don't auto-detect from localStorage in backend
2351
+ });
2352
+ }
2353
+ var createClientFromRequest = createServerClient;
2354
+
2355
+ // src/analytics.ts
2356
+ var STORAGE_KEY = "omnikit_session";
2357
+ var SESSION_TIMEOUT = 30 * 60 * 1e3;
2358
+ var FLUSH_INTERVAL = 5e3;
2359
+ var MAX_RETRIES = 3;
2360
+ var Analytics = class {
2361
+ constructor(config) {
2362
+ this.eventQueue = [];
2363
+ this.config = config;
2364
+ this.enabled = config.enabled !== false;
2365
+ this.sessionId = config.sessionId || this.initSession();
2366
+ this.userId = config.userId;
2367
+ if (this.enabled && typeof window !== "undefined") {
2368
+ this.startFlushTimer();
2369
+ window.addEventListener("beforeunload", () => this.flush());
2370
+ window.addEventListener("pagehide", () => this.flush());
2371
+ }
2372
+ }
2373
+ // ==========================================================================
2374
+ // Session Management
2375
+ // ==========================================================================
2376
+ initSession() {
2377
+ if (typeof window === "undefined" || typeof localStorage === "undefined") {
2378
+ return this.generateId();
2379
+ }
2380
+ try {
2381
+ const stored = localStorage.getItem(STORAGE_KEY);
2382
+ if (stored) {
2383
+ const { id, timestamp } = JSON.parse(stored);
2384
+ if (Date.now() - timestamp < SESSION_TIMEOUT) {
2385
+ localStorage.setItem(STORAGE_KEY, JSON.stringify({
2386
+ id,
2387
+ timestamp: Date.now()
2388
+ }));
2389
+ return id;
2390
+ }
2391
+ }
2392
+ } catch {
2393
+ }
2394
+ const newId = this.generateId();
2395
+ this.saveSession(newId);
2396
+ return newId;
2397
+ }
2398
+ generateId() {
2399
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
2400
+ return crypto.randomUUID();
2401
+ }
2402
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
2403
+ const r = Math.random() * 16 | 0;
2404
+ const v = c === "x" ? r : r & 3 | 8;
2405
+ return v.toString(16);
2406
+ });
2407
+ }
2408
+ saveSession(id) {
2409
+ if (typeof localStorage === "undefined") return;
2410
+ try {
2411
+ localStorage.setItem(STORAGE_KEY, JSON.stringify({
2412
+ id,
2413
+ timestamp: Date.now()
2414
+ }));
2415
+ } catch {
2416
+ }
2417
+ }
2418
+ /**
2419
+ * Get the current session ID
2420
+ */
2421
+ getSessionId() {
2422
+ return this.sessionId;
2423
+ }
2424
+ /**
2425
+ * Start a new session (e.g., after logout)
2426
+ */
2427
+ startNewSession() {
2428
+ this.sessionId = this.generateId();
2429
+ this.saveSession(this.sessionId);
2430
+ return this.sessionId;
2431
+ }
2432
+ // ==========================================================================
2433
+ // User Identification
2434
+ // ==========================================================================
2435
+ /**
2436
+ * Associate events with a user ID
2437
+ */
2438
+ setUserId(userId) {
2439
+ this.userId = userId;
2440
+ }
2441
+ /**
2442
+ * Clear user association (e.g., on logout)
2443
+ */
2444
+ clearUserId() {
2445
+ this.userId = void 0;
2446
+ }
2447
+ // ==========================================================================
2448
+ // Core Logging Methods
2449
+ // ==========================================================================
2450
+ /**
2451
+ * Log a custom event
2452
+ */
2453
+ async logEvent(eventType, payload = {}) {
2454
+ if (!this.enabled) return;
2455
+ this.eventQueue.push({
2456
+ eventType,
2457
+ payload,
2458
+ timestamp: Date.now()
2459
+ });
2460
+ if (eventType === "error" || eventType === "api_error") {
2461
+ await this.flush();
2462
+ }
2463
+ }
2464
+ /**
2465
+ * Log a page view event
2466
+ */
2467
+ async logPageView(pageName, metadata) {
2468
+ if (!this.enabled) return;
2469
+ const url = typeof window !== "undefined" ? window.location.href : "";
2470
+ const referrer = typeof document !== "undefined" ? document.referrer : "";
2471
+ await this.logEvent("page_view", {
2472
+ page_name: pageName,
2473
+ metadata: {
2474
+ url,
2475
+ referrer,
2476
+ ...metadata
2477
+ }
2478
+ });
2479
+ }
2480
+ /**
2481
+ * Log an error event
2482
+ */
2483
+ async logError(error, componentStack) {
2484
+ if (!this.enabled) return;
2485
+ const url = typeof window !== "undefined" ? window.location.href : "";
2486
+ await this.logEvent("error", {
2487
+ error_message: error.message,
2488
+ error_stack: error.stack,
2489
+ metadata: {
2490
+ name: error.name,
2491
+ component_stack: componentStack,
2492
+ url
2493
+ }
2494
+ });
2495
+ }
2496
+ /**
2497
+ * Log an API error event
2498
+ */
2499
+ async logApiError(endpoint, statusCode, errorMessage, metadata) {
2500
+ if (!this.enabled) return;
2501
+ await this.logEvent("api_error", {
2502
+ error_message: errorMessage,
2503
+ metadata: {
2504
+ endpoint,
2505
+ status_code: statusCode,
2506
+ ...metadata
2507
+ }
2508
+ });
2509
+ }
2510
+ // ==========================================================================
2511
+ // Event Batching & Flushing
2512
+ // ==========================================================================
2513
+ startFlushTimer() {
2514
+ if (this.flushTimer) return;
2515
+ this.flushTimer = setInterval(() => {
2516
+ if (this.eventQueue.length > 0) {
2517
+ this.flush();
2518
+ }
2519
+ }, FLUSH_INTERVAL);
2520
+ }
2521
+ /**
2522
+ * Flush all queued events to the server
2523
+ */
2524
+ async flush() {
2525
+ if (this.eventQueue.length === 0) return;
2526
+ const events = [...this.eventQueue];
2527
+ this.eventQueue = [];
2528
+ const sendPromises = events.map(
2529
+ ({ eventType, payload }) => this.sendEvent(eventType, payload)
2530
+ );
2531
+ await Promise.allSettled(sendPromises);
2532
+ }
2533
+ async sendEvent(eventType, payload) {
2534
+ const pageName = payload.page_name || (typeof window !== "undefined" ? window.location.pathname : "/");
2535
+ const url = `${this.config.apiUrl}/api/app-logs/${this.config.appId}/log-event`;
2536
+ const body = {
2537
+ session_id: this.sessionId,
2538
+ user_id: this.userId,
2539
+ event_type: eventType,
2540
+ page_name: pageName,
2541
+ action: payload.action,
2542
+ inputs: payload.inputs,
2543
+ metadata: payload.metadata,
2544
+ is_error: eventType === "error" || eventType === "api_error",
2545
+ error_message: payload.error_message,
2546
+ error_stack: payload.error_stack
2547
+ };
2548
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
2549
+ try {
2550
+ const response = await fetch(url, {
2551
+ method: "POST",
2552
+ headers: { "Content-Type": "application/json" },
2553
+ body: JSON.stringify(body),
2554
+ keepalive: true
2555
+ // Ensures request completes even on page unload
2556
+ });
2557
+ if (response.ok) {
2558
+ return;
2559
+ }
2560
+ if (response.status >= 400 && response.status < 500) {
2561
+ console.warn(`[Omnikit Analytics] Event rejected: ${response.status}`);
2562
+ return;
2563
+ }
2564
+ } catch (err) {
2565
+ }
2566
+ if (attempt < MAX_RETRIES - 1) {
2567
+ await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1e3));
2568
+ }
2569
+ }
2570
+ console.warn("[Omnikit Analytics] Failed to send event after retries");
2571
+ }
2572
+ // ==========================================================================
2573
+ // Lifecycle
2574
+ // ==========================================================================
2575
+ /**
2576
+ * Enable or disable analytics
2577
+ */
2578
+ setEnabled(enabled) {
2579
+ this.enabled = enabled;
2580
+ if (enabled && typeof window !== "undefined") {
2581
+ this.startFlushTimer();
2582
+ } else if (this.flushTimer) {
2583
+ clearInterval(this.flushTimer);
2584
+ this.flushTimer = void 0;
2585
+ }
2586
+ }
2587
+ /**
2588
+ * Check if analytics is enabled
2589
+ */
2590
+ isEnabled() {
2591
+ return this.enabled;
2592
+ }
2593
+ /**
2594
+ * Clean up resources
2595
+ */
2596
+ destroy() {
2597
+ if (this.flushTimer) {
2598
+ clearInterval(this.flushTimer);
2599
+ this.flushTimer = void 0;
2600
+ }
2601
+ this.flush();
2602
+ }
2603
+ };
2604
+ function createAnalytics(config) {
2605
+ return new Analytics(config);
2606
+ }
2239
2607
 
2240
2608
  exports.APIClient = APIClient;
2609
+ exports.Analytics = Analytics;
2241
2610
  exports.LiveVoiceSessionImpl = LiveVoiceSessionImpl;
2242
2611
  exports.OmnikitError = OmnikitError;
2243
2612
  exports.cleanTokenFromUrl = cleanTokenFromUrl;
2613
+ exports.createAnalytics = createAnalytics;
2244
2614
  exports.createClient = createClient;
2615
+ exports.createClientFromRequest = createClientFromRequest;
2616
+ exports.createServerClient = createServerClient;
2245
2617
  exports.getAccessToken = getAccessToken;
2246
2618
  exports.isTokenInUrl = isTokenInUrl;
2247
2619
  exports.removeAccessToken = removeAccessToken;