@classroomio/mcp 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -1
- package/dist/index.js +655 -251
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,7 +45,14 @@ Current tools:
|
|
|
45
45
|
- `create_course_draft`
|
|
46
46
|
- `create_course_draft_from_course`
|
|
47
47
|
- `get_course_draft`
|
|
48
|
+
- `list_course_exercises`
|
|
49
|
+
- `get_course_exercise`
|
|
50
|
+
- `create_course_exercise`
|
|
51
|
+
- `create_course_exercise_from_template`
|
|
52
|
+
- `update_course_exercise`
|
|
48
53
|
- `update_course_draft`
|
|
54
|
+
- `tag_course_draft`
|
|
55
|
+
- `tag_courses`
|
|
49
56
|
- `publish_course_draft`
|
|
50
57
|
- `publish_course_draft_to_existing_course`
|
|
51
58
|
|
|
@@ -139,6 +146,7 @@ Draft payload shape:
|
|
|
139
146
|
```text
|
|
140
147
|
draft
|
|
141
148
|
course
|
|
149
|
+
tags[]
|
|
142
150
|
sections[]
|
|
143
151
|
lessons[]
|
|
144
152
|
lessonLanguages[]
|
|
@@ -154,6 +162,31 @@ Drafts support:
|
|
|
154
162
|
- safer updates to existing courses
|
|
155
163
|
- auditability of AI-generated structure
|
|
156
164
|
|
|
165
|
+
## After Publish
|
|
166
|
+
|
|
167
|
+
Once a draft has been published, the live course becomes the safer source of truth.
|
|
168
|
+
|
|
169
|
+
Recommended agent rule:
|
|
170
|
+
|
|
171
|
+
- if the draft is still `DRAFT`, continue editing that draft
|
|
172
|
+
- if the draft is `PUBLISHED`, do not keep reusing it for major follow-up edits
|
|
173
|
+
- instead, read the live course and create a fresh draft from that course
|
|
174
|
+
|
|
175
|
+
Recommended post-publish sequence:
|
|
176
|
+
|
|
177
|
+
1. `get_course_structure`
|
|
178
|
+
2. `create_course_draft_from_course`
|
|
179
|
+
3. `update_course_draft`
|
|
180
|
+
4. `publish_course_draft_to_existing_course`
|
|
181
|
+
|
|
182
|
+
Why:
|
|
183
|
+
|
|
184
|
+
- teachers may edit the live course in the UI after publish
|
|
185
|
+
- another agent session may have changed the course
|
|
186
|
+
- reusing an old published draft can overwrite newer live changes
|
|
187
|
+
|
|
188
|
+
For exercises on an already-published course, prefer the direct exercise tools instead of creating a whole new draft when the user only wants to add or revise exercises.
|
|
189
|
+
|
|
157
190
|
## User Flows
|
|
158
191
|
|
|
159
192
|
### Flow 1: Create a new course from a prompt
|
|
@@ -178,6 +211,8 @@ Result:
|
|
|
178
211
|
- a new course is created
|
|
179
212
|
- sections and lessons are created
|
|
180
213
|
- localized lesson content is written
|
|
214
|
+
- publish returns the live `courseId` and public `courseUrl`
|
|
215
|
+
- publish can optionally set a random Unsplash-derived course cover
|
|
181
216
|
|
|
182
217
|
### Flow 2: Create a draft from a PDF
|
|
183
218
|
|
|
@@ -202,7 +237,27 @@ Important:
|
|
|
202
237
|
- the PDF does not need to be uploaded to MCP
|
|
203
238
|
- the MCP package only receives structured JSON
|
|
204
239
|
|
|
205
|
-
### Flow 3:
|
|
240
|
+
### Flow 3: Tag a draft before publish
|
|
241
|
+
|
|
242
|
+
User says:
|
|
243
|
+
|
|
244
|
+
```text
|
|
245
|
+
Add tags for algebra, beginner, and exponential growth to this draft.
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Expected tool sequence:
|
|
249
|
+
|
|
250
|
+
1. Agent calls `tag_course_draft`.
|
|
251
|
+
2. User reviews the updated draft tags.
|
|
252
|
+
3. Agent later calls `publish_course_draft`.
|
|
253
|
+
|
|
254
|
+
Result:
|
|
255
|
+
|
|
256
|
+
- the draft stores tag names
|
|
257
|
+
- publish ensures the tags exist in ClassroomIO
|
|
258
|
+
- publish assigns those tags to the live course
|
|
259
|
+
|
|
260
|
+
### Flow 4: Update an existing course safely
|
|
206
261
|
|
|
207
262
|
User says:
|
|
208
263
|
|
|
@@ -221,10 +276,29 @@ Expected tool sequence:
|
|
|
221
276
|
What this publish does today:
|
|
222
277
|
|
|
223
278
|
- updates the existing course title, description, type, and metadata
|
|
279
|
+
- can set a generated course cover from Unsplash
|
|
280
|
+
- returns the public `courseUrl`
|
|
224
281
|
- updates existing sections when the draft keeps their IDs
|
|
225
282
|
- updates existing lessons when the draft keeps their IDs
|
|
226
283
|
- creates new sections and lessons for draft items without matching live IDs
|
|
227
284
|
- upserts lesson language content
|
|
285
|
+
- merges draft tags into the existing course
|
|
286
|
+
|
|
287
|
+
### Flow 5: Add exercises to a live course
|
|
288
|
+
|
|
289
|
+
User says:
|
|
290
|
+
|
|
291
|
+
```text
|
|
292
|
+
Add a 5-question multiple-choice exercise to lesson 3.
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Expected tool sequence:
|
|
296
|
+
|
|
297
|
+
1. Agent calls `list_course_exercises` if it needs current context.
|
|
298
|
+
2. Agent calls `create_course_exercise` for a brand new exercise.
|
|
299
|
+
3. Agent calls `update_course_exercise` for later revisions.
|
|
300
|
+
|
|
301
|
+
Use this direct path when the course is already live and the change is exercise-specific.
|
|
228
302
|
|
|
229
303
|
What it does not do today:
|
|
230
304
|
|
|
@@ -253,6 +327,28 @@ For large updates, the agent should:
|
|
|
253
327
|
3. only generate new `externalId` values for new records
|
|
254
328
|
4. avoid deleting records from the draft unless the user explicitly wants omitted content to remain untouched
|
|
255
329
|
|
|
330
|
+
Publish input also supports:
|
|
331
|
+
|
|
332
|
+
- `bannerImageUrl`: use this exact course cover
|
|
333
|
+
- `bannerImageQuery`: search Unsplash with this query
|
|
334
|
+
- `generateBannerImage`: fetch a random Unsplash image using the course title or query
|
|
335
|
+
|
|
336
|
+
Tagging tools use:
|
|
337
|
+
|
|
338
|
+
- `tagNames`: human-readable tag names, not tag IDs
|
|
339
|
+
- `mode: "merge" | "replace"`
|
|
340
|
+
- `groupName` optional, defaults to `Automation`
|
|
341
|
+
|
|
342
|
+
The API creates missing tags automatically when needed.
|
|
343
|
+
|
|
344
|
+
Exercise tools use the live course directly:
|
|
345
|
+
|
|
346
|
+
- `list_course_exercises`
|
|
347
|
+
- `get_course_exercise`
|
|
348
|
+
- `create_course_exercise`
|
|
349
|
+
- `create_course_exercise_from_template`
|
|
350
|
+
- `update_course_exercise`
|
|
351
|
+
|
|
256
352
|
## Operational Notes
|
|
257
353
|
|
|
258
354
|
- `create_course_draft_from_course` may add warnings if a live course uses legacy lesson notes instead of localized lesson content
|
package/dist/index.js
CHANGED
|
@@ -40,12 +40,53 @@ var ClassroomIoApiClient = class {
|
|
|
40
40
|
method: "GET"
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
|
+
async listCourseExercises(courseId, query = {}) {
|
|
44
|
+
const searchParams = new URLSearchParams();
|
|
45
|
+
if (query.lessonId) searchParams.set("lessonId", query.lessonId);
|
|
46
|
+
if (query.sectionId) searchParams.set("sectionId", query.sectionId);
|
|
47
|
+
const querySuffix = searchParams.toString() ? `?${searchParams.toString()}` : "";
|
|
48
|
+
return this.request(`/course/${courseId}/exercise${querySuffix}`, {
|
|
49
|
+
method: "GET"
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async getCourseExercise(courseId, exerciseId) {
|
|
53
|
+
return this.request(`/course/${courseId}/exercise/${exerciseId}`, {
|
|
54
|
+
method: "GET"
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async createCourseExercise(courseId, payload) {
|
|
58
|
+
return this.request(`/course/${courseId}/exercise`, {
|
|
59
|
+
method: "POST",
|
|
60
|
+
body: {
|
|
61
|
+
...payload,
|
|
62
|
+
courseId
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
async createCourseExerciseFromTemplate(courseId, payload) {
|
|
67
|
+
return this.request(`/course/${courseId}/exercise/from-template`, {
|
|
68
|
+
method: "POST",
|
|
69
|
+
body: payload
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async updateCourseExercise(courseId, exerciseId, payload) {
|
|
73
|
+
return this.request(`/course/${courseId}/exercise/${exerciseId}`, {
|
|
74
|
+
method: "PUT",
|
|
75
|
+
body: payload
|
|
76
|
+
});
|
|
77
|
+
}
|
|
43
78
|
async updateCourseDraft(draftId, payload) {
|
|
44
79
|
return this.request(`/organization/course-import/drafts/${draftId}`, {
|
|
45
80
|
method: "PUT",
|
|
46
81
|
body: payload
|
|
47
82
|
});
|
|
48
83
|
}
|
|
84
|
+
async tagCourseDraft(draftId, payload) {
|
|
85
|
+
return this.request(`/organization/course-import/drafts/${draftId}/tags`, {
|
|
86
|
+
method: "PUT",
|
|
87
|
+
body: payload
|
|
88
|
+
});
|
|
89
|
+
}
|
|
49
90
|
async publishCourseDraft(draftId, payload) {
|
|
50
91
|
return this.request(`/organization/course-import/drafts/${draftId}/publish`, {
|
|
51
92
|
method: "POST",
|
|
@@ -58,6 +99,15 @@ var ClassroomIoApiClient = class {
|
|
|
58
99
|
body: payload
|
|
59
100
|
});
|
|
60
101
|
}
|
|
102
|
+
async tagCourses(payload) {
|
|
103
|
+
return this.request(
|
|
104
|
+
"/organization/tags/courses/assign",
|
|
105
|
+
{
|
|
106
|
+
method: "PUT",
|
|
107
|
+
body: payload
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
}
|
|
61
111
|
async request(path, options) {
|
|
62
112
|
const response = await fetch(new URL(path, this.config.CLASSROOMIO_API_URL), {
|
|
63
113
|
method: options.method,
|
|
@@ -96,11 +146,71 @@ function getConfig(env = process.env) {
|
|
|
96
146
|
return ZConfig.parse(env);
|
|
97
147
|
}
|
|
98
148
|
|
|
99
|
-
// ../utils/dist/validation/
|
|
100
|
-
import * as z3 from "zod";
|
|
101
|
-
|
|
102
|
-
// ../utils/dist/validation/course/course.js
|
|
149
|
+
// ../utils/dist/validation/tag/tag.js
|
|
103
150
|
import * as z2 from "zod";
|
|
151
|
+
var TAG_COLOR_OPTIONS = [
|
|
152
|
+
"#EF4444",
|
|
153
|
+
"#F97316",
|
|
154
|
+
"#F59E0B",
|
|
155
|
+
"#84CC16",
|
|
156
|
+
"#22C55E",
|
|
157
|
+
"#14B8A6",
|
|
158
|
+
"#06B6D4",
|
|
159
|
+
"#3B82F6",
|
|
160
|
+
"#8B5CF6",
|
|
161
|
+
"#EC4899"
|
|
162
|
+
];
|
|
163
|
+
var ZTagColor = z2.enum(TAG_COLOR_OPTIONS);
|
|
164
|
+
var ZTagGroupParam = z2.object({
|
|
165
|
+
groupId: z2.uuid()
|
|
166
|
+
});
|
|
167
|
+
var ZTagGroupCreate = z2.object({
|
|
168
|
+
name: z2.string().min(1).max(80),
|
|
169
|
+
description: z2.string().max(240).optional(),
|
|
170
|
+
order: z2.number().int().min(0).optional()
|
|
171
|
+
});
|
|
172
|
+
var ZTagGroupUpdate = ZTagGroupCreate.partial().refine((value) => Object.keys(value).length > 0, {
|
|
173
|
+
message: "At least one field is required"
|
|
174
|
+
});
|
|
175
|
+
var ZTagParam = z2.object({
|
|
176
|
+
tagId: z2.uuid()
|
|
177
|
+
});
|
|
178
|
+
var ZTagCreate = z2.object({
|
|
179
|
+
name: z2.string().min(1).max(80),
|
|
180
|
+
description: z2.string().max(240).optional(),
|
|
181
|
+
groupId: z2.uuid(),
|
|
182
|
+
color: ZTagColor
|
|
183
|
+
});
|
|
184
|
+
var ZTagUpdate = z2.object({
|
|
185
|
+
name: z2.string().min(1).max(80).optional(),
|
|
186
|
+
description: z2.string().max(240).optional(),
|
|
187
|
+
groupId: z2.uuid().optional(),
|
|
188
|
+
color: ZTagColor.optional()
|
|
189
|
+
}).refine((value) => Object.keys(value).length > 0, {
|
|
190
|
+
message: "At least one field is required"
|
|
191
|
+
});
|
|
192
|
+
var ZCourseTagParam = z2.object({
|
|
193
|
+
courseId: z2.uuid()
|
|
194
|
+
});
|
|
195
|
+
var ZCourseTagAssignment = z2.object({
|
|
196
|
+
tagIds: z2.array(z2.uuid()).max(100).default([])
|
|
197
|
+
});
|
|
198
|
+
var ZAutomationTagMode = z2.enum(["merge", "replace"]).default("merge");
|
|
199
|
+
var ZAutomationTagName = z2.string().trim().min(1).max(80);
|
|
200
|
+
var ZAutomationDraftTagParam = z2.object({
|
|
201
|
+
draftId: z2.string().uuid()
|
|
202
|
+
});
|
|
203
|
+
var ZAutomationDraftTagAssignment = z2.object({
|
|
204
|
+
tagNames: z2.array(ZAutomationTagName).min(1).max(100),
|
|
205
|
+
mode: ZAutomationTagMode,
|
|
206
|
+
groupName: z2.string().trim().min(1).max(80).optional()
|
|
207
|
+
});
|
|
208
|
+
var ZAutomationCourseTagAssignment = ZAutomationDraftTagAssignment.extend({
|
|
209
|
+
courseIds: z2.array(z2.string().uuid()).min(1).max(100)
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// ../utils/dist/validation/exercise/exercise.js
|
|
213
|
+
import * as z3 from "zod";
|
|
104
214
|
|
|
105
215
|
// ../question-types/dist/question-type-keys.js
|
|
106
216
|
var QUESTION_TYPE_KEY = {
|
|
@@ -444,226 +554,384 @@ var ALLOWED_DOCUMENT_TYPES = [
|
|
|
444
554
|
"application/msword"
|
|
445
555
|
// .doc
|
|
446
556
|
];
|
|
557
|
+
var QUESTION_TYPE = QUESTION_TYPE_IDS;
|
|
558
|
+
|
|
559
|
+
// ../utils/dist/validation/exercise/exercise.js
|
|
560
|
+
var QUESTION_VALIDATION_RULES = {
|
|
561
|
+
[QUESTION_TYPE.RADIO]: [
|
|
562
|
+
(question) => {
|
|
563
|
+
const options = question.options || [];
|
|
564
|
+
if (options.length > 0 && options.length < 2) {
|
|
565
|
+
return "Options must have at least 2 items for RADIO/CHECKBOX questions";
|
|
566
|
+
}
|
|
567
|
+
return null;
|
|
568
|
+
},
|
|
569
|
+
(question) => {
|
|
570
|
+
const options = question.options || [];
|
|
571
|
+
if (options.length > 0 && !options.some((opt) => opt.isCorrect === true)) {
|
|
572
|
+
return "Please mark an option as the correct answer";
|
|
573
|
+
}
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
],
|
|
577
|
+
[QUESTION_TYPE.CHECKBOX]: [
|
|
578
|
+
(question) => {
|
|
579
|
+
const options = question.options || [];
|
|
580
|
+
if (options.length > 0 && options.length < 2) {
|
|
581
|
+
return "Options must have at least 2 items for RADIO/CHECKBOX questions";
|
|
582
|
+
}
|
|
583
|
+
return null;
|
|
584
|
+
},
|
|
585
|
+
(question) => {
|
|
586
|
+
const options = question.options || [];
|
|
587
|
+
if (options.length > 0 && !options.some((opt) => opt.isCorrect === true)) {
|
|
588
|
+
return "Please mark an option as the correct answer";
|
|
589
|
+
}
|
|
590
|
+
return null;
|
|
591
|
+
}
|
|
592
|
+
],
|
|
593
|
+
[QUESTION_TYPE.TRUE_FALSE]: [
|
|
594
|
+
(question) => {
|
|
595
|
+
const options = question.options || [];
|
|
596
|
+
if (options.length > 0 && options.length < 2) {
|
|
597
|
+
return "True/False questions need both True and False options";
|
|
598
|
+
}
|
|
599
|
+
return null;
|
|
600
|
+
},
|
|
601
|
+
(question) => {
|
|
602
|
+
const options = question.options || [];
|
|
603
|
+
if (options.length > 0 && !options.some((opt) => opt.isCorrect === true)) {
|
|
604
|
+
return "Please mark an option as the correct answer";
|
|
605
|
+
}
|
|
606
|
+
return null;
|
|
607
|
+
}
|
|
608
|
+
],
|
|
609
|
+
[QUESTION_TYPE.TEXTAREA]: []
|
|
610
|
+
};
|
|
611
|
+
var ZExerciseUpdateQuestionBase = z3.object({
|
|
612
|
+
id: z3.number().int().optional(),
|
|
613
|
+
question: z3.string().min(1),
|
|
614
|
+
questionTypeId: z3.number().int().min(1).optional(),
|
|
615
|
+
// Added to support question type updates
|
|
616
|
+
points: z3.number().int().min(0).optional(),
|
|
617
|
+
order: z3.number().int().min(0).optional(),
|
|
618
|
+
settings: z3.record(z3.string(), z3.unknown()).optional(),
|
|
619
|
+
deletedAt: z3.string().optional(),
|
|
620
|
+
// Marks question as deleted
|
|
621
|
+
options: z3.array(z3.object({
|
|
622
|
+
id: z3.number().int().optional(),
|
|
623
|
+
label: z3.string().min(1),
|
|
624
|
+
isCorrect: z3.boolean(),
|
|
625
|
+
settings: z3.record(z3.string(), z3.unknown()).optional(),
|
|
626
|
+
deletedAt: z3.string().optional()
|
|
627
|
+
// Marks option as deleted
|
|
628
|
+
})).optional()
|
|
629
|
+
});
|
|
630
|
+
function validateQuestionOptions(question, ctx) {
|
|
631
|
+
if (question.deletedAt)
|
|
632
|
+
return;
|
|
633
|
+
const rules = QUESTION_VALIDATION_RULES[question.questionTypeId ?? -1] ?? [];
|
|
634
|
+
for (const rule of rules) {
|
|
635
|
+
const message = rule(question);
|
|
636
|
+
if (!message)
|
|
637
|
+
continue;
|
|
638
|
+
ctx.addIssue({
|
|
639
|
+
code: "custom",
|
|
640
|
+
message,
|
|
641
|
+
path: ["options"]
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
var ZExerciseUpdateQuestion = ZExerciseUpdateQuestionBase.superRefine(validateQuestionOptions);
|
|
646
|
+
var ZExerciseCreate = z3.object({
|
|
647
|
+
title: z3.string().min(1),
|
|
648
|
+
description: z3.string().optional(),
|
|
649
|
+
lessonId: z3.string().optional(),
|
|
650
|
+
sectionId: z3.string().optional(),
|
|
651
|
+
order: z3.number().int().optional(),
|
|
652
|
+
courseId: z3.string().min(1),
|
|
653
|
+
dueBy: z3.string().optional(),
|
|
654
|
+
questions: z3.array(z3.object({
|
|
655
|
+
question: z3.string().min(1),
|
|
656
|
+
questionTypeId: z3.number().int().min(1).optional(),
|
|
657
|
+
points: z3.number().int().min(0).optional(),
|
|
658
|
+
settings: z3.record(z3.string(), z3.unknown()).optional(),
|
|
659
|
+
options: z3.array(z3.object({
|
|
660
|
+
label: z3.string().min(1),
|
|
661
|
+
isCorrect: z3.boolean(),
|
|
662
|
+
settings: z3.record(z3.string(), z3.unknown()).optional()
|
|
663
|
+
})).min(2)
|
|
664
|
+
})).optional()
|
|
665
|
+
});
|
|
666
|
+
var ZExerciseUpdate = z3.object({
|
|
667
|
+
title: z3.string().min(1).optional(),
|
|
668
|
+
description: z3.string().optional(),
|
|
669
|
+
lessonId: z3.string().optional(),
|
|
670
|
+
sectionId: z3.string().optional(),
|
|
671
|
+
order: z3.number().int().optional(),
|
|
672
|
+
isUnlocked: z3.boolean().optional(),
|
|
673
|
+
dueBy: z3.string().optional(),
|
|
674
|
+
// Changed from iso.datetime() to string to match frontend format
|
|
675
|
+
questions: z3.array(ZExerciseUpdateQuestion).optional()
|
|
676
|
+
});
|
|
677
|
+
var ZExerciseGetParam = z3.object({
|
|
678
|
+
exerciseId: z3.string().min(1)
|
|
679
|
+
});
|
|
680
|
+
var ZExerciseListQuery = z3.object({
|
|
681
|
+
lessonId: z3.string().optional(),
|
|
682
|
+
sectionId: z3.string().optional()
|
|
683
|
+
});
|
|
684
|
+
var ZExerciseSubmissionCreate = z3.object({
|
|
685
|
+
exerciseId: z3.string().min(1),
|
|
686
|
+
answers: z3.array(z3.object({
|
|
687
|
+
questionId: z3.number().int(),
|
|
688
|
+
optionId: z3.number().int().optional(),
|
|
689
|
+
answer: z3.string().optional()
|
|
690
|
+
})).min(1)
|
|
691
|
+
});
|
|
692
|
+
var ZExerciseFromTemplate = z3.object({
|
|
693
|
+
lessonId: z3.string().optional(),
|
|
694
|
+
sectionId: z3.string().optional(),
|
|
695
|
+
order: z3.number().int().optional(),
|
|
696
|
+
templateId: z3.number().int().min(1)
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
// ../utils/dist/validation/course-import/course-import.js
|
|
700
|
+
import * as z5 from "zod";
|
|
447
701
|
|
|
448
702
|
// ../utils/dist/validation/course/course.js
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
703
|
+
import * as z4 from "zod";
|
|
704
|
+
var ZCourseClone = z4.object({
|
|
705
|
+
title: z4.string().min(1),
|
|
706
|
+
description: z4.string().optional(),
|
|
707
|
+
slug: z4.string().min(1),
|
|
708
|
+
organizationId: z4.string().min(1)
|
|
709
|
+
});
|
|
710
|
+
var ZCourseCloneParam = z4.object({
|
|
711
|
+
courseId: z4.string().min(1)
|
|
712
|
+
});
|
|
713
|
+
var ZCourseGetParam = z4.object({
|
|
714
|
+
courseId: z4.string().min(1)
|
|
715
|
+
});
|
|
716
|
+
var ZCourseGetQuery = z4.object({
|
|
717
|
+
slug: z4.string().optional()
|
|
718
|
+
});
|
|
719
|
+
var ZCourseGetBySlugParam = z4.object({
|
|
720
|
+
slug: z4.string().min(1)
|
|
721
|
+
});
|
|
722
|
+
var ZCourseEnrollParam = z4.object({
|
|
723
|
+
courseId: z4.string().min(1)
|
|
724
|
+
});
|
|
725
|
+
var ZCourseEnrollBody = z4.object({
|
|
726
|
+
inviteToken: z4.string().min(1).optional()
|
|
727
|
+
});
|
|
728
|
+
var ZCourseDownloadParam = z4.object({
|
|
729
|
+
courseId: z4.string().min(1)
|
|
730
|
+
});
|
|
731
|
+
var ZCertificateDownload = z4.object({
|
|
732
|
+
theme: z4.string().min(1),
|
|
733
|
+
studentName: z4.string().min(1),
|
|
734
|
+
courseName: z4.string().min(1),
|
|
735
|
+
courseDescription: z4.string().min(1),
|
|
736
|
+
orgName: z4.string().min(1),
|
|
737
|
+
orgLogoUrl: z4.string().url().optional(),
|
|
738
|
+
facilitator: z4.string().optional()
|
|
739
|
+
});
|
|
740
|
+
var ZCourseDownloadContent = z4.object({
|
|
741
|
+
courseTitle: z4.string().min(1),
|
|
742
|
+
orgName: z4.string().min(1),
|
|
743
|
+
orgTheme: z4.string().min(1),
|
|
744
|
+
lessons: z4.array(z4.object({
|
|
745
|
+
lessonTitle: z4.string().min(1),
|
|
746
|
+
lessonNumber: z4.string().min(1),
|
|
747
|
+
lessonNote: z4.string(),
|
|
748
|
+
slideUrl: z4.string().url().optional(),
|
|
749
|
+
video: z4.array(z4.string().url()).optional()
|
|
495
750
|
}))
|
|
496
751
|
});
|
|
497
|
-
var ZLessonDownloadContent =
|
|
498
|
-
title:
|
|
499
|
-
number:
|
|
500
|
-
orgName:
|
|
501
|
-
note:
|
|
502
|
-
slideUrl:
|
|
503
|
-
video:
|
|
504
|
-
courseTitle:
|
|
505
|
-
});
|
|
506
|
-
var ZCoursePresignUrlUpload =
|
|
507
|
-
fileName:
|
|
508
|
-
fileType:
|
|
509
|
-
});
|
|
510
|
-
var ZCourseDocumentPresignUrlUpload =
|
|
511
|
-
fileName:
|
|
512
|
-
fileType:
|
|
513
|
-
});
|
|
514
|
-
var ZCourseDownloadPresignedUrl =
|
|
515
|
-
keys:
|
|
516
|
-
});
|
|
517
|
-
var ZCourseContentUpdateItem =
|
|
518
|
-
id:
|
|
519
|
-
type:
|
|
520
|
-
isUnlocked:
|
|
521
|
-
order:
|
|
522
|
-
sectionId:
|
|
523
|
-
});
|
|
524
|
-
var ZCourseContentUpdate =
|
|
525
|
-
items:
|
|
526
|
-
});
|
|
527
|
-
var ZCourseContentDeleteItem =
|
|
528
|
-
id:
|
|
529
|
-
type:
|
|
530
|
-
});
|
|
531
|
-
var ZCourseContentDelete =
|
|
532
|
-
sectionId:
|
|
533
|
-
items:
|
|
752
|
+
var ZLessonDownloadContent = z4.object({
|
|
753
|
+
title: z4.string().min(1),
|
|
754
|
+
number: z4.string().min(1),
|
|
755
|
+
orgName: z4.string().min(1),
|
|
756
|
+
note: z4.string(),
|
|
757
|
+
slideUrl: z4.string().url().optional(),
|
|
758
|
+
video: z4.array(z4.string().url()).optional(),
|
|
759
|
+
courseTitle: z4.string().min(1)
|
|
760
|
+
});
|
|
761
|
+
var ZCoursePresignUrlUpload = z4.object({
|
|
762
|
+
fileName: z4.string().min(1),
|
|
763
|
+
fileType: z4.enum(ALLOWED_CONTENT_TYPES)
|
|
764
|
+
});
|
|
765
|
+
var ZCourseDocumentPresignUrlUpload = z4.object({
|
|
766
|
+
fileName: z4.string().min(1),
|
|
767
|
+
fileType: z4.enum(ALLOWED_DOCUMENT_TYPES)
|
|
768
|
+
});
|
|
769
|
+
var ZCourseDownloadPresignedUrl = z4.object({
|
|
770
|
+
keys: z4.array(z4.string().min(1)).min(1)
|
|
771
|
+
});
|
|
772
|
+
var ZCourseContentUpdateItem = z4.object({
|
|
773
|
+
id: z4.string().min(1),
|
|
774
|
+
type: z4.enum(["LESSON", "EXERCISE"]),
|
|
775
|
+
isUnlocked: z4.boolean().optional(),
|
|
776
|
+
order: z4.number().int().min(0).optional(),
|
|
777
|
+
sectionId: z4.string().nullable().optional()
|
|
778
|
+
});
|
|
779
|
+
var ZCourseContentUpdate = z4.object({
|
|
780
|
+
items: z4.array(ZCourseContentUpdateItem).min(1)
|
|
781
|
+
});
|
|
782
|
+
var ZCourseContentDeleteItem = z4.object({
|
|
783
|
+
id: z4.string().min(1),
|
|
784
|
+
type: z4.enum(["LESSON", "EXERCISE"])
|
|
785
|
+
});
|
|
786
|
+
var ZCourseContentDelete = z4.object({
|
|
787
|
+
sectionId: z4.string().min(1).optional(),
|
|
788
|
+
items: z4.array(ZCourseContentDeleteItem).min(1).optional()
|
|
534
789
|
}).refine((data) => Boolean(data.sectionId) !== Boolean(data.items), {
|
|
535
790
|
message: "Provide either sectionId or items",
|
|
536
791
|
path: ["sectionId"]
|
|
537
792
|
});
|
|
538
|
-
var ZCourseCreate =
|
|
539
|
-
title:
|
|
540
|
-
description:
|
|
541
|
-
type:
|
|
542
|
-
organizationId:
|
|
543
|
-
});
|
|
544
|
-
var ZCourseMetadata =
|
|
545
|
-
requirements:
|
|
546
|
-
description:
|
|
547
|
-
goals:
|
|
548
|
-
videoUrl:
|
|
549
|
-
showDiscount:
|
|
550
|
-
discount:
|
|
551
|
-
paymentLink:
|
|
552
|
-
reward:
|
|
553
|
-
show:
|
|
554
|
-
description:
|
|
793
|
+
var ZCourseCreate = z4.object({
|
|
794
|
+
title: z4.string().min(1),
|
|
795
|
+
description: z4.string().min(1),
|
|
796
|
+
type: z4.enum(["LIVE_CLASS", "SELF_PACED"]),
|
|
797
|
+
organizationId: z4.string().min(1)
|
|
798
|
+
});
|
|
799
|
+
var ZCourseMetadata = z4.object({
|
|
800
|
+
requirements: z4.string().optional(),
|
|
801
|
+
description: z4.string().optional(),
|
|
802
|
+
goals: z4.string().optional(),
|
|
803
|
+
videoUrl: z4.string().optional(),
|
|
804
|
+
showDiscount: z4.boolean().optional(),
|
|
805
|
+
discount: z4.number().optional(),
|
|
806
|
+
paymentLink: z4.string().optional(),
|
|
807
|
+
reward: z4.object({
|
|
808
|
+
show: z4.boolean(),
|
|
809
|
+
description: z4.string()
|
|
555
810
|
}).optional(),
|
|
556
|
-
instructor:
|
|
557
|
-
name:
|
|
558
|
-
role:
|
|
559
|
-
coursesNo:
|
|
560
|
-
description:
|
|
561
|
-
imgUrl:
|
|
811
|
+
instructor: z4.object({
|
|
812
|
+
name: z4.string(),
|
|
813
|
+
role: z4.string(),
|
|
814
|
+
coursesNo: z4.number(),
|
|
815
|
+
description: z4.string(),
|
|
816
|
+
imgUrl: z4.string()
|
|
562
817
|
}).optional(),
|
|
563
|
-
certificate:
|
|
564
|
-
templateUrl:
|
|
818
|
+
certificate: z4.object({
|
|
819
|
+
templateUrl: z4.string()
|
|
565
820
|
}).optional(),
|
|
566
|
-
reviews:
|
|
567
|
-
id:
|
|
568
|
-
hide:
|
|
569
|
-
name:
|
|
570
|
-
avatar_url:
|
|
571
|
-
rating:
|
|
572
|
-
created_at:
|
|
573
|
-
description:
|
|
821
|
+
reviews: z4.array(z4.object({
|
|
822
|
+
id: z4.number(),
|
|
823
|
+
hide: z4.boolean(),
|
|
824
|
+
name: z4.string(),
|
|
825
|
+
avatar_url: z4.string(),
|
|
826
|
+
rating: z4.number(),
|
|
827
|
+
created_at: z4.number(),
|
|
828
|
+
description: z4.string()
|
|
574
829
|
})).optional(),
|
|
575
|
-
lessonTabsOrder:
|
|
576
|
-
id:
|
|
577
|
-
name:
|
|
830
|
+
lessonTabsOrder: z4.array(z4.object({
|
|
831
|
+
id: z4.union([z4.literal(1), z4.literal(2), z4.literal(3), z4.literal(4)]),
|
|
832
|
+
name: z4.string()
|
|
578
833
|
})).optional(),
|
|
579
|
-
grading:
|
|
580
|
-
lessonDownload:
|
|
581
|
-
allowNewStudent:
|
|
582
|
-
sectionDisplay:
|
|
583
|
-
isContentGroupingEnabled:
|
|
584
|
-
});
|
|
585
|
-
var ZCourseUpdate =
|
|
586
|
-
title:
|
|
587
|
-
description:
|
|
588
|
-
type:
|
|
589
|
-
logo:
|
|
590
|
-
slug:
|
|
591
|
-
isPublished:
|
|
592
|
-
overview:
|
|
834
|
+
grading: z4.boolean().optional(),
|
|
835
|
+
lessonDownload: z4.boolean().optional(),
|
|
836
|
+
allowNewStudent: z4.boolean(),
|
|
837
|
+
sectionDisplay: z4.record(z4.string(), z4.boolean()).optional(),
|
|
838
|
+
isContentGroupingEnabled: z4.boolean().optional()
|
|
839
|
+
});
|
|
840
|
+
var ZCourseUpdate = z4.object({
|
|
841
|
+
title: z4.string().min(1).optional(),
|
|
842
|
+
description: z4.string().min(1).optional(),
|
|
843
|
+
type: z4.enum(["LIVE_CLASS", "SELF_PACED"]).optional(),
|
|
844
|
+
logo: z4.string().optional(),
|
|
845
|
+
slug: z4.string().optional(),
|
|
846
|
+
isPublished: z4.boolean().optional(),
|
|
847
|
+
overview: z4.string().optional(),
|
|
593
848
|
metadata: ZCourseMetadata.optional(),
|
|
594
|
-
isCertificateDownloadable:
|
|
595
|
-
certificateTheme:
|
|
596
|
-
tagIds:
|
|
849
|
+
isCertificateDownloadable: z4.boolean().optional(),
|
|
850
|
+
certificateTheme: z4.string().optional(),
|
|
851
|
+
tagIds: z4.array(z4.uuid()).max(100).optional()
|
|
597
852
|
});
|
|
598
|
-
var ZCourseUpdateParam =
|
|
599
|
-
courseId:
|
|
853
|
+
var ZCourseUpdateParam = z4.object({
|
|
854
|
+
courseId: z4.string().min(1)
|
|
600
855
|
});
|
|
601
|
-
var ZCourseDeleteParam =
|
|
602
|
-
courseId:
|
|
856
|
+
var ZCourseDeleteParam = z4.object({
|
|
857
|
+
courseId: z4.string().min(1)
|
|
603
858
|
});
|
|
604
|
-
var ZCourseProgressParam =
|
|
605
|
-
courseId:
|
|
859
|
+
var ZCourseProgressParam = z4.object({
|
|
860
|
+
courseId: z4.string().min(1)
|
|
606
861
|
});
|
|
607
|
-
var ZCourseProgressQuery =
|
|
608
|
-
profileId:
|
|
862
|
+
var ZCourseProgressQuery = z4.object({
|
|
863
|
+
profileId: z4.string().uuid()
|
|
609
864
|
});
|
|
610
|
-
var ZCourseUserAnalyticsParam =
|
|
611
|
-
courseId:
|
|
612
|
-
userId:
|
|
865
|
+
var ZCourseUserAnalyticsParam = z4.object({
|
|
866
|
+
courseId: z4.string().min(1),
|
|
867
|
+
userId: z4.string().uuid()
|
|
613
868
|
});
|
|
614
869
|
|
|
615
870
|
// ../utils/dist/validation/course-import/course-import.js
|
|
616
|
-
var ZSupportedLocale =
|
|
617
|
-
var ZCourseImportWarning =
|
|
618
|
-
code:
|
|
619
|
-
message:
|
|
620
|
-
severity:
|
|
621
|
-
});
|
|
622
|
-
var ZCourseImportSourceReference =
|
|
623
|
-
type:
|
|
624
|
-
label:
|
|
625
|
-
pageStart:
|
|
626
|
-
pageEnd:
|
|
627
|
-
});
|
|
628
|
-
var ZCourseImportDraftCourse =
|
|
629
|
-
title:
|
|
630
|
-
description:
|
|
631
|
-
type:
|
|
871
|
+
var ZSupportedLocale = z5.enum(["en", "hi", "fr", "pt", "de", "vi", "ru", "es", "pl", "da"]);
|
|
872
|
+
var ZCourseImportWarning = z5.object({
|
|
873
|
+
code: z5.string().min(1),
|
|
874
|
+
message: z5.string().min(1),
|
|
875
|
+
severity: z5.enum(["info", "warning", "error"])
|
|
876
|
+
});
|
|
877
|
+
var ZCourseImportSourceReference = z5.object({
|
|
878
|
+
type: z5.enum(["prompt", "pdf", "course"]),
|
|
879
|
+
label: z5.string().min(1),
|
|
880
|
+
pageStart: z5.number().int().min(1).optional(),
|
|
881
|
+
pageEnd: z5.number().int().min(1).optional()
|
|
882
|
+
});
|
|
883
|
+
var ZCourseImportDraftCourse = z5.object({
|
|
884
|
+
title: z5.string().min(1),
|
|
885
|
+
description: z5.string().min(1),
|
|
886
|
+
type: z5.enum(["LIVE_CLASS", "SELF_PACED"]),
|
|
632
887
|
locale: ZSupportedLocale.default("en"),
|
|
633
888
|
metadata: ZCourseMetadata.optional()
|
|
634
889
|
});
|
|
635
|
-
var ZCourseImportDraftSection =
|
|
636
|
-
externalId:
|
|
637
|
-
title:
|
|
638
|
-
order:
|
|
890
|
+
var ZCourseImportDraftSection = z5.object({
|
|
891
|
+
externalId: z5.string().min(1),
|
|
892
|
+
title: z5.string().min(1),
|
|
893
|
+
order: z5.number().int().min(0)
|
|
639
894
|
});
|
|
640
|
-
var ZCourseImportDraftLesson =
|
|
641
|
-
externalId:
|
|
642
|
-
sectionExternalId:
|
|
643
|
-
title:
|
|
644
|
-
order:
|
|
645
|
-
isUnlocked:
|
|
646
|
-
public:
|
|
895
|
+
var ZCourseImportDraftLesson = z5.object({
|
|
896
|
+
externalId: z5.string().min(1),
|
|
897
|
+
sectionExternalId: z5.string().min(1),
|
|
898
|
+
title: z5.string().min(1),
|
|
899
|
+
order: z5.number().int().min(0),
|
|
900
|
+
isUnlocked: z5.boolean().optional(),
|
|
901
|
+
public: z5.boolean().optional()
|
|
647
902
|
});
|
|
648
|
-
var ZCourseImportDraftLessonLanguage =
|
|
649
|
-
lessonExternalId:
|
|
903
|
+
var ZCourseImportDraftLessonLanguage = z5.object({
|
|
904
|
+
lessonExternalId: z5.string().min(1),
|
|
650
905
|
locale: ZSupportedLocale,
|
|
651
|
-
content:
|
|
906
|
+
content: z5.string().min(1)
|
|
652
907
|
});
|
|
653
|
-
var ZCourseImportDraftPayload =
|
|
908
|
+
var ZCourseImportDraftPayload = z5.object({
|
|
654
909
|
course: ZCourseImportDraftCourse,
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
910
|
+
tags: z5.array(z5.string().trim().min(1).max(80)).max(100).default([]),
|
|
911
|
+
sections: z5.array(ZCourseImportDraftSection).min(1),
|
|
912
|
+
lessons: z5.array(ZCourseImportDraftLesson).min(1),
|
|
913
|
+
lessonLanguages: z5.array(ZCourseImportDraftLessonLanguage).min(1),
|
|
914
|
+
exercises: z5.array(z5.record(z5.string(), z5.unknown())).optional(),
|
|
915
|
+
sourceReferences: z5.array(ZCourseImportSourceReference).optional(),
|
|
916
|
+
warnings: z5.array(ZCourseImportWarning).default([])
|
|
661
917
|
}).superRefine((value, ctx) => {
|
|
918
|
+
const normalizedTags = /* @__PURE__ */ new Set();
|
|
919
|
+
value.tags.forEach((tag, index) => {
|
|
920
|
+
const tagKey = tag.trim().toLowerCase();
|
|
921
|
+
if (normalizedTags.has(tagKey)) {
|
|
922
|
+
ctx.addIssue({
|
|
923
|
+
code: z5.ZodIssueCode.custom,
|
|
924
|
+
path: ["tags", index],
|
|
925
|
+
message: "Draft tags must be unique"
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
normalizedTags.add(tagKey);
|
|
929
|
+
});
|
|
662
930
|
const sectionIds = /* @__PURE__ */ new Set();
|
|
663
931
|
value.sections.forEach((section, index) => {
|
|
664
932
|
if (sectionIds.has(section.externalId)) {
|
|
665
933
|
ctx.addIssue({
|
|
666
|
-
code:
|
|
934
|
+
code: z5.ZodIssueCode.custom,
|
|
667
935
|
path: ["sections", index, "externalId"],
|
|
668
936
|
message: "Section externalId must be unique"
|
|
669
937
|
});
|
|
@@ -674,7 +942,7 @@ var ZCourseImportDraftPayload = z3.object({
|
|
|
674
942
|
value.lessons.forEach((lesson, index) => {
|
|
675
943
|
if (lessonIds.has(lesson.externalId)) {
|
|
676
944
|
ctx.addIssue({
|
|
677
|
-
code:
|
|
945
|
+
code: z5.ZodIssueCode.custom,
|
|
678
946
|
path: ["lessons", index, "externalId"],
|
|
679
947
|
message: "Lesson externalId must be unique"
|
|
680
948
|
});
|
|
@@ -682,7 +950,7 @@ var ZCourseImportDraftPayload = z3.object({
|
|
|
682
950
|
lessonIds.add(lesson.externalId);
|
|
683
951
|
if (!sectionIds.has(lesson.sectionExternalId)) {
|
|
684
952
|
ctx.addIssue({
|
|
685
|
-
code:
|
|
953
|
+
code: z5.ZodIssueCode.custom,
|
|
686
954
|
path: ["lessons", index, "sectionExternalId"],
|
|
687
955
|
message: "Lesson sectionExternalId must reference an existing section"
|
|
688
956
|
});
|
|
@@ -691,52 +959,76 @@ var ZCourseImportDraftPayload = z3.object({
|
|
|
691
959
|
value.lessonLanguages.forEach((lessonLanguage, index) => {
|
|
692
960
|
if (!lessonIds.has(lessonLanguage.lessonExternalId)) {
|
|
693
961
|
ctx.addIssue({
|
|
694
|
-
code:
|
|
962
|
+
code: z5.ZodIssueCode.custom,
|
|
695
963
|
path: ["lessonLanguages", index, "lessonExternalId"],
|
|
696
964
|
message: "Lesson language must reference an existing lesson"
|
|
697
965
|
});
|
|
698
966
|
}
|
|
699
967
|
});
|
|
700
968
|
});
|
|
701
|
-
var ZCourseImportDraftCreate =
|
|
702
|
-
sourceType:
|
|
703
|
-
idempotencyKey:
|
|
704
|
-
summary:
|
|
705
|
-
sourceArtifacts:
|
|
969
|
+
var ZCourseImportDraftCreate = z5.object({
|
|
970
|
+
sourceType: z5.enum(["prompt", "pdf", "course"]),
|
|
971
|
+
idempotencyKey: z5.string().min(1).optional(),
|
|
972
|
+
summary: z5.record(z5.string(), z5.unknown()).optional(),
|
|
973
|
+
sourceArtifacts: z5.array(z5.record(z5.string(), z5.unknown())).optional(),
|
|
706
974
|
draft: ZCourseImportDraftPayload
|
|
707
975
|
});
|
|
708
|
-
var ZCourseImportCourseParam =
|
|
709
|
-
courseId:
|
|
976
|
+
var ZCourseImportCourseParam = z5.object({
|
|
977
|
+
courseId: z5.string().min(1)
|
|
710
978
|
});
|
|
711
|
-
var ZCourseImportDraftCreateFromCourse =
|
|
712
|
-
courseId:
|
|
713
|
-
idempotencyKey:
|
|
714
|
-
summary:
|
|
715
|
-
sourceArtifacts:
|
|
979
|
+
var ZCourseImportDraftCreateFromCourse = z5.object({
|
|
980
|
+
courseId: z5.string().min(1),
|
|
981
|
+
idempotencyKey: z5.string().min(1).optional(),
|
|
982
|
+
summary: z5.record(z5.string(), z5.unknown()).optional(),
|
|
983
|
+
sourceArtifacts: z5.array(z5.record(z5.string(), z5.unknown())).optional()
|
|
716
984
|
});
|
|
717
|
-
var ZCourseImportDraftGetParam =
|
|
718
|
-
draftId:
|
|
985
|
+
var ZCourseImportDraftGetParam = z5.object({
|
|
986
|
+
draftId: z5.string().uuid()
|
|
719
987
|
});
|
|
720
|
-
var ZCourseImportDraftUpdate =
|
|
721
|
-
summary:
|
|
722
|
-
sourceArtifacts:
|
|
723
|
-
warnings:
|
|
988
|
+
var ZCourseImportDraftUpdate = z5.object({
|
|
989
|
+
summary: z5.record(z5.string(), z5.unknown()).optional(),
|
|
990
|
+
sourceArtifacts: z5.array(z5.record(z5.string(), z5.unknown())).optional(),
|
|
991
|
+
warnings: z5.array(ZCourseImportWarning).optional(),
|
|
724
992
|
draft: ZCourseImportDraftPayload.optional()
|
|
725
993
|
});
|
|
726
|
-
var ZCourseImportDraftPublish =
|
|
727
|
-
title:
|
|
728
|
-
description:
|
|
729
|
-
type:
|
|
730
|
-
metadata: ZCourseMetadata.optional()
|
|
994
|
+
var ZCourseImportDraftPublish = z5.object({
|
|
995
|
+
title: z5.string().min(1).optional(),
|
|
996
|
+
description: z5.string().min(1).optional(),
|
|
997
|
+
type: z5.enum(["LIVE_CLASS", "SELF_PACED"]).optional(),
|
|
998
|
+
metadata: ZCourseMetadata.optional(),
|
|
999
|
+
bannerImageUrl: z5.string().url().optional(),
|
|
1000
|
+
bannerImageQuery: z5.string().min(1).max(120).optional(),
|
|
1001
|
+
generateBannerImage: z5.boolean().optional()
|
|
731
1002
|
});
|
|
732
1003
|
var ZCourseImportDraftPublishToCourse = ZCourseImportDraftPublish.extend({
|
|
733
|
-
courseId:
|
|
1004
|
+
courseId: z5.string().min(1)
|
|
734
1005
|
});
|
|
735
1006
|
|
|
736
1007
|
// src/tools/course-drafts.ts
|
|
737
1008
|
var ZUpdateCourseDraftToolInput = ZCourseImportDraftUpdate.extend({
|
|
738
1009
|
draftId: ZCourseImportDraftGetParam.shape.draftId
|
|
739
1010
|
});
|
|
1011
|
+
var ZTagCourseDraftToolInput = ZAutomationDraftTagAssignment.extend({
|
|
1012
|
+
draftId: ZAutomationDraftTagParam.shape.draftId
|
|
1013
|
+
});
|
|
1014
|
+
var ZListCourseExercisesToolInput = ZExerciseListQuery.extend({
|
|
1015
|
+
courseId: ZCourseImportCourseParam.shape.courseId
|
|
1016
|
+
});
|
|
1017
|
+
var ZGetCourseExerciseToolInput = ZExerciseGetParam.extend({
|
|
1018
|
+
courseId: ZCourseImportCourseParam.shape.courseId
|
|
1019
|
+
});
|
|
1020
|
+
var ZCreateCourseExerciseToolInput = ZExerciseCreate.omit({
|
|
1021
|
+
courseId: true
|
|
1022
|
+
}).extend({
|
|
1023
|
+
courseId: ZCourseImportCourseParam.shape.courseId
|
|
1024
|
+
});
|
|
1025
|
+
var ZCreateCourseExerciseFromTemplateToolInput = ZExerciseFromTemplate.extend({
|
|
1026
|
+
courseId: ZCourseImportCourseParam.shape.courseId
|
|
1027
|
+
});
|
|
1028
|
+
var ZUpdateCourseExerciseToolInput = ZExerciseUpdate.extend({
|
|
1029
|
+
courseId: ZCourseImportCourseParam.shape.courseId,
|
|
1030
|
+
exerciseId: ZExerciseGetParam.shape.exerciseId
|
|
1031
|
+
});
|
|
740
1032
|
var ZPublishCourseDraftToolInput = ZCourseImportDraftPublish.extend({
|
|
741
1033
|
draftId: ZCourseImportDraftGetParam.shape.draftId
|
|
742
1034
|
});
|
|
@@ -748,44 +1040,156 @@ var createCourseDraftFromCourseShape = ZCourseImportDraftCreateFromCourse.shape;
|
|
|
748
1040
|
var getCourseStructureShape = ZCourseImportCourseParam.shape;
|
|
749
1041
|
var getCourseDraftShape = ZCourseImportDraftGetParam.shape;
|
|
750
1042
|
var updateCourseDraftShape = ZUpdateCourseDraftToolInput.shape;
|
|
1043
|
+
var tagCourseDraftShape = ZTagCourseDraftToolInput.shape;
|
|
1044
|
+
var listCourseExercisesShape = ZListCourseExercisesToolInput.shape;
|
|
1045
|
+
var getCourseExerciseShape = ZGetCourseExerciseToolInput.shape;
|
|
1046
|
+
var createCourseExerciseShape = ZCreateCourseExerciseToolInput.shape;
|
|
1047
|
+
var createCourseExerciseFromTemplateShape = ZCreateCourseExerciseFromTemplateToolInput.shape;
|
|
1048
|
+
var updateCourseExerciseShape = ZUpdateCourseExerciseToolInput.shape;
|
|
751
1049
|
var publishCourseDraftShape = ZPublishCourseDraftToolInput.shape;
|
|
752
1050
|
var publishCourseDraftToExistingCourseShape = ZPublishCourseDraftToExistingCourseToolInput.shape;
|
|
1051
|
+
var tagCoursesShape = ZAutomationCourseTagAssignment.shape;
|
|
753
1052
|
function registerCourseDraftTools(server, apiClient) {
|
|
754
|
-
server.tool(
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
server.tool(
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
server.tool(
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
server.tool(
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
1053
|
+
server.tool(
|
|
1054
|
+
"get_course_structure",
|
|
1055
|
+
"Read the current live course structure. Use this before creating a fresh draft for post-publish edits.",
|
|
1056
|
+
getCourseStructureShape,
|
|
1057
|
+
async (args) => {
|
|
1058
|
+
const { courseId } = ZCourseImportCourseParam.parse(args);
|
|
1059
|
+
const result = await apiClient.getCourseStructure(courseId);
|
|
1060
|
+
return jsonContent(result);
|
|
1061
|
+
}
|
|
1062
|
+
);
|
|
1063
|
+
server.tool(
|
|
1064
|
+
"create_course_draft",
|
|
1065
|
+
"Create a new unpublished course draft from structured course JSON. Use this before the first publish.",
|
|
1066
|
+
createCourseDraftShape,
|
|
1067
|
+
async (args) => {
|
|
1068
|
+
const payload = ZCourseImportDraftCreate.parse(args);
|
|
1069
|
+
const result = await apiClient.createCourseDraft(payload);
|
|
1070
|
+
return jsonContent(result);
|
|
1071
|
+
}
|
|
1072
|
+
);
|
|
1073
|
+
server.tool(
|
|
1074
|
+
"create_course_draft_from_course",
|
|
1075
|
+
"Create a fresh draft from an existing live course. Use this after a course has already been published and the user wants more changes.",
|
|
1076
|
+
createCourseDraftFromCourseShape,
|
|
1077
|
+
async (args) => {
|
|
1078
|
+
const payload = ZCourseImportDraftCreateFromCourse.parse(args);
|
|
1079
|
+
const result = await apiClient.createCourseDraftFromCourse(payload);
|
|
1080
|
+
return jsonContent(result);
|
|
1081
|
+
}
|
|
1082
|
+
);
|
|
1083
|
+
server.tool(
|
|
1084
|
+
"get_course_draft",
|
|
1085
|
+
"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.",
|
|
1086
|
+
getCourseDraftShape,
|
|
1087
|
+
async (args) => {
|
|
1088
|
+
const { draftId } = ZCourseImportDraftGetParam.parse(args);
|
|
1089
|
+
const result = await apiClient.getCourseDraft(draftId);
|
|
1090
|
+
return jsonContent(result);
|
|
1091
|
+
}
|
|
1092
|
+
);
|
|
1093
|
+
server.tool(
|
|
1094
|
+
"list_course_exercises",
|
|
1095
|
+
"List exercises for a live course, optionally filtered by lessonId or sectionId.",
|
|
1096
|
+
listCourseExercisesShape,
|
|
1097
|
+
async (args) => {
|
|
1098
|
+
const { courseId, ...query } = ZListCourseExercisesToolInput.parse(args);
|
|
1099
|
+
const result = await apiClient.listCourseExercises(courseId, query);
|
|
1100
|
+
return jsonContent(result);
|
|
1101
|
+
}
|
|
1102
|
+
);
|
|
1103
|
+
server.tool(
|
|
1104
|
+
"get_course_exercise",
|
|
1105
|
+
"Fetch a single exercise from a live course, including its questions and options.",
|
|
1106
|
+
getCourseExerciseShape,
|
|
1107
|
+
async (args) => {
|
|
1108
|
+
const { courseId, exerciseId } = ZGetCourseExerciseToolInput.parse(args);
|
|
1109
|
+
const result = await apiClient.getCourseExercise(courseId, exerciseId);
|
|
1110
|
+
return jsonContent(result);
|
|
1111
|
+
}
|
|
1112
|
+
);
|
|
1113
|
+
server.tool(
|
|
1114
|
+
"create_course_exercise",
|
|
1115
|
+
"Create a new exercise directly on a live course. Use this for adding exercises after a course has already been published.",
|
|
1116
|
+
createCourseExerciseShape,
|
|
1117
|
+
async (args) => {
|
|
1118
|
+
const { courseId, ...payload } = ZCreateCourseExerciseToolInput.parse(args);
|
|
1119
|
+
const result = await apiClient.createCourseExercise(courseId, payload);
|
|
1120
|
+
return jsonContent(result);
|
|
1121
|
+
}
|
|
1122
|
+
);
|
|
1123
|
+
server.tool(
|
|
1124
|
+
"create_course_exercise_from_template",
|
|
1125
|
+
"Create a new exercise on a live course from an existing template.",
|
|
1126
|
+
createCourseExerciseFromTemplateShape,
|
|
1127
|
+
async (args) => {
|
|
1128
|
+
const { courseId, ...payload } = ZCreateCourseExerciseFromTemplateToolInput.parse(args);
|
|
1129
|
+
const result = await apiClient.createCourseExerciseFromTemplate(courseId, payload);
|
|
1130
|
+
return jsonContent(result);
|
|
1131
|
+
}
|
|
1132
|
+
);
|
|
1133
|
+
server.tool(
|
|
1134
|
+
"update_course_exercise",
|
|
1135
|
+
"Update an existing exercise on a live course, including questions and options.",
|
|
1136
|
+
updateCourseExerciseShape,
|
|
1137
|
+
async (args) => {
|
|
1138
|
+
const { courseId, exerciseId, ...payload } = ZUpdateCourseExerciseToolInput.parse(args);
|
|
1139
|
+
const result = await apiClient.updateCourseExercise(courseId, exerciseId, payload);
|
|
1140
|
+
return jsonContent(result);
|
|
1141
|
+
}
|
|
1142
|
+
);
|
|
1143
|
+
server.tool(
|
|
1144
|
+
"update_course_draft",
|
|
1145
|
+
"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.",
|
|
1146
|
+
updateCourseDraftShape,
|
|
1147
|
+
async (args) => {
|
|
1148
|
+
const { draftId, ...payload } = ZUpdateCourseDraftToolInput.parse(args);
|
|
1149
|
+
const result = await apiClient.updateCourseDraft(draftId, payload);
|
|
1150
|
+
return jsonContent(result);
|
|
1151
|
+
}
|
|
1152
|
+
);
|
|
1153
|
+
server.tool(
|
|
1154
|
+
"tag_course_draft",
|
|
1155
|
+
"Attach human-readable tags to a draft before publish. The tags are stored on the draft and applied during publish.",
|
|
1156
|
+
tagCourseDraftShape,
|
|
1157
|
+
async (args) => {
|
|
1158
|
+
const { draftId, ...payload } = ZTagCourseDraftToolInput.parse(args);
|
|
1159
|
+
const result = await apiClient.tagCourseDraft(draftId, payload);
|
|
1160
|
+
return jsonContent(result);
|
|
1161
|
+
}
|
|
1162
|
+
);
|
|
1163
|
+
server.tool(
|
|
1164
|
+
"publish_course_draft",
|
|
1165
|
+
"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.",
|
|
1166
|
+
publishCourseDraftShape,
|
|
1167
|
+
async (args) => {
|
|
1168
|
+
const { draftId, ...payload } = ZPublishCourseDraftToolInput.parse(args);
|
|
1169
|
+
const result = await apiClient.publishCourseDraft(draftId, payload);
|
|
1170
|
+
return jsonContent(result);
|
|
1171
|
+
}
|
|
1172
|
+
);
|
|
1173
|
+
server.tool(
|
|
1174
|
+
"publish_course_draft_to_existing_course",
|
|
1175
|
+
"Apply a fresh draft back onto an existing live course. Use this after reading the live course and creating a new draft from it.",
|
|
1176
|
+
publishCourseDraftToExistingCourseShape,
|
|
1177
|
+
async (args) => {
|
|
1178
|
+
const { draftId, ...payload } = ZPublishCourseDraftToExistingCourseToolInput.parse(args);
|
|
1179
|
+
const result = await apiClient.publishCourseDraftToExistingCourse(draftId, payload);
|
|
1180
|
+
return jsonContent(result);
|
|
1181
|
+
}
|
|
1182
|
+
);
|
|
1183
|
+
server.tool(
|
|
1184
|
+
"tag_courses",
|
|
1185
|
+
"Assign tags directly to one or more live courses by name. Default mode is merge, so existing tags are preserved unless replace is requested.",
|
|
1186
|
+
tagCoursesShape,
|
|
1187
|
+
async (args) => {
|
|
1188
|
+
const payload = ZAutomationCourseTagAssignment.parse(args);
|
|
1189
|
+
const result = await apiClient.tagCourses(payload);
|
|
1190
|
+
return jsonContent(result);
|
|
1191
|
+
}
|
|
1192
|
+
);
|
|
789
1193
|
}
|
|
790
1194
|
function jsonContent(data) {
|
|
791
1195
|
return {
|