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/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
- await listConnections(context);
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
- await disconnectIntegration(context, options.connectionId);
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 (options.hideSensitive !== void 0) {
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 = await context.unifiedToApi.getAvailableWebhookEventsByType(selectedIntegration.value);
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.getAuthUrl(selectedIntegration.value, {
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
- await finalizeConnection(context, selectedIntegration, result.connectionId, selectedScopes, hideSensitive, {
42313
- triggers: selectedTriggers,
42314
- webhookUrl,
42315
- isCustomWebhook
42316
- }, options.accountLabel);
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', 'webhooks'
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 connected integrations
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