@danieltmn/openbridge 0.1.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/CHANGELOG.md +60 -0
- package/LICENSE +21 -0
- package/README.md +143 -0
- package/bin/openbridge.js +18 -0
- package/docs/ARCHITECTURE.md +77 -0
- package/package.json +54 -0
- package/src/auth.js +204 -0
- package/src/bridge/bridge.js +2280 -0
- package/src/cli.js +791 -0
- package/src/config.js +108 -0
- package/src/log.js +22 -0
- package/src/paths.js +152 -0
- package/src/push.js +85 -0
- package/src/store/index.js +930 -0
- package/src/store/jsonfile.js +54 -0
- package/src/tunnel/index.js +141 -0
- package/src/web/assets/app.js +3939 -0
- package/src/web/assets/icons/icon-128x128.png +0 -0
- package/src/web/assets/icons/icon-144x144.png +0 -0
- package/src/web/assets/icons/icon-152x152.png +0 -0
- package/src/web/assets/icons/icon-192x192.png +0 -0
- package/src/web/assets/icons/icon-384x384.png +0 -0
- package/src/web/assets/icons/icon-512x512.png +0 -0
- package/src/web/assets/icons/icon-72x72.png +0 -0
- package/src/web/assets/icons/icon-96x96.png +0 -0
- package/src/web/assets/manifest.webmanifest +24 -0
- package/src/web/assets/sw.js +85 -0
- package/src/web/assets/themes/dark-plus.css +19 -0
- package/src/web/assets/themes/dark-red.css +18 -0
- package/src/web/assets/themes/default.css +18 -0
- package/src/web/assets/themes/github-light.css +19 -0
- package/src/web/assets/themes/high-contrast.css +18 -0
- package/src/web/assets/themes/index.json +15 -0
- package/src/web/assets/themes/light-plus.css +19 -0
- package/src/web/assets/themes/light.css +18 -0
- package/src/web/assets/themes/monokai.css +19 -0
- package/src/web/assets/themes/one-dark.css +19 -0
- package/src/web/assets/themes/solarized.css +18 -0
- package/src/web/assets/themes/terminal.css +28 -0
- package/src/web/assets/themes/themes.css +2 -0
- package/src/web/routes.js +871 -0
- package/src/web/server.js +130 -0
- package/src/web/templates/chat.html +1296 -0
- package/src/web/templates/login.html +144 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "OpenBridge",
|
|
3
|
+
"short_name": "OpenBridge",
|
|
4
|
+
"description": "Chat con tu IA en el PC (opencode)",
|
|
5
|
+
"start_url": "./chat.php",
|
|
6
|
+
"scope": "./",
|
|
7
|
+
"display": "standalone",
|
|
8
|
+
"display_override": ["window-controls-overlay", "standalone", "browser"],
|
|
9
|
+
"orientation": "any",
|
|
10
|
+
"background_color": "#0a0d12",
|
|
11
|
+
"theme_color": "#0a0d12",
|
|
12
|
+
"icons": [
|
|
13
|
+
{ "src": "icons/icon-72x72.png", "sizes": "72x72", "type": "image/png" },
|
|
14
|
+
{ "src": "icons/icon-96x96.png", "sizes": "96x96", "type": "image/png" },
|
|
15
|
+
{ "src": "icons/icon-128x128.png", "sizes": "128x128", "type": "image/png" },
|
|
16
|
+
{ "src": "icons/icon-144x144.png", "sizes": "144x144", "type": "image/png" },
|
|
17
|
+
{ "src": "icons/icon-152x152.png", "sizes": "152x152", "type": "image/png" },
|
|
18
|
+
{ "src": "icons/icon-192x192.png", "sizes": "192x192", "type": "image/png" },
|
|
19
|
+
{ "src": "icons/icon-384x384.png", "sizes": "384x384", "type": "image/png" },
|
|
20
|
+
{ "src": "icons/icon-512x512.png", "sizes": "512x512", "type": "image/png" },
|
|
21
|
+
{ "src": "icons/icon-192x192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable" },
|
|
22
|
+
{ "src": "icons/icon-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Service Worker mínimo de "openbridge".
|
|
3
|
+
* Estrategia: network-first. Nunca cachea api.php (debe devolver datos frescos).
|
|
4
|
+
* El resto de recursos se cachean para que la PWA funcione rápido, pero si hay
|
|
5
|
+
* red se prefiere la versión del servidor.
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const CACHE = 'openbridge-v3';
|
|
10
|
+
|
|
11
|
+
self.addEventListener('install', () => self.skipWaiting());
|
|
12
|
+
self.addEventListener('activate', (event) => {
|
|
13
|
+
event.waitUntil(
|
|
14
|
+
caches.keys().then((keys) =>
|
|
15
|
+
Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))
|
|
16
|
+
).then(() => self.clients.claim())
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
self.addEventListener('fetch', (event) => {
|
|
21
|
+
const req = event.request;
|
|
22
|
+
if (req.method !== 'GET') return;
|
|
23
|
+
|
|
24
|
+
const url = new URL(req.url);
|
|
25
|
+
if (url.origin !== location.origin) return;
|
|
26
|
+
if (url.pathname.endsWith('/api.php')) return;
|
|
27
|
+
|
|
28
|
+
// Navegación: siempre a la red y sin mirar el cache HTTP del navegador
|
|
29
|
+
// (evita mezclar un chat.php viejo con un app.js nuevo).
|
|
30
|
+
const opts = req.mode === 'navigate' ? { cache: 'reload' } : {};
|
|
31
|
+
|
|
32
|
+
event.respondWith(
|
|
33
|
+
fetch(req, opts).then((res) => {
|
|
34
|
+
if (res && res.status === 200) {
|
|
35
|
+
const copy = res.clone();
|
|
36
|
+
caches.open(CACHE).then((c) => c.put(req, copy)).catch(() => {});
|
|
37
|
+
}
|
|
38
|
+
return res;
|
|
39
|
+
}).catch(() =>
|
|
40
|
+
caches.match(req).then((hit) => {
|
|
41
|
+
if (hit) return hit;
|
|
42
|
+
if (url.pathname.endsWith('/')) return caches.match('./chat.php');
|
|
43
|
+
return Response.error();
|
|
44
|
+
})
|
|
45
|
+
)
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Aviso push: muestra la notificacion del sistema. El payload viaja cifrado
|
|
50
|
+
// (RFC 8291) y es un JSON { title, body, url }.
|
|
51
|
+
self.addEventListener('push', (event) => {
|
|
52
|
+
let data = {};
|
|
53
|
+
try {
|
|
54
|
+
if (event.data) data = event.data.json();
|
|
55
|
+
} catch (e) { /* payload ilegible: se muestra un aviso generico */ }
|
|
56
|
+
const title = data.title || 'openbridge';
|
|
57
|
+
const options = {
|
|
58
|
+
body: data.body || '',
|
|
59
|
+
icon: new URL('./icons/icon-192x192.png', self.registration.scope).href,
|
|
60
|
+
badge: new URL('./icons/icon-192x192.png', self.registration.scope).href,
|
|
61
|
+
data: { url: data.url || self.registration.scope },
|
|
62
|
+
vibrate: [100, 60, 100],
|
|
63
|
+
};
|
|
64
|
+
event.waitUntil(self.registration.showNotification(title, options));
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Tocar la notificacion abre (o enfoca) el chat indicado.
|
|
68
|
+
self.addEventListener('notificationclick', (event) => {
|
|
69
|
+
event.notification.close();
|
|
70
|
+
const url = (event.notification.data && event.notification.data.url) || self.registration.scope;
|
|
71
|
+
event.waitUntil(
|
|
72
|
+
self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((list) => {
|
|
73
|
+
for (const client of list) {
|
|
74
|
+
if ('focus' in client) {
|
|
75
|
+
client.navigate(url).catch(() => {});
|
|
76
|
+
return client.focus();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return self.clients.openWindow(url);
|
|
80
|
+
})
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Si el navegador renueva la suscripcion, la pagina vuelve a reportarla en el
|
|
85
|
+
// proximo arranque (la app re-sincroniza cuando el permiso ya esta concedido).
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* dark-plus: VSCode Dark+ — azul oscuro neutro con azules clásicos */
|
|
2
|
+
:root {
|
|
3
|
+
--bg: #1e1e1e;
|
|
4
|
+
--panel: #252526;
|
|
5
|
+
--rail: #181818;
|
|
6
|
+
--theirs: #2d2d30;
|
|
7
|
+
--mine: #3794ff;
|
|
8
|
+
--mine-soft: rgba(55, 148, 255, .10);
|
|
9
|
+
--text: #d4d4d4;
|
|
10
|
+
--muted: #8a8a8a;
|
|
11
|
+
--border: #3c3c3c;
|
|
12
|
+
--danger: #f48771;
|
|
13
|
+
--accent: #569cd6;
|
|
14
|
+
--accent-soft: rgba(86, 156, 214, .16);
|
|
15
|
+
--shadow: 0 10px 30px rgba(0, 0, 0, .45);
|
|
16
|
+
--code-bg: #1b1b1b;
|
|
17
|
+
--warn-bg: rgba(244, 135, 113, .12);
|
|
18
|
+
--warn-border: #6e3b2f;
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* dark-red: carmesí/carbón */
|
|
2
|
+
:root {
|
|
3
|
+
--bg: #14080a;
|
|
4
|
+
--panel: #1f1115;
|
|
5
|
+
--mine: #ef4444;
|
|
6
|
+
--theirs: #2d141a;
|
|
7
|
+
--text: #f3e8e8;
|
|
8
|
+
--muted: #b08888;
|
|
9
|
+
--border: #3a1d22;
|
|
10
|
+
--danger: #fb7185;
|
|
11
|
+
--accent: #f87171;
|
|
12
|
+
--accent-soft: rgba(248, 113, 113, .16);
|
|
13
|
+
--shadow: 0 10px 30px rgba(0, 0, 0, .55);
|
|
14
|
+
--code-bg: #0d0406;
|
|
15
|
+
--warn-bg: rgba(127, 29, 29, .35);
|
|
16
|
+
--warn-border: #b91c1c;
|
|
17
|
+
--rail: #1a0a0e;
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* default: azul/gris oscuro (paleta original) */
|
|
2
|
+
:root {
|
|
3
|
+
--bg: #0f172a;
|
|
4
|
+
--panel: #1e293b;
|
|
5
|
+
--mine: #0ea5e9;
|
|
6
|
+
--theirs: #334155;
|
|
7
|
+
--text: #e2e8f0;
|
|
8
|
+
--muted: #94a3b8;
|
|
9
|
+
--border: #334155;
|
|
10
|
+
--danger: #f87171;
|
|
11
|
+
--accent: #38bdf8;
|
|
12
|
+
--accent-soft: rgba(56, 189, 248, .14);
|
|
13
|
+
--shadow: 0 10px 30px rgba(0, 0, 0, .4);
|
|
14
|
+
--code-bg: #0b1220;
|
|
15
|
+
--warn-bg: rgba(127, 29, 29, .28);
|
|
16
|
+
--warn-border: #7f1d1d;
|
|
17
|
+
--rail: #111c30;
|
|
18
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* github-light: GitHub Light — blanco con verde y azul GitHub */
|
|
2
|
+
:root {
|
|
3
|
+
--bg: #ffffff;
|
|
4
|
+
--panel: #f6f8fa;
|
|
5
|
+
--rail: #eaeef2;
|
|
6
|
+
--theirs: #eff2f5;
|
|
7
|
+
--mine: #1a7f37;
|
|
8
|
+
--mine-soft: rgba(26, 127, 55, .08);
|
|
9
|
+
--text: #1f2328;
|
|
10
|
+
--muted: #656d76;
|
|
11
|
+
--border: #d0d7de;
|
|
12
|
+
--danger: #cf222e;
|
|
13
|
+
--accent: #0969da;
|
|
14
|
+
--accent-soft: rgba(9, 105, 218, .10);
|
|
15
|
+
--shadow: 0 4px 20px rgba(31, 35, 40, .08);
|
|
16
|
+
--code-bg: #f6f8fa;
|
|
17
|
+
--warn-bg: rgba(207, 34, 46, .08);
|
|
18
|
+
--warn-border: #f0b3b5;
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* high-contrast: blanco/negro, alto contraste */
|
|
2
|
+
:root {
|
|
3
|
+
--bg: #000000;
|
|
4
|
+
--panel: #0a0a0a;
|
|
5
|
+
--mine: #ffffff;
|
|
6
|
+
--theirs: #1a1a1a;
|
|
7
|
+
--text: #ffffff;
|
|
8
|
+
--muted: #cfcfcf;
|
|
9
|
+
--border: #ffffff;
|
|
10
|
+
--danger: #ffd400;
|
|
11
|
+
--accent: #ffd400;
|
|
12
|
+
--accent-soft: rgba(255, 212, 0, .22);
|
|
13
|
+
--shadow: 0 0 0 2px #ffffff;
|
|
14
|
+
--code-bg: #111111;
|
|
15
|
+
--warn-bg: rgba(255, 212, 0, .18);
|
|
16
|
+
--warn-border: #ffd400;
|
|
17
|
+
--rail: #050505;
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"themes": [
|
|
3
|
+
{ "slug": "terminal", "label": "Terminal", "sw": ["#0a0d12", "#3fb950"] },
|
|
4
|
+
{ "slug": "default", "label": "Oscuro", "sw": ["#0f172a", "#0ea5e9"] },
|
|
5
|
+
{ "slug": "dark-plus", "label": "Dark+", "sw": ["#1e1e1e", "#3794ff"] },
|
|
6
|
+
{ "slug": "monokai", "label": "Monokai", "sw": ["#272822", "#a6e22e"] },
|
|
7
|
+
{ "slug": "one-dark", "label": "One Dark", "sw": ["#282c34", "#61afef"] },
|
|
8
|
+
{ "slug": "dark-red", "label": "Carmesí", "sw": ["#14080a", "#ef4444"] },
|
|
9
|
+
{ "slug": "light", "label": "Claro", "sw": ["#f1f5f9", "#4f46e5"] },
|
|
10
|
+
{ "slug": "light-plus", "label": "Light+", "sw": ["#ffffff", "#007acc"] },
|
|
11
|
+
{ "slug": "github-light", "label": "GitHub Claro", "sw": ["#ffffff", "#1a7f37"] },
|
|
12
|
+
{ "slug": "solarized", "label": "Solarized", "sw": ["#fdf6e3", "#268bd2"] },
|
|
13
|
+
{ "slug": "high-contrast", "label": "Alto contraste", "sw": ["#000000", "#ffffff"] }
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* light-plus: VSCode Light+ — blanco con azul clásico */
|
|
2
|
+
:root {
|
|
3
|
+
--bg: #ffffff;
|
|
4
|
+
--panel: #f3f3f3;
|
|
5
|
+
--rail: #e9e9e9;
|
|
6
|
+
--theirs: #ececec;
|
|
7
|
+
--mine: #007acc;
|
|
8
|
+
--mine-soft: rgba(0, 122, 204, .08);
|
|
9
|
+
--text: #333333;
|
|
10
|
+
--muted: #6f6f6f;
|
|
11
|
+
--border: #dcdcdc;
|
|
12
|
+
--danger: #cd3131;
|
|
13
|
+
--accent: #005cc5;
|
|
14
|
+
--accent-soft: rgba(0, 92, 197, .12);
|
|
15
|
+
--shadow: 0 4px 20px rgba(0, 0, 0, .10);
|
|
16
|
+
--code-bg: #f7f7f7;
|
|
17
|
+
--warn-bg: rgba(205, 49, 49, .08);
|
|
18
|
+
--warn-border: #f0b1af;
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* light: gris claro/índigo */
|
|
2
|
+
:root {
|
|
3
|
+
--bg: #f1f5f9;
|
|
4
|
+
--panel: #ffffff;
|
|
5
|
+
--mine: #4f46e5;
|
|
6
|
+
--theirs: #e2e8f0;
|
|
7
|
+
--text: #0f172a;
|
|
8
|
+
--muted: #64748b;
|
|
9
|
+
--border: #cbd5e1;
|
|
10
|
+
--danger: #dc2626;
|
|
11
|
+
--accent: #4f46e5;
|
|
12
|
+
--accent-soft: rgba(79, 70, 229, .12);
|
|
13
|
+
--shadow: 0 4px 20px rgba(15, 23, 42, .08);
|
|
14
|
+
--code-bg: #f8fafc;
|
|
15
|
+
--warn-bg: rgba(220, 38, 38, .10);
|
|
16
|
+
--warn-border: #fca5a5;
|
|
17
|
+
--rail: #e2e8f0;
|
|
18
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* monokai: Monokai clásico — fondo carbón con verde lima y cian */
|
|
2
|
+
:root {
|
|
3
|
+
--bg: #272822;
|
|
4
|
+
--panel: #2d2e27;
|
|
5
|
+
--rail: #1e1f1c;
|
|
6
|
+
--theirs: #34352f;
|
|
7
|
+
--mine: #a6e22e;
|
|
8
|
+
--mine-soft: rgba(166, 226, 46, .10);
|
|
9
|
+
--text: #f8f8f2;
|
|
10
|
+
--muted: #90908a;
|
|
11
|
+
--border: #3e3d32;
|
|
12
|
+
--danger: #f92672;
|
|
13
|
+
--accent: #66d9ef;
|
|
14
|
+
--accent-soft: rgba(102, 217, 239, .14);
|
|
15
|
+
--shadow: 0 10px 30px rgba(0, 0, 0, .45);
|
|
16
|
+
--code-bg: #1f201b;
|
|
17
|
+
--warn-bg: rgba(249, 38, 114, .14);
|
|
18
|
+
--warn-border: #7c1338;
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* one-dark: One Dark Pro — gris azulado con azul y cian */
|
|
2
|
+
:root {
|
|
3
|
+
--bg: #282c34;
|
|
4
|
+
--panel: #21252b;
|
|
5
|
+
--rail: #1b1f27;
|
|
6
|
+
--theirs: #2f343e;
|
|
7
|
+
--mine: #61afef;
|
|
8
|
+
--mine-soft: rgba(97, 175, 239, .12);
|
|
9
|
+
--text: #abb2bf;
|
|
10
|
+
--muted: #7f848e;
|
|
11
|
+
--border: #3e4451;
|
|
12
|
+
--danger: #e06c75;
|
|
13
|
+
--accent: #56b6c2;
|
|
14
|
+
--accent-soft: rgba(86, 182, 194, .14);
|
|
15
|
+
--shadow: 0 10px 30px rgba(0, 0, 0, .45);
|
|
16
|
+
--code-bg: #1d2027;
|
|
17
|
+
--warn-bg: rgba(224, 108, 117, .12);
|
|
18
|
+
--warn-border: #7c3d41;
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* solarized: crema/índigo apagado (tonos cálidos) */
|
|
2
|
+
:root {
|
|
3
|
+
--bg: #fdf6e3;
|
|
4
|
+
--panel: #eee8d5;
|
|
5
|
+
--mine: #268bd2;
|
|
6
|
+
--theirs: #e3dcc4;
|
|
7
|
+
--text: #073642;
|
|
8
|
+
--muted: #657b83;
|
|
9
|
+
--border: #d6cfb6;
|
|
10
|
+
--danger: #dc322f;
|
|
11
|
+
--accent: #2aa198;
|
|
12
|
+
--accent-soft: rgba(42, 161, 152, .18);
|
|
13
|
+
--shadow: 0 10px 30px rgba(0, 0, 0, .15);
|
|
14
|
+
--code-bg: #f5efdc;
|
|
15
|
+
--warn-bg: rgba(220, 50, 47, .12);
|
|
16
|
+
--warn-border: #cb4b16;
|
|
17
|
+
--rail: #ece4ce;
|
|
18
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* terminal: opencode/codex-like — casi negro, mono, verde para el usuario */
|
|
2
|
+
:root {
|
|
3
|
+
--bg: #0a0d12;
|
|
4
|
+
--panel: #0f131a;
|
|
5
|
+
--rail: #0c1016;
|
|
6
|
+
--theirs: #141a23;
|
|
7
|
+
--mine: #3fb950;
|
|
8
|
+
--mine-soft: rgba(63, 185, 80, .08);
|
|
9
|
+
--text: #c9d1d9;
|
|
10
|
+
--muted: #7d8590;
|
|
11
|
+
--border: #21262d;
|
|
12
|
+
--danger: #f85149;
|
|
13
|
+
--accent: #79c0ff;
|
|
14
|
+
--accent-soft: rgba(121, 192, 255, .12);
|
|
15
|
+
--shadow: 0 10px 30px rgba(0, 0, 0, .5);
|
|
16
|
+
--code-bg: #0d1117;
|
|
17
|
+
--warn-bg: rgba(187, 128, 9, .16);
|
|
18
|
+
--warn-border: #9e6a03;
|
|
19
|
+
--warn-text: #d29922;
|
|
20
|
+
--info-bg: rgba(2, 55, 76, .16);
|
|
21
|
+
--info-border: #02374c;
|
|
22
|
+
--info-text: #7dbddb;
|
|
23
|
+
--success-bg: rgba(0, 128, 0, .16);
|
|
24
|
+
--success-border: #008000;
|
|
25
|
+
--success-text: #3fb950;
|
|
26
|
+
--ok: #3fb950;
|
|
27
|
+
--font: ui-monospace, "Cascadia Code", "JetBrains Mono", "SF Mono", "Fira Code", Consolas, "Roboto Mono", monospace;
|
|
28
|
+
}
|