forkit-ai-footprints 0.2.4 → 0.2.5
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/README.md +53 -7
- package/STATUS.md +4 -4
- package/dist/activity-page.js +6 -1
- package/dist/bootstrap/Forkit AI Footprint.app.zip +0 -0
- package/dist/locales.json +157 -123
- package/dist/localization.d.ts +157 -123
- package/dist/localization.js +8 -8
- package/docs/GLOBAL_DISTRIBUTION.md +3 -3
- package/docs/MACOS_TESTER_GUIDE.md +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
# Forkit AI Footprints
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
[](https://github.com/arpitasarker01/forkit-ai-footprints/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/forkit-ai-footprints)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
|
|
7
|
+
Forkit AI Footprints is an open-source, private-by-default, metadata-only view
|
|
8
|
+
of the models, runtimes, and AI agents present on a device. The public package
|
|
9
|
+
and command are `forkit-ai-footprints`.
|
|
10
|
+
|
|
11
|
+
[Product page](https://www.forkit.dev/ai-footprint) ·
|
|
12
|
+
[npm](https://www.npmjs.com/package/forkit-ai-footprints) ·
|
|
13
|
+
[Releases](https://github.com/arpitasarker01/forkit-ai-footprints/releases) ·
|
|
14
|
+
[Contributing](./CONTRIBUTING.md) · [Security](./SECURITY.md)
|
|
6
15
|
|
|
7
16
|
It is derived from the discovery lessons in Forkit Connect, but it has a
|
|
8
17
|
different safety boundary: AI Footprints does not authenticate to Forkit.dev and has
|
|
@@ -58,7 +67,7 @@ git clone https://github.com/arpitasarker01/forkit-ai-footprints.git
|
|
|
58
67
|
cd forkit-ai-footprints
|
|
59
68
|
npm ci
|
|
60
69
|
npm pack
|
|
61
|
-
npm install -g ./forkit-ai-footprints-0.2.
|
|
70
|
+
npm install -g ./forkit-ai-footprints-0.2.5.tgz
|
|
62
71
|
forkit-ai-footprints serve
|
|
63
72
|
```
|
|
64
73
|
|
|
@@ -188,9 +197,34 @@ CLI flags cannot bypass the required monitoring session. The local UI enables
|
|
|
188
197
|
an exact schema `2.0` aggregate preview only after 3,600 valid observed seconds;
|
|
189
198
|
the installed app asks with versioned consent whether the anonymous aggregate
|
|
190
199
|
may join the Global AI Preview. When allowed, the app sends one latest signed
|
|
191
|
-
aggregate after the one-hour gate and refreshes
|
|
192
|
-
|
|
193
|
-
|
|
200
|
+
aggregate after the one-hour eligibility gate and refreshes that same row when
|
|
201
|
+
new valid observation time is available. The server does not count refreshes as
|
|
202
|
+
new installations. The payload excludes app/model names, process counts,
|
|
203
|
+
CPU/RAM, chat, workspace, repository, device, guess, local scan, and account
|
|
204
|
+
identifiers.
|
|
205
|
+
|
|
206
|
+
### How the community Preview is calculated
|
|
207
|
+
|
|
208
|
+
- A **contributing installation** is an opted-in local installation that has
|
|
209
|
+
reached at least 3,600 valid observed seconds and successfully submitted a
|
|
210
|
+
signed aggregate. Installing or downloading the package alone does not count.
|
|
211
|
+
- Repeated syncs use the same private local contribution key and replace that
|
|
212
|
+
installation's previous aggregate. They do not increase the participant
|
|
213
|
+
total.
|
|
214
|
+
- The global AI-active rate is duration-weighted:
|
|
215
|
+
`sum(AI-active seconds) / sum(valid observed seconds)` across eligible latest
|
|
216
|
+
rows. A long observation therefore contributes proportionally more evidence
|
|
217
|
+
than a short observation.
|
|
218
|
+
- npm downloads are reported separately as registry download events. They are
|
|
219
|
+
not unique people, devices, installations, or consenting participants.
|
|
220
|
+
- Percentile placement stays unavailable until at least 30 eligible
|
|
221
|
+
installations. Regional output stays hidden until its privacy threshold is
|
|
222
|
+
met.
|
|
223
|
+
|
|
224
|
+
The service evaluates Preview eligibility once a minute. Network submissions
|
|
225
|
+
are locally throttled and retried when internet is available; this is not a
|
|
226
|
+
promise to upload once every 60 minutes. See [the privacy boundary](./PRIVACY.md)
|
|
227
|
+
for the exact allowlist and exclusions.
|
|
194
228
|
|
|
195
229
|
## Shared core API
|
|
196
230
|
|
|
@@ -294,6 +328,18 @@ Storage, runtime-freshness, resource-attribution, and Developer ID truth
|
|
|
294
328
|
boundaries are defined in
|
|
295
329
|
[`docs/MACOS_MEASUREMENT_FOUNDATION.md`](./docs/MACOS_MEASUREMENT_FOUNDATION.md).
|
|
296
330
|
|
|
331
|
+
## Contributing and security
|
|
332
|
+
|
|
333
|
+
Forkit AI Footprints is an MIT-licensed open-source client. Bug reports, focused
|
|
334
|
+
feature proposals, tests, accessibility and localization improvements, and
|
|
335
|
+
privacy-preserving detector fixes are welcome through GitHub issues and pull
|
|
336
|
+
requests. Maintainers review every accepted change; pull requests cannot publish
|
|
337
|
+
an npm release automatically.
|
|
338
|
+
|
|
339
|
+
Read [`CONTRIBUTING.md`](./CONTRIBUTING.md) before opening a pull request. Report
|
|
340
|
+
security or privacy vulnerabilities privately through the repository Security
|
|
341
|
+
tab as described in [`SECURITY.md`](./SECURITY.md), never in a public issue.
|
|
342
|
+
|
|
297
343
|
## Accuracy boundary
|
|
298
344
|
|
|
299
345
|
AI Footprints has no defensible global accuracy percentage yet. Runtime API evidence is
|
package/STATUS.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Forkit AI Footprints Status
|
|
2
2
|
|
|
3
|
-
Last updated: 2026-08-
|
|
3
|
+
Last updated: 2026-08-29
|
|
4
4
|
|
|
5
|
-
Version: `0.2.
|
|
5
|
+
Version: `0.2.5`
|
|
6
6
|
|
|
7
7
|
Production classification: `current` for Apple Silicon macOS
|
|
8
8
|
|
|
9
|
-
Release state: v0.2.
|
|
9
|
+
Release state: v0.2.5 npm-first community Preview is public. The npm
|
|
10
10
|
bootstrap installs a persistent unsigned local app; Developer ID signing and
|
|
11
11
|
notarization remain future work.
|
|
12
12
|
|
|
@@ -131,7 +131,7 @@ notarization remain future work.
|
|
|
131
131
|
|
|
132
132
|
## Validation completed locally
|
|
133
133
|
|
|
134
|
-
- TypeScript build and the complete local test suite: **
|
|
134
|
+
- TypeScript build and the complete local test suite: **135/135** tests passed.
|
|
135
135
|
- Deterministic active → idle/locked → resumed lifecycle coverage confirms that
|
|
136
136
|
paused time changes neither observed nor AI-active seconds and cannot bridge
|
|
137
137
|
the longest continuous activity block.
|
package/dist/activity-page.js
CHANGED
|
@@ -38,13 +38,14 @@ body{font-size:var(--ui-md);line-height:1.45}.brand-copy strong,.inspector-head
|
|
|
38
38
|
@media(max-width:1320px){.workspace{grid-template-columns:minmax(210px,.28fr) minmax(0,1fr)}.workspace>.system-panel{grid-column:1;grid-row:1}.workspace>.explorer{grid-column:2;grid-row:1}.workspace>.insight-rail{grid-column:1/-1;grid-row:2;max-height:none}.observation-tools{grid-template-columns:repeat(4,1fr)}.below-timeline{grid-template-columns:1fr 1fr}.observation-metrics{grid-column:1/-1}.below-metrics{grid-template-columns:repeat(3,1fr)}}
|
|
39
39
|
@media(max-width:760px){.workspace{grid-template-columns:1fr}.workspace>.system-panel,.workspace>.explorer,.workspace>.insight-rail{grid-column:1;grid-row:auto;max-height:none}.workspace>.explorer{order:-1}.below-timeline{grid-template-columns:1fr}.observation-metrics{grid-column:1}.below-metrics{grid-template-columns:1fr 1fr}.timeline-axis,.timeline-row{grid-template-columns:82px minmax(0,1fr)}}
|
|
40
40
|
@media(max-width:760px){.timeline-tick:not(:first-child):not(:last-child){display:none}}
|
|
41
|
+
.footprint-strip.global-contribution{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:18px;border-left:4px solid var(--accent-system,var(--brand));background:linear-gradient(110deg,color-mix(in srgb,var(--brand-soft) 45%,var(--paper)),var(--paper) 58%)}.global-contribution-copy{min-width:0}.global-contribution-copy>strong{display:block;font-size:var(--ui-lg);letter-spacing:-.02em}.global-contribution-copy>span{display:block;margin-top:3px;color:var(--muted);font-size:var(--ui-xs);line-height:1.4}.eligibility-meter{height:7px;margin-top:9px;overflow:hidden;border-radius:999px;background:var(--surface-2)}.eligibility-meter i{display:block;width:0;height:100%;border-radius:inherit;background:var(--accent-system,var(--brand));transition:width .35s ease}.global-meta{display:flex;justify-content:space-between;gap:10px;margin-top:6px;color:var(--muted);font-size:var(--ui-xs)}.global-action{flex-wrap:wrap;justify-content:flex-end}.global-action .button{white-space:nowrap}.observation-metrics{display:none!important}.monitor-line{display:none}@media(max-width:760px){.footprint-strip.global-contribution{grid-template-columns:1fr}.global-action{justify-content:flex-start}.global-meta{align-items:flex-start;flex-direction:column}}
|
|
41
42
|
@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}
|
|
42
43
|
</style></head><body><main class="shell">
|
|
43
44
|
<header class="surface command"><div class="brand">${brand}<div class="brand-copy"><strong>Forkit AI Footprint</strong><span class="status stopped" id="monitor-status"><i class="status-dot"></i><span id="monitor-status-text">${t('stopped')}</span></span></div></div><div class="command-actions"><span class="privacy-chip"><b>${t('localOnly')}</b> · ${t('nothingUploaded')}</span><div class="locale" aria-label="Language"><button id="locale-en" class="${locale === 'en' ? 'active' : ''}" type="button">EN</button><button id="locale-de" class="${locale === 'de' ? 'active' : ''}" type="button">DE</button></div><button class="button primary" id="share-button" type="button">${t('share')}</button>${rescan ? `<button class="button" id="monitor-button" type="button">${t('startMonitoring')}</button><details class="more"><summary>${t('more')}</summary><div class="more-menu"><button class="button" id="clear-button" type="button">${t('clearHistory')}</button><button class="button" id="scan-button" type="button">${t('scanAgain')}</button><span class="scan-status" id="scan-status"></span></div></details>` : ''}</div></header>
|
|
44
45
|
<section class="surface metric-rail" aria-label="${t('scorecard')}"><div class="headline"><span class="metric-label">${t('activityRatio')}</span><strong class="metric-value" id="metric-ratio">—</strong><span class="metric-sub" id="metric-active">0s</span></div><div class="headline"><span class="metric-label">${t('activeAiTools')}</span><strong class="metric-value" id="metric-systems">0</strong><span class="metric-sub" id="ratio-label">${t('earlyObservation')}</span></div><div class="headline"><span class="metric-label">${t('localAi')}</span><strong class="metric-value" id="metric-local">${snapshot.model_record_count} · ${snapshot.model_storage_decimal_display}</strong><span class="metric-sub" id="metric-local-running">${snapshot.confirmed_running_model_count} ${t('runningShort')}</span></div></section>
|
|
45
46
|
<section class="workspace"><article class="surface explorer"><div class="panel-head"><div><p class="eyebrow" id="explorer-eyebrow">${t('currentSession')}</p><h1 id="explorer-title">${t('activityExplorer')}</h1><p class="panel-copy" id="explorer-copy">${t('activityExplorerCopy')}</p></div><div class="chart-controls"><div class="resource-modes" aria-label="${t('resourcePulse')}"><button class="active" data-resource-mode="both" type="button">${t('both')}</button><button data-resource-mode="cpu" type="button">${t('cpu')}</button><button data-resource-mode="memory" type="button">${t('memory')}</button></div><button class="icon-button" id="zoom-out" type="button" title="${t('zoomOut')}" aria-label="${t('zoomOut')}">−</button><button class="icon-button" id="zoom-in" type="button" title="${t('zoomIn')}" aria-label="${t('zoomIn')}">+</button><button class="icon-button" id="reset-view" type="button">${t('resetView')}</button></div></div><div class="temporal"><div class="temporal-main"><details class="date-picker" id="date-picker"><summary><span>${t('dateHistory')}</span> <strong id="observation-date">—</strong></summary><div class="date-popover" id="date-options"></div></details><span id="observation-span"></span></div><span id="resource-history-state">${t('resourceCollecting')}</span></div><section class="focus-summary" id="focus-summary" aria-live="polite"><div class="focus-copy"><strong id="focus-title">${t('pulseDetailTitle')}</strong><span id="focus-copy">${t('pulseDetailCopy')}</span></div><div class="focus-stats" id="focus-stats"></div></section><div class="chart-wrap"><div class="system-timeline" id="system-timeline" aria-label="${t('mainChartDescription')}"><div class="timeline-empty">${t('earlyObservationCopy')}</div></div><canvas class="chart-canvas legacy-timeline" id="activity-explorer" tabindex="0" role="img" aria-label="${t('mainChartDescription')}"></canvas><canvas class="navigator" id="activity-navigator" aria-hidden="true"></canvas><div class="chart-tooltip" id="chart-tooltip" hidden></div></div><div class="chart-help"><div class="legend"><span><i class="active"></i>${t('working')}</span><span><i></i>${t('idleTimeLabel')}</span><span><i class="excluded"></i>${t('excluded')}</span></div><span>${t('timelineHelp')}</span></div><section class="below-timeline"><article class="below-card observation-metrics" data-center-view="pulse"><h2>${t('observationMetrics')}</h2><div class="below-metrics"><div class="below-stat"><span>${t('observed')}</span><strong id="below-observed">0s</strong></div><div class="below-stat"><span>${t('aiActive')}</span><strong id="below-active">0s</strong></div><div class="below-stat"><span>${t('activityRatio')}</span><strong id="below-ratio">—</strong></div><div class="below-stat"><span>${t('activityPeriods')}</span><strong id="below-periods">0</strong></div><div class="below-stat"><span>${t('longestActive')}</span><strong id="below-longest">0s</strong></div><div class="below-stat"><span>${t('systems')}</span><strong id="below-systems">0</strong></div></div></article><article class="below-card" data-center-view="runtime"><h2>${t('runtimeFootprint')}</h2><div class="runtime-footprint-list" id="runtime-footprint-list"></div></article><article class="below-card" data-center-view="resources"><h2>${t('liveResourceFootprint')}</h2><div class="resource-summary"><div class="below-stat"><span>${t('cpu')}</span><strong id="below-cpu">—</strong></div><div class="below-stat"><span>${t('memory')}</span><strong id="below-memory">—</strong></div><div class="below-stat"><span>${t('gpu')}</span><strong>${t('notMeasured')}</strong></div><div class="below-stat"><span>${t('resourcePulse')}</span><strong id="resource-history-label">${t('resourceCollecting')}</strong></div></div><h3 class="resource-history-title">${t('resourceHistory')}</h3><div class="resource-history-list" id="center-resource-history"></div><p class="truth-note" id="resource-history-note">${t('resourceCollecting')}</p></article><article class="below-card" data-center-view="apps"><h2>${t('appsDetailTitle')}</h2><div class="center-app-list" id="center-apps-list"></div></article><article class="below-card" data-center-view="pulse"><h2>${t('localAiFootprint')}</h2><div class="local-summary"><div class="below-stat"><span>${t('localRuntimes')}</span><strong id="below-runtimes">0</strong></div><div class="below-stat"><span>${t('modelRecordsShort')}</span><strong id="below-models">${snapshot.model_record_count}</strong></div><div class="below-stat"><span>${t('hostedModelsRunning')}</span><strong id="below-running">${snapshot.confirmed_running_model_count}</strong></div><div class="below-stat"><span>${t('recognizedModelSpace')}</span><strong id="below-space">${snapshot.model_storage_decimal_display}</strong></div></div><p class="statement" id="below-local-insight">${t('localAiNone')}</p></article></section></article>
|
|
46
47
|
<aside class="surface insight-rail"><div class="device-identity"><div><p class="eyebrow">${t('deviceIdentity')}</p><strong>${escapeHtml(device)}</strong></div><span class="observation-number" id="observation-identity">${t('observationIdentity', { number: '—' })}</span></div><nav class="observation-tools" aria-label="${t('observationTools')}"><button class="feature-tool" data-explorer-mode="pulse" data-tip="${t('inspectPulseHint')}" title="${t('inspectPulseHint')}" type="button" aria-pressed="true"><span class="feature-icon" aria-hidden="true">⌁</span><span>${t('pulseView')}</span></button><button class="feature-tool" data-explorer-mode="runtime" data-tip="${t('inspectRuntimeHint')}" title="${t('inspectRuntimeHint')}" type="button" aria-pressed="false"><span class="feature-icon" aria-hidden="true">◷</span><span>${t('runtimeView')}</span></button><button class="feature-tool" data-explorer-mode="resources" data-tip="${t('inspectResourcesHint')}" title="${t('inspectResourcesHint')}" type="button" aria-pressed="false"><span class="feature-icon" aria-hidden="true">◉</span><span>${t('resourcesView')}</span></button><button class="feature-tool" data-explorer-mode="apps" data-tip="${t('inspectAppsHint')}" title="${t('inspectAppsHint')}" type="button" aria-pressed="false"><span class="feature-icon" aria-hidden="true">▦</span><span>${t('appsView')}</span></button></nav><section class="rail-section" data-view="pulse"><div id="inspector-headline"><h2>${t('observationShows')}</h2><p class="insight-copy">${t('selectTimelineBlock')}</p></div><div class="selected-evidence" id="selected-evidence" aria-live="polite">${t('noSelection')}</div><div class="insight-list" id="insight-list"></div></section><section class="rail-section" data-view="runtime"><div class="rail-heading"><strong>${t('observationOverview')}</strong><span>${t('measuredCurrentOnly')}</span></div><div class="observation-grid"><div class="rail-stat"><span>${t('observed')}</span><strong id="rail-observed">0s</strong></div><div class="rail-stat"><span>${t('aiActive')}</span><strong id="rail-active">0s</strong></div><div class="rail-stat"><span>${t('activityRatio')}</span><strong id="rail-ratio">—</strong></div><div class="rail-stat"><span>${t('longestActive')}</span><strong id="rail-longest">0s</strong></div></div></section><section class="rail-section" data-view="resources"><div class="rail-heading"><strong>${t('resourcesNow')}</strong><span>${t('resourceLiveOnly')}</span></div><div class="resource-grid"><div class="rail-stat"><span>${t('cpu')}</span><strong id="rail-cpu">—</strong></div><div class="rail-stat"><span>${t('memory')}</span><strong id="rail-memory">—</strong></div><div class="rail-stat"><span>${t('gpu')}</span><strong>${t('notMeasured')}</strong></div><div class="rail-stat"><span>${t('recognizedModelSpace')}</span><strong id="rail-space">${snapshot.model_storage_decimal_display}</strong></div></div></section><section class="rail-section" data-view="runtime"><div class="rail-heading"><strong>${t('localRuntimes')}</strong><span>${t('localOnly')}</span></div><div class="rail-list" id="rail-runtimes"></div></section><section class="rail-section" data-view="apps"><div class="rail-heading"><strong>${t('appsView')}</strong><span>${t('measuredCurrentOnly')}</span></div><div class="inspector-app-list" id="inspector-apps-list"></div></section><section class="system-mix rail-section"><div class="rail-heading"><strong>${t('aiAppsOnDevice')}</strong></div><p class="insight-copy">${t('focusSystemHelp')}</p><div class="mix-bar" id="mix-bar"></div><div class="mix-list" id="mix-list"></div><div class="system-actions"><button class="button all-apps" id="all-apps" type="button">${t('allApps')}</button><button class="button" id="focus-system" type="button" disabled>${t('focusSystem')}</button></div><div class="app-inspector" id="app-inspector" hidden></div></section></aside></section>
|
|
47
|
-
<section class="surface footprint-strip"><div class="
|
|
48
|
+
<section class="surface footprint-strip global-contribution" aria-labelledby="global-status-title"><div class="global-contribution-copy"><p class="eyebrow">${t('globalContribution')} · ${t('notAvailableYet')}</p><strong id="global-status-title">${t('globalNotJoined')}</strong><span id="global-status-copy">${t('globalNotJoinedCopy')}</span><div class="eligibility-meter" role="progressbar" aria-valuemin="0" aria-valuemax="3600" aria-valuenow="0" id="global-progress"><i id="global-progress-fill"></i></div><div class="global-meta"><span id="global-valid-progress">${t('globalValidProgress', { current: '0s', required: '1h' })}</span><span id="global-last-sync">${t('globalNeverSynced')}</span><span>${t('globalDownloadsNote')}</span></div><span class="scan-status" id="compare-progress"></span><span class="scan-status" id="global-result"></span></div><div class="global-action">${rescan ? `<button class="button" id="global-permission-button" type="button">${t('reviewPermission')}</button><button class="button primary" id="review-payload" type="button">${t('openGlobal')}</button>` : ''}</div><div class="local-strip" hidden><span id="models-found">${snapshot.model_record_count}</span><span id="device-storage">${snapshot.model_storage_decimal_display}</span><span id="models-running">${snapshot.confirmed_running_model_count}</span><span id="hosted-insight">${t('localAiNone')}</span></div></section>
|
|
48
49
|
<div class="monitor-line"><span>${t('monitorOverhead')} · <span id="overhead">${t('warmingUp')}</span> · <span id="overhead-note"></span></span><span>${t('validObservation')} <b id="metric-observed">0s</b> · ${t('longestActive')} <b id="metric-longest">0s</b></span></div>
|
|
49
50
|
<details class="surface advanced"><summary>${t('technical')}</summary><div class="details-grid"><article class="detail-box"><h3>${t('engines')}</h3><ul class="detail-list" id="runtime-list"></ul></article><article class="detail-box"><h3>${t('modelsDevice')}</h3><ul class="detail-list" id="model-list"></ul></article><article class="detail-box"><h3>${t('appsTools')}</h3><ul class="detail-list" id="app-list"></ul></article><article class="detail-box"><h3>${t('measurementBoundary')}</h3><ul class="detail-list"><li><strong>${t('activityMeasurement')}</strong>${t('workingBoundary')}</li><li><strong>${t('loadedModel')}</strong>${t('loadedBoundary')}</li><li><strong>${t('storage')}</strong>${t('storageBoundary')}</li><li><strong>${t('resourcePulse')}</strong>${t('resourceLiveOnly')}</li><li><strong>${t('systems')}</strong><span id="raw-system-count">0</span></li></ul></article></div></details>
|
|
50
51
|
<footer class="footer"><span>${t('localDevice')} · ${t('nothingUploaded')}</span><span>${t('noPrivateFooter')} · v${snapshot.product_version}</span></footer></main>
|
|
@@ -83,6 +84,9 @@ function renderHostedLocal(){const products=monitor.products||[],workingLocal=pr
|
|
|
83
84
|
function renderRuntimes(){const container=$('rail-runtimes');container.replaceChildren();for(const runtime of footprint.local_details?.runtimes||[]){const row=document.createElement('button'),name=document.createElement('strong'),status=document.createElement('span');row.type='button';row.className='runtime-item';row.title=text('inspectRuntimeHint');name.textContent=runtime.name;status.textContent=runtime.loaded_model_count?text('modelsLoaded',{count:runtime.loaded_model_count,models:runtime.loaded_model_count===1?text('model'):text('models')}):text('verifiedAvailable');row.append(name,status);row.addEventListener('click',()=>setExplorerMode('runtime'));container.append(row)}if(!container.children.length){const note=document.createElement('div');note.className='early';note.textContent='—';container.append(note)}}function renderDetails(){if(!footprint.local_details)return;const details=footprint.local_details,runtimeList=$('runtime-list'),modelList=$('model-list'),appList=$('app-list');for(const node of [runtimeList,modelList,appList])node.replaceChildren();for(const runtime of details.runtimes)add(runtimeList,runtime.name,runtime.loaded_model_count?text('modelsLoaded',{count:runtime.loaded_model_count,models:runtime.loaded_model_count===1?text('model'):text('models')}):text('verifiedAvailable'));for(const model of details.models.filter(item=>item.loaded))add(modelList,model.name,text('loaded',{count:''}).trim());for(const product of monitor.products||[]){const note=[text(product.state==='working-now'?'working':'openIdleLegend'),format.format(product.process_count)+' '+text(product.process_count===1?'measuredProcess':'measuredProcesses'),product.memory_bytes?bytes(product.memory_bytes):''].filter(Boolean).join(' · ');add(appList,product.name,note)}if(!appList.children.length)for(const agent of details.agents)add(appList,agent.name,text('processActivity'));if($('raw-system-count'))$('raw-system-count').textContent=format.format((monitor.products||[]).length);renderRuntimes()}
|
|
84
85
|
function saveLastSummary(){if(!monitor.observed_seconds)return;const dominant=dominantProduct();try{localStorage.setItem(lastSummaryKey,JSON.stringify({activity_ratio:monitor.activity_ratio,active_tool:dominant?.name||null,local_models_found:footprint.model_record_count||0,local_models_running:footprint.confirmed_running_model_count||0}))}catch{}}
|
|
85
86
|
function renderMonitor(){const products=monitor.products||[],working=userFacingProducts().filter(product=>product.state==='working-now'),presence=monitor.presence||{state:'active',observation_eligible:true},status=$('monitor-status');status.className='status';let statusText;if(monitor.lifecycle==='stopped'){status.classList.add('stopped');statusText=text('stopped')}else if(!presence.observation_eligible){status.classList.add('paused');statusText=text('paused')}else if(working.length)statusText=text('menuWorking',{count:working.length});else statusText=text('menuMonitoring');$('monitor-status-text').textContent=statusText;if($('monitor-button'))$('monitor-button').textContent=monitor.lifecycle==='monitoring'?text('stopMonitoring'):monitor.observed_seconds?text('startMonitoringAgain'):text('startMonitoring');appendResourceSample();renderDates();const view=currentView()||{segments:[],contributions:[],systems:[],insights:[],longest_active_seconds:0},totals=selectedDateKey||focusedSystemSignature?viewTotals(view):{active:monitor.active_seconds,observed:monitor.observed_seconds,ratio:monitor.activity_ratio},resources=aggregateResources(products),historical=selectedDateKey&&selectedDateKey!==localDateKey(monitor.activity_view?.extent?.end_ms||Date.now()),live=!historical&&monitor.lifecycle==='monitoring',dominant=dominantProduct();if(dominant)document.documentElement.style.setProperty('--accent-system',systemColor(dominant));$('metric-ratio').textContent=pct(totals.ratio);$('ratio-label').textContent=totals.observed<60?text('earlyObservation'):totals.ratio>=.7?text('mostlyActive'):totals.ratio<=.3?text('mostlyIdle'):text('mixedActivity');$('metric-active').textContent=seconds(totals.active);$('metric-observed').textContent=seconds(totals.observed);$('metric-systems').textContent=format.format((view.systems||[]).filter(system=>system.active_seconds>0||system.state==='working-now').length);$('metric-longest').textContent=seconds(view.longest_active_seconds||0);$('observation-identity').textContent=text('observationIdentity',{number:monitor.observation_id??'—'});$('rail-observed').textContent=seconds(totals.observed);$('rail-active').textContent=seconds(totals.active);$('rail-ratio').textContent=pct(totals.ratio);$('rail-longest').textContent=seconds(view.longest_active_seconds||0);$('rail-cpu').textContent=live&&Number.isFinite(resources.cpu)?format.format(Number(resources.cpu.toFixed(1)))+'%':'—';$('rail-memory').textContent=live&&Number.isFinite(resources.memory)?bytes(resources.memory):'—';$('rail-space').textContent=bytes(footprint.model_storage_bytes||0);explorer.setData(view);renderInsights();renderMix();renderHostedLocal();renderDetails();renderInspector();renderFocusSummary();renderSystemTimeline();renderInspectorApps();renderBelowSummary();if($('overhead')){$('overhead').textContent=monitor.overhead?.current_cpu_percent===null?text('warmingUp'):format.format(Number(monitor.overhead.current_cpu_percent.toFixed(1)))+'% CPU';$('overhead-note').textContent=bytes(monitor.overhead?.current_memory_bytes||0)+' RAM'}if($('review-payload')){const eligible=monitor.observed_seconds>=3600;$('review-payload').disabled=!eligible;$('compare-progress').textContent=eligible?'':text('globalProgress',{remaining:seconds(Math.max(0,3600-monitor.observed_seconds))});const preview=monitor.global_preview;$('global-result').textContent=preview?.state==='contributed'?text('previewContributed'):preview?.state==='unavailable'?text('previewUnavailable'):monitor.global_permission==='granted'?text('previewWaiting'):text('permissionNeeded')}saveLastSummary()}
|
|
87
|
+
function renderGlobalContribution(){if(!$('global-status-title'))return;const observed=Math.max(0,Number(monitor.observed_seconds)||0),eligible=observed>=3600,permission=monitor.global_permission==='granted',preview=monitor.global_preview||{},progress=Math.min(100,(observed/3600)*100),lastSuccess=preview.last_success_at?new Date(preview.last_success_at):null;let title,copyText;if(!permission){title=text('globalNotJoined');copyText=text('globalNotJoinedCopy')}else if(preview.state==='contributed'){title=text('globalQualified');copyText=text('globalQualifiedCopy')}else if(preview.state==='unavailable'){title=eligible?text('globalRetry'):text('globalBuilding');copyText=eligible?text('globalRetryCopy'):text('globalBuildingCopy')}else if(eligible){title=text('globalSyncing');copyText=text('globalSyncingCopy')}else{title=text('globalBuilding');copyText=text('globalBuildingCopy')}$('global-status-title').textContent=title;$('global-status-copy').textContent=copyText;$('global-progress').setAttribute('aria-valuenow',String(Math.min(3600,Math.round(observed))));$('global-progress-fill').style.width=progress+'%';$('global-valid-progress').textContent=text('globalValidProgress',{current:seconds(Math.min(observed,3600)),required:seconds(3600)});$('global-last-sync').textContent=lastSuccess&&!Number.isNaN(lastSuccess.getTime())?text('globalLastSync',{time:shareDateLabel.format(lastSuccess)+' '+clock.format(lastSuccess)}):text('globalNeverSynced');$('compare-progress').textContent=permission&&!eligible?text('globalProgress',{remaining:seconds(Math.max(0,3600-observed))}):'';$('global-result').textContent=preview.state==='contributed'?text('previewContributed'):preview.state==='unavailable'?text('previewUnavailable'):permission?text('previewWaiting'):text('permissionNeeded')}
|
|
88
|
+
const renderMonitorContributionBase=renderMonitor;renderMonitor=function(){renderMonitorContributionBase();renderGlobalContribution()}
|
|
89
|
+
const renderGlobalContributionAccessBase=renderGlobalContribution;renderGlobalContribution=function(){renderGlobalContributionAccessBase();if($('review-payload'))$('review-payload').disabled=false;const last=monitor.global_preview?.last_success_at?new Date(monitor.global_preview.last_success_at):null;if(last&&!Number.isNaN(last.getTime()))$('global-last-sync').textContent=text('globalLastSync',{time:fullMoment.format(last)})}
|
|
86
90
|
function shareObservationWindow(){const extent=monitor.activity_view?.extent;if(!extent)return text('shareMeasuredLocally');const start=new Date(extent.start_ms),end=new Date(Math.max(extent.start_ms,extent.end_ms-1));return shareDateLabel.format(start)+' '+clock.format(start)+' – '+shareDateLabel.format(end)+' '+clock.format(end)}
|
|
87
91
|
function primaryInsight(){return monitor.insights?.[0]||text('activeShareFallback',{ratio:pct(monitor.activity_ratio)})}function shareInsightLine(){const tools=activeToolNames();if(tools.length===1)return text('shareFocusedLine');if(tools.length>1)return text('shareMultiLine',{count:format.format(tools.length)});if(footprint.model_record_count)return text('shareModelReserveLine');return primaryInsight()}function shareDurationLine(){return monitor.observed_seconds?text('shareMeasuredDurations',{observed:seconds(monitor.observed_seconds),active:seconds(monitor.active_seconds||0)}):''}function shareCaption(){const tools=activeToolNames(),running=footprint.confirmed_running_model_count||0,details=[text('shareActivity',{ratio:pct(monitor.activity_ratio)}),shareDurationLine(),tools.length?text('shareTool',{tools:list(tools)}):'',text('shareModels',{count:footprint.model_record_count,models:footprint.model_record_count===1?text('model'):text('models'),running:format.format(running)})].filter(Boolean).join(' ');return text('captionFirst',{ratio:pct(monitor.activity_ratio)})+'\\n\\n'+details+'\\n\\n'+text('captionQuestion')+'\\n\\nforkit.dev/ai-footprint'}
|
|
88
92
|
function wrap(x,value,left,top,maxWidth,lineHeight,maxLines){const words=String(value||'').split(' '),lines=[];let line='';for(const word of words){const next=line?line+' '+word:word;if(x.measureText(next).width>maxWidth&&line){lines.push(line);line=word}else line=next}if(line)lines.push(line);for(let index=0;index<Math.min(lines.length,maxLines);index++)x.fillText(lines[index],left,top+index*lineHeight)}
|
|
@@ -91,6 +95,7 @@ const drawShareBase=drawShare;drawShare=async function(){await drawShareBase();c
|
|
|
91
95
|
const dialog=$('share-dialog');async function openShare(){$('share-heading').textContent=shareInsightLine();await drawShare();dialog.showModal()}$('share-button').addEventListener('click',openShare);$('close-share').addEventListener('click',()=>dialog.close());async function blob(){await drawShare();return new Promise(resolve=>$('share-canvas').toBlob(resolve,'image/png'))}async function dataUrl(){const value=await blob();if(!value)return null;return new Promise(resolve=>{const reader=new FileReader();reader.onload=()=>resolve(reader.result);reader.onerror=()=>resolve(null);reader.readAsDataURL(value)})}function nativeAction(action,payload={}){const bridge=window.webkit?.messageHandlers?.forkitShare;if(!bridge)return false;bridge.postMessage({action,...payload});return true}window.forkitNativeShareResult=result=>{if(result.action==='open-global'){$('global-result').textContent=result.message||'';return}$('share-status').textContent=result.message||text('sharePrivacy');if(result.action==='copy-image')$('copy-image').textContent=text('copied');if(result.action==='copy-caption')$('copy-share').textContent=text('copied')};$('download-share').addEventListener('click',async()=>{const png=await dataUrl();if(!png)return;if(nativeAction('save',{png}))return;const value=await blob(),url=URL.createObjectURL(value),anchor=document.createElement('a');anchor.href=url;anchor.download='my-forkit-ai-footprint.png';anchor.click();setTimeout(()=>URL.revokeObjectURL(url),1000)});$('copy-share').addEventListener('click',async()=>{const caption=shareCaption();if(nativeAction('copy-caption',{caption}))return;await navigator.clipboard.writeText(caption);$('copy-share').textContent=text('copied')});$('copy-image').addEventListener('click',async()=>{const png=await dataUrl();if(!png)return;if(nativeAction('copy-image',{png}))return;const value=await blob();try{await navigator.clipboard.write([new ClipboardItem({'image/png':value})]);$('copy-image').textContent=text('copied')}catch{$('share-status').textContent=text('copyUnavailable')}});$('native-share').addEventListener('click',async()=>{const png=await dataUrl();if(!png)return;const caption=shareCaption();if(nativeAction('share',{png,caption}))return;const value=await blob(),file=new File([value],'my-forkit-ai-footprint.png',{type:'image/png'});if(navigator.share&&(!navigator.canShare||navigator.canShare({files:[file]})))await navigator.share({title:text('shareTitle'),text:caption,files:[file]});else await navigator.clipboard.writeText(caption)});
|
|
92
96
|
for(const button of document.querySelectorAll('[data-resource-mode]'))button.addEventListener('click',()=>{explorerMode='resources';syncModeButtons();resourceMode=button.dataset.resourceMode;for(const item of document.querySelectorAll('[data-resource-mode]'))item.classList.toggle('active',item===button);explorer.setResourceMode(resourceMode);renderFocusSummary()});for(const button of document.querySelectorAll('[data-explorer-mode]'))button.addEventListener('click',()=>setExplorerMode(button.dataset.explorerMode));for(const section of document.querySelectorAll('.rail-section[data-view]'))section.addEventListener('click',event=>{if(event.target.closest('button,select'))return;setExplorerMode(section.dataset.view)});$('zoom-in').addEventListener('click',()=>{explorer.zoom(.65,explorer.visibleRange().reduce((a,b)=>a+b)/2);renderSystemTimeline()});$('zoom-out').addEventListener('click',()=>{explorer.zoom(1.4,explorer.visibleRange().reduce((a,b)=>a+b)/2);renderSystemTimeline()});$('reset-view').addEventListener('click',()=>{explorer.reset();setExplorerMode('pulse',false);renderMonitor()});$('system-timeline').addEventListener('wheel',event=>{event.preventDefault();explorer.zoom(event.deltaY<0?.78:1.22,explorer.visibleRange().reduce((a,b)=>a+b)/2);renderSystemTimeline()},{passive:false});let timelineResizeFrame=0;new ResizeObserver(()=>{cancelAnimationFrame(timelineResizeFrame);timelineResizeFrame=requestAnimationFrame(renderSystemTimeline)}).observe($('system-timeline'));for(const next of ['en','de'])$('locale-'+next).addEventListener('click',()=>{localStorage.setItem('forkit-footprints-locale',next);const url=new URL(location.href);url.searchParams.set('lang',next);location.href=url.href});const preferred=localStorage.getItem('forkit-footprints-locale');if(preferred&&preferred!==locale){const url=new URL(location.href);url.searchParams.set('lang',preferred);location.replace(url.href)}
|
|
93
97
|
async function auth(endpoint,options={}){return fetch(endpoint,{method:'POST',headers:{'x-forkit-footprints-session':config.session_token,'content-type':'application/json'},...options})}
|
|
98
|
+
if(config&&$('global-permission-button'))$('global-permission-button').addEventListener('click',()=>{if(!nativeAction('global-permission'))$('global-result').textContent=text('permissionNativeOnly')})
|
|
94
99
|
if(config){void auth(config.locale_endpoint,{body:JSON.stringify({locale})});$('monitor-button').addEventListener('click',async()=>{const endpoint=monitor.lifecycle==='monitoring'?config.monitor_stop_endpoint:config.monitor_start_endpoint,response=await auth(endpoint);if(response.ok){monitor=await response.json();renderMonitor()}});$('clear-button').addEventListener('click',async()=>{if(!confirm(text('clearConfirm')))return;const response=await auth(config.monitor_clear_endpoint);if(response.ok){monitor=await response.json();renderMonitor()}});$('scan-button').addEventListener('click',async()=>{const button=$('scan-button');button.disabled=true;$('scan-status').textContent=text('scanning');try{const response=await auth(config.endpoint);if(!response.ok)throw new Error();footprint=await response.json();renderHostedLocal();renderDetails();$('scan-status').textContent=text('refreshed')}catch{$('scan-status').textContent=text('scanFailed')}finally{button.disabled=false}});$('review-payload').addEventListener('click',()=>{if(!nativeAction('open-global'))location.assign(globalUrl)});(async()=>{try{const response=await auth(config.monitor_stream_endpoint);if(!response.ok||!response.body)return;const reader=response.body.getReader(),decoder=new TextDecoder();let buffer='';while(true){const part=await reader.read();if(part.done)break;buffer+=decoder.decode(part.value,{stream:true});const lines=buffer.split('\\n');buffer=lines.pop()||'';for(const line of lines)if(line){monitor=JSON.parse(line);renderMonitor()}}}catch{}})()}
|
|
95
100
|
function appendSystemComparison(){const view=viewForDate(monitor.activity_view,selectedDateKey),selected=(view?.systems||[]).find(system=>system.signature===selectedSystemSignature),others=(view?.systems||[]).filter(system=>system.signature!==selectedSystemSignature&&system.active_seconds>0);if(!selected||!others.length)return;const container=document.createElement('div'),label=document.createElement('label'),select=document.createElement('select'),comparison=document.createElement('div');container.className='inspector';label.className='eyebrow';label.textContent=text('compareSystems');select.className='button';for(const system of others){const option=document.createElement('option');option.value=system.signature;option.textContent=system.name;select.append(option)}comparison.className='inspector-grid';const draw=()=>{comparison.replaceChildren();const other=(view.systems||[]).find(system=>system.signature===select.value);if(!other)return;appendInspectorStat(comparison,selected.name+' · '+text('activeDuration'),seconds(selected.active_seconds));appendInspectorStat(comparison,other.name+' · '+text('activeDuration'),seconds(other.active_seconds));appendInspectorStat(comparison,selected.name+' · '+text('contribution'),Math.round(selected.share*100)+'%');appendInspectorStat(comparison,other.name+' · '+text('contribution'),Math.round(other.share*100)+'%');appendInspectorStat(comparison,selected.name+' · '+text('longestActive'),seconds(selected.longest_active_seconds));appendInspectorStat(comparison,other.name+' · '+text('longestActive'),seconds(other.longest_active_seconds))};select.addEventListener('change',draw);container.append(label,select,comparison);$('app-inspector').append(container);draw()}const activeToolNamesBase=activeToolNames;activeToolNames=function(){return selectedDateKey?(currentView()?.systems||[]).filter(system=>system.active_seconds>0).map(system=>system.name):activeToolNamesBase()};const renderInsightsBase=renderInsights;renderInsights=function(){const source=monitor.activity_view?.insights;if(!source)return renderInsightsBase();monitor.activity_view.insights=source.filter(insight=>insight.kind!=='activity-share');try{return renderInsightsBase()}finally{monitor.activity_view.insights=source}};const renderInspectorBase=renderInspector;renderInspector=function(){renderInspectorBase();appendSystemComparison()};
|
|
96
101
|
const renderInspectorLifecycleBase=renderInspector;renderInspector=function(){renderInspectorLifecycleBase();const stateNode=document.querySelector('.inspector-state'),evidence=$('selected-evidence'),previous=monitor.last_observation;if(stateNode&&monitor.lifecycle!=='monitoring')stateNode.textContent=text('previousObservationSaved');if(evidence&&monitor.lifecycle==='monitoring'&&!monitor.presence?.observation_eligible&&!monitor.observed_seconds&&previous)evidence.textContent=text('lastObservation')+' · '+pct(previous.activity_ratio)+' '+text('aiActive')+' · '+(previous.active_tool||text('noApp'))+' · '+format.format(previous.local_models_found||0)+' '+text('models')+' · '+format.format(previous.local_models_running||0)+' '+text('runningShort')};
|
|
Binary file
|