infinicode 2.6.1 → 2.7.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/dist/cli.js +16 -1
- package/dist/kernel/federation/dashboard-html.d.ts +1 -1
- package/dist/kernel/federation/dashboard-html.js +346 -110
- package/dist/kernel/federation/telemetry.d.ts +5 -0
- package/dist/kernel/federation/telemetry.js +72 -9
- package/dist/kernel/federation/transport-http.js +35 -0
- package/dist/kernel/federation/types.d.ts +3 -0
- package/package.json +8 -7
|
@@ -7,6 +7,11 @@ export declare class TelemetryCollector {
|
|
|
7
7
|
/** Per-core % from a single snapshot (approximate, no per-core delta kept). */
|
|
8
8
|
private perCore;
|
|
9
9
|
private network;
|
|
10
|
+
/** Usage of the primary filesystem (root on POSIX, system drive on Windows) via
|
|
11
|
+
* fs.statfs (Node ≥18.15, no native deps). Never throws. */
|
|
12
|
+
private diskUsage;
|
|
13
|
+
/** CPU temperature in °C from the Linux thermal zone (Pi/SBC). null elsewhere. */
|
|
14
|
+
private temperatureC;
|
|
10
15
|
collect(): HardwareSnapshot;
|
|
11
16
|
}
|
|
12
17
|
/** A 0–100 load figure derived from a snapshot (used by the compute router). */
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* on a fixed cadence (~1 Hz) so deltas are meaningful.
|
|
10
10
|
*/
|
|
11
11
|
import os from 'node:os';
|
|
12
|
+
import { readFileSync, statfsSync } from 'node:fs';
|
|
12
13
|
export class TelemetryCollector {
|
|
13
14
|
lastCpu = null;
|
|
14
15
|
lastNet = new Map();
|
|
@@ -44,22 +45,83 @@ export class TelemetryCollector {
|
|
|
44
45
|
});
|
|
45
46
|
}
|
|
46
47
|
network() {
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
|
|
48
|
+
// Real rx/tx byte-rate deltas where the kernel exposes counters. Linux lists
|
|
49
|
+
// per-interface byte totals in /proc/net/dev; we diff against the previous
|
|
50
|
+
// tick. On platforms without it (e.g. Windows) rates stay 0 but interface
|
|
51
|
+
// presence is still reported. Never throws — a read failure degrades to the
|
|
52
|
+
// os.networkInterfaces() presence list with zeroed rates.
|
|
53
|
+
const now = Date.now();
|
|
50
54
|
const out = [];
|
|
55
|
+
let counters = null;
|
|
56
|
+
if (os.platform() === 'linux') {
|
|
57
|
+
try {
|
|
58
|
+
counters = new Map();
|
|
59
|
+
for (const line of readFileSync('/proc/net/dev', 'utf8').split('\n')) {
|
|
60
|
+
const m = line.match(/^\s*([^:]+):\s*(.+)$/);
|
|
61
|
+
if (!m)
|
|
62
|
+
continue;
|
|
63
|
+
const cols = m[2].trim().split(/\s+/).map(Number);
|
|
64
|
+
// Receive bytes = column 0, Transmit bytes = column 8.
|
|
65
|
+
counters.set(m[1].trim(), { rx: cols[0] || 0, tx: cols[8] || 0 });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
counters = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const ifaces = os.networkInterfaces();
|
|
51
73
|
for (const [name, addrs] of Object.entries(ifaces)) {
|
|
52
74
|
if (!addrs || addrs.every(a => a.internal))
|
|
53
75
|
continue;
|
|
54
76
|
const prev = this.lastNet.get(name);
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
77
|
+
const cur = counters?.get(name);
|
|
78
|
+
let rxRate = 0;
|
|
79
|
+
let txRate = 0;
|
|
80
|
+
if (cur && prev && prev.at) {
|
|
81
|
+
const dt = (now - prev.at) / 1000;
|
|
82
|
+
if (dt > 0) {
|
|
83
|
+
rxRate = Math.max(0, Math.round((cur.rx - prev.rx) / dt));
|
|
84
|
+
txRate = Math.max(0, Math.round((cur.tx - prev.tx) / dt));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
out.push({ name, rxBytesPerSec: rxRate, txBytesPerSec: txRate });
|
|
88
|
+
this.lastNet.set(name, { rx: cur?.rx ?? 0, tx: cur?.tx ?? 0, at: now });
|
|
60
89
|
}
|
|
61
90
|
return { interfaces: out };
|
|
62
91
|
}
|
|
92
|
+
/** Usage of the primary filesystem (root on POSIX, system drive on Windows) via
|
|
93
|
+
* fs.statfs (Node ≥18.15, no native deps). Never throws. */
|
|
94
|
+
diskUsage() {
|
|
95
|
+
const path = os.platform() === 'win32'
|
|
96
|
+
? (process.env.SystemDrive ? process.env.SystemDrive + '\\' : 'C:\\')
|
|
97
|
+
: '/';
|
|
98
|
+
try {
|
|
99
|
+
const st = statfsSync(path);
|
|
100
|
+
const totalBytes = Number(st.blocks) * Number(st.bsize);
|
|
101
|
+
const freeBytes = Number(st.bavail) * Number(st.bsize);
|
|
102
|
+
const usedBytes = Math.max(0, totalBytes - freeBytes);
|
|
103
|
+
const percent = totalBytes > 0 ? Math.round((usedBytes / totalBytes) * 100) : 0;
|
|
104
|
+
return { mounts: [{ path, totalBytes, usedBytes, percent }] };
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return { mounts: [] };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** CPU temperature in °C from the Linux thermal zone (Pi/SBC). null elsewhere. */
|
|
111
|
+
temperatureC() {
|
|
112
|
+
if (os.platform() !== 'linux')
|
|
113
|
+
return null;
|
|
114
|
+
try {
|
|
115
|
+
const milli = Number(readFileSync('/sys/class/thermal/thermal_zone0/temp', 'utf8').trim());
|
|
116
|
+
if (!Number.isFinite(milli) || milli <= 0)
|
|
117
|
+
return null;
|
|
118
|
+
// Usually millidegrees (47000 = 47°C); some SoCs report whole °C.
|
|
119
|
+
return Math.round((milli > 1000 ? milli / 1000 : milli) * 10) / 10;
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
63
125
|
collect() {
|
|
64
126
|
const total = os.totalmem();
|
|
65
127
|
const free = os.freemem();
|
|
@@ -80,7 +142,7 @@ export class TelemetryCollector {
|
|
|
80
142
|
percent: total > 0 ? Math.round((used / total) * 100) : 0,
|
|
81
143
|
},
|
|
82
144
|
gpu: null, // populated by an optional platform GPU probe later
|
|
83
|
-
disk:
|
|
145
|
+
disk: this.diskUsage(),
|
|
84
146
|
network: this.network(),
|
|
85
147
|
system: {
|
|
86
148
|
hostname: os.hostname(),
|
|
@@ -90,6 +152,7 @@ export class TelemetryCollector {
|
|
|
90
152
|
kernelRelease: os.release(),
|
|
91
153
|
},
|
|
92
154
|
processes: [],
|
|
155
|
+
temperatureC: this.temperatureC(),
|
|
93
156
|
timestamp: Date.now(),
|
|
94
157
|
};
|
|
95
158
|
}
|
|
@@ -16,7 +16,28 @@
|
|
|
16
16
|
import { createServer } from 'node:http';
|
|
17
17
|
import { createServer as createHttpsServer } from 'node:https';
|
|
18
18
|
import { timingSafeEqual } from 'node:crypto';
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
import { createRequire } from 'node:module';
|
|
19
21
|
import { frameToSSE, parseSSE } from './protocol.js';
|
|
22
|
+
/** Read the livekit-client UMD bundle from the installed dependency, once.
|
|
23
|
+
* Returns null (cached) if the package isn't present — the dashboard then
|
|
24
|
+
* falls back to an idle camera panel instead of erroring. */
|
|
25
|
+
let _lkUmd;
|
|
26
|
+
function loadLiveKitUmd() {
|
|
27
|
+
if (_lkUmd !== undefined)
|
|
28
|
+
return _lkUmd;
|
|
29
|
+
try {
|
|
30
|
+
const require = createRequire(import.meta.url);
|
|
31
|
+
// The package's exports map blocks deep subpaths, but its main/require entry
|
|
32
|
+
// IS the UMD bundle — so a bare resolve returns dist/livekit-client.umd.js.
|
|
33
|
+
const file = require.resolve('livekit-client');
|
|
34
|
+
_lkUmd = readFileSync(file, 'utf8');
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
_lkUmd = null;
|
|
38
|
+
}
|
|
39
|
+
return _lkUmd;
|
|
40
|
+
}
|
|
20
41
|
/** Constant-time string compare — avoids leaking the token via response timing. */
|
|
21
42
|
function safeEqual(a, b) {
|
|
22
43
|
const ab = Buffer.from(a);
|
|
@@ -131,6 +152,20 @@ export class MeshServer {
|
|
|
131
152
|
res.end(this.opts.dashboardHtml);
|
|
132
153
|
return;
|
|
133
154
|
}
|
|
155
|
+
// LiveKit browser SDK, served from the installed dependency so the Park
|
|
156
|
+
// tab can subscribe to a robot's live camera track. Loaded by the dashboard
|
|
157
|
+
// with the ?token= appended (so it passes the auth gate above). Optional:
|
|
158
|
+
// if livekit-client isn't installed, the dashboard just shows an idle cam.
|
|
159
|
+
if (req.method === 'GET' && path === '/vendor/livekit-client.umd.js') {
|
|
160
|
+
const js = loadLiveKitUmd();
|
|
161
|
+
if (!js) {
|
|
162
|
+
res.writeHead(404).end('livekit-client not installed');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
res.writeHead(200, { 'content-type': 'application/javascript; charset=utf-8', 'cache-control': 'public, max-age=86400' });
|
|
166
|
+
res.end(js);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
134
169
|
if (req.method === 'GET' && path === '/fed/status') {
|
|
135
170
|
res.writeHead(200, { 'content-type': 'application/json' });
|
|
136
171
|
res.end(JSON.stringify(this.opts.getStatus?.() ?? {}));
|
|
@@ -107,6 +107,9 @@ export interface HardwareSnapshot {
|
|
|
107
107
|
cpuPercent: number;
|
|
108
108
|
memoryBytes: number;
|
|
109
109
|
}>;
|
|
110
|
+
/** CPU package temperature in °C (Pi thermal zone / lm-sensors). null when the
|
|
111
|
+
* platform doesn't expose it (e.g. most Windows hosts). */
|
|
112
|
+
temperatureC?: number | null;
|
|
110
113
|
timestamp: number;
|
|
111
114
|
}
|
|
112
115
|
/** Node status entry consumed by the mesh map (neutral form of nodes.status). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infinicode",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "OpenKernel — provider-agnostic AI execution kernel. Native coding agent + mission-driven execution runtime.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/kernel/index.js",
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"conf": "^12.0.0",
|
|
76
76
|
"execa": "^9.3.0",
|
|
77
77
|
"inquirer": "^9.2.23",
|
|
78
|
+
"livekit-client": "^2.20.1",
|
|
78
79
|
"mitt": "^3.0.1",
|
|
79
80
|
"nanoid": "^5.0.7",
|
|
80
81
|
"node-fetch": "^3.3.2",
|
|
@@ -88,12 +89,12 @@
|
|
|
88
89
|
"typescript": "^5.4.5"
|
|
89
90
|
},
|
|
90
91
|
"optionalDependencies": {
|
|
91
|
-
"
|
|
92
|
-
"infinicode-tui-win32-x64": "^2.3.0",
|
|
93
|
-
"infinicode-tui-win32-arm64": "^2.3.0",
|
|
94
|
-
"infinicode-tui-linux-x64": "^2.3.0",
|
|
95
|
-
"infinicode-tui-linux-arm64": "^2.3.0",
|
|
92
|
+
"infinicode-tui-darwin-arm64": "^2.3.0",
|
|
96
93
|
"infinicode-tui-darwin-x64": "^2.3.0",
|
|
97
|
-
"infinicode-tui-
|
|
94
|
+
"infinicode-tui-linux-arm64": "^2.3.0",
|
|
95
|
+
"infinicode-tui-linux-x64": "^2.3.0",
|
|
96
|
+
"infinicode-tui-win32-arm64": "^2.3.0",
|
|
97
|
+
"infinicode-tui-win32-x64": "^2.3.0",
|
|
98
|
+
"playwright": "^1.47.0"
|
|
98
99
|
}
|
|
99
100
|
}
|