@learnpack/learnpack 5.0.274 → 5.0.276

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.
@@ -21,15 +21,7 @@ const misc_1 = require("../utils/misc");
21
21
  const creatorUtilities_1 = require("../utils/creatorUtilities");
22
22
  const uploadZipEndpont = api_1.RIGOBOT_HOST + "/v1/learnpack/upload";
23
23
  const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, learnpackDeployUrl, b64IndexReadme, all_translations = []) => {
24
- const categories = {
25
- en: 9,
26
- us: 9,
27
- es: 10,
28
- };
29
- let category = categories[selectedLang];
30
- if (!category) {
31
- category = 91;
32
- }
24
+ const category = "uncategorized";
33
25
  try {
34
26
  const user = await api_1.default.validateToken(sessionPayload.token);
35
27
  const slug = (0, creatorUtilities_1.slugify)(learnJson.title[selectedLang]).slice(0, 50);
@@ -59,6 +51,7 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
59
51
  const asset = await api_1.default.updateAsset(sessionPayload.token, slug, {
60
52
  learnpack_deploy_url: learnpackDeployUrl,
61
53
  title: learnJson.title[selectedLang],
54
+ category: category,
62
55
  description: learnJson.description[selectedLang],
63
56
  all_translations,
64
57
  });
@@ -216,7 +209,9 @@ class BuildCommand extends SessionCommand_1.default {
216
209
  this.copyDirectory(assetsDir, path.join(buildDir, ".learn", "assets"));
217
210
  }
218
211
  else {
219
- fs.mkdirSync(path.join(buildDir, ".learn", "assets"), { recursive: true });
212
+ fs.mkdirSync(path.join(buildDir, ".learn", "assets"), {
213
+ recursive: true,
214
+ });
220
215
  }
221
216
  // Copy .learn/_app directory files to the same level as config.json
222
217
  const appDir = path.join(process.cwd(), ".learn", "_app");
@@ -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) {
@@ -147,6 +145,7 @@ async function startExerciseGeneration(rigoToken, steps, packageContext, exercis
147
145
  last_lesson: lastLesson,
148
146
  }, purposeSlug, webhookUrl);
149
147
  console.log("README CREATOR RES", res);
148
+ return res.id;
150
149
  }
151
150
  async function createInitialReadme(tutorialInfo, tutorialSlug, rigoToken) {
152
151
  const webhookUrl = `${process.env.HOST}/webhooks/${tutorialSlug}/initial-readme-processor`;
@@ -432,10 +431,18 @@ class ServeCommand extends SessionCommand_1.default {
432
431
  previousReadme = content.toString();
433
432
  }
434
433
  }
435
- await startExerciseGeneration(rigoToken, syllabusJson.lessons, syllabusJson.courseInfo, exercise, courseSlug, syllabusJson.courseInfo.purpose, previousReadme +
434
+ const completionId = await startExerciseGeneration(rigoToken, syllabusJson.lessons, syllabusJson.courseInfo, exercise, courseSlug, syllabusJson.courseInfo.purpose, previousReadme +
436
435
  "\n\nThe user provided the following feedback related to the content of the course so far: \n\n" +
437
436
  feedback);
438
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)]);
439
446
  if (syllabusJson.feedback &&
440
447
  typeof syllabusJson.feedback === "string") {
441
448
  syllabusJson.feedback += "\n\n" + feedback;
@@ -446,6 +453,23 @@ class ServeCommand extends SessionCommand_1.default {
446
453
  await uploadFileToBucket(bucket, JSON.stringify(syllabusJson), `courses/${courseSlug}/.learn/initialSyllabus.json`);
447
454
  res.json({ status: "SUCCESS" });
448
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
+ });
449
473
  app.post("/webhooks/:courseSlug/exercise-processor/:lessonID/:rigoToken", async (req, res) => {
450
474
  // console.log("Receiving a webhook to exercise processor")
451
475
  const { courseSlug, lessonID, rigoToken } = req.params;
@@ -498,40 +522,55 @@ class ServeCommand extends SessionCommand_1.default {
498
522
  log: `✅ Code file created for ${exercise.title}`,
499
523
  });
500
524
  }
501
- let nextStarted = false;
525
+ let nextCompletionId = null;
502
526
  if (nextExercise &&
503
527
  (exerciseIndex === 0 || !(exerciseIndex % 3 === 0))) {
504
528
  let feedback = "";
505
529
  if (syllabusJson.feedback) {
506
530
  feedback = `\n\nThe user added the following feedback with relation to the previous generations: ${syllabusJson.feedback}`;
507
531
  }
508
- startExerciseGeneration(rigoToken, syllabusJson.lessons, syllabusJson.courseInfo, nextExercise, courseSlug, syllabusJson.courseInfo.purpose, readme.parsed.content + "\n\n" + feedback);
509
- nextStarted = true;
532
+ nextCompletionId = await startExerciseGeneration(rigoToken, syllabusJson.lessons, syllabusJson.courseInfo, nextExercise, courseSlug, syllabusJson.courseInfo.purpose, readme.parsed.content + "\n\n" + feedback);
510
533
  }
511
534
  else {
512
535
  console.log("Stopping generation process at", exerciseIndex, exercise.title, "because it's a multiple of 3");
513
536
  }
514
537
  const imagesArray = (0, creatorUtilities_1.extractImagesFromMarkdown)(readability.newMarkdown);
515
- if (imagesArray.length > 0) {
516
- (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
517
- lesson: exSlug,
518
- status: "pending",
519
- log: `🔄 Generating images for ${exercise.title}`,
520
- });
521
- for (const image of imagesArray) {
522
- // eslint-disable-next-line no-await-in-loop
523
- await (0, exports.processImage)(image.url, image.alt, rigoToken, courseSlug);
524
- }
525
- }
526
538
  const newSyllabus = Object.assign(Object.assign({}, syllabusJson), { lessons: syllabusJson.lessons.map((lesson, index) => {
527
539
  if (index === exerciseIndex) {
528
- 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
+ } });
529
560
  }
530
- if (nextExercise && nextExercise.id === lesson.id && nextStarted) {
531
- 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
+ } });
532
570
  }
533
571
  return Object.assign({}, lesson);
534
572
  }) });
573
+ console.log("New syllabus", newSyllabus);
535
574
  await uploadFileToBucket(bucket, JSON.stringify(newSyllabus), `courses/${courseSlug}/.learn/initialSyllabus.json`);
536
575
  (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
537
576
  lesson: exSlug,
@@ -936,7 +975,16 @@ class ServeCommand extends SessionCommand_1.default {
936
975
  await uploadFileToBucket(bucket, JSON.stringify(sidebar), `${tutorialDir}/.learn/sidebar.json`);
937
976
  const firstLesson = syllabus.lessons[0];
938
977
  const lastResult = "---";
939
- await startExerciseGeneration(rigoToken, syllabus.lessons, syllabus.courseInfo, firstLesson, 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
+ }
940
988
  await createInitialReadme(JSON.stringify(syllabus.courseInfo), courseSlug, rigoToken);
941
989
  return res.json({
942
990
  message: "Course generation started",
@@ -957,29 +1005,44 @@ class ServeCommand extends SessionCommand_1.default {
957
1005
  return res.status(500).json({ error: "Error getting syllabus" });
958
1006
  }
959
1007
  });
960
- app.post("/actions/continue-course/:courseSlug", async (req, res) => {
961
- console.log("POST /actions/continue-course/:courseSlug");
962
- const { courseSlug } = req.params;
963
- const { feedback } = req.body;
964
- const rigoToken = req.header("x-rigo-token");
965
- const bcToken = req.header("x-breathecode-token");
966
- if (!rigoToken || !bcToken) {
967
- return res.status(400).json({ error: "Missing tokens" });
968
- }
969
- const syllabus = await bucket.file(`courses/${courseSlug}/.learn/initialSyllabus.json`);
970
- const [content] = await syllabus.download();
971
- const syllabusJson = JSON.parse(content.toString());
972
- const notGeneratedLessons = syllabusJson.lessons.filter(lesson => !lesson.generated);
973
- const lastGeneratedLesson = findLast(syllabusJson.lessons, lesson => { var _a; return (_a = lesson.generated) !== null && _a !== void 0 ? _a : false; });
974
- console.log("ABout to generate", notGeneratedLessons.length, "lessons");
975
- const firstLessonToGenerate = notGeneratedLessons[0];
976
- await startExerciseGeneration(rigoToken, syllabusJson.lessons, syllabusJson.courseInfo, firstLessonToGenerate, courseSlug, syllabusJson.courseInfo.purpose, JSON.stringify(lastGeneratedLesson) +
977
- `\n\nThe user provided this feedback in relation to the course: ${feedback}`);
978
- return res.json({
979
- message: "Course continued",
980
- slug: courseSlug,
981
- });
982
- });
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
+ // })
983
1046
  app.get("/courses/:courseSlug/exercises/:exerciseSlug/", async (req, res) => {
984
1047
  var _a;
985
1048
  console.log("GET /courses/:courseSlug/exercises/:exerciseSlug/");
@@ -1134,9 +1197,7 @@ class ServeCommand extends SessionCommand_1.default {
1134
1197
  }
1135
1198
  catch (error) {
1136
1199
  console.error("❌ Error fetching file:", error);
1137
- return res
1138
- .status(500)
1139
- .json({
1200
+ return res.status(500).json({
1140
1201
  error: error.message || "Unable to fetch file",
1141
1202
  });
1142
1203
  }
@@ -1150,28 +1211,25 @@ class ServeCommand extends SessionCommand_1.default {
1150
1211
  const ytMatch = decoded.match(YT_REGEX);
1151
1212
  if (ytMatch) {
1152
1213
  const videoId = ytMatch[1];
1153
- console.log("VIDEO ID", videoId);
1154
- // fetch metadata
1155
- const items = await youtube_transcript_1.YoutubeTranscript.fetchTranscript(videoId);
1156
- console.log("ITEMS FROM YOUTUBE TRANSCRIPT", items);
1157
- const transcript = items.map(i => i.text).join(" ");
1158
- let meta = null;
1159
- try {
1160
- const { data: meta } = await axios_1.default.get("https://www.youtube.com/oembed", {
1161
- params: { url: decoded, format: "json" },
1162
- });
1163
- console.log("META", meta);
1164
- }
1165
- catch (error) {
1166
- console.error("ERROR FETCHING META", error);
1167
- meta = null;
1168
- }
1169
- throw new Error("test");
1214
+ const resFromRigo = await axios_1.default.get(`${api_1.RIGOBOT_REALTIME_HOST}/actions/youtube-transcript/${videoId}`);
1215
+ const transcript = resFromRigo.data.transcript;
1216
+ // let meta: any = null
1217
+ // try {
1218
+ // const { data: meta } = await axios.get(
1219
+ // "https://www.youtube.com/oembed",
1220
+ // {
1221
+ // params: { url: decoded, format: "json" },
1222
+ // }
1223
+ // )
1224
+ // console.log("META", meta)
1225
+ // } catch (error) {
1226
+ // console.error("ERROR FETCHING META", error)
1227
+ // meta = null
1228
+ // }
1170
1229
  return res.json({
1171
1230
  url: decoded,
1172
- title: (meta === null || meta === void 0 ? void 0 : meta.title) || null,
1173
- author: (meta === null || meta === void 0 ? void 0 : meta.author_name) || null,
1174
- thumbnail: (meta === null || meta === void 0 ? void 0 : meta.thumbnail_url) || null,
1231
+ title: resFromRigo.data.title || null,
1232
+ author: resFromRigo.data.author || null,
1175
1233
  transcript,
1176
1234
  });
1177
1235
  }
@@ -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>;
@@ -18,7 +19,7 @@ type TAssetMissing = {
18
19
  description: string;
19
20
  learnpack_deploy_url: string;
20
21
  technologies: string[];
21
- category: number;
22
+ category: number | string;
22
23
  owner: number;
23
24
  author: number;
24
25
  preview: string;
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"
@@ -419,13 +420,14 @@ const getCategories = async (token) => {
419
420
  }
420
421
  };
421
422
  const updateRigoAssetID = async (token, slug, asset_id) => {
423
+ const cleanToken = token.replace(/[\n\r]/g, "");
422
424
  const url = `${exports.RIGOBOT_HOST}/v1/learnpack/package/${slug}/`;
423
- const headers = {
424
- Authorization: "Token " + token.trim(),
425
- };
426
- console.log("HEADERS", headers);
427
425
  try {
428
- const response = await axios_1.default.put(url, { asset_id }, { headers });
426
+ const response = await axios_1.default.put(url, { asset_id }, {
427
+ headers: {
428
+ Authorization: "Token " + cleanToken,
429
+ },
430
+ });
429
431
  return response.data;
430
432
  }
431
433
  catch (error) {
@@ -435,11 +437,13 @@ const updateRigoAssetID = async (token, slug, asset_id) => {
435
437
  };
436
438
  const createRigoPackage = async (token, slug, config) => {
437
439
  const url = `${exports.RIGOBOT_HOST}/v1/learnpack/package`;
438
- const headers = {
439
- Authorization: "Token " + token.trim(),
440
- };
440
+ const cleanToken = token.replace(/[\n\r]/g, "");
441
441
  try {
442
- const response = await axios_1.default.post(url, { slug, config }, { headers });
442
+ const response = await axios_1.default.post(url, { slug, config }, {
443
+ headers: {
444
+ Authorization: "Token " + cleanToken,
445
+ },
446
+ });
443
447
  return response.data;
444
448
  }
445
449
  catch (error) {
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.274",
4
+ "version": "5.0.276",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {