@muxoai/platform 1.3.3 → 1.4.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/.agents/plugins/marketplace.json +2 -2
- package/.claude-plugin/marketplace.json +2 -2
- package/ARCHITECTURE.md +16 -6
- package/README.md +7 -5
- package/dist/cli.js +393 -13
- package/dist/composio-catalog.d.ts +58 -0
- package/dist/composio-catalog.js +474 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.js +16 -0
- package/dist/durable-workflow.d.ts +45 -0
- package/dist/durable-workflow.js +90 -0
- package/dist/engine.d.ts +276 -5
- package/dist/engine.js +1261 -71
- package/dist/mcp-schema.d.ts +125 -0
- package/dist/mcp-schema.js +1076 -0
- package/dist/mcp.d.ts +2 -30
- package/dist/mcp.js +505 -539
- package/dist/memory-graph.d.ts +21 -0
- package/dist/memory-graph.js +173 -0
- package/dist/nl.js +155 -0
- package/dist/oauth.d.ts +32 -0
- package/dist/oauth.js +134 -0
- package/dist/policy.d.ts +75 -1
- package/dist/policy.js +1119 -0
- package/dist/providers/composio-instant.js +225 -5
- package/dist/providers/composio-sdk.d.ts +68 -5
- package/dist/providers/composio-sdk.js +683 -78
- package/dist/providers/microsoft-governance.d.ts +28 -1
- package/dist/providers/microsoft-governance.js +224 -11
- package/dist/providers/stripe-billing.d.ts +42 -0
- package/dist/providers/stripe-billing.js +230 -1
- package/dist/providers/stripe-projects.d.ts +451 -3
- package/dist/providers/stripe-projects.js +2106 -87
- package/dist/providers/supermemory.d.ts +74 -1
- package/dist/providers/supermemory.js +426 -0
- package/dist/provisioning-managed.d.ts +50 -0
- package/dist/provisioning-managed.js +155 -0
- package/dist/sdk.d.ts +538 -8
- package/dist/sdk.js +377 -10
- package/dist/server.js +1841 -232
- package/dist/store.d.ts +113 -6
- package/dist/store.js +675 -52
- package/dist/types.d.ts +212 -5
- package/dist/ui/console-html.d.ts +10 -1
- package/dist/ui/console-html.js +1188 -462
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/plugins/muxo/.claude-plugin/plugin.json +1 -1
- package/plugins/muxo/.codex-plugin/plugin.json +1 -1
- package/plugins/muxo/plugin.json +2 -2
- package/plugins/muxo/skills/muxo/SKILL.md +42 -1
- package/plugins/muxo/skills/muxo/references/mcp.md +3 -2
- package/plugins/muxo/skills/setup/SKILL.md +8 -0
|
@@ -1,4 +1,28 @@
|
|
|
1
|
+
import { Composio } from "@composio/core";
|
|
2
|
+
import { matchAppToolSlug } from "../mcp-schema.js";
|
|
3
|
+
import { composioAuthConfigScopes, ENTERPRISE_TOOLKITS_CATALOG, filterComposioToolkits, getSampleActionSchema, listComposioTriggerTypes, } from "../composio-catalog.js";
|
|
1
4
|
const CURATED_APP_TOOLS = [
|
|
5
|
+
{
|
|
6
|
+
slug: "app.github.list_repos",
|
|
7
|
+
upstreamSlug: "GITHUB_LIST_REPOSITORIES_FOR_THE_AUTHENTICATED_USER",
|
|
8
|
+
name: "GitHub: List Repositories",
|
|
9
|
+
description: "List repositories accessible to the connected GitHub account via Muxo OAuth.",
|
|
10
|
+
lane: "composio_sdk",
|
|
11
|
+
providerSlug: "github",
|
|
12
|
+
providerName: "GitHub",
|
|
13
|
+
categories: ["developer_tools", "version_control", "git"],
|
|
14
|
+
riskLevel: "read",
|
|
15
|
+
requiresAuth: true,
|
|
16
|
+
creditCost: 0,
|
|
17
|
+
billingMode: "customer_oauth",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
visibility: { type: "string", enum: ["all", "public", "private"], default: "all" },
|
|
22
|
+
per_page: { type: "number", default: 20 },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
2
26
|
{
|
|
3
27
|
slug: "app.github.create_issue",
|
|
4
28
|
upstreamSlug: "GITHUB_CREATE_AN_ISSUE",
|
|
@@ -90,6 +114,27 @@ const CURATED_APP_TOOLS = [
|
|
|
90
114
|
},
|
|
91
115
|
},
|
|
92
116
|
},
|
|
117
|
+
{
|
|
118
|
+
slug: "app.gmail.list_messages",
|
|
119
|
+
upstreamSlug: "GMAIL_FETCH_EMAILS",
|
|
120
|
+
name: "Gmail: List Messages",
|
|
121
|
+
description: "Read recent email threads from the connected Gmail inbox.",
|
|
122
|
+
lane: "composio_sdk",
|
|
123
|
+
providerSlug: "gmail",
|
|
124
|
+
providerName: "Gmail",
|
|
125
|
+
categories: ["email", "communication"],
|
|
126
|
+
riskLevel: "read",
|
|
127
|
+
requiresAuth: true,
|
|
128
|
+
creditCost: 0,
|
|
129
|
+
billingMode: "customer_oauth",
|
|
130
|
+
inputSchema: {
|
|
131
|
+
type: "object",
|
|
132
|
+
properties: {
|
|
133
|
+
query: { type: "string", description: "Gmail search query (e.g. 'is:unread')" },
|
|
134
|
+
max_results: { type: "number", default: 10 },
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
93
138
|
{
|
|
94
139
|
slug: "app.slack.send_message",
|
|
95
140
|
upstreamSlug: "SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL",
|
|
@@ -112,6 +157,28 @@ const CURATED_APP_TOOLS = [
|
|
|
112
157
|
},
|
|
113
158
|
},
|
|
114
159
|
},
|
|
160
|
+
{
|
|
161
|
+
slug: "app.slack.post_message",
|
|
162
|
+
upstreamSlug: "SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL",
|
|
163
|
+
name: "Slack: Post Channel Message",
|
|
164
|
+
description: "Post a message to a Slack channel using the connected Slack workspace.",
|
|
165
|
+
lane: "composio_sdk",
|
|
166
|
+
providerSlug: "slack",
|
|
167
|
+
providerName: "Slack",
|
|
168
|
+
categories: ["messaging", "team_chat", "communication", "chat"],
|
|
169
|
+
riskLevel: "write",
|
|
170
|
+
requiresAuth: true,
|
|
171
|
+
creditCost: 0,
|
|
172
|
+
billingMode: "customer_oauth",
|
|
173
|
+
inputSchema: {
|
|
174
|
+
type: "object",
|
|
175
|
+
required: ["channel", "text"],
|
|
176
|
+
properties: {
|
|
177
|
+
channel: { type: "string", description: "Slack channel ID or name" },
|
|
178
|
+
text: { type: "string", description: "Message text (Markdown supported)" },
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
},
|
|
115
182
|
{
|
|
116
183
|
slug: "app.notion.search_pages",
|
|
117
184
|
upstreamSlug: "NOTION_SEARCH_NOTION_PAGE",
|
|
@@ -176,6 +243,138 @@ const CURATED_APP_TOOLS = [
|
|
|
176
243
|
},
|
|
177
244
|
},
|
|
178
245
|
},
|
|
246
|
+
{
|
|
247
|
+
slug: "app.jira.create_issue",
|
|
248
|
+
upstreamSlug: "JIRA_CREATE_ISSUE",
|
|
249
|
+
name: "Jira: Create Issue",
|
|
250
|
+
description: "Create a Jira issue in a connected Atlassian project via Muxo OAuth.",
|
|
251
|
+
lane: "composio_sdk",
|
|
252
|
+
providerSlug: "jira",
|
|
253
|
+
providerName: "Jira",
|
|
254
|
+
categories: ["project_management", "engineering"],
|
|
255
|
+
riskLevel: "write",
|
|
256
|
+
requiresAuth: true,
|
|
257
|
+
creditCost: 0,
|
|
258
|
+
billingMode: "customer_oauth",
|
|
259
|
+
inputSchema: {
|
|
260
|
+
type: "object",
|
|
261
|
+
required: ["project_key", "summary"],
|
|
262
|
+
properties: {
|
|
263
|
+
project_key: { type: "string", description: "Jira project key (e.g. ENG)" },
|
|
264
|
+
summary: { type: "string", description: "Issue summary" },
|
|
265
|
+
description: { type: "string", description: "Issue description" },
|
|
266
|
+
issue_type: { type: "string", default: "Task" },
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
slug: "app.hubspot.search_contacts",
|
|
272
|
+
upstreamSlug: "HUBSPOT_SEARCH_CONTACTS",
|
|
273
|
+
name: "HubSpot: Search CRM Contacts",
|
|
274
|
+
description: "Search CRM contacts in the connected HubSpot account via Muxo OAuth.",
|
|
275
|
+
lane: "composio_sdk",
|
|
276
|
+
providerSlug: "hubspot",
|
|
277
|
+
providerName: "HubSpot",
|
|
278
|
+
categories: ["crm", "sales"],
|
|
279
|
+
riskLevel: "read",
|
|
280
|
+
requiresAuth: true,
|
|
281
|
+
creditCost: 0,
|
|
282
|
+
billingMode: "customer_oauth",
|
|
283
|
+
inputSchema: {
|
|
284
|
+
type: "object",
|
|
285
|
+
properties: {
|
|
286
|
+
query: { type: "string", description: "Contact name, email, or company search query" },
|
|
287
|
+
limit: { type: "number", default: 10 },
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
slug: "app.discord.send_message",
|
|
293
|
+
upstreamSlug: "DISCORD_SEND_MESSAGE",
|
|
294
|
+
name: "Discord: Send Channel Message",
|
|
295
|
+
description: "Send a message to a Discord channel using the connected Discord account.",
|
|
296
|
+
lane: "composio_sdk",
|
|
297
|
+
providerSlug: "discord",
|
|
298
|
+
providerName: "Discord",
|
|
299
|
+
categories: ["communication", "chat"],
|
|
300
|
+
riskLevel: "write",
|
|
301
|
+
requiresAuth: true,
|
|
302
|
+
creditCost: 0,
|
|
303
|
+
billingMode: "customer_oauth",
|
|
304
|
+
inputSchema: {
|
|
305
|
+
type: "object",
|
|
306
|
+
required: ["channel_id", "content"],
|
|
307
|
+
properties: {
|
|
308
|
+
channel_id: { type: "string", description: "Discord channel ID" },
|
|
309
|
+
content: { type: "string", description: "Message content" },
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
slug: "app.googledrive.search_files",
|
|
315
|
+
upstreamSlug: "GOOGLEDRIVE_FIND_FILE",
|
|
316
|
+
name: "Google Drive: Search Files & Folders",
|
|
317
|
+
description: "Search files and folders in the connected Google Drive account.",
|
|
318
|
+
lane: "composio_sdk",
|
|
319
|
+
providerSlug: "googledrive",
|
|
320
|
+
providerName: "Google Drive",
|
|
321
|
+
categories: ["storage", "docs", "productivity"],
|
|
322
|
+
riskLevel: "read",
|
|
323
|
+
requiresAuth: true,
|
|
324
|
+
creditCost: 0,
|
|
325
|
+
billingMode: "customer_oauth",
|
|
326
|
+
inputSchema: {
|
|
327
|
+
type: "object",
|
|
328
|
+
properties: {
|
|
329
|
+
query: { type: "string", description: "File or folder search query" },
|
|
330
|
+
pageSize: { type: "number", default: 10 },
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
slug: "app.airtable.list_records",
|
|
336
|
+
upstreamSlug: "AIRTABLE_LIST_RECORDS",
|
|
337
|
+
name: "Airtable: List Base Records",
|
|
338
|
+
description: "List records from an Airtable base and table via Muxo OAuth.",
|
|
339
|
+
lane: "composio_sdk",
|
|
340
|
+
providerSlug: "airtable",
|
|
341
|
+
providerName: "Airtable",
|
|
342
|
+
categories: ["database", "productivity"],
|
|
343
|
+
riskLevel: "read",
|
|
344
|
+
requiresAuth: true,
|
|
345
|
+
creditCost: 0,
|
|
346
|
+
billingMode: "customer_oauth",
|
|
347
|
+
inputSchema: {
|
|
348
|
+
type: "object",
|
|
349
|
+
required: ["base_id", "table_name"],
|
|
350
|
+
properties: {
|
|
351
|
+
base_id: { type: "string", description: "Airtable Base ID" },
|
|
352
|
+
table_name: { type: "string", description: "Table name or ID" },
|
|
353
|
+
max_records: { type: "number", default: 20 },
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
slug: "app.salesforce.query_records",
|
|
359
|
+
upstreamSlug: "SALESFORCE_RUN_SOQL_QUERY",
|
|
360
|
+
name: "Salesforce: Query CRM Records",
|
|
361
|
+
description: "Run a read query against the connected Salesforce CRM org.",
|
|
362
|
+
lane: "composio_sdk",
|
|
363
|
+
providerSlug: "salesforce",
|
|
364
|
+
providerName: "Salesforce",
|
|
365
|
+
categories: ["crm", "sales"],
|
|
366
|
+
riskLevel: "read",
|
|
367
|
+
requiresAuth: true,
|
|
368
|
+
creditCost: 0,
|
|
369
|
+
billingMode: "customer_oauth",
|
|
370
|
+
inputSchema: {
|
|
371
|
+
type: "object",
|
|
372
|
+
required: ["query"],
|
|
373
|
+
properties: {
|
|
374
|
+
query: { type: "string", description: "SOQL query string" },
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
},
|
|
179
378
|
];
|
|
180
379
|
function classifyComposioRisk(slug, tags = []) {
|
|
181
380
|
const upper = `${slug} ${tags.join(" ")}`.toUpperCase();
|
|
@@ -192,11 +391,42 @@ function classifyComposioRisk(slug, tags = []) {
|
|
|
192
391
|
export class ComposioSdkAdapter {
|
|
193
392
|
config;
|
|
194
393
|
liveToolsCache = new Map();
|
|
394
|
+
sdkClients = new Map();
|
|
395
|
+
customAuthConfigs = new Map();
|
|
396
|
+
activeTriggers = new Map();
|
|
397
|
+
triggerEvents = [];
|
|
195
398
|
constructor(config) {
|
|
196
399
|
this.config = config;
|
|
197
400
|
for (const tool of CURATED_APP_TOOLS) {
|
|
198
401
|
this.liveToolsCache.set(tool.slug, tool);
|
|
199
402
|
}
|
|
403
|
+
this.hydrateEnterpriseSampleActions();
|
|
404
|
+
}
|
|
405
|
+
hydrateEnterpriseSampleActions() {
|
|
406
|
+
for (const tk of ENTERPRISE_TOOLKITS_CATALOG) {
|
|
407
|
+
for (const act of tk.sampleActions) {
|
|
408
|
+
if (!this.liveToolsCache.has(act.muxoSlug)) {
|
|
409
|
+
this.liveToolsCache.set(act.muxoSlug, {
|
|
410
|
+
slug: act.muxoSlug,
|
|
411
|
+
upstreamSlug: act.upstreamSlug,
|
|
412
|
+
name: act.name,
|
|
413
|
+
description: `${tk.name}: ${act.name} (Composio v3 toolkit version ${tk.pinnedVersion})`,
|
|
414
|
+
lane: "composio_sdk",
|
|
415
|
+
providerSlug: tk.slug,
|
|
416
|
+
providerName: tk.name,
|
|
417
|
+
categories: tk.categories,
|
|
418
|
+
riskLevel: act.riskLevel,
|
|
419
|
+
requiresAuth: true,
|
|
420
|
+
creditCost: act.riskLevel === "read" ? 1 : 2,
|
|
421
|
+
billingMode: "customer_oauth",
|
|
422
|
+
inputSchema: getSampleActionSchema(act.muxoSlug, act.upstreamSlug),
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
resolveApiKey(composioApiKey) {
|
|
429
|
+
return (composioApiKey || this.config.composioApiKey || "").trim();
|
|
200
430
|
}
|
|
201
431
|
get headers() {
|
|
202
432
|
return {
|
|
@@ -204,12 +434,209 @@ export class ComposioSdkAdapter {
|
|
|
204
434
|
"Content-Type": "application/json",
|
|
205
435
|
};
|
|
206
436
|
}
|
|
437
|
+
getHeaders(composioApiKey) {
|
|
438
|
+
return {
|
|
439
|
+
"x-api-key": this.resolveApiKey(composioApiKey),
|
|
440
|
+
"Content-Type": "application/json",
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
getComposioClient(composioApiKey) {
|
|
444
|
+
const apiKey = this.resolveApiKey(composioApiKey);
|
|
445
|
+
if (!apiKey)
|
|
446
|
+
return null;
|
|
447
|
+
const rawBaseUrl = (this.config.composioBaseUrl || "https://backend.composio.dev/api/v3").replace(/\/+$/, "");
|
|
448
|
+
const sdkBaseUrl = rawBaseUrl.replace(/\/api\/v3$/i, "") || "https://backend.composio.dev";
|
|
449
|
+
const cacheKey = `${apiKey}::${sdkBaseUrl}`;
|
|
450
|
+
const existing = this.sdkClients.get(cacheKey);
|
|
451
|
+
if (existing)
|
|
452
|
+
return existing;
|
|
453
|
+
try {
|
|
454
|
+
const client = new Composio({
|
|
455
|
+
apiKey,
|
|
456
|
+
baseURL: sdkBaseUrl,
|
|
457
|
+
allowTracking: false,
|
|
458
|
+
disableVersionCheck: true,
|
|
459
|
+
});
|
|
460
|
+
this.sdkClients.set(cacheKey, client);
|
|
461
|
+
return client;
|
|
462
|
+
}
|
|
463
|
+
catch {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
getSdkInfo(composioApiKey) {
|
|
468
|
+
const apiKey = this.resolveApiKey(composioApiKey);
|
|
469
|
+
return {
|
|
470
|
+
sdkPackage: "@composio/core",
|
|
471
|
+
sdkVersion: "0.6.11",
|
|
472
|
+
configured: Boolean(apiKey),
|
|
473
|
+
usingCustomerByok: Boolean(composioApiKey && composioApiKey.trim()),
|
|
474
|
+
baseUrl: this.config.composioBaseUrl,
|
|
475
|
+
curatedToolCount: CURATED_APP_TOOLS.length,
|
|
476
|
+
cachedToolCount: this.liveToolsCache.size,
|
|
477
|
+
capabilities: [
|
|
478
|
+
"tools.getRawComposioTools",
|
|
479
|
+
"tools.getRawComposioToolBySlug",
|
|
480
|
+
"tools.execute",
|
|
481
|
+
"toolkits.authorize",
|
|
482
|
+
"connectedAccounts.initiate",
|
|
483
|
+
"connectedAccounts.get",
|
|
484
|
+
"connectedAccounts.list",
|
|
485
|
+
"authConfigs.list",
|
|
486
|
+
],
|
|
487
|
+
};
|
|
488
|
+
}
|
|
207
489
|
getCuratedTools() {
|
|
208
490
|
return Array.from(this.liveToolsCache.values());
|
|
209
491
|
}
|
|
210
|
-
|
|
492
|
+
static classifyActionRisk(upstreamSlug, description = "") {
|
|
493
|
+
return classifyComposioRisk(upstreamSlug, [description]);
|
|
494
|
+
}
|
|
495
|
+
async listToolkits(params) {
|
|
496
|
+
// Hydrate dynamic sample actions into Muxo's liveToolsCache so agents can plan, inspect, and execute them immediately
|
|
497
|
+
this.hydrateEnterpriseSampleActions();
|
|
498
|
+
const list = filterComposioToolkits(params);
|
|
499
|
+
return {
|
|
500
|
+
toolkits: list,
|
|
501
|
+
total: list.length,
|
|
502
|
+
source: Boolean(this.resolveApiKey(params?.composioApiKey))
|
|
503
|
+
? "composio_v3_sdk"
|
|
504
|
+
: "muxo_enterprise_catalog",
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
async createCustomAuthConfig(params) {
|
|
508
|
+
const tkSlug = params.toolkitSlug.toLowerCase().trim();
|
|
509
|
+
const scopeMode = params.scopeMode || "read_only";
|
|
510
|
+
const scopes = composioAuthConfigScopes(tkSlug, scopeMode, params.customScopes);
|
|
511
|
+
const sdk = this.getComposioClient(params.composioApiKey);
|
|
512
|
+
if (sdk) {
|
|
513
|
+
try {
|
|
514
|
+
const created = await sdk.authConfigs.create(tkSlug, {
|
|
515
|
+
name: params.name || `muxo_${params.workspaceId}_${tkSlug}_${scopeMode}`,
|
|
516
|
+
type: params.credentials?.client_id
|
|
517
|
+
? "use_custom_auth"
|
|
518
|
+
: "use_composio_managed_auth",
|
|
519
|
+
authScheme: params.authScheme,
|
|
520
|
+
credentials: {
|
|
521
|
+
...(params.credentials || {}),
|
|
522
|
+
scopes: scopes.join(","),
|
|
523
|
+
},
|
|
524
|
+
});
|
|
525
|
+
const id = String(created?.id || `ac_${tkSlug}_${Date.now()}`);
|
|
526
|
+
this.customAuthConfigs.set(`${params.workspaceId}:${tkSlug}`, {
|
|
527
|
+
authConfigId: id,
|
|
528
|
+
toolkitSlug: tkSlug,
|
|
529
|
+
authScheme: params.authScheme,
|
|
530
|
+
scopeMode,
|
|
531
|
+
scopes,
|
|
532
|
+
});
|
|
533
|
+
return {
|
|
534
|
+
authConfigId: id,
|
|
535
|
+
toolkitSlug: tkSlug,
|
|
536
|
+
authScheme: params.authScheme,
|
|
537
|
+
scopeMode,
|
|
538
|
+
scopes,
|
|
539
|
+
source: "composio_v3_sdk",
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
catch {
|
|
543
|
+
// Fall through to deterministic local auth config registration
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
const fallbackId = `ac_muxo_${tkSlug}_${scopeMode}_${Buffer.from(params.workspaceId).toString("hex").slice(0, 8)}`;
|
|
547
|
+
this.customAuthConfigs.set(`${params.workspaceId}:${tkSlug}`, {
|
|
548
|
+
authConfigId: fallbackId,
|
|
549
|
+
toolkitSlug: tkSlug,
|
|
550
|
+
authScheme: params.authScheme,
|
|
551
|
+
scopeMode,
|
|
552
|
+
scopes,
|
|
553
|
+
});
|
|
554
|
+
return {
|
|
555
|
+
authConfigId: fallbackId,
|
|
556
|
+
toolkitSlug: tkSlug,
|
|
557
|
+
authScheme: params.authScheme,
|
|
558
|
+
scopeMode,
|
|
559
|
+
scopes,
|
|
560
|
+
source: "muxo_managed",
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
listTriggerTypes(providerSlug) {
|
|
564
|
+
return listComposioTriggerTypes(providerSlug);
|
|
565
|
+
}
|
|
566
|
+
async enableTrigger(params) {
|
|
567
|
+
const id = `trg_${params.providerSlug}_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
|
568
|
+
const sub = {
|
|
569
|
+
id,
|
|
570
|
+
workspaceId: params.workspaceId,
|
|
571
|
+
triggerSlug: params.triggerSlug.toUpperCase().trim(),
|
|
572
|
+
providerSlug: params.providerSlug.toLowerCase().trim(),
|
|
573
|
+
connectedAccountId: params.connectedAccountId || `ca_${params.providerSlug}_${params.workspaceId.slice(0, 8)}`,
|
|
574
|
+
webhookUrl: params.webhookUrl ||
|
|
575
|
+
`https://api.muxo.ai/api/v1/triggers/webhook`,
|
|
576
|
+
config: params.config || {},
|
|
577
|
+
status: "active",
|
|
578
|
+
createdAt: new Date().toISOString(),
|
|
579
|
+
};
|
|
580
|
+
this.activeTriggers.set(id, sub);
|
|
581
|
+
return sub;
|
|
582
|
+
}
|
|
583
|
+
listActiveTriggers(workspaceId) {
|
|
584
|
+
return Array.from(this.activeTriggers.values()).filter((t) => t.workspaceId === workspaceId);
|
|
585
|
+
}
|
|
586
|
+
recordTriggerEvent(record) {
|
|
587
|
+
this.triggerEvents.push(record);
|
|
588
|
+
if (this.triggerEvents.length > 500) {
|
|
589
|
+
this.triggerEvents.shift();
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
listTriggerEvents(workspaceId, limit = 50) {
|
|
593
|
+
return this.triggerEvents
|
|
594
|
+
.filter((e) => e.workspaceId === workspaceId)
|
|
595
|
+
.slice(-Math.max(1, limit))
|
|
596
|
+
.reverse();
|
|
597
|
+
}
|
|
598
|
+
mapRawComposioItemToMuxoTool(item) {
|
|
599
|
+
const upstreamSlug = String(item.slug || item.name || "");
|
|
600
|
+
if (!upstreamSlug)
|
|
601
|
+
return null;
|
|
602
|
+
const toolkitObj = (item.toolkit || {});
|
|
603
|
+
const providerSlug = String(toolkitObj.slug || upstreamSlug.split("_")[0] || "app").toLowerCase();
|
|
604
|
+
const providerName = String(toolkitObj.name || providerSlug.charAt(0).toUpperCase() + providerSlug.slice(1));
|
|
605
|
+
const actionPart = upstreamSlug
|
|
606
|
+
.toLowerCase()
|
|
607
|
+
.replace(new RegExp(`^${providerSlug}_`), "");
|
|
608
|
+
const muxoSlug = `app.${providerSlug}.${actionPart}`;
|
|
609
|
+
const rawTags = Array.isArray(item.tags) ? item.tags.map(String) : [providerSlug];
|
|
610
|
+
const inputParams = item.inputParameters ||
|
|
611
|
+
item.input_parameters || {
|
|
612
|
+
type: "object",
|
|
613
|
+
properties: {},
|
|
614
|
+
};
|
|
615
|
+
const noAuth = Boolean(item.noAuth ?? item.no_auth);
|
|
616
|
+
const tool = {
|
|
617
|
+
slug: muxoSlug,
|
|
618
|
+
upstreamSlug,
|
|
619
|
+
name: `${providerName}: ${String(item.name || actionPart)}`,
|
|
620
|
+
description: String(item.description || `Execute ${upstreamSlug} via Composio`),
|
|
621
|
+
lane: "composio_sdk",
|
|
622
|
+
providerSlug,
|
|
623
|
+
providerName,
|
|
624
|
+
categories: rawTags,
|
|
625
|
+
riskLevel: classifyComposioRisk(upstreamSlug, rawTags),
|
|
626
|
+
requiresAuth: !noAuth,
|
|
627
|
+
creditCost: 0,
|
|
628
|
+
billingMode: "customer_oauth",
|
|
629
|
+
inputSchema: inputParams,
|
|
630
|
+
};
|
|
631
|
+
this.liveToolsCache.set(muxoSlug, tool);
|
|
632
|
+
return tool;
|
|
633
|
+
}
|
|
634
|
+
async searchTools(query, limit = 15, composioApiKey, toolkit) {
|
|
211
635
|
const q = (query || "").trim().toLowerCase();
|
|
636
|
+
const tk = (toolkit || "").trim().toLowerCase();
|
|
212
637
|
const localMatches = this.getCuratedTools().filter((t) => {
|
|
638
|
+
if (tk && t.providerSlug.toLowerCase() !== tk)
|
|
639
|
+
return false;
|
|
213
640
|
if (!q)
|
|
214
641
|
return true;
|
|
215
642
|
return (t.slug.toLowerCase().includes(q) ||
|
|
@@ -217,82 +644,109 @@ export class ComposioSdkAdapter {
|
|
|
217
644
|
t.description.toLowerCase().includes(q) ||
|
|
218
645
|
t.providerSlug.toLowerCase().includes(q));
|
|
219
646
|
});
|
|
220
|
-
|
|
647
|
+
const apiKey = this.resolveApiKey(composioApiKey);
|
|
648
|
+
if ((!q && !tk) || !apiKey) {
|
|
221
649
|
return localMatches.slice(0, limit);
|
|
222
650
|
}
|
|
651
|
+
const merged = new Map();
|
|
652
|
+
for (const m of localMatches)
|
|
653
|
+
merged.set(m.slug, m);
|
|
654
|
+
// 1. Official @composio/core SDK tool discovery
|
|
655
|
+
const sdk = this.getComposioClient(composioApiKey);
|
|
656
|
+
if (sdk) {
|
|
657
|
+
try {
|
|
658
|
+
const rawTools = await Promise.race([
|
|
659
|
+
sdk.tools.getRawComposioTools({
|
|
660
|
+
search: q || undefined,
|
|
661
|
+
toolkits: tk ? [tk] : undefined,
|
|
662
|
+
limit: Math.min(limit, 20),
|
|
663
|
+
}),
|
|
664
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK search timeout")), 4500)),
|
|
665
|
+
]);
|
|
666
|
+
if (Array.isArray(rawTools)) {
|
|
667
|
+
for (const rawItem of rawTools) {
|
|
668
|
+
const tool = this.mapRawComposioItemToMuxoTool(rawItem);
|
|
669
|
+
if (tool && !merged.has(tool.slug)) {
|
|
670
|
+
merged.set(tool.slug, tool);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
if (merged.size > localMatches.length) {
|
|
674
|
+
return Array.from(merged.values()).slice(0, limit);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
catch {
|
|
679
|
+
// Fall through to direct v3 REST endpoint
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
// 2. Direct Composio v3 API fallback
|
|
223
683
|
try {
|
|
224
684
|
const url = new URL(`${this.config.composioBaseUrl}/tools`);
|
|
225
|
-
|
|
685
|
+
if (q)
|
|
686
|
+
url.searchParams.set("search", q);
|
|
687
|
+
if (tk)
|
|
688
|
+
url.searchParams.set("toolkit_slug", tk);
|
|
226
689
|
url.searchParams.set("limit", String(Math.min(limit, 15)));
|
|
227
690
|
const res = await fetch(url.toString(), {
|
|
228
|
-
headers: this.
|
|
691
|
+
headers: this.getHeaders(composioApiKey),
|
|
229
692
|
signal: AbortSignal.timeout(4500),
|
|
230
693
|
});
|
|
231
694
|
if (!res.ok)
|
|
232
|
-
return
|
|
695
|
+
return Array.from(merged.values()).slice(0, limit);
|
|
233
696
|
const data = (await res.json());
|
|
234
697
|
const items = Array.isArray(data.items) ? data.items : [];
|
|
235
|
-
const merged = new Map();
|
|
236
|
-
for (const m of localMatches)
|
|
237
|
-
merged.set(m.slug, m);
|
|
238
698
|
for (const item of items) {
|
|
239
|
-
const
|
|
240
|
-
if (!
|
|
241
|
-
|
|
242
|
-
const toolkitObj = (item.toolkit || {});
|
|
243
|
-
const providerSlug = String(toolkitObj.slug || upstreamSlug.split("_")[0] || "app").toLowerCase();
|
|
244
|
-
const providerName = String(toolkitObj.name || providerSlug.charAt(0).toUpperCase() + providerSlug.slice(1));
|
|
245
|
-
const actionPart = upstreamSlug
|
|
246
|
-
.toLowerCase()
|
|
247
|
-
.replace(new RegExp(`^${providerSlug}_`), "");
|
|
248
|
-
const muxoSlug = `app.${providerSlug}.${actionPart}`;
|
|
249
|
-
const tool = {
|
|
250
|
-
slug: muxoSlug,
|
|
251
|
-
upstreamSlug,
|
|
252
|
-
name: `${providerName}: ${String(item.name || actionPart)}`,
|
|
253
|
-
description: String(item.description || `Execute ${upstreamSlug} via Composio`),
|
|
254
|
-
lane: "composio_sdk",
|
|
255
|
-
providerSlug,
|
|
256
|
-
providerName,
|
|
257
|
-
categories: Array.isArray(item.tags) ? item.tags.map(String) : [providerSlug],
|
|
258
|
-
riskLevel: classifyComposioRisk(upstreamSlug, Array.isArray(item.tags) ? item.tags.map(String) : []),
|
|
259
|
-
requiresAuth: !Boolean(item.no_auth),
|
|
260
|
-
creditCost: 0,
|
|
261
|
-
billingMode: "customer_oauth",
|
|
262
|
-
inputSchema: item.input_parameters || {
|
|
263
|
-
type: "object",
|
|
264
|
-
properties: {},
|
|
265
|
-
},
|
|
266
|
-
};
|
|
267
|
-
this.liveToolsCache.set(muxoSlug, tool);
|
|
268
|
-
if (!merged.has(muxoSlug)) {
|
|
269
|
-
merged.set(muxoSlug, tool);
|
|
699
|
+
const tool = this.mapRawComposioItemToMuxoTool(item);
|
|
700
|
+
if (tool && !merged.has(tool.slug)) {
|
|
701
|
+
merged.set(tool.slug, tool);
|
|
270
702
|
}
|
|
271
703
|
}
|
|
272
704
|
return Array.from(merged.values()).slice(0, limit);
|
|
273
705
|
}
|
|
274
706
|
catch {
|
|
275
|
-
return
|
|
707
|
+
return Array.from(merged.values()).slice(0, limit);
|
|
276
708
|
}
|
|
277
709
|
}
|
|
278
|
-
async inspectTool(slug) {
|
|
710
|
+
async inspectTool(slug, composioApiKey) {
|
|
279
711
|
const normalizedSlug = slug === "app.gmail.list_messages" ? "app.gmail.fetch_emails" : slug;
|
|
280
712
|
if (this.liveToolsCache.has(normalizedSlug)) {
|
|
281
713
|
return this.liveToolsCache.get(normalizedSlug);
|
|
282
714
|
}
|
|
283
|
-
|
|
715
|
+
const apiKey = this.resolveApiKey(composioApiKey);
|
|
716
|
+
if (!normalizedSlug.startsWith("app.") || !apiKey)
|
|
284
717
|
return null;
|
|
285
718
|
const parts = normalizedSlug.split(".");
|
|
719
|
+
if (parts.length >= 3) {
|
|
720
|
+
const upstreamCandidate = parts.slice(1).join("_").toUpperCase();
|
|
721
|
+
const sdk = this.getComposioClient(composioApiKey);
|
|
722
|
+
if (sdk) {
|
|
723
|
+
try {
|
|
724
|
+
const rawTool = await Promise.race([
|
|
725
|
+
sdk.tools.getRawComposioToolBySlug(upstreamCandidate),
|
|
726
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK inspect timeout")), 4000)),
|
|
727
|
+
]);
|
|
728
|
+
if (rawTool) {
|
|
729
|
+
const mapped = this.mapRawComposioItemToMuxoTool(rawTool);
|
|
730
|
+
if (mapped)
|
|
731
|
+
return mapped;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
catch {
|
|
735
|
+
// Fall through to searchTools
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
286
739
|
const query = parts.slice(1).join(" ");
|
|
287
|
-
const matches = await this.searchTools(query, 10);
|
|
288
|
-
return
|
|
740
|
+
const matches = await this.searchTools(query, 10, composioApiKey);
|
|
741
|
+
return matchAppToolSlug(normalizedSlug, matches);
|
|
289
742
|
}
|
|
290
743
|
async startConnection(params) {
|
|
291
744
|
const providerSlug = params.providerSlug.toLowerCase().replace(/^app\./, "");
|
|
292
745
|
const providerName = providerSlug.charAt(0).toUpperCase() + providerSlug.slice(1);
|
|
293
746
|
const callbackUrl = params.callbackUrl ||
|
|
294
747
|
`${this.config.baseUrl}/oauth/callback?workspace_id=${encodeURIComponent(params.workspaceId)}&provider=${encodeURIComponent(providerSlug)}`;
|
|
295
|
-
|
|
748
|
+
const apiKey = this.resolveApiKey(params.composioApiKey);
|
|
749
|
+
if (!apiKey) {
|
|
296
750
|
const externalAccountId = `ca_ext_${providerSlug}_${params.workspaceId.slice(-8)}`;
|
|
297
751
|
return {
|
|
298
752
|
providerSlug,
|
|
@@ -304,11 +758,72 @@ export class ComposioSdkAdapter {
|
|
|
304
758
|
message: `Open the authorization URL to connect ${providerName} to your Muxo workspace.`,
|
|
305
759
|
};
|
|
306
760
|
}
|
|
307
|
-
// 1.
|
|
761
|
+
// 1. Official @composio/core SDK Tool Router & Connected Accounts initiation
|
|
762
|
+
const sdk = this.getComposioClient(params.composioApiKey);
|
|
763
|
+
if (sdk) {
|
|
764
|
+
try {
|
|
765
|
+
const authConfigs = await Promise.race([
|
|
766
|
+
sdk.authConfigs.list({ toolkitSlug: providerSlug }),
|
|
767
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK authConfigs timeout")), 5000)),
|
|
768
|
+
]);
|
|
769
|
+
const authConfigId = authConfigs?.items?.[0]?.id;
|
|
770
|
+
if (authConfigId) {
|
|
771
|
+
const connRequest = await Promise.race([
|
|
772
|
+
sdk.connectedAccounts.initiate(params.workspaceId, authConfigId, {
|
|
773
|
+
callbackUrl,
|
|
774
|
+
allowMultiple: true,
|
|
775
|
+
}),
|
|
776
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK initiate timeout")), 6000)),
|
|
777
|
+
]);
|
|
778
|
+
const externalAccountId = connRequest?.id || null;
|
|
779
|
+
const redirectUrl = connRequest?.redirectUrl ||
|
|
780
|
+
(externalAccountId
|
|
781
|
+
? `${callbackUrl}&connected_account_id=${encodeURIComponent(externalAccountId)}`
|
|
782
|
+
: null);
|
|
783
|
+
if (redirectUrl) {
|
|
784
|
+
return {
|
|
785
|
+
providerSlug,
|
|
786
|
+
providerName,
|
|
787
|
+
status: "pending",
|
|
788
|
+
redirectUrl,
|
|
789
|
+
callbackUrl,
|
|
790
|
+
externalAccountId,
|
|
791
|
+
message: `Complete ${providerName} OAuth authorization in your browser.`,
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
else {
|
|
796
|
+
const authReq = await Promise.race([
|
|
797
|
+
sdk.toolkits.authorize(params.workspaceId, providerSlug),
|
|
798
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK toolkits.authorize timeout")), 6000)),
|
|
799
|
+
]);
|
|
800
|
+
const externalAccountId = authReq?.id || null;
|
|
801
|
+
const redirectUrl = authReq?.redirectUrl ||
|
|
802
|
+
(externalAccountId
|
|
803
|
+
? `${callbackUrl}&connected_account_id=${encodeURIComponent(externalAccountId)}`
|
|
804
|
+
: null);
|
|
805
|
+
if (redirectUrl) {
|
|
806
|
+
return {
|
|
807
|
+
providerSlug,
|
|
808
|
+
providerName,
|
|
809
|
+
status: "pending",
|
|
810
|
+
redirectUrl,
|
|
811
|
+
callbackUrl,
|
|
812
|
+
externalAccountId,
|
|
813
|
+
message: `Complete ${providerName} OAuth authorization in your browser.`,
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
catch {
|
|
819
|
+
// Fall through to Tool Router REST session / v3 API fallback
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
// 2. Try creating a Composio Tool Router Session & link for the workspace user
|
|
308
823
|
try {
|
|
309
824
|
const sessionRes = await fetch(`${this.config.composioBaseUrl}/tool_router/session`, {
|
|
310
825
|
method: "POST",
|
|
311
|
-
headers: this.
|
|
826
|
+
headers: this.getHeaders(params.composioApiKey),
|
|
312
827
|
body: JSON.stringify({
|
|
313
828
|
user_id: params.workspaceId,
|
|
314
829
|
toolkits: { enable: [providerSlug] },
|
|
@@ -321,7 +836,7 @@ export class ComposioSdkAdapter {
|
|
|
321
836
|
if (sessionId) {
|
|
322
837
|
const linkRes = await fetch(`${this.config.composioBaseUrl}/tool_router/session/${sessionId}/link`, {
|
|
323
838
|
method: "POST",
|
|
324
|
-
headers: this.
|
|
839
|
+
headers: this.getHeaders(params.composioApiKey),
|
|
325
840
|
body: JSON.stringify({
|
|
326
841
|
toolkit: providerSlug,
|
|
327
842
|
callback_url: callbackUrl,
|
|
@@ -348,9 +863,9 @@ export class ComposioSdkAdapter {
|
|
|
348
863
|
catch {
|
|
349
864
|
// Fall through to auth_configs / connected_accounts flow
|
|
350
865
|
}
|
|
351
|
-
//
|
|
866
|
+
// 3. Fallback: look up or create an auth_config for the toolkit and initiate connected_accounts
|
|
352
867
|
const authCfgRes = await fetch(`${this.config.composioBaseUrl}/auth_configs?toolkit_slug=${encodeURIComponent(providerSlug)}&limit=5`, {
|
|
353
|
-
headers: this.
|
|
868
|
+
headers: this.getHeaders(params.composioApiKey),
|
|
354
869
|
signal: AbortSignal.timeout(6000),
|
|
355
870
|
});
|
|
356
871
|
if (authCfgRes.ok) {
|
|
@@ -359,7 +874,7 @@ export class ComposioSdkAdapter {
|
|
|
359
874
|
if (authConfigId) {
|
|
360
875
|
const connRes = await fetch(`${this.config.composioBaseUrl}/connected_accounts`, {
|
|
361
876
|
method: "POST",
|
|
362
|
-
headers: this.
|
|
877
|
+
headers: this.getHeaders(params.composioApiKey),
|
|
363
878
|
body: JSON.stringify({
|
|
364
879
|
auth_config: { id: authConfigId },
|
|
365
880
|
connection: {
|
|
@@ -371,7 +886,10 @@ export class ComposioSdkAdapter {
|
|
|
371
886
|
});
|
|
372
887
|
if (connRes.ok) {
|
|
373
888
|
const connData = (await connRes.json());
|
|
374
|
-
const
|
|
889
|
+
const fallbackRedirect = `${callbackUrl}&connected_account_id=${encodeURIComponent(connData.id || `ca_${providerSlug}`)}`;
|
|
890
|
+
const redirectUrl = connData.redirect_url ||
|
|
891
|
+
connData.connectionData?.val?.redirectUrl ||
|
|
892
|
+
fallbackRedirect;
|
|
375
893
|
return {
|
|
376
894
|
providerSlug,
|
|
377
895
|
providerName,
|
|
@@ -379,21 +897,57 @@ export class ComposioSdkAdapter {
|
|
|
379
897
|
redirectUrl,
|
|
380
898
|
callbackUrl,
|
|
381
899
|
externalAccountId: connData.id || null,
|
|
382
|
-
message:
|
|
383
|
-
? `Complete ${providerName} OAuth authorization in your browser.`
|
|
384
|
-
: `Created ${providerName} connection request (${connData.id}).`,
|
|
900
|
+
message: `Complete ${providerName} OAuth authorization in your browser.`,
|
|
385
901
|
};
|
|
386
902
|
}
|
|
387
903
|
}
|
|
388
904
|
}
|
|
389
|
-
|
|
905
|
+
const fallbackExtId = `ca_ext_${providerSlug}_${params.workspaceId.slice(-8)}`;
|
|
906
|
+
return {
|
|
907
|
+
providerSlug,
|
|
908
|
+
providerName,
|
|
909
|
+
status: "pending",
|
|
910
|
+
redirectUrl: `${callbackUrl}&connected_account_id=${encodeURIComponent(fallbackExtId)}`,
|
|
911
|
+
callbackUrl,
|
|
912
|
+
externalAccountId: fallbackExtId,
|
|
913
|
+
message: `Open the authorization URL to connect ${providerName} to your Muxo workspace.`,
|
|
914
|
+
};
|
|
390
915
|
}
|
|
391
|
-
async getConnectedAccountStatus(externalAccountId) {
|
|
392
|
-
|
|
916
|
+
async getConnectedAccountStatus(externalAccountId, composioApiKey) {
|
|
917
|
+
const apiKey = this.resolveApiKey(composioApiKey);
|
|
918
|
+
if (!apiKey || !externalAccountId)
|
|
393
919
|
return null;
|
|
920
|
+
// 1. Official @composio/core SDK connectedAccounts.get
|
|
921
|
+
const sdk = this.getComposioClient(composioApiKey);
|
|
922
|
+
if (sdk) {
|
|
923
|
+
try {
|
|
924
|
+
const item = (await Promise.race([
|
|
925
|
+
sdk.connectedAccounts.get(externalAccountId),
|
|
926
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK get account timeout")), 4500)),
|
|
927
|
+
]));
|
|
928
|
+
if (item && (item.id || item.status)) {
|
|
929
|
+
const toolkit = (item.toolkit || {});
|
|
930
|
+
const slug = String(toolkit.slug || item.toolkit_slug || "unknown").toLowerCase();
|
|
931
|
+
const rawStatus = String(item.status || "INITIALIZING").toUpperCase();
|
|
932
|
+
const status = rawStatus === "ACTIVE"
|
|
933
|
+
? "active"
|
|
934
|
+
: rawStatus === "EXPIRED" || rawStatus === "FAILED"
|
|
935
|
+
? "expired"
|
|
936
|
+
: "pending";
|
|
937
|
+
return {
|
|
938
|
+
id: String(item.id || externalAccountId),
|
|
939
|
+
providerSlug: slug,
|
|
940
|
+
status,
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
catch {
|
|
945
|
+
// Fall through to REST
|
|
946
|
+
}
|
|
947
|
+
}
|
|
394
948
|
try {
|
|
395
949
|
const res = await fetch(`${this.config.composioBaseUrl}/connected_accounts/${encodeURIComponent(externalAccountId)}`, {
|
|
396
|
-
headers: this.
|
|
950
|
+
headers: this.getHeaders(composioApiKey),
|
|
397
951
|
signal: AbortSignal.timeout(5000),
|
|
398
952
|
});
|
|
399
953
|
if (!res.ok)
|
|
@@ -417,21 +971,11 @@ export class ComposioSdkAdapter {
|
|
|
417
971
|
return null;
|
|
418
972
|
}
|
|
419
973
|
}
|
|
420
|
-
async listLiveConnections(workspaceId) {
|
|
421
|
-
|
|
974
|
+
async listLiveConnections(workspaceId, composioApiKey) {
|
|
975
|
+
const apiKey = this.resolveApiKey(composioApiKey);
|
|
976
|
+
if (!apiKey)
|
|
422
977
|
return [];
|
|
423
|
-
|
|
424
|
-
const url = new URL(`${this.config.composioBaseUrl}/connected_accounts`);
|
|
425
|
-
url.searchParams.set("user_ids", workspaceId);
|
|
426
|
-
url.searchParams.set("limit", "50");
|
|
427
|
-
const res = await fetch(url.toString(), {
|
|
428
|
-
headers: this.headers,
|
|
429
|
-
signal: AbortSignal.timeout(5000),
|
|
430
|
-
});
|
|
431
|
-
if (!res.ok)
|
|
432
|
-
return [];
|
|
433
|
-
const data = (await res.json());
|
|
434
|
-
const items = Array.isArray(data.items) ? data.items : [];
|
|
978
|
+
const mapConnectionItems = (items) => {
|
|
435
979
|
const rank = {
|
|
436
980
|
active: 3,
|
|
437
981
|
pending: 2,
|
|
@@ -459,6 +1003,39 @@ export class ComposioSdkAdapter {
|
|
|
459
1003
|
}
|
|
460
1004
|
}
|
|
461
1005
|
return Array.from(bySlug.values());
|
|
1006
|
+
};
|
|
1007
|
+
// 1. Official @composio/core SDK connectedAccounts.list
|
|
1008
|
+
const sdk = this.getComposioClient(composioApiKey);
|
|
1009
|
+
if (sdk) {
|
|
1010
|
+
try {
|
|
1011
|
+
const listed = (await Promise.race([
|
|
1012
|
+
sdk.connectedAccounts.list({
|
|
1013
|
+
userIds: [workspaceId],
|
|
1014
|
+
limit: 50,
|
|
1015
|
+
}),
|
|
1016
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK list accounts timeout")), 4500)),
|
|
1017
|
+
]));
|
|
1018
|
+
if (Array.isArray(listed?.items)) {
|
|
1019
|
+
return mapConnectionItems(listed.items);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
catch {
|
|
1023
|
+
// Fall through to REST
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
try {
|
|
1027
|
+
const url = new URL(`${this.config.composioBaseUrl}/connected_accounts`);
|
|
1028
|
+
url.searchParams.set("user_ids", workspaceId);
|
|
1029
|
+
url.searchParams.set("limit", "50");
|
|
1030
|
+
const res = await fetch(url.toString(), {
|
|
1031
|
+
headers: this.getHeaders(composioApiKey),
|
|
1032
|
+
signal: AbortSignal.timeout(5000),
|
|
1033
|
+
});
|
|
1034
|
+
if (!res.ok)
|
|
1035
|
+
return [];
|
|
1036
|
+
const data = (await res.json());
|
|
1037
|
+
const items = Array.isArray(data.items) ? data.items : [];
|
|
1038
|
+
return mapConnectionItems(items);
|
|
462
1039
|
}
|
|
463
1040
|
catch {
|
|
464
1041
|
return [];
|
|
@@ -536,10 +1113,41 @@ export class ComposioSdkAdapter {
|
|
|
536
1113
|
},
|
|
537
1114
|
};
|
|
538
1115
|
};
|
|
539
|
-
const effectiveApiKey =
|
|
1116
|
+
const effectiveApiKey = this.resolveApiKey(params.composioApiKey);
|
|
540
1117
|
if (!effectiveApiKey) {
|
|
541
1118
|
return buildConnectedAccountFallback();
|
|
542
1119
|
}
|
|
1120
|
+
// 1. Official @composio/core SDK tool execution
|
|
1121
|
+
const sdk = this.getComposioClient(params.composioApiKey);
|
|
1122
|
+
if (sdk) {
|
|
1123
|
+
try {
|
|
1124
|
+
const sdkExec = await Promise.race([
|
|
1125
|
+
sdk.tools.execute(params.tool.upstreamSlug, {
|
|
1126
|
+
userId: params.workspaceId,
|
|
1127
|
+
arguments: params.input,
|
|
1128
|
+
connectedAccountId: params.connectedAccountId && params.connectedAccountId.startsWith("ca_")
|
|
1129
|
+
? params.connectedAccountId
|
|
1130
|
+
: undefined,
|
|
1131
|
+
dangerouslySkipVersionCheck: true,
|
|
1132
|
+
}),
|
|
1133
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK execute timeout")), 20000)),
|
|
1134
|
+
]);
|
|
1135
|
+
if (sdkExec && !sdkExec.error && sdkExec.successful !== false) {
|
|
1136
|
+
return {
|
|
1137
|
+
provider: "composio_sdk",
|
|
1138
|
+
sdk: "@composio/core",
|
|
1139
|
+
tool: params.tool.slug,
|
|
1140
|
+
upstreamTool: params.tool.upstreamSlug,
|
|
1141
|
+
connectedAccountId: params.connectedAccountId,
|
|
1142
|
+
logId: sdkExec.logId,
|
|
1143
|
+
data: sdkExec.data ?? sdkExec,
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
catch {
|
|
1148
|
+
// Fall through to direct v3 REST execute
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
543
1151
|
const body = {
|
|
544
1152
|
user_id: params.workspaceId,
|
|
545
1153
|
arguments: params.input,
|
|
@@ -550,10 +1158,7 @@ export class ComposioSdkAdapter {
|
|
|
550
1158
|
try {
|
|
551
1159
|
const res = await fetch(`${this.config.composioBaseUrl}/tools/execute/${encodeURIComponent(params.tool.upstreamSlug)}`, {
|
|
552
1160
|
method: "POST",
|
|
553
|
-
headers:
|
|
554
|
-
...this.headers,
|
|
555
|
-
"x-api-key": effectiveApiKey,
|
|
556
|
-
},
|
|
1161
|
+
headers: this.getHeaders(params.composioApiKey),
|
|
557
1162
|
body: JSON.stringify(body),
|
|
558
1163
|
signal: AbortSignal.timeout(25000),
|
|
559
1164
|
});
|