pinokiod 3.180.0 → 3.182.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/kernel/favicon.js +91 -34
- package/kernel/peer.js +73 -0
- package/kernel/util.js +28 -4
- package/package.json +1 -1
- package/server/index.js +237 -35
- package/server/public/common.js +677 -240
- package/server/public/files-app/app.css +64 -0
- package/server/public/files-app/app.js +87 -0
- package/server/public/install.js +8 -1
- package/server/public/layout.js +124 -0
- package/server/public/nav.js +227 -64
- package/server/public/sound/beep.mp3 +0 -0
- package/server/public/sound/bell.mp3 +0 -0
- package/server/public/sound/bright-ring.mp3 +0 -0
- package/server/public/sound/clap.mp3 +0 -0
- package/server/public/sound/deep-ring.mp3 +0 -0
- package/server/public/sound/gasp.mp3 +0 -0
- package/server/public/sound/hehe.mp3 +0 -0
- package/server/public/sound/levelup.mp3 +0 -0
- package/server/public/sound/light-pop.mp3 +0 -0
- package/server/public/sound/light-ring.mp3 +0 -0
- package/server/public/sound/meow.mp3 +0 -0
- package/server/public/sound/piano.mp3 +0 -0
- package/server/public/sound/pop.mp3 +0 -0
- package/server/public/sound/uhoh.mp3 +0 -0
- package/server/public/sound/whistle.mp3 +0 -0
- package/server/public/style.css +195 -4
- package/server/public/tab-idle-notifier.js +700 -4
- package/server/public/terminal-settings.js +1131 -0
- package/server/public/urldropdown.css +28 -1
- package/server/socket.js +71 -4
- package/server/views/{terminals.ejs → agents.ejs} +108 -32
- package/server/views/app.ejs +321 -104
- package/server/views/bootstrap.ejs +8 -0
- package/server/views/connect.ejs +10 -1
- package/server/views/d.ejs +172 -18
- package/server/views/editor.ejs +8 -0
- package/server/views/file_browser.ejs +4 -0
- package/server/views/index.ejs +10 -1
- package/server/views/init/index.ejs +18 -3
- package/server/views/install.ejs +8 -0
- package/server/views/layout.ejs +2 -0
- package/server/views/net.ejs +10 -1
- package/server/views/network.ejs +10 -1
- package/server/views/pro.ejs +8 -0
- package/server/views/prototype/index.ejs +8 -0
- package/server/views/screenshots.ejs +10 -2
- package/server/views/settings.ejs +10 -2
- package/server/views/shell.ejs +8 -0
- package/server/views/terminal.ejs +8 -0
- package/server/views/tools.ejs +10 -2
|
@@ -415,9 +415,30 @@ body.dark .create-launcher-modal-tools-title {
|
|
|
415
415
|
color: rgba(203, 213, 225, 0.7);
|
|
416
416
|
}
|
|
417
417
|
.create-launcher-modal-tools-options {
|
|
418
|
+
display: grid;
|
|
419
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
420
|
+
gap: 12px;
|
|
421
|
+
align-items: flex-start;
|
|
422
|
+
}
|
|
423
|
+
.create-launcher-modal-tools-group {
|
|
418
424
|
display: flex;
|
|
425
|
+
flex-direction: column;
|
|
426
|
+
gap: 8px;
|
|
427
|
+
}
|
|
428
|
+
.create-launcher-modal-tools-group-title {
|
|
429
|
+
font-size: 11px;
|
|
430
|
+
font-weight: 600;
|
|
431
|
+
letter-spacing: 0.08em;
|
|
432
|
+
text-transform: uppercase;
|
|
433
|
+
color: rgba(71, 85, 105, 0.75);
|
|
434
|
+
}
|
|
435
|
+
body.dark .create-launcher-modal-tools-group-title {
|
|
436
|
+
color: rgba(203, 213, 225, 0.65);
|
|
437
|
+
}
|
|
438
|
+
.create-launcher-modal-tools-group-options {
|
|
439
|
+
display: flex;
|
|
440
|
+
flex-direction: column;
|
|
419
441
|
gap: 10px;
|
|
420
|
-
flex-wrap: wrap;
|
|
421
442
|
}
|
|
422
443
|
.create-launcher-modal-tool {
|
|
423
444
|
position: relative;
|
|
@@ -462,6 +483,12 @@ body.dark .create-launcher-modal-tool.selected {
|
|
|
462
483
|
background: rgba(127, 91, 243, 0.28);
|
|
463
484
|
box-shadow: 0 14px 40px rgba(5, 9, 25, 0.55);
|
|
464
485
|
}
|
|
486
|
+
|
|
487
|
+
@media (max-width: 720px) {
|
|
488
|
+
.create-launcher-modal-tools-options {
|
|
489
|
+
grid-template-columns: 1fr;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
465
492
|
.create-launcher-modal-tool input[type="radio"] {
|
|
466
493
|
position: absolute;
|
|
467
494
|
opacity: 0;
|
package/server/socket.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const querystring = require("querystring");
|
|
2
2
|
const WebSocket = require('ws');
|
|
3
3
|
const path = require('path')
|
|
4
|
+
const os = require('os')
|
|
4
5
|
const Util = require("../kernel/util")
|
|
5
6
|
const Environment = require("../kernel/environment")
|
|
6
7
|
const NOTIFICATION_CHANNEL = 'kernel.notifications'
|
|
@@ -15,6 +16,20 @@ class Socket {
|
|
|
15
16
|
this.server = parent.server
|
|
16
17
|
// this.kernel = parent.kernel
|
|
17
18
|
const wss = new WebSocket.Server({ server: this.parent.server })
|
|
19
|
+
this.localDeviceIds = new Set()
|
|
20
|
+
this.localAddresses = new Set()
|
|
21
|
+
try {
|
|
22
|
+
const ifaces = os.networkInterfaces() || {}
|
|
23
|
+
Object.values(ifaces).forEach((arr) => {
|
|
24
|
+
(arr || []).forEach((info) => {
|
|
25
|
+
if (info && info.address) {
|
|
26
|
+
this.localAddresses.add(info.address)
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
this.localAddresses.add('127.0.0.1')
|
|
31
|
+
this.localAddresses.add('::1')
|
|
32
|
+
} catch (_) {}
|
|
18
33
|
this.subscriptions = new Map(); // Initialize a Map to store the WebSocket connections interested in each event
|
|
19
34
|
this.notificationChannel = NOTIFICATION_CHANNEL
|
|
20
35
|
this.notificationBridgeDispose = null
|
|
@@ -36,6 +51,12 @@ class Socket {
|
|
|
36
51
|
this.subscriptions.delete(eventName);
|
|
37
52
|
}
|
|
38
53
|
});
|
|
54
|
+
// Cleanup device tracking
|
|
55
|
+
try {
|
|
56
|
+
if (ws._isLocalClient && ws._deviceId) {
|
|
57
|
+
this.localDeviceIds.delete(ws._deviceId)
|
|
58
|
+
}
|
|
59
|
+
} catch (_) {}
|
|
39
60
|
this.checkNotificationBridge();
|
|
40
61
|
});
|
|
41
62
|
ws.on('message', async (message, isBinary) => {
|
|
@@ -171,6 +192,25 @@ class Socket {
|
|
|
171
192
|
this.parent.kernel.api.process(req)
|
|
172
193
|
}
|
|
173
194
|
} else {
|
|
195
|
+
if (req.method === this.notificationChannel) {
|
|
196
|
+
if (typeof req.device_id === 'string' && req.device_id.trim()) {
|
|
197
|
+
ws._deviceId = req.device_id.trim()
|
|
198
|
+
}
|
|
199
|
+
// Mark local client sockets by IP matching any local address
|
|
200
|
+
try {
|
|
201
|
+
const ip = ws._ip || ''
|
|
202
|
+
const isLocal = (addr) => {
|
|
203
|
+
if (!addr || typeof addr !== 'string') return false
|
|
204
|
+
if (this.localAddresses.has(addr)) return true
|
|
205
|
+
const v = addr.trim().toLowerCase()
|
|
206
|
+
return v.startsWith('::ffff:127.') || v.startsWith('127.')
|
|
207
|
+
}
|
|
208
|
+
ws._isLocalClient = isLocal(ip)
|
|
209
|
+
if (ws._isLocalClient && ws._deviceId) {
|
|
210
|
+
this.localDeviceIds.add(ws._deviceId)
|
|
211
|
+
}
|
|
212
|
+
} catch (_) {}
|
|
213
|
+
}
|
|
174
214
|
this.subscribe(ws, req.method)
|
|
175
215
|
if (req.mode !== "listen") {
|
|
176
216
|
this.parent.kernel.api.process(req)
|
|
@@ -350,11 +390,38 @@ class Socket {
|
|
|
350
390
|
data: payload,
|
|
351
391
|
}
|
|
352
392
|
const frame = JSON.stringify(envelope)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
393
|
+
const targetId = (payload && typeof payload.device_id === 'string' && payload.device_id.trim()) ? payload.device_id.trim() : null
|
|
394
|
+
const audience = (payload && typeof payload.audience === 'string' && payload.audience.trim()) ? payload.audience.trim() : null
|
|
395
|
+
if (audience === 'device' && targetId) {
|
|
396
|
+
let delivered = false
|
|
397
|
+
subscribers.forEach((subscriber) => {
|
|
398
|
+
if (subscriber.readyState !== WebSocket.OPEN) {
|
|
399
|
+
return
|
|
400
|
+
}
|
|
401
|
+
if (subscriber._deviceId && subscriber._deviceId === targetId) {
|
|
402
|
+
try { subscriber.send(frame); delivered = true } catch (_) {}
|
|
403
|
+
}
|
|
404
|
+
})
|
|
405
|
+
if (!delivered) {
|
|
406
|
+
// Fallback: broadcast if no matching device subscriber is available
|
|
407
|
+
subscribers.forEach((subscriber) => {
|
|
408
|
+
if (subscriber.readyState === WebSocket.OPEN) {
|
|
409
|
+
try { subscriber.send(frame) } catch (_) {}
|
|
410
|
+
}
|
|
411
|
+
})
|
|
356
412
|
}
|
|
357
|
-
}
|
|
413
|
+
} else {
|
|
414
|
+
subscribers.forEach((subscriber) => {
|
|
415
|
+
if (subscriber.readyState === WebSocket.OPEN) {
|
|
416
|
+
try { subscriber.send(frame) } catch (_) {}
|
|
417
|
+
}
|
|
418
|
+
})
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
isLocalDevice(deviceId) {
|
|
423
|
+
if (!deviceId || typeof deviceId !== 'string') return false
|
|
424
|
+
return this.localDeviceIds.has(deviceId)
|
|
358
425
|
}
|
|
359
426
|
|
|
360
427
|
ensureNotificationBridge() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<html>
|
|
2
2
|
<head>
|
|
3
|
-
<title>Pinokio
|
|
3
|
+
<title>Pinokio Agents</title>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
|
6
6
|
<link href="/xterm.min.css" rel="stylesheet" />
|
|
@@ -53,7 +53,6 @@ body.plugin-page .btn-tab {
|
|
|
53
53
|
display: flex;
|
|
54
54
|
align-items: center;
|
|
55
55
|
gap: 10px;
|
|
56
|
-
margin-bottom: 15px;
|
|
57
56
|
}
|
|
58
57
|
body.plugin-page .btn-tab .btn {
|
|
59
58
|
display: flex;
|
|
@@ -76,6 +75,32 @@ body.plugin-page .btn-tab .btn {
|
|
|
76
75
|
grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
|
|
77
76
|
gap: 14px;
|
|
78
77
|
}
|
|
78
|
+
.plugin-category-layout {
|
|
79
|
+
display: grid;
|
|
80
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
81
|
+
gap: 24px;
|
|
82
|
+
}
|
|
83
|
+
.plugin-category {
|
|
84
|
+
display: flex;
|
|
85
|
+
flex-direction: column;
|
|
86
|
+
}
|
|
87
|
+
.plugin-category-header {
|
|
88
|
+
display: flex;
|
|
89
|
+
flex-direction: column;
|
|
90
|
+
gap: 4px;
|
|
91
|
+
margin-bottom: 12px;
|
|
92
|
+
}
|
|
93
|
+
.plugin-category-title {
|
|
94
|
+
font-size: 30px;
|
|
95
|
+
font-weight: 700;
|
|
96
|
+
margin: 0;
|
|
97
|
+
}
|
|
98
|
+
.plugin-category-subtitle {
|
|
99
|
+
font-size: 12px;
|
|
100
|
+
opacity: 0.6;
|
|
101
|
+
text-transform: uppercase;
|
|
102
|
+
letter-spacing: 0.08em;
|
|
103
|
+
}
|
|
79
104
|
.plugin-card {
|
|
80
105
|
display: flex;
|
|
81
106
|
align-items: center;
|
|
@@ -195,6 +220,9 @@ body.dark .plugin-card:focus-visible .disclosure-indicator {
|
|
|
195
220
|
background: rgba(0, 0, 0, 0.02);
|
|
196
221
|
text-align: center;
|
|
197
222
|
}
|
|
223
|
+
.plugin-empty.plugin-empty-category {
|
|
224
|
+
padding: 20px;
|
|
225
|
+
}
|
|
198
226
|
body.dark .plugin-empty {
|
|
199
227
|
border-color: rgba(255, 255, 255, 0.16);
|
|
200
228
|
background: rgba(255, 255, 255, 0.04);
|
|
@@ -305,6 +333,11 @@ body.dark .plugin-option:hover {
|
|
|
305
333
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
306
334
|
}
|
|
307
335
|
}
|
|
336
|
+
@media (max-width: 900px) {
|
|
337
|
+
.plugin-category-layout {
|
|
338
|
+
grid-template-columns: 1fr;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
308
341
|
@media (max-width: 600px) {
|
|
309
342
|
body.plugin-page main {
|
|
310
343
|
flex-direction: column;
|
|
@@ -394,37 +427,71 @@ body.dark .plugin-option:hover {
|
|
|
394
427
|
</header>
|
|
395
428
|
<main class='plugin-main'>
|
|
396
429
|
<div class='plugin-container'>
|
|
397
|
-
<h1><i class="fa-solid fa-
|
|
430
|
+
<h1><i class="fa-solid fa-robot"></i>Agents</h1>
|
|
431
|
+
<% const pluginCategories = pluginMenu.reduce((acc, pluginItem, index) => {
|
|
432
|
+
const runs = Array.isArray(pluginItem.run) ? pluginItem.run : []
|
|
433
|
+
const hasExec = runs.some((step) => step && step.method === 'exec')
|
|
434
|
+
const hasShellRun = runs.some((step) => step && step.method === 'shell.run')
|
|
435
|
+
if (hasExec) {
|
|
436
|
+
acc.ide.push({ pluginItem, index })
|
|
437
|
+
} else if (hasShellRun) {
|
|
438
|
+
acc.cli.push({ pluginItem, index })
|
|
439
|
+
} else {
|
|
440
|
+
acc.cli.push({ pluginItem, index })
|
|
441
|
+
}
|
|
442
|
+
return acc
|
|
443
|
+
}, { ide: [], cli: [] }) %>
|
|
398
444
|
<% if (pluginMenu.length === 0) { %>
|
|
399
445
|
<div class='plugin-empty'>
|
|
400
|
-
<p>No
|
|
401
|
-
<a class='btn' href="/home?mode=download"><i class="fa-solid fa-download"></i> Download
|
|
446
|
+
<p>No agents found. Clone or add agent definitions under <code>~/pinokio/plugin</code> to see them listed here.</p>
|
|
447
|
+
<a class='btn' href="/home?mode=download"><i class="fa-solid fa-download"></i> Download Agents</a>
|
|
402
448
|
</div>
|
|
403
449
|
<% } else { %>
|
|
404
|
-
<div class='plugin-
|
|
405
|
-
<%
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
<h2><%=pluginItem.title || pluginItem.text || pluginItem.name || 'Plugin'%></h2>
|
|
416
|
-
<% if (pluginItem.description) { %>
|
|
417
|
-
<div class='subtitle'><%=pluginItem.description%></div>
|
|
418
|
-
<% } %>
|
|
450
|
+
<div class='plugin-category-layout'>
|
|
451
|
+
<% [
|
|
452
|
+
{ key: 'ide', title: 'IDE', subtitle: 'Launch externally' },
|
|
453
|
+
{ key: 'cli', title: 'CLI', subtitle: 'Launch in Pinokio' }
|
|
454
|
+
].forEach(({ key, title, subtitle }) => {
|
|
455
|
+
const items = pluginCategories[key] || []
|
|
456
|
+
%>
|
|
457
|
+
<section class='plugin-category' aria-label="<%=title%> agents">
|
|
458
|
+
<div class='plugin-category-header'>
|
|
459
|
+
<h2 class='plugin-category-title'><%=title%></h2>
|
|
460
|
+
<div class='plugin-category-subtitle'><%=subtitle%></div>
|
|
419
461
|
</div>
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
462
|
+
<% if (items.length) { %>
|
|
463
|
+
<div class='plugin-grid'>
|
|
464
|
+
<% items.forEach(({ pluginItem, index }) => { %>
|
|
465
|
+
<div class='plugin-card' role="button" tabindex="0" data-plugin-index="<%=index%>">
|
|
466
|
+
<% if (pluginItem.image) { %>
|
|
467
|
+
<img src="<%=pluginItem.image%>" alt="<%=pluginItem.title || pluginItem.text || 'Plugin'%> icon">
|
|
468
|
+
<% } else if (pluginItem.icon) { %>
|
|
469
|
+
<div class='plugin-icon'><i class="<%=pluginItem.icon%>"></i></div>
|
|
470
|
+
<% } else { %>
|
|
471
|
+
<div class='plugin-icon'><i class="fa-solid fa-robot"></i></div>
|
|
472
|
+
<% } %>
|
|
473
|
+
<div class='plugin-details'>
|
|
474
|
+
<h2><%=pluginItem.title || pluginItem.text || pluginItem.name || 'Plugin'%></h2>
|
|
475
|
+
<% if (pluginItem.description) { %>
|
|
476
|
+
<div class='subtitle'><%=pluginItem.description%></div>
|
|
477
|
+
<% } %>
|
|
478
|
+
</div>
|
|
479
|
+
<div class='flexible'></div>
|
|
480
|
+
<% if (pluginItem.link) { %>
|
|
481
|
+
<button type='button' class='plugin-info' data-link="<%=pluginItem.link.replace(/"/g, '"')%>" aria-label="Open info"><i class="fa-solid fa-circle-info"></i></button>
|
|
482
|
+
<% } %>
|
|
483
|
+
<div class='disclosure-indicator' aria-hidden="true">
|
|
484
|
+
<i class="fa-solid fa-chevron-right"></i>
|
|
485
|
+
</div>
|
|
486
|
+
</div>
|
|
487
|
+
<% }) %>
|
|
488
|
+
</div>
|
|
489
|
+
<% } else { %>
|
|
490
|
+
<div class='plugin-empty plugin-empty-category'>
|
|
491
|
+
<p>No <%=title%> agents available.</p>
|
|
492
|
+
</div>
|
|
423
493
|
<% } %>
|
|
424
|
-
|
|
425
|
-
<i class="fa-solid fa-chevron-right"></i>
|
|
426
|
-
</div>
|
|
427
|
-
</div>
|
|
494
|
+
</section>
|
|
428
495
|
<% }) %>
|
|
429
496
|
</div>
|
|
430
497
|
<% } %>
|
|
@@ -447,9 +514,18 @@ body.dark .plugin-option:hover {
|
|
|
447
514
|
<a class='tab' id='genlog'><i class="fa-solid fa-laptop-code"></i><div class='caption'>Logs</div></a>
|
|
448
515
|
<a id='downloadlogs' download class='hidden btn2' href="/pinokio/logs.zip"><i class="fa-solid fa-download"></i><div class='caption'>Download logs</div></a>
|
|
449
516
|
<a class='tab' href="/screenshots"><i class="fa-solid fa-camera"></i><div class='caption'>Screenshots</div></a>
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
517
|
+
<a class='tab' href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Installed Tools</div></a>
|
|
518
|
+
<a class='tab selected' href="/agents"><i class="fa-solid fa-robot"></i><div class='caption'>Agents</div></a>
|
|
519
|
+
<a class='tab' href="/home?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
|
|
520
|
+
<% if (typeof peer_qr !== 'undefined' && peer_qr) { %>
|
|
521
|
+
<div class='qr' style='padding:12px 10px; text-align:center;'>
|
|
522
|
+
<a href="<%=peer_url%>" target="_blank" style="text-decoration:none; color:inherit; display:block;">
|
|
523
|
+
<img src="<%=peer_qr%>" alt="Open <%=peer_url%>" style="width:128px; height:128px; image-rendering: pixelated;"/>
|
|
524
|
+
<div class='caption'>Scan to open</div>
|
|
525
|
+
<div class='caption' style='font-size:10px; opacity:0.7;'><%=peer_url%></div>
|
|
526
|
+
</a>
|
|
527
|
+
</div>
|
|
528
|
+
<% } %>
|
|
453
529
|
</aside>
|
|
454
530
|
</main>
|
|
455
531
|
<script type="application/json" id="plugin-data"><%- JSON.stringify(serializedPlugins) %></script>
|
|
@@ -495,7 +571,7 @@ body.dark .plugin-option:hover {
|
|
|
495
571
|
<div class="url-modal-content" role="dialog" aria-modal="true" aria-labelledby="plugin-modal-title" aria-describedby="plugin-modal-description">
|
|
496
572
|
<button type="button" class="url-modal-close" aria-label="Close">×</button>
|
|
497
573
|
<h3 id="plugin-modal-title">Run Plugin</h3>
|
|
498
|
-
<p class="url-modal-description" id="plugin-modal-description">Choose a project to open the
|
|
574
|
+
<p class="url-modal-description" id="plugin-modal-description">Choose a project to open the agent from.</p>
|
|
499
575
|
<input type="search" class="url-modal-input" placeholder="Filter projects" autocomplete="off" />
|
|
500
576
|
<div class="url-dropdown plugin-modal-dropdown"></div>
|
|
501
577
|
<div class="url-modal-actions single">
|
|
@@ -730,7 +806,7 @@ body.dark .plugin-option:hover {
|
|
|
730
806
|
if (state.apps.length === 0) {
|
|
731
807
|
descriptionEl.textContent = 'No projects found under ~/pinokio/api. Create or download a project to continue.'
|
|
732
808
|
} else {
|
|
733
|
-
descriptionEl.textContent = 'Choose a project to open the
|
|
809
|
+
descriptionEl.textContent = 'Choose a project to open the agent from.'
|
|
734
810
|
}
|
|
735
811
|
renderList('')
|
|
736
812
|
dropdownEl.style.display = 'block'
|