@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.
- package/lib/commands/publish.js +5 -10
- package/lib/commands/serve.js +125 -67
- package/lib/models/creator.d.ts +7 -0
- package/lib/utils/api.d.ts +2 -1
- package/lib/utils/api.js +14 -10
- package/package.json +1 -1
- package/src/commands/publish.ts +517 -522
- package/src/commands/serve.ts +159 -99
- package/src/models/creator.ts +7 -0
- package/src/ui/_app/app.css +1 -1
- package/src/ui/_app/app.js +366 -363
- package/src/ui/app.tar.gz +0 -0
- package/src/utils/api.ts +25 -10
package/lib/commands/publish.js
CHANGED
@@ -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
|
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"), {
|
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");
|
package/lib/commands/serve.js
CHANGED
@@ -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
|
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
|
-
|
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 &&
|
531
|
-
|
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
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
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
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
const
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
}
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
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:
|
1173
|
-
author:
|
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
|
}
|
package/lib/models/creator.d.ts
CHANGED
@@ -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;
|
package/lib/utils/api.d.ts
CHANGED
@@ -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 }, {
|
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
|
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 }, {
|
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.
|
4
|
+
"version": "5.0.276",
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
6
6
|
"contributors": [
|
7
7
|
{
|