lua-cli 2.5.1 → 2.5.3

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 (41) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/CLI_REFERENCE.md +90 -0
  3. package/INSTANCE_TYPES.md +1158 -0
  4. package/README.md +6 -0
  5. package/dist/api/agent.api.service.d.ts +52 -0
  6. package/dist/api/agent.api.service.js +63 -0
  7. package/dist/api/logs.api.service.d.ts +35 -0
  8. package/dist/api/logs.api.service.js +43 -0
  9. package/dist/api/products.api.service.js +2 -0
  10. package/dist/cli/command-definitions.js +17 -1
  11. package/dist/commands/admin.d.ts +23 -0
  12. package/dist/commands/admin.js +62 -0
  13. package/dist/commands/channels.d.ts +17 -0
  14. package/dist/commands/channels.js +676 -0
  15. package/dist/commands/docs.d.ts +19 -0
  16. package/dist/commands/docs.js +30 -0
  17. package/dist/commands/index.d.ts +4 -0
  18. package/dist/commands/index.js +4 -0
  19. package/dist/commands/logs.d.ts +17 -0
  20. package/dist/commands/logs.js +271 -0
  21. package/dist/common/basket.instance.d.ts +4 -1
  22. package/dist/common/basket.instance.js +76 -1
  23. package/dist/common/data.entry.instance.d.ts +4 -1
  24. package/dist/common/data.entry.instance.js +57 -1
  25. package/dist/common/http.client.js +11 -1
  26. package/dist/common/order.instance.d.ts +4 -1
  27. package/dist/common/order.instance.js +76 -1
  28. package/dist/common/product.instance.d.ts +4 -1
  29. package/dist/common/product.instance.js +57 -1
  30. package/dist/common/product.pagination.instance.d.ts +58 -0
  31. package/dist/common/product.pagination.instance.js +78 -0
  32. package/dist/common/product.search.instance.d.ts +58 -0
  33. package/dist/common/product.search.instance.js +78 -0
  34. package/dist/common/user.instance.d.ts +4 -12
  35. package/dist/common/user.instance.js +5 -24
  36. package/dist/index.js +3 -0
  37. package/dist/interfaces/agent.d.ts +218 -0
  38. package/dist/interfaces/logs.d.ts +40 -0
  39. package/dist/interfaces/logs.js +5 -0
  40. package/package.json +2 -1
  41. package/template/package.json +1 -1
@@ -0,0 +1,676 @@
1
+ /**
2
+ * Channels Command
3
+ * Manages agent communication channels (list, create)
4
+ */
5
+ import inquirer from "inquirer";
6
+ import open from 'open';
7
+ import { loadApiKey } from "../services/auth.js";
8
+ import { readSkillConfig } from "../utils/files.js";
9
+ import { withErrorHandling, writeSuccess, writeInfo, clearPromptLines, writeProgress } from "../utils/cli.js";
10
+ import AgentApi from '../api/agent.api.service.js';
11
+ import { BASE_URLS } from '../config/constants.js';
12
+ /**
13
+ * Main channels command - manages agent communication channels.
14
+ *
15
+ * This command provides:
16
+ * 1. List all channels for an agent
17
+ * 2. Create new channels (WhatsApp, Facebook, Slack)
18
+ *
19
+ * The agent ID is automatically loaded from lua.skill.yaml
20
+ *
21
+ * @returns Promise that resolves when the operation is complete
22
+ * @throws Error if API key is not found or API call fails
23
+ */
24
+ export async function channelsCommand() {
25
+ return withErrorHandling(async () => {
26
+ // Load API key
27
+ const apiKey = await loadApiKey();
28
+ if (!apiKey) {
29
+ console.error("❌ No API key found. Run `lua auth configure` first.");
30
+ process.exit(1);
31
+ }
32
+ // Load agent ID from configuration
33
+ const config = readSkillConfig();
34
+ const agentId = config?.agent?.agentId;
35
+ if (!agentId) {
36
+ throw new Error("No agent ID found in lua.skill.yaml. Please ensure you are in a Lua skill directory, or run 'lua init' to create a new skill.");
37
+ }
38
+ writeProgress(`✅ Using agent: ${agentId}`);
39
+ const agentApi = new AgentApi(BASE_URLS.API, apiKey);
40
+ // Step 1: Ask what operation they want to perform
41
+ const { operation } = await inquirer.prompt([
42
+ {
43
+ type: "list",
44
+ name: "operation",
45
+ message: "What would you like to do?",
46
+ choices: [
47
+ { name: "📋 List channels", value: "list" },
48
+ { name: "🔗 Link new channel", value: "create" },
49
+ { name: "🌐 Link on admin dashboard", value: "admin" }
50
+ ]
51
+ }
52
+ ]);
53
+ clearPromptLines(2);
54
+ if (operation === "list") {
55
+ await listChannels(agentApi, agentId, config);
56
+ }
57
+ else if (operation === "create") {
58
+ await createChannel(agentApi, agentId);
59
+ }
60
+ else if (operation === "admin") {
61
+ await openAdminDashboard(apiKey, config);
62
+ }
63
+ }, "channels management");
64
+ }
65
+ /**
66
+ * Opens the admin dashboard for channel management
67
+ */
68
+ async function openAdminDashboard(apiKey, config) {
69
+ writeProgress('🌐 Opening Lua Admin Dashboard...');
70
+ // Extract agentId and orgId from config
71
+ const agentId = config?.agent?.agentId;
72
+ const orgId = config?.agent?.orgId;
73
+ if (!agentId) {
74
+ throw new Error('No agentId found in lua.skill.yaml. Please ensure your configuration is valid.');
75
+ }
76
+ if (!orgId) {
77
+ throw new Error('No orgId found in lua.skill.yaml. Please ensure your configuration is valid.');
78
+ }
79
+ // Construct the admin dashboard URL
80
+ const adminUrl = `https://admin.heylua.ai/validate-token/${apiKey}?redirect=/admin/usage?&agentId=${agentId}&orgId=${orgId}&hideToolBar=true`;
81
+ // Open the URL in the default browser
82
+ await open(adminUrl);
83
+ writeSuccess('✅ Lua Admin Dashboard opened in your browser');
84
+ console.log(`\n Dashboard URL: https://admin.heylua.ai`);
85
+ console.log(` Agent ID: ${agentId}`);
86
+ console.log(` Organization ID: ${orgId}\n`);
87
+ }
88
+ /**
89
+ * Lists all channels for a specific agent
90
+ */
91
+ async function listChannels(agentApi, agentId, config) {
92
+ writeInfo(`📡 Fetching channels for agent: ${agentId}...`);
93
+ const response = await agentApi.getAgentChannels(agentId);
94
+ if (!response.success) {
95
+ console.error(`❌ Error: ${response.error?.message || 'Unknown error'}`);
96
+ process.exit(1);
97
+ }
98
+ const data = response.data;
99
+ writeSuccess(`✅ Found ${data.channels.length} channel(s)\n`);
100
+ if (data.channels.length === 0) {
101
+ writeInfo("No channels configured for this agent.");
102
+ writeInfo("\n💡 Use the 'Create new channel' option to add channels.\n");
103
+ return;
104
+ }
105
+ // Create channel choices with icons and labels
106
+ const channelChoices = data.channels.map((channel, index) => {
107
+ const icon = getChannelIcon(channel.type);
108
+ const label = getChannelLabel(channel);
109
+ return {
110
+ name: `${icon} ${channel.type.toUpperCase()} - ${label}`,
111
+ value: index
112
+ };
113
+ });
114
+ channelChoices.push({
115
+ name: '← Back to main menu',
116
+ value: -1
117
+ });
118
+ const { selectedIndex } = await inquirer.prompt([
119
+ {
120
+ type: "list",
121
+ name: "selectedIndex",
122
+ message: "Select a channel to view details:",
123
+ choices: channelChoices
124
+ }
125
+ ]);
126
+ if (selectedIndex === -1) {
127
+ return;
128
+ }
129
+ clearPromptLines(2);
130
+ // Display selected channel details
131
+ const selectedChannel = data.channels[selectedIndex];
132
+ displayChannelDetails(selectedChannel);
133
+ }
134
+ /**
135
+ * Gets the emoji icon for a channel type
136
+ */
137
+ function getChannelIcon(type) {
138
+ const icons = {
139
+ 'whatsapp': '📱',
140
+ 'facebook': '💬',
141
+ 'instagram': '📸',
142
+ 'slack': '💼',
143
+ 'email': '📧',
144
+ 'webchat': '💻'
145
+ };
146
+ return icons[type] || '📡';
147
+ }
148
+ /**
149
+ * Gets a human-readable label for a channel
150
+ */
151
+ function getChannelLabel(channel) {
152
+ switch (channel.type) {
153
+ case 'whatsapp':
154
+ return channel.whatsapp || channel.identifier;
155
+ case 'facebook':
156
+ return channel.facebook || channel.identifier;
157
+ case 'instagram':
158
+ return channel.instagram || channel.identifier;
159
+ case 'slack':
160
+ return channel.data?.botName || channel.data?.url || channel.identifier;
161
+ case 'email':
162
+ return channel.identifier;
163
+ default:
164
+ return channel.identifier;
165
+ }
166
+ }
167
+ /**
168
+ * Displays detailed information about a channel
169
+ */
170
+ function displayChannelDetails(channel) {
171
+ const icon = getChannelIcon(channel.type);
172
+ writeSuccess(`\n${icon} ${channel.type.toUpperCase()} Channel Details\n`);
173
+ writeInfo(`${'─'.repeat(50)}`);
174
+ writeInfo(`Type: ${channel.type}`);
175
+ writeInfo(`Identifier: ${channel.identifier}`);
176
+ writeInfo(`Created: ${new Date(channel.createdAt).toLocaleString()}`);
177
+ // Type-specific details
178
+ switch (channel.type) {
179
+ case 'whatsapp':
180
+ displayWhatsAppDetails(channel);
181
+ break;
182
+ case 'facebook':
183
+ displayFacebookDetails(channel);
184
+ break;
185
+ case 'instagram':
186
+ displayInstagramDetails(channel);
187
+ break;
188
+ case 'slack':
189
+ displaySlackDetails(channel);
190
+ break;
191
+ case 'email':
192
+ displayEmailDetails(channel);
193
+ break;
194
+ default:
195
+ writeInfo(`\nRaw data:`);
196
+ console.log(JSON.stringify(channel, null, 2));
197
+ }
198
+ writeInfo(`${'─'.repeat(50)}\n`);
199
+ }
200
+ /**
201
+ * Displays WhatsApp-specific details
202
+ */
203
+ function displayWhatsAppDetails(channel) {
204
+ if (channel.whatsapp) {
205
+ writeInfo(`Phone Number: ${channel.whatsapp}`);
206
+ }
207
+ if (channel.data) {
208
+ if (channel.data.number) {
209
+ writeInfo(`Number: ${channel.data.number}`);
210
+ }
211
+ if (channel.data.phoneId) {
212
+ writeInfo(`Phone ID: ${channel.data.phoneId}`);
213
+ }
214
+ if (channel.data.global !== undefined) {
215
+ writeInfo(`Global: ${channel.data.global ? 'Yes' : 'No'}`);
216
+ }
217
+ if (channel.data.country) {
218
+ writeInfo(`Country: ${channel.data.country}`);
219
+ }
220
+ if (channel.data.metadata) {
221
+ const meta = channel.data.metadata;
222
+ writeInfo(`\nMetadata:`);
223
+ if (meta.verified_name) {
224
+ writeInfo(` Verified Name: ${meta.verified_name}`);
225
+ }
226
+ if (meta.status) {
227
+ writeInfo(` Status: ${meta.status}`);
228
+ }
229
+ if (meta.quality_rating) {
230
+ writeInfo(` Quality: ${meta.quality_rating}`);
231
+ }
232
+ if (meta.wabaId) {
233
+ writeInfo(` WABA ID: ${meta.wabaId}`);
234
+ }
235
+ }
236
+ }
237
+ }
238
+ /**
239
+ * Displays Facebook-specific details
240
+ */
241
+ function displayFacebookDetails(channel) {
242
+ if (channel.facebook) {
243
+ writeInfo(`Page Name: ${channel.facebook}`);
244
+ }
245
+ if (channel.data) {
246
+ if (channel.data.name) {
247
+ writeInfo(`Name: ${channel.data.name}`);
248
+ }
249
+ if (channel.data.pageId) {
250
+ writeInfo(`Page ID: ${channel.data.pageId}`);
251
+ }
252
+ if (channel.data.category) {
253
+ writeInfo(`Category: ${channel.data.category}`);
254
+ }
255
+ }
256
+ }
257
+ /**
258
+ * Displays Instagram-specific details
259
+ */
260
+ function displayInstagramDetails(channel) {
261
+ if (channel.instagram) {
262
+ writeInfo(`Username: ${channel.instagram}`);
263
+ }
264
+ if (channel.data) {
265
+ if (channel.data.username) {
266
+ writeInfo(`Username: ${channel.data.username}`);
267
+ }
268
+ if (channel.data.account_type) {
269
+ writeInfo(`Account Type: ${channel.data.account_type}`);
270
+ }
271
+ if (channel.data.account_id) {
272
+ writeInfo(`Account ID: ${channel.data.account_id}`);
273
+ }
274
+ }
275
+ }
276
+ /**
277
+ * Displays Slack-specific details
278
+ */
279
+ function displaySlackDetails(channel) {
280
+ if (channel.data) {
281
+ if (channel.data.type) {
282
+ writeInfo(`Type: ${channel.data.type}`);
283
+ }
284
+ if (channel.data.botName) {
285
+ writeInfo(`Bot Name: ${channel.data.botName}`);
286
+ }
287
+ if (channel.data.url) {
288
+ writeInfo(`Workspace: ${channel.data.url}`);
289
+ }
290
+ if (channel.data.teamId) {
291
+ writeInfo(`Team ID: ${channel.data.teamId}`);
292
+ }
293
+ if (channel.data.appId) {
294
+ writeInfo(`App ID: ${channel.data.appId}`);
295
+ }
296
+ if (channel.data.botId) {
297
+ writeInfo(`Bot ID: ${channel.data.botId}`);
298
+ }
299
+ }
300
+ }
301
+ /**
302
+ * Displays Email-specific details
303
+ */
304
+ function displayEmailDetails(channel) {
305
+ if (channel.data) {
306
+ if (channel.data.from) {
307
+ writeInfo(`From Address: ${channel.data.from}`);
308
+ }
309
+ if (channel.data.name) {
310
+ writeInfo(`Name: ${channel.data.name}`);
311
+ }
312
+ if (channel.data.templateId) {
313
+ writeInfo(`Template ID: ${channel.data.templateId}`);
314
+ }
315
+ if (channel.data.active !== undefined) {
316
+ writeInfo(`Active: ${channel.data.active ? 'Yes' : 'No'}`);
317
+ }
318
+ }
319
+ }
320
+ /**
321
+ * Creates a new channel for a specific agent
322
+ */
323
+ async function createChannel(agentApi, agentId) {
324
+ // Ask for channel type
325
+ const { channelType } = await inquirer.prompt([
326
+ {
327
+ type: "list",
328
+ name: "channelType",
329
+ message: "Select channel type:",
330
+ choices: [
331
+ { name: "📱 WhatsApp", value: "whatsapp" },
332
+ { name: "💬 Facebook Messenger", value: "facebook" },
333
+ { name: "📧 Email", value: "email" },
334
+ { name: "🔒 Slack (Private)", value: "slack-private" },
335
+ { name: "🌐 Slack (Public)", value: "slack-public" }
336
+ ]
337
+ }
338
+ ]);
339
+ clearPromptLines(2);
340
+ // Create channel based on type
341
+ switch (channelType) {
342
+ case "whatsapp":
343
+ await createWhatsAppChannel(agentApi, agentId);
344
+ break;
345
+ case "facebook":
346
+ await createFacebookChannel(agentApi, agentId);
347
+ break;
348
+ case "email":
349
+ await createEmailChannel(agentApi, agentId);
350
+ break;
351
+ case "slack-private":
352
+ await createSlackPrivateChannel(agentApi, agentId);
353
+ break;
354
+ case "slack-public":
355
+ await createSlackPublicChannel(agentApi, agentId);
356
+ break;
357
+ }
358
+ }
359
+ /**
360
+ * Creates a WhatsApp channel
361
+ */
362
+ async function createWhatsAppChannel(agentApi, agentId) {
363
+ writeInfo("📱 Creating WhatsApp Channel\n");
364
+ const answers = await inquirer.prompt([
365
+ {
366
+ type: "input",
367
+ name: "phoneNumberId",
368
+ message: "Enter phone number ID:",
369
+ validate: (input) => {
370
+ return input.trim().length > 0 || "Phone number ID is required";
371
+ }
372
+ },
373
+ {
374
+ type: "input",
375
+ name: "wabaId",
376
+ message: "Enter WhatsApp Business Account ID (WABA ID):",
377
+ validate: (input) => {
378
+ return input.trim().length > 0 || "WABA ID is required";
379
+ }
380
+ },
381
+ {
382
+ type: "password",
383
+ name: "accessToken",
384
+ message: "Enter access token:",
385
+ mask: "*",
386
+ validate: (input) => {
387
+ return input.trim().length > 0 || "Access token is required";
388
+ }
389
+ }
390
+ ]);
391
+ const channelData = {
392
+ type: "whatsapp",
393
+ whatsapp: {
394
+ phoneNumberId: answers.phoneNumberId.trim(),
395
+ wabaId: answers.wabaId.trim(),
396
+ accessToken: answers.accessToken.trim()
397
+ },
398
+ isManual: true
399
+ };
400
+ writeInfo("\n📡 Creating WhatsApp channel...");
401
+ const response = await agentApi.createWhatsAppChannel(agentId, channelData);
402
+ if (!response.success) {
403
+ const error = response.error;
404
+ if (error?.statusCode === 400 && error?.message?.includes('already exists')) {
405
+ console.error(`\n❌ Channel already exists`);
406
+ console.error(`💡 This WhatsApp number is already connected to this agent.`);
407
+ console.error(` Use 'lua channels' to list existing channels.\n`);
408
+ }
409
+ else {
410
+ console.error(`\n❌ Error: ${error?.message || 'Unknown error'}`);
411
+ if (error?.error) {
412
+ console.error(` ${error.error}\n`);
413
+ }
414
+ }
415
+ process.exit(1);
416
+ }
417
+ const data = response.data;
418
+ writeSuccess("\n✅ WhatsApp channel created successfully!\n");
419
+ writeInfo(`📞 Phone Number: ${data.whatsapp.number}`);
420
+ writeInfo(`🆔 Identifier: ${data.identifier}`);
421
+ writeInfo(`✅ Status: ${data.whatsapp.metadata.status}`);
422
+ writeInfo(`🔗 Webhook URL: ${data.webhookUrl}\n`);
423
+ }
424
+ /**
425
+ * Creates a Facebook Messenger channel
426
+ */
427
+ async function createFacebookChannel(agentApi, agentId) {
428
+ writeInfo("💬 Creating Facebook Messenger Channel\n");
429
+ const answers = await inquirer.prompt([
430
+ {
431
+ type: "password",
432
+ name: "accessToken",
433
+ message: "Enter Facebook page access token:",
434
+ mask: "*",
435
+ validate: (input) => {
436
+ return input.trim().length > 0 || "Access token is required";
437
+ }
438
+ },
439
+ {
440
+ type: "input",
441
+ name: "pageId",
442
+ message: "Enter Facebook page ID:",
443
+ validate: (input) => {
444
+ return input.trim().length > 0 || "Page ID is required";
445
+ }
446
+ }
447
+ ]);
448
+ const channelData = {
449
+ type: "facebook",
450
+ facebook: {
451
+ accessToken: answers.accessToken.trim(),
452
+ pageId: answers.pageId.trim()
453
+ }
454
+ };
455
+ writeInfo("\n📡 Creating Facebook channel...");
456
+ const response = await agentApi.createFacebookChannel(agentId, channelData);
457
+ if (!response.success) {
458
+ const error = response.error;
459
+ if (error?.statusCode === 400 && error?.message?.includes('already exists')) {
460
+ console.error(`\n❌ Channel already exists`);
461
+ console.error(`💡 This Facebook page is already connected to this agent.`);
462
+ console.error(` Use 'lua channels' to list existing channels.\n`);
463
+ }
464
+ else {
465
+ console.error(`\n❌ Error: ${error?.message || 'Unknown error'}`);
466
+ if (error?.error) {
467
+ console.error(` ${error.error}\n`);
468
+ }
469
+ }
470
+ process.exit(1);
471
+ }
472
+ const data = response.data;
473
+ writeSuccess("\n✅ Facebook channel created successfully!\n");
474
+ writeInfo(`📄 Page Name: ${data.facebook.name}`);
475
+ writeInfo(`🆔 Identifier: ${data.identifier}`);
476
+ writeInfo(`📋 Category: ${data.facebook.category}`);
477
+ writeInfo(`🔗 Webhook URL: ${data.webhookUrl}\n`);
478
+ }
479
+ /**
480
+ * Creates an Email channel
481
+ */
482
+ async function createEmailChannel(agentApi, agentId) {
483
+ writeInfo("📧 Creating Email Channel\n");
484
+ const answers = await inquirer.prompt([
485
+ {
486
+ type: "input",
487
+ name: "name",
488
+ message: "Enter sender name (shown in email header):",
489
+ validate: (input) => {
490
+ return input.trim().length > 0 || "Sender name is required";
491
+ }
492
+ },
493
+ {
494
+ type: "input",
495
+ name: "email",
496
+ message: "Enter sender email address:",
497
+ validate: (input) => {
498
+ if (input.trim().length === 0) {
499
+ return "Email address is required";
500
+ }
501
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
502
+ if (!emailRegex.test(input.trim())) {
503
+ return "Please enter a valid email address";
504
+ }
505
+ return true;
506
+ }
507
+ }
508
+ ]);
509
+ const channelData = {
510
+ type: "email",
511
+ email: {
512
+ name: answers.name.trim(),
513
+ email: answers.email.trim()
514
+ }
515
+ };
516
+ writeInfo("\n📡 Creating Email channel...");
517
+ const response = await agentApi.createEmailChannel(agentId, channelData);
518
+ if (!response.success) {
519
+ const error = response.error;
520
+ if (error?.statusCode === 400 && error?.message?.includes('already exists')) {
521
+ console.error(`\n❌ Channel already exists`);
522
+ console.error(`💡 This email address is already connected to this agent.`);
523
+ console.error(` Use 'lua channels' to list existing channels.\n`);
524
+ }
525
+ else {
526
+ console.error(`\n❌ Error: ${error?.message || 'Unknown error'}`);
527
+ if (error?.error) {
528
+ console.error(` ${error.error}\n`);
529
+ }
530
+ }
531
+ process.exit(1);
532
+ }
533
+ const data = response.data;
534
+ writeSuccess("\n✅ Email channel created successfully!\n");
535
+ writeInfo(`📧 Sender Name: ${answers.name}`);
536
+ writeInfo(`📧 Sender Email: ${answers.email}`);
537
+ writeInfo(`📬 Forward To: ${data.forwardTo}\n`);
538
+ writeInfo(`${'─'.repeat(60)}`);
539
+ writeInfo(`⚠️ IMPORTANT: Email Forwarding Setup Required`);
540
+ writeInfo(`${'─'.repeat(60)}`);
541
+ writeInfo(`\nTo complete the email channel setup, configure email forwarding:`);
542
+ writeInfo(`\n1. Log into your email provider's settings`);
543
+ writeInfo(`2. Set up email forwarding or filtering`);
544
+ writeInfo(`3. Forward all emails from ${answers.email} to:`);
545
+ writeInfo(` ${data.forwardTo}`);
546
+ writeInfo(`\n4. Test by sending an email to ${answers.email}`);
547
+ writeInfo(` Your agent will respond automatically!\n`);
548
+ writeInfo(`${'─'.repeat(60)}\n`);
549
+ }
550
+ /**
551
+ * Creates a private Slack channel
552
+ */
553
+ async function createSlackPrivateChannel(agentApi, agentId) {
554
+ writeInfo("🔒 Creating Private Slack Channel\n");
555
+ const answers = await inquirer.prompt([
556
+ {
557
+ type: "password",
558
+ name: "accessToken",
559
+ message: "Enter Slack bot access token (xoxb-...):",
560
+ mask: "*",
561
+ validate: (input) => {
562
+ if (input.trim().length === 0) {
563
+ return "Access token is required";
564
+ }
565
+ if (!input.trim().startsWith("xoxb-")) {
566
+ return "Bot token should start with 'xoxb-'";
567
+ }
568
+ return true;
569
+ }
570
+ }
571
+ ]);
572
+ const channelData = {
573
+ type: "slack",
574
+ slack: {
575
+ type: "private",
576
+ accessToken: answers.accessToken.trim()
577
+ }
578
+ };
579
+ writeInfo("\n📡 Creating Slack private channel...");
580
+ const response = await agentApi.createSlackChannel(agentId, channelData);
581
+ if (!response.success) {
582
+ const error = response.error;
583
+ if (error?.statusCode === 400 && error?.message?.includes('already exists')) {
584
+ console.error(`\n❌ Channel already exists`);
585
+ console.error(`💡 This Slack workspace is already connected to this agent.`);
586
+ console.error(` Use 'lua channels' to list existing channels.\n`);
587
+ }
588
+ else {
589
+ console.error(`\n❌ Error: ${error?.message || 'Unknown error'}`);
590
+ if (error?.error) {
591
+ console.error(` ${error.error}\n`);
592
+ }
593
+ }
594
+ process.exit(1);
595
+ }
596
+ const data = response.data;
597
+ // Type guard to check if it's a private channel response
598
+ if ('slack' in data && 'botName' in data.slack) {
599
+ writeSuccess("\n✅ Slack private channel created successfully!\n");
600
+ writeInfo(`🤖 Bot Name: ${data.slack.botName}`);
601
+ writeInfo(`🏢 Workspace: ${data.slack.url}`);
602
+ writeInfo(`🆔 Bot ID: ${data.slack.botId}`);
603
+ writeInfo(`Ensure your app Manifest is configured correctly. Below is an example:\n`);
604
+ writeInfo(JSON.stringify(data.slack.manifest, null, 2));
605
+ }
606
+ }
607
+ /**
608
+ * Creates a public Slack channel
609
+ */
610
+ async function createSlackPublicChannel(agentApi, agentId) {
611
+ writeInfo("🌐 Creating Public Slack Channel\n");
612
+ const answers = await inquirer.prompt([
613
+ {
614
+ type: "input",
615
+ name: "appId",
616
+ message: "Enter Slack app ID:",
617
+ validate: (input) => {
618
+ return input.trim().length > 0 || "App ID is required";
619
+ }
620
+ },
621
+ {
622
+ type: "input",
623
+ name: "clientId",
624
+ message: "Enter Slack client ID:",
625
+ validate: (input) => {
626
+ return input.trim().length > 0 || "Client ID is required";
627
+ }
628
+ },
629
+ {
630
+ type: "password",
631
+ name: "clientSecret",
632
+ message: "Enter Slack client secret:",
633
+ mask: "*",
634
+ validate: (input) => {
635
+ return input.trim().length > 0 || "Client secret is required";
636
+ }
637
+ }
638
+ ]);
639
+ const channelData = {
640
+ type: "slack",
641
+ slack: {
642
+ type: "public",
643
+ appId: answers.appId.trim(),
644
+ clientId: answers.clientId.trim(),
645
+ clientSecret: answers.clientSecret.trim()
646
+ }
647
+ };
648
+ writeInfo("\n📡 Creating Slack public channel...");
649
+ const response = await agentApi.createSlackChannel(agentId, channelData);
650
+ if (!response.success) {
651
+ const error = response.error;
652
+ if (error?.statusCode === 400 && error?.message?.includes('already exists')) {
653
+ console.error(`\n❌ Channel already exists`);
654
+ console.error(`💡 This Slack app is already connected to this agent.`);
655
+ console.error(` Use 'lua channels' to list existing channels.\n`);
656
+ }
657
+ else {
658
+ console.error(`\n❌ Error: ${error?.message || 'Unknown error'}`);
659
+ if (error?.error) {
660
+ console.error(` ${error.error}\n`);
661
+ }
662
+ }
663
+ process.exit(1);
664
+ }
665
+ const data = response.data;
666
+ // Type guard to check if it's a public channel response
667
+ if ('redirectUri' in data) {
668
+ writeSuccess("\n✅ Slack public channel created successfully!\n");
669
+ writeInfo(`🆔 App ID: ${data.appId}`);
670
+ writeInfo(`🔗 Redirect URI: ${data.redirectUri}`);
671
+ writeInfo(`🔗 Webhook URL: ${data.webhookUrl}\n`);
672
+ writeInfo(`🔗 Install URL: https://auth.heylua.ai/slack/install/${data.appId}`);
673
+ writeInfo(`Ensure your app Manifest is configured correctly. Below is an example:\n`);
674
+ writeInfo(JSON.stringify(data.manifest, null, 2));
675
+ }
676
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Docs Command
3
+ * Opens the Lua documentation website in the user's default browser
4
+ */
5
+ /**
6
+ * Docs command - opens the Lua documentation in the browser.
7
+ *
8
+ * This command opens https://docs.heylua.ai in the user's default browser,
9
+ * providing quick access to:
10
+ * - Platform overview and concepts
11
+ * - CLI command reference
12
+ * - API documentation
13
+ * - Tool examples and guides
14
+ * - Integration tutorials
15
+ * - Best practices
16
+ *
17
+ * @returns Promise that resolves when browser is launched
18
+ */
19
+ export declare function docsCommand(): Promise<void>;