@classroomio/mcp 0.0.6 → 0.0.8
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 +6 -0
- package/dist/index.js +1 -1618
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,1619 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/index.ts
|
|
4
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
-
|
|
7
|
-
// src/api-client.ts
|
|
8
|
-
var ClassroomIoApiError = class extends Error {
|
|
9
|
-
constructor(message, status, code, field) {
|
|
10
|
-
super(message);
|
|
11
|
-
this.status = status;
|
|
12
|
-
this.code = code;
|
|
13
|
-
this.field = field;
|
|
14
|
-
this.name = "ClassroomIoApiError";
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
var ClassroomIoApiClient = class {
|
|
18
|
-
constructor(config) {
|
|
19
|
-
this.config = config;
|
|
20
|
-
}
|
|
21
|
-
async createCourseDraft(payload) {
|
|
22
|
-
return this.request("/organization/course-import/drafts", {
|
|
23
|
-
method: "POST",
|
|
24
|
-
body: payload
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
async createCourseDraftFromCourse(payload) {
|
|
28
|
-
return this.request("/organization/course-import/drafts/from-course", {
|
|
29
|
-
method: "POST",
|
|
30
|
-
body: payload
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
async getCourseStructure(courseId) {
|
|
34
|
-
return this.request(`/organization/course-import/courses/${courseId}/structure`, {
|
|
35
|
-
method: "GET"
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
async updateCourseLandingPage(courseId, payload) {
|
|
39
|
-
return this.request(`/course/${courseId}/landing-page`, {
|
|
40
|
-
method: "PUT",
|
|
41
|
-
body: payload
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
async getCourseDraft(draftId) {
|
|
45
|
-
return this.request(`/organization/course-import/drafts/${draftId}`, {
|
|
46
|
-
method: "GET"
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
async listOrganizationCourses(query = {}) {
|
|
50
|
-
const searchParams = new URLSearchParams();
|
|
51
|
-
if (query.tags) searchParams.set("tags", query.tags);
|
|
52
|
-
const querySuffix = searchParams.toString() ? `?${searchParams.toString()}` : "";
|
|
53
|
-
return this.request(`/organization/courses${querySuffix}`, {
|
|
54
|
-
method: "GET"
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
async listCourseExercises(courseId, query = {}) {
|
|
58
|
-
const searchParams = new URLSearchParams();
|
|
59
|
-
if (query.lessonId) searchParams.set("lessonId", query.lessonId);
|
|
60
|
-
if (query.sectionId) searchParams.set("sectionId", query.sectionId);
|
|
61
|
-
const querySuffix = searchParams.toString() ? `?${searchParams.toString()}` : "";
|
|
62
|
-
return this.request(`/course/${courseId}/exercise${querySuffix}`, {
|
|
63
|
-
method: "GET"
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
async getCourseExercise(courseId, exerciseId) {
|
|
67
|
-
return this.request(`/course/${courseId}/exercise/${exerciseId}`, {
|
|
68
|
-
method: "GET"
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
async createCourseExercise(courseId, payload) {
|
|
72
|
-
return this.request(`/course/${courseId}/exercise`, {
|
|
73
|
-
method: "POST",
|
|
74
|
-
body: {
|
|
75
|
-
...payload,
|
|
76
|
-
courseId
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
async createCourseExerciseFromTemplate(courseId, payload) {
|
|
81
|
-
return this.request(`/course/${courseId}/exercise/from-template`, {
|
|
82
|
-
method: "POST",
|
|
83
|
-
body: payload
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
async updateCourseExercise(courseId, exerciseId, payload) {
|
|
87
|
-
return this.request(`/course/${courseId}/exercise/${exerciseId}`, {
|
|
88
|
-
method: "PUT",
|
|
89
|
-
body: payload
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
async updateCourseDraft(draftId, payload) {
|
|
93
|
-
return this.request(`/organization/course-import/drafts/${draftId}`, {
|
|
94
|
-
method: "PUT",
|
|
95
|
-
body: payload
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
async tagCourseDraft(draftId, payload) {
|
|
99
|
-
return this.request(`/organization/course-import/drafts/${draftId}/tags`, {
|
|
100
|
-
method: "PUT",
|
|
101
|
-
body: payload
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
async publishCourseDraft(draftId, payload) {
|
|
105
|
-
return this.request(`/organization/course-import/drafts/${draftId}/publish`, {
|
|
106
|
-
method: "POST",
|
|
107
|
-
body: payload
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
async publishCourseDraftToExistingCourse(draftId, payload) {
|
|
111
|
-
return this.request(`/organization/course-import/drafts/${draftId}/publish-existing-course`, {
|
|
112
|
-
method: "POST",
|
|
113
|
-
body: payload
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
async tagCourses(payload) {
|
|
117
|
-
return this.request(
|
|
118
|
-
"/organization/tags/courses/assign",
|
|
119
|
-
{
|
|
120
|
-
method: "PUT",
|
|
121
|
-
body: payload
|
|
122
|
-
}
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
async request(path, options) {
|
|
126
|
-
const response = await fetch(new URL(path, this.config.CLASSROOMIO_API_URL), {
|
|
127
|
-
method: options.method,
|
|
128
|
-
headers: {
|
|
129
|
-
Authorization: `Bearer ${this.config.CLASSROOMIO_API_KEY}`,
|
|
130
|
-
"content-type": "application/json",
|
|
131
|
-
"user-agent": this.config.CLASSROOMIO_USER_AGENT
|
|
132
|
-
},
|
|
133
|
-
body: options.body ? JSON.stringify(options.body) : void 0
|
|
134
|
-
});
|
|
135
|
-
const json = await response.json().catch(() => null);
|
|
136
|
-
if (!response.ok) {
|
|
137
|
-
const errorPayload = json;
|
|
138
|
-
throw new ClassroomIoApiError(
|
|
139
|
-
errorPayload?.error ?? errorPayload?.message ?? `ClassroomIO request failed with status ${response.status}`,
|
|
140
|
-
response.status,
|
|
141
|
-
errorPayload?.code,
|
|
142
|
-
errorPayload?.field
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
if (!json || typeof json !== "object" || !("success" in json) || !json.success) {
|
|
146
|
-
throw new ClassroomIoApiError("ClassroomIO returned an invalid response payload", response.status);
|
|
147
|
-
}
|
|
148
|
-
return json.data;
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
// src/config.ts
|
|
153
|
-
import { z } from "zod";
|
|
154
|
-
var ZConfig = z.object({
|
|
155
|
-
CLASSROOMIO_API_URL: z.url().default("https://api.classroomio.com"),
|
|
156
|
-
CLASSROOMIO_API_KEY: z.string().min(1),
|
|
157
|
-
CLASSROOMIO_USER_AGENT: z.string().min(1).default("classroomio-mcp/0.0.1")
|
|
158
|
-
});
|
|
159
|
-
function getConfig(env = process.env) {
|
|
160
|
-
return ZConfig.parse(env);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// ../utils/dist/validation/tag/tag.js
|
|
164
|
-
import * as z2 from "zod";
|
|
165
|
-
var TAG_COLOR_OPTIONS = [
|
|
166
|
-
"#EF4444",
|
|
167
|
-
"#F97316",
|
|
168
|
-
"#F59E0B",
|
|
169
|
-
"#84CC16",
|
|
170
|
-
"#22C55E",
|
|
171
|
-
"#14B8A6",
|
|
172
|
-
"#06B6D4",
|
|
173
|
-
"#3B82F6",
|
|
174
|
-
"#8B5CF6",
|
|
175
|
-
"#EC4899"
|
|
176
|
-
];
|
|
177
|
-
var ZTagColor = z2.enum(TAG_COLOR_OPTIONS);
|
|
178
|
-
var ZTagGroupParam = z2.object({
|
|
179
|
-
groupId: z2.uuid()
|
|
180
|
-
});
|
|
181
|
-
var ZTagGroupCreate = z2.object({
|
|
182
|
-
name: z2.string().min(1).max(80),
|
|
183
|
-
description: z2.string().max(240).optional(),
|
|
184
|
-
order: z2.number().int().min(0).optional()
|
|
185
|
-
});
|
|
186
|
-
var ZTagGroupUpdate = ZTagGroupCreate.partial().refine((value) => Object.keys(value).length > 0, {
|
|
187
|
-
message: "At least one field is required"
|
|
188
|
-
});
|
|
189
|
-
var ZTagParam = z2.object({
|
|
190
|
-
tagId: z2.uuid()
|
|
191
|
-
});
|
|
192
|
-
var ZTagCreate = z2.object({
|
|
193
|
-
name: z2.string().min(1).max(80),
|
|
194
|
-
description: z2.string().max(240).optional(),
|
|
195
|
-
groupId: z2.uuid(),
|
|
196
|
-
color: ZTagColor
|
|
197
|
-
});
|
|
198
|
-
var ZTagUpdate = z2.object({
|
|
199
|
-
name: z2.string().min(1).max(80).optional(),
|
|
200
|
-
description: z2.string().max(240).optional(),
|
|
201
|
-
groupId: z2.uuid().optional(),
|
|
202
|
-
color: ZTagColor.optional()
|
|
203
|
-
}).refine((value) => Object.keys(value).length > 0, {
|
|
204
|
-
message: "At least one field is required"
|
|
205
|
-
});
|
|
206
|
-
var ZCourseTagParam = z2.object({
|
|
207
|
-
courseId: z2.uuid()
|
|
208
|
-
});
|
|
209
|
-
var ZCourseTagAssignment = z2.object({
|
|
210
|
-
tagIds: z2.array(z2.uuid()).max(100).default([])
|
|
211
|
-
});
|
|
212
|
-
var ZAutomationTagMode = z2.enum(["merge", "replace"]).default("merge");
|
|
213
|
-
var ZAutomationTagName = z2.string().trim().min(1).max(80);
|
|
214
|
-
var ZAutomationDraftTagParam = z2.object({
|
|
215
|
-
draftId: z2.string().uuid()
|
|
216
|
-
});
|
|
217
|
-
var ZAutomationDraftTagAssignment = z2.object({
|
|
218
|
-
tagNames: z2.array(ZAutomationTagName).min(1).max(100),
|
|
219
|
-
mode: ZAutomationTagMode,
|
|
220
|
-
groupName: z2.string().trim().min(1).max(80).optional()
|
|
221
|
-
});
|
|
222
|
-
var ZAutomationCourseTagAssignment = ZAutomationDraftTagAssignment.extend({
|
|
223
|
-
courseIds: z2.array(z2.string().uuid()).min(1).max(100)
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
// ../utils/dist/validation/organization/organization.js
|
|
227
|
-
import * as z3 from "zod";
|
|
228
|
-
|
|
229
|
-
// ../utils/dist/constants/org.js
|
|
230
|
-
var blockedSubdomain = [
|
|
231
|
-
"academy",
|
|
232
|
-
"app",
|
|
233
|
-
"apps",
|
|
234
|
-
"blog",
|
|
235
|
-
"build",
|
|
236
|
-
"campaign",
|
|
237
|
-
"church",
|
|
238
|
-
"cloud",
|
|
239
|
-
"community",
|
|
240
|
-
"conference",
|
|
241
|
-
"course",
|
|
242
|
-
"courses",
|
|
243
|
-
"demo",
|
|
244
|
-
"dev",
|
|
245
|
-
"forum",
|
|
246
|
-
"group",
|
|
247
|
-
"groups",
|
|
248
|
-
"help",
|
|
249
|
-
"launch",
|
|
250
|
-
"launchweek",
|
|
251
|
-
"mobile",
|
|
252
|
-
"play",
|
|
253
|
-
"prod",
|
|
254
|
-
"production",
|
|
255
|
-
"schools",
|
|
256
|
-
"stage",
|
|
257
|
-
"staging",
|
|
258
|
-
"support",
|
|
259
|
-
"teacher",
|
|
260
|
-
"teachers",
|
|
261
|
-
"tech",
|
|
262
|
-
"training"
|
|
263
|
-
];
|
|
264
|
-
|
|
265
|
-
// ../utils/dist/constants/content.js
|
|
266
|
-
var ContentType;
|
|
267
|
-
(function(ContentType2) {
|
|
268
|
-
ContentType2["Section"] = "SECTION";
|
|
269
|
-
ContentType2["Lesson"] = "LESSON";
|
|
270
|
-
ContentType2["Exercise"] = "EXERCISE";
|
|
271
|
-
})(ContentType || (ContentType = {}));
|
|
272
|
-
|
|
273
|
-
// ../utils/dist/validation/organization/organization.js
|
|
274
|
-
var ZGetOrganizations = z3.object({
|
|
275
|
-
siteName: z3.string().min(1).optional(),
|
|
276
|
-
customDomain: z3.string().min(1).optional(),
|
|
277
|
-
isCustomDomainVerified: z3.boolean().optional()
|
|
278
|
-
});
|
|
279
|
-
var ZGetCoursesBySiteName = z3.object({
|
|
280
|
-
siteName: z3.string().min(1),
|
|
281
|
-
tags: z3.string().optional()
|
|
282
|
-
});
|
|
283
|
-
var ZGetOrganizationCoursesQuery = z3.object({
|
|
284
|
-
tags: z3.string().optional()
|
|
285
|
-
});
|
|
286
|
-
var ZGetOrgSetup = z3.object({
|
|
287
|
-
siteName: z3.string().min(1)
|
|
288
|
-
});
|
|
289
|
-
var ZCreateOrgPlan = z3.object({
|
|
290
|
-
orgId: z3.uuid(),
|
|
291
|
-
planName: z3.enum(["EARLY_ADOPTER", "ENTERPRISE", "BASIC"]),
|
|
292
|
-
subscriptionId: z3.string().min(1),
|
|
293
|
-
triggeredBy: z3.number().int().positive(),
|
|
294
|
-
payload: z3.record(z3.string(), z3.unknown())
|
|
295
|
-
});
|
|
296
|
-
var ZUpdateOrgPlan = z3.object({
|
|
297
|
-
subscriptionId: z3.string().min(1),
|
|
298
|
-
payload: z3.record(z3.string(), z3.unknown())
|
|
299
|
-
});
|
|
300
|
-
var ZCancelOrgPlan = z3.object({
|
|
301
|
-
subscriptionId: z3.string().min(1),
|
|
302
|
-
payload: z3.record(z3.string(), z3.unknown())
|
|
303
|
-
});
|
|
304
|
-
var ZCreateOrganization = z3.object({
|
|
305
|
-
name: z3.string().min(5).refine((val) => !/^[-]|[-]$/.test(val), {
|
|
306
|
-
message: "Organization name cannot start or end with a hyphen"
|
|
307
|
-
}),
|
|
308
|
-
siteName: z3.string().min(5).refine((val) => !/^[-]|[-]$/.test(val), {
|
|
309
|
-
message: "Site name cannot start or end with a hyphen"
|
|
310
|
-
}).refine((val) => !blockedSubdomain.includes(val), {
|
|
311
|
-
message: "Sitename already exists."
|
|
312
|
-
})
|
|
313
|
-
});
|
|
314
|
-
var ZUpdateOrganization = z3.object({
|
|
315
|
-
name: z3.string().min(5).refine((val) => !/^[-]|[-]$/.test(val), {
|
|
316
|
-
message: "Organization name cannot start or end with a hyphen"
|
|
317
|
-
}).optional(),
|
|
318
|
-
avatarUrl: z3.url().optional(),
|
|
319
|
-
theme: z3.string().optional(),
|
|
320
|
-
landingpage: z3.record(z3.string(), z3.unknown()).optional(),
|
|
321
|
-
siteName: z3.string().min(1).optional(),
|
|
322
|
-
customDomain: z3.string().nullable().optional(),
|
|
323
|
-
isCustomDomainVerified: z3.boolean().optional(),
|
|
324
|
-
customization: z3.record(z3.string(), z3.unknown()).optional(),
|
|
325
|
-
disableSignup: z3.boolean().optional(),
|
|
326
|
-
disableSignupMessage: z3.string().optional(),
|
|
327
|
-
disableEmailPassword: z3.boolean().optional(),
|
|
328
|
-
disableGoogleAuth: z3.boolean().optional(),
|
|
329
|
-
/** Nested settings (stored in organization.settings JSONB). signup.inviteOnly = true means invite-only signup. */
|
|
330
|
-
settings: z3.object({
|
|
331
|
-
signup: z3.object({
|
|
332
|
-
inviteOnly: z3.boolean().optional()
|
|
333
|
-
}).optional()
|
|
334
|
-
}).optional()
|
|
335
|
-
});
|
|
336
|
-
var ZInviteTeamMembers = z3.object({
|
|
337
|
-
emails: z3.array(z3.string().email()).min(1).max(50),
|
|
338
|
-
roleId: z3.number().int().positive()
|
|
339
|
-
});
|
|
340
|
-
var ZRemoveTeamMember = z3.object({
|
|
341
|
-
memberId: z3.coerce.number().int().positive()
|
|
342
|
-
});
|
|
343
|
-
var ZGetUserAnalytics = z3.object({
|
|
344
|
-
userId: z3.uuid()
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
// ../utils/dist/validation/organization/quiz.js
|
|
348
|
-
import * as z4 from "zod";
|
|
349
|
-
var ZQuizGetParam = z4.object({
|
|
350
|
-
orgId: z4.uuid(),
|
|
351
|
-
quizId: z4.uuid()
|
|
352
|
-
});
|
|
353
|
-
var ZQuizListParam = z4.object({
|
|
354
|
-
orgId: z4.uuid()
|
|
355
|
-
});
|
|
356
|
-
var ZQuizCreate = z4.object({
|
|
357
|
-
title: z4.string().min(1),
|
|
358
|
-
questions: z4.array(z4.any()).optional(),
|
|
359
|
-
timelimit: z4.string().optional(),
|
|
360
|
-
theme: z4.string().optional()
|
|
361
|
-
});
|
|
362
|
-
var ZQuizUpdate = z4.object({
|
|
363
|
-
title: z4.string().min(1).optional(),
|
|
364
|
-
questions: z4.array(z4.any()).optional(),
|
|
365
|
-
timelimit: z4.string().optional(),
|
|
366
|
-
theme: z4.string().optional()
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
// ../utils/dist/validation/organization/exercise.js
|
|
370
|
-
import * as z5 from "zod";
|
|
371
|
-
var ZLMSExercisesParam = z5.object({
|
|
372
|
-
orgId: z5.uuid()
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
// ../utils/dist/validation/organization/invite.js
|
|
376
|
-
import * as z6 from "zod";
|
|
377
|
-
var ZOrganizationInviteTokenParam = z6.object({
|
|
378
|
-
token: z6.string().min(10).max(512)
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
// ../utils/dist/validation/organization/sso.js
|
|
382
|
-
import * as z7 from "zod";
|
|
383
|
-
var SSO_PROVIDERS = ["OKTA", "GOOGLE_WORKSPACE", "AUTH0"];
|
|
384
|
-
var ZCreateSsoConnection = z7.object({
|
|
385
|
-
provider: z7.enum(SSO_PROVIDERS),
|
|
386
|
-
displayName: z7.string().min(1, "Display name is required"),
|
|
387
|
-
issuer: z7.url("Must be a valid URL"),
|
|
388
|
-
domain: z7.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"),
|
|
389
|
-
clientId: z7.string().min(1, "Client ID is required"),
|
|
390
|
-
clientSecret: z7.string().min(1, "Client secret is required"),
|
|
391
|
-
scopes: z7.string().default("openid profile email")
|
|
392
|
-
});
|
|
393
|
-
var ZUpdateSsoConnection = z7.object({
|
|
394
|
-
displayName: z7.string().min(1).optional(),
|
|
395
|
-
issuer: z7.url().optional(),
|
|
396
|
-
domain: z7.string().optional(),
|
|
397
|
-
clientId: z7.string().optional(),
|
|
398
|
-
clientSecret: z7.string().optional(),
|
|
399
|
-
scopes: z7.string().optional(),
|
|
400
|
-
isActive: z7.boolean().optional()
|
|
401
|
-
});
|
|
402
|
-
var ZUpdateSsoPolicy = z7.object({
|
|
403
|
-
forceSso: z7.boolean().optional(),
|
|
404
|
-
autoJoinSsoDomains: z7.boolean().optional(),
|
|
405
|
-
breakGlassEnabled: z7.boolean().optional(),
|
|
406
|
-
defaultRoleId: z7.number().int().positive().optional(),
|
|
407
|
-
roleMapping: z7.record(z7.string(), z7.number()).optional()
|
|
408
|
-
});
|
|
409
|
-
var ZSsoDiscoveryQuery = z7.object({
|
|
410
|
-
email: z7.email("Valid email is required")
|
|
411
|
-
});
|
|
412
|
-
var ZGetSsoConfig = z7.object({
|
|
413
|
-
orgId: z7.uuid()
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
// ../utils/dist/validation/organization/login-link.js
|
|
417
|
-
import { z as z8 } from "zod";
|
|
418
|
-
var ZLoginLinkQuery = z8.object({
|
|
419
|
-
token: z8.string().min(1),
|
|
420
|
-
redirect: z8.string().optional()
|
|
421
|
-
});
|
|
422
|
-
var ZLoginLinkPayload = z8.object({
|
|
423
|
-
sub: z8.uuid(),
|
|
424
|
-
email: z8.email(),
|
|
425
|
-
type: z8.literal("login-link")
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
// ../utils/dist/validation/organization/token-auth.js
|
|
429
|
-
import { z as z9 } from "zod";
|
|
430
|
-
var ZTokenExchangeQuery = z9.object({
|
|
431
|
-
token: z9.string().min(1),
|
|
432
|
-
redirect: z9.string().optional()
|
|
433
|
-
});
|
|
434
|
-
var ZTokenExchangePayload = z9.object({
|
|
435
|
-
sub: z9.string(),
|
|
436
|
-
email: z9.email(),
|
|
437
|
-
name: z9.string().optional(),
|
|
438
|
-
avatar: z9.url().optional(),
|
|
439
|
-
iat: z9.number(),
|
|
440
|
-
exp: z9.number()
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
// ../utils/dist/validation/organization/automation-key.js
|
|
444
|
-
import { z as z10 } from "zod";
|
|
445
|
-
var ZOrganizationApiKeyType = z10.enum(["mcp", "api", "zapier"]);
|
|
446
|
-
var ZOrganizationApiKeyScope = z10.enum([
|
|
447
|
-
"course_import:draft:create",
|
|
448
|
-
"course_import:draft:read",
|
|
449
|
-
"course_import:draft:update",
|
|
450
|
-
"course_import:draft:publish",
|
|
451
|
-
"course:read",
|
|
452
|
-
"course:write",
|
|
453
|
-
"course:tag:write",
|
|
454
|
-
"course:exercise:read",
|
|
455
|
-
"course:exercise:write",
|
|
456
|
-
"public_api:*"
|
|
457
|
-
]);
|
|
458
|
-
var ZListOrganizationApiKeysQuery = z10.object({
|
|
459
|
-
type: ZOrganizationApiKeyType.optional()
|
|
460
|
-
});
|
|
461
|
-
var ZOrganizationAutomationUsageQuery = z10.object({
|
|
462
|
-
type: ZOrganizationApiKeyType.default("mcp")
|
|
463
|
-
});
|
|
464
|
-
var ZCreateOrganizationApiKey = z10.object({
|
|
465
|
-
type: ZOrganizationApiKeyType,
|
|
466
|
-
label: z10.string().min(1).max(120).optional(),
|
|
467
|
-
expiresAt: z10.string().datetime().optional(),
|
|
468
|
-
scopes: z10.array(ZOrganizationApiKeyScope).min(1).optional()
|
|
469
|
-
});
|
|
470
|
-
var ZOrganizationApiKeyParam = z10.object({
|
|
471
|
-
keyId: z10.string().uuid()
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
// ../utils/dist/validation/exercise/exercise.js
|
|
475
|
-
import * as z11 from "zod";
|
|
476
|
-
|
|
477
|
-
// ../question-types/dist/question-type-keys.js
|
|
478
|
-
var QUESTION_TYPE_KEY = {
|
|
479
|
-
RADIO: "RADIO",
|
|
480
|
-
CHECKBOX: "CHECKBOX",
|
|
481
|
-
TEXTAREA: "TEXTAREA",
|
|
482
|
-
TRUE_FALSE: "TRUE_FALSE",
|
|
483
|
-
SHORT_ANSWER: "SHORT_ANSWER",
|
|
484
|
-
NUMERIC: "NUMERIC",
|
|
485
|
-
FILL_BLANK: "FILL_BLANK",
|
|
486
|
-
FILE_UPLOAD: "FILE_UPLOAD",
|
|
487
|
-
MATCHING: "MATCHING",
|
|
488
|
-
ORDERING: "ORDERING",
|
|
489
|
-
HOTSPOT: "HOTSPOT",
|
|
490
|
-
LINK: "LINK"
|
|
491
|
-
};
|
|
492
|
-
var LEGACY_QUESTION_TYPE_KEYS = [
|
|
493
|
-
QUESTION_TYPE_KEY.RADIO,
|
|
494
|
-
QUESTION_TYPE_KEY.CHECKBOX,
|
|
495
|
-
QUESTION_TYPE_KEY.TEXTAREA
|
|
496
|
-
];
|
|
497
|
-
|
|
498
|
-
// ../question-types/dist/question-type-registry.js
|
|
499
|
-
var QUESTION_TYPE_REGISTRY = [
|
|
500
|
-
{
|
|
501
|
-
key: QUESTION_TYPE_KEY.RADIO,
|
|
502
|
-
typename: "RADIO",
|
|
503
|
-
label: "Single answer",
|
|
504
|
-
id: 1,
|
|
505
|
-
autoGradable: true,
|
|
506
|
-
supportsPartialCredit: false,
|
|
507
|
-
manualGradingRequired: false
|
|
508
|
-
},
|
|
509
|
-
{
|
|
510
|
-
key: QUESTION_TYPE_KEY.CHECKBOX,
|
|
511
|
-
typename: "CHECKBOX",
|
|
512
|
-
label: "Multiple answers",
|
|
513
|
-
id: 2,
|
|
514
|
-
autoGradable: true,
|
|
515
|
-
supportsPartialCredit: true,
|
|
516
|
-
manualGradingRequired: false
|
|
517
|
-
},
|
|
518
|
-
{
|
|
519
|
-
key: QUESTION_TYPE_KEY.TEXTAREA,
|
|
520
|
-
typename: "TEXTAREA",
|
|
521
|
-
label: "Paragraph",
|
|
522
|
-
id: 3,
|
|
523
|
-
autoGradable: false,
|
|
524
|
-
supportsPartialCredit: false,
|
|
525
|
-
manualGradingRequired: true
|
|
526
|
-
},
|
|
527
|
-
{
|
|
528
|
-
key: QUESTION_TYPE_KEY.TRUE_FALSE,
|
|
529
|
-
typename: "TRUE_FALSE",
|
|
530
|
-
label: "True/False",
|
|
531
|
-
id: 4,
|
|
532
|
-
autoGradable: true,
|
|
533
|
-
supportsPartialCredit: false,
|
|
534
|
-
manualGradingRequired: false
|
|
535
|
-
},
|
|
536
|
-
{
|
|
537
|
-
key: QUESTION_TYPE_KEY.SHORT_ANSWER,
|
|
538
|
-
typename: "SHORT_ANSWER",
|
|
539
|
-
label: "Short answer",
|
|
540
|
-
id: 5,
|
|
541
|
-
autoGradable: false,
|
|
542
|
-
supportsPartialCredit: false,
|
|
543
|
-
manualGradingRequired: true
|
|
544
|
-
},
|
|
545
|
-
{
|
|
546
|
-
key: QUESTION_TYPE_KEY.NUMERIC,
|
|
547
|
-
typename: "NUMERIC",
|
|
548
|
-
label: "Numeric answer",
|
|
549
|
-
id: 6,
|
|
550
|
-
autoGradable: true,
|
|
551
|
-
supportsPartialCredit: true,
|
|
552
|
-
manualGradingRequired: false
|
|
553
|
-
},
|
|
554
|
-
{
|
|
555
|
-
key: QUESTION_TYPE_KEY.FILL_BLANK,
|
|
556
|
-
typename: "FILL_BLANK",
|
|
557
|
-
label: "Fill in the blank",
|
|
558
|
-
id: 7,
|
|
559
|
-
autoGradable: true,
|
|
560
|
-
supportsPartialCredit: true,
|
|
561
|
-
manualGradingRequired: false
|
|
562
|
-
},
|
|
563
|
-
{
|
|
564
|
-
key: QUESTION_TYPE_KEY.FILE_UPLOAD,
|
|
565
|
-
typename: "FILE_UPLOAD",
|
|
566
|
-
label: "File upload",
|
|
567
|
-
id: 8,
|
|
568
|
-
autoGradable: false,
|
|
569
|
-
supportsPartialCredit: false,
|
|
570
|
-
manualGradingRequired: true
|
|
571
|
-
},
|
|
572
|
-
{
|
|
573
|
-
key: QUESTION_TYPE_KEY.MATCHING,
|
|
574
|
-
typename: "MATCHING",
|
|
575
|
-
label: "Matching",
|
|
576
|
-
id: 9,
|
|
577
|
-
autoGradable: true,
|
|
578
|
-
supportsPartialCredit: true,
|
|
579
|
-
manualGradingRequired: false
|
|
580
|
-
},
|
|
581
|
-
{
|
|
582
|
-
key: QUESTION_TYPE_KEY.ORDERING,
|
|
583
|
-
typename: "ORDERING",
|
|
584
|
-
label: "Ordering",
|
|
585
|
-
id: 10,
|
|
586
|
-
autoGradable: true,
|
|
587
|
-
supportsPartialCredit: true,
|
|
588
|
-
manualGradingRequired: false
|
|
589
|
-
},
|
|
590
|
-
{
|
|
591
|
-
key: QUESTION_TYPE_KEY.HOTSPOT,
|
|
592
|
-
typename: "HOTSPOT",
|
|
593
|
-
label: "Hotspot",
|
|
594
|
-
id: 11,
|
|
595
|
-
autoGradable: true,
|
|
596
|
-
supportsPartialCredit: true,
|
|
597
|
-
manualGradingRequired: false
|
|
598
|
-
},
|
|
599
|
-
{
|
|
600
|
-
key: QUESTION_TYPE_KEY.LINK,
|
|
601
|
-
typename: "LINK",
|
|
602
|
-
label: "Links",
|
|
603
|
-
id: 12,
|
|
604
|
-
autoGradable: false,
|
|
605
|
-
supportsPartialCredit: false,
|
|
606
|
-
manualGradingRequired: true
|
|
607
|
-
}
|
|
608
|
-
];
|
|
609
|
-
var QUESTION_TYPE_BY_KEY = Object.freeze(QUESTION_TYPE_REGISTRY.reduce((acc, type) => {
|
|
610
|
-
acc[type.key] = type;
|
|
611
|
-
return acc;
|
|
612
|
-
}, {}));
|
|
613
|
-
var QUESTION_TYPE_IDS = Object.freeze(QUESTION_TYPE_REGISTRY.reduce((acc, type) => {
|
|
614
|
-
acc[type.key] = type.id;
|
|
615
|
-
return acc;
|
|
616
|
-
}, {}));
|
|
617
|
-
var QUESTION_TYPE_TYPENAME_TO_KEY = Object.freeze(QUESTION_TYPE_REGISTRY.reduce((acc, type) => {
|
|
618
|
-
acc[type.typename] = type.key;
|
|
619
|
-
return acc;
|
|
620
|
-
}, {}));
|
|
621
|
-
var QUESTION_TYPE_ID_TO_KEY = Object.freeze(QUESTION_TYPE_REGISTRY.reduce((acc, type) => {
|
|
622
|
-
acc[type.id] = type.key;
|
|
623
|
-
return acc;
|
|
624
|
-
}, {}));
|
|
625
|
-
|
|
626
|
-
// ../question-types/dist/answer-codecs.js
|
|
627
|
-
var TRUE_LABELS = /* @__PURE__ */ new Set(["true", "1", "yes"]);
|
|
628
|
-
var FALSE_LABELS = /* @__PURE__ */ new Set(["false", "0", "no"]);
|
|
629
|
-
function labelToBoolean(label) {
|
|
630
|
-
const normalized = label.trim().toLowerCase();
|
|
631
|
-
if (TRUE_LABELS.has(normalized))
|
|
632
|
-
return true;
|
|
633
|
-
if (FALSE_LABELS.has(normalized))
|
|
634
|
-
return false;
|
|
635
|
-
return void 0;
|
|
636
|
-
}
|
|
637
|
-
var RADIO_CODEC = {
|
|
638
|
-
type: "RADIO",
|
|
639
|
-
toApiPayload(data, questionId) {
|
|
640
|
-
return { questionId, optionId: data.optionId };
|
|
641
|
-
},
|
|
642
|
-
fromApiPayload(payload) {
|
|
643
|
-
const optionId = payload.optionId;
|
|
644
|
-
if (optionId === void 0 || Number.isNaN(optionId))
|
|
645
|
-
return null;
|
|
646
|
-
return { type: "RADIO", optionId };
|
|
647
|
-
}
|
|
648
|
-
};
|
|
649
|
-
var CHECKBOX_CODEC = {
|
|
650
|
-
type: "CHECKBOX",
|
|
651
|
-
toApiPayload(data, questionId) {
|
|
652
|
-
return {
|
|
653
|
-
questionId,
|
|
654
|
-
answer: JSON.stringify({ type: "CHECKBOX", optionIds: data.optionIds })
|
|
655
|
-
};
|
|
656
|
-
},
|
|
657
|
-
fromApiPayload(payload) {
|
|
658
|
-
if (!payload.answer)
|
|
659
|
-
return null;
|
|
660
|
-
const { optionIds } = JSON.parse(payload.answer);
|
|
661
|
-
if (!Array.isArray(optionIds))
|
|
662
|
-
return null;
|
|
663
|
-
return { type: "CHECKBOX", optionIds };
|
|
664
|
-
}
|
|
665
|
-
};
|
|
666
|
-
var TRUE_FALSE_CODEC = {
|
|
667
|
-
type: "TRUE_FALSE",
|
|
668
|
-
toApiPayload(data, questionId) {
|
|
669
|
-
return { questionId, answer: String(data.value) };
|
|
670
|
-
},
|
|
671
|
-
fromApiPayload(payload) {
|
|
672
|
-
if (payload.answer === void 0)
|
|
673
|
-
return null;
|
|
674
|
-
const resolved = labelToBoolean(payload.answer);
|
|
675
|
-
if (resolved === void 0)
|
|
676
|
-
return null;
|
|
677
|
-
return { type: "TRUE_FALSE", value: resolved };
|
|
678
|
-
}
|
|
679
|
-
};
|
|
680
|
-
var TEXTAREA_CODEC = {
|
|
681
|
-
type: "TEXTAREA",
|
|
682
|
-
toApiPayload(data, questionId) {
|
|
683
|
-
return { questionId, answer: data.text };
|
|
684
|
-
},
|
|
685
|
-
fromApiPayload(payload) {
|
|
686
|
-
if (payload.answer === void 0)
|
|
687
|
-
return null;
|
|
688
|
-
return { type: "TEXTAREA", text: payload.answer };
|
|
689
|
-
}
|
|
690
|
-
};
|
|
691
|
-
var SHORT_ANSWER_CODEC = {
|
|
692
|
-
type: "SHORT_ANSWER",
|
|
693
|
-
toApiPayload(data, questionId) {
|
|
694
|
-
return { questionId, answer: data.text };
|
|
695
|
-
},
|
|
696
|
-
fromApiPayload(payload) {
|
|
697
|
-
if (payload.answer === void 0)
|
|
698
|
-
return null;
|
|
699
|
-
return { type: "SHORT_ANSWER", text: payload.answer };
|
|
700
|
-
}
|
|
701
|
-
};
|
|
702
|
-
var NUMERIC_CODEC = {
|
|
703
|
-
type: "NUMERIC",
|
|
704
|
-
toApiPayload(data, questionId) {
|
|
705
|
-
return { questionId, answer: JSON.stringify({ type: "NUMERIC", value: data.value }) };
|
|
706
|
-
},
|
|
707
|
-
fromApiPayload(payload) {
|
|
708
|
-
if (!payload.answer)
|
|
709
|
-
return null;
|
|
710
|
-
const { value } = JSON.parse(payload.answer);
|
|
711
|
-
return { type: "NUMERIC", value };
|
|
712
|
-
}
|
|
713
|
-
};
|
|
714
|
-
var FILL_BLANK_CODEC = {
|
|
715
|
-
type: "FILL_BLANK",
|
|
716
|
-
toApiPayload(data, questionId) {
|
|
717
|
-
return { questionId, answer: JSON.stringify({ type: "FILL_BLANK", answers: data.values }) };
|
|
718
|
-
},
|
|
719
|
-
fromApiPayload(payload) {
|
|
720
|
-
if (!payload.answer)
|
|
721
|
-
return null;
|
|
722
|
-
const { answers } = JSON.parse(payload.answer);
|
|
723
|
-
const values = answers.map((v) => String(v).trim()).filter(Boolean);
|
|
724
|
-
return values.length > 0 ? { type: "FILL_BLANK", values } : null;
|
|
725
|
-
}
|
|
726
|
-
};
|
|
727
|
-
var FILE_UPLOAD_CODEC = {
|
|
728
|
-
type: "FILE_UPLOAD",
|
|
729
|
-
toApiPayload(data, questionId) {
|
|
730
|
-
const obj = { fileKey: data.fileKey, fileName: data.fileName, mimeType: data.mimeType, size: data.size };
|
|
731
|
-
return { questionId, answer: JSON.stringify(obj) };
|
|
732
|
-
},
|
|
733
|
-
fromApiPayload(payload) {
|
|
734
|
-
if (!payload.answer)
|
|
735
|
-
return null;
|
|
736
|
-
const p = JSON.parse(payload.answer);
|
|
737
|
-
return {
|
|
738
|
-
type: "FILE_UPLOAD",
|
|
739
|
-
fileKey: p.fileKey,
|
|
740
|
-
fileName: p.fileName ?? "",
|
|
741
|
-
mimeType: p.mimeType,
|
|
742
|
-
size: p.size
|
|
743
|
-
};
|
|
744
|
-
}
|
|
745
|
-
};
|
|
746
|
-
var MATCHING_CODEC = {
|
|
747
|
-
type: "MATCHING",
|
|
748
|
-
toApiPayload(data, questionId) {
|
|
749
|
-
return { questionId, answer: JSON.stringify({ type: "MATCHING", value: data.pairs }) };
|
|
750
|
-
},
|
|
751
|
-
fromApiPayload(payload) {
|
|
752
|
-
if (!payload.answer)
|
|
753
|
-
return null;
|
|
754
|
-
const { value: pairs } = JSON.parse(payload.answer);
|
|
755
|
-
return pairs.length > 0 ? { type: "MATCHING", pairs } : null;
|
|
756
|
-
}
|
|
757
|
-
};
|
|
758
|
-
var ORDERING_CODEC = {
|
|
759
|
-
type: "ORDERING",
|
|
760
|
-
toApiPayload(data, questionId) {
|
|
761
|
-
return { questionId, answer: JSON.stringify({ type: "ORDERING", value: data.orderedValues }) };
|
|
762
|
-
},
|
|
763
|
-
fromApiPayload(payload) {
|
|
764
|
-
if (!payload.answer)
|
|
765
|
-
return null;
|
|
766
|
-
const { value: orderedValues } = JSON.parse(payload.answer);
|
|
767
|
-
return orderedValues.length > 0 ? { type: "ORDERING", orderedValues } : null;
|
|
768
|
-
}
|
|
769
|
-
};
|
|
770
|
-
var LINK_CODEC = {
|
|
771
|
-
type: "LINK",
|
|
772
|
-
toApiPayload(data, questionId) {
|
|
773
|
-
return { questionId, answer: JSON.stringify({ type: "LINK", links: data.urls }) };
|
|
774
|
-
},
|
|
775
|
-
fromApiPayload(payload) {
|
|
776
|
-
if (!payload.answer)
|
|
777
|
-
return null;
|
|
778
|
-
const { links } = JSON.parse(payload.answer);
|
|
779
|
-
const urls = links.map((v) => String(v).trim()).filter(Boolean);
|
|
780
|
-
return urls.length > 0 ? { type: "LINK", urls } : null;
|
|
781
|
-
}
|
|
782
|
-
};
|
|
783
|
-
var HOTSPOT_CODEC = {
|
|
784
|
-
type: "HOTSPOT",
|
|
785
|
-
toApiPayload(data, questionId) {
|
|
786
|
-
return { questionId, answer: JSON.stringify({ type: "HOTSPOT", value: data.coordinates }) };
|
|
787
|
-
},
|
|
788
|
-
fromApiPayload(payload) {
|
|
789
|
-
if (!payload.answer)
|
|
790
|
-
return null;
|
|
791
|
-
const { value: coordinates } = JSON.parse(payload.answer);
|
|
792
|
-
return coordinates.length > 0 ? { type: "HOTSPOT", coordinates } : null;
|
|
793
|
-
}
|
|
794
|
-
};
|
|
795
|
-
var ANSWER_CODECS = {
|
|
796
|
-
[QUESTION_TYPE_KEY.RADIO]: RADIO_CODEC,
|
|
797
|
-
[QUESTION_TYPE_KEY.CHECKBOX]: CHECKBOX_CODEC,
|
|
798
|
-
[QUESTION_TYPE_KEY.TRUE_FALSE]: TRUE_FALSE_CODEC,
|
|
799
|
-
[QUESTION_TYPE_KEY.TEXTAREA]: TEXTAREA_CODEC,
|
|
800
|
-
[QUESTION_TYPE_KEY.SHORT_ANSWER]: SHORT_ANSWER_CODEC,
|
|
801
|
-
[QUESTION_TYPE_KEY.NUMERIC]: NUMERIC_CODEC,
|
|
802
|
-
[QUESTION_TYPE_KEY.FILL_BLANK]: FILL_BLANK_CODEC,
|
|
803
|
-
[QUESTION_TYPE_KEY.FILE_UPLOAD]: FILE_UPLOAD_CODEC,
|
|
804
|
-
[QUESTION_TYPE_KEY.MATCHING]: MATCHING_CODEC,
|
|
805
|
-
[QUESTION_TYPE_KEY.ORDERING]: ORDERING_CODEC,
|
|
806
|
-
[QUESTION_TYPE_KEY.LINK]: LINK_CODEC,
|
|
807
|
-
[QUESTION_TYPE_KEY.HOTSPOT]: HOTSPOT_CODEC
|
|
808
|
-
};
|
|
809
|
-
|
|
810
|
-
// ../utils/dist/validation/constants.js
|
|
811
|
-
var ALLOWED_CONTENT_TYPES = ["video/mp4", "video/quicktime", "video/x-msvideo", "video/x-matroska"];
|
|
812
|
-
var ALLOWED_DOCUMENT_TYPES = [
|
|
813
|
-
"application/pdf",
|
|
814
|
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
815
|
-
// .docx
|
|
816
|
-
"application/msword"
|
|
817
|
-
// .doc
|
|
818
|
-
];
|
|
819
|
-
var QUESTION_TYPE = QUESTION_TYPE_IDS;
|
|
820
|
-
|
|
821
|
-
// ../utils/dist/validation/exercise/exercise.js
|
|
822
|
-
var QUESTION_VALIDATION_RULES = {
|
|
823
|
-
[QUESTION_TYPE.RADIO]: [
|
|
824
|
-
(question) => {
|
|
825
|
-
const options = question.options || [];
|
|
826
|
-
if (options.length > 0 && options.length < 2) {
|
|
827
|
-
return "Options must have at least 2 items for RADIO/CHECKBOX questions";
|
|
828
|
-
}
|
|
829
|
-
return null;
|
|
830
|
-
},
|
|
831
|
-
(question) => {
|
|
832
|
-
const options = question.options || [];
|
|
833
|
-
if (options.length > 0 && !options.some((opt) => opt.isCorrect === true)) {
|
|
834
|
-
return "Please mark an option as the correct answer";
|
|
835
|
-
}
|
|
836
|
-
return null;
|
|
837
|
-
}
|
|
838
|
-
],
|
|
839
|
-
[QUESTION_TYPE.CHECKBOX]: [
|
|
840
|
-
(question) => {
|
|
841
|
-
const options = question.options || [];
|
|
842
|
-
if (options.length > 0 && options.length < 2) {
|
|
843
|
-
return "Options must have at least 2 items for RADIO/CHECKBOX questions";
|
|
844
|
-
}
|
|
845
|
-
return null;
|
|
846
|
-
},
|
|
847
|
-
(question) => {
|
|
848
|
-
const options = question.options || [];
|
|
849
|
-
if (options.length > 0 && !options.some((opt) => opt.isCorrect === true)) {
|
|
850
|
-
return "Please mark an option as the correct answer";
|
|
851
|
-
}
|
|
852
|
-
return null;
|
|
853
|
-
}
|
|
854
|
-
],
|
|
855
|
-
[QUESTION_TYPE.TRUE_FALSE]: [
|
|
856
|
-
(question) => {
|
|
857
|
-
const options = question.options || [];
|
|
858
|
-
if (options.length > 0 && options.length < 2) {
|
|
859
|
-
return "True/False questions need both True and False options";
|
|
860
|
-
}
|
|
861
|
-
return null;
|
|
862
|
-
},
|
|
863
|
-
(question) => {
|
|
864
|
-
const options = question.options || [];
|
|
865
|
-
if (options.length > 0 && !options.some((opt) => opt.isCorrect === true)) {
|
|
866
|
-
return "Please mark an option as the correct answer";
|
|
867
|
-
}
|
|
868
|
-
return null;
|
|
869
|
-
}
|
|
870
|
-
],
|
|
871
|
-
[QUESTION_TYPE.TEXTAREA]: []
|
|
872
|
-
};
|
|
873
|
-
var ZExerciseUpdateQuestionBase = z11.object({
|
|
874
|
-
id: z11.number().int().optional(),
|
|
875
|
-
question: z11.string().min(1),
|
|
876
|
-
questionTypeId: z11.number().int().min(1).optional(),
|
|
877
|
-
// Added to support question type updates
|
|
878
|
-
points: z11.number().int().min(0).optional(),
|
|
879
|
-
order: z11.number().int().min(0).optional(),
|
|
880
|
-
settings: z11.record(z11.string(), z11.unknown()).optional(),
|
|
881
|
-
deletedAt: z11.string().optional(),
|
|
882
|
-
// Marks question as deleted
|
|
883
|
-
options: z11.array(z11.object({
|
|
884
|
-
id: z11.number().int().optional(),
|
|
885
|
-
label: z11.string().min(1),
|
|
886
|
-
isCorrect: z11.boolean(),
|
|
887
|
-
settings: z11.record(z11.string(), z11.unknown()).optional(),
|
|
888
|
-
deletedAt: z11.string().optional()
|
|
889
|
-
// Marks option as deleted
|
|
890
|
-
})).optional()
|
|
891
|
-
});
|
|
892
|
-
function validateQuestionOptions(question, ctx) {
|
|
893
|
-
if (question.deletedAt)
|
|
894
|
-
return;
|
|
895
|
-
const rules = QUESTION_VALIDATION_RULES[question.questionTypeId ?? -1] ?? [];
|
|
896
|
-
for (const rule of rules) {
|
|
897
|
-
const message = rule(question);
|
|
898
|
-
if (!message)
|
|
899
|
-
continue;
|
|
900
|
-
ctx.addIssue({
|
|
901
|
-
code: "custom",
|
|
902
|
-
message,
|
|
903
|
-
path: ["options"]
|
|
904
|
-
});
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
var ZExerciseUpdateQuestion = ZExerciseUpdateQuestionBase.superRefine(validateQuestionOptions);
|
|
908
|
-
var ZExerciseCreate = z11.object({
|
|
909
|
-
title: z11.string().min(1),
|
|
910
|
-
description: z11.string().optional(),
|
|
911
|
-
lessonId: z11.string().optional(),
|
|
912
|
-
sectionId: z11.string().optional(),
|
|
913
|
-
order: z11.number().int().optional(),
|
|
914
|
-
courseId: z11.string().min(1),
|
|
915
|
-
dueBy: z11.string().optional(),
|
|
916
|
-
questions: z11.array(z11.object({
|
|
917
|
-
question: z11.string().min(1),
|
|
918
|
-
questionTypeId: z11.number().int().min(1).optional(),
|
|
919
|
-
points: z11.number().int().min(0).optional(),
|
|
920
|
-
settings: z11.record(z11.string(), z11.unknown()).optional(),
|
|
921
|
-
options: z11.array(z11.object({
|
|
922
|
-
label: z11.string().min(1),
|
|
923
|
-
isCorrect: z11.boolean(),
|
|
924
|
-
settings: z11.record(z11.string(), z11.unknown()).optional()
|
|
925
|
-
})).min(2)
|
|
926
|
-
})).optional()
|
|
927
|
-
});
|
|
928
|
-
var ZExerciseUpdate = z11.object({
|
|
929
|
-
title: z11.string().min(1).optional(),
|
|
930
|
-
description: z11.string().optional(),
|
|
931
|
-
lessonId: z11.string().optional(),
|
|
932
|
-
sectionId: z11.string().optional(),
|
|
933
|
-
order: z11.number().int().optional(),
|
|
934
|
-
isUnlocked: z11.boolean().optional(),
|
|
935
|
-
dueBy: z11.string().optional(),
|
|
936
|
-
// Changed from iso.datetime() to string to match frontend format
|
|
937
|
-
questions: z11.array(ZExerciseUpdateQuestion).optional()
|
|
938
|
-
});
|
|
939
|
-
var ZExerciseGetParam = z11.object({
|
|
940
|
-
exerciseId: z11.string().min(1)
|
|
941
|
-
});
|
|
942
|
-
var ZExerciseListQuery = z11.object({
|
|
943
|
-
lessonId: z11.string().optional(),
|
|
944
|
-
sectionId: z11.string().optional()
|
|
945
|
-
});
|
|
946
|
-
var ZExerciseSubmissionCreate = z11.object({
|
|
947
|
-
exerciseId: z11.string().min(1),
|
|
948
|
-
answers: z11.array(z11.object({
|
|
949
|
-
questionId: z11.number().int(),
|
|
950
|
-
optionId: z11.number().int().optional(),
|
|
951
|
-
answer: z11.string().optional()
|
|
952
|
-
})).min(1)
|
|
953
|
-
});
|
|
954
|
-
var ZExerciseFromTemplate = z11.object({
|
|
955
|
-
lessonId: z11.string().optional(),
|
|
956
|
-
sectionId: z11.string().optional(),
|
|
957
|
-
order: z11.number().int().optional(),
|
|
958
|
-
templateId: z11.number().int().min(1)
|
|
959
|
-
});
|
|
960
|
-
|
|
961
|
-
// ../utils/dist/validation/course-import/course-import.js
|
|
962
|
-
import * as z13 from "zod";
|
|
963
|
-
|
|
964
|
-
// ../utils/dist/validation/course/course.js
|
|
965
|
-
import * as z12 from "zod";
|
|
966
|
-
var ZCourseClone = z12.object({
|
|
967
|
-
title: z12.string().min(1),
|
|
968
|
-
description: z12.string().optional(),
|
|
969
|
-
slug: z12.string().min(1),
|
|
970
|
-
organizationId: z12.string().min(1)
|
|
971
|
-
});
|
|
972
|
-
var ZCourseCloneParam = z12.object({
|
|
973
|
-
courseId: z12.string().min(1)
|
|
974
|
-
});
|
|
975
|
-
var ZCourseGetParam = z12.object({
|
|
976
|
-
courseId: z12.string().min(1)
|
|
977
|
-
});
|
|
978
|
-
var ZCourseGetQuery = z12.object({
|
|
979
|
-
slug: z12.string().optional()
|
|
980
|
-
});
|
|
981
|
-
var ZCourseGetBySlugParam = z12.object({
|
|
982
|
-
slug: z12.string().min(1)
|
|
983
|
-
});
|
|
984
|
-
var ZCourseEnrollParam = z12.object({
|
|
985
|
-
courseId: z12.string().min(1)
|
|
986
|
-
});
|
|
987
|
-
var ZCourseEnrollBody = z12.object({
|
|
988
|
-
inviteToken: z12.string().min(1).optional()
|
|
989
|
-
});
|
|
990
|
-
var ZCourseDownloadParam = z12.object({
|
|
991
|
-
courseId: z12.string().min(1)
|
|
992
|
-
});
|
|
993
|
-
var ZCertificateDownload = z12.object({
|
|
994
|
-
theme: z12.string().min(1),
|
|
995
|
-
studentName: z12.string().min(1),
|
|
996
|
-
courseName: z12.string().min(1),
|
|
997
|
-
courseDescription: z12.string().min(1),
|
|
998
|
-
orgName: z12.string().min(1),
|
|
999
|
-
orgLogoUrl: z12.string().url().optional(),
|
|
1000
|
-
facilitator: z12.string().optional()
|
|
1001
|
-
});
|
|
1002
|
-
var ZCourseDownloadContent = z12.object({
|
|
1003
|
-
courseTitle: z12.string().min(1),
|
|
1004
|
-
orgName: z12.string().min(1),
|
|
1005
|
-
orgTheme: z12.string().min(1),
|
|
1006
|
-
lessons: z12.array(z12.object({
|
|
1007
|
-
lessonTitle: z12.string().min(1),
|
|
1008
|
-
lessonNumber: z12.string().min(1),
|
|
1009
|
-
lessonNote: z12.string(),
|
|
1010
|
-
slideUrl: z12.string().url().optional(),
|
|
1011
|
-
video: z12.array(z12.string().url()).optional()
|
|
1012
|
-
}))
|
|
1013
|
-
});
|
|
1014
|
-
var ZLessonDownloadContent = z12.object({
|
|
1015
|
-
title: z12.string().min(1),
|
|
1016
|
-
number: z12.string().min(1),
|
|
1017
|
-
orgName: z12.string().min(1),
|
|
1018
|
-
note: z12.string(),
|
|
1019
|
-
slideUrl: z12.string().url().optional(),
|
|
1020
|
-
video: z12.array(z12.string().url()).optional(),
|
|
1021
|
-
courseTitle: z12.string().min(1)
|
|
1022
|
-
});
|
|
1023
|
-
var ZCoursePresignUrlUpload = z12.object({
|
|
1024
|
-
fileName: z12.string().min(1),
|
|
1025
|
-
fileType: z12.enum(ALLOWED_CONTENT_TYPES)
|
|
1026
|
-
});
|
|
1027
|
-
var ZCourseDocumentPresignUrlUpload = z12.object({
|
|
1028
|
-
fileName: z12.string().min(1),
|
|
1029
|
-
fileType: z12.enum(ALLOWED_DOCUMENT_TYPES)
|
|
1030
|
-
});
|
|
1031
|
-
var ZCourseDownloadPresignedUrl = z12.object({
|
|
1032
|
-
keys: z12.array(z12.string().min(1)).min(1)
|
|
1033
|
-
});
|
|
1034
|
-
var ZCourseContentUpdateItem = z12.object({
|
|
1035
|
-
id: z12.string().min(1),
|
|
1036
|
-
type: z12.enum(["LESSON", "EXERCISE"]),
|
|
1037
|
-
isUnlocked: z12.boolean().optional(),
|
|
1038
|
-
order: z12.number().int().min(0).optional(),
|
|
1039
|
-
sectionId: z12.string().nullable().optional()
|
|
1040
|
-
});
|
|
1041
|
-
var ZCourseContentUpdate = z12.object({
|
|
1042
|
-
items: z12.array(ZCourseContentUpdateItem).min(1)
|
|
1043
|
-
});
|
|
1044
|
-
var ZCourseContentDeleteItem = z12.object({
|
|
1045
|
-
id: z12.string().min(1),
|
|
1046
|
-
type: z12.enum(["LESSON", "EXERCISE"])
|
|
1047
|
-
});
|
|
1048
|
-
var ZCourseContentDelete = z12.object({
|
|
1049
|
-
sectionId: z12.string().min(1).optional(),
|
|
1050
|
-
items: z12.array(ZCourseContentDeleteItem).min(1).optional()
|
|
1051
|
-
}).refine((data) => Boolean(data.sectionId) !== Boolean(data.items), {
|
|
1052
|
-
message: "Provide either sectionId or items",
|
|
1053
|
-
path: ["sectionId"]
|
|
1054
|
-
});
|
|
1055
|
-
var ZCourseCreate = z12.object({
|
|
1056
|
-
title: z12.string().min(1),
|
|
1057
|
-
description: z12.string().min(1),
|
|
1058
|
-
type: z12.enum(["LIVE_CLASS", "SELF_PACED"]),
|
|
1059
|
-
organizationId: z12.string().min(1)
|
|
1060
|
-
});
|
|
1061
|
-
var ZCourseReward = z12.object({
|
|
1062
|
-
show: z12.boolean(),
|
|
1063
|
-
description: z12.string()
|
|
1064
|
-
});
|
|
1065
|
-
var ZCourseInstructor = z12.object({
|
|
1066
|
-
name: z12.string(),
|
|
1067
|
-
role: z12.string(),
|
|
1068
|
-
coursesNo: z12.number(),
|
|
1069
|
-
description: z12.string(),
|
|
1070
|
-
imgUrl: z12.string()
|
|
1071
|
-
});
|
|
1072
|
-
var ZCourseCertificate = z12.object({
|
|
1073
|
-
templateUrl: z12.string()
|
|
1074
|
-
});
|
|
1075
|
-
var ZCourseReview = z12.object({
|
|
1076
|
-
id: z12.number(),
|
|
1077
|
-
hide: z12.boolean(),
|
|
1078
|
-
name: z12.string(),
|
|
1079
|
-
avatar_url: z12.string(),
|
|
1080
|
-
rating: z12.number(),
|
|
1081
|
-
created_at: z12.number(),
|
|
1082
|
-
description: z12.string()
|
|
1083
|
-
});
|
|
1084
|
-
var ZCourseLessonTabsOrder = z12.array(z12.object({
|
|
1085
|
-
id: z12.union([z12.literal(1), z12.literal(2), z12.literal(3), z12.literal(4)]),
|
|
1086
|
-
name: z12.string()
|
|
1087
|
-
}));
|
|
1088
|
-
var ZCourseMetadata = z12.object({
|
|
1089
|
-
requirements: z12.string().optional(),
|
|
1090
|
-
description: z12.string().optional(),
|
|
1091
|
-
goals: z12.string().optional(),
|
|
1092
|
-
videoUrl: z12.string().optional(),
|
|
1093
|
-
showDiscount: z12.boolean().optional(),
|
|
1094
|
-
discount: z12.number().optional(),
|
|
1095
|
-
paymentLink: z12.string().optional(),
|
|
1096
|
-
reward: ZCourseReward.optional(),
|
|
1097
|
-
instructor: ZCourseInstructor.optional(),
|
|
1098
|
-
certificate: ZCourseCertificate.optional(),
|
|
1099
|
-
reviews: z12.array(ZCourseReview).optional(),
|
|
1100
|
-
lessonTabsOrder: ZCourseLessonTabsOrder.optional(),
|
|
1101
|
-
grading: z12.boolean().optional(),
|
|
1102
|
-
lessonDownload: z12.boolean().optional(),
|
|
1103
|
-
allowNewStudent: z12.boolean(),
|
|
1104
|
-
sectionDisplay: z12.record(z12.string(), z12.boolean()).optional(),
|
|
1105
|
-
isContentGroupingEnabled: z12.boolean().optional()
|
|
1106
|
-
});
|
|
1107
|
-
var ZCourseLandingPageMetadataUpdate = z12.object({
|
|
1108
|
-
requirements: z12.string().optional(),
|
|
1109
|
-
description: z12.string().optional(),
|
|
1110
|
-
goals: z12.string().optional(),
|
|
1111
|
-
videoUrl: z12.string().optional(),
|
|
1112
|
-
showDiscount: z12.boolean().optional(),
|
|
1113
|
-
discount: z12.number().optional(),
|
|
1114
|
-
paymentLink: z12.string().optional(),
|
|
1115
|
-
reward: ZCourseReward.partial().optional(),
|
|
1116
|
-
instructor: ZCourseInstructor.partial().optional(),
|
|
1117
|
-
certificate: ZCourseCertificate.partial().optional(),
|
|
1118
|
-
reviews: z12.array(ZCourseReview).optional(),
|
|
1119
|
-
lessonTabsOrder: ZCourseLessonTabsOrder.optional(),
|
|
1120
|
-
grading: z12.boolean().optional(),
|
|
1121
|
-
lessonDownload: z12.boolean().optional(),
|
|
1122
|
-
allowNewStudent: z12.boolean().optional(),
|
|
1123
|
-
sectionDisplay: z12.record(z12.string(), z12.boolean()).optional(),
|
|
1124
|
-
isContentGroupingEnabled: z12.boolean().optional()
|
|
1125
|
-
});
|
|
1126
|
-
var ZCourseUpdate = z12.object({
|
|
1127
|
-
title: z12.string().min(1).optional(),
|
|
1128
|
-
description: z12.string().min(1).optional(),
|
|
1129
|
-
type: z12.enum(["LIVE_CLASS", "SELF_PACED"]).optional(),
|
|
1130
|
-
logo: z12.string().optional(),
|
|
1131
|
-
slug: z12.string().optional(),
|
|
1132
|
-
isPublished: z12.boolean().optional(),
|
|
1133
|
-
overview: z12.string().optional(),
|
|
1134
|
-
metadata: ZCourseMetadata.optional(),
|
|
1135
|
-
cost: z12.number().int().min(0).optional(),
|
|
1136
|
-
currency: z12.enum(["NGN", "USD"]).optional(),
|
|
1137
|
-
isCertificateDownloadable: z12.boolean().optional(),
|
|
1138
|
-
certificateTheme: z12.string().optional(),
|
|
1139
|
-
tagIds: z12.array(z12.uuid()).max(100).optional()
|
|
1140
|
-
});
|
|
1141
|
-
var ZCourseUpdateParam = z12.object({
|
|
1142
|
-
courseId: z12.string().min(1)
|
|
1143
|
-
});
|
|
1144
|
-
var ZCourseLandingPageUpdate = z12.object({
|
|
1145
|
-
title: z12.string().min(1).optional(),
|
|
1146
|
-
description: z12.string().min(1).optional(),
|
|
1147
|
-
overview: z12.string().optional(),
|
|
1148
|
-
cost: z12.number().int().min(0).optional(),
|
|
1149
|
-
currency: z12.enum(["NGN", "USD"]).optional(),
|
|
1150
|
-
imageUrl: z12.string().url().optional(),
|
|
1151
|
-
generateImage: z12.boolean().optional(),
|
|
1152
|
-
imageQuery: z12.string().min(1).max(120).optional(),
|
|
1153
|
-
metadata: ZCourseLandingPageMetadataUpdate.optional()
|
|
1154
|
-
});
|
|
1155
|
-
var ZCourseDeleteParam = z12.object({
|
|
1156
|
-
courseId: z12.string().min(1)
|
|
1157
|
-
});
|
|
1158
|
-
var ZCourseProgressParam = z12.object({
|
|
1159
|
-
courseId: z12.string().min(1)
|
|
1160
|
-
});
|
|
1161
|
-
var ZCourseProgressQuery = z12.object({
|
|
1162
|
-
profileId: z12.string().uuid()
|
|
1163
|
-
});
|
|
1164
|
-
var ZCourseUserAnalyticsParam = z12.object({
|
|
1165
|
-
courseId: z12.string().min(1),
|
|
1166
|
-
userId: z12.string().uuid()
|
|
1167
|
-
});
|
|
1168
|
-
|
|
1169
|
-
// ../utils/dist/validation/course-import/course-import.js
|
|
1170
|
-
var ZSupportedLocale = z13.enum(["en", "hi", "fr", "pt", "de", "vi", "ru", "es", "pl", "da"]);
|
|
1171
|
-
var ZCourseImportWarning = z13.object({
|
|
1172
|
-
code: z13.string().min(1),
|
|
1173
|
-
message: z13.string().min(1),
|
|
1174
|
-
severity: z13.enum(["info", "warning", "error"])
|
|
1175
|
-
});
|
|
1176
|
-
var ZCourseImportSourceReference = z13.object({
|
|
1177
|
-
type: z13.enum(["prompt", "pdf", "course"]),
|
|
1178
|
-
label: z13.string().min(1),
|
|
1179
|
-
pageStart: z13.number().int().min(1).optional(),
|
|
1180
|
-
pageEnd: z13.number().int().min(1).optional()
|
|
1181
|
-
});
|
|
1182
|
-
var ZCourseImportDraftCourse = z13.object({
|
|
1183
|
-
title: z13.string().min(1),
|
|
1184
|
-
description: z13.string().min(1),
|
|
1185
|
-
type: z13.enum(["LIVE_CLASS", "SELF_PACED"]),
|
|
1186
|
-
locale: ZSupportedLocale.default("en"),
|
|
1187
|
-
metadata: ZCourseMetadata.optional()
|
|
1188
|
-
});
|
|
1189
|
-
var ZCourseImportDraftSection = z13.object({
|
|
1190
|
-
externalId: z13.string().min(1),
|
|
1191
|
-
title: z13.string().min(1),
|
|
1192
|
-
order: z13.number().int().min(0)
|
|
1193
|
-
});
|
|
1194
|
-
var ZCourseImportDraftLesson = z13.object({
|
|
1195
|
-
externalId: z13.string().min(1),
|
|
1196
|
-
sectionExternalId: z13.string().min(1),
|
|
1197
|
-
title: z13.string().min(1),
|
|
1198
|
-
order: z13.number().int().min(0),
|
|
1199
|
-
isUnlocked: z13.boolean().optional(),
|
|
1200
|
-
public: z13.boolean().optional()
|
|
1201
|
-
});
|
|
1202
|
-
var ZCourseImportDraftLessonLanguage = z13.object({
|
|
1203
|
-
lessonExternalId: z13.string().min(1),
|
|
1204
|
-
locale: ZSupportedLocale,
|
|
1205
|
-
content: z13.string().min(1)
|
|
1206
|
-
});
|
|
1207
|
-
var ZCourseImportDraftPayload = z13.object({
|
|
1208
|
-
course: ZCourseImportDraftCourse,
|
|
1209
|
-
tags: z13.array(z13.string().trim().min(1).max(80)).max(100).default([]),
|
|
1210
|
-
sections: z13.array(ZCourseImportDraftSection).min(1),
|
|
1211
|
-
lessons: z13.array(ZCourseImportDraftLesson).min(1),
|
|
1212
|
-
lessonLanguages: z13.array(ZCourseImportDraftLessonLanguage).min(1),
|
|
1213
|
-
exercises: z13.array(z13.record(z13.string(), z13.unknown())).optional(),
|
|
1214
|
-
sourceReferences: z13.array(ZCourseImportSourceReference).optional(),
|
|
1215
|
-
warnings: z13.array(ZCourseImportWarning).default([])
|
|
1216
|
-
}).superRefine((value, ctx) => {
|
|
1217
|
-
const normalizedTags = /* @__PURE__ */ new Set();
|
|
1218
|
-
value.tags.forEach((tag, index) => {
|
|
1219
|
-
const tagKey = tag.trim().toLowerCase();
|
|
1220
|
-
if (normalizedTags.has(tagKey)) {
|
|
1221
|
-
ctx.addIssue({
|
|
1222
|
-
code: z13.ZodIssueCode.custom,
|
|
1223
|
-
path: ["tags", index],
|
|
1224
|
-
message: "Draft tags must be unique"
|
|
1225
|
-
});
|
|
1226
|
-
}
|
|
1227
|
-
normalizedTags.add(tagKey);
|
|
1228
|
-
});
|
|
1229
|
-
const sectionIds = /* @__PURE__ */ new Set();
|
|
1230
|
-
value.sections.forEach((section, index) => {
|
|
1231
|
-
if (sectionIds.has(section.externalId)) {
|
|
1232
|
-
ctx.addIssue({
|
|
1233
|
-
code: z13.ZodIssueCode.custom,
|
|
1234
|
-
path: ["sections", index, "externalId"],
|
|
1235
|
-
message: "Section externalId must be unique"
|
|
1236
|
-
});
|
|
1237
|
-
}
|
|
1238
|
-
sectionIds.add(section.externalId);
|
|
1239
|
-
});
|
|
1240
|
-
const lessonIds = /* @__PURE__ */ new Set();
|
|
1241
|
-
value.lessons.forEach((lesson, index) => {
|
|
1242
|
-
if (lessonIds.has(lesson.externalId)) {
|
|
1243
|
-
ctx.addIssue({
|
|
1244
|
-
code: z13.ZodIssueCode.custom,
|
|
1245
|
-
path: ["lessons", index, "externalId"],
|
|
1246
|
-
message: "Lesson externalId must be unique"
|
|
1247
|
-
});
|
|
1248
|
-
}
|
|
1249
|
-
lessonIds.add(lesson.externalId);
|
|
1250
|
-
if (!sectionIds.has(lesson.sectionExternalId)) {
|
|
1251
|
-
ctx.addIssue({
|
|
1252
|
-
code: z13.ZodIssueCode.custom,
|
|
1253
|
-
path: ["lessons", index, "sectionExternalId"],
|
|
1254
|
-
message: "Lesson sectionExternalId must reference an existing section"
|
|
1255
|
-
});
|
|
1256
|
-
}
|
|
1257
|
-
});
|
|
1258
|
-
value.lessonLanguages.forEach((lessonLanguage, index) => {
|
|
1259
|
-
if (!lessonIds.has(lessonLanguage.lessonExternalId)) {
|
|
1260
|
-
ctx.addIssue({
|
|
1261
|
-
code: z13.ZodIssueCode.custom,
|
|
1262
|
-
path: ["lessonLanguages", index, "lessonExternalId"],
|
|
1263
|
-
message: "Lesson language must reference an existing lesson"
|
|
1264
|
-
});
|
|
1265
|
-
}
|
|
1266
|
-
});
|
|
1267
|
-
});
|
|
1268
|
-
var ZCourseImportDraftCreate = z13.object({
|
|
1269
|
-
sourceType: z13.enum(["prompt", "pdf", "course"]),
|
|
1270
|
-
idempotencyKey: z13.string().min(1).optional(),
|
|
1271
|
-
summary: z13.record(z13.string(), z13.unknown()).optional(),
|
|
1272
|
-
sourceArtifacts: z13.array(z13.record(z13.string(), z13.unknown())).optional(),
|
|
1273
|
-
draft: ZCourseImportDraftPayload
|
|
1274
|
-
});
|
|
1275
|
-
var ZCourseImportCourseParam = z13.object({
|
|
1276
|
-
courseId: z13.string().min(1)
|
|
1277
|
-
});
|
|
1278
|
-
var ZCourseImportDraftCreateFromCourse = z13.object({
|
|
1279
|
-
courseId: z13.string().min(1),
|
|
1280
|
-
idempotencyKey: z13.string().min(1).optional(),
|
|
1281
|
-
summary: z13.record(z13.string(), z13.unknown()).optional(),
|
|
1282
|
-
sourceArtifacts: z13.array(z13.record(z13.string(), z13.unknown())).optional()
|
|
1283
|
-
});
|
|
1284
|
-
var ZCourseImportDraftGetParam = z13.object({
|
|
1285
|
-
draftId: z13.string().uuid()
|
|
1286
|
-
});
|
|
1287
|
-
var ZCourseImportDraftUpdate = z13.object({
|
|
1288
|
-
summary: z13.record(z13.string(), z13.unknown()).optional(),
|
|
1289
|
-
sourceArtifacts: z13.array(z13.record(z13.string(), z13.unknown())).optional(),
|
|
1290
|
-
warnings: z13.array(ZCourseImportWarning).optional(),
|
|
1291
|
-
draft: ZCourseImportDraftPayload.optional()
|
|
1292
|
-
});
|
|
1293
|
-
var ZCourseImportDraftPublish = z13.object({
|
|
1294
|
-
title: z13.string().min(1).optional(),
|
|
1295
|
-
description: z13.string().min(1).optional(),
|
|
1296
|
-
type: z13.enum(["LIVE_CLASS", "SELF_PACED"]).optional(),
|
|
1297
|
-
metadata: ZCourseMetadata.optional(),
|
|
1298
|
-
bannerImageUrl: z13.string().url().optional(),
|
|
1299
|
-
bannerImageQuery: z13.string().min(1).max(120).optional(),
|
|
1300
|
-
generateBannerImage: z13.boolean().optional()
|
|
1301
|
-
});
|
|
1302
|
-
var ZCourseImportDraftPublishToCourse = ZCourseImportDraftPublish.extend({
|
|
1303
|
-
courseId: z13.string().min(1)
|
|
1304
|
-
});
|
|
1305
|
-
|
|
1306
|
-
// ../utils/dist/validation/course/section.js
|
|
1307
|
-
import * as z14 from "zod";
|
|
1308
|
-
var ZCourseSectionCreate = z14.object({
|
|
1309
|
-
title: z14.string().min(1),
|
|
1310
|
-
courseId: z14.string().min(1),
|
|
1311
|
-
order: z14.number().int().min(0).optional()
|
|
1312
|
-
});
|
|
1313
|
-
var ZCourseSectionUpdate = z14.object({
|
|
1314
|
-
title: z14.string().min(1).optional(),
|
|
1315
|
-
order: z14.number().int().min(0).optional()
|
|
1316
|
-
});
|
|
1317
|
-
var ZCourseSectionPromoteUngrouped = z14.object({
|
|
1318
|
-
title: z14.string().min(1)
|
|
1319
|
-
});
|
|
1320
|
-
var ZCourseSectionGetParam = z14.object({
|
|
1321
|
-
sectionId: z14.string().min(1)
|
|
1322
|
-
});
|
|
1323
|
-
var ZCourseSectionReorder = z14.object({
|
|
1324
|
-
sections: z14.array(z14.object({
|
|
1325
|
-
id: z14.string().min(1),
|
|
1326
|
-
order: z14.number().int().min(0)
|
|
1327
|
-
})).min(1)
|
|
1328
|
-
});
|
|
1329
|
-
|
|
1330
|
-
// ../utils/dist/validation/course/invite.js
|
|
1331
|
-
import * as z15 from "zod";
|
|
1332
|
-
var ZCourseInviteParam = z15.object({
|
|
1333
|
-
courseId: z15.string().min(1)
|
|
1334
|
-
});
|
|
1335
|
-
var ZCourseInviteRevokeParam = z15.object({
|
|
1336
|
-
courseId: z15.string().min(1),
|
|
1337
|
-
inviteId: z15.string().uuid()
|
|
1338
|
-
});
|
|
1339
|
-
var ZCourseInviteAuditParam = z15.object({
|
|
1340
|
-
courseId: z15.string().min(1),
|
|
1341
|
-
inviteId: z15.string().uuid()
|
|
1342
|
-
});
|
|
1343
|
-
var ZCourseInviteTokenParam = z15.object({
|
|
1344
|
-
token: z15.string().min(1)
|
|
1345
|
-
});
|
|
1346
|
-
var ZCreatePublicCourseInviteLink = z15.object({
|
|
1347
|
-
courseId: z15.uuid()
|
|
1348
|
-
});
|
|
1349
|
-
var ZCourseInvitePreset = z15.enum(["ONE_TIME_24H", "MULTI_USE_7D", "MULTI_USE_30D", "CUSTOM"]);
|
|
1350
|
-
var ZCreateCourseInvite = z15.object({
|
|
1351
|
-
preset: ZCourseInvitePreset.default("MULTI_USE_30D"),
|
|
1352
|
-
expiresAt: z15.string().min(1).optional(),
|
|
1353
|
-
maxUses: z15.number().int().min(1).max(1e3).optional(),
|
|
1354
|
-
allowedEmails: z15.array(z15.string().email()).max(100).optional(),
|
|
1355
|
-
allowedDomains: z15.array(z15.string().min(1)).max(100).optional(),
|
|
1356
|
-
recipientEmails: z15.array(z15.string().email()).max(500).optional(),
|
|
1357
|
-
recipientCsv: z15.string().max(25e3).optional(),
|
|
1358
|
-
sendEmail: z15.boolean().default(false),
|
|
1359
|
-
metadata: z15.record(z15.string(), z15.unknown()).optional()
|
|
1360
|
-
}).refine((data) => {
|
|
1361
|
-
const hasEmails = (data.recipientEmails?.length ?? 0) > 0;
|
|
1362
|
-
const hasCsv = typeof data.recipientCsv === "string" && data.recipientCsv.trim().length > 0;
|
|
1363
|
-
return hasEmails || hasCsv;
|
|
1364
|
-
}, { message: "recipientEmails or recipientCsv is required", path: ["recipientEmails"] });
|
|
1365
|
-
|
|
1366
|
-
// src/tools/course-drafts.ts
|
|
1367
|
-
var ZUpdateCourseDraftToolInput = ZCourseImportDraftUpdate.extend({
|
|
1368
|
-
draftId: ZCourseImportDraftGetParam.shape.draftId
|
|
1369
|
-
});
|
|
1370
|
-
var ZTagCourseDraftToolInput = ZAutomationDraftTagAssignment.extend({
|
|
1371
|
-
draftId: ZAutomationDraftTagParam.shape.draftId
|
|
1372
|
-
});
|
|
1373
|
-
var ZListCourseExercisesToolInput = ZExerciseListQuery.extend({
|
|
1374
|
-
courseId: ZCourseImportCourseParam.shape.courseId
|
|
1375
|
-
});
|
|
1376
|
-
var ZGetCourseExerciseToolInput = ZExerciseGetParam.extend({
|
|
1377
|
-
courseId: ZCourseImportCourseParam.shape.courseId
|
|
1378
|
-
});
|
|
1379
|
-
var ZCreateCourseExerciseToolInput = ZExerciseCreate.omit({
|
|
1380
|
-
courseId: true
|
|
1381
|
-
}).extend({
|
|
1382
|
-
courseId: ZCourseImportCourseParam.shape.courseId
|
|
1383
|
-
});
|
|
1384
|
-
var ZCreateCourseExerciseFromTemplateToolInput = ZExerciseFromTemplate.extend({
|
|
1385
|
-
courseId: ZCourseImportCourseParam.shape.courseId
|
|
1386
|
-
});
|
|
1387
|
-
var ZUpdateCourseExerciseToolInput = ZExerciseUpdate.extend({
|
|
1388
|
-
courseId: ZCourseImportCourseParam.shape.courseId,
|
|
1389
|
-
exerciseId: ZExerciseGetParam.shape.exerciseId
|
|
1390
|
-
});
|
|
1391
|
-
var ZUpdateCourseLandingPageToolInput = ZCourseLandingPageUpdate.extend({
|
|
1392
|
-
courseId: ZCourseImportCourseParam.shape.courseId
|
|
1393
|
-
});
|
|
1394
|
-
var ZPublishCourseDraftToolInput = ZCourseImportDraftPublish.extend({
|
|
1395
|
-
draftId: ZCourseImportDraftGetParam.shape.draftId
|
|
1396
|
-
});
|
|
1397
|
-
var ZPublishCourseDraftToExistingCourseToolInput = ZCourseImportDraftPublishToCourse.extend({
|
|
1398
|
-
draftId: ZCourseImportDraftGetParam.shape.draftId
|
|
1399
|
-
});
|
|
1400
|
-
var createCourseDraftShape = ZCourseImportDraftCreate.shape;
|
|
1401
|
-
var createCourseDraftFromCourseShape = ZCourseImportDraftCreateFromCourse.shape;
|
|
1402
|
-
var listOrganizationCoursesShape = ZGetOrganizationCoursesQuery.shape;
|
|
1403
|
-
var getCourseStructureShape = ZCourseImportCourseParam.shape;
|
|
1404
|
-
var getCourseDraftShape = ZCourseImportDraftGetParam.shape;
|
|
1405
|
-
var updateCourseDraftShape = ZUpdateCourseDraftToolInput.shape;
|
|
1406
|
-
var tagCourseDraftShape = ZTagCourseDraftToolInput.shape;
|
|
1407
|
-
var listCourseExercisesShape = ZListCourseExercisesToolInput.shape;
|
|
1408
|
-
var getCourseExerciseShape = ZGetCourseExerciseToolInput.shape;
|
|
1409
|
-
var createCourseExerciseShape = ZCreateCourseExerciseToolInput.shape;
|
|
1410
|
-
var createCourseExerciseFromTemplateShape = ZCreateCourseExerciseFromTemplateToolInput.shape;
|
|
1411
|
-
var updateCourseExerciseShape = ZUpdateCourseExerciseToolInput.shape;
|
|
1412
|
-
var updateCourseLandingPageShape = ZUpdateCourseLandingPageToolInput.shape;
|
|
1413
|
-
var publishCourseDraftShape = ZPublishCourseDraftToolInput.shape;
|
|
1414
|
-
var publishCourseDraftToExistingCourseShape = ZPublishCourseDraftToExistingCourseToolInput.shape;
|
|
1415
|
-
var tagCoursesShape = ZAutomationCourseTagAssignment.shape;
|
|
1416
|
-
var SUPPORTED_QUESTION_TYPES_GUIDE = "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.";
|
|
1417
|
-
function registerCourseDraftTools(server, apiClient) {
|
|
1418
|
-
server.tool(
|
|
1419
|
-
"list_org_courses",
|
|
1420
|
-
"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.",
|
|
1421
|
-
listOrganizationCoursesShape,
|
|
1422
|
-
async (args) => {
|
|
1423
|
-
const query = ZGetOrganizationCoursesQuery.parse(args);
|
|
1424
|
-
const result = await apiClient.listOrganizationCourses(query);
|
|
1425
|
-
return jsonContent(result);
|
|
1426
|
-
}
|
|
1427
|
-
);
|
|
1428
|
-
server.tool(
|
|
1429
|
-
"get_course_structure",
|
|
1430
|
-
"Read the current live course structure. Use this before creating a fresh draft for post-publish edits.",
|
|
1431
|
-
getCourseStructureShape,
|
|
1432
|
-
async (args) => {
|
|
1433
|
-
const { courseId } = ZCourseImportCourseParam.parse(args);
|
|
1434
|
-
const result = await apiClient.getCourseStructure(courseId);
|
|
1435
|
-
return jsonContent(result);
|
|
1436
|
-
}
|
|
1437
|
-
);
|
|
1438
|
-
server.tool(
|
|
1439
|
-
"update_course_landing_page",
|
|
1440
|
-
"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.",
|
|
1441
|
-
updateCourseLandingPageShape,
|
|
1442
|
-
async (args) => {
|
|
1443
|
-
const { courseId, ...payload } = ZUpdateCourseLandingPageToolInput.parse(args);
|
|
1444
|
-
const result = await apiClient.updateCourseLandingPage(courseId, payload);
|
|
1445
|
-
return jsonContent(result);
|
|
1446
|
-
}
|
|
1447
|
-
);
|
|
1448
|
-
server.tool(
|
|
1449
|
-
"create_course_draft",
|
|
1450
|
-
"Create a new unpublished course draft from structured course JSON. Use this before the first publish.",
|
|
1451
|
-
createCourseDraftShape,
|
|
1452
|
-
async (args) => {
|
|
1453
|
-
const payload = ZCourseImportDraftCreate.parse(args);
|
|
1454
|
-
const result = await apiClient.createCourseDraft(payload);
|
|
1455
|
-
return jsonContent(result);
|
|
1456
|
-
}
|
|
1457
|
-
);
|
|
1458
|
-
server.tool(
|
|
1459
|
-
"create_course_draft_from_course",
|
|
1460
|
-
"Create a fresh draft from an existing live course. Use this after a course has already been published and the user wants more changes.",
|
|
1461
|
-
createCourseDraftFromCourseShape,
|
|
1462
|
-
async (args) => {
|
|
1463
|
-
const payload = ZCourseImportDraftCreateFromCourse.parse(args);
|
|
1464
|
-
const result = await apiClient.createCourseDraftFromCourse(payload);
|
|
1465
|
-
return jsonContent(result);
|
|
1466
|
-
}
|
|
1467
|
-
);
|
|
1468
|
-
server.tool(
|
|
1469
|
-
"get_course_draft",
|
|
1470
|
-
"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.",
|
|
1471
|
-
getCourseDraftShape,
|
|
1472
|
-
async (args) => {
|
|
1473
|
-
const { draftId } = ZCourseImportDraftGetParam.parse(args);
|
|
1474
|
-
const result = await apiClient.getCourseDraft(draftId);
|
|
1475
|
-
return jsonContent(result);
|
|
1476
|
-
}
|
|
1477
|
-
);
|
|
1478
|
-
server.tool(
|
|
1479
|
-
"list_course_exercises",
|
|
1480
|
-
"List exercises for a live course, optionally filtered by lessonId or sectionId.",
|
|
1481
|
-
listCourseExercisesShape,
|
|
1482
|
-
async (args) => {
|
|
1483
|
-
const { courseId, ...query } = ZListCourseExercisesToolInput.parse(args);
|
|
1484
|
-
const result = await apiClient.listCourseExercises(courseId, query);
|
|
1485
|
-
return jsonContent(result);
|
|
1486
|
-
}
|
|
1487
|
-
);
|
|
1488
|
-
server.tool(
|
|
1489
|
-
"get_course_exercise",
|
|
1490
|
-
"Fetch a single exercise from a live course, including its questions and options.",
|
|
1491
|
-
getCourseExerciseShape,
|
|
1492
|
-
async (args) => {
|
|
1493
|
-
const { courseId, exerciseId } = ZGetCourseExerciseToolInput.parse(args);
|
|
1494
|
-
const result = await apiClient.getCourseExercise(courseId, exerciseId);
|
|
1495
|
-
return jsonContent(result);
|
|
1496
|
-
}
|
|
1497
|
-
);
|
|
1498
|
-
server.tool(
|
|
1499
|
-
"create_course_exercise",
|
|
1500
|
-
`Create a new exercise directly on a live course. Use this for adding exercises after a course has already been published. ${SUPPORTED_QUESTION_TYPES_GUIDE}`,
|
|
1501
|
-
createCourseExerciseShape,
|
|
1502
|
-
async (args) => {
|
|
1503
|
-
const { courseId, ...payload } = ZCreateCourseExerciseToolInput.parse(args);
|
|
1504
|
-
const result = await apiClient.createCourseExercise(courseId, payload);
|
|
1505
|
-
return jsonContent(result);
|
|
1506
|
-
}
|
|
1507
|
-
);
|
|
1508
|
-
server.tool(
|
|
1509
|
-
"create_course_exercise_from_template",
|
|
1510
|
-
"Create a new exercise on a live course from an existing template.",
|
|
1511
|
-
createCourseExerciseFromTemplateShape,
|
|
1512
|
-
async (args) => {
|
|
1513
|
-
const { courseId, ...payload } = ZCreateCourseExerciseFromTemplateToolInput.parse(args);
|
|
1514
|
-
const result = await apiClient.createCourseExerciseFromTemplate(courseId, payload);
|
|
1515
|
-
return jsonContent(result);
|
|
1516
|
-
}
|
|
1517
|
-
);
|
|
1518
|
-
server.tool(
|
|
1519
|
-
"update_course_exercise",
|
|
1520
|
-
`Update an existing exercise on a live course, including questions and options. ${SUPPORTED_QUESTION_TYPES_GUIDE}`,
|
|
1521
|
-
updateCourseExerciseShape,
|
|
1522
|
-
async (args) => {
|
|
1523
|
-
const { courseId, exerciseId, ...payload } = ZUpdateCourseExerciseToolInput.parse(args);
|
|
1524
|
-
const result = await apiClient.updateCourseExercise(courseId, exerciseId, payload);
|
|
1525
|
-
return jsonContent(result);
|
|
1526
|
-
}
|
|
1527
|
-
);
|
|
1528
|
-
server.tool(
|
|
1529
|
-
"update_course_draft",
|
|
1530
|
-
"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.",
|
|
1531
|
-
updateCourseDraftShape,
|
|
1532
|
-
async (args) => {
|
|
1533
|
-
const { draftId, ...payload } = ZUpdateCourseDraftToolInput.parse(args);
|
|
1534
|
-
const result = await apiClient.updateCourseDraft(draftId, payload);
|
|
1535
|
-
return jsonContent(result);
|
|
1536
|
-
}
|
|
1537
|
-
);
|
|
1538
|
-
server.tool(
|
|
1539
|
-
"tag_course_draft",
|
|
1540
|
-
"Attach human-readable tags to a draft before publish. The tags are stored on the draft and applied during publish.",
|
|
1541
|
-
tagCourseDraftShape,
|
|
1542
|
-
async (args) => {
|
|
1543
|
-
const { draftId, ...payload } = ZTagCourseDraftToolInput.parse(args);
|
|
1544
|
-
const result = await apiClient.tagCourseDraft(draftId, payload);
|
|
1545
|
-
return jsonContent(result);
|
|
1546
|
-
}
|
|
1547
|
-
);
|
|
1548
|
-
server.tool(
|
|
1549
|
-
"publish_course_draft",
|
|
1550
|
-
"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.",
|
|
1551
|
-
publishCourseDraftShape,
|
|
1552
|
-
async (args) => {
|
|
1553
|
-
const { draftId, ...payload } = ZPublishCourseDraftToolInput.parse(args);
|
|
1554
|
-
const result = await apiClient.publishCourseDraft(draftId, payload);
|
|
1555
|
-
return jsonContent(result);
|
|
1556
|
-
}
|
|
1557
|
-
);
|
|
1558
|
-
server.tool(
|
|
1559
|
-
"publish_course_draft_to_existing_course",
|
|
1560
|
-
"Apply a fresh draft back onto an existing live course. Use this after reading the live course and creating a new draft from it.",
|
|
1561
|
-
publishCourseDraftToExistingCourseShape,
|
|
1562
|
-
async (args) => {
|
|
1563
|
-
const { draftId, ...payload } = ZPublishCourseDraftToExistingCourseToolInput.parse(args);
|
|
1564
|
-
const result = await apiClient.publishCourseDraftToExistingCourse(draftId, payload);
|
|
1565
|
-
return jsonContent(result);
|
|
1566
|
-
}
|
|
1567
|
-
);
|
|
1568
|
-
server.tool(
|
|
1569
|
-
"tag_courses",
|
|
1570
|
-
"Assign tags directly to one or more live courses by name. Default mode is merge, so existing tags are preserved unless replace is requested.",
|
|
1571
|
-
tagCoursesShape,
|
|
1572
|
-
async (args) => {
|
|
1573
|
-
const payload = ZAutomationCourseTagAssignment.parse(args);
|
|
1574
|
-
const result = await apiClient.tagCourses(payload);
|
|
1575
|
-
return jsonContent(result);
|
|
1576
|
-
}
|
|
1577
|
-
);
|
|
1578
|
-
}
|
|
1579
|
-
function jsonContent(data) {
|
|
1580
|
-
return {
|
|
1581
|
-
content: [
|
|
1582
|
-
{
|
|
1583
|
-
type: "text",
|
|
1584
|
-
text: JSON.stringify(data)
|
|
1585
|
-
}
|
|
1586
|
-
]
|
|
1587
|
-
};
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
|
-
// src/index.ts
|
|
1591
|
-
async function main() {
|
|
1592
|
-
const config = getConfig();
|
|
1593
|
-
const apiClient = new ClassroomIoApiClient(config);
|
|
1594
|
-
const server = new McpServer({
|
|
1595
|
-
name: "classroomio-course-authoring",
|
|
1596
|
-
version: "0.0.1"
|
|
1597
|
-
});
|
|
1598
|
-
registerCourseDraftTools(server, apiClient);
|
|
1599
|
-
const transport = new StdioServerTransport();
|
|
1600
|
-
await server.connect(transport);
|
|
1601
|
-
process.stdin.on("close", () => {
|
|
1602
|
-
void server.close();
|
|
1603
|
-
});
|
|
1604
|
-
}
|
|
1605
|
-
main().catch((error) => {
|
|
1606
|
-
if (error instanceof ClassroomIoApiError) {
|
|
1607
|
-
console.error(
|
|
1608
|
-
JSON.stringify({
|
|
1609
|
-
error: error.message,
|
|
1610
|
-
status: error.status,
|
|
1611
|
-
code: error.code,
|
|
1612
|
-
field: error.field
|
|
1613
|
-
})
|
|
1614
|
-
);
|
|
1615
|
-
process.exit(1);
|
|
1616
|
-
}
|
|
1617
|
-
console.error(error);
|
|
1618
|
-
process.exit(1);
|
|
1619
|
-
});
|
|
2
|
+
import{McpServer as ht}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as St}from"@modelcontextprotocol/sdk/server/stdio.js";var b=class extends Error{constructor(n,i,u,l){super(n);this.status=i;this.code=u;this.field=l;this.name="ClassroomIoApiError"}},S=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 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 i=n.toString()?`?${n.toString()}`:"";return this.request(`/organization/courses${i}`,{method:"GET"})}async listCourseExercises(t,n={}){let i=new URLSearchParams;n.lessonId&&i.set("lessonId",n.lessonId),n.sectionId&&i.set("sectionId",n.sectionId);let u=i.toString()?`?${i.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,i){return this.request(`/course/${t}/exercise/${n}`,{method:"PUT",body:i})}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 i=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 i.json().catch(()=>null);if(!i.ok){let l=u;throw new b(l?.error??l?.message??`ClassroomIO request failed with status ${i.status}`,i.status,l?.code,l?.field)}if(!u||typeof u!="object"||!("success"in u)||!u.success)throw new b("ClassroomIO returned an invalid response payload",i.status);return u.data}};import{z as O}from"zod";var be=O.object({CLASSROOMIO_API_URL:O.url().default("https://api.classroomio.com"),CLASSROOMIO_API_KEY:O.string().min(1),CLASSROOMIO_USER_AGENT:O.string().min(1).default("classroomio-mcp/0.0.1")});function F(o=process.env){return be.parse(o)}import*as p from"zod";var Te=["#EF4444","#F97316","#F59E0B","#84CC16","#22C55E","#14B8A6","#06B6D4","#3B82F6","#8B5CF6","#EC4899"],M=p.enum(Te),wt=p.object({groupId:p.uuid()}),he=p.object({name:p.string().min(1).max(80),description:p.string().max(240).optional(),order:p.number().int().min(0).optional()}),Lt=he.partial().refine(o=>Object.keys(o).length>0,{message:"At least one field is required"}),Rt=p.object({tagId:p.uuid()}),Dt=p.object({name:p.string().min(1).max(80),description:p.string().max(240).optional(),groupId:p.uuid(),color:M}),Nt=p.object({name:p.string().min(1).max(80).optional(),description:p.string().max(240).optional(),groupId:p.uuid().optional(),color:M.optional()}).refine(o=>Object.keys(o).length>0,{message:"At least one field is required"}),Zt=p.object({courseId:p.uuid()}),Ut=p.object({tagIds:p.array(p.uuid()).max(100).default([])}),Se=p.enum(["merge","replace"]).default("merge"),Oe=p.string().trim().min(1).max(80),K=p.object({draftId:p.string().uuid()}),L=p.object({tagNames:p.array(Oe).min(1).max(100),mode:Se,groupName:p.string().trim().min(1).max(80).optional()}),R=L.extend({courseIds:p.array(p.string().uuid()).min(1).max(100)});import*as a from"zod";var H=["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 q;(function(o){o.Section="SECTION",o.Lesson="LESSON",o.Exercise="EXERCISE"})(q||(q={}));var Yt=a.object({siteName:a.string().min(1).optional(),customDomain:a.string().min(1).optional(),isCustomDomainVerified:a.boolean().optional()}),Xt=a.object({siteName:a.string().min(1),tags:a.string().optional()}),D=a.object({tags:a.string().optional()}),$t=a.object({siteName:a.string().min(1)}),Vt=a.object({orgId:a.uuid(),planName:a.enum(["EARLY_ADOPTER","ENTERPRISE","BASIC"]),subscriptionId:a.string().min(1),triggeredBy:a.number().int().positive(),payload:a.record(a.string(),a.unknown())}),Jt=a.object({subscriptionId:a.string().min(1),payload:a.record(a.string(),a.unknown())}),Wt=a.object({subscriptionId:a.string().min(1),payload:a.record(a.string(),a.unknown())}),eo=a.object({name:a.string().min(5).refine(o=>!/^[-]|[-]$/.test(o),{message:"Organization name cannot start or end with a hyphen"}),siteName:a.string().min(5).refine(o=>!/^[-]|[-]$/.test(o),{message:"Site name cannot start or end with a hyphen"}).refine(o=>!H.includes(o),{message:"Sitename already exists."})}),to=a.object({name:a.string().min(5).refine(o=>!/^[-]|[-]$/.test(o),{message:"Organization name cannot start or end with a hyphen"}).optional(),avatarUrl:a.url().optional(),theme:a.string().optional(),landingpage:a.record(a.string(),a.unknown()).optional(),siteName:a.string().min(1).optional(),customDomain:a.string().nullable().optional(),isCustomDomainVerified:a.boolean().optional(),customization:a.record(a.string(),a.unknown()).optional(),disableSignup:a.boolean().optional(),disableSignupMessage:a.string().optional(),disableEmailPassword:a.boolean().optional(),disableGoogleAuth:a.boolean().optional(),settings:a.object({signup:a.object({inviteOnly:a.boolean().optional()}).optional()}).optional()}),oo=a.object({emails:a.array(a.string().email()).min(1).max(50),roleId:a.number().int().positive()}),ro=a.object({memberId:a.coerce.number().int().positive()}),no=a.object({userId:a.uuid()});import*as g from"zod";var ao=g.object({orgId:g.uuid(),quizId:g.uuid()}),io=g.object({orgId:g.uuid()}),uo=g.object({title:g.string().min(1),questions:g.array(g.any()).optional(),timelimit:g.string().optional(),theme:g.string().optional()}),po=g.object({title:g.string().min(1).optional(),questions:g.array(g.any()).optional(),timelimit:g.string().optional(),theme:g.string().optional()});import*as A from"zod";var lo=A.object({orgId:A.uuid()});import*as _ from"zod";var go=_.object({token:_.string().min(10).max(512)});import*as c from"zod";var Ae=["OKTA","GOOGLE_WORKSPACE","AUTH0"],fo=c.object({provider:c.enum(Ae),displayName:c.string().min(1,"Display name is required"),issuer:c.url("Must be a valid URL"),domain:c.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:c.string().min(1,"Client ID is required"),clientSecret:c.string().min(1,"Client secret is required"),scopes:c.string().default("openid profile email")}),Io=c.object({displayName:c.string().min(1).optional(),issuer:c.url().optional(),domain:c.string().optional(),clientId:c.string().optional(),clientSecret:c.string().optional(),scopes:c.string().optional(),isActive:c.boolean().optional()}),Co=c.object({forceSso:c.boolean().optional(),autoJoinSsoDomains:c.boolean().optional(),breakGlassEnabled:c.boolean().optional(),defaultRoleId:c.number().int().positive().optional(),roleMapping:c.record(c.string(),c.number()).optional()}),xo=c.object({email:c.email("Valid email is required")}),yo=c.object({orgId:c.uuid()});import{z as E}from"zod";var To=E.object({token:E.string().min(1),redirect:E.string().optional()}),ho=E.object({sub:E.uuid(),email:E.email(),type:E.literal("login-link")});import{z as C}from"zod";var Ao=C.object({token:C.string().min(1),redirect:C.string().optional()}),_o=C.object({sub:C.string(),email:C.email(),name:C.string().optional(),avatar:C.url().optional(),iat:C.number(),exp:C.number()});import{z as x}from"zod";var N=x.enum(["mcp","api","zapier"]),_e=x.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:*"]),Lo=x.object({type:N.optional()}),Ro=x.object({type:N.default("mcp")}),Do=x.object({type:N,label:x.string().min(1).max(120).optional(),expiresAt:x.string().datetime().optional(),scopes:x.array(_e).min(1).optional()}),No=x.object({keyId:x.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"},qo=[m.RADIO,m.CHECKBOX,m.TEXTAREA];var P=[{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}],Pe=Object.freeze(P.reduce((o,t)=>(o[t.key]=t,o),{})),Z=Object.freeze(P.reduce((o,t)=>(o[t.key]=t.id,o),{})),we=Object.freeze(P.reduce((o,t)=>(o[t.typename]=t.key,o),{})),Le=Object.freeze(P.reduce((o,t)=>(o[t.id]=t.key,o),{}));var Re=new Set(["true","1","yes"]),De=new Set(["false","0","no"]);function Ne(o){let t=o.trim().toLowerCase();if(Re.has(t))return!0;if(De.has(t))return!1}var Ze={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}}},Ue={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}},je={type:"TRUE_FALSE",toApiPayload(o,t){return{questionId:t,answer:String(o.value)}},fromApiPayload(o){if(o.answer===void 0)return null;let t=Ne(o.answer);return t===void 0?null:{type:"TRUE_FALSE",value:t}}},ve={type:"TEXTAREA",toApiPayload(o,t){return{questionId:t,answer:o.text}},fromApiPayload(o){return o.answer===void 0?null:{type:"TEXTAREA",text:o.answer}}},Ge={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}}},ke={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}}},Fe={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(i=>String(i).trim()).filter(Boolean);return n.length>0?{type:"FILL_BLANK",values:n}:null}},Me={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}}},Ke={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}},He={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}},qe={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(i=>String(i).trim()).filter(Boolean);return n.length>0?{type:"LINK",urls:n}:null}},Be={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}},nr={[m.RADIO]:Ze,[m.CHECKBOX]:Ue,[m.TRUE_FALSE]:je,[m.TEXTAREA]:ve,[m.SHORT_ANSWER]:Ge,[m.NUMERIC]:ke,[m.FILL_BLANK]:Fe,[m.FILE_UPLOAD]:Me,[m.MATCHING]:Ke,[m.ORDERING]:He,[m.LINK]:qe,[m.HOTSPOT]:Be};var B=["video/mp4","video/quicktime","video/x-msvideo","video/x-matroska"],Q=["application/pdf","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/msword"];var h=Z;var Qe={[h.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}],[h.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}],[h.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}],[h.TEXTAREA]:[]},Ye=s.object({id:s.number().int().optional(),question:s.string().min(1),questionTypeId:s.number().int().min(1).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 Xe(o,t){if(o.deletedAt)return;let n=Qe[o.questionTypeId??-1]??[];for(let i of n){let u=i(o);u&&t.addIssue({code:"custom",message:u,path:["options"]})}}var $e=Ye.superRefine(Xe),Y=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(s.object({question:s.string().min(1),questionTypeId:s.number().int().min(1).optional(),points: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()})).min(2)})).optional()}),X=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($e).optional()}),U=s.object({exerciseId:s.string().min(1)}),$=s.object({lessonId:s.string().optional(),sectionId:s.string().optional()}),hr=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)}),V=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 Pr=e.object({title:e.string().min(1),description:e.string().optional(),slug:e.string().min(1),organizationId:e.string().min(1)}),wr=e.object({courseId:e.string().min(1)}),Ve=e.object({courseId:e.string().min(1)}),Lr=e.object({slug:e.string().optional()}),Rr=e.object({slug:e.string().min(1)}),Dr=e.object({courseId:e.string().min(1)}),Nr=e.object({inviteToken:e.string().min(1).optional()}),Zr=e.object({courseId:e.string().min(1)}),Ur=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()}),jr=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()}))}),vr=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)}),Gr=e.object({fileName:e.string().min(1),fileType:e.enum(B)}),kr=e.object({fileName:e.string().min(1),fileType:e.enum(Q)}),Fr=e.object({keys:e.array(e.string().min(1)).min(1)}),Je=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()}),Mr=e.object({items:e.array(Je).min(1)}),We=e.object({id:e.string().min(1),type:e.enum(["LESSON","EXERCISE"])}),Kr=e.object({sectionId:e.string().min(1).optional(),items:e.array(We).min(1).optional()}).refine(o=>!!o.sectionId!=!!o.items,{message:"Provide either sectionId or items",path:["sectionId"]}),Hr=e.object({title:e.string().min(1),description:e.string().min(1),type:e.enum(["LIVE_CLASS","SELF_PACED"]),organizationId:e.string().min(1)}),J=e.object({show:e.boolean(),description:e.string()}),W=e.object({name:e.string(),role:e.string(),coursesNo:e.number(),description:e.string(),imgUrl:e.string()}),ee=e.object({templateUrl:e.string()}),te=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()}),oe=e.array(e.object({id:e.union([e.literal(1),e.literal(2),e.literal(3),e.literal(4)]),name:e.string()})),w=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:J.optional(),instructor:W.optional(),certificate:ee.optional(),reviews:e.array(te).optional(),lessonTabsOrder:oe.optional(),grading:e.boolean().optional(),lessonDownload:e.boolean().optional(),allowNewStudent:e.boolean(),sectionDisplay:e.record(e.string(),e.boolean()).optional(),isContentGroupingEnabled:e.boolean().optional()}),et=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:J.partial().optional(),instructor:W.partial().optional(),certificate:ee.partial().optional(),reviews:e.array(te).optional(),lessonTabsOrder:oe.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()}),qr=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:w.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()}),Br=e.object({courseId:e.string().min(1)}),re=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:et.optional()}),Qr=e.object({courseId:e.string().min(1)}),Yr=e.object({courseId:e.string().min(1)}),Xr=e.object({profileId:e.string().uuid()}),$r=e.object({courseId:e.string().min(1),userId:e.string().uuid()});var ne=r.enum(["en","hi","fr","pt","de","vi","ru","es","pl","da"]),tt="Lesson body HTML only. Do not include the lesson title or a top-level heading that repeats lessons[].title because the ClassroomIO UI already renders the lesson title separately.",se=r.object({code:r.string().min(1),message:r.string().min(1),severity:r.enum(["info","warning","error"])}),ot=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()}),rt=r.object({title:r.string().min(1),description:r.string().min(1),type:r.enum(["LIVE_CLASS","SELF_PACED"]),locale:ne.default("en"),metadata:w.optional()}),nt=r.object({externalId:r.string().min(1),title:r.string().min(1),order:r.number().int().min(0)}),st=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()}),at=r.object({lessonExternalId:r.string().min(1),locale:ne,content:r.string().min(1).describe(tt)}),ae=r.object({course:rt,tags:r.array(r.string().trim().min(1).max(80)).max(100).default([]),sections:r.array(nt).min(1),lessons:r.array(st).min(1),lessonLanguages:r.array(at).min(1),exercises:r.array(r.record(r.string(),r.unknown())).optional(),sourceReferences:r.array(ot).optional(),warnings:r.array(se).default([])}).superRefine((o,t)=>{let n=new Set;o.tags.forEach((l,I)=>{let k=l.trim().toLowerCase();n.has(k)&&t.addIssue({code:r.ZodIssueCode.custom,path:["tags",I],message:"Draft tags must be unique"}),n.add(k)});let i=new Set;o.sections.forEach((l,I)=>{i.has(l.externalId)&&t.addIssue({code:r.ZodIssueCode.custom,path:["sections",I,"externalId"],message:"Section externalId must be unique"}),i.add(l.externalId)});let u=new Set;o.lessons.forEach((l,I)=>{u.has(l.externalId)&&t.addIssue({code:r.ZodIssueCode.custom,path:["lessons",I,"externalId"],message:"Lesson externalId must be unique"}),u.add(l.externalId),i.has(l.sectionExternalId)||t.addIssue({code:r.ZodIssueCode.custom,path:["lessons",I,"sectionExternalId"],message:"Lesson sectionExternalId must reference an existing section"})}),o.lessonLanguages.forEach((l,I)=>{u.has(l.lessonExternalId)||t.addIssue({code:r.ZodIssueCode.custom,path:["lessonLanguages",I,"lessonExternalId"],message:"Lesson language must reference an existing lesson"})})}),j=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:ae}),y=r.object({courseId:r.string().min(1)}),v=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()}),T=r.object({draftId:r.string().uuid()}),ie=r.object({summary:r.record(r.string(),r.unknown()).optional(),sourceArtifacts:r.array(r.record(r.string(),r.unknown())).optional(),warnings:r.array(se).optional(),draft:ae.optional()}),G=r.object({title:r.string().min(1).optional(),description:r.string().min(1).optional(),type:r.enum(["LIVE_CLASS","SELF_PACED"]).optional(),metadata:w.optional(),bannerImageUrl:r.string().url().optional(),bannerImageQuery:r.string().min(1).max(120).optional(),generateBannerImage:r.boolean().optional()}),ue=G.extend({courseId:r.string().min(1)});import*as z from"zod";var on=z.object({title:z.string().min(1),courseId:z.string().min(1),order:z.number().int().min(0).optional()}),rn=z.object({title:z.string().min(1).optional(),order:z.number().int().min(0).optional()}),nn=z.object({title:z.string().min(1)}),sn=z.object({sectionId:z.string().min(1)}),an=z.object({sections:z.array(z.object({id:z.string().min(1),order:z.number().int().min(0)})).min(1)});import*as d from"zod";var pn=d.object({courseId:d.string().min(1)}),cn=d.object({courseId:d.string().min(1),inviteId:d.string().uuid()}),ln=d.object({courseId:d.string().min(1),inviteId:d.string().uuid()}),dn=d.object({token:d.string().min(1)}),mn=d.object({courseId:d.uuid()}),it=d.enum(["ONE_TIME_24H","MULTI_USE_7D","MULTI_USE_30D","CUSTOM"]),gn=d.object({preset:it.default("MULTI_USE_30D"),expiresAt:d.string().min(1).optional(),maxUses:d.number().int().min(1).max(1e3).optional(),allowedEmails:d.array(d.string().email()).max(100).optional(),allowedDomains:d.array(d.string().min(1)).max(100).optional(),recipientEmails:d.array(d.string().email()).max(500).optional(),recipientCsv:d.string().max(25e3).optional(),sendEmail:d.boolean().default(!1),metadata:d.record(d.string(),d.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 le=ie.extend({draftId:T.shape.draftId}),de=L.extend({draftId:K.shape.draftId}),me=$.extend({courseId:y.shape.courseId}),ge=U.extend({courseId:y.shape.courseId}),ze=Y.omit({courseId:!0}).extend({courseId:y.shape.courseId}),fe=V.extend({courseId:y.shape.courseId}),Ie=X.extend({courseId:y.shape.courseId,exerciseId:U.shape.exerciseId}),Ce=re.extend({courseId:y.shape.courseId}),xe=G.extend({draftId:T.shape.draftId}),ye=ue.extend({draftId:T.shape.draftId}),ut=j.shape,pt=v.shape,ct=D.shape,lt=y.shape,dt=T.shape,mt=le.shape,gt=de.shape,zt=me.shape,ft=ge.shape,It=ze.shape,Ct=fe.shape,xt=Ie.shape,yt=Ce.shape,Et=xe.shape,bt=ye.shape,Tt=R.shape,pe="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.",ce="For draft lesson HTML, put only the lesson body in lessonLanguages[].content. Do not include the lesson title or a top-level heading that repeats lessons[].title because the ClassroomIO UI already renders the lesson title.";function Ee(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.",ct,async n=>{let i=D.parse(n),u=await t.listOrganizationCourses(i);return f(u)}),o.tool("get_course_structure","Read the current live course structure. Use this before creating a fresh draft for post-publish edits.",lt,async n=>{let{courseId:i}=y.parse(n),u=await t.getCourseStructure(i);return f(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.",yt,async n=>{let{courseId:i,...u}=Ce.parse(n),l=await t.updateCourseLandingPage(i,u);return f(l)}),o.tool("create_course_draft",`Create a new unpublished course draft from structured course JSON. Use this before the first publish. ${ce}`,ut,async n=>{let i=j.parse(n),u=await t.createCourseDraft(i);return f(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.",pt,async n=>{let i=v.parse(n),u=await t.createCourseDraftFromCourse(i);return f(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.",dt,async n=>{let{draftId:i}=T.parse(n),u=await t.getCourseDraft(i);return f(u)}),o.tool("list_course_exercises","List exercises for a live course, optionally filtered by lessonId or sectionId.",zt,async n=>{let{courseId:i,...u}=me.parse(n),l=await t.listCourseExercises(i,u);return f(l)}),o.tool("get_course_exercise","Fetch a single exercise from a live course, including its questions and options.",ft,async n=>{let{courseId:i,exerciseId:u}=ge.parse(n),l=await t.getCourseExercise(i,u);return f(l)}),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. ${pe}`,It,async n=>{let{courseId:i,...u}=ze.parse(n),l=await t.createCourseExercise(i,u);return f(l)}),o.tool("create_course_exercise_from_template","Create a new exercise on a live course from an existing template.",Ct,async n=>{let{courseId:i,...u}=fe.parse(n),l=await t.createCourseExerciseFromTemplate(i,u);return f(l)}),o.tool("update_course_exercise",`Update an existing exercise on a live course, including questions and options. ${pe}`,xt,async n=>{let{courseId:i,exerciseId:u,...l}=Ie.parse(n),I=await t.updateCourseExercise(i,u,l);return f(I)}),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. ${ce}`,mt,async n=>{let{draftId:i,...u}=le.parse(n),l=await t.updateCourseDraft(i,u);return f(l)}),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.",gt,async n=>{let{draftId:i,...u}=de.parse(n),l=await t.tagCourseDraft(i,u);return f(l)}),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.",Et,async n=>{let{draftId:i,...u}=xe.parse(n),l=await t.publishCourseDraft(i,u);return f(l)}),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.",bt,async n=>{let{draftId:i,...u}=ye.parse(n),l=await t.publishCourseDraftToExistingCourse(i,u);return f(l)}),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.",Tt,async n=>{let i=R.parse(n),u=await t.tagCourses(i);return f(u)})}function f(o){return{content:[{type:"text",text:JSON.stringify(o)}]}}async function Ot(){let o=F(),t=new S(o),n=new ht({name:"classroomio-course-authoring",version:"0.0.1"});Ee(n,t);let i=new St;await n.connect(i),process.stdin.on("close",()=>{n.close()})}Ot().catch(o=>{o instanceof b&&(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)});
|