@learnpack/learnpack 2.1.36 → 2.1.38

Sign up to get free protection for your applications and to get access to all the features.
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/2.1.36 darwin-arm64 node-v16.20.0
24
+ @learnpack/learnpack/2.1.38 darwin-arm64 node-v16.20.0
25
25
  $ learnpack --help [COMMAND]
26
26
  USAGE
27
27
  $ learnpack COMMAND
@@ -74,7 +74,7 @@ DESCRIPTION
74
74
  12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
75
75
  ```
76
76
 
77
- _See code: [src/commands/audit.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.36/src/commands/audit.ts)_
77
+ _See code: [src/commands/audit.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/src/commands/audit.ts)_
78
78
 
79
79
  ## `learnpack clean`
80
80
 
@@ -89,7 +89,7 @@ DESCRIPTION
89
89
  Extra documentation goes here
90
90
  ```
91
91
 
92
- _See code: [src/commands/clean.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.36/src/commands/clean.ts)_
92
+ _See code: [src/commands/clean.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/src/commands/clean.ts)_
93
93
 
94
94
  ## `learnpack download [PACKAGE]`
95
95
 
@@ -107,7 +107,7 @@ DESCRIPTION
107
107
  Extra documentation goes here
108
108
  ```
109
109
 
110
- _See code: [src/commands/download.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.36/src/commands/download.ts)_
110
+ _See code: [src/commands/download.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/src/commands/download.ts)_
111
111
 
112
112
  ## `learnpack help [COMMAND]`
113
113
 
@@ -138,7 +138,7 @@ OPTIONS
138
138
  -h, --grading show CLI help
139
139
  ```
140
140
 
141
- _See code: [src/commands/init.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.36/src/commands/init.ts)_
141
+ _See code: [src/commands/init.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/src/commands/init.ts)_
142
142
 
143
143
  ## `learnpack login [PACKAGE]`
144
144
 
@@ -156,7 +156,7 @@ DESCRIPTION
156
156
  Extra documentation goes here
157
157
  ```
158
158
 
159
- _See code: [src/commands/login.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.36/src/commands/login.ts)_
159
+ _See code: [src/commands/login.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/src/commands/login.ts)_
160
160
 
161
161
  ## `learnpack logout [PACKAGE]`
162
162
 
@@ -174,7 +174,7 @@ DESCRIPTION
174
174
  Extra documentation goes here
175
175
  ```
176
176
 
177
- _See code: [src/commands/logout.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.36/src/commands/logout.ts)_
177
+ _See code: [src/commands/logout.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/src/commands/logout.ts)_
178
178
 
179
179
  ## `learnpack plugins`
180
180
 
@@ -309,7 +309,7 @@ DESCRIPTION
309
309
  Extra documentation goes here
310
310
  ```
311
311
 
312
- _See code: [src/commands/publish.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.36/src/commands/publish.ts)_
312
+ _See code: [src/commands/publish.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/src/commands/publish.ts)_
313
313
 
314
314
  ## `learnpack start`
315
315
 
@@ -322,7 +322,7 @@ USAGE
322
322
  OPTIONS
323
323
  -D, --disableGrading disble grading functionality
324
324
  -d, --debug debugger mode for more verbage
325
- -e, --editor=standalone|gitpod [standalone, gitpod]
325
+ -e, --editor=extension|preview [preview, extension]
326
326
  -g, --grading=isolated|incremental [isolated, incremental]
327
327
  -h, --host=host server host
328
328
  -p, --port=port server port
@@ -330,7 +330,7 @@ OPTIONS
330
330
  -w, --watch Watch for file changes
331
331
  ```
332
332
 
333
- _See code: [src/commands/start.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.36/src/commands/start.ts)_
333
+ _See code: [src/commands/start.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/src/commands/start.ts)_
334
334
 
335
335
  ## `learnpack test [EXERCISESLUG]`
336
336
 
@@ -344,7 +344,7 @@ ARGUMENTS
344
344
  EXERCISESLUG The name of the exercise to test
345
345
  ```
346
346
 
347
- _See code: [src/commands/test.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.36/src/commands/test.ts)_
347
+ _See code: [src/commands/test.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/src/commands/test.ts)_
348
348
  <!-- commandsstop -->
349
349
 
350
350
  > > > > > > > 0cb3e56d84c197f9d008836bb573eade212b7e57
@@ -10,6 +10,7 @@ const server_1 = require("../managers/server");
10
10
  const fileQueue_1 = require("../utils/fileQueue");
11
11
  const file_1 = require("../managers/file");
12
12
  const misc_1 = require("../utils/misc");
13
+ const osOperations_1 = require("../utils/osOperations");
13
14
  class StartCommand extends SessionCommand_1.default {
14
15
  // 🛑 IMPORTANT
15
16
  // Every command that will use the configManager needs this init method
@@ -38,6 +39,9 @@ class StartCommand extends SessionCommand_1.default {
38
39
  // listen to socket commands
39
40
  if (config && this.configManager) {
40
41
  const server = await server_1.default(configObject, this.configManager, process.env.NODE_ENV === "test");
42
+ server.setMaxListeners(20);
43
+ // console.log(config, "this is the f*!shell.which("code")!shell.which("code")cking config");
44
+ // I should call a method to get the EventListener
41
45
  const dispatcher = fileQueue_1.default.dispatcher({
42
46
  create: true,
43
47
  path: `${config.dirPath}/vscode_queue.json`,
@@ -46,7 +50,6 @@ class StartCommand extends SessionCommand_1.default {
46
50
  const agent = ((_d = configObject.config) === null || _d === void 0 ? void 0 : _d.editor.agent) || "";
47
51
  const path = ((_e = configObject.config) === null || _e === void 0 ? void 0 : _e.dirPath) || "";
48
52
  const tutorialSlug = ((_f = configObject.config) === null || _f === void 0 ? void 0 : _f.slug) || "";
49
- console_1.default.info("Starting Telemetry with slug...", tutorialSlug);
50
53
  const steps = configObject.exercises.map((e, index) => ({
51
54
  slug: e.slug,
52
55
  position: e.position || index,
@@ -67,12 +70,21 @@ class StartCommand extends SessionCommand_1.default {
67
70
  socket_1.default.on("open", (data) => {
68
71
  console_1.default.debug("Opening these files: ", data);
69
72
  const files = misc_1.prioritizeHTMLFile(data.files);
70
- dispatcher.enqueue(dispatcher.events.OPEN_FILES, files);
73
+ if (config.editor.agent === "os") {
74
+ osOperations_1.eventManager.enqueue(dispatcher.events.OPEN_FILES, files);
75
+ }
76
+ else {
77
+ dispatcher.enqueue(dispatcher.events.OPEN_FILES, files);
78
+ }
71
79
  socket_1.default.ready("Ready to compile...");
72
80
  });
73
81
  socket_1.default.on("open_window", (data) => {
74
82
  console_1.default.debug("Opening window: ", data);
75
- dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data);
83
+ // if (config.editor.agent === "os") {
84
+ // } else {
85
+ // dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data);
86
+ // }
87
+ osOperations_1.eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data);
76
88
  socket_1.default.ready("Ready to compile...");
77
89
  });
78
90
  socket_1.default.on("reset", (exercise) => {
@@ -88,11 +100,6 @@ class StartCommand extends SessionCommand_1.default {
88
100
  setTimeout(() => socket_1.default.ready("Ready to compile..."), 2000);
89
101
  }
90
102
  });
91
- // socket.on("preview", (data) => {
92
- // Console.debug("Preview triggered, removing the 'preview' action ")
93
- // socket.removeAllowed("preview")
94
- // socket.log('ready',['Ready to compile...'])
95
- // })
96
103
  socket_1.default.on("build", async (data) => {
97
104
  var _a;
98
105
  const exercise = (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.getExercise(data.exerciseSlug);
@@ -144,14 +151,12 @@ class StartCommand extends SessionCommand_1.default {
144
151
  return true;
145
152
  });
146
153
  const terminate = () => {
147
- console_1.default.debug("Terminating Learnpack...");
154
+ var _a;
155
+ console_1.default.error("Terminating Learnpack...");
148
156
  telemetry_1.default.submit();
149
- server.terminate(() => {
150
- var _a;
151
- (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.noCurrentExercise();
152
- dispatcher.enqueue(dispatcher.events.END);
153
- process.exit();
154
- });
157
+ (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.noCurrentExercise();
158
+ dispatcher.enqueue(dispatcher.events.END);
159
+ process.exit();
155
160
  };
156
161
  server.on("close", terminate);
157
162
  process.on("SIGINT", terminate);
@@ -183,8 +188,8 @@ StartCommand.flags = Object.assign(Object.assign({}, SessionCommand_1.default.fl
183
188
  default: false,
184
189
  }), editor: command_1.flags.string({
185
190
  char: "e",
186
- description: "[standalone, gitpod]",
187
- options: ["standalone", "gitpod"],
191
+ description: "[preview, extension]",
192
+ options: ["extension", "preview"],
188
193
  }), version: command_1.flags.string({
189
194
  char: "v",
190
195
  description: "E.g: 1.0.1",
@@ -1,6 +1,8 @@
1
+ /// <reference types="node" />
1
2
  declare const _default: {
2
3
  config: {
3
4
  port: number;
5
+ os: NodeJS.Platform;
4
6
  editor: {
5
7
  mode: null;
6
8
  agent: null;
@@ -1,8 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const os = require("os");
3
4
  exports.default = {
4
5
  config: {
5
6
  port: 3000,
7
+ os: (function () {
8
+ return os.platform();
9
+ })(),
6
10
  editor: {
7
11
  mode: null,
8
12
  agent: null,
@@ -82,11 +82,14 @@ exports.exercise = (path, position, configObject) => {
82
82
  fs.mkdirSync(`${config === null || config === void 0 ? void 0 : config.dirPath}/resets`);
83
83
  if (!fs.existsSync(`${config === null || config === void 0 ? void 0 : config.dirPath}/resets/` + this.slug)) {
84
84
  fs.mkdirSync(`${config === null || config === void 0 ? void 0 : config.dirPath}/resets/` + this.slug);
85
- for (const _file of this.files) {
86
- const fileContent = fs.readFileSync(_file.path);
87
- if (!fs.existsSync(`${config === null || config === void 0 ? void 0 : config.dirPath}/resets/${this.slug}/${_file.name}`)) {
88
- fs.writeFileSync(`${config === null || config === void 0 ? void 0 : config.dirPath}/resets/${this.slug}/${_file.name}`, fileContent);
89
- }
85
+ }
86
+ for (const _file of this.files) {
87
+ const stats = fs.statSync(_file.path);
88
+ if (stats.isDirectory() || _file.hidden)
89
+ continue;
90
+ const fileContent = fs.readFileSync(_file.path);
91
+ if (!fs.existsSync(`${config === null || config === void 0 ? void 0 : config.dirPath}/resets/${this.slug}/${_file.name}`)) {
92
+ fs.writeFileSync(`${config === null || config === void 0 ? void 0 : config.dirPath}/resets/${this.slug}/${_file.name}`, fileContent);
90
93
  }
91
94
  }
92
95
  return content;
@@ -15,6 +15,12 @@ const fetch = require("node-fetch");
15
15
  // eslint-disable-next-line
16
16
  const chalk = require("chalk");
17
17
  /* exercise folder name standard */
18
+ /**
19
+ * Retrieves the configuration path for the learnpack package.
20
+ *
21
+ * @returns An object containing the configuration file path and the base directory.
22
+ * @throws NotFoundError if the learn.json file is not found in the current folder.
23
+ */
18
24
  const getConfigPath = () => {
19
25
  const possibleFileNames = ["learn.json", ".learn/learn.json"];
20
26
  const config = possibleFileNames.find(file => fs.existsSync(file)) || null;
@@ -38,7 +44,7 @@ const getGitpodAddress = () => {
38
44
  return "http://localhost";
39
45
  };
40
46
  const getCodespacesNamespace = () => {
41
- // https://orange-rotary-phone-wxpg49q5gcg4rp-3000.app.github.dev
47
+ // Example: https://orange-rotary-phone-wxpg49q5gcg4rp-3000.app.github.dev
42
48
  const codespace_name = shell
43
49
  .exec("echo $CODESPACE_NAME", { silent: true })
44
50
  .stdout.replace(/(\r\n|\n|\r)/gm, "");
@@ -52,12 +58,12 @@ const getCodespacesNamespace = () => {
52
58
  return codespace_name;
53
59
  };
54
60
  exports.default = async ({ grading, mode, disableGrading, version, }) => {
55
- var _a, _b, _c, _d, _e;
61
+ var _a, _b, _c, _d;
56
62
  const confPath = getConfigPath();
57
63
  console_1.default.debug("This is the config path: ", confPath);
58
64
  let configObj = {};
59
65
  if (confPath) {
60
- const bcContent = fs.readFileSync(confPath.config);
66
+ const learnJsonContent = fs.readFileSync(confPath.config);
61
67
  let hiddenBcContent = {};
62
68
  if (fs.existsSync(confPath.base + "/config.json")) {
63
69
  hiddenBcContent = fs.readFileSync(confPath.base + "/config.json");
@@ -65,7 +71,7 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
65
71
  if (!hiddenBcContent)
66
72
  throw new Error(`Invalid ${confPath.base}/config.json syntax: Unable to parse.`);
67
73
  }
68
- const jsonConfig = JSON.parse(`${bcContent}`);
74
+ const jsonConfig = JSON.parse(`${learnJsonContent}`);
69
75
  if (!jsonConfig)
70
76
  throw new Error(`Invalid ${confPath.config} syntax: Unable to parse.`);
71
77
  let session;
@@ -83,7 +89,7 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
83
89
  });
84
90
  }
85
91
  else {
86
- throw errors_1.ValidationError("No learn.json file has been found, make sure you are in the folder");
92
+ throw errors_1.ValidationError("No learn.json file has been found, make sure you are in the correct directory. To start a new LearnPack package run: $ learnpack init my-course-name");
87
93
  }
88
94
  configObj = deepMerge(defaults_1.default || {}, configObj, {
89
95
  config: {
@@ -91,9 +97,9 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
91
97
  configPath: confPath.config,
92
98
  },
93
99
  });
94
- if (configObj.config) {
95
- configObj.config.outputPath = confPath.base + "/dist";
96
- }
100
+ if (!configObj.config)
101
+ throw errors_1.InternalError("Config object not found");
102
+ configObj.config.outputPath = confPath.base + "/dist";
97
103
  console_1.default.debug("This is your configuration object: ", Object.assign(Object.assign({}, configObj), { exercises: configObj.exercises ?
98
104
  configObj.exercises.map(e => e.slug) :
99
105
  [] }));
@@ -103,46 +109,43 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
103
109
  console_1.default.debug("This is the agent, and this should be null an the beginning: ", (_c = (_b = configObj.config) === null || _b === void 0 ? void 0 : _b.editor) === null || _c === void 0 ? void 0 : _c.agent);
104
110
  if (shell.which("gp") && configObj && configObj.config) {
105
111
  console_1.default.debug("Gitpod detected");
106
- configObj.config.editor.agent = "vscode";
107
112
  configObj.address = getGitpodAddress();
108
113
  configObj.config.publicUrl = `https://${configObj.config.port}-${(_d = configObj.address) === null || _d === void 0 ? void 0 : _d.slice(8)}`;
109
114
  }
110
115
  else if (configObj.config && Boolean(codespaces_workspace)) {
111
116
  console_1.default.debug("Codespaces detected: ", codespaces_workspace);
112
- configObj.config.editor.agent = "vscode";
113
117
  configObj.address = `https://${codespaces_workspace}.github.dev`;
114
118
  configObj.config.publicUrl = `https://${codespaces_workspace}-${configObj.config.port}.app.github.dev`;
115
119
  // TODO: Why is needed to have an agent?
116
- // } else if (configObj.config && !configObj.config.editor.agent) {
117
120
  }
118
- else if (configObj.config) {
119
- console_1.default.debug("Localhost detected");
120
- configObj.config.editor.agent = "localhost";
121
+ else {
122
+ console_1.default.debug("Neither Gitpod nor Codespaces detected, using localhost.");
121
123
  configObj.address = `http://localhost:${configObj.config.port}`;
122
124
  configObj.config.publicUrl = `http://localhost:${configObj.config.port}`;
123
125
  }
124
- console_1.default.debug("This is the agent later", (_e = configObj === null || configObj === void 0 ? void 0 : configObj.config) === null || _e === void 0 ? void 0 : _e.editor.agent);
126
+ if (!configObj.config.editor.agent) {
127
+ configObj.config.editor.agent = shell.which("code") ? "vscode" : "os";
128
+ }
129
+ // TODO: We need to set the correct EventListener if the agent is not vscode
125
130
  if (configObj.config && !configObj.config.publicUrl)
126
131
  configObj.config.publicUrl = `${configObj.address}:${configObj.config.port}`;
127
- // Assign default editor mode if not set already
128
- if (configObj.config && !mode) {
132
+ if (configObj.config && !configObj.config.editor.mode && mode) {
129
133
  configObj.config.editor.mode = mode;
130
134
  }
131
- if (configObj.config && !configObj.config.editor.mode)
135
+ if (!configObj.config.editor.mode) {
132
136
  configObj.config.editor.mode =
133
- configObj.config.editor.agent === "localhost" ? "standalone" : "preview";
134
- if (version && configObj.config)
137
+ configObj.config.editor.agent === "vscode" ? "extension" : "preview";
138
+ }
139
+ if (version)
135
140
  configObj.config.editor.version = version;
136
- else if (configObj.config && configObj.config.editor.version === null) {
141
+ else if (configObj.config.editor.version === null) {
137
142
  console_1.default.debug("Config version not found, downloading default.");
138
143
  const resp = await fetch("https://raw.githubusercontent.com/learnpack/ide/master/package.json");
139
144
  const packageJSON = await resp.json();
140
- configObj.config.editor.version = packageJSON.version || "3.0.20";
141
- }
142
- if (configObj.config) {
143
- configObj.config.dirPath = "./" + confPath.base;
144
- configObj.config.exercisesPath = getExercisesPath(confPath.base) || "./";
145
+ configObj.config.editor.version = packageJSON.version || "3.1.22";
145
146
  }
147
+ configObj.config.dirPath = "./" + confPath.base;
148
+ configObj.config.exercisesPath = getExercisesPath(confPath.base) || "./";
146
149
  return {
147
150
  validLanguages: {},
148
151
  get: () => configObj,
@@ -168,6 +171,7 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
168
171
  return true;
169
172
  }
170
173
  console_1.default.info(`Language engine for ${language} not found, installing...`);
174
+ // Install the compiler in their new versions
171
175
  result = shell.exec(`learnpack plugins:install learnpack-${language}`, {
172
176
  silent: true,
173
177
  });
@@ -197,6 +201,8 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
197
201
  fs.unlinkSync(configObj.config.dirPath + "/app.tar.gz");
198
202
  if (fs.existsSync(configObj.config.dirPath + "/config.json"))
199
203
  fs.unlinkSync(configObj.config.dirPath + "/config.json");
204
+ if (fs.existsSync(configObj.config.dirPath + "/telemetry.json"))
205
+ fs.unlinkSync(configObj.config.dirPath + "/telemetry.json");
200
206
  if (fs.existsSync(configObj.config.dirPath + "/vscode_queue.json"))
201
207
  fs.unlinkSync(configObj.config.dirPath + "/vscode_queue.json");
202
208
  }
@@ -32,7 +32,7 @@ async function default_1(configObj, configManager, isTestingEnvironment = false)
32
32
  console_1.default.success(`Exercises are running 😃 Open your browser to start practicing!`);
33
33
  console_1.default.success(`\n Open the exercise on this link:`);
34
34
  console_1.default.log(` ${config === null || config === void 0 ? void 0 : config.publicUrl}`);
35
- if ((config === null || config === void 0 ? void 0 : config.editor.mode) === "standalone")
35
+ if ((config === null || config === void 0 ? void 0 : config.editor.mode) === "preview")
36
36
  cli_ux_1.default.open(`${config.publicUrl}`);
37
37
  }
38
38
  });
@@ -10,6 +10,7 @@ const fileQueue_1 = require("../../utils/fileQueue");
10
10
  const exercise_1 = require("../config/exercise");
11
11
  const session_1 = require("../../managers/session");
12
12
  const telemetry_1 = require("../telemetry");
13
+ const osOperations_1 = require("../../utils/osOperations");
13
14
  const withHandler = (func) => (req, res) => {
14
15
  try {
15
16
  func(req, res);
@@ -112,7 +113,7 @@ async function default_1(app, configObject, configManager) {
112
113
  }
113
114
  }));
114
115
  app.get("/exercise/:slug", withHandler((req, res) => {
115
- var _a, _b, _c, _d;
116
+ var _a, _b, _c, _d, _e;
116
117
  // no need to re-start exercise if it's already started
117
118
  if (configObject.currentExercise &&
118
119
  req.params.slug === configObject.currentExercise) {
@@ -127,7 +128,12 @@ async function default_1(app, configObject, configManager) {
127
128
  if (exercise.position) {
128
129
  telemetry_1.default.registerStepEvent(exercise.position, "open_step", {});
129
130
  }
130
- dispatcher.enqueue(dispatcher.events.START_EXERCISE, req.params.slug);
131
+ if (((_a = configObject.config) === null || _a === void 0 ? void 0 : _a.editor.agent) === "os") {
132
+ osOperations_1.eventManager.enqueue(dispatcher.events.START_EXERCISE, exercise);
133
+ }
134
+ else {
135
+ dispatcher.enqueue(dispatcher.events.START_EXERCISE, req.params.slug);
136
+ }
131
137
  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]));
132
138
  // if we are in incremental grading, the entry file can by dinamically detected
133
139
  // based on the changes the student is making during the exercise
@@ -159,23 +165,23 @@ async function default_1(app, configObject, configManager) {
159
165
  exercise.entry = detected === null || detected === void 0 ? void 0 : detected.entry;
160
166
  console_1.default.debug(`Exercise detected entry: ${detected === null || detected === void 0 ? void 0 : detected.entry} and language ${exercise.language}`);
161
167
  // exercises.graded and exercises.disableGrading deprecated.
162
- if (!exercise.graded || (config === null || config === void 0 ? void 0 : config.disableGrading) || ((_a = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _a === void 0 ? void 0 : _a.includes("test"))) {
168
+ if (!exercise.graded || (config === null || config === void 0 ? void 0 : config.disableGrading) || ((_b = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _b === void 0 ? void 0 : _b.includes("test"))) {
163
169
  socket_1.default.removeAllowed("test");
164
170
  }
165
171
  else {
166
172
  socket_1.default.addAllowed("test");
167
173
  }
168
- if (!exercise.entry || ((_b = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _b === void 0 ? void 0 : _b.includes("build"))) {
174
+ if (!exercise.entry || ((_c = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _c === void 0 ? void 0 : _c.includes("build"))) {
169
175
  socket_1.default.removeAllowed("build");
170
176
  }
171
177
  else {
172
178
  socket_1.default.addAllowed("build");
173
179
  }
174
180
  if (exercise.files.filter((f) => !f.name.toLowerCase().includes("readme.") &&
175
- !f.name.toLowerCase().includes("test.")).length === 0 || ((_c = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _c === void 0 ? void 0 : _c.includes("reset"))) {
181
+ !f.name.toLowerCase().includes("test.")).length === 0 || ((_d = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _d === void 0 ? void 0 : _d.includes("reset"))) {
176
182
  socket_1.default.removeAllowed("reset");
177
183
  }
178
- else if (!((_d = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _d === void 0 ? void 0 : _d.includes("reset"))) {
184
+ else if (!((_e = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _e === void 0 ? void 0 : _e.includes("reset"))) {
179
185
  socket_1.default.addAllowed("reset");
180
186
  }
181
187
  socket_1.default.log("ready");
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const api_1 = require("../utils/api");
4
+ const console_1 = require("../utils/console");
4
5
  const fs = require("fs");
5
6
  function createUUID() {
6
7
  return (Math.random().toString(36).slice(2, 10) +
@@ -14,7 +15,7 @@ const TelemetryManager = {
14
15
  urls: {},
15
16
  configPath: "",
16
17
  salute: message => {
17
- console.log(message);
18
+ console_1.default.info(message);
18
19
  },
19
20
  start: function (agent, steps, path, tutorialSlug) {
20
21
  this.configPath = path;
@@ -44,7 +45,7 @@ const TelemetryManager = {
44
45
  this.submit();
45
46
  })
46
47
  .catch(error => {
47
- console.error(error);
48
+ console_1.default.debug(error);
48
49
  });
49
50
  }
50
51
  },
@@ -95,6 +96,10 @@ const TelemetryManager = {
95
96
  if (data.stderr) {
96
97
  data.stderr = stringToBase64(data.stderr);
97
98
  }
99
+ if (Object.prototype.hasOwnProperty.call(data, "exitCode")) {
100
+ data.exit_code = data.exitCode;
101
+ data.exitCode = undefined;
102
+ }
98
103
  switch (event) {
99
104
  case "compile":
100
105
  if (!step.compilations) {
@@ -163,6 +168,7 @@ const TelemetryManager = {
163
168
  });
164
169
  },
165
170
  submit: async function () {
171
+ console_1.default.debug("Submitting telemetry...");
166
172
  if (!this.current)
167
173
  return Promise.resolve();
168
174
  const url = this.urls.batch;
@@ -1,17 +1,18 @@
1
1
  import { IExercise } from "./exercise-obj";
2
2
  import { TTelemetryUrls } from "../managers/telemetry";
3
3
  export declare type TGrading = "isolated" | "incremental" | "no-grading";
4
- export declare type TMode = "preview" | "standalone";
4
+ export declare type TMode = "preview" | "extension";
5
5
  export declare type TConfigAction = "test" | "build" | "tutorial" | "reset" | "generate";
6
6
  export declare type TConfigObjAttributes = "config" | "exercises" | "grading";
7
7
  export declare type TCompiler = "webpack" | "vanillajs" | "vue" | "react" | "css" | "html";
8
8
  export interface IConfigPath {
9
9
  base: string;
10
10
  }
11
+ declare type TAgent = "vscode" | "os";
11
12
  export interface IEditor {
12
13
  mode?: TMode;
13
14
  version: string;
14
- agent?: string;
15
+ agent?: TAgent;
15
16
  }
16
17
  export interface TEntries {
17
18
  python3?: string;
@@ -21,6 +22,7 @@ export interface TEntries {
21
22
  java?: string;
22
23
  }
23
24
  export interface IConfig {
25
+ os: string;
24
26
  port?: string;
25
27
  repository?: string;
26
28
  description?: string;
@@ -62,3 +64,4 @@ export interface IConfigObj {
62
64
  confPath?: IConfigPath;
63
65
  address?: string;
64
66
  }
67
+ export {};
@@ -1,5 +1,5 @@
1
- import BaseCommand from './BaseCommand';
2
- import { IConfigManager } from '../models/config-manager';
1
+ import BaseCommand from "./BaseCommand";
2
+ import { IConfigManager } from "../models/config-manager";
3
3
  export default class SessionCommand extends BaseCommand {
4
4
  session: any;
5
5
  configManager: IConfigManager | null;
@@ -24,8 +24,8 @@ class SessionCommand extends BaseCommand_1.default {
24
24
  }
25
25
  else {
26
26
  if (_private)
27
- throw errors_1.AuthError('You need to log in, run the following command to continue: $ learnpack login');
28
- console_1.default.debug('No active session available', _private);
27
+ throw errors_1.AuthError("You need to log in, run the following command to continue: $ learnpack login");
28
+ console_1.default.debug("No active session available", _private);
29
29
  }
30
30
  }
31
31
  catch (error) {
@@ -36,12 +36,8 @@ class SessionCommand extends BaseCommand_1.default {
36
36
  this.configManager = await index_1.default(flags);
37
37
  }
38
38
  async catch(err) {
39
- console_1.default.debug('COMMAND CATCH', err);
39
+ console_1.default.debug("COMMAND CATCH", err);
40
40
  throw err;
41
41
  }
42
42
  }
43
43
  exports.default = SessionCommand;
44
- // SessionCommand.description = `Describe the command here
45
- // ...
46
- // Extra documentation goes here
47
- // `
package/lib/utils/api.js CHANGED
@@ -214,6 +214,7 @@ const sendBatchTelemetry = async function (url, body) {
214
214
  body: JSON.stringify(body),
215
215
  }, false)
216
216
  .then(response => {
217
+ console_1.default.debug("Telemetry sent successfully");
217
218
  return response.text();
218
219
  })
219
220
  .catch(error => {
@@ -39,7 +39,7 @@ const checkForEmptySpaces = (str) => {
39
39
  return isEmpty;
40
40
  };
41
41
  const checkLearnpackClean = (configObj, errors) => {
42
- var _a, _b, _c, _d, _e, _f, _g, _h;
42
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
43
43
  if ((((_a = configObj.config) === null || _a === void 0 ? void 0 : _a.outputPath) &&
44
44
  fs.existsSync((_b = configObj.config) === null || _b === void 0 ? void 0 : _b.outputPath)) ||
45
45
  fs.existsSync(`${(_c = configObj.config) === null || _c === void 0 ? void 0 : _c.dirPath}/_app`) ||
@@ -47,7 +47,8 @@ const checkLearnpackClean = (configObj, errors) => {
47
47
  fs.existsSync(`${(_e = configObj.config) === null || _e === void 0 ? void 0 : _e.dirPath}/resets`) ||
48
48
  fs.existsSync(`${(_f = configObj.config) === null || _f === void 0 ? void 0 : _f.dirPath}/app.tar.gz`) ||
49
49
  fs.existsSync(`${(_g = configObj.config) === null || _g === void 0 ? void 0 : _g.dirPath}/config.json`) ||
50
- fs.existsSync(`${(_h = configObj.config) === null || _h === void 0 ? void 0 : _h.dirPath}/vscode_queue.json`)) {
50
+ fs.existsSync(`${(_h = configObj.config) === null || _h === void 0 ? void 0 : _h.dirPath}/vscode_queue.json`) ||
51
+ fs.existsSync(`${(_j = configObj.config) === null || _j === void 0 ? void 0 : _j.dirPath}/telemetry.json`)) {
51
52
  errors.push({
52
53
  exercise: undefined,
53
54
  msg: "You have to run learnpack clean command",
@@ -0,0 +1,4 @@
1
+ declare const eventManager: {
2
+ enqueue: (event: string, data: any) => void;
3
+ };
4
+ export { eventManager };
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.eventManager = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const os = require("os");
6
+ const path = require("path");
7
+ const cli_ux_1 = require("cli-ux");
8
+ const openFile = (filePath) => {
9
+ const fullPath = path.join(process.cwd(), filePath);
10
+ let command;
11
+ switch (os.platform()) {
12
+ case "darwin": // macOS
13
+ command = `open ${fullPath}`;
14
+ break;
15
+ case "win32": // Windows
16
+ command = `start ${fullPath}`;
17
+ break;
18
+ case "linux": // Linux
19
+ command = `xdg-open ${fullPath}`;
20
+ break;
21
+ default:
22
+ throw new Error("Unsupported OS");
23
+ }
24
+ child_process_1.exec(command, (error, stdout, stderr) => {
25
+ if (error) {
26
+ console.error(`exec error: ${error}`);
27
+ }
28
+ });
29
+ };
30
+ const eventManager = {
31
+ enqueue: (event, data) => {
32
+ if (event === "start_exercise") {
33
+ const exercise = data;
34
+ const filesToOpen = exercise.files
35
+ .filter((file) => !file.hidden)
36
+ .map((file) => {
37
+ return file.path.replace("\\", "/");
38
+ });
39
+ for (const file of filesToOpen) {
40
+ openFile(file);
41
+ }
42
+ }
43
+ if (event === "open_files") {
44
+ const files = data;
45
+ for (const file of files) {
46
+ const correctedPath = file.replace("\\", "/");
47
+ openFile(correctedPath);
48
+ }
49
+ }
50
+ if (event === "open_window") {
51
+ const url = data.url;
52
+ cli_ux_1.default.open(url);
53
+ }
54
+ },
55
+ };
56
+ exports.eventManager = eventManager;
@@ -1 +1 @@
1
- {"version":"2.1.36","commands":{"audit":{"id":"audit","description":"learnpack audit is the command in charge of creating an auditory of the repository\n...\nlearnpack audit checks for the following information in a repository:\n 1. The configuration object has slug, repository and description. (Error)\n 2. The command learnpack clean has been run. (Error)\n 3. If a markdown or test file doesn't have any content. (Error)\n 4. The links are accessing to valid servers. (Error)\n 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)\n 6. The external images are working (If they are pointing to a valid server). (Error)\n 7. The exercises directory names are valid. (Error)\n 8. If an exercise doesn't have a README file. (Error)\n 9. The exercises array (Of the config file) has content. (Error)\n 10. The exercses have the same translations. (Warning)\n 11. The .gitignore file exists. (Warning)\n 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"clean":{"id":"clean","description":"Clean the configuration object\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"download":{"id":"download","description":"Describe the command here\n...\nExtra documentation goes here\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"init":{"id":"init","description":"Create a new learning package: Book, Tutorial or Exercise","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"login":{"id":"login","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"logout":{"id":"logout","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"publish":{"id":"publish","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"disableGrading":{"name":"disableGrading","type":"boolean","char":"D","description":"disble grading functionality","allowNo":false},"watch":{"name":"watch","type":"boolean","char":"w","description":"Watch for file changes","allowNo":false},"editor":{"name":"editor","type":"option","char":"e","description":"[standalone, gitpod]","options":["standalone","gitpod"]},"version":{"name":"version","type":"option","char":"v","description":"E.g: 1.0.1"},"grading":{"name":"grading","type":"option","char":"g","description":"[isolated, incremental]","options":["isolated","incremental"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"test":{"id":"test","description":"Test exercises","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]}}}
1
+ {"version":"2.1.38","commands":{"audit":{"id":"audit","description":"learnpack audit is the command in charge of creating an auditory of the repository\n...\nlearnpack audit checks for the following information in a repository:\n 1. The configuration object has slug, repository and description. (Error)\n 2. The command learnpack clean has been run. (Error)\n 3. If a markdown or test file doesn't have any content. (Error)\n 4. The links are accessing to valid servers. (Error)\n 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)\n 6. The external images are working (If they are pointing to a valid server). (Error)\n 7. The exercises directory names are valid. (Error)\n 8. If an exercise doesn't have a README file. (Error)\n 9. The exercises array (Of the config file) has content. (Error)\n 10. The exercses have the same translations. (Warning)\n 11. The .gitignore file exists. (Warning)\n 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"clean":{"id":"clean","description":"Clean the configuration object\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"download":{"id":"download","description":"Describe the command here\n...\nExtra documentation goes here\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"init":{"id":"init","description":"Create a new learning package: Book, Tutorial or Exercise","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"login":{"id":"login","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"logout":{"id":"logout","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"publish":{"id":"publish","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"disableGrading":{"name":"disableGrading","type":"boolean","char":"D","description":"disble grading functionality","allowNo":false},"watch":{"name":"watch","type":"boolean","char":"w","description":"Watch for file changes","allowNo":false},"editor":{"name":"editor","type":"option","char":"e","description":"[preview, extension]","options":["extension","preview"]},"version":{"name":"version","type":"option","char":"v","description":"E.g: 1.0.1"},"grading":{"name":"grading","type":"option","char":"g","description":"[isolated, incremental]","options":["isolated","incremental"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"test":{"id":"test","description":"Test exercises","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]}}}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@learnpack/learnpack",
3
3
  "description": "Create, sell or download and take learning amazing learning packages",
4
- "version": "2.1.36",
4
+ "version": "2.1.38",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "bin": {
7
7
  "learnpack": "bin/run"
@@ -16,6 +16,7 @@ import { prioritizeHTMLFile } from "../utils/misc"
16
16
 
17
17
  import { IGitpodData } from "../models/gitpod-data"
18
18
  import { IExercise, IExerciseData } from "../models/exercise-obj"
19
+ import { eventManager } from "../utils/osOperations"
19
20
 
20
21
  export default class StartCommand extends SessionCommand {
21
22
  static description = "Runs a small server with all the exercise instructions"
@@ -37,8 +38,8 @@ export default class StartCommand extends SessionCommand {
37
38
  }),
38
39
  editor: flags.string({
39
40
  char: "e",
40
- description: "[standalone, gitpod]",
41
- options: ["standalone", "gitpod"],
41
+ description: "[preview, extension]",
42
+ options: ["extension", "preview"],
42
43
  }),
43
44
  version: flags.string({
44
45
  char: "v",
@@ -110,7 +111,11 @@ export default class StartCommand extends SessionCommand {
110
111
  this.configManager,
111
112
  process.env.NODE_ENV === "test"
112
113
  )
114
+ server.setMaxListeners(20)
113
115
 
116
+ // console.log(config, "this is the f*!shell.which("code")!shell.which("code")cking config");
117
+
118
+ // I should call a method to get the EventListener
114
119
  const dispatcher = queue.dispatcher({
115
120
  create: true,
116
121
  path: `${config.dirPath}/vscode_queue.json`,
@@ -121,7 +126,6 @@ export default class StartCommand extends SessionCommand {
121
126
  const path = configObject.config?.dirPath || ""
122
127
  const tutorialSlug = configObject.config?.slug || ""
123
128
 
124
- Console.info("Starting Telemetry with slug...", tutorialSlug)
125
129
  const steps = configObject.exercises.map(
126
130
  (e: IExercise, index): TStep => ({
127
131
  slug: e.slug,
@@ -149,13 +153,22 @@ export default class StartCommand extends SessionCommand {
149
153
 
150
154
  const files = prioritizeHTMLFile(data.files)
151
155
 
152
- dispatcher.enqueue(dispatcher.events.OPEN_FILES, files)
156
+ if (config.editor.agent === "os") {
157
+ eventManager.enqueue(dispatcher.events.OPEN_FILES, files)
158
+ } else {
159
+ dispatcher.enqueue(dispatcher.events.OPEN_FILES, files)
160
+ }
161
+
153
162
  socket.ready("Ready to compile...")
154
163
  })
155
164
 
156
165
  socket.on("open_window", (data: IGitpodData) => {
157
166
  Console.debug("Opening window: ", data)
158
- dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data)
167
+ // if (config.editor.agent === "os") {
168
+ // } else {
169
+ // dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data);
170
+ // }
171
+ eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data)
159
172
  socket.ready("Ready to compile...")
160
173
  })
161
174
 
@@ -176,11 +189,6 @@ export default class StartCommand extends SessionCommand {
176
189
  setTimeout(() => socket.ready("Ready to compile..."), 2000)
177
190
  }
178
191
  })
179
- // socket.on("preview", (data) => {
180
- // Console.debug("Preview triggered, removing the 'preview' action ")
181
- // socket.removeAllowed("preview")
182
- // socket.log('ready',['Ready to compile...'])
183
- // })
184
192
 
185
193
  socket.on("build", async (data: IExerciseData) => {
186
194
  const exercise = this.configManager?.getExercise(data.exerciseSlug)
@@ -257,15 +265,11 @@ export default class StartCommand extends SessionCommand {
257
265
  })
258
266
 
259
267
  const terminate = () => {
260
- Console.debug("Terminating Learnpack...")
261
-
268
+ Console.error("Terminating Learnpack...")
262
269
  TelemetryManager.submit()
263
-
264
- server.terminate(() => {
265
- this.configManager?.noCurrentExercise()
266
- dispatcher.enqueue(dispatcher.events.END)
267
- process.exit()
268
- })
270
+ this.configManager?.noCurrentExercise()
271
+ dispatcher.enqueue(dispatcher.events.END)
272
+ process.exit()
269
273
  }
270
274
 
271
275
  server.on("close", terminate)
@@ -1,10 +1,14 @@
1
+ import * as os from "os"
1
2
  export default {
2
3
  config: {
3
4
  port: 3000,
5
+ os: (function () {
6
+ return os.platform()
7
+ })(),
4
8
  editor: {
5
- mode: null, // [standalone, preview]
6
- agent: null, // [vscode, gitpod, localhost, codespaces] TODO: We need to check if we are in codespaces
7
- version: null,
9
+ mode: null, // [extension, preview]
10
+ agent: null, // [vscode, null]
11
+ version: null, // By default downloads the latest version
8
12
  },
9
13
  dirPath: "./.learn",
10
14
  configPath: "./learn.json",
@@ -121,18 +121,20 @@ lang = null
121
121
  fs.mkdirSync(`${config?.dirPath}/resets`)
122
122
  if (!fs.existsSync(`${config?.dirPath}/resets/` + this.slug)) {
123
123
  fs.mkdirSync(`${config?.dirPath}/resets/` + this.slug)
124
- for (const _file of this.files) {
125
- const fileContent = fs.readFileSync(_file.path)
126
- if (
127
- !fs.existsSync(
128
- `${config?.dirPath}/resets/${this.slug}/${_file.name}`
129
- )
130
- ) {
131
- fs.writeFileSync(
132
- `${config?.dirPath}/resets/${this.slug}/${_file.name}`,
133
- fileContent
134
- )
135
- }
124
+ }
125
+
126
+ for (const _file of this.files) {
127
+ const stats = fs.statSync(_file.path)
128
+ if (stats.isDirectory() || _file.hidden)
129
+ continue
130
+ const fileContent = fs.readFileSync(_file.path)
131
+ if (
132
+ !fs.existsSync(`${config?.dirPath}/resets/${this.slug}/${_file.name}`)
133
+ ) {
134
+ fs.writeFileSync(
135
+ `${config?.dirPath}/resets/${this.slug}/${_file.name}`,
136
+ fileContent
137
+ )
136
138
  }
137
139
  }
138
140
 
@@ -29,6 +29,12 @@ const chalk = require("chalk");
29
29
 
30
30
  /* exercise folder name standard */
31
31
 
32
+ /**
33
+ * Retrieves the configuration path for the learnpack package.
34
+ *
35
+ * @returns An object containing the configuration file path and the base directory.
36
+ * @throws NotFoundError if the learn.json file is not found in the current folder.
37
+ */
32
38
  const getConfigPath = () => {
33
39
  const possibleFileNames = ["learn.json", ".learn/learn.json"]
34
40
  const config = possibleFileNames.find(file => fs.existsSync(file)) || null
@@ -58,7 +64,7 @@ const getGitpodAddress = () => {
58
64
  }
59
65
 
60
66
  const getCodespacesNamespace = () => {
61
- // https://orange-rotary-phone-wxpg49q5gcg4rp-3000.app.github.dev
67
+ // Example: https://orange-rotary-phone-wxpg49q5gcg4rp-3000.app.github.dev
62
68
 
63
69
  const codespace_name = shell
64
70
  .exec("echo $CODESPACE_NAME", { silent: true })
@@ -84,13 +90,12 @@ export default async ({
84
90
  version,
85
91
  }: IConfigManagerAttributes): Promise<IConfigManager> => {
86
92
  const confPath = getConfigPath()
87
-
88
93
  Console.debug("This is the config path: ", confPath)
89
94
 
90
95
  let configObj: IConfigObj = {}
91
96
 
92
97
  if (confPath) {
93
- const bcContent = fs.readFileSync(confPath.config)
98
+ const learnJsonContent = fs.readFileSync(confPath.config)
94
99
  let hiddenBcContent = {}
95
100
  if (fs.existsSync(confPath.base + "/config.json")) {
96
101
  hiddenBcContent = fs.readFileSync(confPath.base + "/config.json")
@@ -101,7 +106,8 @@ export default async ({
101
106
  )
102
107
  }
103
108
 
104
- const jsonConfig = JSON.parse(`${bcContent}`)
109
+ const jsonConfig = JSON.parse(`${learnJsonContent}`)
110
+
105
111
  if (!jsonConfig)
106
112
  throw new Error(`Invalid ${confPath.config} syntax: Unable to parse.`)
107
113
 
@@ -121,7 +127,7 @@ export default async ({
121
127
  })
122
128
  } else {
123
129
  throw ValidationError(
124
- "No learn.json file has been found, make sure you are in the folder"
130
+ "No learn.json file has been found, make sure you are in the correct directory. To start a new LearnPack package run: $ learnpack init my-course-name"
125
131
  )
126
132
  }
127
133
 
@@ -131,10 +137,10 @@ export default async ({
131
137
  configPath: confPath.config,
132
138
  },
133
139
  })
140
+ if (!configObj.config)
141
+ throw InternalError("Config object not found")
134
142
 
135
- if (configObj.config) {
136
- configObj.config.outputPath = confPath.base + "/dist"
137
- }
143
+ configObj.config.outputPath = confPath.base + "/dist"
138
144
 
139
145
  Console.debug("This is your configuration object: ", {
140
146
  ...configObj,
@@ -145,7 +151,6 @@ export default async ({
145
151
 
146
152
  // auto detect agent (if possible)
147
153
  const codespaces_workspace = getCodespacesNamespace()
148
-
149
154
  Console.debug("This is the codespace namespace: ", codespaces_workspace)
150
155
 
151
156
  Console.debug(
@@ -155,55 +160,52 @@ export default async ({
155
160
 
156
161
  if (shell.which("gp") && configObj && configObj.config) {
157
162
  Console.debug("Gitpod detected")
158
- configObj.config.editor.agent = "vscode"
159
163
  configObj.address = getGitpodAddress()
160
164
  configObj.config.publicUrl = `https://${
161
165
  configObj.config.port
162
166
  }-${configObj.address?.slice(8)}`
163
167
  } else if (configObj.config && Boolean(codespaces_workspace)) {
164
168
  Console.debug("Codespaces detected: ", codespaces_workspace)
165
- configObj.config.editor.agent = "vscode"
166
-
167
169
  configObj.address = `https://${codespaces_workspace}.github.dev`
168
170
  configObj.config.publicUrl = `https://${codespaces_workspace}-${configObj.config.port}.app.github.dev`
169
171
  // TODO: Why is needed to have an agent?
170
- // } else if (configObj.config && !configObj.config.editor.agent) {
171
- } else if (configObj.config) {
172
- Console.debug("Localhost detected")
173
- configObj.config.editor.agent = "localhost"
172
+ } else {
173
+ Console.debug("Neither Gitpod nor Codespaces detected, using localhost.")
174
174
  configObj.address = `http://localhost:${configObj.config.port}`
175
175
  configObj.config.publicUrl = `http://localhost:${configObj.config.port}`
176
176
  }
177
177
 
178
- Console.debug("This is the agent later", configObj?.config?.editor.agent)
178
+ if (!configObj.config.editor.agent) {
179
+ configObj.config.editor.agent = shell.which("code") ? "vscode" : "os"
180
+ }
181
+
182
+ // TODO: We need to set the correct EventListener if the agent is not vscode
179
183
 
180
184
  if (configObj.config && !configObj.config.publicUrl)
181
185
  configObj.config.publicUrl = `${configObj.address}:${configObj.config.port}`
182
186
 
183
- // Assign default editor mode if not set already
184
- if (configObj.config && !mode) {
187
+ if (configObj.config && !configObj.config.editor.mode && mode) {
185
188
  configObj.config.editor.mode = mode as TMode
186
189
  }
187
190
 
188
- if (configObj.config && !configObj.config.editor.mode)
191
+ if (!configObj.config.editor.mode) {
189
192
  configObj.config.editor.mode =
190
- configObj.config.editor.agent === "localhost" ? "standalone" : "preview"
193
+ configObj.config.editor.agent === "vscode" ? "extension" : "preview"
194
+ }
191
195
 
192
- if (version && configObj.config)
196
+ if (version)
193
197
  configObj.config.editor.version = version
194
- else if (configObj.config && configObj.config.editor.version === null) {
198
+ else if (configObj.config.editor.version === null) {
195
199
  Console.debug("Config version not found, downloading default.")
196
200
  const resp = await fetch(
197
201
  "https://raw.githubusercontent.com/learnpack/ide/master/package.json"
198
202
  )
199
203
  const packageJSON = await resp.json()
200
- configObj.config.editor.version = packageJSON.version || "3.0.20"
204
+ configObj.config.editor.version = packageJSON.version || "3.1.22"
201
205
  }
202
206
 
203
- if (configObj.config) {
204
- configObj.config.dirPath = "./" + confPath.base
205
- configObj.config.exercisesPath = getExercisesPath(confPath.base) || "./"
206
- }
207
+ configObj.config.dirPath = "./" + confPath.base
208
+ configObj.config.exercisesPath = getExercisesPath(confPath.base) || "./"
207
209
 
208
210
  return {
209
211
  validLanguages: {},
@@ -237,6 +239,7 @@ return true
237
239
  }
238
240
 
239
241
  Console.info(`Language engine for ${language} not found, installing...`)
242
+ // Install the compiler in their new versions
240
243
  result = shell.exec(`learnpack plugins:install learnpack-${language}`, {
241
244
  silent: true,
242
245
  })
@@ -278,6 +281,9 @@ return true
278
281
  if (fs.existsSync(configObj.config.dirPath + "/config.json"))
279
282
  fs.unlinkSync(configObj.config.dirPath + "/config.json")
280
283
 
284
+ if (fs.existsSync(configObj.config.dirPath + "/telemetry.json"))
285
+ fs.unlinkSync(configObj.config.dirPath + "/telemetry.json")
286
+
281
287
  if (fs.existsSync(configObj.config.dirPath + "/vscode_queue.json"))
282
288
  fs.unlinkSync(configObj.config.dirPath + "/vscode_queue.json")
283
289
  }
@@ -46,7 +46,8 @@ server = require("http").Server(app)
46
46
  )
47
47
  Console.success(`\n Open the exercise on this link:`)
48
48
  Console.log(` ${config?.publicUrl}`)
49
- if (config?.editor.mode === "standalone")
49
+
50
+ if (config?.editor.mode === "preview")
50
51
  cli.open(`${config.publicUrl}`)
51
52
  }
52
53
  })
@@ -67,7 +68,6 @@ cli.open(`${config.publicUrl}`)
67
68
  server.terminate = (callback: void) => {
68
69
  for (const socket of sockets) {
69
70
  socket.destroy()
70
-
71
71
  sockets.delete(socket)
72
72
  }
73
73
 
@@ -12,6 +12,7 @@ import { IConfigManager } from "../../models/config-manager"
12
12
  import { IExercise } from "../../models/exercise-obj"
13
13
  import SessionManager from "../../managers/session"
14
14
  import TelemetryManager from "../telemetry"
15
+ import { eventManager } from "../../utils/osOperations"
15
16
 
16
17
  const withHandler =
17
18
  (func: (req: express.Request, res: express.Response) => void) =>
@@ -194,7 +195,11 @@ export default async function (
194
195
  TelemetryManager.registerStepEvent(exercise.position, "open_step", {})
195
196
  }
196
197
 
197
- dispatcher.enqueue(dispatcher.events.START_EXERCISE, req.params.slug)
198
+ if (configObject.config?.editor.agent === "os") {
199
+ eventManager.enqueue(dispatcher.events.START_EXERCISE, exercise)
200
+ } else {
201
+ dispatcher.enqueue(dispatcher.events.START_EXERCISE, req.params.slug)
202
+ }
198
203
 
199
204
  type TEntry = "python3" | "html" | "node" | "react" | "java";
200
205
 
@@ -279,7 +284,6 @@ export default async function (
279
284
  }
280
285
 
281
286
  socket.log("ready")
282
-
283
287
  res.json(exercise)
284
288
  })
285
289
  )
@@ -1,5 +1,7 @@
1
1
  import { IFile } from "../models/file"
2
2
  import API from "../utils/api"
3
+ import Console from "../utils/console"
4
+
3
5
  const fs = require("fs")
4
6
 
5
7
  function createUUID(): string {
@@ -112,7 +114,7 @@ const TelemetryManager: ITelemetryManager = {
112
114
  urls: {},
113
115
  configPath: "",
114
116
  salute: message => {
115
- console.log(message)
117
+ Console.info(message)
116
118
  },
117
119
 
118
120
  start: function (agent, steps, path, tutorialSlug) {
@@ -143,7 +145,7 @@ const TelemetryManager: ITelemetryManager = {
143
145
  this.submit()
144
146
  })
145
147
  .catch(error => {
146
- console.error(error)
148
+ Console.debug(error)
147
149
  })
148
150
  }
149
151
  },
@@ -208,6 +210,11 @@ const TelemetryManager: ITelemetryManager = {
208
210
  data.stderr = stringToBase64(data.stderr)
209
211
  }
210
212
 
213
+ if (Object.prototype.hasOwnProperty.call(data, "exitCode")) {
214
+ data.exit_code = data.exitCode
215
+ data.exitCode = undefined
216
+ }
217
+
211
218
  switch (event) {
212
219
  case "compile":
213
220
  if (!step.compilations) {
@@ -288,6 +295,8 @@ const TelemetryManager: ITelemetryManager = {
288
295
  })
289
296
  },
290
297
  submit: async function () {
298
+ Console.debug("Submitting telemetry...")
299
+
291
300
  if (!this.current)
292
301
  return Promise.resolve()
293
302
  const url = this.urls.batch
@@ -3,7 +3,7 @@ import { TTelemetryUrls } from "../managers/telemetry"
3
3
 
4
4
  export type TGrading = "isolated" | "incremental" | "no-grading";
5
5
 
6
- export type TMode = "preview" | "standalone";
6
+ export type TMode = "preview" | "extension";
7
7
 
8
8
  export type TConfigAction =
9
9
  | "test"
@@ -26,10 +26,12 @@ export interface IConfigPath {
26
26
  base: string;
27
27
  }
28
28
 
29
+ type TAgent = "vscode" | "os";
30
+
29
31
  export interface IEditor {
30
32
  mode?: TMode;
31
33
  version: string;
32
- agent?: string;
34
+ agent?: TAgent;
33
35
  }
34
36
 
35
37
  export interface TEntries {
@@ -41,6 +43,7 @@ export interface TEntries {
41
43
  }
42
44
 
43
45
  export interface IConfig {
46
+ os: string;
44
47
  port?: string;
45
48
  repository?: string;
46
49
  description?: string;
@@ -1,10 +1,10 @@
1
1
  // import { flags } from "@oclif/command";
2
- import BaseCommand from './BaseCommand'
3
- import Console from './console'
4
- import SessionManager from '../managers/session'
5
- import configManager from '../managers/config/index'
6
- import {AuthError} from './errors'
7
- import {IConfigManager} from '../models/config-manager'
2
+ import BaseCommand from "./BaseCommand"
3
+ import Console from "./console"
4
+ import SessionManager from "../managers/session"
5
+ import configManager from "../managers/config/index"
6
+ import { AuthError } from "./errors"
7
+ import { IConfigManager } from "../models/config-manager"
8
8
 
9
9
  export default class SessionCommand extends BaseCommand {
10
10
  session: any = null
@@ -23,9 +23,9 @@ export default class SessionCommand extends BaseCommand {
23
23
  } else {
24
24
  if (_private)
25
25
  throw AuthError(
26
- 'You need to log in, run the following command to continue: $ learnpack login',
26
+ "You need to log in, run the following command to continue: $ learnpack login"
27
27
  )
28
- Console.debug('No active session available', _private)
28
+ Console.debug("No active session available", _private)
29
29
  }
30
30
  } catch (error) {
31
31
  Console.error((error as TypeError).message)
@@ -37,12 +37,7 @@ export default class SessionCommand extends BaseCommand {
37
37
  }
38
38
 
39
39
  async catch(err: any) {
40
- Console.debug('COMMAND CATCH', err)
40
+ Console.debug("COMMAND CATCH", err)
41
41
  throw err
42
42
  }
43
43
  }
44
-
45
- // SessionCommand.description = `Describe the command here
46
- // ...
47
- // Extra documentation goes here
48
- // `
package/src/utils/api.ts CHANGED
@@ -252,6 +252,7 @@ const sendBatchTelemetry = async function (url: string, body: object) {
252
252
  false
253
253
  )
254
254
  .then(response => {
255
+ Console.debug("Telemetry sent successfully")
255
256
  return response.text()
256
257
  })
257
258
  .catch(error => {
@@ -62,7 +62,8 @@ const checkLearnpackClean = (configObj: IConfigObj, errors: IAuditErrors[]) => {
62
62
  fs.existsSync(`${configObj.config?.dirPath}/resets`) ||
63
63
  fs.existsSync(`${configObj.config?.dirPath}/app.tar.gz`) ||
64
64
  fs.existsSync(`${configObj.config?.dirPath}/config.json`) ||
65
- fs.existsSync(`${configObj.config?.dirPath}/vscode_queue.json`)
65
+ fs.existsSync(`${configObj.config?.dirPath}/vscode_queue.json`) ||
66
+ fs.existsSync(`${configObj.config?.dirPath}/telemetry.json`)
66
67
  ) {
67
68
  errors.push({
68
69
  exercise: undefined,
@@ -0,0 +1,63 @@
1
+ import { exec } from "child_process"
2
+ import * as os from "os"
3
+ import * as path from "path"
4
+ import cli from "cli-ux"
5
+
6
+ const openFile = (filePath: string): void => {
7
+ const fullPath = path.join(process.cwd(), filePath)
8
+ let command: string
9
+
10
+ switch (os.platform()) {
11
+ case "darwin": // macOS
12
+ command = `open ${fullPath}`
13
+ break
14
+ case "win32": // Windows
15
+ command = `start ${fullPath}`
16
+ break
17
+ case "linux": // Linux
18
+ command = `xdg-open ${fullPath}`
19
+ break
20
+ default:
21
+ throw new Error("Unsupported OS")
22
+ }
23
+
24
+ exec(command, (error, stdout, stderr) => {
25
+ if (error) {
26
+ console.error(`exec error: ${error}`)
27
+
28
+ }
29
+ })
30
+ }
31
+
32
+ const eventManager = {
33
+ enqueue: (event: string, data: any) => {
34
+ if (event === "start_exercise") {
35
+ const exercise = data
36
+ const filesToOpen = exercise.files
37
+ .filter((file: any) => !file.hidden)
38
+ .map((file: any) => {
39
+ return file.path.replace("\\", "/")
40
+ })
41
+
42
+ for (const file of filesToOpen) {
43
+ openFile(file)
44
+ }
45
+ }
46
+
47
+ if (event === "open_files") {
48
+ const files = data
49
+
50
+ for (const file of files) {
51
+ const correctedPath = file.replace("\\", "/")
52
+ openFile(correctedPath)
53
+ }
54
+ }
55
+
56
+ if (event === "open_window") {
57
+ const url = data.url
58
+ cli.open(url)
59
+ }
60
+ },
61
+ }
62
+
63
+ export { eventManager }