pinokiod 3.24.0 → 3.26.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.
package/server/index.js CHANGED
@@ -188,16 +188,9 @@ class Server {
188
188
  }
189
189
  }
190
190
  } else if (key === "shell") {
191
- console.log("CHECK SHELL", { name, indexPath, key })
192
- console.log("obj", obj)
193
- console.log("running", this.kernel.api.running)
194
191
  let unix_path = Util.p2u(this.kernel.path("api", name))
195
- console.log({ unix_path })
196
192
  let shell_id = this.get_shell_id(unix_path, indexPath, obj[key])
197
-
198
- console.log("SHELL ID 2", shell_id)
199
193
  let decoded_shell_id = decodeURIComponent(shell_id)
200
- console.log({ shell_id, decoded_shell_id })
201
194
  if (this.kernel.api.running["shell/" + decoded_shell_id]) {
202
195
  obj.running = true
203
196
  obj.display = "indent"
@@ -391,6 +384,20 @@ class Server {
391
384
  }
392
385
  // console.timeEnd("1 chrome " + d)
393
386
 
387
+
388
+ console.log("req.query", req.query)
389
+ if (req.query.autolaunch === "1") {
390
+ let fullpath = path.resolve(this.kernel.homedir, "ENVIRONMENT")
391
+ await Util.update_env(fullpath, {
392
+ PINOKIO_ONDEMAND_AUTOLAUNCH: "1"
393
+ })
394
+ } else if (req.query.autolaunch === "0") {
395
+ let fullpath = path.resolve(this.kernel.homedir, "ENVIRONMENT")
396
+ await Util.update_env(fullpath, {
397
+ PINOKIO_ONDEMAND_AUTOLAUNCH: "0"
398
+ })
399
+ }
400
+
394
401
  let name = req.params.name
395
402
  let config = await this.kernel.api.meta(name)
396
403
 
@@ -431,34 +438,8 @@ class Server {
431
438
  // }
432
439
 
433
440
 
434
- if (config.init_required) {
435
- res.redirect("/init/" + name)
436
- return
437
-
438
- // // none of the pinokio.js, pinokio.json, pinokio_meta.json exists => need to initialize
439
- // // if there is no menu, display all files
440
- // let p = this.kernel.path("api", name)
441
- // let files = await fs.promises.readdir(p, { withFileTypes: true })
442
- // files = files.filter((file) => {
443
- // return file.name.endsWith(".json") || file.name.endsWith(".js")
444
- // }).filter((file) => {
445
- // return file.name !== "pinokio.js" && file.name !== "pinokio.json" && file.name !== "pinokio_meta.json"
446
- // })
447
- // config = {
448
- // init_required: true,
449
- // icon: config.icon,
450
- // title: name,
451
- // menu: files.map((file) => {
452
- // return {
453
- // text: file.name,
454
- // href: file.name
455
- // }
456
- // })
457
- // }
458
- // let uri = this.kernel.path("api")
459
- // await this.renderMenu(uri, name, config, [])
460
- } else {
461
- let menu = config.menu || []
441
+ let menu = config.menu || []
442
+ try {
462
443
  if (typeof config.menu === "function") {
463
444
  if (config.menu.constructor.name === "AsyncFunction") {
464
445
  config.menu = await config.menu(this.kernel, this.kernel.info)
@@ -466,12 +447,20 @@ class Server {
466
447
  config.menu = config.menu(this.kernel, this.kernel.info)
467
448
  }
468
449
  }
450
+ } catch (e) {
451
+ err = e.stack
452
+ config.menu = []
453
+ }
469
454
 
470
- let uri = this.kernel.path("api")
455
+ let uri = this.kernel.path("api")
456
+ try {
471
457
  await this.renderMenu(uri, name, config, [])
472
-
458
+ } catch(e) {
459
+ config.menu = []
460
+ err = e.stack
473
461
  }
474
462
 
463
+
475
464
  let platform = os.platform()
476
465
 
477
466
  // if (config.icon) {
@@ -585,18 +574,10 @@ class Server {
585
574
 
586
575
  let plugin_menu = null
587
576
  let plugin = await this.getPlugin(name)
588
- console.log(">>> Plugin", plugin)
589
577
  if (plugin && plugin.menu && Array.isArray(plugin.menu)) {
590
- console.log(">>> 2")
591
578
  plugin = structuredClone(plugin)
592
579
  plugin_menu = this.running_dynamic(name, plugin.menu)
593
- console.log("GOT THE PLUGIN MENU")
594
-
595
- // plugin_menu = plugin.menu
596
580
  }
597
- console.log("PLUGIN_MENU", plugin_menu)
598
-
599
-
600
581
  let menu_hidden = false
601
582
  if (this.menu_hidden[name] && this.menu_hidden[name][type]) {
602
583
  menu_hidden = true
@@ -686,7 +667,6 @@ class Server {
686
667
  is_subpath(parent, child) {
687
668
  const relative = path.relative(parent, child);
688
669
  let check = !!relative && !relative.startsWith('..') && !path.isAbsolute(relative);
689
- console.log({ relative, parent, child, check })
690
670
  return check
691
671
  }
692
672
  async render(req, res, pathComponents, meta) {
@@ -1375,7 +1355,6 @@ class Server {
1375
1355
  }
1376
1356
  // check if there is a running process with this folder name
1377
1357
  let runningApps = new Set()
1378
- console.log("RUNNING", this.kernel.api.running)
1379
1358
  for(let key in this.kernel.api.running) {
1380
1359
  //let p = this.kernel.path("api", items[i].name) + path.sep
1381
1360
  let p = this.kernel.path("api", items[i].name)
@@ -1396,23 +1375,17 @@ class Server {
1396
1375
  } else {
1397
1376
  // shell sessions
1398
1377
  if (key.startsWith("shell/")) {
1399
- // if (!path.isAbsolute(key)) {
1400
1378
  let unix_path = key.slice(6)
1401
1379
  let native_path = Util.u2p(unix_path)
1402
1380
  let chunks = native_path.split("_")
1403
1381
  if (chunks.length > 1) {
1404
1382
  let folder = chunks[0]
1405
- console.log({ chunks, folder, item: items[i] })
1406
1383
  /// if the folder name matches, it's running
1407
1384
  let item_path = this.kernel.path("api", items[i].name)
1408
- console.log({ item_path, folder })
1409
1385
  if (item_path === folder) {
1410
- console.log("is_running = true")
1411
- // if (folder === items[i].name) {
1412
1386
  is_running = true
1413
1387
  }
1414
1388
  }
1415
-
1416
1389
  }
1417
1390
  }
1418
1391
  }
@@ -1423,7 +1396,6 @@ class Server {
1423
1396
  // => check inlcludes and endsWith
1424
1397
 
1425
1398
  //if (key.includes(p) && key.endsWith(p)) {
1426
- console.log("is_running", is_running)
1427
1399
  if (is_running) {
1428
1400
  // add to running
1429
1401
  running.push(items[i])
@@ -1453,10 +1425,8 @@ class Server {
1453
1425
  } else {
1454
1426
  let shell = this.kernel.shell.find({
1455
1427
  filter: (shell) => {
1456
- //return shell.id.startsWith(items[i].name + "_")
1457
1428
  let item_path = this.kernel.path("api", items[i].name)
1458
1429
  let unix_item_path = Util.p2u(item_path)
1459
- console.log("startsWith", { item_path, unix_item_path, id: shell.id })
1460
1430
  return shell.id.startsWith("shell/" + unix_item_path + "_")
1461
1431
  }
1462
1432
  })
@@ -1747,6 +1717,86 @@ class Server {
1747
1717
  }
1748
1718
  return config
1749
1719
  }
1720
+ renderShell(cwd, indexPath, subIndexPath, menuitem) {
1721
+ if (menuitem.shell) {
1722
+ /*
1723
+ shell :- {
1724
+ id (optional),
1725
+ path (required), // api, bin, quick, network, api/
1726
+ message (optional), // if not specified, start an empty shell
1727
+ venv,
1728
+ input, // input mode if true
1729
+ callback, // callback url after shutting down
1730
+ kill, // when to kill (regular expression)
1731
+ }
1732
+ */
1733
+
1734
+ let rendered = this.kernel.template.render(menuitem.shell, {})
1735
+ let params = new URLSearchParams()
1736
+ // if (rendered.id) {
1737
+ // params.set("id", encodeURIComponent(rendered.id))
1738
+ // } else {
1739
+ // let shell_id = "sh_" + name + "_" + i
1740
+ // params.set("id", encodeURIComponent(shell_id))
1741
+ // }
1742
+ if (rendered.path) {
1743
+ params.set("path", encodeURIComponent(this.kernel.api.filePath(rendered.path, cwd)))
1744
+ } else {
1745
+ params.set("path", encodeURIComponent(cwd))
1746
+ }
1747
+ if (rendered.message) params.set("message", encodeURIComponent(rendered.message))
1748
+ if (rendered.venv) params.set("venv", encodeURIComponent(rendered.venv))
1749
+ if (rendered.input) params.set("input", true)
1750
+ if (rendered.callback) params.set("callback", encodeURIComponent(rendered.callback))
1751
+ if (rendered.callback_target) params.set("callback_target", rendered_callback_target)
1752
+ if (rendered.kill) params.set("kill", encodeURIComponent(rendered.kill))
1753
+ if (rendered.done) params.set("done", encodeURIComponent(rendered.done))
1754
+ if (rendered.env) {
1755
+ for(let key in rendered.env) {
1756
+ let env_key = "env." + key
1757
+ params.set(env_key, rendered.env[key])
1758
+ }
1759
+ }
1760
+ if (rendered.conda) {
1761
+ for(let key in rendered.conda) {
1762
+ let conda_key = "conda." + key
1763
+ params.set(conda_key, rendered.conda[key])
1764
+ }
1765
+ }
1766
+
1767
+ // deterministic shell id generation
1768
+ // `${api_path}_${i}_${hash}`
1769
+ let currentIndexPath
1770
+ if (indexPath) {
1771
+ currentIndexPath = indexPath + "." + subIndexPath
1772
+ } else {
1773
+ currentIndexPath = "" + subIndexPath
1774
+ }
1775
+ let unix_path = Util.p2u(cwd)
1776
+ let shell_id = this.get_shell_id(unix_path, currentIndexPath, rendered)
1777
+
1778
+ // let hash = crypto.createHash('md5').update(JSON.stringify(rendered)).digest('hex')
1779
+ // let shell_id
1780
+ // if (rendered.id) {
1781
+ // shell_id = encodeURIComponent(`${name}_${rendered.id}`)
1782
+ // } else {
1783
+ // shell_id = encodeURIComponent(`${name}_${i}_${hash}`)
1784
+ // }
1785
+ menuitem.href = "/shell/" + shell_id + "?" + params.toString()
1786
+ let decoded_shell_id = decodeURIComponent(shell_id)
1787
+ let shell = this.kernel.shell.get(decoded_shell_id)
1788
+ menuitem.shell_id = "shell/" + decoded_shell_id
1789
+ if (shell) {
1790
+ menuitem.running = true
1791
+ } else {
1792
+ let shell = this.kernel.shell.get(decoded_shell_id)
1793
+ if (shell) {
1794
+ menuitem.running = true
1795
+ }
1796
+ }
1797
+ }
1798
+ return menuitem
1799
+ }
1750
1800
 
1751
1801
  async renderMenu(uri, name, config, pathComponents, indexPath) {
1752
1802
  if (config.menu) {
@@ -1830,84 +1880,8 @@ class Server {
1830
1880
 
1831
1881
 
1832
1882
  if (menuitem.shell) {
1833
- /*
1834
- shell :- {
1835
- id (optional),
1836
- path (required), // api, bin, quick, network, api/
1837
- message (optional), // if not specified, start an empty shell
1838
- venv,
1839
- input, // input mode if true
1840
- callback, // callback url after shutting down
1841
- kill, // when to kill (regular expression)
1842
- }
1843
- */
1844
-
1845
- let rendered = this.kernel.template.render(menuitem.shell, {})
1846
- let params = new URLSearchParams()
1847
- // if (rendered.id) {
1848
- // params.set("id", encodeURIComponent(rendered.id))
1849
- // } else {
1850
- // let shell_id = "sh_" + name + "_" + i
1851
- // params.set("id", encodeURIComponent(shell_id))
1852
- // }
1853
1883
  let basePath = this.kernel.path("api", name)
1854
- if (rendered.path) {
1855
- params.set("path", encodeURIComponent(this.kernel.api.filePath(rendered.path, basePath)))
1856
- } else {
1857
- params.set("path", encodeURIComponent(basePath))
1858
- }
1859
- if (rendered.message) params.set("message", encodeURIComponent(rendered.message))
1860
- if (rendered.venv) params.set("venv", encodeURIComponent(rendered.venv))
1861
- if (rendered.input) params.set("input", true)
1862
- if (rendered.callback) params.set("callback", encodeURIComponent(rendered.callback))
1863
- if (rendered.callback_target) params.set("callback_target", rendered_callback_target)
1864
- if (rendered.kill) params.set("kill", encodeURIComponent(rendered.kill))
1865
- if (rendered.done) params.set("done", encodeURIComponent(rendered.done))
1866
- if (rendered.env) {
1867
- for(let key in rendered.env) {
1868
- let env_key = "env." + key
1869
- params.set(env_key, rendered.env[key])
1870
- }
1871
- }
1872
- if (rendered.conda) {
1873
- for(let key in rendered.conda) {
1874
- let conda_key = "conda." + key
1875
- params.set(conda_key, rendered.conda[key])
1876
- }
1877
- }
1878
-
1879
- // deterministic shell id generation
1880
- // `${api_path}_${i}_${hash}`
1881
- let currentIndexPath
1882
- if (indexPath) {
1883
- currentIndexPath = indexPath + "." + i
1884
- } else {
1885
- currentIndexPath = "" + i
1886
- }
1887
- let unix_path = Util.p2u(this.kernel.path("api", name))
1888
- let shell_id = this.get_shell_id(unix_path, currentIndexPath, rendered)
1889
-
1890
- console.log("SHELL ID 1", shell_id)
1891
-
1892
- // let hash = crypto.createHash('md5').update(JSON.stringify(rendered)).digest('hex')
1893
- // let shell_id
1894
- // if (rendered.id) {
1895
- // shell_id = encodeURIComponent(`${name}_${rendered.id}`)
1896
- // } else {
1897
- // shell_id = encodeURIComponent(`${name}_${i}_${hash}`)
1898
- // }
1899
- menuitem.href = "/shell/" + shell_id + "?" + params.toString()
1900
- let decoded_shell_id = decodeURIComponent(shell_id)
1901
- let shell = this.kernel.shell.get(decoded_shell_id)
1902
- menuitem.shell_id = "shell/" + decoded_shell_id
1903
- if (shell) {
1904
- menuitem.running = true
1905
- } else {
1906
- let shell = this.kernel.shell.get(decoded_shell_id)
1907
- if (shell) {
1908
- menuitem.running = true
1909
- }
1910
- }
1884
+ this.renderShell(basePath, indexPath, i, menuitem)
1911
1885
  }
1912
1886
 
1913
1887
  if (menuitem.href) {
@@ -2022,9 +1996,7 @@ class Server {
2022
1996
  config.menu[i].btn = menuitem.html
2023
1997
  }
2024
1998
  } else if (menuitem.hasOwnProperty("text")) {
2025
- if (menuitem.hasOwnProperty("icon")) {
2026
- menuitem.html = `<i class="${menuitem.icon}"></i> ${menuitem.text}`
2027
- } else if (menuitem.hasOwnProperty("image")) {
1999
+ if (menuitem.hasOwnProperty("image")) {
2028
2000
  let imagePath
2029
2001
  if (menuitem.image.startsWith("/")) {
2030
2002
  imagePath = menuitem.image
@@ -2032,6 +2004,8 @@ class Server {
2032
2004
  imagePath = `/api/${name}/${menuitem.image}?raw=true`
2033
2005
  }
2034
2006
  menuitem.html = `<img class='menu-item-image' src='${imagePath}' /> ${menuitem.text}`
2007
+ } else if (menuitem.hasOwnProperty("icon")) {
2008
+ menuitem.html = `<i class="${menuitem.icon}"></i> ${menuitem.text}`
2035
2009
  } else {
2036
2010
  menuitem.html = `${menuitem.text}`
2037
2011
  }
@@ -2546,7 +2520,6 @@ class Server {
2546
2520
  ...item
2547
2521
  }
2548
2522
  })
2549
- console.log("MENU 1", JSON.stringify(menu, null, 2))
2550
2523
  // let menu = await this.kernel.plugin.config.menu(this.kernel, info)
2551
2524
  let plugin = { menu }
2552
2525
  let uri = filepath
@@ -2594,9 +2567,6 @@ class Server {
2594
2567
  ...item
2595
2568
  }
2596
2569
  })
2597
-
2598
- console.log("MENU 2", JSON.stringify(menu, null, 2))
2599
-
2600
2570
  let plugin = { menu }
2601
2571
  let uri = this.kernel.path("api")
2602
2572
  await this.renderMenu(uri, name, plugin, [])
@@ -2913,6 +2883,52 @@ class Server {
2913
2883
 
2914
2884
  //let home = this.kernel.homedir
2915
2885
  //let home = this.kernel.store.get("home")
2886
+ this.app.get("/launch", ex(async (req, res) => {
2887
+ // parse the url
2888
+ /*
2889
+ is it https://<name>.localhost ?
2890
+ - is <name> already installed?
2891
+ - yes: display
2892
+ - no: 404
2893
+ else: 404
2894
+ */
2895
+ let url = req.query.url
2896
+ let u = new URL(url)
2897
+ let host = u.host
2898
+ if (host.endsWith(".localhost")) {
2899
+ let name = host.replace(/\.localhost$/, '')
2900
+ let env = await Environment.get(this.kernel.homedir)
2901
+ let autolaunch = false
2902
+ if (env && env.PINOKIO_ONDEMAND_AUTOLAUNCH === "1") {
2903
+ autolaunch = true
2904
+ }
2905
+ let api_path = this.kernel.path("api", name)
2906
+ let exists = await this.exists(api_path)
2907
+ if (exists) {
2908
+ let meta = await this.kernel.api.meta(name)
2909
+ console.log({ autolaunch })
2910
+ res.render("start", {
2911
+ autolaunch,
2912
+ logo: this.logo,
2913
+ theme: this.theme,
2914
+ agent: this.agent,
2915
+ name: meta.title,
2916
+ image: meta.icon,
2917
+ //link: autolaunch ? "/p/" + name : "/p/" + name + "?autolaunch"
2918
+ link: autolaunch ? "/p/" + name + "?autolaunch=1" : "/p/" + name + "?autolaunch=0"
2919
+ })
2920
+ return
2921
+ }
2922
+ }
2923
+ res.render("start", {
2924
+ logo: this.logo,
2925
+ theme: this.theme,
2926
+ agent: this.agent,
2927
+ name: "Does not exist",
2928
+ image: "/pinokio-black.png",
2929
+ link: null
2930
+ })
2931
+ }))
2916
2932
  this.app.get("/", ex(async (req, res) => {
2917
2933
  // check bin folder
2918
2934
  // let bin_path = this.kernel.path("bin/miniconda")
@@ -3128,8 +3144,17 @@ class Server {
3128
3144
  // console.log("this.kernel.proto.init")
3129
3145
  // await this.kernel.proto.init()
3130
3146
  let list = this.getPeerInfo()
3147
+ let ai = await this.kernel.proto.ai()
3148
+ ai.push({
3149
+ title: "Use your own AI recipe",
3150
+ description: "Enter your own markdown instruction for AI",
3151
+ meta: {},
3152
+ content: ""
3153
+ })
3154
+ console.log("ai", ai)
3131
3155
  res.render("init/index", {
3132
3156
  list,
3157
+ ai,
3133
3158
  current_host: this.kernel.peer.host,
3134
3159
  cwd: this.kernel.path("api"),
3135
3160
  name: null,
@@ -3758,6 +3783,25 @@ class Server {
3758
3783
  agent: this.agent,
3759
3784
  })
3760
3785
  }))
3786
+ this.app.post("/plugin/update", ex(async (req, res) => {
3787
+ console.time("/plugin/update")
3788
+ try {
3789
+ await this.kernel.exec({
3790
+ message: "git pull",
3791
+ path: this.kernel.path("plugin/code")
3792
+ }, (e) => {
3793
+ console.log(e)
3794
+ })
3795
+ console.timeEnd("/plugin/update")
3796
+ res.json({
3797
+ success: true
3798
+ })
3799
+ } catch (e) {
3800
+ res.json({
3801
+ error: e.stack
3802
+ })
3803
+ }
3804
+ }))
3761
3805
  this.app.post("/network/reset", ex(async (req, res) => {
3762
3806
  let caddy_path = this.kernel.path("cache/XDG_DATA_HOME/caddy")
3763
3807
  await rimraf(caddy_path)
@@ -3780,6 +3824,7 @@ class Server {
3780
3824
  })
3781
3825
  }))
3782
3826
  this.app.get("/net/:name", ex(async (req, res) => {
3827
+ let protocol = req.get('X-Forwarded-Proto')
3783
3828
  let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
3784
3829
  bin: this.kernel.bin.preset("network"),
3785
3830
  })
@@ -3791,21 +3836,19 @@ class Server {
3791
3836
  }
3792
3837
 
3793
3838
  let list = this.getPeerInfo()
3794
- let processes
3839
+ let processes = []
3795
3840
  let host
3796
3841
  let peer
3797
3842
  for(let item of list) {
3798
3843
  if (item.name === req.params.name) {
3799
3844
  processes = item.processes
3800
3845
  host = item.host
3801
- console.log("matched", processes)
3802
3846
  peer = item
3803
3847
  }
3804
3848
  }
3805
3849
  let favicons = {}
3806
3850
  let titles = {}
3807
3851
  let descriptions = {}
3808
- console.time("Favicon")
3809
3852
  //await Promise.all(peer.processes.map((proc) => {
3810
3853
  // console.log("Proc", proc)
3811
3854
  // return new Promise(async (resolve, reject) => {
@@ -3816,55 +3859,63 @@ class Server {
3816
3859
  // }
3817
3860
  // })
3818
3861
  //}))
3819
- let pinokio_ip
3820
- for(let proc of peer.processes) {
3821
- if (proc.internal_port === 42000) {
3822
- // pinokio ip
3823
- pinokio_ip = proc.external_ip
3824
- }
3825
- if (proc.external_router) {
3826
- // try to get icons from pinokio
3827
- for(let router of proc.external_router) {
3828
- // replace the root domain: facefusion-pinokio.git.x.localhost => facefusion-pinokio.git
3829
- let pattern = `.${req.params.name}.localhost`
3830
- if (router.endsWith(pattern)) {
3831
- let name = router.replace(pattern, "")
3832
- let api_path = this.kernel.path("api", name)
3833
- let exists = await this.exists(api_path)
3834
- if (exists) {
3835
- let meta = await this.kernel.api.meta(name)
3836
- if (meta.icon) {
3837
- favicons[proc.external_ip] = meta.icon
3838
- }
3839
- if (meta.title) {
3840
- titles[proc.external_ip] = meta.title
3862
+
3863
+ if (peer && peer.processes) {
3864
+ let pinokio_ip
3865
+ for(let proc of peer.processes) {
3866
+ if (proc.internal_port === 42000) {
3867
+ // pinokio ip
3868
+ pinokio_ip = proc.external_ip
3869
+ }
3870
+ if (proc.external_router) {
3871
+ // try to get icons from pinokio
3872
+ for(let router of proc.external_router) {
3873
+ // replace the root domain: facefusion-pinokio.git.x.localhost => facefusion-pinokio.git
3874
+ let pattern = `.${req.params.name}.localhost`
3875
+ if (router.endsWith(pattern)) {
3876
+ let name = router.replace(pattern, "")
3877
+ let api_path = this.kernel.path("api", name)
3878
+ let exists = await this.exists(api_path)
3879
+ if (exists) {
3880
+ let meta = await this.kernel.api.meta(name)
3881
+ if (meta.icon) {
3882
+ favicons[proc.external_ip] = meta.icon
3883
+ }
3884
+ if (meta.title) {
3885
+ titles[proc.external_ip] = meta.title
3886
+ }
3887
+ if (meta.description) {
3888
+ descriptions[proc.external_ip] = meta.description
3889
+ }
3841
3890
  }
3842
- if (meta.description) {
3843
- descriptions[proc.external_ip] = meta.description
3891
+ }
3892
+ }
3893
+ }
3894
+ // if not an app running inside pinokio, try to fetch and infer the favicon
3895
+ if (!favicons[proc.external_ip]) {
3896
+ if (protocol === "https") {
3897
+ if (proc.external_router.length > 0) {
3898
+ let favicon = await this.kernel.favicon.get("https://" + proc.external_router[0])
3899
+ if (favicon) {
3900
+ favicons[proc.external_ip] = favicon
3844
3901
  }
3845
3902
  }
3903
+ } else {
3904
+ let favicon = await this.kernel.favicon.get("http://" + proc.external_ip)
3905
+ if (favicon) {
3906
+ favicons[proc.external_ip] = favicon
3907
+ }
3846
3908
  }
3847
3909
  }
3848
3910
  }
3849
- // if not running from pinokio, try to fetch and infer the favicon
3850
- if (!favicons[proc.external_ip]) {
3851
- let favicon = await this.kernel.favicon.get("http://" + proc.external_ip)
3852
- if (favicon) {
3853
- favicons[proc.external_ip] = favicon
3911
+ for (let external_ip in favicons) {
3912
+ let favicon_path = favicons[external_ip]
3913
+ if (!favicon_path.startsWith("http")) {
3914
+ favicons[external_ip] = "http://" + pinokio_ip + favicon_path
3854
3915
  }
3855
3916
  }
3856
3917
  }
3857
- for (let external_ip in favicons) {
3858
- let favicon_path = favicons[external_ip]
3859
- if (!favicon_path.startsWith("http")) {
3860
- favicons[external_ip] = "http://" + pinokio_ip + favicon_path
3861
- }
3862
- }
3863
- console.timeEnd("Favicon")
3864
- console.log("favicons", favicons)
3865
-
3866
3918
  let current_urls = await this.current_urls(req.originalUrl.slice(1))
3867
- console.log("LIST", JSON.stringify(list, null, 2))
3868
3919
  res.render("net", {
3869
3920
  selected_name: req.params.name,
3870
3921
  favicons,
@@ -4025,7 +4076,6 @@ class Server {
4025
4076
  })
4026
4077
  }))
4027
4078
  this.app.post("/state", ex(async (req, res) => {
4028
- console.log("POST /state", req.body)
4029
4079
  /*
4030
4080
  req.body := {
4031
4081
  name: <name>,
@@ -4051,7 +4101,6 @@ class Server {
4051
4101
  this.menu_hidden[req.body.name][req.body.type] = true
4052
4102
  }
4053
4103
  }
4054
- console.log("select", req.body)
4055
4104
  res.json({
4056
4105
  success: true
4057
4106
  })
@@ -4524,9 +4573,6 @@ class Server {
4524
4573
  } catch (e) {
4525
4574
  console.log("ERROR", e)
4526
4575
  }
4527
-
4528
- console.log("gitRemote", gitRemote)
4529
-
4530
4576
  res.render("env_editor", {
4531
4577
  gitRemote,
4532
4578
  home: null,
@@ -4965,11 +5011,12 @@ console.log("gitRemote", gitRemote)
4965
5011
  })
4966
5012
  }))
4967
5013
  this.app.get("/d/*", ex(async (req, res) => {
5014
+ console.log("> 1")
4968
5015
  let filepath = Util.u2p(req.params[0])
4969
5016
  let plugin = await this.getPluginGlobal(filepath)
5017
+ console.log("> 2")
4970
5018
  let html = ""
4971
5019
  let plugin_menu
4972
- console.log("*********** plugin", plugin)
4973
5020
  try {
4974
5021
  plugin_menu = plugin.menu
4975
5022
  //plugin_menu = plugin.menu[0].menu
@@ -4977,65 +5024,104 @@ console.log("gitRemote", gitRemote)
4977
5024
  plugin_menu = []
4978
5025
  }
4979
5026
  let current_urls = await this.current_urls(req.originalUrl.slice(1))
4980
- console.log("RUNNING", this.kernel.api.running)
4981
- console.log("DYNAMIC", JSON.stringify(plugin_menu, null, 2))
5027
+ console.log("> 3")
4982
5028
  let retry = false
4983
5029
  // if plugin_menu is empty, try again in 1 sec
4984
5030
  if (plugin_menu.length === 0) {
4985
5031
  retry = true
4986
5032
  }
4987
-
4988
5033
  let venvs = await Util.find_venv(filepath)
4989
- console.log({ venvs, filepath })
5034
+ console.log("> 4")
4990
5035
  let terminal
4991
-
4992
5036
  if (venvs.length > 0) {
4993
5037
  let terminals = []
4994
5038
  try {
4995
- for(let venv of venvs) {
5039
+ for(let i=0; i<venvs.length; i++) {
5040
+ let venv = venvs[i]
4996
5041
  let parsed = path.parse(venv)
4997
- terminals.push({
5042
+ terminals.push(this.renderShell(filepath, i, 0, {
4998
5043
  icon: "fa-brands fa-python",
4999
- title: "[venv] " + parsed.name,
5044
+ title: "Python virtual environment",
5045
+ subtitle: this.kernel.path("api", parsed.name),
5046
+ type: "Start",
5000
5047
  shell: {
5001
5048
  venv: venv,
5002
5049
  input: true,
5003
5050
  }
5004
- })
5051
+ }))
5005
5052
  }
5006
5053
  } catch (e) {
5007
5054
  console.log(e)
5008
5055
  }
5009
5056
  terminal = {
5010
5057
  icon: "fa-solid fa-terminal",
5011
- text: "Terminal",
5058
+ title: "Open web terminal",
5059
+ subtitle: "Open the terminal in the browser",
5012
5060
  menu: terminals
5013
5061
  }
5014
5062
  } else {
5015
5063
  terminal = {
5016
5064
  icon: "fa-solid fa-terminal",
5017
- text: "Terminal",
5018
- menu: [{
5065
+ title: "Open web terminal",
5066
+ subtitle: "Work with the terminal directly in the browser",
5067
+ menu: [this.renderShell(filepath, 0, 0, {
5019
5068
  icon: "fa-solid fa-terminal",
5020
- title: `Terminal (${filepath})`,
5069
+ title: "Terminal",
5070
+ subtitle: filepath,
5071
+ type: "Start",
5021
5072
  shell: {
5022
5073
  input: true
5023
5074
  }
5024
- }]
5075
+ })]
5025
5076
  }
5026
5077
  }
5027
-
5078
+ console.log("> 5")
5079
+
5080
+ let exec_menus = []
5081
+ let shell_menus = []
5082
+ if (plugin_menu.length > 0) {
5083
+ for(let item of plugin_menu) {
5084
+ // if shell.run method exists
5085
+ // if exec method exists
5086
+ let mode
5087
+ for(let step of item.run) {
5088
+ if (step.method === "exec") {
5089
+ mode = "exec"
5090
+ break
5091
+ }
5092
+ if (step.method === "shell.run") {
5093
+ mode = "shell"
5094
+ break
5095
+ }
5096
+ }
5097
+ if (mode === "exec") {
5098
+ item.type = "Open"
5099
+ exec_menus.push(item)
5100
+ } else if (mode === "shell") {
5101
+ item.type = "Start"
5102
+ shell_menus.push(item)
5103
+ }
5104
+ }
5105
+ exec_menus.sort((a, b) => { return a > b })
5106
+ shell_menus.sort((a, b) => { return a > b })
5107
+ }
5108
+ console.log("> 6")
5028
5109
  let dynamic = [
5029
- terminal,
5030
5110
  {
5031
5111
  icon: "fa-solid fa-robot",
5032
- text: "Build with AI",
5033
- menu: plugin_menu
5034
- }
5112
+ title: "Get started building with AI",
5113
+ subtitle: "Start making changes to this project using AI",
5114
+ menu: shell_menus
5115
+ },
5116
+ {
5117
+ icon: "fa-solid fa-arrow-up-right-from-square",
5118
+ title: "Open in external apps",
5119
+ subtitle: "Open this project in 3rd party apps",
5120
+ menu: exec_menus
5121
+ },
5122
+ terminal,
5035
5123
  ]
5036
-
5037
- console.log("### dynamic", JSON.stringify(dynamic, null, 2))
5038
-
5124
+ console.log("> 7")
5039
5125
  res.render("d", {
5040
5126
  retry,
5041
5127
  current_urls,
@@ -5191,7 +5277,6 @@ console.log("gitRemote", gitRemote)
5191
5277
  // await this.kernel.plugin.init()
5192
5278
  let plugin = await this.getPlugin(req.params.name)
5193
5279
  let html = ""
5194
- console.log("plugin", JSON.stringify(plugin, null, 2))
5195
5280
  let plugin_menu
5196
5281
  if (plugin) {
5197
5282
  if (plugin && plugin.menu && Array.isArray(plugin.menu)) {
@@ -5203,9 +5288,7 @@ console.log("gitRemote", gitRemote)
5203
5288
  })
5204
5289
  })
5205
5290
  }
5206
- } else {
5207
5291
  }
5208
- console.log({ html })
5209
5292
  res.send(html)
5210
5293
  }))
5211
5294
  this.app.get("/pinokio/ai/:name", ex(async (req, res) => {
@@ -5326,6 +5409,9 @@ console.log("gitRemote", gitRemote)
5326
5409
  }))
5327
5410
  this.app.get("/pinokio/peer", ex(async (req, res) => {
5328
5411
  // await this.kernel.refresh()
5412
+ let current_peer_info = this.kernel.peer.current_host(this.kernel)
5413
+ res.json(current_peer_info)
5414
+ /*
5329
5415
  res.json({
5330
5416
  home: this.kernel.homedir,
5331
5417
  arch: this.kernel.arch,
@@ -5338,6 +5424,7 @@ console.log("gitRemote", gitRemote)
5338
5424
  router: this.kernel.router.published(),
5339
5425
  memory: this.kernel.memory
5340
5426
  })
5427
+ */
5341
5428
  }))
5342
5429
  this.app.get("/pinokio/memory", ex((req, res) => {
5343
5430
  let filepath = req.query.filepath
@@ -5409,25 +5496,25 @@ console.log("gitRemote", gitRemote)
5409
5496
  }
5410
5497
  }))
5411
5498
  this.app.get("/pinokio/launch/:name", ex(async (req, res) => {
5412
- this.chrome(req, res, "launch")
5499
+ await this.chrome(req, res, "launch")
5413
5500
  }))
5414
5501
  this.app.get("/pinokio/browser/:name/dev", ex(async (req, res) => {
5415
- this.chrome(req, res, "browse")
5502
+ await this.chrome(req, res, "browse")
5416
5503
  }))
5417
5504
  this.app.get("/pinokio/browser/:name/browse", ex(async (req, res) => {
5418
- this.chrome(req, res, "browse")
5505
+ await this.chrome(req, res, "browse")
5419
5506
  }))
5420
5507
  this.app.get("/pinokio/browser/:name", ex(async (req, res) => {
5421
- this.chrome(req, res, "run")
5508
+ await this.chrome(req, res, "run")
5422
5509
  }))
5423
5510
  this.app.get("/p/:name/dev", ex(async (req, res) => {
5424
- this.chrome(req, res, "browse")
5511
+ await this.chrome(req, res, "browse")
5425
5512
  }))
5426
5513
  this.app.get("/p/:name/browse", ex(async (req, res) => {
5427
- this.chrome(req, res, "browse")
5514
+ await this.chrome(req, res, "browse")
5428
5515
  }))
5429
5516
  this.app.get("/p/:name", ex(async (req, res) => {
5430
- this.chrome(req, res, "run")
5517
+ await this.chrome(req, res, "run")
5431
5518
  }))
5432
5519
  this.app.post("/pinokio/delete", ex(async (req, res) => {
5433
5520
  try {