lua-cli 3.26.0 → 3.28.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.
@@ -16,6 +16,8 @@ var __export = (target, all) => {
16
16
 
17
17
  // ../shared-types/dist/index.mjs
18
18
  import { z } from "zod";
19
+ import { z as z2 } from "zod";
20
+ import { z as z3 } from "zod";
19
21
  function isPersonaTextObject(value) {
20
22
  if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
21
23
  const obj = value;
@@ -384,7 +386,88 @@ function normalizeLuaJobExecutionTimeoutSeconds(timeout) {
384
386
  }
385
387
  return Math.min(Math.max(timeout, LUA_JOB_MIN_TIMEOUT_SECONDS), LUA_JOB_MAX_TIMEOUT_SECONDS);
386
388
  }
387
- var __defProp2, __name2, CHANNEL_SEND_CHANNELS, 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, IMPLICIT_MODEL_SELECTION_SOURCES, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, PERSONAL_SPACE_STARTING_PERSONA, 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;
389
+ function triggerUrlEnvKey(triggerKey) {
390
+ const upper = triggerKey.trim().replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
391
+ return `${TEMPLATE_TRIGGER_URL_ENV_PREFIX}${upper}`;
392
+ }
393
+ function parsePrincipalContext(value) {
394
+ const parsed = PrincipalContextSchema.safeParse(value);
395
+ return parsed.success ? parsed.data : void 0;
396
+ }
397
+ function isTypedApiKeyPrincipal(context) {
398
+ return context?.subject.subjectType === "apiKey" && context.credential.type === "apiKey" && !context.compatibility;
399
+ }
400
+ function typedApiKeyPrincipalId(context) {
401
+ if (!context || !isTypedApiKeyPrincipal(context) || !context.subject.subjectId || context.credential.id !== context.subject.subjectId || context.actor?.actorType !== "apiKey" || context.actor.actorId !== context.subject.subjectId || context.owner?.type !== "user" || !context.owner.id) {
402
+ return void 0;
403
+ }
404
+ return context.subject.subjectId;
405
+ }
406
+ function parseLuaClientHeader(value) {
407
+ if (typeof value !== "string" || value.length > 64) return void 0;
408
+ const match = CLIENT_HEADER_PATTERN.exec(value.trim());
409
+ if (!match) return void 0;
410
+ const app = LUA_CLIENT_APPS.find((candidate) => candidate === match[1]);
411
+ if (!app) return void 0;
412
+ const wireVersion = match[2];
413
+ if (wireVersion === "unversioned") return {
414
+ app,
415
+ version: "unknown",
416
+ attribution: "unknown"
417
+ };
418
+ const semver = SEMVER_PATTERN.exec(wireVersion);
419
+ if (semver) return {
420
+ app,
421
+ version: semver[1],
422
+ attribution: "known"
423
+ };
424
+ if (app === "web" && WEB_RELEASE_PATTERN.test(wireVersion)) {
425
+ return {
426
+ app,
427
+ version: wireVersion.toLowerCase(),
428
+ attribution: "known"
429
+ };
430
+ }
431
+ return void 0;
432
+ }
433
+ function serializeLuaClientHeader(client) {
434
+ if (!client) return void 0;
435
+ return `${client.app}/${client.version === "unknown" ? "unversioned" : client.version}`;
436
+ }
437
+ function formatLuaClientHeader(app, version) {
438
+ if (typeof version !== "string") return `${app}/unversioned`;
439
+ const normalized = version.trim();
440
+ if (SEMVER_PATTERN.test(normalized)) return `${app}/${normalized}`;
441
+ if (app === "web" && WEB_RELEASE_PATTERN.test(normalized)) return `${app}/${normalized.toLowerCase()}`;
442
+ return `${app}/unversioned`;
443
+ }
444
+ function luaClientMetricLabels(client) {
445
+ const canonical = parseLuaClientHeader(serializeLuaClientHeader(client));
446
+ return {
447
+ client_family: canonical?.app ?? "unknown",
448
+ client_version: canonical?.version ?? "unknown",
449
+ client_attribution: canonical?.attribution ?? "unknown"
450
+ };
451
+ }
452
+ function parseEffectiveAuthorization(value) {
453
+ const parsed = EffectiveAuthorizationSchema.safeParse(value);
454
+ return parsed.success ? parsed.data : void 0;
455
+ }
456
+ function parseResourcePage(value) {
457
+ const parsed = ResourcePageSchema.safeParse(value);
458
+ return parsed.success ? parsed.data : void 0;
459
+ }
460
+ function capabilitiesFor(profiles, profileId) {
461
+ if (!profileId) return [];
462
+ return profiles[profileId] ?? [];
463
+ }
464
+ function isKnownProfile(profiles, profileId) {
465
+ return profileId !== void 0 && Object.prototype.hasOwnProperty.call(profiles, profileId);
466
+ }
467
+ function hasCapability(profiles, profileId, required) {
468
+ return capabilitiesFor(profiles, profileId).includes(required);
469
+ }
470
+ var __defProp2, __name2, CHANNEL_SEND_CHANNELS, 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, IMPLICIT_MODEL_SELECTION_SOURCES, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, PERSONAL_SPACE_STARTING_PERSONA, 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, TEMPLATE_TRIGGER_URL_ENV_PREFIX, SUBJECT_TYPES, SubjectTypeSchema, CREDENTIAL_TYPES, CredentialTypeSchema, PrincipalContextSchema, LUA_CLIENT_HEADER, LUA_CLIENT_APPS, SEMVER_PATTERN, WEB_RELEASE_PATTERN, CLIENT_HEADER_PATTERN, AUTHZ_PROJECTION_VERSION, ProjectedScopeSchema, DisplayRoleSchema, AuthorizationPrincipalSchema, CredentialContextSchema, ProjectionAnomalySchema, ProjectedOrgSchema, ProjectedResourceSchema, CapabilityProfilesSchema, RoleCatalogSchema, EffectiveAuthorizationSchema, ResourcePageSchema;
388
471
  var init_dist = __esm({
389
472
  "../shared-types/dist/index.mjs"() {
390
473
  "use strict";
@@ -1049,6 +1132,201 @@ This text is who you are for this person. As you learn them, their name, their w
1049
1132
  __name2(resolveLuaJobTimeoutSeconds, "resolveLuaJobTimeoutSeconds");
1050
1133
  __name(normalizeLuaJobExecutionTimeoutSeconds, "normalizeLuaJobExecutionTimeoutSeconds");
1051
1134
  __name2(normalizeLuaJobExecutionTimeoutSeconds, "normalizeLuaJobExecutionTimeoutSeconds");
1135
+ TEMPLATE_TRIGGER_URL_ENV_PREFIX = "LUA_TRIGGER_URL__";
1136
+ __name(triggerUrlEnvKey, "triggerUrlEnvKey");
1137
+ __name2(triggerUrlEnvKey, "triggerUrlEnvKey");
1138
+ SUBJECT_TYPES = [
1139
+ "user",
1140
+ "apiKey",
1141
+ "service",
1142
+ "endUser",
1143
+ "staff"
1144
+ ];
1145
+ SubjectTypeSchema = z2.enum(SUBJECT_TYPES);
1146
+ CREDENTIAL_TYPES = [
1147
+ "firstPartySession",
1148
+ "apiKey",
1149
+ "deviceCredential",
1150
+ "endUserSession",
1151
+ "staffAssertion",
1152
+ "serviceCredential",
1153
+ "legacyInternalToken"
1154
+ ];
1155
+ CredentialTypeSchema = z2.enum(CREDENTIAL_TYPES);
1156
+ PrincipalContextSchema = z2.object({
1157
+ version: z2.literal(1),
1158
+ subject: z2.object({
1159
+ subjectType: SubjectTypeSchema,
1160
+ subjectId: z2.string().min(1).max(256)
1161
+ }).strict(),
1162
+ actor: z2.object({
1163
+ actorType: SubjectTypeSchema,
1164
+ actorId: z2.string().min(1).max(256)
1165
+ }).strict().optional(),
1166
+ credential: z2.object({
1167
+ type: CredentialTypeSchema,
1168
+ id: z2.string().min(1).max(256).optional(),
1169
+ issuedAt: z2.number().int().nonnegative().optional(),
1170
+ expiresAt: z2.number().int().nonnegative().optional()
1171
+ }).strict(),
1172
+ owner: z2.object({
1173
+ type: z2.enum([
1174
+ "user",
1175
+ "org",
1176
+ "service"
1177
+ ]),
1178
+ id: z2.string().min(1).max(256)
1179
+ }).strict().optional(),
1180
+ compatibility: z2.object({
1181
+ mode: z2.literal("legacy-owner-delegation")
1182
+ }).strict().optional()
1183
+ }).strict();
1184
+ __name(parsePrincipalContext, "parsePrincipalContext");
1185
+ __name2(parsePrincipalContext, "parsePrincipalContext");
1186
+ __name(isTypedApiKeyPrincipal, "isTypedApiKeyPrincipal");
1187
+ __name2(isTypedApiKeyPrincipal, "isTypedApiKeyPrincipal");
1188
+ __name(typedApiKeyPrincipalId, "typedApiKeyPrincipalId");
1189
+ __name2(typedApiKeyPrincipalId, "typedApiKeyPrincipalId");
1190
+ LUA_CLIENT_HEADER = "X-Lua-Client";
1191
+ LUA_CLIENT_APPS = [
1192
+ "desktop",
1193
+ "web",
1194
+ "cli",
1195
+ "mobile",
1196
+ "claude-plugin",
1197
+ "codex-plugin",
1198
+ "cursor-plugin",
1199
+ "platform-mcp"
1200
+ ];
1201
+ SEMVER_PATTERN = /^(\d{1,3}\.\d{1,3}\.\d{1,3})(?:-[0-9A-Za-z.-]{1,16})?(?:\+[0-9A-Za-z.-]{1,16})?$/;
1202
+ WEB_RELEASE_PATTERN = /^[0-9a-f]{12}$/i;
1203
+ CLIENT_HEADER_PATTERN = /^([^/]+)\/(.+)$/;
1204
+ __name(parseLuaClientHeader, "parseLuaClientHeader");
1205
+ __name2(parseLuaClientHeader, "parseLuaClientHeader");
1206
+ __name(serializeLuaClientHeader, "serializeLuaClientHeader");
1207
+ __name2(serializeLuaClientHeader, "serializeLuaClientHeader");
1208
+ __name(formatLuaClientHeader, "formatLuaClientHeader");
1209
+ __name2(formatLuaClientHeader, "formatLuaClientHeader");
1210
+ __name(luaClientMetricLabels, "luaClientMetricLabels");
1211
+ __name2(luaClientMetricLabels, "luaClientMetricLabels");
1212
+ AUTHZ_PROJECTION_VERSION = 1;
1213
+ ProjectedScopeSchema = z3.string().min(1).max(128);
1214
+ DisplayRoleSchema = z3.object({
1215
+ role: z3.string().min(1).max(128),
1216
+ boundTo: z3.enum([
1217
+ "org",
1218
+ "agent",
1219
+ "platform"
1220
+ ]),
1221
+ resourceId: z3.string().min(1).max(256)
1222
+ }).passthrough();
1223
+ AuthorizationPrincipalSchema = z3.object({
1224
+ subjectType: SubjectTypeSchema,
1225
+ subjectId: z3.string().min(1).max(256)
1226
+ }).passthrough();
1227
+ CredentialContextSchema = z3.object({
1228
+ type: CredentialTypeSchema,
1229
+ id: z3.string().min(1).max(256).optional(),
1230
+ owner: z3.object({
1231
+ type: z3.enum([
1232
+ "user",
1233
+ "org",
1234
+ "service"
1235
+ ]),
1236
+ id: z3.string().min(1).max(256)
1237
+ }).passthrough().optional(),
1238
+ compatibility: z3.object({
1239
+ mode: z3.literal("legacy-owner-delegation")
1240
+ }).passthrough().optional()
1241
+ }).passthrough();
1242
+ ProjectionAnomalySchema = z3.enum([
1243
+ "grant-without-membership",
1244
+ "live-grant-on-inactive-membership"
1245
+ ]);
1246
+ ProjectedOrgSchema = z3.object({
1247
+ orgId: z3.string().min(1).max(256),
1248
+ name: z3.string().optional(),
1249
+ archived: z3.boolean(),
1250
+ /** Key into `capabilityProfiles`. */
1251
+ capabilityProfile: z3.string().min(1).max(64),
1252
+ displayRoles: z3.array(DisplayRoleSchema),
1253
+ /** Product data — notifications, ordering, labels. NEVER an authorization input. */
1254
+ membership: z3.object({
1255
+ rostered: z3.boolean(),
1256
+ active: z3.boolean()
1257
+ }).passthrough(),
1258
+ discoveredVia: z3.enum([
1259
+ "org-grant",
1260
+ "agent-grant-only"
1261
+ ]).describe("For user and staff principals, grant provenance: org-grant means a stored org grant produced the row. For owner-ceiling API keys, effective provenance: org-grant means the credential-owner intersection has org-level capability; agent-grant-only means the intersection reaches only an exact agent inside the org."),
1262
+ anomalies: z3.array(ProjectionAnomalySchema).optional()
1263
+ }).passthrough();
1264
+ ProjectedResourceSchema = z3.object({
1265
+ resourceType: z3.literal("agent"),
1266
+ resourceId: z3.string().min(1).max(256),
1267
+ kind: z3.enum([
1268
+ "agent",
1269
+ "space"
1270
+ ]),
1271
+ /** `null` for an org-less platform agent. */
1272
+ orgId: z3.string().min(1).max(256).nullable(),
1273
+ name: z3.string().optional(),
1274
+ visibility: z3.enum([
1275
+ "private",
1276
+ "public",
1277
+ "platform"
1278
+ ]),
1279
+ capabilityProfile: z3.string().min(1).max(64),
1280
+ displayRoles: z3.array(DisplayRoleSchema),
1281
+ discoveredVia: z3.enum([
1282
+ "org-cascade",
1283
+ "agent-grant",
1284
+ "owner-override",
1285
+ "platform-allowlist"
1286
+ ]),
1287
+ /** Product data only. A roster row confers nothing. */
1288
+ rostered: z3.boolean()
1289
+ }).passthrough();
1290
+ CapabilityProfilesSchema = z3.record(z3.string().min(1).max(64), z3.array(ProjectedScopeSchema));
1291
+ RoleCatalogSchema = z3.record(z3.string().min(1).max(128), z3.object({
1292
+ label: z3.string()
1293
+ }).passthrough());
1294
+ EffectiveAuthorizationSchema = z3.object({
1295
+ version: z3.literal(AUTHZ_PROJECTION_VERSION),
1296
+ generatedAt: z3.string().min(1),
1297
+ authorizationPrincipal: AuthorizationPrincipalSchema,
1298
+ credentialContext: CredentialContextSchema.optional(),
1299
+ orgs: z3.array(ProjectedOrgSchema),
1300
+ /**
1301
+ * Present only when the caller holds platform grants. This is what replaces a
1302
+ * client-side staff check: staff-ness is a scope on a resource, not an email
1303
+ * suffix and not a boolean that would read `false` on an ordinary session.
1304
+ */
1305
+ platform: z3.object({
1306
+ capabilityProfile: z3.string().min(1).max(64)
1307
+ }).passthrough().optional(),
1308
+ capabilityProfiles: CapabilityProfilesSchema,
1309
+ roleCatalog: RoleCatalogSchema.optional()
1310
+ }).passthrough();
1311
+ ResourcePageSchema = z3.object({
1312
+ version: z3.literal(AUTHZ_PROJECTION_VERSION),
1313
+ generatedAt: z3.string().min(1),
1314
+ orgId: z3.string().min(1).max(256),
1315
+ resources: z3.array(ProjectedResourceSchema),
1316
+ capabilityProfiles: CapabilityProfilesSchema,
1317
+ /** `null` = last page. Opaque; do not construct one. */
1318
+ nextCursor: z3.string().nullable().describe("Opaque continuation. A non-null value may accompany a short or empty resource page when the server reaches its per-request scan cap; clients must continue until null.")
1319
+ }).passthrough();
1320
+ __name(parseEffectiveAuthorization, "parseEffectiveAuthorization");
1321
+ __name2(parseEffectiveAuthorization, "parseEffectiveAuthorization");
1322
+ __name(parseResourcePage, "parseResourcePage");
1323
+ __name2(parseResourcePage, "parseResourcePage");
1324
+ __name(capabilitiesFor, "capabilitiesFor");
1325
+ __name2(capabilitiesFor, "capabilitiesFor");
1326
+ __name(isKnownProfile, "isKnownProfile");
1327
+ __name2(isKnownProfile, "isKnownProfile");
1328
+ __name(hasCapability, "hasCapability");
1329
+ __name2(hasCapability, "hasCapability");
1052
1330
  }
1053
1331
  });
1054
1332
 
@@ -1070,7 +1348,7 @@ var init_baskets = __esm({
1070
1348
  // src/config/constants.ts
1071
1349
  import { join } from "path";
1072
1350
  import { homedir } from "os";
1073
- var CLI_CONFIG_DIR, VERSION_CHECK_FILE, TELEMETRY_FILE, CLI_CACHE_FILE, BASE_URLS, CREDENTIALS_FILE, SANDBOX_STORAGE_FILE, AUTH_STORAGE_FILE;
1351
+ var CLI_CONFIG_DIR, VERSION_CHECK_FILE, TELEMETRY_FILE, CLI_CACHE_FILE, BASE_URLS, CREDENTIALS_FILE, FIREBASE_WEB_API_KEY, SANDBOX_STORAGE_FILE, AUTH_STORAGE_FILE;
1074
1352
  var init_constants = __esm({
1075
1353
  "src/config/constants.ts"() {
1076
1354
  "use strict";
@@ -1086,6 +1364,7 @@ var init_constants = __esm({
1086
1364
  CDN: "https://cdn.heylua.ai"
1087
1365
  };
1088
1366
  CREDENTIALS_FILE = join(CLI_CONFIG_DIR, "credentials");
1367
+ FIREBASE_WEB_API_KEY = process.env.LUA_FIREBASE_WEB_API_KEY || "AIzaSyAGI0AxOz6UmtJgwFIA_UNTuYUEgQRRGhw";
1089
1368
  SANDBOX_STORAGE_FILE = join(CLI_CONFIG_DIR, "sandbox.json");
1090
1369
  AUTH_STORAGE_FILE = join(CLI_CONFIG_DIR, "auth.json");
1091
1370
  }
@@ -1134,13 +1413,105 @@ var init_auth_error = __esm({
1134
1413
  }
1135
1414
  });
1136
1415
 
1416
+ // src/utils/package-root.ts
1417
+ import { readFileSync, existsSync } from "fs";
1418
+ import { fileURLToPath, pathToFileURL } from "url";
1419
+ import { dirname, join as join2 } from "path";
1420
+ function locate() {
1421
+ if (cachedRoot && cachedPkg) return {
1422
+ root: cachedRoot,
1423
+ pkg: cachedPkg
1424
+ };
1425
+ let dir = dirname(fileURLToPath(import.meta.url));
1426
+ while (true) {
1427
+ const candidate = join2(dir, "package.json");
1428
+ if (existsSync(candidate)) {
1429
+ try {
1430
+ const parsed = JSON.parse(readFileSync(candidate, "utf8"));
1431
+ if (parsed?.name === "lua-cli") {
1432
+ cachedRoot = dir;
1433
+ cachedPkg = parsed;
1434
+ return {
1435
+ root: dir,
1436
+ pkg: parsed
1437
+ };
1438
+ }
1439
+ } catch {
1440
+ }
1441
+ }
1442
+ const parent = dirname(dir);
1443
+ if (parent === dir) break;
1444
+ dir = parent;
1445
+ }
1446
+ throw new Error("Could not locate lua-cli package root from " + fileURLToPath(import.meta.url));
1447
+ }
1448
+ function getCliVersion() {
1449
+ try {
1450
+ return locate().pkg.version;
1451
+ } catch {
1452
+ return "0.0.0";
1453
+ }
1454
+ }
1455
+ var cachedRoot, cachedPkg;
1456
+ var init_package_root = __esm({
1457
+ "src/utils/package-root.ts"() {
1458
+ "use strict";
1459
+ cachedRoot = null;
1460
+ cachedPkg = null;
1461
+ __name(locate, "locate");
1462
+ __name(getCliVersion, "getCliVersion");
1463
+ }
1464
+ });
1465
+
1466
+ // src/utils/lua-fetch.ts
1467
+ function luaClientHeaderValue() {
1468
+ return formatLuaClientHeader("cli", getCliVersion());
1469
+ }
1470
+ function headerRecord(headers) {
1471
+ if (!headers) return {};
1472
+ if (headers instanceof Headers) return Object.fromEntries(headers.entries());
1473
+ if (Array.isArray(headers)) return Object.fromEntries(headers);
1474
+ const record = {};
1475
+ for (const [name, value] of Object.entries(headers)) {
1476
+ if (typeof value === "string") record[name] = value;
1477
+ }
1478
+ return record;
1479
+ }
1480
+ function luaFetch(input, init = {}) {
1481
+ const headers = headerRecord(init.headers);
1482
+ for (const name of Object.keys(headers)) {
1483
+ if (name.toLowerCase() === LUA_CLIENT_HEADER.toLowerCase()) delete headers[name];
1484
+ }
1485
+ headers[LUA_CLIENT_HEADER] = luaClientHeaderValue();
1486
+ return fetch(input, {
1487
+ ...init,
1488
+ headers
1489
+ });
1490
+ }
1491
+ var init_lua_fetch = __esm({
1492
+ "src/utils/lua-fetch.ts"() {
1493
+ "use strict";
1494
+ init_dist();
1495
+ init_package_root();
1496
+ __name(luaClientHeaderValue, "luaClientHeaderValue");
1497
+ __name(headerRecord, "headerRecord");
1498
+ __name(luaFetch, "luaFetch");
1499
+ }
1500
+ });
1501
+
1137
1502
  // src/api/http.client.ts
1138
1503
  import { randomUUID } from "crypto";
1504
+ function parseRetryAfter(raw) {
1505
+ if (!raw) return void 0;
1506
+ const seconds = Number.parseInt(raw, 10);
1507
+ return Number.isFinite(seconds) && seconds > 0 ? seconds : void 0;
1508
+ }
1139
1509
  var HttpClient;
1140
1510
  var init_http_client = __esm({
1141
1511
  "src/api/http.client.ts"() {
1142
1512
  "use strict";
1143
1513
  init_auth_error();
1514
+ init_lua_fetch();
1144
1515
  HttpClient = class {
1145
1516
  static {
1146
1517
  __name(this, "HttpClient");
@@ -1164,7 +1535,7 @@ var init_http_client = __esm({
1164
1535
  const controller = new AbortController();
1165
1536
  const timeoutId = setTimeout(() => controller.abort(), 3e4);
1166
1537
  try {
1167
- const response = await fetch(url, {
1538
+ const response = await luaFetch(url, {
1168
1539
  ...options,
1169
1540
  signal: controller.signal,
1170
1541
  headers: {
@@ -1203,6 +1574,7 @@ Check that your API key has access to this agent/organization.`);
1203
1574
  message: errorData.message || `HTTP ${response.status}: ${response.statusText}`,
1204
1575
  statusCode: response.status,
1205
1576
  error: errorData.error,
1577
+ retryAfterSeconds: parseRetryAfter(response.headers.get("retry-after")),
1206
1578
  ...errorData
1207
1579
  }
1208
1580
  };
@@ -1301,7 +1673,8 @@ Check that your API key has access to this agent/organization.`);
1301
1673
  throw error;
1302
1674
  }
1303
1675
  if (attempt < maxRetries) {
1304
- const backoff = this.calculateBackoff(attempt);
1676
+ const serverDelay = Number(lastResult?.error?.retryAfterSeconds ?? 0) * 1e3;
1677
+ const backoff = Math.max(this.calculateBackoff(attempt), serverDelay);
1305
1678
  await new Promise((resolve) => setTimeout(resolve, backoff));
1306
1679
  }
1307
1680
  }
@@ -1336,6 +1709,19 @@ Check that your API key has access to this agent/organization.`);
1336
1709
  });
1337
1710
  }
1338
1711
  /**
1712
+ * Performs one HTTP POST attempt.
1713
+ *
1714
+ * Use this only when a successful response contains a one-time secret that
1715
+ * cannot be recovered after an ambiguous network or server failure.
1716
+ */
1717
+ async httpPostOnce(url, data, headers) {
1718
+ return this.retryableRequest(this.baseUrl + url, {
1719
+ method: "POST",
1720
+ body: data ? JSON.stringify(data) : void 0,
1721
+ headers
1722
+ }, 0);
1723
+ }
1724
+ /**
1339
1725
  * Performs an HTTP PUT request
1340
1726
  * @param url - The relative URL path to request (will be appended to baseUrl)
1341
1727
  * @param data - Optional request body data (will be JSON stringified)
@@ -1379,6 +1765,7 @@ Check that your API key has access to this agent/organization.`);
1379
1765
  });
1380
1766
  }
1381
1767
  };
1768
+ __name(parseRetryAfter, "parseRetryAfter");
1382
1769
  }
1383
1770
  });
1384
1771
 
@@ -1392,13 +1779,13 @@ var init_auth_api_service = __esm({
1392
1779
 
1393
1780
  // src/services/auth.ts
1394
1781
  import "dotenv/config";
1395
- import { readFileSync, writeFileSync, mkdirSync, unlinkSync } from "fs";
1782
+ import { readFileSync as readFileSync2, writeFileSync, mkdirSync, unlinkSync } from "fs";
1396
1783
  function getToken() {
1397
1784
  if (process.env.LUA_API_KEY) {
1398
1785
  return process.env.LUA_API_KEY;
1399
1786
  }
1400
1787
  try {
1401
- const token = readFileSync(CREDENTIALS_FILE, "utf8").trim();
1788
+ const token = readFileSync2(CREDENTIALS_FILE, "utf8").trim();
1402
1789
  if (token) return token;
1403
1790
  } catch {
1404
1791
  }
@@ -2490,13 +2877,6 @@ var init_version_check = __esm({
2490
2877
  }
2491
2878
  });
2492
2879
 
2493
- // src/utils/package-root.ts
2494
- var init_package_root = __esm({
2495
- "src/utils/package-root.ts"() {
2496
- "use strict";
2497
- }
2498
- });
2499
-
2500
2880
  // src/services/analytics.ts
2501
2881
  import { PostHog } from "posthog-node";
2502
2882
  var init_analytics = __esm({
@@ -4244,8 +4624,7 @@ var init_user_data_api_service = __esm({
4244
4624
  * @throws Error if the message sending fails or the request is unsuccessful
4245
4625
  */
4246
4626
  async sendMessage(messages) {
4247
- const user = await this.getAdminUser();
4248
- const response = await this.httpPost(`/admin/agents/${this.agentId}/conversations/${user.uid}`, {
4627
+ const response = await this.httpPost(`/admin/agents/${this.agentId}/conversations/me`, {
4249
4628
  messages
4250
4629
  }, {
4251
4630
  Authorization: `Bearer ${this.apiKey}`
@@ -4256,20 +4635,6 @@ var init_user_data_api_service = __esm({
4256
4635
  return response.data;
4257
4636
  }
4258
4637
  /**
4259
- * Gets the admin user for the specific agent
4260
- * @returns Promise resolving to the admin user data
4261
- * @throws Error if the admin user cannot be retrieved or the request is unsuccessful
4262
- */
4263
- async getAdminUser() {
4264
- const response = await this.httpGet(`/admin`, {
4265
- Authorization: `Bearer ${this.apiKey}`
4266
- });
4267
- if (!response.success) {
4268
- throw new Error(response.error?.message || "Failed to get admin user");
4269
- }
4270
- return response.data;
4271
- }
4272
- /**
4273
4638
  * Gets the chat history for the current user and agent
4274
4639
  * @returns Promise resolving to an array of chat messages
4275
4640
  * @throws Error if the chat history cannot be retrieved or the request is unsuccessful
@@ -5486,6 +5851,7 @@ var CdnApi;
5486
5851
  var init_cdn_api_service = __esm({
5487
5852
  "src/api/cdn.api.service.ts"() {
5488
5853
  "use strict";
5854
+ init_lua_fetch();
5489
5855
  CdnApi = class {
5490
5856
  static {
5491
5857
  __name(this, "CdnApi");
@@ -5504,7 +5870,7 @@ var init_cdn_api_service = __esm({
5504
5870
  async upload(file) {
5505
5871
  const formData = new FormData();
5506
5872
  formData.append("file", file, file.name);
5507
- const response = await fetch(`${this.baseUrl}/upload`, {
5873
+ const response = await luaFetch(`${this.baseUrl}/upload`, {
5508
5874
  method: "POST",
5509
5875
  headers: {
5510
5876
  Authorization: `Bearer ${this.apiKey}`
@@ -5522,7 +5888,7 @@ var init_cdn_api_service = __esm({
5522
5888
  * Fetches a file from the CDN by its ID
5523
5889
  */
5524
5890
  async get(fileId) {
5525
- const response = await fetch(`${this.baseUrl}/${fileId}`);
5891
+ const response = await luaFetch(`${this.baseUrl}/${fileId}`);
5526
5892
  if (!response.ok) {
5527
5893
  throw new Error(`File not found: ${response.status}`);
5528
5894
  }
@@ -6484,6 +6850,7 @@ var LuaWebhook = class {
6484
6850
  querySchema;
6485
6851
  headerSchema;
6486
6852
  bodySchema;
6853
+ secret;
6487
6854
  executeFunction;
6488
6855
  /**
6489
6856
  * Creates a new LuaWebhook instance.
@@ -6505,9 +6872,17 @@ var LuaWebhook = class {
6505
6872
  this.querySchema = config.querySchema;
6506
6873
  this.headerSchema = config.headerSchema;
6507
6874
  this.bodySchema = config.bodySchema;
6875
+ this.secret = config.secret;
6508
6876
  this.executeFunction = config.execute;
6509
6877
  }
6510
6878
  /**
6879
+ * Gets the webhook's signing key, if one is configured.
6880
+ * Never print this — it is the shared secret callers sign with.
6881
+ */
6882
+ getSecret() {
6883
+ return this.secret;
6884
+ }
6885
+ /**
6511
6886
  * Gets the webhook name.
6512
6887
  */
6513
6888
  getName() {
@@ -6584,12 +6959,16 @@ var LuaTrigger = class {
6584
6959
  verify;
6585
6960
  filter;
6586
6961
  transform;
6962
+ tool;
6587
6963
  constructor(config) {
6588
6964
  if (!config.name || !config.name.trim()) {
6589
6965
  throw new Error("LuaTrigger requires a non-empty `name` (used as the server-side identifier).");
6590
6966
  }
6591
- if (!config.verify && !config.filter && !config.transform) {
6592
- throw new Error("LuaTrigger requires at least one of verify, filter, or transform.");
6967
+ if (!config.verify && !config.filter && !config.transform && !config.tool) {
6968
+ throw new Error("LuaTrigger requires at least one of verify, filter, transform, or tool.");
6969
+ }
6970
+ if (config.tool && (!config.tool.name || !config.tool.name.trim())) {
6971
+ throw new Error("LuaTrigger `tool` requires a non-empty `name` (the bare authored tool name).");
6593
6972
  }
6594
6973
  this.name = config.name;
6595
6974
  this.description = config.description;
@@ -6598,6 +6977,7 @@ var LuaTrigger = class {
6598
6977
  this.verify = config.verify;
6599
6978
  this.filter = config.filter;
6600
6979
  this.transform = config.transform;
6980
+ this.tool = config.tool;
6601
6981
  }
6602
6982
  getName() {
6603
6983
  return this.name;