@learnpack/learnpack 5.0.28 → 5.0.29
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/publish.js +1 -1
- package/lib/commands/start.js +9 -12
- package/lib/managers/config/index.js +77 -77
- package/lib/managers/server/routes.js +1 -5
- 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 +1 -1
- package/src/commands/breakToken.ts +13 -2
- package/src/commands/init.ts +103 -46
- package/src/commands/publish.ts +1 -1
- package/src/commands/start.ts +10 -14
- package/src/managers/config/index.ts +715 -715
- package/src/managers/server/routes.ts +1 -3
- package/src/utils/creatorUtilities.ts +241 -147
- package/src/utils/rigoActions.ts +30 -0
@@ -279,9 +279,7 @@ throw new Error("File not found: " + filePath)
|
|
279
279
|
TelemetryManager.registerStepEvent(exercise.position, "open_step", {})
|
280
280
|
}
|
281
281
|
|
282
|
-
if (configObject.config?.editor.agent
|
283
|
-
eventManager.enqueue(dispatcher.events.START_EXERCISE, exercise)
|
284
|
-
} else {
|
282
|
+
if (configObject.config?.editor.agent !== "os") {
|
285
283
|
dispatcher.enqueue(dispatcher.events.START_EXERCISE, req.params.slug)
|
286
284
|
}
|
287
285
|
|
@@ -1,147 +1,241 @@
|
|
1
|
-
// eslint-disable-next-line
|
2
|
-
const frontMatter = require("front-matter")
|
3
|
-
import * as path from "path"
|
4
|
-
|
5
|
-
import
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
}
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
}
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
.
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
return
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
}
|
146
|
-
|
147
|
-
|
1
|
+
// eslint-disable-next-line
|
2
|
+
const frontMatter = require("front-matter")
|
3
|
+
import * as path from "path"
|
4
|
+
import * as fs from "fs"
|
5
|
+
import { homedir } from "os"
|
6
|
+
import { join } from "path"
|
7
|
+
import { exec } from "child_process"
|
8
|
+
import { promisify } from "util"
|
9
|
+
|
10
|
+
import * as yaml from "js-yaml"
|
11
|
+
|
12
|
+
type TEstimateReadingTimeReturns = {
|
13
|
+
minutes: number
|
14
|
+
words: number
|
15
|
+
}
|
16
|
+
|
17
|
+
export const estimateReadingTime = (
|
18
|
+
text: string,
|
19
|
+
wordsPerMinute = 150
|
20
|
+
): TEstimateReadingTimeReturns => {
|
21
|
+
const words = text.trim().split(/\s+/).length
|
22
|
+
const minutes = words / wordsPerMinute
|
23
|
+
|
24
|
+
if (minutes < 1) {
|
25
|
+
if (words === 0)
|
26
|
+
return {
|
27
|
+
minutes: 1,
|
28
|
+
words,
|
29
|
+
}
|
30
|
+
} else {
|
31
|
+
return {
|
32
|
+
minutes,
|
33
|
+
words,
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
return {
|
38
|
+
minutes: 1,
|
39
|
+
words,
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
export type PackageInfo = {
|
44
|
+
grading: string
|
45
|
+
difficulty: string
|
46
|
+
duration: number
|
47
|
+
description: {
|
48
|
+
us: string
|
49
|
+
}
|
50
|
+
title: {
|
51
|
+
us: string
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
export function checkReadingTime(
|
56
|
+
markdown: string,
|
57
|
+
wordsPerMinute = 200,
|
58
|
+
maxMinutes = 1
|
59
|
+
): {
|
60
|
+
newMarkdown: string
|
61
|
+
exceedsThreshold: boolean
|
62
|
+
minutes: number
|
63
|
+
body: string
|
64
|
+
} {
|
65
|
+
const parsed = frontMatter(markdown)
|
66
|
+
|
67
|
+
const readingTime = estimateReadingTime(parsed.body, wordsPerMinute)
|
68
|
+
|
69
|
+
let attributes = parsed.attributes ? parsed.attributes : {}
|
70
|
+
|
71
|
+
if (typeof parsed.attributes !== "object") {
|
72
|
+
attributes = {}
|
73
|
+
}
|
74
|
+
|
75
|
+
const updatedAttributes = {
|
76
|
+
...attributes,
|
77
|
+
readingTime,
|
78
|
+
}
|
79
|
+
|
80
|
+
let yamlFrontMatter = ""
|
81
|
+
try {
|
82
|
+
yamlFrontMatter = yaml.dump(updatedAttributes).trim()
|
83
|
+
} catch {
|
84
|
+
return {
|
85
|
+
newMarkdown: "",
|
86
|
+
exceedsThreshold: false,
|
87
|
+
minutes: 0,
|
88
|
+
body: "",
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
// Reconstruct the markdown with the front matter
|
93
|
+
const newMarkdown = `---\n${yamlFrontMatter}\n---\n\n${parsed.body}`
|
94
|
+
|
95
|
+
return {
|
96
|
+
newMarkdown,
|
97
|
+
exceedsThreshold: readingTime.minutes > maxMinutes,
|
98
|
+
minutes: readingTime.minutes,
|
99
|
+
body: parsed.body,
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
const slugify = (text: string) => {
|
104
|
+
return text
|
105
|
+
.toString()
|
106
|
+
.normalize("NFD")
|
107
|
+
.replace(/[\u0300-\u036F]/g, "")
|
108
|
+
.toLowerCase()
|
109
|
+
.trim()
|
110
|
+
.replace(/\s+/g, "-")
|
111
|
+
.replace(/[^\w-]+/g, "")
|
112
|
+
}
|
113
|
+
|
114
|
+
export const getExInfo = (title: string) => {
|
115
|
+
// Example title: '1.0 - Introduction to AI [READ: Small introduction to important concepts such as AI, machine learning, and their applications]'
|
116
|
+
let [exNumber, exTitle] = title.split(" - ")
|
117
|
+
|
118
|
+
// Extract kind and description
|
119
|
+
const kindMatch = exTitle.match(/\[(.*?):(.*?)]/)
|
120
|
+
const kind = kindMatch ? kindMatch[1].trim().toLowerCase() : "read"
|
121
|
+
const description = kindMatch ? kindMatch[2].trim() : ""
|
122
|
+
|
123
|
+
exNumber = exNumber.trim()
|
124
|
+
// Clean title
|
125
|
+
exTitle = exTitle.replace(kindMatch?.[0] || "", "").trim()
|
126
|
+
exTitle = slugify(exTitle)
|
127
|
+
|
128
|
+
return {
|
129
|
+
exNumber,
|
130
|
+
kind,
|
131
|
+
description,
|
132
|
+
exTitle,
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
export function extractImagesFromMarkdown(markdown: string) {
|
137
|
+
const imageRegex = /!\[([^\]]*)]\(([^)]+)\)/g
|
138
|
+
const images = []
|
139
|
+
let match
|
140
|
+
|
141
|
+
while ((match = imageRegex.exec(markdown)) !== null) {
|
142
|
+
const altText = match[1]
|
143
|
+
const url = match[2]
|
144
|
+
images.push({ alt: altText, url: url })
|
145
|
+
}
|
146
|
+
|
147
|
+
return images
|
148
|
+
}
|
149
|
+
|
150
|
+
export function getFilenameFromUrl(url: string) {
|
151
|
+
return path.basename(url)
|
152
|
+
}
|
153
|
+
|
154
|
+
export const makePackageInfo = (choices: any) => {
|
155
|
+
const packageInfo = {
|
156
|
+
grading: choices.grading,
|
157
|
+
difficulty: "beginner",
|
158
|
+
duration: 5,
|
159
|
+
description: {
|
160
|
+
us: choices.description,
|
161
|
+
},
|
162
|
+
title: {
|
163
|
+
us: choices.title,
|
164
|
+
},
|
165
|
+
slug: choices.title
|
166
|
+
.toLowerCase()
|
167
|
+
.replace(/ /g, "-")
|
168
|
+
.replace(/[^\w-]+/g, ""),
|
169
|
+
}
|
170
|
+
return packageInfo
|
171
|
+
}
|
172
|
+
|
173
|
+
export function estimateDuration(listOfSteps: string[]): number {
|
174
|
+
let duration = 0
|
175
|
+
|
176
|
+
for (const step of listOfSteps) {
|
177
|
+
if (step.includes("[READ:")) {
|
178
|
+
duration += 1
|
179
|
+
} else if (step.includes("[QUIZ:")) {
|
180
|
+
duration += 2
|
181
|
+
} else if (step.includes("[CODE:")) {
|
182
|
+
duration += 3
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
return duration
|
187
|
+
}
|
188
|
+
|
189
|
+
const writeFilePromise = promisify(fs.writeFile)
|
190
|
+
const execPromise = promisify(exec)
|
191
|
+
|
192
|
+
const example_content = `
|
193
|
+
00.1 - Introduction to AI [READ: Small introduction to important concepts such as AI, machine learning, and their applications]
|
194
|
+
01.1 - Introduction to Machine Learning [READ: Small introduction to important concepts such as AI, machine learning, and their applications]
|
195
|
+
01.2 - Introduction to Deep Learning [QUIZ: Small introduction to important concepts such as AI, machine learning, and their applications]
|
196
|
+
02.1 - Test your knowledge [CODE: Code problem to solve]
|
197
|
+
`
|
198
|
+
|
199
|
+
export async function createFileOnDesktop() {
|
200
|
+
try {
|
201
|
+
const desktopPath = join(homedir(), "Desktop")
|
202
|
+
const filePath = join(desktopPath, "content_index.txt")
|
203
|
+
|
204
|
+
const content = example_content.trim()
|
205
|
+
|
206
|
+
await writeFilePromise(filePath, content)
|
207
|
+
console.log(`File created successfully at: ${filePath}`)
|
208
|
+
|
209
|
+
await openFile(filePath)
|
210
|
+
} catch (error) {
|
211
|
+
console.error("Error:", error)
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
async function openFile(filePath: string) {
|
216
|
+
const platform = process.platform
|
217
|
+
let command
|
218
|
+
|
219
|
+
if (platform === "win32") {
|
220
|
+
command = `start "" "${filePath}"`
|
221
|
+
} else if (platform === "darwin") {
|
222
|
+
command = `open "${filePath}"`
|
223
|
+
} else {
|
224
|
+
command = `xdg-open "${filePath}"`
|
225
|
+
}
|
226
|
+
|
227
|
+
try {
|
228
|
+
await execPromise(command)
|
229
|
+
console.log("File opened successfully.")
|
230
|
+
} catch (error) {
|
231
|
+
console.error("Error opening the file:", error)
|
232
|
+
}
|
233
|
+
}
|
234
|
+
|
235
|
+
export function getContentIndex() {
|
236
|
+
const desktopPath = join(homedir(), "Desktop")
|
237
|
+
const filePath = join(desktopPath, "content_index.txt")
|
238
|
+
|
239
|
+
const content = fs.readFileSync(filePath, "utf8")
|
240
|
+
return content
|
241
|
+
}
|
package/src/utils/rigoActions.ts
CHANGED
@@ -293,3 +293,33 @@ export async function createPreviewReadme(
|
|
293
293
|
})
|
294
294
|
fs.writeFileSync(path.join(tutorialDir, readmeFilename), readmeContent.answer)
|
295
295
|
}
|
296
|
+
|
297
|
+
// {"lesson": "The text of the lesson", "number_of_words": "Words lenght of the lesson", "expected_number_words": "The expected number of words"}
|
298
|
+
|
299
|
+
type TReduceReadmeInputs = {
|
300
|
+
lesson: string
|
301
|
+
number_of_words: string
|
302
|
+
expected_number_words: string
|
303
|
+
}
|
304
|
+
export async function reduceReadme(
|
305
|
+
rigoToken: string,
|
306
|
+
inputs: TReduceReadmeInputs
|
307
|
+
) {
|
308
|
+
try {
|
309
|
+
const response = await axios.post(
|
310
|
+
`${RIGOBOT_HOST}/v1/prompting/completion/588/`,
|
311
|
+
{ inputs, include_purpose_objective: false, execute_async: false },
|
312
|
+
{
|
313
|
+
headers: {
|
314
|
+
"Content-Type": "application/json",
|
315
|
+
Authorization: "Token " + rigoToken,
|
316
|
+
},
|
317
|
+
}
|
318
|
+
)
|
319
|
+
|
320
|
+
return response.data
|
321
|
+
} catch (error) {
|
322
|
+
Console.debug(error)
|
323
|
+
return null
|
324
|
+
}
|
325
|
+
}
|