pinokiod 3.9.0 → 3.9.3
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/:w +1247 -0
- 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 +215 -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 +368 -98
- 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 +275 -121
- 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/kernel/index.js
CHANGED
|
@@ -7,6 +7,7 @@ const fastq = require('fastq')
|
|
|
7
7
|
const fetch = require('cross-fetch');
|
|
8
8
|
const waitOn = require('wait-on');
|
|
9
9
|
const system = require('systeminformation');
|
|
10
|
+
const shellPath = require('shell-path');
|
|
10
11
|
const Sysinfo = require('./sysinfo')
|
|
11
12
|
const portfinder = require('portfinder-cp');
|
|
12
13
|
const { execSync } = require('child_process')
|
|
@@ -19,6 +20,7 @@ const Template = require('jimini')
|
|
|
19
20
|
const which = require('which')
|
|
20
21
|
const Shells = require("./shells")
|
|
21
22
|
const Key = require("./key")
|
|
23
|
+
const KV = require('./kv')
|
|
22
24
|
const Script = require('./script')
|
|
23
25
|
const Environment = require("./environment")
|
|
24
26
|
const Util = require('./util')
|
|
@@ -26,6 +28,14 @@ const Config = require("./pinokio.json")
|
|
|
26
28
|
const Info = require('./info')
|
|
27
29
|
const Pipe = require("../pipe")
|
|
28
30
|
const Cloudflare = require('./api/cloudflare')
|
|
31
|
+
const Store = require('./store')
|
|
32
|
+
const Proto = require('./prototype')
|
|
33
|
+
const Router = require("./router")
|
|
34
|
+
const Procs = require('./procs')
|
|
35
|
+
const Peer = require('./peer')
|
|
36
|
+
const Connect = require('./connect')
|
|
37
|
+
//const kill = require('./tree-kill');
|
|
38
|
+
const kill = require('kill-sync')
|
|
29
39
|
const VARS = {
|
|
30
40
|
pip: {
|
|
31
41
|
install: {
|
|
@@ -36,13 +46,31 @@ const VARS = {
|
|
|
36
46
|
|
|
37
47
|
//const memwatch = require('@airbnb/node-memwatch');
|
|
38
48
|
class Kernel {
|
|
39
|
-
|
|
40
|
-
//schema = "<=2.1.0"
|
|
41
|
-
//schema = "<=3.2.0"
|
|
42
|
-
schema = "<=3.7.0"
|
|
49
|
+
schema = "<=4.0.0"
|
|
43
50
|
constructor(store) {
|
|
44
51
|
this.fetch = fetch
|
|
45
|
-
|
|
52
|
+
|
|
53
|
+
// STORE
|
|
54
|
+
// Previously custom STORE could be passed in. This was to support both electron and server based initialization
|
|
55
|
+
// But this creates complexity because the store location is not fixed
|
|
56
|
+
// Migrating it to ~/.pinokio/config.json
|
|
57
|
+
//
|
|
58
|
+
// 1. does ~/.pinokio exist?
|
|
59
|
+
// - load from that file
|
|
60
|
+
// 2. does ~/.pinokio NOT exist?
|
|
61
|
+
// - save the passed in options to the `~/.pinokio`
|
|
62
|
+
// - load the config
|
|
63
|
+
|
|
64
|
+
this.store = new Store()
|
|
65
|
+
let exists = this.store.exists()
|
|
66
|
+
console.log("Store exists?", exists)
|
|
67
|
+
if (!exists) {
|
|
68
|
+
console.log("doesn't exist")
|
|
69
|
+
// clone the store to the new store
|
|
70
|
+
this.store.clone(store.store)
|
|
71
|
+
// load from the store (this will be the last time this is used, since the next time it loads, it will load from the new store)
|
|
72
|
+
}
|
|
73
|
+
|
|
46
74
|
this.arch = os.arch()
|
|
47
75
|
this.os = os
|
|
48
76
|
this.platform = os.platform()
|
|
@@ -50,9 +78,9 @@ class Kernel {
|
|
|
50
78
|
this.jsdom = jsdom
|
|
51
79
|
this.exposed = {}
|
|
52
80
|
this.envs = {}
|
|
53
|
-
this.
|
|
54
|
-
|
|
55
|
-
|
|
81
|
+
this.shellpath = shellPath.sync()
|
|
82
|
+
|
|
83
|
+
|
|
56
84
|
}
|
|
57
85
|
/*
|
|
58
86
|
kernel.env() => return the system environment
|
|
@@ -147,6 +175,15 @@ class Kernel {
|
|
|
147
175
|
let id = this.api.filePath(uri, cwd)
|
|
148
176
|
return this.api.running[id]
|
|
149
177
|
}
|
|
178
|
+
start_port () {
|
|
179
|
+
if (this.router.port_mapping && Object.keys(this.router.port_mapping).length > 0) {
|
|
180
|
+
let max_caddy_port = Math.max(...Object.values(this.router.port_mapping))
|
|
181
|
+
let start_port = Math.max(42003, max_caddy_port + 1)
|
|
182
|
+
return start_port
|
|
183
|
+
} else {
|
|
184
|
+
return 42003
|
|
185
|
+
}
|
|
186
|
+
}
|
|
150
187
|
port(port) {
|
|
151
188
|
// 1. if port is passed, check port
|
|
152
189
|
// 2. if no port is passed, get the next available port
|
|
@@ -158,9 +195,21 @@ class Kernel {
|
|
|
158
195
|
if (port) {
|
|
159
196
|
return portfinder.isAvailablePromise({ host: "0.0.0.0", port })
|
|
160
197
|
} else {
|
|
161
|
-
return portfinder.getPortPromise({ port:
|
|
198
|
+
return portfinder.getPortPromise({ port: this.start_port() })
|
|
162
199
|
}
|
|
163
200
|
}
|
|
201
|
+
ports(count) {
|
|
202
|
+
// get "count" number of available ports
|
|
203
|
+
return new Promise((resolve, reject) => {
|
|
204
|
+
portfinder.getPorts(count, { port: this.start_port() }, (err, ports) => {
|
|
205
|
+
if (err) {
|
|
206
|
+
reject(err)
|
|
207
|
+
} else {
|
|
208
|
+
resolve(ports)
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
}
|
|
164
213
|
path(...args) {
|
|
165
214
|
return path.resolve(this.homedir, ...args)
|
|
166
215
|
}
|
|
@@ -185,6 +234,56 @@ class Kernel {
|
|
|
185
234
|
log(data, group, info) {
|
|
186
235
|
this.log_queue.push({ data, group, info })
|
|
187
236
|
}
|
|
237
|
+
async refresh(notify_peers) {
|
|
238
|
+
let env = await Environment.get(this.homedir)
|
|
239
|
+
let peer_active = false
|
|
240
|
+
// if PINOKIO_NETWORK_SHARE is 0 or false, turn it off
|
|
241
|
+
if (env && env.PINOKIO_NETWORK_ACTIVE && (env.PINOKIO_NETWORK_ACTIVE==="1" || env.PINOKIO_NETWORK_ACTIVE.toLowerCase()==="true")) {
|
|
242
|
+
peer_active = true
|
|
243
|
+
}
|
|
244
|
+
let https_active = false
|
|
245
|
+
if (env && env.PINOKIO_HTTPS_ACTIVE && (env.PINOKIO_HTTPS_ACTIVE==="1" || env.PINOKIO_HTTPS_ACTIVE.toLowerCase()==="true")) {
|
|
246
|
+
https_active = true
|
|
247
|
+
}
|
|
248
|
+
// console.log("kernel.refresh", { active, notify_peers })
|
|
249
|
+
|
|
250
|
+
let caddy_installed = await this.bin.check_installed({ name: "caddy" })
|
|
251
|
+
|
|
252
|
+
// 1. https
|
|
253
|
+
// 2. https + local share
|
|
254
|
+
// console.log("caddy installed?", caddy_installed)
|
|
255
|
+
if (caddy_installed && https_active) {
|
|
256
|
+
|
|
257
|
+
// 1. get the process list
|
|
258
|
+
await this.processes.refresh()
|
|
259
|
+
|
|
260
|
+
// 2. refresh peer info to reflect the proc info
|
|
261
|
+
await this.peer.refresh()
|
|
262
|
+
|
|
263
|
+
// 3. load custom routers from ~/pinokio/network
|
|
264
|
+
await this.router.init()
|
|
265
|
+
|
|
266
|
+
// 4. set current local host router info
|
|
267
|
+
await this.router.local()
|
|
268
|
+
|
|
269
|
+
// 5. refresh peer info to reflect the updated router info
|
|
270
|
+
await this.peer.refresh()
|
|
271
|
+
|
|
272
|
+
// 6. tell peers to refresh
|
|
273
|
+
if (notify_peers) {
|
|
274
|
+
await this.peer.notify_peers()
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 7. update remote router
|
|
278
|
+
await this.router.remote()
|
|
279
|
+
|
|
280
|
+
// 8. update caddy config
|
|
281
|
+
await this.router.update()
|
|
282
|
+
|
|
283
|
+
// 9. announce self to the peer network
|
|
284
|
+
this.peer.announce()
|
|
285
|
+
}
|
|
286
|
+
}
|
|
188
287
|
async clearLog(group) {
|
|
189
288
|
let relativePath = path.relative(this.homedir, group)
|
|
190
289
|
for(let type of ["info", "buf", "cleaned"]) {
|
|
@@ -236,12 +335,9 @@ class Kernel {
|
|
|
236
335
|
}
|
|
237
336
|
async getInfo(refresh){
|
|
238
337
|
let info = this.sysinfo
|
|
239
|
-
console.time(">>>>> updateSysinfo")
|
|
240
|
-
console.log("refresh", refresh)
|
|
241
338
|
if (refresh) {
|
|
242
339
|
await this.update_sysinfo()
|
|
243
340
|
}
|
|
244
|
-
console.timeEnd(">>>>> updateSysinfo")
|
|
245
341
|
let i = {
|
|
246
342
|
version: this.version,
|
|
247
343
|
platform: this.platform,
|
|
@@ -313,8 +409,20 @@ class Kernel {
|
|
|
313
409
|
let folders = files.filter((f) => { return f.isDirectory() }).map((x) => { return x.name })
|
|
314
410
|
let meta = {}
|
|
315
411
|
for(let folder of folders) {
|
|
316
|
-
|
|
317
|
-
let
|
|
412
|
+
|
|
413
|
+
let pinokiojson = this.path("api", folder, "pinokio.json")
|
|
414
|
+
let pinokiometajson = this.path("api", folder, "pinokio_meta.json")
|
|
415
|
+
let pinokiojs = this.path("api", folder, "pinokio.js")
|
|
416
|
+
let exists1 = await this.exists(pinokiojson)
|
|
417
|
+
let exists2 = await this.exists(pinokiometajson)
|
|
418
|
+
let exists3 = await this.exists(pinokiojs)
|
|
419
|
+
if (!exists1 && !exists2 && !exists3) {
|
|
420
|
+
await fs.promises.writeFile(pinokiojson, JSON.stringify({
|
|
421
|
+
title: "No title",
|
|
422
|
+
description: ""
|
|
423
|
+
}))
|
|
424
|
+
}
|
|
425
|
+
let pinokio = await this.api.meta(folder)
|
|
318
426
|
if (pinokio) {
|
|
319
427
|
i.api.push({
|
|
320
428
|
path: folder,
|
|
@@ -390,13 +498,28 @@ class Kernel {
|
|
|
390
498
|
// }
|
|
391
499
|
}
|
|
392
500
|
}
|
|
501
|
+
kill() {
|
|
502
|
+
process.kill(process.pid, "SIGTERM")
|
|
503
|
+
}
|
|
393
504
|
async init(options) {
|
|
505
|
+
|
|
394
506
|
let home = this.store.get("home")
|
|
395
507
|
|
|
396
508
|
// reset shells if they exist
|
|
397
509
|
if (this.shell) {
|
|
398
510
|
this.shell.reset()
|
|
399
511
|
}
|
|
512
|
+
if (this.peer) {
|
|
513
|
+
this.peer.stop()
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
this.info = new Info(this)
|
|
517
|
+
this.pipe = new Pipe(this)
|
|
518
|
+
this.proto = new Proto(this)
|
|
519
|
+
this.processes = new Procs()
|
|
520
|
+
this.kv = new KV(this)
|
|
521
|
+
this.cloudflare = new Cloudflare()
|
|
522
|
+
this.peer = new Peer()
|
|
400
523
|
|
|
401
524
|
this.homedir = home
|
|
402
525
|
|
|
@@ -443,6 +566,8 @@ class Kernel {
|
|
|
443
566
|
this.api = new Api(this)
|
|
444
567
|
this.python = new Python(this)
|
|
445
568
|
this.shell = new Shells(this)
|
|
569
|
+
this.router = new Router(this)
|
|
570
|
+
this.connect = new Connect(this)
|
|
446
571
|
this.system = system
|
|
447
572
|
this.keys = {}
|
|
448
573
|
this.memory = {
|
|
@@ -462,7 +587,6 @@ class Kernel {
|
|
|
462
587
|
|
|
463
588
|
// 0. create homedir
|
|
464
589
|
let home_exists = await this.exists(this.homedir)
|
|
465
|
-
console.log({ home_exists, homedir: this.homedir })
|
|
466
590
|
if (!home_exists) {
|
|
467
591
|
// home didn't exist.
|
|
468
592
|
// 1. mkdir
|
|
@@ -487,7 +611,6 @@ class Kernel {
|
|
|
487
611
|
NO_PROXY: (this.store.get("NO_PROXY") || "")
|
|
488
612
|
}
|
|
489
613
|
let fullpath = path.resolve(this.homedir, "ENVIRONMENT")
|
|
490
|
-
console.log("> update env", { fullpath, updated })
|
|
491
614
|
await Util.update_env(fullpath, updated)
|
|
492
615
|
|
|
493
616
|
// 2. mkdir all the folders if not already created
|
|
@@ -510,9 +633,9 @@ class Kernel {
|
|
|
510
633
|
//await this.bin.init()
|
|
511
634
|
let ts = Date.now()
|
|
512
635
|
this.bin.init().then(() => {
|
|
513
|
-
console.log("bin init finished")
|
|
514
636
|
if (this.homedir) {
|
|
515
637
|
this.shell.init().then(async () => {
|
|
638
|
+
// this.bin.check()
|
|
516
639
|
if (this.envs) {
|
|
517
640
|
this.template.update({
|
|
518
641
|
env: this.envs,
|
|
@@ -542,16 +665,12 @@ class Kernel {
|
|
|
542
665
|
let interval = setInterval(async () => {
|
|
543
666
|
if (this.i) {
|
|
544
667
|
for(let api of this.i.api) {
|
|
545
|
-
console.log({ api })
|
|
546
668
|
let env_path = path.resolve(this.api.userdir, api.path)
|
|
547
669
|
let e = await Environment.get(env_path)
|
|
548
|
-
console.log(e)
|
|
549
670
|
if (e.PINOKIO_SCRIPT_AUTOLAUNCH && e.PINOKIO_SCRIPT_AUTOLAUNCH.trim().length > 0) {
|
|
550
671
|
let autolaunch_path = path.resolve(env_path, e.PINOKIO_SCRIPT_AUTOLAUNCH)
|
|
551
672
|
let exists = await this.exists(autolaunch_path)
|
|
552
|
-
console.log("ATTEMPTING AUTOLAUNCH", autolaunch_path)
|
|
553
673
|
if (exists) {
|
|
554
|
-
console.log("SCRIPT EXISTS. Starting...", autolaunch_path)
|
|
555
674
|
this.api.process({
|
|
556
675
|
uri: autolaunch_path,
|
|
557
676
|
input: {}
|
|
@@ -566,7 +685,6 @@ class Kernel {
|
|
|
566
685
|
}
|
|
567
686
|
}
|
|
568
687
|
}
|
|
569
|
-
console.log("clear Interval")
|
|
570
688
|
clearInterval(interval)
|
|
571
689
|
setTimeout(() => {
|
|
572
690
|
this.launch_complete = true
|
|
@@ -580,14 +698,11 @@ class Kernel {
|
|
|
580
698
|
}
|
|
581
699
|
})
|
|
582
700
|
let ts2 = Date.now()
|
|
583
|
-
console.time(">>>> 6 api.init")
|
|
584
701
|
await this.api.init()
|
|
585
|
-
console.timeEnd(">>>> 6 api.init")
|
|
586
702
|
|
|
587
703
|
//await this.shell.init()
|
|
588
704
|
|
|
589
705
|
if (this.homedir) {
|
|
590
|
-
console.time(">>>> 7 template.init")
|
|
591
706
|
await this.template.init({
|
|
592
707
|
kernel: this,
|
|
593
708
|
system,
|
|
@@ -597,25 +712,30 @@ class Kernel {
|
|
|
597
712
|
return this.api.get_proxy_url("/proxy", port)
|
|
598
713
|
},
|
|
599
714
|
})
|
|
600
|
-
console.timeEnd(">>>> 7 template.init")
|
|
601
715
|
this.sys = new Sysinfo()
|
|
602
|
-
console.time("sys init")
|
|
603
|
-
console.time(">>>> 8 sys.init")
|
|
604
716
|
await this.sys.init(this)
|
|
605
|
-
console.timeEnd(">>>> 8 sys.init")
|
|
606
|
-
console.timeEnd("sys init")
|
|
607
717
|
let info = this.sys.info
|
|
608
718
|
|
|
609
719
|
this.sysinfo = info
|
|
610
720
|
|
|
611
|
-
console.time(">>>> 9 getInfo")
|
|
612
721
|
await this.getInfo()
|
|
613
|
-
console.timeEnd(">>>> 9 getInfo")
|
|
614
722
|
|
|
615
723
|
await fs.promises.mkdir(this.path("logs"), { recursive: true }).catch((e) => { })
|
|
616
724
|
await fs.promises.writeFile(this.path("logs/system.json"), JSON.stringify(this.i, null, 2))
|
|
617
725
|
let pwpath = this.bin.path("playwright/node_modules/playwright")
|
|
618
726
|
this.playwright = (await this.loader.load(pwpath)).resolved
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
await this.proto.init()
|
|
730
|
+
|
|
731
|
+
// refresh every 5 second
|
|
732
|
+
if (this.refresh_interval) {
|
|
733
|
+
clearInterval(this.refresh_interval)
|
|
734
|
+
}
|
|
735
|
+
this.refresh_interval = setInterval(() => {
|
|
736
|
+
this.refresh()
|
|
737
|
+
}, 5000)
|
|
738
|
+
|
|
619
739
|
}
|
|
620
740
|
|
|
621
741
|
} catch (e) {
|
package/kernel/info.js
CHANGED
|
@@ -40,12 +40,14 @@ class Info {
|
|
|
40
40
|
running(...args) {
|
|
41
41
|
let cwd = path.dirname(this.caller())
|
|
42
42
|
let resolved_path = path.resolve(cwd, ...args)
|
|
43
|
-
|
|
43
|
+
let running = this.kernel.api.running[resolved_path]
|
|
44
|
+
return running ? running : false
|
|
44
45
|
}
|
|
45
46
|
exists(...args) {
|
|
46
47
|
let cwd = path.dirname(this.caller())
|
|
47
48
|
let resolved_path = path.resolve(cwd, ...args)
|
|
48
|
-
|
|
49
|
+
let exists = fs.existsSync(resolved_path)
|
|
50
|
+
return exists ? exists: false
|
|
49
51
|
}
|
|
50
52
|
local(...args) {
|
|
51
53
|
let cwd = path.dirname(this.caller())
|
package/kernel/kv.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
class KV {
|
|
2
|
+
constructor(kernel) {
|
|
3
|
+
this.kernel = kernel
|
|
4
|
+
}
|
|
5
|
+
async set(host, val, index) {
|
|
6
|
+
if (this.kernel.homedir) {
|
|
7
|
+
let filePath = this.kernel.path("key.json")
|
|
8
|
+
let json = await this.kernel.load(filePath)
|
|
9
|
+
if (!json) json = {}
|
|
10
|
+
// if the array at host path doesn't exist, create an empty array
|
|
11
|
+
if (!json[host]) {
|
|
12
|
+
json[host] = []
|
|
13
|
+
}
|
|
14
|
+
// if index doesn't exist, set index 0
|
|
15
|
+
if (!index) {
|
|
16
|
+
index = 0
|
|
17
|
+
}
|
|
18
|
+
json[host][index] = val
|
|
19
|
+
await fs.promises.writeFile(filePath, JSON.stringify(json, null, 2), 'utf8')
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async get(host, index) {
|
|
23
|
+
if (this.kernel.homedir) {
|
|
24
|
+
let filePath = this.kernel.path("key.json")
|
|
25
|
+
console.log({ host, index, filePath })
|
|
26
|
+
let json = await this.kernel.load(filePath)
|
|
27
|
+
if (!json) {
|
|
28
|
+
return null
|
|
29
|
+
}
|
|
30
|
+
console.log({ json })
|
|
31
|
+
if (json[host]) {
|
|
32
|
+
return json[host][index]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
module.exports = KV
|
package/kernel/loader.js
CHANGED
|
@@ -11,6 +11,11 @@ class Loader {
|
|
|
11
11
|
let extension = path.extname(_path)
|
|
12
12
|
clearModule(_path)
|
|
13
13
|
|
|
14
|
+
let exists = await this.exists(_path)
|
|
15
|
+
if (!exists) {
|
|
16
|
+
return { resolved: null, dirname: path.dirname(_path), }
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
if (/\.json$/i.test(_path)) {
|
|
15
20
|
resolved = await this.requireJSON(_path)
|
|
16
21
|
dirname = path.dirname(_path)
|
|
@@ -32,23 +37,25 @@ class Loader {
|
|
|
32
37
|
resolved = null
|
|
33
38
|
}
|
|
34
39
|
} catch (e) {
|
|
35
|
-
console.log(e)
|
|
36
40
|
resolved = null
|
|
37
41
|
}
|
|
38
42
|
}
|
|
43
|
+
if (!resolved) {
|
|
44
|
+
console.log(`[did not load] ${_path}`)
|
|
45
|
+
}
|
|
39
46
|
return { resolved, extension, dirname }
|
|
40
47
|
}
|
|
41
48
|
async requireJSON(filepath) {
|
|
42
49
|
let config
|
|
43
50
|
try { config = require(filepath) } catch (e) {
|
|
44
|
-
console.log("load
|
|
51
|
+
console.log("> load", e, filepath)
|
|
45
52
|
}
|
|
46
53
|
return config
|
|
47
54
|
}
|
|
48
55
|
async requireJS(filepath) {
|
|
49
56
|
let config
|
|
50
57
|
try { config = require(filepath) } catch (e) {
|
|
51
|
-
console.log("load
|
|
58
|
+
console.log("> load", e, filepath)
|
|
52
59
|
}
|
|
53
60
|
try {
|
|
54
61
|
// if the required module is a class, return the instantiated object
|
|
@@ -79,5 +86,8 @@ class Loader {
|
|
|
79
86
|
})
|
|
80
87
|
return result
|
|
81
88
|
}
|
|
89
|
+
exists(abspath) {
|
|
90
|
+
return new Promise(r=>fs.access(abspath, fs.constants.F_OK, e => r(!e)))
|
|
91
|
+
}
|
|
82
92
|
}
|
|
83
93
|
module.exports = Loader
|
package/kernel/peer.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
const dgram = require('dgram');
|
|
2
|
+
const axios = require('axios');
|
|
3
|
+
const os = require('os')
|
|
4
|
+
const Environment = require("./environment")
|
|
5
|
+
class PeerDiscovery {
|
|
6
|
+
constructor(port = 41234, message = 'ping', interval = 1000) {
|
|
7
|
+
this.port = port;
|
|
8
|
+
this.message = Buffer.from(message);
|
|
9
|
+
this.interval = interval;
|
|
10
|
+
this.peers = new Set();
|
|
11
|
+
this.host = this._getLocalIPAddress()
|
|
12
|
+
this.default_port = 42000
|
|
13
|
+
this.peers.add(this.host)
|
|
14
|
+
// this.start();
|
|
15
|
+
}
|
|
16
|
+
stop() {
|
|
17
|
+
if (this.socket) {
|
|
18
|
+
console.log("peer stop")
|
|
19
|
+
clearInterval(this.interval_handle)
|
|
20
|
+
this.socket.close()
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
announce() {
|
|
24
|
+
if (this.socket) {
|
|
25
|
+
this.socket.send(this.message, 0, this.message.length, this.port, '192.168.1.255');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async start(kernel) {
|
|
29
|
+
let env = await Environment.get(kernel.homedir)
|
|
30
|
+
|
|
31
|
+
// by default expose to the local network
|
|
32
|
+
this.active = false
|
|
33
|
+
// if PINOKIO_NETWORK_SHARE is 0 or false, turn it off
|
|
34
|
+
if (env && env.PINOKIO_NETWORK_ACTIVE && (env.PINOKIO_NETWORK_ACTIVE==="1" || env.PINOKIO_NETWORK_ACTIVE.toLowerCase()==="true")) {
|
|
35
|
+
this.active = true
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.name = os.userInfo().username
|
|
39
|
+
if (env && env.PINOKIO_NETWORK_NAME && env.PINOKIO_NETWORK_NAME.length > 0) {
|
|
40
|
+
this.name = env.PINOKIO_NETWORK_NAME
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (this.active) {
|
|
44
|
+
// Listen for incoming pings
|
|
45
|
+
this.socket = dgram.createSocket('udp4');
|
|
46
|
+
this.socket.on('message', (msg, rinfo) => {
|
|
47
|
+
const ip = rinfo.address;
|
|
48
|
+
if (msg.toString() === this.message.toString() && this._isLocalLAN(ip)) {
|
|
49
|
+
if (!this.peers.has(ip)) {
|
|
50
|
+
console.log(`Discovered peer: ${ip}`);
|
|
51
|
+
this.peers.add(ip);
|
|
52
|
+
this.refresh()
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
this.socket.on('error', (err) => {
|
|
57
|
+
console.error('UDP error', err);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Enable broadcast
|
|
61
|
+
console.log("binding socket")
|
|
62
|
+
this.socket.bind({ address: "0.0.0.0", port: this.port }, () => {
|
|
63
|
+
console.log("socket bound")
|
|
64
|
+
// this.socket.setMulticastLoopback(true);
|
|
65
|
+
|
|
66
|
+
this.socket.setBroadcast(true);
|
|
67
|
+
|
|
68
|
+
this.announce()
|
|
69
|
+
|
|
70
|
+
// // Send broadcast pings every interval
|
|
71
|
+
// this.interval_handle = setInterval(() => {
|
|
72
|
+
// //this.socket.send(this.message, 0, this.message.length, this.port, '255.255.255.255');
|
|
73
|
+
// this.socket.send(this.message, 0, this.message.length, this.port, '192.168.1.255');
|
|
74
|
+
// }, this.interval);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async _refresh(host) {
|
|
79
|
+
try {
|
|
80
|
+
let res = await axios.get(`http://${host}:${this.default_port}/pinokio/peer`, {
|
|
81
|
+
timeout: 2000
|
|
82
|
+
})
|
|
83
|
+
return res.data
|
|
84
|
+
} catch (e) {
|
|
85
|
+
return null
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async _broadcast(host) {
|
|
89
|
+
try {
|
|
90
|
+
let res = await axios.post(`http://${host}:${this.default_port}/pinokio/peer/refresh`, {
|
|
91
|
+
timeout: 2000
|
|
92
|
+
})
|
|
93
|
+
return res.data
|
|
94
|
+
} catch (e) {
|
|
95
|
+
return null
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// notify peers to refresh my info
|
|
99
|
+
async notify_peers() {
|
|
100
|
+
if (this.active) {
|
|
101
|
+
let res = await Promise.all(Array.from(this.peers).map((host) => {
|
|
102
|
+
return this._broadcast(host)
|
|
103
|
+
}))
|
|
104
|
+
return res
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// refresh peer info
|
|
108
|
+
async refresh(peers) {
|
|
109
|
+
// if (this.active) {
|
|
110
|
+
this.refreshing = true
|
|
111
|
+
let refresh_peers
|
|
112
|
+
if (peers) {
|
|
113
|
+
refresh_peers = peers
|
|
114
|
+
} else {
|
|
115
|
+
this.info = {}
|
|
116
|
+
refresh_peers = Array.from(this.peers)
|
|
117
|
+
}
|
|
118
|
+
let peer_info = await Promise.all(refresh_peers.map((host) => {
|
|
119
|
+
return this._refresh(host)
|
|
120
|
+
}))
|
|
121
|
+
for(let i=0; i<peer_info.length; i++) {
|
|
122
|
+
let peer = peer_info[i]
|
|
123
|
+
if (peer) {
|
|
124
|
+
this.info[peer.host] = {
|
|
125
|
+
host: peer.host,
|
|
126
|
+
...peer
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
this.refreshing = false
|
|
131
|
+
return this.info
|
|
132
|
+
// }
|
|
133
|
+
}
|
|
134
|
+
_isLocalLAN(ip) {
|
|
135
|
+
return ip.startsWith('192.168.') || ip.startsWith('10.') || (ip.startsWith('172.') && is172Private(ip));
|
|
136
|
+
}
|
|
137
|
+
_getLocalIPAddress() {
|
|
138
|
+
const interfaces = os.networkInterfaces();
|
|
139
|
+
for (const ifaceList of Object.values(interfaces)) {
|
|
140
|
+
for (const iface of ifaceList) {
|
|
141
|
+
if (iface.family === 'IPv4' && !iface.internal) {
|
|
142
|
+
const ip = iface.address;
|
|
143
|
+
if (
|
|
144
|
+
ip.startsWith('10.') ||
|
|
145
|
+
ip.startsWith('192.168.') ||
|
|
146
|
+
(ip.startsWith('172.') && is172Private(ip))
|
|
147
|
+
) {
|
|
148
|
+
return ip;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
function is172Private(ip) {
|
|
155
|
+
const secondOctet = parseInt(ip.split('.')[1], 10);
|
|
156
|
+
return secondOctet >= 16 && secondOctet <= 31;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = PeerDiscovery;
|