CPater commited on
Commit
fd8b234
·
verified ·
1 Parent(s): 6b9cd8d

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +46 -5
  2. index.html +515 -18
  3. package.json +13 -0
README.md CHANGED
@@ -1,10 +1,51 @@
1
  ---
2
- title: Nwo Blackbox
3
- emoji: 📊
4
- colorFrom: yellow
5
- colorTo: green
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: NWO Blackbox Node
3
+ emoji: 📡
4
+ colorFrom: green
5
+ colorTo: gray
6
  sdk: static
7
  pinned: false
8
+ license: mit
9
+ app_file: static/index.html
10
  ---
11
 
12
+ # NWO Blackbox Node - HF Space
13
+
14
+ Off-grid command center integrating Meshtastic/LoRa mesh networks, local AI, TAK/CoT tactical mapping, and Bitcoin payments.
15
+
16
+ ## Features
17
+
18
+ - 📡 **Mesh Network Management** - Monitor Meshtastic/LoRa nodes
19
+ - 🤖 **Local AI Assistant** - Offline AI for tactical analysis
20
+ - 🗺️ **TAK/CoT Mapping** - Cursor on Target tactical objects
21
+ - 💰 **Bitcoin Wallet** - MetaMask integration for payments
22
+ - 💬 **Mesh Chat** - Communicate with @bot AI commands
23
+ - 📊 **Network Stats** - Real-time telemetry and monitoring
24
+
25
+ ## Deploy to Hugging Face
26
+
27
+ 1. Create new Space with `sdk: static`
28
+ 2. Upload `static/index.html`
29
+ 3. Space auto-deploys
30
+
31
+ ## Local Development
32
+
33
+ ```bash
34
+ cd static
35
+ python -m http.server 8080
36
+ # Open http://localhost:8080
37
+ ```
38
+
39
+ ## NWO Integration
40
+
41
+ This is a standalone demo of the NWO Blackbox Node interface. For full functionality with:
42
+ - Real Meshtastic radio connection
43
+ - Local LLM (llama.cpp)
44
+ - Cashu wallet integration
45
+ - Live CoT data
46
+
47
+ See the full NWO Blackbox Node repository.
48
+
49
+ ## Credits
50
+
51
+ Based on Blackbox Node by wadadawadada - offline-first command post for mesh networks.
index.html CHANGED
@@ -1,19 +1,516 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>NWO Blackbox Node - Off-Grid Command Center</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/web3@1.10.0/dist/web3.min.js"></script>
8
+ <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
9
+ <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
10
+ <style>
11
+ * { margin: 0; padding: 0; box-sizing: border-box; }
12
+ :root {
13
+ --primary: #00ff00;
14
+ --secondary: #008800;
15
+ --dark: #0a0a0a;
16
+ --darker: #050505;
17
+ --alert: #ff3333;
18
+ --warning: #ffaa00;
19
+ --info: #00aaff;
20
+ }
21
+ body {
22
+ background: var(--darker);
23
+ color: var(--primary);
24
+ font-family: 'Courier New', monospace;
25
+ min-height: 100vh;
26
+ }
27
+ body::before {
28
+ content: '';
29
+ position: fixed;
30
+ top: 0; left: 0; width: 100%; height: 100%;
31
+ background: repeating-linear-gradient(0deg, rgba(0,255,0,0.03), rgba(0,255,0,0.03) 1px, transparent 1px, transparent 2px);
32
+ pointer-events: none;
33
+ z-index: 1000;
34
+ }
35
+ .header {
36
+ background: linear-gradient(180deg, rgba(0,255,0,0.1) 0%, transparent 100%);
37
+ border-bottom: 1px solid var(--primary);
38
+ padding: 15px 20px;
39
+ display: flex;
40
+ justify-content: space-between;
41
+ align-items: center;
42
+ position: sticky;
43
+ top: 0;
44
+ z-index: 100;
45
+ }
46
+ .logo {
47
+ font-size: 24px;
48
+ font-weight: bold;
49
+ text-shadow: 0 0 10px var(--primary);
50
+ display: flex;
51
+ align-items: center;
52
+ gap: 10px;
53
+ }
54
+ .logo::before { content: '◉'; animation: pulse 2s infinite; }
55
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
56
+ .status-bar { display: flex; gap: 20px; font-size: 12px; }
57
+ .status-item { display: flex; align-items: center; gap: 5px; }
58
+ .status-indicator {
59
+ width: 8px; height: 8px; border-radius: 50%;
60
+ background: var(--primary);
61
+ animation: blink 1s infinite;
62
+ }
63
+ .status-indicator.offline { background: var(--alert); animation: none; }
64
+ @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
65
+ .main-container {
66
+ display: grid;
67
+ grid-template-columns: 300px 1fr 350px;
68
+ gap: 20px;
69
+ padding: 20px;
70
+ max-width: 1920px;
71
+ margin: 0 auto;
72
+ }
73
+ @media (max-width: 1400px) {
74
+ .main-container { grid-template-columns: 250px 1fr; }
75
+ .right-panel { display: none; }
76
+ }
77
+ @media (max-width: 900px) {
78
+ .main-container { grid-template-columns: 1fr; }
79
+ .left-panel { display: none; }
80
+ }
81
+ .panel {
82
+ background: rgba(0, 20, 0, 0.8);
83
+ border: 1px solid var(--secondary);
84
+ border-radius: 8px;
85
+ overflow: hidden;
86
+ }
87
+ .panel-header {
88
+ background: rgba(0, 255, 0, 0.1);
89
+ padding: 12px 15px;
90
+ border-bottom: 1px solid var(--secondary);
91
+ font-weight: bold;
92
+ font-size: 14px;
93
+ text-transform: uppercase;
94
+ display: flex;
95
+ justify-content: space-between;
96
+ align-items: center;
97
+ }
98
+ .panel-content { padding: 15px; max-height: calc(100vh - 200px); overflow-y: auto; }
99
+ .node-list { display: flex; flex-direction: column; gap: 10px; }
100
+ .node-item {
101
+ background: rgba(0, 255, 0, 0.05);
102
+ border: 1px solid var(--secondary);
103
+ border-radius: 6px;
104
+ padding: 12px;
105
+ cursor: pointer;
106
+ transition: all 0.3s;
107
+ }
108
+ .node-item:hover { background: rgba(0, 255, 0, 0.15); border-color: var(--primary); }
109
+ .node-item.active { background: rgba(0, 255, 0, 0.2); border-color: var(--primary); box-shadow: 0 0 15px rgba(0, 255, 0, 0.3); }
110
+ .node-name { font-weight: bold; margin-bottom: 5px; }
111
+ .node-meta { font-size: 11px; opacity: 0.7; display: flex; justify-content: space-between; }
112
+ .node-battery.low { color: var(--alert); }
113
+ .map-container { height: 500px; border-radius: 8px; overflow: hidden; border: 1px solid var(--secondary); }
114
+ #map { width: 100%; height: 100%; background: var(--dark); }
115
+ .chat-container { display: flex; flex-direction: column; height: 400px; }
116
+ .chat-messages { flex: 1; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 10px; }
117
+ .message { background: rgba(0, 255, 0, 0.1); border-left: 3px solid var(--primary); padding: 10px; border-radius: 0 8px 8px 0; font-size: 13px; }
118
+ .message.system { border-left-color: var(--info); background: rgba(0, 170, 255, 0.1); }
119
+ .message-sender { font-weight: bold; margin-bottom: 3px; font-size: 11px; opacity: 0.8; }
120
+ .message-time { font-size: 10px; opacity: 0.5; margin-top: 5px; }
121
+ .chat-input { display: flex; gap: 10px; padding: 10px; border-top: 1px solid var(--secondary); }
122
+ .chat-input input {
123
+ flex: 1; background: rgba(0, 0, 0, 0.5); border: 1px solid var(--secondary);
124
+ color: var(--primary); padding: 10px; border-radius: 4px; font-family: inherit;
125
+ }
126
+ .chat-input input:focus { outline: none; border-color: var(--primary); }
127
+ .btn {
128
+ background: var(--secondary); border: 1px solid var(--primary); color: var(--primary);
129
+ padding: 10px 20px; cursor: pointer; border-radius: 4px; font-family: inherit;
130
+ font-weight: bold; transition: all 0.3s;
131
+ }
132
+ .btn:hover { background: var(--primary); color: var(--dark); }
133
+ .ai-container { display: flex; flex-direction: column; height: 350px; }
134
+ .ai-messages { flex: 1; overflow-y: auto; padding: 10px; font-size: 13px; line-height: 1.6; }
135
+ .ai-message { margin-bottom: 15px; padding: 10px; background: rgba(0, 255, 0, 0.05); border-radius: 8px; }
136
+ .ai-message.user { background: rgba(0, 255, 0, 0.1); border-right: 3px solid var(--primary); }
137
+ .ai-message.assistant { background: rgba(0, 100, 0, 0.2); border-left: 3px solid var(--secondary); }
138
+ .ai-input { display: flex; gap: 10px; padding: 10px; border-top: 1px solid var(--secondary); }
139
+ .ai-input input {
140
+ flex: 1; background: rgba(0, 0, 0, 0.5); border: 1px solid var(--secondary);
141
+ color: var(--primary); padding: 10px; border-radius: 4px; font-family: inherit;
142
+ }
143
+ .wallet-section { padding: 15px; border-bottom: 1px solid var(--secondary); }
144
+ .wallet-balance { font-size: 28px; font-weight: bold; text-align: center; margin: 10px 0; text-shadow: 0 0 10px var(--primary); }
145
+ .wallet-address { font-size: 11px; text-align: center; opacity: 0.7; word-break: break-all; margin-bottom: 15px; }
146
+ .wallet-actions { display: flex; gap: 10px; }
147
+ .wallet-actions .btn { flex: 1; font-size: 12px; padding: 8px; }
148
+ .stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-bottom: 15px; }
149
+ .stat-box { background: rgba(0, 255, 0, 0.05); border: 1px solid var(--secondary); border-radius: 6px; padding: 12px; text-align: center; }
150
+ .stat-value { font-size: 24px; font-weight: bold; margin-bottom: 3px; }
151
+ .stat-label { font-size: 10px; opacity: 0.7; text-transform: uppercase; }
152
+ .command-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin-top: 15px; }
153
+ .cmd-btn {
154
+ background: rgba(0, 255, 0, 0.1); border: 1px solid var(--secondary); color: var(--primary);
155
+ padding: 10px; cursor: pointer; border-radius: 4px; font-family: inherit; font-size: 11px; transition: all 0.3s;
156
+ }
157
+ .cmd-btn:hover { background: rgba(0, 255, 0, 0.2); border-color: var(--primary); }
158
+ .terminal {
159
+ background: rgba(0, 0, 0, 0.8); border: 1px solid var(--secondary); border-radius: 6px;
160
+ padding: 15px; font-family: 'Courier New', monospace; font-size: 12px;
161
+ height: 200px; overflow-y: auto; margin-top: 20px;
162
+ }
163
+ .terminal-line { margin-bottom: 5px; opacity: 0.9; }
164
+ .terminal-line.command { color: var(--info); }
165
+ .terminal-line.response { color: var(--primary); }
166
+ .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.9); z-index: 1000; justify-content: center; align-items: center; }
167
+ .modal.active { display: flex; }
168
+ .modal-content { background: var(--dark); border: 2px solid var(--primary); border-radius: 12px; padding: 30px; max-width: 500px; width: 90%; box-shadow: 0 0 50px rgba(0, 255, 0, 0.3); }
169
+ .modal-title { font-size: 20px; margin-bottom: 20px; text-align: center; }
170
+ .modal-input { width: 100%; background: rgba(0, 0, 0, 0.5); border: 1px solid var(--secondary); color: var(--primary); padding: 12px; border-radius: 6px; font-family: inherit; margin-bottom: 15px; }
171
+ .modal-actions { display: flex; gap: 10px; justify-content: flex-end; }
172
+ </style>
173
+ </head>
174
+ <body>
175
+ <header class="header">
176
+ <div class="logo">NWO BLACKBOX NODE</div>
177
+ <div class="status-bar">
178
+ <div class="status-item">
179
+ <span class="status-indicator offline" id="mesh-status"></span>
180
+ <span>MESH: <span id="mesh-text">OFFLINE</span></span>
181
+ </div>
182
+ <div class="status-item">
183
+ <span class="status-indicator" id="ai-status"></span>
184
+ <span>AI: <span id="ai-text">STANDBY</span></span>
185
+ </div>
186
+ <div class="status-item">
187
+ <span class="status-indicator offline" id="wallet-status"></span>
188
+ <span>WALLET: <span id="wallet-text">LOCKED</span></span>
189
+ </div>
190
+ <div class="status-item"><span>NODES: <span id="node-count">0</span></span></div>
191
+ </div>
192
+ </header>
193
+
194
+ <div class="main-container">
195
+ <div class="left-panel">
196
+ <div class="panel">
197
+ <div class="panel-header"><span>📡 Mesh Nodes</span><span id="online-count">0/0</span></div>
198
+ <div class="panel-content">
199
+ <div class="node-list" id="node-list"></div>
200
+ <div class="command-grid">
201
+ <button class="cmd-btn" onclick="scanNodes()">🔍 SCAN</button>
202
+ <button class="cmd-btn" onclick="broadcastMessage()">📢 BROADCAST</button>
203
+ <button class="cmd-btn" onclick="sendCoT()">🎯 SEND CoT</button>
204
+ <button class="cmd-btn" onclick="requestTelemetry()">📊 TELEMETRY</button>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ <div class="panel" style="margin-top: 20px;">
209
+ <div class="panel-header"><span>💰 Bitcoin Wallet</span></div>
210
+ <div class="wallet-section">
211
+ <div class="wallet-balance" id="btc-balance">0.00000000</div>
212
+ <div class="wallet-address" id="btc-address">Not connected</div>
213
+ <div class="wallet-actions">
214
+ <button class="btn" onclick="connectWallet()">CONNECT</button>
215
+ <button class="btn" onclick="sendPayment()">SEND</button>
216
+ </div>
217
+ </div>
218
+ </div>
219
+ </div>
220
+
221
+ <div class="center-panel">
222
+ <div class="panel">
223
+ <div class="panel-header"><span>🗺️ Tactical Map (TAK/CoT)</span><span id="map-coords">--° --' --"</span></div>
224
+ <div class="panel-content" style="padding: 0;"><div class="map-container"><div id="map"></div></div></div>
225
+ </div>
226
+ <div class="panel" style="margin-top: 20px;">
227
+ <div class="panel-header"><span>💬 Mesh Chat</span><span id="chat-channel">#general</span></div>
228
+ <div class="chat-container">
229
+ <div class="chat-messages" id="chat-messages">
230
+ <div class="message system">
231
+ <div class="message-sender">SYSTEM</div>
232
+ <div>Welcome to NWO Blackbox Node. Mesh network offline. Connect radio to begin.</div>
233
+ <div class="message-time">--:--</div>
234
+ </div>
235
+ </div>
236
+ <div class="chat-input">
237
+ <input type="text" id="chat-input" placeholder="Type message... (use @bot for AI)" onkeypress="handleChatKey(event)">
238
+ <button class="btn" onclick="sendChatMessage()">SEND</button>
239
+ </div>
240
+ </div>
241
+ </div>
242
+ <div class="terminal" id="terminal">
243
+ <div class="terminal-line command">$ blackbox-node --init</div>
244
+ <div class="terminal-line response">NWO Blackbox Node v1.0.0 initialized</div>
245
+ <div class="terminal-line response">Loading mesh configuration...</div>
246
+ <div class="terminal-line response">AI model: llama-3.1-8b (local)</div>
247
+ <div class="terminal-line response">Wallet: Not connected</div>
248
+ <div class="terminal-line response">Ready. Waiting for mesh connection...</div>
249
+ </div>
250
+ </div>
251
+
252
+ <div class="right-panel">
253
+ <div class="panel">
254
+ <div class="panel-header"><span>🤖 Local AI Assistant</span><span id="ai-model">llama-3.1</span></div>
255
+ <div class="ai-container">
256
+ <div class="ai-messages" id="ai-messages">
257
+ <div class="ai-message assistant"><strong>NWO AI:</strong> I am your local offline AI assistant. I can help with mesh operations, tactical analysis, and off-grid coordination. No internet required.</div>
258
+ </div>
259
+ <div class="ai-input">
260
+ <input type="text" id="ai-input" placeholder="Ask anything..." onkeypress="handleAIKey(event)">
261
+ <button class="btn" onclick="sendAIQuery()">ASK</button>
262
+ </div>
263
+ </div>
264
+ </div>
265
+ <div class="panel" style="margin-top: 20px;">
266
+ <div class="panel-header"><span>📈 Network Stats</span></div>
267
+ <div class="panel-content">
268
+ <div class="stats-grid">
269
+ <div class="stat-box"><div class="stat-value" id="stat-nodes">0</div><div class="stat-label">Active Nodes</div></div>
270
+ <div class="stat-box"><div class="stat-value" id="stat-messages">0</div><div class="stat-label">Messages</div></div>
271
+ <div class="stat-box"><div class="stat-value" id="stat-range">0</div><div class="stat-label">km Range</div></div>
272
+ <div class="stat-box"><div class="stat-value" id="stat-uptime">00:00</div><div class="stat-label">Uptime</div></div>
273
+ </div>
274
+ <div class="panel-header" style="margin-top: 15px; border-top: 1px solid var(--secondary); padding-top: 15px;"><span>⚡ Quick Commands</span></div>
275
+ <div class="command-grid">
276
+ <button class="cmd-btn" onclick="runCommand('/help')">/help</button>
277
+ <button class="cmd-btn" onclick="runCommand('/summary')">/summary</button>
278
+ <button class="cmd-btn" onclick="runCommand('/weather')">/weather</button>
279
+ <button class="cmd-btn" onclick="runCommand('/nodes')">/nodes</button>
280
+ <button class="cmd-btn" onclick="runCommand('/battery')">/battery</button>
281
+ <button class="cmd-btn" onclick="runCommand('/wallet')">/wallet</button>
282
+ </div>
283
+ </div>
284
+ </div>
285
+ </div>
286
+ </div>
287
+
288
+ <div class="modal" id="payment-modal">
289
+ <div class="modal-content">
290
+ <div class="modal-title">⚡ Send Bitcoin</div>
291
+ <input type="text" class="modal-input" id="payment-address" placeholder="Recipient address or node name">
292
+ <input type="number" class="modal-input" id="payment-amount" placeholder="Amount (sats)" step="1">
293
+ <div class="modal-actions">
294
+ <button class="btn" onclick="closeModal()">CANCEL</button>
295
+ <button class="btn" onclick="confirmPayment()">SEND</button>
296
+ </div>
297
+ </div>
298
+ </div>
299
+
300
+ <script>
301
+ let state = { nodes: [], messages: [], map: null, markers: [], uptime: 0, wallet: null };
302
+
303
+ function init() {
304
+ initMap();
305
+ loadDemoData();
306
+ startUptimeCounter();
307
+ }
308
+
309
+ function initMap() {
310
+ state.map = L.map('map').setView([59.9139, 10.7522], 12);
311
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap', maxZoom: 19 }).addTo(state.map);
312
+ }
313
+
314
+ function loadDemoData() {
315
+ state.nodes = [
316
+ { id: 'n1', name: 'Alpha-1', lat: 59.9139, lng: 10.7522, signal: -45, battery: 85, status: 'online' },
317
+ { id: 'n2', name: 'Bravo-2', lat: 59.9200, lng: 10.7600, signal: -62, battery: 42, status: 'online' },
318
+ { id: 'n3', name: 'Charlie-3', lat: 59.9050, lng: 10.7400, signal: -78, battery: 12, status: 'warning' },
319
+ { id: 'n4', name: 'Delta-4', lat: 59.9300, lng: 10.7700, signal: -55, battery: 67, status: 'online' }
320
+ ];
321
+ renderNodes();
322
+ renderCoTObjects();
323
+ }
324
+
325
+ function renderNodes() {
326
+ const list = document.getElementById('node-list');
327
+ list.innerHTML = '';
328
+ state.nodes.forEach(node => {
329
+ const el = document.createElement('div');
330
+ el.className = 'node-item';
331
+ el.innerHTML = `<div class="node-name">${node.name}</div><div class="node-meta"><span>📶 ${node.signal}dBm</span><span class="node-battery ${node.battery < 20 ? 'low' : ''}">🔋 ${node.battery}%</span></div>`;
332
+ el.onclick = () => { state.map.setView([node.lat, node.lng], 15); };
333
+ list.appendChild(el);
334
+ const marker = L.marker([node.lat, node.lng]).addTo(state.map);
335
+ marker.bindPopup(`<b>${node.name}</b><br>Signal: ${node.signal}dBm<br>Battery: ${node.battery}%`);
336
+ state.markers.push(marker);
337
+ });
338
+ document.getElementById('online-count').textContent = `${state.nodes.filter(n => n.status === 'online').length}/${state.nodes.length}`;
339
+ document.getElementById('node-count').textContent = state.nodes.length;
340
+ document.getElementById('stat-nodes').textContent = state.nodes.length;
341
+ }
342
+
343
+ function renderCoTObjects() {
344
+ const cotObjects = [
345
+ { type: 'friendly', lat: 59.9150, lng: 10.7550, label: 'Team Alpha' },
346
+ { type: 'neutral', lat: 59.9180, lng: 10.7580, label: 'Checkpoint B' },
347
+ { type: 'hostile', lat: 59.9100, lng: 10.7450, label: 'Unknown Contact' }
348
+ ];
349
+ cotObjects.forEach(obj => {
350
+ const color = obj.type === 'friendly' ? '#00ff00' : obj.type === 'hostile' ? '#ff3333' : '#00aaff';
351
+ L.circleMarker([obj.lat, obj.lng], { radius: 12, fillColor: color, color: '#000', weight: 2, opacity: 1, fillOpacity: 0.8 }).addTo(state.map).bindPopup(`<b>${obj.label}</b><br>Type: ${obj.type.toUpperCase()}`);
352
+ });
353
+ }
354
+
355
+ function sendChatMessage() {
356
+ const input = document.getElementById('chat-input');
357
+ const text = input.value.trim();
358
+ if (!text) return;
359
+ addChatMessage('You', text);
360
+ input.value = '';
361
+ if (text.includes('@bot') || text.includes('!ask')) {
362
+ setTimeout(() => handleAIQuery(text.replace(/@bot|!ask/g, '').trim(), true), 500);
363
+ }
364
+ }
365
+
366
+ function handleChatKey(e) { if (e.key === 'Enter') sendChatMessage(); }
367
+
368
+ function addChatMessage(sender, text, type = 'normal') {
369
+ const container = document.getElementById('chat-messages');
370
+ const msg = document.createElement('div');
371
+ msg.className = `message ${type}`;
372
+ msg.innerHTML = `<div class="message-sender">${sender}</div><div>${text}</div><div class="message-time">${new Date().toLocaleTimeString()}</div>`;
373
+ container.appendChild(msg);
374
+ container.scrollTop = container.scrollHeight;
375
+ state.messages.push({ sender, text, time: Date.now() });
376
+ document.getElementById('stat-messages').textContent = state.messages.length;
377
+ }
378
+
379
+ function sendAIQuery() {
380
+ const input = document.getElementById('ai-input');
381
+ const query = input.value.trim();
382
+ if (!query) return;
383
+ handleAIQuery(query);
384
+ input.value = '';
385
+ }
386
+
387
+ function handleAIKey(e) { if (e.key === 'Enter') sendAIQuery(); }
388
+
389
+ function handleAIQuery(query, fromChat = false) {
390
+ const aiContainer = document.getElementById('ai-messages');
391
+ const userMsg = document.createElement('div');
392
+ userMsg.className = 'ai-message user';
393
+ userMsg.innerHTML = `<strong>You:</strong> ${query}`;
394
+ aiContainer.appendChild(userMsg);
395
+
396
+ setTimeout(() => {
397
+ const responses = [
398
+ "Based on current mesh telemetry, all nodes are operating within normal parameters. Signal strength is adequate for reliable communication.",
399
+ "I've analyzed the network topology. We have 4 active nodes with an average hop count of 2.3. The mesh is stable.",
400
+ "Tactical analysis complete. Friendly forces are positioned at coordinates 59.9150°N, 10.7550°E. No hostile contacts detected.",
401
+ "Weather data from node sensors indicates 18°C, 65% humidity, light winds from the southwest. Conditions favorable for operations.",
402
+ "Battery levels across the mesh: Average 51%, minimum 12% (Charlie-3). Recommend solar charging or battery replacement.",
403
+ "Cashu wallet sync complete. You have 15,420 sats available for off-grid transactions. Last mint verified 3 minutes ago."
404
+ ];
405
+ const response = responses[Math.floor(Math.random() * responses.length)];
406
+ const aiMsg = document.createElement('div');
407
+ aiMsg.className = 'ai-message assistant';
408
+ aiMsg.innerHTML = `<strong>NWO AI:</strong> ${response}`;
409
+ aiContainer.appendChild(aiMsg);
410
+ aiContainer.scrollTop = aiContainer.scrollHeight;
411
+ if (fromChat) addChatMessage('NWO AI', response);
412
+ }, 1000);
413
+ }
414
+
415
+ async function connectWallet() {
416
+ if (typeof window.ethereum !== 'undefined') {
417
+ try {
418
+ const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
419
+ state.wallet = accounts[0];
420
+ document.getElementById('btc-address').textContent = state.wallet.slice(0, 6) + '...' + state.wallet.slice(-4);
421
+ document.getElementById('wallet-text').textContent = 'CONNECTED';
422
+ document.getElementById('wallet-status').classList.remove('offline');
423
+ document.getElementById('btc-balance').textContent = '0.00452300';
424
+ addTerminalLine('Wallet connected: ' + state.wallet.slice(0, 10) + '...');
425
+ } catch (err) {
426
+ addTerminalLine('Wallet connection failed: ' + err.message, 'error');
427
+ }
428
+ } else {
429
+ addTerminalLine('MetaMask not installed', 'error');
430
+ }
431
+ }
432
+
433
+ function sendPayment() { document.getElementById('payment-modal').classList.add('active'); }
434
+ function closeModal() { document.getElementById('payment-modal').classList.remove('active'); }
435
+ function confirmPayment() {
436
+ const address = document.getElementById('payment-address').value;
437
+ const amount = document.getElementById('payment-amount').value;
438
+ if (!address || !amount) return;
439
+ addTerminalLine(`Payment sent: ${amount} sats to ${address.slice(0, 20)}...`);
440
+ closeModal();
441
+ addChatMessage('SYSTEM', `Payment of ${amount} sats initiated to ${address.slice(0, 10)}...`, 'system');
442
+ }
443
+
444
+ function scanNodes() {
445
+ addTerminalLine('Scanning for mesh nodes...');
446
+ setTimeout(() => {
447
+ addTerminalLine(`Found ${state.nodes.length} nodes in range`);
448
+ addChatMessage('SYSTEM', 'Node scan complete. 4 nodes discovered.', 'system');
449
+ }, 2000);
450
+ }
451
+
452
+ function broadcastMessage() {
453
+ const msg = prompt('Enter broadcast message:');
454
+ if (msg) {
455
+ addTerminalLine('Broadcasting: ' + msg);
456
+ addChatMessage('BROADCAST', msg, 'alert');
457
+ }
458
+ }
459
+
460
+ function sendCoT() {
461
+ addTerminalLine('CoT object placement mode active. Click on map.');
462
+ state.map.once('click', function(e) {
463
+ const type = prompt('Type (friendly/hostile/neutral):', 'friendly');
464
+ const label = prompt('Label:', 'New Contact');
465
+ if (type && label) {
466
+ const color = type === 'friendly' ? '#00ff00' : type === 'hostile' ? '#ff3333' : '#00aaff';
467
+ L.circleMarker([e.latlng.lat, e.latlng.lng], { radius: 12, fillColor: color, color: '#000', weight: 2, fillOpacity: 0.8 }).addTo(state.map).bindPopup(`<b>${label}</b><br>Type: ${type.toUpperCase()}`);
468
+ addTerminalLine(`CoT object created: ${label} (${type})`);
469
+ }
470
+ });
471
+ }
472
+
473
+ function requestTelemetry() {
474
+ addTerminalLine('Requesting telemetry from all nodes...');
475
+ setTimeout(() => {
476
+ state.nodes.forEach(node => {
477
+ addTerminalLine(`${node.name}: ${node.signal}dBm, ${node.battery}% battery`);
478
+ });
479
+ }, 1500);
480
+ }
481
+
482
+ function runCommand(cmd) {
483
+ addTerminalLine('$ ' + cmd);
484
+ const responses = {
485
+ '/help': 'Available commands: /summary, /weather, /nodes, /battery, /wallet, /help',
486
+ '/summary': 'Network: 4 nodes, 3 online. Messages: 12. Range: 8.5km. Uptime: ' + document.getElementById('stat-uptime').textContent,
487
+ '/weather': 'Temperature: 18°C, Humidity: 65%, Wind: SW 12km/h, Pressure: 1013hPa',
488
+ '/nodes': 'Alpha-1: Online (-45dBm), Bravo-2: Online (-62dBm), Charlie-3: Warning (-78dBm), Delta-4: Online (-55dBm)',
489
+ '/battery': 'Alpha-1: 85%, Bravo-2: 42%, Charlie-3: 12% LOW, Delta-4: 67%. Average: 51%',
490
+ '/wallet': state.wallet ? `Connected: ${state.wallet.slice(0, 10)}... Balance: 0.00452300 BTC` : 'Wallet not connected. Use CONNECT button.'
491
+ };
492
+ setTimeout(() => addTerminalLine(responses[cmd] || 'Unknown command'), 500);
493
+ }
494
+
495
+ function addTerminalLine(text, type = 'response') {
496
+ const term = document.getElementById('terminal');
497
+ const line = document.createElement('div');
498
+ line.className = 'terminal-line ' + type;
499
+ line.textContent = type === 'command' ? '$ ' + text : text;
500
+ term.appendChild(line);
501
+ term.scrollTop = term.scrollHeight;
502
+ }
503
+
504
+ function startUptimeCounter() {
505
+ setInterval(() => {
506
+ state.uptime++;
507
+ const hours = Math.floor(state.uptime / 3600).toString().padStart(2, '0');
508
+ const minutes = Math.floor((state.uptime % 3600) / 60).toString().padStart(2, '0');
509
+ document.getElementById('stat-uptime').textContent = `${hours}:${minutes}`;
510
+ }, 1000);
511
+ }
512
+
513
+ window.onload = init;
514
+ </script>
515
+ </body>
516
  </html>
package.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "nwo-blackbox-node",
3
+ "version": "1.0.0",
4
+ "description": "NWO Blackbox Node - Off-grid command center with mesh, AI, and Bitcoin",
5
+ "main": "static/index.html",
6
+ "scripts": {
7
+ "start": "python -m http.server 8080 --directory static",
8
+ "dev": "python -m http.server 3000 --directory static"
9
+ },
10
+ "keywords": ["nwo", "blackbox", "mesh", "meshtastic", "lora", "tak", "cot", "bitcoin", "off-grid"],
11
+ "author": "NWO Robotics",
12
+ "license": "MIT"
13
+ }