@learnpack/learnpack 5.0.32 → 5.0.33
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/start.js +3 -27
- package/lib/managers/server/routes.js +9 -10
- package/lib/managers/session.js +0 -6
- package/lib/managers/telemetry.d.ts +6 -25
- package/lib/managers/telemetry.js +14 -212
- package/lib/models/action.d.ts +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/start.ts +330 -361
- package/src/managers/server/routes.ts +10 -11
- package/src/managers/session.ts +0 -5
- package/src/managers/telemetry.ts +27 -277
- package/src/models/action.ts +13 -12
- package/src/models/status.ts +1 -1
@@ -58,7 +58,7 @@ export default async function (
|
|
58
58
|
|
59
59
|
// Added this line to parse the json body
|
60
60
|
|
61
|
-
const jsonBodyParser = bodyParser.json()
|
61
|
+
const jsonBodyParser = bodyParser.json({ limit: "10mb" })
|
62
62
|
// Trying to log in from frontend
|
63
63
|
app.post(
|
64
64
|
"/login",
|
@@ -172,10 +172,15 @@ export default async function (
|
|
172
172
|
})
|
173
173
|
)
|
174
174
|
|
175
|
-
app.get("/telemetry
|
176
|
-
const
|
177
|
-
|
178
|
-
|
175
|
+
app.get("/telemetry", async (req, res) => {
|
176
|
+
const telemetry = await TelemetryManager.retrieve()
|
177
|
+
res.json(telemetry)
|
178
|
+
})
|
179
|
+
|
180
|
+
app.post("/telemetry", jsonBodyParser, async (req, res) => {
|
181
|
+
const telemetry = req.body
|
182
|
+
await TelemetryManager.save(telemetry)
|
183
|
+
res.json({ message: "Telemetry saved successfully" })
|
179
184
|
})
|
180
185
|
|
181
186
|
app.get(
|
@@ -185,12 +190,6 @@ export default async function (
|
|
185
190
|
|
186
191
|
if (payload && payload.rigobot && payload.rigobot.key) {
|
187
192
|
res.json({ rigoToken: payload.rigobot.key, payload: payload })
|
188
|
-
|
189
|
-
TelemetryManager.setStudent({
|
190
|
-
user_id: payload.user_id,
|
191
|
-
email: payload.email,
|
192
|
-
token: payload.token,
|
193
|
-
})
|
194
193
|
} else {
|
195
194
|
res
|
196
195
|
.status(400)
|
package/src/managers/session.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { IFile } from "../models/file"
|
2
|
-
import
|
2
|
+
import { ISocket } from "../models/socket"
|
3
3
|
const packageInfo = require("../../package.json")
|
4
4
|
import Console from "../utils/console"
|
5
5
|
|
@@ -107,242 +107,46 @@ type TUser = {
|
|
107
107
|
email: string
|
108
108
|
}
|
109
109
|
|
110
|
-
interface
|
111
|
-
current: ITelemetryJSONSchema | null
|
110
|
+
interface ITelemetryManagerCLI {
|
112
111
|
configPath: string | null
|
113
|
-
|
114
|
-
urls: TTelemetryUrls
|
115
|
-
started: boolean
|
112
|
+
socket: ISocket | null
|
116
113
|
version: string
|
117
|
-
|
118
|
-
start: (
|
119
|
-
agent: string,
|
120
|
-
steps: TStep[],
|
121
|
-
path: string,
|
122
|
-
tutorialSlug: string
|
123
|
-
) => void
|
124
|
-
prevStep?: number
|
114
|
+
start: (path: string, socket: ISocket) => void
|
125
115
|
registerStepEvent: (
|
126
116
|
stepPosition: number,
|
127
117
|
event: TStepEvent,
|
128
118
|
data: any
|
129
119
|
) => void
|
130
|
-
|
131
|
-
submit: () => Promise<void>
|
132
|
-
finishWorkoutSession: () => void
|
133
|
-
setStudent: (student: TStudent) => void
|
134
|
-
save: () => void
|
120
|
+
save: (telemetry: ITelemetryJSONSchema) => void
|
135
121
|
retrieve: () => Promise<ITelemetryJSONSchema | null>
|
136
|
-
getStep: (stepPosition: number) => TStep | null
|
137
122
|
}
|
138
123
|
|
139
|
-
const TelemetryManager:
|
140
|
-
current: null,
|
141
|
-
urls: {},
|
124
|
+
const TelemetryManager: ITelemetryManagerCLI = {
|
142
125
|
version: packageInfo.version,
|
143
|
-
|
144
|
-
|
145
|
-
id: "",
|
146
|
-
email: "",
|
147
|
-
},
|
126
|
+
socket: null,
|
127
|
+
|
148
128
|
configPath: "",
|
149
|
-
started: false,
|
150
|
-
salute: message => {
|
151
|
-
Console.info(message)
|
152
|
-
},
|
153
129
|
|
154
|
-
start: function (
|
130
|
+
start: function (path, socket) {
|
155
131
|
this.configPath = path
|
156
|
-
|
157
|
-
this.retrieve()
|
158
|
-
.then(data => {
|
159
|
-
const prevTelemetry = data
|
160
|
-
if (prevTelemetry) {
|
161
|
-
this.current = prevTelemetry
|
162
|
-
this.finishWorkoutSession()
|
163
|
-
} else {
|
164
|
-
this.current = {
|
165
|
-
telemetry_id: createUUID(),
|
166
|
-
slug: tutorialSlug,
|
167
|
-
version: `CLI:${this.version}`,
|
168
|
-
user_id: 0,
|
169
|
-
agent,
|
170
|
-
tutorial_started_at: Date.now(),
|
171
|
-
last_interaction_at: Date.now(),
|
172
|
-
steps,
|
173
|
-
workout_session: [
|
174
|
-
{
|
175
|
-
started_at: Date.now(),
|
176
|
-
},
|
177
|
-
],
|
178
|
-
}
|
179
|
-
}
|
180
|
-
|
181
|
-
if (this.current.user_id) {
|
182
|
-
this.user.id = this.current.user_id.toString()
|
183
|
-
}
|
184
|
-
|
185
|
-
if (!this.current.version) {
|
186
|
-
this.current.version = `CLI:${this.version}`
|
187
|
-
}
|
188
|
-
|
189
|
-
this.save()
|
190
|
-
|
191
|
-
this.started = true
|
192
|
-
Console.debug("Telemetry started successfully!")
|
193
|
-
|
194
|
-
if (!this.user.id) {
|
195
|
-
Console.debug(
|
196
|
-
"No user ID found, impossible to submit telemetry at start"
|
197
|
-
)
|
198
|
-
return
|
199
|
-
}
|
200
|
-
|
201
|
-
this.submit()
|
202
|
-
})
|
203
|
-
.catch(error => {
|
204
|
-
Console.debug(error)
|
205
|
-
// Delete the telemetry.json if it exists
|
206
|
-
fs.unlinkSync(`${this.configPath}/telemetry.json`)
|
207
|
-
throw new Error(
|
208
|
-
"There was a problem starting, reload LearnPack\nRun\n$ learnpack start"
|
209
|
-
)
|
210
|
-
})
|
211
|
-
}
|
212
|
-
},
|
213
|
-
|
214
|
-
setStudent: function (student) {
|
215
|
-
if (!this.current) {
|
216
|
-
Console.debug("Telemetry has not been started")
|
217
|
-
|
218
|
-
return
|
219
|
-
}
|
220
|
-
|
221
|
-
Console.debug("Setting student", student)
|
222
|
-
|
223
|
-
this.current.user_id = student.user_id
|
224
|
-
this.user.id = student.user_id
|
225
|
-
this.user.token = student.token
|
226
|
-
this.user.email = student.email
|
227
|
-
this.save()
|
228
|
-
this.submit()
|
229
|
-
},
|
230
|
-
finishWorkoutSession: function () {
|
231
|
-
if (!this.current) {
|
232
|
-
return
|
233
|
-
}
|
234
|
-
|
235
|
-
const lastSession =
|
236
|
-
this.current?.workout_session[this.current.workout_session.length - 1]
|
237
|
-
if (
|
238
|
-
lastSession &&
|
239
|
-
!lastSession.ended_at &&
|
240
|
-
this.current?.last_interaction_at
|
241
|
-
) {
|
242
|
-
lastSession.ended_at = this.current.last_interaction_at
|
243
|
-
this.current.workout_session.push({
|
244
|
-
started_at: Date.now(),
|
245
|
-
})
|
246
|
-
}
|
132
|
+
this.socket = socket
|
247
133
|
},
|
248
134
|
|
249
135
|
registerStepEvent: function (stepPosition, event, data) {
|
250
|
-
Console.debug(`Registering Event ${event}
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
if (data.source_code) {
|
263
|
-
data.source_code = stringToBase64(data.source_code)
|
264
|
-
}
|
265
|
-
|
266
|
-
if (data.stdout) {
|
267
|
-
data.stdout = stringToBase64(data.stdout)
|
268
|
-
}
|
269
|
-
|
270
|
-
if (data.stderr) {
|
271
|
-
data.stderr = stringToBase64(data.stderr)
|
272
|
-
}
|
273
|
-
|
274
|
-
if (Object.prototype.hasOwnProperty.call(data, "exitCode")) {
|
275
|
-
data.exit_code = data.exitCode
|
276
|
-
data.exitCode = undefined
|
277
|
-
}
|
278
|
-
|
279
|
-
switch (event) {
|
280
|
-
case "compile":
|
281
|
-
if (!step.compilations) {
|
282
|
-
step.compilations = []
|
283
|
-
}
|
284
|
-
|
285
|
-
step.compilations.push(data)
|
286
|
-
this.current.steps[stepPosition] = step
|
287
|
-
break
|
288
|
-
case "test":
|
289
|
-
if (!step.tests) {
|
290
|
-
step.tests = []
|
291
|
-
}
|
292
|
-
|
293
|
-
// data.stdout =
|
294
|
-
step.tests.push(data)
|
295
|
-
if (data.exit_code === 0) {
|
296
|
-
step.completed_at = Date.now()
|
297
|
-
}
|
298
|
-
|
299
|
-
this.current.steps[stepPosition] = step
|
300
|
-
break
|
301
|
-
case "ai_interaction":
|
302
|
-
if (!step.ai_interactions) {
|
303
|
-
step.ai_interactions = []
|
304
|
-
}
|
305
|
-
|
306
|
-
step.ai_interactions.push(data)
|
307
|
-
break
|
308
|
-
|
309
|
-
case "quiz_submission": {
|
310
|
-
if (!step.quiz_submissions) {
|
311
|
-
step.quiz_submissions = []
|
312
|
-
}
|
313
|
-
|
314
|
-
step.quiz_submissions.push(data)
|
315
|
-
break
|
136
|
+
Console.debug(`Registering Telemetry Event ${event}`)
|
137
|
+
|
138
|
+
this.socket?.emit(
|
139
|
+
"telemetry_event",
|
140
|
+
"ready",
|
141
|
+
[`Registering telemetry event ${event}`],
|
142
|
+
[],
|
143
|
+
[],
|
144
|
+
{
|
145
|
+
stepPosition,
|
146
|
+
event,
|
147
|
+
data,
|
316
148
|
}
|
317
|
-
|
318
|
-
case "open_step": {
|
319
|
-
const now = Date.now()
|
320
|
-
|
321
|
-
if (!step.opened_at) {
|
322
|
-
step.opened_at = now
|
323
|
-
this.current.steps[stepPosition] = step
|
324
|
-
}
|
325
|
-
|
326
|
-
if (this.prevStep || this.prevStep === 0) {
|
327
|
-
const prevStep = this.current.steps[this.prevStep]
|
328
|
-
if (!prevStep.is_testeable && !prevStep.completed_at) {
|
329
|
-
prevStep.completed_at = now
|
330
|
-
this.current.steps[this.prevStep] = prevStep
|
331
|
-
}
|
332
|
-
}
|
333
|
-
|
334
|
-
this.prevStep = stepPosition
|
335
|
-
this.submit()
|
336
|
-
break
|
337
|
-
}
|
338
|
-
|
339
|
-
default:
|
340
|
-
throw new Error(`Event type ${event} is not supported`)
|
341
|
-
}
|
342
|
-
|
343
|
-
this.current.last_interaction_at = Date.now()
|
344
|
-
this.streamEvent(stepPosition, event, data)
|
345
|
-
this.save()
|
149
|
+
)
|
346
150
|
},
|
347
151
|
retrieve: function () {
|
348
152
|
return new Promise((resolve, reject) => {
|
@@ -360,8 +164,8 @@ const TelemetryManager: ITelemetryManager = {
|
|
360
164
|
} else {
|
361
165
|
try {
|
362
166
|
resolve(JSON.parse(data))
|
363
|
-
} catch
|
364
|
-
|
167
|
+
} catch {
|
168
|
+
resolve(null)
|
365
169
|
}
|
366
170
|
}
|
367
171
|
}
|
@@ -369,70 +173,16 @@ const TelemetryManager: ITelemetryManager = {
|
|
369
173
|
})
|
370
174
|
},
|
371
175
|
|
372
|
-
|
373
|
-
return this.current?.steps[stepPosition] || null
|
374
|
-
},
|
375
|
-
|
376
|
-
submit: async function () {
|
377
|
-
Console.debug("Submitting telemetry...")
|
378
|
-
|
379
|
-
if (!this.current) {
|
380
|
-
Console.debug("Telemetry has not been started")
|
381
|
-
return Promise.resolve()
|
382
|
-
}
|
383
|
-
|
384
|
-
if (!this.user.id) {
|
385
|
-
Console.debug("User ID not found, skipping batch telemetry delivery")
|
386
|
-
return Promise.resolve()
|
387
|
-
}
|
388
|
-
|
389
|
-
const url = this.urls.batch
|
390
|
-
if (!url) {
|
391
|
-
Console.debug("Batch URL not found, skipping batch telemetry delivery")
|
392
|
-
return Promise.resolve()
|
393
|
-
}
|
394
|
-
|
395
|
-
const body = this.current
|
396
|
-
|
397
|
-
if (!body.user_id) {
|
398
|
-
body.user_id = this.user.id
|
399
|
-
}
|
400
|
-
|
401
|
-
API.sendBatchTelemetry(url, body)
|
402
|
-
},
|
403
|
-
save: function () {
|
176
|
+
save: function (telemetry: ITelemetryJSONSchema) {
|
404
177
|
fs.writeFile(
|
405
178
|
`${this.configPath}/telemetry.json`,
|
406
|
-
JSON.stringify(
|
179
|
+
JSON.stringify(telemetry),
|
407
180
|
(err: any) => {
|
408
181
|
if (err)
|
409
182
|
throw err
|
410
183
|
}
|
411
184
|
)
|
412
185
|
},
|
413
|
-
|
414
|
-
streamEvent: async function (stepPosition, event, data) {
|
415
|
-
if (!this.current)
|
416
|
-
return
|
417
|
-
|
418
|
-
const url = this.urls.streaming
|
419
|
-
if (!url) {
|
420
|
-
return
|
421
|
-
}
|
422
|
-
|
423
|
-
const stepSlug = this.current.steps[stepPosition].slug
|
424
|
-
|
425
|
-
const body = {
|
426
|
-
slug: stepSlug,
|
427
|
-
telemetry_id: this.current.telemetry_id,
|
428
|
-
user_id: this.current.user_id,
|
429
|
-
step_position: stepPosition,
|
430
|
-
event,
|
431
|
-
data,
|
432
|
-
}
|
433
|
-
|
434
|
-
API.sendStreamTelemetry(url, body)
|
435
|
-
},
|
436
186
|
}
|
437
187
|
|
438
188
|
export default TelemetryManager
|
package/src/models/action.ts
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
-
export type TAction =
|
2
|
-
| "test"
|
3
|
-
| "log"
|
4
|
-
| "reload"
|
5
|
-
| "ready"
|
6
|
-
| "clean"
|
7
|
-
| "ask"
|
8
|
-
| "file_change"
|
9
|
-
| "dialog"
|
10
|
-
| "session-refreshed"
|
11
|
-
|
12
|
-
|
1
|
+
export type TAction =
|
2
|
+
| "test"
|
3
|
+
| "log"
|
4
|
+
| "reload"
|
5
|
+
| "ready"
|
6
|
+
| "clean"
|
7
|
+
| "ask"
|
8
|
+
| "file_change"
|
9
|
+
| "dialog"
|
10
|
+
| "session-refreshed"
|
11
|
+
| "telemetry_event"
|
12
|
+
|
13
|
+
export type ICallback = (...agrs: any[]) => any
|
package/src/models/status.ts
CHANGED