@learnpack/learnpack 5.0.272 → 5.0.275

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.
@@ -4,7 +4,6 @@ exports.processImage = exports.createLearnJson = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const command_1 = require("@oclif/command");
6
6
  const buffer_1 = require("buffer");
7
- const youtube_transcript_1 = require("youtube-transcript");
8
7
  const express = require("express");
9
8
  const cors = require("cors");
10
9
  const path = require("path");
@@ -82,7 +81,6 @@ const processImage = async (url, description, rigoToken, courseSlug) => {
82
81
  prompt: description,
83
82
  callbackUrl: webhookUrl,
84
83
  });
85
- // console.log("✅ Image", imagePath, "generated successfully!")
86
84
  return true;
87
85
  }
88
86
  catch (_a) {
@@ -105,9 +103,7 @@ const uploadInitialReadme = async (bucket, exSlug, targetDir, packageContext) =>
105
103
  :rigo
106
104
  \`\`\`
107
105
  `;
108
- const readmeFilename = `README.${packageContext.language && packageContext.language !== "en" ?
109
- `${packageContext.language}.` :
110
- ""}md`;
106
+ const readmeFilename = `README${(0, creatorUtilities_1.getReadmeExtension)(packageContext.language || "en")}`;
111
107
  await uploadFileToBucket(bucket, isGeneratingText, `${targetDir}/${readmeFilename}`);
112
108
  };
113
109
  const cleanFormState = (formState) => {
@@ -134,12 +130,12 @@ const createMultiLangAsset = async (bucket, rigoToken, bcToken, courseSlug, cour
134
130
  all_translations.push(asset.slug);
135
131
  }
136
132
  };
137
- async function startExerciseGeneration(bucket, rigoToken, steps, packageContext, exercise, tutorialDir, courseSlug, purposeSlug, lastLesson = "") {
133
+ async function startExerciseGeneration(rigoToken, steps, packageContext, exercise, courseSlug, purposeSlug, lastLesson = "") {
138
134
  const exSlug = (0, creatorUtilities_2.slugify)(exercise.id + "-" + exercise.title);
139
135
  console.log("Starting generation of", exSlug);
140
136
  const webhookUrl = `${process.env.HOST}/webhooks/${courseSlug}/exercise-processor/${exercise.id}/${rigoToken}`;
141
137
  console.log("WEBHOOK URL", webhookUrl);
142
- await (0, rigoActions_1.readmeCreator)(rigoToken, {
138
+ const res = await (0, rigoActions_1.readmeCreator)(rigoToken, {
143
139
  title: `${exercise.id} - ${exercise.title}`,
144
140
  output_lang: packageContext.language || "en",
145
141
  list_of_exercises: JSON.stringify(steps.map(step => step.id + "-" + step.title)),
@@ -148,6 +144,8 @@ async function startExerciseGeneration(bucket, rigoToken, steps, packageContext,
148
144
  kind: exercise.type.toLowerCase(),
149
145
  last_lesson: lastLesson,
150
146
  }, purposeSlug, webhookUrl);
147
+ console.log("README CREATOR RES", res);
148
+ return res.id;
151
149
  }
152
150
  async function createInitialReadme(tutorialInfo, tutorialSlug, rigoToken) {
153
151
  const webhookUrl = `${process.env.HOST}/webhooks/${tutorialSlug}/initial-readme-processor`;
@@ -433,10 +431,18 @@ class ServeCommand extends SessionCommand_1.default {
433
431
  previousReadme = content.toString();
434
432
  }
435
433
  }
436
- await startExerciseGeneration(bucket, rigoToken, syllabusJson.lessons, syllabusJson.courseInfo, exercise, `courses/${courseSlug}`, courseSlug, syllabusJson.courseInfo.purpose, previousReadme +
434
+ const completionId = await startExerciseGeneration(rigoToken, syllabusJson.lessons, syllabusJson.courseInfo, exercise, courseSlug, syllabusJson.courseInfo.purpose, previousReadme +
437
435
  "\n\nThe user provided the following feedback related to the content of the course so far: \n\n" +
438
436
  feedback);
439
437
  syllabusJson.lessons[parseInt(position)].status = "GENERATING";
438
+ syllabusJson.lessons[parseInt(position)].translations = {
439
+ [syllabusJson.courseInfo.language || "en"]: {
440
+ completionId,
441
+ startedAt: Date.now(),
442
+ completedAt: 0,
443
+ },
444
+ };
445
+ console.log("Lesson", syllabusJson.lessons[parseInt(position)]);
440
446
  if (syllabusJson.feedback &&
441
447
  typeof syllabusJson.feedback === "string") {
442
448
  syllabusJson.feedback += "\n\n" + feedback;
@@ -447,6 +453,23 @@ class ServeCommand extends SessionCommand_1.default {
447
453
  await uploadFileToBucket(bucket, JSON.stringify(syllabusJson), `courses/${courseSlug}/.learn/initialSyllabus.json`);
448
454
  res.json({ status: "SUCCESS" });
449
455
  });
456
+ app.post("/actions/generate-image/:courseSlug", async (req, res) => {
457
+ const rigoToken = req.header("x-rigo-token");
458
+ const { courseSlug } = req.params;
459
+ const { image } = req.body;
460
+ if (!image) {
461
+ return res.status(400).json({
462
+ error: "Image is required",
463
+ });
464
+ }
465
+ if (!rigoToken) {
466
+ return res.status(400).json({
467
+ error: "Rigo token is required. x-rigo-token header is missing",
468
+ });
469
+ }
470
+ await (0, exports.processImage)(image.url, image.alt, rigoToken, courseSlug);
471
+ res.json({ status: "QUEUED" });
472
+ });
450
473
  app.post("/webhooks/:courseSlug/exercise-processor/:lessonID/:rigoToken", async (req, res) => {
451
474
  // console.log("Receiving a webhook to exercise processor")
452
475
  const { courseSlug, lessonID, rigoToken } = req.params;
@@ -499,40 +522,55 @@ class ServeCommand extends SessionCommand_1.default {
499
522
  log: `✅ Code file created for ${exercise.title}`,
500
523
  });
501
524
  }
502
- let nextStarted = false;
525
+ let nextCompletionId = null;
503
526
  if (nextExercise &&
504
527
  (exerciseIndex === 0 || !(exerciseIndex % 3 === 0))) {
505
528
  let feedback = "";
506
529
  if (syllabusJson.feedback) {
507
530
  feedback = `\n\nThe user added the following feedback with relation to the previous generations: ${syllabusJson.feedback}`;
508
531
  }
509
- startExerciseGeneration(bucket, rigoToken, syllabusJson.lessons, syllabusJson.courseInfo, nextExercise, `courses/${courseSlug}`, courseSlug, syllabusJson.courseInfo.purpose, readme.parsed.content + "\n\n" + feedback);
510
- nextStarted = true;
532
+ nextCompletionId = await startExerciseGeneration(rigoToken, syllabusJson.lessons, syllabusJson.courseInfo, nextExercise, courseSlug, syllabusJson.courseInfo.purpose, readme.parsed.content + "\n\n" + feedback);
511
533
  }
512
534
  else {
513
535
  console.log("Stopping generation process at", exerciseIndex, exercise.title, "because it's a multiple of 3");
514
536
  }
515
537
  const imagesArray = (0, creatorUtilities_1.extractImagesFromMarkdown)(readability.newMarkdown);
516
- if (imagesArray.length > 0) {
517
- (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
518
- lesson: exSlug,
519
- status: "pending",
520
- log: `🔄 Generating images for ${exercise.title}`,
521
- });
522
- for (const image of imagesArray) {
523
- // eslint-disable-next-line no-await-in-loop
524
- await (0, exports.processImage)(image.url, image.alt, rigoToken, courseSlug);
525
- }
526
- }
527
538
  const newSyllabus = Object.assign(Object.assign({}, syllabusJson), { lessons: syllabusJson.lessons.map((lesson, index) => {
528
539
  if (index === exerciseIndex) {
529
- return Object.assign(Object.assign({}, lesson), { generated: true, status: "DONE" });
540
+ const currentTranslations = lesson.translations || {};
541
+ let currentTranslation = currentTranslations[syllabusJson.courseInfo.language || "en"];
542
+ if (currentTranslation) {
543
+ currentTranslation.completedAt = Date.now();
544
+ }
545
+ else {
546
+ currentTranslation = {
547
+ completionId: readme.id,
548
+ startedAt: Date.now(),
549
+ completedAt: Date.now(),
550
+ };
551
+ }
552
+ currentTranslations[syllabusJson.courseInfo.language || "en"] =
553
+ currentTranslation;
554
+ return Object.assign(Object.assign({}, lesson), { generated: true, status: "DONE", translations: {
555
+ [syllabusJson.courseInfo.language || "en"]: {
556
+ completionId: nextCompletionId,
557
+ completedAt: Date.now(),
558
+ },
559
+ } });
530
560
  }
531
- if (nextExercise && nextExercise.id === lesson.id && nextStarted) {
532
- return Object.assign(Object.assign({}, lesson), { generated: false, status: "GENERATING" });
561
+ if (nextExercise &&
562
+ nextExercise.id === lesson.id &&
563
+ nextCompletionId) {
564
+ return Object.assign(Object.assign({}, lesson), { generated: false, status: "GENERATING", translations: {
565
+ [syllabusJson.courseInfo.language || "en"]: {
566
+ completionId: nextCompletionId,
567
+ startedAt: Date.now(),
568
+ },
569
+ } });
533
570
  }
534
571
  return Object.assign({}, lesson);
535
572
  }) });
573
+ console.log("New syllabus", newSyllabus);
536
574
  await uploadFileToBucket(bucket, JSON.stringify(newSyllabus), `courses/${courseSlug}/.learn/initialSyllabus.json`);
537
575
  (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
538
576
  lesson: exSlug,
@@ -937,7 +975,16 @@ class ServeCommand extends SessionCommand_1.default {
937
975
  await uploadFileToBucket(bucket, JSON.stringify(sidebar), `${tutorialDir}/.learn/sidebar.json`);
938
976
  const firstLesson = syllabus.lessons[0];
939
977
  const lastResult = "---";
940
- await startExerciseGeneration(bucket, rigoToken, syllabus.lessons, syllabus.courseInfo, firstLesson, tutorialDir, courseSlug, syllabus.courseInfo.purpose, lastResult);
978
+ const completionId = await startExerciseGeneration(rigoToken, syllabus.lessons, syllabus.courseInfo, firstLesson, courseSlug, syllabus.courseInfo.purpose, lastResult);
979
+ if (firstLesson) {
980
+ firstLesson.translations = {
981
+ [syllabus.courseInfo.language || "en"]: {
982
+ completionId,
983
+ startedAt: Date.now(),
984
+ completedAt: 0,
985
+ },
986
+ };
987
+ }
941
988
  await createInitialReadme(JSON.stringify(syllabus.courseInfo), courseSlug, rigoToken);
942
989
  return res.json({
943
990
  message: "Course generation started",
@@ -958,29 +1005,44 @@ class ServeCommand extends SessionCommand_1.default {
958
1005
  return res.status(500).json({ error: "Error getting syllabus" });
959
1006
  }
960
1007
  });
961
- app.post("/actions/continue-course/:courseSlug", async (req, res) => {
962
- console.log("POST /actions/continue-course/:courseSlug");
963
- const { courseSlug } = req.params;
964
- const { feedback } = req.body;
965
- const rigoToken = req.header("x-rigo-token");
966
- const bcToken = req.header("x-breathecode-token");
967
- if (!rigoToken || !bcToken) {
968
- return res.status(400).json({ error: "Missing tokens" });
969
- }
970
- const syllabus = await bucket.file(`courses/${courseSlug}/.learn/initialSyllabus.json`);
971
- const [content] = await syllabus.download();
972
- const syllabusJson = JSON.parse(content.toString());
973
- const notGeneratedLessons = syllabusJson.lessons.filter(lesson => !lesson.generated);
974
- const lastGeneratedLesson = findLast(syllabusJson.lessons, lesson => { var _a; return (_a = lesson.generated) !== null && _a !== void 0 ? _a : false; });
975
- console.log("ABout to generate", notGeneratedLessons.length, "lessons");
976
- const firstLessonToGenerate = notGeneratedLessons[0];
977
- await startExerciseGeneration(bucket, rigoToken, syllabusJson.lessons, syllabusJson.courseInfo, firstLessonToGenerate, `courses/${courseSlug}`, courseSlug, syllabusJson.courseInfo.purpose, JSON.stringify(lastGeneratedLesson) +
978
- `\n\nThe user provided this feedback in relation to the course: ${feedback}`);
979
- return res.json({
980
- message: "Course continued",
981
- slug: courseSlug,
982
- });
983
- });
1008
+ // app.post("/actions/continue-course/:courseSlug", async (req, res) => {
1009
+ // console.log("POST /actions/continue-course/:courseSlug")
1010
+ // const { courseSlug } = req.params
1011
+ // const { feedback }: { feedback: string } = req.body
1012
+ // const rigoToken = req.header("x-rigo-token")
1013
+ // const bcToken = req.header("x-breathecode-token")
1014
+ // if (!rigoToken || !bcToken) {
1015
+ // return res.status(400).json({ error: "Missing tokens" })
1016
+ // }
1017
+ // const syllabus = await bucket.file(
1018
+ // `courses/${courseSlug}/.learn/initialSyllabus.json`
1019
+ // )
1020
+ // const [content] = await syllabus.download()
1021
+ // const syllabusJson: Syllabus = JSON.parse(content.toString())
1022
+ // const notGeneratedLessons = syllabusJson.lessons.filter(
1023
+ // lesson => !lesson.generated
1024
+ // )
1025
+ // const lastGeneratedLesson = findLast(
1026
+ // syllabusJson.lessons,
1027
+ // lesson => lesson.generated ?? false
1028
+ // )
1029
+ // console.log("ABout to generate", notGeneratedLessons.length, "lessons")
1030
+ // const firstLessonToGenerate = notGeneratedLessons[0]
1031
+ // const completionId = await startExerciseGeneration(
1032
+ // rigoToken,
1033
+ // syllabusJson.lessons,
1034
+ // syllabusJson.courseInfo,
1035
+ // firstLessonToGenerate,
1036
+ // courseSlug,
1037
+ // syllabusJson.courseInfo.purpose,
1038
+ // JSON.stringify(lastGeneratedLesson) +
1039
+ // `\n\nThe user provided this feedback in relation to the course: ${feedback}`
1040
+ // )
1041
+ // return res.json({
1042
+ // message: "Course continued",
1043
+ // slug: courseSlug,
1044
+ // })
1045
+ // })
984
1046
  app.get("/courses/:courseSlug/exercises/:exerciseSlug/", async (req, res) => {
985
1047
  var _a;
986
1048
  console.log("GET /courses/:courseSlug/exercises/:exerciseSlug/");
@@ -1135,9 +1197,9 @@ class ServeCommand extends SessionCommand_1.default {
1135
1197
  }
1136
1198
  catch (error) {
1137
1199
  console.error("❌ Error fetching file:", error);
1138
- return res
1139
- .status(500)
1140
- .json({ error: error.message || "Unable to fetch file" });
1200
+ return res.status(500).json({
1201
+ error: error.message || "Unable to fetch file",
1202
+ });
1141
1203
  }
1142
1204
  });
1143
1205
  const YT_REGEX = /(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]{11})/;
@@ -1149,20 +1211,30 @@ class ServeCommand extends SessionCommand_1.default {
1149
1211
  const ytMatch = decoded.match(YT_REGEX);
1150
1212
  if (ytMatch) {
1151
1213
  const videoId = ytMatch[1];
1152
- // fetch metadata
1153
- const items = await youtube_transcript_1.YoutubeTranscript.fetchTranscript(videoId);
1154
- const transcript = items.map(i => i.text).join(" ");
1155
- const { data: meta } = await axios_1.default.get("https://www.youtube.com/oembed", {
1156
- params: { url: decoded, format: "json" },
1157
- });
1214
+ const resFromRigo = await axios_1.default.get(`${api_1.RIGOBOT_REALTIME_HOST}/actions/youtube-transcript/${videoId}`);
1215
+ console.log("RES FROM RIGO", resFromRigo.data);
1216
+ const transcript = resFromRigo.data.transcript;
1217
+ // let meta: any = null
1218
+ // try {
1219
+ // const { data: meta } = await axios.get(
1220
+ // "https://www.youtube.com/oembed",
1221
+ // {
1222
+ // params: { url: decoded, format: "json" },
1223
+ // }
1224
+ // )
1225
+ // console.log("META", meta)
1226
+ // } catch (error) {
1227
+ // console.error("ERROR FETCHING META", error)
1228
+ // meta = null
1229
+ // }
1158
1230
  return res.json({
1159
1231
  url: decoded,
1160
- title: meta.title,
1161
- author: meta.author_name,
1162
- thumbnail: meta.thumbnail_url,
1232
+ title: resFromRigo.data.title || null,
1233
+ author: resFromRigo.data.author || null,
1163
1234
  transcript,
1164
1235
  });
1165
1236
  }
1237
+ console.log("NOT A YOUTUBE LINK", decoded);
1166
1238
  const response = await axios_1.default.get(decoded, { responseType: "text" });
1167
1239
  const html = response.data;
1168
1240
  const title = getTitleFromHTML(html);
@@ -18650,7 +18650,7 @@ const Cv = async (e) => {
18650
18650
  const l = Qu(15),
18651
18651
  u = `${
18652
18652
  Bb
18653
- ? "https://9cw5zmww-3000.use2.devtunnels.ms"
18653
+ ? "https://1gm40gnb-3000.use2.devtunnels.ms"
18654
18654
  : window.location.origin
18655
18655
  }/notifications/${l}`
18656
18656
  return (
@@ -30522,7 +30522,8 @@ const qD = ({ messages: e, syllabus: t }) => {
30522
30522
  p.lessons.length === 0 &&
30523
30523
  t.length < 3 &&
30524
30524
  E.jsx("div", {
30525
- className: "text-center text-gray-200 font-bold text-lg",
30525
+ className:
30526
+ "text-center text-gray-500 font-bold text-lg h-[50vh] flex items-center justify-center",
30526
30527
  children: N1("contentIndex.noLessons"),
30527
30528
  }),
30528
30529
  p.lessons.map((T, S) =>
@@ -38943,6 +38944,8 @@ const RL = {
38943
38944
  creatingCourseAs: "Creating the course as {{name}}",
38944
38945
  loginAsSomeoneElse: "Login as someone else",
38945
38946
  continueScrolling: "Continue scrolling",
38947
+ noLessons:
38948
+ "The content could not be generated, please talk with Rigobot to give it more context.",
38946
38949
  okMessage: "If you're satisfied, type 'OK' in the chat.",
38947
38950
  instructionsMessage: `If not, what would you like me to change? You can say things like:
38948
38951
  - Add more exercises
@@ -39090,6 +39093,8 @@ const RL = {
39090
39093
  creatingCourseAs: "Creando el curso como {{name}}",
39091
39094
  loginAsSomeoneElse: "Iniciar sesión como alguien más",
39092
39095
  continueScrolling: "Continuar desplazando",
39096
+ noLessons:
39097
+ "El contenido no pudo ser generado, por favor habla con Rigobot para darle más contexto.",
39093
39098
  okMessage: "Si estás satisfecho, escribe 'OK' en el chat.",
39094
39099
  instructionsMessage: `Si no, ¿qué te gustaría que cambiara? Puedes decir cosas como:
39095
39100
  - Añadir más ejercicios
@@ -566,6 +566,9 @@
566
566
  .h-60 {
567
567
  height: calc(var(--spacing) * 60);
568
568
  }
569
+ .h-\[50vh\] {
570
+ height: 50vh;
571
+ }
569
572
  .h-\[85\%\] {
570
573
  height: 85%;
571
574
  }
@@ -1043,9 +1046,6 @@
1043
1046
  .text-blue-900 {
1044
1047
  color: var(--color-blue-900);
1045
1048
  }
1046
- .text-gray-200 {
1047
- color: var(--color-gray-200);
1048
- }
1049
1049
  .text-gray-400 {
1050
1050
  color: var(--color-gray-400);
1051
1051
  }
@@ -10,8 +10,8 @@
10
10
  />
11
11
 
12
12
  <title>Learnpack Creator: Craft tutorials in seconds!</title>
13
- <script type="module" crossorigin src="/creator/assets/index-C1pv1wUb.js"></script>
14
- <link rel="stylesheet" crossorigin href="/creator/assets/index-B4khtb0r.css">
13
+ <script type="module" crossorigin src="/creator/assets/index-BfLyIQVh.js"></script>
14
+ <link rel="stylesheet" crossorigin href="/creator/assets/index-C39zeF3W.css">
15
15
  </head>
16
16
  <body>
17
17
  <div id="root"></div>
@@ -7,6 +7,13 @@ export interface Lesson {
7
7
  duration?: number;
8
8
  generated?: boolean;
9
9
  status?: "PENDING" | "GENERATING" | "DONE" | "ERROR";
10
+ translations?: {
11
+ [key: string]: {
12
+ completionId: number;
13
+ startedAt: number;
14
+ completedAt?: number;
15
+ };
16
+ };
10
17
  }
11
18
  export interface ParsedLink {
12
19
  name: string;
@@ -1,4 +1,5 @@
1
1
  export declare const RIGOBOT_HOST = "https://rigobot.herokuapp.com";
2
+ export declare const RIGOBOT_REALTIME_HOST = "https://chat.4geeks.com";
2
3
  type TConsumableSlug = "ai-conversation-message" | "ai-compilation" | "ai-tutorial-generation" | "ai-generation" | "learnpack-publish";
3
4
  export declare const countConsumables: (consumables: any, consumableSlug?: TConsumableSlug) => any;
4
5
  export declare const getConsumable: (token: string, consumableSlug?: TConsumableSlug) => Promise<any>;
package/lib/utils/api.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getCurrentTechnologies = exports.fetchTechnologies = exports.doesAssetExists = exports.createAsset = exports.validateToken = exports.listUserAcademies = exports.getConsumable = exports.countConsumables = exports.RIGOBOT_HOST = void 0;
3
+ exports.getCurrentTechnologies = exports.fetchTechnologies = exports.doesAssetExists = exports.createAsset = exports.validateToken = exports.listUserAcademies = exports.getConsumable = exports.countConsumables = exports.RIGOBOT_REALTIME_HOST = exports.RIGOBOT_HOST = void 0;
4
4
  const console_1 = require("../utils/console");
5
5
  const storage = require("node-persist");
6
6
  const cli_ux_1 = require("cli-ux");
@@ -9,6 +9,7 @@ const dotenv = require("dotenv");
9
9
  dotenv.config();
10
10
  const HOST = "https://breathecode.herokuapp.com";
11
11
  exports.RIGOBOT_HOST = "https://rigobot.herokuapp.com";
12
+ exports.RIGOBOT_REALTIME_HOST = "https://chat.4geeks.com";
12
13
  // export const RIGOBOT_HOST = "https://rigobot-test-cca7d841c9d8.herokuapp.com"
13
14
  // export const RIGOBOT_HOST =
14
15
  // "https://8000-charlytoc-rigobot-bmwdeam7cev.ws-us118.gitpod.io"
@@ -354,6 +355,7 @@ const createAsset = async (token, asset) => {
354
355
  learnpack_deploy_url: asset.learnpack_deploy_url,
355
356
  technologies: asset.technologies,
356
357
  readme_raw: asset.readme_raw,
358
+ all_translations: asset.all_translations,
357
359
  };
358
360
  const url = `https://breathecode.herokuapp.com/v1/registry/asset/me`;
359
361
  const headers = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@learnpack/learnpack",
3
3
  "description": "Seamlessly build, sell and/or take interactive & auto-graded tutorials, start learning now or build a new tutorial to your audience.",
4
- "version": "5.0.272",
4
+ "version": "5.0.275",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {