pinokiod 3.9.0 → 3.9.2
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 +59 -0
- package/kernel/api/fs/index.js +6 -4
- package/kernel/api/index.js +123 -37
- package/kernel/api/input/index.js +2 -0
- package/kernel/api/web/index.js +6 -0
- package/kernel/bin/caddy.js +127 -0
- package/kernel/bin/cloudflared.js +1 -0
- package/kernel/bin/conda.js +10 -2
- package/kernel/bin/cuda.js +0 -16
- package/kernel/bin/ffmpeg.js +4 -6
- package/kernel/bin/git.js +55 -29
- package/kernel/bin/gxx.js +8 -6
- package/kernel/bin/huggingface.js +4 -5
- package/kernel/bin/index.js +347 -88
- package/kernel/bin/node.js +13 -6
- package/kernel/bin/py.js +1 -1
- package/kernel/bin/setup.js +146 -0
- package/kernel/bin/uv.js +4 -5
- package/kernel/bin/zip.js +7 -8
- package/kernel/connect/index.js +25 -0
- package/kernel/connect/providers/github/README.md +11 -0
- package/kernel/connect/providers/x/README.md +96 -0
- package/kernel/connect/providers/x/index.js +152 -0
- package/kernel/environment.js +50 -1
- package/kernel/index.js +152 -32
- package/kernel/info.js +4 -2
- package/kernel/kv.js +38 -0
- package/kernel/loader.js +13 -3
- package/kernel/peer.js +161 -0
- package/kernel/procs.js +116 -0
- package/kernel/prototype.js +62 -0
- package/kernel/router/common.js +42 -0
- package/kernel/router/connector.js +81 -0
- package/kernel/router/index.js +235 -0
- package/kernel/router/localhost_home_router.js +84 -0
- package/kernel/router/localhost_port_router.js +15 -0
- package/kernel/router/localhost_variable_router.js +37 -0
- package/kernel/router/peer_home_router.js +15 -0
- package/kernel/router/peer_peer_router.js +26 -0
- package/kernel/router/peer_port_router.js +45 -0
- package/kernel/router/peer_variable_router.js +46 -0
- package/kernel/router/processor.js +31 -0
- package/kernel/shell.js +141 -60
- package/kernel/shells.js +32 -17
- package/kernel/store.js +58 -0
- package/kernel/sysinfo.js +0 -1
- package/kernel/util.js +143 -36
- package/package.json +4 -1
- package/pipe/index.js +0 -2
- package/script/index.js +12 -0
- package/server/index.js +1473 -553
- package/server/public/Socket.js +5 -1
- package/server/public/SourceCodePro-Regular.ttf +0 -0
- package/server/public/SpaceMono-Bold.ttf +0 -0
- package/server/public/SpaceMono-BoldItalic.ttf +0 -0
- package/server/public/SpaceMono-Italic.ttf +0 -0
- package/server/public/SpaceMono-Regular.ttf +0 -0
- package/server/public/commander.js +40 -0
- package/server/public/common.js +60 -0
- package/server/public/dropzone-min.js +2 -0
- package/server/public/dropzone.css +1 -0
- package/server/public/filepond-plugin-file-validate-type.min.js +9 -0
- package/server/public/filepond-plugin-image-crop.min.js +9 -0
- package/server/public/filepond-plugin-image-edit.min.css +8 -0
- package/server/public/filepond-plugin-image-edit.min.js +9 -0
- package/server/public/filepond-plugin-image-exif-orientation.min.js +9 -0
- package/server/public/filepond-plugin-image-preview.min.css +8 -0
- package/server/public/filepond-plugin-image-preview.min.js +9 -0
- package/server/public/filepond-plugin-image-resize.min.js +9 -0
- package/server/public/filepond-plugin-image-transform.min.js +9 -0
- package/server/public/filepond.min.css +8 -0
- package/server/public/filepond.min.js +9 -0
- package/server/public/fscreator.js +174 -0
- package/server/public/fseditor.js +212 -0
- package/server/public/github.png +0 -0
- package/server/public/highlight-js.js +81 -0
- package/server/public/highlight.css +9 -0
- package/server/public/highlight.js +1244 -0
- package/server/public/install.js +17 -20
- package/server/public/modalinput.js +105 -9
- package/server/public/opener.js +3 -1
- package/server/public/pinokio-black.png +0 -0
- package/server/public/pinokio-black2.png +0 -0
- package/server/public/redoc.standalone.js +1832 -0
- package/server/public/style.css +211 -17
- package/server/public/swagger-ui-bundle.js +2 -0
- package/server/public/swagger-ui.css +3 -0
- package/server/public/x.js +63 -0
- package/server/socket.js +136 -39
- package/server/views/404.ejs +0 -1
- package/server/views/app.ejs +356 -90
- package/server/views/bootstrap.ejs +22 -19
- package/server/views/connect/x.ejs +324 -0
- package/server/views/connect.ejs +197 -0
- package/server/views/download.ejs +7 -1
- package/server/views/editor.ejs +68 -9
- package/server/views/explore.ejs +13 -10
- package/server/views/file_explorer.ejs +10 -37
- package/server/views/github.ejs +218 -0
- package/server/views/help.ejs +4 -3
- package/server/views/index.ejs +268 -118
- package/server/views/install.ejs +56 -29
- package/server/views/keys.ejs +276 -0
- package/server/views/network.ejs +889 -0
- package/server/views/partials/dynamic.ejs +9 -0
- package/server/views/partials/menu.ejs +7 -1
- package/server/views/prototype/index.ejs +311 -0
- package/server/views/prototype/show.ejs +383 -0
- package/server/views/redoc.ejs +18 -0
- package/server/views/required_env_editor.ejs +11 -2
- package/server/views/settings.ejs +38 -10
- package/server/views/setup.ejs +254 -0
- package/server/views/setup_home.ejs +192 -0
- package/server/views/shell.ejs +781 -0
- package/server/views/swaggerui.ejs +19 -0
- package/server/views/terminal.ejs +174 -42
- package/LICENSE +0 -7
- package/server/views/proxy.ejs +0 -405
package/server/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
|
+
const kill = require('kill-sync')
|
|
2
3
|
const portfinder = require('portfinder-cp');
|
|
3
4
|
const proxy = require('express-http-proxy-cp');
|
|
4
5
|
const sudo = require("sudo-prompt-programfiles-x86");
|
|
@@ -13,12 +14,12 @@ const cors = require('cors');
|
|
|
13
14
|
const path = require("path")
|
|
14
15
|
const fs = require('fs');
|
|
15
16
|
const os = require('os')
|
|
16
|
-
const { fork } = require('child_process');
|
|
17
|
+
const { fork, exec } = require('child_process');
|
|
17
18
|
const semver = require('semver')
|
|
18
19
|
const fse = require('fs-extra')
|
|
19
20
|
const QRCode = require('qrcode')
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
const axios = require('axios')
|
|
22
|
+
const crypto = require('crypto')
|
|
22
23
|
|
|
23
24
|
const git = require('isomorphic-git')
|
|
24
25
|
const http = require('isomorphic-git/http/node')
|
|
@@ -29,6 +30,8 @@ const multer = require('multer');
|
|
|
29
30
|
|
|
30
31
|
const ejs = require('ejs');
|
|
31
32
|
|
|
33
|
+
const DEFAULT_PORT = 42000
|
|
34
|
+
|
|
32
35
|
const ex = fn => (req, res, next) => {
|
|
33
36
|
Promise.resolve(fn(req, res, next)).catch(next);
|
|
34
37
|
};
|
|
@@ -42,11 +45,15 @@ const packagejson = require("../package.json")
|
|
|
42
45
|
const Environment = require("../kernel/environment")
|
|
43
46
|
const Cloudflare = require("../kernel/api/cloudflare")
|
|
44
47
|
const Util = require("../kernel/util")
|
|
48
|
+
|
|
49
|
+
const Setup = require("../kernel/bin/setup")
|
|
50
|
+
|
|
45
51
|
class Server {
|
|
46
52
|
constructor(config) {
|
|
47
53
|
this.tabs = {}
|
|
48
54
|
this.agent = config.agent
|
|
49
|
-
this.port =
|
|
55
|
+
this.port = DEFAULT_PORT
|
|
56
|
+
// this.port = config.port
|
|
50
57
|
this.kernel = new Kernel(config.store)
|
|
51
58
|
// this.tunnels = {}
|
|
52
59
|
this.version = {
|
|
@@ -93,29 +100,48 @@ class Server {
|
|
|
93
100
|
exists (s) {
|
|
94
101
|
return new Promise(r=>fs.access(s, fs.constants.F_OK, e => r(!e)))
|
|
95
102
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
103
|
+
async updateMeta(formData, app_path) {
|
|
104
|
+
console.log("updateMeta", app_path)
|
|
105
|
+
if (formData.icon_dirty) {
|
|
106
|
+
// write icon file
|
|
107
|
+
let icon_path = this.kernel.path("api", formData.new_path, formData.icon_path)
|
|
108
|
+
console.log({ icon_path })
|
|
109
|
+
await fs.promises.writeFile(icon_path, formData.avatar)
|
|
101
110
|
}
|
|
102
|
-
return buildNumber;
|
|
103
|
-
}
|
|
104
|
-
compatible() {
|
|
105
|
-
if (this.kernel.platform === "win32") {
|
|
106
|
-
let buildNumber = this.winBuildNumber()
|
|
107
|
-
if (buildNumber < 18309) {
|
|
108
|
-
// must use conpty for node-pty, and conpty is only supported in win>=18309
|
|
109
|
-
console.log("Windows buildNumber", buildNumber)
|
|
110
|
-
throw new Error(`Pinokio supports Windows release 18309 and up (current system: ${buildNumber}`)
|
|
111
|
-
}
|
|
112
111
|
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
// write title/description to pinokio.json
|
|
113
|
+
let dirty
|
|
114
|
+
let meta_path = this.kernel.path("api", app_path, "pinokio.json")
|
|
115
|
+
console.log("meta_path", meta_path)
|
|
116
|
+
let meta = (await this.kernel.loader.load(meta_path)).resolved
|
|
117
|
+
console.log("meta", meta)
|
|
118
|
+
if (!meta) meta = {}
|
|
119
|
+
if (formData.title) {
|
|
120
|
+
meta.title = formData.title
|
|
121
|
+
dirty = true
|
|
118
122
|
}
|
|
123
|
+
if (formData.description) {
|
|
124
|
+
meta.description = formData.description
|
|
125
|
+
dirty = true
|
|
126
|
+
}
|
|
127
|
+
if (dirty) {
|
|
128
|
+
await fs.promises.writeFile(meta_path, JSON.stringify(meta, null, 2))
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async createMeta(formData) {
|
|
132
|
+
let _path = this.kernel.path("api", formData.path)
|
|
133
|
+
await fs.promises.mkdir(_path, { recursive: true }).catch((e) => {})
|
|
134
|
+
let icon_path = this.kernel.path("api", formData.path, "icon.png")
|
|
135
|
+
await fs.promises.writeFile(icon_path, formData.avatar)
|
|
136
|
+
|
|
137
|
+
// write title/description to pinokio.json
|
|
138
|
+
let meta_path = this.kernel.path("api", formData.path, "pinokio.json")
|
|
139
|
+
let meta = {
|
|
140
|
+
title: formData.title,
|
|
141
|
+
description: formData.description,
|
|
142
|
+
icon: "icon.png"
|
|
143
|
+
}
|
|
144
|
+
await fs.promises.writeFile(meta_path, JSON.stringify(meta, null, 2))
|
|
119
145
|
}
|
|
120
146
|
getMemory(filepath) {
|
|
121
147
|
let localMem = this.kernel.memory.local[filepath]
|
|
@@ -152,8 +178,10 @@ class Server {
|
|
|
152
178
|
return items.map((x) => {
|
|
153
179
|
let name
|
|
154
180
|
let description
|
|
155
|
-
let icon
|
|
181
|
+
let icon = "/pinokio-black.png"
|
|
156
182
|
let uri
|
|
183
|
+
let iconpath
|
|
184
|
+
let apipath
|
|
157
185
|
if (meta) {
|
|
158
186
|
let m = meta[x.name]
|
|
159
187
|
name = (m && m.title ? m.title : x.name)
|
|
@@ -161,7 +189,14 @@ class Server {
|
|
|
161
189
|
if (m && m.icon) {
|
|
162
190
|
icon = m.icon
|
|
163
191
|
} else {
|
|
164
|
-
icon =
|
|
192
|
+
icon = "/pinokio-black.png"
|
|
193
|
+
//icon = null
|
|
194
|
+
}
|
|
195
|
+
if (m && m.iconpath) {
|
|
196
|
+
iconpath = m.iconpath
|
|
197
|
+
}
|
|
198
|
+
if (m && m.path) {
|
|
199
|
+
apipath = m.path
|
|
165
200
|
}
|
|
166
201
|
uri = x.name
|
|
167
202
|
} else {
|
|
@@ -182,6 +217,8 @@ class Server {
|
|
|
182
217
|
let browser_browse_url = browser_url + "/browse"
|
|
183
218
|
return {
|
|
184
219
|
icon,
|
|
220
|
+
iconpath,
|
|
221
|
+
path: apipath,
|
|
185
222
|
running: x.running ? true : false,
|
|
186
223
|
run: x.run,
|
|
187
224
|
menu: x.menu,
|
|
@@ -232,9 +269,7 @@ class Server {
|
|
|
232
269
|
}
|
|
233
270
|
async chrome(req, res, type) {
|
|
234
271
|
let name = req.params.name
|
|
235
|
-
let
|
|
236
|
-
let rawpath = "/api/" + name
|
|
237
|
-
let config = (await this.kernel.loader.load(app_path)).resolved
|
|
272
|
+
let config = await this.kernel.api.meta(name)
|
|
238
273
|
|
|
239
274
|
let error = null
|
|
240
275
|
if (config && config.version) {
|
|
@@ -293,8 +328,12 @@ class Server {
|
|
|
293
328
|
let files = await fs.promises.readdir(p, { withFileTypes: true })
|
|
294
329
|
files = files.filter((file) => {
|
|
295
330
|
return file.name.endsWith(".json") || file.name.endsWith(".js")
|
|
331
|
+
}).filter((file) => {
|
|
332
|
+
return file.name !== "pinokio.js" && file.name !== "pinokio.json" && file.name !== "pinokio_meta.json"
|
|
296
333
|
})
|
|
334
|
+
console.log("files", files)
|
|
297
335
|
config = {
|
|
336
|
+
icon: config.icon,
|
|
298
337
|
title: name,
|
|
299
338
|
menu: files.map((file) => {
|
|
300
339
|
return {
|
|
@@ -307,6 +346,20 @@ class Server {
|
|
|
307
346
|
await this.renderMenu(uri, name, config, [])
|
|
308
347
|
}
|
|
309
348
|
let platform = os.platform()
|
|
349
|
+
console.log("config before", { config })
|
|
350
|
+
|
|
351
|
+
// if (config.icon) {
|
|
352
|
+
// //config.iconpath = this.kernel.path("api", name, config.icon)
|
|
353
|
+
// config.iconpath = config.icon
|
|
354
|
+
// config.icon = `${rawpath}/${config.icon}?raw=true`
|
|
355
|
+
// } else {
|
|
356
|
+
// //config.iconpath = this.kernel.path("api", name, "pinokio_icon.png")
|
|
357
|
+
// config.iconpath = "pinokio.png"
|
|
358
|
+
// config.icon = "/pinokio-black.png"
|
|
359
|
+
// }
|
|
360
|
+
|
|
361
|
+
console.log("config after", { config })
|
|
362
|
+
|
|
310
363
|
|
|
311
364
|
|
|
312
365
|
// get all memory variable stied to the current repository
|
|
@@ -330,7 +383,6 @@ class Server {
|
|
|
330
383
|
|
|
331
384
|
await this.init_env("api/" + name)
|
|
332
385
|
|
|
333
|
-
|
|
334
386
|
let mode = "run"
|
|
335
387
|
if (req.query && req.query.mode) {
|
|
336
388
|
mode = req.query.mode
|
|
@@ -339,6 +391,16 @@ class Server {
|
|
|
339
391
|
|
|
340
392
|
// profile + feed
|
|
341
393
|
const repositoryPath = path.resolve(this.kernel.api.userdir, name)
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
await git.resolveRef({ fs, dir: repositoryPath, ref: 'HEAD' });
|
|
397
|
+
} catch (err) {
|
|
398
|
+
// repo doesn't exist. initialize.
|
|
399
|
+
console.log(`repo doesn't exist at ${repositoryPath}. initialize`)
|
|
400
|
+
await git.init({ fs, dir: repositoryPath });
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
|
|
342
404
|
let gitRemote = await git.getConfig({ fs, http, dir: repositoryPath, path: 'remote.origin.url' })
|
|
343
405
|
let profile
|
|
344
406
|
let feed
|
|
@@ -353,7 +415,13 @@ class Server {
|
|
|
353
415
|
feed = this.newsfeed(gitRemote)
|
|
354
416
|
}
|
|
355
417
|
|
|
418
|
+
let plugin = await this.getPlugin(name)
|
|
419
|
+
let plugin_menu = null
|
|
420
|
+
if (plugin && plugin.menu && Array.isArray(plugin.menu)) {
|
|
421
|
+
plugin_menu = plugin.menu
|
|
422
|
+
}
|
|
356
423
|
res.render("app", {
|
|
424
|
+
plugin_menu,
|
|
357
425
|
portal: this.portal,
|
|
358
426
|
install: this.install,
|
|
359
427
|
error,
|
|
@@ -366,6 +434,9 @@ class Server {
|
|
|
366
434
|
running:this.kernel.api.running,
|
|
367
435
|
memory: this.kernel.memory,
|
|
368
436
|
sidebar: "/pinokio/sidebar/" + name,
|
|
437
|
+
dynamic: "/pinokio/dynamic/" + name,
|
|
438
|
+
// dynamic: "/pinokio/dynamic/" + name,
|
|
439
|
+
dynamic_content: null,
|
|
369
440
|
name,
|
|
370
441
|
profile,
|
|
371
442
|
feed,
|
|
@@ -378,13 +449,27 @@ class Server {
|
|
|
378
449
|
agent: this.agent,
|
|
379
450
|
src: "/_api/" + name,
|
|
380
451
|
execUrl: "/api/" + name,
|
|
381
|
-
rawpath,
|
|
452
|
+
// rawpath,
|
|
382
453
|
})
|
|
383
454
|
}
|
|
384
|
-
|
|
455
|
+
getVariationUrls(req) {
|
|
456
|
+
let edu = new URL("http://localhost" + req.originalUrl)
|
|
457
|
+
edu.searchParams.set("mode", "source")
|
|
458
|
+
let editorUrl = edu.pathname + edu.search
|
|
385
459
|
|
|
386
|
-
|
|
387
|
-
let
|
|
460
|
+
let referer = req.get("Referer")
|
|
461
|
+
let prevUrl = null
|
|
462
|
+
try {
|
|
463
|
+
if (/\/env\/api\/.+/.test(new URL(referer).pathname)) {
|
|
464
|
+
prevUrl = referer
|
|
465
|
+
}
|
|
466
|
+
} catch (e) {
|
|
467
|
+
}
|
|
468
|
+
return { editorUrl, prevUrl }
|
|
469
|
+
}
|
|
470
|
+
async render(req, res, pathComponents, meta) {
|
|
471
|
+
let base_path = req.base || this.kernel.path("api")
|
|
472
|
+
let full_filepath = path.resolve(base_path, ...pathComponents)
|
|
388
473
|
|
|
389
474
|
let re = /^(.+\..+)(#.*)$/
|
|
390
475
|
let match = re.exec(full_filepath)
|
|
@@ -512,85 +597,9 @@ class Server {
|
|
|
512
597
|
display: ["form"]
|
|
513
598
|
})
|
|
514
599
|
} else if (pathComponents.length === 0 && req.query.mode === "download") {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}, {
|
|
519
|
-
name: "git",
|
|
520
|
-
}, {
|
|
521
|
-
name: "zip",
|
|
522
|
-
}, {
|
|
523
|
-
name: "node",
|
|
524
|
-
}, {
|
|
525
|
-
name: "ffmpeg",
|
|
526
|
-
// type: "conda",
|
|
527
|
-
// name: "ffmpeg",
|
|
528
|
-
// args: "-c conda-forge"
|
|
529
|
-
}]
|
|
530
|
-
let platform = os.platform()
|
|
531
|
-
if (platform === "win32") {
|
|
532
|
-
requirements.push({
|
|
533
|
-
name: "registry"
|
|
534
|
-
})
|
|
535
|
-
requirements.push({
|
|
536
|
-
name: "vs"
|
|
537
|
-
})
|
|
538
|
-
}
|
|
539
|
-
if (platform === "darwin") {
|
|
540
|
-
requirements.push({
|
|
541
|
-
name: "brew"
|
|
542
|
-
})
|
|
543
|
-
}
|
|
544
|
-
if (platform === "linux") {
|
|
545
|
-
requirements.push({
|
|
546
|
-
name: "gxx"
|
|
547
|
-
})
|
|
548
|
-
}
|
|
549
|
-
if (this.kernel.gpu === "nvidia") {
|
|
550
|
-
requirements.push({
|
|
551
|
-
name: "cuda",
|
|
552
|
-
})
|
|
553
|
-
}
|
|
554
|
-
requirements = requirements.concat([{
|
|
555
|
-
name: "py"
|
|
556
|
-
}, {
|
|
557
|
-
name: "cloudflared"
|
|
558
|
-
}, {
|
|
559
|
-
name: "playwright"
|
|
560
|
-
}, {
|
|
561
|
-
name: "huggingface"
|
|
562
|
-
}, {
|
|
563
|
-
name: "uv"
|
|
564
|
-
}])
|
|
565
|
-
|
|
566
|
-
let requirements_pending = !this.kernel.bin.installed_initialized
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
let install_required = true
|
|
570
|
-
if (!requirements_pending) {
|
|
571
|
-
install_required = false
|
|
572
|
-
for(let i=0; i<requirements.length; i++) {
|
|
573
|
-
let r = requirements[i]
|
|
574
|
-
console.time(r.name)
|
|
575
|
-
let installed = await this.installed(r)
|
|
576
|
-
console.log({ r, installed })
|
|
577
|
-
console.timeEnd(r.name)
|
|
578
|
-
requirements[i].installed = installed
|
|
579
|
-
if (!installed) {
|
|
580
|
-
install_required = true
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
let error = null
|
|
586
|
-
try {
|
|
587
|
-
this.compatible()
|
|
588
|
-
} catch (e) {
|
|
589
|
-
error = e.message
|
|
590
|
-
install_required = true
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
|
|
600
|
+
let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
|
|
601
|
+
bin: this.kernel.bin.preset("ai"),
|
|
602
|
+
})
|
|
594
603
|
res.render("download", {
|
|
595
604
|
error,
|
|
596
605
|
current: req.originalUrl,
|
|
@@ -649,7 +658,6 @@ class Server {
|
|
|
649
658
|
drive: path.resolve(this.kernel.homedir, "drive"),
|
|
650
659
|
}
|
|
651
660
|
}
|
|
652
|
-
console.log({ configArray })
|
|
653
661
|
res.render("settings", {
|
|
654
662
|
platform,
|
|
655
663
|
version: this.version,
|
|
@@ -742,7 +750,10 @@ class Server {
|
|
|
742
750
|
//} else {
|
|
743
751
|
// uri = path.resolve(this.kernel.api.userdir, ...pathComponents)
|
|
744
752
|
//}
|
|
745
|
-
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
//uri = path.resolve(this.kernel.api.userdir, ...pathComponents)
|
|
756
|
+
uri = full_filepath
|
|
746
757
|
|
|
747
758
|
let pinokioPath
|
|
748
759
|
if (gitRemote) {
|
|
@@ -770,6 +781,7 @@ class Server {
|
|
|
770
781
|
let stem = filename.replace(/\.(json|js)$/, "")
|
|
771
782
|
let stempath = pathComponents.slice(0,-1).join("/") + "/_" + stem
|
|
772
783
|
for(let p of [stempath + ".json", stempath + ".js"]) {
|
|
784
|
+
//const schemaFullPath = path.resolve(this.kernel.api.userdir, p)
|
|
773
785
|
const schemaFullPath = path.resolve(this.kernel.api.userdir, p)
|
|
774
786
|
let exists = await this.exists(schemaFullPath)
|
|
775
787
|
if (exists) {
|
|
@@ -796,227 +808,59 @@ class Server {
|
|
|
796
808
|
resolved = runner
|
|
797
809
|
}
|
|
798
810
|
|
|
799
|
-
let template
|
|
800
|
-
template = "terminal"
|
|
801
|
-
|
|
811
|
+
let template = "terminal"
|
|
802
812
|
if (req.query && req.query.mode === "source") {
|
|
803
813
|
template = "editor"
|
|
804
814
|
}
|
|
805
815
|
|
|
816
|
+
console.log("check requirements")
|
|
817
|
+
let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
|
|
818
|
+
bin: this.kernel.bin.preset("ai"),
|
|
819
|
+
script: resolved
|
|
820
|
+
})
|
|
821
|
+
console.log({ requirements })
|
|
822
|
+
|
|
823
|
+
//let requirements = this.kernel.bin.requirements(resolved)
|
|
824
|
+
//let requirements_pending = !this.kernel.bin.installed_initialized
|
|
825
|
+
//let install_required = true
|
|
826
|
+
//if (!requirements_pending) {
|
|
827
|
+
// install_required = false
|
|
828
|
+
// for(let i=0; i<requirements.length; i++) {
|
|
829
|
+
// let r = requirements[i]
|
|
830
|
+
|
|
831
|
+
// let relevant = this.relevant(r)
|
|
832
|
+
// requirements[i].relevant = relevant
|
|
833
|
+
// if (relevant) {
|
|
834
|
+
// let installed = await this.installed(r)
|
|
835
|
+
// requirements[i].installed = installed
|
|
836
|
+
// if (!installed) {
|
|
837
|
+
// install_required = true
|
|
838
|
+
// }
|
|
839
|
+
// }
|
|
840
|
+
// }
|
|
841
|
+
//}
|
|
806
842
|
|
|
807
|
-
//
|
|
808
|
-
//
|
|
809
|
-
//
|
|
810
|
-
//
|
|
811
|
-
//
|
|
812
|
-
//
|
|
813
|
-
//
|
|
814
|
-
// } else if (schemaPath && schemaPath.length > 0) {
|
|
815
|
-
// template = "form"
|
|
816
|
-
// } else {
|
|
817
|
-
// if (req.query && req.query.fullscreen) {
|
|
818
|
-
// template = "fullscreen_editor"
|
|
819
|
-
// } else {
|
|
820
|
-
// template = "editor"
|
|
821
|
-
// }
|
|
822
|
-
// }
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
let requirements = [{
|
|
826
|
-
name: "conda",
|
|
827
|
-
}, {
|
|
828
|
-
name: "git",
|
|
829
|
-
}, {
|
|
830
|
-
name: "zip",
|
|
831
|
-
}, {
|
|
832
|
-
name: "node",
|
|
833
|
-
}, {
|
|
834
|
-
name: "ffmpeg",
|
|
835
|
-
// type: "conda",
|
|
836
|
-
// name: "ffmpeg",
|
|
837
|
-
// args: "-c conda-forge"
|
|
838
|
-
}]
|
|
839
|
-
let platform = os.platform()
|
|
840
|
-
if (platform === "win32") {
|
|
841
|
-
requirements.push({
|
|
842
|
-
name: "registry"
|
|
843
|
-
})
|
|
844
|
-
requirements.push({
|
|
845
|
-
name: "vs"
|
|
846
|
-
})
|
|
847
|
-
}
|
|
848
|
-
if (platform === "darwin") {
|
|
849
|
-
requirements.push({
|
|
850
|
-
name: "brew"
|
|
851
|
-
})
|
|
852
|
-
}
|
|
853
|
-
if (platform === "linux") {
|
|
854
|
-
requirements.push({
|
|
855
|
-
name: "gxx"
|
|
856
|
-
})
|
|
857
|
-
}
|
|
858
|
-
if (this.kernel.gpu === "nvidia") {
|
|
859
|
-
requirements.push({
|
|
860
|
-
name: "cuda"
|
|
861
|
-
})
|
|
862
|
-
}
|
|
863
|
-
requirements = requirements.concat([{
|
|
864
|
-
name: "py"
|
|
865
|
-
}, {
|
|
866
|
-
name: "cloudflared"
|
|
867
|
-
}, {
|
|
868
|
-
name: "playwright"
|
|
869
|
-
}, {
|
|
870
|
-
name: "huggingface"
|
|
871
|
-
}, {
|
|
872
|
-
name: "uv"
|
|
873
|
-
}])
|
|
874
|
-
// if (platform === "linux") {
|
|
875
|
-
// requirements.push({
|
|
876
|
-
// name: "brew"
|
|
877
|
-
// })
|
|
878
|
-
// }
|
|
879
|
-
|
|
880
|
-
if (resolved && resolved.requires && resolved.requires.length > 0) {
|
|
881
|
-
/*********************************************************************
|
|
882
|
-
|
|
883
|
-
syntax :=
|
|
884
|
-
|
|
885
|
-
{
|
|
886
|
-
platform: <win32|darwin|linux>,
|
|
887
|
-
type: <conda|pip|brew|none>,
|
|
888
|
-
name: <package name>, (example: "ffmpeg", "git")
|
|
889
|
-
args: <install command flags> (example: "-c conda-forge")
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
1. pinokio native install: no need for specifying platforms since they are included
|
|
894
|
-
|
|
895
|
-
{
|
|
896
|
-
name: "conda"
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
2. non native install (conda, pip, brew)
|
|
901
|
-
|
|
902
|
-
2.1. Same on all platforms
|
|
903
|
-
|
|
904
|
-
[{
|
|
905
|
-
type: "conda",
|
|
906
|
-
name: "ffmpeg",
|
|
907
|
-
args: "-c conda-forge"
|
|
908
|
-
}]
|
|
909
|
-
|
|
910
|
-
2.2. Specify per platform
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
[
|
|
914
|
-
{ name: "conda" },
|
|
915
|
-
{ platform: "darwin", type: "brew", name: "llvm" },
|
|
916
|
-
{ platform: "linux", tyoe: "conda", name: "llvm", args: "-c conda-forge llvm" },
|
|
917
|
-
{ platform: "win32", tyoe: "conda", name: "llvm", args: "-c conda-forge llvm" }
|
|
918
|
-
]
|
|
919
|
-
|
|
920
|
-
[
|
|
921
|
-
{ name: "conda" },
|
|
922
|
-
{ platform: ["darwin", "linux"], type: "brew", name: "llvm" },
|
|
923
|
-
{ platform: "win32", tyoe: "conda", name: "llvm", args: "-c conda-forge llvm" }
|
|
924
|
-
]
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
*********************************************************************/
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
let platform = os.platform()
|
|
932
|
-
let type_name_set = new Set()
|
|
933
|
-
for(let r of resolved.requires) {
|
|
934
|
-
// if no platform specified, or if the specified platform matches the current platform
|
|
935
|
-
if (!r.platform || platform === r.platform || Array.isArray(r.platform) && r.platform.includes(platform) ) {
|
|
936
|
-
if (Array.isArray(r.name)) {
|
|
937
|
-
// if array, just add it
|
|
938
|
-
requirements.push(r)
|
|
939
|
-
} else {
|
|
940
|
-
let type_name = `${r.type ? r.type : ''}/${r.name}`
|
|
941
|
-
if (!type_name_set.has(type_name)) {
|
|
942
|
-
type_name_set.add(type_name)
|
|
943
|
-
requirements.push(r)
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
}
|
|
950
|
-
let requirements_pending = !this.kernel.bin.installed_initialized
|
|
951
|
-
|
|
952
|
-
let install_required = true
|
|
953
|
-
if (!requirements_pending) {
|
|
954
|
-
install_required = false
|
|
955
|
-
console.time(">>>>>>>>>> 1")
|
|
956
|
-
for(let i=0; i<requirements.length; i++) {
|
|
957
|
-
let r = requirements[i]
|
|
958
|
-
|
|
959
|
-
let relevant = this.relevant(r)
|
|
960
|
-
requirements[i].relevant = relevant
|
|
961
|
-
if (relevant) {
|
|
962
|
-
let installed = await this.installed(r)
|
|
963
|
-
requirements[i].installed = installed
|
|
964
|
-
if (!installed) {
|
|
965
|
-
install_required = true
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
console.timeEnd(">>>>>>>>>> 1")
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
let error = null
|
|
973
|
-
try {
|
|
974
|
-
this.compatible()
|
|
975
|
-
} catch (e) {
|
|
976
|
-
error = e.message
|
|
977
|
-
install_required = true
|
|
978
|
-
}
|
|
843
|
+
//let error = null
|
|
844
|
+
//try {
|
|
845
|
+
// this.kernel.bin.compatible()
|
|
846
|
+
//} catch (e) {
|
|
847
|
+
// error = e.message
|
|
848
|
+
// install_required = true
|
|
849
|
+
//}
|
|
979
850
|
|
|
980
|
-
requirements = requirements.filter((r) => {
|
|
981
|
-
|
|
982
|
-
})
|
|
851
|
+
//requirements = requirements.filter((r) => {
|
|
852
|
+
// return r.relevant
|
|
853
|
+
//})
|
|
983
854
|
|
|
984
855
|
|
|
985
856
|
let mem = this.getMemory(filepath)
|
|
986
|
-
let edu = new URL("http://localhost" + req.originalUrl)
|
|
987
|
-
edu.searchParams.set("mode", "source")
|
|
988
|
-
let editorUrl = edu.pathname + edu.search
|
|
989
|
-
|
|
990
|
-
let referer = req.get("Referer")
|
|
991
857
|
|
|
992
|
-
let
|
|
993
|
-
try {
|
|
994
|
-
if (/\/env\/api\/.+/.test(new URL(referer).pathname)) {
|
|
995
|
-
prev = referer
|
|
996
|
-
}
|
|
997
|
-
} catch (e) {
|
|
998
|
-
}
|
|
858
|
+
let { editorUrl, prevUrl } = this.getVariationUrls(req)
|
|
999
859
|
|
|
1000
860
|
|
|
1001
|
-
let
|
|
1002
|
-
|
|
1003
|
-
if (
|
|
1004
|
-
let env = await Environment.get2(filepath, this.kernel)
|
|
1005
|
-
for(let item of resolved.pre) {
|
|
1006
|
-
if (item.env) {
|
|
1007
|
-
if (env[item.env]) {
|
|
1008
|
-
item.val = env[item.env]
|
|
1009
|
-
} else {
|
|
1010
|
-
if (item.default) {
|
|
1011
|
-
item.val = item.default
|
|
1012
|
-
}
|
|
1013
|
-
requires_instantiation = true
|
|
1014
|
-
}
|
|
1015
|
-
pre_items.push(item)
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
if (requires_instantiation) {
|
|
861
|
+
let env_requirements = await Environment.requirements(resolved, filepath, this.kernel)
|
|
862
|
+
console.log({ env_requirements })
|
|
863
|
+
if (env_requirements.requires_instantiation) {
|
|
1020
864
|
let p = Util.api_path(filepath, this.kernel)
|
|
1021
865
|
let platform = os.platform()
|
|
1022
866
|
if (platform === "win32") {
|
|
@@ -1028,14 +872,24 @@ class Server {
|
|
|
1028
872
|
theme: this.theme,
|
|
1029
873
|
filename,
|
|
1030
874
|
filepath: p,
|
|
1031
|
-
items:
|
|
875
|
+
items: env_requirements.items
|
|
1032
876
|
})
|
|
1033
877
|
} else {
|
|
878
|
+
|
|
879
|
+
// check if it's a prototype script
|
|
880
|
+
let kill_message
|
|
881
|
+
let callback
|
|
882
|
+
if (req.query.callback) {
|
|
883
|
+
callback = req.query.callback
|
|
884
|
+
// kill_message = "Done! Click to go to the project"
|
|
885
|
+
}
|
|
886
|
+
|
|
1034
887
|
let logpath = encodeURIComponent(Util.log_path(filepath, this.kernel))
|
|
1035
|
-
console.log({ logpath })
|
|
1036
888
|
const result = {
|
|
1037
889
|
portal: this.portal,
|
|
1038
|
-
|
|
890
|
+
kill_message,
|
|
891
|
+
callback,
|
|
892
|
+
prev: prevUrl,
|
|
1039
893
|
error,
|
|
1040
894
|
memory: mem,
|
|
1041
895
|
// memory: mem,
|
|
@@ -1069,6 +923,8 @@ class Server {
|
|
|
1069
923
|
editorUrl,
|
|
1070
924
|
execUrl: "~" + req.originalUrl.replace(/^\/_api/, "\/api"),
|
|
1071
925
|
proxies: this.kernel.api.proxies[filepath],
|
|
926
|
+
cwd: req.query.cwd,
|
|
927
|
+
script_path: (req.base ? full_filepath : null),
|
|
1072
928
|
}
|
|
1073
929
|
|
|
1074
930
|
res.render(template, result)
|
|
@@ -1178,6 +1034,24 @@ class Server {
|
|
|
1178
1034
|
config.update = "/api/" + link
|
|
1179
1035
|
}
|
|
1180
1036
|
}
|
|
1037
|
+
|
|
1038
|
+
// override config
|
|
1039
|
+
if (file.name === "pinokio_meta.json" || file.name === "pinokio.json") {
|
|
1040
|
+
let p = path.resolve(filepath, file.name)
|
|
1041
|
+
let c = (await this.kernel.loader.load(p)).resolved
|
|
1042
|
+
if (c.title) {
|
|
1043
|
+
if (!config) config = {}
|
|
1044
|
+
config.title = c.title
|
|
1045
|
+
}
|
|
1046
|
+
if (c.description) {
|
|
1047
|
+
if (!config) config = {}
|
|
1048
|
+
config.description = c.description
|
|
1049
|
+
}
|
|
1050
|
+
if (c.icon) {
|
|
1051
|
+
if (!config) config = {}
|
|
1052
|
+
config.icon = c.icon
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1181
1055
|
}
|
|
1182
1056
|
if (!config) config = {}
|
|
1183
1057
|
|
|
@@ -1232,7 +1106,6 @@ class Server {
|
|
|
1232
1106
|
pinokioPath = `pinokio://?uri=${gitRemote}/${pathComponents.slice(1).join("/")}`
|
|
1233
1107
|
}
|
|
1234
1108
|
|
|
1235
|
-
|
|
1236
1109
|
let running = []
|
|
1237
1110
|
let notRunning = []
|
|
1238
1111
|
if (pathComponents.length === 0) {
|
|
@@ -1314,7 +1187,18 @@ class Server {
|
|
|
1314
1187
|
}
|
|
1315
1188
|
index++;
|
|
1316
1189
|
running.push(items[i])
|
|
1317
|
-
break
|
|
1190
|
+
// break
|
|
1191
|
+
}
|
|
1192
|
+
let shell = this.kernel.shell.find({
|
|
1193
|
+
filter: (shell) => {
|
|
1194
|
+
return shell.id.startsWith(items[i].name + "_")
|
|
1195
|
+
}
|
|
1196
|
+
})
|
|
1197
|
+
if (shell.length > 0) {
|
|
1198
|
+
items[i].running = true
|
|
1199
|
+
items[i].index = index
|
|
1200
|
+
index++;
|
|
1201
|
+
running.push(items[i])
|
|
1318
1202
|
}
|
|
1319
1203
|
}
|
|
1320
1204
|
if (!items[i].running) {
|
|
@@ -1336,7 +1220,9 @@ class Server {
|
|
|
1336
1220
|
//let name = (x.name.startsWith("0x") ? Buffer.from(x.name.slice(2), "hex").toString() : x.name)
|
|
1337
1221
|
let name
|
|
1338
1222
|
let description
|
|
1339
|
-
let icon
|
|
1223
|
+
let icon = "/pinokio-black.png"
|
|
1224
|
+
let iconpath
|
|
1225
|
+
let apipath
|
|
1340
1226
|
let uri
|
|
1341
1227
|
if (meta) {
|
|
1342
1228
|
let m = meta[x.name]
|
|
@@ -1345,7 +1231,14 @@ class Server {
|
|
|
1345
1231
|
if (m && m.icon) {
|
|
1346
1232
|
icon = m.icon
|
|
1347
1233
|
} else {
|
|
1348
|
-
icon = null
|
|
1234
|
+
//icon = null
|
|
1235
|
+
icon = "/pinokio-black.png"
|
|
1236
|
+
}
|
|
1237
|
+
if (m && m.iconpath) {
|
|
1238
|
+
iconpath = m.iconpath
|
|
1239
|
+
}
|
|
1240
|
+
if (m && m.path) {
|
|
1241
|
+
apipath = m.path
|
|
1349
1242
|
}
|
|
1350
1243
|
uri = x.name
|
|
1351
1244
|
} else {
|
|
@@ -1359,6 +1252,8 @@ class Server {
|
|
|
1359
1252
|
}
|
|
1360
1253
|
return {
|
|
1361
1254
|
icon,
|
|
1255
|
+
iconpath,
|
|
1256
|
+
path: apipath,
|
|
1362
1257
|
menu: x.menu,
|
|
1363
1258
|
run: x.run,
|
|
1364
1259
|
shortcuts: x.shortcuts,
|
|
@@ -1374,6 +1269,7 @@ class Server {
|
|
|
1374
1269
|
}
|
|
1375
1270
|
})
|
|
1376
1271
|
|
|
1272
|
+
|
|
1377
1273
|
// if (req.query && req.query.mode === "task") {
|
|
1378
1274
|
// running = running.filter((x) => {
|
|
1379
1275
|
// return x.run && Array.isArray(x.run)
|
|
@@ -1422,7 +1318,6 @@ class Server {
|
|
|
1422
1318
|
}
|
|
1423
1319
|
if (config.xterm) {
|
|
1424
1320
|
this.xterm = config.xterm
|
|
1425
|
-
console.log("this.xterm", this.xterm)
|
|
1426
1321
|
}
|
|
1427
1322
|
}
|
|
1428
1323
|
}
|
|
@@ -1433,6 +1328,7 @@ class Server {
|
|
|
1433
1328
|
res.render("index", {
|
|
1434
1329
|
portal: this.portal,
|
|
1435
1330
|
install: this.install,
|
|
1331
|
+
folders: null,
|
|
1436
1332
|
launch_complete: this.kernel.launch_complete,
|
|
1437
1333
|
home_url: `http://localhost:${this.port}`,
|
|
1438
1334
|
proxy: home_proxy,
|
|
@@ -1571,6 +1467,9 @@ class Server {
|
|
|
1571
1467
|
if (menuitem.fs) {
|
|
1572
1468
|
// file explorer
|
|
1573
1469
|
config.menu[i].href = path.resolve(this.kernel.homedir, "api", name, menuitem.href)
|
|
1470
|
+
} else if (menuitem.command) {
|
|
1471
|
+
// file explorer
|
|
1472
|
+
config.menu[i].href = path.resolve(this.kernel.homedir, "api", name, menuitem.href)
|
|
1574
1473
|
} else {
|
|
1575
1474
|
let absolute = path.resolve(__dirname, ...pathComponents, menuitem.href)
|
|
1576
1475
|
let seed = path.resolve(__dirname)
|
|
@@ -1585,6 +1484,69 @@ class Server {
|
|
|
1585
1484
|
menuitem.href = menuitem.href + "?" + new URLSearchParams(menuitem.params).toString();
|
|
1586
1485
|
}
|
|
1587
1486
|
|
|
1487
|
+
|
|
1488
|
+
if (menuitem.shell) {
|
|
1489
|
+
/*
|
|
1490
|
+
shell :- {
|
|
1491
|
+
id (optional),
|
|
1492
|
+
path (required), // api, bin, quick, network, api/
|
|
1493
|
+
message (optional), // if not specified, start an empty shell
|
|
1494
|
+
venv,
|
|
1495
|
+
input, // input mode if true
|
|
1496
|
+
callback, // callback url after shutting down
|
|
1497
|
+
kill, // when to kill (regular expression)
|
|
1498
|
+
}
|
|
1499
|
+
*/
|
|
1500
|
+
|
|
1501
|
+
let rendered = this.kernel.template.render(menuitem.shell, {})
|
|
1502
|
+
let params = new URLSearchParams()
|
|
1503
|
+
// if (rendered.id) {
|
|
1504
|
+
// params.set("id", encodeURIComponent(rendered.id))
|
|
1505
|
+
// } else {
|
|
1506
|
+
// let shell_id = "sh_" + name + "_" + i
|
|
1507
|
+
// params.set("id", encodeURIComponent(shell_id))
|
|
1508
|
+
// }
|
|
1509
|
+
let basePath = this.kernel.path("api", name)
|
|
1510
|
+
if (rendered.path) {
|
|
1511
|
+
params.set("path", encodeURIComponent(this.kernel.api.filePath(rendered.path, basePath)))
|
|
1512
|
+
} else {
|
|
1513
|
+
params.set("path", encodeURIComponent(basePath))
|
|
1514
|
+
}
|
|
1515
|
+
if (rendered.message) params.set("message", encodeURIComponent(rendered.message))
|
|
1516
|
+
if (rendered.venv) params.set("venv", encodeURIComponent(rendered.venv))
|
|
1517
|
+
if (rendered.input) params.set("input", true)
|
|
1518
|
+
if (rendered.callback) params.set("callback", encodeURIComponent(rendered.callback))
|
|
1519
|
+
if (rendered.kill) params.set("kill", encodeURIComponent(rendered.kill))
|
|
1520
|
+
if (rendered.done) params.set("done", encodeURIComponent(rendered.done))
|
|
1521
|
+
if (rendered.env) {
|
|
1522
|
+
for(let key in rendered.env) {
|
|
1523
|
+
let env_key = "env." + key
|
|
1524
|
+
params.set(env_key, rendered.env[key])
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
if (rendered.conda) {
|
|
1528
|
+
for(let key in rendered.conda) {
|
|
1529
|
+
let conda_key = "conda." + key
|
|
1530
|
+
params.set(conda_key, rendered.conda[key])
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
// deterministic shell id generation
|
|
1535
|
+
// `${api_path}_${i}_${hash}`
|
|
1536
|
+
let hash = crypto.createHash('md5').update(JSON.stringify(rendered)).digest('hex')
|
|
1537
|
+
let shell_id
|
|
1538
|
+
if (rendered.id) {
|
|
1539
|
+
shell_id = encodeURIComponent(`${name}_${rendered.id}`)
|
|
1540
|
+
} else {
|
|
1541
|
+
shell_id = encodeURIComponent(`${name}_${i}_${hash}`)
|
|
1542
|
+
}
|
|
1543
|
+
menuitem.href = "/shell/" + shell_id + "?" + params.toString()
|
|
1544
|
+
let shell = this.kernel.shell.get(shell_id)
|
|
1545
|
+
if (shell) {
|
|
1546
|
+
menuitem.running = true
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1588
1550
|
if (menuitem.href) {
|
|
1589
1551
|
let u
|
|
1590
1552
|
if (menuitem.href.startsWith("http")) {
|
|
@@ -1744,95 +1706,20 @@ class Server {
|
|
|
1744
1706
|
//
|
|
1745
1707
|
// console.log("MENU", JSON.stringify(config.menu, null, 2))
|
|
1746
1708
|
|
|
1709
|
+
// if (!config.icon) {
|
|
1710
|
+
// if (this.theme === "light") {
|
|
1711
|
+
// config.icon = "/pinokio-black.png"
|
|
1712
|
+
// } else {
|
|
1713
|
+
// config.icon = "/pinokio-white.png"
|
|
1714
|
+
// }
|
|
1715
|
+
// }
|
|
1716
|
+
// console.log("############## config", config)
|
|
1747
1717
|
|
|
1748
1718
|
return config
|
|
1749
1719
|
} else {
|
|
1750
1720
|
return config
|
|
1751
1721
|
}
|
|
1752
1722
|
}
|
|
1753
|
-
relevant(r) {
|
|
1754
|
-
/*
|
|
1755
|
-
single platform
|
|
1756
|
-
[
|
|
1757
|
-
{ name: "conda" },
|
|
1758
|
-
{ platform: "darwin", type: "brew", name: "llvm" },
|
|
1759
|
-
{ platform: "linux", tyoe: "conda", name: "llvm", args: "-c conda-forge llvm" },
|
|
1760
|
-
{ platform: "win32", tyoe: "conda", name: "llvm", args: "-c conda-forge llvm" }
|
|
1761
|
-
]
|
|
1762
|
-
|
|
1763
|
-
multiple platforms
|
|
1764
|
-
[
|
|
1765
|
-
{ name: "conda" },
|
|
1766
|
-
{ platform: ["darwin", "linux"], type: "brew", name: "llvm" },
|
|
1767
|
-
{ platform: "win32", tyoe: "conda", name: "llvm", args: "-c conda-forge llvm" }
|
|
1768
|
-
]
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
platform & arch
|
|
1772
|
-
[
|
|
1773
|
-
{ name: "conda" },
|
|
1774
|
-
{ platform: "darwin", arch: ["arm64", "x64"], type: "brew", name: "llvm" },
|
|
1775
|
-
]
|
|
1776
|
-
*/
|
|
1777
|
-
|
|
1778
|
-
let platform = os.platform()
|
|
1779
|
-
let arch = os.arch()
|
|
1780
|
-
let gpu = this.kernel.gpu
|
|
1781
|
-
let relevant = {
|
|
1782
|
-
platform: false,
|
|
1783
|
-
arch: false,
|
|
1784
|
-
gpu: false,
|
|
1785
|
-
}
|
|
1786
|
-
if (r.platform) {
|
|
1787
|
-
if (Array.isArray(r.platform)) {
|
|
1788
|
-
// multiple items
|
|
1789
|
-
if (r.platform.includes(platform)) {
|
|
1790
|
-
relevant.platform = true
|
|
1791
|
-
}
|
|
1792
|
-
} else {
|
|
1793
|
-
// one item
|
|
1794
|
-
if (r.platform === platform) {
|
|
1795
|
-
relevant.platform = true
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
} else {
|
|
1799
|
-
// all platforms
|
|
1800
|
-
relevant.platform = true
|
|
1801
|
-
}
|
|
1802
|
-
if (r.arch) {
|
|
1803
|
-
if (Array.isArray(r.arch)) {
|
|
1804
|
-
// multiple items
|
|
1805
|
-
if (r.arch.includes(arch)) {
|
|
1806
|
-
relevant.arch = true
|
|
1807
|
-
}
|
|
1808
|
-
} else {
|
|
1809
|
-
// one item
|
|
1810
|
-
if (r.arch === arch) {
|
|
1811
|
-
relevant.arch = true
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
} else {
|
|
1815
|
-
// all platforms
|
|
1816
|
-
relevant.arch = true
|
|
1817
|
-
}
|
|
1818
|
-
if (r.gpu) {
|
|
1819
|
-
if (Array.isArray(r.gpu)) {
|
|
1820
|
-
// multiple items
|
|
1821
|
-
if (r.gpu.includes(gpu)) {
|
|
1822
|
-
relevant.gpu = true
|
|
1823
|
-
}
|
|
1824
|
-
} else {
|
|
1825
|
-
// one item
|
|
1826
|
-
if (r.gpu === gpu) {
|
|
1827
|
-
relevant.gpu = true
|
|
1828
|
-
}
|
|
1829
|
-
}
|
|
1830
|
-
} else {
|
|
1831
|
-
// all platforms
|
|
1832
|
-
relevant.gpu = true
|
|
1833
|
-
}
|
|
1834
|
-
return relevant.platform && relevant.arch && relevant.gpu
|
|
1835
|
-
}
|
|
1836
1723
|
async _installed(name, type) {
|
|
1837
1724
|
if (type === "conda") {
|
|
1838
1725
|
return this.kernel.bin.installed.conda.has(name)
|
|
@@ -1864,7 +1751,6 @@ class Server {
|
|
|
1864
1751
|
}
|
|
1865
1752
|
}
|
|
1866
1753
|
async sudo_exec(message, homedir) {
|
|
1867
|
-
console.log("sudo_exec", { message, homedir })
|
|
1868
1754
|
// sudo-prompt uses TEMP
|
|
1869
1755
|
// let TEMP = path.resolve(homedir, "cache", "TEMP")
|
|
1870
1756
|
// await fs.promises.mkdir(TEMP, { recursive: true }).catch((e) => { })
|
|
@@ -1922,6 +1808,85 @@ class Server {
|
|
|
1922
1808
|
}
|
|
1923
1809
|
console.log("FINISHED MV")
|
|
1924
1810
|
}
|
|
1811
|
+
getPeerInfo() {
|
|
1812
|
+
let list = []
|
|
1813
|
+
let peers_info = {}
|
|
1814
|
+
if (this.kernel.peer.info) {
|
|
1815
|
+
peers_info = this.kernel.peer.info
|
|
1816
|
+
let remote_peers = Object.keys(this.kernel.peer.info).filter(x => x !== this.kernel.peer.host)
|
|
1817
|
+
let nodes = [this.kernel.peer.host].concat(remote_peers)
|
|
1818
|
+
for(let host of nodes) {
|
|
1819
|
+
let processes = []
|
|
1820
|
+
try {
|
|
1821
|
+
let procs = this.kernel.peer.info[host].proc
|
|
1822
|
+
let router = this.kernel.peer.info[host].router
|
|
1823
|
+
let port_mapping = this.kernel.peer.info[host].port_mapping
|
|
1824
|
+
for(let proc of procs) {
|
|
1825
|
+
let chunks = proc.ip.split(":")
|
|
1826
|
+
let internal_port = chunks[chunks.length-1]
|
|
1827
|
+
let internal_host = chunks.slice(0, chunks.length-1).join(":")
|
|
1828
|
+
let external_port = port_mapping[internal_port]
|
|
1829
|
+
|
|
1830
|
+
let merged
|
|
1831
|
+
let external_ip
|
|
1832
|
+
if (external_port) {
|
|
1833
|
+
external_ip = `${host}:${external_port}`
|
|
1834
|
+
// merged = Array.from(new Set(router[external_ip].concat(router[proc.ip])))
|
|
1835
|
+
} else {
|
|
1836
|
+
// merged = router[proc.ip]
|
|
1837
|
+
}
|
|
1838
|
+
processes.push({
|
|
1839
|
+
external_router: router[external_ip] || [],
|
|
1840
|
+
internal_router: router[proc.ip] || [],
|
|
1841
|
+
//router: merged || [],
|
|
1842
|
+
external_ip,
|
|
1843
|
+
external_port: parseInt(external_port),
|
|
1844
|
+
internal_port: parseInt(internal_port),
|
|
1845
|
+
...proc
|
|
1846
|
+
})
|
|
1847
|
+
//if (external_port) {
|
|
1848
|
+
// let external_ip = `${host}:${external_port}`
|
|
1849
|
+
// // merge router
|
|
1850
|
+
// let merged = Array.from(new Set(router[external_ip].concat(router[proc.ip))
|
|
1851
|
+
// processes.push({
|
|
1852
|
+
// //proxy: this.kernel.caddy.mapping[item.ip] || [],
|
|
1853
|
+
// //router: router[external_ip] || [],
|
|
1854
|
+
// router: merged || [],
|
|
1855
|
+
// external_ip,
|
|
1856
|
+
// external_port: parseInt(external_port),
|
|
1857
|
+
// internal_port: parseInt(internal_port),
|
|
1858
|
+
// ...proc
|
|
1859
|
+
// })
|
|
1860
|
+
//} else {
|
|
1861
|
+
// processes.push({
|
|
1862
|
+
// router: [],
|
|
1863
|
+
// external_port: parseInt(external_port),
|
|
1864
|
+
// internal_port: parseInt(internal_port),
|
|
1865
|
+
// ...proc
|
|
1866
|
+
// })
|
|
1867
|
+
//}
|
|
1868
|
+
}
|
|
1869
|
+
// merge processes
|
|
1870
|
+
// 1.
|
|
1871
|
+
processes.sort((a, b) => {
|
|
1872
|
+
return b.external_port-a.external_port
|
|
1873
|
+
})
|
|
1874
|
+
list.push({
|
|
1875
|
+
host,
|
|
1876
|
+
name: this.kernel.peer.info[host].name,
|
|
1877
|
+
platform: this.kernel.peer.info[host].platform,
|
|
1878
|
+
processes
|
|
1879
|
+
})
|
|
1880
|
+
} catch (e) {
|
|
1881
|
+
console.log(">>e", e)
|
|
1882
|
+
console.log({ host, info: this.kernel.peer.info })
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
// console.log("Loaded yet?", nodes.length, Object.keys(peers_info).length, nodes.length === Object.keys(peers_info).length)
|
|
1886
|
+
}
|
|
1887
|
+
return list
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1925
1890
|
|
|
1926
1891
|
async syncConfig() {
|
|
1927
1892
|
|
|
@@ -1948,79 +1913,78 @@ class Server {
|
|
|
1948
1913
|
// 5. existing home is NOT set + new home is set => go through with the "home" setting procedure
|
|
1949
1914
|
// 6. existing home is NOT set + new home is NOT set => don't touch anything => the homedir will be the default home
|
|
1950
1915
|
|
|
1951
|
-
// 2. HOME
|
|
1952
|
-
// 2.1. Check if the config includes NEW_HOME => if so,
|
|
1953
|
-
// - move the HOME folder to NEW_HOME
|
|
1954
|
-
// - set HOME=NEW_HOME
|
|
1955
|
-
// - remove NEW_HOME
|
|
1956
|
-
let existing_home = this.kernel.store.get("home")
|
|
1957
|
-
let new_home = this.kernel.store.get("new_home")
|
|
1958
|
-
|
|
1959
|
-
if (existing_home) {
|
|
1960
|
-
let exists = await fse.pathExists(existing_home)
|
|
1961
|
-
if (exists) {
|
|
1962
|
-
if (new_home) {
|
|
1963
|
-
let new_home_exists = await fse.pathExists(new_home)
|
|
1964
|
-
if (new_home_exists) {
|
|
1965
|
-
// - existing home is set
|
|
1966
|
-
// - existing home exists
|
|
1967
|
-
// - new home is set
|
|
1968
|
-
// - new home exists already
|
|
1969
|
-
// => delete store.new_home ==> will load at store.home
|
|
1970
|
-
this.kernel.store.delete("new_home")
|
|
1971
|
-
} else {
|
|
1972
|
-
// - existing home is set
|
|
1973
|
-
// - existing home exists
|
|
1974
|
-
// - new home is set
|
|
1975
|
-
// - new home does not exist
|
|
1976
|
-
// => run mv()
|
|
1977
|
-
// => update store.home
|
|
1978
|
-
// => delete store.new_home
|
|
1979
|
-
await this.mv(existing_home, new_home)
|
|
1980
|
-
this.kernel.store.set("home", new_home)
|
|
1981
|
-
this.kernel.store.delete("new_home")
|
|
1982
|
-
}
|
|
1983
|
-
} else {
|
|
1984
|
-
// - existing home is set
|
|
1985
|
-
// - existing home exists
|
|
1986
|
-
// - new home is not set
|
|
1987
|
-
// => This is most typical scenario => don't touch anything => the homedir will be the existing home
|
|
1988
|
-
}
|
|
1989
|
-
} else {
|
|
1990
|
-
if (new_home) {
|
|
1991
|
-
// - existing home is set
|
|
1992
|
-
// - but the existing home path DOES NOT exist
|
|
1993
|
-
// - new home is set
|
|
1994
|
-
// => This is an invalid scenario => Just to avoid disaster, just delete store.home and delete store.new_home
|
|
1995
|
-
// => the app will load at ~/pinokio
|
|
1996
|
-
this.kernel.store.delete("home")
|
|
1997
|
-
this.kernel.store.delete("new_home")
|
|
1998
|
-
} else {
|
|
1999
|
-
// - existing home is set
|
|
2000
|
-
// - but the existing home path DOES NOT exist
|
|
2001
|
-
// - new home is NOT set
|
|
2002
|
-
// => This is an invalid scenario => just delete store.home
|
|
2003
|
-
// => the app will load at ~/pinokio
|
|
2004
|
-
this.kernel.store.delete("home")
|
|
2005
|
-
}
|
|
2006
|
-
}
|
|
2007
|
-
} else {
|
|
2008
|
-
if (new_home) {
|
|
2009
|
-
// - existing home is NOT set
|
|
2010
|
-
// - new home is set
|
|
2011
|
-
// => update store.home
|
|
2012
|
-
// => delete store.new_home
|
|
2013
|
-
this.kernel.store.set("home", new_home)
|
|
2014
|
-
this.kernel.store.delete("new_home")
|
|
2015
|
-
} else {
|
|
2016
|
-
// - existing home is NOT set
|
|
2017
|
-
// - new home is NOT set
|
|
2018
|
-
// => don't touch anything => will load at ~/pinokio
|
|
2019
|
-
}
|
|
2020
|
-
}
|
|
1916
|
+
// // 2. HOME
|
|
1917
|
+
// // 2.1. Check if the config includes NEW_HOME => if so,
|
|
1918
|
+
// // - move the HOME folder to NEW_HOME
|
|
1919
|
+
// // - set HOME=NEW_HOME
|
|
1920
|
+
// // - remove NEW_HOME
|
|
1921
|
+
// let existing_home = this.kernel.store.get("home")
|
|
1922
|
+
// let new_home = this.kernel.store.get("new_home")
|
|
1923
|
+
//
|
|
1924
|
+
// if (existing_home) {
|
|
1925
|
+
// let exists = await fse.pathExists(existing_home)
|
|
1926
|
+
// if (exists) {
|
|
1927
|
+
// if (new_home) {
|
|
1928
|
+
// let new_home_exists = await fse.pathExists(new_home)
|
|
1929
|
+
// if (new_home_exists) {
|
|
1930
|
+
// // - existing home is set
|
|
1931
|
+
// // - existing home exists
|
|
1932
|
+
// // - new home is set
|
|
1933
|
+
// // - new home exists already
|
|
1934
|
+
// // => delete store.new_home ==> will load at store.home
|
|
1935
|
+
// this.kernel.store.delete("new_home")
|
|
1936
|
+
// } else {
|
|
1937
|
+
// // - existing home is set
|
|
1938
|
+
// // - existing home exists
|
|
1939
|
+
// // - new home is set
|
|
1940
|
+
// // - new home does not exist
|
|
1941
|
+
// // => run mv()
|
|
1942
|
+
// // => update store.home
|
|
1943
|
+
// // => delete store.new_home
|
|
1944
|
+
// await this.mv(existing_home, new_home)
|
|
1945
|
+
// this.kernel.store.set("home", new_home)
|
|
1946
|
+
// this.kernel.store.delete("new_home")
|
|
1947
|
+
// }
|
|
1948
|
+
// } else {
|
|
1949
|
+
// // - existing home is set
|
|
1950
|
+
// // - existing home exists
|
|
1951
|
+
// // - new home is not set
|
|
1952
|
+
// // => This is most typical scenario => don't touch anything => the homedir will be the existing home
|
|
1953
|
+
// }
|
|
1954
|
+
// } else {
|
|
1955
|
+
// if (new_home) {
|
|
1956
|
+
// // - existing home is set
|
|
1957
|
+
// // - but the existing home path DOES NOT exist
|
|
1958
|
+
// // - new home is set
|
|
1959
|
+
// // => This is an invalid scenario => Just to avoid disaster, just delete store.home and delete store.new_home
|
|
1960
|
+
// // => the app will load at ~/pinokio
|
|
1961
|
+
// this.kernel.store.delete("home")
|
|
1962
|
+
// this.kernel.store.delete("new_home")
|
|
1963
|
+
// } else {
|
|
1964
|
+
// // - existing home is set
|
|
1965
|
+
// // - but the existing home path DOES NOT exist
|
|
1966
|
+
// // - new home is NOT set
|
|
1967
|
+
// // => This is an invalid scenario => just delete store.home
|
|
1968
|
+
// // => the app will load at ~/pinokio
|
|
1969
|
+
// this.kernel.store.delete("home")
|
|
1970
|
+
// }
|
|
1971
|
+
// }
|
|
1972
|
+
// } else {
|
|
1973
|
+
// if (new_home) {
|
|
1974
|
+
// // - existing home is NOT set
|
|
1975
|
+
// // - new home is set
|
|
1976
|
+
// // => update store.home
|
|
1977
|
+
// // => delete store.new_home
|
|
1978
|
+
// this.kernel.store.set("home", new_home)
|
|
1979
|
+
// this.kernel.store.delete("new_home")
|
|
1980
|
+
// } else {
|
|
1981
|
+
// // - existing home is NOT set
|
|
1982
|
+
// // - new home is NOT set
|
|
1983
|
+
// // => don't touch anything => will load at ~/pinokio
|
|
1984
|
+
// }
|
|
1985
|
+
// }
|
|
2021
1986
|
}
|
|
2022
1987
|
async setConfig(config) {
|
|
2023
|
-
console.log("setConfig", config)
|
|
2024
1988
|
let home = this.kernel.store.get("home")
|
|
2025
1989
|
let theme = this.kernel.store.get("theme")
|
|
2026
1990
|
// let drive = this.kernel.store.get("drive")
|
|
@@ -2043,13 +2007,14 @@ class Server {
|
|
|
2043
2007
|
throw new Error("Invalid path: " + config.home)
|
|
2044
2008
|
}
|
|
2045
2009
|
|
|
2046
|
-
// check if the destination already exists => throw error
|
|
2047
|
-
let exists = await fse.pathExists(config.home)
|
|
2048
|
-
if (exists) {
|
|
2049
|
-
throw new Error(`The path ${config.home} already exists. Please remove the folder and retry`)
|
|
2050
|
-
}
|
|
2010
|
+
// // check if the destination already exists => throw error
|
|
2011
|
+
// let exists = await fse.pathExists(config.home)
|
|
2012
|
+
// if (exists) {
|
|
2013
|
+
// throw new Error(`The path ${config.home} already exists. Please remove the folder and retry`)
|
|
2014
|
+
// }
|
|
2051
2015
|
|
|
2052
|
-
this.kernel.store.set("new_home", config.home)
|
|
2016
|
+
//this.kernel.store.set("new_home", config.home)
|
|
2017
|
+
this.kernel.store.set("home", config.home)
|
|
2053
2018
|
}
|
|
2054
2019
|
|
|
2055
2020
|
}
|
|
@@ -2090,24 +2055,28 @@ class Server {
|
|
|
2090
2055
|
// if (config.HTTPS_PROXY) {
|
|
2091
2056
|
// updated.HTTPS_PROXY = config.HTTPS_PROXY
|
|
2092
2057
|
// }
|
|
2093
|
-
|
|
2058
|
+
if (this.kernel.homedir) {
|
|
2059
|
+
const updated = {
|
|
2060
|
+
HTTP_PROXY: config.HTTP_PROXY,
|
|
2061
|
+
HTTPS_PROXY: config.HTTPS_PROXY,
|
|
2062
|
+
NO_PROXY: config.NO_PROXY,
|
|
2063
|
+
}
|
|
2064
|
+
let fullpath = path.resolve(this.kernel.homedir, "ENVIRONMENT")
|
|
2065
|
+
await Util.update_env(fullpath, updated)
|
|
2066
|
+
}
|
|
2094
2067
|
|
|
2095
2068
|
this.kernel.store.set("HTTP_PROXY", config.HTTP_PROXY)
|
|
2096
2069
|
this.kernel.store.set("HTTPS_PROXY", config.HTTPS_PROXY)
|
|
2097
2070
|
this.kernel.store.set("NO_PROXY", config.NO_PROXY)
|
|
2098
2071
|
}
|
|
2099
2072
|
async startLogging(homedir) {
|
|
2100
|
-
console.log(">>>>>>> startLogging", homedir)
|
|
2101
2073
|
if (!this.debug) {
|
|
2102
2074
|
if (this.logInterval) {
|
|
2103
2075
|
clearInterval(this.logInterval)
|
|
2104
2076
|
}
|
|
2105
2077
|
if (homedir) {
|
|
2106
|
-
console.log({ homedir })
|
|
2107
2078
|
let logsdir = path.resolve(homedir, "logs")
|
|
2108
|
-
console.log({ logsdir })
|
|
2109
2079
|
await fs.promises.mkdir(logsdir, { recursive: true }).catch((e) => { console.log(e) })
|
|
2110
|
-
console.log("this.log", this.log)
|
|
2111
2080
|
if (!this.log) {
|
|
2112
2081
|
this.log = fs.createWriteStream(path.resolve(homedir, "logs/stdout.txt"))
|
|
2113
2082
|
process.stdout.write = process.stderr.write = this.log.write.bind(this.log)
|
|
@@ -2131,14 +2100,35 @@ class Server {
|
|
|
2131
2100
|
}
|
|
2132
2101
|
}
|
|
2133
2102
|
}
|
|
2103
|
+
async running(port) {
|
|
2104
|
+
let p = port || DEFAULT_PORT
|
|
2105
|
+
const available = await Util.is_port_available(p)
|
|
2106
|
+
if (available) {
|
|
2107
|
+
return false
|
|
2108
|
+
} else {
|
|
2109
|
+
return true
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
async getPlugin(name) {
|
|
2113
|
+
let api_path = this.kernel.path("api", name)
|
|
2114
|
+
let pinokio_json_path = this.kernel.path("api", name, "pinokio.json")
|
|
2115
|
+
let pinokio_json = (await this.kernel.loader.load(pinokio_json_path)).resolved
|
|
2116
|
+
let plugin_menu = null
|
|
2117
|
+
if (pinokio_json && pinokio_json.plugin && pinokio_json.plugin.menu && Array.isArray(pinokio_json.plugin.menu)) {
|
|
2118
|
+
let uri = this.kernel.path("api")
|
|
2119
|
+
let config = pinokio_json.plugin
|
|
2120
|
+
await this.renderMenu(uri, name, config, [])
|
|
2121
|
+
return config
|
|
2122
|
+
}
|
|
2123
|
+
return null
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2134
2126
|
async start(options) {
|
|
2135
|
-
console.time("SERVER START")
|
|
2136
2127
|
this.debug = false
|
|
2137
2128
|
if (options) {
|
|
2138
2129
|
this.debug = options.debug
|
|
2139
2130
|
this.browser = options.browser
|
|
2140
2131
|
}
|
|
2141
|
-
console.time(">>>> 1")
|
|
2142
2132
|
|
|
2143
2133
|
if (this.listening) {
|
|
2144
2134
|
// stop proxies
|
|
@@ -2165,29 +2155,23 @@ class Server {
|
|
|
2165
2155
|
// } catch (e) {
|
|
2166
2156
|
// }
|
|
2167
2157
|
}
|
|
2168
|
-
console.timeEnd(">>>> 1")
|
|
2169
2158
|
|
|
2170
2159
|
// configure from kernel.store
|
|
2171
|
-
console.time(">>>> 2")
|
|
2172
2160
|
await this.syncConfig()
|
|
2173
|
-
console.timeEnd(">>>> 2")
|
|
2174
2161
|
|
|
2175
|
-
console.time(">>>> 3")
|
|
2176
2162
|
try {
|
|
2177
2163
|
let _home = this.kernel.store.get("home")
|
|
2178
|
-
console.log({ _home })
|
|
2179
2164
|
if (_home) {
|
|
2180
2165
|
await this.startLogging(_home)
|
|
2181
2166
|
}
|
|
2182
2167
|
} catch (e) {
|
|
2183
2168
|
console.log("start logging attempt", e)
|
|
2184
2169
|
}
|
|
2185
|
-
console.timeEnd(">>>> 3")
|
|
2186
2170
|
|
|
2187
2171
|
// determine port if port is not passed in
|
|
2188
2172
|
|
|
2189
2173
|
if (!this.port) {
|
|
2190
|
-
this.port =
|
|
2174
|
+
this.port = DEFAULT_PORT
|
|
2191
2175
|
// let platform = os.platform()
|
|
2192
2176
|
// if (platform === 'linux') {
|
|
2193
2177
|
// // on linux you are not allowed to listen on ports below 1024
|
|
@@ -2211,11 +2195,8 @@ class Server {
|
|
|
2211
2195
|
// }
|
|
2212
2196
|
}
|
|
2213
2197
|
|
|
2214
|
-
console.log("available port", this.port)
|
|
2215
|
-
|
|
2216
2198
|
let version = this.kernel.store.get("version")
|
|
2217
2199
|
let home = this.kernel.store.get("home")
|
|
2218
|
-
console.log({ home, version })
|
|
2219
2200
|
|
|
2220
2201
|
let needInitHome = false
|
|
2221
2202
|
if (home) {
|
|
@@ -2232,23 +2213,27 @@ class Server {
|
|
|
2232
2213
|
console.log(`[TRY] reset ${p}`)
|
|
2233
2214
|
await fse.remove(p)
|
|
2234
2215
|
console.log(`[DONE] reset ${p}`)
|
|
2216
|
+
|
|
2217
|
+
let p2 = path.resolve(home, "prototype")
|
|
2218
|
+
await fse.remove(p2)
|
|
2219
|
+
|
|
2235
2220
|
console.log("[TRY] Updating to the new version")
|
|
2236
2221
|
this.kernel.store.set("version", this.version.pinokiod)
|
|
2237
2222
|
console.log("[DONE] Updating to the new version")
|
|
2223
|
+
|
|
2224
|
+
|
|
2238
2225
|
}
|
|
2239
2226
|
}
|
|
2240
|
-
|
|
2241
2227
|
// initialize kernel
|
|
2242
|
-
|
|
2228
|
+
|
|
2229
|
+
|
|
2243
2230
|
await this.kernel.init({ port: this.port})
|
|
2244
|
-
|
|
2231
|
+
this.kernel.peer.start(this.kernel)
|
|
2232
|
+
|
|
2245
2233
|
|
|
2246
|
-
console.time(">>>> 5 kernel.initHome")
|
|
2247
|
-
console.log({ needInitHome })
|
|
2248
2234
|
if (needInitHome) {
|
|
2249
2235
|
await this.kernel.initHome()
|
|
2250
2236
|
}
|
|
2251
|
-
console.timeEnd(">>>> 5 kernel.initHome")
|
|
2252
2237
|
|
|
2253
2238
|
if (this.kernel.homedir) {
|
|
2254
2239
|
let ex = await this.kernel.exists(this.kernel.homedir, "ENVIRONMENT")
|
|
@@ -2350,6 +2335,7 @@ class Server {
|
|
|
2350
2335
|
|
|
2351
2336
|
if (this.kernel.homedir) {
|
|
2352
2337
|
this.app.use(express.static(this.kernel.path("web/public")))
|
|
2338
|
+
this.app.use('/prototype', express.static(this.kernel.path("prototype")))
|
|
2353
2339
|
}
|
|
2354
2340
|
this.app.use(express.static(path.resolve(__dirname, 'public')));
|
|
2355
2341
|
this.app.use("/web", express.static(path.resolve(__dirname, "..", "..", "web")))
|
|
@@ -2373,6 +2359,15 @@ class Server {
|
|
|
2373
2359
|
//let home = this.kernel.homedir
|
|
2374
2360
|
//let home = this.kernel.store.get("home")
|
|
2375
2361
|
this.app.get("/", ex(async (req, res) => {
|
|
2362
|
+
// check bin folder
|
|
2363
|
+
// let bin_path = this.kernel.path("bin/miniconda")
|
|
2364
|
+
// let bin_exists = await this.exists(bin_path)
|
|
2365
|
+
// if (!bin_exists) {
|
|
2366
|
+
// res.redirect("/setup")
|
|
2367
|
+
// return
|
|
2368
|
+
// }
|
|
2369
|
+
|
|
2370
|
+
|
|
2376
2371
|
if (req.query.mode !== "settings" && !home) {
|
|
2377
2372
|
res.redirect("/?mode=settings")
|
|
2378
2373
|
return
|
|
@@ -2477,15 +2472,30 @@ class Server {
|
|
|
2477
2472
|
})
|
|
2478
2473
|
let meta = {}
|
|
2479
2474
|
for(let folder of folders) {
|
|
2480
|
-
|
|
2481
|
-
let
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
}
|
|
2475
|
+
meta[folder] = await this.kernel.api.meta(folder)
|
|
2476
|
+
// let p = path.resolve(apipath, folder, "pinokio.js")
|
|
2477
|
+
// let pinokio = (await this.kernel.loader.load(p)).resolved
|
|
2478
|
+
// let p2 = path.resolve(apipath, folder, "pinokio_meta.json")
|
|
2479
|
+
// let pinokio2 = (await this.kernel.loader.load(p2)).resolved
|
|
2480
|
+
//
|
|
2481
|
+
// meta[folder] = Object.assign({}, pinokio, pinokio2)
|
|
2482
|
+
// meta[folder].iconpath = meta[folder].icon ? path.resolve(apipath, folder, meta[folder].icon) : null
|
|
2483
|
+
// meta[folder].icon = meta[folder].icon ? `/api/${folder}/${meta[folder].icon}?raw=true` : null
|
|
2484
|
+
// meta[folder].path = path.resolve(apipath, folder)
|
|
2485
|
+
|
|
2486
|
+
|
|
2487
|
+
// if (pinokio) {
|
|
2488
|
+
// meta[folder] = {
|
|
2489
|
+
// title: pinokio.title,
|
|
2490
|
+
// description: pinokio.description,
|
|
2491
|
+
// icon: pinokio.icon ? `/api/${folder}/${pinokio.icon}?raw=true` : null
|
|
2492
|
+
// }
|
|
2493
|
+
// }
|
|
2494
|
+
// if (pinokio2) {
|
|
2495
|
+
// if (pinokio2.title) meta[folder].title = pinokio2.title
|
|
2496
|
+
// if (pinokio2.description) meta[folder].description = pinokio2.description
|
|
2497
|
+
// if (pinokio2.icon) meta[folder].icon = pinokio2.icon
|
|
2498
|
+
// }
|
|
2489
2499
|
}
|
|
2490
2500
|
await this.render(req, res, [], meta)
|
|
2491
2501
|
// if (this.kernel.bin.all_installed) {
|
|
@@ -2522,11 +2532,504 @@ class Server {
|
|
|
2522
2532
|
// }
|
|
2523
2533
|
}))
|
|
2524
2534
|
|
|
2535
|
+
/*
|
|
2536
|
+
GET /connect => display connection options
|
|
2537
|
+
- github
|
|
2538
|
+
- x
|
|
2539
|
+
*/
|
|
2540
|
+
this.app.get("/connect", ex(async (req, res) => {
|
|
2541
|
+
res.render(`connect`, {
|
|
2542
|
+
portal: this.portal,
|
|
2543
|
+
logo: this.logo,
|
|
2544
|
+
theme: this.theme,
|
|
2545
|
+
agent: this.agent,
|
|
2546
|
+
items: [{
|
|
2547
|
+
icon: "fa-brands fa-square-x-twitter",
|
|
2548
|
+
title: "X",
|
|
2549
|
+
description: "Connect with X.com",
|
|
2550
|
+
url: "/connect/x"
|
|
2551
|
+
}, {
|
|
2552
|
+
icon: "fa-brands fa-github",
|
|
2553
|
+
title: "GitHub",
|
|
2554
|
+
description: "Connect with GitHub.com",
|
|
2555
|
+
url: "/github"
|
|
2556
|
+
}]
|
|
2557
|
+
})
|
|
2558
|
+
}))
|
|
2559
|
+
/*
|
|
2560
|
+
* GET /connect/x
|
|
2561
|
+
* GET /connect/discord
|
|
2562
|
+
*/
|
|
2563
|
+
this.app.get("/connect/:provider", ex(async (req, res) => {
|
|
2564
|
+
|
|
2565
|
+
// check if all the connect related modules are installed
|
|
2566
|
+
let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
|
|
2567
|
+
bin: this.kernel.bin.preset("connect"),
|
|
2568
|
+
})
|
|
2569
|
+
console.log({ requirements_pending, install_required })
|
|
2570
|
+
if (!requirements_pending && install_required) {
|
|
2571
|
+
res.redirect("/setup/connect?callback=/connect/" + req.params.provider)
|
|
2572
|
+
return
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
// check if router is running
|
|
2576
|
+
let router_running = false
|
|
2577
|
+
let router = this.kernel.router.published()
|
|
2578
|
+
for(let ip in router) {
|
|
2579
|
+
let domains = router[ip]
|
|
2580
|
+
if (domains.includes("pinokio.localhost")) {
|
|
2581
|
+
router_running = true
|
|
2582
|
+
break
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
console.log({ router_running })
|
|
2586
|
+
if (!router_running) {
|
|
2587
|
+
res.redirect("/setup/connect?callback=/connect/" + req.params.provider)
|
|
2588
|
+
return
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
// check if caddy is runnign properly
|
|
2592
|
+
// try https://pinokio.localhost
|
|
2593
|
+
// if it works, proceed
|
|
2594
|
+
// if not, redirect
|
|
2595
|
+
let https_running = false
|
|
2596
|
+
try {
|
|
2597
|
+
let res = await axios.get(`http://127.0.0.1:2019/config/`, {
|
|
2598
|
+
timeout: 2000
|
|
2599
|
+
})
|
|
2600
|
+
let test = /pinokio\.localhost/.test(JSON.stringify(res.data))
|
|
2601
|
+
if (test) {
|
|
2602
|
+
https_running = true
|
|
2603
|
+
}
|
|
2604
|
+
} catch (e) {
|
|
2605
|
+
console.log(e)
|
|
2606
|
+
}
|
|
2607
|
+
console.log({ https_running })
|
|
2608
|
+
if (!https_running) {
|
|
2609
|
+
res.redirect("/setup/connect?callback=/connect/" + req.params.provider)
|
|
2610
|
+
return
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
let readme = await this.kernel.connect[req.params.provider].readme()
|
|
2614
|
+
res.render(`connect/${req.params.provider}`, {
|
|
2615
|
+
portal: this.portal,
|
|
2616
|
+
logo: this.logo,
|
|
2617
|
+
theme: this.theme,
|
|
2618
|
+
agent: this.agent,
|
|
2619
|
+
id: this.kernel.connect[req.params.provider].id,
|
|
2620
|
+
readme
|
|
2621
|
+
})
|
|
2622
|
+
}))
|
|
2623
|
+
|
|
2624
|
+
/*
|
|
2625
|
+
* POST /connect/x/login => login and acquire auth token
|
|
2626
|
+
* POST /connect/x/logout => loout
|
|
2627
|
+
* POST /connect/x/keys => return the up-to-date token
|
|
2628
|
+
* POST /connect/x/api => make request
|
|
2629
|
+
*
|
|
2630
|
+
*/
|
|
2631
|
+
this.app.post("/connect/:provider/login", ex(async (req, res) => {
|
|
2632
|
+
try {
|
|
2633
|
+
console.log("params", req.params)
|
|
2634
|
+
let response = await this.kernel.connect.login(req.params.provider, req.body)
|
|
2635
|
+
res.json(response)
|
|
2636
|
+
} catch (e) {
|
|
2637
|
+
res.json({ error: e.message })
|
|
2638
|
+
}
|
|
2639
|
+
}))
|
|
2640
|
+
this.app.post("/connect/:provider/logout", ex(async (req, res) => {
|
|
2641
|
+
try {
|
|
2642
|
+
console.log("params", req.params)
|
|
2643
|
+
await this.kernel.connect.logout(req.params.provider, req.body)
|
|
2644
|
+
res.json({ success: true })
|
|
2645
|
+
} catch (e) {
|
|
2646
|
+
res.json({ error: e.message })
|
|
2647
|
+
}
|
|
2648
|
+
}))
|
|
2649
|
+
this.app.post("/connect/:provider/keys", ex(async (req, res) => {
|
|
2650
|
+
try {
|
|
2651
|
+
let response = await this.kernel.connect.keys(req.params.provider)
|
|
2652
|
+
res.json(response)
|
|
2653
|
+
} catch (e) {
|
|
2654
|
+
res.json({ error: e.message })
|
|
2655
|
+
}
|
|
2656
|
+
}))
|
|
2657
|
+
this.app.post("/connect/:provider/api/:method", this.upload.any(), ex(async (req, res) => {
|
|
2658
|
+
try {
|
|
2659
|
+
let response = await this.kernel.connect.request(req.params.provider, req.params.method, req)
|
|
2660
|
+
res.json(response)
|
|
2661
|
+
} catch (e) {
|
|
2662
|
+
console.log("ERROR", e)
|
|
2663
|
+
res.json({ error: e.message })
|
|
2664
|
+
}
|
|
2665
|
+
}))
|
|
2525
2666
|
this.app.post("/openfs", ex(async (req, res) => {
|
|
2526
|
-
Util.openfs(req.body.path, req.body.mode)
|
|
2667
|
+
//Util.openfs(req.body.path, req.body.mode)
|
|
2668
|
+
Util.openfs(req.body.path, req.body, this.kernel)
|
|
2527
2669
|
res.json({ success: true })
|
|
2528
2670
|
}))
|
|
2529
|
-
this.app.
|
|
2671
|
+
this.app.post("/keys", ex(async (req, res) => {
|
|
2672
|
+
let p = this.kernel.path("key.json")
|
|
2673
|
+
let keys = (await this.kernel.loader.load(p)).resolved
|
|
2674
|
+
console.log("update", req.body)
|
|
2675
|
+
for(let host in req.body) {
|
|
2676
|
+
let updated = req.body[host]
|
|
2677
|
+
for(let indexStr in updated) {
|
|
2678
|
+
let index = parseInt(indexStr)
|
|
2679
|
+
keys[host][index] = updated[indexStr]
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
await fs.promises.writeFile(p, JSON.stringify(keys, null, 2))
|
|
2683
|
+
res.json({ success: true })
|
|
2684
|
+
}))
|
|
2685
|
+
this.app.get("/keys", ex(async (req, res) => {
|
|
2686
|
+
let p = this.kernel.path("key.json")
|
|
2687
|
+
let keys = (await this.kernel.loader.load(p)).resolved
|
|
2688
|
+
console.log({ keys })
|
|
2689
|
+
let items = []
|
|
2690
|
+
if (keys) {
|
|
2691
|
+
let sorted_keys = Object.keys(keys)
|
|
2692
|
+
sorted_keys.sort((a, b) => { return a > b })
|
|
2693
|
+
for(let key of sorted_keys) {
|
|
2694
|
+
console.log({ key })
|
|
2695
|
+
items.push({
|
|
2696
|
+
host: key,
|
|
2697
|
+
vals: keys[key]
|
|
2698
|
+
})
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
res.render("keys", {
|
|
2702
|
+
filepath: p,
|
|
2703
|
+
theme: this.theme,
|
|
2704
|
+
agent: this.agent,
|
|
2705
|
+
items
|
|
2706
|
+
})
|
|
2707
|
+
}))
|
|
2708
|
+
this.app.get("/docs", ex(async (req, res) => {
|
|
2709
|
+
let url = req.query.url
|
|
2710
|
+
const possiblePaths = [
|
|
2711
|
+
'/openapi.json',
|
|
2712
|
+
'/swagger.json',
|
|
2713
|
+
'/v1/openapi.json',
|
|
2714
|
+
'/v1/swagger.json',
|
|
2715
|
+
'/docs/openapi.json',
|
|
2716
|
+
'/api-docs',
|
|
2717
|
+
'/api-docs.json',
|
|
2718
|
+
];
|
|
2719
|
+
let selected = null
|
|
2720
|
+
if (req.query.url) {
|
|
2721
|
+
const localHosts = ['localhost', '127.0.0.1', '::1'];
|
|
2722
|
+
const urlObj = new URL(req.query.url)
|
|
2723
|
+
const baseOrigins = [urlObj.origin];
|
|
2724
|
+
if (urlObj.hostname === 'localhost' || urlObj.hostname === '::1' || urlObj.hostname.startsWith('127.')) {
|
|
2725
|
+
for (const host of localHosts) {
|
|
2726
|
+
const origin = urlObj.origin.replace(urlObj.hostname, host);
|
|
2727
|
+
if (!baseOrigins.includes(origin)) {
|
|
2728
|
+
baseOrigins.push(origin);
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
for (const origin of baseOrigins) {
|
|
2734
|
+
for (const possiblePath of possiblePaths) {
|
|
2735
|
+
try {
|
|
2736
|
+
const url = new URL(possiblePath, origin).href;
|
|
2737
|
+
const res = await axios.get(url, { timeout: 500 });
|
|
2738
|
+
const contentType = res.headers['content-type'];
|
|
2739
|
+
if (contentType?.includes('application/json')) {
|
|
2740
|
+
const json = res.data;
|
|
2741
|
+
if (json.openapi || json.swagger) {
|
|
2742
|
+
selected = json
|
|
2743
|
+
break
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
} catch (e) {
|
|
2747
|
+
console.log("error", e)
|
|
2748
|
+
// ignore errors
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
if (selected) break
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
let type = "redoc" // "swaggerui"
|
|
2755
|
+
if (req.query.type) {
|
|
2756
|
+
type = req.query.type
|
|
2757
|
+
}
|
|
2758
|
+
if (selected) {
|
|
2759
|
+
res.render(type, {
|
|
2760
|
+
spec: JSON.stringify(selected)
|
|
2761
|
+
})
|
|
2762
|
+
} else {
|
|
2763
|
+
res.render(type, {
|
|
2764
|
+
spec: null
|
|
2765
|
+
})
|
|
2766
|
+
}
|
|
2767
|
+
}))
|
|
2768
|
+
this.app.get("/github", ex(async (req, res) => {
|
|
2769
|
+
let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
|
|
2770
|
+
bin: this.kernel.bin.preset("connect"),
|
|
2771
|
+
})
|
|
2772
|
+
if (!requirements_pending && install_required) {
|
|
2773
|
+
res.redirect("/setup/connect?callback=/github")
|
|
2774
|
+
return
|
|
2775
|
+
}
|
|
2776
|
+
let md = await fs.promises.readFile(path.resolve(__dirname, "..", "kernel/connect/providers/github/README.md"), "utf8")
|
|
2777
|
+
let readme = marked.parse(md)
|
|
2778
|
+
res.render("github", {
|
|
2779
|
+
readme,
|
|
2780
|
+
portal: this.portal,
|
|
2781
|
+
logo: this.logo,
|
|
2782
|
+
theme: this.theme,
|
|
2783
|
+
agent: this.agent,
|
|
2784
|
+
items: [{
|
|
2785
|
+
icon: "fa-solid fa-key",
|
|
2786
|
+
title: "Login",
|
|
2787
|
+
description: "Log into Github",
|
|
2788
|
+
url: "/github/login"
|
|
2789
|
+
}, {
|
|
2790
|
+
icon: "fa-solid fa-check",
|
|
2791
|
+
title: "Status",
|
|
2792
|
+
description: "Check Github login status",
|
|
2793
|
+
url: "/github/status"
|
|
2794
|
+
}, {
|
|
2795
|
+
icon: "fa-solid fa-circle-xmark",
|
|
2796
|
+
title: "Logout",
|
|
2797
|
+
description: "Log out of Github",
|
|
2798
|
+
url: "/github/logout"
|
|
2799
|
+
}]
|
|
2800
|
+
})
|
|
2801
|
+
}))
|
|
2802
|
+
this.app.get("/github/status", ex(async (req, res) => {
|
|
2803
|
+
let id = "gh_status"
|
|
2804
|
+
let params = new URLSearchParams()
|
|
2805
|
+
let message = "gh auth status"
|
|
2806
|
+
params.set("message", encodeURIComponent(message))
|
|
2807
|
+
params.set("path", this.kernel.homedir)
|
|
2808
|
+
// params.set("kill", "/Logged in/i")
|
|
2809
|
+
params.set("kill_message", "Click to return home")
|
|
2810
|
+
params.set("callback", encodeURIComponent("/github"))
|
|
2811
|
+
params.set("id", id)
|
|
2812
|
+
let url = `/shell/${id}?${params.toString()}`
|
|
2813
|
+
res.redirect(url)
|
|
2814
|
+
}))
|
|
2815
|
+
this.app.get("/github/logout", ex(async (req, res) => {
|
|
2816
|
+
let id = "gh_logout"
|
|
2817
|
+
let params = new URLSearchParams()
|
|
2818
|
+
let message = "gh auth logout"
|
|
2819
|
+
params.set("message", encodeURIComponent(message))
|
|
2820
|
+
params.set("path", this.kernel.homedir)
|
|
2821
|
+
// params.set("kill", "/Logged in/i")
|
|
2822
|
+
params.set("kill_message", "Click to return home")
|
|
2823
|
+
params.set("callback", encodeURIComponent("/github"))
|
|
2824
|
+
params.set("id", id)
|
|
2825
|
+
let url = `/shell/${id}?${params.toString()}`
|
|
2826
|
+
res.redirect(url)
|
|
2827
|
+
}))
|
|
2828
|
+
this.app.get("/github/login", ex(async (req, res) => {
|
|
2829
|
+
let id = "gh_login"
|
|
2830
|
+
let params = new URLSearchParams()
|
|
2831
|
+
let delimiter
|
|
2832
|
+
if (this.kernel.platform === "win32") {
|
|
2833
|
+
delimiter = " && "; // must use &&. & doesn't necessariliy wait until the curruent command finishes
|
|
2834
|
+
} else {
|
|
2835
|
+
delimiter = " ; ";
|
|
2836
|
+
}
|
|
2837
|
+
let message = [
|
|
2838
|
+
"gh auth setup-git --hostname github.com --force",
|
|
2839
|
+
"gh auth login --web --git-protocol https"
|
|
2840
|
+
].join(delimiter)
|
|
2841
|
+
params.set("message", encodeURIComponent(message))
|
|
2842
|
+
params.set("input", true)
|
|
2843
|
+
params.set("path", this.kernel.homedir)
|
|
2844
|
+
params.set("kill", "/Logged in/i")
|
|
2845
|
+
params.set("kill_message", "Your Github account is now connected.")
|
|
2846
|
+
params.set("callback", encodeURIComponent("/github"))
|
|
2847
|
+
params.set("id", id)
|
|
2848
|
+
let url = `/shell/${id}?${params.toString()}`
|
|
2849
|
+
res.redirect(url)
|
|
2850
|
+
}))
|
|
2851
|
+
this.app.get("/shell/:id", ex(async (req, res) => {
|
|
2852
|
+
/*
|
|
2853
|
+
req.query := {
|
|
2854
|
+
path (required), // api, bin, prototype, network, api/
|
|
2855
|
+
message (optional), // if not specified, start an empty shell
|
|
2856
|
+
venv,
|
|
2857
|
+
callback,
|
|
2858
|
+
kill, // regex for killing
|
|
2859
|
+
on.<regex1>: <key>,
|
|
2860
|
+
on.<regex2>: <key>,
|
|
2861
|
+
env.<key1>,
|
|
2862
|
+
env.<key2>,
|
|
2863
|
+
...
|
|
2864
|
+
}
|
|
2865
|
+
*/
|
|
2866
|
+
|
|
2867
|
+
console.log("SHELLS", this.kernel.shell.shells.map((s) => { return { id: s.id, message: s.params.message } }))
|
|
2868
|
+
// create a new term from cwd
|
|
2869
|
+
let id = decodeURIComponent(req.params.id)
|
|
2870
|
+
let cwd = this.kernel.path(this.kernel.api.filePath(decodeURIComponent(req.query.path)))
|
|
2871
|
+
let message = req.query.message ? decodeURIComponent(req.query.message) : null
|
|
2872
|
+
//let message = req.query.message ? req.query.message : null
|
|
2873
|
+
console.log({ message })
|
|
2874
|
+
let venv = req.query.venv ? decodeURIComponent(req.query.venv) : null
|
|
2875
|
+
let input = req.query.input ? true : false
|
|
2876
|
+
let callback = req.query.callback ? decodeURIComponent(req.query.callback) : null
|
|
2877
|
+
let kill_message = req.query.kill_message ? decodeURIComponent(req.query.kill_message) : null
|
|
2878
|
+
let done_message = req.query.done_message ? decodeURIComponent(req.query.done_message) : null
|
|
2879
|
+
let kill = req.query.kill ? decodeURIComponent(req.query.kill) : null
|
|
2880
|
+
let done = req.query.done ? decodeURIComponent(req.query.done) : null
|
|
2881
|
+
let env = {}
|
|
2882
|
+
for(let env_key in req.query) {
|
|
2883
|
+
if (env_key.startsWith("env.")) {
|
|
2884
|
+
let chunks = env_key.split(".")
|
|
2885
|
+
let key = chunks.slice(1).join(".")
|
|
2886
|
+
env[key] = req.query[env_key]
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
let conda = {}
|
|
2890
|
+
let conda_exists = false
|
|
2891
|
+
for(let conda_key in req.query) {
|
|
2892
|
+
if (conda_key.startsWith("conda.")) {
|
|
2893
|
+
let chunks = conda_key.split(".")
|
|
2894
|
+
let key = chunks.slice(1).join(".")
|
|
2895
|
+
conda[key] = req.query[conda_key]
|
|
2896
|
+
conda_exists = true
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
// let pattern = {}
|
|
2900
|
+
// for(let pattern_key in req.query) {
|
|
2901
|
+
// if (pattern_key.startsWith("pattern.")) {
|
|
2902
|
+
// let chunks = pattern_key.split(".")
|
|
2903
|
+
// let key = chunks.slice(1).join(".")
|
|
2904
|
+
// pattern[key] = req.query[pattern_key]
|
|
2905
|
+
// }
|
|
2906
|
+
// }
|
|
2907
|
+
|
|
2908
|
+
let shell = this.kernel.shell.get(id)
|
|
2909
|
+
console.log({ shell })
|
|
2910
|
+
res.render("shell", {
|
|
2911
|
+
theme: this.theme,
|
|
2912
|
+
agent: this.agent,
|
|
2913
|
+
id,
|
|
2914
|
+
cwd,
|
|
2915
|
+
message,
|
|
2916
|
+
venv,
|
|
2917
|
+
conda: (conda_exists ? conda: null),
|
|
2918
|
+
env,
|
|
2919
|
+
// pattern,
|
|
2920
|
+
input,
|
|
2921
|
+
kill,
|
|
2922
|
+
kill_message,
|
|
2923
|
+
done,
|
|
2924
|
+
done_message,
|
|
2925
|
+
callback,
|
|
2926
|
+
running: (shell ? true : false)
|
|
2927
|
+
})
|
|
2928
|
+
}))
|
|
2929
|
+
// this.app.get("/terminal/:api/:id", ex(async (req, res) => {
|
|
2930
|
+
// res.render("shell", {
|
|
2931
|
+
// theme: this.theme,
|
|
2932
|
+
// agent: this.agent,
|
|
2933
|
+
// cwd: this.kernel.path("api/" + req.params.api),
|
|
2934
|
+
// id: req.params.id
|
|
2935
|
+
// })
|
|
2936
|
+
// }))
|
|
2937
|
+
this.app.get("/peer_check", ex(async (req, res) => {
|
|
2938
|
+
if (this.kernel.peer.refreshing) {
|
|
2939
|
+
res.json({ updated: false })
|
|
2940
|
+
} else {
|
|
2941
|
+
let list = this.getPeerInfo()
|
|
2942
|
+
if (JSON.stringify(this.last_list) !== JSON.stringify(list)) {
|
|
2943
|
+
this.last_list = list
|
|
2944
|
+
res.json({ updated: true })
|
|
2945
|
+
} else {
|
|
2946
|
+
res.json({ updated: false })
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
}))
|
|
2950
|
+
this.app.get("/setup", ex(async (req, res) => {
|
|
2951
|
+
let items = []
|
|
2952
|
+
for(let id in Setup) {
|
|
2953
|
+
let item = Setup[id](this.kernel)
|
|
2954
|
+
items.push({
|
|
2955
|
+
id,
|
|
2956
|
+
...item
|
|
2957
|
+
})
|
|
2958
|
+
}
|
|
2959
|
+
res.render("setup_home", {
|
|
2960
|
+
items,
|
|
2961
|
+
portal: this.portal,
|
|
2962
|
+
logo: this.logo,
|
|
2963
|
+
theme: this.theme,
|
|
2964
|
+
agent: this.agent,
|
|
2965
|
+
})
|
|
2966
|
+
}))
|
|
2967
|
+
this.app.get("/setup/:mode", ex(async (req, res) => {
|
|
2968
|
+
/*
|
|
2969
|
+
1. mode:ai => all
|
|
2970
|
+
2. mode:coding => conda, nodejs, git
|
|
2971
|
+
3. mode:network => conda, git, caddy
|
|
2972
|
+
4. mode:connect => conda, git, caddy
|
|
2973
|
+
*/
|
|
2974
|
+
|
|
2975
|
+
console.log("setup mode", req.params.mode)
|
|
2976
|
+
|
|
2977
|
+
let bin = this.kernel.bin.preset(req.params.mode)
|
|
2978
|
+
|
|
2979
|
+
console.log("__ bin", bin)
|
|
2980
|
+
|
|
2981
|
+
let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
|
|
2982
|
+
bin
|
|
2983
|
+
})
|
|
2984
|
+
// set dependencies for conda
|
|
2985
|
+
for(let i=0; i<requirements.length; i++) {
|
|
2986
|
+
let r = requirements[i]
|
|
2987
|
+
if (r.name === "conda") {
|
|
2988
|
+
requirements[i].dependencies = bin.conda_requirements
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
console.log("RE", JSON.stringify(requirements, null, 2))
|
|
2992
|
+
let current = req.query.callback || req.originalUrl
|
|
2993
|
+
res.render("setup", {
|
|
2994
|
+
error,
|
|
2995
|
+
current,
|
|
2996
|
+
install_required,
|
|
2997
|
+
requirements,
|
|
2998
|
+
requirements_pending,
|
|
2999
|
+
logo: this.logo,
|
|
3000
|
+
theme: this.theme,
|
|
3001
|
+
agent: this.agent,
|
|
3002
|
+
})
|
|
3003
|
+
}))
|
|
3004
|
+
this.app.post("/network/reset", ex(async (req, res) => {
|
|
3005
|
+
let caddy_path = this.kernel.path("cache/XDG_DATA_HOME/caddy")
|
|
3006
|
+
await rimraf(caddy_path)
|
|
3007
|
+
res.json({ success: true })
|
|
3008
|
+
}))
|
|
3009
|
+
this.app.get("/network", ex(async (req, res) => {
|
|
3010
|
+
let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
|
|
3011
|
+
bin: this.kernel.bin.preset("network"),
|
|
3012
|
+
})
|
|
3013
|
+
console.log({ requirements, install_required, requirements_pending })
|
|
3014
|
+
|
|
3015
|
+
if (!requirements_pending && install_required) {
|
|
3016
|
+
res.redirect("/setup/network?callback=/network")
|
|
3017
|
+
return
|
|
3018
|
+
}
|
|
3019
|
+
|
|
3020
|
+
let list = this.getPeerInfo()
|
|
3021
|
+
|
|
3022
|
+
|
|
3023
|
+
let peers = []
|
|
3024
|
+
for(let host in this.kernel.peer.info) {
|
|
3025
|
+
let peer_info = this.kernel.peer.info[host]
|
|
3026
|
+
peers.push({
|
|
3027
|
+
host,
|
|
3028
|
+
name: peer_info.name,
|
|
3029
|
+
domain: `https://pinokio.${peer_info.name}.localhost`,
|
|
3030
|
+
router: `https://pinokio.${peer_info.name}.localhost/proxy`
|
|
3031
|
+
})
|
|
3032
|
+
}
|
|
2530
3033
|
let live_proxies = this.kernel.api.proxies["/proxy"]
|
|
2531
3034
|
if (!live_proxies) live_proxies = []
|
|
2532
3035
|
let proxies = []
|
|
@@ -2581,18 +3084,40 @@ class Server {
|
|
|
2581
3084
|
})
|
|
2582
3085
|
let apps = []
|
|
2583
3086
|
for(let folder of folders) {
|
|
2584
|
-
let
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
3087
|
+
let meta = await this.kernel.api.meta(folder)
|
|
3088
|
+
// meta.link = `/pinokio/browser/${folder}/browse#n1`,
|
|
3089
|
+
// meta.icon = meta.icon ? `/api/${folder}/${meta.icon}?raw=true` : null
|
|
3090
|
+
// meta.name = meta.title
|
|
3091
|
+
apps.push(meta)
|
|
3092
|
+
//let p = path.resolve(apipath, folder, "pinokio.js")
|
|
3093
|
+
//let pinokio = (await this.kernel.loader.load(p)).resolved
|
|
3094
|
+
//let p2 = path.resolve(apipath, folder, "pinokio_meta.json")
|
|
3095
|
+
//let pinokio2 = (await this.kernel.loader.load(p2)).resolved
|
|
3096
|
+
//if (pinokio) {
|
|
3097
|
+
// let cc = Object.assign({} , pinokio , pinokio2);
|
|
3098
|
+
// cc.link = `/pinokio/browser/${folder}/browse#n1`,
|
|
3099
|
+
// cc.icon = cc.icon ? `/api/${folder}/${cc.icon}?raw=true` : null
|
|
3100
|
+
// cc.name = cc.title
|
|
3101
|
+
// apps.push(cc)
|
|
3102
|
+
//}
|
|
2594
3103
|
}
|
|
2595
|
-
|
|
3104
|
+
|
|
3105
|
+
|
|
3106
|
+
|
|
3107
|
+
res.render("network", {
|
|
3108
|
+
docs: this.docs,
|
|
3109
|
+
portal: this.portal,
|
|
3110
|
+
install: this.install,
|
|
3111
|
+
current_host: this.kernel.peer.host,
|
|
3112
|
+
peers,
|
|
3113
|
+
list,
|
|
3114
|
+
name: this.kernel.peer.name,
|
|
3115
|
+
https_active: this.kernel.router.active,
|
|
3116
|
+
peer_active: this.kernel.peer.active,
|
|
3117
|
+
port_mapping: this.kernel.router.port_mapping,
|
|
3118
|
+
// port_mapping: this.kernel.caddy.port_mapping,
|
|
3119
|
+
// ip_mapping: this.kernel.caddy.ip_mapping,
|
|
3120
|
+
lan: this.kernel.router.local_network_mapping,
|
|
2596
3121
|
agent: this.agent,
|
|
2597
3122
|
theme: this.theme,
|
|
2598
3123
|
items: proxies,
|
|
@@ -2607,6 +3132,55 @@ class Server {
|
|
|
2607
3132
|
let str = await fs.promises.readFile(req.query.logpath, "utf8")
|
|
2608
3133
|
res.send(str)
|
|
2609
3134
|
}))
|
|
3135
|
+
this.app.post("/mkdir", ex(async (req, res) => {
|
|
3136
|
+
let folder = req.body.folder
|
|
3137
|
+
let folder_path = path.resolve(this.kernel.api.userdir, req.body.folder)
|
|
3138
|
+
try {
|
|
3139
|
+
// mkdir
|
|
3140
|
+
await fs.promises.mkdir(folder_path)
|
|
3141
|
+
|
|
3142
|
+
// create basic pinokio.json
|
|
3143
|
+
|
|
3144
|
+
// add default icon
|
|
3145
|
+
|
|
3146
|
+
let default_icon_path = path.resolve(__dirname, "public/pinokio-black.png")
|
|
3147
|
+
let icon_path = path.resolve(folder_path, "icon.png")
|
|
3148
|
+
await fs.promises.cp(default_icon_path, icon_path)
|
|
3149
|
+
|
|
3150
|
+
|
|
3151
|
+
// write title/description to pinokio.json
|
|
3152
|
+
let meta_path = path.resolve(folder_path, "pinokio.json")
|
|
3153
|
+
let meta = {
|
|
3154
|
+
title: "No title",
|
|
3155
|
+
description: "",
|
|
3156
|
+
icon: "icon.png"
|
|
3157
|
+
}
|
|
3158
|
+
console.log({ folder_path, default_icon_path, icon_path, meta_path, meta })
|
|
3159
|
+
await fs.promises.writeFile(meta_path, JSON.stringify(meta, null, 2))
|
|
3160
|
+
|
|
3161
|
+
res.json({
|
|
3162
|
+
success: "/pinokio/browser/"+folder
|
|
3163
|
+
})
|
|
3164
|
+
} catch (e) {
|
|
3165
|
+
res.json({
|
|
3166
|
+
error: e.message
|
|
3167
|
+
})
|
|
3168
|
+
}
|
|
3169
|
+
}))
|
|
3170
|
+
this.app.post("/copy", ex(async (req, res) => {
|
|
3171
|
+
let src_path = path.resolve(this.kernel.api.userdir, req.body.src)
|
|
3172
|
+
let dest_path = path.resolve(this.kernel.api.userdir, req.body.dest)
|
|
3173
|
+
try {
|
|
3174
|
+
await fs.promises.cp(src_path, dest_path, { recursive: true })
|
|
3175
|
+
res.json({
|
|
3176
|
+
success: "/pinokio/browser/"+ req.body.dest + "/browse"
|
|
3177
|
+
})
|
|
3178
|
+
} catch (e) {
|
|
3179
|
+
res.json({
|
|
3180
|
+
error: e.message
|
|
3181
|
+
})
|
|
3182
|
+
}
|
|
3183
|
+
}))
|
|
2610
3184
|
this.app.post("/proxy", ex(async (req, res) => {
|
|
2611
3185
|
/*
|
|
2612
3186
|
req.body := {
|
|
@@ -2695,10 +3269,133 @@ class Server {
|
|
|
2695
3269
|
res.json({ error: "type must be 'local' or 'cloudflare'" })
|
|
2696
3270
|
}
|
|
2697
3271
|
}))
|
|
3272
|
+
this.app.get("/prototype/run/*", ex(async (req, res) => {
|
|
3273
|
+
let pathComponents = req.params[0].split("/").concat("pinokio.js")
|
|
3274
|
+
req.base = this.kernel.path("prototype")
|
|
3275
|
+
req.query.callback = "/pinokio/browser/" + req.query.api_path
|
|
3276
|
+
req.query.cwd = this.kernel.path("api", req.query.api_path)
|
|
3277
|
+
await this.render(req, res, pathComponents, null)
|
|
3278
|
+
}))
|
|
3279
|
+
this.app.get("/prototype/show/*", ex(async (req, res) => {
|
|
3280
|
+
let name = req.params[0].split("/").filter((x) => { return x }).join("/")
|
|
3281
|
+
let icon = this.kernel.proto.kv[name].icon
|
|
3282
|
+
let title = this.kernel.proto.kv[name].title
|
|
3283
|
+
let description = this.kernel.proto.kv[name].description
|
|
3284
|
+
let readme = await this.kernel.proto.readme(name)
|
|
3285
|
+
res.render("prototype/show", {
|
|
3286
|
+
icon,
|
|
3287
|
+
title,
|
|
3288
|
+
description,
|
|
3289
|
+
name,
|
|
3290
|
+
prototype_path: name,
|
|
3291
|
+
portal: this.portal,
|
|
3292
|
+
readme,
|
|
3293
|
+
items: this.kernel.proto.items,
|
|
3294
|
+
logo: this.logo,
|
|
3295
|
+
theme: this.theme,
|
|
3296
|
+
agent: this.agent,
|
|
3297
|
+
kernel: this.kernel,
|
|
3298
|
+
})
|
|
3299
|
+
}))
|
|
3300
|
+
this.app.get("/prototype", ex(async (req, res) => {
|
|
3301
|
+
res.render("prototype/index", {
|
|
3302
|
+
portal: this.portal,
|
|
3303
|
+
items: this.kernel.proto.items,
|
|
3304
|
+
logo: this.logo,
|
|
3305
|
+
theme: this.theme,
|
|
3306
|
+
agent: this.agent,
|
|
3307
|
+
kernel: this.kernel,
|
|
3308
|
+
})
|
|
3309
|
+
}))
|
|
3310
|
+
this.app.post("/prototype", this.upload.any(), ex(async (req, res) => {
|
|
3311
|
+
console.log("POST /prototype")
|
|
3312
|
+
try {
|
|
3313
|
+
/*
|
|
3314
|
+
{
|
|
3315
|
+
title,
|
|
3316
|
+
description,
|
|
3317
|
+
path,
|
|
3318
|
+
id
|
|
3319
|
+
}
|
|
3320
|
+
*/
|
|
3321
|
+
console.log("req.body", req.body)
|
|
3322
|
+
let formData = req.body
|
|
3323
|
+
for(let key in req.files) {
|
|
3324
|
+
let file = req.files[key]
|
|
3325
|
+
formData[file.fieldname] = file.buffer
|
|
3326
|
+
}
|
|
3327
|
+
console.log({ formData })
|
|
3328
|
+
|
|
3329
|
+
|
|
3330
|
+
// check if the path exists. if it does, return error
|
|
3331
|
+
let api_path = this.kernel.path("api", formData.path)
|
|
3332
|
+
let e = await this.exists(api_path)
|
|
3333
|
+
if (e) {
|
|
3334
|
+
console.log("e", e)
|
|
3335
|
+
console.log("e.message", e.message)
|
|
3336
|
+
res.status(500).json({ error: `The path ${api_path} already exists` })
|
|
3337
|
+
} else {
|
|
3338
|
+
await this.createMeta(formData)
|
|
3339
|
+
|
|
3340
|
+
// run
|
|
3341
|
+
|
|
3342
|
+
res.json({ success: true })
|
|
3343
|
+
}
|
|
3344
|
+
} catch (e) {
|
|
3345
|
+
console.log("e", e)
|
|
3346
|
+
console.log("e.message", e.message)
|
|
3347
|
+
res.status(500).json({ error: e.message })
|
|
3348
|
+
}
|
|
3349
|
+
}))
|
|
3350
|
+
this.app.post("/new", this.upload.any(), ex(async (req, res) => {
|
|
3351
|
+
try {
|
|
3352
|
+
/*
|
|
3353
|
+
{
|
|
3354
|
+
title,
|
|
3355
|
+
description,
|
|
3356
|
+
path,
|
|
3357
|
+
id
|
|
3358
|
+
}
|
|
3359
|
+
*/
|
|
3360
|
+
console.log("req.body", req.body)
|
|
3361
|
+
let formData = req.body
|
|
3362
|
+
for(let key in req.files) {
|
|
3363
|
+
let file = req.files[key]
|
|
3364
|
+
formData[file.fieldname] = file.buffer
|
|
3365
|
+
}
|
|
3366
|
+
console.log({ formData })
|
|
3367
|
+
|
|
3368
|
+
|
|
3369
|
+
// check if the path exists. if it does, return error
|
|
3370
|
+
let api_path = this.kernel.path("api", formData.path)
|
|
3371
|
+
let e = await this.exists(api_path)
|
|
3372
|
+
if (e) {
|
|
3373
|
+
console.log("e", e)
|
|
3374
|
+
console.log("e.message", e.message)
|
|
3375
|
+
res.status(500).json({ error: `The path ${api_path} already exists` })
|
|
3376
|
+
} else {
|
|
3377
|
+
await this.createMeta(formData)
|
|
3378
|
+
res.json({ success: true })
|
|
3379
|
+
}
|
|
3380
|
+
} catch (e) {
|
|
3381
|
+
console.log("e", e)
|
|
3382
|
+
console.log("e.message", e.message)
|
|
3383
|
+
res.status(500).json({ error: e.message })
|
|
3384
|
+
}
|
|
3385
|
+
}))
|
|
2698
3386
|
this.app.post("/env", ex(async (req, res) => {
|
|
2699
3387
|
let fullpath = path.resolve(this.kernel.homedir, req.body.filepath, "ENVIRONMENT")
|
|
2700
3388
|
let updated = req.body.vals
|
|
3389
|
+
let hosts = req.body.hosts
|
|
2701
3390
|
await Util.update_env(fullpath, updated)
|
|
3391
|
+
// for all environment variables that have hosts, save the key as well
|
|
3392
|
+
// hosts := { env_key: host }
|
|
3393
|
+
for(let env in hosts) {
|
|
3394
|
+
let host = hosts[env]
|
|
3395
|
+
let val = updated[env]
|
|
3396
|
+
console.log({ hosts, updated, host, val })
|
|
3397
|
+
await this.kernel.kv.set(host.value, val, host.index)
|
|
3398
|
+
}
|
|
2702
3399
|
res.json({})
|
|
2703
3400
|
}))
|
|
2704
3401
|
this.app.get("/env", ex(async (req, res) => {
|
|
@@ -2744,6 +3441,8 @@ class Server {
|
|
|
2744
3441
|
}
|
|
2745
3442
|
if (config.icon) {
|
|
2746
3443
|
config.icon = `/${env_path}/${config.icon}?raw=true`
|
|
3444
|
+
} else {
|
|
3445
|
+
config.icon = "/pinokio-black.png"
|
|
2747
3446
|
}
|
|
2748
3447
|
|
|
2749
3448
|
let name
|
|
@@ -2804,6 +3503,8 @@ class Server {
|
|
|
2804
3503
|
config.pre.forEach((item) => {
|
|
2805
3504
|
if (item.icon) {
|
|
2806
3505
|
item.icon = `/api/${req.params.name}/${item.icon}?raw=true`
|
|
3506
|
+
} else {
|
|
3507
|
+
item.icon = "/pinokio-black.png"
|
|
2807
3508
|
}
|
|
2808
3509
|
if (!item.href.startsWith("http")) {
|
|
2809
3510
|
item.href = path.resolve(this.kernel.homedir, "api", req.params.name, item.href)
|
|
@@ -2885,7 +3586,6 @@ class Server {
|
|
|
2885
3586
|
}
|
|
2886
3587
|
}
|
|
2887
3588
|
}
|
|
2888
|
-
console.log({ cloudflare_links, local_links })
|
|
2889
3589
|
res.render("share_editor", {
|
|
2890
3590
|
cloudflare_links,
|
|
2891
3591
|
local_links,
|
|
@@ -2914,8 +3614,13 @@ class Server {
|
|
|
2914
3614
|
}))
|
|
2915
3615
|
this.app.get("/du/:name", ex(async (req, res) => {
|
|
2916
3616
|
let p = this.kernel.path("api", req.params.name)
|
|
2917
|
-
|
|
2918
|
-
|
|
3617
|
+
try {
|
|
3618
|
+
let d1 = await Util.du(p)
|
|
3619
|
+
res.json({ du: d1 })
|
|
3620
|
+
} catch (e) {
|
|
3621
|
+
console.log("disk usage error", e)
|
|
3622
|
+
res.json({ du: 0 })
|
|
3623
|
+
}
|
|
2919
3624
|
}))
|
|
2920
3625
|
this.app.get("/edit/*", ex(async (req, res) => {
|
|
2921
3626
|
let pathComponents = req.params[0].split("/")
|
|
@@ -2959,7 +3664,13 @@ class Server {
|
|
|
2959
3664
|
}))
|
|
2960
3665
|
this.app.get("/api/*", ex(async (req, res) => {
|
|
2961
3666
|
let pathComponents = req.params[0].split("/")
|
|
2962
|
-
if (req.query && '
|
|
3667
|
+
if (req.query && 'command' in req.query) {
|
|
3668
|
+
let full_filepath = this.kernel.path("api", ...pathComponents)
|
|
3669
|
+
Util.openfs(full_filepath, { command: req.query.command })
|
|
3670
|
+
res.render("fs", {
|
|
3671
|
+
path: full_filepath
|
|
3672
|
+
})
|
|
3673
|
+
} else if (req.query && 'fs' in req.query) {
|
|
2963
3674
|
// open in file system
|
|
2964
3675
|
let full_filepath = this.kernel.path("api", ...pathComponents)
|
|
2965
3676
|
if (req.query.fs) {
|
|
@@ -2985,6 +3696,20 @@ class Server {
|
|
|
2985
3696
|
}
|
|
2986
3697
|
}
|
|
2987
3698
|
}))
|
|
3699
|
+
this.app.get("/pinokio/dynamic/:name", ex(async (req, res) => {
|
|
3700
|
+
let plugin = await this.getPlugin(req.params.name)
|
|
3701
|
+
let plugin_menu = null
|
|
3702
|
+
let html = ""
|
|
3703
|
+
if (plugin && plugin.menu && Array.isArray(plugin.menu)) {
|
|
3704
|
+
plugin_menu = plugin.menu
|
|
3705
|
+
html = await new Promise((resolve, reject) => {
|
|
3706
|
+
ejs.renderFile(path.resolve(__dirname, "views/partials/dynamic.ejs"), { dynamic: plugin.menu }, (err, html) => {
|
|
3707
|
+
resolve(html)
|
|
3708
|
+
})
|
|
3709
|
+
})
|
|
3710
|
+
}
|
|
3711
|
+
res.send(html)
|
|
3712
|
+
}))
|
|
2988
3713
|
this.app.get("/pinokio/sidebar/:name", ex(async (req, res) => {
|
|
2989
3714
|
let name = req.params.name
|
|
2990
3715
|
let app_path = this.kernel.path("api", name, "pinokio.js")
|
|
@@ -3007,7 +3732,10 @@ class Server {
|
|
|
3007
3732
|
let files = await fs.promises.readdir(p, { withFileTypes: true })
|
|
3008
3733
|
files = files.filter((file) => {
|
|
3009
3734
|
return file.name.endsWith(".json") || file.name.endsWith(".js")
|
|
3735
|
+
}).filter((file) => {
|
|
3736
|
+
return file.name !== "pinokio.js" && file.name !== "pinokio.json" && file.name !== "pinokio_meta.json"
|
|
3010
3737
|
})
|
|
3738
|
+
console.log("files", files)
|
|
3011
3739
|
config = {
|
|
3012
3740
|
title: name,
|
|
3013
3741
|
menu: files.map((file) => {
|
|
@@ -3039,6 +3767,27 @@ class Server {
|
|
|
3039
3767
|
*/
|
|
3040
3768
|
|
|
3041
3769
|
}))
|
|
3770
|
+
this.app.post("/pinokio/peer/refresh", ex(async (req, res) => {
|
|
3771
|
+
// refresh and broadcast
|
|
3772
|
+
console.log("/peer/refresh")
|
|
3773
|
+
await this.kernel.refresh()
|
|
3774
|
+
res.json({ success: true })
|
|
3775
|
+
}))
|
|
3776
|
+
this.app.get("/pinokio/peer", ex(async (req, res) => {
|
|
3777
|
+
// await this.kernel.refresh()
|
|
3778
|
+
res.json({
|
|
3779
|
+
home: this.kernel.homedir,
|
|
3780
|
+
arch: this.kernel.arch,
|
|
3781
|
+
platform: this.kernel.platform,
|
|
3782
|
+
name: this.kernel.peer.name,
|
|
3783
|
+
host: this.kernel.peer.host,
|
|
3784
|
+
port_mapping: this.kernel.router.port_mapping,
|
|
3785
|
+
//router: this.kernel.router.info(),
|
|
3786
|
+
proc: this.kernel.processes.info,
|
|
3787
|
+
router: this.kernel.router.published(),
|
|
3788
|
+
memory: this.kernel.memory
|
|
3789
|
+
})
|
|
3790
|
+
}))
|
|
3042
3791
|
this.app.get("/pinokio/memory", ex((req, res) => {
|
|
3043
3792
|
let filepath = req.query.filepath
|
|
3044
3793
|
let mem = this.getMemory(filepath)
|
|
@@ -3282,6 +4031,88 @@ class Server {
|
|
|
3282
4031
|
}
|
|
3283
4032
|
res.redirect(webpath)
|
|
3284
4033
|
}))
|
|
4034
|
+
this.app.post("/pinokio/upload", this.upload.any(), ex(async (req, res) => {
|
|
4035
|
+
console.log("POST /pinokio/upload")
|
|
4036
|
+
try {
|
|
4037
|
+
|
|
4038
|
+
|
|
4039
|
+
/*
|
|
4040
|
+
1. edit
|
|
4041
|
+
2. copy
|
|
4042
|
+
3. copy + edit
|
|
4043
|
+
4. move
|
|
4044
|
+
5. move + edit
|
|
4045
|
+
|
|
4046
|
+
*/
|
|
4047
|
+
|
|
4048
|
+
let formData = req.body
|
|
4049
|
+
for(let key in req.files) {
|
|
4050
|
+
let file = req.files[key]
|
|
4051
|
+
formData[file.fieldname] = file.buffer
|
|
4052
|
+
}
|
|
4053
|
+
console.log({ formData })
|
|
4054
|
+
|
|
4055
|
+
if (formData.edit) {
|
|
4056
|
+
if (formData.copy) {
|
|
4057
|
+
if (formData.old_path !== formData.new_path) {
|
|
4058
|
+
|
|
4059
|
+
// 1. copy first
|
|
4060
|
+
let old_path = this.kernel.path("api", formData.old_path)
|
|
4061
|
+
let new_path = this.kernel.path("api", formData.new_path)
|
|
4062
|
+
console.log({ old_path, new_path })
|
|
4063
|
+
await fs.promises.cp(old_path, new_path, { recursive: true })
|
|
4064
|
+
|
|
4065
|
+
// 2. edit meta in the new_path
|
|
4066
|
+
await this.updateMeta(formData, formData.new_path)
|
|
4067
|
+
|
|
4068
|
+
}
|
|
4069
|
+
} else if (formData.move) {
|
|
4070
|
+
|
|
4071
|
+
// 1. move first
|
|
4072
|
+
if (formData.old_path !== formData.new_path) {
|
|
4073
|
+
let old_path = this.kernel.path("api", formData.old_path)
|
|
4074
|
+
let new_path = this.kernel.path("api", formData.new_path)
|
|
4075
|
+
console.log({ old_path, new_path })
|
|
4076
|
+
await fs.promises.rename(old_path, new_path)
|
|
4077
|
+
}
|
|
4078
|
+
|
|
4079
|
+
// 2. edit meta in the new_path
|
|
4080
|
+
await this.updateMeta(formData, formData.new_path)
|
|
4081
|
+
} else {
|
|
4082
|
+
// 1. edit only
|
|
4083
|
+
if (formData.old_path === formData.new_path) {
|
|
4084
|
+
await this.updateMeta(formData, formData.new_path)
|
|
4085
|
+
}
|
|
4086
|
+
}
|
|
4087
|
+
} else {
|
|
4088
|
+
if (formData.copy) {
|
|
4089
|
+
// 1. copy only
|
|
4090
|
+
let old_path = this.kernel.path("api", formData.old_path)
|
|
4091
|
+
let new_path = this.kernel.path("api", formData.new_path)
|
|
4092
|
+
console.log({ old_path, new_path })
|
|
4093
|
+
await fs.promises.cp(old_path, new_path, { recursive: true })
|
|
4094
|
+
} else if (formData.move) {
|
|
4095
|
+
// 2. move only
|
|
4096
|
+
let old_path = this.kernel.path("api", formData.old_path)
|
|
4097
|
+
let new_path = this.kernel.path("api", formData.new_path)
|
|
4098
|
+
console.log({ old_path, new_path })
|
|
4099
|
+
await fs.promises.rename(old_path, new_path)
|
|
4100
|
+
} else {
|
|
4101
|
+
// nothing
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4104
|
+
res.json({
|
|
4105
|
+
success: true,
|
|
4106
|
+
reload: formData.new_path,
|
|
4107
|
+
new_path: formData.new_path,
|
|
4108
|
+
})
|
|
4109
|
+
} catch (e) {
|
|
4110
|
+
console.log("e", e)
|
|
4111
|
+
console.log("e.message", e.message)
|
|
4112
|
+
res.status(500).json({ error: e.message })
|
|
4113
|
+
}
|
|
4114
|
+
|
|
4115
|
+
}))
|
|
3285
4116
|
/*
|
|
3286
4117
|
SYNTAX
|
|
3287
4118
|
fs.uri(<bin|api>, path)
|
|
@@ -3341,12 +4172,15 @@ class Server {
|
|
|
3341
4172
|
object that's not (array, file, blob, uint8array, arraybuffer) => Object
|
|
3342
4173
|
the rest => typeof(value)
|
|
3343
4174
|
*/
|
|
4175
|
+
console.log("req", req)
|
|
3344
4176
|
let formData = req.body
|
|
3345
4177
|
for(let key in req.files) {
|
|
3346
4178
|
let file = req.files[key]
|
|
3347
4179
|
formData[file.fieldname] = file.buffer
|
|
3348
4180
|
}
|
|
3349
4181
|
|
|
4182
|
+
console.log({ formData })
|
|
4183
|
+
|
|
3350
4184
|
const drive = formData.drive
|
|
3351
4185
|
const home = formData.path
|
|
3352
4186
|
const method = formData.method
|
|
@@ -3475,11 +4309,42 @@ class Server {
|
|
|
3475
4309
|
|
|
3476
4310
|
}))
|
|
3477
4311
|
this.app.get("/pinokio/requirements_ready", ex((req, res) => {
|
|
3478
|
-
|
|
3479
4312
|
let requirements_pending = !this.kernel.bin.installed_initialized
|
|
3480
4313
|
console.log({ requirements_pending })
|
|
3481
4314
|
res.json({ requirements_pending })
|
|
3482
4315
|
}))
|
|
4316
|
+
this.app.get("/check_peer", ex((req, res) => {
|
|
4317
|
+
if (this.kernel.peer.active) {
|
|
4318
|
+
// if network is active, return success only if the router is up for all of its peers (including itself)
|
|
4319
|
+
console.log({ peer_info: this.kernel.peer.info })
|
|
4320
|
+
console.time("check_peer")
|
|
4321
|
+
let ready = true
|
|
4322
|
+
if (this.kernel.peer.info && Object.keys(this.kernel.peer.info).length > 0) {
|
|
4323
|
+
for(let host in this.kernel.peer.info) {
|
|
4324
|
+
let info = this.kernel.peer.info[host]
|
|
4325
|
+
if (info.router && Object.keys(info.router).length > 0) {
|
|
4326
|
+
ready = true
|
|
4327
|
+
} else {
|
|
4328
|
+
ready = false
|
|
4329
|
+
break;
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
} else {
|
|
4333
|
+
ready = false;
|
|
4334
|
+
}
|
|
4335
|
+
console.log({ info: this.kernel.peer.info, ready })
|
|
4336
|
+
console.timeEnd("check_peer")
|
|
4337
|
+
if (ready) {
|
|
4338
|
+
res.json({ success: true })
|
|
4339
|
+
} else {
|
|
4340
|
+
res.json({ success: false })
|
|
4341
|
+
}
|
|
4342
|
+
} else {
|
|
4343
|
+
// if network is not active, return success immediately (just checking if the server is up)
|
|
4344
|
+
console.log("this.kernel.router.published()")
|
|
4345
|
+
res.json({ success: true })
|
|
4346
|
+
}
|
|
4347
|
+
}))
|
|
3483
4348
|
this.app.get("/check", ex((req, res) => {
|
|
3484
4349
|
res.json({ success: true })
|
|
3485
4350
|
}))
|
|
@@ -3487,6 +4352,17 @@ class Server {
|
|
|
3487
4352
|
console.log("post /restart")
|
|
3488
4353
|
this.start({ debug: this.debug, browser: this.browser })
|
|
3489
4354
|
}))
|
|
4355
|
+
this.app.post("/network", ex(async (req, res) => {
|
|
4356
|
+
if (this.kernel.homedir) {
|
|
4357
|
+
let fullpath = path.resolve(this.kernel.homedir, "ENVIRONMENT")
|
|
4358
|
+
console.log("req.body", req.body)
|
|
4359
|
+
await Util.update_env(fullpath, req.body)
|
|
4360
|
+
res.json({ success: true })
|
|
4361
|
+
} else {
|
|
4362
|
+
res.json({ error: "homedir doesn't exist" })
|
|
4363
|
+
}
|
|
4364
|
+
}))
|
|
4365
|
+
|
|
3490
4366
|
this.app.post("/config", ex(async (req, res) => {
|
|
3491
4367
|
try {
|
|
3492
4368
|
await this.setConfig(req.body)
|
|
@@ -3508,15 +4384,58 @@ class Server {
|
|
|
3508
4384
|
stack: err.stack
|
|
3509
4385
|
})
|
|
3510
4386
|
});
|
|
3511
|
-
|
|
3512
|
-
//
|
|
3513
|
-
//
|
|
3514
|
-
//
|
|
3515
|
-
//
|
|
3516
|
-
//
|
|
3517
|
-
//
|
|
3518
|
-
//
|
|
3519
|
-
//
|
|
4387
|
+
process.on('SIGINT', () => {
|
|
4388
|
+
//if (this.kernel && this.kernel.shell) {
|
|
4389
|
+
// console.log("shell reset")
|
|
4390
|
+
// this.kernel.shell.reset(() => {
|
|
4391
|
+
// process.exit()
|
|
4392
|
+
// })
|
|
4393
|
+
//} else {
|
|
4394
|
+
// process.exit()
|
|
4395
|
+
//}
|
|
4396
|
+
console.log("[SigInt event] Kill", process.pid)
|
|
4397
|
+
if (this.kernel.processes.caddy_pid) {
|
|
4398
|
+
console.log("kill caddy", this.kernel.processes.caddy_pid)
|
|
4399
|
+
kill(this.kernel.processes.caddy_pid, "SIGKILL", true)
|
|
4400
|
+
}
|
|
4401
|
+
console.log("kill self")
|
|
4402
|
+
kill(process.pid, 'SIGKILL', true)
|
|
4403
|
+
//kill(process.pid, map, 'SIGKILL', () => {
|
|
4404
|
+
// console.log("child procs killed for", process.pid)
|
|
4405
|
+
// process.exit()
|
|
4406
|
+
//});
|
|
4407
|
+
})
|
|
4408
|
+
|
|
4409
|
+
process.on('SIGTERM', () => {
|
|
4410
|
+
// if (this.kernel && this.kernel.shell) {
|
|
4411
|
+
// console.log("shell reset")
|
|
4412
|
+
// this.kernel.shell.reset(() => {
|
|
4413
|
+
// process.exit()
|
|
4414
|
+
// })
|
|
4415
|
+
// } else {
|
|
4416
|
+
// process.exit()
|
|
4417
|
+
// }
|
|
4418
|
+
console.log("[Sigterm event] Kill", process.pid)
|
|
4419
|
+
if (this.kernel.processes.caddy_pid) {
|
|
4420
|
+
console.log("kill caddy", this.kernel.processes.caddy_pid)
|
|
4421
|
+
kill(this.kernel.processes.caddy_pid, "SIGKILL", true)
|
|
4422
|
+
}
|
|
4423
|
+
console.log("kill self")
|
|
4424
|
+
kill(process.pid, 'SIGKILL', true)
|
|
4425
|
+
//let map = this.kernel.processes.map || {}
|
|
4426
|
+
//kill(process.pid, map, 'SIGKILL', () => {
|
|
4427
|
+
// console.log("child procs killed for", process.pid)
|
|
4428
|
+
// process.exit()
|
|
4429
|
+
//});
|
|
4430
|
+
})
|
|
4431
|
+
// process.on('exit', () => {
|
|
4432
|
+
// console.log("[Exit event]")
|
|
4433
|
+
// kill(process.pid, 'SIGKILL', true)
|
|
4434
|
+
// //let map = this.kernel.processes.map || {}
|
|
4435
|
+
// //kill(process.pid, map, 'SIGKILL', () => {
|
|
4436
|
+
// // console.log("child procs killed for", process.pid)
|
|
4437
|
+
// // process.exit()
|
|
4438
|
+
// //});
|
|
3520
4439
|
// })
|
|
3521
4440
|
// process.on('exit', () => {
|
|
3522
4441
|
// console.log("exit Event")
|
|
@@ -3540,7 +4459,8 @@ class Server {
|
|
|
3540
4459
|
server: this.listening
|
|
3541
4460
|
});
|
|
3542
4461
|
})
|
|
3543
|
-
|
|
4462
|
+
// this.kernel.peer.start(this.kernel)
|
|
4463
|
+
|
|
3544
4464
|
|
|
3545
4465
|
}
|
|
3546
4466
|
}
|