aboutsummaryrefslogtreecommitdiff
path: root/modules/core/module-servermon.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-06-04 06:01:04 +0200
committerAgando <roduccomm@gmail.com>2026-06-04 06:01:04 +0200
commita5023e61be61f2f2287342039addab6bb28ad2fd (patch)
tree9b1b3c166869cd6fee91bffe1f4134513005b8b1 /modules/core/module-servermon.js
parent301f75ab03302a05b3c35950a3b0ab49f9f69ed6 (diff)
auto: 2026-06-04_06:00 changes
Diffstat (limited to 'modules/core/module-servermon.js')
-rw-r--r--modules/core/module-servermon.js172
1 files changed, 140 insertions, 32 deletions
diff --git a/modules/core/module-servermon.js b/modules/core/module-servermon.js
index 23407db..293e9af 100644
--- a/modules/core/module-servermon.js
+++ b/modules/core/module-servermon.js
@@ -1,35 +1,143 @@
-// Extracted from newOS4 index.html — ServerMonitorModule
- class ServerMonitorModule {
- constructor() {
- this.commands = {}; this.terminal = null;
- this.servers = this.loadServers(); this.monitorInterval = null;
- }
- getHelpInfo() {
- return {
- 'server': 'Add a server to monitor: server <name> <url>',
- 'servers': 'List all monitored servers',
- 'server-check': 'Check a server status: server-check <name-or-index>',
- 'server-remove': 'Remove a server: server-remove <name-or-index>',
- 'monitor': 'Toggle auto-monitoring (checks every 60s)'
- };
- }
- loadServers() {
- try {
- const saved = localStorage.getItem('webx11_servers');
- return saved ? JSON.parse(saved) : [];
- } catch (e) { return []; }
- }
- saveServers() {
- localStorage.setItem('webx11_servers', JSON.stringify(this.servers));
- }
- registerCommands() {
- this.commands['server'] = (args) => this.addServer(args);
- this.commands['servers'] = () => this.listServers();
- this.commands['server-check'] = (args) => this.checkServer(args);
- this.commands['server-remove'] = (args) => this.removeServer(args);
- this.commands['monitor'] = (args) => this.toggleMonitor(args);
- }
- addServer(args) {
+// module-servermon.js — Server monitoring + self-optimization telemetry
+// Commands: server, servers, server-check, server-remove, monitor, system-health
+class ServerMonitorModule {
+ constructor() {
+ this.commands = {}; this.terminal = null;
+ this.servers = this.loadServers(); this.monitorInterval = null;
+ this._latencyLog = [];
+ this._maxLatencyEntries = 200;
+ }
+ getHelpInfo() {
+ return {
+ 'server': 'server <name> <url> — Add a server to monitor',
+ 'servers': 'servers — List all monitored servers',
+ 'server-check': 'server-check <name-or-index> — Check a server status',
+ 'server-remove': 'server-remove <name-or-index> — Remove a server',
+ 'monitor': 'monitor — Toggle auto-monitoring (checks every 60s)',
+ 'system-health': 'system-health — Show tool latency, module load times, VFS size, and error rates'
+ };
+ }
+ registerCommands() {
+ this.commands['server'] = (args) => this.addServer(args);
+ this.commands['servers'] = () => this.listServers();
+ this.commands['server-check'] = (args) => this.checkServer(args);
+ this.commands['server-remove'] = (args) => this.removeServer(args);
+ this.commands['monitor'] = (args) => this.toggleMonitor(args);
+ this.commands['system-health'] = () => this.showSystemHealth();
+ }
+ logToolCall(name, durationMs, error) {
+ this._latencyLog.push({
+ name,
+ duration: durationMs,
+ error: !!error,
+ time: Date.now()
+ });
+ if (this._latencyLog.length > this._maxLatencyEntries) {
+ this._latencyLog = this._latencyLog.slice(-this._maxLatencyEntries);
+ }
+ // Persist every 20 calls
+ if (this._latencyLog.length % 20 === 0) {
+ this._persistLatency();
+ }
+ }
+ _persistLatency() {
+ try {
+ this.terminal.fs.fsWriteJSON('/system/metrics/tool-latency.json', this._latencyLog.slice(-100));
+ } catch (e) {}
+ }
+ _loadLatency() {
+ try {
+ const data = this.terminal.fs.fsReadJSON('/system/metrics/tool-latency.json');
+ if (data) this._latencyLog = data;
+ } catch (e) {}
+ }
+ showSystemHealth() {
+ this._loadLatency();
+ const fs = this.terminal.fs;
+
+ // Tool latency stats
+ const toolStats = {};
+ for (const entry of this._latencyLog) {
+ if (!toolStats[entry.name]) toolStats[entry.name] = { calls: 0, totalMs: 0, errors: 0, maxMs: 0 };
+ toolStats[entry.name].calls++;
+ toolStats[entry.name].totalMs += entry.duration;
+ toolStats[entry.name].errors += entry.error ? 1 : 0;
+ toolStats[entry.name].maxMs = Math.max(toolStats[entry.name].maxMs, entry.duration);
+ }
+
+ this.terminal.print('📊 System Health', 'header');
+
+ // Tool latency table (sorted by avg desc)
+ this.terminal.print('── Tool Latency ──', 'info');
+ const sorted = Object.entries(toolStats)
+ .map(([name, s]) => ({ name, avg: (s.totalMs / s.calls).toFixed(0), ...s }))
+ .sort((a, b) => b.avg - a.avg);
+ for (const t of sorted.slice(0, 10)) {
+ const avgMs = t.avg.padStart(5);
+ const maxMs = String(t.maxMs).padStart(5);
+ const err = t.errors > 0 ? ` ⚠️${t.errors}` : '';
+ this.terminal.print(` ${avgMs}ms avg / ${maxMs}ms max — ${t.name} (${t.calls} calls)${err}`, t.errors > 0 ? 'warning' : '');
+ }
+ if (sorted.length > 10) this.terminal.print(` ... and ${sorted.length - 10} more tools`, 'info');
+
+ // VFS stats
+ this.terminal.print('── VFS Stats ──', 'info');
+ try {
+ const totalSize = this._vfsSize(fs.root);
+ const fileCount = this._vfsCount(fs.root, 'file');
+ const dirCount = this._vfsCount(fs.root, 'dir');
+ this.terminal.print(` ${fileCount} files, ${dirCount} dirs, ${(totalSize / 1024).toFixed(0)} KB total`, '');
+ } catch (e) { this.terminal.print(' VFS stats unavailable', 'warning'); }
+
+ // Module stats
+ this.terminal.print('── Module Stats ──', 'info');
+ const mm = this.terminal.moduleManager;
+ const loaded = Object.keys(mm.modules).length;
+ const reg = (mm.moduleRegistry || []).length;
+ this.terminal.print(` ${loaded} modules loaded, ${reg} in registry`, '');
+
+ // Memory
+ this.terminal.print('── Memory ──', 'info');
+ if (performance.memory) {
+ const heap = (performance.memory.usedJSHeapSize / 1048576).toFixed(1);
+ const total = (performance.memory.jsHeapSizeLimit / 1048576).toFixed(0);
+ this.terminal.print(` ${heap} MB used / ${total} MB limit`, heap > 100 ? 'warning' : '');
+ } else {
+ this.terminal.print(' Memory info not available (non-Chrome browser)', '');
+ }
+
+ // Total tool call count
+ const totalCalls = this._latencyLog.length;
+ const totalErrors = this._latencyLog.filter(e => e.error).length;
+ this.terminal.print(` ${totalCalls} tool calls logged, ${totalErrors} errors (${totalCalls > 0 ? (totalErrors / totalCalls * 100).toFixed(1) : 0}% error rate)`, totalErrors > 0 ? 'warning' : '');
+ }
+ _vfsSize(node) {
+ if (!node) return 0;
+ if (node.type === 'file') return (node.content || '').length;
+ let s = 0;
+ for (const k in (node.content || {})) s += this._vfsSize(node.content[k]);
+ return s;
+ }
+ _vfsCount(node, type) {
+ if (!node) return 0;
+ let c = 0;
+ for (const k in (node.content || {})) {
+ const child = node.content[k];
+ if (child.type === type) c++;
+ c += this._vfsCount(child, type);
+ }
+ return c;
+ }
+ loadServers() {
+ try {
+ const saved = localStorage.getItem('webx11_servers');
+ return saved ? JSON.parse(saved) : [];
+ } catch (e) { return []; }
+ }
+ saveServers() {
+ localStorage.setItem('webx11_servers', JSON.stringify(this.servers));
+ }
+ addServer(args) {
if (args.length < 2) {
this.terminal.print("Usage: server <name> <url>", "warning"); return;
}