@wave-ai-os/wave-cli 0.1.77 → 0.1.80

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.
Files changed (3) hide show
  1. package/dist/index.cjs +509 -42
  2. package/dist/index.js +509 -42
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -327,6 +327,9 @@ function buildAtlasActionLedgerRequest(params) {
327
327
  }
328
328
 
329
329
  // src/client/graphqlClient.ts
330
+ var MAX_QUERY_ATTEMPTS = 3;
331
+ var MAX_RETRY_DELAY_MS = 2e3;
332
+ var TRANSIENT_QUERY_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
330
333
  function buildEnvelope(params) {
331
334
  return {
332
335
  ok: params.ok,
@@ -357,6 +360,47 @@ function debugLog(config, message) {
357
360
  process.stderr.write(`[wave:debug] ${message}
358
361
  `);
359
362
  }
363
+ function retryAfterDelayMs(value, nowMs = Date.now()) {
364
+ if (value === null) {
365
+ return null;
366
+ }
367
+ const trimmed = value.trim();
368
+ if (/^\d+$/.test(trimmed)) {
369
+ return Math.min(Number(trimmed) * 1e3, MAX_RETRY_DELAY_MS);
370
+ }
371
+ const retryAtMs = Date.parse(trimmed);
372
+ if (Number.isNaN(retryAtMs)) {
373
+ return null;
374
+ }
375
+ return Math.min(Math.max(retryAtMs - nowMs, 0), MAX_RETRY_DELAY_MS);
376
+ }
377
+ function retryDelayMs(attempt, retryAfter) {
378
+ const serverDelay = retryAfterDelayMs(retryAfter);
379
+ if (serverDelay !== null) {
380
+ return serverDelay;
381
+ }
382
+ const exponentialCeiling = Math.min(100 * 2 ** (attempt - 1), MAX_RETRY_DELAY_MS);
383
+ return Math.floor(Math.random() * exponentialCeiling);
384
+ }
385
+ function waitForRetry(delayMs, signal) {
386
+ if (signal.aborted) {
387
+ return Promise.reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
388
+ }
389
+ if (delayMs <= 0) {
390
+ return Promise.resolve();
391
+ }
392
+ return new Promise((resolve, reject) => {
393
+ const timeoutId = setTimeout(() => {
394
+ signal.removeEventListener("abort", handleAbort);
395
+ resolve();
396
+ }, delayMs);
397
+ const handleAbort = () => {
398
+ clearTimeout(timeoutId);
399
+ reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
400
+ };
401
+ signal.addEventListener("abort", handleAbort, { once: true });
402
+ });
403
+ }
360
404
  function graphqlOperationName(command) {
361
405
  return command.split(/[^a-zA-Z0-9]/).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join("");
362
406
  }
@@ -674,10 +718,41 @@ async function requestGraphqlHttp(params) {
674
718
  return {
675
719
  status: response.status,
676
720
  requestId: responseRequestId,
721
+ retryAfter: response.headers.get("retry-after"),
677
722
  body,
678
723
  graph
679
724
  };
680
725
  }
726
+ async function requestGraphqlHttpWithRetry(params) {
727
+ const maxAttempts = params.input.operationType === "query" ? MAX_QUERY_ATTEMPTS : 1;
728
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
729
+ try {
730
+ const response = await requestGraphqlHttp(params);
731
+ const shouldRetry = attempt < maxAttempts && TRANSIENT_QUERY_STATUSES.has(response.status);
732
+ if (!shouldRetry) {
733
+ return response;
734
+ }
735
+ const delayMs = retryDelayMs(attempt, response.retryAfter);
736
+ debugLog(
737
+ params.input.config,
738
+ `graphql retry command=${params.input.command} attempt=${attempt + 1}/${maxAttempts} status=${response.status} delayMs=${delayMs}`
739
+ );
740
+ await waitForRetry(delayMs, params.controller.signal);
741
+ } catch (error) {
742
+ if (attempt >= maxAttempts || params.controller.signal.aborted) {
743
+ throw error;
744
+ }
745
+ const delayMs = retryDelayMs(attempt, null);
746
+ const message = error instanceof Error ? error.message : "Network error.";
747
+ debugLog(
748
+ params.input.config,
749
+ `graphql retry command=${params.input.command} attempt=${attempt + 1}/${maxAttempts} error=${message} delayMs=${delayMs}`
750
+ );
751
+ await waitForRetry(delayMs, params.controller.signal);
752
+ }
753
+ }
754
+ throw new Error("GraphQL query retry attempts exhausted.");
755
+ }
681
756
  async function reportAtlasLedgerForResult(input, url, result) {
682
757
  const ledgerInput = buildAtlasActionLedgerRequest({
683
758
  config: input.config,
@@ -858,7 +933,7 @@ async function graphqlRequest(input) {
858
933
  }
859
934
  }
860
935
  }
861
- const http = await requestGraphqlHttp({
936
+ const http = await requestGraphqlHttpWithRetry({
862
937
  input,
863
938
  url,
864
939
  requestId,
@@ -3036,18 +3111,24 @@ var dtoHelpCatalog = {
3036
3111
  create: [
3037
3112
  ["rank", "number"],
3038
3113
  ["organization_id", "string"],
3039
- ["list_id", "string"],
3040
- ["summary", "string"],
3114
+ ["list_id*", "string"],
3115
+ ["summary*", "string"],
3041
3116
  ["member_id", "string"],
3042
- ["status", "ListItemStatus"],
3117
+ ["status", "enum(open|completed)"],
3043
3118
  ["due_date", "string"],
3044
- ["priority", "Priority"],
3119
+ ["priority", "enum(no_priority|urgent|high|medium|low)"],
3045
3120
  ["description", "string"],
3046
3121
  ["notes", "string | null"],
3047
3122
  ["label_ids", "number[]"],
3048
3123
  ["estimate_ids", "number[]"],
3049
3124
  ["resources_attributes", "ResourceDTO[]"],
3050
- ["subitems_attributes", "SubitemAttributes[]"]
3125
+ ["subitems_attributes", "object[]"],
3126
+ ["subitems_attributes[].name", "string"],
3127
+ ["subitems_attributes[].member_id", "string"],
3128
+ ["subitems_attributes[].status", "enum(active|completed)"],
3129
+ ["subitems_attributes[].priority", "enum(no_priority|urgent|high|medium|low)"],
3130
+ ["subitems_attributes[].description", "string"],
3131
+ ["subitems_attributes[].notes", "string | null"]
3051
3132
  ],
3052
3133
  update: [
3053
3134
  ["rank", "number"],
@@ -3055,18 +3136,50 @@ var dtoHelpCatalog = {
3055
3136
  ["list_id", "string"],
3056
3137
  ["summary", "string"],
3057
3138
  ["member_id", "string"],
3058
- ["status", "ListItemStatus"],
3139
+ ["status", "enum(open|completed)"],
3059
3140
  ["due_date", "string"],
3060
- ["priority", "Priority"],
3141
+ ["priority", "enum(no_priority|urgent|high|medium|low)"],
3061
3142
  ["description", "string"],
3062
3143
  ["notes", "string | null"],
3063
3144
  ["label_ids", "number[]"],
3064
3145
  ["estimate_ids", "number[]"],
3065
3146
  ["resources_attributes", "ResourceDTO[]"],
3066
- ["subitems_attributes", "SubitemAttributes[]"],
3147
+ ["subitems_attributes", "object[]"],
3148
+ ["subitems_attributes[].name", "string"],
3149
+ ["subitems_attributes[].member_id", "string"],
3150
+ ["subitems_attributes[].status", "enum(active|completed)"],
3151
+ ["subitems_attributes[].priority", "enum(no_priority|urgent|high|medium|low)"],
3152
+ ["subitems_attributes[].description", "string"],
3153
+ ["subitems_attributes[].notes", "string | null"],
3067
3154
  ["archived_at", "string | null"]
3068
3155
  ]
3069
3156
  },
3157
+ subitem: {
3158
+ source: "wave-api-rb/config/initializers/constants/subitem.rb + wave-api-rb/app/models/subitem.rb",
3159
+ create: [
3160
+ ["list_item_id*", "string"],
3161
+ ["name*", "string"],
3162
+ ["member_id", "string"],
3163
+ ["status", "enum(active|completed)"],
3164
+ ["priority", "enum(no_priority|urgent|high|medium|low)"],
3165
+ ["description", "string"],
3166
+ ["notes", "string | null"],
3167
+ ["rank", "number"],
3168
+ ["due_date", "string"],
3169
+ ["estimate_ids", "number[]"]
3170
+ ],
3171
+ update: [
3172
+ ["name", "string"],
3173
+ ["member_id", "string"],
3174
+ ["status", "enum(active|completed)"],
3175
+ ["priority", "enum(no_priority|urgent|high|medium|low)"],
3176
+ ["description", "string"],
3177
+ ["notes", "string | null"],
3178
+ ["rank", "number"],
3179
+ ["due_date", "string"],
3180
+ ["estimate_ids", "number[]"]
3181
+ ]
3182
+ },
3070
3183
  issue_group: {
3071
3184
  source: "wave-ui/src/types/issueGroup.ts + wave-api-rb/config/initializers/constants/issue_group.rb + wave-api-rb/app/models/issue_group.rb",
3072
3185
  create: [
@@ -3441,6 +3554,24 @@ function toRows(fields, mode) {
3441
3554
  return `${field} | required:${requiredForMode} | type:${type} | ${humanize(field)}`;
3442
3555
  });
3443
3556
  }
3557
+ function exampleValue(type) {
3558
+ if (type === "number") return 1;
3559
+ if (type === "boolean") return true;
3560
+ if (type.endsWith("[]")) return [];
3561
+ const enumValues = /^enum\(([^)]+)\)$/u.exec(type)?.[1]?.split("|");
3562
+ return enumValues?.[0] ?? "value";
3563
+ }
3564
+ function examplePayload(rootKey, fields, mode) {
3565
+ const requiredFields = mode === "create" ? fields.filter(([field]) => field.endsWith("*")) : [];
3566
+ const exampleFields = requiredFields.length > 0 ? requiredFields : fields.slice(0, 1);
3567
+ const entity = Object.fromEntries(
3568
+ exampleFields.map(([rawField, type]) => [
3569
+ rawField.replace(/\*$/u, ""),
3570
+ exampleValue(type)
3571
+ ])
3572
+ );
3573
+ return JSON.stringify({ [rootKey]: entity });
3574
+ }
3444
3575
  function buildDataJsonHelp(rootKey, mode) {
3445
3576
  const config = dtoHelpCatalog[rootKey];
3446
3577
  if (!config) return null;
@@ -3453,10 +3584,205 @@ function buildDataJsonHelp(rootKey, mode) {
3453
3584
  `note: fields containing "preference" are intentionally omitted.`,
3454
3585
  "field | required | type | description",
3455
3586
  ...toRows(fields, mode),
3456
- `example: {"${rootKey}":{"${fields[0][0].replace(/\*$/, "")}":"value"}}`
3587
+ `example: ${examplePayload(rootKey, fields, mode)}`
3457
3588
  ].join("\n");
3458
3589
  }
3459
3590
 
3591
+ // src/entityPayloadContracts.ts
3592
+ var PRIORITIES = ["no_priority", "urgent", "high", "medium", "low"];
3593
+ var SUBITEM_STATUSES = ["active", "completed"];
3594
+ var LIST_ITEM_STATUSES = ["open", "completed"];
3595
+ var subitemFields = {
3596
+ list_item_id: { type: "string" },
3597
+ name: { type: "string" },
3598
+ status: { type: "string", enum: SUBITEM_STATUSES },
3599
+ priority: { type: "string", enum: PRIORITIES }
3600
+ };
3601
+ var listItemFields = {
3602
+ list_id: { type: "string" },
3603
+ summary: { type: "string" },
3604
+ status: { type: "string", enum: LIST_ITEM_STATUSES },
3605
+ priority: { type: "string", enum: PRIORITIES },
3606
+ subitems_attributes: {
3607
+ type: "array",
3608
+ items: {
3609
+ type: "object",
3610
+ fields: subitemFields,
3611
+ required: ["name"]
3612
+ }
3613
+ }
3614
+ };
3615
+ var listItemCreateContract = {
3616
+ rootKey: "list_item",
3617
+ fields: listItemFields,
3618
+ required: ["list_id", "summary"]
3619
+ };
3620
+ var listItemUpdateContract = {
3621
+ rootKey: "list_item",
3622
+ fields: listItemFields
3623
+ };
3624
+ var subitemCreateContract = {
3625
+ rootKey: "subitem",
3626
+ fields: subitemFields,
3627
+ required: ["list_item_id", "name"]
3628
+ };
3629
+ var subitemUpdateContract = {
3630
+ rootKey: "subitem",
3631
+ fields: subitemFields
3632
+ };
3633
+ var CONTRACTS = {
3634
+ "list-items.create": listItemCreateContract,
3635
+ "list-items.update": listItemUpdateContract,
3636
+ "subitems.create": subitemCreateContract,
3637
+ "subitems.update": subitemUpdateContract
3638
+ };
3639
+ function isPlainObject(value) {
3640
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3641
+ }
3642
+ function parsePayload(value) {
3643
+ if (typeof value !== "string") return value;
3644
+ try {
3645
+ return JSON.parse(value);
3646
+ } catch {
3647
+ return /* @__PURE__ */ Symbol.for("invalid-json");
3648
+ }
3649
+ }
3650
+ function typeMatches(value, type) {
3651
+ if (!type) return true;
3652
+ if (type === "array") return Array.isArray(value);
3653
+ if (type === "object") return isPlainObject(value);
3654
+ return typeof value === type;
3655
+ }
3656
+ function validateField(contract, value, path) {
3657
+ if (!typeMatches(value, contract.type)) {
3658
+ return [`${path.join(".")} must be ${contract.type}.`];
3659
+ }
3660
+ if (contract.enum && !contract.enum.includes(value)) {
3661
+ return [`${path.join(".")} must be one of ${contract.enum.join(", ")}.`];
3662
+ }
3663
+ if (contract.type === "object" && contract.fields && isPlainObject(value)) {
3664
+ const missing = (contract.required ?? []).filter(
3665
+ (field) => value[field] === void 0 || value[field] === null || value[field] === ""
3666
+ );
3667
+ return [
3668
+ ...missing.map((field) => `${[...path, field].join(".")} is required.`),
3669
+ ...validateFields(contract.fields, value, path)
3670
+ ];
3671
+ }
3672
+ if (contract.type === "array" && contract.items && Array.isArray(value)) {
3673
+ return value.flatMap(
3674
+ (item, index) => validateField(contract.items, item, [...path, index])
3675
+ );
3676
+ }
3677
+ return [];
3678
+ }
3679
+ function validateFields(fields, value, path) {
3680
+ return Object.entries(fields).flatMap(([field, contract]) => {
3681
+ const fieldValue = value[field];
3682
+ return fieldValue === void 0 ? [] : validateField(contract, fieldValue, [...path, field]);
3683
+ });
3684
+ }
3685
+ function contractEntity(contract, payload) {
3686
+ if (!isPlainObject(payload)) return null;
3687
+ if (!(contract.rootKey in payload)) return payload;
3688
+ const rooted = payload[contract.rootKey];
3689
+ return isPlainObject(rooted) ? rooted : null;
3690
+ }
3691
+ function validateOperationDataJson(operationId2, value) {
3692
+ const contract = CONTRACTS[operationId2];
3693
+ if (!contract) return [];
3694
+ const payload = parsePayload(value);
3695
+ if (payload === /* @__PURE__ */ Symbol.for("invalid-json")) {
3696
+ return ["Invalid input value for dataJson: must be valid JSON."];
3697
+ }
3698
+ const entity = contractEntity(contract, payload);
3699
+ if (!entity) {
3700
+ return [
3701
+ `Invalid input value for dataJson: ${contract.rootKey} payload must be a JSON object.`
3702
+ ];
3703
+ }
3704
+ const missing = (contract.required ?? []).filter(
3705
+ (field) => entity[field] === void 0 || entity[field] === null || entity[field] === ""
3706
+ );
3707
+ return [
3708
+ ...missing.map((field) => `${contract.rootKey}.${field} is required.`),
3709
+ ...validateFields(contract.fields, entity, [contract.rootKey])
3710
+ ].map(
3711
+ (error) => `Invalid input value for dataJson: ${error}`
3712
+ );
3713
+ }
3714
+ function assertOperationPayload(operationId2, body) {
3715
+ const errors = validateOperationDataJson(operationId2, body);
3716
+ if (errors.length === 0) return;
3717
+ throw new CliError({
3718
+ message: errors.join(" "),
3719
+ kind: "invalid_args",
3720
+ status: 400,
3721
+ exitCode: EXIT_CODES.invalidArgs
3722
+ });
3723
+ }
3724
+ function fieldSchema(contract) {
3725
+ const type = contract.type ?? "object";
3726
+ return {
3727
+ type,
3728
+ ...contract.enum ? { enum: [...contract.enum] } : {},
3729
+ ...contract.fields ? {
3730
+ properties: Object.fromEntries(
3731
+ Object.entries(contract.fields).map(([field, nested]) => [
3732
+ field,
3733
+ fieldSchema(nested)
3734
+ ])
3735
+ ),
3736
+ ...contract.required ? { required: [...contract.required] } : {},
3737
+ additionalProperties: true
3738
+ } : {},
3739
+ ...contract.items ? { items: fieldSchema(contract.items) } : {}
3740
+ };
3741
+ }
3742
+ function entitySchema(contract) {
3743
+ return {
3744
+ type: "object",
3745
+ properties: Object.fromEntries(
3746
+ Object.entries(contract.fields).map(([field, nested]) => [
3747
+ field,
3748
+ fieldSchema(nested)
3749
+ ])
3750
+ ),
3751
+ ...contract.required ? { required: [...contract.required] } : {},
3752
+ additionalProperties: true
3753
+ };
3754
+ }
3755
+ function exampleDataJsonForOperation(operationId2) {
3756
+ const contract = CONTRACTS[operationId2];
3757
+ if (!contract) return null;
3758
+ const entity = Object.fromEntries(
3759
+ (contract.required ?? []).map((field) => [field, "example"])
3760
+ );
3761
+ return { [contract.rootKey]: entity };
3762
+ }
3763
+ function dataJsonSchemaPropertyForOperation(operationId2, description) {
3764
+ const contract = CONTRACTS[operationId2];
3765
+ if (!contract) return null;
3766
+ const entity = entitySchema(contract);
3767
+ return {
3768
+ type: ["object", "string"],
3769
+ ...description ? { description } : {},
3770
+ anyOf: [
3771
+ {
3772
+ type: "string",
3773
+ description: "A JSON-serialized payload matching one of the object forms."
3774
+ },
3775
+ entity,
3776
+ {
3777
+ type: "object",
3778
+ properties: { [contract.rootKey]: entity },
3779
+ required: [contract.rootKey],
3780
+ additionalProperties: false
3781
+ }
3782
+ ]
3783
+ };
3784
+ }
3785
+
3460
3786
  // src/commands/entityCrud.ts
3461
3787
  var idSchema2 = z4.string().min(1);
3462
3788
  function toSingularResourceName(resourcePath) {
@@ -3630,6 +3956,7 @@ function registerEntityCrudCommands(program, config) {
3630
3956
  if (config.normalizeCreateBody) {
3631
3957
  body = config.normalizeCreateBody(body);
3632
3958
  }
3959
+ assertOperationPayload(`${config.command}.create`, body);
3633
3960
  assertRequiredCreateFields(body, config.rootKey, config.requiredCreateFields);
3634
3961
  const defaultCommand = `${config.command}.create`;
3635
3962
  const defaultField = `create_${toSingularResourceName(config.resourcePath)}`;
@@ -3662,6 +3989,7 @@ function registerEntityCrudCommands(program, config) {
3662
3989
  if (config.normalizeUpdateBody) {
3663
3990
  body = config.normalizeUpdateBody(body);
3664
3991
  }
3992
+ assertOperationPayload(`${config.command}.update`, body);
3665
3993
  const singular = toSingularResourceName(config.resourcePath);
3666
3994
  await runGraphqlMutationCommand({
3667
3995
  command: `${config.command}.update`,
@@ -4420,10 +4748,17 @@ var nameSchema = z10.string().min(1);
4420
4748
  var issueTypeSchema = z10.string().min(1).refine((value) => value === "short_term" || value === "long_term", {
4421
4749
  message: "Invalid issue type. Use one of: short_term, long_term."
4422
4750
  });
4751
+ var issueStatusSchema = z10.string().min(1).refine((value) => value === "open" || value === "completed", {
4752
+ message: "Invalid issue status. Use one of: open, completed."
4753
+ });
4423
4754
  var updateHelp4 = buildDataJsonHelp("issue", "update") ?? 'JSON object for issue or {"issue": {...}}';
4755
+ function parseIssueIndexEnum(value, schema) {
4756
+ if (value === void 0) return void 0;
4757
+ return schema.parse(String(value)).toUpperCase();
4758
+ }
4424
4759
  function registerIssueCommands(program) {
4425
4760
  const issues = program.command("issues").description("Issue operations");
4426
- issues.command("list").option("--page <page>").option("--per <per>").option("--issue-group-id <issueGroupId>").option("--start-date <startDate>").option("--issue-type <issueType>").option("--team-id <teamId>").option("--member-id <memberId>").option("--meeting-id <meetingId>").action(async (opts, cmd) => {
4761
+ issues.command("list").option("--page <page>").option("--per <per>").option("--issue-group-id <issueGroupId>").option("--start-date <startDate>").option("--issue-type <issueType>").option("--status <status>", "Issue status: open | completed").option("--team-id <teamId>").option("--member-id <memberId>").option("--meeting-id <meetingId>").action(async (opts, cmd) => {
4427
4762
  const context = await resolveCommandContext(cmd.optsWithGlobals());
4428
4763
  const organizationId = resolveOrganizationId(context.organizationId);
4429
4764
  await runGraphqlQueryCommand({
@@ -4436,11 +4771,24 @@ function registerIssueCommands(program) {
4436
4771
  per: opts.per,
4437
4772
  issue_group_id: opts.issueGroupId,
4438
4773
  start_date: opts.startDate,
4439
- issue_type: opts.issueType,
4774
+ issue_type: parseIssueIndexEnum(opts.issueType, issueTypeSchema),
4775
+ status: parseIssueIndexEnum(opts.status, issueStatusSchema),
4440
4776
  team_id: opts.teamId,
4441
4777
  member_id: opts.memberId,
4442
4778
  meeting_id: opts.meetingId
4443
4779
  }),
4780
+ variableTypes: {
4781
+ organization_id: "ID!",
4782
+ page: "Int",
4783
+ per: "Int",
4784
+ issue_group_id: "ID",
4785
+ start_date: "String",
4786
+ issue_type: "IssueType",
4787
+ status: "IssueStatus",
4788
+ team_id: "ID",
4789
+ member_id: "ID",
4790
+ meeting_id: "ID"
4791
+ },
4444
4792
  isList: true
4445
4793
  });
4446
4794
  });
@@ -6174,13 +6522,17 @@ function registerChildCommands(program, config) {
6174
6522
  ...config.searchRequiredOptions ? { requiredOptions: config.searchRequiredOptions } : {}
6175
6523
  };
6176
6524
  registerGraphqlSearchCommand(command, searchConfig);
6177
- command.command("create").requiredOption("--data-json <dataJson>").action(async (opts, cmd) => {
6525
+ command.command("create").requiredOption(
6526
+ "--data-json <dataJson>",
6527
+ buildDataJsonHelp(config.rootKey, "create") ?? void 0
6528
+ ).action(async (opts, cmd) => {
6178
6529
  const context = await resolveCommandContext(cmd.optsWithGlobals());
6179
6530
  const organizationId = resolveOrganizationId(context.organizationId);
6180
6531
  let body = normalizeBody2(String(opts.dataJson), config.rootKey);
6181
6532
  if (config.normalizeCreateBody) {
6182
6533
  body = config.normalizeCreateBody(body);
6183
6534
  }
6535
+ assertOperationPayload(`${config.command}.create`, body);
6184
6536
  assertRequiredParent(body, config.rootKey, config.parentFlag.replace(/-/g, "_"));
6185
6537
  await runGraphqlMutationCommand({
6186
6538
  command: `${config.command}.create`,
@@ -6193,11 +6545,15 @@ function registerChildCommands(program, config) {
6193
6545
  }
6194
6546
  });
6195
6547
  });
6196
- command.command("update").requiredOption("--id <id>").requiredOption("--data-json <dataJson>").action(async (opts, cmd) => {
6548
+ command.command("update").requiredOption("--id <id>").requiredOption(
6549
+ "--data-json <dataJson>",
6550
+ buildDataJsonHelp(config.rootKey, "update") ?? void 0
6551
+ ).action(async (opts, cmd) => {
6197
6552
  const context = await resolveCommandContext(cmd.optsWithGlobals());
6198
6553
  const organizationId = resolveOrganizationId(context.organizationId);
6199
6554
  const id = idSchema12.parse(opts.id);
6200
6555
  const body = normalizeBody2(String(opts.dataJson), config.rootKey);
6556
+ assertOperationPayload(`${config.command}.update`, body);
6201
6557
  await runGraphqlMutationCommand({
6202
6558
  command: `${config.command}.update`,
6203
6559
  operationName: config.updateOperationName,
@@ -14811,6 +15167,11 @@ var AUTOMATION_SELECTION = `{
14811
15167
  organizationId
14812
15168
  memberId
14813
15169
  agentId
15170
+ agentTriggerId
15171
+ scheduleStatus
15172
+ triggerEnabled
15173
+ cronExpression
15174
+ timezone
14814
15175
  createdAt
14815
15176
  updatedAt
14816
15177
  updateVersion
@@ -14837,6 +15198,29 @@ var AUTOMATION_RUN_SELECTION = `{
14837
15198
  targetId
14838
15199
  }
14839
15200
  }`;
15201
+ var AUTOMATION_RUN_STATUS_SELECTION = `{
15202
+ status
15203
+ automationTaskId
15204
+ automationTaskSlug
15205
+ agentId
15206
+ agentSlug
15207
+ agentTriggerId
15208
+ chatId
15209
+ chatSlug
15210
+ chatLane
15211
+ messageId
15212
+ runId
15213
+ executionId
15214
+ executionSlug
15215
+ traceId
15216
+ correlationId
15217
+ outputContentId
15218
+ outputContentSlug
15219
+ terminalReason
15220
+ error
15221
+ queuedAt
15222
+ completedAt
15223
+ }`;
14840
15224
  function asRecord7(value) {
14841
15225
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
14842
15226
  }
@@ -14967,15 +15351,58 @@ function normalizeAutomationRunPayload(payload) {
14967
15351
  const automationTaskId = asRequiredString(data?.automation_task_id);
14968
15352
  const automationTaskSlug = asRequiredString(data?.automation_task_slug);
14969
15353
  const agentId = asRequiredString(data?.agent_id);
14970
- const agentSlug = asRequiredString(data?.agent_slug);
14971
- const agentTriggerId = asRequiredString(data?.agent_trigger_id);
14972
- const executionId = asRequiredString(data?.execution_id);
14973
- const executionSlug = asRequiredString(data?.execution_slug);
14974
15354
  const executionStatus = asRequiredString(data?.execution_status);
14975
- const traceId = asRequiredString(data?.trace_id);
14976
- if (!data || !automationTaskId || !automationTaskSlug || !agentId || !agentSlug || !agentTriggerId || !executionId || !executionSlug || !executionStatus || !traceId) {
15355
+ if (!data || !automationTaskId || !automationTaskSlug || !agentId || !executionStatus) {
15356
+ throw new CliError({
15357
+ message: "Automation run response did not include automation identity and status.",
15358
+ kind: "malformed_response",
15359
+ status: 502,
15360
+ exitCode: EXIT_CODES.networkOrUpstream
15361
+ });
15362
+ }
15363
+ if (executionStatus === "queued") {
15364
+ const chatId = asRequiredString(data.chat_id);
15365
+ const chatSlug = asRequiredString(data.chat_slug);
15366
+ const chatLane = asRequiredString(data.chat_lane);
15367
+ const messageId = asRequiredString(data.message_id);
15368
+ const runId = asRequiredString(data.run_id);
15369
+ const memberId = asRequiredString(data.member_id);
15370
+ if (!chatId || !chatSlug || !chatLane || !messageId || !runId || !memberId) {
15371
+ throw new CliError({
15372
+ message: "Queued Automation response did not include chat, message, run, and member identity.",
15373
+ kind: "malformed_response",
15374
+ status: 502,
15375
+ exitCode: EXIT_CODES.networkOrUpstream
15376
+ });
15377
+ }
15378
+ return {
15379
+ ok: mutation.ok,
15380
+ status: mutation.status,
15381
+ errorCode: mutation.errorCode ?? null,
15382
+ errors: mutation.errors ?? null,
15383
+ run: {
15384
+ executed: data.executed === true,
15385
+ status: executionStatus,
15386
+ automation: { id: automationTaskId, slug: automationTaskSlug },
15387
+ agent: { id: agentId },
15388
+ trigger: { id: asRequiredString(data.agent_trigger_id) },
15389
+ queue: {
15390
+ chat: { id: chatId, slug: chatSlug, lane: chatLane },
15391
+ message_id: messageId,
15392
+ run_id: runId,
15393
+ member_id: memberId
15394
+ },
15395
+ readback_command: `wave automations run-status --id ${automationTaskSlug} --message-id ${messageId} --run-id ${runId}`
15396
+ }
15397
+ };
15398
+ }
15399
+ const agentSlug = asRequiredString(data.agent_slug);
15400
+ const executionId = asRequiredString(data.execution_id);
15401
+ const executionSlug = asRequiredString(data.execution_slug);
15402
+ const traceId = asRequiredString(data.trace_id);
15403
+ if (!agentSlug || !executionId || !executionSlug || !traceId) {
14977
15404
  throw new CliError({
14978
- message: "Automation run response did not include automation, execution, and trace evidence.",
15405
+ message: "Terminal Automation response did not include execution and trace evidence.",
14979
15406
  kind: "malformed_response",
14980
15407
  status: 502,
14981
15408
  exitCode: EXIT_CODES.networkOrUpstream
@@ -14998,7 +15425,7 @@ function normalizeAutomationRunPayload(payload) {
14998
15425
  slug: agentSlug
14999
15426
  },
15000
15427
  trigger: {
15001
- id: agentTriggerId,
15428
+ id: asRequiredString(data.agent_trigger_id),
15002
15429
  type: asRequiredString(data.trigger_type)
15003
15430
  },
15004
15431
  execution: {
@@ -15295,7 +15722,7 @@ function registerAtlasAuthoringCommands(program) {
15295
15722
  isShow: true
15296
15723
  });
15297
15724
  });
15298
- automations.command("run").description("Run one scheduled Automation by id or slug and return execution trace evidence").requiredOption("--id <idOrSlug>", "Automation id or slug").action(async (opts, cmd) => {
15725
+ automations.command("run").description("Queue one Automation by id or slug and return exact status readback identifiers").requiredOption("--id <idOrSlug>", "Automation id or slug").action(async (opts, cmd) => {
15299
15726
  const automationId = idSchema14.parse(opts.id);
15300
15727
  const context = await resolveCommandContext(cmd.optsWithGlobals());
15301
15728
  const organizationId = resolveOrganizationId(context.organizationId);
@@ -15309,6 +15736,33 @@ function registerAtlasAuthoringCommands(program) {
15309
15736
  transformMutationPayload: normalizeAutomationRunPayload
15310
15737
  });
15311
15738
  });
15739
+ automations.command("run-status").description("Read one queued or terminal Automation execution by its run identity").requiredOption("--id <idOrSlug>", "Automation id or slug returned by automations run").requiredOption("--message-id <messageId>", "Message id returned by automations run").requiredOption("--run-id <runId>", "Run id returned by automations run").action(async (opts, cmd) => {
15740
+ const automationId = idSchema14.parse(opts.id);
15741
+ const messageId = idSchema14.parse(opts.messageId);
15742
+ const runId = idSchema14.parse(opts.runId);
15743
+ const context = await resolveCommandContext(cmd.optsWithGlobals());
15744
+ const organizationId = resolveOrganizationId(context.organizationId);
15745
+ await runGraphqlQueryCommand({
15746
+ command: "automations.run-status",
15747
+ operationName: "AutomationsRunStatus",
15748
+ runtimeOptions: context.runtimeOptions,
15749
+ field: "automation_run_status",
15750
+ variables: {
15751
+ organization_id: organizationId,
15752
+ automation_id: automationId,
15753
+ message_id: messageId,
15754
+ run_id: runId
15755
+ },
15756
+ variableTypes: {
15757
+ organization_id: "ID!",
15758
+ automation_id: "ID!",
15759
+ message_id: "ID!",
15760
+ run_id: "ID!"
15761
+ },
15762
+ selectionSet: AUTOMATION_RUN_STATUS_SELECTION,
15763
+ isShow: true
15764
+ });
15765
+ });
15312
15766
  automations.command("run-show").description("Read one Automation run artifact by Agent id and execution slug").requiredOption("--agent-id <agentIdOrSlug>", "Agent id or slug").requiredOption("--execution-slug <executionSlug>", "Execution slug returned by automations run").action(async (opts, cmd) => {
15313
15767
  const agentId = idSchema14.parse(opts.agentId);
15314
15768
  const executionSlug = idSchema14.parse(opts.executionSlug);
@@ -15471,7 +15925,7 @@ import { join as join3 } from "path";
15471
15925
  import { createHash } from "crypto";
15472
15926
 
15473
15927
  // src/version.ts
15474
- var CLI_VERSION = "0.1.77";
15928
+ var CLI_VERSION = "0.1.80";
15475
15929
 
15476
15930
  // src/operationMetadata.ts
15477
15931
  var DISCOVERY_OPERATION_IDS = [
@@ -15594,6 +16048,7 @@ var READ_OPERATION_IDS = [
15594
16048
  "agents.show",
15595
16049
  "auth.status",
15596
16050
  "automations.run-show",
16051
+ "automations.run-status",
15597
16052
  "automations.show",
15598
16053
  "broadcast.community",
15599
16054
  "broadcast.list",
@@ -16008,7 +16463,7 @@ function operationId(path) {
16008
16463
  function inputKeyForArgument(argument) {
16009
16464
  return argument.name().replace(/<|>|\[|\]|\.\.\./g, "");
16010
16465
  }
16011
- function inputSchemaFor(command) {
16466
+ function inputSchemaFor(command, id) {
16012
16467
  const properties = {};
16013
16468
  const required = [];
16014
16469
  for (const argument of command.registeredArguments) {
@@ -16022,7 +16477,8 @@ function inputSchemaFor(command) {
16022
16477
  for (const option of command.options) {
16023
16478
  const key = option.attributeName();
16024
16479
  if (MACHINE_PRESENTATION_OPTIONS.has(key)) continue;
16025
- properties[key] = {
16480
+ const payloadSchema = key === "dataJson" ? dataJsonSchemaPropertyForOperation(id, option.description) : null;
16481
+ properties[key] = payloadSchema ?? {
16026
16482
  type: option.required ? "string" : option.optional ? ["boolean", "string"] : "boolean",
16027
16483
  ...option.description ? { description: option.description } : {}
16028
16484
  };
@@ -16035,12 +16491,17 @@ function inputSchemaFor(command) {
16035
16491
  required: required.sort()
16036
16492
  };
16037
16493
  }
16038
- function exampleFor(inputSchema) {
16494
+ function exampleFor(inputSchema, id) {
16039
16495
  const input = {};
16040
16496
  for (const key of inputSchema.required) {
16497
+ const payloadExample = key === "dataJson" ? exampleDataJsonForOperation(id) : null;
16498
+ if (payloadExample) {
16499
+ input[key] = payloadExample;
16500
+ continue;
16501
+ }
16041
16502
  const propertyType = inputSchema.properties[key]?.type;
16042
16503
  const types = Array.isArray(propertyType) ? propertyType : [propertyType];
16043
- input[key] = types.includes("array") ? ["example"] : types.includes("boolean") ? true : "example";
16504
+ input[key] = types.includes("object") ? {} : types.includes("array") ? ["example"] : types.includes("boolean") ? true : "example";
16044
16505
  }
16045
16506
  return [{ name: "minimal valid input", input }];
16046
16507
  }
@@ -16071,7 +16532,8 @@ function listInvocableCommands(program) {
16071
16532
  }
16072
16533
  function entryFor(command, metadata) {
16073
16534
  const path = commandPath(command);
16074
- const inputSchema = inputSchemaFor(command);
16535
+ const id = operationId(path);
16536
+ const inputSchema = inputSchemaFor(command, id);
16075
16537
  const summary = command.description() || `Run ${path.join(" ")}.`;
16076
16538
  return {
16077
16539
  id: operationId(path),
@@ -16084,7 +16546,7 @@ function entryFor(command, metadata) {
16084
16546
  atlasSafetyClass: metadata.atlasSafetyClass,
16085
16547
  inputSchema,
16086
16548
  outputSchema: outputSchema(),
16087
- examples: exampleFor(inputSchema),
16549
+ examples: exampleFor(inputSchema, id),
16088
16550
  machine: metadata.machine
16089
16551
  };
16090
16552
  }
@@ -16302,6 +16764,7 @@ var NAMESPACE_DOCS = {
16302
16764
  `wave automations create --summary "Weekly review" --agent-id 123 --ai-task-config '{"cycle":"repeat","repeatSchedule":{"interval":1,"frequency":"week","weekdays":[1,3],"at":"2:30 PM","starting":"2026-07-20T00:00:00.000Z","ending":"never","endingRuns":1,"endingDate":null,"timezone":"America/Phoenix"}}' --idempotency-key automation-create-001`,
16303
16765
  "wave automations show --id ATK-01",
16304
16766
  "wave automations run --id ATK-01",
16767
+ "wave automations run-status --id ATK-01 --message-id 123 --run-id run_123",
16305
16768
  "wave automations run-show --agent-id AGT-01 --execution-slug ART-1234567890",
16306
16769
  'wave automations update --id ATK-01 --expected-update-version 2026-07-17T12:00:00.123456Z --summary "Weekly project review"'
16307
16770
  ],
@@ -16314,11 +16777,12 @@ var NAMESPACE_DOCS = {
16314
16777
  "Only the API-defined ai_task_config field accepts validated JSON.",
16315
16778
  "Optional executor Agent linkage is sent only through the API-defined top-level agentId input.",
16316
16779
  "Update calls only updateTask with an exact opaque updateVersion token; it cannot change status, task type, ownership, or project linkage.",
16317
- "Run calls only runAutomationNow for an existing scheduled Automation and returns execution/trace evidence.",
16780
+ "Run calls only runAutomationNow and returns the exact queued chat, message, and run identity.",
16781
+ "Run-status reads queued or terminal execution and trace evidence using that exact identity.",
16318
16782
  "Run-show reads the persisted Automation run artifact through agentAutomationRun by Agent id and execution slug.",
16319
16783
  "Show uses the API-owned automation query by id; no slug fallback is implemented."
16320
16784
  ],
16321
- commands: ["create", "show", "run", "run-show", "update"]
16785
+ commands: ["create", "show", "run", "run-status", "run-show", "update"]
16322
16786
  }
16323
16787
  },
16324
16788
  broadcast: {
@@ -17209,7 +17673,7 @@ function machineEnvelope(params) {
17209
17673
  meta: { requestId: "machine" }
17210
17674
  };
17211
17675
  }
17212
- function isPlainObject(value) {
17676
+ function isPlainObject2(value) {
17213
17677
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
17214
17678
  }
17215
17679
  function hasAction2(command) {
@@ -17283,7 +17747,7 @@ async function readMachineInput(location) {
17283
17747
  }
17284
17748
  if (!raw.trim()) throw new Error("Machine input JSON is required.");
17285
17749
  const parsed = JSON.parse(raw);
17286
- if (!isPlainObject(parsed)) throw new Error("Machine input must be a JSON object.");
17750
+ if (!isPlainObject2(parsed)) throw new Error("Machine input must be a JSON object.");
17287
17751
  return parsed;
17288
17752
  }
17289
17753
  function inputDigest(input) {
@@ -17291,7 +17755,7 @@ function inputDigest(input) {
17291
17755
  }
17292
17756
  function canonicalJson(value) {
17293
17757
  if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
17294
- if (isPlainObject(value)) {
17758
+ if (isPlainObject2(value)) {
17295
17759
  return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(",")}}`;
17296
17760
  }
17297
17761
  return JSON.stringify(value);
@@ -17321,18 +17785,18 @@ function parseTrustedJsonFromFd(fd, predicate) {
17321
17785
  }
17322
17786
  }
17323
17787
  function isTrustedAuth(value) {
17324
- return isPlainObject(value) && typeof value.token === "string" && value.token.trim() !== "" && typeof value.baseUrl === "string" && value.baseUrl.trim() !== "" && typeof value.organizationId === "string" && value.organizationId.trim() !== "" && typeof value.actorId === "string" && value.actorId.trim() !== "";
17788
+ return isPlainObject2(value) && typeof value.token === "string" && value.token.trim() !== "" && typeof value.baseUrl === "string" && value.baseUrl.trim() !== "" && typeof value.organizationId === "string" && value.organizationId.trim() !== "" && typeof value.actorId === "string" && value.actorId.trim() !== "";
17325
17789
  }
17326
17790
  function isTrustedApproval(value) {
17327
- return isPlainObject(value) && typeof value.operation === "string" && typeof value.inputDigest === "string" && typeof value.cliVersion === "string" && typeof value.registryDigest === "string" && typeof value.organizationId === "string" && value.organizationId.trim() !== "" && typeof value.actorId === "string" && value.actorId.trim() !== "";
17791
+ return isPlainObject2(value) && typeof value.operation === "string" && typeof value.inputDigest === "string" && typeof value.cliVersion === "string" && typeof value.registryDigest === "string" && typeof value.organizationId === "string" && value.organizationId.trim() !== "" && typeof value.actorId === "string" && value.actorId.trim() !== "";
17328
17792
  }
17329
17793
  function operationCommand(program, id) {
17330
17794
  return listInvocableCommands(program).find((command) => commandId(command) === id);
17331
17795
  }
17332
- function typeMatches(value, type) {
17796
+ function typeMatches2(value, type) {
17333
17797
  if (type === "array") return Array.isArray(value);
17334
17798
  if (type === "null") return value === null;
17335
- if (type === "object") return isPlainObject(value);
17799
+ if (type === "object") return isPlainObject2(value);
17336
17800
  return typeof value === type;
17337
17801
  }
17338
17802
  function validateInput(operation, input) {
@@ -17341,16 +17805,19 @@ function validateInput(operation, input) {
17341
17805
  const missing = operation.inputSchema.required.filter(
17342
17806
  (key) => input[key] === void 0 || input[key] === null || input[key] === ""
17343
17807
  );
17344
- const typeErrors = Object.entries(input).flatMap(([key, value]) => {
17808
+ const valueErrors = Object.entries(input).flatMap(([key, value]) => {
17345
17809
  const property = operation.inputSchema.properties[key];
17346
17810
  if (!property) return [];
17347
17811
  const types = Array.isArray(property.type) ? property.type : [property.type];
17348
- return types.some((type) => typeMatches(value, type)) ? [] : [`Invalid input type for ${key}: expected ${types.join(" or ")}.`];
17812
+ if (!types.some((type) => typeMatches2(value, type))) {
17813
+ return [`Invalid input type for ${key}: expected ${types.join(" or ")}.`];
17814
+ }
17815
+ return key === "dataJson" ? validateOperationDataJson(operation.id, value) : [];
17349
17816
  });
17350
17817
  return [
17351
17818
  ...unknown.map((key) => `Unknown input property: ${key}`),
17352
17819
  ...missing.map((key) => `Missing required input property: ${key}`),
17353
- ...typeErrors
17820
+ ...valueErrors
17354
17821
  ];
17355
17822
  }
17356
17823
  function optionValue(value) {