@learnpack/learnpack 5.0.28 → 5.0.30
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 +12 -12
- package/lib/commands/breakToken.js +11 -3
- package/lib/commands/init.js +69 -38
- package/lib/commands/logout.js +6 -11
- package/lib/commands/publish.js +1 -1
- package/lib/commands/start.js +6 -12
- package/lib/managers/config/index.js +25 -0
- package/lib/managers/file.js +1 -1
- package/lib/managers/server/routes.js +31 -5
- package/lib/managers/session.js +3 -0
- package/lib/models/config-manager.d.ts +2 -0
- package/lib/utils/creatorUtilities.d.ts +7 -2
- package/lib/utils/creatorUtilities.js +88 -6
- package/lib/utils/rigoActions.d.ts +6 -0
- package/lib/utils/rigoActions.js +16 -0
- package/oclif.manifest.json +1 -1
- package/package.json +2 -2
- package/src/commands/breakToken.ts +13 -2
- package/src/commands/init.ts +103 -46
- package/src/commands/logout.ts +38 -43
- package/src/commands/publish.ts +1 -1
- package/src/commands/start.ts +7 -14
- package/src/managers/config/index.ts +26 -0
- package/src/managers/file.ts +1 -1
- package/src/managers/server/routes.ts +43 -3
- package/src/managers/session.ts +4 -0
- package/src/models/config-manager.ts +25 -23
- package/src/utils/creatorUtilities.ts +241 -147
- package/src/utils/rigoActions.ts +30 -0
- package/src/utils/templates/incremental/.github/workflows/learnpack-audit.yml +29 -0
- package/src/utils/templates/isolated/.github/workflows/learnpack-audit.yml +29 -0
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.
|
24
|
+
@learnpack/learnpack/5.0.30 win32-x64 node-v20.16.0
|
25
25
|
$ learnpack --help [COMMAND]
|
26
26
|
USAGE
|
27
27
|
$ learnpack COMMAND
|
@@ -76,7 +76,7 @@ DESCRIPTION
|
|
76
76
|
12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
|
77
77
|
```
|
78
78
|
|
79
|
-
_See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
79
|
+
_See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.30/src\commands\audit.ts)_
|
80
80
|
|
81
81
|
## `learnpack breakToken`
|
82
82
|
|
@@ -91,7 +91,7 @@ OPTIONS
|
|
91
91
|
-y, --yes Skip all prompts and initialize an empty project
|
92
92
|
```
|
93
93
|
|
94
|
-
_See code: [src\commands\breakToken.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
94
|
+
_See code: [src\commands\breakToken.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.30/src\commands\breakToken.ts)_
|
95
95
|
|
96
96
|
## `learnpack clean`
|
97
97
|
|
@@ -106,7 +106,7 @@ DESCRIPTION
|
|
106
106
|
Extra documentation goes here
|
107
107
|
```
|
108
108
|
|
109
|
-
_See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
109
|
+
_See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.30/src\commands\clean.ts)_
|
110
110
|
|
111
111
|
## `learnpack download [PACKAGE]`
|
112
112
|
|
@@ -124,7 +124,7 @@ DESCRIPTION
|
|
124
124
|
Extra documentation goes here
|
125
125
|
```
|
126
126
|
|
127
|
-
_See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
127
|
+
_See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.30/src\commands\download.ts)_
|
128
128
|
|
129
129
|
## `learnpack help [COMMAND]`
|
130
130
|
|
@@ -156,7 +156,7 @@ OPTIONS
|
|
156
156
|
-y, --yes Skip all prompts and initialize an empty project
|
157
157
|
```
|
158
158
|
|
159
|
-
_See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
159
|
+
_See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.30/src\commands\init.ts)_
|
160
160
|
|
161
161
|
## `learnpack login [PACKAGE]`
|
162
162
|
|
@@ -174,7 +174,7 @@ DESCRIPTION
|
|
174
174
|
Extra documentation goes here
|
175
175
|
```
|
176
176
|
|
177
|
-
_See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
177
|
+
_See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.30/src\commands\login.ts)_
|
178
178
|
|
179
179
|
## `learnpack logout [PACKAGE]`
|
180
180
|
|
@@ -192,7 +192,7 @@ DESCRIPTION
|
|
192
192
|
Extra documentation goes here
|
193
193
|
```
|
194
194
|
|
195
|
-
_See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
195
|
+
_See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.30/src\commands\logout.ts)_
|
196
196
|
|
197
197
|
## `learnpack plugins`
|
198
198
|
|
@@ -323,7 +323,7 @@ OPTIONS
|
|
323
323
|
-h, --help show CLI help
|
324
324
|
```
|
325
325
|
|
326
|
-
_See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
326
|
+
_See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.30/src\commands\publish.ts)_
|
327
327
|
|
328
328
|
## `learnpack start`
|
329
329
|
|
@@ -345,7 +345,7 @@ OPTIONS
|
|
345
345
|
-y, --yes Skip all prompts and initialize an empty project
|
346
346
|
```
|
347
347
|
|
348
|
-
_See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
348
|
+
_See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.30/src\commands\start.ts)_
|
349
349
|
|
350
350
|
## `learnpack test [EXERCISESLUG]`
|
351
351
|
|
@@ -362,7 +362,7 @@ OPTIONS
|
|
362
362
|
-y, --yes Skip all prompts and initialize an empty project
|
363
363
|
```
|
364
364
|
|
365
|
-
_See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
365
|
+
_See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.30/src\commands\test.ts)_
|
366
366
|
|
367
367
|
## `learnpack translate`
|
368
368
|
|
@@ -376,7 +376,7 @@ OPTIONS
|
|
376
376
|
-y, --yes Skip all prompts and initialize an empty project
|
377
377
|
```
|
378
378
|
|
379
|
-
_See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
379
|
+
_See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.30/src\commands\translate.ts)_
|
380
380
|
<!-- commandsstop -->
|
381
381
|
|
382
382
|
> > > > > > > 0cb3e56d84c197f9d008836bb573eade212b7e57
|
@@ -2,12 +2,20 @@
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
const command_1 = require("@oclif/command");
|
4
4
|
const BaseCommand_1 = require("../utils/BaseCommand");
|
5
|
-
const
|
5
|
+
const console_1 = require("../utils/console");
|
6
|
+
const creatorUtilities_1 = require("../utils/creatorUtilities");
|
6
7
|
class BreakTokenCommand extends BaseCommand_1.default {
|
7
8
|
async run() {
|
8
9
|
const { flags } = this.parse(BreakTokenCommand);
|
9
|
-
await
|
10
|
-
|
10
|
+
// await SessionManager.breakToken()
|
11
|
+
await (0, creatorUtilities_1.createFileOnDesktop)();
|
12
|
+
console_1.default.info("File created on desktop, please make the necessary changes and continue when ready");
|
13
|
+
// Wait for user to press enter
|
14
|
+
process.stdin.once("data", () => {
|
15
|
+
console_1.default.info("File content:");
|
16
|
+
console_1.default.info((0, creatorUtilities_1.getContentIndex)());
|
17
|
+
process.exit(0);
|
18
|
+
});
|
11
19
|
}
|
12
20
|
}
|
13
21
|
BreakTokenCommand.description = "Break the token";
|
package/lib/commands/init.js
CHANGED
@@ -15,12 +15,18 @@ const rigoActions_1 = require("../utils/rigoActions");
|
|
15
15
|
const api_2 = require("../utils/api");
|
16
16
|
const creatorUtilities_1 = require("../utils/creatorUtilities");
|
17
17
|
const session_1 = require("../managers/session");
|
18
|
+
const durationByKind = {
|
19
|
+
code: 3,
|
20
|
+
quiz: 2,
|
21
|
+
read: 1,
|
22
|
+
};
|
18
23
|
const initializeInteractiveCreation = async (rigoToken, courseInfo) => {
|
19
24
|
let prevInteractions = "";
|
20
25
|
let isReady = false;
|
21
26
|
let currentSteps = [];
|
22
27
|
let currentTitle = "";
|
23
28
|
let currentDescription = "";
|
29
|
+
let currentDifficulty = "";
|
24
30
|
while (!isReady) {
|
25
31
|
let wholeInfo = courseInfo;
|
26
32
|
wholeInfo += `
|
@@ -41,6 +47,9 @@ const initializeInteractiveCreation = async (rigoToken, courseInfo) => {
|
|
41
47
|
currentDescription !== res.parsed.description) {
|
42
48
|
currentDescription = res.parsed.description;
|
43
49
|
}
|
50
|
+
if (res.parsed.difficulty && currentDifficulty !== res.parsed.difficulty) {
|
51
|
+
currentDifficulty = res.parsed.difficulty;
|
52
|
+
}
|
44
53
|
if (!isReady) {
|
45
54
|
console.log(currentSteps);
|
46
55
|
console_1.default.info(`AI: ${res.parsed.aiMessage}`);
|
@@ -56,13 +65,43 @@ const initializeInteractiveCreation = async (rigoToken, courseInfo) => {
|
|
56
65
|
prevInteractions += `\nUser: ${userMessage.userMessage}`;
|
57
66
|
}
|
58
67
|
}
|
68
|
+
const duration = (0, creatorUtilities_1.estimateDuration)(currentSteps);
|
59
69
|
return {
|
60
70
|
steps: currentSteps,
|
61
71
|
title: currentTitle,
|
62
72
|
description: currentDescription,
|
63
73
|
interactions: prevInteractions,
|
74
|
+
difficulty: currentDifficulty,
|
75
|
+
duration,
|
64
76
|
};
|
65
77
|
};
|
78
|
+
const appendContentIndex = async () => {
|
79
|
+
const choices = await prompts([
|
80
|
+
{
|
81
|
+
type: "confirm",
|
82
|
+
name: "contentIndex",
|
83
|
+
message: "Do you have a content index for this tutorial?",
|
84
|
+
},
|
85
|
+
]);
|
86
|
+
if (choices.contentIndex) {
|
87
|
+
await (0, creatorUtilities_1.createFileOnDesktop)();
|
88
|
+
console_1.default.info("Please make the necessary in the recently created file in your desktop, it should automatically open. Edit the file to match your expectations and save it. Keep the same name and structure as the example file. Continue when ready.");
|
89
|
+
const isReady = await prompts([
|
90
|
+
{
|
91
|
+
type: "confirm",
|
92
|
+
name: "isReady",
|
93
|
+
message: "Are you ready to continue?",
|
94
|
+
},
|
95
|
+
]);
|
96
|
+
if (!isReady.isReady) {
|
97
|
+
console_1.default.error("Please make the necessary changes and try again.");
|
98
|
+
process.exit(1);
|
99
|
+
}
|
100
|
+
const contentIndex = (0, creatorUtilities_1.getContentIndex)();
|
101
|
+
return contentIndex;
|
102
|
+
}
|
103
|
+
return null;
|
104
|
+
};
|
66
105
|
const handleAILogic = async (tutorialDir, packageInfo) => {
|
67
106
|
fs.removeSync(path.join(tutorialDir, "exercises", "01-hello-world"));
|
68
107
|
let sessionPayload = await session_1.default.getPayload();
|
@@ -90,21 +129,26 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
|
|
90
129
|
process.exit(1);
|
91
130
|
}
|
92
131
|
console_1.default.success("🎉 Let's begin this learning journey!");
|
132
|
+
const contentIndex = await appendContentIndex();
|
93
133
|
let packageContext = `
|
94
134
|
\n
|
95
135
|
Title: ${packageInfo.title.us}
|
96
136
|
Description: ${packageInfo.description.us}
|
97
|
-
|
98
|
-
|
137
|
+
|
138
|
+
${contentIndex ?
|
139
|
+
`Content Index submitted by the user, use this to guide your creation: ${contentIndex}` :
|
140
|
+
""}
|
141
|
+
|
99
142
|
`;
|
100
|
-
const { steps, title, description } = await initializeInteractiveCreation(rigoToken, packageContext);
|
143
|
+
const { steps, title, description, duration, difficulty } = await initializeInteractiveCreation(rigoToken, packageContext);
|
101
144
|
packageInfo.title.us = title;
|
102
145
|
packageInfo.description.us = description;
|
146
|
+
packageInfo.duration = duration;
|
147
|
+
packageInfo.difficulty = difficulty;
|
103
148
|
packageContext = `
|
104
149
|
Title: ${title}
|
105
150
|
Description: ${description}
|
106
|
-
|
107
|
-
Estimated duration: ${packageInfo.duration}
|
151
|
+
|
108
152
|
List of exercises: ${steps.join(", ")}
|
109
153
|
`;
|
110
154
|
const exercisesDir = path.join(tutorialDir, "exercises");
|
@@ -126,13 +170,23 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
|
|
126
170
|
lesson_description: description,
|
127
171
|
kind: kind.toLowerCase(),
|
128
172
|
});
|
129
|
-
const
|
130
|
-
|
131
|
-
|
132
|
-
|
173
|
+
const duration = durationByKind[kind.toLowerCase()];
|
174
|
+
let readingTime = (0, creatorUtilities_1.checkReadingTime)(readme.parsed.content, 200, duration || 1);
|
175
|
+
if (readingTime.exceedsThreshold) {
|
176
|
+
// Console.info(
|
177
|
+
// `The reading time for the lesson ${exTitle} exceeds the threshold, reducing it...`
|
178
|
+
// )
|
179
|
+
const reducedReadme = await (0, rigoActions_1.reduceReadme)(rigoToken, {
|
180
|
+
lesson: readingTime.body,
|
181
|
+
number_of_words: readingTime.minutes.toString(),
|
182
|
+
expected_number_words: "200",
|
183
|
+
});
|
184
|
+
if (reducedReadme) {
|
185
|
+
readingTime = (0, creatorUtilities_1.checkReadingTime)(reducedReadme.parsed.content, 200, duration || 1);
|
186
|
+
}
|
133
187
|
}
|
134
188
|
const readmeFilename = "README.md";
|
135
|
-
fs.writeFileSync(path.join(exerciseDir, readmeFilename), newMarkdown);
|
189
|
+
fs.writeFileSync(path.join(exerciseDir, readmeFilename), readingTime.newMarkdown);
|
136
190
|
if (kind.toLowerCase() === "code") {
|
137
191
|
const codeFile = await (0, rigoActions_1.createCodeFile)(rigoToken, {
|
138
192
|
readme: readme.parsed.content,
|
@@ -140,12 +194,12 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
|
|
140
194
|
});
|
141
195
|
fs.writeFileSync(path.join(exerciseDir, `app.${codeFile.parsed.extension.replace(".", "")}`), codeFile.parsed.content);
|
142
196
|
}
|
143
|
-
return
|
197
|
+
return readingTime.newMarkdown;
|
144
198
|
});
|
145
|
-
let imagesArray = [];
|
146
199
|
const readmeContents = await Promise.all(exercisePromises);
|
147
200
|
console_1.default.success("Lessons created! 🎉");
|
148
201
|
console_1.default.info("Generating images for the lessons...");
|
202
|
+
let imagesArray = [];
|
149
203
|
for (const content of readmeContents) {
|
150
204
|
imagesArray = [...imagesArray, ...(0, creatorUtilities_1.extractImagesFromMarkdown)(content)];
|
151
205
|
}
|
@@ -173,7 +227,7 @@ const getChoices = async (empty) => {
|
|
173
227
|
title: "My Interactive Tutorial",
|
174
228
|
description: "",
|
175
229
|
difficulty: "beginner",
|
176
|
-
duration:
|
230
|
+
duration: 5,
|
177
231
|
useAI: "no",
|
178
232
|
grading: "isolated",
|
179
233
|
};
|
@@ -209,30 +263,6 @@ const getChoices = async (empty) => {
|
|
209
263
|
initial: "",
|
210
264
|
message: "Description for your tutorial? Press enter to leave blank",
|
211
265
|
},
|
212
|
-
{
|
213
|
-
type: "select",
|
214
|
-
name: "difficulty",
|
215
|
-
message: "How difficulty will be to complete the tutorial?",
|
216
|
-
choices: [
|
217
|
-
{ title: "Begginer (no previous experience)", value: "beginner" },
|
218
|
-
{ title: "Easy (just a bit of experience required)", value: "easy" },
|
219
|
-
{
|
220
|
-
title: "Intermediate (you need experience)",
|
221
|
-
value: "intermediate",
|
222
|
-
},
|
223
|
-
{ title: "Hard (master the topic)", value: "hard" },
|
224
|
-
],
|
225
|
-
},
|
226
|
-
{
|
227
|
-
type: "text",
|
228
|
-
name: "duration",
|
229
|
-
initial: "1",
|
230
|
-
message: "How many hours avg it takes to complete (number)?",
|
231
|
-
validate: (value) => {
|
232
|
-
const n = Math.floor(Number(value));
|
233
|
-
return n !== Number.POSITIVE_INFINITY && String(n) === value && n >= 0;
|
234
|
-
},
|
235
|
-
},
|
236
266
|
{
|
237
267
|
type: "select",
|
238
268
|
name: "useAI",
|
@@ -246,7 +276,8 @@ const getChoices = async (empty) => {
|
|
246
276
|
],
|
247
277
|
},
|
248
278
|
]);
|
249
|
-
|
279
|
+
const completeChoices = Object.assign(Object.assign({}, choices), { difficulty: "beginner", duration: 30 });
|
280
|
+
return completeChoices;
|
250
281
|
};
|
251
282
|
class InitComand extends BaseCommand_1.default {
|
252
283
|
async run() {
|
package/lib/commands/logout.js
CHANGED
@@ -1,27 +1,22 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
// import {Command, flags} from '@oclif/command'
|
4
|
-
// import { prompt } from "enquirer"
|
5
|
-
// import fetch from 'node-fetch'
|
6
3
|
const SessionCommand_1 = require("../utils/SessionCommand");
|
7
4
|
const session_1 = require("../managers/session");
|
8
|
-
|
9
|
-
// import { replace } from 'node-emoji'
|
10
|
-
// import { validURL } from "../utils/validators"
|
11
|
-
// const BaseCommand from '../utils/BaseCommand');
|
5
|
+
const console_1 = require("../utils/console");
|
12
6
|
class LogoutCommand extends SessionCommand_1.default {
|
13
7
|
async init() {
|
14
8
|
const { flags } = this.parse(LogoutCommand);
|
15
|
-
await this.initSession(flags)
|
9
|
+
// await this.initSession(flags)
|
10
|
+
console_1.default.debug("Logout command");
|
16
11
|
}
|
17
12
|
async run() {
|
18
13
|
// const {flags, args} = this.parse(LogoutCommand)
|
19
14
|
session_1.default.destroy();
|
20
15
|
}
|
21
16
|
}
|
22
|
-
LogoutCommand.description = `Describe the command here
|
23
|
-
...
|
24
|
-
Extra documentation goes here
|
17
|
+
LogoutCommand.description = `Describe the command here
|
18
|
+
...
|
19
|
+
Extra documentation goes here
|
25
20
|
`;
|
26
21
|
LogoutCommand.flags = {
|
27
22
|
// name: flags.string({char: 'n', description: 'name to print'}),
|
package/lib/commands/publish.js
CHANGED
@@ -92,7 +92,7 @@ class BuildCommand extends SessionCommand_1.default {
|
|
92
92
|
(_c = this.configManager) === null || _c === void 0 ? void 0 : _c.buildIndex();
|
93
93
|
}
|
94
94
|
// const academies = await api.listUserAcademies(sessionPayload.token)
|
95
|
-
// console.log(academies, "academies")
|
95
|
+
// // console.log(academies, "academies")
|
96
96
|
// const academy = await selectAcademy(academies)
|
97
97
|
// console.log(academy, "academy")
|
98
98
|
// Read learn.json to get the slug
|
package/lib/commands/start.js
CHANGED
@@ -82,11 +82,13 @@ class StartCommand extends SessionCommand_1.default {
|
|
82
82
|
socket_1.default.on("open", (data) => {
|
83
83
|
console_1.default.debug("Opening these files: ", data);
|
84
84
|
const files = (0, misc_1.prioritizeHTMLFile)(data.files);
|
85
|
-
if (config.editor.agent
|
85
|
+
if (config.editor.agent !== "os") {
|
86
|
+
// Console.info("Opening files for vscode agent")
|
86
87
|
osOperations_1.eventManager.enqueue(dispatcher.events.OPEN_FILES, files);
|
87
88
|
}
|
88
89
|
else {
|
89
|
-
dispatcher.enqueue(dispatcher.events.OPEN_FILES, files)
|
90
|
+
// dispatcher.enqueue(dispatcher.events.OPEN_FILES, files)
|
91
|
+
console_1.default.debug("Ignoring files for os agent");
|
90
92
|
}
|
91
93
|
socket_1.default.ready("Ready to compile...");
|
92
94
|
});
|
@@ -142,14 +144,6 @@ class StartCommand extends SessionCommand_1.default {
|
|
142
144
|
telemetry: telemetry_1.default,
|
143
145
|
});
|
144
146
|
});
|
145
|
-
// socket.on("quiz_submission", (data: any) => {
|
146
|
-
// const { stepPosition, event, eventData } = data
|
147
|
-
// TelemetryManager.registerStepEvent(stepPosition, event, eventData)
|
148
|
-
// })
|
149
|
-
// socket.on("ai_interaction", (data: any) => {
|
150
|
-
// const { stepPosition, event, eventData } = data
|
151
|
-
// TelemetryManager.registerStepEvent(stepPosition, event, eventData)
|
152
|
-
// })
|
153
147
|
socket_1.default.on("telemetry_event", (data) => {
|
154
148
|
const { stepPosition, event, eventData } = data;
|
155
149
|
telemetry_1.default.registerStepEvent(stepPosition, event, eventData);
|
@@ -222,9 +216,9 @@ class StartCommand extends SessionCommand_1.default {
|
|
222
216
|
setTimeout(() => dispatcher.enqueue(dispatcher.events.RUNNING), 1000);
|
223
217
|
// start watching for file changes
|
224
218
|
if (StartCommand.flags.watch)
|
225
|
-
this.configManager.watchIndex(_filename => {
|
219
|
+
this.configManager.watchIndex((_filename, _fileContent) => {
|
226
220
|
// Instead of reloading with socket.reload(), I just notify the frontend for the file change
|
227
|
-
socket_1.default.emit("file_change", "ready", _filename);
|
221
|
+
socket_1.default.emit("file_change", "ready", [_filename, _fileContent]);
|
228
222
|
});
|
229
223
|
}
|
230
224
|
}
|
@@ -372,6 +372,31 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
|
|
372
372
|
[(0, exercise_1.exercise)(((_b = configObj === null || configObj === void 0 ? void 0 : configObj.config) === null || _b === void 0 ? void 0 : _b.exercisesPath) || "", 0, configObj)];
|
373
373
|
this.save();
|
374
374
|
},
|
375
|
+
createExercise: (slug, content, language) => {
|
376
|
+
var _a;
|
377
|
+
try {
|
378
|
+
const dirPath = `${(_a = configObj.config) === null || _a === void 0 ? void 0 : _a.exercisesPath}/${slug}`;
|
379
|
+
if (!fs.existsSync(dirPath))
|
380
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
381
|
+
const isEnglish = language === "us" || language === "en";
|
382
|
+
const fileName = isEnglish ? "README.md" : `README.${language}.md`;
|
383
|
+
fs.writeFileSync(`${dirPath}/${fileName}`, content);
|
384
|
+
return true;
|
385
|
+
}
|
386
|
+
catch (error) {
|
387
|
+
console_1.default.error("Error creating exercise: ", error);
|
388
|
+
return false;
|
389
|
+
}
|
390
|
+
},
|
391
|
+
deleteExercise: (slug) => {
|
392
|
+
var _a;
|
393
|
+
const dirPath = `${(_a = configObj.config) === null || _a === void 0 ? void 0 : _a.exercisesPath}/${slug}`;
|
394
|
+
if (fs.existsSync(dirPath)) {
|
395
|
+
fs.rmSync(dirPath, { recursive: true, force: true });
|
396
|
+
return true;
|
397
|
+
}
|
398
|
+
return false;
|
399
|
+
},
|
375
400
|
watchIndex: function (onChange) {
|
376
401
|
var _a;
|
377
402
|
if (configObj.config && !configObj.config.exercisesPath)
|
package/lib/managers/file.js
CHANGED
@@ -11,7 +11,6 @@ const fileQueue_1 = require("../../utils/fileQueue");
|
|
11
11
|
const exercise_1 = require("../config/exercise");
|
12
12
|
const session_1 = require("../../managers/session");
|
13
13
|
const telemetry_1 = require("../telemetry");
|
14
|
-
const osOperations_1 = require("../../utils/osOperations");
|
15
14
|
const withHandler = (func) => (req, res) => {
|
16
15
|
try {
|
17
16
|
func(req, res);
|
@@ -200,10 +199,7 @@ async function default_1(app, configObject, configManager) {
|
|
200
199
|
if (exercise.position) {
|
201
200
|
telemetry_1.default.registerStepEvent(exercise.position, "open_step", {});
|
202
201
|
}
|
203
|
-
if (((_a = configObject.config) === null || _a === void 0 ? void 0 : _a.editor.agent)
|
204
|
-
osOperations_1.eventManager.enqueue(dispatcher.events.START_EXERCISE, exercise);
|
205
|
-
}
|
206
|
-
else {
|
202
|
+
if (((_a = configObject.config) === null || _a === void 0 ? void 0 : _a.editor.agent) !== "os") {
|
207
203
|
dispatcher.enqueue(dispatcher.events.START_EXERCISE, req.params.slug);
|
208
204
|
}
|
209
205
|
const entries = new Set(Object.keys(config === null || config === void 0 ? void 0 : config.entries).map(lang => config === null || config === void 0 ? void 0 : config.entries[lang]));
|
@@ -269,6 +265,36 @@ async function default_1(app, configObject, configManager) {
|
|
269
265
|
res.end();
|
270
266
|
}
|
271
267
|
}));
|
268
|
+
app.delete("/exercise/:slug/delete", withHandler(async (req, res) => {
|
269
|
+
const exerciseDeleted = configManager.deleteExercise(req.params.slug);
|
270
|
+
if (exerciseDeleted) {
|
271
|
+
configManager.buildIndex();
|
272
|
+
res.json({ status: "ok" });
|
273
|
+
}
|
274
|
+
else {
|
275
|
+
res.status(500).json({ error: "Failed to delete exercise" });
|
276
|
+
}
|
277
|
+
}));
|
278
|
+
app.post("/exercise/:slug/create", jsonBodyParser, withHandler(async (req, res) => {
|
279
|
+
const { title, readme, language } = req.body;
|
280
|
+
const { slug } = req.params;
|
281
|
+
if (!title || !readme || !language) {
|
282
|
+
return res.status(400).json({ error: "Missing required fields" });
|
283
|
+
}
|
284
|
+
try {
|
285
|
+
const exerciseCreated = await configManager.createExercise(slug, readme, language);
|
286
|
+
if (exerciseCreated) {
|
287
|
+
configManager.buildIndex();
|
288
|
+
res.json({ status: "ok" });
|
289
|
+
}
|
290
|
+
else {
|
291
|
+
res.status(500).json({ error: "Failed to create exercise" });
|
292
|
+
}
|
293
|
+
}
|
294
|
+
catch (_a) {
|
295
|
+
res.status(500).json({ error: "Failed to create exercise" });
|
296
|
+
}
|
297
|
+
}));
|
272
298
|
const textBodyParser = bodyParser.text();
|
273
299
|
app.put("/exercise/:slug/file/:fileName", textBodyParser, withHandler((req, res) => {
|
274
300
|
const exercise = configManager.getExercise(req.params.slug);
|
package/lib/managers/session.js
CHANGED
@@ -13,6 +13,8 @@ export interface IConfigManager {
|
|
13
13
|
getExercise: (slug: string | undefined) => IExercise;
|
14
14
|
startExercise: (slug: string) => IExercise;
|
15
15
|
reset: (slug: string) => void;
|
16
|
+
createExercise: (slug: string, content: string, language: string) => boolean;
|
17
|
+
deleteExercise: (slug: string) => boolean;
|
16
18
|
buildIndex: () => boolean | void;
|
17
19
|
watchIndex: (onChange: (...args: Array<any>) => void) => void;
|
18
20
|
save: () => void;
|
@@ -14,9 +14,11 @@ export type PackageInfo = {
|
|
14
14
|
us: string;
|
15
15
|
};
|
16
16
|
};
|
17
|
-
export declare function checkReadingTime(markdown: string, wordsPerMinute?: number): {
|
17
|
+
export declare function checkReadingTime(markdown: string, wordsPerMinute?: number, maxMinutes?: number): {
|
18
18
|
newMarkdown: string;
|
19
19
|
exceedsThreshold: boolean;
|
20
|
+
minutes: number;
|
21
|
+
body: string;
|
20
22
|
};
|
21
23
|
export declare const getExInfo: (title: string) => {
|
22
24
|
exNumber: string;
|
@@ -31,7 +33,7 @@ export declare function extractImagesFromMarkdown(markdown: string): {
|
|
31
33
|
export declare function getFilenameFromUrl(url: string): string;
|
32
34
|
export declare const makePackageInfo: (choices: any) => {
|
33
35
|
grading: any;
|
34
|
-
difficulty:
|
36
|
+
difficulty: string;
|
35
37
|
duration: number;
|
36
38
|
description: {
|
37
39
|
us: any;
|
@@ -41,4 +43,7 @@ export declare const makePackageInfo: (choices: any) => {
|
|
41
43
|
};
|
42
44
|
slug: any;
|
43
45
|
};
|
46
|
+
export declare function estimateDuration(listOfSteps: string[]): number;
|
47
|
+
export declare function createFileOnDesktop(): Promise<void>;
|
48
|
+
export declare function getContentIndex(): string;
|
44
49
|
export {};
|