@supyagent/sdk 0.1.39 → 0.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/connect.cjs +113 -0
- package/dist/connect.cjs.map +1 -0
- package/dist/connect.d.cts +50 -0
- package/dist/connect.d.ts +50 -0
- package/dist/connect.js +85 -0
- package/dist/connect.js.map +1 -0
- package/dist/index.cjs +182 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +100 -2
- package/dist/index.d.ts +100 -2
- package/dist/index.js +182 -47
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +125 -2
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +36 -2
- package/dist/react.d.ts +36 -2
- package/dist/react.js +122 -1
- package/dist/react.js.map +1 -1
- package/package.json +7 -1
package/dist/connect.cjs
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/connect.ts
|
|
21
|
+
var connect_exports = {};
|
|
22
|
+
__export(connect_exports, {
|
|
23
|
+
handleConnectCallback: () => handleConnectCallback,
|
|
24
|
+
openConnectPopup: () => openConnectPopup
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(connect_exports);
|
|
27
|
+
|
|
28
|
+
// src/connect/popup.ts
|
|
29
|
+
function openConnectPopup(options) {
|
|
30
|
+
const { connectUrl, width = 500, height = 700, onSuccess, onError } = options;
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
const left = Math.round(window.screenX + (window.outerWidth - width) / 2);
|
|
33
|
+
const top = Math.round(window.screenY + (window.outerHeight - height) / 2);
|
|
34
|
+
const features = `width=${width},height=${height},left=${left},top=${top},popup=1`;
|
|
35
|
+
const popup = window.open(connectUrl, "supyagent_connect", features);
|
|
36
|
+
if (!popup) {
|
|
37
|
+
const err = new Error(
|
|
38
|
+
"Popup was blocked by the browser. Please allow popups for this site."
|
|
39
|
+
);
|
|
40
|
+
onError?.(err);
|
|
41
|
+
reject(err);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const popupRef = popup;
|
|
45
|
+
let settled = false;
|
|
46
|
+
function cleanup() {
|
|
47
|
+
window.removeEventListener("message", onMessage);
|
|
48
|
+
clearInterval(pollTimer);
|
|
49
|
+
}
|
|
50
|
+
function onMessage(event) {
|
|
51
|
+
const data = event.data;
|
|
52
|
+
if (!data || data.type !== "supyagent:connect") return;
|
|
53
|
+
settled = true;
|
|
54
|
+
cleanup();
|
|
55
|
+
try {
|
|
56
|
+
popupRef.close();
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
if (data.status === "success" && data.provider && data.accountId) {
|
|
60
|
+
const result = {
|
|
61
|
+
status: "success",
|
|
62
|
+
provider: data.provider,
|
|
63
|
+
accountId: data.accountId
|
|
64
|
+
};
|
|
65
|
+
onSuccess?.(result);
|
|
66
|
+
resolve(result);
|
|
67
|
+
} else {
|
|
68
|
+
const err = new Error(data.error || "OAuth connect failed");
|
|
69
|
+
onError?.(err);
|
|
70
|
+
reject(err);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
window.addEventListener("message", onMessage);
|
|
74
|
+
const pollTimer = setInterval(() => {
|
|
75
|
+
if (!settled && popupRef.closed) {
|
|
76
|
+
settled = true;
|
|
77
|
+
cleanup();
|
|
78
|
+
const err = new Error("Popup was closed before completing the OAuth flow");
|
|
79
|
+
onError?.(err);
|
|
80
|
+
reject(err);
|
|
81
|
+
}
|
|
82
|
+
}, 500);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/connect/callback.ts
|
|
87
|
+
function handleConnectCallback(options) {
|
|
88
|
+
const { targetOrigin = "*", autoClose = true } = options ?? {};
|
|
89
|
+
const params = new URLSearchParams(window.location.search);
|
|
90
|
+
const status = params.get("status");
|
|
91
|
+
const provider = params.get("provider");
|
|
92
|
+
const accountId = params.get("account_id");
|
|
93
|
+
const error = params.get("error");
|
|
94
|
+
const message = {
|
|
95
|
+
type: "supyagent:connect",
|
|
96
|
+
status: status === "success" ? "success" : "error",
|
|
97
|
+
...provider ? { provider } : {},
|
|
98
|
+
...accountId ? { accountId } : {},
|
|
99
|
+
...error ? { error } : {}
|
|
100
|
+
};
|
|
101
|
+
if (window.opener) {
|
|
102
|
+
window.opener.postMessage(message, targetOrigin);
|
|
103
|
+
}
|
|
104
|
+
if (autoClose) {
|
|
105
|
+
window.close();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
109
|
+
0 && (module.exports = {
|
|
110
|
+
handleConnectCallback,
|
|
111
|
+
openConnectPopup
|
|
112
|
+
});
|
|
113
|
+
//# sourceMappingURL=connect.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/connect.ts","../src/connect/popup.ts","../src/connect/callback.ts"],"sourcesContent":["export { openConnectPopup } from \"./connect/popup.js\";\nexport { handleConnectCallback } from \"./connect/callback.js\";\nexport type {\n ConnectMessage,\n ConnectPopupOptions,\n ConnectPopupResult,\n ConnectCallbackOptions,\n} from \"./connect/types.js\";\n","import type { ConnectMessage, ConnectPopupOptions, ConnectPopupResult } from \"./types.js\";\n\n/**\n * Opens an OAuth connect popup and returns a promise that resolves\n * when the flow completes. The partner's redirect page must call\n * `handleConnectCallback()` to post the result back via `postMessage`.\n */\nexport function openConnectPopup(options: ConnectPopupOptions): Promise<ConnectPopupResult> {\n const { connectUrl, width = 500, height = 700, onSuccess, onError } = options;\n\n return new Promise<ConnectPopupResult>((resolve, reject) => {\n // Center the popup on screen\n const left = Math.round(window.screenX + (window.outerWidth - width) / 2);\n const top = Math.round(window.screenY + (window.outerHeight - height) / 2);\n const features = `width=${width},height=${height},left=${left},top=${top},popup=1`;\n\n const popup = window.open(connectUrl, \"supyagent_connect\", features);\n\n if (!popup) {\n const err = new Error(\n \"Popup was blocked by the browser. Please allow popups for this site.\",\n );\n onError?.(err);\n reject(err);\n return;\n }\n\n // Capture non-null ref so TS narrows inside closures\n const popupRef = popup;\n let settled = false;\n\n function cleanup() {\n window.removeEventListener(\"message\", onMessage);\n clearInterval(pollTimer);\n }\n\n function onMessage(event: MessageEvent) {\n const data = event.data as ConnectMessage | undefined;\n if (!data || data.type !== \"supyagent:connect\") return;\n\n settled = true;\n cleanup();\n\n try {\n popupRef.close();\n } catch {\n // Ignore if already closed\n }\n\n if (data.status === \"success\" && data.provider && data.accountId) {\n const result: ConnectPopupResult = {\n status: \"success\",\n provider: data.provider,\n accountId: data.accountId,\n };\n onSuccess?.(result);\n resolve(result);\n } else {\n const err = new Error(data.error || \"OAuth connect failed\");\n onError?.(err);\n reject(err);\n }\n }\n\n window.addEventListener(\"message\", onMessage);\n\n // Poll for manual popup close\n const pollTimer = setInterval(() => {\n if (!settled && popupRef.closed) {\n settled = true;\n cleanup();\n const err = new Error(\"Popup was closed before completing the OAuth flow\");\n onError?.(err);\n reject(err);\n }\n }, 500);\n });\n}\n","import type { ConnectCallbackOptions, ConnectMessage } from \"./types.js\";\n\n/**\n * Call this on the partner's redirect page after OAuth completes.\n * Reads the result from URL query params and posts it back to the\n * opener window via `postMessage`, then optionally closes the window.\n */\nexport function handleConnectCallback(options?: ConnectCallbackOptions): void {\n const { targetOrigin = \"*\", autoClose = true } = options ?? {};\n\n const params = new URLSearchParams(window.location.search);\n const status = params.get(\"status\");\n const provider = params.get(\"provider\");\n const accountId = params.get(\"account_id\");\n const error = params.get(\"error\");\n\n const message: ConnectMessage = {\n type: \"supyagent:connect\",\n status: status === \"success\" ? \"success\" : \"error\",\n ...(provider ? { provider } : {}),\n ...(accountId ? { accountId } : {}),\n ...(error ? { error } : {}),\n };\n\n if (window.opener) {\n window.opener.postMessage(message, targetOrigin);\n }\n\n if (autoClose) {\n window.close();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACOO,SAAS,iBAAiB,SAA2D;AAC1F,QAAM,EAAE,YAAY,QAAQ,KAAK,SAAS,KAAK,WAAW,QAAQ,IAAI;AAEtE,SAAO,IAAI,QAA4B,CAAC,SAAS,WAAW;AAE1D,UAAM,OAAO,KAAK,MAAM,OAAO,WAAW,OAAO,aAAa,SAAS,CAAC;AACxE,UAAM,MAAM,KAAK,MAAM,OAAO,WAAW,OAAO,cAAc,UAAU,CAAC;AACzE,UAAM,WAAW,SAAS,KAAK,WAAW,MAAM,SAAS,IAAI,QAAQ,GAAG;AAExE,UAAM,QAAQ,OAAO,KAAK,YAAY,qBAAqB,QAAQ;AAEnE,QAAI,CAAC,OAAO;AACV,YAAM,MAAM,IAAI;AAAA,QACd;AAAA,MACF;AACA,gBAAU,GAAG;AACb,aAAO,GAAG;AACV;AAAA,IACF;AAGA,UAAM,WAAW;AACjB,QAAI,UAAU;AAEd,aAAS,UAAU;AACjB,aAAO,oBAAoB,WAAW,SAAS;AAC/C,oBAAc,SAAS;AAAA,IACzB;AAEA,aAAS,UAAU,OAAqB;AACtC,YAAM,OAAO,MAAM;AACnB,UAAI,CAAC,QAAQ,KAAK,SAAS,oBAAqB;AAEhD,gBAAU;AACV,cAAQ;AAER,UAAI;AACF,iBAAS,MAAM;AAAA,MACjB,QAAQ;AAAA,MAER;AAEA,UAAI,KAAK,WAAW,aAAa,KAAK,YAAY,KAAK,WAAW;AAChE,cAAM,SAA6B;AAAA,UACjC,QAAQ;AAAA,UACR,UAAU,KAAK;AAAA,UACf,WAAW,KAAK;AAAA,QAClB;AACA,oBAAY,MAAM;AAClB,gBAAQ,MAAM;AAAA,MAChB,OAAO;AACL,cAAM,MAAM,IAAI,MAAM,KAAK,SAAS,sBAAsB;AAC1D,kBAAU,GAAG;AACb,eAAO,GAAG;AAAA,MACZ;AAAA,IACF;AAEA,WAAO,iBAAiB,WAAW,SAAS;AAG5C,UAAM,YAAY,YAAY,MAAM;AAClC,UAAI,CAAC,WAAW,SAAS,QAAQ;AAC/B,kBAAU;AACV,gBAAQ;AACR,cAAM,MAAM,IAAI,MAAM,mDAAmD;AACzE,kBAAU,GAAG;AACb,eAAO,GAAG;AAAA,MACZ;AAAA,IACF,GAAG,GAAG;AAAA,EACR,CAAC;AACH;;;ACtEO,SAAS,sBAAsB,SAAwC;AAC5E,QAAM,EAAE,eAAe,KAAK,YAAY,KAAK,IAAI,WAAW,CAAC;AAE7D,QAAM,SAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM;AACzD,QAAM,SAAS,OAAO,IAAI,QAAQ;AAClC,QAAM,WAAW,OAAO,IAAI,UAAU;AACtC,QAAM,YAAY,OAAO,IAAI,YAAY;AACzC,QAAM,QAAQ,OAAO,IAAI,OAAO;AAEhC,QAAM,UAA0B;AAAA,IAC9B,MAAM;AAAA,IACN,QAAQ,WAAW,YAAY,YAAY;AAAA,IAC3C,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,IAC/B,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,IACjC,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,EAC3B;AAEA,MAAI,OAAO,QAAQ;AACjB,WAAO,OAAO,YAAY,SAAS,YAAY;AAAA,EACjD;AAEA,MAAI,WAAW;AACb,WAAO,MAAM;AAAA,EACf;AACF;","names":[]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** Shape of the postMessage sent from the redirect page back to the opener */
|
|
2
|
+
interface ConnectMessage {
|
|
3
|
+
type: "supyagent:connect";
|
|
4
|
+
status: "success" | "error";
|
|
5
|
+
provider?: string;
|
|
6
|
+
accountId?: string;
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Options for openConnectPopup() */
|
|
10
|
+
interface ConnectPopupOptions {
|
|
11
|
+
/** The connect URL returned by client.accounts.connect() */
|
|
12
|
+
connectUrl: string;
|
|
13
|
+
/** Popup width in pixels (default: 500) */
|
|
14
|
+
width?: number;
|
|
15
|
+
/** Popup height in pixels (default: 700) */
|
|
16
|
+
height?: number;
|
|
17
|
+
/** Called when the OAuth flow succeeds */
|
|
18
|
+
onSuccess?: (result: ConnectPopupResult) => void;
|
|
19
|
+
/** Called when the OAuth flow fails */
|
|
20
|
+
onError?: (error: Error) => void;
|
|
21
|
+
}
|
|
22
|
+
/** Resolved value of the openConnectPopup() promise */
|
|
23
|
+
interface ConnectPopupResult {
|
|
24
|
+
status: "success";
|
|
25
|
+
provider: string;
|
|
26
|
+
accountId: string;
|
|
27
|
+
}
|
|
28
|
+
/** Options for handleConnectCallback() */
|
|
29
|
+
interface ConnectCallbackOptions {
|
|
30
|
+
/** Target origin for postMessage (default: "*") */
|
|
31
|
+
targetOrigin?: string;
|
|
32
|
+
/** Whether to auto-close the window after posting (default: true) */
|
|
33
|
+
autoClose?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Opens an OAuth connect popup and returns a promise that resolves
|
|
38
|
+
* when the flow completes. The partner's redirect page must call
|
|
39
|
+
* `handleConnectCallback()` to post the result back via `postMessage`.
|
|
40
|
+
*/
|
|
41
|
+
declare function openConnectPopup(options: ConnectPopupOptions): Promise<ConnectPopupResult>;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Call this on the partner's redirect page after OAuth completes.
|
|
45
|
+
* Reads the result from URL query params and posts it back to the
|
|
46
|
+
* opener window via `postMessage`, then optionally closes the window.
|
|
47
|
+
*/
|
|
48
|
+
declare function handleConnectCallback(options?: ConnectCallbackOptions): void;
|
|
49
|
+
|
|
50
|
+
export { type ConnectCallbackOptions, type ConnectMessage, type ConnectPopupOptions, type ConnectPopupResult, handleConnectCallback, openConnectPopup };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** Shape of the postMessage sent from the redirect page back to the opener */
|
|
2
|
+
interface ConnectMessage {
|
|
3
|
+
type: "supyagent:connect";
|
|
4
|
+
status: "success" | "error";
|
|
5
|
+
provider?: string;
|
|
6
|
+
accountId?: string;
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Options for openConnectPopup() */
|
|
10
|
+
interface ConnectPopupOptions {
|
|
11
|
+
/** The connect URL returned by client.accounts.connect() */
|
|
12
|
+
connectUrl: string;
|
|
13
|
+
/** Popup width in pixels (default: 500) */
|
|
14
|
+
width?: number;
|
|
15
|
+
/** Popup height in pixels (default: 700) */
|
|
16
|
+
height?: number;
|
|
17
|
+
/** Called when the OAuth flow succeeds */
|
|
18
|
+
onSuccess?: (result: ConnectPopupResult) => void;
|
|
19
|
+
/** Called when the OAuth flow fails */
|
|
20
|
+
onError?: (error: Error) => void;
|
|
21
|
+
}
|
|
22
|
+
/** Resolved value of the openConnectPopup() promise */
|
|
23
|
+
interface ConnectPopupResult {
|
|
24
|
+
status: "success";
|
|
25
|
+
provider: string;
|
|
26
|
+
accountId: string;
|
|
27
|
+
}
|
|
28
|
+
/** Options for handleConnectCallback() */
|
|
29
|
+
interface ConnectCallbackOptions {
|
|
30
|
+
/** Target origin for postMessage (default: "*") */
|
|
31
|
+
targetOrigin?: string;
|
|
32
|
+
/** Whether to auto-close the window after posting (default: true) */
|
|
33
|
+
autoClose?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Opens an OAuth connect popup and returns a promise that resolves
|
|
38
|
+
* when the flow completes. The partner's redirect page must call
|
|
39
|
+
* `handleConnectCallback()` to post the result back via `postMessage`.
|
|
40
|
+
*/
|
|
41
|
+
declare function openConnectPopup(options: ConnectPopupOptions): Promise<ConnectPopupResult>;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Call this on the partner's redirect page after OAuth completes.
|
|
45
|
+
* Reads the result from URL query params and posts it back to the
|
|
46
|
+
* opener window via `postMessage`, then optionally closes the window.
|
|
47
|
+
*/
|
|
48
|
+
declare function handleConnectCallback(options?: ConnectCallbackOptions): void;
|
|
49
|
+
|
|
50
|
+
export { type ConnectCallbackOptions, type ConnectMessage, type ConnectPopupOptions, type ConnectPopupResult, handleConnectCallback, openConnectPopup };
|
package/dist/connect.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// src/connect/popup.ts
|
|
2
|
+
function openConnectPopup(options) {
|
|
3
|
+
const { connectUrl, width = 500, height = 700, onSuccess, onError } = options;
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
const left = Math.round(window.screenX + (window.outerWidth - width) / 2);
|
|
6
|
+
const top = Math.round(window.screenY + (window.outerHeight - height) / 2);
|
|
7
|
+
const features = `width=${width},height=${height},left=${left},top=${top},popup=1`;
|
|
8
|
+
const popup = window.open(connectUrl, "supyagent_connect", features);
|
|
9
|
+
if (!popup) {
|
|
10
|
+
const err = new Error(
|
|
11
|
+
"Popup was blocked by the browser. Please allow popups for this site."
|
|
12
|
+
);
|
|
13
|
+
onError?.(err);
|
|
14
|
+
reject(err);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const popupRef = popup;
|
|
18
|
+
let settled = false;
|
|
19
|
+
function cleanup() {
|
|
20
|
+
window.removeEventListener("message", onMessage);
|
|
21
|
+
clearInterval(pollTimer);
|
|
22
|
+
}
|
|
23
|
+
function onMessage(event) {
|
|
24
|
+
const data = event.data;
|
|
25
|
+
if (!data || data.type !== "supyagent:connect") return;
|
|
26
|
+
settled = true;
|
|
27
|
+
cleanup();
|
|
28
|
+
try {
|
|
29
|
+
popupRef.close();
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
if (data.status === "success" && data.provider && data.accountId) {
|
|
33
|
+
const result = {
|
|
34
|
+
status: "success",
|
|
35
|
+
provider: data.provider,
|
|
36
|
+
accountId: data.accountId
|
|
37
|
+
};
|
|
38
|
+
onSuccess?.(result);
|
|
39
|
+
resolve(result);
|
|
40
|
+
} else {
|
|
41
|
+
const err = new Error(data.error || "OAuth connect failed");
|
|
42
|
+
onError?.(err);
|
|
43
|
+
reject(err);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
window.addEventListener("message", onMessage);
|
|
47
|
+
const pollTimer = setInterval(() => {
|
|
48
|
+
if (!settled && popupRef.closed) {
|
|
49
|
+
settled = true;
|
|
50
|
+
cleanup();
|
|
51
|
+
const err = new Error("Popup was closed before completing the OAuth flow");
|
|
52
|
+
onError?.(err);
|
|
53
|
+
reject(err);
|
|
54
|
+
}
|
|
55
|
+
}, 500);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/connect/callback.ts
|
|
60
|
+
function handleConnectCallback(options) {
|
|
61
|
+
const { targetOrigin = "*", autoClose = true } = options ?? {};
|
|
62
|
+
const params = new URLSearchParams(window.location.search);
|
|
63
|
+
const status = params.get("status");
|
|
64
|
+
const provider = params.get("provider");
|
|
65
|
+
const accountId = params.get("account_id");
|
|
66
|
+
const error = params.get("error");
|
|
67
|
+
const message = {
|
|
68
|
+
type: "supyagent:connect",
|
|
69
|
+
status: status === "success" ? "success" : "error",
|
|
70
|
+
...provider ? { provider } : {},
|
|
71
|
+
...accountId ? { accountId } : {},
|
|
72
|
+
...error ? { error } : {}
|
|
73
|
+
};
|
|
74
|
+
if (window.opener) {
|
|
75
|
+
window.opener.postMessage(message, targetOrigin);
|
|
76
|
+
}
|
|
77
|
+
if (autoClose) {
|
|
78
|
+
window.close();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export {
|
|
82
|
+
handleConnectCallback,
|
|
83
|
+
openConnectPopup
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=connect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/connect/popup.ts","../src/connect/callback.ts"],"sourcesContent":["import type { ConnectMessage, ConnectPopupOptions, ConnectPopupResult } from \"./types.js\";\n\n/**\n * Opens an OAuth connect popup and returns a promise that resolves\n * when the flow completes. The partner's redirect page must call\n * `handleConnectCallback()` to post the result back via `postMessage`.\n */\nexport function openConnectPopup(options: ConnectPopupOptions): Promise<ConnectPopupResult> {\n const { connectUrl, width = 500, height = 700, onSuccess, onError } = options;\n\n return new Promise<ConnectPopupResult>((resolve, reject) => {\n // Center the popup on screen\n const left = Math.round(window.screenX + (window.outerWidth - width) / 2);\n const top = Math.round(window.screenY + (window.outerHeight - height) / 2);\n const features = `width=${width},height=${height},left=${left},top=${top},popup=1`;\n\n const popup = window.open(connectUrl, \"supyagent_connect\", features);\n\n if (!popup) {\n const err = new Error(\n \"Popup was blocked by the browser. Please allow popups for this site.\",\n );\n onError?.(err);\n reject(err);\n return;\n }\n\n // Capture non-null ref so TS narrows inside closures\n const popupRef = popup;\n let settled = false;\n\n function cleanup() {\n window.removeEventListener(\"message\", onMessage);\n clearInterval(pollTimer);\n }\n\n function onMessage(event: MessageEvent) {\n const data = event.data as ConnectMessage | undefined;\n if (!data || data.type !== \"supyagent:connect\") return;\n\n settled = true;\n cleanup();\n\n try {\n popupRef.close();\n } catch {\n // Ignore if already closed\n }\n\n if (data.status === \"success\" && data.provider && data.accountId) {\n const result: ConnectPopupResult = {\n status: \"success\",\n provider: data.provider,\n accountId: data.accountId,\n };\n onSuccess?.(result);\n resolve(result);\n } else {\n const err = new Error(data.error || \"OAuth connect failed\");\n onError?.(err);\n reject(err);\n }\n }\n\n window.addEventListener(\"message\", onMessage);\n\n // Poll for manual popup close\n const pollTimer = setInterval(() => {\n if (!settled && popupRef.closed) {\n settled = true;\n cleanup();\n const err = new Error(\"Popup was closed before completing the OAuth flow\");\n onError?.(err);\n reject(err);\n }\n }, 500);\n });\n}\n","import type { ConnectCallbackOptions, ConnectMessage } from \"./types.js\";\n\n/**\n * Call this on the partner's redirect page after OAuth completes.\n * Reads the result from URL query params and posts it back to the\n * opener window via `postMessage`, then optionally closes the window.\n */\nexport function handleConnectCallback(options?: ConnectCallbackOptions): void {\n const { targetOrigin = \"*\", autoClose = true } = options ?? {};\n\n const params = new URLSearchParams(window.location.search);\n const status = params.get(\"status\");\n const provider = params.get(\"provider\");\n const accountId = params.get(\"account_id\");\n const error = params.get(\"error\");\n\n const message: ConnectMessage = {\n type: \"supyagent:connect\",\n status: status === \"success\" ? \"success\" : \"error\",\n ...(provider ? { provider } : {}),\n ...(accountId ? { accountId } : {}),\n ...(error ? { error } : {}),\n };\n\n if (window.opener) {\n window.opener.postMessage(message, targetOrigin);\n }\n\n if (autoClose) {\n window.close();\n }\n}\n"],"mappings":";AAOO,SAAS,iBAAiB,SAA2D;AAC1F,QAAM,EAAE,YAAY,QAAQ,KAAK,SAAS,KAAK,WAAW,QAAQ,IAAI;AAEtE,SAAO,IAAI,QAA4B,CAAC,SAAS,WAAW;AAE1D,UAAM,OAAO,KAAK,MAAM,OAAO,WAAW,OAAO,aAAa,SAAS,CAAC;AACxE,UAAM,MAAM,KAAK,MAAM,OAAO,WAAW,OAAO,cAAc,UAAU,CAAC;AACzE,UAAM,WAAW,SAAS,KAAK,WAAW,MAAM,SAAS,IAAI,QAAQ,GAAG;AAExE,UAAM,QAAQ,OAAO,KAAK,YAAY,qBAAqB,QAAQ;AAEnE,QAAI,CAAC,OAAO;AACV,YAAM,MAAM,IAAI;AAAA,QACd;AAAA,MACF;AACA,gBAAU,GAAG;AACb,aAAO,GAAG;AACV;AAAA,IACF;AAGA,UAAM,WAAW;AACjB,QAAI,UAAU;AAEd,aAAS,UAAU;AACjB,aAAO,oBAAoB,WAAW,SAAS;AAC/C,oBAAc,SAAS;AAAA,IACzB;AAEA,aAAS,UAAU,OAAqB;AACtC,YAAM,OAAO,MAAM;AACnB,UAAI,CAAC,QAAQ,KAAK,SAAS,oBAAqB;AAEhD,gBAAU;AACV,cAAQ;AAER,UAAI;AACF,iBAAS,MAAM;AAAA,MACjB,QAAQ;AAAA,MAER;AAEA,UAAI,KAAK,WAAW,aAAa,KAAK,YAAY,KAAK,WAAW;AAChE,cAAM,SAA6B;AAAA,UACjC,QAAQ;AAAA,UACR,UAAU,KAAK;AAAA,UACf,WAAW,KAAK;AAAA,QAClB;AACA,oBAAY,MAAM;AAClB,gBAAQ,MAAM;AAAA,MAChB,OAAO;AACL,cAAM,MAAM,IAAI,MAAM,KAAK,SAAS,sBAAsB;AAC1D,kBAAU,GAAG;AACb,eAAO,GAAG;AAAA,MACZ;AAAA,IACF;AAEA,WAAO,iBAAiB,WAAW,SAAS;AAG5C,UAAM,YAAY,YAAY,MAAM;AAClC,UAAI,CAAC,WAAW,SAAS,QAAQ;AAC/B,kBAAU;AACV,gBAAQ;AACR,cAAM,MAAM,IAAI,MAAM,mDAAmD;AACzE,kBAAU,GAAG;AACb,eAAO,GAAG;AAAA,MACZ;AAAA,IACF,GAAG,GAAG;AAAA,EACR,CAAC;AACH;;;ACtEO,SAAS,sBAAsB,SAAwC;AAC5E,QAAM,EAAE,eAAe,KAAK,YAAY,KAAK,IAAI,WAAW,CAAC;AAE7D,QAAM,SAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM;AACzD,QAAM,SAAS,OAAO,IAAI,QAAQ;AAClC,QAAM,WAAW,OAAO,IAAI,UAAU;AACtC,QAAM,YAAY,OAAO,IAAI,YAAY;AACzC,QAAM,QAAQ,OAAO,IAAI,OAAO;AAEhC,QAAM,UAA0B;AAAA,IAC9B,MAAM;AAAA,IACN,QAAQ,WAAW,YAAY,YAAY;AAAA,IAC3C,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,IAC/B,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,IACjC,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,EAC3B;AAEA,MAAI,OAAO,QAAQ;AACjB,WAAO,OAAO,YAAY,SAAS,YAAY;AAAA,EACjD;AAEA,MAAI,WAAW;AACb,WAAO,MAAM;AAAA,EACf;AACF;","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -58,7 +58,7 @@ var TTLCache = class {
|
|
|
58
58
|
var import_ai = require("ai");
|
|
59
59
|
|
|
60
60
|
// src/core/http-executor.ts
|
|
61
|
-
function createExecutor(metadata, baseUrl, apiKey) {
|
|
61
|
+
function createExecutor(metadata, baseUrl, apiKey, accountId) {
|
|
62
62
|
return async (args) => {
|
|
63
63
|
const { method, path, bodyDefaults } = metadata;
|
|
64
64
|
const remainingArgs = { ...args };
|
|
@@ -97,7 +97,8 @@ function createExecutor(metadata, baseUrl, apiKey) {
|
|
|
97
97
|
method,
|
|
98
98
|
headers: {
|
|
99
99
|
Authorization: `Bearer ${apiKey}`,
|
|
100
|
-
"Content-Type": "application/json"
|
|
100
|
+
"Content-Type": "application/json",
|
|
101
|
+
...accountId ? { "X-Account-Id": accountId } : {}
|
|
101
102
|
}
|
|
102
103
|
};
|
|
103
104
|
if (method === "GET" || method === "DELETE") {
|
|
@@ -120,7 +121,7 @@ function createExecutor(metadata, baseUrl, apiKey) {
|
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
// src/core/tool-converter.ts
|
|
123
|
-
function convertTools(tools, baseUrl, apiKey) {
|
|
124
|
+
function convertTools(tools, baseUrl, apiKey, accountId) {
|
|
124
125
|
const result = {};
|
|
125
126
|
for (const t of tools) {
|
|
126
127
|
const { name, description, parameters } = t.function;
|
|
@@ -128,7 +129,7 @@ function convertTools(tools, baseUrl, apiKey) {
|
|
|
128
129
|
result[name] = (0, import_ai.tool)({
|
|
129
130
|
description,
|
|
130
131
|
inputSchema: (0, import_ai.jsonSchema)(parameters),
|
|
131
|
-
execute: async (args) => createExecutor(metadata, baseUrl, apiKey)(args)
|
|
132
|
+
execute: async (args) => createExecutor(metadata, baseUrl, apiKey, accountId)(args)
|
|
132
133
|
});
|
|
133
134
|
}
|
|
134
135
|
return result;
|
|
@@ -252,7 +253,7 @@ function createLoadSkillTool(skills) {
|
|
|
252
253
|
});
|
|
253
254
|
}
|
|
254
255
|
var VALID_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
|
|
255
|
-
function createApiCallTool(baseUrl, apiKey) {
|
|
256
|
+
function createApiCallTool(baseUrl, apiKey, accountId) {
|
|
256
257
|
return (0, import_ai2.tool)({
|
|
257
258
|
description: "Make an authenticated HTTP request to the Supyagent API. Use loadSkill first to understand available endpoints. The authorization header and base URL are handled automatically \u2014 only provide the path (e.g., /api/v1/google/gmail/messages).",
|
|
258
259
|
inputSchema: (0, import_ai2.jsonSchema)({
|
|
@@ -309,7 +310,8 @@ function createApiCallTool(baseUrl, apiKey) {
|
|
|
309
310
|
method,
|
|
310
311
|
headers: {
|
|
311
312
|
Authorization: `Bearer ${apiKey}`,
|
|
312
|
-
"Content-Type": "application/json"
|
|
313
|
+
"Content-Type": "application/json",
|
|
314
|
+
...accountId ? { "X-Account-Id": accountId } : {}
|
|
313
315
|
}
|
|
314
316
|
};
|
|
315
317
|
if (body && method !== "GET" && method !== "DELETE") {
|
|
@@ -334,32 +336,41 @@ function createApiCallTool(baseUrl, apiKey) {
|
|
|
334
336
|
// src/core/client.ts
|
|
335
337
|
var DEFAULT_BASE_URL = "https://app.supyagent.com";
|
|
336
338
|
var CACHE_KEY = "tools";
|
|
337
|
-
function
|
|
338
|
-
|
|
339
|
-
|
|
339
|
+
function createFetcher(baseUrl, apiKey, accountId) {
|
|
340
|
+
return async function fetcher(path, init) {
|
|
341
|
+
const res = await fetch(`${baseUrl}${path}`, {
|
|
342
|
+
...init,
|
|
343
|
+
headers: {
|
|
344
|
+
Authorization: `Bearer ${apiKey}`,
|
|
345
|
+
...accountId ? { "X-Account-Id": accountId } : {},
|
|
346
|
+
...init?.headers ?? {}
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
if (!res.ok) {
|
|
350
|
+
const text = await res.text();
|
|
351
|
+
throw new Error(`Supyagent API error (${res.status}): ${text}`);
|
|
352
|
+
}
|
|
353
|
+
return res;
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
function createDataPlane(fetcher, baseUrl, apiKey, accountId) {
|
|
357
|
+
const toolsCache = new TTLCache();
|
|
340
358
|
const skillsCache = new TTLCache();
|
|
341
359
|
const meCache = new TTLCache();
|
|
342
360
|
return {
|
|
343
361
|
async tools(filterOptions) {
|
|
344
362
|
const cacheTTL = resolveCacheTTL(filterOptions?.cache);
|
|
345
|
-
let response = cacheTTL > 0 ?
|
|
363
|
+
let response = cacheTTL > 0 ? toolsCache.get(CACHE_KEY) : void 0;
|
|
346
364
|
if (!response) {
|
|
347
|
-
const res = await
|
|
365
|
+
const res = await fetcher("/api/v1/tools", {
|
|
348
366
|
headers: {
|
|
349
|
-
Authorization: `Bearer ${apiKey}`,
|
|
350
367
|
"Content-Type": "application/json"
|
|
351
368
|
}
|
|
352
369
|
});
|
|
353
|
-
if (!res.ok) {
|
|
354
|
-
const error = await res.text();
|
|
355
|
-
throw new Error(
|
|
356
|
-
`Supyagent API error (${res.status}): ${error}`
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
370
|
const json = await res.json();
|
|
360
371
|
response = json.data ?? json;
|
|
361
372
|
if (cacheTTL > 0) {
|
|
362
|
-
|
|
373
|
+
toolsCache.set(CACHE_KEY, response, cacheTTL);
|
|
363
374
|
}
|
|
364
375
|
}
|
|
365
376
|
const toolBaseUrl = response.base_url || baseUrl;
|
|
@@ -368,23 +379,13 @@ function supyagent(options) {
|
|
|
368
379
|
only: filterOptions?.only,
|
|
369
380
|
except: filterOptions?.except
|
|
370
381
|
});
|
|
371
|
-
return convertTools(filtered, toolBaseUrl, apiKey);
|
|
382
|
+
return convertTools(filtered, toolBaseUrl, apiKey, accountId);
|
|
372
383
|
},
|
|
373
|
-
async skills(
|
|
374
|
-
const cacheTTL = resolveCacheTTL(
|
|
384
|
+
async skills(options) {
|
|
385
|
+
const cacheTTL = resolveCacheTTL(options?.cache);
|
|
375
386
|
let parsed = cacheTTL > 0 ? skillsCache.get("skills") : void 0;
|
|
376
387
|
if (!parsed) {
|
|
377
|
-
const res = await
|
|
378
|
-
headers: {
|
|
379
|
-
Authorization: `Bearer ${apiKey}`
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
if (!res.ok) {
|
|
383
|
-
const error = await res.text();
|
|
384
|
-
throw new Error(
|
|
385
|
-
`Supyagent API error (${res.status}): ${error}`
|
|
386
|
-
);
|
|
387
|
-
}
|
|
388
|
+
const res = await fetcher("/api/v1/skills");
|
|
388
389
|
parsed = parseSkillsMarkdown(await res.text());
|
|
389
390
|
if (cacheTTL > 0) {
|
|
390
391
|
skillsCache.set("skills", parsed, cacheTTL);
|
|
@@ -394,25 +395,15 @@ function supyagent(options) {
|
|
|
394
395
|
systemPrompt: buildSkillsSystemPrompt(parsed),
|
|
395
396
|
tools: {
|
|
396
397
|
loadSkill: createLoadSkillTool(parsed.skills),
|
|
397
|
-
apiCall: createApiCallTool(baseUrl, apiKey)
|
|
398
|
+
apiCall: createApiCallTool(baseUrl, apiKey, accountId)
|
|
398
399
|
}
|
|
399
400
|
};
|
|
400
401
|
},
|
|
401
|
-
async me(
|
|
402
|
-
const cacheTTL = resolveCacheTTL(
|
|
402
|
+
async me(options) {
|
|
403
|
+
const cacheTTL = resolveCacheTTL(options?.cache);
|
|
403
404
|
let response = cacheTTL > 0 ? meCache.get("me") : void 0;
|
|
404
405
|
if (!response) {
|
|
405
|
-
const res = await
|
|
406
|
-
headers: {
|
|
407
|
-
Authorization: `Bearer ${apiKey}`
|
|
408
|
-
}
|
|
409
|
-
});
|
|
410
|
-
if (!res.ok) {
|
|
411
|
-
const error = await res.text();
|
|
412
|
-
throw new Error(
|
|
413
|
-
`Supyagent API error (${res.status}): ${error}`
|
|
414
|
-
);
|
|
415
|
-
}
|
|
406
|
+
const res = await fetcher("/api/v1/me");
|
|
416
407
|
const json = await res.json();
|
|
417
408
|
response = json.data ?? json;
|
|
418
409
|
if (cacheTTL > 0) {
|
|
@@ -423,6 +414,150 @@ function supyagent(options) {
|
|
|
423
414
|
}
|
|
424
415
|
};
|
|
425
416
|
}
|
|
417
|
+
function supyagent(options) {
|
|
418
|
+
const { apiKey, baseUrl = DEFAULT_BASE_URL } = options;
|
|
419
|
+
const fetcher = createFetcher(baseUrl, apiKey);
|
|
420
|
+
const dataPlane = createDataPlane(fetcher, baseUrl, apiKey);
|
|
421
|
+
async function apiCall(method, path, body) {
|
|
422
|
+
const res = await fetcher(path, {
|
|
423
|
+
method,
|
|
424
|
+
...body ? { headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) } : {}
|
|
425
|
+
});
|
|
426
|
+
const json = await res.json();
|
|
427
|
+
return json.data ?? json;
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
...dataPlane,
|
|
431
|
+
accounts: createAccountsClient(apiCall),
|
|
432
|
+
asAccount(externalId) {
|
|
433
|
+
const scopedFetcher = createFetcher(baseUrl, apiKey, externalId);
|
|
434
|
+
const scopedDataPlane = createDataPlane(scopedFetcher, baseUrl, apiKey, externalId);
|
|
435
|
+
return {
|
|
436
|
+
accountId: externalId,
|
|
437
|
+
...scopedDataPlane
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
function createAccountsClient(apiCall) {
|
|
443
|
+
function toAccount(raw) {
|
|
444
|
+
return {
|
|
445
|
+
id: raw.id,
|
|
446
|
+
externalId: raw.external_id,
|
|
447
|
+
displayName: raw.display_name ?? null,
|
|
448
|
+
metadata: raw.metadata ?? {},
|
|
449
|
+
createdAt: raw.created_at
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
function toIntegration(raw) {
|
|
453
|
+
return {
|
|
454
|
+
id: raw.id,
|
|
455
|
+
provider: raw.provider,
|
|
456
|
+
status: raw.status,
|
|
457
|
+
connectedAt: raw.connected_at
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function toIntegrationDetail(raw) {
|
|
461
|
+
const services = raw.enabled_services ?? [];
|
|
462
|
+
return {
|
|
463
|
+
...toIntegration(raw),
|
|
464
|
+
enabledServices: services.map((s) => ({
|
|
465
|
+
serviceName: s.service_name,
|
|
466
|
+
isEnabled: s.is_enabled
|
|
467
|
+
}))
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
return {
|
|
471
|
+
async create(opts) {
|
|
472
|
+
const raw = await apiCall("POST", "/api/v1/accounts", {
|
|
473
|
+
external_id: opts.externalId,
|
|
474
|
+
...opts.displayName != null ? { display_name: opts.displayName } : {},
|
|
475
|
+
...opts.metadata != null ? { metadata: opts.metadata } : {}
|
|
476
|
+
});
|
|
477
|
+
return toAccount(raw);
|
|
478
|
+
},
|
|
479
|
+
async list(opts) {
|
|
480
|
+
const params = new URLSearchParams();
|
|
481
|
+
if (opts?.limit != null) params.set("limit", String(opts.limit));
|
|
482
|
+
if (opts?.offset != null) params.set("offset", String(opts.offset));
|
|
483
|
+
const qs = params.toString();
|
|
484
|
+
const raw = await apiCall(
|
|
485
|
+
"GET",
|
|
486
|
+
`/api/v1/accounts${qs ? `?${qs}` : ""}`
|
|
487
|
+
);
|
|
488
|
+
return {
|
|
489
|
+
accounts: (raw.accounts ?? []).map(toAccount),
|
|
490
|
+
total: raw.total,
|
|
491
|
+
limit: raw.limit,
|
|
492
|
+
offset: raw.offset
|
|
493
|
+
};
|
|
494
|
+
},
|
|
495
|
+
async get(id) {
|
|
496
|
+
const raw = await apiCall("GET", `/api/v1/accounts/${id}`);
|
|
497
|
+
return {
|
|
498
|
+
...toAccount(raw),
|
|
499
|
+
integrations: (raw.integrations ?? []).map(
|
|
500
|
+
toIntegration
|
|
501
|
+
)
|
|
502
|
+
};
|
|
503
|
+
},
|
|
504
|
+
async update(id, opts) {
|
|
505
|
+
const body = {};
|
|
506
|
+
if (opts.displayName !== void 0) body.display_name = opts.displayName;
|
|
507
|
+
if (opts.metadata !== void 0) body.metadata = opts.metadata;
|
|
508
|
+
const raw = await apiCall("PATCH", `/api/v1/accounts/${id}`, body);
|
|
509
|
+
return toAccount(raw);
|
|
510
|
+
},
|
|
511
|
+
async delete(id) {
|
|
512
|
+
return apiCall("DELETE", `/api/v1/accounts/${id}`);
|
|
513
|
+
},
|
|
514
|
+
async connect(id, opts) {
|
|
515
|
+
const raw = await apiCall(
|
|
516
|
+
"POST",
|
|
517
|
+
`/api/v1/accounts/${id}/connect`,
|
|
518
|
+
{
|
|
519
|
+
provider: opts.provider,
|
|
520
|
+
redirect_url: opts.redirectUrl,
|
|
521
|
+
...opts.scopes ? { scopes: opts.scopes } : {}
|
|
522
|
+
}
|
|
523
|
+
);
|
|
524
|
+
return {
|
|
525
|
+
connectUrl: raw.connect_url,
|
|
526
|
+
sessionId: raw.session_id,
|
|
527
|
+
expiresAt: raw.expires_at
|
|
528
|
+
};
|
|
529
|
+
},
|
|
530
|
+
async getSession(id, sessionId) {
|
|
531
|
+
const raw = await apiCall(
|
|
532
|
+
"GET",
|
|
533
|
+
`/api/v1/accounts/${id}/connect/${sessionId}`
|
|
534
|
+
);
|
|
535
|
+
return {
|
|
536
|
+
sessionId: raw.session_id,
|
|
537
|
+
provider: raw.provider,
|
|
538
|
+
status: raw.status,
|
|
539
|
+
...raw.error ? { error: raw.error } : {},
|
|
540
|
+
createdAt: raw.created_at,
|
|
541
|
+
expiresAt: raw.expires_at
|
|
542
|
+
};
|
|
543
|
+
},
|
|
544
|
+
async integrations(id) {
|
|
545
|
+
const raw = await apiCall(
|
|
546
|
+
"GET",
|
|
547
|
+
`/api/v1/accounts/${id}/integrations`
|
|
548
|
+
);
|
|
549
|
+
return (raw.integrations ?? []).map(
|
|
550
|
+
toIntegrationDetail
|
|
551
|
+
);
|
|
552
|
+
},
|
|
553
|
+
async disconnect(id, provider) {
|
|
554
|
+
return apiCall(
|
|
555
|
+
"DELETE",
|
|
556
|
+
`/api/v1/accounts/${id}/integrations/${provider}`
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
}
|
|
426
561
|
function resolveCacheTTL(cache) {
|
|
427
562
|
if (cache === true) return 60;
|
|
428
563
|
if (typeof cache === "number") return cache;
|