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.
Files changed (150) hide show
  1. package/LICENSE +21 -0
  2. package/PRIVACY.md +150 -0
  3. package/README.md +313 -0
  4. package/STATUS.md +136 -0
  5. package/dist/agents.d.ts +22 -0
  6. package/dist/agents.js +281 -0
  7. package/dist/aggregate.d.ts +28 -0
  8. package/dist/aggregate.js +151 -0
  9. package/dist/assets/forkit-icon-dark.png +0 -0
  10. package/dist/assets/forkit-icon-light.png +0 -0
  11. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Info.plist +5 -0
  12. package/dist/bootstrap/Forkit AI Footprint.app/Contents/MacOS/Forkit AI Footprint +0 -0
  13. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/ForkitAIFootprint.icns +0 -0
  14. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/ForkitStatusTemplate.png +0 -0
  15. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/agents.d.ts +22 -0
  16. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/agents.js +281 -0
  17. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/aggregate.d.ts +28 -0
  18. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/aggregate.js +151 -0
  19. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/assets/forkit-icon-dark.png +0 -0
  20. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/assets/forkit-icon-light.png +0 -0
  21. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/census.d.ts +3 -0
  22. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/census.js +196 -0
  23. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/cli.d.ts +32 -0
  24. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/cli.js +404 -0
  25. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/doctor.d.ts +14 -0
  26. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/doctor.js +71 -0
  27. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/endpoints.d.ts +8 -0
  28. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/endpoints.js +45 -0
  29. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/evaluate.d.ts +44 -0
  30. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/evaluate.js +102 -0
  31. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/filesystem.d.ts +11 -0
  32. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/filesystem.js +226 -0
  33. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/format.d.ts +7 -0
  34. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/format.js +78 -0
  35. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/hash.d.ts +4 -0
  36. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/hash.js +16 -0
  37. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/index.d.ts +22 -0
  38. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/index.js +58 -0
  39. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/insights.d.ts +13 -0
  40. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/insights.js +76 -0
  41. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/local-device.d.ts +19 -0
  42. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/local-device.js +81 -0
  43. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/locales.json +272 -0
  44. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/localization.d.ts +277 -0
  45. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/localization.js +55 -0
  46. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/mcp.d.ts +9 -0
  47. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/mcp.js +93 -0
  48. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/monitor.d.ts +99 -0
  49. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/monitor.js +285 -0
  50. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/base.d.ts +34 -0
  51. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/base.js +88 -0
  52. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/index.d.ts +6 -0
  53. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/index.js +24 -0
  54. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/lmstudio.d.ts +9 -0
  55. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/lmstudio.js +71 -0
  56. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/ollama.d.ts +12 -0
  57. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/ollama.js +171 -0
  58. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/openai-compatible.d.ts +10 -0
  59. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/openai-compatible.js +68 -0
  60. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/resource-evidence.d.ts +17 -0
  61. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/resource-evidence.js +52 -0
  62. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-activity.d.ts +5 -0
  63. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-activity.js +66 -0
  64. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-identity.d.ts +7 -0
  65. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-identity.js +66 -0
  66. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/server.d.ts +21 -0
  67. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/server.js +308 -0
  68. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/share-page.d.ts +75 -0
  69. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/share-page.js +167 -0
  70. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/sharing.d.ts +35 -0
  71. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/sharing.js +67 -0
  72. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/tools.d.ts +9 -0
  73. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/tools.js +106 -0
  74. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/types.d.ts +166 -0
  75. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/types.js +3 -0
  76. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/version.d.ts +4 -0
  77. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/version.js +13 -0
  78. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/index.d.ts +76 -0
  79. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/index.js +366 -0
  80. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/license +9 -0
  81. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/package.json +44 -0
  82. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/readme.md +56 -0
  83. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/vendor/fastlist-0.3.0-x64.exe +0 -0
  84. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/vendor/fastlist-0.3.0-x86.exe +0 -0
  85. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/package.json +6 -0
  86. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/runtime/node +0 -0
  87. package/dist/bootstrap/Forkit AI Footprint.app/Contents/_CodeSignature/CodeResources +931 -0
  88. package/dist/census.d.ts +3 -0
  89. package/dist/census.js +196 -0
  90. package/dist/cli.d.ts +32 -0
  91. package/dist/cli.js +404 -0
  92. package/dist/doctor.d.ts +14 -0
  93. package/dist/doctor.js +71 -0
  94. package/dist/endpoints.d.ts +8 -0
  95. package/dist/endpoints.js +45 -0
  96. package/dist/evaluate.d.ts +44 -0
  97. package/dist/evaluate.js +102 -0
  98. package/dist/filesystem.d.ts +11 -0
  99. package/dist/filesystem.js +226 -0
  100. package/dist/format.d.ts +7 -0
  101. package/dist/format.js +78 -0
  102. package/dist/hash.d.ts +4 -0
  103. package/dist/hash.js +16 -0
  104. package/dist/index.d.ts +22 -0
  105. package/dist/index.js +58 -0
  106. package/dist/insights.d.ts +13 -0
  107. package/dist/insights.js +76 -0
  108. package/dist/local-device.d.ts +19 -0
  109. package/dist/local-device.js +81 -0
  110. package/dist/locales.json +272 -0
  111. package/dist/localization.d.ts +277 -0
  112. package/dist/localization.js +55 -0
  113. package/dist/mcp.d.ts +9 -0
  114. package/dist/mcp.js +93 -0
  115. package/dist/monitor.d.ts +99 -0
  116. package/dist/monitor.js +285 -0
  117. package/dist/providers/base.d.ts +34 -0
  118. package/dist/providers/base.js +88 -0
  119. package/dist/providers/index.d.ts +6 -0
  120. package/dist/providers/index.js +24 -0
  121. package/dist/providers/lmstudio.d.ts +9 -0
  122. package/dist/providers/lmstudio.js +71 -0
  123. package/dist/providers/ollama.d.ts +12 -0
  124. package/dist/providers/ollama.js +171 -0
  125. package/dist/providers/openai-compatible.d.ts +10 -0
  126. package/dist/providers/openai-compatible.js +68 -0
  127. package/dist/resource-evidence.d.ts +17 -0
  128. package/dist/resource-evidence.js +52 -0
  129. package/dist/runtime-activity.d.ts +5 -0
  130. package/dist/runtime-activity.js +66 -0
  131. package/dist/runtime-identity.d.ts +7 -0
  132. package/dist/runtime-identity.js +66 -0
  133. package/dist/server.d.ts +21 -0
  134. package/dist/server.js +308 -0
  135. package/dist/share-page.d.ts +75 -0
  136. package/dist/share-page.js +167 -0
  137. package/dist/sharing.d.ts +35 -0
  138. package/dist/sharing.js +67 -0
  139. package/dist/tools.d.ts +9 -0
  140. package/dist/tools.js +106 -0
  141. package/dist/types.d.ts +166 -0
  142. package/dist/types.js +3 -0
  143. package/dist/version.d.ts +4 -0
  144. package/dist/version.js +13 -0
  145. package/docs/GLOBAL_DISTRIBUTION.md +79 -0
  146. package/docs/MACOS_ACCURACY_GATE.md +140 -0
  147. package/docs/MACOS_TESTER_GUIDE.md +88 -0
  148. package/examples/macos-truth.template.json +10 -0
  149. package/package.json +75 -0
  150. package/release/channels.json +41 -0
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ interface ParsedOptions {
3
+ command: 'install' | 'help' | 'version' | 'scan' | 'monitor' | 'doctor' | 'evaluate' | 'aggregate' | 'share-page' | 'serve';
4
+ json: boolean;
5
+ output: string | null;
6
+ includeRuntimes: boolean;
7
+ includeFilesystem: boolean;
8
+ includeAgents: boolean;
9
+ includeTools: boolean;
10
+ includeMcp: boolean;
11
+ modelDirs: string[];
12
+ verbose: boolean;
13
+ copy: boolean;
14
+ guess: number | null;
15
+ anonymousPayload: boolean;
16
+ shareConsent: boolean;
17
+ truth: string | null;
18
+ results: string | null;
19
+ port: number;
20
+ }
21
+ export declare function parseArgs(args: string[]): ParsedOptions;
22
+ export declare function installPersistentMacApp(options?: {
23
+ applicationsDirectory?: string;
24
+ open?: boolean;
25
+ }): Promise<string>;
26
+ export declare function clipboardCommands(platform: NodeJS.Platform): Array<{
27
+ command: string;
28
+ args: string[];
29
+ }>;
30
+ export declare function main(args?: string[]): Promise<number>;
31
+ export {};
32
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1,404 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.parseArgs = parseArgs;
8
+ exports.installPersistentMacApp = installPersistentMacApp;
9
+ exports.clipboardCommands = clipboardCommands;
10
+ exports.main = main;
11
+ const promises_1 = __importDefault(require("node:fs/promises"));
12
+ const node_path_1 = __importDefault(require("node:path"));
13
+ const node_os_1 = __importDefault(require("node:os"));
14
+ const node_child_process_1 = require("node:child_process");
15
+ const census_1 = require("./census");
16
+ const doctor_1 = require("./doctor");
17
+ const format_1 = require("./format");
18
+ const version_1 = require("./version");
19
+ const monitor_1 = require("./monitor");
20
+ const evaluate_1 = require("./evaluate");
21
+ const aggregate_1 = require("./aggregate");
22
+ const share_page_1 = require("./share-page");
23
+ const server_1 = require("./server");
24
+ const local_device_1 = require("./local-device");
25
+ const HELP = `Forkit AI Footprints
26
+ Private, metadata-only local AI inventory.
27
+ Apple Silicon macOS local release.
28
+
29
+ Usage:
30
+ forkit-ai-footprints
31
+ forkit-ai-footprints serve [--port 47811]
32
+ forkit-ai-footprints monitor [--json]
33
+ forkit-ai-footprints scan [options]
34
+ forkit-ai-footprints doctor [--json]
35
+ forkit-ai-footprints share-page --output /path/to/local-ai-footprint.html
36
+ forkit-ai-footprints --version
37
+
38
+ Options:
39
+ --json Print a machine-readable AI Footprint report
40
+ --verbose Show detector and identity details
41
+ --guess <count> Compare your estimate with discovered models
42
+ --copy Copy the rendered local result to the clipboard
43
+ --anonymous-payload Reserved for a completed 10-minute monitor session
44
+ --consent-share Separate consent; never enables upload in this build
45
+ --output <file> Save the selected human or JSON report
46
+ --model-dir <path> Inspect an explicit model directory; repeatable
47
+ --truth <file> Evaluate locally against manually labelled macOS truth
48
+ --results <directory> Aggregate local macOS evaluation JSON files
49
+ --port <number> Local-only serve port (default: 47811)
50
+ --no-runtimes Skip local runtime API discovery
51
+ --no-model-files Skip filesystem model metadata discovery
52
+ --no-agents Skip local process metadata discovery
53
+ --no-tools Skip passive AI-tool detection
54
+ --no-mcp Skip known MCP configuration counts
55
+ -h, --help Show this help
56
+
57
+ Privacy:
58
+ AI Footprints reads metadata only. It does not read model bytes, retain raw process
59
+ commands or MCP configuration values, authenticate to Forkit.dev, or write to
60
+ any passport, registry, Runtime_C2, or production service.
61
+ `;
62
+ function parseArgs(args) {
63
+ let command = 'install';
64
+ let json = false;
65
+ let output = null;
66
+ let includeRuntimes = true;
67
+ let includeFilesystem = true;
68
+ let includeAgents = true;
69
+ let includeTools = true;
70
+ let includeMcp = true;
71
+ let verbose = false;
72
+ let copy = false;
73
+ let guess = null;
74
+ let anonymousPayload = false;
75
+ let shareConsent = false;
76
+ let truth = null;
77
+ let results = null;
78
+ let port = 47811;
79
+ const modelDirs = [];
80
+ for (let index = 0; index < args.length; index += 1) {
81
+ const arg = args[index];
82
+ if (arg === 'install' || arg === 'install-app')
83
+ command = 'install';
84
+ else if (arg === 'scan' || arg === 'report')
85
+ command = 'scan';
86
+ else if (arg === 'monitor')
87
+ command = 'monitor';
88
+ else if (arg === 'doctor')
89
+ command = 'doctor';
90
+ else if (arg === 'evaluate')
91
+ command = 'evaluate';
92
+ else if (arg === 'aggregate')
93
+ command = 'aggregate';
94
+ else if (arg === 'share-page')
95
+ command = 'share-page';
96
+ else if (arg === 'serve')
97
+ command = 'serve';
98
+ else if (arg === '--version' || arg === '-V' || arg === 'version')
99
+ command = 'version';
100
+ else if (arg === '--help' || arg === '-h' || arg === 'help')
101
+ command = 'help';
102
+ else if (arg === '--json')
103
+ json = true;
104
+ else if (arg === '--verbose')
105
+ verbose = true;
106
+ else if (arg === '--copy')
107
+ copy = true;
108
+ else if (arg === '--anonymous-payload')
109
+ anonymousPayload = true;
110
+ else if (arg === '--consent-share')
111
+ shareConsent = true;
112
+ else if (arg === '--no-runtimes')
113
+ includeRuntimes = false;
114
+ else if (arg === '--no-model-files')
115
+ includeFilesystem = false;
116
+ else if (arg === '--no-agents')
117
+ includeAgents = false;
118
+ else if (arg === '--no-tools')
119
+ includeTools = false;
120
+ else if (arg === '--no-mcp')
121
+ includeMcp = false;
122
+ else if (arg === '--guess') {
123
+ const rawGuess = args[index + 1];
124
+ const parsedGuess = Number(rawGuess);
125
+ if (!rawGuess || !Number.isFinite(parsedGuess) || parsedGuess < 0) {
126
+ throw new Error('--guess requires a non-negative number.');
127
+ }
128
+ guess = Math.floor(parsedGuess);
129
+ index += 1;
130
+ }
131
+ else if (arg === '--port') {
132
+ const rawPort = args[index + 1];
133
+ const parsedPort = Number(rawPort);
134
+ if (!rawPort || !Number.isSafeInteger(parsedPort) || parsedPort < 1024 || parsedPort > 65535) {
135
+ throw new Error('--port requires an integer from 1024 to 65535.');
136
+ }
137
+ port = parsedPort;
138
+ index += 1;
139
+ }
140
+ else if (arg === '--output') {
141
+ output = args[index + 1] ?? null;
142
+ if (!output)
143
+ throw new Error('--output requires a file path.');
144
+ index += 1;
145
+ }
146
+ else if (arg === '--truth') {
147
+ truth = args[index + 1] ?? null;
148
+ if (!truth)
149
+ throw new Error('--truth requires a file path.');
150
+ index += 1;
151
+ }
152
+ else if (arg === '--results') {
153
+ results = args[index + 1] ?? null;
154
+ if (!results)
155
+ throw new Error('--results requires a directory path.');
156
+ index += 1;
157
+ }
158
+ else if (arg === '--model-dir') {
159
+ const directory = args[index + 1];
160
+ if (!directory)
161
+ throw new Error('--model-dir requires a directory path.');
162
+ modelDirs.push(directory);
163
+ index += 1;
164
+ }
165
+ else {
166
+ throw new Error(`Unknown argument: ${arg}`);
167
+ }
168
+ }
169
+ return {
170
+ command,
171
+ json,
172
+ output,
173
+ includeRuntimes,
174
+ includeFilesystem,
175
+ includeAgents,
176
+ includeTools,
177
+ includeMcp,
178
+ modelDirs,
179
+ verbose,
180
+ copy,
181
+ guess,
182
+ anonymousPayload,
183
+ shareConsent,
184
+ truth,
185
+ results,
186
+ port,
187
+ };
188
+ }
189
+ async function installPersistentMacApp(options = {}) {
190
+ if (process.platform !== 'darwin' || process.arch !== 'arm64')
191
+ throw new Error('The persistent GUI currently supports Apple Silicon macOS only.');
192
+ const source = node_path_1.default.join(__dirname, 'bootstrap', 'Forkit AI Footprint.app');
193
+ try {
194
+ await promises_1.default.access(node_path_1.default.join(source, 'Contents', 'Info.plist'));
195
+ }
196
+ catch {
197
+ throw new Error('This package does not contain the macOS GUI bootstrap. Install from the packed release candidate.');
198
+ }
199
+ const applicationsDirectory = options.applicationsDirectory
200
+ ?? process.env.FORKIT_AI_FOOTPRINTS_APPLICATIONS_DIR
201
+ ?? node_path_1.default.join(node_os_1.default.homedir(), 'Applications');
202
+ const destination = node_path_1.default.join(applicationsDirectory, 'Forkit AI Footprint.app');
203
+ const staging = node_path_1.default.join(applicationsDirectory, `.Forkit AI Footprint.installing-${process.pid}.app`);
204
+ const previous = node_path_1.default.join(applicationsDirectory, `.Forkit AI Footprint.previous-${process.pid}.app`);
205
+ await promises_1.default.mkdir(applicationsDirectory, { recursive: true, mode: 0o700 });
206
+ await promises_1.default.rm(staging, { recursive: true, force: true });
207
+ await promises_1.default.cp(source, staging, { recursive: true, preserveTimestamps: true });
208
+ let hadPrevious = false;
209
+ try {
210
+ const existingInfo = await promises_1.default.readFile(node_path_1.default.join(destination, 'Contents', 'Info.plist'), 'utf8').catch(() => '');
211
+ if (existingInfo && !existingInfo.includes('dev.forkit.ai-footprints'))
212
+ throw new Error('The destination contains a different application.');
213
+ if (existingInfo) {
214
+ await promises_1.default.rename(destination, previous);
215
+ hadPrevious = true;
216
+ }
217
+ await promises_1.default.rename(staging, destination);
218
+ if (hadPrevious)
219
+ await promises_1.default.rm(previous, { recursive: true, force: true });
220
+ }
221
+ catch (error) {
222
+ await promises_1.default.rm(staging, { recursive: true, force: true });
223
+ if (hadPrevious)
224
+ await promises_1.default.rename(previous, destination).catch(() => undefined);
225
+ throw error;
226
+ }
227
+ if (options.open !== false && process.env.FORKIT_AI_FOOTPRINTS_NO_OPEN !== '1') {
228
+ const launched = (0, node_child_process_1.spawnSync)('/usr/bin/open', [destination], { stdio: 'ignore', shell: false });
229
+ if (launched.status !== 0)
230
+ throw new Error('Forkit was installed, but macOS could not open it.');
231
+ }
232
+ return destination;
233
+ }
234
+ async function emit(output, filePath) {
235
+ process.stdout.write(output);
236
+ if (filePath)
237
+ await promises_1.default.writeFile(filePath, output, { encoding: 'utf8', flag: 'w' });
238
+ }
239
+ function clipboardCommands(platform) {
240
+ if (platform === 'darwin')
241
+ return [{ command: 'pbcopy', args: [] }];
242
+ if (platform === 'win32')
243
+ return [{ command: 'clip', args: [] }];
244
+ return [
245
+ { command: 'wl-copy', args: [] },
246
+ { command: 'xclip', args: ['-selection', 'clipboard'] },
247
+ ];
248
+ }
249
+ function copyToClipboard(value) {
250
+ for (const invocation of clipboardCommands(process.platform)) {
251
+ const result = (0, node_child_process_1.spawnSync)(invocation.command, invocation.args, {
252
+ input: value,
253
+ encoding: 'utf8',
254
+ stdio: ['pipe', 'ignore', 'ignore'],
255
+ shell: false,
256
+ });
257
+ if (result.status === 0)
258
+ return true;
259
+ }
260
+ return false;
261
+ }
262
+ async function main(args = process.argv.slice(2)) {
263
+ let options;
264
+ try {
265
+ options = parseArgs(args);
266
+ }
267
+ catch (error) {
268
+ process.stderr.write(`${error instanceof Error ? error.message : 'Invalid arguments'}\n\n${HELP}`);
269
+ return 2;
270
+ }
271
+ if (options.command === 'help') {
272
+ process.stdout.write(HELP);
273
+ return 0;
274
+ }
275
+ if (options.command === 'version') {
276
+ process.stdout.write(`${version_1.PRODUCT_VERSION}\n`);
277
+ return 0;
278
+ }
279
+ if (options.command === 'install') {
280
+ 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.\nYour AI activity stays on this device by default. After seeing your results, you can optionally review aggregate measurements for global comparison.\n`);
282
+ return 0;
283
+ }
284
+ if (options.command === 'serve') {
285
+ const service = await (0, server_1.startAiFootprintsServer)({ port: options.port });
286
+ process.stdout.write(`Forkit AI Footprints is ready at ${service.url}\nMetadata stays on this device. Press Ctrl+C to stop.\n`);
287
+ if (process.platform === 'darwin' && process.env.FORKIT_AI_FOOTPRINTS_NO_OPEN !== '1'
288
+ && process.env.FORKIT_AI_FOOTPRINTS_APP_BUNDLE !== '1') {
289
+ (0, node_child_process_1.spawnSync)('open', [service.url], { stdio: 'ignore', shell: false });
290
+ }
291
+ await new Promise((resolve) => {
292
+ const stop = () => { void service.close().finally(resolve); };
293
+ process.once('SIGINT', stop);
294
+ process.once('SIGTERM', stop);
295
+ });
296
+ return 0;
297
+ }
298
+ if (options.command === 'monitor') {
299
+ const monitor = new monitor_1.ActivityMonitor();
300
+ await monitor.start();
301
+ process.stderr.write('Monitoring locally until Ctrl+C. Process IDs and commands are not emitted.\n');
302
+ await new Promise((resolve) => {
303
+ const stop = () => resolve();
304
+ process.once('SIGINT', stop);
305
+ process.once('SIGTERM', stop);
306
+ });
307
+ const summary = monitor.stop();
308
+ const rendered = options.json
309
+ ? `${JSON.stringify(summary, null, 2)}\n`
310
+ : `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
+ await emit(rendered, options.output);
312
+ if (options.anonymousPayload) {
313
+ process.stderr.write('Anonymous preview requires a completed 10-minute GUI session with the exact payload reviewed first. Nothing was uploaded.\n');
314
+ return 2;
315
+ }
316
+ return 0;
317
+ }
318
+ if (options.command === 'doctor') {
319
+ const report = await (0, doctor_1.runDoctor)();
320
+ const rendered = options.json
321
+ ? `${JSON.stringify(report, null, 2)}\n`
322
+ : (0, format_1.formatDoctorReport)(report);
323
+ await emit(rendered, options.output);
324
+ return report.ok ? 0 : 1;
325
+ }
326
+ if (options.command === 'evaluate') {
327
+ if (!options.truth) {
328
+ process.stderr.write('evaluate requires --truth with a local labelled macOS truth file.\n');
329
+ return 2;
330
+ }
331
+ const evaluation = await (0, evaluate_1.evaluateMacosFieldTruthFile)(options.truth);
332
+ await emit(`${JSON.stringify(evaluation, null, 2)}\n`, options.output);
333
+ return 0;
334
+ }
335
+ if (options.command === 'aggregate') {
336
+ if (!options.results) {
337
+ process.stderr.write('aggregate requires --results with a directory of local evaluation JSON files.\n');
338
+ return 2;
339
+ }
340
+ const aggregate = await (0, aggregate_1.aggregateMacosFieldEvaluationDirectory)(options.results);
341
+ await emit(`${JSON.stringify(aggregate, null, 2)}\n`, options.output);
342
+ return 0;
343
+ }
344
+ if (options.command === 'share-page') {
345
+ if (!options.output) {
346
+ process.stderr.write('share-page requires --output with a local HTML file path.\n');
347
+ return 2;
348
+ }
349
+ const report = await (0, census_1.runCensus)({
350
+ includeRuntimes: options.includeRuntimes,
351
+ includeFilesystem: options.includeFilesystem,
352
+ includeAgents: options.includeAgents,
353
+ includeTools: options.includeTools,
354
+ includeMcp: options.includeMcp,
355
+ guess: options.guess,
356
+ ...(options.modelDirs.length > 0 ? { filesystemRoots: options.modelDirs } : {}),
357
+ });
358
+ if (process.env.FORKIT_AI_FOOTPRINTS_DISABLE_JOURNAL !== '1') {
359
+ await (0, local_device_1.recordLocalScan)({ now: () => new Date(report.generated_at) });
360
+ report.privacy.local_state_written = true;
361
+ report.privacy.local_state_scope = 'device-journal-only';
362
+ }
363
+ await promises_1.default.writeFile(options.output, (0, share_page_1.renderCensusSharePage)(report), { encoding: 'utf8', flag: 'w' });
364
+ process.stdout.write('Local aggregate share page written. No data was uploaded.\n');
365
+ return 0;
366
+ }
367
+ const report = await (0, census_1.runCensus)({
368
+ includeRuntimes: options.includeRuntimes,
369
+ includeFilesystem: options.includeFilesystem,
370
+ includeAgents: options.includeAgents,
371
+ includeTools: options.includeTools,
372
+ includeMcp: options.includeMcp,
373
+ guess: options.guess,
374
+ ...(options.modelDirs.length > 0 ? { filesystemRoots: options.modelDirs } : {}),
375
+ });
376
+ if (process.env.FORKIT_AI_FOOTPRINTS_DISABLE_JOURNAL !== '1') {
377
+ await (0, local_device_1.recordLocalScan)({ now: () => new Date(report.generated_at) });
378
+ report.privacy.local_state_written = true;
379
+ report.privacy.local_state_scope = 'device-journal-only';
380
+ }
381
+ const rendered = options.json
382
+ ? `${JSON.stringify(report, null, 2)}\n`
383
+ : (0, format_1.formatCensusReport)(report, { verbose: options.verbose });
384
+ await emit(rendered, options.output);
385
+ if (options.copy && !copyToClipboard(rendered)) {
386
+ process.stderr.write('Clipboard command unavailable; the result remains printable and can be copied from the terminal.\n');
387
+ }
388
+ if (options.anonymousPayload) {
389
+ process.stderr.write(options.shareConsent
390
+ ? 'Anonymous contribution was not prepared: a valid 10-minute monitor session and exact payload review are required. Nothing was uploaded.\n'
391
+ : 'Anonymous contribution was not prepared: separate --consent-share is required. Nothing was uploaded.\n');
392
+ return 2;
393
+ }
394
+ return 0;
395
+ }
396
+ if (require.main === module) {
397
+ void main().then((code) => {
398
+ process.exitCode = code;
399
+ }).catch(() => {
400
+ process.stderr.write('Forkit AI Footprints failed safely. No raw error details were printed.\n');
401
+ process.exitCode = 1;
402
+ });
403
+ }
404
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,14 @@
1
+ import { PRODUCT_NAME } from './version';
2
+ export interface DoctorCheck {
3
+ name: string;
4
+ ok: boolean;
5
+ detail: string;
6
+ }
7
+ export interface DoctorReport {
8
+ product: typeof PRODUCT_NAME;
9
+ version: string;
10
+ ok: boolean;
11
+ checks: DoctorCheck[];
12
+ }
13
+ export declare function runDoctor(): Promise<DoctorReport>;
14
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1,71 @@
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.runDoctor = runDoctor;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const agents_1 = require("./agents");
9
+ const endpoints_1 = require("./endpoints");
10
+ const filesystem_1 = require("./filesystem");
11
+ const version_1 = require("./version");
12
+ async function runDoctor() {
13
+ const nodeMajor = Number(process.versions.node.split('.')[0] ?? 0);
14
+ const supportedNode = [20, 22, 24].includes(nodeMajor);
15
+ const rawEndpoints = process.env.FORKIT_CENSUS_OPENAI_ENDPOINTS ?? 'http://localhost:8000';
16
+ const endpointCandidates = rawEndpoints.split(/[\n,]/).map((entry) => entry.trim()).filter(Boolean);
17
+ const safeEndpoints = (0, endpoints_1.parseLoopbackEndpointList)(rawEndpoints);
18
+ let processInventoryOk = true;
19
+ try {
20
+ await (0, agents_1.listSystemProcesses)();
21
+ }
22
+ catch {
23
+ processInventoryOk = false;
24
+ }
25
+ const existingModelRoots = (0, filesystem_1.getDefaultModelRoots)().filter((root) => {
26
+ try {
27
+ return node_fs_1.default.statSync(root).isDirectory();
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }).length;
33
+ const checks = [
34
+ {
35
+ name: 'operating-system',
36
+ ok: process.platform === 'darwin',
37
+ detail: process.platform === 'darwin'
38
+ ? 'macOS is supported by this release candidate.'
39
+ : `This release candidate supports macOS only; detected ${process.platform}.`,
40
+ },
41
+ {
42
+ name: 'node-version',
43
+ ok: supportedNode,
44
+ detail: supportedNode ? `Node ${nodeMajor} is supported.` : `Node ${nodeMajor} is unsupported; use 20, 22, or 24.`,
45
+ },
46
+ {
47
+ name: 'process-inventory',
48
+ ok: processInventoryOk,
49
+ detail: processInventoryOk ? 'Process metadata can be enumerated.' : 'Process metadata cannot be enumerated.',
50
+ },
51
+ {
52
+ name: 'loopback-only-endpoints',
53
+ ok: safeEndpoints.length === endpointCandidates.length,
54
+ detail: safeEndpoints.length === endpointCandidates.length
55
+ ? `${safeEndpoints.length} configured endpoint(s) are loopback-only.`
56
+ : 'One or more configured endpoints were rejected because Census only allows loopback HTTP(S).',
57
+ },
58
+ {
59
+ name: 'model-roots',
60
+ ok: true,
61
+ detail: `${existingModelRoots} known model root(s) currently exist; zero is allowed.`,
62
+ },
63
+ ];
64
+ return {
65
+ product: version_1.PRODUCT_NAME,
66
+ version: version_1.PRODUCT_VERSION,
67
+ ok: checks.every((check) => check.ok),
68
+ checks,
69
+ };
70
+ }
71
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1,8 @@
1
+ export interface SafeEndpoint {
2
+ url: string;
3
+ display: string;
4
+ id: string;
5
+ }
6
+ export declare function parseLoopbackEndpoint(input: string): SafeEndpoint | null;
7
+ export declare function parseLoopbackEndpointList(input: string): SafeEndpoint[];
8
+ //# sourceMappingURL=endpoints.d.ts.map
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseLoopbackEndpoint = parseLoopbackEndpoint;
4
+ exports.parseLoopbackEndpointList = parseLoopbackEndpointList;
5
+ const hash_1 = require("./hash");
6
+ const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1', '[::1]']);
7
+ function parseLoopbackEndpoint(input) {
8
+ try {
9
+ const parsed = new URL(String(input || '').trim());
10
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
11
+ return null;
12
+ if (parsed.username || parsed.password)
13
+ return null;
14
+ if (!LOOPBACK_HOSTS.has(parsed.hostname))
15
+ return null;
16
+ parsed.hash = '';
17
+ parsed.search = '';
18
+ const displayPath = parsed.pathname === '/' ? '' : parsed.pathname.replace(/\/+$/, '');
19
+ parsed.pathname = displayPath;
20
+ const url = parsed.toString().replace(/\/$/, '');
21
+ const displayHost = parsed.hostname === '::1' ? '[::1]' : parsed.hostname;
22
+ const display = `${parsed.protocol}//${displayHost}${parsed.port ? `:${parsed.port}` : ''}${displayPath}`;
23
+ return {
24
+ url,
25
+ display,
26
+ id: (0, hash_1.stableId)('endpoint', display),
27
+ };
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ function parseLoopbackEndpointList(input) {
34
+ const seen = new Set();
35
+ const endpoints = [];
36
+ for (const candidate of String(input || '').split(/[\n,]/)) {
37
+ const endpoint = parseLoopbackEndpoint(candidate);
38
+ if (!endpoint || seen.has(endpoint.url))
39
+ continue;
40
+ seen.add(endpoint.url);
41
+ endpoints.push(endpoint);
42
+ }
43
+ return endpoints;
44
+ }
45
+ //# sourceMappingURL=endpoints.js.map
@@ -0,0 +1,44 @@
1
+ import type { CensusReport } from './types';
2
+ export interface MacosFieldTruth {
3
+ schema_version: '1.0';
4
+ expected: {
5
+ agent_signatures: string[];
6
+ tool_names: string[];
7
+ online_runtime_names: string[];
8
+ model_keys: string[];
9
+ mcp_clients: string[];
10
+ };
11
+ }
12
+ export interface SurfaceMetrics {
13
+ actual_count: number;
14
+ expected_count: number;
15
+ true_positive: number;
16
+ false_positive: number;
17
+ false_negative: number;
18
+ precision: number | null;
19
+ recall: number | null;
20
+ }
21
+ export interface MacosFieldEvaluation {
22
+ evaluation: 'macos-local-labelled-device';
23
+ schema_version: '1.0';
24
+ product_version: string;
25
+ environment: {
26
+ platform: 'darwin';
27
+ architecture: string;
28
+ macos_major: number | null;
29
+ node_major: number;
30
+ };
31
+ metrics: {
32
+ agents: SurfaceMetrics;
33
+ tools: SurfaceMetrics;
34
+ runtimes: SurfaceMetrics;
35
+ models: SurfaceMetrics;
36
+ mcp: SurfaceMetrics;
37
+ };
38
+ uploaded: false;
39
+ field_accuracy_claim_allowed: false;
40
+ }
41
+ export declare function detectMacosMajorVersion(): number | null;
42
+ export declare function evaluateMacosFieldTruth(truthInput: unknown, report: CensusReport, macosMajor?: number | null): MacosFieldEvaluation;
43
+ export declare function evaluateMacosFieldTruthFile(filePath: string): Promise<MacosFieldEvaluation>;
44
+ //# sourceMappingURL=evaluate.d.ts.map