@trillboards/edge-sdk 0.2.3 → 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.
Files changed (200) hide show
  1. package/README.md +4 -3
  2. package/deploy/docker/Dockerfile.cloud-sense +117 -0
  3. package/deploy/docker/Dockerfile.cpu +30 -21
  4. package/deploy/docker/Dockerfile.cuda +30 -21
  5. package/deploy/docker/Dockerfile.openvino +30 -20
  6. package/deploy/docker/README.md +9 -4
  7. package/deploy/docker/connector-entrypoint.sh +122 -0
  8. package/deploy/helm/README.md +1 -1
  9. package/deploy/k8s/daemonset.yaml +1 -1
  10. package/deploy/macos/README.md +161 -0
  11. package/deploy/macos/TrillboardsSetupApp.m +603 -0
  12. package/deploy/macos/build-macos-pkg.sh +588 -0
  13. package/deploy/macos/com.trillboards.edge.plist +62 -0
  14. package/deploy/macos/com.trillboards.litertlm.plist +45 -0
  15. package/deploy/macos/pair.command +519 -0
  16. package/deploy/macos/scripts/ensure-litertlm.sh +62 -0
  17. package/deploy/macos/scripts/postinstall +153 -0
  18. package/deploy/macos/scripts/setup-litertlm.sh +272 -0
  19. package/deploy/macos/test-install-pair.sh +383 -0
  20. package/deploy/macos/test-setup-litertlm-reinstall.sh +109 -0
  21. package/deploy/systemd/install.sh +25 -15
  22. package/deploy/systemd/trillboards-edge.service +4 -1
  23. package/deploy/windows/README.md +70 -0
  24. package/deploy/windows/build-windows.ps1 +225 -0
  25. package/deploy/windows/bundle-windows-installer.ps1 +153 -0
  26. package/deploy/windows/check-feeds.js +26 -0
  27. package/deploy/windows/e2e/fixtures/person.jpg +0 -0
  28. package/deploy/windows/e2e/test-local-sense.ps1 +185 -0
  29. package/deploy/windows/install-update.ps1 +101 -0
  30. package/deploy/windows/installer.nsi +138 -0
  31. package/deploy/windows/register-litertlm-task.ps1 +42 -0
  32. package/deploy/windows/register-task.ps1 +44 -0
  33. package/deploy/windows/run-connector.ps1 +69 -0
  34. package/deploy/windows/setup-litertlm.ps1 +361 -0
  35. package/deploy/windows/stop-existing.ps1 +72 -0
  36. package/deploy/windows/trillboards-edge-task.vbs +25 -0
  37. package/deploy/windows/trillboards-edge.cmd +33 -0
  38. package/deploy/windows/trillboards-pair.cmd +276 -0
  39. package/deploy/windows/trillboards-setup.ps1 +456 -0
  40. package/deploy/windows/trillboards-setup.vbs +13 -0
  41. package/dist/AudienceAnalyzeClient.d.ts +136 -0
  42. package/dist/AudienceAnalyzeClient.d.ts.map +1 -0
  43. package/dist/AudienceAnalyzeClient.js +246 -0
  44. package/dist/AudienceAnalyzeClient.js.map +1 -0
  45. package/dist/ChannelProber.d.ts +150 -0
  46. package/dist/ChannelProber.d.ts.map +1 -0
  47. package/dist/ChannelProber.js +887 -0
  48. package/dist/ChannelProber.js.map +1 -0
  49. package/dist/CloudEscalationPolicy.d.ts +143 -0
  50. package/dist/CloudEscalationPolicy.d.ts.map +1 -0
  51. package/dist/CloudEscalationPolicy.js +137 -0
  52. package/dist/CloudEscalationPolicy.js.map +1 -0
  53. package/dist/CloudSensingManager.d.ts +533 -0
  54. package/dist/CloudSensingManager.d.ts.map +1 -0
  55. package/dist/CloudSensingManager.js +1254 -0
  56. package/dist/CloudSensingManager.js.map +1 -0
  57. package/dist/DiscoveryReportClient.d.ts +66 -0
  58. package/dist/DiscoveryReportClient.d.ts.map +1 -0
  59. package/dist/DiscoveryReportClient.js +85 -0
  60. package/dist/DiscoveryReportClient.js.map +1 -0
  61. package/dist/EdgeAgent.d.ts +12 -2
  62. package/dist/EdgeAgent.d.ts.map +1 -1
  63. package/dist/EdgeAgent.js +122 -65
  64. package/dist/EdgeAgent.js.map +1 -1
  65. package/dist/LiteRtVlmProvider.d.ts +193 -0
  66. package/dist/LiteRtVlmProvider.d.ts.map +1 -0
  67. package/dist/LiteRtVlmProvider.js +436 -0
  68. package/dist/LiteRtVlmProvider.js.map +1 -0
  69. package/dist/MosaicSource.d.ts +108 -0
  70. package/dist/MosaicSource.d.ts.map +1 -0
  71. package/dist/MosaicSource.js +274 -0
  72. package/dist/MosaicSource.js.map +1 -0
  73. package/dist/OnvifClient.d.ts +83 -0
  74. package/dist/OnvifClient.d.ts.map +1 -0
  75. package/dist/OnvifClient.js +209 -0
  76. package/dist/OnvifClient.js.map +1 -0
  77. package/dist/PairClient.d.ts +106 -0
  78. package/dist/PairClient.d.ts.map +1 -0
  79. package/dist/PairClient.js +186 -0
  80. package/dist/PairClient.js.map +1 -0
  81. package/dist/RecorderAddressRecovery.d.ts +53 -0
  82. package/dist/RecorderAddressRecovery.d.ts.map +1 -0
  83. package/dist/RecorderAddressRecovery.js +359 -0
  84. package/dist/RecorderAddressRecovery.js.map +1 -0
  85. package/dist/SceneChangeDetector.d.ts +291 -0
  86. package/dist/SceneChangeDetector.d.ts.map +1 -0
  87. package/dist/SceneChangeDetector.js +682 -0
  88. package/dist/SceneChangeDetector.js.map +1 -0
  89. package/dist/SensingProfileClient.d.ts +59 -0
  90. package/dist/SensingProfileClient.d.ts.map +1 -0
  91. package/dist/SensingProfileClient.js +78 -0
  92. package/dist/SensingProfileClient.js.map +1 -0
  93. package/dist/SetupSessionClient.d.ts +114 -0
  94. package/dist/SetupSessionClient.d.ts.map +1 -0
  95. package/dist/SetupSessionClient.js +95 -0
  96. package/dist/SetupSessionClient.js.map +1 -0
  97. package/dist/VlmAdmissionGate.d.ts +126 -0
  98. package/dist/VlmAdmissionGate.d.ts.map +1 -0
  99. package/dist/VlmAdmissionGate.js +166 -0
  100. package/dist/VlmAdmissionGate.js.map +1 -0
  101. package/dist/autoSense.d.ts +216 -0
  102. package/dist/autoSense.d.ts.map +1 -0
  103. package/dist/autoSense.js +521 -0
  104. package/dist/autoSense.js.map +1 -0
  105. package/dist/cli.d.ts +27 -1
  106. package/dist/cli.d.ts.map +1 -1
  107. package/dist/cli.js +511 -21
  108. package/dist/cli.js.map +1 -1
  109. package/dist/combinedSense.d.ts +128 -0
  110. package/dist/combinedSense.d.ts.map +1 -0
  111. package/dist/combinedSense.js +261 -0
  112. package/dist/combinedSense.js.map +1 -0
  113. package/dist/config.d.ts +351 -0
  114. package/dist/config.d.ts.map +1 -1
  115. package/dist/config.js +99 -2
  116. package/dist/config.js.map +1 -1
  117. package/dist/connectorCadence.d.ts +135 -0
  118. package/dist/connectorCadence.d.ts.map +1 -0
  119. package/dist/connectorCadence.js +151 -0
  120. package/dist/connectorCadence.js.map +1 -0
  121. package/dist/dataDir.d.ts +33 -0
  122. package/dist/dataDir.d.ts.map +1 -0
  123. package/dist/dataDir.js +76 -0
  124. package/dist/dataDir.js.map +1 -0
  125. package/dist/deployedCadence.d.ts +167 -0
  126. package/dist/deployedCadence.d.ts.map +1 -0
  127. package/dist/deployedCadence.js +209 -0
  128. package/dist/deployedCadence.js.map +1 -0
  129. package/dist/deployedCadenceSource.d.ts +48 -0
  130. package/dist/deployedCadenceSource.d.ts.map +1 -0
  131. package/dist/deployedCadenceSource.js +74 -0
  132. package/dist/deployedCadenceSource.js.map +1 -0
  133. package/dist/diagnose.d.ts +1 -1
  134. package/dist/diagnose.d.ts.map +1 -1
  135. package/dist/diagnose.js +13 -12
  136. package/dist/diagnose.js.map +1 -1
  137. package/dist/discovery.d.ts +131 -0
  138. package/dist/discovery.d.ts.map +1 -0
  139. package/dist/discovery.js +499 -0
  140. package/dist/discovery.js.map +1 -0
  141. package/dist/gemmaModelOta.d.ts +118 -0
  142. package/dist/gemmaModelOta.d.ts.map +1 -0
  143. package/dist/gemmaModelOta.js +291 -0
  144. package/dist/gemmaModelOta.js.map +1 -0
  145. package/dist/index.d.ts +16 -1
  146. package/dist/index.d.ts.map +1 -1
  147. package/dist/index.js +40 -1
  148. package/dist/index.js.map +1 -1
  149. package/dist/init.d.ts.map +1 -1
  150. package/dist/init.js +41 -18
  151. package/dist/init.js.map +1 -1
  152. package/dist/keepAwake.d.ts +33 -0
  153. package/dist/keepAwake.d.ts.map +1 -0
  154. package/dist/keepAwake.js +54 -0
  155. package/dist/keepAwake.js.map +1 -0
  156. package/dist/legacyRecorderProfile.d.ts +20 -0
  157. package/dist/legacyRecorderProfile.d.ts.map +1 -0
  158. package/dist/legacyRecorderProfile.js +27 -0
  159. package/dist/legacyRecorderProfile.js.map +1 -0
  160. package/dist/localInferenceSchema.d.ts +132 -0
  161. package/dist/localInferenceSchema.d.ts.map +1 -0
  162. package/dist/localInferenceSchema.js +274 -0
  163. package/dist/localInferenceSchema.js.map +1 -0
  164. package/dist/localSenseE2eHarness.d.ts +101 -0
  165. package/dist/localSenseE2eHarness.d.ts.map +1 -0
  166. package/dist/localSenseE2eHarness.js +279 -0
  167. package/dist/localSenseE2eHarness.js.map +1 -0
  168. package/dist/localSensing.d.ts +268 -0
  169. package/dist/localSensing.d.ts.map +1 -0
  170. package/dist/localSensing.js +492 -0
  171. package/dist/localSensing.js.map +1 -0
  172. package/dist/modelIntegrity.d.ts +59 -0
  173. package/dist/modelIntegrity.d.ts.map +1 -0
  174. package/dist/modelIntegrity.js +165 -0
  175. package/dist/modelIntegrity.js.map +1 -0
  176. package/dist/pairVerify.d.ts +100 -0
  177. package/dist/pairVerify.d.ts.map +1 -0
  178. package/dist/pairVerify.js +296 -0
  179. package/dist/pairVerify.js.map +1 -0
  180. package/dist/qrScanner.d.ts +33 -0
  181. package/dist/qrScanner.d.ts.map +1 -0
  182. package/dist/qrScanner.js +103 -0
  183. package/dist/qrScanner.js.map +1 -0
  184. package/dist/runtimePaths.d.ts +10 -0
  185. package/dist/runtimePaths.d.ts.map +1 -0
  186. package/dist/runtimePaths.js +79 -0
  187. package/dist/runtimePaths.js.map +1 -0
  188. package/dist/sensingRunMode.d.ts +68 -0
  189. package/dist/sensingRunMode.d.ts.map +1 -0
  190. package/dist/sensingRunMode.js +346 -0
  191. package/dist/sensingRunMode.js.map +1 -0
  192. package/dist/setupMode.d.ts +98 -0
  193. package/dist/setupMode.d.ts.map +1 -0
  194. package/dist/setupMode.js +828 -0
  195. package/dist/setupMode.js.map +1 -0
  196. package/dist/setupProgress.d.ts +31 -0
  197. package/dist/setupProgress.d.ts.map +1 -0
  198. package/dist/setupProgress.js +173 -0
  199. package/dist/setupProgress.js.map +1 -0
  200. 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(/&lt;/g, '<')
183
+ .replace(/&gt;/g, '>')
184
+ .replace(/&quot;/g, '"')
185
+ .replace(/&apos;/g, "'")
186
+ .replace(/&amp;/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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0GA,kDAyCC;AAoBD,gDA6BC;AASD,oEAMC;AAkDD,8CAsBC;AAYD,8CAeC;AAWD,gDAkCC;AAGD,kCAIC;AAGD,gDAKC;AAyBD,kCA6DC;AAcD,wDAEC;AAyDD,oDAsDC;AAvkBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,6CAA+B;AAC/B,mCAAiD;AAoCjD,kFAAkF;AACrE,QAAA,YAAY,GAAG,EAAE,CAAC;AAE/B,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAC5C,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,gFAAgF;AAEhF,SAAS,OAAO,CAAC,IAAY;IAC3B,MAAM,IAAI,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;SACtB,WAAW,EAAE;SACb,IAAI,EAAE;SACN,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,OAAO,IAAI,IAAI,QAAQ,CAAC;AAC1B,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,IAAY,EAAE,IAAY;IAC9C,MAAM,CAAC,GAAG,IAAA,mBAAU,EAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACjC,CAAC;AAaD;;;GAGG;AACH,SAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,IAAI,GACa;IACjB,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC;IAChD,MAAM,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAClC,MAAM,EAAE,GAAG,QAAQ,IAAI,EAAE,CAAC;IAC1B,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC;IAChD,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,MAAM,IAAI,GAAG,UAAU,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;IAEvC,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,WAAW;YACd,OAAO,GAAG,IAAI,uBAAuB,EAAE,IAAI,CAAC;QAC9C,KAAK,OAAO;YACV,OAAO,GAAG,IAAI,4BAA4B,EAAE,YAAY,CAAC;QAC3D,KAAK,SAAS;YACZ,OAAO,GAAG,IAAI,4BAA4B,EAAE,YAAY,CAAC;QAC3D,KAAK,kBAAkB;YACrB,OAAO,GAAG,IAAI,gBAAgB,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;QAC3D,KAAK,SAAS;YACZ,OAAO,GAAG,IAAI,gBAAgB,EAAE,OAAO,CAAC;QAC1C,KAAK,WAAW;YACd,yEAAyE;YACzE,uEAAuE;YACvE,wEAAwE;YACxE,uEAAuE;YACvE,OAAO,GAAG,IAAI,SAAS,EAAE,IAAI,CAAC;QAChC,KAAK,SAAS,CAAC;QACf,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YAChC,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YACnE,OAAO,GAAG,IAAI,GAAG,MAAM,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC;AACH,CAAC;AAeD;;;;GAIG;AACH,SAAgB,kBAAkB,CAAC,IAAyB;IAC1D,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAsB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3F,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,oBAAY,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACvF,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,SAAS,CAAC,CAAC,IAAI,EAAE,IAAI,SAAS,CAAC;IAClE,MAAM,GAAG,GAAuB,EAAE,CAAC;IACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,KAAK,GAAG,CAAC,CAAC;QAC1B,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC;QACpC,MAAM,SAAS,GACb,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACpG,MAAM,OAAO,GAAG,mBAAmB,CAAC;YAClC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,OAAO;YACP,IAAI,EAAE,SAAS;SAChB,CAAC,CAAC;QACH,GAAG,CAAC,IAAI,CAAC;YACP,IAAI;YACJ,OAAO;YACP,SAAS,EAAE,YAAY,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,IAAI,OAAO,EAAE,CAAC;YACtE,MAAM,EAAE,eAAe;YACvB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO;SACR,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,4BAA4B,CAC1C,KAAoB,EACpB,KAAa,EACb,IAAa;IAEb,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,SAAS,CAAC,CAAS;IAC1B,OAAO,CAAC;SACL,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,kFAAkF;AAClF,SAAS,eAAe,CAAC,GAAW,EAAE,SAAiB;IACrD,MAAM,EAAE,GAAG,IAAI,MAAM,CACnB,uBAAuB,SAAS,+CAA+C,SAAS,GAAG,EAC3F,IAAI,CACL,CAAC;IACF,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,CAAyB,CAAC;IAC9B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI;QAAE,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACrE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC7C,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACzB,CAAC;AAED,SAAS,cAAc,CAAC,MAAgB;IACtC,+EAA+E;IAC/E,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,IAAI,CAAC,SAAS;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,IAAI,CAAC;QACH,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,SAAS,CAAC;IAC5E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,SAAS,CAAC;IACxD,CAAC;AACH,CAAC;AAED,SAAS,IAAI,CAAI,GAAQ;IACvB,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3B,CAAC;AAED;;;;GAIG;AACH,SAAgB,iBAAiB,CAAC,IAAc;IAC9C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC5C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,EAAE,QAAQ,CAAC;aAC1C,IAAI,CAAC,GAAG,CAAC;aACT,KAAK,CAAC,KAAK,CAAC;aACZ,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAClC,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,EAAE,QAAQ,CAAC;aAC1C,IAAI,CAAC,GAAG,CAAC;aACT,KAAK,CAAC,KAAK,CAAC;aACZ,MAAM,CAAC,OAAO,CAAC,CAAC;QACnB,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,MAAM,MAAM,GAAc,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QAC/E,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,IAAI,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC;QAC3D,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED,4DAA4D;AAC5D,SAAS,eAAe;IACtB,MAAM,CAAC,GAAG,IAAA,oBAAW,EAAC,EAAE,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5B,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5B,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;AACnG,CAAC;AAED,4EAA4E;AAC5E,SAAgB,iBAAiB,CAAC,SAAiB;IACjD,OAAO;QACL,wCAAwC;QACxC,+DAA+D;QAC/D,6DAA6D;QAC7D,4DAA4D;QAC5D,sDAAsD;QACtD,YAAY;QACZ,qBAAqB,SAAS,gBAAgB;QAC9C,mFAAmF;QACnF,oGAAoG;QACpG,aAAa;QACb,mFAAmF;QACnF,eAAe;KAChB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACb,CAAC;AAMD;;;;GAIG;AACH,SAAgB,kBAAkB,CAAC,OAA8B,EAAE;IACjE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,KAAM,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC;IAC3E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrE,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,IAAI,IAAI;gBAAE,OAAO;YACjB,IAAI,GAAG,IAAI,CAAC;YACZ,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC;YAAC,MAAM,CAAC;gBACP,oBAAoB;YACtB,CAAC;YACD,OAAO,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC;QACxC,CAAC,CAAC;QACF,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;QACnC,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;YAC3B,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACf,IAAI,CAAC;gBACH,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,eAAe;YACjB,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,eAAe,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;YACxE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,CAAC,GAAG,EAAE,EAAE;gBAChF,IAAI,GAAG;oBAAE,MAAM,EAAE,CAAC;YACpB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,6EAA6E;AAC7E,SAAgB,WAAW,CAAC,GAAW;IACrC,OAAO,GAAG,CAAC,OAAO,CAAC,+BAA+B,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CACtE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,EAAE,EAAE,CACxE,CAAC;AACJ,CAAC;AAED,oEAAoE;AACpE,SAAgB,kBAAkB,CAAC,OAA2B;IAC5D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,mBAAmB,CAAC;IACrD,OAAO,OAAO;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,QAAQ,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;SACxF,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,SAAS,MAAM,CAAC,IAAc,EAAE,IAAY;IAC1C,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACzD,CAAC;AAED,SAAS,IAAI,CAAC,OAA2B,EAAE,MAAe;IACxD,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAClD,OAAO;IACT,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,SAAS,OAAO,CAAC,MAAM,QAAQ,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;IACjF,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,wEAAwE,CAAC,CAAC;IACtF,OAAO,CAAC,GAAG,CAAC,8FAA8F,CAAC,CAAC;AAC9G,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,WAAW,CAAC,IAAc;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;IAEjE,+EAA+E;IAC/E,IAAI,GAAG,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,KAAK,GAAI,MAAM,CAAC,IAAI,EAAE,SAAS,CAAmB,IAAI,WAAW,CAAC;QACxE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACpC,IAAI,4BAA4B,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC;YACxD,OAAO,CAAC,KAAK,CACX,oFAAoF,CACrF,CAAC;YACF,OAAO,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAC5D,OAAO,CAAC,KAAK,CACX,qCAAqC,GAAG,+BAA+B,QAAQ,sBAAsB,CACtG,CAAC;YACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,MAAM,OAAO,GAAG,kBAAkB,CAAC;YACjC,KAAK;YACL,IAAI,EAAE,GAAG;YACT,KAAK,EAAE,QAAQ;YACf,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC;YAC1D,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC;YAC5B,QAAQ,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC;YAChC,QAAQ,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC;YAChC,IAAI;SACL,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACtB,OAAO;IACT,CAAC;IAED,8EAA8E;IAC9E,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,GAAG,CAAC,iEAAiE,CAAC,CAAC;IAC5F,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC;QACrC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,MAAM,EAAE,EAAE,CAAC;KAC7D,CAAC,CAAC;IAEH,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5D,OAAO;IACT,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;QAC1D,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;QACrE,OAAO,CAAC,GAAG,CACT,oGAAoG,CACrG,CAAC;QACF,OAAO;IACT,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,CAAC,MAAM,UAAU,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,qBAAqB,CAAC,CAAC;IAC/F,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACrB,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,CACT,iJAAiJ,CAClJ,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;AAC1E,CAAC;AAcD,SAAgB,sBAAsB,CAAC,SAAmB;IACxD,OAAO,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACzD,CAAC;AAiBD,SAAS,qBAAqB;IAC5B,oEAAoE;IACpE,8DAA8D;IAC9D,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAwB,CAAC;IAChD,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,EAAE,CAAC;QAC1D,KAAK,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;YAC5B,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,OAAO;gBAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY,EAAE,IAAY,EAAE,SAAiB;IACtE,8DAA8D;IAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAyB,CAAC;IACnD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,MAAM,MAAM,GAAG,CAAC,IAAa,EAAE,EAAE;YAC/B,IAAI,IAAI;gBAAE,OAAO;YACjB,IAAI,GAAG,IAAI,CAAC;YACZ,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC,CAAC;QACF,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAChD,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACvC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;GAQG;AACI,KAAK,UAAU,oBAAoB,CAAC,OAAqB,EAAE;IAChE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IACrD,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,IAAI,GAAG,CAAC;IACtD,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IACvE,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,iBAAiB,CAAC;IACxD,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,qBAAqB,CAAC,EAAE,CAAC;IAEhE,MAAM,OAAO,GACX,IAAI,CAAC,OAAO;QACZ,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACjH,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpC,2EAA2E;IAC3E,0EAA0E;IAC1E,6EAA6E;IAC7E,gEAAgE;IAChE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,OAAO,GAA0C,EAAE,CAAC;IAC1D,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC;YAC9B,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAS;YACjC,KAAK,MAAM,IAAI,IAAI,KAAK;gBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;IACzC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,MAAM,OAAO,CAAC,GAAG,CACf,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,IAAI,EAAE;QACvE,OAAO,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YAC7B,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YAC1B,IAAI,CAAC;gBACH,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,gBAAgB,CAAC,EAAE,CAAC;oBACvD,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC1D,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,EAAE;QACtD,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACrD,MAAM,SAAS,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAC;QACpD,OAAO;YACL,IAAI;YACJ,IAAI,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,uCAAuC,IAAI,EAAE,CAAC,CAAC,CAAC,eAAe,IAAI,EAAE;YACnG,SAAS;YACT,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnC,MAAM,EAAE,EAAE;SACX,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -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"}