@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.
- package/lib/commands/init.js +7 -2
- package/lib/commands/publish.d.ts +1 -1
- package/lib/commands/publish.js +5 -4
- package/lib/commands/serve.d.ts +2 -4
- package/lib/commands/serve.js +79 -116
- package/lib/creatorDist/assets/{index-CFK5bQP2.js → index-DJn8b8wj.js} +8168 -8115
- package/lib/creatorDist/index.html +1 -1
- package/lib/utils/api.d.ts +1 -0
- package/lib/utils/api.js +1 -0
- package/lib/utils/rigoActions.d.ts +6 -1
- package/lib/utils/rigoActions.js +19 -2
- package/package.json +2 -2
- package/src/commands/init.ts +12 -2
- package/src/commands/publish.ts +12 -4
- package/src/commands/serve.ts +114 -181
- package/src/creator/src/App.tsx +67 -62
- package/src/creator/src/components/LessonItem.tsx +9 -3
- package/src/creator/src/components/NotificationListener.tsx +32 -0
- package/src/creator/src/components/syllabus/SyllabusEditor.tsx +42 -44
- package/src/creator/src/utils/creatorUtils.ts +2 -2
- package/src/creator/src/utils/rigo.ts +12 -3
- package/src/creator/src/utils/store.ts +13 -2
- package/src/creatorDist/assets/{index-CFK5bQP2.js → index-DJn8b8wj.js} +8168 -8115
- package/src/creatorDist/index.html +1 -1
- package/src/ui/_app/app.css +1 -1
- package/src/ui/_app/app.js +131 -131
- package/src/ui/app.tar.gz +0 -0
- package/src/utils/api.ts +2 -0
- package/src/utils/configBuilder.ts +1 -2
- package/src/utils/rigoActions.ts +32 -1
package/lib/commands/init.js
CHANGED
@@ -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, {
|
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
|
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: {
|
package/lib/commands/publish.js
CHANGED
@@ -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:
|
25
|
-
es:
|
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)) {
|
package/lib/commands/serve.d.ts
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import SessionCommand from "../utils/SessionCommand";
|
2
|
-
import {
|
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: (
|
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;
|
package/lib/commands/serve.js
CHANGED
@@ -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
|
-
|
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
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
154
|
-
const
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
""
|
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
|
-
|
211
|
-
|
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)(
|
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
|
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 => {
|