@omnikit-ai/sdk 2.0.10 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +573 -13
- package/dist/index.d.ts +573 -13
- package/dist/index.js +571 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +568 -65
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
@@ -613,6 +660,20 @@ var APIClient = class {
|
|
|
613
660
|
description: "Create a signed URL for a private file",
|
|
614
661
|
method: "POST",
|
|
615
662
|
params: { file_uri: "string" }
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
name: "DownloadPrivateFile",
|
|
666
|
+
path: "/services/files/private/download",
|
|
667
|
+
description: "Download a private file via backend proxy (solves CORS issues)",
|
|
668
|
+
method: "POST",
|
|
669
|
+
params: { file_uri: "string" }
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
name: "DownloadFile",
|
|
673
|
+
path: "/services/files/{file_id}/download",
|
|
674
|
+
description: "Download a public file via backend proxy (like Supabase ?download)",
|
|
675
|
+
method: "GET",
|
|
676
|
+
params: { file_id: "string", download: "boolean", filename: "string" }
|
|
616
677
|
}
|
|
617
678
|
];
|
|
618
679
|
builtInServices.forEach((service) => {
|
|
@@ -758,13 +819,13 @@ var APIClient = class {
|
|
|
758
819
|
return this.createAuthProxy();
|
|
759
820
|
}
|
|
760
821
|
/**
|
|
761
|
-
*
|
|
762
|
-
* Only available when serviceToken is provided
|
|
822
|
+
* Service-level operations (elevated privileges).
|
|
823
|
+
* Only available when serviceToken is provided (e.g., via createServerClient).
|
|
763
824
|
*/
|
|
764
|
-
get
|
|
825
|
+
get service() {
|
|
765
826
|
if (!this._serviceToken) {
|
|
766
827
|
throw new OmnikitError(
|
|
767
|
-
"Service token is required
|
|
828
|
+
"Service token is required. Use createServerClient(req) in backend functions.",
|
|
768
829
|
403,
|
|
769
830
|
"SERVICE_TOKEN_REQUIRED"
|
|
770
831
|
);
|
|
@@ -777,12 +838,70 @@ var APIClient = class {
|
|
|
777
838
|
// Service role collections
|
|
778
839
|
services: this.createServicesProxy(true),
|
|
779
840
|
// Service role services (new flat structure)
|
|
780
|
-
integrations: this.createIntegrationsProxy(true)
|
|
841
|
+
integrations: this.createIntegrationsProxy(true),
|
|
781
842
|
// Service role integrations (legacy)
|
|
843
|
+
connectors: this.connectors
|
|
844
|
+
// Connectors module (requires service token)
|
|
782
845
|
// Note: auth not available in service role for security
|
|
783
846
|
};
|
|
784
847
|
return this._asServiceRole;
|
|
785
848
|
}
|
|
849
|
+
/**
|
|
850
|
+
* @deprecated Use service instead
|
|
851
|
+
*/
|
|
852
|
+
get asServiceRole() {
|
|
853
|
+
return this.service;
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Lazy getter for connectors module
|
|
857
|
+
* Like Base44's connectors - provides access to external service tokens
|
|
858
|
+
*
|
|
859
|
+
* SECURITY: getAccessToken requires service token authentication.
|
|
860
|
+
* Only use in backend functions, not frontend code.
|
|
861
|
+
*
|
|
862
|
+
* @example
|
|
863
|
+
* ```typescript
|
|
864
|
+
* // In a backend function
|
|
865
|
+
* const { access_token } = await omnikit.connectors.getAccessToken('slack');
|
|
866
|
+
*
|
|
867
|
+
* // Make direct Slack API call
|
|
868
|
+
* const response = await fetch('https://slack.com/api/chat.postMessage', {
|
|
869
|
+
* method: 'POST',
|
|
870
|
+
* headers: { Authorization: `Bearer ${access_token}` },
|
|
871
|
+
* body: JSON.stringify({ channel: '#general', text: 'Hello!' })
|
|
872
|
+
* });
|
|
873
|
+
* ```
|
|
874
|
+
*/
|
|
875
|
+
get connectors() {
|
|
876
|
+
if (!this._connectors) {
|
|
877
|
+
this._connectors = createConnectorsModule(
|
|
878
|
+
this.makeRequest.bind(this),
|
|
879
|
+
this.appId,
|
|
880
|
+
this.baseUrl,
|
|
881
|
+
() => this._serviceToken
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
return this._connectors;
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Resolve a return URL to an absolute URL.
|
|
888
|
+
* Handles relative paths like "/profile" by combining with current location.
|
|
889
|
+
* This fixes the OAuth redirect bug where relative URLs like "/profile" become
|
|
890
|
+
* "https://omnikit.ai/profile" instead of "https://omnikit.ai/app-builder/{id}/preview/profile"
|
|
891
|
+
*/
|
|
892
|
+
_resolveReturnUrl(returnUrl) {
|
|
893
|
+
if (typeof window === "undefined") return returnUrl || "/";
|
|
894
|
+
if (!returnUrl) return window.location.href;
|
|
895
|
+
if (returnUrl.startsWith("http://") || returnUrl.startsWith("https://")) {
|
|
896
|
+
return returnUrl;
|
|
897
|
+
}
|
|
898
|
+
if (returnUrl.startsWith("/")) {
|
|
899
|
+
const basePath = window.location.pathname.endsWith("/") ? window.location.pathname.slice(0, -1) : window.location.pathname;
|
|
900
|
+
return window.location.origin + basePath + returnUrl;
|
|
901
|
+
}
|
|
902
|
+
const base = window.location.origin + window.location.pathname;
|
|
903
|
+
return base.endsWith("/") ? base + returnUrl : base + "/" + returnUrl;
|
|
904
|
+
}
|
|
786
905
|
/**
|
|
787
906
|
* Create auth proxy that auto-initializes
|
|
788
907
|
*/
|
|
@@ -810,31 +929,42 @@ var APIClient = class {
|
|
|
810
929
|
client.emitUserChange(response);
|
|
811
930
|
return response;
|
|
812
931
|
},
|
|
813
|
-
login(returnUrl) {
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
} else {
|
|
826
|
-
window.location.href = `/login?return_url=${encodedReturnUrl}`;
|
|
932
|
+
async login(returnUrl) {
|
|
933
|
+
if (typeof window === "undefined") return;
|
|
934
|
+
const fullReturnUrl = client._resolveReturnUrl(returnUrl);
|
|
935
|
+
const token = getAccessToken();
|
|
936
|
+
if (token) {
|
|
937
|
+
try {
|
|
938
|
+
const isAuth = await this.isAuthenticated();
|
|
939
|
+
if (isAuth && returnUrl) {
|
|
940
|
+
window.location.href = fullReturnUrl;
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
} catch {
|
|
827
944
|
}
|
|
828
945
|
}
|
|
946
|
+
if (window.__omnikit_openLoginModal) {
|
|
947
|
+
window.__omnikit_openLoginModal(fullReturnUrl);
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
const encodedReturnUrl = encodeURIComponent(fullReturnUrl);
|
|
951
|
+
const currentPath = window.location.pathname;
|
|
952
|
+
const apiSitesMatch = currentPath.match(/^\/api\/sites\/([^\/]+)/);
|
|
953
|
+
if (apiSitesMatch) {
|
|
954
|
+
window.location.href = `/api/sites/${client.appId}/login?return_url=${encodedReturnUrl}`;
|
|
955
|
+
} else {
|
|
956
|
+
window.location.href = `/login?return_url=${encodedReturnUrl}`;
|
|
957
|
+
}
|
|
829
958
|
},
|
|
830
959
|
/**
|
|
831
960
|
* Request a passwordless login code to email
|
|
832
961
|
*/
|
|
833
962
|
async requestLoginCode(email, returnUrl) {
|
|
963
|
+
const fullReturnUrl = returnUrl ? client._resolveReturnUrl(returnUrl) : void 0;
|
|
834
964
|
return await client.makeRequest(
|
|
835
965
|
`${client.baseUrl}/auth/email/request-code`,
|
|
836
966
|
"POST",
|
|
837
|
-
{ email, return_url:
|
|
967
|
+
{ email, return_url: fullReturnUrl }
|
|
838
968
|
);
|
|
839
969
|
},
|
|
840
970
|
/**
|
|
@@ -882,44 +1012,43 @@ var APIClient = class {
|
|
|
882
1012
|
* Redirects to the backend OAuth endpoint for the specified provider
|
|
883
1013
|
*/
|
|
884
1014
|
loginWithProvider(providerId, returnUrl) {
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1015
|
+
if (typeof window === "undefined") {
|
|
1016
|
+
throw new OmnikitError("loginWithProvider() can only be called in browser environment", 400, "NOT_BROWSER");
|
|
1017
|
+
}
|
|
1018
|
+
const fullReturnUrl = client._resolveReturnUrl(returnUrl);
|
|
1019
|
+
const redirectUrl = `${client.baseUrl}/auth/${providerId}?redirect_url=${encodeURIComponent(fullReturnUrl)}`;
|
|
1020
|
+
const inIframe = window.self !== window.top;
|
|
1021
|
+
if (inIframe) {
|
|
1022
|
+
const width = 600;
|
|
1023
|
+
const height = 700;
|
|
1024
|
+
const left = window.screen.width / 2 - width / 2;
|
|
1025
|
+
const top = window.screen.height / 2 - height / 2;
|
|
1026
|
+
const popup = window.open(
|
|
1027
|
+
redirectUrl,
|
|
1028
|
+
"oauth_popup",
|
|
1029
|
+
`width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes,status=yes`
|
|
1030
|
+
);
|
|
1031
|
+
if (popup) {
|
|
1032
|
+
const checkTimer = setInterval(() => {
|
|
1033
|
+
const token = getAccessToken();
|
|
1034
|
+
if (token) {
|
|
1035
|
+
clearInterval(checkTimer);
|
|
1036
|
+
popup.close();
|
|
1037
|
+
window.location.reload();
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
if (popup.closed) {
|
|
1041
|
+
clearInterval(checkTimer);
|
|
1042
|
+
const finalToken = getAccessToken();
|
|
1043
|
+
if (finalToken) {
|
|
905
1044
|
window.location.reload();
|
|
906
|
-
return;
|
|
907
1045
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
if (finalToken) {
|
|
912
|
-
window.location.reload();
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
}, 1e3);
|
|
916
|
-
return;
|
|
917
|
-
}
|
|
1046
|
+
}
|
|
1047
|
+
}, 1e3);
|
|
1048
|
+
return;
|
|
918
1049
|
}
|
|
919
|
-
window.location.href = redirectUrl;
|
|
920
|
-
} else {
|
|
921
|
-
throw new OmnikitError("loginWithProvider() can only be called in browser environment", 400, "NOT_BROWSER");
|
|
922
1050
|
}
|
|
1051
|
+
window.location.href = redirectUrl;
|
|
923
1052
|
},
|
|
924
1053
|
/**
|
|
925
1054
|
* Initiate Google OAuth Login
|
|
@@ -1131,7 +1260,8 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1131
1260
|
// Remove callback functions from request body
|
|
1132
1261
|
onToken: void 0,
|
|
1133
1262
|
onComplete: void 0,
|
|
1134
|
-
onError: void 0
|
|
1263
|
+
onError: void 0,
|
|
1264
|
+
onToolCall: void 0
|
|
1135
1265
|
};
|
|
1136
1266
|
Object.keys(requestBody).forEach((key) => {
|
|
1137
1267
|
if (requestBody[key] === void 0) {
|
|
@@ -1182,6 +1312,14 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1182
1312
|
if (event.type === "token" && event.content) {
|
|
1183
1313
|
fullResponse += event.content;
|
|
1184
1314
|
params.onToken?.(event.content);
|
|
1315
|
+
} else if (event.type === "tool_call") {
|
|
1316
|
+
if (params.onToolCall && event.id && event.name) {
|
|
1317
|
+
params.onToolCall({
|
|
1318
|
+
id: event.id,
|
|
1319
|
+
name: event.name,
|
|
1320
|
+
arguments: event.arguments || {}
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1185
1323
|
} else if (event.type === "done") {
|
|
1186
1324
|
params.onComplete?.({
|
|
1187
1325
|
result: event.result || fullResponse,
|
|
@@ -1240,6 +1378,56 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1240
1378
|
);
|
|
1241
1379
|
};
|
|
1242
1380
|
}
|
|
1381
|
+
if (normalizedName === "DownloadFile") {
|
|
1382
|
+
return async function(params) {
|
|
1383
|
+
await client.ensureInitialized();
|
|
1384
|
+
if (!params?.file_id) {
|
|
1385
|
+
throw new OmnikitError(
|
|
1386
|
+
"file_id is required for DownloadFile",
|
|
1387
|
+
400,
|
|
1388
|
+
"MISSING_FILE_ID"
|
|
1389
|
+
);
|
|
1390
|
+
}
|
|
1391
|
+
const downloadUrl = new URL(`${client.baseUrl}/apps/${client.appId}/services/files/${params.file_id}/download`);
|
|
1392
|
+
if (params.download !== void 0) {
|
|
1393
|
+
downloadUrl.searchParams.set("download", String(params.download));
|
|
1394
|
+
}
|
|
1395
|
+
if (params.filename) {
|
|
1396
|
+
downloadUrl.searchParams.set("filename", params.filename);
|
|
1397
|
+
}
|
|
1398
|
+
const response = await fetch(downloadUrl.toString(), {
|
|
1399
|
+
method: "GET"
|
|
1400
|
+
});
|
|
1401
|
+
if (!response.ok) {
|
|
1402
|
+
const error = await response.json().catch(() => ({ detail: "Download failed" }));
|
|
1403
|
+
throw new OmnikitError(
|
|
1404
|
+
error.detail || "Download failed",
|
|
1405
|
+
response.status,
|
|
1406
|
+
"DOWNLOAD_FAILED"
|
|
1407
|
+
);
|
|
1408
|
+
}
|
|
1409
|
+
const blob = await response.blob();
|
|
1410
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
1411
|
+
let filename = params.filename;
|
|
1412
|
+
if (!filename) {
|
|
1413
|
+
const contentDisposition = response.headers.get("Content-Disposition");
|
|
1414
|
+
if (contentDisposition) {
|
|
1415
|
+
const match = contentDisposition.match(/filename="?([^"]+)"?/);
|
|
1416
|
+
if (match) filename = match[1];
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
if (!filename) {
|
|
1420
|
+
filename = `file-${params.file_id}`;
|
|
1421
|
+
}
|
|
1422
|
+
const link = document.createElement("a");
|
|
1423
|
+
link.href = blobUrl;
|
|
1424
|
+
link.download = filename;
|
|
1425
|
+
document.body.appendChild(link);
|
|
1426
|
+
link.click();
|
|
1427
|
+
document.body.removeChild(link);
|
|
1428
|
+
URL.revokeObjectURL(blobUrl);
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1243
1431
|
if (normalizedName === "DownloadPrivateFile") {
|
|
1244
1432
|
return async function(params) {
|
|
1245
1433
|
await client.ensureInitialized();
|
|
@@ -1250,16 +1438,47 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1250
1438
|
"MISSING_FILE_URI"
|
|
1251
1439
|
);
|
|
1252
1440
|
}
|
|
1253
|
-
const downloadUrl =
|
|
1254
|
-
downloadUrl.searchParams.set("file_uri", params.file_uri);
|
|
1255
|
-
if (params.filename) {
|
|
1256
|
-
downloadUrl.searchParams.set("filename", params.filename);
|
|
1257
|
-
}
|
|
1441
|
+
const downloadUrl = `${client.baseUrl}/apps/${client.appId}/services/files/private/download`;
|
|
1258
1442
|
const token = client.getAuthToken();
|
|
1259
|
-
|
|
1260
|
-
|
|
1443
|
+
const response = await fetch(downloadUrl, {
|
|
1444
|
+
method: "POST",
|
|
1445
|
+
headers: {
|
|
1446
|
+
"Content-Type": "application/json",
|
|
1447
|
+
...token ? { "Authorization": `Bearer ${token}` } : {}
|
|
1448
|
+
},
|
|
1449
|
+
body: JSON.stringify({ file_uri: params.file_uri })
|
|
1450
|
+
});
|
|
1451
|
+
if (!response.ok) {
|
|
1452
|
+
const error = await response.json().catch(() => ({ detail: "Download failed" }));
|
|
1453
|
+
throw new OmnikitError(
|
|
1454
|
+
error.detail || "Download failed",
|
|
1455
|
+
response.status,
|
|
1456
|
+
"DOWNLOAD_FAILED"
|
|
1457
|
+
);
|
|
1458
|
+
}
|
|
1459
|
+
const blob = await response.blob();
|
|
1460
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
1461
|
+
let filename = params.filename;
|
|
1462
|
+
if (!filename) {
|
|
1463
|
+
const contentDisposition = response.headers.get("Content-Disposition");
|
|
1464
|
+
if (contentDisposition) {
|
|
1465
|
+
const match = contentDisposition.match(/filename="?([^"]+)"?/);
|
|
1466
|
+
if (match) filename = match[1];
|
|
1467
|
+
}
|
|
1261
1468
|
}
|
|
1262
|
-
|
|
1469
|
+
if (!filename) {
|
|
1470
|
+
const uriParts = params.file_uri.split("/");
|
|
1471
|
+
const lastPart = uriParts[uriParts.length - 1];
|
|
1472
|
+
const underscoreIdx = lastPart.indexOf("_");
|
|
1473
|
+
filename = underscoreIdx > 0 ? lastPart.slice(underscoreIdx + 1) : lastPart;
|
|
1474
|
+
}
|
|
1475
|
+
const link = document.createElement("a");
|
|
1476
|
+
link.href = blobUrl;
|
|
1477
|
+
link.download = filename;
|
|
1478
|
+
document.body.appendChild(link);
|
|
1479
|
+
link.click();
|
|
1480
|
+
document.body.removeChild(link);
|
|
1481
|
+
URL.revokeObjectURL(blobUrl);
|
|
1263
1482
|
};
|
|
1264
1483
|
}
|
|
1265
1484
|
return async function(params, asyncOptions) {
|
|
@@ -1278,7 +1497,7 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1278
1497
|
response = await method(params, useServiceToken);
|
|
1279
1498
|
} else {
|
|
1280
1499
|
throw new OmnikitError(
|
|
1281
|
-
`Service '${serviceName}' not found. Known services: SendEmail, InvokeLLM, GenerateImage, GenerateSpeech, GenerateVideo, ExtractData, SendSMS, UploadFile, UploadPrivateFile, CreateFileSignedUrl (camelCase also supported)`,
|
|
1500
|
+
`Service '${serviceName}' not found. Known services: SendEmail, InvokeLLM, GenerateImage, GenerateSpeech, GenerateVideo, ExtractData, SendSMS, UploadFile, UploadPrivateFile, CreateFileSignedUrl, DownloadFile, DownloadPrivateFile (camelCase also supported)`,
|
|
1282
1501
|
404,
|
|
1283
1502
|
"SERVICE_NOT_FOUND"
|
|
1284
1503
|
);
|
|
@@ -1794,6 +2013,8 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1794
2013
|
"files": "UploadFile",
|
|
1795
2014
|
"files/private": "UploadPrivateFile",
|
|
1796
2015
|
"files/signed-url": "CreateFileSignedUrl",
|
|
2016
|
+
"files/private/download": "DownloadPrivateFile",
|
|
2017
|
+
"files/download": "DownloadFile",
|
|
1797
2018
|
"images": "GenerateImage",
|
|
1798
2019
|
"speech": "GenerateSpeech",
|
|
1799
2020
|
"video": "GenerateVideo",
|
|
@@ -2236,12 +2457,298 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
2236
2457
|
function createClient(config) {
|
|
2237
2458
|
return new APIClient(config);
|
|
2238
2459
|
}
|
|
2460
|
+
function createServerClient(request) {
|
|
2461
|
+
const getHeader = (name) => {
|
|
2462
|
+
if (typeof request.headers.get === "function") {
|
|
2463
|
+
return request.headers.get(name);
|
|
2464
|
+
}
|
|
2465
|
+
return request.headers[name] || request.headers[name.toLowerCase()] || null;
|
|
2466
|
+
};
|
|
2467
|
+
const authHeader = getHeader("Authorization") || getHeader("authorization");
|
|
2468
|
+
const userToken = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
|
|
2469
|
+
const serviceToken = getHeader("X-Omnikit-Service-Authorization") || getHeader("x-omnikit-service-authorization");
|
|
2470
|
+
const appId = getHeader("X-Omnikit-App-Id") || getHeader("x-omnikit-app-id");
|
|
2471
|
+
const serverUrl = getHeader("X-Omnikit-Server-Url") || getHeader("x-omnikit-server-url") || "https://omnikit.ai/api";
|
|
2472
|
+
if (!appId) {
|
|
2473
|
+
throw new OmnikitError(
|
|
2474
|
+
"X-Omnikit-App-Id header is required. This function should be invoked through the Omnikit platform.",
|
|
2475
|
+
400,
|
|
2476
|
+
"MISSING_APP_ID"
|
|
2477
|
+
);
|
|
2478
|
+
}
|
|
2479
|
+
return createClient({
|
|
2480
|
+
appId,
|
|
2481
|
+
serverUrl,
|
|
2482
|
+
token: userToken || void 0,
|
|
2483
|
+
serviceToken: serviceToken || void 0,
|
|
2484
|
+
autoInitAuth: false
|
|
2485
|
+
// Don't auto-detect from localStorage in backend
|
|
2486
|
+
});
|
|
2487
|
+
}
|
|
2488
|
+
var createClientFromRequest = createServerClient;
|
|
2489
|
+
|
|
2490
|
+
// src/analytics.ts
|
|
2491
|
+
var STORAGE_KEY = "omnikit_session";
|
|
2492
|
+
var SESSION_TIMEOUT = 30 * 60 * 1e3;
|
|
2493
|
+
var FLUSH_INTERVAL = 5e3;
|
|
2494
|
+
var MAX_RETRIES = 3;
|
|
2495
|
+
var Analytics = class {
|
|
2496
|
+
constructor(config) {
|
|
2497
|
+
this.eventQueue = [];
|
|
2498
|
+
this.config = config;
|
|
2499
|
+
this.enabled = config.enabled !== false;
|
|
2500
|
+
this.sessionId = config.sessionId || this.initSession();
|
|
2501
|
+
this.userId = config.userId;
|
|
2502
|
+
if (this.enabled && typeof window !== "undefined") {
|
|
2503
|
+
this.startFlushTimer();
|
|
2504
|
+
window.addEventListener("beforeunload", () => this.flush());
|
|
2505
|
+
window.addEventListener("pagehide", () => this.flush());
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
// ==========================================================================
|
|
2509
|
+
// Session Management
|
|
2510
|
+
// ==========================================================================
|
|
2511
|
+
initSession() {
|
|
2512
|
+
if (typeof window === "undefined" || typeof localStorage === "undefined") {
|
|
2513
|
+
return this.generateId();
|
|
2514
|
+
}
|
|
2515
|
+
try {
|
|
2516
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
2517
|
+
if (stored) {
|
|
2518
|
+
const { id, timestamp } = JSON.parse(stored);
|
|
2519
|
+
if (Date.now() - timestamp < SESSION_TIMEOUT) {
|
|
2520
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify({
|
|
2521
|
+
id,
|
|
2522
|
+
timestamp: Date.now()
|
|
2523
|
+
}));
|
|
2524
|
+
return id;
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
} catch {
|
|
2528
|
+
}
|
|
2529
|
+
const newId = this.generateId();
|
|
2530
|
+
this.saveSession(newId);
|
|
2531
|
+
return newId;
|
|
2532
|
+
}
|
|
2533
|
+
generateId() {
|
|
2534
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
2535
|
+
return crypto.randomUUID();
|
|
2536
|
+
}
|
|
2537
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
2538
|
+
const r = Math.random() * 16 | 0;
|
|
2539
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
2540
|
+
return v.toString(16);
|
|
2541
|
+
});
|
|
2542
|
+
}
|
|
2543
|
+
saveSession(id) {
|
|
2544
|
+
if (typeof localStorage === "undefined") return;
|
|
2545
|
+
try {
|
|
2546
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify({
|
|
2547
|
+
id,
|
|
2548
|
+
timestamp: Date.now()
|
|
2549
|
+
}));
|
|
2550
|
+
} catch {
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
/**
|
|
2554
|
+
* Get the current session ID
|
|
2555
|
+
*/
|
|
2556
|
+
getSessionId() {
|
|
2557
|
+
return this.sessionId;
|
|
2558
|
+
}
|
|
2559
|
+
/**
|
|
2560
|
+
* Start a new session (e.g., after logout)
|
|
2561
|
+
*/
|
|
2562
|
+
startNewSession() {
|
|
2563
|
+
this.sessionId = this.generateId();
|
|
2564
|
+
this.saveSession(this.sessionId);
|
|
2565
|
+
return this.sessionId;
|
|
2566
|
+
}
|
|
2567
|
+
// ==========================================================================
|
|
2568
|
+
// User Identification
|
|
2569
|
+
// ==========================================================================
|
|
2570
|
+
/**
|
|
2571
|
+
* Associate events with a user ID
|
|
2572
|
+
*/
|
|
2573
|
+
setUserId(userId) {
|
|
2574
|
+
this.userId = userId;
|
|
2575
|
+
}
|
|
2576
|
+
/**
|
|
2577
|
+
* Clear user association (e.g., on logout)
|
|
2578
|
+
*/
|
|
2579
|
+
clearUserId() {
|
|
2580
|
+
this.userId = void 0;
|
|
2581
|
+
}
|
|
2582
|
+
// ==========================================================================
|
|
2583
|
+
// Core Logging Methods
|
|
2584
|
+
// ==========================================================================
|
|
2585
|
+
/**
|
|
2586
|
+
* Log a custom event
|
|
2587
|
+
*/
|
|
2588
|
+
async logEvent(eventType, payload = {}) {
|
|
2589
|
+
if (!this.enabled) return;
|
|
2590
|
+
this.eventQueue.push({
|
|
2591
|
+
eventType,
|
|
2592
|
+
payload,
|
|
2593
|
+
timestamp: Date.now()
|
|
2594
|
+
});
|
|
2595
|
+
if (eventType === "error" || eventType === "api_error") {
|
|
2596
|
+
await this.flush();
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
/**
|
|
2600
|
+
* Log a page view event
|
|
2601
|
+
*/
|
|
2602
|
+
async logPageView(pageName, metadata) {
|
|
2603
|
+
if (!this.enabled) return;
|
|
2604
|
+
const url = typeof window !== "undefined" ? window.location.href : "";
|
|
2605
|
+
const referrer = typeof document !== "undefined" ? document.referrer : "";
|
|
2606
|
+
await this.logEvent("page_view", {
|
|
2607
|
+
page_name: pageName,
|
|
2608
|
+
metadata: {
|
|
2609
|
+
url,
|
|
2610
|
+
referrer,
|
|
2611
|
+
...metadata
|
|
2612
|
+
}
|
|
2613
|
+
});
|
|
2614
|
+
}
|
|
2615
|
+
/**
|
|
2616
|
+
* Log an error event
|
|
2617
|
+
*/
|
|
2618
|
+
async logError(error, componentStack) {
|
|
2619
|
+
if (!this.enabled) return;
|
|
2620
|
+
const url = typeof window !== "undefined" ? window.location.href : "";
|
|
2621
|
+
await this.logEvent("error", {
|
|
2622
|
+
error_message: error.message,
|
|
2623
|
+
error_stack: error.stack,
|
|
2624
|
+
metadata: {
|
|
2625
|
+
name: error.name,
|
|
2626
|
+
component_stack: componentStack,
|
|
2627
|
+
url
|
|
2628
|
+
}
|
|
2629
|
+
});
|
|
2630
|
+
}
|
|
2631
|
+
/**
|
|
2632
|
+
* Log an API error event
|
|
2633
|
+
*/
|
|
2634
|
+
async logApiError(endpoint, statusCode, errorMessage, metadata) {
|
|
2635
|
+
if (!this.enabled) return;
|
|
2636
|
+
await this.logEvent("api_error", {
|
|
2637
|
+
error_message: errorMessage,
|
|
2638
|
+
metadata: {
|
|
2639
|
+
endpoint,
|
|
2640
|
+
status_code: statusCode,
|
|
2641
|
+
...metadata
|
|
2642
|
+
}
|
|
2643
|
+
});
|
|
2644
|
+
}
|
|
2645
|
+
// ==========================================================================
|
|
2646
|
+
// Event Batching & Flushing
|
|
2647
|
+
// ==========================================================================
|
|
2648
|
+
startFlushTimer() {
|
|
2649
|
+
if (this.flushTimer) return;
|
|
2650
|
+
this.flushTimer = setInterval(() => {
|
|
2651
|
+
if (this.eventQueue.length > 0) {
|
|
2652
|
+
this.flush();
|
|
2653
|
+
}
|
|
2654
|
+
}, FLUSH_INTERVAL);
|
|
2655
|
+
}
|
|
2656
|
+
/**
|
|
2657
|
+
* Flush all queued events to the server
|
|
2658
|
+
*/
|
|
2659
|
+
async flush() {
|
|
2660
|
+
if (this.eventQueue.length === 0) return;
|
|
2661
|
+
const events = [...this.eventQueue];
|
|
2662
|
+
this.eventQueue = [];
|
|
2663
|
+
const sendPromises = events.map(
|
|
2664
|
+
({ eventType, payload }) => this.sendEvent(eventType, payload)
|
|
2665
|
+
);
|
|
2666
|
+
await Promise.allSettled(sendPromises);
|
|
2667
|
+
}
|
|
2668
|
+
async sendEvent(eventType, payload) {
|
|
2669
|
+
const pageName = payload.page_name || (typeof window !== "undefined" ? window.location.pathname : "/");
|
|
2670
|
+
const url = `${this.config.apiUrl}/api/app-logs/${this.config.appId}/log-event`;
|
|
2671
|
+
const body = {
|
|
2672
|
+
session_id: this.sessionId,
|
|
2673
|
+
user_id: this.userId,
|
|
2674
|
+
event_type: eventType,
|
|
2675
|
+
page_name: pageName,
|
|
2676
|
+
action: payload.action,
|
|
2677
|
+
inputs: payload.inputs,
|
|
2678
|
+
metadata: payload.metadata,
|
|
2679
|
+
is_error: eventType === "error" || eventType === "api_error",
|
|
2680
|
+
error_message: payload.error_message,
|
|
2681
|
+
error_stack: payload.error_stack
|
|
2682
|
+
};
|
|
2683
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
2684
|
+
try {
|
|
2685
|
+
const response = await fetch(url, {
|
|
2686
|
+
method: "POST",
|
|
2687
|
+
headers: { "Content-Type": "application/json" },
|
|
2688
|
+
body: JSON.stringify(body),
|
|
2689
|
+
keepalive: true
|
|
2690
|
+
// Ensures request completes even on page unload
|
|
2691
|
+
});
|
|
2692
|
+
if (response.ok) {
|
|
2693
|
+
return;
|
|
2694
|
+
}
|
|
2695
|
+
if (response.status >= 400 && response.status < 500) {
|
|
2696
|
+
console.warn(`[Omnikit Analytics] Event rejected: ${response.status}`);
|
|
2697
|
+
return;
|
|
2698
|
+
}
|
|
2699
|
+
} catch (err) {
|
|
2700
|
+
}
|
|
2701
|
+
if (attempt < MAX_RETRIES - 1) {
|
|
2702
|
+
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1e3));
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
console.warn("[Omnikit Analytics] Failed to send event after retries");
|
|
2706
|
+
}
|
|
2707
|
+
// ==========================================================================
|
|
2708
|
+
// Lifecycle
|
|
2709
|
+
// ==========================================================================
|
|
2710
|
+
/**
|
|
2711
|
+
* Enable or disable analytics
|
|
2712
|
+
*/
|
|
2713
|
+
setEnabled(enabled) {
|
|
2714
|
+
this.enabled = enabled;
|
|
2715
|
+
if (enabled && typeof window !== "undefined") {
|
|
2716
|
+
this.startFlushTimer();
|
|
2717
|
+
} else if (this.flushTimer) {
|
|
2718
|
+
clearInterval(this.flushTimer);
|
|
2719
|
+
this.flushTimer = void 0;
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
/**
|
|
2723
|
+
* Check if analytics is enabled
|
|
2724
|
+
*/
|
|
2725
|
+
isEnabled() {
|
|
2726
|
+
return this.enabled;
|
|
2727
|
+
}
|
|
2728
|
+
/**
|
|
2729
|
+
* Clean up resources
|
|
2730
|
+
*/
|
|
2731
|
+
destroy() {
|
|
2732
|
+
if (this.flushTimer) {
|
|
2733
|
+
clearInterval(this.flushTimer);
|
|
2734
|
+
this.flushTimer = void 0;
|
|
2735
|
+
}
|
|
2736
|
+
this.flush();
|
|
2737
|
+
}
|
|
2738
|
+
};
|
|
2739
|
+
function createAnalytics(config) {
|
|
2740
|
+
return new Analytics(config);
|
|
2741
|
+
}
|
|
2239
2742
|
|
|
2240
2743
|
exports.APIClient = APIClient;
|
|
2744
|
+
exports.Analytics = Analytics;
|
|
2241
2745
|
exports.LiveVoiceSessionImpl = LiveVoiceSessionImpl;
|
|
2242
2746
|
exports.OmnikitError = OmnikitError;
|
|
2243
2747
|
exports.cleanTokenFromUrl = cleanTokenFromUrl;
|
|
2748
|
+
exports.createAnalytics = createAnalytics;
|
|
2244
2749
|
exports.createClient = createClient;
|
|
2750
|
+
exports.createClientFromRequest = createClientFromRequest;
|
|
2751
|
+
exports.createServerClient = createServerClient;
|
|
2245
2752
|
exports.getAccessToken = getAccessToken;
|
|
2246
2753
|
exports.isTokenInUrl = isTokenInUrl;
|
|
2247
2754
|
exports.removeAccessToken = removeAccessToken;
|