lua-cli 3.22.0 → 3.23.1

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
@@ -879,6 +879,31 @@ function isAllowedReviewableExecuteTool(tool) {
879
879
  function isReviewableMcpSendTool(tool) {
880
880
  return tool.length > REVIEWABLE_MCP_SEND_TOOL_SUFFIX.length && tool.endsWith(REVIEWABLE_MCP_SEND_TOOL_SUFFIX);
881
881
  }
882
+ function mcpActionTokens(action) {
883
+ return action.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
884
+ }
885
+ function isReviewableMcpDraftTool(tool) {
886
+ const sep4 = tool.indexOf("_");
887
+ if (sep4 <= 0 || sep4 >= tool.length - 1) return false;
888
+ const action = tool.slice(sep4 + 1);
889
+ const tokens = mcpActionTokens(action);
890
+ if (!tokens.includes("draft") && !tokens.includes("drafts")) return false;
891
+ return !MCP_TOOL_READ_VERB_RE.test(action);
892
+ }
893
+ function isMcpDraftCreateTool(tool) {
894
+ if (!isReviewableMcpDraftTool(tool)) return false;
895
+ const tokens = mcpActionTokens(tool.slice(tool.indexOf("_") + 1));
896
+ return tokens[0] === "draft" || tokens.some((t) => MCP_DRAFT_CREATE_VERBS.has(t));
897
+ }
898
+ function mcpSendSiblingForDraftTool(tool, availableToolIds) {
899
+ const candidates = [
900
+ ...availableToolIds
901
+ ].filter((id) => id.endsWith(REVIEWABLE_MCP_SEND_TOOL_SUFFIX)).map((id) => ({
902
+ id,
903
+ serverName: id.slice(0, -REVIEWABLE_MCP_SEND_TOOL_SUFFIX.length)
904
+ })).filter(({ serverName }) => tool.startsWith(`${serverName}_`)).sort((a, b) => b.serverName.length - a.serverName.length);
905
+ return candidates[0]?.id;
906
+ }
882
907
  function isReviewableExecuteTool(tool) {
883
908
  return isAllowedReviewableExecuteTool(tool) || isReviewableMcpSendTool(tool);
884
909
  }
@@ -1115,10 +1140,40 @@ function foldRichPartsIntoMessages(messages, records, makeMessage, sameTurnGroup
1115
1140
  while (next < synthetic.length) combined.push(synthetic[next++].message);
1116
1141
  return mergeRichPartMirrorMessages(combined, sameTurnGroup);
1117
1142
  }
1143
+ function isDesktopFileCommandName(value) {
1144
+ return typeof value === "string" && DESKTOP_FILE_COMMAND_SET.has(value);
1145
+ }
1146
+ function isDesktopFileSessionId(value) {
1147
+ return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
1148
+ }
1149
+ function resolveRequireToolApproval(rules) {
1150
+ const raw = rules?.requireToolApproval ?? rules?.requireApproval;
1151
+ if (raw === void 0 || raw === null) return void 0;
1152
+ const list = Array.isArray(raw) ? raw : [
1153
+ raw
1154
+ ];
1155
+ return list.filter((t) => typeof t === "string");
1156
+ }
1118
1157
  function buildDefaultPersona(agentName) {
1119
1158
  return DEFAULT_PERSONA_GUIDE.replace(AGENT_NAME_TOKEN, () => agentName || "My Agent");
1120
1159
  }
1121
- var __defProp2, __name2, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, REASONING_EFFORT_VALUES, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, AGENT_LOG_SOURCES, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema;
1160
+ function resolveLuaJobTimeoutSeconds(timeout) {
1161
+ const resolved = timeout ?? LUA_JOB_DEFAULT_TIMEOUT_SECONDS;
1162
+ if (!Number.isInteger(resolved)) {
1163
+ throw new TypeError("LuaJob `timeout` must be an integer number of seconds.");
1164
+ }
1165
+ if (resolved < LUA_JOB_MIN_TIMEOUT_SECONDS || resolved > LUA_JOB_MAX_TIMEOUT_SECONDS) {
1166
+ throw new RangeError(`LuaJob \`timeout\` must be between ${LUA_JOB_MIN_TIMEOUT_SECONDS} and ${LUA_JOB_MAX_TIMEOUT_SECONDS} seconds.`);
1167
+ }
1168
+ return resolved;
1169
+ }
1170
+ function normalizeLuaJobExecutionTimeoutSeconds(timeout) {
1171
+ if (typeof timeout !== "number" || !Number.isFinite(timeout)) {
1172
+ return LUA_JOB_DEFAULT_TIMEOUT_SECONDS;
1173
+ }
1174
+ return Math.min(Math.max(timeout, LUA_JOB_MIN_TIMEOUT_SECONDS), LUA_JOB_MAX_TIMEOUT_SECONDS);
1175
+ }
1176
+ var __defProp2, __name2, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, MCP_TOOL_READ_VERB_RE, MCP_DRAFT_CREATE_VERBS, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, DESKTOP_FILE_COMMANDS, DESKTOP_FILE_COMMAND_SET, REASONING_EFFORT_VALUES, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, AGENT_LOG_SOURCES, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema, LUA_JOB_DEFAULT_TIMEOUT_SECONDS, LUA_JOB_MIN_TIMEOUT_SECONDS, LUA_JOB_MAX_TIMEOUT_SECONDS;
1122
1177
  var init_dist = __esm({
1123
1178
  "../shared-types/dist/index.mjs"() {
1124
1179
  "use strict";
@@ -1152,6 +1207,26 @@ var init_dist = __esm({
1152
1207
  REVIEWABLE_MCP_SEND_TOOL_SUFFIX = "_create_messaging_message";
1153
1208
  __name(isReviewableMcpSendTool, "isReviewableMcpSendTool");
1154
1209
  __name2(isReviewableMcpSendTool, "isReviewableMcpSendTool");
1210
+ MCP_TOOL_READ_VERB_RE = /(?:^|_)(list|get|search|read|fetch|find|query|describe|count|retrieve|lookup|show|view)(_|[A-Z0-9]|$)/;
1211
+ __name(mcpActionTokens, "mcpActionTokens");
1212
+ __name2(mcpActionTokens, "mcpActionTokens");
1213
+ __name(isReviewableMcpDraftTool, "isReviewableMcpDraftTool");
1214
+ __name2(isReviewableMcpDraftTool, "isReviewableMcpDraftTool");
1215
+ MCP_DRAFT_CREATE_VERBS = /* @__PURE__ */ new Set([
1216
+ "create",
1217
+ "compose",
1218
+ "make",
1219
+ "new",
1220
+ "save",
1221
+ "add",
1222
+ "write",
1223
+ "stage",
1224
+ "prepare"
1225
+ ]);
1226
+ __name(isMcpDraftCreateTool, "isMcpDraftCreateTool");
1227
+ __name2(isMcpDraftCreateTool, "isMcpDraftCreateTool");
1228
+ __name(mcpSendSiblingForDraftTool, "mcpSendSiblingForDraftTool");
1229
+ __name2(mcpSendSiblingForDraftTool, "mcpSendSiblingForDraftTool");
1155
1230
  __name(isReviewableExecuteTool, "isReviewableExecuteTool");
1156
1231
  __name2(isReviewableExecuteTool, "isReviewableExecuteTool");
1157
1232
  NON_INTERACTIVE_CHANNELS = [
@@ -1184,11 +1259,11 @@ var init_dist = __esm({
1184
1259
  },
1185
1260
  {
1186
1261
  name: "session_open",
1187
- description: "Open/attach a browser session (its own cookies/auth). Args: url?, headed?, profile?, confirmActions?."
1262
+ description: "Open/attach a browser session (its own cookies/auth). Args: url?, headed?, profile?, confirmActions?. Only use the browser when the user explicitly asked for it, or said yes when you asked. For reading a page's content use fetchUrl first."
1188
1263
  },
1189
1264
  {
1190
1265
  name: "navigate",
1191
- description: "Navigate the session to a URL. Args: url, waitUntil?."
1266
+ description: "Navigate the session to a URL. Args: url, waitUntil?. Only use the browser when the user explicitly asked for it, or said yes when you asked. For reading a page's content use fetchUrl first."
1192
1267
  },
1193
1268
  {
1194
1269
  name: "back",
@@ -1383,6 +1458,27 @@ var init_dist = __esm({
1383
1458
  }
1384
1459
  ];
1385
1460
  BROWSER_COMMAND_NAMES = BROWSER_COMMANDS.map((c) => c.name);
1461
+ DESKTOP_FILE_COMMANDS = [
1462
+ "files_roots",
1463
+ "files_stat",
1464
+ "files_list",
1465
+ "files_read",
1466
+ "files_search",
1467
+ "files_write",
1468
+ "files_mkdir",
1469
+ "files_move",
1470
+ "files_delete",
1471
+ "files_undo",
1472
+ "files_transfer_prepare",
1473
+ "files_transfer_upload",
1474
+ "files_watch_start",
1475
+ "files_watch_cancel"
1476
+ ];
1477
+ DESKTOP_FILE_COMMAND_SET = new Set(DESKTOP_FILE_COMMANDS);
1478
+ __name(isDesktopFileCommandName, "isDesktopFileCommandName");
1479
+ __name2(isDesktopFileCommandName, "isDesktopFileCommandName");
1480
+ __name(isDesktopFileSessionId, "isDesktopFileSessionId");
1481
+ __name2(isDesktopFileSessionId, "isDesktopFileSessionId");
1386
1482
  REASONING_EFFORT_VALUES = [
1387
1483
  "off",
1388
1484
  "minimal",
@@ -1391,6 +1487,8 @@ var init_dist = __esm({
1391
1487
  "high",
1392
1488
  "max"
1393
1489
  ];
1490
+ __name(resolveRequireToolApproval, "resolveRequireToolApproval");
1491
+ __name2(resolveRequireToolApproval, "resolveRequireToolApproval");
1394
1492
  AGENT_NAME_TOKEN = "[Your Agent Name]";
1395
1493
  DEFAULT_PERSONA_GUIDE = `# ${AGENT_NAME_TOKEN} - Persona
1396
1494
 
@@ -1701,6 +1799,13 @@ Feel free to add, remove, or rename sections. Your persona can be a single parag
1701
1799
  voiceId: z.string().min(1),
1702
1800
  version: z.string().optional()
1703
1801
  });
1802
+ LUA_JOB_DEFAULT_TIMEOUT_SECONDS = 300;
1803
+ LUA_JOB_MIN_TIMEOUT_SECONDS = 1;
1804
+ LUA_JOB_MAX_TIMEOUT_SECONDS = 600;
1805
+ __name(resolveLuaJobTimeoutSeconds, "resolveLuaJobTimeoutSeconds");
1806
+ __name2(resolveLuaJobTimeoutSeconds, "resolveLuaJobTimeoutSeconds");
1807
+ __name(normalizeLuaJobExecutionTimeoutSeconds, "normalizeLuaJobExecutionTimeoutSeconds");
1808
+ __name2(normalizeLuaJobExecutionTimeoutSeconds, "normalizeLuaJobExecutionTimeoutSeconds");
1704
1809
  }
1705
1810
  });
1706
1811
 
@@ -11034,6 +11139,8 @@ var init_user_instance = __esm({
11034
11139
  "data",
11035
11140
  "userAPI",
11036
11141
  "update",
11142
+ "patch",
11143
+ "unset",
11037
11144
  "clear",
11038
11145
  "toJSON",
11039
11146
  "_luaProfile"
@@ -11112,9 +11219,27 @@ var init_user_instance = __esm({
11112
11219
  this.data = response;
11113
11220
  return this.data;
11114
11221
  } catch (error) {
11115
- throw new Error("Failed to update user data");
11222
+ throw new Error("Failed to update user data", {
11223
+ cause: error
11224
+ });
11116
11225
  }
11117
11226
  }
11227
+ async patch(mutation) {
11228
+ try {
11229
+ const response = await this.userAPI.patch(mutation);
11230
+ this.data = response;
11231
+ return this.data;
11232
+ } catch (error) {
11233
+ throw new Error("Failed to patch user data", {
11234
+ cause: error
11235
+ });
11236
+ }
11237
+ }
11238
+ async unset(...fields) {
11239
+ return this.patch({
11240
+ unset: fields
11241
+ });
11242
+ }
11118
11243
  /**
11119
11244
  * Clears all user data for the current user
11120
11245
  * @returns Promise resolving to true if clearing was successful
@@ -11123,9 +11248,12 @@ var init_user_instance = __esm({
11123
11248
  async clear() {
11124
11249
  try {
11125
11250
  await this.userAPI.clear();
11251
+ this.data = {};
11126
11252
  return true;
11127
11253
  } catch (error) {
11128
- throw new Error("Failed to clear user data");
11254
+ throw new Error("Failed to clear user data", {
11255
+ cause: error
11256
+ });
11129
11257
  }
11130
11258
  }
11131
11259
  /**
@@ -11138,7 +11266,9 @@ var init_user_instance = __esm({
11138
11266
  await this.userAPI.update(this.data);
11139
11267
  return true;
11140
11268
  } catch (error) {
11141
- throw new Error("Failed to save user data");
11269
+ throw new Error("Failed to save user data", {
11270
+ cause: error
11271
+ });
11142
11272
  }
11143
11273
  }
11144
11274
  /**
@@ -11152,7 +11282,9 @@ var init_user_instance = __esm({
11152
11282
  await this.userAPI.sendMessage(messages);
11153
11283
  return true;
11154
11284
  } catch (error) {
11155
- throw new Error("Failed to send message");
11285
+ throw new Error("Failed to send message", {
11286
+ cause: error
11287
+ });
11156
11288
  }
11157
11289
  }
11158
11290
  //get chat history
@@ -11160,7 +11292,9 @@ var init_user_instance = __esm({
11160
11292
  try {
11161
11293
  return await this.userAPI.getChatHistory();
11162
11294
  } catch (error) {
11163
- throw new Error("Failed to get chat history");
11295
+ throw new Error("Failed to get chat history", {
11296
+ cause: error
11297
+ });
11164
11298
  }
11165
11299
  }
11166
11300
  };
@@ -12576,6 +12710,8 @@ var init_data_entry_instance = __esm({
12576
12710
  "score",
12577
12711
  "customDataAPI",
12578
12712
  "update",
12713
+ "patch",
12714
+ "unset",
12579
12715
  "delete",
12580
12716
  "toJSON"
12581
12717
  ];
@@ -12667,9 +12803,33 @@ var init_data_entry_instance = __esm({
12667
12803
  };
12668
12804
  return this.data;
12669
12805
  } catch (error) {
12670
- throw new Error("Failed to update custom data entry");
12806
+ throw new Error("Failed to update custom data entry", {
12807
+ cause: error
12808
+ });
12809
+ }
12810
+ }
12811
+ async patch(mutation) {
12812
+ try {
12813
+ await this.customDataAPI.patch(this.collectionName, this.id, mutation);
12814
+ this.data = {
12815
+ ...this.data,
12816
+ ...mutation.set ?? {}
12817
+ };
12818
+ for (const field of mutation.unset ?? []) {
12819
+ delete this.data[field];
12820
+ }
12821
+ return this.data;
12822
+ } catch (error) {
12823
+ throw new Error("Failed to patch custom data entry", {
12824
+ cause: error
12825
+ });
12671
12826
  }
12672
12827
  }
12828
+ async unset(...fields) {
12829
+ return this.patch({
12830
+ unset: fields
12831
+ });
12832
+ }
12673
12833
  /**
12674
12834
  * Deletes the custom data entry
12675
12835
  * @returns Promise resolving to true if deletion was successful
@@ -12680,7 +12840,9 @@ var init_data_entry_instance = __esm({
12680
12840
  await this.customDataAPI.delete(this.collectionName, this.id);
12681
12841
  return true;
12682
12842
  } catch (error) {
12683
- throw new Error("Failed to delete custom data entry");
12843
+ throw new Error("Failed to delete custom data entry", {
12844
+ cause: error
12845
+ });
12684
12846
  }
12685
12847
  }
12686
12848
  /**
@@ -12694,7 +12856,9 @@ var init_data_entry_instance = __esm({
12694
12856
  await this.customDataAPI.update(this.collectionName, this.id, this.data, searchText);
12695
12857
  return true;
12696
12858
  } catch (error) {
12697
- throw new Error("Failed to save data entry");
12859
+ throw new Error("Failed to save data entry", {
12860
+ cause: error
12861
+ });
12698
12862
  }
12699
12863
  }
12700
12864
  };
@@ -12805,6 +12969,15 @@ var init_custom_data_api_service = __esm({
12805
12969
  }
12806
12970
  throw new Error(response.error?.message || "Failed to update custom data entry");
12807
12971
  }
12972
+ async patch(collectionName, entryId, mutation) {
12973
+ const response = await this.httpPatch(`/developer/agents/${this.agentId}/custom-data/${collectionName}/${entryId}`, mutation, {
12974
+ Authorization: `Bearer ${this.apiKey}`
12975
+ });
12976
+ if (response.success && response.data) {
12977
+ return response.data;
12978
+ }
12979
+ throw new Error(response.error?.message || "Failed to patch custom data entry");
12980
+ }
12808
12981
  /**
12809
12982
  * Performs semantic search on custom data entries using text similarity
12810
12983
  * @param collectionName - The name of the collection to search within
@@ -13261,8 +13434,10 @@ var init_developer_api_service = __esm({
13261
13434
  * @param email - The email address to look up
13262
13435
  * @returns Promise resolving to an ApiResponse containing the profile, or null if not found
13263
13436
  */
13264
- async getUserProfileByEmail(email) {
13265
- return this.httpGet(`/developer/user/profile/email/${encodeURIComponent(email)}`, {
13437
+ async getUserProfileByEmail(email, agentId) {
13438
+ const path18 = `/developer/user/profile/email/${encodeURIComponent(email)}`;
13439
+ const scopedPath = agentId ? `${path18}?agentId=${encodeURIComponent(agentId)}` : path18;
13440
+ return this.httpGet(scopedPath, {
13266
13441
  Authorization: `Bearer ${this.apiKey}`
13267
13442
  });
13268
13443
  }
@@ -13271,9 +13446,11 @@ var init_developer_api_service = __esm({
13271
13446
  * @param phone - The phone number to look up (with or without + prefix)
13272
13447
  * @returns Promise resolving to an ApiResponse containing the profile, or null if not found
13273
13448
  */
13274
- async getUserProfileByPhone(phone) {
13449
+ async getUserProfileByPhone(phone, agentId) {
13275
13450
  const normalizedPhone = phone.replace(/^\+/, "");
13276
- return this.httpGet(`/developer/user/profile/phone/${normalizedPhone}`, {
13451
+ const path18 = `/developer/user/profile/phone/${normalizedPhone}`;
13452
+ const scopedPath = agentId ? `${path18}?agentId=${encodeURIComponent(agentId)}` : path18;
13453
+ return this.httpGet(scopedPath, {
13277
13454
  Authorization: `Bearer ${this.apiKey}`
13278
13455
  });
13279
13456
  }
@@ -13813,22 +13990,28 @@ var init_user_data_api_service = __esm({
13813
13990
  init_http_client();
13814
13991
  init_user_instance();
13815
13992
  init_lazy_instances();
13816
- UserDataApi = class extends HttpClient {
13993
+ UserDataApi = class _UserDataApi extends HttpClient {
13817
13994
  static {
13818
13995
  __name(this, "UserDataApi");
13819
13996
  }
13820
13997
  apiKey;
13821
13998
  agentId;
13999
+ targetUserId;
13822
14000
  /**
13823
14001
  * Creates an instance of UserDataApi
13824
14002
  * @param baseUrl - The base URL for the API
13825
14003
  * @param apiKey - The API key for authentication
13826
14004
  * @param agentId - The unique identifier of the agent
13827
14005
  */
13828
- constructor(baseUrl, apiKey, agentId) {
14006
+ constructor(baseUrl, apiKey, agentId, targetUserId) {
13829
14007
  super(baseUrl);
13830
14008
  this.apiKey = apiKey;
13831
14009
  this.agentId = agentId;
14010
+ this.targetUserId = targetUserId;
14011
+ }
14012
+ get dataPath() {
14013
+ const base = `/developer/user/data/agent/${this.agentId}`;
14014
+ return this.targetUserId ? `${base}/user/${encodeURIComponent(this.targetUserId)}` : base;
13832
14015
  }
13833
14016
  /**
13834
14017
  * Retrieves user data by userId, email, or phone.
@@ -13847,7 +14030,7 @@ var init_user_data_api_service = __esm({
13847
14030
  }
13848
14031
  let url = `/developer/user/data/agent/${this.agentId}`;
13849
14032
  if (userId) {
13850
- url += `/user/${userId}`;
14033
+ url += `/user/${encodeURIComponent(userId)}`;
13851
14034
  }
13852
14035
  const response = await this.httpGet(url, {
13853
14036
  Authorization: `Bearer ${this.apiKey}`
@@ -13857,7 +14040,8 @@ var init_user_data_api_service = __esm({
13857
14040
  }
13858
14041
  const profile = response.data?._luaProfile;
13859
14042
  const { _luaProfile, ...data } = response.data || {};
13860
- return new UserDataInstance(this, data, profile);
14043
+ const scopedApi = userId ? new _UserDataApi(this.baseUrl, this.apiKey, this.agentId, userId) : this;
14044
+ return new UserDataInstance(scopedApi, data, profile);
13861
14045
  }
13862
14046
  /**
13863
14047
  * Resolves email or phone to user profile via DeveloperApi
@@ -13868,11 +14052,11 @@ var init_user_data_api_service = __esm({
13868
14052
  try {
13869
14053
  const developerApi = await getDeveloperInstance();
13870
14054
  if (options.email) {
13871
- const response = await developerApi.getUserProfileByEmail(options.email);
14055
+ const response = await developerApi.getUserProfileByEmail(options.email, this.agentId);
13872
14056
  return response.success ? response.data ?? null : null;
13873
14057
  }
13874
14058
  if (options.phone) {
13875
- const response = await developerApi.getUserProfileByPhone(options.phone);
14059
+ const response = await developerApi.getUserProfileByPhone(options.phone, this.agentId);
13876
14060
  return response.success ? response.data ?? null : null;
13877
14061
  }
13878
14062
  } catch (error) {
@@ -13890,7 +14074,7 @@ var init_user_data_api_service = __esm({
13890
14074
  * @throws Error if the update fails or the request is unsuccessful
13891
14075
  */
13892
14076
  async update(data) {
13893
- const response = await this.httpPut(`/developer/user/data/agent/${this.agentId}`, data, {
14077
+ const response = await this.httpPut(this.dataPath, data, {
13894
14078
  Authorization: `Bearer ${this.apiKey}`
13895
14079
  });
13896
14080
  if (!response.success) {
@@ -13899,13 +14083,23 @@ var init_user_data_api_service = __esm({
13899
14083
  const { _luaProfile, ...cleanData } = response.data || {};
13900
14084
  return cleanData;
13901
14085
  }
14086
+ async patch(mutation) {
14087
+ const response = await this.httpPatch(this.dataPath, mutation, {
14088
+ Authorization: `Bearer ${this.apiKey}`
14089
+ });
14090
+ if (!response.success) {
14091
+ throw new Error(response.error?.message || "Failed to patch user data");
14092
+ }
14093
+ const { _luaProfile, ...cleanData } = response.data || {};
14094
+ return cleanData;
14095
+ }
13902
14096
  /**
13903
14097
  * Clears all user data for the current user and specific agent
13904
14098
  * @returns Promise resolving to an empty object upon successful deletion
13905
14099
  * @throws Error if the clear operation fails or the request is unsuccessful
13906
14100
  */
13907
14101
  async clear() {
13908
- const response = await this.httpDelete(`/developer/user/data/agent/${this.agentId}`, {
14102
+ const response = await this.httpDelete(this.dataPath, {
13909
14103
  Authorization: `Bearer ${this.apiKey}`
13910
14104
  });
13911
14105
  if (!response.success) {
@@ -14898,7 +15092,7 @@ export const governance = {
14898
15092
  }
14899
15093
  if (setup.requireApproval && setup.requireApproval.length > 0) {
14900
15094
  const list = setup.requireApproval.map((t) => `'${t}'`).join(", ");
14901
- ruleLines.push(` requireApproval: [${list}],`);
15095
+ ruleLines.push(` requireToolApproval: [${list}],`);
14902
15096
  }
14903
15097
  if (setup.tokenLimit && setup.tokenLimit > 0) {
14904
15098
  ruleLines.push(` tokenBudget: ${setup.tokenLimit},`);
@@ -19809,6 +20003,15 @@ var DeviceTriggerApi = class extends HttpClient {
19809
20003
 
19810
20004
  // src/primitives/device-trigger.handler.ts
19811
20005
  init_base_handler();
20006
+
20007
+ // src/primitives/device-trigger.response.ts
20008
+ function parseDeviceTriggerList(data) {
20009
+ const triggers = data?.triggers ?? data?.deviceTriggers;
20010
+ return Array.isArray(triggers) ? triggers : null;
20011
+ }
20012
+ __name(parseDeviceTriggerList, "parseDeviceTriggerList");
20013
+
20014
+ // src/primitives/device-trigger.handler.ts
19812
20015
  var DeviceTriggerHandler = class extends BaseVersionedHandler {
19813
20016
  static {
19814
20017
  __name(this, "DeviceTriggerHandler");
@@ -19848,10 +20051,10 @@ var DeviceTriggerHandler = class extends BaseVersionedHandler {
19848
20051
  }
19849
20052
  async fetchFromServer(api) {
19850
20053
  const response = await api.getDeviceTriggers();
19851
- if (!response.success || !response.data?.deviceTriggers) {
20054
+ if (!response.success) {
19852
20055
  return null;
19853
20056
  }
19854
- return response.data.deviceTriggers;
20057
+ return parseDeviceTriggerList(response.data);
19855
20058
  }
19856
20059
  async createOnServer(api, primitive) {
19857
20060
  const response = await api.createDeviceTrigger({
@@ -29634,13 +29837,47 @@ init_cli();
29634
29837
  init_command_utils();
29635
29838
  init_analytics();
29636
29839
  import open from "open";
29840
+
29841
+ // src/utils/auth-handoff.ts
29842
+ init_constants();
29843
+ async function issueAdminHandoff(apiKey) {
29844
+ const response = await fetch(`${BASE_URLS.AUTH}/auth/handoff`, {
29845
+ method: "POST",
29846
+ headers: {
29847
+ Authorization: `Bearer ${apiKey}`,
29848
+ "Content-Type": "application/json"
29849
+ },
29850
+ body: JSON.stringify({
29851
+ audience: "admin"
29852
+ })
29853
+ });
29854
+ if (!response.ok) throw new Error("Could not create a secure dashboard sign-in handoff");
29855
+ const body = await response.json();
29856
+ if (typeof body.code !== "string" || !body.code) {
29857
+ throw new Error("Lua Auth returned an invalid dashboard sign-in handoff");
29858
+ }
29859
+ return body.code;
29860
+ }
29861
+ __name(issueAdminHandoff, "issueAdminHandoff");
29862
+ function buildAdminHandoffUrl(code, agentId, orgId) {
29863
+ const url = new URL("/auth-handoff", "https://admin.heylua.ai");
29864
+ url.searchParams.set("code", code);
29865
+ url.searchParams.set("redirect", "/admin/usage");
29866
+ url.searchParams.set("agentId", agentId);
29867
+ url.searchParams.set("orgId", orgId);
29868
+ return url.toString();
29869
+ }
29870
+ __name(buildAdminHandoffUrl, "buildAdminHandoffUrl");
29871
+
29872
+ // src/commands/admin.ts
29637
29873
  async function adminCommand() {
29638
29874
  return withErrorHandling(async () => {
29639
29875
  writeProgress("Opening Lua Admin Dashboard...");
29640
29876
  const { agentId, orgId, apiKey } = await initializeCommand({
29641
29877
  showProgress: false
29642
29878
  });
29643
- const adminUrl = `https://admin.heylua.ai/validate-token/${apiKey}?redirect=/admin/usage?&agentId=${agentId}&orgId=${orgId}`;
29879
+ const handoffCode = await issueAdminHandoff(apiKey);
29880
+ const adminUrl = buildAdminHandoffUrl(handoffCode, agentId, orgId);
29644
29881
  await open(adminUrl);
29645
29882
  writeSuccess("Lua Admin Dashboard opened in your browser");
29646
29883
  console.log(`
@@ -29695,10 +29932,10 @@ async function docsCommand() {
29695
29932
  __name(docsCommand, "docsCommand");
29696
29933
 
29697
29934
  // src/commands/channels.ts
29698
- init_cli();
29699
- init_command_utils();
29700
29935
  import inquirer12 from "inquirer";
29701
29936
  import open4 from "open";
29937
+ init_cli();
29938
+ init_command_utils();
29702
29939
 
29703
29940
  // src/api/channels.api.service.ts
29704
29941
  init_http_client();
@@ -30266,7 +30503,8 @@ async function openAdminDashboard(apiKey, config) {
30266
30503
  if (!orgId) {
30267
30504
  throw new Error("No orgId found in lua.skill.yaml. Please ensure your configuration is valid.");
30268
30505
  }
30269
- const adminUrl = `https://admin.heylua.ai/validate-token/${apiKey}?redirect=/admin/usage?&agentId=${agentId}&orgId=${orgId}`;
30506
+ const handoffCode = await issueAdminHandoff(apiKey);
30507
+ const adminUrl = buildAdminHandoffUrl(handoffCode, agentId, orgId);
30270
30508
  await open4(adminUrl);
30271
30509
  writeSuccess("\u2705 Lua Admin Dashboard opened in your browser");
30272
30510
  console.log(`
@@ -40625,6 +40863,7 @@ __name(deleteServerInteractive, "deleteServerInteractive");
40625
40863
  init_cli();
40626
40864
  init_constants();
40627
40865
  import http from "http";
40866
+ import { randomBytes } from "crypto";
40628
40867
  import { URL as URL2 } from "url";
40629
40868
  import open5 from "open";
40630
40869
  init_command_utils();
@@ -40666,18 +40905,14 @@ var UnifiedToApi = class extends HttpClient {
40666
40905
  async getAuthUrl(integrationType, options) {
40667
40906
  const params = new URLSearchParams();
40668
40907
  params.append("integrationType", integrationType);
40908
+ params.append("agentId", options.agentId);
40669
40909
  params.append("successRedirect", options.successRedirect);
40670
40910
  params.append("failureRedirect", options.failureRedirect);
40671
40911
  if (options.scopes && options.scopes.length > 0) {
40672
40912
  params.append("scopes", options.scopes.join(","));
40673
40913
  }
40674
- if (options.state) {
40675
- params.append("state", options.state);
40676
- }
40677
- if (options.externalXref) {
40678
- params.append("externalXref", options.externalXref);
40679
- }
40680
- return this.httpGet(`/developer/unifiedto/auth-url?${params.toString()}`, {
40914
+ params.append("state", options.state);
40915
+ return this.httpGet(`/developer/unifiedto/auth-url/v2?${params.toString()}`, {
40681
40916
  Authorization: `Bearer ${this.apiKey}`
40682
40917
  });
40683
40918
  }
@@ -40823,7 +41058,8 @@ var UnifiedToApi = class extends HttpClient {
40823
41058
  // src/commands/integrations.ts
40824
41059
  init_analytics();
40825
41060
  var CALLBACK_PORT = 19837;
40826
- var CALLBACK_URL = `http://localhost:${CALLBACK_PORT}/callback`;
41061
+ var CALLBACK_HOST = "127.0.0.1";
41062
+ var CALLBACK_URL = `http://${CALLBACK_HOST}:${CALLBACK_PORT}/callback`;
40827
41063
  var AGENT_WEBHOOK_URL = `${BASE_URLS.API}/webhook/unifiedto/data`;
40828
41064
  var DEFAULT_VIRTUAL_WEBHOOK_INTERVAL = 1;
40829
41065
  async function fetchAvailableIntegrations(unifiedToApi, agentId) {
@@ -40842,22 +41078,50 @@ async function fetchAvailableIntegrations(unifiedToApi, agentId) {
40842
41078
  }));
40843
41079
  }
40844
41080
  __name(fetchAvailableIntegrations, "fetchAvailableIntegrations");
40845
- function startCallbackServer(timeoutMs = 3e5) {
41081
+ function createOAuthState() {
41082
+ return randomBytes(32).toString("base64url");
41083
+ }
41084
+ __name(createOAuthState, "createOAuthState");
41085
+ function escapeHtml(value) {
41086
+ return value.replace(/[&<>"']/g, (character) => {
41087
+ const escaped = {
41088
+ "&": "&amp;",
41089
+ "<": "&lt;",
41090
+ ">": "&gt;",
41091
+ '"': "&quot;",
41092
+ "'": "&#039;"
41093
+ };
41094
+ return escaped[character];
41095
+ });
41096
+ }
41097
+ __name(escapeHtml, "escapeHtml");
41098
+ function startCallbackServer(expectedState, timeoutMs = 3e5) {
40846
41099
  return new Promise((resolve6) => {
40847
41100
  let resolved = false;
40848
41101
  const server = http.createServer((req, res) => {
40849
41102
  if (resolved) return;
40850
41103
  const reqUrl = new URL2(req.url || "/", `http://localhost:${CALLBACK_PORT}`);
40851
41104
  if (reqUrl.pathname === "/callback") {
41105
+ const returnedState = reqUrl.searchParams.get("state");
41106
+ if (returnedState !== expectedState) {
41107
+ res.writeHead(400, {
41108
+ "Content-Type": "text/plain; charset=utf-8",
41109
+ "Cache-Control": "no-store",
41110
+ "X-Content-Type-Options": "nosniff"
41111
+ });
41112
+ res.end("Invalid OAuth state");
41113
+ return;
41114
+ }
40852
41115
  const connectionId = reqUrl.searchParams.get("id");
40853
41116
  const error = reqUrl.searchParams.get("error");
40854
41117
  const logId = reqUrl.searchParams.get("log_id");
40855
41118
  const integrationType = reqUrl.searchParams.get("type");
40856
41119
  resolved = true;
40857
41120
  if (error) {
40858
- const logIdHtml = logId ? `<p style="color: #888; font-size: 0.85em;">Log ID: <code>${logId}</code></p>` : "";
41121
+ const logIdHtml = logId ? `<p style="color: #888; font-size: 0.85em;">Log ID: <code>${escapeHtml(logId)}</code></p>` : "";
40859
41122
  res.writeHead(200, {
40860
- "Content-Type": "text/html"
41123
+ "Content-Type": "text/html; charset=utf-8",
41124
+ "Cache-Control": "no-store"
40861
41125
  });
40862
41126
  res.end(`
40863
41127
  <!DOCTYPE html>
@@ -40866,7 +41130,7 @@ function startCallbackServer(timeoutMs = 3e5) {
40866
41130
  <body style="font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #1a1a2e;">
40867
41131
  <div style="text-align: center; color: white; max-width: 500px; padding: 0 20px;">
40868
41132
  <h1 style="color: #ff6b6b;">Connection Failed</h1>
40869
- <p style="color: #ccc;">Error: ${error}</p>
41133
+ <p style="color: #ccc;">Authentication failed. Return to the terminal for details.</p>
40870
41134
  ${logIdHtml}
40871
41135
  <p style="color: #888;">You can close this window and try again.</p>
40872
41136
  </div>
@@ -40881,7 +41145,8 @@ function startCallbackServer(timeoutMs = 3e5) {
40881
41145
  });
40882
41146
  } else if (connectionId) {
40883
41147
  res.writeHead(200, {
40884
- "Content-Type": "text/html"
41148
+ "Content-Type": "text/html; charset=utf-8",
41149
+ "Cache-Control": "no-store"
40885
41150
  });
40886
41151
  res.end(`
40887
41152
  <!DOCTYPE html>
@@ -40915,7 +41180,7 @@ function startCallbackServer(timeoutMs = 3e5) {
40915
41180
  res.end("Not found");
40916
41181
  }
40917
41182
  });
40918
- server.listen(CALLBACK_PORT, () => {
41183
+ server.listen(CALLBACK_PORT, CALLBACK_HOST, () => {
40919
41184
  });
40920
41185
  setTimeout(() => {
40921
41186
  if (!resolved) {
@@ -40978,6 +41243,7 @@ async function executeNonInteractive12(context, action, cmdOptions) {
40978
41243
  authMethod: cmdOptions?.authMethod,
40979
41244
  scopes: cmdOptions?.scopes,
40980
41245
  hideSensitive: cmdOptions?.hideSensitive === "true",
41246
+ accountLabel: cmdOptions?.accountLabel,
40981
41247
  // Trigger options
40982
41248
  triggers: cmdOptions?.triggers,
40983
41249
  customWebhook: cmdOptions?.customWebhook === true,
@@ -41616,22 +41882,13 @@ Available triggers for ${selectedIntegration.name}:`);
41616
41882
  writeInfo(`Note: Could not fetch available triggers (${error.message})`);
41617
41883
  }
41618
41884
  writeProgress("\u{1F504} Preparing authorization...");
41619
- const state = Buffer.from(JSON.stringify({
41620
- agentId: context.agentId,
41621
- integration: selectedIntegration.value,
41622
- authMethod,
41623
- timestamp: Date.now()
41624
- })).toString("base64");
41625
- const externalXref = JSON.stringify({
41626
- agentId: context.agentId,
41627
- userId: context.userId
41628
- });
41885
+ const state = createOAuthState();
41629
41886
  const authUrlResult = await context.unifiedToApi.getAuthUrl(selectedIntegration.value, {
41887
+ agentId: context.agentId,
41630
41888
  successRedirect: CALLBACK_URL,
41631
41889
  failureRedirect: CALLBACK_URL,
41632
41890
  scopes: authMethod === "oauth" ? selectedScopes : void 0,
41633
- state,
41634
- externalXref
41891
+ state
41635
41892
  });
41636
41893
  if (!authUrlResult.success || !authUrlResult.data) {
41637
41894
  writeError(`\u274C Failed to get authorization URL: ${authUrlResult.error?.message || "Unknown error"}`);
@@ -41647,7 +41904,7 @@ Integration: ${selectedIntegration.name}`);
41647
41904
  \u{1F4CB} Authorization URL (copy if browser doesn't open):
41648
41905
  ${authUrl}
41649
41906
  `);
41650
- const callbackPromise = startCallbackServer(3e5);
41907
+ const callbackPromise = startCallbackServer(state, 3e5);
41651
41908
  try {
41652
41909
  await open5(authUrl);
41653
41910
  writeInfo("\u{1F310} Browser opened - please complete the authorization");
@@ -41663,7 +41920,7 @@ Integration: ${selectedIntegration.name}`);
41663
41920
  triggers: selectedTriggers,
41664
41921
  webhookUrl,
41665
41922
  isCustomWebhook
41666
- });
41923
+ }, options.accountLabel);
41667
41924
  } else {
41668
41925
  writeError(`
41669
41926
  \u274C Authorization failed: ${result.error || "Unknown error"}`);
@@ -41686,8 +41943,22 @@ async function finalizeConnection(context, integration, connectionId, scopes, hi
41686
41943
  triggers: [],
41687
41944
  webhookUrl: "",
41688
41945
  isCustomWebhook: false
41689
- }) {
41946
+ }, requestedAccountLabel) {
41690
41947
  writeProgress(`\u{1F504} Setting up ${integration.name} connection...`);
41948
+ let displayName = requestedAccountLabel?.trim();
41949
+ if (!displayName) {
41950
+ const labelAnswer = await safePrompt([
41951
+ {
41952
+ type: "input",
41953
+ name: "displayName",
41954
+ message: "Account label (used to distinguish this connection):",
41955
+ default: integration.name,
41956
+ validate: /* @__PURE__ */ __name((value) => value.trim().length > 0 || "Account label is required", "validate")
41957
+ }
41958
+ ]);
41959
+ if (!labelAnswer) return;
41960
+ displayName = labelAnswer.displayName.trim();
41961
+ }
41691
41962
  const triggers = webhookConfig.triggers.map((t) => ({
41692
41963
  objectType: t.objectType,
41693
41964
  event: t.event,
@@ -41697,6 +41968,7 @@ async function finalizeConnection(context, integration, connectionId, scopes, hi
41697
41968
  const result = await context.unifiedToApi.finalizeConnection(context.agentId, {
41698
41969
  connectionId,
41699
41970
  integrationType: integration.value,
41971
+ displayName,
41700
41972
  scopes: scopes.length > 0 ? scopes : void 0,
41701
41973
  hideSensitive,
41702
41974
  triggers: triggers.length > 0 ? triggers : void 0
@@ -42049,22 +42321,13 @@ Available scopes for ${selectedIntegration.name}:`);
42049
42321
  writeError(`\u274C Failed to remove old connection: ${error.message}`);
42050
42322
  return;
42051
42323
  }
42052
- const state = Buffer.from(JSON.stringify({
42053
- agentId: context.agentId,
42054
- integration: selectedIntegration.value,
42055
- authMethod: "oauth",
42056
- timestamp: Date.now()
42057
- })).toString("base64");
42058
- const externalXref = JSON.stringify({
42059
- agentId: context.agentId,
42060
- userId: context.userId
42061
- });
42324
+ const state = createOAuthState();
42062
42325
  const authUrlResult = await context.unifiedToApi.getAuthUrl(selectedIntegration.value, {
42326
+ agentId: context.agentId,
42063
42327
  successRedirect: CALLBACK_URL,
42064
42328
  failureRedirect: CALLBACK_URL,
42065
42329
  scopes: selectedScopes,
42066
- state,
42067
- externalXref
42330
+ state
42068
42331
  });
42069
42332
  if (!authUrlResult.success || !authUrlResult.data) {
42070
42333
  writeError(`\u274C Failed to get authorization URL: ${authUrlResult.error?.message || "Unknown error"}`);
@@ -42078,7 +42341,7 @@ Available scopes for ${selectedIntegration.name}:`);
42078
42341
  \u{1F4CB} Authorization URL (copy if browser doesn't open):
42079
42342
  ${authUrl}
42080
42343
  `);
42081
- const callbackPromise = startCallbackServer(3e5);
42344
+ const callbackPromise = startCallbackServer(state, 3e5);
42082
42345
  try {
42083
42346
  await open5(authUrl);
42084
42347
  writeInfo("\u{1F310} Browser opened - please complete the authorization");
@@ -42114,7 +42377,7 @@ Available scopes for ${selectedIntegration.name}:`);
42114
42377
  triggers: triggersToRestore,
42115
42378
  webhookUrl: AGENT_WEBHOOK_URL,
42116
42379
  isCustomWebhook: hasCustomWebhook
42117
- });
42380
+ }, options.accountLabel);
42118
42381
  if (triggersToRestore.length > 0) {
42119
42382
  writeSuccess(`Restored ${triggersToRestore.length} trigger(s) from previous connection`);
42120
42383
  }
@@ -46377,7 +46640,7 @@ Examples:
46377
46640
  $ lua mcp deactivate --server-name api-server Deactivate a server
46378
46641
  $ lua mcp delete --server-name old-server Delete a server
46379
46642
  `).action(mcpCommand);
46380
- 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("--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", `
46643
+ 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", `
46381
46644
  Arguments:
46382
46645
  action Optional: 'connect', 'update', 'list', 'available', 'info', 'disconnect', 'webhooks'
46383
46646
  (alias: 'triggers'), or 'mcp'