pinokiod 3.86.0 → 3.87.0
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/Dockerfile +61 -0
- package/docker-entrypoint.sh +75 -0
- package/kernel/api/hf/index.js +1 -1
- package/kernel/api/index.js +1 -1
- package/kernel/api/shell/index.js +6 -0
- package/kernel/api/terminal/index.js +166 -0
- package/kernel/bin/conda.js +3 -2
- package/kernel/bin/index.js +53 -2
- package/kernel/bin/setup.js +32 -0
- package/kernel/bin/vs.js +11 -2
- package/kernel/index.js +42 -2
- package/kernel/info.js +36 -0
- package/kernel/peer.js +42 -15
- package/kernel/router/index.js +23 -15
- package/kernel/router/localhost_static_router.js +0 -3
- package/kernel/router/pinokio_domain_router.js +333 -0
- package/kernel/shells.js +21 -1
- package/kernel/util.js +2 -2
- package/package.json +2 -1
- package/script/install-mode.js +33 -0
- package/script/pinokio.json +7 -0
- package/server/index.js +513 -173
- package/server/public/Socket.js +48 -0
- package/server/public/common.js +1441 -276
- package/server/public/fseditor.js +71 -12
- package/server/public/install.js +1 -1
- package/server/public/layout.js +740 -0
- package/server/public/modalinput.js +0 -1
- package/server/public/style.css +97 -105
- package/server/public/tab-idle-notifier.js +629 -0
- package/server/public/terminal_input_tracker.js +63 -0
- package/server/public/urldropdown.css +319 -53
- package/server/public/urldropdown.js +615 -159
- package/server/public/window_storage.js +97 -28
- package/server/socket.js +40 -9
- package/server/views/500.ejs +2 -2
- package/server/views/app.ejs +3136 -1367
- package/server/views/bookmarklet.ejs +1 -1
- package/server/views/bootstrap.ejs +1 -1
- package/server/views/columns.ejs +2 -13
- package/server/views/connect.ejs +3 -4
- package/server/views/container.ejs +1 -2
- package/server/views/d.ejs +223 -53
- package/server/views/editor.ejs +1 -1
- package/server/views/file_explorer.ejs +1 -1
- package/server/views/index.ejs +12 -11
- package/server/views/index2.ejs +4 -4
- package/server/views/init/index.ejs +4 -5
- package/server/views/install.ejs +1 -1
- package/server/views/layout.ejs +105 -0
- package/server/views/net.ejs +39 -7
- package/server/views/network.ejs +20 -6
- package/server/views/network2.ejs +1 -1
- package/server/views/old_network.ejs +2 -2
- package/server/views/partials/dynamic.ejs +3 -5
- package/server/views/partials/menu.ejs +3 -5
- package/server/views/partials/running.ejs +1 -1
- package/server/views/pro.ejs +1 -1
- package/server/views/prototype/index.ejs +1 -1
- package/server/views/review.ejs +11 -23
- package/server/views/rows.ejs +2 -13
- package/server/views/screenshots.ejs +293 -138
- package/server/views/settings.ejs +3 -4
- package/server/views/setup.ejs +1 -2
- package/server/views/shell.ejs +277 -26
- package/server/views/terminal.ejs +322 -49
- package/server/views/tools.ejs +448 -4
package/server/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
|
+
const querystring = require("querystring");
|
|
2
3
|
const diff = require('diff')
|
|
3
4
|
const kill = require('kill-sync')
|
|
4
5
|
const { isBinaryFile } = require("isbinaryfile");
|
|
@@ -199,39 +200,109 @@ class Server {
|
|
|
199
200
|
let id = `${filepath}?cwd=${cwd}`
|
|
200
201
|
obj.script_id = id
|
|
201
202
|
//if (this.kernel.api.running[filepath]) {
|
|
202
|
-
if (
|
|
203
|
+
if (obj.src.startsWith("/run" + selected_query.plugin)) {
|
|
203
204
|
obj.running = true
|
|
204
205
|
obj.display = "indent"
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
if (key !== "plugin") {
|
|
210
|
-
obj.href = obj.href + "&" + key + "=" + encodeURIComponent(selected_query[key])
|
|
211
|
-
}
|
|
206
|
+
obj.default = true
|
|
207
|
+
for(let key in selected_query) {
|
|
208
|
+
if (key !== "plugin") {
|
|
209
|
+
obj.href = obj.href + "&" + key + "=" + encodeURIComponent(selected_query[key])
|
|
212
210
|
}
|
|
213
211
|
}
|
|
214
212
|
running_dynamic.push(obj)
|
|
213
|
+
} else {
|
|
214
|
+
for(let running_id in this.kernel.api.running) {
|
|
215
|
+
if (running_id.startsWith(id)) {
|
|
216
|
+
let obj2 = structuredClone(obj)
|
|
217
|
+
obj2.running = true
|
|
218
|
+
obj2.display = "indent"
|
|
219
|
+
|
|
220
|
+
const query = running_id.split("?")[1];
|
|
221
|
+
const params = query ? querystring.parse(query) : {};
|
|
222
|
+
|
|
223
|
+
let queryStrippedHref = obj2.href.split("?")[0]
|
|
224
|
+
if (params && Object.keys(params).length > 0) {
|
|
225
|
+
obj2.href = queryStrippedHref + "?" + querystring.stringify(params)
|
|
226
|
+
} else {
|
|
227
|
+
obj2.href = queryStrippedHref
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
obj2.script_id = running_id
|
|
231
|
+
obj2.target = "@" + obj2.href
|
|
232
|
+
obj2.target_full = obj2.href
|
|
233
|
+
|
|
234
|
+
running_dynamic.push(obj2)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
215
237
|
}
|
|
216
238
|
}
|
|
217
239
|
} else if (key === "shell") {
|
|
240
|
+
const appendSession = (value, session) => {
|
|
241
|
+
if (!value || !session) {
|
|
242
|
+
return value
|
|
243
|
+
}
|
|
244
|
+
const hasPrefix = value.startsWith("@")
|
|
245
|
+
const raw = hasPrefix ? value.slice(1) : value
|
|
246
|
+
const [pathPart, queryPart] = raw.split("?")
|
|
247
|
+
const parsed = queryPart ? querystring.parse(queryPart) : {}
|
|
248
|
+
parsed.session = session
|
|
249
|
+
const qs = querystring.stringify(parsed)
|
|
250
|
+
const combined = qs ? `${pathPart}?${qs}` : pathPart
|
|
251
|
+
return hasPrefix ? `@${combined}` : combined
|
|
252
|
+
}
|
|
253
|
+
|
|
218
254
|
let unix_path = Util.p2u(this.kernel.path("api", name))
|
|
219
255
|
let shell_id = this.get_shell_id(unix_path, indexPath, obj[key])
|
|
220
256
|
let decoded_shell_id = decodeURIComponent(shell_id)
|
|
221
257
|
let id = "shell/" + decoded_shell_id
|
|
222
|
-
|
|
258
|
+
|
|
259
|
+
const originalHref = obj.href
|
|
260
|
+
const originalTarget = obj.target
|
|
261
|
+
|
|
262
|
+
const activeShells = (this.kernel.shell && Array.isArray(this.kernel.shell.shells))
|
|
263
|
+
? this.kernel.shell.shells.filter((entry) => {
|
|
264
|
+
if (!entry || !entry.id) {
|
|
265
|
+
return false
|
|
266
|
+
}
|
|
267
|
+
return entry.id === id || entry.id.startsWith(`${id}?session=`)
|
|
268
|
+
})
|
|
269
|
+
: []
|
|
270
|
+
|
|
271
|
+
if (activeShells.length > 0 || selected_query.plugin === id) {
|
|
223
272
|
obj.running = true
|
|
224
273
|
obj.display = "indent"
|
|
225
274
|
if (selected_query.plugin === id) {
|
|
226
275
|
obj.default = true
|
|
227
|
-
console.log("selected_query", selected_query)
|
|
228
276
|
for(let key in selected_query) {
|
|
229
277
|
if (key !== "plugin") {
|
|
230
278
|
obj.href = obj.href + "&" + key + "=" + encodeURIComponent(selected_query[key])
|
|
231
279
|
}
|
|
232
280
|
}
|
|
233
281
|
}
|
|
234
|
-
|
|
282
|
+
|
|
283
|
+
if (activeShells.length === 0) {
|
|
284
|
+
running_dynamic.push(obj)
|
|
285
|
+
} else {
|
|
286
|
+
activeShells.forEach((shellEntry) => {
|
|
287
|
+
const clone = structuredClone(obj)
|
|
288
|
+
clone.running = true
|
|
289
|
+
clone.display = "indent"
|
|
290
|
+
clone.shell_id = shellEntry.id
|
|
291
|
+
const sessionMatch = /[?&]session=([^&]+)/.exec(shellEntry.id)
|
|
292
|
+
if (sessionMatch && sessionMatch[1]) {
|
|
293
|
+
const sessionValue = sessionMatch[1]
|
|
294
|
+
clone.href = appendSession(originalHref, sessionValue)
|
|
295
|
+
const baseTarget = originalTarget || `@${originalHref}`
|
|
296
|
+
clone.target = appendSession(baseTarget, sessionValue)
|
|
297
|
+
clone.target_full = clone.href
|
|
298
|
+
} else {
|
|
299
|
+
clone.href = originalHref
|
|
300
|
+
clone.target = originalTarget ? originalTarget : `@${clone.href}`
|
|
301
|
+
clone.target_full = clone.href
|
|
302
|
+
}
|
|
303
|
+
running_dynamic.push(clone)
|
|
304
|
+
})
|
|
305
|
+
}
|
|
235
306
|
}
|
|
236
307
|
}
|
|
237
308
|
traverse(obj[key], indexPath);
|
|
@@ -373,7 +444,6 @@ class Server {
|
|
|
373
444
|
let p = this.kernel.path("api", name)
|
|
374
445
|
let html_path = path.resolve(p, "index.html")
|
|
375
446
|
let html_exists = await this.kernel.exists(html_path)
|
|
376
|
-
console.log({ html_path, html_exists })
|
|
377
447
|
if (html_exists) {
|
|
378
448
|
return Object.assign({
|
|
379
449
|
title: name,
|
|
@@ -381,7 +451,7 @@ class Server {
|
|
|
381
451
|
default: true,
|
|
382
452
|
icon: "fa-solid fa-link",
|
|
383
453
|
text: "index.html",
|
|
384
|
-
href:
|
|
454
|
+
href: `/asset/api/${name}/index.html`,
|
|
385
455
|
}]
|
|
386
456
|
}, cfg)
|
|
387
457
|
} else {
|
|
@@ -399,6 +469,20 @@ class Server {
|
|
|
399
469
|
async getGit(ref, filepath) {
|
|
400
470
|
let dir = this.kernel.path("api", filepath)
|
|
401
471
|
let branches = await git.listBranches({ fs, dir });
|
|
472
|
+
// no branch, means no git repo => initialize
|
|
473
|
+
if (branches.length === 0) {
|
|
474
|
+
return {}
|
|
475
|
+
// const defaultBranch = 'main';
|
|
476
|
+
// await git.init({ fs, dir, defaultBranch });
|
|
477
|
+
// branches = await git.listBranches({ fs, dir });
|
|
478
|
+
// const current = await git.currentBranch({ fs, dir, fullname: false }) || defaultBranch;
|
|
479
|
+
// if (!branches.includes(current)) {
|
|
480
|
+
// branches.unshift(current);
|
|
481
|
+
// }
|
|
482
|
+
// if (!ref || ref === 'HEAD') {
|
|
483
|
+
// ref = current;
|
|
484
|
+
// }
|
|
485
|
+
}
|
|
402
486
|
let log = []
|
|
403
487
|
try {
|
|
404
488
|
log = await git.log({ fs, dir, depth: 50, ref: ref }); // fetch last 50 commits
|
|
@@ -579,7 +663,6 @@ class Server {
|
|
|
579
663
|
}
|
|
580
664
|
}
|
|
581
665
|
|
|
582
|
-
|
|
583
666
|
let uri = this.kernel.path("api")
|
|
584
667
|
try {
|
|
585
668
|
let launcher = await this.kernel.api.launcher(name)
|
|
@@ -625,30 +708,30 @@ class Server {
|
|
|
625
708
|
}
|
|
626
709
|
const env = await this.kernel.env("api/" + name)
|
|
627
710
|
|
|
628
|
-
// profile + feed
|
|
629
|
-
const repositoryPath = path.resolve(this.kernel.api.userdir, name)
|
|
630
|
-
|
|
631
|
-
try {
|
|
632
|
-
await git.resolveRef({ fs, dir: repositoryPath, ref: 'HEAD' });
|
|
633
|
-
} catch (err) {
|
|
634
|
-
// repo doesn't exist. initialize.
|
|
635
|
-
console.log(`repo doesn't exist at ${repositoryPath}. initialize`)
|
|
636
|
-
await git.init({ fs, dir: repositoryPath });
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
let gitRemote = await git.getConfig({ fs, http, dir: repositoryPath, path: 'remote.origin.url' })
|
|
640
|
-
let profile
|
|
641
|
-
let feed
|
|
642
|
-
if (gitRemote) {
|
|
643
|
-
gitRemote = gitRemote.replace(/\.git$/i, '')
|
|
644
|
-
|
|
645
|
-
let system_env = {}
|
|
646
|
-
if (this.kernel.homedir) {
|
|
647
|
-
system_env = await Environment.get(this.kernel.homedir, this.kernel)
|
|
648
|
-
}
|
|
649
|
-
profile = this.profile(gitRemote)
|
|
650
|
-
feed = this.newsfeed(gitRemote)
|
|
651
|
-
}
|
|
711
|
+
// // profile + feed
|
|
712
|
+
// const repositoryPath = path.resolve(this.kernel.api.userdir, name)
|
|
713
|
+
//
|
|
714
|
+
// try {
|
|
715
|
+
// await git.resolveRef({ fs, dir: repositoryPath, ref: 'HEAD' });
|
|
716
|
+
// } catch (err) {
|
|
717
|
+
// // repo doesn't exist. initialize.
|
|
718
|
+
// console.log(`repo doesn't exist at ${repositoryPath}. initialize`)
|
|
719
|
+
// await git.init({ fs, dir: repositoryPath });
|
|
720
|
+
// }
|
|
721
|
+
//
|
|
722
|
+
// let gitRemote = await git.getConfig({ fs, http, dir: repositoryPath, path: 'remote.origin.url' })
|
|
723
|
+
// let profile
|
|
724
|
+
// let feed
|
|
725
|
+
// if (gitRemote) {
|
|
726
|
+
// gitRemote = gitRemote.replace(/\.git$/i, '')
|
|
727
|
+
//
|
|
728
|
+
// let system_env = {}
|
|
729
|
+
// if (this.kernel.homedir) {
|
|
730
|
+
// system_env = await Environment.get(this.kernel.homedir, this.kernel)
|
|
731
|
+
// }
|
|
732
|
+
// profile = this.profile(gitRemote)
|
|
733
|
+
// feed = this.newsfeed(gitRemote)
|
|
734
|
+
// }
|
|
652
735
|
|
|
653
736
|
// git
|
|
654
737
|
|
|
@@ -727,8 +810,8 @@ class Server {
|
|
|
727
810
|
// dynamic: "/pinokio/dynamic/" + name,
|
|
728
811
|
dynamic_content: null,
|
|
729
812
|
name,
|
|
730
|
-
profile,
|
|
731
|
-
feed,
|
|
813
|
+
// profile,
|
|
814
|
+
// feed,
|
|
732
815
|
tabs: (this.tabs[name] || []),
|
|
733
816
|
config,
|
|
734
817
|
// sidebar_url: "/pinokio/sidebar/" + name,
|
|
@@ -1442,9 +1525,51 @@ class Server {
|
|
|
1442
1525
|
}
|
|
1443
1526
|
// check if there is a running process with this folder name
|
|
1444
1527
|
let runningApps = new Set()
|
|
1528
|
+
const item_path = this.kernel.path("api", items[i].name)
|
|
1529
|
+
const normalizedItemPath = path.normalize(item_path)
|
|
1530
|
+
const itemPathWithSep = normalizedItemPath.endsWith(path.sep)
|
|
1531
|
+
? normalizedItemPath
|
|
1532
|
+
: normalizedItemPath + path.sep
|
|
1533
|
+
const unix_item_path = Util.p2u(item_path)
|
|
1534
|
+
const shellPrefix = "shell/" + unix_item_path + "_"
|
|
1535
|
+
const matchesShell = (candidate) => {
|
|
1536
|
+
if (!candidate) return false
|
|
1537
|
+
const idMatches = typeof candidate.id === "string" && candidate.id.startsWith(shellPrefix)
|
|
1538
|
+
const shellPath = typeof candidate.path === "string" ? path.normalize(candidate.path) : null
|
|
1539
|
+
const groupPath = typeof candidate.group === "string" ? path.normalize(candidate.group) : null
|
|
1540
|
+
const parentCwd = candidate.params && candidate.params.$parent && typeof candidate.params.$parent.cwd === "string"
|
|
1541
|
+
? path.normalize(candidate.params.$parent.cwd)
|
|
1542
|
+
: null
|
|
1543
|
+
const paramsCwd = candidate.params && typeof candidate.params.cwd === "string"
|
|
1544
|
+
? path.normalize(candidate.params.cwd)
|
|
1545
|
+
: null
|
|
1546
|
+
const pathMatches = shellPath && (shellPath === normalizedItemPath || shellPath.startsWith(itemPathWithSep))
|
|
1547
|
+
const groupMatches = groupPath && (groupPath === normalizedItemPath || groupPath.startsWith(itemPathWithSep))
|
|
1548
|
+
const parentMatches = parentCwd && (parentCwd === normalizedItemPath || parentCwd.startsWith(itemPathWithSep))
|
|
1549
|
+
const paramsMatches = paramsCwd && (paramsCwd === normalizedItemPath || paramsCwd.startsWith(itemPathWithSep))
|
|
1550
|
+
return idMatches || pathMatches || groupMatches || parentMatches || paramsMatches
|
|
1551
|
+
}
|
|
1552
|
+
const shellMatches = (this.kernel.shell && typeof this.kernel.shell.find === "function")
|
|
1553
|
+
? this.kernel.shell.find({ filter: matchesShell })
|
|
1554
|
+
: []
|
|
1555
|
+
const addShellEntries = () => {
|
|
1556
|
+
if (!shellMatches || shellMatches.length === 0) {
|
|
1557
|
+
return
|
|
1558
|
+
}
|
|
1559
|
+
if (!items[i].running_scripts) {
|
|
1560
|
+
items[i].running_scripts = []
|
|
1561
|
+
}
|
|
1562
|
+
for (const sh of shellMatches) {
|
|
1563
|
+
if (!sh || !sh.id) continue
|
|
1564
|
+
const exists = items[i].running_scripts.some((entry) => entry && entry.id === sh.id)
|
|
1565
|
+
if (!exists) {
|
|
1566
|
+
items[i].running_scripts.push({ id: sh.id, name: sh.params.$title || "Shell", type: "shell" })
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1445
1570
|
for(let key in this.kernel.api.running) {
|
|
1446
1571
|
//let p = this.kernel.path("api", items[i].name) + path.sep
|
|
1447
|
-
let p =
|
|
1572
|
+
let p = item_path
|
|
1448
1573
|
|
|
1449
1574
|
// not only should include the pattern, but also end with it (otherwise can include similar patterns such as /api/qqqa, /api/qqqaaa, etc.
|
|
1450
1575
|
|
|
@@ -1468,7 +1593,6 @@ class Server {
|
|
|
1468
1593
|
if (chunks.length > 1) {
|
|
1469
1594
|
let folder = chunks[0]
|
|
1470
1595
|
/// if the folder name matches, it's running
|
|
1471
|
-
let item_path = this.kernel.path("api", items[i].name)
|
|
1472
1596
|
if (item_path === folder) {
|
|
1473
1597
|
is_running = true
|
|
1474
1598
|
}
|
|
@@ -1485,12 +1609,15 @@ class Server {
|
|
|
1485
1609
|
//if (key.includes(p) && key.endsWith(p)) {
|
|
1486
1610
|
if (is_running) {
|
|
1487
1611
|
// add to running
|
|
1488
|
-
|
|
1612
|
+
if (!items[i].running) {
|
|
1613
|
+
running.push(items[i])
|
|
1614
|
+
items[i].running = true
|
|
1615
|
+
items[i].index = index
|
|
1616
|
+
index++
|
|
1617
|
+
}
|
|
1489
1618
|
if (!items[i].running_scripts) {
|
|
1490
1619
|
items[i].running_scripts = []
|
|
1491
1620
|
}
|
|
1492
|
-
items[i].running = true
|
|
1493
|
-
items[i].index = index
|
|
1494
1621
|
|
|
1495
1622
|
// add the running script to running_scripts array
|
|
1496
1623
|
// 1. normal api script
|
|
@@ -1511,24 +1638,17 @@ class Server {
|
|
|
1511
1638
|
items[i].running_scripts.push({ id: key, name })
|
|
1512
1639
|
}
|
|
1513
1640
|
} else {
|
|
1514
|
-
|
|
1515
|
-
filter: (shell) => {
|
|
1516
|
-
let item_path = this.kernel.path("api", items[i].name)
|
|
1517
|
-
let unix_item_path = Util.p2u(item_path)
|
|
1518
|
-
return shell.id.startsWith("shell/" + unix_item_path + "_")
|
|
1519
|
-
}
|
|
1520
|
-
})
|
|
1521
|
-
if (shell.length > 0) {
|
|
1522
|
-
items[i].running = true
|
|
1523
|
-
items[i].index = index
|
|
1524
|
-
for(let sh of shell) {
|
|
1525
|
-
items[i].running_scripts.push({ id: sh.id, name: "Terminal", type: "shell" })
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1641
|
+
addShellEntries()
|
|
1528
1642
|
}
|
|
1529
|
-
index++;
|
|
1530
1643
|
}
|
|
1531
1644
|
}
|
|
1645
|
+
if (!items[i].running && shellMatches && shellMatches.length > 0) {
|
|
1646
|
+
running.push(items[i])
|
|
1647
|
+
items[i].running = true
|
|
1648
|
+
items[i].index = index
|
|
1649
|
+
addShellEntries()
|
|
1650
|
+
index++
|
|
1651
|
+
}
|
|
1532
1652
|
if (!items[i].running) {
|
|
1533
1653
|
items[i].index = index
|
|
1534
1654
|
index++;
|
|
@@ -1874,15 +1994,19 @@ class Server {
|
|
|
1874
1994
|
// }
|
|
1875
1995
|
menuitem.href = "/shell/" + shell_id + "?" + params.toString()
|
|
1876
1996
|
let decoded_shell_id = decodeURIComponent(shell_id)
|
|
1877
|
-
|
|
1878
|
-
|
|
1997
|
+
const shellPrefixId = "shell/" + decoded_shell_id
|
|
1998
|
+
let shell = this.kernel.shell.get(shellPrefixId)
|
|
1999
|
+
if (!shell && this.kernel.shell && Array.isArray(this.kernel.shell.shells)) {
|
|
2000
|
+
shell = this.kernel.shell.shells.find((entry) => {
|
|
2001
|
+
if (!entry || !entry.id) {
|
|
2002
|
+
return false
|
|
2003
|
+
}
|
|
2004
|
+
return entry.id === shellPrefixId || entry.id.startsWith(`${shellPrefixId}?session=`)
|
|
2005
|
+
})
|
|
2006
|
+
}
|
|
2007
|
+
menuitem.shell_id = shellPrefixId
|
|
1879
2008
|
if (shell) {
|
|
1880
2009
|
menuitem.running = true
|
|
1881
|
-
} else {
|
|
1882
|
-
let shell = this.kernel.shell.get(decoded_shell_id)
|
|
1883
|
-
if (shell) {
|
|
1884
|
-
menuitem.running = true
|
|
1885
|
-
}
|
|
1886
2010
|
}
|
|
1887
2011
|
}
|
|
1888
2012
|
return menuitem
|
|
@@ -2001,10 +2125,11 @@ class Server {
|
|
|
2001
2125
|
} else if (menuitem.href.startsWith("/")) {
|
|
2002
2126
|
let run_path = "/run"
|
|
2003
2127
|
if (menuitem.href.startsWith(run_path)) {
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2128
|
+
menuitem.src = menuitem.href
|
|
2129
|
+
// u = new URL("http://localhost" + menuitem.href.slice(run_path.length))
|
|
2130
|
+
// cwd = u.searchParams.get("cwd")
|
|
2131
|
+
// u.search = ""
|
|
2132
|
+
// menuitem.src = u.pathname
|
|
2008
2133
|
} else {
|
|
2009
2134
|
u = new URL("http://localhost" + menuitem.href)
|
|
2010
2135
|
cwd = u.searchParams.get("cwd")
|
|
@@ -2143,16 +2268,19 @@ class Server {
|
|
|
2143
2268
|
if (config.menu[i].popout) {
|
|
2144
2269
|
config.menu[i].target = "_blank"
|
|
2145
2270
|
} else {
|
|
2146
|
-
config.menu[i].
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
if (config.menu[i].href && config.menu[i].href.startsWith("http")) {
|
|
2151
|
-
if (req.agent !== "electron") {
|
|
2152
|
-
config.menu[i].target = "_blank"
|
|
2271
|
+
const targetBase = config.menu[i].id || config.menu[i].src || config.menu[i].href
|
|
2272
|
+
config.menu[i].target = targetBase ? "@" + targetBase : undefined
|
|
2273
|
+
if (config.menu[i].href) {
|
|
2274
|
+
config.menu[i].target_full = config.menu[i].href
|
|
2153
2275
|
}
|
|
2154
2276
|
}
|
|
2155
2277
|
|
|
2278
|
+
//if (config.menu[i].href && config.menu[i].href.startsWith("http")) {
|
|
2279
|
+
// if (req.agent !== "electron") {
|
|
2280
|
+
// config.menu[i].target = "_blank"
|
|
2281
|
+
// }
|
|
2282
|
+
//}
|
|
2283
|
+
|
|
2156
2284
|
if (menuitem.shell_id) {
|
|
2157
2285
|
config.menu[i].shell_id = menuitem.shell_id
|
|
2158
2286
|
}
|
|
@@ -2478,7 +2606,7 @@ class Server {
|
|
|
2478
2606
|
// }
|
|
2479
2607
|
}
|
|
2480
2608
|
async setConfig(config) {
|
|
2481
|
-
let home = this.kernel.store.get("home")
|
|
2609
|
+
let home = this.kernel.store.get("home") || process.env.PINOKIO_HOME
|
|
2482
2610
|
let theme = this.kernel.store.get("theme")
|
|
2483
2611
|
let mode = this.kernel.store.get("mode")
|
|
2484
2612
|
// let drive = this.kernel.store.get("drive")
|
|
@@ -2543,9 +2671,9 @@ class Server {
|
|
|
2543
2671
|
// }
|
|
2544
2672
|
|
|
2545
2673
|
|
|
2546
|
-
home = this.kernel.store.get("home")
|
|
2674
|
+
home = this.kernel.store.get("home") || process.env.PINOKIO_HOME
|
|
2547
2675
|
theme = this.kernel.store.get("theme")
|
|
2548
|
-
let new_home = this.kernel.store.get("new_home")
|
|
2676
|
+
let new_home = this.kernel.store.get("new_home") || process.env.PINOKIO_HOME
|
|
2549
2677
|
|
|
2550
2678
|
// Handle environment variables
|
|
2551
2679
|
// HTTP_PROXY
|
|
@@ -2618,6 +2746,66 @@ class Server {
|
|
|
2618
2746
|
return true
|
|
2619
2747
|
}
|
|
2620
2748
|
}
|
|
2749
|
+
add_extra_urls(info) {
|
|
2750
|
+
// get only the parts not from this peer
|
|
2751
|
+
for(let host in this.kernel.peer.info) {
|
|
2752
|
+
let host_info = this.kernel.peer.info[host]
|
|
2753
|
+
|
|
2754
|
+
let host_rewrites = host_info.rewrite_mapping
|
|
2755
|
+
for(let key in host_rewrites) {
|
|
2756
|
+
info.push({
|
|
2757
|
+
online: true,
|
|
2758
|
+
host: {
|
|
2759
|
+
ip: host,
|
|
2760
|
+
local: this.kernel.peer.host === host,
|
|
2761
|
+
name: host_info.name,
|
|
2762
|
+
platform: host_info.platform,
|
|
2763
|
+
arch: host_info.arch
|
|
2764
|
+
},
|
|
2765
|
+
name: `[Files] ${host_rewrites[key].name}`,
|
|
2766
|
+
ip: host_rewrites[key].external_ip
|
|
2767
|
+
})
|
|
2768
|
+
}
|
|
2769
|
+
if (this.kernel.peer.host !== host) {
|
|
2770
|
+
let host_routers = host_info.router_info
|
|
2771
|
+
for(let host_router of host_routers) {
|
|
2772
|
+
let ip
|
|
2773
|
+
// the peer sharing works only if external_ip is available (caddy is installed)
|
|
2774
|
+
if (host_router.external_ip) {
|
|
2775
|
+
ip = host_router.external_ip
|
|
2776
|
+
} else {
|
|
2777
|
+
// if caddy is not turned on, set ip as null, so that it suggests turning on peer sharing
|
|
2778
|
+
ip = null
|
|
2779
|
+
}
|
|
2780
|
+
info.push({
|
|
2781
|
+
online: true,
|
|
2782
|
+
host: {
|
|
2783
|
+
ip: host,
|
|
2784
|
+
name: host_info.name,
|
|
2785
|
+
platform: host_info.platform,
|
|
2786
|
+
arch: host_info.arch
|
|
2787
|
+
},
|
|
2788
|
+
name: host_router.title || host_router.name,
|
|
2789
|
+
ip
|
|
2790
|
+
})
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2794
|
+
for(let app of host_info.installed) {
|
|
2795
|
+
info.push({
|
|
2796
|
+
host: {
|
|
2797
|
+
ip: host,
|
|
2798
|
+
local: this.kernel.peer.host === host,
|
|
2799
|
+
name: host_info.name,
|
|
2800
|
+
platform: host_info.platform,
|
|
2801
|
+
arch: host_info.arch
|
|
2802
|
+
},
|
|
2803
|
+
name: app.title || app.folder,
|
|
2804
|
+
ip: app.http_href.replace("http://", "")
|
|
2805
|
+
})
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2621
2809
|
async terminals(filepath) {
|
|
2622
2810
|
let venvs = await Util.find_venv(filepath)
|
|
2623
2811
|
let terminal
|
|
@@ -2644,14 +2832,14 @@ class Server {
|
|
|
2644
2832
|
}
|
|
2645
2833
|
terminal = {
|
|
2646
2834
|
icon: "fa-solid fa-terminal",
|
|
2647
|
-
title: "
|
|
2835
|
+
title: "Terminal",
|
|
2648
2836
|
subtitle: "Open the terminal in the browser",
|
|
2649
2837
|
menu: terminals
|
|
2650
2838
|
}
|
|
2651
2839
|
} else {
|
|
2652
2840
|
terminal = {
|
|
2653
2841
|
icon: "fa-solid fa-terminal",
|
|
2654
|
-
title: "
|
|
2842
|
+
title: "Terminal",
|
|
2655
2843
|
subtitle: "Work with the terminal directly in the browser",
|
|
2656
2844
|
menu: [this.renderShell(filepath, 0, 0, {
|
|
2657
2845
|
icon: "fa-solid fa-terminal",
|
|
@@ -2833,7 +3021,7 @@ class Server {
|
|
|
2833
3021
|
await this.syncConfig()
|
|
2834
3022
|
|
|
2835
3023
|
try {
|
|
2836
|
-
let _home = this.kernel.store.get("home")
|
|
3024
|
+
let _home = this.kernel.store.get("home") || process.env.PINOKIO_HOME
|
|
2837
3025
|
if (_home) {
|
|
2838
3026
|
await this.startLogging(_home)
|
|
2839
3027
|
}
|
|
@@ -2869,7 +3057,7 @@ class Server {
|
|
|
2869
3057
|
}
|
|
2870
3058
|
|
|
2871
3059
|
let version = this.kernel.store.get("version")
|
|
2872
|
-
let home = this.kernel.store.get("home")
|
|
3060
|
+
let home = this.kernel.store.get("home") || process.env.PINOKIO_HOME
|
|
2873
3061
|
|
|
2874
3062
|
let needInitHome = false
|
|
2875
3063
|
if (home) {
|
|
@@ -3181,17 +3369,25 @@ class Server {
|
|
|
3181
3369
|
})
|
|
3182
3370
|
}))
|
|
3183
3371
|
this.app.get("/columns", ex(async (req, res) => {
|
|
3372
|
+
const originSrc = req.query.origin || req.get('Referrer') || '/';
|
|
3373
|
+
const targetSrc = req.query.target || originSrc;
|
|
3184
3374
|
res.render("columns", {
|
|
3185
3375
|
theme: this.theme,
|
|
3186
3376
|
agent: req.agent,
|
|
3187
|
-
|
|
3377
|
+
originSrc,
|
|
3378
|
+
targetSrc,
|
|
3379
|
+
src: originSrc
|
|
3188
3380
|
})
|
|
3189
3381
|
}))
|
|
3190
3382
|
this.app.get("/rows", ex(async (req, res) => {
|
|
3383
|
+
const originSrc = req.query.origin || req.get('Referrer') || '/';
|
|
3384
|
+
const targetSrc = req.query.target || originSrc;
|
|
3191
3385
|
res.render("rows", {
|
|
3192
3386
|
theme: this.theme,
|
|
3193
3387
|
agent: req.agent,
|
|
3194
|
-
|
|
3388
|
+
originSrc,
|
|
3389
|
+
targetSrc,
|
|
3390
|
+
src: originSrc
|
|
3195
3391
|
})
|
|
3196
3392
|
}))
|
|
3197
3393
|
|
|
@@ -3333,7 +3529,7 @@ class Server {
|
|
|
3333
3529
|
link: null
|
|
3334
3530
|
})
|
|
3335
3531
|
}))
|
|
3336
|
-
|
|
3532
|
+
const renderHomePage = ex(async (req, res) => {
|
|
3337
3533
|
// check bin folder
|
|
3338
3534
|
// let bin_path = this.kernel.path("bin/miniconda")
|
|
3339
3535
|
// let bin_exists = await this.exists(bin_path)
|
|
@@ -3350,7 +3546,7 @@ class Server {
|
|
|
3350
3546
|
// }
|
|
3351
3547
|
|
|
3352
3548
|
if (req.query.mode !== "settings" && !home) {
|
|
3353
|
-
res.redirect("
|
|
3549
|
+
res.redirect("/home?mode=settings")
|
|
3354
3550
|
return
|
|
3355
3551
|
}
|
|
3356
3552
|
if (req.query.mode === "help") {
|
|
@@ -3372,7 +3568,6 @@ class Server {
|
|
|
3372
3568
|
return
|
|
3373
3569
|
}
|
|
3374
3570
|
|
|
3375
|
-
|
|
3376
3571
|
if (req.query.mode === 'settings') {
|
|
3377
3572
|
|
|
3378
3573
|
let platform = os.platform()
|
|
@@ -3393,7 +3588,7 @@ class Server {
|
|
|
3393
3588
|
"* NO exFAT drives",
|
|
3394
3589
|
],
|
|
3395
3590
|
val: this.kernel.homedir ? this.kernel.homedir : _home,
|
|
3396
|
-
placeholder: "Enter the absolute path to use as your Pinokio home folder (D
|
|
3591
|
+
placeholder: "Enter the absolute path to use as your Pinokio home folder (D\\pinokio, /Users/alice/pinokiofs, etc.)"
|
|
3397
3592
|
// }, {
|
|
3398
3593
|
// key: "drive",
|
|
3399
3594
|
// val: path.resolve(this.kernel.homedir, "drive"),
|
|
@@ -3466,6 +3661,58 @@ class Server {
|
|
|
3466
3661
|
meta[folder] = await this.kernel.api.meta(folder)
|
|
3467
3662
|
}
|
|
3468
3663
|
await this.render(req, res, [], meta)
|
|
3664
|
+
})
|
|
3665
|
+
|
|
3666
|
+
this.app.get("/", ex(async (req, res) => {
|
|
3667
|
+
const protocol = (req.$source && req.$source.protocol) || req.protocol || 'http'
|
|
3668
|
+
const host = req.get('host') || `localhost:${this.port}`
|
|
3669
|
+
const baseUrl = `${protocol}://${host}`
|
|
3670
|
+
|
|
3671
|
+
const initialUrl = new URL('/home', baseUrl)
|
|
3672
|
+
const defaultUrl = new URL('/home', baseUrl)
|
|
3673
|
+
|
|
3674
|
+
for (const [key, value] of Object.entries(req.query)) {
|
|
3675
|
+
if (key === 'session') {
|
|
3676
|
+
continue
|
|
3677
|
+
}
|
|
3678
|
+
if (Array.isArray(value)) {
|
|
3679
|
+
value.forEach((val) => {
|
|
3680
|
+
initialUrl.searchParams.append(key, val)
|
|
3681
|
+
})
|
|
3682
|
+
} else if (value != null) {
|
|
3683
|
+
initialUrl.searchParams.set(key, value)
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
|
|
3687
|
+
if (!home) {
|
|
3688
|
+
defaultUrl.searchParams.set('mode', 'settings')
|
|
3689
|
+
}
|
|
3690
|
+
|
|
3691
|
+
res.render('layout', {
|
|
3692
|
+
theme: this.theme,
|
|
3693
|
+
agent: req.agent,
|
|
3694
|
+
initialPath: initialUrl.pathname + initialUrl.search + initialUrl.hash,
|
|
3695
|
+
defaultPath: defaultUrl.pathname + defaultUrl.search + defaultUrl.hash,
|
|
3696
|
+
sessionId: typeof req.query.session === 'string' ? req.query.session : null
|
|
3697
|
+
})
|
|
3698
|
+
}))
|
|
3699
|
+
|
|
3700
|
+
this.app.get("/home", renderHomePage)
|
|
3701
|
+
|
|
3702
|
+
|
|
3703
|
+
this.app.get("/bundle/:name", ex(async (req, res) => {
|
|
3704
|
+
let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
|
|
3705
|
+
bin: this.kernel.bin.preset(req.params.name),
|
|
3706
|
+
})
|
|
3707
|
+
if (!requirements_pending && install_required) {
|
|
3708
|
+
res.json({
|
|
3709
|
+
available: false,
|
|
3710
|
+
})
|
|
3711
|
+
} else {
|
|
3712
|
+
res.json({
|
|
3713
|
+
available: true,
|
|
3714
|
+
})
|
|
3715
|
+
}
|
|
3469
3716
|
}))
|
|
3470
3717
|
|
|
3471
3718
|
this.app.get("/init", ex(async (req, res) => {
|
|
@@ -4038,7 +4285,12 @@ class Server {
|
|
|
4038
4285
|
GET /shell/:unix_path => shell id: 'shell/:unix_path'
|
|
4039
4286
|
*/
|
|
4040
4287
|
|
|
4041
|
-
let
|
|
4288
|
+
let baseShellId = "shell/" + decodeURIComponent(req.params.id)
|
|
4289
|
+
const sessionId = typeof req.query.session === "string" && req.query.session.length > 0 ? req.query.session : null
|
|
4290
|
+
let id = baseShellId
|
|
4291
|
+
if (sessionId) {
|
|
4292
|
+
id = `${baseShellId}?session=${sessionId}`
|
|
4293
|
+
}
|
|
4042
4294
|
let target = req.query.target ? req.query.target : null
|
|
4043
4295
|
let cwd = this.kernel.path(this.kernel.api.filePath(decodeURIComponent(req.query.path)))
|
|
4044
4296
|
let message = req.query.message ? decodeURIComponent(req.query.message) : null
|
|
@@ -4304,7 +4556,11 @@ class Server {
|
|
|
4304
4556
|
return serverless_mapping[name]
|
|
4305
4557
|
})
|
|
4306
4558
|
let current_urls = await this.current_urls(req.originalUrl.slice(1))
|
|
4559
|
+
let static_routes = Object.keys(this.kernel.router.rewrite_mapping).map((key) => {
|
|
4560
|
+
return this.kernel.router.rewrite_mapping[key]
|
|
4561
|
+
})
|
|
4307
4562
|
res.render("net", {
|
|
4563
|
+
static_routes,
|
|
4308
4564
|
selected_name: req.params.name,
|
|
4309
4565
|
current_urls,
|
|
4310
4566
|
docs: this.docs,
|
|
@@ -4821,6 +5077,7 @@ class Server {
|
|
|
4821
5077
|
// }))
|
|
4822
5078
|
this.app.post("/env", ex(async (req, res) => {
|
|
4823
5079
|
let fullpath = path.resolve(this.kernel.homedir, req.body.filepath, "ENVIRONMENT")
|
|
5080
|
+
console.log({ fullpath })
|
|
4824
5081
|
let updated = req.body.vals
|
|
4825
5082
|
let hosts = req.body.hosts
|
|
4826
5083
|
await Util.update_env(fullpath, updated)
|
|
@@ -5128,7 +5385,7 @@ class Server {
|
|
|
5128
5385
|
}
|
|
5129
5386
|
}
|
|
5130
5387
|
} catch (err) {
|
|
5131
|
-
console.log("git status matrix error", err)
|
|
5388
|
+
// console.log("git status matrix error 1", err)
|
|
5132
5389
|
}
|
|
5133
5390
|
} else {
|
|
5134
5391
|
try {
|
|
@@ -5187,7 +5444,7 @@ class Server {
|
|
|
5187
5444
|
});
|
|
5188
5445
|
}
|
|
5189
5446
|
} catch (err) {
|
|
5190
|
-
console.log("git diff error", err);
|
|
5447
|
+
console.log("git diff error 2", err);
|
|
5191
5448
|
}
|
|
5192
5449
|
}
|
|
5193
5450
|
let git_commit_url = `/run/scripts/git/commit.json?cwd=${dir}&callback_target=parent&callback=$location.href`
|
|
@@ -5286,22 +5543,15 @@ class Server {
|
|
|
5286
5543
|
}
|
|
5287
5544
|
|
|
5288
5545
|
|
|
5289
|
-
let
|
|
5546
|
+
let response = await this.getGit(req.params.ref, req.params[0])
|
|
5290
5547
|
|
|
5291
5548
|
res.render("git", {
|
|
5292
|
-
config,
|
|
5293
|
-
remote,
|
|
5294
|
-
connected,
|
|
5295
|
-
log,
|
|
5296
|
-
branch,
|
|
5297
|
-
branches,
|
|
5298
|
-
ref,
|
|
5299
5549
|
path: req.params[0],
|
|
5300
5550
|
// changes,
|
|
5301
|
-
dir,
|
|
5302
5551
|
theme: this.theme,
|
|
5303
5552
|
platform: this.kernel.platform,
|
|
5304
5553
|
agent: req.agent,
|
|
5554
|
+
...response
|
|
5305
5555
|
})
|
|
5306
5556
|
}))
|
|
5307
5557
|
this.app.get("/d/*", ex(async (req, res) => {
|
|
@@ -5363,9 +5613,10 @@ class Server {
|
|
|
5363
5613
|
// console.log("online_terminal", online_terminal)
|
|
5364
5614
|
terminal.menus = href_menus
|
|
5365
5615
|
let dynamic = [
|
|
5616
|
+
terminal,
|
|
5366
5617
|
{
|
|
5367
5618
|
icon: "fa-solid fa-robot",
|
|
5368
|
-
title: "AI
|
|
5619
|
+
title: "AI Terminal",
|
|
5369
5620
|
subtitle: "Let AI work on this app",
|
|
5370
5621
|
menu: shell_menus
|
|
5371
5622
|
},
|
|
@@ -5375,7 +5626,6 @@ class Server {
|
|
|
5375
5626
|
subtitle: "Open this project in 3rd party apps",
|
|
5376
5627
|
menu: exec_menus
|
|
5377
5628
|
},
|
|
5378
|
-
terminal
|
|
5379
5629
|
]
|
|
5380
5630
|
let spec = ""
|
|
5381
5631
|
try {
|
|
@@ -5669,12 +5919,122 @@ class Server {
|
|
|
5669
5919
|
// res.json({ success: true })
|
|
5670
5920
|
// }))
|
|
5671
5921
|
|
|
5922
|
+
|
|
5923
|
+
this.app.get("/info/scripts", ex(async (req, res) => {
|
|
5924
|
+
/*
|
|
5925
|
+
returns something like this by using the this.kernel.memory.local variable, extracting the api name and adding all running scripts in each associated array, setting the uri as the script path, and the local variables as the local attribute
|
|
5926
|
+
the api name in the following examples are "comfyui" and "gradio", therefore there are two top level attributes "comfyui" and "gradio", each of which has an array value made up of all scripts running under that api
|
|
5927
|
+
{
|
|
5928
|
+
“comfyui”: [{
|
|
5929
|
+
“uri”: “/data/pinokio/api/comfyui/start.js”,
|
|
5930
|
+
“local”: {
|
|
5931
|
+
"port": 42008,
|
|
5932
|
+
"url": "http://127.0.0.1:42008"
|
|
5933
|
+
}
|
|
5934
|
+
}],
|
|
5935
|
+
“gradio”: [{
|
|
5936
|
+
“uri”: “/data/pinokio/api/gradio/start.js”,
|
|
5937
|
+
“local”: {
|
|
5938
|
+
"port": 7860,
|
|
5939
|
+
"url": "http://127.0.0.1:7860"
|
|
5940
|
+
}
|
|
5941
|
+
}]
|
|
5942
|
+
}
|
|
5943
|
+
*/
|
|
5944
|
+
if (!this.kernel || !this.kernel.info || typeof this.kernel.info.scriptsByApi !== 'function') {
|
|
5945
|
+
res.json({})
|
|
5946
|
+
return
|
|
5947
|
+
}
|
|
5948
|
+
|
|
5949
|
+
const scriptsByApi = this.kernel.info.scriptsByApi()
|
|
5950
|
+
res.json(scriptsByApi)
|
|
5951
|
+
}))
|
|
5952
|
+
this.app.get("/info/local", ex(async (req, res) => {
|
|
5953
|
+
if (this.kernel && this.kernel.memory && this.kernel.memory.local) {
|
|
5954
|
+
res.json(this.kernel.memory.local)
|
|
5955
|
+
} else {
|
|
5956
|
+
res.json({})
|
|
5957
|
+
}
|
|
5958
|
+
}))
|
|
5959
|
+
|
|
5960
|
+
|
|
5672
5961
|
this.app.get("/info/procs", ex(async (req, res) => {
|
|
5673
5962
|
await this.kernel.processes.refresh()
|
|
5674
5963
|
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5964
|
+
const requestedProtocol = ((req.$source && req.$source.protocol) || req.protocol || '').toLowerCase()
|
|
5965
|
+
const preferHttps = requestedProtocol === 'https'
|
|
5966
|
+
|
|
5967
|
+
const routerInfo = (preferHttps && this.kernel.router && this.kernel.router.info && this.kernel.peer)
|
|
5968
|
+
? (this.kernel.router.info[this.kernel.peer.host] || {})
|
|
5969
|
+
: null
|
|
5970
|
+
|
|
5971
|
+
const resolveHttpsHosts = (proc) => {
|
|
5972
|
+
if (!routerInfo) {
|
|
5973
|
+
return []
|
|
5974
|
+
}
|
|
5975
|
+
const possibleKeys = new Set()
|
|
5976
|
+
if (proc.ip) {
|
|
5977
|
+
possibleKeys.add(proc.ip)
|
|
5978
|
+
}
|
|
5979
|
+
if (proc.port) {
|
|
5980
|
+
possibleKeys.add(`127.0.0.1:${proc.port}`)
|
|
5981
|
+
possibleKeys.add(`localhost:${proc.port}`)
|
|
5982
|
+
possibleKeys.add(`0.0.0.0:${proc.port}`)
|
|
5983
|
+
}
|
|
5984
|
+
|
|
5985
|
+
const hosts = new Set()
|
|
5986
|
+
for (const key of possibleKeys) {
|
|
5987
|
+
const matches = routerInfo[key]
|
|
5988
|
+
if (!matches || matches.length === 0) {
|
|
5989
|
+
continue
|
|
5990
|
+
}
|
|
5991
|
+
for (const match of matches) {
|
|
5992
|
+
if (typeof match === 'string' && match.trim().length > 0) {
|
|
5993
|
+
hosts.add(match.trim())
|
|
5994
|
+
}
|
|
5995
|
+
}
|
|
5996
|
+
}
|
|
5997
|
+
return Array.from(hosts)
|
|
5998
|
+
}
|
|
5999
|
+
|
|
6000
|
+
const preferFriendlyHost = (hosts) => {
|
|
6001
|
+
if (!hosts || hosts.length === 0) {
|
|
6002
|
+
return null
|
|
6003
|
+
}
|
|
6004
|
+
for (const host of hosts) {
|
|
6005
|
+
if (!/^\d+\.localhost$/i.test(host)) {
|
|
6006
|
+
return host
|
|
6007
|
+
}
|
|
6008
|
+
}
|
|
6009
|
+
return hosts[0]
|
|
6010
|
+
}
|
|
6011
|
+
|
|
6012
|
+
const info = this.kernel.processes.info.map((item) => {
|
|
6013
|
+
const httpUrl = item.ip ? `http://${item.ip}` : null
|
|
6014
|
+
let httpsHosts = []
|
|
6015
|
+
if (preferHttps) {
|
|
6016
|
+
httpsHosts = resolveHttpsHosts(item)
|
|
6017
|
+
}
|
|
6018
|
+
const httpsUrls = httpsHosts.map((host) => {
|
|
6019
|
+
if (!host) {
|
|
6020
|
+
return null
|
|
6021
|
+
}
|
|
6022
|
+
const trimmed = host.trim()
|
|
6023
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
6024
|
+
return trimmed.replace(/^http:\/\//i, 'https://')
|
|
6025
|
+
}
|
|
6026
|
+
return `https://${trimmed}`
|
|
6027
|
+
}).filter(Boolean)
|
|
6028
|
+
|
|
6029
|
+
const preferredHttpsUrl = preferFriendlyHost(httpsHosts)
|
|
6030
|
+
const displayHttpsUrl = preferredHttpsUrl
|
|
6031
|
+
? (preferredHttpsUrl.startsWith('http') ? preferredHttpsUrl.replace(/^http:/i, 'https:') : `https://${preferredHttpsUrl}`)
|
|
6032
|
+
: (httpsUrls[0] || null)
|
|
6033
|
+
|
|
6034
|
+
const selectedUrl = (preferHttps && displayHttpsUrl) ? displayHttpsUrl : httpUrl
|
|
6035
|
+
const protocol = (preferHttps && displayHttpsUrl) ? 'https' : 'http'
|
|
6036
|
+
|
|
6037
|
+
return {
|
|
5678
6038
|
online: true,
|
|
5679
6039
|
host: {
|
|
5680
6040
|
ip: this.kernel.peer.host,
|
|
@@ -5684,65 +6044,16 @@ class Server {
|
|
|
5684
6044
|
arch: this.kernel.arch,
|
|
5685
6045
|
},
|
|
5686
6046
|
...item,
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
let host_rewrites = host_info.rewrite_mapping
|
|
5693
|
-
for(let key in host_rewrites) {
|
|
5694
|
-
info.push({
|
|
5695
|
-
online: true,
|
|
5696
|
-
host: {
|
|
5697
|
-
ip: host,
|
|
5698
|
-
local: this.kernel.peer.host === host,
|
|
5699
|
-
name: host_info.name,
|
|
5700
|
-
platform: host_info.platform,
|
|
5701
|
-
arch: host_info.arch
|
|
5702
|
-
},
|
|
5703
|
-
name: `[Files] ${host_rewrites[key].name}`,
|
|
5704
|
-
ip: host_rewrites[key].external_ip
|
|
5705
|
-
})
|
|
5706
|
-
}
|
|
5707
|
-
if (this.kernel.peer.host !== host) {
|
|
5708
|
-
let host_routers = host_info.router_info
|
|
5709
|
-
for(let host_router of host_routers) {
|
|
5710
|
-
let ip
|
|
5711
|
-
// the peer sharing works only if external_ip is available (caddy is installed)
|
|
5712
|
-
if (host_router.external_ip) {
|
|
5713
|
-
ip = host_router.external_ip
|
|
5714
|
-
} else {
|
|
5715
|
-
// if caddy is not turned on, set ip as null, so that it suggests turning on peer sharing
|
|
5716
|
-
ip = null
|
|
5717
|
-
}
|
|
5718
|
-
info.push({
|
|
5719
|
-
online: true,
|
|
5720
|
-
host: {
|
|
5721
|
-
ip: host,
|
|
5722
|
-
name: host_info.name,
|
|
5723
|
-
platform: host_info.platform,
|
|
5724
|
-
arch: host_info.arch
|
|
5725
|
-
},
|
|
5726
|
-
name: host_router.title || host_router.name,
|
|
5727
|
-
ip
|
|
5728
|
-
})
|
|
6047
|
+
url: selectedUrl,
|
|
6048
|
+
protocol,
|
|
6049
|
+
urls: {
|
|
6050
|
+
http: httpUrl,
|
|
6051
|
+
https: httpsUrls
|
|
5729
6052
|
}
|
|
5730
6053
|
}
|
|
6054
|
+
})
|
|
5731
6055
|
|
|
5732
|
-
|
|
5733
|
-
info.push({
|
|
5734
|
-
host: {
|
|
5735
|
-
ip: host,
|
|
5736
|
-
local: this.kernel.peer.host === host,
|
|
5737
|
-
name: host_info.name,
|
|
5738
|
-
platform: host_info.platform,
|
|
5739
|
-
arch: host_info.arch
|
|
5740
|
-
},
|
|
5741
|
-
name: app.title || app.folder,
|
|
5742
|
-
ip: app.http_href.replace("http://", "")
|
|
5743
|
-
})
|
|
5744
|
-
}
|
|
5745
|
-
}
|
|
6056
|
+
// this.add_extra_urls(info)
|
|
5746
6057
|
res.json({
|
|
5747
6058
|
info
|
|
5748
6059
|
})
|
|
@@ -5782,6 +6093,10 @@ class Server {
|
|
|
5782
6093
|
})
|
|
5783
6094
|
}
|
|
5784
6095
|
}))
|
|
6096
|
+
this.app.get("/info/shells", ex(async (req,res) => {
|
|
6097
|
+
let shells = this.kernel.shell.info()
|
|
6098
|
+
res.json(shells)
|
|
6099
|
+
}))
|
|
5785
6100
|
this.app.get("/info/api/:name", ex(async (req,res) => {
|
|
5786
6101
|
// api related info
|
|
5787
6102
|
let c = this.kernel.path("api", req.params.name)
|
|
@@ -5865,7 +6180,7 @@ class Server {
|
|
|
5865
6180
|
title: name,
|
|
5866
6181
|
url: gitRemote,
|
|
5867
6182
|
//redirect_uri: "http://localhost:3001/apps/redirect?git=" + gitRemote,
|
|
5868
|
-
redirect_uri: "https://app
|
|
6183
|
+
redirect_uri: "https://app.pinokio.co/apps/redirect?git=" + gitRemote,
|
|
5869
6184
|
platform: this.kernel.platform,
|
|
5870
6185
|
theme: this.theme,
|
|
5871
6186
|
agent: req.agent,
|
|
@@ -5987,7 +6302,7 @@ class Server {
|
|
|
5987
6302
|
}))
|
|
5988
6303
|
this.app.get("/pinokio/download", ex((req, res) => {
|
|
5989
6304
|
let queryStr = new URLSearchParams(req.query).toString()
|
|
5990
|
-
res.redirect("
|
|
6305
|
+
res.redirect("/home?mode=download&" + queryStr)
|
|
5991
6306
|
}))
|
|
5992
6307
|
this.app.post("/pinokio/install", ex((req, res) => {
|
|
5993
6308
|
req.session.requirements = req.body.requirements
|
|
@@ -6131,6 +6446,31 @@ class Server {
|
|
|
6131
6446
|
res.status(404).send("Missing attribute: path")
|
|
6132
6447
|
}
|
|
6133
6448
|
}))
|
|
6449
|
+
const ensureCaptureDir = async () => {
|
|
6450
|
+
await fs.promises.mkdir(this.kernel.path("screenshots"), { recursive: true }).catch(() => {});
|
|
6451
|
+
};
|
|
6452
|
+
|
|
6453
|
+
const saveCaptureFiles = async (files, fallbackExt = '.png') => {
|
|
6454
|
+
await ensureCaptureDir();
|
|
6455
|
+
const saved = [];
|
|
6456
|
+
if (Array.isArray(files)) {
|
|
6457
|
+
for (const file of files) {
|
|
6458
|
+
if (!file || !file.buffer) continue;
|
|
6459
|
+
const origName = file.originalname || '';
|
|
6460
|
+
let ext = path.extname(origName);
|
|
6461
|
+
if (!ext && file.mimetype) {
|
|
6462
|
+
const mapped = mime.extension(file.mimetype);
|
|
6463
|
+
if (mapped) ext = `.${mapped}`;
|
|
6464
|
+
}
|
|
6465
|
+
if (!ext) ext = fallbackExt;
|
|
6466
|
+
const name = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`;
|
|
6467
|
+
await fs.promises.writeFile(this.kernel.path("screenshots", name), file.buffer);
|
|
6468
|
+
saved.push({ name, url: `/asset/screenshots/${name}` });
|
|
6469
|
+
}
|
|
6470
|
+
}
|
|
6471
|
+
return saved;
|
|
6472
|
+
};
|
|
6473
|
+
|
|
6134
6474
|
this.app.get("/snapshots", ex(async (req, res) => {
|
|
6135
6475
|
let files = []
|
|
6136
6476
|
try {
|
|
@@ -6172,13 +6512,13 @@ class Server {
|
|
|
6172
6512
|
res.status(500).json({ error: "Failed to delete file: " + e.message })
|
|
6173
6513
|
}
|
|
6174
6514
|
}))
|
|
6515
|
+
this.app.post("/capture", this.upload.any(), ex(async (req, res) => {
|
|
6516
|
+
const saved = await saveCaptureFiles(req.files);
|
|
6517
|
+
res.json({ saved });
|
|
6518
|
+
}))
|
|
6175
6519
|
this.app.post("/screenshot", this.upload.any(), ex(async (req, res) => {
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
let file = req.files[key]
|
|
6179
|
-
let ts = String(Date.now()) + ".png"
|
|
6180
|
-
await fs.promises.writeFile(this.kernel.path("screenshots", ts), file.buffer)
|
|
6181
|
-
}
|
|
6520
|
+
const saved = await saveCaptureFiles(req.files);
|
|
6521
|
+
res.json({ saved });
|
|
6182
6522
|
}))
|
|
6183
6523
|
this.app.post("/pinokio/fs", this.upload.any(), ex(async (req, res) => {
|
|
6184
6524
|
/*
|
|
@@ -6360,7 +6700,7 @@ class Server {
|
|
|
6360
6700
|
ready = false;
|
|
6361
6701
|
}
|
|
6362
6702
|
if (ready) {
|
|
6363
|
-
res.json({ success: true })
|
|
6703
|
+
res.json({ success: true, peer_name: this.kernel.peer.name })
|
|
6364
6704
|
} else {
|
|
6365
6705
|
res.json({ success: false })
|
|
6366
6706
|
}
|