@trillboards/edge-sdk 0.2.4 → 0.2.7
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 +4 -3
- package/deploy/docker/Dockerfile.cloud-sense +117 -0
- package/deploy/docker/Dockerfile.cpu +30 -21
- package/deploy/docker/Dockerfile.cuda +30 -21
- package/deploy/docker/Dockerfile.openvino +30 -20
- package/deploy/docker/README.md +9 -5
- package/deploy/docker/connector-entrypoint.sh +122 -0
- package/deploy/helm/README.md +1 -1
- package/deploy/k8s/daemonset.yaml +1 -1
- package/deploy/macos/README.md +161 -0
- package/deploy/macos/TrillboardsSetupApp.m +603 -0
- package/deploy/macos/build-macos-pkg.sh +588 -0
- package/deploy/macos/com.trillboards.edge.plist +62 -0
- package/deploy/macos/com.trillboards.litertlm.plist +45 -0
- package/deploy/macos/pair.command +519 -0
- package/deploy/macos/scripts/ensure-litertlm.sh +62 -0
- package/deploy/macos/scripts/postinstall +153 -0
- package/deploy/macos/scripts/setup-litertlm.sh +272 -0
- package/deploy/macos/test-install-pair.sh +383 -0
- package/deploy/macos/test-setup-litertlm-reinstall.sh +109 -0
- package/deploy/systemd/install.sh +25 -15
- package/deploy/systemd/trillboards-edge.service +4 -1
- package/deploy/windows/README.md +70 -0
- package/deploy/windows/build-windows.ps1 +225 -0
- package/deploy/windows/bundle-windows-installer.ps1 +153 -0
- package/deploy/windows/check-feeds.js +26 -0
- package/deploy/windows/e2e/fixtures/person.jpg +0 -0
- package/deploy/windows/e2e/test-local-sense.ps1 +185 -0
- package/deploy/windows/install-service.ps1 +6 -1
- package/deploy/windows/install-update.ps1 +101 -0
- package/deploy/windows/installer.nsi +138 -0
- package/deploy/windows/register-litertlm-task.ps1 +42 -0
- package/deploy/windows/register-task.ps1 +44 -0
- package/deploy/windows/run-connector.ps1 +69 -0
- package/deploy/windows/setup-litertlm.ps1 +361 -0
- package/deploy/windows/stop-existing.ps1 +72 -0
- package/deploy/windows/trillboards-edge-task.vbs +25 -0
- package/deploy/windows/trillboards-edge.cmd +33 -0
- package/deploy/windows/trillboards-pair.cmd +276 -0
- package/deploy/windows/trillboards-setup.ps1 +456 -0
- package/deploy/windows/trillboards-setup.vbs +13 -0
- package/dist/AudienceAnalyzeClient.d.ts +136 -0
- package/dist/AudienceAnalyzeClient.d.ts.map +1 -0
- package/dist/AudienceAnalyzeClient.js +246 -0
- package/dist/AudienceAnalyzeClient.js.map +1 -0
- package/dist/ChannelProber.d.ts +150 -0
- package/dist/ChannelProber.d.ts.map +1 -0
- package/dist/ChannelProber.js +887 -0
- package/dist/ChannelProber.js.map +1 -0
- package/dist/CloudEscalationPolicy.d.ts +143 -0
- package/dist/CloudEscalationPolicy.d.ts.map +1 -0
- package/dist/CloudEscalationPolicy.js +137 -0
- package/dist/CloudEscalationPolicy.js.map +1 -0
- package/dist/CloudSensingManager.d.ts +557 -0
- package/dist/CloudSensingManager.d.ts.map +1 -0
- package/dist/CloudSensingManager.js +1267 -0
- package/dist/CloudSensingManager.js.map +1 -0
- package/dist/DiscoveryReportClient.d.ts +66 -0
- package/dist/DiscoveryReportClient.d.ts.map +1 -0
- package/dist/DiscoveryReportClient.js +85 -0
- package/dist/DiscoveryReportClient.js.map +1 -0
- package/dist/EdgeAgent.d.ts +12 -2
- package/dist/EdgeAgent.d.ts.map +1 -1
- package/dist/EdgeAgent.js +55 -13
- package/dist/EdgeAgent.js.map +1 -1
- package/dist/LiteRtVlmProvider.d.ts +193 -0
- package/dist/LiteRtVlmProvider.d.ts.map +1 -0
- package/dist/LiteRtVlmProvider.js +436 -0
- package/dist/LiteRtVlmProvider.js.map +1 -0
- package/dist/MosaicSource.d.ts +108 -0
- package/dist/MosaicSource.d.ts.map +1 -0
- package/dist/MosaicSource.js +274 -0
- package/dist/MosaicSource.js.map +1 -0
- package/dist/OnvifClient.d.ts +83 -0
- package/dist/OnvifClient.d.ts.map +1 -0
- package/dist/OnvifClient.js +209 -0
- package/dist/OnvifClient.js.map +1 -0
- package/dist/PairClient.d.ts +106 -0
- package/dist/PairClient.d.ts.map +1 -0
- package/dist/PairClient.js +186 -0
- package/dist/PairClient.js.map +1 -0
- package/dist/RecorderAddressRecovery.d.ts +53 -0
- package/dist/RecorderAddressRecovery.d.ts.map +1 -0
- package/dist/RecorderAddressRecovery.js +359 -0
- package/dist/RecorderAddressRecovery.js.map +1 -0
- package/dist/SceneChangeDetector.d.ts +291 -0
- package/dist/SceneChangeDetector.d.ts.map +1 -0
- package/dist/SceneChangeDetector.js +682 -0
- package/dist/SceneChangeDetector.js.map +1 -0
- package/dist/SensingProfileClient.d.ts +59 -0
- package/dist/SensingProfileClient.d.ts.map +1 -0
- package/dist/SensingProfileClient.js +78 -0
- package/dist/SensingProfileClient.js.map +1 -0
- package/dist/SetupSessionClient.d.ts +114 -0
- package/dist/SetupSessionClient.d.ts.map +1 -0
- package/dist/SetupSessionClient.js +95 -0
- package/dist/SetupSessionClient.js.map +1 -0
- package/dist/VlmAdmissionGate.d.ts +126 -0
- package/dist/VlmAdmissionGate.d.ts.map +1 -0
- package/dist/VlmAdmissionGate.js +166 -0
- package/dist/VlmAdmissionGate.js.map +1 -0
- package/dist/autoSense.d.ts +266 -0
- package/dist/autoSense.d.ts.map +1 -0
- package/dist/autoSense.js +606 -0
- package/dist/autoSense.js.map +1 -0
- package/dist/cli.d.ts +27 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +526 -27
- package/dist/cli.js.map +1 -1
- package/dist/combinedSense.d.ts +128 -0
- package/dist/combinedSense.d.ts.map +1 -0
- package/dist/combinedSense.js +261 -0
- package/dist/combinedSense.js.map +1 -0
- package/dist/config.d.ts +351 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +99 -2
- package/dist/config.js.map +1 -1
- package/dist/connectorCadence.d.ts +135 -0
- package/dist/connectorCadence.d.ts.map +1 -0
- package/dist/connectorCadence.js +151 -0
- package/dist/connectorCadence.js.map +1 -0
- package/dist/dataDir.d.ts +33 -0
- package/dist/dataDir.d.ts.map +1 -0
- package/dist/dataDir.js +76 -0
- package/dist/dataDir.js.map +1 -0
- package/dist/deployedCadence.d.ts +167 -0
- package/dist/deployedCadence.d.ts.map +1 -0
- package/dist/deployedCadence.js +209 -0
- package/dist/deployedCadence.js.map +1 -0
- package/dist/deployedCadenceSource.d.ts +48 -0
- package/dist/deployedCadenceSource.d.ts.map +1 -0
- package/dist/deployedCadenceSource.js +74 -0
- package/dist/deployedCadenceSource.js.map +1 -0
- package/dist/diagnose.d.ts +1 -1
- package/dist/diagnose.d.ts.map +1 -1
- package/dist/diagnose.js +15 -13
- package/dist/diagnose.js.map +1 -1
- package/dist/discovery.d.ts +131 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +499 -0
- package/dist/discovery.js.map +1 -0
- package/dist/gemmaModelOta.d.ts +118 -0
- package/dist/gemmaModelOta.d.ts.map +1 -0
- package/dist/gemmaModelOta.js +291 -0
- package/dist/gemmaModelOta.js.map +1 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -1
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +25 -87
- package/dist/init.js.map +1 -1
- package/dist/keepAwake.d.ts +33 -0
- package/dist/keepAwake.d.ts.map +1 -0
- package/dist/keepAwake.js +54 -0
- package/dist/keepAwake.js.map +1 -0
- package/dist/legacyRecorderProfile.d.ts +20 -0
- package/dist/legacyRecorderProfile.d.ts.map +1 -0
- package/dist/legacyRecorderProfile.js +27 -0
- package/dist/legacyRecorderProfile.js.map +1 -0
- package/dist/localInferenceSchema.d.ts +132 -0
- package/dist/localInferenceSchema.d.ts.map +1 -0
- package/dist/localInferenceSchema.js +274 -0
- package/dist/localInferenceSchema.js.map +1 -0
- package/dist/localSenseE2eHarness.d.ts +101 -0
- package/dist/localSenseE2eHarness.d.ts.map +1 -0
- package/dist/localSenseE2eHarness.js +279 -0
- package/dist/localSenseE2eHarness.js.map +1 -0
- package/dist/localSensing.d.ts +284 -0
- package/dist/localSensing.d.ts.map +1 -0
- package/dist/localSensing.js +512 -0
- package/dist/localSensing.js.map +1 -0
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +3 -1
- package/dist/mcp-server.js.map +1 -1
- package/dist/modelIntegrity.d.ts +80 -0
- package/dist/modelIntegrity.d.ts.map +1 -0
- package/dist/modelIntegrity.js +194 -0
- package/dist/modelIntegrity.js.map +1 -0
- package/dist/pairVerify.d.ts +100 -0
- package/dist/pairVerify.d.ts.map +1 -0
- package/dist/pairVerify.js +296 -0
- package/dist/pairVerify.js.map +1 -0
- package/dist/qrScanner.d.ts +33 -0
- package/dist/qrScanner.d.ts.map +1 -0
- package/dist/qrScanner.js +103 -0
- package/dist/qrScanner.js.map +1 -0
- package/dist/runtimePaths.d.ts +10 -0
- package/dist/runtimePaths.d.ts.map +1 -0
- package/dist/runtimePaths.js +79 -0
- package/dist/runtimePaths.js.map +1 -0
- package/dist/sensingRunMode.d.ts +86 -0
- package/dist/sensingRunMode.d.ts.map +1 -0
- package/dist/sensingRunMode.js +346 -0
- package/dist/sensingRunMode.js.map +1 -0
- package/dist/setupMode.d.ts +98 -0
- package/dist/setupMode.d.ts.map +1 -0
- package/dist/setupMode.js +828 -0
- package/dist/setupMode.js.map +1 -0
- package/dist/setupProgress.d.ts +31 -0
- package/dist/setupProgress.d.ts.map +1 -0
- package/dist/setupProgress.js +173 -0
- package/dist/setupProgress.js.map +1 -0
- package/package.json +13 -4
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalSensingClient = void 0;
|
|
4
|
+
exports.fieldSetForProfileType = fieldSetForProfileType;
|
|
5
|
+
exports.isAlertWorthy = isAlertWorthy;
|
|
6
|
+
exports.alertSignature = alertSignature;
|
|
7
|
+
exports.isCloudEscalationEnabled = isCloudEscalationEnabled;
|
|
8
|
+
exports.buildLocalSensingLoops = buildLocalSensingLoops;
|
|
9
|
+
const AudienceAnalyzeClient_1 = require("./AudienceAnalyzeClient");
|
|
10
|
+
const LiteRtVlmProvider_1 = require("./LiteRtVlmProvider");
|
|
11
|
+
const SensingProfileClient_1 = require("./SensingProfileClient");
|
|
12
|
+
const CloudSensingManager_1 = require("./CloudSensingManager");
|
|
13
|
+
const localInferenceSchema_1 = require("./localInferenceSchema");
|
|
14
|
+
const CloudEscalationPolicy_1 = require("./CloudEscalationPolicy");
|
|
15
|
+
const legacyRecorderProfile_1 = require("./legacyRecorderProfile");
|
|
16
|
+
/** How long a fetched profile is cached before the connector re-checks for a new alert. */
|
|
17
|
+
const DEFAULT_PROFILE_TTL_MS = 5 * 60 * 1000;
|
|
18
|
+
/**
|
|
19
|
+
* The server's own name for a Sense-Anything profile — one an operator authored
|
|
20
|
+
* from their venue intent, as opposed to a seeded DOOH audience preset.
|
|
21
|
+
* `sensingProfileService.createProfile` defaults `profile_type` to this and
|
|
22
|
+
* derives `profile_origin='custom_intent'` from it; 147 of the 157 active
|
|
23
|
+
* profiles carried it on 2026-08-11.
|
|
24
|
+
*/
|
|
25
|
+
const CUSTOM_INTENT_PROFILE_TYPE = 'custom_intent';
|
|
26
|
+
/**
|
|
27
|
+
* Map a deployed profile's SELF-DESCRIBING type to the on-device field set.
|
|
28
|
+
*
|
|
29
|
+
* A Sense-Anything (`custom_intent`) profile asked a specific operational
|
|
30
|
+
* question about the operator's own venue, so the on-device model is asked for
|
|
31
|
+
* that profile's fields plus the presence primitives — NOT the 12 DOOH audience
|
|
32
|
+
* fields, which answer nothing the operator asked and cost 1.09-1.53x the
|
|
33
|
+
* per-frame latency depending on delegate (measured; see localInferenceSchema's
|
|
34
|
+
* header). Every other type — a seeded builtin preset, or NO deployed profile at
|
|
35
|
+
* all — is a screen/audience measurement deployment and keeps the full audience
|
|
36
|
+
* contract.
|
|
37
|
+
*
|
|
38
|
+
* This reads the kind the SERVER already reports rather than an allowlist of
|
|
39
|
+
* profile names maintained on the device.
|
|
40
|
+
*/
|
|
41
|
+
function fieldSetForProfileType(profileType) {
|
|
42
|
+
return profileType === CUSTOM_INTENT_PROFILE_TYPE ? 'operator' : 'audience';
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Sentinel values the schema uses for an EMPTY scene (see localInferenceSchema):
|
|
46
|
+
* audience_* enums collapse to 'none'/'unclear', crowd_density to 'empty',
|
|
47
|
+
* narratives to '' when no people are present. A custom enum's "nothing here"
|
|
48
|
+
* value typically reads the same ('empty'/'unknown'). Treated as "no signal" so
|
|
49
|
+
* a blank room doesn't ship a frame.
|
|
50
|
+
*/
|
|
51
|
+
const EMPTY_SCENE_SENTINELS = new Set(['', 'none', 'unclear', 'empty', 'unknown']);
|
|
52
|
+
/**
|
|
53
|
+
* Keys ignored by the gate — they don't DISCRIMINATE an empty scene from a busy
|
|
54
|
+
* one, so gating on them would attach a frame to every observation (defeating
|
|
55
|
+
* the privacy filter):
|
|
56
|
+
* - source / captured_at: connector-stamped provenance + time (never a signal);
|
|
57
|
+
* - confidence: a meta score the model emits ~always > 0 even for a blank room;
|
|
58
|
+
* - scene_description: free text that is non-empty even when the scene is empty
|
|
59
|
+
* ("an empty room") — not a meaningful route equality target.
|
|
60
|
+
* Discriminative scene fields (person_count, people_present, crowd_density,
|
|
61
|
+
* ad_receptivity_score, the audience_* enums, the narrative) are NOT ignored:
|
|
62
|
+
* each reads its empty-sentinel/0/false for a blank scene, so they correctly
|
|
63
|
+
* contribute no signal when empty but DO when people are present.
|
|
64
|
+
*/
|
|
65
|
+
const NON_DISCRIMINATIVE_KEYS = new Set(['source', 'captured_at', 'confidence', 'scene_description']);
|
|
66
|
+
/**
|
|
67
|
+
* Decide whether an on-device result is potentially ALERT-WORTHY — the gate that
|
|
68
|
+
* keeps the evidence frame OFF the wire for an EMPTY/uninteresting scene (privacy
|
|
69
|
+
* + bandwidth) while still attaching it for any observation an operator route
|
|
70
|
+
* could fire on. Sense-Anything routes evaluate gt/gte/lt/lte/eq/neq over
|
|
71
|
+
* numbers (counts AND gauges), booleans, and strings/enums (routeEvaluatorService
|
|
72
|
+
* `compare()`), so the gate cannot be limited to "true boolean / positive count"
|
|
73
|
+
* — a gauge route (`avg_wait_minutes gt 5`) or an enum route
|
|
74
|
+
* (`shelf_state eq "empty"`) would otherwise fire WITHOUT a snapshot.
|
|
75
|
+
*
|
|
76
|
+
* A frame is attached when the result carries ANY meaningful signal:
|
|
77
|
+
* - any boolean field that is `true` (safety_concern, people_present, a custom
|
|
78
|
+
* shelf_empty, …);
|
|
79
|
+
* - any positive finite number — a count (person_count / *_count) OR a gauge
|
|
80
|
+
* (avg_wait_minutes, ad_receptivity_score, …);
|
|
81
|
+
* - any non-empty array (a populated `list` custom field), or non-empty object
|
|
82
|
+
* (a `distribution` field);
|
|
83
|
+
* - any string/enum whose value is NOT an empty-scene sentinel
|
|
84
|
+
* (''/none/unclear/empty/unknown) — e.g. shelf_state='low'.
|
|
85
|
+
* Non-discriminative keys (source, captured_at, confidence, scene_description)
|
|
86
|
+
* are ignored — see NON_DISCRIMINATIVE_KEYS for why.
|
|
87
|
+
*
|
|
88
|
+
* KNOWN BOUNDARY (deliberate privacy tradeoff): a route that alerts on ABSENCE
|
|
89
|
+
* — `person_count lte 0`, `people_present eq false`, `shelf_state eq "empty"` —
|
|
90
|
+
* fires on a genuinely empty scene, which this gate suppresses by design (a
|
|
91
|
+
* blank-room frame is exactly the privacy harm the gate exists to prevent). The
|
|
92
|
+
* server owns the route predicates and can request a frame for those routes;
|
|
93
|
+
* operators who want every frame can disable the gate (attachEvidenceFrame).
|
|
94
|
+
*/
|
|
95
|
+
function isAlertWorthy(result) {
|
|
96
|
+
if (!result || typeof result !== 'object')
|
|
97
|
+
return false;
|
|
98
|
+
for (const [key, value] of Object.entries(result)) {
|
|
99
|
+
if (NON_DISCRIMINATIVE_KEYS.has(key))
|
|
100
|
+
continue;
|
|
101
|
+
if (value === true)
|
|
102
|
+
return true;
|
|
103
|
+
if (typeof value === 'number') {
|
|
104
|
+
if (Number.isFinite(value) && value > 0)
|
|
105
|
+
return true;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (typeof value === 'string') {
|
|
109
|
+
const v = value.trim().toLowerCase();
|
|
110
|
+
if (v.length > 0 && !EMPTY_SCENE_SENTINELS.has(v))
|
|
111
|
+
return true;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (Array.isArray(value)) {
|
|
115
|
+
if (value.length > 0)
|
|
116
|
+
return true;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (value && typeof value === 'object') {
|
|
120
|
+
if (Object.keys(value).length > 0)
|
|
121
|
+
return true;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* A stable fingerprint of the ALERT-BEARING state of one result — "has the
|
|
129
|
+
* operator-visible situation changed?" in one comparable string.
|
|
130
|
+
*
|
|
131
|
+
* Built from EXACTLY the keys `isAlertWorthy` judges (NON_DISCRIMINATIVE_KEYS
|
|
132
|
+
* excluded), so the two can never drift: the same fields that decide whether a
|
|
133
|
+
* frame carries signal decide whether that signal is NEW. Keys are sorted so
|
|
134
|
+
* the model's property order cannot manufacture a change, and values are
|
|
135
|
+
* compared as emitted — a `queue_length` of 3 and of 4 are genuinely different
|
|
136
|
+
* facts and an operator route may key on exactly that boundary, so this
|
|
137
|
+
* deliberately does NOT bucket numbers into invented bands.
|
|
138
|
+
*
|
|
139
|
+
* Used by CloudEscalationPolicy's novelty bound: an unchanged signature means a
|
|
140
|
+
* verdict the cloud has already adjudicated, so the frame stays on-premises.
|
|
141
|
+
*/
|
|
142
|
+
function alertSignature(result) {
|
|
143
|
+
if (!result || typeof result !== 'object')
|
|
144
|
+
return '';
|
|
145
|
+
const parts = [];
|
|
146
|
+
for (const key of Object.keys(result).sort()) {
|
|
147
|
+
if (NON_DISCRIMINATIVE_KEYS.has(key))
|
|
148
|
+
continue;
|
|
149
|
+
const value = result[key];
|
|
150
|
+
// Arrays/objects (a `list` / `distribution` custom field) serialize
|
|
151
|
+
// structurally; scalars stringify. Undefined is elided so an absent key and
|
|
152
|
+
// an explicitly-undefined key read the same.
|
|
153
|
+
if (value === undefined)
|
|
154
|
+
continue;
|
|
155
|
+
parts.push(`${key}=${typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value)}`);
|
|
156
|
+
}
|
|
157
|
+
return parts.join('|');
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Is cloud escalation actually ON for this local-sensing configuration?
|
|
161
|
+
*
|
|
162
|
+
* TWO settings decide it, and this is the ONLY place that combines them:
|
|
163
|
+
* `cloudEscalation.enabled` opts in, and `attachEvidenceFrame: false` — the
|
|
164
|
+
* operator's "no frame ever leaves this machine" switch — outranks it, because
|
|
165
|
+
* escalation sends a frame. `LocalSensingClient` enforces the rule and the
|
|
166
|
+
* startup banner reports it; they must read the same expression or the banner
|
|
167
|
+
* will one day claim an egress posture the client is not running.
|
|
168
|
+
*/
|
|
169
|
+
function isCloudEscalationEnabled(options) {
|
|
170
|
+
if (!options)
|
|
171
|
+
return false;
|
|
172
|
+
return options.cloudEscalation?.enabled === true && options.attachEvidenceFrame !== false;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* LocalSensingClient — the SensingClient the cadence loop uses in LOCAL mode.
|
|
176
|
+
* It runs the VLM ON-DEVICE (LiteRtVlmProvider — Gemma 4 on LiteRT-LM via
|
|
177
|
+
* plain-completion + client-side schema validation, the real CCTV runtime) and
|
|
178
|
+
* POSTs the STRUCTURED result as `preAnalyzed` (uploadClient.analyzePreAnalyzed),
|
|
179
|
+
* skipping the cloud VLM. Implements the same `analyze({jpeg,captureMode})`
|
|
180
|
+
* shape AudienceAnalyzeClient exposes, so CloudSensingManager runs it UNCHANGED.
|
|
181
|
+
*
|
|
182
|
+
* PROFILE-DRIVEN: before analyzing, it fetches (and caches) the screen's active
|
|
183
|
+
* Sense-Anything profile via SensingProfileClient and rebuilds the constrained
|
|
184
|
+
* schema + prompt to include the operator's CUSTOM fields — the on-device
|
|
185
|
+
* equivalent of the cloud `buildProfileDrivenJsonSchema`. So a deployed alert
|
|
186
|
+
* like "queue_length >= 3" makes Gemma emit `queue_length`, which flows through
|
|
187
|
+
* preAnalyzed → OFV → the route → the webhook, exactly like the cloud VLM. The
|
|
188
|
+
* fetch is best-effort: on any failure the last-good (or default) schema is kept,
|
|
189
|
+
* so on-device sensing NEVER goes silent.
|
|
190
|
+
*
|
|
191
|
+
* The connector OVERRIDES `source='vlm_edge'` and `captured_at` (the actual
|
|
192
|
+
* capture time) — never trusting the model for provenance/time. The SERVER
|
|
193
|
+
* additionally forces provenance `local_connector` on the preAnalyzed ingest,
|
|
194
|
+
* gating local-inferred data out of the sold bidstream while still firing the
|
|
195
|
+
* operator's Sense-Anything routes.
|
|
196
|
+
*
|
|
197
|
+
* ── CLOUD ESCALATION (opt-in, Phase 6d) ─────────────────────────────────────
|
|
198
|
+
* Local Gemma is the always-on gate; the CLOUD is the judge for the frames it
|
|
199
|
+
* flags. When `cloudEscalation.enabled` and the policy admits this frame, the
|
|
200
|
+
* SAME JPEG is POSTed through the SAME upload client's `analyze()` — the cloud
|
|
201
|
+
* path the connector already owns — and the cloud VLM's verdict becomes the
|
|
202
|
+
* observation. Three properties are load-bearing:
|
|
203
|
+
*
|
|
204
|
+
* 1. ONE FRAME, ONE OBSERVATION. An escalated frame does NOT also post its
|
|
205
|
+
* local `preAnalyzed` result. Posting both would double-count the same
|
|
206
|
+
* physical frame and put two verdicts for one moment in the stream —
|
|
207
|
+
* exactly the dual write the codebase forbids. The cloud verdict
|
|
208
|
+
* supersedes; the local one was a sampling decision, not a datum.
|
|
209
|
+
* 2. NO PROVENANCE LAUNDERING. What lands is what the CLOUD inferred from a
|
|
210
|
+
* real frame, so the server's normal cloud provenance is the truthful
|
|
211
|
+
* stamp — identical to `cloud-sense` and to the existing
|
|
212
|
+
* `cloudFallbackOnError` frame. Nothing the local model produced enters
|
|
213
|
+
* that record, so `ingest_source='local_connector'` (which exists to keep
|
|
214
|
+
* operator-forgeable structured input out of the sold bidstream) does not
|
|
215
|
+
* apply and is not evaded: there IS no local structured input on this path.
|
|
216
|
+
* 3. THE PRIVACY GATE IS UNCHANGED. Escalation reuses `isAlertWorthy`, so the
|
|
217
|
+
* set of frames that can leave the premises is exactly the set that
|
|
218
|
+
* already leaves as evidence photos — and `attachEvidenceFrame: false`
|
|
219
|
+
* (the operator's "no frame ever leaves" switch) disables escalation
|
|
220
|
+
* outright. An empty scene never escalates.
|
|
221
|
+
*
|
|
222
|
+
* A failed cloud call falls back to posting the local result, so an escalation
|
|
223
|
+
* can degrade the ACCURACY of one frame but can never LOSE it.
|
|
224
|
+
*/
|
|
225
|
+
class LocalSensingClient {
|
|
226
|
+
constructor(localProvider, uploadClient, options = {}) {
|
|
227
|
+
this.localProvider = localProvider;
|
|
228
|
+
this.uploadClient = uploadClient;
|
|
229
|
+
this.lastProfileFetchMs = 0;
|
|
230
|
+
/**
|
|
231
|
+
* Frames the ON-DEVICE runtime actually produced a result for.
|
|
232
|
+
*
|
|
233
|
+
* Counted at the one place that knows — immediately after `localProvider
|
|
234
|
+
* .analyze()` RETURNS — so a `cloudFallbackOnError` frame (local threw, the
|
|
235
|
+
* cloud answered) and an escalated frame are both excluded. `auto-sense`
|
|
236
|
+
* reads this through `getSensingHealth().localInferences` as the only
|
|
237
|
+
* positive proof that a `litert-lm serve` too busy to answer `/v1/models` is
|
|
238
|
+
* busy rather than dead; a counter that a cloud call could advance would make
|
|
239
|
+
* that proof self-fulfilling and mask a genuinely dead runtime forever.
|
|
240
|
+
*/
|
|
241
|
+
this.localInferencesCompleted = 0;
|
|
242
|
+
/** The active profile name (for logging / observability). */
|
|
243
|
+
this.activeProfileName = null;
|
|
244
|
+
/**
|
|
245
|
+
* Which field set the active profile resolved to (for logging / observability):
|
|
246
|
+
* 'operator' for a Sense-Anything profile, 'audience' for a DOOH preset or no
|
|
247
|
+
* profile. See fieldSetForProfileType.
|
|
248
|
+
*/
|
|
249
|
+
this.activeFieldSet = 'audience';
|
|
250
|
+
this.profileClient = options.profileClient;
|
|
251
|
+
this.profileTtlMs = options.profileTtlMs ?? DEFAULT_PROFILE_TTL_MS;
|
|
252
|
+
this.baseSchema = options.schema ?? (0, localInferenceSchema_1.buildLocalSensingSchema)();
|
|
253
|
+
this.basePrompt = options.prompt ?? (0, localInferenceSchema_1.buildLocalSensingPrompt)();
|
|
254
|
+
this.cachedSchema = this.baseSchema;
|
|
255
|
+
this.cachedPrompt = this.basePrompt;
|
|
256
|
+
this.attachEvidenceFrame = options.attachEvidenceFrame !== false;
|
|
257
|
+
this.cloudFallbackOnError = options.cloudFallbackOnError === true;
|
|
258
|
+
this.feedName = options.feedName || 'local-sense';
|
|
259
|
+
// `attachEvidenceFrame: false` is the operator saying NO frame may ever
|
|
260
|
+
// leave this machine, and escalation sends a frame — isCloudEscalationEnabled
|
|
261
|
+
// is the one expression that resolves the pair (the startup banner reads the
|
|
262
|
+
// same one, so the two can never disagree).
|
|
263
|
+
this.escalationPolicy = new CloudEscalationPolicy_1.CloudEscalationPolicy({
|
|
264
|
+
...(options.cloudEscalation || {}),
|
|
265
|
+
enabled: isCloudEscalationEnabled({
|
|
266
|
+
attachEvidenceFrame: this.attachEvidenceFrame,
|
|
267
|
+
cloudEscalation: options.cloudEscalation,
|
|
268
|
+
}),
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
getEndpoint() {
|
|
272
|
+
return this.uploadClient.getEndpoint();
|
|
273
|
+
}
|
|
274
|
+
reportCameraHealth(cameraHealth) {
|
|
275
|
+
return this.uploadClient.reportCameraHealth(cameraHealth);
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Re-fetch the active profile if the cache is stale and rebuild the schema +
|
|
279
|
+
* prompt from its custom fields AND its KIND. Best-effort: a fetch failure
|
|
280
|
+
* keeps the existing schema (never disrupts an in-flight profile).
|
|
281
|
+
*
|
|
282
|
+
* The field set follows the profile's own reported type (fieldSetForProfileType):
|
|
283
|
+
* a Sense-Anything profile gets its declared fields plus the presence
|
|
284
|
+
* primitives; a seeded DOOH preset — or a profile that declares nothing —
|
|
285
|
+
* keeps the full audience contract.
|
|
286
|
+
*/
|
|
287
|
+
async refreshProfileIfStale() {
|
|
288
|
+
if (!this.profileClient)
|
|
289
|
+
return;
|
|
290
|
+
const now = Date.now();
|
|
291
|
+
if (now - this.lastProfileFetchMs < this.profileTtlMs)
|
|
292
|
+
return;
|
|
293
|
+
this.lastProfileFetchMs = now;
|
|
294
|
+
const profile = await this.profileClient.fetchProfile();
|
|
295
|
+
if (profile === null)
|
|
296
|
+
return; // transient error — keep last-good schema
|
|
297
|
+
this.activeProfileName = profile.profileName;
|
|
298
|
+
const fieldSet = fieldSetForProfileType(profile.profileType);
|
|
299
|
+
this.activeFieldSet = fieldSet;
|
|
300
|
+
if (fieldSet === 'operator' || profile.fields.length > 0) {
|
|
301
|
+
this.cachedSchema = (0, localInferenceSchema_1.buildLocalSensingSchema)({ fieldSet, extraFields: profile.fields });
|
|
302
|
+
this.cachedPrompt = (0, localInferenceSchema_1.buildLocalSensingPrompt)({ fieldSet, extraFields: profile.fields });
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
// A DOOH/seeded profile with no custom fields (or no active profile at
|
|
306
|
+
// all) → the default audience schema, byte-for-byte unchanged.
|
|
307
|
+
this.cachedSchema = this.baseSchema;
|
|
308
|
+
this.cachedPrompt = this.basePrompt;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* What the on-device runtime resolved about this host's accelerator (which
|
|
313
|
+
* litert-lm delegate it selected, and why it is not on the GPU when it is not).
|
|
314
|
+
* Surfaced so an operator/support can see a silent CPU fallback without
|
|
315
|
+
* log-diving. See LiteRtVlmProvider.getAcceleratorHealth.
|
|
316
|
+
*/
|
|
317
|
+
getAcceleratorHealth() {
|
|
318
|
+
return this.localProvider.getAcceleratorHealth();
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Cloud-escalation counters for this feed — how many candidate frames were
|
|
322
|
+
* adjudicated by the cloud VLM, and why the rest were not. Read by
|
|
323
|
+
* `CloudSensingManager.getSensingHealth()` so escalation shows up on the
|
|
324
|
+
* operator's periodic capacity line: sending frames off-premises must be
|
|
325
|
+
* VISIBLE, not merely documented.
|
|
326
|
+
*/
|
|
327
|
+
getEscalationStats() {
|
|
328
|
+
return this.escalationPolicy.getStats();
|
|
329
|
+
}
|
|
330
|
+
/** Frames the on-device runtime produced a result for. See the field docs. */
|
|
331
|
+
getLocalInferenceStats() {
|
|
332
|
+
return { completed: this.localInferencesCompleted };
|
|
333
|
+
}
|
|
334
|
+
async analyze(request) {
|
|
335
|
+
await this.refreshProfileIfStale();
|
|
336
|
+
let structured;
|
|
337
|
+
try {
|
|
338
|
+
structured = await this.localProvider.analyze({
|
|
339
|
+
jpeg: request.jpeg,
|
|
340
|
+
schema: this.cachedSchema,
|
|
341
|
+
prompt: this.cachedPrompt,
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
if (!this.cloudFallbackOnError)
|
|
346
|
+
throw err;
|
|
347
|
+
// Per-frame degrade: on-device inference threw for THIS frame, so ship it
|
|
348
|
+
// to the cloud VLM instead of dropping it. Reuses the SAME upload client —
|
|
349
|
+
// the cloud path the connector already has — so no new provider/logic. The
|
|
350
|
+
// server provenance-gates a cloud-served frame exactly like cloud-sense.
|
|
351
|
+
// eslint-disable-next-line no-console
|
|
352
|
+
console.warn(`[local-sense] on-device inference failed for a frame (${err.message}) — ` +
|
|
353
|
+
'falling back to the cloud VLM for this frame');
|
|
354
|
+
return this.uploadClient.analyze({
|
|
355
|
+
jpeg: request.jpeg,
|
|
356
|
+
capturedAt: Date.now(),
|
|
357
|
+
captureMode: request.captureMode,
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
// The on-device runtime returned a result: the ONLY event that proves it is
|
|
361
|
+
// alive. Counted here, past every throw and before any cloud path below, so
|
|
362
|
+
// no cloud call can advance it.
|
|
363
|
+
this.localInferencesCompleted += 1;
|
|
364
|
+
// Connector-controlled provenance + time (don't trust the model for these).
|
|
365
|
+
const capturedAtMs = Date.now();
|
|
366
|
+
// PRIVACY-CONSCIOUS evidence frame: attach the SAME JPEG the VLM analyzed
|
|
367
|
+
// (no re-capture) only when enabled AND the observation is potentially
|
|
368
|
+
// alert-worthy. Empty/uninteresting scenes send results only — no frame.
|
|
369
|
+
// Evaluate over the raw model output (not the provenance-stamped `result`,
|
|
370
|
+
// though both yield the same gate since source/captured_at are strings).
|
|
371
|
+
const hasSignal = isAlertWorthy(structured);
|
|
372
|
+
// ESCALATION: a candidate the local model flagged is adjudicated by the
|
|
373
|
+
// cloud VLM BEFORE it can fire an alert. Same gate as the evidence frame,
|
|
374
|
+
// so no frame leaves that would not already have left; the policy adds the
|
|
375
|
+
// novelty/pacing/budget bounds. On success this frame's observation is the
|
|
376
|
+
// CLOUD's — the local result is deliberately not also posted (one frame,
|
|
377
|
+
// one observation).
|
|
378
|
+
const canEscalate = this.escalationPolicy.isEnabled() && hasSignal;
|
|
379
|
+
if (this.escalationPolicy.tryEscalate({
|
|
380
|
+
hasSignal,
|
|
381
|
+
// Only computed when the frame could actually escalate. This runs on
|
|
382
|
+
// every frame of every camera and the common case — escalation off, or
|
|
383
|
+
// an empty scene — needs no signature at all (tryEscalate rejects on
|
|
384
|
+
// `disabled`/`no_signal` before it reads one).
|
|
385
|
+
signature: canEscalate ? alertSignature(structured) : '',
|
|
386
|
+
}).escalate) {
|
|
387
|
+
try {
|
|
388
|
+
const escalated = await this.uploadClient.analyze({
|
|
389
|
+
jpeg: request.jpeg,
|
|
390
|
+
capturedAt: capturedAtMs,
|
|
391
|
+
captureMode: request.captureMode,
|
|
392
|
+
});
|
|
393
|
+
// Rare by construction (bounded per feed per hour), so log every one:
|
|
394
|
+
// an operator must be able to see each frame that left the premises.
|
|
395
|
+
// eslint-disable-next-line no-console
|
|
396
|
+
console.log(`[${this.feedName}] local candidate escalated to the cloud VLM for confirmation ` +
|
|
397
|
+
`→ HTTP ${escalated.statusCode}${escalated.ok ? '' : ' (NOT OK)'}`);
|
|
398
|
+
if (escalated.ok)
|
|
399
|
+
return escalated;
|
|
400
|
+
// A non-2xx cloud verdict is no verdict at all — fall through and ship
|
|
401
|
+
// the local result rather than losing the observation.
|
|
402
|
+
// eslint-disable-next-line no-console
|
|
403
|
+
console.warn(`[${this.feedName}] cloud escalation returned HTTP ${escalated.statusCode} — ` +
|
|
404
|
+
'keeping the on-device verdict for this frame');
|
|
405
|
+
}
|
|
406
|
+
catch (err) {
|
|
407
|
+
// eslint-disable-next-line no-console
|
|
408
|
+
console.warn(`[${this.feedName}] cloud escalation failed (${err.message}) — ` +
|
|
409
|
+
'keeping the on-device verdict for this frame');
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const result = {
|
|
413
|
+
...structured,
|
|
414
|
+
source: 'vlm_edge',
|
|
415
|
+
captured_at: new Date(capturedAtMs).toISOString(),
|
|
416
|
+
};
|
|
417
|
+
const includeFrame = this.attachEvidenceFrame && hasSignal;
|
|
418
|
+
return this.uploadClient.analyzePreAnalyzed(result, {
|
|
419
|
+
capturedAt: capturedAtMs,
|
|
420
|
+
captureMode: request.captureMode,
|
|
421
|
+
...(includeFrame ? { jpeg: request.jpeg } : {}),
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
exports.LocalSensingClient = LocalSensingClient;
|
|
426
|
+
/**
|
|
427
|
+
* Build per-feed capture loops for LOCAL sensing — one RTSP provider + one
|
|
428
|
+
* LocalSensingClient (local VLM + preAnalyzed upload) per feed, each under its
|
|
429
|
+
* OWN device token. Each client fetches its screen's active Sense-Anything
|
|
430
|
+
* profile so the on-device VLM emits that screen's custom fields. Mirrors
|
|
431
|
+
* buildSensingLoops but swaps the cloud client for the local one.
|
|
432
|
+
*/
|
|
433
|
+
function buildLocalSensingLoops(config, deps) {
|
|
434
|
+
const feeds = (0, CloudSensingManager_1.resolveFeeds)(config);
|
|
435
|
+
if (feeds.length === 0) {
|
|
436
|
+
throw new Error('buildLocalSensingLoops: no camera feeds configured (set `cameras: [...]` or `camera.rtspUrl`)');
|
|
437
|
+
}
|
|
438
|
+
const litertLmBaseUrl = config.localSensing?.litertLmBaseUrl;
|
|
439
|
+
const model = config.localSensing?.model;
|
|
440
|
+
// Which litert-lm delegate to run. Default 'auto' (resolve the `,gpu` variant
|
|
441
|
+
// from the live model list and prefer it) — see LiteRtVlmProvider.
|
|
442
|
+
const accelerator = config.localSensing?.accelerator;
|
|
443
|
+
// Evidence frame defaults ON; resolveConfig normalizes it, but treat a bare
|
|
444
|
+
// (un-normalized) config or absent localSensing the same way (explicit false
|
|
445
|
+
// is the only opt-out).
|
|
446
|
+
const attachEvidenceFrame = config.localSensing?.attachEvidenceFrame !== false;
|
|
447
|
+
// Per-frame cloud fallback defaults OFF (opt-in) so the plain local-sense path
|
|
448
|
+
// is unchanged. When true, a frame whose on-device inference throws is POSTed
|
|
449
|
+
// to the cloud VLM (same upload client) rather than dropped.
|
|
450
|
+
const cloudFallbackOnError = config.localSensing?.cloudFallbackOnError === true;
|
|
451
|
+
// Cloud escalation defaults OFF. Each feed gets its OWN policy instance so the
|
|
452
|
+
// per-camera budget is per camera (a shared one would let a busy feed spend a
|
|
453
|
+
// quiet feed's confirmations).
|
|
454
|
+
const cloudEscalation = config.localSensing?.cloudEscalation;
|
|
455
|
+
const rtspFactory = deps?.rtspProviderFactory ??
|
|
456
|
+
((feed) => {
|
|
457
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
458
|
+
const { RTSPCameraProvider } = require('@trillboards/edge-platform-rtsp');
|
|
459
|
+
return new RTSPCameraProvider({
|
|
460
|
+
rtspUrl: feed.rtspUrl,
|
|
461
|
+
width: feed.width,
|
|
462
|
+
height: feed.height,
|
|
463
|
+
fps: feed.fps,
|
|
464
|
+
transport: feed.transport,
|
|
465
|
+
ffmpegPath: feed.ffmpegPath,
|
|
466
|
+
// Honor a mosaic tile's crop in the per-tile-pull fallback path (shared
|
|
467
|
+
// grouping handles the single-pull case). Without this, a mosaic config
|
|
468
|
+
// in local-sense would send the whole composite for each tile.
|
|
469
|
+
crop: feed.crop,
|
|
470
|
+
outputFormat: feed.outputFormat ?? 'jpeg',
|
|
471
|
+
jpegQuality: feed.jpegQuality,
|
|
472
|
+
...(deps?.streamUrlResolver
|
|
473
|
+
? {
|
|
474
|
+
streamUrlResolver: (currentUrl, error) => deps.streamUrlResolver(feed, currentUrl, error),
|
|
475
|
+
}
|
|
476
|
+
: {}),
|
|
477
|
+
...(0, legacyRecorderProfile_1.legacyRecorderRtspOptions)(feed.sourceType),
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
const localProviderFactory = deps?.localProviderFactory ??
|
|
481
|
+
(() => new LiteRtVlmProvider_1.LiteRtVlmProvider({ litertLmBaseUrl, model, accelerator }));
|
|
482
|
+
const uploadClientFactory = deps?.uploadClientFactory ??
|
|
483
|
+
((feed) => new AudienceAnalyzeClient_1.AudienceAnalyzeClient({
|
|
484
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
485
|
+
deviceToken: feed.deviceToken,
|
|
486
|
+
screenId: feed.screenId,
|
|
487
|
+
imageWidth: feed.width,
|
|
488
|
+
imageHeight: feed.height,
|
|
489
|
+
}, deps?.httpPoster));
|
|
490
|
+
// Each feed fetches its OWN screen's active profile (device token binds to one
|
|
491
|
+
// screen). Falls back to the default schema if the profile fetch ever fails.
|
|
492
|
+
const profileClientFactory = deps?.profileClientFactory ??
|
|
493
|
+
((feed) => new SensingProfileClient_1.SensingProfileClient({ apiBaseUrl: config.apiBaseUrl, deviceToken: feed.deviceToken }, deps?.httpPoster));
|
|
494
|
+
// Mosaic-aware (shared with cloud sensing): collapses tiles of one source onto
|
|
495
|
+
// a single shared pull + crops each tile, so local-sense honors mosaic configs
|
|
496
|
+
// identically to cloud-sense. `sharedSources` lets the VLM attach to the SAME
|
|
497
|
+
// pull the on-device zoo uses (combined-sense), so one RTSP pull feeds both.
|
|
498
|
+
return (0, CloudSensingManager_1.buildTiledCameras)(feeds, rtspFactory, {
|
|
499
|
+
sharedSources: deps?.sharedSources,
|
|
500
|
+
}).map(({ feed, camera }) => ({
|
|
501
|
+
name: feed.name,
|
|
502
|
+
camera,
|
|
503
|
+
client: new LocalSensingClient(localProviderFactory(feed), uploadClientFactory(feed), {
|
|
504
|
+
profileClient: profileClientFactory(feed),
|
|
505
|
+
attachEvidenceFrame,
|
|
506
|
+
cloudFallbackOnError,
|
|
507
|
+
feedName: feed.name,
|
|
508
|
+
...(cloudEscalation ? { cloudEscalation } : {}),
|
|
509
|
+
}),
|
|
510
|
+
}));
|
|
511
|
+
}
|
|
512
|
+
//# sourceMappingURL=localSensing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localSensing.js","sourceRoot":"","sources":["../src/localSensing.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp-server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;
|
|
1
|
+
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AA+3CH,iBAAe,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAmGnC;AAGD,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,CAAC"}
|
package/dist/mcp-server.js
CHANGED
|
@@ -915,7 +915,9 @@ function checkModels() {
|
|
|
915
915
|
return {
|
|
916
916
|
name: 'models',
|
|
917
917
|
status: 'warn',
|
|
918
|
-
message:
|
|
918
|
+
message: 'No ONNX models found. Trillboards does not distribute blazeface.onnx/yamnet.onnx; ' +
|
|
919
|
+
'place them in the models directory or fetch from your own origin with: ' +
|
|
920
|
+
'trillboards-edge download-models --from <url>',
|
|
919
921
|
details: { searchedDirs: modelDirs.map((d) => path.resolve(d)) },
|
|
920
922
|
};
|
|
921
923
|
}
|