lua-cli 3.5.0-alpha.2 → 3.5.0-alpha.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.
- package/dist/api/unifiedto.api.service.d.ts +6 -0
- package/dist/api/unifiedto.api.service.js +8 -0
- package/dist/api/unifiedto.api.service.js.map +1 -1
- package/dist/api/webhook.api.service.js +1 -1
- package/dist/api/webhook.api.service.js.map +1 -1
- package/dist/cli/command-definitions.js +31 -22
- package/dist/cli/command-definitions.js.map +1 -1
- package/dist/commands/integrations.d.ts +2 -2
- package/dist/commands/integrations.js +741 -127
- package/dist/commands/integrations.js.map +1 -1
- package/dist/commands/push.js +204 -215
- package/dist/commands/push.js.map +1 -1
- package/dist/interfaces/unifiedto.d.ts +4 -0
- package/package.json +1 -1
- package/template/package.json +1 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Integrations Command
|
|
3
|
-
* Manages third-party
|
|
3
|
+
* Manages third-party integrations via Unified.to
|
|
4
4
|
*
|
|
5
5
|
* Core concepts:
|
|
6
6
|
* - Integration: A supported third-party service (e.g., Linear, Google Calendar)
|
|
7
|
-
* - Connection: An authenticated link between your agent and a specific
|
|
7
|
+
* - Connection: An authenticated link between your agent and a specific integration
|
|
8
8
|
* - MCP Server: Technical implementation that exposes connection tools to the agent (auto-managed)
|
|
9
9
|
*
|
|
10
10
|
* Flow:
|
|
@@ -25,13 +25,16 @@ import { safePrompt } from '../utils/prompt-handler.js';
|
|
|
25
25
|
import { validateConfig, validateAgentConfig } from '../utils/dev-helpers.js';
|
|
26
26
|
import DeveloperApi from '../api/developer.api.service.js';
|
|
27
27
|
import UnifiedToApi from '../api/unifiedto.api.service.js';
|
|
28
|
-
import { fetchServersCore, activateServerCore, deactivateServerCore } from './mcp.js';
|
|
28
|
+
import { fetchServersCore, activateServerCore, deactivateServerCore, } from './mcp.js';
|
|
29
29
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
30
30
|
// Configuration
|
|
31
31
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
32
32
|
const UNIFIED_MCP_BASE_URL = 'https://mcp-api.unified.to/mcp';
|
|
33
33
|
const CALLBACK_PORT = 19837; // Random high port for OAuth callback
|
|
34
34
|
const CALLBACK_URL = `http://localhost:${CALLBACK_PORT}/callback`;
|
|
35
|
+
// Webhook trigger configuration
|
|
36
|
+
const AGENT_WEBHOOK_URL = `${BASE_URLS.API}/webhook/unifiedto/data`;
|
|
37
|
+
const DEFAULT_VIRTUAL_WEBHOOK_INTERVAL = 60; // minutes
|
|
35
38
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
36
39
|
// Integration API Functions (via Lua API)
|
|
37
40
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -98,7 +101,7 @@ function startCallbackServer(timeoutMs = 300000) {
|
|
|
98
101
|
<body style="font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #1a1a2e;">
|
|
99
102
|
<div style="text-align: center; color: white;">
|
|
100
103
|
<h1 style="color: #4ade80;">✅ Connection Successful!</h1>
|
|
101
|
-
<p style="color: #ccc;">Your
|
|
104
|
+
<p style="color: #ccc;">Your integration has been connected.</p>
|
|
102
105
|
<p style="color: #888;">You can close this window and return to the terminal.</p>
|
|
103
106
|
</div>
|
|
104
107
|
</body>
|
|
@@ -182,7 +185,13 @@ async function executeNonInteractive(context, action, cmdOptions) {
|
|
|
182
185
|
authMethod: cmdOptions?.authMethod,
|
|
183
186
|
scopes: cmdOptions?.scopes,
|
|
184
187
|
hideSensitive: cmdOptions?.hideSensitive !== 'false', // Default true, only false if explicitly set
|
|
188
|
+
// Trigger options
|
|
189
|
+
triggers: cmdOptions?.triggers,
|
|
190
|
+
customWebhook: cmdOptions?.customWebhook === true,
|
|
191
|
+
hookUrl: cmdOptions?.hookUrl,
|
|
185
192
|
};
|
|
193
|
+
// Check for JSON output flag
|
|
194
|
+
const jsonOutput = cmdOptions?.json === true;
|
|
186
195
|
switch (normalizedAction) {
|
|
187
196
|
case 'connect':
|
|
188
197
|
await connectIntegrationFlow(context, options);
|
|
@@ -201,6 +210,18 @@ async function executeNonInteractive(context, action, cmdOptions) {
|
|
|
201
210
|
case 'available':
|
|
202
211
|
await listAvailableIntegrations(context);
|
|
203
212
|
break;
|
|
213
|
+
case 'info':
|
|
214
|
+
// New command: Show detailed info about an integration type
|
|
215
|
+
// Integration type can be passed as positional arg (info <type>) or as --integration flag
|
|
216
|
+
const infoIntegrationType = options.integration || cmdOptions?._?.[0];
|
|
217
|
+
if (!infoIntegrationType) {
|
|
218
|
+
console.error("❌ Integration type is required");
|
|
219
|
+
console.log("\nUsage: lua integrations info <type>");
|
|
220
|
+
console.log(" lua integrations info <type> --json");
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
await showIntegrationInfo(context, infoIntegrationType, jsonOutput);
|
|
224
|
+
break;
|
|
204
225
|
case 'disconnect':
|
|
205
226
|
if (!options.connectionId) {
|
|
206
227
|
console.error("❌ --connection-id is required for disconnect");
|
|
@@ -228,7 +249,7 @@ async function interactiveIntegrationsManagement(context) {
|
|
|
228
249
|
let continueManaging = true;
|
|
229
250
|
while (continueManaging) {
|
|
230
251
|
console.log("\n" + "=".repeat(60));
|
|
231
|
-
console.log("🔗
|
|
252
|
+
console.log("🔗 Integrations");
|
|
232
253
|
console.log("=".repeat(60) + "\n");
|
|
233
254
|
const actionAnswer = await safePrompt([
|
|
234
255
|
{
|
|
@@ -236,13 +257,13 @@ async function interactiveIntegrationsManagement(context) {
|
|
|
236
257
|
name: 'action',
|
|
237
258
|
message: 'What would you like to do?',
|
|
238
259
|
choices: [
|
|
239
|
-
{ name: '➕ Connect a new
|
|
260
|
+
{ name: '➕ Connect a new integration', value: 'connect' },
|
|
240
261
|
{ name: '🔄 Update connection scopes', value: 'update' },
|
|
241
|
-
{ name: '📋 List connected
|
|
262
|
+
{ name: '📋 List connected integrations', value: 'list' },
|
|
242
263
|
{ name: '🔍 View available integrations', value: 'available' },
|
|
243
|
-
{ name: '🔔 Manage
|
|
264
|
+
{ name: '🔔 Manage triggers', value: 'webhooks' },
|
|
244
265
|
{ name: '🔌 Manage MCP servers', value: 'mcp' },
|
|
245
|
-
{ name: '🗑️ Disconnect an
|
|
266
|
+
{ name: '🗑️ Disconnect an integration', value: 'disconnect' },
|
|
246
267
|
{ name: '❌ Exit', value: 'exit' }
|
|
247
268
|
]
|
|
248
269
|
}
|
|
@@ -319,6 +340,130 @@ async function listAvailableIntegrations(context) {
|
|
|
319
340
|
}
|
|
320
341
|
}
|
|
321
342
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
343
|
+
// Integration Info (Non-Interactive Discovery)
|
|
344
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
345
|
+
/**
|
|
346
|
+
* Show detailed information about an integration type
|
|
347
|
+
* Used for non-interactive discovery of OAuth scopes and webhook events
|
|
348
|
+
*/
|
|
349
|
+
async function showIntegrationInfo(context, integrationType, jsonOutput = false) {
|
|
350
|
+
try {
|
|
351
|
+
// Fetch integration details
|
|
352
|
+
const integrations = await fetchAvailableIntegrations(context.unifiedToApi);
|
|
353
|
+
const integration = integrations.find(i => i.value === integrationType);
|
|
354
|
+
if (!integration) {
|
|
355
|
+
if (jsonOutput) {
|
|
356
|
+
console.log(JSON.stringify({ error: `Integration not found: ${integrationType}` }));
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
console.error(`❌ Integration not found: ${integrationType}`);
|
|
360
|
+
console.log('\nAvailable integrations:');
|
|
361
|
+
integrations.forEach(i => console.log(` - ${i.value} (${i.name})`));
|
|
362
|
+
}
|
|
363
|
+
process.exit(1);
|
|
364
|
+
}
|
|
365
|
+
// Fetch available webhook events for this integration
|
|
366
|
+
let webhookEvents = [];
|
|
367
|
+
try {
|
|
368
|
+
const eventsResult = await context.unifiedToApi.getAvailableWebhookEventsByType(integrationType);
|
|
369
|
+
if (eventsResult.success && eventsResult.data) {
|
|
370
|
+
webhookEvents = eventsResult.data;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
// Silently ignore if webhook events can't be fetched
|
|
375
|
+
}
|
|
376
|
+
if (jsonOutput) {
|
|
377
|
+
// Output as JSON for scripting
|
|
378
|
+
const output = {
|
|
379
|
+
type: integration.value,
|
|
380
|
+
name: integration.name,
|
|
381
|
+
categories: integration.categories,
|
|
382
|
+
authSupport: integration.authSupport,
|
|
383
|
+
oauthConfigured: integration.oauthConfigured,
|
|
384
|
+
oauthScopes: integration.oauthScopes || [],
|
|
385
|
+
tokenFields: integration.tokenFields || [],
|
|
386
|
+
webhookEvents: webhookEvents.map(e => ({
|
|
387
|
+
trigger: `${e.objectType}.${e.event}`,
|
|
388
|
+
objectType: e.objectType,
|
|
389
|
+
event: e.event,
|
|
390
|
+
webhookType: e.webhookType,
|
|
391
|
+
friendlyLabel: e.friendlyLabel,
|
|
392
|
+
friendlyDescription: e.friendlyDescription,
|
|
393
|
+
availableFilters: e.availableFilters,
|
|
394
|
+
})),
|
|
395
|
+
webhookDefaults: {
|
|
396
|
+
agentWebhookUrl: AGENT_WEBHOOK_URL,
|
|
397
|
+
defaultVirtualInterval: DEFAULT_VIRTUAL_WEBHOOK_INTERVAL,
|
|
398
|
+
},
|
|
399
|
+
};
|
|
400
|
+
console.log(JSON.stringify(output, null, 2));
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
// Human-readable output
|
|
404
|
+
console.log("\n" + "=".repeat(60));
|
|
405
|
+
console.log(`📋 Integration: ${integration.name}`);
|
|
406
|
+
console.log("=".repeat(60));
|
|
407
|
+
console.log(`\n Type: ${integration.value}`);
|
|
408
|
+
console.log(` Categories: ${integration.categories.join(', ')}`);
|
|
409
|
+
console.log(` Auth Support: ${integration.authSupport}`);
|
|
410
|
+
console.log(` OAuth Configured: ${integration.oauthConfigured ? 'Yes' : 'No'}`);
|
|
411
|
+
// OAuth Scopes
|
|
412
|
+
if (integration.oauthScopes && integration.oauthScopes.length > 0) {
|
|
413
|
+
console.log('\n OAuth Scopes:');
|
|
414
|
+
integration.oauthScopes.forEach(s => {
|
|
415
|
+
if (s.friendlyLabel) {
|
|
416
|
+
console.log(` - ${s.friendlyLabel} (${s.unifiedScope})`);
|
|
417
|
+
if (s.friendlyDescription) {
|
|
418
|
+
console.log(` ${s.friendlyDescription}`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
console.log(` - ${s.unifiedScope} → [${s.originalScopes.join(', ')}]`);
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
// Token Fields
|
|
427
|
+
if (integration.tokenFields && integration.tokenFields.length > 0) {
|
|
428
|
+
console.log('\n Token Fields:');
|
|
429
|
+
integration.tokenFields.forEach(f => {
|
|
430
|
+
console.log(` - ${f.name}`);
|
|
431
|
+
if (f.instructions) {
|
|
432
|
+
console.log(` 💡 ${f.instructions}`);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
// Webhook Events/Triggers
|
|
437
|
+
if (webhookEvents.length > 0) {
|
|
438
|
+
console.log('\n Available Triggers:');
|
|
439
|
+
webhookEvents.forEach(e => {
|
|
440
|
+
console.log(` - ${e.objectType}.${e.event} [${e.webhookType}] - ${e.friendlyDescription}`);
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
console.log('\n Available Triggers: None');
|
|
445
|
+
}
|
|
446
|
+
console.log("\n" + "=".repeat(60));
|
|
447
|
+
console.log("\n💡 Use with non-interactive commands:");
|
|
448
|
+
console.log(` lua integrations connect --integration ${integrationType} --auth-method oauth --scopes all`);
|
|
449
|
+
if (webhookEvents.length > 0) {
|
|
450
|
+
const exampleTrigger = `${webhookEvents[0].objectType}.${webhookEvents[0].event}`;
|
|
451
|
+
console.log(` lua integrations connect --integration ${integrationType} --triggers ${exampleTrigger}`);
|
|
452
|
+
}
|
|
453
|
+
console.log();
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
catch (error) {
|
|
457
|
+
if (jsonOutput) {
|
|
458
|
+
console.log(JSON.stringify({ error: error.message }));
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
writeError(`❌ Failed to get integration info: ${error.message}`);
|
|
462
|
+
}
|
|
463
|
+
process.exit(1);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
322
467
|
// Connect Flow
|
|
323
468
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
324
469
|
async function connectIntegrationFlow(context, options = {}) {
|
|
@@ -527,11 +672,18 @@ async function connectIntegrationFlow(context, options = {}) {
|
|
|
527
672
|
message: 'Select OAuth scopes (Space to toggle, Enter to confirm, leave empty for all):',
|
|
528
673
|
pageSize: 15,
|
|
529
674
|
loop: false,
|
|
530
|
-
choices: selectedIntegration.oauthScopes.map(s =>
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
675
|
+
choices: selectedIntegration.oauthScopes.map(s => {
|
|
676
|
+
// Use friendly label if available, otherwise fall back to technical name
|
|
677
|
+
const displayName = s.friendlyLabel || s.unifiedScope;
|
|
678
|
+
const description = s.friendlyDescription
|
|
679
|
+
? ` (${s.friendlyDescription})`
|
|
680
|
+
: ` → [${s.originalScopes.join(', ')}]`;
|
|
681
|
+
return {
|
|
682
|
+
name: `${displayName}${description}`,
|
|
683
|
+
value: s.unifiedScope,
|
|
684
|
+
checked: false
|
|
685
|
+
};
|
|
686
|
+
})
|
|
535
687
|
}]);
|
|
536
688
|
if (!scopeAnswer)
|
|
537
689
|
return;
|
|
@@ -577,7 +729,132 @@ async function connectIntegrationFlow(context, options = {}) {
|
|
|
577
729
|
hideSensitive = sensitiveAnswer.hide;
|
|
578
730
|
}
|
|
579
731
|
}
|
|
580
|
-
// Step 4:
|
|
732
|
+
// Step 4: Select webhook triggers (agent wake-up events)
|
|
733
|
+
let selectedTriggers = [];
|
|
734
|
+
let webhookUrl = AGENT_WEBHOOK_URL; // Default: agent trigger mode
|
|
735
|
+
let isCustomWebhook = false;
|
|
736
|
+
try {
|
|
737
|
+
const eventsResult = await context.unifiedToApi.getAvailableWebhookEventsByType(selectedIntegration.value);
|
|
738
|
+
if (eventsResult.success && eventsResult.data && eventsResult.data.length > 0) {
|
|
739
|
+
const availableEvents = eventsResult.data;
|
|
740
|
+
// Determine webhook mode
|
|
741
|
+
if (options.customWebhook || options.hookUrl) {
|
|
742
|
+
isCustomWebhook = true;
|
|
743
|
+
if (options.hookUrl) {
|
|
744
|
+
webhookUrl = options.hookUrl;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
if (options.triggers) {
|
|
748
|
+
// Non-interactive: parse comma-separated triggers (e.g., 'task_task.created,task_task.updated')
|
|
749
|
+
const requestedTriggers = options.triggers.split(',').map(t => t.trim());
|
|
750
|
+
for (const trigger of requestedTriggers) {
|
|
751
|
+
const [objectType, event] = trigger.split('.');
|
|
752
|
+
const matchingEvent = availableEvents.find(e => e.objectType === objectType && e.event === event);
|
|
753
|
+
if (!matchingEvent) {
|
|
754
|
+
console.error(`❌ Invalid trigger: ${trigger}`);
|
|
755
|
+
console.log(`\nAvailable triggers for ${selectedIntegration.name}:`);
|
|
756
|
+
availableEvents.forEach(e => {
|
|
757
|
+
console.log(` - ${e.objectType}.${e.event} [${e.webhookType}] - ${e.friendlyDescription}`);
|
|
758
|
+
});
|
|
759
|
+
process.exit(1);
|
|
760
|
+
}
|
|
761
|
+
selectedTriggers.push(matchingEvent);
|
|
762
|
+
}
|
|
763
|
+
writeInfo(`Selected ${selectedTriggers.length} trigger(s)`);
|
|
764
|
+
// If custom webhook mode, prompt for URL if not provided
|
|
765
|
+
if (isCustomWebhook && !options.hookUrl) {
|
|
766
|
+
const urlAnswer = await safePrompt([{
|
|
767
|
+
type: 'input',
|
|
768
|
+
name: 'url',
|
|
769
|
+
message: 'Enter your webhook URL:',
|
|
770
|
+
validate: (input) => {
|
|
771
|
+
try {
|
|
772
|
+
new URL(input);
|
|
773
|
+
return true;
|
|
774
|
+
}
|
|
775
|
+
catch {
|
|
776
|
+
return 'Please enter a valid URL';
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}]);
|
|
780
|
+
if (!urlAnswer)
|
|
781
|
+
return;
|
|
782
|
+
webhookUrl = urlAnswer.url;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
else {
|
|
786
|
+
// Interactive: show trigger selection
|
|
787
|
+
console.log('\n' + '─'.repeat(60));
|
|
788
|
+
console.log('📡 Webhook Triggers');
|
|
789
|
+
console.log('─'.repeat(60));
|
|
790
|
+
console.log('\nSelect events that will wake up your agent:');
|
|
791
|
+
console.log('(Space to toggle, Enter to confirm. All selected by default.)');
|
|
792
|
+
const triggerAnswer = await safePrompt([{
|
|
793
|
+
type: 'checkbox',
|
|
794
|
+
name: 'triggers',
|
|
795
|
+
message: 'Select triggers:',
|
|
796
|
+
pageSize: 15,
|
|
797
|
+
loop: false,
|
|
798
|
+
choices: availableEvents.map(e => ({
|
|
799
|
+
name: `${e.friendlyDescription} [${e.webhookType}]`,
|
|
800
|
+
value: e,
|
|
801
|
+
checked: true // Pre-select all by default
|
|
802
|
+
}))
|
|
803
|
+
}]);
|
|
804
|
+
if (triggerAnswer && triggerAnswer.triggers.length > 0) {
|
|
805
|
+
selectedTriggers = triggerAnswer.triggers;
|
|
806
|
+
// Ask about webhook mode only if triggers are selected
|
|
807
|
+
const modeAnswer = await safePrompt([{
|
|
808
|
+
type: 'list',
|
|
809
|
+
name: 'mode',
|
|
810
|
+
message: 'How should these events be handled?',
|
|
811
|
+
choices: [
|
|
812
|
+
{ name: '🤖 Agent Trigger (recommended) - Events wake up your agent', value: 'agent' },
|
|
813
|
+
{ name: '🔗 Custom URL - Send events to your own webhook URL', value: 'custom' }
|
|
814
|
+
]
|
|
815
|
+
}]);
|
|
816
|
+
if (!modeAnswer)
|
|
817
|
+
return;
|
|
818
|
+
if (modeAnswer.mode === 'custom') {
|
|
819
|
+
isCustomWebhook = true;
|
|
820
|
+
const urlAnswer = await safePrompt([{
|
|
821
|
+
type: 'input',
|
|
822
|
+
name: 'url',
|
|
823
|
+
message: 'Enter your webhook URL:',
|
|
824
|
+
validate: (input) => {
|
|
825
|
+
try {
|
|
826
|
+
new URL(input);
|
|
827
|
+
return true;
|
|
828
|
+
}
|
|
829
|
+
catch {
|
|
830
|
+
return 'Please enter a valid URL';
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}]);
|
|
834
|
+
if (!urlAnswer)
|
|
835
|
+
return;
|
|
836
|
+
webhookUrl = urlAnswer.url;
|
|
837
|
+
}
|
|
838
|
+
console.log(`\n✓ ${selectedTriggers.length} trigger(s) selected`);
|
|
839
|
+
if (isCustomWebhook) {
|
|
840
|
+
console.log(` → Events will be sent to: ${webhookUrl}`);
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
console.log(` → Events will wake up your agent`);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
else {
|
|
847
|
+
console.log(`\n⏭️ No triggers selected - your agent won't be event-driven`);
|
|
848
|
+
console.log(` You can add triggers later with: lua integrations webhooks create`);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
catch (error) {
|
|
854
|
+
// Non-fatal: continue without triggers if we can't fetch events
|
|
855
|
+
writeInfo(`Note: Could not fetch available triggers (${error.message})`);
|
|
856
|
+
}
|
|
857
|
+
// Step 5: Get authorization URL from Lua API
|
|
581
858
|
writeProgress("🔄 Preparing authorization...");
|
|
582
859
|
const state = Buffer.from(JSON.stringify({
|
|
583
860
|
agentId: context.agentId,
|
|
@@ -642,22 +919,24 @@ async function connectIntegrationFlow(context, options = {}) {
|
|
|
642
919
|
const result = await callbackPromise;
|
|
643
920
|
if (result.success && result.connectionId) {
|
|
644
921
|
writeSuccess("\n✅ Authorization successful!");
|
|
645
|
-
// Finalize the connection (sets up MCP server internally)
|
|
646
|
-
await finalizeConnection(context, selectedIntegration, result.connectionId, selectedScopes, hideSensitive
|
|
922
|
+
// Finalize the connection (sets up MCP server internally and creates webhooks)
|
|
923
|
+
await finalizeConnection(context, selectedIntegration, result.connectionId, selectedScopes, hideSensitive, {
|
|
924
|
+
triggers: selectedTriggers,
|
|
925
|
+
webhookUrl,
|
|
926
|
+
isCustomWebhook,
|
|
927
|
+
});
|
|
647
928
|
}
|
|
648
929
|
else {
|
|
649
930
|
writeError(`\n❌ Authorization failed: ${result.error || 'Unknown error'}`);
|
|
650
931
|
console.log("💡 Please try again with 'lua integrations connect'\n");
|
|
651
932
|
}
|
|
652
933
|
}
|
|
653
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
654
|
-
// Connection Management
|
|
655
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
656
934
|
/**
|
|
657
|
-
* Finalizes a new connection by setting up the MCP server
|
|
935
|
+
* Finalizes a new connection by setting up the MCP server and creating webhooks
|
|
658
936
|
* The MCP server enables the agent to use tools from this connection
|
|
937
|
+
* Webhooks enable the agent to be triggered by events from the connection
|
|
659
938
|
*/
|
|
660
|
-
async function finalizeConnection(context, integration, connectionId, scopes, hideSensitive = true) {
|
|
939
|
+
async function finalizeConnection(context, integration, connectionId, scopes, hideSensitive = true, webhookConfig = { triggers: [], webhookUrl: '', isCustomWebhook: false }) {
|
|
661
940
|
writeProgress(`🔄 Setting up ${integration.name} connection...`);
|
|
662
941
|
// Build MCP URL
|
|
663
942
|
let mcpUrl = `${UNIFIED_MCP_BASE_URL}?connection=${connectionId}`;
|
|
@@ -679,44 +958,154 @@ async function finalizeConnection(context, integration, connectionId, scopes, hi
|
|
|
679
958
|
source: 'unifiedto', // Flag for runtime auth injection
|
|
680
959
|
timeout: 30000,
|
|
681
960
|
};
|
|
961
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
962
|
+
// Step 1: Set up MCP Server (for tools)
|
|
963
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
964
|
+
let mcpSetupSuccess = false;
|
|
965
|
+
let isActive = false;
|
|
966
|
+
let mcpError = null;
|
|
682
967
|
try {
|
|
968
|
+
writeProgress(`🔄 Setting up MCP server...`);
|
|
683
969
|
const result = await context.developerApi.createMCPServer(mcpServerData);
|
|
684
970
|
if (result.success && result.data) {
|
|
685
971
|
// Auto-activate the MCP server so the agent can use the connection immediately
|
|
686
972
|
const activateResult = await context.developerApi.activateMCPServer(result.data.id);
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
973
|
+
isActive = activateResult.success && activateResult.data?.active === true;
|
|
974
|
+
mcpSetupSuccess = true;
|
|
975
|
+
}
|
|
976
|
+
else {
|
|
977
|
+
mcpError = result.error?.message || 'Unknown error';
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
catch (error) {
|
|
981
|
+
mcpError = error.message || 'Unknown error';
|
|
982
|
+
}
|
|
983
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
984
|
+
// Step 2: Set up Webhook Triggers (for event-driven agent wake-up)
|
|
985
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
986
|
+
const createdWebhooks = [];
|
|
987
|
+
const failedWebhooks = [];
|
|
988
|
+
if (webhookConfig.triggers.length > 0) {
|
|
989
|
+
writeProgress(`🔄 Setting up ${webhookConfig.triggers.length} trigger(s)...`);
|
|
990
|
+
for (const trigger of webhookConfig.triggers) {
|
|
991
|
+
try {
|
|
992
|
+
// Use per-trigger hookUrl/interval if available, otherwise fall back to defaults
|
|
993
|
+
const hookUrl = trigger.hookUrl || webhookConfig.webhookUrl || AGENT_WEBHOOK_URL;
|
|
994
|
+
const interval = trigger.interval ?? (trigger.webhookType === 'virtual' ? DEFAULT_VIRTUAL_WEBHOOK_INTERVAL : undefined);
|
|
995
|
+
const webhookResult = await context.unifiedToApi.createWebhookSubscription(context.agentId, {
|
|
996
|
+
connectionId,
|
|
997
|
+
objectType: trigger.objectType,
|
|
998
|
+
event: trigger.event,
|
|
999
|
+
hookUrl,
|
|
1000
|
+
interval,
|
|
1001
|
+
});
|
|
1002
|
+
if (webhookResult.success && webhookResult.data) {
|
|
1003
|
+
createdWebhooks.push(webhookResult.data);
|
|
1004
|
+
}
|
|
1005
|
+
else {
|
|
1006
|
+
const errorMsg = webhookResult.error?.message || 'Unknown error';
|
|
1007
|
+
failedWebhooks.push(`${trigger.objectType}.${trigger.event}: ${errorMsg}`);
|
|
1008
|
+
}
|
|
699
1009
|
}
|
|
700
|
-
|
|
701
|
-
|
|
1010
|
+
catch (error) {
|
|
1011
|
+
const errorMsg = error?.message || 'Unknown error';
|
|
1012
|
+
failedWebhooks.push(`${trigger.objectType}.${trigger.event}: ${errorMsg}`);
|
|
702
1013
|
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1017
|
+
// Step 3: Display Results
|
|
1018
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1019
|
+
console.log("\n" + "─".repeat(60));
|
|
1020
|
+
console.log("🎉 Connection Established!");
|
|
1021
|
+
console.log("─".repeat(60));
|
|
1022
|
+
console.log(`\n Integration: ${integration.name}`);
|
|
1023
|
+
console.log(` Connection ID: ${connectionId}`);
|
|
1024
|
+
// MCP Server status
|
|
1025
|
+
console.log(`\n 📦 MCP Server (Tools):`);
|
|
1026
|
+
if (mcpSetupSuccess) {
|
|
1027
|
+
console.log(` Status: ${isActive ? '🟢 Active' : '🟡 Pending activation'}`);
|
|
1028
|
+
console.log(` Name: ${serverName}`);
|
|
1029
|
+
console.log(` Hide Sensitive: ${hideSensitive ? '✅ Yes' : '❌ No'}`);
|
|
1030
|
+
}
|
|
1031
|
+
else {
|
|
1032
|
+
console.log(` Status: ❌ Failed`);
|
|
1033
|
+
console.log(` Error: ${mcpError}`);
|
|
1034
|
+
}
|
|
1035
|
+
// Show requested scopes with friendly labels
|
|
1036
|
+
if (scopes.length > 0) {
|
|
1037
|
+
console.log(`\n 🔑 Permissions (${scopes.length}):`);
|
|
1038
|
+
scopes.forEach(scopeName => {
|
|
1039
|
+
const scopeInfo = integration.oauthScopes?.find(s => s.unifiedScope === scopeName);
|
|
1040
|
+
if (scopeInfo?.friendlyLabel) {
|
|
1041
|
+
console.log(` • ${scopeInfo.friendlyLabel}`);
|
|
707
1042
|
}
|
|
708
1043
|
else {
|
|
709
|
-
console.log(
|
|
1044
|
+
console.log(` • ${scopeName}`);
|
|
710
1045
|
}
|
|
711
|
-
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
else {
|
|
1049
|
+
console.log(`\n 🔑 Permissions: Default`);
|
|
1050
|
+
}
|
|
1051
|
+
// Webhook/Triggers status
|
|
1052
|
+
if (webhookConfig.triggers.length > 0) {
|
|
1053
|
+
console.log(`\n ⚡ Triggers (${createdWebhooks.length}/${webhookConfig.triggers.length}):`);
|
|
1054
|
+
// Compute actual URLs being used (per-trigger hookUrl or default)
|
|
1055
|
+
const actualUrls = webhookConfig.triggers.map(t => t.hookUrl || webhookConfig.webhookUrl || AGENT_WEBHOOK_URL);
|
|
1056
|
+
const uniqueUrls = [...new Set(actualUrls)];
|
|
1057
|
+
const allAgentTriggers = uniqueUrls.every(url => url === AGENT_WEBHOOK_URL);
|
|
1058
|
+
if (allAgentTriggers) {
|
|
1059
|
+
console.log(` Mode: Agent wake-up`);
|
|
1060
|
+
}
|
|
1061
|
+
else if (uniqueUrls.length === 1) {
|
|
1062
|
+
console.log(` Mode: Custom webhook`);
|
|
1063
|
+
console.log(` URL: ${uniqueUrls[0]}`);
|
|
712
1064
|
}
|
|
713
1065
|
else {
|
|
714
|
-
|
|
1066
|
+
console.log(` Mode: Mixed (per-trigger URLs)`);
|
|
1067
|
+
}
|
|
1068
|
+
if (createdWebhooks.length > 0) {
|
|
1069
|
+
createdWebhooks.forEach(wh => {
|
|
1070
|
+
const trigger = webhookConfig.triggers.find(t => t.objectType === wh.objectType && t.event === wh.event);
|
|
1071
|
+
const desc = trigger?.friendlyDescription || `${wh.objectType}.${wh.event}`;
|
|
1072
|
+
console.log(` ✓ ${desc}`);
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
if (failedWebhooks.length > 0) {
|
|
1076
|
+
failedWebhooks.forEach(t => console.log(` ✗ ${t}`));
|
|
715
1077
|
}
|
|
716
1078
|
}
|
|
717
|
-
|
|
718
|
-
|
|
1079
|
+
// Summary message
|
|
1080
|
+
console.log("\n" + "─".repeat(60));
|
|
1081
|
+
const capabilities = [];
|
|
1082
|
+
if (mcpSetupSuccess && isActive) {
|
|
1083
|
+
capabilities.push('tools via MCP');
|
|
1084
|
+
}
|
|
1085
|
+
if (createdWebhooks.length > 0) {
|
|
1086
|
+
// Check if any triggers use custom URLs
|
|
1087
|
+
const hasCustomUrls = webhookConfig.triggers.some(t => {
|
|
1088
|
+
const url = t.hookUrl || webhookConfig.webhookUrl || AGENT_WEBHOOK_URL;
|
|
1089
|
+
return url !== AGENT_WEBHOOK_URL;
|
|
1090
|
+
});
|
|
1091
|
+
if (hasCustomUrls) {
|
|
1092
|
+
capabilities.push('webhooks to custom URL');
|
|
1093
|
+
}
|
|
1094
|
+
else {
|
|
1095
|
+
capabilities.push('event-driven triggers');
|
|
1096
|
+
}
|
|
719
1097
|
}
|
|
1098
|
+
if (capabilities.length > 0) {
|
|
1099
|
+
console.log(`✅ ${integration.name} connected! Your agent now has: ${capabilities.join(', ')}`);
|
|
1100
|
+
}
|
|
1101
|
+
else if (mcpSetupSuccess && !isActive) {
|
|
1102
|
+
console.log(`⚠️ Connection created but MCP server pending activation.`);
|
|
1103
|
+
console.log(` Run: lua mcp activate --server-name ${serverName}`);
|
|
1104
|
+
}
|
|
1105
|
+
else {
|
|
1106
|
+
console.log(`⚠️ Connection created but setup incomplete. Check errors above.`);
|
|
1107
|
+
}
|
|
1108
|
+
console.log();
|
|
720
1109
|
}
|
|
721
1110
|
async function listConnections(context) {
|
|
722
1111
|
writeProgress("🔄 Loading connections...");
|
|
@@ -738,11 +1127,11 @@ async function listConnections(context) {
|
|
|
738
1127
|
}
|
|
739
1128
|
}
|
|
740
1129
|
console.log("\n" + "=".repeat(60));
|
|
741
|
-
console.log("🔗 Connected
|
|
1130
|
+
console.log("🔗 Connected Integrations");
|
|
742
1131
|
console.log("=".repeat(60) + "\n");
|
|
743
1132
|
if (connections.length === 0) {
|
|
744
|
-
console.log("ℹ️ No
|
|
745
|
-
console.log("💡 Run 'lua integrations connect' to connect
|
|
1133
|
+
console.log("ℹ️ No integrations connected yet.");
|
|
1134
|
+
console.log("💡 Run 'lua integrations connect' to connect an integration.\n");
|
|
746
1135
|
return;
|
|
747
1136
|
}
|
|
748
1137
|
for (const connection of connections) {
|
|
@@ -765,23 +1154,26 @@ async function listConnections(context) {
|
|
|
765
1154
|
}
|
|
766
1155
|
else if (connection.status === 'active') {
|
|
767
1156
|
statusIcon = '🟡';
|
|
768
|
-
statusText = 'Connected (
|
|
1157
|
+
statusText = 'Connected (MCP pending)';
|
|
769
1158
|
}
|
|
770
1159
|
console.log(`${statusIcon} ${connection.integrationName || connection.integrationType}`);
|
|
771
|
-
console.log(`
|
|
1160
|
+
console.log(` Connection: ${connection.id}`);
|
|
772
1161
|
console.log(` Status: ${statusText}`);
|
|
1162
|
+
if (linkedServer) {
|
|
1163
|
+
console.log(` MCP Server: ${linkedServer.name} (${linkedServer.active ? 'active' : 'inactive'})`);
|
|
1164
|
+
}
|
|
773
1165
|
console.log(` Connected: ${new Date(connection.createdAt).toLocaleDateString()}`);
|
|
774
1166
|
console.log();
|
|
775
1167
|
}
|
|
776
1168
|
console.log("=".repeat(60));
|
|
777
|
-
console.log(`Total: ${connections.length}
|
|
1169
|
+
console.log(`Total: ${connections.length} integration(s)\n`);
|
|
778
1170
|
}
|
|
779
1171
|
catch (error) {
|
|
780
1172
|
writeError(`❌ Error loading connections: ${error.message}`);
|
|
781
1173
|
}
|
|
782
1174
|
}
|
|
783
1175
|
async function disconnectIntegration(context, connectionId) {
|
|
784
|
-
writeProgress(`🔄 Disconnecting
|
|
1176
|
+
writeProgress(`🔄 Disconnecting... (please wait, do not close this window)`);
|
|
785
1177
|
try {
|
|
786
1178
|
// Clean up associated MCP server (internal implementation detail)
|
|
787
1179
|
const mcpServers = await context.developerApi.getMCPServers();
|
|
@@ -794,9 +1186,9 @@ async function disconnectIntegration(context, connectionId) {
|
|
|
794
1186
|
// Delete the connection (also deletes webhook subscriptions)
|
|
795
1187
|
const deleteResult = await context.unifiedToApi.deleteConnection(connectionId, context.agentId);
|
|
796
1188
|
if (deleteResult.success) {
|
|
797
|
-
writeSuccess(`✅
|
|
1189
|
+
writeSuccess(`✅ Integration disconnected successfully!`);
|
|
798
1190
|
if (deleteResult.data?.deletedWebhooksCount && deleteResult.data.deletedWebhooksCount > 0) {
|
|
799
|
-
console.log(` ✓ Deleted ${deleteResult.data.deletedWebhooksCount}
|
|
1191
|
+
console.log(` ✓ Deleted ${deleteResult.data.deletedWebhooksCount} trigger(s)`);
|
|
800
1192
|
}
|
|
801
1193
|
console.log();
|
|
802
1194
|
}
|
|
@@ -817,14 +1209,14 @@ async function disconnectIntegrationInteractive(context) {
|
|
|
817
1209
|
}
|
|
818
1210
|
const connections = connectionsResult.data || [];
|
|
819
1211
|
if (connections.length === 0) {
|
|
820
|
-
console.log("\nℹ️ No
|
|
1212
|
+
console.log("\nℹ️ No integrations to disconnect.\n");
|
|
821
1213
|
return;
|
|
822
1214
|
}
|
|
823
1215
|
const connectionAnswer = await safePrompt([
|
|
824
1216
|
{
|
|
825
1217
|
type: 'list',
|
|
826
1218
|
name: 'connection',
|
|
827
|
-
message: 'Select an
|
|
1219
|
+
message: 'Select an integration to disconnect:',
|
|
828
1220
|
choices: connections.map(c => ({
|
|
829
1221
|
name: `${c.integrationName || c.integrationType} (${c.id.substring(0, 8)}...)`,
|
|
830
1222
|
value: c.id
|
|
@@ -838,7 +1230,7 @@ async function disconnectIntegrationInteractive(context) {
|
|
|
838
1230
|
{
|
|
839
1231
|
type: 'confirm',
|
|
840
1232
|
name: 'confirm',
|
|
841
|
-
message: `Disconnect ${selectedConnection?.integrationName || selectedConnection?.integrationType}? Your agent will lose access to this
|
|
1233
|
+
message: `Disconnect ${selectedConnection?.integrationName || selectedConnection?.integrationType}? Your agent will lose access to this integration.`,
|
|
842
1234
|
default: false
|
|
843
1235
|
}
|
|
844
1236
|
]);
|
|
@@ -873,8 +1265,8 @@ async function updateConnectionFlow(context, options = {}) {
|
|
|
873
1265
|
return;
|
|
874
1266
|
}
|
|
875
1267
|
if (connections.length === 0) {
|
|
876
|
-
writeInfo("No
|
|
877
|
-
console.log("💡 Run 'lua integrations connect' to connect
|
|
1268
|
+
writeInfo("No integrations to update.");
|
|
1269
|
+
console.log("💡 Run 'lua integrations connect' to connect an integration.\n");
|
|
878
1270
|
return;
|
|
879
1271
|
}
|
|
880
1272
|
// Step 2: Select connection to update
|
|
@@ -953,11 +1345,18 @@ async function updateConnectionFlow(context, options = {}) {
|
|
|
953
1345
|
message: 'Select new OAuth scopes (Space to toggle, Enter to confirm, leave empty for all):',
|
|
954
1346
|
pageSize: 15,
|
|
955
1347
|
loop: false,
|
|
956
|
-
choices: selectedIntegration.oauthScopes.map(s =>
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1348
|
+
choices: selectedIntegration.oauthScopes.map(s => {
|
|
1349
|
+
// Use friendly label if available, otherwise fall back to technical name
|
|
1350
|
+
const displayName = s.friendlyLabel || s.unifiedScope;
|
|
1351
|
+
const description = s.friendlyDescription
|
|
1352
|
+
? ` (${s.friendlyDescription})`
|
|
1353
|
+
: ` → [${s.originalScopes.join(', ')}]`;
|
|
1354
|
+
return {
|
|
1355
|
+
name: `${displayName}${description}`,
|
|
1356
|
+
value: s.unifiedScope,
|
|
1357
|
+
checked: false
|
|
1358
|
+
};
|
|
1359
|
+
})
|
|
961
1360
|
}]);
|
|
962
1361
|
if (!scopeAnswer)
|
|
963
1362
|
return;
|
|
@@ -983,7 +1382,28 @@ async function updateConnectionFlow(context, options = {}) {
|
|
|
983
1382
|
hideSensitive = sensitiveAnswer.hide;
|
|
984
1383
|
}
|
|
985
1384
|
}
|
|
986
|
-
// Step 5:
|
|
1385
|
+
// Step 5: Fetch existing triggers before deletion (so we can re-create them)
|
|
1386
|
+
let existingTriggers = [];
|
|
1387
|
+
try {
|
|
1388
|
+
const webhooksResult = await context.unifiedToApi.getWebhookSubscriptions(context.agentId);
|
|
1389
|
+
if (webhooksResult.success && webhooksResult.data && webhooksResult.data.length > 0) {
|
|
1390
|
+
// Filter webhooks to only those belonging to this connection
|
|
1391
|
+
const connectionWebhooks = webhooksResult.data.filter(w => w.connectionId === selectedConnection.id);
|
|
1392
|
+
existingTriggers = connectionWebhooks.map(w => ({
|
|
1393
|
+
objectType: w.objectType,
|
|
1394
|
+
event: w.event,
|
|
1395
|
+
hookUrl: w.hookUrl,
|
|
1396
|
+
interval: w.interval,
|
|
1397
|
+
}));
|
|
1398
|
+
if (existingTriggers.length > 0) {
|
|
1399
|
+
writeInfo(`Found ${existingTriggers.length} existing trigger(s) - will restore after update`);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
catch (error) {
|
|
1404
|
+
// If we can't fetch existing triggers, continue without them
|
|
1405
|
+
}
|
|
1406
|
+
// Step 6: Confirm the update
|
|
987
1407
|
if (!options.integration) {
|
|
988
1408
|
const confirmAnswer = await safePrompt([
|
|
989
1409
|
{
|
|
@@ -998,7 +1418,7 @@ async function updateConnectionFlow(context, options = {}) {
|
|
|
998
1418
|
return;
|
|
999
1419
|
}
|
|
1000
1420
|
}
|
|
1001
|
-
// Step
|
|
1421
|
+
// Step 7: Delete the old connection (silently)
|
|
1002
1422
|
writeProgress(`🔄 Updating ${selectedIntegration.name}...`);
|
|
1003
1423
|
try {
|
|
1004
1424
|
// Clean up old MCP server
|
|
@@ -1009,14 +1429,14 @@ async function updateConnectionFlow(context, options = {}) {
|
|
|
1009
1429
|
await context.developerApi.deleteMCPServer(associatedServer.id);
|
|
1010
1430
|
}
|
|
1011
1431
|
}
|
|
1012
|
-
// Delete old connection
|
|
1432
|
+
// Delete old connection (this also deletes associated webhooks in Unified.to)
|
|
1013
1433
|
await context.unifiedToApi.deleteConnection(selectedConnection.id, context.agentId);
|
|
1014
1434
|
}
|
|
1015
1435
|
catch (error) {
|
|
1016
1436
|
writeError(`❌ Failed to remove old connection: ${error.message}`);
|
|
1017
1437
|
return;
|
|
1018
1438
|
}
|
|
1019
|
-
// Step
|
|
1439
|
+
// Step 8: Create new connection with new scopes
|
|
1020
1440
|
const state = Buffer.from(JSON.stringify({
|
|
1021
1441
|
agentId: context.agentId,
|
|
1022
1442
|
integration: selectedIntegration.value,
|
|
@@ -1057,7 +1477,7 @@ async function updateConnectionFlow(context, options = {}) {
|
|
|
1057
1477
|
catch (error) {
|
|
1058
1478
|
// Use original auth URL if pre-fetch fails
|
|
1059
1479
|
}
|
|
1060
|
-
// Step
|
|
1480
|
+
// Step 9: Open browser and wait for callback
|
|
1061
1481
|
console.log("\n" + "─".repeat(60));
|
|
1062
1482
|
console.log("🌐 Re-authorizing with new scopes...");
|
|
1063
1483
|
console.log("─".repeat(60));
|
|
@@ -1075,7 +1495,39 @@ async function updateConnectionFlow(context, options = {}) {
|
|
|
1075
1495
|
const result = await callbackPromise;
|
|
1076
1496
|
if (result.success && result.connectionId) {
|
|
1077
1497
|
writeSuccess("\n✅ Authorization successful!");
|
|
1078
|
-
|
|
1498
|
+
// Check if any triggers use custom webhooks
|
|
1499
|
+
const hasCustomWebhook = existingTriggers.some(t => t.hookUrl !== AGENT_WEBHOOK_URL);
|
|
1500
|
+
// Convert existing triggers to TriggerWithSettings format, preserving per-trigger hookUrl and interval
|
|
1501
|
+
let triggersToRestore = [];
|
|
1502
|
+
if (existingTriggers.length > 0) {
|
|
1503
|
+
try {
|
|
1504
|
+
const eventsResult = await context.unifiedToApi.getAvailableWebhookEventsByType(selectedIntegration.value);
|
|
1505
|
+
if (eventsResult.success && eventsResult.data) {
|
|
1506
|
+
for (const trigger of existingTriggers) {
|
|
1507
|
+
const matchingEvent = eventsResult.data.find(e => e.objectType === trigger.objectType && e.event === trigger.event);
|
|
1508
|
+
if (matchingEvent) {
|
|
1509
|
+
// Preserve per-trigger hookUrl and interval from original webhook
|
|
1510
|
+
triggersToRestore.push({
|
|
1511
|
+
...matchingEvent,
|
|
1512
|
+
hookUrl: trigger.hookUrl,
|
|
1513
|
+
interval: trigger.interval,
|
|
1514
|
+
});
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
catch (error) {
|
|
1520
|
+
// If we can't match triggers, continue without them
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
await finalizeConnection(context, selectedIntegration, result.connectionId, selectedScopes, hideSensitive, {
|
|
1524
|
+
triggers: triggersToRestore,
|
|
1525
|
+
webhookUrl: AGENT_WEBHOOK_URL, // Default, but per-trigger hookUrl will override if set
|
|
1526
|
+
isCustomWebhook: hasCustomWebhook,
|
|
1527
|
+
});
|
|
1528
|
+
if (triggersToRestore.length > 0) {
|
|
1529
|
+
writeSuccess(`Restored ${triggersToRestore.length} trigger(s) from previous connection`);
|
|
1530
|
+
}
|
|
1079
1531
|
}
|
|
1080
1532
|
else {
|
|
1081
1533
|
writeError(`\n❌ Authorization failed: ${result.error || 'Unknown error'}`);
|
|
@@ -1095,12 +1547,14 @@ async function webhooksSubcommand(context, cmdOptions) {
|
|
|
1095
1547
|
webhookId: cmdOptions?.webhookId,
|
|
1096
1548
|
objectType: cmdOptions?.object,
|
|
1097
1549
|
event: cmdOptions?.event,
|
|
1098
|
-
|
|
1550
|
+
hookUrl: cmdOptions?.hookUrl,
|
|
1099
1551
|
interval: cmdOptions?.interval ? parseInt(cmdOptions.interval, 10) : undefined,
|
|
1552
|
+
integration: cmdOptions?.integration,
|
|
1100
1553
|
};
|
|
1554
|
+
const jsonOutput = cmdOptions?.json === true;
|
|
1101
1555
|
switch (subAction) {
|
|
1102
1556
|
case 'list':
|
|
1103
|
-
await webhooksListFlow(context);
|
|
1557
|
+
await webhooksListFlow(context, jsonOutput);
|
|
1104
1558
|
break;
|
|
1105
1559
|
case 'create':
|
|
1106
1560
|
await webhooksCreateFlow(context, options);
|
|
@@ -1108,27 +1562,122 @@ async function webhooksSubcommand(context, cmdOptions) {
|
|
|
1108
1562
|
case 'delete':
|
|
1109
1563
|
if (!options.webhookId) {
|
|
1110
1564
|
console.error("❌ --webhook-id is required for delete");
|
|
1111
|
-
console.log("\n💡 Run 'lua integrations webhooks list' to see
|
|
1565
|
+
console.log("\n💡 Run 'lua integrations webhooks list' to see trigger IDs");
|
|
1112
1566
|
process.exit(1);
|
|
1113
1567
|
}
|
|
1114
1568
|
await webhooksDeleteFlow(context, options.webhookId);
|
|
1115
1569
|
break;
|
|
1570
|
+
case 'events':
|
|
1571
|
+
// New: List available webhook events for a connection or integration type
|
|
1572
|
+
await webhooksEventsFlow(context, options, jsonOutput);
|
|
1573
|
+
break;
|
|
1116
1574
|
default:
|
|
1117
1575
|
console.error(`❌ Invalid webhooks action: "${subAction || '(none)'}"`);
|
|
1118
1576
|
console.log('\nUsage:');
|
|
1119
|
-
console.log(' lua integrations webhooks list List
|
|
1120
|
-
console.log(' lua integrations webhooks
|
|
1121
|
-
console.log(' lua integrations webhooks
|
|
1122
|
-
console.log(' lua integrations webhooks
|
|
1577
|
+
console.log(' lua integrations webhooks list List triggers');
|
|
1578
|
+
console.log(' lua integrations webhooks events --connection <id> List available events for a connection');
|
|
1579
|
+
console.log(' lua integrations webhooks events --integration <type> List available events for an integration');
|
|
1580
|
+
console.log(' lua integrations webhooks create Create trigger (interactive)');
|
|
1581
|
+
console.log(' lua integrations webhooks create --connection <id> --object <type> --event <event> --hook-url <url>');
|
|
1582
|
+
console.log(' lua integrations webhooks delete --webhook-id <id> Delete trigger');
|
|
1123
1583
|
process.exit(1);
|
|
1124
1584
|
}
|
|
1125
1585
|
}
|
|
1586
|
+
/**
|
|
1587
|
+
* Show available webhook events for a connection or integration type
|
|
1588
|
+
* Used for non-interactive discovery
|
|
1589
|
+
*/
|
|
1590
|
+
async function webhooksEventsFlow(context, options, jsonOutput = false) {
|
|
1591
|
+
try {
|
|
1592
|
+
let webhookEvents = [];
|
|
1593
|
+
let sourceName = '';
|
|
1594
|
+
if (options.connectionId) {
|
|
1595
|
+
// Get events by connection ID
|
|
1596
|
+
const eventsResult = await context.unifiedToApi.getAvailableWebhookEvents(context.agentId, options.connectionId);
|
|
1597
|
+
if (eventsResult.success && eventsResult.data) {
|
|
1598
|
+
webhookEvents = eventsResult.data;
|
|
1599
|
+
}
|
|
1600
|
+
sourceName = `connection ${options.connectionId}`;
|
|
1601
|
+
}
|
|
1602
|
+
else if (options.integration) {
|
|
1603
|
+
// Get events by integration type
|
|
1604
|
+
const eventsResult = await context.unifiedToApi.getAvailableWebhookEventsByType(options.integration);
|
|
1605
|
+
if (eventsResult.success && eventsResult.data) {
|
|
1606
|
+
webhookEvents = eventsResult.data;
|
|
1607
|
+
}
|
|
1608
|
+
sourceName = `integration ${options.integration}`;
|
|
1609
|
+
}
|
|
1610
|
+
else {
|
|
1611
|
+
if (jsonOutput) {
|
|
1612
|
+
console.log(JSON.stringify({ error: 'Either --connection or --integration is required' }));
|
|
1613
|
+
}
|
|
1614
|
+
else {
|
|
1615
|
+
console.error("❌ Either --connection <id> or --integration <type> is required");
|
|
1616
|
+
console.log("\nUsage:");
|
|
1617
|
+
console.log(" lua integrations webhooks events --connection <id>");
|
|
1618
|
+
console.log(" lua integrations webhooks events --integration <type>");
|
|
1619
|
+
}
|
|
1620
|
+
process.exit(1);
|
|
1621
|
+
}
|
|
1622
|
+
if (jsonOutput) {
|
|
1623
|
+
const output = {
|
|
1624
|
+
source: sourceName,
|
|
1625
|
+
events: webhookEvents.map(e => ({
|
|
1626
|
+
trigger: `${e.objectType}.${e.event}`,
|
|
1627
|
+
objectType: e.objectType,
|
|
1628
|
+
event: e.event,
|
|
1629
|
+
webhookType: e.webhookType,
|
|
1630
|
+
friendlyLabel: e.friendlyLabel,
|
|
1631
|
+
friendlyDescription: e.friendlyDescription,
|
|
1632
|
+
availableFilters: e.availableFilters,
|
|
1633
|
+
})),
|
|
1634
|
+
defaults: {
|
|
1635
|
+
agentWebhookUrl: AGENT_WEBHOOK_URL,
|
|
1636
|
+
defaultVirtualInterval: DEFAULT_VIRTUAL_WEBHOOK_INTERVAL,
|
|
1637
|
+
},
|
|
1638
|
+
};
|
|
1639
|
+
console.log(JSON.stringify(output, null, 2));
|
|
1640
|
+
}
|
|
1641
|
+
else {
|
|
1642
|
+
console.log("\n" + "=".repeat(60));
|
|
1643
|
+
console.log(`📡 Available Trigger Events for ${sourceName}`);
|
|
1644
|
+
console.log("=".repeat(60) + "\n");
|
|
1645
|
+
if (webhookEvents.length === 0) {
|
|
1646
|
+
console.log("ℹ️ No trigger events available for this integration.\n");
|
|
1647
|
+
}
|
|
1648
|
+
else {
|
|
1649
|
+
webhookEvents.forEach(e => {
|
|
1650
|
+
console.log(` ${e.objectType}.${e.event} [${e.webhookType}]`);
|
|
1651
|
+
console.log(` ${e.friendlyDescription}`);
|
|
1652
|
+
if (e.availableFilters.length > 0) {
|
|
1653
|
+
console.log(` Filters: ${e.availableFilters.join(', ')}`);
|
|
1654
|
+
}
|
|
1655
|
+
console.log();
|
|
1656
|
+
});
|
|
1657
|
+
console.log("─".repeat(60));
|
|
1658
|
+
console.log(`Total: ${webhookEvents.length} event(s) available`);
|
|
1659
|
+
console.log("\n💡 Use with --triggers flag:");
|
|
1660
|
+
const exampleTriggers = webhookEvents.slice(0, 2).map(e => `${e.objectType}.${e.event}`).join(',');
|
|
1661
|
+
console.log(` lua integrations connect --integration <type> --triggers ${exampleTriggers}\n`);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
catch (error) {
|
|
1666
|
+
if (jsonOutput) {
|
|
1667
|
+
console.log(JSON.stringify({ error: error.message }));
|
|
1668
|
+
}
|
|
1669
|
+
else {
|
|
1670
|
+
writeError(`❌ Failed to get trigger events: ${error.message}`);
|
|
1671
|
+
}
|
|
1672
|
+
process.exit(1);
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1126
1675
|
/**
|
|
1127
1676
|
* Interactive webhooks menu
|
|
1128
1677
|
*/
|
|
1129
1678
|
async function webhooksInteractiveMenu(context) {
|
|
1130
1679
|
console.log("\n" + "─".repeat(60));
|
|
1131
|
-
console.log("🔔
|
|
1680
|
+
console.log("🔔 Triggers");
|
|
1132
1681
|
console.log("─".repeat(60) + "\n");
|
|
1133
1682
|
const actionAnswer = await safePrompt([
|
|
1134
1683
|
{
|
|
@@ -1136,7 +1685,7 @@ async function webhooksInteractiveMenu(context) {
|
|
|
1136
1685
|
name: 'action',
|
|
1137
1686
|
message: 'What would you like to do?',
|
|
1138
1687
|
choices: [
|
|
1139
|
-
{ name: '📋 List
|
|
1688
|
+
{ name: '📋 List triggers', value: 'list' },
|
|
1140
1689
|
{ name: '➕ Create new subscription', value: 'create' },
|
|
1141
1690
|
{ name: '🗑️ Delete subscription', value: 'delete' },
|
|
1142
1691
|
{ name: '← Back', value: 'back' }
|
|
@@ -1160,35 +1709,81 @@ async function webhooksInteractiveMenu(context) {
|
|
|
1160
1709
|
/**
|
|
1161
1710
|
* List all webhook subscriptions
|
|
1162
1711
|
*/
|
|
1163
|
-
async function webhooksListFlow(context) {
|
|
1164
|
-
writeProgress("🔄 Loading
|
|
1712
|
+
async function webhooksListFlow(context, jsonOutput = false) {
|
|
1713
|
+
writeProgress("🔄 Loading triggers...");
|
|
1165
1714
|
try {
|
|
1166
1715
|
const result = await context.unifiedToApi.getWebhookSubscriptions(context.agentId);
|
|
1167
1716
|
if (!result.success) {
|
|
1168
|
-
|
|
1717
|
+
if (jsonOutput) {
|
|
1718
|
+
console.log(JSON.stringify({ error: result.error?.message || 'Failed to load triggers' }));
|
|
1719
|
+
}
|
|
1720
|
+
else {
|
|
1721
|
+
writeError(`❌ Failed to load triggers: ${result.error?.message}`);
|
|
1722
|
+
}
|
|
1169
1723
|
return;
|
|
1170
1724
|
}
|
|
1171
1725
|
const webhooks = result.data || [];
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1726
|
+
if (jsonOutput) {
|
|
1727
|
+
// Output as JSON for scripting
|
|
1728
|
+
console.log(JSON.stringify({
|
|
1729
|
+
triggers: webhooks.map(wh => ({
|
|
1730
|
+
id: wh.id,
|
|
1731
|
+
integrationType: wh.integrationType,
|
|
1732
|
+
objectType: wh.objectType,
|
|
1733
|
+
event: wh.event,
|
|
1734
|
+
webhookType: wh.webhookType,
|
|
1735
|
+
hookUrl: wh.hookUrl,
|
|
1736
|
+
status: wh.status,
|
|
1737
|
+
interval: wh.interval,
|
|
1738
|
+
connectionId: wh.connectionId,
|
|
1739
|
+
})),
|
|
1740
|
+
total: webhooks.length,
|
|
1741
|
+
}, null, 2));
|
|
1742
|
+
return;
|
|
1743
|
+
}
|
|
1744
|
+
console.log("\n" + "─".repeat(60));
|
|
1745
|
+
console.log("⚡ Triggers / Webhook Subscriptions");
|
|
1746
|
+
console.log("─".repeat(60) + "\n");
|
|
1175
1747
|
if (webhooks.length === 0) {
|
|
1176
|
-
console.log("ℹ️ No
|
|
1177
|
-
console.log("💡
|
|
1748
|
+
console.log("ℹ️ No triggers configured yet.\n");
|
|
1749
|
+
console.log("💡 Add triggers when connecting: lua integrations connect --triggers <events>");
|
|
1750
|
+
console.log(" Or manually: lua integrations webhooks create\n");
|
|
1178
1751
|
return;
|
|
1179
1752
|
}
|
|
1753
|
+
// Group by integration
|
|
1754
|
+
const byIntegration = new Map();
|
|
1180
1755
|
for (const wh of webhooks) {
|
|
1181
|
-
const
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1756
|
+
const key = wh.integrationType;
|
|
1757
|
+
if (!byIntegration.has(key))
|
|
1758
|
+
byIntegration.set(key, []);
|
|
1759
|
+
byIntegration.get(key).push(wh);
|
|
1760
|
+
}
|
|
1761
|
+
for (const [integration, integrationWebhooks] of byIntegration) {
|
|
1762
|
+
console.log(`📦 ${integration}`);
|
|
1763
|
+
for (const wh of integrationWebhooks) {
|
|
1764
|
+
const statusIcon = wh.status === 'active' ? '✅' : '⚪';
|
|
1765
|
+
const typeLabel = wh.webhookType === 'native' ? 'push' : 'poll';
|
|
1766
|
+
const isAgentTrigger = wh.hookUrl.includes('webhook/unifiedto');
|
|
1767
|
+
const mode = isAgentTrigger ? 'agent trigger' : 'custom URL';
|
|
1768
|
+
// Show webhook ID for delete operations
|
|
1769
|
+
console.log(` ${statusIcon} ${wh.objectType}.${wh.event} (${typeLabel}, ${mode})`);
|
|
1770
|
+
console.log(` ID: ${wh.id}`);
|
|
1771
|
+
if (!isAgentTrigger) {
|
|
1772
|
+
console.log(` URL: ${wh.hookUrl}`);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
console.log();
|
|
1187
1776
|
}
|
|
1188
|
-
console.log(
|
|
1777
|
+
console.log("─".repeat(60));
|
|
1778
|
+
console.log(`Total: ${webhooks.length} trigger(s)\n`);
|
|
1189
1779
|
}
|
|
1190
1780
|
catch (error) {
|
|
1191
|
-
|
|
1781
|
+
if (jsonOutput) {
|
|
1782
|
+
console.log(JSON.stringify({ error: error.message }));
|
|
1783
|
+
}
|
|
1784
|
+
else {
|
|
1785
|
+
writeError(`❌ Error: ${error.message}`);
|
|
1786
|
+
}
|
|
1192
1787
|
}
|
|
1193
1788
|
}
|
|
1194
1789
|
/**
|
|
@@ -1212,7 +1807,7 @@ async function webhooksCreateFlow(context, options) {
|
|
|
1212
1807
|
}
|
|
1213
1808
|
if (connections.length === 0) {
|
|
1214
1809
|
writeInfo("No connections available.");
|
|
1215
|
-
console.log("💡 Run 'lua integrations connect' to connect an
|
|
1810
|
+
console.log("💡 Run 'lua integrations connect' to connect an integration first.\n");
|
|
1216
1811
|
return;
|
|
1217
1812
|
}
|
|
1218
1813
|
// Step 2: Select connection
|
|
@@ -1258,8 +1853,8 @@ async function webhooksCreateFlow(context, options) {
|
|
|
1258
1853
|
return;
|
|
1259
1854
|
}
|
|
1260
1855
|
if (availableEvents.length === 0) {
|
|
1261
|
-
writeInfo(`No
|
|
1262
|
-
console.log("💡 This integration may not support
|
|
1856
|
+
writeInfo(`No trigger events available for ${selectedConnection.integrationName || selectedConnection.integrationType}.`);
|
|
1857
|
+
console.log("💡 This integration may not support triggers.\n");
|
|
1263
1858
|
return;
|
|
1264
1859
|
}
|
|
1265
1860
|
// Step 4: Select event
|
|
@@ -1274,7 +1869,7 @@ async function webhooksCreateFlow(context, options) {
|
|
|
1274
1869
|
}
|
|
1275
1870
|
}
|
|
1276
1871
|
else {
|
|
1277
|
-
console.log(`\nAvailable
|
|
1872
|
+
console.log(`\nAvailable trigger events for ${selectedConnection.integrationName || selectedConnection.integrationType}:\n`);
|
|
1278
1873
|
const eventAnswer = await safePrompt([
|
|
1279
1874
|
{
|
|
1280
1875
|
type: 'list',
|
|
@@ -1293,8 +1888,8 @@ async function webhooksCreateFlow(context, options) {
|
|
|
1293
1888
|
}
|
|
1294
1889
|
// Step 5: Get webhook URL
|
|
1295
1890
|
let hookUrl;
|
|
1296
|
-
if (options.
|
|
1297
|
-
hookUrl = options.
|
|
1891
|
+
if (options.hookUrl) {
|
|
1892
|
+
hookUrl = options.hookUrl;
|
|
1298
1893
|
}
|
|
1299
1894
|
else {
|
|
1300
1895
|
const webhookAnswer = await safePrompt([
|
|
@@ -1357,7 +1952,7 @@ async function webhooksCreateFlow(context, options) {
|
|
|
1357
1952
|
// Step 7: Confirmation (interactive only)
|
|
1358
1953
|
if (!options.connectionId) {
|
|
1359
1954
|
console.log("\n" + "─".repeat(60));
|
|
1360
|
-
console.log("📋
|
|
1955
|
+
console.log("📋 Trigger Summary");
|
|
1361
1956
|
console.log("─".repeat(60));
|
|
1362
1957
|
console.log(` Connection: ${selectedConnection.integrationName || selectedConnection.integrationType}`);
|
|
1363
1958
|
console.log(` Event: ${selectedEvent.objectType}.${selectedEvent.event} [${selectedEvent.webhookType}]`);
|
|
@@ -1369,7 +1964,7 @@ async function webhooksCreateFlow(context, options) {
|
|
|
1369
1964
|
{
|
|
1370
1965
|
type: 'confirm',
|
|
1371
1966
|
name: 'confirm',
|
|
1372
|
-
message: 'Create this
|
|
1967
|
+
message: 'Create this trigger?',
|
|
1373
1968
|
default: true
|
|
1374
1969
|
}
|
|
1375
1970
|
]);
|
|
@@ -1379,7 +1974,7 @@ async function webhooksCreateFlow(context, options) {
|
|
|
1379
1974
|
}
|
|
1380
1975
|
}
|
|
1381
1976
|
// Step 8: Create the webhook
|
|
1382
|
-
writeProgress("🔄 Creating
|
|
1977
|
+
writeProgress("🔄 Creating trigger...");
|
|
1383
1978
|
try {
|
|
1384
1979
|
const createResult = await context.unifiedToApi.createWebhookSubscription(context.agentId, {
|
|
1385
1980
|
connectionId: selectedConnection.id,
|
|
@@ -1389,22 +1984,32 @@ async function webhooksCreateFlow(context, options) {
|
|
|
1389
1984
|
interval,
|
|
1390
1985
|
});
|
|
1391
1986
|
if (!createResult.success || !createResult.data) {
|
|
1392
|
-
writeError(`❌ Failed to create
|
|
1987
|
+
writeError(`❌ Failed to create trigger: ${createResult.error?.message}`);
|
|
1393
1988
|
return;
|
|
1394
1989
|
}
|
|
1395
1990
|
const webhook = createResult.data;
|
|
1991
|
+
const isAgentTrigger = webhook.hookUrl.includes('webhook/unifiedto');
|
|
1992
|
+
const typeLabel = webhook.webhookType === 'native' ? 'push' : 'polling';
|
|
1396
1993
|
console.log("\n" + "─".repeat(60));
|
|
1397
|
-
writeSuccess("✅
|
|
1994
|
+
writeSuccess("✅ Trigger created!");
|
|
1398
1995
|
console.log("─".repeat(60));
|
|
1399
|
-
console.log(` ID: ${webhook.id}`);
|
|
1400
1996
|
console.log(` Integration: ${webhook.integrationType}`);
|
|
1401
|
-
console.log(` Event: ${webhook.objectType}.${webhook.event}
|
|
1402
|
-
console.log(`
|
|
1997
|
+
console.log(` Event: ${webhook.objectType}.${webhook.event}`);
|
|
1998
|
+
console.log(` Type: ${typeLabel}`);
|
|
1999
|
+
console.log(` Mode: ${isAgentTrigger ? 'Agent wake-up' : 'Custom URL'}`);
|
|
2000
|
+
if (!isAgentTrigger) {
|
|
2001
|
+
console.log(` URL: ${webhook.hookUrl}`);
|
|
2002
|
+
}
|
|
1403
2003
|
if (webhook.interval)
|
|
1404
|
-
console.log(`
|
|
2004
|
+
console.log(` Poll interval: ${formatInterval(webhook.interval)}`);
|
|
1405
2005
|
console.log(` Status: ${webhook.status}`);
|
|
1406
2006
|
console.log("─".repeat(60) + "\n");
|
|
1407
|
-
|
|
2007
|
+
if (isAgentTrigger) {
|
|
2008
|
+
console.log("💡 Your agent will now wake up when this event occurs.\n");
|
|
2009
|
+
}
|
|
2010
|
+
else {
|
|
2011
|
+
console.log("💡 Ensure your webhook endpoint is ready to receive events.\n");
|
|
2012
|
+
}
|
|
1408
2013
|
}
|
|
1409
2014
|
catch (error) {
|
|
1410
2015
|
writeError(`❌ Error: ${error.message}`);
|
|
@@ -1414,23 +2019,23 @@ async function webhooksCreateFlow(context, options) {
|
|
|
1414
2019
|
* Delete a webhook subscription (interactive)
|
|
1415
2020
|
*/
|
|
1416
2021
|
async function webhooksDeleteInteractive(context) {
|
|
1417
|
-
writeProgress("🔄 Loading
|
|
2022
|
+
writeProgress("🔄 Loading triggers...");
|
|
1418
2023
|
try {
|
|
1419
2024
|
const result = await context.unifiedToApi.getWebhookSubscriptions(context.agentId);
|
|
1420
2025
|
if (!result.success) {
|
|
1421
|
-
writeError(`❌ Failed to load
|
|
2026
|
+
writeError(`❌ Failed to load triggers: ${result.error?.message}`);
|
|
1422
2027
|
return;
|
|
1423
2028
|
}
|
|
1424
2029
|
const webhooks = result.data || [];
|
|
1425
2030
|
if (webhooks.length === 0) {
|
|
1426
|
-
console.log("\nℹ️ No
|
|
2031
|
+
console.log("\nℹ️ No triggers to delete.\n");
|
|
1427
2032
|
return;
|
|
1428
2033
|
}
|
|
1429
2034
|
const webhookAnswer = await safePrompt([
|
|
1430
2035
|
{
|
|
1431
2036
|
type: 'list',
|
|
1432
2037
|
name: 'webhook',
|
|
1433
|
-
message: 'Select a
|
|
2038
|
+
message: 'Select a trigger to delete:',
|
|
1434
2039
|
choices: webhooks.map(wh => {
|
|
1435
2040
|
// Show last part of URL for readability
|
|
1436
2041
|
const urlParts = (wh.hookUrl || '').split('/');
|
|
@@ -1449,7 +2054,7 @@ async function webhooksDeleteInteractive(context) {
|
|
|
1449
2054
|
{
|
|
1450
2055
|
type: 'confirm',
|
|
1451
2056
|
name: 'confirm',
|
|
1452
|
-
message: `Delete
|
|
2057
|
+
message: `Delete trigger for ${selectedWebhook?.objectType}.${selectedWebhook?.event}?`,
|
|
1453
2058
|
default: false
|
|
1454
2059
|
}
|
|
1455
2060
|
]);
|
|
@@ -1467,14 +2072,14 @@ async function webhooksDeleteInteractive(context) {
|
|
|
1467
2072
|
* Delete a webhook subscription (non-interactive)
|
|
1468
2073
|
*/
|
|
1469
2074
|
async function webhooksDeleteFlow(context, webhookId) {
|
|
1470
|
-
writeProgress("🔄 Deleting
|
|
2075
|
+
writeProgress("🔄 Deleting trigger...");
|
|
1471
2076
|
try {
|
|
1472
2077
|
const result = await context.unifiedToApi.deleteWebhookSubscription(context.agentId, webhookId);
|
|
1473
2078
|
if (result.success) {
|
|
1474
|
-
writeSuccess(`✅
|
|
2079
|
+
writeSuccess(`✅ Trigger deleted: ${webhookId}\n`);
|
|
1475
2080
|
}
|
|
1476
2081
|
else {
|
|
1477
|
-
writeError(`❌ Failed to delete
|
|
2082
|
+
writeError(`❌ Failed to delete trigger: ${result.error?.message}`);
|
|
1478
2083
|
}
|
|
1479
2084
|
}
|
|
1480
2085
|
catch (error) {
|
|
@@ -1758,21 +2363,30 @@ async function mcpDeactivateInteractive(context) {
|
|
|
1758
2363
|
function showUsage() {
|
|
1759
2364
|
console.log('\nUsage:');
|
|
1760
2365
|
console.log(' lua integrations Interactive integration management');
|
|
1761
|
-
console.log(' lua integrations connect Connect a new
|
|
2366
|
+
console.log(' lua integrations connect Connect a new integration (interactive)');
|
|
1762
2367
|
console.log(' lua integrations connect --integration <type> Connect a specific integration');
|
|
2368
|
+
console.log(' lua integrations connect --integration <type> --triggers <events> Connect with triggers');
|
|
1763
2369
|
console.log(' lua integrations update Update connection scopes (interactive)');
|
|
1764
2370
|
console.log(' lua integrations update --integration <type> Update scopes for a specific integration');
|
|
1765
|
-
console.log(' lua integrations list List connected
|
|
2371
|
+
console.log(' lua integrations list List connected integrations');
|
|
1766
2372
|
console.log(' lua integrations available List available integrations');
|
|
1767
|
-
console.log(' lua integrations
|
|
1768
|
-
console.log('
|
|
1769
|
-
console.log(' lua integrations
|
|
1770
|
-
console.log('
|
|
1771
|
-
console.log(' lua integrations webhooks
|
|
1772
|
-
console.log(' lua integrations webhooks
|
|
2373
|
+
console.log(' lua integrations info <type> Show integration details (scopes, triggers)');
|
|
2374
|
+
console.log(' lua integrations info <type> --json Output as JSON for scripting');
|
|
2375
|
+
console.log(' lua integrations disconnect --connection-id <id> Disconnect an integration');
|
|
2376
|
+
console.log('\nTriggers:');
|
|
2377
|
+
console.log(' lua integrations webhooks list List all triggers');
|
|
2378
|
+
console.log(' lua integrations webhooks events --connection <id> List available events for a connection');
|
|
2379
|
+
console.log(' lua integrations webhooks events --integration <type> List available events for an integration');
|
|
2380
|
+
console.log(' lua integrations webhooks create Create trigger (interactive)');
|
|
2381
|
+
console.log(' lua integrations webhooks create --connection <id> --object <type> --event <event> --hook-url <url>');
|
|
2382
|
+
console.log(' lua integrations webhooks delete --webhook-id <id> Delete a trigger');
|
|
1773
2383
|
console.log('\nMCP Server Management:');
|
|
1774
2384
|
console.log(' lua integrations mcp list List connections with MCP status');
|
|
1775
2385
|
console.log(' lua integrations mcp activate --connection <id> Activate MCP server');
|
|
1776
2386
|
console.log(' lua integrations mcp deactivate --connection <id> Deactivate MCP server');
|
|
2387
|
+
console.log('\nTrigger Options (use with connect):');
|
|
2388
|
+
console.log(' --triggers <events> Comma-separated triggers (e.g., task_task.created,task_task.updated)');
|
|
2389
|
+
console.log(' --custom-webhook Use custom URL instead of agent trigger');
|
|
2390
|
+
console.log(' --hook-url <url> Custom URL for triggers');
|
|
1777
2391
|
}
|
|
1778
2392
|
//# sourceMappingURL=integrations.js.map
|