obsbot-mcp 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +195 -38
- package/dist/codec/commands.d.ts +42 -4
- package/dist/codec/commands.js +72 -10
- package/dist/codec/commands.js.map +1 -1
- package/dist/codec/frame.d.ts +1 -0
- package/dist/codec/frame.js +1 -1
- package/dist/codec/frame.js.map +1 -1
- package/dist/codec/preset.d.ts +40 -0
- package/dist/codec/preset.js +198 -0
- package/dist/codec/preset.js.map +1 -0
- package/dist/codec/types.d.ts +4 -0
- package/dist/device/helper-factory.d.ts +25 -0
- package/dist/device/helper-factory.js +40 -0
- package/dist/device/helper-factory.js.map +1 -0
- package/dist/device/manager.d.ts +326 -2
- package/dist/device/manager.js +720 -24
- package/dist/device/manager.js.map +1 -1
- package/dist/ipc/client.d.ts +19 -0
- package/dist/ipc/client.js +81 -0
- package/dist/ipc/client.js.map +1 -0
- package/dist/ipc/coordinator.d.ts +29 -0
- package/dist/ipc/coordinator.js +94 -0
- package/dist/ipc/coordinator.js.map +1 -0
- package/dist/ipc/owner.d.ts +21 -0
- package/dist/ipc/owner.js +59 -0
- package/dist/ipc/owner.js.map +1 -0
- package/dist/ipc/protocol.d.ts +21 -0
- package/dist/ipc/protocol.js +56 -0
- package/dist/ipc/protocol.js.map +1 -0
- package/dist/ipc/rendezvous.d.ts +26 -0
- package/dist/ipc/rendezvous.js +93 -0
- package/dist/ipc/rendezvous.js.map +1 -0
- package/dist/mcp/log-sink.d.ts +12 -0
- package/dist/mcp/log-sink.js +27 -0
- package/dist/mcp/log-sink.js.map +1 -0
- package/dist/mcp/ready.d.ts +16 -4
- package/dist/mcp/ready.js +11 -8
- package/dist/mcp/ready.js.map +1 -1
- package/dist/mcp/render.js +1 -1
- package/dist/mcp/render.js.map +1 -1
- package/dist/mcp/server.js +68 -18
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts +5 -3
- package/dist/mcp/tools.js +787 -186
- package/dist/mcp/tools.js.map +1 -1
- package/dist/transport/helper-process.d.ts +36 -0
- package/dist/transport/helper-process.js +190 -10
- package/dist/transport/helper-process.js.map +1 -1
- package/dist/transport/linux.d.ts +40 -0
- package/dist/transport/linux.js +92 -2
- package/dist/transport/linux.js.map +1 -1
- package/dist/transport/macos.d.ts +13 -0
- package/dist/transport/macos.js +59 -3
- package/dist/transport/macos.js.map +1 -1
- package/dist/transport/read-serial.d.ts +26 -0
- package/dist/transport/read-serial.js +87 -0
- package/dist/transport/read-serial.js.map +1 -0
- package/dist/transport/transport.d.ts +24 -0
- package/dist/transport/windows.d.ts +4 -0
- package/dist/transport/windows.js +31 -4
- package/dist/transport/windows.js.map +1 -1
- package/native/prebuilt/darwin-arm64/obsbot-helper +0 -0
- package/native/prebuilt/darwin-x64/obsbot-helper +0 -0
- package/native/prebuilt/linux-x64/obsbot-helper +0 -0
- package/native/prebuilt/win32-x64/obsbot-helper.exe +0 -0
- package/package.json +3 -2
- package/dist/device/session.d.ts +0 -22
- package/dist/device/session.js +0 -37
- package/dist/device/session.js.map +0 -1
package/dist/codec/frame.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frame.js","sourceRoot":"","sources":["../../src/codec/frame.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAItC,MAAM,UAAU,UAAU,CAAC,CAAY;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAY,2BAA2B;IACtE,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAChB,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,
|
|
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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"}
|
package/dist/codec/types.d.ts
CHANGED
|
@@ -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"}
|