thinknagent 0.1.20 → 0.1.23

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/lib/app.js DELETED
@@ -1,327 +0,0 @@
1
- 'use strict';
2
-
3
- const http = require('http');
4
- const path = require('path');
5
- const os = require('os');
6
- const { exec } = require('child_process');
7
- const store = require('./store');
8
- const Agent = require('./agent');
9
- const si = require('systeminformation');
10
-
11
- class AgentApp {
12
- constructor(port = 4455) {
13
- this.port = port;
14
- this.server = null;
15
- this.agentInstance = null;
16
- }
17
-
18
- start(autoOpen = true) {
19
- this.server = http.createServer((req, res) => this._handleRequest(req, res));
20
- this.server.listen(this.port, '127.0.0.1', () => {
21
- const url = `http://localhost:${this.port}`;
22
- console.log(`\n \x1b[32m✔\x1b[0m ThinkNCollab Agent App is running at: \x1b[36m${url}\x1b[0m\n`);
23
- if (autoOpen) {
24
- this._openBrowser(url);
25
- }
26
- });
27
-
28
- // Auto-start agent if already initialized
29
- const cfg = store.read();
30
- if (cfg.serverUrl && cfg.roomId) {
31
- try {
32
- this.agentInstance = new Agent();
33
- this.agentInstance.start();
34
- } catch (err) {
35
- console.warn('[app] Auto-start agent notice:', err.message);
36
- }
37
- }
38
- }
39
-
40
- _openBrowser(url) {
41
- const startCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
42
- exec(`${startCmd} ${url}`, () => {});
43
- }
44
-
45
- async _handleRequest(req, res) {
46
- const url = new URL(req.url, `http://${req.headers.host}`);
47
-
48
- // API: Status & Telemetry
49
- if (url.pathname === '/api/status' && req.method === 'GET') {
50
- try {
51
- const [cpu, mem, disk] = await Promise.all([
52
- si.currentLoad(),
53
- si.mem(),
54
- si.fsSize()
55
- ]);
56
- const cfg = store.read();
57
- res.writeHead(200, { 'Content-Type': 'application/json' });
58
- return res.end(JSON.stringify({
59
- success: true,
60
- config: {
61
- name: cfg.name || os.hostname(),
62
- serverUrl: cfg.serverUrl || '',
63
- roomId: cfg.roomId || '',
64
- agentId: cfg.agentId || '',
65
- role: cfg.role || 'monitor',
66
- status: cfg.agentToken ? 'approved' : cfg.agentId ? 'pending' : 'not_initialized'
67
- },
68
- telemetry: {
69
- hostname: os.hostname(),
70
- platform: `${os.type()} ${os.release()} (${os.arch()})`,
71
- uptime: os.uptime(),
72
- cpuPercent: parseFloat(cpu.currentLoad.toFixed(1)),
73
- cores: cpu.cpus?.length || os.cpus().length,
74
- memoryTotalMB: Math.round(mem.total / 1048576),
75
- memoryUsedMB: Math.round(mem.used / 1048576),
76
- memoryUsedPct: parseFloat(((mem.used / mem.total) * 100).toFixed(1)),
77
- disk: disk.map(d => ({
78
- mount: d.mount,
79
- sizeGB: (d.size / 1073741824).toFixed(1),
80
- usedPct: parseFloat((d.use || 0).toFixed(1))
81
- }))
82
- }
83
- }));
84
- } catch (err) {
85
- res.writeHead(500, { 'Content-Type': 'application/json' });
86
- return res.end(JSON.stringify({ error: err.message }));
87
- }
88
- }
89
-
90
- // API: Connect / Configure
91
- if (url.pathname === '/api/connect' && req.method === 'POST') {
92
- let body = '';
93
- req.on('data', chunk => body += chunk);
94
- req.on('end', () => {
95
- try {
96
- const data = JSON.parse(body);
97
- const { serverUrl, name, roomId } = data;
98
- if (!serverUrl || !roomId) {
99
- res.writeHead(400, { 'Content-Type': 'application/json' });
100
- return res.end(JSON.stringify({ error: 'Server URL and Room ID are required.' }));
101
- }
102
-
103
- const { v4: uuid } = require('uuid');
104
- const existing = store.read();
105
- const agentId = existing.agentId || uuid();
106
-
107
- store.write({
108
- ...existing,
109
- serverUrl: serverUrl.replace(/\/$/, ''),
110
- name: name || os.hostname(),
111
- roomId,
112
- agentId
113
- });
114
-
115
- // Restart Agent instance
116
- if (this.agentInstance) {
117
- try { this.agentInstance._shutdown('reconnect'); } catch (e) {}
118
- }
119
- this.agentInstance = new Agent();
120
- this.agentInstance.start();
121
-
122
- res.writeHead(200, { 'Content-Type': 'application/json' });
123
- return res.end(JSON.stringify({ success: true, message: 'Agent connected! Check DevOps Wall for approval.' }));
124
- } catch (err) {
125
- res.writeHead(500, { 'Content-Type': 'application/json' });
126
- return res.end(JSON.stringify({ error: err.message }));
127
- }
128
- });
129
- return;
130
- }
131
-
132
- // Serve Local App UI
133
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
134
- res.end(this._getHtml());
135
- }
136
-
137
- _getHtml() {
138
- return `<!DOCTYPE html>
139
- <html lang="en">
140
- <head>
141
- <meta charset="UTF-8">
142
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
143
- <title>ThinkNCollab Agent App</title>
144
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" />
145
- <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&family=Syne:wght@400;600;700;800&display=swap" rel="stylesheet">
146
- <style>
147
- * { margin:0; padding:0; box-sizing:border-box; }
148
- :root {
149
- --bg: #090910; --bg2: #12121e; --card: #161626; --border: rgba(255,255,255,0.08);
150
- --accent: #00ff88; --accent-glow: rgba(0,255,136,0.2); --text: #f1f5f9; --text2: #94a3b8; --text3: #475569;
151
- --orange: #f97316; --red: #ef4444;
152
- --font: 'Syne', sans-serif; --mono: 'JetBrains Mono', monospace;
153
- }
154
- body { background: var(--bg); color: var(--text); font-family: var(--font); min-height: 100vh; display: flex; flex-direction: column; }
155
- header { height: 60px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; padding: 0 24px; background: rgba(18,18,30,0.8); backdrop-filter: blur(12px); }
156
- .brand { display: flex; align-items: center; gap: 10px; font-weight: 800; font-size: 16px; letter-spacing: -0.02em; }
157
- .brand-dot { color: var(--accent); }
158
- .badge { font-family: var(--mono); font-size: 11px; padding: 4px 10px; border-radius: 20px; font-weight: 600; display: flex; align-items: center; gap: 6px; }
159
- .badge.online { background: rgba(0,255,136,0.12); color: var(--accent); border: 1px solid rgba(0,255,136,0.3); }
160
- .badge.pending { background: rgba(249,115,22,0.12); color: var(--orange); border: 1px solid rgba(249,115,22,0.3); }
161
- .badge-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; animation: pulse 2s infinite; }
162
- @keyframes pulse { 0%,100%{opacity:1;} 50%{opacity:0.3;} }
163
- .container { max-width: 960px; margin: 0 auto; width: 100%; padding: 24px; flex: 1; display: flex; flex-direction: column; gap: 20px; }
164
- .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; }
165
- .card { background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 18px; }
166
- .card-title { font-size: 11px; font-family: var(--mono); color: var(--text2); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 6px; display: flex; align-items: center; gap: 6px; }
167
- .metric-val { font-size: 26px; font-weight: 800; font-family: var(--mono); color: var(--accent); }
168
- .metric-sub { font-size: 11px; color: var(--text3); font-family: var(--mono); margin-top: 4px; }
169
- .form-group { margin-bottom: 12px; }
170
- .form-group label { display: block; font-size: 11px; font-family: var(--mono); color: var(--text2); margin-bottom: 4px; }
171
- .form-group input { width: 100%; background: var(--bg2); border: 1px solid var(--border); color: #fff; padding: 8px 12px; border-radius: 6px; font-size: 12px; font-family: var(--mono); }
172
- .form-group input:focus { outline: none; border-color: var(--accent); }
173
- .btn { background: var(--accent); color: #05140c; border: none; padding: 10px 18px; border-radius: 6px; font-size: 12px; font-weight: 700; cursor: pointer; font-family: var(--font); transition: all 0.15s; }
174
- .btn:hover { box-shadow: 0 0 15px var(--accent-glow); transform: translateY(-1px); }
175
- .toast { position: fixed; bottom: 20px; right: 20px; background: var(--card); border: 1px solid var(--accent); padding: 12px 20px; border-radius: 8px; font-size: 12px; color: #fff; display: none; z-index: 100; }
176
- </style>
177
- </head>
178
- <body>
179
- <header>
180
- <div class="brand">
181
- <i class="fa-solid fa-server" style="color:var(--accent);"></i>
182
- ThinkNCollab Agent App<span class="brand-dot">.</span>
183
- </div>
184
- <div id="status-badge" class="badge pending">
185
- <div class="badge-dot"></div>
186
- <span id="status-text">INITIALIZING</span>
187
- </div>
188
- </header>
189
-
190
- <div class="container">
191
- <div class="grid">
192
- <div class="card">
193
- <div class="card-title"><i class="fa-solid fa-microchip"></i> CPU Usage</div>
194
- <div class="metric-val" id="val-cpu">--%</div>
195
- <div class="metric-sub" id="val-cores">-- Cores</div>
196
- </div>
197
- <div class="card">
198
- <div class="card-title"><i class="fa-solid fa-memory"></i> Memory</div>
199
- <div class="metric-val" id="val-mem">--%</div>
200
- <div class="metric-sub" id="val-mem-detail">-- / -- MB</div>
201
- </div>
202
- <div class="card">
203
- <div class="card-title"><i class="fa-solid fa-hard-drive"></i> Primary Disk</div>
204
- <div class="metric-val" id="val-disk">--%</div>
205
- <div class="metric-sub" id="val-disk-detail">-- GB</div>
206
- </div>
207
- <div class="card">
208
- <div class="card-title"><i class="fa-solid fa-clock"></i> Node Uptime</div>
209
- <div class="metric-val" id="val-uptime" style="font-size:18px;">--</div>
210
- <div class="metric-sub" id="val-platform">--</div>
211
- </div>
212
- </div>
213
-
214
- <div class="card">
215
- <div style="font-size:14px; font-weight:700; margin-bottom:14px; display:flex; align-items:center; gap:8px;">
216
- <i class="fa-solid fa-link" style="color:var(--accent);"></i> Connect to ThinkNCollab DevOps Wall
217
- </div>
218
- <div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); gap:12px;">
219
- <div class="form-group">
220
- <label>ThinkNCollab Server URL</label>
221
- <input type="text" id="cfg-server" placeholder="https://thinkncollab.com" value="https://thinkncollab.com" />
222
- </div>
223
- <div class="form-group">
224
- <label>Target Room ID (from URL)</label>
225
- <input type="text" id="cfg-room" placeholder="e.g. 6a318170496c7b00a7f74260" />
226
- </div>
227
- <div class="form-group">
228
- <label>Node Display Name</label>
229
- <input type="text" id="cfg-name" placeholder="prod-web-01" />
230
- </div>
231
- </div>
232
- <div style="display:flex; justify-content:flex-end; margin-top:8px;">
233
- <button class="btn" onclick="saveConnection()"><i class="fa-solid fa-paper-plane"></i> Connect Node</button>
234
- </div>
235
- </div>
236
- </div>
237
-
238
- <div id="toast" class="toast"></div>
239
-
240
- <script>
241
- async function fetchStatus() {
242
- try {
243
- const res = await fetch('/api/status');
244
- const data = await res.json();
245
- if (data.success) {
246
- const t = data.telemetry;
247
- const c = data.config;
248
-
249
- document.getElementById('val-cpu').textContent = t.cpuPercent + '%';
250
- document.getElementById('val-cores').textContent = t.cores + ' Cores';
251
- document.getElementById('val-mem').textContent = t.memoryUsedPct + '%';
252
- document.getElementById('val-mem-detail').textContent = t.memoryUsedMB + ' / ' + t.memoryTotalMB + ' MB';
253
- if (t.disk && t.disk[0]) {
254
- document.getElementById('val-disk').textContent = t.disk[0].usedPct + '%';
255
- document.getElementById('val-disk-detail').textContent = t.disk[0].sizeGB + ' GB';
256
- }
257
- const upHours = Math.floor(t.uptime / 3600);
258
- const upMins = Math.floor((t.uptime % 3600) / 60);
259
- document.getElementById('val-uptime').textContent = upHours + 'h ' + upMins + 'm';
260
- document.getElementById('val-platform').textContent = t.platform;
261
-
262
- const badge = document.getElementById('status-badge');
263
- const badgeText = document.getElementById('status-text');
264
- if (c.status === 'approved') {
265
- badge.className = 'badge online';
266
- badgeText.textContent = 'ONLINE (APPROVED)';
267
- } else if (c.status === 'pending') {
268
- badge.className = 'badge pending';
269
- badgeText.textContent = 'PENDING APPROVAL';
270
- } else {
271
- badge.className = 'badge pending';
272
- badgeText.textContent = 'NOT CONFIGURED';
273
- }
274
-
275
- if (c.serverUrl && !document.getElementById('cfg-server').value) document.getElementById('cfg-server').value = c.serverUrl;
276
- if (c.roomId && !document.getElementById('cfg-room').value) document.getElementById('cfg-room').value = c.roomId;
277
- if (c.name && !document.getElementById('cfg-name').value) document.getElementById('cfg-name').value = c.name;
278
- }
279
- } catch (err) {
280
- console.error('Status fetch error:', err);
281
- }
282
- }
283
-
284
- async function saveConnection() {
285
- const serverUrl = document.getElementById('cfg-server').value.trim();
286
- const roomId = document.getElementById('cfg-room').value.trim();
287
- const name = document.getElementById('cfg-name').value.trim();
288
-
289
- if (!serverUrl || !roomId) {
290
- showToast('Server URL and Room ID are required!');
291
- return;
292
- }
293
-
294
- try {
295
- const res = await fetch('/api/connect', {
296
- method: 'POST',
297
- headers: { 'Content-Type': 'application/json' },
298
- body: JSON.stringify({ serverUrl, roomId, name })
299
- });
300
- const d = await res.json();
301
- if (d.success) {
302
- showToast(d.message || 'Connected successfully!');
303
- fetchStatus();
304
- } else {
305
- showToast(d.error || 'Failed to connect');
306
- }
307
- } catch (err) {
308
- showToast('Connection error: ' + err.message);
309
- }
310
- }
311
-
312
- function showToast(msg) {
313
- const t = document.getElementById('toast');
314
- t.textContent = msg;
315
- t.style.display = 'block';
316
- setTimeout(() => t.style.display = 'none', 4000);
317
- }
318
-
319
- fetchStatus();
320
- setInterval(fetchStatus, 3000);
321
- </script>
322
- </body>
323
- </html>`;
324
- }
325
- }
326
-
327
- module.exports = AgentApp;