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/api/filepicker/index.js +5 -21
- package/kernel/api/index.js +0 -2
- package/kernel/api/push/index.js +21 -0
- package/kernel/bin/cli.js +31 -0
- package/kernel/bin/git.js +34 -0
- package/kernel/bin/index.js +36 -44
- package/kernel/bin/setup.js +3 -0
- package/kernel/environment.js +0 -3
- package/kernel/git.js +64 -0
- package/kernel/index.js +2 -0
- package/kernel/router/index.js +13 -11
- package/kernel/scripts/git/commit +21 -0
- package/kernel/scripts/git/create +12 -0
- package/kernel/scripts/git/push +22 -0
- package/kernel/shell.js +17 -1
- package/kernel/shells.js +47 -22
- package/kernel/util.js +258 -1
- package/package.json +5 -2
- package/server/index.js +214 -5
- package/server/public/RobotoMono-Regular.ttf +0 -0
- package/server/public/install.js +4 -0
- package/server/public/style.css +11 -1
- package/server/socket.js +89 -2
- package/server/views/app.ejs +46 -9
- package/server/views/file_explorer.ejs +3 -4
- package/server/views/git.ejs +149 -0
- package/server/views/install.ejs +3 -0
- package/server/views/mini.ejs +1542 -0
- package/server/views/partials/dynamic.ejs +10 -9
- package/server/views/prototype/init.ejs +1 -1
- package/server/views/shell.ejs +4 -5
- package/server/views/terminal.ejs +4 -18
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.
|
|
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.
|
|
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}
|
|
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)
|
|
Binary file
|