monday-cli 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/CHANGELOG.md +250 -0
  2. package/README.md +87 -45
  3. package/dist/api/assets.d.ts +3 -3
  4. package/dist/api/board-metadata.d.ts +7 -4
  5. package/dist/api/board-metadata.d.ts.map +1 -1
  6. package/dist/api/board-metadata.js +21 -6
  7. package/dist/api/board-metadata.js.map +1 -1
  8. package/dist/api/column-types.d.ts +14 -7
  9. package/dist/api/column-types.d.ts.map +1 -1
  10. package/dist/api/column-types.js +14 -7
  11. package/dist/api/column-types.js.map +1 -1
  12. package/dist/api/error-decoration.d.ts +124 -0
  13. package/dist/api/error-decoration.d.ts.map +1 -0
  14. package/dist/api/error-decoration.js +161 -0
  15. package/dist/api/error-decoration.js.map +1 -0
  16. package/dist/api/fetch-transport-helpers.d.ts +97 -0
  17. package/dist/api/fetch-transport-helpers.d.ts.map +1 -0
  18. package/dist/api/fetch-transport-helpers.js +175 -0
  19. package/dist/api/fetch-transport-helpers.js.map +1 -0
  20. package/dist/api/file-column-set.d.ts +388 -82
  21. package/dist/api/file-column-set.d.ts.map +1 -1
  22. package/dist/api/file-column-set.js +466 -88
  23. package/dist/api/file-column-set.js.map +1 -1
  24. package/dist/api/multipart-transport.d.ts +95 -60
  25. package/dist/api/multipart-transport.d.ts.map +1 -1
  26. package/dist/api/multipart-transport.js +102 -120
  27. package/dist/api/multipart-transport.js.map +1 -1
  28. package/dist/api/transport.d.ts.map +1 -1
  29. package/dist/api/transport.js +2 -99
  30. package/dist/api/transport.js.map +1 -1
  31. package/dist/cli/program.js +1 -1
  32. package/dist/cli/program.js.map +1 -1
  33. package/dist/commands/auth/login.js +1 -1
  34. package/dist/commands/auth/login.js.map +1 -1
  35. package/dist/commands/auth/logout.js +1 -1
  36. package/dist/commands/auth/logout.js.map +1 -1
  37. package/dist/commands/board/column-create.d.ts +20 -2
  38. package/dist/commands/board/column-create.d.ts.map +1 -1
  39. package/dist/commands/board/column-create.js +191 -20
  40. package/dist/commands/board/column-create.js.map +1 -1
  41. package/dist/commands/board/describe.d.ts +5 -3
  42. package/dist/commands/board/describe.d.ts.map +1 -1
  43. package/dist/commands/board/describe.js +12 -4
  44. package/dist/commands/board/describe.js.map +1 -1
  45. package/dist/commands/completion.js +1 -1
  46. package/dist/commands/completion.js.map +1 -1
  47. package/dist/commands/dev/configure.js +1 -1
  48. package/dist/commands/dev/configure.js.map +1 -1
  49. package/dist/commands/dev/discover.js +1 -1
  50. package/dist/commands/dev/discover.js.map +1 -1
  51. package/dist/commands/dev/doctor.js +1 -1
  52. package/dist/commands/dev/doctor.js.map +1 -1
  53. package/dist/commands/dev/epic/items.js +2 -2
  54. package/dist/commands/dev/epic/items.js.map +1 -1
  55. package/dist/commands/dev/epic/list.js +2 -2
  56. package/dist/commands/dev/epic/list.js.map +1 -1
  57. package/dist/commands/dev/release/list.js +2 -2
  58. package/dist/commands/dev/release/list.js.map +1 -1
  59. package/dist/commands/dev/sprint/current.js +2 -2
  60. package/dist/commands/dev/sprint/current.js.map +1 -1
  61. package/dist/commands/dev/sprint/items.js +2 -2
  62. package/dist/commands/dev/sprint/items.js.map +1 -1
  63. package/dist/commands/dev/sprint/list.js +2 -2
  64. package/dist/commands/dev/sprint/list.js.map +1 -1
  65. package/dist/commands/dev/task/block.js +2 -2
  66. package/dist/commands/dev/task/block.js.map +1 -1
  67. package/dist/commands/dev/task/done.js +2 -2
  68. package/dist/commands/dev/task/done.js.map +1 -1
  69. package/dist/commands/dev/task/list.js +2 -2
  70. package/dist/commands/dev/task/list.js.map +1 -1
  71. package/dist/commands/dev/task/start.js +2 -2
  72. package/dist/commands/dev/task/start.js.map +1 -1
  73. package/dist/commands/doc/get.js +1 -1
  74. package/dist/commands/doc/get.js.map +1 -1
  75. package/dist/commands/doc/list.js +1 -1
  76. package/dist/commands/doc/list.js.map +1 -1
  77. package/dist/commands/emit.d.ts.map +1 -1
  78. package/dist/commands/emit.js +19 -16
  79. package/dist/commands/emit.js.map +1 -1
  80. package/dist/commands/item/clear.d.ts.map +1 -1
  81. package/dist/commands/item/clear.js +15 -41
  82. package/dist/commands/item/clear.js.map +1 -1
  83. package/dist/commands/item/create.d.ts +93 -1
  84. package/dist/commands/item/create.d.ts.map +1 -1
  85. package/dist/commands/item/create.js +474 -53
  86. package/dist/commands/item/create.js.map +1 -1
  87. package/dist/commands/item/search.js +7 -7
  88. package/dist/commands/item/search.js.map +1 -1
  89. package/dist/commands/item/set.d.ts +1 -0
  90. package/dist/commands/item/set.d.ts.map +1 -1
  91. package/dist/commands/item/set.js +94 -1
  92. package/dist/commands/item/set.js.map +1 -1
  93. package/dist/commands/item/time-track/start.js +2 -2
  94. package/dist/commands/item/time-track/start.js.map +1 -1
  95. package/dist/commands/item/time-track/stop.js +2 -2
  96. package/dist/commands/item/time-track/stop.js.map +1 -1
  97. package/dist/commands/item/update.d.ts +128 -11
  98. package/dist/commands/item/update.d.ts.map +1 -1
  99. package/dist/commands/item/update.js +784 -82
  100. package/dist/commands/item/update.js.map +1 -1
  101. package/dist/commands/item/upload.js +5 -5
  102. package/dist/commands/item/upload.js.map +1 -1
  103. package/dist/commands/item/watch.js +2 -2
  104. package/dist/commands/item/watch.js.map +1 -1
  105. package/dist/commands/notification/send.js +1 -1
  106. package/dist/commands/notification/send.js.map +1 -1
  107. package/dist/commands/update/upload.js +3 -3
  108. package/dist/commands/update/upload.js.map +1 -1
  109. package/dist/commands/user/team-add-members.js +1 -1
  110. package/dist/commands/user/team-add-members.js.map +1 -1
  111. package/dist/commands/user/team-create.js +1 -1
  112. package/dist/commands/user/team-create.js.map +1 -1
  113. package/dist/commands/user/team-remove-members.js +1 -1
  114. package/dist/commands/user/team-remove-members.js.map +1 -1
  115. package/dist/commands/webhook/create.js +1 -1
  116. package/dist/commands/webhook/create.js.map +1 -1
  117. package/dist/commands/webhook/delete.js +1 -1
  118. package/dist/commands/webhook/delete.js.map +1 -1
  119. package/dist/commands/webhook/list.js +1 -1
  120. package/dist/commands/webhook/list.js.map +1 -1
  121. package/dist/utils/file-source.d.ts +109 -0
  122. package/dist/utils/file-source.d.ts.map +1 -1
  123. package/dist/utils/file-source.js +123 -0
  124. package/dist/utils/file-source.js.map +1 -1
  125. package/dist/utils/output/select.d.ts +22 -0
  126. package/dist/utils/output/select.d.ts.map +1 -1
  127. package/dist/utils/output/select.js +30 -0
  128. package/dist/utils/output/select.js.map +1 -1
  129. package/dist/utils/output/table.d.ts +10 -0
  130. package/dist/utils/output/table.d.ts.map +1 -1
  131. package/dist/utils/output/table.js +40 -3
  132. package/dist/utils/output/table.js.map +1 -1
  133. package/package.json +2 -1
@@ -77,13 +77,15 @@ import { buildResolutionContexts, } from '../../api/resolution-context.js';
77
77
  import { lookupItemBoard, lookupItemBoardWithHierarchy, } from '../../api/item-board-lookup.js';
78
78
  import { SourceAggregator, mergeCacheAge, mergeSourceWithPreflight, } from '../../api/source-aggregator.js';
79
79
  import { resolveAndTranslate } from '../../api/resolution-pass.js';
80
- import { executeFileColumnSet, preCheckM38FileDispatch, } from '../../api/file-column-set.js';
80
+ import { executeFileColumnSet, dispatchFileLegsSequentially, preCheckM38FileDispatch, } from '../../api/file-column-set.js';
81
+ import { addFileToColumn } from '../../api/assets.js';
81
82
  import { foldAndRemap, mergeResolverWarningsIntoError, } from '../../api/resolver-error-fold.js';
83
+ import { projectCauseForEnvelope } from '../../api/error-decoration.js';
82
84
  import { planCreate } from '../../api/dry-run.js';
83
85
  import { loadBoardMetadata } from '../../api/board-metadata.js';
84
86
  import { assertResponseFieldPresent } from '../../api/response-root.js';
85
87
  import { unwrapOrThrow } from '../../utils/parse-boundary.js';
86
- import { precheckLocalFile } from '../../utils/file-source.js';
88
+ import { precheckLocalFile, isStdinFileSetSource, readStdinFileSource, resolveStdinFilename, STDIN_FILE_SENTINEL, } from '../../utils/file-source.js';
87
89
  import { invalidateBoard } from '../../api/cache.js';
88
90
  /**
89
91
  * Dedupes resolver warnings by `code + message + details.token`.
@@ -171,6 +173,73 @@ const itemCreateOutputSchema = z.object({
171
173
  group_id: z.string().nullable(),
172
174
  parent_id: ItemIdSchema.optional(),
173
175
  });
176
+ /**
177
+ * v0.8-M46 create-time multi-file `--set` envelope `data` shape
178
+ * (D6 closure). Two-leg-group dispatch: leg-1 `create_item` (or
179
+ * `create_subitem`) bundling non-file `column_values`, then N
180
+ * sequential `add_file_to_column` legs (legs 2..N+1) against
181
+ * the new item ID. The envelope projects:
182
+ *
183
+ * - `operation: 'item_create_with_files'` literal discriminator
184
+ * (plural distinguishes from M43's single-file
185
+ * `'item_create'` literal at `itemCreateOutputSchema`).
186
+ * - Leg-1's `ItemCreateOutput` shape (item ID + name + board_id
187
+ * + group_id + parent_id for subitems) inlined as `item`.
188
+ * - Per-leg asset projections — one per file column that landed,
189
+ * length N on success; length 0..N-1 on partial failure
190
+ * (reflecting columns landed before the failing leg).
191
+ * - `applied_file_columns: [<col_ids>]` echo (length 1..N on
192
+ * success; length 0..N-1 on partial failure).
193
+ *
194
+ * Atomicity-envelope shape on partial failure (D2 closure):
195
+ * extends M43's `'create_then_file_upload_partial_failure'`
196
+ * discriminator at `details.reason` with always-present
197
+ * `details.applied_file_columns: []` slot (length 0..N-1
198
+ * reflecting file columns landed after leg-1 succeeded but
199
+ * before the failing leg). Length 0 corresponds to M43's
200
+ * single-file failure case; length k>0 corresponds to M46
201
+ * multi-file partial failure after k file legs succeeded.
202
+ *
203
+ * **Status: schema landed at v0.8-M46 pre-flight contract diff
204
+ * (Codex R1 P2-2 fix); runtime emit shipped at v0.8-M46 IMPL.**
205
+ * `runItemCreateFileMultiDispatch` (below) emits against this
206
+ * schema on the create-time multi-file path.
207
+ */
208
+ export const itemCreateWithFilesOutputSchema = z.object({
209
+ operation: z.literal('item_create_with_files'),
210
+ item: itemCreateOutputSchema,
211
+ assets: z
212
+ .array(z
213
+ .object({
214
+ column_id: z.string().min(1),
215
+ filename: z.string().min(1),
216
+ file_size_bytes: z.number().int().nonnegative(),
217
+ asset: z
218
+ .object({
219
+ id: z.string().min(1),
220
+ name: z.string().min(1),
221
+ })
222
+ .loose(),
223
+ })
224
+ .strict())
225
+ .min(2),
226
+ applied_file_columns: z.array(z.string().min(1)).min(2),
227
+ });
228
+ /**
229
+ * Command-registry output union for `monday item create`. The JSON /
230
+ * single-file create paths emit the canonical `ItemCreateOutput`
231
+ * (v0.7-M43 deliberately kept its single-file two-leg path on the
232
+ * canonical shape); v0.8-M46 multi-file create emits the distinct
233
+ * `item_create_with_files` shape. Both are single-resource `data`
234
+ * payloads, so both belong in the advertised `outputSchema` union
235
+ * (agents discriminate on the presence of `operation` /
236
+ * `applied_file_columns`). Mirrors `item.update`'s union admitting
237
+ * its single-item file shapes.
238
+ */
239
+ export const itemCreateCommandOutputSchema = z.union([
240
+ itemCreateOutputSchema,
241
+ itemCreateWithFilesOutputSchema,
242
+ ]);
174
243
  const createItemResponseSchema = z
175
244
  .object({
176
245
  id: ItemIdSchema,
@@ -199,6 +268,14 @@ const inputSchema = z
199
268
  position: positionEnum.optional(),
200
269
  relativeTo: ItemIdSchema.optional(),
201
270
  createLabelsIfMissing: z.boolean().optional(),
271
+ // v0.8-M47 (D7 fold): companion to a stdin file `--set
272
+ // <file-col>=-` source — sets Monday's wire `Asset.name` on the
273
+ // leg-2 `add_file_to_column`. OPTIONAL; `.min(1)` rejects an empty
274
+ // `--filename ""` at the parse boundary (Monday `500`s empty
275
+ // filenames). Default on a stdin source: `DEFAULT_STDIN_FILENAME`
276
+ // (`"blob"`). Consulted only on a `<file-col>=-` dispatch; ignored
277
+ // otherwise. See `update.ts` schema for the full rationale.
278
+ filename: z.string().min(1).optional(),
202
279
  })
203
280
  .strict();
204
281
  /**
@@ -460,13 +537,13 @@ const resolveCreateMode = async (inputs) => {
460
537
  `creation is deferred. Use a classic board ` +
461
538
  `(hierarchy_type null/"classic"). v0.3 M28 Decision 11 closure: ` +
462
539
  `Monday's sub_items_board carries no subtasks column at API ` +
463
- `2026-01, so depth-2 subitems have no data-model home — v0.8 ` +
540
+ `2026-01, so depth-2 subitems have no data-model home — v0.9 ` +
464
541
  `picks the feature up if Monday surfaces the capability.`, {
465
542
  details: {
466
543
  parent_item_id: dispatch.parentItemId,
467
544
  parent_board_id: parent.boardId,
468
545
  hierarchy_type: parent.hierarchyType,
469
- deferred_to: 'v0.8',
546
+ deferred_to: 'v0.9',
470
547
  },
471
548
  });
472
549
  }
@@ -561,7 +638,7 @@ export const itemCreateCommand = {
561
638
  // create-or-update use `monday item upsert` (M12).
562
639
  idempotent: false,
563
640
  inputSchema,
564
- outputSchema: itemCreateOutputSchema,
641
+ outputSchema: itemCreateCommandOutputSchema,
565
642
  attach: (program, ctx) => {
566
643
  const noun = ensureSubcommand(program, 'item', 'Item commands');
567
644
  noun
@@ -572,6 +649,7 @@ export const itemCreateCommand = {
572
649
  .option('--group <gid>', 'group ID (top-level only; default = board\'s default group)')
573
650
  .option('--set <expr>', 'repeatable <col>=<val> column write (bundled into create_item.column_values)', (value, prev) => [...prev, value], [])
574
651
  .option('--set-raw <expr>', 'repeatable <col>=<json> raw write (escape hatch — bypasses friendly translator)', (value, prev) => [...prev, value], [])
652
+ .option('--filename <name>', "Asset.name for a stdin file `--set <file-col>=-` source (default \"blob\")")
575
653
  .option('--parent <iid>', 'create as subitem of this parent item ID')
576
654
  .option('--position <method>', 'item placement: "before" | "after" (requires --relative-to)')
577
655
  .option('--relative-to <iid>', 'item ID for --position; must be on the same board')
@@ -629,9 +707,17 @@ export const itemCreateCommand = {
629
707
  // entry; branches into the v0.7-M43 two-leg dispatch
630
708
  // helper {@link runItemCreateFileDispatch} (carve-out
631
709
  // fold from v0.6-M38's permanent rejection).
710
+ // - `kind: 'file_create_multi'` — clean multi-file
711
+ // `--set` entries with distinct file columns; branches
712
+ // into the v0.8-M46 two-leg-group dispatch helper
713
+ // `runItemCreateFileMultiDispatch` (D2 carve-out fold
714
+ // from v0.6-M38's universal multi-file rejection;
715
+ // runtime body shipped at v0.8-M46 IMPL).
632
716
  // - Throws `usage_error` with
633
- // `details.reason: 'multi_file_set_unsupported'` on 2+
634
- // file `--set` entries (universal mutex rule). The
717
+ // `details.reason: 'duplicate_resolved_file_columns'` when
718
+ // 2+ file `--set` entries resolve to the same column ID
719
+ // (mirrors JSON path's cross-token duplicate-resolved-ID
720
+ // contract; M46 R1 P2-1 fix). The
635
721
  // `'mixed_file_and_value_sets'` rule is SUPPRESSED on
636
722
  // `'item_create'` callShape per the v0.7-M43 D6 mixed-
637
723
  // rule asymmetry — `create_item` natively bundles
@@ -643,6 +729,7 @@ export const itemCreateCommand = {
643
729
  let m38CacheAge = null;
644
730
  let m38Warnings = [];
645
731
  let m38FileCreate;
732
+ let m38FileCreateMulti;
646
733
  if (setEntries.length > 0) {
647
734
  const m38 = await preCheckM38FileDispatch({
648
735
  client,
@@ -660,6 +747,12 @@ export const itemCreateCommand = {
660
747
  if (m38.kind === 'file_create') {
661
748
  m38FileCreate = m38;
662
749
  }
750
+ if (m38.kind === 'file_create_multi') {
751
+ // v0.8-M46 D2 carve-out fold. Hold the file_create_multi
752
+ // slot for the two-leg-group multi-file dispatch helper
753
+ // below (runtime body shipped at v0.8-M46 IMPL).
754
+ m38FileCreateMulti = m38;
755
+ }
663
756
  }
664
757
  // v0.6-M38 → v0.7-M43 D6 fold — branch into the two-leg
665
758
  // dispatch helper. {@link runItemCreateFileDispatch} runs
@@ -700,6 +793,42 @@ export const itemCreateCommand = {
700
793
  });
701
794
  return;
702
795
  }
796
+ // v0.8-M46 D2 carve-out fold — create-time multi-file
797
+ // dispatch path. argv parse + create-mode resolution + M38
798
+ // pre-check (which returned `kind: 'file_create_multi'`
799
+ // here) have already run as live contract; the two-leg-
800
+ // group multi-file body runs leg-1 `create_item` then N
801
+ // sequential `add_file_to_column` legs (runtime body
802
+ // shipped at v0.8-M46 IMPL).
803
+ if (m38FileCreateMulti !== undefined) {
804
+ await runItemCreateFileMultiDispatch({
805
+ parsed,
806
+ client,
807
+ multipart,
808
+ ctx,
809
+ programOpts: program.opts(),
810
+ apiVersion,
811
+ createMode,
812
+ resolveBoardId,
813
+ setEntries,
814
+ rawEntries,
815
+ m38: m38FileCreateMulti,
816
+ preflightSource: createModeResult.preflightSource,
817
+ preflightCacheAgeSeconds: createModeResult.preflightCacheAgeSeconds,
818
+ metaSource: m38Source,
819
+ metaCacheAgeSeconds: m38CacheAge,
820
+ preflightWarnings: m38Warnings,
821
+ dateResolution,
822
+ peopleResolution,
823
+ tagResolution,
824
+ relationResolution,
825
+ isDryRun: globalFlags.dryRun,
826
+ noCache: globalFlags.noCache,
827
+ retries: globalFlags.retry,
828
+ toEmit,
829
+ });
830
+ return;
831
+ }
703
832
  if (globalFlags.dryRun) {
704
833
  let result;
705
834
  try {
@@ -1052,14 +1181,21 @@ const executeCreateSubitem = async (client, inputs) => {
1052
1181
  * leg-2 failure means no file mutation occurred wire-side.
1053
1182
  */
1054
1183
  const runItemCreateFileDispatch = async (inputs) => {
1055
- // 1) Upfront local file pre-check. Atomicity-before-wire per
1056
- // cli-design §5.8: pre-checks fire BEFORE any wire round-trip
1057
- // so a bad path surfaces `usage_error` (exit 1) with
1058
- // `details.reason: 'file_not_readable'` / `'file_empty'`
1059
- // without burning either wire leg. R-v0.6-NEW-1 4th-consumer
1060
- // site (M31 upload + M38 single-item + M42 file-bulk + here);
1061
- // 5-consumer graduation threshold not yet hit.
1062
- const precheck = await precheckLocalFile(inputs.m38.rawValue);
1184
+ // 1) Resolve the file source. v0.8-M47 (D7 fold) adds the stdin
1185
+ // `<file-col>=-` source alongside the path source:
1186
+ // - path: upfront `precheckLocalFile`. Atomicity-before-wire per
1187
+ // cli-design §5.8 a bad path surfaces `usage_error` (exit 1)
1188
+ // with `details.reason: 'file_not_readable'` / `'file_empty'`
1189
+ // BEFORE either wire leg; the Blob build stays deferred to
1190
+ // `executeFileColumnSet` after leg-1. R-v0.6-NEW-1 consumer.
1191
+ // - stdin: a `<file-col>=-` sentinel (scope-gated to the sole
1192
+ // file entry by `routeFileColumnDispatch`). The read is DEFERRED
1193
+ // here: dry-run must not consume stdin, and the live read fires
1194
+ // once BEFORE leg-1 so an empty pipe rejects without orphaning a
1195
+ // created item.
1196
+ const fileSource = isStdinFileSetSource(inputs.m38.rawValue)
1197
+ ? { kind: 'stdin', filename: resolveStdinFilename(inputs.parsed.filename) }
1198
+ : { kind: 'path', precheck: await precheckLocalFile(inputs.m38.rawValue) };
1063
1199
  // 2) Partition setEntries: the file entry's `token` matches
1064
1200
  // `inputs.m38.token` (the pre-check identified it); every
1065
1201
  // other token goes to leg-1's column_values.
@@ -1115,14 +1251,25 @@ const runItemCreateFileDispatch = async (inputs) => {
1115
1251
  // dry-run shape minus `item_id` (the item doesn't exist yet).
1116
1252
  // `file_path` is the argv-derived raw value per cli-design §6.4;
1117
1253
  // resolved absolute path lives in pre-check rejections, not in
1118
- // the success-shaped dry-run envelope.
1119
- const fileEntry = {
1120
- operation: 'add_file_to_column',
1121
- column_id: inputs.m38.columnId,
1122
- file_path: inputs.m38.rawValue,
1123
- filename: precheck.filename,
1124
- file_size_bytes: precheck.fileSizeBytes,
1125
- };
1254
+ // the success-shaped dry-run envelope. v0.8-M47 (D4): a stdin
1255
+ // source echoes `file_path: '-'` + `filename` but OMITS
1256
+ // `file_size_bytes` (a stream can't be `fs.stat`'d without
1257
+ // consuming it; additive per §6.4, so omission is non-breaking)
1258
+ // and MUST NOT read stdin here (dry-run is a preview).
1259
+ const fileEntry = fileSource.kind === 'stdin'
1260
+ ? {
1261
+ operation: 'add_file_to_column',
1262
+ column_id: inputs.m38.columnId,
1263
+ file_path: STDIN_FILE_SENTINEL,
1264
+ filename: fileSource.filename,
1265
+ }
1266
+ : {
1267
+ operation: 'add_file_to_column',
1268
+ column_id: inputs.m38.columnId,
1269
+ file_path: inputs.m38.rawValue,
1270
+ filename: fileSource.precheck.filename,
1271
+ file_size_bytes: fileSource.precheck.fileSizeBytes,
1272
+ };
1126
1273
  const plannedChanges = [
1127
1274
  ...planResult.plannedChanges,
1128
1275
  fileEntry,
@@ -1212,6 +1359,14 @@ const runItemCreateFileDispatch = async (inputs) => {
1212
1359
  // `column_values: null` distinctly from an empty map).
1213
1360
  const translated = resolutionResult.translated;
1214
1361
  const columnValues = translated.length === 0 ? null : bundleColumnValues(translated);
1362
+ // v0.8-M47: when the file source is stdin, buffer it ONCE here —
1363
+ // BEFORE leg-1's `create_item` — so an empty / unwired pipe rejects
1364
+ // (`usage_error`) without orphaning a created item. A path source
1365
+ // keeps its Blob build deferred to `executeFileColumnSet` below
1366
+ // (after leg-1) so a leg-1 failure doesn't pay for the read.
1367
+ const stdinSource = fileSource.kind === 'stdin'
1368
+ ? await readStdinFileSource(inputs.ctx.stdin, fileSource.filename)
1369
+ : undefined;
1215
1370
  // Leg-1: create_item or create_subitem. F4 remap on failure
1216
1371
  // mirrors the JSON path's catch arm (cache-served resolution +
1217
1372
  // Monday rejecting as `validation_failed` → check live archived
@@ -1263,24 +1418,47 @@ const runItemCreateFileDispatch = async (inputs) => {
1263
1418
  // Leg-2: add_file_to_column via M31's multipart fetcher. On
1264
1419
  // success, build the create envelope below; on `MondayCliError`,
1265
1420
  // surface the D1 orphan-warn envelope with the freshly-created
1266
- // item ID as the recovery handle.
1267
- const fileEntry = {
1268
- columnId: inputs.m38.columnId,
1269
- columnType: 'file',
1270
- rawValue: inputs.m38.rawValue,
1271
- filePath: precheck.filePath,
1272
- filename: precheck.filename,
1273
- fileSizeBytes: precheck.fileSizeBytes,
1274
- };
1421
+ // item ID as the recovery handle. The source diverges: a path leg
1422
+ // builds its Blob from the pre-checked path inside
1423
+ // `executeFileColumnSet`; a stdin leg (v0.8-M47) dispatches the
1424
+ // Blob already buffered before leg-1 directly via `addFileToColumn`.
1275
1425
  try {
1276
- await executeFileColumnSet({
1277
- client: inputs.client,
1278
- multipart: inputs.multipart,
1279
- itemId: leg1Result.projected.id,
1280
- entry: fileEntry,
1281
- signal: inputs.ctx.signal,
1282
- retries: inputs.retries,
1283
- });
1426
+ if (fileSource.kind === 'stdin') {
1427
+ /* c8 ignore next 6 — defensive: a stdin source buffered
1428
+ `stdinSource` before leg-1 above; the guard exists for TS
1429
+ narrowing (kind 'stdin' ⟹ stdinSource defined). */
1430
+ if (stdinSource === undefined) {
1431
+ throw new ApiError('internal_error', 'runItemCreateFileDispatch: stdin source not buffered before leg-2');
1432
+ }
1433
+ await addFileToColumn({
1434
+ client: inputs.client,
1435
+ multipart: inputs.multipart,
1436
+ itemId: leg1Result.projected.id,
1437
+ columnId: inputs.m38.columnId,
1438
+ file: stdinSource.blob,
1439
+ filename: stdinSource.filename,
1440
+ signal: inputs.ctx.signal,
1441
+ retries: inputs.retries,
1442
+ });
1443
+ }
1444
+ else {
1445
+ const fileEntry = {
1446
+ columnId: inputs.m38.columnId,
1447
+ columnType: 'file',
1448
+ rawValue: inputs.m38.rawValue,
1449
+ filePath: fileSource.precheck.filePath,
1450
+ filename: fileSource.precheck.filename,
1451
+ fileSizeBytes: fileSource.precheck.fileSizeBytes,
1452
+ };
1453
+ await executeFileColumnSet({
1454
+ client: inputs.client,
1455
+ multipart: inputs.multipart,
1456
+ itemId: leg1Result.projected.id,
1457
+ entry: fileEntry,
1458
+ signal: inputs.ctx.signal,
1459
+ retries: inputs.retries,
1460
+ });
1461
+ }
1284
1462
  }
1285
1463
  catch (err) {
1286
1464
  if (err instanceof MondayCliError) {
@@ -1304,19 +1482,11 @@ const runItemCreateFileDispatch = async (inputs) => {
1304
1482
  // shape; the remapped error embeds as `details.cause` JSON
1305
1483
  // projection so the agent sees `{code, message, details?}`
1306
1484
  // for the underlying failure. Error.cause threads the
1307
- // remapped error for stack debugging in `--debug` mode.
1308
- const causeProjection = {
1309
- code: remapped.code,
1310
- message: remapped.message,
1311
- };
1312
- // Defensive: M31's wire-error rewraps (and foldAndRemap) always
1313
- // populate `details` on the returned error in practice; the
1314
- // undefined-arm exists only to satisfy the optional shape on
1315
- // the `MondayCliError` type.
1316
- /* c8 ignore next 3 */
1317
- if (remapped.details !== undefined) {
1318
- causeProjection.details = remapped.details;
1319
- }
1485
+ // remapped error for stack debugging in `--debug` mode. The
1486
+ // shared `projectCauseForEnvelope` builder (R-v0.8-NEW-6) owns
1487
+ // the `{code, message, details?}` shape; its focused unit test
1488
+ // covers the details-absent arm this site previously c8-ignored.
1489
+ const causeProjection = projectCauseForEnvelope(remapped);
1320
1490
  const createdItemId = leg1Result.projected.id;
1321
1491
  throw new ApiError('internal_error', `Item ${createdItemId} was created on board ${inputs.resolveBoardId} ` +
1322
1492
  `but the file upload to column ${inputs.m38.columnId} failed ` +
@@ -1389,4 +1559,255 @@ const runItemCreateFileDispatch = async (inputs) => {
1389
1559
  resolvedIds,
1390
1560
  });
1391
1561
  };
1562
+ const runItemCreateFileMultiDispatch = async (inputs) => {
1563
+ // 1) Upfront pre-check per file path (N pre-checks, argv order;
1564
+ // D3). Atomicity-before-wire per cli-design §5.8 — a bad path
1565
+ // surfaces `usage_error` BEFORE either wire leg fires.
1566
+ const legEntries = [];
1567
+ for (const entry of inputs.m38.entries) {
1568
+ const precheck = await precheckLocalFile(entry.rawValue);
1569
+ legEntries.push({
1570
+ columnId: entry.columnId,
1571
+ rawValue: entry.rawValue,
1572
+ filePath: precheck.filePath,
1573
+ filename: precheck.filename,
1574
+ fileSizeBytes: precheck.fileSizeBytes,
1575
+ });
1576
+ }
1577
+ // 2) Partition setEntries: every file token routes to a file leg;
1578
+ // the rest bundle into leg-1's `create_item.column_values`.
1579
+ const fileTokens = new Set(inputs.m38.entries.map((e) => e.token));
1580
+ const nonFileSetEntries = inputs.setEntries.filter((e) => !fileTokens.has(e.token));
1581
+ // 3) Dry-run branch — leg-1 planned-changes from planCreate +
1582
+ // N synthetic `add_file_to_column` entries (no item_id; the
1583
+ // item doesn't exist yet). Mirrors M43 single-file dry-run
1584
+ // extended to N file entries.
1585
+ if (inputs.isDryRun) {
1586
+ let planResult;
1587
+ try {
1588
+ planResult = await planCreate({
1589
+ client: inputs.client,
1590
+ mode: inputs.createMode,
1591
+ name: inputs.parsed.name,
1592
+ setEntries: nonFileSetEntries,
1593
+ ...(inputs.rawEntries.length === 0
1594
+ ? {}
1595
+ : { rawEntries: inputs.rawEntries }),
1596
+ dateResolution: inputs.dateResolution,
1597
+ peopleResolution: inputs.peopleResolution,
1598
+ tagResolution: inputs.tagResolution,
1599
+ relationResolution: inputs.relationResolution,
1600
+ env: inputs.ctx.env,
1601
+ noCache: inputs.noCache,
1602
+ });
1603
+ }
1604
+ catch (err) {
1605
+ if (err instanceof MondayCliError) {
1606
+ throw mergeResolverWarningsIntoError(err, inputs.preflightWarnings);
1607
+ }
1608
+ throw err;
1609
+ }
1610
+ const dryRunSource = mergeSourceWithPreflight(mergeSourceWithPreflight(planResult.source, inputs.metaSource), inputs.preflightSource);
1611
+ const dryRunCacheAge = mergeCacheAge(mergeCacheAge(inputs.metaCacheAgeSeconds, planResult.cacheAgeSeconds), inputs.preflightCacheAgeSeconds);
1612
+ const fileEntries = legEntries.map((leg) => ({
1613
+ operation: 'add_file_to_column',
1614
+ column_id: leg.columnId,
1615
+ file_path: leg.rawValue,
1616
+ filename: leg.filename,
1617
+ file_size_bytes: leg.fileSizeBytes,
1618
+ }));
1619
+ const plannedChanges = [
1620
+ ...planResult.plannedChanges,
1621
+ ...fileEntries,
1622
+ ];
1623
+ emitDryRun({
1624
+ ctx: inputs.ctx,
1625
+ programOpts: inputs.programOpts,
1626
+ plannedChanges,
1627
+ source: dryRunSource,
1628
+ cacheAgeSeconds: dryRunCacheAge,
1629
+ warnings: dedupeCreateWarnings([
1630
+ ...inputs.preflightWarnings,
1631
+ ...planResult.warnings,
1632
+ ]),
1633
+ apiVersion: inputs.apiVersion,
1634
+ });
1635
+ return;
1636
+ }
1637
+ // 4) Live branch — leg-1 (create_item / create_subitem with bundled
1638
+ // non-file column_values) then legs 2..N+1 (sequential
1639
+ // `add_file_to_column`).
1640
+ let resolutionResult;
1641
+ try {
1642
+ resolutionResult = await resolveAndTranslate({
1643
+ client: inputs.client,
1644
+ boardId: inputs.resolveBoardId,
1645
+ setEntries: nonFileSetEntries,
1646
+ rawEntries: inputs.rawEntries,
1647
+ dateResolution: inputs.dateResolution,
1648
+ peopleResolution: inputs.peopleResolution,
1649
+ tagResolution: inputs.tagResolution,
1650
+ relationResolution: inputs.relationResolution,
1651
+ env: inputs.ctx.env,
1652
+ noCache: inputs.noCache,
1653
+ });
1654
+ }
1655
+ catch (err) {
1656
+ if (err instanceof MondayCliError) {
1657
+ throw mergeResolverWarningsIntoError(err, inputs.preflightWarnings);
1658
+ }
1659
+ throw err;
1660
+ }
1661
+ const collectedWarnings = dedupeCreateWarnings([...inputs.preflightWarnings, ...resolutionResult.warnings]);
1662
+ const sourceAgg = new SourceAggregator();
1663
+ /* c8 ignore next 3 */
1664
+ if (inputs.metaSource !== undefined) {
1665
+ sourceAgg.record(inputs.metaSource, inputs.metaCacheAgeSeconds);
1666
+ }
1667
+ if (resolutionResult.source !== undefined) {
1668
+ sourceAgg.record(resolutionResult.source, resolutionResult.cacheAgeSeconds);
1669
+ }
1670
+ if (inputs.preflightSource !== undefined) {
1671
+ sourceAgg.record(inputs.preflightSource, inputs.preflightCacheAgeSeconds);
1672
+ }
1673
+ // resolved_ids — file tokens + non-file tokens.
1674
+ const resolvedIds = {
1675
+ ...Object.fromEntries(inputs.m38.entries.map((e) => [e.token, e.columnId])),
1676
+ ...resolutionResult.resolvedIds,
1677
+ };
1678
+ const translated = resolutionResult.translated;
1679
+ const columnValues = translated.length === 0 ? null : bundleColumnValues(translated);
1680
+ // Leg-1: create_item / create_subitem. F4 remap on failure mirrors
1681
+ // M43 single-file leg-1; no orphan handle (no item created yet).
1682
+ let leg1Result;
1683
+ try {
1684
+ if (inputs.createMode.kind === 'subitem') {
1685
+ leg1Result = await executeCreateSubitem(inputs.client, {
1686
+ parentItemId: inputs.createMode.parentItemId,
1687
+ itemName: inputs.parsed.name,
1688
+ columnValues,
1689
+ createLabelsIfMissing: inputs.parsed.createLabelsIfMissing,
1690
+ });
1691
+ }
1692
+ else {
1693
+ leg1Result = await executeCreateItem(inputs.client, {
1694
+ boardId: inputs.createMode.boardId,
1695
+ itemName: inputs.parsed.name,
1696
+ groupId: inputs.createMode.groupId,
1697
+ position: inputs.createMode.position,
1698
+ columnValues,
1699
+ createLabelsIfMissing: inputs.parsed.createLabelsIfMissing,
1700
+ });
1701
+ }
1702
+ }
1703
+ catch (err) {
1704
+ if (err instanceof MondayCliError) {
1705
+ throw await foldAndRemap({
1706
+ err,
1707
+ warnings: collectedWarnings,
1708
+ client: inputs.client,
1709
+ boardId: inputs.resolveBoardId,
1710
+ columnIds: translated.map((t) => t.columnId),
1711
+ env: inputs.ctx.env,
1712
+ noCache: inputs.noCache,
1713
+ resolutionSource: resolutionResult.source ?? 'live',
1714
+ });
1715
+ }
1716
+ /* c8 ignore next */
1717
+ throw err;
1718
+ }
1719
+ sourceAgg.record('live', null);
1720
+ // Legs 2..N+1: sequential `add_file_to_column` fan-out against the
1721
+ // freshly-created item ID (R-v0.8-NEW-1 shared helper, D1 sequential
1722
+ // within-item).
1723
+ const createdItemId = leg1Result.projected.id;
1724
+ const dispatch = await dispatchFileLegsSequentially({
1725
+ client: inputs.client,
1726
+ multipart: inputs.multipart,
1727
+ itemId: createdItemId,
1728
+ entries: legEntries,
1729
+ signal: inputs.ctx.signal,
1730
+ retries: inputs.retries,
1731
+ });
1732
+ if (dispatch.failure !== undefined) {
1733
+ // Orphan-warn (D1 + D2). Extends M43's single-file discriminator
1734
+ // `'create_then_file_upload_partial_failure'` with the always-
1735
+ // present `applied_file_columns` slot (length 0..N-1 reflecting
1736
+ // file columns that landed after leg-1 succeeded but before the
1737
+ // failing file leg). foldAndRemap surfaces `column_archived` for
1738
+ // cache-served resolution against an archived column.
1739
+ if (dispatch.appliedColumns.length > 0) {
1740
+ // ≥1 file leg landed wire-side — invalidate the board cache so a
1741
+ // follow-up read doesn't serve stale asset metadata. (M43's
1742
+ // single-file leg-2 failure never lands a file, so it skips the
1743
+ // invalidate; the multi-file partial case can land legs.)
1744
+ await invalidateBoard(inputs.resolveBoardId, inputs.ctx.env);
1745
+ }
1746
+ const remapped = await foldAndRemap({
1747
+ err: dispatch.failure.cause,
1748
+ warnings: collectedWarnings,
1749
+ client: inputs.client,
1750
+ boardId: inputs.resolveBoardId,
1751
+ columnIds: [dispatch.failure.failedColumn],
1752
+ env: inputs.ctx.env,
1753
+ noCache: inputs.noCache,
1754
+ resolutionSource: inputs.metaSource ?? 'live',
1755
+ });
1756
+ // Shared cause-projection builder (R-v0.8-NEW-6) — same
1757
+ // `{code, message, details?}` shape as the M43 single-file site.
1758
+ const causeProjection = projectCauseForEnvelope(remapped);
1759
+ const failedColumn = dispatch.failure.failedColumn;
1760
+ throw new ApiError('internal_error', `Item ${createdItemId} was created on board ${inputs.resolveBoardId} ` +
1761
+ `but file upload to column ${failedColumn} failed after ` +
1762
+ `${String(dispatch.appliedColumns.length)} of ` +
1763
+ `${String(legEntries.length)} file column(s) landed ` +
1764
+ `(${remapped.code}: ${remapped.message}). The item + applied file ` +
1765
+ `columns persist on Monday; retry the unfailed columns with ` +
1766
+ `\`monday item set ${createdItemId} <file-col>=<path>\`, or roll ` +
1767
+ `back with \`monday item delete ${createdItemId} --yes\`.`, {
1768
+ cause: remapped,
1769
+ details: {
1770
+ reason: 'create_then_file_upload_partial_failure',
1771
+ created_item_id: createdItemId,
1772
+ applied_file_columns: dispatch.appliedColumns,
1773
+ failed_file_column: failedColumn,
1774
+ column_id: failedColumn,
1775
+ cause: causeProjection,
1776
+ hint: `the item was created (id ${createdItemId}) and ` +
1777
+ `${String(dispatch.appliedColumns.length)} file column(s) ` +
1778
+ `landed before column ${failedColumn} failed. Retry the ` +
1779
+ `unfailed file columns alone with \`monday item set ` +
1780
+ `${createdItemId} <file-col>=<path>\`; or rollback the orphan ` +
1781
+ `with \`monday item delete ${createdItemId} --yes\` and re-run ` +
1782
+ `the original \`monday item create\` once the underlying ` +
1783
+ `cause is fixed.`,
1784
+ },
1785
+ });
1786
+ }
1787
+ sourceAgg.record('live', null);
1788
+ // All N file legs succeeded — single board-cache invalidate before
1789
+ // emit (leg-2..N+1 mutated file-column asset state wire-side).
1790
+ await invalidateBoard(inputs.resolveBoardId, inputs.ctx.env);
1791
+ const data = {
1792
+ operation: 'item_create_with_files',
1793
+ item: leg1Result.projected,
1794
+ assets: dispatch.assets.map((a) => ({
1795
+ column_id: a.column_id,
1796
+ filename: a.filename,
1797
+ file_size_bytes: a.file_size_bytes,
1798
+ asset: a.asset,
1799
+ })),
1800
+ applied_file_columns: [...dispatch.appliedColumns],
1801
+ };
1802
+ emitMutation({
1803
+ ctx: inputs.ctx,
1804
+ data,
1805
+ schema: itemCreateWithFilesOutputSchema,
1806
+ programOpts: inputs.programOpts,
1807
+ warnings: collectedWarnings,
1808
+ ...inputs.toEmit(leg1Result.response),
1809
+ ...sourceAgg.result(),
1810
+ resolvedIds,
1811
+ });
1812
+ };
1392
1813
  //# sourceMappingURL=create.js.map