pinokiod 3.40.0 → 3.42.0

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.
Files changed (82) hide show
  1. package/kernel/api/browser/index.js +3 -1
  2. package/kernel/api/cloudflare/index.js +3 -3
  3. package/kernel/api/index.js +187 -51
  4. package/kernel/api/loading/index.js +15 -0
  5. package/kernel/api/process/index.js +7 -0
  6. package/kernel/api/shell/index.js +0 -2
  7. package/kernel/bin/browserless.js +22 -0
  8. package/kernel/bin/caddy.js +36 -4
  9. package/kernel/bin/index.js +4 -1
  10. package/kernel/bin/setup.js +38 -5
  11. package/kernel/connect/backend.js +110 -0
  12. package/kernel/connect/config.js +171 -0
  13. package/kernel/connect/index.js +18 -7
  14. package/kernel/connect/providers/huggingface/index.js +98 -0
  15. package/kernel/connect/providers/x/index.js +0 -1
  16. package/kernel/environment.js +91 -19
  17. package/kernel/git.js +46 -3
  18. package/kernel/index.js +119 -39
  19. package/kernel/peer.js +40 -5
  20. package/kernel/plugin.js +3 -2
  21. package/kernel/procs.js +27 -20
  22. package/kernel/prototype.js +30 -16
  23. package/kernel/router/common.js +1 -1
  24. package/kernel/router/connector.js +1 -3
  25. package/kernel/router/index.js +38 -4
  26. package/kernel/router/localhost_home_router.js +5 -1
  27. package/kernel/router/localhost_port_router.js +27 -1
  28. package/kernel/router/localhost_static_router.js +93 -0
  29. package/kernel/router/localhost_variable_router.js +14 -9
  30. package/kernel/router/peer_peer_router.js +3 -0
  31. package/kernel/router/peer_static_router.js +43 -0
  32. package/kernel/router/peer_variable_router.js +15 -14
  33. package/kernel/router/processor.js +26 -1
  34. package/kernel/router/rewriter.js +59 -0
  35. package/kernel/scripts/git/commit +11 -1
  36. package/kernel/shell.js +8 -3
  37. package/kernel/util.js +65 -6
  38. package/package.json +2 -1
  39. package/server/index.js +1048 -970
  40. package/server/public/common.js +382 -1
  41. package/server/public/fscreator.js +0 -1
  42. package/server/public/loading.js +17 -0
  43. package/server/public/notifyinput.js +0 -1
  44. package/server/public/opener.js +4 -2
  45. package/server/public/style.css +310 -11
  46. package/server/socket.js +7 -1
  47. package/server/views/app.ejs +1747 -351
  48. package/server/views/columns.ejs +338 -0
  49. package/server/views/connect/huggingface.ejs +353 -0
  50. package/server/views/connect/index.ejs +410 -0
  51. package/server/views/connect/x.ejs +43 -9
  52. package/server/views/connect.ejs +709 -49
  53. package/server/views/container.ejs +357 -0
  54. package/server/views/d.ejs +251 -62
  55. package/server/views/download.ejs +54 -10
  56. package/server/views/editor.ejs +11 -0
  57. package/server/views/explore.ejs +40 -15
  58. package/server/views/file_explorer.ejs +25 -246
  59. package/server/views/form.ejs +44 -1
  60. package/server/views/frame.ejs +39 -1
  61. package/server/views/github.ejs +48 -11
  62. package/server/views/help.ejs +48 -7
  63. package/server/views/index.ejs +119 -58
  64. package/server/views/index2.ejs +3 -4
  65. package/server/views/init/index.ejs +651 -197
  66. package/server/views/install.ejs +1 -1
  67. package/server/views/mini.ejs +47 -18
  68. package/server/views/net.ejs +199 -67
  69. package/server/views/network.ejs +229 -93
  70. package/server/views/network2.ejs +3 -4
  71. package/server/views/old_network.ejs +3 -3
  72. package/server/views/prototype/index.ejs +48 -11
  73. package/server/views/review.ejs +1005 -0
  74. package/server/views/rows.ejs +341 -0
  75. package/server/views/screenshots.ejs +1020 -0
  76. package/server/views/settings.ejs +160 -23
  77. package/server/views/setup.ejs +49 -7
  78. package/server/views/setup_home.ejs +43 -10
  79. package/server/views/shell.ejs +7 -1
  80. package/server/views/start.ejs +14 -9
  81. package/server/views/terminal.ejs +13 -2
  82. package/server/views/tools.ejs +1015 -0
@@ -1,6 +1,8 @@
1
+ const Util = require('../../util')
1
2
  class Browser {
2
3
  async open(req, ondata, kernel) {
3
- ondata(req.params, "browser.open")
4
+ Util.openURL(req.params.uri)
5
+ //ondata(req.params, "browser.open")
4
6
  }
5
7
  async close(req, ondata, kernel) {
6
8
  ondata(req.params, "browser.close")
@@ -19,9 +19,9 @@ class C {
19
19
  let pipe_uri
20
20
  if (req.params.passcode) {
21
21
  // 1. start a pipe server => needed for authentication
22
- const api_path = Util.api_path(req.parent.path, kernel)
23
- const pinokio_path = path.resolve(api_path, "pinokio.js")
24
- const config = (await kernel.loader.load(pinokio_path)).resolved
22
+ const api_name = Util.api_name(req.parent.path, kernel)
23
+ const launcher = await kernel.api.launcher(api_name)
24
+ const config = launcher.script
25
25
  pipe_uri = await kernel.pipe.start(req.params.uri, req.parent.path, req.params.passcode, config)
26
26
  } else {
27
27
  // 2. if no passcode required, no need for a pipe server just use the original
@@ -28,6 +28,105 @@ class Api {
28
28
  this.child_procs = {}
29
29
  this.lproxy = new Lproxy()
30
30
  }
31
+ async launcher_path(name) {
32
+ let root_path = this.kernel.path("api", name)
33
+ let primary_path = path.resolve(root_path, "pinokio.js")
34
+ let exists = await this.exists(primary_path)
35
+ if (exists) {
36
+ return root_path
37
+ } else {
38
+ let secondary_path = path.resolve(root_path, "pinokio/pinokio.js")
39
+ let exists = await this.exists(secondary_path)
40
+ if (exists) {
41
+ return path.resolve(root_path, "pinokio")
42
+ }
43
+ }
44
+ // default is the
45
+ return root_path
46
+ }
47
+ async launcher(name) {
48
+ /*
49
+ look for:
50
+ 1. pinokio.js
51
+ 2. ./pinokio/pinokio.js
52
+ */
53
+ let root_path
54
+ if (typeof name === "object") {
55
+ if (name.name) {
56
+ root_path = this.kernel.path("api", name.name)
57
+ } else if (name.path) {
58
+ root_path = name.path
59
+ }
60
+ } else {
61
+ root_path = this.kernel.path("api", name)
62
+ }
63
+ let primary_path = path.resolve(root_path, "pinokio.js")
64
+ let secondary_path = path.resolve(root_path, "pinokio/pinokio.js")
65
+ let pinokio = (await this.kernel.loader.load(primary_path)).resolved
66
+ let launcher_root = ""
67
+ if (!pinokio) {
68
+ pinokio = (await this.kernel.loader.load(secondary_path)).resolved
69
+ if (pinokio) {
70
+ launcher_root = "pinokio"
71
+ }
72
+ }
73
+ return {
74
+ script: pinokio,
75
+ root: root_path,
76
+ launcher_root
77
+ }
78
+ }
79
+ // async createMeta(formData) {
80
+ // let _path = this.kernel.path("api", formData.path)
81
+ // await fs.promises.mkdir(_path, { recursive: true }).catch((e) => {})
82
+ // let icon_path = this.kernel.path("api", formData.path, "icon.png")
83
+ // await fs.promises.writeFile(icon_path, formData.avatar)
84
+ //
85
+ // // write title/description to pinokio.json
86
+ // let meta_path = this.kernel.path("api", formData.path, "pinokio.json")
87
+ // let meta = {
88
+ // title: formData.title,
89
+ // description: formData.description,
90
+ // icon: "icon.png",
91
+ // plugin: {
92
+ // menu: []
93
+ // }
94
+ // }
95
+ // await fs.promises.writeFile(meta_path, JSON.stringify(meta, null, 2))
96
+ // }
97
+ async updateMeta(formData, app_path) {
98
+ // write title/description to pinokio.json
99
+ let dirty
100
+ let launcher_path = await this.launcher_path(app_path)
101
+ let meta_path = path.resolve(launcher_path, "pinokio.json")
102
+ let meta = (await this.kernel.loader.load(meta_path)).resolved
103
+ if (!meta) meta = {}
104
+ if (formData.title) {
105
+ meta.title = formData.title
106
+ dirty = true
107
+ }
108
+ if (formData.description) {
109
+ meta.description = formData.description
110
+ dirty = true
111
+ }
112
+ if (!meta.plugin) {
113
+ meta.plugin = {
114
+ menu: []
115
+ }
116
+ }
117
+
118
+ if (formData.icon_dirty) {
119
+ //
120
+ // write icon file
121
+ let icon_path = this.kernel.path("api", formData.new_path, formData.icon_path)
122
+ await fs.promises.writeFile(icon_path, formData.avatar)
123
+ meta.icon = formData.icon_path
124
+ dirty = true
125
+ }
126
+ if (dirty) {
127
+ await fs.promises.writeFile(meta_path, JSON.stringify(meta, null, 2))
128
+ }
129
+ }
31
130
  async meta(name) {
32
131
  let p1
33
132
  let p2
@@ -35,19 +134,19 @@ class Api {
35
134
  let api_path
36
135
  let api_name
37
136
  if (typeof name === "object") {
38
- if (name.path) {
39
- api_path = name.path
40
- api_name = path.relative(this.kernel.path("api"), api_path)
41
- p1 = path.resolve(name.path, "pinokio.js")
42
- p2 = path.resolve(name.path, "pinokio_meta.json")
43
- p3 = path.resolve(name.path, "pinokio.json")
44
- }
137
+ // if (name.path) {
138
+ // api_path = name.path
139
+ // api_name = path.relative(this.kernel.path("api"), api_path)
140
+ // p1 = path.resolve(name.path, "pinokio.js")
141
+ // p2 = path.resolve(name.path, "pinokio_meta.json")
142
+ // p3 = path.resolve(name.path, "pinokio.json")
143
+ // }
45
144
  } else {
46
- api_path = this.kernel.path("api", name)
145
+ api_path = await this.launcher_path(name)
47
146
  api_name = name
48
- p1 = this.kernel.path("api", name, "pinokio.js")
49
- p2 = this.kernel.path("api", name, "pinokio_meta.json")
50
- p3 = this.kernel.path("api", name, "pinokio.json")
147
+ p1 = path.resolve(api_path, "pinokio.js")
148
+ p2 = path.resolve(api_path, "pinokio_meta.json")
149
+ p3 = path.resolve(api_path, "pinokio.json")
51
150
  }
52
151
  let pinokio = (await this.kernel.loader.load(p1)).resolved
53
152
  if (pinokio && pinokio.menu && !(Array.isArray(pinokio.menu) || typeof pinokio.menu === "function")) {
@@ -79,16 +178,23 @@ class Api {
79
178
 
80
179
  meta.iconpath = meta.icon ? meta.icon : null
81
180
  //meta.iconpath = meta.icon ? path.resolve(api_path, meta.icon) : null
82
- meta.icon = meta.icon ? `/api/${api_name}/${meta.icon}?raw=true` : "/pinokio-black.png"
83
181
  meta.path = api_path
84
182
  meta.name = meta.title
85
- //meta.link = `/pinokio/browser/${api_name}/dev#n1`
86
- meta.link = `/p/${api_name}/dev#n1`
87
- meta.web_path = `/api/${api_name}`
88
- //meta.ui = `/pinokio/browser/${api_name}`
89
- meta.ui = `/p/${api_name}`
90
- //meta.browse = `/pinokio/browser/${api_name}/dev`
91
- meta.browse = `/p/${api_name}/dev`
183
+
184
+ let relpath = path.relative(this.kernel.path("api", name), api_path)
185
+ if (relpath === ".") {
186
+ meta.icon = meta.icon ? `/asset/api/${api_name}/${meta.icon}` : "/pinokio-black.png"
187
+ meta.link = `/p/${api_name}/dev#n1`
188
+ meta.web_path = `/api/${api_name}`
189
+ meta.ui = `/p/${api_name}`
190
+ meta.browse = `/p/${api_name}/dev`
191
+ } else {
192
+ meta.icon = meta.icon ? `/asset/api/${api_name}/${meta.icon}` : "/pinokio-black.png"
193
+ meta.link = `/p/${api_name}/${relpath}/dev#n1`
194
+ meta.web_path = `/api/${api_name}/${relpath}`
195
+ meta.ui = `/p/${api_name}/${relpath}`
196
+ meta.browse = `/p/${api_name}/dev/${relpath}`
197
+ }
92
198
  if (!pinokio && !pinokio2 && !pinokio3 ) {
93
199
  meta.init_required = true
94
200
  }
@@ -264,32 +370,36 @@ class Api {
264
370
  } catch (e) {
265
371
  }
266
372
  if (files) {
267
- let folders = files.filter((file) => { return file.isDirectory() }).map((folder) => { return folder.name })
373
+ let folders = []
374
+ for(let file of files) {
375
+ let type = await Util.file_type(this.userdir, file)
376
+ if (type.directory) {
377
+ folders.push(file.name)
378
+ }
379
+ }
268
380
  for(let folder of folders) {
269
381
  try {
270
- const configPath = path.resolve(this.userdir, folder, "pinokio.js")
271
- let m = (await this.loader.load(configPath))
272
- if (m.resolved) {
273
- if (m.resolved.start) {
274
- if (typeof m.resolved.start === "function") {
275
- if (m.resolved.start.constructor.name === "AsyncFunction") {
276
- m.resolved.start = await m.resolved.start(this.kernel)
382
+ let m = await this.launcher(folder)
383
+ if (m && m.script) {
384
+ if (m.script.start) {
385
+ if (typeof m.script.start === "function") {
386
+ if (m.script.start.constructor.name === "AsyncFunction") {
387
+ m.script.start = await m.script.start(this.kernel)
277
388
  } else {
278
- m.resolved.start = m.resolved.start(this.kernel)
389
+ m.script.start = m.script.start(this.kernel)
279
390
  }
280
391
  }
281
392
  // start script name => turn into hex to find the folder
282
393
  // and add the path to the start script array
283
- if (m.resolved.start) {
394
+ if (m.script.start) {
284
395
  let uri = folder
285
396
  startScripts.push({
286
397
  name: folder,
287
398
  path: "/api/" + folder,
288
399
  uri,
289
- script: m.resolved
400
+ script: m.script
290
401
  })
291
402
  }
292
-
293
403
  }
294
404
  }
295
405
  } catch (e) {
@@ -309,7 +419,13 @@ class Api {
309
419
  } catch (e) {
310
420
  }
311
421
  if (files) {
312
- let folders = files.filter((file) => { return file.isDirectory() }).map((folder) => { return folder.name })
422
+ let folders = []
423
+ for(let file of files) {
424
+ let type = await Util.file_type(this.userdir, file)
425
+ if (type.directory) {
426
+ folders.push(file.name)
427
+ }
428
+ }
313
429
  for(let folder of folders) {
314
430
  try {
315
431
  const repositoryPath = path.resolve(this.userdir, folder)
@@ -580,25 +696,31 @@ class Api {
580
696
  }
581
697
  async dirs(p) {
582
698
  const files = await fs.promises.readdir(p, { withFileTypes: true })
583
- return files.filter(file => file.isDirectory()).map((x) => {
584
- return {
585
- name: x.name,
586
- path: path.resolve(p, x.name)
587
- }
588
- });
699
+ let folders = []
700
+ for(let file of files) {
701
+ let type = await Util.file_type(this.userdir, file)
702
+ if (type.directory) {
703
+ folders.push({
704
+ name: file.name,
705
+ path: path.resolve(p, file.name)
706
+ })
707
+ }
708
+ }
709
+ return folders
589
710
  }
590
711
  async files(p) {
591
712
  const files = await fs.promises.readdir(p, { withFileTypes: true })
592
- return files.filter(file => !file.isDirectory())
593
- .filter((file) => {
594
- return !file.name.startsWith(".") // no hidden files
595
- })
596
- .map((x) => {
597
- return {
598
- name: x.name,
599
- path: path.resolve(p, x.name)
713
+ const res = []
714
+ for(let file of files) {
715
+ let type = await Util.file_type(this.userdir, file)
716
+ if (type.file && !file.name.startsWith(".")) {
717
+ res.push({
718
+ name: file.name,
719
+ path: path.resolve(p, file.name)
720
+ })
600
721
  }
601
- });
722
+ }
723
+ return res
602
724
  }
603
725
  exists(_path) {
604
726
  return new Promise(r=>fs.access(_path, fs.constants.F_OK, e => r(!e)))
@@ -662,6 +784,8 @@ class Api {
662
784
 
663
785
  let { cwd, script } = await this.resolveScript(request.path)
664
786
 
787
+ let name = path.relative(this.kernel.path("api"), cwd)
788
+
665
789
  if (request.cwd) {
666
790
  cwd = request.cwd
667
791
  }
@@ -678,6 +802,7 @@ class Api {
678
802
  current: i,
679
803
  uri: request.uri,
680
804
  cwd,
805
+ name,
681
806
  self: script,
682
807
  port,
683
808
  ...this.kernel.vars,
@@ -798,7 +923,6 @@ class Api {
798
923
  } else {
799
924
  rpc.next = null
800
925
  }
801
- console.log("next is not set. compute the next value", rpc.next)
802
926
  }
803
927
 
804
928
  if (rpc.hasOwnProperty("when")) {
@@ -1241,9 +1365,10 @@ class Api {
1241
1365
  })
1242
1366
  }
1243
1367
  async get_default(repo_path) {
1244
- let p = path.resolve(repo_path, "pinokio.js")
1245
- // call 'process' based on the current status
1246
- let config = (await this.loader.load(p)).resolved
1368
+ let launcher = await this.launcher({
1369
+ path: repo_path
1370
+ })
1371
+ let config = launcher.script
1247
1372
  if (config && config.menu) {
1248
1373
  if (typeof config.menu === "function") {
1249
1374
  if (config.menu.constructor.name === "AsyncFunction") {
@@ -1359,6 +1484,17 @@ class Api {
1359
1484
  this.done[request.path] = done
1360
1485
  }
1361
1486
 
1487
+ // set DNS
1488
+
1489
+ if (request.path) {
1490
+ if (request.path.startsWith(this.kernel.path("api"))) {
1491
+ await this.kernel.dns({
1492
+ path: request.path
1493
+ })
1494
+ }
1495
+ }
1496
+
1497
+
1362
1498
 
1363
1499
  this.queue(request, script.run[0], request.input, 0, script.run.length, cwd, request.input)
1364
1500
 
@@ -0,0 +1,15 @@
1
+ class Loading {
2
+ async start (req, ondata, kernel) {
3
+ ondata(req.params, "loading.start")
4
+ let response = await kernel.api.wait(req.parent.path)
5
+ console.log("loading.start response", response)
6
+ return response
7
+ }
8
+ async end (req, ondata, kernel) {
9
+ ondata(req.params, "loading.end")
10
+ let response = await kernel.api.wait(req.parent.path)
11
+ console.log("loading.end response", response)
12
+ return response
13
+ }
14
+ }
15
+ module.exports = Loading
@@ -215,9 +215,16 @@ class Process {
215
215
  this.resolve()
216
216
  }, ms)
217
217
  })
218
+ } else if (req.params.uri) {
219
+ let interval = req.params.interval ? req.params.interval * 1000 : 1000
220
+ ondata(req.params, "loading.start")
221
+ await waitForUrl(req.params.uri, req.params.message, interval, ondata)
222
+ ondata(req.params, "loading.end")
218
223
  } else if (req.params.url) {
219
224
  let interval = req.params.interval ? req.params.interval * 1000 : 1000
225
+ ondata(req.params, "loading.start")
220
226
  await waitForUrl(req.params.url, req.params.message, interval, ondata)
227
+ ondata(req.params, "loading.end")
221
228
  } else if (req.params.on) {
222
229
  // Wait
223
230
  if (req.params.message) {
@@ -97,8 +97,6 @@ class Shell {
97
97
  req.params.$parent = req.parent
98
98
  }
99
99
 
100
- console.log("#### shell.enter", req.params)
101
-
102
100
  let response = await kernel.shell.enter(req.params, ondata)
103
101
  //let response = await this.send(req, ondata, kernel, true)
104
102
  return response
@@ -0,0 +1,22 @@
1
+ class Browserless {
2
+ description = "The headless Chrome/Chromium driver"
3
+ async uninstall(req, ondata) {
4
+ await this.kernel.bin.exec({
5
+ message: "npm uninstall -g @browserless/cli"
6
+ }, ondata)
7
+ }
8
+ async install(req, ondata) {
9
+ await this.kernel.bin.exec({
10
+ message: "npm install -g @browserless/cli"
11
+ }, ondata)
12
+ }
13
+ async installed() {
14
+ let browserless = this.kernel.which('browserless')
15
+ if (browserless) {
16
+ return true
17
+ } else {
18
+ return false
19
+ }
20
+ }
21
+ }
22
+ module.exports = Browserless
@@ -2,6 +2,7 @@ const axios = require('axios')
2
2
  const path = require('path')
3
3
  const fs = require('fs')
4
4
  const semver = require('semver')
5
+ const kill = require('kill-sync')
5
6
  const Util = require('../util')
6
7
 
7
8
  class Caddy {
@@ -13,18 +14,24 @@ class Caddy {
13
14
  }
14
15
  }
15
16
  async running() {
16
- console.log("caddy running check")
17
- console.log("stack", new Error().stack)
18
17
  try {
19
18
  let response = await axios.get('http://127.0.0.1:2019/config/')
20
19
  return true
21
20
  } catch (e) {
22
- console.log(e.message)
21
+ // console.log(e.message)
23
22
  return false
24
23
  }
25
24
  }
26
25
  async start() {
27
- // console.log(">> proc", this.kernel.processes.caddy_pid, this.kernel.processes.info)
26
+ // console.log("Existing caddy pid?", this.kernel.processes.caddy_pid)
27
+ let running = await this.running()
28
+ console.log("Caddy running?", running)
29
+ // if (this.kernel.processes.caddy_pid) {
30
+ if (running) {
31
+ console.log("kill existing caddy")
32
+ kill(this.kernel.processes.caddy_pid, "SIGKILL", true)
33
+ console.log("killed existing caddy")
34
+ }
28
35
  // let running = await this.running()
29
36
  // console.log("Running", running)
30
37
  // if (running) {
@@ -82,6 +89,19 @@ class Caddy {
82
89
  }
83
90
  })
84
91
  })
92
+
93
+ // wait until running
94
+ await new Promise((resolve, reject) => {
95
+ ondata({ raw: "waiting until caddy server is up...\r\n" })
96
+ setInterval(async () => {
97
+ let running = await this.running()
98
+ if (running) {
99
+ resolve()
100
+ }
101
+ }, 2000)
102
+ })
103
+ ondata({ raw: "caddy is running!\r\n" })
104
+
85
105
  if (this.kernel.platform === "win32") {
86
106
  await this.kernel.exec({
87
107
  message: `caddy trust`,
@@ -155,6 +175,18 @@ class Caddy {
155
175
  return false
156
176
  }
157
177
  }
178
+ async running() {
179
+ let running = false
180
+ try {
181
+ let res = await axios.get(`http://127.0.0.1:2019/config/`, {
182
+ timeout: 2000
183
+ })
184
+ running = true
185
+ } catch (e) {
186
+ // console.log(e)
187
+ }
188
+ return running
189
+ }
158
190
  async uninstall(req, ondata) {
159
191
  ondata({ raw: "cleaning up\r\n" })
160
192
  await this.kernel.bin.exec({
@@ -210,7 +210,7 @@ class Bin {
210
210
  process.env.PLAYWRIGHT_BROWSERS_PATH = playwright_folder
211
211
  }
212
212
  // await fs.promises.mkdir(playwright_folder, { recursive: true }).catch((e) => { })
213
- let system_env = await Environment.get(this.kernel.homedir)
213
+ let system_env = await Environment.get(this.kernel.homedir, this.kernel)
214
214
 
215
215
  if (system_env.HTTP_PROXY) {
216
216
  process.env.HTTP_PROXY = system_env.HTTP_PROXY
@@ -1054,6 +1054,9 @@ class Bin {
1054
1054
 
1055
1055
  return {
1056
1056
  error,
1057
+ title: config.bin.title,
1058
+ description: config.bin.description,
1059
+ icon: config.bin.icon,
1057
1060
  requirements,
1058
1061
  install_required,
1059
1062
  requirements_pending
@@ -49,6 +49,7 @@ module.exports = {
49
49
  { name: "huggingface" },
50
50
  { name: "uv" },
51
51
  { name: "py" },
52
+ { name: "browserless" },
52
53
  ])
53
54
  return {
54
55
  icon: "fa-solid fa-brain",
@@ -71,6 +72,7 @@ module.exports = {
71
72
  { name: "node", },
72
73
  { name: "cli", },
73
74
  { name: "py" },
75
+ { name: "browserless" },
74
76
  ])
75
77
  return {
76
78
  icon: "fa-brands fa-js",
@@ -110,6 +112,36 @@ module.exports = {
110
112
  }
111
113
  },
112
114
  dev: (kernel) => {
115
+ let requirements = [
116
+ { name: "conda", },
117
+ { name: "zip", },
118
+ ]
119
+ if (platform === "darwin") {
120
+ requirements.push({ name: "brew" })
121
+ }
122
+ requirements = requirements.concat([
123
+ { name: "git", },
124
+ { name: "node", },
125
+ { name: "cli", },
126
+ { name: "uv", },
127
+ { name: "py", },
128
+ { name: "browserless" },
129
+ ])
130
+ let conda_requirements = [
131
+ zip_cmd,
132
+ "uv",
133
+ "node",
134
+ "git",
135
+ ]
136
+ return {
137
+ icon: "fa-solid fa-laptop-code",
138
+ title: "Coding (Essential)",
139
+ description: "Install common modules required for development (Node.js, python, Visual Studio Developer Tools (Windows), Xcode build tools (Mac)",
140
+ requirements,
141
+ conda_requirements,
142
+ }
143
+ },
144
+ advanced_dev: (kernel) => {
113
145
  let requirements = [
114
146
  { name: "conda", },
115
147
  { name: "zip", },
@@ -123,12 +155,15 @@ module.exports = {
123
155
  { name: "cli", },
124
156
  { name: "uv", },
125
157
  { name: "caddy", },
158
+ { name: "huggingface" },
126
159
  { name: "py", },
160
+ { name: "browserless" },
127
161
  ])
128
162
  let conda_requirements = [
129
163
  zip_cmd,
130
164
  "uv",
131
165
  "node",
166
+ "huggingface",
132
167
  "git",
133
168
  "caddy",
134
169
  ]
@@ -136,17 +171,14 @@ module.exports = {
136
171
  requirements.push({ name: "registry" })
137
172
  requirements.push({ name: "vs" })
138
173
  }
139
- if (platform === "darwin") {
140
- requirements.push({ name: "brew" })
141
- }
142
174
  if (platform === "linux") {
143
175
  requirements.push({ name: "gxx" })
144
176
  conda_requirements.push("gxx")
145
177
  }
146
178
  return {
147
179
  icon: "fa-solid fa-laptop-code",
148
- title: "Coding",
149
- description: "Install common modules required for development (Node.js, python, Visual Studio Developer Tools (Windows), Xcode build tools (Mac)",
180
+ title: "Coding (Advanced)",
181
+ description: "Coding (Essential) + More modules useful for building",
150
182
  requirements,
151
183
  conda_requirements,
152
184
  }
@@ -199,6 +231,7 @@ module.exports = {
199
231
  requirements,
200
232
  conda_requirements: [
201
233
  zip_cmd,
234
+ "huggingface",
202
235
  "uv",
203
236
  "git",
204
237
  "caddy"