@learnpack/learnpack 5.0.50 → 5.0.52

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/README.md CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @learnpack/learnpack
21
21
  $ learnpack COMMAND
22
22
  running command...
23
23
  $ learnpack (-v|--version|version)
24
- @learnpack/learnpack/5.0.50 win32-x64 node-v22.14.0
24
+ @learnpack/learnpack/5.0.52 win32-x64 node-v22.14.0
25
25
  $ learnpack --help [COMMAND]
26
26
  USAGE
27
27
  $ learnpack COMMAND
@@ -79,7 +79,7 @@ DESCRIPTION
79
79
  12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
80
80
  ```
81
81
 
82
- _See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.50/src\commands\audit.ts)_
82
+ _See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.52/src\commands\audit.ts)_
83
83
 
84
84
  ## `learnpack breakToken`
85
85
 
@@ -94,7 +94,7 @@ OPTIONS
94
94
  -y, --yes Skip all prompts and initialize an empty project
95
95
  ```
96
96
 
97
- _See code: [src\commands\breakToken.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.50/src\commands\breakToken.ts)_
97
+ _See code: [src\commands\breakToken.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.52/src\commands\breakToken.ts)_
98
98
 
99
99
  ## `learnpack clean`
100
100
 
@@ -109,7 +109,7 @@ DESCRIPTION
109
109
  Extra documentation goes here
110
110
  ```
111
111
 
112
- _See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.50/src\commands\clean.ts)_
112
+ _See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.52/src\commands\clean.ts)_
113
113
 
114
114
  ## `learnpack download [PACKAGE]`
115
115
 
@@ -127,7 +127,7 @@ DESCRIPTION
127
127
  Extra documentation goes here
128
128
  ```
129
129
 
130
- _See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.50/src\commands\download.ts)_
130
+ _See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.52/src\commands\download.ts)_
131
131
 
132
132
  ## `learnpack help [COMMAND]`
133
133
 
@@ -159,7 +159,7 @@ OPTIONS
159
159
  -y, --yes Skip all prompts and initialize an empty project
160
160
  ```
161
161
 
162
- _See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.50/src\commands\init.ts)_
162
+ _See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.52/src\commands\init.ts)_
163
163
 
164
164
  ## `learnpack login [PACKAGE]`
165
165
 
@@ -177,7 +177,7 @@ DESCRIPTION
177
177
  Extra documentation goes here
178
178
  ```
179
179
 
180
- _See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.50/src\commands\login.ts)_
180
+ _See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.52/src\commands\login.ts)_
181
181
 
182
182
  ## `learnpack logout [PACKAGE]`
183
183
 
@@ -195,7 +195,7 @@ DESCRIPTION
195
195
  Extra documentation goes here
196
196
  ```
197
197
 
198
- _See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.50/src\commands\logout.ts)_
198
+ _See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.52/src\commands\logout.ts)_
199
199
 
200
200
  ## `learnpack plugins`
201
201
 
@@ -327,7 +327,7 @@ OPTIONS
327
327
  -s, --strict strict mode
328
328
  ```
329
329
 
330
- _See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.50/src\commands\publish.ts)_
330
+ _See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.52/src\commands\publish.ts)_
331
331
 
332
332
  ## `learnpack start`
333
333
 
@@ -349,7 +349,7 @@ OPTIONS
349
349
  -y, --yes Skip all prompts and initialize an empty project
350
350
  ```
351
351
 
352
- _See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.50/src\commands\start.ts)_
352
+ _See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.52/src\commands\start.ts)_
353
353
 
354
354
  ## `learnpack test [EXERCISESLUG]`
355
355
 
@@ -366,7 +366,7 @@ OPTIONS
366
366
  -y, --yes Skip all prompts and initialize an empty project
367
367
  ```
368
368
 
369
- _See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.50/src\commands\test.ts)_
369
+ _See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.52/src\commands\test.ts)_
370
370
 
371
371
  ## `learnpack translate`
372
372
 
@@ -380,7 +380,7 @@ OPTIONS
380
380
  -y, --yes Skip all prompts and initialize an empty project
381
381
  ```
382
382
 
383
- _See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.50/src\commands\translate.ts)_
383
+ _See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.52/src\commands\translate.ts)_
384
384
  <!-- commandsstop -->
385
385
 
386
386
  > > > > > > > 0cb3e56d84c197f9d008836bb573eade212b7e57
@@ -7,6 +7,7 @@ const audit_1 = require("../utils/audit");
7
7
  const SessionCommand_1 = require("../utils/SessionCommand");
8
8
  const path = require("path");
9
9
  const command_1 = require("@oclif/command");
10
+ const sidebarGenerator_1 = require("../utils/sidebarGenerator");
10
11
  // eslint-disable-next-line
11
12
  const fetch = require("node-fetch");
12
13
  class AuditCommand extends SessionCommand_1.default {
@@ -83,6 +84,22 @@ class AuditCommand extends SessionCommand_1.default {
83
84
  msg: "The slug property is not a valid slug. It must start and end with a letter and be less than 50 characters.",
84
85
  });
85
86
  }
87
+ console_1.default.info("Checking if the sidebar.json file is fine...");
88
+ const isSidebarFine = await (0, sidebarGenerator_1.checkAndFixSidebar)(config, false);
89
+ if (!isSidebarFine) {
90
+ if (strict) {
91
+ errors.push({
92
+ exercise: undefined,
93
+ msg: "The sidebar.json file has errors, please fill all missing translations",
94
+ });
95
+ }
96
+ else {
97
+ warnings.push({
98
+ exercise: undefined,
99
+ msg: "The sidebar.json file has errors, please fill all missing translations",
100
+ });
101
+ }
102
+ }
86
103
  // check if the duration property is in the configuration object
87
104
  if (!((_h = config.config) === null || _h === void 0 ? void 0 : _h.duration))
88
105
  warnings.push({
@@ -9,15 +9,16 @@ class CleanCommand extends SessionCommand_1.default {
9
9
  await this.initSession(flags);
10
10
  }
11
11
  async run() {
12
- var _a;
12
+ var _a, _b;
13
13
  const { flags } = this.parse(CleanCommand);
14
- (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.clean();
14
+ (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.buildIndex();
15
+ await ((_b = this.configManager) === null || _b === void 0 ? void 0 : _b.clean());
15
16
  console_1.default.success("Package cleaned successfully, ready to publish");
16
17
  }
17
18
  }
18
- CleanCommand.description = `Clean the configuration object
19
- ...
20
- Extra documentation goes here
19
+ CleanCommand.description = `Clean the configuration object
20
+ ...
21
+ Extra documentation goes here
21
22
  `;
22
23
  CleanCommand.flags = {
23
24
  // name: flags.string({char: 'n', description: 'name to print'}),
@@ -17,9 +17,9 @@ const api_2 = require("../utils/api");
17
17
  const creatorUtilities_1 = require("../utils/creatorUtilities");
18
18
  const session_1 = require("../managers/session");
19
19
  const durationByKind = {
20
- code: 3,
21
- quiz: 2,
22
- read: 1,
20
+ code: 5,
21
+ quiz: 3,
22
+ read: 2,
23
23
  };
24
24
  function estimateActivities(estimatedDuration) {
25
25
  const result = {};
@@ -78,6 +78,7 @@ async function processExercise(rigoToken, steps, packageContext, exercise, exerc
78
78
  fkgl_results: JSON.stringify(readability.fkglResult),
79
79
  expected_grade_level: PARAMS.expected_grade_level,
80
80
  });
81
+ // console.log("REDUCED README START", reducedReadme, "REDUCED README END")
81
82
  if (!reducedReadme)
82
83
  break;
83
84
  readability = (0, creatorUtilities_1.checkReadability)(reducedReadme.parsed.content, PARAMS.max_words, duration || 1);
@@ -192,9 +193,11 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
192
193
  const { targetAudience, estimatedDuration } = await whichTargetAudienceAndEstimatedDuration();
193
194
  const contentIndex = await (0, creatorUtilities_1.appendContentIndex)();
194
195
  const airules = await (0, creatorUtilities_1.appendAIRules)();
195
- // If the airules is not empty, create the file in target directory
196
196
  if (airules) {
197
- fs.writeFileSync(path.join(tutorialDir, ".learn", "rules", "airules.txt"), airules);
197
+ const rulesDir = path.join(tutorialDir, ".learn", "rules");
198
+ // Crear el directorio si no existe
199
+ fs.mkdirSync(rulesDir, { recursive: true });
200
+ fs.writeFileSync(path.join(rulesDir, "airules.txt"), airules);
198
201
  }
199
202
  let packageContext = `
200
203
  \n
@@ -218,6 +221,10 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
218
221
 
219
222
 
220
223
  Within the estimated duration, is possible to have the following activities:
224
+ Format=
225
+ Activity: Maximum number of steps for duration
226
+
227
+ Estimated activities:
221
228
  ${JSON.stringify(estimateActivities(estimatedDuration))}
222
229
 
223
230
  You should create a tutorial that is engaging and fun to follow.
@@ -276,6 +283,13 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
276
283
  console_1.default.info("Images generated successfully! 🎉 Your tutorial will be ready soon!");
277
284
  console_1.default.info("Creating preview readme...");
278
285
  await (0, rigoActions_1.createPreviewReadme)(tutorialDir, packageInfo, rigoToken, readmeContents);
286
+ const imagePath = path.join(tutorialDir, "preview.png");
287
+ const res = await (0, rigoActions_1.generateImage)(rigoToken, {
288
+ prompt: "Generate a preview image for the tutorial. This is all the tutorial information: " +
289
+ packageContext +
290
+ "\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.",
291
+ });
292
+ await (0, rigoActions_1.downloadImage)(res.image_url, imagePath);
279
293
  return true;
280
294
  };
281
295
  const getChoices = async (empty) => {
@@ -364,15 +378,17 @@ class InitComand extends BaseCommand_1.default {
364
378
  for (const language of languages) {
365
379
  const readmeFilename = `README${language !== "en" ? `.${language}` : ""}`;
366
380
  const readmeTemplatePath = path.resolve(templatesDir, `${readmeFilename}.ejs`);
367
- const readmeObject = {
368
- title: packageInfo.title.us,
369
- description: packageInfo.description.us,
370
- grading: packageInfo.grading,
371
- difficulty: packageInfo.difficulty,
372
- duration: packageInfo.duration,
373
- };
374
- const readmeContent = eta.render(fs.readFileSync(readmeTemplatePath, "utf-8"), readmeObject);
375
- fs.writeFileSync(path.join(tutorialDir, `${readmeFilename}.md`), readmeContent);
381
+ if (choices.useAI !== "yes") {
382
+ const readmeObject = {
383
+ title: packageInfo.title.us,
384
+ description: packageInfo.description.us,
385
+ grading: packageInfo.grading,
386
+ difficulty: packageInfo.difficulty,
387
+ duration: packageInfo.duration,
388
+ };
389
+ const readmeContent = eta.render(fs.readFileSync(readmeTemplatePath, "utf-8"), readmeObject);
390
+ fs.writeFileSync(path.join(tutorialDir, `${readmeFilename}.md`), readmeContent);
391
+ }
376
392
  if (fs.existsSync(path.join(tutorialDir, `${readmeFilename}.ejs`)))
377
393
  fs.removeSync(path.join(tutorialDir, `${readmeFilename}.ejs`));
378
394
  }
@@ -16,7 +16,38 @@ const file_1 = require("../managers/file");
16
16
  const api_1 = require("../utils/api");
17
17
  const prompts = require("prompts");
18
18
  const rigoActions_1 = require("../utils/rigoActions");
19
+ const misc_1 = require("../utils/misc");
19
20
  const uploadZipEndpont = api_1.RIGOBOT_HOST + "/v1/learnpack/upload";
21
+ const handleAssetCreation = async (sessionPayload, academy, learnJson, learnpackDeployUrl) => {
22
+ try {
23
+ const { exists, academyId } = await api_1.default.doesAssetExists(sessionPayload.token, learnJson.slug);
24
+ if (!exists) {
25
+ console_1.default.info("Asset does not exist in this academy, creating it");
26
+ const asset = await api_1.default.createAsset(sessionPayload.token, academy.id, {
27
+ slug: learnJson.slug,
28
+ title: learnJson.title.us,
29
+ lang: "us",
30
+ description: learnJson.description.us,
31
+ learnpack_deploy_url: learnpackDeployUrl,
32
+ technologies: ["node", "bash"],
33
+ url: "https://4geeksacademy.com",
34
+ });
35
+ console_1.default.info("Asset created with id", asset.id);
36
+ }
37
+ else {
38
+ console_1.default.info("Asset exists, updating it");
39
+ const asset = await api_1.default.updateAsset(sessionPayload.token, academyId, learnJson.slug, {
40
+ learnpack_deploy_url: learnpackDeployUrl,
41
+ title: learnJson.title.us,
42
+ description: learnJson.description.us,
43
+ });
44
+ console_1.default.info("Asset updated with id", asset.id);
45
+ }
46
+ }
47
+ catch (error) {
48
+ console_1.default.error("Error updating or creating asset:", error);
49
+ }
50
+ };
20
51
  const runAudit = (strict) => {
21
52
  try {
22
53
  console_1.default.info("Running learnpack audit before publishing...");
@@ -104,10 +135,9 @@ class BuildCommand extends SessionCommand_1.default {
104
135
  console_1.default.debug("Building exercises");
105
136
  (_c = this.configManager) === null || _c === void 0 ? void 0 : _c.buildIndex();
106
137
  }
107
- // const academies = await api.listUserAcademies(sessionPayload.token)
138
+ const academies = await api_1.default.listUserAcademies(sessionPayload.token);
108
139
  // // console.log(academies, "academies")
109
- // const academy = await selectAcademy(academies)
110
- // console.log(academy, "academy")
140
+ const academy = await selectAcademy(academies);
111
141
  // Read learn.json to get the slug
112
142
  const learnJsonPath = path.join(process.cwd(), "learn.json");
113
143
  if (!fs.existsSync(learnJsonPath)) {
@@ -170,7 +200,8 @@ class BuildCommand extends SessionCommand_1.default {
170
200
  .replace(/<title>.*<\/title>/, `<title>${title}</title>`)
171
201
  .replace(/{{title}}/g, title)
172
202
  .replace(/{{preview}}/g, previewUrl)
173
- .replace(/{{slug}}/g, learnJson.slug);
203
+ .replace(/{{slug}}/g, learnJson.slug)
204
+ .replace(/{{duration}}/g, (0, misc_1.minutesToISO8601Duration)(learnJson.duration));
174
205
  // Write the modified content to the build directory
175
206
  fs.writeFileSync(buildIndexHtmlPath, indexHtmlContent);
176
207
  }
@@ -225,9 +256,9 @@ class BuildCommand extends SessionCommand_1.default {
225
256
  headers: Object.assign(Object.assign({}, formData.getHeaders()), { Authorization: `Token ${rigoToken}` }),
226
257
  });
227
258
  console.log(res.data);
228
- // Remove the zip file after uploading
229
259
  fs.unlinkSync(zipFilePath);
230
260
  this.removeDirectory(buildDir);
261
+ await handleAssetCreation(sessionPayload, academy, learnJson, res.data.url);
231
262
  }
232
263
  catch (error) {
233
264
  if (axios_1.default.isAxiosError(error)) {
@@ -244,8 +275,8 @@ class BuildCommand extends SessionCommand_1.default {
244
275
  else {
245
276
  console.error("Error uploading file:", error);
246
277
  }
247
- fs.unlinkSync(zipFilePath);
248
- this.removeDirectory(buildDir);
278
+ // fs.unlinkSync(zipFilePath)
279
+ // this.removeDirectory(buildDir)
249
280
  }
250
281
  });
251
282
  archive.on("error", (err) => {
@@ -8,6 +8,7 @@ const util_1 = require("util");
8
8
  const console_1 = require("../../utils/console");
9
9
  const watcher_1 = require("../../utils/watcher");
10
10
  const errors_1 = require("../../utils/errors");
11
+ const sidebarGenerator_1 = require("../../utils/sidebarGenerator");
11
12
  const defaults_1 = require("./defaults");
12
13
  const exercise_1 = require("./exercise");
13
14
  const file_1 = require("../file");
@@ -59,6 +60,18 @@ const getCodespacesNamespace = () => {
59
60
  }
60
61
  return codespace_name;
61
62
  };
63
+ const checkDuration = (configObj) => {
64
+ if (!configObj.exercises || !configObj.config || !configObj.config.dirPath)
65
+ return;
66
+ const learnJSONPath = path.join(configObj.config.dirPath, "../learn.json");
67
+ const learnJSON = fs.readFileSync(learnJSONPath, "utf8");
68
+ const learnJSONObj = JSON.parse(learnJSON);
69
+ const duration = learnJSONObj.duration;
70
+ if (!duration) {
71
+ learnJSONObj.duration = configObj.exercises.reduce((acc) => acc + 2, 0);
72
+ fs.writeFileSync(learnJSONPath, JSON.stringify(learnJSONObj, null, 2));
73
+ }
74
+ };
62
75
  exports.default = async ({ grading, mode, disableGrading, version, }) => {
63
76
  var _a, _b, _c;
64
77
  const confPath = getConfigPath();
@@ -264,7 +277,7 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
264
277
  (0, file_1.rmSync)(configObj.config.dirPath + "/.session");
265
278
  }
266
279
  },
267
- clean: () => {
280
+ clean: async () => {
268
281
  if (configObj.config) {
269
282
  if (configObj.config.outputPath) {
270
283
  (0, file_1.rmSync)(configObj.config.outputPath);
@@ -292,6 +305,8 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
292
305
  fs.unlinkSync(configObj.config.dirPath + "/telemetry.json");
293
306
  if (fs.existsSync(configObj.config.dirPath + "/vscode_queue.json"))
294
307
  fs.unlinkSync(configObj.config.dirPath + "/vscode_queue.json");
308
+ await (0, sidebarGenerator_1.checkAndFixSidebar)(configObj, true);
309
+ checkDuration(configObj);
295
310
  }
296
311
  },
297
312
  getExercise: slug => {
@@ -10,6 +10,20 @@ export interface TAcademy {
10
10
  }
11
11
  export declare const listUserAcademies: (breathecodeToken: string) => Promise<TAcademy[]>;
12
12
  export declare const validateToken: (token: string) => Promise<any>;
13
+ type TAssetMissing = {
14
+ slug: string;
15
+ title: string;
16
+ lang: string;
17
+ url: string;
18
+ description: string;
19
+ learnpack_deploy_url: string;
20
+ technologies: string[];
21
+ };
22
+ export declare const createAsset: (token: string, academyId: number, asset: TAssetMissing) => Promise<any>;
23
+ export declare const doesAssetExists: (token: string, assetSlug: string) => Promise<{
24
+ exists: boolean;
25
+ academyId?: number;
26
+ }>;
13
27
  declare const _default: {
14
28
  login: (identification: string, password: string) => Promise<any>;
15
29
  publish: (config: any) => Promise<any>;
@@ -24,5 +38,11 @@ declare const _default: {
24
38
  sendStreamTelemetry: (url: string, body: object) => Promise<void>;
25
39
  listUserAcademies: (breathecodeToken: string) => Promise<TAcademy[]>;
26
40
  validateToken: (token: string) => Promise<any>;
41
+ createAsset: (token: string, academyId: number, asset: TAssetMissing) => Promise<any>;
42
+ doesAssetExists: (token: string, assetSlug: string) => Promise<{
43
+ exists: boolean;
44
+ academyId?: number;
45
+ }>;
46
+ updateAsset: (token: string, academyId: number, assetSlug: string, asset: Partial<TAssetMissing>) => Promise<any>;
27
47
  };
28
48
  export default _default;
package/lib/utils/api.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateToken = exports.listUserAcademies = exports.getConsumable = exports.countConsumables = exports.RIGOBOT_HOST = void 0;
3
+ exports.doesAssetExists = exports.createAsset = exports.validateToken = exports.listUserAcademies = exports.getConsumable = exports.countConsumables = exports.RIGOBOT_HOST = void 0;
4
4
  const console_1 = require("../utils/console");
5
5
  const storage = require("node-persist");
6
6
  const cli_ux_1 = require("cli-ux");
@@ -272,6 +272,14 @@ const getConsumable = async (token, consumableSlug = "ai-generation") => {
272
272
  }
273
273
  };
274
274
  exports.getConsumable = getConsumable;
275
+ const with_crud_asset_roles = new Set([
276
+ "content_writer",
277
+ "growth_manager",
278
+ "syllabus_coordinator",
279
+ "country_manager",
280
+ "community_manager",
281
+ "carrer_support_head",
282
+ ]);
275
283
  const listUserAcademies = async (breathecodeToken) => {
276
284
  const url = "https://breathecode.herokuapp.com/v1/auth/user/me";
277
285
  try {
@@ -283,9 +291,12 @@ const listUserAcademies = async (breathecodeToken) => {
283
291
  const data = response.data;
284
292
  const academiesMap = new Map();
285
293
  for (const role of data.roles) {
286
- const academy = role.academy;
287
- if (!academiesMap.has(academy.id)) {
288
- academiesMap.set(academy.id, academy);
294
+ // Only add academies where the user's role is in the whitelist
295
+ if (with_crud_asset_roles.has(role.role)) {
296
+ const academy = role.academy;
297
+ if (!academiesMap.has(academy.id)) {
298
+ academiesMap.set(academy.id, academy);
299
+ }
289
300
  }
290
301
  }
291
302
  return [...academiesMap.values()];
@@ -310,6 +321,83 @@ const validateToken = async (token) => {
310
321
  }
311
322
  };
312
323
  exports.validateToken = validateToken;
324
+ const createAsset = async (token, academyId, asset) => {
325
+ const body = {
326
+ slug: asset.slug,
327
+ title: asset.title,
328
+ lang: asset.lang,
329
+ asset_type: "EXERCISE",
330
+ visibility: "PUBLIC",
331
+ status: "PUBLISHED",
332
+ url: "https://4geeksacademy.com",
333
+ readme_url: null,
334
+ difficulty: null,
335
+ duration: null,
336
+ graded: false,
337
+ gitpod: true,
338
+ category: 7,
339
+ preview: null,
340
+ description: asset.description,
341
+ external: true,
342
+ interactive: true,
343
+ solution_video_url: null,
344
+ intro_video_url: null,
345
+ translations: ["us"],
346
+ learnpack_deploy_url: asset.learnpack_deploy_url,
347
+ technologies: asset.technologies,
348
+ };
349
+ const url = `https://breathecode.herokuapp.com/v1/registry/academy/asset`;
350
+ const headers = {
351
+ Authorization: `Token ${token}`,
352
+ Academy: academyId,
353
+ };
354
+ try {
355
+ const response = await axios_1.default.post(url, body, { headers });
356
+ return response.data;
357
+ }
358
+ catch (error) {
359
+ // console.error("Failed to create asset:", error)
360
+ throw error.response.data;
361
+ }
362
+ };
363
+ exports.createAsset = createAsset;
364
+ const doesAssetExists = async (token, assetSlug) => {
365
+ const url = `https://breathecode.herokuapp.com/v1/registry/asset/${assetSlug}`;
366
+ const headers = {
367
+ Authorization: `Token ${token}`,
368
+ };
369
+ try {
370
+ const response = await axios_1.default.get(url, { headers });
371
+ if (response.status === 200) {
372
+ const data = response.data;
373
+ const academy = data.academy.id;
374
+ return { exists: true, academyId: academy };
375
+ }
376
+ return { exists: false };
377
+ }
378
+ catch (error) {
379
+ console.error("Failed to get asset:", error);
380
+ return { exists: false };
381
+ }
382
+ };
383
+ exports.doesAssetExists = doesAssetExists;
384
+ const updateAsset = async (token, academyId, assetSlug, asset) => {
385
+ const url = `https://breathecode.herokuapp.com/v1/registry/academy/asset/${assetSlug}`;
386
+ const headers = {
387
+ Authorization: `Token ${token}`,
388
+ Academy: academyId,
389
+ };
390
+ try {
391
+ const response = await axios_1.default.put(url, asset, { headers });
392
+ return response.data;
393
+ }
394
+ catch (error) {
395
+ // console.error("Failed to update asset:", error)
396
+ // Try to print the data
397
+ // console.log(error.response.data)
398
+ throw error.response.data;
399
+ }
400
+ };
313
401
  exports.default = {
314
402
  login,
315
403
  publish,
@@ -321,4 +409,7 @@ exports.default = {
321
409
  sendStreamTelemetry,
322
410
  listUserAcademies: exports.listUserAcademies,
323
411
  validateToken: exports.validateToken,
412
+ createAsset: exports.createAsset,
413
+ doesAssetExists: exports.doesAssetExists,
414
+ updateAsset,
324
415
  };
@@ -199,7 +199,7 @@ function getDesktopFile(fileName) {
199
199
  const filePath = (0, path_1.join)(desktopPath, fileName);
200
200
  const content = fs.readFileSync(filePath, "utf8");
201
201
  // Delete the file after reading it
202
- fs.unlinkSync(filePath);
202
+ // fs.unlinkSync(filePath)
203
203
  return content;
204
204
  }
205
205
  // export function fleschKincaidReadingEase(text: string): number {
@@ -1 +1,2 @@
1
1
  export declare const prioritizeHTMLFile: (entryFiles: string[]) => string[];
2
+ export declare function minutesToISO8601Duration(minutes: number): string;
package/lib/utils/misc.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.prioritizeHTMLFile = void 0;
4
+ exports.minutesToISO8601Duration = minutesToISO8601Duration;
4
5
  const prioritizeHTMLFile = (entryFiles) => {
5
6
  let files = [];
6
7
  // Find the html file and put it as latest in the files array
@@ -22,3 +23,25 @@ const prioritizeHTMLFile = (entryFiles) => {
22
23
  return files;
23
24
  };
24
25
  exports.prioritizeHTMLFile = prioritizeHTMLFile;
26
+ function minutesToISO8601Duration(minutes) {
27
+ if (minutes <= 0)
28
+ return "PT0M";
29
+ const weeks = Math.floor(minutes / (60 * 24 * 7));
30
+ minutes %= 60 * 24 * 7;
31
+ const days = Math.floor(minutes / (60 * 24));
32
+ minutes %= 60 * 24;
33
+ const hours = Math.floor(minutes / 60);
34
+ const mins = minutes % 60;
35
+ let duration = "P";
36
+ if (weeks)
37
+ duration += `${weeks}W`;
38
+ if (days)
39
+ duration += `${days}D`;
40
+ if (hours || mins)
41
+ duration += "T";
42
+ if (hours)
43
+ duration += `${hours}H`;
44
+ if (mins)
45
+ duration += `${mins}M`;
46
+ return duration;
47
+ }
@@ -66,4 +66,9 @@ type TGenerateCourseShortNameInputs = {
66
66
  learnJSON: string;
67
67
  };
68
68
  export declare const generateCourseShortName: (token: string, inputs: TGenerateCourseShortNameInputs) => Promise<any>;
69
+ type TFillSidebarJSONInputs = {
70
+ needed_translations: string;
71
+ sidebar_json: string;
72
+ };
73
+ export declare const fillSidebarJSON: (token: string, inputs: TFillSidebarJSONInputs) => Promise<any>;
69
74
  export {};
@@ -1,11 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- 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.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;
4
4
  exports.downloadImage = downloadImage;
5
5
  exports.createPreviewReadme = createPreviewReadme;
6
6
  exports.makeReadmeReadable = makeReadmeReadable;
7
7
  const axios_1 = require("axios");
8
- const fs_1 = require("fs");
8
+ const promises_1 = require("fs/promises");
9
9
  const console_1 = require("../utils/console");
10
10
  const fs = require("fs");
11
11
  const path = require("path");
@@ -72,11 +72,7 @@ exports.generateImage = generateImage;
72
72
  async function downloadImage(imageUrl, savePath) {
73
73
  const response = await axios_1.default.get(imageUrl, { responseType: "arraybuffer" });
74
74
  const buffer = Buffer.from(response.data, "binary");
75
- (0, fs_1.writeFile)(savePath, buffer, err => {
76
- if (err) {
77
- console_1.default.error(`Error saving the image: ${err}`);
78
- }
79
- });
75
+ await (0, promises_1.writeFile)(savePath, buffer);
80
76
  }
81
77
  const translateExercise = async (token, inputs) => {
82
78
  const response = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/prompting/completion/159/`, {
@@ -212,3 +208,13 @@ const generateCourseShortName = async (token, inputs) => {
212
208
  return response.data;
213
209
  };
214
210
  exports.generateCourseShortName = generateCourseShortName;
211
+ const fillSidebarJSON = async (token, inputs) => {
212
+ const response = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/prompting/completion/951/`, { inputs, include_purpose_objective: false, execute_async: false }, {
213
+ headers: {
214
+ "Content-Type": "application/json",
215
+ Authorization: "Token " + token,
216
+ },
217
+ });
218
+ return response.data;
219
+ };
220
+ exports.fillSidebarJSON = fillSidebarJSON;
@@ -1,4 +1,5 @@
1
1
  import { IExercise } from "../models/exercise-obj";
2
+ import { IConfigObj } from "../models/config";
2
3
  type TTitleTranslations = {
3
4
  [key: string]: string;
4
5
  };
@@ -7,4 +8,5 @@ export type TSidebar = {
7
8
  };
8
9
  export declare const generateSidebar: (exercises: IExercise[], learnPath: string) => TSidebar;
9
10
  export declare const addExerciseToSidebar: (exerciseSlug: string, targetLanguage: string, translatedSlug: string, learnPath: string) => TSidebar;
11
+ export declare const checkAndFixSidebar: (configObj: IConfigObj, autoFix?: boolean) => Promise<boolean>;
10
12
  export {};