lua-cli 3.32.4 → 3.32.6

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
@@ -65,6 +65,16 @@ var init_auth_error = __esm({
65
65
  });
66
66
 
67
67
  // src/errors/cli.error.ts
68
+ function apiErrorDetail(error) {
69
+ return {
70
+ serverCode: error?.code,
71
+ issues: error?.issues,
72
+ upstream: error?.upstream,
73
+ requestId: error?.requestId,
74
+ vendor: error?.vendor,
75
+ retryAfterSeconds: error?.retryAfterSeconds
76
+ };
77
+ }
68
78
  function isTypedCliError(error) {
69
79
  return CliError.isCliError(error) || AuthenticationError.isAuthenticationError(error);
70
80
  }
@@ -93,6 +103,17 @@ function debugEnabled() {
93
103
  const v = process.env.LUA_DEBUG;
94
104
  return debugFlag || v === "1" || v === "true" || v === "yes";
95
105
  }
106
+ function upstreamUnavailableHint(upstream, requestId) {
107
+ const service = typeof upstream === "string" && upstream ? `its ${upstream} service` : "a service behind it";
108
+ const ref = typeof requestId === "string" && requestId ? ` If it persists, quote request ${requestId}.` : "";
109
+ return `The Lua API is up, but ${service} is temporarily unavailable (503 UPSTREAM_UNAVAILABLE) \u2014 retry in a moment.${ref}`;
110
+ }
111
+ function vendorUnavailableHint(vendor, requestId, retryAfterSeconds) {
112
+ const name = typeof vendor === "string" && vendor ? VENDOR_LABELS[vendor] ?? vendor : "a vendor it depends on";
113
+ const ref = typeof requestId === "string" && requestId ? ` If it persists, quote request ${requestId}.` : "";
114
+ const retry = typeof retryAfterSeconds === "number" ? "retry in a moment" : "the request may have applied at the vendor \u2014 check before retrying";
115
+ return `The Lua API is up, but ${name} is temporarily unavailable (503 VENDOR_UNAVAILABLE) \u2014 ${retry}.${ref}`;
116
+ }
96
117
  function authHint(error) {
97
118
  if (error.suppressDefaultRemediation) return void 0;
98
119
  if (error.reason === "no_agent_access") {
@@ -116,7 +137,10 @@ function classifyCliError(error) {
116
137
  code: error.code,
117
138
  exitCode: error.exitCode,
118
139
  message: error.message,
119
- hint: error.hint
140
+ hint: error.hint,
141
+ statusCode: error.statusCode,
142
+ serverCode: error.serverCode,
143
+ issues: error.issues
120
144
  };
121
145
  }
122
146
  if (AuthenticationError.isAuthenticationError(error)) {
@@ -138,30 +162,36 @@ function classifyCliError(error) {
138
162
  }
139
163
  const status = numericStatus(e);
140
164
  if (status !== void 0) {
165
+ const statusCode = status;
141
166
  if (status === 401) return {
142
167
  code: "auth",
143
168
  exitCode: CLI_EXIT.AUTH,
144
- message
169
+ message,
170
+ statusCode
145
171
  };
146
172
  if (status === 403) return {
147
173
  code: "forbidden",
148
174
  exitCode: CLI_EXIT.FORBIDDEN,
149
- message
175
+ message,
176
+ statusCode
150
177
  };
151
178
  if (status === 404) return {
152
179
  code: "not_found",
153
180
  exitCode: CLI_EXIT.NOT_FOUND,
154
- message
181
+ message,
182
+ statusCode
155
183
  };
156
184
  if (status >= 400 && status < 500) return {
157
185
  code: `http_${status}`,
158
186
  exitCode: CLI_EXIT.FORBIDDEN,
159
- message
187
+ message,
188
+ statusCode
160
189
  };
161
190
  if (status >= 500 || status === 0) return {
162
191
  code: "unavailable",
163
192
  exitCode: CLI_EXIT.UNAVAILABLE,
164
- message
193
+ message,
194
+ statusCode
165
195
  };
166
196
  }
167
197
  const causeCode = e.cause?.code;
@@ -179,9 +209,18 @@ function classifyCliError(error) {
179
209
  message
180
210
  };
181
211
  }
212
+ function issueLines(issues) {
213
+ return (issues ?? []).map((issue) => {
214
+ const where = issue.path !== void 0 ? `${issue.path || "/"}: ` : "";
215
+ const what = issue.message ?? issue.code ?? "invalid";
216
+ const tag = issue.message && issue.code ? ` (${issue.code})` : "";
217
+ return ` \u2022 ${where}${what}${tag}`;
218
+ });
219
+ }
182
220
  function renderCliError(reported, options = {}) {
183
221
  const lines = [
184
- `\u2716 ${reported.code}: ${reported.message}`
222
+ `\u2716 ${reported.code}: ${reported.message}`,
223
+ ...issueLines(reported.issues)
185
224
  ];
186
225
  for (const hint of [
187
226
  reported.hint,
@@ -193,9 +232,30 @@ function renderCliError(reported, options = {}) {
193
232
  if (options.stack) lines.push("", options.stack);
194
233
  return lines;
195
234
  }
235
+ function cliErrorEnvelope(error) {
236
+ const reported = classifyCliError(error);
237
+ return {
238
+ success: false,
239
+ error: {
240
+ code: reported.serverCode ?? reported.code,
241
+ ...reported.statusCode !== void 0 ? {
242
+ statusCode: reported.statusCode
243
+ } : {},
244
+ message: reported.message,
245
+ ...reported.issues?.length ? {
246
+ issues: reported.issues
247
+ } : {}
248
+ }
249
+ };
250
+ }
196
251
  function reportCliError(error, options = {}) {
197
252
  const reported = classifyCliError(error);
198
253
  const stack = debugEnabled() && error instanceof Error ? error.stack : void 0;
254
+ if (options.json) {
255
+ console.log(JSON.stringify(cliErrorEnvelope(error), null, 2));
256
+ if (stack) console.error(stack);
257
+ return reported;
258
+ }
199
259
  for (const line of renderCliError(reported, {
200
260
  extraHint: options.extraHint,
201
261
  stack
@@ -224,7 +284,7 @@ function reportUnhandledCliError(error) {
224
284
  process.exitCode = reported.exitCode;
225
285
  return reported.exitCode;
226
286
  }
227
- var CLI_EXIT, CliError, HandledCliError, debugFlag, NETWORK_ERRNO, NETWORK_MESSAGE, UNAVAILABLE_HINT, CLI_EXIT_CODE_HELP;
287
+ var CLI_EXIT, CliError, HandledCliError, debugFlag, NETWORK_ERRNO, NETWORK_MESSAGE, UNAVAILABLE_HINT, VENDOR_LABELS, CLI_EXIT_CODE_HELP;
228
288
  var init_cli_error = __esm({
229
289
  "src/errors/cli.error.ts"() {
230
290
  "use strict";
@@ -238,6 +298,7 @@ var init_cli_error = __esm({
238
298
  FORBIDDEN: 10,
239
299
  UNAVAILABLE: 11
240
300
  };
301
+ __name(apiErrorDetail, "apiErrorDetail");
241
302
  CliError = class _CliError extends Error {
242
303
  static {
243
304
  __name(this, "CliError");
@@ -247,6 +308,8 @@ var init_cli_error = __esm({
247
308
  exitCode;
248
309
  hint;
249
310
  statusCode;
311
+ serverCode;
312
+ issues;
250
313
  constructor(code, message, options = {}) {
251
314
  super(message);
252
315
  this.name = "CliError";
@@ -254,6 +317,8 @@ var init_cli_error = __esm({
254
317
  this.exitCode = options.exitCode ?? CLI_EXIT.ERROR;
255
318
  this.hint = options.hint;
256
319
  this.statusCode = options.statusCode;
320
+ this.serverCode = options.serverCode;
321
+ this.issues = options.issues?.length ? options.issues : void 0;
257
322
  if (Error.captureStackTrace) Error.captureStackTrace(this, _CliError);
258
323
  }
259
324
  /** Bad arguments, an unknown action, no project — exit 2. */
@@ -282,19 +347,23 @@ var init_cli_error = __esm({
282
347
  /**
283
348
  * An API refusal the site already holds the status of (LUA-766) — classified by the same table the top-level
284
349
  * classifier applies to an untyped error: 401 auth · 403 forbidden · 404 not_found · other 4xx `http_<status>`
285
- * (10) · 5xx / 0 unavailable (11, with the network hint unless the site gives its own) · no status `error` (1).
350
+ * (10) · 5xx / 0 unavailable (11, with the network hint unless the site gives its own or the body's code
351
+ * picks one: a 503 UPSTREAM_UNAVAILABLE names the Lua service behind the API, LUA-810) · no status `error` (1).
286
352
  * A command that reads `response.error.statusCode` throws through here, so `lua logs` on a 503 exits 11 like
287
353
  * every other verb instead of printing the message itself and then throwing an exit-1 `Error`.
288
354
  */
289
- static fromStatus(statusCode, message, hint) {
355
+ static fromStatus(statusCode, message, hint, detail = {}) {
290
356
  const reported = classifyCliError(Object.assign(new Error(message), {
291
357
  statusCode
292
358
  }));
359
+ const codeHint = detail.serverCode === "UPSTREAM_UNAVAILABLE" ? upstreamUnavailableHint(detail.upstream, detail.requestId) : detail.serverCode === "VENDOR_UNAVAILABLE" ? vendorUnavailableHint(detail.vendor, detail.requestId, detail.retryAfterSeconds) : void 0;
293
360
  const classHint = reported.exitCode === CLI_EXIT.UNAVAILABLE ? UNAVAILABLE_HINT : reported.hint;
294
361
  return new _CliError(reported.code, message, {
295
362
  exitCode: reported.exitCode,
296
- hint: hint ?? classHint,
297
- statusCode
363
+ hint: hint ?? codeHint ?? classHint,
364
+ statusCode,
365
+ serverCode: detail.serverCode,
366
+ issues: detail.issues
298
367
  });
299
368
  }
300
369
  static isCliError(error) {
@@ -340,10 +409,20 @@ var init_cli_error = __esm({
340
409
  ]);
341
410
  NETWORK_MESSAGE = /fetch failed|socket hang up|network request failed|request timeout|ECONNREFUSED|ENOTFOUND/i;
342
411
  UNAVAILABLE_HINT = "The Lua API could not be reached \u2014 check your network and https://status.heylua.ai, then retry.";
412
+ __name(upstreamUnavailableHint, "upstreamUnavailableHint");
413
+ VENDOR_LABELS = {
414
+ unified: "Unified.to",
415
+ github: "GitHub",
416
+ pusher: "Pusher",
417
+ google: "Google"
418
+ };
419
+ __name(vendorUnavailableHint, "vendorUnavailableHint");
343
420
  __name(authHint, "authHint");
344
421
  __name(numericStatus, "numericStatus");
345
422
  __name(classifyCliError, "classifyCliError");
423
+ __name(issueLines, "issueLines");
346
424
  __name(renderCliError, "renderCliError");
425
+ __name(cliErrorEnvelope, "cliErrorEnvelope");
347
426
  __name(reportCliError, "reportCliError");
348
427
  __name(commanderExitCode, "commanderExitCode");
349
428
  __name(reportUnhandledCliError, "reportUnhandledCliError");
@@ -1066,6 +1145,18 @@ function modelUnresolvedMessage(r) {
1066
1145
  }
1067
1146
  return r.candidates.length ? `model "${r.requested}" does not resolve to an approved model \u2014 accepted forms: ${r.candidates.join(", ")}` : `model "${r.requested}" does not resolve to an approved model \u2014 accepted forms are the registry's provider-prefixed ids (provider/model) or a bare id that names exactly one of them`;
1068
1147
  }
1148
+ function providerModelId(code) {
1149
+ const requested = typeof code === "string" ? code.trim() : "";
1150
+ if (!requested || isModelIdSentinel(requested)) return requested;
1151
+ const slash = requested.indexOf("/");
1152
+ if (slash <= 0) return requested;
1153
+ const provider = requested.slice(0, slash).toLowerCase();
1154
+ if (MODEL_ID_BYOK_PROVIDERS.includes(provider)) return requested;
1155
+ return requested.slice(slash + 1);
1156
+ }
1157
+ function providerModelFamily(code) {
1158
+ return providerModelId(code).toLowerCase().replace(MODEL_SNAPSHOT_SUFFIX, "");
1159
+ }
1069
1160
  function isImplicitModelSelectionSource(source) {
1070
1161
  return source !== void 0 && IMPLICIT_MODEL_SELECTION_SOURCES.includes(source);
1071
1162
  }
@@ -1179,6 +1270,11 @@ function isDeviceCredentialPrincipal(context) {
1179
1270
  function hasDeviceCredentialType(value3) {
1180
1271
  return DeviceCredentialClaimSchema.safeParse(value3).success;
1181
1272
  }
1273
+ function sessionAuthTime(context) {
1274
+ if (!context || context.credential.type !== "firstPartySession") return void 0;
1275
+ const authTime = context.authTime;
1276
+ return typeof authTime === "number" && Number.isInteger(authTime) && authTime >= 0 && authTime <= SESSION_AUTH_TIME_MAX_S ? authTime : void 0;
1277
+ }
1182
1278
  function isTypedApiKeyPrincipal(context) {
1183
1279
  return context?.subject.subjectType === "apiKey" && context.credential.type === "apiKey" && !context.compatibility;
1184
1280
  }
@@ -1351,6 +1447,39 @@ function scheduledWorkflowRunId(jobId, scheduledTime) {
1351
1447
  const key = typeof scheduledTime === "number" ? String(scheduledTime) : scheduledTimeKey(scheduledTime);
1352
1448
  return `${WORKFLOW_SCHEDULED_RUN_ID_PREFIX}${jobId}_${key}`;
1353
1449
  }
1450
+ function renderWorkflowScheduleKeyTemplate(template3, ctx) {
1451
+ if (!template3) return void 0;
1452
+ const read = /* @__PURE__ */ __name2((path25) => path25.split(".").reduce((o, k) => o && typeof o === "object" ? o[k] : void 0, ctx.input), "read");
1453
+ let unresolved = false;
1454
+ const out = template3.replace(/\$\{\s*([a-zA-Z0-9_.]+)\s*\}/g, (_m, expr) => {
1455
+ let v = "";
1456
+ if (expr === "scheduledTime") v = ctx.scheduledTime ?? "";
1457
+ else if (expr.startsWith("input.")) v = read(expr.slice("input.".length));
1458
+ const s = v === void 0 || v === null ? "" : String(v);
1459
+ if (s === "") unresolved = true;
1460
+ return s;
1461
+ });
1462
+ if (unresolved) return void 0;
1463
+ const key = out.slice(0, WORKFLOW_SCHEDULE_KEY_MAX);
1464
+ return key && /^[A-Za-z0-9:_\-.\/]+$/.test(key) ? key : void 0;
1465
+ }
1466
+ function scheduledWorkflowIdempotencyKey(jobId, rendered) {
1467
+ const head = `${WORKFLOW_SCHEDULE_IDEMPOTENCY_KEY_PREFIX}${jobId}:`;
1468
+ if (head.length + rendered.length <= WORKFLOW_SCHEDULE_KEY_MAX) return `${head}${rendered}`;
1469
+ const digest = stableKeyDigest(rendered);
1470
+ const room = WORKFLOW_SCHEDULE_KEY_MAX - head.length - digest.length - 1;
1471
+ return `${head}${rendered.slice(0, Math.max(0, room))}~${digest}`;
1472
+ }
1473
+ function stableKeyDigest(s) {
1474
+ let a = 2166136261;
1475
+ let b = 84696351;
1476
+ for (let i = 0; i < s.length; i++) {
1477
+ const c = s.charCodeAt(i);
1478
+ a = Math.imul(a ^ c, 16777619);
1479
+ b = Math.imul(b ^ c, 16777619) ^ b >>> 13;
1480
+ }
1481
+ return (a >>> 0).toString(16).padStart(8, "0") + (b >>> 0).toString(16).padStart(8, "0");
1482
+ }
1354
1483
  function workflowOperationId(runId, stepId, billingEpoch) {
1355
1484
  return `${WORKFLOW_OPERATION_ID_PREFIX}${runId}:${stepId}:${billingEpoch}`;
1356
1485
  }
@@ -1654,7 +1783,59 @@ function extractSingleJsonValue(text) {
1654
1783
  };
1655
1784
  }
1656
1785
  }
1657
- var __defProp2, __name2, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, MCP_TOOL_READ_VERB_RE, MCP_DRAFT_CREATE_VERBS, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, DESKTOP_FILE_COMMANDS, DESKTOP_FILE_COMMAND_SET, MODEL_ID_BYOK_PROVIDERS, 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, TEMPLATE_TRIGGER_URL_ENV_PREFIX, SUBJECT_TYPES, SubjectTypeSchema, CREDENTIAL_TYPES, CredentialTypeSchema, DEVICE_OPERATIONS, DeviceOperationSchema, DEVICE_SCOPE_BY_OPERATION, DeviceBindingSchema, TYPED_API_KEY, IdSchema, 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, 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_STEP_STATUSES, WORKFLOW_STEP_IN_FLIGHT, 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_OPERATION_ID_PREFIX, WORKFLOW_JOURNAL_PROTOCOL_VERSION, WORKFLOW_CONNECTION_KEY_RE, WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES, 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_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, 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;
1786
+ function agentFeatureCatalogDefault(featureName) {
1787
+ return DEFAULT_ON_AGENT_FEATURES.includes(featureName);
1788
+ }
1789
+ function hasExplicitFeatureActive(row2) {
1790
+ return typeof row2?.active === "boolean";
1791
+ }
1792
+ function effectiveFeatureActive(row2, catalogDefault) {
1793
+ return hasExplicitFeatureActive(row2) ? row2.active : catalogDefault;
1794
+ }
1795
+ function resolveEffectiveFeature(row2, catalogDefault) {
1796
+ return hasExplicitFeatureActive(row2) ? {
1797
+ active: row2.active,
1798
+ source: "agent",
1799
+ default: catalogDefault
1800
+ } : {
1801
+ active: catalogDefault,
1802
+ source: "default",
1803
+ default: catalogDefault
1804
+ };
1805
+ }
1806
+ function isFeatureRow(value3) {
1807
+ return typeof value3 === "object" && value3 !== null;
1808
+ }
1809
+ function effectiveAgentFeatureRows(base, override) {
1810
+ const merged = /* @__PURE__ */ new Map();
1811
+ for (const [name, row2] of Object.entries(base ?? {})) {
1812
+ if (isFeatureRow(row2)) merged.set(name, {
1813
+ row: row2,
1814
+ origin: "baseAgent"
1815
+ });
1816
+ }
1817
+ for (const [name, row2] of Object.entries(override ?? {})) {
1818
+ if (isFeatureRow(row2)) merged.set(name, {
1819
+ row: row2,
1820
+ origin: "subAgent"
1821
+ });
1822
+ }
1823
+ return {
1824
+ rows: Object.fromEntries([
1825
+ ...merged
1826
+ ].map(([name, e]) => [
1827
+ name,
1828
+ e.row
1829
+ ])),
1830
+ origins: Object.fromEntries([
1831
+ ...merged
1832
+ ].map(([name, e]) => [
1833
+ name,
1834
+ e.origin
1835
+ ]))
1836
+ };
1837
+ }
1838
+ var __defProp2, __name2, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, MCP_TOOL_READ_VERB_RE, MCP_DRAFT_CREATE_VERBS, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, DESKTOP_FILE_COMMANDS, DESKTOP_FILE_COMMAND_SET, MODEL_ID_BYOK_PROVIDERS, 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, TEMPLATE_TRIGGER_URL_ENV_PREFIX, 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, 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_STEP_STATUSES, WORKFLOW_STEP_IN_FLIGHT, 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_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES, 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_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, 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;
1658
1839
  var init_dist = __esm({
1659
1840
  "../shared-types/dist/index.mjs"() {
1660
1841
  "use strict";
@@ -1972,6 +2153,11 @@ var init_dist = __esm({
1972
2153
  __name2(normalizeModelId, "normalizeModelId");
1973
2154
  __name(modelUnresolvedMessage, "modelUnresolvedMessage");
1974
2155
  __name2(modelUnresolvedMessage, "modelUnresolvedMessage");
2156
+ __name(providerModelId, "providerModelId");
2157
+ __name2(providerModelId, "providerModelId");
2158
+ MODEL_SNAPSHOT_SUFFIX = /(?:[-@](?:19|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])|-(?:19|20)\d{2}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01]))$/;
2159
+ __name(providerModelFamily, "providerModelFamily");
2160
+ __name2(providerModelFamily, "providerModelFamily");
1975
2161
  REASONING_EFFORT_VALUES = [
1976
2162
  "off",
1977
2163
  "minimal",
@@ -2431,6 +2617,7 @@ This text is who you are for this person. As you learn them, their name, their w
2431
2617
  });
2432
2618
  TYPED_API_KEY = /^api_([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\.([A-Za-z0-9_-]{43})$/;
2433
2619
  IdSchema = z2.string().min(1).max(256);
2620
+ SESSION_AUTH_TIME_MAX_S = 4102444800;
2434
2621
  PrincipalDescriptorSchema = z2.object({
2435
2622
  subjectType: SubjectTypeSchema,
2436
2623
  subjectId: IdSchema
@@ -2479,7 +2666,8 @@ This text is who you are for this person. As you learn them, their name, their w
2479
2666
  owner: PrincipalOwnerSchema.optional(),
2480
2667
  compatibility: z2.object({
2481
2668
  mode: z2.literal("legacy-owner-delegation")
2482
- }).strict().optional()
2669
+ }).strict().optional(),
2670
+ authTime: z2.number().int().nonnegative().max(SESSION_AUTH_TIME_MAX_S).optional()
2483
2671
  }).strict();
2484
2672
  DeviceCredentialPrincipalContextSchema = z2.object({
2485
2673
  version: z2.literal(1),
@@ -2535,6 +2723,8 @@ This text is who you are for this person. As you learn them, their name, their w
2535
2723
  }).passthrough();
2536
2724
  __name(hasDeviceCredentialType, "hasDeviceCredentialType");
2537
2725
  __name2(hasDeviceCredentialType, "hasDeviceCredentialType");
2726
+ __name(sessionAuthTime, "sessionAuthTime");
2727
+ __name2(sessionAuthTime, "sessionAuthTime");
2538
2728
  __name(isTypedApiKeyPrincipal, "isTypedApiKeyPrincipal");
2539
2729
  __name2(isTypedApiKeyPrincipal, "isTypedApiKeyPrincipal");
2540
2730
  __name(typedApiKeyPrincipalId, "typedApiKeyPrincipalId");
@@ -2766,6 +2956,14 @@ This text is who you are for this person. As you learn them, their name, their w
2766
2956
  __name2(isScheduledWorkflowRunId, "isScheduledWorkflowRunId");
2767
2957
  __name(scheduledWorkflowRunId, "scheduledWorkflowRunId");
2768
2958
  __name2(scheduledWorkflowRunId, "scheduledWorkflowRunId");
2959
+ WORKFLOW_SCHEDULE_KEY_MAX = 128;
2960
+ __name(renderWorkflowScheduleKeyTemplate, "renderWorkflowScheduleKeyTemplate");
2961
+ __name2(renderWorkflowScheduleKeyTemplate, "renderWorkflowScheduleKeyTemplate");
2962
+ WORKFLOW_SCHEDULE_IDEMPOTENCY_KEY_PREFIX = "sched:";
2963
+ __name(scheduledWorkflowIdempotencyKey, "scheduledWorkflowIdempotencyKey");
2964
+ __name2(scheduledWorkflowIdempotencyKey, "scheduledWorkflowIdempotencyKey");
2965
+ __name(stableKeyDigest, "stableKeyDigest");
2966
+ __name2(stableKeyDigest, "stableKeyDigest");
2769
2967
  WORKFLOW_OPERATION_ID_PREFIX = "wf:";
2770
2968
  __name(workflowOperationId, "workflowOperationId");
2771
2969
  __name2(workflowOperationId, "workflowOperationId");
@@ -3212,6 +3410,23 @@ listed here; never invent a target.`;
3212
3410
  JSON_FENCE_RE = /```(?:json)?[ \t]*\r?\n([\s\S]*?)\r?\n?```/g;
3213
3411
  __name(extractSingleJsonValue, "extractSingleJsonValue");
3214
3412
  __name2(extractSingleJsonValue, "extractSingleJsonValue");
3413
+ DEFAULT_ON_AGENT_FEATURES = [
3414
+ "workflows",
3415
+ "workflowCompose",
3416
+ "observationalMemory"
3417
+ ];
3418
+ __name(agentFeatureCatalogDefault, "agentFeatureCatalogDefault");
3419
+ __name2(agentFeatureCatalogDefault, "agentFeatureCatalogDefault");
3420
+ __name(hasExplicitFeatureActive, "hasExplicitFeatureActive");
3421
+ __name2(hasExplicitFeatureActive, "hasExplicitFeatureActive");
3422
+ __name(effectiveFeatureActive, "effectiveFeatureActive");
3423
+ __name2(effectiveFeatureActive, "effectiveFeatureActive");
3424
+ __name(resolveEffectiveFeature, "resolveEffectiveFeature");
3425
+ __name2(resolveEffectiveFeature, "resolveEffectiveFeature");
3426
+ __name(isFeatureRow, "isFeatureRow");
3427
+ __name2(isFeatureRow, "isFeatureRow");
3428
+ __name(effectiveAgentFeatureRows, "effectiveAgentFeatureRows");
3429
+ __name2(effectiveAgentFeatureRows, "effectiveAgentFeatureRows");
3215
3430
  }
3216
3431
  });
3217
3432
 
@@ -4098,7 +4313,7 @@ function fillHitl(node) {
4098
4313
  if (a.onTimeout === void 0) a.onTimeout = "deny";
4099
4314
  if (a.onDeny === void 0) a.onDeny = "continue";
4100
4315
  if (a.excludeInitiator === void 0) a.excludeInitiator = false;
4101
- if (a.editable === void 0) a.editable = false;
4316
+ if (a.editable === void 0) a.editable = approvalEditable(a);
4102
4317
  return;
4103
4318
  }
4104
4319
  const w = node;
@@ -4342,7 +4557,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
4342
4557
  const id = singleId(node);
4343
4558
  const unknown = unknownWorkflowRetryMembers(r);
4344
4559
  if (unknown.length) err("invalid-envelope", workflowRetryUnknownMembersMessage(unknown), `${path25}.retry`, id);
4345
- if (r.maxAttempts !== void 0 && !isWithinWorkflowRetryAttempts(r.maxAttempts)) {
4560
+ if (!isWithinWorkflowRetryAttempts(r.maxAttempts)) {
4346
4561
  const over = typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS;
4347
4562
  err(over ? "cap-exceeded" : "invalid-envelope", workflowRetryMaxAttemptsMessage(r.maxAttempts), `${path25}.retry.maxAttempts`, id);
4348
4563
  }
@@ -4580,7 +4795,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
4580
4795
  }
4581
4796
  const a = node;
4582
4797
  checkId(a.id, path25);
4583
- if (a.approver === "creator" && a.excludeInitiator === true) {
4798
+ if ((a.approver ?? "creator") === "creator" && a.excludeInitiator === true) {
4584
4799
  err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path25, a.id);
4585
4800
  }
4586
4801
  const editable = approvalEditable(a);
@@ -5883,6 +6098,7 @@ function runErrorIssues(issues) {
5883
6098
  function runNextAction(run) {
5884
6099
  if (isTerminalRunStatus(run.status)) return "none";
5885
6100
  if (run.status === "suspended" && run.gate?.kind === "budget") return "raise_budget";
6101
+ if (run.status === "suspended" && run.gate?.kind === "billing") return "top_up";
5886
6102
  if (!run.cancel?.requestedAt) return "none";
5887
6103
  const forceAt = run.cancel.forceAfter ?? run.cancel.requestedAt + FORCE_CANCEL_STALE_MS;
5888
6104
  return Date.now() >= forceAt ? "force" : "cancel_again";
@@ -5910,6 +6126,12 @@ function runCountsFromStepStatuses(statuses) {
5910
6126
  for (const s of statuses) tally[s] = (tally[s] ?? 0) + 1;
5911
6127
  return runCountsFromStatusTally(tally);
5912
6128
  }
6129
+ function isBillingHeldStep(row2) {
6130
+ return row2.status === "ready" && row2.billingHold === true;
6131
+ }
6132
+ function stepEffectiveStatus(row2) {
6133
+ return isBillingHeldStep(row2) ? "suspended" : row2.status;
6134
+ }
5913
6135
  function runCounts(counts) {
5914
6136
  const c = counts ?? {};
5915
6137
  const rawInFlight = c.dispatched !== void 0 || c.claimed !== void 0 || c.running !== void 0 || c.cancellation_requested !== void 0;
@@ -6596,6 +6818,53 @@ function rebaseItemPointer(pointer, itemsPath, index) {
6596
6818
  const base = `/${itemsPath.split(".").map(escapePointer).join("/")}/${index}`;
6597
6819
  return pointer === "/" || pointer === "" ? base : `${base}${pointer}`;
6598
6820
  }
6821
+ function validateWorkflowSchedule(schedule, path25 = "/schedule") {
6822
+ if (schedule === void 0 || schedule === null) return [];
6823
+ const issue = /* @__PURE__ */ __name3((at, detail) => [
6824
+ {
6825
+ code: WORKFLOW_SCHEDULE_SHAPE_ISSUE,
6826
+ severity: "error",
6827
+ path: at,
6828
+ message: `${detail} \u2014 ${WORKFLOW_SCHEDULE_SHAPES_HINT}`
6829
+ }
6830
+ ], "issue");
6831
+ if (!isObject(schedule)) {
6832
+ return issue(path25, `\`schedule\` is ${Array.isArray(schedule) ? "an array" : `a ${typeof schedule}`}, not a typed schedule object`);
6833
+ }
6834
+ const type = schedule.type;
6835
+ if (type === void 0) {
6836
+ const keys = Object.keys(schedule);
6837
+ const seen = keys.length ? ` (got { ${keys.join(", ")} })` : " (got {})";
6838
+ return issue(path25, `\`schedule\` carries no \`type\` discriminator${seen}`);
6839
+ }
6840
+ if (typeof type !== "string" || !WORKFLOW_SCHEDULE_TYPES.includes(type)) {
6841
+ return issue(path25, `\`schedule.type\` ${JSON.stringify(type)} is not one of ${WORKFLOW_SCHEDULE_TYPES.map((t) => `'${t}'`).join(" | ")}`);
6842
+ }
6843
+ switch (type) {
6844
+ case "cron": {
6845
+ if (typeof schedule.expression !== "string" || schedule.expression.trim().length === 0) {
6846
+ return issue(`${path25}/expression`, "a { type: 'cron' } schedule needs a non-empty string `expression`");
6847
+ }
6848
+ if (schedule.timezone !== void 0 && (typeof schedule.timezone !== "string" || schedule.timezone.length === 0)) {
6849
+ return issue(`${path25}/timezone`, "a { type: 'cron' } schedule's `timezone`, when given, is a non-empty IANA string");
6850
+ }
6851
+ return [];
6852
+ }
6853
+ case "interval": {
6854
+ const s = schedule.seconds;
6855
+ if (typeof s !== "number" || !Number.isFinite(s) || s <= 0) {
6856
+ return issue(`${path25}/seconds`, "a { type: 'interval' } schedule needs a positive number `seconds`");
6857
+ }
6858
+ return [];
6859
+ }
6860
+ case "once": {
6861
+ if (typeof schedule.executeAt !== "string" || Number.isNaN(Date.parse(schedule.executeAt))) {
6862
+ return issue(`${path25}/executeAt`, "a { type: 'once' } schedule needs an ISO-8601 string `executeAt`");
6863
+ }
6864
+ return [];
6865
+ }
6866
+ }
6867
+ }
6599
6868
  function collectEnvTemplateKeys(value22) {
6600
6869
  const keys = /* @__PURE__ */ new Set();
6601
6870
  const walk22 = /* @__PURE__ */ __name3((v) => {
@@ -6885,7 +7154,7 @@ function needsInheritedWorkspace(graph) {
6885
7154
  }
6886
7155
  return false;
6887
7156
  }
6888
- var __defProp3, __name3, WorkflowTemplateError, TEMPLATE_PLACEHOLDER, TEMPLATE_NAMESPACES, MAP_DESCRIPTOR_KEYS, MAP_MEMBER_MALFORMED_CODE, fromInit, fromStep, value, template, fromRequest, rows, fromKnowledge, SideEffectsSchema, JobResourcesSchema, APPROVER_SPEC_MAX_USERS, ESCALATION_MAX_HOPS, TemplateBindingSchema, ApproverSpecSchema, FourEyesSchema, EscalationHopSchema, TerminalOutcomeSchema, ApprovalOnTimeoutSchema, APPROVER_SPEC_SHAPES, APPROVER_WRITTEN_MAX, USER_ID_SHAPED_RE, BINDING_ROOTS, WORKSPACE_TEMPLATE_EXPR_RE, SLEEP_UNTIL_REPLACEMENT, WORKFLOW_CAPS_DEFAULT, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_FOREACH_DEFAULT_CONCURRENCY, WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS, WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS, WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS, WORKFLOW_SIGNAL_DEFAULT_SOURCES, clone, CONNECTION_ID_HEX_RE, WORKFLOW_JOB_TOOLS, WORKFLOW_JOB_MAX_WORKTREE_ARMS, workspaceOf, mountsWorkspace, isJobTier, jobToolsOf, schemaIsArray, isHitlNode, isSingleStep, singleId, armId, TEMPLATE_STEP_REF, EDITABLE_PATH_RE, PREDICATE_OPS, isPredicateScalar, GRAPH_HASH_PREFIX, WorkflowPlanError, isArmStep, armStepId, armStepKind, joinIdOf, containerIdOf, PATH_PLACEHOLDER, MISSING, stepIdOf, cmp, eq, ne, gt, gte, lt, lte, inSet, notIn, exists, notExists, truthy, falsy, and, or, not, CONTINUED_FAILURE_TAG, CONTINUED_FAILURE_DEFAULT_CODE, CONTINUED_FAILURE_OUTPUT_SCHEMA, CONTINUED_FAILURE_LEAF_PATHS, isHitlNode2, nodeIdOf, GOAL_JUDGE_STEP_ID, NON_LEAF_KINDS, CONDITIONAL_JOIN_ID, branchArmId, canonical, sortKeys, JOIN, entryOfJoin, FORCE_CANCEL_STALE_MS, TERMINAL, WORKFLOW_INLINE_RUN_TAG, RUN_ERROR_ISSUES_MAX, IN_FLIGHT, n, STEP_ERROR_DETAIL_KEYS, STEP_ERROR_DETAIL_MAX_BYTES, DETAIL_MAX_DEPTH, DETAIL_MAX_ITEMS, MAX_HOLIDAYS, MAX_WALK_DAYS, HHMM, YMD, MS_PER_MIN, MS_PER_DAY, MON_FRI, supportedTz, fmtCache, WEEKDAYS, JSON_PATCH_OPS, JSON_PATCH_MAX_OPS, JSON_PATCH_MAX_VALUE_BYTES, JSON_PATCH_MAX_TOTAL_BYTES, SEGMENT_RE, WORKFLOW_ENV_OVERLAY_MAX_KEYS, WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES, WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE, isEnvRef, looksLikeEmbeddedJson, ZERO, isRecord2;
7157
+ var __defProp3, __name3, WorkflowTemplateError, TEMPLATE_PLACEHOLDER, TEMPLATE_NAMESPACES, MAP_DESCRIPTOR_KEYS, MAP_MEMBER_MALFORMED_CODE, fromInit, fromStep, value, template, fromRequest, rows, fromKnowledge, SideEffectsSchema, JobResourcesSchema, APPROVER_SPEC_MAX_USERS, ESCALATION_MAX_HOPS, TemplateBindingSchema, ApproverSpecSchema, FourEyesSchema, EscalationHopSchema, TerminalOutcomeSchema, ApprovalOnTimeoutSchema, APPROVER_SPEC_SHAPES, APPROVER_WRITTEN_MAX, USER_ID_SHAPED_RE, BINDING_ROOTS, WORKSPACE_TEMPLATE_EXPR_RE, SLEEP_UNTIL_REPLACEMENT, WORKFLOW_CAPS_DEFAULT, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_FOREACH_DEFAULT_CONCURRENCY, WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS, WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS, WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS, WORKFLOW_SIGNAL_DEFAULT_SOURCES, clone, CONNECTION_ID_HEX_RE, WORKFLOW_JOB_TOOLS, WORKFLOW_JOB_MAX_WORKTREE_ARMS, workspaceOf, mountsWorkspace, isJobTier, jobToolsOf, schemaIsArray, isHitlNode, isSingleStep, singleId, armId, TEMPLATE_STEP_REF, EDITABLE_PATH_RE, PREDICATE_OPS, isPredicateScalar, GRAPH_HASH_PREFIX, WorkflowPlanError, isArmStep, armStepId, armStepKind, joinIdOf, containerIdOf, PATH_PLACEHOLDER, MISSING, stepIdOf, cmp, eq, ne, gt, gte, lt, lte, inSet, notIn, exists, notExists, truthy, falsy, and, or, not, CONTINUED_FAILURE_TAG, CONTINUED_FAILURE_DEFAULT_CODE, CONTINUED_FAILURE_OUTPUT_SCHEMA, CONTINUED_FAILURE_LEAF_PATHS, isHitlNode2, nodeIdOf, GOAL_JUDGE_STEP_ID, NON_LEAF_KINDS, CONDITIONAL_JOIN_ID, branchArmId, canonical, sortKeys, JOIN, entryOfJoin, FORCE_CANCEL_STALE_MS, TERMINAL, WORKFLOW_INLINE_RUN_TAG, RUN_ERROR_ISSUES_MAX, IN_FLIGHT, n, STEP_ERROR_DETAIL_KEYS, STEP_ERROR_DETAIL_MAX_BYTES, DETAIL_MAX_DEPTH, DETAIL_MAX_ITEMS, MAX_HOLIDAYS, MAX_WALK_DAYS, HHMM, YMD, MS_PER_MIN, MS_PER_DAY, MON_FRI, supportedTz, fmtCache, WEEKDAYS, JSON_PATCH_OPS, JSON_PATCH_MAX_OPS, JSON_PATCH_MAX_VALUE_BYTES, JSON_PATCH_MAX_TOTAL_BYTES, SEGMENT_RE, WORKFLOW_SCHEDULE_TYPES, WORKFLOW_SCHEDULE_SHAPE_ISSUE, WORKFLOW_SCHEDULE_SHAPES_HINT, isObject, WORKFLOW_ENV_OVERLAY_MAX_KEYS, WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES, WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE, isEnvRef, looksLikeEmbeddedJson, ZERO, isRecord2;
6889
7158
  var init_dist2 = __esm({
6890
7159
  "../workflow-graph/dist/index.mjs"() {
6891
7160
  "use strict";
@@ -7440,6 +7709,10 @@ var init_dist2 = __esm({
7440
7709
  __name3(runCountsFromStatusTally, "runCountsFromStatusTally");
7441
7710
  __name(runCountsFromStepStatuses, "runCountsFromStepStatuses");
7442
7711
  __name3(runCountsFromStepStatuses, "runCountsFromStepStatuses");
7712
+ __name(isBillingHeldStep, "isBillingHeldStep");
7713
+ __name3(isBillingHeldStep, "isBillingHeldStep");
7714
+ __name(stepEffectiveStatus, "stepEffectiveStatus");
7715
+ __name3(stepEffectiveStatus, "stepEffectiveStatus");
7443
7716
  n = /* @__PURE__ */ __name3((v) => typeof v === "number" && Number.isFinite(v) ? v : 0, "n");
7444
7717
  __name(runCounts, "runCounts");
7445
7718
  __name3(runCounts, "runCounts");
@@ -7591,6 +7864,16 @@ var init_dist2 = __esm({
7591
7864
  __name3(applyJsonPatch, "applyJsonPatch");
7592
7865
  __name(rebaseItemPointer, "rebaseItemPointer");
7593
7866
  __name3(rebaseItemPointer, "rebaseItemPointer");
7867
+ WORKFLOW_SCHEDULE_TYPES = [
7868
+ "cron",
7869
+ "interval",
7870
+ "once"
7871
+ ];
7872
+ WORKFLOW_SCHEDULE_SHAPE_ISSUE = "schedule-shape-invalid";
7873
+ WORKFLOW_SCHEDULE_SHAPES_HINT = "`schedule` must be one of { type: 'cron', expression: '<5-field cron>', timezone?: '<IANA tz>' } | { type: 'interval', seconds: <n> } | { type: 'once', executeAt: '<ISO-8601>' }";
7874
+ isObject = /* @__PURE__ */ __name3((v) => typeof v === "object" && v !== null && !Array.isArray(v), "isObject");
7875
+ __name(validateWorkflowSchedule, "validateWorkflowSchedule");
7876
+ __name3(validateWorkflowSchedule, "validateWorkflowSchedule");
7594
7877
  WORKFLOW_ENV_OVERLAY_MAX_KEYS = 64;
7595
7878
  WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES = 4096;
7596
7879
  WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE = /(SECRET|TOKEN|KEY|PASSWORD)$/;
@@ -7860,7 +8143,7 @@ var init_workflow = __esm({
7860
8143
  }, "assertPredicate");
7861
8144
  assertRetry = /* @__PURE__ */ __name((r, id) => {
7862
8145
  if (!r) return;
7863
- if (r.maxAttempts !== void 0 && !isWithinWorkflowRetryAttempts(r.maxAttempts)) {
8146
+ if (!isWithinWorkflowRetryAttempts(r.maxAttempts)) {
7864
8147
  const over = typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS;
7865
8148
  throw new LuaWorkflowBuildError(over ? "cap-exceeded" : "invalid-envelope", `"${id}": ${workflowRetryMaxAttemptsMessage(r.maxAttempts)}`);
7866
8149
  }
@@ -8381,9 +8664,12 @@ var init_workflow = __esm({
8381
8664
  if (opts.approver === "creator" && opts.excludeInitiator === true) {
8382
8665
  throw new LuaWorkflowBuildError("approver-excludes-only-candidate", `"${id}": approver:'creator' with excludeInitiator:true always excludes the only candidate`);
8383
8666
  }
8384
- if (opts.fourEyes !== void 0 && opts.editable !== true) throw new LuaWorkflowBuildError("four-eyes-requires-editable", `"${id}": fourEyes requires editable:true`);
8385
- if ((opts.editablePaths !== void 0 || opts.editedPayloadSchema !== void 0) && opts.editable !== true) {
8386
- throw new LuaWorkflowBuildError("editable-path-invalid", `"${id}": editablePaths / editedPayloadSchema require editable:true`);
8667
+ const editable = approvalEditable(opts);
8668
+ if (opts.fourEyes !== void 0 && !editable) throw new LuaWorkflowBuildError("four-eyes-requires-editable", `"${id}": \`fourEyes\` requires editable:true`);
8669
+ if (opts.editable === false && Array.isArray(opts.editablePaths) && opts.editablePaths.length > 0) {
8670
+ throw new LuaWorkflowBuildError("editable-path-invalid", `"${id}": \`editablePaths\` beside editable:false is contradictory \u2014 drop the paths or set editable:true`);
8671
+ } else if ((opts.editablePaths !== void 0 || opts.editedPayloadSchema !== void 0) && !editable) {
8672
+ throw new LuaWorkflowBuildError("editable-path-invalid", `"${id}": \`editablePaths\` / \`editedPayloadSchema\` require editable:true (a non-empty editablePaths implies it)`);
8387
8673
  }
8388
8674
  for (const p of opts.editablePaths ?? []) {
8389
8675
  if (!EDITABLE_PATH_RE2.test(p)) throw new LuaWorkflowBuildError("editable-path-invalid", `"${id}": editablePaths entry "${p}" is outside the grammar seg(.seg)* with [*]/[n] selectors`);
@@ -8719,6 +9005,71 @@ var init_types = __esm({
8719
9005
 
8720
9006
  // src/api/http.client.ts
8721
9007
  import { randomUUID as randomUUID3 } from "crypto";
9008
+ async function classifyErrorResponse(response) {
9009
+ let errorData;
9010
+ try {
9011
+ errorData = await response.json();
9012
+ } catch (jsonError) {
9013
+ errorData = {};
9014
+ }
9015
+ if (response.status === 401) {
9016
+ const serverMessage = typeof errorData.message === "string" ? errorData.message : void 0;
9017
+ if (serverMessage && /not an admin/i.test(serverMessage)) {
9018
+ throw new AuthenticationError(`Access denied for this agent: ${serverMessage}`, "no_agent_access", serverMessage);
9019
+ }
9020
+ const isExplicitCredential = !!serverMessage && /(invalid|expired|missing|no)\s+(api[\s_-]?key|token|credential)/i.test(serverMessage);
9021
+ const isBareAuthRejection = !serverMessage || /^unauthorized$/i.test(serverMessage);
9022
+ if (isExplicitCredential || isBareAuthRejection) {
9023
+ throw new AuthenticationError("Authentication failed. Your Lua credential may be invalid or expired.", "invalid_credentials", serverMessage);
9024
+ }
9025
+ throw new AuthenticationError(`Authentication failed: ${serverMessage}`, "unknown", serverMessage);
9026
+ }
9027
+ if (response.status === 403) {
9028
+ const detail = errorData.message || "You do not have permission to access this resource.";
9029
+ const serverCode = serverCodeOf(errorData.code, errorData.error);
9030
+ throw new CliError("forbidden", `Access denied (403): ${detail}${serverCode ? ` (${serverCode})` : ""}`, {
9031
+ exitCode: CLI_EXIT.FORBIDDEN,
9032
+ statusCode: 403,
9033
+ serverCode,
9034
+ issues: Array.isArray(errorData.issues) ? errorData.issues : void 0,
9035
+ hint: "Check that your Lua login has access to this agent or organization."
9036
+ });
9037
+ }
9038
+ return {
9039
+ success: false,
9040
+ error: {
9041
+ message: errorData.message || `HTTP ${response.status}: ${response.statusText}`,
9042
+ statusCode: response.status,
9043
+ error: errorData.error,
9044
+ retryAfterSeconds: parseRetryAfter(response.headers.get("retry-after")),
9045
+ ...errorData
9046
+ }
9047
+ };
9048
+ }
9049
+ function serverCodeOf(code, error) {
9050
+ if (typeof code === "string" && code.length > 0) return code;
9051
+ if (typeof error === "string" && /^[A-Z0-9][A-Z0-9_]*$/.test(error)) return error;
9052
+ return void 0;
9053
+ }
9054
+ async function refusalFromResponse(response) {
9055
+ const { error } = await classifyErrorResponse(response);
9056
+ const serverCode = serverCodeOf(error?.code, error?.error);
9057
+ return CliError.fromStatus(
9058
+ error?.statusCode ?? response.status,
9059
+ error?.message ?? `HTTP ${response.status}: ${response.statusText}`,
9060
+ void 0,
9061
+ // LUA-810: `fromStatus` picks the UPSTREAM_UNAVAILABLE hint from the body's `upstream` / `requestId`.
9062
+ // LUA-812: and the VENDOR_UNAVAILABLE hint from `vendor` / `retryAfterSeconds` (whether a blind retry is safe).
9063
+ {
9064
+ serverCode,
9065
+ issues: error?.issues,
9066
+ upstream: error?.upstream,
9067
+ requestId: error?.requestId,
9068
+ vendor: error?.vendor,
9069
+ retryAfterSeconds: error?.retryAfterSeconds
9070
+ }
9071
+ );
9072
+ }
8722
9073
  async function* parseSseStream(body, signal) {
8723
9074
  const reader = body.getReader();
8724
9075
  const decoder = new TextDecoder();
@@ -8899,42 +9250,7 @@ var init_http_client = __esm({
8899
9250
  * @private
8900
9251
  */
8901
9252
  async classifyErrorResponse(response) {
8902
- let errorData;
8903
- try {
8904
- errorData = await response.json();
8905
- } catch (jsonError) {
8906
- errorData = {};
8907
- }
8908
- if (response.status === 401) {
8909
- const serverMessage = typeof errorData.message === "string" ? errorData.message : void 0;
8910
- if (serverMessage && /not an admin/i.test(serverMessage)) {
8911
- throw new AuthenticationError(`Access denied for this agent: ${serverMessage}`, "no_agent_access", serverMessage);
8912
- }
8913
- const isExplicitCredential = !!serverMessage && /(invalid|expired|missing|no)\s+(api[\s_-]?key|token|credential)/i.test(serverMessage);
8914
- const isBareAuthRejection = !serverMessage || /^unauthorized$/i.test(serverMessage);
8915
- if (isExplicitCredential || isBareAuthRejection) {
8916
- throw new AuthenticationError("Authentication failed. Your Lua credential may be invalid or expired.", "invalid_credentials", serverMessage);
8917
- }
8918
- throw new AuthenticationError(`Authentication failed: ${serverMessage}`, "unknown", serverMessage);
8919
- }
8920
- if (response.status === 403) {
8921
- const detail = errorData.message || "You do not have permission to access this resource.";
8922
- throw new CliError("forbidden", `Access denied (403): ${detail}`, {
8923
- exitCode: CLI_EXIT.FORBIDDEN,
8924
- statusCode: 403,
8925
- hint: "Check that your Lua login has access to this agent or organization."
8926
- });
8927
- }
8928
- return {
8929
- success: false,
8930
- error: {
8931
- message: errorData.message || `HTTP ${response.status}: ${response.statusText}`,
8932
- statusCode: response.status,
8933
- error: errorData.error,
8934
- retryAfterSeconds: parseRetryAfter(response.headers.get("retry-after")),
8935
- ...errorData
8936
- }
8937
- };
9253
+ return classifyErrorResponse(response);
8938
9254
  }
8939
9255
  /**
8940
9256
  * Checks if an HTTP status code is retryable
@@ -8958,6 +9274,21 @@ var init_http_client = __esm({
8958
9274
  return Math.max(100, Math.random() * exponential);
8959
9275
  }
8960
9276
  /**
9277
+ * The wait before the next attempt: the client's jittered exponential backoff, floored by the server's
9278
+ * `retryAfterSeconds` on a 429 (the limiter's word is final) and on an idempotent read (GET / HEAD). LUA-810: a
9279
+ * POST / PUT / PATCH / DELETE that met a 5xx keeps the client's own backoff — every 503 body carries
9280
+ * `retryAfterSeconds: 5` (`CONTROL_UNAVAILABLE`, `UPSTREAM_UNAVAILABLE`), which floored all three waits at 5 s:
9281
+ * a ≥15 s stall on a write that may already have landed, and retrying an ambiguous write harder does not make
9282
+ * it less ambiguous. The client's own schedule is ≤1 s + ≤2 s + ≤4 s.
9283
+ */
9284
+ retryDelayMs(attempt, error, method) {
9285
+ const own = this.calculateBackoff(attempt);
9286
+ const advised = Number(error?.retryAfterSeconds ?? 0) * 1e3;
9287
+ const verb = (method ?? "GET").toUpperCase();
9288
+ const honourAdvice = error?.statusCode === 429 || verb === "GET" || verb === "HEAD";
9289
+ return honourAdvice ? Math.max(own, advised) : own;
9290
+ }
9291
+ /**
8961
9292
  * Wraps request with retry logic for transient failures
8962
9293
  * @param url - The full URL to request
8963
9294
  * @param options - Fetch API request options
@@ -8991,8 +9322,7 @@ var init_http_client = __esm({
8991
9322
  throw error;
8992
9323
  }
8993
9324
  if (attempt < maxRetries) {
8994
- const serverDelay = Number(lastResult?.error?.retryAfterSeconds ?? 0) * 1e3;
8995
- const backoff = Math.max(this.calculateBackoff(attempt), serverDelay);
9325
+ const backoff = this.retryDelayMs(attempt, lastResult?.error, options.method);
8996
9326
  await new Promise((resolve7) => setTimeout(resolve7, backoff));
8997
9327
  }
8998
9328
  }
@@ -9142,6 +9472,9 @@ var init_http_client = __esm({
9142
9472
  };
9143
9473
  }
9144
9474
  };
9475
+ __name(classifyErrorResponse, "classifyErrorResponse");
9476
+ __name(serverCodeOf, "serverCodeOf");
9477
+ __name(refusalFromResponse, "refusalFromResponse");
9145
9478
  __name(parseSseStream, "parseSseStream");
9146
9479
  __name(parseRetryAfter, "parseRetryAfter");
9147
9480
  __name(isCoreDrainApiError, "isCoreDrainApiError");
@@ -11568,8 +11901,14 @@ async function withErrorHandling(commandFn, commandName, opts) {
11568
11901
  } catch {
11569
11902
  }
11570
11903
  }
11904
+ let json = false;
11905
+ try {
11906
+ json = typeof opts?.json === "function" ? !!opts.json() : !!opts?.json;
11907
+ } catch {
11908
+ }
11571
11909
  const reported = reportCliError(error, {
11572
- extraHint
11910
+ extraHint,
11911
+ json
11573
11912
  });
11574
11913
  process.exitCode = reported.exitCode;
11575
11914
  await showUpdateWarningIfNeeded(versionCheckPromise);
@@ -25850,6 +26189,7 @@ var init_job_api_service = __esm({
25850
26189
  "src/api/job.api.service.ts"() {
25851
26190
  "use strict";
25852
26191
  init_http_client();
26192
+ init_cli_error();
25853
26193
  init_job_instance();
25854
26194
  JobApi = class extends HttpClient {
25855
26195
  static {
@@ -25906,7 +26246,7 @@ var init_job_api_service = __esm({
25906
26246
  if (response.success && response.data) {
25907
26247
  return new JobInstance(this, response.data);
25908
26248
  }
25909
- throw new Error(response.error?.message || "Failed to get job");
26249
+ throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Failed to get job", void 0, apiErrorDetail(response.error));
25910
26250
  }
25911
26251
  /**
25912
26252
  * Creates a new job for the agent.
@@ -27381,7 +27721,7 @@ async function fetchApprovedModelsOrThrow(apiKey, agentId, orgId) {
27381
27721
  const agentApi = new AgentApi(BASE_URLS.API, apiKey);
27382
27722
  const result = await agentApi.getApprovedModels(void 0, agentId, orgId);
27383
27723
  if (!result.success) {
27384
- throw CliError.fromStatus(result.error?.statusCode, `Could not fetch models from the server: ${result.error?.message || "Unknown error"}`);
27724
+ throw CliError.fromStatus(result.error?.statusCode, `Could not fetch models from the server: ${result.error?.message || "Unknown error"}`, void 0, apiErrorDetail(result.error));
27385
27725
  }
27386
27726
  return result.data ?? [];
27387
27727
  }
@@ -29752,7 +30092,7 @@ async function handleAgentSwitch(credential, organizations, apiKey, existingYaml
29752
30092
  });
29753
30093
  writeSuccess("\u2705 lua.skill.yaml updated successfully!");
29754
30094
  writeSuccess("\u2705 LuaAgent configuration updated!");
29755
- writeInfo("\n\u{1F4A1} Your project now uses the new agent. Run 'lua compile' to update your skills.\n");
30095
+ writeInfo("\n\u{1F4A1} Your project now uses the new agent. Run 'lua sync' (or 'lua push all') to reconcile your skills.\n");
29756
30096
  trackEvent("cli_init_agent_switched", {
29757
30097
  model_selected: !!selectedModel,
29758
30098
  model: selectedModel
@@ -30137,7 +30477,9 @@ async function syncCommand(options) {
30137
30477
  let syncCompletedSuccessfully = false;
30138
30478
  try {
30139
30479
  writeProgress("\u{1F504} Compiling to get latest local state...");
30140
- await compileCommand();
30480
+ await compileCommand({
30481
+ serverSync: true
30482
+ });
30141
30483
  writeProgress("\u{1F504} Checking for drift between server and local code...");
30142
30484
  const { agentId, apiKey } = await initializeCommand({
30143
30485
  showProgress: false
@@ -34877,7 +35219,8 @@ var WorkflowHandler = class extends BaseVersionedHandler {
34877
35219
  success: response.success,
34878
35220
  error: response.error?.message,
34879
35221
  statusCode: response.error?.statusCode,
34880
- code: response.error?.code ?? response.error?.error
35222
+ code: response.error?.code ?? response.error?.error,
35223
+ issues: response.error?.issues
34881
35224
  };
34882
35225
  }
34883
35226
  /**
@@ -35083,7 +35426,7 @@ async function compileCommand(options) {
35083
35426
  const debugMode = options?.debug || debugEnabled();
35084
35427
  const verboseMode = options?.verbose || debugMode;
35085
35428
  const doSync = options?.sync === true;
35086
- const doServerSync = options?.serverSync !== false;
35429
+ const doServerSync = options?.serverSync === true;
35087
35430
  if (debugMode) {
35088
35431
  console.log("\u{1F41B} Debug mode enabled");
35089
35432
  }
@@ -35180,7 +35523,7 @@ async function compileCommand(options) {
35180
35523
  let syncConfig = readYamlConfig();
35181
35524
  const agentId = syncConfig?.agent?.agentId;
35182
35525
  if (!doServerSync) {
35183
- writeInfo("\u2139\uFE0F Server sync skipped (offline verb) \u2014 `lua push` when you are ready to publish.");
35526
+ writeInfo("\u2139\uFE0F Server sync skipped \u2014 nothing was sent to the server; `lua push` publishes.");
35184
35527
  } else if (apiKey && agentId) {
35185
35528
  writeProgress("\u{1F504} Syncing with server...");
35186
35529
  const fetchResults = await Promise.all(syncableHandlers.map((h) => h.fetchServerState(apiKey, agentId)));
@@ -38458,7 +38801,8 @@ async function pushBackupCommand(options = {}) {
38458
38801
  }
38459
38802
  writeInfo("No compilation output found. Running compile first...");
38460
38803
  await compileCommand({
38461
- sync: false
38804
+ sync: false,
38805
+ serverSync: true
38462
38806
  });
38463
38807
  }
38464
38808
  writeProgress("Pushing backup...");
@@ -39156,12 +39500,24 @@ function formatPushFailureSummary(failedItems) {
39156
39500
  return lines.join("\n");
39157
39501
  }
39158
39502
  __name(formatPushFailureSummary, "formatPushFailureSummary");
39503
+ function pushRefusalError(handler, name, result) {
39504
+ const reason = result.error || "Unknown error";
39505
+ const isVersionConflict = reason.toLowerCase().includes("already exists");
39506
+ return new CliError("error", `${handler.displayName.toLowerCase()} "${name}" is not pushed: ${reason}`, {
39507
+ exitCode: CLI_EXIT.ERROR,
39508
+ statusCode: result.statusCode,
39509
+ serverCode: result.code,
39510
+ issues: result.issues,
39511
+ hint: isVersionConflict ? `Version conflict \u2014 this version already exists on the server. Auto-bump: lua push ${handler.kind} --force \xB7 or push all: lua push all --force` : void 0
39512
+ });
39513
+ }
39514
+ __name(pushRefusalError, "pushRefusalError");
39159
39515
  function stalePushEntryError(handler, name, entityId, serverMessage) {
39160
39516
  const field = handler.yamlConfig.idField;
39161
39517
  return new CliError("not_found", `${handler.displayName} "${name}" (${entityId}) no longer exists on the server${serverMessage ? ` \u2014 ${serverMessage}` : ""}`, {
39162
39518
  exitCode: CLI_EXIT.NOT_FOUND,
39163
39519
  statusCode: 404,
39164
- hint: `lua.skill.yaml still holds ${field}: ${entityId} for "${name}". Run \`lua compile\` \u2014 the server sync clears a deleted id and registers the definition afresh \u2014 then push again; or remove the ${field} line by hand.`
39520
+ hint: `lua.skill.yaml still holds ${field}: ${entityId} for "${name}". Remove that ${field}: line from lua.skill.yaml, then run \`lua push ${handler.displayName}\` again \u2014 its compile step registers the definition afresh and writes the new id.`
39165
39521
  });
39166
39522
  }
39167
39523
  __name(stalePushEntryError, "stalePushEntryError");
@@ -39310,7 +39666,9 @@ __name(shouldDeployAfterPush, "shouldDeployAfterPush");
39310
39666
  async function pushVersionedPrimitive(handler, options = {}) {
39311
39667
  try {
39312
39668
  writeProgress("\u{1F4E6} Compiling project...");
39313
- await compileCommand();
39669
+ await compileCommand({
39670
+ serverSync: true
39671
+ });
39314
39672
  writeSuccess("\u2705 Compilation complete");
39315
39673
  const apiKey = await authenticateOrFail();
39316
39674
  const config = readYamlConfig();
@@ -39332,7 +39690,7 @@ async function pushVersionedPrimitive(handler, options = {}) {
39332
39690
  });
39333
39691
  const entityId = selected[handler.yamlConfig.idField];
39334
39692
  if (!entityId) {
39335
- throw new Error(`${handler.displayName} "${selected.name}" has no server ID. Run 'lua compile' first.`);
39693
+ throw new Error(`${handler.displayName} "${selected.name}" has no server ID \u2014 the create above failed. Fix it and run 'lua push ${handler.displayName}' again.`);
39336
39694
  }
39337
39695
  const manifest = loadManifest();
39338
39696
  const bundleAccumulator = /* @__PURE__ */ new Map();
@@ -39363,24 +39721,7 @@ async function pushVersionedPrimitive(handler, options = {}) {
39363
39721
  });
39364
39722
  if (!result.success) {
39365
39723
  if (result.statusCode === 404) throw stalePushEntryError(handler, selected.name, entityId, result.error);
39366
- const isVersionConflict = result.error?.toLowerCase().includes("already exists");
39367
- if (isVersionConflict) {
39368
- writeHintBlock({
39369
- headline: "Version conflict \u2014 this version already exists on the server.",
39370
- lines: [
39371
- {
39372
- label: "Auto-bump:",
39373
- command: `lua push ${handler.kind} --force`
39374
- },
39375
- {
39376
- label: "Or push all:",
39377
- command: "lua push all --force"
39378
- }
39379
- ],
39380
- when: "error"
39381
- });
39382
- }
39383
- throw new Error(`Push Error: ${result.error || "Unknown error"}`);
39724
+ throw pushRefusalError(handler, selected.name, result);
39384
39725
  }
39385
39726
  writeSuccess(`
39386
39727
  \u2705 Successfully pushed ${selected.name} v${confirmedVersion}
@@ -39726,7 +40067,9 @@ async function runIncludeSourceHook(apiKey, agentId, selectedType) {
39726
40067
  __name(runIncludeSourceHook, "runIncludeSourceHook");
39727
40068
  async function pushAgentVersion(options = {}) {
39728
40069
  writeProgress("\u{1F504} Compiling...");
39729
- await compileCommand();
40070
+ await compileCommand({
40071
+ serverSync: true
40072
+ });
39730
40073
  const config = readYamlConfig();
39731
40074
  if (!config || !config.agent?.agentId) {
39732
40075
  throw new Error("No agent configuration found. Please run 'lua init' first.");
@@ -39833,7 +40176,9 @@ __name(deployPersonaVersionAfterPush, "deployPersonaVersionAfterPush");
39833
40176
  async function pushMCPServer(options = {}) {
39834
40177
  try {
39835
40178
  writeProgress("\u{1F4E6} Compiling project...");
39836
- await compileCommand();
40179
+ await compileCommand({
40180
+ serverSync: true
40181
+ });
39837
40182
  writeSuccess("\u2705 Compilation complete");
39838
40183
  const apiKey = await authenticateOrFail();
39839
40184
  writeSuccess("\u2705 Authentication verified");
@@ -39886,7 +40231,9 @@ async function pushAllCommand(options) {
39886
40231
  writeInfo("\u{1F680} Auto-deploy to production is ENABLED\n");
39887
40232
  }
39888
40233
  writeProgress("\u{1F4E6} Compiling project...");
39889
- await compileCommand();
40234
+ await compileCommand({
40235
+ serverSync: true
40236
+ });
39890
40237
  const config = readYamlConfig();
39891
40238
  if (!config?.agent?.agentId) {
39892
40239
  throw new Error('No agent ID found in lua.skill.yaml. Run "lua init" first.');
@@ -39974,7 +40321,7 @@ ${icon} Pushing ${items.length} ${handler.displayName}(s)...`);
39974
40321
  }
39975
40322
  const entityId = item[handler.yamlConfig.idField];
39976
40323
  if (!entityId) {
39977
- console.warn(`\u26A0\uFE0F ${handler.displayName} "${item.name}" has no server ID, skipping. Run 'lua compile' first.`);
40324
+ console.warn(`\u26A0\uFE0F ${handler.displayName} "${item.name}" has no server ID, skipping \u2014 the create above failed. Fix it and run 'lua push ${handler.displayName}' again.`);
39978
40325
  continue;
39979
40326
  }
39980
40327
  if (!item.version) {
@@ -41921,7 +42268,9 @@ __name(chatCommand, "chatCommand");
41921
42268
  async function setupChatEnvironment(chatEnv, config) {
41922
42269
  writeProgress("\u{1F504} Setting up sandbox environment...");
41923
42270
  writeProgress("\u{1F504} Compiling skill...");
41924
- await compileCommand();
42271
+ await compileCommand({
42272
+ serverSync: true
42273
+ });
41925
42274
  const manifest = loadManifest();
41926
42275
  const skills = getPrimitivesByKind(manifest, PrimitiveKind.SKILL);
41927
42276
  let sandboxIds = {};
@@ -45026,7 +45375,7 @@ function channelCreateError(error, alreadyConnected) {
45026
45375
  return CliError.fromStatus(400, "Channel already exists", `${alreadyConnected}
45027
45376
  Use 'lua channels' to list existing channels.`);
45028
45377
  }
45029
- return CliError.fromStatus(error?.statusCode, error?.message || "Unknown error", error?.error);
45378
+ return CliError.fromStatus(error?.statusCode, error?.message || "Unknown error", error?.error, apiErrorDetail(error));
45030
45379
  }
45031
45380
  __name(channelCreateError, "channelCreateError");
45032
45381
  async function fetchChannelsCore(agentApi, agentId) {
@@ -45962,7 +46311,7 @@ async function nonInteractiveLogs(logsApi, agentId, apiKey, options) {
45962
46311
  }
45963
46312
  const response = await logsApi.getAgentLogs(agentId, options.limit || 20, options.page || 1, filters);
45964
46313
  if (!response.success) {
45965
- throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Unknown error");
46314
+ throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Unknown error", void 0, apiErrorDetail(response.error));
45966
46315
  }
45967
46316
  const data = response.data;
45968
46317
  if (options.json) {
@@ -46174,7 +46523,7 @@ async function viewAgentLogsInteractive(logsApi, agentId, filters = {}) {
46174
46523
  while (keepViewing) {
46175
46524
  const response = await logsApi.getAgentLogs(agentId, limit, currentPage, filters);
46176
46525
  if (!response.success) {
46177
- throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Unknown error");
46526
+ throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Unknown error", void 0, apiErrorDetail(response.error));
46178
46527
  }
46179
46528
  const data = response.data;
46180
46529
  displayLogsCore(data.logs, data.pagination, "All Agent Logs", true);
@@ -49703,7 +50052,7 @@ async function jobsCommand(action, cmdObj) {
49703
50052
  }, "jobs");
49704
50053
  }
49705
50054
  __name(jobsCommand, "jobsCommand");
49706
- async function displayJobsCore(context, jobs) {
50055
+ async function displayJobsCore(context, jobs, opts = {}) {
49707
50056
  console.log("\n" + "=".repeat(60));
49708
50057
  console.log("\u2699\uFE0F Production Jobs");
49709
50058
  console.log("=".repeat(60) + "\n");
@@ -49726,7 +50075,14 @@ async function displayJobsCore(context, jobs) {
49726
50075
  }
49727
50076
  console.log();
49728
50077
  } catch (error) {
49729
- displayJobError(job, "Error loading info");
50078
+ if (CliError.isCliError(error) && error.statusCode === 404) {
50079
+ if (opts.single) {
50080
+ throw CliError.notFound(`Job "${job.name}" (${job.jobId}) no longer exists on the server \u2014 ${error.message}`, "lua.skill.yaml still lists it; `lua sync` reconciles the local config with the server.");
50081
+ }
50082
+ displayJobError(job, "not found on the server");
50083
+ continue;
50084
+ }
50085
+ throw error;
49730
50086
  }
49731
50087
  }
49732
50088
  console.log("=".repeat(60));
@@ -49994,7 +50350,13 @@ async function executeNonInteractive7(context, config, action, options) {
49994
50350
  console.log("\u2139\uFE0F No jobs found in configuration.");
49995
50351
  return;
49996
50352
  }
49997
- await displayJobsCore(context, jobs);
50353
+ const viewed = options.jobName ? jobs.filter((j) => j.jobId === options.jobName || j.name === options.jobName) : jobs;
50354
+ if (viewed.length === 0) {
50355
+ throw CliError.notFound(`Job "${options.jobName}" not found`, listHint("Available jobs in local config:", jobs.map((j) => `${j.name} (${j.jobId})`)));
50356
+ }
50357
+ await displayJobsCore(context, viewed, {
50358
+ single: !!options.jobName
50359
+ });
49998
50360
  return;
49999
50361
  }
50000
50362
  if (!options.jobName) {
@@ -50500,69 +50862,77 @@ function exitCodeForRunStatus(status) {
50500
50862
  }
50501
50863
  __name(exitCodeForRunStatus, "exitCodeForRunStatus");
50502
50864
  async function workflowsCommand(action, target, extra, cmdObj) {
50503
- return withErrorHandling(async () => {
50504
- const options = {
50505
- ...cmdObj ?? {}
50506
- };
50507
- const json = !!options.json;
50508
- let resolved;
50509
- if (action) {
50510
- resolved = validateOrSuggest("workflows.action", action);
50511
- } else {
50512
- const answer = await safePrompt([
50513
- {
50514
- type: "list",
50515
- name: "action",
50516
- message: "What would you like to do?",
50517
- choices: [
50518
- {
50519
- name: "\u{1F4CB} List workflows",
50520
- value: "list"
50521
- },
50522
- {
50523
- name: "\u{1F9ED} Run a workflow locally",
50524
- value: "run"
50525
- },
50526
- {
50527
- name: "\u25B6\uFE0F Start a run",
50528
- value: "start"
50529
- },
50530
- {
50531
- name: "\u{1F5C2}\uFE0F List runs",
50532
- value: "runs"
50533
- }
50534
- ]
50535
- }
50536
- ]);
50537
- if (!answer) return;
50538
- resolved = answer.action;
50539
- }
50540
- if (resolved === "run") {
50541
- const outcome = await runWorkflowLocalFromProject(target ?? options.workflowName ?? null, options);
50542
- process.exitCode = outcome.exitCode;
50865
+ return withErrorHandling(
50866
+ async () => {
50867
+ const options = {
50868
+ ...cmdObj ?? {}
50869
+ };
50870
+ const json = !!options.json;
50871
+ let resolved;
50872
+ if (action) {
50873
+ resolved = validateOrSuggest("workflows.action", action);
50874
+ } else {
50875
+ const answer = await safePrompt([
50876
+ {
50877
+ type: "list",
50878
+ name: "action",
50879
+ message: "What would you like to do?",
50880
+ choices: [
50881
+ {
50882
+ name: "\u{1F4CB} List workflows",
50883
+ value: "list"
50884
+ },
50885
+ {
50886
+ name: "\u{1F9ED} Run a workflow locally",
50887
+ value: "run"
50888
+ },
50889
+ {
50890
+ name: "\u25B6\uFE0F Start a run",
50891
+ value: "start"
50892
+ },
50893
+ {
50894
+ name: "\u{1F5C2}\uFE0F List runs",
50895
+ value: "runs"
50896
+ }
50897
+ ]
50898
+ }
50899
+ ]);
50900
+ if (!answer) return;
50901
+ resolved = answer.action;
50902
+ }
50903
+ if (resolved === "run") {
50904
+ const outcome = await runWorkflowLocalFromProject(target ?? options.workflowName ?? null, options);
50905
+ process.exitCode = outcome.exitCode;
50906
+ trackEvent("cli_workflows_action", {
50907
+ action: "run",
50908
+ non_interactive: !!action
50909
+ });
50910
+ return;
50911
+ }
50912
+ const { agentId, apiKey } = await initializeCommand({
50913
+ showProgress: !json
50914
+ });
50915
+ const ctx = {
50916
+ agentId,
50917
+ apiKey,
50918
+ api: new WorkflowApi(BASE_URLS.API, apiKey, agentId),
50919
+ json
50920
+ };
50921
+ const code = await executeAction(ctx, resolved, target, extra, options);
50922
+ if (code !== void 0 && code !== 0) process.exitCode = code;
50543
50923
  trackEvent("cli_workflows_action", {
50544
- action: "run",
50545
- non_interactive: !!action
50924
+ action: resolved,
50925
+ non_interactive: !!action,
50926
+ exit_code: code ?? 0
50546
50927
  });
50547
- return;
50928
+ },
50929
+ "workflows",
50930
+ // LUA-803: a typed refusal that escapes a verb under `--json` (a 403 the client threw, an unreachable host) is
50931
+ // the envelope on stdout — the same shape `apiFailure` prints for a refused verb — never the `✖` line.
50932
+ {
50933
+ json: /* @__PURE__ */ __name(() => !!cmdObj?.json, "json")
50548
50934
  }
50549
- const { agentId, apiKey } = await initializeCommand({
50550
- showProgress: !json
50551
- });
50552
- const ctx = {
50553
- agentId,
50554
- apiKey,
50555
- api: new WorkflowApi(BASE_URLS.API, apiKey, agentId),
50556
- json
50557
- };
50558
- const code = await executeAction(ctx, resolved, target, extra, options);
50559
- if (code !== void 0 && code !== 0) process.exitCode = code;
50560
- trackEvent("cli_workflows_action", {
50561
- action: resolved,
50562
- non_interactive: !!action,
50563
- exit_code: code ?? 0
50564
- });
50565
- }, "workflows");
50935
+ );
50566
50936
  }
50567
50937
  __name(workflowsCommand, "workflowsCommand");
50568
50938
  async function executeAction(ctx, action, target, extra, o) {
@@ -50664,18 +51034,12 @@ async function executeAction(ctx, action, target, extra, o) {
50664
51034
  }
50665
51035
  __name(executeAction, "executeAction");
50666
51036
  async function requireName(name, verb, fn) {
50667
- if (!name) {
50668
- console.error(`\u274C ${verb}: a workflow name is required \u2014 lua workflows ${verb} <name> (or -i <name>)`);
50669
- return WORKFLOW_EXIT.USAGE;
50670
- }
51037
+ if (!name) throw CliError.usage(`${verb}: a workflow name is required`, `lua workflows ${verb} <name> (or -i <name>)`);
50671
51038
  return fn();
50672
51039
  }
50673
51040
  __name(requireName, "requireName");
50674
51041
  async function requireRun(runId, verb, fn) {
50675
- if (!runId) {
50676
- console.error(`\u274C ${verb}: a run id is required \u2014 lua workflows ${verb} <runId> (or -r <runId>)`);
50677
- return WORKFLOW_EXIT.USAGE;
50678
- }
51042
+ if (!runId) throw CliError.usage(`${verb}: a run id is required`, `lua workflows ${verb} <runId> (or -r <runId>)`);
50679
51043
  return fn();
50680
51044
  }
50681
51045
  __name(requireRun, "requireRun");
@@ -50683,46 +51047,80 @@ function emitJson(ctx, res) {
50683
51047
  if (ctx.json) console.log(JSON.stringify(res, null, 2));
50684
51048
  }
50685
51049
  __name(emitJson, "emitJson");
50686
- function apiFailure(ctx, res, verb) {
50687
- emitJson(ctx, res);
51050
+ var SCHEDULE_PROVISION_FAILED_HINT = "The schedule provider (EventBridge) refused this schedule \u2014 the reason above names what to change; re-run after fixing it. This is not a connectivity problem.";
51051
+ function refusalExitCode(status) {
51052
+ if (status === 0 || status !== void 0 && status >= 500) return WORKFLOW_EXIT.UNAVAILABLE;
51053
+ if (status === 404) return WORKFLOW_EXIT.NOT_FOUND;
51054
+ return WORKFLOW_EXIT.API;
51055
+ }
51056
+ __name(refusalExitCode, "refusalExitCode");
51057
+ function apiRefusal(res, verb, detail = {}) {
50688
51058
  const err = res.error;
50689
51059
  const status = err?.statusCode;
50690
51060
  const code = err?.code ?? err?.error;
50691
- const message = `${verb} failed${code ? ` (${code})` : ""}: ${err?.message ?? "Unknown error"}`;
50692
- if (status === 0 || status !== void 0 && status >= 500) {
50693
- throw CliError.fromStatus(status, message, status === 503 && code === "CONTROL_UNAVAILABLE" ? CONTROL_UNAVAILABLE_HINT : void 0);
51061
+ const reason = code === "SCHEDULE_PROVISION_FAILED" ? err?.reason : void 0;
51062
+ const serverMessage = `${err?.message ?? "Unknown error"}${typeof reason === "string" && reason ? `: ${reason}` : ""}`;
51063
+ const message = detail.message ? code ? `${detail.message} (${code})` : detail.message : code ? serverMessage === code ? `${verb} failed (${code})` : `${verb} failed (${code}): ${serverMessage}` : `${verb} failed: ${serverMessage}`;
51064
+ const issues = detail.issues ?? err?.issues;
51065
+ const exitCode = refusalExitCode(status);
51066
+ if (exitCode === WORKFLOW_EXIT.UNAVAILABLE) {
51067
+ return CliError.fromStatus(
51068
+ status,
51069
+ message,
51070
+ status === 503 && code === "CONTROL_UNAVAILABLE" ? CONTROL_UNAVAILABLE_HINT : code === "SCHEDULE_PROVISION_FAILED" ? SCHEDULE_PROVISION_FAILED_HINT : detail.hint,
51071
+ // LUA-810: `fromStatus` picks the UPSTREAM_UNAVAILABLE hint from `upstream` / `requestId` itself.
51072
+ // LUA-812: and the VENDOR_UNAVAILABLE hint from `vendor` / `retryAfterSeconds`.
51073
+ {
51074
+ serverCode: code,
51075
+ issues,
51076
+ upstream: err?.upstream,
51077
+ requestId: err?.requestId,
51078
+ vendor: err?.vendor,
51079
+ retryAfterSeconds: err?.retryAfterSeconds
51080
+ }
51081
+ );
51082
+ }
51083
+ return new CliError(exitCode === WORKFLOW_EXIT.NOT_FOUND ? "not_found" : "error", message, {
51084
+ exitCode,
51085
+ statusCode: status,
51086
+ serverCode: code,
51087
+ issues,
51088
+ hint: detail.hint
51089
+ });
51090
+ }
51091
+ __name(apiRefusal, "apiRefusal");
51092
+ function apiFailure(ctx, res, verb, detail = {}) {
51093
+ if (ctx.json) {
51094
+ emitJson(ctx, res);
51095
+ return refusalExitCode(res.error?.statusCode);
50694
51096
  }
50695
- if (!ctx.json) console.error(`\u274C ${message}`);
50696
- if (status === 404) return WORKFLOW_EXIT.NOT_FOUND;
50697
- return WORKFLOW_EXIT.API;
51097
+ throw apiRefusal(res, verb, detail);
50698
51098
  }
50699
51099
  __name(apiFailure, "apiFailure");
50700
51100
  async function resolveWorkflow(ctx, nameOrId) {
50701
- const list = await loadWorkflowList(ctx);
50702
- return list && pickWorkflow(list, nameOrId);
51101
+ return pickWorkflow(await loadWorkflowList(ctx), nameOrId);
50703
51102
  }
50704
51103
  __name(resolveWorkflow, "resolveWorkflow");
50705
51104
  async function loadWorkflowList(ctx) {
50706
51105
  const res = await ctx.api.getWorkflows({
50707
51106
  includeDynamic: true
50708
51107
  });
50709
- if (!res.success || !res.data) {
50710
- console.error(`\u274C Failed to list workflows: ${res.error?.message ?? "Unknown error"}`);
50711
- return void 0;
50712
- }
51108
+ if (!res.success || !res.data) throw apiRefusal(res, "list");
50713
51109
  return res.data.workflows;
50714
51110
  }
50715
51111
  __name(loadWorkflowList, "loadWorkflowList");
50716
51112
  function pickWorkflow(list, nameOrId) {
50717
51113
  const wf = list.find((w) => w.name === nameOrId) ?? list.find((w) => w.id === nameOrId);
50718
51114
  if (!wf) {
50719
- console.error(`\u274C Workflow "${nameOrId}" not found`);
50720
- const names = list.map((w) => w.name);
50721
- if (names.length) console.log(` Available: ${names.join(", ")}`);
51115
+ throw CliError.notFound(`Workflow "${nameOrId}" not found`, listHint("Available:", list.map((w) => w.name)));
50722
51116
  }
50723
51117
  return wf;
50724
51118
  }
50725
51119
  __name(pickWorkflow, "pickWorkflow");
51120
+ function isDefinitionId(nameOrId) {
51121
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(nameOrId);
51122
+ }
51123
+ __name(isDefinitionId, "isDefinitionId");
50726
51124
  var shortHash = /* @__PURE__ */ __name((h) => h ? h.replace(/^sha256-cj1:/, "").slice(0, 12) : "\u2014", "shortHash");
50727
51125
  var when = /* @__PURE__ */ __name((iso) => iso ? new Date(iso).toLocaleString() : "\u2014", "when");
50728
51126
  var clockOf = /* @__PURE__ */ __name((at) => {
@@ -50735,6 +51133,15 @@ var parseIntFlag = /* @__PURE__ */ __name((v, flag) => {
50735
51133
  if (!Number.isFinite(n2)) throw new WorkflowLocalUsageError("usage", `${flag}: expected a number (got "${v}")`);
50736
51134
  return n2;
50737
51135
  }, "parseIntFlag");
51136
+ function parsedOrUsage(parse) {
51137
+ try {
51138
+ return parse();
51139
+ } catch (e) {
51140
+ if (e instanceof WorkflowLocalUsageError) throw CliError.usage(e.message);
51141
+ throw e;
51142
+ }
51143
+ }
51144
+ __name(parsedOrUsage, "parsedOrUsage");
50738
51145
  async function listCore(ctx, opts) {
50739
51146
  const res = await ctx.api.getWorkflows({
50740
51147
  includeDynamic: opts.all
@@ -50921,35 +51328,22 @@ function startResultLine(d, requestedName) {
50921
51328
  }
50922
51329
  __name(startResultLine, "startResultLine");
50923
51330
  async function startCore(ctx, name, o) {
50924
- const wf = await resolveWorkflow(ctx, name);
50925
- if (!wf) return WORKFLOW_EXIT.NOT_FOUND;
50926
- let input = {};
50927
- let waitSeconds;
50928
- let budgetCredits;
50929
- try {
50930
- if (o.input) input = parseJsonOrFile(o.input, "--input");
50931
- waitSeconds = parseIntFlag(o.wait, "--wait");
50932
- budgetCredits = parseIntegerFlag(o.budgetCredits, "--budget-credits", {
50933
- min: 1
50934
- });
50935
- } catch (e) {
50936
- if (e instanceof WorkflowLocalUsageError) {
50937
- console.error(`\u274C ${e.message}`);
50938
- return WORKFLOW_EXIT.USAGE;
50939
- }
50940
- throw e;
50941
- }
51331
+ const input = parsedOrUsage(() => o.input ? parseJsonOrFile(o.input, "--input") : {});
51332
+ const waitSeconds = parsedOrUsage(() => parseIntFlag(o.wait, "--wait"));
51333
+ const budgetCredits = parsedOrUsage(() => parseIntegerFlag(o.budgetCredits, "--budget-credits", {
51334
+ min: 1
51335
+ }));
50942
51336
  if (waitSeconds !== void 0 && (waitSeconds < 0 || waitSeconds > 55)) {
50943
- console.error("\u274C --wait must be 0..55 (a server long-poll; the CLI raises its own deadline to wait+10 s)");
50944
- return WORKFLOW_EXIT.USAGE;
51337
+ throw CliError.usage("--wait must be 0..55 (a server long-poll; the CLI raises its own deadline to wait+10 s)");
50945
51338
  }
50946
51339
  const tags = o.tag === void 0 ? void 0 : Array.isArray(o.tag) ? o.tag : [
50947
51340
  o.tag
50948
51341
  ];
50949
- if (tags && tags.length > 10) {
50950
- console.error("\u274C --tag: at most 10 tags");
50951
- return WORKFLOW_EXIT.USAGE;
50952
- }
51342
+ if (tags && tags.length > 10) throw CliError.usage("--tag: at most 10 tags");
51343
+ const wf = isDefinitionId(name) ? {
51344
+ id: name,
51345
+ name
51346
+ } : await resolveWorkflow(ctx, name);
50953
51347
  let workflowVersionId;
50954
51348
  if (o.workflowVersion) {
50955
51349
  const ref = await resolveVersionRef(ctx, wf, o.workflowVersion, "start");
@@ -51021,8 +51415,7 @@ async function runsCore(ctx, o) {
51021
51415
  try {
51022
51416
  limit = parseIntFlag(o.limit, "--limit");
51023
51417
  } catch (e) {
51024
- console.error(`\u274C ${e.message}`);
51025
- return WORKFLOW_EXIT.USAGE;
51418
+ throw CliError.usage(`${e.message}`);
51026
51419
  }
51027
51420
  const res = await ctx.api.getRuns({
51028
51421
  workflowId,
@@ -51160,6 +51553,8 @@ function printRun(run, withSteps) {
51160
51553
  console.log(`
51161
51554
  \u23F8\uFE0F Paused \u2014 run budget reached (${budgetCopy(run)}):`);
51162
51555
  console.log(` lua workflows raise-budget ${id} --credits <n>`);
51556
+ } else if (kind === "billing") {
51557
+ for (const line of billingParkLines(run)) console.log(line);
51163
51558
  } else {
51164
51559
  console.log(` Gate: ${kind}${run.gate.reason ? ` (${run.gate.reason})` : ""}${run.gate.since ? ` since ${when(run.gate.since)}` : ""}`);
51165
51560
  }
@@ -51208,6 +51603,23 @@ function printRun(run, withSteps) {
51208
51603
  }
51209
51604
  }
51210
51605
  __name(printRun, "printRun");
51606
+ function billingParkLines(run) {
51607
+ const id = runIdOf(run);
51608
+ const gate = run.gate ?? {
51609
+ kind: "billing"
51610
+ };
51611
+ const what = gate.code === "out_of_actions" ? "the workspace is out of actions" : "the workspace has no credits left";
51612
+ const step3 = gate.stepId ? ` before step ${gate.stepId}` : "";
51613
+ const out = [
51614
+ `
51615
+ \u23F8\uFE0F Paused \u2014 payment required (${what})${step3}:`
51616
+ ];
51617
+ out.push(" top up the workspace (or ask a workspace admin to) \u2014 the run resumes on its own within a few minutes of the top-up");
51618
+ out.push(` to resume it right away after topping up: lua workflows retry-step ${id} --step ${gate.stepId ?? "<stepId>"}`);
51619
+ if (typeof gate.expiresAt === "number") out.push(` still unpaid at ${when(gate.expiresAt)} \u21D2 the run fails`);
51620
+ return out;
51621
+ }
51622
+ __name(billingParkLines, "billingParkLines");
51211
51623
  function stepAttemptLines(s) {
51212
51624
  const out = [];
51213
51625
  const cause = stepFailureLine(s.error);
@@ -51315,15 +51727,8 @@ function attemptCountersLine(c) {
51315
51727
  }
51316
51728
  __name(attemptCountersLine, "attemptCountersLine");
51317
51729
  async function watchCore(ctx, runId, o) {
51318
- let afterSeq;
51319
- let timeoutS;
51320
- try {
51321
- afterSeq = parseIntFlag(o.after, "--after");
51322
- timeoutS = parseIntFlag(o.timeout, "--timeout");
51323
- } catch (e) {
51324
- console.error(`\u274C ${e.message}`);
51325
- return WORKFLOW_EXIT.USAGE;
51326
- }
51730
+ const afterSeq = parsedOrUsage(() => parseIntFlag(o.after, "--after"));
51731
+ const timeoutS = parsedOrUsage(() => parseIntFlag(o.timeout, "--timeout"));
51327
51732
  const controller = new AbortController();
51328
51733
  let timedOut = false;
51329
51734
  const timer = timeoutS ? setTimeout(() => (timedOut = true, controller.abort()), timeoutS * 1e3) : void 0;
@@ -51332,7 +51737,7 @@ async function watchCore(ctx, runId, o) {
51332
51737
  let lastEventId = afterSeq !== void 0 ? String(afterSeq) : void 0;
51333
51738
  let exit;
51334
51739
  let reconnects = 0;
51335
- const throttled = /* @__PURE__ */ new Map();
51740
+ const state3 = createWatchState();
51336
51741
  try {
51337
51742
  while (exit === void 0 && !controller.signal.aborted) {
51338
51743
  const res = await ctx.api.watchEvents(runId, {
@@ -51347,7 +51752,7 @@ async function watchCore(ctx, runId, o) {
51347
51752
  let sawEnd = false;
51348
51753
  for await (const frame of res.data) {
51349
51754
  if (frame.id !== void 0) lastEventId = frame.id;
51350
- const verdict = handleWatchFrame(ctx, runId, frame, o, throttled);
51755
+ const verdict = await handleWatchFrame(ctx, runId, frame, o, state3);
51351
51756
  if (verdict === "reconnect") break;
51352
51757
  if (typeof verdict === "number") {
51353
51758
  exit = verdict;
@@ -51357,10 +51762,10 @@ async function watchCore(ctx, runId, o) {
51357
51762
  }
51358
51763
  if (exit !== void 0 || controller.signal.aborted || sawEnd) break;
51359
51764
  reconnects += 1;
51360
- if (reconnects > 20) {
51361
- console.error("\u274C watch: too many reconnects");
51362
- return WORKFLOW_EXIT.API;
51363
- }
51765
+ resetWatchReplay(state3);
51766
+ if (reconnects > 20) throw new CliError("error", "watch: too many reconnects", {
51767
+ exitCode: WORKFLOW_EXIT.API
51768
+ });
51364
51769
  if (!ctx.json) console.error(`\u2026 reconnecting (Last-Event-ID ${lastEventId ?? "none"})`);
51365
51770
  await new Promise((r) => setTimeout(r, Math.min(5e3, 250 * 2 ** reconnects)));
51366
51771
  }
@@ -51385,7 +51790,55 @@ function throttledLine(throttled) {
51385
51790
  ].map(([k, n2]) => `${k} \xD7${n2}`).join(" \xB7 ")}`;
51386
51791
  }
51387
51792
  __name(throttledLine, "throttledLine");
51388
- function handleWatchFrame(ctx, runId, frame, o, throttled) {
51793
+ function createWatchState() {
51794
+ return {
51795
+ throttled: /* @__PURE__ */ new Map(),
51796
+ signalNames: /* @__PURE__ */ new Map()
51797
+ };
51798
+ }
51799
+ __name(createWatchState, "createWatchState");
51800
+ function resetWatchReplay(state3) {
51801
+ state3.truth = void 0;
51802
+ state3.pending = void 0;
51803
+ }
51804
+ __name(resetWatchReplay, "resetWatchReplay");
51805
+ async function readWatchTruth(ctx, runId) {
51806
+ try {
51807
+ const res = await ctx.api.getRun(runId);
51808
+ if (!res?.success || !res.data) return null;
51809
+ const run = res.data;
51810
+ return {
51811
+ parked: run.status === "suspended" || run.status === "gated",
51812
+ watermark: typeof run.eventSeq === "number" && Number.isFinite(run.eventSeq) ? run.eventSeq : void 0
51813
+ };
51814
+ } catch {
51815
+ return null;
51816
+ }
51817
+ }
51818
+ __name(readWatchTruth, "readWatchTruth");
51819
+ function frameSeq(frame, ev) {
51820
+ const fromId = frame.id === void 0 ? NaN : Number(frame.id);
51821
+ if (Number.isFinite(fromId)) return fromId;
51822
+ return typeof ev.seq === "number" ? ev.seq : void 0;
51823
+ }
51824
+ __name(frameSeq, "frameSeq");
51825
+ async function watchBoundary(ctx, runId, state3, seq, line, code, emit2) {
51826
+ if (state3.truth === void 0 || state3.truth !== null && state3.truth.watermark === void 0) {
51827
+ state3.truth = await readWatchTruth(ctx, runId);
51828
+ }
51829
+ const truth = state3.truth;
51830
+ if (truth === null) return emit2(line, code);
51831
+ if (truth.watermark === void 0 || seq === void 0) return truth.parked ? emit2(line, code) : void 0;
51832
+ if (seq > truth.watermark) return emit2(line, code);
51833
+ if (!truth.parked) return void 0;
51834
+ state3.pending = {
51835
+ line,
51836
+ code
51837
+ };
51838
+ return void 0;
51839
+ }
51840
+ __name(watchBoundary, "watchBoundary");
51841
+ async function handleWatchFrame(ctx, runId, frame, o, state3) {
51389
51842
  const data = frame.data ?? {};
51390
51843
  if (o.events || ctx.json) console.log(JSON.stringify({
51391
51844
  id: frame.id,
@@ -51398,10 +51851,17 @@ function handleWatchFrame(ctx, runId, frame, o, throttled) {
51398
51851
  if (!ctx.json) console.error(" --wait-for-human: still following \u2014 Ctrl+C or --timeout to stop");
51399
51852
  return void 0;
51400
51853
  }, "humanBoundary");
51854
+ const flushPending = /* @__PURE__ */ __name(() => {
51855
+ const pending = state3.pending;
51856
+ if (!pending) return void 0;
51857
+ state3.pending = void 0;
51858
+ return humanBoundary(pending.line, pending.code);
51859
+ }, "flushPending");
51401
51860
  switch (frame.event) {
51402
51861
  case "heartbeat":
51403
- return void 0;
51862
+ return flushPending();
51404
51863
  case "reconnect":
51864
+ resetWatchReplay(state3);
51405
51865
  return "reconnect";
51406
51866
  case "error": {
51407
51867
  const code = String(data.code ?? "unknown");
@@ -51418,10 +51878,15 @@ function handleWatchFrame(ctx, runId, frame, o, throttled) {
51418
51878
  const type = frame.event || ev.type;
51419
51879
  if (type === "step.throttled") {
51420
51880
  const kind = String(ev.data?.kind ?? "throttled");
51421
- throttled.set(kind, (throttled.get(kind) ?? 0) + 1);
51422
- if (!ctx.json && !o.events) process.stdout.write(`\r ${throttledLine(throttled)}`);
51881
+ state3.throttled.set(kind, (state3.throttled.get(kind) ?? 0) + 1);
51882
+ if (!ctx.json && !o.events) process.stdout.write(`\r ${throttledLine(state3.throttled)}`);
51423
51883
  return void 0;
51424
51884
  }
51885
+ if (type === "step.suspended") {
51886
+ const d = ev.data;
51887
+ const stepId = ev.stepId ?? (typeof d?.stepId === "string" ? d.stepId : void 0);
51888
+ if (stepId && typeof d?.signalName === "string") state3.signalNames.set(stepId, d.signalName);
51889
+ }
51425
51890
  if (!ctx.json && !o.events) {
51426
51891
  const at = clockOf(ev.ts);
51427
51892
  const subrun = subrunEventDetail({
@@ -51431,39 +51896,58 @@ function handleWatchFrame(ctx, runId, frame, o, throttled) {
51431
51896
  const detail = subrun ? ` \xB7 ${subrun}` : ev.data ? ` ${JSON.stringify(scrubEventData(ev.data)).slice(0, 160)}` : "";
51432
51897
  console.log(`[${at}] ${type}${ev.stepId ? ` \xB7 ${ev.stepId}` : ""}${detail}`);
51433
51898
  }
51434
- if (type === "run.gated") {
51435
- const gate = ev.data?.gate;
51436
- const stepId = ev.data?.stepId;
51437
- if (gate === "exception") {
51438
- return humanBoundary(`\u26A0\uFE0F run parked on an exception gate \u2014 a human decides next: lua workflows status ${runId}`, WORKFLOW_EXIT.RUN_PARKED);
51439
- }
51440
- if (gate === "billing") {
51441
- return humanBoundary(`\u23F8\uFE0F run parked on a billing gate \u2014 top up, then: lua workflows retry-step ${runId} --step ${stepId ?? "<stepId>"}`, WORKFLOW_EXIT.RUN_PARKED);
51442
- }
51443
- if (gate === "budget") {
51444
- return humanBoundary(`\u23F8\uFE0F run budget reached \u2014 lua workflows raise-budget ${runId} --credits <n> (lua workflows status ${runId} says what a credit buys)`, WORKFLOW_EXIT.RUN_PARKED);
51445
- }
51446
- return humanBoundary(`\u23F8\uFE0F run gated (${gate ?? "consent"}) \u2014 approve it from the desktop; then: lua workflows watch ${runId}`, WORKFLOW_EXIT.RUN_GATED);
51447
- }
51448
- if (type === "run.suspended") {
51449
- const kind = ev.data?.kind;
51450
- const stepId = ev.data?.stepId ?? ev.stepId;
51451
- if (kind === "approval" || kind === "input" || kind === "signal") {
51452
- const verb = kind === "approval" ? `lua workflows approve ${runId} --approval <id> --decision approve|deny` : kind === "input" ? `lua workflows resume ${runId} --step ${stepId ?? "<stepId>"} --data <json>` : `lua workflows signal ${runId} <name> --payload <json>`;
51453
- return humanBoundary(`\u23F8\uFE0F run waits for a person (${kind}${stepId ? ` \xB7 ${stepId}` : ""}) \u2014 ${verb}; then: lua workflows watch ${runId}`, WORKFLOW_EXIT.RUN_PARKED);
51454
- }
51455
- }
51456
- if (type === "run.budget_parked") {
51457
- return humanBoundary(`\u23F8\uFE0F run budget reached \u2014 lua workflows raise-budget ${runId} --credits <n> (lua workflows status ${runId} says what a credit buys)`, WORKFLOW_EXIT.RUN_PARKED);
51458
- }
51459
- if (type === "run.completed") return WORKFLOW_EXIT.OK;
51460
- if (type === "run.failed" || type === "run.timed_out") return WORKFLOW_EXIT.RUN_FAILED;
51461
- if (type === "run.cancelled" || type === "run.abandoned") return WORKFLOW_EXIT.RUN_CANCELLED;
51899
+ if (type === "run.resumed") state3.pending = void 0;
51900
+ const seq = frameSeq(frame, ev);
51901
+ const boundary = /* @__PURE__ */ __name((line, code) => watchBoundary(ctx, runId, state3, seq, line, code, humanBoundary), "boundary");
51902
+ const verdict = await boundaryVerdict(runId, {
51903
+ ...ev,
51904
+ type
51905
+ }, state3, boundary);
51906
+ if (verdict !== void 0) return verdict;
51907
+ const watermark = state3.truth?.watermark;
51908
+ if (watermark !== void 0 && seq !== void 0 && seq >= watermark) return flushPending();
51462
51909
  return void 0;
51463
51910
  }
51464
51911
  }
51465
51912
  }
51466
51913
  __name(handleWatchFrame, "handleWatchFrame");
51914
+ async function boundaryVerdict(runId, ev, state3, boundary) {
51915
+ const type = ev.type;
51916
+ if (type === "run.gated") {
51917
+ const gate = ev.data?.gate;
51918
+ const stepId = ev.data?.stepId;
51919
+ if (gate === "exception") {
51920
+ return boundary(`\u26A0\uFE0F run parked on an exception gate \u2014 a human decides next: lua workflows status ${runId}`, WORKFLOW_EXIT.RUN_PARKED);
51921
+ }
51922
+ if (gate === "billing") {
51923
+ 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);
51924
+ }
51925
+ if (gate === "budget") {
51926
+ return boundary(`\u23F8\uFE0F run budget reached \u2014 lua workflows raise-budget ${runId} --credits <n> (lua workflows status ${runId} says what a credit buys)`, WORKFLOW_EXIT.RUN_PARKED);
51927
+ }
51928
+ return boundary(`\u23F8\uFE0F run gated (${gate ?? "consent"}) \u2014 approve it from the desktop; then: lua workflows watch ${runId}`, WORKFLOW_EXIT.RUN_GATED);
51929
+ }
51930
+ if (type === "run.suspended") {
51931
+ const kind = ev.data?.kind;
51932
+ const stepId = ev.data?.stepId ?? ev.stepId;
51933
+ if (kind === "billing") {
51934
+ 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);
51935
+ }
51936
+ if (kind === "approval" || kind === "input" || kind === "signal") {
51937
+ const signalName = stepId ? state3.signalNames.get(stepId) : void 0;
51938
+ const verb = kind === "approval" ? `lua workflows approve ${runId} --approval <id> --decision approve|deny` : kind === "input" ? `lua workflows resume ${runId} --step ${stepId ?? "<stepId>"} --data <json>` : `lua workflows signal ${runId} ${signalName ?? "<name>"} --payload <json>`;
51939
+ return boundary(`\u23F8\uFE0F run waits for a person (${kind}${stepId ? ` \xB7 ${stepId}` : ""}) \u2014 ${verb}; then: lua workflows watch ${runId}`, WORKFLOW_EXIT.RUN_PARKED);
51940
+ }
51941
+ }
51942
+ if (type === "run.budget_parked") {
51943
+ return boundary(`\u23F8\uFE0F run budget reached \u2014 lua workflows raise-budget ${runId} --credits <n> (lua workflows status ${runId} says what a credit buys)`, WORKFLOW_EXIT.RUN_PARKED);
51944
+ }
51945
+ if (type === "run.completed") return WORKFLOW_EXIT.OK;
51946
+ if (type === "run.failed" || type === "run.timed_out") return WORKFLOW_EXIT.RUN_FAILED;
51947
+ if (type === "run.cancelled" || type === "run.abandoned") return WORKFLOW_EXIT.RUN_CANCELLED;
51948
+ return void 0;
51949
+ }
51950
+ __name(boundaryVerdict, "boundaryVerdict");
51467
51951
  async function cancelCore(ctx, runId, o) {
51468
51952
  const res = await ctx.api.cancelRun(runId, {
51469
51953
  mode: o.force ? "force" : "request",
@@ -51486,45 +51970,48 @@ async function cancelCore(ctx, runId, o) {
51486
51970
  }
51487
51971
  __name(cancelCore, "cancelCore");
51488
51972
  async function resumeCore(ctx, runId, o) {
51489
- if (!o.step) {
51490
- console.error("\u274C resume: --step <id> is required");
51491
- return WORKFLOW_EXIT.USAGE;
51492
- }
51493
- let resumeData = {};
51494
- try {
51495
- if (o.data) resumeData = parseJsonOrFile(o.data, "--data");
51496
- } catch (e) {
51497
- console.error(`\u274C ${e.message}`);
51498
- return WORKFLOW_EXIT.USAGE;
51499
- }
51973
+ if (!o.step) throw CliError.usage("resume: --step <id> is required");
51974
+ const resumeData = parsedOrUsage(() => o.data ? parseJsonOrFile(o.data, "--data") : {});
51500
51975
  const res = await ctx.api.resumeRun(runId, o.step, {
51501
51976
  resumeData
51502
51977
  });
51503
- const resumeCode = !res.success ? res.error?.code ?? res.error?.error : void 0;
51504
- const resumeDetail = res.error;
51505
- if (!ctx.json && res.error?.statusCode === 400 && resumeCode === "RESUME_SCHEMA_INVALID") {
51506
- console.error(`\u274C resume rejected \u2014 --data does not match the resumeSchema step "${resumeDetail?.stepId ?? o.step}" declares:`);
51507
- for (const issue of resumeDetail?.issues ?? []) console.error(` \u2022 ${issue.path || "/"}: ${issue.message ?? "invalid"}`);
51508
- console.error(" fix the data and resume again (the step is still waiting for input)");
51509
- } else if (!ctx.json && res.error?.statusCode === 422 && resumeCode === "RESUME_SCHEMA_UNCOMPILABLE") {
51510
- console.error(`\u274C resume could not be checked \u2014 the resumeSchema step "${resumeDetail?.stepId ?? o.step}" declares does not compile:`);
51511
- for (const issue of resumeDetail?.issues ?? []) console.error(` \u2022 ${issue.message ?? "schema does not compile"}`);
51512
- console.error(" this is a workflow definition defect, not a data problem \u2014 fix the `resumeSchema` on the step in the workflow source, push a new version, then resume again");
51513
- }
51514
51978
  if (!res.success || !res.data) {
51515
51979
  const code = res.error?.code ?? res.error?.error;
51516
- if (!ctx.json && res.error?.statusCode === 404 && code === "STEP_NOT_FOUND") {
51517
- console.error(`\u274C step "${o.step}" does not exist on run ${runId} \u2014 list its steps: lua workflows status ${runId} --steps`);
51518
- } else if (!ctx.json && res.error?.statusCode === 409) {
51519
- if (code === "approval_requires_human" || code === "APPROVAL_REQUIRES_HUMAN") {
51520
- console.error(`\u274C step "${o.step}" is an approval \u2014 resolve it with: lua workflows approve ${runId} --approval ${o.step} --decision approve`);
51521
- } else if (code === "use_signal_route" || code === "USE_SIGNAL_ROUTE") {
51522
- console.error(`\u274C step "${o.step}" waits for a signal \u2014 deliver it with: lua workflows signal ${runId} <name> --payload '{}'`);
51523
- } else if (code === "NOT_SUSPENDED") {
51524
- console.error(`\u274C step "${o.step}" is not suspended${res.error?.status ? ` (it is ${res.error.status})` : ""} \u2014 nothing to resume: lua workflows status ${runId} --steps`);
51525
- }
51980
+ const status = res.error?.statusCode;
51981
+ const stepOf3 = res.error?.stepId ?? o.step;
51982
+ let detail = {};
51983
+ if (status === 400 && code === "RESUME_SCHEMA_INVALID") {
51984
+ detail = {
51985
+ message: `resume rejected \u2014 --data does not match the resumeSchema step "${stepOf3}" declares`,
51986
+ issues: res.error?.issues,
51987
+ hint: "fix the data and resume again (the step is still waiting for input)"
51988
+ };
51989
+ } else if (status === 422 && code === "RESUME_SCHEMA_UNCOMPILABLE") {
51990
+ detail = {
51991
+ message: `resume could not be checked \u2014 the resumeSchema step "${stepOf3}" declares does not compile`,
51992
+ issues: (res.error?.issues ?? []).map((issue) => ({
51993
+ message: issue.message ?? "schema does not compile"
51994
+ })),
51995
+ hint: "this is a workflow definition defect, not a data problem \u2014 fix the `resumeSchema` on the step in the workflow source, push a new version, then resume again"
51996
+ };
51997
+ } else if (status === 404 && code === "STEP_NOT_FOUND") {
51998
+ detail = {
51999
+ message: `step "${o.step}" does not exist on run ${runId} \u2014 list its steps: lua workflows status ${runId} --steps`
52000
+ };
52001
+ } else if (status === 409 && (code === "approval_requires_human" || code === "APPROVAL_REQUIRES_HUMAN")) {
52002
+ detail = {
52003
+ message: `step "${o.step}" is an approval \u2014 resolve it with: lua workflows approve ${runId} --approval ${o.step} --decision approve`
52004
+ };
52005
+ } else if (status === 409 && (code === "use_signal_route" || code === "USE_SIGNAL_ROUTE")) {
52006
+ detail = {
52007
+ message: `step "${o.step}" waits for a signal \u2014 deliver it with: lua workflows signal ${runId} <name> --payload '{}'`
52008
+ };
52009
+ } else if (status === 409 && code === "NOT_SUSPENDED") {
52010
+ detail = {
52011
+ message: `step "${o.step}" is not suspended${res.error?.status ? ` (it is ${res.error.status})` : ""} \u2014 nothing to resume: lua workflows status ${runId} --steps`
52012
+ };
51526
52013
  }
51527
- return apiFailure(ctx, res, "resume");
52014
+ return apiFailure(ctx, res, "resume", detail);
51528
52015
  }
51529
52016
  emitJson(ctx, res);
51530
52017
  if (!ctx.json) {
@@ -51539,8 +52026,7 @@ async function resumeCore(ctx, runId, o) {
51539
52026
  __name(resumeCore, "resumeCore");
51540
52027
  async function retryStepCore(ctx, runId, o) {
51541
52028
  if (!o.step) {
51542
- console.error("\u274C --step <id> is required: lua workflows retry-step <runId> --step <id> [--note <text>]");
51543
- return WORKFLOW_EXIT.USAGE;
52029
+ throw CliError.usage("--step <id> is required: lua workflows retry-step <runId> --step <id> [--note <text>]");
51544
52030
  }
51545
52031
  const res = await ctx.api.retryStep(runId, o.step, {
51546
52032
  ...o.note ? {
@@ -51548,21 +52034,30 @@ async function retryStepCore(ctx, runId, o) {
51548
52034
  } : {}
51549
52035
  });
51550
52036
  if (!res.success || !res.data) {
52037
+ let detail = {};
51551
52038
  const code = res.error?.code ?? res.error?.error;
51552
- if (!ctx.json && res.error?.statusCode === 404 && code === "STEP_NOT_FOUND") {
51553
- console.error(`\u274C step "${o.step}" does not exist on run ${runId} \u2014 list its steps: lua workflows status ${runId} --steps`);
51554
- } else if (res.error?.statusCode === 409 && !ctx.json) {
51555
- if (code === "STEP_NOT_PARKED" || code === "step_not_parked") console.error(`\u274C step "${o.step}" is not parked (running, pending or already re-armed) \u2014 nothing to retry`);
51556
- else if (code === "RUN_TERMINAL" || code === "run_terminal") console.error(`\u274C run ${runId} is terminal \u2014 start a new run instead`);
52039
+ if (res.error?.statusCode === 404 && code === "STEP_NOT_FOUND") {
52040
+ detail = {
52041
+ message: `step "${o.step}" does not exist on run ${runId} \u2014 list its steps: lua workflows status ${runId} --steps`
52042
+ };
52043
+ } else if (res.error?.statusCode === 409) {
52044
+ if (code === "STEP_NOT_PARKED" || code === "step_not_parked") detail = {
52045
+ message: `step "${o.step}" is not parked (running, pending or already re-armed) \u2014 nothing to retry`
52046
+ };
52047
+ else if (code === "RUN_TERMINAL" || code === "run_terminal") detail = {
52048
+ message: `run ${runId} is terminal \u2014 start a new run instead`
52049
+ };
51557
52050
  else if (code === "STEP_RETRY_CAP" || code === "step_retry_cap") {
51558
52051
  const cap = res.error;
51559
52052
  const at = typeof cap?.attempt === "number" && typeof cap?.maxAttempts === "number" ? ` (attempt ${cap.attempt} of ${cap.maxAttempts})` : "";
51560
- console.error(`\u274C step "${o.step}" has reached the retry cap${at} \u2014 resolve it or repair the run:
51561
- lua workflows resolve-step ${runId} --step ${o.step} --outcome skip|complete|fail
51562
- to start a repair run instead, decide it from the desktop run page`);
52053
+ detail = {
52054
+ message: `step "${o.step}" has reached the retry cap${at} \u2014 resolve it or repair the run`,
52055
+ hint: `lua workflows resolve-step ${runId} --step ${o.step} --outcome skip|complete|fail
52056
+ to start a repair run instead, decide it from the desktop run page`
52057
+ };
51563
52058
  }
51564
52059
  }
51565
- return apiFailure(ctx, res, "retry-step");
52060
+ return apiFailure(ctx, res, "retry-step", detail);
51566
52061
  }
51567
52062
  emitJson(ctx, res);
51568
52063
  if (!ctx.json) {
@@ -51585,29 +52080,24 @@ __name(refusalIssues, "refusalIssues");
51585
52080
  async function resolveStepCore(ctx, runId, o) {
51586
52081
  const usage = "lua workflows resolve-step <runId> --step <id> --outcome skip|complete|fail [--output <json|@file>] [--note <text>]";
51587
52082
  if (!o.step) {
51588
- console.error(`\u274C --step <id> is required: ${usage}`);
51589
- return WORKFLOW_EXIT.USAGE;
52083
+ throw CliError.usage(`--step <id> is required: ${usage}`);
51590
52084
  }
51591
52085
  const raw = (o.outcome ?? o.action)?.trim().toLowerCase();
51592
52086
  if (raw !== "skip" && raw !== "complete" && raw !== "fail") {
51593
- console.error(`\u274C --outcome must be skip|complete|fail${raw ? ` (got "${raw}")` : ""}: ${usage}`);
51594
- return WORKFLOW_EXIT.USAGE;
52087
+ throw CliError.usage(`--outcome must be skip|complete|fail${raw ? ` (got "${raw}")` : ""}: ${usage}`);
51595
52088
  }
51596
52089
  const outcome = raw;
51597
52090
  let output;
51598
52091
  try {
51599
52092
  if (o.output !== void 0) output = parseJsonOrFile(o.output, "--output");
51600
52093
  } catch (e) {
51601
- console.error(`\u274C ${e.message}`);
51602
- return WORKFLOW_EXIT.USAGE;
52094
+ throw CliError.usage(`${e.message}`);
51603
52095
  }
51604
52096
  if (outcome === "complete" && output === void 0) {
51605
- console.error(`\u274C --outcome complete needs --output <json|@file> (the output the step would have produced): ${usage}`);
51606
- return WORKFLOW_EXIT.USAGE;
52097
+ throw CliError.usage(`--outcome complete needs --output <json|@file> (the output the step would have produced): ${usage}`);
51607
52098
  }
51608
52099
  if (outcome !== "complete" && output !== void 0) {
51609
- console.error(`\u274C --output rides with --outcome complete only (a ${outcome} carries no output)`);
51610
- return WORKFLOW_EXIT.USAGE;
52100
+ throw CliError.usage(`--output rides with --outcome complete only (a ${outcome} carries no output)`);
51611
52101
  }
51612
52102
  const dto = {
51613
52103
  outcome,
@@ -51620,27 +52110,43 @@ async function resolveStepCore(ctx, runId, o) {
51620
52110
  };
51621
52111
  const res = await ctx.api.resolveStep(runId, o.step, dto);
51622
52112
  if (!res.success || !res.data) {
52113
+ let detail = {};
51623
52114
  const err = res.error;
51624
52115
  const code = err?.code ?? err?.error;
51625
52116
  const status = err?.statusCode;
51626
- if (!ctx.json && err) {
52117
+ if (err) {
51627
52118
  if (status === 404 && code === "STEP_NOT_FOUND") {
51628
- console.error(`\u274C step "${o.step}" does not exist on run ${runId} \u2014 list its steps: lua workflows status ${runId} --steps`);
52119
+ detail = {
52120
+ message: `step "${o.step}" does not exist on run ${runId} \u2014 list its steps: lua workflows status ${runId} --steps`
52121
+ };
51629
52122
  } else if (status === 409 && (code === "STEP_NOT_PARKED" || code === "step_not_parked")) {
51630
- console.error(`\u274C step "${o.step}" is not parked (running, pending or already decided) \u2014 nothing to resolve`);
52123
+ detail = {
52124
+ message: `step "${o.step}" is not parked (running, pending or already decided) \u2014 nothing to resolve`
52125
+ };
51631
52126
  } else if (status === 409 && (code === "RUN_TERMINAL" || code === "run_terminal")) {
51632
- console.error(`\u274C run ${runId} is terminal \u2014 start a new run instead`);
52127
+ detail = {
52128
+ message: `run ${runId} is terminal \u2014 start a new run instead`
52129
+ };
51633
52130
  } else if (status === 400 && code === "RESOLVE_OUTPUT_INVALID") {
51634
- console.error(`\u274C --output does not satisfy the outputSchema of step "${o.step}":`);
51635
- for (const i of refusalIssues(err)) console.error(` \u2022 ${i.path ?? "$"}: ${i.message ?? i.code}`);
52131
+ detail = {
52132
+ message: `--output does not satisfy the outputSchema of step "${o.step}"`,
52133
+ issues: refusalIssues(err).map((i) => ({
52134
+ path: i.path ?? "$",
52135
+ message: i.message ?? i.code
52136
+ }))
52137
+ };
51636
52138
  } else if (status === 403 && code === "APPROVAL_REQUIRES_HUMAN") {
51637
- console.error("\u274C resolve-step is a person's decision \u2014 sign in (lua login) instead of an API key");
52139
+ detail = {
52140
+ message: "resolve-step is a person's decision \u2014 sign in (lua login) instead of an API key"
52141
+ };
51638
52142
  } else if (status === 413 && code === "OUTPUT_TOO_LARGE") {
51639
52143
  const { bytes, maxBytes } = err;
51640
- console.error(`\u274C --output is over the cap (${bytes ?? "?"} of ${maxBytes ?? 262144} bytes serialized)`);
52144
+ detail = {
52145
+ message: `--output is over the cap (${bytes ?? "?"} of ${maxBytes ?? 262144} bytes serialized)`
52146
+ };
51641
52147
  }
51642
52148
  }
51643
- return apiFailure(ctx, res, "resolve-step");
52149
+ return apiFailure(ctx, res, "resolve-step", detail);
51644
52150
  }
51645
52151
  emitJson(ctx, res);
51646
52152
  if (!ctx.json) {
@@ -51702,24 +52208,33 @@ async function raiseBudgetCore(ctx, runId, o) {
51702
52208
  }
51703
52209
  const res = await ctx.api.raiseBudget(runId, dto);
51704
52210
  if (!res.success || !res.data) {
52211
+ let detail = {};
51705
52212
  const err = res.error;
51706
52213
  const code = err?.code ?? err?.error;
51707
52214
  const status = err?.statusCode;
51708
- if (!ctx.json && err) {
52215
+ if (err) {
51709
52216
  if (status === 400 && code === "CAP_EXCEEDED") {
51710
52217
  const { cap, value: value3, ceiling } = err;
51711
- console.error(`\u274C ${budgetCapFlag(cap)} ${value3 ?? "?"} is above the org ceiling${ceiling !== void 0 ? ` (${ceiling})` : ""} \u2014 ask an org admin to raise the org cap, or cancel the run`);
52218
+ detail = {
52219
+ message: `${budgetCapFlag(cap)} ${value3 ?? "?"} is above the org ceiling${ceiling !== void 0 ? ` (${ceiling})` : ""} \u2014 ask an org admin to raise the org cap, or cancel the run`
52220
+ };
51712
52221
  } else if (status === 400 && code === "VALIDATION_FAILED") {
51713
- for (const i of refusalIssues(err)) console.error(` ${i.code}${i.path ? ` at ${i.path}` : ""}${i.message ? ` \u2014 ${i.message}` : ""}`);
51714
- console.error(` the raise must be above the current cap \u2014 lua workflows status ${runId} shows it`);
52222
+ detail = {
52223
+ issues: refusalIssues(err),
52224
+ hint: `the raise must be above the current cap \u2014 lua workflows status ${runId} shows it`
52225
+ };
51715
52226
  } else if (status === 409 && code === "BUDGET_NOT_RAISABLE") {
51716
52227
  const { status: runStatus, notRaisableReason } = err;
51717
- console.error(notRaisableReason === "raise_cap" ? `\u274C run ${runId} has reached its raise cap \u2014 cancel it, or start a new run with a larger --budget-credits` : `\u274C run ${runId} is ${runStatus ?? "not parked on its budget"} \u2014 only a run parked on its budget takes a raise`);
52228
+ detail = {
52229
+ message: notRaisableReason === "raise_cap" ? `run ${runId} has reached its raise cap \u2014 cancel it, or start a new run with a larger --budget-credits` : `run ${runId} is ${runStatus ?? "not parked on its budget"} \u2014 only a run parked on its budget takes a raise`
52230
+ };
51718
52231
  } else if (status === 403 && code === "NOT_RUN_CREATOR") {
51719
- console.error("\u274C only the run creator or an org admin can raise a run budget");
52232
+ detail = {
52233
+ message: "only the run creator or an org admin can raise a run budget"
52234
+ };
51720
52235
  }
51721
52236
  }
51722
- return apiFailure(ctx, res, "raise-budget");
52237
+ return apiFailure(ctx, res, "raise-budget", detail);
51723
52238
  }
51724
52239
  emitJson(ctx, res);
51725
52240
  if (!ctx.json) {
@@ -51746,24 +52261,20 @@ function approveResultLine(d) {
51746
52261
  __name(approveResultLine, "approveResultLine");
51747
52262
  async function approveCore(ctx, runId, o) {
51748
52263
  if (!o.approval) {
51749
- console.error("\u274C approve: --approval <id> is required");
51750
- return WORKFLOW_EXIT.USAGE;
52264
+ throw CliError.usage("approve: --approval <id> is required");
51751
52265
  }
51752
52266
  const decision = o.decision ?? "approve";
51753
52267
  if (decision !== "approve" && decision !== "deny") {
51754
- console.error("\u274C approve: --decision must be approve|deny");
51755
- return WORKFLOW_EXIT.USAGE;
52268
+ throw CliError.usage("approve: --decision must be approve|deny");
51756
52269
  }
51757
52270
  let editedPayload;
51758
52271
  try {
51759
52272
  if (o.edit) editedPayload = parseJsonOrFile(o.edit, "--edit");
51760
52273
  } catch (e) {
51761
- console.error(`\u274C ${e.message}`);
51762
- return WORKFLOW_EXIT.USAGE;
52274
+ throw CliError.usage(`${e.message}`);
51763
52275
  }
51764
52276
  if (editedPayload !== void 0 && !o.fingerprint) {
51765
- console.error("\u274C approve: --fingerprint <f> is mandatory with --edit (the payloadFingerprint from approval-payload)");
51766
- return WORKFLOW_EXIT.USAGE;
52277
+ throw CliError.usage("approve: --fingerprint <f> is mandatory with --edit (the payloadFingerprint from approval-payload)");
51767
52278
  }
51768
52279
  const res = await ctx.api.resolveApproval(runId, o.approval, {
51769
52280
  decision,
@@ -51772,15 +52283,22 @@ async function approveCore(ctx, runId, o) {
51772
52283
  expectedFingerprint: o.fingerprint
51773
52284
  });
51774
52285
  if (!res.success || !res.data) {
52286
+ let detail = {};
51775
52287
  const code = res.error?.code ?? res.error?.error;
51776
- if (!ctx.json && res.error?.statusCode === 404 && code === "APPROVAL_NOT_FOUND") {
51777
- console.error(`\u274C approval "${o.approval}" not found on run ${runId} \u2014 pass the pending approval's wfa_\u2026 id: lua workflows status ${runId} --steps --json shows it as the suspended step's suspend.approvalId`);
51778
- } else if (!ctx.json && res.error?.statusCode === 409 && code === "PAYLOAD_MISMATCH") {
51779
- console.error(`\u274C the payload changed since you looked \u2014 refetch: lua workflows approval-payload ${runId} --approval ${o.approval}`);
51780
- } else if (!ctx.json && res.error?.statusCode === 403 && code === "STEP_UP_REQUIRED") {
51781
- console.error("\u274C approve from the desktop with a fresh login (step-up required)");
52288
+ if (res.error?.statusCode === 404 && code === "APPROVAL_NOT_FOUND") {
52289
+ detail = {
52290
+ message: `approval "${o.approval}" not found on run ${runId} \u2014 pass the pending approval's wfa_\u2026 id: lua workflows status ${runId} --steps --json shows it as the suspended step's suspend.approvalId`
52291
+ };
52292
+ } else if (res.error?.statusCode === 409 && code === "PAYLOAD_MISMATCH") {
52293
+ detail = {
52294
+ message: `the payload changed since you looked \u2014 refetch: lua workflows approval-payload ${runId} --approval ${o.approval}`
52295
+ };
52296
+ } else if (res.error?.statusCode === 403 && code === "STEP_UP_REQUIRED") {
52297
+ detail = {
52298
+ message: "approve from the desktop with a fresh login (step-up required)"
52299
+ };
51782
52300
  }
51783
- return apiFailure(ctx, res, "approve");
52301
+ return apiFailure(ctx, res, "approve", detail);
51784
52302
  }
51785
52303
  emitJson(ctx, res);
51786
52304
  if (!ctx.json) {
@@ -51794,8 +52312,7 @@ var indentBlock = /* @__PURE__ */ __name((text, pad2 = " ") => text.split("\
51794
52312
  var APPROVAL_PAYLOAD_PAGE_MAX = 100;
51795
52313
  async function approvalPayloadCore(ctx, runId, o) {
51796
52314
  if (!o.approval) {
51797
- console.error("\u274C approval-payload: --approval <id> is required \u2014 lua workflows approval-payload <runId> --approval <wfa_\u2026> [--path <array.path>]");
51798
- return WORKFLOW_EXIT.USAGE;
52315
+ throw CliError.usage("approval-payload: --approval <id> is required \u2014 lua workflows approval-payload <runId> --approval <wfa_\u2026> [--path <array.path>]");
51799
52316
  }
51800
52317
  let limit;
51801
52318
  try {
@@ -51808,8 +52325,7 @@ async function approvalPayloadCore(ctx, runId, o) {
51808
52325
  throw e;
51809
52326
  }
51810
52327
  if ((o.cursor || limit !== void 0) && !o.path) {
51811
- console.error("\u274C approval-payload: --cursor / --limit page one array \u2014 pass --path <array.path> with them");
51812
- return WORKFLOW_EXIT.USAGE;
52328
+ throw CliError.usage("approval-payload: --cursor / --limit page one array \u2014 pass --path <array.path> with them");
51813
52329
  }
51814
52330
  const res = await ctx.api.getApprovalPayload(runId, o.approval, {
51815
52331
  path: o.path,
@@ -51817,13 +52333,18 @@ async function approvalPayloadCore(ctx, runId, o) {
51817
52333
  limit
51818
52334
  });
51819
52335
  if (!res.success || !res.data) {
52336
+ let detail = {};
51820
52337
  const code = res.error?.code ?? res.error?.error;
51821
- if (!ctx.json && res.error?.statusCode === 404 && code === "APPROVAL_NOT_FOUND") {
51822
- console.error(`\u274C approval "${o.approval}" not found on run ${runId} \u2014 lua workflows status ${runId} --steps --json shows the suspended step's suspend.approvalId`);
51823
- } else if (!ctx.json && res.error?.statusCode === 413 && code === "PAYLOAD_PAGE_REQUIRED") {
51824
- console.error("\u274C the payload is too large to read whole \u2014 page an array with --path <array.path> [--limit <n>] [--cursor <c>]");
52338
+ if (res.error?.statusCode === 404 && code === "APPROVAL_NOT_FOUND") {
52339
+ detail = {
52340
+ message: `approval "${o.approval}" not found on run ${runId} \u2014 lua workflows status ${runId} --steps --json shows the suspended step's suspend.approvalId`
52341
+ };
52342
+ } else if (res.error?.statusCode === 413 && code === "PAYLOAD_PAGE_REQUIRED") {
52343
+ detail = {
52344
+ message: "the payload is too large to read whole \u2014 page an array with --path <array.path> [--limit <n>] [--cursor <c>]"
52345
+ };
51825
52346
  }
51826
- return apiFailure(ctx, res, "approval-payload");
52347
+ return apiFailure(ctx, res, "approval-payload", detail);
51827
52348
  }
51828
52349
  emitJson(ctx, res);
51829
52350
  if (!ctx.json) {
@@ -51887,16 +52408,9 @@ function reservedSecretKeyPaths(value3, path25 = "", depth = 0, out = []) {
51887
52408
  __name(reservedSecretKeyPaths, "reservedSecretKeyPaths");
51888
52409
  async function signalCore(ctx, runId, name, o) {
51889
52410
  if (!name) {
51890
- console.error("\u274C signal: usage \u2014 lua workflows signal <runId> <name> [--payload <json|@file>]");
51891
- return WORKFLOW_EXIT.USAGE;
51892
- }
51893
- let payload;
51894
- try {
51895
- if (o.payload) payload = parseJsonOrFile(o.payload, "--payload");
51896
- } catch (e) {
51897
- console.error(`\u274C ${e.message}`);
51898
- return WORKFLOW_EXIT.USAGE;
52411
+ throw CliError.usage("signal: a signal name is required", "lua workflows signal <runId> <name> [--payload <json|@file>]");
51899
52412
  }
52413
+ const payload = parsedOrUsage(() => o.payload ? parseJsonOrFile(o.payload, "--payload") : void 0);
51900
52414
  const reserved = reservedSecretKeyPaths(payload);
51901
52415
  if (reserved.length > 0) {
51902
52416
  const many = reserved.length > 1;
@@ -51908,22 +52422,24 @@ async function signalCore(ctx, runId, name, o) {
51908
52422
  });
51909
52423
  if (!res.success || !res.data) {
51910
52424
  const code = res.error?.code ?? res.error?.error;
51911
- if (!ctx.json && res.error?.statusCode === 400 && code === "SIGNAL_SCHEMA_INVALID") {
51912
- const where = res.error?.stepId ? `step "${res.error.stepId}"` : "the waiting step";
51913
- console.error(`\u274C signal "${name}" rejected \u2014 the payload does not match the schema ${where} declares:`);
51914
- for (const issue of res.error?.issues ?? []) {
51915
- console.error(` \u2022 ${issue.path || "/"}: ${issue.message ?? "invalid"}`);
51916
- }
51917
- console.error(" fix the payload and send it again (a --dedupe-key is not consumed by a rejected signal)");
51918
- } else if (!ctx.json && res.error?.statusCode === 422 && code === "SIGNAL_SCHEMA_UNCOMPILABLE") {
51919
- const where = res.error?.stepId ? `step "${res.error.stepId}"` : "the waiting step";
51920
- console.error(`\u274C signal "${name}" could not be checked \u2014 the schema ${where} declares does not compile:`);
51921
- for (const issue of res.error?.issues ?? []) {
51922
- console.error(` \u2022 ${issue.message ?? "schema does not compile"}`);
51923
- }
51924
- console.error(` this is a workflow definition defect, not a payload problem \u2014 fix the \`schema\` on the waitForSignal node in the workflow source, push a new version, then send the signal again`);
52425
+ const where = res.error?.stepId ? `step "${res.error.stepId}"` : "the waiting step";
52426
+ let detail = {};
52427
+ if (res.error?.statusCode === 400 && code === "SIGNAL_SCHEMA_INVALID") {
52428
+ detail = {
52429
+ message: `signal "${name}" rejected \u2014 the payload does not match the schema ${where} declares`,
52430
+ issues: res.error?.issues,
52431
+ hint: "fix the payload and send it again (a --dedupe-key is not consumed by a rejected signal)"
52432
+ };
52433
+ } else if (res.error?.statusCode === 422 && code === "SIGNAL_SCHEMA_UNCOMPILABLE") {
52434
+ detail = {
52435
+ message: `signal "${name}" could not be checked \u2014 the schema ${where} declares does not compile`,
52436
+ issues: (res.error?.issues ?? []).map((issue) => ({
52437
+ message: issue.message ?? "schema does not compile"
52438
+ })),
52439
+ hint: "this is a workflow definition defect, not a payload problem \u2014 fix the `schema` on the waitForSignal node in the workflow source, push a new version, then send the signal again"
52440
+ };
51925
52441
  }
51926
- return apiFailure(ctx, res, "signal");
52442
+ return apiFailure(ctx, res, "signal", detail);
51927
52443
  }
51928
52444
  emitJson(ctx, res);
51929
52445
  const r = res.data;
@@ -51936,8 +52452,7 @@ async function signalCore(ctx, runId, name, o) {
51936
52452
  __name(signalCore, "signalCore");
51937
52453
  async function replayCore(ctx, runId, o) {
51938
52454
  if (!o.local) {
51939
- console.error("\u274C replay: only `--local` is available (server-side replay is not a v1 route)");
51940
- return WORKFLOW_EXIT.USAGE;
52455
+ throw CliError.usage("replay: only `--local` is available (server-side replay is not a v1 route)");
51941
52456
  }
51942
52457
  const runRes = await ctx.api.getRun(runId);
51943
52458
  if (!runRes.success || !runRes.data) return apiFailure(ctx, runRes, "replay");
@@ -51946,8 +52461,7 @@ async function replayCore(ctx, runId, o) {
51946
52461
  try {
51947
52462
  manifestWorkflows = getPrimitivesByKind(loadManifest(), PrimitiveKind.WORKFLOW);
51948
52463
  } catch (e) {
51949
- console.error(`\u274C replay --local needs a compiled project: ${e.message}`);
51950
- return WORKFLOW_EXIT.USAGE;
52464
+ throw CliError.usage(`replay --local needs a compiled project: ${e.message}`);
51951
52465
  }
51952
52466
  const wf = (o.workflowName ? manifestWorkflows.find((w) => w.name === o.workflowName) : void 0) ?? manifestWorkflows.find((w) => w.graphHash === run.graphHash);
51953
52467
  if (!wf) {
@@ -52193,11 +52707,11 @@ async function deleteRunCore(ctx, runId, o) {
52193
52707
  const res = await ctx.api.eraseRun(runId);
52194
52708
  if (!res.success || !res.data) {
52195
52709
  const code = res.error?.code ?? res.error?.error;
52196
- if (!ctx.json && res.error?.statusCode === 409 && code === "RUN_NOT_TERMINAL") {
52710
+ if (res.error?.statusCode === 409 && code === "RUN_NOT_TERMINAL") {
52197
52711
  const status = res.error.status;
52198
- console.error(`\u274C run is \`${status ?? "live"}\`; cancel it first: lua workflows cancel ${runId}`);
52199
- emitJson(ctx, res);
52200
- return WORKFLOW_EXIT.API;
52712
+ return apiFailure(ctx, res, "delete-run", {
52713
+ message: `run is \`${status ?? "live"}\`; cancel it first: lua workflows cancel ${runId}`
52714
+ });
52201
52715
  }
52202
52716
  return apiFailure(ctx, res, "delete-run");
52203
52717
  }
@@ -52315,17 +52829,14 @@ async function archiveRunsCore(ctx, o) {
52315
52829
  const now = Date.now();
52316
52830
  const since = parseSinceFlag(o.since, now);
52317
52831
  if (!Number.isFinite(since)) {
52318
- console.error("\u274C archive-runs: --since <iso|dur> is required (e.g. --since 8d)");
52319
- return WORKFLOW_EXIT.USAGE;
52832
+ throw CliError.usage("archive-runs: --since <iso|dur> is required (e.g. --since 8d)");
52320
52833
  }
52321
52834
  const until = o.until ? Date.parse(o.until) : void 0;
52322
52835
  if (o.until && !Number.isFinite(until)) {
52323
- console.error(`\u274C archive-runs: --until is not an ISO timestamp ("${o.until}")`);
52324
- return WORKFLOW_EXIT.USAGE;
52836
+ throw CliError.usage(`archive-runs: --until is not an ISO timestamp ("${o.until}")`);
52325
52837
  }
52326
52838
  if (!o.out) {
52327
- console.error("\u274C archive-runs: --out <dir> is required");
52328
- return WORKFLOW_EXIT.USAGE;
52839
+ throw CliError.usage("archive-runs: --out <dir> is required");
52329
52840
  }
52330
52841
  if (/^(s3|gs):\/\//.test(o.out)) {
52331
52842
  console.error(`\u274C archive-runs: remote sinks (${o.out}) need the org storage connection (--connection) \u2014 not wired in this build; archive to a local dir and sync it`);
@@ -52589,11 +53100,14 @@ async function workspaceCore(ctx, runId, o) {
52589
53100
  note: o.note
52590
53101
  } : {});
52591
53102
  if (!res2.success || !res2.data) {
52592
- if (res2.error?.statusCode === 409 && !ctx.json) {
53103
+ let detail = {};
53104
+ if (res2.error?.statusCode === 409) {
52593
53105
  const stepId = res2.error.stepId;
52594
- console.error(`\u274C Workspace of ${runId} is in use${stepId ? ` by step ${stepId}` : ""} \u2014 cancel the run or wait for the step to finish`);
53106
+ detail = {
53107
+ message: `Workspace of ${runId} is in use${stepId ? ` by step ${stepId}` : ""} \u2014 cancel the run or wait for the step to finish`
53108
+ };
52595
53109
  }
52596
- return apiFailure(ctx, res2, "workspace");
53110
+ return apiFailure(ctx, res2, "workspace", detail);
52597
53111
  }
52598
53112
  emitJson(ctx, res2);
52599
53113
  if (!ctx.json) {
@@ -52674,14 +53188,12 @@ function printJobHeader(v) {
52674
53188
  __name(printJobHeader, "printJobHeader");
52675
53189
  async function jobLogsCore(ctx, runId, stepId, o) {
52676
53190
  if (!stepId) {
52677
- console.error(`\u274C job-logs: a step id is required \u2014 lua workflows job-logs ${runId} <stepId>`);
52678
- return WORKFLOW_EXIT.USAGE;
53191
+ throw CliError.usage(`job-logs: a step id is required \u2014 lua workflows job-logs ${runId} <stepId>`);
52679
53192
  }
52680
53193
  const attempt = o.attempt !== void 0 && o.attempt !== "" ? Number(o.attempt) : void 0;
52681
53194
  const tail = o.tail !== void 0 && o.tail !== "" ? Number(o.tail) : JOB_LOG_TAIL_DEFAULT;
52682
53195
  if (attempt !== void 0 && !(Number.isInteger(attempt) && attempt >= 1) || !(Number.isInteger(tail) && tail >= 1 && tail <= JOB_LOG_TAIL_MAX)) {
52683
- console.error(`\u274C job-logs: --attempt must be \u2265 1 and --tail an integer in 1..${JOB_LOG_TAIL_MAX}`);
52684
- return WORKFLOW_EXIT.USAGE;
53196
+ throw CliError.usage(`job-logs: --attempt must be \u2265 1 and --tail an integer in 1..${JOB_LOG_TAIL_MAX}`);
52685
53197
  }
52686
53198
  let printed = 0;
52687
53199
  let first = true;
@@ -52691,18 +53203,18 @@ async function jobLogsCore(ctx, runId, stepId, o) {
52691
53203
  tail
52692
53204
  });
52693
53205
  if (!res.success || !res.data) {
52694
- if (res.error?.statusCode === 404 && res.error?.code === "JOB_NOT_FOUND" && !ctx.json) {
52695
- console.error(`\u274C ${stepId} is not a Job-tier step of ${runId} (try: lua workflows jobs ${runId})`);
52696
- }
52697
- if (res.error?.statusCode === 404 && res.error?.code === "JOB_LOGS_NOT_PERSISTED") {
52698
- emitJson(ctx, res);
52699
- if (!ctx.json) {
52700
- console.error(`\u274C job-logs: ${res.error.message}`);
52701
- writeInfo(` lua workflows status ${runId} --steps shows the step error; --attempt <n> reads another attempt.`);
52702
- }
52703
- return WORKFLOW_EXIT.NOT_FOUND;
53206
+ let detail = {};
53207
+ if (res.error?.statusCode === 404 && res.error?.code === "JOB_NOT_FOUND") {
53208
+ detail = {
53209
+ message: `${stepId} is not a Job-tier step of ${runId} (try: lua workflows jobs ${runId})`
53210
+ };
53211
+ } else if (res.error?.statusCode === 404 && res.error?.code === "JOB_LOGS_NOT_PERSISTED") {
53212
+ detail = {
53213
+ message: `job-logs: ${res.error.message}`,
53214
+ hint: `lua workflows status ${runId} --steps shows the step error; --attempt <n> reads another attempt.`
53215
+ };
52704
53216
  }
52705
- return apiFailure(ctx, res, "job-logs");
53217
+ return apiFailure(ctx, res, "job-logs", detail);
52706
53218
  }
52707
53219
  const v = res.data;
52708
53220
  if (ctx.json && !o.follow) {
@@ -52965,38 +53477,54 @@ function subVerb(group, noun, raw, verbs) {
52965
53477
  }
52966
53478
  __name(subVerb, "subVerb");
52967
53479
  function goalFailure(ctx, res, verb, ref) {
53480
+ let detail = {};
52968
53481
  const err = res.error;
52969
53482
  const code = err?.code ?? err?.error;
52970
53483
  const status = err?.statusCode;
52971
- if (!ctx.json && err) {
53484
+ if (err) {
52972
53485
  if (status === 409 && code === "GOAL_NOT_ACTIVE") {
52973
53486
  const now = err.status ?? "not active";
52974
53487
  const rule = verb === "goals resume" ? "only a paused goal resumes" : verb === "goals pause" ? "only an active goal pauses" : "a done/closed goal stays closed (a lingering cadence Job goes with `lua workflows schedules delete <jobId>`)";
52975
- console.error(`\u274C goal ${ref ?? ""} is ${now} \u2014 ${rule}`);
52976
- if (verb === "goals resume" && now === "paused") console.error(` ${goalRaiseHint(ref ?? "<goalId>")}`);
53488
+ detail = {
53489
+ message: `goal ${ref ?? ""} is ${now} \u2014 ${rule}`,
53490
+ hint: verb === "goals resume" && now === "paused" ? goalRaiseHint(ref ?? "<goalId>") : void 0
53491
+ };
52977
53492
  } else if (status === 400 && code === "GOAL_RAISE_BELOW_SPENT") {
52978
53493
  const { field, value: value3, spent } = err;
52979
53494
  const flag = field === "maxRuns" ? "--max-runs" : "--max-credits";
52980
53495
  const used = field === "maxRuns" ? "run(s) already used" : "credit(s) already spent";
52981
- console.error(`\u274C ${flag} ${value3 ?? "?"} is not above the ${spent ?? "?"} ${used} \u2014 raise it past ${spent ?? "?"}`);
53496
+ detail = {
53497
+ message: `${flag} ${value3 ?? "?"} is not above the ${spent ?? "?"} ${used} \u2014 raise it past ${spent ?? "?"}`
53498
+ };
52982
53499
  } else if (status === 409 && code === "GOAL_VERSION_CONFLICT") {
52983
53500
  const at = err.updatedAt;
52984
- console.error(`\u274C goal ${ref ?? ""} changed since it was read${at ? ` (updatedAt ${at})` : ""} \u2014 re-run lua workflows goals get ${ref ?? "<goalId>"} and retry${at ? ` with --if-match ${at}` : ""}`);
53501
+ detail = {
53502
+ message: `goal ${ref ?? ""} changed since it was read${at ? ` (updatedAt ${at})` : ""} \u2014 re-run lua workflows goals get ${ref ?? "<goalId>"} and retry${at ? ` with --if-match ${at}` : ""}`
53503
+ };
52985
53504
  } else if (status === 409 && code === "GOAL_CAP") {
52986
- console.error(`\u274C goal cap reached (${err.cap ?? "?"} per agent) \u2014 close or finish one first: lua workflows goals list --status active`);
53505
+ detail = {
53506
+ message: `goal cap reached (${err.cap ?? "?"} per agent) \u2014 close or finish one first: lua workflows goals list --status active`
53507
+ };
52987
53508
  } else if (status === 400 && code === "VALIDATION_FAILED") {
52988
- const issues = err.issues ?? [];
52989
- for (const i of issues) console.error(` ${i.code}${i.path ? ` at ${i.path}` : ""}${i.message ? ` \u2014 ${i.message}` : ""}`);
53509
+ detail = {
53510
+ issues: refusalIssues(err)
53511
+ };
52990
53512
  } else if (status === 400 && code === "GOAL_MAX_RUNS_INVALID") {
52991
- console.error(`\u274C --max-runs must be 1..${GOAL_MAX_RUNS_CAP}`);
53513
+ detail = {
53514
+ message: `--max-runs must be 1..${GOAL_MAX_RUNS_CAP}`
53515
+ };
52992
53516
  } else if (status === 400 && code === "WORKFLOW_NOT_ON_AGENT") {
52993
- console.error(`\u274C that workflow belongs to another agent \u2014 goals are scoped to ${ctx.agentId}`);
53517
+ detail = {
53518
+ message: `that workflow belongs to another agent \u2014 goals are scoped to ${ctx.agentId}`
53519
+ };
52994
53520
  } else if (status === 409 && code === "GOAL_SCHEDULE") {
52995
53521
  const goalId = err.goalId ?? "?";
52996
- console.error(`\u274C GOAL_SCHEDULE: ${goalScheduleRefusal(ref ?? "<jobId>", goalId)}`);
53522
+ detail = {
53523
+ message: `GOAL_SCHEDULE: ${goalScheduleRefusal(ref ?? "<jobId>", goalId)}`
53524
+ };
52997
53525
  }
52998
53526
  }
52999
- return apiFailure(ctx, res, verb);
53527
+ return apiFailure(ctx, res, verb, detail);
53000
53528
  }
53001
53529
  __name(goalFailure, "goalFailure");
53002
53530
  async function goalsCore(ctx, target, extra, o) {
@@ -53005,8 +53533,7 @@ async function goalsCore(ctx, target, extra, o) {
53005
53533
  if (verb === "list") return goalsListCore(ctx, o, extra);
53006
53534
  if (verb === "create") return goalsCreateCore(ctx, o, extra);
53007
53535
  if (!extra) {
53008
- console.error(`\u274C goals ${verb}: a goal id is required \u2014 lua workflows goals ${verb} <goalId>`);
53009
- return WORKFLOW_EXIT.USAGE;
53536
+ throw CliError.usage(`goals ${verb}: a goal id is required \u2014 lua workflows goals ${verb} <goalId>`);
53010
53537
  }
53011
53538
  if (verb === "get") return goalsGetCore(ctx, extra);
53012
53539
  if (verb === "edit") return goalsEditCore(ctx, extra, o);
@@ -53016,8 +53543,7 @@ async function goalsCore(ctx, target, extra, o) {
53016
53543
  __name(goalsCore, "goalsCore");
53017
53544
  async function goalsListCore(ctx, o, positional) {
53018
53545
  if (o.status && !GOAL_STATUSES.includes(o.status)) {
53019
- console.error(`\u274C goals list: --status must be ${GOAL_STATUSES.join("|")} (got "${o.status}")`);
53020
- return WORKFLOW_EXIT.USAGE;
53546
+ throw CliError.usage(`goals list: --status must be ${GOAL_STATUSES.join("|")} (got "${o.status}")`);
53021
53547
  }
53022
53548
  let limit;
53023
53549
  try {
@@ -53026,8 +53552,7 @@ async function goalsListCore(ctx, o, positional) {
53026
53552
  max: GOAL_PAGE_LIMIT
53027
53553
  });
53028
53554
  } catch (e) {
53029
- console.error(`\u274C ${e.message}`);
53030
- return WORKFLOW_EXIT.USAGE;
53555
+ throw CliError.usage(`${e.message}`);
53031
53556
  }
53032
53557
  const list = await loadWorkflowList(ctx);
53033
53558
  if (!list) return WORKFLOW_EXIT.API;
@@ -53188,8 +53713,7 @@ async function goalsEditCore(ctx, goalId, o) {
53188
53713
  if (Object.keys(dto).filter((k) => k !== "ifMatch").length === 0) throw usage("nothing to change \u2014 pass --objective, --judge-predicate, --cadence | --every, --max-runs, --max-credits <n|none> or --note");
53189
53714
  } catch (e) {
53190
53715
  if (e instanceof WorkflowLocalUsageError) {
53191
- console.error(`\u274C goals edit: ${e.message}`);
53192
- return WORKFLOW_EXIT.USAGE;
53716
+ throw CliError.usage(`goals edit: ${e.message}`);
53193
53717
  }
53194
53718
  throw e;
53195
53719
  }
@@ -53221,8 +53745,7 @@ async function goalsRaiseCore(ctx, goalId, o) {
53221
53745
  if (ifMatch !== void 0) dto.ifMatch = ifMatch;
53222
53746
  } catch (e) {
53223
53747
  if (e instanceof WorkflowLocalUsageError) {
53224
- console.error(`\u274C goals raise: ${e.message}`);
53225
- return WORKFLOW_EXIT.USAGE;
53748
+ throw CliError.usage(`goals raise: ${e.message}`);
53226
53749
  }
53227
53750
  throw e;
53228
53751
  }
@@ -53308,20 +53831,16 @@ __name(parseCadenceFlag, "parseCadenceFlag");
53308
53831
  async function goalsCreateCore(ctx, o, positional) {
53309
53832
  const target = positional ?? o.workflowName ?? o.workflow;
53310
53833
  if (!target) {
53311
- console.error("\u274C goals create: a workflow is required \u2014 lua workflows goals create <workflow> (or -i <workflow>)");
53312
- return WORKFLOW_EXIT.USAGE;
53834
+ throw CliError.usage("goals create: a workflow is required \u2014 lua workflows goals create <workflow> (or -i <workflow>)");
53313
53835
  }
53314
53836
  if (!o.objective?.trim()) {
53315
- console.error("\u274C goals create: --objective <text> is required");
53316
- return WORKFLOW_EXIT.USAGE;
53837
+ throw CliError.usage("goals create: --objective <text> is required");
53317
53838
  }
53318
53839
  if (o.judgeAgent === "") {
53319
- console.error("\u274C goals create: --judge-agent is empty \u2014 an unquoted $self is expanded by the shell to nothing; write '$self' (quoted) or self");
53320
- return WORKFLOW_EXIT.USAGE;
53840
+ throw CliError.usage("goals create: --judge-agent is empty \u2014 an unquoted $self is expanded by the shell to nothing; write '$self' (quoted) or self");
53321
53841
  }
53322
53842
  if (!o.judgePredicate && !o.judgeAgent) {
53323
- console.error("\u274C goals create: one of --judge-predicate <spec> | --judge-agent <agentId|'$self'> is required");
53324
- return WORKFLOW_EXIT.USAGE;
53843
+ throw CliError.usage("goals create: one of --judge-predicate <spec> | --judge-agent <agentId|'$self'> is required");
53325
53844
  }
53326
53845
  const usage = /* @__PURE__ */ __name((m) => new WorkflowLocalUsageError("usage", m), "usage");
53327
53846
  let dto;
@@ -53381,8 +53900,7 @@ async function goalsCreateCore(ctx, o, positional) {
53381
53900
  };
53382
53901
  } catch (e) {
53383
53902
  if (e instanceof WorkflowLocalUsageError) {
53384
- console.error(`\u274C ${e.message}`);
53385
- return WORKFLOW_EXIT.USAGE;
53903
+ throw CliError.usage(`${e.message}`);
53386
53904
  }
53387
53905
  throw e;
53388
53906
  }
@@ -53424,8 +53942,7 @@ async function schedulesCore(ctx, target, extra, o) {
53424
53942
  if (verb === "list") return schedulesListCore(ctx, o);
53425
53943
  if (verb === "create") return schedulesCreateCore(ctx, o, extra);
53426
53944
  if (!extra) {
53427
- console.error(`\u274C schedules ${verb}: a schedule (job) id is required \u2014 lua workflows schedules ${verb} <jobId>`);
53428
- return WORKFLOW_EXIT.USAGE;
53945
+ throw CliError.usage(`schedules ${verb}: a schedule (job) id is required \u2014 lua workflows schedules ${verb} <jobId>`);
53429
53946
  }
53430
53947
  if (verb === "delete") return schedulesDeleteCore(ctx, extra, o);
53431
53948
  return schedulesPatchCore(ctx, extra, verb, o);
@@ -53480,17 +53997,19 @@ async function schedulesDeleteCore(ctx, jobId, o) {
53480
53997
  const goalRes = await ctx.api.getGoal(goalId);
53481
53998
  if (goalRes.success && goalRes.data) goalStatus = goalRes.data.status;
53482
53999
  else if (goalRes.error?.statusCode !== 404) {
53483
- if (!ctx.json) console.error(`\u274C cannot prove goal ${goalId} of ${jobId} has ended (goal unreadable) \u2014 refusing to delete`);
53484
- return apiFailure(ctx, goalRes, "schedules delete");
54000
+ return apiFailure(ctx, goalRes, "schedules delete", {
54001
+ message: `cannot prove goal ${goalId} of ${jobId} has ended (goal unreadable) \u2014 refusing to delete`
54002
+ });
53485
54003
  }
53486
54004
  } else {
53487
54005
  const goals = await loadGoals(ctx, row2.workflowId);
53488
54006
  if (goals.error) {
53489
- if (!ctx.json) console.error(`\u274C cannot prove ${jobId} is not a goal's cadence (goals unavailable) \u2014 refusing to delete`);
53490
54007
  return apiFailure(ctx, {
53491
54008
  success: false,
53492
54009
  error: goals.error
53493
- }, "schedules delete");
54010
+ }, "schedules delete", {
54011
+ message: `cannot prove ${jobId} is not a goal's cadence (goals unavailable) \u2014 refusing to delete`
54012
+ });
53494
54013
  }
53495
54014
  const owner = goals.items.find((g) => g.jobId === jobId);
53496
54015
  goalId = owner?.goalId;
@@ -53541,8 +54060,7 @@ var SCHEDULE_NOTIFY = [
53541
54060
  async function schedulesCreateCore(ctx, o, positional) {
53542
54061
  const target = positional ?? o.workflowName ?? o.workflow;
53543
54062
  if (!target) {
53544
- console.error('\u274C schedules create: a workflow is required \u2014 lua workflows schedules create <workflow> --cadence "0 9 * * 1" --timezone Europe/London');
53545
- return WORKFLOW_EXIT.USAGE;
54063
+ throw CliError.usage('schedules create: a workflow is required \u2014 lua workflows schedules create <workflow> --cadence "0 9 * * 1" --timezone Europe/London');
53546
54064
  }
53547
54065
  let draft;
53548
54066
  try {
@@ -53669,11 +54187,12 @@ async function schedulesPatchCore(ctx, jobId, verb, o) {
53669
54187
  if (!rowRes.success || !rowRes.data) return apiFailure(ctx, rowRes, label);
53670
54188
  const owner = await scheduleGoalOwner(ctx, rowRes.data);
53671
54189
  if (owner.error) {
53672
- if (!ctx.json) console.error(`\u274C cannot prove ${jobId} is not a goal's cadence (goals unavailable) \u2014 refusing to ${verb}`);
53673
54190
  return apiFailure(ctx, {
53674
54191
  success: false,
53675
54192
  error: owner.error
53676
- }, label);
54193
+ }, label, {
54194
+ message: `cannot prove ${jobId} is not a goal's cadence (goals unavailable) \u2014 refusing to ${verb}`
54195
+ });
53677
54196
  }
53678
54197
  if (owner.goalId) {
53679
54198
  const message = `Schedule ${jobId} is the cadence of goal ${owner.goalId} and was NOT changed. Pause or resume the goal instead: lua workflows goals pause ${owner.goalId} / lua workflows goals resume ${owner.goalId} \u2014 a goal's Job follows its goal, never the other way round.`;
@@ -55353,6 +55872,7 @@ init_cli();
55353
55872
  // src/api/marketplace.api.service.ts
55354
55873
  init_constants();
55355
55874
  init_lua_fetch();
55875
+ init_http_client();
55356
55876
  init_request_credential();
55357
55877
  var MarketplaceApiService = class {
55358
55878
  static {
@@ -55377,8 +55897,7 @@ var MarketplaceApiService = class {
55377
55897
  headers
55378
55898
  });
55379
55899
  if (!response.ok) {
55380
- const errorText = await response.text();
55381
- throw new Error(`API Error: ${response.status} ${response.statusText} - ${errorText}`);
55900
+ throw await refusalFromResponse(response);
55382
55901
  }
55383
55902
  if (response.status === 204) {
55384
55903
  return null;
@@ -55501,6 +56020,7 @@ import { readFileSync as readFileSync14 } from "fs";
55501
56020
  // src/api/template.api.service.ts
55502
56021
  init_constants();
55503
56022
  init_lua_fetch();
56023
+ init_http_client();
55504
56024
  init_request_credential();
55505
56025
  var TemplateApiService = class {
55506
56026
  static {
@@ -55523,8 +56043,7 @@ var TemplateApiService = class {
55523
56043
  headers
55524
56044
  });
55525
56045
  if (!response.ok) {
55526
- const errorText = await response.text();
55527
- throw new Error(`API Error: ${response.status} ${response.statusText} - ${errorText}`);
56046
+ throw await refusalFromResponse(response);
55528
56047
  }
55529
56048
  if (response.status === 204) {
55530
56049
  return null;
@@ -55747,6 +56266,11 @@ function sleep2(ms) {
55747
56266
  return new Promise((resolve7) => setTimeout(resolve7, ms));
55748
56267
  }
55749
56268
  __name(sleep2, "sleep");
56269
+ function missingVersionOrRethrow(error) {
56270
+ if (CliError.isCliError(error) && error.statusCode === 404) return null;
56271
+ throw error;
56272
+ }
56273
+ __name(missingVersionOrRethrow, "missingVersionOrRethrow");
55750
56274
  async function resolveTemplateId(templateApi, options, message) {
55751
56275
  if (options.templateId) return options.templateId;
55752
56276
  writeProgress("\u{1F504} Loading your templates...");
@@ -56123,8 +56647,8 @@ async function templateViewAction(templateApi, options) {
56123
56647
  if (!Number.isFinite(versionNum) || versionNum <= 0) {
56124
56648
  throw new Error(`Invalid --version "${options.version}": must be a positive integer.`);
56125
56649
  }
56126
- const version = await templateApi.getVersion(templateId, versionNum).catch(() => null);
56127
- if (!version) throw new Error(`Version v${versionNum} not found for this template.`);
56650
+ const version = await templateApi.getVersion(templateId, versionNum).catch(missingVersionOrRethrow);
56651
+ if (!version) throw CliError.notFound(`Version v${versionNum} not found for this template.`);
56128
56652
  if (options.json) {
56129
56653
  console.log(JSON.stringify(version, null, 2));
56130
56654
  return;
@@ -56288,8 +56812,8 @@ async function templateApplyAction(templateApi, options) {
56288
56812
  throw new Error("This template has no published versions. Run `lua marketplace template publish` first.");
56289
56813
  }
56290
56814
  const versionNum = options.version ? Number.parseInt(options.version, 10) : template3.latestVersion;
56291
- const versionObj = await templateApi.getVersion(templateId, versionNum).catch(() => null);
56292
- if (!versionObj) throw new Error(`Version v${versionNum} not found for this template.`);
56815
+ const versionObj = await templateApi.getVersion(templateId, versionNum).catch(missingVersionOrRethrow);
56816
+ if (!versionObj) throw CliError.notFound(`Version v${versionNum} not found for this template.`);
56293
56817
  if (!options.force) {
56294
56818
  console.log(`
56295
56819
  Apply plan:`);
@@ -56540,45 +57064,57 @@ var SKILL_ACTION_LABELS = {
56540
57064
  org: "View this organization's owned skills"
56541
57065
  };
56542
57066
  async function marketplaceCommand(noun, action, options = {}) {
56543
- return withErrorHandling(async () => {
56544
- let domain;
56545
- if (noun) {
56546
- domain = validateOrSuggest("marketplace.noun", noun);
56547
- } else {
56548
- const domainAnswer = await safePrompt([
56549
- {
56550
- type: "list",
56551
- name: "domain",
56552
- message: "What would you like to browse?",
56553
- choices: [
56554
- {
56555
- name: "Skills",
56556
- value: "skill"
56557
- },
56558
- {
56559
- name: "Agent templates",
56560
- value: "template"
56561
- },
56562
- {
56563
- name: "Exit",
56564
- value: "exit"
56565
- }
56566
- ]
56567
- }
56568
- ]);
56569
- if (!domainAnswer || domainAnswer.domain === "exit") {
56570
- console.log("\n\u{1F44B} Goodbye!\n");
56571
- return;
56572
- }
56573
- domain = domainAnswer.domain;
56574
- }
56575
- if (domain === "template") {
56576
- return templateCommand(action, options);
57067
+ return withErrorHandling(
57068
+ async () => {
57069
+ await marketplaceDispatch(noun, action, options);
57070
+ },
57071
+ "marketplace",
57072
+ // LUA-803: under `--json` an escaped refusal (a template 403 / 404, a network failure) is the typed envelope on
57073
+ // stdout, not the `✖` line.
57074
+ {
57075
+ json: /* @__PURE__ */ __name(() => !!options.json, "json")
56577
57076
  }
56578
- return skillMarketplaceCommand(action, options);
56579
- }, "marketplace");
57077
+ );
56580
57078
  }
56581
57079
  __name(marketplaceCommand, "marketplaceCommand");
57080
+ async function marketplaceDispatch(noun, action, options) {
57081
+ let domain;
57082
+ if (noun) {
57083
+ domain = validateOrSuggest("marketplace.noun", noun);
57084
+ } else {
57085
+ const domainAnswer = await safePrompt([
57086
+ {
57087
+ type: "list",
57088
+ name: "domain",
57089
+ message: "What would you like to browse?",
57090
+ choices: [
57091
+ {
57092
+ name: "Skills",
57093
+ value: "skill"
57094
+ },
57095
+ {
57096
+ name: "Agent templates",
57097
+ value: "template"
57098
+ },
57099
+ {
57100
+ name: "Exit",
57101
+ value: "exit"
57102
+ }
57103
+ ]
57104
+ }
57105
+ ]);
57106
+ if (!domainAnswer || domainAnswer.domain === "exit") {
57107
+ console.log("\n\u{1F44B} Goodbye!\n");
57108
+ return;
57109
+ }
57110
+ domain = domainAnswer.domain;
57111
+ }
57112
+ if (domain === "template") {
57113
+ return templateCommand(action, options);
57114
+ }
57115
+ return skillMarketplaceCommand(action, options);
57116
+ }
57117
+ __name(marketplaceDispatch, "marketplaceDispatch");
56582
57118
  async function skillMarketplaceCommand(action, options = {}) {
56583
57119
  const { config, apiKey } = await initializeCommand();
56584
57120
  const marketplaceApi = new MarketplaceApiService(apiKey, config.agent?.orgId);
@@ -56735,7 +57271,7 @@ async function listSkillNonInteractive(marketplaceApi, config, apiKey, options)
56735
57271
  writeProgress("\u{1F504} Verifying skill...");
56736
57272
  const agentSkillsResponse = await skillApi.getSkills();
56737
57273
  if (!agentSkillsResponse.success || !agentSkillsResponse.data) {
56738
- throw CliError.fromStatus(agentSkillsResponse.error?.statusCode, `Failed to fetch agent skills: ${agentSkillsResponse.error?.message ?? agentSkillsResponse.message ?? "Unknown error"}`);
57274
+ throw CliError.fromStatus(agentSkillsResponse.error?.statusCode, `Failed to fetch agent skills: ${agentSkillsResponse.error?.message ?? agentSkillsResponse.message ?? "Unknown error"}`, void 0, apiErrorDetail(agentSkillsResponse.error));
56739
57275
  }
56740
57276
  const skill = agentSkillsResponse.data.skills?.find((s) => s.name === skillName);
56741
57277
  if (!skill) {
@@ -64712,7 +65248,7 @@ Examples:
64712
65248
  $ lua init --from-agent-id baseAgent_agent_xxx Duplicate agent + scaffold project
64713
65249
  $ lua init --from-agent-id baseAgent_agent_xxx --org-id <id> Cross-org duplicate
64714
65250
  `).action(initCommand);
64715
- program2.command("compile").description("\u{1F4E6} Compile skill to deployable format").option("--verbose", "Show detailed progress output").option("--debug", "Enable debug mode with extra verbose logging and temp file preservation").option("--sync", "Run drift detection check before compiling").action((opts) => compileCommand({
65251
+ program2.command("compile").description("\u{1F4E6} Compile skill to deployable format").option("--verbose", "Show detailed progress output").option("--debug", "Enable debug mode with extra verbose logging and temp file preservation").option("--sync", "Check for drift against the server before compiling (reads only \u2014 nothing is written)").action((opts) => compileCommand({
64716
65252
  ...opts,
64717
65253
  topLevel: true
64718
65254
  }));