lua-cli 3.32.3 → 3.32.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -65,6 +65,20 @@ var init_auth_error = __esm({
65
65
  });
66
66
 
67
67
  // src/errors/cli.error.ts
68
+ function isTypedCliError(error) {
69
+ return CliError.isCliError(error) || AuthenticationError.isAuthenticationError(error);
70
+ }
71
+ function listHint(title, items, bullet = "- ") {
72
+ if (items.length === 0) return void 0;
73
+ return [
74
+ title,
75
+ ...items.map((item) => ` ${bullet}${item}`)
76
+ ].join("\n");
77
+ }
78
+ function joinHint(...parts) {
79
+ const lines = parts.filter((part) => typeof part === "string" && part.length > 0);
80
+ return lines.length > 0 ? lines.join("\n") : void 0;
81
+ }
68
82
  function isAccessDeniedError(error) {
69
83
  if (CliError.isCliError(error)) return error.statusCode === 403;
70
84
  return error instanceof Error && error.message.startsWith("Access denied (403)");
@@ -265,10 +279,31 @@ var init_cli_error = __esm({
265
279
  statusCode: 403
266
280
  });
267
281
  }
282
+ /**
283
+ * An API refusal the site already holds the status of (LUA-766) — classified by the same table the top-level
284
+ * classifier applies to an untyped error: 401 auth · 403 forbidden · 404 not_found · other 4xx `http_<status>`
285
+ * (10) · 5xx / 0 unavailable (11, with the network hint unless the site gives its own) · no status `error` (1).
286
+ * A command that reads `response.error.statusCode` throws through here, so `lua logs` on a 503 exits 11 like
287
+ * every other verb instead of printing the message itself and then throwing an exit-1 `Error`.
288
+ */
289
+ static fromStatus(statusCode, message, hint) {
290
+ const reported = classifyCliError(Object.assign(new Error(message), {
291
+ statusCode
292
+ }));
293
+ const classHint = reported.exitCode === CLI_EXIT.UNAVAILABLE ? UNAVAILABLE_HINT : reported.hint;
294
+ return new _CliError(reported.code, message, {
295
+ exitCode: reported.exitCode,
296
+ hint: hint ?? classHint,
297
+ statusCode
298
+ });
299
+ }
268
300
  static isCliError(error) {
269
301
  return error instanceof _CliError || typeof error === "object" && error !== null && error.isCliError === true;
270
302
  }
271
303
  };
304
+ __name(isTypedCliError, "isTypedCliError");
305
+ __name(listHint, "listHint");
306
+ __name(joinHint, "joinHint");
272
307
  __name(isAccessDeniedError, "isAccessDeniedError");
273
308
  HandledCliError = class extends Error {
274
309
  static {
@@ -953,6 +988,84 @@ function isDesktopFileCommandName(value3) {
953
988
  function isDesktopFileSessionId(value3) {
954
989
  return typeof value3 === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value3);
955
990
  }
991
+ function isModelIdSentinel(input) {
992
+ const lower = (input ?? "").trim().toLowerCase();
993
+ return lower === "auto" || lower.startsWith("auto/");
994
+ }
995
+ function normalizeModelId(input, registry) {
996
+ const requested = typeof input === "string" ? input.trim() : "";
997
+ if (!requested) return {
998
+ ok: false,
999
+ reason: "empty",
1000
+ requested,
1001
+ candidates: []
1002
+ };
1003
+ const lower = requested.toLowerCase();
1004
+ if (isModelIdSentinel(lower)) return {
1005
+ ok: true,
1006
+ id: lower,
1007
+ form: "sentinel"
1008
+ };
1009
+ const slash = requested.indexOf("/");
1010
+ const malformed = slash === 0;
1011
+ const provider = slash > 0 ? lower.slice(0, slash) : void 0;
1012
+ if (provider && MODEL_ID_BYOK_PROVIDERS.includes(provider)) {
1013
+ return {
1014
+ ok: true,
1015
+ id: requested,
1016
+ form: "byok"
1017
+ };
1018
+ }
1019
+ const bareId = slash >= 0 ? lower.slice(slash + 1) : lower;
1020
+ const lastSegment = bareId.slice(bareId.lastIndexOf("/") + 1);
1021
+ const exact = /* @__PURE__ */ new Set();
1022
+ const hints = /* @__PURE__ */ new Set();
1023
+ for (const code of registry) {
1024
+ if (typeof code !== "string" || !code) continue;
1025
+ const codeLower = code.toLowerCase();
1026
+ if (!malformed && codeLower === lower) return {
1027
+ ok: true,
1028
+ id: code,
1029
+ form: provider ? "canonical" : "bare"
1030
+ };
1031
+ const i = codeLower.indexOf("/");
1032
+ if (i < 0) continue;
1033
+ const codeBare = codeLower.slice(i + 1);
1034
+ if (bareId && codeBare === bareId) exact.add(code);
1035
+ else if (lastSegment && codeBare.slice(codeBare.lastIndexOf("/") + 1) === lastSegment) hints.add(code);
1036
+ }
1037
+ const sorted = [
1038
+ ...exact
1039
+ ].sort();
1040
+ if (!provider && !malformed && sorted.length === 1) return {
1041
+ ok: true,
1042
+ id: sorted[0],
1043
+ form: "bare"
1044
+ };
1045
+ if (!provider && !malformed && sorted.length > 1) {
1046
+ return {
1047
+ ok: false,
1048
+ reason: "ambiguous",
1049
+ requested,
1050
+ candidates: sorted
1051
+ };
1052
+ }
1053
+ return {
1054
+ ok: false,
1055
+ reason: "unknown",
1056
+ requested,
1057
+ candidates: sorted.length ? sorted : [
1058
+ ...hints
1059
+ ].sort()
1060
+ };
1061
+ }
1062
+ function modelUnresolvedMessage(r) {
1063
+ if (r.reason === "empty") return "model pin is empty \u2014 pin an approved model (provider/model) or omit `model`";
1064
+ if (r.reason === "ambiguous") {
1065
+ return `model "${r.requested}" does not resolve to one approved model \u2014 it names ${r.candidates.length}; pin one of: ${r.candidates.join(", ")}`;
1066
+ }
1067
+ return r.candidates.length ? `model "${r.requested}" does not resolve to an approved model \u2014 accepted forms: ${r.candidates.join(", ")}` : `model "${r.requested}" does not resolve to an approved model \u2014 accepted forms are the registry's provider-prefixed ids (provider/model) or a bare id that names exactly one of them`;
1068
+ }
956
1069
  function isImplicitModelSelectionSource(source) {
957
1070
  return source !== void 0 && IMPLICIT_MODEL_SELECTION_SOURCES.includes(source);
958
1071
  }
@@ -1541,7 +1654,7 @@ function extractSingleJsonValue(text) {
1541
1654
  };
1542
1655
  }
1543
1656
  }
1544
- var __defProp2, __name2, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, MCP_TOOL_READ_VERB_RE, MCP_DRAFT_CREATE_VERBS, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, DESKTOP_FILE_COMMANDS, DESKTOP_FILE_COMMAND_SET, REASONING_EFFORT_VALUES, 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;
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;
1545
1658
  var init_dist = __esm({
1546
1659
  "../shared-types/dist/index.mjs"() {
1547
1660
  "use strict";
@@ -1849,6 +1962,16 @@ var init_dist = __esm({
1849
1962
  __name2(isDesktopFileCommandName, "isDesktopFileCommandName");
1850
1963
  __name(isDesktopFileSessionId, "isDesktopFileSessionId");
1851
1964
  __name2(isDesktopFileSessionId, "isDesktopFileSessionId");
1965
+ MODEL_ID_BYOK_PROVIDERS = [
1966
+ "azure",
1967
+ "bedrock"
1968
+ ];
1969
+ __name(isModelIdSentinel, "isModelIdSentinel");
1970
+ __name2(isModelIdSentinel, "isModelIdSentinel");
1971
+ __name(normalizeModelId, "normalizeModelId");
1972
+ __name2(normalizeModelId, "normalizeModelId");
1973
+ __name(modelUnresolvedMessage, "modelUnresolvedMessage");
1974
+ __name2(modelUnresolvedMessage, "modelUnresolvedMessage");
1852
1975
  REASONING_EFFORT_VALUES = [
1853
1976
  "off",
1854
1977
  "minimal",
@@ -3797,6 +3920,129 @@ function resolveMapping(cfg, ctx) {
3797
3920
  value: result
3798
3921
  };
3799
3922
  }
3923
+ function describeApproverSpecRefusal(spec) {
3924
+ const raw = spec === void 0 ? "undefined" : JSON.stringify(spec) ?? String(spec);
3925
+ const written = raw.length > APPROVER_WRITTEN_MAX ? `${raw.slice(0, APPROVER_WRITTEN_MAX - 1)}\u2026` : raw;
3926
+ const users = typeof spec === "object" && spec !== null ? spec.users : void 0;
3927
+ const approver = typeof users === "string" && USER_ID_SHAPED_RE.test(users) ? {
3928
+ users: [
3929
+ users
3930
+ ]
3931
+ } : "creator";
3932
+ const message = `approver ${written} is not an approver \u2014 legal: ${APPROVER_SPEC_SHAPES.join(" | ")}. 'creator' is the person who started the run: write approver:'creator' for "ask me" / "I approve"; {users:[\u2026]} takes user ids, never emails, names or {type:'user'}` + (approver === "creator" ? "" : `; here: approver:${JSON.stringify(approver)}`);
3933
+ return {
3934
+ approver,
3935
+ written,
3936
+ message
3937
+ };
3938
+ }
3939
+ function bindingRootsOk(template22) {
3940
+ const refs = [
3941
+ ...template22.matchAll(/\$\{\s*([A-Za-z_$][\w$]*)/g)
3942
+ ].map((m) => m[1]);
3943
+ return refs.length > 0 && refs.every((r) => BINDING_ROOTS.includes(r));
3944
+ }
3945
+ function isTemplateBinding(v) {
3946
+ return typeof v === "object" && v !== null && typeof v.template === "string";
3947
+ }
3948
+ function approvalEditable(node) {
3949
+ if (node.editable === true) return true;
3950
+ if (node.editable === false) return false;
3951
+ return Array.isArray(node.editablePaths) && node.editablePaths.length > 0;
3952
+ }
3953
+ function validateApproverBlock(node, opts = {
3954
+ path: "approval"
3955
+ }) {
3956
+ const issues = [];
3957
+ const push = /* @__PURE__ */ __name3((code, path25, message, severity = "error") => issues.push({
3958
+ code,
3959
+ path: path25,
3960
+ severity,
3961
+ message
3962
+ }), "push");
3963
+ const checkSpec = /* @__PURE__ */ __name3((spec, path25) => {
3964
+ const r = ApproverSpecSchema.safeParse(spec);
3965
+ if (!r.success) {
3966
+ const users = spec?.users;
3967
+ if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path25, `at most ${APPROVER_SPEC_MAX_USERS} users`);
3968
+ else {
3969
+ const refusal = describeApproverSpecRefusal(spec);
3970
+ issues.push({
3971
+ code: "approver-invalid",
3972
+ path: path25,
3973
+ severity: "error",
3974
+ message: refusal.message,
3975
+ repair: {
3976
+ approver: refusal.approver,
3977
+ written: refusal.written
3978
+ }
3979
+ });
3980
+ }
3981
+ return;
3982
+ }
3983
+ const s = r.data;
3984
+ if (typeof s === "object") {
3985
+ if ("governance" in s && !opts.governanceEnabled) push("approver-governance-unavailable", path25, "governance reviewer routing is not enabled for this deployment");
3986
+ if ("group" in s && typeof s.group === "string" && !opts.scimEnabled && opts.idpGroups?.includes(s.group)) push("approver-idp-group-unavailable", path25, "IdP-group approvers are not enabled for this deployment");
3987
+ const binding = "users" in s ? s.users : "role" in s ? s.role : "group" in s ? s.group : void 0;
3988
+ if (isTemplateBinding(binding)) {
3989
+ if (!bindingRootsOk(binding.template)) push("approver-binding-invalid", `${path25}.template`, "binding root must be initData / stepResults / requestContext / state");
3990
+ if ("users" in s && opts.customerReachable) push("approver-binding-customer-reachable", `${path25}.users`, "a customer-reachable workflow may not bind its approver list");
3991
+ }
3992
+ }
3993
+ }, "checkSpec");
3994
+ if (node.approver !== void 0) checkSpec(node.approver, `${opts.path}.approver`);
3995
+ if (node.fourEyes !== void 0) {
3996
+ const r = FourEyesSchema.safeParse(node.fourEyes);
3997
+ if (!r.success) push("approver-invalid", `${opts.path}.fourEyes`, "fourEyes needs { edit, approve } approver specs");
3998
+ else {
3999
+ checkSpec(r.data.edit, `${opts.path}.fourEyes.edit`);
4000
+ checkSpec(r.data.approve, `${opts.path}.fourEyes.approve`);
4001
+ }
4002
+ if (!approvalEditable(node)) push("four-eyes-requires-editable", `${opts.path}.fourEyes`, "fourEyes requires editable:true");
4003
+ if (node.approver !== void 0) push("four-eyes-overrides-approver", `${opts.path}.approver`, "fourEyes replaces approver", "warning");
4004
+ if (node.itemsPath) push("four-eyes-items-unsupported", `${opts.path}.fourEyes`, "fourEyes cannot combine with itemsPath");
4005
+ }
4006
+ if (node.excludeInitiator && (node.approver === void 0 || node.approver === "creator") && !node.fourEyes) push("approver-excludes-only-candidate", `${opts.path}.excludeInitiator`, "'creator' with excludeInitiator leaves no approver");
4007
+ if (Array.isArray(node.onTimeout)) {
4008
+ const chain = node.onTimeout;
4009
+ const hops = chain.filter((m) => typeof m === "object" && m !== null && "escalateTo" in m);
4010
+ if (hops.length > ESCALATION_MAX_HOPS) push("escalation-chain-too-long", `${opts.path}.onTimeout`, `at most ${ESCALATION_MAX_HOPS} hops`);
4011
+ const last = chain[chain.length - 1];
4012
+ if (typeof last === "object" && last !== null) push("escalation-chain-not-terminal", `${opts.path}.onTimeout`, "a chain must end in deny | cancel-run | fail");
4013
+ hops.forEach((h, i) => checkSpec(h.escalateTo, `${opts.path}.onTimeout[${i}].escalateTo`));
4014
+ } else if (typeof node.onTimeout === "object" && node.onTimeout !== null) {
4015
+ checkSpec(node.onTimeout.escalateTo, `${opts.path}.onTimeout.escalateTo`);
4016
+ }
4017
+ return issues;
4018
+ }
4019
+ function liftRenderedApprover(row2, rendered) {
4020
+ const text = (rendered ?? "").trim();
4021
+ if (!text) return null;
4022
+ if (row2 === "users") {
4023
+ let members = null;
4024
+ if (text.startsWith("[")) {
4025
+ try {
4026
+ members = JSON.parse(text);
4027
+ } catch {
4028
+ return null;
4029
+ }
4030
+ } else members = text.split(",").map((s) => s.trim());
4031
+ if (!Array.isArray(members) || members.length === 0 || members.length > APPROVER_SPEC_MAX_USERS) return null;
4032
+ if (!members.every((m) => typeof m === "string" && m.length > 0 && m.length <= 128)) return null;
4033
+ return {
4034
+ users: [
4035
+ ...new Set(members)
4036
+ ].sort()
4037
+ };
4038
+ }
4039
+ if (text.length > 128 || text.startsWith("[") || text.startsWith("{")) return null;
4040
+ return row2 === "role" ? {
4041
+ role: text
4042
+ } : {
4043
+ group: text
4044
+ };
4045
+ }
3800
4046
  function workspaceTemplatePath(template22) {
3801
4047
  const key = template22.trim();
3802
4048
  const expr = WORKSPACE_TEMPLATE_EXPR_RE.exec(key);
@@ -3813,6 +4059,9 @@ function retryBackoffs() {
3813
4059
  function sleepUntilUnsupportedMessage(id) {
3814
4060
  return `the engine does not execute \`sleepUntil\` yet (node "${id}") \u2014 replace it with a \`sleep\` node with a \`duration\` in ms, e.g. { type: 'sleep', id: '${id}', duration: ${SLEEP_UNTIL_REPLACEMENT.duration} }`;
3815
4061
  }
4062
+ function armSubrunUnsupportedMessage(id, workflowId) {
4063
+ return `the engine does not execute the implicit \`${workflowId}\` arm subrun (node "${id}") \u2014 a [map, step] container arm is the step itself with the map as its \`input\` since lua-cli 3.32.4; re-run \`lua compile\` with the current CLI (a hand-written artifact: put the map on the arm node's \`input\` and drop the \`workflow\` wrapper)`;
4064
+ }
3816
4065
  function fillPolicy(node, defaultTimeout) {
3817
4066
  if (node.tier === void 0 && node.workspace !== void 0 && node.workspace !== "inherit") node.tier = "job";
3818
4067
  if (node.timeoutSeconds === void 0) node.timeoutSeconds = node.tier === "job" ? WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS : defaultTimeout;
@@ -3838,7 +4087,6 @@ function fillSingle(node) {
3838
4087
  fillPolicy(node, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS);
3839
4088
  return;
3840
4089
  case "workflow":
3841
- if (node.workflowId === WORKFLOW_ARM_SUBRUN_ID && Array.isArray(node.graph) && node.graph[1]) fillSingle(node.graph[1]);
3842
4090
  return;
3843
4091
  }
3844
4092
  }
@@ -3970,8 +4218,14 @@ function nodeStepRefs(entry) {
3970
4218
  case "agent": {
3971
4219
  const a = entry;
3972
4220
  const p = a.promptTemplate;
3973
- return typeof p === "string" ? templateStepRefs(p) : p && "template" in p ? templateStepRefs(p.template) : [];
4221
+ const prompt = typeof p === "string" ? templateStepRefs(p) : p && "template" in p ? templateStepRefs(p.template) : [];
4222
+ return [
4223
+ ...prompt,
4224
+ ...mapConfigStepRefs(a.input)
4225
+ ];
3974
4226
  }
4227
+ case "step":
4228
+ return mapConfigStepRefs(entry.input);
3975
4229
  case "tool":
3976
4230
  return mapConfigStepRefs(entry.input);
3977
4231
  case "workflow":
@@ -4188,6 +4442,21 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
4188
4442
  err("job-tier-provider-unsupported", `model provider '${provider}' is outside LUA_WF_JOB_PROVIDERS [${opts.policy.jobProviders.join(", ")}]`, `${path25}.model`, id);
4189
4443
  }
4190
4444
  }, "checkTier");
4445
+ const checkModel = /* @__PURE__ */ __name3((node, path25) => {
4446
+ if (node.type !== "agent" || typeof node.model !== "string") return;
4447
+ const registry = opts.approvedModels;
4448
+ if (registry === void 0) return;
4449
+ const id = singleId(node);
4450
+ if (registry === "unavailable") {
4451
+ const pin = node.model.trim();
4452
+ if (pin && !normalizeModelId(pin, []).ok) {
4453
+ warn("model-unresolved", `model "${pin}" could not be checked against the approved-model registry (unavailable at push) \u2014 it dispatches only if it resolves there (a provider/model registry code, or a bare id exactly one approved model carries)`, `${path25}.model`, id);
4454
+ }
4455
+ return;
4456
+ }
4457
+ const resolved = normalizeModelId(node.model, registry);
4458
+ if (!resolved.ok) err("model-unresolved", modelUnresolvedMessage(resolved), `${path25}.model`, id);
4459
+ }, "checkModel");
4191
4460
  const checkWorkspace = /* @__PURE__ */ __name3((node, path25) => {
4192
4461
  const id = singleId(node);
4193
4462
  const ws = workspaceOf(node);
@@ -4240,38 +4509,29 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
4240
4509
  }
4241
4510
  }, "checkMapMembers");
4242
4511
  const checkInputShape = /* @__PURE__ */ __name3((node, path25) => {
4243
- if (node.type !== "tool" && node.type !== "workflow") return;
4244
4512
  const input = node.input;
4245
4513
  if (input === void 0) return;
4514
+ const id = singleId(node);
4246
4515
  if (input !== null && typeof input === "object" && !Array.isArray(input)) {
4247
- checkMapMembers(input, `${path25}.input`, node.id);
4516
+ checkMapMembers(input, `${path25}.input`, id);
4248
4517
  return;
4249
4518
  }
4250
- err("invalid-envelope", `\`input\` must be an object map \u2014 each member a binding descriptor ({initData:true, path} | {step, path} | {value} | {template} | {requestContextPath}) or a JSON literal (got ${JSON.stringify(input)})`, `${path25}.input`, node.id);
4519
+ err("invalid-envelope", `\`input\` must be an object map \u2014 each member a binding descriptor ({initData:true, path} | {step, path} | {value} | {template} | {requestContextPath}) or a JSON literal (got ${JSON.stringify(input)})`, `${path25}.input`, id);
4251
4520
  }, "checkInputShape");
4521
+ const checkBodyInput = /* @__PURE__ */ __name3((body, path25, container) => {
4522
+ if (body.type === "workflow" || body.input === void 0) return;
4523
+ err("arm-input-unsupported", container === "foreach" ? `a foreach body receives each item as its input \u2014 drop \`input\` on "${singleId(body)}" and map the items before the foreach instead` : `a loop body receives the previous output as its input \u2014 drop \`input\` on "${singleId(body)}" and put the map before the loop instead`, `${path25}.input`, singleId(body));
4524
+ }, "checkBodyInput");
4252
4525
  const checkSingle = /* @__PURE__ */ __name3((node, path25, depth) => {
4253
4526
  recordOutputSchema(node);
4254
- if (node.type === "workflow" && node.workflowId === WORKFLOW_ARM_SUBRUN_ID) {
4527
+ if (node.type === "workflow" && (typeof node.workflowId !== "string" || node.workflowId.length === 0)) {
4255
4528
  checkId(node.id, path25);
4256
- const body = node.graph;
4257
- if (!Array.isArray(body) || body.length !== 2 || body[0]?.type !== "mapping" || !body[1]) {
4258
- err("container-arm-empty", "an implicit arm subrun needs a [mapping, step] body", `${path25}.graph`, node.id);
4259
- return;
4260
- }
4261
- if (body[1].type === "mapping") {
4262
- err("container-arm-empty", "a bare mapping arm has nothing to run", `${path25}.graph.1`, node.id);
4263
- return;
4264
- }
4265
- const inner = body[1];
4266
- if (isHitlNode(inner)) {
4267
- err("node-type-unsupported-in-container", workflowHitlArmShapeMessage(inner.type, inner.id, "mapped-arm"), `${path25}.graph.1`, inner.id);
4268
- return;
4269
- }
4270
- upstream.add(singleId(body[1]));
4271
- checkArm(body[0], `${path25}.graph.0`, depth, "parallel");
4272
- checkSingle(body[1], `${path25}.graph.1`, depth);
4273
- upstream.add(body[0].id);
4274
- upstream.add(singleId(body[1]));
4529
+ err("invalid-envelope", `\`workflowId\` must be a non-empty string naming the workflow to start (got ${JSON.stringify(node.workflowId)})`, `${path25}.workflowId`, node.id);
4530
+ return;
4531
+ }
4532
+ if (node.type === "workflow" && (node.workflowId.startsWith("$") || Array.isArray(node.graph))) {
4533
+ checkId(node.id, path25);
4534
+ err("node-type-unsupported-by-engine", armSubrunUnsupportedMessage(node.id, node.workflowId), path25, node.id);
4275
4535
  return;
4276
4536
  }
4277
4537
  checkId(singleId(node), path25);
@@ -4279,6 +4539,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
4279
4539
  checkInputShape(node, path25);
4280
4540
  checkTimeout(node, path25);
4281
4541
  checkTier(node, path25);
4542
+ checkModel(node, path25);
4282
4543
  checkRetry(node, path25);
4283
4544
  checkWorkspace(node, path25);
4284
4545
  if (!opts.static) {
@@ -4301,7 +4562,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
4301
4562
  if (node.type === "workflow" && node.kind === "subrun" && depth > caps.maxNestingDepth) {
4302
4563
  err("cap-exceeded", `nesting depth ${depth} exceeds ${caps.maxNestingDepth}`, path25, node.id);
4303
4564
  }
4304
- if (node.type === "workflow" && node.workflowId !== WORKFLOW_ARM_SUBRUN_ID && typeof g.definition?.id === "string" && node.workflowId === g.definition.id) {
4565
+ if (node.type === "workflow" && typeof g.definition?.id === "string" && node.workflowId === g.definition.id) {
4305
4566
  err("subrun-cycle", `"${node.id}" starts "${node.workflowId}", which is this workflow itself`, path25, node.id);
4306
4567
  }
4307
4568
  for (const ref of nodeStepRefs(node)) {
@@ -4322,11 +4583,14 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
4322
4583
  if (a.approver === "creator" && a.excludeInitiator === true) {
4323
4584
  err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path25, a.id);
4324
4585
  }
4325
- if (a.fourEyes !== void 0 && a.editable !== true) {
4586
+ const editable = approvalEditable(a);
4587
+ if (a.fourEyes !== void 0 && !editable) {
4326
4588
  err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path25}.fourEyes`, a.id);
4327
4589
  }
4328
- if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && a.editable !== true) {
4329
- err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true", `${path25}.editablePaths`, a.id);
4590
+ if (a.editable === false && Array.isArray(a.editablePaths) && a.editablePaths.length > 0) {
4591
+ err("editable-path-invalid", "`editablePaths` beside editable:false is contradictory \u2014 drop the paths or set editable:true", `${path25}.editablePaths`, a.id);
4592
+ } else if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && !editable) {
4593
+ err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true (a non-empty editablePaths implies it)", `${path25}.editablePaths`, a.id);
4330
4594
  }
4331
4595
  for (const p of a.editablePaths ?? []) {
4332
4596
  if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path25}.editablePaths`, a.id);
@@ -4507,6 +4771,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
4507
4771
  } else checkHitlArm(f.step, `${path25}.step`, "foreach");
4508
4772
  declared.push(f.step.id);
4509
4773
  } else {
4774
+ checkBodyInput(f.step, `${path25}.step`, "foreach");
4510
4775
  checkSingle(f.step, `${path25}.step`, o.chunk ? 2 : 1);
4511
4776
  declared.push(singleId(f.step));
4512
4777
  }
@@ -4527,6 +4792,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
4527
4792
  checkHitlArm(l.step, `${path25}.step`, "loop");
4528
4793
  declared.push(l.step.id);
4529
4794
  } else {
4795
+ checkBodyInput(l.step, `${path25}.step`, "loop");
4530
4796
  checkSingle(l.step, `${path25}.step`, 1);
4531
4797
  declared.push(singleId(l.step));
4532
4798
  }
@@ -5000,17 +5266,10 @@ function isContinuedFailureValue(v) {
5000
5266
  const err = o.error;
5001
5267
  return o.__lua_workflow === CONTINUED_FAILURE_TAG && o.failed === true && o.text === "" && err !== null && typeof err === "object" && typeof err.code === "string" && typeof err.message === "string";
5002
5268
  }
5003
- function lowerContainerArm(mapping, step22) {
5004
- const stepId = nodeIdOf(step22);
5269
+ function inlineContainerArm(mapping, step22) {
5005
5270
  return {
5006
- type: "workflow",
5007
- id: `${stepId}_arm`,
5008
- workflowId: WORKFLOW_ARM_SUBRUN_ID,
5009
- kind: "subrun",
5010
- graph: [
5011
- mapping,
5012
- step22
5013
- ]
5271
+ ...step22,
5272
+ input: parseMapConfig(mapping.mapConfig, mapping.id)
5014
5273
  };
5015
5274
  }
5016
5275
  function entryIds(entry) {
@@ -5080,6 +5339,27 @@ function resolvePlacements(calls) {
5080
5339
  break;
5081
5340
  }
5082
5341
  });
5342
+ const armMapPlacementIssue = /* @__PURE__ */ __name3((node, ref, i, container) => {
5343
+ if (!ref.armMap || node.type === "mapping" || isHitlNode2(node)) return void 0;
5344
+ const id = nodeIdOf(node);
5345
+ if ((container === "foreach" || container === "loop") && node.type !== "workflow") {
5346
+ return {
5347
+ code: "mapping-placement",
5348
+ message: container === "foreach" ? `foreach body "${id}": a [map, step] body is not supported \u2014 the body receives each item as its input; map the items before the foreach instead (foreach(step, { items: \u2026 }) or a .map() before it)` : `loop body "${id}": a [map, step] body is not supported \u2014 the body receives the previous output as its input; put the .map() before the loop instead`,
5349
+ callIndex: i,
5350
+ stepId: id
5351
+ };
5352
+ }
5353
+ if (node.input !== void 0) {
5354
+ return {
5355
+ code: "mapping-placement",
5356
+ message: `"${id}": the [map, step] arm mapping and the node's own input map would both bind its input \u2014 keep one (drop the arm map, or the \`input\` on the declaration)`,
5357
+ callIndex: i,
5358
+ stepId: id
5359
+ };
5360
+ }
5361
+ return void 0;
5362
+ }, "armMapPlacementIssue");
5083
5363
  const hitlPlacementIssue = /* @__PURE__ */ __name3((node, ref, i, container) => {
5084
5364
  if (!isHitlNode2(node)) return void 0;
5085
5365
  const id = node.id;
@@ -5106,7 +5386,7 @@ function resolvePlacements(calls) {
5106
5386
  if (ref.node.type === "mapping" && !allowMapping) {
5107
5387
  issues.push({
5108
5388
  code: "mapping-placement",
5109
- message: `mapping "${ref.node.id}" cannot be a container arm \u2014 chain it as [map, step]`,
5389
+ message: `mapping "${ref.node.id}" cannot be a container arm \u2014 chain it as [map, step] in a parallel / conditional arm, or place the .map() before the container`,
5110
5390
  callIndex: i,
5111
5391
  stepId: ref.node.id
5112
5392
  });
@@ -5127,7 +5407,7 @@ function resolvePlacements(calls) {
5127
5407
  if (d.node.type === "mapping" && !allowMapping) {
5128
5408
  issues.push({
5129
5409
  code: "mapping-placement",
5130
- message: `map "${ref.ref}" cannot be a parallel/foreach/loop arm \u2014 chain it as [map, step]`,
5410
+ message: `map "${ref.ref}" cannot be a parallel/foreach/loop arm \u2014 chain it as [map, step] in a parallel arm, or place the .map() before the container`,
5131
5411
  callIndex: i,
5132
5412
  stepId: ref.ref
5133
5413
  });
@@ -5138,6 +5418,11 @@ function resolvePlacements(calls) {
5138
5418
  issues.push(hitl);
5139
5419
  return void 0;
5140
5420
  }
5421
+ const mapped = armMapPlacementIssue(d.node, ref, i, container);
5422
+ if (mapped) {
5423
+ issues.push(mapped);
5424
+ return void 0;
5425
+ }
5141
5426
  const prior = placedBy.get(ref.ref);
5142
5427
  if (prior !== void 0 && prior !== i) {
5143
5428
  issues.push({
@@ -5158,6 +5443,10 @@ function resolvePlacements(calls) {
5158
5443
  }
5159
5444
  const hitl = hitlPlacementIssue(ref.node, ref, i, container);
5160
5445
  if (hitl) issues.push(hitl);
5446
+ else {
5447
+ const mapped = armMapPlacementIssue(ref.node, ref, i, container);
5448
+ if (mapped) issues.push(mapped);
5449
+ }
5161
5450
  }, "claim");
5162
5451
  calls.forEach((call, i) => {
5163
5452
  switch (call.kind) {
@@ -5185,7 +5474,7 @@ function resolvePlacements(calls) {
5185
5474
  const lookup = /* @__PURE__ */ __name3((ref) => {
5186
5475
  const n2 = "node" in ref ? ref.node : declared.get(ref.ref)?.node;
5187
5476
  if (!n2 || !ref.armMap || n2.type === "mapping" || isHitlNode2(n2)) return n2;
5188
- return lowerContainerArm(ref.armMap, n2);
5477
+ return inlineContainerArm(ref.armMap, n2);
5189
5478
  }, "lookup");
5190
5479
  calls.forEach((call, i) => {
5191
5480
  switch (call.kind) {
@@ -5570,6 +5859,27 @@ function isTerminalRunStatus(status) {
5570
5859
  function pruneUndefined(o) {
5571
5860
  return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
5572
5861
  }
5862
+ function runOrigin(run) {
5863
+ if (run.goalId) return "goal";
5864
+ if (run.jobId || run.trigger === "schedule") return "schedule";
5865
+ if (run.dynamic === true) return run.tags?.includes(WORKFLOW_INLINE_RUN_TAG) ? "inline" : "compose";
5866
+ return "definition";
5867
+ }
5868
+ function runErrorIssues(issues) {
5869
+ if (!Array.isArray(issues)) return void 0;
5870
+ const out = [];
5871
+ for (const raw of issues.slice(0, RUN_ERROR_ISSUES_MAX)) {
5872
+ if (!raw || typeof raw !== "object") continue;
5873
+ const o = raw;
5874
+ if (typeof o.code !== "string" || !o.code) continue;
5875
+ out.push(pruneUndefined({
5876
+ code: o.code,
5877
+ path: typeof o.path === "string" ? o.path : void 0,
5878
+ message: typeof o.message === "string" ? o.message : void 0
5879
+ }));
5880
+ }
5881
+ return out.length ? out : void 0;
5882
+ }
5573
5883
  function runNextAction(run) {
5574
5884
  if (isTerminalRunStatus(run.status)) return "none";
5575
5885
  if (run.status === "suspended" && run.gate?.kind === "budget") return "raise_budget";
@@ -5739,6 +6049,7 @@ function toWorkflowRunSummary(run) {
5739
6049
  repairOf: run.repairOf,
5740
6050
  repairRunIds: run.repairRunIds,
5741
6051
  trigger: run.trigger ?? "api",
6052
+ origin: runOrigin(run),
5742
6053
  createdBy: {
5743
6054
  subjectType: principal?.subjectType ?? "system",
5744
6055
  subjectId: principal?.subjectId ?? run.userId ?? ""
@@ -5756,11 +6067,13 @@ function toWorkflowRunSummary(run) {
5756
6067
  usage: runUsage(run),
5757
6068
  // LUA-697: a row persisted before the write seams (#2406 / #2465 / the script tier) leaves scrubbed here too —
5758
6069
  // idempotent on a scrubbed message, bounded input; an empty message falls back to the code.
5759
- error: run.error ? {
6070
+ error: run.error ? pruneUndefined({
5760
6071
  code: run.error.code ?? "error",
5761
6072
  message: scrubStepErrorMessage(run.error.message) ?? run.error.code ?? "error",
5762
- stepId: run.error.stepId
5763
- } : void 0,
6073
+ stepId: run.error.stepId,
6074
+ // LUA-784 (item 3): the unattended pre-start failure's refusal rows (`input_schema_invalid` and kin).
6075
+ issues: runErrorIssues(run.error.issues)
6076
+ }) : void 0,
5764
6077
  kind: "run",
5765
6078
  aclHash: run.aclHash,
5766
6079
  migration: run.migration,
@@ -6283,124 +6596,6 @@ function rebaseItemPointer(pointer, itemsPath, index) {
6283
6596
  const base = `/${itemsPath.split(".").map(escapePointer).join("/")}/${index}`;
6284
6597
  return pointer === "/" || pointer === "" ? base : `${base}${pointer}`;
6285
6598
  }
6286
- function describeApproverSpecRefusal(spec) {
6287
- const raw = spec === void 0 ? "undefined" : JSON.stringify(spec) ?? String(spec);
6288
- const written = raw.length > APPROVER_WRITTEN_MAX ? `${raw.slice(0, APPROVER_WRITTEN_MAX - 1)}\u2026` : raw;
6289
- const users = typeof spec === "object" && spec !== null ? spec.users : void 0;
6290
- const approver = typeof users === "string" && USER_ID_SHAPED_RE.test(users) ? {
6291
- users: [
6292
- users
6293
- ]
6294
- } : "creator";
6295
- const message = `approver ${written} is not an approver \u2014 legal: ${APPROVER_SPEC_SHAPES.join(" | ")}. 'creator' is the person who started the run: write approver:'creator' for "ask me" / "I approve"; {users:[\u2026]} takes user ids, never emails, names or {type:'user'}` + (approver === "creator" ? "" : `; here: approver:${JSON.stringify(approver)}`);
6296
- return {
6297
- approver,
6298
- written,
6299
- message
6300
- };
6301
- }
6302
- function bindingRootsOk(template22) {
6303
- const refs = [
6304
- ...template22.matchAll(/\$\{\s*([A-Za-z_$][\w$]*)/g)
6305
- ].map((m) => m[1]);
6306
- return refs.length > 0 && refs.every((r) => BINDING_ROOTS.includes(r));
6307
- }
6308
- function isTemplateBinding(v) {
6309
- return typeof v === "object" && v !== null && typeof v.template === "string";
6310
- }
6311
- function validateApproverBlock(node, opts = {
6312
- path: "approval"
6313
- }) {
6314
- const issues = [];
6315
- const push = /* @__PURE__ */ __name3((code, path25, message, severity = "error") => issues.push({
6316
- code,
6317
- path: path25,
6318
- severity,
6319
- message
6320
- }), "push");
6321
- const checkSpec = /* @__PURE__ */ __name3((spec, path25) => {
6322
- const r = ApproverSpecSchema.safeParse(spec);
6323
- if (!r.success) {
6324
- const users = spec?.users;
6325
- if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path25, `at most ${APPROVER_SPEC_MAX_USERS} users`);
6326
- else {
6327
- const refusal = describeApproverSpecRefusal(spec);
6328
- issues.push({
6329
- code: "approver-invalid",
6330
- path: path25,
6331
- severity: "error",
6332
- message: refusal.message,
6333
- repair: {
6334
- approver: refusal.approver,
6335
- written: refusal.written
6336
- }
6337
- });
6338
- }
6339
- return;
6340
- }
6341
- const s = r.data;
6342
- if (typeof s === "object") {
6343
- if ("governance" in s && !opts.governanceEnabled) push("approver-governance-unavailable", path25, "governance reviewer routing is not enabled for this deployment");
6344
- if ("group" in s && typeof s.group === "string" && !opts.scimEnabled && opts.idpGroups?.includes(s.group)) push("approver-idp-group-unavailable", path25, "IdP-group approvers are not enabled for this deployment");
6345
- const binding = "users" in s ? s.users : "role" in s ? s.role : "group" in s ? s.group : void 0;
6346
- if (isTemplateBinding(binding)) {
6347
- if (!bindingRootsOk(binding.template)) push("approver-binding-invalid", `${path25}.template`, "binding root must be initData / stepResults / requestContext / state");
6348
- if ("users" in s && opts.customerReachable) push("approver-binding-customer-reachable", `${path25}.users`, "a customer-reachable workflow may not bind its approver list");
6349
- }
6350
- }
6351
- }, "checkSpec");
6352
- if (node.approver !== void 0) checkSpec(node.approver, `${opts.path}.approver`);
6353
- if (node.fourEyes !== void 0) {
6354
- const r = FourEyesSchema.safeParse(node.fourEyes);
6355
- if (!r.success) push("approver-invalid", `${opts.path}.fourEyes`, "fourEyes needs { edit, approve } approver specs");
6356
- else {
6357
- checkSpec(r.data.edit, `${opts.path}.fourEyes.edit`);
6358
- checkSpec(r.data.approve, `${opts.path}.fourEyes.approve`);
6359
- }
6360
- if (!node.editable) push("four-eyes-requires-editable", `${opts.path}.fourEyes`, "fourEyes requires editable:true");
6361
- if (node.approver !== void 0) push("four-eyes-overrides-approver", `${opts.path}.approver`, "fourEyes replaces approver", "warning");
6362
- if (node.itemsPath) push("four-eyes-items-unsupported", `${opts.path}.fourEyes`, "fourEyes cannot combine with itemsPath");
6363
- }
6364
- if (node.excludeInitiator && (node.approver === void 0 || node.approver === "creator") && !node.fourEyes) push("approver-excludes-only-candidate", `${opts.path}.excludeInitiator`, "'creator' with excludeInitiator leaves no approver");
6365
- if (Array.isArray(node.onTimeout)) {
6366
- const chain = node.onTimeout;
6367
- const hops = chain.filter((m) => typeof m === "object" && m !== null && "escalateTo" in m);
6368
- if (hops.length > ESCALATION_MAX_HOPS) push("escalation-chain-too-long", `${opts.path}.onTimeout`, `at most ${ESCALATION_MAX_HOPS} hops`);
6369
- const last = chain[chain.length - 1];
6370
- if (typeof last === "object" && last !== null) push("escalation-chain-not-terminal", `${opts.path}.onTimeout`, "a chain must end in deny | cancel-run | fail");
6371
- hops.forEach((h, i) => checkSpec(h.escalateTo, `${opts.path}.onTimeout[${i}].escalateTo`));
6372
- } else if (typeof node.onTimeout === "object" && node.onTimeout !== null) {
6373
- checkSpec(node.onTimeout.escalateTo, `${opts.path}.onTimeout.escalateTo`);
6374
- }
6375
- return issues;
6376
- }
6377
- function liftRenderedApprover(row2, rendered) {
6378
- const text = (rendered ?? "").trim();
6379
- if (!text) return null;
6380
- if (row2 === "users") {
6381
- let members = null;
6382
- if (text.startsWith("[")) {
6383
- try {
6384
- members = JSON.parse(text);
6385
- } catch {
6386
- return null;
6387
- }
6388
- } else members = text.split(",").map((s) => s.trim());
6389
- if (!Array.isArray(members) || members.length === 0 || members.length > APPROVER_SPEC_MAX_USERS) return null;
6390
- if (!members.every((m) => typeof m === "string" && m.length > 0 && m.length <= 128)) return null;
6391
- return {
6392
- users: [
6393
- ...new Set(members)
6394
- ].sort()
6395
- };
6396
- }
6397
- if (text.length > 128 || text.startsWith("[") || text.startsWith("{")) return null;
6398
- return row2 === "role" ? {
6399
- role: text
6400
- } : {
6401
- group: text
6402
- };
6403
- }
6404
6599
  function collectEnvTemplateKeys(value22) {
6405
6600
  const keys = /* @__PURE__ */ new Set();
6406
6601
  const walk22 = /* @__PURE__ */ __name3((v) => {
@@ -6649,7 +6844,6 @@ function* singleStepsOf(entry) {
6649
6844
  return;
6650
6845
  case "workflow":
6651
6846
  yield entry;
6652
- if (Array.isArray(entry.graph)) yield* singleStepsOf(entry.graph[1]);
6653
6847
  return;
6654
6848
  case "parallel":
6655
6849
  case "conditional":
@@ -6691,7 +6885,7 @@ function needsInheritedWorkspace(graph) {
6691
6885
  }
6692
6886
  return false;
6693
6887
  }
6694
- var __defProp3, __name3, WorkflowTemplateError, TEMPLATE_PLACEHOLDER, TEMPLATE_NAMESPACES, MAP_DESCRIPTOR_KEYS, MAP_MEMBER_MALFORMED_CODE, fromInit, fromStep, value, template, fromRequest, rows, fromKnowledge, SideEffectsSchema, JobResourcesSchema, WORKFLOW_ARM_SUBRUN_ID, WORKSPACE_TEMPLATE_EXPR_RE, SLEEP_UNTIL_REPLACEMENT, WORKFLOW_CAPS_DEFAULT, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_FOREACH_DEFAULT_CONCURRENCY, WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS, WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS, WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS, WORKFLOW_SIGNAL_DEFAULT_SOURCES, clone, CONNECTION_ID_HEX_RE, WORKFLOW_JOB_TOOLS, WORKFLOW_JOB_MAX_WORKTREE_ARMS, workspaceOf, mountsWorkspace, isJobTier, jobToolsOf, schemaIsArray, isHitlNode, isSingleStep, singleId, armId, TEMPLATE_STEP_REF, EDITABLE_PATH_RE, PREDICATE_OPS, isPredicateScalar, GRAPH_HASH_PREFIX, WorkflowPlanError, isArmStep, armStepId, armStepKind, joinIdOf, containerIdOf, PATH_PLACEHOLDER, MISSING, stepIdOf, cmp, eq, ne, gt, gte, lt, lte, inSet, notIn, exists, notExists, truthy, falsy, and, or, not, CONTINUED_FAILURE_TAG, CONTINUED_FAILURE_DEFAULT_CODE, CONTINUED_FAILURE_OUTPUT_SCHEMA, CONTINUED_FAILURE_LEAF_PATHS, isHitlNode2, nodeIdOf, GOAL_JUDGE_STEP_ID, NON_LEAF_KINDS, CONDITIONAL_JOIN_ID, branchArmId, canonical, sortKeys, JOIN, entryOfJoin, FORCE_CANCEL_STALE_MS, TERMINAL, IN_FLIGHT, n, STEP_ERROR_DETAIL_KEYS, STEP_ERROR_DETAIL_MAX_BYTES, DETAIL_MAX_DEPTH, DETAIL_MAX_ITEMS, MAX_HOLIDAYS, MAX_WALK_DAYS, HHMM, YMD, MS_PER_MIN, MS_PER_DAY, MON_FRI, supportedTz, fmtCache, WEEKDAYS, JSON_PATCH_OPS, JSON_PATCH_MAX_OPS, JSON_PATCH_MAX_VALUE_BYTES, JSON_PATCH_MAX_TOTAL_BYTES, SEGMENT_RE, APPROVER_SPEC_MAX_USERS, ESCALATION_MAX_HOPS, TemplateBindingSchema, ApproverSpecSchema, FourEyesSchema, EscalationHopSchema, TerminalOutcomeSchema, ApprovalOnTimeoutSchema, APPROVER_SPEC_SHAPES, APPROVER_WRITTEN_MAX, USER_ID_SHAPED_RE, BINDING_ROOTS, WORKFLOW_ENV_OVERLAY_MAX_KEYS, WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES, WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE, isEnvRef, looksLikeEmbeddedJson, ZERO, isRecord2;
6888
+ var __defProp3, __name3, WorkflowTemplateError, TEMPLATE_PLACEHOLDER, TEMPLATE_NAMESPACES, MAP_DESCRIPTOR_KEYS, MAP_MEMBER_MALFORMED_CODE, fromInit, fromStep, value, template, fromRequest, rows, fromKnowledge, SideEffectsSchema, JobResourcesSchema, APPROVER_SPEC_MAX_USERS, ESCALATION_MAX_HOPS, TemplateBindingSchema, ApproverSpecSchema, FourEyesSchema, EscalationHopSchema, TerminalOutcomeSchema, ApprovalOnTimeoutSchema, APPROVER_SPEC_SHAPES, APPROVER_WRITTEN_MAX, USER_ID_SHAPED_RE, BINDING_ROOTS, WORKSPACE_TEMPLATE_EXPR_RE, SLEEP_UNTIL_REPLACEMENT, WORKFLOW_CAPS_DEFAULT, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_FOREACH_DEFAULT_CONCURRENCY, WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS, WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS, WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS, WORKFLOW_SIGNAL_DEFAULT_SOURCES, clone, CONNECTION_ID_HEX_RE, WORKFLOW_JOB_TOOLS, WORKFLOW_JOB_MAX_WORKTREE_ARMS, workspaceOf, mountsWorkspace, isJobTier, jobToolsOf, schemaIsArray, isHitlNode, isSingleStep, singleId, armId, TEMPLATE_STEP_REF, EDITABLE_PATH_RE, PREDICATE_OPS, isPredicateScalar, GRAPH_HASH_PREFIX, WorkflowPlanError, isArmStep, armStepId, armStepKind, joinIdOf, containerIdOf, PATH_PLACEHOLDER, MISSING, stepIdOf, cmp, eq, ne, gt, gte, lt, lte, inSet, notIn, exists, notExists, truthy, falsy, and, or, not, CONTINUED_FAILURE_TAG, CONTINUED_FAILURE_DEFAULT_CODE, CONTINUED_FAILURE_OUTPUT_SCHEMA, CONTINUED_FAILURE_LEAF_PATHS, isHitlNode2, nodeIdOf, GOAL_JUDGE_STEP_ID, NON_LEAF_KINDS, CONDITIONAL_JOIN_ID, branchArmId, canonical, sortKeys, JOIN, entryOfJoin, FORCE_CANCEL_STALE_MS, TERMINAL, WORKFLOW_INLINE_RUN_TAG, RUN_ERROR_ISSUES_MAX, IN_FLIGHT, n, STEP_ERROR_DETAIL_KEYS, STEP_ERROR_DETAIL_MAX_BYTES, DETAIL_MAX_DEPTH, DETAIL_MAX_ITEMS, MAX_HOLIDAYS, MAX_WALK_DAYS, HHMM, YMD, MS_PER_MIN, MS_PER_DAY, MON_FRI, supportedTz, fmtCache, WEEKDAYS, JSON_PATCH_OPS, JSON_PATCH_MAX_OPS, JSON_PATCH_MAX_VALUE_BYTES, JSON_PATCH_MAX_TOTAL_BYTES, SEGMENT_RE, WORKFLOW_ENV_OVERLAY_MAX_KEYS, WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES, WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE, isEnvRef, looksLikeEmbeddedJson, ZERO, isRecord2;
6695
6889
  var init_dist2 = __esm({
6696
6890
  "../workflow-graph/dist/index.mjs"() {
6697
6891
  "use strict";
@@ -6794,7 +6988,88 @@ var init_dist2 = __esm({
6794
6988
  }), "fromKnowledge");
6795
6989
  SideEffectsSchema = z6.enum(WORKFLOW_SIDE_EFFECTS);
6796
6990
  JobResourcesSchema = z6.enum(WORKFLOW_JOB_RESOURCES);
6797
- WORKFLOW_ARM_SUBRUN_ID = "$arm";
6991
+ APPROVER_SPEC_MAX_USERS = 20;
6992
+ ESCALATION_MAX_HOPS = 3;
6993
+ TemplateBindingSchema = z22.object({
6994
+ template: z22.string().min(1).max(2048)
6995
+ }).strict();
6996
+ ApproverSpecSchema = z22.union([
6997
+ z22.literal("creator"),
6998
+ z22.literal("org-admins"),
6999
+ z22.object({
7000
+ users: z22.union([
7001
+ z22.array(z22.string().min(1).max(128)).min(1).max(APPROVER_SPEC_MAX_USERS),
7002
+ TemplateBindingSchema
7003
+ ])
7004
+ }).strict(),
7005
+ z22.object({
7006
+ role: z22.union([
7007
+ z22.string().min(1).max(128),
7008
+ TemplateBindingSchema
7009
+ ])
7010
+ }).strict(),
7011
+ z22.object({
7012
+ group: z22.union([
7013
+ z22.string().min(1).max(128),
7014
+ TemplateBindingSchema
7015
+ ])
7016
+ }).strict(),
7017
+ z22.object({
7018
+ governance: z22.object({
7019
+ policyId: z22.string().min(1).max(128)
7020
+ }).strict()
7021
+ }).strict()
7022
+ ]);
7023
+ FourEyesSchema = z22.object({
7024
+ edit: ApproverSpecSchema,
7025
+ approve: ApproverSpecSchema
7026
+ }).strict();
7027
+ EscalationHopSchema = z22.object({
7028
+ escalateTo: ApproverSpecSchema,
7029
+ timeoutHours: z22.number().finite().min(1).max(720)
7030
+ }).strict();
7031
+ TerminalOutcomeSchema = z22.enum([
7032
+ "deny",
7033
+ "cancel-run",
7034
+ "fail",
7035
+ "continue"
7036
+ ]);
7037
+ ApprovalOnTimeoutSchema = z22.union([
7038
+ TerminalOutcomeSchema,
7039
+ EscalationHopSchema,
7040
+ z22.array(z22.union([
7041
+ TerminalOutcomeSchema,
7042
+ EscalationHopSchema
7043
+ ])).min(1).max(ESCALATION_MAX_HOPS + 1)
7044
+ ]);
7045
+ APPROVER_SPEC_SHAPES = [
7046
+ "'creator'",
7047
+ "'org-admins'",
7048
+ "{users:[userId, \u2026]}",
7049
+ "{role:roleName}",
7050
+ "{group:groupName}",
7051
+ "{governance:{policyId}}"
7052
+ ];
7053
+ APPROVER_WRITTEN_MAX = 120;
7054
+ USER_ID_SHAPED_RE = /^[^\s@]{1,128}$/;
7055
+ __name(describeApproverSpecRefusal, "describeApproverSpecRefusal");
7056
+ __name3(describeApproverSpecRefusal, "describeApproverSpecRefusal");
7057
+ BINDING_ROOTS = [
7058
+ "initData",
7059
+ "stepResults",
7060
+ "requestContext",
7061
+ "state"
7062
+ ];
7063
+ __name(bindingRootsOk, "bindingRootsOk");
7064
+ __name3(bindingRootsOk, "bindingRootsOk");
7065
+ __name(isTemplateBinding, "isTemplateBinding");
7066
+ __name3(isTemplateBinding, "isTemplateBinding");
7067
+ __name(approvalEditable, "approvalEditable");
7068
+ __name3(approvalEditable, "approvalEditable");
7069
+ __name(validateApproverBlock, "validateApproverBlock");
7070
+ __name3(validateApproverBlock, "validateApproverBlock");
7071
+ __name(liftRenderedApprover, "liftRenderedApprover");
7072
+ __name3(liftRenderedApprover, "liftRenderedApprover");
6798
7073
  WORKSPACE_TEMPLATE_EXPR_RE = /^\$\{\s*(?:initData|input)\.([^}]+?)\s*\}$/;
6799
7074
  __name(workspaceTemplatePath, "workspaceTemplatePath");
6800
7075
  __name3(workspaceTemplatePath, "workspaceTemplatePath");
@@ -6806,6 +7081,8 @@ var init_dist2 = __esm({
6806
7081
  });
6807
7082
  __name(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
6808
7083
  __name3(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
7084
+ __name(armSubrunUnsupportedMessage, "armSubrunUnsupportedMessage");
7085
+ __name3(armSubrunUnsupportedMessage, "armSubrunUnsupportedMessage");
6809
7086
  WORKFLOW_CAPS_DEFAULT = Object.freeze({
6810
7087
  maxParallelArms: 16,
6811
7088
  maxForeachConcurrency: 16,
@@ -7087,8 +7364,8 @@ var init_dist2 = __esm({
7087
7364
  __name(isContinuedFailureValue, "isContinuedFailureValue");
7088
7365
  __name3(isContinuedFailureValue, "isContinuedFailureValue");
7089
7366
  isHitlNode2 = /* @__PURE__ */ __name3((n2) => isWorkflowHitlEntryType(n2.type), "isHitlNode");
7090
- __name(lowerContainerArm, "lowerContainerArm");
7091
- __name3(lowerContainerArm, "lowerContainerArm");
7367
+ __name(inlineContainerArm, "inlineContainerArm");
7368
+ __name3(inlineContainerArm, "inlineContainerArm");
7092
7369
  nodeIdOf = /* @__PURE__ */ __name3((n2) => n2.type === "step" ? n2.step.id : n2.id, "nodeIdOf");
7093
7370
  __name(entryIds, "entryIds");
7094
7371
  __name3(entryIds, "entryIds");
@@ -7148,6 +7425,12 @@ var init_dist2 = __esm({
7148
7425
  __name3(isTerminalRunStatus, "isTerminalRunStatus");
7149
7426
  __name(pruneUndefined, "pruneUndefined");
7150
7427
  __name3(pruneUndefined, "pruneUndefined");
7428
+ WORKFLOW_INLINE_RUN_TAG = "inline";
7429
+ __name(runOrigin, "runOrigin");
7430
+ __name3(runOrigin, "runOrigin");
7431
+ RUN_ERROR_ISSUES_MAX = 20;
7432
+ __name(runErrorIssues, "runErrorIssues");
7433
+ __name3(runErrorIssues, "runErrorIssues");
7151
7434
  __name(runNextAction, "runNextAction");
7152
7435
  __name3(runNextAction, "runNextAction");
7153
7436
  IN_FLIGHT = new Set(WORKFLOW_STEP_IN_FLIGHT);
@@ -7308,86 +7591,6 @@ var init_dist2 = __esm({
7308
7591
  __name3(applyJsonPatch, "applyJsonPatch");
7309
7592
  __name(rebaseItemPointer, "rebaseItemPointer");
7310
7593
  __name3(rebaseItemPointer, "rebaseItemPointer");
7311
- APPROVER_SPEC_MAX_USERS = 20;
7312
- ESCALATION_MAX_HOPS = 3;
7313
- TemplateBindingSchema = z22.object({
7314
- template: z22.string().min(1).max(2048)
7315
- }).strict();
7316
- ApproverSpecSchema = z22.union([
7317
- z22.literal("creator"),
7318
- z22.literal("org-admins"),
7319
- z22.object({
7320
- users: z22.union([
7321
- z22.array(z22.string().min(1).max(128)).min(1).max(APPROVER_SPEC_MAX_USERS),
7322
- TemplateBindingSchema
7323
- ])
7324
- }).strict(),
7325
- z22.object({
7326
- role: z22.union([
7327
- z22.string().min(1).max(128),
7328
- TemplateBindingSchema
7329
- ])
7330
- }).strict(),
7331
- z22.object({
7332
- group: z22.union([
7333
- z22.string().min(1).max(128),
7334
- TemplateBindingSchema
7335
- ])
7336
- }).strict(),
7337
- z22.object({
7338
- governance: z22.object({
7339
- policyId: z22.string().min(1).max(128)
7340
- }).strict()
7341
- }).strict()
7342
- ]);
7343
- FourEyesSchema = z22.object({
7344
- edit: ApproverSpecSchema,
7345
- approve: ApproverSpecSchema
7346
- }).strict();
7347
- EscalationHopSchema = z22.object({
7348
- escalateTo: ApproverSpecSchema,
7349
- timeoutHours: z22.number().finite().min(1).max(720)
7350
- }).strict();
7351
- TerminalOutcomeSchema = z22.enum([
7352
- "deny",
7353
- "cancel-run",
7354
- "fail",
7355
- "continue"
7356
- ]);
7357
- ApprovalOnTimeoutSchema = z22.union([
7358
- TerminalOutcomeSchema,
7359
- EscalationHopSchema,
7360
- z22.array(z22.union([
7361
- TerminalOutcomeSchema,
7362
- EscalationHopSchema
7363
- ])).min(1).max(ESCALATION_MAX_HOPS + 1)
7364
- ]);
7365
- APPROVER_SPEC_SHAPES = [
7366
- "'creator'",
7367
- "'org-admins'",
7368
- "{users:[userId, \u2026]}",
7369
- "{role:roleName}",
7370
- "{group:groupName}",
7371
- "{governance:{policyId}}"
7372
- ];
7373
- APPROVER_WRITTEN_MAX = 120;
7374
- USER_ID_SHAPED_RE = /^[^\s@]{1,128}$/;
7375
- __name(describeApproverSpecRefusal, "describeApproverSpecRefusal");
7376
- __name3(describeApproverSpecRefusal, "describeApproverSpecRefusal");
7377
- BINDING_ROOTS = [
7378
- "initData",
7379
- "stepResults",
7380
- "requestContext",
7381
- "state"
7382
- ];
7383
- __name(bindingRootsOk, "bindingRootsOk");
7384
- __name3(bindingRootsOk, "bindingRootsOk");
7385
- __name(isTemplateBinding, "isTemplateBinding");
7386
- __name3(isTemplateBinding, "isTemplateBinding");
7387
- __name(validateApproverBlock, "validateApproverBlock");
7388
- __name3(validateApproverBlock, "validateApproverBlock");
7389
- __name(liftRenderedApprover, "liftRenderedApprover");
7390
- __name3(liftRenderedApprover, "liftRenderedApprover");
7391
7594
  WORKFLOW_ENV_OVERLAY_MAX_KEYS = 64;
7392
7595
  WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES = 4096;
7393
7596
  WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE = /(SECRET|TOKEN|KEY|PASSWORD)$/;
@@ -7490,14 +7693,13 @@ function stepNodeOf(s) {
7490
7693
  }
7491
7694
  function materializeEntry(entry, steps) {
7492
7695
  const single = /* @__PURE__ */ __name((n2) => {
7493
- if (n2.type === "step" && steps[n2.step.id]) return stepNodeOf(steps[n2.step.id]);
7494
- if (n2.type === "workflow" && n2.workflowId === WORKFLOW_ARM_SUBRUN_ID && n2.graph) return {
7495
- ...n2,
7496
- graph: [
7497
- n2.graph[0],
7498
- single(n2.graph[1])
7499
- ]
7500
- };
7696
+ if (n2.type === "step" && steps[n2.step.id]) {
7697
+ const node = stepNodeOf(steps[n2.step.id]);
7698
+ return n2.input !== void 0 ? {
7699
+ ...node,
7700
+ input: n2.input
7701
+ } : node;
7702
+ }
7501
7703
  return n2;
7502
7704
  }, "single");
7503
7705
  switch (entry.type) {
@@ -7658,7 +7860,10 @@ var init_workflow = __esm({
7658
7860
  }, "assertPredicate");
7659
7861
  assertRetry = /* @__PURE__ */ __name((r, id) => {
7660
7862
  if (!r) return;
7661
- if (typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS) throw new LuaWorkflowBuildError("cap-exceeded", `"${id}": ${workflowRetryMaxAttemptsMessage(r.maxAttempts)}`);
7863
+ if (r.maxAttempts !== void 0 && !isWithinWorkflowRetryAttempts(r.maxAttempts)) {
7864
+ const over = typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS;
7865
+ throw new LuaWorkflowBuildError(over ? "cap-exceeded" : "invalid-envelope", `"${id}": ${workflowRetryMaxAttemptsMessage(r.maxAttempts)}`);
7866
+ }
7662
7867
  if (r.backoff !== void 0 && !WORKFLOW_RETRY_BACKOFFS.includes(r.backoff)) throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.backoff must be ${WORKFLOW_RETRY_BACKOFFS.map((b) => `'${b}'`).join(" | ")}`);
7663
7868
  if (r.maxBackoffSeconds !== void 0) {
7664
7869
  if (r.backoff !== "exponential") throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.maxBackoffSeconds is only meaningful with backoff:'exponential'`);
@@ -11790,6 +11995,17 @@ function resolveIdentifier(node, maxDepth = 10) {
11790
11995
  }
11791
11996
  return identNode;
11792
11997
  }
11998
+ function unwrapExpression(node) {
11999
+ let cur = node;
12000
+ while (cur) {
12001
+ if (Node.isAsExpression(cur) || Node.isSatisfiesExpression(cur) || Node.isParenthesizedExpression(cur) || Node.isNonNullExpression(cur) || Node.isTypeAssertion(cur)) {
12002
+ cur = cur.getExpression();
12003
+ continue;
12004
+ }
12005
+ return cur;
12006
+ }
12007
+ return void 0;
12008
+ }
11793
12009
  function findImportSource(identifier, sourceFile, resolveImportPath) {
11794
12010
  for (const importDecl of sourceFile.getImportDeclarations()) {
11795
12011
  const defaultImport = importDecl.getDefaultImport();
@@ -11824,6 +12040,7 @@ var init_ast_helpers = __esm({
11824
12040
  __name(isFunction, "isFunction");
11825
12041
  __name(getFunctionBody, "getFunctionBody");
11826
12042
  __name(resolveIdentifier, "resolveIdentifier");
12043
+ __name(unwrapExpression, "unwrapExpression");
11827
12044
  __name(findImportSource, "findImportSource");
11828
12045
  }
11829
12046
  });
@@ -12519,6 +12736,19 @@ var init_schema_converter = __esm({
12519
12736
 
12520
12737
  // src/compiler/plugins/tool.plugin.ts
12521
12738
  import { Node as Node4 } from "ts-morph";
12739
+ function isToolShapedLiteral(literal, typeAnnotation, initializerText) {
12740
+ if (!extractStringProperty(literal, "name")) return false;
12741
+ const execute = literal.getProperty("execute");
12742
+ if (!execute) return false;
12743
+ const executeIsCallable = Node4.isMethodDeclaration(execute) || Node4.isShorthandPropertyAssignment(execute) || Node4.isPropertyAssignment(execute) && (() => {
12744
+ const init3 = unwrapExpression(execute.getInitializer());
12745
+ return !!init3 && (Node4.isArrowFunction(init3) || Node4.isFunctionExpression(init3) || Node4.isIdentifier(init3) || Node4.isPropertyAccessExpression(init3) || Node4.isCallExpression(init3));
12746
+ })();
12747
+ if (!executeIsCallable) return false;
12748
+ if (literal.getProperty("description") || literal.getProperty("inputSchema")) return true;
12749
+ const annotated = /* @__PURE__ */ __name((text) => !!text && /\bLua(Voice)?Tool\b/.test(text), "annotated");
12750
+ return annotated(typeAnnotation) || annotated(initializerText);
12751
+ }
12522
12752
  var ToolPlugin;
12523
12753
  var init_tool_plugin = __esm({
12524
12754
  "src/compiler/plugins/tool.plugin.ts"() {
@@ -12542,6 +12772,34 @@ var init_tool_plugin = __esm({
12542
12772
  copyAllFields: true
12543
12773
  };
12544
12774
  supportsClassDefinition = true;
12775
+ /** The three call / class shapes, then (LUA-781) the object-literal shape for the identifiers they did not claim. */
12776
+ detect(sourceFile) {
12777
+ const primitives = super.detect(sourceFile);
12778
+ this.detectObjectLiterals(sourceFile, primitives);
12779
+ return primitives;
12780
+ }
12781
+ /**
12782
+ * LUA-781: `const t = { name: 'x', description, inputSchema, execute }` (any export form; the initializer may sit
12783
+ * under `as` / `satisfies` / parentheses). The export IS the tool object, exactly like a `defineTool({...})`
12784
+ * result, so it compiles as the `function` pattern: the entry point imports it and the runtime validation asserts
12785
+ * `execute`. A literal counts when it carries a string `name`, an `execute` member and — so a random
12786
+ * `{ name, execute }` registry entry is not mistaken for one — a `description`, an `inputSchema` or a `LuaTool`
12787
+ * type annotation (`const t: LuaTool = …`, `… as LuaTool`, `… satisfies LuaTool`).
12788
+ */
12789
+ detectObjectLiterals(sourceFile, primitives) {
12790
+ const claimed = new Set(primitives.map((p) => p.exportName));
12791
+ for (const varDecl of sourceFile.getVariableDeclarations()) {
12792
+ const exportName = varDecl.getName();
12793
+ if (claimed.has(exportName)) continue;
12794
+ const initializer = varDecl.getInitializer();
12795
+ const literal = unwrapExpression(initializer);
12796
+ if (!literal || !Node4.isObjectLiteralExpression(literal)) continue;
12797
+ if (!isToolShapedLiteral(literal, varDecl.getTypeNode()?.getText(), initializer?.getText())) continue;
12798
+ const pos = sourceFile.getLineAndColumnAtPos(varDecl.getStart());
12799
+ const metadata = this.extractFromConfig(literal, exportName, sourceFile.getFilePath(), pos, "function");
12800
+ if (metadata) primitives.push(metadata);
12801
+ }
12802
+ }
12545
12803
  /**
12546
12804
  * Extract metadata from a `defineTool({...})` / `new LuaTool({...})`
12547
12805
  * config object literal. (Class-definition shape is handled by the
@@ -12663,12 +12921,12 @@ var init_tool_plugin = __esm({
12663
12921
  } else {
12664
12922
  const varDecl = sourceFile.getVariableDeclaration(metadata.exportName);
12665
12923
  if (!varDecl) return void 0;
12666
- const initializer = varDecl.getInitializer();
12667
- if (!initializer || !Node4.isCallExpression(initializer)) return void 0;
12668
- const args2 = initializer.getArguments();
12669
- if (args2.length === 0) return void 0;
12670
- const config = args2[0];
12671
- if (!Node4.isObjectLiteralExpression(config)) return void 0;
12924
+ const initializer = unwrapExpression(varDecl.getInitializer());
12925
+ if (!initializer) return void 0;
12926
+ let config;
12927
+ if (Node4.isObjectLiteralExpression(initializer)) config = initializer;
12928
+ else if (Node4.isCallExpression(initializer)) config = initializer.getArguments()[0];
12929
+ if (!config || !Node4.isObjectLiteralExpression(config)) return void 0;
12672
12930
  schemaNode = extractSchemaProperty(config, "inputSchema");
12673
12931
  }
12674
12932
  if (!schemaNode) return void 0;
@@ -12693,6 +12951,7 @@ var init_tool_plugin = __esm({
12693
12951
  };
12694
12952
  }
12695
12953
  };
12954
+ __name(isToolShapedLiteral, "isToolShapedLiteral");
12696
12955
  }
12697
12956
  });
12698
12957
 
@@ -19025,6 +19284,7 @@ function computeDeferred(graph, opts = {}) {
19025
19284
  deferred.add("job-tier-disabled");
19026
19285
  deferred.add("job-tier-provider-unsupported");
19027
19286
  }
19287
+ if (n2.type === "agent" && typeof n2.model === "string" && !isModelIdSentinel(n2.model)) deferred.add("model-unresolved");
19028
19288
  if (Array.isArray(n2.requiredConnections) && n2.requiredConnections.some(serverDecides)) deferred.add("required-connection-unknown");
19029
19289
  if (n2.type === "approval") {
19030
19290
  const a = n2.approver;
@@ -19287,22 +19547,24 @@ function collectSingleSteps(graph) {
19287
19547
  }
19288
19548
  function collectGraphIds(graph) {
19289
19549
  const ids = [];
19290
- const idOf = /* @__PURE__ */ __name((n2) => n2.type === "step" ? n2.step.id : n2.id, "idOf");
19291
- for (const entry of graph.definition.graph) {
19292
- const e = entry;
19293
- const own = idOf(e);
19294
- if (own) ids.push(own);
19295
- for (const child of [
19296
- ...e.steps ?? [],
19297
- e.step,
19298
- e.otherwise
19550
+ const visit = /* @__PURE__ */ __name((node) => {
19551
+ if (!node || typeof node !== "object" || Array.isArray(node)) return;
19552
+ const n2 = node;
19553
+ if (typeof n2.type !== "string") return;
19554
+ const own = n2.type === "step" ? n2.step?.id : n2.id;
19555
+ if (typeof own === "string") ids.push(own);
19556
+ for (const key of [
19557
+ "steps",
19558
+ "step",
19559
+ "otherwise",
19560
+ "graph"
19299
19561
  ]) {
19300
- if (child) {
19301
- const id = idOf(child);
19302
- if (id) ids.push(id);
19303
- }
19562
+ const child = n2[key];
19563
+ if (Array.isArray(child)) child.forEach(visit);
19564
+ else visit(child);
19304
19565
  }
19305
- }
19566
+ }, "visit");
19567
+ for (const entry of graph.definition.graph) visit(entry);
19306
19568
  return ids;
19307
19569
  }
19308
19570
  function stripFunctions(v) {
@@ -19347,6 +19609,8 @@ var init_workflow_plugin = __esm({
19347
19609
  STEP_EXECUTE_MISSING: "WORKFLOW_STEP_EXECUTE_MISSING",
19348
19610
  UNPLACED_STEP: "WORKFLOW_UNPLACED_STEP",
19349
19611
  TOOL_REF_UNRESOLVED: "WORKFLOW_TOOL_REF_UNRESOLVED",
19612
+ /** LUA-781: a tool node whose `toolId` names no compiled tool of the project — an ERROR; the push refuses it too. */
19613
+ TOOL_UNBUNDLED: "WORKFLOW_TOOL_UNBUNDLED",
19350
19614
  NESTED_NOT_REGISTERED: "WORKFLOW_NESTED_NOT_REGISTERED",
19351
19615
  SCHEMA_UNSUPPORTED: "WORKFLOW_SCHEMA_UNSUPPORTED",
19352
19616
  SKETCH_TOPOLOGY: "WORKFLOW_SKETCH_TOPOLOGY",
@@ -19425,7 +19689,7 @@ var init_workflow_plugin = __esm({
19425
19689
  for (const call of chain) {
19426
19690
  const callee = call.getExpression();
19427
19691
  if (Node18.isPropertyAccessExpression(callee) && callee.getName() === "toolStep") {
19428
- const ref = call.getArguments()[1];
19692
+ const ref = unwrapExpression(call.getArguments()[1]);
19429
19693
  if (ref && Node18.isIdentifier(ref)) toolRefIdentifiers.push(ref.getText());
19430
19694
  }
19431
19695
  }
@@ -19678,7 +19942,7 @@ ${firstBundleFrame(vmResult.error.stack)}` : ""}`,
19678
19942
  if (!graphIds.has(id) && tier.topology !== "sketch") {
19679
19943
  issues.push({
19680
19944
  code: WORKFLOW_COMPILE_CODES.UNPLACED_STEP,
19681
- message: `createStep "${id}" is declared in ${relative(rootDir, metadata.sourcePath)} but never placed in workflow "${metadata.name}" (error at push)`,
19945
+ message: `createStep "${id}" is declared in ${relative(rootDir, metadata.sourcePath)} but placed in no workflow of the project (compiled with "${metadata.name}"; error at push \u2014 lua push workflow refuses the version: place it in a chain or delete the declaration)`,
19682
19946
  severity: "warning",
19683
19947
  stepId: id,
19684
19948
  source: sources.get(id)
@@ -19739,6 +20003,26 @@ ${firstBundleFrame(vmResult.error.stack)}` : ""}`,
19739
20003
  const workflows = compiled.filter((p) => p.kind === PrimitiveKind.WORKFLOW && p.graph);
19740
20004
  const inheritTargets2 = new Set(workflows.flatMap((p) => p.graph?.inheritRefs ?? []));
19741
20005
  for (const p of workflows) if (p.graph && inheritTargets2.has(p.name)) p.graph = applyInheritCandidate(p.graph);
20006
+ const placed = /* @__PURE__ */ new Set();
20007
+ for (const p of workflows) for (const id of collectGraphIds(p.graph.graph)) placed.add(id);
20008
+ for (const p of workflows) {
20009
+ if (!p.graph) continue;
20010
+ p.graph.issues = p.graph.issues.filter((i) => i.code !== WORKFLOW_COMPILE_CODES.UNPLACED_STEP || !i.stepId || !placed.has(i.stepId));
20011
+ }
20012
+ const bundledTools = new Set(compiled.filter((p) => p.kind === "tool").map((p) => p.name));
20013
+ for (const p of workflows) {
20014
+ if (!p.graph || p.graph.topology === "sketch") continue;
20015
+ for (const node of collectSingleSteps(p.graph.graph)) {
20016
+ if (node.type !== "tool" || bundledTools.has(node.toolId)) continue;
20017
+ p.graph.issues.push({
20018
+ code: WORKFLOW_COMPILE_CODES.TOOL_UNBUNDLED,
20019
+ message: `toolStep "${node.id}" references tool "${node.toolId}" but no compiled tool artifact of that name exists in this project \u2014 the version would push without it and every run would fail the step TOOL_REF_UNRESOLVED (not_in_version_bundle). Declare the tool where the compiler sees it (defineTool({ name: '${node.toolId}', \u2026 }), a class implementing LuaTool, or an exported { name, description, inputSchema, execute } object) and pass that export to toolStep()`,
20020
+ severity: "error",
20021
+ stepId: node.id,
20022
+ source: p.graph.stepMeta.find((m) => m.id === node.id)?.source
20023
+ });
20024
+ }
20025
+ }
19742
20026
  const sketched = workflows.filter((p) => p.graph?.topology === "sketch").map((p) => p.name);
19743
20027
  if (sketched.length === 0) return;
19744
20028
  for (const p of workflows) {
@@ -19790,6 +20074,9 @@ ${firstBundleFrame(vmResult.error.stack)}` : ""}`,
19790
20074
  output: d.graph.definition.outputSchema,
19791
20075
  state: d.graph.definition.stateSchema
19792
20076
  };
20077
+ const unplacedStepIds = [
20078
+ ...new Set(d.issues.filter((i) => i.code === WORKFLOW_COMPILE_CODES.UNPLACED_STEP && typeof i.stepId === "string").map((i) => i.stepId))
20079
+ ].sort();
19793
20080
  const entry = {
19794
20081
  ...this.baseManifestFields(compiled),
19795
20082
  kind: PrimitiveKind.WORKFLOW,
@@ -19805,6 +20092,7 @@ ${firstBundleFrame(vmResult.error.stack)}` : ""}`,
19805
20092
  toolRefs,
19806
20093
  workflowRefs: d.workflowRefs,
19807
20094
  mayInherit: d.mayInherit,
20095
+ unplacedStepIds: unplacedStepIds.length > 0 ? unplacedStepIds : void 0,
19808
20096
  budget: cfg.budget,
19809
20097
  concurrencyPolicy: cfg.concurrencyPolicy,
19810
20098
  schedule: config.schedule,
@@ -20418,8 +20706,16 @@ var init_agent_traverser = __esm({
20418
20706
  }
20419
20707
  this.log(`\u{1F916} Found agent: ${agentConfig.name} in ${agentConfig.sourcePath}`);
20420
20708
  const primitives = [];
20709
+ const seen = /* @__PURE__ */ new Set();
20710
+ const add2 = /* @__PURE__ */ __name((p) => {
20711
+ const key = `${p.kind}\0${p.sourcePath}\0${p.exportName}`;
20712
+ if (seen.has(key)) return false;
20713
+ seen.add(key);
20714
+ primitives.push(p);
20715
+ return true;
20716
+ }, "add");
20421
20717
  const { refs: _refs, ...agentMetadata } = agentConfig;
20422
- primitives.push(agentMetadata);
20718
+ add2(agentMetadata);
20423
20719
  for (const config of PRIMITIVE_TYPES) {
20424
20720
  const refs = agentConfig.refs.get(config.kind) || [];
20425
20721
  for (const ref of refs) {
@@ -20441,10 +20737,9 @@ var init_agent_traverser = __esm({
20441
20737
  viaDefaultImport: ref.viaDefaultImport === true
20442
20738
  });
20443
20739
  if (!primitive) continue;
20444
- primitives.push(primitive);
20740
+ if (!add2(primitive)) continue;
20445
20741
  if (config.resolveNested) {
20446
- const nested = this.resolveReferencesForPrimitive(primitive, sourcePath);
20447
- primitives.push(...nested);
20742
+ for (const nested of this.resolveReferencesForPrimitive(primitive, sourcePath)) add2(nested);
20448
20743
  }
20449
20744
  }
20450
20745
  }
@@ -24642,6 +24937,14 @@ var init_workflow_api_service = __esm({
24642
24937
  async getVersionEnvOverlay(workflowId, version) {
24643
24938
  return this.httpGet(`${this.base}/${workflowId}/versions/${encodeURIComponent(version)}/env-overlay`, await this.auth());
24644
24939
  }
24940
+ /**
24941
+ * WF-403 (13 §13.14; LUA-752) — `GET …/:workflowId/export?version=`: the active (or named) version as pushable
24942
+ * files (`{ form, version, files:[{ path, contents }], warnings }`). `lua workflows export` writes them to disk.
24943
+ */
24944
+ async exportWorkflowFiles(workflowId, version) {
24945
+ const qs = version ? `?version=${encodeURIComponent(version)}` : "";
24946
+ return this.httpGet(`${this.base}/${workflowId}/export${qs}`, await this.auth());
24947
+ }
24645
24948
  async getWorkflowVersions(workflowId) {
24646
24949
  return this.httpGet(`${this.base}/${workflowId}/versions`, await this.auth());
24647
24950
  }
@@ -24738,6 +25041,34 @@ var init_workflow_api_service = __esm({
24738
25041
  async retryStep(runId, stepId, data = {}) {
24739
25042
  return this.httpPost(`${this.runs}/${runId}/steps/${pathId(stepId)}/retry`, data, await this.auth());
24740
25043
  }
25044
+ /**
25045
+ * R37 (LUA-752) — a human decides a parked step: `skip` it, `complete` it with the output it would have produced,
25046
+ * or `fail` it (the step's onError policy applies). 400 `VALIDATION_FAILED{output-required}` / `RESOLVE_OUTPUT_INVALID`,
25047
+ * 403 `APPROVAL_REQUIRES_HUMAN` / `NOT_RUN_CREATOR`, 404 `RUN_NOT_FOUND` / `STEP_NOT_FOUND`, 409 `STEP_NOT_PARKED` /
25048
+ * `RUN_TERMINAL`, 413 `OUTPUT_TOO_LARGE`; the CAS loser is a 200 `{ resolved:false, reason, recorded }`.
25049
+ */
25050
+ async resolveStep(runId, stepId, data) {
25051
+ return this.httpPost(`${this.runs}/${runId}/steps/${pathId(stepId)}/resolve`, data, await this.auth());
25052
+ }
25053
+ /**
25054
+ * R45 (LUA-752) — raise a parked run's budget (`maxCredits` / `maxSteps` / `maxJobSeconds` / `maxDurationSeconds`;
25055
+ * increases only). 400 `VALIDATION_FAILED` / `CAP_EXCEEDED`, 403 `NOT_RUN_CREATOR`, 409 `BUDGET_NOT_RAISABLE`.
25056
+ */
25057
+ async raiseBudget(runId, data) {
25058
+ return this.httpPost(`${this.runs}/${runId}/budget`, data, await this.auth());
25059
+ }
25060
+ /**
25061
+ * R39 (LUA-752) — the current approval payload with its `payloadFingerprint` / `editRevision` (what
25062
+ * `approve --edit --fingerprint` echoes). `path` pages one array of a large payload.
25063
+ */
25064
+ async getApprovalPayload(runId, approvalId, query = {}) {
25065
+ const q = new URLSearchParams();
25066
+ if (query.path) q.append("path", query.path);
25067
+ if (query.cursor) q.append("cursor", query.cursor);
25068
+ if (query.limit !== void 0) q.append("limit", String(query.limit));
25069
+ const qs = q.toString();
25070
+ return this.httpGet(`${this.runs}/${runId}/approvals/${pathId(approvalId)}/payload${qs ? `?${qs}` : ""}`, await this.auth());
25071
+ }
24741
25072
  /** R13 — resolve an approval (human; `expectedFingerprint` guards against an edited payload — 409 `PAYLOAD_MISMATCH`). */
24742
25073
  async resolveApproval(runId, approvalId, data) {
24743
25074
  return this.httpPost(`${this.runs}/${runId}/approvals/${pathId(approvalId)}/resolve`, data, await this.auth());
@@ -24822,6 +25153,14 @@ var init_workflow_api_service = __esm({
24822
25153
  async getSchedule(jobId) {
24823
25154
  return this.httpGet(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
24824
25155
  }
25156
+ /** R27 (LUA-752) — create or replace the plain schedule Job of a workflow (201; 400 `VALIDATION_FAILED` / `WORKFLOW_NOT_ON_AGENT`, 404 `WORKFLOW_NOT_FOUND`, 409 `SCHEDULE_CAP`). */
25157
+ async createSchedule(data) {
25158
+ return this.httpPost(this.schedules, data, await this.auth());
25159
+ }
25160
+ /** R56 (LUA-752) — pause / resume a schedule, persist `backfillOnEnable`, one-shot `backfillNow` (404 `SCHEDULE_NOT_FOUND`, 400 `VALIDATION_FAILED{issues}`). */
25161
+ async updateSchedule(jobId, data) {
25162
+ return this.httpPatch(`${this.schedules}/${encodeURIComponent(jobId)}`, data, await this.auth());
25163
+ }
24825
25164
  /** R28 — delete a schedule Job (404 `SCHEDULE_NOT_FOUND`). The CLI refuses a LIVE goal's job BEFORE this call (`goal_schedule`); an ended goal's lingering Job is retired here (LUA-760). */
24826
25165
  async deleteSchedule(jobId) {
24827
25166
  return this.httpDelete(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
@@ -26945,6 +27284,7 @@ var AgentApi = class extends HttpClient {
26945
27284
  // src/utils/init-agent.ts
26946
27285
  init_constants();
26947
27286
  init_cli();
27287
+ init_cli_error();
26948
27288
  async function fetchAgentTypes(apiKey) {
26949
27289
  writeProgress("\u{1F504} Fetching agent types...");
26950
27290
  const agentApi = new AgentApi(BASE_URLS.API, apiKey);
@@ -27037,12 +27377,18 @@ async function fetchExistingAgentDetails(apiKey, agentId) {
27037
27377
  return fetchAgentDetails(agentApi, agentId);
27038
27378
  }
27039
27379
  __name(fetchExistingAgentDetails, "fetchExistingAgentDetails");
27380
+ async function fetchApprovedModelsOrThrow(apiKey, agentId, orgId) {
27381
+ const agentApi = new AgentApi(BASE_URLS.API, apiKey);
27382
+ const result = await agentApi.getApprovedModels(void 0, agentId, orgId);
27383
+ if (!result.success) {
27384
+ throw CliError.fromStatus(result.error?.statusCode, `Could not fetch models from the server: ${result.error?.message || "Unknown error"}`);
27385
+ }
27386
+ return result.data ?? [];
27387
+ }
27388
+ __name(fetchApprovedModelsOrThrow, "fetchApprovedModelsOrThrow");
27040
27389
  async function fetchApprovedModels(apiKey, agentId, orgId) {
27041
27390
  try {
27042
- const agentApi = new AgentApi(BASE_URLS.API, apiKey);
27043
- const result = await agentApi.getApprovedModels(void 0, agentId, orgId);
27044
- if (!result.success) return null;
27045
- return result.data ?? [];
27391
+ return await fetchApprovedModelsOrThrow(apiKey, agentId, orgId);
27046
27392
  } catch {
27047
27393
  return null;
27048
27394
  }
@@ -28709,12 +29055,7 @@ async function duplicateAgentNonInteractive(apiKey, userData, mode, options) {
28709
29055
  if (mode.orgId) {
28710
29056
  const existingOrg = orgs.find((o) => o.id === mode.orgId);
28711
29057
  if (!existingOrg) {
28712
- console.error(`\u274C Organization not found: ${mode.orgId}`);
28713
- writeInfo("\nAvailable organizations:");
28714
- for (const org of orgs) {
28715
- writeInfo(` - ${org.registeredName} (${org.id})`);
28716
- }
28717
- throw new Error("Operation failed");
29058
+ throw CliError.notFound(`Organization not found: ${mode.orgId}`, availableOrgsHint(orgs));
28718
29059
  }
28719
29060
  targetOrgId = mode.orgId;
28720
29061
  writeProgress(`\u2705 Using target organization: ${existingOrg.registeredName}`);
@@ -28724,12 +29065,7 @@ async function duplicateAgentNonInteractive(apiKey, userData, mode, options) {
28724
29065
  targetOrgId = matchedOrg.id;
28725
29066
  writeProgress(`\u2705 Found target organization: ${matchedOrg.registeredName}`);
28726
29067
  } else {
28727
- console.error(`\u274C Organization not found: ${mode.orgName}`);
28728
- writeInfo("\nAvailable organizations:");
28729
- for (const org of orgs) {
28730
- writeInfo(` - ${org.registeredName} (${org.id})`);
28731
- }
28732
- throw new Error("Operation failed");
29068
+ throw CliError.notFound(`Organization not found: ${mode.orgName}`, availableOrgsHint(orgs));
28733
29069
  }
28734
29070
  } else {
28735
29071
  targetOrgId = sourceOrgId;
@@ -29210,10 +29546,13 @@ async function selectExistingAgent(orgs, apiKey) {
29210
29546
  };
29211
29547
  }
29212
29548
  __name(selectExistingAgent, "selectExistingAgent");
29549
+ function availableOrgsHint(orgs) {
29550
+ return listHint("Available organizations:", orgs.map((org) => `${org.registeredName} (${org.id})`));
29551
+ }
29552
+ __name(availableOrgsHint, "availableOrgsHint");
29213
29553
  async function selectExistingAgentById(orgs, apiKey, agentId) {
29214
29554
  if (orgs.length === 0) {
29215
- console.error("\u274C No organizations found.");
29216
- throw new Error("No organizations found.");
29555
+ throw CliError.notFound("No organizations found.");
29217
29556
  }
29218
29557
  for (const org of orgs) {
29219
29558
  const agent = org.agents.find((candidate) => candidate.agentId === agentId);
@@ -29235,17 +29574,14 @@ async function selectExistingAgentById(orgs, apiKey, agentId) {
29235
29574
  };
29236
29575
  }
29237
29576
  }
29238
- console.error(`\u274C Agent not found: ${agentId}`);
29239
- writeInfo("\nAvailable agents:");
29577
+ const available = [];
29240
29578
  for (const org of orgs) {
29241
29579
  if (org.agents && org.agents.length > 0) {
29242
- writeInfo(` ${org.registeredName ?? org.name ?? org.id}:`);
29243
- for (const agent of org.agents) {
29244
- writeInfo(` - ${agent.name} (${agent.agentId})`);
29245
- }
29580
+ available.push(`${org.registeredName ?? org.name ?? org.id}:`);
29581
+ for (const agent of org.agents) available.push(` - ${agent.name} (${agent.agentId})`);
29246
29582
  }
29247
29583
  }
29248
- throw new Error("Operation failed");
29584
+ throw CliError.notFound(`Agent not found: ${agentId}`, listHint("Available agents:", available, ""));
29249
29585
  }
29250
29586
  __name(selectExistingAgentById, "selectExistingAgentById");
29251
29587
  async function createNewAgentNonInteractive(apiKey, userData, agentName, orgId, orgName, promoCode, model) {
@@ -29255,12 +29591,7 @@ async function createNewAgentNonInteractive(apiKey, userData, agentName, orgId,
29255
29591
  const orgs = userData.admin?.orgs || [];
29256
29592
  const existingOrg = orgs.find((o) => o.id === orgId);
29257
29593
  if (!existingOrg) {
29258
- console.error(`\u274C Organization not found: ${orgId}`);
29259
- writeInfo("\nAvailable organizations:");
29260
- for (const org of orgs) {
29261
- writeInfo(` - ${org.registeredName} (${org.id})`);
29262
- }
29263
- throw new Error("Operation failed");
29594
+ throw CliError.notFound(`Organization not found: ${orgId}`, availableOrgsHint(orgs));
29264
29595
  }
29265
29596
  writeProgress(`\u2705 Using organization: ${existingOrg.registeredName}`);
29266
29597
  } else if (orgName) {
@@ -31576,6 +31907,7 @@ var voiceHandler = new VoiceHandler();
31576
31907
  // src/primitives/workflow.handler.ts
31577
31908
  init_types();
31578
31909
  init_constants();
31910
+ init_cli_error();
31579
31911
  init_workflow_api_service();
31580
31912
  init_artifact_loader();
31581
31913
  init_bundle_upload();
@@ -32732,6 +33064,15 @@ async function runWorkflowLocallyInner(rawOpts) {
32732
33064
  stepResults[d]
32733
33065
  ]));
32734
33066
  }, "inputFor");
33067
+ const nodeInputFor = /* @__PURE__ */ __name((row2, single, base) => {
33068
+ const cfg = single.input;
33069
+ if (!cfg || typeof cfg !== "object" || Array.isArray(cfg) || Object.keys(cfg).length === 0) return base;
33070
+ const resolved = resolveMapping(parseMapConfig(cfg, row2.stepId), mappingCtx());
33071
+ if ("error" in resolved) {
33072
+ throw new StepFailure("binding_unresolved", `input map of "${row2.stepId}": ${"key" in resolved && resolved.key ? resolved.key : resolved.error}`);
33073
+ }
33074
+ return resolved.value;
33075
+ }, "nodeInputFor");
32735
33076
  let runFailure;
32736
33077
  let bailed;
32737
33078
  const terminalize = /* @__PURE__ */ __name((row2, status, extra = {}) => {
@@ -33015,8 +33356,9 @@ async function runWorkflowLocallyInner(rawOpts) {
33015
33356
  const fakeObject = /* @__PURE__ */ __name((single) => single.outputSchema ? {
33016
33357
  object: stubFromSchema(single.outputSchema)
33017
33358
  } : {}, "fakeObject");
33018
- const executeSingle = /* @__PURE__ */ __name(async (row2, single, input) => {
33359
+ const executeSingle = /* @__PURE__ */ __name(async (row2, single, rawInput) => {
33019
33360
  const id = row2.stepId;
33361
+ const input = nodeInputFor(row2, single, rawInput);
33020
33362
  const drift = stepInputIssue(single, input);
33021
33363
  if (drift) throw drift;
33022
33364
  if (opts.stepOutputs && id in opts.stepOutputs) {
@@ -33077,33 +33419,6 @@ async function runWorkflowLocallyInner(rawOpts) {
33077
33419
  return out;
33078
33420
  }
33079
33421
  case "workflow": {
33080
- if (single.workflowId === WORKFLOW_ARM_SUBRUN_ID && single.graph) {
33081
- const [armMap, inner] = single.graph;
33082
- const innerId = armId2(inner);
33083
- const ctx = {
33084
- ...mappingCtx(),
33085
- stepResults: {
33086
- ...stepResults,
33087
- [innerId]: input,
33088
- [row2.stepId]: input,
33089
- $item: input
33090
- }
33091
- };
33092
- const resolved = resolveMapping(parseMapConfig(armMap.mapConfig, armMap.id), ctx);
33093
- if ("error" in resolved) throw new StepFailure("binding_unresolved", `arm map "${armMap.id}": ${resolved.key ?? resolved.error}`);
33094
- stepResults[armMap.id] = resolved.value;
33095
- const innerRow = {
33096
- ...row2,
33097
- stepId: innerId,
33098
- node: {
33099
- ...row2.node,
33100
- entry: inner
33101
- }
33102
- };
33103
- const out = await executeSingle(innerRow, inner, resolved.value);
33104
- stepResults[innerId] = out;
33105
- return out;
33106
- }
33107
33422
  throw new StepFailure("SUBRUN_NOT_SUPPORTED_OFFLINE", `nested workflow "${single.workflowId}" cannot run offline \u2014 supply --step-output ${id}=<json>`);
33108
33423
  }
33109
33424
  }
@@ -33576,7 +33891,13 @@ async function runWorkflowLocallyInner(rawOpts) {
33576
33891
  }
33577
33892
  const suppliedId = stepOutputIdFor(row2);
33578
33893
  if (opts.stepOutputs && suppliedId !== void 0) {
33579
- const drift = entry.type === "step" ? stepInputIssue(entry, inputFor(row2)) : void 0;
33894
+ let drift;
33895
+ try {
33896
+ drift = entry.type === "step" ? stepInputIssue(entry, nodeInputFor(row2, entry, inputFor(row2))) : void 0;
33897
+ } catch (e) {
33898
+ if (!(e instanceof StepFailure)) throw e;
33899
+ drift = e;
33900
+ }
33580
33901
  if (drift) {
33581
33902
  row2.status = "running";
33582
33903
  row2.error = {
@@ -34584,6 +34905,7 @@ var WorkflowHandler = class extends BaseVersionedHandler {
34584
34905
  prepareForPush(manifest, name, projectPath = process.cwd(), bundleAccumulator) {
34585
34906
  const primitive = findPrimitive(manifest, name, this.kind);
34586
34907
  if (!primitive) return null;
34908
+ if (primitive.unplacedStepIds?.length) throw unplacedStepError(primitive.name, primitive.unplacedStepIds);
34587
34909
  if (primitive.form === "script") return this.buildPushData(primitive, void 0);
34588
34910
  const data = super.prepareForPush(manifest, name, projectPath, bundleAccumulator);
34589
34911
  if (!data) return null;
@@ -34599,6 +34921,11 @@ var WorkflowHandler = class extends BaseVersionedHandler {
34599
34921
  * engine's claim could not resolve `toolId` and every static tool step failed `TOOL_REF_UNRESOLVED`
34600
34922
  * (prod 2026-09-05, kitchen-sink `armB`, 39/39 runs). Each referenced tool rides the version as
34601
34923
  * `tools[] = {name, code | codeS3Hash}`, through the same accumulator the workflow bundle uses.
34924
+ *
34925
+ * LUA-781: a reference with NO compiled artifact used to warn and push anyway (exit 0) — the version carried no
34926
+ * `tools[]` and every run failed the node `not_in_version_bundle` (staging 2026-09-08: a `LuaTool` written as a
34927
+ * plain object was never detected). The compile now refuses it (`WORKFLOW_TOOL_UNBUNDLED`, an error); this is the
34928
+ * belt for a stale manifest — one typed line, exit 1, nothing sent.
34602
34929
  */
34603
34930
  toolBundlesFor(wf, manifest, projectPath, bundleAccumulator) {
34604
34931
  const out = [];
@@ -34606,10 +34933,7 @@ var WorkflowHandler = class extends BaseVersionedHandler {
34606
34933
  ...new Set(wf.toolRefs ?? [])
34607
34934
  ]) {
34608
34935
  const tool = findPrimitive(manifest, toolName, PrimitiveKind.TOOL);
34609
- if (!tool) {
34610
- console.warn(`\u26A0\uFE0F workflow "${wf.name}" references tool "${toolName}" but the manifest carries no compiled artifact for it \u2014 the engine can only resolve it through the agent's skills (TOOL_REF_UNRESOLVED otherwise)`);
34611
- continue;
34612
- }
34936
+ if (!tool) throw toolUnbundledError(wf.name, toolName);
34613
34937
  const code = loadArtifact(tool, projectPath);
34614
34938
  if (bundleAccumulator) {
34615
34939
  const rawGzip = compressForPushRaw(code);
@@ -34707,6 +35031,19 @@ var WorkflowHandler = class extends BaseVersionedHandler {
34707
35031
  }
34708
35032
  };
34709
35033
  var workflowHandler = new WorkflowHandler();
35034
+ function unplacedStepError(workflowName, stepIds) {
35035
+ const list = stepIds.map((id) => `"${id}"`).join(", ");
35036
+ return new CliError("unplaced_step", `workflow "${workflowName}" is not pushed: createStep ${list} ${stepIds.length === 1 ? "is" : "are"} declared in its source file but placed in no workflow (WORKFLOW_UNPLACED_STEP)`, {
35037
+ hint: `place ${stepIds.length === 1 ? "it" : "them"} in a chain (.then(step) / .parallel([...])) or delete the declaration, then push again \u2014 lua compile lists every unplaced step`
35038
+ });
35039
+ }
35040
+ __name(unplacedStepError, "unplacedStepError");
35041
+ function toolUnbundledError(workflowName, toolName) {
35042
+ return new CliError("tool_unbundled", `workflow "${workflowName}" is not pushed: toolStep tool "${toolName}" has no compiled artifact in this project (WORKFLOW_TOOL_UNBUNDLED) \u2014 the version would carry no tools[] entry for it and every run would fail the step TOOL_REF_UNRESOLVED (not_in_version_bundle)`, {
35043
+ hint: `declare "${toolName}" where the compiler sees it \u2014 defineTool({ name: '${toolName}', \u2026 }), a class implementing LuaTool, or an exported { name, description, inputSchema, execute } object \u2014 pass that export to toolStep(), then run lua compile (it lists every unbundled tool) and push again`
35044
+ });
35045
+ }
35046
+ __name(toolUnbundledError, "toolUnbundledError");
34710
35047
  function workflowNameTakenHint(name, error) {
34711
35048
  if (error?.code !== "WORKFLOW_NAME_TAKEN") return null;
34712
35049
  const holder = error.dynamic ? "a chat-composed (dynamic) workflow" : "an existing workflow";
@@ -35354,13 +35691,19 @@ var ALIAS_MAP = {
35354
35691
  "cancel",
35355
35692
  "resume",
35356
35693
  "retry-step",
35694
+ // LUA-752: the R37 / R45 / R39 verbs the park hints and the approve refusal already named (they did not exist).
35695
+ "resolve-step",
35696
+ "raise-budget",
35357
35697
  "approve",
35698
+ "approval-payload",
35358
35699
  "signal",
35359
35700
  "replay",
35360
35701
  "logs",
35361
35702
  "delete",
35362
35703
  "delete-run",
35363
35704
  "env-overlay",
35705
+ // LUA-752 (L6.3): WF-403 — the active / named version back as pushable files.
35706
+ "export",
35364
35707
  "archive-runs",
35365
35708
  "workspace",
35366
35709
  "jobs",
@@ -35380,6 +35723,12 @@ var ALIAS_MAP = {
35380
35723
  env: "env-overlay",
35381
35724
  overlay: "env-overlay",
35382
35725
  archive: "archive-runs",
35726
+ resolve: "resolve-step",
35727
+ budget: "raise-budget",
35728
+ raise: "raise-budget",
35729
+ payload: "approval-payload",
35730
+ codegen: "export",
35731
+ "export-workflow": "export",
35383
35732
  show: "view",
35384
35733
  info: "view",
35385
35734
  ls: "list",
@@ -35436,12 +35785,28 @@ var ALIAS_MAP = {
35436
35785
  })
35437
35786
  },
35438
35787
  "workflows.schedules.action": {
35788
+ // LUA-752: `create` (R27) · `patch` / `pause` / `resume` (R56) beside the LUA-627 reads and R28 delete.
35439
35789
  canonical: [
35440
35790
  "list",
35791
+ "create",
35792
+ "patch",
35793
+ "pause",
35794
+ "resume",
35441
35795
  "delete"
35442
35796
  ],
35443
35797
  aliases: lowerKeys({
35444
35798
  ls: "list",
35799
+ new: "create",
35800
+ add: "create",
35801
+ set: "create",
35802
+ update: "patch",
35803
+ edit: "patch",
35804
+ hold: "pause",
35805
+ stop: "pause",
35806
+ disable: "pause",
35807
+ unpause: "resume",
35808
+ enable: "resume",
35809
+ continue: "resume",
35445
35810
  rm: "delete",
35446
35811
  remove: "delete",
35447
35812
  del: "delete",
@@ -37032,6 +37397,7 @@ function writeLedgerFile(file, value3) {
37032
37397
  __name(writeLedgerFile, "writeLedgerFile");
37033
37398
 
37034
37399
  // src/commands/test.ts
37400
+ init_cli_error();
37035
37401
  async function loadAuthOrFail() {
37036
37402
  const { config, agentId, apiKey } = await initializeCommand({
37037
37403
  showProgress: false
@@ -37079,9 +37445,7 @@ function parseJsonInputOrFail(inputJson, expectedFormat) {
37079
37445
  try {
37080
37446
  return JSON.parse(inputJson);
37081
37447
  } catch (error) {
37082
- console.error(`\u274C Invalid JSON input: ${error instanceof Error ? error.message : "Unknown error"}`);
37083
- console.log(`Expected format: ${expectedFormat}`);
37084
- throw new Error(`Invalid JSON input: ${error instanceof Error ? error.message : "Unknown error"}`);
37448
+ throw CliError.usage(`Invalid JSON input: ${error instanceof Error ? error.message : "Unknown error"}`, `Expected format: ${expectedFormat}`);
37085
37449
  }
37086
37450
  }
37087
37451
  __name(parseJsonInputOrFail, "parseJsonInputOrFail");
@@ -37090,8 +37454,7 @@ async function selectEntityOrPrompt(options) {
37090
37454
  if (entityName) {
37091
37455
  const selected = items.find((item) => item[idProperty] === entityName || item[nameProperty] === entityName);
37092
37456
  if (!selected) {
37093
- console.error(`\u274C ${entityType} "${entityName}" not found`);
37094
- throw new Error(`${entityType} "${entityName}" not found`);
37457
+ throw CliError.notFound(`${entityType} "${entityName}" not found`);
37095
37458
  }
37096
37459
  progress(asJson ?? false, `\u2705 Selected ${entityType.toLowerCase()}: ${selected[nameProperty]}`);
37097
37460
  return selected;
@@ -37182,11 +37545,11 @@ async function testCommand(type, cmdObj) {
37182
37545
  type = validateOrSuggest("test.type", type);
37183
37546
  selectedType = type;
37184
37547
  } else if (entityName) {
37185
- console.error(`\u274C Type must be specified when using the --name option.`);
37186
- console.log("\nUsage:");
37187
- console.log(' lua test skill --name mySkill --input "{...}" Test skill with JSON input');
37188
- console.log(' lua test webhook --name myWebhook --input "{...}" Test webhook with JSON input');
37189
- throw new Error("Type must be specified when using the --name option.");
37548
+ throw CliError.usage("Type must be specified when using the --name option.", [
37549
+ "Usage:",
37550
+ ' lua test skill --name mySkill --input "{...}" Test skill with JSON input',
37551
+ ' lua test webhook --name myWebhook --input "{...}" Test webhook with JSON input'
37552
+ ].join("\n"));
37190
37553
  } else {
37191
37554
  const typeAnswer = await safePrompt([
37192
37555
  {
@@ -37273,15 +37636,13 @@ async function testSkill(entityName, inputJson, asJson = false) {
37273
37636
  progress(asJson, `\u{1F4C4} Loaded environment variables from .env file`);
37274
37637
  }
37275
37638
  if (allTools.length === 0) {
37276
- console.error("\u274C No tools found in compiled output.");
37277
- throw new Error("No tools found in compiled output.");
37639
+ throw new CliError("error", "No tools found in compiled output.");
37278
37640
  }
37279
37641
  let selectedTool;
37280
37642
  if (entityName) {
37281
37643
  selectedTool = allTools.find((t) => t.id === entityName || t.name === entityName);
37282
37644
  if (!selectedTool) {
37283
- console.error(`\u274C Tool "${entityName}" not found`);
37284
- throw new Error("Tool");
37645
+ throw CliError.notFound(`Tool "${entityName}" not found`);
37285
37646
  }
37286
37647
  progress(asJson, `\u2705 Selected tool: ${selectedTool.name}`);
37287
37648
  } else {
@@ -41382,6 +41743,7 @@ async function probeAgentErrorsSince(apiKey, agentId, sinceISO) {
41382
41743
  __name(probeAgentErrorsSince, "probeAgentErrorsSince");
41383
41744
 
41384
41745
  // src/commands/chat.ts
41746
+ init_cli_error();
41385
41747
  async function runChatProbe(chatEnv, window) {
41386
41748
  if (hintsDisabled()) return;
41387
41749
  try {
@@ -41508,8 +41870,7 @@ async function chatCommand(cmdObj) {
41508
41870
  break;
41509
41871
  }
41510
41872
  case "error":
41511
- console.log(`\u274C ${envResolution.message}`);
41512
- throw new Error(`${envResolution.message}`);
41873
+ throw CliError.usage(envResolution.message);
41513
41874
  }
41514
41875
  }
41515
41876
  let chatEnv = {
@@ -41890,8 +42251,6 @@ async function sendSingleMessage(chatEnv, message, probeWindow) {
41890
42251
  }
41891
42252
  await runChatProbe(chatEnv, probeWindow);
41892
42253
  } catch (error) {
41893
- console.error(`
41894
- \u274C Error: ${error instanceof Error ? error.message : "Unknown error"}`);
41895
42254
  writeHintBlock({
41896
42255
  headline: "Something went wrong. Debug options:",
41897
42256
  lines: [
@@ -41907,7 +42266,8 @@ async function sendSingleMessage(chatEnv, message, probeWindow) {
41907
42266
  when: "error"
41908
42267
  });
41909
42268
  await runChatProbe(chatEnv, probeWindow);
41910
- throw new Error("Operation failed");
42269
+ if (isTypedCliError(error)) throw error;
42270
+ throw new CliError("error", error instanceof Error ? error.message : "Unknown error");
41911
42271
  }
41912
42272
  }
41913
42273
  __name(sendSingleMessage, "sendSingleMessage");
@@ -42052,6 +42412,7 @@ import fs18 from "fs";
42052
42412
  import path24 from "path";
42053
42413
  import inquirer10 from "inquirer";
42054
42414
  init_analytics();
42415
+ init_cli_error();
42055
42416
  function resolveEnvironment(env, hasNonInteractiveFlags) {
42056
42417
  if (env) {
42057
42418
  const normalized = validateOrSuggest("env.environment", env);
@@ -42166,12 +42527,7 @@ async function envCommand(env, cmdObj) {
42166
42527
  selectedEnvironment = environment;
42167
42528
  break;
42168
42529
  case "error":
42169
- console.log(`\u274C ${envResolution.message}`);
42170
- if (envResolution.usage) {
42171
- console.log("\nUsage:");
42172
- envResolution.usage.forEach((line) => console.log(` ${line}`));
42173
- }
42174
- throw new Error("Operation failed");
42530
+ throw CliError.usage(envResolution.message, listHint("Usage:", envResolution.usage ?? [], ""));
42175
42531
  }
42176
42532
  let context = {
42177
42533
  environment: selectedEnvironment,
@@ -42201,12 +42557,7 @@ async function envCommand(env, cmdObj) {
42201
42557
  await manageEnvironmentVariables(context);
42202
42558
  break;
42203
42559
  case "error":
42204
- console.log(`\u274C ${mode.message}`);
42205
- if (mode.examples) {
42206
- console.log("\nExamples:");
42207
- mode.examples.forEach((ex) => console.log(` ${ex}`));
42208
- }
42209
- throw new Error("Operation failed");
42560
+ throw CliError.usage(mode.message, listHint("Examples:", mode.examples ?? [], ""));
42210
42561
  }
42211
42562
  trackEvent("cli_env_action", {
42212
42563
  environment: env || "interactive",
@@ -42304,8 +42655,7 @@ __name(listVariables, "listVariables");
42304
42655
  async function setVariable(context, key, value3) {
42305
42656
  const validationError2 = validateKeyFormat(key);
42306
42657
  if (validationError2) {
42307
- console.log(`\u274C ${validationError2}`);
42308
- throw new Error(`${validationError2}`);
42658
+ throw CliError.usage(validationError2);
42309
42659
  }
42310
42660
  try {
42311
42661
  const { created } = await saveVariableCore(context, key, value3);
@@ -42319,8 +42669,8 @@ async function setVariable(context, key, value3) {
42319
42669
  });
42320
42670
  }
42321
42671
  } catch (error) {
42322
- console.log(`\u274C Error setting variable: ${error instanceof Error ? error.message : "Unknown error"}`);
42323
- throw new Error(`Error setting variable: ${error instanceof Error ? error.message : "Unknown error"}`);
42672
+ if (isTypedCliError(error)) throw error;
42673
+ throw new CliError("error", `Error setting variable: ${error instanceof Error ? error.message : "Unknown error"}`);
42324
42674
  }
42325
42675
  }
42326
42676
  __name(setVariable, "setVariable");
@@ -42336,8 +42686,8 @@ async function removeVariable(context, key) {
42336
42686
  });
42337
42687
  }
42338
42688
  } catch (error) {
42339
- console.log(`\u274C ${error instanceof Error ? error.message : "Unknown error"}`);
42340
- throw new Error(`${error instanceof Error ? error.message : "Unknown error"}`);
42689
+ if (isTypedCliError(error)) throw error;
42690
+ throw new CliError("error", error instanceof Error ? error.message : "Unknown error");
42341
42691
  }
42342
42692
  }
42343
42693
  __name(removeVariable, "removeVariable");
@@ -42604,6 +42954,7 @@ init_command_utils();
42604
42954
  init_compiler2();
42605
42955
  init_artifact_loader();
42606
42956
  init_analytics();
42957
+ init_cli_error();
42607
42958
  async function personaCommand(env, action, cmdObj) {
42608
42959
  return withErrorHandling(async () => {
42609
42960
  const options = {
@@ -42759,39 +43110,24 @@ async function executeProductionNonInteractive(context, action, options) {
42759
43110
  break;
42760
43111
  case "deploy": {
42761
43112
  if (!options.personaVersion) {
42762
- console.error("\u274C --persona-version is required for deploy action");
42763
- console.log("\nUsage: lua persona production deploy --persona-version <version>");
42764
- console.log(" lua persona production deploy -v latest");
42765
- if (versions.length > 0) {
42766
- console.log("\nAvailable versions:");
42767
- versions.slice(0, 5).forEach((v) => {
42768
- const mark = v.isCurrent ? " (current)" : "";
42769
- console.log(` - ${v.version}${mark}`);
42770
- });
42771
- }
42772
- throw new Error("--persona-version is required for deploy action");
43113
+ throw CliError.usage("--persona-version is required for deploy action", joinHint("Usage: lua persona production deploy --persona-version <version>\n lua persona production deploy -v latest", listHint("Available versions:", versions.slice(0, 5).map((v) => `${v.version}${v.isCurrent ? " (current)" : ""}`))));
42773
43114
  }
42774
43115
  let versionNumber;
42775
43116
  if (options.personaVersion.toLowerCase() === "latest") {
42776
43117
  if (versions.length === 0) {
42777
- console.error("\u274C No versions available to deploy");
42778
- throw new Error("No versions available to deploy");
43118
+ throw CliError.notFound("No versions available to deploy");
42779
43119
  }
42780
43120
  versionNumber = versions[0].version;
42781
43121
  console.log(`\u2139\uFE0F Latest version is ${versionNumber}`);
42782
43122
  } else {
42783
43123
  versionNumber = parseInt(options.personaVersion, 10);
42784
43124
  if (isNaN(versionNumber)) {
42785
- console.error(`\u274C Invalid version number: "${options.personaVersion}"`);
42786
- throw new Error(`Invalid version number: "${options.personaVersion}"`);
43125
+ throw CliError.usage(`Invalid version number: "${options.personaVersion}"`);
42787
43126
  }
42788
43127
  }
42789
43128
  const versionExists = versions.find((v) => v.version === versionNumber);
42790
43129
  if (!versionExists) {
42791
- console.error(`\u274C Version ${versionNumber} not found`);
42792
- console.log("\nAvailable versions:");
42793
- versions.forEach((v) => console.log(` - ${v.version}`));
42794
- throw new Error(`Version ${versionNumber} not found`);
43130
+ throw CliError.notFound(`Version ${versionNumber} not found`, listHint("Available versions:", versions.map((v) => String(v.version))));
42795
43131
  }
42796
43132
  if (versionExists.isCurrent) {
42797
43133
  console.log(`\u2139\uFE0F Version ${versionNumber} is already deployed.`);
@@ -43783,6 +44119,7 @@ var ResourceApi = class extends HttpClient {
43783
44119
 
43784
44120
  // src/commands/resources.ts
43785
44121
  init_analytics();
44122
+ init_cli_error();
43786
44123
  async function resourcesCommand(action, cmdObj) {
43787
44124
  return withErrorHandling(async () => {
43788
44125
  const options = {
@@ -43902,25 +44239,11 @@ async function executeNonInteractive2(context, action, options) {
43902
44239
  return;
43903
44240
  }
43904
44241
  if (!options.resourceName) {
43905
- console.error(`\u274C --resource-name is required for action "${normalizedAction}"`);
43906
- console.log(`
43907
- Usage: lua resources ${normalizedAction} --resource-name <name>`);
43908
- if (resources.length > 0) {
43909
- console.log("\nAvailable resources:");
43910
- resources.forEach((r) => console.log(` - ${r.name}`));
43911
- }
43912
- throw new Error("Operation failed");
44242
+ throw CliError.usage(`--resource-name is required for action "${normalizedAction}"`, joinHint(`Usage: lua resources ${normalizedAction} --resource-name <name>`, listHint("Available resources:", resources.map((r) => r.name))));
43913
44243
  }
43914
44244
  const selectedResource = findResource(resources, options.resourceName);
43915
44245
  if (!selectedResource) {
43916
- console.error(`\u274C Resource "${options.resourceName}" not found`);
43917
- if (resources.length > 0) {
43918
- console.log("\nAvailable resources:");
43919
- resources.forEach((r) => console.log(` - ${r.name}`));
43920
- } else {
43921
- console.log("\nNo resources found.");
43922
- }
43923
- throw new Error("Operation failed");
44246
+ throw CliError.notFound(`Resource "${options.resourceName}" not found`, listHint("Available resources:", resources.map((r) => r.name)) ?? "No resources found.");
43924
44247
  }
43925
44248
  switch (normalizedAction) {
43926
44249
  case "view":
@@ -44681,6 +45004,7 @@ __name(doRelease, "doRelease");
44681
45004
  // src/commands/channels.ts
44682
45005
  init_constants();
44683
45006
  init_analytics();
45007
+ init_cli_error();
44684
45008
  async function channelsCommand(action) {
44685
45009
  return withErrorHandling(async () => {
44686
45010
  const { config, agentId, apiKey } = await initializeCommand();
@@ -44697,6 +45021,14 @@ async function channelsCommand(action) {
44697
45021
  }, "channels management");
44698
45022
  }
44699
45023
  __name(channelsCommand, "channelsCommand");
45024
+ function channelCreateError(error, alreadyConnected) {
45025
+ if (error?.statusCode === 400 && error?.message?.includes("already exists")) {
45026
+ return CliError.fromStatus(400, "Channel already exists", `${alreadyConnected}
45027
+ Use 'lua channels' to list existing channels.`);
45028
+ }
45029
+ return CliError.fromStatus(error?.statusCode, error?.message || "Unknown error", error?.error);
45030
+ }
45031
+ __name(channelCreateError, "channelCreateError");
44700
45032
  async function fetchChannelsCore(agentApi, agentId) {
44701
45033
  const response = await agentApi.getAgentChannels(agentId);
44702
45034
  if (!response.success) {
@@ -45108,22 +45440,7 @@ async function createWhatsAppChannel(agentApi, agentId) {
45108
45440
  writeInfo("\n\u{1F4E1} Creating WhatsApp channel...");
45109
45441
  const response = await agentApi.createWhatsAppChannel(agentId, channelData);
45110
45442
  if (!response.success) {
45111
- const error = response.error;
45112
- if (error?.statusCode === 400 && error?.message?.includes("already exists")) {
45113
- console.error(`
45114
- \u274C Channel already exists`);
45115
- console.error(`\u{1F4A1} This WhatsApp number is already connected to this agent.`);
45116
- console.error(` Use 'lua channels' to list existing channels.
45117
- `);
45118
- } else {
45119
- console.error(`
45120
- \u274C Error: ${error?.message || "Unknown error"}`);
45121
- if (error?.error) {
45122
- console.error(` ${error.error}
45123
- `);
45124
- }
45125
- }
45126
- throw new Error("Operation failed");
45443
+ throw channelCreateError(response.error, "This WhatsApp number is already connected to this agent.");
45127
45444
  }
45128
45445
  const data = response.data;
45129
45446
  writeSuccess("\n\u2705 WhatsApp channel created successfully!\n");
@@ -45165,22 +45482,7 @@ async function createFacebookChannel(agentApi, agentId) {
45165
45482
  writeInfo("\n\u{1F4E1} Creating Facebook channel...");
45166
45483
  const response = await agentApi.createFacebookChannel(agentId, channelData);
45167
45484
  if (!response.success) {
45168
- const error = response.error;
45169
- if (error?.statusCode === 400 && error?.message?.includes("already exists")) {
45170
- console.error(`
45171
- \u274C Channel already exists`);
45172
- console.error(`\u{1F4A1} This Facebook page is already connected to this agent.`);
45173
- console.error(` Use 'lua channels' to list existing channels.
45174
- `);
45175
- } else {
45176
- console.error(`
45177
- \u274C Error: ${error?.message || "Unknown error"}`);
45178
- if (error?.error) {
45179
- console.error(` ${error.error}
45180
- `);
45181
- }
45182
- }
45183
- throw new Error("Operation failed");
45485
+ throw channelCreateError(response.error, "This Facebook page is already connected to this agent.");
45184
45486
  }
45185
45487
  const data = response.data;
45186
45488
  writeSuccess("\n\u2705 Facebook channel created successfully!\n");
@@ -45251,22 +45553,7 @@ async function createEmailChannel(agentApi, agentId) {
45251
45553
  writeInfo("\n\u{1F4E1} Creating Email channel...");
45252
45554
  const response = await agentApi.createEmailChannel(agentId, channelData);
45253
45555
  if (!response.success) {
45254
- const error = response.error;
45255
- if (error?.statusCode === 400 && error?.message?.includes("already exists")) {
45256
- console.error(`
45257
- \u274C Channel already exists`);
45258
- console.error(`\u{1F4A1} This email address is already connected to this agent.`);
45259
- console.error(` Use 'lua channels' to list existing channels.
45260
- `);
45261
- } else {
45262
- console.error(`
45263
- \u274C Error: ${error?.message || "Unknown error"}`);
45264
- if (error?.error) {
45265
- console.error(` ${error.error}
45266
- `);
45267
- }
45268
- }
45269
- throw new Error("Operation failed");
45556
+ throw channelCreateError(response.error, "This email address is already connected to this agent.");
45270
45557
  }
45271
45558
  const data = response.data;
45272
45559
  writeSuccess("\n\u2705 Email channel created successfully!\n");
@@ -45339,22 +45626,7 @@ async function createSlackPrivateChannel(agentApi, agentId) {
45339
45626
  writeInfo("\n\u{1F4E1} Creating Slack private channel...");
45340
45627
  const response = await agentApi.createSlackChannel(agentId, channelData);
45341
45628
  if (!response.success) {
45342
- const error = response.error;
45343
- if (error?.statusCode === 400 && error?.message?.includes("already exists")) {
45344
- console.error(`
45345
- \u274C Channel already exists`);
45346
- console.error(`\u{1F4A1} This Slack workspace is already connected to this agent.`);
45347
- console.error(` Use 'lua channels' to list existing channels.
45348
- `);
45349
- } else {
45350
- console.error(`
45351
- \u274C Error: ${error?.message || "Unknown error"}`);
45352
- if (error?.error) {
45353
- console.error(` ${error.error}
45354
- `);
45355
- }
45356
- }
45357
- throw new Error("Operation failed");
45629
+ throw channelCreateError(response.error, "This Slack workspace is already connected to this agent.");
45358
45630
  }
45359
45631
  const data = response.data;
45360
45632
  if ("slack" in data && "botName" in data.slack) {
@@ -45409,22 +45681,7 @@ async function createSlackPublicChannel(agentApi, agentId) {
45409
45681
  writeInfo("\n\u{1F4E1} Creating Slack public channel...");
45410
45682
  const response = await agentApi.createSlackChannel(agentId, channelData);
45411
45683
  if (!response.success) {
45412
- const error = response.error;
45413
- if (error?.statusCode === 400 && error?.message?.includes("already exists")) {
45414
- console.error(`
45415
- \u274C Channel already exists`);
45416
- console.error(`\u{1F4A1} This Slack app is already connected to this agent.`);
45417
- console.error(` Use 'lua channels' to list existing channels.
45418
- `);
45419
- } else {
45420
- console.error(`
45421
- \u274C Error: ${error?.message || "Unknown error"}`);
45422
- if (error?.error) {
45423
- console.error(` ${error.error}
45424
- `);
45425
- }
45426
- }
45427
- throw new Error("Operation failed");
45684
+ throw channelCreateError(response.error, "This Slack app is already connected to this agent.");
45428
45685
  }
45429
45686
  const data = response.data;
45430
45687
  if ("redirectUri" in data) {
@@ -45481,6 +45738,7 @@ var CallMetricsApi = class extends HttpClient {
45481
45738
  };
45482
45739
 
45483
45740
  // src/commands/logs.ts
45741
+ init_cli_error();
45484
45742
  var ALL_VALID_SOURCES = AGENT_LOG_SOURCES;
45485
45743
  var ENTITY_SOURCES = /* @__PURE__ */ new Set([
45486
45744
  "skill",
@@ -45688,20 +45946,14 @@ async function nonInteractiveLogs(logsApi, agentId, apiKey, options) {
45688
45946
  }
45689
45947
  if (options.name) {
45690
45948
  if (!filters.logSource) {
45691
- console.error("\u274C --type is required when using --name");
45692
- console.log("\nUsage: lua logs --type skill --name mySkill");
45693
- throw new Error("--type is required when using --name");
45949
+ throw CliError.usage("--type is required when using --name", "Usage: lua logs --type skill --name mySkill");
45694
45950
  }
45695
45951
  if (NON_ENTITY_SOURCES.has(filters.logSource)) {
45696
- console.error(`\u274C --name is not applicable for ${filters.logSource} type`);
45697
- console.log(`
45698
- Usage: lua logs --type ${filters.logSource} (no --name needed)`);
45699
- throw new Error("--name is not applicable for ${filters.logSource} type");
45952
+ throw CliError.usage(`--name is not applicable for ${filters.logSource} type`, `Usage: lua logs --type ${filters.logSource} (no --name needed)`);
45700
45953
  }
45701
45954
  const entityId = await resolveEntityId(apiKey, agentId, filters.logSource, options.name);
45702
45955
  if (!entityId) {
45703
- console.error(`\u274C ${capitalize(filters.logSource ?? "")} "${options.name}" not found`);
45704
- throw new Error(`${capitalize(filters.logSource ?? "")} "${options.name}" not found`);
45956
+ throw CliError.notFound(`${capitalize(filters.logSource ?? "")} "${options.name}" not found`);
45705
45957
  }
45706
45958
  filters.primitiveId = entityId;
45707
45959
  }
@@ -45710,8 +45962,7 @@ Usage: lua logs --type ${filters.logSource} (no --name needed)`);
45710
45962
  }
45711
45963
  const response = await logsApi.getAgentLogs(agentId, options.limit || 20, options.page || 1, filters);
45712
45964
  if (!response.success) {
45713
- console.error(`\u274C Error: ${response.error?.message || "Unknown error"}`);
45714
- throw new Error(`Error: ${response.error?.message || "Unknown error"}`);
45965
+ throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Unknown error");
45715
45966
  }
45716
45967
  const data = response.data;
45717
45968
  if (options.json) {
@@ -45923,8 +46174,7 @@ async function viewAgentLogsInteractive(logsApi, agentId, filters = {}) {
45923
46174
  while (keepViewing) {
45924
46175
  const response = await logsApi.getAgentLogs(agentId, limit, currentPage, filters);
45925
46176
  if (!response.success) {
45926
- writeError(`\u274C Error: ${response.error?.message || "Unknown error"}`);
45927
- throw new Error("Operation failed");
46177
+ throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Unknown error");
45928
46178
  }
45929
46179
  const data = response.data;
45930
46180
  displayLogsCore(data.logs, data.pagination, "All Agent Logs", true);
@@ -46300,6 +46550,7 @@ __name(localVersionLine, "localVersionLine");
46300
46550
  init_skills_api_service();
46301
46551
  init_skill_handler();
46302
46552
  init_analytics();
46553
+ init_cli_error();
46303
46554
  async function skillsCommand(actionOrEnv, actionArg, cmdObj) {
46304
46555
  return withErrorHandling(async () => {
46305
46556
  const options = {
@@ -46580,10 +46831,7 @@ async function executeNonInteractive4(context, config, action, options) {
46580
46831
  return;
46581
46832
  }
46582
46833
  if (!options.skillName) {
46583
- console.error(`\u274C --skill-name is required for action "${action}"`);
46584
- console.log(`
46585
- Usage: lua skills ${action} --skill-name <name>`);
46586
- throw new Error(`--skill-name is required for action "${action}"`);
46834
+ throw CliError.usage(`--skill-name is required for action "${action}"`, `Usage: lua skills ${action} --skill-name <name>`);
46587
46835
  }
46588
46836
  let selectedSkill = skills.find((s) => s.skillId === options.skillName || s.name === options.skillName);
46589
46837
  if (!selectedSkill && action === "delete") {
@@ -46602,13 +46850,7 @@ Usage: lua skills ${action} --skill-name <name>`);
46602
46850
  }
46603
46851
  }
46604
46852
  if (!selectedSkill) {
46605
- console.error(`\u274C Skill "${options.skillName}" not found`);
46606
- console.log("\nAvailable skills in local config:");
46607
- skills.forEach((s) => console.log(` - ${s.name} (${s.skillId})`));
46608
- if (action === "delete") {
46609
- console.log("\n\u{1F4A1} Tip: The skill may have already been deleted from the server.");
46610
- }
46611
- throw new Error(`Skill "${options.skillName}" not found`);
46853
+ throw CliError.notFound(`Skill "${options.skillName}" not found`, joinHint(listHint("Available skills in local config:", skills.map((s) => `${s.name} (${s.skillId})`)), action === "delete" && "Tip: The skill may have already been deleted from the server."));
46612
46854
  }
46613
46855
  switch (action) {
46614
46856
  case "versions": {
@@ -46624,17 +46866,12 @@ Usage: lua skills ${action} --skill-name <name>`);
46624
46866
  }
46625
46867
  case "deploy": {
46626
46868
  if (!options.skillVersion) {
46627
- console.error("\u274C --skill-version is required for deploy action");
46628
- console.log("\nUsage: lua skills deploy --skill-name mySkill --skill-version 1.0.3");
46629
- console.log(" lua skills deploy -i mySkill -v latest");
46630
- throw new Error("--skill-version is required for deploy action");
46869
+ throw CliError.usage("--skill-version is required for deploy action", "Usage: lua skills deploy --skill-name mySkill --skill-version 1.0.3\n lua skills deploy -i mySkill -v latest");
46631
46870
  }
46632
46871
  const data = await fetchVersionsCore2(context, selectedSkill);
46633
46872
  if (!data) throw new Error("Failed to fetch skill versions");
46634
46873
  if (data.versions.length === 0) {
46635
- console.error(`\u274C No versions found for ${selectedSkill.name}.`);
46636
- console.log("\u{1F4A1} Push a version first using 'lua push skill'.");
46637
- throw new Error(`No versions found for ${selectedSkill.name}`);
46874
+ throw CliError.notFound(`No versions found for ${selectedSkill.name}.`, "Push a version first using 'lua push skill'.");
46638
46875
  }
46639
46876
  const resolvedVersion = resolveVersion(data.versions, options.skillVersion);
46640
46877
  if (!resolvedVersion) throw new Error("Failed to resolve skill version");
@@ -47727,6 +47964,7 @@ init_command_utils();
47727
47964
  init_webhook_api_service();
47728
47965
  init_analytics();
47729
47966
  init_dist();
47967
+ init_cli_error();
47730
47968
  var AVAILABLE_EVENT_TYPES = Object.values(EventType).filter((event) => event.startsWith("message."));
47731
47969
  async function webhooksCommand(action, cmdObj) {
47732
47970
  return withErrorHandling(async () => {
@@ -48055,10 +48293,7 @@ async function executeNonInteractive5(context, config, action, options) {
48055
48293
  return;
48056
48294
  }
48057
48295
  if (!options.webhookName) {
48058
- console.error(`\u274C --webhook-name is required for action "${normalizedAction}"`);
48059
- console.log(`
48060
- Usage: lua webhooks ${normalizedAction} --webhook-name <name>`);
48061
- throw new Error(`--webhook-name is required for action "${normalizedAction}"`);
48296
+ throw CliError.usage(`--webhook-name is required for action "${normalizedAction}"`, `Usage: lua webhooks ${normalizedAction} --webhook-name <name>`);
48062
48297
  }
48063
48298
  let selectedWebhook = webhooks.find((w) => w.webhookId === options.webhookName || w.name === options.webhookName);
48064
48299
  if (!selectedWebhook && normalizedAction === "delete") {
@@ -48077,13 +48312,7 @@ Usage: lua webhooks ${normalizedAction} --webhook-name <name>`);
48077
48312
  }
48078
48313
  }
48079
48314
  if (!selectedWebhook) {
48080
- console.error(`\u274C Webhook "${options.webhookName}" not found`);
48081
- console.log("\nAvailable webhooks in local config:");
48082
- webhooks.forEach((w) => console.log(` - ${w.name} (${w.webhookId})`));
48083
- if (normalizedAction === "delete") {
48084
- console.log("\n\u{1F4A1} Tip: The webhook may have already been deleted from the server.");
48085
- }
48086
- throw new Error(`Webhook "${options.webhookName}" not found`);
48315
+ throw CliError.notFound(`Webhook "${options.webhookName}" not found`, joinHint(listHint("Available webhooks in local config:", webhooks.map((w) => `${w.name} (${w.webhookId})`)), normalizedAction === "delete" && "Tip: The webhook may have already been deleted from the server."));
48087
48316
  }
48088
48317
  switch (normalizedAction) {
48089
48318
  case "versions": {
@@ -48099,17 +48328,12 @@ Usage: lua webhooks ${normalizedAction} --webhook-name <name>`);
48099
48328
  }
48100
48329
  case "deploy": {
48101
48330
  if (!options.webhookVersion) {
48102
- console.error("\u274C --webhook-version is required for deploy action");
48103
- console.log("\nUsage: lua webhooks deploy --webhook-name myWebhook --webhook-version 1.0.3");
48104
- console.log(" lua webhooks deploy -i myWebhook -v latest");
48105
- throw new Error("--webhook-version is required for deploy action");
48331
+ throw CliError.usage("--webhook-version is required for deploy action", "Usage: lua webhooks deploy --webhook-name myWebhook --webhook-version 1.0.3\n lua webhooks deploy -i myWebhook -v latest");
48106
48332
  }
48107
48333
  const data = await fetchVersionsCore3(context, selectedWebhook);
48108
48334
  if (!data) throw new Error("Failed to fetch webhook versions");
48109
48335
  if (data.versions.length === 0) {
48110
- console.error(`\u274C No versions found for ${selectedWebhook.name}.`);
48111
- console.log("\u{1F4A1} Push a version first using 'lua push webhook'.");
48112
- throw new Error(`No versions found for ${selectedWebhook.name}`);
48336
+ throw CliError.notFound(`No versions found for ${selectedWebhook.name}.`, "Push a version first using 'lua push webhook'.");
48113
48337
  }
48114
48338
  const resolvedVersion = resolveVersion2(data.versions, options.webhookVersion);
48115
48339
  if (!resolvedVersion) throw new Error("Failed to resolve webhook version");
@@ -48134,11 +48358,7 @@ Usage: lua webhooks ${normalizedAction} --webhook-name <name>`);
48134
48358
  }
48135
48359
  case "subscribe": {
48136
48360
  if (!options.event) {
48137
- console.error("\u274C --event is required for subscribe action");
48138
- console.log("\nUsage: lua webhooks subscribe --webhook-name myWebhook --event message.delivered");
48139
- console.log("\nAvailable events:");
48140
- AVAILABLE_EVENT_TYPES.forEach((e) => console.log(` \u2022 ${e}`));
48141
- throw new Error("--event is required for subscribe action");
48361
+ throw CliError.usage("--event is required for subscribe action", joinHint("Usage: lua webhooks subscribe --webhook-name myWebhook --event message.delivered", listHint("Available events:", AVAILABLE_EVENT_TYPES)));
48142
48362
  }
48143
48363
  const success2 = await subscribeCore(context, selectedWebhook.webhookId, selectedWebhook.name, options.event);
48144
48364
  if (!success2) throw new Error("Failed to subscribe webhook to event");
@@ -48146,9 +48366,7 @@ Usage: lua webhooks ${normalizedAction} --webhook-name <name>`);
48146
48366
  }
48147
48367
  case "unsubscribe": {
48148
48368
  if (!options.event) {
48149
- console.error("\u274C --event is required for unsubscribe action");
48150
- console.log("\nUsage: lua webhooks unsubscribe --webhook-name myWebhook --event message.delivered");
48151
- throw new Error("--event is required for unsubscribe action");
48369
+ throw CliError.usage("--event is required for unsubscribe action", "Usage: lua webhooks unsubscribe --webhook-name myWebhook --event message.delivered");
48152
48370
  }
48153
48371
  const success2 = await unsubscribeCore(context, selectedWebhook.webhookId, selectedWebhook.name, options.event);
48154
48372
  if (!success2) throw new Error("Failed to unsubscribe webhook from event");
@@ -48483,6 +48701,7 @@ init_cli();
48483
48701
  init_constants();
48484
48702
  init_command_utils();
48485
48703
  init_analytics();
48704
+ init_cli_error();
48486
48705
  var ACCEPTED_TIMEOUT_MS = 12e4;
48487
48706
  var DEFAULT_LOGS_LIMIT = 20;
48488
48707
  function parseLimitOption(raw) {
@@ -48866,19 +49085,14 @@ async function executeNonInteractive6(context, action, options) {
48866
49085
  }
48867
49086
  if (normalizedAction === "create") {
48868
49087
  if (!options.name) {
48869
- console.error("\u274C --name is required for create action");
48870
- console.log('\nUsage: lua triggers create --name order-created [--description "Fires on new orders"] [--instruction "Reply with the current date and time"]');
48871
- throw new Error("--name is required for create action");
49088
+ throw CliError.usage("--name is required for create action", 'Usage: lua triggers create --name order-created [--description "Fires on new orders"] [--instruction "Reply with the current date and time"]');
48872
49089
  }
48873
49090
  const success2 = await createTriggerCore(context, options.name, options.description, options.instruction);
48874
49091
  if (!success2) throw new Error("Failed to create trigger");
48875
49092
  return;
48876
49093
  }
48877
49094
  if (!options.trigger) {
48878
- console.error(`\u274C --trigger is required for action "${normalizedAction}"`);
48879
- console.log(`
48880
- Usage: lua triggers ${normalizedAction} --trigger <name|id>`);
48881
- throw new Error(`--trigger is required for action "${normalizedAction}"`);
49095
+ throw CliError.usage(`--trigger is required for action "${normalizedAction}"`, `Usage: lua triggers ${normalizedAction} --trigger <name|id>`);
48882
49096
  }
48883
49097
  const triggers = await fetchTriggersCore(context);
48884
49098
  if (!triggers) throw new Error("Failed to fetch triggers");
@@ -49461,6 +49675,7 @@ init_constants();
49461
49675
  init_command_utils();
49462
49676
  init_job_api_service();
49463
49677
  init_analytics();
49678
+ init_cli_error();
49464
49679
  async function jobsCommand(action, cmdObj) {
49465
49680
  return withErrorHandling(async () => {
49466
49681
  const options = {
@@ -49783,10 +49998,7 @@ async function executeNonInteractive7(context, config, action, options) {
49783
49998
  return;
49784
49999
  }
49785
50000
  if (!options.jobName) {
49786
- console.error(`\u274C --job-name is required for action "${normalizedAction}"`);
49787
- console.log(`
49788
- Usage: lua jobs ${normalizedAction} --job-name <name>`);
49789
- throw new Error(`--job-name is required for action "${normalizedAction}"`);
50001
+ throw CliError.usage(`--job-name is required for action "${normalizedAction}"`, `Usage: lua jobs ${normalizedAction} --job-name <name>`);
49790
50002
  }
49791
50003
  let selectedJob = jobs.find((j) => j.jobId === options.jobName || j.name === options.jobName);
49792
50004
  if (!selectedJob && (normalizedAction === "delete" || normalizedAction === "trigger")) {
@@ -49805,13 +50017,7 @@ Usage: lua jobs ${normalizedAction} --job-name <name>`);
49805
50017
  }
49806
50018
  }
49807
50019
  if (!selectedJob) {
49808
- console.error(`\u274C Job "${options.jobName}" not found`);
49809
- console.log("\nAvailable jobs in local config:");
49810
- jobs.forEach((j) => console.log(` - ${j.name} (${j.jobId})`));
49811
- if (normalizedAction === "delete" || normalizedAction === "trigger") {
49812
- console.log("\n\u{1F4A1} Tip: The job may have already been deleted from the server.");
49813
- }
49814
- throw new Error(`Job "${options.jobName}" not found`);
50020
+ throw CliError.notFound(`Job "${options.jobName}" not found`, joinHint(listHint("Available jobs in local config:", jobs.map((j) => `${j.name} (${j.jobId})`)), (normalizedAction === "delete" || normalizedAction === "trigger") && "Tip: The job may have already been deleted from the server."));
49815
50021
  }
49816
50022
  switch (normalizedAction) {
49817
50023
  case "versions": {
@@ -49827,17 +50033,12 @@ Usage: lua jobs ${normalizedAction} --job-name <name>`);
49827
50033
  }
49828
50034
  case "deploy": {
49829
50035
  if (!options.jobVersion) {
49830
- console.error("\u274C --job-version is required for deploy action");
49831
- console.log("\nUsage: lua jobs deploy --job-name myJob --job-version 1.0.3");
49832
- console.log(" lua jobs deploy -i myJob -v latest");
49833
- throw new Error("--job-version is required for deploy action");
50036
+ throw CliError.usage("--job-version is required for deploy action", "Usage: lua jobs deploy --job-name myJob --job-version 1.0.3\n lua jobs deploy -i myJob -v latest");
49834
50037
  }
49835
50038
  const dData = await fetchVersionsCore4(context, selectedJob);
49836
50039
  if (!dData) throw new Error("Failed to fetch job versions");
49837
50040
  if (dData.versions.length === 0) {
49838
- console.error(`\u274C No versions found for ${selectedJob.name}.`);
49839
- console.log("\u{1F4A1} Push a version first using 'lua push job'.");
49840
- throw new Error(`No versions found for ${selectedJob.name}`);
50041
+ throw CliError.notFound(`No versions found for ${selectedJob.name}.`, "Push a version first using 'lua push job'.");
49841
50042
  }
49842
50043
  const resolvedVersion = resolveVersion3(dData.versions, options.jobVersion);
49843
50044
  if (!resolvedVersion) throw new Error("Failed to resolve job version");
@@ -50408,9 +50609,18 @@ async function executeAction(ctx, action, target, extra, o) {
50408
50609
  // ── retry-step ──
50409
50610
  case "retry-step":
50410
50611
  return requireRun(runId, "retry-step", () => retryStepCore(ctx, runId, o));
50612
+ // ── resolve-step (LUA-752 — R37; L2.6) ──
50613
+ case "resolve-step":
50614
+ return requireRun(runId, "resolve-step", () => resolveStepCore(ctx, runId, o));
50615
+ // ── raise-budget (LUA-752 — R45; the verb every budget-park hint named) ──
50616
+ case "raise-budget":
50617
+ return requireRun(runId, "raise-budget", () => raiseBudgetCore(ctx, runId, o));
50411
50618
  // ── approve ──
50412
50619
  case "approve":
50413
50620
  return requireRun(runId, "approve", () => approveCore(ctx, runId, o));
50621
+ // ── approval-payload (LUA-752 — R39; the fingerprint `approve --edit` needs) ──
50622
+ case "approval-payload":
50623
+ return requireRun(runId, "approval-payload", () => approvalPayloadCore(ctx, runId, o));
50414
50624
  // ── signal ──
50415
50625
  case "signal":
50416
50626
  return requireRun(runId, "signal", () => signalCore(ctx, runId, extra, o));
@@ -50429,6 +50639,9 @@ async function executeAction(ctx, action, target, extra, o) {
50429
50639
  // ── env-overlay (Cluster K, B33 — WF-543; R69) ──
50430
50640
  case "env-overlay":
50431
50641
  return requireName(name, "env-overlay", () => envOverlayCore(ctx, name, o));
50642
+ // ── export (LUA-752 — WF-403; L6.3): the version back as pushable files ──
50643
+ case "export":
50644
+ return requireName(name, "export", () => exportCore(ctx, name, o));
50432
50645
  // ── archive-runs (WF-443 retention hop — client half; R2 → R50 → R51) ──
50433
50646
  case "archive-runs":
50434
50647
  return archiveRunsCore(ctx, o);
@@ -50442,7 +50655,7 @@ async function executeAction(ctx, action, target, extra, o) {
50442
50655
  // ── goals <list|get|create|pause|resume|close> (LUA-627 — R57–R62) ──
50443
50656
  case "goals":
50444
50657
  return goalsCore(ctx, target, extra, o);
50445
- // ── schedules <list|delete> (LUA-627 R4-MF-2 reads, R28 delete; goal-owned rows refuse) ──
50658
+ // ── schedules <list|create|patch|pause|resume|delete> (LUA-627 reads + R28; LUA-752 R27 / R56; goal-owned rows refuse) ──
50446
50659
  case "schedules":
50447
50660
  return schedulesCore(ctx, target, extra, o);
50448
50661
  case "run":
@@ -50475,12 +50688,12 @@ function apiFailure(ctx, res, verb) {
50475
50688
  const err = res.error;
50476
50689
  const status = err?.statusCode;
50477
50690
  const code = err?.code ?? err?.error;
50478
- if (!ctx.json) {
50479
- if (status === 503 && code === "CONTROL_UNAVAILABLE") console.error(`\u274C ${verb}: ${CONTROL_UNAVAILABLE_HINT}`);
50480
- else console.error(`\u274C ${verb} failed${code ? ` (${code})` : ""}: ${err?.message ?? "Unknown 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);
50481
50694
  }
50695
+ if (!ctx.json) console.error(`\u274C ${message}`);
50482
50696
  if (status === 404) return WORKFLOW_EXIT.NOT_FOUND;
50483
- if (status === 0 || status !== void 0 && status >= 500) return WORKFLOW_EXIT.UNAVAILABLE;
50484
50697
  return WORKFLOW_EXIT.API;
50485
50698
  }
50486
50699
  __name(apiFailure, "apiFailure");
@@ -50716,7 +50929,9 @@ async function startCore(ctx, name, o) {
50716
50929
  try {
50717
50930
  if (o.input) input = parseJsonOrFile(o.input, "--input");
50718
50931
  waitSeconds = parseIntFlag(o.wait, "--wait");
50719
- budgetCredits = parseIntFlag(o.budgetCredits, "--budget-credits");
50932
+ budgetCredits = parseIntegerFlag(o.budgetCredits, "--budget-credits", {
50933
+ min: 1
50934
+ });
50720
50935
  } catch (e) {
50721
50936
  if (e instanceof WorkflowLocalUsageError) {
50722
50937
  console.error(`\u274C ${e.message}`);
@@ -50939,7 +51154,8 @@ function printRun(run, withSteps) {
50939
51154
  if (kind === "exception") {
50940
51155
  console.log(run.status === "running" ? "\n\u26A0\uFE0F Needs a decision \u2014 a step is parked on an exception gate (the run continues past it):" : "\n\u26A0\uFE0F Needs a decision \u2014 the run is parked on an exception gate:");
50941
51156
  console.log(` lua workflows retry-step ${id} --step <id>`);
50942
- console.log(" To skip, complete or fail the step, or start a repair run, decide it from the desktop run page.");
51157
+ console.log(` lua workflows resolve-step ${id} --step <id> --outcome skip|complete|fail [--output <json|@file>]`);
51158
+ console.log(" To start a repair run instead, decide it from the desktop run page.");
50943
51159
  } else if (kind === "budget") {
50944
51160
  console.log(`
50945
51161
  \u23F8\uFE0F Paused \u2014 run budget reached (${budgetCopy(run)}):`);
@@ -51257,7 +51473,8 @@ async function cancelCore(ctx, runId, o) {
51257
51473
  emitJson(ctx, res);
51258
51474
  if (!ctx.json) {
51259
51475
  const v = res.data;
51260
- if (v.state === "terminal" || v.cancelRequested === false && v.transitioned === false && TERMINAL2.has(v.status)) {
51476
+ const alreadyTerminal = v.transitioned !== true && (v.state === "terminal" || v.state === "abandoned" || TERMINAL2.has(v.status));
51477
+ if (alreadyTerminal) {
51261
51478
  writeInfo(`\u2139\uFE0F run ${runId} was already ${v.status} \u2014 nothing to cancel`);
51262
51479
  return WORKFLOW_EXIT.OK;
51263
51480
  }
@@ -51337,6 +51554,13 @@ async function retryStepCore(ctx, runId, o) {
51337
51554
  } else if (res.error?.statusCode === 409 && !ctx.json) {
51338
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`);
51339
51556
  else if (code === "RUN_TERMINAL" || code === "run_terminal") console.error(`\u274C run ${runId} is terminal \u2014 start a new run instead`);
51557
+ else if (code === "STEP_RETRY_CAP" || code === "step_retry_cap") {
51558
+ const cap = res.error;
51559
+ 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`);
51563
+ }
51340
51564
  }
51341
51565
  return apiFailure(ctx, res, "retry-step");
51342
51566
  }
@@ -51354,6 +51578,165 @@ async function retryStepCore(ctx, runId, o) {
51354
51578
  return WORKFLOW_EXIT.OK;
51355
51579
  }
51356
51580
  __name(retryStepCore, "retryStepCore");
51581
+ function refusalIssues(err) {
51582
+ return err?.issues ?? [];
51583
+ }
51584
+ __name(refusalIssues, "refusalIssues");
51585
+ async function resolveStepCore(ctx, runId, o) {
51586
+ const usage = "lua workflows resolve-step <runId> --step <id> --outcome skip|complete|fail [--output <json|@file>] [--note <text>]";
51587
+ if (!o.step) {
51588
+ console.error(`\u274C --step <id> is required: ${usage}`);
51589
+ return WORKFLOW_EXIT.USAGE;
51590
+ }
51591
+ const raw = (o.outcome ?? o.action)?.trim().toLowerCase();
51592
+ 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;
51595
+ }
51596
+ const outcome = raw;
51597
+ let output;
51598
+ try {
51599
+ if (o.output !== void 0) output = parseJsonOrFile(o.output, "--output");
51600
+ } catch (e) {
51601
+ console.error(`\u274C ${e.message}`);
51602
+ return WORKFLOW_EXIT.USAGE;
51603
+ }
51604
+ 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;
51607
+ }
51608
+ 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;
51611
+ }
51612
+ const dto = {
51613
+ outcome,
51614
+ ...output !== void 0 ? {
51615
+ output
51616
+ } : {},
51617
+ ...o.note ? {
51618
+ note: o.note
51619
+ } : {}
51620
+ };
51621
+ const res = await ctx.api.resolveStep(runId, o.step, dto);
51622
+ if (!res.success || !res.data) {
51623
+ const err = res.error;
51624
+ const code = err?.code ?? err?.error;
51625
+ const status = err?.statusCode;
51626
+ if (!ctx.json && err) {
51627
+ 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`);
51629
+ } 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`);
51631
+ } else if (status === 409 && (code === "RUN_TERMINAL" || code === "run_terminal")) {
51632
+ console.error(`\u274C run ${runId} is terminal \u2014 start a new run instead`);
51633
+ } 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}`);
51636
+ } 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");
51638
+ } else if (status === 413 && code === "OUTPUT_TOO_LARGE") {
51639
+ const { bytes, maxBytes } = err;
51640
+ console.error(`\u274C --output is over the cap (${bytes ?? "?"} of ${maxBytes ?? 262144} bytes serialized)`);
51641
+ }
51642
+ }
51643
+ return apiFailure(ctx, res, "resolve-step");
51644
+ }
51645
+ emitJson(ctx, res);
51646
+ if (!ctx.json) {
51647
+ const d = res.data;
51648
+ if (d.resolved) {
51649
+ const past = d.outcome === "complete" ? "completed" : d.outcome === "skip" ? "skipped" : "failed";
51650
+ writeSuccess(`\u2705 ${past} ${o.step} \xB7 run is ${d.runStatus}` + (d.remainingParks ? ` \xB7 ${d.remainingParks} other parked step(s)` : ""));
51651
+ } else {
51652
+ const standing = d.reason === "already_retried" ? "retried" : `resolved${d.recorded?.outcome ? ` (${d.recorded.outcome})` : ""}`;
51653
+ const by = actorLabel(d.recorded?.by);
51654
+ writeInfo(`\u2139\uFE0F step "${o.step}" was already ${standing}` + (by ? ` by ${by}` : "") + (d.recorded?.at ? ` at ${when(d.recorded.at)}` : "") + ` \xB7 run is ${d.runStatus}`);
51655
+ }
51656
+ }
51657
+ return WORKFLOW_EXIT.OK;
51658
+ }
51659
+ __name(resolveStepCore, "resolveStepCore");
51660
+ function budgetCapFlag(cap) {
51661
+ if (cap === "maxSteps") return "--max-steps";
51662
+ if (cap === "maxJobSeconds") return "--max-job-seconds";
51663
+ if (cap === "maxDurationSeconds") return "--max-duration-seconds";
51664
+ return "--credits";
51665
+ }
51666
+ __name(budgetCapFlag, "budgetCapFlag");
51667
+ function budgetCapsLabel(b) {
51668
+ if (!b) return "";
51669
+ const parts = [];
51670
+ if (b.maxCredits !== void 0) parts.push(`${b.maxCredits} credits`);
51671
+ if (b.maxSteps !== void 0) parts.push(`${b.maxSteps} steps`);
51672
+ if (b.maxJobSeconds !== void 0) parts.push(`${b.maxJobSeconds}s job`);
51673
+ if (b.maxDurationSeconds !== void 0) parts.push(`${b.maxDurationSeconds}s wall`);
51674
+ return parts.join(" \xB7 ");
51675
+ }
51676
+ __name(budgetCapsLabel, "budgetCapsLabel");
51677
+ async function raiseBudgetCore(ctx, runId, o) {
51678
+ let dto;
51679
+ try {
51680
+ dto = {};
51681
+ const credits = parseIntegerFlag(o.credits, "--credits", {
51682
+ min: 1
51683
+ });
51684
+ if (credits !== void 0) dto.maxCredits = credits;
51685
+ const steps = parseIntegerFlag(o.maxSteps, "--max-steps", {
51686
+ min: 1
51687
+ });
51688
+ if (steps !== void 0) dto.maxSteps = steps;
51689
+ const jobSeconds = parseIntegerFlag(o.maxJobSeconds, "--max-job-seconds", {
51690
+ min: 1
51691
+ });
51692
+ if (jobSeconds !== void 0) dto.maxJobSeconds = jobSeconds;
51693
+ const duration = parseIntegerFlag(o.maxDurationSeconds, "--max-duration-seconds", {
51694
+ min: 1
51695
+ });
51696
+ if (duration !== void 0) dto.maxDurationSeconds = duration;
51697
+ if (Object.keys(dto).length === 0) throw new WorkflowLocalUsageError("usage", `--credits <n> (or --max-steps / --max-job-seconds / --max-duration-seconds) is required \u2014 the new cap, above the current one (lua workflows status ${runId} shows it)`);
51698
+ if (o.note) dto.note = o.note;
51699
+ } catch (e) {
51700
+ if (e instanceof WorkflowLocalUsageError) throw CliError.usage(`raise-budget: ${e.message}`);
51701
+ throw e;
51702
+ }
51703
+ const res = await ctx.api.raiseBudget(runId, dto);
51704
+ if (!res.success || !res.data) {
51705
+ const err = res.error;
51706
+ const code = err?.code ?? err?.error;
51707
+ const status = err?.statusCode;
51708
+ if (!ctx.json && err) {
51709
+ if (status === 400 && code === "CAP_EXCEEDED") {
51710
+ 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`);
51712
+ } 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`);
51715
+ } else if (status === 409 && code === "BUDGET_NOT_RAISABLE") {
51716
+ 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`);
51718
+ } 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");
51720
+ }
51721
+ }
51722
+ return apiFailure(ctx, res, "raise-budget");
51723
+ }
51724
+ emitJson(ctx, res);
51725
+ if (!ctx.json) {
51726
+ const d = res.data;
51727
+ const caps = budgetCapsLabel(d.budget ?? d.to);
51728
+ if (d.raised) {
51729
+ const delta = d.from && d.to ? ` (${budgetCapsLabel(d.from)} \u2192 ${budgetCapsLabel(d.to)})` : caps ? ` to ${caps}` : "";
51730
+ writeSuccess(`\u2705 budget raised${delta}${d.runStatus ? ` \xB7 run is ${d.runStatus}` : ""}`);
51731
+ if (d.resumed === false) writeInfo(`\u2139\uFE0F the run did not resume on this raise \u2014 the next step's reserve is still short (raise again), or nothing was parked: lua workflows status ${runId}`);
51732
+ else if (d.resumed) writeInfo(`\u{1F4A1} Follow it: lua workflows watch ${runId}`);
51733
+ } else {
51734
+ writeInfo(`\u2139\uFE0F budget unchanged (${d.reason ?? "already_raised"})${caps ? ` \xB7 ${caps}` : ""}${d.runStatus ? ` \xB7 run is ${d.runStatus}` : ""}`);
51735
+ }
51736
+ }
51737
+ return WORKFLOW_EXIT.OK;
51738
+ }
51739
+ __name(raiseBudgetCore, "raiseBudgetCore");
51357
51740
  function approveResultLine(d) {
51358
51741
  if (d.resolved) return `\u2705 ${d.outcome} \xB7 run is ${d.runStatus}`;
51359
51742
  const ref = d.decidedBy;
@@ -51407,6 +51790,73 @@ async function approveCore(ctx, runId, o) {
51407
51790
  return WORKFLOW_EXIT.OK;
51408
51791
  }
51409
51792
  __name(approveCore, "approveCore");
51793
+ var indentBlock = /* @__PURE__ */ __name((text, pad2 = " ") => text.split("\n").map((l) => `${pad2}${l}`).join("\n"), "indentBlock");
51794
+ var APPROVAL_PAYLOAD_PAGE_MAX = 100;
51795
+ async function approvalPayloadCore(ctx, runId, o) {
51796
+ 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;
51799
+ }
51800
+ let limit;
51801
+ try {
51802
+ limit = parseIntegerFlag(o.limit, "--limit", {
51803
+ min: 1,
51804
+ max: APPROVAL_PAYLOAD_PAGE_MAX
51805
+ });
51806
+ } catch (e) {
51807
+ if (e instanceof WorkflowLocalUsageError) throw CliError.usage(`approval-payload: ${e.message}`);
51808
+ throw e;
51809
+ }
51810
+ 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;
51813
+ }
51814
+ const res = await ctx.api.getApprovalPayload(runId, o.approval, {
51815
+ path: o.path,
51816
+ cursor: o.cursor,
51817
+ limit
51818
+ });
51819
+ if (!res.success || !res.data) {
51820
+ 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>]");
51825
+ }
51826
+ return apiFailure(ctx, res, "approval-payload");
51827
+ }
51828
+ emitJson(ctx, res);
51829
+ if (!ctx.json) {
51830
+ const v = res.data;
51831
+ console.log(`
51832
+ \u{1F9FE} Approval ${v.approvalId} \xB7 revision ${v.editRevision} \xB7 fingerprint ${v.payloadFingerprint ?? "(none)"}`);
51833
+ console.log(` Editable: ${v.editable ? `yes \u2014 ${v.editablePaths.length ? v.editablePaths.join(", ") : "any path"}` : "no"}${v.size !== void 0 ? ` \xB7 ${v.size} bytes` : ""}`);
51834
+ if (v.kind === "whole") {
51835
+ console.log(` Payload:
51836
+ ${indentBlock(JSON.stringify(v.payload, null, 2))}`);
51837
+ } else if (v.kind === "paged") {
51838
+ console.log(" Payload: paged \u2014 read one array at a time with --path <array.path>:");
51839
+ for (const [p, a] of Object.entries(v.arrays)) console.log(` ${p} (${a.totalItems} items)`);
51840
+ } else {
51841
+ console.log(` ${v.path}: ${v.items.length} of ${v.totalItems} item(s)${v.nextCursor ? ` \xB7 next page: --cursor ${v.nextCursor}` : ""}`);
51842
+ for (const it of v.items) console.log(` [${it.index}] ${JSON.stringify(it.value)}`);
51843
+ }
51844
+ if (v.editable && v.payloadFingerprint) {
51845
+ writeHintBlock({
51846
+ headline: "Edit and approve against this revision:",
51847
+ lines: [
51848
+ {
51849
+ label: "Approve:",
51850
+ command: `lua workflows approve ${runId} --approval ${v.approvalId} --edit @edited.json --fingerprint ${v.payloadFingerprint}`
51851
+ }
51852
+ ],
51853
+ when: "success"
51854
+ });
51855
+ }
51856
+ }
51857
+ return WORKFLOW_EXIT.OK;
51858
+ }
51859
+ __name(approvalPayloadCore, "approvalPayloadCore");
51410
51860
  function actorLabel(by, now = Date.now()) {
51411
51861
  if (by === void 0 || by === null) return void 0;
51412
51862
  if (typeof by === "string") return by;
@@ -51790,6 +52240,51 @@ async function envOverlayCore(ctx, name, o) {
51790
52240
  return missing.length > 0 ? WORKFLOW_EXIT.API : WORKFLOW_EXIT.OK;
51791
52241
  }
51792
52242
  __name(envOverlayCore, "envOverlayCore");
52243
+ async function exportCore(ctx, name, o) {
52244
+ const wf = await resolveWorkflow(ctx, name);
52245
+ if (!wf) return WORKFLOW_EXIT.NOT_FOUND;
52246
+ let version;
52247
+ if (o.workflowVersion) {
52248
+ const ref = await resolveVersionRef(ctx, wf, o.workflowVersion, "export");
52249
+ if ("exit" in ref) return ref.exit;
52250
+ version = ref.version.id;
52251
+ }
52252
+ const res = await ctx.api.exportWorkflowFiles(wf.id, version);
52253
+ if (!res.success || !res.data) {
52254
+ const code = res.error?.code ?? res.error?.error;
52255
+ if (!ctx.json && res.error?.statusCode === 404 && code === "VERSION_NOT_FOUND") console.error(`\u274C "${wf.name}" has no active version to export \u2014 pass -v <version|latest>`);
52256
+ return apiFailure(ctx, res, "export");
52257
+ }
52258
+ emitJson(ctx, res);
52259
+ if (ctx.json) return WORKFLOW_EXIT.OK;
52260
+ const fs19 = await import("fs");
52261
+ const path25 = await import("path");
52262
+ const outDir = path25.resolve(o.out ?? ".");
52263
+ const d = res.data;
52264
+ const escaping = d.files.filter((f) => !path25.resolve(outDir, f.path).startsWith(outDir + path25.sep));
52265
+ if (escaping.length > 0) {
52266
+ console.error(`\u274C EXPORT_PATH_ESCAPES: the server named ${escaping.map((f) => JSON.stringify(f.path)).join(", ")} \u2014 outside --out ${outDir}; nothing written`);
52267
+ return WORKFLOW_EXIT.API;
52268
+ }
52269
+ const clash = d.files.filter((f) => fs19.existsSync(path25.join(outDir, f.path)));
52270
+ if (clash.length > 0 && !o.force) {
52271
+ console.error(`\u274C export: ${clash.map((f) => f.path).join(", ")} already exist${clash.length === 1 ? "s" : ""} under ${outDir} \u2014 pass --force to overwrite, or --out <dir>`);
52272
+ return WORKFLOW_EXIT.API;
52273
+ }
52274
+ for (const f of d.files) {
52275
+ const target = path25.join(outDir, f.path);
52276
+ fs19.mkdirSync(path25.dirname(target), {
52277
+ recursive: true
52278
+ });
52279
+ fs19.writeFileSync(target, f.contents);
52280
+ console.log(` ${f.path}`);
52281
+ }
52282
+ writeSuccess(`\u2705 exported "${wf.name}" v${d.version} (${d.form} form) \u2014 ${d.files.length} file(s) under ${outDir}`);
52283
+ for (const w of d.warnings ?? []) console.warn(`\u26A0\uFE0F ${w.code}${w.path ? ` at ${w.path}` : ""}: ${w.message}`);
52284
+ if (d.warnings?.length) writeInfo("\u{1F4A1} Review the TODOs the warnings name; `lua push workflow` then mints a static version from the file.");
52285
+ return WORKFLOW_EXIT.OK;
52286
+ }
52287
+ __name(exportCore, "exportCore");
51793
52288
  function parseSinceFlag(raw, now = Date.now()) {
51794
52289
  if (!raw) return NaN;
51795
52290
  const m = /^(\d+)([dhm])$/i.exec(raw.trim());
@@ -51802,6 +52297,9 @@ function parseSinceFlag(raw, now = Date.now()) {
51802
52297
  }
51803
52298
  __name(parseSinceFlag, "parseSinceFlag");
51804
52299
  var ARCHIVE_EXPORT_TTL_DAYS = 7;
52300
+ var ARCHIVE_TERMINAL_STATUS_FILTER = [
52301
+ ...TERMINAL2
52302
+ ].join(",");
51805
52303
  var ARCHIVE_DEFAULT_RETENTION_DAYS = 90;
51806
52304
  var archiveDownload = {
51807
52305
  fetchBytes: /* @__PURE__ */ __name(async (url) => {
@@ -51865,8 +52363,8 @@ async function archiveRunsCore(ctx, o) {
51865
52363
  let cursor;
51866
52364
  do {
51867
52365
  const res = await ctx.api.getRuns({
51868
- status: "terminal",
51869
- since: new Date(since).toISOString(),
52366
+ status: ARCHIVE_TERMINAL_STATUS_FILTER,
52367
+ since: String(since),
51870
52368
  workflowId,
51871
52369
  tag: o.tag,
51872
52370
  cursor,
@@ -52238,6 +52736,10 @@ var GOAL_VERBS = [
52238
52736
  ];
52239
52737
  var SCHEDULE_VERBS = [
52240
52738
  "list",
52739
+ "create",
52740
+ "patch",
52741
+ "pause",
52742
+ "resume",
52241
52743
  "delete"
52242
52744
  ];
52243
52745
  var GOAL_STATUSES = [
@@ -52920,11 +53422,13 @@ async function schedulesCore(ctx, target, extra, o) {
52920
53422
  const verb = subVerb("workflows.schedules.action", "schedules", target, SCHEDULE_VERBS);
52921
53423
  if (!verb) return WORKFLOW_EXIT.USAGE;
52922
53424
  if (verb === "list") return schedulesListCore(ctx, o);
53425
+ if (verb === "create") return schedulesCreateCore(ctx, o, extra);
52923
53426
  if (!extra) {
52924
- console.error("\u274C schedules delete: a schedule (job) id is required \u2014 lua workflows schedules delete <jobId>");
53427
+ console.error(`\u274C schedules ${verb}: a schedule (job) id is required \u2014 lua workflows schedules ${verb} <jobId>`);
52925
53428
  return WORKFLOW_EXIT.USAGE;
52926
53429
  }
52927
- return schedulesDeleteCore(ctx, extra, o);
53430
+ if (verb === "delete") return schedulesDeleteCore(ctx, extra, o);
53431
+ return schedulesPatchCore(ctx, extra, verb, o);
52928
53432
  }
52929
53433
  __name(schedulesCore, "schedulesCore");
52930
53434
  async function schedulesListCore(ctx, o) {
@@ -53028,12 +53532,183 @@ async function schedulesDeleteCore(ctx, jobId, o) {
53028
53532
  return WORKFLOW_EXIT.OK;
53029
53533
  }
53030
53534
  __name(schedulesDeleteCore, "schedulesDeleteCore");
53535
+ var SCHEDULE_NOTIFY = [
53536
+ "emailApp",
53537
+ "email",
53538
+ "app",
53539
+ "off"
53540
+ ];
53541
+ async function schedulesCreateCore(ctx, o, positional) {
53542
+ const target = positional ?? o.workflowName ?? o.workflow;
53543
+ 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;
53546
+ }
53547
+ let draft;
53548
+ try {
53549
+ const usage = /* @__PURE__ */ __name((m) => new WorkflowLocalUsageError("usage", m), "usage");
53550
+ if (o.every && o.cadence?.length) throw usage("--every and --cadence are alternatives \u2014 pass one");
53551
+ const schedules = o.every ? parseEveryFlag(o.every) : parseCadenceFlag(o.cadence, o.timezone);
53552
+ if (schedules.length === 0) throw usage("--cadence <cron|json|@file> (repeatable, \u2264 5) or --every <interval> is required");
53553
+ draft = {
53554
+ schedules
53555
+ };
53556
+ if (o.input) draft.input = parseJsonOrFile(o.input, "--input");
53557
+ if (o.tag?.length) {
53558
+ if (o.tag.length > 10) throw usage("--tag: at most 10");
53559
+ draft.tags = o.tag;
53560
+ }
53561
+ if (o.notify !== void 0) {
53562
+ if (!SCHEDULE_NOTIFY.includes(o.notify)) throw usage(`--notify: expected ${SCHEDULE_NOTIFY.join("|")} (got "${o.notify}")`);
53563
+ draft.notify = o.notify;
53564
+ }
53565
+ const credits = parseIntegerFlag(o.budgetCredits, "--budget-credits", {
53566
+ min: 1
53567
+ });
53568
+ if (credits !== void 0) draft.budget = {
53569
+ maxCredits: credits
53570
+ };
53571
+ const backfill = parseIntegerFlag(o.backfillOnEnable, "--backfill-on-enable", {
53572
+ min: 1
53573
+ });
53574
+ if (backfill !== void 0) draft.backfillOnEnable = {
53575
+ maxOccurrences: backfill
53576
+ };
53577
+ } catch (e) {
53578
+ if (e instanceof WorkflowLocalUsageError) throw CliError.usage(`schedules create: ${e.message}`);
53579
+ throw e;
53580
+ }
53581
+ const wf = await resolveWorkflow(ctx, target);
53582
+ if (!wf) return WORKFLOW_EXIT.NOT_FOUND;
53583
+ const dto = {
53584
+ workflowId: wf.id,
53585
+ ...draft
53586
+ };
53587
+ if (o.workflowVersion) {
53588
+ const ref = await resolveVersionRef(ctx, wf, o.workflowVersion, "schedules create");
53589
+ if ("exit" in ref) return ref.exit;
53590
+ dto.workflowVersionId = ref.version.id;
53591
+ }
53592
+ const res = await ctx.api.createSchedule(dto);
53593
+ if (!res.success || !res.data) {
53594
+ const code = res.error?.code ?? res.error?.error;
53595
+ if (!ctx.json && res.error?.statusCode === 409 && code === "SCHEDULE_CAP") console.error("\u274C a schedule takes at most 5 trigger slots (SCHEDULE_CAP) \u2014 fewer --cadence entries");
53596
+ return goalFailure(ctx, res, "schedules create", wf.name);
53597
+ }
53598
+ emitJson(ctx, res);
53599
+ if (!ctx.json) {
53600
+ const s = res.data;
53601
+ const slots = (s.schedules ?? []).map((t) => triggerLabel(t)).join(" \xB7 ") || cadenceLabel(dto.schedules);
53602
+ writeSuccess(`\u2705 schedule ${s.jobId} on "${wf.name}" \xB7 ${slots} \xB7 next ${when(s.nextRunAt ?? void 0)}${s.notify ? ` \xB7 notify ${s.notify}` : ""}`);
53603
+ writeHintBlock({
53604
+ headline: "Manage it:",
53605
+ lines: [
53606
+ {
53607
+ label: "List:",
53608
+ command: `lua workflows schedules list -i ${wf.name}`
53609
+ },
53610
+ {
53611
+ label: "Pause:",
53612
+ command: `lua workflows schedules pause ${s.jobId}`
53613
+ },
53614
+ {
53615
+ label: "Delete:",
53616
+ command: `lua workflows schedules delete ${s.jobId}`
53617
+ }
53618
+ ],
53619
+ when: "success"
53620
+ });
53621
+ }
53622
+ return WORKFLOW_EXIT.OK;
53623
+ }
53624
+ __name(schedulesCreateCore, "schedulesCreateCore");
53625
+ async function scheduleGoalOwner(ctx, row2) {
53626
+ if (row2.goalId) return {
53627
+ goalId: row2.goalId
53628
+ };
53629
+ const goals = await loadGoals(ctx, row2.workflowId);
53630
+ if (goals.error) return {
53631
+ error: goals.error
53632
+ };
53633
+ return {
53634
+ goalId: goals.items.find((g) => g.jobId === row2.jobId)?.goalId
53635
+ };
53636
+ }
53637
+ __name(scheduleGoalOwner, "scheduleGoalOwner");
53638
+ async function schedulesPatchCore(ctx, jobId, verb, o) {
53639
+ const label = `schedules ${verb}`;
53640
+ let dto;
53641
+ try {
53642
+ const usage = /* @__PURE__ */ __name((m) => new WorkflowLocalUsageError("usage", m), "usage");
53643
+ dto = {};
53644
+ if (verb === "pause") dto.paused = true;
53645
+ else if (verb === "resume") dto.paused = false;
53646
+ else if (o.paused !== void 0) {
53647
+ const v = String(o.paused).trim().toLowerCase();
53648
+ if (v !== "true" && v !== "false") throw usage(`--paused: expected true|false (got "${o.paused}")`);
53649
+ dto.paused = v === "true";
53650
+ }
53651
+ if (o.backfillOnEnable !== void 0) {
53652
+ if (String(o.backfillOnEnable).trim().toLowerCase() === "none") dto.backfillOnEnable = null;
53653
+ else dto.backfillOnEnable = {
53654
+ maxOccurrences: parseIntegerFlag(o.backfillOnEnable, "--backfill-on-enable", {
53655
+ min: 1
53656
+ })
53657
+ };
53658
+ }
53659
+ if (o.backfillNow) {
53660
+ if (dto.paused !== false) throw usage("--backfill-now rides a re-enable only \u2014 lua workflows schedules resume <jobId> --backfill-now (or patch --paused false)");
53661
+ dto.backfillNow = true;
53662
+ }
53663
+ if (Object.keys(dto).length === 0) throw usage("nothing to change \u2014 pass --paused true|false, --backfill-on-enable <n|none> and/or --backfill-now");
53664
+ } catch (e) {
53665
+ if (e instanceof WorkflowLocalUsageError) throw CliError.usage(`${label}: ${e.message}`);
53666
+ throw e;
53667
+ }
53668
+ const rowRes = await ctx.api.getSchedule(jobId);
53669
+ if (!rowRes.success || !rowRes.data) return apiFailure(ctx, rowRes, label);
53670
+ const owner = await scheduleGoalOwner(ctx, rowRes.data);
53671
+ 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
+ return apiFailure(ctx, {
53674
+ success: false,
53675
+ error: owner.error
53676
+ }, label);
53677
+ }
53678
+ if (owner.goalId) {
53679
+ 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.`;
53680
+ if (ctx.json) console.log(JSON.stringify({
53681
+ success: false,
53682
+ error: {
53683
+ code: "goal_schedule",
53684
+ statusCode: 409,
53685
+ message,
53686
+ jobId,
53687
+ goalId: owner.goalId
53688
+ }
53689
+ }, null, 2));
53690
+ else console.error(`\u274C goal_schedule: ${message}`);
53691
+ return WORKFLOW_EXIT.API;
53692
+ }
53693
+ const res = await ctx.api.updateSchedule(jobId, dto);
53694
+ if (!res.success || !res.data) return goalFailure(ctx, res, label, jobId);
53695
+ emitJson(ctx, res);
53696
+ if (!ctx.json) {
53697
+ const s = res.data;
53698
+ writeSuccess(`\u2705 schedule ${s.jobId} ${s.status}${s.backfillOnEnable ? ` \xB7 backfill on enable \u2264 ${s.backfillOnEnable.maxOccurrences}` : ""}`);
53699
+ if (s.backfill) writeInfo(`\u2139\uFE0F backfill on this re-enable: ${JSON.stringify(s.backfill)}`);
53700
+ if (dto.paused === false && s.status !== "active") writeInfo(`\u2139\uFE0F the schedule reads ${s.status} after the re-enable \u2014 lua workflows schedules list shows why (strikes / autoDisabled)`);
53701
+ }
53702
+ return WORKFLOW_EXIT.OK;
53703
+ }
53704
+ __name(schedulesPatchCore, "schedulesPatchCore");
53031
53705
 
53032
53706
  // src/commands/features.ts
53033
53707
  init_cli();
53034
53708
  init_constants();
53035
53709
  init_command_utils();
53036
53710
  init_analytics();
53711
+ init_cli_error();
53037
53712
  async function featuresCommand(action, cmdObj) {
53038
53713
  return withErrorHandling(async () => {
53039
53714
  const options = {
@@ -53206,21 +53881,11 @@ async function executeNonInteractive8(context, action, options) {
53206
53881
  return;
53207
53882
  }
53208
53883
  if (!options.featureName) {
53209
- console.error(`\u274C --feature-name is required for action "${normalizedAction}"`);
53210
- console.log(`
53211
- Usage: lua features ${normalizedAction} --feature-name <name>`);
53212
- if (features.length > 0) {
53213
- console.log("\nAvailable features:");
53214
- features.forEach((f) => console.log(` - ${f.name} (${f.title})`));
53215
- }
53216
- throw new Error("Operation failed");
53884
+ throw CliError.usage(`--feature-name is required for action "${normalizedAction}"`, joinHint(`Usage: lua features ${normalizedAction} --feature-name <name>`, listHint("Available features:", features.map((f) => `${f.name} (${f.title})`))));
53217
53885
  }
53218
53886
  const selectedFeature = findFeature(features, options.featureName);
53219
53887
  if (!selectedFeature) {
53220
- console.error(`\u274C Feature "${options.featureName}" not found`);
53221
- console.log("\nAvailable features:");
53222
- features.forEach((f) => console.log(` - ${f.name} (${f.title})`));
53223
- throw new Error("Feature");
53888
+ throw CliError.notFound(`Feature "${options.featureName}" not found`, listHint("Available features:", features.map((f) => `${f.name} (${f.title})`)));
53224
53889
  }
53225
53890
  switch (normalizedAction) {
53226
53891
  case "view":
@@ -53533,6 +54198,7 @@ init_cli();
53533
54198
  init_constants();
53534
54199
  init_command_utils();
53535
54200
  init_analytics();
54201
+ init_cli_error();
53536
54202
  async function preprocessorsCommand(action, cmdObj) {
53537
54203
  return withErrorHandling(async () => {
53538
54204
  const options = {
@@ -53761,10 +54427,7 @@ async function executeNonInteractive9(context, config, action, options) {
53761
54427
  return;
53762
54428
  }
53763
54429
  if (!options.preprocessorName) {
53764
- console.error(`\u274C --preprocessor-name is required for action "${normalizedAction}"`);
53765
- console.log(`
53766
- Usage: lua preprocessors ${normalizedAction} --preprocessor-name <name>`);
53767
- throw new Error(`--preprocessor-name is required for action "${normalizedAction}"`);
54430
+ throw CliError.usage(`--preprocessor-name is required for action "${normalizedAction}"`, `Usage: lua preprocessors ${normalizedAction} --preprocessor-name <name>`);
53768
54431
  }
53769
54432
  let selected = preprocessors.find((p) => p.preprocessorId === options.preprocessorName || p.name === options.preprocessorName);
53770
54433
  if (!selected && normalizedAction === "delete") {
@@ -53783,10 +54446,7 @@ Usage: lua preprocessors ${normalizedAction} --preprocessor-name <name>`);
53783
54446
  }
53784
54447
  }
53785
54448
  if (!selected) {
53786
- console.error(`\u274C PreProcessor "${options.preprocessorName}" not found`);
53787
- console.log("\nAvailable preprocessors:");
53788
- preprocessors.forEach((p) => console.log(` - ${p.name} (${p.preprocessorId})`));
53789
- throw new Error(`PreProcessor "${options.preprocessorName}" not found`);
54449
+ throw CliError.notFound(`PreProcessor "${options.preprocessorName}" not found`, listHint("Available preprocessors:", preprocessors.map((p) => `${p.name} (${p.preprocessorId})`)));
53790
54450
  }
53791
54451
  switch (normalizedAction) {
53792
54452
  case "versions": {
@@ -53802,17 +54462,12 @@ Usage: lua preprocessors ${normalizedAction} --preprocessor-name <name>`);
53802
54462
  }
53803
54463
  case "deploy": {
53804
54464
  if (!options.preprocessorVersion) {
53805
- console.error("\u274C --preprocessor-version is required for deploy action");
53806
- console.log("\nUsage: lua preprocessors deploy --preprocessor-name myPre --preprocessor-version 1.0.3");
53807
- console.log(" lua preprocessors deploy -i myPre -v latest");
53808
- throw new Error("--preprocessor-version is required for deploy action");
54465
+ throw CliError.usage("--preprocessor-version is required for deploy action", "Usage: lua preprocessors deploy --preprocessor-name myPre --preprocessor-version 1.0.3\n lua preprocessors deploy -i myPre -v latest");
53809
54466
  }
53810
54467
  const data = await fetchVersionsCore5(context, selected);
53811
54468
  if (!data) throw new Error("Failed to fetch preprocessor versions");
53812
54469
  if (data.versions.length === 0) {
53813
- console.error(`\u274C No versions found for ${selected.name}.`);
53814
- console.log("\u{1F4A1} Push a version first using 'lua push preprocessor'.");
53815
- throw new Error(`No versions found for ${selected.name}`);
54470
+ throw CliError.notFound(`No versions found for ${selected.name}.`, "Push a version first using 'lua push preprocessor'.");
53816
54471
  }
53817
54472
  const resolvedVersion = resolveVersion4(data.versions, options.preprocessorVersion);
53818
54473
  if (!resolvedVersion) throw new Error("Failed to resolve preprocessor version");
@@ -54120,6 +54775,7 @@ init_cli();
54120
54775
  init_constants();
54121
54776
  init_command_utils();
54122
54777
  init_analytics();
54778
+ init_cli_error();
54123
54779
  async function postprocessorsCommand(action, cmdObj) {
54124
54780
  return withErrorHandling(async () => {
54125
54781
  const options = {
@@ -54348,10 +55004,7 @@ async function executeNonInteractive10(context, config, action, options) {
54348
55004
  return;
54349
55005
  }
54350
55006
  if (!options.postprocessorName) {
54351
- console.error(`\u274C --postprocessor-name is required for action "${normalizedAction}"`);
54352
- console.log(`
54353
- Usage: lua postprocessors ${normalizedAction} --postprocessor-name <name>`);
54354
- throw new Error(`--postprocessor-name is required for action "${normalizedAction}"`);
55007
+ throw CliError.usage(`--postprocessor-name is required for action "${normalizedAction}"`, `Usage: lua postprocessors ${normalizedAction} --postprocessor-name <name>`);
54355
55008
  }
54356
55009
  let selected = postprocessors.find((p) => p.postprocessorId === options.postprocessorName || p.name === options.postprocessorName);
54357
55010
  if (!selected && normalizedAction === "delete") {
@@ -54370,10 +55023,7 @@ Usage: lua postprocessors ${normalizedAction} --postprocessor-name <name>`);
54370
55023
  }
54371
55024
  }
54372
55025
  if (!selected) {
54373
- console.error(`\u274C PostProcessor "${options.postprocessorName}" not found`);
54374
- console.log("\nAvailable postprocessors:");
54375
- postprocessors.forEach((p) => console.log(` - ${p.name} (${p.postprocessorId})`));
54376
- throw new Error(`PostProcessor "${options.postprocessorName}" not found`);
55026
+ throw CliError.notFound(`PostProcessor "${options.postprocessorName}" not found`, listHint("Available postprocessors:", postprocessors.map((p) => `${p.name} (${p.postprocessorId})`)));
54377
55027
  }
54378
55028
  switch (normalizedAction) {
54379
55029
  case "versions": {
@@ -54389,17 +55039,12 @@ Usage: lua postprocessors ${normalizedAction} --postprocessor-name <name>`);
54389
55039
  }
54390
55040
  case "deploy": {
54391
55041
  if (!options.postprocessorVersion) {
54392
- console.error("\u274C --postprocessor-version is required for deploy action");
54393
- console.log("\nUsage: lua postprocessors deploy --postprocessor-name myPost --postprocessor-version 1.0.3");
54394
- console.log(" lua postprocessors deploy -i myPost -v latest");
54395
- throw new Error("--postprocessor-version is required for deploy action");
55042
+ throw CliError.usage("--postprocessor-version is required for deploy action", "Usage: lua postprocessors deploy --postprocessor-name myPost --postprocessor-version 1.0.3\n lua postprocessors deploy -i myPost -v latest");
54396
55043
  }
54397
55044
  const data = await fetchVersionsCore6(context, selected);
54398
55045
  if (!data) throw new Error("Failed to fetch postprocessor versions");
54399
55046
  if (data.versions.length === 0) {
54400
- console.error(`\u274C No versions found for ${selected.name}.`);
54401
- console.log("\u{1F4A1} Push a version first using 'lua push postprocessor'.");
54402
- throw new Error(`No versions found for ${selected.name}`);
55047
+ throw CliError.notFound(`No versions found for ${selected.name}.`, "Push a version first using 'lua push postprocessor'.");
54403
55048
  }
54404
55049
  const resolvedVersion = resolveVersion5(data.versions, options.postprocessorVersion);
54405
55050
  if (!resolvedVersion) throw new Error("Failed to resolve postprocessor version");
@@ -54970,6 +55615,7 @@ var TemplateApiService = class {
54970
55615
  init_command_utils();
54971
55616
  init_analytics();
54972
55617
  init_files();
55618
+ init_cli_error();
54973
55619
  function showTemplateUsage() {
54974
55620
  console.log("\nUsage:");
54975
55621
  console.log(" lua marketplace template Interactive mode");
@@ -55344,6 +55990,9 @@ async function templateDraftAction(templateApi, options) {
55344
55990
  return;
55345
55991
  }
55346
55992
  writeSuccess("\u2705 template: section written to lua.skill.yaml");
55993
+ for (const warning of result.warnings ?? []) {
55994
+ console.log(` \u26A0 ${warning.message}`);
55995
+ }
55347
55996
  for (const [section2, diff] of Object.entries(result.diff)) {
55348
55997
  const annotations = Object.entries(diff.annotations ?? {});
55349
55998
  console.log(` ${section2}: +${diff.added.length} added (${diff.added.join(", ") || "\u2014"}), ${diff.kept.length} kept`);
@@ -55546,8 +56195,7 @@ async function templateInstallAction(templateApi, config, options) {
55546
56195
  if (envValues && Object.keys(envValues).length > 0) {
55547
56196
  writeInfo(` Env values: ${Object.keys(envValues).length} configured`);
55548
56197
  }
55549
- console.error("\n\u274C Use --force to confirm installation");
55550
- throw new Error("This action requires --force to confirm");
56198
+ throw CliError.usage("This action requires --force to confirm", "Use --force to confirm installation");
55551
56199
  }
55552
56200
  if (options.skipEnvCheck) {
55553
56201
  writeInfo("\u26A0\uFE0F --skip-env-check is deprecated: the env-contract check is server-enforced; use `lua env` to satisfy the contract.");
@@ -55849,9 +56497,7 @@ async function templateUninstallAction(templateApi, config, options) {
55849
56497
  if (!agentId) throw new Error("Agent ID not found in configuration.");
55850
56498
  const templateId = await resolveTemplateId(templateApi, options, "Which template would you like to uninstall?");
55851
56499
  if (!options.force) {
55852
- console.error(`
55853
- \u274C Use --force to confirm uninstalling template ${templateId} from this agent`);
55854
- throw new Error("This action requires --force to confirm");
56500
+ throw CliError.usage("This action requires --force to confirm", `Use --force to confirm uninstalling template ${templateId} from this agent`);
55855
56501
  }
55856
56502
  writeProgress("\u{1F504} Uninstalling template...");
55857
56503
  await templateApi.uninstall(templateId, agentId);
@@ -55860,6 +56506,7 @@ async function templateUninstallAction(templateApi, config, options) {
55860
56506
  __name(templateUninstallAction, "templateUninstallAction");
55861
56507
 
55862
56508
  // src/commands/marketplace.ts
56509
+ init_cli_error();
55863
56510
  var SKILL_ACTIONS = [
55864
56511
  "list",
55865
56512
  "publish",
@@ -56075,40 +56722,29 @@ __name(executeSkillActionInteractive, "executeSkillActionInteractive");
56075
56722
  async function listSkillNonInteractive(marketplaceApi, config, apiKey, options) {
56076
56723
  const { skillName, displayName, visibility } = options;
56077
56724
  if (!skillName || !displayName) {
56078
- console.error("\u274C Missing required options");
56079
- console.log("\nUsage: lua marketplace skill list --skill-name <name> --display-name <name>");
56080
- throw new Error("Missing required options");
56725
+ throw CliError.usage("Missing required options", "Usage: lua marketplace skill list --skill-name <name> --display-name <name>");
56081
56726
  }
56082
56727
  if (visibility && visibility !== "public" && visibility !== "private") {
56083
- console.error('\u274C Invalid --visibility: must be "public" or "private"');
56084
- throw new Error('Invalid --visibility: must be "public" or "private"');
56728
+ throw CliError.usage('Invalid --visibility: must be "public" or "private"');
56085
56729
  }
56086
56730
  const agentId = config.agent?.agentId;
56087
56731
  if (!agentId) {
56088
- console.error("\u274C Agent ID not found in configuration.");
56089
- throw new Error("Agent ID not found in configuration.");
56732
+ throw CliError.usage("Agent ID not found in configuration.", "Run `lua init` in a project directory first.");
56090
56733
  }
56091
56734
  const skillApi = new SkillApi(BASE_URLS.API, apiKey, agentId);
56092
56735
  writeProgress("\u{1F504} Verifying skill...");
56093
56736
  const agentSkillsResponse = await skillApi.getSkills();
56094
56737
  if (!agentSkillsResponse.success || !agentSkillsResponse.data) {
56095
- console.error(`\u274C Failed to fetch agent skills: ${agentSkillsResponse.message}`);
56096
- throw new Error("Failed to fetch agent skills: ${agentSkillsResponse.message}");
56738
+ throw CliError.fromStatus(agentSkillsResponse.error?.statusCode, `Failed to fetch agent skills: ${agentSkillsResponse.error?.message ?? agentSkillsResponse.message ?? "Unknown error"}`);
56097
56739
  }
56098
56740
  const skill = agentSkillsResponse.data.skills?.find((s) => s.name === skillName);
56099
56741
  if (!skill) {
56100
- console.error(`\u274C Skill not found: ${skillName}`);
56101
- writeInfo("\nAvailable skills:");
56102
- agentSkillsResponse.data.skills?.forEach((s) => {
56103
- writeInfo(` - ${s.name}`);
56104
- });
56105
- throw new Error("Skill not found: ${skillName}");
56742
+ throw CliError.notFound(`Skill not found: ${skillName}`, listHint("Available skills:", (agentSkillsResponse.data.skills ?? []).map((s) => s.name)));
56106
56743
  }
56107
56744
  const creatorSkills = await marketplaceApi.getOrgSkills();
56108
56745
  const existingSkill = creatorSkills?.find((s) => s.sourceSkillId === skill.id);
56109
56746
  if (existingSkill?.listed) {
56110
- console.error(`\u274C Skill "${skill.name}" is already listed as "${existingSkill.displayName}".`);
56111
- throw new Error("Skill");
56747
+ throw new CliError("error", `Skill "${skill.name}" is already listed as "${existingSkill.displayName}".`);
56112
56748
  }
56113
56749
  writeProgress("\u{1F504} Listing skill on marketplace...");
56114
56750
  const marketplaceSkill = await marketplaceApi.listSkill({
@@ -56125,17 +56761,14 @@ __name(listSkillNonInteractive, "listSkillNonInteractive");
56125
56761
  async function publishVersionNonInteractive(marketplaceApi, config, apiKey, options) {
56126
56762
  const { marketplaceId, versionId, changelog, envVarsJson } = options;
56127
56763
  if (!marketplaceId || !versionId) {
56128
- console.error("\u274C Missing required options");
56129
- console.log("\nUsage: lua marketplace skill publish --marketplace-id <id> --version-id <id> [--changelog <text>]");
56130
- throw new Error("Missing required options");
56764
+ throw CliError.usage("Missing required options", "Usage: lua marketplace skill publish --marketplace-id <id> --version-id <id> [--changelog <text>]");
56131
56765
  }
56132
56766
  let envVars;
56133
56767
  if (envVarsJson) {
56134
56768
  try {
56135
56769
  envVars = JSON.parse(envVarsJson);
56136
- } catch (e) {
56137
- console.error("\u274C Invalid --env-vars-json: must be valid JSON");
56138
- throw new Error("Invalid --env-vars-json: must be valid JSON");
56770
+ } catch {
56771
+ throw CliError.usage("Invalid --env-vars-json: must be valid JSON");
56139
56772
  }
56140
56773
  }
56141
56774
  writeProgress("\u{1F504} Publishing version...");
@@ -56152,13 +56785,10 @@ __name(publishVersionNonInteractive, "publishVersionNonInteractive");
56152
56785
  async function updateMetadataNonInteractive(marketplaceApi, options) {
56153
56786
  const { marketplaceId, displayName } = options;
56154
56787
  if (!marketplaceId) {
56155
- console.error("\u274C Missing required option: --marketplace-id");
56156
- console.log("\nUsage: lua marketplace skill edit --marketplace-id <id> --display-name <name>");
56157
- throw new Error("Missing required option: --marketplace-id");
56788
+ throw CliError.usage("Missing required option: --marketplace-id", "Usage: lua marketplace skill edit --marketplace-id <id> --display-name <name>");
56158
56789
  }
56159
56790
  if (!displayName) {
56160
- console.error("\u274C No update specified. Provide --display-name to update.");
56161
- throw new Error("No update specified. Provide --display-name to update.");
56791
+ throw CliError.usage("No update specified. Provide --display-name to update.");
56162
56792
  }
56163
56793
  writeProgress("\u{1F504} Updating skill metadata...");
56164
56794
  await marketplaceApi.updateSkill(marketplaceId, {
@@ -56170,14 +56800,10 @@ __name(updateMetadataNonInteractive, "updateMetadataNonInteractive");
56170
56800
  async function unlistSkillNonInteractive(marketplaceApi, options) {
56171
56801
  const { marketplaceId, force } = options;
56172
56802
  if (!marketplaceId) {
56173
- console.error("\u274C Missing required option: --marketplace-id");
56174
- console.log("\nUsage: lua marketplace skill unlist --marketplace-id <id> [--force]");
56175
- throw new Error("Missing required option: --marketplace-id");
56803
+ throw CliError.usage("Missing required option: --marketplace-id", "Usage: lua marketplace skill unlist --marketplace-id <id> [--force]");
56176
56804
  }
56177
56805
  if (!force) {
56178
- console.error("\u274C This action requires --force to confirm");
56179
- console.log("\nUsage: lua marketplace skill unlist --marketplace-id <id> --force");
56180
- throw new Error("This action requires --force to confirm");
56806
+ throw CliError.usage("This action requires --force to confirm", "Usage: lua marketplace skill unlist --marketplace-id <id> --force");
56181
56807
  }
56182
56808
  writeProgress("\u{1F504} Unlisting skill...");
56183
56809
  await marketplaceApi.unlistSkill(marketplaceId);
@@ -56188,13 +56814,10 @@ __name(unlistSkillNonInteractive, "unlistSkillNonInteractive");
56188
56814
  async function unpublishVersionNonInteractive(marketplaceApi, options) {
56189
56815
  const { marketplaceId, versionId, force } = options;
56190
56816
  if (!marketplaceId || !versionId) {
56191
- console.error("\u274C Missing required options");
56192
- console.log("\nUsage: lua marketplace skill unpublish --marketplace-id <id> --version-id <id> [--force]");
56193
- throw new Error("Missing required options");
56817
+ throw CliError.usage("Missing required options", "Usage: lua marketplace skill unpublish --marketplace-id <id> --version-id <id> [--force]");
56194
56818
  }
56195
56819
  if (!force) {
56196
- console.error("\u274C This action requires --force to confirm");
56197
- throw new Error("This action requires --force to confirm");
56820
+ throw CliError.usage("This action requires --force to confirm");
56198
56821
  }
56199
56822
  writeProgress("\u{1F504} Unpublishing version...");
56200
56823
  await marketplaceApi.unpublishVersion(marketplaceId, versionId);
@@ -56204,8 +56827,7 @@ async function unpublishVersionNonInteractive(marketplaceApi, options) {
56204
56827
  __name(unpublishVersionNonInteractive, "unpublishVersionNonInteractive");
56205
56828
  async function transferOwnershipNonInteractive(marketplaceApi, options) {
56206
56829
  if (!options.marketplaceId || !options.newOrgId || !options.force) {
56207
- console.error("\u274C --marketplace-id, --new-org-id and --force are required");
56208
- throw new Error("Missing transfer options");
56830
+ throw CliError.usage("--marketplace-id, --new-org-id and --force are required");
56209
56831
  }
56210
56832
  writeProgress("\u{1F504} Transferring organization ownership...");
56211
56833
  const skill = await marketplaceApi.transferSkillOwnership(options.marketplaceId, options.newOrgId);
@@ -56284,9 +56906,7 @@ __name(searchSkillsNonInteractive, "searchSkillsNonInteractive");
56284
56906
  async function viewSkillNonInteractive(marketplaceApi, options) {
56285
56907
  const { marketplaceId } = options;
56286
56908
  if (!marketplaceId) {
56287
- console.error("\u274C Missing required option: --marketplace-id");
56288
- console.log("\nUsage: lua marketplace skill view --marketplace-id <id>");
56289
- throw new Error("Missing required option: --marketplace-id");
56909
+ throw CliError.usage("Missing required option: --marketplace-id", "Usage: lua marketplace skill view --marketplace-id <id>");
56290
56910
  }
56291
56911
  writeProgress("\u{1F504} Loading skill details...");
56292
56912
  const skill = await marketplaceApi.getSkillById(marketplaceId);
@@ -56328,14 +56948,11 @@ __name(viewSkillNonInteractive, "viewSkillNonInteractive");
56328
56948
  async function installSkillNonInteractive(marketplaceApi, config, options) {
56329
56949
  const { marketplaceId, versionId, envVars, force } = options;
56330
56950
  if (!marketplaceId || !versionId) {
56331
- console.error("\u274C Missing required options");
56332
- console.log("\nUsage: lua marketplace skill install --marketplace-id <id> --version-id <id> [--env-vars <k=v,...>]");
56333
- throw new Error("Missing required options");
56951
+ throw CliError.usage("Missing required options", "Usage: lua marketplace skill install --marketplace-id <id> --version-id <id> [--env-vars <k=v,...>]");
56334
56952
  }
56335
56953
  const agentId = config.agent?.agentId;
56336
56954
  if (!agentId) {
56337
- console.error("\u274C Agent ID not found in configuration.");
56338
- throw new Error("Agent ID not found in configuration.");
56955
+ throw CliError.usage("Agent ID not found in configuration.", "Run `lua init` in a project directory first.");
56339
56956
  }
56340
56957
  const envVarsConfig = {};
56341
56958
  if (envVars) {
@@ -56356,8 +56973,7 @@ async function installSkillNonInteractive(marketplaceApi, config, options) {
56356
56973
  if (Object.keys(envVarsConfig).length > 0) {
56357
56974
  writeInfo(` Env vars: ${Object.keys(envVarsConfig).length} configured`);
56358
56975
  }
56359
- console.error("\n\u274C Use --force to confirm installation");
56360
- throw new Error("Operation failed");
56976
+ throw CliError.usage("Use --force to confirm installation");
56361
56977
  }
56362
56978
  writeProgress("\u{1F504} Installing skill...");
56363
56979
  await marketplaceApi.installSkill(marketplaceId, agentId, {
@@ -56385,31 +57001,20 @@ __name(installSkillNonInteractive, "installSkillNonInteractive");
56385
57001
  async function updateInstalledSkillNonInteractive(marketplaceApi, config, apiKey, options) {
56386
57002
  const { skillName, versionId, envVars } = options;
56387
57003
  if (!skillName) {
56388
- console.error("\u274C Missing required option: --skill-name");
56389
- console.log("\nUsage: lua marketplace skill update --skill-name <name> [--version-id <id>] [--env-vars <k=v,...>]");
56390
- throw new Error("Missing required option: --skill-name");
57004
+ throw CliError.usage("Missing required option: --skill-name", "Usage: lua marketplace skill update --skill-name <name> [--version-id <id>] [--env-vars <k=v,...>]");
56391
57005
  }
56392
57006
  const agentId = config.agent?.agentId;
56393
57007
  if (!agentId) {
56394
- console.error("\u274C Agent ID not found in configuration.");
56395
- throw new Error("Agent ID not found in configuration.");
57008
+ throw CliError.usage("Agent ID not found in configuration.", "Run `lua init` in a project directory first.");
56396
57009
  }
56397
57010
  writeProgress("\u{1F504} Loading installed skills...");
56398
57011
  const installedSkills = await marketplaceApi.getInstalledSkills(agentId);
56399
57012
  const installedSkill = installedSkills?.find((s) => s.name === skillName || s.title === skillName);
56400
57013
  if (!installedSkill) {
56401
- console.error(`\u274C Installed skill not found: ${skillName}`);
56402
- if (installedSkills?.length) {
56403
- writeInfo("\nInstalled skills:");
56404
- installedSkills.forEach((s) => {
56405
- writeInfo(` - ${s.title || s.name}`);
56406
- });
56407
- }
56408
- throw new Error("Operation failed");
57014
+ throw CliError.notFound(`Installed skill not found: ${skillName}`, listHint("Installed skills:", (installedSkills ?? []).map((s) => s.title || s.name)));
56409
57015
  }
56410
57016
  if (!installedSkill.marketplaceSkillId) {
56411
- console.error("\u274C Skill is missing marketplace skill ID.");
56412
- throw new Error("Skill is missing marketplace skill ID.");
57017
+ throw new CliError("error", "Skill is missing marketplace skill ID.");
56413
57018
  }
56414
57019
  let newEnvVars;
56415
57020
  if (envVars) {
@@ -56426,8 +57031,7 @@ async function updateInstalledSkillNonInteractive(marketplaceApi, config, apiKey
56426
57031
  if (versionId) updatePayload.versionId = versionId;
56427
57032
  if (newEnvVars) updatePayload.envVars = newEnvVars;
56428
57033
  if (!updatePayload.versionId && !updatePayload.envVars) {
56429
- console.error("\u274C No update specified. Provide --version-id or --env-vars.");
56430
- throw new Error("No update specified. Provide --version-id or --env-vars.");
57034
+ throw CliError.usage("No update specified. Provide --version-id or --env-vars.");
56431
57035
  }
56432
57036
  writeProgress("\u{1F504} Updating installed skill...");
56433
57037
  await marketplaceApi.updateInstallation(installedSkill.marketplaceSkillId, agentId, updatePayload);
@@ -56437,29 +57041,23 @@ __name(updateInstalledSkillNonInteractive, "updateInstalledSkillNonInteractive")
56437
57041
  async function uninstallSkillNonInteractive(marketplaceApi, config, options) {
56438
57042
  const { skillName, force } = options;
56439
57043
  if (!skillName) {
56440
- console.error("\u274C Missing required option: --skill-name");
56441
- console.log("\nUsage: lua marketplace skill uninstall --skill-name <name> [--force]");
56442
- throw new Error("Missing required option: --skill-name");
57044
+ throw CliError.usage("Missing required option: --skill-name", "Usage: lua marketplace skill uninstall --skill-name <name> [--force]");
56443
57045
  }
56444
57046
  const agentId = config.agent?.agentId;
56445
57047
  if (!agentId) {
56446
- console.error("\u274C Agent ID not found in configuration.");
56447
- throw new Error("Agent ID not found in configuration.");
57048
+ throw CliError.usage("Agent ID not found in configuration.", "Run `lua init` in a project directory first.");
56448
57049
  }
56449
57050
  writeProgress("\u{1F504} Loading installed skills...");
56450
57051
  const installedSkills = await marketplaceApi.getInstalledSkills(agentId);
56451
57052
  const installedSkill = installedSkills?.find((s) => s.name === skillName || s.title === skillName);
56452
57053
  if (!installedSkill) {
56453
- console.error(`\u274C Installed skill not found: ${skillName}`);
56454
- throw new Error("Installed skill not found: ${skillName}");
57054
+ throw CliError.notFound(`Installed skill not found: ${skillName}`);
56455
57055
  }
56456
57056
  if (!installedSkill.marketplaceSkillId) {
56457
- console.error("\u274C Skill is missing marketplace skill ID.");
56458
- throw new Error("Skill is missing marketplace skill ID.");
57057
+ throw new CliError("error", "Skill is missing marketplace skill ID.");
56459
57058
  }
56460
57059
  if (!force) {
56461
- console.error(`\u274C Use --force to confirm uninstalling "${installedSkill.title || installedSkill.name}"`);
56462
- throw new Error("Use --force to confirm uninstalling");
57060
+ throw CliError.usage(`Use --force to confirm uninstalling "${installedSkill.title || installedSkill.name}"`);
56463
57061
  }
56464
57062
  writeProgress("\u{1F504} Uninstalling skill...");
56465
57063
  await marketplaceApi.uninstallSkill(installedSkill.marketplaceSkillId, agentId);
@@ -56469,8 +57067,7 @@ __name(uninstallSkillNonInteractive, "uninstallSkillNonInteractive");
56469
57067
  async function listInstalledSkillsNonInteractive(marketplaceApi, config, options) {
56470
57068
  const agentId = config.agent?.agentId;
56471
57069
  if (!agentId) {
56472
- console.error("\u274C Agent ID not found in configuration.");
56473
- throw new Error("Agent ID not found in configuration.");
57070
+ throw CliError.usage("Agent ID not found in configuration.", "Run `lua init` in a project directory first.");
56474
57071
  }
56475
57072
  writeProgress("\u{1F504} Loading installed marketplace skills...");
56476
57073
  const skills = await marketplaceApi.getInstalledSkills(agentId);
@@ -57951,6 +58548,7 @@ init_constants();
57951
58548
  init_command_utils();
57952
58549
  init_developer_api_service();
57953
58550
  init_analytics();
58551
+ init_cli_error();
57954
58552
  async function mcpCommand(action, serverNamePositional, cmdObj) {
57955
58553
  return withErrorHandling(async () => {
57956
58554
  const options = {
@@ -58135,23 +58733,11 @@ async function executeNonInteractive11(context, action, options) {
58135
58733
  return;
58136
58734
  }
58137
58735
  if (!options.serverName) {
58138
- console.error(`\u274C --server-name is required for action "${resolvedAction}"`);
58139
- console.log(`
58140
- Usage: lua mcp ${resolvedAction} --server-name <name>`);
58141
- if (servers.length > 0) {
58142
- console.log("\nAvailable servers:");
58143
- servers.forEach((s) => console.log(` - ${s.name} ${s.active ? "(active)" : "(inactive)"}`));
58144
- }
58145
- throw new Error("Operation failed");
58736
+ throw CliError.usage(`--server-name is required for action "${resolvedAction}"`, joinHint(`Usage: lua mcp ${resolvedAction} --server-name <name>`, listHint("Available servers:", servers.map((s) => `${s.name} ${s.active ? "(active)" : "(inactive)"}`))));
58146
58737
  }
58147
58738
  const selectedServer = findServer(servers, options.serverName);
58148
58739
  if (!selectedServer) {
58149
- console.error(`\u274C MCP server "${options.serverName}" not found`);
58150
- if (servers.length > 0) {
58151
- console.log("\nAvailable servers:");
58152
- servers.forEach((s) => console.log(` - ${s.name}`));
58153
- }
58154
- throw new Error("Operation failed");
58740
+ throw CliError.notFound(`MCP server "${options.serverName}" not found`, listHint("Available servers:", servers.map((s) => s.name)));
58155
58741
  }
58156
58742
  switch (resolvedAction) {
58157
58743
  case "activate": {
@@ -58549,6 +59135,7 @@ var UnifiedToApi = class extends HttpClient {
58549
59135
 
58550
59136
  // src/commands/integrations.ts
58551
59137
  init_analytics();
59138
+ init_cli_error();
58552
59139
  var CALLBACK_PORT = 19837;
58553
59140
  var CALLBACK_HOST = "127.0.0.1";
58554
59141
  var CALLBACK_URL = `http://${CALLBACK_HOST}:${CALLBACK_PORT}/callback`;
@@ -58745,9 +59332,7 @@ async function executeNonInteractive12(context, action, cmdOptions) {
58745
59332
  break;
58746
59333
  case "update":
58747
59334
  if (!options.integration && !options.connectionId) {
58748
- console.error("\u274C --connection-id (preferred) or --integration is required for update");
58749
- console.log("\n\u{1F4A1} Run 'lua integrations list' to see connected integrations");
58750
- throw new Error("--connection-id or --integration is required for update");
59335
+ throw CliError.usage("--connection-id (preferred) or --integration is required for update", "Run 'lua integrations list' to see connected integrations");
58751
59336
  }
58752
59337
  if (options.scope === "user") {
58753
59338
  await updateUserConnectionFlow(context, options);
@@ -58767,9 +59352,7 @@ async function executeNonInteractive12(context, action, cmdOptions) {
58767
59352
  break;
58768
59353
  case "convert":
58769
59354
  if (!options.connectionId) {
58770
- console.error("\u274C --connection-id is required for convert");
58771
- console.log("\n\u{1F4A1} Run 'lua integrations list' to see connection IDs");
58772
- throw new Error("--connection-id is required for convert");
59355
+ throw CliError.usage("--connection-id is required for convert", "Run 'lua integrations list' to see connection IDs");
58773
59356
  }
58774
59357
  await convertConnectionFlow(context, options.connectionId, cmdOptions?.force === true);
58775
59358
  break;
@@ -58779,18 +59362,13 @@ async function executeNonInteractive12(context, action, cmdOptions) {
58779
59362
  case "info":
58780
59363
  const infoIntegrationType = options.integration || cmdOptions?._?.[0];
58781
59364
  if (!infoIntegrationType) {
58782
- console.error("\u274C Integration type is required");
58783
- console.log("\nUsage: lua integrations info <type>");
58784
- console.log(" lua integrations info <type> --json");
58785
- throw new Error("Integration type is required");
59365
+ throw CliError.usage("Integration type is required", "Usage: lua integrations info <type>\n lua integrations info <type> --json");
58786
59366
  }
58787
59367
  await showIntegrationInfo(context, infoIntegrationType, jsonOutput);
58788
59368
  break;
58789
59369
  case "disconnect":
58790
59370
  if (!options.connectionId) {
58791
- console.error("\u274C --connection-id is required for disconnect");
58792
- console.log("\n\u{1F4A1} Run 'lua integrations list' to see connection IDs");
58793
- throw new Error("--connection-id is required for disconnect");
59371
+ throw CliError.usage("--connection-id is required for disconnect", "Run 'lua integrations list' to see connection IDs");
58794
59372
  }
58795
59373
  if (options.scope === "user") {
58796
59374
  await disconnectUserConnection(context, options.connectionId);
@@ -58805,9 +59383,7 @@ async function executeNonInteractive12(context, action, cmdOptions) {
58805
59383
  await mcpSubcommand(context, cmdOptions);
58806
59384
  break;
58807
59385
  default:
58808
- console.error(`\u274C Invalid action: "${action}"`);
58809
- showUsage();
58810
- throw new Error("Invalid action:");
59386
+ throw CliError.usage(`Invalid action: "${action}"`, INTEGRATIONS_USAGE.join("\n"));
58811
59387
  }
58812
59388
  }
58813
59389
  __name(executeNonInteractive12, "executeNonInteractive");
@@ -58936,16 +59512,7 @@ async function showIntegrationInfo(context, integrationType, jsonOutput = false)
58936
59512
  const integrations = await fetchAvailableIntegrations(context.unifiedToApi);
58937
59513
  const integration = integrations.find((i) => i.value === integrationType);
58938
59514
  if (!integration) {
58939
- if (jsonOutput) {
58940
- console.log(JSON.stringify({
58941
- error: `Integration not found: ${integrationType}`
58942
- }));
58943
- } else {
58944
- console.error(`\u274C Integration not found: ${integrationType}`);
58945
- console.log("\nAvailable integrations:");
58946
- integrations.forEach((i) => console.log(` - ${i.value} (${i.name})`));
58947
- }
58948
- throw new Error("Operation failed");
59515
+ throw CliError.notFound(`Integration not found: ${integrationType}`, listHint("Available integrations:", integrations.map((i) => `${i.value} (${i.name})`)));
58949
59516
  }
58950
59517
  let webhookEvents = [];
58951
59518
  try {
@@ -59028,14 +59595,11 @@ async function showIntegrationInfo(context, integrationType, jsonOutput = false)
59028
59595
  console.log();
59029
59596
  }
59030
59597
  } catch (error) {
59031
- if (jsonOutput) {
59032
- console.log(JSON.stringify({
59033
- error: error.message
59034
- }));
59035
- } else {
59036
- writeError(`\u274C Failed to get integration info: ${error.message}`);
59037
- }
59038
- throw new Error("Operation failed");
59598
+ if (jsonOutput) console.log(JSON.stringify({
59599
+ error: error.message
59600
+ }));
59601
+ if (isTypedCliError(error)) throw error;
59602
+ throw new CliError("error", `Failed to get integration info: ${error.message}`);
59039
59603
  }
59040
59604
  }
59041
59605
  __name(showIntegrationInfo, "showIntegrationInfo");
@@ -59055,8 +59619,7 @@ function normalizeScopeFlag(raw) {
59055
59619
  if (!raw) return void 0;
59056
59620
  const normalized = raw.toLowerCase();
59057
59621
  if (normalized !== "agent" && normalized !== "user") {
59058
- console.error(`\u274C Invalid --scope: "${raw}". Use 'agent' or 'user'`);
59059
- throw new Error("Invalid --scope");
59622
+ throw CliError.usage(`Invalid --scope: "${raw}". Use 'agent' or 'user'`);
59060
59623
  }
59061
59624
  return normalized;
59062
59625
  }
@@ -59216,22 +59779,13 @@ async function connectIntegrationFlow(context, options = {}) {
59216
59779
  "oauth",
59217
59780
  "token"
59218
59781
  ].includes(options.authMethod)) {
59219
- console.error(`\u274C Invalid --auth-method: "${options.authMethod}". Use 'oauth' or 'token'`);
59220
- throw new Error("Invalid --auth-method:");
59782
+ throw CliError.usage(`Invalid --auth-method: "${options.authMethod}". Use 'oauth' or 'token'`);
59221
59783
  }
59222
59784
  if (options.authMethod === "oauth" && !canUseOAuth) {
59223
- console.error(`\u274C OAuth is not available for ${selectedIntegration.name}.`);
59224
- if (canUseToken) {
59225
- console.log(`\u{1F4A1} Use --auth-method token instead.`);
59226
- }
59227
- throw new Error("Operation failed");
59785
+ throw CliError.usage(`OAuth is not available for ${selectedIntegration.name}.`, canUseToken ? "Use --auth-method token instead." : void 0);
59228
59786
  }
59229
59787
  if (options.authMethod === "token" && !canUseToken) {
59230
- console.error(`\u274C Token authentication is not available for ${selectedIntegration.name}.`);
59231
- if (canUseOAuth) {
59232
- console.log(`\u{1F4A1} Use --auth-method oauth instead.`);
59233
- }
59234
- throw new Error("Operation failed");
59788
+ throw CliError.usage(`Token authentication is not available for ${selectedIntegration.name}.`, canUseOAuth ? "Use --auth-method oauth instead." : void 0);
59235
59789
  }
59236
59790
  authMethod = options.authMethod;
59237
59791
  writeInfo(`Using ${authMethod === "oauth" ? "OAuth 2.0" : "API Token"} authentication`);
@@ -59277,11 +59831,7 @@ async function connectIntegrationFlow(context, options = {}) {
59277
59831
  const requestedScopes = options.scopes.split(",").map((s) => s.trim());
59278
59832
  const invalidScopes = requestedScopes.filter((s) => !availableScopes.includes(s));
59279
59833
  if (invalidScopes.length > 0) {
59280
- console.error(`\u274C Invalid scopes: ${invalidScopes.join(", ")}`);
59281
- console.log(`
59282
- Available scopes for ${selectedIntegration.name}:`);
59283
- availableScopes.forEach((s) => console.log(` - ${s}`));
59284
- throw new Error("Invalid scopes: ${invalidScopes.join(");
59834
+ throw CliError.usage(`Invalid scopes: ${invalidScopes.join(", ")}`, listHint(`Available scopes for ${selectedIntegration.name}:`, availableScopes));
59285
59835
  }
59286
59836
  selectedScopes = requestedScopes;
59287
59837
  writeInfo(`Using ${selectedScopes.length} specified scope(s)`);
@@ -59367,13 +59917,7 @@ Available scopes for ${selectedIntegration.name}:`);
59367
59917
  const [objectType, event] = trigger.split(".");
59368
59918
  const matchingEvent = availableEvents.find((e) => e.objectType === objectType && e.event === event);
59369
59919
  if (!matchingEvent) {
59370
- console.error(`\u274C Invalid trigger: ${trigger}`);
59371
- console.log(`
59372
- Available triggers for ${selectedIntegration.name}:`);
59373
- availableEvents.forEach((e) => {
59374
- console.log(` - ${e.objectType}.${e.event} [${e.webhookType}] - ${e.friendlyDescription}`);
59375
- });
59376
- throw new Error("Invalid trigger: ${trigger}");
59920
+ throw CliError.usage(`Invalid trigger: ${trigger}`, listHint(`Available triggers for ${selectedIntegration.name}:`, availableEvents.map((e) => `${e.objectType}.${e.event} [${e.webhookType}] - ${e.friendlyDescription}`)));
59377
59921
  }
59378
59922
  selectedTriggers.push(matchingEvent);
59379
59923
  }
@@ -60001,10 +60545,7 @@ async function updateConnectionFlow(context, options = {}) {
60001
60545
  }
60002
60546
  selectedConnection = matches[0];
60003
60547
  if (!selectedConnection) {
60004
- console.error(`\u274C No connection found for integration "${options.integration}".`);
60005
- console.log("\nConnected integrations:");
60006
- connections.forEach((c) => console.log(` - ${c.integrationType} (${c.integrationName || c.integrationType})`));
60007
- throw new Error("No connection found for integration");
60548
+ throw CliError.notFound(`No connection found for integration "${options.integration}".`, listHint("Connected integrations:", connections.map((c) => `${c.integrationType} (${c.integrationName || c.integrationType})`)));
60008
60549
  }
60009
60550
  selectedIntegration = integrations.find((i) => i.value === options.integration);
60010
60551
  } else {
@@ -60049,11 +60590,7 @@ async function updateConnectionFlow(context, options = {}) {
60049
60590
  const requestedScopes = options.scopes.split(",").map((s) => s.trim());
60050
60591
  const invalidScopes = requestedScopes.filter((s) => !availableScopes.includes(s));
60051
60592
  if (invalidScopes.length > 0) {
60052
- console.error(`\u274C Invalid scopes: ${invalidScopes.join(", ")}`);
60053
- console.log(`
60054
- Available scopes for ${selectedIntegration.name}:`);
60055
- availableScopes.forEach((s) => console.log(` - ${s}`));
60056
- throw new Error("Invalid scopes: ${invalidScopes.join(");
60593
+ throw CliError.usage(`Invalid scopes: ${invalidScopes.join(", ")}`, listHint(`Available scopes for ${selectedIntegration.name}:`, availableScopes));
60057
60594
  }
60058
60595
  selectedScopes = requestedScopes;
60059
60596
  writeInfo(`Using ${selectedScopes.length} specified scope(s)`);
@@ -60287,9 +60824,7 @@ async function webhooksSubcommand(context, cmdOptions) {
60287
60824
  break;
60288
60825
  case "delete":
60289
60826
  if (!options.webhookId) {
60290
- console.error("\u274C --webhook-id is required for delete");
60291
- console.log("\n\u{1F4A1} Run 'lua integrations webhooks list' to see trigger IDs");
60292
- throw new Error("--webhook-id is required for delete");
60827
+ throw CliError.usage("--webhook-id is required for delete", "Run 'lua integrations webhooks list' to see trigger IDs");
60293
60828
  }
60294
60829
  await webhooksDeleteFlow(context, options.webhookId);
60295
60830
  break;
@@ -60299,9 +60834,7 @@ async function webhooksSubcommand(context, cmdOptions) {
60299
60834
  } else if (options.connectionId) {
60300
60835
  await connectionPauseFlow(context, options.connectionId);
60301
60836
  } else {
60302
- console.error("\u274C --webhook-id or --connection-id is required for pause");
60303
- console.log("\n\u{1F4A1} Run 'lua integrations webhooks list' to see trigger IDs");
60304
- throw new Error("--webhook-id or --connection-id is required for pause");
60837
+ throw CliError.usage("--webhook-id or --connection-id is required for pause", "Run 'lua integrations webhooks list' to see trigger IDs");
60305
60838
  }
60306
60839
  break;
60307
60840
  case "resume":
@@ -60310,9 +60843,7 @@ async function webhooksSubcommand(context, cmdOptions) {
60310
60843
  } else if (options.connectionId) {
60311
60844
  await connectionResumeFlow(context, options.connectionId);
60312
60845
  } else {
60313
- console.error("\u274C --webhook-id or --connection-id is required for resume");
60314
- console.log("\n\u{1F4A1} Run 'lua integrations webhooks list' to see trigger IDs");
60315
- throw new Error("--webhook-id or --connection-id is required for resume");
60846
+ throw CliError.usage("--webhook-id or --connection-id is required for resume", "Run 'lua integrations webhooks list' to see trigger IDs");
60316
60847
  }
60317
60848
  break;
60318
60849
  case "events":
@@ -60355,17 +60886,7 @@ async function webhooksEventsFlow(context, options, jsonOutput = false) {
60355
60886
  }
60356
60887
  sourceName = `integration ${options.integration}`;
60357
60888
  } else {
60358
- if (jsonOutput) {
60359
- console.log(JSON.stringify({
60360
- error: "Either --connection or --integration is required"
60361
- }));
60362
- } else {
60363
- console.error("\u274C Either --connection <id> or --integration <type> is required");
60364
- console.log("\nUsage:");
60365
- console.log(" lua integrations webhooks events --connection <id>");
60366
- console.log(" lua integrations webhooks events --integration <type>");
60367
- }
60368
- throw new Error("Operation failed");
60889
+ throw CliError.usage("Either --connection <id> or --integration <type> is required", "Usage:\n lua integrations webhooks events --connection <id>\n lua integrations webhooks events --integration <type>");
60369
60890
  }
60370
60891
  if (jsonOutput) {
60371
60892
  const output = {
@@ -60409,14 +60930,11 @@ async function webhooksEventsFlow(context, options, jsonOutput = false) {
60409
60930
  }
60410
60931
  }
60411
60932
  } catch (error) {
60412
- if (jsonOutput) {
60413
- console.log(JSON.stringify({
60414
- error: error.message
60415
- }));
60416
- } else {
60417
- writeError(`\u274C Failed to get trigger events: ${error.message}`);
60418
- }
60419
- throw new Error("Operation failed");
60933
+ if (jsonOutput) console.log(JSON.stringify({
60934
+ error: error.message
60935
+ }));
60936
+ if (isTypedCliError(error)) throw error;
60937
+ throw new CliError("error", `Failed to get trigger events: ${error.message}`);
60420
60938
  }
60421
60939
  }
60422
60940
  __name(webhooksEventsFlow, "webhooksEventsFlow");
@@ -60849,10 +61367,7 @@ async function webhooksCreateFlow(context, options) {
60849
61367
  if (options.connectionId) {
60850
61368
  selectedConnection = connections.find((c) => c.id === options.connectionId);
60851
61369
  if (!selectedConnection) {
60852
- console.error(`\u274C Connection "${options.connectionId}" not found.`);
60853
- console.log("\nAvailable connections:");
60854
- connections.forEach((c) => console.log(` - ${c.id} (${c.integrationName || c.integrationType})`));
60855
- throw new Error("Connection");
61370
+ throw CliError.notFound(`Connection "${options.connectionId}" not found.`, listHint("Available connections:", connections.map((c) => `${c.id} (${c.integrationName || c.integrationType})`)));
60856
61371
  }
60857
61372
  } else {
60858
61373
  const connectionAnswer = await safePrompt([
@@ -60891,11 +61406,7 @@ async function webhooksCreateFlow(context, options) {
60891
61406
  if (options.objectType && options.event) {
60892
61407
  selectedEvent = availableEvents.find((e) => e.objectType === options.objectType && e.event === options.event);
60893
61408
  if (!selectedEvent) {
60894
- console.error(`\u274C Event '${options.objectType}.${options.event}' is not supported.`);
60895
- console.log(`
60896
- Available events for ${selectedConnection.integrationName || selectedConnection.integrationType}:`);
60897
- availableEvents.forEach((e) => console.log(` - ${e.objectType}.${e.event} [${e.webhookType}]`));
60898
- throw new Error("Event");
61409
+ throw CliError.usage(`Event '${options.objectType}.${options.event}' is not supported.`, listHint(`Available events for ${selectedConnection.integrationName || selectedConnection.integrationType}:`, availableEvents.map((e) => `${e.objectType}.${e.event} [${e.webhookType}]`)));
60899
61410
  }
60900
61411
  } else {
60901
61412
  console.log(`
@@ -61405,44 +61916,48 @@ async function mcpDeactivateInteractive(context) {
61405
61916
  }
61406
61917
  }
61407
61918
  __name(mcpDeactivateInteractive, "mcpDeactivateInteractive");
61408
- function showUsage() {
61409
- console.log("\nUsage:");
61410
- console.log(" lua integrations Interactive integration management");
61411
- console.log(" lua integrations connect Connect a new integration (interactive)");
61412
- console.log(" lua integrations connect --integration <type> Connect a specific integration");
61413
- console.log(" lua integrations connect --integration <type> --triggers <events> Connect with triggers");
61414
- console.log(" lua integrations update Update connection scopes (interactive)");
61415
- console.log(" lua integrations update --integration <type> Update scopes for a specific integration");
61416
- console.log(" lua integrations list List connected integrations");
61417
- console.log(" lua integrations available List available integrations");
61418
- console.log(" lua integrations info <type> Show integration details (scopes, triggers)");
61419
- console.log(" lua integrations info <type> --json Output as JSON for scripting");
61420
- console.log(" lua integrations disconnect --connection-id <id> Disconnect an integration");
61421
- console.log("\nTriggers (also available as: lua triggers <action>):");
61422
- console.log(" lua integrations webhooks list List all triggers");
61423
- console.log(" lua integrations webhooks list --json Output as JSON");
61424
- console.log(" lua integrations webhooks events --connection <id> List available events for a connection");
61425
- console.log(" lua integrations webhooks events --integration <type> List available events for an integration");
61426
- console.log(" lua integrations webhooks create Create trigger (interactive)");
61427
- console.log(" lua integrations webhooks create --connection <id> --object <type> --event <event> --hook-url <url>");
61428
- console.log(" lua integrations webhooks delete --webhook-id <id> Delete a trigger");
61429
- console.log(" lua integrations webhooks pause --webhook-id <id> Pause a trigger");
61430
- console.log(" lua integrations webhooks pause --connection-id <id> Pause all triggers for a connection");
61431
- console.log(" lua integrations webhooks resume --webhook-id <id> Resume a trigger");
61432
- console.log(" lua integrations webhooks resume --connection-id <id> Resume all triggers for a connection");
61433
- console.log("\nMCP Server Management:");
61434
- console.log(" lua integrations mcp list List connections with MCP status");
61435
- console.log(" lua integrations mcp activate --connection <id> Activate MCP server");
61436
- console.log(" lua integrations mcp deactivate --connection <id> Deactivate MCP server");
61437
- console.log("\nTrigger Options (use with connect):");
61438
- console.log(" --triggers <events> Comma-separated triggers (e.g., task_task.created,task_task.updated)");
61439
- console.log(" Omit to skip triggers and add them later");
61440
- console.log(" --custom-webhook Use custom URL instead of agent trigger");
61441
- console.log(" --hook-url <url> Custom URL for triggers");
61442
- console.log("\nPause Options (use with webhooks pause):");
61443
- console.log(" --reason <text> Optional reason for pausing (informational)");
61444
- }
61445
- __name(showUsage, "showUsage");
61919
+ var INTEGRATIONS_USAGE = [
61920
+ "",
61921
+ "Usage:",
61922
+ " lua integrations Interactive integration management",
61923
+ " lua integrations connect Connect a new integration (interactive)",
61924
+ " lua integrations connect --integration <type> Connect a specific integration",
61925
+ " lua integrations connect --integration <type> --triggers <events> Connect with triggers",
61926
+ " lua integrations update Update connection scopes (interactive)",
61927
+ " lua integrations update --integration <type> Update scopes for a specific integration",
61928
+ " lua integrations list List connected integrations",
61929
+ " lua integrations available List available integrations",
61930
+ " lua integrations info <type> Show integration details (scopes, triggers)",
61931
+ " lua integrations info <type> --json Output as JSON for scripting",
61932
+ " lua integrations disconnect --connection-id <id> Disconnect an integration",
61933
+ "",
61934
+ "Triggers (also available as: lua triggers <action>):",
61935
+ " lua integrations webhooks list List all triggers",
61936
+ " lua integrations webhooks list --json Output as JSON",
61937
+ " lua integrations webhooks events --connection <id> List available events for a connection",
61938
+ " lua integrations webhooks events --integration <type> List available events for an integration",
61939
+ " lua integrations webhooks create Create trigger (interactive)",
61940
+ " lua integrations webhooks create --connection <id> --object <type> --event <event> --hook-url <url>",
61941
+ " lua integrations webhooks delete --webhook-id <id> Delete a trigger",
61942
+ " lua integrations webhooks pause --webhook-id <id> Pause a trigger",
61943
+ " lua integrations webhooks pause --connection-id <id> Pause all triggers for a connection",
61944
+ " lua integrations webhooks resume --webhook-id <id> Resume a trigger",
61945
+ " lua integrations webhooks resume --connection-id <id> Resume all triggers for a connection",
61946
+ "",
61947
+ "MCP Server Management:",
61948
+ " lua integrations mcp list List connections with MCP status",
61949
+ " lua integrations mcp activate --connection <id> Activate MCP server",
61950
+ " lua integrations mcp deactivate --connection <id> Deactivate MCP server",
61951
+ "",
61952
+ "Trigger Options (use with connect):",
61953
+ " --triggers <events> Comma-separated triggers (e.g., task_task.created,task_task.updated)",
61954
+ " Omit to skip triggers and add them later",
61955
+ " --custom-webhook Use custom URL instead of agent trigger",
61956
+ " --hook-url <url> Custom URL for triggers",
61957
+ "",
61958
+ "Pause Options (use with webhooks pause):",
61959
+ " --reason <text> Optional reason for pausing (informational)"
61960
+ ];
61446
61961
 
61447
61962
  // src/commands/agents.ts
61448
61963
  init_cli();
@@ -61904,6 +62419,19 @@ init_compiler2();
61904
62419
  init_artifact_loader();
61905
62420
  init_analytics();
61906
62421
  init_cli_error();
62422
+ function noAgentConfigured() {
62423
+ return CliError.usage("No agent configured.", "Run `lua init` first to set up a project.");
62424
+ }
62425
+ __name(noAgentConfigured, "noAgentConfigured");
62426
+ async function fetchModelsWithProgress(apiKey, agentId) {
62427
+ writeProgress("\u{1F504} Fetching available models...");
62428
+ try {
62429
+ return await fetchApprovedModelsOrThrow(apiKey, agentId);
62430
+ } finally {
62431
+ writeProgress("");
62432
+ }
62433
+ }
62434
+ __name(fetchModelsWithProgress, "fetchModelsWithProgress");
61907
62435
  async function resolveCurrentModel(apiKey, agentId) {
61908
62436
  const serverModel = await fetchServerModel(apiKey, agentId);
61909
62437
  if (serverModel) return serverModel;
@@ -61963,14 +62491,7 @@ async function modelsCommand(action, opts) {
61963
62491
  const config = readYamlConfig();
61964
62492
  const agentId = config?.agent?.agentId;
61965
62493
  if (resolvedAction === "list") {
61966
- writeProgress("\u{1F504} Fetching available models...");
61967
- const models = await fetchApprovedModels(apiKey, agentId);
61968
- writeProgress("");
61969
- if (models === null) {
61970
- throw new CliError("error", "Could not fetch models from the server.", {
61971
- hint: "Check your connection and API key, then retry."
61972
- });
61973
- }
62494
+ const models = await fetchModelsWithProgress(apiKey, agentId);
61974
62495
  if (models.length === 0) {
61975
62496
  writeInfo("\u2139\uFE0F No models are currently available. If your organization restricts models, an admin may need to review its excluded-models list.");
61976
62497
  trackEvent("cli_models_listed", {
@@ -61990,19 +62511,10 @@ async function modelsCommand(action, opts) {
61990
62511
  return;
61991
62512
  }
61992
62513
  if (resolvedAction === "set") {
61993
- if (!agentId) {
61994
- writeError("\u274C No agent configured. Run `lua init` first to set up a project.");
61995
- process.exit(1);
61996
- }
61997
- writeProgress("\u{1F504} Fetching available models...");
61998
- const models = await fetchApprovedModels(apiKey, agentId);
61999
- writeProgress("");
62000
- if (models === null) {
62001
- writeError("\u274C Could not fetch models from the server. Check your connection and API key.");
62002
- process.exit(1);
62003
- } else if (models.length === 0) {
62004
- writeError("\u274C No models are currently available. If your organization restricts models, an admin may need to review its excluded-models list.");
62005
- process.exit(1);
62514
+ if (!agentId) throw noAgentConfigured();
62515
+ const models = await fetchModelsWithProgress(apiKey, agentId);
62516
+ if (models.length === 0) {
62517
+ throw new CliError("error", "No models are currently available. If your organization restricts models, an admin may need to review its excluded-models list.");
62006
62518
  }
62007
62519
  let selectedModel;
62008
62520
  if (opts.model) {
@@ -62047,10 +62559,7 @@ async function modelsCommand(action, opts) {
62047
62559
  return;
62048
62560
  }
62049
62561
  if (resolvedAction === "unset") {
62050
- if (!agentId) {
62051
- writeError("\u274C No agent configured. Run `lua init` first to set up a project.");
62052
- process.exit(1);
62053
- }
62562
+ if (!agentId) throw noAgentConfigured();
62054
62563
  const currentModel = await resolveCurrentModel(apiKey, agentId);
62055
62564
  if (!currentModel) {
62056
62565
  writeInfo("\u2139\uFE0F No model is currently set \u2014 the platform default is already in use.");
@@ -62077,8 +62586,7 @@ async function modelsCommand(action, opts) {
62077
62586
  });
62078
62587
  return;
62079
62588
  }
62080
- writeError(`\u274C Unknown action '${resolvedAction}'. Use 'list', 'set', or 'unset'.`);
62081
- process.exit(1);
62589
+ throw CliError.usage(`Unknown action '${resolvedAction}'. Use 'list', 'set', or 'unset'.`);
62082
62590
  }, "models");
62083
62591
  }
62084
62592
  __name(modelsCommand, "modelsCommand");
@@ -62627,15 +63135,15 @@ function renderTryPage(wsUrl, token) {
62627
63135
  __name(renderTryPage, "renderTryPage");
62628
63136
 
62629
63137
  // src/commands/voice.ts
63138
+ init_cli_error();
62630
63139
  async function voiceTestCommand(options = {}) {
62631
63140
  return withErrorHandling(async () => {
62632
63141
  const cwd = process.cwd();
62633
63142
  const runner = options.runner === "auto" || !options.runner ? detectRunner(cwd) : options.runner;
62634
63143
  if (!runner) {
62635
- console.error("\u274C No supported test runner found.");
62636
- console.log(" Install jest or vitest in this project, then re-run.");
62637
- console.log(" Example: pnpm add -D jest ts-jest @types/jest");
62638
- throw new Error("no test runner");
63144
+ throw new CliError("error", "No supported test runner found.", {
63145
+ hint: "Install jest or vitest in this project, then re-run.\nExample: pnpm add -D jest ts-jest @types/jest"
63146
+ });
62639
63147
  }
62640
63148
  const pattern = options.pattern ?? (options.voice ? `${escapeRegex(options.voice)}\\.voice\\.test\\.` : "\\.voice\\.test\\.");
62641
63149
  const files = findVoiceTestFiles(cwd);
@@ -63775,14 +64283,18 @@ __name(versionStatusCommand, "versionStatusCommand");
63775
64283
  init_cli();
63776
64284
  init_files();
63777
64285
  init_analytics();
64286
+ init_cli_error();
63778
64287
  var NO_YAML_MESSAGE = "No lua.skill.yaml found. Please run this command from a skill directory.";
63779
- function failConnect(status, banner, errorMessage) {
63780
- if (banner) writeError(banner);
64288
+ function failConnect(status, banner, errorMessage, cls = "error") {
63781
64289
  trackEvent("cli_git_connect_completed", {
63782
64290
  ...status,
63783
64291
  succeeded: false
63784
64292
  });
63785
- throw new Error(errorMessage);
64293
+ const hint = banner?.split("\n").map((line) => line.replace(/^✗\s*/, "").trim()).join("\n");
64294
+ if (cls === "usage") throw CliError.usage(errorMessage, hint);
64295
+ throw new CliError("error", errorMessage, {
64296
+ hint
64297
+ });
63786
64298
  }
63787
64299
  __name(failConnect, "failConnect");
63788
64300
  async function gitConnectCommand(opts = {}) {
@@ -63793,7 +64305,7 @@ async function gitConnectCommand(opts = {}) {
63793
64305
  git_available: false,
63794
64306
  in_repo: false,
63795
64307
  user_configured: false
63796
- }, null, NO_YAML_MESSAGE);
64308
+ }, null, NO_YAML_MESSAGE, "usage");
63797
64309
  }
63798
64310
  if (!await isGitAvailable()) {
63799
64311
  failConnect({
@@ -63862,7 +64374,7 @@ async function gitDisconnectCommand() {
63862
64374
  return withErrorHandling(async () => {
63863
64375
  const config = readYamlConfig();
63864
64376
  if (!config) {
63865
- throw new Error(NO_YAML_MESSAGE);
64377
+ throw CliError.usage(NO_YAML_MESSAGE);
63866
64378
  }
63867
64379
  config.git = {
63868
64380
  enabled: false
@@ -64510,15 +65022,17 @@ Examples:
64510
65022
  $ lua jobs versions -i myJob View job versions
64511
65023
  $ lua jobs history -i myJob View execution history
64512
65024
  `).action(jobsCommand);
64513
- program2.command("workflows [action] [target] [extra]").description("\u{1F9ED} Manage workflows and their runs (list, start, watch, cancel, resume, approve, signal, replay)").option("-i, --workflow-name <name>", "Workflow name (or id)").option("-r, --run-id <id>", "Run id").option("-v, --workflow-version <ver>", "deploy/start/goals create: a semver, 'latest' (newest push) or a version id").option("--json", "Print the raw {success,data} envelope").option("--all", "list: include dynamic workflows").option("--input <json|@file>", "start/run/goals create: run input").option("--idempotency-key <key>", "start: idempotency key").option("--correlation-key <key>", "start/runs: correlation key").option("--tag <tag>", "start/runs: tag (repeatable, \u2264 10)", collect, []).option("--budget-credits <n>", "start: run budget (credits)").option("--wait <s>", "start: server long-poll \u2264 55 s").option("--follow", "start/logs: attach watch").option("--status <status>", "runs/goals list: filter by status").option("--workflow <name>", "runs: filter by workflow").option("--limit <n>", "runs/goals list: page size").option("--cursor <c>", "runs/goals list: page cursor").option("--sort <field>", "runs: -createdAt|createdAt|-durationMs|durationMs").option("--steps", "status: per-step table").option("--strict", "status: exit 4/5 by terminal status").option("--after <seq>", "watch: replay from seq").option("--timeout <s>", "watch / start --follow: give up after s seconds while the run is still live (exit 7)").option("--events", "watch: print raw frames").option("--wait-for-human", "watch / start --follow: keep following through a human boundary (approval, input, signal, park) instead of exiting 8").option("--reason <text>", "cancel: reason").option("--step <id>", "resume/logs: step id").option("--data <json|@file>", "resume: resumeData").option("--approval <id>", "approve: approval id").option("--decision <approve|deny>", "approve: decision (default approve)").option("--note <text>", "approve/goals close: note").option("--edit <json|@file>", "approve: edited payload (small inline edits)").option("--fingerprint <f>", "approve: payloadFingerprint (mandatory with --edit)").option("--payload <json|@file>", "signal: payload").option("--dedupe-key <key>", "signal: dedupe key").option("--local", "replay: replay locally against the compiled artifact").option("--since <dur>", "logs: window").option("--yes", "delete/delete-run/schedules delete: skip confirmation").option("--force", "delete: cancel in-flight runs first \xB7 run: seed from a changed graph").option("--step-output <id=json>", "run: complete a step with this output (repeatable)", collect, []).option("--approve <id[=@payload]>", "run: pre-answer an approval (repeatable)", collect, []).option("--deny <id[=@reason]>", "run: pre-deny an approval (repeatable)", collect, []).option("--signal <name=json>", "run: pre-supply a waitForSignal payload (repeatable)", collect, []).option("--from-run <runId>", "run: seed completed steps from a real run").option("--record <dir>", "run: record agent/tool outputs as fixtures").option("--fixtures <dir>", "run: replay recorded fixtures").option("--step-wall <s>", "run: per-step wall in seconds (default 600)").option("--job-wall <s>", "run: virtual wall for tier:'job' steps \u2014 splits at 14400 s segments, > 86400 is exit 2").option("--ledger-out <file>", "run: write the in-memory ledger as JSON").option("--agents <fake|live>", "run: fake agent steps (default) or call the dev API").option("--now <iso>", "run: virtual clock start").option("--park <id>", "run: simulate a platform-fault park of this step (repeatable)", collect, []).option("--fast-retries", "run: collapse retry backoff waits to 0").option("--real-time", "run: actually wait on sleeps/backoffs instead of fast-forwarding").option("--artefacts-dir <dir>", "run: back ctx.artefacts.* on disk").option("--workspace <dir>", "run: the checkout Job-tier code steps run in (ctx.workspace + ctx.exec / ctx.$)").option("--env <KEY=value>", "run: local env.template() overlay (repeatable; missing key \u21D2 exit 2)", collect, []).option("--max-ticks <n>", "run (script form): tick cap before SCRIPT_TICK_LIMIT (default 64)").option("--until <iso>", "archive-runs: window end").option("--out <dir>", "archive-runs: destination dir (archive-index.ndjson + <runId>.zip)").option("--concurrency <n>", "archive-runs: parallel exports (default 2, max 5)").option("--no-inputs", "archive-runs: exclude run inputs from the bundle").option("--no-artefacts", "archive-runs: exclude artefacts from the bundle").option("--retention-days <n>", "archive-runs: org run retention (default 90) \u2014 --since must fit retention \u2212 7 d").option("--connection <id>", "archive-runs: org storage connection for s3:// / gs:// sinks").option("--release", "workspace: release the run workspace (R42)").option("--attempt <n>", "job-logs: attempt (default latest)").option("--tail <n>", "job-logs: log lines (1..2000, default 200)").option("--objective <text>", "goals create: the objective (\u2264 2000 chars)").option("--judge-predicate <spec>", "goals create: deterministic judge \u2014 '<path> <op> [value]' (e.g. 'output.done truthy'), JSON or @file").option("--judge-agent <agentId|'$self'>", "goals create: judge agent \u2014 write '$self' quoted (or self); it needs --judge-role").option("--judge-role <json|@file>", "goals create: D25 role {name,instructions,tools} for a '$self' judge").option("--schema <json|@file>", "goals create: judge output JsonSchema (must declare a boolean `done`)").option("--cadence <cron|json|@file>", "goals create: cadence entry (repeatable, \u2264 5); none \u21D2 immediate", collect, []).option("--timezone <tz>", "goals create/edit: IANA timezone for bare cron cadences").option("--max-runs <n>", "goals create/edit/raise: iteration cap (1..100)").option("--max-total-credits <n>", "goals create: lineage-wide credit gate").option("--max-credits <n|none>", "goals edit/raise: lineage-wide credit gate (edit: none clears it)").option("--every <interval>", "goals edit: interval cadence \u2014 900, 15m, 2h (whole minutes, \u2265 60s)").option("--if-match <updatedAt>", "goals edit/raise: refuse the write if the goal changed since this updatedAt").addHelpText("after", `
65025
+ program2.command("workflows [action] [target] [extra]").description("\u{1F9ED} Manage workflows and their runs (list, start, watch, cancel, resume, retry-step, resolve-step, raise-budget, approve, signal, replay, export, schedules)").option("-i, --workflow-name <name>", "Workflow name (or id)").option("-r, --run-id <id>", "Run id").option("-v, --workflow-version <ver>", "deploy/start/export/goals create/schedules create: a semver, 'latest' (newest push) or a version id").option("--json", "Print the raw {success,data} envelope").option("--all", "list: include dynamic workflows").option("--input <json|@file>", "start/run/goals create/schedules create: run input").option("--idempotency-key <key>", "start: idempotency key").option("--correlation-key <key>", "start/runs: correlation key").option("--tag <tag>", "start/runs/schedules create: tag (repeatable, \u2264 10)", collect, []).option("--budget-credits <n>", "start/schedules create: run budget (credits)").option("--wait <s>", "start: server long-poll \u2264 55 s").option("--follow", "start/logs: attach watch").option("--status <status>", "runs/goals list: filter by status").option("--workflow <name>", "runs: filter by workflow").option("--limit <n>", "runs/goals list/approval-payload --path: page size").option("--cursor <c>", "runs/goals list/approval-payload --path: page cursor").option("--sort <field>", "runs: -createdAt|createdAt|-durationMs|durationMs").option("--steps", "status: per-step table").option("--strict", "status: exit 4/5 by terminal status").option("--after <seq>", "watch: replay from seq").option("--timeout <s>", "watch / start --follow: give up after s seconds while the run is still live (exit 7)").option("--events", "watch: print raw frames").option("--wait-for-human", "watch / start --follow: keep following through a human boundary (approval, input, signal, park) instead of exiting 8").option("--reason <text>", "cancel: reason").option("--step <id>", "resume/retry-step/resolve-step/logs: step id").option("--outcome <skip|complete|fail>", "resolve-step: the decision on the parked step (--action is accepted too)").option("--action <skip|complete|fail>", "resolve-step: alias of --outcome").option("--output <json|@file>", "resolve-step: the output a completed step would have produced (required with --outcome complete)").option("--credits <n>", "raise-budget: the new credit cap of a budget-parked run (above the current one)").option("--max-steps <n>", "raise-budget: the new step cap").option("--max-job-seconds <n>", "raise-budget: the new Job-tier seconds cap").option("--max-duration-seconds <n>", "raise-budget: the new wall-clock cap").option("--data <json|@file>", "resume: resumeData").option("--approval <id>", "approve/approval-payload: approval id").option("--path <array.path>", "approval-payload: page one array of a large payload (--cursor / --limit inside it)").option("--decision <approve|deny>", "approve: decision (default approve)").option("--note <text>", "approve/retry-step/resolve-step/raise-budget/goals close: note").option("--edit <json|@file>", "approve: edited payload (small inline edits)").option("--fingerprint <f>", "approve: payloadFingerprint (mandatory with --edit)").option("--payload <json|@file>", "signal: payload").option("--dedupe-key <key>", "signal: dedupe key").option("--local", "replay: replay locally against the compiled artifact").option("--since <dur>", "logs: window").option("--yes", "delete/delete-run/schedules delete: skip confirmation").option("--force", "delete: cancel in-flight runs first \xB7 run: seed from a changed graph \xB7 export: overwrite files").option("--step-output <id=json>", "run: complete a step with this output (repeatable)", collect, []).option("--approve <id[=@payload]>", "run: pre-answer an approval (repeatable)", collect, []).option("--deny <id[=@reason]>", "run: pre-deny an approval (repeatable)", collect, []).option("--signal <name=json>", "run: pre-supply a waitForSignal payload (repeatable)", collect, []).option("--from-run <runId>", "run: seed completed steps from a real run").option("--record <dir>", "run: record agent/tool outputs as fixtures").option("--fixtures <dir>", "run: replay recorded fixtures").option("--step-wall <s>", "run: per-step wall in seconds (default 600)").option("--job-wall <s>", "run: virtual wall for tier:'job' steps \u2014 splits at 14400 s segments, > 86400 is exit 2").option("--ledger-out <file>", "run: write the in-memory ledger as JSON").option("--agents <fake|live>", "run: fake agent steps (default) or call the dev API").option("--now <iso>", "run: virtual clock start").option("--park <id>", "run: simulate a platform-fault park of this step (repeatable)", collect, []).option("--fast-retries", "run: collapse retry backoff waits to 0").option("--real-time", "run: actually wait on sleeps/backoffs instead of fast-forwarding").option("--artefacts-dir <dir>", "run: back ctx.artefacts.* on disk").option("--workspace <dir>", "run: the checkout Job-tier code steps run in (ctx.workspace + ctx.exec / ctx.$)").option("--env <KEY=value>", "run: local env.template() overlay (repeatable; missing key \u21D2 exit 2)", collect, []).option("--max-ticks <n>", "run (script form): tick cap before SCRIPT_TICK_LIMIT (default 64)").option("--until <iso>", "archive-runs: window end").option("--out <dir>", "archive-runs: destination dir (archive-index.ndjson + <runId>.zip) \xB7 export: target dir").option("--concurrency <n>", "archive-runs: parallel exports (default 2, max 5)").option("--no-inputs", "archive-runs: exclude run inputs from the bundle").option("--no-artefacts", "archive-runs: exclude artefacts from the bundle").option("--retention-days <n>", "archive-runs: org run retention (default 90) \u2014 --since must fit retention \u2212 7 d").option("--connection <id>", "archive-runs: org storage connection for s3:// / gs:// sinks").option("--release", "workspace: release the run workspace (R42)").option("--attempt <n>", "job-logs: attempt (default latest)").option("--tail <n>", "job-logs: log lines (1..2000, default 200)").option("--objective <text>", "goals create: the objective (\u2264 2000 chars)").option("--judge-predicate <spec>", "goals create: deterministic judge \u2014 '<path> <op> [value]' (e.g. 'output.done truthy'), JSON or @file").option("--judge-agent <agentId|'$self'>", "goals create: judge agent \u2014 write '$self' quoted (or self); it needs --judge-role").option("--judge-role <json|@file>", "goals create: D25 role {name,instructions,tools} for a '$self' judge").option("--schema <json|@file>", "goals create: judge output JsonSchema (must declare a boolean `done`)").option("--cadence <cron|json|@file>", "goals create/schedules create: cadence entry (repeatable, \u2264 5); goals: none \u21D2 immediate", collect, []).option("--timezone <tz>", "goals create/edit/schedules create: IANA timezone for bare cron cadences").option("--notify <emailApp|email|app|off>", "schedules create: where each fire notifies").option("--backfill-on-enable <n|none>", "schedules create/patch/resume: replay \u2264 n missed fires on re-enable (patch: none clears it)").option("--backfill-now", "schedules resume (or patch --paused false): one-shot backfill on THIS re-enable").option("--paused <true|false>", "schedules patch: pause (true) or re-enable (false) the schedule").option("--max-runs <n>", "goals create/edit/raise: iteration cap (1..100)").option("--max-total-credits <n>", "goals create: lineage-wide credit gate").option("--max-credits <n|none>", "goals edit/raise: lineage-wide credit gate (edit: none clears it)").option("--every <interval>", "goals edit/schedules create: interval cadence \u2014 900, 15m, 2h (whole minutes, \u2265 60s)").option("--if-match <updatedAt>", "goals edit/raise: refuse the write if the goal changed since this updatedAt").addHelpText("after", `
64514
65026
  Arguments:
64515
65027
  action list \xB7 view \xB7 versions \xB7 deploy \xB7 activate \xB7 deactivate \xB7 start \xB7 run \xB7 runs \xB7 status \xB7
64516
- watch \xB7 cancel \xB7 resume \xB7 retry-step \xB7 approve \xB7 signal \xB7 replay \xB7 logs \xB7 delete \xB7 delete-run \xB7
64517
- env-overlay \xB7 archive-runs \xB7 workspace \xB7 jobs \xB7 job-logs \xB7
64518
- goals <list|get|create|edit|raise|pause|resume|close> \xB7 schedules <list|delete>
64519
- target workflow name (list/view/versions/deploy/activate/deactivate/start/run/delete) or run id (the rest);
64520
- the sub-verb for goals / schedules
64521
- extra signal name (signal <runId> <name>) \xB7 goal id (goals get/edit/raise/pause/resume/close) \xB7 job id (schedules delete)
65028
+ watch \xB7 cancel \xB7 resume \xB7 retry-step \xB7 resolve-step \xB7 raise-budget \xB7 approve \xB7 approval-payload \xB7
65029
+ signal \xB7 replay \xB7 logs \xB7 delete \xB7 delete-run \xB7 env-overlay \xB7 export \xB7 archive-runs \xB7
65030
+ workspace \xB7 jobs \xB7 job-logs \xB7 goals <list|get|create|edit|raise|pause|resume|close> \xB7
65031
+ schedules <list|create|patch|pause|resume|delete>
65032
+ target workflow name (list/view/versions/deploy/activate/deactivate/start/run/delete/env-overlay/export) or
65033
+ run id (the rest); the sub-verb for goals / schedules
65034
+ extra signal name (signal <runId> <name>) \xB7 goal id (goals get/edit/raise/pause/resume/close) \xB7
65035
+ workflow (schedules create) \xB7 job id (schedules patch/pause/resume/delete)
64522
65036
 
64523
65037
  Exit codes: 0 ok \xB7 1 API refusal (a 4xx other than 404) \xB7 2 usage \xB7 3 not found \xB7 4 run failed \xB7 5 run cancelled \xB7
64524
65038
  6 run gated (consent) \xB7 7 --timeout reached \xB7 8 run parked \u2014 waiting for a human
@@ -64545,10 +65059,16 @@ Examples:
64545
65059
  $ lua workflows cancel <runId> --reason "wrong input"
64546
65060
  $ lua workflows resume <runId> --step ask --data '{"answer":42}'
64547
65061
  $ lua workflows retry-step <runId> --step sendEmails --note "vendor back up"
65062
+ $ lua workflows resolve-step <runId> --step sendEmails --outcome complete --output '{"sent":0}'
65063
+ $ lua workflows raise-budget <runId> --credits 20
65064
+ $ lua workflows approval-payload <runId> --approval wfa_1234
64548
65065
  $ lua workflows approve <runId> --approval reviewDrafts --decision approve
64549
65066
  $ lua workflows signal <runId> review --payload '{"ok":true}'
64550
65067
  $ lua workflows replay <runId> --local
64551
65068
  $ lua workflows deploy outreach -v latest
65069
+ $ lua workflows export outreach --out ./exported
65070
+ $ lua workflows schedules create outreach --cadence '0 9 * * 1' --timezone Europe/London --input '{"segment":"trial"}'
65071
+ $ lua workflows schedules resume <jobId> --backfill-now
64552
65072
  $ lua workflows view outreach --json | jq '.data.schedules, .data.goals'
64553
65073
  $ lua workflows goals list -i outreach --status active
64554
65074
  $ lua workflows goals get wfg_1234