@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.
@@ -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, { prompt: image.alt });
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 (0, rigoActions_1.downloadImage)(res.image_url, imagePath);
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: {
@@ -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: 91,
25
- es: 92,
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)) {
@@ -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: (bucket: Bucket, tutorialDir: string, url: string, description: string, rigoToken: string) => Promise<boolean>;
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;
@@ -92,16 +92,46 @@ const PARAMS = {
92
92
  max_rewrite_attempts: 2,
93
93
  max_title_length: 50,
94
94
  };
95
- const processImage = async (bucket, tutorialDir, url, description, rigoToken) => {
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
- console.log("🖼️ IMAGE FILENAME", filename);
100
- const imagePath = `${tutorialDir}/.learn/assets/${filename}`;
101
- console.log("🖼️ Generating image", imagePath);
102
- const res = await (0, rigoActions_1.generateImage)(rigoToken, { prompt: description });
103
- await uploadImageToBucket(bucket, res.image_url, imagePath);
104
- console.log("✅ Image", imagePath, "generated successfully!");
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)(bucket, `courses/${courseSlug}`, image.url, image.alt, rigoToken);
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 (0, publish_1.handleAssetCreation)({ token: bcToken, rigobotToken: rigoToken }, fullConfig.config, selectedLang, rigoRes.data.url);
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(10),
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-x_kA-1DY.js"></script>
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>
@@ -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;
@@ -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.238",
4
+ "version": "5.0.240",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {
@@ -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, { prompt: image.alt })
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
  }
@@ -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: 91,
33
- es: 92,
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(sessionPayload, learnJson, "us", res.data.url)
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) {
@@ -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
- console.log("🖼️ IMAGE FILENAME", filename)
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
- const res = await generateImage(rigoToken, { prompt: description })
154
- await uploadImageToBucket(bucket, res.image_url, imagePath)
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(10)
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.")