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.
Files changed (118) hide show
  1. package/kernel/api/filepicker/index.js +59 -0
  2. package/kernel/api/fs/index.js +6 -4
  3. package/kernel/api/index.js +123 -37
  4. package/kernel/api/input/index.js +2 -0
  5. package/kernel/api/web/index.js +6 -0
  6. package/kernel/bin/caddy.js +127 -0
  7. package/kernel/bin/cloudflared.js +1 -0
  8. package/kernel/bin/conda.js +10 -2
  9. package/kernel/bin/cuda.js +0 -16
  10. package/kernel/bin/ffmpeg.js +4 -6
  11. package/kernel/bin/git.js +55 -29
  12. package/kernel/bin/gxx.js +8 -6
  13. package/kernel/bin/huggingface.js +4 -5
  14. package/kernel/bin/index.js +347 -88
  15. package/kernel/bin/node.js +13 -6
  16. package/kernel/bin/py.js +1 -1
  17. package/kernel/bin/setup.js +146 -0
  18. package/kernel/bin/uv.js +4 -5
  19. package/kernel/bin/zip.js +7 -8
  20. package/kernel/connect/index.js +25 -0
  21. package/kernel/connect/providers/github/README.md +11 -0
  22. package/kernel/connect/providers/x/README.md +96 -0
  23. package/kernel/connect/providers/x/index.js +152 -0
  24. package/kernel/environment.js +50 -1
  25. package/kernel/index.js +152 -32
  26. package/kernel/info.js +4 -2
  27. package/kernel/kv.js +38 -0
  28. package/kernel/loader.js +13 -3
  29. package/kernel/peer.js +161 -0
  30. package/kernel/procs.js +116 -0
  31. package/kernel/prototype.js +62 -0
  32. package/kernel/router/common.js +42 -0
  33. package/kernel/router/connector.js +81 -0
  34. package/kernel/router/index.js +235 -0
  35. package/kernel/router/localhost_home_router.js +84 -0
  36. package/kernel/router/localhost_port_router.js +15 -0
  37. package/kernel/router/localhost_variable_router.js +37 -0
  38. package/kernel/router/peer_home_router.js +15 -0
  39. package/kernel/router/peer_peer_router.js +26 -0
  40. package/kernel/router/peer_port_router.js +45 -0
  41. package/kernel/router/peer_variable_router.js +46 -0
  42. package/kernel/router/processor.js +31 -0
  43. package/kernel/shell.js +141 -60
  44. package/kernel/shells.js +32 -17
  45. package/kernel/store.js +58 -0
  46. package/kernel/sysinfo.js +0 -1
  47. package/kernel/util.js +143 -36
  48. package/package.json +4 -1
  49. package/pipe/index.js +0 -2
  50. package/script/index.js +12 -0
  51. package/server/index.js +1473 -553
  52. package/server/public/Socket.js +5 -1
  53. package/server/public/SourceCodePro-Regular.ttf +0 -0
  54. package/server/public/SpaceMono-Bold.ttf +0 -0
  55. package/server/public/SpaceMono-BoldItalic.ttf +0 -0
  56. package/server/public/SpaceMono-Italic.ttf +0 -0
  57. package/server/public/SpaceMono-Regular.ttf +0 -0
  58. package/server/public/commander.js +40 -0
  59. package/server/public/common.js +60 -0
  60. package/server/public/dropzone-min.js +2 -0
  61. package/server/public/dropzone.css +1 -0
  62. package/server/public/filepond-plugin-file-validate-type.min.js +9 -0
  63. package/server/public/filepond-plugin-image-crop.min.js +9 -0
  64. package/server/public/filepond-plugin-image-edit.min.css +8 -0
  65. package/server/public/filepond-plugin-image-edit.min.js +9 -0
  66. package/server/public/filepond-plugin-image-exif-orientation.min.js +9 -0
  67. package/server/public/filepond-plugin-image-preview.min.css +8 -0
  68. package/server/public/filepond-plugin-image-preview.min.js +9 -0
  69. package/server/public/filepond-plugin-image-resize.min.js +9 -0
  70. package/server/public/filepond-plugin-image-transform.min.js +9 -0
  71. package/server/public/filepond.min.css +8 -0
  72. package/server/public/filepond.min.js +9 -0
  73. package/server/public/fscreator.js +174 -0
  74. package/server/public/fseditor.js +212 -0
  75. package/server/public/github.png +0 -0
  76. package/server/public/highlight-js.js +81 -0
  77. package/server/public/highlight.css +9 -0
  78. package/server/public/highlight.js +1244 -0
  79. package/server/public/install.js +17 -20
  80. package/server/public/modalinput.js +105 -9
  81. package/server/public/opener.js +3 -1
  82. package/server/public/pinokio-black.png +0 -0
  83. package/server/public/pinokio-black2.png +0 -0
  84. package/server/public/redoc.standalone.js +1832 -0
  85. package/server/public/style.css +211 -17
  86. package/server/public/swagger-ui-bundle.js +2 -0
  87. package/server/public/swagger-ui.css +3 -0
  88. package/server/public/x.js +63 -0
  89. package/server/socket.js +136 -39
  90. package/server/views/404.ejs +0 -1
  91. package/server/views/app.ejs +356 -90
  92. package/server/views/bootstrap.ejs +22 -19
  93. package/server/views/connect/x.ejs +324 -0
  94. package/server/views/connect.ejs +197 -0
  95. package/server/views/download.ejs +7 -1
  96. package/server/views/editor.ejs +68 -9
  97. package/server/views/explore.ejs +13 -10
  98. package/server/views/file_explorer.ejs +10 -37
  99. package/server/views/github.ejs +218 -0
  100. package/server/views/help.ejs +4 -3
  101. package/server/views/index.ejs +268 -118
  102. package/server/views/install.ejs +56 -29
  103. package/server/views/keys.ejs +276 -0
  104. package/server/views/network.ejs +889 -0
  105. package/server/views/partials/dynamic.ejs +9 -0
  106. package/server/views/partials/menu.ejs +7 -1
  107. package/server/views/prototype/index.ejs +311 -0
  108. package/server/views/prototype/show.ejs +383 -0
  109. package/server/views/redoc.ejs +18 -0
  110. package/server/views/required_env_editor.ejs +11 -2
  111. package/server/views/settings.ejs +38 -10
  112. package/server/views/setup.ejs +254 -0
  113. package/server/views/setup_home.ejs +192 -0
  114. package/server/views/shell.ejs +781 -0
  115. package/server/views/swaggerui.ejs +19 -0
  116. package/server/views/terminal.ejs +174 -42
  117. package/LICENSE +0 -7
  118. 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
- //schema = ">=1.0.0"
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
- this.store = store
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.info = new Info(this)
54
- this.pipe = new Pipe(this)
55
- this.cloudflare = new Cloudflare()
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: 42003 })
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
- let p = path.resolve(this.api.userdir, folder, "pinokio.js")
317
- let pinokio = (await this.loader.load(p)).resolved
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
- return this.kernel.api.running[resolved_path]
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
- return fs.existsSync(resolved_path)
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 error", filepath, e)
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 error", filepath, e)
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;