lua-cli 3.32.4 → 3.32.5

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
@@ -116,7 +116,10 @@ function classifyCliError(error) {
116
116
  code: error.code,
117
117
  exitCode: error.exitCode,
118
118
  message: error.message,
119
- hint: error.hint
119
+ hint: error.hint,
120
+ statusCode: error.statusCode,
121
+ serverCode: error.serverCode,
122
+ issues: error.issues
120
123
  };
121
124
  }
122
125
  if (AuthenticationError.isAuthenticationError(error)) {
@@ -138,30 +141,36 @@ function classifyCliError(error) {
138
141
  }
139
142
  const status = numericStatus(e);
140
143
  if (status !== void 0) {
144
+ const statusCode = status;
141
145
  if (status === 401) return {
142
146
  code: "auth",
143
147
  exitCode: CLI_EXIT.AUTH,
144
- message
148
+ message,
149
+ statusCode
145
150
  };
146
151
  if (status === 403) return {
147
152
  code: "forbidden",
148
153
  exitCode: CLI_EXIT.FORBIDDEN,
149
- message
154
+ message,
155
+ statusCode
150
156
  };
151
157
  if (status === 404) return {
152
158
  code: "not_found",
153
159
  exitCode: CLI_EXIT.NOT_FOUND,
154
- message
160
+ message,
161
+ statusCode
155
162
  };
156
163
  if (status >= 400 && status < 500) return {
157
164
  code: `http_${status}`,
158
165
  exitCode: CLI_EXIT.FORBIDDEN,
159
- message
166
+ message,
167
+ statusCode
160
168
  };
161
169
  if (status >= 500 || status === 0) return {
162
170
  code: "unavailable",
163
171
  exitCode: CLI_EXIT.UNAVAILABLE,
164
- message
172
+ message,
173
+ statusCode
165
174
  };
166
175
  }
167
176
  const causeCode = e.cause?.code;
@@ -179,9 +188,18 @@ function classifyCliError(error) {
179
188
  message
180
189
  };
181
190
  }
191
+ function issueLines(issues) {
192
+ return (issues ?? []).map((issue) => {
193
+ const where = issue.path !== void 0 ? `${issue.path || "/"}: ` : "";
194
+ const what = issue.message ?? issue.code ?? "invalid";
195
+ const tag = issue.message && issue.code ? ` (${issue.code})` : "";
196
+ return ` \u2022 ${where}${what}${tag}`;
197
+ });
198
+ }
182
199
  function renderCliError(reported, options = {}) {
183
200
  const lines = [
184
- `\u2716 ${reported.code}: ${reported.message}`
201
+ `\u2716 ${reported.code}: ${reported.message}`,
202
+ ...issueLines(reported.issues)
185
203
  ];
186
204
  for (const hint of [
187
205
  reported.hint,
@@ -193,9 +211,30 @@ function renderCliError(reported, options = {}) {
193
211
  if (options.stack) lines.push("", options.stack);
194
212
  return lines;
195
213
  }
214
+ function cliErrorEnvelope(error) {
215
+ const reported = classifyCliError(error);
216
+ return {
217
+ success: false,
218
+ error: {
219
+ code: reported.serverCode ?? reported.code,
220
+ ...reported.statusCode !== void 0 ? {
221
+ statusCode: reported.statusCode
222
+ } : {},
223
+ message: reported.message,
224
+ ...reported.issues?.length ? {
225
+ issues: reported.issues
226
+ } : {}
227
+ }
228
+ };
229
+ }
196
230
  function reportCliError(error, options = {}) {
197
231
  const reported = classifyCliError(error);
198
232
  const stack = debugEnabled() && error instanceof Error ? error.stack : void 0;
233
+ if (options.json) {
234
+ console.log(JSON.stringify(cliErrorEnvelope(error), null, 2));
235
+ if (stack) console.error(stack);
236
+ return reported;
237
+ }
199
238
  for (const line of renderCliError(reported, {
200
239
  extraHint: options.extraHint,
201
240
  stack
@@ -247,6 +286,8 @@ var init_cli_error = __esm({
247
286
  exitCode;
248
287
  hint;
249
288
  statusCode;
289
+ serverCode;
290
+ issues;
250
291
  constructor(code, message, options = {}) {
251
292
  super(message);
252
293
  this.name = "CliError";
@@ -254,6 +295,8 @@ var init_cli_error = __esm({
254
295
  this.exitCode = options.exitCode ?? CLI_EXIT.ERROR;
255
296
  this.hint = options.hint;
256
297
  this.statusCode = options.statusCode;
298
+ this.serverCode = options.serverCode;
299
+ this.issues = options.issues?.length ? options.issues : void 0;
257
300
  if (Error.captureStackTrace) Error.captureStackTrace(this, _CliError);
258
301
  }
259
302
  /** Bad arguments, an unknown action, no project — exit 2. */
@@ -286,7 +329,7 @@ var init_cli_error = __esm({
286
329
  * A command that reads `response.error.statusCode` throws through here, so `lua logs` on a 503 exits 11 like
287
330
  * every other verb instead of printing the message itself and then throwing an exit-1 `Error`.
288
331
  */
289
- static fromStatus(statusCode, message, hint) {
332
+ static fromStatus(statusCode, message, hint, detail = {}) {
290
333
  const reported = classifyCliError(Object.assign(new Error(message), {
291
334
  statusCode
292
335
  }));
@@ -294,7 +337,9 @@ var init_cli_error = __esm({
294
337
  return new _CliError(reported.code, message, {
295
338
  exitCode: reported.exitCode,
296
339
  hint: hint ?? classHint,
297
- statusCode
340
+ statusCode,
341
+ serverCode: detail.serverCode,
342
+ issues: detail.issues
298
343
  });
299
344
  }
300
345
  static isCliError(error) {
@@ -343,7 +388,9 @@ var init_cli_error = __esm({
343
388
  __name(authHint, "authHint");
344
389
  __name(numericStatus, "numericStatus");
345
390
  __name(classifyCliError, "classifyCliError");
391
+ __name(issueLines, "issueLines");
346
392
  __name(renderCliError, "renderCliError");
393
+ __name(cliErrorEnvelope, "cliErrorEnvelope");
347
394
  __name(reportCliError, "reportCliError");
348
395
  __name(commanderExitCode, "commanderExitCode");
349
396
  __name(reportUnhandledCliError, "reportUnhandledCliError");
@@ -1179,6 +1226,11 @@ function isDeviceCredentialPrincipal(context) {
1179
1226
  function hasDeviceCredentialType(value3) {
1180
1227
  return DeviceCredentialClaimSchema.safeParse(value3).success;
1181
1228
  }
1229
+ function sessionAuthTime(context) {
1230
+ if (!context || context.credential.type !== "firstPartySession") return void 0;
1231
+ const authTime = context.authTime;
1232
+ return typeof authTime === "number" && Number.isInteger(authTime) && authTime >= 0 && authTime <= SESSION_AUTH_TIME_MAX_S ? authTime : void 0;
1233
+ }
1182
1234
  function isTypedApiKeyPrincipal(context) {
1183
1235
  return context?.subject.subjectType === "apiKey" && context.credential.type === "apiKey" && !context.compatibility;
1184
1236
  }
@@ -1351,6 +1403,39 @@ function scheduledWorkflowRunId(jobId, scheduledTime) {
1351
1403
  const key = typeof scheduledTime === "number" ? String(scheduledTime) : scheduledTimeKey(scheduledTime);
1352
1404
  return `${WORKFLOW_SCHEDULED_RUN_ID_PREFIX}${jobId}_${key}`;
1353
1405
  }
1406
+ function renderWorkflowScheduleKeyTemplate(template3, ctx) {
1407
+ if (!template3) return void 0;
1408
+ const read = /* @__PURE__ */ __name2((path25) => path25.split(".").reduce((o, k) => o && typeof o === "object" ? o[k] : void 0, ctx.input), "read");
1409
+ let unresolved = false;
1410
+ const out = template3.replace(/\$\{\s*([a-zA-Z0-9_.]+)\s*\}/g, (_m, expr) => {
1411
+ let v = "";
1412
+ if (expr === "scheduledTime") v = ctx.scheduledTime ?? "";
1413
+ else if (expr.startsWith("input.")) v = read(expr.slice("input.".length));
1414
+ const s = v === void 0 || v === null ? "" : String(v);
1415
+ if (s === "") unresolved = true;
1416
+ return s;
1417
+ });
1418
+ if (unresolved) return void 0;
1419
+ const key = out.slice(0, WORKFLOW_SCHEDULE_KEY_MAX);
1420
+ return key && /^[A-Za-z0-9:_\-.\/]+$/.test(key) ? key : void 0;
1421
+ }
1422
+ function scheduledWorkflowIdempotencyKey(jobId, rendered) {
1423
+ const head = `${WORKFLOW_SCHEDULE_IDEMPOTENCY_KEY_PREFIX}${jobId}:`;
1424
+ if (head.length + rendered.length <= WORKFLOW_SCHEDULE_KEY_MAX) return `${head}${rendered}`;
1425
+ const digest = stableKeyDigest(rendered);
1426
+ const room = WORKFLOW_SCHEDULE_KEY_MAX - head.length - digest.length - 1;
1427
+ return `${head}${rendered.slice(0, Math.max(0, room))}~${digest}`;
1428
+ }
1429
+ function stableKeyDigest(s) {
1430
+ let a = 2166136261;
1431
+ let b = 84696351;
1432
+ for (let i = 0; i < s.length; i++) {
1433
+ const c = s.charCodeAt(i);
1434
+ a = Math.imul(a ^ c, 16777619);
1435
+ b = Math.imul(b ^ c, 16777619) ^ b >>> 13;
1436
+ }
1437
+ return (a >>> 0).toString(16).padStart(8, "0") + (b >>> 0).toString(16).padStart(8, "0");
1438
+ }
1354
1439
  function workflowOperationId(runId, stepId, billingEpoch) {
1355
1440
  return `${WORKFLOW_OPERATION_ID_PREFIX}${runId}:${stepId}:${billingEpoch}`;
1356
1441
  }
@@ -1654,7 +1739,59 @@ function extractSingleJsonValue(text) {
1654
1739
  };
1655
1740
  }
1656
1741
  }
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;
1742
+ function agentFeatureCatalogDefault(featureName) {
1743
+ return DEFAULT_ON_AGENT_FEATURES.includes(featureName);
1744
+ }
1745
+ function hasExplicitFeatureActive(row2) {
1746
+ return typeof row2?.active === "boolean";
1747
+ }
1748
+ function effectiveFeatureActive(row2, catalogDefault) {
1749
+ return hasExplicitFeatureActive(row2) ? row2.active : catalogDefault;
1750
+ }
1751
+ function resolveEffectiveFeature(row2, catalogDefault) {
1752
+ return hasExplicitFeatureActive(row2) ? {
1753
+ active: row2.active,
1754
+ source: "agent",
1755
+ default: catalogDefault
1756
+ } : {
1757
+ active: catalogDefault,
1758
+ source: "default",
1759
+ default: catalogDefault
1760
+ };
1761
+ }
1762
+ function isFeatureRow(value3) {
1763
+ return typeof value3 === "object" && value3 !== null;
1764
+ }
1765
+ function effectiveAgentFeatureRows(base, override) {
1766
+ const merged = /* @__PURE__ */ new Map();
1767
+ for (const [name, row2] of Object.entries(base ?? {})) {
1768
+ if (isFeatureRow(row2)) merged.set(name, {
1769
+ row: row2,
1770
+ origin: "baseAgent"
1771
+ });
1772
+ }
1773
+ for (const [name, row2] of Object.entries(override ?? {})) {
1774
+ if (isFeatureRow(row2)) merged.set(name, {
1775
+ row: row2,
1776
+ origin: "subAgent"
1777
+ });
1778
+ }
1779
+ return {
1780
+ rows: Object.fromEntries([
1781
+ ...merged
1782
+ ].map(([name, e]) => [
1783
+ name,
1784
+ e.row
1785
+ ])),
1786
+ origins: Object.fromEntries([
1787
+ ...merged
1788
+ ].map(([name, e]) => [
1789
+ name,
1790
+ e.origin
1791
+ ]))
1792
+ };
1793
+ }
1794
+ 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, 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
1795
  var init_dist = __esm({
1659
1796
  "../shared-types/dist/index.mjs"() {
1660
1797
  "use strict";
@@ -2431,6 +2568,7 @@ This text is who you are for this person. As you learn them, their name, their w
2431
2568
  });
2432
2569
  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
2570
  IdSchema = z2.string().min(1).max(256);
2571
+ SESSION_AUTH_TIME_MAX_S = 4102444800;
2434
2572
  PrincipalDescriptorSchema = z2.object({
2435
2573
  subjectType: SubjectTypeSchema,
2436
2574
  subjectId: IdSchema
@@ -2479,7 +2617,8 @@ This text is who you are for this person. As you learn them, their name, their w
2479
2617
  owner: PrincipalOwnerSchema.optional(),
2480
2618
  compatibility: z2.object({
2481
2619
  mode: z2.literal("legacy-owner-delegation")
2482
- }).strict().optional()
2620
+ }).strict().optional(),
2621
+ authTime: z2.number().int().nonnegative().max(SESSION_AUTH_TIME_MAX_S).optional()
2483
2622
  }).strict();
2484
2623
  DeviceCredentialPrincipalContextSchema = z2.object({
2485
2624
  version: z2.literal(1),
@@ -2535,6 +2674,8 @@ This text is who you are for this person. As you learn them, their name, their w
2535
2674
  }).passthrough();
2536
2675
  __name(hasDeviceCredentialType, "hasDeviceCredentialType");
2537
2676
  __name2(hasDeviceCredentialType, "hasDeviceCredentialType");
2677
+ __name(sessionAuthTime, "sessionAuthTime");
2678
+ __name2(sessionAuthTime, "sessionAuthTime");
2538
2679
  __name(isTypedApiKeyPrincipal, "isTypedApiKeyPrincipal");
2539
2680
  __name2(isTypedApiKeyPrincipal, "isTypedApiKeyPrincipal");
2540
2681
  __name(typedApiKeyPrincipalId, "typedApiKeyPrincipalId");
@@ -2766,6 +2907,14 @@ This text is who you are for this person. As you learn them, their name, their w
2766
2907
  __name2(isScheduledWorkflowRunId, "isScheduledWorkflowRunId");
2767
2908
  __name(scheduledWorkflowRunId, "scheduledWorkflowRunId");
2768
2909
  __name2(scheduledWorkflowRunId, "scheduledWorkflowRunId");
2910
+ WORKFLOW_SCHEDULE_KEY_MAX = 128;
2911
+ __name(renderWorkflowScheduleKeyTemplate, "renderWorkflowScheduleKeyTemplate");
2912
+ __name2(renderWorkflowScheduleKeyTemplate, "renderWorkflowScheduleKeyTemplate");
2913
+ WORKFLOW_SCHEDULE_IDEMPOTENCY_KEY_PREFIX = "sched:";
2914
+ __name(scheduledWorkflowIdempotencyKey, "scheduledWorkflowIdempotencyKey");
2915
+ __name2(scheduledWorkflowIdempotencyKey, "scheduledWorkflowIdempotencyKey");
2916
+ __name(stableKeyDigest, "stableKeyDigest");
2917
+ __name2(stableKeyDigest, "stableKeyDigest");
2769
2918
  WORKFLOW_OPERATION_ID_PREFIX = "wf:";
2770
2919
  __name(workflowOperationId, "workflowOperationId");
2771
2920
  __name2(workflowOperationId, "workflowOperationId");
@@ -3212,6 +3361,23 @@ listed here; never invent a target.`;
3212
3361
  JSON_FENCE_RE = /```(?:json)?[ \t]*\r?\n([\s\S]*?)\r?\n?```/g;
3213
3362
  __name(extractSingleJsonValue, "extractSingleJsonValue");
3214
3363
  __name2(extractSingleJsonValue, "extractSingleJsonValue");
3364
+ DEFAULT_ON_AGENT_FEATURES = [
3365
+ "workflows",
3366
+ "workflowCompose",
3367
+ "observationalMemory"
3368
+ ];
3369
+ __name(agentFeatureCatalogDefault, "agentFeatureCatalogDefault");
3370
+ __name2(agentFeatureCatalogDefault, "agentFeatureCatalogDefault");
3371
+ __name(hasExplicitFeatureActive, "hasExplicitFeatureActive");
3372
+ __name2(hasExplicitFeatureActive, "hasExplicitFeatureActive");
3373
+ __name(effectiveFeatureActive, "effectiveFeatureActive");
3374
+ __name2(effectiveFeatureActive, "effectiveFeatureActive");
3375
+ __name(resolveEffectiveFeature, "resolveEffectiveFeature");
3376
+ __name2(resolveEffectiveFeature, "resolveEffectiveFeature");
3377
+ __name(isFeatureRow, "isFeatureRow");
3378
+ __name2(isFeatureRow, "isFeatureRow");
3379
+ __name(effectiveAgentFeatureRows, "effectiveAgentFeatureRows");
3380
+ __name2(effectiveAgentFeatureRows, "effectiveAgentFeatureRows");
3215
3381
  }
3216
3382
  });
3217
3383
 
@@ -4342,7 +4508,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
4342
4508
  const id = singleId(node);
4343
4509
  const unknown = unknownWorkflowRetryMembers(r);
4344
4510
  if (unknown.length) err("invalid-envelope", workflowRetryUnknownMembersMessage(unknown), `${path25}.retry`, id);
4345
- if (r.maxAttempts !== void 0 && !isWithinWorkflowRetryAttempts(r.maxAttempts)) {
4511
+ if (!isWithinWorkflowRetryAttempts(r.maxAttempts)) {
4346
4512
  const over = typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS;
4347
4513
  err(over ? "cap-exceeded" : "invalid-envelope", workflowRetryMaxAttemptsMessage(r.maxAttempts), `${path25}.retry.maxAttempts`, id);
4348
4514
  }
@@ -5883,6 +6049,7 @@ function runErrorIssues(issues) {
5883
6049
  function runNextAction(run) {
5884
6050
  if (isTerminalRunStatus(run.status)) return "none";
5885
6051
  if (run.status === "suspended" && run.gate?.kind === "budget") return "raise_budget";
6052
+ if (run.status === "suspended" && run.gate?.kind === "billing") return "top_up";
5886
6053
  if (!run.cancel?.requestedAt) return "none";
5887
6054
  const forceAt = run.cancel.forceAfter ?? run.cancel.requestedAt + FORCE_CANCEL_STALE_MS;
5888
6055
  return Date.now() >= forceAt ? "force" : "cancel_again";
@@ -5910,6 +6077,12 @@ function runCountsFromStepStatuses(statuses) {
5910
6077
  for (const s of statuses) tally[s] = (tally[s] ?? 0) + 1;
5911
6078
  return runCountsFromStatusTally(tally);
5912
6079
  }
6080
+ function isBillingHeldStep(row2) {
6081
+ return row2.status === "ready" && row2.billingHold === true;
6082
+ }
6083
+ function stepEffectiveStatus(row2) {
6084
+ return isBillingHeldStep(row2) ? "suspended" : row2.status;
6085
+ }
5913
6086
  function runCounts(counts) {
5914
6087
  const c = counts ?? {};
5915
6088
  const rawInFlight = c.dispatched !== void 0 || c.claimed !== void 0 || c.running !== void 0 || c.cancellation_requested !== void 0;
@@ -7440,6 +7613,10 @@ var init_dist2 = __esm({
7440
7613
  __name3(runCountsFromStatusTally, "runCountsFromStatusTally");
7441
7614
  __name(runCountsFromStepStatuses, "runCountsFromStepStatuses");
7442
7615
  __name3(runCountsFromStepStatuses, "runCountsFromStepStatuses");
7616
+ __name(isBillingHeldStep, "isBillingHeldStep");
7617
+ __name3(isBillingHeldStep, "isBillingHeldStep");
7618
+ __name(stepEffectiveStatus, "stepEffectiveStatus");
7619
+ __name3(stepEffectiveStatus, "stepEffectiveStatus");
7443
7620
  n = /* @__PURE__ */ __name3((v) => typeof v === "number" && Number.isFinite(v) ? v : 0, "n");
7444
7621
  __name(runCounts, "runCounts");
7445
7622
  __name3(runCounts, "runCounts");
@@ -7860,7 +8037,7 @@ var init_workflow = __esm({
7860
8037
  }, "assertPredicate");
7861
8038
  assertRetry = /* @__PURE__ */ __name((r, id) => {
7862
8039
  if (!r) return;
7863
- if (r.maxAttempts !== void 0 && !isWithinWorkflowRetryAttempts(r.maxAttempts)) {
8040
+ if (!isWithinWorkflowRetryAttempts(r.maxAttempts)) {
7864
8041
  const over = typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS;
7865
8042
  throw new LuaWorkflowBuildError(over ? "cap-exceeded" : "invalid-envelope", `"${id}": ${workflowRetryMaxAttemptsMessage(r.maxAttempts)}`);
7866
8043
  }
@@ -8719,6 +8896,59 @@ var init_types = __esm({
8719
8896
 
8720
8897
  // src/api/http.client.ts
8721
8898
  import { randomUUID as randomUUID3 } from "crypto";
8899
+ async function classifyErrorResponse(response) {
8900
+ let errorData;
8901
+ try {
8902
+ errorData = await response.json();
8903
+ } catch (jsonError) {
8904
+ errorData = {};
8905
+ }
8906
+ if (response.status === 401) {
8907
+ const serverMessage = typeof errorData.message === "string" ? errorData.message : void 0;
8908
+ if (serverMessage && /not an admin/i.test(serverMessage)) {
8909
+ throw new AuthenticationError(`Access denied for this agent: ${serverMessage}`, "no_agent_access", serverMessage);
8910
+ }
8911
+ const isExplicitCredential = !!serverMessage && /(invalid|expired|missing|no)\s+(api[\s_-]?key|token|credential)/i.test(serverMessage);
8912
+ const isBareAuthRejection = !serverMessage || /^unauthorized$/i.test(serverMessage);
8913
+ if (isExplicitCredential || isBareAuthRejection) {
8914
+ throw new AuthenticationError("Authentication failed. Your Lua credential may be invalid or expired.", "invalid_credentials", serverMessage);
8915
+ }
8916
+ throw new AuthenticationError(`Authentication failed: ${serverMessage}`, "unknown", serverMessage);
8917
+ }
8918
+ if (response.status === 403) {
8919
+ const detail = errorData.message || "You do not have permission to access this resource.";
8920
+ const serverCode = serverCodeOf(errorData.code, errorData.error);
8921
+ throw new CliError("forbidden", `Access denied (403): ${detail}${serverCode ? ` (${serverCode})` : ""}`, {
8922
+ exitCode: CLI_EXIT.FORBIDDEN,
8923
+ statusCode: 403,
8924
+ serverCode,
8925
+ issues: Array.isArray(errorData.issues) ? errorData.issues : void 0,
8926
+ hint: "Check that your Lua login has access to this agent or organization."
8927
+ });
8928
+ }
8929
+ return {
8930
+ success: false,
8931
+ error: {
8932
+ message: errorData.message || `HTTP ${response.status}: ${response.statusText}`,
8933
+ statusCode: response.status,
8934
+ error: errorData.error,
8935
+ retryAfterSeconds: parseRetryAfter(response.headers.get("retry-after")),
8936
+ ...errorData
8937
+ }
8938
+ };
8939
+ }
8940
+ function serverCodeOf(code, error) {
8941
+ if (typeof code === "string" && code.length > 0) return code;
8942
+ if (typeof error === "string" && /^[A-Z0-9][A-Z0-9_]*$/.test(error)) return error;
8943
+ return void 0;
8944
+ }
8945
+ async function refusalFromResponse(response) {
8946
+ const { error } = await classifyErrorResponse(response);
8947
+ return CliError.fromStatus(error?.statusCode ?? response.status, error?.message ?? `HTTP ${response.status}: ${response.statusText}`, void 0, {
8948
+ serverCode: serverCodeOf(error?.code, error?.error),
8949
+ issues: error?.issues
8950
+ });
8951
+ }
8722
8952
  async function* parseSseStream(body, signal) {
8723
8953
  const reader = body.getReader();
8724
8954
  const decoder = new TextDecoder();
@@ -8899,42 +9129,7 @@ var init_http_client = __esm({
8899
9129
  * @private
8900
9130
  */
8901
9131
  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
- };
9132
+ return classifyErrorResponse(response);
8938
9133
  }
8939
9134
  /**
8940
9135
  * Checks if an HTTP status code is retryable
@@ -9142,6 +9337,9 @@ var init_http_client = __esm({
9142
9337
  };
9143
9338
  }
9144
9339
  };
9340
+ __name(classifyErrorResponse, "classifyErrorResponse");
9341
+ __name(serverCodeOf, "serverCodeOf");
9342
+ __name(refusalFromResponse, "refusalFromResponse");
9145
9343
  __name(parseSseStream, "parseSseStream");
9146
9344
  __name(parseRetryAfter, "parseRetryAfter");
9147
9345
  __name(isCoreDrainApiError, "isCoreDrainApiError");
@@ -11568,8 +11766,14 @@ async function withErrorHandling(commandFn, commandName, opts) {
11568
11766
  } catch {
11569
11767
  }
11570
11768
  }
11769
+ let json = false;
11770
+ try {
11771
+ json = typeof opts?.json === "function" ? !!opts.json() : !!opts?.json;
11772
+ } catch {
11773
+ }
11571
11774
  const reported = reportCliError(error, {
11572
- extraHint
11775
+ extraHint,
11776
+ json
11573
11777
  });
11574
11778
  process.exitCode = reported.exitCode;
11575
11779
  await showUpdateWarningIfNeeded(versionCheckPromise);
@@ -25850,6 +26054,7 @@ var init_job_api_service = __esm({
25850
26054
  "src/api/job.api.service.ts"() {
25851
26055
  "use strict";
25852
26056
  init_http_client();
26057
+ init_cli_error();
25853
26058
  init_job_instance();
25854
26059
  JobApi = class extends HttpClient {
25855
26060
  static {
@@ -25906,7 +26111,7 @@ var init_job_api_service = __esm({
25906
26111
  if (response.success && response.data) {
25907
26112
  return new JobInstance(this, response.data);
25908
26113
  }
25909
- throw new Error(response.error?.message || "Failed to get job");
26114
+ throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Failed to get job");
25910
26115
  }
25911
26116
  /**
25912
26117
  * Creates a new job for the agent.
@@ -29752,7 +29957,7 @@ async function handleAgentSwitch(credential, organizations, apiKey, existingYaml
29752
29957
  });
29753
29958
  writeSuccess("\u2705 lua.skill.yaml updated successfully!");
29754
29959
  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");
29960
+ writeInfo("\n\u{1F4A1} Your project now uses the new agent. Run 'lua sync' (or 'lua push all') to reconcile your skills.\n");
29756
29961
  trackEvent("cli_init_agent_switched", {
29757
29962
  model_selected: !!selectedModel,
29758
29963
  model: selectedModel
@@ -30137,7 +30342,9 @@ async function syncCommand(options) {
30137
30342
  let syncCompletedSuccessfully = false;
30138
30343
  try {
30139
30344
  writeProgress("\u{1F504} Compiling to get latest local state...");
30140
- await compileCommand();
30345
+ await compileCommand({
30346
+ serverSync: true
30347
+ });
30141
30348
  writeProgress("\u{1F504} Checking for drift between server and local code...");
30142
30349
  const { agentId, apiKey } = await initializeCommand({
30143
30350
  showProgress: false
@@ -34877,7 +35084,8 @@ var WorkflowHandler = class extends BaseVersionedHandler {
34877
35084
  success: response.success,
34878
35085
  error: response.error?.message,
34879
35086
  statusCode: response.error?.statusCode,
34880
- code: response.error?.code ?? response.error?.error
35087
+ code: response.error?.code ?? response.error?.error,
35088
+ issues: response.error?.issues
34881
35089
  };
34882
35090
  }
34883
35091
  /**
@@ -35083,7 +35291,7 @@ async function compileCommand(options) {
35083
35291
  const debugMode = options?.debug || debugEnabled();
35084
35292
  const verboseMode = options?.verbose || debugMode;
35085
35293
  const doSync = options?.sync === true;
35086
- const doServerSync = options?.serverSync !== false;
35294
+ const doServerSync = options?.serverSync === true;
35087
35295
  if (debugMode) {
35088
35296
  console.log("\u{1F41B} Debug mode enabled");
35089
35297
  }
@@ -35180,7 +35388,7 @@ async function compileCommand(options) {
35180
35388
  let syncConfig = readYamlConfig();
35181
35389
  const agentId = syncConfig?.agent?.agentId;
35182
35390
  if (!doServerSync) {
35183
- writeInfo("\u2139\uFE0F Server sync skipped (offline verb) \u2014 `lua push` when you are ready to publish.");
35391
+ writeInfo("\u2139\uFE0F Server sync skipped \u2014 nothing was sent to the server; `lua push` publishes.");
35184
35392
  } else if (apiKey && agentId) {
35185
35393
  writeProgress("\u{1F504} Syncing with server...");
35186
35394
  const fetchResults = await Promise.all(syncableHandlers.map((h) => h.fetchServerState(apiKey, agentId)));
@@ -38458,7 +38666,8 @@ async function pushBackupCommand(options = {}) {
38458
38666
  }
38459
38667
  writeInfo("No compilation output found. Running compile first...");
38460
38668
  await compileCommand({
38461
- sync: false
38669
+ sync: false,
38670
+ serverSync: true
38462
38671
  });
38463
38672
  }
38464
38673
  writeProgress("Pushing backup...");
@@ -39156,12 +39365,24 @@ function formatPushFailureSummary(failedItems) {
39156
39365
  return lines.join("\n");
39157
39366
  }
39158
39367
  __name(formatPushFailureSummary, "formatPushFailureSummary");
39368
+ function pushRefusalError(handler, name, result) {
39369
+ const reason = result.error || "Unknown error";
39370
+ const isVersionConflict = reason.toLowerCase().includes("already exists");
39371
+ return new CliError("error", `${handler.displayName.toLowerCase()} "${name}" is not pushed: ${reason}`, {
39372
+ exitCode: CLI_EXIT.ERROR,
39373
+ statusCode: result.statusCode,
39374
+ serverCode: result.code,
39375
+ issues: result.issues,
39376
+ 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
39377
+ });
39378
+ }
39379
+ __name(pushRefusalError, "pushRefusalError");
39159
39380
  function stalePushEntryError(handler, name, entityId, serverMessage) {
39160
39381
  const field = handler.yamlConfig.idField;
39161
39382
  return new CliError("not_found", `${handler.displayName} "${name}" (${entityId}) no longer exists on the server${serverMessage ? ` \u2014 ${serverMessage}` : ""}`, {
39162
39383
  exitCode: CLI_EXIT.NOT_FOUND,
39163
39384
  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.`
39385
+ 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
39386
  });
39166
39387
  }
39167
39388
  __name(stalePushEntryError, "stalePushEntryError");
@@ -39310,7 +39531,9 @@ __name(shouldDeployAfterPush, "shouldDeployAfterPush");
39310
39531
  async function pushVersionedPrimitive(handler, options = {}) {
39311
39532
  try {
39312
39533
  writeProgress("\u{1F4E6} Compiling project...");
39313
- await compileCommand();
39534
+ await compileCommand({
39535
+ serverSync: true
39536
+ });
39314
39537
  writeSuccess("\u2705 Compilation complete");
39315
39538
  const apiKey = await authenticateOrFail();
39316
39539
  const config = readYamlConfig();
@@ -39332,7 +39555,7 @@ async function pushVersionedPrimitive(handler, options = {}) {
39332
39555
  });
39333
39556
  const entityId = selected[handler.yamlConfig.idField];
39334
39557
  if (!entityId) {
39335
- throw new Error(`${handler.displayName} "${selected.name}" has no server ID. Run 'lua compile' first.`);
39558
+ 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
39559
  }
39337
39560
  const manifest = loadManifest();
39338
39561
  const bundleAccumulator = /* @__PURE__ */ new Map();
@@ -39363,24 +39586,7 @@ async function pushVersionedPrimitive(handler, options = {}) {
39363
39586
  });
39364
39587
  if (!result.success) {
39365
39588
  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"}`);
39589
+ throw pushRefusalError(handler, selected.name, result);
39384
39590
  }
39385
39591
  writeSuccess(`
39386
39592
  \u2705 Successfully pushed ${selected.name} v${confirmedVersion}
@@ -39726,7 +39932,9 @@ async function runIncludeSourceHook(apiKey, agentId, selectedType) {
39726
39932
  __name(runIncludeSourceHook, "runIncludeSourceHook");
39727
39933
  async function pushAgentVersion(options = {}) {
39728
39934
  writeProgress("\u{1F504} Compiling...");
39729
- await compileCommand();
39935
+ await compileCommand({
39936
+ serverSync: true
39937
+ });
39730
39938
  const config = readYamlConfig();
39731
39939
  if (!config || !config.agent?.agentId) {
39732
39940
  throw new Error("No agent configuration found. Please run 'lua init' first.");
@@ -39833,7 +40041,9 @@ __name(deployPersonaVersionAfterPush, "deployPersonaVersionAfterPush");
39833
40041
  async function pushMCPServer(options = {}) {
39834
40042
  try {
39835
40043
  writeProgress("\u{1F4E6} Compiling project...");
39836
- await compileCommand();
40044
+ await compileCommand({
40045
+ serverSync: true
40046
+ });
39837
40047
  writeSuccess("\u2705 Compilation complete");
39838
40048
  const apiKey = await authenticateOrFail();
39839
40049
  writeSuccess("\u2705 Authentication verified");
@@ -39886,7 +40096,9 @@ async function pushAllCommand(options) {
39886
40096
  writeInfo("\u{1F680} Auto-deploy to production is ENABLED\n");
39887
40097
  }
39888
40098
  writeProgress("\u{1F4E6} Compiling project...");
39889
- await compileCommand();
40099
+ await compileCommand({
40100
+ serverSync: true
40101
+ });
39890
40102
  const config = readYamlConfig();
39891
40103
  if (!config?.agent?.agentId) {
39892
40104
  throw new Error('No agent ID found in lua.skill.yaml. Run "lua init" first.');
@@ -39974,7 +40186,7 @@ ${icon} Pushing ${items.length} ${handler.displayName}(s)...`);
39974
40186
  }
39975
40187
  const entityId = item[handler.yamlConfig.idField];
39976
40188
  if (!entityId) {
39977
- console.warn(`\u26A0\uFE0F ${handler.displayName} "${item.name}" has no server ID, skipping. Run 'lua compile' first.`);
40189
+ 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
40190
  continue;
39979
40191
  }
39980
40192
  if (!item.version) {
@@ -41921,7 +42133,9 @@ __name(chatCommand, "chatCommand");
41921
42133
  async function setupChatEnvironment(chatEnv, config) {
41922
42134
  writeProgress("\u{1F504} Setting up sandbox environment...");
41923
42135
  writeProgress("\u{1F504} Compiling skill...");
41924
- await compileCommand();
42136
+ await compileCommand({
42137
+ serverSync: true
42138
+ });
41925
42139
  const manifest = loadManifest();
41926
42140
  const skills = getPrimitivesByKind(manifest, PrimitiveKind.SKILL);
41927
42141
  let sandboxIds = {};
@@ -49703,7 +49917,7 @@ async function jobsCommand(action, cmdObj) {
49703
49917
  }, "jobs");
49704
49918
  }
49705
49919
  __name(jobsCommand, "jobsCommand");
49706
- async function displayJobsCore(context, jobs) {
49920
+ async function displayJobsCore(context, jobs, opts = {}) {
49707
49921
  console.log("\n" + "=".repeat(60));
49708
49922
  console.log("\u2699\uFE0F Production Jobs");
49709
49923
  console.log("=".repeat(60) + "\n");
@@ -49726,7 +49940,14 @@ async function displayJobsCore(context, jobs) {
49726
49940
  }
49727
49941
  console.log();
49728
49942
  } catch (error) {
49729
- displayJobError(job, "Error loading info");
49943
+ if (CliError.isCliError(error) && error.statusCode === 404) {
49944
+ if (opts.single) {
49945
+ 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.");
49946
+ }
49947
+ displayJobError(job, "not found on the server");
49948
+ continue;
49949
+ }
49950
+ throw error;
49730
49951
  }
49731
49952
  }
49732
49953
  console.log("=".repeat(60));
@@ -49994,7 +50215,13 @@ async function executeNonInteractive7(context, config, action, options) {
49994
50215
  console.log("\u2139\uFE0F No jobs found in configuration.");
49995
50216
  return;
49996
50217
  }
49997
- await displayJobsCore(context, jobs);
50218
+ const viewed = options.jobName ? jobs.filter((j) => j.jobId === options.jobName || j.name === options.jobName) : jobs;
50219
+ if (viewed.length === 0) {
50220
+ throw CliError.notFound(`Job "${options.jobName}" not found`, listHint("Available jobs in local config:", jobs.map((j) => `${j.name} (${j.jobId})`)));
50221
+ }
50222
+ await displayJobsCore(context, viewed, {
50223
+ single: !!options.jobName
50224
+ });
49998
50225
  return;
49999
50226
  }
50000
50227
  if (!options.jobName) {
@@ -50500,69 +50727,77 @@ function exitCodeForRunStatus(status) {
50500
50727
  }
50501
50728
  __name(exitCodeForRunStatus, "exitCodeForRunStatus");
50502
50729
  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;
50730
+ return withErrorHandling(
50731
+ async () => {
50732
+ const options = {
50733
+ ...cmdObj ?? {}
50734
+ };
50735
+ const json = !!options.json;
50736
+ let resolved;
50737
+ if (action) {
50738
+ resolved = validateOrSuggest("workflows.action", action);
50739
+ } else {
50740
+ const answer = await safePrompt([
50741
+ {
50742
+ type: "list",
50743
+ name: "action",
50744
+ message: "What would you like to do?",
50745
+ choices: [
50746
+ {
50747
+ name: "\u{1F4CB} List workflows",
50748
+ value: "list"
50749
+ },
50750
+ {
50751
+ name: "\u{1F9ED} Run a workflow locally",
50752
+ value: "run"
50753
+ },
50754
+ {
50755
+ name: "\u25B6\uFE0F Start a run",
50756
+ value: "start"
50757
+ },
50758
+ {
50759
+ name: "\u{1F5C2}\uFE0F List runs",
50760
+ value: "runs"
50761
+ }
50762
+ ]
50763
+ }
50764
+ ]);
50765
+ if (!answer) return;
50766
+ resolved = answer.action;
50767
+ }
50768
+ if (resolved === "run") {
50769
+ const outcome = await runWorkflowLocalFromProject(target ?? options.workflowName ?? null, options);
50770
+ process.exitCode = outcome.exitCode;
50771
+ trackEvent("cli_workflows_action", {
50772
+ action: "run",
50773
+ non_interactive: !!action
50774
+ });
50775
+ return;
50776
+ }
50777
+ const { agentId, apiKey } = await initializeCommand({
50778
+ showProgress: !json
50779
+ });
50780
+ const ctx = {
50781
+ agentId,
50782
+ apiKey,
50783
+ api: new WorkflowApi(BASE_URLS.API, apiKey, agentId),
50784
+ json
50785
+ };
50786
+ const code = await executeAction(ctx, resolved, target, extra, options);
50787
+ if (code !== void 0 && code !== 0) process.exitCode = code;
50543
50788
  trackEvent("cli_workflows_action", {
50544
- action: "run",
50545
- non_interactive: !!action
50789
+ action: resolved,
50790
+ non_interactive: !!action,
50791
+ exit_code: code ?? 0
50546
50792
  });
50547
- return;
50793
+ },
50794
+ "workflows",
50795
+ // LUA-803: a typed refusal that escapes a verb under `--json` (a 403 the client threw, an unreachable host) is
50796
+ // the envelope on stdout — the same shape `apiFailure` prints for a refused verb — never the `✖` line.
50797
+ {
50798
+ json: /* @__PURE__ */ __name(() => !!cmdObj?.json, "json")
50548
50799
  }
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");
50800
+ );
50566
50801
  }
50567
50802
  __name(workflowsCommand, "workflowsCommand");
50568
50803
  async function executeAction(ctx, action, target, extra, o) {
@@ -50664,18 +50899,12 @@ async function executeAction(ctx, action, target, extra, o) {
50664
50899
  }
50665
50900
  __name(executeAction, "executeAction");
50666
50901
  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
- }
50902
+ if (!name) throw CliError.usage(`${verb}: a workflow name is required`, `lua workflows ${verb} <name> (or -i <name>)`);
50671
50903
  return fn();
50672
50904
  }
50673
50905
  __name(requireName, "requireName");
50674
50906
  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
- }
50907
+ if (!runId) throw CliError.usage(`${verb}: a run id is required`, `lua workflows ${verb} <runId> (or -r <runId>)`);
50679
50908
  return fn();
50680
50909
  }
50681
50910
  __name(requireRun, "requireRun");
@@ -50683,46 +50912,67 @@ function emitJson(ctx, res) {
50683
50912
  if (ctx.json) console.log(JSON.stringify(res, null, 2));
50684
50913
  }
50685
50914
  __name(emitJson, "emitJson");
50686
- function apiFailure(ctx, res, verb) {
50687
- emitJson(ctx, res);
50915
+ function refusalExitCode(status) {
50916
+ if (status === 0 || status !== void 0 && status >= 500) return WORKFLOW_EXIT.UNAVAILABLE;
50917
+ if (status === 404) return WORKFLOW_EXIT.NOT_FOUND;
50918
+ return WORKFLOW_EXIT.API;
50919
+ }
50920
+ __name(refusalExitCode, "refusalExitCode");
50921
+ function apiRefusal(res, verb, detail = {}) {
50688
50922
  const err = res.error;
50689
50923
  const status = err?.statusCode;
50690
50924
  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);
50925
+ const serverMessage = err?.message ?? "Unknown error";
50926
+ const message = detail.message ? code ? `${detail.message} (${code})` : detail.message : code ? serverMessage === code ? `${verb} failed (${code})` : `${verb} failed (${code}): ${serverMessage}` : `${verb} failed: ${serverMessage}`;
50927
+ const issues = detail.issues ?? err?.issues;
50928
+ const exitCode = refusalExitCode(status);
50929
+ if (exitCode === WORKFLOW_EXIT.UNAVAILABLE) {
50930
+ return CliError.fromStatus(status, message, status === 503 && code === "CONTROL_UNAVAILABLE" ? CONTROL_UNAVAILABLE_HINT : detail.hint, {
50931
+ serverCode: code,
50932
+ issues
50933
+ });
50694
50934
  }
50695
- if (!ctx.json) console.error(`\u274C ${message}`);
50696
- if (status === 404) return WORKFLOW_EXIT.NOT_FOUND;
50697
- return WORKFLOW_EXIT.API;
50935
+ return new CliError(exitCode === WORKFLOW_EXIT.NOT_FOUND ? "not_found" : "error", message, {
50936
+ exitCode,
50937
+ statusCode: status,
50938
+ serverCode: code,
50939
+ issues,
50940
+ hint: detail.hint
50941
+ });
50942
+ }
50943
+ __name(apiRefusal, "apiRefusal");
50944
+ function apiFailure(ctx, res, verb, detail = {}) {
50945
+ if (ctx.json) {
50946
+ emitJson(ctx, res);
50947
+ return refusalExitCode(res.error?.statusCode);
50948
+ }
50949
+ throw apiRefusal(res, verb, detail);
50698
50950
  }
50699
50951
  __name(apiFailure, "apiFailure");
50700
50952
  async function resolveWorkflow(ctx, nameOrId) {
50701
- const list = await loadWorkflowList(ctx);
50702
- return list && pickWorkflow(list, nameOrId);
50953
+ return pickWorkflow(await loadWorkflowList(ctx), nameOrId);
50703
50954
  }
50704
50955
  __name(resolveWorkflow, "resolveWorkflow");
50705
50956
  async function loadWorkflowList(ctx) {
50706
50957
  const res = await ctx.api.getWorkflows({
50707
50958
  includeDynamic: true
50708
50959
  });
50709
- if (!res.success || !res.data) {
50710
- console.error(`\u274C Failed to list workflows: ${res.error?.message ?? "Unknown error"}`);
50711
- return void 0;
50712
- }
50960
+ if (!res.success || !res.data) throw apiRefusal(res, "list");
50713
50961
  return res.data.workflows;
50714
50962
  }
50715
50963
  __name(loadWorkflowList, "loadWorkflowList");
50716
50964
  function pickWorkflow(list, nameOrId) {
50717
50965
  const wf = list.find((w) => w.name === nameOrId) ?? list.find((w) => w.id === nameOrId);
50718
50966
  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(", ")}`);
50967
+ throw CliError.notFound(`Workflow "${nameOrId}" not found`, listHint("Available:", list.map((w) => w.name)));
50722
50968
  }
50723
50969
  return wf;
50724
50970
  }
50725
50971
  __name(pickWorkflow, "pickWorkflow");
50972
+ function isDefinitionId(nameOrId) {
50973
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(nameOrId);
50974
+ }
50975
+ __name(isDefinitionId, "isDefinitionId");
50726
50976
  var shortHash = /* @__PURE__ */ __name((h) => h ? h.replace(/^sha256-cj1:/, "").slice(0, 12) : "\u2014", "shortHash");
50727
50977
  var when = /* @__PURE__ */ __name((iso) => iso ? new Date(iso).toLocaleString() : "\u2014", "when");
50728
50978
  var clockOf = /* @__PURE__ */ __name((at) => {
@@ -50735,6 +50985,15 @@ var parseIntFlag = /* @__PURE__ */ __name((v, flag) => {
50735
50985
  if (!Number.isFinite(n2)) throw new WorkflowLocalUsageError("usage", `${flag}: expected a number (got "${v}")`);
50736
50986
  return n2;
50737
50987
  }, "parseIntFlag");
50988
+ function parsedOrUsage(parse) {
50989
+ try {
50990
+ return parse();
50991
+ } catch (e) {
50992
+ if (e instanceof WorkflowLocalUsageError) throw CliError.usage(e.message);
50993
+ throw e;
50994
+ }
50995
+ }
50996
+ __name(parsedOrUsage, "parsedOrUsage");
50738
50997
  async function listCore(ctx, opts) {
50739
50998
  const res = await ctx.api.getWorkflows({
50740
50999
  includeDynamic: opts.all
@@ -50921,35 +51180,22 @@ function startResultLine(d, requestedName) {
50921
51180
  }
50922
51181
  __name(startResultLine, "startResultLine");
50923
51182
  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
- }
51183
+ const input = parsedOrUsage(() => o.input ? parseJsonOrFile(o.input, "--input") : {});
51184
+ const waitSeconds = parsedOrUsage(() => parseIntFlag(o.wait, "--wait"));
51185
+ const budgetCredits = parsedOrUsage(() => parseIntegerFlag(o.budgetCredits, "--budget-credits", {
51186
+ min: 1
51187
+ }));
50942
51188
  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;
51189
+ throw CliError.usage("--wait must be 0..55 (a server long-poll; the CLI raises its own deadline to wait+10 s)");
50945
51190
  }
50946
51191
  const tags = o.tag === void 0 ? void 0 : Array.isArray(o.tag) ? o.tag : [
50947
51192
  o.tag
50948
51193
  ];
50949
- if (tags && tags.length > 10) {
50950
- console.error("\u274C --tag: at most 10 tags");
50951
- return WORKFLOW_EXIT.USAGE;
50952
- }
51194
+ if (tags && tags.length > 10) throw CliError.usage("--tag: at most 10 tags");
51195
+ const wf = isDefinitionId(name) ? {
51196
+ id: name,
51197
+ name
51198
+ } : await resolveWorkflow(ctx, name);
50953
51199
  let workflowVersionId;
50954
51200
  if (o.workflowVersion) {
50955
51201
  const ref = await resolveVersionRef(ctx, wf, o.workflowVersion, "start");
@@ -51021,8 +51267,7 @@ async function runsCore(ctx, o) {
51021
51267
  try {
51022
51268
  limit = parseIntFlag(o.limit, "--limit");
51023
51269
  } catch (e) {
51024
- console.error(`\u274C ${e.message}`);
51025
- return WORKFLOW_EXIT.USAGE;
51270
+ throw CliError.usage(`${e.message}`);
51026
51271
  }
51027
51272
  const res = await ctx.api.getRuns({
51028
51273
  workflowId,
@@ -51160,6 +51405,8 @@ function printRun(run, withSteps) {
51160
51405
  console.log(`
51161
51406
  \u23F8\uFE0F Paused \u2014 run budget reached (${budgetCopy(run)}):`);
51162
51407
  console.log(` lua workflows raise-budget ${id} --credits <n>`);
51408
+ } else if (kind === "billing") {
51409
+ for (const line of billingParkLines(run)) console.log(line);
51163
51410
  } else {
51164
51411
  console.log(` Gate: ${kind}${run.gate.reason ? ` (${run.gate.reason})` : ""}${run.gate.since ? ` since ${when(run.gate.since)}` : ""}`);
51165
51412
  }
@@ -51208,6 +51455,23 @@ function printRun(run, withSteps) {
51208
51455
  }
51209
51456
  }
51210
51457
  __name(printRun, "printRun");
51458
+ function billingParkLines(run) {
51459
+ const id = runIdOf(run);
51460
+ const gate = run.gate ?? {
51461
+ kind: "billing"
51462
+ };
51463
+ const what = gate.code === "out_of_actions" ? "the workspace is out of actions" : "the workspace has no credits left";
51464
+ const step3 = gate.stepId ? ` before step ${gate.stepId}` : "";
51465
+ const out = [
51466
+ `
51467
+ \u23F8\uFE0F Paused \u2014 payment required (${what})${step3}:`
51468
+ ];
51469
+ 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");
51470
+ out.push(` to resume it right away after topping up: lua workflows retry-step ${id} --step ${gate.stepId ?? "<stepId>"}`);
51471
+ if (typeof gate.expiresAt === "number") out.push(` still unpaid at ${when(gate.expiresAt)} \u21D2 the run fails`);
51472
+ return out;
51473
+ }
51474
+ __name(billingParkLines, "billingParkLines");
51211
51475
  function stepAttemptLines(s) {
51212
51476
  const out = [];
51213
51477
  const cause = stepFailureLine(s.error);
@@ -51315,15 +51579,8 @@ function attemptCountersLine(c) {
51315
51579
  }
51316
51580
  __name(attemptCountersLine, "attemptCountersLine");
51317
51581
  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
- }
51582
+ const afterSeq = parsedOrUsage(() => parseIntFlag(o.after, "--after"));
51583
+ const timeoutS = parsedOrUsage(() => parseIntFlag(o.timeout, "--timeout"));
51327
51584
  const controller = new AbortController();
51328
51585
  let timedOut = false;
51329
51586
  const timer = timeoutS ? setTimeout(() => (timedOut = true, controller.abort()), timeoutS * 1e3) : void 0;
@@ -51332,7 +51589,7 @@ async function watchCore(ctx, runId, o) {
51332
51589
  let lastEventId = afterSeq !== void 0 ? String(afterSeq) : void 0;
51333
51590
  let exit;
51334
51591
  let reconnects = 0;
51335
- const throttled = /* @__PURE__ */ new Map();
51592
+ const state3 = createWatchState();
51336
51593
  try {
51337
51594
  while (exit === void 0 && !controller.signal.aborted) {
51338
51595
  const res = await ctx.api.watchEvents(runId, {
@@ -51347,7 +51604,7 @@ async function watchCore(ctx, runId, o) {
51347
51604
  let sawEnd = false;
51348
51605
  for await (const frame of res.data) {
51349
51606
  if (frame.id !== void 0) lastEventId = frame.id;
51350
- const verdict = handleWatchFrame(ctx, runId, frame, o, throttled);
51607
+ const verdict = await handleWatchFrame(ctx, runId, frame, o, state3);
51351
51608
  if (verdict === "reconnect") break;
51352
51609
  if (typeof verdict === "number") {
51353
51610
  exit = verdict;
@@ -51357,10 +51614,10 @@ async function watchCore(ctx, runId, o) {
51357
51614
  }
51358
51615
  if (exit !== void 0 || controller.signal.aborted || sawEnd) break;
51359
51616
  reconnects += 1;
51360
- if (reconnects > 20) {
51361
- console.error("\u274C watch: too many reconnects");
51362
- return WORKFLOW_EXIT.API;
51363
- }
51617
+ resetWatchReplay(state3);
51618
+ if (reconnects > 20) throw new CliError("error", "watch: too many reconnects", {
51619
+ exitCode: WORKFLOW_EXIT.API
51620
+ });
51364
51621
  if (!ctx.json) console.error(`\u2026 reconnecting (Last-Event-ID ${lastEventId ?? "none"})`);
51365
51622
  await new Promise((r) => setTimeout(r, Math.min(5e3, 250 * 2 ** reconnects)));
51366
51623
  }
@@ -51385,7 +51642,55 @@ function throttledLine(throttled) {
51385
51642
  ].map(([k, n2]) => `${k} \xD7${n2}`).join(" \xB7 ")}`;
51386
51643
  }
51387
51644
  __name(throttledLine, "throttledLine");
51388
- function handleWatchFrame(ctx, runId, frame, o, throttled) {
51645
+ function createWatchState() {
51646
+ return {
51647
+ throttled: /* @__PURE__ */ new Map(),
51648
+ signalNames: /* @__PURE__ */ new Map()
51649
+ };
51650
+ }
51651
+ __name(createWatchState, "createWatchState");
51652
+ function resetWatchReplay(state3) {
51653
+ state3.truth = void 0;
51654
+ state3.pending = void 0;
51655
+ }
51656
+ __name(resetWatchReplay, "resetWatchReplay");
51657
+ async function readWatchTruth(ctx, runId) {
51658
+ try {
51659
+ const res = await ctx.api.getRun(runId);
51660
+ if (!res?.success || !res.data) return null;
51661
+ const run = res.data;
51662
+ return {
51663
+ parked: run.status === "suspended" || run.status === "gated",
51664
+ watermark: typeof run.eventSeq === "number" && Number.isFinite(run.eventSeq) ? run.eventSeq : void 0
51665
+ };
51666
+ } catch {
51667
+ return null;
51668
+ }
51669
+ }
51670
+ __name(readWatchTruth, "readWatchTruth");
51671
+ function frameSeq(frame, ev) {
51672
+ const fromId = frame.id === void 0 ? NaN : Number(frame.id);
51673
+ if (Number.isFinite(fromId)) return fromId;
51674
+ return typeof ev.seq === "number" ? ev.seq : void 0;
51675
+ }
51676
+ __name(frameSeq, "frameSeq");
51677
+ async function watchBoundary(ctx, runId, state3, seq, line, code, emit2) {
51678
+ if (state3.truth === void 0 || state3.truth !== null && state3.truth.watermark === void 0) {
51679
+ state3.truth = await readWatchTruth(ctx, runId);
51680
+ }
51681
+ const truth = state3.truth;
51682
+ if (truth === null) return emit2(line, code);
51683
+ if (truth.watermark === void 0 || seq === void 0) return truth.parked ? emit2(line, code) : void 0;
51684
+ if (seq > truth.watermark) return emit2(line, code);
51685
+ if (!truth.parked) return void 0;
51686
+ state3.pending = {
51687
+ line,
51688
+ code
51689
+ };
51690
+ return void 0;
51691
+ }
51692
+ __name(watchBoundary, "watchBoundary");
51693
+ async function handleWatchFrame(ctx, runId, frame, o, state3) {
51389
51694
  const data = frame.data ?? {};
51390
51695
  if (o.events || ctx.json) console.log(JSON.stringify({
51391
51696
  id: frame.id,
@@ -51398,10 +51703,17 @@ function handleWatchFrame(ctx, runId, frame, o, throttled) {
51398
51703
  if (!ctx.json) console.error(" --wait-for-human: still following \u2014 Ctrl+C or --timeout to stop");
51399
51704
  return void 0;
51400
51705
  }, "humanBoundary");
51706
+ const flushPending = /* @__PURE__ */ __name(() => {
51707
+ const pending = state3.pending;
51708
+ if (!pending) return void 0;
51709
+ state3.pending = void 0;
51710
+ return humanBoundary(pending.line, pending.code);
51711
+ }, "flushPending");
51401
51712
  switch (frame.event) {
51402
51713
  case "heartbeat":
51403
- return void 0;
51714
+ return flushPending();
51404
51715
  case "reconnect":
51716
+ resetWatchReplay(state3);
51405
51717
  return "reconnect";
51406
51718
  case "error": {
51407
51719
  const code = String(data.code ?? "unknown");
@@ -51418,10 +51730,15 @@ function handleWatchFrame(ctx, runId, frame, o, throttled) {
51418
51730
  const type = frame.event || ev.type;
51419
51731
  if (type === "step.throttled") {
51420
51732
  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)}`);
51733
+ state3.throttled.set(kind, (state3.throttled.get(kind) ?? 0) + 1);
51734
+ if (!ctx.json && !o.events) process.stdout.write(`\r ${throttledLine(state3.throttled)}`);
51423
51735
  return void 0;
51424
51736
  }
51737
+ if (type === "step.suspended") {
51738
+ const d = ev.data;
51739
+ const stepId = ev.stepId ?? (typeof d?.stepId === "string" ? d.stepId : void 0);
51740
+ if (stepId && typeof d?.signalName === "string") state3.signalNames.set(stepId, d.signalName);
51741
+ }
51425
51742
  if (!ctx.json && !o.events) {
51426
51743
  const at = clockOf(ev.ts);
51427
51744
  const subrun = subrunEventDetail({
@@ -51431,39 +51748,58 @@ function handleWatchFrame(ctx, runId, frame, o, throttled) {
51431
51748
  const detail = subrun ? ` \xB7 ${subrun}` : ev.data ? ` ${JSON.stringify(scrubEventData(ev.data)).slice(0, 160)}` : "";
51432
51749
  console.log(`[${at}] ${type}${ev.stepId ? ` \xB7 ${ev.stepId}` : ""}${detail}`);
51433
51750
  }
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;
51751
+ if (type === "run.resumed") state3.pending = void 0;
51752
+ const seq = frameSeq(frame, ev);
51753
+ const boundary = /* @__PURE__ */ __name((line, code) => watchBoundary(ctx, runId, state3, seq, line, code, humanBoundary), "boundary");
51754
+ const verdict = await boundaryVerdict(runId, {
51755
+ ...ev,
51756
+ type
51757
+ }, state3, boundary);
51758
+ if (verdict !== void 0) return verdict;
51759
+ const watermark = state3.truth?.watermark;
51760
+ if (watermark !== void 0 && seq !== void 0 && seq >= watermark) return flushPending();
51462
51761
  return void 0;
51463
51762
  }
51464
51763
  }
51465
51764
  }
51466
51765
  __name(handleWatchFrame, "handleWatchFrame");
51766
+ async function boundaryVerdict(runId, ev, state3, boundary) {
51767
+ const type = ev.type;
51768
+ if (type === "run.gated") {
51769
+ const gate = ev.data?.gate;
51770
+ const stepId = ev.data?.stepId;
51771
+ if (gate === "exception") {
51772
+ return boundary(`\u26A0\uFE0F run parked on an exception gate \u2014 a human decides next: lua workflows status ${runId}`, WORKFLOW_EXIT.RUN_PARKED);
51773
+ }
51774
+ if (gate === "billing") {
51775
+ 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);
51776
+ }
51777
+ if (gate === "budget") {
51778
+ 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);
51779
+ }
51780
+ return boundary(`\u23F8\uFE0F run gated (${gate ?? "consent"}) \u2014 approve it from the desktop; then: lua workflows watch ${runId}`, WORKFLOW_EXIT.RUN_GATED);
51781
+ }
51782
+ if (type === "run.suspended") {
51783
+ const kind = ev.data?.kind;
51784
+ const stepId = ev.data?.stepId ?? ev.stepId;
51785
+ if (kind === "billing") {
51786
+ 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);
51787
+ }
51788
+ if (kind === "approval" || kind === "input" || kind === "signal") {
51789
+ const signalName = stepId ? state3.signalNames.get(stepId) : void 0;
51790
+ 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>`;
51791
+ return boundary(`\u23F8\uFE0F run waits for a person (${kind}${stepId ? ` \xB7 ${stepId}` : ""}) \u2014 ${verb}; then: lua workflows watch ${runId}`, WORKFLOW_EXIT.RUN_PARKED);
51792
+ }
51793
+ }
51794
+ if (type === "run.budget_parked") {
51795
+ 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);
51796
+ }
51797
+ if (type === "run.completed") return WORKFLOW_EXIT.OK;
51798
+ if (type === "run.failed" || type === "run.timed_out") return WORKFLOW_EXIT.RUN_FAILED;
51799
+ if (type === "run.cancelled" || type === "run.abandoned") return WORKFLOW_EXIT.RUN_CANCELLED;
51800
+ return void 0;
51801
+ }
51802
+ __name(boundaryVerdict, "boundaryVerdict");
51467
51803
  async function cancelCore(ctx, runId, o) {
51468
51804
  const res = await ctx.api.cancelRun(runId, {
51469
51805
  mode: o.force ? "force" : "request",
@@ -51486,45 +51822,48 @@ async function cancelCore(ctx, runId, o) {
51486
51822
  }
51487
51823
  __name(cancelCore, "cancelCore");
51488
51824
  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
- }
51825
+ if (!o.step) throw CliError.usage("resume: --step <id> is required");
51826
+ const resumeData = parsedOrUsage(() => o.data ? parseJsonOrFile(o.data, "--data") : {});
51500
51827
  const res = await ctx.api.resumeRun(runId, o.step, {
51501
51828
  resumeData
51502
51829
  });
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
51830
  if (!res.success || !res.data) {
51515
51831
  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
- }
51832
+ const status = res.error?.statusCode;
51833
+ const stepOf3 = res.error?.stepId ?? o.step;
51834
+ let detail = {};
51835
+ if (status === 400 && code === "RESUME_SCHEMA_INVALID") {
51836
+ detail = {
51837
+ message: `resume rejected \u2014 --data does not match the resumeSchema step "${stepOf3}" declares`,
51838
+ issues: res.error?.issues,
51839
+ hint: "fix the data and resume again (the step is still waiting for input)"
51840
+ };
51841
+ } else if (status === 422 && code === "RESUME_SCHEMA_UNCOMPILABLE") {
51842
+ detail = {
51843
+ message: `resume could not be checked \u2014 the resumeSchema step "${stepOf3}" declares does not compile`,
51844
+ issues: (res.error?.issues ?? []).map((issue) => ({
51845
+ message: issue.message ?? "schema does not compile"
51846
+ })),
51847
+ 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"
51848
+ };
51849
+ } else if (status === 404 && code === "STEP_NOT_FOUND") {
51850
+ detail = {
51851
+ message: `step "${o.step}" does not exist on run ${runId} \u2014 list its steps: lua workflows status ${runId} --steps`
51852
+ };
51853
+ } else if (status === 409 && (code === "approval_requires_human" || code === "APPROVAL_REQUIRES_HUMAN")) {
51854
+ detail = {
51855
+ message: `step "${o.step}" is an approval \u2014 resolve it with: lua workflows approve ${runId} --approval ${o.step} --decision approve`
51856
+ };
51857
+ } else if (status === 409 && (code === "use_signal_route" || code === "USE_SIGNAL_ROUTE")) {
51858
+ detail = {
51859
+ message: `step "${o.step}" waits for a signal \u2014 deliver it with: lua workflows signal ${runId} <name> --payload '{}'`
51860
+ };
51861
+ } else if (status === 409 && code === "NOT_SUSPENDED") {
51862
+ detail = {
51863
+ message: `step "${o.step}" is not suspended${res.error?.status ? ` (it is ${res.error.status})` : ""} \u2014 nothing to resume: lua workflows status ${runId} --steps`
51864
+ };
51526
51865
  }
51527
- return apiFailure(ctx, res, "resume");
51866
+ return apiFailure(ctx, res, "resume", detail);
51528
51867
  }
51529
51868
  emitJson(ctx, res);
51530
51869
  if (!ctx.json) {
@@ -51539,8 +51878,7 @@ async function resumeCore(ctx, runId, o) {
51539
51878
  __name(resumeCore, "resumeCore");
51540
51879
  async function retryStepCore(ctx, runId, o) {
51541
51880
  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;
51881
+ throw CliError.usage("--step <id> is required: lua workflows retry-step <runId> --step <id> [--note <text>]");
51544
51882
  }
51545
51883
  const res = await ctx.api.retryStep(runId, o.step, {
51546
51884
  ...o.note ? {
@@ -51548,21 +51886,30 @@ async function retryStepCore(ctx, runId, o) {
51548
51886
  } : {}
51549
51887
  });
51550
51888
  if (!res.success || !res.data) {
51889
+ let detail = {};
51551
51890
  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`);
51891
+ if (res.error?.statusCode === 404 && code === "STEP_NOT_FOUND") {
51892
+ detail = {
51893
+ message: `step "${o.step}" does not exist on run ${runId} \u2014 list its steps: lua workflows status ${runId} --steps`
51894
+ };
51895
+ } else if (res.error?.statusCode === 409) {
51896
+ if (code === "STEP_NOT_PARKED" || code === "step_not_parked") detail = {
51897
+ message: `step "${o.step}" is not parked (running, pending or already re-armed) \u2014 nothing to retry`
51898
+ };
51899
+ else if (code === "RUN_TERMINAL" || code === "run_terminal") detail = {
51900
+ message: `run ${runId} is terminal \u2014 start a new run instead`
51901
+ };
51557
51902
  else if (code === "STEP_RETRY_CAP" || code === "step_retry_cap") {
51558
51903
  const cap = res.error;
51559
51904
  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`);
51905
+ detail = {
51906
+ message: `step "${o.step}" has reached the retry cap${at} \u2014 resolve it or repair the run`,
51907
+ hint: `lua workflows resolve-step ${runId} --step ${o.step} --outcome skip|complete|fail
51908
+ to start a repair run instead, decide it from the desktop run page`
51909
+ };
51563
51910
  }
51564
51911
  }
51565
- return apiFailure(ctx, res, "retry-step");
51912
+ return apiFailure(ctx, res, "retry-step", detail);
51566
51913
  }
51567
51914
  emitJson(ctx, res);
51568
51915
  if (!ctx.json) {
@@ -51585,29 +51932,24 @@ __name(refusalIssues, "refusalIssues");
51585
51932
  async function resolveStepCore(ctx, runId, o) {
51586
51933
  const usage = "lua workflows resolve-step <runId> --step <id> --outcome skip|complete|fail [--output <json|@file>] [--note <text>]";
51587
51934
  if (!o.step) {
51588
- console.error(`\u274C --step <id> is required: ${usage}`);
51589
- return WORKFLOW_EXIT.USAGE;
51935
+ throw CliError.usage(`--step <id> is required: ${usage}`);
51590
51936
  }
51591
51937
  const raw = (o.outcome ?? o.action)?.trim().toLowerCase();
51592
51938
  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;
51939
+ throw CliError.usage(`--outcome must be skip|complete|fail${raw ? ` (got "${raw}")` : ""}: ${usage}`);
51595
51940
  }
51596
51941
  const outcome = raw;
51597
51942
  let output;
51598
51943
  try {
51599
51944
  if (o.output !== void 0) output = parseJsonOrFile(o.output, "--output");
51600
51945
  } catch (e) {
51601
- console.error(`\u274C ${e.message}`);
51602
- return WORKFLOW_EXIT.USAGE;
51946
+ throw CliError.usage(`${e.message}`);
51603
51947
  }
51604
51948
  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;
51949
+ throw CliError.usage(`--outcome complete needs --output <json|@file> (the output the step would have produced): ${usage}`);
51607
51950
  }
51608
51951
  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;
51952
+ throw CliError.usage(`--output rides with --outcome complete only (a ${outcome} carries no output)`);
51611
51953
  }
51612
51954
  const dto = {
51613
51955
  outcome,
@@ -51620,27 +51962,43 @@ async function resolveStepCore(ctx, runId, o) {
51620
51962
  };
51621
51963
  const res = await ctx.api.resolveStep(runId, o.step, dto);
51622
51964
  if (!res.success || !res.data) {
51965
+ let detail = {};
51623
51966
  const err = res.error;
51624
51967
  const code = err?.code ?? err?.error;
51625
51968
  const status = err?.statusCode;
51626
- if (!ctx.json && err) {
51969
+ if (err) {
51627
51970
  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`);
51971
+ detail = {
51972
+ message: `step "${o.step}" does not exist on run ${runId} \u2014 list its steps: lua workflows status ${runId} --steps`
51973
+ };
51629
51974
  } 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`);
51975
+ detail = {
51976
+ message: `step "${o.step}" is not parked (running, pending or already decided) \u2014 nothing to resolve`
51977
+ };
51631
51978
  } else if (status === 409 && (code === "RUN_TERMINAL" || code === "run_terminal")) {
51632
- console.error(`\u274C run ${runId} is terminal \u2014 start a new run instead`);
51979
+ detail = {
51980
+ message: `run ${runId} is terminal \u2014 start a new run instead`
51981
+ };
51633
51982
  } 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}`);
51983
+ detail = {
51984
+ message: `--output does not satisfy the outputSchema of step "${o.step}"`,
51985
+ issues: refusalIssues(err).map((i) => ({
51986
+ path: i.path ?? "$",
51987
+ message: i.message ?? i.code
51988
+ }))
51989
+ };
51636
51990
  } 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");
51991
+ detail = {
51992
+ message: "resolve-step is a person's decision \u2014 sign in (lua login) instead of an API key"
51993
+ };
51638
51994
  } else if (status === 413 && code === "OUTPUT_TOO_LARGE") {
51639
51995
  const { bytes, maxBytes } = err;
51640
- console.error(`\u274C --output is over the cap (${bytes ?? "?"} of ${maxBytes ?? 262144} bytes serialized)`);
51996
+ detail = {
51997
+ message: `--output is over the cap (${bytes ?? "?"} of ${maxBytes ?? 262144} bytes serialized)`
51998
+ };
51641
51999
  }
51642
52000
  }
51643
- return apiFailure(ctx, res, "resolve-step");
52001
+ return apiFailure(ctx, res, "resolve-step", detail);
51644
52002
  }
51645
52003
  emitJson(ctx, res);
51646
52004
  if (!ctx.json) {
@@ -51702,24 +52060,33 @@ async function raiseBudgetCore(ctx, runId, o) {
51702
52060
  }
51703
52061
  const res = await ctx.api.raiseBudget(runId, dto);
51704
52062
  if (!res.success || !res.data) {
52063
+ let detail = {};
51705
52064
  const err = res.error;
51706
52065
  const code = err?.code ?? err?.error;
51707
52066
  const status = err?.statusCode;
51708
- if (!ctx.json && err) {
52067
+ if (err) {
51709
52068
  if (status === 400 && code === "CAP_EXCEEDED") {
51710
52069
  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`);
52070
+ detail = {
52071
+ 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`
52072
+ };
51712
52073
  } 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`);
52074
+ detail = {
52075
+ issues: refusalIssues(err),
52076
+ hint: `the raise must be above the current cap \u2014 lua workflows status ${runId} shows it`
52077
+ };
51715
52078
  } else if (status === 409 && code === "BUDGET_NOT_RAISABLE") {
51716
52079
  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`);
52080
+ detail = {
52081
+ 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`
52082
+ };
51718
52083
  } 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");
52084
+ detail = {
52085
+ message: "only the run creator or an org admin can raise a run budget"
52086
+ };
51720
52087
  }
51721
52088
  }
51722
- return apiFailure(ctx, res, "raise-budget");
52089
+ return apiFailure(ctx, res, "raise-budget", detail);
51723
52090
  }
51724
52091
  emitJson(ctx, res);
51725
52092
  if (!ctx.json) {
@@ -51746,24 +52113,20 @@ function approveResultLine(d) {
51746
52113
  __name(approveResultLine, "approveResultLine");
51747
52114
  async function approveCore(ctx, runId, o) {
51748
52115
  if (!o.approval) {
51749
- console.error("\u274C approve: --approval <id> is required");
51750
- return WORKFLOW_EXIT.USAGE;
52116
+ throw CliError.usage("approve: --approval <id> is required");
51751
52117
  }
51752
52118
  const decision = o.decision ?? "approve";
51753
52119
  if (decision !== "approve" && decision !== "deny") {
51754
- console.error("\u274C approve: --decision must be approve|deny");
51755
- return WORKFLOW_EXIT.USAGE;
52120
+ throw CliError.usage("approve: --decision must be approve|deny");
51756
52121
  }
51757
52122
  let editedPayload;
51758
52123
  try {
51759
52124
  if (o.edit) editedPayload = parseJsonOrFile(o.edit, "--edit");
51760
52125
  } catch (e) {
51761
- console.error(`\u274C ${e.message}`);
51762
- return WORKFLOW_EXIT.USAGE;
52126
+ throw CliError.usage(`${e.message}`);
51763
52127
  }
51764
52128
  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;
52129
+ throw CliError.usage("approve: --fingerprint <f> is mandatory with --edit (the payloadFingerprint from approval-payload)");
51767
52130
  }
51768
52131
  const res = await ctx.api.resolveApproval(runId, o.approval, {
51769
52132
  decision,
@@ -51772,15 +52135,22 @@ async function approveCore(ctx, runId, o) {
51772
52135
  expectedFingerprint: o.fingerprint
51773
52136
  });
51774
52137
  if (!res.success || !res.data) {
52138
+ let detail = {};
51775
52139
  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)");
52140
+ if (res.error?.statusCode === 404 && code === "APPROVAL_NOT_FOUND") {
52141
+ detail = {
52142
+ 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`
52143
+ };
52144
+ } else if (res.error?.statusCode === 409 && code === "PAYLOAD_MISMATCH") {
52145
+ detail = {
52146
+ message: `the payload changed since you looked \u2014 refetch: lua workflows approval-payload ${runId} --approval ${o.approval}`
52147
+ };
52148
+ } else if (res.error?.statusCode === 403 && code === "STEP_UP_REQUIRED") {
52149
+ detail = {
52150
+ message: "approve from the desktop with a fresh login (step-up required)"
52151
+ };
51782
52152
  }
51783
- return apiFailure(ctx, res, "approve");
52153
+ return apiFailure(ctx, res, "approve", detail);
51784
52154
  }
51785
52155
  emitJson(ctx, res);
51786
52156
  if (!ctx.json) {
@@ -51794,8 +52164,7 @@ var indentBlock = /* @__PURE__ */ __name((text, pad2 = " ") => text.split("\
51794
52164
  var APPROVAL_PAYLOAD_PAGE_MAX = 100;
51795
52165
  async function approvalPayloadCore(ctx, runId, o) {
51796
52166
  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;
52167
+ throw CliError.usage("approval-payload: --approval <id> is required \u2014 lua workflows approval-payload <runId> --approval <wfa_\u2026> [--path <array.path>]");
51799
52168
  }
51800
52169
  let limit;
51801
52170
  try {
@@ -51808,8 +52177,7 @@ async function approvalPayloadCore(ctx, runId, o) {
51808
52177
  throw e;
51809
52178
  }
51810
52179
  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;
52180
+ throw CliError.usage("approval-payload: --cursor / --limit page one array \u2014 pass --path <array.path> with them");
51813
52181
  }
51814
52182
  const res = await ctx.api.getApprovalPayload(runId, o.approval, {
51815
52183
  path: o.path,
@@ -51817,13 +52185,18 @@ async function approvalPayloadCore(ctx, runId, o) {
51817
52185
  limit
51818
52186
  });
51819
52187
  if (!res.success || !res.data) {
52188
+ let detail = {};
51820
52189
  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>]");
52190
+ if (res.error?.statusCode === 404 && code === "APPROVAL_NOT_FOUND") {
52191
+ detail = {
52192
+ message: `approval "${o.approval}" not found on run ${runId} \u2014 lua workflows status ${runId} --steps --json shows the suspended step's suspend.approvalId`
52193
+ };
52194
+ } else if (res.error?.statusCode === 413 && code === "PAYLOAD_PAGE_REQUIRED") {
52195
+ detail = {
52196
+ message: "the payload is too large to read whole \u2014 page an array with --path <array.path> [--limit <n>] [--cursor <c>]"
52197
+ };
51825
52198
  }
51826
- return apiFailure(ctx, res, "approval-payload");
52199
+ return apiFailure(ctx, res, "approval-payload", detail);
51827
52200
  }
51828
52201
  emitJson(ctx, res);
51829
52202
  if (!ctx.json) {
@@ -51887,16 +52260,9 @@ function reservedSecretKeyPaths(value3, path25 = "", depth = 0, out = []) {
51887
52260
  __name(reservedSecretKeyPaths, "reservedSecretKeyPaths");
51888
52261
  async function signalCore(ctx, runId, name, o) {
51889
52262
  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;
52263
+ throw CliError.usage("signal: a signal name is required", "lua workflows signal <runId> <name> [--payload <json|@file>]");
51899
52264
  }
52265
+ const payload = parsedOrUsage(() => o.payload ? parseJsonOrFile(o.payload, "--payload") : void 0);
51900
52266
  const reserved = reservedSecretKeyPaths(payload);
51901
52267
  if (reserved.length > 0) {
51902
52268
  const many = reserved.length > 1;
@@ -51908,22 +52274,24 @@ async function signalCore(ctx, runId, name, o) {
51908
52274
  });
51909
52275
  if (!res.success || !res.data) {
51910
52276
  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`);
52277
+ const where = res.error?.stepId ? `step "${res.error.stepId}"` : "the waiting step";
52278
+ let detail = {};
52279
+ if (res.error?.statusCode === 400 && code === "SIGNAL_SCHEMA_INVALID") {
52280
+ detail = {
52281
+ message: `signal "${name}" rejected \u2014 the payload does not match the schema ${where} declares`,
52282
+ issues: res.error?.issues,
52283
+ hint: "fix the payload and send it again (a --dedupe-key is not consumed by a rejected signal)"
52284
+ };
52285
+ } else if (res.error?.statusCode === 422 && code === "SIGNAL_SCHEMA_UNCOMPILABLE") {
52286
+ detail = {
52287
+ message: `signal "${name}" could not be checked \u2014 the schema ${where} declares does not compile`,
52288
+ issues: (res.error?.issues ?? []).map((issue) => ({
52289
+ message: issue.message ?? "schema does not compile"
52290
+ })),
52291
+ 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"
52292
+ };
51925
52293
  }
51926
- return apiFailure(ctx, res, "signal");
52294
+ return apiFailure(ctx, res, "signal", detail);
51927
52295
  }
51928
52296
  emitJson(ctx, res);
51929
52297
  const r = res.data;
@@ -51936,8 +52304,7 @@ async function signalCore(ctx, runId, name, o) {
51936
52304
  __name(signalCore, "signalCore");
51937
52305
  async function replayCore(ctx, runId, o) {
51938
52306
  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;
52307
+ throw CliError.usage("replay: only `--local` is available (server-side replay is not a v1 route)");
51941
52308
  }
51942
52309
  const runRes = await ctx.api.getRun(runId);
51943
52310
  if (!runRes.success || !runRes.data) return apiFailure(ctx, runRes, "replay");
@@ -51946,8 +52313,7 @@ async function replayCore(ctx, runId, o) {
51946
52313
  try {
51947
52314
  manifestWorkflows = getPrimitivesByKind(loadManifest(), PrimitiveKind.WORKFLOW);
51948
52315
  } catch (e) {
51949
- console.error(`\u274C replay --local needs a compiled project: ${e.message}`);
51950
- return WORKFLOW_EXIT.USAGE;
52316
+ throw CliError.usage(`replay --local needs a compiled project: ${e.message}`);
51951
52317
  }
51952
52318
  const wf = (o.workflowName ? manifestWorkflows.find((w) => w.name === o.workflowName) : void 0) ?? manifestWorkflows.find((w) => w.graphHash === run.graphHash);
51953
52319
  if (!wf) {
@@ -52193,11 +52559,11 @@ async function deleteRunCore(ctx, runId, o) {
52193
52559
  const res = await ctx.api.eraseRun(runId);
52194
52560
  if (!res.success || !res.data) {
52195
52561
  const code = res.error?.code ?? res.error?.error;
52196
- if (!ctx.json && res.error?.statusCode === 409 && code === "RUN_NOT_TERMINAL") {
52562
+ if (res.error?.statusCode === 409 && code === "RUN_NOT_TERMINAL") {
52197
52563
  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;
52564
+ return apiFailure(ctx, res, "delete-run", {
52565
+ message: `run is \`${status ?? "live"}\`; cancel it first: lua workflows cancel ${runId}`
52566
+ });
52201
52567
  }
52202
52568
  return apiFailure(ctx, res, "delete-run");
52203
52569
  }
@@ -52315,17 +52681,14 @@ async function archiveRunsCore(ctx, o) {
52315
52681
  const now = Date.now();
52316
52682
  const since = parseSinceFlag(o.since, now);
52317
52683
  if (!Number.isFinite(since)) {
52318
- console.error("\u274C archive-runs: --since <iso|dur> is required (e.g. --since 8d)");
52319
- return WORKFLOW_EXIT.USAGE;
52684
+ throw CliError.usage("archive-runs: --since <iso|dur> is required (e.g. --since 8d)");
52320
52685
  }
52321
52686
  const until = o.until ? Date.parse(o.until) : void 0;
52322
52687
  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;
52688
+ throw CliError.usage(`archive-runs: --until is not an ISO timestamp ("${o.until}")`);
52325
52689
  }
52326
52690
  if (!o.out) {
52327
- console.error("\u274C archive-runs: --out <dir> is required");
52328
- return WORKFLOW_EXIT.USAGE;
52691
+ throw CliError.usage("archive-runs: --out <dir> is required");
52329
52692
  }
52330
52693
  if (/^(s3|gs):\/\//.test(o.out)) {
52331
52694
  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 +52952,14 @@ async function workspaceCore(ctx, runId, o) {
52589
52952
  note: o.note
52590
52953
  } : {});
52591
52954
  if (!res2.success || !res2.data) {
52592
- if (res2.error?.statusCode === 409 && !ctx.json) {
52955
+ let detail = {};
52956
+ if (res2.error?.statusCode === 409) {
52593
52957
  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`);
52958
+ detail = {
52959
+ message: `Workspace of ${runId} is in use${stepId ? ` by step ${stepId}` : ""} \u2014 cancel the run or wait for the step to finish`
52960
+ };
52595
52961
  }
52596
- return apiFailure(ctx, res2, "workspace");
52962
+ return apiFailure(ctx, res2, "workspace", detail);
52597
52963
  }
52598
52964
  emitJson(ctx, res2);
52599
52965
  if (!ctx.json) {
@@ -52674,14 +53040,12 @@ function printJobHeader(v) {
52674
53040
  __name(printJobHeader, "printJobHeader");
52675
53041
  async function jobLogsCore(ctx, runId, stepId, o) {
52676
53042
  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;
53043
+ throw CliError.usage(`job-logs: a step id is required \u2014 lua workflows job-logs ${runId} <stepId>`);
52679
53044
  }
52680
53045
  const attempt = o.attempt !== void 0 && o.attempt !== "" ? Number(o.attempt) : void 0;
52681
53046
  const tail = o.tail !== void 0 && o.tail !== "" ? Number(o.tail) : JOB_LOG_TAIL_DEFAULT;
52682
53047
  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;
53048
+ throw CliError.usage(`job-logs: --attempt must be \u2265 1 and --tail an integer in 1..${JOB_LOG_TAIL_MAX}`);
52685
53049
  }
52686
53050
  let printed = 0;
52687
53051
  let first = true;
@@ -52691,18 +53055,18 @@ async function jobLogsCore(ctx, runId, stepId, o) {
52691
53055
  tail
52692
53056
  });
52693
53057
  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;
53058
+ let detail = {};
53059
+ if (res.error?.statusCode === 404 && res.error?.code === "JOB_NOT_FOUND") {
53060
+ detail = {
53061
+ message: `${stepId} is not a Job-tier step of ${runId} (try: lua workflows jobs ${runId})`
53062
+ };
53063
+ } else if (res.error?.statusCode === 404 && res.error?.code === "JOB_LOGS_NOT_PERSISTED") {
53064
+ detail = {
53065
+ message: `job-logs: ${res.error.message}`,
53066
+ hint: `lua workflows status ${runId} --steps shows the step error; --attempt <n> reads another attempt.`
53067
+ };
52704
53068
  }
52705
- return apiFailure(ctx, res, "job-logs");
53069
+ return apiFailure(ctx, res, "job-logs", detail);
52706
53070
  }
52707
53071
  const v = res.data;
52708
53072
  if (ctx.json && !o.follow) {
@@ -52965,38 +53329,54 @@ function subVerb(group, noun, raw, verbs) {
52965
53329
  }
52966
53330
  __name(subVerb, "subVerb");
52967
53331
  function goalFailure(ctx, res, verb, ref) {
53332
+ let detail = {};
52968
53333
  const err = res.error;
52969
53334
  const code = err?.code ?? err?.error;
52970
53335
  const status = err?.statusCode;
52971
- if (!ctx.json && err) {
53336
+ if (err) {
52972
53337
  if (status === 409 && code === "GOAL_NOT_ACTIVE") {
52973
53338
  const now = err.status ?? "not active";
52974
53339
  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>")}`);
53340
+ detail = {
53341
+ message: `goal ${ref ?? ""} is ${now} \u2014 ${rule}`,
53342
+ hint: verb === "goals resume" && now === "paused" ? goalRaiseHint(ref ?? "<goalId>") : void 0
53343
+ };
52977
53344
  } else if (status === 400 && code === "GOAL_RAISE_BELOW_SPENT") {
52978
53345
  const { field, value: value3, spent } = err;
52979
53346
  const flag = field === "maxRuns" ? "--max-runs" : "--max-credits";
52980
53347
  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 ?? "?"}`);
53348
+ detail = {
53349
+ message: `${flag} ${value3 ?? "?"} is not above the ${spent ?? "?"} ${used} \u2014 raise it past ${spent ?? "?"}`
53350
+ };
52982
53351
  } else if (status === 409 && code === "GOAL_VERSION_CONFLICT") {
52983
53352
  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}` : ""}`);
53353
+ detail = {
53354
+ 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}` : ""}`
53355
+ };
52985
53356
  } 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`);
53357
+ detail = {
53358
+ message: `goal cap reached (${err.cap ?? "?"} per agent) \u2014 close or finish one first: lua workflows goals list --status active`
53359
+ };
52987
53360
  } 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}` : ""}`);
53361
+ detail = {
53362
+ issues: refusalIssues(err)
53363
+ };
52990
53364
  } else if (status === 400 && code === "GOAL_MAX_RUNS_INVALID") {
52991
- console.error(`\u274C --max-runs must be 1..${GOAL_MAX_RUNS_CAP}`);
53365
+ detail = {
53366
+ message: `--max-runs must be 1..${GOAL_MAX_RUNS_CAP}`
53367
+ };
52992
53368
  } 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}`);
53369
+ detail = {
53370
+ message: `that workflow belongs to another agent \u2014 goals are scoped to ${ctx.agentId}`
53371
+ };
52994
53372
  } else if (status === 409 && code === "GOAL_SCHEDULE") {
52995
53373
  const goalId = err.goalId ?? "?";
52996
- console.error(`\u274C GOAL_SCHEDULE: ${goalScheduleRefusal(ref ?? "<jobId>", goalId)}`);
53374
+ detail = {
53375
+ message: `GOAL_SCHEDULE: ${goalScheduleRefusal(ref ?? "<jobId>", goalId)}`
53376
+ };
52997
53377
  }
52998
53378
  }
52999
- return apiFailure(ctx, res, verb);
53379
+ return apiFailure(ctx, res, verb, detail);
53000
53380
  }
53001
53381
  __name(goalFailure, "goalFailure");
53002
53382
  async function goalsCore(ctx, target, extra, o) {
@@ -53005,8 +53385,7 @@ async function goalsCore(ctx, target, extra, o) {
53005
53385
  if (verb === "list") return goalsListCore(ctx, o, extra);
53006
53386
  if (verb === "create") return goalsCreateCore(ctx, o, extra);
53007
53387
  if (!extra) {
53008
- console.error(`\u274C goals ${verb}: a goal id is required \u2014 lua workflows goals ${verb} <goalId>`);
53009
- return WORKFLOW_EXIT.USAGE;
53388
+ throw CliError.usage(`goals ${verb}: a goal id is required \u2014 lua workflows goals ${verb} <goalId>`);
53010
53389
  }
53011
53390
  if (verb === "get") return goalsGetCore(ctx, extra);
53012
53391
  if (verb === "edit") return goalsEditCore(ctx, extra, o);
@@ -53016,8 +53395,7 @@ async function goalsCore(ctx, target, extra, o) {
53016
53395
  __name(goalsCore, "goalsCore");
53017
53396
  async function goalsListCore(ctx, o, positional) {
53018
53397
  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;
53398
+ throw CliError.usage(`goals list: --status must be ${GOAL_STATUSES.join("|")} (got "${o.status}")`);
53021
53399
  }
53022
53400
  let limit;
53023
53401
  try {
@@ -53026,8 +53404,7 @@ async function goalsListCore(ctx, o, positional) {
53026
53404
  max: GOAL_PAGE_LIMIT
53027
53405
  });
53028
53406
  } catch (e) {
53029
- console.error(`\u274C ${e.message}`);
53030
- return WORKFLOW_EXIT.USAGE;
53407
+ throw CliError.usage(`${e.message}`);
53031
53408
  }
53032
53409
  const list = await loadWorkflowList(ctx);
53033
53410
  if (!list) return WORKFLOW_EXIT.API;
@@ -53188,8 +53565,7 @@ async function goalsEditCore(ctx, goalId, o) {
53188
53565
  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
53566
  } catch (e) {
53190
53567
  if (e instanceof WorkflowLocalUsageError) {
53191
- console.error(`\u274C goals edit: ${e.message}`);
53192
- return WORKFLOW_EXIT.USAGE;
53568
+ throw CliError.usage(`goals edit: ${e.message}`);
53193
53569
  }
53194
53570
  throw e;
53195
53571
  }
@@ -53221,8 +53597,7 @@ async function goalsRaiseCore(ctx, goalId, o) {
53221
53597
  if (ifMatch !== void 0) dto.ifMatch = ifMatch;
53222
53598
  } catch (e) {
53223
53599
  if (e instanceof WorkflowLocalUsageError) {
53224
- console.error(`\u274C goals raise: ${e.message}`);
53225
- return WORKFLOW_EXIT.USAGE;
53600
+ throw CliError.usage(`goals raise: ${e.message}`);
53226
53601
  }
53227
53602
  throw e;
53228
53603
  }
@@ -53308,20 +53683,16 @@ __name(parseCadenceFlag, "parseCadenceFlag");
53308
53683
  async function goalsCreateCore(ctx, o, positional) {
53309
53684
  const target = positional ?? o.workflowName ?? o.workflow;
53310
53685
  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;
53686
+ throw CliError.usage("goals create: a workflow is required \u2014 lua workflows goals create <workflow> (or -i <workflow>)");
53313
53687
  }
53314
53688
  if (!o.objective?.trim()) {
53315
- console.error("\u274C goals create: --objective <text> is required");
53316
- return WORKFLOW_EXIT.USAGE;
53689
+ throw CliError.usage("goals create: --objective <text> is required");
53317
53690
  }
53318
53691
  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;
53692
+ 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
53693
  }
53322
53694
  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;
53695
+ throw CliError.usage("goals create: one of --judge-predicate <spec> | --judge-agent <agentId|'$self'> is required");
53325
53696
  }
53326
53697
  const usage = /* @__PURE__ */ __name((m) => new WorkflowLocalUsageError("usage", m), "usage");
53327
53698
  let dto;
@@ -53381,8 +53752,7 @@ async function goalsCreateCore(ctx, o, positional) {
53381
53752
  };
53382
53753
  } catch (e) {
53383
53754
  if (e instanceof WorkflowLocalUsageError) {
53384
- console.error(`\u274C ${e.message}`);
53385
- return WORKFLOW_EXIT.USAGE;
53755
+ throw CliError.usage(`${e.message}`);
53386
53756
  }
53387
53757
  throw e;
53388
53758
  }
@@ -53424,8 +53794,7 @@ async function schedulesCore(ctx, target, extra, o) {
53424
53794
  if (verb === "list") return schedulesListCore(ctx, o);
53425
53795
  if (verb === "create") return schedulesCreateCore(ctx, o, extra);
53426
53796
  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;
53797
+ throw CliError.usage(`schedules ${verb}: a schedule (job) id is required \u2014 lua workflows schedules ${verb} <jobId>`);
53429
53798
  }
53430
53799
  if (verb === "delete") return schedulesDeleteCore(ctx, extra, o);
53431
53800
  return schedulesPatchCore(ctx, extra, verb, o);
@@ -53480,17 +53849,19 @@ async function schedulesDeleteCore(ctx, jobId, o) {
53480
53849
  const goalRes = await ctx.api.getGoal(goalId);
53481
53850
  if (goalRes.success && goalRes.data) goalStatus = goalRes.data.status;
53482
53851
  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");
53852
+ return apiFailure(ctx, goalRes, "schedules delete", {
53853
+ message: `cannot prove goal ${goalId} of ${jobId} has ended (goal unreadable) \u2014 refusing to delete`
53854
+ });
53485
53855
  }
53486
53856
  } else {
53487
53857
  const goals = await loadGoals(ctx, row2.workflowId);
53488
53858
  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
53859
  return apiFailure(ctx, {
53491
53860
  success: false,
53492
53861
  error: goals.error
53493
- }, "schedules delete");
53862
+ }, "schedules delete", {
53863
+ message: `cannot prove ${jobId} is not a goal's cadence (goals unavailable) \u2014 refusing to delete`
53864
+ });
53494
53865
  }
53495
53866
  const owner = goals.items.find((g) => g.jobId === jobId);
53496
53867
  goalId = owner?.goalId;
@@ -53541,8 +53912,7 @@ var SCHEDULE_NOTIFY = [
53541
53912
  async function schedulesCreateCore(ctx, o, positional) {
53542
53913
  const target = positional ?? o.workflowName ?? o.workflow;
53543
53914
  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;
53915
+ throw CliError.usage('schedules create: a workflow is required \u2014 lua workflows schedules create <workflow> --cadence "0 9 * * 1" --timezone Europe/London');
53546
53916
  }
53547
53917
  let draft;
53548
53918
  try {
@@ -53669,11 +54039,12 @@ async function schedulesPatchCore(ctx, jobId, verb, o) {
53669
54039
  if (!rowRes.success || !rowRes.data) return apiFailure(ctx, rowRes, label);
53670
54040
  const owner = await scheduleGoalOwner(ctx, rowRes.data);
53671
54041
  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
54042
  return apiFailure(ctx, {
53674
54043
  success: false,
53675
54044
  error: owner.error
53676
- }, label);
54045
+ }, label, {
54046
+ message: `cannot prove ${jobId} is not a goal's cadence (goals unavailable) \u2014 refusing to ${verb}`
54047
+ });
53677
54048
  }
53678
54049
  if (owner.goalId) {
53679
54050
  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 +55724,7 @@ init_cli();
55353
55724
  // src/api/marketplace.api.service.ts
55354
55725
  init_constants();
55355
55726
  init_lua_fetch();
55727
+ init_http_client();
55356
55728
  init_request_credential();
55357
55729
  var MarketplaceApiService = class {
55358
55730
  static {
@@ -55377,8 +55749,7 @@ var MarketplaceApiService = class {
55377
55749
  headers
55378
55750
  });
55379
55751
  if (!response.ok) {
55380
- const errorText = await response.text();
55381
- throw new Error(`API Error: ${response.status} ${response.statusText} - ${errorText}`);
55752
+ throw await refusalFromResponse(response);
55382
55753
  }
55383
55754
  if (response.status === 204) {
55384
55755
  return null;
@@ -55501,6 +55872,7 @@ import { readFileSync as readFileSync14 } from "fs";
55501
55872
  // src/api/template.api.service.ts
55502
55873
  init_constants();
55503
55874
  init_lua_fetch();
55875
+ init_http_client();
55504
55876
  init_request_credential();
55505
55877
  var TemplateApiService = class {
55506
55878
  static {
@@ -55523,8 +55895,7 @@ var TemplateApiService = class {
55523
55895
  headers
55524
55896
  });
55525
55897
  if (!response.ok) {
55526
- const errorText = await response.text();
55527
- throw new Error(`API Error: ${response.status} ${response.statusText} - ${errorText}`);
55898
+ throw await refusalFromResponse(response);
55528
55899
  }
55529
55900
  if (response.status === 204) {
55530
55901
  return null;
@@ -55747,6 +56118,11 @@ function sleep2(ms) {
55747
56118
  return new Promise((resolve7) => setTimeout(resolve7, ms));
55748
56119
  }
55749
56120
  __name(sleep2, "sleep");
56121
+ function missingVersionOrRethrow(error) {
56122
+ if (CliError.isCliError(error) && error.statusCode === 404) return null;
56123
+ throw error;
56124
+ }
56125
+ __name(missingVersionOrRethrow, "missingVersionOrRethrow");
55750
56126
  async function resolveTemplateId(templateApi, options, message) {
55751
56127
  if (options.templateId) return options.templateId;
55752
56128
  writeProgress("\u{1F504} Loading your templates...");
@@ -56123,8 +56499,8 @@ async function templateViewAction(templateApi, options) {
56123
56499
  if (!Number.isFinite(versionNum) || versionNum <= 0) {
56124
56500
  throw new Error(`Invalid --version "${options.version}": must be a positive integer.`);
56125
56501
  }
56126
- const version = await templateApi.getVersion(templateId, versionNum).catch(() => null);
56127
- if (!version) throw new Error(`Version v${versionNum} not found for this template.`);
56502
+ const version = await templateApi.getVersion(templateId, versionNum).catch(missingVersionOrRethrow);
56503
+ if (!version) throw CliError.notFound(`Version v${versionNum} not found for this template.`);
56128
56504
  if (options.json) {
56129
56505
  console.log(JSON.stringify(version, null, 2));
56130
56506
  return;
@@ -56288,8 +56664,8 @@ async function templateApplyAction(templateApi, options) {
56288
56664
  throw new Error("This template has no published versions. Run `lua marketplace template publish` first.");
56289
56665
  }
56290
56666
  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.`);
56667
+ const versionObj = await templateApi.getVersion(templateId, versionNum).catch(missingVersionOrRethrow);
56668
+ if (!versionObj) throw CliError.notFound(`Version v${versionNum} not found for this template.`);
56293
56669
  if (!options.force) {
56294
56670
  console.log(`
56295
56671
  Apply plan:`);
@@ -56540,45 +56916,57 @@ var SKILL_ACTION_LABELS = {
56540
56916
  org: "View this organization's owned skills"
56541
56917
  };
56542
56918
  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);
56919
+ return withErrorHandling(
56920
+ async () => {
56921
+ await marketplaceDispatch(noun, action, options);
56922
+ },
56923
+ "marketplace",
56924
+ // LUA-803: under `--json` an escaped refusal (a template 403 / 404, a network failure) is the typed envelope on
56925
+ // stdout, not the `✖` line.
56926
+ {
56927
+ json: /* @__PURE__ */ __name(() => !!options.json, "json")
56577
56928
  }
56578
- return skillMarketplaceCommand(action, options);
56579
- }, "marketplace");
56929
+ );
56580
56930
  }
56581
56931
  __name(marketplaceCommand, "marketplaceCommand");
56932
+ async function marketplaceDispatch(noun, action, options) {
56933
+ let domain;
56934
+ if (noun) {
56935
+ domain = validateOrSuggest("marketplace.noun", noun);
56936
+ } else {
56937
+ const domainAnswer = await safePrompt([
56938
+ {
56939
+ type: "list",
56940
+ name: "domain",
56941
+ message: "What would you like to browse?",
56942
+ choices: [
56943
+ {
56944
+ name: "Skills",
56945
+ value: "skill"
56946
+ },
56947
+ {
56948
+ name: "Agent templates",
56949
+ value: "template"
56950
+ },
56951
+ {
56952
+ name: "Exit",
56953
+ value: "exit"
56954
+ }
56955
+ ]
56956
+ }
56957
+ ]);
56958
+ if (!domainAnswer || domainAnswer.domain === "exit") {
56959
+ console.log("\n\u{1F44B} Goodbye!\n");
56960
+ return;
56961
+ }
56962
+ domain = domainAnswer.domain;
56963
+ }
56964
+ if (domain === "template") {
56965
+ return templateCommand(action, options);
56966
+ }
56967
+ return skillMarketplaceCommand(action, options);
56968
+ }
56969
+ __name(marketplaceDispatch, "marketplaceDispatch");
56582
56970
  async function skillMarketplaceCommand(action, options = {}) {
56583
56971
  const { config, apiKey } = await initializeCommand();
56584
56972
  const marketplaceApi = new MarketplaceApiService(apiKey, config.agent?.orgId);
@@ -64712,7 +65100,7 @@ Examples:
64712
65100
  $ lua init --from-agent-id baseAgent_agent_xxx Duplicate agent + scaffold project
64713
65101
  $ lua init --from-agent-id baseAgent_agent_xxx --org-id <id> Cross-org duplicate
64714
65102
  `).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({
65103
+ 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
65104
  ...opts,
64717
65105
  topLevel: true
64718
65106
  }));