@learnpack/learnpack 5.0.312 → 5.0.315
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/lib/commands/serve.js +366 -293
- package/lib/utils/creatorUtilities.d.ts +2 -0
- package/lib/utils/creatorUtilities.js +37 -14
- package/lib/utils/rigoActions.d.ts +7 -0
- package/lib/utils/rigoActions.js +17 -1
- package/package.json +1 -1
- package/src/commands/serve.ts +3204 -3179
- package/src/utils/creatorUtilities.ts +536 -504
- package/src/utils/rigoActions.ts +29 -0
package/lib/commands/serve.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.processImage = exports.createLearnJson = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
require("newrelic");
|
|
6
5
|
const command_1 = require("@oclif/command");
|
|
7
6
|
const buffer_1 = require("buffer");
|
|
8
7
|
const express = require("express");
|
|
@@ -34,6 +33,9 @@ const sidebarGenerator_1 = require("../utils/sidebarGenerator");
|
|
|
34
33
|
const publish_1 = require("./publish");
|
|
35
34
|
const export_1 = require("../utils/export");
|
|
36
35
|
const frontMatter = require("front-matter");
|
|
36
|
+
if (process.env.NEW_RELIC_ENABLED === "true") {
|
|
37
|
+
require("newrelic");
|
|
38
|
+
}
|
|
37
39
|
dotenv.config();
|
|
38
40
|
const createLearnJson = (courseInfo) => {
|
|
39
41
|
// console.log("courseInfo to create learn json", courseInfo)
|
|
@@ -75,18 +77,18 @@ async function fetchComponentsYml() {
|
|
|
75
77
|
axios_1.default.get("https://raw.githubusercontent.com/learnpack/ide/refs/heads/master/docs/assessment_components.yml"),
|
|
76
78
|
axios_1.default.get("https://raw.githubusercontent.com/learnpack/ide/refs/heads/master/docs/explanatory_components.yml"),
|
|
77
79
|
]);
|
|
78
|
-
const combinedContent = `
|
|
79
|
-
# ASSESSMENT COMPONENTS
|
|
80
|
-
These components are designed for evaluation and knowledge assessment:
|
|
81
|
-
|
|
82
|
-
${assessmentResponse.data}
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
# EXPLANATORY COMPONENTS
|
|
87
|
-
These components are designed for explanation and learning support:
|
|
88
|
-
|
|
89
|
-
${explanatoryResponse.data}
|
|
80
|
+
const combinedContent = `
|
|
81
|
+
# ASSESSMENT COMPONENTS
|
|
82
|
+
These components are designed for evaluation and knowledge assessment:
|
|
83
|
+
|
|
84
|
+
${assessmentResponse.data}
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
# EXPLANATORY COMPONENTS
|
|
89
|
+
These components are designed for explanation and learning support:
|
|
90
|
+
|
|
91
|
+
${explanatoryResponse.data}
|
|
90
92
|
`;
|
|
91
93
|
return combinedContent;
|
|
92
94
|
}
|
|
@@ -120,10 +122,10 @@ const createInitialSidebar = async (slugs, initialLanguage = "en") => {
|
|
|
120
122
|
return sidebar;
|
|
121
123
|
};
|
|
122
124
|
const uploadInitialReadme = async (bucket, exSlug, targetDir, packageContext) => {
|
|
123
|
-
const isGeneratingText = `
|
|
124
|
-
\`\`\`loader slug="${exSlug}"
|
|
125
|
-
:rigo
|
|
126
|
-
\`\`\`
|
|
125
|
+
const isGeneratingText = `
|
|
126
|
+
\`\`\`loader slug="${exSlug}"
|
|
127
|
+
:rigo
|
|
128
|
+
\`\`\`
|
|
127
129
|
`;
|
|
128
130
|
const readmeFilename = `README${(0, creatorUtilities_1.getReadmeExtension)(packageContext.language || "en")}`;
|
|
129
131
|
await uploadFileToBucket(bucket, isGeneratingText, `${targetDir}/${readmeFilename}`);
|
|
@@ -163,29 +165,13 @@ const createMultiLangAsset = async (bucket, rigoToken, bcToken, courseSlug, cour
|
|
|
163
165
|
all_translations.push(asset.slug);
|
|
164
166
|
}
|
|
165
167
|
};
|
|
166
|
-
async function startExerciseGeneration(rigoToken, steps, packageContext, exercise, courseSlug, purposeSlug, lastLesson = "") {
|
|
167
|
-
const exSlug = (0, creatorUtilities_2.slugify)(exercise.id + "-" + exercise.title);
|
|
168
|
-
console.log("Starting generation of", exSlug);
|
|
169
|
-
const webhookUrl = `${process.env.HOST}/webhooks/${courseSlug}/exercise-processor/${exercise.id}/${rigoToken}`;
|
|
170
|
-
const res = await (0, rigoActions_1.readmeCreator)(rigoToken.trim(), {
|
|
171
|
-
title: `${exercise.id} - ${exercise.title}`,
|
|
172
|
-
output_lang: packageContext.language || "en",
|
|
173
|
-
list_of_exercises: JSON.stringify(steps.map(step => step.id + "-" + step.title)),
|
|
174
|
-
tutorial_description: JSON.stringify(cleanFormState(packageContext)),
|
|
175
|
-
lesson_description: exercise.description,
|
|
176
|
-
kind: exercise.type.toLowerCase(),
|
|
177
|
-
last_lesson: lastLesson,
|
|
178
|
-
}, purposeSlug, webhookUrl);
|
|
179
|
-
console.log("README CREATOR RES", res);
|
|
180
|
-
return res.id;
|
|
181
|
-
}
|
|
182
168
|
const lessonCleaner = (lesson) => {
|
|
183
169
|
return Object.assign(Object.assign({}, lesson), { duration: undefined, generated: undefined, status: undefined, translations: undefined, uid: undefined, initialContent: undefined, locked: undefined });
|
|
184
170
|
};
|
|
185
171
|
async function startInitialContentGeneration(rigoToken, steps, packageContext, exercise, courseSlug, purposeSlug, lastLesson = "") {
|
|
186
172
|
const exSlug = (0, creatorUtilities_2.slugify)(exercise.id + "-" + exercise.title);
|
|
187
173
|
console.log("Starting initial content generation for", exSlug);
|
|
188
|
-
const webhookUrl = `${process.env.HOST}/webhooks/${courseSlug}/initial-content-processor/${exercise.
|
|
174
|
+
const webhookUrl = `${process.env.HOST}/webhooks/${courseSlug}/initial-content-processor/${exercise.uid}/${rigoToken}`;
|
|
189
175
|
// Emit notification that initial content generation is starting
|
|
190
176
|
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
191
177
|
lesson: exSlug,
|
|
@@ -211,7 +197,7 @@ async function startInitialContentGeneration(rigoToken, steps, packageContext, e
|
|
|
211
197
|
async function startInteractivityGeneration(rigoToken, steps, packageContext, exercise, courseSlug, purposeSlug, bucket, lastLesson = "") {
|
|
212
198
|
const exSlug = (0, creatorUtilities_2.slugify)(exercise.id + "-" + exercise.title);
|
|
213
199
|
console.log("Starting interactivity generation for", exSlug);
|
|
214
|
-
const webhookUrl = `${process.env.HOST}/webhooks/${courseSlug}/interactivity-processor/${exercise.
|
|
200
|
+
const webhookUrl = `${process.env.HOST}/webhooks/${courseSlug}/interactivity-processor/${exercise.uid}/${rigoToken}`;
|
|
215
201
|
// Emit notification that interactivity generation is starting
|
|
216
202
|
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
217
203
|
lesson: exSlug,
|
|
@@ -219,7 +205,6 @@ async function startInteractivityGeneration(rigoToken, steps, packageContext, ex
|
|
|
219
205
|
log: `🔄 Starting interactivity generation for lesson: ${exercise.title}`,
|
|
220
206
|
});
|
|
221
207
|
const componentsYml = await fetchComponentsYml();
|
|
222
|
-
// Get current syllabus to include used_components
|
|
223
208
|
const currentSyllabus = await getSyllabus(courseSlug, bucket);
|
|
224
209
|
const fullSyllabus = {
|
|
225
210
|
steps: steps.map(lessonCleaner),
|
|
@@ -236,7 +221,6 @@ async function startInteractivityGeneration(rigoToken, steps, packageContext, ex
|
|
|
236
221
|
output_language: packageContext.language || "en",
|
|
237
222
|
current_syllabus: JSON.stringify(fullSyllabus),
|
|
238
223
|
}, webhookUrl);
|
|
239
|
-
console.log("INTERACTIVITY GENERATOR RES", res);
|
|
240
224
|
return res.id;
|
|
241
225
|
}
|
|
242
226
|
async function createInitialReadme(tutorialInfo, tutorialSlug, rigoToken) {
|
|
@@ -252,6 +236,11 @@ async function createInitialReadme(tutorialInfo, tutorialSlug, rigoToken) {
|
|
|
252
236
|
console.error("Error creating initial readme", error);
|
|
253
237
|
}
|
|
254
238
|
}
|
|
239
|
+
const getConfigJSON = async (bucket, courseSlug) => {
|
|
240
|
+
const configFile = await bucket.file(`courses/${courseSlug}/.learn/config.json`);
|
|
241
|
+
const [content] = await configFile.download();
|
|
242
|
+
return JSON.parse(content.toString());
|
|
243
|
+
};
|
|
255
244
|
async function getSyllabus(courseSlug, bucket) {
|
|
256
245
|
const syllabus = await bucket.file(`courses/${courseSlug}/.learn/initialSyllabus.json`);
|
|
257
246
|
const [content] = await syllabus.download();
|
|
@@ -278,24 +267,25 @@ async function updateUsedComponents(courseSlug, usedComponents, bucket) {
|
|
|
278
267
|
console.log("Updated component usage:", syllabus.used_components);
|
|
279
268
|
await saveSyllabus(courseSlug, syllabus, bucket);
|
|
280
269
|
}
|
|
281
|
-
async function updateLessonWithInitialContent(courseSlug,
|
|
270
|
+
async function updateLessonWithInitialContent(courseSlug, lessonUID, initialResponse, bucket) {
|
|
282
271
|
const syllabus = await getSyllabus(courseSlug, bucket);
|
|
283
|
-
const lessonIndex = syllabus.lessons.findIndex(lesson => lesson.
|
|
272
|
+
const lessonIndex = syllabus.lessons.findIndex(lesson => lesson.uid === lessonUID);
|
|
284
273
|
if (lessonIndex === -1) {
|
|
285
|
-
console.error(`Lesson ${
|
|
286
|
-
return;
|
|
274
|
+
console.error(`Lesson ${lessonUID} not found in syllabus`);
|
|
275
|
+
return null;
|
|
287
276
|
}
|
|
288
277
|
const lesson = syllabus.lessons[lessonIndex];
|
|
289
278
|
// Update initial content
|
|
290
279
|
lesson.initialContent = initialResponse.lesson_content;
|
|
291
280
|
await saveSyllabus(courseSlug, syllabus, bucket);
|
|
281
|
+
return lesson;
|
|
292
282
|
}
|
|
293
|
-
async function updateLessonStatusToError(courseSlug,
|
|
283
|
+
async function updateLessonStatusToError(courseSlug, lessonUID, bucket) {
|
|
294
284
|
try {
|
|
295
285
|
const syllabus = await getSyllabus(courseSlug, bucket);
|
|
296
|
-
const lessonIndex = syllabus.lessons.findIndex(lesson => lesson.
|
|
286
|
+
const lessonIndex = syllabus.lessons.findIndex(lesson => lesson.uid === lessonUID);
|
|
297
287
|
if (lessonIndex === -1) {
|
|
298
|
-
console.error(`Lesson ${
|
|
288
|
+
console.error(`Lesson ${lessonUID} not found in syllabus when updating status to error`);
|
|
299
289
|
return;
|
|
300
290
|
}
|
|
301
291
|
const lesson = syllabus.lessons[lessonIndex];
|
|
@@ -316,10 +306,10 @@ async function updateLessonStatusToError(courseSlug, lessonID, bucket) {
|
|
|
316
306
|
}
|
|
317
307
|
lesson.translations = currentTranslations;
|
|
318
308
|
await saveSyllabus(courseSlug, syllabus, bucket);
|
|
319
|
-
console.log(`Updated lesson ${
|
|
309
|
+
console.log(`Updated lesson ${lessonUID} status to ERROR in syllabus`);
|
|
320
310
|
}
|
|
321
311
|
catch (error) {
|
|
322
|
-
console.error(`Error updating lesson ${
|
|
312
|
+
console.error(`Error updating lesson ${lessonUID} status to ERROR:`, error);
|
|
323
313
|
}
|
|
324
314
|
}
|
|
325
315
|
async function continueWithNextLesson(courseSlug, currentExerciseIndex, rigoToken, finalContent, bucket) {
|
|
@@ -687,18 +677,23 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
687
677
|
res.status(500).json({ error: error.message });
|
|
688
678
|
}
|
|
689
679
|
});
|
|
690
|
-
app.post("/actions/continue-generating/:courseSlug/:
|
|
691
|
-
const { courseSlug,
|
|
680
|
+
app.post("/actions/continue-generating/:courseSlug/:lessonUid", async (req, res) => {
|
|
681
|
+
const { courseSlug, lessonUid } = req.params;
|
|
692
682
|
const { feedback, mode } = req.body;
|
|
693
683
|
const rigoToken = req.header("x-rigo-token");
|
|
684
|
+
console.log("CONTINUE GENERATING REQUEST RECEIVED");
|
|
685
|
+
// console.log("COURSE SLUG", courseSlug);
|
|
686
|
+
console.log("LESSON UID", lessonUid);
|
|
687
|
+
// console.log("FEEDBACK", feedback);
|
|
688
|
+
// console.log("MODE", mode);
|
|
694
689
|
if (!rigoToken) {
|
|
695
690
|
return res.status(400).json({
|
|
696
691
|
error: "Rigo token is required. x-rigo-token header is missing",
|
|
697
692
|
});
|
|
698
693
|
}
|
|
699
694
|
const syllabus = await getSyllabus(courseSlug, bucket);
|
|
700
|
-
const exercise = syllabus.lessons.find(lesson => lesson.
|
|
701
|
-
const exerciseIndex = syllabus.lessons.findIndex(lesson => lesson.
|
|
695
|
+
const exercise = syllabus.lessons.find(lesson => lesson.uid === lessonUid);
|
|
696
|
+
const exerciseIndex = syllabus.lessons.findIndex(lesson => lesson.uid === lessonUid);
|
|
702
697
|
// previous exercise
|
|
703
698
|
let previousReadme = "---";
|
|
704
699
|
const previousExercise = syllabus.lessons[exerciseIndex - 1];
|
|
@@ -817,189 +812,255 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
817
812
|
});
|
|
818
813
|
}
|
|
819
814
|
});
|
|
820
|
-
app.post(
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
815
|
+
// app.post(
|
|
816
|
+
// "/webhooks/:courseSlug/exercise-processor/:lessonID/:rigoToken",
|
|
817
|
+
// async (req, res) => {
|
|
818
|
+
// // console.log("Receiving a webhook to exercise processor")
|
|
819
|
+
// const { courseSlug, lessonID, rigoToken } = req.params
|
|
820
|
+
// const readme = req.body
|
|
821
|
+
// const syllabus = await bucket.file(
|
|
822
|
+
// `courses/${courseSlug}/.learn/initialSyllabus.json`
|
|
823
|
+
// )
|
|
824
|
+
// const [content] = await syllabus.download()
|
|
825
|
+
// const syllabusJson: Syllabus = JSON.parse(content.toString())
|
|
826
|
+
// if (readme.status === "ERROR") {
|
|
827
|
+
// emitToCourse(courseSlug, "course-creation", {
|
|
828
|
+
// lesson: lessonID,
|
|
829
|
+
// status: "error",
|
|
830
|
+
// log: `❌ Error generating the lesson ${lessonID}`,
|
|
831
|
+
// })
|
|
832
|
+
// }
|
|
833
|
+
// const exerciseIndex = syllabusJson.lessons.findIndex(
|
|
834
|
+
// lesson => lesson.id === lessonID
|
|
835
|
+
// )
|
|
836
|
+
// if (exerciseIndex === -1) {
|
|
837
|
+
// console.log(
|
|
838
|
+
// "Exercise not found receiving webhook, this should not happen",
|
|
839
|
+
// lessonID
|
|
840
|
+
// )
|
|
841
|
+
// return res.json({ status: "ERROR", error: "Exercise not found" })
|
|
842
|
+
// }
|
|
843
|
+
// const exercise = syllabusJson.lessons[exerciseIndex]
|
|
844
|
+
// if (!exercise) {
|
|
845
|
+
// return res.json({
|
|
846
|
+
// status: "ERROR",
|
|
847
|
+
// error: "Exercise not found or is invalid",
|
|
848
|
+
// })
|
|
849
|
+
// }
|
|
850
|
+
// const nextExercise = syllabusJson.lessons[exerciseIndex + 1] || null
|
|
851
|
+
// const exSlug = slugify(exercise.id + "-" + exercise.title)
|
|
852
|
+
// const readability = checkReadability(
|
|
853
|
+
// readme.parsed.content,
|
|
854
|
+
// PARAMS.max_words,
|
|
855
|
+
// 3
|
|
856
|
+
// )
|
|
857
|
+
// emitToCourse(courseSlug, "course-creation", {
|
|
858
|
+
// lesson: exSlug,
|
|
859
|
+
// status: "generating",
|
|
860
|
+
// log: `🔄 The lesson ${exercise.title} has a readability score of ${readability.fkglResult.fkgl}`,
|
|
861
|
+
// })
|
|
862
|
+
// const exercisesDir = `courses/${courseSlug}/exercises`
|
|
863
|
+
// const targetDir = `${exercisesDir}/${exSlug}`
|
|
864
|
+
// const readmeFilename = `README${getReadmeExtension(
|
|
865
|
+
// readme.parsed.language_code
|
|
866
|
+
// )}`
|
|
867
|
+
// await uploadFileToBucket(
|
|
868
|
+
// bucket,
|
|
869
|
+
// readability.newMarkdown,
|
|
870
|
+
// `${targetDir}/${readmeFilename}`
|
|
871
|
+
// )
|
|
872
|
+
// if (
|
|
873
|
+
// exercise.type.toLowerCase() === "code" &&
|
|
874
|
+
// readme.parsed.codefile_content
|
|
875
|
+
// ) {
|
|
876
|
+
// emitToCourse(courseSlug, "course-creation", {
|
|
877
|
+
// lesson: exSlug,
|
|
878
|
+
// status: "generating",
|
|
879
|
+
// log: `🔄 Creating code file for ${exercise.title}`,
|
|
880
|
+
// })
|
|
881
|
+
// await uploadFileToBucket(
|
|
882
|
+
// bucket,
|
|
883
|
+
// readme.parsed.codefile_content,
|
|
884
|
+
// `${targetDir}/${readme.parsed.codefile_name.toLowerCase().trim()}`
|
|
885
|
+
// )
|
|
886
|
+
// emitToCourse(courseSlug, "course-creation", {
|
|
887
|
+
// lesson: exSlug,
|
|
888
|
+
// status: "generating",
|
|
889
|
+
// log: `✅ Code file created for ${exercise.title}`,
|
|
890
|
+
// })
|
|
891
|
+
// if (readme.parsed.solution_content) {
|
|
892
|
+
// const codeFileName = readme.parsed.codefile_name
|
|
893
|
+
// .toLowerCase()
|
|
894
|
+
// .trim()
|
|
895
|
+
// const solutionFileName = "solution.hide." + codeFileName
|
|
896
|
+
// await uploadFileToBucket(
|
|
897
|
+
// bucket,
|
|
898
|
+
// readme.parsed.solution_content,
|
|
899
|
+
// `${targetDir}/${solutionFileName}`
|
|
900
|
+
// )
|
|
901
|
+
// emitToCourse(courseSlug, "course-creation", {
|
|
902
|
+
// lesson: exSlug,
|
|
903
|
+
// status: "generating",
|
|
904
|
+
// log: `✅ Solution file created for ${exercise.title}`,
|
|
905
|
+
// })
|
|
906
|
+
// }
|
|
907
|
+
// }
|
|
908
|
+
// let nextCompletionId: number | null = null
|
|
909
|
+
// if (
|
|
910
|
+
// nextExercise &&
|
|
911
|
+
// (exerciseIndex === 0 ||
|
|
912
|
+
// !(exerciseIndex % 3 === 0) ||
|
|
913
|
+
// syllabusJson.generationMode === "continue-with-all")
|
|
914
|
+
// ) {
|
|
915
|
+
// let feedback = ""
|
|
916
|
+
// if (syllabusJson.feedback) {
|
|
917
|
+
// feedback = `\n\nThe user added the following feedback with relation to the previous generations: ${syllabusJson.feedback}`
|
|
918
|
+
// }
|
|
919
|
+
// nextCompletionId = await startExerciseGeneration(
|
|
920
|
+
// rigoToken,
|
|
921
|
+
// syllabusJson.lessons,
|
|
922
|
+
// syllabusJson.courseInfo,
|
|
923
|
+
// nextExercise,
|
|
924
|
+
// courseSlug,
|
|
925
|
+
// syllabusJson.courseInfo.purpose,
|
|
926
|
+
// readme.parsed.content + "\n\n" + feedback
|
|
927
|
+
// )
|
|
928
|
+
// } else {
|
|
929
|
+
// console.log(
|
|
930
|
+
// "Stopping generation process at",
|
|
931
|
+
// exerciseIndex,
|
|
932
|
+
// exercise.title,
|
|
933
|
+
// "because it's a multiple of 3"
|
|
934
|
+
// )
|
|
935
|
+
// }
|
|
936
|
+
// const newSyllabus = {
|
|
937
|
+
// ...syllabusJson,
|
|
938
|
+
// lessons: syllabusJson.lessons.map((lesson, index) => {
|
|
939
|
+
// if (index === exerciseIndex) {
|
|
940
|
+
// const currentTranslations = lesson.translations || {}
|
|
941
|
+
// let currentTranslation =
|
|
942
|
+
// currentTranslations[syllabusJson.courseInfo.language || "en"]
|
|
943
|
+
// if (currentTranslation) {
|
|
944
|
+
// currentTranslation.completedAt = Date.now()
|
|
945
|
+
// } else {
|
|
946
|
+
// currentTranslation = {
|
|
947
|
+
// completionId: readme.id,
|
|
948
|
+
// startedAt: Date.now(),
|
|
949
|
+
// completedAt: Date.now(),
|
|
950
|
+
// }
|
|
951
|
+
// }
|
|
952
|
+
// currentTranslations[syllabusJson.courseInfo.language || "en"] =
|
|
953
|
+
// currentTranslation
|
|
954
|
+
// return {
|
|
955
|
+
// ...lesson,
|
|
956
|
+
// generated: true,
|
|
957
|
+
// status: "DONE",
|
|
958
|
+
// translations: {
|
|
959
|
+
// [syllabusJson.courseInfo.language || "en"]: {
|
|
960
|
+
// completionId: nextCompletionId,
|
|
961
|
+
// completedAt: Date.now(),
|
|
962
|
+
// },
|
|
963
|
+
// },
|
|
964
|
+
// }
|
|
965
|
+
// }
|
|
966
|
+
// if (
|
|
967
|
+
// nextExercise &&
|
|
968
|
+
// nextExercise.id === lesson.id &&
|
|
969
|
+
// nextCompletionId
|
|
970
|
+
// ) {
|
|
971
|
+
// return {
|
|
972
|
+
// ...lesson,
|
|
973
|
+
// generated: false,
|
|
974
|
+
// status: "GENERATING",
|
|
975
|
+
// translations: {
|
|
976
|
+
// [syllabusJson.courseInfo.language || "en"]: {
|
|
977
|
+
// completionId: nextCompletionId,
|
|
978
|
+
// startedAt: Date.now(),
|
|
979
|
+
// },
|
|
980
|
+
// },
|
|
981
|
+
// }
|
|
982
|
+
// }
|
|
983
|
+
// return { ...lesson }
|
|
984
|
+
// }),
|
|
985
|
+
// }
|
|
986
|
+
// console.log("New syllabus", newSyllabus)
|
|
987
|
+
// await uploadFileToBucket(
|
|
988
|
+
// bucket,
|
|
989
|
+
// JSON.stringify(newSyllabus),
|
|
990
|
+
// `courses/${courseSlug}/.learn/initialSyllabus.json`
|
|
991
|
+
// )
|
|
992
|
+
// emitToCourse(courseSlug, "course-creation", {
|
|
993
|
+
// lesson: exSlug,
|
|
994
|
+
// status: "done",
|
|
995
|
+
// log: `✅ The lesson ${exercise.id} - ${exercise.title} has been generated successfully!`,
|
|
996
|
+
// })
|
|
997
|
+
// res.json({ status: "SUCCESS" })
|
|
998
|
+
// }
|
|
999
|
+
// )
|
|
1000
|
+
// Phase 1: Initial content generation webhook
|
|
1001
|
+
app.post("/webhooks/:courseSlug/initial-content-processor/:lessonUID/:rigoToken", async (req, res) => {
|
|
1002
|
+
const { courseSlug, lessonUID, rigoToken } = req.params;
|
|
1003
|
+
const response = req.body;
|
|
1004
|
+
console.log("RECEIVING INITIAL CONTENT WEBHOOK", response);
|
|
1005
|
+
const syllabus = await getSyllabus(courseSlug, bucket);
|
|
1006
|
+
const exerciseIndex = syllabus.lessons.findIndex(lesson => lesson.uid === lessonUID);
|
|
835
1007
|
if (exerciseIndex === -1) {
|
|
836
|
-
console.
|
|
1008
|
+
console.error("Exercise not found receiving webhook:", lessonUID);
|
|
837
1009
|
return res.json({ status: "ERROR", error: "Exercise not found" });
|
|
838
1010
|
}
|
|
839
|
-
|
|
840
|
-
if (!exercise) {
|
|
841
|
-
return res.json({
|
|
842
|
-
status: "ERROR",
|
|
843
|
-
error: "Exercise not found or is invalid",
|
|
844
|
-
});
|
|
845
|
-
}
|
|
846
|
-
const nextExercise = syllabusJson.lessons[exerciseIndex + 1] || null;
|
|
1011
|
+
let exercise = syllabus.lessons[exerciseIndex];
|
|
847
1012
|
const exSlug = (0, creatorUtilities_2.slugify)(exercise.id + "-" + exercise.title);
|
|
848
|
-
const readability = (0, creatorUtilities_2.checkReadability)(readme.parsed.content, PARAMS.max_words, 3);
|
|
849
|
-
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
850
|
-
lesson: exSlug,
|
|
851
|
-
status: "generating",
|
|
852
|
-
log: `🔄 The lesson ${exercise.title} has a readability score of ${readability.fkglResult.fkgl}`,
|
|
853
|
-
});
|
|
854
|
-
const exercisesDir = `courses/${courseSlug}/exercises`;
|
|
855
|
-
const targetDir = `${exercisesDir}/${exSlug}`;
|
|
856
|
-
const readmeFilename = `README${(0, creatorUtilities_1.getReadmeExtension)(readme.parsed.language_code)}`;
|
|
857
|
-
await uploadFileToBucket(bucket, readability.newMarkdown, `${targetDir}/${readmeFilename}`);
|
|
858
|
-
if (exercise.type.toLowerCase() === "code" &&
|
|
859
|
-
readme.parsed.codefile_content) {
|
|
860
|
-
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
861
|
-
lesson: exSlug,
|
|
862
|
-
status: "generating",
|
|
863
|
-
log: `🔄 Creating code file for ${exercise.title}`,
|
|
864
|
-
});
|
|
865
|
-
await uploadFileToBucket(bucket, readme.parsed.codefile_content, `${targetDir}/${readme.parsed.codefile_name.toLowerCase().trim()}`);
|
|
866
|
-
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
867
|
-
lesson: exSlug,
|
|
868
|
-
status: "generating",
|
|
869
|
-
log: `✅ Code file created for ${exercise.title}`,
|
|
870
|
-
});
|
|
871
|
-
if (readme.parsed.solution_content) {
|
|
872
|
-
const codeFileName = readme.parsed.codefile_name
|
|
873
|
-
.toLowerCase()
|
|
874
|
-
.trim();
|
|
875
|
-
const solutionFileName = "solution.hide." + codeFileName;
|
|
876
|
-
await uploadFileToBucket(bucket, readme.parsed.solution_content, `${targetDir}/${solutionFileName}`);
|
|
877
|
-
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
878
|
-
lesson: exSlug,
|
|
879
|
-
status: "generating",
|
|
880
|
-
log: `✅ Solution file created for ${exercise.title}`,
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
let nextCompletionId = null;
|
|
885
|
-
if (nextExercise &&
|
|
886
|
-
(exerciseIndex === 0 ||
|
|
887
|
-
!(exerciseIndex % 3 === 0) ||
|
|
888
|
-
syllabusJson.generationMode === "continue-with-all")) {
|
|
889
|
-
let feedback = "";
|
|
890
|
-
if (syllabusJson.feedback) {
|
|
891
|
-
feedback = `\n\nThe user added the following feedback with relation to the previous generations: ${syllabusJson.feedback}`;
|
|
892
|
-
}
|
|
893
|
-
nextCompletionId = await startExerciseGeneration(rigoToken, syllabusJson.lessons, syllabusJson.courseInfo, nextExercise, courseSlug, syllabusJson.courseInfo.purpose, readme.parsed.content + "\n\n" + feedback);
|
|
894
|
-
}
|
|
895
|
-
else {
|
|
896
|
-
console.log("Stopping generation process at", exerciseIndex, exercise.title, "because it's a multiple of 3");
|
|
897
|
-
}
|
|
898
|
-
const newSyllabus = Object.assign(Object.assign({}, syllabusJson), { lessons: syllabusJson.lessons.map((lesson, index) => {
|
|
899
|
-
if (index === exerciseIndex) {
|
|
900
|
-
const currentTranslations = lesson.translations || {};
|
|
901
|
-
let currentTranslation = currentTranslations[syllabusJson.courseInfo.language || "en"];
|
|
902
|
-
if (currentTranslation) {
|
|
903
|
-
currentTranslation.completedAt = Date.now();
|
|
904
|
-
}
|
|
905
|
-
else {
|
|
906
|
-
currentTranslation = {
|
|
907
|
-
completionId: readme.id,
|
|
908
|
-
startedAt: Date.now(),
|
|
909
|
-
completedAt: Date.now(),
|
|
910
|
-
};
|
|
911
|
-
}
|
|
912
|
-
currentTranslations[syllabusJson.courseInfo.language || "en"] =
|
|
913
|
-
currentTranslation;
|
|
914
|
-
return Object.assign(Object.assign({}, lesson), { generated: true, status: "DONE", translations: {
|
|
915
|
-
[syllabusJson.courseInfo.language || "en"]: {
|
|
916
|
-
completionId: nextCompletionId,
|
|
917
|
-
completedAt: Date.now(),
|
|
918
|
-
},
|
|
919
|
-
} });
|
|
920
|
-
}
|
|
921
|
-
if (nextExercise &&
|
|
922
|
-
nextExercise.id === lesson.id &&
|
|
923
|
-
nextCompletionId) {
|
|
924
|
-
return Object.assign(Object.assign({}, lesson), { generated: false, status: "GENERATING", translations: {
|
|
925
|
-
[syllabusJson.courseInfo.language || "en"]: {
|
|
926
|
-
completionId: nextCompletionId,
|
|
927
|
-
startedAt: Date.now(),
|
|
928
|
-
},
|
|
929
|
-
} });
|
|
930
|
-
}
|
|
931
|
-
return Object.assign({}, lesson);
|
|
932
|
-
}) });
|
|
933
|
-
console.log("New syllabus", newSyllabus);
|
|
934
|
-
await uploadFileToBucket(bucket, JSON.stringify(newSyllabus), `courses/${courseSlug}/.learn/initialSyllabus.json`);
|
|
935
|
-
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
936
|
-
lesson: exSlug,
|
|
937
|
-
status: "done",
|
|
938
|
-
log: `✅ The lesson ${exercise.id} - ${exercise.title} has been generated successfully!`,
|
|
939
|
-
});
|
|
940
|
-
res.json({ status: "SUCCESS" });
|
|
941
|
-
});
|
|
942
|
-
// Phase 1: Initial content generation webhook
|
|
943
|
-
app.post("/webhooks/:courseSlug/initial-content-processor/:lessonID/:rigoToken", async (req, res) => {
|
|
944
|
-
const { courseSlug, lessonID, rigoToken } = req.params;
|
|
945
|
-
const response = req.body;
|
|
946
|
-
console.log("RECEIVING INITIAL CONTENT WEBHOOK", response);
|
|
947
1013
|
// Handle errors
|
|
948
1014
|
if (response.status === "ERROR") {
|
|
949
|
-
await updateLessonStatusToError(courseSlug,
|
|
1015
|
+
await updateLessonStatusToError(courseSlug, lessonUID, bucket);
|
|
950
1016
|
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
951
|
-
lesson:
|
|
1017
|
+
lesson: exSlug,
|
|
952
1018
|
status: "error",
|
|
953
|
-
log: `❌ Error generating initial content for lesson ${
|
|
1019
|
+
log: `❌ Error generating initial content for lesson ${exSlug}`,
|
|
954
1020
|
});
|
|
955
1021
|
// Retry initial content generation
|
|
956
1022
|
try {
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
completedAt: 0,
|
|
974
|
-
},
|
|
975
|
-
};
|
|
976
|
-
await saveSyllabus(courseSlug, syllabus, bucket);
|
|
977
|
-
}
|
|
1023
|
+
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
1024
|
+
lesson: exSlug,
|
|
1025
|
+
status: "generating",
|
|
1026
|
+
log: `🔄 Retrying initial content generation for lesson ${exSlug}`,
|
|
1027
|
+
});
|
|
1028
|
+
const retryCompletionId = await startInitialContentGeneration(rigoToken, syllabus.lessons, syllabus.courseInfo, exercise, courseSlug, syllabus.courseInfo.purpose, "");
|
|
1029
|
+
// Update lesson status to show it's retrying
|
|
1030
|
+
exercise.status = "GENERATING";
|
|
1031
|
+
exercise.translations = {
|
|
1032
|
+
[syllabus.courseInfo.language || "en"]: {
|
|
1033
|
+
completionId: retryCompletionId,
|
|
1034
|
+
startedAt: Date.now(),
|
|
1035
|
+
completedAt: 0,
|
|
1036
|
+
},
|
|
1037
|
+
};
|
|
1038
|
+
await saveSyllabus(courseSlug, syllabus, bucket);
|
|
978
1039
|
}
|
|
979
1040
|
catch (retryError) {
|
|
980
1041
|
console.error("Error retrying initial content generation:", retryError);
|
|
981
1042
|
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
982
|
-
lesson:
|
|
1043
|
+
lesson: lessonUID,
|
|
983
1044
|
status: "error",
|
|
984
|
-
log: `❌ Failed to retry initial content generation for lesson ${
|
|
1045
|
+
log: `❌ Failed to retry initial content generation for lesson ${lessonUID}`,
|
|
985
1046
|
});
|
|
986
1047
|
}
|
|
987
1048
|
return res.json({ status: "ERROR" });
|
|
988
1049
|
}
|
|
989
1050
|
try {
|
|
990
1051
|
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
991
|
-
lesson:
|
|
1052
|
+
lesson: exSlug,
|
|
992
1053
|
status: "generating",
|
|
993
|
-
log: `✅ Initial content generated for lesson ${
|
|
1054
|
+
log: `✅ Initial content generated for lesson ${exSlug}`,
|
|
994
1055
|
});
|
|
995
1056
|
// Update lesson with initial content
|
|
996
|
-
await updateLessonWithInitialContent(courseSlug,
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1057
|
+
exercise = await updateLessonWithInitialContent(courseSlug, lessonUID, response.parsed, bucket);
|
|
1058
|
+
if (!exercise) {
|
|
1059
|
+
console.error("Exercise not found after updating initial content");
|
|
1060
|
+
return res.json({ status: "ERROR", error: "Exercise not found" });
|
|
1061
|
+
}
|
|
1001
1062
|
let lastLesson = "";
|
|
1002
|
-
const prevLessonIndex =
|
|
1063
|
+
const prevLessonIndex = exerciseIndex - 1;
|
|
1003
1064
|
if (prevLessonIndex >= 0) {
|
|
1004
1065
|
try {
|
|
1005
1066
|
const prevLesson = syllabus.lessons[prevLessonIndex];
|
|
@@ -1013,38 +1074,36 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
1013
1074
|
console.error("Error searching previous lesson content:", error);
|
|
1014
1075
|
}
|
|
1015
1076
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
await saveSyllabus(courseSlug, syllabus, bucket);
|
|
1028
|
-
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
1029
|
-
lesson: lessonID,
|
|
1030
|
-
status: "generating",
|
|
1031
|
-
log: `🔄 Starting interactivity phase for lesson ${exercise.title}`,
|
|
1032
|
-
});
|
|
1033
|
-
}
|
|
1077
|
+
const completionId = await startInteractivityGeneration(rigoToken, syllabus.lessons, syllabus.courseInfo, exercise, courseSlug, syllabus.courseInfo.purpose, bucket, lastLesson);
|
|
1078
|
+
// Update lesson status to show it's in Phase 2
|
|
1079
|
+
exercise.status = "GENERATING";
|
|
1080
|
+
exercise.translations = {
|
|
1081
|
+
[syllabus.courseInfo.language || "en"]: {
|
|
1082
|
+
completionId,
|
|
1083
|
+
startedAt: Date.now(),
|
|
1084
|
+
completedAt: 0,
|
|
1085
|
+
},
|
|
1086
|
+
};
|
|
1087
|
+
await saveSyllabus(courseSlug, syllabus, bucket);
|
|
1034
1088
|
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
1035
|
-
lesson:
|
|
1089
|
+
lesson: exSlug,
|
|
1090
|
+
status: "generating",
|
|
1091
|
+
log: `🔄 Starting interactivity phase for lesson ${exercise.title}`,
|
|
1092
|
+
});
|
|
1093
|
+
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
1094
|
+
lesson: exSlug,
|
|
1036
1095
|
status: "initial-content-complete",
|
|
1037
|
-
log: `✅ Initial content generated for lesson ${
|
|
1096
|
+
log: `✅ Initial content generated for lesson ${exSlug}, starting interactivity phase`,
|
|
1038
1097
|
});
|
|
1039
1098
|
res.json({ status: "SUCCESS" });
|
|
1040
1099
|
}
|
|
1041
1100
|
catch (error) {
|
|
1042
1101
|
console.error("Error processing initial content webhook:", error);
|
|
1043
|
-
await updateLessonStatusToError(courseSlug,
|
|
1102
|
+
await updateLessonStatusToError(courseSlug, lessonUID, bucket);
|
|
1044
1103
|
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
1045
|
-
lesson:
|
|
1104
|
+
lesson: exSlug,
|
|
1046
1105
|
status: "error",
|
|
1047
|
-
log: `❌ Error processing initial content for lesson ${
|
|
1106
|
+
log: `❌ Error processing initial content for lesson ${exSlug}`,
|
|
1048
1107
|
});
|
|
1049
1108
|
res
|
|
1050
1109
|
.status(500)
|
|
@@ -1052,28 +1111,30 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
1052
1111
|
}
|
|
1053
1112
|
});
|
|
1054
1113
|
// Phase 2: Interactivity generation webhook (replaces exercise-processor logic)
|
|
1055
|
-
app.post("/webhooks/:courseSlug/interactivity-processor/:
|
|
1056
|
-
const { courseSlug,
|
|
1114
|
+
app.post("/webhooks/:courseSlug/interactivity-processor/:lessonUID/:rigoToken", async (req, res) => {
|
|
1115
|
+
const { courseSlug, lessonUID, rigoToken } = req.params;
|
|
1057
1116
|
const response = req.body;
|
|
1058
|
-
console.log("RECEIVING INTERACTIVITY WEBHOOK"
|
|
1117
|
+
console.log("RECEIVING INTERACTIVITY WEBHOOK");
|
|
1118
|
+
// console.log("LESSON UID", lessonUID)
|
|
1119
|
+
// console.log("RESPONSE", response)
|
|
1059
1120
|
// Handle errors
|
|
1060
1121
|
if (response.status === "ERROR") {
|
|
1061
|
-
await updateLessonStatusToError(courseSlug,
|
|
1122
|
+
await updateLessonStatusToError(courseSlug, lessonUID, bucket);
|
|
1062
1123
|
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
1063
|
-
lesson:
|
|
1124
|
+
lesson: lessonUID,
|
|
1064
1125
|
status: "error",
|
|
1065
|
-
log: `❌ Error adding interactivity to lesson ${
|
|
1126
|
+
log: `❌ Error adding interactivity to lesson ${lessonUID}`,
|
|
1066
1127
|
});
|
|
1067
1128
|
// Retry interactivity generation
|
|
1068
1129
|
try {
|
|
1069
1130
|
const syllabus = await getSyllabus(courseSlug, bucket);
|
|
1070
|
-
const lessonIndex = syllabus.lessons.findIndex(lesson => lesson.
|
|
1131
|
+
const lessonIndex = syllabus.lessons.findIndex(lesson => lesson.uid === lessonUID);
|
|
1071
1132
|
const exercise = syllabus.lessons[lessonIndex];
|
|
1072
1133
|
if (exercise && exercise.initialContent) {
|
|
1073
1134
|
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
1074
|
-
lesson:
|
|
1135
|
+
lesson: lessonUID,
|
|
1075
1136
|
status: "generating",
|
|
1076
|
-
log: `🔄 Retrying interactivity generation for lesson ${
|
|
1137
|
+
log: `🔄 Retrying interactivity generation for lesson ${lessonUID}`,
|
|
1077
1138
|
});
|
|
1078
1139
|
// Get previous lesson content for context
|
|
1079
1140
|
let lastLesson = "";
|
|
@@ -1106,18 +1167,18 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
1106
1167
|
catch (retryError) {
|
|
1107
1168
|
console.error("Error retrying interactivity generation:", retryError);
|
|
1108
1169
|
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
1109
|
-
lesson:
|
|
1170
|
+
lesson: lessonUID,
|
|
1110
1171
|
status: "error",
|
|
1111
|
-
log: `❌ Failed to retry interactivity generation for lesson ${
|
|
1172
|
+
log: `❌ Failed to retry interactivity generation for lesson ${lessonUID}`,
|
|
1112
1173
|
});
|
|
1113
1174
|
}
|
|
1114
1175
|
return res.json({ status: "ERROR" });
|
|
1115
1176
|
}
|
|
1116
1177
|
try {
|
|
1117
1178
|
const syllabus = await getSyllabus(courseSlug, bucket);
|
|
1118
|
-
const exerciseIndex = syllabus.lessons.findIndex(lesson => lesson.
|
|
1179
|
+
const exerciseIndex = syllabus.lessons.findIndex(lesson => lesson.uid === lessonUID);
|
|
1119
1180
|
if (exerciseIndex === -1) {
|
|
1120
|
-
console.error("Exercise not found receiving webhook:",
|
|
1181
|
+
console.error("Exercise not found receiving webhook:", lessonUID);
|
|
1121
1182
|
return res.json({ status: "ERROR", error: "Exercise not found" });
|
|
1122
1183
|
}
|
|
1123
1184
|
const exercise = syllabus.lessons[exerciseIndex];
|
|
@@ -1136,35 +1197,6 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
1136
1197
|
syllabus.courseInfo.language ||
|
|
1137
1198
|
"en")}`;
|
|
1138
1199
|
await uploadFileToBucket(bucket, readability.newMarkdown, `${targetDir}/${readmeFilename}`);
|
|
1139
|
-
// Handle code files if it's a coding exercise
|
|
1140
|
-
if (exercise.type.toLowerCase() === "code" &&
|
|
1141
|
-
response.parsed.codefile_content) {
|
|
1142
|
-
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
1143
|
-
lesson: exSlug,
|
|
1144
|
-
status: "generating",
|
|
1145
|
-
log: `🔄 Creating code file for ${exercise.title}`,
|
|
1146
|
-
});
|
|
1147
|
-
await uploadFileToBucket(bucket, response.parsed.codefile_content, `${targetDir}/${response.parsed.codefile_name
|
|
1148
|
-
.toLowerCase()
|
|
1149
|
-
.trim()}`);
|
|
1150
|
-
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
1151
|
-
lesson: exSlug,
|
|
1152
|
-
status: "generating",
|
|
1153
|
-
log: `✅ Code file created for ${exercise.title}`,
|
|
1154
|
-
});
|
|
1155
|
-
if (response.parsed.solution_content) {
|
|
1156
|
-
const codeFileName = response.parsed.codefile_name
|
|
1157
|
-
.toLowerCase()
|
|
1158
|
-
.trim();
|
|
1159
|
-
const solutionFileName = "solution.hide." + codeFileName;
|
|
1160
|
-
await uploadFileToBucket(bucket, response.parsed.solution_content, `${targetDir}/${solutionFileName}`);
|
|
1161
|
-
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
1162
|
-
lesson: exSlug,
|
|
1163
|
-
status: "generating",
|
|
1164
|
-
log: `✅ Solution file created for ${exercise.title}`,
|
|
1165
|
-
});
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
1200
|
// Update used components if provided by the AI
|
|
1169
1201
|
if (response.parsed.used_components &&
|
|
1170
1202
|
Array.isArray(response.parsed.used_components)) {
|
|
@@ -1186,11 +1218,11 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
1186
1218
|
}
|
|
1187
1219
|
catch (error) {
|
|
1188
1220
|
console.error("Error processing interactivity webhook:", error);
|
|
1189
|
-
await updateLessonStatusToError(courseSlug,
|
|
1221
|
+
await updateLessonStatusToError(courseSlug, lessonUID, bucket);
|
|
1190
1222
|
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
|
1191
|
-
lesson:
|
|
1223
|
+
lesson: lessonUID,
|
|
1192
1224
|
status: "error",
|
|
1193
|
-
log: `❌ Error processing interactivity for lesson ${
|
|
1225
|
+
log: `❌ Error processing interactivity for lesson ${lessonUID}`,
|
|
1194
1226
|
});
|
|
1195
1227
|
res
|
|
1196
1228
|
.status(500)
|
|
@@ -1348,24 +1380,61 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
1348
1380
|
created,
|
|
1349
1381
|
});
|
|
1350
1382
|
});
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
const
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
.json({ error: "Missing title or readme content" });
|
|
1383
|
+
// Create a new step for a course
|
|
1384
|
+
app.post("/course/:slug/create-step", async (req, res) => {
|
|
1385
|
+
console.log("POST /course/:slug/create-step");
|
|
1386
|
+
const params = req.params;
|
|
1387
|
+
const rigoToken = req.header("x-rigo-token");
|
|
1388
|
+
if (!rigoToken) {
|
|
1389
|
+
return res.status(400).json({ error: "RigoToken not found" });
|
|
1359
1390
|
}
|
|
1360
|
-
const
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
const
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1391
|
+
const { description, stepIndex } = req.body;
|
|
1392
|
+
if (!description) {
|
|
1393
|
+
return res.status(400).json({ error: "Missing description" });
|
|
1394
|
+
}
|
|
1395
|
+
const courseSlug = params.slug;
|
|
1396
|
+
const config = await getConfigJSON(bucket, courseSlug);
|
|
1397
|
+
const initialSyllabus = await getSyllabus(courseSlug, bucket);
|
|
1398
|
+
const stepSlugResponse = await (0, rigoActions_1.generateStepSlug)(rigoToken, {
|
|
1399
|
+
description,
|
|
1400
|
+
stepIndex,
|
|
1401
|
+
courseInfo: JSON.stringify(config),
|
|
1402
|
+
lang: initialSyllabus.courseInfo.language || "en",
|
|
1368
1403
|
});
|
|
1404
|
+
if (stepSlugResponse.status !== "SUCCESS") {
|
|
1405
|
+
return res.status(400).json({ error: stepSlugResponse.status_text });
|
|
1406
|
+
}
|
|
1407
|
+
console.log("STEP SLUG GENERATED BY RIGO", stepSlugResponse);
|
|
1408
|
+
const stepSlug = stepSlugResponse.parsed.slug;
|
|
1409
|
+
// split the slug at the first -
|
|
1410
|
+
const stepId = stepSlug.split("-")[0].trim();
|
|
1411
|
+
const stepTitle = stepSlug.replace(`${stepId}-`, "").trim();
|
|
1412
|
+
const newLesson = {
|
|
1413
|
+
id: stepId,
|
|
1414
|
+
title: stepTitle,
|
|
1415
|
+
description: description,
|
|
1416
|
+
type: "READ",
|
|
1417
|
+
duration: 2,
|
|
1418
|
+
generated: false,
|
|
1419
|
+
status: "GENERATING",
|
|
1420
|
+
initialContent: "",
|
|
1421
|
+
translations: {},
|
|
1422
|
+
uid: stepSlug,
|
|
1423
|
+
};
|
|
1424
|
+
const newLessons = (0, creatorUtilities_1.insertStepInCorrectPosition)(initialSyllabus.lessons, newLesson);
|
|
1425
|
+
// Use new two-phase generation workflow
|
|
1426
|
+
const completionId = await startInitialContentGeneration(rigoToken, newLessons, initialSyllabus.courseInfo, newLesson, courseSlug, initialSyllabus.courseInfo.purpose, "lastResult");
|
|
1427
|
+
newLesson.translations = {
|
|
1428
|
+
[initialSyllabus.courseInfo.language || "en"]: {
|
|
1429
|
+
completionId,
|
|
1430
|
+
startedAt: Date.now(),
|
|
1431
|
+
completedAt: 0,
|
|
1432
|
+
},
|
|
1433
|
+
};
|
|
1434
|
+
await uploadFileToBucket(bucket, JSON.stringify(Object.assign(Object.assign({}, initialSyllabus), { lessons: newLessons })), `courses/${courseSlug}/.learn/initialSyllabus.json`);
|
|
1435
|
+
const targetDir = `courses/${courseSlug}/exercises/${stepSlug}`;
|
|
1436
|
+
await uploadInitialReadme(bucket, stepSlug, targetDir, initialSyllabus.courseInfo);
|
|
1437
|
+
res.json({ status: "SUCCESS", message: "Exercise generati on started!" });
|
|
1369
1438
|
});
|
|
1370
1439
|
app.put("/actions/rename", async (req, res) => {
|
|
1371
1440
|
console.log("PUT /actions/rename");
|
|
@@ -1844,7 +1913,9 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
1844
1913
|
}
|
|
1845
1914
|
catch (error) {
|
|
1846
1915
|
console.error("❌ /actions/fetch error:", error.message || error);
|
|
1847
|
-
res
|
|
1916
|
+
res
|
|
1917
|
+
.status(500)
|
|
1918
|
+
.json({ error: error.message || "Failed to fetch link" });
|
|
1848
1919
|
}
|
|
1849
1920
|
});
|
|
1850
1921
|
app.delete("/packages/:slug", async (req, res) => {
|
|
@@ -2118,7 +2189,9 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
2118
2189
|
}
|
|
2119
2190
|
catch (error) {
|
|
2120
2191
|
console.error("Export error:", error);
|
|
2121
|
-
res
|
|
2192
|
+
res
|
|
2193
|
+
.status(500)
|
|
2194
|
+
.json({ error: "Export failed", details: error.message });
|
|
2122
2195
|
}
|
|
2123
2196
|
});
|
|
2124
2197
|
server.listen(PORT, () => {
|