@learnpack/learnpack 5.0.238 → 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 +1 -2
- package/lib/commands/serve.js +75 -11
- package/lib/creatorDist/assets/{index-x_kA-1DY.js → index-DJn8b8wj.js} +2 -2
- 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 +1 -1
- package/src/commands/init.ts +12 -2
- package/src/commands/publish.ts +12 -4
- package/src/commands/serve.ts +111 -21
- package/src/creator/src/utils/rigo.ts +4 -2
- package/src/creatorDist/assets/{index-x_kA-1DY.js → index-DJn8b8wj.js} +2 -2
- package/src/creatorDist/index.html +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/rigoActions.ts +32 -1
- package/oclif.manifest.json +0 -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,5 +1,4 @@
|
|
1
1
|
import SessionCommand from "../utils/SessionCommand";
|
2
|
-
import { Bucket } from "@google-cloud/storage";
|
3
2
|
import { FormState } from "../models/creator";
|
4
3
|
export declare const createLearnJson: (courseInfo: FormState) => {
|
5
4
|
slug: string;
|
@@ -20,7 +19,7 @@ export declare const createLearnJson: (courseInfo: FormState) => {
|
|
20
19
|
};
|
21
20
|
preview: string;
|
22
21
|
};
|
23
|
-
export declare const processImage: (
|
22
|
+
export declare const processImage: (url: string, description: string, rigoToken: string, courseSlug: string) => Promise<boolean>;
|
24
23
|
export default class ServeCommand extends SessionCommand {
|
25
24
|
static description: string;
|
26
25
|
static flags: any;
|
package/lib/commands/serve.js
CHANGED
@@ -92,16 +92,46 @@ const PARAMS = {
|
|
92
92
|
max_rewrite_attempts: 2,
|
93
93
|
max_title_length: 50,
|
94
94
|
};
|
95
|
-
|
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) => {
|
96
125
|
try {
|
97
126
|
// TODO: MAKE THIS ASYNC
|
98
127
|
const filename = (0, creatorUtilities_1.getFilenameFromUrl)(url);
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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!")
|
105
135
|
return true;
|
106
136
|
}
|
107
137
|
catch (_a) {
|
@@ -150,6 +180,19 @@ async function startExerciseGeneration(bucket, rigoToken, steps, packageContext,
|
|
150
180
|
last_lesson: lastLesson,
|
151
181
|
}, purposeSlug, webhookUrl);
|
152
182
|
}
|
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);
|
191
|
+
}
|
192
|
+
catch (error) {
|
193
|
+
console.error("Error creating initial readme", error);
|
194
|
+
}
|
195
|
+
}
|
153
196
|
const fixPreviewUrl = (slug, previewUrl) => {
|
154
197
|
if (!previewUrl) {
|
155
198
|
return null;
|
@@ -315,6 +358,20 @@ class ServeCommand extends SessionCommand_1.default {
|
|
315
358
|
(0, creatorSocket_1.emitToNotification)(id, body);
|
316
359
|
res.json({ id, status: "SUCCESS" });
|
317
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
|
+
});
|
318
375
|
app.post("/webhooks/:courseSlug/images/:imageId", async (req, res) => {
|
319
376
|
const { courseSlug, imageId } = req.params;
|
320
377
|
const body = req.body;
|
@@ -401,7 +458,7 @@ class ServeCommand extends SessionCommand_1.default {
|
|
401
458
|
});
|
402
459
|
for (const image of imagesArray) {
|
403
460
|
// eslint-disable-next-line no-await-in-loop
|
404
|
-
await (0, exports.processImage)(
|
461
|
+
await (0, exports.processImage)(image.url, image.alt, rigoToken, courseSlug);
|
405
462
|
}
|
406
463
|
}
|
407
464
|
(0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
|
@@ -755,6 +812,7 @@ class ServeCommand extends SessionCommand_1.default {
|
|
755
812
|
const firstLesson = syllabus.lessons[0];
|
756
813
|
const lastResult = "Nothing";
|
757
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);
|
758
816
|
return res.json({
|
759
817
|
message: "Course created",
|
760
818
|
slug: (0, creatorUtilities_2.slugify)(syllabus.courseInfo.title),
|
@@ -873,7 +931,6 @@ class ServeCommand extends SessionCommand_1.default {
|
|
873
931
|
title = config.title[availableLangs[0]];
|
874
932
|
selectedLang = availableLangs[0];
|
875
933
|
}
|
876
|
-
console.log(config.description, "CONFIG DESCRIPTION");
|
877
934
|
// console.log(availableLangs, "AVAILABLE LANGs")
|
878
935
|
// console.log(selectedLang, "SELECTED LANG")
|
879
936
|
// console.log(title, "TITLE")
|
@@ -920,10 +977,17 @@ class ServeCommand extends SessionCommand_1.default {
|
|
920
977
|
form.append("file", fs.createReadStream(zipPath));
|
921
978
|
form.append("config", JSON.stringify(config));
|
922
979
|
const rigoRes = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/learnpack/upload`, form, {
|
923
|
-
headers: Object.assign(Object.assign({}, form.getHeaders()), { Authorization: `Token ${rigoToken}` }),
|
980
|
+
headers: Object.assign(Object.assign({}, form.getHeaders()), { Authorization: `Token ${rigoToken.trim()}` }),
|
924
981
|
});
|
925
|
-
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);
|
926
989
|
rimraf.sync(tmpRoot);
|
990
|
+
console.log("RigoRes", rigoRes.data);
|
927
991
|
return res.json({ url: rigoRes.data.url });
|
928
992
|
});
|
929
993
|
archive.on("error", err => {
|
@@ -18246,7 +18246,7 @@ const p2 = async (e) => {
|
|
18246
18246
|
g2 = async (e, t, n, r = !0) => {
|
18247
18247
|
var a
|
18248
18248
|
try {
|
18249
|
-
const l = Ku(
|
18249
|
+
const l = Ku(15),
|
18250
18250
|
u = `${
|
18251
18251
|
_1
|
18252
18252
|
? "https://9cw5zmww-3000.use2.devtunnels.ms"
|
@@ -18277,7 +18277,7 @@ const p2 = async (e) => {
|
|
18277
18277
|
} catch (l) {
|
18278
18278
|
const u = l
|
18279
18279
|
return (
|
18280
|
-
console.log("error", u),
|
18280
|
+
console.log("error trying to create course", u),
|
18281
18281
|
((a = u.response) == null ? void 0 : a.status) === 403
|
18282
18282
|
? Ge.error("You've reached the limit. Please log in to continue.")
|
18283
18283
|
: Ge.error("Something went wrong while generating the course."),
|
@@ -10,7 +10,7 @@
|
|
10
10
|
/>
|
11
11
|
|
12
12
|
<title>Learnpack Creator: Craft tutorials in seconds!</title>
|
13
|
-
<script type="module" crossorigin src="/creator/assets/index-
|
13
|
+
<script type="module" crossorigin src="/creator/assets/index-DJn8b8wj.js"></script>
|
14
14
|
<link rel="stylesheet" crossorigin href="/creator/assets/index-DmpsXknz.css">
|
15
15
|
</head>
|
16
16
|
<body>
|
package/lib/utils/api.d.ts
CHANGED
@@ -22,6 +22,7 @@ type TAssetMissing = {
|
|
22
22
|
owner: number;
|
23
23
|
author: number;
|
24
24
|
preview: string;
|
25
|
+
readme_raw: string;
|
25
26
|
};
|
26
27
|
export declare const createAsset: (token: string, asset: TAssetMissing) => Promise<any>;
|
27
28
|
export declare const doesAssetExists: (token: string, assetSlug: string) => Promise<{
|
package/lib/utils/api.js
CHANGED
@@ -353,6 +353,7 @@ const createAsset = async (token, asset) => {
|
|
353
353
|
translations: [asset.lang],
|
354
354
|
learnpack_deploy_url: asset.learnpack_deploy_url,
|
355
355
|
technologies: asset.technologies,
|
356
|
+
readme_raw: asset.readme_raw,
|
356
357
|
};
|
357
358
|
const url = `https://breathecode.herokuapp.com/v1/registry/asset/me`;
|
358
359
|
const headers = {
|
@@ -12,8 +12,9 @@ export declare const createReadme: (token: string, inputs: TCreateReadmeInputs,
|
|
12
12
|
export declare const hasCreatorPermission: (token: string) => Promise<boolean>;
|
13
13
|
type TGenerateImageParams = {
|
14
14
|
prompt: string;
|
15
|
+
callbackUrl: string;
|
15
16
|
};
|
16
|
-
export declare const generateImage: (token: string, { prompt }: TGenerateImageParams) => Promise<any>;
|
17
|
+
export declare const generateImage: (token: string, { prompt, callbackUrl }: TGenerateImageParams) => Promise<any>;
|
17
18
|
export declare function downloadImage(imageUrl: string, savePath: string): Promise<void>;
|
18
19
|
type TTranslateInputs = {
|
19
20
|
text_to_translate: string;
|
@@ -55,6 +56,10 @@ type TReadmeCreatorInputs = {
|
|
55
56
|
last_lesson: string;
|
56
57
|
};
|
57
58
|
export declare const readmeCreator: (token: string, inputs: TReadmeCreatorInputs, purpose: string, webhookUrl?: string) => Promise<any>;
|
59
|
+
type TCreateStructuredPreviewReadmeInputs = {
|
60
|
+
tutorial_info: string;
|
61
|
+
};
|
62
|
+
export declare const createStructuredPreviewReadme: (token: string, inputs: TCreateStructuredPreviewReadmeInputs, webhookUrl?: string) => Promise<any>;
|
58
63
|
export declare function createPreviewReadme(tutorialDir: string, packageInfo: PackageInfo, rigoToken: string, readmeContents: string[]): Promise<void>;
|
59
64
|
type TReduceReadmeInputs = {
|
60
65
|
lesson: string;
|
package/lib/utils/rigoActions.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.isPackageAuthor = exports.fillSidebarJSON = exports.generateCourseShortName = exports.isValidRigoToken = exports.readmeCreator = exports.createCodingReadme = exports.createCodeFile = exports.interactiveCreation = exports.generateCourseIntroduction = exports.translateExercise = exports.generateImage = exports.hasCreatorPermission = exports.createReadme = void 0;
|
3
|
+
exports.isPackageAuthor = exports.fillSidebarJSON = exports.generateCourseShortName = exports.isValidRigoToken = exports.createStructuredPreviewReadme = exports.readmeCreator = exports.createCodingReadme = exports.createCodeFile = exports.interactiveCreation = exports.generateCourseIntroduction = exports.translateExercise = exports.generateImage = exports.hasCreatorPermission = exports.createReadme = void 0;
|
4
4
|
exports.downloadImage = downloadImage;
|
5
5
|
exports.createPreviewReadme = createPreviewReadme;
|
6
6
|
exports.makeReadmeReadable = makeReadmeReadable;
|
@@ -53,10 +53,13 @@ const hasCreatorPermission = async (token) => {
|
|
53
53
|
}
|
54
54
|
};
|
55
55
|
exports.hasCreatorPermission = hasCreatorPermission;
|
56
|
-
const generateImage = async (token, { prompt }) => {
|
56
|
+
const generateImage = async (token, { prompt, callbackUrl }) => {
|
57
57
|
try {
|
58
58
|
const response = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/learnpack/tools/images`, {
|
59
59
|
prompt,
|
60
|
+
webhook_callback_url: callbackUrl,
|
61
|
+
provider: "bfl",
|
62
|
+
model: "flux-pro-1.1",
|
60
63
|
}, {
|
61
64
|
headers: {
|
62
65
|
"Content-Type": "application/json",
|
@@ -174,6 +177,20 @@ const readmeCreator = async (token, inputs, purpose, webhookUrl) => {
|
|
174
177
|
throw new Error("Invalid kind of lesson");
|
175
178
|
};
|
176
179
|
exports.readmeCreator = readmeCreator;
|
180
|
+
const createStructuredPreviewReadme = async (token, inputs, webhookUrl) => {
|
181
|
+
const response = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/prompting/completion/write-course-introduction-readme/`, {
|
182
|
+
inputs: inputs,
|
183
|
+
include_purpose_objective: false,
|
184
|
+
webhook_url: webhookUrl,
|
185
|
+
}, {
|
186
|
+
headers: {
|
187
|
+
"Content-Type": "application/json",
|
188
|
+
Authorization: "Token " + token,
|
189
|
+
},
|
190
|
+
});
|
191
|
+
return response.data;
|
192
|
+
};
|
193
|
+
exports.createStructuredPreviewReadme = createStructuredPreviewReadme;
|
177
194
|
async function createPreviewReadme(tutorialDir, packageInfo, rigoToken, readmeContents) {
|
178
195
|
const readmeFilename = `README.md`;
|
179
196
|
const readmeContent = await (0, exports.generateCourseIntroduction)(rigoToken, {
|
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.240",
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
6
6
|
"contributors": [
|
7
7
|
{
|
package/src/commands/init.ts
CHANGED
@@ -35,6 +35,7 @@ import {
|
|
35
35
|
estimateDuration,
|
36
36
|
appendContentIndex,
|
37
37
|
appendAIRules,
|
38
|
+
slugify,
|
38
39
|
} from "../utils/creatorUtilities"
|
39
40
|
import SessionManager from "../managers/session"
|
40
41
|
|
@@ -407,10 +408,16 @@ const handleAILogic = async (tutorialDir: string, packageInfo: PackageInfo) => {
|
|
407
408
|
const imagePromises = imagesArray.map(async (image: any) => {
|
408
409
|
try {
|
409
410
|
const filename = getFilenameFromUrl(image.url)
|
411
|
+
const webhookUrl = `${process.env.HOST}/webhooks/${slugify(
|
412
|
+
packageInfo.title.us
|
413
|
+
)}/images/${filename}`
|
410
414
|
|
411
415
|
const imagePath = path.join(tutorialDir, ".learn", "assets", filename)
|
412
416
|
|
413
|
-
const res = await generateImage(rigoToken, {
|
417
|
+
const res = await generateImage(rigoToken, {
|
418
|
+
prompt: image.alt,
|
419
|
+
callbackUrl: webhookUrl,
|
420
|
+
})
|
414
421
|
await downloadImage(res.image_url, imagePath)
|
415
422
|
return true
|
416
423
|
} catch {
|
@@ -434,9 +441,12 @@ const handleAILogic = async (tutorialDir: string, packageInfo: PackageInfo) => {
|
|
434
441
|
"Generate a preview image for the tutorial. This is all the tutorial information: " +
|
435
442
|
packageContext +
|
436
443
|
"\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.",
|
444
|
+
callbackUrl: `${process.env.HOST}/webhooks/${slugify(
|
445
|
+
packageInfo.title.us
|
446
|
+
)}/images/preview.png`,
|
437
447
|
})
|
438
448
|
|
439
|
-
await downloadImage(res.image_url, imagePath)
|
449
|
+
// await downloadImage(res.image_url, imagePath)
|
440
450
|
|
441
451
|
return true
|
442
452
|
}
|
package/src/commands/publish.ts
CHANGED
@@ -26,11 +26,12 @@ export const handleAssetCreation = async (
|
|
26
26
|
sessionPayload: { token: string; rigobotToken: string },
|
27
27
|
learnJson: any,
|
28
28
|
selectedLang: string,
|
29
|
-
learnpackDeployUrl: string
|
29
|
+
learnpackDeployUrl: string,
|
30
|
+
b64IndexReadme: string
|
30
31
|
) => {
|
31
32
|
const categories: Record<string, number> = {
|
32
|
-
us:
|
33
|
-
es:
|
33
|
+
us: 9,
|
34
|
+
es: 10,
|
34
35
|
}
|
35
36
|
|
36
37
|
let category = categories[selectedLang]
|
@@ -61,6 +62,7 @@ export const handleAssetCreation = async (
|
|
61
62
|
owner: user.id,
|
62
63
|
author: user.id,
|
63
64
|
preview: learnJson.preview,
|
65
|
+
readme_raw: b64IndexReadme,
|
64
66
|
})
|
65
67
|
await api.updateRigoAssetID(
|
66
68
|
sessionPayload.token,
|
@@ -435,7 +437,13 @@ class BuildCommand extends SessionCommand {
|
|
435
437
|
fs.unlinkSync(zipFilePath)
|
436
438
|
this.removeDirectory(buildDir)
|
437
439
|
|
438
|
-
await handleAssetCreation(
|
440
|
+
await handleAssetCreation(
|
441
|
+
sessionPayload,
|
442
|
+
learnJson,
|
443
|
+
"us",
|
444
|
+
res.data.url,
|
445
|
+
""
|
446
|
+
)
|
439
447
|
} catch (error) {
|
440
448
|
if (axios.isAxiosError(error)) {
|
441
449
|
if (error.response && error.response.status === 403) {
|
package/src/commands/serve.ts
CHANGED
@@ -30,6 +30,7 @@ import {
|
|
30
30
|
// makeReadmeReadable,
|
31
31
|
generateImage,
|
32
32
|
isPackageAuthor,
|
33
|
+
createStructuredPreviewReadme,
|
33
34
|
} from "../utils/rigoActions"
|
34
35
|
import * as dotenv from "dotenv"
|
35
36
|
import {
|
@@ -134,26 +135,60 @@ const PARAMS = {
|
|
134
135
|
max_title_length: 50,
|
135
136
|
}
|
136
137
|
|
138
|
+
// app.post("/webhooks/:courseSlug/images/:imageId", async (req, res) => {
|
139
|
+
// const { courseSlug, imageId } = req.params
|
140
|
+
// const body = req.body
|
141
|
+
// console.log("RECEIVING IMAGE WEBHOOK", body)
|
142
|
+
|
143
|
+
// const imageUrl = body.image_url
|
144
|
+
// const imagePath = `courses/${courseSlug}/.learn/assets/${imageId}`
|
145
|
+
|
146
|
+
// const imageFile = bucket.file(imagePath)
|
147
|
+
// const [exists] = await imageFile.exists()
|
148
|
+
// if (!exists) {
|
149
|
+
// // Descargar la imagen
|
150
|
+
// const response = await fetch(imageUrl)
|
151
|
+
// if (!response.ok) {
|
152
|
+
// return res.status(400).json({
|
153
|
+
// status: "ERROR",
|
154
|
+
// message: "No se pudo descargar la imagen",
|
155
|
+
// })
|
156
|
+
// }
|
157
|
+
|
158
|
+
// const buffer = await response.arrayBuffer()
|
159
|
+
|
160
|
+
// // Guardar la imagen en el bucket
|
161
|
+
// await imageFile.save(new Uint8Array(buffer), {
|
162
|
+
// contentType: "image/png",
|
163
|
+
// })
|
164
|
+
// }
|
165
|
+
|
166
|
+
// emitToNotification(imageId, {
|
167
|
+
// status: "SUCCESS",
|
168
|
+
// message: "Image generated successfully",
|
169
|
+
// })
|
170
|
+
// res.json({ status: "SUCCESS" })
|
171
|
+
// })
|
172
|
+
|
137
173
|
export const processImage = async (
|
138
|
-
bucket: Bucket,
|
139
|
-
tutorialDir: string,
|
140
174
|
url: string,
|
141
175
|
description: string,
|
142
|
-
rigoToken: string
|
176
|
+
rigoToken: string,
|
177
|
+
courseSlug: string
|
143
178
|
) => {
|
144
179
|
try {
|
145
180
|
// TODO: MAKE THIS ASYNC
|
146
181
|
const filename = getFilenameFromUrl(url)
|
147
182
|
|
148
|
-
|
149
|
-
const imagePath = `${tutorialDir}/.learn/assets/${filename}`
|
150
|
-
|
151
|
-
console.log("🖼️ Generating image", imagePath)
|
183
|
+
const webhookUrl = `${process.env.HOST}/webhooks/${courseSlug}/images/${filename}`
|
152
184
|
|
153
|
-
|
154
|
-
|
185
|
+
await generateImage(rigoToken, {
|
186
|
+
prompt: description,
|
187
|
+
callbackUrl: webhookUrl,
|
188
|
+
})
|
189
|
+
// await uploadImageToBucket(bucket, res.image_url, imagePath)
|
155
190
|
|
156
|
-
console.log("✅ Image", imagePath, "generated successfully!")
|
191
|
+
// console.log("✅ Image", imagePath, "generated successfully!")
|
157
192
|
return true
|
158
193
|
} catch {
|
159
194
|
return false
|
@@ -253,6 +288,27 @@ async function startExerciseGeneration(
|
|
253
288
|
)
|
254
289
|
}
|
255
290
|
|
291
|
+
async function createInitialReadme(
|
292
|
+
tutorialInfo: string,
|
293
|
+
tutorialSlug: string,
|
294
|
+
rigoToken: string
|
295
|
+
) {
|
296
|
+
const webhookUrl = `${process.env.HOST}/webhooks/${tutorialSlug}/initial-readme-processor`
|
297
|
+
console.log("Creating initial readme", webhookUrl)
|
298
|
+
try {
|
299
|
+
const res = await createStructuredPreviewReadme(
|
300
|
+
rigoToken,
|
301
|
+
{
|
302
|
+
tutorial_info: tutorialInfo,
|
303
|
+
},
|
304
|
+
webhookUrl
|
305
|
+
)
|
306
|
+
console.log("Initial readme created", res)
|
307
|
+
} catch (error) {
|
308
|
+
console.error("Error creating initial readme", error)
|
309
|
+
}
|
310
|
+
}
|
311
|
+
|
256
312
|
const fixPreviewUrl = (slug: string, previewUrl: string) => {
|
257
313
|
if (!previewUrl) {
|
258
314
|
return null
|
@@ -482,6 +538,28 @@ export default class ServeCommand extends SessionCommand {
|
|
482
538
|
res.json({ id, status: "SUCCESS" })
|
483
539
|
})
|
484
540
|
|
541
|
+
app.post(
|
542
|
+
"/webhooks/:courseSlug/initial-readme-processor",
|
543
|
+
async (req, res) => {
|
544
|
+
const { courseSlug } = req.params
|
545
|
+
const body = req.body
|
546
|
+
|
547
|
+
console.log("RECEIVING INITIAL README WEBHOOK", body)
|
548
|
+
// Save the file as courses/courseSlug/README.md
|
549
|
+
const filePath = `courses/${courseSlug}/README.${
|
550
|
+
body.parsed.language_code === "us" ||
|
551
|
+
body.parsed.language_code === "en" ?
|
552
|
+
"md" :
|
553
|
+
`${body.parsed.language_code}.md`
|
554
|
+
}`
|
555
|
+
console.log("Saving initial readme to", filePath)
|
556
|
+
await uploadFileToBucket(bucket, body.parsed.content, filePath)
|
557
|
+
console.log("Initial readme saved to", filePath)
|
558
|
+
|
559
|
+
res.json({ status: "SUCCESS" })
|
560
|
+
}
|
561
|
+
)
|
562
|
+
|
485
563
|
app.post("/webhooks/:courseSlug/images/:imageId", async (req, res) => {
|
486
564
|
const { courseSlug, imageId } = req.params
|
487
565
|
const body = req.body
|
@@ -629,13 +707,7 @@ export default class ServeCommand extends SessionCommand {
|
|
629
707
|
})
|
630
708
|
for (const image of imagesArray) {
|
631
709
|
// eslint-disable-next-line no-await-in-loop
|
632
|
-
await processImage(
|
633
|
-
bucket,
|
634
|
-
`courses/${courseSlug}`,
|
635
|
-
image.url,
|
636
|
-
image.alt,
|
637
|
-
rigoToken
|
638
|
-
)
|
710
|
+
await processImage(image.url, image.alt, rigoToken, courseSlug)
|
639
711
|
}
|
640
712
|
}
|
641
713
|
|
@@ -1161,6 +1233,12 @@ export default class ServeCommand extends SessionCommand {
|
|
1161
1233
|
lastResult
|
1162
1234
|
)
|
1163
1235
|
|
1236
|
+
await createInitialReadme(
|
1237
|
+
JSON.stringify(syllabus.courseInfo),
|
1238
|
+
courseSlug,
|
1239
|
+
rigoToken
|
1240
|
+
)
|
1241
|
+
|
1164
1242
|
return res.json({
|
1165
1243
|
message: "Course created",
|
1166
1244
|
slug: slugify(syllabus.courseInfo.title),
|
@@ -1331,7 +1409,6 @@ export default class ServeCommand extends SessionCommand {
|
|
1331
1409
|
selectedLang = availableLangs[0]
|
1332
1410
|
}
|
1333
1411
|
|
1334
|
-
console.log(config.description, "CONFIG DESCRIPTION")
|
1335
1412
|
// console.log(availableLangs, "AVAILABLE LANGs")
|
1336
1413
|
// console.log(selectedLang, "SELECTED LANG")
|
1337
1414
|
// console.log(title, "TITLE")
|
@@ -1403,20 +1480,33 @@ export default class ServeCommand extends SessionCommand {
|
|
1403
1480
|
{
|
1404
1481
|
headers: {
|
1405
1482
|
...form.getHeaders(),
|
1406
|
-
Authorization: `Token ${rigoToken}`,
|
1483
|
+
Authorization: `Token ${rigoToken.trim()}`,
|
1407
1484
|
},
|
1408
1485
|
}
|
1409
1486
|
)
|
1410
1487
|
|
1488
|
+
const indexReadme = await bucket.file(
|
1489
|
+
`courses/${slug}/README.${
|
1490
|
+
selectedLang === "us" || selectedLang === "en" ?
|
1491
|
+
"md" :
|
1492
|
+
`${selectedLang}.md`
|
1493
|
+
}`
|
1494
|
+
)
|
1495
|
+
const [indexReadmeContent] = await indexReadme.download()
|
1496
|
+
const indexReadmeString = indexReadmeContent.toString()
|
1497
|
+
const b64IndexReadme =
|
1498
|
+
Buffer.from(indexReadmeString).toString("base64")
|
1499
|
+
|
1411
1500
|
await handleAssetCreation(
|
1412
1501
|
{ token: bcToken, rigobotToken: rigoToken },
|
1413
1502
|
fullConfig.config,
|
1414
1503
|
selectedLang,
|
1415
|
-
rigoRes.data.url
|
1504
|
+
rigoRes.data.url,
|
1505
|
+
b64IndexReadme
|
1416
1506
|
)
|
1417
1507
|
|
1418
1508
|
rimraf.sync(tmpRoot)
|
1419
|
-
|
1509
|
+
console.log("RigoRes", rigoRes.data)
|
1420
1510
|
return res.json({ url: rigoRes.data.url })
|
1421
1511
|
})
|
1422
1512
|
|
@@ -15,12 +15,14 @@ export const publicInteractiveCreation = async (
|
|
15
15
|
publicRequest: boolean = true
|
16
16
|
): Promise<any | null> => {
|
17
17
|
try {
|
18
|
-
const randomUID = randomUUID(
|
18
|
+
const randomUID = randomUUID(15)
|
19
19
|
const webhookUrl = `${
|
20
20
|
DEV_MODE
|
21
21
|
? "https://9cw5zmww-3000.use2.devtunnels.ms"
|
22
22
|
: window.location.origin
|
23
|
+
// "https://9cw5zmww-3000.use2.devtunnels.ms"
|
23
24
|
}/notifications/${randomUID}`
|
25
|
+
|
24
26
|
const response = await axios.post(
|
25
27
|
`${RIGOBOT_HOST}/v1/prompting${
|
26
28
|
publicRequest ? "/public" : ""
|
@@ -42,7 +44,7 @@ export const publicInteractiveCreation = async (
|
|
42
44
|
return { res: response.data, notificationId: randomUID }
|
43
45
|
} catch (error: unknown) {
|
44
46
|
const err = error as AxiosError
|
45
|
-
console.log("error", err)
|
47
|
+
console.log("error trying to create course", err)
|
46
48
|
|
47
49
|
if (err.response?.status === 403) {
|
48
50
|
toast.error("You've reached the limit. Please log in to continue.")
|