lua-cli 3.37.0 → 3.38.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
@@ -642,7 +642,6 @@ var init_constants = __esm({
642
642
  API: process.env.LUA_API_URL || "https://api.heylua.ai",
643
643
  AUTH: process.env.LUA_AUTH_URL || "https://auth.heylua.ai",
644
644
  CHAT: process.env.LUA_API_URL || "https://api.heylua.ai",
645
- WEBHOOK: process.env.LUA_WEBHOOK_URL || "https://webhook.heylua.ai",
646
645
  CDN: "https://cdn.heylua.ai"
647
646
  };
648
647
  CREDENTIALS_FILE = join(CLI_CONFIG_DIR, "credentials");
@@ -841,6 +840,12 @@ var init_package_root = __esm({
841
840
  import { z } from "zod";
842
841
  import { z as z2 } from "zod";
843
842
  import { z as z3 } from "zod";
843
+ function isChannelConversationChannel(channel) {
844
+ return !!channel && CHANNEL_CONVERSATION_CHANNELS.includes(channel);
845
+ }
846
+ function isChannelConversationThread(threadId, channel) {
847
+ return !!threadId && isChannelConversationChannel(channel);
848
+ }
844
849
  function isPersonaTextObject(value3) {
845
850
  if (value3 === null || typeof value3 !== "object" || Array.isArray(value3)) return false;
846
851
  const obj = value3;
@@ -1403,6 +1408,101 @@ function buildDefaultPersona(agentName) {
1403
1408
  function buildPersonalSpaceStartingPersona(agentName) {
1404
1409
  return PERSONAL_SPACE_STARTING_PERSONA.replace(new RegExp(AGENT_NAME_TOKEN.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), () => agentName || "Your assistant");
1405
1410
  }
1411
+ function maxDrainsForTier(tier) {
1412
+ return tier === "enterprise" ? LOG_DRAIN_MAX_PER_ORG_ENTERPRISE : LOG_DRAIN_MAX_PER_ORG;
1413
+ }
1414
+ function resolvePresetEndpoint(type, site) {
1415
+ const template3 = LOG_DRAIN_ENDPOINT_TEMPLATES[type];
1416
+ if (!template3 || !site) return void 0;
1417
+ return template3.replace("{site}", site);
1418
+ }
1419
+ function nextLogDrainQuotaResetAt(now) {
1420
+ return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1)).toISOString();
1421
+ }
1422
+ function logDrainQuotaWindowExpired(usage, now) {
1423
+ const resetsAt = Date.parse(usage?.resetsAt ?? "");
1424
+ if (!Number.isFinite(resetsAt)) return true;
1425
+ return now.getTime() >= resetsAt;
1426
+ }
1427
+ function logDrainQuotaRatio(usage, now) {
1428
+ if (!usage) return 0;
1429
+ if (now && logDrainQuotaWindowExpired(usage, now)) return 0;
1430
+ const dimension = /* @__PURE__ */ __name2((used, limit) => {
1431
+ if (!Number.isFinite(limit) || limit <= 0) return 0;
1432
+ if (!Number.isFinite(used) || used <= 0) return 0;
1433
+ return used / limit;
1434
+ }, "dimension");
1435
+ return Math.max(dimension(usage.usedEvents, usage.eventsPerDay), dimension(usage.usedBytes, usage.bytesPerDay));
1436
+ }
1437
+ function logDrainQuotaDegradationForRatio(ratio) {
1438
+ if (!Number.isFinite(ratio)) return "none";
1439
+ if (ratio >= LOG_DRAIN_QUOTA_PAUSE_RATIO) return "paused";
1440
+ if (ratio >= LOG_DRAIN_QUOTA_DROP_INFO_RATIO) return "info_dropped";
1441
+ if (ratio >= LOG_DRAIN_QUOTA_DROP_DEBUG_RATIO) return "debug_dropped";
1442
+ return "none";
1443
+ }
1444
+ function logDrainQuotaDegradation(usage, now) {
1445
+ return logDrainQuotaDegradationForRatio(logDrainQuotaRatio(usage, now));
1446
+ }
1447
+ function logDrainQuotaWarned(usage, now) {
1448
+ return logDrainQuotaRatio(usage, now) >= LOG_DRAIN_QUOTA_WARN_RATIO;
1449
+ }
1450
+ function logDrainQuotaDroppedSeverities(degradation) {
1451
+ switch (degradation) {
1452
+ case "debug_dropped":
1453
+ return [
1454
+ 5
1455
+ ];
1456
+ case "info_dropped":
1457
+ return [
1458
+ 5,
1459
+ 9
1460
+ ];
1461
+ default:
1462
+ return [];
1463
+ }
1464
+ }
1465
+ function encodeLogCursor(c) {
1466
+ return Buffer.from(JSON.stringify({
1467
+ t: c.t,
1468
+ id: c.id
1469
+ }), "utf8").toString("base64url");
1470
+ }
1471
+ function decodeLogCursor(raw) {
1472
+ if (typeof raw !== "string") return null;
1473
+ const trimmed2 = raw.trim();
1474
+ if (!trimmed2 || trimmed2.length > LOG_CURSOR_MAX_LENGTH) return null;
1475
+ let parsed;
1476
+ try {
1477
+ parsed = JSON.parse(Buffer.from(trimmed2, "base64url").toString("utf8"));
1478
+ } catch {
1479
+ return null;
1480
+ }
1481
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
1482
+ const { t, id } = parsed;
1483
+ if (typeof t !== "number" || !Number.isSafeInteger(t) || t < 0) return null;
1484
+ if (typeof id !== "string" || !id || id.length > LOG_CURSOR_MAX_ID_LENGTH) return null;
1485
+ return {
1486
+ t,
1487
+ id
1488
+ };
1489
+ }
1490
+ function parseLogTimeBound(raw, now = /* @__PURE__ */ new Date()) {
1491
+ if (typeof raw !== "string") return null;
1492
+ const value3 = raw.trim();
1493
+ if (!value3) return null;
1494
+ const relative3 = LOG_RELATIVE_TIME_PATTERN.exec(value3);
1495
+ if (relative3) {
1496
+ const amount = Number(relative3[1]);
1497
+ const unitMs = RELATIVE_UNIT_MS[relative3[2]];
1498
+ if (!Number.isFinite(amount) || unitMs === void 0) return null;
1499
+ const at2 = now.getTime() - amount * unitMs;
1500
+ return Number.isFinite(at2) ? new Date(at2) : null;
1501
+ }
1502
+ if (!LOG_ISO_TIME_PATTERN.test(value3)) return null;
1503
+ const at = Date.parse(value3);
1504
+ return Number.isNaN(at) ? null : new Date(at);
1505
+ }
1406
1506
  function isRecord(value3) {
1407
1507
  return typeof value3 === "object" && value3 !== null;
1408
1508
  }
@@ -1973,17 +2073,17 @@ function isWorkflowApprovalOutput(value3) {
1973
2073
  return typeof v.approved === "boolean" && WORKFLOW_APPROVAL_OUTPUT_DECISIONS.includes(v.decision) && typeof v.text === "string";
1974
2074
  }
1975
2075
  function extractSingleJsonValue(text) {
1976
- const trimmed = (text ?? "").trim();
1977
- if (!trimmed) return {
2076
+ const trimmed2 = (text ?? "").trim();
2077
+ if (!trimmed2) return {
1978
2078
  reason: "reply is empty"
1979
2079
  };
1980
2080
  const fenced = [
1981
- ...trimmed.matchAll(JSON_FENCE_RE)
2081
+ ...trimmed2.matchAll(JSON_FENCE_RE)
1982
2082
  ];
1983
2083
  if (fenced.length > 1) return {
1984
2084
  reason: "reply carries more than one fenced block"
1985
2085
  };
1986
- const candidate = fenced.length === 1 ? fenced[0][1].trim() : trimmed;
2086
+ const candidate = fenced.length === 1 ? fenced[0][1].trim() : trimmed2;
1987
2087
  try {
1988
2088
  return {
1989
2089
  value: JSON.parse(candidate)
@@ -1994,17 +2094,17 @@ function extractSingleJsonValue(text) {
1994
2094
  };
1995
2095
  }
1996
2096
  const opens = [
1997
- trimmed.indexOf("{"),
1998
- trimmed.indexOf("[")
2097
+ trimmed2.indexOf("{"),
2098
+ trimmed2.indexOf("[")
1999
2099
  ].filter((i) => i !== -1);
2000
2100
  const start = opens.length ? Math.min(...opens) : -1;
2001
- const end = Math.max(trimmed.lastIndexOf("}"), trimmed.lastIndexOf("]"));
2101
+ const end = Math.max(trimmed2.lastIndexOf("}"), trimmed2.lastIndexOf("]"));
2002
2102
  if (start === -1 || end <= start) return {
2003
2103
  reason: "reply is not JSON"
2004
2104
  };
2005
2105
  try {
2006
2106
  return {
2007
- value: JSON.parse(trimmed.slice(start, end + 1))
2107
+ value: JSON.parse(trimmed2.slice(start, end + 1))
2008
2108
  };
2009
2109
  } catch {
2010
2110
  return {
@@ -2080,12 +2180,39 @@ function effectiveAgentFeatures(base, override, rule) {
2080
2180
  if (rule === "wholesale") return override || base || void 0;
2081
2181
  return effectiveAgentFeatureRows(base, override).rows;
2082
2182
  }
2083
- var __defProp2, __name2, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_STANDING_START_TOOLS, 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, WORKFLOW_RETRY_BACKOFFS, WORKFLOW_RETRY_MIN_ATTEMPTS, WORKFLOW_RETRY_POLICY_KEYS, WORKFLOW_RETRY_MAX_ATTEMPTS, WORKFLOW_RETRY_ENGINE_KEYS, WORKFLOW_JOB_RESOURCES, WORKFLOW_SIDE_EFFECTS, WORKFLOW_AGENT_EFFORTS, WORKFLOW_MODEL_CLASSES, WORKFLOW_TASK_CLASSES, WORKFLOW_MODEL_TRAITS, WORKFLOW_MODEL_REASON_MAX_LENGTH, WORKFLOW_MODELS_COMPOSE_MODES, WORKFLOW_MODELS_PIN_MODES, WORKFLOW_MODELS_FALLBACK_MODES, WORKFLOW_MODELS_CONSENT_ACTIONS_MIN, WORKFLOW_DEFAULT_TASK_CLASS_MAP, WORKFLOW_MODELS_MAX_CLASS_DEFAULT, WORKFLOW_AUTONOMY_FORMS, WORKFLOW_AUTONOMY_NUMERIC_KEYS, WORKFLOW_AUTONOMY_ENVELOPE_KEYS, WORKFLOW_AUTONOMY_ENABLED_DEFAULT, WORKFLOW_AUTONOMY_DEFAULTS, WORKFLOW_AUTONOMY_CEILINGS, WORKFLOW_AUTONOMY_MAX_RUNS_PER_HOUR_ENV, WORKFLOW_AUTONOMY_MAX_RUNS_PER_HOUR_CEILING, WORKFLOW_AUTONOMY_FORMS_DEFAULT, WORKFLOW_AUTONOMY_MIN_VALUE, WORKFLOW_JOB_RANGES, WORKFLOW_JOB_RANGE_MEMBERS, WORKFLOW_SINGLE_STEP_TYPES, WORKFLOW_HITL_ENTRY_TYPES, WORKFLOW_ARM_ENTRY_TYPES, WORKFLOW_HITL_ARM_CONTAINERS, WORKFLOW_GRAPH_ENTRY_STEP_KINDS, WORKFLOW_ARM_ENTRY_STEP_KINDS, WORKFLOW_BUDGET_MAX_DURATION_SECONDS, MODEL_ID_BYOK_PROVIDERS, MODEL_CLASS_REF_PREFIX, MODEL_SNAPSHOT_SUFFIX, REASONING_EFFORT_VALUES, IMPLICIT_MODEL_SELECTION_SOURCES, PLATFORM_FALLBACK_MODEL_SOURCE, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, PERSONAL_SPACE_STARTING_PERSONA, AGENT_LOG_SOURCES, CORE_DRAINING_CODE, CORE_DRAINING_DEFAULT_RETRY_MS, CORE_DRAINING_MAX_RETRY_MS, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema, EventType, LUA_JOB_DEFAULT_TIMEOUT_SECONDS, LUA_JOB_MIN_TIMEOUT_SECONDS, LUA_JOB_MAX_TIMEOUT_SECONDS, KEYS, MAX_BODY_BYTES, MAX_ID_LENGTH, UTC_ISO, TEMPLATE_TRIGGER_URL_ENV_PREFIX, TEMPLATE_INSTALL_POLICY_PER_WORKSPACE_VALUES, SUBJECT_TYPES, SubjectTypeSchema, CREDENTIAL_TYPES, CredentialTypeSchema, DEVICE_OPERATIONS, DeviceOperationSchema, DEVICE_SCOPE_BY_OPERATION, DeviceBindingSchema, TYPED_API_KEY, IdSchema, SESSION_AUTH_TIME_MAX_S, PrincipalDescriptorSchema, ActorDescriptorSchema, PrincipalOwnerSchema, CredentialLifecycleSchema, GeneralCredentialDescriptorSchema, DeviceCredentialDescriptorSchema, GeneralPrincipalContextSchema, DeviceCredentialPrincipalContextSchema, RawPrincipalContextSchema, PrincipalContextSchema, DeviceCredentialClaimSchema, LUA_CLIENT_HEADER, LUA_CLIENT_APPS, SEMVER_PATTERN, WEB_RELEASE_PATTERN, CLIENT_HEADER_PATTERN, LUA_SESSION_ID_CLAIM, SESSION_REVOKED_CODE, AUTHZ_PROJECTION_VERSION, ProjectedScopeSchema, DisplayRoleSchema, AuthorizationPrincipalSchema, CredentialContextSchema, ProjectionAnomalySchema, ProjectedOrgSchema, ProjectedResourceSchema, CapabilityProfilesSchema, RoleCatalogSchema, EffectiveAuthorizationSchema, ResourcePageSchema, SYSTEM_USER_PREFIX, WORKFLOW_RUN_IN_FLIGHT, WORKFLOW_RUN_IDLE, WORKFLOW_RUN_TERMINAL, WORKFLOW_RUN_STATUSES, WORKFLOW_RUN_GATE_KINDS, WORKFLOW_STEP_STATUSES, WORKFLOW_STEP_IN_FLIGHT, WORKFLOW_SIGNAL_EVENT_SITES, ARCHIVE_WINDOW_MARGIN_DAYS, WORKFLOW_ORG_PURGING_TTL_S, WORKFLOW_ORG_PURGE_FORCE_AFTER_MS, IDEMPOTENCY_HOLDING_STATUSES, WORKFLOW_SCHEDULED_RUN_ID_PREFIX, CLOUD_TASK_RUN_ID_PREFIX, WORKFLOW_SCHEDULE_KEY_MAX, WORKFLOW_SCHEDULE_IDEMPOTENCY_KEY_PREFIX, WORKFLOW_OPERATION_ID_PREFIX, WORKFLOW_JOURNAL_PROTOCOL_VERSION, WORKFLOW_CONNECTION_KEY_RE, WORKFLOW_JOB_MODEL_SOURCE_LABELS, WORKFLOW_SUSPEND_KINDS, WORKFLOW_RUN_NOTIFICATION_SUSPENDED_KINDS, WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES, WORKFLOW_GOAL_JUDGE_SELF, REDACTED_PLACEHOLDER, PROVIDER_MESSAGE_MAX_CHARS, ERROR_MESSAGE_MAX_CHARS, SECRET_LITERAL_PATTERNS, SECRET_NAME, SECRET_PAIR_PATTERNS, GROUP_COUNT, WORKFLOW_SECRET_KEY_RE, WORKFLOW_RESERVED_SECRET_KEYS, SCRUB_INPUT_MAX_CHARS, SCRUB_CUT_BACKOFF_CHARS, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE, WORKFLOW_APPROVAL_OUTPUT_DECISIONS, WORKFLOW_APPROVAL_OUTPUT_SCHEMA, JSON_FENCE_RE, DEFAULT_ON_AGENT_FEATURES, SUBAGENT_PER_KEY_FLAG_ENV, PER_KEY_FLAG_VALUES;
2183
+ var __defProp2, __name2, PLUMBING_THREAD_CHANNELS, CHANNEL_CONVERSATION_CHANNELS, HIDDEN_THREAD_CHANNELS, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_STANDING_START_TOOLS, 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, WORKFLOW_RETRY_BACKOFFS, WORKFLOW_RETRY_MIN_ATTEMPTS, WORKFLOW_RETRY_POLICY_KEYS, WORKFLOW_RETRY_MAX_ATTEMPTS, WORKFLOW_RETRY_ENGINE_KEYS, WORKFLOW_JOB_RESOURCES, WORKFLOW_SIDE_EFFECTS, WORKFLOW_AGENT_EFFORTS, WORKFLOW_MODEL_CLASSES, WORKFLOW_TASK_CLASSES, WORKFLOW_MODEL_TRAITS, WORKFLOW_MODEL_REASON_MAX_LENGTH, WORKFLOW_MODELS_COMPOSE_MODES, WORKFLOW_MODELS_PIN_MODES, WORKFLOW_MODELS_FALLBACK_MODES, WORKFLOW_MODELS_CONSENT_ACTIONS_MIN, WORKFLOW_DEFAULT_TASK_CLASS_MAP, WORKFLOW_MODELS_MAX_CLASS_DEFAULT, WORKFLOW_AUTONOMY_FORMS, WORKFLOW_AUTONOMY_NUMERIC_KEYS, WORKFLOW_AUTONOMY_ENVELOPE_KEYS, WORKFLOW_AUTONOMY_ENABLED_DEFAULT, WORKFLOW_AUTONOMY_DEFAULTS, WORKFLOW_AUTONOMY_CEILINGS, WORKFLOW_AUTONOMY_MAX_RUNS_PER_HOUR_ENV, WORKFLOW_AUTONOMY_MAX_RUNS_PER_HOUR_CEILING, WORKFLOW_AUTONOMY_FORMS_DEFAULT, WORKFLOW_AUTONOMY_MIN_VALUE, WORKFLOW_JOB_RANGES, WORKFLOW_JOB_RANGE_MEMBERS, WORKFLOW_SINGLE_STEP_TYPES, WORKFLOW_HITL_ENTRY_TYPES, WORKFLOW_ARM_ENTRY_TYPES, WORKFLOW_HITL_ARM_CONTAINERS, WORKFLOW_GRAPH_ENTRY_STEP_KINDS, WORKFLOW_ARM_ENTRY_STEP_KINDS, WORKFLOW_BUDGET_MAX_DURATION_SECONDS, MODEL_ID_BYOK_PROVIDERS, MODEL_CLASS_REF_PREFIX, MODEL_SNAPSHOT_SUFFIX, REASONING_EFFORT_VALUES, IMPLICIT_MODEL_SELECTION_SOURCES, PLATFORM_FALLBACK_MODEL_SOURCE, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, PERSONAL_SPACE_STARTING_PERSONA, AGENT_LOG_SOURCES, AGENT_LOG_ENVIRONMENTS, LOG_DRAIN_TYPES_PHASE1, LOG_DRAIN_FORMATS, LOG_DRAIN_SEVERITIES, LOG_DRAIN_SOURCES, LOG_DRAIN_CONTENT_SOURCES, LOG_DRAIN_DEFAULT_SOURCES, LOG_DRAIN_MAX_PER_ORG, LOG_DRAIN_MAX_PER_ORG_ENTERPRISE, LOG_DRAIN_DATADOG_SITES, LOG_DRAIN_ENDPOINT_TEMPLATES, LOG_DRAIN_QUOTAS, LOG_DRAIN_QUOTA_WARN_RATIO, LOG_DRAIN_QUOTA_DROP_DEBUG_RATIO, LOG_DRAIN_QUOTA_DROP_INFO_RATIO, LOG_DRAIN_QUOTA_PAUSE_RATIO, LOG_CURSOR_MAX_LENGTH, LOG_CURSOR_MAX_ID_LENGTH, LOG_RELATIVE_TIME_PATTERN, LOG_ISO_TIME_PATTERN, RELATIVE_UNIT_MS, CORE_DRAINING_CODE, CORE_DRAINING_DEFAULT_RETRY_MS, CORE_DRAINING_MAX_RETRY_MS, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema, EventType, LUA_JOB_DEFAULT_TIMEOUT_SECONDS, LUA_JOB_MIN_TIMEOUT_SECONDS, LUA_JOB_MAX_TIMEOUT_SECONDS, KEYS, MAX_BODY_BYTES, MAX_ID_LENGTH, UTC_ISO, TEMPLATE_TRIGGER_URL_ENV_PREFIX, TEMPLATE_INSTALL_POLICY_PER_WORKSPACE_VALUES, SUBJECT_TYPES, SubjectTypeSchema, CREDENTIAL_TYPES, CredentialTypeSchema, DEVICE_OPERATIONS, DeviceOperationSchema, DEVICE_SCOPE_BY_OPERATION, DeviceBindingSchema, TYPED_API_KEY, IdSchema, SESSION_AUTH_TIME_MAX_S, PrincipalDescriptorSchema, ActorDescriptorSchema, PrincipalOwnerSchema, CredentialLifecycleSchema, GeneralCredentialDescriptorSchema, DeviceCredentialDescriptorSchema, GeneralPrincipalContextSchema, DeviceCredentialPrincipalContextSchema, RawPrincipalContextSchema, PrincipalContextSchema, DeviceCredentialClaimSchema, LUA_CLIENT_HEADER, LUA_CLIENT_APPS, SEMVER_PATTERN, WEB_RELEASE_PATTERN, CLIENT_HEADER_PATTERN, LUA_SESSION_ID_CLAIM, SESSION_REVOKED_CODE, AUTHZ_PROJECTION_VERSION, ProjectedScopeSchema, DisplayRoleSchema, AuthorizationPrincipalSchema, CredentialContextSchema, ProjectionAnomalySchema, ProjectedOrgSchema, ProjectedResourceSchema, CapabilityProfilesSchema, RoleCatalogSchema, EffectiveAuthorizationSchema, ResourcePageSchema, SYSTEM_USER_PREFIX, WORKFLOW_RUN_IN_FLIGHT, WORKFLOW_RUN_IDLE, WORKFLOW_RUN_TERMINAL, WORKFLOW_RUN_STATUSES, WORKFLOW_RUN_GATE_KINDS, WORKFLOW_STEP_STATUSES, WORKFLOW_STEP_IN_FLIGHT, WORKFLOW_SIGNAL_EVENT_SITES, ARCHIVE_WINDOW_MARGIN_DAYS, WORKFLOW_ORG_PURGING_TTL_S, WORKFLOW_ORG_PURGE_FORCE_AFTER_MS, IDEMPOTENCY_HOLDING_STATUSES, WORKFLOW_SCHEDULED_RUN_ID_PREFIX, CLOUD_TASK_RUN_ID_PREFIX, WORKFLOW_SCHEDULE_KEY_MAX, WORKFLOW_SCHEDULE_IDEMPOTENCY_KEY_PREFIX, WORKFLOW_OPERATION_ID_PREFIX, WORKFLOW_JOURNAL_PROTOCOL_VERSION, WORKFLOW_CONNECTION_KEY_RE, WORKFLOW_JOB_MODEL_SOURCE_LABELS, WORKFLOW_SUSPEND_KINDS, WORKFLOW_RUN_NOTIFICATION_SUSPENDED_KINDS, WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES, WORKFLOW_GOAL_JUDGE_SELF, REDACTED_PLACEHOLDER, PROVIDER_MESSAGE_MAX_CHARS, ERROR_MESSAGE_MAX_CHARS, SECRET_LITERAL_PATTERNS, SECRET_NAME, SECRET_PAIR_PATTERNS, GROUP_COUNT, WORKFLOW_SECRET_KEY_RE, WORKFLOW_RESERVED_SECRET_KEYS, SCRUB_INPUT_MAX_CHARS, SCRUB_CUT_BACKOFF_CHARS, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE, WORKFLOW_APPROVAL_OUTPUT_DECISIONS, WORKFLOW_APPROVAL_OUTPUT_SCHEMA, JSON_FENCE_RE, DEFAULT_ON_AGENT_FEATURES, SUBAGENT_PER_KEY_FLAG_ENV, PER_KEY_FLAG_VALUES;
2084
2184
  var init_dist = __esm({
2085
2185
  "../shared-types/dist/index.mjs"() {
2086
2186
  "use strict";
2087
2187
  __defProp2 = Object.defineProperty;
2088
2188
  __name2 = /* @__PURE__ */ __name((target, value3) => __defProp2(target, "name", { value: value3, configurable: true }), "__name");
2189
+ PLUMBING_THREAD_CHANNELS = [
2190
+ "trigger",
2191
+ "template-install",
2192
+ "template-uninstall"
2193
+ ];
2194
+ CHANNEL_CONVERSATION_CHANNELS = [
2195
+ "whatsapp",
2196
+ "slack",
2197
+ "teams",
2198
+ "front",
2199
+ "email",
2200
+ "messagebird",
2201
+ "sms",
2202
+ "mms",
2203
+ "rcs",
2204
+ "instagram",
2205
+ "facebook",
2206
+ "imessage"
2207
+ ];
2208
+ HIDDEN_THREAD_CHANNELS = [
2209
+ ...PLUMBING_THREAD_CHANNELS,
2210
+ ...CHANNEL_CONVERSATION_CHANNELS
2211
+ ];
2212
+ __name(isChannelConversationChannel, "isChannelConversationChannel");
2213
+ __name2(isChannelConversationChannel, "isChannelConversationChannel");
2214
+ __name(isChannelConversationThread, "isChannelConversationThread");
2215
+ __name2(isChannelConversationThread, "isChannelConversationThread");
2089
2216
  __name(isPersonaTextObject, "isPersonaTextObject");
2090
2217
  __name2(isPersonaTextObject, "isPersonaTextObject");
2091
2218
  __name(flattenPersonaText, "flattenPersonaText");
@@ -2757,6 +2884,124 @@ This text is who you are for this person. As you learn them, their name, their w
2757
2884
  "workflow-script",
2758
2885
  "workflow"
2759
2886
  ];
2887
+ AGENT_LOG_ENVIRONMENTS = [
2888
+ "production",
2889
+ "sandbox"
2890
+ ];
2891
+ LOG_DRAIN_TYPES_PHASE1 = [
2892
+ "http",
2893
+ "otlp",
2894
+ "datadog",
2895
+ "betterstack"
2896
+ ];
2897
+ LOG_DRAIN_FORMATS = [
2898
+ "json",
2899
+ "ndjson"
2900
+ ];
2901
+ LOG_DRAIN_SEVERITIES = [
2902
+ "debug",
2903
+ "info",
2904
+ "warn",
2905
+ "error"
2906
+ ];
2907
+ LOG_DRAIN_SOURCES = [
2908
+ ...AGENT_LOG_SOURCES,
2909
+ "execution",
2910
+ // ── phase 2 ──
2911
+ "delivery",
2912
+ "trigger-execution",
2913
+ "job-execution",
2914
+ "workflow-run",
2915
+ "workflow-audit"
2916
+ ];
2917
+ LOG_DRAIN_CONTENT_SOURCES = [
2918
+ "user_message",
2919
+ "agent_response"
2920
+ ];
2921
+ LOG_DRAIN_DEFAULT_SOURCES = [
2922
+ "skill",
2923
+ "job",
2924
+ "webhook",
2925
+ "preprocessor",
2926
+ "postprocessor",
2927
+ "workflow-step",
2928
+ "workflow-script",
2929
+ "device",
2930
+ "runtime",
2931
+ "model-resolver",
2932
+ "trigger",
2933
+ "device-trigger",
2934
+ "workflow",
2935
+ "mcp",
2936
+ "rag",
2937
+ "execution",
2938
+ "agent_error"
2939
+ ];
2940
+ LOG_DRAIN_MAX_PER_ORG = 5;
2941
+ LOG_DRAIN_MAX_PER_ORG_ENTERPRISE = 10;
2942
+ __name(maxDrainsForTier, "maxDrainsForTier");
2943
+ __name2(maxDrainsForTier, "maxDrainsForTier");
2944
+ LOG_DRAIN_DATADOG_SITES = [
2945
+ "datadoghq.com",
2946
+ "datadoghq.eu",
2947
+ "us3.datadoghq.com",
2948
+ "us5.datadoghq.com",
2949
+ "ap1.datadoghq.com",
2950
+ "ddog-gov.com"
2951
+ ];
2952
+ LOG_DRAIN_ENDPOINT_TEMPLATES = {
2953
+ datadog: "https://http-intake.logs.{site}/api/v2/logs"
2954
+ };
2955
+ __name(resolvePresetEndpoint, "resolvePresetEndpoint");
2956
+ __name2(resolvePresetEndpoint, "resolvePresetEndpoint");
2957
+ LOG_DRAIN_QUOTAS = {
2958
+ team: {
2959
+ eventsPerDay: 1e6,
2960
+ bytesPerDay: 1 * 1024 ** 3
2961
+ },
2962
+ business: {
2963
+ eventsPerDay: 1e7,
2964
+ bytesPerDay: 10 * 1024 ** 3
2965
+ },
2966
+ enterprise: {
2967
+ eventsPerDay: Number.MAX_SAFE_INTEGER,
2968
+ bytesPerDay: Number.MAX_SAFE_INTEGER
2969
+ }
2970
+ };
2971
+ LOG_DRAIN_QUOTA_WARN_RATIO = 0.8;
2972
+ LOG_DRAIN_QUOTA_DROP_DEBUG_RATIO = 1;
2973
+ LOG_DRAIN_QUOTA_DROP_INFO_RATIO = 1.25;
2974
+ LOG_DRAIN_QUOTA_PAUSE_RATIO = 1.5;
2975
+ __name(nextLogDrainQuotaResetAt, "nextLogDrainQuotaResetAt");
2976
+ __name2(nextLogDrainQuotaResetAt, "nextLogDrainQuotaResetAt");
2977
+ __name(logDrainQuotaWindowExpired, "logDrainQuotaWindowExpired");
2978
+ __name2(logDrainQuotaWindowExpired, "logDrainQuotaWindowExpired");
2979
+ __name(logDrainQuotaRatio, "logDrainQuotaRatio");
2980
+ __name2(logDrainQuotaRatio, "logDrainQuotaRatio");
2981
+ __name(logDrainQuotaDegradationForRatio, "logDrainQuotaDegradationForRatio");
2982
+ __name2(logDrainQuotaDegradationForRatio, "logDrainQuotaDegradationForRatio");
2983
+ __name(logDrainQuotaDegradation, "logDrainQuotaDegradation");
2984
+ __name2(logDrainQuotaDegradation, "logDrainQuotaDegradation");
2985
+ __name(logDrainQuotaWarned, "logDrainQuotaWarned");
2986
+ __name2(logDrainQuotaWarned, "logDrainQuotaWarned");
2987
+ __name(logDrainQuotaDroppedSeverities, "logDrainQuotaDroppedSeverities");
2988
+ __name2(logDrainQuotaDroppedSeverities, "logDrainQuotaDroppedSeverities");
2989
+ LOG_CURSOR_MAX_LENGTH = 512;
2990
+ LOG_CURSOR_MAX_ID_LENGTH = 128;
2991
+ LOG_RELATIVE_TIME_PATTERN = /^(\d{1,9})([smhd])$/;
2992
+ LOG_ISO_TIME_PATTERN = /^\d{4}-\d{2}-\d{2}([Tt ]\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?([Zz]|[+-]\d{2}:?\d{2})?)?$/;
2993
+ RELATIVE_UNIT_MS = {
2994
+ s: 1e3,
2995
+ m: 6e4,
2996
+ h: 36e5,
2997
+ d: 864e5
2998
+ };
2999
+ __name(encodeLogCursor, "encodeLogCursor");
3000
+ __name2(encodeLogCursor, "encodeLogCursor");
3001
+ __name(decodeLogCursor, "decodeLogCursor");
3002
+ __name2(decodeLogCursor, "decodeLogCursor");
3003
+ __name(parseLogTimeBound, "parseLogTimeBound");
3004
+ __name2(parseLogTimeBound, "parseLogTimeBound");
2760
3005
  CORE_DRAINING_CODE = "CORE_DRAINING";
2761
3006
  CORE_DRAINING_DEFAULT_RETRY_MS = 1e3;
2762
3007
  CORE_DRAINING_MAX_RETRY_MS = 5e3;
@@ -4792,10 +5037,10 @@ function parseModelTemplate(model) {
4792
5037
  if (!looksLikeModelTemplate(model)) return {
4793
5038
  kind: "static"
4794
5039
  };
4795
- const trimmed = model.trim();
4796
- const m = WORKFLOW_MODEL_TEMPLATE_RE.exec(trimmed);
5040
+ const trimmed2 = model.trim();
5041
+ const m = WORKFLOW_MODEL_TEMPLATE_RE.exec(trimmed2);
4797
5042
  if (!m) {
4798
- const inner = /^\$\{([^}]*)\}$/.exec(trimmed)?.[1];
5043
+ const inner = /^\$\{([^}]*)\}$/.exec(trimmed2)?.[1];
4799
5044
  const root = inner?.split(/[.|]/, 1)[0];
4800
5045
  const why = inner !== void 0 && root !== void 0 && root !== WORKFLOW_MODEL_TEMPLATE_ROOT ? `only the \`${WORKFLOW_MODEL_TEMPLATE_ROOT}\` root is allowed (got \`${root || "(empty)"}\`)` : inner !== void 0 && (inner === WORKFLOW_MODEL_TEMPLATE_ROOT || inner.startsWith(`${WORKFLOW_MODEL_TEMPLATE_ROOT}.`)) ? "the path after `initData.` is empty" : "the whole value must be exactly one placeholder \u2014 no prefix, suffix or second placeholder";
4801
5046
  return {
@@ -4825,7 +5070,7 @@ function parseModelTemplate(model) {
4825
5070
  return {
4826
5071
  kind: "template",
4827
5072
  template: {
4828
- placeholder: trimmed,
5073
+ placeholder: trimmed2,
4829
5074
  path: path26,
4830
5075
  default: dflt
4831
5076
  }
@@ -4834,7 +5079,7 @@ function parseModelTemplate(model) {
4834
5079
  return {
4835
5080
  kind: "template",
4836
5081
  template: {
4837
- placeholder: trimmed,
5082
+ placeholder: trimmed2,
4838
5083
  path: path26
4839
5084
  }
4840
5085
  };
@@ -5487,9 +5732,9 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
5487
5732
  const registry = opts.approvedModels;
5488
5733
  if (registry === void 0) return;
5489
5734
  if (registry === "unavailable") {
5490
- const trimmed = pin.trim();
5491
- if (trimmed && !normalizeModelId(trimmed, []).ok) {
5492
- warn("model-unresolved", inContext(`model "${trimmed}" could not be checked against the approved-model registry (unavailable at push) \u2014 it dispatches only if it resolves there (a provider/model registry code, or a bare id exactly one approved model carries)`), `${path26}.model`, id);
5735
+ const trimmed2 = pin.trim();
5736
+ if (trimmed2 && !normalizeModelId(trimmed2, []).ok) {
5737
+ warn("model-unresolved", inContext(`model "${trimmed2}" could not be checked against the approved-model registry (unavailable at push) \u2014 it dispatches only if it resolves there (a provider/model registry code, or a bare id exactly one approved model carries)`), `${path26}.model`, id);
5493
5738
  }
5494
5739
  return;
5495
5740
  }
@@ -12454,8 +12699,8 @@ function ensureGitignored(rootDir, entry, afterEntry) {
12454
12699
  return;
12455
12700
  }
12456
12701
  }
12457
- const trimmed = content.endsWith("\n") ? content : content + "\n";
12458
- fs2.writeFileSync(gitignorePath, trimmed + entry + "\n");
12702
+ const trimmed2 = content.endsWith("\n") ? content : content + "\n";
12703
+ fs2.writeFileSync(gitignorePath, trimmed2 + entry + "\n");
12459
12704
  }
12460
12705
  function hasEnvFile() {
12461
12706
  return fs2.existsSync(path2.join(process.cwd(), ".env"));
@@ -27854,6 +28099,7 @@ var init_mcp_server_handler = __esm({
27854
28099
 
27855
28100
  // src/index.ts
27856
28101
  import { Command } from "commander";
28102
+ import chalk5 from "chalk";
27857
28103
 
27858
28104
  // src/cli/parse-agent-version.ts
27859
28105
  import { InvalidArgumentError } from "commander";
@@ -29736,8 +29982,8 @@ var PostProcessorApi = class extends HttpClient {
29736
29982
  // src/utils/sync-fetch.ts
29737
29983
  init_developer_api_service();
29738
29984
  init_mcp_server_handler();
29739
- function normalizeForComparison(str) {
29740
- return str.trim().replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/\\\\/g, "\\").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
29985
+ function normalizeForComparison(str2) {
29986
+ return str2.trim().replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/\\\\/g, "\\").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
29741
29987
  }
29742
29988
  __name(normalizeForComparison, "normalizeForComparison");
29743
29989
  function canonicalizeForm(value3) {
@@ -31582,12 +31828,30 @@ __name(buildLogsCommand, "buildLogsCommand");
31582
31828
  // src/utils/sync-display.ts
31583
31829
  init_dist();
31584
31830
  import { diffLines } from "diff";
31831
+
31832
+ // src/utils/color.ts
31833
+ function noColorEnabled(env = process.env) {
31834
+ return !!env.NO_COLOR;
31835
+ }
31836
+ __name(noColorEnabled, "noColorEnabled");
31837
+ function colorize(ansiCode, text) {
31838
+ if (noColorEnabled()) return text;
31839
+ return `${ansiCode}${text}\x1B[0m`;
31840
+ }
31841
+ __name(colorize, "colorize");
31842
+ function configureChalkColor(chalkLike) {
31843
+ if (noColorEnabled()) {
31844
+ chalkLike.level = 0;
31845
+ }
31846
+ }
31847
+ __name(configureChalkColor, "configureChalkColor");
31848
+
31849
+ // src/utils/sync-display.ts
31585
31850
  var colors = {
31586
- red: "\x1B[31m",
31587
- green: "\x1B[32m",
31588
- gray: "\x1B[90m",
31589
- reset: "\x1B[0m",
31590
- bold: "\x1B[1m"
31851
+ red: /* @__PURE__ */ __name((s) => colorize("\x1B[31m", s), "red"),
31852
+ green: /* @__PURE__ */ __name((s) => colorize("\x1B[32m", s), "green"),
31853
+ gray: /* @__PURE__ */ __name((s) => colorize("\x1B[90m", s), "gray"),
31854
+ bold: /* @__PURE__ */ __name((s) => colorize("\x1B[1m", s), "bold")
31591
31855
  };
31592
31856
  var primitiveTypeLabels = {
31593
31857
  skill: "SKILLS",
@@ -31599,31 +31863,31 @@ var primitiveTypeLabels = {
31599
31863
  };
31600
31864
  function showColoredDiff(oldText, newText, oldLabel = "Server", newLabel = "Local") {
31601
31865
  if (!oldText && !newText) {
31602
- console.log(colors.gray + "(both empty)" + colors.reset);
31866
+ console.log(colors.gray("(both empty)"));
31603
31867
  return;
31604
31868
  }
31605
31869
  if (!oldText) {
31606
- console.log(colors.green + `+ (${newLabel} only - no ${oldLabel} content)` + colors.reset);
31870
+ console.log(colors.green(`+ (${newLabel} only - no ${oldLabel} content)`));
31607
31871
  return;
31608
31872
  }
31609
31873
  if (!newText) {
31610
- console.log(colors.red + `- (${oldLabel} only - no ${newLabel} content)` + colors.reset);
31874
+ console.log(colors.red(`- (${oldLabel} only - no ${newLabel} content)`));
31611
31875
  return;
31612
31876
  }
31613
31877
  const diff = diffLines(oldText, newText);
31614
31878
  diff.forEach((part) => {
31615
31879
  if (part.added) {
31616
- part.value.split("\n").filter((l) => l).forEach((line) => console.log(colors.green + "+ " + line + colors.reset));
31880
+ part.value.split("\n").filter((l) => l).forEach((line) => console.log(colors.green("+ " + line)));
31617
31881
  } else if (part.removed) {
31618
- part.value.split("\n").filter((l) => l).forEach((line) => console.log(colors.red + "- " + line + colors.reset));
31882
+ part.value.split("\n").filter((l) => l).forEach((line) => console.log(colors.red("- " + line)));
31619
31883
  } else {
31620
31884
  const lines = part.value.split("\n").filter((l) => l);
31621
31885
  if (lines.length <= 6) {
31622
- lines.forEach((line) => console.log(colors.gray + " " + line + colors.reset));
31886
+ lines.forEach((line) => console.log(colors.gray(" " + line)));
31623
31887
  } else {
31624
- lines.slice(0, 3).forEach((line) => console.log(colors.gray + " " + line + colors.reset));
31625
- console.log(colors.gray + ` ... (${lines.length - 6} unchanged lines) ...` + colors.reset);
31626
- lines.slice(-3).forEach((line) => console.log(colors.gray + " " + line + colors.reset));
31888
+ lines.slice(0, 3).forEach((line) => console.log(colors.gray(" " + line)));
31889
+ console.log(colors.gray(` ... (${lines.length - 6} unchanged lines) ...`));
31890
+ lines.slice(-3).forEach((line) => console.log(colors.gray(" " + line)));
31627
31891
  }
31628
31892
  }
31629
31893
  });
@@ -31631,8 +31895,8 @@ function showColoredDiff(oldText, newText, oldLabel = "Server", newLabel = "Loca
31631
31895
  __name(showColoredDiff, "showColoredDiff");
31632
31896
  function showPersonaDiff(serverPersona, localPersona) {
31633
31897
  console.log("\n" + "=".repeat(60));
31634
- console.log(colors.bold + "PERSONA DIFF" + colors.reset);
31635
- console.log(colors.red + "- Server (deployed)" + colors.reset + " " + colors.green + "+ Local (code)" + colors.reset);
31898
+ console.log(colors.bold("PERSONA DIFF"));
31899
+ console.log(colors.red("- Server (deployed)") + " " + colors.green("+ Local (code)"));
31636
31900
  console.log("=".repeat(60) + "\n");
31637
31901
  showColoredDiff(flattenPersonaTextAll(serverPersona), flattenPersonaTextAll(localPersona), "Server", "Local");
31638
31902
  console.log("\n" + "=".repeat(60) + "\n");
@@ -31640,19 +31904,19 @@ function showPersonaDiff(serverPersona, localPersona) {
31640
31904
  __name(showPersonaDiff, "showPersonaDiff");
31641
31905
  function showNameDiff(serverName, localName) {
31642
31906
  console.log("\n" + "=".repeat(60));
31643
- console.log(colors.bold + "NAME DIFF" + colors.reset);
31907
+ console.log(colors.bold("NAME DIFF"));
31644
31908
  console.log("=".repeat(60));
31645
- console.log(colors.red + `- Server: "${serverName || "(none)"}"` + colors.reset);
31646
- console.log(colors.green + `+ Local: "${localName || "(none)"}"` + colors.reset);
31909
+ console.log(colors.red(`- Server: "${serverName || "(none)"}"`));
31910
+ console.log(colors.green(`+ Local: "${localName || "(none)"}"`));
31647
31911
  console.log("=".repeat(60) + "\n");
31648
31912
  }
31649
31913
  __name(showNameDiff, "showNameDiff");
31650
31914
  function showModelDiff(serverModel, localModel) {
31651
31915
  console.log("\n" + "=".repeat(60));
31652
- console.log(colors.bold + "MODEL DIFF" + colors.reset);
31916
+ console.log(colors.bold("MODEL DIFF"));
31653
31917
  console.log("=".repeat(60));
31654
- console.log(colors.red + `- Server: "${serverModel || "(none)"}"` + colors.reset);
31655
- console.log(colors.green + `+ Local: "${localModel || "(none)"}"` + colors.reset);
31918
+ console.log(colors.red(`- Server: "${serverModel || "(none)"}"`));
31919
+ console.log(colors.green(`+ Local: "${localModel || "(none)"}"`));
31656
31920
  console.log("=".repeat(60) + "\n");
31657
31921
  }
31658
31922
  __name(showModelDiff, "showModelDiff");
@@ -31671,17 +31935,17 @@ function showPrimitiveDrift(items, backupAvailable) {
31671
31935
  }
31672
31936
  for (const [type, groupItems] of grouped) {
31673
31937
  const label2 = primitiveTypeLabels[type] ?? type.toUpperCase();
31674
- console.log("\n" + colors.bold + `\u2501\u2501\u2501 ${label2} \u2501\u2501\u2501` + colors.reset);
31938
+ console.log("\n" + colors.bold(`\u2501\u2501\u2501 ${label2} \u2501\u2501\u2501`));
31675
31939
  for (const item of groupItems) {
31676
- console.log("\n " + colors.bold + item.name + colors.reset);
31677
- const localDisplay = item.localVersion ? `v${item.localVersion}` : colors.gray + "(not found)" + colors.reset;
31940
+ console.log("\n " + colors.bold(item.name));
31941
+ const localDisplay = item.localVersion ? `v${item.localVersion}` : colors.gray("(not found)");
31678
31942
  console.log(` Local: ${localDisplay}`);
31679
- const serverDisplay = item.serverVersion ? `v${item.serverVersion}` : colors.gray + "(not found)" + colors.reset;
31943
+ const serverDisplay = item.serverVersion ? `v${item.serverVersion}` : colors.gray("(not found)");
31680
31944
  console.log(` Server: ${serverDisplay}`);
31681
31945
  }
31682
31946
  }
31683
31947
  if (backupAvailable !== void 0) {
31684
- const backupDisplay = backupAvailable ? colors.green + "available" + colors.reset : colors.red + "not available" + colors.reset;
31948
+ const backupDisplay = backupAvailable ? colors.green("available") : colors.red("not available");
31685
31949
  console.log(`
31686
31950
  Backup: ${backupDisplay}`);
31687
31951
  }
@@ -31703,14 +31967,14 @@ function showPrimitiveDriftSummary(items) {
31703
31967
  versionInfo = `local only${vSuffix(item.localVersion)}`;
31704
31968
  }
31705
31969
  const paddedLabel = label2.padEnd(16);
31706
- console.log(` ${colors.red}${paddedLabel}${colors.reset} ${item.name} (${versionInfo})`);
31970
+ console.log(` ${colors.red(paddedLabel)} ${item.name} (${versionInfo})`);
31707
31971
  }
31708
31972
  }
31709
31973
  __name(showPrimitiveDriftSummary, "showPrimitiveDriftSummary");
31710
31974
  function showGovernanceDiff(serverGovernance, localGovernance) {
31711
31975
  console.log("\n" + "=".repeat(60));
31712
- console.log(colors.bold + "GOVERNANCE DIFF" + colors.reset);
31713
- console.log(colors.red + "- Server (deployed)" + colors.reset + " " + colors.green + "+ Local (code)" + colors.reset);
31976
+ console.log(colors.bold("GOVERNANCE DIFF"));
31977
+ console.log(colors.red("- Server (deployed)") + " " + colors.green("+ Local (code)"));
31714
31978
  console.log("=".repeat(60) + "\n");
31715
31979
  showColoredDiff(JSON.stringify(serverGovernance, null, 2) || "(none)", JSON.stringify(localGovernance, null, 2) || "(none)", "Server", "Local");
31716
31980
  console.log("\n" + "=".repeat(60) + "\n");
@@ -37008,6 +37272,9 @@ init_artifact_loader();
37008
37272
  init_types();
37009
37273
  init_analytics();
37010
37274
 
37275
+ // src/utils/aliases.ts
37276
+ init_dist();
37277
+
37011
37278
  // src/utils/suggest.ts
37012
37279
  function levenshtein(a, b) {
37013
37280
  const m = a.length, n2 = b.length;
@@ -37140,21 +37407,58 @@ var ALIAS_MAP = {
37140
37407
  everything: "all"
37141
37408
  })
37142
37409
  },
37410
+ // PRO-1798 (log drains) — `lua drains <action>`; the canonical list is §B.14's verb
37411
+ // table. `rotate-secret` keeps its hyphen: it is the route name too.
37412
+ "drains.action": {
37413
+ canonical: [
37414
+ "list",
37415
+ "status",
37416
+ "deliveries",
37417
+ "create",
37418
+ "update",
37419
+ "delete",
37420
+ "test",
37421
+ "verify",
37422
+ "pause",
37423
+ "resume",
37424
+ "rotate-secret"
37425
+ ],
37426
+ aliases: lowerKeys({
37427
+ ls: "list",
37428
+ view: "list",
37429
+ show: "status",
37430
+ info: "status",
37431
+ health: "status",
37432
+ history: "deliveries",
37433
+ attempts: "deliveries",
37434
+ deliver: "deliveries",
37435
+ new: "create",
37436
+ add: "create",
37437
+ edit: "update",
37438
+ patch: "update",
37439
+ rm: "delete",
37440
+ remove: "delete",
37441
+ del: "delete",
37442
+ send: "test",
37443
+ check: "verify",
37444
+ off: "pause",
37445
+ disable: "pause",
37446
+ on: "resume",
37447
+ enable: "resume",
37448
+ rotate: "rotate-secret",
37449
+ rotatesecret: "rotate-secret",
37450
+ "rotate-key": "rotate-secret"
37451
+ })
37452
+ },
37143
37453
  "logs.type": {
37454
+ // PRO-1838 (A5) / design D-17: DERIVED from the canonical constant, never hand-listed.
37455
+ // `logs.ts` already derives its own enum from `AGENT_LOG_SOURCES`, but THIS list is what
37456
+ // actually rejects `--type` — hand-maintaining it left the five platform sources
37457
+ // (trigger, model-resolver, workflow, workflow-step, workflow-script) unreachable.
37458
+ // `getCanonicalValues` sorts, so the order here is irrelevant to the `Valid values` hint.
37459
+ // `all` and `calls` are handled by the caller before the check and are not members.
37144
37460
  canonical: [
37145
- "skill",
37146
- "job",
37147
- "webhook",
37148
- "preprocessor",
37149
- "postprocessor",
37150
- "user_message",
37151
- "agent_response",
37152
- "agent_error",
37153
- "mcp",
37154
- "runtime",
37155
- "rag",
37156
- "device",
37157
- "device-trigger"
37461
+ ...AGENT_LOG_SOURCES
37158
37462
  ],
37159
37463
  aliases: lowerKeys({
37160
37464
  skills: "skill",
@@ -40353,8 +40657,8 @@ async function gitUserConfigured(cwd) {
40353
40657
  cwd
40354
40658
  });
40355
40659
  if (code !== 0) return void 0;
40356
- const trimmed = stdout.trim();
40357
- return trimmed.length > 0 ? trimmed : void 0;
40660
+ const trimmed2 = stdout.trim();
40661
+ return trimmed2.length > 0 ? trimmed2 : void 0;
40358
40662
  } catch {
40359
40663
  return void 0;
40360
40664
  }
@@ -40379,8 +40683,8 @@ async function getRemoteOriginUrl(cwd) {
40379
40683
  cwd
40380
40684
  });
40381
40685
  if (code !== 0) return null;
40382
- const trimmed = stdout.trim();
40383
- return trimmed.length > 0 ? trimmed : null;
40686
+ const trimmed2 = stdout.trim();
40687
+ return trimmed2.length > 0 ? trimmed2 : null;
40384
40688
  } catch {
40385
40689
  return null;
40386
40690
  }
@@ -40751,9 +41055,9 @@ async function readCurrentBranch(cwd) {
40751
41055
  cwd
40752
41056
  });
40753
41057
  if (res.code !== 0) return null;
40754
- const trimmed = res.stdout.trim();
40755
- if (!trimmed || trimmed === "HEAD") return null;
40756
- return trimmed;
41058
+ const trimmed2 = res.stdout.trim();
41059
+ if (!trimmed2 || trimmed2 === "HEAD") return null;
41060
+ return trimmed2;
40757
41061
  }
40758
41062
  __name(readCurrentBranch, "readCurrentBranch");
40759
41063
 
@@ -40871,8 +41175,8 @@ __name(reportFailure, "reportFailure");
40871
41175
  var GIT_MESSAGES = {
40872
41176
  pushStaged: "lua: push staged code",
40873
41177
  createVersion: /* @__PURE__ */ __name((version, message) => {
40874
- const trimmed = message?.trim();
40875
- return trimmed ? `lua: create version v${version}: ${trimmed}` : `lua: create version v${version}`;
41178
+ const trimmed2 = message?.trim();
41179
+ return trimmed2 ? `lua: create version v${version}: ${trimmed2}` : `lua: create version v${version}`;
40876
41180
  }, "createVersion"),
40877
41181
  promote: /* @__PURE__ */ __name((version) => `lua: promote v${version} to production`, "promote"),
40878
41182
  delete: /* @__PURE__ */ __name((version) => `lua: delete version v${version}`, "delete"),
@@ -43489,17 +43793,22 @@ var LogsApi = class extends HttpClient {
43489
43793
  }
43490
43794
  /**
43491
43795
  * Retrieves logs for an agent
43796
+ *
43797
+ * The signature is unchanged from before A4 — `filters` simply gained four optional
43798
+ * keys. When `filters.cursor` is set, `page` is NOT sent: the route refuses the two
43799
+ * together (docs `reference/rest/log-drains.mdx`, "Cannot be combined with `page`").
43800
+ *
43492
43801
  * @param agentId - The ID of the agent
43493
43802
  * @param limit - Number of logs per page (default: 20)
43494
- * @param page - Page number (default: 1)
43803
+ * @param page - Page number (default: 1); ignored when `filters.cursor` is set
43495
43804
  * @param filters - Optional filters to narrow log results
43496
- * @returns Promise resolving to an ApiResponse containing logs and pagination
43805
+ * @returns Promise resolving to an ApiResponse containing logs, nextCursor and pagination
43497
43806
  */
43498
43807
  async getAgentLogs(agentId, limit, page, filters = {}) {
43499
43808
  const queryParams = new URLSearchParams({
43500
- limit: limit.toString(),
43501
- page: page.toString()
43809
+ limit: limit.toString()
43502
43810
  });
43811
+ if (!filters.cursor) queryParams.append("page", page.toString());
43503
43812
  if (filters.logSource) queryParams.append("logSource", filters.logSource);
43504
43813
  if (filters.primitiveId) queryParams.append("primitiveId", filters.primitiveId);
43505
43814
  if (filters.primitiveName) queryParams.append("primitiveName", filters.primitiveName);
@@ -43507,6 +43816,10 @@ var LogsApi = class extends HttpClient {
43507
43816
  if (filters.toolName) queryParams.append("toolName", filters.toolName);
43508
43817
  if (filters.logType) queryParams.append("logType", filters.logType);
43509
43818
  if (filters.userId) queryParams.append("userId", filters.userId);
43819
+ if (filters.since) queryParams.append("since", filters.since);
43820
+ if (filters.until) queryParams.append("until", filters.until);
43821
+ if (filters.environment) queryParams.append("environment", filters.environment);
43822
+ if (filters.cursor) queryParams.append("cursor", filters.cursor);
43510
43823
  return this.httpGet(`/developer/agents/${agentId}/logs?${queryParams.toString()}`, {});
43511
43824
  }
43512
43825
  };
@@ -43807,14 +44120,14 @@ async function startChatLoop(chatEnv, probeWindow) {
43807
44120
  });
43808
44121
  rl.prompt();
43809
44122
  rl.on("line", async (input) => {
43810
- const trimmed = input.trim();
43811
- if (!trimmed) {
44123
+ const trimmed2 = input.trim();
44124
+ if (!trimmed2) {
43812
44125
  rl.prompt();
43813
44126
  return;
43814
44127
  }
43815
44128
  let messages;
43816
44129
  try {
43817
- messages = await parseMessageWithAttachments(trimmed);
44130
+ messages = await parseMessageWithAttachments(trimmed2);
43818
44131
  } catch (error) {
43819
44132
  console.error(`
43820
44133
  \u274C ${error instanceof Error ? error.message : "Failed to process attachments"}
@@ -46632,8 +46945,8 @@ async function promptAreaCode() {
46632
46945
  default: ""
46633
46946
  }
46634
46947
  ]);
46635
- const trimmed = pattern.trim();
46636
- return trimmed.length > 0 ? trimmed : void 0;
46948
+ const trimmed2 = pattern.trim();
46949
+ return trimmed2.length > 0 ? trimmed2 : void 0;
46637
46950
  }
46638
46951
  __name(promptAreaCode, "promptAreaCode");
46639
46952
  function smsLabel(transportOrUndefined) {
@@ -47578,6 +47891,7 @@ var CallMetricsApi = class extends HttpClient {
47578
47891
 
47579
47892
  // src/commands/logs.ts
47580
47893
  init_cli_error();
47894
+ init_dist();
47581
47895
  var ALL_VALID_SOURCES = AGENT_LOG_SOURCES;
47582
47896
  var ENTITY_SOURCES = /* @__PURE__ */ new Set([
47583
47897
  "skill",
@@ -47588,19 +47902,35 @@ var ENTITY_SOURCES = /* @__PURE__ */ new Set([
47588
47902
  "device"
47589
47903
  ]);
47590
47904
  var NON_ENTITY_SOURCES = new Set(ALL_VALID_SOURCES.filter((s) => !ENTITY_SOURCES.has(s)));
47905
+ var FOLLOW_POLL_MS = 2e3;
47906
+ var FOLLOW_POLL_JITTER_MS = 500;
47907
+ var FOLLOW_BACKOFF_MIN_MS = 1e3;
47908
+ var FOLLOW_BACKOFF_MAX_MS = 3e4;
47909
+ var FOLLOW_PAGE_CAP = 10;
47910
+ var RELATIVE_WINDOW = LOG_RELATIVE_TIME_PATTERN;
47911
+ var ISO_TIME_SHAPE = LOG_ISO_TIME_PATTERN;
47591
47912
  async function logsCommand(cmdObj) {
47592
47913
  return withErrorHandling(async () => {
47914
+ const pageGiven = cmdObj?.page !== void 0 && cmdObj?.page !== null && cmdObj?.page !== "";
47593
47915
  const options = {
47594
47916
  type: cmdObj?.type || null,
47595
47917
  name: cmdObj?.name || null,
47596
47918
  limit: cmdObj?.limit ? parseInt(cmdObj.limit, 10) : 20,
47597
- page: cmdObj?.page ? parseInt(cmdObj.page, 10) : 1,
47919
+ page: pageGiven ? parseInt(cmdObj.page, 10) : 1,
47920
+ pageGiven,
47598
47921
  json: cmdObj?.json || false,
47599
47922
  userId: cmdObj?.userId || null,
47600
47923
  agentId: cmdObj?.agentId || null,
47601
47924
  direction: cmdObj?.direction || null,
47602
- status: cmdObj?.status || null
47925
+ status: cmdObj?.status || null,
47926
+ since: cmdObj?.since ? parseWindowBound("--since", String(cmdObj.since)) : void 0,
47927
+ until: cmdObj?.until ? parseWindowBound("--until", String(cmdObj.until)) : void 0,
47928
+ follow: cmdObj?.follow || false,
47929
+ environment: cmdObj?.environment ? parseEnvironment(String(cmdObj.environment)) : void 0
47603
47930
  };
47931
+ if (options.follow && options.pageGiven) {
47932
+ throw CliError.usage("lua: --follow cannot be combined with --page.", "Drop --page: --follow starts from --since (or the newest page) and keeps going.");
47933
+ }
47604
47934
  const { agentId: configAgentId, apiKey } = await initializeCommand();
47605
47935
  const effectiveAgentId = options.agentId || configAgentId;
47606
47936
  if (options.type === "calls") {
@@ -47615,7 +47945,18 @@ async function logsCommand(cmdObj) {
47615
47945
  return;
47616
47946
  }
47617
47947
  const logsApi = new LogsApi(BASE_URLS.API, apiKey);
47618
- const hasNonInteractiveFlags = options.type || options.name || options.json || options.userId;
47948
+ const hasNonInteractiveFlags = Boolean(options.type || options.name || options.json || options.userId || options.since || options.until || options.environment || options.follow);
47949
+ trackEvent("cli_logs_action", {
47950
+ filter_type: options.type || null,
47951
+ json_output: options.json || false,
47952
+ non_interactive: hasNonInteractiveFlags,
47953
+ page_requested: options.page || 1,
47954
+ agent_id_override: !!options.agentId,
47955
+ follow: !!options.follow,
47956
+ has_since: !!options.since,
47957
+ has_until: !!options.until,
47958
+ environment: options.environment || null
47959
+ });
47619
47960
  if (hasNonInteractiveFlags) {
47620
47961
  await nonInteractiveLogs(logsApi, effectiveAgentId, apiKey, options);
47621
47962
  } else {
@@ -47623,13 +47964,6 @@ async function logsCommand(cmdObj) {
47623
47964
  `);
47624
47965
  await interactiveLogs(logsApi, effectiveAgentId, apiKey);
47625
47966
  }
47626
- trackEvent("cli_logs_action", {
47627
- filter_type: options.type || null,
47628
- json_output: options.json || false,
47629
- non_interactive: hasNonInteractiveFlags,
47630
- page_requested: options.page || 1,
47631
- agent_id_override: !!options.agentId
47632
- });
47633
47967
  }, "logs viewing");
47634
47968
  }
47635
47969
  __name(logsCommand, "logsCommand");
@@ -47681,7 +48015,7 @@ function renderLogEntry(log, isLast) {
47681
48015
  const logSource = log.metadata?.logSource;
47682
48016
  const primitiveId = log.metadata?.primitiveId;
47683
48017
  const primitiveName = log.metadata?.primitiveName;
47684
- const { toolName, channel, userId } = log.metadata;
48018
+ const { toolName, channel, userId, environment, agentVersion } = log.metadata;
47685
48019
  const icon = getLogIcon(log.subType, logSource);
47686
48020
  const color = getLogColor(log.subType, logSource);
47687
48021
  console.log(color(`${icon} [${timestamp}] ${log.subType.toUpperCase()}`));
@@ -47704,6 +48038,12 @@ function renderLogEntry(log, isLast) {
47704
48038
  console.log(chalk2.gray(` Tool Name: ${toolName}`));
47705
48039
  }
47706
48040
  }
48041
+ if (environment) {
48042
+ console.log(chalk2.gray(` Env: ${environment}`));
48043
+ }
48044
+ if (agentVersion) {
48045
+ console.log(chalk2.gray(` Version: ${agentVersion}`));
48046
+ }
47707
48047
  if (log.duration !== void 0) {
47708
48048
  console.log(chalk2.gray(` Duration: ${log.duration}ms`));
47709
48049
  }
@@ -47713,6 +48053,13 @@ function renderLogEntry(log, isLast) {
47713
48053
  }
47714
48054
  }
47715
48055
  __name(renderLogEntry, "renderLogEntry");
48056
+ function formatPageHeader(pagination) {
48057
+ if (pagination?.totalCountExact === false) {
48058
+ return `Page ${pagination.currentPage} (${pagination.totalCount}+ total logs \u2014 count capped; page with --json nextCursor)`;
48059
+ }
48060
+ return `Page ${pagination.currentPage} of ${pagination.totalPages} (${pagination.totalCount} total logs)`;
48061
+ }
48062
+ __name(formatPageHeader, "formatPageHeader");
47716
48063
  function displayLogsCore(logs, pagination, title, clearScreen = false) {
47717
48064
  if (clearScreen) {
47718
48065
  console.clear();
@@ -47721,7 +48068,7 @@ function displayLogsCore(logs, pagination, title, clearScreen = false) {
47721
48068
  ${title}
47722
48069
  `);
47723
48070
  writeInfo(`${"\u2500".repeat(80)}`);
47724
- writeInfo(`Page ${pagination.currentPage} of ${pagination.totalPages} (${pagination.totalCount} total logs)
48071
+ writeInfo(`${formatPageHeader(pagination)}
47725
48072
  `);
47726
48073
  if (logs.length === 0) {
47727
48074
  writeInfo("No logs found.");
@@ -47777,6 +48124,149 @@ async function resolveEntityId(apiKey, agentId, type, nameOrId) {
47777
48124
  return found?.id || null;
47778
48125
  }
47779
48126
  __name(resolveEntityId, "resolveEntityId");
48127
+ function parseWindowBound(flag, raw) {
48128
+ const value3 = raw.trim();
48129
+ if (RELATIVE_WINDOW.test(value3)) return value3;
48130
+ if (ISO_TIME_SHAPE.test(value3)) {
48131
+ const parsed = Date.parse(value3);
48132
+ if (Number.isFinite(parsed)) return new Date(parsed).toISOString();
48133
+ }
48134
+ throw CliError.usage(`lua: ${flag} "${raw}" is not a time.`, `Use an ISO 8601 instant (2026-09-21T09:00:00Z) or a relative window: 15m, 2h, 7d.`);
48135
+ }
48136
+ __name(parseWindowBound, "parseWindowBound");
48137
+ function parseEnvironment(raw) {
48138
+ const value3 = raw.trim().toLowerCase();
48139
+ const valid = [
48140
+ ...AGENT_LOG_ENVIRONMENTS
48141
+ ].sort();
48142
+ if (valid.includes(value3)) return value3;
48143
+ throw CliError.usage(`lua: "${raw}" is not a valid --environment value.`, `Valid values: ${valid.join(", ")}`);
48144
+ }
48145
+ __name(parseEnvironment, "parseEnvironment");
48146
+ function followDelayMs() {
48147
+ const raw = Number(process.env.LUA_LOGS_FOLLOW_INTERVAL_MS);
48148
+ const base = Number.isFinite(raw) && raw >= 0 && raw <= 6e4 ? raw : FOLLOW_POLL_MS;
48149
+ const jitter = Math.min(FOLLOW_POLL_JITTER_MS, Math.floor(base / 4));
48150
+ return base + (jitter > 0 ? Math.floor(Math.random() * jitter) : 0);
48151
+ }
48152
+ __name(followDelayMs, "followDelayMs");
48153
+ function sleep2(ms, signal) {
48154
+ if (signal.aborted) return Promise.resolve();
48155
+ return new Promise((resolve7) => {
48156
+ const done = /* @__PURE__ */ __name(() => {
48157
+ clearTimeout(timer);
48158
+ signal.removeEventListener("abort", done);
48159
+ resolve7();
48160
+ }, "done");
48161
+ const timer = setTimeout(done, ms);
48162
+ signal.addEventListener("abort", done, {
48163
+ once: true
48164
+ });
48165
+ });
48166
+ }
48167
+ __name(sleep2, "sleep");
48168
+ function rowKey(row2) {
48169
+ return row2.id || row2._id;
48170
+ }
48171
+ __name(rowKey, "rowKey");
48172
+ async function sweepWindow(logsApi, agentId, limit, filters, pageCap) {
48173
+ const rows3 = [];
48174
+ let cursor = filters.cursor;
48175
+ let pagination;
48176
+ let pages = 0;
48177
+ for (; ; ) {
48178
+ const response = await logsApi.getAgentLogs(agentId, limit, 1, {
48179
+ ...filters,
48180
+ cursor
48181
+ });
48182
+ if (!response.success) {
48183
+ throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Unknown error", void 0, apiErrorDetail(response.error));
48184
+ }
48185
+ const data = response.data;
48186
+ rows3.push(...data.logs ?? []);
48187
+ pagination = data.pagination;
48188
+ pages += 1;
48189
+ const next = data.nextCursor ?? null;
48190
+ if (!next || pages >= pageCap) {
48191
+ return {
48192
+ rows: rows3,
48193
+ nextCursor: next,
48194
+ pagination,
48195
+ capped: Boolean(next) && pages >= pageCap
48196
+ };
48197
+ }
48198
+ cursor = next;
48199
+ }
48200
+ }
48201
+ __name(sweepWindow, "sweepWindow");
48202
+ async function followLogs(logsApi, agentId, options, baseFilters) {
48203
+ const limit = options.limit || 20;
48204
+ const controller = new AbortController();
48205
+ const onSigint = /* @__PURE__ */ __name(() => controller.abort(), "onSigint");
48206
+ process.once("SIGINT", onSigint);
48207
+ const untilMs = options.until ? RELATIVE_WINDOW.test(options.until) ? 0 : Date.parse(options.until) : void 0;
48208
+ let boundarySeen = /* @__PURE__ */ new Set();
48209
+ let since = baseFilters.since;
48210
+ let backoff = 0;
48211
+ try {
48212
+ while (!controller.signal.aborted) {
48213
+ let sweep;
48214
+ try {
48215
+ const pageCap = since ? FOLLOW_PAGE_CAP : 1;
48216
+ sweep = await sweepWindow(logsApi, agentId, limit, {
48217
+ ...baseFilters,
48218
+ since,
48219
+ cursor: void 0
48220
+ }, pageCap);
48221
+ } catch (error) {
48222
+ if (controller.signal.aborted) break;
48223
+ if (error instanceof CliError && error.exitCode === CLI_EXIT.UNAVAILABLE) {
48224
+ backoff = Math.min(FOLLOW_BACKOFF_MAX_MS, backoff ? backoff * 2 : FOLLOW_BACKOFF_MIN_MS);
48225
+ await sleep2(backoff, controller.signal);
48226
+ continue;
48227
+ }
48228
+ throw error;
48229
+ }
48230
+ backoff = 0;
48231
+ const fresh = sweep.rows.filter((row2) => !boundarySeen.has(rowKey(row2)));
48232
+ if (fresh.length > 0) {
48233
+ if (options.json) {
48234
+ console.log(JSON.stringify({
48235
+ logs: fresh,
48236
+ nextCursor: sweep.nextCursor ?? null,
48237
+ pagination: sweep.pagination ?? null
48238
+ }, null, 2));
48239
+ } else {
48240
+ const ordered = [
48241
+ ...fresh
48242
+ ].reverse();
48243
+ ordered.forEach((row2, index) => renderLogEntry(row2, index === ordered.length - 1));
48244
+ }
48245
+ if (sweep.capped && !options.json) {
48246
+ writeInfo(` \u2026 more rows remain in this window (stopped after ${FOLLOW_PAGE_CAP} pages); raising --limit catches up faster.`);
48247
+ }
48248
+ }
48249
+ if (sweep.rows.length > 0) {
48250
+ let newestMs = Number.NEGATIVE_INFINITY;
48251
+ for (const row2 of sweep.rows) {
48252
+ const ms = Date.parse(row2.timestamp);
48253
+ if (Number.isFinite(ms) && ms > newestMs) newestMs = ms;
48254
+ }
48255
+ if (Number.isFinite(newestMs)) {
48256
+ since = new Date(newestMs).toISOString();
48257
+ boundarySeen = new Set(sweep.rows.filter((row2) => Date.parse(row2.timestamp) === newestMs).map((row2) => rowKey(row2)));
48258
+ }
48259
+ }
48260
+ if (untilMs !== void 0 && Date.now() >= untilMs) break;
48261
+ if (controller.signal.aborted) break;
48262
+ await sleep2(followDelayMs(), controller.signal);
48263
+ }
48264
+ } finally {
48265
+ process.off("SIGINT", onSigint);
48266
+ if (!controller.signal.aborted) controller.abort();
48267
+ }
48268
+ }
48269
+ __name(followLogs, "followLogs");
47780
48270
  async function nonInteractiveLogs(logsApi, agentId, apiKey, options) {
47781
48271
  const filters = {};
47782
48272
  if (options.type && options.type !== "all") {
@@ -47799,6 +48289,13 @@ async function nonInteractiveLogs(logsApi, agentId, apiKey, options) {
47799
48289
  if (options.userId) {
47800
48290
  filters.userId = options.userId;
47801
48291
  }
48292
+ if (options.since) filters.since = options.since;
48293
+ if (options.until) filters.until = options.until;
48294
+ if (options.environment) filters.environment = options.environment;
48295
+ if (options.follow) {
48296
+ await followLogs(logsApi, agentId, options, filters);
48297
+ return;
48298
+ }
47802
48299
  const response = await logsApi.getAgentLogs(agentId, options.limit || 20, options.page || 1, filters);
47803
48300
  if (!response.success) {
47804
48301
  throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Unknown error", void 0, apiErrorDetail(response.error));
@@ -47807,6 +48304,10 @@ async function nonInteractiveLogs(logsApi, agentId, apiKey, options) {
47807
48304
  if (options.json) {
47808
48305
  console.log(JSON.stringify({
47809
48306
  logs: data.logs,
48307
+ // Always present so a script can `while (nextCursor)` without probing for the
48308
+ // key; `null` is the documented end-of-stream, and a server that predates A4
48309
+ // simply never sets it.
48310
+ nextCursor: data.nextCursor ?? null,
47810
48311
  pagination: data.pagination
47811
48312
  }, null, 2));
47812
48313
  } else {
@@ -48095,8 +48596,8 @@ async function handleNavigation(action, pagination, setPage) {
48095
48596
  }
48096
48597
  }
48097
48598
  __name(handleNavigation, "handleNavigation");
48098
- function capitalize(str) {
48099
- return str.charAt(0).toUpperCase() + str.slice(1);
48599
+ function capitalize(str2) {
48600
+ return str2.charAt(0).toUpperCase() + str2.slice(1);
48100
48601
  }
48101
48602
  __name(capitalize, "capitalize");
48102
48603
  function getLogIcon(subType, logSource) {
@@ -48160,6 +48661,1423 @@ function getLogColor(subType, logSource) {
48160
48661
  }
48161
48662
  __name(getLogColor, "getLogColor");
48162
48663
 
48664
+ // src/commands/drains.ts
48665
+ init_cli();
48666
+ init_command_utils();
48667
+ init_files();
48668
+ init_credential_operational_context();
48669
+ init_constants();
48670
+ init_cli_error();
48671
+ init_analytics();
48672
+
48673
+ // src/api/drains.api.service.ts
48674
+ init_http_client();
48675
+ var DrainsApi = class extends HttpClient {
48676
+ static {
48677
+ __name(this, "DrainsApi");
48678
+ }
48679
+ orgId;
48680
+ constructor(baseUrl, credential, orgId) {
48681
+ super(baseUrl, credential), this.orgId = orgId;
48682
+ }
48683
+ /**
48684
+ * The base path every call hangs off — RELATIVE to `baseUrl`, because every
48685
+ * `HttpClient` verb prepends `this.baseUrl` itself (`httpGet(url) => baseUrl + url`).
48686
+ * The scaffolded body interpolated `baseUrl` a second time, which would have sent
48687
+ * every request to `<base><base>/admin/…`; the signature is unchanged.
48688
+ */
48689
+ base() {
48690
+ return `/admin/orgs/${encodeURIComponent(this.orgId)}/log-drains`;
48691
+ }
48692
+ /** `…/log-drains/:drainId` with the id encoded. */
48693
+ drainPath(drainId, suffix = "") {
48694
+ return `${this.base()}/${encodeURIComponent(drainId)}${suffix}`;
48695
+ }
48696
+ async listDrains() {
48697
+ return this.httpGet(this.base());
48698
+ }
48699
+ async getDrain(drainId) {
48700
+ return this.httpGet(this.drainPath(drainId));
48701
+ }
48702
+ /** The response carries `secret` — print it ONCE and never store it. */
48703
+ async createDrain(input) {
48704
+ return this.httpPostOnce(this.base(), input);
48705
+ }
48706
+ async updateDrain(drainId, input) {
48707
+ return this.httpPatch(this.drainPath(drainId), input);
48708
+ }
48709
+ async deleteDrain(drainId) {
48710
+ return this.httpDelete(this.drainPath(drainId));
48711
+ }
48712
+ async testDrain(drainId) {
48713
+ return this.httpPost(this.drainPath(drainId, "/test"));
48714
+ }
48715
+ async verifyDrain(drainId) {
48716
+ return this.httpPost(this.drainPath(drainId, "/verify"));
48717
+ }
48718
+ async pauseDrain(drainId, reason) {
48719
+ return this.httpPost(this.drainPath(drainId, "/pause"), reason ? {
48720
+ reason
48721
+ } : {});
48722
+ }
48723
+ async resumeDrain(drainId) {
48724
+ return this.httpPost(this.drainPath(drainId, "/resume"), {});
48725
+ }
48726
+ /**
48727
+ * `finalize` closes an open rotation window instead of opening a new one — that
48728
+ * answer is a plain `LogDrainRead` and is safe to retry; minting is not.
48729
+ */
48730
+ async rotateSecret(drainId, finalize) {
48731
+ const path26 = this.drainPath(drainId, `/rotate-secret${finalize ? "?finalize=true" : ""}`);
48732
+ return finalize ? this.httpPost(path26, {}) : this.httpPostOnce(path26, {});
48733
+ }
48734
+ async listDeliveries(drainId, opts = {}) {
48735
+ const query = new URLSearchParams();
48736
+ if (opts.limit !== void 0) query.set("limit", String(opts.limit));
48737
+ if (opts.cursor) query.set("cursor", opts.cursor);
48738
+ if (opts.kind) query.set("kind", opts.kind);
48739
+ const suffix = query.toString() ? `?${query.toString()}` : "";
48740
+ return this.httpGet(this.drainPath(drainId, `/deliveries${suffix}`));
48741
+ }
48742
+ };
48743
+
48744
+ // src/commands/drains.mutations.ts
48745
+ init_dist();
48746
+ init_cli();
48747
+ init_cli_error();
48748
+ var CONTENT_WARNING_TEXT = "These sources carry customer content: user_message and agent_response bodies, and the debug tool envelopes attached to them. Message bodies and tool inputs may contain personal data and secrets. Lua scrubs known token shapes only \u2014 anything it does not recognise leaves the platform with the record.";
48749
+ var CONTENT_ACKNOWLEDGEMENT_TEXT = "I understand that user messages, agent responses and debug tool envelopes may contain personal data and secrets, and that Lua scrubs known token shapes only.";
48750
+ var MAX_HEADERS = 10;
48751
+ var HEADER_NAME_PATTERN = /^[A-Za-z0-9-]{1,64}$/;
48752
+ var ENDPOINT_TYPES = [
48753
+ "http",
48754
+ "otlp",
48755
+ "betterstack"
48756
+ ];
48757
+ var SITE_TYPES = [
48758
+ "datadog"
48759
+ ];
48760
+ var PHASE2_SOURCES = [
48761
+ "delivery",
48762
+ "trigger-execution",
48763
+ "job-execution",
48764
+ "workflow-run",
48765
+ "workflow-audit"
48766
+ ];
48767
+ var SELECTABLE_SOURCES = LOG_DRAIN_SOURCES.filter((source) => !PHASE2_SOURCES.includes(source));
48768
+ var NON_CONTENT_SOURCES = SELECTABLE_SOURCES.filter((source) => !LOG_DRAIN_CONTENT_SOURCES.includes(source));
48769
+ var TOKEN_ECHO_TYPES = [
48770
+ "http"
48771
+ ];
48772
+ var VERIFY_OUTCOME_TEXT = {
48773
+ ok: "verified",
48774
+ no_2xx: "the endpoint did not answer 2xx",
48775
+ token_not_echoed: "token not echoed",
48776
+ ssrf_refused: "the endpoint was refused by the SSRF guard",
48777
+ timeout: "the endpoint did not answer in time",
48778
+ error: "the verification request failed"
48779
+ };
48780
+ var DRAIN_POLL = {
48781
+ intervalMs: 1e3,
48782
+ testAttempts: 30,
48783
+ verifyAttempts: 60,
48784
+ sleep: /* @__PURE__ */ __name((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)), "sleep")
48785
+ };
48786
+ function printJson(payload) {
48787
+ console.log(JSON.stringify(payload, null, 2));
48788
+ }
48789
+ __name(printJson, "printJson");
48790
+ function emitAside(line, json) {
48791
+ if (json) process.stderr.write(`${line}
48792
+ `);
48793
+ else writeInfo(line);
48794
+ }
48795
+ __name(emitAside, "emitAside");
48796
+ function shortIso(value3) {
48797
+ if (typeof value3 !== "string" || value3.length === 0) return "\u2014";
48798
+ const date = new Date(value3);
48799
+ if (Number.isNaN(date.getTime())) return value3;
48800
+ return date.toISOString().replace(/\.\d{3}Z$/, "Z");
48801
+ }
48802
+ __name(shortIso, "shortIso");
48803
+ function trimmed(value3) {
48804
+ if (typeof value3 !== "string") return void 0;
48805
+ const text = value3.trim();
48806
+ return text.length > 0 ? text : void 0;
48807
+ }
48808
+ __name(trimmed, "trimmed");
48809
+ function parseType(raw) {
48810
+ const value3 = trimmed(raw)?.toLowerCase();
48811
+ if (!value3) return void 0;
48812
+ if (!LOG_DRAIN_TYPES_PHASE1.includes(value3)) {
48813
+ throw CliError.usage(`--type "${raw}" is not available.`, listHint("Available destinations", LOG_DRAIN_TYPES_PHASE1));
48814
+ }
48815
+ return value3;
48816
+ }
48817
+ __name(parseType, "parseType");
48818
+ function parseFormat(raw) {
48819
+ const value3 = trimmed(raw)?.toLowerCase();
48820
+ if (!value3) return void 0;
48821
+ if (!LOG_DRAIN_FORMATS.includes(value3)) {
48822
+ throw CliError.usage(`--format "${raw}" is not a drain format.`, listHint("Valid values", LOG_DRAIN_FORMATS));
48823
+ }
48824
+ return value3;
48825
+ }
48826
+ __name(parseFormat, "parseFormat");
48827
+ function parseSeverity(raw) {
48828
+ const value3 = trimmed(raw)?.toLowerCase();
48829
+ if (!value3) return void 0;
48830
+ if (!LOG_DRAIN_SEVERITIES.includes(value3)) {
48831
+ throw CliError.usage(`--min-severity "${raw}" is not a severity.`, listHint("Valid values", LOG_DRAIN_SEVERITIES));
48832
+ }
48833
+ return value3;
48834
+ }
48835
+ __name(parseSeverity, "parseSeverity");
48836
+ function splitList(raw) {
48837
+ return raw.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
48838
+ }
48839
+ __name(splitList, "splitList");
48840
+ function parseSources(raw) {
48841
+ const value3 = trimmed(raw);
48842
+ if (!value3) return void 0;
48843
+ const requested = splitList(value3);
48844
+ if (requested.length === 0) {
48845
+ throw CliError.usage("--sources was empty \u2014 name at least one source, or leave the flag off for the default set.");
48846
+ }
48847
+ const sources = [];
48848
+ for (const source of requested) {
48849
+ if (!SELECTABLE_SOURCES.includes(source)) {
48850
+ throw CliError.usage(`--sources "${source}" is not a selectable source.`, listHint("Valid values", SELECTABLE_SOURCES));
48851
+ }
48852
+ if (!sources.includes(source)) sources.push(source);
48853
+ }
48854
+ return sources;
48855
+ }
48856
+ __name(parseSources, "parseSources");
48857
+ function parseEnvironments(raw) {
48858
+ const value3 = trimmed(raw);
48859
+ if (!value3) return void 0;
48860
+ const environments = [];
48861
+ for (const environment of splitList(value3)) {
48862
+ if (!AGENT_LOG_ENVIRONMENTS.includes(environment)) {
48863
+ throw CliError.usage(`--environments "${environment}" is not an environment.`, listHint("Valid values", AGENT_LOG_ENVIRONMENTS));
48864
+ }
48865
+ if (!environments.includes(environment)) {
48866
+ environments.push(environment);
48867
+ }
48868
+ }
48869
+ return environments.length > 0 ? environments : void 0;
48870
+ }
48871
+ __name(parseEnvironments, "parseEnvironments");
48872
+ function parseAgents(raw) {
48873
+ const value3 = trimmed(raw);
48874
+ if (!value3) return void 0;
48875
+ if (value3.toLowerCase() === "all" || value3 === "*") return "*";
48876
+ const agents = splitList(value3);
48877
+ if (agents.length === 0) {
48878
+ throw CliError.usage("--agents was empty \u2014 pass 'all' or a comma-separated list of agent ids.");
48879
+ }
48880
+ return agents;
48881
+ }
48882
+ __name(parseAgents, "parseAgents");
48883
+ function parseSampling(raw) {
48884
+ const value3 = trimmed(raw);
48885
+ if (!value3) return void 0;
48886
+ const hint = "Only debug is sampleable in phase 1, e.g. --sampling debug=0.1";
48887
+ const eq3 = value3.indexOf("=");
48888
+ if (eq3 === -1) throw CliError.usage(`--sampling "${raw}" must read <key>=<fraction>.`, hint);
48889
+ const key = value3.slice(0, eq3).trim().toLowerCase();
48890
+ const fraction = Number(value3.slice(eq3 + 1).trim());
48891
+ if (key !== "debug") throw CliError.usage(`--sampling "${key}" is not sampleable.`, hint);
48892
+ if (!Number.isFinite(fraction) || fraction <= 0 || fraction > 1) {
48893
+ throw CliError.usage(`--sampling debug must be a fraction in (0, 1] (got "${value3.slice(eq3 + 1).trim()}").`, hint);
48894
+ }
48895
+ return {
48896
+ debug: fraction
48897
+ };
48898
+ }
48899
+ __name(parseSampling, "parseSampling");
48900
+ function assertHeaderName(name, flag) {
48901
+ if (!HEADER_NAME_PATTERN.test(name)) {
48902
+ throw CliError.usage(`${flag} header name "${name}" must match ^[A-Za-z0-9-]{1,64}$.`, "A header name is a token \u2014 no spaces, no colon, no newline");
48903
+ }
48904
+ }
48905
+ __name(assertHeaderName, "assertHeaderName");
48906
+ async function resolveHeaders(options) {
48907
+ const headers = [];
48908
+ const seen = /* @__PURE__ */ new Set();
48909
+ const remember = /* @__PURE__ */ __name((header) => {
48910
+ const key = header.name.toLowerCase();
48911
+ if (seen.has(key)) {
48912
+ throw CliError.usage(`Header "${header.name}" was given twice.`, "Each header name may appear once");
48913
+ }
48914
+ seen.add(key);
48915
+ headers.push(header);
48916
+ }, "remember");
48917
+ for (const entry of options.headerFromEnv ?? []) {
48918
+ const raw = `${entry}`.trim();
48919
+ const eq3 = raw.indexOf("=");
48920
+ if (eq3 <= 0 || eq3 === raw.length - 1) {
48921
+ throw CliError.usage(`--header-from-env "${raw}" must read <name>=<ENV_VAR>.`, "The VALUE stays in the environment \u2014 e.g. --header-from-env DD-API-KEY=DATADOG_API_KEY");
48922
+ }
48923
+ const name = raw.slice(0, eq3).trim();
48924
+ const variable = raw.slice(eq3 + 1).trim();
48925
+ assertHeaderName(name, "--header-from-env");
48926
+ const value3 = process.env[variable];
48927
+ if (value3 === void 0 || value3 === "") {
48928
+ throw CliError.usage(`--header-from-env ${name}=${variable}: the environment variable ${variable} is unset or empty.`, `Export ${variable} in the step that runs this command`);
48929
+ }
48930
+ remember({
48931
+ name,
48932
+ value: value3,
48933
+ fromEnv: variable
48934
+ });
48935
+ }
48936
+ for (const entry of options.header ?? []) {
48937
+ const raw = `${entry}`.trim();
48938
+ if (raw.includes("=")) {
48939
+ throw CliError.usage(`--header takes a header NAME, not a value ("${raw.slice(0, raw.indexOf("="))}=\u2026").`, joinHint("Header values are never flags \u2014 a shell history and a CI log both keep them.", "Interactively: --header " + raw.slice(0, raw.indexOf("=")) + " (the value is prompted)", "In --ci: --header-from-env " + raw.slice(0, raw.indexOf("=")) + "=YOUR_ENV_VAR"));
48940
+ }
48941
+ assertHeaderName(raw, "--header");
48942
+ if (isCiModeEnabled()) {
48943
+ throw CliError.usage(`--header ${raw} needs an interactive prompt for its value, and --ci is set.`, `Use --header-from-env ${raw}=YOUR_ENV_VAR so the value never reaches the command line`);
48944
+ }
48945
+ const answer = await safePrompt([
48946
+ {
48947
+ type: "password",
48948
+ name: "value",
48949
+ mask: "*",
48950
+ message: `Value for header ${raw}:`,
48951
+ validate: /* @__PURE__ */ __name((input) => input && input.length > 0 ? true : "A header value cannot be empty", "validate")
48952
+ }
48953
+ ]);
48954
+ const value3 = answer?.value;
48955
+ if (!value3) throw CliError.usage(`No value was given for header ${raw}.`);
48956
+ remember({
48957
+ name: raw,
48958
+ value: value3
48959
+ });
48960
+ }
48961
+ if (headers.length > MAX_HEADERS) {
48962
+ throw CliError.usage(`A drain carries at most ${MAX_HEADERS} headers (got ${headers.length}).`);
48963
+ }
48964
+ return headers;
48965
+ }
48966
+ __name(resolveHeaders, "resolveHeaders");
48967
+ function wireHeaders(headers) {
48968
+ return headers.map(({ name, value: value3 }) => ({
48969
+ name,
48970
+ value: value3
48971
+ }));
48972
+ }
48973
+ __name(wireHeaders, "wireHeaders");
48974
+ function wantsContent(options, sources) {
48975
+ if (options.includeContent === true) return true;
48976
+ return (sources ?? []).some((source) => LOG_DRAIN_CONTENT_SOURCES.includes(source));
48977
+ }
48978
+ __name(wantsContent, "wantsContent");
48979
+ async function acknowledgeContent(options) {
48980
+ emitAside("", options.json);
48981
+ emitAside(CONTENT_WARNING_TEXT, options.json);
48982
+ emitAside("", options.json);
48983
+ emitAside(CONTENT_ACKNOWLEDGEMENT_TEXT, options.json);
48984
+ emitAside("", options.json);
48985
+ if (options.yes) return true;
48986
+ if (isCiModeEnabled()) {
48987
+ throw CliError.usage("--include-content needs the acknowledgement above; pass --yes to give it non-interactively.", "Nothing was created");
48988
+ }
48989
+ const answer = await safePrompt([
48990
+ {
48991
+ type: "confirm",
48992
+ name: "acknowledged",
48993
+ message: "Do you acknowledge this?",
48994
+ default: false
48995
+ }
48996
+ ]);
48997
+ if (!answer?.acknowledged) {
48998
+ throw CliError.usage("Content was not enabled: the acknowledgement was declined.", "Nothing was created");
48999
+ }
49000
+ return true;
49001
+ }
49002
+ __name(acknowledgeContent, "acknowledgeContent");
49003
+ function missingFlag(flag, what, hint) {
49004
+ return CliError.usage(`${flag} is required (${what}).`, joinHint(hint, "Prompting is not possible under --ci"));
49005
+ }
49006
+ __name(missingFlag, "missingFlag");
49007
+ async function promptText(message, flag, what) {
49008
+ if (isCiModeEnabled()) throw missingFlag(flag, what);
49009
+ const answer = await safePrompt([
49010
+ {
49011
+ type: "input",
49012
+ name: "value",
49013
+ message,
49014
+ validate: /* @__PURE__ */ __name((input) => input && input.trim().length > 0 ? true : "Required", "validate")
49015
+ }
49016
+ ]);
49017
+ const value3 = trimmed(answer?.value);
49018
+ if (!value3) throw missingFlag(flag, what);
49019
+ return value3;
49020
+ }
49021
+ __name(promptText, "promptText");
49022
+ async function promptChoice(message, choices, flag, what) {
49023
+ if (isCiModeEnabled()) throw missingFlag(flag, what, `Values: ${choices.join(" | ")}`);
49024
+ const answer = await safePrompt([
49025
+ {
49026
+ type: "list",
49027
+ name: "value",
49028
+ message,
49029
+ choices
49030
+ }
49031
+ ]);
49032
+ const value3 = answer?.value;
49033
+ if (!value3) throw missingFlag(flag, what, `Values: ${choices.join(" | ")}`);
49034
+ return value3;
49035
+ }
49036
+ __name(promptChoice, "promptChoice");
49037
+ async function promptSources() {
49038
+ const answer = await safePrompt([
49039
+ {
49040
+ type: "checkbox",
49041
+ name: "sources",
49042
+ message: "Which sources should this drain carry?",
49043
+ pageSize: 20,
49044
+ choices: [
49045
+ {
49046
+ type: "separator",
49047
+ line: "\u2500\u2500 Conversation content (needs the acknowledgement) \u2500\u2500"
49048
+ },
49049
+ ...LOG_DRAIN_CONTENT_SOURCES.map((source) => ({
49050
+ name: source,
49051
+ value: source,
49052
+ checked: false
49053
+ })),
49054
+ {
49055
+ type: "separator",
49056
+ line: "\u2500\u2500 Execution and platform \u2500\u2500"
49057
+ },
49058
+ ...NON_CONTENT_SOURCES.map((source) => ({
49059
+ name: source,
49060
+ value: source,
49061
+ checked: LOG_DRAIN_DEFAULT_SOURCES.includes(source)
49062
+ }))
49063
+ ],
49064
+ validate: /* @__PURE__ */ __name((chosen) => chosen.length > 0 ? true : "Pick at least one source", "validate")
49065
+ }
49066
+ ]);
49067
+ const sources = answer?.sources ?? [];
49068
+ if (sources.length === 0) throw CliError.usage("A drain needs at least one source.");
49069
+ return sources;
49070
+ }
49071
+ __name(promptSources, "promptSources");
49072
+ async function resolveDestination(type, options) {
49073
+ const endpoint = trimmed(options.endpoint);
49074
+ const site = trimmed(options.site);
49075
+ if (SITE_TYPES.includes(type)) {
49076
+ if (endpoint) {
49077
+ throw CliError.usage(`--endpoint is not accepted for ${type}: its URL is derived from --site.`, listHint("Sites", LOG_DRAIN_DATADOG_SITES));
49078
+ }
49079
+ const chosen = site ?? await promptChoice("Which site?", LOG_DRAIN_DATADOG_SITES, "--site", `${type}`);
49080
+ if (!LOG_DRAIN_DATADOG_SITES.includes(chosen)) {
49081
+ throw CliError.usage(`--site "${chosen}" is not a ${type} site.`, listHint("Sites", LOG_DRAIN_DATADOG_SITES));
49082
+ }
49083
+ return {
49084
+ site: chosen
49085
+ };
49086
+ }
49087
+ if (!ENDPOINT_TYPES.includes(type)) {
49088
+ throw CliError.usage(`This lua-cli does not know how to address a "${type}" destination yet.`, "Upgrade lua-cli, or create the drain from the dashboard");
49089
+ }
49090
+ if (site) {
49091
+ throw CliError.usage(`--site is not accepted for ${type}: give the destination URL with --endpoint.`);
49092
+ }
49093
+ const url = endpoint ?? await promptText(type === "otlp" ? "OTLP logs endpoint (the full /v1/logs URL):" : "Destination URL:", "--endpoint", `${type} sends to your own URL`);
49094
+ return {
49095
+ endpoint: url
49096
+ };
49097
+ }
49098
+ __name(resolveDestination, "resolveDestination");
49099
+ function verificationCurl(type, endpoint, headers) {
49100
+ const target = endpoint ?? "<your endpoint>";
49101
+ const headerArgs = headers.map((header) => header.fromEnv ? ` -H "${header.name}: $${header.fromEnv}" \\` : ` -H '${header.name}: <the value you entered>' \\`);
49102
+ if (TOKEN_ECHO_TYPES.includes(type)) {
49103
+ return [
49104
+ `curl -isS -X POST '${target}' \\`,
49105
+ ...headerArgs,
49106
+ ` -H 'Content-Type: application/json' \\`,
49107
+ ` -H 'X-Lua-Verify: lua-echo-check' \\`,
49108
+ ` --data '{"records":[]}' | grep -i 'x-lua-verify'`
49109
+ ];
49110
+ }
49111
+ return [
49112
+ `curl -isS -X POST '${target}' \\`,
49113
+ ...headerArgs,
49114
+ ` -H 'Content-Type: application/json' \\`,
49115
+ ` --data '[]'`
49116
+ ];
49117
+ }
49118
+ __name(verificationCurl, "verificationCurl");
49119
+ function verificationInstructions(drain, headers, options) {
49120
+ const type = drain.type;
49121
+ const json = options.json;
49122
+ emitAside("", json);
49123
+ emitAside("Before anything is delivered, Lua has to confirm you own this destination.", json);
49124
+ if (TOKEN_ECHO_TYPES.includes(type)) {
49125
+ emitAside("Lua POSTs one record carrying an X-Lua-Verify header; your receiver must answer 2xx AND", json);
49126
+ emitAside("repeat that header back on the response. Check yours first:", json);
49127
+ } else {
49128
+ emitAside("Lua POSTs one record to the intake; any 2xx from the vendor verifies the drain.", json);
49129
+ emitAside("Check the credentials reach the intake first:", json);
49130
+ }
49131
+ emitAside("", json);
49132
+ for (const line of verificationCurl(type, drain.endpoint, headers)) emitAside(` ${line}`, json);
49133
+ emitAside("", json);
49134
+ emitAside(`Then: lua drains verify ${drain.id}`, json);
49135
+ }
49136
+ __name(verificationInstructions, "verificationInstructions");
49137
+ async function createDrainCore(context, options) {
49138
+ const name = trimmed(options.name) ?? await promptText("Drain name:", "--name", "1\u201364 chars, unique per org");
49139
+ const type = parseType(options.type) ?? await promptChoice("Where should the logs go?", LOG_DRAIN_TYPES_PHASE1, "--type", "the destination kind");
49140
+ const destination = await resolveDestination(type, options);
49141
+ const format = parseFormat(options.format);
49142
+ const headers = await resolveHeaders(options);
49143
+ const flagSources = parseSources(options.sources);
49144
+ const sources = flagSources ?? (isCiModeEnabled() ? void 0 : await promptSources());
49145
+ const minSeverity = parseSeverity(options.minSeverity);
49146
+ const environments = parseEnvironments(options.environments);
49147
+ const sampling = parseSampling(options.sampling);
49148
+ const agents = parseAgents(options.agents) ?? (context.agentId ? [
49149
+ context.agentId
49150
+ ] : "*");
49151
+ const includeContent = wantsContent(options, sources);
49152
+ const includeContentAck = includeContent ? await acknowledgeContent(options) : void 0;
49153
+ const input = {
49154
+ name,
49155
+ type,
49156
+ ...destination.endpoint ? {
49157
+ endpoint: destination.endpoint
49158
+ } : {},
49159
+ ...destination.site ? {
49160
+ site: destination.site
49161
+ } : {},
49162
+ ...format ? {
49163
+ format
49164
+ } : {},
49165
+ ...headers.length > 0 ? {
49166
+ headers: wireHeaders(headers)
49167
+ } : {},
49168
+ ...sources ? {
49169
+ sources
49170
+ } : {},
49171
+ ...minSeverity ? {
49172
+ minSeverity
49173
+ } : {},
49174
+ agents,
49175
+ environments: environments ?? [
49176
+ "production"
49177
+ ],
49178
+ ...sampling ? {
49179
+ sampling
49180
+ } : {},
49181
+ ...includeContent ? {
49182
+ includeContent: true,
49183
+ includeContentAck
49184
+ } : {}
49185
+ };
49186
+ const created = await drainsCall(() => drainsApiFor(context).createDrain(input));
49187
+ const { secret, ...drain } = created;
49188
+ if (options.json) {
49189
+ printJson(secret ? {
49190
+ drain,
49191
+ secret
49192
+ } : {
49193
+ drain
49194
+ });
49195
+ } else {
49196
+ writeSuccess(`\u2705 Created drain ${drain.name} (${drain.id})`);
49197
+ writeInfo(` type ${drain.type} \xB7 ${drain.endpoint ?? drain.site ?? "\u2014"} \xB7 state ${drain.state}`);
49198
+ if (secret) {
49199
+ writeInfo("");
49200
+ writeInfo("\u{1F511} Signing secret \u2014 shown once, and never again. Store it now:");
49201
+ writeInfo(` ${secret}`);
49202
+ writeInfo(" Your receiver verifies X-Lua-Signature with it (docs: /drains/verify-signatures).");
49203
+ }
49204
+ }
49205
+ verificationInstructions(drain, headers, options);
49206
+ if (!options.json) {
49207
+ writeHintBlock({
49208
+ headline: "Next:",
49209
+ lines: [
49210
+ {
49211
+ label: "Verify",
49212
+ command: `lua drains verify ${drain.id}`
49213
+ },
49214
+ {
49215
+ label: "Send one record",
49216
+ command: `lua drains test ${drain.id}`
49217
+ }
49218
+ ]
49219
+ });
49220
+ }
49221
+ }
49222
+ __name(createDrainCore, "createDrainCore");
49223
+ async function updateDrainCore(context, drainId, options) {
49224
+ if (trimmed(options.type)) {
49225
+ throw CliError.usage("A drain cannot change type in place.", "Delete this drain and create a new one of the other type");
49226
+ }
49227
+ const patch = {};
49228
+ const name = trimmed(options.name);
49229
+ if (name) patch.name = name;
49230
+ const endpoint = trimmed(options.endpoint);
49231
+ if (endpoint) patch.endpoint = endpoint;
49232
+ const site = trimmed(options.site);
49233
+ if (site) patch.site = site;
49234
+ const format = parseFormat(options.format);
49235
+ if (format) patch.format = format;
49236
+ const headers = await resolveHeaders(options);
49237
+ if (headers.length > 0) patch.headers = wireHeaders(headers);
49238
+ const sources = parseSources(options.sources);
49239
+ if (sources) patch.sources = sources;
49240
+ const minSeverity = parseSeverity(options.minSeverity);
49241
+ if (minSeverity) patch.minSeverity = minSeverity;
49242
+ const agents = parseAgents(options.agents);
49243
+ if (agents) patch.agents = agents;
49244
+ const environments = parseEnvironments(options.environments);
49245
+ if (environments) patch.environments = environments;
49246
+ const sampling = parseSampling(options.sampling);
49247
+ if (sampling) patch.sampling = sampling;
49248
+ if (options.includeContent === false) {
49249
+ if ((sources ?? []).some((source) => LOG_DRAIN_CONTENT_SOURCES.includes(source))) {
49250
+ throw CliError.usage("--no-include-content cannot be combined with a content source in the same update.", "Send the sources first, then --no-include-content \u2014 or drop the content sources");
49251
+ }
49252
+ patch.includeContent = false;
49253
+ } else if (wantsContent(options, sources)) {
49254
+ patch.includeContent = true;
49255
+ patch.includeContentAck = await acknowledgeContent(options);
49256
+ }
49257
+ if (Object.keys(patch).length === 0) {
49258
+ throw CliError.usage("Nothing to update \u2014 no field flags were given.", listHint("Changeable", [
49259
+ "--name",
49260
+ "--endpoint",
49261
+ "--site",
49262
+ "--format",
49263
+ "--header / --header-from-env",
49264
+ "--sources",
49265
+ "--min-severity",
49266
+ "--agents",
49267
+ "--environments",
49268
+ "--sampling",
49269
+ "--include-content"
49270
+ ]));
49271
+ }
49272
+ const drain = await drainsCall(() => drainsApiFor(context).updateDrain(drainId, patch));
49273
+ if (options.json) {
49274
+ printJson({
49275
+ drain
49276
+ });
49277
+ return;
49278
+ }
49279
+ writeSuccess(`\u2705 Updated ${drain.name} (${drain.id})`);
49280
+ writeInfo(` state ${drain.state}`);
49281
+ if (drain.state === "pending_verification") {
49282
+ writeInfo(" The destination changed, so delivery is paused until the drain is verified again.");
49283
+ writeInfo(" Records keep buffering while you do.");
49284
+ writeHintBlock({
49285
+ headline: "Next:",
49286
+ lines: [
49287
+ {
49288
+ label: "Verify",
49289
+ command: `lua drains verify ${drain.id}`
49290
+ }
49291
+ ]
49292
+ });
49293
+ }
49294
+ }
49295
+ __name(updateDrainCore, "updateDrainCore");
49296
+ async function deleteDrainCore(context, drainId, options) {
49297
+ if (!options.yes) {
49298
+ if (isCiModeEnabled()) {
49299
+ throw CliError.usage(`Deleting ${drainId} needs a confirmation; pass --yes to give it non-interactively.`, "Nothing was deleted");
49300
+ }
49301
+ writeInfo(`Deleting a drain purges the destination's stored credentials and drops its queued records.`);
49302
+ const answer = await safePrompt([
49303
+ {
49304
+ type: "confirm",
49305
+ name: "confirmed",
49306
+ message: `Delete drain ${drainId}?`,
49307
+ default: false
49308
+ }
49309
+ ]);
49310
+ if (!answer?.confirmed) {
49311
+ throw CliError.usage("Cancelled \u2014 nothing was deleted.");
49312
+ }
49313
+ }
49314
+ await drainsCall(() => drainsApiFor(context).deleteDrain(drainId));
49315
+ if (options.json) {
49316
+ printJson({
49317
+ id: drainId,
49318
+ deleted: true
49319
+ });
49320
+ return;
49321
+ }
49322
+ writeSuccess(`\u2705 Deleted drain ${drainId}`);
49323
+ writeInfo(" Stored credentials were purged and queued records were dropped.");
49324
+ }
49325
+ __name(deleteDrainCore, "deleteDrainCore");
49326
+ async function awaitTestDelivery(api, drainId, batchId) {
49327
+ for (let attempt = 0; attempt < DRAIN_POLL.testAttempts; attempt++) {
49328
+ await DRAIN_POLL.sleep(DRAIN_POLL.intervalMs);
49329
+ const page = await drainsCall(() => api.listDeliveries(drainId, {
49330
+ kind: "test",
49331
+ limit: 20
49332
+ }));
49333
+ const row2 = (page.deliveries ?? []).find((delivery) => delivery.batchId === batchId);
49334
+ if (row2) return row2;
49335
+ }
49336
+ return null;
49337
+ }
49338
+ __name(awaitTestDelivery, "awaitTestDelivery");
49339
+ async function testDrainCore(context, drainId, options) {
49340
+ const api = drainsApiFor(context);
49341
+ const started = await drainsCall(() => api.testDrain(drainId));
49342
+ if (!options.json) writeInfo(`Sent one test record to ${drainId} (batch ${started.batchId}) \u2014 waiting for it\u2026`);
49343
+ const attempt = await awaitTestDelivery(api, drainId, started.batchId);
49344
+ const delivered = attempt?.ok === true;
49345
+ if (options.json) {
49346
+ printJson({
49347
+ batchId: started.batchId,
49348
+ statusCode: attempt?.statusCode ?? null,
49349
+ latencyMs: attempt?.latencyMs ?? null,
49350
+ responseExcerpt: attempt?.responseExcerpt ?? null,
49351
+ ok: delivered,
49352
+ errorClass: attempt?.errorClass ?? null,
49353
+ ...attempt ? {} : {
49354
+ pending: true
49355
+ }
49356
+ });
49357
+ } else if (!attempt) {
49358
+ writeInfo(`\u23F3 No delivery attempt recorded yet for batch ${started.batchId}.`);
49359
+ writeHintBlock({
49360
+ headline: "Read it when it lands:",
49361
+ lines: [
49362
+ {
49363
+ label: "Attempts",
49364
+ command: `lua drains deliveries ${drainId} --kind test`
49365
+ }
49366
+ ],
49367
+ when: "partial"
49368
+ });
49369
+ } else {
49370
+ const status = attempt.statusCode === void 0 ? "\u2014" : String(attempt.statusCode);
49371
+ const latency = attempt.latencyMs === void 0 ? "\u2014" : `${attempt.latencyMs} ms`;
49372
+ if (delivered) writeSuccess(`\u2705 Delivered \u2014 HTTP ${status} in ${latency}`);
49373
+ else writeInfo(`Not delivered \u2014 HTTP ${status} in ${latency}${attempt.errorClass ? ` (${attempt.errorClass})` : ""}`);
49374
+ if (attempt.responseExcerpt) writeInfo(` response: ${attempt.responseExcerpt}`);
49375
+ }
49376
+ if (!delivered) process.exitCode = CLI_EXIT.ERROR;
49377
+ }
49378
+ __name(testDrainCore, "testDrainCore");
49379
+ async function awaitVerification(api, drainId, previousAttemptedAt) {
49380
+ for (let attempt = 0; attempt < DRAIN_POLL.verifyAttempts; attempt++) {
49381
+ await DRAIN_POLL.sleep(DRAIN_POLL.intervalMs);
49382
+ const drain = await drainsCall(() => api.getDrain(drainId));
49383
+ const verification = drain.verification;
49384
+ if (verification?.outcome && verification.attemptedAt !== previousAttemptedAt) return drain;
49385
+ }
49386
+ return null;
49387
+ }
49388
+ __name(awaitVerification, "awaitVerification");
49389
+ async function verifyDrainCore(context, drainId, options) {
49390
+ const api = drainsApiFor(context);
49391
+ const before = await drainsCall(() => api.getDrain(drainId));
49392
+ const started = await drainsCall(() => api.verifyDrain(drainId));
49393
+ if (!options.json) writeInfo(`Verifying ${drainId} \u2014 waiting for your endpoint to answer\u2026`);
49394
+ const drain = await awaitVerification(api, drainId, before.verification?.attemptedAt);
49395
+ const verification = drain?.verification;
49396
+ const outcome = verification?.outcome;
49397
+ const verified = outcome === "ok";
49398
+ const line = outcome ? VERIFY_OUTCOME_TEXT[outcome] : "no result yet";
49399
+ if (options.json) {
49400
+ printJson({
49401
+ verificationId: started.verificationId,
49402
+ expiresAt: started.expiresAt,
49403
+ outcome: outcome ?? null,
49404
+ message: line,
49405
+ statusCode: verification?.statusCode ?? null,
49406
+ latencyMs: verification?.latencyMs ?? null,
49407
+ responseExcerpt: verification?.responseExcerpt ?? null,
49408
+ state: drain?.state ?? null,
49409
+ verifiedAt: drain?.verifiedAt ?? null,
49410
+ verified,
49411
+ ...drain ? {} : {
49412
+ pending: true
49413
+ }
49414
+ });
49415
+ } else {
49416
+ const status = verification?.statusCode === void 0 ? "\u2014" : String(verification.statusCode);
49417
+ const latency = verification?.latencyMs === void 0 ? "\u2014" : `${verification.latencyMs} ms`;
49418
+ if (verified) {
49419
+ writeSuccess(`\u2705 Verified \u2014 HTTP ${status} in ${latency}`);
49420
+ writeInfo(` state ${drain?.state ?? "\u2014"} \xB7 verifiedAt ${shortIso(drain?.verifiedAt)}`);
49421
+ } else if (!drain) {
49422
+ writeInfo(`\u23F3 ${line} (attempt ${started.verificationId}, expires ${shortIso(started.expiresAt)}).`);
49423
+ writeHintBlock({
49424
+ headline: "Read it when it lands:",
49425
+ lines: [
49426
+ {
49427
+ label: "Drain",
49428
+ command: `lua drains status ${drainId}`
49429
+ }
49430
+ ],
49431
+ when: "partial"
49432
+ });
49433
+ } else {
49434
+ writeInfo(`Not verified: ${line} \u2014 HTTP ${status} in ${latency}`);
49435
+ if (verification?.responseExcerpt) writeInfo(` response: ${verification.responseExcerpt}`);
49436
+ if (outcome === "token_not_echoed") {
49437
+ writeInfo(" Your receiver answered, but did not repeat the X-Lua-Verify header on its response.");
49438
+ }
49439
+ }
49440
+ }
49441
+ if (!verified) process.exitCode = CLI_EXIT.ERROR;
49442
+ }
49443
+ __name(verifyDrainCore, "verifyDrainCore");
49444
+ async function pauseDrainCore(context, drainId, options) {
49445
+ const reason = trimmed(options.reason);
49446
+ const drain = await drainsCall(() => drainsApiFor(context).pauseDrain(drainId, reason));
49447
+ if (options.json) {
49448
+ printJson({
49449
+ drain
49450
+ });
49451
+ return;
49452
+ }
49453
+ writeSuccess(`\u2705 Paused ${drain.name} (${drain.id})`);
49454
+ writeInfo(` state ${drain.state}${reason ? ` \xB7 reason "${reason}"` : ""}`);
49455
+ writeInfo(" Records keep buffering while the drain is paused; nothing is delivered.");
49456
+ writeHintBlock({
49457
+ headline: "Next:",
49458
+ lines: [
49459
+ {
49460
+ label: "Resume",
49461
+ command: `lua drains resume ${drain.id}`
49462
+ }
49463
+ ]
49464
+ });
49465
+ }
49466
+ __name(pauseDrainCore, "pauseDrainCore");
49467
+ async function resumeDrainCore(context, drainId, options) {
49468
+ const drain = await drainsCall(() => drainsApiFor(context).resumeDrain(drainId));
49469
+ if (options.json) {
49470
+ printJson({
49471
+ drain
49472
+ });
49473
+ return;
49474
+ }
49475
+ writeSuccess(`\u2705 Resumed ${drain.name} (${drain.id})`);
49476
+ writeInfo(` state ${drain.state} \u2014 a resumed drain proves itself before it reads healthy again.`);
49477
+ writeHintBlock({
49478
+ headline: "Next:",
49479
+ lines: [
49480
+ {
49481
+ label: "Health",
49482
+ command: `lua drains status ${drain.id}`
49483
+ }
49484
+ ]
49485
+ });
49486
+ }
49487
+ __name(resumeDrainCore, "resumeDrainCore");
49488
+ async function rotateSecretCore(context, drainId, options) {
49489
+ const finalize = options.finalize === true;
49490
+ const answer = await drainsCall(() => drainsApiFor(context).rotateSecret(drainId, finalize));
49491
+ if (finalize) {
49492
+ const drain = answer;
49493
+ if (options.json) {
49494
+ printJson({
49495
+ drain
49496
+ });
49497
+ return;
49498
+ }
49499
+ writeSuccess(`\u2705 Rotation window closed for ${drain.name ?? drainId}`);
49500
+ writeInfo(" Deliveries now carry the new signature only.");
49501
+ return;
49502
+ }
49503
+ const rotated = answer;
49504
+ if (options.json) {
49505
+ printJson({
49506
+ secret: rotated.secret,
49507
+ rotation: rotated.rotation
49508
+ });
49509
+ return;
49510
+ }
49511
+ writeSuccess(`\u2705 Rotated the signing secret for ${drainId}`);
49512
+ writeInfo("");
49513
+ writeInfo("\u{1F511} New signing secret \u2014 shown once, and never again. Store it now:");
49514
+ writeInfo(` ${rotated.secret}`);
49515
+ writeInfo("");
49516
+ writeInfo(` Both secrets are accepted until ${shortIso(rotated.rotation?.expiresAt)}; every batch in that`);
49517
+ writeInfo(" window carries two signatures, so deploy the new value and then close the window.");
49518
+ writeHintBlock({
49519
+ headline: "When the receiver has the new value:",
49520
+ lines: [
49521
+ {
49522
+ label: "Close early",
49523
+ command: `lua drains rotate-secret ${drainId} --finalize`
49524
+ }
49525
+ ]
49526
+ });
49527
+ }
49528
+ __name(rotateSecretCore, "rotateSecretCore");
49529
+
49530
+ // src/commands/drains.ts
49531
+ var DRAIN_ACTIONS = [
49532
+ "list",
49533
+ "status",
49534
+ "deliveries",
49535
+ "create",
49536
+ "update",
49537
+ "delete",
49538
+ "test",
49539
+ "verify",
49540
+ "pause",
49541
+ "resume",
49542
+ "rotate-secret"
49543
+ ];
49544
+ var MISSING_SCOPE_MESSAGE = "Missing scope: logs:manage. Create a key with that scope (lua auth key) or ask an org admin.";
49545
+ var DRAINS_FAILING_EXIT = 2;
49546
+ var DELIVERY_KINDS = [
49547
+ "batch",
49548
+ "test",
49549
+ "verify",
49550
+ "heartbeat",
49551
+ "dropped",
49552
+ "truncated"
49553
+ ];
49554
+ var DEFAULT_DELIVERY_LIMIT = 20;
49555
+ var MAX_DELIVERY_LIMIT = 100;
49556
+ var MAX_TABLE_WIDTH = 100;
49557
+ async function resolveDrainsContext(options) {
49558
+ const apiKey = await requireAuth();
49559
+ const flagOrg = typeof options.org === "string" ? options.org.trim() : "";
49560
+ if (flagOrg) return {
49561
+ orgId: flagOrg,
49562
+ apiKey
49563
+ };
49564
+ const config = readYamlConfig();
49565
+ const projectOrg = config?.agent?.orgId;
49566
+ if (projectOrg) return {
49567
+ orgId: projectOrg,
49568
+ agentId: config?.agent?.agentId,
49569
+ apiKey
49570
+ };
49571
+ return {
49572
+ orgId: await orgFromCredential(apiKey),
49573
+ apiKey
49574
+ };
49575
+ }
49576
+ __name(resolveDrainsContext, "resolveDrainsContext");
49577
+ async function orgFromCredential(apiKey) {
49578
+ const { authorization } = await resolveCredentialOperationalContext(apiKey);
49579
+ const orgs = (authorization.orgs ?? []).filter((org) => !org.archived);
49580
+ if (orgs.length === 1) return orgs[0].orgId;
49581
+ if (orgs.length === 0) {
49582
+ throw CliError.usage("This credential does not reach any organization, so there is no drain to read.", "Ask an org admin for access, or run the command from a Lua project directory");
49583
+ }
49584
+ throw CliError.usage(`This credential reaches ${orgs.length} organizations \u2014 name one with --org <id>.`, joinHint(listHint("Organizations", orgs.map((org) => `${org.orgId}${org.name ? ` \u2014 ${org.name}` : ""}`)), "Inside a Lua project directory the org comes from lua.skill.yaml"));
49585
+ }
49586
+ __name(orgFromCredential, "orgFromCredential");
49587
+ function drainsApiFor(context) {
49588
+ return new DrainsApi(BASE_URLS.API, context.apiKey, context.orgId);
49589
+ }
49590
+ __name(drainsApiFor, "drainsApiFor");
49591
+ function drainRefusalMessage(error) {
49592
+ const message = error?.message || "The log drains API refused the request.";
49593
+ const code = error?.code;
49594
+ const field = error?.field;
49595
+ const line = code ? `${code}: ${message}` : message;
49596
+ return field ? `${line} (field: ${field})` : line;
49597
+ }
49598
+ __name(drainRefusalMessage, "drainRefusalMessage");
49599
+ async function drainsCall(call) {
49600
+ let response;
49601
+ try {
49602
+ response = await call();
49603
+ } catch (error) {
49604
+ if (CliError.isCliError(error) && error.statusCode === 403) {
49605
+ throw CliError.forbidden(MISSING_SCOPE_MESSAGE);
49606
+ }
49607
+ throw error;
49608
+ }
49609
+ if (response.success && response.data !== void 0) return response.data;
49610
+ if (response.error?.statusCode === 403) throw CliError.forbidden(MISSING_SCOPE_MESSAGE);
49611
+ throw CliError.fromStatus(response.error?.statusCode, drainRefusalMessage(response.error), void 0, apiErrorDetail(response.error));
49612
+ }
49613
+ __name(drainsCall, "drainsCall");
49614
+ function printJson2(payload) {
49615
+ console.log(JSON.stringify(payload, null, 2));
49616
+ }
49617
+ __name(printJson2, "printJson");
49618
+ function tableOverhead(columns) {
49619
+ return 3 * columns + 1;
49620
+ }
49621
+ __name(tableOverhead, "tableOverhead");
49622
+ function clip(value3, width) {
49623
+ if (value3.length <= width) return value3;
49624
+ return width <= 1 ? value3.slice(0, width) : `${value3.slice(0, width - 1)}\u2026`;
49625
+ }
49626
+ __name(clip, "clip");
49627
+ function renderFittedTable(headers, rows3, options = {}) {
49628
+ if (rows3.length === 0) return renderTable(headers, rows3);
49629
+ const { maxWidth = MAX_TABLE_WIDTH, shrinkFirst = [], softFloor = 12 } = options;
49630
+ const budget = maxWidth - tableOverhead(headers.length);
49631
+ const hardFloors = headers.map((header) => header.length);
49632
+ const widths = headers.map((header, i) => Math.max(header.length, ...rows3.map((row2) => (row2[i] ?? "").length)));
49633
+ let total = widths.reduce((sum, width) => sum + width, 0);
49634
+ const shrink = /* @__PURE__ */ __name((candidates, floorOf) => {
49635
+ for (; ; ) {
49636
+ if (total <= budget) return;
49637
+ let widest = -1;
49638
+ for (const i of candidates) {
49639
+ if (widths[i] <= floorOf(i)) continue;
49640
+ if (widest === -1 || widths[i] > widths[widest]) widest = i;
49641
+ }
49642
+ if (widest === -1) return;
49643
+ widths[widest] -= 1;
49644
+ total -= 1;
49645
+ }
49646
+ }, "shrink");
49647
+ shrink(shrinkFirst, (i) => Math.max(hardFloors[i], softFloor));
49648
+ shrink(shrinkFirst, (i) => hardFloors[i]);
49649
+ shrink(headers.map((_, i) => i), (i) => hardFloors[i]);
49650
+ return renderTable(headers, rows3.map((row2) => row2.map((cell, i) => clip(cell ?? "", widths[i]))));
49651
+ }
49652
+ __name(renderFittedTable, "renderFittedTable");
49653
+ function shortIso2(value3) {
49654
+ if (typeof value3 !== "string" || value3.length === 0) return "\u2014";
49655
+ const date = new Date(value3);
49656
+ if (Number.isNaN(date.getTime())) return value3;
49657
+ return date.toISOString().replace(/\.\d{3}Z$/, "Z");
49658
+ }
49659
+ __name(shortIso2, "shortIso");
49660
+ function environmentsOf(drain) {
49661
+ const environments = drain.selectors?.environments ?? [];
49662
+ return environments.length > 0 ? environments.join(",") : "\u2014";
49663
+ }
49664
+ __name(environmentsOf, "environmentsOf");
49665
+ function agentsOf(drain) {
49666
+ const agents = drain.selectors?.agents;
49667
+ if (agents === "*" || agents === void 0) return "all";
49668
+ return String(agents.length);
49669
+ }
49670
+ __name(agentsOf, "agentsOf");
49671
+ function drainsTable(drains) {
49672
+ return renderFittedTable(
49673
+ [
49674
+ "NAME",
49675
+ "TYPE",
49676
+ "STATE",
49677
+ "ENV",
49678
+ "AGENTS",
49679
+ "BACKLOG",
49680
+ "LAST SUCCESS"
49681
+ ],
49682
+ drains.map((drain) => [
49683
+ drain.name ?? "",
49684
+ drain.type ?? "",
49685
+ drain.state ?? "",
49686
+ environmentsOf(drain),
49687
+ agentsOf(drain),
49688
+ String(drain.health?.backlog ?? 0),
49689
+ shortIso2(drain.health?.lastSuccessAt)
49690
+ ]),
49691
+ {
49692
+ shrinkFirst: [
49693
+ 0,
49694
+ 3
49695
+ ]
49696
+ }
49697
+ // NAME and ENV give way before a state, a count or a timestamp
49698
+ );
49699
+ }
49700
+ __name(drainsTable, "drainsTable");
49701
+ function statusTable(drains) {
49702
+ return renderFittedTable([
49703
+ "NAME",
49704
+ "STATE",
49705
+ "BACKLOG",
49706
+ "DELIVERED",
49707
+ "DROPPED",
49708
+ "REJECTED",
49709
+ "P50 MS",
49710
+ "QUOTA",
49711
+ "LAST SUCCESS"
49712
+ ], drains.map((drain) => [
49713
+ drain.name ?? "",
49714
+ drain.state ?? "",
49715
+ String(drain.health?.backlog ?? 0),
49716
+ String(drain.health?.deliveredCount24h ?? 0),
49717
+ String(drain.health?.droppedCount24h ?? 0),
49718
+ String(drain.health?.rejectedCount24h ?? 0),
49719
+ drain.health?.p50LatencyMs === null || drain.health?.p50LatencyMs === void 0 ? "\u2014" : String(drain.health.p50LatencyMs),
49720
+ quotaRatio(drain),
49721
+ shortIso2(drain.health?.lastSuccessAt)
49722
+ ]), {
49723
+ shrinkFirst: [
49724
+ 0
49725
+ ]
49726
+ });
49727
+ }
49728
+ __name(statusTable, "statusTable");
49729
+ function quotaRatio(drain) {
49730
+ const quota = drain.quota;
49731
+ if (!quota || !quota.eventsPerDay) return "\u2014";
49732
+ const ratio = quota.eventsPerDay > 0 ? (quota.usedEvents ?? 0) / quota.eventsPerDay : 0;
49733
+ const percent = Math.round(ratio * 1e3) / 10;
49734
+ return quota.degradation && quota.degradation !== "none" ? `${percent}% ${quota.degradation}` : `${percent}%`;
49735
+ }
49736
+ __name(quotaRatio, "quotaRatio");
49737
+ function printDrainDetail(drain) {
49738
+ const health = drain.health ?? {};
49739
+ const quota = drain.quota ?? {};
49740
+ const rows3 = [
49741
+ [
49742
+ "type",
49743
+ drain.type ?? "\u2014"
49744
+ ],
49745
+ [
49746
+ "state",
49747
+ drain.state ?? "\u2014"
49748
+ ],
49749
+ [
49750
+ "endpoint",
49751
+ drain.endpoint ?? "\u2014"
49752
+ ],
49753
+ [
49754
+ "site",
49755
+ drain.site ?? "\u2014"
49756
+ ],
49757
+ [
49758
+ "format",
49759
+ drain.format ?? "\u2014"
49760
+ ],
49761
+ [
49762
+ "environments",
49763
+ environmentsOf(drain)
49764
+ ],
49765
+ [
49766
+ "agents",
49767
+ drain.selectors?.agents === "*" ? "all" : (drain.selectors?.agents ?? []).join(",") || "\u2014"
49768
+ ],
49769
+ [
49770
+ "minSeverity",
49771
+ drain.selectors?.minSeverity ?? "\u2014"
49772
+ ],
49773
+ [
49774
+ "includeContent",
49775
+ String(drain.selectors?.includeContent ?? false)
49776
+ ],
49777
+ [
49778
+ "verifiedAt",
49779
+ shortIso2(drain.verifiedAt)
49780
+ ],
49781
+ [
49782
+ "lastSuccessAt",
49783
+ shortIso2(health.lastSuccessAt)
49784
+ ],
49785
+ [
49786
+ "lastFailureAt",
49787
+ shortIso2(health.lastFailureAt)
49788
+ ],
49789
+ [
49790
+ "lastStatusCode",
49791
+ health.lastStatusCode === void 0 ? "\u2014" : String(health.lastStatusCode)
49792
+ ],
49793
+ [
49794
+ "lastError",
49795
+ health.lastError ?? "\u2014"
49796
+ ],
49797
+ [
49798
+ "backlog",
49799
+ String(health.backlog ?? 0)
49800
+ ],
49801
+ [
49802
+ "backlogBytes",
49803
+ String(health.backlogBytes ?? 0)
49804
+ ],
49805
+ [
49806
+ "deliveredCount24h",
49807
+ String(health.deliveredCount24h ?? 0)
49808
+ ],
49809
+ [
49810
+ "droppedCount24h",
49811
+ String(health.droppedCount24h ?? 0)
49812
+ ],
49813
+ [
49814
+ "rejectedCount24h",
49815
+ String(health.rejectedCount24h ?? 0)
49816
+ ],
49817
+ [
49818
+ "p50LatencyMs",
49819
+ health.p50LatencyMs === null || health.p50LatencyMs === void 0 ? "\u2014" : String(health.p50LatencyMs)
49820
+ ],
49821
+ [
49822
+ "scrubHits24h",
49823
+ String(health.scrubHits24h ?? 0)
49824
+ ],
49825
+ [
49826
+ "pauseReason",
49827
+ health.pauseReason ?? "\u2014"
49828
+ ],
49829
+ [
49830
+ "usedEvents",
49831
+ `${quota.usedEvents ?? 0} / ${quota.eventsPerDay ?? 0}`
49832
+ ],
49833
+ [
49834
+ "usedBytes",
49835
+ `${quota.usedBytes ?? 0} / ${quota.bytesPerDay ?? 0}`
49836
+ ],
49837
+ [
49838
+ "degradation",
49839
+ quota.degradation ?? "\u2014"
49840
+ ],
49841
+ [
49842
+ "resetsAt",
49843
+ shortIso2(quota.resetsAt)
49844
+ ]
49845
+ ];
49846
+ const label2 = Math.max(...rows3.map(([key]) => key.length));
49847
+ console.log(`
49848
+ \u{1F6B0} ${drain.name} (${drain.id})`);
49849
+ for (const [key, value3] of rows3) {
49850
+ console.log(` ${key.padEnd(label2)} ${clip(value3, MAX_TABLE_WIDTH - label2 - 5)}`);
49851
+ }
49852
+ console.log("");
49853
+ }
49854
+ __name(printDrainDetail, "printDrainDetail");
49855
+ function statusView(drain) {
49856
+ return {
49857
+ id: drain.id,
49858
+ name: drain.name,
49859
+ state: drain.state,
49860
+ health: drain.health,
49861
+ quota: drain.quota
49862
+ };
49863
+ }
49864
+ __name(statusView, "statusView");
49865
+ function str(value3) {
49866
+ if (value3 === void 0 || value3 === null || value3 === "") return "\u2014";
49867
+ return String(value3);
49868
+ }
49869
+ __name(str, "str");
49870
+ function deliveriesTable(deliveries) {
49871
+ return renderFittedTable(
49872
+ [
49873
+ "STARTED",
49874
+ "KIND",
49875
+ "TRY",
49876
+ "OK",
49877
+ "STATUS",
49878
+ "MS",
49879
+ "RECORDS",
49880
+ "BYTES",
49881
+ "ERROR"
49882
+ ],
49883
+ deliveries.map((delivery) => [
49884
+ shortIso2(delivery.startedAt),
49885
+ str(delivery.kind),
49886
+ str(delivery.attempt),
49887
+ delivery.ok === true ? "yes" : "no",
49888
+ str(delivery.statusCode),
49889
+ str(delivery.latencyMs),
49890
+ str(delivery.recordCount),
49891
+ str(delivery.bytes),
49892
+ str(delivery.errorClass)
49893
+ ]),
49894
+ {
49895
+ shrinkFirst: [
49896
+ 8
49897
+ ],
49898
+ softFloor: 8
49899
+ }
49900
+ // the error class gives way before the timings do
49901
+ );
49902
+ }
49903
+ __name(deliveriesTable, "deliveriesTable");
49904
+ async function listDrainsCore(context, options) {
49905
+ const payload = await drainsCall(() => drainsApiFor(context).listDrains());
49906
+ if (options.json) {
49907
+ printJson2(payload);
49908
+ return;
49909
+ }
49910
+ const drains = payload.drains ?? [];
49911
+ if (drains.length === 0) {
49912
+ writeInfo(`No log drains in ${context.orgId}.`);
49913
+ writeHintBlock({
49914
+ headline: "Send this org\u2019s logs somewhere you own:",
49915
+ lines: [
49916
+ {
49917
+ label: "Create one",
49918
+ command: "lua drains create"
49919
+ }
49920
+ ]
49921
+ });
49922
+ return;
49923
+ }
49924
+ console.log(drainsTable(drains));
49925
+ writeHintBlock({
49926
+ headline: "Next:",
49927
+ lines: [
49928
+ {
49929
+ label: "Health",
49930
+ command: "lua drains status"
49931
+ },
49932
+ {
49933
+ label: "Attempts",
49934
+ command: `lua drains deliveries ${drains[0].id}`
49935
+ }
49936
+ ]
49937
+ });
49938
+ }
49939
+ __name(listDrainsCore, "listDrainsCore");
49940
+ async function statusDrainsCore(context, drainId, options) {
49941
+ const api = drainsApiFor(context);
49942
+ const drains = drainId ? [
49943
+ await drainsCall(() => api.getDrain(drainId))
49944
+ ] : (await drainsCall(() => api.listDrains())).drains ?? [];
49945
+ if (options.json) {
49946
+ printJson2({
49947
+ drains: drains.map(statusView)
49948
+ });
49949
+ } else if (drains.length === 0) {
49950
+ writeInfo(`No log drains in ${context.orgId}.`);
49951
+ } else if (drainId) {
49952
+ printDrainDetail(drains[0]);
49953
+ } else {
49954
+ console.log(statusTable(drains));
49955
+ }
49956
+ const failing = drains.filter((drain) => drain.state === "failing");
49957
+ if (failing.length > 0) {
49958
+ if (!options.json) {
49959
+ writeInfo(`\u26A0\uFE0F ${failing.length} drain(s) failing: ${failing.map((drain) => drain.name).join(", ")}`);
49960
+ writeHintBlock({
49961
+ headline: "Why a drain is failing:",
49962
+ lines: [
49963
+ {
49964
+ label: "Attempts",
49965
+ command: `lua drains deliveries ${failing[0].id} --kind batch`
49966
+ }
49967
+ ],
49968
+ when: "partial"
49969
+ });
49970
+ }
49971
+ process.exitCode = DRAINS_FAILING_EXIT;
49972
+ }
49973
+ }
49974
+ __name(statusDrainsCore, "statusDrainsCore");
49975
+ async function deliveriesCore(context, drainId, options) {
49976
+ const limit = parseLimit(options.limit);
49977
+ const kind = parseKind(options.kind);
49978
+ const page = await drainsCall(() => drainsApiFor(context).listDeliveries(drainId, {
49979
+ limit,
49980
+ ...kind ? {
49981
+ kind
49982
+ } : {}
49983
+ }));
49984
+ if (options.json) {
49985
+ printJson2(page);
49986
+ return;
49987
+ }
49988
+ const deliveries = page.deliveries ?? [];
49989
+ if (deliveries.length === 0) {
49990
+ writeInfo(`No delivery attempts recorded for ${drainId}${kind ? ` (kind ${kind})` : ""}.`);
49991
+ return;
49992
+ }
49993
+ console.log(deliveriesTable(deliveries));
49994
+ if (page.nextCursor) {
49995
+ writeInfo(`\u2026 more attempts available \u2014 raise --limit (max ${MAX_DELIVERY_LIMIT}) or read --json for the cursor.`);
49996
+ }
49997
+ }
49998
+ __name(deliveriesCore, "deliveriesCore");
49999
+ function parseLimit(raw) {
50000
+ if (raw === void 0 || raw === null || `${raw}`.trim() === "") return DEFAULT_DELIVERY_LIMIT;
50001
+ const limit = Number(raw);
50002
+ if (!Number.isInteger(limit) || limit < 1 || limit > MAX_DELIVERY_LIMIT) {
50003
+ throw CliError.usage(`--limit must be a whole number between 1 and ${MAX_DELIVERY_LIMIT} (got "${raw}").`, `Default is ${DEFAULT_DELIVERY_LIMIT}`);
50004
+ }
50005
+ return limit;
50006
+ }
50007
+ __name(parseLimit, "parseLimit");
50008
+ function parseKind(raw) {
50009
+ if (raw === void 0 || raw === null || `${raw}`.trim() === "") return void 0;
50010
+ const kind = `${raw}`.trim().toLowerCase();
50011
+ if (!DELIVERY_KINDS.includes(kind)) {
50012
+ throw CliError.usage(`--kind "${raw}" is not a delivery kind.`, listHint("Valid values", DELIVERY_KINDS));
50013
+ }
50014
+ return kind;
50015
+ }
50016
+ __name(parseKind, "parseKind");
50017
+ async function drainsCommand(action, drainId, cmdObj) {
50018
+ return withErrorHandling(
50019
+ async () => {
50020
+ const options = cmdObj ?? {};
50021
+ const verb = action ? validateOrSuggest("drains.action", action) : "list";
50022
+ const needsId = /* @__PURE__ */ __name((v) => v === "deliveries" || v === "update" || v === "delete" || v === "test" || v === "verify" || v === "pause" || v === "resume" || v === "rotate-secret", "needsId");
50023
+ if (needsId(verb) && !drainId) {
50024
+ throw CliError.usage(`lua drains ${verb} needs a drain id.`, listHint("Run", [
50025
+ "lua drains list"
50026
+ ]));
50027
+ }
50028
+ const context = await resolveDrainsContext(options);
50029
+ switch (verb) {
50030
+ case "list":
50031
+ await listDrainsCore(context, options);
50032
+ break;
50033
+ case "status":
50034
+ await statusDrainsCore(context, drainId, options);
50035
+ break;
50036
+ case "deliveries":
50037
+ await deliveriesCore(context, drainId, options);
50038
+ break;
50039
+ case "create":
50040
+ await createDrainCore(context, options);
50041
+ break;
50042
+ case "update":
50043
+ await updateDrainCore(context, drainId, options);
50044
+ break;
50045
+ case "delete":
50046
+ await deleteDrainCore(context, drainId, options);
50047
+ break;
50048
+ case "test":
50049
+ await testDrainCore(context, drainId, options);
50050
+ break;
50051
+ case "verify":
50052
+ await verifyDrainCore(context, drainId, options);
50053
+ break;
50054
+ case "pause":
50055
+ await pauseDrainCore(context, drainId, options);
50056
+ break;
50057
+ case "resume":
50058
+ await resumeDrainCore(context, drainId, options);
50059
+ break;
50060
+ case "rotate-secret":
50061
+ await rotateSecretCore(context, drainId, options);
50062
+ break;
50063
+ default:
50064
+ throw CliError.usage(`Unknown drains action '${action}'.`, listHint("Try", DRAIN_ACTIONS));
50065
+ }
50066
+ trackEvent("cli_drains_action", {
50067
+ action: verb,
50068
+ non_interactive: !!action
50069
+ });
50070
+ },
50071
+ "drains",
50072
+ // LUA-803: a refusal that escapes a verb under `--json` is the typed envelope on stdout,
50073
+ // never the `✖` line — the one shape a script parses.
50074
+ {
50075
+ json: /* @__PURE__ */ __name(() => !!cmdObj?.json, "json")
50076
+ }
50077
+ );
50078
+ }
50079
+ __name(drainsCommand, "drainsCommand");
50080
+
48163
50081
  // src/commands/completion.ts
48164
50082
  init_cli();
48165
50083
  init_analytics();
@@ -49500,10 +51418,10 @@ function truncate2(s, n2) {
49500
51418
  return s.length <= n2 ? s : s.slice(0, Math.max(0, n2 - 1)) + "\u2026";
49501
51419
  }
49502
51420
  __name(truncate2, "truncate");
49503
- function printJson(report2) {
51421
+ function printJson3(report2) {
49504
51422
  console.log(JSON.stringify(report2));
49505
51423
  }
49506
- __name(printJson, "printJson");
51424
+ __name(printJson3, "printJson");
49507
51425
  var TICK = chalk3.green("\u2713");
49508
51426
  var WARN = chalk3.yellow("!");
49509
51427
  var CROSS = chalk3.red("\u2717");
@@ -49786,7 +51704,7 @@ async function statusCommand(options) {
49786
51704
  hints
49787
51705
  };
49788
51706
  if (options.json) {
49789
- printJson(report2);
51707
+ printJson3(report2);
49790
51708
  } else {
49791
51709
  printHuman(report2);
49792
51710
  }
@@ -53822,18 +55740,23 @@ __name(handleWatchFrame, "handleWatchFrame");
53822
55740
  async function boundaryVerdict(runId, ev, state3, boundary) {
53823
55741
  const type = ev.type;
53824
55742
  if (type === "run.gated") {
53825
- const gate = ev.data?.gate;
53826
- const stepId = ev.data?.stepId;
53827
- if (gate === "exception") {
55743
+ const raw = ev.data;
55744
+ const rawKind = raw?.kind ?? raw?.gate;
55745
+ const stepId = raw?.stepId;
55746
+ if (rawKind === "exception") {
53828
55747
  return boundary(`\u26A0\uFE0F run parked on an exception gate \u2014 a human decides next: lua workflows status ${runId}`, WORKFLOW_EXIT.RUN_PARKED);
53829
55748
  }
53830
- if (gate === "billing") {
55749
+ if (rawKind === "billing") {
53831
55750
  return boundary(`\u23F8\uFE0F run parked on a billing gate \u2014 top up the workspace; it resumes within a few minutes (right away: lua workflows retry-step ${runId} --step ${stepId ?? "<stepId>"})`, WORKFLOW_EXIT.RUN_PARKED);
53832
55751
  }
53833
- if (gate === "budget") {
55752
+ if (rawKind === "budget") {
53834
55753
  return boundary(`\u23F8\uFE0F run budget reached \u2014 lua workflows raise-budget ${runId} --credits <n> (the cap is in this run's own budget unit \u2014 credits, or actions on a seat plan; lua workflows status ${runId} says which)`, WORKFLOW_EXIT.RUN_PARKED);
53835
55754
  }
53836
- return boundary(`\u23F8\uFE0F run gated (${gate ?? "consent"}) \u2014 approve it from the desktop; then: lua workflows watch ${runId}`, WORKFLOW_EXIT.RUN_GATED);
55755
+ if (rawKind === "quota") {
55756
+ return boundary(`\u23F8\uFE0F run gated on the concurrency quota \u2014 it clears on its own as a slot frees (oldest-gated run first); check progress: lua workflows status ${runId}`, WORKFLOW_EXIT.RUN_GATED);
55757
+ }
55758
+ const label2 = rawKind === "start-consent" || rawKind === void 0 ? "consent" : rawKind;
55759
+ return boundary(`\u23F8\uFE0F run gated (${label2}) \u2014 approve it from the desktop; then: lua workflows watch ${runId}`, WORKFLOW_EXIT.RUN_GATED);
53837
55760
  }
53838
55761
  if (type === "run.suspended") {
53839
55762
  const kind = ev.data?.kind;
@@ -54554,18 +56477,18 @@ function modelEventDetail(type, data) {
54554
56477
  if (type !== "step.model_resolved" && type !== "step.model_policy_warning") return void 0;
54555
56478
  const d = data ?? {};
54556
56479
  const nested = d.detail ?? {};
54557
- const str = /* @__PURE__ */ __name((...vals) => {
56480
+ const str2 = /* @__PURE__ */ __name((...vals) => {
54558
56481
  for (const v of vals) if (typeof v === "string" && v) return v;
54559
56482
  return void 0;
54560
56483
  }, "str");
54561
- const requested = str(d.requested, nested.requested, d.modelRequested, nested.modelRequested);
54562
- const resolved = str(d.resolved, nested.resolved, d.model, nested.model);
56484
+ const requested = str2(d.requested, nested.requested, d.modelRequested, nested.modelRequested);
56485
+ const resolved = str2(d.resolved, nested.resolved, d.model, nested.model);
54563
56486
  if (type === "step.model_resolved") {
54564
- const cls = requested?.startsWith("class/") ? requested.slice("class/".length) : str(d.taskClass, nested.taskClass);
56487
+ const cls = requested?.startsWith("class/") ? requested.slice("class/".length) : str2(d.taskClass, nested.taskClass);
54565
56488
  return `model resolved \u2192 ${resolved ?? requested ?? "?"}${cls ? ` (${cls})` : ""}`;
54566
56489
  }
54567
- const outcome = str(d.outcome, nested.outcome, d.code, nested.code) ?? "unknown";
54568
- const detail = str(d.message, nested.message) ?? (requested && resolved && requested !== resolved ? `${requested} \u2192 ${resolved}` : requested ?? resolved);
56490
+ const outcome = str2(d.outcome, nested.outcome, d.code, nested.code) ?? "unknown";
56491
+ const detail = str2(d.message, nested.message) ?? (requested && resolved && requested !== resolved ? `${requested} \u2192 ${resolved}` : requested ?? resolved);
54569
56492
  return `model policy warning: ${outcome}${detail ? ` ${detail}` : ""}`;
54570
56493
  }
54571
56494
  __name(modelEventDetail, "modelEventDetail");
@@ -54961,7 +56884,7 @@ async function archiveRunsCore(ctx, o) {
54961
56884
  if (!ctx.json) console.log(line);
54962
56885
  }, "say");
54963
56886
  const sha256 = /* @__PURE__ */ __name((buf) => crypto5.createHash("sha256").update(buf).digest("hex"), "sha256");
54964
- const sleep3 = /* @__PURE__ */ __name((ms) => new Promise((r) => setTimeout(r, ms)), "sleep");
56887
+ const sleep4 = /* @__PURE__ */ __name((ms) => new Promise((r) => setTimeout(r, ms)), "sleep");
54965
56888
  const archiveOne = /* @__PURE__ */ __name(async (apiRow) => {
54966
56889
  const run = {
54967
56890
  ...apiRow,
@@ -55019,7 +56942,7 @@ async function archiveRunsCore(ctx, o) {
55019
56942
  }
55020
56943
  let dto = req.data;
55021
56944
  for (let attempt = 0; !dto || dto.status === "pending"; attempt++) {
55022
- if (attempt > 0) await sleep3(Math.min(5e3, 250 * 2 ** Math.min(attempt, 5)));
56945
+ if (attempt > 0) await sleep4(Math.min(5e3, 250 * 2 ** Math.min(attempt, 5)));
55023
56946
  if (attempt > 60) {
55024
56947
  report2.deferred.push({
55025
56948
  runId: run.id,
@@ -56520,12 +58443,8 @@ async function clearGateCore(ctx, runId, o) {
56520
58443
  if (!res.success || !res.data) return apiFailure(ctx, res, "clear-gate");
56521
58444
  emitJson(ctx, res);
56522
58445
  if (ctx.json) return WORKFLOW_EXIT.OK;
56523
- if (res.data.cleared) {
56524
- writeSuccess(`\u2705 Cleared the ${res.data.kind ?? kind} gate on ${runId}${res.data.status ? ` \u2014 ${res.data.status}` : ""}`);
56525
- console.log(` lua workflows watch ${runId}`);
56526
- } else {
56527
- writeInfo(`\u2139\uFE0F ${runId} is not parked on a ${kind} gate${res.data.reason ? ` \u2014 ${res.data.reason}` : ""}. Nothing to clear.`);
56528
- }
58446
+ writeSuccess(`\u2705 Cleared the ${res.data.kind ?? kind} gate on ${runId}${res.data.status ? ` \u2014 ${res.data.status}` : ""}`);
58447
+ console.log(` lua workflows watch ${runId}`);
56529
58448
  return WORKFLOW_EXIT.OK;
56530
58449
  }
56531
58450
  __name(clearGateCore, "clearGateCore");
@@ -56550,12 +58469,18 @@ async function recomposeCore(ctx, name, _o) {
56550
58469
  emitJson(ctx, res);
56551
58470
  if (ctx.json) return WORKFLOW_EXIT.OK;
56552
58471
  const d = res.data;
56553
- if (d.unchanged) {
58472
+ const rewrittenCount = d.rewritten?.length ?? 0;
58473
+ if (rewrittenCount === 0) {
56554
58474
  writeInfo(`\u2139\uFE0F ${wf.name} already asks for model classes everywhere it can \u2014 no new version was published.`);
56555
58475
  return WORKFLOW_EXIT.OK;
56556
58476
  }
56557
- writeSuccess(`\u2705 ${wf.name} recomposed \u2192 version ${d.version ?? d.versionId ?? "(new)"}` + (d.rewritten !== void 0 ? ` \xB7 ${d.rewritten} agent step${d.rewritten === 1 ? "" : "s"} rewritten` : ""));
56558
- for (const line of recomposeNextSteps(wf, d.version)) console.log(line);
58477
+ const saved = d.composed?.saved;
58478
+ const version = saved?.version ?? saved?.versionId ?? "(new)";
58479
+ writeSuccess(`\u2705 ${wf.name} recomposed \u2192 version ${version} \xB7 ${rewrittenCount} agent step${rewrittenCount === 1 ? "" : "s"} rewritten`);
58480
+ if (saved?.supersededBy) {
58481
+ writeInfo(`\u2139\uFE0F a concurrent recompose already holds the pin at version ${saved.supersededBy} \u2014 that is what runs.`);
58482
+ }
58483
+ for (const line of recomposeNextSteps(wf, version)) console.log(line);
56559
58484
  return WORKFLOW_EXIT.OK;
56560
58485
  }
56561
58486
  __name(recomposeCore, "recomposeCore");
@@ -58600,10 +60525,10 @@ async function templateCommand(action, options = {}) {
58600
60525
  });
58601
60526
  }
58602
60527
  __name(templateCommand, "templateCommand");
58603
- function sleep2(ms) {
60528
+ function sleep3(ms) {
58604
60529
  return new Promise((resolve7) => setTimeout(resolve7, ms));
58605
60530
  }
58606
- __name(sleep2, "sleep");
60531
+ __name(sleep3, "sleep");
58607
60532
  function missingVersionOrRethrow(error) {
58608
60533
  if (CliError.isCliError(error) && error.statusCode === 404) return null;
58609
60534
  throw error;
@@ -58647,12 +60572,12 @@ function parseEnvContract(raw) {
58647
60572
  const result = {};
58648
60573
  for (const entry of raw) {
58649
60574
  for (const pair of entry.split(/,(?=\s*[A-Za-z_][A-Za-z0-9_]*\??\s*=)/)) {
58650
- const trimmed = pair.trim();
58651
- if (!trimmed) continue;
58652
- const eqIdx = trimmed.indexOf("=");
60575
+ const trimmed2 = pair.trim();
60576
+ if (!trimmed2) continue;
60577
+ const eqIdx = trimmed2.indexOf("=");
58653
60578
  if (eqIdx === -1) continue;
58654
- let key = trimmed.slice(0, eqIdx).trim();
58655
- const description = trimmed.slice(eqIdx + 1).trim();
60579
+ let key = trimmed2.slice(0, eqIdx).trim();
60580
+ const description = trimmed2.slice(eqIdx + 1).trim();
58656
60581
  let required = true;
58657
60582
  if (key.endsWith("?")) {
58658
60583
  required = false;
@@ -59219,8 +61144,7 @@ Apply v${versionObj.version} to ${targets.length} agent(s)?`);
59219
61144
  writeProgress("\u{1F504} Starting apply run...");
59220
61145
  const { runId } = await templateApi.apply(templateId, {
59221
61146
  version: versionObj.version,
59222
- targets,
59223
- skipEnvCheck: options.skipEnvCheck
61147
+ targets
59224
61148
  });
59225
61149
  if (options.wait === false) {
59226
61150
  if (options.json) {
@@ -59239,7 +61163,7 @@ Apply v${versionObj.version} to ${targets.length} agent(s)?`);
59239
61163
  if (Date.now() > waitDeadline) {
59240
61164
  throw new Error(`Apply run ${runId} still running after 30 minutes \u2014 a crashed worker can leave a run stuck. Check later with: lua marketplace template status --template-id ${templateId}`);
59241
61165
  }
59242
- await sleep2(3e3);
61166
+ await sleep3(3e3);
59243
61167
  run = await templateApi.getApplyRun(templateId, runId);
59244
61168
  }
59245
61169
  if (options.json) {
@@ -67627,7 +69551,7 @@ function setupMarketplaceCommands(program2) {
67627
69551
  program2.command("marketplace [noun] [action]").description("\u{1F6CD}\uFE0F Browse, install, and manage marketplace skills and agent templates").option("--skill-name <name>", "Skill name (for skill list/update/uninstall)").option("--marketplace-id <id>", "Marketplace skill ID").option("--new-org-id <id>", "Destination organization ID (for skill transfer)").option("--version-id <id>", "Version ID (for skill publish/install)").option("--env-vars-json <json>", "JSON string with env var metadata (for skill publish)").option("--query <text>", "Search query (for skill search)").option("--page <n>", "Page number (for skill search)").option("--limit <n>", "Results per page (for skill search)").option("--name <name>", "Template name, an internal identifier (for template create)").option("--description <text>", "Description (for template create)").option("--template-id <id>", "Template ID").option("--window-days <n>", "Fleet-health window in days (template health, R46)").option("--source-version <n>", "Agent version to freeze into this template version (default: active) (for template publish)").option("--env-contract <pair>", "KEY=description env contract entry, repeatable; use KEY?=description for optional (for template publish)", (val, previous) => [
67628
69552
  ...previous,
67629
69553
  val
67630
- ], []).option("--skip-auto-apply", "Publish without auto-updating the installs that consented to creator updates (for template publish)").option("--version <n>", "Template version (for template view/install/apply)").option("--allow-creator-updates", "Allow the template creator to push future updates onto this install").option("--skip-env-check", "Skip env-contract validation (for template install/apply)").option("--agents <ids>", "Comma-separated target agent IDs (for template apply)").option("--file <path>", "Path to a file with one target agent ID per line (for template apply)").option("--all-installed", "Target every agent that already has this template installed (for template apply)").option("--no-wait", "Print the apply runId immediately instead of polling for completion (for template apply)").option("--display-name <name>", "Display name (for skill list/edit, template create)").option("--visibility <visibility>", "Who can see and install it: public or private (for skill list, template create)").option("--changelog <text>", "Changelog for this version (for skill/template publish)").option("--env-vars <pairs>", "Comma-separated key=value pairs (for skill/template install/update)").option("--json", "Output as JSON").option("--force", "Skip confirmation prompts").option("--yes", "Auto-confirm ONLY the template-publish consequence prompt (section clear/narrow); unlike --force it skips no other confirmation").addHelpText("after", `
69554
+ ], []).option("--skip-auto-apply", "Publish without auto-updating the installs that consented to creator updates (for template publish)").option("--version <n>", "Template version (for template view/install/apply)").option("--allow-creator-updates", "Allow the template creator to push future updates onto this install").option("--skip-env-check", "Deprecated no-op: the env-contract check is server-enforced; use `lua env` to satisfy the contract (for template install/apply)").option("--agents <ids>", "Comma-separated target agent IDs (for template apply)").option("--file <path>", "Path to a file with one target agent ID per line (for template apply)").option("--all-installed", "Target every agent that already has this template installed (for template apply)").option("--no-wait", "Print the apply runId immediately instead of polling for completion (for template apply)").option("--display-name <name>", "Display name (for skill list/edit, template create)").option("--visibility <visibility>", "Who can see and install it: public or private (for skill list, template create)").option("--changelog <text>", "Changelog for this version (for skill/template publish)").option("--env-vars <pairs>", "Comma-separated key=value pairs (for skill/template install/update)").option("--json", "Output as JSON").option("--force", "Skip confirmation prompts").option("--yes", "Auto-confirm ONLY the template-publish consequence prompt (section clear/narrow); unlike --force it skips no other confirmation").addHelpText("after", `
67631
69555
  Arguments:
67632
69556
  noun Optional: 'skill' or 'template' (prompts if not provided)
67633
69557
  action Optional: specific action for non-interactive mode
@@ -67904,9 +69828,9 @@ Examples:
67904
69828
  $ lua channels Interactive management
67905
69829
  $ lua channels list List all channels
67906
69830
  `).action(channelsCommand);
67907
- program2.command("logs").description("\u{1F4CA} View agent and skill logs").option("--type <type>", "Filter by type: all, skill, job, webhook, preprocessor, postprocessor, user_message, agent_response, mcp, mastra").option("--name <name>", "Entity name to filter (requires --type)").option("--user-id <userId>", "Filter logs by user ID").option("--agent-id <agentId>", "View logs for a specific agent ID (admin access required)").option("--limit <n>", "Number of logs to show (default: 20)").option("--page <n>", "Page number for pagination").option("--json", "Output as JSON (for scripting)").option("--direction <direction>", "Filter calls by direction (inbound | outbound) \u2014 only with --type calls").option("--status <status>", "Filter calls by status \u2014 only with --type calls").addHelpText("after", `
69831
+ program2.command("logs").description("\u{1F4CA} View agent and skill logs").option("--type <type>", "Filter by type: all, calls, or any of the 18 log sources (skill, job, webhook, trigger, preprocessor, postprocessor, user_message, agent_response, agent_error, runtime, mcp, rag, device, device-trigger, model-resolver, workflow-step, workflow-script, workflow)").option("--name <name>", "Entity name to filter (requires --type)").option("--user-id <userId>", "Filter logs by user ID").option("--agent-id <agentId>", "View logs for a specific agent ID (admin access required)").option("--limit <n>", "Number of logs to show (default: 20)").option("--page <n>", "Page number for pagination").option("--json", "Output as JSON (for scripting)").option("--direction <direction>", "Filter calls by direction (inbound | outbound) \u2014 only with --type calls").option("--status <status>", "Filter calls by status \u2014 only with --type calls").option("--since <when>", "Only logs at or after this time: an ISO 8601 instant, or 15m | 2h | 7d").option("--until <when>", "Only logs at or before this time; same formats (ends a --follow)").option("--environment <env>", "production | sandbox (a row with no environment reads as production)").option("--follow", "Stream new logs as they arrive (Ctrl-C to stop); not valid with --page").addHelpText("after", `
67908
69832
  Options:
67909
- --type <type> Filter by type: all, skill, job, webhook, preprocessor, postprocessor, user_message, agent_response, mcp, mastra, calls
69833
+ --type <type> Filter by type: all, calls, or any of the 18 log sources (skill, job, webhook, trigger, preprocessor, postprocessor, user_message, agent_response, agent_error, runtime, mcp, rag, device, device-trigger, model-resolver, workflow-step, workflow-script, workflow)
67910
69834
  --name <name> Entity name to filter (requires --type, not for message types)
67911
69835
  --user-id <userId> Filter logs by user ID
67912
69836
  --agent-id <agentId> View logs for a specific agent ID (admin access required, overrides the agent in lua.config.yaml)
@@ -67915,6 +69839,11 @@ Options:
67915
69839
  --json Output as JSON for scripting
67916
69840
  --direction <dir> Filter calls by direction (inbound | outbound) \u2014 only with --type calls
67917
69841
  --status <status> Filter calls by status \u2014 only with --type calls
69842
+ --since <when> Inclusive lower bound: an ISO 8601 instant, or a relative window (15m, 2h, 7d)
69843
+ --until <when> Inclusive upper bound, same formats; it also ends a --follow
69844
+ --environment <env> production | sandbox \u2014 a row with no environment reads as production
69845
+ --follow Poll for new logs until Ctrl-C; refuses --page, and --json emits one
69846
+ {logs, nextCursor, pagination} envelope per poll
67918
69847
 
67919
69848
  Examples:
67920
69849
  $ lua logs Interactive selection
@@ -67928,6 +69857,16 @@ Examples:
67928
69857
  $ lua logs --type calls Recent voice calls (renders as table)
67929
69858
  $ lua logs --type calls --direction outbound Outbound calls only
67930
69859
  $ lua logs --type calls --status failed --json Failed calls as JSON
69860
+ $ lua logs --since 15m The last fifteen minutes
69861
+ $ lua logs --since 2026-09-21T09:00:00Z --until 2026-09-21T10:00:00Z One hour, exactly
69862
+ $ lua logs --environment sandbox --type skill Sandbox skill logs only
69863
+ $ lua logs --follow Stream new logs until Ctrl-C
69864
+ $ lua logs --follow --since 5m --json Tail the last five minutes, as JSON
69865
+
69866
+ Notes:
69867
+ A relative --since/--until is resolved by the SERVER's clock, so a skewed machine cannot
69868
+ shift the window. --type calls reads voice calls and ignores --since/--until/--environment
69869
+ /--follow. --since is INCLUSIVE; the boundary row is de-duplicated by id while following.
67931
69870
  `).action(logsCommand);
67932
69871
  program2.command("skills [actionOrEnv] [action]").description("\u2699\uFE0F Manage and view skills").option("--skill-name <name>", "Skill name").option("--skill-version <version>", "Version for deploy action (or 'latest')").addHelpText("after", `
67933
69872
  Arguments:
@@ -67970,6 +69909,28 @@ Examples:
67970
69909
  $ lua webhooks subscribe --webhook-name myWebhook --event message.delivered Subscribe to event
67971
69910
  $ lua webhooks unsubscribe --webhook-name myWebhook --event message.delivered Unsubscribe from event
67972
69911
  `).action(webhooksCommand);
69912
+ program2.command("drains [action] [id]").description("\u{1F6B0} Manage log drains (stream this organization's logs to your own destination)").option("--json", "Output as JSON").option("--yes", "Skip confirmation prompts (required with --include-content in CI)").option("--org <id>", "The organization to act on (default: the project, or the key when it reaches one org)").option("--name <name>", "Drain name (1-64 chars, unique per org)").option("--type <type>", "http | otlp | datadog | betterstack").option("--endpoint <url>", "Destination URL (http and otlp only; https, public host)").option("--site <site>", "Preset site/region, e.g. datadoghq.eu").option("--format <format>", "json | ndjson").option("--header <name>", "Custom header NAME (the value is prompted)", collect, []).option("--header-from-env <name=ENV_VAR>", "Custom header, value read from an env var (CI form)", collect, []).option("--sources <list>", "Comma-separated source selectors (default: the recommended set)").option("--min-severity <level>", "debug | info | warn | error (default info)").option("--agents <list>", "'all' or a comma-separated list of agent ids").option("--environments <list>", "production,sandbox (default production)").option("--sampling <spec>", "Head sampling, e.g. debug=0.1").option("--include-content", "Include user messages and agent responses (prints an acknowledgement)").option("--no-include-content", "update: stop sending message bodies (the drain keeps its sources)").option("--limit <n>", "deliveries: rows to return (max 100)").option("--kind <kind>", "deliveries: batch | test | verify | heartbeat | dropped | truncated").option("--reason <text>", "pause: why").option("--finalize", "rotate-secret: close an open rotation window early").addHelpText("after", `
69913
+ Arguments:
69914
+ action Optional: list (default), status, deliveries, create, update, delete,
69915
+ test, verify, pause, resume, rotate-secret
69916
+ id The drain id, for every action but list, status and create
69917
+
69918
+ Examples:
69919
+ $ lua drains List this org's drains
69920
+ $ lua drains status --json Health + quota (exit 2 if any is failing)
69921
+ $ lua drains create --name prod-siem --type http --endpoint https://logs.example.com/lua
69922
+ $ lua drains verify drn_... Prove you own the endpoint
69923
+ $ lua drains deliveries drn_... --limit 20 --kind batch Recent delivery attempts
69924
+ $ lua drains rotate-secret drn_... New signing secret (shown once)
69925
+ $ lua drains rotate-secret drn_... --finalize Close the rotation window
69926
+ $ lua drains pause drn_... --reason "noisy vendor" Stop delivering (buffering continues)
69927
+ $ lua drains update drn_... --no-include-content Stop sending message bodies
69928
+
69929
+ Notes:
69930
+ Secrets are never flags. --header takes a NAME and prompts for the value; in --ci mode use
69931
+ --header-from-env DD_API_KEY=DATADOG_KEY. The HMAC signing secret is minted by the server and
69932
+ printed exactly once, at create and at rotate-secret.
69933
+ `).action(drainsCommand);
67973
69934
  program2.command("devices [action]").description("\u{1F4E1} Manage connected devices").option("--device-name <name>", "Device name").option("--group <group>", "Filter by device group").option("--payload <json>", "JSON payload for test/test-trigger", "{}").option("--timeout <ms>", "Timeout in milliseconds", "30000").option("--force", "Skip confirmation prompts").addHelpText("after", `
67974
69935
  Arguments:
67975
69936
  action Optional: list, status, enable, disable, remove, test, test-trigger
@@ -68433,7 +70394,7 @@ __name(visibleLength, "visibleLength");
68433
70394
  function printBanner(opts) {
68434
70395
  if (process.env.LUA_NO_BANNER) return;
68435
70396
  if (!process.stdout.isTTY && !process.env.LUA_FORCE_BANNER) return;
68436
- if (process.env.NO_COLOR) {
70397
+ if (noColorEnabled()) {
68437
70398
  process.stdout.write(`
68438
70399
  lua-cli v${opts.version} \xB7 Build and deploy AI agents with superpowers
68439
70400
 
@@ -68456,6 +70417,7 @@ __name(printBanner, "printBanner");
68456
70417
  // src/index.ts
68457
70418
  init_package_root();
68458
70419
  var CLI_VERSION = getCliVersion();
70420
+ configureChalkColor(chalk5);
68459
70421
  var args = process.argv.slice(2);
68460
70422
  var isBareInvocation = args.length === 0;
68461
70423
  var isHelpInvocation = args.includes("--help") || args.includes("-h");