@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.
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 +55 -13
  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,209 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OnvifClient = exports.OnvifError = void 0;
4
+ exports.buildUsernameToken = buildUsernameToken;
5
+ exports.escapeXml = escapeXml;
6
+ exports.decodeXmlEntities = decodeXmlEntities;
7
+ exports.xmlTag = xmlTag;
8
+ exports.xmlTags = xmlTags;
9
+ const crypto_1 = require("crypto");
10
+ const AudienceAnalyzeClient_1 = require("./AudienceAnalyzeClient");
11
+ class OnvifError extends Error {
12
+ constructor(kind, message) {
13
+ super(message);
14
+ this.kind = kind;
15
+ }
16
+ }
17
+ exports.OnvifError = OnvifError;
18
+ /** Build the WS-Security UsernameToken header (PasswordDigest per ONVIF core). */
19
+ function buildUsernameToken(username, password, nonceRaw = (0, crypto_1.randomBytes)(16), created = new Date().toISOString()) {
20
+ const digest = (0, crypto_1.createHash)('sha1')
21
+ .update(Buffer.concat([nonceRaw, Buffer.from(created, 'utf8'), Buffer.from(password, 'utf8')]))
22
+ .digest('base64');
23
+ const nonceB64 = nonceRaw.toString('base64');
24
+ return ('<s:Header><wsse:Security s:mustUnderstand="1" ' +
25
+ 'xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" ' +
26
+ 'xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">' +
27
+ `<wsse:UsernameToken><wsse:Username>${escapeXml(username)}</wsse:Username>` +
28
+ '<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">' +
29
+ `${digest}</wsse:Password>` +
30
+ `<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">${nonceB64}</wsse:Nonce>` +
31
+ `<wsu:Created>${created}</wsu:Created></wsse:UsernameToken></wsse:Security></s:Header>`);
32
+ }
33
+ function escapeXml(s) {
34
+ return String(s)
35
+ .replace(/&/g, '&amp;')
36
+ .replace(/</g, '&lt;')
37
+ .replace(/>/g, '&gt;')
38
+ .replace(/"/g, '&quot;')
39
+ .replace(/'/g, '&apos;');
40
+ }
41
+ function envelope(header, body) {
42
+ return ('<?xml version="1.0" encoding="UTF-8"?>' +
43
+ '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' +
44
+ header +
45
+ `<s:Body>${body}</s:Body></s:Envelope>`);
46
+ }
47
+ /** Decode the XML entities recorders emit inside tag text (Dahua returns
48
+ * `...?channel=1&amp;subtype=0` — feeding the raw text into new URL/ffmpeg
49
+ * probes the wrong stream and persists an unusable pathTemplate). */
50
+ function decodeXmlEntities(s) {
51
+ return s
52
+ .replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCodePoint(parseInt(h, 16)))
53
+ .replace(/&#(\d+);/g, (_, d) => String.fromCodePoint(parseInt(d, 10)))
54
+ .replace(/&lt;/g, '<')
55
+ .replace(/&gt;/g, '>')
56
+ .replace(/&quot;/g, '"')
57
+ .replace(/&apos;/g, "'")
58
+ .replace(/&amp;/g, '&'); // LAST so &amp;lt; decodes correctly
59
+ }
60
+ /** First match group of a namespace-agnostic tag (entity-decoded), or null. */
61
+ function xmlTag(xml, localName) {
62
+ const m = xml.match(new RegExp(`<(?:[A-Za-z0-9_]+:)?${localName}[^>]*>([\\s\\S]*?)</(?:[A-Za-z0-9_]+:)?${localName}>`));
63
+ return m ? decodeXmlEntities(m[1].trim()) : null;
64
+ }
65
+ /** All matches (full element text) of a namespace-agnostic tag. */
66
+ function xmlTags(xml, localName) {
67
+ const out = [];
68
+ const re = new RegExp(`<(?:[A-Za-z0-9_]+:)?${localName}(\\s[^>]*)?>([\\s\\S]*?)</(?:[A-Za-z0-9_]+:)?${localName}>`, 'g');
69
+ let m;
70
+ while ((m = re.exec(xml)) !== null)
71
+ out.push(m[0]);
72
+ return out;
73
+ }
74
+ function attrOf(elementXml, attr) {
75
+ const m = elementXml.match(new RegExp(`${attr}="([^"]*)"`));
76
+ return m ? m[1] : null;
77
+ }
78
+ class OnvifClient {
79
+ constructor(options, poster) {
80
+ /** Media service endpoint discovered via GetServices (falls back to device). */
81
+ this.mediaXAddr = null;
82
+ this.host = options.host;
83
+ this.port = options.port ?? 80;
84
+ this.username = options.username;
85
+ this.password = options.password;
86
+ this.timeoutMs = options.timeoutMs ?? 5000;
87
+ this.poster = poster ?? AudienceAnalyzeClient_1.defaultHttpPoster;
88
+ }
89
+ deviceEndpoint() {
90
+ return `http://${this.host}:${this.port}/onvif/device_service`;
91
+ }
92
+ async soap(endpoint, body) {
93
+ let res;
94
+ try {
95
+ res = await this.poster(endpoint, {
96
+ method: 'POST',
97
+ headers: { 'Content-Type': 'application/soap+xml; charset=utf-8' },
98
+ timeoutMs: this.timeoutMs,
99
+ }, envelope(buildUsernameToken(this.username, this.password), body));
100
+ }
101
+ catch (err) {
102
+ throw new OnvifError('unreachable', `ONVIF ${endpoint} unreachable: ${err.message}`);
103
+ }
104
+ // 401/403 → bad creds. Many recorders return 400 + a NotAuthorized SOAP
105
+ // fault instead — treat both as the auth circuit-breaker signal.
106
+ if (res.statusCode === 401 || res.statusCode === 403) {
107
+ throw new OnvifError('auth', `ONVIF auth rejected (HTTP ${res.statusCode})`);
108
+ }
109
+ if (/NotAuthorized|not\s+authorized|sender not authorized/i.test(res.body)) {
110
+ throw new OnvifError('auth', 'ONVIF auth rejected (SOAP NotAuthorized fault)');
111
+ }
112
+ if (res.statusCode < 200 || res.statusCode >= 300) {
113
+ throw new OnvifError('not_onvif', `ONVIF endpoint answered HTTP ${res.statusCode}`);
114
+ }
115
+ if (/<(?:[A-Za-z0-9_]+:)?Fault[\s>]/.test(res.body)) {
116
+ throw new OnvifError('fault', `ONVIF SOAP fault: ${xmlTag(res.body, 'Text') || 'unknown'}`);
117
+ }
118
+ return res.body;
119
+ }
120
+ /** Stable physical identity (the channelKey backbone). */
121
+ async getDeviceInformation() {
122
+ const xml = await this.soap(this.deviceEndpoint(), '<GetDeviceInformation xmlns="http://www.onvif.org/ver10/device/wsdl"/>');
123
+ return {
124
+ manufacturer: xmlTag(xml, 'Manufacturer'),
125
+ model: xmlTag(xml, 'Model'),
126
+ serialNumber: xmlTag(xml, 'SerialNumber'),
127
+ firmwareVersion: xmlTag(xml, 'FirmwareVersion'),
128
+ };
129
+ }
130
+ /** Resolve the media service endpoint (GetServices; fall back to device path). */
131
+ async resolveMediaXAddr() {
132
+ if (this.mediaXAddr)
133
+ return this.mediaXAddr;
134
+ try {
135
+ const xml = await this.soap(this.deviceEndpoint(), '<GetServices xmlns="http://www.onvif.org/ver10/device/wsdl"><IncludeCapability>false</IncludeCapability></GetServices>');
136
+ for (const svc of xmlTags(xml, 'Service')) {
137
+ const ns = xmlTag(svc, 'Namespace') || '';
138
+ if (ns.includes('/media/')) {
139
+ const xaddr = xmlTag(svc, 'XAddr');
140
+ if (xaddr) {
141
+ // Rewrite the advertised host with the one WE reached — recorders
142
+ // behind NAT/port-forwards advertise internal addresses.
143
+ this.mediaXAddr = this.rewriteHost(xaddr);
144
+ return this.mediaXAddr;
145
+ }
146
+ }
147
+ }
148
+ }
149
+ catch (err) {
150
+ if (err.kind === 'auth')
151
+ throw err; // never mask auth
152
+ /* fall through to the conventional path */
153
+ }
154
+ this.mediaXAddr = `http://${this.host}:${this.port}/onvif/media_service`;
155
+ return this.mediaXAddr;
156
+ }
157
+ rewriteHost(xaddr) {
158
+ try {
159
+ const u = new URL(xaddr);
160
+ u.hostname = this.host;
161
+ if (!u.port)
162
+ u.port = String(this.port);
163
+ return u.toString();
164
+ }
165
+ catch {
166
+ return `http://${this.host}:${this.port}/onvif/media_service`;
167
+ }
168
+ }
169
+ /** The recorder's REAL channel/profile list. */
170
+ async getProfiles() {
171
+ const media = await this.resolveMediaXAddr();
172
+ const xml = await this.soap(media, '<GetProfiles xmlns="http://www.onvif.org/ver10/media/wsdl"/>');
173
+ const out = [];
174
+ for (const el of xmlTags(xml, 'Profiles')) {
175
+ const token = attrOf(el, 'token');
176
+ if (!token)
177
+ continue;
178
+ const srcEl = xmlTags(el, 'VideoSourceConfiguration')[0] || '';
179
+ out.push({
180
+ token,
181
+ name: xmlTag(el, 'Name'),
182
+ videoSourceToken: srcEl ? xmlTag(srcEl, 'SourceToken') : null,
183
+ });
184
+ }
185
+ return out;
186
+ }
187
+ /** The ACTUAL RTSP URI for one profile (no guessed brand paths). */
188
+ async getStreamUri(profileToken) {
189
+ const media = await this.resolveMediaXAddr();
190
+ const xml = await this.soap(media, '<GetStreamUri xmlns="http://www.onvif.org/ver10/media/wsdl">' +
191
+ '<StreamSetup><Stream xmlns="http://www.onvif.org/ver10/schema">RTP-Unicast</Stream>' +
192
+ '<Transport xmlns="http://www.onvif.org/ver10/schema"><Protocol>RTSP</Protocol></Transport></StreamSetup>' +
193
+ `<ProfileToken>${escapeXml(profileToken)}</ProfileToken></GetStreamUri>`);
194
+ const uri = xmlTag(xml, 'Uri');
195
+ if (!uri)
196
+ return null;
197
+ // Rewrite the advertised host (NAT-advertised internals) but keep path/port.
198
+ try {
199
+ const u = new URL(uri);
200
+ u.hostname = this.host;
201
+ return u.toString();
202
+ }
203
+ catch {
204
+ return uri;
205
+ }
206
+ }
207
+ }
208
+ exports.OnvifClient = OnvifClient;
209
+ //# sourceMappingURL=OnvifClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OnvifClient.js","sourceRoot":"","sources":["../src/OnvifClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ import { type HttpPoster } from './AudienceAnalyzeClient';
2
+ import type { EdgeAgentConfig, MosaicTile } from './config';
3
+ /**
4
+ * PairClient — exchanges a short-TTL claim code for the connector's full
5
+ * config via `POST {apiBaseUrl}/v2/earner/cameras/pair`.
6
+ *
7
+ * This is the security conduit for the one-click installer: the device token
8
+ * is NEVER baked into the downloadable binary. Instead the operator generates
9
+ * a claim code in the portal (bound to the camera screens they just
10
+ * provisioned), the installer asks for that code once on first run, and this
11
+ * client trades it for `{ apiBaseUrl, cloudSensing, cameras: [{ screenId,
12
+ * deviceToken, rtspUrl? }] }` — which is written verbatim to
13
+ * trillboards.config.json.
14
+ *
15
+ * The claim code IS the credential, so the request carries NO device-token
16
+ * headers (mirrors the unauthenticated, rate-limited
17
+ * `POST /v2/mdm/enrollment/device` enrollment endpoint). Uses the same
18
+ * built-in-http poster as AudienceAnalyzeClient (no extra dep → survives
19
+ * bundling into a self-contained binary; trivially mockable in tests).
20
+ */
21
+ export interface PairClientOptions {
22
+ /** Base URL, e.g. https://api.trillboards.com (no trailing slash needed). */
23
+ apiBaseUrl: string;
24
+ /** Request timeout in ms. Default: 30000. */
25
+ timeoutMs?: number;
26
+ }
27
+ export interface PairRequest {
28
+ /** The claim code from the portal (with or without the XXXX-XXXX dash). */
29
+ claimCode: string;
30
+ /** Stable per-host fingerprint, sent for correlation/audit. Optional. */
31
+ deviceFingerprint?: string;
32
+ /** Connector version string, for server-side compat checks. Optional. */
33
+ connectorVersion?: string;
34
+ }
35
+ /**
36
+ * The config the server returns for a valid claim code — exactly the shape the
37
+ * connector persists to trillboards.config.json and then runs `cloud-sense`
38
+ * against. `rtspUrl` is OPTIONAL per camera: the portal MAY deliver it
39
+ * (stored encrypted) or the operator enters it locally so credentials never
40
+ * leave the venue.
41
+ */
42
+ export interface PairedConfig {
43
+ apiBaseUrl: string;
44
+ cloudSensing: {
45
+ enabled: true;
46
+ periodicIntervalMs?: number;
47
+ faceTriggeredIntervalMs?: number;
48
+ jpegQuality?: number;
49
+ feedStaggerMs?: number;
50
+ maxFeedStaggerMs?: number;
51
+ cameraHealthFailureGraceCount?: number;
52
+ };
53
+ cameras: Array<{
54
+ name: string;
55
+ screenId: string;
56
+ deviceToken: string;
57
+ rtspUrl?: string;
58
+ /**
59
+ * Mosaic tiling (carve one composite/quad RTSP into per-screen tiles).
60
+ * `id` marks a SOURCE feed (carries the rtspUrl); `sourceFeedId` points a
61
+ * tile at that source so it shares the single RTSP pull; `tile` is the crop
62
+ * rect (source pixels). All optional — absent for whole-frame feeds. These
63
+ * MUST survive pairing or a DVR-mosaic feed can't be split into N screens.
64
+ */
65
+ id?: string;
66
+ sourceFeedId?: string;
67
+ tile?: MosaicTile;
68
+ width?: number;
69
+ height?: number;
70
+ fps?: number;
71
+ transport?: 'tcp' | 'udp';
72
+ }>;
73
+ }
74
+ export interface PairResponse {
75
+ /** HTTP status code. */
76
+ statusCode: number;
77
+ /** Whether pairing succeeded (2xx with a usable config). */
78
+ ok: boolean;
79
+ /** The connector config to persist, when ok. */
80
+ config: PairedConfig | null;
81
+ /** Server error code/message when not ok (e.g. INVALID_CODE, EXPIRED_CODE). */
82
+ error?: string;
83
+ /** Raw response text. */
84
+ rawBody: string;
85
+ }
86
+ /** Normalize a user-entered claim code: strip spaces/dashes, upper-case. */
87
+ export declare function normalizeClaimCode(code: string): string;
88
+ export declare class PairClient {
89
+ private readonly endpoint;
90
+ private readonly timeoutMs;
91
+ private readonly poster;
92
+ constructor(options: PairClientOptions, poster?: HttpPoster);
93
+ /** The resolved POST URL (exposed for assertions / logging). */
94
+ getEndpoint(): string;
95
+ /** Exchange a claim code for the connector config. */
96
+ pair(request: PairRequest): Promise<PairResponse>;
97
+ }
98
+ /**
99
+ * Validate + shape the server payload into a PairedConfig. Accepts either the
100
+ * config at the top level or nested under `config`. Requires at least one
101
+ * camera with a screenId + deviceToken (rtspUrl optional — creds-local mode).
102
+ */
103
+ export declare function extractPairedConfig(body: Record<string, unknown> | null): PairedConfig | null;
104
+ /** Build the EdgeAgentConfig-compatible object written to disk after pairing. */
105
+ export declare function pairedConfigToFile(config: PairedConfig): Partial<EdgeAgentConfig>;
106
+ //# sourceMappingURL=PairClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PairClient.d.ts","sourceRoot":"","sources":["../src/PairClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC7E,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE5D;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE;QACZ,OAAO,EAAE,IAAI,CAAC;QACd,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,uBAAuB,CAAC,EAAE,MAAM,CAAC;QACjC,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,6BAA6B,CAAC,EAAE,MAAM,CAAC;KACxC,CAAC;IACF,OAAO,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB;;;;;;WAMG;QACH,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,IAAI,CAAC,EAAE,UAAU,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,SAAS,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;KAC3B,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,YAAY;IAC3B,wBAAwB;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,EAAE,EAAE,OAAO,CAAC;IACZ,gDAAgD;IAChD,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;gBAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,CAAC,EAAE,UAAU;IAU3D,gEAAgE;IAChE,WAAW,IAAI,MAAM;IAIrB,sDAAsD;IAChD,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;CAwDxD;AAoBD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACnC,YAAY,GAAG,IAAI,CA8DrB;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAqBjF"}
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PairClient = void 0;
4
+ exports.normalizeClaimCode = normalizeClaimCode;
5
+ exports.extractPairedConfig = extractPairedConfig;
6
+ exports.pairedConfigToFile = pairedConfigToFile;
7
+ const AudienceAnalyzeClient_1 = require("./AudienceAnalyzeClient");
8
+ /** Normalize a user-entered claim code: strip spaces/dashes, upper-case. */
9
+ function normalizeClaimCode(code) {
10
+ return String(code || '')
11
+ .replace(/[\s-]+/g, '')
12
+ .toUpperCase();
13
+ }
14
+ class PairClient {
15
+ constructor(options, poster) {
16
+ if (!options.apiBaseUrl) {
17
+ throw new Error('apiBaseUrl is required for PairClient');
18
+ }
19
+ const base = options.apiBaseUrl.replace(/\/+$/, '');
20
+ this.endpoint = `${base}/v2/earner/cameras/pair`;
21
+ this.timeoutMs = options.timeoutMs ?? 30000;
22
+ this.poster = poster ?? AudienceAnalyzeClient_1.defaultHttpPoster;
23
+ }
24
+ /** The resolved POST URL (exposed for assertions / logging). */
25
+ getEndpoint() {
26
+ return this.endpoint;
27
+ }
28
+ /** Exchange a claim code for the connector config. */
29
+ async pair(request) {
30
+ const claimCode = normalizeClaimCode(request?.claimCode);
31
+ if (!claimCode) {
32
+ throw new Error('pair() requires a non-empty claimCode');
33
+ }
34
+ const payload = {
35
+ claimCode,
36
+ ...(request.deviceFingerprint ? { deviceFingerprint: request.deviceFingerprint } : {}),
37
+ ...(request.connectorVersion ? { connectorVersion: request.connectorVersion } : {}),
38
+ };
39
+ const result = await this.poster(this.endpoint, {
40
+ method: 'POST',
41
+ headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
42
+ timeoutMs: this.timeoutMs,
43
+ }, JSON.stringify(payload));
44
+ let parsed = null;
45
+ try {
46
+ parsed = result.body ? JSON.parse(result.body) : null;
47
+ }
48
+ catch {
49
+ parsed = null;
50
+ }
51
+ const ok = result.statusCode >= 200 && result.statusCode < 300;
52
+ if (!ok) {
53
+ return {
54
+ statusCode: result.statusCode,
55
+ ok: false,
56
+ config: null,
57
+ error: (parsed && parsed.error) ||
58
+ (parsed && parsed.message) ||
59
+ `pair failed (HTTP ${result.statusCode})`,
60
+ rawBody: result.body,
61
+ };
62
+ }
63
+ const config = extractPairedConfig(parsed);
64
+ if (!config) {
65
+ return {
66
+ statusCode: result.statusCode,
67
+ ok: false,
68
+ config: null,
69
+ error: 'pair response did not contain a usable camera config',
70
+ rawBody: result.body,
71
+ };
72
+ }
73
+ return { statusCode: result.statusCode, ok: true, config, rawBody: result.body };
74
+ }
75
+ }
76
+ exports.PairClient = PairClient;
77
+ /**
78
+ * Sanitize a mosaic tile rectangle (source pixels) → { x, y, w, h } with
79
+ * non-negative integer offsets + positive integer dimensions, or null when the
80
+ * shape is absent/invalid. Mirrors the server-side cameraClaimCodeService
81
+ * sanitizeTile so a malformed rect never reaches the connector's ffmpeg crop.
82
+ */
83
+ function sanitizeTile(tile) {
84
+ if (!tile || typeof tile !== 'object')
85
+ return null;
86
+ const t = tile;
87
+ const x = Math.trunc(Number(t.x));
88
+ const y = Math.trunc(Number(t.y));
89
+ const w = Math.trunc(Number(t.w));
90
+ const h = Math.trunc(Number(t.h));
91
+ if (![x, y, w, h].every(Number.isFinite))
92
+ return null;
93
+ if (x < 0 || y < 0 || w <= 0 || h <= 0)
94
+ return null;
95
+ return { x, y, w, h };
96
+ }
97
+ /**
98
+ * Validate + shape the server payload into a PairedConfig. Accepts either the
99
+ * config at the top level or nested under `config`. Requires at least one
100
+ * camera with a screenId + deviceToken (rtspUrl optional — creds-local mode).
101
+ */
102
+ function extractPairedConfig(body) {
103
+ if (!body)
104
+ return null;
105
+ const src = (body.config && typeof body.config === 'object'
106
+ ? body.config
107
+ : body);
108
+ const rawCameras = Array.isArray(src.cameras) ? src.cameras : [];
109
+ const cameras = rawCameras
110
+ .map((c) => (c && typeof c === 'object' ? c : null))
111
+ .filter((c) => !!c && !!c.screenId && !!c.deviceToken)
112
+ .map((c) => {
113
+ // Re-sanitize the tile defensively: the connector feeds these coords
114
+ // straight into the ffmpeg crop filter, so a malformed rect from any
115
+ // source must never reach it (mirrors the server-side sanitizeTile).
116
+ const tile = sanitizeTile(c.tile);
117
+ return {
118
+ name: String(c.name || c.screenId),
119
+ screenId: String(c.screenId),
120
+ deviceToken: String(c.deviceToken),
121
+ ...(c.rtspUrl ? { rtspUrl: String(c.rtspUrl) } : {}),
122
+ // Mosaic tiling: carry the source link + crop so a composite/quad feed
123
+ // can be carved into per-tile screens (config.ts resolveConfig groups
124
+ // tiles by sourceFeedId and inherits the source's single RTSP pull).
125
+ ...(c.id ? { id: String(c.id) } : {}),
126
+ ...(c.sourceFeedId ? { sourceFeedId: String(c.sourceFeedId) } : {}),
127
+ ...(tile ? { tile } : {}),
128
+ ...(typeof c.width === 'number' ? { width: c.width } : {}),
129
+ ...(typeof c.height === 'number' ? { height: c.height } : {}),
130
+ ...(typeof c.fps === 'number' ? { fps: c.fps } : {}),
131
+ ...(c.transport === 'udp' || c.transport === 'tcp'
132
+ ? { transport: c.transport }
133
+ : {}),
134
+ };
135
+ });
136
+ if (cameras.length === 0)
137
+ return null;
138
+ const cs = (src.cloudSensing && typeof src.cloudSensing === 'object'
139
+ ? src.cloudSensing
140
+ : {});
141
+ return {
142
+ apiBaseUrl: String(src.apiBaseUrl || 'https://api.trillboards.com'),
143
+ cloudSensing: {
144
+ enabled: true,
145
+ ...(typeof cs.periodicIntervalMs === 'number'
146
+ ? { periodicIntervalMs: cs.periodicIntervalMs }
147
+ : {}),
148
+ ...(typeof cs.faceTriggeredIntervalMs === 'number'
149
+ ? { faceTriggeredIntervalMs: cs.faceTriggeredIntervalMs }
150
+ : {}),
151
+ ...(typeof cs.jpegQuality === 'number' ? { jpegQuality: cs.jpegQuality } : {}),
152
+ ...(typeof cs.feedStaggerMs === 'number' ? { feedStaggerMs: cs.feedStaggerMs } : {}),
153
+ ...(typeof cs.maxFeedStaggerMs === 'number'
154
+ ? { maxFeedStaggerMs: cs.maxFeedStaggerMs }
155
+ : {}),
156
+ ...(typeof cs.cameraHealthFailureGraceCount === 'number'
157
+ ? { cameraHealthFailureGraceCount: cs.cameraHealthFailureGraceCount }
158
+ : {}),
159
+ },
160
+ cameras,
161
+ };
162
+ }
163
+ /** Build the EdgeAgentConfig-compatible object written to disk after pairing. */
164
+ function pairedConfigToFile(config) {
165
+ return {
166
+ apiBaseUrl: config.apiBaseUrl,
167
+ cloudSensing: config.cloudSensing,
168
+ cameras: config.cameras.map((c) => ({
169
+ name: c.name,
170
+ screenId: c.screenId,
171
+ deviceToken: c.deviceToken,
172
+ rtspUrl: c.rtspUrl || '',
173
+ // Mosaic tiling — persist the source link + crop so loadConfig/resolveConfig
174
+ // can reconstitute the tile feeds (a tile inherits its source's rtspUrl via
175
+ // the id→rtspUrl map). A tile's own rtspUrl stays '' (inherited on load).
176
+ ...(c.id ? { id: c.id } : {}),
177
+ ...(c.sourceFeedId ? { sourceFeedId: c.sourceFeedId } : {}),
178
+ ...(c.tile ? { tile: c.tile } : {}),
179
+ ...(c.width ? { width: c.width } : {}),
180
+ ...(c.height ? { height: c.height } : {}),
181
+ ...(c.fps ? { fps: c.fps } : {}),
182
+ ...(c.transport ? { transport: c.transport } : {}),
183
+ })),
184
+ };
185
+ }
186
+ //# sourceMappingURL=PairClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PairClient.js","sourceRoot":"","sources":["../src/PairClient.ts"],"names":[],"mappings":";;;AA4FA,gDAIC;AAwGD,kDAgEC;AAGD,gDAqBC;AAhSD,mEAA6E;AA2F7E,4EAA4E;AAC5E,SAAgB,kBAAkB,CAAC,IAAY;IAC7C,OAAO,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;SACtB,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;SACtB,WAAW,EAAE,CAAC;AACnB,CAAC;AAED,MAAa,UAAU;IAKrB,YAAY,OAA0B,EAAE,MAAmB;QACzD,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,QAAQ,GAAG,GAAG,IAAI,yBAAyB,CAAC;QACjD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAM,CAAC;QAC7C,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,yCAAiB,CAAC;IAC5C,CAAC;IAED,gEAAgE;IAChE,WAAW;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,sDAAsD;IACtD,KAAK,CAAC,IAAI,CAAC,OAAoB;QAC7B,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QACzD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,OAAO,GAA4B;YACvC,SAAS;YACT,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACtF,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACpF,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAC9B,IAAI,CAAC,QAAQ,EACb;YACE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,EAAE,kBAAkB,EAAE;YAC3E,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,EACD,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CACxB,CAAC;QAEF,IAAI,MAAM,GAAmC,IAAI,CAAC;QAClD,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAA6B,CAAC,CAAC,CAAC,IAAI,CAAC;QACrF,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,IAAI,GAAG,IAAI,MAAM,CAAC,UAAU,GAAG,GAAG,CAAC;QAC/D,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,OAAO;gBACL,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,IAAI;gBACZ,KAAK,EACH,CAAC,MAAM,IAAK,MAAM,CAAC,KAAgB,CAAC;oBACpC,CAAC,MAAM,IAAK,MAAM,CAAC,OAAkB,CAAC;oBACtC,qBAAqB,MAAM,CAAC,UAAU,GAAG;gBAC3C,OAAO,EAAE,MAAM,CAAC,IAAI;aACrB,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO;gBACL,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,sDAAsD;gBAC7D,OAAO,EAAE,MAAM,CAAC,IAAI;aACrB,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;IACnF,CAAC;CACF;AA7ED,gCA6EC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,IAAa;IACjC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,CAAC,GAAG,IAA+B,CAAC;IAC1C,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACtD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;AACxB,CAAC;AAED;;;;GAIG;AACH,SAAgB,mBAAmB,CACjC,IAAoC;IAEpC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;QACzD,CAAC,CAAE,IAAI,CAAC,MAAkC;QAC1C,CAAC,CAAC,IAAI,CAA4B,CAAC;IAErC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,OAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;IAChF,MAAM,OAAO,GAAG,UAAU;SACvB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,CAA6B,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;SAChF,MAAM,CAAC,CAAC,CAAC,EAAgC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;SACnF,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,qEAAqE;QACrE,qEAAqE;QACrE,qEAAqE;QACrE,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAClC,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,QAAQ,CAAC;YAClC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC5B,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC;YAClC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,uEAAuE;YACvE,sEAAsE;YACtE,qEAAqE;YACrE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzB,GAAG,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7D,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,GAAG,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,IAAI,CAAC,CAAC,SAAS,KAAK,KAAK;gBAChD,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAA0B,EAAE;gBAC7C,CAAC,CAAC,EAAE,CAAC;SACR,CAAC;IACJ,CAAC,CAAC,CAAC;IAEL,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ;QAClE,CAAC,CAAE,GAAG,CAAC,YAAwC;QAC/C,CAAC,CAAC,EAAE,CAA4B,CAAC;IAEnC,OAAO;QACL,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,IAAI,6BAA6B,CAAC;QACnE,YAAY,EAAE;YACZ,OAAO,EAAE,IAAI;YACb,GAAG,CAAC,OAAO,EAAE,CAAC,kBAAkB,KAAK,QAAQ;gBAC3C,CAAC,CAAC,EAAE,kBAAkB,EAAE,EAAE,CAAC,kBAAkB,EAAE;gBAC/C,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,OAAO,EAAE,CAAC,uBAAuB,KAAK,QAAQ;gBAChD,CAAC,CAAC,EAAE,uBAAuB,EAAE,EAAE,CAAC,uBAAuB,EAAE;gBACzD,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,OAAO,EAAE,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9E,GAAG,CAAC,OAAO,EAAE,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,EAAE,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpF,GAAG,CAAC,OAAO,EAAE,CAAC,gBAAgB,KAAK,QAAQ;gBACzC,CAAC,CAAC,EAAE,gBAAgB,EAAE,EAAE,CAAC,gBAAgB,EAAE;gBAC3C,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,OAAO,EAAE,CAAC,6BAA6B,KAAK,QAAQ;gBACtD,CAAC,CAAC,EAAE,6BAA6B,EAAE,EAAE,CAAC,6BAA6B,EAAE;gBACrE,CAAC,CAAC,EAAE,CAAC;SACR;QACD,OAAO;KACR,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,SAAgB,kBAAkB,CAAC,MAAoB;IACrD,OAAO;QACL,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAClC,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,EAAE;YACxB,6EAA6E;YAC7E,4EAA4E;YAC5E,0EAA0E;YAC1E,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7B,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3D,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACtC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACnD,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,53 @@
1
+ import type { EdgeAgentConfig, CameraFeedConfig } from './config';
2
+ import type { ResolvedFeed } from './CloudSensingManager';
3
+ import { discoverOnvifHosts, sweepLanForRecorders, type OnvifHost } from './discovery';
4
+ export interface RecorderAddressRecoveryOptions {
5
+ configPath?: string;
6
+ log?: (message: string) => void;
7
+ cooldownMs?: number;
8
+ now?: () => number;
9
+ discover?: typeof discoverOnvifHosts;
10
+ sweep?: typeof sweepLanForRecorders;
11
+ validateStream?: (url: string, feed: CameraFeedConfig) => Promise<boolean>;
12
+ matchSerial?: (candidate: OnvifHost, expectedSerial: string, credentials: {
13
+ username: string;
14
+ password: string;
15
+ }) => Promise<boolean>;
16
+ lookupMac?: (host: string) => Promise<string | null>;
17
+ }
18
+ export declare function normalizeMacAddress(value: string): string | null;
19
+ /** Read one already-contacted LAN host from the operating system's neighbor table. */
20
+ export declare function lookupMacAddress(host: string): Promise<string | null>;
21
+ /** Replace only the host, preserving credentials, port, path, query, and RTSPS. */
22
+ export declare function rewriteStreamHost(streamUrl: string, host: string): string;
23
+ export declare function shouldAttemptRecorderAddressRecovery(error: unknown): boolean;
24
+ /**
25
+ * Finds a recorder again after DHCP changes its address.
26
+ *
27
+ * Recovery is deliberately conservative: the MAC address must match before
28
+ * credentials are used. An ONVIF serial is then verified when setup captured
29
+ * one; otherwise an existing channel recipe validates the matched recorder.
30
+ * Credentials are never tried against an unmatched LAN device.
31
+ */
32
+ export declare class RecorderAddressRecovery {
33
+ private readonly groups;
34
+ private readonly currentHost;
35
+ private readonly inFlight;
36
+ private readonly lastFailedAt;
37
+ private readonly configPath?;
38
+ private readonly log;
39
+ private readonly cooldownMs;
40
+ private readonly now;
41
+ private readonly discover;
42
+ private readonly sweep;
43
+ private readonly validateStream;
44
+ private readonly matchSerial;
45
+ private readonly lookupMac;
46
+ constructor(config: EdgeAgentConfig, options?: RecorderAddressRecoveryOptions);
47
+ canRecover(feed: ResolvedFeed): boolean;
48
+ recover(feed: ResolvedFeed, currentUrl: string, error: unknown): Promise<string | null>;
49
+ private findCurrentHost;
50
+ private updateGroupUrls;
51
+ private persistGroupUrls;
52
+ }
53
+ //# sourceMappingURL=RecorderAddressRecovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecorderAddressRecovery.d.ts","sourceRoot":"","sources":["../src/RecorderAddressRecovery.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAoB,MAAM,UAAU,CAAC;AACpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAavF,MAAM,WAAW,8BAA8B;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,kBAAkB,CAAC;IACrC,KAAK,CAAC,EAAE,OAAO,oBAAoB,CAAC;IACpC,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3E,WAAW,CAAC,EAAE,CACZ,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAChD,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACtD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAShE;AAaD,sFAAsF;AACtF,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoB3E;AA4BD,mFAAmF;AACnF,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAIzE;AAED,wBAAgB,oCAAoC,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAG5E;AAuCD;;;;;;;GAOG;AACH,qBAAa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;IAC3D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6C;IACtE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA4B;IAChD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4B;IACrD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA8B;IACpD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4D;IAC3F,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6D;IACzF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2D;gBAEzE,MAAM,EAAE,eAAe,EAAE,OAAO,GAAE,8BAAmC;IA4BjF,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO;IAMjC,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;YAwC/E,eAAe;IAmD7B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,gBAAgB;CAuCzB"}