lua-cli 3.26.0 → 3.27.0
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-exports.d.ts +26 -1
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +426 -19
- package/dist/index.js.map +1 -1
- package/docs/README.md +2 -2
- package/package.json +1 -1
- package/template/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22748,12 +22748,14 @@ var ALIAS_MAP = {
|
|
|
22748
22748
|
"available",
|
|
22749
22749
|
"info",
|
|
22750
22750
|
"disconnect",
|
|
22751
|
+
"convert",
|
|
22751
22752
|
"webhooks",
|
|
22752
22753
|
"mcp"
|
|
22753
22754
|
],
|
|
22754
22755
|
aliases: lowerKeys({
|
|
22755
22756
|
add: "connect",
|
|
22756
22757
|
link: "connect",
|
|
22758
|
+
claim: "convert",
|
|
22757
22759
|
edit: "update",
|
|
22758
22760
|
modify: "update",
|
|
22759
22761
|
ls: "list",
|
|
@@ -41446,6 +41448,109 @@ var UnifiedToApi = class extends HttpClient {
|
|
|
41446
41448
|
Authorization: `Bearer ${this.apiKey}`
|
|
41447
41449
|
});
|
|
41448
41450
|
}
|
|
41451
|
+
// ==================== Personal (user-owned) connections ====================
|
|
41452
|
+
//
|
|
41453
|
+
// The whole tier is agent-free: the acting principal is the API key's owner, resolved server-side
|
|
41454
|
+
// from the same /profile hop a browser session uses. None of these take an agentId — that is the
|
|
41455
|
+
// point, so the signatures don't pretend otherwise.
|
|
41456
|
+
/**
|
|
41457
|
+
* Lists the API key owner's personal connections
|
|
41458
|
+
* @returns Promise resolving to an ApiResponse containing personal connections
|
|
41459
|
+
*/
|
|
41460
|
+
async getUserConnections() {
|
|
41461
|
+
return this.httpGet("/developer/unifiedto/user-connection", {
|
|
41462
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
41463
|
+
});
|
|
41464
|
+
}
|
|
41465
|
+
/**
|
|
41466
|
+
* Gets a state-bound OAuth URL for a PERSONAL connection
|
|
41467
|
+
* @param integrationType - The integration type
|
|
41468
|
+
* @param options - Redirect pair, scopes and CSRF state
|
|
41469
|
+
* @returns Promise resolving to an ApiResponse containing the authorization URL
|
|
41470
|
+
*/
|
|
41471
|
+
async getUserAuthUrl(integrationType, options) {
|
|
41472
|
+
const params = new URLSearchParams();
|
|
41473
|
+
params.append("integrationType", integrationType);
|
|
41474
|
+
params.append("successRedirect", options.successRedirect);
|
|
41475
|
+
params.append("failureRedirect", options.failureRedirect);
|
|
41476
|
+
if (options.scopes && options.scopes.length > 0) {
|
|
41477
|
+
params.append("scopes", options.scopes.join(","));
|
|
41478
|
+
}
|
|
41479
|
+
params.append("state", options.state);
|
|
41480
|
+
return this.httpGet(`/developer/unifiedto/user-connection/auth-url/v2?${params.toString()}`, {
|
|
41481
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
41482
|
+
});
|
|
41483
|
+
}
|
|
41484
|
+
/**
|
|
41485
|
+
* Creates a PERSONAL connection from API-key credentials
|
|
41486
|
+
* @param params - Integration type and credential fields
|
|
41487
|
+
* @returns Promise resolving to an ApiResponse containing the new connection id
|
|
41488
|
+
*/
|
|
41489
|
+
async createUserTokenAuthConnection(params) {
|
|
41490
|
+
return this.httpPost("/developer/unifiedto/user-connection/token-auth", params, {
|
|
41491
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
41492
|
+
});
|
|
41493
|
+
}
|
|
41494
|
+
/**
|
|
41495
|
+
* Finalizes a PERSONAL connection: stores it, mounts it on every private agent of the owner's,
|
|
41496
|
+
* and seeds ingest triggers. Takes no displayName/hideSensitive/triggers — the user tier has none.
|
|
41497
|
+
* @param params - The connection id and the OAuth scopes that were consented to
|
|
41498
|
+
* @returns Promise resolving to an ApiResponse containing the agents it mounted on
|
|
41499
|
+
*/
|
|
41500
|
+
async finalizeUserConnection(params) {
|
|
41501
|
+
return this.httpPost("/developer/unifiedto/user-connection/finalize", params, {
|
|
41502
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
41503
|
+
});
|
|
41504
|
+
}
|
|
41505
|
+
/**
|
|
41506
|
+
* Converts an agent-owned connection you created into a personal one
|
|
41507
|
+
* @param connectionId - The connection ID
|
|
41508
|
+
* @returns Promise resolving to an ApiResponse containing the agents it mounted on
|
|
41509
|
+
*/
|
|
41510
|
+
async convertConnectionToUser(connectionId) {
|
|
41511
|
+
return this.httpPost("/developer/unifiedto/user-connection/convert", {
|
|
41512
|
+
connectionId
|
|
41513
|
+
}, {
|
|
41514
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
41515
|
+
});
|
|
41516
|
+
}
|
|
41517
|
+
/**
|
|
41518
|
+
* Disconnects a personal connection, sweeping every mount
|
|
41519
|
+
* @param connectionId - The connection ID
|
|
41520
|
+
* @param forgetMemories - Also purge what it added to memory (default true server-side)
|
|
41521
|
+
* @returns Promise resolving to an ApiResponse with the number of triggers removed
|
|
41522
|
+
*/
|
|
41523
|
+
async deleteUserConnection(connectionId, forgetMemories) {
|
|
41524
|
+
return this.httpDelete(`/developer/unifiedto/user-connection/${connectionId}?forgetMemories=${forgetMemories}`, {
|
|
41525
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
41526
|
+
});
|
|
41527
|
+
}
|
|
41528
|
+
/**
|
|
41529
|
+
* Pauses or resumes a personal connection across every agent it is mounted on
|
|
41530
|
+
* @param connectionId - The connection ID
|
|
41531
|
+
* @param status - 'paused' or 'active'
|
|
41532
|
+
* @returns Promise resolving to an ApiResponse with the resulting status
|
|
41533
|
+
*/
|
|
41534
|
+
async setUserConnectionStatus(connectionId, status) {
|
|
41535
|
+
return this.httpPatch(`/developer/unifiedto/user-connection/${connectionId}`, {
|
|
41536
|
+
status
|
|
41537
|
+
}, {
|
|
41538
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
41539
|
+
});
|
|
41540
|
+
}
|
|
41541
|
+
/**
|
|
41542
|
+
* Turns a personal connection's org-memory feed on or off
|
|
41543
|
+
* @param connectionId - The connection ID
|
|
41544
|
+
* @param memoryFeed - Whether new items from this source should be remembered
|
|
41545
|
+
* @returns Promise resolving to an ApiResponse with the resulting state
|
|
41546
|
+
*/
|
|
41547
|
+
async setUserConnectionMemoryFeed(connectionId, memoryFeed) {
|
|
41548
|
+
return this.httpPatch(`/developer/unifiedto/user-connection/${connectionId}/memory-feed`, {
|
|
41549
|
+
memoryFeed
|
|
41550
|
+
}, {
|
|
41551
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
41552
|
+
});
|
|
41553
|
+
}
|
|
41449
41554
|
};
|
|
41450
41555
|
|
|
41451
41556
|
// src/commands/integrations.ts
|
|
@@ -41467,7 +41572,9 @@ async function fetchAvailableIntegrations(unifiedToApi, agentId) {
|
|
|
41467
41572
|
authSupport: integration.authSupport,
|
|
41468
41573
|
oauthConfigured: integration.oauthConfigured,
|
|
41469
41574
|
oauthScopes: integration.oauthScopes,
|
|
41470
|
-
tokenFields: integration.tokenFields
|
|
41575
|
+
tokenFields: integration.tokenFields,
|
|
41576
|
+
orgOnly: integration.orgOnly,
|
|
41577
|
+
native: integration.native
|
|
41471
41578
|
}));
|
|
41472
41579
|
}
|
|
41473
41580
|
__name(fetchAvailableIntegrations, "fetchAvailableIntegrations");
|
|
@@ -41636,7 +41743,9 @@ async function executeNonInteractive12(context, action, cmdOptions) {
|
|
|
41636
41743
|
authMethod: cmdOptions?.authMethod,
|
|
41637
41744
|
scopes: cmdOptions?.scopes,
|
|
41638
41745
|
hideSensitive: cmdOptions?.hideSensitive === "true",
|
|
41746
|
+
hideSensitiveProvided: cmdOptions?.hideSensitive !== void 0,
|
|
41639
41747
|
accountLabel: cmdOptions?.accountLabel,
|
|
41748
|
+
scope: cmdOptions?.scope,
|
|
41640
41749
|
// Trigger options
|
|
41641
41750
|
triggers: cmdOptions?.triggers,
|
|
41642
41751
|
customWebhook: cmdOptions?.customWebhook === true,
|
|
@@ -41656,7 +41765,22 @@ async function executeNonInteractive12(context, action, cmdOptions) {
|
|
|
41656
41765
|
await updateConnectionFlow(context, options);
|
|
41657
41766
|
break;
|
|
41658
41767
|
case "list":
|
|
41659
|
-
|
|
41768
|
+
if (options.scope === "user") {
|
|
41769
|
+
await listUserConnections(context);
|
|
41770
|
+
} else if (options.scope === "all") {
|
|
41771
|
+
await listConnections(context);
|
|
41772
|
+
await listUserConnections(context);
|
|
41773
|
+
} else {
|
|
41774
|
+
await listConnections(context);
|
|
41775
|
+
}
|
|
41776
|
+
break;
|
|
41777
|
+
case "convert":
|
|
41778
|
+
if (!options.connectionId) {
|
|
41779
|
+
console.error("\u274C --connection-id is required for convert");
|
|
41780
|
+
console.log("\n\u{1F4A1} Run 'lua integrations list' to see connection IDs");
|
|
41781
|
+
throw new Error("--connection-id is required for convert");
|
|
41782
|
+
}
|
|
41783
|
+
await convertConnectionFlow(context, options.connectionId, cmdOptions?.force === true);
|
|
41660
41784
|
break;
|
|
41661
41785
|
case "available":
|
|
41662
41786
|
await listAvailableIntegrations(context);
|
|
@@ -41677,7 +41801,11 @@ async function executeNonInteractive12(context, action, cmdOptions) {
|
|
|
41677
41801
|
console.log("\n\u{1F4A1} Run 'lua integrations list' to see connection IDs");
|
|
41678
41802
|
throw new Error("--connection-id is required for disconnect");
|
|
41679
41803
|
}
|
|
41680
|
-
|
|
41804
|
+
if (options.scope === "user") {
|
|
41805
|
+
await disconnectUserConnection(context, options.connectionId);
|
|
41806
|
+
} else {
|
|
41807
|
+
await disconnectIntegration(context, options.connectionId);
|
|
41808
|
+
}
|
|
41681
41809
|
break;
|
|
41682
41810
|
case "webhooks":
|
|
41683
41811
|
await webhooksSubcommand(context, cmdOptions);
|
|
@@ -41716,6 +41844,10 @@ async function interactiveIntegrationsManagement(context) {
|
|
|
41716
41844
|
name: "\u{1F4CB} List connected integrations",
|
|
41717
41845
|
value: "list"
|
|
41718
41846
|
},
|
|
41847
|
+
{
|
|
41848
|
+
name: "\u{1F464} List my personal connections",
|
|
41849
|
+
value: "list-user"
|
|
41850
|
+
},
|
|
41719
41851
|
{
|
|
41720
41852
|
name: "\u{1F50D} View available integrations",
|
|
41721
41853
|
value: "available"
|
|
@@ -41751,6 +41883,9 @@ async function interactiveIntegrationsManagement(context) {
|
|
|
41751
41883
|
case "list":
|
|
41752
41884
|
await listConnections(context);
|
|
41753
41885
|
break;
|
|
41886
|
+
case "list-user":
|
|
41887
|
+
await listUserConnections(context);
|
|
41888
|
+
break;
|
|
41754
41889
|
case "available":
|
|
41755
41890
|
await listAvailableIntegrations(context);
|
|
41756
41891
|
break;
|
|
@@ -41913,17 +42048,83 @@ async function showIntegrationInfo(context, integrationType, jsonOutput = false)
|
|
|
41913
42048
|
}
|
|
41914
42049
|
}
|
|
41915
42050
|
__name(showIntegrationInfo, "showIntegrationInfo");
|
|
42051
|
+
function agentOnlyFlagsIn(options) {
|
|
42052
|
+
return [
|
|
42053
|
+
options.triggers && "--triggers",
|
|
42054
|
+
options.customWebhook && "--custom-webhook",
|
|
42055
|
+
options.hookUrl && "--hook-url",
|
|
42056
|
+
options.accountLabel && "--account-label",
|
|
42057
|
+
// NOT `options.hideSensitive !== undefined`: executeNonInteractive coerces that to a boolean for
|
|
42058
|
+
// every run, so its presence has to be tracked separately.
|
|
42059
|
+
options.hideSensitiveProvided && "--hide-sensitive"
|
|
42060
|
+
].filter((f) => typeof f === "string" && f.length > 0);
|
|
42061
|
+
}
|
|
42062
|
+
__name(agentOnlyFlagsIn, "agentOnlyFlagsIn");
|
|
42063
|
+
function normalizeScopeFlag(raw) {
|
|
42064
|
+
if (!raw) return void 0;
|
|
42065
|
+
const normalized = raw.toLowerCase();
|
|
42066
|
+
if (normalized !== "agent" && normalized !== "user") {
|
|
42067
|
+
console.error(`\u274C Invalid --scope: "${raw}". Use 'agent' or 'user'`);
|
|
42068
|
+
throw new Error("Invalid --scope");
|
|
42069
|
+
}
|
|
42070
|
+
return normalized;
|
|
42071
|
+
}
|
|
42072
|
+
__name(normalizeScopeFlag, "normalizeScopeFlag");
|
|
42073
|
+
async function resolveConnectScope(context, options) {
|
|
42074
|
+
const flagged = normalizeScopeFlag(options.scope);
|
|
42075
|
+
if (flagged) return flagged;
|
|
42076
|
+
const answer = await safePrompt([
|
|
42077
|
+
{
|
|
42078
|
+
type: "list",
|
|
42079
|
+
name: "scope",
|
|
42080
|
+
message: "Who should own this connection?",
|
|
42081
|
+
choices: [
|
|
42082
|
+
{
|
|
42083
|
+
name: `\u{1F916} This agent only \u2014 ${context.agentId} keeps the credential, and loses it when the agent goes`,
|
|
42084
|
+
value: "agent"
|
|
42085
|
+
},
|
|
42086
|
+
{
|
|
42087
|
+
name: "\u{1F464} Me \u2014 every private agent I own can use it, including ones I create later",
|
|
42088
|
+
value: "user"
|
|
42089
|
+
}
|
|
42090
|
+
]
|
|
42091
|
+
}
|
|
42092
|
+
]);
|
|
42093
|
+
return answer?.scope;
|
|
42094
|
+
}
|
|
42095
|
+
__name(resolveConnectScope, "resolveConnectScope");
|
|
41916
42096
|
async function connectIntegrationFlow(context, options = {}) {
|
|
42097
|
+
const scope = await resolveConnectScope(context, options);
|
|
42098
|
+
if (!scope) return;
|
|
42099
|
+
const userScope = scope === "user";
|
|
42100
|
+
if (userScope) {
|
|
42101
|
+
const unsupported = agentOnlyFlagsIn(options);
|
|
42102
|
+
if (unsupported.length > 0) {
|
|
42103
|
+
console.error(`\u274C ${unsupported.join(", ")} ${unsupported.length > 1 ? "are" : "is"} agent-scoped and cannot be used with --scope user.`);
|
|
42104
|
+
console.log("\u{1F4A1} Connect at agent scope, or drop the flag. Triggers can be added later on an agent connection.");
|
|
42105
|
+
throw new Error("Agent-scoped options cannot be used with --scope user");
|
|
42106
|
+
}
|
|
42107
|
+
}
|
|
41917
42108
|
writeProgress("\u{1F504} Fetching integrations...");
|
|
41918
42109
|
let availableIntegrations;
|
|
42110
|
+
let agentConnectedTypes = /* @__PURE__ */ new Set();
|
|
41919
42111
|
try {
|
|
41920
|
-
availableIntegrations = await fetchAvailableIntegrations(context.unifiedToApi, context.agentId);
|
|
42112
|
+
availableIntegrations = await fetchAvailableIntegrations(context.unifiedToApi, userScope ? void 0 : context.agentId);
|
|
42113
|
+
if (userScope) {
|
|
42114
|
+
const [personal, agentConnections] = await Promise.all([
|
|
42115
|
+
context.unifiedToApi.getUserConnections(),
|
|
42116
|
+
context.unifiedToApi.getConnections(context.agentId)
|
|
42117
|
+
]);
|
|
42118
|
+
const personalTypes = new Set((personal.data ?? []).map((c) => c.integrationType));
|
|
42119
|
+
agentConnectedTypes = new Set((agentConnections.data ?? []).filter((c) => c.status !== "deleted").map((c) => c.integrationType));
|
|
42120
|
+
availableIntegrations = availableIntegrations.filter((i) => !i.orgOnly && !i.native && !personalTypes.has(i.value));
|
|
42121
|
+
}
|
|
41921
42122
|
} catch (error) {
|
|
41922
42123
|
writeError(`\u274C Failed to fetch integrations: ${error.message}`);
|
|
41923
42124
|
return;
|
|
41924
42125
|
}
|
|
41925
42126
|
if (availableIntegrations.length === 0) {
|
|
41926
|
-
writeInfo("All available integrations are already connected (or none are enabled).");
|
|
42127
|
+
writeInfo(userScope ? "You have already connected every integration available as a personal connection." : "All available integrations are already connected (or none are enabled).");
|
|
41927
42128
|
console.log("\u{1F4A1} Use 'lua integrations update' to change scopes on an existing connection.\n");
|
|
41928
42129
|
return;
|
|
41929
42130
|
}
|
|
@@ -41932,8 +42133,8 @@ async function connectIntegrationFlow(context, options = {}) {
|
|
|
41932
42133
|
if (options.integration) {
|
|
41933
42134
|
selectedIntegration = availableIntegrations.find((i) => i.value === options.integration);
|
|
41934
42135
|
if (!selectedIntegration) {
|
|
41935
|
-
console.error(`\u274C Integration "${options.integration}" not found or already connected.`);
|
|
41936
|
-
console.log("\u{1F4A1} If already connected, use 'lua integrations update --integration " + options.integration + "' to change scopes.");
|
|
42136
|
+
console.error(userScope ? `\u274C "${options.integration}" is not available as a personal connection.` : `\u274C Integration "${options.integration}" not found or already connected.`);
|
|
42137
|
+
console.log(userScope ? "\u{1F4A1} You may already have it, or it's org-only / a memory-source connector. Run 'lua integrations list --scope user' to check." : "\u{1F4A1} If already connected, use 'lua integrations update --integration " + options.integration + "' to change scopes.");
|
|
41937
42138
|
if (availableIntegrations.length > 0) {
|
|
41938
42139
|
console.log("\nAvailable integrations to connect:");
|
|
41939
42140
|
availableIntegrations.forEach((i) => console.log(` - ${i.value} (${i.name})`));
|
|
@@ -41993,6 +42194,22 @@ async function connectIntegrationFlow(context, options = {}) {
|
|
|
41993
42194
|
console.log(` Categories: ${selectedIntegration.categories.join(", ")}`);
|
|
41994
42195
|
console.log(` Auth Support: ${selectedIntegration.authSupport}`);
|
|
41995
42196
|
console.log(` OAuth Configured: ${selectedIntegration.oauthConfigured ? "Yes" : "No"}`);
|
|
42197
|
+
console.log(` Owner: ${userScope ? "you (every private agent you own)" : `this agent (${context.agentId})`}`);
|
|
42198
|
+
if (userScope && agentConnectedTypes.has(selectedIntegration.value)) {
|
|
42199
|
+
writeInfo(`This agent already has its own ${selectedIntegration.name} connection. Connecting as yourself adds a SECOND credential rather than replacing it.`);
|
|
42200
|
+
const proceed = await safePrompt([
|
|
42201
|
+
{
|
|
42202
|
+
type: "confirm",
|
|
42203
|
+
name: "go",
|
|
42204
|
+
message: "Continue?",
|
|
42205
|
+
default: false
|
|
42206
|
+
}
|
|
42207
|
+
]);
|
|
42208
|
+
if (!proceed?.go) {
|
|
42209
|
+
console.log("\n\u{1F4A1} To re-home the existing one instead: lua integrations convert --connection-id <id>\n");
|
|
42210
|
+
return;
|
|
42211
|
+
}
|
|
42212
|
+
}
|
|
41996
42213
|
const canUseOAuth = selectedIntegration.oauthConfigured && [
|
|
41997
42214
|
"oauth",
|
|
41998
42215
|
"both"
|
|
@@ -42118,7 +42335,8 @@ Available scopes for ${selectedIntegration.name}:`);
|
|
|
42118
42335
|
console.log("\nYou will enter these on the Unified.to authorization page.\n");
|
|
42119
42336
|
}
|
|
42120
42337
|
let hideSensitive = false;
|
|
42121
|
-
if (
|
|
42338
|
+
if (userScope) {
|
|
42339
|
+
} else if (options.hideSensitive !== void 0) {
|
|
42122
42340
|
hideSensitive = options.hideSensitive;
|
|
42123
42341
|
} else {
|
|
42124
42342
|
const sensitiveAnswer = await safePrompt([
|
|
@@ -42136,8 +42354,14 @@ Available scopes for ${selectedIntegration.name}:`);
|
|
|
42136
42354
|
let selectedTriggers = [];
|
|
42137
42355
|
let webhookUrl = AGENT_WEBHOOK_URL;
|
|
42138
42356
|
let isCustomWebhook = false;
|
|
42357
|
+
if (userScope) {
|
|
42358
|
+
writeInfo("Triggers are agent-owned \u2014 skipping. Add them later on an agent connection.");
|
|
42359
|
+
}
|
|
42139
42360
|
try {
|
|
42140
|
-
const eventsResult =
|
|
42361
|
+
const eventsResult = userScope ? {
|
|
42362
|
+
success: false,
|
|
42363
|
+
data: void 0
|
|
42364
|
+
} : await context.unifiedToApi.getAvailableWebhookEventsByType(selectedIntegration.value);
|
|
42141
42365
|
if (eventsResult.success && eventsResult.data && eventsResult.data.length > 0) {
|
|
42142
42366
|
const availableEvents = eventsResult.data;
|
|
42143
42367
|
if (options.customWebhook || options.hookUrl) {
|
|
@@ -42276,7 +42500,12 @@ Available triggers for ${selectedIntegration.name}:`);
|
|
|
42276
42500
|
}
|
|
42277
42501
|
writeProgress("\u{1F504} Preparing authorization...");
|
|
42278
42502
|
const state = createOAuthState();
|
|
42279
|
-
const authUrlResult = await context.unifiedToApi.
|
|
42503
|
+
const authUrlResult = userScope ? await context.unifiedToApi.getUserAuthUrl(selectedIntegration.value, {
|
|
42504
|
+
successRedirect: CALLBACK_URL,
|
|
42505
|
+
failureRedirect: CALLBACK_URL,
|
|
42506
|
+
scopes: authMethod === "oauth" ? selectedScopes : void 0,
|
|
42507
|
+
state
|
|
42508
|
+
}) : await context.unifiedToApi.getAuthUrl(selectedIntegration.value, {
|
|
42280
42509
|
agentId: context.agentId,
|
|
42281
42510
|
successRedirect: CALLBACK_URL,
|
|
42282
42511
|
failureRedirect: CALLBACK_URL,
|
|
@@ -42309,11 +42538,15 @@ Integration: ${selectedIntegration.name}`);
|
|
|
42309
42538
|
const result = await callbackPromise;
|
|
42310
42539
|
if (result.success && result.connectionId) {
|
|
42311
42540
|
writeSuccess("\n\u2705 Authorization successful!");
|
|
42312
|
-
|
|
42313
|
-
|
|
42314
|
-
|
|
42315
|
-
|
|
42316
|
-
|
|
42541
|
+
if (userScope) {
|
|
42542
|
+
await finalizeUserConnection(context, selectedIntegration, result.connectionId, selectedScopes);
|
|
42543
|
+
} else {
|
|
42544
|
+
await finalizeConnection(context, selectedIntegration, result.connectionId, selectedScopes, hideSensitive, {
|
|
42545
|
+
triggers: selectedTriggers,
|
|
42546
|
+
webhookUrl,
|
|
42547
|
+
isCustomWebhook
|
|
42548
|
+
}, options.accountLabel);
|
|
42549
|
+
}
|
|
42317
42550
|
} else {
|
|
42318
42551
|
writeError(`
|
|
42319
42552
|
\u274C Authorization failed: ${result.error || "Unknown error"}`);
|
|
@@ -42443,6 +42676,140 @@ async function finalizeConnection(context, integration, connectionId, scopes, hi
|
|
|
42443
42676
|
});
|
|
42444
42677
|
}
|
|
42445
42678
|
__name(finalizeConnection, "finalizeConnection");
|
|
42679
|
+
async function finalizeUserConnection(context, integration, connectionId, scopes) {
|
|
42680
|
+
writeProgress(`\u{1F504} Setting up ${integration.name} connection...`);
|
|
42681
|
+
const result = await context.unifiedToApi.finalizeUserConnection({
|
|
42682
|
+
connectionId,
|
|
42683
|
+
scopes: scopes.length > 0 ? scopes : void 0
|
|
42684
|
+
});
|
|
42685
|
+
if (!result.success || !result.data) {
|
|
42686
|
+
writeError(`\u274C Failed to finalize connection: ${result.error?.message || "Unknown error"}`);
|
|
42687
|
+
return;
|
|
42688
|
+
}
|
|
42689
|
+
const { mountedAgentIds, webhooks, failedWebhooks } = result.data;
|
|
42690
|
+
console.log("\n" + "\u2500".repeat(60));
|
|
42691
|
+
console.log("\u{1F389} Connection Established!");
|
|
42692
|
+
console.log("\u2500".repeat(60));
|
|
42693
|
+
console.log(`
|
|
42694
|
+
Integration: ${integration.name}`);
|
|
42695
|
+
console.log(` Connection ID: ${connectionId}`);
|
|
42696
|
+
console.log(` Owner: you`);
|
|
42697
|
+
console.log(`
|
|
42698
|
+
\u{1F916} Available on ${mountedAgentIds.length} agent(s):`);
|
|
42699
|
+
if (mountedAgentIds.length === 0) {
|
|
42700
|
+
console.log(" (none yet \u2014 you have no private agents)");
|
|
42701
|
+
} else {
|
|
42702
|
+
mountedAgentIds.forEach((agentId) => {
|
|
42703
|
+
console.log(` \u2022 ${agentId}${agentId === context.agentId ? " \u2190 this project" : ""}`);
|
|
42704
|
+
});
|
|
42705
|
+
}
|
|
42706
|
+
console.log(" Private agents you create later are added automatically.");
|
|
42707
|
+
console.log(" Publishing an agent removes its access.");
|
|
42708
|
+
if (scopes.length > 0) {
|
|
42709
|
+
console.log(`
|
|
42710
|
+
\u{1F511} Permissions (${scopes.length}):`);
|
|
42711
|
+
scopes.forEach((scopeName) => {
|
|
42712
|
+
const scopeInfo = integration.oauthScopes?.find((sc) => sc.unifiedScope === scopeName);
|
|
42713
|
+
console.log(` \u2022 ${scopeInfo?.friendlyLabel || scopeName}`);
|
|
42714
|
+
});
|
|
42715
|
+
} else {
|
|
42716
|
+
console.log(`
|
|
42717
|
+
\u{1F511} Permissions: Default`);
|
|
42718
|
+
}
|
|
42719
|
+
if (webhooks.length > 0) {
|
|
42720
|
+
console.log(`
|
|
42721
|
+
\u{1F9E0} Memory sources seeded: ${webhooks.length}`);
|
|
42722
|
+
}
|
|
42723
|
+
if (failedWebhooks && failedWebhooks.length > 0) {
|
|
42724
|
+
failedWebhooks.forEach((t) => console.log(` \u2717 ${t.objectType}.${t.event}: ${t.error}`));
|
|
42725
|
+
}
|
|
42726
|
+
console.log("\n" + "\u2500".repeat(60));
|
|
42727
|
+
console.log(`\u2705 ${integration.name} connected as you.`);
|
|
42728
|
+
console.log(` Manage it with: lua integrations list --scope user
|
|
42729
|
+
`);
|
|
42730
|
+
trackEvent("cli_integration_connected", {
|
|
42731
|
+
integration_type: integration.value,
|
|
42732
|
+
integration_name: integration.name,
|
|
42733
|
+
scope: "user",
|
|
42734
|
+
mounted_agents: mountedAgentIds.length
|
|
42735
|
+
});
|
|
42736
|
+
}
|
|
42737
|
+
__name(finalizeUserConnection, "finalizeUserConnection");
|
|
42738
|
+
async function listUserConnections(context) {
|
|
42739
|
+
writeProgress("\u{1F504} Loading your connections...");
|
|
42740
|
+
try {
|
|
42741
|
+
const result = await context.unifiedToApi.getUserConnections();
|
|
42742
|
+
const connections = result.success ? result.data || [] : [];
|
|
42743
|
+
console.log("\n" + "=".repeat(60));
|
|
42744
|
+
console.log("\u{1F464} Your Personal Connections");
|
|
42745
|
+
console.log("=".repeat(60) + "\n");
|
|
42746
|
+
if (connections.length === 0) {
|
|
42747
|
+
console.log("\u2139\uFE0F You have no personal connections yet.");
|
|
42748
|
+
console.log("\u{1F4A1} Run 'lua integrations connect --scope user' to connect one.\n");
|
|
42749
|
+
return;
|
|
42750
|
+
}
|
|
42751
|
+
for (const connection of connections) {
|
|
42752
|
+
const statusIcon = connection.status === "unhealthy" ? "\u{1F534}" : connection.status === "paused" ? "\u23F8\uFE0F" : "\u{1F7E2}";
|
|
42753
|
+
const mounted = connection.mountedAgentIds || [];
|
|
42754
|
+
console.log(`${statusIcon} ${connection.displayName || connection.integrationType}`);
|
|
42755
|
+
console.log(` Connection: ${connection.connectionId}`);
|
|
42756
|
+
console.log(` Status: ${connection.status}`);
|
|
42757
|
+
console.log(` Available on: ${mounted.length} agent(s)${mounted.includes(context.agentId) ? " (including this project)" : ""}`);
|
|
42758
|
+
if (connection.removedAgentIds && connection.removedAgentIds.length > 0) {
|
|
42759
|
+
console.log(` Removed from: ${connection.removedAgentIds.length} agent(s)`);
|
|
42760
|
+
}
|
|
42761
|
+
console.log(` Memory feed: ${connection.memoryFeed ? "on" : "off"}`);
|
|
42762
|
+
if (connection.createdAt) {
|
|
42763
|
+
console.log(` Connected: ${new Date(connection.createdAt).toLocaleDateString()}`);
|
|
42764
|
+
}
|
|
42765
|
+
console.log();
|
|
42766
|
+
}
|
|
42767
|
+
console.log("=".repeat(60));
|
|
42768
|
+
console.log(`Total: ${connections.length} personal connection(s)
|
|
42769
|
+
`);
|
|
42770
|
+
} catch (error) {
|
|
42771
|
+
writeError(`\u274C Error loading your connections: ${error.message}`);
|
|
42772
|
+
}
|
|
42773
|
+
}
|
|
42774
|
+
__name(listUserConnections, "listUserConnections");
|
|
42775
|
+
async function convertConnectionFlow(context, connectionId, force = false) {
|
|
42776
|
+
if (!force) {
|
|
42777
|
+
const ok = await safePrompt([
|
|
42778
|
+
{
|
|
42779
|
+
type: "confirm",
|
|
42780
|
+
name: "go",
|
|
42781
|
+
message: `Make ${connectionId} yours? It spreads to every private agent you own, and there is no way to convert it back.`,
|
|
42782
|
+
default: false
|
|
42783
|
+
}
|
|
42784
|
+
]);
|
|
42785
|
+
if (!ok?.go) {
|
|
42786
|
+
console.log("\nNothing changed.\n");
|
|
42787
|
+
return;
|
|
42788
|
+
}
|
|
42789
|
+
}
|
|
42790
|
+
writeProgress("\u{1F504} Making this connection yours...");
|
|
42791
|
+
const result = await context.unifiedToApi.convertConnectionToUser(connectionId);
|
|
42792
|
+
if (!result.success || !result.data) {
|
|
42793
|
+
writeError(`\u274C Failed to convert connection: ${result.error?.message || "Unknown error"}`);
|
|
42794
|
+
console.log("\u{1F4A1} Only a private-agent connection you created yourself can become personal.");
|
|
42795
|
+
console.log(" A connection shared with the workspace must stop being shared first.\n");
|
|
42796
|
+
return;
|
|
42797
|
+
}
|
|
42798
|
+
const { mountedAgentIds } = result.data;
|
|
42799
|
+
writeSuccess(`
|
|
42800
|
+
\u2705 ${connectionId} is now yours.`);
|
|
42801
|
+
console.log(`
|
|
42802
|
+
\u{1F916} Available on ${mountedAgentIds.length} agent(s):`);
|
|
42803
|
+
mountedAgentIds.forEach((agentId) => {
|
|
42804
|
+
console.log(` \u2022 ${agentId}${agentId === context.agentId ? " \u2190 this project" : ""}`);
|
|
42805
|
+
});
|
|
42806
|
+
console.log(" Private agents you create later are added automatically.\n");
|
|
42807
|
+
trackEvent("cli_integration_converted", {
|
|
42808
|
+
connection_id: connectionId,
|
|
42809
|
+
mounted_agents: mountedAgentIds.length
|
|
42810
|
+
});
|
|
42811
|
+
}
|
|
42812
|
+
__name(convertConnectionFlow, "convertConnectionFlow");
|
|
42446
42813
|
async function listConnections(context) {
|
|
42447
42814
|
writeProgress("\u{1F504} Loading connections...");
|
|
42448
42815
|
try {
|
|
@@ -42490,6 +42857,37 @@ async function listConnections(context) {
|
|
|
42490
42857
|
}
|
|
42491
42858
|
}
|
|
42492
42859
|
__name(listConnections, "listConnections");
|
|
42860
|
+
async function disconnectUserConnection(context, connectionId) {
|
|
42861
|
+
const forget = await safePrompt([
|
|
42862
|
+
{
|
|
42863
|
+
type: "confirm",
|
|
42864
|
+
name: "forget",
|
|
42865
|
+
message: "Also forget what this connection already added to memory? (it is kept otherwise)",
|
|
42866
|
+
default: false
|
|
42867
|
+
}
|
|
42868
|
+
]);
|
|
42869
|
+
if (!forget) return;
|
|
42870
|
+
writeProgress("\u{1F504} Disconnecting... (please wait, do not close this window)");
|
|
42871
|
+
const result = await context.unifiedToApi.deleteUserConnection(connectionId, forget.forget);
|
|
42872
|
+
if (!result.success) {
|
|
42873
|
+
writeError(`\u274C Failed to disconnect: ${result.error?.message || "Unknown error"}`);
|
|
42874
|
+
return;
|
|
42875
|
+
}
|
|
42876
|
+
writeSuccess("\u2705 Personal connection disconnected from every agent it was on.");
|
|
42877
|
+
if (result.data?.deletedWebhooksCount) {
|
|
42878
|
+
console.log(` \u2713 Deleted ${result.data.deletedWebhooksCount} memory source(s)`);
|
|
42879
|
+
}
|
|
42880
|
+
if (!forget.forget) {
|
|
42881
|
+
console.log(" \u2139\uFE0F What it already added to memory was kept.");
|
|
42882
|
+
}
|
|
42883
|
+
console.log();
|
|
42884
|
+
trackEvent("cli_integration_disconnected", {
|
|
42885
|
+
scope: "user",
|
|
42886
|
+
webhooks_deleted: result.data?.deletedWebhooksCount || 0,
|
|
42887
|
+
forget_memories: forget.forget
|
|
42888
|
+
});
|
|
42889
|
+
}
|
|
42890
|
+
__name(disconnectUserConnection, "disconnectUserConnection");
|
|
42493
42891
|
async function disconnectIntegration(context, connectionId) {
|
|
42494
42892
|
writeProgress(`\u{1F504} Disconnecting... (please wait, do not close this window)`);
|
|
42495
42893
|
try {
|
|
@@ -47036,16 +47434,19 @@ Examples:
|
|
|
47036
47434
|
$ lua mcp deactivate --server-name api-server Deactivate a server
|
|
47037
47435
|
$ lua mcp delete --server-name old-server Delete a server
|
|
47038
47436
|
`).action(mcpCommand);
|
|
47039
|
-
program2.command("integrations [action] [subaction]").description("\u{1F517} Connect third-party integrations via Unified.to").option("--integration <type>", "Integration type (e.g., linear, googlecalendar)").option("--auth-method <method>", "Authentication method: 'oauth' or 'token'").option("--scopes <scopes>", "Comma-separated OAuth scopes (or 'all' for all scopes)").option("--hide-sensitive <bool>", "Hide sensitive data from MCP tools (default: true)").option("--account-label <label>", "Account label used to distinguish multiple connected accounts").option("--connection-id <id>", "Connection ID to disconnect or pause/resume").option("--connection <id>", "Connection ID for trigger").option("--webhook-id <id>", "Trigger ID to delete/pause/resume").option("--object <type>", "Object type for webhook (e.g., task_task, calendar_event)").option("--event <type>", "Event type for webhook: created, updated, or deleted").option("--hook-url <url>", "Custom webhook URL (default: agent trigger)").option("--interval <minutes>", "Polling interval for virtual webhooks (60, 120, 240, 480, 720, 1440, 2880)").option("--triggers <events>", "Comma-separated triggers (e.g., task_task.created,task_task.updated)").option("--custom-webhook", "Use custom webhook URL instead of agent trigger").option("--json", "Output as JSON (for info and webhooks events commands)").option("--reason <text>", "Optional reason for pausing a trigger").addHelpText("after", `
|
|
47437
|
+
program2.command("integrations [action] [subaction]").description("\u{1F517} Connect third-party integrations via Unified.to").option("--integration <type>", "Integration type (e.g., linear, googlecalendar)").option("--auth-method <method>", "Authentication method: 'oauth' or 'token'").option("--scopes <scopes>", "Comma-separated OAuth scopes (or 'all' for all scopes)").option("--hide-sensitive <bool>", "Hide sensitive data from MCP tools (default: true)").option("--account-label <label>", "Account label used to distinguish multiple connected accounts").option("--scope <scope>", "Who owns the connection: 'agent' (default) or 'user'").option("--force", "Skip the confirmation prompt (for convert)").option("--connection-id <id>", "Connection ID to disconnect, convert or pause/resume").option("--connection <id>", "Connection ID for trigger").option("--webhook-id <id>", "Trigger ID to delete/pause/resume").option("--object <type>", "Object type for webhook (e.g., task_task, calendar_event)").option("--event <type>", "Event type for webhook: created, updated, or deleted").option("--hook-url <url>", "Custom webhook URL (default: agent trigger)").option("--interval <minutes>", "Polling interval for virtual webhooks (60, 120, 240, 480, 720, 1440, 2880)").option("--triggers <events>", "Comma-separated triggers (e.g., task_task.created,task_task.updated)").option("--custom-webhook", "Use custom webhook URL instead of agent trigger").option("--json", "Output as JSON (for info and webhooks events commands)").option("--reason <text>", "Optional reason for pausing a trigger").addHelpText("after", `
|
|
47040
47438
|
Arguments:
|
|
47041
|
-
action Optional: 'connect', 'update', 'list', 'available', 'info', 'disconnect',
|
|
47042
|
-
(alias: 'triggers'), or 'mcp'
|
|
47439
|
+
action Optional: 'connect', 'update', 'list', 'available', 'info', 'disconnect',
|
|
47440
|
+
'convert', 'webhooks' (alias: 'triggers'), or 'mcp'
|
|
47043
47441
|
subaction For info: <integration-type>
|
|
47044
47442
|
For webhooks/triggers: 'list', 'events', 'create', 'delete', 'pause', or 'resume'
|
|
47045
47443
|
For mcp: 'list', 'activate', or 'deactivate'
|
|
47046
47444
|
|
|
47047
47445
|
Options:
|
|
47048
47446
|
--integration <type> Integration type (linear, googlecalendar, hubspot, etc.)
|
|
47447
|
+
--scope <scope> Who owns the connection: 'agent' (default) or 'user'. 'user' is a personal
|
|
47448
|
+
connection \u2014 every private agent you own can use it, including ones you
|
|
47449
|
+
create later. Publishing an agent removes its access.
|
|
47049
47450
|
--auth-method <method> Authentication method: 'oauth' or 'token'
|
|
47050
47451
|
--scopes <scopes> Comma-separated OAuth scopes, or 'all' for all available scopes
|
|
47051
47452
|
--hide-sensitive <bool> Hide sensitive data from MCP tools (default: true, use 'false' to show)
|
|
@@ -47063,6 +47464,7 @@ Webhook/Trigger Options:
|
|
|
47063
47464
|
--interval <minutes> Polling interval for virtual webhooks (60=1h, 120=2h, etc.)
|
|
47064
47465
|
|
|
47065
47466
|
Note: Only 1 connection per integration type is allowed per agent.
|
|
47467
|
+
Note: Triggers, account labels and --hide-sensitive are agent-scoped; they don't apply to --scope user.
|
|
47066
47468
|
|
|
47067
47469
|
Examples:
|
|
47068
47470
|
$ lua integrations Interactive management
|
|
@@ -47072,7 +47474,12 @@ Examples:
|
|
|
47072
47474
|
$ lua integrations connect --integration linear --triggers task_task.created,task_task.updated
|
|
47073
47475
|
$ lua integrations update --integration linear --scopes all Update Linear scopes
|
|
47074
47476
|
$ lua integrations available List available integrations
|
|
47075
|
-
$ lua integrations list List
|
|
47477
|
+
$ lua integrations list List this agent's connections
|
|
47478
|
+
$ lua integrations connect --scope user Connect as yourself (all your private agents)
|
|
47479
|
+
$ lua integrations list --scope user List your personal connections
|
|
47480
|
+
$ lua integrations list --scope all List both
|
|
47481
|
+
$ lua integrations convert --connection-id abc123 Re-home an agent connection to yourself
|
|
47482
|
+
$ lua integrations convert --connection-id abc123 --force Same, without the confirmation
|
|
47076
47483
|
$ lua integrations info linear Show Linear integration details
|
|
47077
47484
|
$ lua integrations info linear --json Output as JSON (for scripting)
|
|
47078
47485
|
$ lua integrations disconnect --connection-id abc123 Disconnect an integration
|