@learnpack/learnpack 5.0.234 → 5.0.240

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.
@@ -270,8 +270,12 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
270
270
  const imagePromises = imagesArray.map(async (image) => {
271
271
  try {
272
272
  const filename = (0, creatorUtilities_1.getFilenameFromUrl)(image.url);
273
+ const webhookUrl = `${process.env.HOST}/webhooks/${(0, creatorUtilities_1.slugify)(packageInfo.title.us)}/images/${filename}`;
273
274
  const imagePath = path.join(tutorialDir, ".learn", "assets", filename);
274
- const res = await (0, rigoActions_1.generateImage)(rigoToken, { prompt: image.alt });
275
+ const res = await (0, rigoActions_1.generateImage)(rigoToken, {
276
+ prompt: image.alt,
277
+ callbackUrl: webhookUrl,
278
+ });
275
279
  await (0, rigoActions_1.downloadImage)(res.image_url, imagePath);
276
280
  return true;
277
281
  }
@@ -289,8 +293,9 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
289
293
  prompt: "Generate a preview image for the tutorial. This is all the tutorial information: " +
290
294
  packageContext +
291
295
  "\n Generate only a basic preview image, add the tutorial Title as a text add the top middle, avoid adding any other text elements. Try to generate an image that related with the tutorial content.",
296
+ callbackUrl: `${process.env.HOST}/webhooks/${(0, creatorUtilities_1.slugify)(packageInfo.title.us)}/images/preview.png`,
292
297
  });
293
- await (0, rigoActions_1.downloadImage)(res.image_url, imagePath);
298
+ // await downloadImage(res.image_url, imagePath)
294
299
  return true;
295
300
  };
296
301
  const getChoices = async (empty) => {
@@ -2,7 +2,7 @@ import SessionCommand from "../utils/SessionCommand";
2
2
  export declare const handleAssetCreation: (sessionPayload: {
3
3
  token: string;
4
4
  rigobotToken: string;
5
- }, learnJson: any, selectedLang: string, learnpackDeployUrl: string) => Promise<void>;
5
+ }, learnJson: any, selectedLang: string, learnpackDeployUrl: string, b64IndexReadme: string) => Promise<void>;
6
6
  declare class BuildCommand extends SessionCommand {
7
7
  static description: string;
8
8
  static flags: {
@@ -19,10 +19,10 @@ const prompts = require("prompts");
19
19
  const rigoActions_1 = require("../utils/rigoActions");
20
20
  const misc_1 = require("../utils/misc");
21
21
  const uploadZipEndpont = api_1.RIGOBOT_HOST + "/v1/learnpack/upload";
22
- const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, learnpackDeployUrl) => {
22
+ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, learnpackDeployUrl, b64IndexReadme) => {
23
23
  const categories = {
24
- us: 91,
25
- es: 92,
24
+ us: 9,
25
+ es: 10,
26
26
  };
27
27
  let category = categories[selectedLang];
28
28
  if (!category) {
@@ -45,6 +45,7 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
45
45
  owner: user.id,
46
46
  author: user.id,
47
47
  preview: learnJson.preview,
48
+ readme_raw: b64IndexReadme,
48
49
  });
49
50
  await api_1.default.updateRigoAssetID(sessionPayload.token, learnJson.slug, asset.id);
50
51
  console_1.default.info("Asset created with id", asset.id);
@@ -295,7 +296,7 @@ class BuildCommand extends SessionCommand_1.default {
295
296
  console.log(res.data);
296
297
  fs.unlinkSync(zipFilePath);
297
298
  this.removeDirectory(buildDir);
298
- await (0, exports.handleAssetCreation)(sessionPayload, learnJson, "us", res.data.url);
299
+ await (0, exports.handleAssetCreation)(sessionPayload, learnJson, "us", res.data.url, "");
299
300
  }
300
301
  catch (error) {
301
302
  if (axios_1.default.isAxiosError(error)) {
@@ -1,6 +1,5 @@
1
1
  import SessionCommand from "../utils/SessionCommand";
2
- import { Bucket } from "@google-cloud/storage";
3
- import { FormState, Lesson } from "../models/creator";
2
+ import { FormState } from "../models/creator";
4
3
  export declare const createLearnJson: (courseInfo: FormState) => {
5
4
  slug: string;
6
5
  title: {
@@ -20,8 +19,7 @@ export declare const createLearnJson: (courseInfo: FormState) => {
20
19
  };
21
20
  preview: string;
22
21
  };
23
- export declare const processImage: (bucket: Bucket, tutorialDir: string, url: string, description: string, rigoToken: string) => Promise<boolean>;
24
- export declare function processExercise(bucket: Bucket, rigoToken: string, steps: Lesson[], packageContext: FormState, exercise: Lesson, tutorialDir: string, courseSlug: string, purposeSlug: string, lastLesson?: string): Promise<string>;
22
+ export declare const processImage: (url: string, description: string, rigoToken: string, courseSlug: string) => Promise<boolean>;
25
23
  export default class ServeCommand extends SessionCommand {
26
24
  static description: string;
27
25
  static flags: any;
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.processImage = exports.createLearnJson = void 0;
4
- exports.processExercise = processExercise;
5
4
  const tslib_1 = require("tslib");
6
5
  const command_1 = require("@oclif/command");
7
6
  // import { readDocument } from "../utils/readDocuments"
@@ -93,14 +92,46 @@ const PARAMS = {
93
92
  max_rewrite_attempts: 2,
94
93
  max_title_length: 50,
95
94
  };
96
- const processImage = async (bucket, tutorialDir, url, description, rigoToken) => {
95
+ // app.post("/webhooks/:courseSlug/images/:imageId", async (req, res) => {
96
+ // const { courseSlug, imageId } = req.params
97
+ // const body = req.body
98
+ // console.log("RECEIVING IMAGE WEBHOOK", body)
99
+ // const imageUrl = body.image_url
100
+ // const imagePath = `courses/${courseSlug}/.learn/assets/${imageId}`
101
+ // const imageFile = bucket.file(imagePath)
102
+ // const [exists] = await imageFile.exists()
103
+ // if (!exists) {
104
+ // // Descargar la imagen
105
+ // const response = await fetch(imageUrl)
106
+ // if (!response.ok) {
107
+ // return res.status(400).json({
108
+ // status: "ERROR",
109
+ // message: "No se pudo descargar la imagen",
110
+ // })
111
+ // }
112
+ // const buffer = await response.arrayBuffer()
113
+ // // Guardar la imagen en el bucket
114
+ // await imageFile.save(new Uint8Array(buffer), {
115
+ // contentType: "image/png",
116
+ // })
117
+ // }
118
+ // emitToNotification(imageId, {
119
+ // status: "SUCCESS",
120
+ // message: "Image generated successfully",
121
+ // })
122
+ // res.json({ status: "SUCCESS" })
123
+ // })
124
+ const processImage = async (url, description, rigoToken, courseSlug) => {
97
125
  try {
126
+ // TODO: MAKE THIS ASYNC
98
127
  const filename = (0, creatorUtilities_1.getFilenameFromUrl)(url);
99
- const imagePath = `${tutorialDir}/.learn/assets/${filename}`;
100
- console.log("🖼️ Generating image", imagePath);
101
- const res = await (0, rigoActions_1.generateImage)(rigoToken, { prompt: description });
102
- await uploadImageToBucket(bucket, res.image_url, imagePath);
103
- console.log("✅ Image", imagePath, "generated successfully!");
128
+ const webhookUrl = `${process.env.HOST}/webhooks/${courseSlug}/images/${filename}`;
129
+ await (0, rigoActions_1.generateImage)(rigoToken, {
130
+ prompt: description,
131
+ callbackUrl: webhookUrl,
132
+ });
133
+ // await uploadImageToBucket(bucket, res.image_url, imagePath)
134
+ // console.log("✅ Image", imagePath, "generated successfully!")
104
135
  return true;
105
136
  }
106
137
  catch (_a) {
@@ -148,83 +179,19 @@ async function startExerciseGeneration(bucket, rigoToken, steps, packageContext,
148
179
  kind: exercise.type.toLowerCase(),
149
180
  last_lesson: lastLesson,
150
181
  }, purposeSlug, webhookUrl);
151
- // console.log("res processing in background", res)
152
182
  }
153
- async function processExercise(bucket, rigoToken, steps, packageContext, exercise, tutorialDir, courseSlug, purposeSlug, lastLesson = "") {
154
- const exercisesDir = `${tutorialDir}/exercises`;
155
- // const tid = toast.loading("Generating lesson...")
156
- const exSlug = (0, creatorUtilities_2.slugify)(exercise.id + "-" + exercise.title);
157
- console.log("exSlug", exSlug);
158
- const readmeFilename = `README.${packageContext.language && packageContext.language !== "en" ?
159
- `${packageContext.language}.` :
160
- ""}md`;
161
- const targetDir = `${exercisesDir}/${exSlug}`;
162
- console.log("✍🏻 Generating lesson", exercise.id, exercise.title);
163
- const readme = await (0, rigoActions_1.readmeCreator)(rigoToken, {
164
- title: `${exercise.id} - ${exercise.title}`,
165
- output_lang: packageContext.language || "en",
166
- list_of_exercises: JSON.stringify(steps.map(step => step.id + "-" + step.title)),
167
- tutorial_description: JSON.stringify(cleanFormState(packageContext)),
168
- lesson_description: exercise.description,
169
- kind: exercise.type.toLowerCase(),
170
- last_lesson: lastLesson,
171
- }, purposeSlug);
172
- const duration = exercise.duration;
173
- // let attempts = 0
174
- const readability = (0, creatorUtilities_2.checkReadability)(readme.parsed.content, PARAMS.max_words, duration || 3);
175
- (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
176
- lesson: exSlug,
177
- status: "generating",
178
- log: `🔄 The lesson ${exercise.id} - ${exercise.title} has a readability score of ${readability.fkglResult.fkgl}`,
179
- });
180
- // while (
181
- // readability.fkglResult.fkgl > PARAMS.max_fkgl &&
182
- // attempts < PARAMS.max_rewrite_attempts
183
- // ) {
184
- // // eslint-disable-next-line
185
- // const reducedReadme = await makeReadmeReadable(
186
- // rigoToken,
187
- // {
188
- // lesson: readability.body,
189
- // number_of_words: readability.minutes.toString(),
190
- // expected_number_words: PARAMS.max_words.toString(),
191
- // fkgl_results: JSON.stringify(readability.fkglResult),
192
- // expected_grade_level: PARAMS.expected_grade_level,
193
- // },
194
- // purposeSlug
195
- // )
196
- // if (!reducedReadme) break
197
- // readability = checkReadability(
198
- // reducedReadme.parsed.content,
199
- // PARAMS.max_words,
200
- // duration || 3
201
- // )
202
- // attempts++
203
- // }
204
- await uploadFileToBucket(bucket, readability.newMarkdown, `${targetDir}/${readmeFilename}`);
205
- if (exercise.type.toLowerCase() === "code" &&
206
- readme.parsed.codefile_content) {
207
- console.log("🔍 Creating code file for", exercise.title);
208
- await uploadFileToBucket(bucket, readme.parsed.codefile_content, `${targetDir}/${readme.parsed.codefile_name.toLowerCase().trim()}`);
183
+ async function createInitialReadme(tutorialInfo, tutorialSlug, rigoToken) {
184
+ const webhookUrl = `${process.env.HOST}/webhooks/${tutorialSlug}/initial-readme-processor`;
185
+ console.log("Creating initial readme", webhookUrl);
186
+ try {
187
+ const res = await (0, rigoActions_1.createStructuredPreviewReadme)(rigoToken, {
188
+ tutorial_info: tutorialInfo,
189
+ }, webhookUrl);
190
+ console.log("Initial readme created", res);
209
191
  }
210
- const imagesArray = (0, creatorUtilities_1.extractImagesFromMarkdown)(readability.newMarkdown);
211
- if (imagesArray.length > 0) {
212
- (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
213
- lesson: exSlug,
214
- status: "done",
215
- log: `🔄 Generating images for ${exercise.title}`,
216
- });
217
- for (const image of imagesArray) {
218
- // eslint-disable-next-line no-await-in-loop
219
- await (0, exports.processImage)(bucket, tutorialDir, image.url, image.alt, rigoToken);
220
- }
192
+ catch (error) {
193
+ console.error("Error creating initial readme", error);
221
194
  }
222
- (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
223
- lesson: exSlug,
224
- status: "done",
225
- log: `✅ The lesson ${exercise.id} - ${exercise.title} has been generated successfully!`,
226
- });
227
- return readability.newMarkdown;
228
195
  }
229
196
  const fixPreviewUrl = (slug, previewUrl) => {
230
197
  if (!previewUrl) {
@@ -391,6 +358,20 @@ class ServeCommand extends SessionCommand_1.default {
391
358
  (0, creatorSocket_1.emitToNotification)(id, body);
392
359
  res.json({ id, status: "SUCCESS" });
393
360
  });
361
+ app.post("/webhooks/:courseSlug/initial-readme-processor", async (req, res) => {
362
+ const { courseSlug } = req.params;
363
+ const body = req.body;
364
+ console.log("RECEIVING INITIAL README WEBHOOK", body);
365
+ // Save the file as courses/courseSlug/README.md
366
+ const filePath = `courses/${courseSlug}/README.${body.parsed.language_code === "us" ||
367
+ body.parsed.language_code === "en" ?
368
+ "md" :
369
+ `${body.parsed.language_code}.md`}`;
370
+ console.log("Saving initial readme to", filePath);
371
+ await uploadFileToBucket(bucket, body.parsed.content, filePath);
372
+ console.log("Initial readme saved to", filePath);
373
+ res.json({ status: "SUCCESS" });
374
+ });
394
375
  app.post("/webhooks/:courseSlug/images/:imageId", async (req, res) => {
395
376
  const { courseSlug, imageId } = req.params;
396
377
  const body = req.body;
@@ -459,13 +440,17 @@ class ServeCommand extends SessionCommand_1.default {
459
440
  log: `🔄 Creating code file for ${exercise.title}`,
460
441
  });
461
442
  await uploadFileToBucket(bucket, readme.parsed.codefile_content, `${targetDir}/${readme.parsed.codefile_name.toLowerCase().trim()}`);
443
+ (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
444
+ lesson: exSlug,
445
+ status: "done",
446
+ log: `✅ Code file created for ${exercise.title}`,
447
+ });
462
448
  }
463
449
  if (nextExercise) {
464
450
  startExerciseGeneration(bucket, rigoToken, syllabusJson.lessons, syllabusJson.courseInfo, nextExercise, `courses/${courseSlug}`, courseSlug, syllabusJson.courseInfo.purpose, readme.parsed.content);
465
451
  }
466
452
  const imagesArray = (0, creatorUtilities_1.extractImagesFromMarkdown)(readability.newMarkdown);
467
453
  if (imagesArray.length > 0) {
468
- console.log("This course requires images and I don't have the token :)");
469
454
  (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
470
455
  lesson: exSlug,
471
456
  status: "pending",
@@ -473,7 +458,7 @@ class ServeCommand extends SessionCommand_1.default {
473
458
  });
474
459
  for (const image of imagesArray) {
475
460
  // eslint-disable-next-line no-await-in-loop
476
- await (0, exports.processImage)(bucket, `courses/${courseSlug}`, image.url, image.alt, rigoToken);
461
+ await (0, exports.processImage)(image.url, image.alt, rigoToken, courseSlug);
477
462
  }
478
463
  }
479
464
  (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
@@ -826,41 +811,13 @@ class ServeCommand extends SessionCommand_1.default {
826
811
  await uploadFileToBucket(bucket, JSON.stringify(sidebar), `${tutorialDir}/.learn/sidebar.json`);
827
812
  const firstLesson = syllabus.lessons[0];
828
813
  const lastResult = "Nothing";
829
- startExerciseGeneration(bucket, rigoToken, syllabus.lessons, syllabus.courseInfo, firstLesson, tutorialDir, courseSlug, syllabus.courseInfo.purpose, lastResult);
814
+ await startExerciseGeneration(bucket, rigoToken, syllabus.lessons, syllabus.courseInfo, firstLesson, tutorialDir, courseSlug, syllabus.courseInfo.purpose, lastResult);
815
+ await createInitialReadme(JSON.stringify(syllabus.courseInfo), courseSlug, rigoToken);
830
816
  return res.json({
831
817
  message: "Course created",
832
818
  slug: (0, creatorUtilities_2.slugify)(syllabus.courseInfo.title),
833
819
  });
834
820
  });
835
- // app.post(
836
- // "/check-latex/:courseSlug/:exerciseSlug/:lang",
837
- // async (req, res) => {
838
- // const { courseSlug, exerciseSlug, lang } = req.params
839
- // const rigoToken = req.header("x-rigo-token")
840
- // if (!rigoToken) {
841
- // return res.status(400).json({ error: "Missing tokens" })
842
- // }
843
- // const exercise = await bucket.file(
844
- // `courses/${courseSlug}/exercises/${exerciseSlug}/README.${lang}.md`
845
- // )
846
- // const [content] = await exercise.download()
847
- // const headers = {
848
- // Authorization: `Token ${rigoToken}`,
849
- // }
850
- // const response = await axios.get(
851
- // `${RIGOBOT_HOST}/v1/prompting/completion/60865/`,
852
- // {
853
- // headers,
854
- // }
855
- // )
856
- // console.log(response.data.parsed.content, "RESPONSE from Rigobot")
857
- // res.json({
858
- // message: "Exercise downloaded",
859
- // completion: response.data,
860
- // exercise: content.toString(),
861
- // })
862
- // }
863
- // )
864
821
  app.get("/courses/:courseSlug/syllabus", async (req, res) => {
865
822
  try {
866
823
  console.log("GET /courses/:courseSlug/syllabus");
@@ -974,7 +931,6 @@ class ServeCommand extends SessionCommand_1.default {
974
931
  title = config.title[availableLangs[0]];
975
932
  selectedLang = availableLangs[0];
976
933
  }
977
- console.log(config.description, "CONFIG DESCRIPTION");
978
934
  // console.log(availableLangs, "AVAILABLE LANGs")
979
935
  // console.log(selectedLang, "SELECTED LANG")
980
936
  // console.log(title, "TITLE")
@@ -1021,10 +977,17 @@ class ServeCommand extends SessionCommand_1.default {
1021
977
  form.append("file", fs.createReadStream(zipPath));
1022
978
  form.append("config", JSON.stringify(config));
1023
979
  const rigoRes = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/learnpack/upload`, form, {
1024
- headers: Object.assign(Object.assign({}, form.getHeaders()), { Authorization: `Token ${rigoToken}` }),
980
+ headers: Object.assign(Object.assign({}, form.getHeaders()), { Authorization: `Token ${rigoToken.trim()}` }),
1025
981
  });
1026
- await (0, publish_1.handleAssetCreation)({ token: bcToken, rigobotToken: rigoToken }, fullConfig.config, selectedLang, rigoRes.data.url);
982
+ const indexReadme = await bucket.file(`courses/${slug}/README.${selectedLang === "us" || selectedLang === "en" ?
983
+ "md" :
984
+ `${selectedLang}.md`}`);
985
+ const [indexReadmeContent] = await indexReadme.download();
986
+ const indexReadmeString = indexReadmeContent.toString();
987
+ const b64IndexReadme = Buffer.from(indexReadmeString).toString("base64");
988
+ await (0, publish_1.handleAssetCreation)({ token: bcToken, rigobotToken: rigoToken }, fullConfig.config, selectedLang, rigoRes.data.url, b64IndexReadme);
1027
989
  rimraf.sync(tmpRoot);
990
+ console.log("RigoRes", rigoRes.data);
1028
991
  return res.json({ url: rigoRes.data.url });
1029
992
  });
1030
993
  archive.on("error", err => {