@classroomio/mcp 0.0.9 → 0.2.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 (3) hide show
  1. package/README.md +211 -15
  2. package/dist/index.js +16 -1
  3. package/package.json +9 -4
package/README.md CHANGED
@@ -43,21 +43,168 @@ Current tools:
43
43
 
44
44
  - `list_org_courses`
45
45
  - `get_course_structure`
46
- - `reorder_course_content`
47
46
  - `update_course_landing_page`
48
47
  - `create_course_draft`
49
48
  - `create_course_draft_from_course`
50
49
  - `get_course_draft`
51
- - `list_course_exercises`
52
- - `get_course_exercise`
53
- - `create_course_exercise`
54
- - `create_course_exercise_from_template`
55
- - `update_course_exercise`
56
50
  - `update_course_draft`
57
51
  - `tag_course_draft`
58
52
  - `tag_courses`
59
53
  - `publish_course_draft`
60
54
  - `publish_course_draft_to_existing_course`
55
+ - `list_course_members`
56
+ - `add_course_member`
57
+ - `get_course_member`
58
+ - `update_course_member`
59
+ - `delete_course_member`
60
+ - `reset_course_member_progress`
61
+ - `get_course_member_analytics`
62
+ - `list_course_invites`
63
+ - `create_course_invite`
64
+ - `revoke_course_invite`
65
+ - `list_org_cohorts`
66
+ - `create_cohort`
67
+ - `get_cohort`
68
+ - `update_cohort`
69
+ - `delete_cohort`
70
+ - `list_cohort_members`
71
+ - `add_cohort_members`
72
+ - `update_cohort_member`
73
+ - `delete_cohort_member`
74
+ - `list_cohort_courses`
75
+ - `add_cohort_course`
76
+ - `remove_cohort_course`
77
+ - `list_cohort_newsfeed`
78
+ - `create_cohort_newsfeed_post`
79
+ - `update_cohort_newsfeed_post`
80
+ - `update_cohort_newsfeed_reaction`
81
+ - `delete_cohort_newsfeed_post`
82
+ - `list_cohort_newsfeed_comments`
83
+ - `create_cohort_newsfeed_comment`
84
+ - `delete_cohort_newsfeed_comment`
85
+ - `list_cohort_goals`
86
+ - `create_cohort_goal`
87
+ - `get_cohort_goal`
88
+ - `update_cohort_goal`
89
+ - `archive_cohort_goal`
90
+ - `delete_cohort_goal`
91
+ - `evaluate_cohort_goal`
92
+ - `evaluate_all_cohort_goals`
93
+ - `get_org_goals_overview`
94
+ - `list_my_cohort_goals`
95
+ - `list_my_enrolled_cohorts`
96
+ - `invite_students_to_cohort`
97
+ - `assign_students_to_cohort`
98
+ - `get_cohort_invite_link`
99
+ - `create_cohort_invite_link`
100
+ - `set_cohort_invite_link_revoked`
101
+ - `get_org_analytics`
102
+ - `list_compliance_learners`
103
+ - `get_learner_analytics`
104
+ - `get_course_analytics`
105
+ - `list_course_analytics_students`
106
+ - `list_audience_members`
107
+ - `get_audience_member`
108
+ - `add_audience_member`
109
+ - `assign_audience_courses`
110
+ - `update_audience_member`
111
+ - `remove_audience_member`
112
+ - `list_team_members`
113
+ - `get_team_member`
114
+ - `update_team_member`
115
+
116
+ Course certificate tools:
117
+
118
+ - `get_course_certificate`
119
+ - `update_course_certificate`
120
+ - `list_course_certificates`
121
+ - `download_course_certificate`
122
+
123
+ Certificate tools call the public API certificate endpoints. MCP keys get the `course:certificate:read` and `course:certificate:write` scopes by default. Existing MCP keys that still have the default scope set were given them too; a key created with a narrower scope list is left unchanged, so create a new key to use these tools. These scopes open only the certificate endpoints.
124
+
125
+ The tools act as the person who created the key, with the same permissions that person has in the dashboard. That role is checked on every call, so if the creator loses access, the key loses it too:
126
+
127
+ - Reading the certificate settings and design needs them to be a member of the course (including access through a program) or an org admin.
128
+ - Changing the settings or design, listing who earned the certificate, and downloading a certificate need them to be a course tutor/admin or an org admin.
129
+
130
+ `get_course_certificate` and `update_course_certificate` return effective settings: stored values with the dashboard's defaults filled in where nothing is stored (`isDownloadable` false, `threshold` 100, the default design, and so on).
131
+
132
+ `update_course_certificate` is a partial update: fields you leave out keep their values, and `null` clears `deadline`, `requiredExerciseId`, `exerciseMinScorePercent`, or `emailMessage`. `design` is replaced as a whole object, so read it first and send the full design. `deadline` must be an ISO 8601 datetime with a timezone, for example `2026-12-31T23:59:59Z`. Like the dashboard, it needs a paid plan: on the Basic (free) plan it fails with 403 `UPGRADE_REQUIRED`.
133
+
134
+ `list_course_certificates` lists the students who earned the course certificate, with when they earned it and when the certificate email was sent. It takes `page` and `limit` (default 20, max 100) and an optional `search` on name or email. `download_course_certificate` takes a `memberId` from that list and a `format` of `pdf` (default) or `png`.
135
+
136
+ What the tools cover compared with the dashboard:
137
+
138
+ - Covered: certificate settings and design, the list of students who earned the certificate, and downloading an issued certificate.
139
+ - Not covered, on purpose: previewing a design, which is a UI concern.
140
+ - Not covered yet: compliance certificate history. For compliance courses, the dashboard also shows each learner's cycle-by-cycle completion and recertification history. That belongs to a separate compliance API and is not part of these tools.
141
+ - Not covered because the dashboard does not have it: manual issuance. Certificates are issued automatically when a student meets the course's completion rules.
142
+
143
+ Course section, lesson and content tools:
144
+
145
+ - `list_course_sections`, `create_course_section`, `update_course_section`, `delete_course_section`
146
+ - `list_course_lessons`, `get_course_lesson`, `delete_course_lesson`, `notify_course_lesson_session_update`
147
+ - `list_course_lesson_translations`, `set_course_lesson_translation`, `list_course_lesson_history`
148
+ - `list_course_lesson_comments`, `create_course_lesson_comment`, `update_course_lesson_comment`, `delete_course_lesson_comment`
149
+ - `reorder_course_content`, `set_course_content_unlocked`, `delete_course_content`
150
+
151
+ They call `/public-api/v1/courses/:id/{sections,lessons,content}` with the key's `course:read` (reads) and `course:write` (everything else) scopes, which MCP keys have by default. Every tool, reads included, needs the key creator to be a course tutor/admin or an org admin.
152
+
153
+ - `create_course_section` with `moveUngrouped: true` moves every lesson and exercise without a section into the new section.
154
+ - `delete_course_section`, `delete_course_lesson` and `delete_course_content` are hard deletes. Deleting a section also deletes its lessons and exercises. If students have submissions or lesson completions in what you delete, they fail with 409 unless `force` is true, which deletes that work too.
155
+ - `reorder_course_content`, `set_course_content_unlocked` and `delete_course_content` are all or nothing: one id outside the course fails the whole call with 404.
156
+ - Comments are posted as the key creator. Only the author can edit a comment; the author or the course team can delete it.
157
+ - Creating and editing lessons and uploading videos use `create_lesson`, `update_lesson` and `upload_video`. Restoring a lesson version is not covered.
158
+
159
+ Course exercise tools:
160
+
161
+ - `list_course_exercises`
162
+ - `get_course_exercise`
163
+ - `create_course_exercise`
164
+ - `update_course_exercise`
165
+ - `delete_course_exercise`
166
+ - `add_exercise_question`
167
+ - `update_exercise_question`
168
+ - `delete_exercise_question`
169
+ - `add_exercise_section`
170
+ - `update_exercise_section`
171
+ - `delete_exercise_section`
172
+ - `notify_course_exercise`
173
+ - `get_course_exercise_notify_status`
174
+ - `list_exercise_templates`
175
+ - `get_exercise_template`
176
+
177
+ Submission and marks tools:
178
+
179
+ - `list_course_submissions`
180
+ - `get_course_submission`
181
+ - `grade_course_submission`
182
+ - `update_course_submission`
183
+ - `delete_course_submission`
184
+ - `get_course_marks`
185
+
186
+ Exercise and template tools call the public API (`/public-api/v1/courses/:id/exercises` and `/public-api/v1/exercise-templates`) with the `course:exercise:read` / `course:exercise:write` scopes. Submission and marks tools call `/public-api/v1/courses/:id/submissions` and `/public-api/v1/courses/:id/marks` with the `course:submission:read` / `course:submission:write` scopes. MCP keys get all four by default. Existing MCP keys that still have the default scope set were given the submission scopes too; a key created with a narrower scope list is left unchanged, so create a new key to use the submission tools.
187
+
188
+ These tools act as the person who created the key:
189
+
190
+ - Every exercise, template, and submission tool needs them to be a course tutor/admin or an org admin (templates: an org admin or tutor). Exercise reads include the correct answers, which is why reading needs the team role too.
191
+ - `get_course_marks` needs them to be a member of the course or an org admin. A student only gets their own row, as in the dashboard.
192
+
193
+ `create_course_exercise` takes either `title` and `questions`, or a `templateId` from `list_exercise_templates`; with a template, only `sectionId`, `lessonId` and `order` may be sent alongside it. `create_course_exercise_from_template` was merged into it. `update_course_exercise` is a diff: send a question with its `id` to edit it, with `id` and `delete: true` to remove it, and without an `id` to add it; options work the same way. Question, option, and section ids must belong to the exercise. File upload, ordering, link, star rating, and video recording questions need a paid plan (403 `UPGRADE_REQUIRED` on Basic).
194
+
195
+ For one question or exercise section, use `add_exercise_question`, `update_exercise_question` and `delete_exercise_question`, or `add_exercise_section`, `update_exercise_section` and `delete_exercise_section`. They never delete anything you leave out. `delete_exercise_question` and `update_course_exercise` fail with 409 when they would delete a question students have answered, unless `force` is true, which also deletes their answers. In `update_course_exercise`, sections follow the same diff rules as questions: sections you leave out are kept. Exercise changes apply immediately, also on published courses, and past submissions are not regraded.
196
+
197
+ `delete_course_exercise` and `delete_course_submission` are hard deletes. Deleting an exercise also deletes every submission for it.
198
+
199
+ `grade_course_submission` sets the points per answer, the total, and feedback, and marks the submission completed. `update_course_submission` only changes the grading state or feedback. The student is emailed when the state changes, as in the dashboard. `notify_course_exercise` emails every course member and returns a `jobId` for `get_course_exercise_notify_status`; each call sends the emails again.
200
+
201
+ List tools (`list_course_exercises`, `list_exercise_templates`, `list_course_submissions`, `get_course_marks`) take `page` and `limit` (default 20, max 100) and return `{ data, pagination }`.
202
+
203
+ What the tools cover compared with the dashboard:
204
+
205
+ - Covered: creating, editing, deleting, and notifying about exercises (including exercise sections and templates), reviewing and grading submissions, and the gradebook.
206
+ - Not covered, on purpose: student actions (submitting an exercise, recording video answers). A key acts as its creator, an admin or tutor, and must not submit on a student's behalf.
207
+ - Covered by other tools: moving or reordering exercises (`reorder_course_content`) and unlocking one (`isUnlocked` on `update_course_exercise`).
61
208
 
62
209
  ## Auth Model
63
210
 
@@ -74,6 +221,42 @@ ClassroomIO API:
74
221
 
75
222
  The MCP package never decides permissions.
76
223
 
224
+ Course member and invite tools call the public API (`/public-api/v1/courses/:id/members` and `/public-api/v1/courses/:id/invites`) and need the key's `course:member:read` / `course:member:write` scopes. The course draft tools above call internal-only endpoints and use their own scopes (`course_import:draft:*`, `course:read`/`write`, etc.). Both kinds count toward the same MCP rate limits.
225
+
226
+ Course member and invite tools act as the person who created the API key, with the same rule as the dashboard's People and Invites pages: the key creator must be a tutor/admin of the course or an org admin. Adding a member by `profileId` or email requires that person to already be in the organization; adding someone already in the course fails with 409.
227
+
228
+ Cohort tools call the public API (`/public-api/v1/cohorts/...`) and need the key's `cohort:read` (reads) and `cohort:write` (everything else) scopes, which MCP keys have by default. Analytics tools call `/public-api/v1/analytics/...` and `/public-api/v1/courses/{courseId}/analytics/...` and need `analytics:read`, also on by default. Audience and team tools call `/public-api/v1/audience/...` and `/public-api/v1/team/...` and need `audience:read` / `audience:write` and `team:read` / `team:write`, also on by default. MCP keys don't get `public_api:*`, so the only public API routes they can reach are the cohort, course member and invite, course certificate, course section, lesson and content, course exercise and exercise template, course submission and marks, student activity, asset upload, analytics, audience, and team routes. The course/draft tools above call other, internal-only endpoints and use their own scopes.
229
+
230
+ Cohort tools act as the person who created the API key and follow the same rules as the dashboard:
231
+
232
+ - Reads need the key creator to be a cohort member or an org admin.
233
+ - Other writes, including invites, invite links, and goal evaluation, need the key creator to be a cohort tutor/admin or an org admin.
234
+ - Posts, comments, and reactions are made as the key creator, so they also need the key creator to be a member of the cohort. A reaction only ever changes the key creator's own reaction.
235
+ - A comment can also be deleted by its author.
236
+ - `get_org_goals_overview` needs the key creator to be an org admin or tutor.
237
+ - `list_my_enrolled_cohorts` and `list_my_cohort_goals` return the key creator's own data in the key's organization.
238
+
239
+ `add_cohort_members` creates memberships directly and sends no email; `invite_students_to_cohort` is the dashboard invite flow (organization invites plus emails).
240
+
241
+ List tools (`list_org_cohorts`, `list_cohort_members`, `list_cohort_courses`, `list_cohort_goals`, `list_cohort_newsfeed_comments`, `list_my_enrolled_cohorts`, `list_my_cohort_goals`, `get_org_goals_overview`) take `page` and `limit` (default 20, max 100) and return `{ data, pagination }`. `list_cohort_newsfeed` is cursor-based: pass the returned `nextCursor` back as `cursor`.
242
+
243
+ Audience and team tools act as the key creator:
244
+
245
+ - Every audience and team tool needs the key creator to be an org admin, the same as the dashboard's Audience and Team settings pages.
246
+ - `update_team_member` switches a member between admin and tutor with `roleId` (1 or 2). The key creator cannot change their own role, and the organization must keep at least one admin.
247
+ - `update_audience_member` and `update_team_member` take `name` to set a joined member's full name. The profile is shared, so the name changes in every organization that person belongs to. Email, password, username and avatar stay with the person. On a member who has not accepted their invite yet, `update_audience_member` takes `email` to correct the address instead.
248
+ - `remove_audience_member` deletes the organization membership; the person keeps their account. Inviting and removing team members is not available yet.
249
+ - `list_audience_members` and `list_team_members` take `page` and `limit` (default 20, max 100) and return `{ data, pagination }`.
250
+
251
+ Analytics tools are read-only, cost no credits, and act as the key creator:
252
+
253
+ - `get_org_analytics` returns the sections picked with `include` (default `overview`): `overview`, `traffic`, `countries`, `funnel`, `courseTypes`, `topCourses`, `loginActivity`, `compliance`. It takes `days` (7, 30, 90 or 365; default 30) and `limit` (1-20, default 5) for list sections, and returns `{ data, meta }`.
254
+ - `get_org_analytics` and `get_learner_analytics` need the key creator to be an org admin or tutor. (The dashboard server lets any org member read these; the tools follow the dashboard UI, where only admins and tutors reach the analytics pages.) `loginActivity` and `compliance` need an org admin; for a tutor they are left out and listed in `meta.omitted`.
255
+ - `list_compliance_learners` needs an org admin.
256
+ - `get_course_analytics` (sections `summary`, `funnel`) and `list_course_analytics_students` need a course tutor/admin or an org admin.
257
+ - Each section is cached for up to 10 minutes (login activity: 24 hours); `meta.generatedAt` says when the oldest one was computed.
258
+ - `list_compliance_learners` takes `page` and `limit` (default 20, max 100); `list_course_analytics_students` caps `limit` at 50. Both return `{ data, pagination }`.
259
+
77
260
  ## Required Environment Variables
78
261
 
79
262
  - `CLASSROOMIO_API_URL`
@@ -360,6 +543,8 @@ Expected tool sequence:
360
543
  Use this direct path when the live course only needs section ordering or lesson or exercise moves between sections.
361
544
  It avoids the full draft round-trip for simple structural changes.
362
545
 
546
+ Courses with grouping enabled can mix sections and standalone lessons or exercises. Sections and standalone items share one top-level `order`; items within a section have their own sibling order. Pass `sectionId: null` to move an item outside its section. Omitted fields retain their current values. Include sections and items in one request to save a mixed reorder atomically.
547
+
363
548
  ### Flow 7: Add exercises to a live course
364
549
 
365
550
  User says:
@@ -371,8 +556,9 @@ Add a 5-question multiple-choice exercise to lesson 3.
371
556
  Expected tool sequence:
372
557
 
373
558
  1. Agent calls `list_course_exercises` if it needs current context.
374
- 2. Agent calls `create_course_exercise` for a brand new exercise.
375
- 3. Agent calls `update_course_exercise` for later revisions.
559
+ 2. Agent calls `create_course_exercise` for a brand new exercise, or with a `templateId` from `list_exercise_templates`.
560
+ 3. Agent calls `get_course_exercise`, then `update_course_exercise` for later revisions.
561
+ 4. Agent calls `notify_course_exercise` if the user wants students emailed.
376
562
 
377
563
  Use this direct path when the course is already live and the change is exercise-specific.
378
564
 
@@ -422,28 +608,38 @@ Exercise tools use the live course directly:
422
608
  - `list_course_exercises`
423
609
  - `get_course_exercise`
424
610
  - `create_course_exercise`
425
- - `create_course_exercise_from_template`
426
611
  - `update_course_exercise`
612
+ - `delete_course_exercise`
613
+ - `add_exercise_question`
614
+ - `update_exercise_question`
615
+ - `delete_exercise_question`
616
+ - `add_exercise_section`
617
+ - `update_exercise_section`
618
+ - `delete_exercise_section`
427
619
 
428
620
  Supported `questionTypeId` values for exercise payloads:
429
621
 
430
622
  - `1` `RADIO` - Single answer
431
623
  - `2` `CHECKBOX` - Multiple answers
432
624
  - `3` `TEXTAREA` - Paragraph
433
- - `4` `TRUE_FALSE` - True/False
625
+ - `4` `TRUE_FALSE` - True/False (factual statements; options: True, False; auto-graded)
434
626
  - `5` `SHORT_ANSWER` - Short answer
435
627
  - `6` `NUMERIC` - Numeric answer
436
628
  - `7` `FILL_BLANK` - Fill in the blank
437
629
  - `8` `FILE_UPLOAD` - File upload
438
- - `9` `MATCHING` - Matching
439
- - `10` `ORDERING` - Ordering
440
- - `11` `HOTSPOT` - Hotspot
441
- - `12` `LINK` - Links
630
+ - `9` `ORDERING` - Ordering
631
+ - `10` `LINK` - Links
632
+ - `11` `WORD_BANK` - Word bank
633
+ - `12` `STAR` - Star rating
634
+ - `13` `VIDEO_RECORDING` - Video recording
635
+ - `14` `THUMBS` - Thumbs up/down (open-ended poll with customizable Yes/No labels; no correct answer)
636
+
637
+ `TRUE_FALSE` is auto-graded with `settings.correctValue` (boolean) and fixed True/False labels for factual statements. `THUMBS` is open-ended (not auto-graded): two customizable Yes/No-style options with thumbs icons for sentiment or poll-style questions — do not set `settings.correctValue`. `MATCHING` and `HOTSPOT` are disabled and must not be used.
442
638
 
443
639
  ## Operational Notes
444
640
 
445
641
  - `create_course_draft_from_course` may add warnings if a live course uses legacy lesson notes instead of localized lesson content
446
- - if a course has ungrouped lessons, the seeded draft may add a synthetic `Ungrouped` section so the structure stays valid
642
+ - lessons and exercises outside any section stay at the top level of the draft: a lesson without `sectionExternalId` and an exercise with neither a section nor a lesson share one 1-based `order` sequence with the sections
447
643
  - published drafts are treated as finalized snapshots
448
644
 
449
645
  ## Recommended Agent Behavior
package/dist/index.js CHANGED
@@ -1,2 +1,17 @@
1
1
  #!/usr/bin/env node
2
- import{McpServer as Gt}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as kt}from"@modelcontextprotocol/sdk/server/stdio.js";var S=class extends Error{constructor(n,a,u,d){super(n);this.status=a;this.code=u;this.field=d;this.name="ClassroomIoApiError"}},A=class{constructor(t){this.config=t}async createCourseDraft(t){return this.request("/organization/course-import/drafts",{method:"POST",body:t})}async createCourseDraftFromCourse(t){return this.request("/organization/course-import/drafts/from-course",{method:"POST",body:t})}async getCourseStructure(t){return this.request(`/organization/course-import/courses/${t}/structure`,{method:"GET"})}async updateCourseLandingPage(t,n){return this.request(`/course/${t}/landing-page`,{method:"PUT",body:n})}async reorderCourseContent(t,n){return this.request(`/course/${t}/content/reorder`,{method:"PUT",body:n})}async getCourseDraft(t){return this.request(`/organization/course-import/drafts/${t}`,{method:"GET"})}async listOrganizationCourses(t={}){let n=new URLSearchParams;t.tags&&n.set("tags",t.tags);let a=n.toString()?`?${n.toString()}`:"";return this.request(`/organization/courses${a}`,{method:"GET"})}async listCourseExercises(t,n={}){let a=new URLSearchParams;n.lessonId&&a.set("lessonId",n.lessonId),n.sectionId&&a.set("sectionId",n.sectionId);let u=a.toString()?`?${a.toString()}`:"";return this.request(`/course/${t}/exercise${u}`,{method:"GET"})}async getCourseExercise(t,n){return this.request(`/course/${t}/exercise/${n}`,{method:"GET"})}async createCourseExercise(t,n){return this.request(`/course/${t}/exercise`,{method:"POST",body:{...n,courseId:t}})}async createCourseExerciseFromTemplate(t,n){return this.request(`/course/${t}/exercise/from-template`,{method:"POST",body:n})}async updateCourseExercise(t,n,a){return this.request(`/course/${t}/exercise/${n}`,{method:"PUT",body:a})}async updateCourseDraft(t,n){return this.request(`/organization/course-import/drafts/${t}`,{method:"PUT",body:n})}async tagCourseDraft(t,n){return this.request(`/organization/course-import/drafts/${t}/tags`,{method:"PUT",body:n})}async publishCourseDraft(t,n){return this.request(`/organization/course-import/drafts/${t}/publish`,{method:"POST",body:n})}async publishCourseDraftToExistingCourse(t,n){return this.request(`/organization/course-import/drafts/${t}/publish-existing-course`,{method:"POST",body:n})}async tagCourses(t){return this.request("/organization/tags/courses/assign",{method:"PUT",body:t})}async request(t,n){let a=await fetch(new URL(t,this.config.CLASSROOMIO_API_URL),{method:n.method,headers:{Authorization:`Bearer ${this.config.CLASSROOMIO_API_KEY}`,"content-type":"application/json","user-agent":this.config.CLASSROOMIO_USER_AGENT},body:n.body?JSON.stringify(n.body):void 0}),u=await a.json().catch(()=>null);if(!a.ok){let d=u;throw new S(d?.error??d?.message??`ClassroomIO request failed with status ${a.status}`,a.status,d?.code,d?.field)}if(!u||typeof u!="object"||!("success"in u)||!u.success)throw new S("ClassroomIO returned an invalid response payload",a.status);return u.data}};import{z as _}from"zod";var we=_.object({CLASSROOMIO_API_URL:_.url().default("https://api.classroomio.com"),CLASSROOMIO_API_KEY:_.string().min(1),CLASSROOMIO_USER_AGENT:_.string().min(1).default("classroomio-mcp/0.0.9")});function q(o=process.env){return we.parse(o)}import*as c from"zod";var Re=["#EF4444","#F97316","#F59E0B","#84CC16","#22C55E","#14B8A6","#06B6D4","#3B82F6","#8B5CF6","#EC4899"],B=c.enum(Re),qt=c.object({groupId:c.uuid()}),Le=c.object({name:c.string().min(1).max(80),description:c.string().max(240).optional(),order:c.number().int().min(0).optional()}),Bt=Le.partial().refine(o=>Object.keys(o).length>0,{message:"At least one field is required"}),Qt=c.object({tagId:c.uuid()}),Yt=c.object({name:c.string().min(1).max(80),description:c.string().max(240).optional(),groupId:c.uuid(),color:B}),Xt=c.object({name:c.string().min(1).max(80).optional(),description:c.string().max(240).optional(),groupId:c.uuid().optional(),color:B.optional()}).refine(o=>Object.keys(o).length>0,{message:"At least one field is required"}),$t=c.object({courseId:c.uuid()}),Vt=c.object({tagIds:c.array(c.uuid()).max(100).default([])}),Ze=c.enum(["merge","replace"]).default("merge"),De=c.string().trim().min(1).max(80),Q=c.object({draftId:c.string().uuid()}),D=c.object({tagNames:c.array(De).min(1).max(100),mode:Ze,groupName:c.string().trim().min(1).max(80).optional()}),N=D.extend({courseIds:c.array(c.string().uuid()).min(1).max(100)});import*as x from"zod";var to=x.object({recipientCsv:x.string().max(25e3),courseIds:x.array(x.string().uuid()).optional(),allCourses:x.boolean().optional().default(!1),sendEmail:x.boolean().default(!0)}),oo=x.object({profileIds:x.array(x.string().uuid()).min(1).max(500),courseIds:x.array(x.string().uuid()).min(1),sendEmail:x.boolean().default(!0)});import*as i from"zod";var Y=["academy","app","apps","blog","build","campaign","church","cloud","community","conference","course","courses","demo","dev","forum","group","groups","help","launch","launchweek","mobile","play","prod","production","schools","stage","staging","support","teacher","teachers","tech","training"];var X;(function(o){o.Section="SECTION",o.Lesson="LESSON",o.Exercise="EXERCISE"})(X||(X={}));var mo=i.object({siteName:i.string().min(1).optional(),customDomain:i.string().min(1).optional(),isCustomDomainVerified:i.boolean().optional()}),go=i.object({siteName:i.string().min(1),tags:i.string().optional()}),U=i.object({tags:i.string().optional()}),zo=i.object({siteName:i.string().min(1)}),fo=i.object({orgId:i.uuid(),planName:i.enum(["EARLY_ADOPTER","ENTERPRISE","BASIC"]),subscriptionId:i.string().min(1),triggeredBy:i.number().int().positive(),payload:i.record(i.string(),i.unknown())}),Io=i.object({subscriptionId:i.string().min(1),payload:i.record(i.string(),i.unknown())}),Co=i.object({subscriptionId:i.string().min(1),payload:i.record(i.string(),i.unknown())}),xo=i.object({name:i.string().min(5).refine(o=>!/^[-]|[-]$/.test(o),{message:"Organization name cannot start or end with a hyphen"}),siteName:i.string().min(5).refine(o=>!/^[-]|[-]$/.test(o),{message:"Site name cannot start or end with a hyphen"}).refine(o=>!Y.includes(o),{message:"Sitename already exists."})}),Eo=i.object({name:i.string().min(5).refine(o=>!/^[-]|[-]$/.test(o),{message:"Organization name cannot start or end with a hyphen"}).optional(),avatarUrl:i.url().optional(),theme:i.string().optional(),landingpage:i.record(i.string(),i.unknown()).optional(),siteName:i.string().min(1).optional(),customDomain:i.string().nullable().optional(),isCustomDomainVerified:i.boolean().optional(),customization:i.record(i.string(),i.unknown()).optional(),disableSignup:i.boolean().optional(),disableSignupMessage:i.string().optional(),disableEmailPassword:i.boolean().optional(),disableGoogleAuth:i.boolean().optional(),settings:i.object({signup:i.object({inviteOnly:i.boolean().optional()}).optional()}).optional()}),yo=i.object({emails:i.array(i.string().email()).min(1).max(50),roleId:i.number().int().positive()}),bo=i.object({memberId:i.coerce.number().int().positive()}),To=i.object({userId:i.uuid()});import*as z from"zod";var So=z.object({orgId:z.uuid(),quizId:z.uuid()}),Oo=z.object({orgId:z.uuid()}),Ao=z.object({title:z.string().min(1),questions:z.array(z.any()).optional(),timelimit:z.string().optional(),theme:z.string().optional()}),_o=z.object({title:z.string().min(1).optional(),questions:z.array(z.any()).optional(),timelimit:z.string().optional(),theme:z.string().optional()});import*as P from"zod";var wo=P.object({orgId:P.uuid()});import*as w from"zod";var Lo=w.object({token:w.string().min(10).max(512)});import*as p from"zod";var Ne=["OKTA","GOOGLE_WORKSPACE","AUTH0"],Do=p.object({provider:p.enum(Ne),displayName:p.string().min(1,"Display name is required"),issuer:p.url("Must be a valid URL"),domain:p.string().min(1,"Domain is required").regex(/^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$/,"Invalid domain format"),clientId:p.string().min(1,"Client ID is required"),clientSecret:p.string().min(1,"Client secret is required"),scopes:p.string().default("openid profile email")}),No=p.object({displayName:p.string().min(1).optional(),issuer:p.url().optional(),domain:p.string().optional(),clientId:p.string().optional(),clientSecret:p.string().optional(),scopes:p.string().optional(),isActive:p.boolean().optional()}),Uo=p.object({forceSso:p.boolean().optional(),autoJoinSsoDomains:p.boolean().optional(),breakGlassEnabled:p.boolean().optional(),defaultRoleId:p.number().int().positive().optional(),roleMapping:p.record(p.string(),p.number()).optional()}),jo=p.object({email:p.email("Valid email is required")}),vo=p.object({orgId:p.uuid()});import{z as h}from"zod";var Fo=h.object({token:h.string().min(1),redirect:h.string().optional()}),Mo=h.object({sub:h.uuid(),email:h.email(),type:h.literal("login-link")});import{z as y}from"zod";var qo=y.object({token:y.string().min(1),redirect:y.string().optional()}),Bo=y.object({sub:y.string(),email:y.email(),name:y.string().optional(),avatar:y.url().optional(),iat:y.number(),exp:y.number()});import{z as b}from"zod";var j=b.enum(["mcp","api","zapier"]),Ue=b.enum(["course_import:draft:create","course_import:draft:read","course_import:draft:update","course_import:draft:publish","course:read","course:write","course:tag:write","course:exercise:read","course:exercise:write","public_api:*"]),Xo=b.object({type:j.optional()}),$o=b.object({type:j.default("mcp")}),Vo=b.object({type:j,label:b.string().min(1).max(120).optional(),expiresAt:b.string().datetime().optional(),scopes:b.array(Ue).min(1).optional()}),Jo=b.object({keyId:b.string().uuid()});import*as s from"zod";var m={RADIO:"RADIO",CHECKBOX:"CHECKBOX",TEXTAREA:"TEXTAREA",TRUE_FALSE:"TRUE_FALSE",SHORT_ANSWER:"SHORT_ANSWER",NUMERIC:"NUMERIC",FILL_BLANK:"FILL_BLANK",FILE_UPLOAD:"FILE_UPLOAD",MATCHING:"MATCHING",ORDERING:"ORDERING",HOTSPOT:"HOTSPOT",LINK:"LINK"},pr=[m.RADIO,m.CHECKBOX,m.TEXTAREA];var R=[{key:m.RADIO,typename:"RADIO",label:"Single answer",id:1,autoGradable:!0,supportsPartialCredit:!1,manualGradingRequired:!1},{key:m.CHECKBOX,typename:"CHECKBOX",label:"Multiple answers",id:2,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.TEXTAREA,typename:"TEXTAREA",label:"Paragraph",id:3,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0},{key:m.TRUE_FALSE,typename:"TRUE_FALSE",label:"True/False",id:4,autoGradable:!0,supportsPartialCredit:!1,manualGradingRequired:!1},{key:m.SHORT_ANSWER,typename:"SHORT_ANSWER",label:"Short answer",id:5,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0},{key:m.NUMERIC,typename:"NUMERIC",label:"Numeric answer",id:6,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.FILL_BLANK,typename:"FILL_BLANK",label:"Fill in the blank",id:7,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.FILE_UPLOAD,typename:"FILE_UPLOAD",label:"File upload",id:8,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0},{key:m.MATCHING,typename:"MATCHING",label:"Matching",id:9,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.ORDERING,typename:"ORDERING",label:"Ordering",id:10,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.HOTSPOT,typename:"HOTSPOT",label:"Hotspot",id:11,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.LINK,typename:"LINK",label:"Links",id:12,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0}],je=Object.freeze(R.reduce((o,t)=>(o[t.key]=t,o),{})),v=Object.freeze(R.reduce((o,t)=>(o[t.key]=t.id,o),{})),ve=Object.freeze(R.reduce((o,t)=>(o[t.typename]=t.key,o),{})),Ge=Object.freeze(R.reduce((o,t)=>(o[t.id]=t.key,o),{}));var ke=new Set(["true","1","yes"]),Fe=new Set(["false","0","no"]);function Me(o){let t=o.trim().toLowerCase();if(ke.has(t))return!0;if(Fe.has(t))return!1}var Ke={type:"RADIO",toApiPayload(o,t){return{questionId:t,optionId:o.optionId}},fromApiPayload(o){let t=o.optionId;return t===void 0||Number.isNaN(t)?null:{type:"RADIO",optionId:t}}},He={type:"CHECKBOX",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"CHECKBOX",optionIds:o.optionIds})}},fromApiPayload(o){if(!o.answer)return null;let{optionIds:t}=JSON.parse(o.answer);return Array.isArray(t)?{type:"CHECKBOX",optionIds:t}:null}},qe={type:"TRUE_FALSE",toApiPayload(o,t){return{questionId:t,answer:String(o.value)}},fromApiPayload(o){if(o.answer===void 0)return null;let t=Me(o.answer);return t===void 0?null:{type:"TRUE_FALSE",value:t}}},Be={type:"TEXTAREA",toApiPayload(o,t){return{questionId:t,answer:o.text}},fromApiPayload(o){return o.answer===void 0?null:{type:"TEXTAREA",text:o.answer}}},Qe={type:"SHORT_ANSWER",toApiPayload(o,t){return{questionId:t,answer:o.text}},fromApiPayload(o){return o.answer===void 0?null:{type:"SHORT_ANSWER",text:o.answer}}},Ye={type:"NUMERIC",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"NUMERIC",value:o.value})}},fromApiPayload(o){if(!o.answer)return null;let{value:t}=JSON.parse(o.answer);return{type:"NUMERIC",value:t}}},Xe={type:"FILL_BLANK",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"FILL_BLANK",answers:o.values})}},fromApiPayload(o){if(!o.answer)return null;let{answers:t}=JSON.parse(o.answer),n=t.map(a=>String(a).trim()).filter(Boolean);return n.length>0?{type:"FILL_BLANK",values:n}:null}},$e={type:"FILE_UPLOAD",toApiPayload(o,t){let n={fileKey:o.fileKey,fileName:o.fileName,mimeType:o.mimeType,size:o.size};return{questionId:t,answer:JSON.stringify(n)}},fromApiPayload(o){if(!o.answer)return null;let t=JSON.parse(o.answer);return{type:"FILE_UPLOAD",fileKey:t.fileKey,fileName:t.fileName??"",mimeType:t.mimeType,size:t.size}}},Ve={type:"MATCHING",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"MATCHING",value:o.pairs})}},fromApiPayload(o){if(!o.answer)return null;let{value:t}=JSON.parse(o.answer);return t.length>0?{type:"MATCHING",pairs:t}:null}},Je={type:"ORDERING",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"ORDERING",value:o.orderedValues})}},fromApiPayload(o){if(!o.answer)return null;let{value:t}=JSON.parse(o.answer);return t.length>0?{type:"ORDERING",orderedValues:t}:null}},We={type:"LINK",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"LINK",links:o.urls})}},fromApiPayload(o){if(!o.answer)return null;let{links:t}=JSON.parse(o.answer),n=t.map(a=>String(a).trim()).filter(Boolean);return n.length>0?{type:"LINK",urls:n}:null}},et={type:"HOTSPOT",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"HOTSPOT",value:o.coordinates})}},fromApiPayload(o){if(!o.answer)return null;let{value:t}=JSON.parse(o.answer);return t.length>0?{type:"HOTSPOT",coordinates:t}:null}},Tr={[m.RADIO]:Ke,[m.CHECKBOX]:He,[m.TRUE_FALSE]:qe,[m.TEXTAREA]:Be,[m.SHORT_ANSWER]:Qe,[m.NUMERIC]:Ye,[m.FILL_BLANK]:Xe,[m.FILE_UPLOAD]:$e,[m.MATCHING]:Ve,[m.ORDERING]:Je,[m.LINK]:We,[m.HOTSPOT]:et};var $=["video/mp4","video/quicktime","video/x-msvideo","video/x-matroska"],V=["application/pdf","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/msword"];var I=v;var tt=[s.literal(I.RADIO),s.literal(I.CHECKBOX),s.literal(I.TEXTAREA),s.literal(I.TRUE_FALSE),s.literal(I.SHORT_ANSWER),s.literal(I.NUMERIC),s.literal(I.FILL_BLANK),s.literal(I.FILE_UPLOAD),s.literal(I.MATCHING),s.literal(I.ORDERING),s.literal(I.HOTSPOT),s.literal(I.LINK)],L=s.union(tt),ot={[I.RADIO]:[o=>{let t=o.options||[];return t.length>0&&t.length<2?"Options must have at least 2 items for RADIO/CHECKBOX questions":null},o=>{let t=o.options||[];return t.length>0&&!t.some(n=>n.isCorrect===!0)?"Please mark an option as the correct answer":null}],[I.CHECKBOX]:[o=>{let t=o.options||[];return t.length>0&&t.length<2?"Options must have at least 2 items for RADIO/CHECKBOX questions":null},o=>{let t=o.options||[];return t.length>0&&!t.some(n=>n.isCorrect===!0)?"Please mark an option as the correct answer":null}],[I.TRUE_FALSE]:[o=>{let t=o.options||[];return t.length>0&&t.length<2?"True/False questions need both True and False options":null},o=>{let t=o.options||[];return t.length>0&&!t.some(n=>n.isCorrect===!0)?"Please mark an option as the correct answer":null}],[I.TEXTAREA]:[]},rt=s.object({id:s.number().int().optional(),question:s.string().min(1),questionTypeId:L.optional(),points:s.number().int().min(0).optional(),order:s.number().int().min(0).optional(),settings:s.record(s.string(),s.unknown()).optional(),deletedAt:s.string().optional(),options:s.array(s.object({id:s.number().int().optional(),label:s.string().min(1),isCorrect:s.boolean(),settings:s.record(s.string(),s.unknown()).optional(),deletedAt:s.string().optional()})).optional()});function J(o,t){if(o.deletedAt)return;let n=ot[o.questionTypeId??-1]??[];for(let a of n){let u=a(o);u&&t.addIssue({code:"custom",message:u,path:["options"]})}}var nt=rt.superRefine(J),st=s.object({question:s.string().min(1),questionTypeId:L.optional(),points:s.number().int().min(0).optional(),order:s.number().int().min(0).optional(),settings:s.record(s.string(),s.unknown()).optional(),options:s.array(s.object({label:s.string().min(1),isCorrect:s.boolean(),settings:s.record(s.string(),s.unknown()).optional()})).optional()}),it=st.superRefine(J),W=s.object({title:s.string().min(1),description:s.string().optional(),lessonId:s.string().optional(),sectionId:s.string().optional(),order:s.number().int().optional(),courseId:s.string().min(1),dueBy:s.string().optional(),questions:s.array(it).optional()}),ee=s.object({title:s.string().min(1).optional(),description:s.string().optional(),lessonId:s.string().optional(),sectionId:s.string().optional(),order:s.number().int().optional(),isUnlocked:s.boolean().optional(),dueBy:s.string().optional(),questions:s.array(nt).optional()}),G=s.object({exerciseId:s.string().min(1)}),te=s.object({lessonId:s.string().optional(),sectionId:s.string().optional()}),Kr=s.object({exerciseId:s.string().min(1),answers:s.array(s.object({questionId:s.number().int(),optionId:s.number().int().optional(),answer:s.string().optional()})).min(1)}),oe=s.object({lessonId:s.string().optional(),sectionId:s.string().optional(),order:s.number().int().optional(),templateId:s.number().int().min(1)});import*as r from"zod";import*as e from"zod";var Yr=e.object({title:e.string().min(1),description:e.string().optional(),slug:e.string().min(1),organizationId:e.string().min(1)}),Xr=e.object({courseId:e.string().min(1)}),at=e.object({courseId:e.string().min(1)}),$r=e.object({slug:e.string().optional()}),Vr=e.object({slug:e.string().min(1)}),Jr=e.object({courseId:e.string().min(1)}),Wr=e.object({inviteToken:e.string().min(1).optional()}),en=e.object({courseId:e.string().min(1)}),tn=e.object({theme:e.string().min(1),studentName:e.string().min(1),courseName:e.string().min(1),courseDescription:e.string().min(1),orgName:e.string().min(1),orgLogoUrl:e.string().url().optional(),facilitator:e.string().optional()}),on=e.object({courseTitle:e.string().min(1),orgName:e.string().min(1),orgTheme:e.string().min(1),lessons:e.array(e.object({lessonTitle:e.string().min(1),lessonNumber:e.string().min(1),lessonNote:e.string(),slideUrl:e.string().url().optional(),video:e.array(e.string().url()).optional()}))}),rn=e.object({title:e.string().min(1),number:e.string().min(1),orgName:e.string().min(1),note:e.string(),slideUrl:e.string().url().optional(),video:e.array(e.string().url()).optional(),courseTitle:e.string().min(1)}),nn=e.object({fileName:e.string().min(1),fileType:e.enum($)}),sn=e.object({fileName:e.string().min(1),fileType:e.enum(V)}),an=e.object({keys:e.array(e.string().min(1)).min(1)}),re=e.object({id:e.string().min(1),type:e.enum(["LESSON","EXERCISE"]),isUnlocked:e.boolean().optional(),order:e.number().int().min(0).optional(),sectionId:e.string().nullable().optional()}),un=e.object({items:e.array(re).min(1)}),ut=e.object({id:e.string().min(1),order:e.number().int().min(0)}),ct=re.omit({isUnlocked:!0}),k=e.object({sections:e.array(ut).min(1).optional(),items:e.array(ct).min(1).optional()}),ne=k.superRefine((o,t)=>{!o.sections?.length&&!o.items?.length&&t.addIssue({code:e.ZodIssueCode.custom,path:["sections"],message:"Provide sections or items to reorder"});let n=new Set;o.sections?.forEach((u,d)=>{n.has(u.id)&&t.addIssue({code:e.ZodIssueCode.custom,path:["sections",d,"id"],message:"Section IDs must be unique"}),n.add(u.id)});let a=new Set;o.items?.forEach((u,d)=>{u.order===void 0&&u.sectionId===void 0&&t.addIssue({code:e.ZodIssueCode.custom,path:["items",d],message:"Each item must provide order or sectionId"});let g=`${u.type}:${u.id}`;a.has(g)&&t.addIssue({code:e.ZodIssueCode.custom,path:["items",d,"id"],message:"Item IDs must be unique per content type"}),a.add(g)})}),pt=e.object({id:e.string().min(1),type:e.enum(["LESSON","EXERCISE"])}),cn=e.object({sectionId:e.string().min(1).optional(),items:e.array(pt).min(1).optional()}).refine(o=>!!o.sectionId!=!!o.items,{message:"Provide either sectionId or items",path:["sectionId"]}),pn=e.object({title:e.string().min(1),description:e.string().min(1),type:e.enum(["LIVE_CLASS","SELF_PACED"]),organizationId:e.string().min(1)}),se=e.object({show:e.boolean(),description:e.string()}),ie=e.object({name:e.string(),role:e.string(),coursesNo:e.number(),description:e.string(),imgUrl:e.string()}),ae=e.object({templateUrl:e.string()}),ue=e.object({id:e.number(),hide:e.boolean(),name:e.string(),avatar_url:e.string(),rating:e.number(),created_at:e.number(),description:e.string()}),ce=e.array(e.object({id:e.union([e.literal(1),e.literal(2),e.literal(3),e.literal(4)]),name:e.string()})),Z=e.object({requirements:e.string().optional(),description:e.string().optional(),goals:e.string().optional(),videoUrl:e.string().optional(),showDiscount:e.boolean().optional(),discount:e.number().optional(),paymentLink:e.string().optional(),reward:se.optional(),instructor:ie.optional(),certificate:ae.optional(),reviews:e.array(ue).optional(),lessonTabsOrder:ce.optional(),grading:e.boolean().optional(),lessonDownload:e.boolean().optional(),allowNewStudent:e.boolean(),sectionDisplay:e.record(e.string(),e.boolean()).optional(),isContentGroupingEnabled:e.boolean().optional()}),lt=e.object({requirements:e.string().optional(),description:e.string().optional(),goals:e.string().optional(),videoUrl:e.string().optional(),showDiscount:e.boolean().optional(),discount:e.number().optional(),paymentLink:e.string().optional(),reward:se.partial().optional(),instructor:ie.partial().optional(),certificate:ae.partial().optional(),reviews:e.array(ue).optional(),lessonTabsOrder:ce.optional(),grading:e.boolean().optional(),lessonDownload:e.boolean().optional(),allowNewStudent:e.boolean().optional(),sectionDisplay:e.record(e.string(),e.boolean()).optional(),isContentGroupingEnabled:e.boolean().optional()}),ln=e.object({title:e.string().min(1).optional(),description:e.string().min(1).optional(),type:e.enum(["LIVE_CLASS","SELF_PACED"]).optional(),logo:e.string().optional(),slug:e.string().optional(),isPublished:e.boolean().optional(),overview:e.string().optional(),metadata:Z.optional(),cost:e.number().int().min(0).optional(),currency:e.enum(["NGN","USD"]).optional(),isCertificateDownloadable:e.boolean().optional(),certificateTheme:e.string().optional(),tagIds:e.array(e.uuid()).max(100).optional()}),dn=e.object({courseId:e.string().min(1)}),pe=e.object({title:e.string().min(1).optional(),description:e.string().min(1).optional(),overview:e.string().optional(),cost:e.number().int().min(0).optional(),currency:e.enum(["NGN","USD"]).optional(),imageUrl:e.url().optional(),generateImage:e.boolean().optional(),imageQuery:e.string().min(1).max(120).optional(),metadata:lt.optional()}),mn=e.object({courseId:e.string().min(1)}),gn=e.object({courseId:e.string().min(1)}),zn=e.object({profileId:e.string().uuid()}),fn=e.object({courseId:e.string().min(1),userId:e.string().uuid()});var le=r.enum(["en","hi","fr","pt","de","vi","ru","es","pl","da"]),dt="Lesson body HTML only. Do not include the lesson title. Do not use h1 or h2 anywhere in lessonLanguages[].content. Start headings at h3 because that is the highest heading level allowed in lesson content.",de=r.object({code:r.string().min(1),message:r.string().min(1),severity:r.enum(["info","warning","error"])}),mt=r.object({type:r.enum(["prompt","pdf","course"]),label:r.string().min(1),pageStart:r.number().int().min(1).optional(),pageEnd:r.number().int().min(1).optional()}),gt=r.object({title:r.string().min(1),description:r.string().min(1),type:r.enum(["LIVE_CLASS","SELF_PACED"]),locale:le.default("en"),metadata:Z.optional()}),zt=r.object({externalId:r.string().min(1),title:r.string().min(1),order:r.number().int().min(0)}),ft=r.object({externalId:r.string().min(1),sectionExternalId:r.string().min(1),title:r.string().min(1),order:r.number().int().min(0),isUnlocked:r.boolean().optional(),public:r.boolean().optional()}),It=r.object({lessonExternalId:r.string().min(1),locale:le,content:r.string().min(1).describe(dt)}),Ct=r.object({label:r.string().min(1),isCorrect:r.boolean(),settings:r.record(r.string(),r.unknown()).optional()}),xt=r.object({question:r.string().min(1),questionTypeId:L.optional(),points:r.number().int().min(0).optional(),order:r.number().int().min(0).optional(),settings:r.record(r.string(),r.unknown()).optional(),options:r.array(Ct).optional()}),Et=r.object({externalId:r.string().min(1),lessonExternalId:r.string().min(1).optional(),sectionExternalId:r.string().min(1).optional(),title:r.string().min(1),description:r.string().optional(),order:r.number().int().min(0).optional(),dueBy:r.string().optional(),questions:r.array(xt).optional()}),me=r.object({course:gt,tags:r.array(r.string().trim().min(1).max(80)).max(100).default([]),sections:r.array(zt).min(1),lessons:r.array(ft).min(1),lessonLanguages:r.array(It).min(1),exercises:r.array(Et).optional(),sourceReferences:r.array(mt).optional(),warnings:r.array(de).default([])}).superRefine((o,t)=>{let n=new Set;o.tags.forEach((g,E)=>{let H=g.trim().toLowerCase();n.has(H)&&t.addIssue({code:r.ZodIssueCode.custom,path:["tags",E],message:"Draft tags must be unique"}),n.add(H)});let a=new Set;o.sections.forEach((g,E)=>{a.has(g.externalId)&&t.addIssue({code:r.ZodIssueCode.custom,path:["sections",E,"externalId"],message:"Section externalId must be unique"}),a.add(g.externalId)});let u=new Set;o.lessons.forEach((g,E)=>{u.has(g.externalId)&&t.addIssue({code:r.ZodIssueCode.custom,path:["lessons",E,"externalId"],message:"Lesson externalId must be unique"}),u.add(g.externalId),a.has(g.sectionExternalId)||t.addIssue({code:r.ZodIssueCode.custom,path:["lessons",E,"sectionExternalId"],message:"Lesson sectionExternalId must reference an existing section"})}),o.lessonLanguages.forEach((g,E)=>{u.has(g.lessonExternalId)||t.addIssue({code:r.ZodIssueCode.custom,path:["lessonLanguages",E,"lessonExternalId"],message:"Lesson language must reference an existing lesson"})});let d=new Set;o.exercises?.forEach((g,E)=>{d.has(g.externalId)&&t.addIssue({code:r.ZodIssueCode.custom,path:["exercises",E,"externalId"],message:"Exercise externalId must be unique"}),d.add(g.externalId),g.lessonExternalId&&!u.has(g.lessonExternalId)&&t.addIssue({code:r.ZodIssueCode.custom,path:["exercises",E,"lessonExternalId"],message:"Exercise lessonExternalId must reference an existing lesson"}),g.sectionExternalId&&!a.has(g.sectionExternalId)&&t.addIssue({code:r.ZodIssueCode.custom,path:["exercises",E,"sectionExternalId"],message:"Exercise sectionExternalId must reference an existing section"})})}),F=r.object({sourceType:r.enum(["prompt","pdf","course"]),idempotencyKey:r.string().min(1).optional(),summary:r.record(r.string(),r.unknown()).optional(),sourceArtifacts:r.array(r.record(r.string(),r.unknown())).optional(),draft:me}),T=r.object({courseId:r.string().min(1)}),M=r.object({courseId:r.string().min(1),idempotencyKey:r.string().min(1).optional(),summary:r.record(r.string(),r.unknown()).optional(),sourceArtifacts:r.array(r.record(r.string(),r.unknown())).optional()}),O=r.object({draftId:r.string().uuid()}),ge=r.object({summary:r.record(r.string(),r.unknown()).optional(),sourceArtifacts:r.array(r.record(r.string(),r.unknown())).optional(),warnings:r.array(de).optional(),draft:me.optional()}),K=r.object({title:r.string().min(1).optional(),description:r.string().min(1).optional(),type:r.enum(["LIVE_CLASS","SELF_PACED"]).optional(),metadata:Z.optional(),bannerImageUrl:r.string().url().optional(),bannerImageQuery:r.string().min(1).max(120).optional(),generateBannerImage:r.boolean().optional()}),ze=K.extend({courseId:r.string().min(1)});import*as f from"zod";var Tn=f.object({title:f.string().min(1),courseId:f.string().min(1),order:f.number().int().min(0).optional()}),hn=f.object({title:f.string().min(1).optional(),order:f.number().int().min(0).optional()}),Sn=f.object({title:f.string().min(1)}),On=f.object({sectionId:f.string().min(1)}),An=f.object({sections:f.array(f.object({id:f.string().min(1),order:f.number().int().min(0)})).min(1)});import*as l from"zod";var Pn=l.object({courseId:l.string().min(1)}),wn=l.object({courseId:l.string().min(1),inviteId:l.string().uuid()}),Rn=l.object({courseId:l.string().min(1),inviteId:l.string().uuid()}),Ln=l.object({token:l.string().min(1)}),Zn=l.object({courseId:l.uuid()}),yt=l.enum(["ONE_TIME_24H","MULTI_USE_7D","MULTI_USE_30D","CUSTOM"]),Dn=l.object({preset:yt.default("MULTI_USE_30D"),expiresAt:l.string().min(1).optional(),maxUses:l.number().int().min(1).max(1e3).optional(),allowedEmails:l.array(l.string().email()).max(100).optional(),allowedDomains:l.array(l.string().min(1)).max(100).optional(),recipientEmails:l.array(l.string().email()).max(500).optional(),recipientCsv:l.string().max(25e3).optional(),sendEmail:l.boolean().default(!1),metadata:l.record(l.string(),l.unknown()).optional()}).refine(o=>{let t=(o.recipientEmails?.length??0)>0,n=typeof o.recipientCsv=="string"&&o.recipientCsv.trim().length>0;return t||n},{message:"recipientEmails or recipientCsv is required",path:["recipientEmails"]});var Ce=ge.extend({draftId:O.shape.draftId}),xe=D.extend({draftId:Q.shape.draftId}),Ee=te.extend({courseId:T.shape.courseId}),ye=G.extend({courseId:T.shape.courseId}),be=W.omit({courseId:!0}).extend({courseId:T.shape.courseId}),Te=oe.extend({courseId:T.shape.courseId}),he=ee.extend({courseId:T.shape.courseId,exerciseId:G.shape.exerciseId}),Se=pe.extend({courseId:T.shape.courseId}),Oe=k.extend({courseId:T.shape.courseId}),Ae=K.extend({draftId:O.shape.draftId}),_e=ze.extend({draftId:O.shape.draftId}),bt=F.shape,Tt=M.shape,ht=U.shape,St=T.shape,Ot=O.shape,At=Ce.shape,_t=xe.shape,Pt=Ee.shape,wt=ye.shape,Rt=be.shape,Lt=Te.shape,Zt=he.shape,Dt=Se.shape,Nt=Oe.shape,Ut=Ae.shape,jt=_e.shape,vt=N.shape,fe="Supported questionTypeId values: RADIO=1 (Single answer), CHECKBOX=2 (Multiple answers), TEXTAREA=3 (Paragraph), TRUE_FALSE=4 (True/False), SHORT_ANSWER=5 (Short answer), NUMERIC=6 (Numeric answer), FILL_BLANK=7 (Fill in the blank), FILE_UPLOAD=8 (File upload), MATCHING=9 (Matching), ORDERING=10 (Ordering), HOTSPOT=11 (Hotspot), LINK=12 (Links). Use the numeric ids in exercise payloads.",Ie="For draft lesson HTML, put only the lesson body in lessonLanguages[].content. Do not include the lesson title. Do not use h1 or h2 anywhere in lesson HTML. Start headings at h3 because that is the highest heading level allowed in lesson content.";function Pe(o,t){o.tool("list_org_courses","List all courses in the authenticated organization. Use this when the user refers to a course by name and you need to discover the available course IDs first.",ht,async n=>{let a=U.parse(n),u=await t.listOrganizationCourses(a);return C(u)}),o.tool("get_course_structure","Read the current live course structure. Use this before creating a fresh draft for post-publish edits.",St,async n=>{let{courseId:a}=T.parse(n),u=await t.getCourseStructure(a);return C(u)}),o.tool("update_course_landing_page","Update landing-page-facing course fields on a live course, including headline copy, overview, requirements, goals, pricing, reviews, instructor information, and the course image. Use imageUrl to set an explicit cover image, or set generateImage/imageQuery to fetch a random Unsplash-based image.",Dt,async n=>{let{courseId:a,...u}=Se.parse(n),d=await t.updateCourseLandingPage(a,u);return C(d)}),o.tool("reorder_course_content","Batch reorder live course sections and lesson or exercise content without creating a draft. Use this for section ordering or moving/reordering lessons and exercises between sections. For items, pass type as LESSON or EXERCISE and use sectionId to move between sections or null for ungrouped content.",Nt,async n=>{let{courseId:a,...u}=Oe.parse(n),d=ne.parse(u),g=await t.reorderCourseContent(a,d);return C(g)}),o.tool("create_course_draft",`Create a new unpublished course draft from structured course JSON. Use this before the first publish. ${Ie}`,bt,async n=>{let a=F.parse(n),u=await t.createCourseDraft(a);return C(u)}),o.tool("create_course_draft_from_course","Create a fresh draft from an existing live course. Use this after a course has already been published and the user wants more changes.",Tt,async n=>{let a=M.parse(n),u=await t.createCourseDraftFromCourse(a);return C(u)}),o.tool("get_course_draft","Fetch an existing draft. Draft responses include status and publishedCourseId so you can decide whether to keep editing the draft or seed a fresh one from the live course.",Ot,async n=>{let{draftId:a}=O.parse(n),u=await t.getCourseDraft(a);return C(u)}),o.tool("list_course_exercises","List exercises for a live course, optionally filtered by lessonId or sectionId.",Pt,async n=>{let{courseId:a,...u}=Ee.parse(n),d=await t.listCourseExercises(a,u);return C(d)}),o.tool("get_course_exercise","Fetch a single exercise from a live course, including its questions and options.",wt,async n=>{let{courseId:a,exerciseId:u}=ye.parse(n),d=await t.getCourseExercise(a,u);return C(d)}),o.tool("create_course_exercise",`Create a new exercise directly on a live course. Use this for adding exercises after a course has already been published. ${fe}`,Rt,async n=>{let{courseId:a,...u}=be.parse(n),d=await t.createCourseExercise(a,u);return C(d)}),o.tool("create_course_exercise_from_template","Create a new exercise on a live course from an existing template.",Lt,async n=>{let{courseId:a,...u}=Te.parse(n),d=await t.createCourseExerciseFromTemplate(a,u);return C(d)}),o.tool("update_course_exercise",`Update an existing exercise on a live course, including questions and options. ${fe}`,Zt,async n=>{let{courseId:a,exerciseId:u,...d}=he.parse(n),g=await t.updateCourseExercise(a,u,d);return C(g)}),o.tool("update_course_draft",`Update an unpublished draft. If the draft is already published, do not keep editing it blindly; create a fresh draft from the live course first. ${Ie}`,At,async n=>{let{draftId:a,...u}=Ce.parse(n),d=await t.updateCourseDraft(a,u);return C(d)}),o.tool("tag_course_draft","Attach human-readable tags to a draft before publish. The tags are stored on the draft and applied during publish.",_t,async n=>{let{draftId:a,...u}=xe.parse(n),d=await t.tagCourseDraft(a,u);return C(d)}),o.tool("publish_course_draft","Publish an unpublished draft as a new live course. The result includes courseId and courseUrl. After publish, future major edits should usually start from a fresh draft created from the live course.",Ut,async n=>{let{draftId:a,...u}=Ae.parse(n),d=await t.publishCourseDraft(a,u);return C(d)}),o.tool("publish_course_draft_to_existing_course","Apply a fresh draft back onto an existing live course. Use this after reading the live course and creating a new draft from it.",jt,async n=>{let{draftId:a,...u}=_e.parse(n),d=await t.publishCourseDraftToExistingCourse(a,u);return C(d)}),o.tool("tag_courses","Assign tags directly to one or more live courses by name. Default mode is merge, so existing tags are preserved unless replace is requested.",vt,async n=>{let a=N.parse(n),u=await t.tagCourses(a);return C(u)})}function C(o){return{content:[{type:"text",text:JSON.stringify(o)}]}}async function Ft(){let o=q(),t=new A(o),n=new Gt({name:"classroomio-course-authoring",version:"0.0.1"});Pe(n,t);let a=new kt;await n.connect(a),process.stdin.on("close",()=>{n.close()})}Ft().catch(o=>{o instanceof S&&(console.error(JSON.stringify({error:o.message,status:o.status,code:o.code,field:o.field})),process.exit(1)),console.error(o),process.exit(1)});
2
+ import{Readable as fu}from"node:stream";var ue=t=>{let e=new URLSearchParams;return t.page&&e.set("page",String(t.page)),t.limit&&e.set("limit",String(t.limit)),e.toString()?`?${e.toString()}`:""},ut=t=>{let e=new URLSearchParams;for(let[o,r]of Object.entries(t))r!==void 0&&r!==""&&e.set(o,String(r));return e.toString()?`?${e.toString()}`:""},ar=t=>{let e=new URLSearchParams;return t.include?.length&&e.set("include",t.include.join(",")),t.days&&e.set("days",String(t.days)),"limit"in t&&t.limit&&e.set("limit",String(t.limit)),e.toString()?`?${e.toString()}`:""},Ge=class extends Error{constructor(o,r,n,a){super(o);this.status=r;this.code=n;this.field=a;this.name="ClassroomIoApiError"}},Tt=class{constructor(e){this.config=e}async createCourseDraft(e){return this.request("/organization/course-import/drafts",{method:"POST",body:e})}async createCourseDraftFromCourse(e){return this.request("/organization/course-import/drafts/from-course",{method:"POST",body:e})}async getCourseStructure(e){return this.request(`/organization/course-import/courses/${e}/structure`,{method:"GET"})}async updateCourseLandingPage(e,o){return this.request(`/course/${e}/landing-page`,{method:"PUT",body:o})}async getCourseDraft(e){return this.request(`/organization/course-import/drafts/${e}`,{method:"GET"})}async listOrganizationCourses(e={}){let o=new URLSearchParams;e.tags&&o.set("tags",e.tags);let r=o.toString()?`?${o.toString()}`:"";return this.request(`/organization/courses${r}`,{method:"GET"})}async listCourseExercises(e,o={}){return this.requestPaginated(`/public-api/v1/courses/${e}/exercises${ut(o)}`,{method:"GET"})}async getCourseExercise(e,o){return this.request(`/public-api/v1/courses/${e}/exercises/${o}`,{method:"GET"})}async createCourseExercise(e,o){return this.request(`/public-api/v1/courses/${e}/exercises`,{method:"POST",body:o})}async updateCourseExercise(e,o,r,n=!1){let a=n?"?force=true":"";return this.request(`/public-api/v1/courses/${e}/exercises/${o}${a}`,{method:"PUT",body:r})}async deleteCourseExercise(e,o){return this.request(`/public-api/v1/courses/${e}/exercises/${o}`,{method:"DELETE"})}async addExerciseQuestion(e,o,r){return this.request(`/public-api/v1/courses/${e}/exercises/${o}/questions`,{method:"POST",body:r})}async updateExerciseQuestion(e,o,r,n){return this.request(`/public-api/v1/courses/${e}/exercises/${o}/questions/${r}`,{method:"PATCH",body:n})}async deleteExerciseQuestion(e,o,r,n){let a=n?"?force=true":"";return this.request(`/public-api/v1/courses/${e}/exercises/${o}/questions/${r}${a}`,{method:"DELETE"})}async addExerciseSection(e,o,r){return this.request(`/public-api/v1/courses/${e}/exercises/${o}/sections`,{method:"POST",body:r})}async updateExerciseSection(e,o,r,n){return this.request(`/public-api/v1/courses/${e}/exercises/${o}/sections/${r}`,{method:"PATCH",body:n})}async deleteExerciseSection(e,o,r){return this.request(`/public-api/v1/courses/${e}/exercises/${o}/sections/${r}`,{method:"DELETE"})}async notifyCourseExercise(e,o){return this.request(`/public-api/v1/courses/${e}/exercises/${o}/notify`,{method:"POST"})}async getCourseExerciseNotifyStatus(e,o,r,n){let a=`/public-api/v1/courses/${e}/exercises/${o}/notify/${encodeURIComponent(r)}`;return this.request(`${a}${ut({pollCount:n})}`,{method:"GET"})}async listExerciseTemplates(e={}){return this.requestPaginated(`/public-api/v1/exercise-templates${ut(e)}`,{method:"GET"})}async getExerciseTemplate(e){return this.request(`/public-api/v1/exercise-templates/${e}`,{method:"GET"})}async listCourseSubmissions(e,o={}){return this.requestPaginated(`/public-api/v1/courses/${e}/submissions${ut(o)}`,{method:"GET"})}async getCourseSubmission(e,o){return this.request(`/public-api/v1/courses/${e}/submissions/${o}`,{method:"GET"})}async gradeCourseSubmission(e,o,r){return this.request(`/public-api/v1/courses/${e}/submissions/${o}/grades`,{method:"PUT",body:r})}async updateCourseSubmission(e,o,r){return this.request(`/public-api/v1/courses/${e}/submissions/${o}`,{method:"PATCH",body:r})}async deleteCourseSubmission(e,o){return this.request(`/public-api/v1/courses/${e}/submissions/${o}`,{method:"DELETE"})}async getCourseMarks(e,o={}){return this.requestPaginated(`/public-api/v1/courses/${e}/marks${ut(o)}`,{method:"GET"})}async updateCourseDraft(e,o){return this.request(`/organization/course-import/drafts/${e}`,{method:"PUT",body:o})}async tagCourseDraft(e,o){return this.request(`/organization/course-import/drafts/${e}/tags`,{method:"PUT",body:o})}async publishCourseDraft(e,o){return this.request(`/organization/course-import/drafts/${e}/publish`,{method:"POST",body:o})}async publishCourseDraftToExistingCourse(e,o){return this.request(`/organization/course-import/drafts/${e}/publish-existing-course`,{method:"POST",body:o})}async tagCourses(e){return this.request("/organization/tags/courses/assign",{method:"PUT",body:e})}async getCourseCertificate(e){return this.request(`/public-api/v1/courses/${e}/certificate`,{method:"GET"})}async updateCourseCertificate(e,o){return this.request(`/public-api/v1/courses/${e}/certificate`,{method:"PATCH",body:o})}async listCourseCertificates(e,o={}){let r=new URLSearchParams;o.page&&r.set("page",String(o.page)),o.limit&&r.set("limit",String(o.limit)),o.search&&r.set("search",o.search);let n=r.toString()?`?${r.toString()}`:"";return this.requestPaginated(`/public-api/v1/courses/${e}/certificates${n}`,{method:"GET"})}async downloadCourseCertificate(e,o,r){let n=await this.send(`/public-api/v1/courses/${e}/certificates/${o}/download?format=${r}`,{method:"GET"});if(!n.ok){let _=await n.json().catch(()=>null);throw ur(n.status,_)}let a=Buffer.from(await n.arrayBuffer()),z=n.headers.get("content-type")??"application/octet-stream";return{base64:a.toString("base64"),mimeType:z}}async listCourseSections(e,o={}){return this.requestPaginated(`/public-api/v1/courses/${e}/sections${ue(o)}`,{method:"GET"})}async createCourseSection(e,o){return this.request(`/public-api/v1/courses/${e}/sections`,{method:"POST",body:o})}async updateCourseSection(e,o,r){return this.request(`/public-api/v1/courses/${e}/sections/${o}`,{method:"PUT",body:r})}async deleteCourseSection(e,o,r=!1){let n=r?"?force=true":"";return this.request(`/public-api/v1/courses/${e}/sections/${o}${n}`,{method:"DELETE"})}async listCourseLessons(e,o={}){let r=new URLSearchParams(ue(o).slice(1));o.sectionId&&r.set("sectionId",o.sectionId);let n=r.toString()?`?${r.toString()}`:"";return this.requestPaginated(`/public-api/v1/courses/${e}/lessons${n}`,{method:"GET"})}async getCourseLesson(e,o){return this.request(`/public-api/v1/courses/${e}/lessons/${o}`,{method:"GET"})}async deleteCourseLesson(e,o,r=!1){let n=r?"?force=true":"";return this.request(`/public-api/v1/courses/${e}/lessons/${o}${n}`,{method:"DELETE"})}async notifyCourseLessonSessionUpdate(e,o){return this.request(`/public-api/v1/courses/${e}/lessons/${o}/notify-session-update`,{method:"POST"})}async listCourseLessonTranslations(e,o,r){let n=r?`?locale=${r}`:"";return this.request(`/public-api/v1/courses/${e}/lessons/${o}/translations${n}`,{method:"GET"})}async setCourseLessonTranslation(e,o,r,n){return this.request(`/public-api/v1/courses/${e}/lessons/${o}/translations/${r}`,{method:"PUT",body:n})}async listCourseLessonHistory(e,o,r){let n=new URLSearchParams;return r.locale&&n.set("locale",r.locale),r.limit&&n.set("limit",String(r.limit)),r.cursor&&n.set("cursor",r.cursor),this.request(`/public-api/v1/courses/${e}/lessons/${o}/history?${n.toString()}`,{method:"GET"})}async listCourseLessonComments(e,o,r={}){let n=new URLSearchParams;r.limit&&n.set("limit",String(r.limit)),r.cursor&&n.set("cursor",r.cursor);let a=n.toString()?`?${n.toString()}`:"";return this.request(`/public-api/v1/courses/${e}/lessons/${o}/comments${a}`,{method:"GET"})}async createCourseLessonComment(e,o,r){return this.request(`/public-api/v1/courses/${e}/lessons/${o}/comments`,{method:"POST",body:r})}async updateCourseLessonComment(e,o,r,n){return this.request(`/public-api/v1/courses/${e}/lessons/${o}/comments/${r}`,{method:"PUT",body:n})}async deleteCourseLessonComment(e,o,r){return this.request(`/public-api/v1/courses/${e}/lessons/${o}/comments/${r}`,{method:"DELETE"})}async reorderCourseContent(e,o){return this.request(`/public-api/v1/courses/${e}/content/reorder`,{method:"PUT",body:o})}async setCourseContentUnlocked(e,o){return this.request(`/public-api/v1/courses/${e}/content`,{method:"PATCH",body:o})}async deleteCourseContent(e,o,r=!1){let n=r?"?force=true":"";return this.request(`/public-api/v1/courses/${e}/content${n}`,{method:"DELETE",body:o})}async listAudienceMembers(e={}){let o=new URLSearchParams;for(let[n,a]of Object.entries(e))a!==void 0&&a!==""&&o.set(n,String(a));let r=o.toString()?`?${o.toString()}`:"";return this.requestPaginated(`/public-api/v1/audience${r}`,{method:"GET"})}async addAudienceMember(e){return this.request("/public-api/v1/audience",{method:"POST",body:e})}async assignAudienceCourses(e){return this.request("/public-api/v1/audience/assign-courses",{method:"POST",body:e})}async getAudienceMember(e){return this.request(`/public-api/v1/audience/${e}`,{method:"GET"})}async updateAudienceMember(e,o){return this.request(`/public-api/v1/audience/${e}`,{method:"PUT",body:o})}async removeAudienceMember(e){return this.request(`/public-api/v1/audience/${e}`,{method:"DELETE"})}async listTeamMembers(e={}){return this.requestPaginated(`/public-api/v1/team${ue(e)}`,{method:"GET"})}async getTeamMember(e){return this.request(`/public-api/v1/team/${e}`,{method:"GET"})}async updateTeamMember(e,o){return this.request(`/public-api/v1/team/${e}`,{method:"PUT",body:o})}async listCourseMembers(e,o={}){let r=new URLSearchParams;o.page&&r.set("page",String(o.page)),o.limit&&r.set("limit",String(o.limit)),o.search&&r.set("search",o.search),o.roleId&&r.set("roleId",String(o.roleId));let n=r.toString()?`?${r.toString()}`:"";return this.requestPaginated(`/public-api/v1/courses/${e}/members${n}`,{method:"GET"})}async addCourseMember(e,o){return this.request(`/public-api/v1/courses/${e}/members`,{method:"POST",body:o})}async getCourseMember(e,o){return this.request(`/public-api/v1/courses/${e}/members/${o}`,{method:"GET"})}async updateCourseMember(e,o,r){return this.request(`/public-api/v1/courses/${e}/members/${o}`,{method:"PUT",body:r})}async deleteCourseMember(e,o){return this.request(`/public-api/v1/courses/${e}/members/${o}`,{method:"DELETE"})}async resetCourseMemberProgress(e,o){return this.request(`/public-api/v1/courses/${e}/members/${o}/reset-progress`,{method:"POST"})}async getCourseMemberAnalytics(e,o,r={}){let n=new URLSearchParams;for(let[z,_]of Object.entries(r))_!==void 0&&n.set(z,String(_));let a=n.toString()?`?${n.toString()}`:"";return this.request(`/public-api/v1/courses/${e}/members/${o}/analytics${a}`,{method:"GET"})}async listCourseInvites(e,o={}){let r=new URLSearchParams;o.page&&r.set("page",String(o.page)),o.limit&&r.set("limit",String(o.limit));let n=r.toString()?`?${r.toString()}`:"";return this.requestPaginated(`/public-api/v1/courses/${e}/invites${n}`,{method:"GET"})}async createCourseInvite(e,o){return this.request(`/public-api/v1/courses/${e}/invites`,{method:"POST",body:o})}async revokeCourseInvite(e,o){return this.request(`/public-api/v1/courses/${e}/invites/${o}/revoke`,{method:"POST"})}async listCohorts(e={}){return this.requestPaginated(`/public-api/v1/cohorts${ue(e)}`,{method:"GET"})}async createCohort(e){return this.request("/public-api/v1/cohorts",{method:"POST",body:e})}async getCohort(e){return this.request(`/public-api/v1/cohorts/${e}`,{method:"GET"})}async updateCohort(e,o){return this.request(`/public-api/v1/cohorts/${e}`,{method:"PUT",body:o})}async deleteCohort(e){return this.request(`/public-api/v1/cohorts/${e}`,{method:"DELETE"})}async listCohortMembers(e,o={}){return this.requestPaginated(`/public-api/v1/cohorts/${e}/members${ue(o)}`,{method:"GET"})}async addCohortMembers(e,o){return this.request(`/public-api/v1/cohorts/${e}/members`,{method:"POST",body:o})}async updateCohortMember(e,o,r){return this.request(`/public-api/v1/cohorts/${e}/members/${o}`,{method:"PUT",body:r})}async deleteCohortMember(e,o){return this.request(`/public-api/v1/cohorts/${e}/members/${o}`,{method:"DELETE"})}async listCohortCourses(e,o={}){return this.requestPaginated(`/public-api/v1/cohorts/${e}/courses${ue(o)}`,{method:"GET"})}async addCohortCourse(e,o){return this.request(`/public-api/v1/cohorts/${e}/courses`,{method:"POST",body:o})}async removeCohortCourse(e,o){return this.request(`/public-api/v1/cohorts/${e}/courses/${o}`,{method:"DELETE"})}async listCohortNewsfeed(e,o={}){let r=new URLSearchParams;o.cursor&&r.set("cursor",o.cursor),o.limit&&r.set("limit",String(o.limit));let n=r.toString()?`?${r.toString()}`:"";return this.request(`/public-api/v1/cohorts/${e}/newsfeed${n}`,{method:"GET"})}async createCohortNewsfeedPost(e,o){return this.request(`/public-api/v1/cohorts/${e}/newsfeed`,{method:"POST",body:o})}async updateCohortNewsfeedPost(e,o,r){return this.request(`/public-api/v1/cohorts/${e}/newsfeed/${o}`,{method:"PUT",body:r})}async setCohortNewsfeedReaction(e,o,r){return this.request(`/public-api/v1/cohorts/${e}/newsfeed/${o}/react`,{method:"PUT",body:r})}async deleteCohortNewsfeedPost(e,o){return this.request(`/public-api/v1/cohorts/${e}/newsfeed/${o}`,{method:"DELETE"})}async listCohortNewsfeedComments(e,o,r={}){return this.requestPaginated(`/public-api/v1/cohorts/${e}/newsfeed/${o}/comments${ue(r)}`,{method:"GET"})}async createCohortNewsfeedComment(e,o,r){return this.request(`/public-api/v1/cohorts/${e}/newsfeed/${o}/comment`,{method:"POST",body:r})}async deleteCohortNewsfeedComment(e,o,r){return this.request(`/public-api/v1/cohorts/${e}/newsfeed/${o}/comment/${r}`,{method:"DELETE"})}async listCohortGoals(e,o={}){return this.requestPaginated(`/public-api/v1/cohorts/${e}/goals${ue(o)}`,{method:"GET"})}async createCohortGoal(e,o){return this.request(`/public-api/v1/cohorts/${e}/goals`,{method:"POST",body:o})}async getCohortGoal(e,o){return this.request(`/public-api/v1/cohorts/${e}/goals/${o}`,{method:"GET"})}async updateCohortGoal(e,o,r){return this.request(`/public-api/v1/cohorts/${e}/goals/${o}`,{method:"PUT",body:r})}async archiveCohortGoal(e,o){return this.request(`/public-api/v1/cohorts/${e}/goals/${o}/archive`,{method:"POST"})}async deleteCohortGoal(e,o){return this.request(`/public-api/v1/cohorts/${e}/goals/${o}`,{method:"DELETE"})}async evaluateCohortGoal(e,o){return this.request(`/public-api/v1/cohorts/${e}/goals/${o}/evaluate`,{method:"POST"})}async evaluateAllCohortGoals(e){return this.request(`/public-api/v1/cohorts/${e}/goals/evaluate-all`,{method:"POST"})}async getOrgGoalsOverview(e={}){return this.requestPaginated(`/public-api/v1/cohorts/goals/overview${ue(e)}`,{method:"GET"})}async listMyEnrolledCohorts(e={}){return this.requestPaginated(`/public-api/v1/cohorts/enrolled${ue(e)}`,{method:"GET"})}async listMyCohortGoals(e={}){return this.requestPaginated(`/public-api/v1/cohorts/my/goals${ue(e)}`,{method:"GET"})}async inviteStudentsToCohort(e,o){return this.request(`/public-api/v1/cohorts/${e}/invite`,{method:"POST",body:o})}async assignStudentsToCohort(e,o){return this.request(`/public-api/v1/cohorts/${e}/invite/assign`,{method:"POST",body:o})}async getCohortInviteLink(e){return this.request(`/public-api/v1/cohorts/${e}/invite-link`,{method:"GET"})}async createCohortInviteLink(e){return this.request(`/public-api/v1/cohorts/${e}/invite-link`,{method:"POST"})}async setCohortInviteLinkRevoked(e,o){return this.request(`/public-api/v1/cohorts/${e}/invite-link`,{method:"PATCH",body:o})}async getOrgAnalytics(e={}){return this.requestWithMeta(`/public-api/v1/analytics${ar(e)}`)}async listComplianceLearners(e={}){return this.requestPaginated(`/public-api/v1/analytics/compliance/learners${ue(e)}`,{method:"GET"})}async getLearnerAnalytics(e){return this.request(`/public-api/v1/analytics/learners/${e}`,{method:"GET"})}async getCourseAnalytics(e,o={}){return this.requestWithMeta(`/public-api/v1/courses/${e}/analytics${ar(o)}`)}async listCourseAnalyticsStudents(e,o={}){return this.requestPaginated(`/public-api/v1/courses/${e}/analytics/students${ue(o)}`,{method:"GET"})}send(e,o){return fetch(new URL(e,this.config.CLASSROOMIO_API_URL),{method:o.method,headers:{Authorization:`Bearer ${this.config.CLASSROOMIO_API_KEY}`,"content-type":"application/json","user-agent":this.config.CLASSROOMIO_USER_AGENT},body:o.body?JSON.stringify(o.body):void 0})}async requestRaw(e,o){let r=await this.send(e,o),n=await r.json().catch(()=>null);if(!r.ok)throw ur(r.status,n);if(!n||typeof n!="object"||!("success"in n)||!n.success)throw new Ge("ClassroomIO returned an invalid response payload",r.status);return n}async createAssetUpload(e){return this.request("/public-api/v1/assets",{method:"POST",body:e})}async createLesson(e,o){return this.request(`/public-api/v1/courses/${encodeURIComponent(e)}/lessons`,{method:"POST",body:o})}async updateLesson(e,o,r){return this.request(`/public-api/v1/courses/${encodeURIComponent(e)}/lessons/${encodeURIComponent(o)}`,{method:"PUT",body:r})}async putToPresignedUrl(e,o,r,n){let a=await fetch(e,{method:"PUT",headers:{"content-type":r,"content-length":String(n)},body:fu.toWeb(o),duplex:"half"});if(!a.ok)throw new Ge(`Upload to storage failed with status ${a.status}`,a.status)}async request(e,o){return(await this.requestRaw(e,o)).data}async requestPaginated(e,o){let r=await this.requestRaw(e,o);if(!r.pagination)throw new Ge("ClassroomIO returned a response without pagination metadata",502);return{data:r.data,pagination:r.pagination}}async requestWithMeta(e){let o=await this.requestRaw(e,{method:"GET"});return{data:o.data,meta:o.meta}}};function ur(t,e){return new Ge(e?.error??e?.message??`ClassroomIO request failed with status ${t}`,t,e?.code,e?.field)}import{McpServer as tm}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as om}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as xt}from"zod";var zu="0.2.0",_u=xt.object({CLASSROOMIO_API_URL:xt.url().default("https://api.classroomio.com"),CLASSROOMIO_API_KEY:xt.string().min(1),CLASSROOMIO_USER_AGENT:xt.string().min(1).default(`classroomio-mcp/${zu}`)});function cr(t=process.env){return _u.parse(t)}import*as Q from"zod";var St=["overview","traffic","countries","funnel","courseTypes","topCourses","loginActivity","compliance"],to=["summary","funnel"],Eu=[7,30,90,365],Iu=t=>typeof t=="string"?t.split(",").map(e=>e.trim()).filter(Boolean):t,lr=(t,e)=>Q.preprocess(Iu,Q.array(Q.enum(t)).min(1)).default([e]).describe(`Comma-separated sections to return: ${t.join(", ")}. Default: ${e}.`),pr=Q.coerce.number().pipe(Q.literal(Eu,{error:"days must be one of 7, 30, 90, 365"})).default(30).describe("Window in days, counted back from today (UTC): 7, 30, 90 or 365. Default: 30."),dr=Q.object({include:lr(St,"overview"),days:pr,limit:Q.coerce.number().int().min(1).max(20).default(5).describe("Max rows in list sections (overview lists, topCourses, countries). 1-20, default 5.")}),mr=Q.object({include:lr(to,"summary"),days:pr}),gr=Q.object({page:Q.coerce.number().int().min(1).default(1),limit:Q.coerce.number().int().min(1).max(50).default(20)}),br=Q.object({profileId:Q.string().uuid()});import*as c from"zod";var Au=c.string(),We=c.string().nullable(),fr=["compliant","expiring_soon","in_grace_period","non_compliant","waived","in_progress","not_started","no_record"],hr=c.object(Object.fromEntries(fr.map(t=>[t,c.number()]))),Cu=c.object({totalCertificates:c.number(),numberOfCourses:c.number(),totalStudents:c.number(),topCourses:c.array(c.object({id:c.string().uuid(),title:c.string(),enrollments:c.number(),completion:c.number().describe("Percentage of enrolled students who completed the course"),certification:c.number().describe("Percentage of enrolled students who earned the certificate")})).describe("Up to 5 courses with the most students"),recentCertifications:c.array(c.object({id:c.string().uuid().describe("Profile id of the learner"),avatarUrl:c.string().nullable(),name:c.string(),courseId:c.string().uuid(),course:c.string(),date:Au})).describe("Up to 5 most recent certificates")}),yu=c.object({totals:c.object({landingViews:c.number(),coursePageViews:c.number(),uniqueVisitors:c.number(),enrollments:c.number(),completions:c.number()}),sparkline:c.array(c.object({date:c.string().describe("YYYY-MM-DD"),views:c.number(),enrollments:c.number()})).describe("One entry per rolled-up day, oldest first; days with no rollup row are omitted")}),Tu=c.object({country:c.string().describe("ISO 3166-1 alpha-2 code"),views:c.number(),enrollments:c.number()}),zr=c.object({steps:c.array(c.object({name:c.enum(["landing_view","course_page_view","enrollment_completed","course_completed"]),count:c.number(),conversionFromPrev:c.number().nullable().describe("Ratio (0-1) to the previous step; null for the first step")}))}),xu=c.object({type:c.string(),enrollments:c.number(),views:c.number(),completions:c.number(),courseCount:c.number()}),Su=c.object({courseId:c.string().uuid(),title:c.string(),views:c.number()}),Pu=c.object({day:c.enum(["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]),count:c.number()}),wu=c.object({summary:c.object({totalLearners:c.number(),totalCourses:c.number(),counts:hr}),courses:c.array(c.object({courseId:c.string().uuid(),courseTitle:c.string(),learnerCount:c.number(),counts:hr}))}),lm=c.object({groupMemberId:c.string().uuid(),profileId:c.string().uuid().nullable(),fullname:c.string().nullable(),email:c.string().nullable(),avatarUrl:c.string().nullable(),courseId:c.string().uuid(),courseTitle:c.string(),status:c.enum(fr),cycleNumber:c.number().nullable(),dueDate:We,completedAt:We,validUntil:We}),Ou=c.object({id:c.string().uuid(),title:c.string(),lessonId:c.string().uuid().nullable(),lessonTitle:c.string(),status:c.number().nullable().describe("Submission status id; null when not submitted"),score:c.number(),totalPoints:c.number(),isCompleted:c.boolean()}),pm=c.object({user:c.object({id:c.string().uuid(),fullName:c.string(),email:c.string(),avatarUrl:c.string(),lastSeen:We}),overallCourseProgress:c.number().describe("Percentage of lessons and exercises completed across all courses"),overallAverageGrade:c.number().nullable().describe("Average of per-course grades; null when nothing is graded"),courses:c.array(c.object({id:c.string().uuid(),title:c.string(),type:c.string().nullable(),lessonsCount:c.number(),lessonsCompleted:c.number(),exercisesCount:c.number(),exercisesCompleted:c.number(),progressPercentage:c.number(),progressFailed:c.boolean().describe("True when progress could not be loaded; the course is left out of overall progress"),averageGrade:c.number().nullable(),certificateEarnedAt:We,complianceStatus:c.string().nullable(),exercises:c.array(Ou).nullable().describe("null when exercise stats could not be loaded")}))}),Ru=c.object({totalTutors:c.number(),totalStudents:c.number(),totalLessons:c.number(),totalExercises:c.number(),averageProgress:c.number().describe("Average per-student progress (lessons and exercises), as a percentage"),averageExerciseCompletion:c.number().describe("Average share of exercises submitted per student, as a percentage"),averageGrade:c.number().describe("Average per-student grade, as a percentage")}),dm=c.object({profileId:c.string().uuid(),fullname:c.string(),email:c.string(),avatarUrl:c.string(),lessonsCompleted:c.number(),totalLessons:c.number(),exercisesSubmitted:c.number(),totalExercises:c.number(),averageGrade:c.number(),progressPercentage:c.number(),lastSeen:We}),_r=c.string().nullable().describe("When the oldest returned section was computed (ISO 8601); null when no section was returned"),mm=c.object({overview:Cu.optional(),traffic:yu.optional(),countries:c.array(Tu).optional(),funnel:zr.optional(),courseTypes:c.array(xu).optional(),topCourses:c.array(Su).optional(),loginActivity:c.array(Pu).optional(),compliance:wu.optional()}).describe("Only the requested sections the caller may see are present"),gm=c.object({include:c.array(c.enum(St)),days:c.number(),limit:c.number(),omitted:c.array(c.object({section:c.enum(St),reason:c.enum(["requires_org_admin"])})).describe("Requested sections left out because the key creator may not see them"),generatedAt:_r}),bm=c.object({summary:Ru.optional(),funnel:zr.optional()}).describe("Only the requested sections are present"),hm=c.object({include:c.array(c.enum(to)),days:c.number(),generatedAt:_r});import*as ce from"zod";var C={RADIO:"RADIO",CHECKBOX:"CHECKBOX",TEXTAREA:"TEXTAREA",TRUE_FALSE:"TRUE_FALSE",SHORT_ANSWER:"SHORT_ANSWER",NUMERIC:"NUMERIC",FILL_BLANK:"FILL_BLANK",FILE_UPLOAD:"FILE_UPLOAD",MATCHING:"MATCHING",ORDERING:"ORDERING",HOTSPOT:"HOTSPOT",LINK:"LINK",WORD_BANK:"WORD_BANK",STAR:"STAR",VIDEO_RECORDING:"VIDEO_RECORDING",THUMBS:"THUMBS"},zm=[C.RADIO,C.CHECKBOX,C.TEXTAREA];var $e=[{key:C.RADIO,typename:"RADIO",label:"Single answer",id:1,autoGradable:!0,supportsPartialCredit:!1,manualGradingRequired:!1},{key:C.CHECKBOX,typename:"CHECKBOX",label:"Multiple answers",id:2,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:C.TEXTAREA,typename:"TEXTAREA",label:"Paragraph",id:3,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0},{key:C.TRUE_FALSE,typename:"TRUE_FALSE",label:"True/False",id:4,autoGradable:!0,supportsPartialCredit:!1,manualGradingRequired:!1},{key:C.SHORT_ANSWER,typename:"SHORT_ANSWER",label:"Short answer",id:5,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0},{key:C.NUMERIC,typename:"NUMERIC",label:"Numeric answer",id:6,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:C.FILL_BLANK,typename:"FILL_BLANK",label:"Fill in the blank",id:7,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:C.FILE_UPLOAD,typename:"FILE_UPLOAD",label:"File upload",id:8,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0},{key:C.ORDERING,typename:"ORDERING",label:"Ordering",id:9,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:C.LINK,typename:"LINK",label:"Links",id:10,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0},{key:C.WORD_BANK,typename:"WORD_BANK",label:"Word bank",id:11,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:C.STAR,typename:"STAR",label:"Star rating",id:12,autoGradable:!0,supportsPartialCredit:!1,manualGradingRequired:!1},{key:C.VIDEO_RECORDING,typename:"VIDEO_RECORDING",label:"Video recording",id:13,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0},{key:C.THUMBS,typename:"THUMBS",label:"Thumbs up/down",id:14,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!1},{key:C.MATCHING,typename:"MATCHING",label:"Matching",id:15,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1,disabled:!0},{key:C.HOTSPOT,typename:"HOTSPOT",label:"Hotspot",id:16,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1,disabled:!0}],oo=$e.filter(t=>!t.disabled),vu=Object.freeze($e.reduce((t,e)=>(t[e.key]=e,t),{})),ro=Object.freeze($e.reduce((t,e)=>(t[e.key]=e.id,t),{})),Lu=Object.freeze($e.reduce((t,e)=>(t[e.typename]=e.key,t),{})),Er=Object.freeze($e.reduce((t,e)=>(t[e.id]=e.key,t),{}));var Cm=new Set([C.FILE_UPLOAD,C.ORDERING,C.LINK,C.STAR,C.VIDEO_RECORDING]);var Om=$e.filter(t=>t.autoGradable).map(t=>t.id);function ct(t){let e=t?.maxStars,o=typeof e=="number"?e:e!=null?Number(e):NaN;return!Number.isFinite(o)||o<1?5:Math.min(10,Math.max(1,Math.floor(o)))}function lt(t,e){return typeof t=="number"&&Number.isInteger(t)&&t>=1&&t<=e}var Nu=new Set(["true","1","yes"]),Du=new Set(["false","0","no"]);function Ir(t){let e=t.trim().toLowerCase();if(Nu.has(e))return!0;if(Du.has(e))return!1}var Uu={type:"RADIO",toApiPayload(t,e){return{questionId:e,optionId:t.optionId}},fromApiPayload(t){let e=t.optionId;return e===void 0||Number.isNaN(e)?null:{type:"RADIO",optionId:e}}},Zu={type:"CHECKBOX",toApiPayload(t,e){return{questionId:e,answer:JSON.stringify({type:"CHECKBOX",optionIds:t.optionIds})}},fromApiPayload(t){if(!t.answer)return null;let{optionIds:e}=JSON.parse(t.answer);return Array.isArray(e)?{type:"CHECKBOX",optionIds:e}:null}},ku={type:"TRUE_FALSE",toApiPayload(t,e){return{questionId:e,answer:String(t.value)}},fromApiPayload(t){if(t.answer===void 0)return null;let e=Ir(t.answer);return e===void 0?null:{type:"TRUE_FALSE",value:e}}},ju={type:"THUMBS",toApiPayload(t,e){return{questionId:e,answer:String(t.value)}},fromApiPayload(t){if(t.answer===void 0)return null;let e=Ir(t.answer);return e===void 0?null:{type:"THUMBS",value:e}}},Mu={type:"TEXTAREA",toApiPayload(t,e){return{questionId:e,answer:t.text}},fromApiPayload(t){return t.answer===void 0?null:{type:"TEXTAREA",text:t.answer}}},Gu={type:"SHORT_ANSWER",toApiPayload(t,e){return{questionId:e,answer:t.text}},fromApiPayload(t){return t.answer===void 0?null:{type:"SHORT_ANSWER",text:t.answer}}},Fu={type:"NUMERIC",toApiPayload(t,e){return{questionId:e,answer:JSON.stringify({type:"NUMERIC",value:t.value})}},fromApiPayload(t){if(!t.answer)return null;let{value:e}=JSON.parse(t.answer);return{type:"NUMERIC",value:e}}},qu={type:"FILL_BLANK",toApiPayload(t,e){return{questionId:e,answer:JSON.stringify({type:"FILL_BLANK",answers:t.values})}},fromApiPayload(t){if(!t.answer)return null;let{answers:e}=JSON.parse(t.answer),o=e.map(r=>String(r).trim()).filter(Boolean);return o.length>0?{type:"FILL_BLANK",values:o}:null}},$u={type:"WORD_BANK",toApiPayload(t,e){return{questionId:e,answer:JSON.stringify({type:"WORD_BANK",filledBlanks:t.filledBlanks})}},fromApiPayload(t){if(!t.answer)return null;try{let e=JSON.parse(t.answer);return e?.type!=="WORD_BANK"||!Array.isArray(e.filledBlanks)?null:{type:"WORD_BANK",filledBlanks:e.filledBlanks.map(o=>String(o??""))}}catch{return null}}},Hu={type:"FILE_UPLOAD",toApiPayload(t,e){let o={fileKey:t.fileKey,fileName:t.fileName,mimeType:t.mimeType,size:t.size};return{questionId:e,answer:JSON.stringify(o)}},fromApiPayload(t){if(!t.answer)return null;let e=JSON.parse(t.answer);return{type:"FILE_UPLOAD",fileKey:e.fileKey,fileName:e.fileName??"",mimeType:e.mimeType,size:e.size}}},Bu={type:"MATCHING",toApiPayload(t,e){return{questionId:e,answer:JSON.stringify({type:"MATCHING",value:t.pairs})}},fromApiPayload(t){if(!t.answer)return null;let{value:e}=JSON.parse(t.answer);return e.length>0?{type:"MATCHING",pairs:e}:null}},Qu={type:"ORDERING",toApiPayload(t,e){return{questionId:e,answer:JSON.stringify({type:"ORDERING",value:t.orderedValues})}},fromApiPayload(t){if(!t.answer)return null;let{value:e}=JSON.parse(t.answer);return e.length>0?{type:"ORDERING",orderedValues:e}:null}},Vu={type:"LINK",toApiPayload(t,e){return{questionId:e,answer:JSON.stringify({type:"LINK",links:t.urls})}},fromApiPayload(t){if(!t.answer)return null;let{links:e}=JSON.parse(t.answer),o=e.map(r=>String(r).trim()).filter(Boolean);return o.length>0?{type:"LINK",urls:o}:null}},Ku={type:"HOTSPOT",toApiPayload(t,e){return{questionId:e,answer:JSON.stringify({type:"HOTSPOT",value:t.coordinates})}},fromApiPayload(t){if(!t.answer)return null;let{value:e}=JSON.parse(t.answer);return e.length>0?{type:"HOTSPOT",coordinates:e}:null}},Wu={type:"STAR",toApiPayload(t,e){return{questionId:e,answer:JSON.stringify({type:"STAR",value:t.value})}},fromApiPayload(t,e){if(!t.answer)return null;try{let o=JSON.parse(t.answer);if(o?.type!=="STAR")return null;let r=ct(e.settings);return lt(o.value,r)?{type:"STAR",value:o.value}:null}catch{return null}}};function Yu(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.assetId=="string"&&e.assetId.length>0&&typeof e.storageKey=="string"&&e.storageKey.length>0&&typeof e.fileName=="string"&&typeof e.mimeType=="string"&&e.mimeType.startsWith("video/")&&typeof e.size=="number"&&Number.isFinite(e.size)&&e.size>0&&typeof e.durationSeconds=="number"&&Number.isFinite(e.durationSeconds)&&e.durationSeconds>0&&typeof e.recordedAt=="string"&&typeof e.uploadedAt=="string"&&e.provider==="cloudflare"}var Xu={type:"VIDEO_RECORDING",toApiPayload(t,e){let{playbackUrl:o,...r}=t;return{questionId:e,answer:JSON.stringify(r)}},fromApiPayload(t){if(!t.answer)return null;try{let e=JSON.parse(t.answer);return e?.type!=="VIDEO_RECORDING"||!Yu(e)?null:{type:"VIDEO_RECORDING",assetId:e.assetId,storageKey:e.storageKey,fileName:e.fileName,mimeType:e.mimeType,size:e.size,durationSeconds:e.durationSeconds,recordedAt:e.recordedAt,uploadedAt:e.uploadedAt,provider:"cloudflare",retakeCount:typeof e.retakeCount=="number"?e.retakeCount:void 0}}catch{return null}}},Km={[C.RADIO]:Uu,[C.CHECKBOX]:Zu,[C.TRUE_FALSE]:ku,[C.THUMBS]:ju,[C.TEXTAREA]:Mu,[C.SHORT_ANSWER]:Gu,[C.NUMERIC]:Fu,[C.FILL_BLANK]:qu,[C.WORD_BANK]:$u,[C.FILE_UPLOAD]:Hu,[C.MATCHING]:Bu,[C.ORDERING]:Qu,[C.LINK]:Vu,[C.HOTSPOT]:Ku,[C.STAR]:Wu,[C.VIDEO_RECORDING]:Xu};var og=oo.map(t=>`${t.id}=${t.typename} (${t.label})`).join(", ");function Ar(){return`Supported questionTypeId values: ${oo.map(e=>`${e.id}=${e.typename} (${e.label})`).join(", ")}. TRUE_FALSE (4) is auto-graded with settings.correctValue (boolean) and True/False labels for factual statements. THUMBS (14) is open-ended (no correct answer, not auto-graded): two customizable Yes/No-style options with thumbs icons for sentiment or poll-style questions. MATCHING and HOTSPOT are disabled and must not be used.`}var Cr=[{value:"application/pdf",label:"PDF (.pdf)",aliases:["pdf",".pdf"]},{value:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",label:"DOCX (.docx)",aliases:["docx",".docx"]},{value:"application/msword",label:"DOC (.doc)",aliases:["doc",".doc"]},{value:"text/csv",label:"CSV (.csv)",aliases:["csv",".csv","application/csv","application/vnd.ms-excel"]},{value:"application/x-ipynb+json",label:"Jupyter Notebook (.ipynb)",aliases:["ipynb",".ipynb","application/json"]},{value:"image/jpeg",label:"JPEG (.jpg, .jpeg)",aliases:["jpg",".jpg","jpeg",".jpeg","image/jpg"]},{value:"image/png",label:"PNG (.png)",aliases:["png",".png"]},{value:"image/webp",label:"WEBP (.webp)",aliases:["webp",".webp"]},{value:"image/gif",label:"GIF (.gif)",aliases:["gif",".gif"]},{value:"video/mp4",label:"MP4 (.mp4)",aliases:["mp4",".mp4"]},{value:"video/quicktime",label:"MOV (.mov)",aliases:["mov",".mov"]},{value:"video/x-msvideo",label:"AVI (.avi)",aliases:["avi",".avi"]},{value:"video/x-matroska",label:"MKV (.mkv)",aliases:["mkv",".mkv"]}],Ju=[...Cr.flatMap(t=>[t.value,...t.aliases.filter(e=>e.includes("/"))])],Sg=new Map(Cr.map(t=>[t.value,t]));var Pt=["video/mp4","video/quicktime","video/x-msvideo","video/x-matroska"];var x=ro;var Lg=ce.object({kind:ce.enum(["video"]).describe("Only video uploads are supported today."),fileName:ce.string().min(1).max(255).describe("Original file name, used as the asset title."),mimeType:ce.enum(Pt).describe("Content type the upload will be sent with."),byteSize:ce.number().int().positive().describe("Size of the file in bytes. Checked against the organization upload limit.")}),Ng=ce.object({assetId:ce.string().uuid().describe("Reference this when attaching the asset to a lesson."),uploadUrl:ce.string().describe("Send the file bytes here with a single PUT and no authorization header."),expiresAt:ce.string().describe("ISO timestamp after which uploadUrl stops working.")});import*as S from"zod";var yr=S.enum(["7d","30d","90d","180d","never"]),Tr=S.object({page:S.coerce.number().int().min(1).default(1),limit:S.coerce.number().int().min(1).max(100).default(20),search:S.string().trim().optional(),sortBy:S.enum(["createdAt","name","email","lastLoginAt","lastActiveAt"]).default("createdAt"),sortOrder:S.enum(["asc","desc"]).default("desc"),status:S.enum(["ACTIVE","DEACTIVATED","ARCHIVED"]).default("ACTIVE"),inviteStatus:S.enum(["active","pending","expired","revoked"]).optional(),enrollment:S.enum(["enrolled","not_enrolled"]).optional(),completion:S.enum(["not_started","in_progress","completed"]).optional(),lastLoginBefore:yr.optional(),lastActiveBefore:yr.optional(),excludeRecentJoiners:S.union([S.boolean(),S.enum(["true","false"])]).default(!0).transform(t=>t===!0||t==="true")}),xr=S.object({email:S.string().email(),courseIds:S.array(S.string().uuid()).optional(),sendEmail:S.boolean().default(!0)}),no=S.string().trim().min(3).max(120),so=S.object({email:S.string().email().optional().describe("Pending members only: corrects the invited email"),sendEmail:S.boolean().default(!0).describe("Whether to email the new invite when email changes"),name:no.optional().describe("Joined members only: sets the full name on their profile")}).refine(t=>t.email!==void 0||t.name!==void 0,{message:"No fields to update"}).refine(t=>t.email===void 0||t.name===void 0,{message:"Send email or name, not both: email is for pending members and name is for joined members",path:["name"]}),Sr=S.object({profileIds:S.array(S.uuid()).min(1).max(500),courseIds:S.array(S.uuid()).optional(),cohortIds:S.array(S.uuid()).optional(),sendEmail:S.boolean().default(!0)}).refine(t=>(t.courseIds?.length??0)>0||(t.cohortIds?.length??0)>0,{message:"At least one course or cohort must be selected",path:["courseIds"]}),wt=S.object({memberId:S.coerce.number().int().positive()});import*as I from"zod";var ec=I.string(),Ot=I.string().nullable(),Pr=I.enum(["ACTIVE","DEACTIVATED","ARCHIVED"]),Zg=I.object({id:I.number(),profileId:I.string().uuid().nullable().describe("null until the invited person creates an account"),name:I.string(),email:I.string(),avatarUrl:I.string(),createdAt:I.string().describe('Day the member joined this organization, as a date string (e.g. "Mon Oct 05 2026")'),status:I.enum(["active","pending","expired","revoked"]).describe("Invite state"),memberStatus:Pr.describe("Lifecycle state of the membership"),lastLoginAt:Ot,lastActiveAt:Ot,enrolledCount:I.number(),completedCount:I.number(),progressPercent:I.number(),certificateCount:I.number().describe("Courses in this organization where the learner has earned a certificate")}),kg=I.object({imported:I.number(),enrolled:I.number(),emailsSent:I.number(),emailsFailed:I.number(),rows:I.array(I.object({email:I.string(),name:I.string().optional(),status:I.enum(["ready","already_member","invalid_email","duplicate_in_file","is_staff","over_seat_limit","missing_email"])})),assigned:I.number(),alreadyEnrolledInCourses:I.number(),alreadyEnrolledInCohorts:I.number(),pendingInvitesRenewed:I.number(),truncated:I.number().describe("Rows beyond the import limit, which were not processed")}),jg=I.object({assigned:I.number(),alreadyEnrolled:I.number(),emailsSent:I.number()}),Mg=I.object({id:I.number(),organizationId:I.string().uuid(),roleId:I.number().describe("1 = admin, 2 = tutor, 3 = student"),profileId:I.string().uuid().nullable(),email:I.string().nullable(),verified:I.boolean().nullable(),status:Pr,statusChangedAt:Ot,statusChangedBy:I.string().uuid().nullable(),lastActiveAt:Ot,createdAt:ec});import*as ie from"zod";var Z=ie.object({cohortId:ie.string().uuid()}),wr=ie.object({name:ie.string().min(1).max(255),description:ie.string().max(2e3).optional(),coverImage:ie.string().url().optional()}),io=ie.object({name:ie.string().min(1).max(255).optional(),description:ie.string().max(2e3).nullable().optional(),coverImage:ie.string().url().nullable().optional(),status:ie.enum(["ACTIVE","INACTIVE","ARCHIVED"]).optional()}).refine(t=>Object.keys(t).length>0,{message:"No fields to update"});import*as pt from"zod";var Or=Z.extend({courseId:pt.string().uuid()}),Rr=pt.object({courseId:pt.string().uuid()});import*as w from"zod";var vr=["complete_all","n_of_m","score","pass_rate","readiness"],Lr=["absolute","relative_to_join","recurring","none"],He=Z.extend({goalId:w.string().uuid()}),Nr=w.object({type:w.enum(vr),title:w.string().min(1).max(255),description:w.string().max(2e3).nullable().optional(),courseIds:w.array(w.string().uuid()).max(50),requiredCount:w.number().int().min(1).max(50).nullable().optional(),scoreThreshold:w.number().int().min(0).max(100).nullable().optional(),teamPassRateThreshold:w.number().int().min(0).max(100).nullable().optional(),reminderDaysBefore:w.array(w.number().int().min(0).max(365)).max(5).default([7,1]),deadlineKind:w.enum(Lr).default("none"),deadlineDate:w.string().datetime().nullable().optional(),relativeDays:w.number().int().min(1).max(3650).nullable().optional(),recurringMonths:w.number().int().min(1).max(120).nullable().optional()}).refine(t=>t.courseIds.length>0,{message:"At least one course is required",path:["courseIds"]}).refine(t=>t.type!=="n_of_m"||t.requiredCount&&t.requiredCount>0,{message:"requiredCount is required for n_of_m goals",path:["requiredCount"]}).refine(t=>t.type!=="n_of_m"||!t.requiredCount||t.requiredCount<=t.courseIds.length,{message:"requiredCount cannot exceed selected courses",path:["requiredCount"]}).refine(t=>t.type!=="score"||t.scoreThreshold!==null&&t.scoreThreshold!==void 0,{message:"scoreThreshold is required for score goals",path:["scoreThreshold"]}).refine(t=>t.type!=="pass_rate"||t.scoreThreshold!==null&&t.scoreThreshold!==void 0&&t.teamPassRateThreshold!==null&&t.teamPassRateThreshold!==void 0,{message:"scoreThreshold and teamPassRateThreshold are required for pass_rate goals",path:["teamPassRateThreshold"]}).refine(t=>t.deadlineKind!=="absolute"||!!t.deadlineDate,{message:"deadlineDate is required for absolute deadlines",path:["deadlineDate"]}).refine(t=>t.deadlineKind!=="relative_to_join"||!!t.relativeDays,{message:"relativeDays is required for relative-to-join deadlines",path:["relativeDays"]}).refine(t=>t.deadlineKind!=="recurring"||!!t.recurringMonths,{message:"recurringMonths is required for recurring deadlines",path:["recurringMonths"]}).refine(t=>t.type!=="readiness"||t.deadlineKind==="none",{message:"Readiness goals do not support deadlines",path:["deadlineKind"]}),ao=w.object({type:w.enum(vr).optional(),title:w.string().min(1).max(255).optional(),description:w.string().max(2e3).nullable().optional(),courseIds:w.array(w.string().uuid()).max(50).optional(),requiredCount:w.number().int().min(1).max(50).nullable().optional(),scoreThreshold:w.number().int().min(0).max(100).nullable().optional(),teamPassRateThreshold:w.number().int().min(0).max(100).nullable().optional(),reminderDaysBefore:w.array(w.number().int().min(0).max(365)).max(5).optional(),deadlineKind:w.enum(Lr).optional(),deadlineDate:w.string().datetime().nullable().optional(),relativeDays:w.number().int().min(1).max(3650).nullable().optional(),recurringMonths:w.number().int().min(1).max(120).nullable().optional(),status:w.enum(["active","archived"]).optional()}).refine(t=>Object.keys(t).length>0,{message:"No fields to update"});import*as le from"zod";var Dr=le.object({recipientCsv:le.string().min(1).max(25e3),sendEmail:le.boolean().default(!0)}),Ur=le.object({profileIds:le.array(le.string().uuid()).min(1).max(500),sendEmail:le.boolean().default(!0)}),Zr=le.object({isRevoked:le.boolean()});import*as te from"zod";var ee={ADMIN:1,TUTOR:2,STUDENT:3};var kr=["academy","app","apps","blog","build","campaign","church","cloud","community","conference","course","courses","demo","dev","embed","embeds","forum","group","groups","help","launch","launchweek","mobile","play","prod","production","schools","stage","staging","support","teacher","teachers","tech","training"];var jr;(function(t){t.Section="SECTION",t.Lesson="LESSON",t.Exercise="EXERCISE"})(jr||(jr={}));var Rt=["SELF_PACED","LIVE_CLASS","COMPLIANCE","PUBLIC"];var Mr="embed.classroomio.com",tc=`https://${Mr}`;var oc=["auto","manual","publish"],tb=[...oc,"none"],ob=600*1e3,rb=3600*1e3;var Gr=15,Fr=10080,qr=5,$r=["pending","queued","sent","failed","skipped"];var Hr=te.union([te.literal(ee.TUTOR),te.literal(ee.STUDENT)]),vt=Z.extend({memberId:te.string().uuid()}),Br=te.object({members:te.array(te.object({profileId:te.string().uuid().optional(),email:te.string().email().optional(),name:te.string().optional(),roleId:Hr})).min(1).refine(t=>t.every(e=>e.profileId||e.email),{message:"Each member must include a profileId or email"}).refine(t=>{let e=t.map(o=>o.profileId??o.email?.toLowerCase().trim());return new Set(e).size===e.length},{message:"Each profileId or email may appear only once per request"})}),Qr=te.object({roleId:Hr});import*as V from"zod";var rc=["clap","smile","thumbsup","thumbsdown"],nc=/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(\.\d{1,6})?(Z|[+-]\d{2}(:?\d{2})?)(\|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})?$/i,Se=Z.extend({feedId:V.string().uuid()}),Vr=Se.extend({commentId:V.coerce.number().int()}),Kr=V.object({cursor:V.string().refine(t=>nc.test(t)&&!Number.isNaN(Date.parse(t.split("|")[0])),{message:"Invalid cursor"}).optional(),limit:V.coerce.number().int().min(1).max(50).default(10)}),Wr=V.object({content:V.string().min(1),isPinned:V.boolean().optional()}),uo=V.object({content:V.string().min(1).optional(),isPinned:V.boolean().optional()}).refine(t=>Object.keys(t).length>0,{message:"No fields to update"}),Yr=V.object({reaction:V.enum(rc).nullable()}),Xr=V.object({content:V.string().min(1)});import*as u from"zod";var Ue=u.string(),Ye=u.string().nullable(),Jr=["complete_all","n_of_m","score","pass_rate","readiness"],en=["absolute","relative_to_join","recurring","none"],tn=["not_started","in_progress","completed","at_risk","overdue","waived"],sc=u.object({id:u.string().uuid(),createdAt:Ue,updatedAt:Ye,organizationId:u.string().uuid(),name:u.string(),description:u.string().nullable(),coverImage:u.string().nullable(),status:u.enum(["ACTIVE","INACTIVE","ARCHIVED"]),createdByProfileId:u.string().uuid().nullable()}),ic=sc.extend({courseCount:u.number(),studentCount:u.number()}),yb=ic.extend({roleId:u.number()}),on=u.object({id:u.string().uuid(),cohortId:u.string().uuid(),profileId:u.string().uuid().nullable(),roleId:u.number(),email:u.string().nullable(),createdAt:Ue}),Tb=on.extend({profile:u.object({id:u.string().uuid(),fullname:u.string().nullable(),username:u.string().nullable(),avatarUrl:u.string().nullable(),email:u.string().nullable()}).nullable()}),xb=u.object({added:u.array(on),errors:u.array(u.object({index:u.number().describe("Position of the failed entry in the request members array"),email:u.string().nullable(),profileId:u.string().nullable(),code:u.string(),message:u.string()}))}),ac=u.object({id:u.string().uuid(),cohortId:u.string().uuid(),courseId:u.string().uuid(),addedAt:Ue}),Sb=ac.extend({course:u.object({id:u.string().uuid(),title:u.string().nullable(),description:u.string().nullable(),coverImage:u.string().nullable(),slug:u.string().nullable(),status:u.string().nullable(),isPublished:u.boolean().nullable()})}),uc=u.object({clap:u.array(u.string()),smile:u.array(u.string()),thumbsup:u.array(u.string()),thumbsdown:u.array(u.string())}),cc=u.object({id:u.string().uuid(),createdAt:Ue,authorId:u.string().uuid().nullable().describe("Cohort member id of the author"),cohortId:u.string().uuid().nullable(),content:u.string().nullable(),reaction:uc.nullable().describe("Cohort member ids per reaction type"),isPinned:u.boolean()}),rn={authorProfileId:u.string().nullable(),authorFullname:u.string().nullable(),authorUsername:u.string().nullable(),authorAvatarUrl:u.string().nullable()},lc=cc.extend({...rn,commentCount:u.number()}),Pb=u.object({items:u.array(lc),totalCount:u.number(),hasMore:u.boolean(),nextCursor:u.string().nullable()}),pc=u.object({id:u.number(),createdAt:Ue,authorId:u.string().uuid().nullable().describe("Cohort member id of the author"),content:u.string().nullable(),cohortNewsfeedId:u.string().uuid().nullable()}),wb=pc.extend(rn),nn=u.object({id:u.string().uuid(),cohortId:u.string().uuid(),title:u.string(),description:u.string().nullable(),type:u.enum(Jr),courseIds:u.array(u.string()),requiredCount:u.number().nullable(),scoreThreshold:u.number().nullable(),teamPassRateThreshold:u.number().nullable(),deadlineKind:u.enum(en),deadlineDate:Ye,relativeDays:u.number().nullable(),recurringMonths:u.number().nullable(),reminderDaysBefore:u.array(u.number()),status:u.enum(["active","archived"]),createdByProfileId:u.string().uuid().nullable(),createdAt:Ue,updatedAt:Ue}),Ob=nn.extend({statusCounts:u.partialRecord(u.enum(tn),u.number()).describe("Learner count per assignment status; statuses with no learners are omitted")}),Rb=u.object({evaluated:u.number()}),vb=u.object({goalId:u.string().uuid(),cohortId:u.string().uuid(),cohortName:u.string(),title:u.string(),type:u.enum(Jr),deadlineKind:u.enum(en),totalLearners:u.number(),completedCount:u.number(),inProgressCount:u.number(),atRiskCount:u.number(),overdueCount:u.number(),notStartedCount:u.number(),waivedCount:u.number(),onTrackPct:u.number()}),Lb=u.object({id:u.string().uuid(),goalId:u.string().uuid(),cohortMemberId:u.string().uuid(),dueDate:Ye,status:u.enum(tn),completedCount:u.number(),requiredCount:u.number(),completedAt:Ye,lastEvaluatedAt:Ye,createdAt:Ue,updatedAt:Ue,goal:nn,cohortId:u.string().uuid(),cohortName:u.string()}),Nb=u.object({imported:u.number(),enrolled:u.number(),emailsSent:u.number().describe("Emails queued for delivery, not confirmed delivered"),emailsFailed:u.number(),rows:u.array(u.object({email:u.string(),name:u.string().optional(),status:u.enum(["ready","already_member","invalid_email","duplicate_in_file","is_staff","over_seat_limit","missing_email"])})),assigned:u.number().optional(),alreadyEnrolledInCourses:u.number().optional(),alreadyEnrolledInCohorts:u.number().optional(),pendingInvitesRenewed:u.number().optional(),truncated:u.number().optional()}),Db=u.object({assigned:u.number(),alreadyEnrolled:u.number(),emailsSent:u.number()}),Ub=u.object({id:u.string().uuid(),token:u.string(),inviteLink:u.string().describe("Full join URL to share with students"),isRevoked:u.boolean(),joinCount:u.number(),lastUsedAt:Ye});import*as Y from"zod";import*as i from"zod";function sn(t){if(typeof t=="number"&&Number.isFinite(t))return t;if(typeof t=="string"&&t.trim()){let e=Number(t);if(Number.isFinite(e))return e}}import*as Ze from"zod";var dc=Ze.enum(["waves","dotted","none"]),mc=Ze.object({title:Ze.string().min(1).max(120),description:Ze.string().min(1).max(500),buttonLabel:Ze.string().min(1).max(40),buttonUrl:Ze.string().url(),animation:dc.default("waves")}),an=mc.nullable();import*as un from"zod";var Fe=un.enum(Rt);import*as pe from"zod";var cn=pe.array(pe.number().int().min(Gr).max(Fr)).max(qr).refine(t=>new Set(t).size===t.length,{message:"Reminder offsets must be unique"}),qb=pe.object({page:pe.coerce.number().int().min(1).default(1),limit:pe.coerce.number().int().min(1).max(100).default(50),status:pe.enum($r).optional(),lessonId:pe.uuid().optional()});var Wb=i.object({limit:i.string().transform(Number).pipe(i.number().min(1).max(50)).optional(),page:i.string().transform(Number).pipe(i.number().min(1)).optional()}),Yb=i.object({title:i.string().min(1),description:i.string().optional(),slug:i.string().min(1),organizationId:i.string().min(1)}),Xb=i.object({courseId:i.string().min(1)}),Jb=i.object({courseId:i.string().min(1)}),eh=i.object({slug:i.string().optional()}),th=i.object({slug:i.string().min(1)}),oh=i.object({courseId:i.string().min(1)}),rh=i.object({inviteToken:i.string().min(1).optional()}),nh=i.object({courseId:i.string().min(1)}),ln=i.object({name:i.string().max(80).default(""),role:i.string().max(80).default(""),enabled:i.boolean().default(!0),signatureUrl:i.url().optional()}),gc=i.enum(["classique","brutalist","noir","poster","minimal"]),bc=i.object({templateId:gc,accentColor:i.string().regex(/^#[0-9a-fA-F]{6}$/,{message:"Accent must be a 6-digit hex color"}),subtitle:i.string().max(120).optional(),descriptionOverride:i.string().max(500).optional(),signatories:i.tuple([ln,ln]).optional(),idFormat:i.string().max(40).optional()}),sh=i.object({studentName:i.string().min(1).max(120),studentId:i.string().min(1).max(64).optional(),issuedAt:i.preprocess(t=>{if(typeof t!="string"||t.trim()==="")return;let e=new Date(t);if(!Number.isNaN(e.getTime()))return e.toISOString()},i.string().datetime().optional()),previewMode:i.boolean().optional()}),ih=i.object({courseTitle:i.string().min(1),orgName:i.string().min(1),orgTheme:i.string().min(1),lessons:i.array(i.object({lessonTitle:i.string().min(1),lessonNumber:i.string().min(1),lessonNote:i.string(),slideUrl:i.string().url().optional(),video:i.array(i.string().url()).optional()}))}),ah=i.object({title:i.string().min(1),number:i.string().min(1),orgName:i.string().min(1),note:i.string(),slideUrl:i.string().url().optional(),video:i.array(i.string().url()).optional(),courseTitle:i.string().min(1)}),uh=i.object({fileName:i.string().min(1),fileType:i.enum(Pt),fileSize:i.number().int().min(0).optional()}),ch=i.object({fileName:i.string().min(1),fileType:i.string(),fileSize:i.number().int().min(0).optional(),acceptedTypes:i.array(i.string()).optional()}),lh=i.object({keys:i.array(i.string().min(1)).min(1)}),pn=i.object({id:i.string().min(1),type:i.enum(["LESSON","EXERCISE"]),isUnlocked:i.boolean().optional(),order:i.number().int().min(1).optional(),sectionId:i.string().nullable().optional()}),ph=i.object({items:i.array(pn).min(1)}),hc=i.object({id:i.string().min(1),order:i.number().int().min(1)}),fc=pn.omit({isUnlocked:!0}),zc=i.object({sections:i.array(hc).min(1).optional(),items:i.array(fc).min(1).optional()}),dn=zc.superRefine((t,e)=>{!t.sections?.length&&!t.items?.length&&e.addIssue({code:i.ZodIssueCode.custom,path:["sections"],message:"Provide sections or items to reorder"});let o=new Set;t.sections?.forEach((z,_)=>{o.has(z.id)&&e.addIssue({code:i.ZodIssueCode.custom,path:["sections",_,"id"],message:"Section IDs must be unique"}),o.add(z.id)});let r=new Set,n=new Map;t.items?.forEach((z,_)=>{if(z.order===void 0&&z.sectionId===void 0&&e.addIssue({code:i.ZodIssueCode.custom,path:["items",_],message:"Each item must provide order or sectionId"}),z.sectionId!==void 0){let G=z.sectionId??"__unsectioned__",ae=n.get(G)??{orders:[],total:0,withOrder:0};ae.total+=1,z.order!==void 0&&(ae.orders.push(z.order),ae.withOrder+=1),n.set(G,ae)}let U=`${z.type}:${z.id}`;r.has(U)&&e.addIssue({code:i.ZodIssueCode.custom,path:["items",_,"id"],message:"Item IDs must be unique per content type"}),r.add(U)});let a=[...t.sections?.map(z=>z.order)??[]];for(let[z,_]of n)if(_.withOrder===_.total){if(z==="__unsectioned__"){a.push(..._.orders);continue}new Set(_.orders).size!==_.orders.length&&e.addIssue({code:i.ZodIssueCode.custom,path:["items"],message:"Sibling orders must be unique"})}new Set(a).size!==a.length&&e.addIssue({code:i.ZodIssueCode.custom,path:["items"],message:"Top-level orders must be unique"})}),_c=i.object({id:i.string().min(1),type:i.enum(["LESSON","EXERCISE"])}),dh=i.object({sectionId:i.string().min(1).optional(),items:i.array(_c).min(1).optional()}).superRefine((t,e)=>{!!t.sectionId==!!t.items&&e.addIssue({code:i.ZodIssueCode.custom,path:["sectionId"],message:"Provide either sectionId or items"});let o=new Set;t.items?.forEach((r,n)=>{let a=`${r.type}:${r.id}`;o.has(a)&&e.addIssue({code:i.ZodIssueCode.custom,path:["items",n,"id"],message:"Item IDs must be unique per content type"}),o.add(a)})}),dt=i.object({retakeIntervalMonths:i.number().int().min(1).max(120),gracePeriodDays:i.number().int().min(0).max(365).optional().default(0),reminderDaysBefore:i.array(i.number().int().min(1).max(365)).max(10).optional().default([30,7,1]),isMandatory:i.boolean().optional().default(!0),framework:i.enum(["HIPAA","OSHA","SOX","GDPR","PCI_DSS","FERPA","ISO","CUSTOM"]).nullable().optional(),maxRetakeAttempts:i.number().int().min(1).nullable().optional(),passingScore:i.number().int().min(0).max(100).optional().default(80)}),co=i.object({title:i.string().min(1),description:i.string().min(1),type:Fe,organizationId:i.string().min(1),compliance:dt.optional()}),mh=co.refine(t=>t.type!=="COMPLIANCE"||t.compliance!==void 0,{message:"Compliance settings are required for COMPLIANCE courses",path:["compliance"]}),mn=i.object({show:i.boolean(),description:i.string().optional()}),Ec=["requirements","description","goals","videoUrl","paymentLink"];function gn(t){if(!t||typeof t!="object"||Array.isArray(t))return t;let e={...t};for(let o of Ec)e[o]===null&&(e[o]=void 0);if(e.instructor&&typeof e.instructor=="object"&&!Array.isArray(e.instructor)){let o={...e.instructor};o.coursesNo===void 0&&o.courseNo!==void 0&&(o.coursesNo=o.courseNo),delete o.courseNo,e.instructor=o}if(e.discount===null||e.discount==="")e.discount=void 0;else if(typeof e.discount=="string"){let o=sn(e.discount);o!==void 0&&(e.discount=o)}return e}var bn=i.object({name:i.string(),role:i.string(),coursesNo:i.coerce.number().optional(),description:i.string(),imgUrl:i.string()}),hn=i.object({templateUrl:i.string()}),Ic=i.object({id:i.number(),hide:i.boolean(),name:i.string(),avatar_url:i.string(),rating:i.number(),created_at:i.number(),description:i.string()}),Ac=i.array(i.object({id:i.union([i.literal(1),i.literal(2),i.literal(3),i.literal(4)]),name:i.string()})),fn=i.object({requirements:i.string().optional(),description:i.string().optional(),goals:i.string().optional(),videoUrl:i.string().optional(),showDiscount:i.boolean().optional(),discount:i.number().optional(),paymentEnabled:i.boolean().optional(),paymentLink:i.string().optional(),reward:mn.optional(),instructor:bn.optional(),certificate:hn.optional(),reviews:i.array(Ic).optional(),skills:i.array(i.string().min(1).max(80)).max(60).optional(),tools:i.array(i.string().min(1).max(80)).max(60).optional(),lessonTabsOrder:Ac.optional(),grading:i.boolean().optional(),lessonDownload:i.boolean().optional(),allowSelfEnrollment:i.boolean().optional(),allowNewStudent:i.boolean().optional(),allowMarkdownExport:i.boolean().optional(),welcomeEmailMessage:i.string().max(2e4).nullish(),sessionTimezone:i.string().max(64).nullish(),liveSessionReminderOffsetsMinutes:cn.optional(),sectionDisplay:i.record(i.string(),i.boolean()).optional(),isContentGroupingEnabled:i.boolean().optional(),progressionMode:i.enum(["free","sequential"]).optional(),commentsEnabled:i.boolean().optional()}),Lt=i.preprocess(gn,fn),Cc=fn.extend({reward:mn.partial().optional(),instructor:bn.partial().optional(),certificate:hn.partial().optional()}),yc=i.preprocess(gn,Cc),Tc=i.object({isDownloadable:i.boolean().optional(),theme:i.string().optional(),design:bc.optional(),deadline:i.union([i.string().min(1),i.null()]).optional(),threshold:i.number().int().min(0).max(100).optional(),requiredExerciseId:i.union([i.string().uuid(),i.null()]).optional(),exerciseMinScorePercent:i.number().int().min(0).max(100).nullable().optional(),emailMessage:i.string().max(5e3).nullable().optional()}),xc=i.url({protocol:/^https?$/});function Sc(t,e){let o=e?.paymentEnabled;return typeof o=="boolean"?o:typeof t=="number"&&t>0}function Pc(t,e){let o=[];if(!Sc(t,e))return o;let r=typeof e?.paymentLink=="string"?e.paymentLink.trim():"";return r?xc.safeParse(r).success||o.push({message:"Payment link must be a valid http(s) URL",path:["metadata","paymentLink"]}):o.push({message:"A payment link is required for paid courses",path:["metadata","paymentLink"]}),(t==null||t<=0)&&o.push({message:"Paid courses must have a cost greater than 0",path:["cost"]}),o}function zn(t,e){for(let o of Pc(t.cost,t.metadata))e.addIssue({code:"custom",message:o.message,path:o.path})}var lo=i.object({title:i.string().min(1).optional(),description:i.string().min(1).optional(),type:Fe.optional(),bannerImage:i.string().optional(),logo:i.string().optional(),slug:i.string().optional(),isPublished:i.boolean().optional(),orgOnly:i.boolean().optional(),overview:i.string().optional(),metadata:Lt.optional(),cost:i.number().int().min(0).optional(),currency:i.enum(["NGN","USD"]).optional(),certificate:Tc.optional(),tagIds:i.array(i.uuid()).max(100).optional(),compliance:dt.optional(),callout:an.optional()}),gh=lo.refine(t=>t.type!=="COMPLIANCE"||t.compliance!==void 0,{message:"Compliance settings are required when changing a course to COMPLIANCE",path:["compliance"]}).superRefine(zn),bh=i.object({courseId:i.string().min(1)}),po=i.object({title:i.string().min(1).optional(),description:i.string().min(1).optional(),overview:i.string().optional(),cost:i.number().int().min(0).optional(),currency:i.enum(["NGN","USD"]).optional(),imageUrl:i.url().optional(),generateImage:i.boolean().optional(),imageQuery:i.string().min(1).max(120).optional(),metadata:yc.optional()}),hh=po.superRefine(zn),fh=i.object({courseId:i.string().min(1)}),zh=i.object({courseId:i.string().min(1)}),_h=i.object({profileId:i.string().uuid()}),Eh=i.object({courseId:i.string().min(1),userId:i.string().uuid()}),Ih=i.object({includeProgressImpact:i.enum(["true","false"]).optional().default("false").transform(t=>t==="true")});import*as d from"zod";import*as f from"zod";import*as mo from"zod";var _n=/^[a-z0-9]+(?:-[a-z0-9]+)*$/,ze=mo.string().min(1,{message:"Slug is required"}).max(80,{message:"Slug must be 80 characters or fewer"}).regex(_n,{message:"Slug must be lowercase letters, numbers, and single dashes"}),go=mo.string().min(1,{message:"Slug is required"}).regex(_n,{message:"Slug must be lowercase letters, numbers, and single dashes"});import{z as W}from"zod";var wc=W.discriminatedUnion("action",[W.object({action:W.literal("continue")}),W.object({action:W.literal("go_to_section"),exerciseSectionId:W.string().uuid()}),W.object({action:W.literal("submit")})]),Nt=W.object({id:W.string().uuid().optional(),title:W.string().min(1,"Section title is required"),description:W.string().optional().nullable(),order:W.number().int().min(0),colorTheme:W.enum(["blue","green","amber","rose","violet","slate"]).default("blue"),afterBehavior:wc.default({action:"continue"}),questionIds:W.array(W.union([W.string(),W.number().int()])).optional()}),Th=Nt.omit({id:!0}),xh=Nt.partial().required({id:!0});var Oc=[f.literal(x.RADIO),f.literal(x.CHECKBOX),f.literal(x.TEXTAREA),f.literal(x.TRUE_FALSE),f.literal(x.SHORT_ANSWER),f.literal(x.NUMERIC),f.literal(x.FILL_BLANK),f.literal(x.FILE_UPLOAD),f.literal(x.MATCHING),f.literal(x.ORDERING),f.literal(x.HOTSPOT),f.literal(x.LINK),f.literal(x.WORD_BANK),f.literal(x.STAR),f.literal(x.VIDEO_RECORDING),f.literal(x.THUMBS)],Dt=f.union(Oc);function In(t){return e=>{let o=e.options||[];return o.length>0&&o.length<2?t:null}}function Rc(t){let e=t.options||[];return e.length>0&&!e.some(o=>o.isCorrect===!0)?"Please mark an option as the correct answer":null}var vc={[x.RADIO]:[t=>{let e=t.options||[];return e.length>0&&e.length<2?"Options must have at least 2 items for RADIO/CHECKBOX questions":null},t=>{if(typeof t.settings?.correctValue=="boolean")return null;let e=t.options||[];return e.length>0&&!e.some(o=>o.isCorrect===!0)?"Please mark an option as the correct answer":null}],[x.CHECKBOX]:[t=>{let e=t.options||[];return e.length>0&&e.length<2?"Options must have at least 2 items for RADIO/CHECKBOX questions":null},t=>{let e=t.options||[];return e.length>0&&!e.some(o=>o.isCorrect===!0)?"Please mark an option as the correct answer":null}],[x.TRUE_FALSE]:[In("True/False questions need both True and False options"),Rc],[x.THUMBS]:[In("Thumbs up/down questions need both Yes and No options")],[x.TEXTAREA]:[],[x.WORD_BANK]:[t=>{let e=t.settings,r=(String(e?.template??"").match(/_{3,}/g)??[]).length;if(r<1)return"Word bank questions need at least one ___ blank in the template";let n=e?.correctAnswers,a=Array.isArray(n)?n:[];if(a.length<r)return"Provide a correct answer for each blank";for(let z=0;z<r;z++)if(!String(a[z]??"").trim())return`Correct answer for blank ${z+1} is required`;return null}],[x.STAR]:[t=>{let e=t.settings??{},o=ct(e),r=e.correctValue,n=typeof r=="number"?r:r!=null?Number(r):NaN;return lt(n,o)?null:"Set a correct star value from 1 up to the maximum number of stars"}],[x.VIDEO_RECORDING]:[t=>{let o=(t.settings??{}).maxDurationSeconds,r=typeof o=="number"?o:o!=null?Number(o):NaN;return!Number.isFinite(r)||r<=0?"Set a recording duration greater than 0 seconds":r>120?`Recording duration cannot exceed ${120} seconds`:null}]},Lc=f.object({id:f.number().int().optional(),exerciseSectionId:f.string().uuid().nullable().optional(),question:f.string().min(1),questionTypeId:Dt.optional(),points:f.number().int().min(1),order:f.number().int().min(0).optional(),settings:f.record(f.string(),f.unknown()).optional(),deletedAt:f.string().optional(),options:f.array(f.object({id:f.number().int().optional(),label:f.string().min(1),isCorrect:f.boolean(),settings:f.record(f.string(),f.unknown()).optional(),deletedAt:f.string().optional()})).optional()});function Nc(t){return(vc[t.questionTypeId??-1]??[]).map(o=>o(t)).filter(o=>!!o)}function mt(t,e){if(!t.deletedAt)for(let o of Nc(t))e.addIssue({code:"custom",message:o,path:["options"]})}var Dc=Lc.superRefine(mt),Uc=f.object({question:f.string().min(1),questionTypeId:Dt.optional(),points:f.number().int().min(1),order:f.number().int().min(0).optional(),settings:f.record(f.string(),f.unknown()).optional(),options:f.array(f.object({label:f.string().min(1),isCorrect:f.boolean(),settings:f.record(f.string(),f.unknown()).optional()})).optional()}),Zc=Uc.superRefine(mt),vh=f.object({title:f.string().min(1),description:f.string().optional(),lessonId:f.string().optional(),sectionId:f.string().optional(),order:f.number().int().min(1),courseId:f.string().min(1),dueBy:f.string().optional(),slug:ze.optional(),questions:f.array(Zc).optional()}),Lh=f.object({title:f.string().min(1).optional(),description:f.string().optional(),lessonId:f.string().optional(),sectionId:f.string().optional(),order:f.number().int().min(1).optional(),isUnlocked:f.boolean().optional(),dueBy:f.string().optional(),allowMultipleAttempts:f.boolean().optional(),slug:ze.optional(),questions:f.array(Dc).optional(),sections:f.array(Nt).optional(),sectionDisplayMode:f.enum(["one_question","all_questions"]).optional(),completionPolicy:f.enum(["submitted","passed"]).optional(),passThreshold:f.number().int().min(0).max(100).optional()}),Nh=f.object({exerciseId:f.string().min(1)}),Dh=f.object({lessonId:f.string().optional(),sectionId:f.string().optional()}),Uh=f.object({exerciseId:f.string().min(1),answers:f.array(f.object({questionId:f.number().int(),optionId:f.number().int().optional(),answer:f.string().optional()})).min(1)}),Zh=f.object({lessonId:f.string().optional(),sectionId:f.string().optional(),order:f.number().int().min(1),templateId:f.number().int().min(1)});import*as b from"zod";var An=["google-slides","canva","powerpoint","keynote","figma","prezi","pitch","gamma","slideshare","beautiful"],Cn=[{id:"google-slides",name:"Google Slides",hosts:["docs.google.com"],docsHash:"google-slides",exampleEmbed:'<iframe src="https://docs.google.com/presentation/d/e/2PACX-1vTDemoPresentationId/embed?start=false&loop=false&delayms=3000" width="960" height="569" allowfullscreen="true"></iframe>'},{id:"canva",name:"Canva",hosts:["canva.com"],docsHash:"canva",exampleEmbed:'<iframe src="https://www.canva.com/design/DAFDemoDesignId/view?embed" width="960" height="540" allowfullscreen="allowfullscreen"></iframe>'},{id:"powerpoint",name:"PowerPoint",hosts:["onedrive.live.com","officeapps.live.com","1drv.ms","sharepoint.com"],docsHash:"powerpoint",exampleEmbed:'<iframe src="https://onedrive.live.com/embed?resid=DEMO123&authkey=Ademo&em=2" width="640" height="480" frameborder="0"></iframe>'},{id:"keynote",name:"Keynote",hosts:["icloud.com"],docsHash:"keynote",exampleEmbed:'<iframe src="https://www.icloud.com/keynote/0demoKeynoteShareId?embed=true" width="640" height="500" allowfullscreen></iframe>'},{id:"figma",name:"Figma Slides",hosts:["figma.com","embed.figma.com"],docsHash:"figma",exampleEmbed:'<iframe src="https://embed.figma.com/slides/DemoFileKey123?embed-host=classroomio" width="800" height="450" allowfullscreen></iframe>'},{id:"prezi",name:"Prezi",hosts:["prezi.com"],docsHash:"prezi",exampleEmbed:'<iframe src="https://prezi.com/view/demoPreziViewId/embed" width="550" height="400" allowfullscreen></iframe>'},{id:"pitch",name:"Pitch",hosts:["pitch.com"],docsHash:"pitch",exampleEmbed:'<iframe src="https://pitch.com/embed/demo-pitch-public-id" width="640" height="360" allowfullscreen></iframe>'},{id:"gamma",name:"Gamma",hosts:["gamma.app"],docsHash:"gamma",exampleEmbed:'<iframe src="https://gamma.app/embed/demo-gamma-deck-id" width="700" height="450" allowfullscreen></iframe>'},{id:"slideshare",name:"SlideShare",hosts:["slideshare.net"],docsHash:"slideshare",exampleEmbed:'<iframe src="https://www.slideshare.net/slideshow/embed_code/key/demoSlideShareKey" width="595" height="485" frameborder="0"></iframe>'},{id:"beautiful",name:"Beautiful.ai",hosts:["beautiful.ai"],docsHash:"beautiful",exampleEmbed:'<iframe src="https://www.beautiful.ai/player/-demoBeautifulAiId" width="960" height="540" allowfullscreen></iframe>'}],jh=Object.fromEntries(Cn.map(t=>[t.id,t]));function kc(t){return t.hostname.replace(/^www\./i,"").toLowerCase()}function jc(t,e){let o=e.replace(/^www\./i,"").toLowerCase();return t===o||t.endsWith(`.${o}`)}function bo(t){let e=t.replace(/^www\./i,"").toLowerCase();return Cn.find(o=>o.hosts.some(r=>jc(e,r)))??null}function yn(t){try{let e=new URL(t);return e.protocol!=="https:"?!1:bo(kc(e))!==null}catch{return!1}}var Mc=/^(https?|mailto|tel):/i,Gc=/^(javascript|data|vbscript)$/i;function Fc(t){return!!(Mc.test(t)||t.startsWith("#")||t.startsWith("/")||t.startsWith("./")||t.startsWith("../")||t.startsWith("?"))}function Tn(t){if(typeof t!="string")return!1;let e=t.trim();return e.length===0?!1:Fc(e)}function qc(t){let e=t.indexOf(":");if(e<0)return!1;let o=t.slice(0,e).replace(/[\s\x00-\x1f]+/g,"");return Gc.test(o)}function Ut(t){if(typeof t=="string"){let e=t.trim();return!!(e.length>0&&qc(e))}return Array.isArray(t)?t.some(Ut):t!==null&&typeof t=="object"?Object.values(t).some(Ut):!1}var Bc=/^\/hls\/[\w./-]+$/,Zt=b.object({type:b.enum(["youtube","vimeo","generic","upload","google_drive"]),link:b.string().refine(t=>Bc.test(t)||Tn(t),{message:"Video link scheme is not allowed"}),key:b.string().optional(),assetId:b.string().uuid().optional(),watchEnforced:b.boolean().optional(),fileName:b.string().optional(),metadata:b.object({svid:b.string().optional(),title:b.string().optional(),description:b.string().optional(),thumbnailUrl:b.string().optional(),duration:b.number().optional(),aspectRatio:b.string().optional(),createdAt:b.string().optional(),videoId:b.string().optional(),hash:b.string().optional()}).catchall(b.unknown()).optional()}),Qc=b.object({id:b.string().min(1),src:b.url().refine(t=>yn(t),{message:"Slide embed source is not from a supported platform"}),platform:b.enum(An)}).refine(t=>{try{let e=new URL(t.src).hostname.replace(/^www\./i,"").toLowerCase();return bo(e)?.id===t.platform}catch{return!1}},{message:"Slide platform does not match embed source",path:["platform"]}),Vc=b.url({protocol:/^https?$/,message:"Recording link must be an http(s) URL"}),Kc=b.number().int().min(15).max(480),Qh=b.object({title:b.string().min(1),note:b.string().optional(),courseId:b.string().min(1),sectionId:b.string().optional(),order:b.number().int().min(1),lessonAt:b.string().optional(),teacherId:b.string().optional(),isUnlocked:b.boolean().optional(),public:b.boolean().optional(),slug:ze.optional(),videos:b.array(Zt).optional()}),Vh=b.object({title:b.string().min(1).optional(),note:b.string().optional(),sectionId:b.string().optional(),order:b.number().int().min(1).optional(),callUrl:b.string().nullable().optional(),lessonAt:b.string().nullable().optional(),sessionDurationMinutes:Kc.nullable().optional(),recordingUrl:Vc.nullable().optional(),teacherId:b.string().optional(),isUnlocked:b.boolean().optional(),public:b.boolean().optional(),slug:ze.optional(),isComplete:b.boolean().optional(),completionPolicy:b.enum(["manual","video_watch","none"]).optional(),videoWatchThreshold:b.number().int().min(1).max(100).optional(),commentsEnabled:b.boolean().optional(),videoUrl:b.url().optional(),slideUrl:b.string().optional(),slides:b.array(Qc).optional(),videos:b.array(Zt).optional(),documents:b.array(b.object({type:b.string(),name:b.string(),link:b.string(),size:b.number().optional(),key:b.string(),assetId:b.string().uuid().optional()})).optional()}),Kh=b.object({lessonId:b.string().min(1)}),Wh=b.object({sectionId:b.string().optional(),courseId:b.string().min(1)}),Yh=b.object({courseId:b.string().min(1),lessonId:b.string().min(1)}),Xh=b.object({locale:b.string().min(1),limit:b.string().transform(Number).pipe(b.number().int().min(1).max(50)).default(10),cursor:b.string().min(1).optional()}),Jh=b.object({lessonId:b.string().min(1),comment:b.string().min(1)}),ef=b.object({commentId:b.string().min(1)}),tf=b.object({comment:b.string().min(1)}),of=b.object({cursor:b.string().optional(),limit:b.coerce.number().min(1).max(50).optional().default(10)}),rf=b.object({lessonId:b.string().min(1),isComplete:b.boolean()}),nf=b.object({isComplete:b.boolean()}),sf=b.object({positionSeconds:b.number().min(0),playedDeltaSeconds:b.number().transform(t=>Math.round(t)).pipe(b.number().min(0).max(120)),durationSeconds:b.number().transform(t=>Math.round(t)).pipe(b.int().min(1)),assetId:b.string().optional()});var xn=d.enum(["en","hi","fr","pt","de","vi","ru","es","pl","da","tr","it"]),Wc="Lesson body HTML only. Do not include the lesson title. Do not use h1 or h2 anywhere in lessonLanguages[].content. Start headings at h3 because that is the highest heading level allowed in lesson content.",Yc="Top-level position. Sections, lessons without a section, and exercises with neither a section nor a lesson share one contiguous 1-based sequence.",Sn=d.object({code:d.string().min(1),message:d.string().min(1),severity:d.enum(["info","warning","error"])}),Xc=d.object({type:d.enum(["prompt","pdf","course"]),label:d.string().min(1),pageStart:d.number().int().min(1).optional(),pageEnd:d.number().int().min(1).optional()}),Jc=d.object({title:d.string().min(1),description:d.string().min(1),type:Fe,locale:xn.default("en"),metadata:Lt.optional(),compliance:dt.optional()}).refine(t=>t.type!=="COMPLIANCE"||t.compliance!==void 0,{message:"Compliance settings are required for COMPLIANCE courses",path:["compliance"]}),el=d.object({externalId:d.string().min(1),title:d.string().min(1),order:d.number().int().min(1).describe(Yc)}),tl=d.object({externalId:d.string().min(1),sectionExternalId:d.string().min(1).optional().describe("Section the lesson belongs to. Omit it for a lesson that sits at the top level of the course."),title:d.string().min(1),order:d.number().int().min(1).describe("Position within the section, 1-based. A lesson without a section takes its place in the top-level sequence it shares with sections and top-level exercises."),isUnlocked:d.boolean().optional(),public:d.boolean().optional(),videos:d.array(Zt).optional()}),ol=d.object({lessonExternalId:d.string().min(1),locale:xn,content:d.string().min(1).describe(Wc)}),rl=d.object({label:d.string().min(1),isCorrect:d.boolean(),settings:d.record(d.string(),d.unknown()).optional()}),nl=d.object({question:d.string().min(1),questionTypeId:Dt.optional(),points:d.number().int().min(1),order:d.number().int().min(1).optional(),settings:d.record(d.string(),d.unknown()).optional(),options:d.array(rl).optional()}),sl=d.object({externalId:d.string().min(1),lessonExternalId:d.string().min(1).optional(),sectionExternalId:d.string().min(1).optional(),title:d.string().min(1),description:d.string().optional(),order:d.number().int().min(1).describe("Position within its section or lesson, 1-based. An exercise with neither takes its place in the top-level sequence it shares with sections and top-level lessons."),dueBy:d.string().optional(),questions:d.array(nl).optional()});function ho(t){return[...t].sort((e,o)=>e-o).every((e,o)=>e===o+1)}function il(t){let e=t.sections.map(n=>({kind:"section",externalId:n.externalId,order:n.order})),o=t.lessons.filter(n=>!n.sectionExternalId).map(n=>({kind:"lesson",externalId:n.externalId,order:n.order})),r=(t.exercises??[]).filter(n=>!n.sectionExternalId&&!n.lessonExternalId).map(n=>({kind:"exercise",externalId:n.externalId,order:n.order}));return{sections:e,lessons:o,exercises:r}}function al(t){let{sections:e,lessons:o,exercises:r}=il(t),n=[...e,...o,...r];if(ho(n.map(_=>_.order)))return n.sort((_,U)=>_.order-U.order);if(!(o.length===0&&ho(e.map(_=>_.order))&&ho(r.map(_=>_.order))))return null;let z=r.map(_=>({..._,order:e.length+_.order}));return[...e,...z].sort((_,U)=>_.order-U.order)}var Xe=d.object({course:Jc,tags:d.array(d.string().trim().min(1).max(80)).max(100).default([]),sections:d.array(el).default([]),lessons:d.array(tl).min(1),lessonLanguages:d.array(ol).min(1),exercises:d.array(sl).optional(),sourceReferences:d.array(Xc).optional(),warnings:d.array(Sn).default([])}).superRefine((t,e)=>{let o=new Set;t.tags.forEach((_,U)=>{let G=_.trim().toLowerCase();o.has(G)&&e.addIssue({code:d.ZodIssueCode.custom,path:["tags",U],message:"Draft tags must be unique"}),o.add(G)});let r=new Set;t.sections.forEach((_,U)=>{r.has(_.externalId)&&e.addIssue({code:d.ZodIssueCode.custom,path:["sections",U,"externalId"],message:"Section externalId must be unique"}),r.add(_.externalId)}),al(t)||e.addIssue({code:d.ZodIssueCode.custom,path:["sections"],message:"Top-level orders (sections, lessons without a section, and exercises with neither) must be a contiguous 1-based sequence (1, 2, 3, ...)"});let n=new Set;t.lessons.forEach((_,U)=>{n.has(_.externalId)&&e.addIssue({code:d.ZodIssueCode.custom,path:["lessons",U,"externalId"],message:"Lesson externalId must be unique"}),n.add(_.externalId),_.sectionExternalId&&!r.has(_.sectionExternalId)&&e.addIssue({code:d.ZodIssueCode.custom,path:["lessons",U,"sectionExternalId"],message:"Lesson sectionExternalId must reference an existing section"})});let a=new Map;for(let _ of t.lessons){if(!_.sectionExternalId)continue;let U=a.get(_.sectionExternalId)??[];U.push(_.order),a.set(_.sectionExternalId,U)}for(let[,_]of a)[..._].sort((G,ae)=>G-ae).some((G,ae)=>G!==ae+1)&&e.addIssue({code:d.ZodIssueCode.custom,path:["lessons"],message:"Lesson orders within each section must be a contiguous 1-based sequence (1, 2, 3, ...)"});t.lessonLanguages.forEach((_,U)=>{n.has(_.lessonExternalId)||e.addIssue({code:d.ZodIssueCode.custom,path:["lessonLanguages",U,"lessonExternalId"],message:"Lesson language must reference an existing lesson"})});let z=new Set;if(t.exercises?.forEach((_,U)=>{z.has(_.externalId)&&e.addIssue({code:d.ZodIssueCode.custom,path:["exercises",U,"externalId"],message:"Exercise externalId must be unique"}),z.add(_.externalId),_.lessonExternalId&&!n.has(_.lessonExternalId)&&e.addIssue({code:d.ZodIssueCode.custom,path:["exercises",U,"lessonExternalId"],message:"Exercise lessonExternalId must reference an existing lesson"}),_.sectionExternalId&&!r.has(_.sectionExternalId)&&e.addIssue({code:d.ZodIssueCode.custom,path:["exercises",U,"sectionExternalId"],message:"Exercise sectionExternalId must reference an existing section"})}),t.exercises){let _=new Map(t.lessons.map(G=>[G.externalId,G])),U=new Map;for(let G of t.exercises){if(!G.sectionExternalId&&!G.lessonExternalId)continue;let ae=G.sectionExternalId??"";G.lessonExternalId&&(ae=_.get(G.lessonExternalId)?.sectionExternalId??G.lessonExternalId);let Ke=U.get(ae)??[];Ke.push(G.order),U.set(ae,Ke)}for(let[,G]of U)[...G].sort((Ke,yt)=>Ke-yt).some((Ke,yt)=>Ke!==yt+1)&&e.addIssue({code:d.ZodIssueCode.custom,path:["exercises"],message:"Exercise orders within each group must be a contiguous 1-based sequence (1, 2, 3, ...)"})}}),fo=d.object({sourceType:d.enum(["prompt","pdf","course"]),idempotencyKey:d.string().min(1).optional(),summary:d.record(d.string(),d.unknown()).optional(),sourceArtifacts:d.array(d.record(d.string(),d.unknown())).optional(),draft:Xe}),kt=d.object({courseId:d.string().min(1)}),zo=d.object({courseId:d.string().min(1),idempotencyKey:d.string().min(1).optional(),summary:d.record(d.string(),d.unknown()).optional(),sourceArtifacts:d.array(d.record(d.string(),d.unknown())).optional()}),Je=d.object({draftId:d.string().uuid()}),Pn=d.object({summary:d.record(d.string(),d.unknown()).optional(),sourceArtifacts:d.array(d.record(d.string(),d.unknown())).optional(),warnings:d.array(Sn).optional(),draft:Xe.optional()}),jt=d.object({title:d.string().min(1).optional(),description:d.string().min(1).optional(),type:Fe.optional(),metadata:Lt.optional(),compliance:dt.optional(),bannerImageUrl:d.string().url().optional(),bannerImageQuery:d.string().min(1).max(120).optional(),generateBannerImage:d.boolean().optional()}),df=jt.refine(t=>t.type!=="COMPLIANCE"||t.compliance!==void 0,{message:"Compliance settings are required for COMPLIANCE courses",path:["compliance"]}),_o=jt.extend({courseId:d.string().min(1),syncMode:d.enum(["merge","replace"]).default("merge")}),mf=_o.refine(t=>t.type!=="COMPLIANCE"||t.compliance!==void 0,{message:"Compliance settings are required for COMPLIANCE courses",path:["compliance"]});import*as E from"zod";var ul=E.enum(["createdAt","name","email","lastLoginAt","lastActiveAt"]),cl=E.enum(["asc","desc"]),ll=E.enum(["ACTIVE","DEACTIVATED","ARCHIVED"]),pl=E.enum(["active","pending","expired","revoked"]),dl=E.enum(["enrolled","not_enrolled"]),ml=E.enum(["not_started","in_progress","completed"]),gl=E.enum(["earned","not_earned"]),wn=E.enum(["7d","30d","90d","180d","never"]),On=E.object({page:E.coerce.number().int().min(1).default(1),limit:E.coerce.number().int().min(1).max(100).default(20),search:E.string().trim().optional(),sortBy:ul.default("createdAt"),sortOrder:cl.default("desc"),status:ll.default("ACTIVE"),inviteStatus:pl.optional(),enrollment:dl.optional(),completion:ml.optional(),certificate:gl.optional(),lastLoginBefore:wn.optional(),lastActiveBefore:wn.optional(),excludeRecentJoiners:E.union([E.boolean(),E.enum(["true","false"])]).default(!0).transform(t=>t===!0||t==="true")});var Eo=500;var bl=E.enum(["deactivate","reactivate","archive","unarchive","delete"]),hl=E.discriminatedUnion("mode",[E.object({mode:E.literal("ids"),memberIds:E.array(E.number().int().positive()).min(1).max(Eo)}),E.object({mode:E.literal("filter"),filter:On.omit({page:!0,limit:!0,sortBy:!0,sortOrder:!0}),expectedCount:E.number().int().positive(),expectedTargetHash:E.string().length(64)})]),bf=E.object({target:hl,action:bl,reason:E.string().trim().max(500).optional()}),hf=On.omit({page:!0,limit:!0}).extend({memberIds:E.union([E.string(),E.array(E.string())]).optional().transform(t=>{if(t==null)return;let o=(Array.isArray(t)?t:[t]).flatMap(r=>r.split(",")).map(r=>Number(r.trim())).filter(r=>Number.isInteger(r)&&r>0);return o.length>0?[...new Set(o)]:void 0}).refine(t=>t==null||t.length<=Eo,{message:`Select at most ${Eo} learners, or export the filtered view instead`})}),ff=E.object({undoToken:E.string().min(1).max(200)}),zf=E.object({profileIds:E.array(E.uuid()).min(1).max(500),courseIds:E.array(E.uuid()).optional(),cohortIds:E.array(E.uuid()).optional(),sendEmail:E.boolean().default(!0)}).refine(t=>(t.courseIds?.length??0)>0||(t.cohortIds?.length??0)>0,{message:"At least one course or cohort must be selected",path:["courseIds"]}),_f=E.object({email:E.email()});import*as H from"zod";var fl=1e3,If=5*1024*1024;var Af=H.enum(["ready","already_member","invalid_email","duplicate_in_file","is_staff","over_seat_limit","missing_email"]),zl=H.object({email:H.string().trim().toLowerCase(),name:H.string().trim().max(200).optional(),courses:H.array(H.string().trim()).optional()}),Cf=H.object({recipients:H.array(zl).max(fl).optional(),recipientCsv:H.string().max(25e3).optional(),courseIds:H.array(H.string().uuid()).optional(),cohortIds:H.array(H.string().uuid()).optional(),allCourses:H.boolean().optional().default(!1),allCohorts:H.boolean().optional().default(!1),sendEmail:H.boolean().default(!0)}).refine(t=>!!t.recipients?.length||!!t.recipientCsv?.trim(),{message:"Provide at least one recipient",path:["recipients"]});import*as l from"zod";var Rn=["en","hi","fr","pt","de","vi","ru","es","pl","da","tr","it"],vn=["studentCourseInvite","studentCourseWelcome","studentCourseCompletion","studentOrgInvite","studentCohortWelcome","studentProvePayment","cohortGoalReminder","quizAssigned","sessionReminder","sessionUpdated","submissionGraded","newsfeedPost"];var jf=l.object({siteName:l.string().min(1).optional(),customDomain:l.string().min(1).optional(),isCustomDomainVerified:l.union([l.boolean(),l.literal("true").transform(()=>!0),l.literal("false").transform(()=>!1)]).optional()}),Mf=l.object({siteName:l.string().min(1),tags:l.string().optional(),types:l.string().optional(),search:l.string().trim().optional(),pricing:l.enum(["free","paid"]).optional(),page:l.coerce.number().int().min(1).optional(),limit:l.coerce.number().int().min(1).max(100).optional()}),Mt=l.object({page:l.coerce.number().int().min(1).default(1),limit:l.coerce.number().int().min(1).max(100).default(20),search:l.string().trim().optional(),tags:l.string().optional(),type:Fe.optional(),status:l.enum(["published","unpublished"]).optional(),sort:l.enum(["date_created","last_updated_at","published","lessons"]).optional(),order:l.enum(["asc","desc"]).optional()}),Gf=l.object({courses:l.array(l.object({id:l.string().uuid(),order:l.number().int().min(0)})).min(1).max(4).superRefine((t,e)=>{new Set(t.map(a=>a.id)).size!==t.length&&e.addIssue({code:"custom",path:["courses"],message:"Course ids must be unique"});let r=Array.from({length:t.length},(a,z)=>z);t.map(a=>a.order).sort((a,z)=>a-z).some((a,z)=>a!==r[z])&&e.addIssue({code:"custom",path:["courses"],message:"Orders must form a contiguous sequence starting at 0"})})}),Ff=l.object({siteName:l.string().min(1)}),qf=l.object({orgId:l.uuid(),planName:l.enum(["EARLY_ADOPTER","ENTERPRISE","BASIC"]),subscriptionId:l.string().min(1),triggeredBy:l.number().int().positive(),payload:l.record(l.string(),l.unknown())}),$f=l.object({subscriptionId:l.string().min(1),payload:l.record(l.string(),l.unknown())}),Hf=l.object({subscriptionId:l.string().min(1),payload:l.record(l.string(),l.unknown())}),Bf=l.object({event:l.enum(["activated","updated","revoked"]),subscriptionId:l.string().min(1),customerId:l.string().min(1).nullish(),customerEmail:l.email(),checkoutId:l.string().min(1).nullish(),payload:l.record(l.string(),l.unknown())}),Qf=l.object({token:l.string().trim().min(20).max(200)}),Vf=l.object({name:l.string().min(5).refine(t=>!/^[-]|[-]$/.test(t),{message:"Organization name cannot start or end with a hyphen"}),siteName:l.string().min(5).refine(t=>!/^[-]|[-]$/.test(t),{message:"Site name cannot start or end with a hyphen"}).refine(t=>!kr.includes(t),{message:"Sitename already exists."})}),Kf=l.object({name:l.string().min(5).refine(t=>!/^[-]|[-]$/.test(t),{message:"Organization name cannot start or end with a hyphen"}).optional(),avatarUrl:l.url().optional(),favicon:l.union([l.url(),l.null()]).optional(),theme:l.string().optional(),landingpage:l.record(l.string(),l.unknown()).optional().refine(t=>!t||!Ut(t),{message:"URLs with javascript:, data:, or vbscript: schemes are not allowed"}),siteName:l.string().min(1).optional(),customDomain:l.string().nullable().optional(),isCustomDomainVerified:l.boolean().optional(),customization:l.record(l.string(),l.unknown()).optional(),disableSignup:l.boolean().optional(),disableSignupMessage:l.string().optional(),disableEmailPassword:l.boolean().optional(),disableGoogleAuth:l.boolean().optional(),settings:l.object({signup:l.object({inviteOnly:l.boolean().optional()}).optional(),internalEnrollmentOnly:l.boolean().optional(),language:l.object({locale:l.enum(["en","hi","fr","pt","de","vi","ru","es","pl","da","tr","it"]).optional(),enforced:l.boolean().optional()}).optional(),emailNotifications:l.object({newStudent:l.boolean().optional(),newSubmission:l.boolean().optional(),gradingResult:l.boolean().optional(),newsfeed:l.boolean().optional(),quizAssigned:l.boolean().optional(),cohortReminder:l.boolean().optional(),session:l.boolean().optional(),enrollmentWelcome:l.boolean().optional(),courseCompletion:l.boolean().optional()}).optional()}).optional()}),Wf=l.object({emailId:l.enum(vn),locale:l.enum(Rn)}),Yf=l.object({content:l.string().min(1).max(5e4),subject:l.string().min(1).max(200).nullable().optional()}),_l=l.array(l.string().trim().email()).min(1).max(5),Xf=l.object({content:l.string().min(1).max(5e4),subject:l.string().min(1).max(200),recipientEmails:_l}),Jf=l.object({emails:l.array(l.string().email()).min(1).max(50),roleId:l.number().int().positive(),confirmedStudentEmails:l.array(l.string().email()).max(50).default([])}),ez=l.object({memberId:l.coerce.number().int().positive()}),tz=l.object({roleId:l.union([l.literal(ee.ADMIN),l.literal(ee.TUTOR)])}),oz=l.object({roleId:l.number().int().positive()}),rz=l.object({roleId:l.coerce.number().int().positive()}),nz=l.object({roleId:l.number().int().positive(),isRevoked:l.boolean()}),sz=l.object({userId:l.uuid()});import*as et from"zod";var El=et.enum(["connect","refresh","remove"]),az=et.object({action:El,domain:et.string().min(1)});import*as F from"zod";var cz=F.object({orgId:F.uuid(),quizId:F.uuid()}),lz=F.object({orgId:F.uuid()}),pz=F.object({title:F.string().min(1),questions:F.array(F.any()).optional(),timelimit:F.string().optional(),theme:F.string().optional()}),dz=F.object({title:F.string().min(1).optional(),questions:F.array(F.any()).optional(),timelimit:F.string().optional(),theme:F.string().optional()});import*as Gt from"zod";var gz=Gt.object({orgId:Gt.uuid()});import*as Ft from"zod";var hz=Ft.object({token:Ft.string().min(10).max(512)});import*as O from"zod";var Il=["OKTA","GOOGLE_WORKSPACE","AUTH0"],zz=O.object({provider:O.enum(Il),displayName:O.string().min(1,"Display name is required"),issuer:O.url("Must be a valid URL"),domain:O.string().min(1,"Domain is required").regex(/^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$/,"Invalid domain format"),clientId:O.string().min(1,"Client ID is required"),clientSecret:O.string().min(1,"Client secret is required"),scopes:O.string().default("openid profile email")}),_z=O.object({displayName:O.string().min(1).optional(),issuer:O.url().optional(),domain:O.string().optional(),clientId:O.string().optional(),clientSecret:O.string().optional(),scopes:O.string().optional(),isActive:O.boolean().optional()}),Ez=O.object({forceSso:O.boolean().optional(),autoJoinSsoDomains:O.boolean().optional(),breakGlassEnabled:O.boolean().optional(),defaultRoleId:O.number().int().positive().optional(),roleMapping:O.record(O.string(),O.number()).optional()}),Iz=O.object({email:O.email("Valid email is required")}),Az=O.object({orgId:O.uuid()});import{z as Be}from"zod";var Tz=Be.object({token:Be.string().min(1),redirect:Be.string().optional()}),xz=Be.object({sub:Be.uuid(),email:Be.email(),type:Be.literal("login-link")});import{z as Pe}from"zod";var wz=Pe.object({token:Pe.string().min(1),redirect:Pe.string().optional()}),Oz=Pe.object({sub:Pe.string(),email:Pe.email(),name:Pe.string().optional(),avatar:Pe.url().optional(),iat:Pe.number(),exp:Pe.number()});import{z as we}from"zod";var Io=we.enum(["mcp","api","zapier"]),Al=we.enum(["course_import:draft:create","course_import:draft:read","course_import:draft:update","course_import:draft:publish","course:read","course:write","course:tag:write","course:exercise:read","course:exercise:write","cohort:read","cohort:write","course:member:read","course:member:write","course:certificate:read","course:certificate:write","analytics:read","media:write","course:submission:read","course:submission:write","course:student:write","audience:read","audience:write","team:read","team:write","public_api:*"]),Lz=we.object({type:Io.optional()}),Nz=we.object({type:Io.default("mcp")}),Dz=we.object({type:Io,label:we.string().min(1).max(120).optional(),expiresAt:we.string().datetime().optional(),scopes:we.array(Al).min(1).optional()}).refine(t=>t.type!=="mcp"||!t.scopes?.includes("public_api:*"),{message:"MCP keys cannot have the public_api:* scope",path:["scopes"]}),Uz=we.object({keyId:we.string().uuid()});import*as tt from"zod";var kz=tt.object({q:tt.string().trim().min(1).max(200),limit:tt.coerce.number().int().min(1).max(20).default(5)});var P=Y.object({courseId:Y.string().uuid()}),o_=co.omit({organizationId:!0}).refine(t=>t.type!=="COMPLIANCE"||t.compliance!==void 0,{message:"Compliance settings are required for COMPLIANCE courses",path:["compliance"]}),r_=lo.omit({tagIds:!0}).refine(t=>t.type!=="COMPLIANCE"||t.compliance!==void 0,{message:"Compliance settings are required when changing a course to COMPLIANCE",path:["compliance"]}),n_=Y.object({mode:Y.enum(["merge","replace"]).default("merge"),idempotencyKey:Y.string().min(1).optional(),summary:Y.record(Y.string(),Y.unknown()).optional(),sourceArtifacts:Y.array(Y.record(Y.string(),Y.unknown())).optional(),draft:Xe});import*as N from"zod";import*as gt from"zod";var L=gt.object({page:gt.coerce.number().int().min(1).default(1),limit:gt.coerce.number().int().min(1).max(100).default(20)});var Cl=N.enum(["classique","brutalist","noir","poster","minimal"]),Ln=N.object({name:N.string().max(80).default(""),role:N.string().max(80).default(""),enabled:N.boolean().default(!0),signatureUrl:N.url().optional()}),Ao=N.object({templateId:Cl,accentColor:N.string().regex(/^#[0-9a-fA-F]{6}$/,{message:"Accent must be a 6-digit hex color"}),subtitle:N.string().max(120).optional(),descriptionOverride:N.string().max(500).optional(),signatories:N.tuple([Ln,Ln]),idFormat:N.string().max(40).optional()}),yl=N.iso.datetime({offset:!0,message:"deadline must be an ISO 8601 datetime with a timezone, e.g. 2026-12-31T23:59:59Z"}),Nn=N.object({isDownloadable:N.boolean().optional(),theme:N.string().min(1).max(40).optional(),design:Ao.optional(),deadline:yl.nullable().optional(),threshold:N.number().int().min(0).max(100).optional(),requiredExerciseId:N.string().uuid().nullable().optional(),exerciseMinScorePercent:N.number().int().min(0).max(100).nullable().optional(),emailMessage:N.string().max(5e3).nullable().optional()}),Tl=Object.keys(Nn.shape),Dn=Nn.refine(t=>Tl.some(e=>t[e]!==void 0),{message:"Provide at least one certificate field to update"}),Un=L.extend({search:N.string().trim().max(200).optional()}),Zn=P.extend({memberId:N.string().uuid()}),xl=N.enum(["pdf","png"]),kn=N.object({format:xl.default("pdf")});import*as K from"zod";var p_=K.object({isDownloadable:K.boolean().default(!1),theme:K.string(),design:Ao,deadline:K.iso.datetime({offset:!0}).nullable(),threshold:K.number().default(100),requiredExerciseId:K.string().uuid().nullable(),exerciseMinScorePercent:K.number().nullable(),emailMessage:K.string().nullable()}),d_=K.object({memberId:K.string(),profileId:K.string().nullable(),fullname:K.string().nullable(),email:K.string().nullable(),certificateEarnedAt:K.string(),certificationEmailSentAt:K.string().nullable()});import*as q from"zod";var qt=500,Co=q.object({id:q.string().uuid(),type:q.enum(["LESSON","EXERCISE"])});function jn(t,e){let o=new Set;t.items?.forEach((r,n)=>{let a=`${r.type}:${r.id}`;o.has(a)&&e.addIssue({code:"custom",path:["items",n,"id"],message:"Item IDs must be unique per content type"}),o.add(a)})}var Mn=q.object({sections:q.array(q.object({id:q.string().uuid(),order:q.number().int().min(1)})).min(1).max(qt).optional(),items:q.array(Co.extend({order:q.number().int().min(1).optional(),sectionId:q.string().uuid().nullable().optional()})).min(1).max(qt).optional()}).superRefine((t,e)=>{dn.safeParse(t).error?.issues.forEach(o=>e.addIssue({code:"custom",path:o.path,message:o.message}))}),Gn=q.object({items:q.array(Co.extend({isUnlocked:q.boolean()})).min(1).max(qt)}).superRefine(jn),Fn=q.object({items:q.array(Co).min(1).max(qt)}).superRefine(jn);import*as h from"zod";var yo=h.string(),Qe=h.string().nullable(),qn=h.enum(["LESSON","EXERCISE"]),Sl=h.object({id:h.string().uuid(),courseId:h.string().uuid().nullable(),title:h.string().nullable(),order:h.number(),createdAt:yo,updatedAt:Qe}),h_=Sl.extend({movedLessons:h.number().describe("Lessons moved in by moveUngrouped; 0 otherwise"),movedExercises:h.number().describe("Exercises moved in by moveUngrouped; 0 otherwise")}),Pl=h.object({id:h.string().uuid(),courseId:h.string().uuid(),sectionId:h.string().uuid().nullable(),title:h.string(),slug:h.string().nullable(),order:h.number(),isUnlocked:h.boolean().nullable(),public:h.boolean().nullable(),lessonAt:Qe,callUrl:h.string().nullable(),teacherId:h.string().uuid().nullable(),completionPolicy:h.string(),videoWatchThreshold:h.number().nullable(),commentsEnabled:h.boolean(),createdAt:Qe,updatedAt:Qe}),wl=h.object({id:h.number(),lessonId:h.string().uuid().nullable(),locale:h.string().nullable(),content:h.string().nullable(),updatedAt:yo}),f_=Pl.extend({note:h.string().nullable(),videoUrl:h.string().nullable(),slideUrl:h.string().nullable(),slides:h.array(h.object({id:h.string(),src:h.string(),platform:h.string()})),videos:h.array(h.object({type:h.string(),link:h.string().describe("For uploaded videos, a short-lived signed URL"),assetId:h.string().optional(),fileName:h.string().optional(),watchEnforced:h.boolean().optional(),metadata:h.record(h.string(),h.unknown()).optional()})),documents:h.array(h.object({type:h.string(),name:h.string(),link:h.string().describe("A short-lived signed URL"),size:h.number().optional(),assetId:h.string().optional()})),translations:h.array(wl)}),z_=h.object({jobId:h.string()}),__=h.object({items:h.array(h.object({id:h.number().describe("0 for the current content of a lesson with no saved history yet"),locale:h.string().nullable(),kind:h.enum(["auto","manual","publish"]).nullable(),label:h.string().nullable(),oldContent:h.string().nullable(),newContent:h.string().nullable(),timestamp:Qe,sessionStartedAt:Qe,editCount:h.number().nullable(),isSealed:h.boolean().nullable(),authorId:h.string().uuid().nullable(),authorName:h.string().nullable()})),nextCursor:h.string().nullable().describe("Pass back as cursor for older versions; null on the last page")}),Ol=h.object({id:h.number(),lessonId:h.string().uuid().nullable(),groupmemberId:h.string().uuid().nullable().describe("Course member who wrote it"),comment:h.string().nullable(),createdAt:yo,updatedAt:Qe}),E_=h.object({items:h.array(Ol.extend({author:h.object({fullname:h.string().nullable(),avatarUrl:h.string().nullable()}).nullable()})),total:h.number(),nextCursor:h.string().nullable().describe("Pass back as cursor for older comments; null on the last page")}),I_=h.object({updatedSections:h.number(),updatedLessons:h.number(),updatedExercises:h.number()}),A_=h.object({items:h.array(h.object({id:h.string().uuid(),type:qn,isUnlocked:h.boolean()})).describe("Each item as stored after the change")}),C_=h.object({items:h.array(h.object({id:h.string().uuid(),type:qn})).describe("The lessons and exercises deleted"),deletedSubmissions:h.number().describe("Exercise submissions deleted with them"),deletedLessonCompletions:h.number().describe("Lesson completions deleted with them")});import*as p from"zod";var ht=p.union([p.literal(x.RADIO),p.literal(x.CHECKBOX),p.literal(x.TEXTAREA),p.literal(x.TRUE_FALSE),p.literal(x.SHORT_ANSWER),p.literal(x.NUMERIC),p.literal(x.FILL_BLANK),p.literal(x.FILE_UPLOAD),p.literal(x.ORDERING),p.literal(x.LINK),p.literal(x.WORD_BANK),p.literal(x.STAR),p.literal(x.VIDEO_RECORDING),p.literal(x.THUMBS)]),$n=200,bt=50,Rl=50,Ve=p.record(p.string(),p.unknown()),Oe=P.extend({exerciseId:p.string().uuid()}),Hn=Oe.extend({jobId:p.string().min(1).max(64)}),Bn=p.object({pollCount:p.coerce.number().int().min(0).max(1e3).default(0)}),Qn=L.extend({sectionId:p.string().uuid().optional(),lessonId:p.string().uuid().optional()}),Vn=p.object({question:p.string().min(1),questionTypeId:ht.default(x.RADIO),points:p.number().int().min(1),order:p.number().int().min(0).optional(),settings:Ve.optional(),options:p.array(p.object({label:p.string().min(1),isCorrect:p.boolean(),settings:Ve.optional()})).max(bt).optional()}).superRefine(mt),Kn=p.object({title:p.string().min(1).optional(),description:p.string().optional(),sectionId:p.string().uuid().optional(),lessonId:p.string().uuid().optional(),order:p.number().int().min(1),dueBy:p.string().datetime({offset:!0}).optional(),slug:ze.optional(),questions:p.array(Vn).max($n).optional(),templateId:p.number().int().min(1).optional()}).superRefine((t,e)=>{if(t.templateId===void 0){t.title||e.addIssue({code:"custom",message:"title is required unless templateId is set",path:["title"]});return}for(let o of["title","description","dueBy","slug","questions"])t[o]!==void 0&&e.addIssue({code:"custom",message:`${o} cannot be combined with templateId: the template supplies the title, description and questions`,path:[o]})}),vl=p.object({id:p.number().int().positive().optional(),delete:p.boolean().optional(),exerciseSectionId:p.string().uuid().nullable().optional(),question:p.string().min(1).optional().describe("Required for a new question"),questionTypeId:ht.optional().describe("Defaults to RADIO (1) for a new question"),points:p.number().int().min(1).optional().describe("Required for a new question"),order:p.number().int().min(0).optional(),settings:Ve.optional(),options:p.array(p.object({id:p.number().int().positive().optional(),delete:p.boolean().optional(),label:p.string().min(1).optional().describe("Required for a new option"),isCorrect:p.boolean().optional().describe("Required for a new option"),settings:Ve.optional()})).max(bt).optional()}).superRefine((t,e)=>{t.delete&&!t.id&&e.addIssue({code:"custom",message:"delete needs the id of an existing question",path:["delete"]}),(t.options??[]).forEach((o,r)=>{o.id||(o.delete&&e.addIssue({code:"custom",message:"delete needs the id of an existing option",path:["options",r,"delete"]}),o.label===void 0&&e.addIssue({code:"custom",message:"label is required for a new option",path:["options",r,"label"]}),o.isCorrect===void 0&&e.addIssue({code:"custom",message:"isCorrect is required for a new option",path:["options",r,"isCorrect"]}))}),!t.id&&(t.question===void 0&&e.addIssue({code:"custom",message:"question is required for a new question",path:["question"]}),t.points===void 0&&e.addIssue({code:"custom",message:"points is required for a new question",path:["points"]}),mt({...t,questionTypeId:t.questionTypeId??x.RADIO,deletedAt:t.delete?"deleted":void 0,options:t.options?.filter(o=>!o.delete).map(o=>({isCorrect:o.isCorrect===!0}))},e))}),ft=p.discriminatedUnion("action",[p.object({action:p.literal("continue")}),p.object({action:p.literal("go_to_section"),exerciseSectionId:p.string().uuid()}),p.object({action:p.literal("submit")})]),Ll=p.object({id:p.string().uuid().optional(),title:p.string().min(1),description:p.string().nullable().optional(),order:p.number().int().min(0),colorTheme:p.enum(["blue","green","amber","rose","violet","slate"]).default("blue"),afterBehavior:ft.default({action:"continue"})}),Nl=p.object({id:p.string().uuid().optional(),delete:p.boolean().optional(),title:p.string().min(1).optional().describe("Required for a new section"),description:p.string().nullable().optional(),order:p.number().int().min(0).optional().describe("Required for a new section"),colorTheme:p.enum(["blue","green","amber","rose","violet","slate"]).optional().describe("Defaults to blue for a new section"),afterBehavior:ft.optional().describe("Defaults to continue for a new section")}).superRefine((t,e)=>{t.delete&&!t.id&&e.addIssue({code:"custom",message:"delete needs the id of an existing section",path:["delete"]}),!t.id&&(t.title===void 0&&e.addIssue({code:"custom",message:"title is required for a new section",path:["title"]}),t.order===void 0&&e.addIssue({code:"custom",message:"order is required for a new section",path:["order"]}))}),Wn=p.object({title:p.string().min(1).optional(),description:p.string().optional(),sectionId:p.string().uuid().optional(),lessonId:p.string().uuid().optional(),order:p.number().int().min(1).optional(),isUnlocked:p.boolean().optional(),dueBy:p.string().datetime({offset:!0}).optional(),allowMultipleAttempts:p.boolean().optional(),slug:ze.optional(),questions:p.array(vl).max($n).optional(),sections:p.array(Nl).max(Rl).optional(),sectionDisplayMode:p.enum(["one_question","all_questions"]).optional(),completionPolicy:p.enum(["submitted","passed"]).optional(),passThreshold:p.number().int().min(0).max(100).optional()}).refine(t=>Object.values(t).some(e=>e!==void 0),{message:"Send at least one field to update"}),To=Oe.extend({questionId:p.coerce.number().int().positive()}),Yn=Vn.safeExtend({exerciseSectionId:p.string().uuid().optional().describe("The exercise section to put the question in; required when the exercise has sections")}),Dl=p.object({label:p.string().min(1),isCorrect:p.boolean(),settings:Ve.optional()}),Ul=p.object({id:p.number().int().positive(),label:p.string().min(1).optional(),isCorrect:p.boolean().optional(),settings:Ve.optional()}),xo=p.object({exerciseSectionId:p.string().uuid().nullable().optional(),question:p.string().min(1).optional(),questionTypeId:ht.optional(),points:p.number().int().min(1).optional(),order:p.number().int().min(0).optional(),settings:Ve.optional(),addOptions:p.array(Dl).max(bt).optional(),updateOptions:p.array(Ul).max(bt).optional().describe("Edits existing options by id; fields you leave out keep their values"),removeOptionIds:p.array(p.number().int().positive()).max(bt).optional().describe("Options to remove; options you do not name are kept")}).refine(t=>Object.values(t).some(e=>e!==void 0),{message:"Send at least one field to update"}).refine(t=>{let e=[...(t.updateOptions??[]).map(o=>o.id),...t.removeOptionIds??[]];return new Set(e).size===e.length},{message:"An option id can appear only once across updateOptions and removeOptionIds",path:["removeOptionIds"]}),ke=p.object({force:p.union([p.boolean(),p.enum(["true","false"])]).default(!1).transform(t=>t===!0||t==="true").describe("Also delete the answers students gave to the deleted questions; without it, deleting an answered question returns 409")}),So=Oe.extend({sectionId:p.string().uuid()}),Xn=Ll.omit({id:!0}),Po=p.object({title:p.string().min(1).optional(),description:p.string().nullable().optional(),order:p.number().int().min(0).optional(),colorTheme:p.enum(["blue","green","amber","rose","violet","slate"]).optional(),afterBehavior:ft.optional()}).refine(t=>Object.values(t).some(e=>e!==void 0),{message:"Send at least one field to update"});import*as g from"zod";import*as X from"zod";var zt=X.enum(["queued","processing","awaiting_manual","completed","failed"]),_t=P.extend({submissionId:X.string().uuid()}),Jn=L.extend({exerciseId:X.string().uuid().optional(),memberId:X.string().uuid().optional(),gradingState:zt.optional()}),es=X.object({answers:X.array(X.object({questionId:X.number().int().positive(),points:X.number().int().min(0)})).max(500),total:X.number().int().min(0),feedback:X.string().optional()}).refine(t=>new Set(t.answers.map(e=>e.questionId)).size===t.answers.length,{message:"Each questionId can appear only once",path:["answers"]}),ts=X.object({gradingState:zt.optional(),feedback:X.string().optional()}).refine(t=>t.gradingState!==void 0||t.feedback!==void 0,{message:"Send gradingState or feedback"}),os=L;var rs=g.string(),ot=g.string().nullable(),$t=g.record(g.string(),g.unknown()),Zl=g.object({id:g.string().uuid(),courseId:g.string().uuid().nullable(),sectionId:g.string().uuid().nullable(),lessonId:g.string().uuid().nullable().describe("Deprecated: exercises now belong to a course section"),title:g.string(),description:g.string().nullable(),order:g.number(),slug:g.string().nullable(),isUnlocked:g.boolean().nullable(),dueBy:ot,allowMultipleAttempts:g.boolean(),sectionDisplayMode:g.string().nullable().describe("one_question | all_questions"),completionPolicy:g.string().describe("submitted | passed"),passThreshold:g.number().nullable(),createdAt:ot,updatedAt:ot}),kl=g.object({id:g.number(),name:g.string().describe("Stable key used in submission answers"),question:g.string(),questionTypeId:ht,points:g.number(),order:g.number(),settings:$t,exerciseSectionId:g.string().uuid().nullable(),options:g.array(g.object({id:g.number(),label:g.string().nullable(),value:g.string().nullable(),isCorrect:g.boolean(),settings:$t}))}),jl=g.object({id:g.string().uuid(),title:g.string(),description:g.string().nullable(),order:g.number(),colorTheme:g.string(),afterBehavior:ft,questionIds:g.array(g.number())}),U_=Zl.extend({questions:g.array(kl),sections:g.array(jl)}),Z_=g.object({jobId:g.string()}),k_=g.object({jobId:g.string(),status:g.enum(["queued","running","completed","failed","canceled"]),createdAt:rs,updatedAt:rs,error:g.object({code:g.string(),message:g.string()}).nullable(),nextPollMs:g.number().describe("Suggested wait in ms before polling again; 0 once the job has finished")}),Ml=g.object({id:g.number(),title:g.string().nullable(),description:g.string().nullable(),tag:g.string().nullable(),questionCount:g.number(),points:g.number()}),j_=Ml.extend({questions:g.array(g.object({question:g.string(),questionTypeId:g.number(),points:g.number(),order:g.number(),settings:$t,options:g.array(g.object({label:g.string(),isCorrect:g.boolean(),settings:$t}))}))}),Gl=g.object({profileId:g.string().uuid(),fullname:g.string().nullable(),email:g.string().nullable(),avatarUrl:g.string().nullable()}).nullable(),ns=g.enum(["auto_graded","manual_required","hybrid"]),M_=g.object({id:g.string().uuid(),exerciseId:g.string().uuid(),exerciseTitle:g.string(),memberId:g.string().uuid().nullable(),student:Gl,gradingState:zt,overallStatus:ns,total:g.number().nullable(),feedback:g.string().nullable(),isEarly:g.boolean().nullable().describe("Submitted on or before the exercise due date. true when there is no due date; null when there is one but the submission time is missing"),submittedAt:ot}),Fl=g.object({id:g.string().uuid(),courseId:g.string().uuid().nullable(),exerciseId:g.string().uuid(),memberId:g.string().uuid().nullable(),gradingState:zt,overallStatus:ns.nullable(),total:g.number().nullable(),feedback:g.string().nullable(),submittedAt:ot,updatedAt:ot}),G_=Fl.extend({answers:g.array(g.object({questionId:g.number(),points:g.number().nullable(),answerData:g.record(g.string(),g.unknown()).nullable().describe("Typed answer. File and video answers include a short-lived fileUrl / playbackUrl")}))}),F_=g.object({memberId:g.string().uuid(),profileId:g.string().uuid().nullable(),fullname:g.string().nullable(),email:g.string().nullable(),marks:g.array(g.object({exerciseId:g.string().uuid(),exerciseTitle:g.string(),maxPoints:g.number(),points:g.number().nullable().describe("null when the student has no graded submission for this exercise")}))});import*as k from"zod";var wo=P,ss=P.extend({inviteId:k.string().uuid()}),is=k.object({preset:k.enum(["ONE_TIME_24H","MULTI_USE_7D","MULTI_USE_30D","CUSTOM"]).default("MULTI_USE_30D"),expiresAt:k.string().min(1).optional(),maxUses:k.number().int().min(1).max(1e3).optional(),allowedEmails:k.array(k.string().email()).max(100).optional(),allowedDomains:k.array(k.string().min(1)).max(100).optional(),recipientEmails:k.array(k.string().email()).max(500).optional(),recipientCsv:k.string().max(25e3).optional(),sendEmail:k.boolean().default(!1),metadata:k.record(k.string(),k.unknown()).optional()}).refine(t=>{let e=(t.recipientEmails?.length??0)>0,o=typeof t.recipientCsv=="string"&&t.recipientCsv.trim().length>0;return e||o},{message:"recipientEmails or recipientCsv is required",path:["recipientEmails"]}),as=L;import*as Oo from"zod";var _e=P.extend({lessonId:Oo.string().uuid()}),us=L.extend({sectionId:Oo.string().uuid().optional()});import*as Ee from"zod";var Ro=_e.extend({commentId:Ee.coerce.number().int().positive()}),ql=/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(\.\d{1,6})?(Z|[+-]\d{2}(:?\d{2})?)\|\d+$/;function $l(t){if(Number.isNaN(Date.parse(t)))return!1;let[e,o,r]=t.slice(0,10).split("-").map(Number),n=new Date(Date.UTC(e,o-1,r));return n.getUTCMonth()===o-1&&n.getUTCDate()===r}var cs=Ee.object({cursor:Ee.string().max(100).regex(ql,"cursor must be a nextCursor value").refine(t=>$l(t.slice(0,t.lastIndexOf("|"))),{message:"cursor must be a nextCursor value"}).optional(),limit:Ee.coerce.number().int().min(1).max(50).default(10)}),ls=Ee.object({comment:Ee.string().min(1)}),ps=Ee.object({comment:Ee.string().min(1)});import*as de from"zod";var vo=de.enum(["en","hi","fr","pt","de","vi","ru","es","pl","da"]),ds=_e.extend({locale:vo}),ms=de.object({locale:vo.optional()}),gs=de.object({content:de.string(),versionIntent:de.enum(["auto","manual"]).optional(),versionLabel:de.string().max(120).optional()}),bs=de.object({locale:vo,limit:de.coerce.number().int().min(1).max(50).default(10),cursor:de.string().min(1).max(100).optional()});import*as M from"zod";var hs=M.union([M.literal(ee.ADMIN),M.literal(ee.TUTOR),M.literal(ee.STUDENT)]).describe("1 = admin, 2 = tutor, 3 = student"),Ie=P.extend({memberId:M.string().uuid()}),fs=L.extend({search:M.string().trim().max(200).optional(),roleId:M.coerce.number().int().min(1).optional()}),zs=M.object({profileId:M.uuid().optional(),roleId:hs,email:M.email().optional(),name:M.string().optional().describe("Used in the welcome email sent to added tutors/admins")}).refine(t=>!!t.profileId||!!t.email,{message:"Either profileId or email must be provided",path:["profileId"]}),Lo=M.object({roleId:hs.optional(),email:M.email().optional()}).refine(t=>Object.keys(t).length>0,{message:"No fields to update"}),_s=M.object({includeProgressImpact:M.enum(["true","false"]).optional().default("false").transform(t=>t==="true")});import*as m from"zod";import*as oe from"zod";var nE=oe.object({organizationId:oe.string().min(1)}),sE=oe.object({templateId:oe.string().min(1)}),iE=oe.object({organizationId:oe.string().min(1)}),aE=oe.object({title:oe.string().min(1)}),uE=oe.object({courseId:oe.string().min(1)}),cE=oe.object({title:oe.string().min(1),organizationId:oe.string().min(1)});import{z as Et}from"zod";var Hl=["description","bannerImage","welcomeEmail","completionDeadline","completionThreshold","finalExercise","lessonTabsOrder","contentGrouping","progression","comments","selfEnrollment","markdownExport","lessonDownload","landingRequirements","landingDescription","landingGoals","landingSkills","landingTools","landingInstructor","pricing","certificateDesign","certificateRules","aiTutor"],bE=Et.object({unitIds:Et.array(Et.string().uuid()).default([]),settingKeys:Et.array(Et.enum(Hl)).default([])});import{z as be}from"zod";var Bl=be.object({courseId:be.string().uuid()}),zE=Bl.extend({profileId:be.string().uuid()}),No=be.array(be.string().uuid()).min(1).optional(),_E=be.object({profileIds:No,dueDate:be.string().datetime()}),EE=be.object({profileIds:No,dueDate:be.string().datetime()}),IE=be.object({profileIds:No,waiverReason:be.string().trim().min(1).max(1e3).optional(),waiverExpiresAt:be.string().datetime().optional()});import*as Do from"zod";var yE=Do.object({courseSlug:go}),TE=Do.object({courseSlug:go,itemSlug:ze});import*as $ from"zod";var SE=$.object({title:$.string().min(1),courseId:$.string().min(1),order:$.number().int().min(1)}),PE=$.object({title:$.string().min(1).optional(),order:$.number().int().min(1).optional()}),wE=$.object({title:$.string().min(1)}),OE=$.object({sectionId:$.string().min(1)}),RE=$.object({sections:$.array($.object({id:$.string().min(1),order:$.number().int().min(1)})).min(1)}).superRefine((t,e)=>{t.sections.map(r=>r.order).sort((r,n)=>r-n).some((r,n)=>r!==n+1)&&e.addIssue({code:$.ZodIssueCode.custom,path:["sections"],message:"Section orders must be a contiguous 1-based sequence (1, 2, 3, ...)"})});import*as v from"zod";var LE=v.object({courseId:v.string().min(1)}),NE=v.object({courseId:v.string().min(1),inviteId:v.string().uuid()}),DE=v.object({courseId:v.string().min(1),inviteId:v.string().uuid()}),UE=v.object({token:v.string().min(1)}),ZE=v.object({courseId:v.uuid()}),Ql=v.enum(["ONE_TIME_24H","MULTI_USE_7D","MULTI_USE_30D","CUSTOM"]),kE=v.object({preset:Ql.default("MULTI_USE_30D"),expiresAt:v.string().min(1).optional(),maxUses:v.number().int().min(1).max(1e3).optional(),allowedEmails:v.array(v.string().email()).max(100).optional(),allowedDomains:v.array(v.string().min(1)).max(100).optional(),recipientEmails:v.array(v.string().email()).max(500).optional(),recipientCsv:v.string().max(25e3).optional(),sendEmail:v.boolean().default(!1),metadata:v.record(v.string(),v.unknown()).optional()}).refine(t=>{let e=(t.recipientEmails?.length??0)>0,o=typeof t.recipientCsv=="string"&&t.recipientCsv.trim().length>0;return e||o},{message:"recipientEmails or recipientCsv is required",path:["recipientEmails"]});import*as y from"zod";var Es=y.union([y.literal(ee.ADMIN),y.literal(ee.TUTOR),y.literal(ee.STUDENT)]),GE=y.object({courseId:y.string().min(1)}),Vl=y.enum(["name","role","progress","lastLogin","enrolledAt","certificate"]),Kl=y.enum(["asc","desc"]),Uo=y.enum(["not_started","in_progress","completed"]),Wl=y.enum(["joined","invited"]),Yl=y.enum(["7d","30d","90d","180d","never"]),Xl=y.enum(["7d","30d","90d","180d"]),FE=y.object({page:y.coerce.number().int().min(1).default(1),limit:y.coerce.number().int().min(1).max(100).default(20),search:y.string().trim().max(200).optional(),roleId:y.coerce.number().int().min(1).optional(),sortBy:Vl.default("role"),sortOrder:Kl.default("asc"),progress:Uo.optional(),membership:Wl.optional(),enrolledWithin:Xl.optional(),lastLoginBefore:Yl.optional(),certificateEarned:y.union([y.boolean(),y.enum(["true","false"])]).optional().transform(t=>t==null?void 0:t===!0||t==="true")});var qE=y.object({courseId:y.string().min(1),memberId:y.string().min(1)}),$E=y.array(y.object({profileId:y.uuid().optional(),roleId:Es,email:y.email().optional(),name:y.string().optional()})),HE=y.object({roleId:Es.optional(),email:y.email().optional()}).refine(t=>Object.keys(t).length>0,{message:"No fields to update"}),BE=y.object({courseId:y.string().uuid(),memberId:y.string().uuid()});var Ht=m.string(),Re=m.string().nullable(),Is=m.number().describe("1 = admin, 2 = tutor, 3 = student"),Jl=m.object({id:m.string().uuid(),groupId:m.string().uuid(),roleId:Is,profileId:m.string().uuid().nullable(),email:m.string().nullable(),createdAt:Re,assignedStudentId:m.string().nullable(),certificateEarnedAt:Re,certificationEmailSentAt:Re}),ep=Jl.extend({profile:m.object({id:m.string().uuid(),fullname:m.string().nullable(),username:m.string().nullable(),avatarUrl:m.string().nullable(),email:m.string().nullable()}).nullable()}),aI=ep.extend({progressPercent:m.number().optional().describe("Students only"),progressBucket:Uo.nullable(),stage:m.discriminatedUnion("kind",[m.object({kind:m.literal("not_started")}),m.object({kind:m.literal("certificate_earned")}),m.object({kind:m.literal("content"),position:m.number(),title:m.string(),contentType:m.enum(["lesson","exercise"])})]).optional().describe("Students only"),lastLoginAt:Re.optional().describe("Students only"),enrolledAt:Re.optional().describe("Students only")}),uI=m.object({lessonCompletion:m.number(),lessonVideoProgress:m.number(),submissions:m.number(),groupAttendance:m.number(),lessonComments:m.number(),courseNewsfeed:m.number(),courseNewsfeedComments:m.number(),courseCertificateIssues:m.number(),courseCompletionRecords:m.number(),aiChatConversations:m.number()}),cI=m.object({user:m.object({id:m.string().uuid(),fullName:m.string(),email:m.string(),avatarUrl:m.string(),lastSeen:Ht.optional()}),averageGrade:m.number().nullable().describe("Percentage across graded exercises; null when nothing is graded yet"),userExercisesStats:m.array(m.object({id:m.string().uuid(),lessonId:m.string().uuid().nullable(),lessonTitle:m.string(),title:m.string(),status:m.number().optional().describe("Submission status id; absent when not submitted"),score:m.number(),totalPoints:m.number(),isCompleted:m.boolean()})),totalExercises:m.number(),completedExercises:m.number(),lessonsCompleted:m.number(),lessonsCount:m.number(),progressPercentage:m.number(),progressImpact:m.object({completedLessons:m.number(),totalLessons:m.number(),exerciseSubmissions:m.number(),lessonComments:m.number(),courseNewsfeedActivity:m.number(),videoProgressLessons:m.number(),attendanceEntries:m.number(),hasCertificationRecords:m.boolean()}).nullable().describe("Only set when includeProgressImpact=true")}),As=m.enum(["ACTIVE","REVOKED","EXPIRED","USED_UP"]),Cs=m.object({id:m.string().uuid(),courseId:m.string().uuid(),roleId:Is,expiresAt:Ht,maxUses:m.number(),usedCount:m.number(),allowedEmails:m.array(m.string()).nullable(),allowedDomains:m.array(m.string()).nullable(),createdAt:Ht,status:As}),lI=Cs.extend({isRevoked:m.boolean(),updatedAt:Ht,lastUsedAt:Re,revokedAt:Re,revokedByProfileId:m.string().uuid().nullable(),createdBy:m.object({id:m.string().uuid(),fullname:m.string().nullable(),email:m.string().nullable()}).nullable(),usesRemaining:m.number(),activity:m.object({previewedCount:m.number(),acceptedCount:m.number(),emailSentCount:m.number(),emailFailedCount:m.number(),lastPreviewedAt:Re,lastAcceptedAt:Re,lastEmailSentAt:Re})}),pI=m.object({mode:m.enum(["single","bulk"]).describe("single = one shareable link, bulk = one invite per recipient email"),invites:m.array(Cs.extend({inviteLink:m.string().describe("Enroll URL containing the invite token; only returned at creation")})),inviteLink:m.string().nullable(),duplicatesSkipped:m.array(m.string()),delivery:m.object({requested:m.number(),sent:m.number(),failed:m.number(),failures:m.array(m.object({email:m.string(),error:m.string()}))})}),dI=m.object({id:m.string().uuid(),status:As,isRevoked:m.boolean()});import*as s from"zod";var he=s.string().nullable(),tp=s.number().describe("1 = admin, 2 = tutor, 3 = student"),op=s.object({requirements:s.string().optional(),description:s.string().optional(),goals:s.string().optional(),videoUrl:s.string().optional(),showDiscount:s.boolean().optional(),discount:s.number().optional(),paymentEnabled:s.boolean().optional(),paymentLink:s.string().optional(),reward:s.object({show:s.boolean(),description:s.string().optional()}).optional(),instructor:s.object({name:s.string(),role:s.string(),coursesNo:s.number().optional(),description:s.string(),imgUrl:s.string()}).optional(),certificate:s.object({templateUrl:s.string()}).optional(),reviews:s.array(s.object({id:s.number(),hide:s.boolean(),name:s.string(),avatar_url:s.string(),rating:s.number(),created_at:s.number(),description:s.string()})).optional(),skills:s.array(s.string()).optional(),tools:s.array(s.string()).optional(),lessonTabsOrder:s.array(s.object({id:s.number(),name:s.string()})).optional(),grading:s.boolean().optional(),lessonDownload:s.boolean().optional(),allowSelfEnrollment:s.boolean().optional(),allowNewStudent:s.boolean().optional().describe("Deprecated; use allowSelfEnrollment"),allowMarkdownExport:s.boolean().optional(),welcomeEmailMessage:s.string().nullable().optional(),sessionTimezone:s.string().nullable().optional(),liveSessionReminderOffsetsMinutes:s.array(s.number()).optional(),sectionDisplay:s.record(s.string(),s.boolean()).optional(),isContentGroupingEnabled:s.boolean().optional(),progressionMode:s.enum(["free","sequential"]).optional(),commentsEnabled:s.boolean().optional(),aiTutor:s.record(s.string(),s.unknown()).optional().describe("Per-course AI tutor overrides")}).catchall(s.unknown()),rp=s.object({name:s.string(),role:s.string(),enabled:s.boolean().optional(),signatureUrl:s.string().optional()}),np=s.object({isDownloadable:s.boolean().optional(),theme:s.string().optional().describe("Deprecated; use design.templateId"),design:s.object({templateId:s.enum(["classique","brutalist","noir","poster","minimal"]),accentColor:s.string(),subtitle:s.string().optional(),descriptionOverride:s.string().optional(),signatories:s.array(rp).optional(),idFormat:s.string().optional()}).optional(),deadline:s.string().nullable().optional(),threshold:s.number().optional(),requiredExerciseId:s.string().uuid().nullable().optional(),exerciseMinScorePercent:s.number().nullable().optional(),emailMessage:s.string().nullable().optional()}).catchall(s.unknown()),sp=s.object({retakeIntervalMonths:s.number(),gracePeriodDays:s.number().optional(),reminderDaysBefore:s.array(s.number()).optional(),isMandatory:s.boolean().optional(),framework:s.enum(["HIPAA","OSHA","SOX","GDPR","PCI_DSS","FERPA","ISO","CUSTOM"]).nullable().optional(),maxRetakeAttempts:s.number().nullable().optional(),passingScore:s.number().optional()}).catchall(s.unknown()),ip=s.object({title:s.string(),description:s.string(),buttonLabel:s.string(),buttonUrl:s.string(),animation:s.enum(["waves","dotted","none"]).optional()}),Zo=s.object({id:s.string().uuid(),title:s.string(),description:s.string(),overview:s.string().nullable(),createdAt:he,updatedAt:he,groupId:s.string().uuid().nullable(),isTemplate:s.boolean(),templateId:s.string().uuid().nullable(),publicForAll:s.boolean(),seedKey:s.string().nullable(),logo:s.string().describe("Deprecated; use bannerImage"),slug:s.string().nullable(),metadata:op,cost:s.number().nullable(),currency:s.string(),bannerImage:s.string().nullable(),isPublished:s.boolean().nullable(),displayOrder:s.number().nullable(),certificate:np.nullable(),compliance:sp.nullable(),status:s.string().describe("ACTIVE, or DELETED once the course is deleted"),type:s.enum(Rt).nullable(),callout:ip.nullable()}),hI=Zo.extend({lessonCount:s.number(),totalStudents:s.number(),exerciseCount:s.number(),tags:s.array(s.object({id:s.string().uuid(),name:s.string(),slug:s.string(),color:s.string()}))}),ys=s.object({id:s.string().uuid(),type:s.enum(["LESSON","EXERCISE"]),title:s.string(),order:s.number().nullable(),createdAt:he,sectionId:s.string().uuid().nullable(),isUnlocked:s.boolean().nullable(),isComplete:s.boolean().nullable(),lessonAt:he,callUrl:s.string().nullable(),sessionDurationMinutes:s.number().nullable(),recordingUrl:s.string().nullable(),hasNoteContent:s.boolean().nullable(),hasSlideContent:s.boolean().nullable(),videosCount:s.number().nullable(),documentsCount:s.number().nullable(),questionCount:s.number().nullable(),dueBy:he}),fI=Zo.extend({group:s.object({id:s.string().uuid(),name:s.string(),description:s.string().nullable(),createdAt:he,updatedAt:he,organizationId:s.string().uuid().nullable(),members:s.array(s.object({id:s.string().uuid(),groupId:s.string().uuid(),roleId:tp,profileId:s.string().uuid().nullable(),email:s.string().nullable(),createdAt:he,assignedStudentId:s.string().nullable(),certificateEarnedAt:he,certificationEmailSentAt:he,profile:s.object({id:s.string().uuid(),fullname:s.string().nullable(),username:s.string().nullable(),avatarUrl:s.string().nullable(),email:s.string().nullable()}).nullable()}))}).nullable(),attendance:s.array(s.object({id:s.number(),createdAt:he,updatedAt:he,courseId:s.string().uuid().nullable(),studentId:s.string().uuid().nullable().describe("Course member id"),isPresent:s.boolean().nullable(),lessonId:s.string().uuid()})),content:s.object({grouped:s.boolean().describe("true when content is organized into sections"),sections:s.array(s.object({id:s.string().describe('Section id, or "ungrouped" for items that belong to no section'),title:s.string(),order:s.number().nullable(),items:s.array(ys)})),items:s.array(ys).describe("Filled only when grouped is false"),outline:s.array(s.discriminatedUnion("kind",[s.object({kind:s.literal("section"),id:s.string()}),s.object({kind:s.literal("item"),id:s.string().uuid(),type:s.enum(["LESSON","EXERCISE"])})])).describe("Top-level reading order: sections and lessons or exercises outside any section, in course order. Items inside a section follow sections[].items.")}),studentLimitReached:s.boolean(),org:s.object({id:s.string().uuid(),name:s.string(),siteName:s.string(),theme:s.string().optional()}).optional()}),zI=s.object({course:Zo,groupId:s.string().uuid(),memberId:s.string().uuid().describe("The key creator's course member id; they are added as a tutor")}),ap=s.object({questionId:s.union([s.number(),s.string()]),questionTitle:s.string(),exerciseId:s.string(),exerciseTitle:s.string(),typeId:s.number()}),_I=s.object({conversionBlocked:s.array(ap).optional().describe("Set when a type change was skipped because these questions cannot be auto-graded")}),EI=s.object({courseId:s.string().uuid(),draft:Xe}),II=s.object({draftId:s.string().uuid(),syncMode:s.enum(["merge","replace"]),courseId:s.string().uuid(),courseUrl:s.string(),bannerImageUrl:s.string().nullable(),tagNames:s.array(s.string()),localeCount:s.number(),createdSections:s.number(),createdLessons:s.number(),createdExercises:s.number(),createdLessonLanguages:s.number(),updatedSections:s.number(),updatedLessons:s.number(),updatedExercises:s.number(),updatedLessonLanguages:s.number(),deletedSections:s.number(),deletedLessons:s.number(),deletedExercises:s.number(),deletedLessonLanguages:s.number(),untouchedSections:s.number(),untouchedLessons:s.number(),untouchedExercises:s.number()});import*as fe from"zod";var ko=P.extend({sectionId:fe.string().uuid()}),Ts=L,xs="Top-level position, starting at 1. Sections share these positions with lessons and exercises outside any section; the entry at that position and every later one move down one.",Ss=fe.object({title:fe.string().min(1),order:fe.number().int().min(1).optional().describe(xs),moveUngrouped:fe.boolean().optional()}).refine(t=>t.moveUngrouped?t.order===void 0:t.order!==void 0,{message:"Send order, or moveUngrouped: true without order",path:["order"]}),Ps=fe.object({title:fe.string().min(1).optional(),order:fe.number().int().min(1).optional().describe(xs)}).refine(t=>t.title!==void 0||t.order!==void 0,{message:"Provide title or order"});import*as re from"zod";var SI=Ie.extend({lessonId:re.string().uuid()}),PI=re.object({isComplete:re.boolean().describe("true records the lesson as completed; false undoes it.")}),wI=Ie.extend({exerciseId:re.string().uuid()}),up=['True/false: "true" or "false".',"Short answer and paragraph: the plain text.","Every other type: a JSON string.",'Multiple choice: {"type":"CHECKBOX","optionIds":[30,31]}.','Numeric: {"type":"NUMERIC","value":2}.','Fill in the blank: {"type":"FILL_BLANK","answers":["Paris"]}.','Word bank: {"type":"WORD_BANK","filledBlanks":["Paris"]}.','Matching: {"type":"MATCHING","value":[{"left":"A","right":"1"}]}.','Ordering: {"type":"ORDERING","value":["first","second"]}.','Link: {"type":"LINK","links":["https://example.com"]}.','Hotspot: {"type":"HOTSPOT","value":[{"x":10,"y":20}]}.','Star rating: {"type":"STAR","value":4}.'].join(" "),OI=re.object({answers:re.array(re.object({questionId:re.number().int().describe("Must be a question of this exercise."),optionId:re.number().int().optional().describe("Single choice questions only: the chosen option, which must belong to the question."),answer:re.string().optional().describe(`Every question type except single choice. ${up}`)})).min(1).max(500).refine(t=>new Set(t.map(e=>e.questionId)).size===t.length,{message:"Each questionId can appear only once"}).describe("One entry per answered question. File-upload and video-recording questions cannot be answered through the API.")});import*as qe from"zod";var vI=qe.object({lessonId:qe.string().uuid(),memberId:qe.string().uuid(),isComplete:qe.boolean(),updatedAt:qe.string().nullable()});import*as rt from"zod";var ws=L.extend({tag:rt.string().min(1).max(50).optional()}),Os=rt.object({templateId:rt.coerce.number().int().min(1)});import*as T from"zod";var cp=T.object({type:T.literal("upload"),assetId:T.string().uuid().describe("From POST /assets, after its bytes have been uploaded.")}),lp=T.object({type:T.enum(["youtube","vimeo","generic"]),link:T.url().describe("Public URL. Embedded as-is; never fetched server-side.")}),Rs=T.discriminatedUnion("type",[cp,lp]),ZI=P.extend({lessonId:T.string().uuid()}),vs="Position within the section, starting at 1. Without a section, the position among the course top-level sections, lessons and exercises; the entry at that position and every later one move down one.",kI=T.object({title:T.string().min(1).max(255),sectionId:T.string().uuid().optional(),order:T.number().int().min(1).describe(vs),isUnlocked:T.boolean().optional(),public:T.boolean().optional(),videos:T.array(Rs).max(20).optional()}),jI=T.object({title:T.string().min(1).max(255).optional(),order:T.number().int().min(1).optional().describe(vs),isUnlocked:T.boolean().optional(),public:T.boolean().optional(),videos:T.array(Rs).max(20).optional().describe("Replaces the lesson videos entirely. Omit to leave them unchanged.")}),MI=T.object({id:T.string(),title:T.string(),slug:T.string().nullable(),order:T.number().nullable(),sectionId:T.string().nullable(),isUnlocked:T.boolean().nullable(),public:T.boolean().nullable(),videos:T.array(T.object({type:T.string(),assetId:T.string().optional(),link:T.string().optional()})).nullable()});import*as ve from"zod";var pp=ve.union([ve.literal(ee.ADMIN),ve.literal(ee.TUTOR)]).describe("1 = admin, 2 = tutor"),Ls=L,jo=ve.object({memberId:ve.coerce.number().int().positive()}),Mo=ve.object({roleId:pp.optional(),name:no.optional().describe("Joined members only: sets the full name on their profile")}).refine(t=>t.roleId!==void 0||t.name!==void 0,{message:"No fields to update"});import*as Ae from"zod";var BI=Ae.object({id:Ae.number(),profileId:Ae.string().uuid().nullable().describe("null until the invited person creates an account"),name:Ae.string().describe("Empty until the invited person creates an account"),email:Ae.string(),roleId:Ae.number().describe("1 = admin, 2 = tutor"),verified:Ae.boolean().describe("false while the invite is still pending")});var Le="The API key creator must be a member of the cohort or an org admin, otherwise 403.",j="The API key creator must be a cohort tutor/admin or an org admin, otherwise 403.",me="Paginated: page (default 1) and limit (default 20, max 100); the result includes pagination.",B={readOnlyHint:!0,destructiveHint:!1},J={readOnlyHint:!1,destructiveHint:!1},je={readOnlyHint:!1,destructiveHint:!0};function A(t){return{content:[{type:"text",text:JSON.stringify(t)}]}}var Ns="The API key creator must be an org admin or tutor, otherwise 403.",dp="The API key creator must be an org admin, otherwise 403.",Ds="The API key creator must be a course tutor/admin or an org admin, otherwise 403.",Us="Each section may be up to 10 minutes old (login activity: 24 hours); meta.generatedAt says when.",Zs=dr,ks=L,js=br,Ms=P.extend(mr.shape),Gs=P.extend(gr.shape),mp=Zs.shape,gp=ks.shape,bp=js.shape,hp=Ms.shape,fp=Gs.shape;function Fs(t,e){t.tool("get_org_analytics",`Organization analytics. Pick sections with include (default ["overview"]): overview (totals, top courses by students, recent certificates), traffic (views, visitors, enrollments, completions, per-day series), countries, funnel (landing \u2192 course page \u2192 enrollment \u2192 completion), courseTypes, topCourses (most viewed), loginActivity (logins by day of week, org admin only), compliance (status counts, org admin only). days is 7, 30, 90 or 365 (default 30); limit (1-20, default 5) caps list sections. Sections the key creator can't see are left out and listed in meta.omitted. ${Us} ${Ns}`,mp,B,async o=>{let r=Zs.parse(o),n=await e.getOrgAnalytics(r);return A(n)}),t.tool("list_compliance_learners",`One row per student per compliance course with their latest status, due date and validity, ordered by course title then learner name. For the counts, use get_org_analytics with include ["compliance"]. ${me} ${dp}`,gp,B,async o=>{let r=ks.parse(o),n=await e.listComplianceLearners(r);return A(n)}),t.tool("get_learner_analytics",`A learner's progress and grades across every course they're enrolled in within this organization, with per-exercise results. profileId is the id list_compliance_learners and list_course_analytics_students return; 404 if it isn't in the organization. ${Ns}`,bp,B,async o=>{let{profileId:r}=js.parse(o),n=await e.getLearnerAnalytics(r);return A(n)}),t.tool("get_course_analytics",`Course analytics. Pick sections with include (default ["summary"]): summary (tutors, students, lessons, exercises, and per-student averages for progress, exercise completion and grade) and funnel (course page \u2192 enrollment \u2192 completion over days: 7, 30, 90 or 365, default 30). Use list_course_analytics_students for the per-student rows. ${Us} ${Ds}`,hp,B,async o=>{let{courseId:r,...n}=Ms.parse(o),a=await e.getCourseAnalytics(r,n);return A(a)}),t.tool("list_course_analytics_students",`Per-student progress, exercise submissions, average grade and last seen for a course, ordered by name. Paginated: page (default 1) and limit (default 20, max 50); the result includes pagination. ${Ds}`,fp,B,async o=>{let{courseId:r,...n}=Gs.parse(o),a=await e.listCourseAnalyticsStudents(r,n);return A(a)})}var nt="The API key creator must be an org admin, otherwise 403.",qs={readOnlyHint:!0,destructiveHint:!1},Go={readOnlyHint:!1,destructiveHint:!1},zp={readOnlyHint:!1,destructiveHint:!0},$s=Tr,Hs=xr,Bs=Sr,Qs=wt,Vs=so.safeExtend({memberId:wt.shape.memberId}),Ks=wt,_p=$s.shape,Ep=Hs.shape,Ip=Bs.shape,Ap=Qs.shape,Cp=Vs.shape,yp=Ks.shape;function Ws(t,e){t.tool("list_audience_members",`List the organization's audience (students), with invite state, enrollment counts and progress. Paginated, with search, sort and filters. Admins and tutors are listed by list_team_members. ${nt}`,_p,qs,async o=>{let r=$s.parse(o),n=await e.listAudienceMembers(r);return st(n)}),t.tool("add_audience_member",`Invite someone to the organization as a student by email, optionally enrolling them in courses. Sends an invite email unless sendEmail is false. ${nt}`,Ep,Go,async o=>{let r=Hs.parse(o),n=await e.addAudienceMember(r);return st(n)}),t.tool("assign_audience_courses",`Enroll existing students (by profileId) in one or more courses or cohorts. Requires courseIds or cohortIds. Students already enrolled are skipped and counted. ${nt}`,Ip,Go,async o=>{let r=Bs.parse(o),n=await e.assignAudienceCourses(r);return st(n)}),t.tool("get_audience_member",`Get a single student by their organization member id (from list_audience_members). ${nt}`,Ap,qs,async o=>{let{memberId:r}=Qs.parse(o),n=await e.getAudienceMember(r);return st(n)}),t.tool("update_audience_member",`Update a student. Send only the fields to change. email corrects the address of a member who has not accepted their invite yet and re-sends the invite (400 for a joined member). name sets the full name on a joined member's profile (400 for a pending member); the profile is shared, so the name changes in every organization the person belongs to. To change an admin's or tutor's role, use update_team_member. ${nt}`,Cp,Go,async o=>{let{memberId:r,...n}=Vs.parse(o),a=await e.updateAudienceMember(r,so.parse(n));return st(a)}),t.tool("remove_audience_member",`Remove a student from the organization. This is a hard delete of the organization membership and revokes their pending invites; the person keeps their account. ${nt}`,yp,zp,async o=>{let{memberId:r}=Ks.parse(o),n=await e.removeAudienceMember(r);return st(n)})}function st(t){return{content:[{type:"text",text:JSON.stringify(t)}]}}var Xs=Z.extend(L.shape),Js=Nr.safeExtend({cohortId:Z.shape.cohortId}),ei=He,ti=ao.safeExtend({cohortId:He.shape.cohortId,goalId:He.shape.goalId}),oi=He,ri=He,ni=He,si=Z,Tp=Xs.shape,xp=Js.shape,Sp=ei.shape,Pp=ti.shape,wp=oi.shape,Op=ri.shape,Rp=ni.shape,vp=si.shape,Ys=L.shape,Lp=`Create a progress goal for a cohort. ${j}
3
+
4
+ Required fields depend on "type":
5
+ - complete_all: no extra fields beyond courseIds.
6
+ - n_of_m: requiredCount (must be <= courseIds.length).
7
+ - score: scoreThreshold.
8
+ - pass_rate: scoreThreshold and teamPassRateThreshold.
9
+ - readiness: no extra fields; deadlineKind must be "none".
10
+
11
+ Required fields depend on "deadlineKind":
12
+ - absolute: deadlineDate.
13
+ - relative_to_join: relativeDays.
14
+ - recurring: recurringMonths (and deadlineDate for the current cycle).
15
+ - none: no extra fields.
16
+
17
+ courseIds must be non-empty and every course must already be linked to the cohort (see add_cohort_course).`;function ii(t,e){t.tool("list_cohort_goals",`List the active goals in a cohort, with per-status learner counts. ${me} ${Le}`,Tp,B,async o=>{let{cohortId:r,...n}=Xs.parse(o),a=await e.listCohortGoals(r,n);return A(a)}),t.tool("create_cohort_goal",Lp,xp,J,async o=>{let{cohortId:r,...n}=Js.parse(o),a=await e.createCohortGoal(r,n);return A(a)}),t.tool("get_cohort_goal",`Get a cohort goal. ${Le}`,Sp,B,async o=>{let{cohortId:r,goalId:n}=ei.parse(o),a=await e.getCohortGoal(r,n);return A(a)}),t.tool("update_cohort_goal",`Update a cohort goal. Send only the fields to change; omitted fields keep their values, and the resulting goal must still satisfy the create_cohort_goal rules. ${j}`,Pp,J,async o=>{let{cohortId:r,goalId:n,...a}=ti.parse(o),z=await e.updateCohortGoal(r,n,ao.parse(a));return A(z)}),t.tool("archive_cohort_goal",`Archive a cohort goal. It stops being evaluated and drops out of list_cohort_goals, but its history is kept. ${j}`,wp,J,async o=>{let{cohortId:r,goalId:n}=oi.parse(o),a=await e.archiveCohortGoal(r,n);return A(a)}),t.tool("delete_cohort_goal",`Permanently delete a cohort goal and its learner progress. This is a hard delete; use archive_cohort_goal to keep history. ${j}`,Op,je,async o=>{let{cohortId:r,goalId:n}=ri.parse(o),a=await e.deleteCohortGoal(r,n);return A(a)}),t.tool("evaluate_cohort_goal",`Re-evaluate one goal's learner statuses now instead of waiting for the scheduled run. Returns the number of learner assignments evaluated. Archived goals are not evaluated (409), so their history is kept. ${j}`,Rp,J,async o=>{let{cohortId:r,goalId:n}=ni.parse(o),a=await e.evaluateCohortGoal(r,n);return A(a)}),t.tool("evaluate_all_cohort_goals",`Re-evaluate every active goal in a cohort now. Returns the number of learner assignments evaluated. ${j}`,vp,J,async o=>{let{cohortId:r}=si.parse(o),n=await e.evaluateAllCohortGoals(r);return A(n)}),t.tool("get_org_goals_overview",`Organization-wide goal roll-up: one entry per active goal across all cohorts, with learner counts per status and on-track percentage. ${me} The API key creator must be an org admin or tutor, otherwise 403.`,Ys,B,async o=>{let r=await e.getOrgGoalsOverview(L.parse(o));return A(r)}),t.tool("list_my_cohort_goals",`List the API key creator's own goal assignments across their cohorts in this organization, with status and progress. ${me}`,Ys,B,async o=>{let r=await e.listMyCohortGoals(L.parse(o));return A(r)})}var ai=Kr.extend({cohortId:Z.shape.cohortId}),ui=Wr.extend({cohortId:Z.shape.cohortId}),ci=uo.safeExtend({cohortId:Se.shape.cohortId,feedId:Se.shape.feedId}),li=Yr.extend({cohortId:Se.shape.cohortId,feedId:Se.shape.feedId}),pi=Se,di=Se.extend(L.shape),mi=Xr.extend({cohortId:Se.shape.cohortId,feedId:Se.shape.feedId}),gi=Vr,Np=ai.shape,Dp=ui.shape,Up=ci.shape,Zp=li.shape,kp=pi.shape,jp=di.shape,Mp=mi.shape,Gp=gi.shape;function bi(t,e){t.tool("list_cohort_newsfeed",`List a cohort newsfeed, newest first. Cursor-paginated: pass the returned nextCursor back as cursor for the next page; limit defaults to 10, max 50. ${Le}`,Np,B,async o=>{let{cohortId:r,...n}=ai.parse(o),a=await e.listCohortNewsfeed(r,n);return A(a)}),t.tool("create_cohort_newsfeed_post",`Create a post on a cohort newsfeed, authored by the API key creator. ${j} The key creator must also be a member of the cohort to author a post.`,Dp,J,async o=>{let{cohortId:r,...n}=ui.parse(o),a=await e.createCohortNewsfeedPost(r,n);return A(a)}),t.tool("update_cohort_newsfeed_post",`Update a cohort newsfeed post's content or pinned state. Send only the fields to change. ${j}`,Up,J,async o=>{let{cohortId:r,feedId:n,...a}=ci.parse(o),z=await e.updateCohortNewsfeedPost(r,n,uo.parse(a));return A(z)}),t.tool("update_cohort_newsfeed_reaction","Set the API key creator's own reaction on a cohort newsfeed post: clap, smile, thumbsup, thumbsdown, or null to remove it. Other members' reactions are untouched, and repeating the same value is a no-op. The key creator must be a member of the cohort (being an org admin is not enough), otherwise 403.",Zp,{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0},async o=>{let{cohortId:r,feedId:n,...a}=li.parse(o),z=await e.setCohortNewsfeedReaction(r,n,a);return A(z)}),t.tool("delete_cohort_newsfeed_post",`Permanently delete a cohort newsfeed post and its comments. This is a hard delete and cannot be undone. ${j}`,kp,je,async o=>{let{cohortId:r,feedId:n}=pi.parse(o),a=await e.deleteCohortNewsfeedPost(r,n);return A(a)}),t.tool("list_cohort_newsfeed_comments",`List the comments on a cohort newsfeed post, oldest first. ${me} ${Le}`,jp,B,async o=>{let{cohortId:r,feedId:n,...a}=di.parse(o),z=await e.listCohortNewsfeedComments(r,n,a);return A(z)}),t.tool("create_cohort_newsfeed_comment","Add a comment to a cohort newsfeed post, authored by the API key creator. The key creator must be a member of the cohort (being an org admin is not enough), otherwise 403.",Mp,J,async o=>{let{cohortId:r,feedId:n,...a}=mi.parse(o),z=await e.createCohortNewsfeedComment(r,n,a);return A(z)}),t.tool("delete_cohort_newsfeed_comment","Permanently delete a comment from a cohort newsfeed post. The API key creator must be the comment author, a cohort tutor/admin, or an org admin, otherwise 403.",Gp,je,async o=>{let{cohortId:r,feedId:n,commentId:a}=gi.parse(o),z=await e.deleteCohortNewsfeedComment(r,n,a);return A(z)})}import*as D from"zod";var fi=D.union([D.object({type:D.literal("upload"),assetId:D.string().uuid().describe("An assetId returned by upload_video.")}),D.object({type:D.enum(["youtube","vimeo","generic"]),link:D.url().describe("Public video URL. Embedded rather than downloaded.")})]),zi='Videos for this lesson. Use { "type": "upload", "assetId": "..." } for a file uploaded with upload_video, or { "type": "youtube" | "vimeo" | "generic", "link": "..." } for an external URL.',_i="Position within the section, starting at 1. Without a section, the position among the course top-level sections, lessons and exercises; the entry at that position and every later one move down one.",Ei=D.object({courseId:D.string().min(1),title:D.string().min(1).max(255),order:D.number().int().min(1).describe(_i),sectionId:D.string().uuid().optional(),isUnlocked:D.boolean().optional(),public:D.boolean().optional(),videos:D.array(fi).max(20).optional().describe(zi)}),Ii=D.object({courseId:D.string().min(1),lessonId:D.string().uuid(),title:D.string().min(1).max(255).optional(),order:D.number().int().min(1).optional().describe(_i),isUnlocked:D.boolean().optional(),public:D.boolean().optional(),videos:D.array(fi).max(20).optional().describe(`${zi} Supplying this replaces the lesson's videos entirely.`)}),Fp=Ei.shape,qp=Ii.shape;function Ai(t,e){t.tool("create_lesson","Create a lesson in a course, optionally with videos. Attaching an uploaded video here is what starts its thumbnail, transcript and adaptive-bitrate encoding.",Fp,async o=>{let{courseId:r,...n}=Ei.parse(o),a=await e.createLesson(r,n);return hi(a)}),t.tool("update_lesson","Update a lesson. Omitted fields are left unchanged; supplying videos replaces the lesson's videos entirely.",qp,async o=>{let{courseId:r,lessonId:n,...a}=Ii.parse(o),z=await e.updateLesson(r,n,a);return hi(z)})}function hi(t){return{content:[{type:"text",text:JSON.stringify(t)}]}}import{createReadStream as $p}from"node:fs";import{stat as Hp}from"node:fs/promises";import{extname as Bp}from"node:path";import*as Bt from"zod";var Ci={".mp4":"video/mp4",".mov":"video/quicktime",".avi":"video/x-msvideo",".mkv":"video/x-matroska"};function Qp(t){let e=Bp(t).toLowerCase(),o=Ci[e];if(!o)throw new Error(`Unsupported video extension "${e||"(none)"}". Supported: ${Object.keys(Ci).join(", ")}`);return o}function Vp(t){return t.split(/[\\/]/).pop()??t}var yi=Bt.object({filePath:Bt.string().min(1).describe("Absolute local path to the video file to upload.")}),Kp=yi.shape;function Ti(t,e){t.tool("upload_video",'Upload a local video file (mp4, mov, avi, mkv) to ClassroomIO. Returns { assetId } \u2014 pass that to create_lesson or update_lesson in videos[] as { "type": "upload", "assetId": "..." } to attach it. Attaching is what starts thumbnailing, transcription and adaptive-bitrate encoding, so an uploaded video should always be attached to a lesson.',Kp,async o=>{let{filePath:r}=yi.parse(o),n=Qp(r),a=Vp(r),z=(await Hp(r)).size,{assetId:_,uploadUrl:U}=await e.createAssetUpload({kind:"video",fileName:a,mimeType:n,byteSize:z});return await e.putToPresignedUrl(U,$p(r),n,z),Wp({assetId:_,fileName:a,mimeType:n,byteSize:z})})}function Wp(t){return{content:[{type:"text",text:JSON.stringify(t)}]}}var Pi=L,wi=wr,Oi=Z,Ri=io.safeExtend({cohortId:Z.shape.cohortId}),vi=Z,Li=Z.extend(L.shape),Ni=Br.safeExtend({cohortId:Z.shape.cohortId}),Di=Qr.extend({cohortId:vt.shape.cohortId,memberId:vt.shape.memberId}),Ui=vt,Zi=Z.extend(L.shape),ki=Rr.extend({cohortId:Z.shape.cohortId}),ji=Or,Mi=Dr.extend({cohortId:Z.shape.cohortId}),Gi=Ur.extend({cohortId:Z.shape.cohortId}),Fo=Z,Fi=Zr.extend({cohortId:Z.shape.cohortId}),xi=Pi.shape,Yp=wi.shape,Xp=Oi.shape,Jp=Ri.shape,ed=vi.shape,td=Li.shape,od=Ni.shape,rd=Di.shape,nd=Ui.shape,sd=Zi.shape,id=ki.shape,ad=ji.shape,ud=Mi.shape,cd=Gi.shape,Si=Fo.shape,ld=Fi.shape;function qi(t,e){t.tool("list_org_cohorts",`List the cohorts the API key creator can see: all cohorts for an org admin, otherwise only cohorts they belong to. ${me}`,xi,B,async o=>{let r=Pi.parse(o),n=await e.listCohorts(r);return A(n)}),t.tool("create_cohort","Create a cohort. The API key creator becomes its tutor.",Yp,J,async o=>{let r=wi.parse(o),n=await e.createCohort(r);return A(n)}),t.tool("get_cohort",`Get a cohort by id. ${Le}`,Xp,B,async o=>{let{cohortId:r}=Oi.parse(o),n=await e.getCohort(r);return A(n)}),t.tool("update_cohort",`Update a cohort. Send only the fields to change. ${j}`,Jp,J,async o=>{let{cohortId:r,...n}=Ri.parse(o),a=await e.updateCohort(r,io.parse(n));return A(a)}),t.tool("delete_cohort",`Permanently delete a cohort with its memberships, newsfeed, and goals. This is a hard delete and cannot be undone; to keep history, use update_cohort with status ARCHIVED. ${j}`,ed,je,async o=>{let{cohortId:r}=vi.parse(o),n=await e.deleteCohort(r);return A(n)}),t.tool("list_cohort_members",`List the members of a cohort. ${me} ${Le}`,td,B,async o=>{let{cohortId:r,...n}=Li.parse(o),a=await e.listCohortMembers(r,n);return A(a)}),t.tool("add_cohort_members",`Create cohort memberships directly, each with a role. This sends no invitation email; use invite_students_to_cohort for the dashboard invite flow. A profileId must belong to someone already in the organization; an email is linked to its existing profile if there is one. Returns { added, errors }, where each error has the entry's index, email, profileId, code, and message. Retrying is safe: existing members fail with code MEMBER_ALREADY_IN_COHORT. ${j}`,od,J,async o=>{let{cohortId:r,...n}=Ni.parse(o),a=await e.addCohortMembers(r,n);return A(a)}),t.tool("update_cohort_member",`Change a cohort member's role (tutor or student). ${j}`,rd,J,async o=>{let{cohortId:r,memberId:n,...a}=Di.parse(o),z=await e.updateCohortMember(r,n,a);return A(z)}),t.tool("delete_cohort_member",`Remove a member from a cohort. Hard-deletes the cohort membership only; the person keeps their account, organization membership, and course enrolments. ${j}`,nd,je,async o=>{let{cohortId:r,memberId:n}=Ui.parse(o),a=await e.deleteCohortMember(r,n);return A(a)}),t.tool("list_cohort_courses",`List the courses linked to a cohort. If the API key creator is a student in the cohort, only published courses are returned. ${me} ${Le}`,sd,B,async o=>{let{cohortId:r,...n}=Zi.parse(o),a=await e.listCohortCourses(r,n);return A(a)}),t.tool("add_cohort_course",`Link a course from the organization to a cohort; existing cohort students are enrolled in it. Fails with 409 if already linked. ${j}`,id,J,async o=>{let{cohortId:r,...n}=ki.parse(o),a=await e.addCohortCourse(r,n);return A(a)}),t.tool("remove_cohort_course",`Unlink a course from a cohort. The course itself is not deleted. ${j}`,ad,je,async o=>{let{cohortId:r,courseId:n}=ji.parse(o),a=await e.removeCohortCourse(r,n);return A(a)}),t.tool("list_my_enrolled_cohorts",`List the cohorts the API key creator is enrolled in within this organization, with their role in each. ${me}`,xi,B,async o=>{let r=await e.listMyEnrolledCohorts(L.parse(o));return A(r)}),t.tool("invite_students_to_cohort",`Invite students to a cohort by email, like the dashboard invite modal. recipientCsv is a CSV of emails (optionally with names). New emails get a 7-day organization invite that joins the cohort on acceptance; existing students are enrolled directly; staff are skipped. With sendEmail true (default), invite and welcome emails are queued. Returns per-row statuses. ${j}`,ud,J,async o=>{let{cohortId:r,...n}=Mi.parse(o),a=await e.inviteStudentsToCohort(r,n);return A(a)}),t.tool("assign_students_to_cohort",`Add existing students from the organization's audience to a cohort. Profiles that are not students in this organization are skipped. With sendEmail true (default), a welcome email is queued for each newly assigned student. ${j}`,cd,J,async o=>{let{cohortId:r,...n}=Gi.parse(o),a=await e.assignStudentsToCohort(r,n);return A(a)}),t.tool("get_cohort_invite_link",`Get the cohort's shareable student join link, or null if none has been created. ${j}`,Si,B,async o=>{let{cohortId:r}=Fo.parse(o),n=await e.getCohortInviteLink(r);return A(n)}),t.tool("create_cohort_invite_link",`Get the cohort's shareable student join link, creating it on the first call. ${j}`,Si,{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0},async o=>{let{cohortId:r}=Fo.parse(o),n=await e.createCohortInviteLink(r);return A(n)}),t.tool("set_cohort_invite_link_revoked",`Disable (isRevoked true) or re-enable (isRevoked false) the cohort's join link. ${j}`,ld,{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0},async o=>{let{cohortId:r,...n}=Fi.parse(o),a=await e.setCohortInviteLinkRevoked(r,n);return A(a)})}var qo={readOnlyHint:!0,destructiveHint:!1},pd={readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0},dd="The API key creator must be a member of the course (including access through a program) or an org admin, otherwise the call fails with 403.",$o="The API key creator must be a course tutor/admin or an org admin, otherwise the call fails with 403.",$i=P,Hi=Dn.safeExtend({courseId:P.shape.courseId}),Bi=Un.extend({courseId:P.shape.courseId}),Qi=Zn.extend(kn.shape),md=$i.shape,gd=Hi.shape,bd=Bi.shape,hd=Qi.shape;function Vi(t,e){t.tool("get_course_certificate",`Read a course's certificate settings and design: template, accent colour, signatories, subtitle, ID format, whether learners can download it, the completion email message, and completion rules (deadline, threshold, required exercise). Returns effective values: defaults are filled in where nothing is stored, matching what the dashboard shows. ${dd}`,md,qo,async o=>{let{courseId:r}=$i.parse(o),n=await e.getCourseCertificate(r);return Ho(n)}),t.tool("update_course_certificate",`Partially update a course's certificate settings and design. Only the fields you send change; omitted fields keep their current values. Send null to clear deadline, requiredExerciseId, exerciseMinScorePercent, or emailMessage. design is replaced as a whole object, so read it with get_course_certificate first and send the full design. Sending design without theme sets theme to design.templateId. deadline must be an ISO 8601 datetime with a timezone, e.g. 2026-12-31T23:59:59Z. Returns the effective settings. Certificates need a paid plan: on the Basic (free) plan this fails with 403 UPGRADE_REQUIRED. ${$o}`,gd,pd,async o=>{let{courseId:r,...n}=Hi.parse(o),a=await e.updateCourseCertificate(r,n);return Ho(a)}),t.tool("list_course_certificates",`List the students who earned a course's certificate, when it was earned, and when the certificate email was sent. Compliance cycle and recertification history is not included. Optional search matches name or email. Paginated: page (default 1) and limit (default 20, max 100); the result includes pagination. ${$o}`,bd,qo,async o=>{let{courseId:r,...n}=Bi.parse(o),a=await e.listCourseCertificates(r,n);return Ho(a)}),t.tool("download_course_certificate",`Download the certificate a student earned, rendered with the course's current design, as a PDF (format "pdf", default) or PNG image (format "png"). Get memberId from list_course_certificates. ${$o}`,hd,qo,async o=>{let{courseId:r,memberId:n,format:a}=Qi.parse(o),z=await e.downloadCourseCertificate(r,n,a);return a==="png"?{content:[{type:"image",data:z.base64,mimeType:z.mimeType}]}:{content:[{type:"resource",resource:{uri:`classroomio://courses/${r}/certificates/${n}.pdf`,mimeType:z.mimeType,blob:z.base64}}]}})}function Ho(t){return{content:[{type:"text",text:JSON.stringify(t)}]}}var Ki={readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0},fd={readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},Bo="The API key creator must be a course tutor/admin or an org admin, otherwise the call fails with 403.",Qo="All or nothing: if any id is not a lesson or exercise of this course, nothing changes (404). Up to 500 items. type is LESSON or EXERCISE.",Wo=P.shape.courseId,Wi=Mn.safeExtend({courseId:Wo}),Yi=Gn.safeExtend({courseId:Wo}),Xi=Fn.safeExtend({courseId:Wo}).safeExtend(ke.shape),Vo=t=>t.shape;function Ji(t,e){t.tool("reorder_course_content",`Reorder sections and move or reorder lessons and exercises of a live course in one transaction, without a draft. sections sets section order; items set each item's order and, with sectionId, move it to another section (null for no section). Orders must be 1, 2, 3, \u2026 within each group. ${Qo} ${Bo}`,Vo(Wi),Ki,async o=>{let{courseId:r,...n}=Wi.parse(o);return Ko(await e.reorderCourseContent(r,n))}),t.tool("set_course_content_unlocked",`Lock (isUnlocked false) or unlock (isUnlocked true) lessons and exercises for students. ${Qo} ${Bo}`,Vo(Yi),Ki,async o=>{let{courseId:r,...n}=Yi.parse(o);return Ko(await e.setCourseContentUnlocked(r,n))}),t.tool("delete_course_content",`Permanently delete lessons and exercises with their translations and comments (hard delete, cannot be undone), and return what was deleted. To delete a whole section, use delete_course_section. If students have submissions or lesson completions in it, this fails with 409 unless force is true, which deletes their work too; ask the user before forcing. ${Qo} ${Bo}`,Vo(Xi),fd,async o=>{let{courseId:r,force:n,...a}=Xi.parse(o);return Ko(await e.deleteCourseContent(r,a,n))})}function Ko(t){return{content:[{type:"text",text:JSON.stringify(t)}]}}import*as R from"zod";var zd=["#EF4444","#F97316","#F59E0B","#84CC16","#22C55E","#14B8A6","#06B6D4","#3B82F6","#8B5CF6","#EC4899"],ea=R.enum(zd),eC=R.object({groupId:R.uuid()}),_d=R.object({name:R.string().min(1).max(80),description:R.string().max(240).optional(),order:R.number().int().min(0).optional()}),tC=_d.partial().refine(t=>Object.keys(t).length>0,{message:"At least one field is required"}),oC=R.object({tagId:R.uuid()}),rC=R.object({name:R.string().min(1).max(80),description:R.string().max(240).optional(),groupId:R.uuid(),color:ea}),nC=R.object({name:R.string().min(1).max(80).optional(),description:R.string().max(240).optional(),groupId:R.uuid().optional(),color:ea.optional()}).refine(t=>Object.keys(t).length>0,{message:"At least one field is required"}),sC=R.object({courseId:R.uuid()}),iC=R.object({tagIds:R.array(R.uuid()).max(100).default([])}),Ed=R.enum(["merge","replace"]).default("merge"),Id=R.string().trim().min(1).max(80),ta=R.object({draftId:R.string().uuid()}),Yo=R.object({tagNames:R.array(Id).min(1).max(100),mode:Ed,groupName:R.string().trim().min(1).max(80).optional()}),Xo=Yo.extend({courseIds:R.array(R.string().uuid()).min(1).max(100)});var ra=Pn.extend({draftId:Je.shape.draftId}),na=Yo.extend({draftId:ta.shape.draftId}),sa=po.extend({courseId:kt.shape.courseId}),ia=jt.extend({draftId:Je.shape.draftId}).refine(t=>t.type!=="COMPLIANCE"||t.compliance!==void 0,{message:"Compliance settings are required for COMPLIANCE courses",path:["compliance"]}),aa=_o.extend({draftId:Je.shape.draftId}).refine(t=>t.type!=="COMPLIANCE"||t.compliance!==void 0,{message:"Compliance settings are required for COMPLIANCE courses",path:["compliance"]}),Ad=fo.shape,Cd=zo.shape,yd=Mt.shape,Td=kt.shape,xd=Je.shape,Sd=ra.shape,Pd=na.shape,wd=sa.shape,Od=ia.shape,Rd=aa.shape,vd=Xo.shape,oa="For draft lesson HTML, put only the lesson body in lessonLanguages[].content. Do not include the lesson title. Do not use h1 or h2 anywhere in lesson HTML. Start headings at h3 because that is the highest heading level allowed in lesson content. To attach a video to a lesson, set lessons[].videos[] with a { type, link } entry (type is one of youtube, vimeo, or generic for a hosted URL). To attach a local video file, call upload_video; attaching the assetId it returns needs create_lesson or update_lesson, which act on a published course, so for a draft publish it first and then attach the video to the resulting lesson.";function ua(t,e){t.tool("list_org_courses","List all courses in the authenticated organization. Use this when the user refers to a course by name and you need to discover the available course IDs first.",yd,async o=>{let r=Mt.parse(o),n=await e.listOrganizationCourses(r);return Ce(n)}),t.tool("get_course_structure","Read the current live course structure. Use this before creating a fresh draft for post-publish edits.",Td,async o=>{let{courseId:r}=kt.parse(o),n=await e.getCourseStructure(r);return Ce(n)}),t.tool("update_course_landing_page","Update landing-page-facing course fields on a live course, including headline copy, overview, requirements, goals, pricing, reviews, instructor information, and the course image. Use imageUrl to set an explicit cover image, or set generateImage/imageQuery to fetch a random Unsplash-based image.",wd,async o=>{let{courseId:r,...n}=sa.parse(o),a=await e.updateCourseLandingPage(r,n);return Ce(a)}),t.tool("create_course_draft",`Create a new unpublished course draft from structured course JSON. Use this before the first publish. ${oa}`,Ad,async o=>{let r=fo.parse(o),n=await e.createCourseDraft(r);return Ce(n)}),t.tool("create_course_draft_from_course","Create a fresh draft from an existing live course. Use this after a course has already been published and the user wants more changes.",Cd,async o=>{let r=zo.parse(o),n=await e.createCourseDraftFromCourse(r);return Ce(n)}),t.tool("get_course_draft","Fetch an existing draft. Draft responses include status and publishedCourseId so you can decide whether to keep editing the draft or seed a fresh one from the live course.",xd,async o=>{let{draftId:r}=Je.parse(o),n=await e.getCourseDraft(r);return Ce(n)}),t.tool("update_course_draft",`Update an unpublished draft. If the draft is already published, do not keep editing it blindly; create a fresh draft from the live course first. ${oa}`,Sd,async o=>{let{draftId:r,...n}=ra.parse(o),a=await e.updateCourseDraft(r,n);return Ce(a)}),t.tool("tag_course_draft","Attach human-readable tags to a draft before publish. The tags are stored on the draft and applied during publish.",Pd,async o=>{let{draftId:r,...n}=na.parse(o),a=await e.tagCourseDraft(r,n);return Ce(a)}),t.tool("publish_course_draft","Publish an unpublished draft as a new live course. The result includes courseId and courseUrl. After publish, future major edits should usually start from a fresh draft created from the live course.",Od,async o=>{let{draftId:r,...n}=ia.parse(o),a=await e.publishCourseDraft(r,n);return Ce(a)}),t.tool("publish_course_draft_to_existing_course","Apply a fresh draft back onto an existing live course. Use this after reading the live course and creating a new draft from it.",Rd,async o=>{let{draftId:r,...n}=aa.parse(o),a=await e.publishCourseDraftToExistingCourse(r,n);return Ce(a)}),t.tool("tag_courses","Assign tags directly to one or more live courses by name. Default mode is merge, so existing tags are preserved unless replace is requested.",vd,async o=>{let r=Xo.parse(o),n=await e.tagCourses(r);return Ce(n)})}function Ce(t){return{content:[{type:"text",text:JSON.stringify(t)}]}}var It={readOnlyHint:!0,destructiveHint:!1},ca={readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},la={readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0},pa={readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},ye="The API key creator must be a course tutor/admin or an org admin, otherwise the call fails with 403.",Ld="Paginated: page (default 1) and limit (default 20, max 100); the result includes pagination.",da="Pass nextCursor back as cursor for the next page; limit defaults to 10, max 50.",Qt="Fails with 403 COMMENTS_DISABLED when comments are off for the organization, course or lesson.",ma=us.extend({courseId:P.shape.courseId}),Vt=_e,ga=_e.extend(ke.shape),ba=_e.extend(ms.shape),ha=gs.extend(ds.shape),fa=_e.extend(bs.shape),za=_e.extend(cs.shape),_a=ls.extend(_e.shape),Ea=ps.extend(Ro.shape),Ia=Ro,Te=t=>t.shape;function Aa(t,e){t.tool("list_course_lessons",`List a live course's lessons in order, without their content. Optionally filter by sectionId. ${Ld} ${ye}`,Te(ma),It,async o=>{let{courseId:r,...n}=ma.parse(o);return xe(await e.listCourseLessons(r,n))}),t.tool("get_course_lesson",`Get one lesson with its note, slides, videos, documents and the content of every language. Uploaded files come back as short-lived signed links. ${ye}`,Te(Vt),It,async o=>{let{courseId:r,lessonId:n}=Vt.parse(o);return xe(await e.getCourseLesson(r,n))}),t.tool("delete_course_lesson",`Permanently delete a lesson with its translations, history, comments and learners' completion (hard delete, cannot be undone). Use delete_course_content to delete several lessons or exercises at once. If students have submissions or lesson completions in it, this fails with 409 unless force is true, which deletes their work too; ask the user before forcing. ${ye}`,Te(ga),pa,async o=>{let{courseId:r,lessonId:n,force:a}=ga.parse(o);return xe(await e.deleteCourseLesson(r,n,a))}),t.tool("notify_course_lesson_session_update",`Email every student an updated calendar invite for a live lesson after its time or call link changed. Runs in the background and returns a job id. Fails with 409 if the lesson has no callUrl and lessonAt. ${ye}`,Te(Vt),ca,async o=>{let{courseId:r,lessonId:n}=Vt.parse(o);return xe(await e.notifyCourseLessonSessionUpdate(r,n))}),t.tool("list_course_lesson_translations",`Get a lesson's HTML content in each language, or in one language with locale (empty list if that language has none). ${ye}`,Te(ba),It,async o=>{let{courseId:r,lessonId:n,locale:a}=ba.parse(o);return xe(await e.listCourseLessonTranslations(r,n,a))}),t.tool("set_course_lesson_translation",`Create or replace a lesson's HTML content in one language. The HTML is sanitized and the save is recorded in the lesson history as the API key creator. versionIntent "manual" with versionLabel saves a named version. ${ye}`,Te(ha),la,async o=>{let{courseId:r,lessonId:n,locale:a,...z}=ha.parse(o);return xe(await e.setCourseLessonTranslation(r,n,a,z))}),t.tool("list_course_lesson_history",`List saved versions of a lesson's content in one language, newest first, with the content before and after each save. ${da} ${ye}`,Te(fa),It,async o=>{let{courseId:r,lessonId:n,...a}=fa.parse(o);return xe(await e.listCourseLessonHistory(r,n,a))}),t.tool("list_course_lesson_comments",`List a lesson's comments, newest first, with each author's name. ${da} ${Qt} ${ye}`,Te(za),It,async o=>{let{courseId:r,lessonId:n,...a}=za.parse(o);return xe(await e.listCourseLessonComments(r,n,a))}),t.tool("create_course_lesson_comment",`Post a comment on a lesson as the API key creator. An org admin who is not in the course is added to it as an admin first. ${Qt} ${ye}`,Te(_a),ca,async o=>{let{courseId:r,lessonId:n,...a}=_a.parse(o);return xe(await e.createCourseLessonComment(r,n,a))}),t.tool("update_course_lesson_comment",`Edit a lesson comment. Only its author (the API key creator) can edit it, otherwise 403. ${Qt} ${ye}`,Te(Ea),la,async o=>{let{courseId:r,lessonId:n,commentId:a,...z}=Ea.parse(o);return xe(await e.updateCourseLessonComment(r,n,a,z))}),t.tool("delete_course_lesson_comment",`Permanently delete a lesson comment (hard delete). Its author, a course tutor/admin or an org admin can delete it. ${Qt} ${ye}`,Te(Ia),pa,async o=>{let{courseId:r,lessonId:n,commentId:a}=Ia.parse(o);return xe(await e.deleteCourseLessonComment(r,n,a))})}function xe(t){return{content:[{type:"text",text:JSON.stringify(t)}]}}var Ne="The API key creator must be a course tutor/admin or an org admin, otherwise 403.",Kt={readOnlyHint:!0,destructiveHint:!1},Jo={readOnlyHint:!1,destructiveHint:!1},er={readOnlyHint:!1,destructiveHint:!0},Ca=fs.safeExtend({courseId:P.shape.courseId}),ya=zs.safeExtend({courseId:P.shape.courseId}),Ta=Ie,xa=Lo.safeExtend({courseId:Ie.shape.courseId,memberId:Ie.shape.memberId}),Sa=Ie,Pa=Ie,wa=_s.safeExtend({courseId:Ie.shape.courseId,memberId:Ie.shape.memberId}),Oa=as.safeExtend({courseId:wo.shape.courseId}),Ra=is.safeExtend({courseId:wo.shape.courseId}),va=ss,Nd=Ca.shape,Dd=ya.shape,Ud=Ta.shape,Zd=xa.shape,kd=Sa.shape,jd=Pa.shape,Md=wa.shape,Gd=Oa.shape,Fd=Ra.shape,qd=va.shape;function La(t,e){t.tool("list_course_members",`List everyone with access to a course (students and tutors), with role and progress. Paginated. ${Ne}`,Nd,Kt,async o=>{let{courseId:r,...n}=Ca.parse(o),a=await e.listCourseMembers(r,n);return De(a)}),t.tool("add_course_member",`Give someone access to a course, by profileId or email. Either must belong to someone already in the organization, otherwise 404. Fails with 409 if they are already a member. To onboard someone new with an invite email, use create_course_invite. ${Ne}`,Dd,Jo,async o=>{let{courseId:r,...n}=ya.parse(o),a=await e.addCourseMember(r,n);return De(a)}),t.tool("get_course_member",`Get a single course member's detail. ${Ne}`,Ud,Kt,async o=>{let{courseId:r,memberId:n}=Ta.parse(o),a=await e.getCourseMember(r,n);return De(a)}),t.tool("update_course_member",`Change a course member's role or email. Send only the fields to change. ${Ne}`,Zd,Jo,async o=>{let{courseId:r,memberId:n,...a}=xa.parse(o),z=await e.updateCourseMember(r,n,Lo.parse(a));return De(z)}),t.tool("delete_course_member",`Remove someone's access to a course. This is a hard delete of the course membership; the person keeps their account and organization membership. ${Ne}`,kd,er,async o=>{let{courseId:r,memberId:n}=Sa.parse(o),a=await e.deleteCourseMember(r,n);return De(a)}),t.tool("reset_course_member_progress",`Clear a student's completion progress while keeping them enrolled. This cannot be undone. Only student members can have their progress reset. ${Ne}`,jd,er,async o=>{let{courseId:r,memberId:n}=Pa.parse(o),a=await e.resetCourseMemberProgress(r,n);return De(a)}),t.tool("get_course_member_analytics",`Fetch a student's progress and grade analytics for a course. Only student members have analytics. ${Ne}`,Md,Kt,async o=>{let{courseId:r,memberId:n,...a}=wa.parse(o),z=await e.getCourseMemberAnalytics(r,n,a);return De(z)}),t.tool("list_course_invites",`List invites for a course, of any status (active, revoked, expired, or used up). Paginated. ${Ne}`,Gd,Kt,async o=>{let{courseId:r,...n}=Oa.parse(o),a=await e.listCourseInvites(r,n);return De(a)}),t.tool("create_course_invite",`Invite one or more people to a course by email or CSV. Unlike add_course_member, this can onboard someone who is not yet an organization member. Requires recipientEmails or recipientCsv. Limited to 60 requests per hour per key creator per course. ${Ne}`,Fd,Jo,async o=>{let{courseId:r,...n}=Ra.parse(o),a=await e.createCourseInvite(r,n);return De(a)}),t.tool("revoke_course_invite",`Revoke a pending course invite. The invite link stops working and this cannot be undone. ${Ne}`,qd,er,async o=>{let{courseId:r,inviteId:n}=va.parse(o),a=await e.revokeCourseInvite(r,n);return De(a)})}function De(t){return{content:[{type:"text",text:JSON.stringify(t)}]}}var $d={readOnlyHint:!0,destructiveHint:!1},Hd={readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},Bd={readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0},Qd={readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},Wt="The API key creator must be a course tutor/admin or an org admin, otherwise the call fails with 403.",Vd="Paginated: page (default 1) and limit (default 20, max 100); the result includes pagination.",Na=Ts.extend({courseId:P.shape.courseId}),Da=Ss.safeExtend({courseId:P.shape.courseId}),Ua=Ps.safeExtend(ko.shape),Za=ko.extend(ke.shape),Yt=t=>t.shape;function ka(t,e){t.tool("list_course_sections",`List a live course's sections in order. Use get_course_structure for sections with their lessons and exercises. ${Vd} ${Wt}`,Yt(Na),$d,async o=>{let{courseId:r,...n}=Na.parse(o);return Xt(await e.listCourseSections(r,n))}),t.tool("create_course_section",`Create a section on a live course. Send order to place it, or moveUngrouped: true (without order) to add it at the end and move every lesson and exercise that has no section into it. ${Wt}`,Yt(Da),Hd,async o=>{let{courseId:r,...n}=Da.parse(o);return Xt(await e.createCourseSection(r,n))}),t.tool("update_course_section",`Rename or move one section. To reorder several sections or move content between them, use reorder_course_content. ${Wt}`,Yt(Ua),Bd,async o=>{let{courseId:r,sectionId:n,...a}=Ua.parse(o);return Xt(await e.updateCourseSection(r,n,a))}),t.tool("delete_course_section",`Permanently delete a section together with every lesson and exercise in it (hard delete, cannot be undone). To keep the content, first move it out with reorder_course_content. If students have submissions or lesson completions in it, this fails with 409 unless force is true, which deletes their work too; ask the user before forcing. ${Wt}`,Yt(Za),Qd,async o=>{let{courseId:r,sectionId:n,force:a}=Za.parse(o);return Xt(await e.deleteCourseSection(r,n,a))})}function Xt(t){return{content:[{type:"text",text:JSON.stringify(t)}]}}var At={readOnlyHint:!0,destructiveHint:!1},Jt={readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},tr={readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0},or={readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},ge="The API key creator must be a course tutor/admin or an org admin, otherwise the call fails with 403.",ja="The API key creator must be an organization admin or tutor, otherwise the call fails with 403.",Ma="Paginated: page (default 1) and limit (default 20, max 100); the result includes pagination.",rr=Ar(),Me="Changes apply immediately, also on published courses. Submissions already made are not regraded, and students submit against the current questions.",eo="File upload, ordering, link, star rating and video recording questions need a paid plan (403 UPGRADE_REQUIRED on the Basic plan). PUBLIC courses only accept auto-graded question types.",Ga=Qn.extend({courseId:P.shape.courseId}),Fa=Oe,qa=Kn.safeExtend({courseId:P.shape.courseId}),$a=Wn.safeExtend(Oe.shape).safeExtend(ke.shape),Ha=Oe,Ba=Yn.safeExtend(Oe.shape),Qa=xo.safeExtend(To.shape),Va=To.extend(ke.shape),Ka=Xn.safeExtend(Oe.shape),Wa=Po.safeExtend(So.shape),Ya=So,Xa=Oe,Ja=Hn.extend(Bn.shape),eu=ws,tu=Os,ne=t=>t.shape;function ou(t,e){t.tool("list_course_exercises",`List a live course's exercises in course order, without their questions (use get_course_exercise for those). Optionally filter by sectionId (course section) or the deprecated lessonId. ${Ma} ${ge}`,ne(Ga),At,async o=>{let{courseId:r,...n}=Ga.parse(o);return se(await e.listCourseExercises(r,n))}),t.tool("get_course_exercise",`Get one exercise from a live course with its questions, options (including which are correct) and exercise sections. Question and option ids from here are what update_course_exercise needs. ${ge}`,ne(Fa),At,async o=>{let{courseId:r,exerciseId:n}=Fa.parse(o);return se(await e.getCourseExercise(r,n))}),t.tool("create_course_exercise",`Create an exercise on a live course. Either send title and questions, or send templateId (from list_exercise_templates) to copy a built-in template; with templateId only sectionId, lessonId and order may be sent alongside it. sectionId and lessonId must belong to this course. ${Me} ${eo} ${rr} ${ge}`,ne(qa),Jt,async o=>{let{courseId:r,...n}=qa.parse(o);return se(await e.createCourseExercise(r,n))}),t.tool("update_course_exercise",`Partially update an exercise on a live course. Omitted fields keep their values. questions is a diff: send a question with its id to edit it, with its id and delete: true to remove it, or without an id to add it; options work the same inside their question, and questions you leave out are unchanged. Read ids with get_course_exercise first. To change one question or one exercise section, use the add/update/delete_exercise_question and _exercise_section tools instead. sections works the same way: id to edit, id and delete: true to remove, no id to add, and sections you leave out are unchanged; a new section may carry a UUID you generate so new questions can point at it with exerciseSectionId, and a removed section must not still hold questions. Deleting a question students have answered fails with 409 unless force is true, which also deletes their answers; ask the user before forcing. ${Me} ${eo} ${rr} ${ge}`,ne($a),tr,async o=>{let{courseId:r,exerciseId:n,force:a,...z}=$a.parse(o);return se(await e.updateCourseExercise(r,n,z,a))}),t.tool("delete_course_exercise",`Permanently delete an exercise from a live course. This is a hard delete, not an archive: its questions and every student submission for it are deleted too, and it cannot be undone. ${Me} ${ge}`,ne(Ha),or,async o=>{let{courseId:r,exerciseId:n}=Ha.parse(o);return se(await e.deleteCourseExercise(r,n))}),t.tool("add_exercise_question",`Add one question, with its options, to an exercise on a live course. If the exercise has sections, pass exerciseSectionId (from get_course_exercise). ${Me} ${eo} ${rr} ${ge}`,ne(Ba),Jt,async o=>{let{courseId:r,exerciseId:n,...a}=Ba.parse(o);return se(await e.addExerciseQuestion(r,n,a))}),t.tool("update_exercise_question",`Edit one question. Fields you leave out keep their values. Options change only through addOptions (new), updateOptions (by id; omitted fields kept) and removeOptionIds; options you do not name are kept. Read ids with get_course_exercise first. ${Me} ${eo} ${ge}`,ne(Qa),tr,async o=>{let{courseId:r,exerciseId:n,questionId:a,...z}=Qa.parse(o),_=xo.parse(z);return se(await e.updateExerciseQuestion(r,n,a,_))}),t.tool("delete_exercise_question",`Permanently delete one question (hard delete). If students have answered it, the call fails with 409 unless force is true, which also deletes their answers; ask the user before forcing. ${Me} ${ge}`,ne(Va),or,async o=>{let{courseId:r,exerciseId:n,questionId:a,force:z}=Va.parse(o);return se(await e.deleteExerciseQuestion(r,n,a,z))}),t.tool("add_exercise_section",`Add one empty exercise section (a part that groups questions) to an exercise. Put questions in it with add_exercise_question or update_exercise_question (exerciseSectionId). ${Me} ${ge}`,ne(Ka),Jt,async o=>{let{courseId:r,exerciseId:n,...a}=Ka.parse(o);return se(await e.addExerciseSection(r,n,a))}),t.tool("update_exercise_section",`Edit one exercise section's title, description, order, colorTheme or afterBehavior. Fields you leave out keep their values; its questions are not touched. ${Me} ${ge}`,ne(Wa),tr,async o=>{let{courseId:r,exerciseId:n,sectionId:a,...z}=Wa.parse(o),_=Po.parse(z);return se(await e.updateExerciseSection(r,n,a,_))}),t.tool("delete_exercise_section",`Permanently delete one empty exercise section (hard delete). Fails with 409 while it holds questions or another section's afterBehavior goes to it: move or delete those first. ${Me} ${ge}`,ne(Ya),or,async o=>{let{courseId:r,exerciseId:n,sectionId:a}=Ya.parse(o);return se(await e.deleteExerciseSection(r,n,a))}),t.tool("notify_course_exercise",`Email every member of a course a link to take an exercise. Runs as a background job and returns a jobId; check it with get_course_exercise_notify_status. Every call sends the emails again, so don't retry a call that succeeded. ${ge}`,ne(Xa),Jt,async o=>{let{courseId:r,exerciseId:n}=Xa.parse(o);return se(await e.notifyCourseExercise(r,n))}),t.tool("get_course_exercise_notify_status",`Check a notification job started by notify_course_exercise. status is queued, running, completed or failed; wait nextPollMs before checking again and pass how many times you have checked as pollCount. ${ge}`,ne(Ja),At,async o=>{let{courseId:r,exerciseId:n,jobId:a,pollCount:z}=Ja.parse(o);return se(await e.getCourseExerciseNotifyStatus(r,n,a,z))}),t.tool("list_exercise_templates",`List the built-in exercise templates (the same for every organization), optionally filtered by tag. Pass a template's id as templateId to create_course_exercise. ${Ma} ${ja}`,ne(eu),At,async o=>{let r=eu.parse(o);return se(await e.listExerciseTemplates(r))}),t.tool("get_exercise_template",`Get a built-in exercise template with the questions it creates. ${ja}`,ne(tu),At,async o=>{let{templateId:r}=tu.parse(o);return se(await e.getExerciseTemplate(r))})}function se(t){return{content:[{type:"text",text:JSON.stringify(t)}]}}var nr={readOnlyHint:!0,destructiveHint:!1},ru={readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0},Kd={readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},Ct="The API key creator must be a course tutor/admin or an org admin, otherwise the call fails with 403.",Wd="The API key creator must be a member of the course (including access through a program) or an org admin, otherwise the call fails with 403.",nu="Paginated: page (default 1) and limit (default 20, max 100); the result includes pagination.",su=Jn.extend({courseId:P.shape.courseId}),iu=_t,au=es.safeExtend(_t.shape),uu=ts.safeExtend(_t.shape),cu=_t,lu=os.extend({courseId:P.shape.courseId}),it=t=>t.shape;function pu(t,e){t.tool("list_course_submissions",`List students' exercise submissions for a course, newest first, without their answers. Filter by exerciseId, memberId (a course member id) or gradingState (queued, processing, awaiting_manual, completed, failed). Use get_course_submission to read answers. ${nu} ${Ct}`,it(su),nr,async o=>{let{courseId:r,...n}=su.parse(o);return at(await e.listCourseSubmissions(r,n))}),t.tool("get_course_submission",`Get one submission with the student's answers and the points given for each. File and video answers include a short-lived download URL. Pair it with get_course_exercise to see the questions and correct answers. ${Ct}`,it(iu),nr,async o=>{let{courseId:r,submissionId:n}=iu.parse(o);return at(await e.getCourseSubmission(r,n))}),t.tool("grade_course_submission",`Grade a submission: set the points for each answer (questionId from get_course_exercise), the total, and optional feedback, and mark it completed. The student is emailed when it first becomes graded. Grading it again overwrites the grades. ${Ct}`,it(au),ru,async o=>{let{courseId:r,submissionId:n,...a}=au.parse(o);return at(await e.gradeCourseSubmission(r,n,a))}),t.tool("update_course_submission",`Change a submission's gradingState or feedback without grading it (use grade_course_submission to grade). Allowed moves: queued \u2192 processing \u2192 completed, awaiting_manual or failed; awaiting_manual \u2192 completed; failed \u2192 queued. The student is emailed when the state changes. ${Ct}`,it(uu),ru,async o=>{let{courseId:r,submissionId:n,...a}=uu.parse(o);return at(await e.updateCourseSubmission(r,n,a))}),t.tool("delete_course_submission",`Permanently delete a submission and its answers so the student can submit again. This is a hard delete and cannot be undone. ${Ct}`,it(cu),Kd,async o=>{let{courseId:r,submissionId:n}=cu.parse(o);return at(await e.deleteCourseSubmission(r,n))}),t.tool("get_course_marks",`Get a course's gradebook: one row per student with their points on every exercise (null when not graded yet). If the key creator is a student, only their own row comes back. ${nu} ${Wd}`,it(lu),nr,async o=>{let{courseId:r,...n}=lu.parse(o);return at(await e.getCourseMarks(r,n))})}function at(t){return{content:[{type:"text",text:JSON.stringify(t)}]}}var sr="The API key creator must be an org admin, otherwise 403.",du={readOnlyHint:!0,destructiveHint:!1},Yd={readOnlyHint:!1,destructiveHint:!1},mu=Ls,gu=jo,bu=Mo.safeExtend({memberId:jo.shape.memberId}),Xd=mu.shape,Jd=gu.shape,em=bu.shape;function hu(t,e){t.tool("list_team_members",`List the organization's admins and tutors, including people whose invite is still pending. Paginated. Students are listed by list_audience_members. ${sr}`,Xd,du,async o=>{let r=mu.parse(o),n=await e.listTeamMembers(r);return ir(n)}),t.tool("get_team_member",`Get a single admin or tutor by their organization member id (from list_team_members). ${sr}`,Jd,du,async o=>{let{memberId:r}=gu.parse(o),n=await e.getTeamMember(r);return ir(n)}),t.tool("update_team_member",`Change a team member's organization role, their name, or both. Send only the fields to change. roleId switches them between admin (1) and tutor (2): the key creator cannot change their own role, and the organization must keep at least one admin (both 400). name sets the full name on a joined member's profile (400 for a pending member); the profile is shared, so the name changes in every organization the person belongs to. ${sr}`,em,Yd,async o=>{let{memberId:r,...n}=bu.parse(o),a=await e.updateTeamMember(r,Mo.parse(n));return ir(a)})}function ir(t){return{content:[{type:"text",text:JSON.stringify(t)}]}}async function rm(){let t=cr(),e=new Tt(t),o=new tm({name:"classroomio-course-authoring",version:"0.0.1"});ua(o,e),qi(o,e),bi(o,e),ii(o,e),Ti(o,e),Ai(o,e),La(o,e),Vi(o,e),ka(o,e),Aa(o,e),Ji(o,e),ou(o,e),pu(o,e),Fs(o,e),Ws(o,e),hu(o,e);let r=new om;await o.connect(r),process.stdin.on("close",()=>{o.close()})}rm().catch(t=>{t instanceof Ge&&(console.error(JSON.stringify({error:t.message,status:t.status,code:t.code,field:t.field})),process.exit(1)),console.error(t),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classroomio/mcp",
3
- "version": "0.0.9",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -29,6 +29,10 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@modelcontextprotocol/sdk": "^1.27.1",
32
+ "@cio/question-types": "1.0.0",
33
+ "@cio/utils": "1.0.0"
34
+ },
35
+ "peerDependencies": {
32
36
  "zod": "^4.1.8"
33
37
  },
34
38
  "devDependencies": {
@@ -36,11 +40,12 @@
36
40
  "esbuild": "^0.25.11",
37
41
  "tsx": "^4.20.3",
38
42
  "typescript": "^5.9.3",
39
- "@cio/tsconfig": "0.0.1",
40
- "@cio/utils": "1.0.0"
43
+ "zod": "^4.1.8",
44
+ "@cio/tsconfig": "0.0.1"
41
45
  },
42
46
  "scripts": {
43
- "build": "tsc --noEmit && rm -rf dist tsconfig.tsbuildinfo && node ./scripts/build.mjs",
47
+ "build": "rimraf dist tsconfig.tsbuildinfo && node ./scripts/build.mjs",
48
+ "typecheck": "tsc --noEmit",
44
49
  "dev": "tsx src/index.ts",
45
50
  "start": "node dist/index.js",
46
51
  "format": "prettier --write \"src/**/*.ts\""