forkit-ai-footprints 0.2.0 → 0.2.4
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/PRIVACY.md +57 -27
- package/README.md +43 -41
- package/STATUS.md +92 -37
- package/dist/activity-page.d.ts +13 -0
- package/dist/activity-page.js +105 -0
- package/dist/activity-view-model.d.ts +154 -0
- package/dist/activity-view-model.js +364 -0
- package/dist/agents.d.ts +2 -0
- package/dist/agents.js +108 -6
- package/dist/bootstrap/.metadata_never_index +0 -0
- package/dist/bootstrap/Forkit AI Footprint.app.zip +0 -0
- package/dist/cli.js +91 -14
- package/dist/device-presence.d.ts +17 -0
- package/dist/device-presence.js +64 -0
- package/dist/global-preview.d.ts +34 -0
- package/dist/global-preview.js +209 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -1
- package/dist/insights.d.ts +1 -1
- package/dist/insights.js +10 -8
- package/dist/local-observation-state.d.ts +43 -0
- package/dist/local-observation-state.js +124 -0
- package/dist/locales.json +564 -108
- package/dist/localization.d.ts +564 -108
- package/dist/localization.js +40 -30
- package/dist/monitor.d.ts +38 -2
- package/dist/monitor.js +257 -19
- package/dist/runtime-activity.d.ts +4 -1
- package/dist/runtime-activity.js +21 -5
- package/dist/server.d.ts +5 -0
- package/dist/server.js +160 -17
- package/dist/share-page.d.ts +2 -1
- package/dist/share-page.js +16 -52
- package/dist/sharing.d.ts +12 -1
- package/dist/sharing.js +59 -1
- package/dist/types.d.ts +1 -1
- package/dist/workflow-context.d.ts +13 -0
- package/dist/workflow-context.js +144 -0
- package/docs/GLOBAL_DISTRIBUTION.md +7 -3
- package/docs/MACOS_ACCURACY_GATE.md +3 -3
- package/docs/MACOS_TESTER_GUIDE.md +11 -10
- package/package.json +6 -6
- package/release/channels.json +2 -2
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Info.plist +0 -5
- 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 +0 -22
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/agents.js +0 -281
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/aggregate.d.ts +0 -28
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/aggregate.js +0 -151
- 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 +0 -3
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/census.js +0 -196
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/cli.d.ts +0 -32
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/cli.js +0 -404
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/doctor.d.ts +0 -14
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/doctor.js +0 -71
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/endpoints.d.ts +0 -8
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/endpoints.js +0 -45
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/evaluate.d.ts +0 -44
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/evaluate.js +0 -102
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/filesystem.d.ts +0 -11
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/filesystem.js +0 -226
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/format.d.ts +0 -7
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/format.js +0 -78
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/hash.d.ts +0 -4
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/hash.js +0 -16
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/index.d.ts +0 -22
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/index.js +0 -58
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/insights.d.ts +0 -13
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/insights.js +0 -76
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/local-device.d.ts +0 -19
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/local-device.js +0 -81
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/locales.json +0 -272
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/localization.d.ts +0 -277
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/localization.js +0 -55
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/mcp.d.ts +0 -9
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/mcp.js +0 -93
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/monitor.d.ts +0 -99
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/monitor.js +0 -285
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/base.d.ts +0 -34
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/base.js +0 -88
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/index.d.ts +0 -6
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/index.js +0 -24
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/lmstudio.d.ts +0 -9
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/lmstudio.js +0 -71
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/ollama.d.ts +0 -12
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/ollama.js +0 -171
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/openai-compatible.d.ts +0 -10
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/openai-compatible.js +0 -68
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/resource-evidence.d.ts +0 -17
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/resource-evidence.js +0 -52
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-activity.d.ts +0 -5
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-activity.js +0 -66
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-identity.d.ts +0 -7
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-identity.js +0 -66
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/server.d.ts +0 -21
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/server.js +0 -308
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/share-page.d.ts +0 -75
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/share-page.js +0 -167
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/sharing.d.ts +0 -35
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/sharing.js +0 -67
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/tools.d.ts +0 -9
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/tools.js +0 -106
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/types.d.ts +0 -166
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/types.js +0 -3
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/version.d.ts +0 -4
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/version.js +0 -13
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/index.d.ts +0 -76
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/index.js +0 -366
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/license +0 -9
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/package.json +0 -44
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/readme.md +0 -56
- 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 +0 -6
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/runtime/node +0 -0
- package/dist/bootstrap/Forkit AI Footprint.app/Contents/_CodeSignature/CodeResources +0 -931
package/dist/cli.js
CHANGED
|
@@ -40,8 +40,8 @@ Options:
|
|
|
40
40
|
--verbose Show detector and identity details
|
|
41
41
|
--guess <count> Compare your estimate with discovered models
|
|
42
42
|
--copy Copy the rendered local result to the clipboard
|
|
43
|
-
--anonymous-payload Reserved for a completed
|
|
44
|
-
--consent-share
|
|
43
|
+
--anonymous-payload Reserved for a completed one-hour monitor session
|
|
44
|
+
--consent-share Prepare an aggregate payload; native-app consent controls Preview sync
|
|
45
45
|
--output <file> Save the selected human or JSON report
|
|
46
46
|
--model-dir <path> Inspect an explicit model directory; repeatable
|
|
47
47
|
--truth <file> Evaluate locally against manually labelled macOS truth
|
|
@@ -55,9 +55,10 @@ Options:
|
|
|
55
55
|
-h, --help Show this help
|
|
56
56
|
|
|
57
57
|
Privacy:
|
|
58
|
-
AI Footprints reads metadata only. It does not read model bytes
|
|
59
|
-
commands or MCP configuration values
|
|
60
|
-
|
|
58
|
+
AI Footprints reads metadata only. It does not read model bytes or retain raw
|
|
59
|
+
process commands or MCP configuration values. It does not write to any passport,
|
|
60
|
+
registry, or Runtime_C2 service. The installed app may sync only the allowlisted anonymous
|
|
61
|
+
Global AI Preview aggregate after explicit v2 consent and one valid hour.
|
|
61
62
|
`;
|
|
62
63
|
function parseArgs(args) {
|
|
63
64
|
let command = 'install';
|
|
@@ -186,12 +187,65 @@ function parseArgs(args) {
|
|
|
186
187
|
port,
|
|
187
188
|
};
|
|
188
189
|
}
|
|
190
|
+
const FORKIT_APP_NAME = 'Forkit AI Footprint.app';
|
|
191
|
+
const FORKIT_BUNDLE_IDENTIFIER = 'dev.forkit.ai-footprints';
|
|
192
|
+
const LAUNCH_SERVICES = '/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister';
|
|
193
|
+
async function isForkitApp(appPath) {
|
|
194
|
+
const info = await promises_1.default.readFile(node_path_1.default.join(appPath, 'Contents', 'Info.plist'), 'utf8').catch(() => '');
|
|
195
|
+
return info.includes(FORKIT_BUNDLE_IDENTIFIER);
|
|
196
|
+
}
|
|
197
|
+
async function recognizedForkitApps(applicationsDirectory) {
|
|
198
|
+
const entries = await promises_1.default.readdir(applicationsDirectory, { withFileTypes: true }).catch(() => []);
|
|
199
|
+
const apps = [];
|
|
200
|
+
for (const entry of entries) {
|
|
201
|
+
if (!entry.isDirectory() || !entry.name.endsWith('.app') || entry.name.startsWith('.'))
|
|
202
|
+
continue;
|
|
203
|
+
const candidate = node_path_1.default.join(applicationsDirectory, entry.name);
|
|
204
|
+
if (await isForkitApp(candidate))
|
|
205
|
+
apps.push(candidate);
|
|
206
|
+
}
|
|
207
|
+
return apps;
|
|
208
|
+
}
|
|
209
|
+
function stopRunningForkitApps(appPaths) {
|
|
210
|
+
const snapshot = (0, node_child_process_1.spawnSync)('/bin/ps', ['-axo', 'pid=,ppid=,comm='], { encoding: 'utf8', shell: false });
|
|
211
|
+
if (snapshot.status !== 0 || !snapshot.stdout)
|
|
212
|
+
return;
|
|
213
|
+
const processes = snapshot.stdout.split('\n').map((line) => {
|
|
214
|
+
const match = line.trim().match(/^(\d+)\s+(\d+)\s+(.+)$/);
|
|
215
|
+
return match ? { pid: Number(match[1]), parent: Number(match[2]), command: match[3] } : null;
|
|
216
|
+
}).filter((entry) => Boolean(entry));
|
|
217
|
+
const matching = processes.filter((entry) => appPaths.some((appPath) => entry.command.startsWith(`${appPath}/Contents/`)));
|
|
218
|
+
if (matching.length === 0)
|
|
219
|
+
return;
|
|
220
|
+
const matchingPids = new Set(matching.map((entry) => entry.pid));
|
|
221
|
+
const ordered = [...matching].sort((left, right) => Number(matchingPids.has(right.parent)) - Number(matchingPids.has(left.parent)));
|
|
222
|
+
for (const entry of ordered) {
|
|
223
|
+
try {
|
|
224
|
+
process.kill(entry.pid, 'SIGTERM');
|
|
225
|
+
}
|
|
226
|
+
catch { /* process already ended */ }
|
|
227
|
+
}
|
|
228
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 800);
|
|
229
|
+
for (const entry of ordered) {
|
|
230
|
+
try {
|
|
231
|
+
process.kill(entry.pid, 0);
|
|
232
|
+
process.kill(entry.pid, 'SIGKILL');
|
|
233
|
+
}
|
|
234
|
+
catch { /* process ended cleanly */ }
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function unregisterApp(appPath) {
|
|
238
|
+
(0, node_child_process_1.spawnSync)(LAUNCH_SERVICES, ['-u', appPath], { stdio: 'ignore', shell: false });
|
|
239
|
+
}
|
|
240
|
+
function registerApp(appPath) {
|
|
241
|
+
(0, node_child_process_1.spawnSync)(LAUNCH_SERVICES, ['-f', appPath], { stdio: 'ignore', shell: false });
|
|
242
|
+
}
|
|
189
243
|
async function installPersistentMacApp(options = {}) {
|
|
190
244
|
if (process.platform !== 'darwin' || process.arch !== 'arm64')
|
|
191
245
|
throw new Error('The persistent GUI currently supports Apple Silicon macOS only.');
|
|
192
|
-
const source = node_path_1.default.join(__dirname, 'bootstrap',
|
|
246
|
+
const source = node_path_1.default.join(__dirname, 'bootstrap', `${FORKIT_APP_NAME}.zip`);
|
|
193
247
|
try {
|
|
194
|
-
await promises_1.default.access(
|
|
248
|
+
await promises_1.default.access(source);
|
|
195
249
|
}
|
|
196
250
|
catch {
|
|
197
251
|
throw new Error('This package does not contain the macOS GUI bootstrap. Install from the packed release candidate.');
|
|
@@ -199,32 +253,55 @@ async function installPersistentMacApp(options = {}) {
|
|
|
199
253
|
const applicationsDirectory = options.applicationsDirectory
|
|
200
254
|
?? process.env.FORKIT_AI_FOOTPRINTS_APPLICATIONS_DIR
|
|
201
255
|
?? node_path_1.default.join(node_os_1.default.homedir(), 'Applications');
|
|
202
|
-
const destination = node_path_1.default.join(applicationsDirectory,
|
|
256
|
+
const destination = node_path_1.default.join(applicationsDirectory, FORKIT_APP_NAME);
|
|
203
257
|
const staging = node_path_1.default.join(applicationsDirectory, `.Forkit AI Footprint.installing-${process.pid}.app`);
|
|
204
258
|
const previous = node_path_1.default.join(applicationsDirectory, `.Forkit AI Footprint.previous-${process.pid}.app`);
|
|
259
|
+
const extraction = node_path_1.default.join(applicationsDirectory, `.Forkit AI Footprint.extracting-${process.pid}`);
|
|
260
|
+
const shouldOpen = options.open !== false && process.env.FORKIT_AI_FOOTPRINTS_NO_OPEN !== '1';
|
|
205
261
|
await promises_1.default.mkdir(applicationsDirectory, { recursive: true, mode: 0o700 });
|
|
262
|
+
const recognizedApps = await recognizedForkitApps(applicationsDirectory);
|
|
263
|
+
stopRunningForkitApps(recognizedApps);
|
|
206
264
|
await promises_1.default.rm(staging, { recursive: true, force: true });
|
|
207
|
-
await promises_1.default.
|
|
265
|
+
await promises_1.default.rm(extraction, { recursive: true, force: true });
|
|
266
|
+
await promises_1.default.mkdir(extraction, { recursive: true, mode: 0o700 });
|
|
267
|
+
const unpacked = (0, node_child_process_1.spawnSync)('/usr/bin/ditto', ['-x', '-k', source, extraction], { stdio: 'ignore', shell: false });
|
|
268
|
+
const extractedApp = node_path_1.default.join(extraction, FORKIT_APP_NAME);
|
|
269
|
+
if (unpacked.status !== 0 || !(await isForkitApp(extractedApp))) {
|
|
270
|
+
await promises_1.default.rm(extraction, { recursive: true, force: true });
|
|
271
|
+
throw new Error('The packaged macOS app has an unexpected identity.');
|
|
272
|
+
}
|
|
273
|
+
await promises_1.default.rename(extractedApp, staging);
|
|
274
|
+
await promises_1.default.rm(extraction, { recursive: true, force: true });
|
|
208
275
|
let hadPrevious = false;
|
|
209
276
|
try {
|
|
210
277
|
const existingInfo = await promises_1.default.readFile(node_path_1.default.join(destination, 'Contents', 'Info.plist'), 'utf8').catch(() => '');
|
|
211
|
-
if (existingInfo && !existingInfo.includes(
|
|
278
|
+
if (existingInfo && !existingInfo.includes(FORKIT_BUNDLE_IDENTIFIER))
|
|
212
279
|
throw new Error('The destination contains a different application.');
|
|
213
280
|
if (existingInfo) {
|
|
281
|
+
unregisterApp(destination);
|
|
214
282
|
await promises_1.default.rename(destination, previous);
|
|
215
283
|
hadPrevious = true;
|
|
216
284
|
}
|
|
217
285
|
await promises_1.default.rename(staging, destination);
|
|
218
286
|
if (hadPrevious)
|
|
219
287
|
await promises_1.default.rm(previous, { recursive: true, force: true });
|
|
288
|
+
for (const duplicate of recognizedApps) {
|
|
289
|
+
if (duplicate === destination)
|
|
290
|
+
continue;
|
|
291
|
+
unregisterApp(duplicate);
|
|
292
|
+
await promises_1.default.rm(duplicate, { recursive: true, force: true });
|
|
293
|
+
}
|
|
294
|
+
if (shouldOpen)
|
|
295
|
+
registerApp(destination);
|
|
220
296
|
}
|
|
221
297
|
catch (error) {
|
|
222
298
|
await promises_1.default.rm(staging, { recursive: true, force: true });
|
|
299
|
+
await promises_1.default.rm(extraction, { recursive: true, force: true });
|
|
223
300
|
if (hadPrevious)
|
|
224
301
|
await promises_1.default.rename(previous, destination).catch(() => undefined);
|
|
225
302
|
throw error;
|
|
226
303
|
}
|
|
227
|
-
if (
|
|
304
|
+
if (shouldOpen) {
|
|
228
305
|
const launched = (0, node_child_process_1.spawnSync)('/usr/bin/open', [destination], { stdio: 'ignore', shell: false });
|
|
229
306
|
if (launched.status !== 0)
|
|
230
307
|
throw new Error('Forkit was installed, but macOS could not open it.');
|
|
@@ -278,7 +355,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
278
355
|
}
|
|
279
356
|
if (options.command === 'install') {
|
|
280
357
|
const destination = await installPersistentMacApp();
|
|
281
|
-
process.stdout.write(`Forkit AI Footprint is installed at ${destination}\nOpen it later from Applications or Spotlight; Terminal is no longer required.\
|
|
358
|
+
process.stdout.write(`Forkit AI Footprint is installed at ${destination}\nOpen it later from Applications or Spotlight; Terminal is no longer required.\nOn first launch, Forkit asks whether an aggregate-only measurement may be contributed to the Global AI Preview after one valid observed hour. Nothing is sent unless the person explicitly agrees.\n`);
|
|
282
359
|
return 0;
|
|
283
360
|
}
|
|
284
361
|
if (options.command === 'serve') {
|
|
@@ -310,7 +387,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
310
387
|
: `Observed ${summary.observed_seconds.toFixed(1)}s · AI active ${summary.active_seconds.toFixed(1)}s · ratio ${summary.activity_ratio === null ? 'unavailable' : `${Math.round(summary.activity_ratio * 100)}%`}\n`;
|
|
311
388
|
await emit(rendered, options.output);
|
|
312
389
|
if (options.anonymousPayload) {
|
|
313
|
-
process.stderr.write('Anonymous preview requires a completed
|
|
390
|
+
process.stderr.write('Anonymous preview requires a completed one-hour GUI session with the exact payload reviewed first. Nothing was uploaded.\n');
|
|
314
391
|
return 2;
|
|
315
392
|
}
|
|
316
393
|
return 0;
|
|
@@ -387,7 +464,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
387
464
|
}
|
|
388
465
|
if (options.anonymousPayload) {
|
|
389
466
|
process.stderr.write(options.shareConsent
|
|
390
|
-
? 'Anonymous contribution was not prepared: a valid
|
|
467
|
+
? 'Anonymous contribution was not prepared: a valid one-hour monitor session and exact payload review are required. Nothing was uploaded.\n'
|
|
391
468
|
: 'Anonymous contribution was not prepared: separate --consent-share is required. Nothing was uploaded.\n');
|
|
392
469
|
return 2;
|
|
393
470
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type DevicePresenceState = 'active' | 'idle' | 'locked' | 'unavailable';
|
|
2
|
+
export interface DevicePresence {
|
|
3
|
+
state: DevicePresenceState;
|
|
4
|
+
idle_seconds: number | null;
|
|
5
|
+
observation_eligible: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface MacosDevicePresenceOptions {
|
|
8
|
+
idleThresholdSeconds?: number;
|
|
9
|
+
cacheMs?: number;
|
|
10
|
+
now?: () => number;
|
|
11
|
+
readConsole?: () => Promise<string>;
|
|
12
|
+
readHid?: () => Promise<string>;
|
|
13
|
+
}
|
|
14
|
+
export declare function parseMacosConsoleLocked(value: string): boolean | null;
|
|
15
|
+
export declare function parseMacosIdleSeconds(value: string): number | null;
|
|
16
|
+
export declare function createMacosDevicePresenceProvider(options?: MacosDevicePresenceOptions): () => Promise<DevicePresence>;
|
|
17
|
+
//# sourceMappingURL=device-presence.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseMacosConsoleLocked = parseMacosConsoleLocked;
|
|
4
|
+
exports.parseMacosIdleSeconds = parseMacosIdleSeconds;
|
|
5
|
+
exports.createMacosDevicePresenceProvider = createMacosDevicePresenceProvider;
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const node_util_1 = require("node:util");
|
|
8
|
+
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
9
|
+
function parseMacosConsoleLocked(value) {
|
|
10
|
+
const match = /"IOConsoleLocked"\s*=\s*(Yes|No)/i.exec(value);
|
|
11
|
+
if (!match)
|
|
12
|
+
return null;
|
|
13
|
+
return match[1].toLowerCase() === 'yes';
|
|
14
|
+
}
|
|
15
|
+
function parseMacosIdleSeconds(value) {
|
|
16
|
+
const match = /"HIDIdleTime"\s*=\s*(\d+)/.exec(value);
|
|
17
|
+
if (!match)
|
|
18
|
+
return null;
|
|
19
|
+
const nanoseconds = Number(match[1]);
|
|
20
|
+
return Number.isFinite(nanoseconds) && nanoseconds >= 0 ? nanoseconds / 1_000_000_000 : null;
|
|
21
|
+
}
|
|
22
|
+
async function readIoreg(args) {
|
|
23
|
+
const { stdout } = await execFileAsync('/usr/sbin/ioreg', args, {
|
|
24
|
+
encoding: 'utf8', maxBuffer: 2_000_000, env: { ...process.env, LC_ALL: 'C', LANG: 'C' },
|
|
25
|
+
});
|
|
26
|
+
return stdout;
|
|
27
|
+
}
|
|
28
|
+
function createMacosDevicePresenceProvider(options = {}) {
|
|
29
|
+
const idleThresholdSeconds = Math.max(15, options.idleThresholdSeconds ?? 60);
|
|
30
|
+
const cacheMs = Math.max(500, options.cacheMs ?? 2_000);
|
|
31
|
+
const now = options.now ?? Date.now;
|
|
32
|
+
const readConsole = options.readConsole ?? (() => readIoreg(['-n', 'Root', '-d1']));
|
|
33
|
+
const readHid = options.readHid ?? (() => readIoreg(['-c', 'IOHIDSystem', '-r', '-d1']));
|
|
34
|
+
let cachedAt = 0;
|
|
35
|
+
let cached = { state: 'unavailable', idle_seconds: null, observation_eligible: false };
|
|
36
|
+
return async () => {
|
|
37
|
+
const current = now();
|
|
38
|
+
if (cachedAt > 0 && current - cachedAt < cacheMs)
|
|
39
|
+
return cached;
|
|
40
|
+
cachedAt = current;
|
|
41
|
+
try {
|
|
42
|
+
const [consoleOutput, hidOutput] = await Promise.all([readConsole(), readHid()]);
|
|
43
|
+
const locked = parseMacosConsoleLocked(consoleOutput);
|
|
44
|
+
const idleSeconds = parseMacosIdleSeconds(hidOutput);
|
|
45
|
+
if (locked === null || idleSeconds === null) {
|
|
46
|
+
cached = { state: 'unavailable', idle_seconds: idleSeconds, observation_eligible: false };
|
|
47
|
+
}
|
|
48
|
+
else if (locked) {
|
|
49
|
+
cached = { state: 'locked', idle_seconds: idleSeconds, observation_eligible: false };
|
|
50
|
+
}
|
|
51
|
+
else if (idleSeconds >= idleThresholdSeconds) {
|
|
52
|
+
cached = { state: 'idle', idle_seconds: idleSeconds, observation_eligible: false };
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
cached = { state: 'active', idle_seconds: idleSeconds, observation_eligible: true };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
cached = { state: 'unavailable', idle_seconds: null, observation_eligible: false };
|
|
60
|
+
}
|
|
61
|
+
return cached;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=device-presence.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AnonymousAiFootprintContribution } from './sharing';
|
|
2
|
+
export declare const GLOBAL_PREVIEW_ENDPOINT = "https://www.forkit.dev/api/v1/ai-footprints";
|
|
3
|
+
export declare const GLOBAL_PREVIEW_SYNC_INTERVAL_MS: number;
|
|
4
|
+
export interface GlobalPreviewStatus {
|
|
5
|
+
state: 'disabled' | 'waiting' | 'syncing' | 'contributed' | 'unavailable';
|
|
6
|
+
last_attempt_at: string | null;
|
|
7
|
+
last_success_at: string | null;
|
|
8
|
+
last_observed_seconds: number;
|
|
9
|
+
last_error: string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface GlobalPreviewContributorOptions {
|
|
12
|
+
stateDirectory?: string;
|
|
13
|
+
endpoint?: string;
|
|
14
|
+
fetchFn?: typeof fetch;
|
|
15
|
+
now?: () => Date;
|
|
16
|
+
minimumSyncIntervalMs?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare class GlobalPreviewContributor {
|
|
19
|
+
private readonly stateDirectory;
|
|
20
|
+
private readonly endpoint;
|
|
21
|
+
private readonly fetchFn;
|
|
22
|
+
private readonly now;
|
|
23
|
+
private readonly minimumSyncIntervalMs;
|
|
24
|
+
private currentStatus;
|
|
25
|
+
private loaded;
|
|
26
|
+
private syncing;
|
|
27
|
+
constructor(options?: GlobalPreviewContributorOptions);
|
|
28
|
+
status(permission: 'granted' | 'declined' | 'unset'): GlobalPreviewStatus;
|
|
29
|
+
private loadReceipt;
|
|
30
|
+
private saveStatus;
|
|
31
|
+
sync(contribution: AnonymousAiFootprintContribution, permission: 'granted' | 'declined' | 'unset'): Promise<GlobalPreviewStatus>;
|
|
32
|
+
private performSync;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=global-preview.d.ts.map
|
|
@@ -0,0 +1,209 @@
|
|
|
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.GlobalPreviewContributor = exports.GLOBAL_PREVIEW_SYNC_INTERVAL_MS = exports.GLOBAL_PREVIEW_ENDPOINT = void 0;
|
|
7
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const local_device_1 = require("./local-device");
|
|
11
|
+
exports.GLOBAL_PREVIEW_ENDPOINT = 'https://www.forkit.dev/api/v1/ai-footprints';
|
|
12
|
+
exports.GLOBAL_PREVIEW_SYNC_INTERVAL_MS = 15 * 60 * 1000;
|
|
13
|
+
function base64url(value) {
|
|
14
|
+
return value.toString('base64url');
|
|
15
|
+
}
|
|
16
|
+
function stableJson(value) {
|
|
17
|
+
if (Array.isArray(value))
|
|
18
|
+
return `[${value.map(stableJson).join(',')}]`;
|
|
19
|
+
if (value && typeof value === 'object') {
|
|
20
|
+
const record = value;
|
|
21
|
+
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(',')}}`;
|
|
22
|
+
}
|
|
23
|
+
return JSON.stringify(value);
|
|
24
|
+
}
|
|
25
|
+
function previewKeyPath(stateDirectory) {
|
|
26
|
+
return node_path_1.default.join(stateDirectory, 'global-preview-key.json');
|
|
27
|
+
}
|
|
28
|
+
function previewReceiptPath(stateDirectory) {
|
|
29
|
+
return node_path_1.default.join(stateDirectory, 'global-preview-receipt.json');
|
|
30
|
+
}
|
|
31
|
+
async function writePrivateJson(target, value) {
|
|
32
|
+
await promises_1.default.mkdir(node_path_1.default.dirname(target), { recursive: true, mode: 0o700 });
|
|
33
|
+
await promises_1.default.chmod(node_path_1.default.dirname(target), 0o700);
|
|
34
|
+
const temporary = `${target}.${process.pid}.tmp`;
|
|
35
|
+
await promises_1.default.writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
|
|
36
|
+
await promises_1.default.chmod(temporary, 0o600);
|
|
37
|
+
await promises_1.default.rename(temporary, target);
|
|
38
|
+
await promises_1.default.chmod(target, 0o600);
|
|
39
|
+
}
|
|
40
|
+
function validKeyFile(value) {
|
|
41
|
+
if (!value || typeof value !== 'object')
|
|
42
|
+
return false;
|
|
43
|
+
const key = value;
|
|
44
|
+
return key.schema_version === '1.0'
|
|
45
|
+
&& key.algorithm === 'Ed25519'
|
|
46
|
+
&& typeof key.public_key === 'string'
|
|
47
|
+
&& /^[A-Za-z0-9_-]{40,256}$/.test(key.public_key)
|
|
48
|
+
&& typeof key.private_key === 'string'
|
|
49
|
+
&& /^[A-Za-z0-9_-]{40,256}$/.test(key.private_key);
|
|
50
|
+
}
|
|
51
|
+
async function loadOrCreateKey(stateDirectory) {
|
|
52
|
+
const target = previewKeyPath(stateDirectory);
|
|
53
|
+
try {
|
|
54
|
+
const value = JSON.parse(await promises_1.default.readFile(target, 'utf8'));
|
|
55
|
+
if (validKeyFile(value))
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Missing or invalid local preview identity is replaced before contribution.
|
|
60
|
+
}
|
|
61
|
+
const pair = node_crypto_1.default.generateKeyPairSync('ed25519');
|
|
62
|
+
const value = {
|
|
63
|
+
schema_version: '1.0',
|
|
64
|
+
algorithm: 'Ed25519',
|
|
65
|
+
public_key: base64url(pair.publicKey.export({ type: 'spki', format: 'der' })),
|
|
66
|
+
private_key: base64url(pair.privateKey.export({ type: 'pkcs8', format: 'der' })),
|
|
67
|
+
};
|
|
68
|
+
await writePrivateJson(target, value);
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
function emptyStatus() {
|
|
72
|
+
return {
|
|
73
|
+
state: 'waiting',
|
|
74
|
+
last_attempt_at: null,
|
|
75
|
+
last_success_at: null,
|
|
76
|
+
last_observed_seconds: 0,
|
|
77
|
+
last_error: null,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function validReceipt(value) {
|
|
81
|
+
if (!value || typeof value !== 'object')
|
|
82
|
+
return false;
|
|
83
|
+
const receipt = value;
|
|
84
|
+
return receipt.schema_version === '1.0'
|
|
85
|
+
&& ['disabled', 'waiting', 'syncing', 'contributed', 'unavailable'].includes(String(receipt.state))
|
|
86
|
+
&& (receipt.last_attempt_at === null || typeof receipt.last_attempt_at === 'string')
|
|
87
|
+
&& (receipt.last_success_at === null || typeof receipt.last_success_at === 'string')
|
|
88
|
+
&& Number.isSafeInteger(receipt.last_observed_seconds)
|
|
89
|
+
&& Number(receipt.last_observed_seconds) >= 0
|
|
90
|
+
&& (receipt.last_error === null || typeof receipt.last_error === 'string');
|
|
91
|
+
}
|
|
92
|
+
class GlobalPreviewContributor {
|
|
93
|
+
stateDirectory;
|
|
94
|
+
endpoint;
|
|
95
|
+
fetchFn;
|
|
96
|
+
now;
|
|
97
|
+
minimumSyncIntervalMs;
|
|
98
|
+
currentStatus = emptyStatus();
|
|
99
|
+
loaded = false;
|
|
100
|
+
syncing = null;
|
|
101
|
+
constructor(options = {}) {
|
|
102
|
+
this.stateDirectory = options.stateDirectory ?? (0, local_device_1.defaultLocalStateDirectory)();
|
|
103
|
+
this.endpoint = options.endpoint ?? exports.GLOBAL_PREVIEW_ENDPOINT;
|
|
104
|
+
this.fetchFn = options.fetchFn ?? fetch;
|
|
105
|
+
this.now = options.now ?? (() => new Date());
|
|
106
|
+
this.minimumSyncIntervalMs = options.minimumSyncIntervalMs ?? exports.GLOBAL_PREVIEW_SYNC_INTERVAL_MS;
|
|
107
|
+
const endpointUrl = new URL(this.endpoint);
|
|
108
|
+
if (endpointUrl.protocol !== 'https:' || endpointUrl.origin !== 'https://www.forkit.dev' || endpointUrl.pathname !== '/api/v1/ai-footprints') {
|
|
109
|
+
throw new Error('GLOBAL_PREVIEW_ENDPOINT_NOT_ALLOWED');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
status(permission) {
|
|
113
|
+
return permission === 'granted' ? { ...this.currentStatus } : { ...this.currentStatus, state: 'disabled', last_error: null };
|
|
114
|
+
}
|
|
115
|
+
async loadReceipt() {
|
|
116
|
+
if (this.loaded)
|
|
117
|
+
return;
|
|
118
|
+
this.loaded = true;
|
|
119
|
+
try {
|
|
120
|
+
const value = JSON.parse(await promises_1.default.readFile(previewReceiptPath(this.stateDirectory), 'utf8'));
|
|
121
|
+
if (validReceipt(value))
|
|
122
|
+
this.currentStatus = { ...value };
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
this.currentStatus = emptyStatus();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async saveStatus() {
|
|
129
|
+
await writePrivateJson(previewReceiptPath(this.stateDirectory), {
|
|
130
|
+
schema_version: '1.0',
|
|
131
|
+
...this.currentStatus,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
async sync(contribution, permission) {
|
|
135
|
+
if (permission !== 'granted')
|
|
136
|
+
return this.status(permission);
|
|
137
|
+
if (this.syncing)
|
|
138
|
+
return this.syncing;
|
|
139
|
+
this.syncing = this.performSync(contribution).finally(() => { this.syncing = null; });
|
|
140
|
+
return this.syncing;
|
|
141
|
+
}
|
|
142
|
+
async performSync(contribution) {
|
|
143
|
+
await this.loadReceipt();
|
|
144
|
+
const now = this.now();
|
|
145
|
+
const lastAttempt = this.currentStatus.last_attempt_at ? Date.parse(this.currentStatus.last_attempt_at) : 0;
|
|
146
|
+
const observedSeconds = contribution.observation.valid_seconds;
|
|
147
|
+
if (lastAttempt && now.getTime() - lastAttempt < this.minimumSyncIntervalMs && observedSeconds <= this.currentStatus.last_observed_seconds + 60) {
|
|
148
|
+
return { ...this.currentStatus };
|
|
149
|
+
}
|
|
150
|
+
this.currentStatus = { ...this.currentStatus, state: 'syncing', last_attempt_at: now.toISOString(), last_error: null };
|
|
151
|
+
await this.saveStatus();
|
|
152
|
+
try {
|
|
153
|
+
const challengeResponse = await this.fetchFn(`${this.endpoint}/challenges`, {
|
|
154
|
+
method: 'POST',
|
|
155
|
+
headers: { accept: 'application/json', 'content-type': 'application/json' },
|
|
156
|
+
body: JSON.stringify({ purpose: 'preview-contribution' }),
|
|
157
|
+
signal: AbortSignal.timeout(10_000),
|
|
158
|
+
});
|
|
159
|
+
if (!challengeResponse.ok)
|
|
160
|
+
throw new Error(`CHALLENGE_${challengeResponse.status}`);
|
|
161
|
+
const challenge = await challengeResponse.json();
|
|
162
|
+
if (typeof challenge.challengeId !== 'string' || !/^[a-f0-9]{64}$/.test(challenge.challengeId))
|
|
163
|
+
throw new Error('CHALLENGE_INVALID');
|
|
164
|
+
const key = await loadOrCreateKey(this.stateDirectory);
|
|
165
|
+
const clientDataHash = node_crypto_1.default.createHash('sha256').update(`${challenge.challengeId}.${stableJson(contribution)}`).digest();
|
|
166
|
+
const privateKey = node_crypto_1.default.createPrivateKey({ key: Buffer.from(key.private_key, 'base64url'), type: 'pkcs8', format: 'der' });
|
|
167
|
+
const signature = node_crypto_1.default.sign(null, clientDataHash, privateKey);
|
|
168
|
+
const contributionResponse = await this.fetchFn(`${this.endpoint}/contributions`, {
|
|
169
|
+
method: 'POST',
|
|
170
|
+
headers: { accept: 'application/json', 'content-type': 'application/json' },
|
|
171
|
+
body: JSON.stringify({
|
|
172
|
+
consent: true,
|
|
173
|
+
contribution,
|
|
174
|
+
proof: {
|
|
175
|
+
type: 'community-ed25519',
|
|
176
|
+
challenge_id: challenge.challengeId,
|
|
177
|
+
public_key: key.public_key,
|
|
178
|
+
signature: base64url(signature),
|
|
179
|
+
client_data_hash: clientDataHash.toString('hex'),
|
|
180
|
+
},
|
|
181
|
+
}),
|
|
182
|
+
signal: AbortSignal.timeout(10_000),
|
|
183
|
+
});
|
|
184
|
+
if (!contributionResponse.ok)
|
|
185
|
+
throw new Error(`CONTRIBUTION_${contributionResponse.status}`);
|
|
186
|
+
const accepted = await contributionResponse.json();
|
|
187
|
+
if (accepted.accepted !== true || accepted.trustState !== 'preview')
|
|
188
|
+
throw new Error('CONTRIBUTION_NOT_ACCEPTED');
|
|
189
|
+
this.currentStatus = {
|
|
190
|
+
state: 'contributed',
|
|
191
|
+
last_attempt_at: now.toISOString(),
|
|
192
|
+
last_success_at: now.toISOString(),
|
|
193
|
+
last_observed_seconds: observedSeconds,
|
|
194
|
+
last_error: null,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
this.currentStatus = {
|
|
199
|
+
...this.currentStatus,
|
|
200
|
+
state: 'unavailable',
|
|
201
|
+
last_error: error instanceof Error ? error.message.slice(0, 96) : 'GLOBAL_PREVIEW_UNAVAILABLE',
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
await this.saveStatus();
|
|
205
|
+
return { ...this.currentStatus };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
exports.GlobalPreviewContributor = GlobalPreviewContributor;
|
|
209
|
+
//# sourceMappingURL=global-preview.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export { detectAgentProducts, listSystemProcesses } from './agents';
|
|
|
2
2
|
export { aggregateMacosFieldEvaluationDirectory, aggregateMacosFieldEvaluations } from './aggregate';
|
|
3
3
|
export { runCensus } from './census';
|
|
4
4
|
export { authorizeAnonymousAiFootprintContribution, buildAnonymousAiFootprintPreview, MINIMUM_COMPARISON_SECONDS } from './sharing';
|
|
5
|
+
export { GlobalPreviewContributor, GLOBAL_PREVIEW_ENDPOINT, GLOBAL_PREVIEW_SYNC_INTERVAL_MS } from './global-preview';
|
|
6
|
+
export type { GlobalPreviewContributorOptions, GlobalPreviewStatus } from './global-preview';
|
|
5
7
|
export { ActivityMonitor } from './monitor';
|
|
6
8
|
export type * from './monitor';
|
|
7
9
|
export { buildCensusShareSnapshot, renderCensusSharePage } from './share-page';
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.evaluateMacosFieldTruthFile = exports.evaluateMacosFieldTruth = exports.detectMacosMajorVersion = exports.formatDoctorReport = exports.formatCensusReport = exports.scanFilesystemModels = exports.parseLoopbackEndpointList = exports.parseLoopbackEndpoint = exports.runDoctor = exports.detectMcpConfigs = exports.detectAiTools = exports.startAiFootprintsServer = exports.buildLocalInsights = exports.renderCensusSharePage = exports.buildCensusShareSnapshot = exports.ActivityMonitor = exports.MINIMUM_COMPARISON_SECONDS = exports.buildAnonymousAiFootprintPreview = exports.authorizeAnonymousAiFootprintContribution = exports.runCensus = exports.aggregateMacosFieldEvaluations = exports.aggregateMacosFieldEvaluationDirectory = exports.listSystemProcesses = exports.detectAgentProducts = void 0;
|
|
17
|
+
exports.evaluateMacosFieldTruthFile = exports.evaluateMacosFieldTruth = exports.detectMacosMajorVersion = exports.formatDoctorReport = exports.formatCensusReport = exports.scanFilesystemModels = exports.parseLoopbackEndpointList = exports.parseLoopbackEndpoint = exports.runDoctor = exports.detectMcpConfigs = exports.detectAiTools = exports.startAiFootprintsServer = exports.buildLocalInsights = exports.renderCensusSharePage = exports.buildCensusShareSnapshot = exports.ActivityMonitor = exports.GLOBAL_PREVIEW_SYNC_INTERVAL_MS = exports.GLOBAL_PREVIEW_ENDPOINT = exports.GlobalPreviewContributor = exports.MINIMUM_COMPARISON_SECONDS = exports.buildAnonymousAiFootprintPreview = exports.authorizeAnonymousAiFootprintContribution = exports.runCensus = exports.aggregateMacosFieldEvaluations = exports.aggregateMacosFieldEvaluationDirectory = exports.listSystemProcesses = exports.detectAgentProducts = void 0;
|
|
18
18
|
var agents_1 = require("./agents");
|
|
19
19
|
Object.defineProperty(exports, "detectAgentProducts", { enumerable: true, get: function () { return agents_1.detectAgentProducts; } });
|
|
20
20
|
Object.defineProperty(exports, "listSystemProcesses", { enumerable: true, get: function () { return agents_1.listSystemProcesses; } });
|
|
@@ -27,6 +27,10 @@ var sharing_1 = require("./sharing");
|
|
|
27
27
|
Object.defineProperty(exports, "authorizeAnonymousAiFootprintContribution", { enumerable: true, get: function () { return sharing_1.authorizeAnonymousAiFootprintContribution; } });
|
|
28
28
|
Object.defineProperty(exports, "buildAnonymousAiFootprintPreview", { enumerable: true, get: function () { return sharing_1.buildAnonymousAiFootprintPreview; } });
|
|
29
29
|
Object.defineProperty(exports, "MINIMUM_COMPARISON_SECONDS", { enumerable: true, get: function () { return sharing_1.MINIMUM_COMPARISON_SECONDS; } });
|
|
30
|
+
var global_preview_1 = require("./global-preview");
|
|
31
|
+
Object.defineProperty(exports, "GlobalPreviewContributor", { enumerable: true, get: function () { return global_preview_1.GlobalPreviewContributor; } });
|
|
32
|
+
Object.defineProperty(exports, "GLOBAL_PREVIEW_ENDPOINT", { enumerable: true, get: function () { return global_preview_1.GLOBAL_PREVIEW_ENDPOINT; } });
|
|
33
|
+
Object.defineProperty(exports, "GLOBAL_PREVIEW_SYNC_INTERVAL_MS", { enumerable: true, get: function () { return global_preview_1.GLOBAL_PREVIEW_SYNC_INTERVAL_MS; } });
|
|
30
34
|
var monitor_1 = require("./monitor");
|
|
31
35
|
Object.defineProperty(exports, "ActivityMonitor", { enumerable: true, get: function () { return monitor_1.ActivityMonitor; } });
|
|
32
36
|
var share_page_1 = require("./share-page");
|
package/dist/insights.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MonitorSnapshot } from './monitor';
|
|
2
2
|
import { type UiLocale } from './localization';
|
|
3
|
-
export type LocalInsightKind = 'activity-share' | 'longest-block' | 'workflow' | 'local-models-unused' | 'mostly-idle';
|
|
3
|
+
export type LocalInsightKind = 'activity-share' | 'longest-block' | 'workflow' | 'local-models-unused' | 'mostly-idle' | 'hosted-local';
|
|
4
4
|
export interface LocalInsight {
|
|
5
5
|
kind: LocalInsightKind;
|
|
6
6
|
text: string;
|
package/dist/insights.js
CHANGED
|
@@ -9,7 +9,9 @@ function durationLabel(value, locale) {
|
|
|
9
9
|
return locale === 'de' ? `${seconds} Sek` : `${seconds}s`;
|
|
10
10
|
if (seconds < 3600)
|
|
11
11
|
return locale === 'de' ? `${Math.floor(seconds / 60)} Min ${seconds % 60} Sek` : `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
12
|
-
return locale === 'de'
|
|
12
|
+
return locale === 'de'
|
|
13
|
+
? `${Math.floor(seconds / 3600)} Std ${Math.floor((seconds % 3600) / 60)} Min ${seconds % 60} Sek`
|
|
14
|
+
: `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}m ${seconds % 60}s`;
|
|
13
15
|
}
|
|
14
16
|
function longestActiveSeconds(timeline, gapToleranceMs) {
|
|
15
17
|
let longest = 0;
|
|
@@ -54,22 +56,22 @@ function buildLocalInsights(monitor, footprint, minimumSeconds = MINIMUM_INSIGHT
|
|
|
54
56
|
insights.push({ kind: 'mostly-idle', text: (0, localization_1.uiText)(locale, 'insightMostlyIdle') });
|
|
55
57
|
}
|
|
56
58
|
else if (activeProducts.length === 1) {
|
|
57
|
-
insights.push({ kind: '
|
|
59
|
+
insights.push({ kind: 'workflow', text: (0, localization_1.uiText)(locale, 'insightFocused', { product: activeProducts[0].name }) });
|
|
58
60
|
}
|
|
59
61
|
else {
|
|
60
62
|
insights.push({ kind: 'activity-share', text: (0, localization_1.uiText)(locale, 'insightActivityGeneric', { percent: activityPercent }) });
|
|
61
63
|
}
|
|
62
64
|
const longest = longestActiveSeconds(monitor.timeline, monitor.sample_interval_ms * 3);
|
|
63
65
|
if (longest >= 10)
|
|
64
|
-
insights.push({ kind: 'longest-block', text: (0, localization_1.uiText)(locale, '
|
|
65
|
-
if (
|
|
66
|
+
insights.push({ kind: 'longest-block', text: (0, localization_1.uiText)(locale, 'insightDeepWork', { duration: durationLabel(longest, locale) }) });
|
|
67
|
+
if (footprint.confirmed_running_model_count > 0) {
|
|
68
|
+
insights.push({ kind: 'hosted-local', text: (0, localization_1.uiText)(locale, 'hostedModelsRunningInsight') });
|
|
69
|
+
}
|
|
70
|
+
else if (activeProducts.length > 1) {
|
|
66
71
|
insights.push({ kind: 'workflow', text: (0, localization_1.uiText)(locale, 'insightMulti', { count: activeProducts.length }) });
|
|
67
72
|
}
|
|
68
73
|
else if (footprint.model_record_count > 0 && footprint.confirmed_running_model_count === 0) {
|
|
69
|
-
insights.push({ kind: 'local-models-unused', text: (0, localization_1.uiText)(locale, 'insightModelsUnused', { count: footprint.model_record_count }) });
|
|
70
|
-
}
|
|
71
|
-
else if (activeProducts.length === 1 && insights[0]?.kind !== 'activity-share') {
|
|
72
|
-
insights.push({ kind: 'workflow', text: (0, localization_1.uiText)(locale, 'insightFocused', { product: activeProducts[0].name }) });
|
|
74
|
+
insights.push({ kind: activeProducts.length ? 'hosted-local' : 'local-models-unused', text: (0, localization_1.uiText)(locale, activeProducts.length ? 'insightHostedFirst' : 'insightModelsUnused', { product: activeProducts[0]?.name ?? '', count: footprint.model_record_count }) });
|
|
73
75
|
}
|
|
74
76
|
return insights.slice(0, 3);
|
|
75
77
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { MonitorSnapshot } from './monitor';
|
|
2
|
+
export interface LastStoppedSummary {
|
|
3
|
+
saved_at: string;
|
|
4
|
+
observed_seconds: number;
|
|
5
|
+
active_seconds: number;
|
|
6
|
+
activity_ratio: number | null;
|
|
7
|
+
active_tool: string | null;
|
|
8
|
+
local_models_found: number;
|
|
9
|
+
local_models_running: number;
|
|
10
|
+
share_card_data?: {
|
|
11
|
+
ratio: number | null;
|
|
12
|
+
active_tool: string | null;
|
|
13
|
+
local_models_found: number;
|
|
14
|
+
local_models_running: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface LocalObservationState {
|
|
18
|
+
schema_version: '1.0';
|
|
19
|
+
current_observation_id: number;
|
|
20
|
+
started_at: string | null;
|
|
21
|
+
observed_seconds: number;
|
|
22
|
+
active_seconds: number;
|
|
23
|
+
timeline_blocks: MonitorSnapshot['timeline'];
|
|
24
|
+
detected_systems: Array<Pick<MonitorSnapshot['products'][number], 'signature' | 'name' | 'kind' | 'state' | 'active_seconds'>>;
|
|
25
|
+
app_color_mapping: Record<string, string>;
|
|
26
|
+
latest_insights: string[];
|
|
27
|
+
last_stopped_summary: LastStoppedSummary | null;
|
|
28
|
+
user_stopped: boolean;
|
|
29
|
+
snapshot: MonitorSnapshot;
|
|
30
|
+
}
|
|
31
|
+
export declare function localObservationStatePath(stateDirectory?: string): string;
|
|
32
|
+
export declare function loadLocalObservationState(stateDirectory?: string): Promise<LocalObservationState | null>;
|
|
33
|
+
export declare function saveLocalObservationState(snapshot: MonitorSnapshot, options: {
|
|
34
|
+
latestInsights: string[];
|
|
35
|
+
localModelsFound: number;
|
|
36
|
+
localModelsRunning: number;
|
|
37
|
+
appColorMapping?: Record<string, string>;
|
|
38
|
+
userStopped?: boolean;
|
|
39
|
+
lastStoppedSummary?: LastStoppedSummary | null;
|
|
40
|
+
stateDirectory?: string;
|
|
41
|
+
}): Promise<LocalObservationState>;
|
|
42
|
+
export declare function clearLocalObservationState(stateDirectory?: string): Promise<void>;
|
|
43
|
+
//# sourceMappingURL=local-observation-state.d.ts.map
|