infinicode 1.0.0 → 2.0.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.
Files changed (157) hide show
  1. package/.opencode/plugins/home-logo-animation.tsx +120 -0
  2. package/.opencode/plugins/routing-mode-display.tsx +135 -0
  3. package/.opencode/themes/infinibot-gold.json +222 -0
  4. package/.opencode/tui.json +8 -0
  5. package/README.md +527 -75
  6. package/dist/ascii-video-animation.d.ts +2 -0
  7. package/dist/ascii-video-animation.js +243 -0
  8. package/dist/cli.js +195 -50
  9. package/dist/commands/console.d.ts +6 -0
  10. package/dist/commands/console.js +111 -0
  11. package/dist/commands/kernel-setup.d.ts +3 -0
  12. package/dist/commands/kernel-setup.js +303 -0
  13. package/dist/commands/mcp.d.ts +12 -0
  14. package/dist/commands/mcp.js +96 -0
  15. package/dist/commands/mission.d.ts +16 -0
  16. package/dist/commands/mission.js +301 -0
  17. package/dist/commands/models.d.ts +3 -1
  18. package/dist/commands/models.js +111 -55
  19. package/dist/commands/providers.d.ts +6 -0
  20. package/dist/commands/providers.js +95 -0
  21. package/dist/commands/run.d.ts +2 -1
  22. package/dist/commands/run.js +349 -59
  23. package/dist/commands/serve.d.ts +18 -0
  24. package/dist/commands/serve.js +127 -0
  25. package/dist/commands/setup.d.ts +1 -1
  26. package/dist/commands/setup.js +77 -44
  27. package/dist/commands/status.d.ts +2 -1
  28. package/dist/commands/status.js +46 -30
  29. package/dist/commands/workers.d.ts +5 -0
  30. package/dist/commands/workers.js +103 -0
  31. package/dist/kernel/autonomy/goal-loop.d.ts +49 -0
  32. package/dist/kernel/autonomy/goal-loop.js +113 -0
  33. package/dist/kernel/autonomy/index.d.ts +8 -0
  34. package/dist/kernel/autonomy/index.js +7 -0
  35. package/dist/kernel/browser/browser-controller.d.ts +57 -0
  36. package/dist/kernel/browser/browser-controller.js +175 -0
  37. package/dist/kernel/checkpoint-engine.d.ts +17 -0
  38. package/dist/kernel/checkpoint-engine.js +128 -0
  39. package/dist/kernel/command-system.d.ts +37 -0
  40. package/dist/kernel/command-system.js +239 -0
  41. package/dist/kernel/config-schema.d.ts +53 -0
  42. package/dist/kernel/config-schema.js +36 -0
  43. package/dist/kernel/event-bus.d.ts +19 -0
  44. package/dist/kernel/event-bus.js +65 -0
  45. package/dist/kernel/federation/auto-update.d.ts +36 -0
  46. package/dist/kernel/federation/auto-update.js +57 -0
  47. package/dist/kernel/federation/compute-router.d.ts +27 -0
  48. package/dist/kernel/federation/compute-router.js +44 -0
  49. package/dist/kernel/federation/config-sync.d.ts +33 -0
  50. package/dist/kernel/federation/config-sync.js +37 -0
  51. package/dist/kernel/federation/discovery.d.ts +11 -0
  52. package/dist/kernel/federation/discovery.js +44 -0
  53. package/dist/kernel/federation/event-bridge.d.ts +30 -0
  54. package/dist/kernel/federation/event-bridge.js +61 -0
  55. package/dist/kernel/federation/federation-plugin.d.ts +24 -0
  56. package/dist/kernel/federation/federation-plugin.js +35 -0
  57. package/dist/kernel/federation/federation.d.ts +126 -0
  58. package/dist/kernel/federation/federation.js +335 -0
  59. package/dist/kernel/federation/index.d.ts +31 -0
  60. package/dist/kernel/federation/index.js +23 -0
  61. package/dist/kernel/federation/moltfed-adapter.d.ts +30 -0
  62. package/dist/kernel/federation/moltfed-adapter.js +52 -0
  63. package/dist/kernel/federation/moltfed-connector.d.ts +98 -0
  64. package/dist/kernel/federation/moltfed-connector.js +193 -0
  65. package/dist/kernel/federation/node-identity.d.ts +19 -0
  66. package/dist/kernel/federation/node-identity.js +86 -0
  67. package/dist/kernel/federation/peer-mesh.d.ts +46 -0
  68. package/dist/kernel/federation/peer-mesh.js +117 -0
  69. package/dist/kernel/federation/protocol.d.ts +20 -0
  70. package/dist/kernel/federation/protocol.js +61 -0
  71. package/dist/kernel/federation/role-context.d.ts +5 -0
  72. package/dist/kernel/federation/role-context.js +45 -0
  73. package/dist/kernel/federation/telemetry.d.ts +21 -0
  74. package/dist/kernel/federation/telemetry.js +138 -0
  75. package/dist/kernel/federation/transport-http.d.ts +44 -0
  76. package/dist/kernel/federation/transport-http.js +207 -0
  77. package/dist/kernel/federation/types.d.ts +212 -0
  78. package/dist/kernel/federation/types.js +3 -0
  79. package/dist/kernel/free-providers.d.ts +28 -0
  80. package/dist/kernel/free-providers.js +162 -0
  81. package/dist/kernel/frontend-scoring.d.ts +21 -0
  82. package/dist/kernel/frontend-scoring.js +125 -0
  83. package/dist/kernel/index.d.ts +63 -0
  84. package/dist/kernel/index.js +45 -0
  85. package/dist/kernel/kernel.d.ts +75 -0
  86. package/dist/kernel/kernel.js +223 -0
  87. package/dist/kernel/logger.d.ts +18 -0
  88. package/dist/kernel/logger.js +26 -0
  89. package/dist/kernel/mcp/index.d.ts +9 -0
  90. package/dist/kernel/mcp/index.js +8 -0
  91. package/dist/kernel/mcp/mcp-server.d.ts +27 -0
  92. package/dist/kernel/mcp/mcp-server.js +178 -0
  93. package/dist/kernel/mission-engine.d.ts +42 -0
  94. package/dist/kernel/mission-engine.js +160 -0
  95. package/dist/kernel/orchestrator.d.ts +51 -0
  96. package/dist/kernel/orchestrator.js +226 -0
  97. package/dist/kernel/plugin-manager.d.ts +28 -0
  98. package/dist/kernel/plugin-manager.js +76 -0
  99. package/dist/kernel/plugins/browser-plugin.d.ts +22 -0
  100. package/dist/kernel/plugins/browser-plugin.js +34 -0
  101. package/dist/kernel/plugins/dashboard-plugin.d.ts +17 -0
  102. package/dist/kernel/plugins/dashboard-plugin.js +72 -0
  103. package/dist/kernel/plugins/discord-plugin.d.ts +11 -0
  104. package/dist/kernel/plugins/discord-plugin.js +35 -0
  105. package/dist/kernel/plugins/metrics-plugin.d.ts +33 -0
  106. package/dist/kernel/plugins/metrics-plugin.js +86 -0
  107. package/dist/kernel/plugins/search-plugin.d.ts +17 -0
  108. package/dist/kernel/plugins/search-plugin.js +20 -0
  109. package/dist/kernel/plugins/slack-plugin.d.ts +11 -0
  110. package/dist/kernel/plugins/slack-plugin.js +35 -0
  111. package/dist/kernel/plugins/telegram-plugin.d.ts +20 -0
  112. package/dist/kernel/plugins/telegram-plugin.js +122 -0
  113. package/dist/kernel/policies.d.ts +33 -0
  114. package/dist/kernel/policies.js +105 -0
  115. package/dist/kernel/provider-discovery.d.ts +41 -0
  116. package/dist/kernel/provider-discovery.js +179 -0
  117. package/dist/kernel/provider-manager.d.ts +38 -0
  118. package/dist/kernel/provider-manager.js +206 -0
  119. package/dist/kernel/provider-url.d.ts +18 -0
  120. package/dist/kernel/provider-url.js +45 -0
  121. package/dist/kernel/providers/gemini-provider.d.ts +43 -0
  122. package/dist/kernel/providers/gemini-provider.js +203 -0
  123. package/dist/kernel/providers/ollama-provider.d.ts +44 -0
  124. package/dist/kernel/providers/ollama-provider.js +241 -0
  125. package/dist/kernel/providers/openai-compatible-provider.d.ts +43 -0
  126. package/dist/kernel/providers/openai-compatible-provider.js +233 -0
  127. package/dist/kernel/recovery-manager.d.ts +38 -0
  128. package/dist/kernel/recovery-manager.js +156 -0
  129. package/dist/kernel/router.d.ts +44 -0
  130. package/dist/kernel/router.js +222 -0
  131. package/dist/kernel/sample-missions.d.ts +11 -0
  132. package/dist/kernel/sample-missions.js +132 -0
  133. package/dist/kernel/scheduler.d.ts +30 -0
  134. package/dist/kernel/scheduler.js +147 -0
  135. package/dist/kernel/sdk/harness-sdk.d.ts +37 -0
  136. package/dist/kernel/sdk/harness-sdk.js +48 -0
  137. package/dist/kernel/sdk/plugin-sdk.d.ts +27 -0
  138. package/dist/kernel/sdk/plugin-sdk.js +40 -0
  139. package/dist/kernel/search/search-controller.d.ts +32 -0
  140. package/dist/kernel/search/search-controller.js +141 -0
  141. package/dist/kernel/setup.d.ts +13 -0
  142. package/dist/kernel/setup.js +59 -0
  143. package/dist/kernel/types.d.ts +479 -0
  144. package/dist/kernel/types.js +7 -0
  145. package/dist/kernel/verification-engine.d.ts +33 -0
  146. package/dist/kernel/verification-engine.js +287 -0
  147. package/dist/kernel/worker-runtime.d.ts +66 -0
  148. package/dist/kernel/worker-runtime.js +261 -0
  149. package/dist/kernel/workers/browser-worker.d.ts +6 -0
  150. package/dist/kernel/workers/browser-worker.js +92 -0
  151. package/dist/kernel/workers/builtin-workers.d.ts +20 -0
  152. package/dist/kernel/workers/builtin-workers.js +120 -0
  153. package/dist/kernel/workers/research-worker.d.ts +5 -0
  154. package/dist/kernel/workers/research-worker.js +90 -0
  155. package/dist/prompt-ascii-video-animation.d.ts +2 -0
  156. package/dist/prompt-ascii-video-animation.js +243 -0
  157. package/package.json +43 -9
@@ -0,0 +1,138 @@
1
+ /**
2
+ * OpenKernel — Telemetry Collector
3
+ *
4
+ * Produces a HardwareSnapshot on the same shape InfiniBot uses, so the MOLTFED
5
+ * adapter can forward it verbatim to `hardware.snapshot` and light up InfiniBot's
6
+ * hardware panels + neural-mesh viz. Pure `node:os` — no native deps, safe on a Pi.
7
+ *
8
+ * CPU % is a delta between ticks; network is a byte-rate delta. Call collect()
9
+ * on a fixed cadence (~1 Hz) so deltas are meaningful.
10
+ */
11
+ import os from 'node:os';
12
+ export class TelemetryCollector {
13
+ lastCpu = null;
14
+ lastNet = new Map();
15
+ cpuTimes() {
16
+ let idle = 0;
17
+ let total = 0;
18
+ for (const c of os.cpus()) {
19
+ idle += c.times.idle;
20
+ total += c.times.user + c.times.nice + c.times.sys + c.times.idle + c.times.irq;
21
+ }
22
+ return { idle, total };
23
+ }
24
+ cpuPercent() {
25
+ const cur = this.cpuTimes();
26
+ if (!this.lastCpu) {
27
+ this.lastCpu = cur;
28
+ const load = os.loadavg()[0];
29
+ return Math.min(100, Math.round((load / os.cpus().length) * 100));
30
+ }
31
+ const idleDiff = cur.idle - this.lastCpu.idle;
32
+ const totalDiff = cur.total - this.lastCpu.total;
33
+ this.lastCpu = cur;
34
+ if (totalDiff <= 0)
35
+ return 0;
36
+ return Math.max(0, Math.min(100, Math.round((1 - idleDiff / totalDiff) * 100)));
37
+ }
38
+ /** Per-core % from a single snapshot (approximate, no per-core delta kept). */
39
+ perCore() {
40
+ return os.cpus().map(c => {
41
+ const t = c.times;
42
+ const total = t.user + t.nice + t.sys + t.idle + t.irq;
43
+ return total > 0 ? Math.round((1 - t.idle / total) * 100) : 0;
44
+ });
45
+ }
46
+ network() {
47
+ // node:os doesn't expose byte counters; report interface presence with
48
+ // zeroed rates by default. A platform collector can override later.
49
+ const ifaces = os.networkInterfaces();
50
+ const out = [];
51
+ for (const [name, addrs] of Object.entries(ifaces)) {
52
+ if (!addrs || addrs.every(a => a.internal))
53
+ continue;
54
+ const prev = this.lastNet.get(name);
55
+ const now = Date.now();
56
+ // Without kernel counters we can't compute a true rate; keep 0 but retain
57
+ // the hook so a native collector can populate rx/tx deltas.
58
+ out.push({ name, rxBytesPerSec: 0, txBytesPerSec: 0 });
59
+ this.lastNet.set(name, { rx: prev?.rx ?? 0, tx: prev?.tx ?? 0, at: now });
60
+ }
61
+ return { interfaces: out };
62
+ }
63
+ collect() {
64
+ const total = os.totalmem();
65
+ const free = os.freemem();
66
+ const used = total - free;
67
+ const la = os.loadavg();
68
+ return {
69
+ cpu: {
70
+ percent: this.cpuPercent(),
71
+ perCorePercent: this.perCore(),
72
+ loadAvg: [la[0], la[1], la[2]],
73
+ cores: os.cpus().length,
74
+ model: os.cpus()[0]?.model ?? 'unknown',
75
+ },
76
+ memory: {
77
+ totalBytes: total,
78
+ usedBytes: used,
79
+ freeBytes: free,
80
+ percent: total > 0 ? Math.round((used / total) * 100) : 0,
81
+ },
82
+ gpu: null, // populated by an optional platform GPU probe later
83
+ disk: { mounts: [] },
84
+ network: this.network(),
85
+ system: {
86
+ hostname: os.hostname(),
87
+ platform: os.platform(),
88
+ arch: os.arch(),
89
+ uptimeSec: Math.round(os.uptime()),
90
+ kernelRelease: os.release(),
91
+ },
92
+ processes: [],
93
+ timestamp: Date.now(),
94
+ };
95
+ }
96
+ }
97
+ /** A 0–100 load figure derived from a snapshot (used by the compute router). */
98
+ export function loadFromSnapshot(hw) {
99
+ if (!hw)
100
+ return 50; // unknown → mid
101
+ // Blend CPU% and memory% so a memory-bound Pi isn't picked for heavy work.
102
+ return Math.round(hw.cpu.percent * 0.7 + hw.memory.percent * 0.3);
103
+ }
104
+ /** Build the neutral NodeStatus entry the mesh map renders. */
105
+ export function nodeStatusFromPeer(peer) {
106
+ return {
107
+ nodeId: peer.nodeId,
108
+ displayName: peer.displayName,
109
+ role: peer.role,
110
+ platform: peer.manifest?.platform ?? 'unknown',
111
+ deviceFamily: peer.manifest?.deviceFamily,
112
+ modelIdentifier: peer.manifest?.modelIdentifier,
113
+ version: peer.manifest?.version ?? peer.version ?? '?',
114
+ capabilities: peer.manifest?.capabilities ?? [],
115
+ commands: peer.manifest?.commands ?? [],
116
+ connected: peer.connected,
117
+ connectedAtMs: peer.connected ? peer.lastSeenAt : undefined,
118
+ load: loadFromSnapshot(peer.hardware),
119
+ lastSeenAt: peer.lastSeenAt,
120
+ };
121
+ }
122
+ /** Local node's own status entry. */
123
+ export function selfNodeStatus(identity, hw, manifest) {
124
+ return {
125
+ nodeId: identity.nodeId,
126
+ displayName: identity.displayName,
127
+ role: identity.role,
128
+ platform: hw.system.platform,
129
+ modelIdentifier: hw.cpu.model,
130
+ version: manifest.version,
131
+ capabilities: manifest.capabilities,
132
+ commands: manifest.commands,
133
+ connected: true,
134
+ connectedAtMs: Date.now(),
135
+ load: loadFromSnapshot(hw),
136
+ lastSeenAt: Date.now(),
137
+ };
138
+ }
@@ -0,0 +1,44 @@
1
+ import type { Logger } from '../types.js';
2
+ import type { FederationEnvelope, NodeManifest, StreamFrame } from './types.js';
3
+ export type RpcHandler = (env: FederationEnvelope, remote: string) => Promise<FederationEnvelope | void>;
4
+ export interface MeshServerOptions {
5
+ host?: string;
6
+ port: number;
7
+ /** Optional shared token; requests must present it as Bearer auth. */
8
+ token?: string;
9
+ getManifest: () => NodeManifest;
10
+ onRpc: RpcHandler;
11
+ logger: Logger;
12
+ }
13
+ export declare class MeshServer {
14
+ private opts;
15
+ private server?;
16
+ private sse;
17
+ private seq;
18
+ constructor(opts: MeshServerOptions);
19
+ get streamCount(): number;
20
+ start(): Promise<void>;
21
+ /** Push a frame to every connected stream subscriber. */
22
+ broadcast(frame: StreamFrame): void;
23
+ private authOk;
24
+ private handle;
25
+ private openStream;
26
+ private readBody;
27
+ stop(): Promise<void>;
28
+ }
29
+ export interface MeshClientOptions {
30
+ token?: string;
31
+ timeoutMs?: number;
32
+ }
33
+ export declare class MeshClient {
34
+ private opts;
35
+ constructor(opts?: MeshClientOptions);
36
+ private headers;
37
+ fetchManifest(baseUrl: string): Promise<NodeManifest>;
38
+ rpc(baseUrl: string, env: FederationEnvelope): Promise<FederationEnvelope>;
39
+ /**
40
+ * Open an SSE stream to a peer. Returns a stop() fn. Reconnection is the
41
+ * caller's job (peer-mesh handles it on heartbeat failure).
42
+ */
43
+ openStream(baseUrl: string, onFrame: (f: StreamFrame) => void, onClose?: () => void): () => void;
44
+ }
@@ -0,0 +1,207 @@
1
+ /**
2
+ * OpenKernel — Federation Transport (HTTP + SSE)
3
+ *
4
+ * Dependency-free transport built on node:http (server) + global fetch (client).
5
+ *
6
+ * POST /fed/rpc → receive a FederationEnvelope, return a reply envelope
7
+ * GET /fed/stream → SSE: server pushes compact StreamFrames to a peer
8
+ * GET /fed/manifest → this node's manifest (cheap discovery probe)
9
+ *
10
+ * This directionality is deliberate: a hub OPENS the connection to a satellite
11
+ * (GET /stream to receive its activity/telemetry, POST /rpc to send tasks). The
12
+ * satellite is accept-only and never dials out — matching the secure one-way
13
+ * network model. A WebSocket/MOLTFED transport can be added behind the same
14
+ * MeshClient/MeshServer shape later for deep InfiniBot-fabric interop.
15
+ */
16
+ import { createServer } from 'node:http';
17
+ import { frameToSSE, parseSSE } from './protocol.js';
18
+ export class MeshServer {
19
+ opts;
20
+ server;
21
+ sse = new Map();
22
+ seq = 0;
23
+ constructor(opts) {
24
+ this.opts = opts;
25
+ }
26
+ get streamCount() {
27
+ return this.sse.size;
28
+ }
29
+ async start() {
30
+ this.server = createServer((req, res) => this.handle(req, res));
31
+ await new Promise((resolve, reject) => {
32
+ this.server.once('error', reject);
33
+ this.server.listen(this.opts.port, this.opts.host ?? '0.0.0.0', () => {
34
+ this.server.off('error', reject);
35
+ resolve();
36
+ });
37
+ });
38
+ this.opts.logger.info(`[federation] mesh listening on ${this.opts.host ?? '0.0.0.0'}:${this.opts.port}`);
39
+ }
40
+ /** Push a frame to every connected stream subscriber. */
41
+ broadcast(frame) {
42
+ if (this.sse.size === 0)
43
+ return;
44
+ const payload = frameToSSE(frame);
45
+ for (const client of this.sse.values()) {
46
+ try {
47
+ client.res.write(payload);
48
+ }
49
+ catch {
50
+ this.sse.delete(client.id);
51
+ }
52
+ }
53
+ }
54
+ authOk(req) {
55
+ if (!this.opts.token)
56
+ return true;
57
+ const h = req.headers['authorization'];
58
+ return typeof h === 'string' && h === `Bearer ${this.opts.token}`;
59
+ }
60
+ handle(req, res) {
61
+ const url = req.url ?? '/';
62
+ if (!this.authOk(req)) {
63
+ res.writeHead(401).end('unauthorized');
64
+ return;
65
+ }
66
+ const remote = req.socket.remoteAddress ?? 'unknown';
67
+ if (req.method === 'GET' && url.startsWith('/fed/manifest')) {
68
+ res.writeHead(200, { 'content-type': 'application/json' });
69
+ res.end(JSON.stringify(this.opts.getManifest()));
70
+ return;
71
+ }
72
+ if (req.method === 'GET' && url.startsWith('/fed/stream')) {
73
+ this.openStream(req, res);
74
+ return;
75
+ }
76
+ if (req.method === 'POST' && url.startsWith('/fed/rpc')) {
77
+ this.readBody(req)
78
+ .then(async (body) => {
79
+ let env;
80
+ try {
81
+ env = JSON.parse(body);
82
+ }
83
+ catch {
84
+ res.writeHead(400).end('bad envelope');
85
+ return;
86
+ }
87
+ const out = await this.opts.onRpc(env, remote);
88
+ res.writeHead(200, { 'content-type': 'application/json' });
89
+ res.end(JSON.stringify(out ?? { ok: true }));
90
+ })
91
+ .catch(err => {
92
+ this.opts.logger.warn(`[federation] rpc error: ${err instanceof Error ? err.message : String(err)}`);
93
+ res.writeHead(500).end('rpc failed');
94
+ });
95
+ return;
96
+ }
97
+ res.writeHead(404).end('not found');
98
+ }
99
+ openStream(req, res) {
100
+ res.writeHead(200, {
101
+ 'content-type': 'text/event-stream',
102
+ 'cache-control': 'no-cache',
103
+ connection: 'keep-alive',
104
+ });
105
+ res.write(': connected\n\n');
106
+ const id = `sse_${++this.seq}`;
107
+ this.sse.set(id, { id, res });
108
+ this.opts.logger.info(`[federation] stream subscriber ${id} (${this.sse.size} total)`);
109
+ req.on('close', () => {
110
+ this.sse.delete(id);
111
+ });
112
+ }
113
+ readBody(req) {
114
+ return new Promise((resolve, reject) => {
115
+ let data = '';
116
+ req.on('data', chunk => {
117
+ data += chunk;
118
+ if (data.length > 8_000_000)
119
+ reject(new Error('body too large'));
120
+ });
121
+ req.on('end', () => resolve(data));
122
+ req.on('error', reject);
123
+ });
124
+ }
125
+ async stop() {
126
+ for (const c of this.sse.values()) {
127
+ try {
128
+ c.res.end();
129
+ }
130
+ catch { /* ignore */ }
131
+ }
132
+ this.sse.clear();
133
+ if (this.server) {
134
+ await new Promise(resolve => this.server.close(() => resolve()));
135
+ this.server = undefined;
136
+ }
137
+ }
138
+ }
139
+ export class MeshClient {
140
+ opts;
141
+ constructor(opts = {}) {
142
+ this.opts = opts;
143
+ }
144
+ headers() {
145
+ const h = { 'content-type': 'application/json' };
146
+ if (this.opts.token)
147
+ h['authorization'] = `Bearer ${this.opts.token}`;
148
+ return h;
149
+ }
150
+ async fetchManifest(baseUrl) {
151
+ const res = await fetch(`${baseUrl}/fed/manifest`, {
152
+ headers: this.headers(),
153
+ signal: AbortSignal.timeout(this.opts.timeoutMs ?? 5000),
154
+ });
155
+ if (!res.ok)
156
+ throw new Error(`manifest ${res.status}`);
157
+ return (await res.json());
158
+ }
159
+ async rpc(baseUrl, env) {
160
+ const res = await fetch(`${baseUrl}/fed/rpc`, {
161
+ method: 'POST',
162
+ headers: this.headers(),
163
+ body: JSON.stringify(env),
164
+ signal: AbortSignal.timeout(this.opts.timeoutMs ?? 30_000),
165
+ });
166
+ if (!res.ok)
167
+ throw new Error(`rpc ${res.status}`);
168
+ return (await res.json());
169
+ }
170
+ /**
171
+ * Open an SSE stream to a peer. Returns a stop() fn. Reconnection is the
172
+ * caller's job (peer-mesh handles it on heartbeat failure).
173
+ */
174
+ openStream(baseUrl, onFrame, onClose) {
175
+ const ctrl = new AbortController();
176
+ (async () => {
177
+ try {
178
+ const res = await fetch(`${baseUrl}/fed/stream`, {
179
+ headers: this.headers(),
180
+ signal: ctrl.signal,
181
+ });
182
+ if (!res.ok || !res.body)
183
+ throw new Error(`stream ${res.status}`);
184
+ const reader = res.body.getReader();
185
+ const decoder = new TextDecoder();
186
+ let buf = '';
187
+ for (;;) {
188
+ const { done, value } = await reader.read();
189
+ if (done)
190
+ break;
191
+ buf += decoder.decode(value, { stream: true });
192
+ const { frames, rest } = parseSSE(buf);
193
+ buf = rest;
194
+ for (const f of frames)
195
+ onFrame(f);
196
+ }
197
+ }
198
+ catch {
199
+ // aborted or dropped
200
+ }
201
+ finally {
202
+ onClose?.();
203
+ }
204
+ })();
205
+ return () => ctrl.abort();
206
+ }
207
+ }
@@ -0,0 +1,212 @@
1
+ /**
2
+ * OpenKernel — Federation Types
3
+ *
4
+ * The mesh is provider/harness-agnostic. Everything on the wire is plain JSON:
5
+ * a small envelope carrying one of a handful of message kinds, plus a compact
6
+ * "stream frame" format tuned for high-frequency, animation-friendly rendering
7
+ * in a harness UI (short keys, coalesced telemetry).
8
+ *
9
+ * Nothing here is InfiniBot-specific — the MOLTFED adapter translates these
10
+ * neutral shapes into InfiniBot's message names so its neural-mesh map + hardware
11
+ * panels light up without any InfiniBot changes.
12
+ */
13
+ import type { Capability } from '../types.js';
14
+ /** Role of a node in the mesh. Governs connection direction + permissions. */
15
+ export type NodeRole = 'hub' | 'peer' | 'satellite' | 'relay';
16
+ /** Stable identity of a node, persisted to disk so it survives restarts. */
17
+ export interface NodeIdentity {
18
+ nodeId: string;
19
+ displayName: string;
20
+ role: NodeRole;
21
+ /** Public key (base64) — used for app-layer auth of peers. */
22
+ publicKey?: string;
23
+ createdAt: number;
24
+ }
25
+ /** What a node can do — advertised to peers + the harness map. */
26
+ export interface NodeManifest {
27
+ nodeId: string;
28
+ displayName: string;
29
+ role: NodeRole;
30
+ version: string;
31
+ platform: string;
32
+ arch: string;
33
+ /** e.g. "Raspberry Pi 5", "Windows Workstation". */
34
+ deviceFamily?: string;
35
+ modelIdentifier?: string;
36
+ /** Aggregate capabilities of this node's registered workers. */
37
+ capabilities: Capability[];
38
+ /** Worker types this node can run. */
39
+ workers: string[];
40
+ /** Slash/MCP commands this node exposes (for click-to-invoke). */
41
+ commands: string[];
42
+ /** Provider ids available on this node. */
43
+ providers: string[];
44
+ startedAt: number;
45
+ }
46
+ /** A peer known to this node. */
47
+ export interface PeerInfo {
48
+ nodeId: string;
49
+ displayName: string;
50
+ role: NodeRole;
51
+ /** Base URL we reach this peer at, e.g. http://100.x.y.z:47913 . */
52
+ url: string;
53
+ connected: boolean;
54
+ lastSeenAt: number;
55
+ latencyMs?: number;
56
+ manifest?: NodeManifest;
57
+ /** Latest telemetry snapshot (load used by the compute router). */
58
+ hardware?: HardwareSnapshot;
59
+ version?: string;
60
+ }
61
+ export interface HardwareSnapshot {
62
+ cpu: {
63
+ percent: number;
64
+ perCorePercent: number[];
65
+ loadAvg: [number, number, number];
66
+ cores: number;
67
+ model: string;
68
+ };
69
+ memory: {
70
+ totalBytes: number;
71
+ usedBytes: number;
72
+ freeBytes: number;
73
+ percent: number;
74
+ };
75
+ gpu: {
76
+ vendor: string;
77
+ utilizationPercent: number;
78
+ vramUsedBytes: number;
79
+ vramTotalBytes: number;
80
+ name: string;
81
+ } | null;
82
+ disk: {
83
+ mounts: Array<{
84
+ path: string;
85
+ totalBytes: number;
86
+ usedBytes: number;
87
+ percent: number;
88
+ }>;
89
+ };
90
+ network: {
91
+ interfaces: Array<{
92
+ name: string;
93
+ rxBytesPerSec: number;
94
+ txBytesPerSec: number;
95
+ }>;
96
+ };
97
+ system: {
98
+ hostname: string;
99
+ platform: string;
100
+ arch: string;
101
+ uptimeSec: number;
102
+ kernelRelease: string;
103
+ };
104
+ processes: Array<{
105
+ pid: number;
106
+ name: string;
107
+ cpuPercent: number;
108
+ memoryBytes: number;
109
+ }>;
110
+ timestamp: number;
111
+ }
112
+ /** Node status entry consumed by the mesh map (neutral form of nodes.status). */
113
+ export interface NodeStatus {
114
+ nodeId: string;
115
+ displayName: string;
116
+ role: NodeRole;
117
+ platform: string;
118
+ deviceFamily?: string;
119
+ modelIdentifier?: string;
120
+ version: string;
121
+ capabilities: Capability[];
122
+ commands: string[];
123
+ connected: boolean;
124
+ connectedAtMs?: number;
125
+ /** 0–100 rough load, from the latest hardware snapshot. */
126
+ load?: number;
127
+ lastSeenAt: number;
128
+ }
129
+ export type MessageKind = 'hello' | 'welcome' | 'heartbeat' | 'manifest' | 'task.dispatch' | 'task.accepted' | 'task.status' | 'task.result' | 'task.cancel' | 'command' | 'command.result' | 'config.request' | 'config.offer' | 'update.announce' | 'update.request' | 'role.get' | 'role.set' | 'event' | 'telemetry' | 'ack' | 'error';
130
+ /** A dispatched/received task run — tracked for non-blocking handoff + monitoring. */
131
+ export interface RunRecord {
132
+ runId: string;
133
+ status: 'running' | 'completed' | 'failed';
134
+ description: string;
135
+ missionId?: string;
136
+ outputs?: Array<{
137
+ status: string;
138
+ content?: string;
139
+ error?: string;
140
+ }>;
141
+ error?: string;
142
+ startedAt: number;
143
+ completedAt?: number;
144
+ }
145
+ /** Persistent per-device role + server-architecture context. */
146
+ export interface RoleContext {
147
+ /** e.g. "RoboPanda" — the identity a spawned agent adopts. */
148
+ role: string;
149
+ /** Free-text description of this device's job + the wider architecture. */
150
+ architecture?: string;
151
+ /** Extra structured context injected into every task run on this node. */
152
+ extra?: Record<string, unknown>;
153
+ updatedAt: number;
154
+ }
155
+ /** Signed, routable message envelope. */
156
+ export interface FederationEnvelope<T = unknown> {
157
+ /** Protocol version for forward-compat. */
158
+ v: number;
159
+ /** Message id (for request/response correlation). */
160
+ id: string;
161
+ kind: MessageKind;
162
+ from: string;
163
+ to?: string;
164
+ ts: number;
165
+ /** Correlation id — set on replies to the request's id. */
166
+ re?: string;
167
+ data?: T;
168
+ /** Optional bearer/token for app-layer auth. */
169
+ auth?: string;
170
+ }
171
+ /**
172
+ * A minimal, high-frequency frame for the harness to render/animate. Short keys
173
+ * keep bytes low for smooth 1–10 Hz updates. `t` is a frame type; the harness
174
+ * switches on it to drive different animations.
175
+ */
176
+ export interface StreamFrame {
177
+ /** frame type: ev=event, hw=telemetry, hb=heartbeat, nd=node status, rt=routing */
178
+ t: 'ev' | 'hw' | 'hb' | 'nd' | 'rt' | 'log';
179
+ /** origin nodeId */
180
+ n: string;
181
+ /** monotonic sequence for gap detection */
182
+ s: number;
183
+ /** epoch ms */
184
+ ts: number;
185
+ /** compact payload — shape depends on t */
186
+ d?: unknown;
187
+ }
188
+ /** A shareable slice of config a hub can push to satellites. */
189
+ export interface SharedConfig {
190
+ /** Cloud providers (id/name/baseURL/apiKey/enabled). */
191
+ cloudProviders?: Array<Record<string, unknown>>;
192
+ /** Default policy + model. */
193
+ policy?: string;
194
+ defaultModel?: string;
195
+ /** Free-form extra keys. */
196
+ extra?: Record<string, unknown>;
197
+ /** Version/etag so a satellite only re-applies when it changes. */
198
+ revision: number;
199
+ updatedAt: number;
200
+ }
201
+ export interface UpdateAnnounce {
202
+ version: string;
203
+ /** How to fetch the update (npm tag, url, or "git"). */
204
+ channel: 'npm' | 'url' | 'git';
205
+ ref?: string;
206
+ notes?: string;
207
+ /** Minimum protocol version the update requires. */
208
+ minProtocol?: number;
209
+ }
210
+ export declare const FEDERATION_PROTOCOL_VERSION = 1;
211
+ /** Default mesh ports — chosen to sit alongside InfiniBot's 47913/47914. */
212
+ export declare const DEFAULT_MESH_PORT = 47913;
@@ -0,0 +1,3 @@
1
+ export const FEDERATION_PROTOCOL_VERSION = 1;
2
+ /** Default mesh ports — chosen to sit alongside InfiniBot's 47913/47914. */
3
+ export const DEFAULT_MESH_PORT = 47913;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Free / freemium cloud providers that the wizard recommends setting up.
3
+ *
4
+ * Setting up as many as possible enables optimal provider rotation:
5
+ * if one rate-limits (429), the router fails over to the next.
6
+ */
7
+ import type { CloudProviderConfig } from './config-schema.js';
8
+ import type { ProviderModel, WorkerType } from './types.js';
9
+ export interface FreeProviderPreset {
10
+ id: string;
11
+ name: string;
12
+ baseURL: string;
13
+ headerName?: string;
14
+ /** Where to get the API key. */
15
+ keyUrl: string;
16
+ /** True = has a genuinely free tier. */
17
+ free: boolean;
18
+ /** Recommended models per worker type, by id. */
19
+ recommended: Partial<Record<WorkerType, string[]>>;
20
+ /** Known model catalog (used as fallback when /v1/models is unreachable). */
21
+ knownModels: Array<Partial<ProviderModel> & {
22
+ id: string;
23
+ }>;
24
+ notes?: string;
25
+ }
26
+ export declare const FREE_PROVIDERS: FreeProviderPreset[];
27
+ export declare function getProviderPreset(id: string): FreeProviderPreset | undefined;
28
+ export declare function toCloudProviderConfig(preset: FreeProviderPreset, apiKey: string): CloudProviderConfig;