apteva 0.2.8 → 0.2.9
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/App.m4hg4bxq.js +218 -0
- package/dist/index.html +2 -2
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/db.ts +130 -16
- package/src/integrations/composio.ts +437 -0
- package/src/integrations/index.ts +80 -0
- package/src/openapi.ts +1724 -0
- package/src/routes/api.ts +599 -107
- package/src/server.ts +75 -6
- package/src/web/App.tsx +3 -0
- package/src/web/components/agents/AgentPanel.tsx +62 -37
- package/src/web/components/api/ApiDocsPage.tsx +583 -0
- package/src/web/components/common/Icons.tsx +8 -0
- package/src/web/components/common/Modal.tsx +183 -0
- package/src/web/components/layout/Sidebar.tsx +7 -1
- package/src/web/components/mcp/IntegrationsPanel.tsx +743 -0
- package/src/web/components/mcp/McpPage.tsx +242 -83
- package/src/web/components/settings/SettingsPage.tsx +24 -9
- package/src/web/components/tasks/TasksPage.tsx +1 -1
- package/src/web/index.html +1 -1
- package/src/web/types.ts +4 -1
- package/dist/App.hzbfeg94.js +0 -217
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
// Composio Integration Provider
|
|
2
|
+
// https://docs.composio.dev/api-reference
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
IntegrationProvider,
|
|
6
|
+
IntegrationApp,
|
|
7
|
+
ConnectedAccount,
|
|
8
|
+
ConnectionRequest,
|
|
9
|
+
ConnectionCredentials,
|
|
10
|
+
} from "./index";
|
|
11
|
+
|
|
12
|
+
const COMPOSIO_API_BASE = "https://backend.composio.dev";
|
|
13
|
+
|
|
14
|
+
export const ComposioProvider: IntegrationProvider = {
|
|
15
|
+
id: "composio",
|
|
16
|
+
name: "Composio",
|
|
17
|
+
|
|
18
|
+
async listApps(apiKey: string): Promise<IntegrationApp[]> {
|
|
19
|
+
const res = await fetch(`${COMPOSIO_API_BASE}/api/v3/toolkits`, {
|
|
20
|
+
headers: {
|
|
21
|
+
"x-api-key": apiKey,
|
|
22
|
+
"Content-Type": "application/json",
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (!res.ok) {
|
|
27
|
+
console.error("Composio listApps error:", res.status, await res.text());
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const data = await res.json();
|
|
32
|
+
const items = data.items || data.toolkits || data || [];
|
|
33
|
+
|
|
34
|
+
return items.map((item: any) => ({
|
|
35
|
+
id: item.slug || item.key || item.name,
|
|
36
|
+
name: item.name || item.slug,
|
|
37
|
+
slug: item.slug || item.key || item.name?.toLowerCase(),
|
|
38
|
+
description: item.meta?.description || item.description || null,
|
|
39
|
+
logo: item.meta?.logo || item.logo || null,
|
|
40
|
+
categories: (item.meta?.categories || item.categories || []).map((c: any) =>
|
|
41
|
+
typeof c === "string" ? c : c.name || c.id
|
|
42
|
+
),
|
|
43
|
+
authSchemes: item.auth_schemes || item.authSchemes || ["OAUTH2"],
|
|
44
|
+
}));
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
async listConnectedAccounts(apiKey: string, userId: string): Promise<ConnectedAccount[]> {
|
|
48
|
+
console.log(`Fetching connected accounts for user: ${userId}`);
|
|
49
|
+
const res = await fetch(
|
|
50
|
+
`${COMPOSIO_API_BASE}/api/v3/connected_accounts?user_id=${encodeURIComponent(userId)}&limit=100`,
|
|
51
|
+
{
|
|
52
|
+
headers: {
|
|
53
|
+
"x-api-key": apiKey,
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if (!res.ok) {
|
|
60
|
+
console.error("Composio listConnectedAccounts error:", res.status, await res.text());
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const data = await res.json();
|
|
65
|
+
const items = data.items || data.connections || data || [];
|
|
66
|
+
console.log(`Found ${items.length} connected accounts`);
|
|
67
|
+
if (items.length > 0) {
|
|
68
|
+
console.log(`Sample account:`, JSON.stringify(items[0], null, 2));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return items.map((item: any) => ({
|
|
72
|
+
id: item.id,
|
|
73
|
+
appId: item.toolkit?.slug || item.toolkit_slug || item.appId || item.app_id,
|
|
74
|
+
appName: item.toolkit?.name || item.toolkit_name || item.appName || item.toolkit?.slug,
|
|
75
|
+
status: mapStatus(item.status),
|
|
76
|
+
createdAt: item.created_at || item.createdAt || new Date().toISOString(),
|
|
77
|
+
metadata: {
|
|
78
|
+
entityId: item.entity_id || item.user_id,
|
|
79
|
+
integrationId: item.auth_config?.id,
|
|
80
|
+
},
|
|
81
|
+
}));
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
async initiateConnection(
|
|
85
|
+
apiKey: string,
|
|
86
|
+
userId: string,
|
|
87
|
+
appSlug: string,
|
|
88
|
+
redirectUrl: string,
|
|
89
|
+
credentials?: ConnectionCredentials
|
|
90
|
+
): Promise<ConnectionRequest> {
|
|
91
|
+
const isApiKeyAuth = credentials?.authScheme === "API_KEY" && credentials?.apiKey;
|
|
92
|
+
|
|
93
|
+
console.log(`Initiating ${isApiKeyAuth ? "API_KEY" : "OAuth"} connection for ${appSlug}`);
|
|
94
|
+
|
|
95
|
+
// Step 1: Get toolkit info to find the API key field name
|
|
96
|
+
let apiKeyFieldName = "api_key"; // default
|
|
97
|
+
try {
|
|
98
|
+
const toolkitRes = await fetch(`${COMPOSIO_API_BASE}/api/v3/toolkits/${appSlug}`, {
|
|
99
|
+
headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
|
|
100
|
+
});
|
|
101
|
+
if (toolkitRes.ok) {
|
|
102
|
+
const toolkitData = await toolkitRes.json();
|
|
103
|
+
// Find the API_KEY auth config details
|
|
104
|
+
const apiKeyConfig = toolkitData.auth_config_details?.find(
|
|
105
|
+
(c: any) => c.mode === "API_KEY"
|
|
106
|
+
);
|
|
107
|
+
if (apiKeyConfig?.fields?.connected_account_initiation?.required?.[0]?.name) {
|
|
108
|
+
apiKeyFieldName = apiKeyConfig.fields.connected_account_initiation.required[0].name;
|
|
109
|
+
}
|
|
110
|
+
console.log(`Toolkit ${appSlug} API key field: ${apiKeyFieldName}`);
|
|
111
|
+
}
|
|
112
|
+
} catch (e) {
|
|
113
|
+
console.error(`Failed to get toolkit info:`, e);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Step 2: Get existing auth configs for this toolkit
|
|
117
|
+
const configsRes = await fetch(`${COMPOSIO_API_BASE}/api/v3/auth_configs?toolkit=${appSlug}`, {
|
|
118
|
+
headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
let authConfigId: string | null = null;
|
|
122
|
+
|
|
123
|
+
if (configsRes.ok) {
|
|
124
|
+
const configsData = await configsRes.json();
|
|
125
|
+
const allConfigs = configsData.items || [];
|
|
126
|
+
|
|
127
|
+
// Filter to configs for this toolkit
|
|
128
|
+
const configs = allConfigs.filter((c: any) => {
|
|
129
|
+
const toolkit = c.toolkit?.slug || c.toolkit_slug || "";
|
|
130
|
+
return toolkit.toLowerCase() === appSlug.toLowerCase();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
console.log(`Found ${configs.length} auth configs for ${appSlug}`);
|
|
134
|
+
|
|
135
|
+
if (isApiKeyAuth) {
|
|
136
|
+
const apiKeyConfig = configs.find((c: any) => c.auth_scheme === "API_KEY");
|
|
137
|
+
if (apiKeyConfig) {
|
|
138
|
+
authConfigId = apiKeyConfig.id;
|
|
139
|
+
console.log(`Using existing API_KEY config: ${authConfigId}`);
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
const oauthConfig = configs.find((c: any) =>
|
|
143
|
+
c.auth_scheme === "OAUTH2" || c.is_composio_managed
|
|
144
|
+
);
|
|
145
|
+
if (oauthConfig) {
|
|
146
|
+
authConfigId = oauthConfig.id;
|
|
147
|
+
console.log(`Using existing OAuth config: ${authConfigId}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Step 3: Create auth config if not found
|
|
153
|
+
if (!authConfigId) {
|
|
154
|
+
console.log(`Creating new auth config for ${appSlug}...`);
|
|
155
|
+
|
|
156
|
+
const createBody = isApiKeyAuth
|
|
157
|
+
? {
|
|
158
|
+
toolkit: { slug: appSlug },
|
|
159
|
+
auth_config: {
|
|
160
|
+
type: "use_custom_auth",
|
|
161
|
+
authScheme: "API_KEY",
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
: {
|
|
165
|
+
toolkit: { slug: appSlug },
|
|
166
|
+
auth_config: {
|
|
167
|
+
type: "use_composio_managed_auth",
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const createRes = await fetch(`${COMPOSIO_API_BASE}/api/v3/auth_configs`, {
|
|
172
|
+
method: "POST",
|
|
173
|
+
headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
|
|
174
|
+
body: JSON.stringify(createBody),
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
if (createRes.ok) {
|
|
178
|
+
const createData = await createRes.json();
|
|
179
|
+
authConfigId = createData.auth_config?.id;
|
|
180
|
+
console.log(`Created auth config: ${authConfigId}`);
|
|
181
|
+
} else {
|
|
182
|
+
const errText = await createRes.text();
|
|
183
|
+
console.error(`Failed to create auth config:`, errText);
|
|
184
|
+
throw new Error(`Failed to create auth config: ${errText}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (!authConfigId) {
|
|
189
|
+
throw new Error(`Could not find or create auth configuration for ${appSlug}.`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Step 4: Create connected account
|
|
193
|
+
const connectionBody: any = {
|
|
194
|
+
auth_config: { id: authConfigId },
|
|
195
|
+
connection: {
|
|
196
|
+
user_id: userId,
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
if (isApiKeyAuth && credentials?.apiKey) {
|
|
201
|
+
connectionBody.connection.state = {
|
|
202
|
+
authScheme: "API_KEY",
|
|
203
|
+
val: {
|
|
204
|
+
[apiKeyFieldName]: credentials.apiKey,
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
} else {
|
|
208
|
+
connectionBody.connection.callback_url = redirectUrl;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
console.log(`Creating connected account...`);
|
|
212
|
+
|
|
213
|
+
const res = await fetch(`${COMPOSIO_API_BASE}/api/v3/connected_accounts`, {
|
|
214
|
+
method: "POST",
|
|
215
|
+
headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
|
|
216
|
+
body: JSON.stringify(connectionBody),
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (!res.ok) {
|
|
220
|
+
const text = await res.text();
|
|
221
|
+
console.error("Composio connection error:", res.status, text);
|
|
222
|
+
throw new Error(`Failed to create connection: ${text}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const data = await res.json();
|
|
226
|
+
const status = (data.status || "").toLowerCase();
|
|
227
|
+
|
|
228
|
+
console.log(`Connection created: ${data.id}, status: ${status}`);
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
redirectUrl: isApiKeyAuth ? null : (data.redirect_url || data.redirectUrl),
|
|
232
|
+
connectionId: data.id,
|
|
233
|
+
status: (status === "active" || status === "connected") ? "active" : "pending",
|
|
234
|
+
};
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
async getConnectionStatus(apiKey: string, connectionId: string): Promise<ConnectedAccount | null> {
|
|
238
|
+
const res = await fetch(`${COMPOSIO_API_BASE}/api/v3/connected_accounts/${connectionId}`, {
|
|
239
|
+
headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
if (!res.ok) {
|
|
243
|
+
if (res.status === 404) return null;
|
|
244
|
+
console.error("Composio getConnectionStatus error:", res.status, await res.text());
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const item = await res.json();
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
id: item.id,
|
|
252
|
+
appId: item.toolkit_slug || item.appId,
|
|
253
|
+
appName: item.toolkit_name || item.appName || item.toolkit_slug,
|
|
254
|
+
status: mapStatus(item.status),
|
|
255
|
+
createdAt: item.created_at || item.createdAt,
|
|
256
|
+
metadata: {
|
|
257
|
+
entityId: item.entity_id,
|
|
258
|
+
integrationId: item.integration_id,
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
async disconnect(apiKey: string, connectionId: string): Promise<boolean> {
|
|
264
|
+
const res = await fetch(`${COMPOSIO_API_BASE}/api/v3/connected_accounts/${connectionId}`, {
|
|
265
|
+
method: "DELETE",
|
|
266
|
+
headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
return res.ok;
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// MCP Server types
|
|
274
|
+
export interface McpServer {
|
|
275
|
+
id: string;
|
|
276
|
+
name: string;
|
|
277
|
+
authConfigIds: string[];
|
|
278
|
+
mcpUrl: string;
|
|
279
|
+
toolkits: string[];
|
|
280
|
+
toolkitIcons: Record<string, string>;
|
|
281
|
+
allowedTools: string[];
|
|
282
|
+
createdAt: string;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export async function listMcpServers(apiKey: string): Promise<McpServer[]> {
|
|
286
|
+
const res = await fetch(`${COMPOSIO_API_BASE}/api/v3/mcp/servers`, {
|
|
287
|
+
headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
if (!res.ok) {
|
|
291
|
+
console.error("Failed to list MCP servers:", await res.text());
|
|
292
|
+
return [];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const data = await res.json();
|
|
296
|
+
const items = data.items || [];
|
|
297
|
+
|
|
298
|
+
return items.map((item: any) => ({
|
|
299
|
+
id: item.id,
|
|
300
|
+
name: item.name,
|
|
301
|
+
authConfigIds: item.auth_config_ids || [],
|
|
302
|
+
mcpUrl: item.mcp_url,
|
|
303
|
+
toolkits: item.toolkits || [],
|
|
304
|
+
toolkitIcons: item.toolkit_icons || {},
|
|
305
|
+
allowedTools: item.allowed_tools || [],
|
|
306
|
+
createdAt: item.created_at,
|
|
307
|
+
}));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export async function createMcpServer(
|
|
311
|
+
apiKey: string,
|
|
312
|
+
name: string,
|
|
313
|
+
authConfigIds: string[],
|
|
314
|
+
allowedTools?: string[]
|
|
315
|
+
): Promise<McpServer | null> {
|
|
316
|
+
// Use auth_config_ids - Composio includes all tools by default when allowed_tools is not provided
|
|
317
|
+
const body: any = {
|
|
318
|
+
name,
|
|
319
|
+
auth_config_ids: authConfigIds,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
// Only set allowed_tools if explicitly provided to restrict tools
|
|
323
|
+
// If not provided, Composio enables all tools by default
|
|
324
|
+
if (allowedTools?.length) {
|
|
325
|
+
body.allowed_tools = allowedTools;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const res = await fetch(`${COMPOSIO_API_BASE}/api/v3/mcp/servers`, {
|
|
329
|
+
method: "POST",
|
|
330
|
+
headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
|
|
331
|
+
body: JSON.stringify(body),
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
if (!res.ok) {
|
|
335
|
+
const errText = await res.text();
|
|
336
|
+
console.error("Failed to create MCP server:", errText);
|
|
337
|
+
throw new Error(`Failed to create MCP server: ${errText}`);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const item = await res.json();
|
|
341
|
+
return {
|
|
342
|
+
id: item.id,
|
|
343
|
+
name: item.name,
|
|
344
|
+
authConfigIds: item.auth_config_ids || [],
|
|
345
|
+
mcpUrl: item.mcp_url,
|
|
346
|
+
toolkits: item.toolkits || [],
|
|
347
|
+
toolkitIcons: item.toolkit_icons || {},
|
|
348
|
+
allowedTools: item.allowed_tools || [],
|
|
349
|
+
createdAt: item.created_at,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export async function deleteMcpServer(apiKey: string, serverId: string): Promise<boolean> {
|
|
354
|
+
const res = await fetch(`${COMPOSIO_API_BASE}/api/v3/mcp/servers/${serverId}`, {
|
|
355
|
+
method: "DELETE",
|
|
356
|
+
headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
|
|
357
|
+
});
|
|
358
|
+
return res.ok;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Create a server instance for a user
|
|
362
|
+
export async function createMcpServerInstance(
|
|
363
|
+
apiKey: string,
|
|
364
|
+
serverId: string,
|
|
365
|
+
userId: string
|
|
366
|
+
): Promise<{ id: string; instanceId: string } | null> {
|
|
367
|
+
const res = await fetch(`${COMPOSIO_API_BASE}/api/v3/mcp/servers/${serverId}/instances`, {
|
|
368
|
+
method: "POST",
|
|
369
|
+
headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
|
|
370
|
+
body: JSON.stringify({ user_id: userId }),
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
if (!res.ok) {
|
|
374
|
+
const errText = await res.text();
|
|
375
|
+
console.error("Failed to create MCP server instance:", errText);
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const data = await res.json();
|
|
380
|
+
return {
|
|
381
|
+
id: data.id,
|
|
382
|
+
instanceId: data.instance_id,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Get user_id from connected accounts for a specific auth config
|
|
387
|
+
export async function getUserIdForAuthConfig(
|
|
388
|
+
apiKey: string,
|
|
389
|
+
authConfigId: string
|
|
390
|
+
): Promise<string | null> {
|
|
391
|
+
const res = await fetch(`${COMPOSIO_API_BASE}/api/v3/connected_accounts?limit=100`, {
|
|
392
|
+
headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
if (!res.ok) return null;
|
|
396
|
+
|
|
397
|
+
const data = await res.json();
|
|
398
|
+
const items = data.items || [];
|
|
399
|
+
|
|
400
|
+
// Find an active connected account for this auth config
|
|
401
|
+
const account = items.find((item: any) =>
|
|
402
|
+
item.auth_config?.id === authConfigId && item.status === "ACTIVE"
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
return account?.user_id || null;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Get auth config ID for a connected account's toolkit
|
|
409
|
+
export async function getAuthConfigForToolkit(
|
|
410
|
+
apiKey: string,
|
|
411
|
+
toolkitSlug: string,
|
|
412
|
+
authScheme: "API_KEY" | "OAUTH2" = "API_KEY"
|
|
413
|
+
): Promise<string | null> {
|
|
414
|
+
const res = await fetch(`${COMPOSIO_API_BASE}/api/v3/auth_configs?toolkit=${toolkitSlug}`, {
|
|
415
|
+
headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
if (!res.ok) return null;
|
|
419
|
+
|
|
420
|
+
const data = await res.json();
|
|
421
|
+
const configs = (data.items || []).filter((c: any) => {
|
|
422
|
+
const toolkit = c.toolkit?.slug || "";
|
|
423
|
+
return toolkit.toLowerCase() === toolkitSlug.toLowerCase();
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
const config = configs.find((c: any) => c.auth_scheme === authScheme);
|
|
427
|
+
return config?.id || null;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function mapStatus(status: string): ConnectedAccount["status"] {
|
|
431
|
+
const s = (status || "").toLowerCase();
|
|
432
|
+
if (s === "active" || s === "connected") return "active";
|
|
433
|
+
if (s === "pending" || s === "initiated") return "pending";
|
|
434
|
+
if (s === "failed" || s === "error") return "failed";
|
|
435
|
+
if (s === "expired") return "expired";
|
|
436
|
+
return "pending";
|
|
437
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Generic Integration Provider Interface
|
|
2
|
+
// Allows multiple providers (Composio, Smithery, etc.) to offer OAuth app connections
|
|
3
|
+
|
|
4
|
+
export interface IntegrationApp {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
slug: string;
|
|
8
|
+
description: string | null;
|
|
9
|
+
logo: string | null;
|
|
10
|
+
categories: string[];
|
|
11
|
+
authSchemes: string[]; // e.g., ["OAUTH2", "API_KEY"]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ConnectedAccount {
|
|
15
|
+
id: string;
|
|
16
|
+
appId: string;
|
|
17
|
+
appName: string;
|
|
18
|
+
status: "active" | "pending" | "failed" | "expired";
|
|
19
|
+
createdAt: string;
|
|
20
|
+
metadata?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ConnectionRequest {
|
|
24
|
+
redirectUrl: string | null; // null for API_KEY auth (no redirect needed)
|
|
25
|
+
connectionId?: string;
|
|
26
|
+
status?: "active" | "pending"; // API_KEY connections are immediately active
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ConnectionCredentials {
|
|
30
|
+
authScheme: "OAUTH2" | "API_KEY" | "BEARER_TOKEN" | "BASIC";
|
|
31
|
+
apiKey?: string;
|
|
32
|
+
bearerToken?: string;
|
|
33
|
+
username?: string;
|
|
34
|
+
password?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface IntegrationProvider {
|
|
38
|
+
id: string;
|
|
39
|
+
name: string;
|
|
40
|
+
|
|
41
|
+
// List available apps/toolkits
|
|
42
|
+
listApps(apiKey: string): Promise<IntegrationApp[]>;
|
|
43
|
+
|
|
44
|
+
// List user's connected accounts
|
|
45
|
+
listConnectedAccounts(apiKey: string, userId: string): Promise<ConnectedAccount[]>;
|
|
46
|
+
|
|
47
|
+
// Initiate connection (OAuth or API Key)
|
|
48
|
+
initiateConnection(
|
|
49
|
+
apiKey: string,
|
|
50
|
+
userId: string,
|
|
51
|
+
appSlug: string,
|
|
52
|
+
redirectUrl: string,
|
|
53
|
+
credentials?: ConnectionCredentials
|
|
54
|
+
): Promise<ConnectionRequest>;
|
|
55
|
+
|
|
56
|
+
// Check connection status
|
|
57
|
+
getConnectionStatus(apiKey: string, connectionId: string): Promise<ConnectedAccount | null>;
|
|
58
|
+
|
|
59
|
+
// Disconnect/revoke
|
|
60
|
+
disconnect(apiKey: string, connectionId: string): Promise<boolean>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Provider registry
|
|
64
|
+
const providers: Map<string, IntegrationProvider> = new Map();
|
|
65
|
+
|
|
66
|
+
export function registerProvider(provider: IntegrationProvider) {
|
|
67
|
+
providers.set(provider.id, provider);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function getProvider(id: string): IntegrationProvider | undefined {
|
|
71
|
+
return providers.get(id);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function getAllProviders(): IntegrationProvider[] {
|
|
75
|
+
return Array.from(providers.values());
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getProviderIds(): string[] {
|
|
79
|
+
return Array.from(providers.keys());
|
|
80
|
+
}
|