@trillboards/edge-sdk 0.2.4 → 0.2.6
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 -4
- 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-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 +533 -0
- package/dist/CloudSensingManager.d.ts.map +1 -0
- package/dist/CloudSensingManager.js +1254 -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 +216 -0
- package/dist/autoSense.d.ts.map +1 -0
- package/dist/autoSense.js +521 -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 +511 -21
- 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 +13 -12
- 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 +41 -18
- 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 +268 -0
- package/dist/localSensing.d.ts.map +1 -0
- package/dist/localSensing.js +492 -0
- package/dist/localSensing.js.map +1 -0
- package/dist/modelIntegrity.d.ts +59 -0
- package/dist/modelIntegrity.d.ts.map +1 -0
- package/dist/modelIntegrity.js +165 -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 +68 -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,499 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.MAX_CHANNELS = void 0;
|
|
37
|
+
exports.buildChannelRtspUrl = buildChannelRtspUrl;
|
|
38
|
+
exports.expandChannelRange = expandChannelRange;
|
|
39
|
+
exports.genericChannelRangeNeedsPath = genericChannelRangeNeedsPath;
|
|
40
|
+
exports.parseProbeMatches = parseProbeMatches;
|
|
41
|
+
exports.buildProbeMessage = buildProbeMessage;
|
|
42
|
+
exports.discoverOnvifHosts = discoverOnvifHosts;
|
|
43
|
+
exports.maskRtspUrl = maskRtspUrl;
|
|
44
|
+
exports.formatCamerasTable = formatCamerasTable;
|
|
45
|
+
exports.runDiscover = runDiscover;
|
|
46
|
+
exports.inferRecorderBrandHint = inferRecorderBrandHint;
|
|
47
|
+
exports.sweepLanForRecorders = sweepLanForRecorders;
|
|
48
|
+
/**
|
|
49
|
+
* discovery.ts — BYO-CCTV camera auto-discovery for the connector.
|
|
50
|
+
*
|
|
51
|
+
* Two zero-config ways for a non-technical venue owner to find their cameras
|
|
52
|
+
* without typing an RTSP URL per feed:
|
|
53
|
+
*
|
|
54
|
+
* 1. ONVIF WS-Discovery (LAN multicast) — most IP cameras + NVRs answer a
|
|
55
|
+
* standard, UNAUTHENTICATED WS-Discovery Probe on 239.255.255.250:3702 with
|
|
56
|
+
* their device-service address + scopes. We collect the responders' IPs +
|
|
57
|
+
* friendly names so the operator learns what's on their network. (The
|
|
58
|
+
* browser portal can't do this — it can't reach a LAN multicast/RTSP port —
|
|
59
|
+
* which is exactly why it lives in the installed connector.)
|
|
60
|
+
* 2. NVR channel-range expansion — given an NVR's base IP + brand + channel
|
|
61
|
+
* count, build the per-channel RTSP URLs from the SAME vendor presets the
|
|
62
|
+
* portal uses (modern-trillboards-portal lib/add-camera.buildRtspUrl), so a
|
|
63
|
+
* single 8-channel DVR becomes 8 feeds from one input.
|
|
64
|
+
*
|
|
65
|
+
* The output shape ({ name, rtspUrl, cameraKey }) matches what the portal's
|
|
66
|
+
* POST /v2/earner/cameras/register consumes — discovery NEVER provisions on its
|
|
67
|
+
* own (the connector holds per-feed device tokens, not the earner's auth); it
|
|
68
|
+
* just tells the operator what to register. No new provisioning path.
|
|
69
|
+
*
|
|
70
|
+
* Network I/O is confined to discoverOnvifHosts(); the value-bearing logic
|
|
71
|
+
* (parseProbeMatches, expandChannelRange, buildChannelRtspUrl) is pure + unit
|
|
72
|
+
* tested with fixtures — no camera required.
|
|
73
|
+
*/
|
|
74
|
+
const dgram = __importStar(require("dgram"));
|
|
75
|
+
const crypto_1 = require("crypto");
|
|
76
|
+
/** Hard ceiling — matches the backend provisioning cap (≤64 cameras/register). */
|
|
77
|
+
exports.MAX_CHANNELS = 64;
|
|
78
|
+
const WS_DISCOVERY_ADDR = '239.255.255.250';
|
|
79
|
+
const WS_DISCOVERY_PORT = 3702;
|
|
80
|
+
// ── Pure helpers (unit-tested) ───────────────────────────────────────────────
|
|
81
|
+
function slugify(name) {
|
|
82
|
+
const slug = (name || '')
|
|
83
|
+
.toLowerCase()
|
|
84
|
+
.trim()
|
|
85
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
86
|
+
.replace(/^-+|-+$/g, '');
|
|
87
|
+
return slug || 'camera';
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Deterministic per-feed key `<slug>-<8hex(seed)>`. Stable across re-discovery
|
|
91
|
+
* (seeded by host:brand:channel) so a re-register reuses the same screen rather
|
|
92
|
+
* than minting a sibling — the register endpoint treats cameraKey as the
|
|
93
|
+
* idempotency key.
|
|
94
|
+
*/
|
|
95
|
+
function cameraKeyFor(name, seed) {
|
|
96
|
+
const h = (0, crypto_1.createHash)('sha1').update(seed).digest('hex').slice(0, 8);
|
|
97
|
+
return `${slugify(name)}-${h}`;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Build one channel's RTSP URL. Vendor paths are byte-identical to the portal's
|
|
101
|
+
* buildRtspUrl so the same NVR yields the same URLs on both surfaces.
|
|
102
|
+
*/
|
|
103
|
+
function buildChannelRtspUrl({ brand, host, port, username, password, channel, path, }) {
|
|
104
|
+
const h = (host || '').trim();
|
|
105
|
+
const p = String(port ?? '554').trim() || '554';
|
|
106
|
+
const u = (username || '').trim();
|
|
107
|
+
const pw = password ?? '';
|
|
108
|
+
const ch = String(channel ?? '1').trim() || '1';
|
|
109
|
+
const auth = u ? `${encodeURIComponent(u)}:${encodeURIComponent(pw)}@` : '';
|
|
110
|
+
const base = `rtsp://${auth}${h}:${p}`;
|
|
111
|
+
switch (brand) {
|
|
112
|
+
case 'hikvision':
|
|
113
|
+
return `${base}/Streaming/Channels/${ch}01`;
|
|
114
|
+
case 'dahua':
|
|
115
|
+
return `${base}/cam/realmonitor?channel=${ch}&subtype=0`;
|
|
116
|
+
case 'amcrest':
|
|
117
|
+
return `${base}/cam/realmonitor?channel=${ch}&subtype=0`;
|
|
118
|
+
case 'amcrest-recorder':
|
|
119
|
+
return `${base}/h264Preview_${ch.padStart(2, '0')}_main`;
|
|
120
|
+
case 'reolink':
|
|
121
|
+
return `${base}/h264Preview_${ch}_main`;
|
|
122
|
+
case 'honeywell':
|
|
123
|
+
// 35/60 Series mainstream, documented with a worked example in Honeywell
|
|
124
|
+
// 800-26904-F. The `/live/<Camera ID>/<Streaming ID>` form is the only
|
|
125
|
+
// Honeywell one with a channel slot; 30 Series cameras use the editable
|
|
126
|
+
// access name (default `live.sdp`), reachable via the "generic" brand.
|
|
127
|
+
return `${base}/live/${ch}/1`;
|
|
128
|
+
case 'generic':
|
|
129
|
+
default: {
|
|
130
|
+
const raw = (path ?? '').trim();
|
|
131
|
+
const suffix = raw ? (raw.startsWith('/') ? raw : `/${raw}`) : '/';
|
|
132
|
+
return `${base}${suffix}`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Expand an NVR's base connection into one DiscoveredCamera per channel. Caps at
|
|
138
|
+
* MAX_CHANNELS; returns [] for count<=0. Branded presets bake the channel into
|
|
139
|
+
* the path; "generic" uses the `{ch}` template.
|
|
140
|
+
*/
|
|
141
|
+
function expandChannelRange(opts) {
|
|
142
|
+
const start = Number.isFinite(opts.startChannel) ? Number(opts.startChannel) : 1;
|
|
143
|
+
const count = Math.max(0, Math.min(exports.MAX_CHANNELS, Math.floor(Number(opts.count) || 0)));
|
|
144
|
+
const prefix = (opts.namePrefix || 'Channel').trim() || 'Channel';
|
|
145
|
+
const out = [];
|
|
146
|
+
for (let i = 0; i < count; i++) {
|
|
147
|
+
const channel = start + i;
|
|
148
|
+
const name = `${prefix} ${channel}`;
|
|
149
|
+
const pathForCh = opts.brand === 'generic' && opts.path ? opts.path.replace(/\{ch\}/g, String(channel)) : undefined;
|
|
150
|
+
const rtspUrl = buildChannelRtspUrl({
|
|
151
|
+
brand: opts.brand,
|
|
152
|
+
host: opts.host,
|
|
153
|
+
port: opts.port,
|
|
154
|
+
username: opts.username,
|
|
155
|
+
password: opts.password,
|
|
156
|
+
channel,
|
|
157
|
+
path: pathForCh,
|
|
158
|
+
});
|
|
159
|
+
out.push({
|
|
160
|
+
name,
|
|
161
|
+
rtspUrl,
|
|
162
|
+
cameraKey: cameraKeyFor(name, `${opts.host}:${opts.brand}:${channel}`),
|
|
163
|
+
source: 'channel-range',
|
|
164
|
+
host: opts.host,
|
|
165
|
+
channel,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return out;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Generic NVRs need a per-channel path template (`{ch}`) so each channel yields a
|
|
172
|
+
* DISTINCT URL — without it, multi-channel generic expansion produces N identical
|
|
173
|
+
* `rtsp://host:port/` feeds (duplicate/invalid). Returns true when generic
|
|
174
|
+
* channel-range expansion must be blocked. (Branded presets bake the channel into
|
|
175
|
+
* their path, so they never need this.)
|
|
176
|
+
*/
|
|
177
|
+
function genericChannelRangeNeedsPath(brand, count, path) {
|
|
178
|
+
return brand === 'generic' && count > 1 && !((path ?? '').includes('{ch}'));
|
|
179
|
+
}
|
|
180
|
+
function decodeXml(s) {
|
|
181
|
+
return s
|
|
182
|
+
.replace(/</g, '<')
|
|
183
|
+
.replace(/>/g, '>')
|
|
184
|
+
.replace(/"/g, '"')
|
|
185
|
+
.replace(/'/g, "'")
|
|
186
|
+
.replace(/&/g, '&');
|
|
187
|
+
}
|
|
188
|
+
/** All text contents of <ns:LocalName>…</LocalName> (any/no namespace prefix). */
|
|
189
|
+
function matchAllTagText(xml, localName) {
|
|
190
|
+
const re = new RegExp(`<(?:[A-Za-z0-9_]+:)?${localName}\\b[^>]*>([\\s\\S]*?)<\\/(?:[A-Za-z0-9_]+:)?${localName}>`, 'gi');
|
|
191
|
+
const out = [];
|
|
192
|
+
let m;
|
|
193
|
+
while ((m = re.exec(xml)) !== null)
|
|
194
|
+
out.push(decodeXml(m[1].trim()));
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
function hostFromUrl(url) {
|
|
198
|
+
const m = url.match(/^https?:\/\/([^/:]+)/i);
|
|
199
|
+
return m ? m[1] : null;
|
|
200
|
+
}
|
|
201
|
+
function nameFromScopes(scopes) {
|
|
202
|
+
// ONVIF scopes look like onvif://www.onvif.org/name/My%20Camera (URL-encoded).
|
|
203
|
+
const nameScope = scopes.find((s) => /\/name\//i.test(s));
|
|
204
|
+
if (!nameScope)
|
|
205
|
+
return undefined;
|
|
206
|
+
const tail = nameScope.split('/name/')[1];
|
|
207
|
+
if (!tail)
|
|
208
|
+
return undefined;
|
|
209
|
+
try {
|
|
210
|
+
return decodeURIComponent(tail).replace(/[_+]/g, ' ').trim() || undefined;
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return tail.replace(/[_+]/g, ' ').trim() || undefined;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function uniq(arr) {
|
|
217
|
+
return [...new Set(arr)];
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Parse ONVIF WS-Discovery ProbeMatch datagrams into hosts. Namespace-agnostic
|
|
221
|
+
* (vendors use d:/wsdd:/default prefixes) — matches local element names. Pure:
|
|
222
|
+
* feed it captured datagrams, no network. Responders are deduped by IP.
|
|
223
|
+
*/
|
|
224
|
+
function parseProbeMatches(xmls) {
|
|
225
|
+
const byHost = new Map();
|
|
226
|
+
for (const xml of xmls) {
|
|
227
|
+
if (!xml)
|
|
228
|
+
continue;
|
|
229
|
+
const xaddrs = matchAllTagText(xml, 'XAddrs')
|
|
230
|
+
.join(' ')
|
|
231
|
+
.split(/\s+/)
|
|
232
|
+
.filter((s) => /^https?:\/\//i.test(s));
|
|
233
|
+
if (xaddrs.length === 0)
|
|
234
|
+
continue;
|
|
235
|
+
const scopes = matchAllTagText(xml, 'Scopes')
|
|
236
|
+
.join(' ')
|
|
237
|
+
.split(/\s+/)
|
|
238
|
+
.filter(Boolean);
|
|
239
|
+
const host = hostFromUrl(xaddrs[0]);
|
|
240
|
+
if (!host)
|
|
241
|
+
continue;
|
|
242
|
+
const merged = byHost.get(host) || { host, xaddrs: [], scopes: [] };
|
|
243
|
+
merged.xaddrs = uniq([...merged.xaddrs, ...xaddrs]);
|
|
244
|
+
merged.scopes = uniq([...merged.scopes, ...scopes]);
|
|
245
|
+
merged.name = nameFromScopes(merged.scopes) || merged.name;
|
|
246
|
+
byHost.set(host, merged);
|
|
247
|
+
}
|
|
248
|
+
return [...byHost.values()];
|
|
249
|
+
}
|
|
250
|
+
/** RFC4122 v4 from crypto randomness (Math.random-free). */
|
|
251
|
+
function randomMessageId() {
|
|
252
|
+
const b = (0, crypto_1.randomBytes)(16);
|
|
253
|
+
b[6] = (b[6] & 0x0f) | 0x40;
|
|
254
|
+
b[8] = (b[8] & 0x3f) | 0x80;
|
|
255
|
+
const h = b.toString('hex');
|
|
256
|
+
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
|
|
257
|
+
}
|
|
258
|
+
/** Standard ONVIF WS-Discovery Probe envelope (NetworkVideoTransmitter). */
|
|
259
|
+
function buildProbeMessage(messageId) {
|
|
260
|
+
return [
|
|
261
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
262
|
+
'<e:Envelope xmlns:e="http://www.w3.org/2003/05/soap-envelope"',
|
|
263
|
+
' xmlns:w="http://schemas.xmlsoap.org/ws/2004/08/addressing"',
|
|
264
|
+
' xmlns:d="http://schemas.xmlsoap.org/ws/2005/04/discovery"',
|
|
265
|
+
' xmlns:dn="http://www.onvif.org/ver10/network/wsdl">',
|
|
266
|
+
'<e:Header>',
|
|
267
|
+
`<w:MessageID>uuid:${messageId}</w:MessageID>`,
|
|
268
|
+
'<w:To e:mustUnderstand="true">urn:schemas-xmlsoap-org:ws:2005:04:discovery</w:To>',
|
|
269
|
+
'<w:Action e:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</w:Action>',
|
|
270
|
+
'</e:Header>',
|
|
271
|
+
'<e:Body><d:Probe><d:Types>dn:NetworkVideoTransmitter</d:Types></d:Probe></e:Body>',
|
|
272
|
+
'</e:Envelope>',
|
|
273
|
+
].join('');
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Send a WS-Discovery Probe and collect ONVIF responders for `timeoutMs`. Never
|
|
277
|
+
* rejects — a socket error or no responders resolves to whatever was collected
|
|
278
|
+
* (often []), so the CLI can always print a friendly fallback. Clamped 1–30s.
|
|
279
|
+
*/
|
|
280
|
+
function discoverOnvifHosts(opts = {}) {
|
|
281
|
+
const timeoutMs = Math.max(1000, Math.min(30000, opts.timeoutMs ?? 4000));
|
|
282
|
+
return new Promise((resolve) => {
|
|
283
|
+
const responses = [];
|
|
284
|
+
const socket = dgram.createSocket({ type: 'udp4', reuseAddr: true });
|
|
285
|
+
let done = false;
|
|
286
|
+
const finish = () => {
|
|
287
|
+
if (done)
|
|
288
|
+
return;
|
|
289
|
+
done = true;
|
|
290
|
+
clearTimeout(timer);
|
|
291
|
+
try {
|
|
292
|
+
socket.close();
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
/* already closed */
|
|
296
|
+
}
|
|
297
|
+
resolve(parseProbeMatches(responses));
|
|
298
|
+
};
|
|
299
|
+
socket.on('error', () => finish());
|
|
300
|
+
socket.on('message', (msg) => {
|
|
301
|
+
responses.push(msg.toString('utf8'));
|
|
302
|
+
});
|
|
303
|
+
socket.bind(() => {
|
|
304
|
+
try {
|
|
305
|
+
socket.setBroadcast(true);
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
/* not fatal */
|
|
309
|
+
}
|
|
310
|
+
const probe = Buffer.from(buildProbeMessage(randomMessageId()), 'utf8');
|
|
311
|
+
socket.send(probe, 0, probe.length, WS_DISCOVERY_PORT, WS_DISCOVERY_ADDR, (err) => {
|
|
312
|
+
if (err)
|
|
313
|
+
finish();
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
const timer = setTimeout(finish, timeoutMs);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
/** Mask the password segment of an rtsp://user:pass@host URL for display. */
|
|
320
|
+
function maskRtspUrl(url) {
|
|
321
|
+
return url.replace(/(rtsp:\/\/[^:/@]+:)([^@]*)(@)/, (_m, pre, pw, at) => pw ? `${pre}${'•'.repeat(Math.min(pw.length, 8))}${at}` : `${pre}${at}`);
|
|
322
|
+
}
|
|
323
|
+
/** Numbered, password-masked one-line-per-feed list for the CLI. */
|
|
324
|
+
function formatCamerasTable(cameras) {
|
|
325
|
+
if (cameras.length === 0)
|
|
326
|
+
return 'No cameras found.';
|
|
327
|
+
return cameras
|
|
328
|
+
.map((c, i) => ` ${String(i + 1).padStart(2)}. ${c.name} → ${maskRtspUrl(c.rtspUrl)}`)
|
|
329
|
+
.join('\n');
|
|
330
|
+
}
|
|
331
|
+
function getArg(args, flag) {
|
|
332
|
+
const i = args.indexOf(flag);
|
|
333
|
+
return i >= 0 && args[i + 1] ? args[i + 1] : undefined;
|
|
334
|
+
}
|
|
335
|
+
function emit(cameras, asJson) {
|
|
336
|
+
if (asJson) {
|
|
337
|
+
console.log(JSON.stringify({ cameras }, null, 2));
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
console.log(`Built ${cameras.length} feed${cameras.length === 1 ? '' : 's'}:\n`);
|
|
341
|
+
console.log(formatCamerasTable(cameras));
|
|
342
|
+
console.log('\nAdd these in the Trillboards portal → Add cameras → "I have an NVR".');
|
|
343
|
+
console.log('(The connector never stores camera credentials — they travel in the short-lived claim code.)');
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* CLI entrypoint for `trillboards-edge discover` (lazy-loaded from cli.ts).
|
|
347
|
+
*
|
|
348
|
+
* discover ONVIF LAN scan (list IPs)
|
|
349
|
+
* discover --nvr <ip> --brand hikvision --channels 8 expand an NVR's channels
|
|
350
|
+
* [--user u --pass p --port 554 --start 1 --path /live/{ch}] [--json]
|
|
351
|
+
*/
|
|
352
|
+
async function runDiscover(args) {
|
|
353
|
+
const asJson = args.includes('--json');
|
|
354
|
+
const nvr = getArg(args, '--nvr');
|
|
355
|
+
const channels = parseInt(getArg(args, '--channels') || '0', 10);
|
|
356
|
+
// ── Channel-range expansion: operator already knows the NVR IP. ─────────────
|
|
357
|
+
if (nvr && channels > 0) {
|
|
358
|
+
const brand = getArg(args, '--brand') || 'hikvision';
|
|
359
|
+
const path = getArg(args, '--path');
|
|
360
|
+
if (genericChannelRangeNeedsPath(brand, channels, path)) {
|
|
361
|
+
console.error('Generic NVRs need a per-channel path template so each channel gets a distinct URL.');
|
|
362
|
+
console.error('Pass --path with a {ch} placeholder, e.g.:');
|
|
363
|
+
console.error(` trillboards-edge discover --nvr ${nvr} --brand generic --channels ${channels} --path "/live/{ch}"`);
|
|
364
|
+
process.exitCode = 1;
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const cameras = expandChannelRange({
|
|
368
|
+
brand,
|
|
369
|
+
host: nvr,
|
|
370
|
+
count: channels,
|
|
371
|
+
startChannel: parseInt(getArg(args, '--start') || '1', 10),
|
|
372
|
+
port: getArg(args, '--port'),
|
|
373
|
+
username: getArg(args, '--user'),
|
|
374
|
+
password: getArg(args, '--pass'),
|
|
375
|
+
path,
|
|
376
|
+
});
|
|
377
|
+
emit(cameras, asJson);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
// ── ONVIF LAN probe: find what's on the network. ───────────────────────────
|
|
381
|
+
if (!asJson)
|
|
382
|
+
console.log('Scanning the local network for ONVIF cameras (a few seconds)…\n');
|
|
383
|
+
const hosts = await discoverOnvifHosts({
|
|
384
|
+
timeoutMs: parseInt(getArg(args, '--timeout') || '4000', 10),
|
|
385
|
+
});
|
|
386
|
+
if (asJson) {
|
|
387
|
+
console.log(JSON.stringify({ onvifHosts: hosts }, null, 2));
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (hosts.length === 0) {
|
|
391
|
+
console.log('No ONVIF cameras answered on this network.');
|
|
392
|
+
console.log('If you have an NVR/DVR, expand its channels directly:');
|
|
393
|
+
console.log(' trillboards-edge discover --nvr <NVR-IP> --brand hikvision --channels 8 --user admin --pass ****');
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
console.log(`Found ${hosts.length} camera${hosts.length === 1 ? '' : 's'} on your network:\n`);
|
|
397
|
+
hosts.forEach((h, i) => {
|
|
398
|
+
console.log(` ${String(i + 1).padStart(2)}. ${h.name || h.host} (${h.host})`);
|
|
399
|
+
});
|
|
400
|
+
console.log('\nTo turn one into feeds, expand its channels:');
|
|
401
|
+
console.log(' trillboards-edge discover --nvr <IP-above> --brand <hikvision|dahua|amcrest-recorder|reolink|generic> --channels <N> --user admin --pass ****');
|
|
402
|
+
console.log('\nThen add them in the Trillboards portal → Add cameras.');
|
|
403
|
+
}
|
|
404
|
+
function inferRecorderBrandHint(openPorts) {
|
|
405
|
+
return openPorts.includes(34567) ? 'xmeye' : undefined;
|
|
406
|
+
}
|
|
407
|
+
function defaultLocalAddresses() {
|
|
408
|
+
// Lazy os import keeps the dgram-only top of this module unchanged.
|
|
409
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
410
|
+
const os = require('os');
|
|
411
|
+
const out = [];
|
|
412
|
+
for (const addrs of Object.values(os.networkInterfaces())) {
|
|
413
|
+
for (const a of addrs || []) {
|
|
414
|
+
if (a.family === 'IPv4' && !a.internal && a.address)
|
|
415
|
+
out.push(a.address);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return out;
|
|
419
|
+
}
|
|
420
|
+
function defaultTryConnect(host, port, timeoutMs) {
|
|
421
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
422
|
+
const net = require('net');
|
|
423
|
+
return new Promise((resolve) => {
|
|
424
|
+
const sock = net.connect({ host, port });
|
|
425
|
+
let done = false;
|
|
426
|
+
const settle = (open) => {
|
|
427
|
+
if (done)
|
|
428
|
+
return;
|
|
429
|
+
done = true;
|
|
430
|
+
sock.destroy();
|
|
431
|
+
resolve(open);
|
|
432
|
+
};
|
|
433
|
+
sock.setTimeout(timeoutMs, () => settle(false));
|
|
434
|
+
sock.on('connect', () => settle(true));
|
|
435
|
+
sock.on('error', () => settle(false));
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Sweep the local /24 subnet(s) for recorder-shaped devices by TCP-connecting
|
|
440
|
+
* to RTSP/DVR ports. This is the fallback for the huge installed base of cheap
|
|
441
|
+
* analog DVRs (XMeye/Night Owl/older Swann/Lorex) that answer NO ONVIF
|
|
442
|
+
* WS-Discovery probe — without it, "Scan" finds nothing at exactly the venues
|
|
443
|
+
* this product targets. Shaped like OnvifHost so the setup discovery envelope
|
|
444
|
+
* and the portal's recorder cards treat both sources identically.
|
|
445
|
+
* Never rejects — resolves to [] on any environmental failure.
|
|
446
|
+
*/
|
|
447
|
+
async function sweepLanForRecorders(opts = {}) {
|
|
448
|
+
const ports = opts.ports ?? [554, 8554, 8899, 34567];
|
|
449
|
+
const connectTimeoutMs = opts.connectTimeoutMs ?? 350;
|
|
450
|
+
const concurrency = Math.max(4, Math.min(opts.concurrency ?? 64, 128));
|
|
451
|
+
const tryConnect = opts.tryConnect ?? defaultTryConnect;
|
|
452
|
+
const locals = (opts.localAddresses ?? defaultLocalAddresses)();
|
|
453
|
+
const subnets = opts.subnets ??
|
|
454
|
+
[...new Set(locals.map((ip) => ip.split('.').slice(0, 3).join('.')).filter((s) => s.split('.').length === 3))];
|
|
455
|
+
if (subnets.length === 0)
|
|
456
|
+
return [];
|
|
457
|
+
// Sweep the primary RTSP port first across all hosts; then check secondary
|
|
458
|
+
// ports only on responders' neighbors? No — keep it simple + bounded: all
|
|
459
|
+
// (host, port) pairs, capped by concurrency, ~250ms each; a /24 × 4 ports is
|
|
460
|
+
// ≈1k sockets ≈ 6s at 64-way. Local IPs themselves are skipped.
|
|
461
|
+
const localSet = new Set(locals);
|
|
462
|
+
const targets = [];
|
|
463
|
+
for (const subnet of subnets.slice(0, 2)) {
|
|
464
|
+
for (let i = 1; i <= 254; i++) {
|
|
465
|
+
const host = `${subnet}.${i}`;
|
|
466
|
+
if (localSet.has(host))
|
|
467
|
+
continue;
|
|
468
|
+
for (const port of ports)
|
|
469
|
+
targets.push({ host, port });
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
const open = new Map();
|
|
473
|
+
let next = 0;
|
|
474
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, targets.length) }, async () => {
|
|
475
|
+
while (next < targets.length) {
|
|
476
|
+
const t = targets[next++];
|
|
477
|
+
try {
|
|
478
|
+
if (await tryConnect(t.host, t.port, connectTimeoutMs)) {
|
|
479
|
+
open.set(t.host, [...(open.get(t.host) || []), t.port]);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
catch {
|
|
483
|
+
/* closed */
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}));
|
|
487
|
+
return [...open.entries()].map(([host, rawOpenPorts]) => {
|
|
488
|
+
const openPorts = rawOpenPorts.sort((a, b) => a - b);
|
|
489
|
+
const brandHint = inferRecorderBrandHint(openPorts);
|
|
490
|
+
return {
|
|
491
|
+
host,
|
|
492
|
+
name: brandHint === 'xmeye' ? `NETSurveillance / XMEye recorder at ${host}` : `Recorder at ${host}`,
|
|
493
|
+
openPorts,
|
|
494
|
+
...(brandHint ? { brandHint } : {}),
|
|
495
|
+
xaddrs: [],
|
|
496
|
+
};
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gemmaModelOta — keep the BYO-CCTV connector's on-device LiteRT-LM VLM (Gemma)
|
|
3
|
+
* up to date over the air, REUSING the generic @trillboards/edge-core
|
|
4
|
+
* `ModelManager` (atomic .tmp+rename download, SHA-256 verify, redirect-follow
|
|
5
|
+
* for S3 presigned URLs, NO size cap — purpose-built for the ~2.4 GB `.litertlm`
|
|
6
|
+
* file). This is NOT `@trillboards/edge-federated`'s `FederatedModelClient`,
|
|
7
|
+
* whose hard 500 MB cap is for tiny aggregated slice weights.
|
|
8
|
+
*
|
|
9
|
+
* How it reuses ModelManager + the existing /v1/federated/models channel:
|
|
10
|
+
* - The model is registered under modelType `gemma-vlm`, whose `filePath` is
|
|
11
|
+
* the litert-lm STORE path (`~/.litert-lm/models/<id>/model.litertlm`). The
|
|
12
|
+
* litert-lm registry IS the filesystem (no manifest), so ModelManager's
|
|
13
|
+
* atomic download straight INTO the store path is itself the drop-in update.
|
|
14
|
+
* - ModelManager.checkForUpdates polls `GET /v1/federated/models?
|
|
15
|
+
* deviceFingerprint=X&modelType=gemma-vlm`; the server returns the version +
|
|
16
|
+
* an S3 presigned `downloadUrl` from the existing sensing_model_registry SoR.
|
|
17
|
+
* - A model pre-bundled into the installer is recorded as already-installed via
|
|
18
|
+
* a version marker written next to the store file by setup-litertlm, so the
|
|
19
|
+
* first check does NOT re-download the bundled model.
|
|
20
|
+
* - On `modelUpdated` we clear stale XNNPACK caches (the new weights must not
|
|
21
|
+
* reuse the old compiled graph) and RESTART `litert-lm serve` (its engine is
|
|
22
|
+
* lazy-loaded + cached in memory per model id, so a file swap alone is not
|
|
23
|
+
* picked up until restart).
|
|
24
|
+
*
|
|
25
|
+
* Entirely NON-FATAL: any failure here only means the connector keeps serving its
|
|
26
|
+
* current model — it must never break sensing. Node built-ins only (survives
|
|
27
|
+
* bundling into the self-contained connector; trivially mockable in tests).
|
|
28
|
+
*/
|
|
29
|
+
import type { EdgeAgentConfig } from './config';
|
|
30
|
+
/** The connector-facing OTA modelType for the on-device Gemma VLM. */
|
|
31
|
+
export declare const GEMMA_VLM_MODEL_TYPE = "gemma-vlm";
|
|
32
|
+
/**
|
|
33
|
+
* The slice of edge-core's ModelManager this module drives. Narrowed so tests can
|
|
34
|
+
* inject a fake without a real network/file ModelManager.
|
|
35
|
+
*/
|
|
36
|
+
export interface ModelManagerLike {
|
|
37
|
+
registerModel(name: string, filePath: string, seed?: {
|
|
38
|
+
version?: string;
|
|
39
|
+
sha256?: string;
|
|
40
|
+
}): void;
|
|
41
|
+
initialize(): Promise<void>;
|
|
42
|
+
start(): void;
|
|
43
|
+
stop(): void;
|
|
44
|
+
on(event: string, listener: (...args: any[]) => void): void;
|
|
45
|
+
}
|
|
46
|
+
export interface GemmaModelOtaDeps {
|
|
47
|
+
/** Build the ModelManager (default: the real @trillboards/edge-core one). */
|
|
48
|
+
createModelManager?: (opts: {
|
|
49
|
+
modelsDir: string;
|
|
50
|
+
apiBaseUrl: string;
|
|
51
|
+
deviceFingerprint: string;
|
|
52
|
+
checkIntervalMs?: number;
|
|
53
|
+
autoUpdate?: boolean;
|
|
54
|
+
}) => ModelManagerLike;
|
|
55
|
+
/** Restart `litert-lm serve` after a model update (default: platform-aware). */
|
|
56
|
+
restartServe?: () => Promise<void>;
|
|
57
|
+
/** Log sink (default: console.log). */
|
|
58
|
+
log?: (msg: string) => void;
|
|
59
|
+
/** Override the resolved litert-lm store model path (tests). */
|
|
60
|
+
storeModelPath?: string;
|
|
61
|
+
/** Platform override (default process.platform). */
|
|
62
|
+
platform?: NodeJS.Platform;
|
|
63
|
+
/** Home dir override (default os.homedir()). */
|
|
64
|
+
homedir?: string;
|
|
65
|
+
/** Env override (default process.env). */
|
|
66
|
+
env?: NodeJS.ProcessEnv;
|
|
67
|
+
/** execFile override for the serve restart (default child_process.execFile). */
|
|
68
|
+
execFileFn?: (cmd: string, args: string[]) => Promise<void>;
|
|
69
|
+
/** uid for the macOS `launchctl kickstart gui/<uid>` domain (default process.getuid()). */
|
|
70
|
+
uid?: number;
|
|
71
|
+
}
|
|
72
|
+
/** Handle returned by startGemmaModelOta so the run-mode can stop it on shutdown. */
|
|
73
|
+
export interface GemmaModelOtaHandle {
|
|
74
|
+
stop(): void;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Resolve the litert-lm STORE path for a model id:
|
|
78
|
+
* ${LITERT_LM_DIR:-~/.litert-lm}/models/<id>/model.litertlm
|
|
79
|
+
* This is exactly where `litert-lm serve` reads the model from (the registry is
|
|
80
|
+
* the filesystem), so writing here IS the model swap.
|
|
81
|
+
*/
|
|
82
|
+
export declare function resolveLiteRtLmStoreModelPath(opts: {
|
|
83
|
+
modelId: string;
|
|
84
|
+
env?: NodeJS.ProcessEnv;
|
|
85
|
+
homedir?: string;
|
|
86
|
+
}): string;
|
|
87
|
+
/**
|
|
88
|
+
* Read the bundled/installed version marker written by setup-litertlm next to the
|
|
89
|
+
* store model. Returns null when absent/unreadable (the model may not be
|
|
90
|
+
* installed yet — in which case the OTA will simply download it, a bonus
|
|
91
|
+
* self-heal). NEVER throws.
|
|
92
|
+
*/
|
|
93
|
+
export declare function readBundledModelVersion(storeModelPath: string): string | null;
|
|
94
|
+
/**
|
|
95
|
+
* Delete stale XNNPACK cache siblings of the store model. litert-lm compiles the
|
|
96
|
+
* model graph into `model.litertlm.*xnnpack_cache*` files on first serve; after a
|
|
97
|
+
* weights swap those caches are stale and MUST be cleared so the new model is
|
|
98
|
+
* recompiled cleanly (a same-size replace would otherwise silently reuse the old
|
|
99
|
+
* compiled graph). NEVER throws.
|
|
100
|
+
*/
|
|
101
|
+
export declare function clearStaleXnnpackCaches(storeModelPath: string, log?: (m: string) => void): void;
|
|
102
|
+
/**
|
|
103
|
+
* Default platform-aware `litert-lm serve` restart. The serve process caches the
|
|
104
|
+
* model engine in memory, so after a file swap it must be restarted to load the
|
|
105
|
+
* new weights.
|
|
106
|
+
* macOS: launchctl kickstart -k gui/<uid>/com.trillboards.litertlm
|
|
107
|
+
* Windows: schtasks /End then /Run on TrillboardsLiteRtLm
|
|
108
|
+
* Linux: no managed serve unit in this repo — log + rely on the next natural
|
|
109
|
+
* restart (KeepAlive supervisors, if any, reload the swapped file).
|
|
110
|
+
*/
|
|
111
|
+
export declare function restartLiteRtLmServe(deps?: GemmaModelOtaDeps): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Start the gemma-vlm OTA loop. Returns a handle (or null when disabled / not
|
|
114
|
+
* applicable). NON-FATAL: a thrown error is swallowed (logged) so OTA wiring can
|
|
115
|
+
* never break the sensing run mode.
|
|
116
|
+
*/
|
|
117
|
+
export declare function startGemmaModelOta(config: EdgeAgentConfig, deps?: GemmaModelOtaDeps): GemmaModelOtaHandle | null;
|
|
118
|
+
//# sourceMappingURL=gemmaModelOta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemmaModelOta.d.ts","sourceRoot":"","sources":["../src/gemmaModelOta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,sEAAsE;AACtE,eAAO,MAAM,oBAAoB,cAAc,CAAC;AAYhD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAClG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,IAAI,IAAI,CAAC;IACb,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;CAC7D;AAED,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE;QAC1B,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,KAAK,gBAAgB,CAAC;IACvB,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,uCAAuC;IACvC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,gEAAgE;IAChE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,gFAAgF;IAChF,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,2FAA2F;IAC3F,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qFAAqF;AACrF,MAAM,WAAW,mBAAmB;IAClC,IAAI,IAAI,IAAI,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,MAAM,CAOT;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAc7E;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAe,GAAG,IAAI,CAoBzG;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBtF;AAkBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,eAAe,EACvB,IAAI,GAAE,iBAAsB,GAC3B,mBAAmB,GAAG,IAAI,CAoF5B"}
|