pinokiod 3.41.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 +1037 -964
  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 +220 -94
  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
package/server/index.js CHANGED
@@ -23,6 +23,7 @@ const fse = require('fs-extra')
23
23
  const QRCode = require('qrcode')
24
24
  const axios = require('axios')
25
25
  const crypto = require('crypto')
26
+ const serveIndex = require('serve-index')
26
27
 
27
28
  const git = require('isomorphic-git')
28
29
  const http = require('isomorphic-git/http/node')
@@ -60,8 +61,6 @@ function normalize(str) {
60
61
 
61
62
  class Server {
62
63
  constructor(config) {
63
- this.menu_hidden = {}
64
- this.selected = {}
65
64
  this.tabs = {}
66
65
  this.agent = config.agent
67
66
  this.port = DEFAULT_PORT
@@ -112,38 +111,6 @@ class Server {
112
111
  exists (s) {
113
112
  return new Promise(r=>fs.access(s, fs.constants.F_OK, e => r(!e)))
114
113
  }
115
- async updateMeta(formData, app_path) {
116
- // write title/description to pinokio.json
117
- let dirty
118
- let meta_path = this.kernel.path("api", app_path, "pinokio.json")
119
- let meta = (await this.kernel.loader.load(meta_path)).resolved
120
- if (!meta) meta = {}
121
- if (formData.title) {
122
- meta.title = formData.title
123
- dirty = true
124
- }
125
- if (formData.description) {
126
- meta.description = formData.description
127
- dirty = true
128
- }
129
- if (!meta.plugin) {
130
- meta.plugin = {
131
- menu: []
132
- }
133
- }
134
-
135
- if (formData.icon_dirty) {
136
- //
137
- // write icon file
138
- let icon_path = this.kernel.path("api", formData.new_path, formData.icon_path)
139
- await fs.promises.writeFile(icon_path, formData.avatar)
140
- meta.icon = formData.icon_path
141
- dirty = true
142
- }
143
- if (dirty) {
144
- await fs.promises.writeFile(meta_path, JSON.stringify(meta, null, 2))
145
- }
146
- }
147
114
  running_dynamic (name, menu) {
148
115
  let cwd = this.kernel.path("api", name)
149
116
  let running_dynamic = []
@@ -204,24 +171,6 @@ class Server {
204
171
  traverse(menu)
205
172
  return running_dynamic
206
173
  }
207
- async createMeta(formData) {
208
- let _path = this.kernel.path("api", formData.path)
209
- await fs.promises.mkdir(_path, { recursive: true }).catch((e) => {})
210
- let icon_path = this.kernel.path("api", formData.path, "icon.png")
211
- await fs.promises.writeFile(icon_path, formData.avatar)
212
-
213
- // write title/description to pinokio.json
214
- let meta_path = this.kernel.path("api", formData.path, "pinokio.json")
215
- let meta = {
216
- title: formData.title,
217
- description: formData.description,
218
- icon: "icon.png",
219
- plugin: {
220
- menu: []
221
- }
222
- }
223
- await fs.promises.writeFile(meta_path, JSON.stringify(meta, null, 2))
224
- }
225
174
  getMemory(filepath) {
226
175
  let localMem = this.kernel.memory.local[filepath]
227
176
  let globalMem = this.kernel.memory.global[filepath]
@@ -287,14 +236,22 @@ class Server {
287
236
  name = x.name
288
237
  description = ""
289
238
  }
239
+
240
+
290
241
  let browser_url
242
+ let target
243
+
291
244
  if (x.run) {
292
245
  browser_url = "/env/api/" + x.name
293
246
  } else {
294
247
  //browser_url = "/pinokio/browser/" + x.name
295
248
  browser_url = "/p/" + x.name
296
249
  }
297
- let browser_browse_url = browser_url + "/dev"
250
+ let dev_url = browser_url + "/dev"
251
+ let review_url = browser_url + "/review"
252
+
253
+ let dns = this.kernel.pinokio_configs[x.name].dns
254
+ let routes = dns["@"]
298
255
  return {
299
256
  filepath: this.kernel.path("api", x.name),
300
257
  icon,
@@ -313,12 +270,86 @@ class Server {
313
270
  description,
314
271
  url: p + "/" + x.name,
315
272
  browser_url,
273
+ target,
316
274
  url: browser_url,
317
275
  path: uri,
318
- browse_url: browser_browse_url,
276
+ dev_url,
277
+ review_url,
319
278
  }
320
279
  })
321
280
  }
281
+ async getGit(ref, filepath) {
282
+ let dir = this.kernel.path("api", filepath)
283
+ let branches = await git.listBranches({ fs, dir });
284
+ let log = []
285
+ try {
286
+ log = await git.log({ fs, dir, depth: 50, ref: ref }); // fetch last 50 commits
287
+ log.forEach((item) => {
288
+ item.info = `/gitcommit/${item.oid}/${filepath}`
289
+ })
290
+ } catch (e) {
291
+ console.log("Log error", e)
292
+ }
293
+
294
+ let config = await this.kernel.git.config(dir)
295
+
296
+ let hosts = ""
297
+ let hosts_file = this.kernel.path("config/gh/hosts.yml")
298
+ let e = await this.exists(hosts_file)
299
+ if (e) {
300
+ hosts = await fs.promises.readFile(hosts_file, "utf8")
301
+ if (hosts.startsWith("{}")) {
302
+ hosts = ""
303
+ }
304
+ }
305
+ let connected = (hosts.length > 0)
306
+ let remote = null
307
+ if (config["remote \"origin\""]) {
308
+ remote = config["remote \"origin\""].url
309
+ }
310
+
311
+ let branch = await git.currentBranch({ fs, dir, fullname: false });
312
+
313
+ const remote2 = await git.getConfig({
314
+ fs,
315
+ dir,
316
+ path: `branch.${branch}.remote`
317
+ });
318
+
319
+ // if current branch exitss => currengt branch is selected
320
+ // if current branch does not exist => get logs[0].oid
321
+ if (branch) {
322
+ branches = branches.map((b) => {
323
+ if (b === branch) {
324
+ return {
325
+ branch: b,
326
+ selected: true
327
+ }
328
+ } else {
329
+ return {
330
+ branch: b,
331
+ selected: false
332
+ }
333
+ }
334
+ })
335
+ } else {
336
+ branches.push(log[0].oid)
337
+ branches = branches.map((b) => {
338
+ if (b === log[0].oid) {
339
+ return {
340
+ branch: b,
341
+ selected: true
342
+ }
343
+ } else {
344
+ return {
345
+ branch: b,
346
+ selected: false
347
+ }
348
+ }
349
+ })
350
+ }
351
+ return { ref, config, remote, connected, log, branch, branches, dir }
352
+ }
322
353
  async init_env(env_dir_path, options) {
323
354
  let current = this.kernel.path(env_dir_path, "ENVIRONMENT")
324
355
  // if environment.json doesn't exist,
@@ -338,7 +369,7 @@ class Server {
338
369
  await fs.promises.writeFile(current, _environmentStr)
339
370
  }
340
371
  } else {
341
- let content = await Environment.ENV("app", this.kernel.homedir)
372
+ let content = await Environment.ENV("app", this.kernel.homedir, this.kernel)
342
373
  if (_exists) {
343
374
  let _environmentStr = await fs.promises.readFile(_environment, "utf8")
344
375
  await fs.promises.writeFile(current, _environmentStr + "\n\n\n" + content)
@@ -348,31 +379,45 @@ class Server {
348
379
  }
349
380
  }
350
381
  }
351
- async current_urls(current_path) {
352
- let router_running = await this.check_router_up()
353
- let u = new URL("http://localhost:42000")
354
-
355
- let current_urls = {}
356
-
357
- // http
358
- if (current_path) {
359
- u.pathname = current_path
360
- }
361
- current_urls.http = u.toString()
362
-
363
- // https
364
- if (router_running.success) {
365
- let u = new URL("https://pinokio.localhost")
366
- if (current_path) {
367
- u.pathname = current_path
382
+ async get_github_hosts() {
383
+ let hosts = ""
384
+ let hosts_file = this.kernel.path("config/gh/hosts.yml")
385
+ let e = await this.exists(hosts_file)
386
+ if (e) {
387
+ hosts = await fs.promises.readFile(hosts_file, "utf8")
388
+ if (hosts.startsWith("{}")) {
389
+ hosts = ""
368
390
  }
369
- current_urls.https = u.toString()
370
391
  }
371
-
372
- return current_urls
392
+ return hosts
393
+ }
394
+ async current_urls(current_path) {
395
+ return {}
396
+ // let router_running = await this.check_router_up()
397
+ // let u = new URL("http://localhost:42000")
398
+ //
399
+ // let current_urls = {}
400
+ //
401
+ // // http
402
+ // if (current_path) {
403
+ // u.pathname = current_path
404
+ // }
405
+ // current_urls.http = u.toString()
406
+ //
407
+ // // https
408
+ // if (router_running.success) {
409
+ // let u = new URL("https://pinokio.localhost")
410
+ // if (current_path) {
411
+ // u.pathname = current_path
412
+ // }
413
+ // current_urls.https = u.toString()
414
+ // }
415
+ //
416
+ // return current_urls
373
417
  }
374
418
 
375
419
  async chrome(req, res, type) {
420
+
376
421
  let d = Date.now()
377
422
  let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
378
423
  bin: this.kernel.bin.preset("dev"),
@@ -408,32 +453,6 @@ class Server {
408
453
  }
409
454
  }
410
455
 
411
-
412
-
413
- // let requires_instantiation = false
414
- // console.log("## CONFIG", config)
415
- // if (config && config.pre) {
416
- // let env = await Environment.get2(app_path, this.kernel)
417
- // for(let item of config.pre) {
418
- // console.log("ITEM" , item)
419
- // if (item.env) {
420
- // if (env[item.env]) {
421
- //
422
- // } else {
423
- // requires_instantiation = true
424
- // break;
425
- // }
426
- // }
427
- // }
428
- // }
429
- // console.log({ requires_instantiation })
430
- // if (requires_instantiation) {
431
- // // redirect to pre
432
- // res.redirect("/required_env/api/" + name)
433
- // return
434
- // }
435
-
436
-
437
456
  let menu = config.menu || []
438
457
  try {
439
458
  if (typeof config.menu === "function") {
@@ -448,80 +467,53 @@ class Server {
448
467
  config.menu = []
449
468
  }
450
469
 
470
+
451
471
  let uri = this.kernel.path("api")
452
472
  try {
473
+ let launcher = await this.kernel.api.launcher(name)
474
+ req.launcher_root = launcher.launcher_root
453
475
  await this.renderMenu(req, uri, name, config, [])
454
476
  } catch(e) {
455
477
  config.menu = []
456
478
  err = e.stack
457
479
  }
458
480
 
459
-
460
481
  let platform = os.platform()
461
482
 
462
- // if (config.icon) {
463
- // //config.iconpath = this.kernel.path("api", name, config.icon)
464
- // config.iconpath = config.icon
465
- // config.icon = `${rawpath}/${config.icon}?raw=true`
466
- // } else {
467
- // //config.iconpath = this.kernel.path("api", name, "pinokio_icon.png")
468
- // config.iconpath = "pinokio.png"
469
- // config.icon = "/pinokio-black.png"
470
- // }
471
-
472
-
473
- // // get all memory variable stied to the current repository
474
- // let api_path = this.kernel.path("api", name)
475
- // let mem = {}
476
- // for(let type in this.kernel.memory) {
477
- // // type := local|global
478
- // let vars = this.kernel.memory[type]
479
- // for(let k in vars) {
480
- // if (k.includes(api_path)) {
481
- // if (mem[k]) {
482
- // mem[k][type] = vars[k]
483
- // } else {
484
- // mem[k] = {
485
- // [type]: vars[k]
486
- // }
487
- // }
488
- // }
489
- // }
490
- // }
491
-
492
- // console.time("2 chrome " + d)
493
- await this.init_env("api/" + name)
483
+ await Environment.init({ name }, this.kernel)
494
484
 
495
485
  // copy gitignore from ~pinokio/prototype/system/gitignore if it doesn't exist
496
486
 
497
487
 
498
488
  let gitignore_path = this.kernel.path("api/" + name + "/.gitignore")
489
+ let dot_path = this.kernel.path("api", name, "pinokio")
499
490
  let gitignore_template_path = this.kernel.path("prototype/system/gitignore")
500
491
  let template_exists = await this.exists(gitignore_template_path)
501
492
  if (template_exists) {
502
- await Util.mergeLines(
503
- gitignore_path, // existing path
504
- gitignore_template_path // overwrite with template
505
- )
493
+ let exists = await this.exists(dot_path)
494
+ if (exists) {
495
+ // 1. when importing existing projects (.pinokio exists), don't mess with .gitignore
496
+ } else {
497
+ // 2. otherwise, merge gitignore
498
+ await Util.mergeLines(
499
+ gitignore_path, // existing path
500
+ gitignore_template_path // overwrite with template
501
+ )
502
+ }
506
503
  }
507
504
 
508
505
 
509
506
 
510
507
 
511
- // console.timeEnd("2 chrome " + d)
512
-
513
- // console.time("3 chrome " + d)
514
508
  let mode = "run"
515
509
  if (req.query && req.query.mode) {
516
510
  mode = req.query.mode
517
511
  }
518
512
  const env = await this.kernel.env("api/" + name)
519
- // console.timeEnd("3 chrome " + d)
520
513
 
521
514
  // profile + feed
522
515
  const repositoryPath = path.resolve(this.kernel.api.userdir, name)
523
516
 
524
- // console.time("4 chrome " + d)
525
517
  try {
526
518
  await git.resolveRef({ fs, dir: repositoryPath, ref: 'HEAD' });
527
519
  } catch (err) {
@@ -530,9 +522,6 @@ class Server {
530
522
  await git.init({ fs, dir: repositoryPath });
531
523
  }
532
524
 
533
- // console.timeEnd("4 chrome " + d)
534
-
535
- // console.time("5 chrome " + d)
536
525
  let gitRemote = await git.getConfig({ fs, http, dir: repositoryPath, path: 'remote.origin.url' })
537
526
  let profile
538
527
  let feed
@@ -541,24 +530,18 @@ class Server {
541
530
 
542
531
  let system_env = {}
543
532
  if (this.kernel.homedir) {
544
- system_env = await Environment.get(this.kernel.homedir)
533
+ system_env = await Environment.get(this.kernel.homedir, this.kernel)
545
534
  }
546
535
  profile = this.profile(gitRemote)
547
536
  feed = this.newsfeed(gitRemote)
548
537
  }
549
- // console.timeEnd("5 chrome " + d)
550
538
 
551
539
  // git
552
540
 
553
541
  let c = this.kernel.path("api", name)
554
542
 
555
- // console.time("6 chrome " + d)
556
543
  // await this.kernel.plugin.init()
557
- // console.timeEnd("6 chrome " + d)
558
- // console.time("7 chrome " + d)
559
544
  // let plugin = await this.getPlugin(name)
560
- // console.timeEnd("7 chrome " + d)
561
- // console.time("8 chrome " + d)
562
545
  // let plugin_menu = null
563
546
  // if (plugin && plugin.menu && Array.isArray(plugin.menu)) {
564
547
  // let running_dynamic = this.running_dynamic(name, plugin.menu)
@@ -569,15 +552,12 @@ class Server {
569
552
  let current_urls = await this.current_urls(req.originalUrl.slice(1))
570
553
 
571
554
  let plugin_menu = null
572
- let plugin = await this.getPlugin(req, name)
555
+ let plugin_config = structuredClone(this.kernel.plugin.config)
556
+ let plugin = await this.getPlugin(req, plugin_config, name)
573
557
  if (plugin && plugin.menu && Array.isArray(plugin.menu)) {
574
558
  plugin = structuredClone(plugin)
575
559
  plugin_menu = this.running_dynamic(name, plugin.menu)
576
560
  }
577
- let menu_hidden = false
578
- if (this.menu_hidden[name] && this.menu_hidden[name][type]) {
579
- menu_hidden = true
580
- }
581
561
 
582
562
  let posix_path = Util.p2u(this.kernel.path("api", name))
583
563
  let dev_link
@@ -587,9 +567,13 @@ class Server {
587
567
  dev_link = "/d/" + posix_path
588
568
  }
589
569
 
570
+ let run_tab = "/p/" + name
571
+ let dev_tab = "/p/" + name + "/dev"
572
+ let review_tab = "/p/" + name + "/review"
573
+
574
+
590
575
  const result = {
591
576
  dev_link,
592
- minimized: menu_hidden,
593
577
  // repos,
594
578
  current_urls,
595
579
  path: this.kernel.path("api", name),
@@ -619,20 +603,22 @@ class Server {
619
603
  config,
620
604
  // sidebar_url: "/pinokio/sidebar/" + name,
621
605
  home: req.originalUrl,
606
+ run_tab,
607
+ dev_tab,
608
+ review_tab,
622
609
  // paths,
623
610
  theme: this.theme,
624
611
  agent: this.agent,
625
612
  src: "/_api/" + name,
626
613
  logs: "/_api/" + name + "/logs",
627
614
  execUrl: "/api/" + name,
615
+ git_monitor_url: `/gitcommit/HEAD/${name}`,
616
+ git_history_url: `/info/git/HEAD/${name}`,
628
617
  // rawpath,
629
618
  }
630
- // console.timeEnd("8 chrome " + d)
631
- // console.time("9 chrome " + d)
632
619
  // if (!this.kernel.proto.config) {
633
620
  // await this.kernel.proto.init()
634
621
  // }
635
- // console.timeEnd("9 chrome " + d)
636
622
  res.render("app", result)
637
623
  }
638
624
  getVariationUrls(req) {
@@ -651,12 +637,13 @@ class Server {
651
637
  return { editorUrl, prevUrl }
652
638
  }
653
639
  get_shell_id(name, i, rendered) {
654
- let hash = crypto.createHash('md5').update(JSON.stringify(rendered)).digest('hex')
655
640
  let shell_id
656
641
  if (rendered.id) {
657
642
  shell_id = encodeURIComponent(`${name}_${rendered.id}`)
658
643
  } else {
659
- shell_id = encodeURIComponent(`${name}_${i}_session_${hash}`)
644
+ let hash = crypto.createHash('md5').update(JSON.stringify(rendered)).digest('hex')
645
+ //shell_id = encodeURIComponent(`${name}_${i}_session_${hash}`)
646
+ shell_id = encodeURIComponent(`${name}_session_${hash}`)
660
647
  }
661
648
  return shell_id
662
649
  }
@@ -728,50 +715,11 @@ class Server {
728
715
 
729
716
  }
730
717
 
731
- // if (pathComponents.length > 1) {
732
- // if (pathComponents[1] === 'web') {
733
- // let filepath = this.kernel.path("api", ...pathComponents)
734
- // console.log("filepath", filepath)
735
- // try {
736
- // console.log("testing")
737
- // let stat = await fs.promises.stat(filepath)
738
- // console.log("stat", stat)
739
- // // if it's a folder
740
- // if (stat.isDirectory()) {
741
- // // if the current folder has "index.html", send that file
742
- // // otherwise 404
743
- // let indexFile = path.resolve(filepath, "index.html")
744
- // let exists = await this.exists(indexFile)
745
- // if (exists) {
746
- // res.sendFile(indexFile)
747
- // } else {
748
- // // res.redirect("/api/" + pathComponents[0])
749
- // res.status(404).render("404", {
750
- // message: "index.html not found"
751
- // })
752
- // }
753
- // } else if (stat.isFile()) {
754
- // res.sendFile(filepath)
755
- // }
756
- // return
757
- // } catch (e) {
758
- // console.log("E", e)
759
- // res.redirect("/api/" + pathComponents[0])
760
- // return
761
- // /*
762
- // res.status(404).render("404", {
763
- // message: e.message
764
- // })
765
- // */
766
- // }
767
- // }
768
- // }
769
-
770
718
  if (path.basename(filepath) === "ENVIRONMENT") {
771
719
  // if environment.json doesn't exist,
772
720
  let exists = await this.exists(filepath)
773
721
  if (!exists) {
774
- let content = await Environment.ENV("app", this.kernel.homedir)
722
+ let content = await Environment.ENV("app", this.kernel.homedir, this.kernel)
775
723
  await fs.promises.writeFile(filepath, content)
776
724
  }
777
725
  }
@@ -816,7 +764,7 @@ class Server {
816
764
  } else if (pathComponents.length === 0 && req.query.mode === "settings") {
817
765
  let system_env = {}
818
766
  if (this.kernel.homedir) {
819
- system_env = await Environment.get(this.kernel.homedir)
767
+ system_env = await Environment.get(this.kernel.homedir, this.kernel)
820
768
  }
821
769
  let configArray = [{
822
770
  key: "home",
@@ -837,7 +785,7 @@ class Server {
837
785
  }, {
838
786
  key: "mode",
839
787
  val: this.mode,
840
- options: ["full", "minimal"]
788
+ options: ["desktop", "background"]
841
789
  }, {
842
790
  key: "HTTP_PROXY",
843
791
  val: (system_env.HTTP_PROXY || ""),
@@ -862,7 +810,10 @@ class Server {
862
810
  drive: path.resolve(this.kernel.homedir, "drive"),
863
811
  }
864
812
  }
813
+ let list = this.getPeers()
865
814
  res.render("settings", {
815
+ list,
816
+ current_host: this.kernel.peer.host,
866
817
  platform,
867
818
  version: this.version,
868
819
  logo: this.logo,
@@ -1068,23 +1019,23 @@ class Server {
1068
1019
  let env_requirements = await Environment.requirements(resolved, cwd, this.kernel)
1069
1020
  if (env_requirements.requires_instantiation) {
1070
1021
  //let p = Util.api_path(filepath, this.kernel)
1071
- let p = Util.api_path(cwd, this.kernel)
1022
+ let api_path = Util.api_path(cwd, this.kernel)
1023
+ let root = await Environment.get_root({ path: api_path }, this.kernel)
1024
+ let root_path = root.root
1072
1025
  let platform = os.platform()
1073
1026
  if (platform === "win32") {
1074
- p = p.replace(/\\/g, '\\\\')
1027
+ root_path = root_path.replace(/\\/g, '\\\\')
1075
1028
  }
1076
- console.log({ p, cwd })
1029
+ console.log({ cwd, api_path, root, root_path })
1077
1030
  res.render("required_env_editor", {
1078
1031
  portal: this.portal,
1079
1032
  agent: this.agent,
1080
1033
  theme: this.theme,
1081
1034
  filename,
1082
- filepath: p,
1035
+ filepath: root_path,
1083
1036
  items: env_requirements.items
1084
1037
  })
1085
1038
  } else {
1086
- console.log("req.query.callback", req.query.callback)
1087
-
1088
1039
  // check if it's a prototype script
1089
1040
  let kill_message
1090
1041
  let callback
@@ -1162,7 +1113,6 @@ class Server {
1162
1113
  })
1163
1114
  }
1164
1115
  } else if (stat.isDirectory()) {
1165
-
1166
1116
  if (req.query && req.query.mode === "browser") {
1167
1117
  return
1168
1118
  }
@@ -1185,12 +1135,11 @@ class Server {
1185
1135
  }
1186
1136
 
1187
1137
  for(let file of files) {
1188
- if (!file.name.startsWith(".")) {
1189
- if (file.isDirectory()) {
1190
- f.folders.push(file)
1191
- } else {
1192
- f.files.push(file)
1193
- }
1138
+ let type = await Util.file_type(filepath, file)
1139
+ if (type.directory) {
1140
+ f.folders.push(file)
1141
+ } else {
1142
+ f.files.push(file)
1194
1143
  }
1195
1144
  }
1196
1145
 
@@ -1210,8 +1159,6 @@ class Server {
1210
1159
  let p = path.resolve(filepath, file.name)
1211
1160
  config = (await this.kernel.loader.load(p)).resolved
1212
1161
 
1213
-
1214
-
1215
1162
  if (config && config.menu) {
1216
1163
  if (typeof config.menu === "function") {
1217
1164
  if (config.menu.constructor.name === "AsyncFunction") {
@@ -1272,8 +1219,13 @@ class Server {
1272
1219
 
1273
1220
 
1274
1221
  // let folder = pathComponents[pathComponents.length - 1]
1275
-
1276
- items = f.folders.concat(f.files)
1222
+ if (meta) {
1223
+ // home => only show the folders
1224
+ items = f.folders
1225
+ } else {
1226
+ // app view file explorer => show all files and folders
1227
+ items = f.folders.concat(f.files)
1228
+ }
1277
1229
  // }
1278
1230
  let display = pathComponents.length === 0 ? ["form", "explore"] : []
1279
1231
  //let display = ["form"]
@@ -1329,9 +1281,16 @@ class Server {
1329
1281
  let index = 0
1330
1282
  for(let i=0; i<items.length; i++) {
1331
1283
  let item = items[i]
1332
- let p = path.resolve(uri, item.name, "pinokio.js")
1333
- let config = (await this.kernel.loader.load(p)).resolved
1284
+ let launcher = await this.kernel.api.launcher(item.name)
1285
+ let config = launcher.script
1286
+ await this.kernel.dns({
1287
+ name: item.name,
1288
+ config
1289
+ })
1290
+
1291
+
1334
1292
  if (config) {
1293
+
1335
1294
  if (config.shortcuts) {
1336
1295
  if (typeof config.shortcuts === "function") {
1337
1296
  if (config.shortcuts.constructor.name === "AsyncFunction") {
@@ -1414,8 +1373,9 @@ class Server {
1414
1373
  // other global scripts
1415
1374
  let chunks = key.split("?")
1416
1375
  let dev = chunks[0]
1417
- let name_chunks = dev.split(path.sep)
1418
- let name = name_chunks[name_chunks.length-1]
1376
+ let relpath = path.relative(this.kernel.homedir, dev)
1377
+ let name_chunks = relpath.split(path.sep)
1378
+ let name = "/" + relpath
1419
1379
  items[i].running_scripts.push({ id: key, name })
1420
1380
  }
1421
1381
  } else {
@@ -1603,6 +1563,7 @@ class Server {
1603
1563
  items
1604
1564
  })
1605
1565
  } else {
1566
+ console.log("RENDER FILE EXPLORER")
1606
1567
  res.render("file_explorer", {
1607
1568
  docs: this.docs,
1608
1569
  portal: this.portal,
@@ -1819,9 +1780,10 @@ class Server {
1819
1780
  //// icon: "fa-solid fa-gear"
1820
1781
  // }].concat(config.menu)
1821
1782
 
1783
+ let launcher_root = req.launcher_root || ""
1784
+
1822
1785
  for(let i=0; i<config.menu.length; i++) {
1823
1786
  let menuitem = config.menu[i]
1824
-
1825
1787
  if (menuitem.menu) {
1826
1788
  let newIndexPath
1827
1789
  if (indexPath) {
@@ -1841,10 +1803,10 @@ class Server {
1841
1803
  // href resolution
1842
1804
  if (menuitem.fs) {
1843
1805
  // file explorer
1844
- config.menu[i].href = path.resolve(this.kernel.homedir, "api", name, menuitem.href)
1806
+ config.menu[i].href = path.resolve(this.kernel.homedir, "api", name, launcher_root, menuitem.href)
1845
1807
  } else if (menuitem.command) {
1846
1808
  // file explorer
1847
- config.menu[i].href = path.resolve(this.kernel.homedir, "api", name, menuitem.href)
1809
+ config.menu[i].href = path.resolve(this.kernel.homedir, "api", name, launcher_root, menuitem.href)
1848
1810
  } else {
1849
1811
  if (menuitem.href.startsWith("/")) {
1850
1812
  config.menu[i].href = menuitem.href
@@ -1853,7 +1815,11 @@ class Server {
1853
1815
  let seed = path.resolve(__dirname)
1854
1816
  let p = absolute.replace(seed, "")
1855
1817
  let link = p.split(/[\/\\]/).filter((x) => { return x }).join("/")
1856
- config.menu[i].href = "/api/" + name + "/" + link
1818
+ if (launcher_root) {
1819
+ config.menu[i].href = "/api/" + name + "/" + launcher_root + "/" + link
1820
+ } else {
1821
+ config.menu[i].href = "/api/" + name + "/" + link
1822
+ }
1857
1823
  }
1858
1824
  }
1859
1825
  } else if (menuitem.run) {
@@ -1862,12 +1828,21 @@ class Server {
1862
1828
  if (typeof rendered.run === "object") {
1863
1829
  let run = rendered.run
1864
1830
  config.menu[i].run = run.message
1865
- config.menu[i].cwd = run.path ? path.resolve(this.kernel.homedir, "api", name, run.path) : path.resolve(this.kernel.homedir, "api", name)
1866
- config.menu[i].href = "/api/" + name
1831
+ if (launcher_root) {
1832
+ config.menu[i].cwd = run.path ? path.resolve(this.kernel.homedir, "api", name, launcher_root, run.path) : path.resolve(this.kernel.homedir, "api", name, launcher_root)
1833
+ config.menu[i].href = "/api/" + name + "/" + launcher_root
1834
+ } else {
1835
+ config.menu[i].cwd = run.path ? path.resolve(this.kernel.homedir, "api", name, run.path) : path.resolve(this.kernel.homedir, "api", name)
1836
+ config.menu[i].href = "/api/" + name
1837
+ }
1867
1838
  } else {
1868
1839
  config.menu[i].run = rendered.run
1869
- config.menu[i].cwd = path.resolve(this.kernel.homedir, "api", name)
1870
- config.menu[i].href = "/api/" + name
1840
+ config.menu[i].cwd = path.resolve(this.kernel.homedir, "api", launcher_root, name)
1841
+ if (launcher_root) {
1842
+ config.menu[i].href = "/api/" + name + "/" + launcher_root
1843
+ } else {
1844
+ config.menu[i].href = "/api/" + name
1845
+ }
1871
1846
  }
1872
1847
  }
1873
1848
  }
@@ -1878,8 +1853,13 @@ class Server {
1878
1853
 
1879
1854
 
1880
1855
  if (menuitem.shell) {
1881
- let basePath = this.kernel.path("api", name)
1882
- this.renderShell(basePath, indexPath, i, menuitem)
1856
+ if (launcher_root) {
1857
+ let basePath = this.kernel.path("api", name, launcher_root)
1858
+ this.renderShell(basePath, indexPath, i, menuitem)
1859
+ } else {
1860
+ let basePath = this.kernel.path("api", name)
1861
+ this.renderShell(basePath, indexPath, i, menuitem)
1862
+ }
1883
1863
  }
1884
1864
 
1885
1865
  if (menuitem.href) {
@@ -1917,7 +1897,12 @@ class Server {
1917
1897
  }
1918
1898
  } else {
1919
1899
  // prototype script
1920
- let api_path = this.kernel.path("api", name)
1900
+ let api_path
1901
+ if (launcher_root) {
1902
+ api_path = this.kernel.path("api", name, launcher_root)
1903
+ } else {
1904
+ api_path = this.kernel.path("api", name)
1905
+ }
1921
1906
  let id = `${fullpath}?cwd=${api_path}`
1922
1907
  if (this.kernel.api.running[id]) {
1923
1908
  menuitem.running = true
@@ -1934,7 +1919,6 @@ class Server {
1934
1919
  let p = absolute.replace(seed, "")
1935
1920
  let link = p.split(/[\/\\]/).filter((x) => { return x }).join("/")
1936
1921
  let uri = "~/api/" + name + "/" + link
1937
-
1938
1922
  config.menu[i].action.uri = uri
1939
1923
  }
1940
1924
  }
@@ -1999,7 +1983,12 @@ class Server {
1999
1983
  if (menuitem.image.startsWith("/")) {
2000
1984
  imagePath = menuitem.image
2001
1985
  } else {
2002
- imagePath = `/api/${name}/${menuitem.image}?raw=true`
1986
+ console.log({ launcher_root, name, image: menuitem.image })
1987
+ if (launcher_root) {
1988
+ imagePath = `/api/${name}/${launcher_root}/${menuitem.image}?raw=true`
1989
+ } else {
1990
+ imagePath = `/api/${name}/${menuitem.image}?raw=true`
1991
+ }
2003
1992
  }
2004
1993
  menuitem.html = `<img class='menu-item-image' src='${imagePath}' /> ${menuitem.text}`
2005
1994
  } else if (menuitem.hasOwnProperty("icon")) {
@@ -2077,10 +2066,7 @@ class Server {
2077
2066
  // config.icon = "/pinokio-white.png"
2078
2067
  // }
2079
2068
  // }
2080
- console.log("############## config", JSON.stringify(config, null, 2))
2081
-
2082
2069
  config = Util.rewrite_localhost(this.kernel, config, req.$source)
2083
-
2084
2070
  return config
2085
2071
  } else {
2086
2072
  return config
@@ -2260,12 +2246,12 @@ class Server {
2260
2246
 
2261
2247
  // 1. THEME
2262
2248
  this.theme = this.kernel.store.get("theme") || "light"
2263
- this.mode = this.kernel.store.get("mode") || "full"
2249
+ this.mode = this.kernel.store.get("mode") || "desktop"
2264
2250
 
2265
2251
  // when loaded in electron but in minimal mode,
2266
2252
  // the app is loaded in the web so the agent should be "web"
2267
2253
  if (this.agent === "electron") {
2268
- if (this.mode === "minimal") {
2254
+ if (this.mode === "minimal" || this.mode === "background") {
2269
2255
  this.agent = "web"
2270
2256
  }
2271
2257
  }
@@ -2504,17 +2490,70 @@ class Server {
2504
2490
  return true
2505
2491
  }
2506
2492
  }
2507
- async getPluginGlobal(req, filepath) {
2493
+ async terminals(filepath) {
2494
+ let venvs = await Util.find_venv(filepath)
2495
+ let terminal
2496
+ if (venvs.length > 0) {
2497
+ let terminals = []
2498
+ try {
2499
+ for(let i=0; i<venvs.length; i++) {
2500
+ let venv = venvs[i]
2501
+ let parsed = path.parse(venv)
2502
+ terminals.push(this.renderShell(filepath, i, 0, {
2503
+ icon: "fa-brands fa-python",
2504
+ title: "Python virtual environment",
2505
+ subtitle: this.kernel.path("api", parsed.name),
2506
+ text: `[venv] ${parsed.name}`,
2507
+ type: "Start",
2508
+ shell: {
2509
+ venv: venv,
2510
+ input: true,
2511
+ }
2512
+ }))
2513
+ }
2514
+ } catch (e) {
2515
+ console.log(e)
2516
+ }
2517
+ terminal = {
2518
+ icon: "fa-solid fa-terminal",
2519
+ title: "Web Terminal",
2520
+ subtitle: "Open the terminal in the browser",
2521
+ menu: terminals
2522
+ }
2523
+ } else {
2524
+ terminal = {
2525
+ icon: "fa-solid fa-terminal",
2526
+ title: "Web terminal",
2527
+ subtitle: "Work with the terminal directly in the browser",
2528
+ menu: [this.renderShell(filepath, 0, 0, {
2529
+ icon: "fa-solid fa-terminal",
2530
+ title: "Terminal",
2531
+ subtitle: filepath,
2532
+ text: `Terminal`,
2533
+ type: "Start",
2534
+ shell: {
2535
+ input: true
2536
+ }
2537
+ })]
2538
+ }
2539
+ }
2540
+ return terminal
2541
+ }
2542
+ async getPluginGlobal(req, config, terminal, filepath) {
2508
2543
  // if (!this.kernel.plugin.config) {
2509
2544
  // await this.kernel.plugin.init()
2510
2545
  // }
2511
- if (this.kernel.plugin.config) {
2546
+ if (config) {
2547
+
2548
+ let c = structuredClone(config)
2549
+ let menu = structuredClone(terminal.menu)
2550
+ c.menu = c.menu.concat(menu)
2512
2551
  try {
2513
2552
  let info = new Info(this.kernel)
2514
2553
  info.cwd = () => {
2515
2554
  return filepath
2516
2555
  }
2517
- let menu = this.kernel.plugin.config.menu.map((item) => {
2556
+ let menu = c.menu.map((item) => {
2518
2557
  return {
2519
2558
  params: {
2520
2559
  cwd: filepath
@@ -2547,34 +2586,26 @@ class Server {
2547
2586
  return null
2548
2587
  }
2549
2588
  }
2550
- async getPlugin(req, name) {
2551
- if (this.kernel.plugin.config) {
2589
+ async getPlugin(req, config, name) {
2590
+ if (config) {
2591
+ let c = structuredClone(config)
2552
2592
  try {
2553
- if (this.kernel.plugin.cache[name]) {
2554
- let cached = this.kernel.plugin.cache[name]
2555
- return cached
2556
- } else {
2557
- // let info = new Info(this.kernel)
2558
- // info.caller = () => {
2559
- // return this.kernel.path("api", name, "pinokio.js")
2560
- // }
2561
- // let menu = await this.kernel.plugin.config.menu(this.kernel, info)
2562
2593
 
2563
- let menu = this.kernel.plugin.config.menu.map((item) => {
2564
- return {
2565
- params: {
2566
- //cwd: this.kernel.path("api", name, "pinokio.js")
2567
- cwd: this.kernel.path("api", name)
2568
- },
2569
- ...item
2570
- }
2571
- })
2572
- let plugin = { menu }
2573
- let uri = this.kernel.path("api")
2574
- await this.renderMenu(req, uri, name, plugin, [])
2575
- this.kernel.plugin.cache[name] = plugin
2576
- return plugin
2577
- }
2594
+ let filepath = this.kernel.path("api", name)
2595
+ let terminal = await this.terminals(filepath)
2596
+ c.menu = c.menu.concat(terminal.menu)
2597
+ let menu = c.menu.map((item) => {
2598
+ return {
2599
+ params: {
2600
+ cwd: filepath,
2601
+ },
2602
+ ...item
2603
+ }
2604
+ })
2605
+ let plugin = { menu }
2606
+ let uri = this.kernel.path("api")
2607
+ await this.renderMenu(req, uri, name, plugin, [])
2608
+ return plugin
2578
2609
  } catch (e) {
2579
2610
  console.log("getPlugin ERROR", e)
2580
2611
  return null
@@ -2767,7 +2798,7 @@ class Server {
2767
2798
  if (this.kernel.homedir) {
2768
2799
  let ex = await this.kernel.exists(this.kernel.homedir, "ENVIRONMENT")
2769
2800
  if (!ex) {
2770
- let str = await Environment.ENV("system", this.kernel.homedir)
2801
+ let str = await Environment.ENV("system", this.kernel.homedir, this.kernel)
2771
2802
  await fs.promises.writeFile(path.resolve(this.kernel.homedir, "ENVIRONMENT"), str)
2772
2803
  }
2773
2804
  }
@@ -2869,11 +2900,45 @@ class Server {
2869
2900
  this.app.use(express.static(path.resolve(__dirname, 'public')));
2870
2901
  this.app.use("/web", express.static(path.resolve(__dirname, "..", "..", "web")))
2871
2902
  this.app.set('view engine', 'ejs');
2903
+ this.app.use((req, res, next) => {
2904
+ let protocol = req.get('X-Forwarded-Proto') || "http"
2905
+ req.$source = {
2906
+ protocol,
2907
+ host: req.get("host")
2908
+ }
2909
+ next()
2910
+ })
2872
2911
  if (this.kernel.homedir) {
2873
2912
  this.app.set("views", [
2874
2913
  this.kernel.path("web/views"),
2875
2914
  path.resolve(__dirname, "views")
2876
2915
  ])
2916
+ let serve = express.static(this.kernel.homedir, { fallthrough: true })
2917
+ let http_serve = express.static(this.kernel.homedir, {
2918
+ redirect: true,
2919
+ })
2920
+ let https_serve = express.static(this.kernel.homedir, {
2921
+ redirect: false,
2922
+ })
2923
+ this.app.use('/asset', serve, serveIndex(this.kernel.homedir, {'icons': true}))
2924
+ this.app.use('/asset', (req, res, next) => {
2925
+ if (req.path.match(/\.(png|jpg|jpeg|gif|ico|svg)$/)) {
2926
+ res.sendFile(path.resolve(__dirname, 'public', 'pinokio-black.png'));
2927
+ } else {
2928
+ next();
2929
+ }
2930
+ });
2931
+ this.app.use("/asset", async (req, res, next) => {
2932
+ let asset_path = this.kernel.path(req.path.slice(1), "index.html")
2933
+ let exists = await this.exists(asset_path)
2934
+ if (exists) {
2935
+ return res.sendFile(asset_path)
2936
+ } else {
2937
+ let chunks = req.path.slice(1).split("/")
2938
+ let parent_path = chunks.slice(0, -1).join("/")
2939
+ res.redirect("/asset/" + parent_path)
2940
+ }
2941
+ })
2877
2942
  } else {
2878
2943
  this.app.set("views", [
2879
2944
  path.resolve(__dirname, "views")
@@ -2891,16 +2956,106 @@ class Server {
2891
2956
  };
2892
2957
  next();
2893
2958
  });
2894
- this.app.use((req, res, next) => {
2895
- let protocol = req.get('X-Forwarded-Proto') || "http"
2896
- req.$source = {
2897
- protocol,
2898
- host: req.get("host")
2959
+ /*
2960
+ this.app.get("/asset/*", ex((req, res) => {
2961
+ let pathComponents = req.params[0].split("/")
2962
+ let filepath = this.kernel.path(...pathComponents)
2963
+ console.log("req.originalUrl", req.originalUrl)
2964
+ console.log("pathComponents", pathComponents)
2965
+ // if (pathComponents.length === 2 && pathComponents[0] === "api") {
2966
+ // // ex: /asset/api/comfy.git
2967
+ // filepath = path.resolve(filepath, "index.html")
2968
+ // }
2969
+ try {
2970
+ if (req.query.frame) {
2971
+ let m = mime.lookup(filepath)
2972
+ res.type("text/plain")
2973
+ }
2974
+ //res.setHeader('Content-Disposition', 'inline');
2975
+ res.sendFile(filepath)
2976
+ } catch (e) {
2977
+ res.status(404).send(e.message);
2899
2978
  }
2900
- next()
2901
- })
2979
+ }))
2980
+ */
2981
+ this.app.get("/tools", ex(async (req, res) => {
2982
+ let list = this.getPeers()
2983
+ let installs = []
2984
+ for(let key in this.kernel.bin.installed) {
2985
+ let installed = this.kernel.bin.installed[key]
2986
+ let modules = Array.from(installed)
2987
+ if (modules.length > 0) {
2988
+ installs.push({
2989
+ package_manager: key,
2990
+ modules,
2991
+ })
2992
+ }
2993
+ }
2994
+ // add minimal
2995
+ const bundle_names = ["dev", "advanced_dev", "ai", "network"]
2996
+ let bundles = []
2997
+ let pending
2998
+ for(let bundle_name of bundle_names) {
2999
+ let result = await this.kernel.bin.check({
3000
+ bin: this.kernel.bin.preset(bundle_name)
3001
+ })
3002
+ if (result.requirements_pending) {
3003
+ pending = true
3004
+ }
3005
+ bundles.push({
3006
+ name: bundle_name,
3007
+ setup: "/setup/" + bundle_name + "?callback=/tools",
3008
+ ...result
3009
+ })
3010
+ }
3011
+ console.log(JSON.stringify(bundles, null, 2))
3012
+ res.render("tools", {
3013
+ pending,
3014
+ installs,
3015
+ bundles,
3016
+ version: this.version,
3017
+ portal: this.portal,
3018
+ logo: this.logo,
3019
+ theme: this.theme,
3020
+ agent: this.agent,
3021
+ list,
3022
+ })
3023
+ }))
3024
+ this.app.get("/screenshots", ex(async (req, res) => {
3025
+ let list = this.getPeers()
3026
+ res.render("screenshots", {
3027
+ version: this.version,
3028
+ portal: this.portal,
3029
+ logo: this.logo,
3030
+ theme: this.theme,
3031
+ agent: this.agent,
3032
+ list,
3033
+ })
3034
+ }))
3035
+ this.app.get("/columns", ex(async (req, res) => {
3036
+ res.render("columns", {
3037
+ theme: this.theme,
3038
+ agent: this.agent,
3039
+ src: req.get('Referrer')
3040
+ })
3041
+ }))
3042
+ this.app.get("/rows", ex(async (req, res) => {
3043
+ res.render("rows", {
3044
+ theme: this.theme,
3045
+ agent: this.agent,
3046
+ src: req.get('Referrer')
3047
+ })
3048
+ }))
2902
3049
 
2903
3050
 
3051
+ this.app.get("/container", ex(async (req, res) => {
3052
+ res.render("container", {
3053
+ theme: this.theme,
3054
+ agent: this.agent,
3055
+ src: req.query.url
3056
+ })
3057
+ }))
3058
+
2904
3059
  //let home = this.kernel.homedir
2905
3060
  //let home = this.kernel.store.get("home")
2906
3061
  this.app.get("/launch", ex(async (req, res) => {
@@ -2915,7 +3070,7 @@ class Server {
2915
3070
  let url = req.query.url
2916
3071
  let u = new URL(url)
2917
3072
  let host = u.host
2918
- let env = await Environment.get(this.kernel.homedir)
3073
+ let env = await Environment.get(this.kernel.homedir, this.kernel)
2919
3074
  let autolaunch = false
2920
3075
  if (env && env.PINOKIO_ONDEMAND_AUTOLAUNCH === "1") {
2921
3076
  autolaunch = true
@@ -2932,18 +3087,17 @@ class Server {
2932
3087
  // otherwise => redirect
2933
3088
 
2934
3089
 
2935
- if (chunks.length > 2) {
3090
+ if (chunks.length >= 2) {
2936
3091
 
2937
3092
  let apipath = this.kernel.path("api")
2938
3093
  let files = await fs.promises.readdir(apipath, { withFileTypes: true })
2939
- let folders = files.filter((f) => {
2940
- return f.isDirectory()
2941
- }).map((x) => {
2942
- return x.name
2943
- })
2944
-
2945
- console.log({ folders })
2946
-
3094
+ let folders = []
3095
+ for(let file of files) {
3096
+ let type = await Util.file_type(apipath, file)
3097
+ if (type.directory) {
3098
+ folders.push(file.name)
3099
+ }
3100
+ }
2947
3101
 
2948
3102
  let matched = false
2949
3103
  for(let folder of folders) {
@@ -2988,7 +3142,15 @@ class Server {
2988
3142
  let exists = await this.exists(api_path)
2989
3143
  if (exists) {
2990
3144
  let meta = await this.kernel.api.meta(name)
3145
+ let launcher = await this.kernel.api.launcher(name)
3146
+ let pinokio = launcher.script
3147
+ let launchable = false
3148
+ if (pinokio && pinokio.menu && pinokio.menu.length > 0) {
3149
+ launchable = true
3150
+ }
2991
3151
  res.render("start", {
3152
+ url,
3153
+ launchable,
2992
3154
  autolaunch,
2993
3155
  logo: this.logo,
2994
3156
  theme: this.theme,
@@ -3001,6 +3163,8 @@ class Server {
3001
3163
  }
3002
3164
  }
3003
3165
  res.render("start", {
3166
+ url,
3167
+ launchable: false,
3004
3168
  autolaunch,
3005
3169
  logo: this.logo,
3006
3170
  theme: this.theme,
@@ -3061,7 +3225,7 @@ class Server {
3061
3225
  }
3062
3226
  let system_env = {}
3063
3227
  if (this.kernel.homedir) {
3064
- system_env = await Environment.get(this.kernel.homedir)
3228
+ system_env = await Environment.get(this.kernel.homedir, this.kernel)
3065
3229
  }
3066
3230
  let configArray = [{
3067
3231
  key: "home",
@@ -3083,7 +3247,7 @@ class Server {
3083
3247
  }, {
3084
3248
  key: "mode",
3085
3249
  val: this.mode,
3086
- options: ["full", "minimal"]
3250
+ options: ["desktop", "background"]
3087
3251
  }, {
3088
3252
  key: "HTTP_PROXY",
3089
3253
  val: (system_env.HTTP_PROXY || ""),
@@ -3109,7 +3273,10 @@ class Server {
3109
3273
  drive: path.resolve(this.kernel.homedir, "drive"),
3110
3274
  }
3111
3275
  }
3276
+ let list = this.getPeers()
3112
3277
  res.render("settings", {
3278
+ current_host: this.kernel.peer.host,
3279
+ list,
3113
3280
  platform,
3114
3281
  version: this.version,
3115
3282
  portal: this.portal,
@@ -3127,75 +3294,21 @@ class Server {
3127
3294
 
3128
3295
  let apipath = this.kernel.path("api")
3129
3296
  let files = await fs.promises.readdir(apipath, { withFileTypes: true })
3130
- let folders = files.filter((f) => {
3131
- return f.isDirectory()
3132
- }).map((x) => {
3133
- return x.name
3134
- })
3297
+
3298
+ let folders = []
3299
+ for(let file of files) {
3300
+ let type = await Util.file_type(apipath, file)
3301
+ if (type.directory) {
3302
+ folders.push(file.name)
3303
+ }
3304
+ }
3135
3305
  let meta = {}
3136
3306
  for(let folder of folders) {
3137
3307
  meta[folder] = await this.kernel.api.meta(folder)
3138
- // let p = path.resolve(apipath, folder, "pinokio.js")
3139
- // let pinokio = (await this.kernel.loader.load(p)).resolved
3140
- // let p2 = path.resolve(apipath, folder, "pinokio_meta.json")
3141
- // let pinokio2 = (await this.kernel.loader.load(p2)).resolved
3142
- //
3143
- // meta[folder] = Object.assign({}, pinokio, pinokio2)
3144
- // meta[folder].iconpath = meta[folder].icon ? path.resolve(apipath, folder, meta[folder].icon) : null
3145
- // meta[folder].icon = meta[folder].icon ? `/api/${folder}/${meta[folder].icon}?raw=true` : null
3146
- // meta[folder].path = path.resolve(apipath, folder)
3147
-
3148
-
3149
- // if (pinokio) {
3150
- // meta[folder] = {
3151
- // title: pinokio.title,
3152
- // description: pinokio.description,
3153
- // icon: pinokio.icon ? `/api/${folder}/${pinokio.icon}?raw=true` : null
3154
- // }
3155
- // }
3156
- // if (pinokio2) {
3157
- // if (pinokio2.title) meta[folder].title = pinokio2.title
3158
- // if (pinokio2.description) meta[folder].description = pinokio2.description
3159
- // if (pinokio2.icon) meta[folder].icon = pinokio2.icon
3160
- // }
3161
3308
  }
3162
3309
  await this.render(req, res, [], meta)
3163
- // if (this.kernel.bin.all_installed) {
3164
- // this.started = true
3165
- // let apipath = this.kernel.path("api")
3166
- // let files = await fs.promises.readdir(apipath, { withFileTypes: true })
3167
- // let folders = files.filter((f) => {
3168
- // return f.isDirectory()
3169
- // }).map((x) => {
3170
- // return x.name
3171
- // })
3172
- // let meta = {}
3173
- // for(let folder of folders) {
3174
- // let p = path.resolve(apipath, folder, "pinokio.js")
3175
- // let pinokio = (await this.kernel.loader.load(p)).resolved
3176
- // if (pinokio) {
3177
- // meta[folder] = {
3178
- // title: pinokio.title,
3179
- // description: pinokio.description,
3180
- // icon: pinokio.icon ? `/api/${folder}/${pinokio.icon}?raw=true` : null
3181
- // }
3182
- // }
3183
- // }
3184
- // await this.render(req, res, [], meta)
3185
- // } else {
3186
- // // get all the "start" scripts from pinokio.json
3187
- // // render installer page
3188
- // this.started = true
3189
- // let home = this.kernel.homedir ? this.kernel.homedir : path.resolve(os.homedir(), "pinokio")
3190
- // res.render("bootstrap", {
3191
- // home,
3192
- // agent: this.agent,
3193
- // })
3194
- // }
3195
3310
  }))
3196
3311
 
3197
- // this.app.get("/init/:name", ex(async (req, res) => {
3198
- // console.log("Rnder init", req.params.name)
3199
3312
  this.app.get("/init", ex(async (req, res) => {
3200
3313
  /*
3201
3314
  option 1: new vs. clone
@@ -3222,17 +3335,15 @@ class Server {
3222
3335
  return
3223
3336
  }
3224
3337
 
3225
- // console.log("this.kernel.proto.init")
3226
- // await this.kernel.proto.init()
3227
- let list = this.getPeerInfo()
3338
+ let list = this.getPeers()
3228
3339
  let ai = await this.kernel.proto.ai()
3229
- ai.push({
3340
+ ai = [{
3230
3341
  title: "Use your own AI recipe",
3231
3342
  description: "Enter your own markdown instruction for AI",
3343
+ placeholder: "(example: 'build a launcher for https://github.com/comfyanonymous/ComfyUI)",
3232
3344
  meta: {},
3233
3345
  content: ""
3234
- })
3235
- console.log("ai", ai)
3346
+ }].concat(ai)
3236
3347
  res.render("init/index", {
3237
3348
  list,
3238
3349
  ai,
@@ -3281,22 +3392,71 @@ class Server {
3281
3392
  - x
3282
3393
  */
3283
3394
  this.app.get("/connect", ex(async (req, res) => {
3395
+ let list = this.getPeers()
3396
+ let current_urls = await this.current_urls(req.originalUrl.slice(1))
3397
+ let items = [{
3398
+ image: "/pinokio-black.png",
3399
+ name: "pinokio",
3400
+ title: "pinokio.co",
3401
+ description: "Connect with pinokio.co",
3402
+ url: "/connect/pinokio"
3403
+ }, {
3404
+ icon: "fa-brands fa-square-x-twitter",
3405
+ name: "x",
3406
+ title: "x.com",
3407
+ description: "Connect with X.com",
3408
+ url: "/connect/x"
3409
+ }, {
3410
+ emoji: "🤗",
3411
+ name: "huggingface",
3412
+ title: "huggingface.co",
3413
+ description: "Connect with huggingface.co",
3414
+ url: "/connect/huggingface"
3415
+ }, {
3416
+ icon: "fa-brands fa-github",
3417
+ name: "github",
3418
+ title: "github.com",
3419
+ description: "Connect with GitHub.com",
3420
+ url: "/github"
3421
+ }]
3422
+ let github_hosts = await this.get_github_hosts()
3423
+ for(let i=0; i<items.length; i++) {
3424
+ try {
3425
+ if (items[i].name === "github") {
3426
+ if (github_hosts.length > 0) {
3427
+ items[i].profile = {
3428
+ icon: "fa-brands fa-github",
3429
+ items: [{
3430
+ key: "config",
3431
+ val: github_hosts
3432
+ }]
3433
+ }
3434
+ items[i].description = `<i class="fa-solid fa-circle-check"></i> Connected with ${items[i].title}`
3435
+ items[i].connected = true
3436
+ }
3437
+ } else {
3438
+ const config = this.kernel.connect.config[items[i].name]
3439
+ if (config) {
3440
+ let profile = await this.kernel.connect.profile(items[i].name)
3441
+ if (profile) {
3442
+ items[i].profile = profile
3443
+ items[i].description = `<i class="fa-solid fa-circle-check"></i> Connected with ${items[i].title}`
3444
+ items[i].connected = true
3445
+ }
3446
+ }
3447
+ }
3448
+ } catch (e) {
3449
+ }
3450
+ }
3284
3451
  res.render(`connect`, {
3452
+ current_urls,
3453
+ current_host: this.kernel.peer.host,
3454
+ list,
3285
3455
  portal: this.portal,
3286
3456
  logo: this.logo,
3287
3457
  theme: this.theme,
3288
3458
  agent: this.agent,
3289
- items: [{
3290
- icon: "fa-brands fa-square-x-twitter",
3291
- title: "X",
3292
- description: "Connect with X.com",
3293
- url: "/connect/x"
3294
- }, {
3295
- icon: "fa-brands fa-github",
3296
- title: "GitHub",
3297
- description: "Connect with GitHub.com",
3298
- url: "/github"
3299
- }]
3459
+ items,
3300
3460
  })
3301
3461
  }))
3302
3462
  /*
@@ -3353,17 +3513,32 @@ class Server {
3353
3513
  }
3354
3514
 
3355
3515
 
3356
- let readme = await this.kernel.connect[req.params.provider].readme()
3357
- res.render(`connect/${req.params.provider}`, {
3516
+ let readme = ""
3517
+ let id = ""
3518
+ try {
3519
+ readme = await this.kernel.connect[req.params.provider].readme()
3520
+ id = this.kernel.connect[req.params.provider].id
3521
+ } catch (e) {
3522
+ }
3523
+ //res.render(`connect/${req.params.provider}`, {
3524
+ const config = this.kernel.connect.config[req.params.provider]
3525
+ res.render(`connect/index`, {
3526
+ protocol: req.$source.protocol,
3527
+ name: req.params.provider,
3528
+ config,
3358
3529
  portal: this.portal,
3359
3530
  logo: this.logo,
3360
3531
  theme: this.theme,
3361
3532
  agent: this.agent,
3362
- id: this.kernel.connect[req.params.provider].id,
3533
+ id,
3363
3534
  readme
3364
3535
  })
3365
3536
  }))
3366
3537
 
3538
+ this.app.get("/connect/:provider/profile", ex(async (req, res) => {
3539
+ let response = await this.kernel.connect.profile(req.params.provider, req.body)
3540
+ res.send(response)
3541
+ }))
3367
3542
  /*
3368
3543
  * POST /connect/x/login => login and acquire auth token
3369
3544
  * POST /connect/x/logout => loout
@@ -3548,17 +3723,8 @@ class Server {
3548
3723
  let md = await fs.promises.readFile(path.resolve(__dirname, "..", "kernel/connect/providers/github/README.md"), "utf8")
3549
3724
  let readme = marked.parse(md)
3550
3725
 
3551
- let hosts = ""
3552
- let hosts_file = this.kernel.path("config/gh/hosts.yml")
3553
- let e = await this.exists(hosts_file)
3554
- console.log({ hosts_file, e })
3555
- if (e) {
3556
- hosts = await fs.promises.readFile(hosts_file, "utf8")
3557
- console.log( { hosts: `#${hosts}#` })
3558
- if (hosts.startsWith("{}")) {
3559
- hosts = ""
3560
- }
3561
- }
3726
+ let hosts = await this.get_github_hosts()
3727
+
3562
3728
  console.log("hosts", hosts)
3563
3729
 
3564
3730
  let items
@@ -3865,8 +4031,22 @@ class Server {
3865
4031
  agent: this.agent,
3866
4032
  })
3867
4033
  }))
4034
+ this.app.post("/plugin/update_spec", ex(async (req, res) => {
4035
+ try {
4036
+ let filepath = req.body.filepath
4037
+ let content = req.body.spec
4038
+ let spec_path = path.resolve(filepath, "SPEC.md")
4039
+ await fs.promises.writeFile(spec_path, content)
4040
+ res.json({
4041
+ success: true
4042
+ })
4043
+ } catch (e) {
4044
+ res.error({
4045
+ error: e.stack
4046
+ })
4047
+ }
4048
+ }))
3868
4049
  this.app.post("/plugin/update", ex(async (req, res) => {
3869
- console.time("/plugin/update")
3870
4050
  try {
3871
4051
  await this.kernel.exec({
3872
4052
  message: "git pull",
@@ -3874,7 +4054,6 @@ class Server {
3874
4054
  }, (e) => {
3875
4055
  console.log(e)
3876
4056
  })
3877
- console.timeEnd("/plugin/update")
3878
4057
  res.json({
3879
4058
  success: true
3880
4059
  })
@@ -3946,8 +4125,13 @@ class Server {
3946
4125
  }
3947
4126
  } catch (e) {
3948
4127
  }
4128
+ console.log("PEER INFO", JSON.stringify(this.kernel.peer.info[host], null, 2))
3949
4129
 
3950
4130
  let installed = this.kernel.peer.info[host].installed
4131
+ let serverless_mapping = this.kernel.peer.info[host].rewrite_mapping
4132
+ let serverless = Object.keys(serverless_mapping).map((name) => {
4133
+ return serverless_mapping[name]
4134
+ })
3951
4135
  let current_urls = await this.current_urls(req.originalUrl.slice(1))
3952
4136
  res.render("net", {
3953
4137
  selected_name: req.params.name,
@@ -3959,6 +4143,7 @@ class Server {
3959
4143
  theme: this.theme,
3960
4144
  processes,
3961
4145
  installed,
4146
+ serverless,
3962
4147
  error: null,
3963
4148
  list,
3964
4149
  host,
@@ -3996,8 +4181,6 @@ class Server {
3996
4181
  })
3997
4182
  }
3998
4183
 
3999
- console.log("Info", this.kernel.peer.info)
4000
-
4001
4184
  // if (peers.length === 0) {
4002
4185
  // console.log("network not yet ready")
4003
4186
  // res.redirect("/")
@@ -4052,11 +4235,13 @@ class Server {
4052
4235
  // App sharing
4053
4236
  let apipath = this.kernel.path("api")
4054
4237
  let files = await fs.promises.readdir(apipath, { withFileTypes: true })
4055
- let folders = files.filter((f) => {
4056
- return f.isDirectory()
4057
- }).map((x) => {
4058
- return x.name
4059
- })
4238
+ let folders = []
4239
+ for(let file of files) {
4240
+ let type = await Util.file_type(apipath, file)
4241
+ if (type.directory) {
4242
+ folders.push(file.name)
4243
+ }
4244
+ }
4060
4245
  let apps = []
4061
4246
  for(let folder of folders) {
4062
4247
  let meta = await this.kernel.api.meta(folder)
@@ -4077,14 +4262,16 @@ class Server {
4077
4262
 
4078
4263
  let processes = []
4079
4264
  try {
4080
- processes = current_peer.router_info
4081
- for(let i=0; i<processes.length; i++) {
4082
- if (!processes[i].icon) {
4083
- if (protocol === "https") {
4084
- processes[i].icon = processes[i].https_icon
4085
- } else {
4086
- // http
4087
- processes[i].icon = processes[i].http_icon
4265
+ if (current_peer) {
4266
+ processes = current_peer.router_info
4267
+ for(let i=0; i<processes.length; i++) {
4268
+ if (!processes[i].icon) {
4269
+ if (protocol === "https") {
4270
+ processes[i].icon = processes[i].https_icon
4271
+ } else {
4272
+ // http
4273
+ processes[i].icon = processes[i].http_icon
4274
+ }
4088
4275
  }
4089
4276
  }
4090
4277
  }
@@ -4101,8 +4288,12 @@ class Server {
4101
4288
 
4102
4289
  let list = this.getPeers()
4103
4290
  let installed = this.kernel.peer.info && this.kernel.peer.info[host] ? this.kernel.peer.info[host].installed : []
4104
- res.render("network", {
4105
4291
 
4292
+ let static_routes = Object.keys(this.kernel.router.rewrite_mapping).map((key) => {
4293
+ return this.kernel.router.rewrite_mapping[key]
4294
+ })
4295
+ res.render("network", {
4296
+ static_routes,
4106
4297
  host,
4107
4298
  favicons,
4108
4299
  titles,
@@ -4142,78 +4333,16 @@ class Server {
4142
4333
  let str = await fs.promises.readFile(req.query.logpath, "utf8")
4143
4334
  res.send(str)
4144
4335
  }))
4145
- this.app.get("/state/:type/:name", ex(async (req,res) => {
4146
- let selected = null
4147
- try {
4148
- selected = this.selected[req.params.name][req.params.type]
4149
- } catch (e) {
4150
- }
4151
- res.json({
4152
- selected,
4153
- })
4154
- }))
4155
- this.app.post("/state", ex(async (req, res) => {
4156
- /*
4157
- req.body := {
4158
- name: <name>,
4159
- type: "browse"|"run",
4160
- method: "toggleMenu"|"select",
4161
- params: {
4162
- <url>,
4163
- }
4164
- }
4165
- */
4166
- if (req.body.method === "select") {
4167
- if (!this.selected[req.body.name]) {
4168
- this.selected[req.body.name] = {}
4169
- }
4170
- this.selected[req.body.name][req.body.type] = req.body.params.url
4171
- } else if (req.body.method === "toggleMenu") {
4172
- if (!this.menu_hidden[req.body.name]) {
4173
- this.menu_hidden[req.body.name] = {}
4174
- }
4175
- if (this.menu_hidden[req.body.name][req.body.type]) {
4176
- this.menu_hidden[req.body.name][req.body.type] = false
4177
- } else {
4178
- this.menu_hidden[req.body.name][req.body.type] = true
4179
- }
4180
- }
4181
- res.json({
4182
- success: true
4183
- })
4184
- }))
4185
4336
  this.app.post("/mkdir", ex(async (req, res) => {
4186
4337
  let folder = req.body.folder
4187
4338
  let folder_path = path.resolve(this.kernel.api.userdir, req.body.folder)
4188
4339
  try {
4189
4340
  // mkdir
4190
4341
  await fs.promises.mkdir(folder_path)
4191
-
4192
- // create basic pinokio.json
4193
-
4194
- // add default icon
4195
-
4196
4342
  let default_icon_path = path.resolve(__dirname, "public/pinokio-black.png")
4197
4343
  let icon_path = path.resolve(folder_path, "icon.png")
4198
4344
  await fs.promises.cp(default_icon_path, icon_path)
4199
-
4200
-
4201
- // write title/description to pinokio.json
4202
- // let meta_path = path.resolve(folder_path, "pinokio.json")
4203
- // let meta = {
4204
- // title: "No title",
4205
- // description: "",
4206
- // icon: "icon.png",
4207
- // plugin: {
4208
- // menu: []
4209
- // }
4210
- // }
4211
- // console.log({ folder_path, default_icon_path, icon_path, meta_path, meta })
4212
- // await fs.promises.writeFile(meta_path, JSON.stringify(meta, null, 2))
4213
-
4214
4345
  res.json({
4215
- //success: "/pinokio/browser/"+folder
4216
- //success: "/p/"+folder
4217
4346
  success: "/init/"+folder
4218
4347
  })
4219
4348
  } catch (e) {
@@ -4296,7 +4425,7 @@ class Server {
4296
4425
  */
4297
4426
  if (req.body.type) {
4298
4427
  if (req.body.type === "local") {
4299
- let env = await Environment.get(this.kernel.homedir)
4428
+ let env = await Environment.get(this.kernel.homedir, this.kernel)
4300
4429
  if (env && env.PINOKIO_SHARE_LOCAL_PORT) {
4301
4430
  let port = env.PINOKIO_SHARE_LOCAL_PORT.trim()
4302
4431
  if (port.length > 0) {
@@ -4325,244 +4454,217 @@ class Server {
4325
4454
  res.json({ error: "type must be 'local' or 'cloudflare'" })
4326
4455
  }
4327
4456
  }))
4328
- this.app.get("/prototype/run/*", ex(async (req, res) => {
4329
- let pathComponents = req.params[0].split("/").concat("pinokio.js")
4330
- let config = await this.kernel.api.meta({ path: req.query.path })
4331
- let pinokiojson_path = path.resolve(req.query.path, "pinokio.json")
4332
- let pinokiojson = await this.kernel.require(pinokiojson_path)
4333
- if (pinokiojson) {
4334
- if (pinokiojson.plugin) {
4335
- if (pinokiojson.plugin.menu) {
4336
- } else {
4337
- pinokiojson.plugin.menu = []
4338
- await fs.promises.writeFile(pinokiojson_path, JSON.stringify(pinokiojson, null, 2))
4339
- }
4340
- } else {
4341
- pinokiojson.plugin = { menu: [] }
4342
- await fs.promises.writeFile(pinokiojson_path, JSON.stringify(pinokiojson, null, 2))
4343
- }
4344
- } else {
4345
- pinokiojson = {
4346
- plugin: {
4347
- menu: []
4348
- }
4349
- }
4350
- await fs.promises.writeFile(pinokiojson_path, JSON.stringify(pinokiojson, null, 2))
4351
- }
4352
- req.base = this.kernel.path("prototype")
4353
- req.query.callback = config.ui
4354
- //req.query.callback = config.browse
4355
- req.query.cwd = req.query.path
4356
- await this.render(req, res, pathComponents, null)
4357
- }))
4358
- this.app.get("/prototype/show/*", ex(async (req, res) => {
4359
- let name = req.params[0].split("/").filter((x) => { return x }).join("/")
4360
-
4361
- // print readme
4362
-
4363
-
4364
-
4365
-
4366
- let paths = req.params[0].split("/")
4367
- let item
4368
- let config = this.kernel.proto.config
4369
- for(let key of paths) {
4370
- config = config.menu[key]
4371
- }
4372
- console.log("config.shell", config.shell)
4373
- if (config.shell) {
4374
-
4375
- let rendered = this.kernel.template.render(config.shell, {})
4376
- let params = new URLSearchParams()
4377
- if (rendered.path) params.set("path", encodeURIComponent(rendered.path))
4378
- if (rendered.message) params.set("message", encodeURIComponent(rendered.message))
4379
- if (rendered.venv) params.set("venv", encodeURIComponent(rendered.venv))
4380
- if (rendered.input) params.set("input", true)
4381
- if (rendered.callback) params.set("callback", encodeURIComponent(rendered.callback))
4382
- if (rendered.kill) params.set("kill", encodeURIComponent(rendered.kill))
4383
- if (rendered.done) params.set("done", encodeURIComponent(rendered.done))
4384
- if (rendered.env) {
4385
- for(let key in rendered.env) {
4386
- let env_key = "env." + key
4387
- params.set(env_key, rendered.env[key])
4388
- }
4389
- }
4390
- if (rendered.conda) {
4391
- for(let key in rendered.conda) {
4392
- let conda_key = "conda." + key
4393
- params.set(conda_key, rendered.conda[key])
4394
- }
4395
- }
4396
- let shell_id = Math.floor("SH_" + 1000000000000 * Math.random())
4397
- let href = "/shell/" + shell_id + "?" + params.toString()
4398
- res.redirect(href)
4399
- } else {
4400
- let run_path = "/run/prototype/system/" + config.href + "?cwd=" + req.query.path
4401
- let readme_path = this.kernel.path("prototype/system", config.readme)
4402
- let md = await fs.promises.readFile(readme_path, "utf8")
4403
- let baseUrl = "/asset/prototype/system/" + (config.readme.split("/").slice(0, -1).join("/")) + "/"
4404
- let readme = marked.parse(md, {
4405
- baseUrl
4406
- })
4407
- res.render("prototype/show", {
4408
- run_path,
4409
- portal: this.portal,
4410
- readme,
4411
- logo: this.logo,
4412
- theme: this.theme,
4413
- agent: this.agent,
4414
- kernel: this.kernel,
4415
- })
4416
- }
4417
-
4418
- }))
4419
- this.app.get("/prototype", ex(async (req, res) => {
4420
- // load meta
4457
+ // this.app.get("/prototype/run/*", ex(async (req, res) => {
4458
+ // let pathComponents = req.params[0].split("/").concat("pinokio.js")
4421
4459
  // let config = await this.kernel.api.meta({ path: req.query.path })
4422
- // let items = this.kernel.proto.items
4423
- // if (req.query.type) {
4424
- // items = this.kernel.proto.items.filter(item => item.type === req.query.type)
4460
+ // let pinokiojson_path = path.resolve(req.query.path, "pinokio.json")
4461
+ // let pinokiojson = await this.kernel.require(pinokiojson_path)
4462
+ // if (pinokiojson) {
4463
+ // if (pinokiojson.plugin) {
4464
+ // if (pinokiojson.plugin.menu) {
4465
+ // } else {
4466
+ // pinokiojson.plugin.menu = []
4467
+ // await fs.promises.writeFile(pinokiojson_path, JSON.stringify(pinokiojson, null, 2))
4468
+ // }
4469
+ // } else {
4470
+ // pinokiojson.plugin = { menu: [] }
4471
+ // await fs.promises.writeFile(pinokiojson_path, JSON.stringify(pinokiojson, null, 2))
4472
+ // }
4473
+ // } else {
4474
+ // pinokiojson = {
4475
+ // plugin: {
4476
+ // menu: []
4477
+ // }
4478
+ // }
4479
+ // await fs.promises.writeFile(pinokiojson_path, JSON.stringify(pinokiojson, null, 2))
4425
4480
  // }
4426
- let title
4427
- let description
4428
- if (req.query.type === "init") {
4429
- title = "Initialize"
4430
- description = "Select an option to intitialize the project with. This may overwrite the folder if you already have existing files"
4431
- } else if (req.query.type === "extension") {
4432
- title = "Extensions"
4433
- description = "Add extension modules to the current folder"
4434
- }
4435
-
4436
- let config = structuredClone(this.kernel.proto.config)
4437
- config = this.renderMenu2(config, {
4438
- cwd: req.query.path,
4439
- href: "/prototype/show",
4440
- path: this.kernel.path("prototype/system"),
4441
- web_path: "/asset/prototype/system"
4442
- })
4443
-
4444
- // {
4445
- // "icon": "fa-solid fa-power-off",
4446
- // "text": "Run Default",
4447
- // "href": "/api/facefusion-pinokio.git/run.js?run=true&fullscreen=true&mode=Default",
4448
- // "params": {
4449
- // "run": true,
4450
- // "fullscreen": true,
4451
- // "mode": "Default"
4452
- // },
4453
- // "src": "/api/facefusion-pinokio.git/run.js",
4454
- // "html": "<i class=\"fa-solid fa-power-off\"></i> Run Default",
4455
- // "btn": "<i class=\"fa-solid fa-power-off\"></i> Run Default",
4456
- // "target": "@/api/facefusion-pinokio.git/run.js"
4457
- // },
4481
+ // req.base = this.kernel.path("prototype")
4482
+ // req.query.callback = config.ui
4483
+ // //req.query.callback = config.browse
4484
+ // req.query.cwd = req.query.path
4485
+ // await this.render(req, res, pathComponents, null)
4486
+ // }))
4487
+ // this.app.get("/prototype/show/*", ex(async (req, res) => {
4488
+ // let name = req.params[0].split("/").filter((x) => { return x }).join("/")
4458
4489
  //
4459
- res.render("prototype/index", {
4460
- title,
4461
- description,
4462
- config,
4463
- path: req.query.path,
4464
- portal: this.portal,
4465
- // items,
4466
- logo: this.logo,
4467
- platform: this.kernel.platform,
4468
- theme: this.theme,
4469
- agent: this.agent,
4470
- kernel: this.kernel,
4471
- })
4472
- }))
4473
- this.app.post("/prototype", this.upload.any(), ex(async (req, res) => {
4474
- try {
4475
- /*
4476
- {
4477
- title,
4478
- description,
4479
- path,
4480
- id
4481
- }
4482
- */
4483
- let formData = req.body
4484
- for(let key in req.files) {
4485
- let file = req.files[key]
4486
- formData[file.fieldname] = file.buffer
4487
- }
4488
- console.log({ formData })
4489
-
4490
-
4491
- // check if the path exists. if it does, return error
4492
- let api_path = this.kernel.path("api", formData.path)
4493
- let e = await this.exists(api_path)
4494
- if (e) {
4495
- console.log("e", e)
4496
- console.log("e.message", e.message)
4497
- res.status(500).json({ error: `The path ${api_path} already exists` })
4498
- } else {
4499
- await this.createMeta(formData)
4500
-
4501
- // run
4502
-
4503
- res.json({ success: true })
4504
- }
4505
- } catch (e) {
4506
- console.log("e", e)
4507
- console.log("e.message", e.message)
4508
- res.status(500).json({ error: e.message })
4509
- }
4510
- }))
4511
- this.app.post("/new", this.upload.any(), ex(async (req, res) => {
4512
- try {
4513
- /*
4514
- {
4515
- title,
4516
- description,
4517
- path,
4518
- id
4519
- }
4520
- */
4521
- let formData = req.body
4522
- for(let key in req.files) {
4523
- let file = req.files[key]
4524
- formData[file.fieldname] = file.buffer
4525
- }
4526
- console.log({ formData })
4527
-
4528
-
4529
- // check if the path exists. if it does, return error
4530
- let api_path = this.kernel.path("api", formData.path)
4531
- let e = await this.exists(api_path)
4532
- if (e) {
4533
- console.log("e", e)
4534
- console.log("e.message", e.message)
4535
- res.status(500).json({ error: `The path ${api_path} already exists` })
4536
- } else {
4537
- await this.createMeta(formData)
4538
- res.json({ success: true })
4539
- }
4540
- } catch (e) {
4541
- console.log("e", e)
4542
- console.log("e.message", e.message)
4543
- res.status(500).json({ error: e.message })
4544
- }
4545
- }))
4490
+ // // print readme
4491
+ //
4492
+ //
4493
+ //
4494
+ //
4495
+ // let paths = req.params[0].split("/")
4496
+ // let item
4497
+ // let config = this.kernel.proto.config
4498
+ // for(let key of paths) {
4499
+ // config = config.menu[key]
4500
+ // }
4501
+ // console.log("config.shell", config.shell)
4502
+ // if (config.shell) {
4503
+ //
4504
+ // let rendered = this.kernel.template.render(config.shell, {})
4505
+ // let params = new URLSearchParams()
4506
+ // if (rendered.path) params.set("path", encodeURIComponent(rendered.path))
4507
+ // if (rendered.message) params.set("message", encodeURIComponent(rendered.message))
4508
+ // if (rendered.venv) params.set("venv", encodeURIComponent(rendered.venv))
4509
+ // if (rendered.input) params.set("input", true)
4510
+ // if (rendered.callback) params.set("callback", encodeURIComponent(rendered.callback))
4511
+ // if (rendered.kill) params.set("kill", encodeURIComponent(rendered.kill))
4512
+ // if (rendered.done) params.set("done", encodeURIComponent(rendered.done))
4513
+ // if (rendered.env) {
4514
+ // for(let key in rendered.env) {
4515
+ // let env_key = "env." + key
4516
+ // params.set(env_key, rendered.env[key])
4517
+ // }
4518
+ // }
4519
+ // if (rendered.conda) {
4520
+ // for(let key in rendered.conda) {
4521
+ // let conda_key = "conda." + key
4522
+ // params.set(conda_key, rendered.conda[key])
4523
+ // }
4524
+ // }
4525
+ // let shell_id = Math.floor("SH_" + 1000000000000 * Math.random())
4526
+ // let href = "/shell/" + shell_id + "?" + params.toString()
4527
+ // res.redirect(href)
4528
+ // } else {
4529
+ // let run_path = "/run/prototype/system/" + config.href + "?cwd=" + req.query.path
4530
+ // let readme_path = this.kernel.path("prototype/system", config.readme)
4531
+ // let md = await fs.promises.readFile(readme_path, "utf8")
4532
+ // let baseUrl = "/asset/prototype/system/" + (config.readme.split("/").slice(0, -1).join("/")) + "/"
4533
+ // let readme = marked.parse(md, {
4534
+ // baseUrl
4535
+ // })
4536
+ // res.render("prototype/show", {
4537
+ // run_path,
4538
+ // portal: this.portal,
4539
+ // readme,
4540
+ // logo: this.logo,
4541
+ // theme: this.theme,
4542
+ // agent: this.agent,
4543
+ // kernel: this.kernel,
4544
+ // })
4545
+ // }
4546
+ //
4547
+ // }))
4548
+ // this.app.get("/prototype", ex(async (req, res) => {
4549
+ // let title
4550
+ // let description
4551
+ // if (req.query.type === "init") {
4552
+ // title = "Initialize"
4553
+ // description = "Select an option to intitialize the project with. This may overwrite the folder if you already have existing files"
4554
+ // } else if (req.query.type === "extension") {
4555
+ // title = "Extensions"
4556
+ // description = "Add extension modules to the current folder"
4557
+ // }
4558
+ //
4559
+ // let config = structuredClone(this.kernel.proto.config)
4560
+ // config = this.renderMenu2(config, {
4561
+ // cwd: req.query.path,
4562
+ // href: "/prototype/show",
4563
+ // path: this.kernel.path("prototype/system"),
4564
+ // web_path: "/asset/prototype/system"
4565
+ // })
4566
+ // res.render("prototype/index", {
4567
+ // title,
4568
+ // description,
4569
+ // config,
4570
+ // path: req.query.path,
4571
+ // portal: this.portal,
4572
+ // logo: this.logo,
4573
+ // platform: this.kernel.platform,
4574
+ // theme: this.theme,
4575
+ // agent: this.agent,
4576
+ // kernel: this.kernel,
4577
+ // })
4578
+ // }))
4579
+ // this.app.post("/prototype", this.upload.any(), ex(async (req, res) => {
4580
+ // try {
4581
+ // /*
4582
+ // {
4583
+ // title,
4584
+ // description,
4585
+ // path,
4586
+ // id
4587
+ // }
4588
+ // */
4589
+ // let formData = req.body
4590
+ // for(let key in req.files) {
4591
+ // let file = req.files[key]
4592
+ // formData[file.fieldname] = file.buffer
4593
+ // }
4594
+ // console.log({ formData })
4595
+ //
4596
+ //
4597
+ // // check if the path exists. if it does, return error
4598
+ // let api_path = this.kernel.path("api", formData.path)
4599
+ // let e = await this.exists(api_path)
4600
+ // if (e) {
4601
+ // console.log("e", e)
4602
+ // console.log("e.message", e.message)
4603
+ // res.status(500).json({ error: `The path ${api_path} already exists` })
4604
+ // } else {
4605
+ // await this.kernel.api.createMeta(formData)
4606
+ //
4607
+ // // run
4608
+ //
4609
+ // res.json({ success: true })
4610
+ // }
4611
+ // } catch (e) {
4612
+ // console.log("e", e)
4613
+ // console.log("e.message", e.message)
4614
+ // res.status(500).json({ error: e.message })
4615
+ // }
4616
+ // }))
4617
+ // this.app.post("/new", this.upload.any(), ex(async (req, res) => {
4618
+ // try {
4619
+ // /*
4620
+ // {
4621
+ // title,
4622
+ // description,
4623
+ // path,
4624
+ // id
4625
+ // }
4626
+ // */
4627
+ // let formData = req.body
4628
+ // for(let key in req.files) {
4629
+ // let file = req.files[key]
4630
+ // formData[file.fieldname] = file.buffer
4631
+ // }
4632
+ // console.log({ formData })
4633
+ //
4634
+ //
4635
+ // // check if the path exists. if it does, return error
4636
+ // let api_path = this.kernel.path("api", formData.path)
4637
+ // let e = await this.exists(api_path)
4638
+ // if (e) {
4639
+ // console.log("e", e)
4640
+ // console.log("e.message", e.message)
4641
+ // res.status(500).json({ error: `The path ${api_path} already exists` })
4642
+ // } else {
4643
+ // await this.kernel.api.createMeta(formData)
4644
+ // res.json({ success: true })
4645
+ // }
4646
+ // } catch (e) {
4647
+ // console.log("e", e)
4648
+ // console.log("e.message", e.message)
4649
+ // res.status(500).json({ error: e.message })
4650
+ // }
4651
+ // }))
4546
4652
  this.app.post("/env", ex(async (req, res) => {
4547
4653
  let fullpath = path.resolve(this.kernel.homedir, req.body.filepath, "ENVIRONMENT")
4548
4654
  let updated = req.body.vals
4549
4655
  let hosts = req.body.hosts
4550
- console.log("Util.update_env", { fullpath, filepath: req.body.filepath, updated })
4551
4656
  await Util.update_env(fullpath, updated)
4552
4657
  // for all environment variables that have hosts, save the key as well
4553
4658
  // hosts := { env_key: host }
4554
4659
  for(let env in hosts) {
4555
4660
  let host = hosts[env]
4556
4661
  let val = updated[env]
4557
- console.log({ hosts, updated, host, val })
4558
4662
  await this.kernel.kv.set(host.value, val, host.index)
4559
4663
  }
4560
4664
  res.json({})
4561
4665
  }))
4562
4666
  this.app.get("/env", ex(async (req, res) => {
4563
- let env_path = path.resolve(this.kernel.homedir)
4564
- await this.init_env(env_path)
4565
-
4667
+ await Environment.init({}, this.kernel)
4566
4668
  let filepath = path.resolve(this.kernel.homedir, "ENVIRONMENT")
4567
4669
  let editorpath = "/edit/ENVIRONMENT"
4568
4670
 
@@ -4581,41 +4683,47 @@ class Server {
4581
4683
  })
4582
4684
  }))
4583
4685
  this.app.get("/env/*", ex(async (req, res) => {
4584
-
4585
4686
  let env_path = req.params[0]
4586
- // let p = path.resolve(this.kernel.homedir, env_path, "pinokio.js")
4587
- // let config = (await this.kernel.loader.load(p)).resolved
4588
4687
  let api_path
4589
4688
  if (env_path.startsWith("api/")) {
4590
4689
  api_path = env_path.slice(4)
4591
4690
  }
4592
4691
  let config = await this.kernel.api.meta(api_path)
4692
+ let env_result
4593
4693
  if (config.run) {
4594
- await this.init_env(env_path, { no_inherit: true })
4694
+ env_result = await Environment.init({
4695
+ name: api_path,
4696
+ no_inherit: true
4697
+ }, this.kernel)
4595
4698
  } else {
4596
- await this.init_env(env_path)
4699
+ env_result = await Environment.init({
4700
+ name: api_path,
4701
+ }, this.kernel)
4597
4702
  }
4598
4703
 
4599
- let pathComponents = req.params[0].split("/")
4600
- let filepath = path.resolve(this.kernel.homedir, req.params[0], "ENVIRONMENT")
4704
+
4705
+ let filepath = env_result.env_path
4706
+
4707
+ // let pathComponents = req.params[0].split("/")
4708
+ // let filepath = path.resolve(this.kernel.homedir, req.params[0], "ENVIRONMENT")
4601
4709
 
4602
4710
  let items = []
4603
4711
  let e = await this.exists(filepath)
4604
4712
  if (e) {
4605
4713
  items = await Util.parse_env_detail(filepath)
4606
4714
  }
4607
- // if (config.icon) {
4608
- // config.icon = `/${env_path}/${config.icon}?raw=true`
4609
- // } else {
4610
- // config.icon = "/pinokio-black.png"
4611
- // }
4612
4715
 
4613
4716
  let name
4614
4717
  if (env_path.startsWith("api")) {
4615
4718
  name = env_path.split("/")[1]
4616
4719
  }
4617
- let editorpath = "/edit/" + req.params[0] + "/ENVIRONMENT"
4618
4720
 
4721
+ let editorpath
4722
+ if (env_result.relpath) {
4723
+ editorpath = "/edit/" + req.params[0] + "/" + env_result.relpath + "/ENVIRONMENT"
4724
+ } else {
4725
+ editorpath = "/edit/" + req.params[0] + "/ENVIRONMENT"
4726
+ }
4619
4727
  if (config.run) {
4620
4728
  let configStr = await fs.promises.readFile(p, "utf8")
4621
4729
  res.render("task", {
@@ -4676,9 +4784,8 @@ class Server {
4676
4784
  //})
4677
4785
  }))
4678
4786
  this.app.get("/pre/api/:name", ex(async (req, res) => {
4679
- let p = path.resolve(this.kernel.homedir, "api", req.params.name, "pinokio.js")
4680
- let p2 = path.resolve(this.kernel.homedir, "api", req.params.name)
4681
- let config = (await this.kernel.loader.load(p)).resolved
4787
+ let launcher = await this.kernel.api.launcher(req.params.name)
4788
+ let config = launcher.script
4682
4789
  if (config && config.pre) {
4683
4790
  config.pre.forEach((item) => {
4684
4791
  if (item.icon) {
@@ -4690,6 +4797,7 @@ class Server {
4690
4797
  item.href = path.resolve(this.kernel.homedir, "api", req.params.name, item.href)
4691
4798
  }
4692
4799
  })
4800
+ let p2 = launcher.root
4693
4801
  let env = await Environment.get2(p2, this.kernel)
4694
4802
  res.render("pre", {
4695
4803
  name: req.params.name,
@@ -4704,8 +4812,8 @@ class Server {
4704
4812
  }
4705
4813
  }))
4706
4814
  this.app.get("/initialize/:name", ex(async (req, res) => {
4707
- let p = path.resolve(this.kernel.homedir, "api", req.params.name, "pinokio.js")
4708
- let config = (await this.kernel.loader.load(p)).resolved
4815
+ let launcher = await this.kernel.api.launcher(req.params.name)
4816
+ let config = launcher.script
4709
4817
  if (config) {
4710
4818
  // if pinokio.js exists
4711
4819
  if (config.pre && Array.isArray(config.pre)) {
@@ -4822,8 +4930,8 @@ class Server {
4822
4930
  this.app.get("/gitcommit/:ref/*", ex(async (req, res) => {
4823
4931
  // return git log
4824
4932
  let dir = this.kernel.path("api", req.params[0])
4825
- let changes = []
4826
4933
  let d = Date.now()
4934
+ let changes = []
4827
4935
  if (req.params.ref === "HEAD") {
4828
4936
  try {
4829
4937
  let statusMatrix = await git.statusMatrix({ dir, fs });
@@ -4846,6 +4954,7 @@ class Server {
4846
4954
  webpath,
4847
4955
  file: filepath,
4848
4956
  path: fullPath,
4957
+ diffpath: `/gitdiff/${req.params.ref}/${req.params[0]}/${filepath}`,
4849
4958
  status: Util.classifyChange(head, workdir, stage),
4850
4959
  });
4851
4960
  }
@@ -4905,6 +5014,7 @@ class Server {
4905
5014
  webpath,
4906
5015
  file: filepath,
4907
5016
  path: fullPath,
5017
+ diffpath: `/gitdiff/${req.params.ref}/${req.params[0]}/${filepath}`,
4908
5018
  status: type,
4909
5019
  });
4910
5020
  }
@@ -4912,7 +5022,8 @@ class Server {
4912
5022
  console.log("git diff error", err);
4913
5023
  }
4914
5024
  }
4915
- res.json({ changes })
5025
+ let git_commit_url = `/run/scripts/git/commit.json?cwd=${dir}&callback_target=parent&callback=$location.href`
5026
+ res.json({ git_commit_url, changes })
4916
5027
  }))
4917
5028
  this.app.get("/gitdiff/:ref/*", ex(async (req, res) => {
4918
5029
  let fullpath = this.kernel.path("api", req.params[0])
@@ -4992,6 +5103,10 @@ class Server {
4992
5103
  }
4993
5104
  res.json(response)
4994
5105
  }))
5106
+ this.app.get("/info/git/:ref/*", ex(async (req, res) => {
5107
+ let response = await this.getGit(req.params.ref, req.params[0])
5108
+ res.json(response)
5109
+ }))
4995
5110
  this.app.get("/git/:ref/*", ex(async (req, res) => {
4996
5111
 
4997
5112
  let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
@@ -5003,95 +5118,28 @@ class Server {
5003
5118
  }
5004
5119
 
5005
5120
 
5006
-
5007
- let dir = this.kernel.path("api", req.params[0])
5008
- let branches = await git.listBranches({ fs, dir });
5009
- let log = []
5010
- try {
5011
- log = await git.log({ fs, dir, depth: 50, ref: req.params.ref }); // fetch last 50 commits
5012
- } catch (e) {
5013
- console.log("Log error", e)
5014
- }
5015
-
5016
- let config = await this.kernel.git.config(dir)
5017
-
5018
- let hosts = ""
5019
- let hosts_file = this.kernel.path("config/gh/hosts.yml")
5020
- let e = await this.exists(hosts_file)
5021
- if (e) {
5022
- hosts = await fs.promises.readFile(hosts_file, "utf8")
5023
- if (hosts.startsWith("{}")) {
5024
- hosts = ""
5025
- }
5026
- }
5027
- let connected = (hosts.length > 0)
5028
- let remote = null
5029
- if (config["remote \"origin\""]) {
5030
- remote = config["remote \"origin\""].url
5031
- }
5032
-
5033
- let branch = await git.currentBranch({ fs, dir, fullname: false });
5034
-
5035
- const remote2 = await git.getConfig({
5036
- fs,
5037
- dir,
5038
- path: `branch.${branch}.remote`
5039
- });
5040
-
5041
- // if current branch exitss => currengt branch is selected
5042
- // if current branch does not exist => get logs[0].oid
5043
- if (branch) {
5044
- branches = branches.map((b) => {
5045
- if (b === branch) {
5046
- return {
5047
- branch: b,
5048
- selected: true
5049
- }
5050
- } else {
5051
- return {
5052
- branch: b,
5053
- selected: false
5054
- }
5055
- }
5056
- })
5057
- } else {
5058
- branches.push(log[0].oid)
5059
- branches = branches.map((b) => {
5060
- if (b === log[0].oid) {
5061
- return {
5062
- branch: b,
5063
- selected: true
5064
- }
5065
- } else {
5066
- return {
5067
- branch: b,
5068
- selected: false
5069
- }
5070
- }
5071
- })
5072
- }
5121
+ let { ref, config, remote, connected, log, branch, branches, dir } = await this.getGit(req.params.ref, req.params[0])
5073
5122
 
5074
5123
  res.render("git", {
5124
+ config,
5125
+ remote,
5126
+ connected,
5127
+ log,
5075
5128
  branch,
5076
5129
  branches,
5077
- ref: req.params.ref,
5130
+ ref,
5078
5131
  path: req.params[0],
5079
- log,
5080
- connected,
5081
5132
  // changes,
5082
5133
  dir,
5083
- config,
5084
- remote,
5085
5134
  theme: this.theme,
5086
5135
  platform: this.kernel.platform,
5087
5136
  agent: this.agent,
5088
5137
  })
5089
5138
  }))
5090
5139
  this.app.get("/d/*", ex(async (req, res) => {
5091
- console.log("> 1")
5092
5140
  let filepath = Util.u2p(req.params[0])
5093
- let plugin = await this.getPluginGlobal(req, filepath)
5094
- console.log("> 2")
5141
+ let terminal = await this.terminals(filepath)
5142
+ let plugin = await this.getPluginGlobal(req, this.kernel.plugin.config, terminal, filepath)
5095
5143
  let html = ""
5096
5144
  let plugin_menu
5097
5145
  try {
@@ -5101,105 +5149,74 @@ class Server {
5101
5149
  plugin_menu = []
5102
5150
  }
5103
5151
  let current_urls = await this.current_urls(req.originalUrl.slice(1))
5104
- console.log("> 3")
5105
5152
  let retry = false
5106
5153
  // if plugin_menu is empty, try again in 1 sec
5107
5154
  if (plugin_menu.length === 0) {
5108
5155
  retry = true
5109
5156
  }
5110
- let venvs = await Util.find_venv(filepath)
5111
- console.log("> 4")
5112
- let terminal
5113
- if (venvs.length > 0) {
5114
- let terminals = []
5115
- try {
5116
- for(let i=0; i<venvs.length; i++) {
5117
- let venv = venvs[i]
5118
- let parsed = path.parse(venv)
5119
- terminals.push(this.renderShell(filepath, i, 0, {
5120
- icon: "fa-brands fa-python",
5121
- title: "Python virtual environment",
5122
- subtitle: this.kernel.path("api", parsed.name),
5123
- type: "Start",
5124
- shell: {
5125
- venv: venv,
5126
- input: true,
5127
- }
5128
- }))
5129
- }
5130
- } catch (e) {
5131
- console.log(e)
5132
- }
5133
- terminal = {
5134
- icon: "fa-solid fa-terminal",
5135
- title: "Open web terminal",
5136
- subtitle: "Open the terminal in the browser",
5137
- menu: terminals
5138
- }
5139
- } else {
5140
- terminal = {
5141
- icon: "fa-solid fa-terminal",
5142
- title: "Open web terminal",
5143
- subtitle: "Work with the terminal directly in the browser",
5144
- menu: [this.renderShell(filepath, 0, 0, {
5145
- icon: "fa-solid fa-terminal",
5146
- title: "Terminal",
5147
- subtitle: filepath,
5148
- type: "Start",
5149
- shell: {
5150
- input: true
5151
- }
5152
- })]
5153
- }
5154
- }
5155
- console.log("> 5")
5156
5157
 
5157
5158
  let exec_menus = []
5158
5159
  let shell_menus = []
5160
+ let href_menus = []
5159
5161
  if (plugin_menu.length > 0) {
5160
5162
  for(let item of plugin_menu) {
5161
5163
  // if shell.run method exists
5162
5164
  // if exec method exists
5163
5165
  let mode
5164
- for(let step of item.run) {
5165
- if (step.method === "exec") {
5166
- mode = "exec"
5167
- break
5166
+ if (item.run) {
5167
+ for(let step of item.run) {
5168
+ if (step.method === "exec") {
5169
+ mode = "exec"
5170
+ break
5171
+ }
5172
+ if (step.method === "shell.run") {
5173
+ mode = "shell"
5174
+ break
5175
+ }
5168
5176
  }
5169
- if (step.method === "shell.run") {
5170
- mode = "shell"
5171
- break
5177
+ if (mode === "exec") {
5178
+ item.type = "Open"
5179
+ exec_menus.push(item)
5180
+ } else if (mode === "shell") {
5181
+ item.type = "Start"
5182
+ shell_menus.push(item)
5172
5183
  }
5173
- }
5174
- if (mode === "exec") {
5175
- item.type = "Open"
5176
- exec_menus.push(item)
5177
- } else if (mode === "shell") {
5178
- item.type = "Start"
5179
- shell_menus.push(item)
5184
+ } else {
5185
+ href_menus.push(item)
5180
5186
  }
5181
5187
  }
5182
5188
  exec_menus.sort((a, b) => { return a > b })
5183
5189
  shell_menus.sort((a, b) => { return a > b })
5190
+ href_menus.sort((a, b) => { return a > b })
5184
5191
  }
5185
- console.log("> 6")
5192
+
5193
+ // let terminal = await this.terminals(filepath)
5194
+ // let online_terminal = await this.getPluginGlobal(req, terminal, filepath)
5195
+ // console.log("online_terminal", online_terminal)
5196
+ terminal.menus = href_menus
5186
5197
  let dynamic = [
5187
5198
  {
5188
5199
  icon: "fa-solid fa-robot",
5189
- title: "Get started building with AI",
5190
- subtitle: "Start making changes to this project using AI",
5200
+ title: "AI Engineer",
5201
+ subtitle: "Let AI work on this app",
5191
5202
  menu: shell_menus
5192
5203
  },
5193
5204
  {
5194
5205
  icon: "fa-solid fa-arrow-up-right-from-square",
5195
- title: "Open in external apps",
5206
+ title: "External apps",
5196
5207
  subtitle: "Open this project in 3rd party apps",
5197
5208
  menu: exec_menus
5198
5209
  },
5199
- terminal,
5210
+ terminal
5200
5211
  ]
5201
- console.log("> 7")
5212
+ let spec = ""
5213
+ try {
5214
+ spec = await fs.promises.readFile(path.resolve(filepath, "SPEC.md"), "utf8")
5215
+ } catch (e) {
5216
+ }
5202
5217
  res.render("d", {
5218
+ filepath,
5219
+ spec,
5203
5220
  retry,
5204
5221
  current_urls,
5205
5222
  docs: this.docs,
@@ -5248,20 +5265,6 @@ class Server {
5248
5265
  }
5249
5266
  res.render("mini", result)
5250
5267
  }))
5251
- this.app.get("/asset/*", ex((req, res) => {
5252
- let pathComponents = req.params[0].split("/")
5253
- let filepath = this.kernel.path(...pathComponents)
5254
- try {
5255
- if (req.query.frame) {
5256
- let m = mime.lookup(filepath)
5257
- res.type("text/plain")
5258
- }
5259
- //res.setHeader('Content-Disposition', 'inline');
5260
- res.sendFile(filepath)
5261
- } catch (e) {
5262
- res.status(404).send(e.message);
5263
- }
5264
- }))
5265
5268
  this.app.get("/raw/*", ex((req, res) => {
5266
5269
  let pathComponents = req.params[0].split("/")
5267
5270
  let filepath = this.kernel.path("api", ...pathComponents)
@@ -5329,7 +5332,9 @@ class Server {
5329
5332
  }
5330
5333
  }))
5331
5334
  this.app.get("/pinokio/dynamic_global/*", ex(async (req, res) => {
5332
- let plugin = await this.getPluginGlobal(req, "/" + req.params[0])
5335
+ let filepath = Util.u2p(req.params[0])
5336
+ let terminal = await this.terminals(filepath)
5337
+ let plugin = await this.getPluginGlobal(req, this.kernel.plugin.config, terminal, filepath)
5333
5338
  if (plugin) {
5334
5339
  let html = ""
5335
5340
  if (plugin && plugin.menu) {
@@ -5352,7 +5357,8 @@ class Server {
5352
5357
  }))
5353
5358
  this.app.get("/pinokio/dynamic/:name", ex(async (req, res) => {
5354
5359
  // await this.kernel.plugin.init()
5355
- let plugin = await this.getPlugin(req, req.params.name)
5360
+
5361
+ let plugin = await this.getPlugin(req, this.kernel.plugin.config, req.params.name)
5356
5362
  let html = ""
5357
5363
  let plugin_menu
5358
5364
  if (plugin) {
@@ -5404,6 +5410,15 @@ class Server {
5404
5410
  })
5405
5411
  res.send(html)
5406
5412
  }))
5413
+ this.app.get("/repos/:name", ex(async (req, res) => {
5414
+ // await this.kernel.plugin.init()
5415
+ let c = this.kernel.path("api", req.params.name)
5416
+ let repos = await this.kernel.git.repos(c)
5417
+ let repos_with_remote = repos.filter((repo) => {
5418
+ return repo.url
5419
+ })
5420
+ res.json(repos_with_remote)
5421
+ }))
5407
5422
  this.app.get("/pinokio/repos/:name", ex(async (req, res) => {
5408
5423
  // await this.kernel.plugin.init()
5409
5424
  let c = this.kernel.path("api", req.params.name)
@@ -5425,9 +5440,10 @@ class Server {
5425
5440
  }))
5426
5441
  this.app.get("/pinokio/sidebar/:name", ex(async (req, res) => {
5427
5442
  let name = req.params.name
5428
- let app_path = this.kernel.path("api", name, "pinokio.js")
5443
+ let launcher = await this.kernel.api.launcher(name)
5429
5444
  let rawpath = "/api/" + name
5430
- let config = (await this.kernel.loader.load(app_path)).resolved
5445
+ let config = launcher.script
5446
+ req.launcher_root = launcher.launcher_root
5431
5447
  if (config && config.menu) {
5432
5448
  if (typeof config.menu === "function") {
5433
5449
  if (config.menu.constructor.name === "AsyncFunction") {
@@ -5505,81 +5521,72 @@ class Server {
5505
5521
  // await this.kernel.refresh()
5506
5522
  // res.json({ success: true })
5507
5523
  // }))
5508
- this.app.get("/pinokio/peer", ex(async (req, res) => {
5509
- // await this.kernel.refresh()
5524
+
5525
+
5526
+ this.app.get("/info/system", ex(async (req,res) => {
5510
5527
  let current_peer_info = await this.kernel.peer.current_host()
5511
5528
  res.json(current_peer_info)
5512
- /*
5529
+ }))
5530
+ this.app.get("/info/api", ex(async (req,res) => {
5531
+ // api related info
5532
+ let repo = this.kernel.git.find(req.query.git)
5533
+ if (repo) {
5534
+ let repos = await this.kernel.git.repos(repo.path)
5535
+ repos = repos.filter((r) => {
5536
+ return r.main
5537
+ })
5538
+ let repos_with_remote = []
5539
+ let main_repo
5540
+ for(let repo of repos) {
5541
+ if (repo.url) {
5542
+ repo.commit = await this.kernel.git.getHead(repo.gitParentPath)
5543
+ let du = await Util.du(repo.gitParentPath)
5544
+ repo.du = du
5545
+ repos_with_remote.push(repo)
5546
+ }
5547
+ if (repo.main) {
5548
+ main_repo = repo
5549
+ }
5550
+ }
5551
+ res.json({
5552
+ repos: repos_with_remote
5553
+ })
5554
+ } else {
5555
+ res.json({
5556
+ repos: []
5557
+ })
5558
+ }
5559
+ }))
5560
+ this.app.get("/info/api/:name", ex(async (req,res) => {
5561
+ // api related info
5562
+ let c = this.kernel.path("api", req.params.name)
5563
+ let repos = await this.kernel.git.repos(c)
5564
+ let repos_with_remote = []
5565
+ let main_repo
5566
+ for(let repo of repos) {
5567
+ if (repo.url) {
5568
+ repo.commit = await this.kernel.git.getHead(repo.gitParentPath)
5569
+ repos_with_remote.push(repo)
5570
+ }
5571
+ if (repo.main) {
5572
+ main_repo = repo
5573
+ }
5574
+ }
5513
5575
  res.json({
5514
- home: this.kernel.homedir,
5515
- arch: this.kernel.arch,
5516
- platform: this.kernel.platform,
5517
- name: this.kernel.peer.name,
5518
- host: this.kernel.peer.host,
5519
- port_mapping: this.kernel.router.port_mapping,
5520
- //router: this.kernel.router.info(),
5521
- proc: this.kernel.processes.info,
5522
- router: this.kernel.router.published(),
5523
- memory: this.kernel.memory
5576
+ repos: repos_with_remote
5524
5577
  })
5525
- */
5578
+ }))
5579
+
5580
+
5581
+ this.app.get("/pinokio/peer", ex(async (req, res) => {
5582
+ let current_peer_info = await this.kernel.peer.current_host()
5583
+ res.json(current_peer_info)
5526
5584
  }))
5527
5585
  this.app.get("/pinokio/memory", ex((req, res) => {
5528
5586
  let filepath = req.query.filepath
5529
5587
  let mem = this.getMemory(filepath)
5530
5588
  res.json(mem)
5531
5589
  }))
5532
- // this.app.post("/pinokio/tunnel", async (req, res) => {
5533
- // let port
5534
- // let local_host
5535
- // try {
5536
- // let u = new URL(req.body.url)
5537
- // port = u.port
5538
- // local_host = u.hostname
5539
- // console.log({ local_host, port })
5540
- // if (req.body.action === "start") {
5541
- // // Output ngrok url to console
5542
- //
5543
- // let url = req.body.url
5544
- // console.log("tunnel", req.body)
5545
- // const tunnel = await ngrok.forward({ addr: port, authtoken: req.body.token });
5546
- // console.log("created", tunnel)
5547
- // console.log("url", tunnel.url())
5548
- // this.tunnels[url] = tunnel
5549
- // res.json({ url: tunnel.url() })
5550
- //
5551
- //
5552
- // // localtunnel
5553
- // //const tunnel = await localtunnel({ local_host, port: parseInt(port) });
5554
- // //const tunnel = await localtunnel({ local_host: "127.0.0.1", port: parseInt(port) });
5555
- //
5556
- // //const tunnel = await localtunnel({ port: parseInt(port) });
5557
- // //this.tunnels[url] = tunnel
5558
- // //tunnel.on('error', (err) => {
5559
- // // console.log(err)
5560
- // // delete this.tunnels[url]
5561
- // //})
5562
- // //tunnel.on('close', () => {
5563
- // // // tunnels are closed
5564
- // // console.log("tunnel closed", { url, tunnel_url: tunnel.url })
5565
- // // delete this.tunnels[url]
5566
- // //});
5567
- // //res.json({ url: tunnel.url })
5568
- // } else if (req.body.action === "stop") {
5569
- // let url = req.body.url
5570
- // await this.tunnels[url].close()
5571
- // delete this.tunnels[url]
5572
- // res.json({ url })
5573
- //// let url = req.body.url
5574
- //// console.log({ tunnels: this.tunnels, url })
5575
- //// this.tunnels[url].close()
5576
- //// res.json({ url })
5577
- // }
5578
- // } catch (e) {
5579
- // console.log("ERROR", e)
5580
- // res.json({ error: e.message })
5581
- // }
5582
- // })
5583
5590
  this.app.post("/pinokio/tabs", ex(async (req, res) => {
5584
5591
  this.tabs[req.body.name] = req.body.tabs
5585
5592
  res.json({ success: true })
@@ -5605,6 +5612,41 @@ class Server {
5605
5612
  this.app.get("/pinokio/browser/:name", ex(async (req, res) => {
5606
5613
  await this.chrome(req, res, "run")
5607
5614
  }))
5615
+
5616
+
5617
+ this.app.get("/p/:name/review", ex(async (req, res) => {
5618
+ let gitRemote = null
5619
+ try {
5620
+ const repositoryPath = path.resolve(this.kernel.api.userdir, req.params.name)
5621
+ console.log({ repositoryPath })
5622
+ gitRemote = await git.getConfig({
5623
+ fs,
5624
+ http,
5625
+ dir: repositoryPath,
5626
+ path: 'remote.origin.url'
5627
+ })
5628
+ } catch (e) {
5629
+ console.log("ERROR", e)
5630
+ }
5631
+ let name = req.params.name
5632
+ let run_tab = "/p/" + name
5633
+ let dev_tab = "/p/" + name + "/dev"
5634
+ let review_tab = "/p/" + name + "/review"
5635
+ res.render("review", {
5636
+ run_tab,
5637
+ dev_tab,
5638
+ review_tab,
5639
+ name: req.params.name,
5640
+ type: "review",
5641
+ title: name,
5642
+ url: gitRemote,
5643
+ //redirect_uri: "http://localhost:3001/apps/redirect?git=" + gitRemote,
5644
+ redirect_uri: "https://app-7pt7.onrender.com/apps/redirect?git=" + gitRemote,
5645
+ platform: this.kernel.platform,
5646
+ theme: this.theme,
5647
+ agent: this.agent,
5648
+ })
5649
+ }))
5608
5650
  this.app.get("/p/:name/dev", ex(async (req, res) => {
5609
5651
  await this.chrome(req, res, "browse")
5610
5652
  }))
@@ -5628,7 +5670,7 @@ class Server {
5628
5670
  res.json({ success: true })
5629
5671
  } else if (req.body.type === 'env') {
5630
5672
  let envpath = this.kernel.path("ENVIRONMENT")
5631
- let str = await Environment.ENV("system", this.kernel.homedir)
5673
+ let str = await Environment.ENV("system", this.kernel.homedir, this.kernel)
5632
5674
  await fs.promises.writeFile(path.resolve(this.kernel.homedir, "ENVIRONMENT"), str)
5633
5675
  res.json({ success: true })
5634
5676
  } else if (req.body.type === 'browser-cache') {
@@ -5651,35 +5693,8 @@ class Server {
5651
5693
  res.json({ error: err.stack })
5652
5694
  }
5653
5695
  }))
5654
- // this.app.get("/pinokio/shell_state", (req, res) => {
5655
- // let states = this.kernel.shell.shells.map((s) => {
5656
- // return {
5657
- // state: s.state,
5658
- // id: s.id,
5659
- // group: s.group,
5660
- // env: s.env,
5661
- // path: s.path,
5662
- // cmd: s.cmd,
5663
- // done: s.done,
5664
- // ready: s.ready,
5665
- // }
5666
- // })
5667
- //
5668
- // let info = {
5669
- // platform: this.kernel.platform,
5670
- // arch: this.kernel.arch,
5671
- // running: this.kernel.api.running,
5672
- // home: this.kernel.homedir,
5673
- // vars: this.kernel.vars,
5674
- // memory: this.kernel.memory,
5675
- // procs: this.kernel.procs,
5676
- // gpu: this.kernel.gpu,
5677
- // gpus: this.kernel.gpus
5678
- // }
5679
- // })
5680
5696
  this.app.get("/pinokio/logs.zip", ex((req, res) => {
5681
5697
  let zipPath = this.kernel.path("logs.zip")
5682
- console.log("sendFile", zipPath)
5683
5698
  res.download(zipPath)
5684
5699
  }))
5685
5700
  this.app.post("/pinokio/log", ex(async (req, res) => {
@@ -5812,7 +5827,7 @@ class Server {
5812
5827
  await fs.promises.cp(old_path, new_path, { recursive: true })
5813
5828
 
5814
5829
  // 2. edit meta in the new_path
5815
- await this.updateMeta(formData, formData.new_path)
5830
+ await this.kernel.api.updateMeta(formData, formData.new_path)
5816
5831
 
5817
5832
  }
5818
5833
  } else if (formData.move) {
@@ -5825,11 +5840,11 @@ class Server {
5825
5840
  }
5826
5841
 
5827
5842
  // 2. edit meta in the new_path
5828
- await this.updateMeta(formData, formData.new_path)
5843
+ await this.kernel.api.updateMeta(formData, formData.new_path)
5829
5844
  } else {
5830
5845
  // 1. edit only
5831
5846
  if (formData.old_path === formData.new_path) {
5832
- await this.updateMeta(formData, formData.new_path)
5847
+ await this.kernel.api.updateMeta(formData, formData.new_path)
5833
5848
  }
5834
5849
  }
5835
5850
  } else {
@@ -5893,6 +5908,56 @@ class Server {
5893
5908
  res.status(404).send("Missing attribute: path")
5894
5909
  }
5895
5910
  }))
5911
+ this.app.get("/snapshots", ex(async (req, res) => {
5912
+ let files = []
5913
+ try {
5914
+ files = await fs.promises.readdir(this.kernel.path("screenshots"))
5915
+ files = files.map((file) => {
5916
+ return `/asset/screenshots/${file}`
5917
+ })
5918
+ } catch (e) {
5919
+ }
5920
+ res.json({ files })
5921
+ }))
5922
+
5923
+ this.app.post("/snapshots", ex(async (req, res) => {
5924
+ const { filename } = req.body
5925
+
5926
+ if (!filename) {
5927
+ return res.status(400).json({ error: "Missing filename parameter" })
5928
+ }
5929
+
5930
+ try {
5931
+ // Extract just the filename from the path (security measure)
5932
+ const baseFilename = path.basename(filename.replace('/asset/screenshots/', ''))
5933
+ const fullPath = this.kernel.path("screenshots", baseFilename)
5934
+
5935
+ // Check if file exists before attempting to delete
5936
+ try {
5937
+ await fs.promises.access(fullPath)
5938
+ } catch (e) {
5939
+ return res.status(404).json({ error: "File not found" })
5940
+ }
5941
+
5942
+ // Delete the file
5943
+ await fs.promises.unlink(fullPath)
5944
+
5945
+ res.json({ success: true, message: "File deleted successfully" })
5946
+
5947
+ } catch (e) {
5948
+ console.log("Error deleting screenshot:", e)
5949
+ res.status(500).json({ error: "Failed to delete file: " + e.message })
5950
+ }
5951
+ }))
5952
+ this.app.post("/screenshot", this.upload.any(), ex(async (req, res) => {
5953
+ await fs.promises.mkdir(this.kernel.path("screenshots"), { recursive: true }).catch((e) => { })
5954
+ for(let key in req.files) {
5955
+ let file = req.files[key]
5956
+ console.log({ file, key })
5957
+ let ts = String(Date.now()) + ".png"
5958
+ await fs.promises.writeFile(this.kernel.path("screenshots", ts), file.buffer)
5959
+ }
5960
+ }))
5896
5961
  this.app.post("/pinokio/fs", this.upload.any(), ex(async (req, res) => {
5897
5962
  /*
5898
5963
  Packet format:
@@ -6084,6 +6149,14 @@ class Server {
6084
6149
  res.json({ success: true })
6085
6150
  }
6086
6151
  }))
6152
+ this.app.get("/bin_ready", ex((req, res) => {
6153
+ if (this.kernel.bin && !this.kernel.bin.requirements_pending) {
6154
+ res.json({ success: true })
6155
+ } else {
6156
+ res.json({ success: false })
6157
+ }
6158
+ }))
6159
+
6087
6160
  this.app.get("/check", ex((req, res) => {
6088
6161
  res.json({ success: true })
6089
6162
  }))