obsbot-mcp 0.3.0 → 0.4.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 (69) hide show
  1. package/README.md +197 -38
  2. package/dist/codec/commands.d.ts +42 -4
  3. package/dist/codec/commands.js +72 -10
  4. package/dist/codec/commands.js.map +1 -1
  5. package/dist/codec/frame.d.ts +1 -0
  6. package/dist/codec/frame.js +1 -1
  7. package/dist/codec/frame.js.map +1 -1
  8. package/dist/codec/preset.d.ts +40 -0
  9. package/dist/codec/preset.js +198 -0
  10. package/dist/codec/preset.js.map +1 -0
  11. package/dist/codec/types.d.ts +4 -0
  12. package/dist/device/helper-factory.d.ts +25 -0
  13. package/dist/device/helper-factory.js +40 -0
  14. package/dist/device/helper-factory.js.map +1 -0
  15. package/dist/device/manager.d.ts +326 -2
  16. package/dist/device/manager.js +720 -24
  17. package/dist/device/manager.js.map +1 -1
  18. package/dist/ipc/client.d.ts +19 -0
  19. package/dist/ipc/client.js +81 -0
  20. package/dist/ipc/client.js.map +1 -0
  21. package/dist/ipc/coordinator.d.ts +29 -0
  22. package/dist/ipc/coordinator.js +94 -0
  23. package/dist/ipc/coordinator.js.map +1 -0
  24. package/dist/ipc/owner.d.ts +21 -0
  25. package/dist/ipc/owner.js +59 -0
  26. package/dist/ipc/owner.js.map +1 -0
  27. package/dist/ipc/protocol.d.ts +21 -0
  28. package/dist/ipc/protocol.js +56 -0
  29. package/dist/ipc/protocol.js.map +1 -0
  30. package/dist/ipc/rendezvous.d.ts +26 -0
  31. package/dist/ipc/rendezvous.js +93 -0
  32. package/dist/ipc/rendezvous.js.map +1 -0
  33. package/dist/mcp/log-sink.d.ts +12 -0
  34. package/dist/mcp/log-sink.js +27 -0
  35. package/dist/mcp/log-sink.js.map +1 -0
  36. package/dist/mcp/ready.d.ts +16 -4
  37. package/dist/mcp/ready.js +11 -8
  38. package/dist/mcp/ready.js.map +1 -1
  39. package/dist/mcp/render.js +1 -1
  40. package/dist/mcp/render.js.map +1 -1
  41. package/dist/mcp/server.js +68 -18
  42. package/dist/mcp/server.js.map +1 -1
  43. package/dist/mcp/tools.d.ts +5 -3
  44. package/dist/mcp/tools.js +787 -186
  45. package/dist/mcp/tools.js.map +1 -1
  46. package/dist/transport/helper-process.d.ts +36 -0
  47. package/dist/transport/helper-process.js +190 -10
  48. package/dist/transport/helper-process.js.map +1 -1
  49. package/dist/transport/linux.d.ts +40 -0
  50. package/dist/transport/linux.js +92 -2
  51. package/dist/transport/linux.js.map +1 -1
  52. package/dist/transport/macos.d.ts +13 -0
  53. package/dist/transport/macos.js +59 -3
  54. package/dist/transport/macos.js.map +1 -1
  55. package/dist/transport/read-serial.d.ts +26 -0
  56. package/dist/transport/read-serial.js +87 -0
  57. package/dist/transport/read-serial.js.map +1 -0
  58. package/dist/transport/transport.d.ts +24 -0
  59. package/dist/transport/windows.d.ts +4 -0
  60. package/dist/transport/windows.js +31 -4
  61. package/dist/transport/windows.js.map +1 -1
  62. package/native/prebuilt/darwin-arm64/obsbot-helper +0 -0
  63. package/native/prebuilt/darwin-x64/obsbot-helper +0 -0
  64. package/native/prebuilt/linux-x64/obsbot-helper +0 -0
  65. package/native/prebuilt/win32-x64/obsbot-helper.exe +0 -0
  66. package/package.json +3 -2
  67. package/dist/device/session.d.ts +0 -22
  68. package/dist/device/session.js +0 -37
  69. package/dist/device/session.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"frame.js","sourceRoot":"","sources":["../../src/codec/frame.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"frame.js","sourceRoot":"","sources":["../../src/codec/frame.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ export interface PresetPose {
2
+ pan: number;
3
+ tilt: number;
4
+ roll: number;
5
+ zoom: number;
6
+ }
7
+ export declare const encodePresetAdd: (seq: number, slot: number, pose: PresetPose) => Buffer;
8
+ export declare const encodePresetUpdate: (seq: number, slot: number, pose: PresetPose) => Buffer;
9
+ export declare const encodePresetRecall: (seq: number, slot: number) => Buffer;
10
+ export declare const encodePresetDelete: (seq: number, slot: number) => Buffer;
11
+ export declare const encodePresetSetName: (seq: number, slot: number, name: string) => Buffer;
12
+ export declare const encodeBootPose: (seq: number, slot: number, pose: PresetPose) => Buffer;
13
+ export declare const encodeBootFlags: (seq: number) => Buffer;
14
+ export interface PresetListBlock {
15
+ count: number;
16
+ slots: number[];
17
+ }
18
+ export declare const decodePresetList: (block: Buffer) => PresetListBlock;
19
+ export declare const implausiblePresetListReason: (block: Buffer) => string | null;
20
+ export interface PresetEntry {
21
+ end: boolean;
22
+ slot?: 1 | 2 | 3;
23
+ name?: string;
24
+ pose?: PresetPose;
25
+ }
26
+ export declare const decodePresetEntry: (block: Buffer) => PresetEntry;
27
+ export interface PresetSlot {
28
+ slot: 1 | 2 | 3;
29
+ occupied: boolean;
30
+ name: string | null;
31
+ pose: PresetPose | null;
32
+ }
33
+ export declare const assemblePresetSlots: (perSlot: {
34
+ slot: 1 | 2 | 3;
35
+ name: string;
36
+ pose: PresetPose;
37
+ }[]) => PresetSlot[];
38
+ export declare const encodeGimBootPosSet: (seq: number, pose: PresetPose) => Buffer;
39
+ export declare const encodeGimBootPosReset: (seq: number) => Buffer;
40
+ export declare const encodeGimBootPosTrigger: (seq: number) => Buffer;
@@ -0,0 +1,198 @@
1
+ import { buildFrame } from "./frame.js";
2
+ import { f32le, u32le, concat } from "./encoding.js";
3
+ import { OP_BY_NAME } from "./opcodes.js";
4
+ // Opcodes come from the generated table (tools/opcodes/opcodes.json, extracted from
5
+ // libdev.dll with PDB symbols) under the firmware's OWN names. This file previously
6
+ // kept a hand-written copy with invented names — notably `BOOT_POSE: 0x3ec4` and
7
+ // `BOOT_FLAGS: 0x3e44`, neither of which is what those commands do:
8
+ // 0x3ec4 = AI_SET_BOOT_PRESET_UPDATE_ONLY — BINDS AN EXISTING PRESET as the boot
9
+ // preset (hence OBSBOT Center's preset-identifying step before it)
10
+ // 0x3e44 = AI_SET_BOOT_PRESETS_ACTIONS — a "boot presets actions" record
11
+ // Those invented names actively misled the reading of this protocol, so resolve by
12
+ // real name and let the table be the single source of truth.
13
+ const op = (name) => {
14
+ const wire = OP_BY_NAME.get(name)?.wireCmd;
15
+ if (wire == null)
16
+ throw new Error(`unknown or non-sendable opcode: ${name}`);
17
+ return wire;
18
+ };
19
+ const CMD = {
20
+ ADD: op("AI_SET_GIMBAL_PRESET_ADD"),
21
+ UPDATE: op("AI_SET_PRESET_UPDATE_ONLY"),
22
+ RECALL: op("AI_SET_GIMBAL_PRESET_TRIG"),
23
+ DELETE: op("AI_SET_GIMBAL_PRESET_DELETE"),
24
+ SET_NAME: op("AI_SET_GIMBAL_PRESET_ID_NAME"),
25
+ // Legacy names retained for the existing As-Initial-State replay path; see the
26
+ // real semantics above. Prefer the GIM_BOOT_POS family below.
27
+ BOOT_POSE: op("AI_SET_BOOT_PRESET_UPDATE_ONLY"),
28
+ BOOT_FLAGS: op("AI_SET_BOOT_PRESETS_ACTIONS"),
29
+ // The purpose-built boot-pose family: set / reset / trigger, all decoded.
30
+ GIM_BOOT_POS_SET: op("AI_SET_GIM_BOOT_POS"),
31
+ GIM_BOOT_POS_RESET: op("AI_RST_GIM_BOOT_POS"),
32
+ GIM_BOOT_POS_TRIGGER: op("AI_TRG_GIM_BOOT_POS"),
33
+ };
34
+ const RECEIVER = 0x04;
35
+ const idx = (slot) => u32le(slot - 1);
36
+ const poseBytes = (p) => concat(f32le(p.pan), f32le(p.tilt), f32le(p.roll), f32le(p.zoom), f32le(-1000));
37
+ export const encodePresetAdd = (seq, slot, pose) => buildFrame({ seq, cmd: CMD.ADD, receiver: RECEIVER, payload: concat(idx(slot), poseBytes(pose)) });
38
+ export const encodePresetUpdate = (seq, slot, pose) => buildFrame({ seq, cmd: CMD.UPDATE, receiver: RECEIVER, payload: concat(idx(slot), poseBytes(pose)) });
39
+ export const encodePresetRecall = (seq, slot) => buildFrame({ seq, cmd: CMD.RECALL, receiver: RECEIVER,
40
+ payload: concat(idx(slot), f32le(1), f32le(1), f32le(1), f32le(1)) });
41
+ export const encodePresetDelete = (seq, slot) => buildFrame({ seq, cmd: CMD.DELETE, receiver: RECEIVER, payload: idx(slot) });
42
+ export const encodePresetSetName = (seq, slot, name) => buildFrame({ seq, cmd: CMD.SET_NAME, receiver: RECEIVER,
43
+ payload: concat(idx(slot), Buffer.from(name, "ascii")) });
44
+ // RETAINED AS DECODED PROTOCOL KNOWLEDGE — NOT REACHABLE FROM ANY TOOL.
45
+ // This is the OBSBOT Center "As Initial State" replay. It was removed from the tool
46
+ // surface once the command names were recovered: 0x3ec4 is AI_SET_BOOT_PRESET_UPDATE_ONLY
47
+ // (binds an existing preset as the boot preset) and 0x3e44 is AI_SET_BOOT_PRESETS_ACTIONS
48
+ // (an undecoded 40-byte "actions" record captured from one device state, carrying no slot
49
+ // binding of its own). Neither has a decoded way to be undone. The GIM_BOOT_POS family
50
+ // below does the same job with a real reset, so prefer it. Kept here, with its golden
51
+ // tests, so the captured sequence is not lost.
52
+ //
53
+ // Boot-pose sequence: slot index + pose, but — unlike ADD/UPDATE — the trailing
54
+ // float is a plain 0.0, not the -1000 sentinel. Confirmed against a captured
55
+ // OBSBOT Center "As Initial State" wire frame (cmd 0x3ec4); do not fold this
56
+ // back into poseBytes(), which ADD/UPDATE's golden tests pin to -1000.
57
+ export const encodeBootPose = (seq, slot, pose) => buildFrame({
58
+ seq,
59
+ cmd: CMD.BOOT_POSE,
60
+ receiver: RECEIVER,
61
+ payload: concat(idx(slot), f32le(pose.pan), f32le(pose.tilt), f32le(pose.roll), f32le(pose.zoom), f32le(0)),
62
+ });
63
+ // Captured verbatim from the single observed As-Initial-State wire sequence (cmd
64
+ // 0x3e44). Carries no slot index of its own — the target slot is conveyed by the
65
+ // preceding encodeBootPose frame. Internal structure of these 40 bytes is NOT
66
+ // decoded (no per-field meaning known) and this constant has NOT been
67
+ // hardware-replayed independently — only observed as part of the captured sequence.
68
+ const BOOT_FLAGS_BLOCK = Buffer.from("feffffffffffffff80ffffffffffffff00000000ffffffff00000000000000000000000000000000", "hex");
69
+ export const encodeBootFlags = (seq) => buildFrame({ seq, cmd: CMD.BOOT_FLAGS, receiver: RECEIVER, payload: BOOT_FLAGS_BLOCK });
70
+ export const decodePresetList = (block) => {
71
+ const count = block[0];
72
+ const slots = [];
73
+ for (let i = 0; i < count; i++)
74
+ slots.push(block[1 + i]);
75
+ return { count, slots };
76
+ };
77
+ // C1: sanity-check a raw selector-12 read BEFORE a caller echoes it back to reset
78
+ // the entry cursor. Selector 12's write semantics for anything OTHER than exactly
79
+ // what the device just returned are undecoded, so a failed/short/garbage read
80
+ // (asleep camera, stale handle, transient USB error) must never be echoed —
81
+ // echoing is only proven non-destructive for a genuine device response. Returns
82
+ // a human-readable reason if the block looks implausible, or null if it's safe
83
+ // to trust and echo.
84
+ //
85
+ // Design note: this checks the WHOLE returned block for all-zero, not just the
86
+ // leading `1 + count` bytes. `block[0]` IS `count`, so for any count > 0 the
87
+ // leading bytes can never be all-zero by construction — a "leading bytes"
88
+ // check is only ever triggered by count === 0, which would make it equivalent
89
+ // to unconditionally rejecting "zero presets saved" (a legitimate device
90
+ // state, not distinguishable from failure by count alone). Checking the full
91
+ // block instead targets the actual failure signature — a dead/asleep read
92
+ // returning nothing — while still accepting a genuine empty-list reply as
93
+ // long as ANY other byte in the 60-byte block is non-zero.
94
+ export const implausiblePresetListReason = (block) => {
95
+ if (block.length < 1)
96
+ return "empty response (0 bytes)";
97
+ const count = block[0];
98
+ if (count > 3)
99
+ return `implausible slot count ${count} (device has 3 slots)`;
100
+ if (block.length < 1 + count)
101
+ return `short response: ${block.length} bytes for count=${count}`;
102
+ if (block.equals(Buffer.alloc(block.length)))
103
+ return "all-zero response — looks like a failed/short read";
104
+ return null;
105
+ };
106
+ const ENTRY_END = 0x02;
107
+ // Minimum bytes needed to read the fixed header (status, slotIdx, 2 reserved,
108
+ // pitch i16, yaw i16, zoom u8) before the variable-length base64 name starts
109
+ // at offset 10.
110
+ const ENTRY_HEADER_LEN = 10;
111
+ export const decodePresetEntry = (block) => {
112
+ if (block.length > 0 && block[0] === ENTRY_END)
113
+ return { end: true };
114
+ // I4: a buffer too short to hold the fixed header can't be decoded — without
115
+ // this guard, block.readInt16LE(4)/(6) below throws a raw RangeError that
116
+ // surfaces to the caller as an opaque ERR_OUT_OF_RANGE. Treat it the same as
117
+ // end-of-list/invalid instead.
118
+ if (block.length < ENTRY_HEADER_LEN)
119
+ return { end: true };
120
+ // I3/I4: an all-zero header is exactly what a failed/short USB read decodes
121
+ // as (status 0x00 "not end", slotIdx 0, pitch/yaw/zoom all 0) — it is NOT a
122
+ // real occupied slot. A committed preset's zoom is always >=1.0 (block[8]
123
+ // >=100), so an all-zero header never corresponds to a genuine entry. Treat
124
+ // it as end-of-list/invalid: the dangerous failure mode for a create-once
125
+ // resource is a false OCCUPIED read, never a false EMPTY.
126
+ if (block.subarray(0, ENTRY_HEADER_LEN).equals(Buffer.alloc(ENTRY_HEADER_LEN))) {
127
+ return { end: true };
128
+ }
129
+ const slotIdx = block[1];
130
+ // I4: only slot indices 0..2 are valid on this 3-slot device. Without this
131
+ // check a garbage block[1] (e.g. 7) silently becomes slot:8, which then
132
+ // misses assemblePresetSlots's 1|2|3 lookup and collapses to an all-empty
133
+ // result instead of surfacing the corruption.
134
+ if (slotIdx > 2)
135
+ return { end: true };
136
+ const slot = (slotIdx + 1);
137
+ const pitch = block.readInt16LE(4) / 100;
138
+ const yaw = block.readInt16LE(6) / 100;
139
+ const zoom = block[8] / 100;
140
+ const nul = block.indexOf(0, 10);
141
+ const b64 = block.subarray(10, nul === -1 ? block.length : nul).toString("ascii");
142
+ const name = Buffer.from(b64, "base64").toString("ascii");
143
+ return { end: false, slot, name, pose: { pan: yaw, tilt: pitch, roll: 0, zoom } };
144
+ };
145
+ // I1: this used to take an unused `_count` parameter. Decision: drop it rather
146
+ // than consume it here — the authoritative-count reconciliation belongs one
147
+ // level up, in mcp/tools.ts's getPresetSlots, which has BOTH the selector-12
148
+ // list (the device's own claim of which slots are occupied) and the walked
149
+ // selector-13 entries available to cross-check against each other. By the time
150
+ // perSlot reaches this function it is already trusted; this function's only
151
+ // job is reshaping it into the fixed 3-slot view.
152
+ export const assemblePresetSlots = (perSlot) => {
153
+ const byslot = new Map(perSlot.map((e) => [e.slot, e]));
154
+ return [1, 2, 3].map((slot) => {
155
+ const e = byslot.get(slot);
156
+ return e
157
+ ? { slot, occupied: true, name: e.name, pose: e.pose }
158
+ : { slot, occupied: false, name: null, pose: null };
159
+ });
160
+ };
161
+ // --- Boot pose: the direct, reversible family --------------------------------
162
+ // AI_SET_GIM_BOOT_POS / AI_RST_GIM_BOOT_POS / AI_TRG_GIM_BOOT_POS. Unlike the
163
+ // As-Initial-State replay (which binds a preset and writes an undecoded 40-byte
164
+ // "actions" record), every command here is decoded, and RST restores the factory
165
+ // default — so setting a boot pose is undoable.
166
+ //
167
+ // PAYLOAD LAYOUT IS A HYPOTHESIS, pending hardware. Field ORDER is solid: read
168
+ // from libdev's own movss stores in aiSetGimbalBootPosR (buf+0x05 = yaw from
169
+ // [rbx+0xC], +0x09 = pitch from [rbx+8], +0x0D = roll from [rbx+4], +0x11 = zoom).
170
+ // What is NOT confirmed is whether the UVC path uses the same framing as that
171
+ // (network) path, which had an extra zero byte at +0x04 and unaligned floats. We
172
+ // mirror our own frame convention instead: u32 id, then four aligned floats.
173
+ //
174
+ // RESOLVED 2026-07-20. The layout above is correct, and it is now confirmed by
175
+ // readback rather than inference: AI_GET_GIM_BOOT_POS (0x3884) returns this exact
176
+ // record, and the vendor GET path works once framed with frame[1] = 0x01 (the
177
+ // header-only GET flavour). The old note here — that our transport cannot read
178
+ // vendor GET replies — was wrong; GETs were being sent with the SET flags byte,
179
+ // which the device does not answer.
180
+ //
181
+ // PAYLOAD WIDTH IS LOAD-BEARING: the device requires 24 bytes (six float32) and
182
+ // SILENTLY DISCARDS a 20-byte payload — no error, no state change, readback
183
+ // unchanged. This encoder shipped 20 bytes, so every boot-pose write it ever made
184
+ // was thrown away. Proven by write -> physical replug -> read: writing
185
+ // [0, -35, -20, 0, 1, 0] brought the camera up at yaw -34 / pitch -20.
186
+ // See tiny2_specification.md section 6.1.
187
+ export const encodeGimBootPosSet = (seq, pose) => buildFrame({
188
+ seq,
189
+ cmd: CMD.GIM_BOOT_POS_SET,
190
+ receiver: RECEIVER,
191
+ // Slot 0 is 0: the boot pose is a single global setting, not one of the 3 slots.
192
+ // (u32le(0) is bit-identical to the float32 0.0 the device reports in slot 0.)
193
+ // The trailing float32 0.0 is REQUIRED — without it the write is discarded.
194
+ payload: concat(u32le(0), f32le(pose.pan), f32le(pose.tilt), f32le(pose.roll), f32le(pose.zoom), f32le(0)),
195
+ });
196
+ export const encodeGimBootPosReset = (seq) => buildFrame({ seq, cmd: CMD.GIM_BOOT_POS_RESET, receiver: RECEIVER, payload: Buffer.alloc(0) });
197
+ export const encodeGimBootPosTrigger = (seq) => buildFrame({ seq, cmd: CMD.GIM_BOOT_POS_TRIGGER, receiver: RECEIVER, payload: Buffer.alloc(0) });
198
+ //# sourceMappingURL=preset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preset.js","sourceRoot":"","sources":["../../src/codec/preset.ts"],"names":[],"mappings":"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"}
@@ -10,4 +10,8 @@ export interface ZoomUnits {
10
10
  export interface DeviceInfo {
11
11
  path: string;
12
12
  name: string;
13
+ locationId?: number;
14
+ serial?: string;
15
+ vid?: number;
16
+ pid?: number;
13
17
  }
@@ -0,0 +1,25 @@
1
+ import { DeviceManager } from "./manager.js";
2
+ import { HelperProcess } from "../transport/helper-process.js";
3
+ /**
4
+ * Build the factory DeviceManager spawns its helpers through, with every
5
+ * helper subscribed to the OS bus events before it is started.
6
+ *
7
+ * Subscribing HERE rather than at a call site is the point: there is no single
8
+ * long-lived helper. The scratch scanner is promoted into the registry when a
9
+ * camera binds and the next scan spawns another, so whichever process happens
10
+ * to be alive when the cable moves has to be the one that reports it. Without
11
+ * this the server learns about an unplug only by failing a call against a dead
12
+ * handle — `obsbot_devices` reports a phantom `bound` entry, serial and all,
13
+ * for a camera sitting on the desk, and the first call after a replug fails by
14
+ * design.
15
+ *
16
+ * `getMgr` is a thunk rather than the manager itself because the manager is
17
+ * constructed WITH this factory and so does not exist yet when the factory is
18
+ * built. It is resolved when an event fires, long after construction.
19
+ *
20
+ * `make` is injectable so this can be tested against a fake helper instead of
21
+ * spawning the real binary. The subscription is the whole feature and it is
22
+ * two lines; left inline in startServer() nothing covered it, and deleting it
23
+ * broke no test.
24
+ */
25
+ export declare function helperFactory(getMgr: () => DeviceManager, make?: () => HelperProcess): () => Promise<HelperProcess>;
@@ -0,0 +1,40 @@
1
+ import { HelperProcess } from "../transport/helper-process.js";
2
+ /**
3
+ * Build the factory DeviceManager spawns its helpers through, with every
4
+ * helper subscribed to the OS bus events before it is started.
5
+ *
6
+ * Subscribing HERE rather than at a call site is the point: there is no single
7
+ * long-lived helper. The scratch scanner is promoted into the registry when a
8
+ * camera binds and the next scan spawns another, so whichever process happens
9
+ * to be alive when the cable moves has to be the one that reports it. Without
10
+ * this the server learns about an unplug only by failing a call against a dead
11
+ * handle — `obsbot_devices` reports a phantom `bound` entry, serial and all,
12
+ * for a camera sitting on the desk, and the first call after a replug fails by
13
+ * design.
14
+ *
15
+ * `getMgr` is a thunk rather than the manager itself because the manager is
16
+ * constructed WITH this factory and so does not exist yet when the factory is
17
+ * built. It is resolved when an event fires, long after construction.
18
+ *
19
+ * `make` is injectable so this can be tested against a fake helper instead of
20
+ * spawning the real binary. The subscription is the whole feature and it is
21
+ * two lines; left inline in startServer() nothing covered it, and deleting it
22
+ * broke no test.
23
+ */
24
+ export function helperFactory(getMgr, make = () => new HelperProcess()) {
25
+ return async () => {
26
+ const helper = make();
27
+ // Before start(), not after: a camera plugged in during spawn would
28
+ // otherwise emit into nothing and the arrival would simply be lost.
29
+ //
30
+ // The handlers never reject (see DeviceManager), but they ARE async while
31
+ // these listeners are sync — swallow explicitly so a surprise rejection
32
+ // cannot become an unhandled one and kill the process from a stdout line
33
+ // handler, losing every in-flight request because a cable moved.
34
+ helper.onCameraArrived((e) => void getMgr().handleCameraArrived(e).catch(() => { }));
35
+ helper.onCameraDeparted((e) => void getMgr().handleCameraDeparted(e).catch(() => { }));
36
+ await helper.start();
37
+ return helper;
38
+ };
39
+ }
40
+ //# sourceMappingURL=helper-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helper-factory.js","sourceRoot":"","sources":["../../src/device/helper-factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,aAAa,CAC3B,MAA2B,EAC3B,OAA4B,GAAG,EAAE,CAAC,IAAI,aAAa,EAAE;IAErD,OAAO,KAAK,IAAI,EAAE;QAChB,MAAM,MAAM,GAAG,IAAI,EAAE,CAAC;QACtB,oEAAoE;QACpE,oEAAoE;QACpE,EAAE;QACF,0EAA0E;QAC1E,wEAAwE;QACxE,yEAAyE;QACzE,iEAAiE;QACjE,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC;QACpF,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC;QACtF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC"}