pinokiod 3.8.151 → 3.8.152

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.
@@ -369,7 +369,7 @@ class Bin {
369
369
  let channel = chunks[3]
370
370
  let coerced = semver.coerce(version)
371
371
  let mamba_requirement = ">=24.11.1"
372
- console.log({ name, channel, version })
372
+ // console.log({ name, channel, version })
373
373
  //if (semver.satisfies(coerced, mamba_requirement) && channel === "conda-forge") {
374
374
  if (semver.satisfies(coerced, mamba_requirement)) {
375
375
  conda_check.mamba = true
@@ -439,11 +439,10 @@ class Bin {
439
439
 
440
440
  if (this.platform === "darwin") {
441
441
  // 3. brew
442
- console.log("## check brew")
443
442
 
444
443
  let brew_path = path.resolve(this.kernel.homedir, "bin", "homebrew")
445
444
  let brew_exists = await this.exists(brew_path)
446
- console.log({ brew_path, brew_exists })
445
+ // console.log({ brew_path, brew_exists })
447
446
 
448
447
  let brew = []
449
448
  if (brew_exists) {
@@ -451,7 +450,7 @@ class Bin {
451
450
  res = await this.exec({ message: `brew list -1`, conda: { skip: true } }, (stream) => {
452
451
  // console.log("brew list check", { stream })
453
452
  })
454
- console.log("BREW", res.response)
453
+ // console.log("BREW", res.response)
455
454
  lines = res.response.split(/[\r\n]+/).slice(0, -1) // ignore last line since it's the prompt
456
455
  let end = false
457
456
  for(let line of lines) {
@@ -475,7 +474,7 @@ class Bin {
475
474
 
476
475
 
477
476
  // check brew_installed
478
- console.log("checking brew installed")
477
+ // console.log("checking brew installed")
479
478
  let e = await this.kernel.bin.exists("homebrew")
480
479
  let { stdout }= await this.exec({ message: "xcode-select -p", conda: { skip: true } }, (stream) => { })
481
480
  let e2 = /(.*Library.*Developer.*CommandLineTools.*|.*Xcode.*Developer.*)/gi.test(stdout)
@@ -493,7 +492,7 @@ class Bin {
493
492
  e4 = /xcode-select version ([0-9]+)/gi.exec(result.stdout)
494
493
  if (e4 && e4.length > 1) {
495
494
  let version = Number(e4[1])
496
- console.log("xcode-select version", version)
495
+ // console.log("xcode-select version", version)
497
496
  if (version >= 2349) {
498
497
  e4 = true
499
498
  } else {
package/kernel/index.js CHANGED
@@ -257,12 +257,12 @@ class Kernel {
257
257
  }
258
258
  // console.log("kernel.refresh", { active, notify_peers })
259
259
 
260
- // let caddy_installed = await this.bin.check_installed({ name: "caddy" })
260
+ let caddy_installed = await this.bin.check_installed({ name: "caddy" })
261
261
 
262
262
  // 1. https
263
263
  // 2. https + local share
264
264
  // console.log("caddy installed?", caddy_installed)
265
- // if (caddy_installed && https_active) {
265
+ if (caddy_installed && https_active) {
266
266
 
267
267
  // 1. get the process list
268
268
  await this.processes.refresh()
@@ -293,7 +293,7 @@ class Kernel {
293
293
  // 9. announce self to the peer network
294
294
  this.peer.announce()
295
295
 
296
- // }
296
+ }
297
297
 
298
298
  //if (https_installed && peer_active) {
299
299
 
package/kernel/loader.js CHANGED
@@ -43,12 +43,14 @@ class Loader {
43
43
  async requireJSON(filepath) {
44
44
  let config
45
45
  try { config = require(filepath) } catch (e) {
46
+ console.log(e)
46
47
  }
47
48
  return config
48
49
  }
49
50
  async requireJS(filepath) {
50
51
  let config
51
52
  try { config = require(filepath) } catch (e) {
53
+ console.log(e)
52
54
  }
53
55
  try {
54
56
  // if the required module is a class, return the instantiated object
@@ -33,7 +33,6 @@ class Router {
33
33
  this.custom_routers = {}
34
34
  }
35
35
  async init() {
36
- console.log("router.init")
37
36
  // if ~/pinokio/network doesn't exist, clone
38
37
  let exists = await this.kernel.exists("network/system")
39
38
  if (!exists) {
@@ -190,7 +189,6 @@ class Router {
190
189
  await this.fill()
191
190
  }
192
191
  this.mapping = this._mapping
193
- console.log("after local()", JSON.stringify(this.mapping, null, 2))
194
192
  }
195
193
 
196
194
  // set remote config
@@ -9,9 +9,7 @@ class PeerPortRouter extends Processor {
9
9
  }
10
10
  handle(peer) {
11
11
  let https_domains_mapping = this.router.info[peer.host]
12
- console.log({ https_domains_mapping })
13
12
  for(let dial in https_domains_mapping) {
14
- console.log({ dial })
15
13
  let parsed_dial = this.parse_ip(dial)
16
14
  if (parsed_dial.host !== peer.host) {
17
15
  // ONLY connect for 127.0.0.1 or localhost
package/kernel/shells.js CHANGED
@@ -99,6 +99,7 @@ class Shells {
99
99
  break: false
100
100
  }]
101
101
  params.on = params.on.concat(defaultHandlers)
102
+
102
103
  let response = await sh.start(params, async (stream) => {
103
104
  /*
104
105
  {
@@ -462,6 +463,9 @@ class Shells {
462
463
  return shell.group === group
463
464
  })
464
465
  return found
466
+ } else if (request.filter) {
467
+ let found = this.shells.filter(request.filter)
468
+ return found
465
469
  }
466
470
  }
467
471
  get(id) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "3.8.151",
3
+ "version": "3.8.152",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -19,7 +19,7 @@ const semver = require('semver')
19
19
  const fse = require('fs-extra')
20
20
  const QRCode = require('qrcode')
21
21
  const axios = require('axios')
22
-
22
+ const crypto = require('crypto')
23
23
 
24
24
  const git = require('isomorphic-git')
25
25
  const http = require('isomorphic-git/http/node')
@@ -1126,6 +1126,7 @@ class Server {
1126
1126
 
1127
1127
  // check if there is a running process with this folder name
1128
1128
  let runningApps = new Set()
1129
+ console.log("API RUNNING", this.kernel.api.running)
1129
1130
  for(let key in this.kernel.api.running) {
1130
1131
  let p = this.kernel.path("api", items[i].name) + path.sep
1131
1132
  //let re = new RegExp(items[i].name)
@@ -1143,6 +1144,17 @@ class Server {
1143
1144
  running.push(items[i])
1144
1145
  // break
1145
1146
  }
1147
+ let shell = this.kernel.shell.find({
1148
+ filter: (shell) => {
1149
+ return shell.id.startsWith(items[i].name + "_")
1150
+ }
1151
+ })
1152
+ if (shell.length > 0) {
1153
+ items[i].running = true
1154
+ items[i].index = index
1155
+ index++;
1156
+ running.push(items[i])
1157
+ }
1146
1158
  }
1147
1159
  if (!items[i].running) {
1148
1160
  items[i].index = index
@@ -1156,6 +1168,7 @@ class Server {
1156
1168
 
1157
1169
  running = this.getItems(running, meta, p)
1158
1170
  notRunning = this.getItems(notRunning, meta, p)
1171
+ console.log({ notRunning })
1159
1172
 
1160
1173
  // check running for each
1161
1174
  // running_items
@@ -1423,6 +1436,61 @@ class Server {
1423
1436
  menuitem.href = menuitem.href + "?" + new URLSearchParams(menuitem.params).toString();
1424
1437
  }
1425
1438
 
1439
+
1440
+ if (menuitem.shell) {
1441
+ /*
1442
+ shell :- {
1443
+ id (optional),
1444
+ path (required), // api, bin, prototype, network, api/
1445
+ message (optional), // if not specified, start an empty shell
1446
+ venv,
1447
+ input, // input mode if true
1448
+ callback, // callback url after shutting down
1449
+ kill, // when to kill (regular expression)
1450
+ }
1451
+ */
1452
+
1453
+ let rendered = this.kernel.template.render(menuitem.shell, {})
1454
+ let params = new URLSearchParams()
1455
+ // if (rendered.id) {
1456
+ // params.set("id", encodeURIComponent(rendered.id))
1457
+ // } else {
1458
+ // let shell_id = "sh_" + name + "_" + i
1459
+ // params.set("id", encodeURIComponent(shell_id))
1460
+ // }
1461
+ let basePath = this.kernel.path("api", name)
1462
+ if (rendered.path) {
1463
+ params.set("path", encodeURIComponent(this.kernel.api.filePath(rendered.path, basePath)))
1464
+ } else {
1465
+ params.set("path", encodeURIComponent(basePath))
1466
+ }
1467
+ if (rendered.message) params.set("message", encodeURIComponent(rendered.message))
1468
+ if (rendered.venv) params.set("venv", encodeURIComponent(rendered.venv))
1469
+ if (rendered.input) params.set("input", true)
1470
+ if (rendered.callback) params.set("callback", encodeURIComponent(rendered.callback))
1471
+ if (rendered.env) {
1472
+ for(let key in rendered.env) {
1473
+ let env_key = "env." + key
1474
+ params.set(env_key, rendered.env[key])
1475
+ }
1476
+ }
1477
+
1478
+ // deterministic shell id generation
1479
+ // `${api_path}_${i}_${hash}`
1480
+ let hash = crypto.createHash('md5').update(JSON.stringify(rendered)).digest('hex')
1481
+ let shell_id
1482
+ if (rendered.id) {
1483
+ shell_id = encodeURIComponent(`${name}_${rendered.id}`)
1484
+ } else {
1485
+ shell_id = encodeURIComponent(`${name}_${i}_${hash}`)
1486
+ }
1487
+ menuitem.href = "/shell/" + shell_id + "?" + params.toString()
1488
+ let shell = this.kernel.shell.get(shell_id)
1489
+ if (shell) {
1490
+ menuitem.running = true
1491
+ }
1492
+ }
1493
+
1426
1494
  if (menuitem.href) {
1427
1495
  let u
1428
1496
  if (menuitem.href.startsWith("http")) {
@@ -2222,12 +2290,12 @@ class Server {
2222
2290
  //let home = this.kernel.store.get("home")
2223
2291
  this.app.get("/", ex(async (req, res) => {
2224
2292
  // check bin folder
2225
- let bin_path = this.kernel.path("bin/miniconda")
2226
- let bin_exists = await this.exists(bin_path)
2227
- if (!bin_exists) {
2228
- res.redirect("/setup")
2229
- return
2230
- }
2293
+ // let bin_path = this.kernel.path("bin/miniconda")
2294
+ // let bin_exists = await this.exists(bin_path)
2295
+ // if (!bin_exists) {
2296
+ // res.redirect("/setup")
2297
+ // return
2298
+ // }
2231
2299
 
2232
2300
 
2233
2301
  if (req.query.mode !== "settings" && !home) {
@@ -2456,14 +2524,60 @@ class Server {
2456
2524
  })
2457
2525
  }
2458
2526
  }))
2459
- this.app.get("/terminal/:api/:id", ex(async (req, res) => {
2460
- res.render("minterm", {
2527
+ this.app.get("/shell/:id", ex(async (req, res) => {
2528
+ /*
2529
+ req.query := {
2530
+ path (required), // api, bin, prototype, network, api/
2531
+ message (optional), // if not specified, start an empty shell
2532
+ venv,
2533
+ callback,
2534
+ kill, // regex for killing
2535
+ env.<key1>,
2536
+ env.<key2>,
2537
+ ...
2538
+ }
2539
+ */
2540
+
2541
+ console.log("SHELLS", this.kernel.shell.shells.map((s) => { return { id: s.id, message: s.params.message } }))
2542
+ // create a new term from cwd
2543
+ let id = decodeURIComponent(req.params.id)
2544
+ let cwd = this.kernel.path(this.kernel.api.filePath(decodeURIComponent(req.query.path)))
2545
+ let message = req.query.message ? decodeURIComponent(req.query.message) : null
2546
+ let venv = req.query.venv ? decodeURIComponent(req.query.venv) : null
2547
+ let input = req.query.input ? true : false
2548
+ let callback = req.query.callback ? decodeURIComponent(req.query.callback) : null
2549
+ let env = {}
2550
+ for(let env_key in req.query) {
2551
+ if (env_key.startsWith("env.")) {
2552
+ let chunks = env_key.split(".")
2553
+ let key = chunks.slice(1).join(".")
2554
+ env[key] = req.query[env_key]
2555
+ }
2556
+ }
2557
+
2558
+ let shell = this.kernel.shell.get(id)
2559
+ console.log({ shell })
2560
+ res.render("shell", {
2461
2561
  theme: this.theme,
2462
2562
  agent: this.agent,
2463
- cwd: this.kernel.path("api/" + req.params.api),
2464
- id: req.params.id
2563
+ id,
2564
+ cwd,
2565
+ message,
2566
+ venv,
2567
+ env,
2568
+ input,
2569
+ callback,
2570
+ running: (shell ? true : false)
2465
2571
  })
2466
2572
  }))
2573
+ // this.app.get("/terminal/:api/:id", ex(async (req, res) => {
2574
+ // res.render("shell", {
2575
+ // theme: this.theme,
2576
+ // agent: this.agent,
2577
+ // cwd: this.kernel.path("api/" + req.params.api),
2578
+ // id: req.params.id
2579
+ // })
2580
+ // }))
2467
2581
  this.app.get("/peer_check", ex(async (req, res) => {
2468
2582
  if (this.kernel.peer.refreshing) {
2469
2583
  res.json({ updated: false })
@@ -2532,7 +2646,7 @@ class Server {
2532
2646
  })
2533
2647
  console.log({ requirements, install_required, requirements_pending })
2534
2648
 
2535
- if (install_required) {
2649
+ if (!requirements_pending && install_required) {
2536
2650
  res.redirect("/setup/network?callback=/network")
2537
2651
  return
2538
2652
  }
@@ -2617,6 +2731,8 @@ class Server {
2617
2731
  }
2618
2732
  }
2619
2733
 
2734
+
2735
+
2620
2736
  res.render("network", {
2621
2737
  current_host: this.kernel.peer.host,
2622
2738
  peers,
@@ -3774,6 +3890,7 @@ class Server {
3774
3890
  this.app.post("/network", ex(async (req, res) => {
3775
3891
  if (this.kernel.homedir) {
3776
3892
  let fullpath = path.resolve(this.kernel.homedir, "ENVIRONMENT")
3893
+ console.log("req.body", req.body)
3777
3894
  await Util.update_env(fullpath, req.body)
3778
3895
  res.json({ success: true })
3779
3896
  } else {
@@ -0,0 +1,40 @@
1
+ const Commander = async (config) => {
2
+ console.log({ config })
3
+ let result = await Swal.fire({
4
+ title: `<i class='${config.icon}'></i> ${config.title}`,
5
+ html: `<div class='commander-dialog'>${config.description}</div>
6
+ <input style='width:100%; padding: 10px;' class='cmd' type='text' placeholder='${config.placeholder}'>`,
7
+ allowOutsideClick: true,
8
+ focusConfirm: false,
9
+ // showCancelButton: true,
10
+ confirmButtonText: 'OK',
11
+ didOpen: () => {
12
+ const input = document.querySelector(".cmd")
13
+ input.focus()
14
+ },
15
+ preConfirm: () => {
16
+ let message = config.message(Swal.getPopup().querySelector(".cmd").value)
17
+ let path = config.path
18
+ let callback = config.callback
19
+ let search_params = new URLSearchParams()
20
+ search_params.set("message", message)
21
+ if (config.callback) {
22
+ search_params.set("callback", config.callback)
23
+ }
24
+ search_params.set("path", config.path)
25
+ if (config.venv) {
26
+ search_params.set("venv", config.venv)
27
+ }
28
+
29
+ // env
30
+ if (config.env) {
31
+ for(let key in config.env) {
32
+ search_params.set("env." + key, config.env[key])
33
+ }
34
+ }
35
+ let terminal_path = "/shell/" + config.id + "?" + search_params.toString()
36
+ location.href = terminal_path
37
+ }
38
+ })
39
+ console.log({ result })
40
+ }
@@ -571,6 +571,16 @@ body.fullscreen .btn {
571
571
  border: 1px solid rgba(0,0,0,0.1);
572
572
  */
573
573
  }
574
+ body.dark .commander-dialog {
575
+ background: rgba(255,255,100,0.08);
576
+ }
577
+ .commander-dialog {
578
+ font-size: 14px;
579
+ text-align: left;
580
+ margin-bottom: 10px;
581
+ padding: 15px;
582
+ background: rgba(0,0,100,0.04);
583
+ }
574
584
  .runnables {
575
585
  padding: 20px;
576
586
  box-sizing: border-box;
@@ -1067,7 +1077,7 @@ body.dark .line i {
1067
1077
  /*
1068
1078
  padding: 10px;
1069
1079
  */
1070
- padding: 4px;
1080
+ padding: 8px;
1071
1081
  /*
1072
1082
  width: 40px;
1073
1083
  height: 40px;
@@ -1681,7 +1691,7 @@ body.dark .not-running-apps, body.dark .running-apps {
1681
1691
  }
1682
1692
  */
1683
1693
  .running-apps .line img {
1684
- background: #82bc09;
1694
+ border: 3px solid #82bc09;
1685
1695
  /*
1686
1696
  border: 3px solid #8fd400;
1687
1697
  */
package/server/socket.js CHANGED
@@ -19,6 +19,7 @@ class Socket {
19
19
  });
20
20
  ws.on('message', async (message) => {
21
21
  const req = JSON.parse(message);
22
+ console.log({ req })
22
23
  if (req.response) {
23
24
  this.parent.kernel.api.respond(req)
24
25
  } else {
@@ -89,7 +90,17 @@ class Socket {
89
90
  let buf = this.buffer[req.id]
90
91
  let sh = this.active_shell[req.id]
91
92
  this.subscribe(ws, req.id, buf, sh)
92
- if (!sh) {
93
+ if (sh) {
94
+ // if the active shell exists, check if it's killed
95
+ // if the shell is running, don't do anything
96
+ // if the shell is not running, run the request
97
+ let shell = this.parent.kernel.shell.get(sh)
98
+ console.log("get shell for", sh, shell)
99
+ if (!shell) {
100
+ this.parent.kernel.api.process(req)
101
+ }
102
+ // if it's not killed, don't do anything
103
+ } else {
93
104
  this.parent.kernel.api.process(req)
94
105
  }
95
106
  } else {
@@ -686,9 +686,11 @@ body.dark .loader-container {
686
686
  <a target="<%=src%>" href="<%=src%>" class='btn header-item frame-link' data-index="0">
687
687
  <div class='tab'><i class="fa-regular fa-folder-open"></i> Files <div class='disk-usage'></div></div>
688
688
  </a>
689
+ <!--
689
690
  <a target="_blank" href="/terminal/<%=name%>/<%=Date.now()%>" class='btn header-item frame-link' data-index="1">
690
691
  <div class='tab'><i class="fa-solid fa-terminal"></i> Terminal</div>
691
692
  </a>
693
+ -->
692
694
  <a target="_blank" href="<%=profile%>" class='btn header-item frame-link'>
693
695
  <div class='tab'><i class="fa-regular fa-circle-user"></i> Publisher</div>
694
696
  </a>
@@ -12,6 +12,7 @@
12
12
  <link href="/noty.css" rel="stylesheet"/>
13
13
  <link href="/style.css" rel="stylesheet"/>
14
14
  <script src="/sweetalert2.js"></script>
15
+ <script src="/commander.js"></script>
15
16
  <% if (agent === "electron") { %>
16
17
  <link href="/electron.css" rel="stylesheet"/>
17
18
  <% } %>
@@ -200,6 +201,7 @@ a.ln:hover {
200
201
  box-sizing: border-box;
201
202
  }
202
203
  .container-row.current {
204
+ background: rgba(0,0,255,0.08);
203
205
  }
204
206
  /*
205
207
  .container-row.current .header-label-sub {
@@ -212,6 +214,11 @@ a.ln:hover {
212
214
  */
213
215
  padding: 10px 0;
214
216
  }
217
+ #advanced-label {
218
+ text-decoration: underline;
219
+ color: royalblue;
220
+ cursor: pointer;
221
+ }
215
222
  .container-row table {
216
223
  width: 100%;
217
224
  }
@@ -266,14 +273,16 @@ td:first-child, th:first-child {
266
273
  .config-row {
267
274
  padding: 10px 20px 0 0;
268
275
  }
269
- .config-row .label {
276
+ .label {
270
277
  padding-bottom: 5px;
271
278
  font-weight: bold;
272
279
  }
273
280
  .config-row input[type=text] {
274
281
  padding: 7px;
275
282
  }
276
-
283
+ .btn.save {
284
+ padding: 8px;
285
+ }
277
286
  .switcher {
278
287
  position: relative;
279
288
  display: inline-block;
@@ -340,6 +349,22 @@ input:checked + .slider:before {
340
349
  text-align: center;
341
350
  padding: 50px;
342
351
  }
352
+ .advanced a.btn {
353
+ text-align: center;
354
+ }
355
+ .advanced {
356
+ margin: 20px 0;
357
+ }
358
+ .advanced h3 {
359
+ margin: 0 0 5px;
360
+ }
361
+ .advanced .row {
362
+ display: flex;
363
+ }
364
+ .advanced input[type=text] {
365
+ flex-grow: 1;
366
+ padding: 10px;
367
+ }
343
368
 
344
369
 
345
370
  @media only screen and (max-width: 480px) {
@@ -377,8 +402,9 @@ input:checked + .slider:before {
377
402
  <div class='config'>
378
403
  <div class='config-header'>
379
404
  <div class='header-label'><i class='fa-solid fa-wifi'></i> Network</div>
380
- <div class='desc'>Serve Pinokio, installed apps, and even external apps to any device on the same network.</div>
405
+ <div class='desc'>Instant HTTPS for every app on your local network.</div>
381
406
  </div>
407
+ <!--
382
408
  <div class='config-body'>
383
409
  <div class='config-row'>
384
410
  <div class='label'>Instant HTTPS</div>
@@ -388,21 +414,41 @@ input:checked + .slider:before {
388
414
  </label>
389
415
  </div>
390
416
  </div>
417
+ -->
391
418
  <div class='config-body'>
419
+ <!--
392
420
  <div class='config-row'>
393
- <div class='label'>Network Share</div>
421
+ <div class='label'>Local Network Share</div>
394
422
  <label class="switcher">
395
423
  <input id='peer_active' type="checkbox" <%=peer_active ? 'checked' : ''%>>
396
424
  <span class="slider"></span>
397
425
  </label>
398
426
  </div>
427
+ -->
428
+ <div class='config-row'>
429
+ <div id='active-label' class='label'><%=peer_active ? "On" : "Off"%></div>
430
+ <label class="switcher">
431
+ <input id='active' type="checkbox" <%=peer_active ? 'checked' : ''%>>
432
+ <span class="slider"></span>
433
+ </label>
434
+ </div>
399
435
  <div class='config-row' id='network_name'>
400
436
  <div class='label'>Network Name</div>
401
437
  <input id='name' type='text' value="<%=name%>">
402
438
  </div>
403
- <div class='flexible'></div>
404
439
  <button class='btn save'>Save</button>
405
440
  </div>
441
+ <div class='advanced'>
442
+ <div class='row'>
443
+ <div id='advanced-label' class='label'>Advanced</div>
444
+ </div>
445
+ <!--
446
+ <div id='advanced-input' class='row hidden'>
447
+ <input id='git' type='text' placeholder='git url for custom network router'>
448
+ <a id="custom_download" class='btn'>Install</a>
449
+ </div>
450
+ -->
451
+ </div>
406
452
  </div>
407
453
  <div class='loading hidden'>
408
454
  <i class="fa-solid fa-circle-notch fa-spin"></i> Stand by. Do not close this window..
@@ -549,19 +595,36 @@ input:checked + .slider:before {
549
595
  </div>
550
596
  </main>
551
597
  <script>
552
- if (document.querySelector("#peer_active").checked) {
598
+ document.querySelector("#advanced-label").addEventListener("click", (e) => {
599
+ //document.querySelector("#advanced-input").classList.toggle("hidden")
600
+ Commander({
601
+ callback: "/network",
602
+ path: "~/network",
603
+ icon: "fa-solid fa-circle-down",
604
+ title: "Custom Router",
605
+ placeholder: "enter a custom router git url",
606
+ description: `<div>you can write your own custom routers to handle custom request handling for any port <a target="_blank" href="https://program.pinokio.computer">Learn more</a></div>`,
607
+ message: (url) => {
608
+ let name = new URL(url).pathname.split("/").slice(-2).join("_")
609
+ return encodeURIComponent(`git clone ${url} ${name}`)
610
+ }
611
+ })
612
+ })
613
+ if (document.querySelector("#active").checked) {
553
614
  console.log("checked")
554
615
  document.querySelector("#network_name").classList.remove("hidden")
555
616
  } else {
556
617
  console.log("not checked")
557
618
  document.querySelector("#network_name").classList.add("hidden")
558
619
  }
559
- document.querySelector("#peer_active").addEventListener("change", (e)=> {
620
+ document.querySelector("#active").addEventListener("change", (e)=> {
560
621
  console.log(e.target.checked)
561
622
  if (e.target.checked) {
562
623
  document.querySelector("#network_name").classList.remove("hidden")
624
+ document.querySelector("#active-label").innerHTML = "On"
563
625
  } else {
564
626
  document.querySelector("#network_name").classList.add("hidden")
627
+ document.querySelector("#active-label").innerHTML = "Off"
565
628
  }
566
629
  })
567
630
  document.querySelector("main").addEventListener("click", async (e) => {
@@ -574,13 +637,20 @@ document.querySelector("main").addEventListener("click", async (e) => {
574
637
  }
575
638
 
576
639
  if (target) {
640
+ let active = document.querySelector("#active").checked ? "1": "0"
641
+ let body = {
642
+ PINOKIO_NETWORK_NAME: document.querySelector("#name").value,
643
+ PINOKIO_NETWORK_ACTIVE: active,
644
+ PINOKIO_HTTPS_ACTIVE: active,
645
+ }
646
+ /*
577
647
  let body = {
578
- PINOKIO_NETWORK_NAME: document.querySelector("#network_name").value,
648
+ PINOKIO_NETWORK_NAME: document.querySelector("#name").value,
579
649
  PINOKIO_NETWORK_ACTIVE: (document.querySelector("#peer_active").checked ? "1": "0"),
580
650
  PINOKIO_HTTPS_ACTIVE: (document.querySelector("#https_active").checked ? "1": "0")
581
651
  }
652
+ */
582
653
  console.log(body)
583
- debugger
584
654
  let r = await fetch("/network", {
585
655
  method: "post",
586
656
  headers: { "Content-Type": "application/json" },
@@ -141,6 +141,9 @@ body.dark .badge {
141
141
  height: 20px;
142
142
  margin-right: 10px;
143
143
  }
144
+ .prototype-header {
145
+ padding: 10px;
146
+ }
144
147
  /*
145
148
  body.dark .browser-options-row {
146
149
  border-top: 1px solid rgba(255,255,255,0.05);
@@ -183,6 +186,7 @@ body.dark .browser-options-row {
183
186
  <script src="/filepond-plugin-image-transform.min.js"></script>
184
187
  <script src="/filepond.min.js"></script>
185
188
  <script src="/fscreator.js"></script>
189
+ <script src="/commander.js"></script>
186
190
  </head>
187
191
  <body class='<%=theme%>'>
188
192
  <header class='navheader grabbable'>
@@ -213,6 +217,9 @@ body.dark .browser-options-row {
213
217
  <main>
214
218
  <div id='terminal' class='hidden'></div>
215
219
  <div class='container'>
220
+ <div class='prototype-header'>
221
+ <a class='btn' id='download'><i class='fa-solid fa-download'></i> Download a prototype</a>
222
+ </div>
216
223
  <div class='folder-templates drawer'>
217
224
  <% items.forEach((folder, i) => { %>
218
225
  <a href="/prototypes/<%=folder.id%>" class='line folder' data-id="<%=folder.id%>" data-icon="<%=folder.icon%>" data-name="<%=folder.title%>" data-description="<%=folder.description || ''%>" data-title="<%=folder.title%>" data-index="<%=i%>">
@@ -312,6 +319,20 @@ const renderSearch = () => {
312
319
 
313
320
  renderSearch()
314
321
 
322
+ document.querySelector("#download").addEventListener("click", (e) => {
323
+ Commander({
324
+ callback: "/new",
325
+ path: "~/prototype",
326
+ icon: "fa-solid fa-circle-down",
327
+ title: "Download a Prototype",
328
+ placeholder: "enter a custom prototype git url",
329
+ description: `<div>prototypes are recipes for automatically bootstrapping projects. You can write your own custom prototypes <a target="_blank" href="https://program.pinokio.computer">Learn more</a></div>`,
330
+ message: (url) => {
331
+ let name = new URL(url).pathname.split("/").slice(-2).join("_")
332
+ return encodeURIComponent(`git clone ${url} ${name}`)
333
+ }
334
+ })
335
+ })
315
336
  document.querySelector("form input[type=search]").addEventListener("input", (e) => {
316
337
  selectedIndex = 0
317
338
  selectedBtnIndex = 0
@@ -153,11 +153,21 @@ body.dark .card {
153
153
  <a class='path' href="/"><%-logo%></a>
154
154
  <div class='flexible'></div>
155
155
  <div class='nav-btns'>
156
- <button class='btn2' id='report'><div><i class="fa-solid fa-bug"></i></div><div>Report Bug</div></button>
157
- <a class='btn2' href='https://discord.gg/TQdNwadtE4' target="_blank"><div><i class="fa-brands fa-discord"></i></div><div>Discord</div></a>
158
- <a class='btn2' href='https://twitter.com/cocktailpeanut' target="_blank"><div><i class="fa-brands fa-twitter"></i></div><div>Twitter</div></a>
156
+ <a href="/network" class='btn2'><div><i class="fa-solid fa-wifi"></i></div><div>Network</div></a>
157
+ <a class='btn2' href="https://portal.pinokio.computer/" target="_blank"><div><i class="fa-solid fa-question"></i></div><div>Help</div></a>
158
+ <% if (agent === "electron") { %>
159
+ <a class='btn2' data-filepath="<%=filepath%>"><div><i class="fa-regular fa-folder-open"></i></div><div>Files</div></a>
160
+ <% } %>
161
+ <button class='btn2' id='genlog'><div><i class="fa-solid fa-laptop-code"></i></div><div>Debug</div></button>
162
+ <a id='downloadlogs' download class='hidden btn2' href="/pinokio/logs.zip"><div><i class="fa-solid fa-download"></i></div><div>Download logs</div></a>
159
163
  <a class='btn2' href="/?mode=settings"><div><i class="fa-solid fa-gear"></i></div><div>Settings</div></a>
164
+ <% if (agent === "electron") { %>
165
+ <a href="/" target="_blank" class='btn2' features="browser"><div><i class="fa-solid fa-square-up-right"></i></div><div>Open</div></a>
166
+ <% } else { %>
167
+ <a href="/" target="_blank" class='btn2'><div><i class="fa-solid fa-square-up-right"></i></div><div>Open</div></a>
168
+ <% } %>
160
169
  <button id='new-window' title='open a new window' class='btn2'><i class="fa-solid fa-plus"></i></button>
170
+
161
171
  </div>
162
172
  </h1>
163
173
  </h1>
@@ -157,6 +157,9 @@ document.addEventListener("DOMContentLoaded", async () => {
157
157
  text: `[Success] All steps complete`,
158
158
  })
159
159
  */
160
+ document.querySelector(".play-btn").classList.remove("hidden")
161
+ document.querySelector(".stop-btn").classList.add("hidden")
162
+ document.querySelector(".starting-btn").classList.add("hidden")
160
163
  }
161
164
  stop() {
162
165
  this.socket.run({
@@ -197,21 +200,42 @@ document.addEventListener("DOMContentLoaded", async () => {
197
200
  let query = Object.fromEntries(new URLSearchParams(location.search))
198
201
  console.log(">>>>>>>> 1")
199
202
 
203
+ let firstPacketReceived = false
200
204
  this.socket.run({
201
- method: "kernel.bin.shell_start",
202
- input: true,
205
+ //method: "kernel.bin.shell_start",
206
+ method: "kernel.bin.exec",
207
+ <% if (message) { %>
208
+ message: "<%=message%>",
209
+ <% } %>
210
+ <% if (venv) { %>
211
+ venv: "<%=venv%>",
212
+ <% } %>
213
+ <% if (input) { %>
214
+ input: true,
215
+ <% } %>
216
+ <% if (env) { %>
217
+ env: <%-JSON.stringify(env)%>,
218
+ <% } %>
203
219
  id: "<%=id%>",
204
- // params: {
205
- path: "<%=cwd%>",
206
- // },
220
+ path: "<%=cwd%>",
207
221
  client: {
208
222
  cols: this.term.cols,
209
223
  rows: this.term.rows,
210
224
  }
211
225
  }, async (packet) => {
212
- console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ", { packet })
226
+ if (!firstPacketReceived) {
227
+ refreshParent({
228
+ type: "start"
229
+ })
230
+ firstPacketReceived = true
231
+ }
232
+ console.log({ packet, type: packet.type })
213
233
  if (packet.type === 'start') {
234
+ console.log("refreshParent", packet)
214
235
  refreshParent(packet)
236
+ document.querySelector(".play-btn").classList.add("hidden")
237
+ document.querySelector(".starting-btn").classList.add("hidden")
238
+ document.querySelector(".stop-btn").classList.remove("hidden")
215
239
  if (packet.data && packet.data.description) {
216
240
  if ('current' in packet.data) {
217
241
  document.querySelector("footer").innerHTML = `<b>
@@ -255,12 +279,20 @@ document.addEventListener("DOMContentLoaded", async () => {
255
279
  this.write(JSON.stringify(packet.data.json2, null, 2).replace(/\n/g, "\r\n"))
256
280
  this.write("\r\n")
257
281
  }
282
+ document.querySelector(".play-btn").classList.add("hidden")
283
+ document.querySelector(".starting-btn").classList.add("hidden")
284
+ document.querySelector(".stop-btn").classList.remove("hidden")
258
285
  } else if (packet.type === 'disconnect') {
286
+ console.log("refreshParent", packet)
259
287
  refreshParent(packet)
260
288
  this.term.write("\r\nDisconnected...\r\n")
261
289
  document.querySelector("footer").innerHTML = "<b>Ready</b>"
262
290
  this.socket.close()
291
+ document.querySelector(".play-btn").classList.remove("hidden")
292
+ document.querySelector(".stop-btn").classList.add("hidden")
263
293
  } else if (packet.type === 'connect') {
294
+ console.log("refreshParent", packet)
295
+ refreshParent(packet)
264
296
  if (packet.data) {
265
297
  if (packet.data.shell) {
266
298
  shell_id = packet.data.shell
@@ -269,39 +301,9 @@ document.addEventListener("DOMContentLoaded", async () => {
269
301
  this.write(packet.data.state)
270
302
  }
271
303
  }
272
- /*
273
- for(let i=0; i<message.length; i++) {
274
- await new Promise((r, reject) => {
275
- setTimeout(()=> {
276
- this.term.write(message[i])
277
- r()
278
- }, 20)
279
- })
280
- }
281
- */
282
- console.log(">>>>>>>> 2")
283
- // } else if (packet.type === "key.set") {
284
- // let keys = await fetch("/pinokio/keys").then((res) => {
285
- // return res.json()
286
- // })
287
- // let input = await ModalInput({
288
- // title: "Key",
289
- // description: "select a key",
290
- // form: [{
291
- // key: "key",
292
- // type: "select",
293
- // items: keys,
294
- // }, {
295
- // key: "password",
296
- // type: "password",
297
- // }]
298
- // })
299
- // if (input) {
300
- // this.socket.respond({
301
- // response: input,
302
- // uri: "~" + location.pathname,
303
- // })
304
- // }
304
+ document.querySelector(".play-btn").classList.add("hidden")
305
+ document.querySelector(".starting-btn").classList.add("hidden")
306
+ document.querySelector(".stop-btn").classList.remove("hidden")
305
307
  } else if (packet.type === 'wait.end') {
306
308
  try {
307
309
  Swal.close()
@@ -435,7 +437,7 @@ document.addEventListener("DOMContentLoaded", async () => {
435
437
  n.Noty(payload)
436
438
  }
437
439
  } else if (packet.type === "result") {
438
- refreshParent(packet)
440
+ this.socket.close()
439
441
  } else if (packet.type === "info") {
440
442
  n.Noty({
441
443
  text: `${packet.data}`,
@@ -457,6 +459,10 @@ document.addEventListener("DOMContentLoaded", async () => {
457
459
  // document.querySelector("footer").classList.add("expanded")
458
460
  document.querySelector("footer i").className = "fa-solid fa-triangle-exclamation"
459
461
 
462
+ document.querySelector(".play-btn").classList.remove("hidden")
463
+ document.querySelector(".starting-btn").classList.add("hidden")
464
+ document.querySelector(".stop-btn").classList.add("hidden")
465
+
460
466
  document.querySelector("#error-screen").classList.remove("hidden")
461
467
  document.querySelector("#error-screen pre").textContent = packet.data
462
468
  let instance = new Mark(document.querySelector("#error-screen pre"), {
@@ -477,6 +483,8 @@ document.addEventListener("DOMContentLoaded", async () => {
477
483
 
478
484
  } else if (packet.type === "event") {
479
485
  if (packet.data === "stop") {
486
+ document.querySelector(".stop-btn").classList.add("hidden")
487
+ document.querySelector(".stopped-btn").classList.remove("hidden")
480
488
  document.querySelector("footer").innerHTML = "<b>Ready</b>"
481
489
  setTimeout(() => {
482
490
 
@@ -513,6 +521,9 @@ document.addEventListener("DOMContentLoaded", async () => {
513
521
  type: 'success'
514
522
  })
515
523
  */
524
+ document.querySelector(".play-btn").classList.remove("hidden")
525
+ document.querySelector(".starting-btn").classList.add("hidden")
526
+ document.querySelector(".stop-btn").classList.add("hidden")
516
527
  }, 0)
517
528
  //this.socket.close()
518
529
  }
@@ -534,6 +545,15 @@ document.addEventListener("DOMContentLoaded", async () => {
534
545
  // this.fit.fit()
535
546
  // let pageBottom = document.querySelector("#end")
536
547
  // pageBottom.scrollIntoView()
548
+ }).then(() => {
549
+ refreshParent({
550
+ type: "disconnect"
551
+ })
552
+ document.querySelector(".stop-btn").classList.add("hidden")
553
+ document.querySelector(".stopped-btn").classList.remove("hidden")
554
+ <% if (callback) { %>
555
+ location.href = "<%=callback%>"
556
+ <% } %>
537
557
  })
538
558
  })
539
559
  }
@@ -649,7 +669,7 @@ document.addEventListener("DOMContentLoaded", async () => {
649
669
  }
650
670
  }
651
671
  const rpc = new RPC()
652
- document.querySelector(".stop").addEventListener("click", async (e) => {
672
+ document.querySelector(".stop-btn").addEventListener("click", async (e) => {
653
673
  await rpc.stop()
654
674
  // socket.run({
655
675
  // id: location.pathname,
@@ -657,13 +677,31 @@ document.addEventListener("DOMContentLoaded", async () => {
657
677
  // }, (stream) => {
658
678
  // console.log("#", stream)
659
679
  // })
660
- /*
661
- document.querySelector(".run .play").classList.remove("hidden")
662
- document.querySelector(".run .starting").classList.add("hidden")
663
- */
680
+
681
+
682
+ document.querySelector(".play-btn").classList.remove("hidden")
683
+ document.querySelector(".starting-btn").classList.add("hidden")
664
684
  document.querySelector(".stop-btn").classList.add("hidden")
665
685
  document.querySelector(".stopped-btn").classList.remove("hidden")
666
686
  })
687
+ document.querySelector(".play-btn").addEventListener("click", async (e) => {
688
+ document.querySelector(".play-btn").classList.add("hidden")
689
+ document.querySelector(".starting-btn").classList.remove("hidden")
690
+ await rpc.run()
691
+ })
692
+ <% if (running) { %>
693
+ // if running, connect
694
+ rpc.run("listen")
695
+ // hide play button
696
+ document.querySelector(".play-btn").classList.add("hidden")
697
+ // show stop button
698
+ document.querySelector(".stop-btn").classList.remove("hidden")
699
+ <% } else { %>
700
+ // if not running, start
701
+ rpc.run()
702
+ // show starting button
703
+ document.querySelector(".starting-btn").classList.remove("hidden")
704
+ <% } %>
667
705
 
668
706
  window.addEventListener('message', function(event) {
669
707
  console.log("Message received from the parent: ", event.data); // Message received from parent
@@ -681,7 +719,7 @@ document.addEventListener("DOMContentLoaded", async () => {
681
719
  }
682
720
  }
683
721
  });
684
- rpc.run()
722
+ // rpc.run()
685
723
  })
686
724
 
687
725
  </script>
@@ -689,10 +727,16 @@ document.addEventListener("DOMContentLoaded", async () => {
689
727
  <body class='<%=theme%>'>
690
728
  <header class='navheader grabbable'>
691
729
  <div class='navheader3'>
692
- <div class='btn run stop-btn'>
730
+ <div class='hidden btn run play-btn'>
731
+ <span class='play'><i class="fa-solid fa-play"></i> Start</span>
732
+ </div>
733
+ <div class='hidden btn run starting-btn'>
734
+ <span class='starting'><i class="fa-solid fa-circle-notch fa-spin"></i> Starting...</span>
735
+ </div>
736
+ <div class='hidden btn run stop-btn'>
693
737
  <span class='stop'><i class="fa-solid fa-stop"></i> Stop</span>
694
738
  </div>
695
- <div class='btn stopped-btn hidden'>
739
+ <div class='hidden btn stopped-btn'>
696
740
  <span class='stopped'><i class="fa-solid fa-hand"></i> Stopped</span>
697
741
  </div>
698
742
  </div>