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.
- package/QUICKSTART.md +55 -0
- package/README.md +96 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +4724 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +21 -0
- package/dist/launcher.d.ts +33 -0
- package/dist/launcher.js +9344 -0
- package/dist/ps-list-loader.d.ts +5 -0
- package/dist/ps-list-loader.js +20 -0
- package/dist/v1/agent-observation.d.ts +42 -0
- package/dist/v1/agent-observation.js +499 -0
- package/dist/v1/api.d.ts +276 -0
- package/dist/v1/api.js +390 -0
- package/dist/v1/credential-store.d.ts +92 -0
- package/dist/v1/credential-store.js +797 -0
- package/dist/v1/currency.d.ts +41 -0
- package/dist/v1/currency.js +127 -0
- package/dist/v1/daemon.d.ts +50 -0
- package/dist/v1/daemon.js +265 -0
- package/dist/v1/discovery.d.ts +61 -0
- package/dist/v1/discovery.js +168 -0
- package/dist/v1/filesystem-models.d.ts +11 -0
- package/dist/v1/filesystem-models.js +261 -0
- package/dist/v1/heartbeat.d.ts +45 -0
- package/dist/v1/heartbeat.js +463 -0
- package/dist/v1/lifecycle-monitor.d.ts +78 -0
- package/dist/v1/lifecycle-monitor.js +512 -0
- package/dist/v1/lmstudio.d.ts +11 -0
- package/dist/v1/lmstudio.js +148 -0
- package/dist/v1/ollama.d.ts +19 -0
- package/dist/v1/ollama.js +164 -0
- package/dist/v1/openai-compatible.d.ts +12 -0
- package/dist/v1/openai-compatible.js +124 -0
- package/dist/v1/process-scout.d.ts +50 -0
- package/dist/v1/process-scout.js +715 -0
- package/dist/v1/providers.d.ts +50 -0
- package/dist/v1/providers.js +106 -0
- package/dist/v1/service.d.ts +680 -0
- package/dist/v1/service.js +8286 -0
- package/dist/v1/state.d.ts +87 -0
- package/dist/v1/state.js +1318 -0
- package/dist/v1/test-credential-backend.d.ts +19 -0
- package/dist/v1/test-credential-backend.js +49 -0
- package/dist/v1/types.d.ts +873 -0
- package/dist/v1/types.js +3 -0
- package/dist/v1/update.d.ts +38 -0
- package/dist/v1/update.js +184 -0
- package/dist/v1/vitality-pulse.d.ts +36 -0
- package/dist/v1/vitality-pulse.js +512 -0
- 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
|
+
}
|