forkit-connect 0.1.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 (51) hide show
  1. package/QUICKSTART.md +55 -0
  2. package/README.md +96 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.js +4724 -0
  5. package/dist/index.d.ts +7 -0
  6. package/dist/index.js +21 -0
  7. package/dist/launcher.d.ts +33 -0
  8. package/dist/launcher.js +9344 -0
  9. package/dist/ps-list-loader.d.ts +5 -0
  10. package/dist/ps-list-loader.js +20 -0
  11. package/dist/v1/agent-observation.d.ts +42 -0
  12. package/dist/v1/agent-observation.js +499 -0
  13. package/dist/v1/api.d.ts +276 -0
  14. package/dist/v1/api.js +390 -0
  15. package/dist/v1/credential-store.d.ts +92 -0
  16. package/dist/v1/credential-store.js +797 -0
  17. package/dist/v1/currency.d.ts +41 -0
  18. package/dist/v1/currency.js +127 -0
  19. package/dist/v1/daemon.d.ts +50 -0
  20. package/dist/v1/daemon.js +265 -0
  21. package/dist/v1/discovery.d.ts +61 -0
  22. package/dist/v1/discovery.js +168 -0
  23. package/dist/v1/filesystem-models.d.ts +11 -0
  24. package/dist/v1/filesystem-models.js +261 -0
  25. package/dist/v1/heartbeat.d.ts +45 -0
  26. package/dist/v1/heartbeat.js +463 -0
  27. package/dist/v1/lifecycle-monitor.d.ts +78 -0
  28. package/dist/v1/lifecycle-monitor.js +512 -0
  29. package/dist/v1/lmstudio.d.ts +11 -0
  30. package/dist/v1/lmstudio.js +148 -0
  31. package/dist/v1/ollama.d.ts +19 -0
  32. package/dist/v1/ollama.js +164 -0
  33. package/dist/v1/openai-compatible.d.ts +12 -0
  34. package/dist/v1/openai-compatible.js +124 -0
  35. package/dist/v1/process-scout.d.ts +50 -0
  36. package/dist/v1/process-scout.js +715 -0
  37. package/dist/v1/providers.d.ts +50 -0
  38. package/dist/v1/providers.js +106 -0
  39. package/dist/v1/service.d.ts +680 -0
  40. package/dist/v1/service.js +8286 -0
  41. package/dist/v1/state.d.ts +87 -0
  42. package/dist/v1/state.js +1318 -0
  43. package/dist/v1/test-credential-backend.d.ts +19 -0
  44. package/dist/v1/test-credential-backend.js +49 -0
  45. package/dist/v1/types.d.ts +873 -0
  46. package/dist/v1/types.js +3 -0
  47. package/dist/v1/update.d.ts +38 -0
  48. package/dist/v1/update.js +184 -0
  49. package/dist/v1/vitality-pulse.d.ts +36 -0
  50. package/dist/v1/vitality-pulse.js +512 -0
  51. package/package.json +53 -0
@@ -0,0 +1,512 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.checkEndpointPortAlive = checkEndpointPortAlive;
7
+ exports.probeGpuHealth = probeGpuHealth;
8
+ exports.classifyPulseStatus = classifyPulseStatus;
9
+ exports.collectVitalityPulse = collectVitalityPulse;
10
+ exports.buildPulseEvents = buildPulseEvents;
11
+ const node_child_process_1 = require("node:child_process");
12
+ const node_net_1 = __importDefault(require("node:net"));
13
+ const node_util_1 = require("node:util");
14
+ const ps_list_loader_1 = require("../ps-list-loader");
15
+ const discovery_1 = require("./discovery");
16
+ const ollama_1 = require("./ollama");
17
+ const process_scout_1 = require("./process-scout");
18
+ const process_scout_2 = require("./process-scout");
19
+ const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
20
+ function nowIso() {
21
+ return new Date().toISOString();
22
+ }
23
+ function roundMetric(value) {
24
+ if (value === null || !Number.isFinite(value))
25
+ return null;
26
+ return Math.round(value * 100) / 100;
27
+ }
28
+ function bytesToMb(value) {
29
+ const numeric = Number(value);
30
+ if (!Number.isFinite(numeric) || numeric <= 0)
31
+ return null;
32
+ return roundMetric(numeric / (1024 * 1024));
33
+ }
34
+ function cpuValue(value) {
35
+ const numeric = Number(value);
36
+ if (!Number.isFinite(numeric) || numeric < 0)
37
+ return null;
38
+ return roundMetric(numeric);
39
+ }
40
+ function parseEndpointPort(endpoint) {
41
+ try {
42
+ const url = new URL(endpoint);
43
+ const port = Number(url.port || (url.protocol === 'https:' ? 443 : 80));
44
+ if (!Number.isFinite(port) || port <= 0)
45
+ return null;
46
+ return {
47
+ host: url.hostname,
48
+ port,
49
+ };
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
55
+ function parseElapsedRuntimeSeconds(value) {
56
+ const raw = String(value || '').trim();
57
+ if (!raw)
58
+ return null;
59
+ const normalized = raw.replace(/\s+/g, '');
60
+ const [dayPart, clockPart] = normalized.includes('-')
61
+ ? normalized.split('-', 2)
62
+ : [null, normalized];
63
+ const dayCount = dayPart === null ? 0 : Number(dayPart);
64
+ if (!Number.isFinite(dayCount) || dayCount < 0)
65
+ return null;
66
+ const clockSegments = clockPart.split(':').map((segment) => Number(segment));
67
+ if (clockSegments.some((segment) => !Number.isFinite(segment) || segment < 0)) {
68
+ return null;
69
+ }
70
+ if (clockSegments.length === 2) {
71
+ const minutes = clockSegments[0] ?? 0;
72
+ const seconds = clockSegments[1] ?? 0;
73
+ return Math.round((dayCount * 24 * 60 * 60) + (minutes * 60) + seconds);
74
+ }
75
+ if (clockSegments.length === 3) {
76
+ const hours = clockSegments[0] ?? 0;
77
+ const minutes = clockSegments[1] ?? 0;
78
+ const seconds = clockSegments[2] ?? 0;
79
+ return Math.round((dayCount * 24 * 60 * 60) + (hours * 60 * 60) + (minutes * 60) + seconds);
80
+ }
81
+ return null;
82
+ }
83
+ async function probeProcessElapsedSeconds(pid) {
84
+ try {
85
+ const { stdout } = await execFileAsync('ps', ['-o', 'etime=', '-p', String(pid)]);
86
+ const firstLine = String(stdout || '').split(/\r?\n/).map((line) => line.trim()).find(Boolean);
87
+ return firstLine ? parseElapsedRuntimeSeconds(firstLine) : null;
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ }
93
+ async function checkEndpointPortHealth(endpoint, timeoutMs = 1500) {
94
+ const startedAt = Date.now();
95
+ const alive = await checkEndpointPortAlive(endpoint, timeoutMs);
96
+ const latencyMs = alive ? roundMetric(Date.now() - startedAt) : null;
97
+ return { alive, latencyMs };
98
+ }
99
+ function metricAvailability(available, note, source) {
100
+ return {
101
+ available,
102
+ note,
103
+ source,
104
+ };
105
+ }
106
+ function isInventoryOnlyRuntime(runtime) {
107
+ return runtime.runtimeType === 'local-filesystem';
108
+ }
109
+ function buildMetricAvailability(input) {
110
+ const processMissingNote = 'Local process metrics are unavailable because Connect could not safely match a runtime process this cycle.';
111
+ const processPartialNote = 'Local process visibility is limited right now, so this metric was not exposed by the operating system sample.';
112
+ const latencyMissingNote = input.portAlive
113
+ ? 'Latency probe ran without a stable round-trip measurement this cycle.'
114
+ : 'Latency is unavailable because the local runtime endpoint did not accept a probe connection.';
115
+ const gpuMissingNote = input.gpuAvailable
116
+ ? 'GPU probe responded, but utilization details were not exposed this cycle.'
117
+ : 'GPU metrics are unavailable because no supported local GPU probe responded.';
118
+ if (input.inventoryOnly) {
119
+ const inventoryOnlyNote = 'Metrics are unavailable because this source is inventory-only local model discovery, not a live runtime endpoint.';
120
+ return {
121
+ cpu_usage_percent: metricAvailability(false, inventoryOnlyNote, 'inventory_scan'),
122
+ memory_usage_mb: metricAvailability(false, inventoryOnlyNote, 'inventory_scan'),
123
+ uptime_seconds: metricAvailability(false, inventoryOnlyNote, 'inventory_scan'),
124
+ runtime_latency_ms: metricAvailability(false, inventoryOnlyNote, 'inventory_scan'),
125
+ token_metrics: metricAvailability(false, 'Token metrics stay unavailable in metadata-only observation mode because Connect does not inspect prompts or responses.', 'privacy_boundary'),
126
+ gpu_utilization_percent: metricAvailability(false, inventoryOnlyNote, 'inventory_scan'),
127
+ gpu_vram_used_mb: metricAvailability(false, inventoryOnlyNote, 'inventory_scan'),
128
+ };
129
+ }
130
+ return {
131
+ cpu_usage_percent: input.processMatched
132
+ ? input.cpuUsagePercent !== null
133
+ ? metricAvailability(true, 'Captured from the local process CPU sample.', 'process_list')
134
+ : metricAvailability(false, processPartialNote, 'process_list')
135
+ : metricAvailability(false, processMissingNote, 'process_match'),
136
+ memory_usage_mb: input.processMatched
137
+ ? input.memoryUsageMb !== null
138
+ ? metricAvailability(true, 'Captured from the local process memory sample.', 'process_list')
139
+ : metricAvailability(false, processPartialNote, 'process_list')
140
+ : metricAvailability(false, processMissingNote, 'process_match'),
141
+ uptime_seconds: input.processMatched
142
+ ? input.uptimeSeconds !== null
143
+ ? metricAvailability(true, 'Captured from operating-system elapsed process time.', 'process_elapsed')
144
+ : metricAvailability(false, 'Runtime uptime is unavailable because the operating system did not expose elapsed process time.', 'process_elapsed')
145
+ : metricAvailability(false, processMissingNote, 'process_match'),
146
+ runtime_latency_ms: input.portAlive && input.runtimeLatencyMs !== null
147
+ ? metricAvailability(true, 'Captured from the local TCP connect probe.', 'tcp_probe')
148
+ : metricAvailability(false, latencyMissingNote, 'tcp_probe'),
149
+ token_metrics: metricAvailability(false, 'Token metrics stay unavailable in metadata-only observation mode because Connect does not inspect prompts or responses.', 'privacy_boundary'),
150
+ gpu_utilization_percent: input.gpuAvailable && input.gpuUtilizationPercent !== null
151
+ ? metricAvailability(true, 'Captured from the supported local GPU probe.', 'gpu_probe')
152
+ : metricAvailability(false, gpuMissingNote, input.gpuAvailable ? 'gpu_probe' : 'gpu_probe_unavailable'),
153
+ gpu_vram_used_mb: input.gpuAvailable && input.gpuVramUsedMb !== null
154
+ ? metricAvailability(true, 'Captured from the supported local GPU probe.', 'gpu_probe')
155
+ : metricAvailability(false, gpuMissingNote, input.gpuAvailable ? 'gpu_probe' : 'gpu_probe_unavailable'),
156
+ };
157
+ }
158
+ function buildDegradedObservation(input) {
159
+ if (input.inventoryOnly) {
160
+ return {
161
+ degradedMode: 'normal',
162
+ degradedReasons: [],
163
+ };
164
+ }
165
+ if (input.providerStatus === 'unavailable' || input.providerStatus === 'error' || input.portAlive === false) {
166
+ return {
167
+ degradedMode: 'provider_unavailable',
168
+ degradedReasons: ['The local runtime endpoint did not respond to the observation probe.'],
169
+ };
170
+ }
171
+ const reasons = [];
172
+ if (!input.processMatched) {
173
+ reasons.push('Connect could not safely match a local runtime process, so CPU, memory, and uptime are partial.');
174
+ }
175
+ else {
176
+ if (input.cpuUsagePercent === null || input.memoryUsageMb === null) {
177
+ reasons.push('The operating-system process sample did not expose full CPU and memory metrics this cycle.');
178
+ }
179
+ if (input.uptimeSeconds === null) {
180
+ reasons.push('The operating system did not expose elapsed runtime uptime for this process.');
181
+ }
182
+ }
183
+ if (input.runtimeLatencyMs === null) {
184
+ reasons.push('The local latency probe did not produce a stable runtime round-trip measurement.');
185
+ }
186
+ return {
187
+ degradedMode: reasons.length > 0 ? 'partial_metrics' : 'normal',
188
+ degradedReasons: reasons,
189
+ };
190
+ }
191
+ function overallDegradedObservation(entries) {
192
+ if (entries.some((entry) => entry.degraded_mode === 'provider_unavailable')) {
193
+ return {
194
+ degradedMode: 'provider_unavailable',
195
+ degradedReasons: [...new Set(entries.flatMap((entry) => entry.degraded_reasons ?? []).filter(Boolean))],
196
+ };
197
+ }
198
+ if (entries.some((entry) => entry.degraded_mode === 'partial_metrics')) {
199
+ return {
200
+ degradedMode: 'partial_metrics',
201
+ degradedReasons: [...new Set(entries.flatMap((entry) => entry.degraded_reasons ?? []).filter(Boolean))],
202
+ };
203
+ }
204
+ return {
205
+ degradedMode: 'normal',
206
+ degradedReasons: [],
207
+ };
208
+ }
209
+ async function checkEndpointPortAlive(endpoint, timeoutMs = 1500) {
210
+ const target = parseEndpointPort(endpoint);
211
+ if (!target)
212
+ return false;
213
+ return await new Promise((resolve) => {
214
+ const socket = new node_net_1.default.Socket();
215
+ let resolved = false;
216
+ const finish = (value) => {
217
+ if (resolved)
218
+ return;
219
+ resolved = true;
220
+ socket.destroy();
221
+ resolve(value);
222
+ };
223
+ socket.setTimeout(timeoutMs);
224
+ socket.once('connect', () => finish(true));
225
+ socket.once('timeout', () => finish(false));
226
+ socket.once('error', () => finish(false));
227
+ socket.connect(target.port, target.host);
228
+ });
229
+ }
230
+ async function probeGpuHealth() {
231
+ try {
232
+ const { stdout } = await execFileAsync('nvidia-smi', [
233
+ '--query-gpu=utilization.gpu,memory.used',
234
+ '--format=csv,noheader,nounits',
235
+ ]);
236
+ const firstLine = String(stdout || '').split(/\r?\n/).map((line) => line.trim()).find(Boolean);
237
+ if (!firstLine) {
238
+ return { gpu_available: true, gpu_vram_used_mb: null, gpu_utilization_percent: null };
239
+ }
240
+ const [utilizationRaw, memoryRaw] = firstLine.split(',').map((item) => item.trim());
241
+ const gpuUtilizationPercent = roundMetric(Number(utilizationRaw));
242
+ const gpuVramUsedMb = roundMetric(Number(memoryRaw));
243
+ return {
244
+ gpu_available: true,
245
+ gpu_vram_used_mb: Number.isFinite(Number(memoryRaw)) ? gpuVramUsedMb : null,
246
+ gpu_utilization_percent: Number.isFinite(Number(utilizationRaw)) ? gpuUtilizationPercent : null,
247
+ };
248
+ }
249
+ catch {
250
+ return {
251
+ gpu_available: false,
252
+ gpu_vram_used_mb: null,
253
+ gpu_utilization_percent: null,
254
+ };
255
+ }
256
+ }
257
+ function classifyPulseStatus(input) {
258
+ if (input.inventoryOnly) {
259
+ return 'unknown';
260
+ }
261
+ const reachable = input.providerStatus === 'detected' || input.portAlive === true;
262
+ const previouslyKnownNowUnreachable = input.knownRuntime
263
+ && input.portAlive === false
264
+ && input.processAlive === false
265
+ && input.providerStatus !== 'detected';
266
+ if (previouslyKnownNowUnreachable) {
267
+ return 'red';
268
+ }
269
+ if (!reachable) {
270
+ return 'unknown';
271
+ }
272
+ const highPressure = (input.cpuUsagePercent !== null && input.cpuUsagePercent >= 85)
273
+ || (input.memoryUsageMb !== null && input.memoryUsageMb >= 4096)
274
+ || (input.gpuUtilizationPercent !== null && input.gpuUtilizationPercent >= 90)
275
+ || (input.gpuVramUsedMb !== null && input.gpuVramUsedMb >= 16384);
276
+ const partialMetrics = input.processAlive !== true
277
+ || input.cpuUsagePercent === null
278
+ || input.memoryUsageMb === null
279
+ || (input.gpuAvailable && (input.gpuUtilizationPercent === null || input.gpuVramUsedMb === null));
280
+ if (highPressure || partialMetrics) {
281
+ return 'amber';
282
+ }
283
+ return 'green';
284
+ }
285
+ function processByPid(processes) {
286
+ return new Map(processes.map((entry) => [entry.pid, entry]));
287
+ }
288
+ function findProcessForRuntime(runtime, processScoutResults) {
289
+ return processScoutResults.find((entry) => (0, process_scout_2.matchesRuntimeObservation)(entry, runtime.runtimeName, runtime.endpoint)) ?? null;
290
+ }
291
+ function findModelForRuntime(state, runtime) {
292
+ return state.detected_models.find((model) => model.runtime === runtime.runtime && model.status !== 'ignored') ?? null;
293
+ }
294
+ function findRuntimePassportForRuntime(state, runtime) {
295
+ return state.runtime_passports.find((runtimePassport) => runtimePassport.discoveryHash === runtime.discoveryHash)
296
+ ?? state.runtime_passports.find((runtimePassport) => runtimePassport.runtime_name === runtime.runtimeName && runtimePassport.runtime_type === runtime.runtimeType);
297
+ }
298
+ function findBindingForModel(state, model, runtimeIdentity) {
299
+ if (!model)
300
+ return null;
301
+ const registrationKey = (0, discovery_1.buildModelRegistrationKey)(model, runtimeIdentity);
302
+ return state.model_bindings.find((item) => item.modelKey === (0, ollama_1.modelKey)(model)
303
+ || item.discoveryHash === model.discoveryHash
304
+ || item.registrationKey === registrationKey) ?? null;
305
+ }
306
+ function registrationStateForModel(state, model, runtimeIdentity) {
307
+ if (!model) {
308
+ return {
309
+ registrationState: 'unknown',
310
+ registrationKey: null,
311
+ boundPassportGaid: null,
312
+ bindingStatus: null,
313
+ };
314
+ }
315
+ const registrationKey = (0, discovery_1.buildModelRegistrationKey)(model, runtimeIdentity);
316
+ const binding = findBindingForModel(state, model, runtimeIdentity);
317
+ if (!binding) {
318
+ return {
319
+ registrationState: 'unregistered',
320
+ registrationKey,
321
+ boundPassportGaid: null,
322
+ bindingStatus: null,
323
+ };
324
+ }
325
+ return {
326
+ registrationState: binding.status === 'bound' ? 'registered' : 'unregistered',
327
+ registrationKey: binding.registrationKey ?? registrationKey,
328
+ boundPassportGaid: binding.gaid ?? null,
329
+ bindingStatus: binding.status,
330
+ };
331
+ }
332
+ function connectionClassificationForPulse(input) {
333
+ if (input.bindingStatus === 'bound') {
334
+ return 'registered_runtime';
335
+ }
336
+ if (input.providerStatus === 'unavailable' || input.providerStatus === 'error') {
337
+ return 'provider_unavailable';
338
+ }
339
+ if (input.model) {
340
+ return 'known_unregistered';
341
+ }
342
+ return null;
343
+ }
344
+ function providerStatusForRuntime(runtime, portAlive) {
345
+ if (isInventoryOnlyRuntime(runtime)) {
346
+ return runtime.status === 'error' ? 'error' : 'detected';
347
+ }
348
+ if (runtime.status === 'error')
349
+ return portAlive ? 'detected' : 'error';
350
+ if (runtime.status === 'detected' || runtime.status === 'connected')
351
+ return portAlive ? 'detected' : 'unavailable';
352
+ return portAlive ? 'detected' : 'unknown';
353
+ }
354
+ function overallPulseStatus(entries) {
355
+ if (entries.some((entry) => entry.pulse_status === 'red'))
356
+ return 'red';
357
+ if (entries.some((entry) => entry.pulse_status === 'amber'))
358
+ return 'amber';
359
+ if (entries.some((entry) => entry.pulse_status === 'green'))
360
+ return 'green';
361
+ return 'unknown';
362
+ }
363
+ async function collectVitalityPulse(state, dependencies) {
364
+ const measuredAt = dependencies?.measuredAt ?? nowIso();
365
+ const processScoutResults = dependencies?.processScoutResults ?? await (0, process_scout_1.scanLikelyAiProcesses)();
366
+ const processes = dependencies?.processList ? await dependencies.processList() : await (0, ps_list_loader_1.listProcesses)();
367
+ const processesByPid = processByPid(processes);
368
+ const gpu = dependencies?.gpuProbe ? await dependencies.gpuProbe() : await probeGpuHealth();
369
+ const runtimes = state.detected_runtimes;
370
+ const entries = await Promise.all(runtimes.map(async (runtime) => {
371
+ const inventoryOnly = isInventoryOnlyRuntime(runtime);
372
+ const matchedProcess = findProcessForRuntime(runtime, processScoutResults);
373
+ const rawProcess = matchedProcess ? processesByPid.get(matchedProcess.pid) ?? null : null;
374
+ const endpointHealth = inventoryOnly
375
+ ? { alive: null, latencyMs: null }
376
+ : dependencies?.portChecker
377
+ ? await (async () => {
378
+ const startedAt = Date.now();
379
+ const alive = await dependencies.portChecker?.(runtime.endpoint);
380
+ return {
381
+ alive: Boolean(alive),
382
+ latencyMs: alive ? roundMetric(Date.now() - startedAt) : null,
383
+ };
384
+ })()
385
+ : await checkEndpointPortHealth(runtime.endpoint);
386
+ const portAlive = endpointHealth.alive;
387
+ const processAlive = inventoryOnly ? null : matchedProcess ? Boolean(rawProcess || matchedProcess.pid > 0) : false;
388
+ const model = findModelForRuntime(state, runtime);
389
+ const runtimePassport = findRuntimePassportForRuntime(state, runtime);
390
+ const registration = registrationStateForModel(state, model, state.runtime_identity.runtimeId);
391
+ const providerStatus = providerStatusForRuntime(runtime, portAlive);
392
+ const cpuUsagePercent = inventoryOnly ? null : rawProcess ? cpuValue(rawProcess.cpu) : null;
393
+ const memoryUsageMb = inventoryOnly ? null : rawProcess ? bytesToMb(rawProcess.memory) : null;
394
+ const uptimeSeconds = inventoryOnly
395
+ ? null
396
+ : matchedProcess
397
+ ? (dependencies?.processUptimeProbe
398
+ ? await dependencies.processUptimeProbe(matchedProcess.pid)
399
+ : await probeProcessElapsedSeconds(matchedProcess.pid))
400
+ : null;
401
+ const metricAvailability = buildMetricAvailability({
402
+ inventoryOnly,
403
+ processMatched: Boolean(matchedProcess),
404
+ cpuUsagePercent,
405
+ memoryUsageMb,
406
+ uptimeSeconds,
407
+ runtimeLatencyMs: endpointHealth.latencyMs,
408
+ portAlive,
409
+ gpuAvailable: gpu.gpu_available,
410
+ gpuUtilizationPercent: gpu.gpu_utilization_percent,
411
+ gpuVramUsedMb: gpu.gpu_vram_used_mb,
412
+ });
413
+ const degradedObservation = buildDegradedObservation({
414
+ inventoryOnly,
415
+ providerStatus,
416
+ processMatched: Boolean(matchedProcess),
417
+ cpuUsagePercent,
418
+ memoryUsageMb,
419
+ uptimeSeconds,
420
+ runtimeLatencyMs: endpointHealth.latencyMs,
421
+ portAlive,
422
+ });
423
+ const pulseStatus = classifyPulseStatus({
424
+ inventoryOnly,
425
+ knownRuntime: true,
426
+ providerStatus,
427
+ portAlive,
428
+ processAlive,
429
+ cpuUsagePercent,
430
+ memoryUsageMb,
431
+ gpuAvailable: gpu.gpu_available,
432
+ gpuUtilizationPercent: gpu.gpu_utilization_percent,
433
+ gpuVramUsedMb: gpu.gpu_vram_used_mb,
434
+ });
435
+ return {
436
+ measured_at: measuredAt,
437
+ runtime_gaid: runtimePassport?.runtime_gaid ?? null,
438
+ runtime_name: runtime.runtimeName,
439
+ runtime_type: runtime.runtimeType,
440
+ model_name: model?.model ?? null,
441
+ discovery_hash: model?.discoveryHash ?? runtime.discoveryHash ?? null,
442
+ registration_key: registration.registrationKey,
443
+ bound_passport_gaid: registration.boundPassportGaid,
444
+ binding_status: registration.bindingStatus,
445
+ connection_classification: connectionClassificationForPulse({
446
+ model,
447
+ bindingStatus: registration.bindingStatus,
448
+ providerStatus,
449
+ }),
450
+ provider_status: providerStatus,
451
+ registration_state: registration.registrationState,
452
+ port_alive: portAlive,
453
+ process_alive: inventoryOnly ? null : matchedProcess ? processAlive : false,
454
+ cpu_usage_percent: cpuUsagePercent,
455
+ memory_usage_mb: memoryUsageMb,
456
+ uptime_seconds: uptimeSeconds,
457
+ runtime_latency_ms: endpointHealth.latencyMs,
458
+ gpu_available: gpu.gpu_available,
459
+ gpu_vram_used_mb: gpu.gpu_vram_used_mb,
460
+ gpu_utilization_percent: gpu.gpu_utilization_percent,
461
+ observation_mode: 'metadata_only',
462
+ degraded_mode: degradedObservation.degradedMode,
463
+ degraded_reasons: degradedObservation.degradedReasons,
464
+ metric_availability: metricAvailability,
465
+ pulse_status: pulseStatus,
466
+ };
467
+ }));
468
+ const overallDegraded = overallDegradedObservation(entries);
469
+ return {
470
+ measured_at: measuredAt,
471
+ pulse_status: overallPulseStatus(entries),
472
+ observation_mode: 'metadata_only',
473
+ degraded_mode: overallDegraded.degradedMode,
474
+ degraded_reasons: overallDegraded.degradedReasons,
475
+ entries,
476
+ green_count: entries.filter((entry) => entry.pulse_status === 'green').length,
477
+ amber_count: entries.filter((entry) => entry.pulse_status === 'amber').length,
478
+ red_count: entries.filter((entry) => entry.pulse_status === 'red').length,
479
+ unknown_count: entries.filter((entry) => entry.pulse_status === 'unknown').length,
480
+ };
481
+ }
482
+ function buildPulseEvents(summary) {
483
+ return summary.entries.map((entry) => ({
484
+ measured_at: entry.measured_at,
485
+ runtime_gaid: entry.runtime_gaid ?? null,
486
+ runtime_name: entry.runtime_name,
487
+ runtime_type: entry.runtime_type,
488
+ model_name: entry.model_name,
489
+ discovery_hash: entry.discovery_hash,
490
+ registration_key: entry.registration_key,
491
+ bound_passport_gaid: entry.bound_passport_gaid,
492
+ binding_status: entry.binding_status,
493
+ connection_classification: entry.connection_classification,
494
+ provider_status: entry.provider_status,
495
+ registration_state: entry.registration_state,
496
+ port_alive: entry.port_alive,
497
+ process_alive: entry.process_alive,
498
+ cpu_usage_percent: entry.cpu_usage_percent,
499
+ memory_usage_mb: entry.memory_usage_mb,
500
+ uptime_seconds: entry.uptime_seconds ?? null,
501
+ runtime_latency_ms: entry.runtime_latency_ms ?? null,
502
+ gpu_available: entry.gpu_available,
503
+ gpu_vram_used_mb: entry.gpu_vram_used_mb,
504
+ gpu_utilization_percent: entry.gpu_utilization_percent,
505
+ observation_mode: entry.observation_mode ?? 'metadata_only',
506
+ degraded_mode: entry.degraded_mode ?? 'normal',
507
+ degraded_reasons: entry.degraded_reasons ?? [],
508
+ ...(entry.metric_availability ? { metric_availability: entry.metric_availability } : {}),
509
+ pulse_status: entry.pulse_status,
510
+ }));
511
+ }
512
+ //# sourceMappingURL=vitality-pulse.js.map
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "forkit-connect",
3
+ "version": "0.1.0",
4
+ "description": "Forkit Connect Local Engine - The Global AI Governance Fabric",
5
+ "license": "MIT",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "engines": {
9
+ "node": ">=20.0.0"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "README.md",
14
+ "QUICKSTART.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "pnpm run clean && tsc -p tsconfig.build.json",
18
+ "dev": "tsc -w -p tsconfig.build.json",
19
+ "clean": "rm -rf dist",
20
+ "prepack": "pnpm run build && node ./scripts/prune-package-dist.js",
21
+ "install:ubuntu": "bash ./scripts/install-ubuntu-local.sh",
22
+ "smoke:package": "bash ./scripts/smoke-package.sh",
23
+ "desktop:dev": "pnpm run build && node dist/launcher.js",
24
+ "desktop:build": "pnpm run build && node dist/launcher.js --build-shell",
25
+ "deploy:local": "bash scripts/deploy-local.sh"
26
+ },
27
+ "bin": {
28
+ "forkit-connect": "./dist/cli.js"
29
+ },
30
+ "dependencies": {
31
+ "@modelcontextprotocol/sdk": "^1.0.1",
32
+ "better-sqlite3": "^11.10.0",
33
+ "cors": "^2.8.6",
34
+ "express": "^4.19.2",
35
+ "keytar": "^7.9.0",
36
+ "ps-list": "^8.1.1",
37
+ "uuid": "^10.0.0",
38
+ "ws": "^8.16.0",
39
+ "zod": "^3.22.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/better-sqlite3": "^7.6.13",
43
+ "@types/cors": "^2.8.19",
44
+ "@types/express": "^4.17.21",
45
+ "@types/node": "^20.0.0",
46
+ "@types/uuid": "^10.0.0",
47
+ "@types/ws": "^8.5.10",
48
+ "typescript": "^5.0.0"
49
+ },
50
+ "publishConfig": {
51
+ "access": "public"
52
+ }
53
+ }