aui-agent-builder 0.3.57 → 0.3.59

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.
@@ -0,0 +1,1265 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { render, Box, Text } from "ink";
3
+ import inquirer from "inquirer";
4
+ import chalk from "chalk";
5
+ import { getAuthenticatedSession, discoverMCPTools, discoverComposioTools, saveIntegration as saveIntegrationSvc, resolveScopeIds, resolveNetworkCategoryId, listComposioToolkits, createComposioSession, authorizeComposioToolkit, waitForComposioConnection, resetComposioClient, fetchComposioApiKey, } from "../services/integration.service.js";
6
+ import { MCPToolList, IntegrationCreatedView, NativeIntegrationCreatedView, } from "../ui/views/IntegrationView.js";
7
+ import { Header, Spinner, StatusLine, ErrorDisplay, } from "../ui/components/index.js";
8
+ import { AUIClient } from "../api-client/index.js";
9
+ import { getConfig, loadSession, saveSession, } from "../config/index.js";
10
+ import { isJsonMode, outputJson, outputJsonError, stderrLog } from "../utils/json-output.js";
11
+ import open from "open";
12
+ // ─── Ink Rendering Helpers ───
13
+ function log(node) {
14
+ const { unmount } = render(node);
15
+ unmount();
16
+ }
17
+ function startSpinner(label) {
18
+ const inst = render(_jsx(Spinner, { label: label }));
19
+ return {
20
+ succeed(msg) {
21
+ inst.unmount();
22
+ log(_jsx(StatusLine, { kind: "success", label: msg }));
23
+ },
24
+ fail(msg) {
25
+ inst.unmount();
26
+ log(_jsx(StatusLine, { kind: "error", label: msg }));
27
+ },
28
+ stop() {
29
+ inst.unmount();
30
+ },
31
+ };
32
+ }
33
+ async function selectTarget(session, opts) {
34
+ const config = getConfig();
35
+ const client = new AUIClient({
36
+ baseUrl: config.apiUrl,
37
+ authToken: config.authToken,
38
+ accountId: config.accountId,
39
+ organizationId: config.organizationId,
40
+ environment: config.environment,
41
+ });
42
+ let orgId = opts.organizationId || config.organizationId || "";
43
+ let accId = opts.accountId || config.accountId || "";
44
+ let netId = opts.networkId || session.network_id || "";
45
+ let catId = opts.networkCategoryId || "";
46
+ let verId = opts.versionId || session.version_id || "";
47
+ let agentName = session.network_name || "";
48
+ const isFullySpecified = !!(opts.organizationId && opts.accountId && opts.networkId);
49
+ if (!isFullySpecified && !isJsonMode()) {
50
+ // Step 1: Organization
51
+ if (!opts.organizationId) {
52
+ log(_jsx(Text, { color: "gray", children: " \u250C Step 1 \u2192 Select Organization" }));
53
+ const orgSpinner = render(_jsx(Spinner, { label: "Fetching organizations..." }));
54
+ try {
55
+ const orgResp = await client.organizations.listMy(1, 50);
56
+ orgSpinner.unmount();
57
+ const orgs = orgResp.data.docs;
58
+ if (orgs.length === 1) {
59
+ orgId = orgs[0]._id || orgs[0].id;
60
+ client.setScope({ organizationId: orgId });
61
+ log(_jsx(StatusLine, { kind: "success", label: `Organization: ${orgs[0].name}` }));
62
+ }
63
+ else if (orgs.length > 1) {
64
+ const { chosen } = await inquirer.prompt([
65
+ {
66
+ type: "list",
67
+ name: "chosen",
68
+ message: "Select organization:",
69
+ choices: orgs.map((o) => ({
70
+ name: o.name,
71
+ value: o._id || o.id,
72
+ })),
73
+ pageSize: 15,
74
+ },
75
+ ]);
76
+ orgId = chosen;
77
+ client.setScope({ organizationId: orgId });
78
+ }
79
+ }
80
+ catch {
81
+ orgSpinner.unmount();
82
+ }
83
+ }
84
+ else {
85
+ orgId = opts.organizationId;
86
+ client.setScope({ organizationId: orgId });
87
+ }
88
+ // Step 2: Account
89
+ if (!opts.accountId) {
90
+ log(_jsx(Text, { color: "gray", children: " \u251C Step 2 \u2192 Select Account" }));
91
+ const accSpinner = render(_jsx(Spinner, { label: "Fetching accounts..." }));
92
+ try {
93
+ const accResp = await client.accounts.list(1, 50);
94
+ accSpinner.unmount();
95
+ const accounts = accResp.data.docs;
96
+ if (accounts.length === 1) {
97
+ accId = accounts[0]._id || accounts[0].id;
98
+ client.setScope({ accountId: accId });
99
+ log(_jsx(StatusLine, { kind: "success", label: `Account: ${accounts[0].name}` }));
100
+ }
101
+ else if (accounts.length > 1) {
102
+ const currentId = session.account_id;
103
+ const { chosen } = await inquirer.prompt([
104
+ {
105
+ type: "list",
106
+ name: "chosen",
107
+ message: "Select account:",
108
+ choices: accounts.map((a) => {
109
+ const isCurrent = a._id === currentId || a.id === currentId;
110
+ const label = isCurrent
111
+ ? `${a.name} ${chalk.gray(`(${a.niceName})`)} ${chalk.green("← current")}`
112
+ : `${a.name} ${chalk.gray(`(${a.niceName})`)}`;
113
+ return { name: label, value: a._id || a.id };
114
+ }),
115
+ pageSize: 15,
116
+ },
117
+ ]);
118
+ accId = chosen;
119
+ client.setScope({ accountId: accId });
120
+ }
121
+ }
122
+ catch {
123
+ accSpinner.unmount();
124
+ }
125
+ }
126
+ else {
127
+ accId = opts.accountId;
128
+ client.setScope({ accountId: accId });
129
+ }
130
+ // Step 3: Agent (try agent-management first, fallback to networks)
131
+ if (!opts.networkId) {
132
+ log(_jsx(Text, { color: "gray", children: " \u251C Step 3 \u2192 Select Agent" }));
133
+ const agentSpinner = render(_jsx(Spinner, { label: "Fetching agents..." }));
134
+ let agents = [];
135
+ try {
136
+ const agentResp = await client.agentManagement.listAgents(orgId, 1, 50);
137
+ agents = agentResp.items;
138
+ }
139
+ catch {
140
+ // agent-management endpoint not available
141
+ }
142
+ // Fallback to old networks endpoint
143
+ if (agents.length === 0) {
144
+ try {
145
+ const netResp = await client.networks.list();
146
+ agents = netResp.data.map((n) => ({
147
+ id: n._id || n.id,
148
+ name: n.name,
149
+ scope: {
150
+ network_id: n._id || n.id,
151
+ account_id: n.account || null,
152
+ organization_id: n.organization || null,
153
+ network_category_id: typeof n.category === "string"
154
+ ? n.category
155
+ : n.category?._id || null,
156
+ type: "NETWORK",
157
+ },
158
+ active_version_id: null,
159
+ created_at: n.createdAt,
160
+ created_by: n.createdBy,
161
+ updated_at: n.updatedAt,
162
+ scope_id: n._id || n.id,
163
+ }));
164
+ }
165
+ catch {
166
+ // networks endpoint also failed
167
+ }
168
+ }
169
+ agentSpinner.unmount();
170
+ if (agents.length === 0) {
171
+ log(_jsx(ErrorDisplay, { message: "No agents found in this account.", suggestion: 'Create one first with: aui agents --create' }));
172
+ return null;
173
+ }
174
+ const currentNetId = session.network_id;
175
+ const { chosenAgent } = await inquirer.prompt([
176
+ {
177
+ type: "list",
178
+ name: "chosenAgent",
179
+ message: "Select agent:",
180
+ choices: agents.map((a) => {
181
+ const isCurrent = (a.scope.network_id || a.id) === currentNetId;
182
+ const label = isCurrent
183
+ ? `${a.name} ${chalk.green("← current")}`
184
+ : a.name;
185
+ return { name: label, value: a };
186
+ }),
187
+ pageSize: 15,
188
+ },
189
+ ]);
190
+ const agent = chosenAgent;
191
+ netId = agent.scope.network_id || agent.id;
192
+ catId = agent.scope.network_category_id || "";
193
+ agentName = agent.name;
194
+ log(_jsx(StatusLine, { kind: "success", label: `Agent: ${agent.name}` }));
195
+ // Prompt version selection if agent has versions
196
+ if (agent.active_version_id) {
197
+ try {
198
+ const versionsResp = await client.agentManagement.listVersions(agent.id, 1, 50);
199
+ const versions = versionsResp.items;
200
+ if (versions.length > 0) {
201
+ const currentVerid = session.version_id;
202
+ const { chosenVersion } = await inquirer.prompt([
203
+ {
204
+ type: "list",
205
+ name: "chosenVersion",
206
+ message: "Select version:",
207
+ choices: [
208
+ ...versions.map((v) => {
209
+ const isCurrent = v.id === currentVerid;
210
+ const vLabel = `v${v.version_number}.${v.version_revision_number}`;
211
+ const statusBadge = v.status === "published"
212
+ ? chalk.green(v.status)
213
+ : chalk.yellow(v.status);
214
+ const label = isCurrent
215
+ ? `${vLabel} [${statusBadge}] ${chalk.green("← current")}`
216
+ : `${vLabel} [${statusBadge}]`;
217
+ return { name: label, value: v };
218
+ }),
219
+ new inquirer.Separator(),
220
+ { name: chalk.gray("Skip — no version"), value: null },
221
+ ],
222
+ pageSize: 15,
223
+ },
224
+ ]);
225
+ if (chosenVersion) {
226
+ const v = chosenVersion;
227
+ verId = v.id;
228
+ }
229
+ }
230
+ }
231
+ catch {
232
+ // versions unavailable — proceed without
233
+ }
234
+ }
235
+ }
236
+ else {
237
+ netId = opts.networkId;
238
+ // Fetch agent name and category from network
239
+ try {
240
+ const netResp = await client.networks.get(netId);
241
+ agentName = netResp.data.name;
242
+ catId =
243
+ typeof netResp.data.category === "string"
244
+ ? netResp.data.category
245
+ : netResp.data.category?._id || "";
246
+ }
247
+ catch {
248
+ // proceed with empty
249
+ }
250
+ }
251
+ // Persist session
252
+ const currentSession = loadSession();
253
+ currentSession.organization_id = orgId;
254
+ currentSession.account_id = accId;
255
+ if (netId) {
256
+ currentSession.network_id = netId;
257
+ currentSession.network_name = agentName;
258
+ }
259
+ saveSession(currentSession);
260
+ }
261
+ // Resolve category if still empty
262
+ if (!catId && netId) {
263
+ const scope = resolveScopeIds(session, {
264
+ organizationId: orgId,
265
+ accountId: accId,
266
+ networkId: netId,
267
+ });
268
+ catId = await resolveNetworkCategoryId(session, netId, scope);
269
+ }
270
+ return {
271
+ scope: {
272
+ organizationId: orgId,
273
+ accountId: accId,
274
+ networkId: netId,
275
+ networkCategoryId: catId,
276
+ userId: session.user_id || "",
277
+ versionId: verId,
278
+ },
279
+ agentName,
280
+ };
281
+ }
282
+ // ─── Main Entry Point ───
283
+ export async function integration(_options = {}) {
284
+ await getAuthenticatedSession();
285
+ if (isJsonMode()) {
286
+ stderrLog("Use a subcommand: aui integration create, aui integration discover");
287
+ outputJson({ available_commands: ["create", "discover"] });
288
+ return;
289
+ }
290
+ log(_jsx(Header, { title: "Integrations", subtitle: "Create and manage integrations" }));
291
+ const { action } = await inquirer.prompt([
292
+ {
293
+ type: "list",
294
+ name: "action",
295
+ message: "What would you like to do?",
296
+ choices: [
297
+ { name: "Create a new integration", value: "create" },
298
+ { name: "Discover tools from an MCP server", value: "discover" },
299
+ new inquirer.Separator(),
300
+ { name: "Back", value: "back" },
301
+ ],
302
+ },
303
+ ]);
304
+ if (action === "back")
305
+ return;
306
+ if (action === "create")
307
+ await integrationCreate({});
308
+ else if (action === "discover")
309
+ await integrationDiscover({});
310
+ }
311
+ // ─── Discover Subcommand ───
312
+ export async function integrationDiscover(options = {}) {
313
+ const session = await getAuthenticatedSession();
314
+ let serverUrl = options.url || "";
315
+ let auth = { type: "none" };
316
+ if (options.authType === "token" && options.authToken) {
317
+ auth = { type: "token", token: options.authToken };
318
+ }
319
+ // Resolve scope
320
+ const scope = resolveScopeIds(session, {
321
+ organizationId: options.organizationId,
322
+ accountId: options.accountId,
323
+ networkId: options.networkId,
324
+ });
325
+ // Interactive: prompt for URL if not provided
326
+ if (!serverUrl) {
327
+ if (isJsonMode()) {
328
+ outputJsonError({ code: "VALIDATION_ERROR", message: "Missing required option: --url" });
329
+ }
330
+ log(_jsx(Header, { title: "MCP Discovery" }));
331
+ const answers = await inquirer.prompt([
332
+ {
333
+ type: "input",
334
+ name: "url",
335
+ message: "MCP server URL:",
336
+ validate: (input) => {
337
+ const trimmed = input.trim();
338
+ if (!trimmed)
339
+ return "URL is required";
340
+ try {
341
+ new URL(trimmed);
342
+ return true;
343
+ }
344
+ catch {
345
+ return "Must be a valid URL";
346
+ }
347
+ },
348
+ },
349
+ ]);
350
+ serverUrl = answers.url.trim();
351
+ const { authMethod } = await inquirer.prompt([
352
+ {
353
+ type: "list",
354
+ name: "authMethod",
355
+ message: "Authentication method:",
356
+ choices: [
357
+ { name: "None", value: "none" },
358
+ { name: "Token", value: "token" },
359
+ ],
360
+ },
361
+ ]);
362
+ if (authMethod === "token") {
363
+ const { token } = await inquirer.prompt([
364
+ {
365
+ type: "password",
366
+ name: "token",
367
+ message: "Authentication token:",
368
+ mask: "*",
369
+ validate: (input) => input.trim().length > 0 || "Token is required",
370
+ },
371
+ ]);
372
+ auth = { type: "token", token: token.trim() };
373
+ }
374
+ }
375
+ if (isJsonMode()) {
376
+ stderrLog("Discovering MCP tools...");
377
+ }
378
+ const spinner = isJsonMode()
379
+ ? null
380
+ : startSpinner("Discovering tools from MCP server...");
381
+ try {
382
+ const result = await discoverMCPTools(session, serverUrl, scope, auth);
383
+ if (isJsonMode()) {
384
+ outputJson({
385
+ server_url: serverUrl,
386
+ tools: result.tools.map((t) => ({
387
+ name: t.name,
388
+ description: t.description,
389
+ input_schema: t.inputSchema || null,
390
+ })),
391
+ total: result.tools.length,
392
+ });
393
+ return;
394
+ }
395
+ spinner?.succeed(`Discovered ${result.tools.length} tools`);
396
+ if (result.tools.length === 0) {
397
+ log(_jsx(StatusLine, { kind: "warning", label: "No tools found on this MCP server." }));
398
+ return;
399
+ }
400
+ log(_jsx(MCPToolList, { tools: result.tools }));
401
+ }
402
+ catch (error) {
403
+ if (isJsonMode()) {
404
+ outputJsonError({
405
+ code: "API_CLIENT_ERROR",
406
+ message: error instanceof Error ? error.message : String(error),
407
+ });
408
+ }
409
+ spinner?.fail("Discovery failed");
410
+ log(_jsx(ErrorDisplay, { error: error, message: "Failed to discover MCP tools." }));
411
+ }
412
+ }
413
+ // ─── Create Subcommand ───
414
+ export async function integrationCreate(options = {}) {
415
+ const session = await getAuthenticatedSession();
416
+ // Detect non-interactive mode for each path
417
+ const isManualNonInteractive = !!(options.url && options.name && (options.tools || options.allTools));
418
+ const isNativeNonInteractive = !!(options.composioApiKey && options.toolkit && options.name && (options.tools || options.allTools));
419
+ const isNonInteractive = isManualNonInteractive || isNativeNonInteractive;
420
+ // Determine config method
421
+ let configMethod = options.configMethod || "";
422
+ if (!configMethod) {
423
+ if (isManualNonInteractive) {
424
+ configMethod = "manual";
425
+ }
426
+ else if (isNativeNonInteractive || options.toolkit || options.composioApiKey) {
427
+ configMethod = "native";
428
+ }
429
+ }
430
+ if (!isJsonMode() && !isNonInteractive) {
431
+ log(_jsx(Header, { title: "Create Integration", subtitle: "MCP Integration" }));
432
+ }
433
+ // ── Step 1: Org → Account → Agent selection (shared) ──
434
+ if (!isJsonMode() && !isNonInteractive) {
435
+ log(_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", children: [" ", "Select the organization, account, and agent for this integration"] }) }));
436
+ }
437
+ const target = await selectTarget(session, {
438
+ organizationId: options.organizationId,
439
+ accountId: options.accountId,
440
+ networkId: options.networkId,
441
+ networkCategoryId: options.networkCategoryId,
442
+ versionId: options.versionId,
443
+ });
444
+ if (!target)
445
+ return;
446
+ const scope = target.scope;
447
+ if (!scope.networkId) {
448
+ if (isJsonMode()) {
449
+ outputJsonError({
450
+ code: "VALIDATION_ERROR",
451
+ message: "No agent selected. Provide --network-id or select one interactively.",
452
+ });
453
+ }
454
+ else {
455
+ log(_jsx(ErrorDisplay, { message: "No agent selected.", suggestion: "Select an agent or pass --network-id" }));
456
+ }
457
+ return;
458
+ }
459
+ // ── Step 2: Choose method ──
460
+ if (!configMethod) {
461
+ if (isJsonMode()) {
462
+ configMethod = "manual";
463
+ }
464
+ else {
465
+ const { method } = await inquirer.prompt([
466
+ {
467
+ type: "list",
468
+ name: "method",
469
+ message: "How would you like to configure the MCP integration?",
470
+ choices: [
471
+ {
472
+ name: `${chalk.bold("Manual Configuration")} — Configure MCP server manually`,
473
+ value: "manual",
474
+ },
475
+ {
476
+ name: `${chalk.bold("Native Integration")} — Browse & connect toolkits from the directory`,
477
+ value: "native",
478
+ },
479
+ ],
480
+ },
481
+ ]);
482
+ configMethod = method;
483
+ }
484
+ }
485
+ // ── Dispatch to the appropriate flow ──
486
+ if (configMethod === "native") {
487
+ await nativeIntegrationFlow(session, options, target);
488
+ }
489
+ else {
490
+ await manualIntegrationFlow(session, options, target);
491
+ }
492
+ }
493
+ // ─── Manual Integration Flow ───
494
+ async function manualIntegrationFlow(session, options, target) {
495
+ const scope = target.scope;
496
+ const isNonInteractive = !!(options.url && options.name && (options.tools || options.allTools));
497
+ let integrationName = options.name || "";
498
+ let description = options.description || "";
499
+ let serverUrl = options.url || "";
500
+ let auth = { type: "none" };
501
+ if (options.authType === "token" && options.authToken) {
502
+ auth = { type: "token", token: options.authToken };
503
+ }
504
+ // ── Name & Description ──
505
+ if (!integrationName) {
506
+ if (isJsonMode()) {
507
+ outputJsonError({ code: "VALIDATION_ERROR", message: "Missing required option: --name" });
508
+ return;
509
+ }
510
+ log(_jsx(Text, { color: "gray", children: " \u251C Integration Details" }));
511
+ const answers = await inquirer.prompt([
512
+ {
513
+ type: "input",
514
+ name: "name",
515
+ message: "Integration name:",
516
+ validate: (input) => {
517
+ const trimmed = input.trim();
518
+ if (!trimmed)
519
+ return "Name is required";
520
+ if (trimmed.length < 2)
521
+ return "Name must be at least 2 characters";
522
+ if (trimmed.length > 100)
523
+ return "Name must be under 100 characters";
524
+ return true;
525
+ },
526
+ },
527
+ {
528
+ type: "input",
529
+ name: "description",
530
+ message: "Description (optional):",
531
+ },
532
+ ]);
533
+ integrationName = answers.name.trim();
534
+ description = answers.description?.trim() || "";
535
+ }
536
+ // ── MCP Server URL ──
537
+ if (!serverUrl) {
538
+ if (isJsonMode()) {
539
+ outputJsonError({ code: "VALIDATION_ERROR", message: "Missing required option: --url" });
540
+ return;
541
+ }
542
+ const urlAnswer = await inquirer.prompt([
543
+ {
544
+ type: "input",
545
+ name: "url",
546
+ message: "MCP server URL:",
547
+ validate: (input) => {
548
+ const trimmed = input.trim();
549
+ if (!trimmed)
550
+ return "URL is required";
551
+ try {
552
+ new URL(trimmed);
553
+ return true;
554
+ }
555
+ catch {
556
+ return "Must be a valid URL";
557
+ }
558
+ },
559
+ },
560
+ ]);
561
+ serverUrl = urlAnswer.url.trim();
562
+ }
563
+ // ── Authentication ──
564
+ if (!options.authType && !isNonInteractive) {
565
+ const { authMethod } = await inquirer.prompt([
566
+ {
567
+ type: "list",
568
+ name: "authMethod",
569
+ message: "Authentication method:",
570
+ choices: [
571
+ { name: "None", value: "none" },
572
+ { name: "Token", value: "token" },
573
+ ],
574
+ },
575
+ ]);
576
+ if (authMethod === "token") {
577
+ const { token } = await inquirer.prompt([
578
+ {
579
+ type: "password",
580
+ name: "token",
581
+ message: "Authentication token:",
582
+ mask: "*",
583
+ validate: (input) => input.trim().length > 0 || "Token is required",
584
+ },
585
+ ]);
586
+ auth = { type: "token", token: token.trim() };
587
+ }
588
+ }
589
+ // ── Discover Tools ──
590
+ if (isJsonMode()) {
591
+ stderrLog("Discovering MCP tools...");
592
+ }
593
+ const spinner = isJsonMode()
594
+ ? null
595
+ : startSpinner("Discovering tools from MCP server...");
596
+ let discoveredTools;
597
+ try {
598
+ const result = await discoverMCPTools(session, serverUrl, scope, auth);
599
+ discoveredTools = result.tools;
600
+ if (discoveredTools.length === 0) {
601
+ if (isJsonMode()) {
602
+ outputJsonError({ code: "API_CLIENT_ERROR", message: "No tools discovered from the MCP server." });
603
+ }
604
+ spinner?.fail("No tools found on this MCP server");
605
+ return;
606
+ }
607
+ spinner?.succeed(`Discovered ${discoveredTools.length} tools`);
608
+ }
609
+ catch (error) {
610
+ if (isJsonMode()) {
611
+ outputJsonError({
612
+ code: "API_CLIENT_ERROR",
613
+ message: `Discovery failed: ${error instanceof Error ? error.message : String(error)}`,
614
+ });
615
+ }
616
+ spinner?.fail("Discovery failed");
617
+ log(_jsx(ErrorDisplay, { error: error, message: "Failed to discover MCP tools." }));
618
+ return;
619
+ }
620
+ // ── Select Tools ──
621
+ let selectedToolNames = [];
622
+ if (options.tools) {
623
+ const requestedTools = options.tools.split(",").map((t) => t.trim()).filter(Boolean);
624
+ const availableNames = new Set(discoveredTools.map((t) => t.name));
625
+ const missing = requestedTools.filter((t) => !availableNames.has(t));
626
+ if (missing.length > 0) {
627
+ const msg = `Tools not found on server: ${missing.join(", ")}`;
628
+ if (isJsonMode()) {
629
+ outputJsonError({ code: "VALIDATION_ERROR", message: msg });
630
+ }
631
+ else {
632
+ log(_jsx(StatusLine, { kind: "error", label: msg }));
633
+ }
634
+ return;
635
+ }
636
+ selectedToolNames = requestedTools;
637
+ }
638
+ else if (options.allTools) {
639
+ selectedToolNames = discoveredTools.map((t) => t.name);
640
+ }
641
+ else {
642
+ if (isJsonMode()) {
643
+ outputJsonError({
644
+ code: "VALIDATION_ERROR",
645
+ message: "Missing required option: --tools or --all-tools",
646
+ suggestion: `Available tools: ${discoveredTools.map((t) => t.name).join(", ")}`,
647
+ });
648
+ return;
649
+ }
650
+ const { selected } = await inquirer.prompt([
651
+ {
652
+ type: "checkbox",
653
+ name: "selected",
654
+ message: "Select tools to include:",
655
+ choices: discoveredTools.map((tool) => ({
656
+ name: `${tool.name} ${chalk.gray(`— ${tool.description || "No description"}`)}`,
657
+ value: tool.name,
658
+ checked: true,
659
+ })),
660
+ pageSize: 20,
661
+ validate: (input) => input.length > 0 || "Select at least one tool",
662
+ },
663
+ ]);
664
+ selectedToolNames = selected;
665
+ }
666
+ // ── Confirm ──
667
+ if (!isNonInteractive && !isJsonMode()) {
668
+ console.log("");
669
+ log(_jsx(StatusLine, { kind: "info", label: `Name: ${integrationName}` }));
670
+ log(_jsx(StatusLine, { kind: "info", label: `Type: MCP (Manual Configuration)` }));
671
+ log(_jsx(StatusLine, { kind: "info", label: `Agent: ${target.agentName || scope.networkId}` }));
672
+ log(_jsx(StatusLine, { kind: "info", label: `URL: ${serverUrl}` }));
673
+ log(_jsx(StatusLine, { kind: "info", label: `Tools: ${selectedToolNames.length} selected` }));
674
+ console.log(chalk.gray(selectedToolNames.map((t) => ` • ${t}`).join("\n")));
675
+ console.log("");
676
+ const { confirm } = await inquirer.prompt([
677
+ { type: "confirm", name: "confirm", message: "Create this integration?", default: true },
678
+ ]);
679
+ if (!confirm) {
680
+ log(_jsx(StatusLine, { kind: "muted", label: "Cancelled." }));
681
+ return;
682
+ }
683
+ }
684
+ // ── Save ──
685
+ if (isJsonMode()) {
686
+ stderrLog("Creating integration...");
687
+ }
688
+ const saveSpinner = isJsonMode() ? null : startSpinner("Creating integration...");
689
+ const request = {
690
+ name: integrationName,
691
+ display_name: integrationName,
692
+ description,
693
+ server_url: serverUrl,
694
+ authentication: auth,
695
+ tool_names: selectedToolNames,
696
+ };
697
+ try {
698
+ const result = await saveIntegrationSvc(session, request, scope);
699
+ if (!result.success) {
700
+ if (isJsonMode()) {
701
+ outputJsonError({ code: "API_CLIENT_ERROR", message: result.error || "Unknown error" });
702
+ }
703
+ saveSpinner?.fail("Failed to create integration");
704
+ log(_jsx(ErrorDisplay, { message: result.error || "Unknown error" }));
705
+ return;
706
+ }
707
+ if (isJsonMode()) {
708
+ outputJson({
709
+ integration: {
710
+ name: integrationName, type: "MCP", config_method: "manual",
711
+ server_url: serverUrl, authentication: { type: auth.type },
712
+ tool_names: selectedToolNames, tool_count: selectedToolNames.length,
713
+ scope: {
714
+ organization_id: scope.organizationId, account_id: scope.accountId,
715
+ network_id: scope.networkId, network_category_id: scope.networkCategoryId,
716
+ version_id: scope.versionId || null,
717
+ },
718
+ },
719
+ response: result.data,
720
+ });
721
+ return;
722
+ }
723
+ saveSpinner?.succeed("Integration created");
724
+ log(_jsx(IntegrationCreatedView, { name: integrationName, serverUrl: serverUrl, toolCount: selectedToolNames.length, toolNames: selectedToolNames }));
725
+ }
726
+ catch (error) {
727
+ if (isJsonMode()) {
728
+ outputJsonError({ code: "API_CLIENT_ERROR", message: error instanceof Error ? error.message : String(error) });
729
+ }
730
+ saveSpinner?.fail("Failed to create integration");
731
+ log(_jsx(ErrorDisplay, { error: error, message: "Failed to create integration." }));
732
+ }
733
+ }
734
+ // ─── Native Integration Flow (Composio) ───
735
+ async function nativeIntegrationFlow(session, options, target) {
736
+ const scope = target.scope;
737
+ const isNonInteractive = !!(options.composioApiKey && options.toolkit && options.name && (options.tools || options.allTools));
738
+ // ── Composio API Key (auto-fetched from backend, prompt as fallback) ──
739
+ let apiKey = options.composioApiKey || process.env.COMPOSIO_API_KEY || "";
740
+ if (!apiKey) {
741
+ // Try auto-fetch from backend configuration
742
+ const fetchedKey = await fetchComposioApiKey(session);
743
+ if (fetchedKey) {
744
+ apiKey = fetchedKey;
745
+ if (!isJsonMode()) {
746
+ log(_jsx(StatusLine, { kind: "success", label: "Integration configuration loaded" }));
747
+ }
748
+ }
749
+ else {
750
+ if (isJsonMode()) {
751
+ outputJsonError({
752
+ code: "CONFIG_ERROR",
753
+ message: "Integration API key not configured. Contact your administrator.",
754
+ });
755
+ return;
756
+ }
757
+ const { key } = await inquirer.prompt([
758
+ {
759
+ type: "password",
760
+ name: "key",
761
+ message: "Integration API Key:",
762
+ mask: "*",
763
+ validate: (input) => input.trim().length > 0 || "API key is required",
764
+ },
765
+ ]);
766
+ apiKey = key.trim();
767
+ }
768
+ }
769
+ // ── List & Select Toolkit ──
770
+ let selectedToolkit = null;
771
+ let toolkitSlug = options.toolkit || "";
772
+ if (!toolkitSlug) {
773
+ if (isJsonMode()) {
774
+ outputJsonError({
775
+ code: "VALIDATION_ERROR",
776
+ message: "Missing required option: --toolkit",
777
+ });
778
+ return;
779
+ }
780
+ // Search prompt
781
+ const { searchQuery } = await inquirer.prompt([
782
+ {
783
+ type: "input",
784
+ name: "searchQuery",
785
+ message: "Search toolkits (or press Enter to browse all):",
786
+ },
787
+ ]);
788
+ let searchTerm = searchQuery?.trim() || undefined;
789
+ const toolkitSpinner = startSpinner(searchTerm ? `Searching toolkits for "${searchTerm}"...` : "Fetching available toolkits...");
790
+ let toolkits;
791
+ let nextCursor;
792
+ try {
793
+ const result = await listComposioToolkits(apiKey, {
794
+ limit: 50,
795
+ search: searchTerm,
796
+ });
797
+ toolkits = result.items;
798
+ nextCursor = result.nextCursor;
799
+ toolkitSpinner.succeed(`Found ${toolkits.length} toolkits`);
800
+ }
801
+ catch (error) {
802
+ toolkitSpinner.fail("Failed to fetch toolkits");
803
+ const msg = error instanceof Error ? error.message : String(error);
804
+ if (msg.includes("401") || msg.includes("Unauthorized") || msg.includes("invalid")) {
805
+ log(_jsx(ErrorDisplay, { message: "Invalid API key.", suggestion: "Contact your administrator for a valid key." }));
806
+ }
807
+ else {
808
+ log(_jsx(ErrorDisplay, { error: error, message: "Failed to fetch toolkits." }));
809
+ }
810
+ resetComposioClient();
811
+ return;
812
+ }
813
+ if (toolkits.length === 0) {
814
+ log(_jsx(ErrorDisplay, { message: searchTerm ? `No toolkits found matching "${searchTerm}".` : "No toolkits found.", suggestion: searchTerm ? "Try a different search term." : "Verify your API key is valid." }));
815
+ resetComposioClient();
816
+ return;
817
+ }
818
+ // Load all pages upfront if user wants more, then present one list
819
+ let picked = false;
820
+ let scrollToIndex = 0;
821
+ while (!picked) {
822
+ // Build choices with a stable value object per toolkit
823
+ const choices = toolkits.map((tk, idx) => ({
824
+ name: `${tk.name} ${chalk.gray(`(${tk.slug})`)}${tk.toolsCount ? chalk.gray(` — ${tk.toolsCount} tools`) : ""}`,
825
+ value: { type: "toolkit", toolkit: tk, idx },
826
+ }));
827
+ if (nextCursor) {
828
+ choices.push(new inquirer.Separator());
829
+ choices.push({
830
+ name: chalk.cyan("↓ Load more toolkits..."),
831
+ value: { type: "load_more" },
832
+ });
833
+ }
834
+ choices.push(new inquirer.Separator());
835
+ choices.push({
836
+ name: chalk.yellow("⌕ Search again..."),
837
+ value: { type: "search_again" },
838
+ });
839
+ // Set default to scroll near newly loaded items
840
+ const defaultValue = scrollToIndex > 0 && scrollToIndex < choices.length
841
+ ? choices[scrollToIndex]?.value
842
+ : undefined;
843
+ const { chosen } = await inquirer.prompt([
844
+ {
845
+ type: "list",
846
+ name: "chosen",
847
+ message: `Select a toolkit to connect (${toolkits.length} loaded):`,
848
+ choices,
849
+ default: defaultValue,
850
+ pageSize: 20,
851
+ },
852
+ ]);
853
+ if (chosen.type === "load_more" && nextCursor) {
854
+ const previousCount = toolkits.length;
855
+ const moreSpinner = startSpinner("Loading more toolkits...");
856
+ try {
857
+ const more = await listComposioToolkits(apiKey, {
858
+ limit: 50,
859
+ cursor: nextCursor,
860
+ search: searchTerm,
861
+ });
862
+ toolkits.push(...more.items);
863
+ nextCursor = more.nextCursor;
864
+ moreSpinner.succeed(`Loaded ${more.items.length} more (${toolkits.length} total)`);
865
+ scrollToIndex = previousCount;
866
+ }
867
+ catch {
868
+ moreSpinner.fail("Failed to load more");
869
+ nextCursor = undefined;
870
+ scrollToIndex = 0;
871
+ }
872
+ }
873
+ else if (chosen.type === "search_again") {
874
+ const { newSearch } = await inquirer.prompt([
875
+ {
876
+ type: "input",
877
+ name: "newSearch",
878
+ message: "Search toolkits:",
879
+ },
880
+ ]);
881
+ const q = newSearch?.trim() || undefined;
882
+ searchTerm = q;
883
+ const searchSpinner = startSpinner(q ? `Searching for "${q}"...` : "Fetching toolkits...");
884
+ try {
885
+ const result = await listComposioToolkits(apiKey, { limit: 50, search: q });
886
+ toolkits = result.items;
887
+ nextCursor = result.nextCursor;
888
+ searchSpinner.succeed(`Found ${toolkits.length} toolkits`);
889
+ scrollToIndex = 0;
890
+ if (toolkits.length === 0) {
891
+ log(_jsx(StatusLine, { kind: "warning", label: q ? `No toolkits match "${q}". Try again.` : "No toolkits found." }));
892
+ }
893
+ }
894
+ catch {
895
+ searchSpinner.fail("Search failed");
896
+ scrollToIndex = 0;
897
+ }
898
+ }
899
+ else {
900
+ selectedToolkit = chosen.toolkit;
901
+ toolkitSlug = selectedToolkit.slug;
902
+ picked = true;
903
+ }
904
+ }
905
+ log(_jsx(StatusLine, { kind: "success", label: `Toolkit: ${selectedToolkit.name} (${toolkitSlug})` }));
906
+ }
907
+ // ── Integration Name ──
908
+ let integrationName = options.name || "";
909
+ let description = options.description || "";
910
+ if (!integrationName) {
911
+ if (isJsonMode()) {
912
+ outputJsonError({ code: "VALIDATION_ERROR", message: "Missing required option: --name" });
913
+ resetComposioClient();
914
+ return;
915
+ }
916
+ const defaultName = selectedToolkit
917
+ ? `${selectedToolkit.name} MCP`
918
+ : `${toolkitSlug} MCP`;
919
+ const answers = await inquirer.prompt([
920
+ {
921
+ type: "input",
922
+ name: "name",
923
+ message: "Integration name:",
924
+ default: defaultName,
925
+ validate: (input) => {
926
+ const trimmed = input.trim();
927
+ if (!trimmed)
928
+ return "Name is required";
929
+ if (trimmed.length < 2)
930
+ return "Name must be at least 2 characters";
931
+ if (trimmed.length > 100)
932
+ return "Name must be under 100 characters";
933
+ return true;
934
+ },
935
+ },
936
+ {
937
+ type: "input",
938
+ name: "description",
939
+ message: "Description (optional):",
940
+ },
941
+ ]);
942
+ integrationName = answers.name.trim();
943
+ description = answers.description?.trim() || "";
944
+ }
945
+ // ── Step 5: Create Composio Session ──
946
+ const userId = session.user_id || session.email || `aui_${scope.accountId}`;
947
+ if (isJsonMode()) {
948
+ stderrLog("Creating session...");
949
+ }
950
+ const sessionSpinner = isJsonMode()
951
+ ? null
952
+ : startSpinner(`Creating session for ${toolkitSlug}...`);
953
+ let composioSession;
954
+ try {
955
+ composioSession = await createComposioSession(apiKey, userId, toolkitSlug);
956
+ sessionSpinner?.succeed("Session created");
957
+ }
958
+ catch (error) {
959
+ sessionSpinner?.fail("Failed to create session");
960
+ if (isJsonMode()) {
961
+ outputJsonError({
962
+ code: "API_CLIENT_ERROR",
963
+ message: error instanceof Error ? error.message : String(error),
964
+ });
965
+ }
966
+ else {
967
+ log(_jsx(ErrorDisplay, { error: error, message: "Failed to create session." }));
968
+ }
969
+ resetComposioClient();
970
+ return;
971
+ }
972
+ // ── Step 6: OAuth Authentication ──
973
+ if (isJsonMode()) {
974
+ stderrLog("Starting authentication...");
975
+ }
976
+ const authSpinner = isJsonMode()
977
+ ? null
978
+ : startSpinner("Preparing authentication...");
979
+ let authResult;
980
+ try {
981
+ authResult = await authorizeComposioToolkit(composioSession, toolkitSlug);
982
+ authSpinner?.succeed("Authentication link ready");
983
+ }
984
+ catch (error) {
985
+ authSpinner?.fail("Failed to create auth link");
986
+ if (isJsonMode()) {
987
+ outputJsonError({
988
+ code: "API_CLIENT_ERROR",
989
+ message: error instanceof Error ? error.message : String(error),
990
+ });
991
+ }
992
+ else {
993
+ log(_jsx(ErrorDisplay, { error: error, message: "Failed to start authentication." }));
994
+ }
995
+ resetComposioClient();
996
+ return;
997
+ }
998
+ if (!authResult.redirectUrl) {
999
+ if (isJsonMode()) {
1000
+ outputJsonError({ code: "API_CLIENT_ERROR", message: "No redirect URL received for authentication." });
1001
+ }
1002
+ else {
1003
+ log(_jsx(StatusLine, { kind: "warning", label: "No authentication required for this toolkit." }));
1004
+ }
1005
+ }
1006
+ if (authResult.redirectUrl) {
1007
+ if (isJsonMode()) {
1008
+ stderrLog(`Auth URL: ${authResult.redirectUrl}`);
1009
+ }
1010
+ else {
1011
+ log(_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsxs(Text, { color: "cyan", children: [" ", "Opening browser for authentication..."] }), _jsxs(Text, { color: "gray", children: [" ", "If the browser doesn't open, visit:", "\n", " ", _jsx(Text, { color: "cyan", underline: true, children: authResult.redirectUrl })] })] }));
1012
+ }
1013
+ try {
1014
+ await open(authResult.redirectUrl);
1015
+ }
1016
+ catch {
1017
+ // Browser open failed — user can use the URL manually
1018
+ }
1019
+ const waitSpinner = isJsonMode()
1020
+ ? null
1021
+ : startSpinner("Waiting for authentication (up to 2 minutes)...");
1022
+ try {
1023
+ const connected = await waitForComposioConnection(authResult, 120000);
1024
+ waitSpinner?.succeed(`Authenticated — account ${connected.id}`);
1025
+ }
1026
+ catch (error) {
1027
+ waitSpinner?.fail("Authentication timed out or failed");
1028
+ if (isJsonMode()) {
1029
+ outputJsonError({
1030
+ code: "AUTH_TIMEOUT",
1031
+ message: "Authentication did not complete within the timeout period.",
1032
+ });
1033
+ }
1034
+ else {
1035
+ log(_jsx(ErrorDisplay, { message: "Authentication timed out.", suggestion: "Try again \u2014 make sure to complete the auth flow in the browser within 2 minutes." }));
1036
+ }
1037
+ resetComposioClient();
1038
+ return;
1039
+ }
1040
+ }
1041
+ // ── Step 7: Discover Tools via Composio API ──
1042
+ if (isJsonMode()) {
1043
+ stderrLog("Discovering tools...");
1044
+ }
1045
+ const discoverSpinner = isJsonMode()
1046
+ ? null
1047
+ : startSpinner(`Discovering tools for ${toolkitSlug}...`);
1048
+ let discoveredTools;
1049
+ try {
1050
+ const result = await discoverComposioTools(apiKey, toolkitSlug);
1051
+ discoveredTools = result.tools;
1052
+ if (discoveredTools.length === 0) {
1053
+ if (isJsonMode()) {
1054
+ outputJsonError({ code: "API_CLIENT_ERROR", message: `No tools found for toolkit: ${toolkitSlug}` });
1055
+ }
1056
+ discoverSpinner?.fail("No tools found");
1057
+ resetComposioClient();
1058
+ return;
1059
+ }
1060
+ const totalLabel = result.totalItems
1061
+ ? `${discoveredTools.length} of ${result.totalItems}`
1062
+ : `${discoveredTools.length}`;
1063
+ discoverSpinner?.succeed(`Discovered ${totalLabel} tools for ${toolkitSlug}`);
1064
+ }
1065
+ catch (error) {
1066
+ discoverSpinner?.fail("Discovery failed");
1067
+ if (isJsonMode()) {
1068
+ outputJsonError({
1069
+ code: "API_CLIENT_ERROR",
1070
+ message: `Discovery failed: ${error instanceof Error ? error.message : String(error)}`,
1071
+ });
1072
+ }
1073
+ else {
1074
+ log(_jsx(ErrorDisplay, { error: error, message: "Failed to discover tools." }));
1075
+ }
1076
+ resetComposioClient();
1077
+ return;
1078
+ }
1079
+ // ── Step 8: Select Tools ──
1080
+ let selectedToolNames = [];
1081
+ if (options.tools) {
1082
+ const requestedTools = options.tools.split(",").map((t) => t.trim()).filter(Boolean);
1083
+ const availableNames = new Set(discoveredTools.map((t) => t.name));
1084
+ const missing = requestedTools.filter((t) => !availableNames.has(t));
1085
+ if (missing.length > 0) {
1086
+ const msg = `Tools not found: ${missing.join(", ")}`;
1087
+ if (isJsonMode()) {
1088
+ outputJsonError({ code: "VALIDATION_ERROR", message: msg });
1089
+ }
1090
+ else {
1091
+ log(_jsx(StatusLine, { kind: "error", label: msg }));
1092
+ }
1093
+ resetComposioClient();
1094
+ return;
1095
+ }
1096
+ selectedToolNames = requestedTools;
1097
+ }
1098
+ else if (options.allTools) {
1099
+ selectedToolNames = discoveredTools.map((t) => t.name);
1100
+ }
1101
+ else {
1102
+ if (isJsonMode()) {
1103
+ outputJsonError({
1104
+ code: "VALIDATION_ERROR",
1105
+ message: "Missing required option: --tools or --all-tools",
1106
+ suggestion: `Available tools: ${discoveredTools.map((t) => t.name).join(", ")}`,
1107
+ });
1108
+ resetComposioClient();
1109
+ return;
1110
+ }
1111
+ // Offer search before selecting if many tools
1112
+ let toolsToShow = discoveredTools;
1113
+ if (discoveredTools.length > 15) {
1114
+ const { filterAction } = await inquirer.prompt([
1115
+ {
1116
+ type: "list",
1117
+ name: "filterAction",
1118
+ message: `${discoveredTools.length} tools available. How would you like to proceed?`,
1119
+ choices: [
1120
+ { name: "Select all tools", value: "all" },
1121
+ { name: "Browse and pick tools", value: "browse" },
1122
+ { name: "Search tools first", value: "search" },
1123
+ ],
1124
+ },
1125
+ ]);
1126
+ if (filterAction === "all") {
1127
+ selectedToolNames = discoveredTools.map((t) => t.name);
1128
+ }
1129
+ else if (filterAction === "search") {
1130
+ const { toolSearch } = await inquirer.prompt([
1131
+ {
1132
+ type: "input",
1133
+ name: "toolSearch",
1134
+ message: "Search tools by name or description:",
1135
+ validate: (input) => input.trim().length > 0 || "Enter a search term",
1136
+ },
1137
+ ]);
1138
+ const searchSpinner = startSpinner(`Searching tools for "${toolSearch.trim()}"...`);
1139
+ try {
1140
+ const searchResult = await discoverComposioTools(apiKey, toolkitSlug, {
1141
+ query: toolSearch.trim(),
1142
+ });
1143
+ toolsToShow = searchResult.tools;
1144
+ searchSpinner.succeed(`Found ${toolsToShow.length} matching tools`);
1145
+ }
1146
+ catch {
1147
+ searchSpinner.fail("Search failed, showing all tools");
1148
+ }
1149
+ if (toolsToShow.length === 0) {
1150
+ log(_jsx(StatusLine, { kind: "warning", label: "No tools match your search. Showing all tools." }));
1151
+ toolsToShow = discoveredTools;
1152
+ }
1153
+ }
1154
+ // filterAction === "browse" falls through to the checkbox below
1155
+ }
1156
+ if (selectedToolNames.length === 0) {
1157
+ const { selected } = await inquirer.prompt([
1158
+ {
1159
+ type: "checkbox",
1160
+ name: "selected",
1161
+ message: "Select tools to include:",
1162
+ choices: toolsToShow.map((tool) => ({
1163
+ name: `${tool.name} ${chalk.gray(`— ${tool.description || "No description"}`)}`,
1164
+ value: tool.name,
1165
+ checked: true,
1166
+ })),
1167
+ pageSize: 20,
1168
+ validate: (input) => input.length > 0 || "Select at least one tool",
1169
+ },
1170
+ ]);
1171
+ selectedToolNames = selected;
1172
+ }
1173
+ }
1174
+ // ── Step 9: Confirm ──
1175
+ if (!isNonInteractive && !isJsonMode()) {
1176
+ console.log("");
1177
+ log(_jsx(StatusLine, { kind: "info", label: `Name: ${integrationName}` }));
1178
+ log(_jsx(StatusLine, { kind: "info", label: `Type: MCP (Native — ${toolkitSlug})` }));
1179
+ log(_jsx(StatusLine, { kind: "info", label: `Agent: ${target.agentName || scope.networkId}` }));
1180
+ log(_jsx(StatusLine, { kind: "info", label: `Tools: ${selectedToolNames.length} selected` }));
1181
+ console.log(chalk.gray(selectedToolNames.map((t) => ` • ${t}`).join("\n")));
1182
+ console.log("");
1183
+ const { confirm } = await inquirer.prompt([
1184
+ {
1185
+ type: "confirm",
1186
+ name: "confirm",
1187
+ message: "Create this integration?",
1188
+ default: true,
1189
+ },
1190
+ ]);
1191
+ if (!confirm) {
1192
+ log(_jsx(StatusLine, { kind: "muted", label: "Cancelled." }));
1193
+ resetComposioClient();
1194
+ return;
1195
+ }
1196
+ }
1197
+ // ── Step 10: Save Integration ──
1198
+ if (isJsonMode()) {
1199
+ stderrLog("Creating integration...");
1200
+ }
1201
+ const saveSpinner = isJsonMode()
1202
+ ? null
1203
+ : startSpinner("Creating integration...");
1204
+ const request = {
1205
+ name: integrationName,
1206
+ display_name: integrationName,
1207
+ description,
1208
+ server_url: "",
1209
+ authentication: { type: "none" },
1210
+ tool_names: selectedToolNames,
1211
+ composio: {
1212
+ user_id: scope.networkId,
1213
+ toolkits: [toolkitSlug],
1214
+ tool_names: selectedToolNames,
1215
+ },
1216
+ };
1217
+ try {
1218
+ const result = await saveIntegrationSvc(session, request, scope);
1219
+ if (!result.success) {
1220
+ if (isJsonMode()) {
1221
+ outputJsonError({ code: "API_CLIENT_ERROR", message: result.error || "Unknown error" });
1222
+ }
1223
+ saveSpinner?.fail("Failed to create integration");
1224
+ log(_jsx(ErrorDisplay, { message: result.error || "Unknown error" }));
1225
+ resetComposioClient();
1226
+ return;
1227
+ }
1228
+ if (isJsonMode()) {
1229
+ outputJson({
1230
+ integration: {
1231
+ name: integrationName,
1232
+ type: "MCP",
1233
+ config_method: "native",
1234
+ toolkit: toolkitSlug,
1235
+ tool_names: selectedToolNames,
1236
+ tool_count: selectedToolNames.length,
1237
+ scope: {
1238
+ organization_id: scope.organizationId,
1239
+ account_id: scope.accountId,
1240
+ network_id: scope.networkId,
1241
+ network_category_id: scope.networkCategoryId,
1242
+ version_id: scope.versionId || null,
1243
+ },
1244
+ },
1245
+ response: result.data,
1246
+ });
1247
+ resetComposioClient();
1248
+ return;
1249
+ }
1250
+ saveSpinner?.succeed("Integration created");
1251
+ log(_jsx(NativeIntegrationCreatedView, { name: integrationName, toolkitSlug: toolkitSlug, toolCount: selectedToolNames.length, toolNames: selectedToolNames }));
1252
+ }
1253
+ catch (error) {
1254
+ if (isJsonMode()) {
1255
+ outputJsonError({
1256
+ code: "API_CLIENT_ERROR",
1257
+ message: error instanceof Error ? error.message : String(error),
1258
+ });
1259
+ }
1260
+ saveSpinner?.fail("Failed to create integration");
1261
+ log(_jsx(ErrorDisplay, { error: error, message: "Failed to create integration." }));
1262
+ }
1263
+ resetComposioClient();
1264
+ }
1265
+ //# sourceMappingURL=integration.js.map