@agentdeck/bridge 1.0.2 → 1.0.3
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/assets/fm-helper/agentdeck-fm-helper +0 -0
- package/dist/apme/collector.d.ts.map +1 -1
- package/dist/apme/collector.js +5 -2
- package/dist/apme/collector.js.map +1 -1
- package/dist/apme/runner.d.ts +11 -0
- package/dist/apme/runner.d.ts.map +1 -1
- package/dist/apme/runner.js +60 -4
- package/dist/apme/runner.js.map +1 -1
- package/dist/apme/settings.d.ts +10 -0
- package/dist/apme/settings.d.ts.map +1 -1
- package/dist/apme/settings.js +14 -0
- package/dist/apme/settings.js.map +1 -1
- package/dist/awaiting-overlay.d.ts +44 -10
- package/dist/awaiting-overlay.d.ts.map +1 -1
- package/dist/awaiting-overlay.js +96 -28
- package/dist/awaiting-overlay.js.map +1 -1
- package/dist/bridge-core.d.ts +10 -0
- package/dist/bridge-core.d.ts.map +1 -1
- package/dist/bridge-core.js +47 -12
- package/dist/bridge-core.js.map +1 -1
- package/dist/card-feed.d.ts +107 -0
- package/dist/card-feed.d.ts.map +1 -0
- package/dist/card-feed.js +277 -0
- package/dist/card-feed.js.map +1 -0
- package/dist/card-modules.d.ts +87 -0
- package/dist/card-modules.d.ts.map +1 -0
- package/dist/card-modules.js +179 -0
- package/dist/card-modules.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +93 -0
- package/dist/cli.js.map +1 -1
- package/dist/daemon-server.d.ts.map +1 -1
- package/dist/daemon-server.js +1008 -47
- package/dist/daemon-server.js.map +1 -1
- package/dist/daemon-ws-client.d.ts +2 -1
- package/dist/daemon-ws-client.d.ts.map +1 -1
- package/dist/daemon-ws-client.js +2 -1
- package/dist/daemon-ws-client.js.map +1 -1
- package/dist/daemon.js +1 -1
- package/dist/device-photo.d.ts +86 -0
- package/dist/device-photo.d.ts.map +1 -0
- package/dist/device-photo.js +200 -0
- package/dist/device-photo.js.map +1 -0
- package/dist/device-voice-reply.d.ts +124 -0
- package/dist/device-voice-reply.d.ts.map +1 -0
- package/dist/device-voice-reply.js +236 -0
- package/dist/device-voice-reply.js.map +1 -0
- package/dist/device-voice.d.ts +56 -0
- package/dist/device-voice.d.ts.map +1 -0
- package/dist/device-voice.js +134 -0
- package/dist/device-voice.js.map +1 -0
- package/dist/esp32-serial.d.ts +40 -0
- package/dist/esp32-serial.d.ts.map +1 -1
- package/dist/esp32-serial.js +109 -13
- package/dist/esp32-serial.js.map +1 -1
- package/dist/foundation-models-helper.d.ts +30 -0
- package/dist/foundation-models-helper.d.ts.map +1 -1
- package/dist/foundation-models-helper.js +45 -0
- package/dist/foundation-models-helper.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/mdns.d.ts +11 -0
- package/dist/mdns.d.ts.map +1 -1
- package/dist/mdns.js +13 -0
- package/dist/mdns.js.map +1 -1
- package/dist/observed-inject.d.ts +119 -0
- package/dist/observed-inject.d.ts.map +1 -0
- package/dist/observed-inject.js +405 -0
- package/dist/observed-inject.js.map +1 -0
- package/dist/passive-observer.d.ts +14 -0
- package/dist/passive-observer.d.ts.map +1 -1
- package/dist/passive-observer.js +35 -5
- package/dist/passive-observer.js.map +1 -1
- package/dist/peripheral-mapping.d.ts +65 -0
- package/dist/peripheral-mapping.d.ts.map +1 -0
- package/dist/peripheral-mapping.js +115 -0
- package/dist/peripheral-mapping.js.map +1 -0
- package/dist/session-aggregator.d.ts +4 -1
- package/dist/session-aggregator.d.ts.map +1 -1
- package/dist/session-aggregator.js +5 -5
- package/dist/session-aggregator.js.map +1 -1
- package/dist/session-id-resolve.d.ts +17 -0
- package/dist/session-id-resolve.d.ts.map +1 -0
- package/dist/session-id-resolve.js +35 -0
- package/dist/session-id-resolve.js.map +1 -0
- package/dist/session-transcript-timeline.d.ts +7 -0
- package/dist/session-transcript-timeline.d.ts.map +1 -1
- package/dist/session-transcript-timeline.js +16 -6
- package/dist/session-transcript-timeline.js.map +1 -1
- package/dist/timeline-store.js +2 -2
- package/dist/timeline-store.js.map +1 -1
- package/dist/usage-event.d.ts.map +1 -1
- package/dist/usage-event.js +14 -7
- package/dist/usage-event.js.map +1 -1
- package/dist/voice.d.ts +13 -10
- package/dist/voice.d.ts.map +1 -1
- package/dist/voice.js +26 -272
- package/dist/voice.js.map +1 -1
- package/dist/ws-server.d.ts +14 -0
- package/dist/ws-server.d.ts.map +1 -1
- package/dist/ws-server.js +66 -1
- package/dist/ws-server.js.map +1 -1
- package/fm-helper/AgentDeckFMHelper.swift +303 -0
- package/package.json +3 -3
- package/dist/d200h/hid-protocol.d.ts +0 -62
- package/dist/d200h/hid-protocol.d.ts.map +0 -1
- package/dist/d200h/hid-protocol.js +0 -124
- package/dist/d200h/hid-protocol.js.map +0 -1
- package/dist/d200h/image-renderer.d.ts +0 -24
- package/dist/d200h/image-renderer.d.ts.map +0 -1
- package/dist/d200h/image-renderer.js +0 -321
- package/dist/d200h/image-renderer.js.map +0 -1
- package/dist/hook-migration.d.ts +0 -2
- package/dist/hook-migration.d.ts.map +0 -1
- package/dist/hook-migration.js +0 -104
- package/dist/hook-migration.js.map +0 -1
- package/dist/modules/d200h-module.d.ts +0 -65
- package/dist/modules/d200h-module.d.ts.map +0 -1
- package/dist/modules/d200h-module.js +0 -495
- package/dist/modules/d200h-module.js.map +0 -1
- package/dist/modules/d200h-renderer.d.ts +0 -7
- package/dist/modules/d200h-renderer.d.ts.map +0 -1
- package/dist/modules/d200h-renderer.js +0 -321
- package/dist/modules/d200h-renderer.js.map +0 -1
- package/dist/modules/trmnl-module.d.ts +0 -38
- package/dist/modules/trmnl-module.d.ts.map +0 -1
- package/dist/modules/trmnl-module.js +0 -111
- package/dist/modules/trmnl-module.js.map +0 -1
- package/dist/trmnl/byos-server.d.ts +0 -39
- package/dist/trmnl/byos-server.d.ts.map +0 -1
- package/dist/trmnl/byos-server.js +0 -301
- package/dist/trmnl/byos-server.js.map +0 -1
- package/dist/trmnl/frame-cache.d.ts +0 -37
- package/dist/trmnl/frame-cache.d.ts.map +0 -1
- package/dist/trmnl/frame-cache.js +0 -157
- package/dist/trmnl/frame-cache.js.map +0 -1
- package/dist/trmnl/image-renderer.d.ts +0 -27
- package/dist/trmnl/image-renderer.d.ts.map +0 -1
- package/dist/trmnl/image-renderer.js +0 -235
- package/dist/trmnl/image-renderer.js.map +0 -1
- package/dist/trmnl/trmnl-settings.d.ts +0 -96
- package/dist/trmnl/trmnl-settings.d.ts.map +0 -1
- package/dist/trmnl/trmnl-settings.js +0 -177
- package/dist/trmnl/trmnl-settings.js.map +0 -1
- package/dist/trmnl/trmnl-telemetry.d.ts +0 -47
- package/dist/trmnl/trmnl-telemetry.d.ts.map +0 -1
- package/dist/trmnl/trmnl-telemetry.js +0 -56
- package/dist/trmnl/trmnl-telemetry.js.map +0 -1
- package/dist/whisper-server-manager.d.ts +0 -19
- package/dist/whisper-server-manager.d.ts.map +0 -1
- package/dist/whisper-server-manager.js +0 -171
- package/dist/whisper-server-manager.js.map +0 -1
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spoken replies back to the board that asked the question.
|
|
3
|
+
*
|
|
4
|
+
* The mic half of the voice round trip (device → daemon → session) ends with
|
|
5
|
+
* text arriving in a terminal. This is the other half: when the session that
|
|
6
|
+
* was dictated to finishes its turn, the reply is synthesized on the host and
|
|
7
|
+
* streamed to that board's own amplifier. The host's speakers are the wrong
|
|
8
|
+
* output here by construction — the reason to hold a battery-powered board is
|
|
9
|
+
* that you walked away from the desk.
|
|
10
|
+
*
|
|
11
|
+
* Shape of the exchange (WS only; the serial transport has no binary framing):
|
|
12
|
+
* daemon → board {"type":"audio_play_begin","sampleRate":16000,"text":"…"}
|
|
13
|
+
* daemon → board binary frames of PCM16LE
|
|
14
|
+
* daemon → board {"type":"audio_play_end"}
|
|
15
|
+
*
|
|
16
|
+
* The board cannot buffer a whole reply, so frames are paced at roughly
|
|
17
|
+
* playback speed. Sending as fast as the socket allows would overrun a ring
|
|
18
|
+
* buffer measured in seconds and cost the tail of every long answer.
|
|
19
|
+
*/
|
|
20
|
+
import { rawSessionId } from '@agentdeck/shared';
|
|
21
|
+
/** PCM16 mono frame size. 1 KB = 32 ms at 16 kHz — small enough that a dropped
|
|
22
|
+
* frame is inaudible, large enough that framing overhead stays negligible. */
|
|
23
|
+
export const PCM_FRAME_BYTES = 1024;
|
|
24
|
+
/** A spoken reply past this is a monologue, not an answer. Truncated at a
|
|
25
|
+
* sentence boundary when one is near the cap. */
|
|
26
|
+
export const MAX_SPOKEN_CHARS = 700;
|
|
27
|
+
/**
|
|
28
|
+
* How long an arming survives *without the session making progress*. Measured
|
|
29
|
+
* from the last sign of work, not from the dictation: a question that kicks off
|
|
30
|
+
* half an hour of tool calls still deserves its answer read out, while a session
|
|
31
|
+
* that went quiet has moved on and speaking then is worse than silence.
|
|
32
|
+
*/
|
|
33
|
+
export const REPLY_ARM_TTL_MS = 10 * 60 * 1000;
|
|
34
|
+
/**
|
|
35
|
+
* Strip the parts of an assistant reply that make no sense read aloud (code
|
|
36
|
+
* fences, markdown scaffolding, bare URLs) and cap the length. Returns an empty
|
|
37
|
+
* string when nothing speakable is left — a reply that was only a diff should
|
|
38
|
+
* play nothing rather than spell out punctuation.
|
|
39
|
+
*/
|
|
40
|
+
export function speakableReply(raw, maxChars = MAX_SPOKEN_CHARS) {
|
|
41
|
+
if (!raw)
|
|
42
|
+
return '';
|
|
43
|
+
let text = raw
|
|
44
|
+
// Fenced blocks read as noise; say so once instead of reciting them.
|
|
45
|
+
.replace(/```[\s\S]*?```/g, ' (code) ')
|
|
46
|
+
.replace(/`([^`]+)`/g, '$1')
|
|
47
|
+
.replace(/!?\[([^\]]*)\]\([^)]*\)/g, '$1')
|
|
48
|
+
.replace(/https?:\/\/\S+/g, ' link ')
|
|
49
|
+
.replace(/^\s*(?:#{1,6}|>|[*\-+])\s+/gm, '')
|
|
50
|
+
.replace(/\*\*|__|~~/g, '')
|
|
51
|
+
.replace(/[ \t]+/g, ' ')
|
|
52
|
+
.replace(/\n{2,}/g, '\n')
|
|
53
|
+
.trim();
|
|
54
|
+
if (!text)
|
|
55
|
+
return '';
|
|
56
|
+
if (text.length > maxChars) {
|
|
57
|
+
const head = text.slice(0, maxChars);
|
|
58
|
+
// Prefer ending on a sentence so the cut doesn't sound like a dropout.
|
|
59
|
+
const lastStop = Math.max(head.lastIndexOf('. '), head.lastIndexOf('。'), head.lastIndexOf('! '), head.lastIndexOf('? '), head.lastIndexOf('다.'));
|
|
60
|
+
text = lastStop > maxChars * 0.5 ? head.slice(0, lastStop + 1) : head;
|
|
61
|
+
}
|
|
62
|
+
return text.trim();
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Extract PCM samples from a canonical RIFF/WAVE file by walking the chunk
|
|
66
|
+
* list. Fixed-offset slicing at byte 44 is wrong for any file carrying a LIST
|
|
67
|
+
* or fact chunk, and would emit metadata as audio.
|
|
68
|
+
*/
|
|
69
|
+
export function pcmFromWav(wav) {
|
|
70
|
+
if (wav.length < 44)
|
|
71
|
+
return null;
|
|
72
|
+
if (wav.toString('ascii', 0, 4) !== 'RIFF' || wav.toString('ascii', 8, 12) !== 'WAVE')
|
|
73
|
+
return null;
|
|
74
|
+
let offset = 12;
|
|
75
|
+
let sampleRate = 0;
|
|
76
|
+
let pcm = null;
|
|
77
|
+
while (offset + 8 <= wav.length) {
|
|
78
|
+
const id = wav.toString('ascii', offset, offset + 4);
|
|
79
|
+
const size = wav.readUInt32LE(offset + 4);
|
|
80
|
+
const body = offset + 8;
|
|
81
|
+
if (id === 'fmt ' && body + 16 <= wav.length) {
|
|
82
|
+
sampleRate = wav.readUInt32LE(body + 4);
|
|
83
|
+
}
|
|
84
|
+
else if (id === 'data') {
|
|
85
|
+
const end = Math.min(body + size, wav.length);
|
|
86
|
+
pcm = wav.subarray(body, end);
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
offset = body + size + (size % 2); // chunks are word-aligned
|
|
90
|
+
}
|
|
91
|
+
if (!pcm || pcm.length === 0 || sampleRate === 0)
|
|
92
|
+
return null;
|
|
93
|
+
return { pcm, sampleRate };
|
|
94
|
+
}
|
|
95
|
+
/** Split PCM into fixed-size frames; the last one may be short. */
|
|
96
|
+
export function pcmFrames(pcm, frameBytes = PCM_FRAME_BYTES) {
|
|
97
|
+
const frames = [];
|
|
98
|
+
for (let i = 0; i < pcm.length; i += frameBytes) {
|
|
99
|
+
frames.push(pcm.subarray(i, Math.min(i + frameBytes, pcm.length)));
|
|
100
|
+
}
|
|
101
|
+
return frames;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Tracks which board is waiting to hear the answer to which session, and
|
|
105
|
+
* streams the audio when it arrives.
|
|
106
|
+
*/
|
|
107
|
+
export class DeviceVoiceReplyRouter {
|
|
108
|
+
now;
|
|
109
|
+
sleep;
|
|
110
|
+
resolveLive;
|
|
111
|
+
armed = new Map();
|
|
112
|
+
streaming = new Set();
|
|
113
|
+
constructor(now = () => Date.now(), sleep = (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
114
|
+
/** Current live transport for a board, when its original one has gone. */
|
|
115
|
+
resolveLive = () => null) {
|
|
116
|
+
this.now = now;
|
|
117
|
+
this.sleep = sleep;
|
|
118
|
+
this.resolveLive = resolveLive;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Remember that this board dictated into this session and should hear the
|
|
122
|
+
* reply. Only boards advertising an amplifier are armed — streaming audio at
|
|
123
|
+
* a board that cannot play it wastes the link and, worse, makes the feature
|
|
124
|
+
* look broken on the boards that can.
|
|
125
|
+
*/
|
|
126
|
+
arm(sink, sessionId) {
|
|
127
|
+
if (!sessionId)
|
|
128
|
+
return false;
|
|
129
|
+
if (!sink.capabilities().includes('audio_out'))
|
|
130
|
+
return false;
|
|
131
|
+
// Store the bare uuid: devices speak the prefixed `observed:<agent>:<uuid>`
|
|
132
|
+
// form while timeline rows are keyed by the uuid alone, so keying on what
|
|
133
|
+
// the device sent means the completion never matches what was armed.
|
|
134
|
+
this.armed.set(sink, { sessionId: rawSessionId(sessionId), armedAt: this.now() });
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
disarm(sink) {
|
|
138
|
+
this.armed.delete(sink);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Mark these sessions as still working, so a long turn does not age out
|
|
142
|
+
* mid-flight. Called with whatever the daemon currently considers active.
|
|
143
|
+
*/
|
|
144
|
+
refresh(activeSessionIds) {
|
|
145
|
+
if (activeSessionIds.length === 0)
|
|
146
|
+
return;
|
|
147
|
+
const active = new Set(activeSessionIds.map((id) => rawSessionId(id)));
|
|
148
|
+
const now = this.now();
|
|
149
|
+
for (const entry of this.armed.values()) {
|
|
150
|
+
if (active.has(entry.sessionId))
|
|
151
|
+
entry.armedAt = now;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Drop armings that have gone stale. Deliberately does NOT evict on a closed
|
|
156
|
+
* transport: a board's link can blink between the dictation and the end of the
|
|
157
|
+
* turn — a serial connection gets recycled, a WebSocket reconnects — and
|
|
158
|
+
* dropping the arming then loses the reply permanently for a device that is
|
|
159
|
+
* about to be reachable again. Liveness is checked where it matters, at the
|
|
160
|
+
* moment of streaming.
|
|
161
|
+
*/
|
|
162
|
+
sweep() {
|
|
163
|
+
const cutoff = this.now() - REPLY_ARM_TTL_MS;
|
|
164
|
+
for (const [sink, entry] of this.armed) {
|
|
165
|
+
if (entry.armedAt < cutoff)
|
|
166
|
+
this.armed.delete(sink);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/** How many armings are outstanding — for diagnostics only. */
|
|
170
|
+
armedCount() {
|
|
171
|
+
return this.armed.size;
|
|
172
|
+
}
|
|
173
|
+
/** Transport labels of everything currently armed, for diagnostics. */
|
|
174
|
+
describeArmed() {
|
|
175
|
+
return [...this.armed].map(([sink, e]) => `${sink.describe()}->${e.sessionId.slice(0, 12)}`).join(', ') || '(none)';
|
|
176
|
+
}
|
|
177
|
+
/** Boards currently waiting on this session's reply. */
|
|
178
|
+
targetsFor(sessionId) {
|
|
179
|
+
this.sweep();
|
|
180
|
+
const raw = rawSessionId(sessionId);
|
|
181
|
+
const out = [];
|
|
182
|
+
for (const [sink, entry] of this.armed) {
|
|
183
|
+
if (entry.sessionId === raw)
|
|
184
|
+
out.push(sink);
|
|
185
|
+
}
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Stream one already-synthesized WAV to a board. Consumes the arming, so a
|
|
190
|
+
* single dictation yields a single spoken reply rather than narrating every
|
|
191
|
+
* subsequent turn of that session.
|
|
192
|
+
*/
|
|
193
|
+
async stream(armedSink, wav, spokenText) {
|
|
194
|
+
this.armed.delete(armedSink);
|
|
195
|
+
// Follow the board, not the socket: a USB-attached board parks its WiFi and
|
|
196
|
+
// closes the WebSocket the dictation came in on, so by now the same device is
|
|
197
|
+
// very likely reachable over serial instead.
|
|
198
|
+
const sink = armedSink.isOpen()
|
|
199
|
+
? armedSink
|
|
200
|
+
: this.resolveLive(armedSink.deviceKey()) ?? armedSink;
|
|
201
|
+
if (this.streaming.has(sink))
|
|
202
|
+
return false; // one utterance at a time per board
|
|
203
|
+
const parsed = pcmFromWav(wav);
|
|
204
|
+
if (!parsed || !sink.isOpen())
|
|
205
|
+
return false;
|
|
206
|
+
const frames = pcmFrames(parsed.pcm);
|
|
207
|
+
const frameMs = (PCM_FRAME_BYTES / 2) / parsed.sampleRate * 1000;
|
|
208
|
+
this.streaming.add(sink);
|
|
209
|
+
try {
|
|
210
|
+
sink.send(JSON.stringify({
|
|
211
|
+
type: 'audio_play_begin',
|
|
212
|
+
sampleRate: parsed.sampleRate,
|
|
213
|
+
durationMs: Math.round((parsed.pcm.length / 2) / parsed.sampleRate * 1000),
|
|
214
|
+
text: spokenText.slice(0, 160),
|
|
215
|
+
}));
|
|
216
|
+
// Prime the board's ring buffer, then pace. Without the burst the first
|
|
217
|
+
// syllable stutters while the playback task waits on the second frame.
|
|
218
|
+
const burst = Math.min(frames.length, 8);
|
|
219
|
+
for (let i = 0; i < frames.length; i++) {
|
|
220
|
+
if (!sink.isOpen())
|
|
221
|
+
return false;
|
|
222
|
+
sink.sendBinary(frames[i]);
|
|
223
|
+
if (i >= burst)
|
|
224
|
+
await this.sleep(Math.max(1, Math.round(frameMs * 0.9)));
|
|
225
|
+
}
|
|
226
|
+
if (!sink.isOpen())
|
|
227
|
+
return false;
|
|
228
|
+
sink.send(JSON.stringify({ type: 'audio_play_end' }));
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
finally {
|
|
232
|
+
this.streaming.delete(sink);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=device-voice-reply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-voice-reply.js","sourceRoot":"","sources":["../src/device-voice-reply.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device-sourced voice: a board streams PCM16 while its push-to-talk key is
|
|
3
|
+
* held, and the daemon turns that into a prompt for the session the board was
|
|
4
|
+
* pointing at.
|
|
5
|
+
*
|
|
6
|
+
* Wire shape (per WS connection):
|
|
7
|
+
* {"type":"voice_begin","sampleRate":16000,"format":"pcm16","sessionId":…}
|
|
8
|
+
* <binary frames: raw little-endian PCM16 mono>
|
|
9
|
+
* {"type":"voice_end","durationMs":…,"cancel":false}
|
|
10
|
+
*
|
|
11
|
+
* Binary frames carry no envelope, so they are attributed to whichever socket
|
|
12
|
+
* has an open utterance — hence the per-connection state here.
|
|
13
|
+
*
|
|
14
|
+
* The board contributes a microphone and a button; transcription is Apple's
|
|
15
|
+
* on-device recognizer through the bundled Swift helper (same engine as the
|
|
16
|
+
* Swift daemon — see docs/voice-setup.md).
|
|
17
|
+
*/
|
|
18
|
+
export interface VoiceUtterance {
|
|
19
|
+
sampleRate: number;
|
|
20
|
+
sessionId: string;
|
|
21
|
+
board: string;
|
|
22
|
+
chunks: Buffer[];
|
|
23
|
+
bytes: number;
|
|
24
|
+
startedAt: number;
|
|
25
|
+
truncated: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface VoiceCaptureResult {
|
|
28
|
+
sessionId: string;
|
|
29
|
+
board: string;
|
|
30
|
+
wavPath: string;
|
|
31
|
+
durationMs: number;
|
|
32
|
+
cleanup: () => void;
|
|
33
|
+
}
|
|
34
|
+
/** Build a canonical 16-bit PCM WAV around raw little-endian samples. */
|
|
35
|
+
export declare function buildWav(pcm: Buffer, sampleRate: number): Buffer;
|
|
36
|
+
/**
|
|
37
|
+
* Per-connection utterance assembly. One instance per daemon; connections are
|
|
38
|
+
* keyed by whatever opaque handle the server uses for a socket.
|
|
39
|
+
*/
|
|
40
|
+
export declare class DeviceVoiceCollector {
|
|
41
|
+
private open;
|
|
42
|
+
begin(conn: unknown, msg: Record<string, unknown>): void;
|
|
43
|
+
/** Returns false when no utterance is open — the caller should ignore the frame. */
|
|
44
|
+
append(conn: unknown, data: Buffer): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Finish the utterance. Returns null when it was cancelled, empty, or when
|
|
47
|
+
* nothing was open. The caller owns `cleanup()` once transcription is done.
|
|
48
|
+
*/
|
|
49
|
+
end(conn: unknown, msg: Record<string, unknown>): VoiceCaptureResult | null;
|
|
50
|
+
/** Drop an utterance whose socket died mid-stream. */
|
|
51
|
+
abandon(conn: unknown): void;
|
|
52
|
+
/** Reap utterances whose voice_end never arrived. */
|
|
53
|
+
sweep(now?: number): void;
|
|
54
|
+
get openCount(): number;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=device-voice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-voice.d.ts","sourceRoot":"","sources":["../src/device-voice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAYH,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,yEAAyE;AACzE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAiBhE;AAED;;;GAGG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,IAAI,CAAsC;IAElD,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAexD,oFAAoF;IACpF,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IAc5C;;;OAGG;IACH,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,kBAAkB,GAAG,IAAI;IA6B3E,sDAAsD;IACtD,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAI5B,qDAAqD;IACrD,KAAK,CAAC,GAAG,SAAa,GAAG,IAAI;IAS7B,IAAI,SAAS,IAAI,MAAM,CAEtB;CACF"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device-sourced voice: a board streams PCM16 while its push-to-talk key is
|
|
3
|
+
* held, and the daemon turns that into a prompt for the session the board was
|
|
4
|
+
* pointing at.
|
|
5
|
+
*
|
|
6
|
+
* Wire shape (per WS connection):
|
|
7
|
+
* {"type":"voice_begin","sampleRate":16000,"format":"pcm16","sessionId":…}
|
|
8
|
+
* <binary frames: raw little-endian PCM16 mono>
|
|
9
|
+
* {"type":"voice_end","durationMs":…,"cancel":false}
|
|
10
|
+
*
|
|
11
|
+
* Binary frames carry no envelope, so they are attributed to whichever socket
|
|
12
|
+
* has an open utterance — hence the per-connection state here.
|
|
13
|
+
*
|
|
14
|
+
* The board contributes a microphone and a button; transcription is Apple's
|
|
15
|
+
* on-device recognizer through the bundled Swift helper (same engine as the
|
|
16
|
+
* Swift daemon — see docs/voice-setup.md).
|
|
17
|
+
*/
|
|
18
|
+
import { mkdtempSync, writeFileSync, rmSync } from 'fs';
|
|
19
|
+
import { tmpdir } from 'os';
|
|
20
|
+
import { join } from 'path';
|
|
21
|
+
import { debug } from './logger.js';
|
|
22
|
+
/** Refuse to buffer more than ~30s of 16 kHz mono PCM16 from one utterance. */
|
|
23
|
+
const MAX_UTTERANCE_BYTES = 16000 * 2 * 30;
|
|
24
|
+
/** An utterance whose voice_end never arrives (board reboot, WiFi drop). */
|
|
25
|
+
const UTTERANCE_TTL_MS = 60_000;
|
|
26
|
+
/** Build a canonical 16-bit PCM WAV around raw little-endian samples. */
|
|
27
|
+
export function buildWav(pcm, sampleRate) {
|
|
28
|
+
const header = Buffer.alloc(44);
|
|
29
|
+
const byteRate = sampleRate * 2;
|
|
30
|
+
header.write('RIFF', 0);
|
|
31
|
+
header.writeUInt32LE(36 + pcm.length, 4);
|
|
32
|
+
header.write('WAVE', 8);
|
|
33
|
+
header.write('fmt ', 12);
|
|
34
|
+
header.writeUInt32LE(16, 16); // PCM chunk size
|
|
35
|
+
header.writeUInt16LE(1, 20); // format = PCM
|
|
36
|
+
header.writeUInt16LE(1, 22); // channels = mono
|
|
37
|
+
header.writeUInt32LE(sampleRate, 24);
|
|
38
|
+
header.writeUInt32LE(byteRate, 28);
|
|
39
|
+
header.writeUInt16LE(2, 32); // block align
|
|
40
|
+
header.writeUInt16LE(16, 34); // bits per sample
|
|
41
|
+
header.write('data', 36);
|
|
42
|
+
header.writeUInt32LE(pcm.length, 40);
|
|
43
|
+
return Buffer.concat([header, pcm]);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Per-connection utterance assembly. One instance per daemon; connections are
|
|
47
|
+
* keyed by whatever opaque handle the server uses for a socket.
|
|
48
|
+
*/
|
|
49
|
+
export class DeviceVoiceCollector {
|
|
50
|
+
open = new Map();
|
|
51
|
+
begin(conn, msg) {
|
|
52
|
+
const sampleRate = typeof msg.sampleRate === 'number' && msg.sampleRate > 0
|
|
53
|
+
? msg.sampleRate : 16000;
|
|
54
|
+
this.open.set(conn, {
|
|
55
|
+
sampleRate,
|
|
56
|
+
sessionId: typeof msg.sessionId === 'string' ? msg.sessionId : '',
|
|
57
|
+
board: typeof msg.board === 'string' ? msg.board : 'esp32',
|
|
58
|
+
chunks: [],
|
|
59
|
+
bytes: 0,
|
|
60
|
+
startedAt: Date.now(),
|
|
61
|
+
truncated: false,
|
|
62
|
+
});
|
|
63
|
+
debug('voice', `utterance begin (${sampleRate} Hz, session ${String(msg.sessionId ?? '').slice(0, 20)})`);
|
|
64
|
+
}
|
|
65
|
+
/** Returns false when no utterance is open — the caller should ignore the frame. */
|
|
66
|
+
append(conn, data) {
|
|
67
|
+
const u = this.open.get(conn);
|
|
68
|
+
if (!u)
|
|
69
|
+
return false;
|
|
70
|
+
if (u.bytes + data.length > MAX_UTTERANCE_BYTES) {
|
|
71
|
+
// Keep the head rather than the tail: the command is at the start, and
|
|
72
|
+
// dropping silently would look like a mis-transcription.
|
|
73
|
+
u.truncated = true;
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
u.chunks.push(Buffer.from(data));
|
|
77
|
+
u.bytes += data.length;
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Finish the utterance. Returns null when it was cancelled, empty, or when
|
|
82
|
+
* nothing was open. The caller owns `cleanup()` once transcription is done.
|
|
83
|
+
*/
|
|
84
|
+
end(conn, msg) {
|
|
85
|
+
const u = this.open.get(conn);
|
|
86
|
+
this.open.delete(conn);
|
|
87
|
+
if (!u)
|
|
88
|
+
return null;
|
|
89
|
+
if (msg.cancel === true) {
|
|
90
|
+
debug('voice', 'utterance cancelled by device');
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
if (u.bytes === 0) {
|
|
94
|
+
debug('voice', 'utterance empty — nothing captured');
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const pcm = Buffer.concat(u.chunks);
|
|
98
|
+
const dir = mkdtempSync(join(tmpdir(), 'agentdeck-voice-'));
|
|
99
|
+
const wavPath = join(dir, 'utterance.wav');
|
|
100
|
+
writeFileSync(wavPath, buildWav(pcm, u.sampleRate));
|
|
101
|
+
const durationMs = typeof msg.durationMs === 'number'
|
|
102
|
+
? msg.durationMs
|
|
103
|
+
: Math.round((u.bytes / 2 / u.sampleRate) * 1000);
|
|
104
|
+
debug('voice', `utterance end: ${u.bytes} bytes, ${durationMs}ms${u.truncated ? ' (truncated)' : ''}`);
|
|
105
|
+
return {
|
|
106
|
+
sessionId: u.sessionId,
|
|
107
|
+
board: u.board,
|
|
108
|
+
wavPath,
|
|
109
|
+
durationMs,
|
|
110
|
+
cleanup: () => { try {
|
|
111
|
+
rmSync(dir, { recursive: true, force: true });
|
|
112
|
+
}
|
|
113
|
+
catch { /* best effort */ } },
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/** Drop an utterance whose socket died mid-stream. */
|
|
117
|
+
abandon(conn) {
|
|
118
|
+
if (this.open.delete(conn))
|
|
119
|
+
debug('voice', 'utterance abandoned (socket closed)');
|
|
120
|
+
}
|
|
121
|
+
/** Reap utterances whose voice_end never arrived. */
|
|
122
|
+
sweep(now = Date.now()) {
|
|
123
|
+
for (const [conn, u] of this.open) {
|
|
124
|
+
if (now - u.startedAt > UTTERANCE_TTL_MS) {
|
|
125
|
+
this.open.delete(conn);
|
|
126
|
+
debug('voice', 'utterance expired without voice_end');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
get openCount() {
|
|
131
|
+
return this.open.size;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=device-voice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-voice.js","sourceRoot":"","sources":["../src/device-voice.ts"],"names":[],"mappings":"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"}
|
package/dist/esp32-serial.d.ts
CHANGED
|
@@ -61,6 +61,21 @@ export interface SerialConnection {
|
|
|
61
61
|
sessionCount?: number;
|
|
62
62
|
usageFiveH?: number;
|
|
63
63
|
processingCount?: number;
|
|
64
|
+
/** Peripheral telemetry/diag (capability-advertising boards). */
|
|
65
|
+
capabilities?: string[];
|
|
66
|
+
batteryPercent?: number;
|
|
67
|
+
batteryVoltageMv?: number;
|
|
68
|
+
batteryCharging?: boolean;
|
|
69
|
+
usbPowered?: boolean;
|
|
70
|
+
batteryDiag?: number;
|
|
71
|
+
touchReady?: boolean;
|
|
72
|
+
touchDownSamples?: number;
|
|
73
|
+
touchGestures?: number;
|
|
74
|
+
touchLastX?: number;
|
|
75
|
+
touchLastY?: number;
|
|
76
|
+
touchMaxX?: number;
|
|
77
|
+
touchMaxY?: number;
|
|
78
|
+
alsReady?: boolean;
|
|
64
79
|
} | null;
|
|
65
80
|
/** True once a device_info arrived on THIS connection (vs. cache-seeded).
|
|
66
81
|
* Identify retries key off this — a cache-seeded deviceInfo must not stop
|
|
@@ -116,6 +131,31 @@ export declare function prepareForSerial(event: BridgeEvent, _conn?: Pick<Serial
|
|
|
116
131
|
export declare function isRetryableSerialIoError(err: unknown): boolean;
|
|
117
132
|
/** @internal Exported for testing only */
|
|
118
133
|
export declare function capturePanicLine(conn: SerialConnection, line: string): boolean;
|
|
134
|
+
/** While true for a port, the daemon holds its outbound serial writes —
|
|
135
|
+
* used to keep the line half-duplex during a board→daemon photo upload. */
|
|
136
|
+
export declare function setSerialQuiesceCheck(check: (port: string) => boolean): void;
|
|
137
|
+
export declare function setSerialCommandSink(sink: (cmd: Record<string, unknown>) => void): void;
|
|
138
|
+
/**
|
|
139
|
+
* Voice frames (voice_begin / audio_chunk / voice_end) from a serial-attached
|
|
140
|
+
* board. Separate from the command sink because they carry a port identity: an
|
|
141
|
+
* utterance has to be assembled per board, and the reply has to go back to the
|
|
142
|
+
* same one.
|
|
143
|
+
*/
|
|
144
|
+
export declare function setSerialVoiceSink(sink: (port: string, msg: Record<string, unknown>) => void): void;
|
|
145
|
+
/** Write one JSON line to a specific board. Bypasses FORWARDED_EVENTS: audio is
|
|
146
|
+
* addressed to one device, not broadcast state. */
|
|
147
|
+
export declare function sendSerialJson(port: string, msg: Record<string, unknown> | string): boolean;
|
|
148
|
+
/** True while this port has an open serial connection — the reply sink uses it
|
|
149
|
+
* as its liveness signal, the way the WS sink uses readyState. */
|
|
150
|
+
export declare function serialPortConnected(port: string): boolean;
|
|
151
|
+
/** Board wire string identified on this port, if any. */
|
|
152
|
+
export declare function serialPortBoard(port: string): string | undefined;
|
|
153
|
+
/** Port of a live serial connection for this board string, if one is attached. */
|
|
154
|
+
export declare function liveSerialPortForBoard(board: string): string | undefined;
|
|
155
|
+
/** Board strings of every live serial connection — diagnostics for reply routing. */
|
|
156
|
+
export declare function serialBoardsAttached(): string[];
|
|
157
|
+
/** Capabilities the board on this port advertised (empty when unidentified). */
|
|
158
|
+
export declare function serialPortCapabilities(port: string): string[];
|
|
119
159
|
/** @internal Exported for testing only */
|
|
120
160
|
export declare function handleSerialLine(conn: SerialConnection, line: string): void;
|
|
121
161
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"esp32-serial.d.ts","sourceRoot":"","sources":["../src/esp32-serial.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EASL,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,IAAI,CAAC;AAGZ,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAI3F,0CAA0C;AAC1C,eAAO,MAAM,mBAAmB,UAM/B,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,gBAAgB,UAG5B,CAAC;AA0CF;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,MAAM,CAM1F;AAUD,0CAA0C;AAC1C,eAAO,MAAM,qBAAqB,QAAwC,CAAC;AAkB3E,0CAA0C;AAC1C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE;QACV,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B;;8EAEsE;QACtE,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,eAAe,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"esp32-serial.d.ts","sourceRoot":"","sources":["../src/esp32-serial.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EASL,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,IAAI,CAAC;AAGZ,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAI3F,0CAA0C;AAC1C,eAAO,MAAM,mBAAmB,UAM/B,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,gBAAgB,UAG5B,CAAC;AA0CF;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,MAAM,CAM1F;AAUD,0CAA0C;AAC1C,eAAO,MAAM,qBAAqB,QAAwC,CAAC;AAkB3E,0CAA0C;AAC1C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE;QACV,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B;;8EAEsE;QACtE,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,iEAAiE;QACjE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,GAAG,IAAI,CAAC;IACT;;;+CAG2C;IAC3C,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,uBAAuB,EAAE,MAAM,CAAC;IAChC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,IAAI,CAAC;IACjD;;;8CAG0C;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAkED;;;;GAIG;AACH,0CAA0C;AAI1C,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC;;;;;;;;;;;GAWG;AACH;;;0EAG0E;AAC1E,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD;;yCAEyC;AACzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,CAU3D;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,CAoCzE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,YAAY,CAAC,GAAG,WAAW,CAgJ9G;AA4LD,0CAA0C;AAC1C,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAG9D;AAkBD,0CAA0C;AAC1C,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAgB9E;AAOD;4EAC4E;AAC5E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,CAE5E;AACD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,IAAI,CAEvF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GACzD,IAAI,CAEN;AAED;oDACoD;AACpD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,OAAO,CAK3F;AAED;mEACmE;AACnE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,yDAAyD;AACzD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIhE;AAED,kFAAkF;AAClF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGxE;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAI/C;AAED,gFAAgF;AAChF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAI7D;AAED,0CAA0C;AAC1C,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CA4F3E;AAoMD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,CAE9E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,CAE9E;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,CAErF;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,CAErF;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,WAAW,EAAE,GAAG,IAAI,CAEhF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,kBAAkB,KAAK,IAAI,GAAG,IAAI,CAE7F;AA8FD;;;;;GAKG;AACH,0CAA0C;AAC1C,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAErE;AAED;;;;;;;;GAQG;AACH,0CAA0C;AAC1C,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,YAAY,GAAG,YAAY,GAAG,aAAa,CAAC,EAClF,GAAG,SAAa,GACf,OAAO,CAKT;AAED;;;;;;;;GAQG;AACH,0CAA0C;AAC1C,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,YAAY,GAAG,YAAY,CAAC,EAClE,GAAG,SAAa,GACf,OAAO,CAKT;AAED;;;;;;;;GAQG;AACH,0CAA0C;AAC1C,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,WAAW,GAAG,iBAAiB,GAAG,wBAAwB,GAAG,yBAAyB,CAAC,EACpH,GAAG,SAAa,GACf,OAAO,CAKT;AAQD,mFAAmF;AACnF,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO,CAG/E;AAED,kFAAkF;AAClF,wBAAgB,uBAAuB,IAAI,IAAI,CAG9C;AAED;mFACmF;AACnF,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAM5D;AAgFD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAmBvC;AAiFD;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAqBvD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAOlF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,oBAAoB,GAAG,MAAM,CAUxE;AAED,0CAA0C;AAC1C,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,WAAW,GAAG,YAAY,GAAG,eAAe,CAAC,GAAG,OAAO,CAiB3H;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,KAAK,CAAC;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAQD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,IAAI,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAUhF;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAatC;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAgBtC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAG7C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,EAAE,CAGxC;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,KAAK,CAAC;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC,CA+BD;AAED,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,IAAI,CAKlD"}
|