@omnikit-ai/sdk 2.2.0 → 2.2.2
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 +547 -13
- package/dist/index.d.ts +547 -13
- package/dist/index.js +490 -69
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +490 -69
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -660,6 +660,20 @@ var APIClient = class {
|
|
|
660
660
|
description: "Create a signed URL for a private file",
|
|
661
661
|
method: "POST",
|
|
662
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" }
|
|
663
677
|
}
|
|
664
678
|
];
|
|
665
679
|
builtInServices.forEach((service) => {
|
|
@@ -869,6 +883,25 @@ var APIClient = class {
|
|
|
869
883
|
}
|
|
870
884
|
return this._connectors;
|
|
871
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
|
+
}
|
|
872
905
|
/**
|
|
873
906
|
* Create auth proxy that auto-initializes
|
|
874
907
|
*/
|
|
@@ -896,31 +929,42 @@ var APIClient = class {
|
|
|
896
929
|
client.emitUserChange(response);
|
|
897
930
|
return response;
|
|
898
931
|
},
|
|
899
|
-
login(returnUrl) {
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
} else {
|
|
912
|
-
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 {
|
|
913
944
|
}
|
|
914
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
|
+
}
|
|
915
958
|
},
|
|
916
959
|
/**
|
|
917
960
|
* Request a passwordless login code to email
|
|
918
961
|
*/
|
|
919
962
|
async requestLoginCode(email, returnUrl) {
|
|
963
|
+
const fullReturnUrl = returnUrl ? client._resolveReturnUrl(returnUrl) : void 0;
|
|
920
964
|
return await client.makeRequest(
|
|
921
965
|
`${client.baseUrl}/auth/email/request-code`,
|
|
922
966
|
"POST",
|
|
923
|
-
{ email, return_url:
|
|
967
|
+
{ email, return_url: fullReturnUrl }
|
|
924
968
|
);
|
|
925
969
|
},
|
|
926
970
|
/**
|
|
@@ -968,44 +1012,43 @@ var APIClient = class {
|
|
|
968
1012
|
* Redirects to the backend OAuth endpoint for the specified provider
|
|
969
1013
|
*/
|
|
970
1014
|
loginWithProvider(providerId, returnUrl) {
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
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) {
|
|
991
1044
|
window.location.reload();
|
|
992
|
-
return;
|
|
993
1045
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
if (finalToken) {
|
|
998
|
-
window.location.reload();
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
}, 1e3);
|
|
1002
|
-
return;
|
|
1003
|
-
}
|
|
1046
|
+
}
|
|
1047
|
+
}, 1e3);
|
|
1048
|
+
return;
|
|
1004
1049
|
}
|
|
1005
|
-
window.location.href = redirectUrl;
|
|
1006
|
-
} else {
|
|
1007
|
-
throw new OmnikitError("loginWithProvider() can only be called in browser environment", 400, "NOT_BROWSER");
|
|
1008
1050
|
}
|
|
1051
|
+
window.location.href = redirectUrl;
|
|
1009
1052
|
},
|
|
1010
1053
|
/**
|
|
1011
1054
|
* Initiate Google OAuth Login
|
|
@@ -1217,7 +1260,8 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1217
1260
|
// Remove callback functions from request body
|
|
1218
1261
|
onToken: void 0,
|
|
1219
1262
|
onComplete: void 0,
|
|
1220
|
-
onError: void 0
|
|
1263
|
+
onError: void 0,
|
|
1264
|
+
onToolCall: void 0
|
|
1221
1265
|
};
|
|
1222
1266
|
Object.keys(requestBody).forEach((key) => {
|
|
1223
1267
|
if (requestBody[key] === void 0) {
|
|
@@ -1268,6 +1312,14 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1268
1312
|
if (event.type === "token" && event.content) {
|
|
1269
1313
|
fullResponse += event.content;
|
|
1270
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
|
+
}
|
|
1271
1323
|
} else if (event.type === "done") {
|
|
1272
1324
|
params.onComplete?.({
|
|
1273
1325
|
result: event.result || fullResponse,
|
|
@@ -1326,6 +1378,56 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1326
1378
|
);
|
|
1327
1379
|
};
|
|
1328
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
|
+
}
|
|
1329
1431
|
if (normalizedName === "DownloadPrivateFile") {
|
|
1330
1432
|
return async function(params) {
|
|
1331
1433
|
await client.ensureInitialized();
|
|
@@ -1336,16 +1438,47 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1336
1438
|
"MISSING_FILE_URI"
|
|
1337
1439
|
);
|
|
1338
1440
|
}
|
|
1339
|
-
const downloadUrl =
|
|
1340
|
-
downloadUrl.searchParams.set("file_uri", params.file_uri);
|
|
1341
|
-
if (params.filename) {
|
|
1342
|
-
downloadUrl.searchParams.set("filename", params.filename);
|
|
1343
|
-
}
|
|
1441
|
+
const downloadUrl = `${client.baseUrl}/apps/${client.appId}/services/files/private/download`;
|
|
1344
1442
|
const token = client.getAuthToken();
|
|
1345
|
-
|
|
1346
|
-
|
|
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
|
+
}
|
|
1347
1468
|
}
|
|
1348
|
-
|
|
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);
|
|
1349
1482
|
};
|
|
1350
1483
|
}
|
|
1351
1484
|
return async function(params, asyncOptions) {
|
|
@@ -1364,7 +1497,7 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1364
1497
|
response = await method(params, useServiceToken);
|
|
1365
1498
|
} else {
|
|
1366
1499
|
throw new OmnikitError(
|
|
1367
|
-
`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)`,
|
|
1368
1501
|
404,
|
|
1369
1502
|
"SERVICE_NOT_FOUND"
|
|
1370
1503
|
);
|
|
@@ -1417,6 +1550,18 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1417
1550
|
if (params?.model) {
|
|
1418
1551
|
params = { ...params, model: mapLLMModel(params.model) };
|
|
1419
1552
|
}
|
|
1553
|
+
if (params?.saveResult) {
|
|
1554
|
+
params = {
|
|
1555
|
+
...params,
|
|
1556
|
+
save_result: {
|
|
1557
|
+
collection: params.saveResult.collection,
|
|
1558
|
+
document_id: params.saveResult.documentId,
|
|
1559
|
+
field: params.saveResult.field,
|
|
1560
|
+
additional_fields: params.saveResult.additionalFields
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
delete params.saveResult;
|
|
1564
|
+
}
|
|
1420
1565
|
}
|
|
1421
1566
|
const integrationPackage = client._integrations[packageName];
|
|
1422
1567
|
if (!integrationPackage) {
|
|
@@ -1531,7 +1676,7 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1531
1676
|
);
|
|
1532
1677
|
return response;
|
|
1533
1678
|
},
|
|
1534
|
-
// List all records with MongoDB/Mongoose-style filtering
|
|
1679
|
+
// List all records with MongoDB/Mongoose-style filtering and cursor pagination
|
|
1535
1680
|
async list(...args) {
|
|
1536
1681
|
const queryParams = new URLSearchParams();
|
|
1537
1682
|
let filter;
|
|
@@ -1576,11 +1721,25 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1576
1721
|
if (options.offset !== void 0) {
|
|
1577
1722
|
queryParams.append("offset", String(options.offset));
|
|
1578
1723
|
}
|
|
1724
|
+
if (options.after !== void 0) {
|
|
1725
|
+
queryParams.append("after", options.after);
|
|
1726
|
+
}
|
|
1579
1727
|
}
|
|
1580
1728
|
}
|
|
1581
1729
|
const url = queryParams.toString() ? `${client.baseUrl}/apps/${client.appId}/collections/${collectionName}?${queryParams}` : `${client.baseUrl}/apps/${client.appId}/collections/${collectionName}`;
|
|
1582
1730
|
const response = await client.makeRequest(url, "GET");
|
|
1583
|
-
|
|
1731
|
+
if (response && typeof response === "object" && "data" in response && "hasMore" in response) {
|
|
1732
|
+
return {
|
|
1733
|
+
data: makeArrayForgiving(response.data),
|
|
1734
|
+
hasMore: response.hasMore,
|
|
1735
|
+
nextCursor: response.nextCursor
|
|
1736
|
+
};
|
|
1737
|
+
}
|
|
1738
|
+
return {
|
|
1739
|
+
data: makeArrayForgiving(Array.isArray(response) ? response : []),
|
|
1740
|
+
hasMore: false,
|
|
1741
|
+
nextCursor: null
|
|
1742
|
+
};
|
|
1584
1743
|
},
|
|
1585
1744
|
// Filter records by query (alias for list)
|
|
1586
1745
|
async filter(...args) {
|
|
@@ -1589,21 +1748,21 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1589
1748
|
// Find single record matching query
|
|
1590
1749
|
async findOne(...args) {
|
|
1591
1750
|
if (args.length === 0) {
|
|
1592
|
-
const
|
|
1593
|
-
return
|
|
1751
|
+
const result = await this.list({}, { limit: 1 });
|
|
1752
|
+
return result.data[0] || null;
|
|
1594
1753
|
} else if (args.length === 1) {
|
|
1595
1754
|
const arg = args[0];
|
|
1596
1755
|
if (arg && (arg.q !== void 0 || arg._count !== void 0)) {
|
|
1597
|
-
const
|
|
1598
|
-
return
|
|
1756
|
+
const result = await this.list({ ...arg, limit: 1 });
|
|
1757
|
+
return result.data[0] || null;
|
|
1599
1758
|
} else {
|
|
1600
|
-
const
|
|
1601
|
-
return
|
|
1759
|
+
const result = await this.list(arg, { limit: 1 });
|
|
1760
|
+
return result.data[0] || null;
|
|
1602
1761
|
}
|
|
1603
1762
|
} else {
|
|
1604
1763
|
const [filter, options] = args;
|
|
1605
|
-
const
|
|
1606
|
-
return
|
|
1764
|
+
const result = await this.list(filter, { ...options, limit: 1 });
|
|
1765
|
+
return result.data[0] || null;
|
|
1607
1766
|
}
|
|
1608
1767
|
},
|
|
1609
1768
|
// Create new record
|
|
@@ -1880,6 +2039,8 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1880
2039
|
"files": "UploadFile",
|
|
1881
2040
|
"files/private": "UploadPrivateFile",
|
|
1882
2041
|
"files/signed-url": "CreateFileSignedUrl",
|
|
2042
|
+
"files/private/download": "DownloadPrivateFile",
|
|
2043
|
+
"files/download": "DownloadFile",
|
|
1883
2044
|
"images": "GenerateImage",
|
|
1884
2045
|
"speech": "GenerateSpeech",
|
|
1885
2046
|
"video": "GenerateVideo",
|
|
@@ -2227,6 +2388,266 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
2227
2388
|
config
|
|
2228
2389
|
);
|
|
2229
2390
|
}
|
|
2391
|
+
/**
|
|
2392
|
+
* Run an assistant with streaming response.
|
|
2393
|
+
*
|
|
2394
|
+
* Assistants are server-side AI agents with custom instructions and built-in actions.
|
|
2395
|
+
* They can create/update/delete records, send emails, make HTTP requests, etc.
|
|
2396
|
+
*
|
|
2397
|
+
* @example
|
|
2398
|
+
* ```typescript
|
|
2399
|
+
* const result = await omnikit.runAssistant('customer_support', {
|
|
2400
|
+
* messages: [{ role: 'user', content: 'Help me track my order #12345' }],
|
|
2401
|
+
* onToken: (token) => setResponse(prev => prev + token),
|
|
2402
|
+
* onActionStart: (action) => console.log(`Starting: ${action.name}`),
|
|
2403
|
+
* onActionComplete: (action) => console.log(`Completed: ${action.name}`),
|
|
2404
|
+
* });
|
|
2405
|
+
*
|
|
2406
|
+
* // Continue the conversation
|
|
2407
|
+
* await omnikit.runAssistant('customer_support', {
|
|
2408
|
+
* messages: [{ role: 'user', content: 'When will it arrive?' }],
|
|
2409
|
+
* threadId: result.threadId,
|
|
2410
|
+
* onToken: (token) => setResponse(prev => prev + token),
|
|
2411
|
+
* });
|
|
2412
|
+
* ```
|
|
2413
|
+
*
|
|
2414
|
+
* @param assistantName - Name of the assistant to run
|
|
2415
|
+
* @param params - Run parameters including messages and callbacks
|
|
2416
|
+
* @returns Promise resolving to the run result with threadId
|
|
2417
|
+
*/
|
|
2418
|
+
async runAssistant(assistantName, params) {
|
|
2419
|
+
await this.ensureInitialized();
|
|
2420
|
+
const headers = {
|
|
2421
|
+
"Content-Type": "application/json"
|
|
2422
|
+
};
|
|
2423
|
+
const token = this.getAuthToken();
|
|
2424
|
+
if (token) {
|
|
2425
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
2426
|
+
}
|
|
2427
|
+
if (this._serviceToken) {
|
|
2428
|
+
headers["X-Service-Token"] = this._serviceToken;
|
|
2429
|
+
}
|
|
2430
|
+
if (this._apiKey) {
|
|
2431
|
+
headers["X-API-Key"] = this._apiKey;
|
|
2432
|
+
}
|
|
2433
|
+
const requestBody = {
|
|
2434
|
+
messages: params.messages,
|
|
2435
|
+
thread_id: params.threadId,
|
|
2436
|
+
stream: true
|
|
2437
|
+
};
|
|
2438
|
+
const response = await fetch(
|
|
2439
|
+
`${this.baseUrl}/apps/${this.appId}/assistants/${assistantName}/runs`,
|
|
2440
|
+
{
|
|
2441
|
+
method: "POST",
|
|
2442
|
+
headers,
|
|
2443
|
+
body: JSON.stringify(requestBody)
|
|
2444
|
+
}
|
|
2445
|
+
);
|
|
2446
|
+
if (!response.ok) {
|
|
2447
|
+
const errorText = await response.text();
|
|
2448
|
+
let errorMessage = `Assistant run failed: ${response.statusText}`;
|
|
2449
|
+
try {
|
|
2450
|
+
const errorJson = JSON.parse(errorText);
|
|
2451
|
+
errorMessage = errorJson.detail || errorJson.message || errorMessage;
|
|
2452
|
+
} catch {
|
|
2453
|
+
errorMessage = errorText || errorMessage;
|
|
2454
|
+
}
|
|
2455
|
+
const err = new OmnikitError(errorMessage, response.status, "ASSISTANT_ERROR");
|
|
2456
|
+
params.onError?.(err);
|
|
2457
|
+
throw err;
|
|
2458
|
+
}
|
|
2459
|
+
const reader = response.body?.getReader();
|
|
2460
|
+
if (!reader) {
|
|
2461
|
+
const err = new OmnikitError("No response body", 500, "NO_BODY");
|
|
2462
|
+
params.onError?.(err);
|
|
2463
|
+
throw err;
|
|
2464
|
+
}
|
|
2465
|
+
const decoder = new TextDecoder();
|
|
2466
|
+
let fullResponse = "";
|
|
2467
|
+
let buffer = "";
|
|
2468
|
+
let threadId = params.threadId || "";
|
|
2469
|
+
let messageCount = 0;
|
|
2470
|
+
try {
|
|
2471
|
+
while (true) {
|
|
2472
|
+
const { done, value } = await reader.read();
|
|
2473
|
+
if (done) break;
|
|
2474
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2475
|
+
const lines = buffer.split("\n");
|
|
2476
|
+
buffer = lines.pop() || "";
|
|
2477
|
+
for (const line of lines) {
|
|
2478
|
+
if (line.startsWith("data: ")) {
|
|
2479
|
+
try {
|
|
2480
|
+
const event = JSON.parse(line.slice(6));
|
|
2481
|
+
if (event.type === "token" && event.content) {
|
|
2482
|
+
fullResponse += event.content;
|
|
2483
|
+
params.onToken?.(event.content);
|
|
2484
|
+
} else if (event.type === "action_start") {
|
|
2485
|
+
params.onActionStart?.({
|
|
2486
|
+
id: event.id || "",
|
|
2487
|
+
name: event.name || "",
|
|
2488
|
+
arguments: event.arguments || {}
|
|
2489
|
+
});
|
|
2490
|
+
} else if (event.type === "action_complete") {
|
|
2491
|
+
params.onActionComplete?.({
|
|
2492
|
+
id: event.id || "",
|
|
2493
|
+
name: event.name || "",
|
|
2494
|
+
output: event.output
|
|
2495
|
+
});
|
|
2496
|
+
} else if (event.type === "action_failed") {
|
|
2497
|
+
params.onActionFailed?.({
|
|
2498
|
+
id: event.id || "",
|
|
2499
|
+
name: event.name || "",
|
|
2500
|
+
error: event.error || "Action failed"
|
|
2501
|
+
});
|
|
2502
|
+
} else if (event.type === "done") {
|
|
2503
|
+
threadId = event.thread_id || threadId;
|
|
2504
|
+
messageCount = event.message_count || 0;
|
|
2505
|
+
const result = {
|
|
2506
|
+
threadId,
|
|
2507
|
+
messageCount,
|
|
2508
|
+
response: fullResponse
|
|
2509
|
+
};
|
|
2510
|
+
params.onComplete?.(result);
|
|
2511
|
+
} else if (event.type === "error") {
|
|
2512
|
+
const err = new OmnikitError(
|
|
2513
|
+
event.message || "Assistant error",
|
|
2514
|
+
500,
|
|
2515
|
+
"ASSISTANT_ERROR"
|
|
2516
|
+
);
|
|
2517
|
+
params.onError?.(err);
|
|
2518
|
+
throw err;
|
|
2519
|
+
}
|
|
2520
|
+
} catch (parseError) {
|
|
2521
|
+
if (parseError instanceof OmnikitError) {
|
|
2522
|
+
throw parseError;
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
} finally {
|
|
2529
|
+
reader.releaseLock();
|
|
2530
|
+
}
|
|
2531
|
+
return {
|
|
2532
|
+
threadId,
|
|
2533
|
+
messageCount,
|
|
2534
|
+
response: fullResponse
|
|
2535
|
+
};
|
|
2536
|
+
}
|
|
2537
|
+
/**
|
|
2538
|
+
* Subscribe to a thread via WebSocket for real-time updates.
|
|
2539
|
+
*
|
|
2540
|
+
* Use this to watch a thread without triggering a run. Useful for:
|
|
2541
|
+
* - Showing live updates when another client runs the assistant
|
|
2542
|
+
* - Reconnecting after page navigation
|
|
2543
|
+
* - Observing thread activity in real-time
|
|
2544
|
+
*
|
|
2545
|
+
* @example
|
|
2546
|
+
* ```typescript
|
|
2547
|
+
* // Subscribe to a thread
|
|
2548
|
+
* const unsubscribe = omnikit.subscribeToThread(threadId, {
|
|
2549
|
+
* onConnected: (info) => console.log(`Connected to ${info.assistantName}`),
|
|
2550
|
+
* onToken: (token) => setResponse(prev => prev + token),
|
|
2551
|
+
* onActionStart: (action) => console.log(`Action: ${action.name}`),
|
|
2552
|
+
* onActionComplete: (action) => console.log(`Result: ${JSON.stringify(action.output)}`),
|
|
2553
|
+
* onRunComplete: (result) => console.log(`Run complete, ${result.messageCount} messages`),
|
|
2554
|
+
* onError: (error) => console.error(error),
|
|
2555
|
+
* });
|
|
2556
|
+
*
|
|
2557
|
+
* // Later: disconnect
|
|
2558
|
+
* unsubscribe();
|
|
2559
|
+
* ```
|
|
2560
|
+
*
|
|
2561
|
+
* @param threadId - Thread ID to subscribe to
|
|
2562
|
+
* @param callbacks - Event callbacks for real-time updates
|
|
2563
|
+
* @returns Unsubscribe function to close the WebSocket
|
|
2564
|
+
*/
|
|
2565
|
+
subscribeToThread(threadId, callbacks) {
|
|
2566
|
+
const wsProtocol = this.baseUrl.startsWith("https") ? "wss" : "ws";
|
|
2567
|
+
const wsBaseUrl = this.baseUrl.replace(/^https?:\/\//, "");
|
|
2568
|
+
let wsUrl = `${wsProtocol}://${wsBaseUrl}/apps/${this.appId}/threads/${threadId}/subscribe`;
|
|
2569
|
+
const token = this.getAuthToken();
|
|
2570
|
+
if (token) {
|
|
2571
|
+
wsUrl += `?token=${encodeURIComponent(token)}`;
|
|
2572
|
+
}
|
|
2573
|
+
const ws = new WebSocket(wsUrl);
|
|
2574
|
+
let pingInterval = null;
|
|
2575
|
+
ws.onopen = () => {
|
|
2576
|
+
pingInterval = setInterval(() => {
|
|
2577
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
2578
|
+
ws.send(JSON.stringify({ type: "ping" }));
|
|
2579
|
+
}
|
|
2580
|
+
}, 3e4);
|
|
2581
|
+
};
|
|
2582
|
+
ws.onmessage = (event) => {
|
|
2583
|
+
try {
|
|
2584
|
+
const data = JSON.parse(event.data);
|
|
2585
|
+
switch (data.type) {
|
|
2586
|
+
case "connected":
|
|
2587
|
+
callbacks.onConnected?.({
|
|
2588
|
+
threadId: data.thread_id,
|
|
2589
|
+
assistantName: data.assistant_name,
|
|
2590
|
+
messageCount: data.message_count
|
|
2591
|
+
});
|
|
2592
|
+
break;
|
|
2593
|
+
case "token":
|
|
2594
|
+
callbacks.onToken?.(data.content || "");
|
|
2595
|
+
break;
|
|
2596
|
+
case "action_start":
|
|
2597
|
+
callbacks.onActionStart?.({
|
|
2598
|
+
id: data.id || "",
|
|
2599
|
+
name: data.name || "",
|
|
2600
|
+
arguments: data.arguments || {}
|
|
2601
|
+
});
|
|
2602
|
+
break;
|
|
2603
|
+
case "action_complete":
|
|
2604
|
+
callbacks.onActionComplete?.({
|
|
2605
|
+
id: data.id || "",
|
|
2606
|
+
name: data.name || "",
|
|
2607
|
+
output: data.output
|
|
2608
|
+
});
|
|
2609
|
+
break;
|
|
2610
|
+
case "action_failed":
|
|
2611
|
+
callbacks.onActionFailed?.({
|
|
2612
|
+
id: data.id || "",
|
|
2613
|
+
name: data.name || "",
|
|
2614
|
+
error: data.error || "Action failed"
|
|
2615
|
+
});
|
|
2616
|
+
break;
|
|
2617
|
+
case "run_complete":
|
|
2618
|
+
case "done":
|
|
2619
|
+
callbacks.onRunComplete?.({
|
|
2620
|
+
threadId: data.thread_id || threadId,
|
|
2621
|
+
messageCount: data.message_count || 0
|
|
2622
|
+
});
|
|
2623
|
+
break;
|
|
2624
|
+
case "error":
|
|
2625
|
+
callbacks.onError?.(new Error(data.message || "WebSocket error"));
|
|
2626
|
+
break;
|
|
2627
|
+
case "pong":
|
|
2628
|
+
break;
|
|
2629
|
+
}
|
|
2630
|
+
} catch (parseError) {
|
|
2631
|
+
}
|
|
2632
|
+
};
|
|
2633
|
+
ws.onerror = (event) => {
|
|
2634
|
+
callbacks.onError?.(new Error("WebSocket connection error"));
|
|
2635
|
+
};
|
|
2636
|
+
ws.onclose = () => {
|
|
2637
|
+
if (pingInterval) {
|
|
2638
|
+
clearInterval(pingInterval);
|
|
2639
|
+
}
|
|
2640
|
+
callbacks.onDisconnect?.();
|
|
2641
|
+
};
|
|
2642
|
+
return () => {
|
|
2643
|
+
if (pingInterval) {
|
|
2644
|
+
clearInterval(pingInterval);
|
|
2645
|
+
}
|
|
2646
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
2647
|
+
ws.close();
|
|
2648
|
+
}
|
|
2649
|
+
};
|
|
2650
|
+
}
|
|
2230
2651
|
/**
|
|
2231
2652
|
* Invoke a backend function by name.
|
|
2232
2653
|
*
|