forkit-ai-footprints 0.2.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/LICENSE +21 -0
- package/PRIVACY.md +150 -0
- package/README.md +313 -0
- package/STATUS.md +136 -0
- package/dist/agents.d.ts +22 -0
- package/dist/agents.js +281 -0
- package/dist/aggregate.d.ts +28 -0
- package/dist/aggregate.js +151 -0
- package/dist/assets/forkit-icon-dark.png +0 -0
- package/dist/assets/forkit-icon-light.png +0 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Info.plist +5 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/MacOS/Forkit AI Footprint +0 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/ForkitAIFootprint.icns +0 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/ForkitStatusTemplate.png +0 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/agents.d.ts +22 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/agents.js +281 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/aggregate.d.ts +28 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/aggregate.js +151 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/assets/forkit-icon-dark.png +0 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/assets/forkit-icon-light.png +0 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/census.d.ts +3 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/census.js +196 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/cli.d.ts +32 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/cli.js +404 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/doctor.d.ts +14 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/doctor.js +71 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/endpoints.d.ts +8 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/endpoints.js +45 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/evaluate.d.ts +44 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/evaluate.js +102 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/filesystem.d.ts +11 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/filesystem.js +226 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/format.d.ts +7 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/format.js +78 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/hash.d.ts +4 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/hash.js +16 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/index.d.ts +22 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/index.js +58 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/insights.d.ts +13 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/insights.js +76 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/local-device.d.ts +19 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/local-device.js +81 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/locales.json +272 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/localization.d.ts +277 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/localization.js +55 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/mcp.d.ts +9 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/mcp.js +93 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/monitor.d.ts +99 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/monitor.js +285 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/base.d.ts +34 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/base.js +88 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/index.d.ts +6 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/index.js +24 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/lmstudio.d.ts +9 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/lmstudio.js +71 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/ollama.d.ts +12 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/ollama.js +171 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/openai-compatible.d.ts +10 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/openai-compatible.js +68 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/resource-evidence.d.ts +17 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/resource-evidence.js +52 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-activity.d.ts +5 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-activity.js +66 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-identity.d.ts +7 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-identity.js +66 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/server.d.ts +21 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/server.js +308 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/share-page.d.ts +75 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/share-page.js +167 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/sharing.d.ts +35 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/sharing.js +67 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/tools.d.ts +9 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/tools.js +106 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/types.d.ts +166 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/types.js +3 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/version.d.ts +4 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/version.js +13 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/index.d.ts +76 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/index.js +366 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/license +9 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/package.json +44 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/readme.md +56 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/vendor/fastlist-0.3.0-x64.exe +0 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/vendor/fastlist-0.3.0-x86.exe +0 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/package.json +6 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/runtime/node +0 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/_CodeSignature/CodeResources +931 -0
- package/dist/census.d.ts +3 -0
- package/dist/census.js +196 -0
- package/dist/cli.d.ts +32 -0
- package/dist/cli.js +404 -0
- package/dist/doctor.d.ts +14 -0
- package/dist/doctor.js +71 -0
- package/dist/endpoints.d.ts +8 -0
- package/dist/endpoints.js +45 -0
- package/dist/evaluate.d.ts +44 -0
- package/dist/evaluate.js +102 -0
- package/dist/filesystem.d.ts +11 -0
- package/dist/filesystem.js +226 -0
- package/dist/format.d.ts +7 -0
- package/dist/format.js +78 -0
- package/dist/hash.d.ts +4 -0
- package/dist/hash.js +16 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +58 -0
- package/dist/insights.d.ts +13 -0
- package/dist/insights.js +76 -0
- package/dist/local-device.d.ts +19 -0
- package/dist/local-device.js +81 -0
- package/dist/locales.json +272 -0
- package/dist/localization.d.ts +277 -0
- package/dist/localization.js +55 -0
- package/dist/mcp.d.ts +9 -0
- package/dist/mcp.js +93 -0
- package/dist/monitor.d.ts +99 -0
- package/dist/monitor.js +285 -0
- package/dist/providers/base.d.ts +34 -0
- package/dist/providers/base.js +88 -0
- package/dist/providers/index.d.ts +6 -0
- package/dist/providers/index.js +24 -0
- package/dist/providers/lmstudio.d.ts +9 -0
- package/dist/providers/lmstudio.js +71 -0
- package/dist/providers/ollama.d.ts +12 -0
- package/dist/providers/ollama.js +171 -0
- package/dist/providers/openai-compatible.d.ts +10 -0
- package/dist/providers/openai-compatible.js +68 -0
- package/dist/resource-evidence.d.ts +17 -0
- package/dist/resource-evidence.js +52 -0
- package/dist/runtime-activity.d.ts +5 -0
- package/dist/runtime-activity.js +66 -0
- package/dist/runtime-identity.d.ts +7 -0
- package/dist/runtime-identity.js +66 -0
- package/dist/server.d.ts +21 -0
- package/dist/server.js +308 -0
- package/dist/share-page.d.ts +75 -0
- package/dist/share-page.js +167 -0
- package/dist/sharing.d.ts +35 -0
- package/dist/sharing.js +67 -0
- package/dist/tools.d.ts +9 -0
- package/dist/tools.js +106 -0
- package/dist/types.d.ts +166 -0
- package/dist/types.js +3 -0
- package/dist/version.d.ts +4 -0
- package/dist/version.js +13 -0
- package/docs/GLOBAL_DISTRIBUTION.md +79 -0
- package/docs/MACOS_ACCURACY_GATE.md +140 -0
- package/docs/MACOS_TESTER_GUIDE.md +88 -0
- package/examples/macos-truth.template.json +10 -0
- package/package.json +75 -0
- package/release/channels.json +41 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OllamaProvider = void 0;
|
|
4
|
+
const endpoints_1 = require("../endpoints");
|
|
5
|
+
const hash_1 = require("../hash");
|
|
6
|
+
const runtime_identity_1 = require("../runtime-identity");
|
|
7
|
+
const base_1 = require("./base");
|
|
8
|
+
function parseSha256(value) {
|
|
9
|
+
if (!value)
|
|
10
|
+
return null;
|
|
11
|
+
const match = /^(?:sha256:)?([0-9a-f]{64})$/i.exec(value);
|
|
12
|
+
return match?.[1]?.toLowerCase() ?? null;
|
|
13
|
+
}
|
|
14
|
+
function validOllamaVersion(value) {
|
|
15
|
+
const body = (0, base_1.readRecord)(value);
|
|
16
|
+
const version = (0, base_1.readString)(body?.version);
|
|
17
|
+
return version !== null && /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(version);
|
|
18
|
+
}
|
|
19
|
+
function validModelArray(value) {
|
|
20
|
+
const body = (0, base_1.readRecord)(value);
|
|
21
|
+
return body !== null && Array.isArray(body.models) ? body.models : null;
|
|
22
|
+
}
|
|
23
|
+
class OllamaProvider {
|
|
24
|
+
fetchImpl;
|
|
25
|
+
verifyIdentity;
|
|
26
|
+
name = 'ollama';
|
|
27
|
+
endpoint;
|
|
28
|
+
detailsCache = new Map();
|
|
29
|
+
constructor(endpoint = 'http://localhost:11434', fetchImpl = fetch, verifyIdentity = runtime_identity_1.verifyMacosLoopbackRuntimeIdentity) {
|
|
30
|
+
this.fetchImpl = fetchImpl;
|
|
31
|
+
this.verifyIdentity = verifyIdentity;
|
|
32
|
+
const safeEndpoint = (0, endpoints_1.parseLoopbackEndpoint)(endpoint);
|
|
33
|
+
if (!safeEndpoint)
|
|
34
|
+
throw new Error('Ollama endpoint must be loopback HTTP(S).');
|
|
35
|
+
this.endpoint = safeEndpoint;
|
|
36
|
+
}
|
|
37
|
+
async scan(observedAt) {
|
|
38
|
+
if (!await this.verifyIdentity(this.endpoint.url, 'ollama')) {
|
|
39
|
+
return {
|
|
40
|
+
runtime: (0, base_1.createRuntime)({
|
|
41
|
+
name: this.name,
|
|
42
|
+
endpoint: this.endpoint.display,
|
|
43
|
+
status: 'unavailable',
|
|
44
|
+
modelCount: 0,
|
|
45
|
+
observedAt,
|
|
46
|
+
errorCode: 'runtime_identity_unverified',
|
|
47
|
+
}),
|
|
48
|
+
models: [],
|
|
49
|
+
warnings: [{
|
|
50
|
+
code: 'runtime_identity_unverified',
|
|
51
|
+
message: 'A loopback response could not be tied to the supported Ollama executable.',
|
|
52
|
+
scope: 'runtime',
|
|
53
|
+
}],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const [version, tags, running] = await Promise.all([
|
|
57
|
+
(0, base_1.fetchJson)(`${this.endpoint.url}/api/version`, { method: 'GET' }, this.fetchImpl),
|
|
58
|
+
(0, base_1.fetchJson)(`${this.endpoint.url}/api/tags`, { method: 'GET' }, this.fetchImpl),
|
|
59
|
+
(0, base_1.fetchJson)(`${this.endpoint.url}/api/ps`, { method: 'GET' }, this.fetchImpl),
|
|
60
|
+
]);
|
|
61
|
+
const tagEntries = tags.ok ? validModelArray(tags.body) : null;
|
|
62
|
+
if (!version.ok || !validOllamaVersion(version.body) || tagEntries === null) {
|
|
63
|
+
return {
|
|
64
|
+
runtime: (0, base_1.createRuntime)({
|
|
65
|
+
name: this.name,
|
|
66
|
+
endpoint: this.endpoint.display,
|
|
67
|
+
status: 'unavailable',
|
|
68
|
+
modelCount: 0,
|
|
69
|
+
observedAt,
|
|
70
|
+
errorCode: !version.ok ? version.error : !tags.ok ? tags.error : 'invalid_ollama_api_shape',
|
|
71
|
+
}),
|
|
72
|
+
models: [],
|
|
73
|
+
warnings: [{
|
|
74
|
+
code: 'ollama_api_unverified',
|
|
75
|
+
message: 'The listener owner matched Ollama, but its version or inventory response was not valid.',
|
|
76
|
+
scope: 'runtime',
|
|
77
|
+
}],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const validatedRunning = running.ok ? validModelArray(running.body) : null;
|
|
81
|
+
const runningEntries = validatedRunning ?? [];
|
|
82
|
+
const runningNames = new Set(runningEntries.flatMap((value) => {
|
|
83
|
+
const entry = (0, base_1.readRecord)(value);
|
|
84
|
+
const name = (0, base_1.readString)(entry?.name) ?? (0, base_1.readString)(entry?.model);
|
|
85
|
+
return name ? [name.toLowerCase()] : [];
|
|
86
|
+
}));
|
|
87
|
+
const entries = tagEntries;
|
|
88
|
+
const models = await Promise.all(entries.map(async (entryValue) => {
|
|
89
|
+
const entry = (0, base_1.readRecord)(entryValue);
|
|
90
|
+
const name = (0, base_1.readString)(entry?.name) ?? (0, base_1.readString)(entry?.model);
|
|
91
|
+
if (!name)
|
|
92
|
+
return null;
|
|
93
|
+
const manifestDigest = parseSha256((0, base_1.readString)(entry?.digest));
|
|
94
|
+
const cacheKey = manifestDigest ?? name.toLowerCase();
|
|
95
|
+
let detailsValue = this.detailsCache.get(cacheKey);
|
|
96
|
+
if (detailsValue === undefined) {
|
|
97
|
+
const detailsResult = await (0, base_1.fetchJson)(`${this.endpoint.url}/api/show`, {
|
|
98
|
+
method: 'POST',
|
|
99
|
+
headers: { 'Content-Type': 'application/json' },
|
|
100
|
+
body: JSON.stringify({ model: name, verbose: false }),
|
|
101
|
+
}, this.fetchImpl, 4000);
|
|
102
|
+
if (detailsResult.ok) {
|
|
103
|
+
detailsValue = detailsResult.body;
|
|
104
|
+
this.detailsCache.set(cacheKey, detailsValue);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const detailsBody = (0, base_1.readRecord)(detailsValue);
|
|
108
|
+
const layers = (0, base_1.readArray)(detailsBody?.layers);
|
|
109
|
+
const modelLayer = layers
|
|
110
|
+
.map((layer) => (0, base_1.readRecord)(layer))
|
|
111
|
+
.find((layer) => (0, base_1.readString)(layer?.mediaType) === 'application/vnd.ollama.image.model');
|
|
112
|
+
const weightsDigest = parseSha256((0, base_1.readString)(modelLayer?.digest));
|
|
113
|
+
const details = (0, base_1.readRecord)(detailsBody?.details);
|
|
114
|
+
const contentIdentity = weightsDigest ?? manifestDigest;
|
|
115
|
+
const identity = contentIdentity ?? (0, hash_1.sha256)(`ollama:${name}`);
|
|
116
|
+
const model = (0, base_1.createModel)({
|
|
117
|
+
name,
|
|
118
|
+
runtime: this.name,
|
|
119
|
+
identityKind: contentIdentity ? 'content-sha256' : 'provider-id',
|
|
120
|
+
identity,
|
|
121
|
+
source: 'runtime-api',
|
|
122
|
+
confidence: contentIdentity ? 'high' : 'medium',
|
|
123
|
+
sizeBytes: (0, base_1.readNumber)(entry?.size),
|
|
124
|
+
modifiedAt: (0, base_1.readString)(entry?.modified_at),
|
|
125
|
+
quantization: (0, base_1.readString)(details?.quantization_level),
|
|
126
|
+
architecture: (0, base_1.readString)(details?.family),
|
|
127
|
+
parameterSize: (0, base_1.readString)(details?.parameter_size),
|
|
128
|
+
});
|
|
129
|
+
return {
|
|
130
|
+
...model,
|
|
131
|
+
evidence_status: runningNames.has(name.toLowerCase()) ? 'confirmed-running' : 'discovered',
|
|
132
|
+
};
|
|
133
|
+
}));
|
|
134
|
+
const detectedModels = models.filter((model) => model !== null);
|
|
135
|
+
const detectedNames = new Set(detectedModels.map((model) => model.name.toLowerCase()));
|
|
136
|
+
for (const value of runningEntries) {
|
|
137
|
+
const entry = (0, base_1.readRecord)(value);
|
|
138
|
+
const name = (0, base_1.readString)(entry?.name) ?? (0, base_1.readString)(entry?.model);
|
|
139
|
+
if (!name || detectedNames.has(name.toLowerCase()))
|
|
140
|
+
continue;
|
|
141
|
+
const digest = parseSha256((0, base_1.readString)(entry?.digest));
|
|
142
|
+
const model = (0, base_1.createModel)({
|
|
143
|
+
name,
|
|
144
|
+
runtime: this.name,
|
|
145
|
+
identityKind: digest ? 'content-sha256' : 'provider-id',
|
|
146
|
+
identity: digest ?? (0, hash_1.sha256)(`ollama:${name}`),
|
|
147
|
+
source: 'runtime-api',
|
|
148
|
+
confidence: digest ? 'high' : 'medium',
|
|
149
|
+
sizeBytes: (0, base_1.readNumber)(entry?.size),
|
|
150
|
+
});
|
|
151
|
+
detectedModels.push({ ...model, evidence_status: 'confirmed-running' });
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
runtime: (0, base_1.createRuntime)({
|
|
155
|
+
name: this.name,
|
|
156
|
+
endpoint: this.endpoint.display,
|
|
157
|
+
status: 'available',
|
|
158
|
+
modelCount: detectedModels.length,
|
|
159
|
+
observedAt,
|
|
160
|
+
}),
|
|
161
|
+
models: detectedModels,
|
|
162
|
+
warnings: validatedRunning !== null ? [] : [{
|
|
163
|
+
code: 'ollama_running_state_unavailable',
|
|
164
|
+
message: 'Ollama inventory was available, but confirmed loaded-model state could not be read.',
|
|
165
|
+
scope: 'runtime',
|
|
166
|
+
}],
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
exports.OllamaProvider = OllamaProvider;
|
|
171
|
+
//# sourceMappingURL=ollama.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RuntimeProvider, RuntimeScanResult } from '../types';
|
|
2
|
+
export declare class OpenAICompatibleProvider implements RuntimeProvider {
|
|
3
|
+
private readonly fetchImpl;
|
|
4
|
+
readonly name: "openai-compatible";
|
|
5
|
+
private readonly endpoint;
|
|
6
|
+
constructor(endpoint?: string, fetchImpl?: typeof fetch);
|
|
7
|
+
scan(observedAt: string): Promise<RuntimeScanResult>;
|
|
8
|
+
}
|
|
9
|
+
export declare function configuredOpenAICompatibleProviders(raw?: string, fetchImpl?: typeof fetch): OpenAICompatibleProvider[];
|
|
10
|
+
//# sourceMappingURL=openai-compatible.d.ts.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpenAICompatibleProvider = void 0;
|
|
4
|
+
exports.configuredOpenAICompatibleProviders = configuredOpenAICompatibleProviders;
|
|
5
|
+
const endpoints_1 = require("../endpoints");
|
|
6
|
+
const hash_1 = require("../hash");
|
|
7
|
+
const base_1 = require("./base");
|
|
8
|
+
class OpenAICompatibleProvider {
|
|
9
|
+
fetchImpl;
|
|
10
|
+
name = 'openai-compatible';
|
|
11
|
+
endpoint;
|
|
12
|
+
constructor(endpoint = 'http://localhost:8000', fetchImpl = fetch) {
|
|
13
|
+
this.fetchImpl = fetchImpl;
|
|
14
|
+
const safeEndpoint = (0, endpoints_1.parseLoopbackEndpoint)(endpoint);
|
|
15
|
+
if (!safeEndpoint)
|
|
16
|
+
throw new Error('OpenAI-compatible endpoint must be loopback HTTP(S).');
|
|
17
|
+
this.endpoint = safeEndpoint;
|
|
18
|
+
}
|
|
19
|
+
async scan(observedAt) {
|
|
20
|
+
const result = await (0, base_1.fetchJson)(`${this.endpoint.url}/v1/models`, { method: 'GET' }, this.fetchImpl);
|
|
21
|
+
if (!result.ok) {
|
|
22
|
+
return {
|
|
23
|
+
runtime: (0, base_1.createRuntime)({
|
|
24
|
+
name: this.name,
|
|
25
|
+
endpoint: this.endpoint.display,
|
|
26
|
+
status: 'unavailable',
|
|
27
|
+
modelCount: 0,
|
|
28
|
+
observedAt,
|
|
29
|
+
errorCode: result.error,
|
|
30
|
+
}),
|
|
31
|
+
models: [],
|
|
32
|
+
warnings: [],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const body = (0, base_1.readRecord)(result.body);
|
|
36
|
+
const models = (0, base_1.readArray)(body?.data).flatMap((value) => {
|
|
37
|
+
const entry = (0, base_1.readRecord)(value);
|
|
38
|
+
const name = (0, base_1.readString)(entry?.id);
|
|
39
|
+
if (!name)
|
|
40
|
+
return [];
|
|
41
|
+
return [(0, base_1.createModel)({
|
|
42
|
+
name,
|
|
43
|
+
runtime: this.name,
|
|
44
|
+
identityKind: 'provider-id',
|
|
45
|
+
identity: (0, hash_1.sha256)(`openai-compatible:${this.endpoint.id}:${name}`),
|
|
46
|
+
source: 'runtime-api',
|
|
47
|
+
confidence: 'medium',
|
|
48
|
+
})];
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
runtime: (0, base_1.createRuntime)({
|
|
52
|
+
name: this.name,
|
|
53
|
+
endpoint: this.endpoint.display,
|
|
54
|
+
status: 'available',
|
|
55
|
+
modelCount: models.length,
|
|
56
|
+
observedAt,
|
|
57
|
+
}),
|
|
58
|
+
models,
|
|
59
|
+
warnings: [],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.OpenAICompatibleProvider = OpenAICompatibleProvider;
|
|
64
|
+
function configuredOpenAICompatibleProviders(raw = process.env.FORKIT_CENSUS_OPENAI_ENDPOINTS ?? 'http://localhost:8000', fetchImpl = fetch) {
|
|
65
|
+
return (0, endpoints_1.parseLoopbackEndpointList)(raw)
|
|
66
|
+
.map((endpoint) => new OpenAICompatibleProvider(endpoint.url, fetchImpl));
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=openai-compatible.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type ResourceEvidenceState = 'measured' | 'hardware-fact' | 'not-available';
|
|
2
|
+
export type AttributionStrength = 'detected-process-window' | 'device' | 'none';
|
|
3
|
+
export interface ResourceEvidenceCapability {
|
|
4
|
+
metric: 'cpu' | 'memory' | 'gpu' | 'hardware' | 'network_traffic';
|
|
5
|
+
state: ResourceEvidenceState;
|
|
6
|
+
attribution: AttributionStrength;
|
|
7
|
+
source: string;
|
|
8
|
+
exclusive_task_proof: false;
|
|
9
|
+
limitation: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ResourceEvidenceManifest {
|
|
12
|
+
schema_version: '1.0';
|
|
13
|
+
platform: 'macos';
|
|
14
|
+
capabilities: ResourceEvidenceCapability[];
|
|
15
|
+
}
|
|
16
|
+
export declare function macosResourceEvidenceManifest(): ResourceEvidenceManifest;
|
|
17
|
+
//# sourceMappingURL=resource-evidence.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.macosResourceEvidenceManifest = macosResourceEvidenceManifest;
|
|
4
|
+
function macosResourceEvidenceManifest() {
|
|
5
|
+
return {
|
|
6
|
+
schema_version: '1.0',
|
|
7
|
+
platform: 'macos',
|
|
8
|
+
capabilities: [
|
|
9
|
+
{
|
|
10
|
+
metric: 'cpu',
|
|
11
|
+
state: 'measured',
|
|
12
|
+
attribution: 'detected-process-window',
|
|
13
|
+
source: 'operating-system-process-sample',
|
|
14
|
+
exclusive_task_proof: false,
|
|
15
|
+
limitation: 'Detected process work is measured; concurrent background work inside the same process can be included.',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
metric: 'memory',
|
|
19
|
+
state: 'measured',
|
|
20
|
+
attribution: 'detected-process-window',
|
|
21
|
+
source: 'operating-system-process-sample',
|
|
22
|
+
exclusive_task_proof: false,
|
|
23
|
+
limitation: 'Memory is shared and may persist across tasks; the window cannot prove which task owns every byte.',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
metric: 'gpu',
|
|
27
|
+
state: 'not-available',
|
|
28
|
+
attribution: 'none',
|
|
29
|
+
source: 'no-supported-cross-process-proof-source',
|
|
30
|
+
exclusive_task_proof: false,
|
|
31
|
+
limitation: 'Metal counters can measure work submitted by an instrumented app, not arbitrary third-party AI process work as exclusive task proof.',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
metric: 'hardware',
|
|
35
|
+
state: 'hardware-fact',
|
|
36
|
+
attribution: 'device',
|
|
37
|
+
source: 'operating-system-hardware-metadata',
|
|
38
|
+
exclusive_task_proof: false,
|
|
39
|
+
limitation: 'Hardware inventory identifies device capability; it does not prove task-specific utilization.',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
metric: 'network_traffic',
|
|
43
|
+
state: 'not-available',
|
|
44
|
+
attribution: 'none',
|
|
45
|
+
source: 'no-supported-per-process-proof-source-integrated',
|
|
46
|
+
exclusive_task_proof: false,
|
|
47
|
+
limitation: 'The current release does not use privileged packet inspection or unsupported per-process network APIs.',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=resource-evidence.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ClassifiedAgentProcess } from './agents';
|
|
2
|
+
import type { ProcessEntry, RuntimeProvider } from './types';
|
|
3
|
+
export declare function loadedRuntimeSignatures(providers: RuntimeProvider[], observedAt: string): Promise<Set<string>>;
|
|
4
|
+
export declare function classifyLoadedRuntimeProcesses(processes: ProcessEntry[], loaded: Set<string>, excludedRootPid?: number | null): ClassifiedAgentProcess[];
|
|
5
|
+
//# sourceMappingURL=runtime-activity.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
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.loadedRuntimeSignatures = loadedRuntimeSignatures;
|
|
7
|
+
exports.classifyLoadedRuntimeProcesses = classifyLoadedRuntimeProcesses;
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
function basename(value) {
|
|
10
|
+
return node_path_1.default.basename(String(value || '').replaceAll('\\', '/')).toLowerCase().replace(/\.exe$/i, '');
|
|
11
|
+
}
|
|
12
|
+
async function loadedRuntimeSignatures(providers, observedAt) {
|
|
13
|
+
const loaded = new Set();
|
|
14
|
+
const results = await Promise.all(providers.map(async (provider) => {
|
|
15
|
+
try {
|
|
16
|
+
return await provider.scan(observedAt);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}));
|
|
22
|
+
for (const result of results) {
|
|
23
|
+
if (!result)
|
|
24
|
+
continue;
|
|
25
|
+
if (result.models.some((model) => model.evidence_status === 'confirmed-running'))
|
|
26
|
+
loaded.add(result.runtime.name);
|
|
27
|
+
}
|
|
28
|
+
return loaded;
|
|
29
|
+
}
|
|
30
|
+
function classifyLoadedRuntimeProcesses(processes, loaded, excludedRootPid = process.pid) {
|
|
31
|
+
const definitions = [
|
|
32
|
+
{ signature: 'runtime:ollama', runtime: 'ollama', name: 'Ollama', executables: ['ollama'] },
|
|
33
|
+
{ signature: 'runtime:lmstudio', runtime: 'lmstudio', name: 'LM Studio', executables: ['lm studio', 'lmstudio'] },
|
|
34
|
+
];
|
|
35
|
+
const excluded = new Set();
|
|
36
|
+
if (excludedRootPid !== null) {
|
|
37
|
+
excluded.add(excludedRootPid);
|
|
38
|
+
let changed = true;
|
|
39
|
+
while (changed) {
|
|
40
|
+
changed = false;
|
|
41
|
+
for (const entry of processes)
|
|
42
|
+
if (!excluded.has(entry.pid) && entry.ppid !== undefined && excluded.has(entry.ppid)) {
|
|
43
|
+
excluded.add(entry.pid);
|
|
44
|
+
changed = true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const result = [];
|
|
49
|
+
for (const entry of processes) {
|
|
50
|
+
if (excluded.has(entry.pid))
|
|
51
|
+
continue;
|
|
52
|
+
const executable = basename(entry.name || String(entry.cmd || '').split(/\s+/)[0] || '');
|
|
53
|
+
const definition = definitions.find((candidate) => loaded.has(candidate.runtime) && candidate.executables.includes(executable));
|
|
54
|
+
if (!definition)
|
|
55
|
+
continue;
|
|
56
|
+
result.push({
|
|
57
|
+
pid: entry.pid, ppid: entry.ppid ?? null, signature: definition.signature, name: definition.name,
|
|
58
|
+
kind: 'local-model-runtime', confidence: 'high', relationship: 'direct',
|
|
59
|
+
cpu_percent: Number.isFinite(entry.cpu_percent) ? Number(entry.cpu_percent) : null,
|
|
60
|
+
memory_percent: Number.isFinite(entry.memory_percent) ? Number(entry.memory_percent) : null,
|
|
61
|
+
cpu_time_ms: Number.isFinite(entry.cpu_time_ms) ? Number(entry.cpu_time_ms) : null,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=runtime-activity.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type RuntimeIdentityVerifier = (endpoint: string, expectedExecutable: string) => Promise<boolean>;
|
|
2
|
+
/**
|
|
3
|
+
* Correlates a loopback listener with an exact executable name. Only the
|
|
4
|
+
* boolean result leaves this adapter; PID and executable path stay ephemeral.
|
|
5
|
+
*/
|
|
6
|
+
export declare const verifyMacosLoopbackRuntimeIdentity: RuntimeIdentityVerifier;
|
|
7
|
+
//# sourceMappingURL=runtime-identity.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
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.verifyMacosLoopbackRuntimeIdentity = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const node_child_process_1 = require("node:child_process");
|
|
9
|
+
const node_util_1 = require("node:util");
|
|
10
|
+
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
11
|
+
function exactBasename(value) {
|
|
12
|
+
return node_path_1.default.basename(value.trim()).toLowerCase();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Correlates a loopback listener with an exact executable name. Only the
|
|
16
|
+
* boolean result leaves this adapter; PID and executable path stay ephemeral.
|
|
17
|
+
*/
|
|
18
|
+
const verifyMacosLoopbackRuntimeIdentity = async (endpoint, expectedExecutable) => {
|
|
19
|
+
if (process.platform !== 'darwin')
|
|
20
|
+
return false;
|
|
21
|
+
let parsed;
|
|
22
|
+
try {
|
|
23
|
+
parsed = new URL(endpoint);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const port = Number(parsed.port || (parsed.protocol === 'https:' ? 443 : 80));
|
|
29
|
+
if (!Number.isSafeInteger(port) || port < 1 || port > 65535)
|
|
30
|
+
return false;
|
|
31
|
+
try {
|
|
32
|
+
const { stdout } = await execFileAsync('lsof', [
|
|
33
|
+
'-nP', `-iTCP:${port}`, '-sTCP:LISTEN', '-Fpc',
|
|
34
|
+
], {
|
|
35
|
+
encoding: 'utf8',
|
|
36
|
+
maxBuffer: 1_000_000,
|
|
37
|
+
env: { ...process.env, LC_ALL: 'C', LANG: 'C' },
|
|
38
|
+
});
|
|
39
|
+
let currentPid = null;
|
|
40
|
+
const candidates = [];
|
|
41
|
+
for (const line of stdout.split('\n')) {
|
|
42
|
+
if (line.startsWith('p'))
|
|
43
|
+
currentPid = Number(line.slice(1));
|
|
44
|
+
else if (line.startsWith('c') && currentPid !== null
|
|
45
|
+
&& exactBasename(line.slice(1)) === expectedExecutable.toLowerCase())
|
|
46
|
+
candidates.push(currentPid);
|
|
47
|
+
}
|
|
48
|
+
for (const pid of candidates) {
|
|
49
|
+
if (!Number.isSafeInteger(pid) || pid < 1)
|
|
50
|
+
continue;
|
|
51
|
+
const { stdout: command } = await execFileAsync('ps', ['-p', String(pid), '-o', 'comm='], {
|
|
52
|
+
encoding: 'utf8',
|
|
53
|
+
maxBuffer: 64_000,
|
|
54
|
+
env: { ...process.env, LC_ALL: 'C', LANG: 'C' },
|
|
55
|
+
});
|
|
56
|
+
if (exactBasename(command) === expectedExecutable.toLowerCase())
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
exports.verifyMacosLoopbackRuntimeIdentity = verifyMacosLoopbackRuntimeIdentity;
|
|
66
|
+
//# sourceMappingURL=runtime-identity.js.map
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import { type LocalDeviceJournal } from './local-device';
|
|
3
|
+
import { ActivityMonitor } from './monitor';
|
|
4
|
+
import type { CensusReport } from './types';
|
|
5
|
+
export interface AiFootprintsServerOptions {
|
|
6
|
+
hostname?: '127.0.0.1';
|
|
7
|
+
port?: number;
|
|
8
|
+
scan?: () => Promise<CensusReport>;
|
|
9
|
+
recordScan?: (generatedAt: string) => Promise<LocalDeviceJournal>;
|
|
10
|
+
monitor?: ActivityMonitor;
|
|
11
|
+
nativeToken?: string | null;
|
|
12
|
+
}
|
|
13
|
+
export interface AiFootprintsServer {
|
|
14
|
+
server: http.Server;
|
|
15
|
+
url: string;
|
|
16
|
+
sessionToken: string;
|
|
17
|
+
monitor: ActivityMonitor;
|
|
18
|
+
close: () => Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export declare function startAiFootprintsServer(options?: AiFootprintsServerOptions): Promise<AiFootprintsServer>;
|
|
21
|
+
//# sourceMappingURL=server.d.ts.map
|