obsbot-mcp 0.7.0 → 0.9.0

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 (56) hide show
  1. package/README.md +111 -20
  2. package/dist/build-info.json +5 -0
  3. package/dist/device/manager.d.ts +8 -0
  4. package/dist/device/manager.js +37 -4
  5. package/dist/device/manager.js.map +1 -1
  6. package/dist/geometry/aim.d.ts +24 -13
  7. package/dist/geometry/aim.js +26 -13
  8. package/dist/geometry/aim.js.map +1 -1
  9. package/dist/ipc/build-id.d.ts +25 -0
  10. package/dist/ipc/build-id.js +60 -0
  11. package/dist/ipc/build-id.js.map +1 -0
  12. package/dist/ipc/client.d.ts +36 -0
  13. package/dist/ipc/client.js +106 -4
  14. package/dist/ipc/client.js.map +1 -1
  15. package/dist/ipc/coordinator.d.ts +91 -12
  16. package/dist/ipc/coordinator.js +293 -57
  17. package/dist/ipc/coordinator.js.map +1 -1
  18. package/dist/ipc/gate.d.ts +14 -0
  19. package/dist/ipc/gate.js +45 -0
  20. package/dist/ipc/gate.js.map +1 -0
  21. package/dist/ipc/owner.d.ts +27 -1
  22. package/dist/ipc/owner.js +67 -4
  23. package/dist/ipc/owner.js.map +1 -1
  24. package/dist/ipc/protocol.d.ts +30 -0
  25. package/dist/ipc/protocol.js +40 -0
  26. package/dist/ipc/protocol.js.map +1 -1
  27. package/dist/ipc/rendezvous.d.ts +37 -11
  28. package/dist/ipc/rendezvous.js +143 -30
  29. package/dist/ipc/rendezvous.js.map +1 -1
  30. package/dist/mcp/server.js +40 -12
  31. package/dist/mcp/server.js.map +1 -1
  32. package/dist/tail2/api.d.ts +381 -0
  33. package/dist/tail2/api.js +547 -0
  34. package/dist/tail2/api.js.map +1 -0
  35. package/dist/tail2/mdns.d.ts +48 -0
  36. package/dist/tail2/mdns.js +216 -0
  37. package/dist/tail2/mdns.js.map +1 -0
  38. package/dist/tail2/move.d.ts +41 -0
  39. package/dist/tail2/move.js +79 -0
  40. package/dist/tail2/move.js.map +1 -0
  41. package/dist/tail2/registry.d.ts +100 -0
  42. package/dist/tail2/registry.js +200 -0
  43. package/dist/tail2/registry.js.map +1 -0
  44. package/dist/tail2/snapshot.d.ts +54 -0
  45. package/dist/tail2/snapshot.js +157 -0
  46. package/dist/tail2/snapshot.js.map +1 -0
  47. package/dist/tail2/tools.d.ts +7 -0
  48. package/dist/tail2/tools.js +772 -0
  49. package/dist/tail2/tools.js.map +1 -0
  50. package/dist/version.d.ts +1 -1
  51. package/dist/version.js +1 -1
  52. package/native/prebuilt/darwin-arm64/obsbot-helper +0 -0
  53. package/native/prebuilt/darwin-x64/obsbot-helper +0 -0
  54. package/native/prebuilt/linux-x64/obsbot-helper +0 -0
  55. package/native/prebuilt/win32-x64/obsbot-helper.exe +0 -0
  56. package/package.json +4 -1
@@ -0,0 +1,547 @@
1
+ import WebSocket from "ws";
2
+ // The camera stores preset names as base64 of UTF-8 (decoded from the web
3
+ // bundle's own btoa-encoder, hardware-round-tripped 2026-09-26).
4
+ const b64encode = (s) => Buffer.from(s, "utf8").toString("base64");
5
+ const b64decode = (s) => Buffer.from(s, "base64").toString("utf8");
6
+ export class Tail2HttpError extends Error {
7
+ status;
8
+ path;
9
+ body;
10
+ constructor(status, path, body) {
11
+ super(`Tail 2 API ${path} failed with HTTP ${status}${body ? `: ${body}` : ""}`);
12
+ this.status = status;
13
+ this.path = path;
14
+ this.body = body;
15
+ this.name = "Tail2HttpError";
16
+ }
17
+ }
18
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
19
+ export class Tail2Api {
20
+ baseUrl;
21
+ timeoutMs;
22
+ wsUrl;
23
+ constructor(opts) {
24
+ // Accept a bare host too — the common case is "192.168.0.132".
25
+ this.baseUrl = opts.baseUrl.startsWith("http") ? opts.baseUrl : `http://${opts.baseUrl}`;
26
+ this.timeoutMs = opts.timeoutMs ?? 5000;
27
+ this.wsUrl = `${this.baseUrl.replace(/^http/, "ws")}/ws/`;
28
+ }
29
+ async req(method, path, body) {
30
+ let res;
31
+ try {
32
+ res = await fetch(this.baseUrl + path, {
33
+ method,
34
+ headers: body !== undefined ? { "Content-Type": "application/json" } : undefined,
35
+ // A string body is used as-is: one endpoint (evbias) needs an exact
36
+ // JSON float literal that JSON.stringify cannot produce from a number.
37
+ body: body === undefined ? undefined : typeof body === "string" ? body : JSON.stringify(body),
38
+ signal: AbortSignal.timeout(this.timeoutMs),
39
+ });
40
+ }
41
+ catch (e) {
42
+ throw new Error(`Tail 2 at ${this.baseUrl} is unreachable (${e instanceof Error ? e.message : String(e)}). ` +
43
+ `Check the camera is powered and on the network; if it changed IP, re-run obsbot_tail2_scan.`);
44
+ }
45
+ if (!res.ok) {
46
+ const text = await res.text().catch(() => "");
47
+ throw new Tail2HttpError(res.status, path, text.slice(0, 300));
48
+ }
49
+ try {
50
+ return (await res.json());
51
+ }
52
+ catch (e) {
53
+ // HTTP-level success but a body that isn't JSON: this is some OTHER
54
+ // device's web server on that address, not a Tail 2. Same actionable
55
+ // shape as unreachable — the fix is a different address, not a retry.
56
+ throw new Error(`Tail 2 at ${this.baseUrl} did not reply with JSON for ${path} ` +
57
+ `(${e instanceof Error ? e.message : String(e)}) — the address is probably another ` +
58
+ `device, not a Tail 2. Re-run obsbot_tail2_scan to find the camera.`);
59
+ }
60
+ }
61
+ /**
62
+ * PUT/POST with ack checking. `body` may be an object (JSON.stringify'd)
63
+ * or a pre-built JSON string — evbias needs a float literal like `0.0`
64
+ * that stringifying a number cannot produce (measured 2026-09-30: JSON
65
+ * integers 0 and -1 get 400 "Invalid value type"; 0.0 and -1.0 apply).
66
+ */
67
+ async send(method, path, body) {
68
+ const ack = await this.req(method, path, body);
69
+ // Every confirmed endpoint replies {"code":200,"err_idx":0}; a non-200
70
+ // code with HTTP 200 has not been observed, but checking it is free and
71
+ // makes an err_idx≠0 reply loud instead of silent.
72
+ if (typeof ack.code === "number" && ack.code !== 200) {
73
+ throw new Error(`${path} rejected: code=${ack.code} err_idx=${ack.err_idx ?? "?"}`);
74
+ }
75
+ }
76
+ /**
77
+ * Read `read()` until `match()` holds, on a bounded ladder. Returns
78
+ * `settled:false` (NOT a throw) when the ladder runs out — the command was
79
+ * sent and acknowledged, so the caller decides whether that is failure.
80
+ * Mirrors `obsbot_zoom_uvc`'s `settled` contract.
81
+ */
82
+ async verified(read, match, opts = {}) {
83
+ const attempts = opts.attempts ?? 6;
84
+ const delayMs = opts.delayMs ?? 400;
85
+ let value = await read();
86
+ for (let i = 1; i < attempts && !match(value); i++) {
87
+ await sleep(delayMs);
88
+ value = await read();
89
+ }
90
+ return { settled: match(value), value };
91
+ }
92
+ // ---- reads ---------------------------------------------------------------
93
+ info() {
94
+ return this.req("GET", "/camera/sdk/device_info");
95
+ }
96
+ /** One-shot status: connect, take the first push, close. No persistent socket. */
97
+ status() {
98
+ return new Promise((resolve, reject) => {
99
+ const ws = new WebSocket(this.wsUrl, { handshakeTimeout: this.timeoutMs });
100
+ // One winner across message/error/close/timer. NOT removeAllListeners():
101
+ // the camera keeps pushing its ~1 Hz status while our close handshake is
102
+ // in flight, and a late teardown frame can raise 'error' AFTER we've
103
+ // stripped the handler — an unhandled 'error' on a WebSocket kills the
104
+ // process (measured on live hardware 2026-09-26: "Invalid WebSocket
105
+ // frame: invalid status code 12592"). The settled flag makes stray
106
+ // events no-ops; success tears the socket down with terminate() so no
107
+ // further frames are parsed at all.
108
+ let settled = false;
109
+ const settle = (fn) => {
110
+ if (settled)
111
+ return;
112
+ settled = true;
113
+ clearTimeout(timer);
114
+ fn();
115
+ };
116
+ const timer = setTimeout(() => settle(() => {
117
+ ws.terminate();
118
+ reject(new Error(`Tail 2 status channel (${this.wsUrl}) timed out. Retry this call.`));
119
+ }), this.timeoutMs + 2000);
120
+ ws.on("error", (e) => settle(() => reject(new Error(`Tail 2 status channel failed: ${e.message}`))));
121
+ ws.on("close", () => settle(() => reject(new Error("status channel closed before a push arrived"))));
122
+ ws.on("message", (data) => {
123
+ try {
124
+ const parsed = JSON.parse(data.toString());
125
+ settle(() => {
126
+ ws.terminate();
127
+ resolve(parsed);
128
+ });
129
+ }
130
+ catch (e) {
131
+ settle(() => reject(e instanceof Error ? e : new Error(String(e))));
132
+ }
133
+ });
134
+ });
135
+ }
136
+ ranges() {
137
+ return this.req("GET", "/camera/sdk/range");
138
+ }
139
+ networkConfig() {
140
+ return this.req("GET", "/camera/sdk/networkconfig");
141
+ }
142
+ imageState() {
143
+ return this.req("GET", "/camera/sdk/image");
144
+ }
145
+ // ---- PTZ -----------------------------------------------------------------
146
+ zoomGet() {
147
+ return this.req("GET", "/camera/sdk/ptz/zoom");
148
+ }
149
+ /**
150
+ * Absolute zoom ratio on the Tail 2's own 1.0–12.0 scale (`GET range`).
151
+ * `speed` is REQUIRED by the firmware (a ratio-only PUT is a 400) — both
152
+ * fields MEASURED 2026-09-26.
153
+ */
154
+ async zoomSet(ratio, speed, verify = {}) {
155
+ await this.send("PUT", "/camera/sdk/ptz/zoom", { ratio, speed });
156
+ // The zoom motor ramps; 4.5s of polling covers a full 1x→12x sweep at
157
+ // default speed without hanging the call on a wedged motor.
158
+ const v = await this.verified(() => this.zoomGet(), (r) => Math.abs(r.ratio - ratio) <= 0.01, { attempts: 10, delayMs: 500, ...verify });
159
+ return { settled: v.settled, ratio: v.value.ratio };
160
+ }
161
+ /** Gimbal recenter (`POST ptz/reset`). Open-loop: the Tail 2 reports no live pose. */
162
+ async recenter() {
163
+ await this.send("POST", "/camera/sdk/ptz/reset", {});
164
+ }
165
+ // ---- gimbal speed control (the joystick primitive) -------------------------
166
+ //
167
+ // POST /camera/sdk/ptz/gimbalcontrol {stop, pitch, roll, yaw} — from the
168
+ // vendor REST doc (SDKs/obsbot_tail_2_res_tful.zip), hardware-verified
169
+ // 2026-09-30: each axis −178..178, sign = direction, magnitude = SPEED.
170
+ // stop:true halts. This is a continuous speed command with no endpoint of
171
+ // its own — the camera keeps moving until stopped.
172
+ gimbalSpeed(yaw, pitch, roll) {
173
+ return this.send("POST", "/camera/sdk/ptz/gimbalcontrol", {
174
+ stop: false,
175
+ pitch,
176
+ roll,
177
+ yaw,
178
+ });
179
+ }
180
+ gimbalStop() {
181
+ return this.send("POST", "/camera/sdk/ptz/gimbalcontrol", {
182
+ stop: true,
183
+ pitch: 0,
184
+ roll: 0,
185
+ yaw: 0,
186
+ });
187
+ }
188
+ gimbalInvertGet() {
189
+ return this.req("GET", "/camera/sdk/ptz/gimbalinvert");
190
+ }
191
+ /** Reverse (invert) control directions. Readback-verified like every write. */
192
+ async gimbalInvertSet(enable, verify = {}) {
193
+ await this.send("PUT", "/camera/sdk/ptz/gimbalinvert", { enable });
194
+ const v = await this.verified(() => this.gimbalInvertGet(), (r) => r.enable === enable, verify);
195
+ return { settled: v.settled, enable: v.value.enable };
196
+ }
197
+ // ---- pose readback via the preset API ----------------------------------------
198
+ //
199
+ // The API reports no live pose directly, but `preset set` captures the LIVE
200
+ // pose into a slot and `GET preset` returns it in degrees. Save-to-scratch
201
+ // → read → delete is therefore a pose sensor (measured 2026-09-30: it
202
+ // caught a 7.9° yaw move exactly). The scratch slot must be EMPTY — save
203
+ // overwrites and there is no pose-by-value write to restore a clobbered
204
+ // preset, so a full preset bank is a hard error, not a guess.
205
+ /** Marker name for pose-probe scratch slots, so crashed probes are reusable. */
206
+ static POSE_PROBE_NAME = "pose-probe";
207
+ async readPose(verify = {}) {
208
+ let scratch;
209
+ const list = await this.presetsGet();
210
+ // A leftover probe slot from a crashed run is ours by name — delete it
211
+ // first and wait for it to be GONE, so the freshness check below (slot
212
+ // APPEARS after our save) cannot match its stale contents.
213
+ const leftover = list.find((p) => p.name === Tail2Api.POSE_PROBE_NAME);
214
+ if (leftover) {
215
+ await this.presetOp("delete", leftover.id);
216
+ await this.verified(() => this.presetsGet(), (l) => !l.some((x) => x.id === leftover.id), { attempts: 6, delayMs: 350, ...verify });
217
+ }
218
+ const fresh = await this.presetsGet();
219
+ const used = new Set(fresh.map((p) => p.id));
220
+ scratch = [0, 1, 2].find((id) => !used.has(id));
221
+ if (scratch === undefined) {
222
+ throw new Error("all three preset slots are occupied — the pose read needs an empty scratch slot. " +
223
+ "Delete one preset (obsbot_tail2_preset_delete) and retry.");
224
+ }
225
+ try {
226
+ await this.presetOp("set", scratch, Tail2Api.POSE_PROBE_NAME);
227
+ const v = await this.verified(() => this.presetsGet(), (l) => {
228
+ const p = l.find((x) => x.id === scratch);
229
+ return p !== undefined && p.name === Tail2Api.POSE_PROBE_NAME;
230
+ }, { attempts: 8, delayMs: 350, ...verify });
231
+ const p = v.value.find((x) => x.id === scratch);
232
+ if (!p)
233
+ throw new Error("pose probe: the scratch slot vanished before it could be read");
234
+ return { yaw: p.yaw, pitch: p.pitch, roll: p.roll, ratio: p.ratio };
235
+ }
236
+ finally {
237
+ // Best-effort cleanup; must not mask the pose result.
238
+ await this.presetOp("delete", scratch).catch(() => { });
239
+ }
240
+ }
241
+ // ---- rotation (no Tiny 2 equivalent) --------------------------------------
242
+ /**
243
+ * Motorized 90° barrel rotation. Verified against the WS push rather than a
244
+ * REST readback (none exists) — and the readback MUST be tolerant of the
245
+ * motor taking ~1.5s, plus the swallowed-write hazard during motion.
246
+ */
247
+ async portraitSet(enable, verify = {}) {
248
+ await this.send("POST", "/camera/sdk/switch_portrait", { enable });
249
+ const v = await this.verified(() => this.status(), (s) => s.switch_portrait === enable, { attempts: 12, delayMs: 450, ...verify });
250
+ return { settled: v.settled, switch_portrait: v.value.switch_portrait === true };
251
+ }
252
+ rollBiasGet() {
253
+ return this.req("GET", "/camera/sdk/ptz/rollbias");
254
+ }
255
+ /** Roll trim in degrees. MEASURED write + readback 2026-09-26. */
256
+ async rollBiasSet(angle, verify = {}) {
257
+ await this.send("PUT", "/camera/sdk/ptz/rollbias", { angle });
258
+ const v = await this.verified(() => this.rollBiasGet(), (r) => Math.abs(r.angle - angle) <= 0.01, verify);
259
+ return { settled: v.settled, angle: v.value.angle };
260
+ }
261
+ // ---- AI tracking -----------------------------------------------------------
262
+ aiModeGet() {
263
+ return this.req("GET", "/camera/sdk/ai/workmode");
264
+ }
265
+ /**
266
+ * `mode` strings are the camera's own enum (bundle-verified, TAIL2-PROTOCOL
267
+ * §3): none | humanTrackingSingleMode | humanTrackingGroupMode |
268
+ * animalTrackingNormal | animalTrackingCloseUp | objectTracking |
269
+ * objectTrackingNormal | objectTrackingCloseUp.
270
+ */
271
+ async aiModeSet(mode, verify = {}) {
272
+ await this.send("PUT", "/camera/sdk/ai/workmode", { mode });
273
+ const v = await this.verified(() => this.aiModeGet(), (r) => r.mode === mode, verify);
274
+ return { settled: v.settled, mode: v.value.mode };
275
+ }
276
+ trackSpeedGet() {
277
+ return this.req("GET", "/camera/sdk/ai/trackspeed");
278
+ }
279
+ /**
280
+ * The Tail 2's own six-speed enum (bundle-verified): superLazy | lazy |
281
+ * slow | fast | crazy | customized. NOT the Tiny 2's standard/sport pair.
282
+ */
283
+ async trackSpeedSet(speed, verify = {}) {
284
+ await this.send("PUT", "/camera/sdk/ai/trackspeed", { speed });
285
+ const v = await this.verified(() => this.trackSpeedGet(), (r) => r.speed === speed, verify);
286
+ return { settled: v.settled, speed: v.value.speed };
287
+ }
288
+ // ---- record / capture --------------------------------------------------------
289
+ //
290
+ // From the vendor REST doc, hardware-verified 2026-09-30: record is a
291
+ // {"recording":"on"|"off"} switch (GET read back "off" with no SD card —
292
+ // state is readable regardless; starting without media will fail), and
293
+ // capture is a body-less POST trigger.
294
+ recordGet() {
295
+ return this.req("GET", "/camera/sdk/record/control");
296
+ }
297
+ async recordSet(on, verify = {}) {
298
+ const want = on ? "on" : "off";
299
+ await this.send("PUT", "/camera/sdk/record/control", { recording: want });
300
+ const v = await this.verified(() => this.recordGet(), (r) => r.recording === want, verify);
301
+ return { settled: v.settled, recording: v.value.recording };
302
+ }
303
+ /** Still-photo trigger. Needs storage; the ack says the camera accepted it. */
304
+ captureTrigger() {
305
+ return this.send("POST", "/camera/sdk/capture/trigger", {});
306
+ }
307
+ // ---- focus ---------------------------------------------------------------------
308
+ //
309
+ // af/mode is {"mode":"afc"|"afs"|"mf"}; af/motorposition is 0-100 and is
310
+ // MODE-GATED: HTTP 500 while the mode is not mf (measured — the value does
311
+ // not exist outside manual focus).
312
+ focusModeGet() {
313
+ return this.req("GET", "/camera/sdk/image/af/mode");
314
+ }
315
+ async focusModeSet(mode, verify = {}) {
316
+ await this.send("PUT", "/camera/sdk/image/af/mode", { mode });
317
+ const v = await this.verified(() => this.focusModeGet(), (r) => r.mode === mode, verify);
318
+ return { settled: v.settled, mode: v.value.mode };
319
+ }
320
+ focusPositionGet() {
321
+ return this.req("GET", "/camera/sdk/image/af/motorposition");
322
+ }
323
+ async focusPositionSet(position, verify = {}) {
324
+ await this.send("PUT", "/camera/sdk/image/af/motorposition", { position });
325
+ const v = await this.verified(() => this.focusPositionGet(), (r) => Math.abs(r.position - position) <= 1, verify);
326
+ return { settled: v.settled, position: v.value.position };
327
+ }
328
+ // ---- tap-to-focus / tap-to-track ------------------------------------------------
329
+ //
330
+ // Both take NORMALIZED frame coordinates (0.01-0.99, top-left 0.01/0.01),
331
+ // so a pixel from obsbot_tail2_snapshot divides by frameWidth/frameHeight
332
+ // and lands directly — the geometry-free aiming loop.
333
+ focusWindowGet() {
334
+ return this.req("GET", "/camera/sdk/image/af/windowcenter");
335
+ }
336
+ /** Move the focus window AND start a point focus (per the vendor doc). AFC/AFS only. */
337
+ async focusWindowSet(x, y, verify = {}) {
338
+ await this.send("PUT", "/camera/sdk/image/af/windowcenter", { x, y });
339
+ const v = await this.verified(() => this.focusWindowGet(), (r) => Math.abs(r.x - x) <= 0.01 && Math.abs(r.y - y) <= 0.01, verify);
340
+ return { settled: v.settled, x: v.value.x, y: v.value.y };
341
+ }
342
+ /**
343
+ * Tap-to-track (MEASURED 2026-09-30, undocumented in the Tail 2's own doc):
344
+ * POST a normalized coordinate and the camera engages tracking on the
345
+ * subject there — from mode none it armed humanTrackingSingleMode on its
346
+ * own. Fire-and-ack; which mode engages depends on what (if anything) is
347
+ * at the coordinate, so the caller reads ai mode back when it matters.
348
+ */
349
+ async targetSelect(x, y) {
350
+ await this.send("POST", "/camera/sdk/ai/workmode/normaltrack/targetselect", { x, y });
351
+ }
352
+ // ---- exposure --------------------------------------------------------------------
353
+ //
354
+ // mode manual|auto. In AUTO: auto/mode global|face (face-priority AE) and
355
+ // evbias (-3.0..3.0 in 0.3-ish steps). In MANUAL: iso and shutter ("1/N"
356
+ // string). The manual values are MODE-GATED: HTTP 500 while exposure is
357
+ // auto (measured). Manual ISO read back 894 on first switch — a live-AE
358
+ // inherited value, NOT the doc's "increment of 100"; the range is real but
359
+ // the granularity claim is not.
360
+ exposureModeGet() {
361
+ return this.req("GET", "/camera/sdk/image/exposure/mode");
362
+ }
363
+ async exposureModeSet(mode, verify = {}) {
364
+ await this.send("PUT", "/camera/sdk/image/exposure/mode", { mode });
365
+ const v = await this.verified(() => this.exposureModeGet(), (r) => r.mode === mode, verify);
366
+ return { settled: v.settled, mode: v.value.mode };
367
+ }
368
+ exposureAutoModeGet() {
369
+ return this.req("GET", "/camera/sdk/image/exposure/auto/mode");
370
+ }
371
+ async exposureAutoModeSet(mode, verify = {}) {
372
+ await this.send("PUT", "/camera/sdk/image/exposure/auto/mode", { mode });
373
+ const v = await this.verified(() => this.exposureAutoModeGet(), (r) => r.mode === mode, verify);
374
+ return { settled: v.settled, mode: v.value.mode };
375
+ }
376
+ exposureEvbiasGet() {
377
+ return this.req("GET", "/camera/sdk/image/exposure/auto/compensation");
378
+ }
379
+ async exposureEvbiasSet(evbias, verify = {}) {
380
+ // The firmware rejects JSON integers here (400 "Invalid value type" —
381
+ // measured: 0 and -1 rejected, 0.0 and -1.0 applied), and some integer
382
+ // bodies ACK without ever applying. Always send a decimal-point float.
383
+ await this.send("PUT", "/camera/sdk/image/exposure/auto/compensation", `{"evbias":${evbias.toFixed(1)}}`);
384
+ const v = await this.verified(() => this.exposureEvbiasGet(), (r) => Math.abs(r.evbias - evbias) < 0.05, verify);
385
+ return { settled: v.settled, evbias: v.value.evbias };
386
+ }
387
+ exposureIsoGet() {
388
+ return this.req("GET", "/camera/sdk/image/exposure/manual/iso");
389
+ }
390
+ async exposureIsoSet(iso, verify = {}) {
391
+ await this.send("PUT", "/camera/sdk/image/exposure/manual/iso", { iso });
392
+ const v = await this.verified(() => this.exposureIsoGet(), (r) => Math.abs(r.iso - iso) < 1, verify);
393
+ return { settled: v.settled, iso: v.value.iso };
394
+ }
395
+ exposureShutterGet() {
396
+ return this.req("GET", "/camera/sdk/image/exposure/manual/shuttertime");
397
+ }
398
+ async exposureShutterSet(shutter, verify = {}) {
399
+ await this.send("PUT", "/camera/sdk/image/exposure/manual/shuttertime", { shutter });
400
+ const v = await this.verified(() => this.exposureShutterGet(), (r) => r.shutter === shutter, verify);
401
+ return { settled: v.settled, shutter: v.value.shutter };
402
+ }
403
+ // ---- image style / hdr / white balance ---------------------------------------------
404
+ //
405
+ // style/<control> GETs return {mode, <control>} (the mode rides along);
406
+ // style/mode GET returns the whole bundle. Values are absolute 0-100.
407
+ styleGet() {
408
+ return this.req("GET", "/camera/sdk/image/style/mode");
409
+ }
410
+ async styleSet(control, value, verify = {}) {
411
+ await this.send("PUT", `/camera/sdk/image/style/${control}`, { [control]: value });
412
+ const v = await this.verified(() => this.req("GET", `/camera/sdk/image/style/${control}`), (r) => r[control] === value, verify);
413
+ return { settled: v.settled, value: v.value[control], mode: v.value.mode };
414
+ }
415
+ async styleModeSet(mode, verify = {}) {
416
+ // The PUT takes the mode with the full value bundle — send the current
417
+ // values alongside so a mode switch never stomps the adjustments.
418
+ const current = await this.styleGet();
419
+ await this.send("PUT", "/camera/sdk/image/style/mode", { ...current, mode });
420
+ const v = await this.verified(() => this.styleGet(), (r) => r.mode === mode, verify);
421
+ return { settled: v.settled, mode: v.value.mode };
422
+ }
423
+ hdrGet() {
424
+ return this.req("GET", "/camera/sdk/image/hdr/control");
425
+ }
426
+ async hdrSet(on, verify = {}) {
427
+ const want = on ? "on" : "off";
428
+ await this.send("PUT", "/camera/sdk/image/hdr/control", { control: want });
429
+ const v = await this.verified(() => this.hdrGet(), (r) => r.control === want, verify);
430
+ return { settled: v.settled, control: v.value.control };
431
+ }
432
+ wbConfigGet() {
433
+ return this.req("GET", "/camera/sdk/image/whitebalance/config");
434
+ }
435
+ async wbConfigSet(mode, temperature, verify = {}) {
436
+ // Temperature only matters in manual mode (doc), but sending the current
437
+ // one alongside is harmless and keeps the readback honest.
438
+ const current = await this.wbConfigGet();
439
+ await this.send("PUT", "/camera/sdk/image/whitebalance/config", {
440
+ mode,
441
+ temperature: temperature ?? current.temperature,
442
+ });
443
+ const v = await this.verified(() => this.wbConfigGet(), (r) => r.mode === mode && (temperature === undefined || Math.abs(r.temperature - temperature) <= 1), verify);
444
+ return { settled: v.settled, mode: v.value.mode, temperature: v.value.temperature };
445
+ }
446
+ // ---- streaming output select ---------------------------------------------------
447
+ //
448
+ // {"control":"ndi"|"rtsp"|"srt"|"off"} — exactly ONE active output. Setting
449
+ // srt displaces ndi (the exclusivity TAIL2-PROTOCOL §7a/§8 already
450
+ // measured); this is the programmatic way to arm SRT for snapshots.
451
+ streamControlGet() {
452
+ return this.req("GET", "/camera/sdk/ndi-rtsp-srt/control");
453
+ }
454
+ async streamControlSet(control, verify = {}) {
455
+ await this.send("PUT", "/camera/sdk/ndi-rtsp-srt/control", { control });
456
+ const v = await this.verified(() => this.streamControlGet(), (r) => r.control === control, verify);
457
+ return { settled: v.settled, control: v.value.control };
458
+ }
459
+ // ---- tracking extra / audio ------------------------------------------------------
460
+ onlyMeGet() {
461
+ return this.req("GET", "/camera/sdk/ai/human/onlyme");
462
+ }
463
+ async onlyMeSet(enable, verify = {}) {
464
+ await this.send("PUT", "/camera/sdk/ai/human/onlyme", { enable });
465
+ const v = await this.verified(() => this.onlyMeGet(), (r) => r.enable === enable, verify);
466
+ return { settled: v.settled, enable: v.value.enable };
467
+ }
468
+ audioVolumeGet() {
469
+ return this.req("GET", "/camera/sdk/audio/input/volume");
470
+ }
471
+ async audioVolumeSet(volume, verify = {}) {
472
+ await this.send("PUT", "/camera/sdk/audio/input/volume", { volume });
473
+ const v = await this.verified(() => this.audioVolumeGet(), (r) => Math.abs(r.volume - volume) <= 1, verify);
474
+ return { settled: v.settled, volume: v.value.volume };
475
+ }
476
+ audioMuteGet() {
477
+ return this.req("GET", "/camera/sdk/audio/input/mute");
478
+ }
479
+ async audioMuteSet(enable, verify = {}) {
480
+ await this.send("PUT", "/camera/sdk/audio/input/mute", { enable });
481
+ const v = await this.verified(() => this.audioMuteGet(), (r) => r.enable === enable, verify);
482
+ return { settled: v.settled, enable: v.value.enable };
483
+ }
484
+ // ---- presets ----------------------------------------------------------------
485
+ /**
486
+ * A saved pose: angles in DEGREES plus zoom ratio — the same units the
487
+ * tools speak. `name` is base64 on the wire; encode/decode lives here so
488
+ * callers never see it raw.
489
+ */
490
+ async presetsGet() {
491
+ const r = await this.req("GET", "/camera/sdk/ptz/preset");
492
+ return (r.presetList ?? []).map((p) => ({ ...p, name: b64decode(p.name) }));
493
+ }
494
+ /**
495
+ * `PUT ptz/preset {"operation": ..., "id": ..., "name": base64}` — one
496
+ * endpoint, four operations (grammar decoded from the camera's own web
497
+ * bundle and hardware-verified 2026-09-26; see TAIL2-PROTOCOL.md §3).
498
+ * IDs are 0-based (0–2); the tools expose 1-based slots for consistency
499
+ * with the Tiny 2's preset tools.
500
+ *
501
+ * `set` saves the CURRENT live pose — there is NO explicit-pose write
502
+ * anywhere in this API (measured), and `set` on an occupied slot
503
+ * OVERWRITES it (unlike the Tiny 2's create-once slots).
504
+ */
505
+ async presetOp(operation, id, name) {
506
+ await this.send("PUT", "/camera/sdk/ptz/preset", {
507
+ operation,
508
+ id,
509
+ ...(name !== undefined ? { name: b64encode(name) } : {}),
510
+ });
511
+ }
512
+ /**
513
+ * Save the current live pose into slot `id`. The preset LIST lags the
514
+ * write by up to ~1s (measured), so verification is a ladder, and the
515
+ * pose itself can't be verified at all — the camera reports no live pose,
516
+ * so "the slot exists afterwards" is the honest success criterion.
517
+ */
518
+ async presetSave(id, name, verify = {}) {
519
+ await this.presetOp("set", id, name);
520
+ const v = await this.verified(() => this.presetsGet(), (list) => list.some((p) => p.id === id), { attempts: 6, delayMs: 350, ...verify });
521
+ return { settled: v.settled, presets: v.value };
522
+ }
523
+ /**
524
+ * Recall slot `id`. Physical arrival is verifiable through exactly one
525
+ * observable: the preset's zoom ratio landing in the live zoom_ratio (the
526
+ * WS push carries it). The gimbal axes themselves are open-loop.
527
+ */
528
+ async presetRecall(id, verify = {}) {
529
+ const target = (await this.presetsGet()).find((p) => p.id === id);
530
+ if (!target)
531
+ throw new Error(`preset slot ${id} is empty — nothing to recall`);
532
+ await this.presetOp("call", id);
533
+ const v = await this.verified(() => this.zoomGet(), (r) => Math.abs(r.ratio - target.ratio) <= 0.05, { attempts: 14, delayMs: 450, ...verify });
534
+ return { settled: v.settled, zoom: v.value.ratio };
535
+ }
536
+ async presetDelete(id, verify = {}) {
537
+ await this.presetOp("delete", id);
538
+ const v = await this.verified(() => this.presetsGet(), (list) => !list.some((p) => p.id === id), { attempts: 6, delayMs: 350, ...verify });
539
+ return { settled: v.settled, presets: v.value };
540
+ }
541
+ async presetRename(id, name, verify = {}) {
542
+ await this.presetOp("rename", id, name);
543
+ const v = await this.verified(() => this.presetsGet(), (list) => (list.find((p) => p.id === id)?.name ?? null) === name, { attempts: 6, delayMs: 350, ...verify });
544
+ return { settled: v.settled, presets: v.value };
545
+ }
546
+ }
547
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/tail2/api.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAC;AAoF3B,0EAA0E;AAC1E,iEAAiE;AACjE,MAAM,SAAS,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACnF,MAAM,SAAS,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAEnF,MAAM,OAAO,cAAe,SAAQ,KAAK;IAE5B;IACA;IACA;IAHX,YACW,MAAc,EACd,IAAY,EACZ,IAAY;QAErB,KAAK,CAAC,cAAc,IAAI,qBAAqB,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAJxE,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAQ;QACZ,SAAI,GAAJ,IAAI,CAAQ;QAGrB,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAgBD,MAAM,KAAK,GAAG,CAAC,EAAU,EAAiB,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAEnF,MAAM,OAAO,QAAQ;IACV,OAAO,CAAS;IACR,SAAS,CAAS;IAClB,KAAK,CAAS;IAE/B,YAAY,IAA6C;QACvD,+DAA+D;QAC/D,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,OAAO,EAAE,CAAC;QACzF,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC;QACxC,IAAI,CAAC,KAAK,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC;IAC5D,CAAC;IAEO,KAAK,CAAC,GAAG,CAAI,MAAc,EAAE,IAAY,EAAE,IAAsB;QACvE,IAAI,GAAa,CAAC;QAClB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE;gBACrC,MAAM;gBACN,OAAO,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,SAAS;gBAChF,oEAAoE;gBACpE,uEAAuE;gBACvE,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;gBAC7F,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC;aAC5C,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CACb,aAAa,IAAI,CAAC,OAAO,oBAAoB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK;gBAC1F,6FAA6F,CAChG,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,IAAI,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACjE,CAAC;QACD,IAAI,CAAC;YACH,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAM,CAAC;QACjC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,oEAAoE;YACpE,qEAAqE;YACrE,sEAAsE;YACtE,MAAM,IAAI,KAAK,CACb,aAAa,IAAI,CAAC,OAAO,gCAAgC,IAAI,GAAG;gBAC9D,IAAI,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,sCAAsC;gBACpF,oEAAoE,CACvE,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,IAAI,CAAC,MAAsB,EAAE,IAAY,EAAE,IAAqB;QAC5E,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAM,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACpD,uEAAuE;QACvE,wEAAwE;QACxE,mDAAmD;QACnD,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;YACrD,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,mBAAmB,GAAG,CAAC,IAAI,YAAY,GAAG,CAAC,OAAO,IAAI,GAAG,EAAE,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,QAAQ,CACZ,IAAsB,EACtB,KAAwB,EACxB,OAAmB,EAAE;QAErB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,GAAG,CAAC;QACpC,IAAI,KAAK,GAAG,MAAM,IAAI,EAAE,CAAC;QACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACnD,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC;YACrB,KAAK,GAAG,MAAM,IAAI,EAAE,CAAC;QACvB,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC;IAC1C,CAAC;IAED,6EAA6E;IAE7E,IAAI;QACF,OAAO,IAAI,CAAC,GAAG,CAAkB,KAAK,EAAE,yBAAyB,CAAC,CAAC;IACrE,CAAC;IAED,kFAAkF;IAClF,MAAM;QACJ,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,gBAAgB,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;YAC3E,yEAAyE;YACzE,yEAAyE;YACzE,qEAAqE;YACrE,uEAAuE;YACvE,oEAAoE;YACpE,mEAAmE;YACnE,sEAAsE;YACtE,oCAAoC;YACpC,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,MAAM,MAAM,GAAG,CAAC,EAAc,EAAQ,EAAE;gBACtC,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,EAAE,EAAE,CAAC;YACP,CAAC,CAAC;YACF,MAAM,KAAK,GAAG,UAAU,CACtB,GAAG,EAAE,CACH,MAAM,CAAC,GAAG,EAAE;gBACV,EAAE,CAAC,SAAS,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,KAAK,CAAC,0BAA0B,IAAI,CAAC,KAAK,+BAA+B,CAAC,CAAC,CAAC;YACzF,CAAC,CAAC,EACJ,IAAI,CAAC,SAAS,GAAG,IAAI,CACtB,CAAC;YACF,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAQ,EAAE,EAAE,CAC1B,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAC9E,CAAC;YACF,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAClB,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC,CAAC,CAC/E,CAAC;YACF,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAuB,EAAE,EAAE;gBAC3C,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAgB,CAAC;oBAC1D,MAAM,CAAC,GAAG,EAAE;wBACV,EAAE,CAAC,SAAS,EAAE,CAAC;wBACf,OAAO,CAAC,MAAM,CAAC,CAAC;oBAClB,CAAC,CAAC,CAAC;gBACL,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACtE,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,GAAG,CAA0B,KAAK,EAAE,mBAAmB,CAAC,CAAC;IACvE,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,GAAG,CAA0B,KAAK,EAAE,2BAA2B,CAAC,CAAC;IAC/E,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,GAAG,CAA0B,KAAK,EAAE,mBAAmB,CAAC,CAAC;IACvE,CAAC;IAED,6EAA6E;IAE7E,OAAO;QACL,OAAO,IAAI,CAAC,GAAG,CAAoB,KAAK,EAAE,sBAAsB,CAAC,CAAC;IACpE,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO,CACX,KAAa,EACb,KAAa,EACb,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,sBAAsB,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QACjE,sEAAsE;QACtE,4DAA4D;QAC5D,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,EACpB,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,IAAI,EACxC,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,CAC1C,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACtD,CAAC;IAED,sFAAsF;IACtF,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,uBAAuB,EAAE,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,+EAA+E;IAC/E,EAAE;IACF,yEAAyE;IACzE,uEAAuE;IACvE,wEAAwE;IACxE,0EAA0E;IAC1E,mDAAmD;IAEnD,WAAW,CAAC,GAAW,EAAE,KAAa,EAAE,IAAY;QAClD,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,+BAA+B,EAAE;YACxD,IAAI,EAAE,KAAK;YACX,KAAK;YACL,IAAI;YACJ,GAAG;SACJ,CAAC,CAAC;IACL,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,+BAA+B,EAAE;YACxD,IAAI,EAAE,IAAI;YACV,KAAK,EAAE,CAAC;YACR,IAAI,EAAE,CAAC;YACP,GAAG,EAAE,CAAC;SACP,CAAC,CAAC;IACL,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC,GAAG,CAAsB,KAAK,EAAE,8BAA8B,CAAC,CAAC;IAC9E,CAAC;IAED,+EAA+E;IAC/E,KAAK,CAAC,eAAe,CACnB,MAAe,EACf,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,8BAA8B,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACnE,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,eAAe,EAAE,EAC5B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,EAC1B,MAAM,CACP,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;IACxD,CAAC;IAED,iFAAiF;IACjF,EAAE;IACF,4EAA4E;IAC5E,2EAA2E;IAC3E,sEAAsE;IACtE,yEAAyE;IACzE,wEAAwE;IACxE,8DAA8D;IAE9D,gFAAgF;IAChF,MAAM,CAAU,eAAe,GAAG,YAAY,CAAC;IAE/C,KAAK,CAAC,QAAQ,CAAC,SAAqB,EAAE;QACpC,IAAI,OAA2B,CAAC;QAChC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACrC,uEAAuE;QACvE,uEAAuE;QACvE,2DAA2D;QAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,eAAe,CAAC,CAAC;QACvE,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC3C,MAAM,IAAI,CAAC,QAAQ,CACjB,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,EACvB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,EAAE,CAAC,EAC3C,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,CACzC,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7C,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAChD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,mFAAmF;gBACjF,2DAA2D,CAC9D,CAAC;QACJ,CAAC;QACD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;YAC9D,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,EACvB,CAAC,CAAC,EAAE,EAAE;gBACJ,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;gBAC1C,OAAO,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,eAAe,CAAC;YAChE,CAAC,EACD,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,CACzC,CAAC;YACF,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;YAChD,IAAI,CAAC,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;YACzF,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;QACtE,CAAC;gBAAS,CAAC;YACT,sDAAsD;YACtD,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAED,8EAA8E;IAE9E;;;;OAIG;IACH,KAAK,CAAC,WAAW,CACf,MAAe,EACf,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,6BAA6B,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACnE,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,EACnB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,KAAK,MAAM,EACnC,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,CAC1C,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,KAAK,IAAI,EAAE,CAAC;IACnF,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,GAAG,CAAoB,KAAK,EAAE,0BAA0B,CAAC,CAAC;IACxE,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,WAAW,CACf,KAAa,EACb,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,0BAA0B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9D,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,EACxB,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,IAAI,EACxC,MAAM,CACP,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACtD,CAAC;IAED,+EAA+E;IAE/E,SAAS;QACP,OAAO,IAAI,CAAC,GAAG,CAAmB,KAAK,EAAE,yBAAyB,CAAC,CAAC;IACtE,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,SAAS,CACb,IAAY,EACZ,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,yBAAyB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,EAAE,MAAM,CAAC,CAAC;QACtF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IACpD,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,GAAG,CAAoB,KAAK,EAAE,2BAA2B,CAAC,CAAC;IACzE,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,aAAa,CACjB,KAAa,EACb,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,2BAA2B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/D,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,EAAE,MAAM,CAAC,CAAC;QAC5F,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACtD,CAAC;IAED,iFAAiF;IACjF,EAAE;IACF,sEAAsE;IACtE,yEAAyE;IACzE,uEAAuE;IACvE,uCAAuC;IAEvC,SAAS;QACP,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,4BAA4B,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,EAAW,EAAE,SAAqB,EAAE;QAClD,MAAM,IAAI,GAAiB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;QAC7C,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,4BAA4B,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1E,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3F,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;IAC9D,CAAC;IAED,+EAA+E;IAC/E,cAAc;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,6BAA6B,EAAE,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,mFAAmF;IACnF,EAAE;IACF,yEAAyE;IACzE,2EAA2E;IAC3E,mCAAmC;IAEnC,YAAY;QACV,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,2BAA2B,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,IAA0B,EAC1B,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,2BAA2B,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9D,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,EAAE,MAAM,CAAC,CAAC;QACzF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IACpD,CAAC;IAED,gBAAgB;QACd,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,oCAAoC,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,QAAgB,EAChB,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,oCAAoC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC3E,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAC7B,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,EAC3C,MAAM,CACP,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC5D,CAAC;IAED,oFAAoF;IACpF,EAAE;IACF,0EAA0E;IAC1E,0EAA0E;IAC1E,sDAAsD;IAEtD,cAAc;QACZ,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,mCAAmC,CAAC,CAAC;IAC9D,CAAC;IAED,wFAAwF;IACxF,KAAK,CAAC,cAAc,CAClB,CAAS,EACT,CAAS,EACT,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,mCAAmC,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACtE,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,EAC3B,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,EAC7D,MAAM,CACP,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;IAC5D,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,YAAY,CAAC,CAAS,EAAE,CAAS;QACrC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,kDAAkD,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACxF,CAAC;IAED,qFAAqF;IACrF,EAAE;IACF,0EAA0E;IAC1E,yEAAyE;IACzE,wEAAwE;IACxE,wEAAwE;IACxE,2EAA2E;IAC3E,gCAAgC;IAEhC,eAAe;QACb,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,iCAAiC,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,eAAe,CACnB,IAAuB,EACvB,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,iCAAiC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,EAAE,MAAM,CAAC,CAAC;QAC5F,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IACpD,CAAC;IAED,mBAAmB;QACjB,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,sCAAsC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,mBAAmB,CACvB,IAAuB,EACvB,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,sCAAsC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACzE,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,EAAE,MAAM,CAAC,CAAC;QAChG,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IACpD,CAAC;IAED,iBAAiB;QACf,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,8CAA8C,CAAC,CAAC;IACzE,CAAC;IAED,KAAK,CAAC,iBAAiB,CACrB,MAAc,EACd,SAAqB,EAAE;QAEvB,sEAAsE;QACtE,uEAAuE;QACvE,uEAAuE;QACvE,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,8CAA8C,EAAE,aAAa,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC1G,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAC9B,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI,EACzC,MAAM,CACP,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;IACxD,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,uCAAuC,CAAC,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,GAAW,EACX,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,uCAAuC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACzE,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,EAC3B,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,EAChC,MAAM,CACP,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;IAClD,CAAC;IAED,kBAAkB;QAChB,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,+CAA+C,CAAC,CAAC;IAC1E,CAAC;IAED,KAAK,CAAC,kBAAkB,CACtB,OAAe,EACf,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,+CAA+C,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACrF,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,EAAE,MAAM,CAAC,CAAC;QACrG,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IAC1D,CAAC;IAED,uFAAuF;IACvF,EAAE;IACF,wEAAwE;IACxE,sEAAsE;IAEtE,QAAQ;QAQN,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,8BAA8B,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,QAAQ,CACZ,OAAuE,EACvE,KAAa,EACb,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,2BAA2B,OAAO,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACnF,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAkC,KAAK,EAAE,2BAA2B,OAAO,EAAE,CAAC,EAC5F,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,KAAK,EAC3B,MAAM,CACP,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAW,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAc,EAAE,CAAC;IACjG,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,IAAkD,EAClD,SAAqB,EAAE;QAEvB,uEAAuE;QACvE,kEAAkE;QAClE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,8BAA8B,EAAE,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7E,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,EAAE,MAAM,CAAC,CAAC;QACrF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IACpD,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,+BAA+B,CAAC,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAW,EAAE,SAAqB,EAAE;QAC/C,MAAM,IAAI,GAAiB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;QAC7C,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,+BAA+B,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3E,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,EAAE,MAAM,CAAC,CAAC;QACtF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IAC1D,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,uCAAuC,CAAC,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,WAAW,CACf,IAA4E,EAC5E,WAA+B,EAC/B,SAAqB,EAAE;QAEvB,yEAAyE;QACzE,2DAA2D;QAC3D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QACzC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,uCAAuC,EAAE;YAC9D,IAAI;YACJ,WAAW,EAAE,WAAW,IAAI,OAAO,CAAC,WAAW;SAChD,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,EACxB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,EACnG,MAAM,CACP,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;IACtF,CAAC;IAED,mFAAmF;IACnF,EAAE;IACF,4EAA4E;IAC5E,mEAAmE;IACnE,oEAAoE;IAEpE,gBAAgB;QACd,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,kCAAkC,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,OAAuC,EACvC,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,kCAAkC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACxE,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,EAAE,MAAM,CAAC,CAAC;QACnG,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IAC1D,CAAC;IAED,qFAAqF;IAErF,SAAS;QACP,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,6BAA6B,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,SAAS,CACb,MAAe,EACf,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,6BAA6B,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAClE,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1F,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;IACxD,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,gCAAgC,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,MAAc,EACd,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,gCAAgC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACrE,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,EAC3B,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,EACvC,MAAM,CACP,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;IACxD,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,8BAA8B,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,MAAe,EACf,SAAqB,EAAE;QAEvB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,8BAA8B,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACnE,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC;QAC7F,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;IACxD,CAAC;IAED,gFAAgF;IAEhF;;;;OAIG;IACH,KAAK,CAAC,UAAU;QACd,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,GAAG,CACtB,KAAK,EACL,wBAAwB,CACzB,CAAC;QACF,OAAO,CAAC,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9E,CAAC;IAED;;;;;;;;;;OAUG;IACK,KAAK,CAAC,QAAQ,CACpB,SAA+C,EAC/C,EAAU,EACV,IAAa;QAEb,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,wBAAwB,EAAE;YAC/C,SAAS;YACT,EAAE;YACF,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACzD,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,UAAU,CAAC,EAAU,EAAE,IAAY,EAAE,SAAqB,EAAE;QAChE,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,EACvB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EACvC,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,CACzC,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;IAClD,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY,CAAC,EAAU,EAAE,SAAqB,EAAE;QACpD,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAClE,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,eAAe,EAAE,+BAA+B,CAAC,CAAC;QAC/E,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAChC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,EACpB,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,EAC/C,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,CAC1C,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,EAAU,EAAE,SAAqB,EAAE;QACpD,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,EACvB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EACxC,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,CACzC,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,EAAU,EAAE,IAAY,EAAE,SAAqB,EAAE;QAClE,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,EACvB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC,KAAK,IAAI,EAChE,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,CACzC,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;IAClD,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Passive mDNS discovery for the OBSBOT Tail 2.
3
+ *
4
+ * The Tail 2 has no mDNS query responder — it ANNOUNCES. Every few seconds it
5
+ * multicasts a response-framed packet to 224.0.0.251:5353 (IPv4, and the same
6
+ * digest over IPv6 link-local) carrying, in the TXT record of its
7
+ * `_remo_mdns._tcp.local` service, a full device digest: both MACs, both IPs,
8
+ * the hex-encoded device name, battery and sleep flags. Measured 2026-09-30 by
9
+ * capturing OBSBOT Center's discovery: Center sent no queries either — the
10
+ * announcements alone are how everything on the LAN finds the camera.
11
+ * TAIL2-PROTOCOL.md §2a.
12
+ *
13
+ * So discovery here is a listen, not a sweep: bind 5353 with SO_REUSEADDR
14
+ * (the OS's own responder holds the port on every platform; multicast
15
+ * delivery to multiple REUSEADDR sockets is uniform across Windows, Linux
16
+ * and macOS — it is how NDI tools coexist), join the group on every
17
+ * non-internal IPv4 interface, and collect Tail 2-shaped announcements.
18
+ * TAIL2-PROTOCOL.md §7a.
19
+ */
20
+ /** What one camera's announcement tells us — identity straight from the camera. */
21
+ export interface Tail2Announcement {
22
+ mac: string;
23
+ name: string;
24
+ /** Wired first, then wireless, then the address the packet arrived from. */
25
+ hosts: string[];
26
+ }
27
+ /**
28
+ * Extract a Tail 2 announcement from one mDNS packet, or null for anything
29
+ * else on the wire. Detection is by the digest's own shape — TXT strings that
30
+ * JSON-parse into an object carrying a `ble_mac` (the registry's identity
31
+ * MAC) plus a reachable IP — so other devices' mDNS traffic parses to nothing
32
+ * and registers nothing. `srcIp` is where the packet came from; it becomes a
33
+ * host of last resort and a live-reachability hint.
34
+ */
35
+ export declare function parseTail2Announcement(buf: Buffer, srcIp?: string): Tail2Announcement | null;
36
+ /** Accumulates announcements across packets, keyed by MAC. Testable without
37
+ * sockets: push() buffers, results() the deduplicated list. */
38
+ export declare function createAnnouncementCollector(): {
39
+ push(buf: Buffer, srcIp?: string): void;
40
+ results(): Tail2Announcement[];
41
+ };
42
+ /**
43
+ * Listen for Tail 2 announcements for `ms` milliseconds (the camera repeats
44
+ * every few seconds, so 5s is ample) and return what was heard. Never throws:
45
+ * a bind failure or a network without multicast yields an empty list, and the
46
+ * caller falls back to the HTTP sweep.
47
+ */
48
+ export declare function listenForTail2Announcements(ms?: number): Promise<Tail2Announcement[]>;