pinokiod 3.19.2 → 3.19.4

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/kernel/util.js CHANGED
@@ -1,5 +1,7 @@
1
1
  const fs = require('fs')
2
+ const { spawn } = require('child_process')
2
3
  const http = require('http');
4
+ const notifier = require('node-notifier');
3
5
  const os = require('os')
4
6
  const net = require('node:net')
5
7
  const path = require('path')
@@ -19,6 +21,7 @@ const {
19
21
  } = require('glob')
20
22
 
21
23
 
24
+ const platform = os.platform()
22
25
  // asar handling for go-get-folder-size
23
26
  let g
24
27
  if( __dirname.includes(".asar") ) {
@@ -36,6 +39,51 @@ const du = async (folderpath) => {
36
39
  return totalSize;
37
40
  }
38
41
 
42
+ const filepicker = async(req, ondata, kernel) => {
43
+ if (req.params.filetype) {
44
+ /*
45
+ 2 types:
46
+ filetype: [ 'images/*.png,*.jpg', 'docs/*.pdf' ]
47
+ filetype: 'images/*.png,*.jpg', 'docs/*.pdf'
48
+ */
49
+ let filetype
50
+ if (Array.isArray(req.params.filetype)) {
51
+ filetype = req.params.filetype
52
+ } else {
53
+ filetype = [req.params.filetype]
54
+ }
55
+ req.params.filetypes = filetype.map((str) => {
56
+ let chunks = str.split("/")
57
+ let type = chunks[0]
58
+ let extensions = chunks[1].split(",").join(" ")
59
+ return [type, extensions]
60
+ })
61
+ }
62
+ console.log("Filetype", req.params.filetype)
63
+ console.log("Filetypes", req.params.filetypes)
64
+ let response = await new Promise((resolve, reject) => {
65
+ let picker_path = kernel.path("bin/py/picker.py")
66
+ const proc = spawn("python", [picker_path])
67
+
68
+ let output = "";
69
+ proc.stdout.on("data", (chunk) => output += chunk);
70
+ proc.stderr.on("data", (err) => console.error("Python error:", err.toString()));
71
+ proc.on("close", () => {
72
+ try {
73
+ const result = JSON.parse(output);
74
+ if (result.error) return reject(result.error);
75
+ resolve(result.paths); // Always an array
76
+ } catch (e) {
77
+ reject("Failed to parse Python output: " + output);
78
+ }
79
+ });
80
+
81
+ proc.stdin.write(JSON.stringify(req.params));
82
+ proc.stdin.end();
83
+ });
84
+ return { paths: response }
85
+ }
86
+
39
87
  const is_port_available = async (port) => {
40
88
  return new Promise((resolve) => {
41
89
  const server = http.createServer();
@@ -91,6 +139,33 @@ const parse_env = async (filename) => {
91
139
  return {}
92
140
  }
93
141
  }
142
+
143
+ const exists= (abspath) => {
144
+ return new Promise(r=>fs.access(abspath, fs.constants.F_OK, e => r(!e)))
145
+ }
146
+ const log = async (filepath, str, session) => {
147
+ try {
148
+ await fs.promises.mkdir(filepath, { recursive: true })
149
+ } catch (e) {
150
+ }
151
+
152
+ let output = '';
153
+ for (let line of str.split('\n')) {
154
+ line = line.split('\r').pop(); // handle overwriting lines
155
+ output += line + '\n';
156
+ }
157
+
158
+ const pattern = [
159
+ '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
160
+ '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-Za-z=><~]))'
161
+ ].join('|');
162
+ const regex = new RegExp(pattern, 'gi')
163
+ let stripped = str.replaceAll(regex, '');
164
+ let logpath = path.resolve(filepath, session)
165
+ await fs.promises.writeFile(logpath, stripped)
166
+ let latest_logpath = path.resolve(filepath, "latest")
167
+ await fs.promises.writeFile(latest_logpath, stripped)
168
+ }
94
169
  const run = (cmd, cwd, kernel) => {
95
170
  // console.log("Util.run", { cmd, cwd })
96
171
  // child_process.exec(cmd, { cwd })
@@ -373,7 +448,189 @@ function fill_object(obj, pattern, list, cache) {
373
448
  let res = recurse(obj);
374
449
  return { result: res, replaced_map }
375
450
  }
451
+ function push(params) {
452
+ /*
453
+ params :- {
454
+ title: <string>,
455
+ subtitle: <string>,
456
+ message: <string>,
457
+ image: <image path>,
458
+ sound: true|false,
459
+ }
460
+ */
461
+ if (params.image && !params.contentImage) {
462
+ params.contentImage = params.image
463
+ }
464
+ notifier.notify(params)
465
+ }
466
+ function p2u(localPath) {
467
+ /*
468
+ unix-like: /users/b/c => users/b/c
469
+ windows: C:\\\users\b\c => c/users/b/c
470
+ */
471
+ if (platform === 'win32') {
472
+ const match = localPath.match(/^([a-zA-Z]):\\(.*)/);
473
+ const drive = match[1].toLowerCase();
474
+ const path = match[2].replace(/\\/g, '/');
475
+ return `/${drive}/${path}`;
476
+ } else {
477
+ return `${localPath}`;
478
+ }
479
+ }
480
+ function u2p(urlPath) {
481
+ /*
482
+ unix-like: users/b/c => /users/b/c/
483
+ windows: c/users/b/c => C:\\users\b\c
484
+ */
485
+ const parts = urlPath.split('/');
486
+ if (platform === 'win32') {
487
+ const drive = parts[0];
488
+ return `${drive.toUpperCase()}:\\${parts.slice(1).join("\\")}`
489
+ } else {
490
+ return `/${parts.join("/")}`
491
+ }
492
+ }
493
+
494
+ function classifyChange(head, workdir, stage) {
495
+ const key = `${head}${workdir}${stage}`;
496
+
497
+ // HEAD | WORKDIR | STAGE
498
+ switch (key) {
499
+ case '000':
500
+ return 'unmodified'; // shouldn't show up
501
+ case '001':
502
+ return 'added (staged)';
503
+ case '002':
504
+ return 'added (unstaged)';
505
+ case '003':
506
+ return 'added (staged + modified)';
507
+ case '010':
508
+ case '020':
509
+ return 'untracked';
510
+ case '100':
511
+ return 'deleted (unstaged)';
512
+ case '101':
513
+ return 'deleted (staged)';
514
+ case '102':
515
+ return 'deleted (staged, but modified)';
516
+ case '110':
517
+ return 'modified (unstaged)';
518
+ case '111':
519
+ return 'modified (staged)';
520
+ case '112':
521
+ return 'modified (staged + unstaged)';
522
+ case '120':
523
+ return 'type changed (unstaged)';
524
+ case '121':
525
+ return 'type changed (staged)';
526
+ default:
527
+ return `unknown (${key})`;
528
+ }
529
+
530
+ }
531
+
532
+
533
+ function diffLinesWithContext(diffs, context = 3) {
534
+ const result = [];
535
+ let lineOld = 1;
536
+ let lineNew = 1;
537
+
538
+ const blocks = [];
539
+ let i = 0;
540
+
541
+ while (i < diffs.length) {
542
+ if (!diffs[i].added && !diffs[i].removed) {
543
+ // Flatten unchanged lines
544
+ const lines = [];
545
+ while (i < diffs.length && !diffs[i].added && !diffs[i].removed) {
546
+ const split = diffs[i].value.split('\n');
547
+ if (split[split.length - 1] === '') split.pop();
548
+ for (const line of split) {
549
+ lines.push({
550
+ line,
551
+ lineOld,
552
+ lineNew,
553
+ });
554
+ lineOld++;
555
+ lineNew++;
556
+ }
557
+ i++;
558
+ }
559
+ blocks.push({ type: 'context', lines });
560
+ } else {
561
+ // Change block (added or removed)
562
+ const change = [];
563
+ while (i < diffs.length && (diffs[i].added || diffs[i].removed)) {
564
+ const split = diffs[i].value.split('\n');
565
+ if (split[split.length - 1] === '') split.pop();
566
+ const type = diffs[i].added ? 'add' : 'del';
567
+
568
+ for (const line of split) {
569
+ change.push({
570
+ line,
571
+ lineOld: type === 'add' ? '' : lineOld,
572
+ lineNew: type === 'del' ? '' : lineNew,
573
+ type,
574
+ });
575
+ if (type !== 'add') lineOld++;
576
+ if (type !== 'del') lineNew++;
577
+ }
578
+ i++;
579
+ }
580
+ blocks.push({ type: 'change', lines: change });
581
+ }
582
+ }
583
+
584
+ // Now emit with context
585
+ const summarized = [];
586
+
587
+ for (let j = 0; j < blocks.length; j++) {
588
+ const block = blocks[j];
589
+
590
+ if (block.type === 'change') {
591
+ // Add leading context
592
+ const leading = (j > 0 && blocks[j - 1].type === 'context')
593
+ ? blocks[j - 1].lines.slice(-context)
594
+ : [];
376
595
 
596
+ const trailing = (j < blocks.length - 1 && blocks[j + 1].type === 'context')
597
+ ? blocks[j + 1].lines.slice(0, context)
598
+ : [];
599
+
600
+ // Push leading context
601
+ for (const l of leading) {
602
+ summarized.push({
603
+ line: l.line,
604
+ lineOld: l.lineOld,
605
+ lineNew: l.lineNew,
606
+ type: 'context',
607
+ });
608
+ }
609
+
610
+ // Push change lines
611
+ for (const l of block.lines) {
612
+ summarized.push(l);
613
+ }
614
+
615
+ // Push trailing context
616
+ for (const l of trailing) {
617
+ summarized.push({
618
+ line: l.line,
619
+ lineOld: l.lineOld,
620
+ lineNew: l.lineNew,
621
+ type: 'context',
622
+ });
623
+ }
624
+
625
+ // Remove consumed context from next context block
626
+ if (j < blocks.length - 1 && blocks[j + 1].type === 'context') {
627
+ blocks[j + 1].lines = blocks[j + 1].lines.slice(context);
628
+ }
629
+ }
630
+ }
631
+
632
+ return summarized;
633
+ }
377
634
  module.exports = {
378
- parse_env, log_path, api_path, update_env, parse_env_detail, openfs, port_running, du, is_port_available, find_python, find_venv, fill_object, run, openURL
635
+ parse_env, log_path, api_path, update_env, parse_env_detail, openfs, port_running, du, is_port_available, find_python, find_venv, fill_object, run, openURL, u2p, p2u, log, diffLinesWithContext, classifyChange, push, filepicker
379
636
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "3.19.2",
3
+ "version": "3.19.4",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -21,6 +21,7 @@
21
21
  "cross-fetch": "^3.1.5",
22
22
  "csv-parse": "^5.3.10",
23
23
  "decompress": "^4.2.1",
24
+ "diff": "^8.0.2",
24
25
  "dotenv": "^16.4.5",
25
26
  "ejs": "^3.1.9",
26
27
  "eol": "^0.9.1",
@@ -39,6 +40,7 @@
39
40
  "http-terminator": "^3.2.0",
40
41
  "https": "^1.0.0",
41
42
  "ini": "^5.0.0",
43
+ "isbinaryfile": "^5.0.4",
42
44
  "isomorphic-git": "^1.23.0",
43
45
  "jimini": "^0.0.8",
44
46
  "jsdom": "^24.0.0",
@@ -51,8 +53,9 @@
51
53
  "multer": "^1.4.5-lts.1",
52
54
  "node-downloader-helper": "^2.1.9",
53
55
  "node-gradio-client": "^0.14.6",
56
+ "node-notifier": "^10.0.1",
54
57
  "p-limit": "^3.1.0",
55
- "pdrive": "^0.0.26",
58
+ "pdrive": "^0.0.27",
56
59
  "portfinder-cp": "^1.0.34",
57
60
  "proxy-agent": "^6.5.0",
58
61
  "qrcode": "^1.5.3",
package/server/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  const express = require('express');
2
+ const diff = require('diff')
2
3
  const kill = require('kill-sync')
4
+ const { isBinaryFile } = require("isbinaryfile");
5
+ const { glob, sync, hasMagic } = require('glob-gitignore')
3
6
  const portfinder = require('portfinder-cp');
4
7
  const proxy = require('express-http-proxy-cp');
5
8
  const sudo = require("sudo-prompt-programfiles-x86");
@@ -50,6 +53,11 @@ const Info = require("../kernel/info")
50
53
 
51
54
  const Setup = require("../kernel/bin/setup")
52
55
 
56
+ function normalize(str) {
57
+ if (!str) return '';
58
+ return (str.endsWith('\n') ? str : str + '\n').replace(/\r\n/g, '\n');
59
+ }
60
+
53
61
  class Server {
54
62
  constructor(config) {
55
63
  this.tabs = {}
@@ -358,7 +366,10 @@ class Server {
358
366
  }
359
367
 
360
368
  async chrome(req, res, type) {
369
+ let d = "Request " + Date.now()
370
+ console.time(d)
361
371
 
372
+ console.time("bin check " + d)
362
373
  let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
363
374
  bin: this.kernel.bin.preset("dev"),
364
375
  })
@@ -366,6 +377,7 @@ class Server {
366
377
  res.redirect(`/setup/dev?callback=${req.originalUrl}`)
367
378
  return
368
379
  }
380
+ console.timeEnd("bin check " + d)
369
381
 
370
382
  let name = req.params.name
371
383
  let config = await this.kernel.api.meta(name)
@@ -514,7 +526,11 @@ class Server {
514
526
  feed = this.newsfeed(gitRemote)
515
527
  }
516
528
 
529
+ // git
517
530
 
531
+ let c = this.kernel.path("api", name)
532
+
533
+ let repos = await this.kernel.git.repos(c)
518
534
  await this.kernel.plugin.init()
519
535
  let plugin = await this.getPlugin(name)
520
536
  let plugin_menu = null
@@ -527,6 +543,7 @@ class Server {
527
543
  let current_urls = await this.current_urls(req.originalUrl.slice(1))
528
544
 
529
545
  const result = {
546
+ repos,
530
547
  current_urls,
531
548
  path: this.kernel.path("api", name),
532
549
  plugin_menu,
@@ -562,6 +579,7 @@ class Server {
562
579
  if (!this.kernel.proto.config) {
563
580
  await this.kernel.proto.init()
564
581
  }
582
+ console.timeEnd(d)
565
583
  res.render("app", result)
566
584
  }
567
585
  getVariationUrls(req) {
@@ -585,7 +603,7 @@ class Server {
585
603
  if (rendered.id) {
586
604
  shell_id = encodeURIComponent(`${name}_${rendered.id}`)
587
605
  } else {
588
- shell_id = encodeURIComponent(`${name}_${i}_${hash}`)
606
+ shell_id = encodeURIComponent(`${name}_${i}_session_${hash}`)
589
607
  }
590
608
  return shell_id
591
609
  }
@@ -947,7 +965,6 @@ class Server {
947
965
  bin: this.kernel.bin.preset("ai"),
948
966
  script: resolved
949
967
  })
950
- console.log({ requirements })
951
968
 
952
969
  //let requirements = this.kernel.bin.requirements(resolved)
953
970
  //let requirements_pending = !this.kernel.bin.installed_initialized
@@ -988,7 +1005,6 @@ class Server {
988
1005
 
989
1006
 
990
1007
  let env_requirements = await Environment.requirements(resolved, filepath, this.kernel)
991
- console.log({ env_requirements })
992
1008
  if (env_requirements.requires_instantiation) {
993
1009
  let p = Util.api_path(filepath, this.kernel)
994
1010
  let platform = os.platform()
@@ -1776,7 +1792,6 @@ class Server {
1776
1792
  }
1777
1793
  let shell_id = this.get_shell_id(name, currentIndexPath, rendered)
1778
1794
 
1779
-
1780
1795
  // let hash = crypto.createHash('md5').update(JSON.stringify(rendered)).digest('hex')
1781
1796
  // let shell_id
1782
1797
  // if (rendered.id) {
@@ -1789,26 +1804,36 @@ class Server {
1789
1804
  let shell = this.kernel.shell.get(shell_id)
1790
1805
  if (shell) {
1791
1806
  menuitem.running = true
1807
+ } else {
1808
+ let decoded_shell_id = decodeURIComponent(shell_id)
1809
+ let shell = this.kernel.shell.get(decoded_shell_id)
1810
+ if (shell) {
1811
+ menuitem.running = true
1812
+ }
1792
1813
  }
1793
1814
  }
1794
1815
 
1795
1816
  if (menuitem.href) {
1796
1817
  let u
1818
+ let cwd
1797
1819
  if (menuitem.href.startsWith("http")) {
1798
1820
  menuitem.src = menuitem.href
1799
1821
  } else if (menuitem.href.startsWith("/")) {
1800
1822
  let run_path = "/run"
1801
1823
  if (menuitem.href.startsWith(run_path)) {
1802
1824
  u = new URL("http://localhost" + menuitem.href.slice(run_path.length))
1825
+ cwd = u.searchParams.get("cwd")
1803
1826
  u.search = ""
1804
1827
  menuitem.src = u.pathname
1805
1828
  } else {
1806
1829
  u = new URL("http://localhost" + menuitem.href)
1830
+ cwd = u.searchParams.get("cwd")
1807
1831
  u.search = ""
1808
1832
  menuitem.src = u.pathname
1809
1833
  }
1810
1834
  } else {
1811
1835
  u = new URL("http://localhost/" + menuitem.href)
1836
+ cwd = u.searchParams.get("cwd")
1812
1837
  u.search = ""
1813
1838
  menuitem.src = u.pathname
1814
1839
  }
@@ -1829,7 +1854,6 @@ class Server {
1829
1854
  menuitem.running = true
1830
1855
  }
1831
1856
  }
1832
-
1833
1857
 
1834
1858
  }
1835
1859
 
@@ -2407,6 +2431,42 @@ class Server {
2407
2431
  return true
2408
2432
  }
2409
2433
  }
2434
+ async getPluginGlobal(filepath) {
2435
+ if (this.kernel.plugin.config) {
2436
+ try {
2437
+ let info = new Info(this.kernel)
2438
+ info.cwd = () => {
2439
+ return filepath
2440
+ }
2441
+ let menu = await this.kernel.plugin.config.menu(this.kernel, info)
2442
+ let plugin = { menu }
2443
+ let uri = filepath
2444
+ await this.renderMenu(uri, filepath, plugin, [])
2445
+
2446
+ function setOnlineIfRunning(obj) {
2447
+ if (Array.isArray(obj)) {
2448
+ for (const item of obj) setOnlineIfRunning(item);
2449
+ } else if (obj && typeof obj === 'object') {
2450
+ if (obj.running === true) obj.online = true;
2451
+ for (const key in obj) setOnlineIfRunning(obj[key]);
2452
+ }
2453
+ }
2454
+
2455
+ setOnlineIfRunning(plugin)
2456
+
2457
+ return plugin
2458
+ } catch (e) {
2459
+ console.log("getPlugin ERROR", e)
2460
+ return {
2461
+ menu: []
2462
+ }
2463
+ }
2464
+ } else {
2465
+ return {
2466
+ menu: []
2467
+ }
2468
+ }
2469
+ }
2410
2470
  async getPlugin(name) {
2411
2471
  console.log("getPlugin", name)
2412
2472
  if (this.kernel.plugin.config) {
@@ -3098,6 +3158,14 @@ class Server {
3098
3158
  res.json({ error: e.message })
3099
3159
  }
3100
3160
  }))
3161
+ this.app.post("/push", ex(async (req, res) => {
3162
+ try {
3163
+ Util.push(req.body)
3164
+ res.json({ success: true })
3165
+ } catch (e) {
3166
+ res.json({ error: e.stack })
3167
+ }
3168
+ }))
3101
3169
  this.app.post("/runcmd", ex(async (req, res) => {
3102
3170
  //Util.openfs(req.body.path, req.body.mode)
3103
3171
  let cwd = req.body.cwd
@@ -4304,6 +4372,129 @@ class Server {
4304
4372
  res.json(this.startScripts)
4305
4373
  }
4306
4374
  }))
4375
+ this.app.get("/git/*", ex(async (req, res) => {
4376
+ let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
4377
+ bin: this.kernel.bin.preset("dev"),
4378
+ })
4379
+ if (!requirements_pending && install_required) {
4380
+ res.redirect(`/setup/dev?callback=${req.originalUrl}`)
4381
+ return
4382
+ }
4383
+ let dir = this.kernel.path("api", req.params[0])
4384
+ let config = await this.kernel.git.config(dir)
4385
+
4386
+ let remote = null
4387
+ if (config["remote \"origin\""]) {
4388
+ remote = config["remote \"origin\""].url
4389
+ }
4390
+ let changes = []
4391
+ try {
4392
+ const statusMatrix = await git.statusMatrix({ dir, fs });
4393
+ for (const [filepath, head, workdir, stage] of statusMatrix) {
4394
+ if (head !== workdir || head !== stage) {
4395
+ const fullPath = path.join(dir, filepath);
4396
+ let relpath = path.relative(this.kernel.homedir, fullPath)
4397
+ let webpath = "/asset/" + relpath
4398
+
4399
+ // Skip if binary
4400
+ let binary = false;
4401
+ try {
4402
+ binary = await isBinaryFile(fullPath);
4403
+ } catch {
4404
+ binary = false; // fallback
4405
+ }
4406
+
4407
+ if (binary) {
4408
+ changes.push({
4409
+ webpath,
4410
+ file: filepath,
4411
+ //status: `${head}${workdir}${stage}`,
4412
+ status: Util.classifyChange(head, workdir, stage),
4413
+ binary: true,
4414
+ diff: null,
4415
+ });
4416
+ continue;
4417
+
4418
+ } else {
4419
+
4420
+ let oldContent = "";
4421
+ let newContent = "";
4422
+
4423
+ // HEAD version (from Git blob)
4424
+ try {
4425
+ const commitOid = await git.resolveRef({ fs, dir, ref: "HEAD" });
4426
+ const { blob } = await git.readBlob({ fs, dir, oid: commitOid, filepath });
4427
+ oldContent = Buffer.from(blob).toString("utf8");
4428
+ } catch (e) {
4429
+ oldContent = "";
4430
+ }
4431
+
4432
+ // Working directory version
4433
+ try {
4434
+ newContent = await fs.promises.readFile(fullPath, "utf8");
4435
+ } catch (e) {
4436
+ newContent = "";
4437
+ }
4438
+
4439
+
4440
+ const diffs = diff.diffLines(normalize(oldContent), normalize(newContent));
4441
+
4442
+ const summarized = Util.diffLinesWithContext(diffs, 5);
4443
+ changes.push({ webpath, file: filepath, status: Util.classifyChange(head, workdir, stage)/*`${head}${workdir}${stage}`*/, diff: summarized, binary: false, });
4444
+ }
4445
+ }
4446
+ }
4447
+
4448
+ } catch (err) {
4449
+ console.log("git status matrix error", err)
4450
+ }
4451
+
4452
+ res.render("git", {
4453
+ changes,
4454
+ dir,
4455
+ config,
4456
+ remote,
4457
+ theme: this.theme,
4458
+ platform: this.kernel.platform,
4459
+ agent: this.agent,
4460
+ })
4461
+ }))
4462
+ this.app.get("/dev/*", ex(async (req, res) => {
4463
+ let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
4464
+ bin: this.kernel.bin.preset("dev"),
4465
+ })
4466
+ if (!requirements_pending && install_required) {
4467
+ res.redirect(`/setup/dev?callback=${req.originalUrl}`)
4468
+ return
4469
+ }
4470
+ let platform = os.platform()
4471
+ await this.kernel.plugin.init()
4472
+ let filepath = Util.u2p(req.params[0])
4473
+ let plugin = await this.getPluginGlobal(filepath)
4474
+ let current_urls = await this.current_urls(req.originalUrl.slice(1))
4475
+ let plugin_menu
4476
+ try {
4477
+ plugin_menu = plugin.menu[0].menu
4478
+ } catch (e) {
4479
+ plugin_menu = []
4480
+ }
4481
+ const result = {
4482
+ current_urls,
4483
+ plugin_menu,
4484
+ portal: this.portal,
4485
+ install: this.install,
4486
+ port: this.port,
4487
+ platform,
4488
+ running:this.kernel.api.running,
4489
+ memory: this.kernel.memory,
4490
+ dynamic: "/pinokio/dynamic_global/" + req.params[0],
4491
+ dynamic_content: null,
4492
+ home: req.originalUrl,
4493
+ theme: this.theme,
4494
+ agent: this.agent,
4495
+ }
4496
+ res.render("mini", result)
4497
+ }))
4307
4498
  this.app.get("/asset/*", ex((req, res) => {
4308
4499
  let pathComponents = req.params[0].split("/")
4309
4500
  let filepath = this.kernel.path(...pathComponents)
@@ -4384,6 +4575,24 @@ class Server {
4384
4575
  }
4385
4576
  }
4386
4577
  }))
4578
+ this.app.get("/pinokio/dynamic_global/*", ex(async (req, res) => {
4579
+ let plugin = await this.getPluginGlobal("/" + req.params[0])
4580
+ let html = ""
4581
+ if (plugin && plugin.menu) {
4582
+ let plugin_menu
4583
+ try {
4584
+ plugin_menu = plugin.menu[0].menu
4585
+ } catch (e) {
4586
+ plugin_menu = []
4587
+ }
4588
+ html = await new Promise((resolve, reject) => {
4589
+ ejs.renderFile(path.resolve(__dirname, "views/partials/dynamic.ejs"), { dynamic: plugin_menu }, (err, html) => {
4590
+ resolve(html)
4591
+ })
4592
+ })
4593
+ }
4594
+ res.send(html)
4595
+ }))
4387
4596
  this.app.get("/pinokio/dynamic/:name", ex(async (req, res) => {
4388
4597
  // await this.kernel.plugin.init()
4389
4598
  let plugin = await this.getPlugin(req.params.name)
@@ -62,6 +62,10 @@ const install = async (name, url, term, socket, options) => {
62
62
  }
63
63
  socket.run({
64
64
  method: "shell.run",
65
+ client: {
66
+ cols: term.cols,
67
+ rows: term.rows,
68
+ },
65
69
  params: {
66
70
  message: cmd,
67
71
  path: "~/api"