@muxoai/sdk 1.3.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/.agents/plugins/marketplace.json +2 -2
  2. package/.claude-plugin/marketplace.json +19 -4
  3. package/.codex/config.toml +9 -2
  4. package/ARCHITECTURE.md +16 -6
  5. package/README.md +69 -24
  6. package/dist/clerk.d.ts +35 -0
  7. package/dist/clerk.js +201 -0
  8. package/dist/cli.js +1589 -103
  9. package/dist/composio-catalog.d.ts +234 -0
  10. package/dist/composio-catalog.js +1894 -0
  11. package/dist/config.d.ts +16 -0
  12. package/dist/config.js +27 -1
  13. package/dist/durable-workflow.d.ts +45 -0
  14. package/dist/durable-workflow.js +90 -0
  15. package/dist/engine.d.ts +315 -6
  16. package/dist/engine.js +1778 -94
  17. package/dist/mcp-schema.d.ts +286 -0
  18. package/dist/mcp-schema.js +2559 -0
  19. package/dist/mcp.d.ts +2 -25
  20. package/dist/mcp.js +949 -507
  21. package/dist/memory-graph.d.ts +21 -0
  22. package/dist/memory-graph.js +173 -0
  23. package/dist/nl.js +214 -35
  24. package/dist/oauth.d.ts +32 -0
  25. package/dist/oauth.js +134 -0
  26. package/dist/policy.d.ts +190 -0
  27. package/dist/policy.js +1597 -0
  28. package/dist/providers/composio-instant.d.ts +4 -0
  29. package/dist/providers/composio-instant.js +890 -535
  30. package/dist/providers/composio-sdk.d.ts +126 -5
  31. package/dist/providers/composio-sdk.js +902 -79
  32. package/dist/providers/microsoft-governance.d.ts +89 -3
  33. package/dist/providers/microsoft-governance.js +673 -26
  34. package/dist/providers/stripe-billing.d.ts +42 -0
  35. package/dist/providers/stripe-billing.js +244 -5
  36. package/dist/providers/stripe-projects.d.ts +459 -4
  37. package/dist/providers/stripe-projects.js +2674 -122
  38. package/dist/providers/supermemory.d.ts +102 -6
  39. package/dist/providers/supermemory.js +609 -55
  40. package/dist/provisioning-managed.d.ts +50 -0
  41. package/dist/provisioning-managed.js +155 -0
  42. package/dist/sdk.d.ts +705 -7
  43. package/dist/sdk.js +488 -10
  44. package/dist/server.js +2646 -348
  45. package/dist/store.d.ts +138 -6
  46. package/dist/store.js +932 -58
  47. package/dist/types.d.ts +312 -4
  48. package/dist/ui/console-html.d.ts +62 -0
  49. package/dist/ui/console-html.js +3742 -2228
  50. package/dist/ui/design-tokens.d.ts +177 -0
  51. package/dist/ui/design-tokens.js +210 -0
  52. package/dist/version.d.ts +1 -0
  53. package/dist/version.js +1 -0
  54. package/package.json +5 -4
  55. package/plugins/muxo/.app.json +1 -1
  56. package/plugins/muxo/.claude-plugin/plugin.json +3 -3
  57. package/plugins/muxo/.codex-plugin/plugin.json +19 -10
  58. package/plugins/muxo/.cursor-plugin/plugin.json +30 -0
  59. package/plugins/muxo/.mcp.json +1 -33
  60. package/plugins/muxo/assets/logo.png +0 -0
  61. package/plugins/muxo/hooks/session-start.sh +16 -4
  62. package/plugins/muxo/hooks/user-prompt-submit.sh +14 -1
  63. package/plugins/muxo/mcp.json +0 -10
  64. package/plugins/muxo/plugin.json +65 -8
  65. package/plugins/muxo/skills/muxo/SKILL.md +142 -14
  66. package/plugins/muxo/skills/muxo/agents/openai.yaml +12 -0
  67. package/plugins/muxo/skills/muxo/references/cli.md +9 -3
  68. package/plugins/muxo/skills/muxo/references/mcp.md +22 -10
  69. package/plugins/muxo/skills/muxo-activity-summary/SKILL.md +81 -0
  70. package/plugins/muxo/skills/setup/SKILL.md +17 -3
  71. package/plugins/muxo/skills/setup/agents/openai.yaml +12 -0
  72. package/src/catalogues/stripe-projects-catalog.json +140 -0
@@ -1,4 +1,28 @@
1
+ import { Composio } from "@composio/core";
2
+ import { matchAppToolSlug } from "../mcp-schema.js";
3
+ import { composioAuthConfigScopes, COMPOSIO_V3_WEBHOOK_EVENTS, ensureOAuthCallbackParams, ENTERPRISE_TOOLKITS_CATALOG, filterComposioToolkits, getComposioTriggerType, getSampleActionSchema, isStripeProjectsExclusiveProvider, listComposioTriggerTypes, parseComposioWebhookEnvelope, verifyComposioWebhookSignature, wrapMuxoConnectUrl, } 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,46 @@ 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
+ if (isStripeProjectsExclusiveProvider(tk.slug))
408
+ continue;
409
+ for (const act of tk.sampleActions) {
410
+ if (act.muxoSlug.startsWith("resource."))
411
+ continue;
412
+ if (!this.liveToolsCache.has(act.muxoSlug)) {
413
+ this.liveToolsCache.set(act.muxoSlug, {
414
+ slug: act.muxoSlug,
415
+ upstreamSlug: act.upstreamSlug,
416
+ name: act.name,
417
+ description: `${tk.name}: ${act.name} (Muxo Connected Apps toolkit v${tk.pinnedVersion})`,
418
+ lane: "composio_sdk",
419
+ providerSlug: tk.slug,
420
+ providerName: tk.name,
421
+ categories: tk.categories,
422
+ riskLevel: act.riskLevel,
423
+ requiresAuth: true,
424
+ creditCost: act.riskLevel === "read" ? 1 : 2,
425
+ billingMode: "customer_oauth",
426
+ inputSchema: getSampleActionSchema(act.muxoSlug, act.upstreamSlug),
427
+ });
428
+ }
429
+ }
430
+ }
431
+ }
432
+ resolveApiKey(composioApiKey) {
433
+ return (composioApiKey || this.config.composioApiKey || "").trim();
200
434
  }
201
435
  get headers() {
202
436
  return {
@@ -204,12 +438,405 @@ export class ComposioSdkAdapter {
204
438
  "Content-Type": "application/json",
205
439
  };
206
440
  }
441
+ getHeaders(composioApiKey) {
442
+ return {
443
+ "x-api-key": this.resolveApiKey(composioApiKey),
444
+ "Content-Type": "application/json",
445
+ };
446
+ }
447
+ getComposioClient(composioApiKey) {
448
+ const apiKey = this.resolveApiKey(composioApiKey);
449
+ if (!apiKey)
450
+ return null;
451
+ const rawBaseUrl = (this.config.composioBaseUrl || "https://backend.composio.dev/api/v3").replace(/\/+$/, "");
452
+ const sdkBaseUrl = rawBaseUrl.replace(/\/api\/v3$/i, "") || "https://backend.composio.dev";
453
+ const cacheKey = `${apiKey}::${sdkBaseUrl}`;
454
+ const existing = this.sdkClients.get(cacheKey);
455
+ if (existing)
456
+ return existing;
457
+ try {
458
+ const client = new Composio({
459
+ apiKey,
460
+ baseURL: sdkBaseUrl,
461
+ allowTracking: false,
462
+ disableVersionCheck: true,
463
+ });
464
+ this.sdkClients.set(cacheKey, client);
465
+ return client;
466
+ }
467
+ catch {
468
+ return null;
469
+ }
470
+ }
471
+ getSdkInfo(composioApiKey) {
472
+ const apiKey = this.resolveApiKey(composioApiKey);
473
+ return {
474
+ sdkPackage: "@composio/core",
475
+ sdkVersion: "0.6.11",
476
+ configured: Boolean(apiKey),
477
+ usingCustomerByok: Boolean(composioApiKey && composioApiKey.trim()),
478
+ baseUrl: this.config.composioBaseUrl,
479
+ curatedToolCount: CURATED_APP_TOOLS.length,
480
+ cachedToolCount: this.liveToolsCache.size,
481
+ capabilities: [
482
+ "tools.getRawComposioTools",
483
+ "tools.getRawComposioToolBySlug",
484
+ "tools.execute",
485
+ "toolkits.authorize",
486
+ "connectedAccounts.initiate",
487
+ "connectedAccounts.get",
488
+ "connectedAccounts.list",
489
+ "authConfigs.list",
490
+ "triggers.getType",
491
+ "triggers.create",
492
+ "triggers.subscribe",
493
+ "triggers.verifyWebhook",
494
+ ],
495
+ };
496
+ }
497
+ getWebhookConfigInfo() {
498
+ return {
499
+ webhookUrl: "https://api.muxo.ai/webhooks",
500
+ legacyWebhookUrl: "https://api.muxo.ai/api/v1/triggers/webhook",
501
+ version: "V3",
502
+ events: [...COMPOSIO_V3_WEBHOOK_EVENTS],
503
+ secretConfigured: Boolean(this.config.composioWebhookSecret),
504
+ subscriptionId: this.config.composioWebhookSubscriptionId || null,
505
+ projectId: this.config.composioProjectId || null,
506
+ orgId: this.config.composioOrgId || null,
507
+ userId: this.config.composioUserId || null,
508
+ };
509
+ }
207
510
  getCuratedTools() {
208
511
  return Array.from(this.liveToolsCache.values());
209
512
  }
210
- async searchTools(query, limit = 15) {
513
+ static classifyActionRisk(upstreamSlug, description = "") {
514
+ return classifyComposioRisk(upstreamSlug, [description]);
515
+ }
516
+ async listToolkits(params) {
517
+ // Hydrate dynamic sample actions into Muxo's liveToolsCache so agents can plan, inspect, and execute them immediately
518
+ this.hydrateEnterpriseSampleActions();
519
+ const list = filterComposioToolkits(params);
520
+ return {
521
+ toolkits: list,
522
+ total: list.length,
523
+ source: Boolean(this.resolveApiKey(params?.composioApiKey))
524
+ ? "composio_v3_sdk"
525
+ : "muxo_enterprise_catalog",
526
+ };
527
+ }
528
+ async createCustomAuthConfig(params) {
529
+ const tkSlug = params.toolkitSlug.toLowerCase().trim();
530
+ const scopeMode = params.scopeMode || "read_only";
531
+ const scopes = composioAuthConfigScopes(tkSlug, scopeMode, params.customScopes);
532
+ const sdk = this.getComposioClient(params.composioApiKey);
533
+ if (sdk) {
534
+ try {
535
+ const created = await sdk.authConfigs.create(tkSlug, {
536
+ name: params.name || `muxo_${params.workspaceId}_${tkSlug}_${scopeMode}`,
537
+ type: params.credentials?.client_id
538
+ ? "use_custom_auth"
539
+ : "use_composio_managed_auth",
540
+ authScheme: params.authScheme,
541
+ credentials: {
542
+ ...(params.credentials || {}),
543
+ scopes: scopes.join(","),
544
+ },
545
+ });
546
+ const id = String(created?.id || `ac_${tkSlug}_${Date.now()}`);
547
+ this.customAuthConfigs.set(`${params.workspaceId}:${tkSlug}`, {
548
+ authConfigId: id,
549
+ toolkitSlug: tkSlug,
550
+ authScheme: params.authScheme,
551
+ scopeMode,
552
+ scopes,
553
+ });
554
+ return {
555
+ authConfigId: id,
556
+ toolkitSlug: tkSlug,
557
+ authScheme: params.authScheme,
558
+ scopeMode,
559
+ scopes,
560
+ source: "composio_v3_sdk",
561
+ };
562
+ }
563
+ catch {
564
+ // Fall through to deterministic local auth config registration
565
+ }
566
+ }
567
+ const fallbackId = `ac_muxo_${tkSlug}_${scopeMode}_${Buffer.from(params.workspaceId).toString("hex").slice(0, 8)}`;
568
+ this.customAuthConfigs.set(`${params.workspaceId}:${tkSlug}`, {
569
+ authConfigId: fallbackId,
570
+ toolkitSlug: tkSlug,
571
+ authScheme: params.authScheme,
572
+ scopeMode,
573
+ scopes,
574
+ });
575
+ return {
576
+ authConfigId: fallbackId,
577
+ toolkitSlug: tkSlug,
578
+ authScheme: params.authScheme,
579
+ scopeMode,
580
+ scopes,
581
+ source: "muxo_managed",
582
+ };
583
+ }
584
+ listTriggerTypes(providerSlug) {
585
+ return listComposioTriggerTypes(providerSlug);
586
+ }
587
+ /**
588
+ * Check what configuration is required for a trigger (`composio.triggers.getType(slug)`).
589
+ */
590
+ async getTriggerType(triggerSlug, composioApiKey) {
591
+ const normalizedSlug = String(triggerSlug || "GITHUB_COMMIT_EVENT")
592
+ .toUpperCase()
593
+ .trim();
594
+ const fallback = getComposioTriggerType(normalizedSlug);
595
+ const sdk = this.getComposioClient(composioApiKey);
596
+ if (sdk) {
597
+ try {
598
+ const triggerType = await Promise.race([
599
+ sdk.triggers.getType(normalizedSlug),
600
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK triggers.getType timeout")), 4500)),
601
+ ]);
602
+ if (triggerType) {
603
+ const toolkitSlug = String(triggerType.toolkit?.slug || fallback.providerSlug).toLowerCase();
604
+ return {
605
+ slug: String(triggerType.slug || normalizedSlug),
606
+ providerSlug: toolkitSlug,
607
+ name: String(triggerType.name || fallback.name),
608
+ description: String(triggerType.description || fallback.description),
609
+ config: triggerType.config && typeof triggerType.config === "object"
610
+ ? triggerType.config
611
+ : fallback.config,
612
+ source: "composio_v3_sdk",
613
+ };
614
+ }
615
+ }
616
+ catch {
617
+ // Fall back to Muxo trigger catalog when offline or unconfigured
618
+ }
619
+ }
620
+ return {
621
+ ...fallback,
622
+ source: "muxo_trigger_catalog",
623
+ };
624
+ }
625
+ /**
626
+ * Create a trigger with required config (`composio.triggers.create(userId, slug, { triggerConfig })`)
627
+ * and optionally subscribe to real-time trigger events (`composio.triggers.subscribe`).
628
+ */
629
+ async enableTrigger(params) {
630
+ const normalizedTriggerSlug = params.triggerSlug.toUpperCase().trim();
631
+ const triggerType = await this.getTriggerType(normalizedTriggerSlug, params.composioApiKey);
632
+ const providerSlug = (params.providerSlug ||
633
+ triggerType.providerSlug ||
634
+ "github")
635
+ .toLowerCase()
636
+ .trim();
637
+ const userId = params.userId || this.config.composioUserId || params.workspaceId;
638
+ const triggerConfig = params.config || {};
639
+ let upstreamTriggerId;
640
+ let source = "muxo_managed";
641
+ const sdk = this.getComposioClient(params.composioApiKey);
642
+ if (sdk) {
643
+ try {
644
+ const created = await Promise.race([
645
+ sdk.triggers.create(userId, normalizedTriggerSlug, {
646
+ ...(params.connectedAccountId
647
+ ? { connectedAccountId: params.connectedAccountId }
648
+ : {}),
649
+ triggerConfig,
650
+ }),
651
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK triggers.create timeout")), 6000)),
652
+ ]);
653
+ if (created?.triggerId) {
654
+ upstreamTriggerId = String(created.triggerId);
655
+ source = "composio_v3_sdk";
656
+ if (params.subscribe) {
657
+ await this.subscribeToTriggerEvents({
658
+ workspaceId: params.workspaceId,
659
+ triggerId: upstreamTriggerId,
660
+ composioApiKey: params.composioApiKey,
661
+ }).catch(() => { });
662
+ }
663
+ }
664
+ }
665
+ catch {
666
+ // Fall through to deterministic local subscription record
667
+ }
668
+ }
669
+ const id = upstreamTriggerId ||
670
+ `trg_${providerSlug}_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
671
+ const sub = {
672
+ id,
673
+ upstreamTriggerId,
674
+ workspaceId: params.workspaceId,
675
+ userId,
676
+ triggerSlug: normalizedTriggerSlug,
677
+ providerSlug,
678
+ connectedAccountId: params.connectedAccountId || `ca_${providerSlug}_${params.workspaceId.slice(0, 8)}`,
679
+ webhookUrl: params.webhookUrl ||
680
+ `https://api.muxo.ai/webhooks`,
681
+ config: triggerConfig,
682
+ status: "active",
683
+ source,
684
+ createdAt: new Date().toISOString(),
685
+ };
686
+ this.activeTriggers.set(id, sub);
687
+ return sub;
688
+ }
689
+ /**
690
+ * Subscribe to real-time trigger events via `@composio/core` (`composio.triggers.subscribe`).
691
+ */
692
+ async subscribeToTriggerEvents(params) {
693
+ const sdk = this.getComposioClient(params.composioApiKey);
694
+ if (!sdk) {
695
+ return { subscribed: false, triggerId: params.triggerId };
696
+ }
697
+ await sdk.triggers.subscribe((data) => {
698
+ const recordPayload = (data?.payload || data || {});
699
+ const triggerSlug = String(data?.triggerSlug || "GITHUB_COMMIT_EVENT").toUpperCase();
700
+ const providerSlug = String(data?.toolkitSlug || triggerSlug.split("_")[0] || "github").toLowerCase();
701
+ this.recordTriggerEvent({
702
+ id: `trgevt_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
703
+ workspaceId: params.workspaceId,
704
+ eventType: "composio.trigger.message",
705
+ webhookVersion: "V3",
706
+ triggerId: data?.id || params.triggerId,
707
+ triggerSlug,
708
+ providerSlug,
709
+ connectedAccountId: data?.metadata?.connectedAccount?.id,
710
+ userId: data?.userId || data?.metadata?.connectedAccount?.userId,
711
+ receivedAt: new Date().toISOString(),
712
+ promptInjectionScan: {
713
+ safe: true,
714
+ riskScore: 0,
715
+ findings: [],
716
+ },
717
+ disposition: "dispatched",
718
+ payloadPreview: recordPayload,
719
+ });
720
+ params.onEvent?.(data);
721
+ }, {
722
+ ...(params.triggerId ? { triggerId: params.triggerId } : {}),
723
+ ...(params.toolkits ? { toolkits: params.toolkits } : {}),
724
+ });
725
+ return { subscribed: true, triggerId: params.triggerId };
726
+ }
727
+ /**
728
+ * Verify an incoming Composio webhook payload and HMAC-SHA256 signature.
729
+ */
730
+ async verifyWebhook(params) {
731
+ const secret = (params.secret ?? this.config.composioWebhookSecret ?? "").trim();
732
+ const sigCheck = await verifyComposioWebhookSignature({
733
+ rawPayload: params.rawPayload,
734
+ signatureHeader: params.signatureHeader,
735
+ webhookIdHeader: params.webhookIdHeader,
736
+ webhookTimestampHeader: params.webhookTimestampHeader,
737
+ secret,
738
+ toleranceSeconds: params.toleranceSeconds,
739
+ });
740
+ let parsedJson = {};
741
+ try {
742
+ parsedJson = JSON.parse(params.rawPayload || "{}");
743
+ }
744
+ catch {
745
+ parsedJson = {};
746
+ }
747
+ const envelope = parseComposioWebhookEnvelope(parsedJson, {
748
+ webhookId: params.webhookIdHeader,
749
+ webhookTimestamp: params.webhookTimestampHeader,
750
+ });
751
+ return {
752
+ verified: sigCheck.verified,
753
+ reason: sigCheck.reason,
754
+ envelope,
755
+ };
756
+ }
757
+ disableTriggersMatching(params) {
758
+ let count = 0;
759
+ for (const sub of this.activeTriggers.values()) {
760
+ if (params.workspaceId && sub.workspaceId !== params.workspaceId)
761
+ continue;
762
+ const matchesTriggerId = Boolean(params.triggerId) &&
763
+ (sub.id === params.triggerId || sub.upstreamTriggerId === params.triggerId);
764
+ const matchesConnectedAccount = Boolean(params.connectedAccountId) &&
765
+ sub.connectedAccountId === params.connectedAccountId;
766
+ const matchesSlug = Boolean(params.triggerSlug) &&
767
+ sub.triggerSlug.toUpperCase() === params.triggerSlug.toUpperCase();
768
+ if (matchesTriggerId || matchesConnectedAccount || matchesSlug) {
769
+ sub.status = "disabled";
770
+ count += 1;
771
+ }
772
+ }
773
+ return count;
774
+ }
775
+ listActiveTriggers(workspaceId) {
776
+ return Array.from(this.activeTriggers.values()).filter((t) => t.workspaceId === workspaceId);
777
+ }
778
+ recordTriggerEvent(record) {
779
+ this.triggerEvents.push(record);
780
+ if (this.triggerEvents.length > 500) {
781
+ this.triggerEvents.shift();
782
+ }
783
+ }
784
+ listTriggerEvents(workspaceId, limit = 50) {
785
+ return this.triggerEvents
786
+ .filter((e) => e.workspaceId === workspaceId)
787
+ .slice(-Math.max(1, limit))
788
+ .reverse();
789
+ }
790
+ mapRawComposioItemToMuxoTool(item) {
791
+ const upstreamSlug = String(item.slug || item.name || "");
792
+ if (!upstreamSlug)
793
+ return null;
794
+ const toolkitObj = (item.toolkit || {});
795
+ const providerSlug = String(toolkitObj.slug || upstreamSlug.split("_")[0] || "app").toLowerCase();
796
+ // Never surface Stripe Projects cloud infrastructure providers as Composio OAuth tools
797
+ if (isStripeProjectsExclusiveProvider(providerSlug)) {
798
+ return null;
799
+ }
800
+ const providerName = String(toolkitObj.name || providerSlug.charAt(0).toUpperCase() + providerSlug.slice(1));
801
+ const actionPart = upstreamSlug
802
+ .toLowerCase()
803
+ .replace(new RegExp(`^${providerSlug}_`), "");
804
+ const muxoSlug = `app.${providerSlug}.${actionPart}`;
805
+ const rawTags = Array.isArray(item.tags) ? item.tags.map(String) : [providerSlug];
806
+ const inputParams = item.inputParameters ||
807
+ item.input_parameters || {
808
+ type: "object",
809
+ properties: {},
810
+ };
811
+ const noAuth = Boolean(item.noAuth ?? item.no_auth);
812
+ const tool = {
813
+ slug: muxoSlug,
814
+ upstreamSlug,
815
+ name: `${providerName}: ${String(item.name || actionPart)}`,
816
+ description: String(item.description || `Execute ${upstreamSlug} via Muxo Connected Apps`),
817
+ lane: "composio_sdk",
818
+ providerSlug,
819
+ providerName,
820
+ categories: rawTags,
821
+ riskLevel: classifyComposioRisk(upstreamSlug, rawTags),
822
+ requiresAuth: !noAuth,
823
+ creditCost: 0,
824
+ billingMode: "customer_oauth",
825
+ inputSchema: inputParams,
826
+ };
827
+ this.liveToolsCache.set(muxoSlug, tool);
828
+ return tool;
829
+ }
830
+ async searchTools(query, limit = 15, composioApiKey, toolkit) {
211
831
  const q = (query || "").trim().toLowerCase();
832
+ const tk = (toolkit || "").trim().toLowerCase();
833
+ // Stripe Projects infrastructure providers belong to stripe_projects lane, not Composio OAuth
834
+ if (tk && isStripeProjectsExclusiveProvider(tk)) {
835
+ return [];
836
+ }
212
837
  const localMatches = this.getCuratedTools().filter((t) => {
838
+ if (tk && t.providerSlug.toLowerCase() !== tk)
839
+ return false;
213
840
  if (!q)
214
841
  return true;
215
842
  return (t.slug.toLowerCase().includes(q) ||
@@ -217,82 +844,119 @@ export class ComposioSdkAdapter {
217
844
  t.description.toLowerCase().includes(q) ||
218
845
  t.providerSlug.toLowerCase().includes(q));
219
846
  });
220
- if (!q || !this.config.composioApiKey) {
847
+ const apiKey = this.resolveApiKey(composioApiKey);
848
+ if ((!q && !tk) || !apiKey) {
221
849
  return localMatches.slice(0, limit);
222
850
  }
851
+ const merged = new Map();
852
+ for (const m of localMatches)
853
+ merged.set(m.slug, m);
854
+ // 1. Official @composio/core SDK tool discovery
855
+ const sdk = this.getComposioClient(composioApiKey);
856
+ if (sdk) {
857
+ try {
858
+ const rawTools = await Promise.race([
859
+ sdk.tools.getRawComposioTools({
860
+ search: q || undefined,
861
+ toolkits: tk ? [tk] : undefined,
862
+ limit: Math.min(limit, 20),
863
+ }),
864
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK search timeout")), 4500)),
865
+ ]);
866
+ if (Array.isArray(rawTools)) {
867
+ for (const rawItem of rawTools) {
868
+ const tool = this.mapRawComposioItemToMuxoTool(rawItem);
869
+ if (tool && !merged.has(tool.slug)) {
870
+ merged.set(tool.slug, tool);
871
+ }
872
+ }
873
+ if (merged.size > localMatches.length) {
874
+ return Array.from(merged.values()).slice(0, limit);
875
+ }
876
+ }
877
+ }
878
+ catch {
879
+ // Fall through to direct v3 REST endpoint
880
+ }
881
+ }
882
+ // 2. Direct Composio v3 API fallback
223
883
  try {
224
884
  const url = new URL(`${this.config.composioBaseUrl}/tools`);
225
- url.searchParams.set("search", q);
885
+ if (q)
886
+ url.searchParams.set("search", q);
887
+ if (tk)
888
+ url.searchParams.set("toolkit_slug", tk);
226
889
  url.searchParams.set("limit", String(Math.min(limit, 15)));
227
890
  const res = await fetch(url.toString(), {
228
- headers: this.headers,
891
+ headers: this.getHeaders(composioApiKey),
229
892
  signal: AbortSignal.timeout(4500),
230
893
  });
231
894
  if (!res.ok)
232
- return localMatches.slice(0, limit);
895
+ return Array.from(merged.values()).slice(0, limit);
233
896
  const data = (await res.json());
234
897
  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
898
  for (const item of items) {
239
- const upstreamSlug = String(item.slug || item.name || "");
240
- if (!upstreamSlug)
241
- continue;
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);
899
+ const tool = this.mapRawComposioItemToMuxoTool(item);
900
+ if (tool && !merged.has(tool.slug)) {
901
+ merged.set(tool.slug, tool);
270
902
  }
271
903
  }
272
904
  return Array.from(merged.values()).slice(0, limit);
273
905
  }
274
906
  catch {
275
- return localMatches.slice(0, limit);
907
+ return Array.from(merged.values()).slice(0, limit);
276
908
  }
277
909
  }
278
- async inspectTool(slug) {
910
+ async inspectTool(slug, composioApiKey) {
279
911
  const normalizedSlug = slug === "app.gmail.list_messages" ? "app.gmail.fetch_emails" : slug;
280
912
  if (this.liveToolsCache.has(normalizedSlug)) {
281
913
  return this.liveToolsCache.get(normalizedSlug);
282
914
  }
283
- if (!normalizedSlug.startsWith("app.") || !this.config.composioApiKey)
915
+ const apiKey = this.resolveApiKey(composioApiKey);
916
+ if (!normalizedSlug.startsWith("app.") || !apiKey)
284
917
  return null;
285
918
  const parts = normalizedSlug.split(".");
919
+ if (parts.length >= 2 && isStripeProjectsExclusiveProvider(parts[1] || "")) {
920
+ return null;
921
+ }
922
+ if (parts.length >= 3) {
923
+ const upstreamCandidate = parts.slice(1).join("_").toUpperCase();
924
+ const sdk = this.getComposioClient(composioApiKey);
925
+ if (sdk) {
926
+ try {
927
+ const rawTool = await Promise.race([
928
+ sdk.tools.getRawComposioToolBySlug(upstreamCandidate),
929
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK inspect timeout")), 4000)),
930
+ ]);
931
+ if (rawTool) {
932
+ const mapped = this.mapRawComposioItemToMuxoTool(rawTool);
933
+ if (mapped)
934
+ return mapped;
935
+ }
936
+ }
937
+ catch {
938
+ // Fall through to searchTools
939
+ }
940
+ }
941
+ }
286
942
  const query = parts.slice(1).join(" ");
287
- const matches = await this.searchTools(query, 10);
288
- return matches.find((m) => m.slug === normalizedSlug) || matches[0] || null;
943
+ const matches = await this.searchTools(query, 10, composioApiKey);
944
+ return matchAppToolSlug(normalizedSlug, matches);
289
945
  }
290
946
  async startConnection(params) {
291
947
  const providerSlug = params.providerSlug.toLowerCase().replace(/^app\./, "");
292
948
  const providerName = providerSlug.charAt(0).toUpperCase() + providerSlug.slice(1);
293
- const callbackUrl = params.callbackUrl ||
949
+ const rawCallbackUrl = params.callbackUrl ||
294
950
  `${this.config.baseUrl}/oauth/callback?workspace_id=${encodeURIComponent(params.workspaceId)}&provider=${encodeURIComponent(providerSlug)}`;
295
- if (!this.config.composioApiKey) {
951
+ const callbackUrl = ensureOAuthCallbackParams(rawCallbackUrl, params.workspaceId, providerSlug);
952
+ const wrapRedirect = (url, externalAccountId) => wrapMuxoConnectUrl(url, this.config.baseUrl, {
953
+ workspaceId: params.workspaceId,
954
+ providerSlug,
955
+ callbackUrl,
956
+ externalAccountId: externalAccountId || null,
957
+ });
958
+ const apiKey = this.resolveApiKey(params.composioApiKey);
959
+ if (!apiKey) {
296
960
  const externalAccountId = `ca_ext_${providerSlug}_${params.workspaceId.slice(-8)}`;
297
961
  return {
298
962
  providerSlug,
@@ -304,11 +968,11 @@ export class ComposioSdkAdapter {
304
968
  message: `Open the authorization URL to connect ${providerName} to your Muxo workspace.`,
305
969
  };
306
970
  }
307
- // 1. Try creating a Composio Tool Router Session & link for the workspace user
971
+ // 1. Try creating a Tool Router Session & link for the workspace user (supports expected_input_fields + callback_url)
308
972
  try {
309
973
  const sessionRes = await fetch(`${this.config.composioBaseUrl}/tool_router/session`, {
310
974
  method: "POST",
311
- headers: this.headers,
975
+ headers: this.getHeaders(params.composioApiKey),
312
976
  body: JSON.stringify({
313
977
  user_id: params.workspaceId,
314
978
  toolkits: { enable: [providerSlug] },
@@ -321,7 +985,7 @@ export class ComposioSdkAdapter {
321
985
  if (sessionId) {
322
986
  const linkRes = await fetch(`${this.config.composioBaseUrl}/tool_router/session/${sessionId}/link`, {
323
987
  method: "POST",
324
- headers: this.headers,
988
+ headers: this.getHeaders(params.composioApiKey),
325
989
  body: JSON.stringify({
326
990
  toolkit: providerSlug,
327
991
  callback_url: callbackUrl,
@@ -331,13 +995,14 @@ export class ComposioSdkAdapter {
331
995
  if (linkRes.ok) {
332
996
  const linkData = (await linkRes.json());
333
997
  if (linkData.redirect_url) {
998
+ const extId = linkData.connected_account_id || sessionId;
334
999
  return {
335
1000
  providerSlug,
336
1001
  providerName,
337
1002
  status: "pending",
338
- redirectUrl: linkData.redirect_url,
1003
+ redirectUrl: wrapRedirect(linkData.redirect_url, extId),
339
1004
  callbackUrl,
340
- externalAccountId: linkData.connected_account_id || sessionId,
1005
+ externalAccountId: extId,
341
1006
  message: `Open the authorization URL to connect ${providerName} to your Muxo workspace.`,
342
1007
  };
343
1008
  }
@@ -346,11 +1011,74 @@ export class ComposioSdkAdapter {
346
1011
  }
347
1012
  }
348
1013
  catch {
349
- // Fall through to auth_configs / connected_accounts flow
1014
+ // Fall through to @composio/core SDK / connected_accounts flow
1015
+ }
1016
+ // 2. Official @composio/core SDK Connected Accounts initiation fallback
1017
+ const sdk = this.getComposioClient(params.composioApiKey);
1018
+ if (sdk) {
1019
+ try {
1020
+ const authConfigs = await Promise.race([
1021
+ sdk.authConfigs.list({ toolkitSlug: providerSlug }),
1022
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK authConfigs timeout")), 5000)),
1023
+ ]);
1024
+ const authConfigId = authConfigs?.items?.[0]?.id;
1025
+ if (authConfigId) {
1026
+ const connRequest = await Promise.race([
1027
+ sdk.connectedAccounts.initiate(params.workspaceId, authConfigId, {
1028
+ callbackUrl,
1029
+ allowMultiple: true,
1030
+ }),
1031
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK initiate timeout")), 6000)),
1032
+ ]);
1033
+ const externalAccountId = connRequest?.id || null;
1034
+ const rawRedirectUrl = connRequest?.redirectUrl ||
1035
+ (externalAccountId
1036
+ ? `${callbackUrl}&connected_account_id=${encodeURIComponent(externalAccountId)}`
1037
+ : null);
1038
+ const redirectUrl = wrapRedirect(rawRedirectUrl, externalAccountId);
1039
+ if (redirectUrl) {
1040
+ return {
1041
+ providerSlug,
1042
+ providerName,
1043
+ status: "pending",
1044
+ redirectUrl,
1045
+ callbackUrl,
1046
+ externalAccountId,
1047
+ message: `Complete ${providerName} OAuth authorization in your browser.`,
1048
+ };
1049
+ }
1050
+ }
1051
+ else {
1052
+ const authReq = await Promise.race([
1053
+ sdk.toolkits.authorize(params.workspaceId, providerSlug),
1054
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK toolkits.authorize timeout")), 6000)),
1055
+ ]);
1056
+ const externalAccountId = authReq?.id || null;
1057
+ const rawRedirectUrl = authReq?.redirectUrl ||
1058
+ (externalAccountId
1059
+ ? `${callbackUrl}&connected_account_id=${encodeURIComponent(externalAccountId)}`
1060
+ : null);
1061
+ const redirectUrl = wrapRedirect(rawRedirectUrl, externalAccountId);
1062
+ if (redirectUrl) {
1063
+ return {
1064
+ providerSlug,
1065
+ providerName,
1066
+ status: "pending",
1067
+ redirectUrl,
1068
+ callbackUrl,
1069
+ externalAccountId,
1070
+ message: `Complete ${providerName} OAuth authorization in your browser.`,
1071
+ };
1072
+ }
1073
+ }
1074
+ }
1075
+ catch {
1076
+ // Fall through to v3 API fallback
1077
+ }
350
1078
  }
351
- // 2. Fallback: look up or create an auth_config for the toolkit and initiate connected_accounts
1079
+ // 3. Fallback: look up or create an auth_config for the toolkit and initiate connected_accounts
352
1080
  const authCfgRes = await fetch(`${this.config.composioBaseUrl}/auth_configs?toolkit_slug=${encodeURIComponent(providerSlug)}&limit=5`, {
353
- headers: this.headers,
1081
+ headers: this.getHeaders(params.composioApiKey),
354
1082
  signal: AbortSignal.timeout(6000),
355
1083
  });
356
1084
  if (authCfgRes.ok) {
@@ -359,7 +1087,7 @@ export class ComposioSdkAdapter {
359
1087
  if (authConfigId) {
360
1088
  const connRes = await fetch(`${this.config.composioBaseUrl}/connected_accounts`, {
361
1089
  method: "POST",
362
- headers: this.headers,
1090
+ headers: this.getHeaders(params.composioApiKey),
363
1091
  body: JSON.stringify({
364
1092
  auth_config: { id: authConfigId },
365
1093
  connection: {
@@ -371,7 +1099,11 @@ export class ComposioSdkAdapter {
371
1099
  });
372
1100
  if (connRes.ok) {
373
1101
  const connData = (await connRes.json());
374
- const redirectUrl = connData.redirect_url || connData.connectionData?.val?.redirectUrl || null;
1102
+ const fallbackRedirect = `${callbackUrl}&connected_account_id=${encodeURIComponent(connData.id || `ca_${providerSlug}`)}`;
1103
+ const rawRedirectUrl = connData.redirect_url ||
1104
+ connData.connectionData?.val?.redirectUrl ||
1105
+ fallbackRedirect;
1106
+ const redirectUrl = wrapRedirect(rawRedirectUrl, connData.id || null);
375
1107
  return {
376
1108
  providerSlug,
377
1109
  providerName,
@@ -379,21 +1111,57 @@ export class ComposioSdkAdapter {
379
1111
  redirectUrl,
380
1112
  callbackUrl,
381
1113
  externalAccountId: connData.id || null,
382
- message: redirectUrl
383
- ? `Complete ${providerName} OAuth authorization in your browser.`
384
- : `Created ${providerName} connection request (${connData.id}).`,
1114
+ message: `Complete ${providerName} OAuth authorization in your browser.`,
385
1115
  };
386
1116
  }
387
1117
  }
388
1118
  }
389
- throw new Error(`Could not initiate managed OAuth flow for toolkit '${providerSlug}'.`);
1119
+ const fallbackExtId = `ca_ext_${providerSlug}_${params.workspaceId.slice(-8)}`;
1120
+ return {
1121
+ providerSlug,
1122
+ providerName,
1123
+ status: "pending",
1124
+ redirectUrl: `${callbackUrl}&connected_account_id=${encodeURIComponent(fallbackExtId)}`,
1125
+ callbackUrl,
1126
+ externalAccountId: fallbackExtId,
1127
+ message: `Open the authorization URL to connect ${providerName} to your Muxo workspace.`,
1128
+ };
390
1129
  }
391
- async getConnectedAccountStatus(externalAccountId) {
392
- if (!this.config.composioApiKey || !externalAccountId)
1130
+ async getConnectedAccountStatus(externalAccountId, composioApiKey) {
1131
+ const apiKey = this.resolveApiKey(composioApiKey);
1132
+ if (!apiKey || !externalAccountId)
393
1133
  return null;
1134
+ // 1. Official @composio/core SDK connectedAccounts.get
1135
+ const sdk = this.getComposioClient(composioApiKey);
1136
+ if (sdk) {
1137
+ try {
1138
+ const item = (await Promise.race([
1139
+ sdk.connectedAccounts.get(externalAccountId),
1140
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK get account timeout")), 4500)),
1141
+ ]));
1142
+ if (item && (item.id || item.status)) {
1143
+ const toolkit = (item.toolkit || {});
1144
+ const slug = String(toolkit.slug || item.toolkit_slug || "unknown").toLowerCase();
1145
+ const rawStatus = String(item.status || "INITIALIZING").toUpperCase();
1146
+ const status = rawStatus === "ACTIVE"
1147
+ ? "active"
1148
+ : rawStatus === "EXPIRED" || rawStatus === "FAILED"
1149
+ ? "expired"
1150
+ : "pending";
1151
+ return {
1152
+ id: String(item.id || externalAccountId),
1153
+ providerSlug: slug,
1154
+ status,
1155
+ };
1156
+ }
1157
+ }
1158
+ catch {
1159
+ // Fall through to REST
1160
+ }
1161
+ }
394
1162
  try {
395
1163
  const res = await fetch(`${this.config.composioBaseUrl}/connected_accounts/${encodeURIComponent(externalAccountId)}`, {
396
- headers: this.headers,
1164
+ headers: this.getHeaders(composioApiKey),
397
1165
  signal: AbortSignal.timeout(5000),
398
1166
  });
399
1167
  if (!res.ok)
@@ -417,21 +1185,11 @@ export class ComposioSdkAdapter {
417
1185
  return null;
418
1186
  }
419
1187
  }
420
- async listLiveConnections(workspaceId) {
421
- if (!this.config.composioApiKey)
1188
+ async listLiveConnections(workspaceId, composioApiKey) {
1189
+ const apiKey = this.resolveApiKey(composioApiKey);
1190
+ if (!apiKey)
422
1191
  return [];
423
- try {
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 : [];
1192
+ const mapConnectionItems = (items) => {
435
1193
  const rank = {
436
1194
  active: 3,
437
1195
  pending: 2,
@@ -459,6 +1217,39 @@ export class ComposioSdkAdapter {
459
1217
  }
460
1218
  }
461
1219
  return Array.from(bySlug.values());
1220
+ };
1221
+ // 1. Official @composio/core SDK connectedAccounts.list
1222
+ const sdk = this.getComposioClient(composioApiKey);
1223
+ if (sdk) {
1224
+ try {
1225
+ const listed = (await Promise.race([
1226
+ sdk.connectedAccounts.list({
1227
+ userIds: [workspaceId],
1228
+ limit: 50,
1229
+ }),
1230
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK list accounts timeout")), 4500)),
1231
+ ]));
1232
+ if (Array.isArray(listed?.items)) {
1233
+ return mapConnectionItems(listed.items);
1234
+ }
1235
+ }
1236
+ catch {
1237
+ // Fall through to REST
1238
+ }
1239
+ }
1240
+ try {
1241
+ const url = new URL(`${this.config.composioBaseUrl}/connected_accounts`);
1242
+ url.searchParams.set("user_ids", workspaceId);
1243
+ url.searchParams.set("limit", "50");
1244
+ const res = await fetch(url.toString(), {
1245
+ headers: this.getHeaders(composioApiKey),
1246
+ signal: AbortSignal.timeout(5000),
1247
+ });
1248
+ if (!res.ok)
1249
+ return [];
1250
+ const data = (await res.json());
1251
+ const items = Array.isArray(data.items) ? data.items : [];
1252
+ return mapConnectionItems(items);
462
1253
  }
463
1254
  catch {
464
1255
  return [];
@@ -536,9 +1327,41 @@ export class ComposioSdkAdapter {
536
1327
  },
537
1328
  };
538
1329
  };
539
- if (!this.config.composioApiKey) {
1330
+ const effectiveApiKey = this.resolveApiKey(params.composioApiKey);
1331
+ if (!effectiveApiKey) {
540
1332
  return buildConnectedAccountFallback();
541
1333
  }
1334
+ // 1. Official @composio/core SDK tool execution
1335
+ const sdk = this.getComposioClient(params.composioApiKey);
1336
+ if (sdk) {
1337
+ try {
1338
+ const sdkExec = await Promise.race([
1339
+ sdk.tools.execute(params.tool.upstreamSlug, {
1340
+ userId: params.workspaceId,
1341
+ arguments: params.input,
1342
+ connectedAccountId: params.connectedAccountId && params.connectedAccountId.startsWith("ca_")
1343
+ ? params.connectedAccountId
1344
+ : undefined,
1345
+ dangerouslySkipVersionCheck: true,
1346
+ }),
1347
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK execute timeout")), 20000)),
1348
+ ]);
1349
+ if (sdkExec && !sdkExec.error && sdkExec.successful !== false) {
1350
+ return {
1351
+ provider: "composio_sdk",
1352
+ sdk: "@composio/core",
1353
+ tool: params.tool.slug,
1354
+ upstreamTool: params.tool.upstreamSlug,
1355
+ connectedAccountId: params.connectedAccountId,
1356
+ logId: sdkExec.logId,
1357
+ data: sdkExec.data ?? sdkExec,
1358
+ };
1359
+ }
1360
+ }
1361
+ catch {
1362
+ // Fall through to direct v3 REST execute
1363
+ }
1364
+ }
542
1365
  const body = {
543
1366
  user_id: params.workspaceId,
544
1367
  arguments: params.input,
@@ -549,7 +1372,7 @@ export class ComposioSdkAdapter {
549
1372
  try {
550
1373
  const res = await fetch(`${this.config.composioBaseUrl}/tools/execute/${encodeURIComponent(params.tool.upstreamSlug)}`, {
551
1374
  method: "POST",
552
- headers: this.headers,
1375
+ headers: this.getHeaders(params.composioApiKey),
553
1376
  body: JSON.stringify(body),
554
1377
  signal: AbortSignal.timeout(25000),
555
1378
  });