@mmmbuto/nexuscrew 0.7.6 → 0.8.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/README.md +173 -18
- package/frontend/dist/assets/index-BBOgTCoO.css +32 -0
- package/frontend/dist/assets/index-DQrDBogT.js +83 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/dist/sw.js +29 -0
- package/frontend/dist/version.json +1 -0
- package/lib/auth/middleware.js +7 -1
- package/lib/auth/token.js +31 -1
- package/lib/cli/commands.js +462 -31
- package/lib/cli/doctor.js +160 -0
- package/lib/cli/fleet-service.js +319 -0
- package/lib/cli/init.js +107 -7
- package/lib/cli/path.js +24 -0
- package/lib/cli/service.js +14 -3
- package/lib/cli/url.js +63 -0
- package/lib/config.js +5 -0
- package/lib/decks/routes.js +81 -0
- package/lib/decks/store.js +117 -0
- package/lib/files/routes.js +59 -2
- package/lib/files/store.js +16 -1
- package/lib/fleet/boot.js +62 -0
- package/lib/fleet/builtin.js +594 -0
- package/lib/fleet/definitions.js +410 -0
- package/lib/fleet/index.js +49 -10
- package/lib/fleet/managed.js +211 -0
- package/lib/fleet/provider.js +102 -0
- package/lib/fleet/routes.js +78 -8
- package/lib/fs/routes.js +56 -0
- package/lib/mcp/server.js +362 -0
- package/lib/nodes/commands.js +396 -0
- package/lib/nodes/store.js +358 -0
- package/lib/nodes/tunnel-supervisor.js +102 -0
- package/lib/nodes/tunnel.js +300 -0
- package/lib/notify/asks.js +150 -0
- package/lib/notify/events.js +59 -0
- package/lib/notify/notifier.js +37 -0
- package/lib/notify/persist.js +73 -0
- package/lib/notify/push.js +289 -0
- package/lib/notify/routes.js +260 -0
- package/lib/proxy/node-proxy.js +292 -0
- package/lib/pty/attach.js +34 -9
- package/lib/pty/provider.js +21 -6
- package/lib/server.js +214 -15
- package/lib/settings/routes.js +473 -0
- package/lib/ws/bridge.js +10 -1
- package/package.json +8 -2
- package/skills/nexuscrew-agent/SKILL.md +83 -0
- package/skills/nexuscrew-agent/bin/nc-deliver +23 -0
- package/skills/nexuscrew-agent/bin/nc-send +48 -0
- package/frontend/dist/assets/index-BWhSqR3J.css +0 -32
- package/frontend/dist/assets/index-Bx8vdLZY.css +0 -32
- package/frontend/dist/assets/index-CLb2xmyu.js +0 -81
- package/frontend/dist/assets/index-CgxfkmRo.js +0 -81
- package/frontend/dist/assets/index-DE67kR0M.js +0 -81
- package/frontend/dist/assets/index-DQMx2p4x.js +0 -81
- package/frontend/dist/assets/index-DWhfVwKW.css +0 -32
- package/frontend/dist/assets/index-DoPZ_3-h.js +0 -81
- package/frontend/dist/assets/index-o9l7pPAf.css +0 -32
- package/frontend/dist/assets/index-q9PUrGO0.js +0 -81
package/frontend/dist/index.html
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
<meta name="apple-mobile-web-app-title" content="NexusCrew" />
|
|
12
12
|
<link rel="manifest" href="/manifest.json" />
|
|
13
13
|
<title>NexusCrew</title>
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-DQrDBogT.js"></script>
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BBOgTCoO.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
18
18
|
<div id="root"></div>
|
package/frontend/dist/sw.js
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
1
|
self.addEventListener('install', () => self.skipWaiting());
|
|
2
2
|
self.addEventListener('activate', () => self.clients.claim());
|
|
3
3
|
self.addEventListener('fetch', (e) => e.respondWith(fetch(e.request)));
|
|
4
|
+
|
|
5
|
+
// Web Push del MCP bridge: payload JSON {title, body?, url?} dal server.
|
|
6
|
+
// tag fisso 'nexuscrew': le notifiche si sostituiscono invece di accumularsi.
|
|
7
|
+
self.addEventListener('push', (e) => {
|
|
8
|
+
let data = {};
|
|
9
|
+
try { data = e.data ? e.data.json() : {}; } catch (_) { /* payload non JSON: ignora */ }
|
|
10
|
+
const title = typeof data.title === 'string' && data.title ? data.title : 'NexusCrew';
|
|
11
|
+
e.waitUntil(self.registration.showNotification(title, {
|
|
12
|
+
body: typeof data.body === 'string' ? data.body : '',
|
|
13
|
+
tag: 'nexuscrew',
|
|
14
|
+
data: { url: typeof data.url === 'string' ? data.url : '/' },
|
|
15
|
+
}));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Click sulla notifica: focus di una finestra gia' aperta (deep-link via
|
|
19
|
+
// navigate) oppure apertura di una nuova su data.url (es. /#ask=<id>).
|
|
20
|
+
self.addEventListener('notificationclick', (e) => {
|
|
21
|
+
e.notification.close();
|
|
22
|
+
const url = (e.notification.data && e.notification.data.url) || '/';
|
|
23
|
+
e.waitUntil(self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((wins) => {
|
|
24
|
+
for (const w of wins) {
|
|
25
|
+
if ('focus' in w) {
|
|
26
|
+
if ('navigate' in w) w.navigate(url).catch(() => {});
|
|
27
|
+
return w.focus();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return self.clients.openWindow ? self.clients.openWindow(url) : undefined;
|
|
31
|
+
}));
|
|
32
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":"0.8.0"}
|
package/lib/auth/middleware.js
CHANGED
|
@@ -7,9 +7,15 @@ function bearerFrom(req) {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
// 401 uniforme: nessun dettaglio su cosa è andato storto.
|
|
10
|
+
// `token` puo' essere una stringa (storico) o un holder live {get}: l'holder si
|
|
11
|
+
// rilegge ad OGNI richiesta, cosi' una rotazione (audit F7 / §4b(3)) invalida il
|
|
12
|
+
// vecchio token senza restart. backward-compat: la stringa resta accettata.
|
|
10
13
|
function requireToken(token) {
|
|
14
|
+
const read = (token && typeof token === 'object' && typeof token.get === 'function')
|
|
15
|
+
? token.get
|
|
16
|
+
: () => token;
|
|
11
17
|
return (req, res, next) => {
|
|
12
|
-
if (verify(
|
|
18
|
+
if (verify(read(), bearerFrom(req))) return next();
|
|
13
19
|
res.status(401).json({ error: 'unauthorized' });
|
|
14
20
|
};
|
|
15
21
|
}
|
package/lib/auth/token.js
CHANGED
|
@@ -53,6 +53,36 @@ function loadOrCreateToken(tokenPath) {
|
|
|
53
53
|
return tok;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
// Rotazione token: genera un NUOVO segreto e sostituisce il file in modo ATOMICO
|
|
57
|
+
// (tmp stessa dir -> chmod 0600 -> rename), no-follow symlink. [A2 §4b(3)]
|
|
58
|
+
// A differenza di loadOrCreateToken NON preserva l'esistente: overwrite voluto.
|
|
59
|
+
// L'invalidazione reale delle sessioni attive la fa il chiamante (restart service:
|
|
60
|
+
// il server carica il token solo allo startup). Ritorna il nuovo token (il chiamante
|
|
61
|
+
// NON deve stamparlo: si usa `nexuscrew url`).
|
|
62
|
+
function rotateToken(tokenPath) {
|
|
63
|
+
// reject symlink target preesistente (no-follow), ENOENT ok (nuovo file)
|
|
64
|
+
try {
|
|
65
|
+
const st = fs.lstatSync(tokenPath);
|
|
66
|
+
if (st.isSymbolicLink()) {
|
|
67
|
+
throw new Error(`refusing symlink token path: ${tokenPath}`);
|
|
68
|
+
}
|
|
69
|
+
} catch (e) {
|
|
70
|
+
if (e.code !== 'ENOENT') throw e;
|
|
71
|
+
}
|
|
72
|
+
fs.mkdirSync(path.dirname(tokenPath), { recursive: true });
|
|
73
|
+
const tok = crypto.randomBytes(24).toString('base64url');
|
|
74
|
+
const tmp = tokenPath + '.tmp.' + process.pid;
|
|
75
|
+
try {
|
|
76
|
+
fs.writeFileSync(tmp, tok + '\n', { flag: 'wx', mode: 0o600 }); // exclusive temp
|
|
77
|
+
fs.chmodSync(tmp, 0o600);
|
|
78
|
+
fs.renameSync(tmp, tokenPath); // atomic replace
|
|
79
|
+
} catch (e) {
|
|
80
|
+
try { fs.unlinkSync(tmp); } catch (_) {} // cleanup temp su failure
|
|
81
|
+
throw e;
|
|
82
|
+
}
|
|
83
|
+
return tok;
|
|
84
|
+
}
|
|
85
|
+
|
|
56
86
|
function verify(expected, given) {
|
|
57
87
|
if (typeof expected !== 'string' || typeof given !== 'string') return false;
|
|
58
88
|
const a = Buffer.from(expected);
|
|
@@ -61,4 +91,4 @@ function verify(expected, given) {
|
|
|
61
91
|
return crypto.timingSafeEqual(a, b);
|
|
62
92
|
}
|
|
63
93
|
|
|
64
|
-
module.exports = { loadOrCreateToken, readTokenSafe, verify };
|
|
94
|
+
module.exports = { loadOrCreateToken, readTokenSafe, rotateToken, verify };
|