@learnpack/learnpack 5.0.238 → 5.0.244

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.
@@ -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-BWHp9KF3.js"></script>
14
14
  <link rel="stylesheet" crossorigin href="/creator/assets/index-DmpsXknz.css">
15
15
  </head>
16
16
  <body>
@@ -23,6 +23,7 @@ export type FormState = {
23
23
  currentStep: string;
24
24
  title: string;
25
25
  purpose: string;
26
+ slug: string;
26
27
  };
27
28
  export type Syllabus = {
28
29
  lessons: Lesson[];
@@ -22,6 +22,8 @@ type TAssetMissing = {
22
22
  owner: number;
23
23
  author: number;
24
24
  preview: string;
25
+ readme_raw: string;
26
+ all_translations: string[];
25
27
  };
26
28
  export declare const createAsset: (token: string, asset: TAssetMissing) => Promise<any>;
27
29
  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 = {
@@ -1,5 +1,5 @@
1
1
  import { Server as SocketIOServer } from "socket.io";
2
2
  export declare function initSocketIO(server: any): SocketIOServer<import("socket.io/dist/typed-events").DefaultEventsMap, import("socket.io/dist/typed-events").DefaultEventsMap, import("socket.io/dist/typed-events").DefaultEventsMap, any>;
3
3
  export declare function emitToCourse(courseSlug: string, event: string, payload: any): void;
4
- export declare function emitToNotification(notificationId: string, payload: any): void;
4
+ export declare function emitToNotification(notificationId: string, payload: any, retry?: number): void;
5
5
  export declare function getSocketIO(): SocketIOServer<import("socket.io/dist/typed-events").DefaultEventsMap, import("socket.io/dist/typed-events").DefaultEventsMap, import("socket.io/dist/typed-events").DefaultEventsMap, any>;
@@ -62,11 +62,18 @@ function emitToCourse(courseSlug, event, payload) {
62
62
  socket.emit(event, payload);
63
63
  }
64
64
  }
65
- function emitToNotification(notificationId, payload) {
66
- console.log("Emitting to notification", notificationId, payload);
65
+ function emitToNotification(notificationId, payload, retry = 0) {
67
66
  const socketIds = notificationSocketMap.get(notificationId);
68
- if (!socketIds || socketIds.size === 0)
67
+ if (!socketIds || socketIds.size === 0) {
68
+ if (retry > 3) {
69
+ console.log("❌ Notification", notificationId, "not found");
70
+ return;
71
+ }
72
+ setTimeout(() => {
73
+ emitToNotification(notificationId, payload, retry + 1);
74
+ }, 3000);
69
75
  return;
76
+ }
70
77
  for (const id of socketIds) {
71
78
  const socket = socketStore.get(id);
72
79
  if (socket)
@@ -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,15 @@ 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>;
63
+ export declare const translateCourseMetadata: (token: string, inputs: {
64
+ title: string;
65
+ description: string;
66
+ destination_lang_code: string;
67
+ }) => Promise<any>;
58
68
  export declare function createPreviewReadme(tutorialDir: string, packageInfo: PackageInfo, rigoToken: string, readmeContents: string[]): Promise<void>;
59
69
  type TReduceReadmeInputs = {
60
70
  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.translateCourseMetadata = 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,30 @@ 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;
194
+ const translateCourseMetadata = async (token, inputs) => {
195
+ const response = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/prompting/completion/translate-course-metadata/`, { inputs, include_purpose_objective: false, execute_async: false }, {
196
+ headers: {
197
+ "Content-Type": "application/json",
198
+ Authorization: "Token " + token,
199
+ },
200
+ });
201
+ return response.data;
202
+ };
203
+ exports.translateCourseMetadata = translateCourseMetadata;
177
204
  async function createPreviewReadme(tutorialDir, packageInfo, rigoToken, readmeContents) {
178
205
  const readmeFilename = `README.md`;
179
206
  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.244",
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
  }
@@ -19,6 +19,7 @@ import api, { getConsumable, RIGOBOT_HOST, TAcademy } from "../utils/api"
19
19
  import * as prompts from "prompts"
20
20
  import { isValidRigoToken } from "../utils/rigoActions"
21
21
  import { minutesToISO8601Duration } from "../utils/misc"
22
+ import { slugify } from "../utils/creatorUtilities"
22
23
 
23
24
  const uploadZipEndpont = RIGOBOT_HOST + "/v1/learnpack/upload"
24
25
 
@@ -26,11 +27,13 @@ export const handleAssetCreation = async (
26
27
  sessionPayload: { token: string; rigobotToken: string },
27
28
  learnJson: any,
28
29
  selectedLang: string,
29
- learnpackDeployUrl: string
30
+ learnpackDeployUrl: string,
31
+ b64IndexReadme: string,
32
+ all_translations: string[] = []
30
33
  ) => {
31
34
  const categories: Record<string, number> = {
32
- us: 91,
33
- es: 92,
35
+ us: 9,
36
+ es: 10,
34
37
  }
35
38
 
36
39
  let category = categories[selectedLang]
@@ -42,15 +45,13 @@ export const handleAssetCreation = async (
42
45
  try {
43
46
  const user = await api.validateToken(sessionPayload.token)
44
47
 
45
- const { exists } = await api.doesAssetExists(
46
- sessionPayload.token,
47
- learnJson.slug
48
- )
48
+ const slug = slugify(learnJson.title[selectedLang]).slice(0, 50)
49
+ const { exists } = await api.doesAssetExists(sessionPayload.token, slug)
49
50
 
50
51
  if (!exists) {
51
52
  Console.info("Asset does not exist in this academy, creating it")
52
53
  const asset = await api.createAsset(sessionPayload.token, {
53
- slug: learnJson.slug,
54
+ slug: slug,
54
55
  title: learnJson.title[selectedLang],
55
56
  lang: selectedLang,
56
57
  description: learnJson.description[selectedLang],
@@ -61,6 +62,8 @@ export const handleAssetCreation = async (
61
62
  owner: user.id,
62
63
  author: user.id,
63
64
  preview: learnJson.preview,
65
+ readme_raw: b64IndexReadme,
66
+ all_translations,
64
67
  })
65
68
  await api.updateRigoAssetID(
66
69
  sessionPayload.token,
@@ -68,26 +71,26 @@ export const handleAssetCreation = async (
68
71
  asset.id
69
72
  )
70
73
  Console.info("Asset created with id", asset.id)
71
- } else {
72
- Console.info("Asset exists, updating it")
73
- const asset = await api.updateAsset(
74
- sessionPayload.token,
75
- learnJson.slug,
76
- {
77
- learnpack_deploy_url: learnpackDeployUrl,
78
- title: learnJson.title[selectedLang],
79
- description: learnJson.description[selectedLang],
80
- }
81
- )
82
- await api.updateRigoAssetID(
83
- sessionPayload.rigobotToken,
84
- learnJson.slug,
85
- asset.id
86
- )
87
- Console.info("Asset updated with id", asset.id)
74
+ return asset
88
75
  }
76
+
77
+ Console.info("Asset exists, updating it")
78
+ const asset = await api.updateAsset(sessionPayload.token, slug, {
79
+ learnpack_deploy_url: learnpackDeployUrl,
80
+ title: learnJson.title[selectedLang],
81
+ description: learnJson.description[selectedLang],
82
+ all_translations,
83
+ })
84
+ await api.updateRigoAssetID(
85
+ sessionPayload.rigobotToken.trim(),
86
+ learnJson.slug,
87
+ asset.id
88
+ )
89
+ Console.info("Asset updated with id", asset.id)
90
+ return asset
89
91
  } catch (error) {
90
92
  Console.error("Error updating or creating asset:", error)
93
+ return null
91
94
  }
92
95
  }
93
96
 
@@ -435,7 +438,14 @@ class BuildCommand extends SessionCommand {
435
438
  fs.unlinkSync(zipFilePath)
436
439
  this.removeDirectory(buildDir)
437
440
 
438
- await handleAssetCreation(sessionPayload, learnJson, "us", res.data.url)
441
+ await handleAssetCreation(
442
+ sessionPayload,
443
+ learnJson,
444
+ "us",
445
+ res.data.url,
446
+ "",
447
+ []
448
+ )
439
449
  } catch (error) {
440
450
  if (axios.isAxiosError(error)) {
441
451
  if (error.response && error.response.status === 403) {