monday-cli 0.3.0 → 0.5.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 (198) hide show
  1. package/CHANGELOG.md +719 -0
  2. package/README.md +208 -36
  3. package/dist/api/assets.d.ts +326 -0
  4. package/dist/api/assets.d.ts.map +1 -0
  5. package/dist/api/assets.js +519 -0
  6. package/dist/api/assets.js.map +1 -0
  7. package/dist/api/column-types.d.ts +13 -7
  8. package/dist/api/column-types.d.ts.map +1 -1
  9. package/dist/api/column-types.js +7 -3
  10. package/dist/api/column-types.js.map +1 -1
  11. package/dist/api/column-values.d.ts +8 -1
  12. package/dist/api/column-values.d.ts.map +1 -1
  13. package/dist/api/column-values.js +16 -6
  14. package/dist/api/column-values.js.map +1 -1
  15. package/dist/api/documents.d.ts +1652 -0
  16. package/dist/api/documents.d.ts.map +1 -0
  17. package/dist/api/documents.js +2411 -0
  18. package/dist/api/documents.js.map +1 -0
  19. package/dist/api/item-watch.d.ts +263 -0
  20. package/dist/api/item-watch.d.ts.map +1 -0
  21. package/dist/api/item-watch.js +709 -0
  22. package/dist/api/item-watch.js.map +1 -0
  23. package/dist/api/multipart-transport.d.ts +223 -0
  24. package/dist/api/multipart-transport.d.ts.map +1 -0
  25. package/dist/api/multipart-transport.js +274 -0
  26. package/dist/api/multipart-transport.js.map +1 -0
  27. package/dist/api/parallel-dispatch.d.ts +155 -0
  28. package/dist/api/parallel-dispatch.d.ts.map +1 -0
  29. package/dist/api/parallel-dispatch.js +243 -0
  30. package/dist/api/parallel-dispatch.js.map +1 -0
  31. package/dist/api/partial-success-bulk.d.ts +118 -60
  32. package/dist/api/partial-success-bulk.d.ts.map +1 -1
  33. package/dist/api/partial-success-bulk.js +137 -79
  34. package/dist/api/partial-success-bulk.js.map +1 -1
  35. package/dist/api/partial-success-mutation.d.ts +13 -1
  36. package/dist/api/partial-success-mutation.d.ts.map +1 -1
  37. package/dist/api/partial-success-mutation.js +5 -1
  38. package/dist/api/partial-success-mutation.js.map +1 -1
  39. package/dist/api/raw-write.d.ts +13 -4
  40. package/dist/api/raw-write.d.ts.map +1 -1
  41. package/dist/api/raw-write.js +22 -11
  42. package/dist/api/raw-write.js.map +1 -1
  43. package/dist/api/resolve-client.d.ts +11 -0
  44. package/dist/api/resolve-client.d.ts.map +1 -1
  45. package/dist/api/resolve-client.js +9 -1
  46. package/dist/api/resolve-client.js.map +1 -1
  47. package/dist/api/teams.d.ts +657 -0
  48. package/dist/api/teams.d.ts.map +1 -0
  49. package/dist/api/teams.js +880 -0
  50. package/dist/api/teams.js.map +1 -0
  51. package/dist/cli/run.d.ts +20 -0
  52. package/dist/cli/run.d.ts.map +1 -1
  53. package/dist/cli/run.js +1 -0
  54. package/dist/cli/run.js.map +1 -1
  55. package/dist/commands/board/column-create.d.ts +6 -5
  56. package/dist/commands/board/column-create.d.ts.map +1 -1
  57. package/dist/commands/board/column-create.js +9 -6
  58. package/dist/commands/board/column-create.js.map +1 -1
  59. package/dist/commands/completion.d.ts +188 -0
  60. package/dist/commands/completion.d.ts.map +1 -0
  61. package/dist/commands/completion.js +418 -0
  62. package/dist/commands/completion.js.map +1 -0
  63. package/dist/commands/doc/append-markdown.d.ts +117 -0
  64. package/dist/commands/doc/append-markdown.d.ts.map +1 -0
  65. package/dist/commands/doc/append-markdown.js +253 -0
  66. package/dist/commands/doc/append-markdown.js.map +1 -0
  67. package/dist/commands/doc/block-create.d.ts +114 -0
  68. package/dist/commands/doc/block-create.d.ts.map +1 -0
  69. package/dist/commands/doc/block-create.js +206 -0
  70. package/dist/commands/doc/block-create.js.map +1 -0
  71. package/dist/commands/doc/block-delete.d.ts +72 -0
  72. package/dist/commands/doc/block-delete.d.ts.map +1 -0
  73. package/dist/commands/doc/block-delete.js +161 -0
  74. package/dist/commands/doc/block-delete.js.map +1 -0
  75. package/dist/commands/doc/block-update.d.ts +75 -0
  76. package/dist/commands/doc/block-update.d.ts.map +1 -0
  77. package/dist/commands/doc/block-update.js +162 -0
  78. package/dist/commands/doc/block-update.js.map +1 -0
  79. package/dist/commands/doc/create-in-workspace.d.ts +76 -0
  80. package/dist/commands/doc/create-in-workspace.d.ts.map +1 -0
  81. package/dist/commands/doc/create-in-workspace.js +164 -0
  82. package/dist/commands/doc/create-in-workspace.js.map +1 -0
  83. package/dist/commands/doc/create-on-column.d.ts +71 -0
  84. package/dist/commands/doc/create-on-column.d.ts.map +1 -0
  85. package/dist/commands/doc/create-on-column.js +146 -0
  86. package/dist/commands/doc/create-on-column.js.map +1 -0
  87. package/dist/commands/doc/delete.d.ts +68 -0
  88. package/dist/commands/doc/delete.d.ts.map +1 -0
  89. package/dist/commands/doc/delete.js +146 -0
  90. package/dist/commands/doc/delete.js.map +1 -0
  91. package/dist/commands/doc/duplicate.d.ts +101 -0
  92. package/dist/commands/doc/duplicate.d.ts.map +1 -0
  93. package/dist/commands/doc/duplicate.js +191 -0
  94. package/dist/commands/doc/duplicate.js.map +1 -0
  95. package/dist/commands/doc/get.d.ts +46 -0
  96. package/dist/commands/doc/get.d.ts.map +1 -0
  97. package/dist/commands/doc/get.js +95 -0
  98. package/dist/commands/doc/get.js.map +1 -0
  99. package/dist/commands/doc/import-html.d.ts +125 -0
  100. package/dist/commands/doc/import-html.d.ts.map +1 -0
  101. package/dist/commands/doc/import-html.js +273 -0
  102. package/dist/commands/doc/import-html.js.map +1 -0
  103. package/dist/commands/doc/list.d.ts +86 -0
  104. package/dist/commands/doc/list.d.ts.map +1 -0
  105. package/dist/commands/doc/list.js +217 -0
  106. package/dist/commands/doc/list.js.map +1 -0
  107. package/dist/commands/doc/rename.d.ts +60 -0
  108. package/dist/commands/doc/rename.d.ts.map +1 -0
  109. package/dist/commands/doc/rename.js +135 -0
  110. package/dist/commands/doc/rename.js.map +1 -0
  111. package/dist/commands/index.d.ts.map +1 -1
  112. package/dist/commands/index.js +162 -0
  113. package/dist/commands/index.js.map +1 -1
  114. package/dist/commands/item/create.js +2 -2
  115. package/dist/commands/item/update.d.ts +1 -0
  116. package/dist/commands/item/update.d.ts.map +1 -1
  117. package/dist/commands/item/update.js +61 -0
  118. package/dist/commands/item/update.js.map +1 -1
  119. package/dist/commands/item/upload.d.ts +108 -0
  120. package/dist/commands/item/upload.d.ts.map +1 -0
  121. package/dist/commands/item/upload.js +370 -0
  122. package/dist/commands/item/upload.js.map +1 -0
  123. package/dist/commands/item/watch.d.ts +90 -0
  124. package/dist/commands/item/watch.d.ts.map +1 -0
  125. package/dist/commands/item/watch.js +342 -0
  126. package/dist/commands/item/watch.js.map +1 -0
  127. package/dist/commands/update/create.d.ts.map +1 -1
  128. package/dist/commands/update/create.js +6 -4
  129. package/dist/commands/update/create.js.map +1 -1
  130. package/dist/commands/update/edit.d.ts +4 -2
  131. package/dist/commands/update/edit.d.ts.map +1 -1
  132. package/dist/commands/update/edit.js +10 -6
  133. package/dist/commands/update/edit.js.map +1 -1
  134. package/dist/commands/update/reply.d.ts +4 -2
  135. package/dist/commands/update/reply.d.ts.map +1 -1
  136. package/dist/commands/update/reply.js +10 -6
  137. package/dist/commands/update/reply.js.map +1 -1
  138. package/dist/commands/update/upload.d.ts +69 -0
  139. package/dist/commands/update/upload.d.ts.map +1 -0
  140. package/dist/commands/update/upload.js +235 -0
  141. package/dist/commands/update/upload.js.map +1 -0
  142. package/dist/commands/user/_team-membership.d.ts +10 -0
  143. package/dist/commands/user/_team-membership.d.ts.map +1 -0
  144. package/dist/commands/user/_team-membership.js +88 -0
  145. package/dist/commands/user/_team-membership.js.map +1 -0
  146. package/dist/commands/user/team-add-members.d.ts +81 -0
  147. package/dist/commands/user/team-add-members.d.ts.map +1 -0
  148. package/dist/commands/user/team-add-members.js +186 -0
  149. package/dist/commands/user/team-add-members.js.map +1 -0
  150. package/dist/commands/user/team-create.d.ts +82 -0
  151. package/dist/commands/user/team-create.d.ts.map +1 -0
  152. package/dist/commands/user/team-create.js +206 -0
  153. package/dist/commands/user/team-create.js.map +1 -0
  154. package/dist/commands/user/team-delete.d.ts +56 -0
  155. package/dist/commands/user/team-delete.d.ts.map +1 -0
  156. package/dist/commands/user/team-delete.js +137 -0
  157. package/dist/commands/user/team-delete.js.map +1 -0
  158. package/dist/commands/user/team-get.d.ts +41 -0
  159. package/dist/commands/user/team-get.d.ts.map +1 -0
  160. package/dist/commands/user/team-get.js +87 -0
  161. package/dist/commands/user/team-get.js.map +1 -0
  162. package/dist/commands/user/team-list.d.ts +39 -0
  163. package/dist/commands/user/team-list.d.ts.map +1 -0
  164. package/dist/commands/user/team-list.js +90 -0
  165. package/dist/commands/user/team-list.js.map +1 -0
  166. package/dist/commands/user/team-remove-members.d.ts +71 -0
  167. package/dist/commands/user/team-remove-members.d.ts.map +1 -0
  168. package/dist/commands/user/team-remove-members.js +176 -0
  169. package/dist/commands/user/team-remove-members.js.map +1 -0
  170. package/dist/types/ids.d.ts +8 -0
  171. package/dist/types/ids.d.ts.map +1 -1
  172. package/dist/types/ids.js +53 -5
  173. package/dist/types/ids.js.map +1 -1
  174. package/dist/utils/mime.d.ts +24 -0
  175. package/dist/utils/mime.d.ts.map +1 -0
  176. package/dist/utils/mime.js +64 -0
  177. package/dist/utils/mime.js.map +1 -0
  178. package/dist/utils/output/envelope.d.ts +30 -0
  179. package/dist/utils/output/envelope.d.ts.map +1 -1
  180. package/dist/utils/output/envelope.js +26 -0
  181. package/dist/utils/output/envelope.js.map +1 -1
  182. package/dist/utils/output/ndjson.d.ts +25 -0
  183. package/dist/utils/output/ndjson.d.ts.map +1 -1
  184. package/dist/utils/output/ndjson.js +12 -0
  185. package/dist/utils/output/ndjson.js.map +1 -1
  186. package/dist/utils/parse-brand-list.d.ts +95 -0
  187. package/dist/utils/parse-brand-list.d.ts.map +1 -0
  188. package/dist/utils/parse-brand-list.js +96 -0
  189. package/dist/utils/parse-brand-list.js.map +1 -0
  190. package/dist/utils/signal.d.ts +42 -0
  191. package/dist/utils/signal.d.ts.map +1 -0
  192. package/dist/utils/signal.js +45 -0
  193. package/dist/utils/signal.js.map +1 -0
  194. package/dist/utils/source-content.d.ts +93 -0
  195. package/dist/utils/source-content.d.ts.map +1 -0
  196. package/dist/utils/source-content.js +120 -0
  197. package/dist/utils/source-content.js.map +1 -0
  198. package/package.json +1 -1
@@ -0,0 +1,880 @@
1
+ /**
2
+ * Team writer + reader surface for the v0.5-M34 `monday user
3
+ * team-*` verbs (`cli-design.md` §4.3 USER section + §13 v0.5
4
+ * entry; `v0.5-plan.md` §3 M34).
5
+ *
6
+ * **Wire surface (empirical probe 2026-05-15, API `2026-01`).** Six
7
+ * Monday GraphQL operations land here — two reads against
8
+ * `Query.teams(...)` + four writes covering the v0.5 team-CRUD
9
+ * frame:
10
+ *
11
+ * - **List variant** — `Query.teams { id name picture_url
12
+ * is_guest users { id name email } owners { id name email } }`.
13
+ * NO pagination at the wire (D6 closure — `Query.teams` has no
14
+ * `limit:` / `page:` / cursor; returns every team visible to
15
+ * the token in one shot). Account-size cap on team count is
16
+ * the only natural limit.
17
+ * - **Get variant** — `Query.teams(ids: [<tid>])` with the same
18
+ * selection set. Single-id wire shape — Monday returns
19
+ * `[Team]` (an array even for one id); the fetcher extracts
20
+ * index 0. Empty array → `not_found` with `details.team_id`
21
+ * (Monday's wire collapses "doesn't exist" + "exists but not
22
+ * visible to token" into the same shape, same convention as
23
+ * M32 doc-get D8 closure).
24
+ * - **Create variant** — `create_team(input:
25
+ * CreateTeamAttributesInput!, options: CreateTeamOptionsInput)
26
+ * → Team`. Two input objects: `input` is required (`name!`,
27
+ * `is_guest_team?`, `parent_team_id?`, `subscriber_ids?:
28
+ * [ID!]`); `options` is optional (`allow_empty_team?`). M34
29
+ * surfaces `--name`, `--users <id,...>`, `--guest-team`,
30
+ * `--allow-empty` flags; `--parent <ptid>` deferred per D3.
31
+ * - **Delete variant** — `delete_team(team_id: ID!) → Team`.
32
+ * Returns the deleted Team verbatim. Destructive gate per
33
+ * cli-design §3.1 (M34 verb requires `--yes`).
34
+ * - **Add-members variant** — `add_users_to_team(team_id: ID!,
35
+ * user_ids: [ID!]!) → ChangeTeamMembershipsResult { failed_
36
+ * users: [User!], successful_users: [User!] }`. Wire-level
37
+ * partial-success envelope; the action body wraps this into
38
+ * the §6.1 universal `data.results: [{ ok, user_id, ... }]`
39
+ * shape (D5 closure).
40
+ * - **Remove-members variant** — `remove_users_from_team(team_
41
+ * id: ID!, user_ids: [ID!]!) → ChangeTeamMembershipsResult`.
42
+ * Same envelope shape as add-members.
43
+ *
44
+ * **`Team` object — 6 fields.** Per the v0.5 kickoff probe: `id`
45
+ * (ID!), `name` (String!), `picture_url` (String, nullable),
46
+ * `is_guest` (Boolean, nullable), `users` ([User], nullable —
47
+ * projected to slim `{id, name, email}` per the M19 / M32 slim-
48
+ * User convention), `owners` ([User!]!, non-null wire-side; same
49
+ * slim projection). **No `description` field on the wire** — D1
50
+ * closure drops the speculative `--description` flag the v0.4
51
+ * cli-design row pencilled. **No `update_team` mutation exists**
52
+ * — D2 closure drops a `team-update` verb from v0.5 scope (no
53
+ * rename / re-describe surface on the wire).
54
+ *
55
+ * **`CreateTeamAttributesInput` — 4 input fields.** Per the
56
+ * round-2 probe: `name` (String!), `is_guest_team` (Boolean,
57
+ * nullable), `parent_team_id` (ID, nullable), `subscriber_ids`
58
+ * ([ID!], nullable — must not be empty unless
59
+ * `allow_empty_team: true` per the description). Hierarchical
60
+ * teams exist via `parent_team_id`; v0.5-M34 surfaces the wire
61
+ * slot in the docstring but does NOT ship a `--parent <ptid>`
62
+ * argv flag (D3 deferral — agent-UX hierarchical-team semantics
63
+ * unclear; flag deferred to v0.5.x if user demand surfaces).
64
+ *
65
+ * **`CreateTeamOptionsInput` — 1 input field.** `allow_empty_
66
+ * team` (Boolean, nullable). Maps to the M34 `--allow-empty`
67
+ * argv flag.
68
+ *
69
+ * **`ChangeTeamMembershipsResult` — 2 fields.** Per the round-2
70
+ * probe: `failed_users: [User!]` + `successful_users: [User!]`.
71
+ * Wire returns the User objects (not just IDs); the M34 action
72
+ * body projects to slim `{id, name, email}` then wraps into the
73
+ * §6.1 partial-success envelope per D5.
74
+ *
75
+ * **No new ERROR_CODES at M34.** Existing codes route team-verb
76
+ * failures: `not_found` (team-get against missing/inaccessible
77
+ * tid), `usage_error` (argv-parse rejections — bad TeamId, empty
78
+ * `--users`, etc.), `forbidden` / `unauthorized` (token lacks
79
+ * team-write scope), `confirmation_required` (destructive gate
80
+ * on team-delete missing `--yes`), `validation_failed` (Monday-
81
+ * side rejection, e.g. duplicate team name).
82
+ *
83
+ * **Teams are live-only at v0.5-M34.** Per cli-design §8 cache
84
+ * scope, teams aren't cached — the `team-list` / `team-get` paths
85
+ * emit `meta.source: "live"` with `cache_age_seconds: null`.
86
+ * Team membership churns frequently in organisations and the
87
+ * stale-cache risk outweighs the cache-hit value (mirrors
88
+ * `monday user list` cadence — no cache).
89
+ *
90
+ * **Runtime bodies landed at v0.5-M34 IMPL.** All six fetchers
91
+ * issue a single `client.raw` round-trip with literal-pinned
92
+ * operationNames (`ListTeams` / `GetTeam` / `CreateTeam` /
93
+ * `DeleteTeam` / `AddUsersToTeam` / `RemoveUsersFromTeam`;
94
+ * R-NEW-37 W2 audit-point — operationNames pinned at the
95
+ * fetcher boundary, NOT caller-overridable). Read responses
96
+ * parse via wrapping `*ResponseSchema` shapes through
97
+ * `unwrapOrThrow`, so payload drift surfaces `internal_error`
98
+ * with `details.issues`. `getTeam`'s empty-array case rewraps
99
+ * to `not_found` per the M32 doc-get D8 cadence (Monday's wire
100
+ * collapses "doesn't exist" + "not visible to token"). Wire
101
+ * Team objects thread through {@link projectTeam} which filters
102
+ * null entries out of `users` before the agent-facing
103
+ * {@link teamSchema} sees the value — wire-vs-output split per
104
+ * round-2 P2-1 + R-v0.5-NEW-4 discipline.
105
+ *
106
+ * **Out of M34 scope** (probe-surfaced + carried forward):
107
+ *
108
+ * - `assign_team_owners` / `remove_team_owners` (owner-vs-member
109
+ * surface) — D4 deferral; tangential to the v0.5 "team CRUD"
110
+ * frame; revisit at v0.5.x candidate-selection.
111
+ * - `add_teams_to_board` / `delete_teams_from_board` (board
112
+ * subscription) — D4 deferral; tangential.
113
+ * - `add_teams_to_workspace` / `delete_teams_from_workspace`
114
+ * (workspace subscription) — D4 deferral; tangential.
115
+ * - `--parent <ptid>` on team-create — D3 deferral.
116
+ * - `monday user teams <uid>` per-user team-list — wire path
117
+ * exists via `User.teams`; deferred to v0.5.x.
118
+ */
119
+ import { z } from 'zod';
120
+ import { ApiError } from '../utils/errors.js';
121
+ import { unwrapOrThrow } from '../utils/parse-boundary.js';
122
+ import { assertResponseFieldPresent } from './response-root.js';
123
+ /**
124
+ * Slim projection of Monday's `User` for the `Team.users` +
125
+ * `Team.owners` slots + the `ChangeTeamMembershipsResult.{failed_,
126
+ * successful_}users` slots. Mirrors the M19 `account_tags` + M31
127
+ * `Asset.uploaded_by` + M32 `Document.created_by` slim-User
128
+ * cadence: `{id, name, email}` is the smallest agent-useful
129
+ * projection (email is the canonical resolver token for team
130
+ * membership ops).
131
+ *
132
+ * Full-User reads still route through `monday user get <uid>`.
133
+ */
134
+ export const teamUserSchema = z
135
+ .object({
136
+ id: z.string().min(1),
137
+ name: z.string().min(1),
138
+ email: z.string().min(1),
139
+ })
140
+ .strict();
141
+ /**
142
+ * Wire-parse Team projection — Monday's 6-field wire shape per
143
+ * the v0.5 kickoff probe (rounds 1 + 2; `scripts/probe/v0.5-team-
144
+ * mutations.ts` 2026-05-15, API `2026-01`). Fetcher-internal;
145
+ * NOT the agent-facing output schema (see {@link teamSchema}).
146
+ *
147
+ * Wire-side nullability per the probe:
148
+ *
149
+ * - `id: ID!` — non-null
150
+ * - `name: String!` — non-null
151
+ * - `picture_url: String` — nullable
152
+ * - `is_guest: Boolean` — nullable (NOT `Boolean!` despite the
153
+ * conceptual non-null shape; pin the wire's actual nullable
154
+ * surface so a `null` from a non-guest-aware org doesn't
155
+ * fault the schema-parse)
156
+ * - `users: [User]` — nullable container AND nullable entries
157
+ * per the wire signature (the probe-formatter renders
158
+ * `LIST/<wrapped>[OBJECT/User]` — no inner `NON_NULL` wrap on
159
+ * the entries; compare to `ChangeTeamMembershipsResult.
160
+ * successful_users -> LIST/<wrapped>[NON_NULL/<wrapped>]<OBJECT/
161
+ * User>` from the round-2 probe which DOES carry the inner
162
+ * non-null marker). The wire schema accepts `users: null` for
163
+ * the container AND `[user1, null, user2]` for sparse entries
164
+ * so a wire-shape variant doesn't surface as `internal_error`
165
+ * from the response-parse boundary (Monday surfaces null
166
+ * entries when a team member's User record was tombstoned
167
+ * post-team-creation; verify at M34 IMPL cassette).
168
+ * - `owners: [User!]!` — non-null wire-side per the round-1
169
+ * probe `NON_NULL/<wrapped>[LIST/<wrapped>]` outer wrapper.
170
+ * The probe-formatter truncates the inner detail beyond the
171
+ * LIST wrapper so the inner non-null is inferred from the
172
+ * description (owners is conceptually a non-empty set of
173
+ * User objects); if M34 IMPL cassette surfaces a null entry,
174
+ * widen to `z.array(teamUserSchema.nullable())` here.
175
+ */
176
+ export const teamWireSchema = z
177
+ .object({
178
+ id: z.string().min(1),
179
+ name: z.string().min(1),
180
+ picture_url: z.string().nullable(),
181
+ is_guest: z.boolean().nullable(),
182
+ users: z.array(teamUserSchema.nullable()).nullable(),
183
+ owners: z.array(teamUserSchema),
184
+ })
185
+ .strict();
186
+ /**
187
+ * Agent-facing Team projection — the exported output schema
188
+ * reused by `teamListOutputSchema` / `teamGetOutputSchema` /
189
+ * `teamCreateOutputSchema` / `teamDeleteOutputSchema`. Mirrors
190
+ * {@link teamWireSchema}'s field shape EXCEPT for the `users`
191
+ * entries which are pinned non-null at this boundary.
192
+ *
193
+ * **Wire-vs-CLI projection.** M34 IMPL fetcher bodies parse the
194
+ * wire response via {@link teamWireSchema}, then filter null
195
+ * entries out of `Team.users` before constructing the
196
+ * `outputSchema.parse(...)` call. Agents see a clean array;
197
+ * `monday schema user.team-list` exports a non-null
198
+ * entries shape; `emitSuccess` then enforces the output schema
199
+ * for free at the action boundary (`outputSchema.parse(data)`
200
+ * would reject a leaked null otherwise — defence-in-depth
201
+ * against a missed projection-layer filter at IMPL).
202
+ *
203
+ * Splitting wire-parse from output-projection — the discipline
204
+ * Codex flagged at M34 pre-flight round-2 P2-1 — keeps the
205
+ * agent contract narrow while the wire-parse boundary stays
206
+ * permissive enough to absorb wire-shape variations. Mirrors
207
+ * v0.3-M22 `monday usage` cadence (loose wire schema, strict
208
+ * output projection) at a sub-field granularity.
209
+ */
210
+ export const teamSchema = z
211
+ .object({
212
+ id: z.string().min(1),
213
+ name: z.string().min(1),
214
+ picture_url: z.string().nullable(),
215
+ is_guest: z.boolean().nullable(),
216
+ users: z.array(teamUserSchema).nullable(),
217
+ owners: z.array(teamUserSchema),
218
+ })
219
+ .strict();
220
+ /**
221
+ * Output shape for `monday user team-list`. Wrapped record (NOT
222
+ * bare array) so the envelope leaves headroom for a future
223
+ * `meta.team_count` or per-team-state aggregate without breaking
224
+ * the agent contract — mirrors M22 `monday usage` + M32 `doc
225
+ * list` wrapped-record convention.
226
+ *
227
+ * Monday's `Query.teams` exposes no pagination slot (D6); the
228
+ * shape pins a flat `teams: [Team]` array with no `has_more` /
229
+ * cursor / page slot. Agents fetch the entire visible-team set
230
+ * in one call.
231
+ */
232
+ export const teamListOutputSchema = z
233
+ .object({
234
+ teams: z.array(teamSchema),
235
+ returned_count: z.number().int().min(0),
236
+ })
237
+ .strict()
238
+ .superRefine((value, ctx) => {
239
+ if (value.returned_count !== value.teams.length) {
240
+ ctx.addIssue({
241
+ code: 'custom',
242
+ path: ['returned_count'],
243
+ message: `returned_count (${String(value.returned_count)}) must equal ` +
244
+ `teams.length (${String(value.teams.length)})`,
245
+ });
246
+ }
247
+ });
248
+ /**
249
+ * Output shape for `monday user team-get <tid>`. Direct unwrap
250
+ * of the single Team — mirrors the read-one-verb convention
251
+ * (`monday user get <uid>` returns `data: <User>`).
252
+ */
253
+ export const teamGetOutputSchema = teamSchema;
254
+ /**
255
+ * Output shape for `monday user team-create`. Direct unwrap of
256
+ * the created Team — Monday returns the full `Team` object
257
+ * post-create with `id` populated + any `subscriber_ids` already
258
+ * hydrated into the `users` slot.
259
+ */
260
+ export const teamCreateOutputSchema = teamSchema;
261
+ /**
262
+ * Output shape for `monday user team-delete`. Direct unwrap of
263
+ * the deleted Team — mirrors the M14 `workspace delete` cadence.
264
+ * Monday returns the deleted team verbatim so agents see the
265
+ * final state (name + member list) at the moment of deletion.
266
+ */
267
+ export const teamDeleteOutputSchema = teamSchema;
268
+ /**
269
+ * Per-target result record for the `team-add-members` /
270
+ * `team-remove-members` partial-success envelope (cli-design
271
+ * §6.1 universal partial-success shape; D5 closure). Mirrors
272
+ * the v0.2-M14 `workspace add-users` / M15 `board add-users`
273
+ * shape verbatim — `user_id` is the input numeric ID (echoed
274
+ * for correlation against the `--users <id,...>` argv slot),
275
+ * `ok` is the per-user success bit, `error` carries an optional
276
+ * `{code, message}` reason when the wire reported a failed-
277
+ * user entry.
278
+ *
279
+ * **Wire-vs-CLI semantics asymmetry note.** Monday's
280
+ * `ChangeTeamMembershipsResult` returns `failed_users: [User]`
281
+ * — a list of User objects, NOT a list of `{user_id, error_
282
+ * code, error_message}` triples. The CLI projects the wire
283
+ * shape into the universal envelope at the action body: every
284
+ * `successful_users[]` entry surfaces as `{ok: true, user_id,
285
+ * user: {...}}`; every `failed_users[]` entry surfaces as
286
+ * `{ok: false, user_id, error: {code: 'membership_failed',
287
+ * message: <wire-supplied or generic>}}`. The wire's
288
+ * `failed_users[]` carries the User object but NO per-user
289
+ * reason — the CLI emits a generic `membership_failed` message
290
+ * (verify at IMPL cassette whether Monday surfaces a reason
291
+ * elsewhere in the response or via the `errors[]` extension).
292
+ *
293
+ * **Wire-vs-CLI semantics convention.** This asymmetry extends
294
+ * `docs/architecture.md`'s "Wire-vs-CLI semantics documentation
295
+ * conventions" section (R-NEW-41 4th consumer trigger — see
296
+ * also v0.5-plan §22 R-v0.5-NEW-3). Per-verb docstrings at
297
+ * `src/commands/user/team-add-members.ts` +
298
+ * `src/commands/user/team-remove-members.ts` cross-link this
299
+ * note + the architecture section for the canonical convention.
300
+ */
301
+ export const teamMembershipResultSchema = z
302
+ .object({
303
+ user_id: z.string().min(1),
304
+ ok: z.boolean(),
305
+ user: teamUserSchema.optional(),
306
+ error: z
307
+ .object({
308
+ code: z.string().min(1),
309
+ message: z.string(),
310
+ })
311
+ .strict()
312
+ .optional(),
313
+ })
314
+ .strict();
315
+ /**
316
+ * Output shape for `monday user team-add-members <tid> --users
317
+ * <id,...>`. Universal partial-success envelope per cli-design
318
+ * §6.1 — `operation: 'add_users_to_team'`, `team_id` echoed
319
+ * input, `results: [{user_id, ok, ...}]` one record per input
320
+ * user (whether resolved as `ok: true` post-wire-success or
321
+ * `ok: false` if Monday's `failed_users[]` includes the user).
322
+ */
323
+ export const teamAddMembersOutputSchema = z
324
+ .object({
325
+ operation: z.literal('add_users_to_team'),
326
+ team_id: z.string().min(1),
327
+ results: z.array(teamMembershipResultSchema),
328
+ })
329
+ .strict();
330
+ /**
331
+ * Output shape for `monday user team-remove-members <tid> --users
332
+ * <id,...>`. Same envelope shape as
333
+ * {@link teamAddMembersOutputSchema} but with `operation:
334
+ * 'remove_users_from_team'` so agents that key off the operation
335
+ * literal can dispatch the right post-mutation recovery flow.
336
+ */
337
+ export const teamRemoveMembersOutputSchema = z
338
+ .object({
339
+ operation: z.literal('remove_users_from_team'),
340
+ team_id: z.string().min(1),
341
+ results: z.array(teamMembershipResultSchema),
342
+ })
343
+ .strict();
344
+ /**
345
+ * Shared Team selection fragment — every verb returning a `Team`
346
+ * pins the same 6-field projection. Inlined into each operation
347
+ * via string template to keep the GraphQL document readable +
348
+ * the wire payload bounded. The `users { id name email }` +
349
+ * `owners { id name email }` slim-User selections match the
350
+ * `teamUserSchema` shape verbatim.
351
+ */
352
+ const TEAM_FIELDS_FRAGMENT = `
353
+ id
354
+ name
355
+ picture_url
356
+ is_guest
357
+ users { id name email }
358
+ owners { id name email }
359
+ `;
360
+ /**
361
+ * GraphQL query document for `Query.teams` listing variant.
362
+ * Operation name pinned literally to `ListTeams` and matches the
363
+ * wire `operationName` payload (R-NEW-37 W2 audit-point —
364
+ * caller-overridable operationName slots were closed at M27 IMPL
365
+ * round-1 P2-1; M34 maintains the safely-by-construction shape).
366
+ *
367
+ * No variables — Monday's `Query.teams` exposes only the `ids:`
368
+ * filter (which the list verb omits) + no pagination slots
369
+ * (D6 closure). The query returns every team visible to the
370
+ * token.
371
+ */
372
+ export const LIST_TEAMS_QUERY = `
373
+ query ListTeams {
374
+ teams {
375
+ ${TEAM_FIELDS_FRAGMENT}
376
+ }
377
+ }
378
+ `;
379
+ /**
380
+ * GraphQL query document for `Query.teams(ids:)` single-id read
381
+ * variant. Operation name pinned to `GetTeam` (R-NEW-37 W2).
382
+ *
383
+ * Single-id wire shape — Monday returns `[Team]` (an array even
384
+ * for one id); the fetcher extracts index 0. An empty array
385
+ * surfaces `not_found` with `details.team_id` (Monday's wire
386
+ * collapses "doesn't exist" + "not visible to token" into the
387
+ * same shape per D8-equivalent — mirrors M32 doc-get).
388
+ */
389
+ export const GET_TEAM_QUERY = `
390
+ query GetTeam($ids: [ID!]!) {
391
+ teams(ids: $ids) {
392
+ ${TEAM_FIELDS_FRAGMENT}
393
+ }
394
+ }
395
+ `;
396
+ /**
397
+ * GraphQL mutation document for `create_team(input, options)`.
398
+ * Operation name pinned to `CreateTeam` (R-NEW-37 W2). The
399
+ * `options:` arg is optional on the wire; the fetcher omits the
400
+ * variable entirely when callers don't supply `allowEmptyTeam`
401
+ * (rather than threading an explicit `null` which Monday treats
402
+ * as "field present").
403
+ */
404
+ export const CREATE_TEAM_MUTATION = `
405
+ mutation CreateTeam(
406
+ $input: CreateTeamAttributesInput!,
407
+ $options: CreateTeamOptionsInput
408
+ ) {
409
+ create_team(input: $input, options: $options) {
410
+ ${TEAM_FIELDS_FRAGMENT}
411
+ }
412
+ }
413
+ `;
414
+ /**
415
+ * GraphQL mutation document for `delete_team(team_id)`. Operation
416
+ * name pinned to `DeleteTeam` (R-NEW-37 W2). Returns the deleted
417
+ * `Team` so agents see the final state at the moment of
418
+ * deletion.
419
+ */
420
+ export const DELETE_TEAM_MUTATION = `
421
+ mutation DeleteTeam($teamId: ID!) {
422
+ delete_team(team_id: $teamId) {
423
+ ${TEAM_FIELDS_FRAGMENT}
424
+ }
425
+ }
426
+ `;
427
+ /**
428
+ * GraphQL mutation document for `add_users_to_team(team_id,
429
+ * user_ids)`. Operation name pinned to `AddUsersToTeam` (R-NEW-37
430
+ * W2). Returns a `ChangeTeamMembershipsResult` with two User
431
+ * lists — `failed_users` + `successful_users`. The CLI projects
432
+ * the wire shape to the universal §6.1 partial-success envelope
433
+ * at the action body per D5.
434
+ */
435
+ export const ADD_USERS_TO_TEAM_MUTATION = `
436
+ mutation AddUsersToTeam($teamId: ID!, $userIds: [ID!]!) {
437
+ add_users_to_team(team_id: $teamId, user_ids: $userIds) {
438
+ failed_users { id name email }
439
+ successful_users { id name email }
440
+ }
441
+ }
442
+ `;
443
+ /**
444
+ * GraphQL mutation document for `remove_users_from_team(team_id,
445
+ * user_ids)`. Operation name pinned to `RemoveUsersFromTeam`
446
+ * (R-NEW-37 W2). Same `ChangeTeamMembershipsResult` return shape
447
+ * as add-members; the action body wraps into the universal
448
+ * partial-success envelope with `operation:
449
+ * 'remove_users_from_team'`.
450
+ */
451
+ export const REMOVE_USERS_FROM_TEAM_MUTATION = `
452
+ mutation RemoveUsersFromTeam($teamId: ID!, $userIds: [ID!]!) {
453
+ remove_users_from_team(team_id: $teamId, user_ids: $userIds) {
454
+ failed_users { id name email }
455
+ successful_users { id name email }
456
+ }
457
+ }
458
+ `;
459
+ /**
460
+ * `ChangeTeamMembershipsResult` wire shape per the v0.5 kickoff
461
+ * round-2 probe: `failed_users: [User!]` + `successful_users:
462
+ * [User!]`. Both lists are nullable containers on the wire (per
463
+ * the probe's outer-wrapper introspection — outer `LIST` is NOT
464
+ * `NON_NULL`-wrapped), but the inner entries are non-null
465
+ * (`NON_NULL/<wrapped>` inside the `LIST/<wrapped>`). The
466
+ * fetcher normalises a null bucket to `[]` at the projection
467
+ * boundary so the partial-success envelope downstream sees a
468
+ * uniform shape regardless of which (or both) buckets are
469
+ * `null` on the wire — addresses Codex IMPL round-1 P2-1
470
+ * (rejecting a wire-valid all-success or all-failed response
471
+ * was the pre-fix risk).
472
+ */
473
+ const changeTeamMembershipsResultSchema = z
474
+ .object({
475
+ failed_users: z.array(teamUserSchema).nullable(),
476
+ successful_users: z.array(teamUserSchema).nullable(),
477
+ })
478
+ .strict();
479
+ /**
480
+ * Wrapping response schema for the `ListTeams` operation. Monday's
481
+ * documented wire shape is `[Team]` (an array, possibly empty) —
482
+ * never null. The wrapper accepts `null` defensively so a wire-
483
+ * shape regression parses cleanly and the fetcher rewraps it as
484
+ * `internal_error` with a drift hint (rather than faulting the
485
+ * parse with a confusing zod issue path).
486
+ *
487
+ * `.loose()` mirrors M27 `listWebhooksResponseSchema` + M32
488
+ * `listDocsResponseSchema` — Monday occasionally returns side-
489
+ * band debug keys (`extensions`, `account_id`) alongside the
490
+ * documented data root; the loose mode lets them pass without
491
+ * faulting the parse.
492
+ *
493
+ * Inner entries parse via {@link teamWireSchema} which accepts
494
+ * sparse `users` arrays at the wire boundary; {@link projectTeam}
495
+ * filters nulls before the agent-facing {@link teamSchema} sees
496
+ * the value at `emitSuccess`.
497
+ */
498
+ const listTeamsResponseSchema = z
499
+ .object({
500
+ teams: z.array(teamWireSchema).nullable(),
501
+ })
502
+ .loose();
503
+ /**
504
+ * Wrapping response schema for the `GetTeam` operation. Same
505
+ * shape as the list variant (Monday's wire returns `[Team]` even
506
+ * for a single-id query). Empty-array surfaces `not_found` per
507
+ * D8-equivalent (mirrors M32 doc-get); null root surfaces
508
+ * `internal_error`.
509
+ */
510
+ const getTeamResponseSchema = z
511
+ .object({
512
+ teams: z.array(teamWireSchema).nullable(),
513
+ })
514
+ .loose();
515
+ /**
516
+ * Wrapping response schema for the `CreateTeam` mutation. The
517
+ * `create_team` root is `z.unknown()` here so `assertResponseField
518
+ * Present` can distinguish "key absent" (schema drift →
519
+ * `internal_error`) from "value null" (no payload returned —
520
+ * also `internal_error` per the workspace-create cadence; a
521
+ * non-existent team after a successful mutation indicates a
522
+ * Monday-side regression worth surfacing loudly). The payload
523
+ * itself parses via {@link teamWireSchema} downstream.
524
+ */
525
+ const createTeamResponseSchema = z
526
+ .object({
527
+ create_team: z.unknown(),
528
+ })
529
+ .loose();
530
+ /**
531
+ * Wrapping response schema for the `DeleteTeam` mutation. Same
532
+ * shape as `CreateTeam` but the null-value contract differs —
533
+ * a null `delete_team` payload surfaces `not_found` (mirrors
534
+ * M14 workspace-delete cadence — id bogus / already deleted by
535
+ * a concurrent caller).
536
+ */
537
+ const deleteTeamResponseSchema = z
538
+ .object({
539
+ delete_team: z.unknown(),
540
+ })
541
+ .loose();
542
+ /**
543
+ * Wrapping response schema for the `AddUsersToTeam` mutation.
544
+ * Returns `ChangeTeamMembershipsResult` non-null on the wire;
545
+ * the wrapper accepts `null` defensively so a wire-shape
546
+ * regression surfaces `internal_error` rather than faulting the
547
+ * parse.
548
+ */
549
+ const addUsersToTeamResponseSchema = z
550
+ .object({
551
+ add_users_to_team: changeTeamMembershipsResultSchema.nullable(),
552
+ })
553
+ .loose();
554
+ /**
555
+ * Wrapping response schema for the `RemoveUsersFromTeam`
556
+ * mutation. Same shape as `AddUsersToTeam`.
557
+ */
558
+ const removeUsersFromTeamResponseSchema = z
559
+ .object({
560
+ remove_users_from_team: changeTeamMembershipsResultSchema.nullable(),
561
+ })
562
+ .loose();
563
+ /**
564
+ * Wire-to-output projection for a single Team — filters null
565
+ * entries out of {@link TeamWire.users} so the agent-facing
566
+ * {@link teamSchema} sees a clean array (R-v0.5-NEW-4 wire-vs-
567
+ * output split discipline; round-2 P2-1 closure). A wire `users:
568
+ * null` container passes through unchanged (the output schema
569
+ * accepts `null` for "team has no member list" — see
570
+ * {@link teamSchema}). Owners are surfaced verbatim — Monday's
571
+ * wire pins `[User!]!` for `owners` per the round-1 probe, so
572
+ * there's no inner null to filter.
573
+ */
574
+ const projectTeam = (wire) => ({
575
+ id: wire.id,
576
+ name: wire.name,
577
+ picture_url: wire.picture_url,
578
+ is_guest: wire.is_guest,
579
+ users: wire.users === null
580
+ ? null
581
+ : wire.users.filter((u) => u !== null),
582
+ owners: wire.owners,
583
+ });
584
+ /**
585
+ * Fetches every team visible to the token via a single
586
+ * `Query.teams` round-trip with `operationName: 'ListTeams'`
587
+ * (R-NEW-37 W2). Source is always `'live'` per cli-design §8
588
+ * cache scope; teams aren't cached at v0.5.
589
+ *
590
+ * A null `teams` root surfaces `internal_error` with a drift
591
+ * hint — Monday's documented shape is `[Team]` (an array,
592
+ * possibly empty), never null at this layer. Each wire team
593
+ * threads through {@link projectTeam} so the agent-facing
594
+ * {@link teamSchema} sees a clean (non-nullable-entries) `users`
595
+ * array.
596
+ */
597
+ export const listTeams = async (inputs) => {
598
+ const response = await inputs.client.raw(LIST_TEAMS_QUERY, {}, { operationName: 'ListTeams' });
599
+ const parsed = unwrapOrThrow(listTeamsResponseSchema.safeParse(response.data), {
600
+ context: 'Monday `Query.teams(ListTeams)` response',
601
+ hint: 'Monday may have amended the `Team` selection — re-probe and ' +
602
+ 'amend `src/api/teams.ts` if so',
603
+ });
604
+ if (parsed.teams === null) {
605
+ throw new ApiError('internal_error', 'Monday returned a null `teams` payload from ListTeams', {
606
+ details: {
607
+ hint: 'Monday\'s documented shape is `[Team]` (an array, possibly empty) — ' +
608
+ 'a null root indicates a wire change that needs re-probing',
609
+ },
610
+ });
611
+ }
612
+ return {
613
+ teams: parsed.teams.map(projectTeam),
614
+ source: 'live',
615
+ cacheAgeSeconds: null,
616
+ complexity: response.complexity,
617
+ };
618
+ };
619
+ /**
620
+ * Fetches a single team by ID via a single `Query.teams(ids:)`
621
+ * round-trip with `operationName: 'GetTeam'` (R-NEW-37 W2).
622
+ *
623
+ * Empty wire response (Monday's shape for "team doesn't exist"
624
+ * OR "team not visible to token") surfaces `not_found` with
625
+ * `details.team_id` — same wire-shape collapse as M32 doc-get.
626
+ * A null `teams` root surfaces `internal_error` with a drift
627
+ * hint (distinct from the empty-array case); a multi-element
628
+ * response surfaces `internal_error` defensively (a single-id
629
+ * query shouldn't return more than one team).
630
+ */
631
+ export const getTeam = async (inputs) => {
632
+ const response = await inputs.client.raw(GET_TEAM_QUERY, { ids: [inputs.teamId] }, { operationName: 'GetTeam' });
633
+ const parsed = unwrapOrThrow(getTeamResponseSchema.safeParse(response.data), {
634
+ context: 'Monday `Query.teams(GetTeam)` response',
635
+ details: { team_id: inputs.teamId },
636
+ hint: 'Monday may have amended the `Team` selection — re-probe and ' +
637
+ 'amend `src/api/teams.ts` if so',
638
+ });
639
+ if (parsed.teams === null) {
640
+ throw new ApiError('internal_error', `Monday returned a null \`teams\` payload from GetTeam(${inputs.teamId})`, {
641
+ details: {
642
+ team_id: inputs.teamId,
643
+ hint: 'Monday\'s documented shape is `[Team]` (an array, possibly empty) — ' +
644
+ 'a null root indicates a wire change that needs re-probing',
645
+ },
646
+ });
647
+ }
648
+ if (parsed.teams.length === 0) {
649
+ throw new ApiError('not_found', `team ${inputs.teamId} not found (does not exist or not visible to token)`, { details: { team_id: inputs.teamId } });
650
+ }
651
+ if (parsed.teams.length > 1) {
652
+ throw new ApiError('internal_error', `Monday returned ${String(parsed.teams.length)} teams for a single-id GetTeam query`, {
653
+ details: {
654
+ team_id: inputs.teamId,
655
+ hint: 'wire shape regression — re-probe `Query.teams(ids:)`',
656
+ },
657
+ });
658
+ }
659
+ const [wireTeam] = parsed.teams;
660
+ /* c8 ignore next 6 */
661
+ if (wireTeam === undefined) {
662
+ throw new ApiError('internal_error', `Monday returned a sparse teams array for GetTeam(${inputs.teamId})`, { details: { team_id: inputs.teamId } });
663
+ }
664
+ return {
665
+ team: projectTeam(wireTeam),
666
+ source: 'live',
667
+ cacheAgeSeconds: null,
668
+ complexity: response.complexity,
669
+ };
670
+ };
671
+ /**
672
+ * Creates a new team via `create_team(input, options)` with
673
+ * `operationName: 'CreateTeam'` (R-NEW-37 W2). Returns the
674
+ * created `Team` with `id` populated post-create + any
675
+ * `subscriberIds` hydrated into the `users` slot.
676
+ *
677
+ * The fetcher composes `input` from `name` + `subscriberIds` +
678
+ * `isGuestTeam` and omits the `options` variable entirely when
679
+ * `allowEmptyTeam` is unset (Monday's wire `options:` arg is
680
+ * optional and a `null` value would be treated as "field
681
+ * present" rather than "field omitted"). The same omit-vs-null
682
+ * discipline applies to each nullable `input.*` slot — only
683
+ * supplied fields land in the wire payload.
684
+ *
685
+ * A missing-key wire response surfaces `internal_error` via
686
+ * `assertResponseFieldPresent` (R42 helper); a null payload
687
+ * surfaces `internal_error` too — a successful `create_team`
688
+ * mutation must return the created Team (a non-existent team
689
+ * after a 200 response indicates a Monday-side regression).
690
+ */
691
+ export const createTeam = async (inputs) => {
692
+ const input = { name: inputs.name };
693
+ if (inputs.subscriberIds !== undefined) {
694
+ input.subscriber_ids = inputs.subscriberIds;
695
+ }
696
+ if (inputs.isGuestTeam !== undefined) {
697
+ input.is_guest_team = inputs.isGuestTeam;
698
+ }
699
+ const variables = { input };
700
+ if (inputs.allowEmptyTeam !== undefined) {
701
+ variables.options = { allow_empty_team: inputs.allowEmptyTeam };
702
+ }
703
+ const response = await inputs.client.raw(CREATE_TEAM_MUTATION, variables, { operationName: 'CreateTeam' });
704
+ const data = unwrapOrThrow(createTeamResponseSchema.safeParse(response.data), {
705
+ context: 'Monday returned a malformed CreateTeam response',
706
+ details: { team_name: inputs.name },
707
+ hint: 'this is a data-integrity error in Monday\'s response; verify ' +
708
+ 'the response shape and update `createTeamResponseSchema` if ' +
709
+ 'Monday\'s contract has changed.',
710
+ });
711
+ assertResponseFieldPresent({
712
+ data,
713
+ key: 'create_team',
714
+ operationLabel: 'CreateTeam',
715
+ details: { team_name: inputs.name },
716
+ nullHandling: 'caller_handles',
717
+ });
718
+ const rawTeam = data.create_team;
719
+ if (rawTeam === null || rawTeam === undefined) {
720
+ throw new ApiError('internal_error', `Monday returned no team payload from create_team for name ${JSON.stringify(inputs.name)}.`, { details: { team_name: inputs.name } });
721
+ }
722
+ const wireTeam = unwrapOrThrow(teamWireSchema.safeParse(rawTeam), {
723
+ context: `Monday returned a malformed team payload for name ${JSON.stringify(inputs.name)}`,
724
+ details: { team_name: inputs.name },
725
+ });
726
+ return {
727
+ team: projectTeam(wireTeam),
728
+ source: 'live',
729
+ cacheAgeSeconds: null,
730
+ complexity: response.complexity,
731
+ };
732
+ };
733
+ /**
734
+ * Deletes a team by ID via `delete_team(team_id)` with
735
+ * `operationName: 'DeleteTeam'` (R-NEW-37 W2). Returns the
736
+ * deleted Team verbatim.
737
+ *
738
+ * A null `delete_team` payload surfaces `not_found` —
739
+ * mirrors the M14 `workspace delete` cadence (id was bogus
740
+ * OR team already deleted by a concurrent caller). A missing
741
+ * `delete_team` key surfaces `internal_error` per the R42
742
+ * helper's "key absent" branch (schema drift).
743
+ *
744
+ * **Destructive-gate ordering.** The verb's action body MUST
745
+ * call `enforceDestructiveGate` BEFORE this fetcher per the
746
+ * M10 round-1 P2 invariant. A missing `--yes` surfaces as
747
+ * `confirmation_required` from the action layer, never
748
+ * masked by `config_error` when no token is configured.
749
+ */
750
+ export const deleteTeam = async (inputs) => {
751
+ const response = await inputs.client.raw(DELETE_TEAM_MUTATION, { teamId: inputs.teamId }, { operationName: 'DeleteTeam' });
752
+ const data = unwrapOrThrow(deleteTeamResponseSchema.safeParse(response.data), {
753
+ context: 'Monday returned a malformed DeleteTeam response',
754
+ details: { team_id: inputs.teamId },
755
+ hint: 'this is a data-integrity error in Monday\'s response; verify ' +
756
+ 'the response shape and update `deleteTeamResponseSchema` if ' +
757
+ 'Monday\'s contract has changed.',
758
+ });
759
+ assertResponseFieldPresent({
760
+ data,
761
+ key: 'delete_team',
762
+ operationLabel: 'DeleteTeam',
763
+ details: { team_id: inputs.teamId },
764
+ nullHandling: 'caller_handles',
765
+ });
766
+ const rawTeam = data.delete_team;
767
+ if (rawTeam === null || rawTeam === undefined) {
768
+ throw new ApiError('not_found', `Monday returned no team payload from delete_team for id ${inputs.teamId}`, { details: { team_id: inputs.teamId } });
769
+ }
770
+ const wireTeam = unwrapOrThrow(teamWireSchema.safeParse(rawTeam), {
771
+ context: `Monday returned a malformed team payload for id ${inputs.teamId}`,
772
+ details: { team_id: inputs.teamId },
773
+ });
774
+ return {
775
+ team: projectTeam(wireTeam),
776
+ source: 'live',
777
+ cacheAgeSeconds: null,
778
+ complexity: response.complexity,
779
+ };
780
+ };
781
+ /**
782
+ * Adds a list of users to a team via `add_users_to_team(team_id,
783
+ * user_ids)` with `operationName: 'AddUsersToTeam'` (R-NEW-37
784
+ * W2). Returns Monday's `ChangeTeamMembershipsResult` split into
785
+ * `failedUsers` + `successfulUsers` — the action body wraps
786
+ * this into the §6.1 universal partial-success envelope at the
787
+ * verb boundary (D5 closure).
788
+ *
789
+ * **Wire returns User objects, not error reasons.** Monday's
790
+ * `failed_users[]` carries the User who failed but NO per-user
791
+ * reason on the wire today. The CLI action body emits a generic
792
+ * `membership_failed` error code per failed user. The IMPL
793
+ * cassette suite covers the single-shot non-partial happy case
794
+ * + the partial-success failed/successful split; no per-user
795
+ * reason key surfaced on Monday's wire today (verified via
796
+ * round-2 probe introspection of `ChangeTeamMembershipsResult`
797
+ * — 2 fields only, no reason / error / message slot).
798
+ *
799
+ * A null `add_users_to_team` payload surfaces `internal_error`
800
+ * — the wire returns the result envelope even when every
801
+ * supplied user fails (failures go into `failed_users[]`, not
802
+ * the root). A null root indicates a wire-shape regression.
803
+ */
804
+ export const addUsersToTeam = async (inputs) => {
805
+ const response = await inputs.client.raw(ADD_USERS_TO_TEAM_MUTATION, { teamId: inputs.teamId, userIds: inputs.userIds }, { operationName: 'AddUsersToTeam' });
806
+ const parsed = unwrapOrThrow(addUsersToTeamResponseSchema.safeParse(response.data), {
807
+ context: 'Monday returned a malformed AddUsersToTeam response',
808
+ details: { team_id: inputs.teamId },
809
+ hint: 'this is a data-integrity error in Monday\'s response; verify ' +
810
+ 'the response shape and update `addUsersToTeamResponseSchema` ' +
811
+ 'if Monday\'s contract has changed.',
812
+ });
813
+ if (parsed.add_users_to_team === null) {
814
+ throw new ApiError('internal_error', `Monday returned a null \`add_users_to_team\` payload for team ${inputs.teamId}`, {
815
+ details: {
816
+ team_id: inputs.teamId,
817
+ hint: 'Monday\'s documented shape is ChangeTeamMembershipsResult ' +
818
+ '(non-null on the wire) — a null root indicates a wire ' +
819
+ 'change that needs re-probing',
820
+ },
821
+ });
822
+ }
823
+ // Normalise null buckets to `[]` so the partial-success
824
+ // projection sees a uniform shape — Monday's wire types both
825
+ // bucket containers as nullable list (`[User!]`, no outer
826
+ // `NON_NULL`), so an all-success response can land with
827
+ // `failed_users: null` and vice versa (Codex IMPL round-1 P2-1).
828
+ return {
829
+ failedUsers: parsed.add_users_to_team.failed_users ?? [],
830
+ successfulUsers: parsed.add_users_to_team.successful_users ?? [],
831
+ source: 'live',
832
+ cacheAgeSeconds: null,
833
+ complexity: response.complexity,
834
+ };
835
+ };
836
+ /**
837
+ * Removes a list of users from a team via
838
+ * `remove_users_from_team(team_id, user_ids)` with
839
+ * `operationName: 'RemoveUsersFromTeam'` (R-NEW-37 W2). Same
840
+ * `ChangeTeamMembershipsResult` return shape as
841
+ * {@link addUsersToTeam}; the action body wraps into the
842
+ * universal partial-success envelope with `operation:
843
+ * 'remove_users_from_team'`.
844
+ *
845
+ * Same null-root → `internal_error` contract as
846
+ * {@link addUsersToTeam}.
847
+ */
848
+ export const removeUsersFromTeam = async (inputs) => {
849
+ const response = await inputs.client.raw(REMOVE_USERS_FROM_TEAM_MUTATION, { teamId: inputs.teamId, userIds: inputs.userIds }, { operationName: 'RemoveUsersFromTeam' });
850
+ const parsed = unwrapOrThrow(removeUsersFromTeamResponseSchema.safeParse(response.data), {
851
+ context: 'Monday returned a malformed RemoveUsersFromTeam response',
852
+ details: { team_id: inputs.teamId },
853
+ hint: 'this is a data-integrity error in Monday\'s response; verify ' +
854
+ 'the response shape and update `removeUsersFromTeamResponseSchema` ' +
855
+ 'if Monday\'s contract has changed.',
856
+ });
857
+ if (parsed.remove_users_from_team === null) {
858
+ throw new ApiError('internal_error', `Monday returned a null \`remove_users_from_team\` payload for team ${inputs.teamId}`, {
859
+ details: {
860
+ team_id: inputs.teamId,
861
+ hint: 'Monday\'s documented shape is ChangeTeamMembershipsResult ' +
862
+ '(non-null on the wire) — a null root indicates a wire ' +
863
+ 'change that needs re-probing',
864
+ },
865
+ });
866
+ }
867
+ // Normalise null buckets to `[]` so the partial-success
868
+ // projection sees a uniform shape — Monday's wire types both
869
+ // bucket containers as nullable list (`[User!]`, no outer
870
+ // `NON_NULL`), so an all-success response can land with
871
+ // `failed_users: null` and vice versa (Codex IMPL round-1 P2-1).
872
+ return {
873
+ failedUsers: parsed.remove_users_from_team.failed_users ?? [],
874
+ successfulUsers: parsed.remove_users_from_team.successful_users ?? [],
875
+ source: 'live',
876
+ cacheAgeSeconds: null,
877
+ complexity: response.complexity,
878
+ };
879
+ };
880
+ //# sourceMappingURL=teams.js.map