@overshoot/sdk 0.1.0-alpha.3 → 0.1.0-alpha.4
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 +276 -31
- package/dist/index.d.mts +3 -25
- package/dist/index.d.ts +3 -25
- package/dist/index.js +36 -38
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +36 -38
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Overshoot SDK
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> **Warning: Alpha Release**: This is an alpha version (0.1.0-alpha.3). The API may change in future versions.
|
|
4
4
|
|
|
5
5
|
TypeScript SDK for real-time AI vision analysis on live video streams.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install overshoot@alpha
|
|
10
|
+
npm install @overshoot/sdk@alpha
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Or install a specific alpha version:
|
|
@@ -21,7 +21,7 @@ npm install @overshoot/sdk@0.1.0-alpha.3
|
|
|
21
21
|
### Camera Source
|
|
22
22
|
|
|
23
23
|
```typescript
|
|
24
|
-
import { RealtimeVision } from "overshoot";
|
|
24
|
+
import { RealtimeVision } from "@overshoot/sdk";
|
|
25
25
|
|
|
26
26
|
const vision = new RealtimeVision({
|
|
27
27
|
apiUrl: "https://cluster1.overshoot.ai/api/v0.2",
|
|
@@ -56,6 +56,8 @@ const vision = new RealtimeVision({
|
|
|
56
56
|
await vision.start();
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
> **Note:** Video files automatically loop continuously until you call `stop()`.
|
|
60
|
+
|
|
59
61
|
## Configuration
|
|
60
62
|
|
|
61
63
|
### RealtimeVisionConfig
|
|
@@ -71,19 +73,19 @@ interface RealtimeVisionConfig {
|
|
|
71
73
|
// Optional
|
|
72
74
|
source?: StreamSource; // Video source (default: environment-facing camera)
|
|
73
75
|
backend?: "overshoot"; // Model backend (default: "overshoot")
|
|
74
|
-
model?: string; // Model name (
|
|
76
|
+
model?: string; // Model name (see Available Models below)
|
|
75
77
|
outputSchema?: Record<string, any>; // JSON schema for structured output
|
|
76
78
|
onError?: (error: Error) => void;
|
|
77
79
|
debug?: boolean; // Enable debug logging (default: false)
|
|
78
80
|
|
|
79
81
|
processing?: {
|
|
80
|
-
fps?: number; //
|
|
82
|
+
fps?: number; // Source frames per second (1-120, auto-detected for cameras)
|
|
81
83
|
sampling_ratio?: number; // Fraction of frames to process (0-1, default: 0.1)
|
|
82
|
-
clip_length_seconds?: number; //
|
|
83
|
-
delay_seconds?: number; //
|
|
84
|
+
clip_length_seconds?: number; // Duration of each clip sent to the model (0.1-60, default: 1.0)
|
|
85
|
+
delay_seconds?: number; // Interval between inference runs (0-60, default: 1.0)
|
|
84
86
|
};
|
|
85
87
|
|
|
86
|
-
iceServers?: RTCIceServer[]; // Custom WebRTC ICE servers
|
|
88
|
+
iceServers?: RTCIceServer[]; // Custom WebRTC ICE servers (uses Overshoot TURN servers by default, see RealtimeVision.ts)
|
|
87
89
|
}
|
|
88
90
|
```
|
|
89
91
|
|
|
@@ -95,6 +97,84 @@ type StreamSource =
|
|
|
95
97
|
| { type: "video"; file: File };
|
|
96
98
|
```
|
|
97
99
|
|
|
100
|
+
### Available Models
|
|
101
|
+
|
|
102
|
+
| Model | Description |
|
|
103
|
+
| -------------------------------- | ------------------------------------------------------------------------------------ |
|
|
104
|
+
| `Qwen/Qwen3-VL-30B-A3B-Instruct` | Default. Very fast and performant general-purpose vision-language model. |
|
|
105
|
+
| `Qwen/Qwen3-VL-8B-Instruct` | Similar latency to 30B. Particularly good at OCR and text extraction tasks. |
|
|
106
|
+
| `OpenGVLab/InternVL3_5-30B-A3B` | Excels at capturing visual detail. More verbose output, higher latency. |
|
|
107
|
+
|
|
108
|
+
### Processing Parameters Explained
|
|
109
|
+
|
|
110
|
+
The processing parameters control how video frames are sampled and sent to the model:
|
|
111
|
+
|
|
112
|
+
- **`fps`**: The frame rate of your video source. Auto-detected for camera streams; defaults to 30 for video files.
|
|
113
|
+
- **`sampling_ratio`**: What fraction of frames to include in each clip (0.1 = 10% of frames).
|
|
114
|
+
- **`clip_length_seconds`**: Duration of video captured for each inference (e.g., 1.0 = 1 second of video).
|
|
115
|
+
- **`delay_seconds`**: How often inference runs (e.g., 1.0 = one inference per second).
|
|
116
|
+
|
|
117
|
+
**Example:** With `fps=30`, `clip_length_seconds=1.0`, `sampling_ratio=0.1`:
|
|
118
|
+
|
|
119
|
+
- Each clip captures 1 second of video (30 frames at 30fps)
|
|
120
|
+
- 10% of frames are sampled = 3 frames sent to the model
|
|
121
|
+
- If `delay_seconds=1.0`, you get ~1 inference result per second
|
|
122
|
+
|
|
123
|
+
#### Configuration by Use Case
|
|
124
|
+
|
|
125
|
+
Different applications need different processing configurations:
|
|
126
|
+
|
|
127
|
+
**Real-time tracking** (low latency, frequent updates):
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
processing: {
|
|
131
|
+
sampling_ratio: 0.7,
|
|
132
|
+
clip_length_seconds: 0.5,
|
|
133
|
+
delay_seconds: 0.3,
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Event detection** (monitoring for specific occurrences):
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
processing: {
|
|
141
|
+
sampling_ratio: 0.2,
|
|
142
|
+
clip_length_seconds: 5.0,
|
|
143
|
+
delay_seconds: 5.0,
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Structured Output (JSON Schema)
|
|
148
|
+
|
|
149
|
+
Use `outputSchema` to constrain the model's output to a specific JSON structure. The schema follows [JSON Schema](https://json-schema.org/) specification.
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const vision = new RealtimeVision({
|
|
153
|
+
apiUrl: "https://cluster1.overshoot.ai/api/v0.2",
|
|
154
|
+
apiKey: "your-api-key",
|
|
155
|
+
prompt: "Detect objects and return structured data",
|
|
156
|
+
outputSchema: {
|
|
157
|
+
type: "object",
|
|
158
|
+
properties: {
|
|
159
|
+
objects: {
|
|
160
|
+
type: "array",
|
|
161
|
+
items: { type: "string" },
|
|
162
|
+
},
|
|
163
|
+
count: { type: "integer" },
|
|
164
|
+
},
|
|
165
|
+
required: ["objects", "count"],
|
|
166
|
+
},
|
|
167
|
+
onResult: (result) => {
|
|
168
|
+
const data = JSON.parse(result.result);
|
|
169
|
+
console.log(`Found ${data.count} objects:`, data.objects);
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The model will return valid JSON matching your schema. If the model cannot produce valid output, `result.ok` will be `false` and `result.error` will contain details.
|
|
175
|
+
|
|
176
|
+
> **Note:** `result.result` is always a string. When using `outputSchema`, you must parse it with `JSON.parse()`.
|
|
177
|
+
|
|
98
178
|
## API Methods
|
|
99
179
|
|
|
100
180
|
```typescript
|
|
@@ -106,11 +186,196 @@ await vision.stop(); // Stop and cleanup resources
|
|
|
106
186
|
await vision.updatePrompt(newPrompt); // Update task while running
|
|
107
187
|
|
|
108
188
|
// State access
|
|
109
|
-
vision.getMediaStream(); // Get MediaStream for video preview
|
|
110
|
-
vision.getStreamId(); // Get current stream ID
|
|
189
|
+
vision.getMediaStream(); // Get MediaStream for video preview (null if not started)
|
|
190
|
+
vision.getStreamId(); // Get current stream ID (null if not started)
|
|
111
191
|
vision.isActive(); // Check if stream is running
|
|
112
192
|
```
|
|
113
193
|
|
|
194
|
+
## Stream Lifecycle
|
|
195
|
+
|
|
196
|
+
### Keepalive
|
|
197
|
+
|
|
198
|
+
Streams have a server-side lease (typically 300 seconds). The SDK automatically sends keepalive requests to maintain the connection. If the keepalive fails (e.g., network issues), the stream will stop and `onError` will be called.
|
|
199
|
+
|
|
200
|
+
You don't need to manage keepalives manually - just call `start()` and the SDK handles the rest.
|
|
201
|
+
|
|
202
|
+
### State and Memory
|
|
203
|
+
|
|
204
|
+
The SDK does not maintain memory or state between inference calls - each frame clip is processed independently. If your application needs to track state over time (e.g., counting repetitions, detecting transitions), implement this in your `onResult` callback:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
let lastPosition = "up";
|
|
208
|
+
let repCount = 0;
|
|
209
|
+
|
|
210
|
+
const vision = new RealtimeVision({
|
|
211
|
+
// ...config
|
|
212
|
+
onResult: (result) => {
|
|
213
|
+
const data = JSON.parse(result.result);
|
|
214
|
+
|
|
215
|
+
// Track state transitions externally
|
|
216
|
+
if (lastPosition === "down" && data.position === "up") {
|
|
217
|
+
repCount++;
|
|
218
|
+
}
|
|
219
|
+
lastPosition = data.position;
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
For result deduplication (e.g., avoiding repeated announcements), track previous results and implement cooldown logic in your application code.
|
|
225
|
+
|
|
226
|
+
## Prompt Engineering
|
|
227
|
+
|
|
228
|
+
Prompt quality significantly affects results. Here are some tips:
|
|
229
|
+
|
|
230
|
+
**Be specific about output format:**
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
prompt: "Count the people visible. Return only a number.";
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Include examples for complex tasks:**
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
prompt: `Describe the primary action happening. Examples:
|
|
240
|
+
- "Person walking left"
|
|
241
|
+
- "Car turning right"
|
|
242
|
+
- "Dog sitting still"`;
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Request minimal output for lower latency:**
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
prompt: "Is there a person in frame? Answer only 'yes' or 'no'.";
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Provide context when needed:**
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
prompt: `You are monitoring a ${locationName}. Alert if you see: ${alertConditions.join(", ")}.`;
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Use JSON schema for structured data:**
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
prompt: "Analyze the scene",
|
|
261
|
+
outputSchema: {
|
|
262
|
+
type: "object",
|
|
263
|
+
properties: {
|
|
264
|
+
description: { type: "string" },
|
|
265
|
+
alert: { type: "boolean" }
|
|
266
|
+
},
|
|
267
|
+
required: ["description", "alert"]
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
> **Note:** Prompt effectiveness varies by model. Test different approaches to find what works best for your use case.
|
|
272
|
+
|
|
273
|
+
## React Integration
|
|
274
|
+
|
|
275
|
+
When using the SDK in React applications, ensure proper cleanup:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
import { useEffect, useRef, useState } from "react";
|
|
279
|
+
import { RealtimeVision } from "@overshoot/sdk";
|
|
280
|
+
|
|
281
|
+
function VisionComponent() {
|
|
282
|
+
const visionRef = useRef<RealtimeVision | null>(null);
|
|
283
|
+
const videoRef = useRef<HTMLVideoElement>(null);
|
|
284
|
+
const [isRunning, setIsRunning] = useState(false);
|
|
285
|
+
|
|
286
|
+
const startVision = async () => {
|
|
287
|
+
const vision = new RealtimeVision({
|
|
288
|
+
apiUrl: "https://cluster1.overshoot.ai/api/v0.2",
|
|
289
|
+
apiKey: "your-api-key",
|
|
290
|
+
prompt: "Describe what you see",
|
|
291
|
+
onResult: (result) => {
|
|
292
|
+
console.log(result.result);
|
|
293
|
+
},
|
|
294
|
+
onError: (error) => {
|
|
295
|
+
console.error("Vision error:", error);
|
|
296
|
+
setIsRunning(false);
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
await vision.start();
|
|
301
|
+
visionRef.current = vision;
|
|
302
|
+
setIsRunning(true);
|
|
303
|
+
|
|
304
|
+
// Attach stream to video element for preview
|
|
305
|
+
const stream = vision.getMediaStream();
|
|
306
|
+
if (stream && videoRef.current) {
|
|
307
|
+
videoRef.current.srcObject = stream;
|
|
308
|
+
videoRef.current.play().catch(console.error);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// Cleanup on unmount
|
|
313
|
+
useEffect(() => {
|
|
314
|
+
return () => {
|
|
315
|
+
visionRef.current?.stop();
|
|
316
|
+
};
|
|
317
|
+
}, []);
|
|
318
|
+
|
|
319
|
+
return (
|
|
320
|
+
<div>
|
|
321
|
+
<video ref={videoRef} autoPlay playsInline muted />
|
|
322
|
+
<button onClick={startVision} disabled={isRunning}>
|
|
323
|
+
Start
|
|
324
|
+
</button>
|
|
325
|
+
<button onClick={() => visionRef.current?.stop()} disabled={!isRunning}>
|
|
326
|
+
Stop
|
|
327
|
+
</button>
|
|
328
|
+
</div>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Advanced: Custom Video Sources with StreamClient
|
|
334
|
+
|
|
335
|
+
For advanced use cases like streaming from a canvas, screen capture, or other custom sources, use `StreamClient` directly:
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
import { StreamClient } from "@overshoot/sdk";
|
|
339
|
+
|
|
340
|
+
const client = new StreamClient({
|
|
341
|
+
baseUrl: "https://cluster1.overshoot.ai/api/v0.2",
|
|
342
|
+
apiKey: "your-api-key",
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Get stream from any source (canvas, screen capture, etc.)
|
|
346
|
+
const canvas = document.querySelector("canvas");
|
|
347
|
+
const stream = canvas.captureStream(30);
|
|
348
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
349
|
+
|
|
350
|
+
// Set up WebRTC connection
|
|
351
|
+
const peerConnection = new RTCPeerConnection({ iceServers: [...] }); // See default ice servers in RealtimeVison.ts file
|
|
352
|
+
peerConnection.addTrack(videoTrack, stream);
|
|
353
|
+
|
|
354
|
+
const offer = await peerConnection.createOffer();
|
|
355
|
+
await peerConnection.setLocalDescription(offer);
|
|
356
|
+
|
|
357
|
+
// Create stream on server
|
|
358
|
+
const response = await client.createStream({
|
|
359
|
+
webrtc: { type: "offer", sdp: peerConnection.localDescription.sdp },
|
|
360
|
+
processing: { sampling_ratio: 0.5, fps: 30, clip_length_seconds: 1.0, delay_seconds: 1.0 },
|
|
361
|
+
inference: {
|
|
362
|
+
prompt: "Analyze the content",
|
|
363
|
+
backend: "overshoot",
|
|
364
|
+
model: "Qwen/Qwen3-VL-30B-A3B-Instruct",
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
await peerConnection.setRemoteDescription(response.webrtc);
|
|
369
|
+
|
|
370
|
+
// Connect WebSocket for results
|
|
371
|
+
const ws = client.connectWebSocket(response.stream_id);
|
|
372
|
+
ws.onopen = () => ws.send(JSON.stringify({ api_key: "your-api-key" }));
|
|
373
|
+
ws.onmessage = (event) => {
|
|
374
|
+
const result = JSON.parse(event.data);
|
|
375
|
+
console.log("Result:", result);
|
|
376
|
+
};
|
|
377
|
+
```
|
|
378
|
+
|
|
114
379
|
## Examples
|
|
115
380
|
|
|
116
381
|
### Object Detection with Structured Output
|
|
@@ -152,26 +417,6 @@ const vision = new RealtimeVision({
|
|
|
152
417
|
await vision.start();
|
|
153
418
|
```
|
|
154
419
|
|
|
155
|
-
### Video Preview Display
|
|
156
|
-
|
|
157
|
-
```typescript
|
|
158
|
-
const vision = new RealtimeVision({
|
|
159
|
-
apiUrl: "https://cluster1.overshoot.ai/api/v0.2",
|
|
160
|
-
apiKey: "your-api-key",
|
|
161
|
-
prompt: "Describe what you see",
|
|
162
|
-
onResult: (result) => console.log(result.result),
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
await vision.start();
|
|
166
|
-
|
|
167
|
-
// Attach to video element for preview
|
|
168
|
-
const videoElement = document.querySelector("video");
|
|
169
|
-
const stream = vision.getMediaStream();
|
|
170
|
-
if (stream) {
|
|
171
|
-
videoElement.srcObject = stream;
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
420
|
### Dynamic Prompt Updates
|
|
176
421
|
|
|
177
422
|
```typescript
|
|
@@ -246,7 +491,7 @@ interface StreamInferenceResult {
|
|
|
246
491
|
model_backend: "overshoot";
|
|
247
492
|
model_name: string; // Model used
|
|
248
493
|
prompt: string; // Task that was run
|
|
249
|
-
result: string; // Model output (
|
|
494
|
+
result: string; // Model output (always a string - parse JSON if using outputSchema)
|
|
250
495
|
inference_latency_ms: number; // Model inference time
|
|
251
496
|
total_latency_ms: number; // End-to-end latency
|
|
252
497
|
ok: boolean; // Success status
|
package/dist/index.d.mts
CHANGED
|
@@ -64,20 +64,6 @@ type StreamConfigResponse = {
|
|
|
64
64
|
created_at?: string;
|
|
65
65
|
updated_at?: string;
|
|
66
66
|
};
|
|
67
|
-
type FeedbackCreateRequest = {
|
|
68
|
-
rating: number;
|
|
69
|
-
category: string;
|
|
70
|
-
feedback?: string;
|
|
71
|
-
};
|
|
72
|
-
type FeedbackResponse = {
|
|
73
|
-
id: string;
|
|
74
|
-
stream_id: string;
|
|
75
|
-
rating: number;
|
|
76
|
-
category: string;
|
|
77
|
-
feedback: string;
|
|
78
|
-
created_at?: string;
|
|
79
|
-
updated_at?: string;
|
|
80
|
-
};
|
|
81
67
|
type KeepaliveResponse = {
|
|
82
68
|
status: "ok";
|
|
83
69
|
stream_id: string;
|
|
@@ -105,8 +91,6 @@ declare class StreamClient {
|
|
|
105
91
|
createStream(request: StreamCreateRequest): Promise<StreamCreateResponse>;
|
|
106
92
|
renewLease(streamId: string): Promise<KeepaliveResponse>;
|
|
107
93
|
updatePrompt(streamId: string, prompt: string): Promise<StreamConfigResponse>;
|
|
108
|
-
submitFeedback(streamId: string, feedback: FeedbackCreateRequest): Promise<StatusResponse>;
|
|
109
|
-
getAllFeedback(): Promise<FeedbackResponse[]>;
|
|
110
94
|
connectWebSocket(streamId: string): WebSocket;
|
|
111
95
|
/**
|
|
112
96
|
* Health check endpoint (for testing, uses internal port if available)
|
|
@@ -203,6 +187,8 @@ declare class RealtimeVision {
|
|
|
203
187
|
private streamId;
|
|
204
188
|
private keepaliveInterval;
|
|
205
189
|
private videoElement;
|
|
190
|
+
private canvasElement;
|
|
191
|
+
private canvasAnimationFrameId;
|
|
206
192
|
private isRunning;
|
|
207
193
|
constructor(config: RealtimeVisionConfig);
|
|
208
194
|
/**
|
|
@@ -253,14 +239,6 @@ declare class RealtimeVision {
|
|
|
253
239
|
* Stop the vision stream and clean up resources
|
|
254
240
|
*/
|
|
255
241
|
stop(): Promise<void>;
|
|
256
|
-
/**
|
|
257
|
-
* Submit feedback for the stream
|
|
258
|
-
*/
|
|
259
|
-
submitFeedback(feedback: {
|
|
260
|
-
rating: number;
|
|
261
|
-
category: string;
|
|
262
|
-
feedback?: string;
|
|
263
|
-
}): Promise<void>;
|
|
264
242
|
/**
|
|
265
243
|
* Get the current stream ID
|
|
266
244
|
*/
|
|
@@ -299,4 +277,4 @@ declare class ServerError extends ApiError {
|
|
|
299
277
|
constructor(message: string, requestId?: string, details?: any);
|
|
300
278
|
}
|
|
301
279
|
|
|
302
|
-
export { ApiError, type ErrorResponse, type
|
|
280
|
+
export { ApiError, type ErrorResponse, type KeepaliveResponse, NetworkError, NotFoundError, RealtimeVision, type RealtimeVisionConfig, ServerError, type StatusResponse, StreamClient, type StreamClientMeta, type StreamConfigResponse, type StreamCreateRequest, type StreamCreateResponse, type StreamInferenceConfig, type StreamInferenceResult, type StreamProcessingConfig, type StreamSource, UnauthorizedError, ValidationError, type WebRtcAnswer, type WebRtcOffer };
|
package/dist/index.d.ts
CHANGED
|
@@ -64,20 +64,6 @@ type StreamConfigResponse = {
|
|
|
64
64
|
created_at?: string;
|
|
65
65
|
updated_at?: string;
|
|
66
66
|
};
|
|
67
|
-
type FeedbackCreateRequest = {
|
|
68
|
-
rating: number;
|
|
69
|
-
category: string;
|
|
70
|
-
feedback?: string;
|
|
71
|
-
};
|
|
72
|
-
type FeedbackResponse = {
|
|
73
|
-
id: string;
|
|
74
|
-
stream_id: string;
|
|
75
|
-
rating: number;
|
|
76
|
-
category: string;
|
|
77
|
-
feedback: string;
|
|
78
|
-
created_at?: string;
|
|
79
|
-
updated_at?: string;
|
|
80
|
-
};
|
|
81
67
|
type KeepaliveResponse = {
|
|
82
68
|
status: "ok";
|
|
83
69
|
stream_id: string;
|
|
@@ -105,8 +91,6 @@ declare class StreamClient {
|
|
|
105
91
|
createStream(request: StreamCreateRequest): Promise<StreamCreateResponse>;
|
|
106
92
|
renewLease(streamId: string): Promise<KeepaliveResponse>;
|
|
107
93
|
updatePrompt(streamId: string, prompt: string): Promise<StreamConfigResponse>;
|
|
108
|
-
submitFeedback(streamId: string, feedback: FeedbackCreateRequest): Promise<StatusResponse>;
|
|
109
|
-
getAllFeedback(): Promise<FeedbackResponse[]>;
|
|
110
94
|
connectWebSocket(streamId: string): WebSocket;
|
|
111
95
|
/**
|
|
112
96
|
* Health check endpoint (for testing, uses internal port if available)
|
|
@@ -203,6 +187,8 @@ declare class RealtimeVision {
|
|
|
203
187
|
private streamId;
|
|
204
188
|
private keepaliveInterval;
|
|
205
189
|
private videoElement;
|
|
190
|
+
private canvasElement;
|
|
191
|
+
private canvasAnimationFrameId;
|
|
206
192
|
private isRunning;
|
|
207
193
|
constructor(config: RealtimeVisionConfig);
|
|
208
194
|
/**
|
|
@@ -253,14 +239,6 @@ declare class RealtimeVision {
|
|
|
253
239
|
* Stop the vision stream and clean up resources
|
|
254
240
|
*/
|
|
255
241
|
stop(): Promise<void>;
|
|
256
|
-
/**
|
|
257
|
-
* Submit feedback for the stream
|
|
258
|
-
*/
|
|
259
|
-
submitFeedback(feedback: {
|
|
260
|
-
rating: number;
|
|
261
|
-
category: string;
|
|
262
|
-
feedback?: string;
|
|
263
|
-
}): Promise<void>;
|
|
264
242
|
/**
|
|
265
243
|
* Get the current stream ID
|
|
266
244
|
*/
|
|
@@ -299,4 +277,4 @@ declare class ServerError extends ApiError {
|
|
|
299
277
|
constructor(message: string, requestId?: string, details?: any);
|
|
300
278
|
}
|
|
301
279
|
|
|
302
|
-
export { ApiError, type ErrorResponse, type
|
|
280
|
+
export { ApiError, type ErrorResponse, type KeepaliveResponse, NetworkError, NotFoundError, RealtimeVision, type RealtimeVisionConfig, ServerError, type StatusResponse, StreamClient, type StreamClientMeta, type StreamConfigResponse, type StreamCreateRequest, type StreamCreateResponse, type StreamInferenceConfig, type StreamInferenceResult, type StreamProcessingConfig, type StreamSource, UnauthorizedError, ValidationError, type WebRtcAnswer, type WebRtcOffer };
|
package/dist/index.js
CHANGED
|
@@ -132,17 +132,6 @@ var StreamClient = class {
|
|
|
132
132
|
}
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
|
-
async submitFeedback(streamId, feedback) {
|
|
136
|
-
return this.request(`/streams/${streamId}/feedback`, {
|
|
137
|
-
method: "POST",
|
|
138
|
-
body: JSON.stringify(feedback)
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
async getAllFeedback() {
|
|
142
|
-
return this.request("/streams/feedback", {
|
|
143
|
-
method: "GET"
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
135
|
connectWebSocket(streamId) {
|
|
147
136
|
const wsUrl = this.baseUrl.replace("http://", "ws://").replace("https://", "wss://");
|
|
148
137
|
return new WebSocket(`${wsUrl}/ws/streams/${streamId}`);
|
|
@@ -192,13 +181,11 @@ var DEFAULTS = {
|
|
|
192
181
|
}
|
|
193
182
|
]
|
|
194
183
|
};
|
|
195
|
-
console.log("defaults", DEFAULTS);
|
|
196
184
|
var CONSTRAINTS = {
|
|
197
185
|
SAMPLING_RATIO: { min: 0, max: 1 },
|
|
198
186
|
FPS: { min: 1, max: 120 },
|
|
199
187
|
CLIP_LENGTH_SECONDS: { min: 0.1, max: 60 },
|
|
200
|
-
DELAY_SECONDS: { min: 0, max: 60 }
|
|
201
|
-
RATING: { min: 1, max: 5 }
|
|
188
|
+
DELAY_SECONDS: { min: 0, max: 60 }
|
|
202
189
|
};
|
|
203
190
|
var Logger = class {
|
|
204
191
|
constructor(debugEnabled = false) {
|
|
@@ -233,6 +220,8 @@ var RealtimeVision = class {
|
|
|
233
220
|
this.streamId = null;
|
|
234
221
|
this.keepaliveInterval = null;
|
|
235
222
|
this.videoElement = null;
|
|
223
|
+
this.canvasElement = null;
|
|
224
|
+
this.canvasAnimationFrameId = null;
|
|
236
225
|
this.isRunning = false;
|
|
237
226
|
this.validateConfig(config);
|
|
238
227
|
this.config = config;
|
|
@@ -342,7 +331,31 @@ var RealtimeVision = class {
|
|
|
342
331
|
});
|
|
343
332
|
await video.play();
|
|
344
333
|
this.logger.debug("Video playback started");
|
|
345
|
-
|
|
334
|
+
let stream;
|
|
335
|
+
if (typeof video.captureStream === "function") {
|
|
336
|
+
this.logger.debug("Using native video.captureStream()");
|
|
337
|
+
stream = video.captureStream();
|
|
338
|
+
} else {
|
|
339
|
+
this.logger.debug(
|
|
340
|
+
"captureStream not supported, using canvas fallback for Safari"
|
|
341
|
+
);
|
|
342
|
+
const canvas = document.createElement("canvas");
|
|
343
|
+
canvas.width = video.videoWidth || 640;
|
|
344
|
+
canvas.height = video.videoHeight || 480;
|
|
345
|
+
const ctx = canvas.getContext("2d");
|
|
346
|
+
if (!ctx) {
|
|
347
|
+
throw new Error("Failed to get canvas 2D context");
|
|
348
|
+
}
|
|
349
|
+
const drawFrame = () => {
|
|
350
|
+
if (!video.paused && !video.ended) {
|
|
351
|
+
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
352
|
+
this.canvasAnimationFrameId = requestAnimationFrame(drawFrame);
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
drawFrame();
|
|
356
|
+
stream = canvas.captureStream(30);
|
|
357
|
+
this.canvasElement = canvas;
|
|
358
|
+
}
|
|
346
359
|
if (!stream) {
|
|
347
360
|
throw new Error("Failed to capture video stream");
|
|
348
361
|
}
|
|
@@ -605,29 +618,6 @@ var RealtimeVision = class {
|
|
|
605
618
|
await this.cleanup();
|
|
606
619
|
this.isRunning = false;
|
|
607
620
|
}
|
|
608
|
-
/**
|
|
609
|
-
* Submit feedback for the stream
|
|
610
|
-
*/
|
|
611
|
-
async submitFeedback(feedback) {
|
|
612
|
-
if (!this.streamId) {
|
|
613
|
-
throw new Error("No active stream");
|
|
614
|
-
}
|
|
615
|
-
if (feedback.rating < CONSTRAINTS.RATING.min || feedback.rating > CONSTRAINTS.RATING.max) {
|
|
616
|
-
throw new ValidationError2(
|
|
617
|
-
`rating must be between ${CONSTRAINTS.RATING.min} and ${CONSTRAINTS.RATING.max}`
|
|
618
|
-
);
|
|
619
|
-
}
|
|
620
|
-
if (!feedback.category || typeof feedback.category !== "string") {
|
|
621
|
-
throw new ValidationError2("category must be a non-empty string");
|
|
622
|
-
}
|
|
623
|
-
this.logger.debug("Submitting feedback");
|
|
624
|
-
await this.client.submitFeedback(this.streamId, {
|
|
625
|
-
rating: feedback.rating,
|
|
626
|
-
category: feedback.category,
|
|
627
|
-
feedback: feedback.feedback ?? ""
|
|
628
|
-
});
|
|
629
|
-
this.logger.info("Feedback submitted");
|
|
630
|
-
}
|
|
631
621
|
/**
|
|
632
622
|
* Get the current stream ID
|
|
633
623
|
*/
|
|
@@ -664,6 +654,14 @@ var RealtimeVision = class {
|
|
|
664
654
|
this.mediaStream.getTracks().forEach((track) => track.stop());
|
|
665
655
|
this.mediaStream = null;
|
|
666
656
|
}
|
|
657
|
+
if (this.canvasAnimationFrameId) {
|
|
658
|
+
cancelAnimationFrame(this.canvasAnimationFrameId);
|
|
659
|
+
this.canvasAnimationFrameId = null;
|
|
660
|
+
}
|
|
661
|
+
if (this.canvasElement) {
|
|
662
|
+
this.canvasElement.remove();
|
|
663
|
+
this.canvasElement = null;
|
|
664
|
+
}
|
|
667
665
|
if (this.videoElement) {
|
|
668
666
|
this.videoElement.pause();
|
|
669
667
|
URL.revokeObjectURL(this.videoElement.src);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/errors.ts","../src/client/client.ts","../src/client/RealtimeVision.ts"],"names":["ValidationError"],"mappings":"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","file":"index.js","sourcesContent":["export class ApiError extends Error {\n readonly statusCode?: number;\n readonly requestId?: string;\n readonly details?: any;\n\n constructor(\n message: string,\n statusCode?: number,\n requestId?: string,\n details?: any,\n ) {\n super(message);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.requestId = requestId;\n this.details = details;\n }\n}\n\nexport class UnauthorizedError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 401, requestId);\n this.name = \"UnauthorizedError\";\n }\n}\n\nexport class ValidationError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 422, requestId, details);\n this.name = \"ValidationError\";\n }\n}\n\nexport class NotFoundError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 404, requestId);\n this.name = \"NotFoundError\";\n }\n}\n\nexport class NetworkError extends ApiError {\n readonly cause?: Error;\n\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"NetworkError\";\n this.cause = cause;\n }\n}\n\nexport class ServerError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 500, requestId, details);\n this.name = \"ServerError\";\n }\n}\n","import type {\n StreamCreateRequest,\n StreamCreateResponse,\n KeepaliveResponse,\n StreamConfigResponse,\n FeedbackCreateRequest,\n FeedbackResponse,\n StatusResponse,\n ErrorResponse,\n} from \"./types\";\nimport {\n ApiError,\n ValidationError,\n NotFoundError,\n NetworkError,\n ServerError,\n UnauthorizedError,\n} from \"./errors\";\n\ntype ClientConfig = {\n baseUrl: string;\n apiKey: string;\n};\n\nexport class StreamClient {\n private baseUrl: string;\n private apiKey: string;\n\n constructor(config: ClientConfig) {\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new Error(\"apiKey is required and must be a string\");\n }\n\n this.baseUrl = config.baseUrl;\n this.apiKey = config.apiKey;\n }\n\n private async request<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n credentials: \"include\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const errorData: ErrorResponse = await response.json().catch(() => ({\n error: \"unknown_error\",\n message: response.statusText,\n }));\n\n const message = errorData.message || errorData.error;\n\n if (response.status === 401) {\n throw new UnauthorizedError(\n message || \"Invalid or revoked API key\",\n errorData.request_id,\n );\n }\n if (response.status === 422 || response.status === 400) {\n throw new ValidationError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n if (response.status === 404) {\n throw new NotFoundError(message, errorData.request_id);\n }\n if (response.status >= 500) {\n throw new ServerError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n\n throw new ApiError(\n message,\n response.status,\n errorData.request_id,\n errorData.details,\n );\n }\n\n return await response.json();\n } catch (error) {\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n throw new NetworkError(`Network error: ${error.message}`, error);\n }\n\n throw new NetworkError(\"Unknown network error\");\n }\n }\n async createStream(\n request: StreamCreateRequest,\n ): Promise<StreamCreateResponse> {\n return this.request<StreamCreateResponse>(\"/streams\", {\n method: \"POST\",\n body: JSON.stringify(request),\n });\n }\n\n async renewLease(streamId: string): Promise<KeepaliveResponse> {\n return this.request<KeepaliveResponse>(`/streams/${streamId}/keepalive`, {\n method: \"POST\",\n });\n }\n\n async updatePrompt(\n streamId: string,\n prompt: string,\n ): Promise<StreamConfigResponse> {\n return this.request<StreamConfigResponse>(\n `/streams/${streamId}/config/prompt`,\n {\n method: \"PATCH\",\n body: JSON.stringify({ prompt }),\n },\n );\n }\n\n async submitFeedback(\n streamId: string,\n feedback: FeedbackCreateRequest,\n ): Promise<StatusResponse> {\n return this.request<StatusResponse>(`/streams/${streamId}/feedback`, {\n method: \"POST\",\n body: JSON.stringify(feedback),\n });\n }\n\n async getAllFeedback(): Promise<FeedbackResponse[]> {\n return this.request<FeedbackResponse[]>(\"/streams/feedback\", {\n method: \"GET\",\n });\n }\n\n connectWebSocket(streamId: string): WebSocket {\n const wsUrl = this.baseUrl\n .replace(\"http://\", \"ws://\")\n .replace(\"https://\", \"wss://\");\n return new WebSocket(`${wsUrl}/ws/streams/${streamId}`);\n }\n\n /**\n * Health check endpoint (for testing, uses internal port if available)\n * Note: This endpoint may not be available via the main API\n */\n async healthCheck(): Promise<string> {\n const url = `${this.baseUrl}/healthz`;\n const response = await fetch(url, {\n credentials: \"include\",\n });\n return response.text();\n }\n}\n","import { StreamClient } from \"./client\";\n\nimport {\n type StreamInferenceResult,\n type StreamProcessingConfig,\n type StreamSource,\n} from \"./types\";\n\n/**\n * Default configuration values for RealtimeVision\n */\nconst DEFAULTS = {\n BACKEND: \"overshoot\" as const,\n MODEL: \"Qwen/Qwen3-VL-30B-A3B-Instruct\",\n SOURCE: { type: \"camera\", cameraFacing: \"environment\" } as const,\n SAMPLING_RATIO: 0.1,\n CLIP_LENGTH_SECONDS: 1.0,\n DELAY_SECONDS: 1.0,\n FALLBACK_FPS: 30,\n ICE_SERVERS: [\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n ] as RTCIceServer[],\n} as const;\n\nconsole.log(\"defaults\", DEFAULTS);\n\n/**\n * Validation constraints\n */\nconst CONSTRAINTS = {\n SAMPLING_RATIO: { min: 0, max: 1 },\n FPS: { min: 1, max: 120 },\n CLIP_LENGTH_SECONDS: { min: 0.1, max: 60 },\n DELAY_SECONDS: { min: 0, max: 60 },\n RATING: { min: 1, max: 5 },\n} as const;\n\n/**\n * Logger utility for controlled logging\n */\nclass Logger {\n private debugEnabled: boolean;\n\n constructor(debugEnabled: boolean = false) {\n this.debugEnabled = debugEnabled;\n }\n\n debug(...args: any[]): void {\n if (this.debugEnabled) {\n console.log(\"[RealtimeVision Debug]\", ...args);\n }\n }\n\n info(...args: any[]): void {\n console.log(\"[RealtimeVision]\", ...args);\n }\n\n warn(...args: any[]): void {\n console.warn(\"[RealtimeVision]\", ...args);\n }\n\n error(...args: any[]): void {\n console.error(\"[RealtimeVision]\", ...args);\n }\n}\n\nexport interface RealtimeVisionConfig {\n /**\n * Base URL for the API (e.g., \"https://api.example.com\")\n */\n apiUrl: string;\n\n /**\n * API key for authentication\n * Required for all API requests\n */\n apiKey: string;\n\n /**\n * The prompt/task to run on window segments of the stream.\n * This runs continuously (at a defined window interval).\n *\n * Examples:\n * - \"Read any visible text\"\n * - \"Detect objects and return as JSON array\"\n * - \"Describe facial expression\"\n */\n prompt: string;\n\n /**\n * Video source configuration\n * Defaults to camera with environment facing if not specified\n */\n source?: StreamSource;\n\n /**\n * Model backend to use\n */\n backend?: \"overshoot\";\n\n /**\n * Model name to use for inference\n */\n model?: string;\n\n /**\n * Optional JSON schema for structured output\n */\n outputSchema?: Record<string, any>;\n\n /**\n * Called when a new inference result arrives (~1 per second)\n */\n onResult: (result: StreamInferenceResult) => void;\n\n /**\n * Called when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Custom processing configuration\n * All fields are optional and will use defaults if not provided\n */\n processing?: {\n /**\n * Sampling ratio (0-1). Controls what fraction of frames are processed.\n */\n sampling_ratio?: number;\n /**\n * Frames per second (1-120)\n */\n fps?: number;\n /**\n * Clip length in seconds (0.1-60)\n */\n clip_length_seconds?: number;\n /**\n * Delay in seconds (0-60)\n */\n delay_seconds?: number;\n };\n\n /**\n * ICE servers for WebRTC connection\n * If not provided, uses default TURN servers\n */\n iceServers?: RTCIceServer[];\n\n /**\n * Enable debug logging\n * @default false\n */\n debug?: boolean;\n}\n\nclass ValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ValidationError\";\n }\n}\n\nexport class RealtimeVision {\n private config: RealtimeVisionConfig;\n private client: StreamClient;\n private logger: Logger;\n\n private mediaStream: MediaStream | null = null;\n private peerConnection: RTCPeerConnection | null = null;\n private webSocket: WebSocket | null = null;\n private streamId: string | null = null;\n private keepaliveInterval: number | null = null;\n private videoElement: HTMLVideoElement | null = null;\n\n private isRunning = false;\n\n constructor(config: RealtimeVisionConfig) {\n this.validateConfig(config);\n this.config = config;\n this.logger = new Logger(config.debug ?? false);\n this.client = new StreamClient({\n baseUrl: config.apiUrl,\n apiKey: config.apiKey,\n });\n }\n\n /**\n * Validate configuration values\n */\n private validateConfig(config: RealtimeVisionConfig): void {\n if (!config.apiUrl || typeof config.apiUrl !== \"string\") {\n throw new ValidationError(\"apiUrl is required and must be a string\");\n }\n\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new ValidationError(\"apiKey is required and must be a string\");\n }\n\n if (!config.prompt || typeof config.prompt !== \"string\") {\n throw new ValidationError(\"prompt is required and must be a string\");\n }\n\n if (config.source) {\n if (config.source.type === \"camera\") {\n if (\n config.source.cameraFacing !== \"user\" &&\n config.source.cameraFacing !== \"environment\"\n ) {\n throw new ValidationError(\n 'cameraFacing must be \"user\" or \"environment\"',\n );\n }\n } else if (config.source.type === \"video\") {\n if (!(config.source.file instanceof File)) {\n throw new ValidationError(\"video source must provide a File object\");\n }\n } else {\n throw new ValidationError('source.type must be \"camera\" or \"video\"');\n }\n }\n\n if (config.processing?.sampling_ratio !== undefined) {\n const ratio = config.processing.sampling_ratio;\n if (\n ratio < CONSTRAINTS.SAMPLING_RATIO.min ||\n ratio > CONSTRAINTS.SAMPLING_RATIO.max\n ) {\n throw new ValidationError(\n `sampling_ratio must be between ${CONSTRAINTS.SAMPLING_RATIO.min} and ${CONSTRAINTS.SAMPLING_RATIO.max}`,\n );\n }\n }\n\n if (config.processing?.fps !== undefined) {\n const fps = config.processing.fps;\n if (fps < CONSTRAINTS.FPS.min || fps > CONSTRAINTS.FPS.max) {\n throw new ValidationError(\n `fps must be between ${CONSTRAINTS.FPS.min} and ${CONSTRAINTS.FPS.max}`,\n );\n }\n }\n\n if (config.processing?.clip_length_seconds !== undefined) {\n const clip = config.processing.clip_length_seconds;\n if (\n clip < CONSTRAINTS.CLIP_LENGTH_SECONDS.min ||\n clip > CONSTRAINTS.CLIP_LENGTH_SECONDS.max\n ) {\n throw new ValidationError(\n `clip_length_seconds must be between ${CONSTRAINTS.CLIP_LENGTH_SECONDS.min} and ${CONSTRAINTS.CLIP_LENGTH_SECONDS.max}`,\n );\n }\n }\n\n if (config.processing?.delay_seconds !== undefined) {\n const delay = config.processing.delay_seconds;\n if (\n delay < CONSTRAINTS.DELAY_SECONDS.min ||\n delay > CONSTRAINTS.DELAY_SECONDS.max\n ) {\n throw new ValidationError(\n `delay_seconds must be between ${CONSTRAINTS.DELAY_SECONDS.min} and ${CONSTRAINTS.DELAY_SECONDS.max}`,\n );\n }\n }\n }\n\n /**\n * Create media stream from the configured source\n */\n private async createMediaStream(source: StreamSource): Promise<MediaStream> {\n this.logger.debug(\"Creating media stream from source:\", source.type);\n\n switch (source.type) {\n case \"camera\":\n return await navigator.mediaDevices.getUserMedia({\n video: { facingMode: { ideal: source.cameraFacing } },\n audio: false,\n });\n\n case \"video\":\n const video = document.createElement(\"video\");\n video.src = URL.createObjectURL(source.file);\n video.muted = true;\n video.loop = true;\n video.playsInline = true;\n\n this.logger.debug(\"Loading video file:\", source.file.name);\n\n // Wait for video to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(\"Video loading timeout after 10 seconds\"));\n }, 10000);\n\n video.onloadedmetadata = () => {\n clearTimeout(timeout);\n this.logger.debug(\"Video metadata loaded\");\n resolve();\n };\n\n video.onerror = (e) => {\n clearTimeout(timeout);\n this.logger.error(\"Video loading error:\", e);\n reject(new Error(\"Failed to load video file\"));\n };\n\n if (video.readyState >= 1) {\n clearTimeout(timeout);\n resolve();\n }\n });\n\n await video.play();\n this.logger.debug(\"Video playback started\");\n\n const stream = video.captureStream();\n if (!stream) {\n throw new Error(\"Failed to capture video stream\");\n }\n\n const videoTracks = stream.getVideoTracks();\n if (videoTracks.length === 0) {\n throw new Error(\"Video stream has no video tracks\");\n }\n\n this.videoElement = video;\n return stream;\n\n default:\n throw new Error(`Unknown source type: ${(source as any).type}`);\n }\n }\n\n /**\n * Get FPS from media stream\n */\n private async getStreamFps(\n stream: MediaStream | null,\n source: StreamSource,\n ): Promise<number> {\n if (!stream) {\n this.logger.warn(\"Stream is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTracks = stream.getVideoTracks();\n if (!videoTracks || videoTracks.length === 0) {\n this.logger.warn(\"No video tracks found, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTrack = videoTracks[0];\n if (!videoTrack) {\n this.logger.warn(\"First video track is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n // For camera sources, get FPS from track settings\n if (source.type === \"camera\") {\n const settings = videoTrack.getSettings();\n const fps = settings.frameRate ?? DEFAULTS.FALLBACK_FPS;\n this.logger.debug(\"Detected camera FPS:\", fps);\n return fps;\n }\n\n // For video file sources, try to get FPS from video element\n if (source.type === \"video\" && this.videoElement) {\n await new Promise<void>((resolve, reject) => {\n if (this.videoElement!.readyState >= 1) {\n resolve();\n } else {\n this.videoElement!.onloadedmetadata = () => resolve();\n this.videoElement!.onerror = () =>\n reject(new Error(\"Failed to load video metadata\"));\n }\n });\n\n // For video files, use fallback FPS or user-specified config\n this.logger.debug(\"Using fallback FPS for video file\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n return DEFAULTS.FALLBACK_FPS;\n }\n\n /**\n * Get processing configuration with defaults applied\n */\n private getProcessingConfig(detectedFps: number): StreamProcessingConfig {\n const userProcessing = this.config.processing || {};\n\n return {\n sampling_ratio: userProcessing.sampling_ratio ?? DEFAULTS.SAMPLING_RATIO,\n fps: userProcessing.fps ?? detectedFps,\n clip_length_seconds:\n userProcessing.clip_length_seconds ?? DEFAULTS.CLIP_LENGTH_SECONDS,\n delay_seconds: userProcessing.delay_seconds ?? DEFAULTS.DELAY_SECONDS,\n };\n }\n\n /**\n * Get the effective source configuration\n */\n private getSource(): StreamSource {\n return this.config.source ?? DEFAULTS.SOURCE;\n }\n\n /**\n * Start the vision stream\n */\n async start(): Promise<void> {\n if (this.isRunning) {\n throw new Error(\"Vision stream already running\");\n }\n\n try {\n const source = this.getSource();\n this.logger.debug(\"Starting stream with source type:\", source.type);\n\n if (source.type === \"video\") {\n this.logger.debug(\"Video file:\", {\n name: source.file.name,\n size: source.file.size,\n type: source.file.type,\n });\n\n if (!source.file || !(source.file instanceof File)) {\n throw new Error(\"Invalid video file\");\n }\n }\n\n // Create media stream\n this.mediaStream = await this.createMediaStream(source);\n const videoTrack = this.mediaStream.getVideoTracks()[0];\n if (!videoTrack) {\n throw new Error(\"No video track available\");\n }\n\n // Get FPS for the stream\n const detectedFps = await this.getStreamFps(this.mediaStream, source);\n\n // Set up WebRTC peer connection\n const iceServers = this.config.iceServers ?? DEFAULTS.ICE_SERVERS;\n this.logger.debug(\"Creating peer connection with ICE servers\");\n this.peerConnection = new RTCPeerConnection({ iceServers });\n\n // Set up ICE logging\n this.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n this.logger.debug(\"ICE candidate:\", {\n type: event.candidate.type,\n protocol: event.candidate.protocol,\n });\n } else {\n this.logger.debug(\"ICE gathering complete\");\n }\n };\n\n this.peerConnection.oniceconnectionstatechange = () => {\n this.logger.debug(\n \"ICE connection state:\",\n this.peerConnection?.iceConnectionState,\n );\n };\n\n this.peerConnection.addTrack(videoTrack, this.mediaStream);\n\n // Create and set local offer\n const offer = await this.peerConnection.createOffer();\n await this.peerConnection.setLocalDescription(offer);\n\n if (!this.peerConnection.localDescription) {\n throw new Error(\"Failed to create local description\");\n }\n\n // Create stream on server\n this.logger.debug(\"Creating stream on server\");\n const response = await this.client.createStream({\n webrtc: {\n type: \"offer\",\n sdp: this.peerConnection.localDescription.sdp,\n },\n processing: this.getProcessingConfig(detectedFps),\n inference: {\n prompt: this.config.prompt,\n backend: this.config.backend ?? DEFAULTS.BACKEND,\n model: this.config.model ?? DEFAULTS.MODEL,\n output_schema_json: this.config.outputSchema,\n },\n });\n\n this.logger.debug(\"Backend response received:\", {\n stream_id: response.stream_id,\n has_turn_servers: !!response.turn_servers,\n });\n\n // Set remote description\n await this.peerConnection.setRemoteDescription(response.webrtc);\n\n this.streamId = response.stream_id;\n this.logger.info(\"Stream started:\", this.streamId);\n\n // Set up keepalive\n this.setupKeepalive(response.lease?.ttl_seconds);\n\n // Connect WebSocket for results\n this.setupWebSocket(response.stream_id);\n\n this.isRunning = true;\n } catch (error) {\n await this.handleFatalError(error);\n throw error;\n }\n }\n\n /**\n * Set up keepalive interval with error handling\n */\n private setupKeepalive(ttlSeconds: number | undefined): void {\n if (!ttlSeconds) {\n return;\n }\n\n const intervalMs = (ttlSeconds / 2) * 1000;\n this.logger.debug(\"Setting up keepalive with interval:\", intervalMs, \"ms\");\n\n this.keepaliveInterval = window.setInterval(async () => {\n try {\n if (this.streamId) {\n await this.client.renewLease(this.streamId);\n this.logger.debug(\"Lease renewed\");\n }\n } catch (error) {\n this.logger.error(\"Keepalive failed:\", error);\n const keepaliveError = new Error(\n `Keepalive failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n await this.handleFatalError(keepaliveError);\n }\n }, intervalMs);\n }\n\n /**\n * Set up WebSocket connection with error handling\n */\n private setupWebSocket(streamId: string): void {\n this.logger.debug(\"Connecting WebSocket for stream:\", streamId);\n this.webSocket = this.client.connectWebSocket(streamId);\n\n this.webSocket.onopen = () => {\n this.logger.debug(\"WebSocket connected\");\n if (this.webSocket) {\n this.webSocket.send(JSON.stringify({ api_key: this.config.apiKey }));\n }\n };\n\n this.webSocket.onmessage = (event) => {\n try {\n const result: StreamInferenceResult = JSON.parse(event.data);\n this.config.onResult(result);\n } catch (error) {\n const parseError = new Error(\n `Failed to parse WebSocket message: ${error instanceof Error ? error.message : String(error)}`,\n );\n this.handleNonFatalError(parseError);\n }\n };\n\n this.webSocket.onerror = () => {\n this.logger.error(\"WebSocket error occurred\");\n const error = new Error(\"WebSocket error occurred\");\n this.handleFatalError(error);\n };\n\n this.webSocket.onclose = (event) => {\n if (this.isRunning) {\n if (event.code === 1008) {\n this.logger.error(\"WebSocket authentication failed\");\n const error = new Error(\n \"WebSocket authentication failed: Invalid or revoked API key\",\n );\n this.handleFatalError(error);\n } else {\n this.logger.warn(\"WebSocket closed unexpectedly:\", event.code);\n const error = new Error(\"WebSocket closed unexpectedly\");\n this.handleFatalError(error);\n }\n } else {\n this.logger.debug(\"WebSocket closed\");\n }\n };\n }\n\n /**\n * Handle non-fatal errors (report but don't stop stream)\n */\n private handleNonFatalError(error: Error): void {\n this.logger.warn(\"Non-fatal error:\", error.message);\n if (this.config.onError) {\n this.config.onError(error);\n }\n }\n\n /**\n * Handle fatal errors (stop stream and report)\n */\n private async handleFatalError(error: unknown): Promise<void> {\n this.logger.error(\"Fatal error:\", error);\n await this.cleanup();\n this.isRunning = false;\n\n const normalizedError =\n error instanceof Error ? error : new Error(String(error));\n\n if (this.config.onError) {\n this.config.onError(normalizedError);\n }\n }\n\n /**\n * Update the prompt/task while stream is running\n */\n async updatePrompt(prompt: string): Promise<void> {\n if (!this.isRunning || !this.streamId) {\n throw new Error(\"Vision stream not running\");\n }\n\n if (!prompt || typeof prompt !== \"string\") {\n throw new ValidationError(\"prompt must be a non-empty string\");\n }\n\n this.logger.debug(\"Updating prompt\");\n await this.client.updatePrompt(this.streamId, prompt);\n this.logger.info(\"Prompt updated\");\n }\n\n /**\n * Stop the vision stream and clean up resources\n */\n async stop(): Promise<void> {\n this.logger.info(\"Stopping stream\");\n await this.cleanup();\n this.isRunning = false;\n }\n\n /**\n * Submit feedback for the stream\n */\n async submitFeedback(feedback: {\n rating: number;\n category: string;\n feedback?: string;\n }): Promise<void> {\n if (!this.streamId) {\n throw new Error(\"No active stream\");\n }\n\n if (\n feedback.rating < CONSTRAINTS.RATING.min ||\n feedback.rating > CONSTRAINTS.RATING.max\n ) {\n throw new ValidationError(\n `rating must be between ${CONSTRAINTS.RATING.min} and ${CONSTRAINTS.RATING.max}`,\n );\n }\n\n if (!feedback.category || typeof feedback.category !== \"string\") {\n throw new ValidationError(\"category must be a non-empty string\");\n }\n\n this.logger.debug(\"Submitting feedback\");\n await this.client.submitFeedback(this.streamId, {\n rating: feedback.rating,\n category: feedback.category,\n feedback: feedback.feedback ?? \"\",\n });\n this.logger.info(\"Feedback submitted\");\n }\n\n /**\n * Get the current stream ID\n */\n getStreamId(): string | null {\n return this.streamId;\n }\n\n /**\n * Get the media stream (for displaying video preview)\n */\n getMediaStream(): MediaStream | null {\n return this.mediaStream;\n }\n\n /**\n * Check if the stream is running\n */\n isActive(): boolean {\n return this.isRunning;\n }\n\n private async cleanup(): Promise<void> {\n this.logger.debug(\"Cleaning up resources\");\n\n if (this.keepaliveInterval) {\n window.clearInterval(this.keepaliveInterval);\n this.keepaliveInterval = null;\n }\n\n if (this.webSocket) {\n this.webSocket.close();\n this.webSocket = null;\n }\n\n if (this.peerConnection) {\n this.peerConnection.close();\n this.peerConnection = null;\n }\n\n if (this.mediaStream) {\n this.mediaStream.getTracks().forEach((track) => track.stop());\n this.mediaStream = null;\n }\n\n if (this.videoElement) {\n this.videoElement.pause();\n URL.revokeObjectURL(this.videoElement.src);\n this.videoElement.remove();\n this.videoElement = null;\n }\n\n this.streamId = null;\n this.logger.debug(\"Cleanup complete\");\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/client/errors.ts","../src/client/client.ts","../src/client/RealtimeVision.ts"],"names":["ValidationError"],"mappings":"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","file":"index.js","sourcesContent":["export class ApiError extends Error {\n readonly statusCode?: number;\n readonly requestId?: string;\n readonly details?: any;\n\n constructor(\n message: string,\n statusCode?: number,\n requestId?: string,\n details?: any,\n ) {\n super(message);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.requestId = requestId;\n this.details = details;\n }\n}\n\nexport class UnauthorizedError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 401, requestId);\n this.name = \"UnauthorizedError\";\n }\n}\n\nexport class ValidationError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 422, requestId, details);\n this.name = \"ValidationError\";\n }\n}\n\nexport class NotFoundError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 404, requestId);\n this.name = \"NotFoundError\";\n }\n}\n\nexport class NetworkError extends ApiError {\n readonly cause?: Error;\n\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"NetworkError\";\n this.cause = cause;\n }\n}\n\nexport class ServerError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 500, requestId, details);\n this.name = \"ServerError\";\n }\n}\n","import type {\n StreamCreateRequest,\n StreamCreateResponse,\n KeepaliveResponse,\n StreamConfigResponse,\n ErrorResponse,\n} from \"./types\";\nimport {\n ApiError,\n ValidationError,\n NotFoundError,\n NetworkError,\n ServerError,\n UnauthorizedError,\n} from \"./errors\";\n\ntype ClientConfig = {\n baseUrl: string;\n apiKey: string;\n};\n\nexport class StreamClient {\n private baseUrl: string;\n private apiKey: string;\n\n constructor(config: ClientConfig) {\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new Error(\"apiKey is required and must be a string\");\n }\n\n this.baseUrl = config.baseUrl;\n this.apiKey = config.apiKey;\n }\n\n private async request<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n credentials: \"include\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const errorData: ErrorResponse = await response.json().catch(() => ({\n error: \"unknown_error\",\n message: response.statusText,\n }));\n\n const message = errorData.message || errorData.error;\n\n if (response.status === 401) {\n throw new UnauthorizedError(\n message || \"Invalid or revoked API key\",\n errorData.request_id,\n );\n }\n if (response.status === 422 || response.status === 400) {\n throw new ValidationError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n if (response.status === 404) {\n throw new NotFoundError(message, errorData.request_id);\n }\n if (response.status >= 500) {\n throw new ServerError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n\n throw new ApiError(\n message,\n response.status,\n errorData.request_id,\n errorData.details,\n );\n }\n\n return await response.json();\n } catch (error) {\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n throw new NetworkError(`Network error: ${error.message}`, error);\n }\n\n throw new NetworkError(\"Unknown network error\");\n }\n }\n async createStream(\n request: StreamCreateRequest,\n ): Promise<StreamCreateResponse> {\n return this.request<StreamCreateResponse>(\"/streams\", {\n method: \"POST\",\n body: JSON.stringify(request),\n });\n }\n\n async renewLease(streamId: string): Promise<KeepaliveResponse> {\n return this.request<KeepaliveResponse>(`/streams/${streamId}/keepalive`, {\n method: \"POST\",\n });\n }\n\n async updatePrompt(\n streamId: string,\n prompt: string,\n ): Promise<StreamConfigResponse> {\n return this.request<StreamConfigResponse>(\n `/streams/${streamId}/config/prompt`,\n {\n method: \"PATCH\",\n body: JSON.stringify({ prompt }),\n },\n );\n }\n\n connectWebSocket(streamId: string): WebSocket {\n const wsUrl = this.baseUrl\n .replace(\"http://\", \"ws://\")\n .replace(\"https://\", \"wss://\");\n return new WebSocket(`${wsUrl}/ws/streams/${streamId}`);\n }\n\n /**\n * Health check endpoint (for testing, uses internal port if available)\n * Note: This endpoint may not be available via the main API\n */\n async healthCheck(): Promise<string> {\n const url = `${this.baseUrl}/healthz`;\n const response = await fetch(url, {\n credentials: \"include\",\n });\n return response.text();\n }\n}\n","import { StreamClient } from \"./client\";\n\nimport {\n type StreamInferenceResult,\n type StreamProcessingConfig,\n type StreamSource,\n} from \"./types\";\n\n/**\n * Default configuration values for RealtimeVision\n */\nconst DEFAULTS = {\n BACKEND: \"overshoot\" as const,\n MODEL: \"Qwen/Qwen3-VL-30B-A3B-Instruct\",\n SOURCE: { type: \"camera\", cameraFacing: \"environment\" } as const,\n SAMPLING_RATIO: 0.1,\n CLIP_LENGTH_SECONDS: 1.0,\n DELAY_SECONDS: 1.0,\n FALLBACK_FPS: 30,\n ICE_SERVERS: [\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n ] as RTCIceServer[],\n} as const;\n\n/**\n * Validation constraints\n */\nconst CONSTRAINTS = {\n SAMPLING_RATIO: { min: 0, max: 1 },\n FPS: { min: 1, max: 120 },\n CLIP_LENGTH_SECONDS: { min: 0.1, max: 60 },\n DELAY_SECONDS: { min: 0, max: 60 },\n} as const;\n\n/**\n * Logger utility for controlled logging\n */\nclass Logger {\n private debugEnabled: boolean;\n\n constructor(debugEnabled: boolean = false) {\n this.debugEnabled = debugEnabled;\n }\n\n debug(...args: any[]): void {\n if (this.debugEnabled) {\n console.log(\"[RealtimeVision Debug]\", ...args);\n }\n }\n\n info(...args: any[]): void {\n console.log(\"[RealtimeVision]\", ...args);\n }\n\n warn(...args: any[]): void {\n console.warn(\"[RealtimeVision]\", ...args);\n }\n\n error(...args: any[]): void {\n console.error(\"[RealtimeVision]\", ...args);\n }\n}\n\nexport interface RealtimeVisionConfig {\n /**\n * Base URL for the API (e.g., \"https://api.example.com\")\n */\n apiUrl: string;\n\n /**\n * API key for authentication\n * Required for all API requests\n */\n apiKey: string;\n\n /**\n * The prompt/task to run on window segments of the stream.\n * This runs continuously (at a defined window interval).\n *\n * Examples:\n * - \"Read any visible text\"\n * - \"Detect objects and return as JSON array\"\n * - \"Describe facial expression\"\n */\n prompt: string;\n\n /**\n * Video source configuration\n * Defaults to camera with environment facing if not specified\n */\n source?: StreamSource;\n\n /**\n * Model backend to use\n */\n backend?: \"overshoot\";\n\n /**\n * Model name to use for inference\n */\n model?: string;\n\n /**\n * Optional JSON schema for structured output\n */\n outputSchema?: Record<string, any>;\n\n /**\n * Called when a new inference result arrives (~1 per second)\n */\n onResult: (result: StreamInferenceResult) => void;\n\n /**\n * Called when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Custom processing configuration\n * All fields are optional and will use defaults if not provided\n */\n processing?: {\n /**\n * Sampling ratio (0-1). Controls what fraction of frames are processed.\n */\n sampling_ratio?: number;\n /**\n * Frames per second (1-120)\n */\n fps?: number;\n /**\n * Clip length in seconds (0.1-60)\n */\n clip_length_seconds?: number;\n /**\n * Delay in seconds (0-60)\n */\n delay_seconds?: number;\n };\n\n /**\n * ICE servers for WebRTC connection\n * If not provided, uses default TURN servers\n */\n iceServers?: RTCIceServer[];\n\n /**\n * Enable debug logging\n * @default false\n */\n debug?: boolean;\n}\n\nclass ValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ValidationError\";\n }\n}\n\nexport class RealtimeVision {\n private config: RealtimeVisionConfig;\n private client: StreamClient;\n private logger: Logger;\n\n private mediaStream: MediaStream | null = null;\n private peerConnection: RTCPeerConnection | null = null;\n private webSocket: WebSocket | null = null;\n private streamId: string | null = null;\n private keepaliveInterval: number | null = null;\n private videoElement: HTMLVideoElement | null = null;\n private canvasElement: HTMLCanvasElement | null = null;\n private canvasAnimationFrameId: number | null = null;\n\n private isRunning = false;\n\n constructor(config: RealtimeVisionConfig) {\n this.validateConfig(config);\n this.config = config;\n this.logger = new Logger(config.debug ?? false);\n this.client = new StreamClient({\n baseUrl: config.apiUrl,\n apiKey: config.apiKey,\n });\n }\n\n /**\n * Validate configuration values\n */\n private validateConfig(config: RealtimeVisionConfig): void {\n if (!config.apiUrl || typeof config.apiUrl !== \"string\") {\n throw new ValidationError(\"apiUrl is required and must be a string\");\n }\n\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new ValidationError(\"apiKey is required and must be a string\");\n }\n\n if (!config.prompt || typeof config.prompt !== \"string\") {\n throw new ValidationError(\"prompt is required and must be a string\");\n }\n\n if (config.source) {\n if (config.source.type === \"camera\") {\n if (\n config.source.cameraFacing !== \"user\" &&\n config.source.cameraFacing !== \"environment\"\n ) {\n throw new ValidationError(\n 'cameraFacing must be \"user\" or \"environment\"',\n );\n }\n } else if (config.source.type === \"video\") {\n if (!(config.source.file instanceof File)) {\n throw new ValidationError(\"video source must provide a File object\");\n }\n } else {\n throw new ValidationError('source.type must be \"camera\" or \"video\"');\n }\n }\n\n if (config.processing?.sampling_ratio !== undefined) {\n const ratio = config.processing.sampling_ratio;\n if (\n ratio < CONSTRAINTS.SAMPLING_RATIO.min ||\n ratio > CONSTRAINTS.SAMPLING_RATIO.max\n ) {\n throw new ValidationError(\n `sampling_ratio must be between ${CONSTRAINTS.SAMPLING_RATIO.min} and ${CONSTRAINTS.SAMPLING_RATIO.max}`,\n );\n }\n }\n\n if (config.processing?.fps !== undefined) {\n const fps = config.processing.fps;\n if (fps < CONSTRAINTS.FPS.min || fps > CONSTRAINTS.FPS.max) {\n throw new ValidationError(\n `fps must be between ${CONSTRAINTS.FPS.min} and ${CONSTRAINTS.FPS.max}`,\n );\n }\n }\n\n if (config.processing?.clip_length_seconds !== undefined) {\n const clip = config.processing.clip_length_seconds;\n if (\n clip < CONSTRAINTS.CLIP_LENGTH_SECONDS.min ||\n clip > CONSTRAINTS.CLIP_LENGTH_SECONDS.max\n ) {\n throw new ValidationError(\n `clip_length_seconds must be between ${CONSTRAINTS.CLIP_LENGTH_SECONDS.min} and ${CONSTRAINTS.CLIP_LENGTH_SECONDS.max}`,\n );\n }\n }\n\n if (config.processing?.delay_seconds !== undefined) {\n const delay = config.processing.delay_seconds;\n if (\n delay < CONSTRAINTS.DELAY_SECONDS.min ||\n delay > CONSTRAINTS.DELAY_SECONDS.max\n ) {\n throw new ValidationError(\n `delay_seconds must be between ${CONSTRAINTS.DELAY_SECONDS.min} and ${CONSTRAINTS.DELAY_SECONDS.max}`,\n );\n }\n }\n }\n\n /**\n * Create media stream from the configured source\n */\n private async createMediaStream(source: StreamSource): Promise<MediaStream> {\n this.logger.debug(\"Creating media stream from source:\", source.type);\n\n switch (source.type) {\n case \"camera\":\n return await navigator.mediaDevices.getUserMedia({\n video: { facingMode: { ideal: source.cameraFacing } },\n audio: false,\n });\n\n case \"video\":\n const video = document.createElement(\"video\");\n video.src = URL.createObjectURL(source.file);\n video.muted = true;\n video.loop = true;\n video.playsInline = true;\n\n this.logger.debug(\"Loading video file:\", source.file.name);\n\n // Wait for video to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(\"Video loading timeout after 10 seconds\"));\n }, 10000);\n\n video.onloadedmetadata = () => {\n clearTimeout(timeout);\n this.logger.debug(\"Video metadata loaded\");\n resolve();\n };\n\n video.onerror = (e) => {\n clearTimeout(timeout);\n this.logger.error(\"Video loading error:\", e);\n reject(new Error(\"Failed to load video file\"));\n };\n\n if (video.readyState >= 1) {\n clearTimeout(timeout);\n resolve();\n }\n });\n\n await video.play();\n this.logger.debug(\"Video playback started\");\n\n let stream: MediaStream;\n\n // Check if captureStream is supported (Chrome, Firefox)\n if (typeof video.captureStream === \"function\") {\n this.logger.debug(\"Using native video.captureStream()\");\n stream = video.captureStream();\n } else {\n // Safari fallback: use canvas to capture the video stream\n this.logger.debug(\n \"captureStream not supported, using canvas fallback for Safari\",\n );\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = video.videoWidth || 640;\n canvas.height = video.videoHeight || 480;\n const ctx = canvas.getContext(\"2d\");\n\n if (!ctx) {\n throw new Error(\"Failed to get canvas 2D context\");\n }\n\n // Draw video frames to canvas continuously\n const drawFrame = () => {\n if (!video.paused && !video.ended) {\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n this.canvasAnimationFrameId = requestAnimationFrame(drawFrame);\n }\n };\n drawFrame();\n\n // Capture stream from canvas (30 fps)\n stream = canvas.captureStream(30);\n this.canvasElement = canvas;\n }\n\n if (!stream) {\n throw new Error(\"Failed to capture video stream\");\n }\n\n const videoTracks = stream.getVideoTracks();\n if (videoTracks.length === 0) {\n throw new Error(\"Video stream has no video tracks\");\n }\n\n this.videoElement = video;\n return stream;\n\n default:\n throw new Error(`Unknown source type: ${(source as any).type}`);\n }\n }\n\n /**\n * Get FPS from media stream\n */\n private async getStreamFps(\n stream: MediaStream | null,\n source: StreamSource,\n ): Promise<number> {\n if (!stream) {\n this.logger.warn(\"Stream is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTracks = stream.getVideoTracks();\n if (!videoTracks || videoTracks.length === 0) {\n this.logger.warn(\"No video tracks found, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTrack = videoTracks[0];\n if (!videoTrack) {\n this.logger.warn(\"First video track is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n // For camera sources, get FPS from track settings\n if (source.type === \"camera\") {\n const settings = videoTrack.getSettings();\n const fps = settings.frameRate ?? DEFAULTS.FALLBACK_FPS;\n this.logger.debug(\"Detected camera FPS:\", fps);\n return fps;\n }\n\n // For video file sources, try to get FPS from video element\n if (source.type === \"video\" && this.videoElement) {\n await new Promise<void>((resolve, reject) => {\n if (this.videoElement!.readyState >= 1) {\n resolve();\n } else {\n this.videoElement!.onloadedmetadata = () => resolve();\n this.videoElement!.onerror = () =>\n reject(new Error(\"Failed to load video metadata\"));\n }\n });\n\n // For video files, use fallback FPS or user-specified config\n this.logger.debug(\"Using fallback FPS for video file\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n return DEFAULTS.FALLBACK_FPS;\n }\n\n /**\n * Get processing configuration with defaults applied\n */\n private getProcessingConfig(detectedFps: number): StreamProcessingConfig {\n const userProcessing = this.config.processing || {};\n\n return {\n sampling_ratio: userProcessing.sampling_ratio ?? DEFAULTS.SAMPLING_RATIO,\n fps: userProcessing.fps ?? detectedFps,\n clip_length_seconds:\n userProcessing.clip_length_seconds ?? DEFAULTS.CLIP_LENGTH_SECONDS,\n delay_seconds: userProcessing.delay_seconds ?? DEFAULTS.DELAY_SECONDS,\n };\n }\n\n /**\n * Get the effective source configuration\n */\n private getSource(): StreamSource {\n return this.config.source ?? DEFAULTS.SOURCE;\n }\n\n /**\n * Start the vision stream\n */\n async start(): Promise<void> {\n if (this.isRunning) {\n throw new Error(\"Vision stream already running\");\n }\n\n try {\n const source = this.getSource();\n this.logger.debug(\"Starting stream with source type:\", source.type);\n\n if (source.type === \"video\") {\n this.logger.debug(\"Video file:\", {\n name: source.file.name,\n size: source.file.size,\n type: source.file.type,\n });\n\n if (!source.file || !(source.file instanceof File)) {\n throw new Error(\"Invalid video file\");\n }\n }\n\n // Create media stream\n this.mediaStream = await this.createMediaStream(source);\n const videoTrack = this.mediaStream.getVideoTracks()[0];\n if (!videoTrack) {\n throw new Error(\"No video track available\");\n }\n\n // Get FPS for the stream\n const detectedFps = await this.getStreamFps(this.mediaStream, source);\n\n // Set up WebRTC peer connection\n const iceServers = this.config.iceServers ?? DEFAULTS.ICE_SERVERS;\n this.logger.debug(\"Creating peer connection with ICE servers\");\n this.peerConnection = new RTCPeerConnection({ iceServers });\n\n // Set up ICE logging\n this.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n this.logger.debug(\"ICE candidate:\", {\n type: event.candidate.type,\n protocol: event.candidate.protocol,\n });\n } else {\n this.logger.debug(\"ICE gathering complete\");\n }\n };\n\n this.peerConnection.oniceconnectionstatechange = () => {\n this.logger.debug(\n \"ICE connection state:\",\n this.peerConnection?.iceConnectionState,\n );\n };\n\n this.peerConnection.addTrack(videoTrack, this.mediaStream);\n\n // Create and set local offer\n const offer = await this.peerConnection.createOffer();\n await this.peerConnection.setLocalDescription(offer);\n\n if (!this.peerConnection.localDescription) {\n throw new Error(\"Failed to create local description\");\n }\n\n // Create stream on server\n this.logger.debug(\"Creating stream on server\");\n const response = await this.client.createStream({\n webrtc: {\n type: \"offer\",\n sdp: this.peerConnection.localDescription.sdp,\n },\n processing: this.getProcessingConfig(detectedFps),\n inference: {\n prompt: this.config.prompt,\n backend: this.config.backend ?? DEFAULTS.BACKEND,\n model: this.config.model ?? DEFAULTS.MODEL,\n output_schema_json: this.config.outputSchema,\n },\n });\n\n this.logger.debug(\"Backend response received:\", {\n stream_id: response.stream_id,\n has_turn_servers: !!response.turn_servers,\n });\n\n // Set remote description\n await this.peerConnection.setRemoteDescription(response.webrtc);\n\n this.streamId = response.stream_id;\n this.logger.info(\"Stream started:\", this.streamId);\n\n // Set up keepalive\n this.setupKeepalive(response.lease?.ttl_seconds);\n\n // Connect WebSocket for results\n this.setupWebSocket(response.stream_id);\n\n this.isRunning = true;\n } catch (error) {\n await this.handleFatalError(error);\n throw error;\n }\n }\n\n /**\n * Set up keepalive interval with error handling\n */\n private setupKeepalive(ttlSeconds: number | undefined): void {\n if (!ttlSeconds) {\n return;\n }\n\n const intervalMs = (ttlSeconds / 2) * 1000;\n this.logger.debug(\"Setting up keepalive with interval:\", intervalMs, \"ms\");\n\n this.keepaliveInterval = window.setInterval(async () => {\n try {\n if (this.streamId) {\n await this.client.renewLease(this.streamId);\n this.logger.debug(\"Lease renewed\");\n }\n } catch (error) {\n this.logger.error(\"Keepalive failed:\", error);\n const keepaliveError = new Error(\n `Keepalive failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n await this.handleFatalError(keepaliveError);\n }\n }, intervalMs);\n }\n\n /**\n * Set up WebSocket connection with error handling\n */\n private setupWebSocket(streamId: string): void {\n this.logger.debug(\"Connecting WebSocket for stream:\", streamId);\n this.webSocket = this.client.connectWebSocket(streamId);\n\n this.webSocket.onopen = () => {\n this.logger.debug(\"WebSocket connected\");\n if (this.webSocket) {\n this.webSocket.send(JSON.stringify({ api_key: this.config.apiKey }));\n }\n };\n\n this.webSocket.onmessage = (event) => {\n try {\n const result: StreamInferenceResult = JSON.parse(event.data);\n this.config.onResult(result);\n } catch (error) {\n const parseError = new Error(\n `Failed to parse WebSocket message: ${error instanceof Error ? error.message : String(error)}`,\n );\n this.handleNonFatalError(parseError);\n }\n };\n\n this.webSocket.onerror = () => {\n this.logger.error(\"WebSocket error occurred\");\n const error = new Error(\"WebSocket error occurred\");\n this.handleFatalError(error);\n };\n\n this.webSocket.onclose = (event) => {\n if (this.isRunning) {\n if (event.code === 1008) {\n this.logger.error(\"WebSocket authentication failed\");\n const error = new Error(\n \"WebSocket authentication failed: Invalid or revoked API key\",\n );\n this.handleFatalError(error);\n } else {\n this.logger.warn(\"WebSocket closed unexpectedly:\", event.code);\n const error = new Error(\"WebSocket closed unexpectedly\");\n this.handleFatalError(error);\n }\n } else {\n this.logger.debug(\"WebSocket closed\");\n }\n };\n }\n\n /**\n * Handle non-fatal errors (report but don't stop stream)\n */\n private handleNonFatalError(error: Error): void {\n this.logger.warn(\"Non-fatal error:\", error.message);\n if (this.config.onError) {\n this.config.onError(error);\n }\n }\n\n /**\n * Handle fatal errors (stop stream and report)\n */\n private async handleFatalError(error: unknown): Promise<void> {\n this.logger.error(\"Fatal error:\", error);\n await this.cleanup();\n this.isRunning = false;\n\n const normalizedError =\n error instanceof Error ? error : new Error(String(error));\n\n if (this.config.onError) {\n this.config.onError(normalizedError);\n }\n }\n\n /**\n * Update the prompt/task while stream is running\n */\n async updatePrompt(prompt: string): Promise<void> {\n if (!this.isRunning || !this.streamId) {\n throw new Error(\"Vision stream not running\");\n }\n\n if (!prompt || typeof prompt !== \"string\") {\n throw new ValidationError(\"prompt must be a non-empty string\");\n }\n\n this.logger.debug(\"Updating prompt\");\n await this.client.updatePrompt(this.streamId, prompt);\n this.logger.info(\"Prompt updated\");\n }\n\n /**\n * Stop the vision stream and clean up resources\n */\n async stop(): Promise<void> {\n this.logger.info(\"Stopping stream\");\n await this.cleanup();\n this.isRunning = false;\n }\n\n /**\n * Get the current stream ID\n */\n getStreamId(): string | null {\n return this.streamId;\n }\n\n /**\n * Get the media stream (for displaying video preview)\n */\n getMediaStream(): MediaStream | null {\n return this.mediaStream;\n }\n\n /**\n * Check if the stream is running\n */\n isActive(): boolean {\n return this.isRunning;\n }\n\n private async cleanup(): Promise<void> {\n this.logger.debug(\"Cleaning up resources\");\n\n if (this.keepaliveInterval) {\n window.clearInterval(this.keepaliveInterval);\n this.keepaliveInterval = null;\n }\n\n if (this.webSocket) {\n this.webSocket.close();\n this.webSocket = null;\n }\n\n if (this.peerConnection) {\n this.peerConnection.close();\n this.peerConnection = null;\n }\n\n if (this.mediaStream) {\n this.mediaStream.getTracks().forEach((track) => track.stop());\n this.mediaStream = null;\n }\n\n if (this.canvasAnimationFrameId) {\n cancelAnimationFrame(this.canvasAnimationFrameId);\n this.canvasAnimationFrameId = null;\n }\n\n if (this.canvasElement) {\n this.canvasElement.remove();\n this.canvasElement = null;\n }\n\n if (this.videoElement) {\n this.videoElement.pause();\n URL.revokeObjectURL(this.videoElement.src);\n this.videoElement.remove();\n this.videoElement = null;\n }\n\n this.streamId = null;\n this.logger.debug(\"Cleanup complete\");\n }\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -130,17 +130,6 @@ var StreamClient = class {
|
|
|
130
130
|
}
|
|
131
131
|
);
|
|
132
132
|
}
|
|
133
|
-
async submitFeedback(streamId, feedback) {
|
|
134
|
-
return this.request(`/streams/${streamId}/feedback`, {
|
|
135
|
-
method: "POST",
|
|
136
|
-
body: JSON.stringify(feedback)
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
async getAllFeedback() {
|
|
140
|
-
return this.request("/streams/feedback", {
|
|
141
|
-
method: "GET"
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
133
|
connectWebSocket(streamId) {
|
|
145
134
|
const wsUrl = this.baseUrl.replace("http://", "ws://").replace("https://", "wss://");
|
|
146
135
|
return new WebSocket(`${wsUrl}/ws/streams/${streamId}`);
|
|
@@ -190,13 +179,11 @@ var DEFAULTS = {
|
|
|
190
179
|
}
|
|
191
180
|
]
|
|
192
181
|
};
|
|
193
|
-
console.log("defaults", DEFAULTS);
|
|
194
182
|
var CONSTRAINTS = {
|
|
195
183
|
SAMPLING_RATIO: { min: 0, max: 1 },
|
|
196
184
|
FPS: { min: 1, max: 120 },
|
|
197
185
|
CLIP_LENGTH_SECONDS: { min: 0.1, max: 60 },
|
|
198
|
-
DELAY_SECONDS: { min: 0, max: 60 }
|
|
199
|
-
RATING: { min: 1, max: 5 }
|
|
186
|
+
DELAY_SECONDS: { min: 0, max: 60 }
|
|
200
187
|
};
|
|
201
188
|
var Logger = class {
|
|
202
189
|
constructor(debugEnabled = false) {
|
|
@@ -231,6 +218,8 @@ var RealtimeVision = class {
|
|
|
231
218
|
this.streamId = null;
|
|
232
219
|
this.keepaliveInterval = null;
|
|
233
220
|
this.videoElement = null;
|
|
221
|
+
this.canvasElement = null;
|
|
222
|
+
this.canvasAnimationFrameId = null;
|
|
234
223
|
this.isRunning = false;
|
|
235
224
|
this.validateConfig(config);
|
|
236
225
|
this.config = config;
|
|
@@ -340,7 +329,31 @@ var RealtimeVision = class {
|
|
|
340
329
|
});
|
|
341
330
|
await video.play();
|
|
342
331
|
this.logger.debug("Video playback started");
|
|
343
|
-
|
|
332
|
+
let stream;
|
|
333
|
+
if (typeof video.captureStream === "function") {
|
|
334
|
+
this.logger.debug("Using native video.captureStream()");
|
|
335
|
+
stream = video.captureStream();
|
|
336
|
+
} else {
|
|
337
|
+
this.logger.debug(
|
|
338
|
+
"captureStream not supported, using canvas fallback for Safari"
|
|
339
|
+
);
|
|
340
|
+
const canvas = document.createElement("canvas");
|
|
341
|
+
canvas.width = video.videoWidth || 640;
|
|
342
|
+
canvas.height = video.videoHeight || 480;
|
|
343
|
+
const ctx = canvas.getContext("2d");
|
|
344
|
+
if (!ctx) {
|
|
345
|
+
throw new Error("Failed to get canvas 2D context");
|
|
346
|
+
}
|
|
347
|
+
const drawFrame = () => {
|
|
348
|
+
if (!video.paused && !video.ended) {
|
|
349
|
+
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
350
|
+
this.canvasAnimationFrameId = requestAnimationFrame(drawFrame);
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
drawFrame();
|
|
354
|
+
stream = canvas.captureStream(30);
|
|
355
|
+
this.canvasElement = canvas;
|
|
356
|
+
}
|
|
344
357
|
if (!stream) {
|
|
345
358
|
throw new Error("Failed to capture video stream");
|
|
346
359
|
}
|
|
@@ -603,29 +616,6 @@ var RealtimeVision = class {
|
|
|
603
616
|
await this.cleanup();
|
|
604
617
|
this.isRunning = false;
|
|
605
618
|
}
|
|
606
|
-
/**
|
|
607
|
-
* Submit feedback for the stream
|
|
608
|
-
*/
|
|
609
|
-
async submitFeedback(feedback) {
|
|
610
|
-
if (!this.streamId) {
|
|
611
|
-
throw new Error("No active stream");
|
|
612
|
-
}
|
|
613
|
-
if (feedback.rating < CONSTRAINTS.RATING.min || feedback.rating > CONSTRAINTS.RATING.max) {
|
|
614
|
-
throw new ValidationError2(
|
|
615
|
-
`rating must be between ${CONSTRAINTS.RATING.min} and ${CONSTRAINTS.RATING.max}`
|
|
616
|
-
);
|
|
617
|
-
}
|
|
618
|
-
if (!feedback.category || typeof feedback.category !== "string") {
|
|
619
|
-
throw new ValidationError2("category must be a non-empty string");
|
|
620
|
-
}
|
|
621
|
-
this.logger.debug("Submitting feedback");
|
|
622
|
-
await this.client.submitFeedback(this.streamId, {
|
|
623
|
-
rating: feedback.rating,
|
|
624
|
-
category: feedback.category,
|
|
625
|
-
feedback: feedback.feedback ?? ""
|
|
626
|
-
});
|
|
627
|
-
this.logger.info("Feedback submitted");
|
|
628
|
-
}
|
|
629
619
|
/**
|
|
630
620
|
* Get the current stream ID
|
|
631
621
|
*/
|
|
@@ -662,6 +652,14 @@ var RealtimeVision = class {
|
|
|
662
652
|
this.mediaStream.getTracks().forEach((track) => track.stop());
|
|
663
653
|
this.mediaStream = null;
|
|
664
654
|
}
|
|
655
|
+
if (this.canvasAnimationFrameId) {
|
|
656
|
+
cancelAnimationFrame(this.canvasAnimationFrameId);
|
|
657
|
+
this.canvasAnimationFrameId = null;
|
|
658
|
+
}
|
|
659
|
+
if (this.canvasElement) {
|
|
660
|
+
this.canvasElement.remove();
|
|
661
|
+
this.canvasElement = null;
|
|
662
|
+
}
|
|
665
663
|
if (this.videoElement) {
|
|
666
664
|
this.videoElement.pause();
|
|
667
665
|
URL.revokeObjectURL(this.videoElement.src);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/errors.ts","../src/client/client.ts","../src/client/RealtimeVision.ts"],"names":["ValidationError"],"mappings":"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","file":"index.mjs","sourcesContent":["export class ApiError extends Error {\n readonly statusCode?: number;\n readonly requestId?: string;\n readonly details?: any;\n\n constructor(\n message: string,\n statusCode?: number,\n requestId?: string,\n details?: any,\n ) {\n super(message);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.requestId = requestId;\n this.details = details;\n }\n}\n\nexport class UnauthorizedError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 401, requestId);\n this.name = \"UnauthorizedError\";\n }\n}\n\nexport class ValidationError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 422, requestId, details);\n this.name = \"ValidationError\";\n }\n}\n\nexport class NotFoundError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 404, requestId);\n this.name = \"NotFoundError\";\n }\n}\n\nexport class NetworkError extends ApiError {\n readonly cause?: Error;\n\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"NetworkError\";\n this.cause = cause;\n }\n}\n\nexport class ServerError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 500, requestId, details);\n this.name = \"ServerError\";\n }\n}\n","import type {\n StreamCreateRequest,\n StreamCreateResponse,\n KeepaliveResponse,\n StreamConfigResponse,\n FeedbackCreateRequest,\n FeedbackResponse,\n StatusResponse,\n ErrorResponse,\n} from \"./types\";\nimport {\n ApiError,\n ValidationError,\n NotFoundError,\n NetworkError,\n ServerError,\n UnauthorizedError,\n} from \"./errors\";\n\ntype ClientConfig = {\n baseUrl: string;\n apiKey: string;\n};\n\nexport class StreamClient {\n private baseUrl: string;\n private apiKey: string;\n\n constructor(config: ClientConfig) {\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new Error(\"apiKey is required and must be a string\");\n }\n\n this.baseUrl = config.baseUrl;\n this.apiKey = config.apiKey;\n }\n\n private async request<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n credentials: \"include\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const errorData: ErrorResponse = await response.json().catch(() => ({\n error: \"unknown_error\",\n message: response.statusText,\n }));\n\n const message = errorData.message || errorData.error;\n\n if (response.status === 401) {\n throw new UnauthorizedError(\n message || \"Invalid or revoked API key\",\n errorData.request_id,\n );\n }\n if (response.status === 422 || response.status === 400) {\n throw new ValidationError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n if (response.status === 404) {\n throw new NotFoundError(message, errorData.request_id);\n }\n if (response.status >= 500) {\n throw new ServerError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n\n throw new ApiError(\n message,\n response.status,\n errorData.request_id,\n errorData.details,\n );\n }\n\n return await response.json();\n } catch (error) {\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n throw new NetworkError(`Network error: ${error.message}`, error);\n }\n\n throw new NetworkError(\"Unknown network error\");\n }\n }\n async createStream(\n request: StreamCreateRequest,\n ): Promise<StreamCreateResponse> {\n return this.request<StreamCreateResponse>(\"/streams\", {\n method: \"POST\",\n body: JSON.stringify(request),\n });\n }\n\n async renewLease(streamId: string): Promise<KeepaliveResponse> {\n return this.request<KeepaliveResponse>(`/streams/${streamId}/keepalive`, {\n method: \"POST\",\n });\n }\n\n async updatePrompt(\n streamId: string,\n prompt: string,\n ): Promise<StreamConfigResponse> {\n return this.request<StreamConfigResponse>(\n `/streams/${streamId}/config/prompt`,\n {\n method: \"PATCH\",\n body: JSON.stringify({ prompt }),\n },\n );\n }\n\n async submitFeedback(\n streamId: string,\n feedback: FeedbackCreateRequest,\n ): Promise<StatusResponse> {\n return this.request<StatusResponse>(`/streams/${streamId}/feedback`, {\n method: \"POST\",\n body: JSON.stringify(feedback),\n });\n }\n\n async getAllFeedback(): Promise<FeedbackResponse[]> {\n return this.request<FeedbackResponse[]>(\"/streams/feedback\", {\n method: \"GET\",\n });\n }\n\n connectWebSocket(streamId: string): WebSocket {\n const wsUrl = this.baseUrl\n .replace(\"http://\", \"ws://\")\n .replace(\"https://\", \"wss://\");\n return new WebSocket(`${wsUrl}/ws/streams/${streamId}`);\n }\n\n /**\n * Health check endpoint (for testing, uses internal port if available)\n * Note: This endpoint may not be available via the main API\n */\n async healthCheck(): Promise<string> {\n const url = `${this.baseUrl}/healthz`;\n const response = await fetch(url, {\n credentials: \"include\",\n });\n return response.text();\n }\n}\n","import { StreamClient } from \"./client\";\n\nimport {\n type StreamInferenceResult,\n type StreamProcessingConfig,\n type StreamSource,\n} from \"./types\";\n\n/**\n * Default configuration values for RealtimeVision\n */\nconst DEFAULTS = {\n BACKEND: \"overshoot\" as const,\n MODEL: \"Qwen/Qwen3-VL-30B-A3B-Instruct\",\n SOURCE: { type: \"camera\", cameraFacing: \"environment\" } as const,\n SAMPLING_RATIO: 0.1,\n CLIP_LENGTH_SECONDS: 1.0,\n DELAY_SECONDS: 1.0,\n FALLBACK_FPS: 30,\n ICE_SERVERS: [\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n ] as RTCIceServer[],\n} as const;\n\nconsole.log(\"defaults\", DEFAULTS);\n\n/**\n * Validation constraints\n */\nconst CONSTRAINTS = {\n SAMPLING_RATIO: { min: 0, max: 1 },\n FPS: { min: 1, max: 120 },\n CLIP_LENGTH_SECONDS: { min: 0.1, max: 60 },\n DELAY_SECONDS: { min: 0, max: 60 },\n RATING: { min: 1, max: 5 },\n} as const;\n\n/**\n * Logger utility for controlled logging\n */\nclass Logger {\n private debugEnabled: boolean;\n\n constructor(debugEnabled: boolean = false) {\n this.debugEnabled = debugEnabled;\n }\n\n debug(...args: any[]): void {\n if (this.debugEnabled) {\n console.log(\"[RealtimeVision Debug]\", ...args);\n }\n }\n\n info(...args: any[]): void {\n console.log(\"[RealtimeVision]\", ...args);\n }\n\n warn(...args: any[]): void {\n console.warn(\"[RealtimeVision]\", ...args);\n }\n\n error(...args: any[]): void {\n console.error(\"[RealtimeVision]\", ...args);\n }\n}\n\nexport interface RealtimeVisionConfig {\n /**\n * Base URL for the API (e.g., \"https://api.example.com\")\n */\n apiUrl: string;\n\n /**\n * API key for authentication\n * Required for all API requests\n */\n apiKey: string;\n\n /**\n * The prompt/task to run on window segments of the stream.\n * This runs continuously (at a defined window interval).\n *\n * Examples:\n * - \"Read any visible text\"\n * - \"Detect objects and return as JSON array\"\n * - \"Describe facial expression\"\n */\n prompt: string;\n\n /**\n * Video source configuration\n * Defaults to camera with environment facing if not specified\n */\n source?: StreamSource;\n\n /**\n * Model backend to use\n */\n backend?: \"overshoot\";\n\n /**\n * Model name to use for inference\n */\n model?: string;\n\n /**\n * Optional JSON schema for structured output\n */\n outputSchema?: Record<string, any>;\n\n /**\n * Called when a new inference result arrives (~1 per second)\n */\n onResult: (result: StreamInferenceResult) => void;\n\n /**\n * Called when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Custom processing configuration\n * All fields are optional and will use defaults if not provided\n */\n processing?: {\n /**\n * Sampling ratio (0-1). Controls what fraction of frames are processed.\n */\n sampling_ratio?: number;\n /**\n * Frames per second (1-120)\n */\n fps?: number;\n /**\n * Clip length in seconds (0.1-60)\n */\n clip_length_seconds?: number;\n /**\n * Delay in seconds (0-60)\n */\n delay_seconds?: number;\n };\n\n /**\n * ICE servers for WebRTC connection\n * If not provided, uses default TURN servers\n */\n iceServers?: RTCIceServer[];\n\n /**\n * Enable debug logging\n * @default false\n */\n debug?: boolean;\n}\n\nclass ValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ValidationError\";\n }\n}\n\nexport class RealtimeVision {\n private config: RealtimeVisionConfig;\n private client: StreamClient;\n private logger: Logger;\n\n private mediaStream: MediaStream | null = null;\n private peerConnection: RTCPeerConnection | null = null;\n private webSocket: WebSocket | null = null;\n private streamId: string | null = null;\n private keepaliveInterval: number | null = null;\n private videoElement: HTMLVideoElement | null = null;\n\n private isRunning = false;\n\n constructor(config: RealtimeVisionConfig) {\n this.validateConfig(config);\n this.config = config;\n this.logger = new Logger(config.debug ?? false);\n this.client = new StreamClient({\n baseUrl: config.apiUrl,\n apiKey: config.apiKey,\n });\n }\n\n /**\n * Validate configuration values\n */\n private validateConfig(config: RealtimeVisionConfig): void {\n if (!config.apiUrl || typeof config.apiUrl !== \"string\") {\n throw new ValidationError(\"apiUrl is required and must be a string\");\n }\n\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new ValidationError(\"apiKey is required and must be a string\");\n }\n\n if (!config.prompt || typeof config.prompt !== \"string\") {\n throw new ValidationError(\"prompt is required and must be a string\");\n }\n\n if (config.source) {\n if (config.source.type === \"camera\") {\n if (\n config.source.cameraFacing !== \"user\" &&\n config.source.cameraFacing !== \"environment\"\n ) {\n throw new ValidationError(\n 'cameraFacing must be \"user\" or \"environment\"',\n );\n }\n } else if (config.source.type === \"video\") {\n if (!(config.source.file instanceof File)) {\n throw new ValidationError(\"video source must provide a File object\");\n }\n } else {\n throw new ValidationError('source.type must be \"camera\" or \"video\"');\n }\n }\n\n if (config.processing?.sampling_ratio !== undefined) {\n const ratio = config.processing.sampling_ratio;\n if (\n ratio < CONSTRAINTS.SAMPLING_RATIO.min ||\n ratio > CONSTRAINTS.SAMPLING_RATIO.max\n ) {\n throw new ValidationError(\n `sampling_ratio must be between ${CONSTRAINTS.SAMPLING_RATIO.min} and ${CONSTRAINTS.SAMPLING_RATIO.max}`,\n );\n }\n }\n\n if (config.processing?.fps !== undefined) {\n const fps = config.processing.fps;\n if (fps < CONSTRAINTS.FPS.min || fps > CONSTRAINTS.FPS.max) {\n throw new ValidationError(\n `fps must be between ${CONSTRAINTS.FPS.min} and ${CONSTRAINTS.FPS.max}`,\n );\n }\n }\n\n if (config.processing?.clip_length_seconds !== undefined) {\n const clip = config.processing.clip_length_seconds;\n if (\n clip < CONSTRAINTS.CLIP_LENGTH_SECONDS.min ||\n clip > CONSTRAINTS.CLIP_LENGTH_SECONDS.max\n ) {\n throw new ValidationError(\n `clip_length_seconds must be between ${CONSTRAINTS.CLIP_LENGTH_SECONDS.min} and ${CONSTRAINTS.CLIP_LENGTH_SECONDS.max}`,\n );\n }\n }\n\n if (config.processing?.delay_seconds !== undefined) {\n const delay = config.processing.delay_seconds;\n if (\n delay < CONSTRAINTS.DELAY_SECONDS.min ||\n delay > CONSTRAINTS.DELAY_SECONDS.max\n ) {\n throw new ValidationError(\n `delay_seconds must be between ${CONSTRAINTS.DELAY_SECONDS.min} and ${CONSTRAINTS.DELAY_SECONDS.max}`,\n );\n }\n }\n }\n\n /**\n * Create media stream from the configured source\n */\n private async createMediaStream(source: StreamSource): Promise<MediaStream> {\n this.logger.debug(\"Creating media stream from source:\", source.type);\n\n switch (source.type) {\n case \"camera\":\n return await navigator.mediaDevices.getUserMedia({\n video: { facingMode: { ideal: source.cameraFacing } },\n audio: false,\n });\n\n case \"video\":\n const video = document.createElement(\"video\");\n video.src = URL.createObjectURL(source.file);\n video.muted = true;\n video.loop = true;\n video.playsInline = true;\n\n this.logger.debug(\"Loading video file:\", source.file.name);\n\n // Wait for video to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(\"Video loading timeout after 10 seconds\"));\n }, 10000);\n\n video.onloadedmetadata = () => {\n clearTimeout(timeout);\n this.logger.debug(\"Video metadata loaded\");\n resolve();\n };\n\n video.onerror = (e) => {\n clearTimeout(timeout);\n this.logger.error(\"Video loading error:\", e);\n reject(new Error(\"Failed to load video file\"));\n };\n\n if (video.readyState >= 1) {\n clearTimeout(timeout);\n resolve();\n }\n });\n\n await video.play();\n this.logger.debug(\"Video playback started\");\n\n const stream = video.captureStream();\n if (!stream) {\n throw new Error(\"Failed to capture video stream\");\n }\n\n const videoTracks = stream.getVideoTracks();\n if (videoTracks.length === 0) {\n throw new Error(\"Video stream has no video tracks\");\n }\n\n this.videoElement = video;\n return stream;\n\n default:\n throw new Error(`Unknown source type: ${(source as any).type}`);\n }\n }\n\n /**\n * Get FPS from media stream\n */\n private async getStreamFps(\n stream: MediaStream | null,\n source: StreamSource,\n ): Promise<number> {\n if (!stream) {\n this.logger.warn(\"Stream is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTracks = stream.getVideoTracks();\n if (!videoTracks || videoTracks.length === 0) {\n this.logger.warn(\"No video tracks found, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTrack = videoTracks[0];\n if (!videoTrack) {\n this.logger.warn(\"First video track is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n // For camera sources, get FPS from track settings\n if (source.type === \"camera\") {\n const settings = videoTrack.getSettings();\n const fps = settings.frameRate ?? DEFAULTS.FALLBACK_FPS;\n this.logger.debug(\"Detected camera FPS:\", fps);\n return fps;\n }\n\n // For video file sources, try to get FPS from video element\n if (source.type === \"video\" && this.videoElement) {\n await new Promise<void>((resolve, reject) => {\n if (this.videoElement!.readyState >= 1) {\n resolve();\n } else {\n this.videoElement!.onloadedmetadata = () => resolve();\n this.videoElement!.onerror = () =>\n reject(new Error(\"Failed to load video metadata\"));\n }\n });\n\n // For video files, use fallback FPS or user-specified config\n this.logger.debug(\"Using fallback FPS for video file\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n return DEFAULTS.FALLBACK_FPS;\n }\n\n /**\n * Get processing configuration with defaults applied\n */\n private getProcessingConfig(detectedFps: number): StreamProcessingConfig {\n const userProcessing = this.config.processing || {};\n\n return {\n sampling_ratio: userProcessing.sampling_ratio ?? DEFAULTS.SAMPLING_RATIO,\n fps: userProcessing.fps ?? detectedFps,\n clip_length_seconds:\n userProcessing.clip_length_seconds ?? DEFAULTS.CLIP_LENGTH_SECONDS,\n delay_seconds: userProcessing.delay_seconds ?? DEFAULTS.DELAY_SECONDS,\n };\n }\n\n /**\n * Get the effective source configuration\n */\n private getSource(): StreamSource {\n return this.config.source ?? DEFAULTS.SOURCE;\n }\n\n /**\n * Start the vision stream\n */\n async start(): Promise<void> {\n if (this.isRunning) {\n throw new Error(\"Vision stream already running\");\n }\n\n try {\n const source = this.getSource();\n this.logger.debug(\"Starting stream with source type:\", source.type);\n\n if (source.type === \"video\") {\n this.logger.debug(\"Video file:\", {\n name: source.file.name,\n size: source.file.size,\n type: source.file.type,\n });\n\n if (!source.file || !(source.file instanceof File)) {\n throw new Error(\"Invalid video file\");\n }\n }\n\n // Create media stream\n this.mediaStream = await this.createMediaStream(source);\n const videoTrack = this.mediaStream.getVideoTracks()[0];\n if (!videoTrack) {\n throw new Error(\"No video track available\");\n }\n\n // Get FPS for the stream\n const detectedFps = await this.getStreamFps(this.mediaStream, source);\n\n // Set up WebRTC peer connection\n const iceServers = this.config.iceServers ?? DEFAULTS.ICE_SERVERS;\n this.logger.debug(\"Creating peer connection with ICE servers\");\n this.peerConnection = new RTCPeerConnection({ iceServers });\n\n // Set up ICE logging\n this.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n this.logger.debug(\"ICE candidate:\", {\n type: event.candidate.type,\n protocol: event.candidate.protocol,\n });\n } else {\n this.logger.debug(\"ICE gathering complete\");\n }\n };\n\n this.peerConnection.oniceconnectionstatechange = () => {\n this.logger.debug(\n \"ICE connection state:\",\n this.peerConnection?.iceConnectionState,\n );\n };\n\n this.peerConnection.addTrack(videoTrack, this.mediaStream);\n\n // Create and set local offer\n const offer = await this.peerConnection.createOffer();\n await this.peerConnection.setLocalDescription(offer);\n\n if (!this.peerConnection.localDescription) {\n throw new Error(\"Failed to create local description\");\n }\n\n // Create stream on server\n this.logger.debug(\"Creating stream on server\");\n const response = await this.client.createStream({\n webrtc: {\n type: \"offer\",\n sdp: this.peerConnection.localDescription.sdp,\n },\n processing: this.getProcessingConfig(detectedFps),\n inference: {\n prompt: this.config.prompt,\n backend: this.config.backend ?? DEFAULTS.BACKEND,\n model: this.config.model ?? DEFAULTS.MODEL,\n output_schema_json: this.config.outputSchema,\n },\n });\n\n this.logger.debug(\"Backend response received:\", {\n stream_id: response.stream_id,\n has_turn_servers: !!response.turn_servers,\n });\n\n // Set remote description\n await this.peerConnection.setRemoteDescription(response.webrtc);\n\n this.streamId = response.stream_id;\n this.logger.info(\"Stream started:\", this.streamId);\n\n // Set up keepalive\n this.setupKeepalive(response.lease?.ttl_seconds);\n\n // Connect WebSocket for results\n this.setupWebSocket(response.stream_id);\n\n this.isRunning = true;\n } catch (error) {\n await this.handleFatalError(error);\n throw error;\n }\n }\n\n /**\n * Set up keepalive interval with error handling\n */\n private setupKeepalive(ttlSeconds: number | undefined): void {\n if (!ttlSeconds) {\n return;\n }\n\n const intervalMs = (ttlSeconds / 2) * 1000;\n this.logger.debug(\"Setting up keepalive with interval:\", intervalMs, \"ms\");\n\n this.keepaliveInterval = window.setInterval(async () => {\n try {\n if (this.streamId) {\n await this.client.renewLease(this.streamId);\n this.logger.debug(\"Lease renewed\");\n }\n } catch (error) {\n this.logger.error(\"Keepalive failed:\", error);\n const keepaliveError = new Error(\n `Keepalive failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n await this.handleFatalError(keepaliveError);\n }\n }, intervalMs);\n }\n\n /**\n * Set up WebSocket connection with error handling\n */\n private setupWebSocket(streamId: string): void {\n this.logger.debug(\"Connecting WebSocket for stream:\", streamId);\n this.webSocket = this.client.connectWebSocket(streamId);\n\n this.webSocket.onopen = () => {\n this.logger.debug(\"WebSocket connected\");\n if (this.webSocket) {\n this.webSocket.send(JSON.stringify({ api_key: this.config.apiKey }));\n }\n };\n\n this.webSocket.onmessage = (event) => {\n try {\n const result: StreamInferenceResult = JSON.parse(event.data);\n this.config.onResult(result);\n } catch (error) {\n const parseError = new Error(\n `Failed to parse WebSocket message: ${error instanceof Error ? error.message : String(error)}`,\n );\n this.handleNonFatalError(parseError);\n }\n };\n\n this.webSocket.onerror = () => {\n this.logger.error(\"WebSocket error occurred\");\n const error = new Error(\"WebSocket error occurred\");\n this.handleFatalError(error);\n };\n\n this.webSocket.onclose = (event) => {\n if (this.isRunning) {\n if (event.code === 1008) {\n this.logger.error(\"WebSocket authentication failed\");\n const error = new Error(\n \"WebSocket authentication failed: Invalid or revoked API key\",\n );\n this.handleFatalError(error);\n } else {\n this.logger.warn(\"WebSocket closed unexpectedly:\", event.code);\n const error = new Error(\"WebSocket closed unexpectedly\");\n this.handleFatalError(error);\n }\n } else {\n this.logger.debug(\"WebSocket closed\");\n }\n };\n }\n\n /**\n * Handle non-fatal errors (report but don't stop stream)\n */\n private handleNonFatalError(error: Error): void {\n this.logger.warn(\"Non-fatal error:\", error.message);\n if (this.config.onError) {\n this.config.onError(error);\n }\n }\n\n /**\n * Handle fatal errors (stop stream and report)\n */\n private async handleFatalError(error: unknown): Promise<void> {\n this.logger.error(\"Fatal error:\", error);\n await this.cleanup();\n this.isRunning = false;\n\n const normalizedError =\n error instanceof Error ? error : new Error(String(error));\n\n if (this.config.onError) {\n this.config.onError(normalizedError);\n }\n }\n\n /**\n * Update the prompt/task while stream is running\n */\n async updatePrompt(prompt: string): Promise<void> {\n if (!this.isRunning || !this.streamId) {\n throw new Error(\"Vision stream not running\");\n }\n\n if (!prompt || typeof prompt !== \"string\") {\n throw new ValidationError(\"prompt must be a non-empty string\");\n }\n\n this.logger.debug(\"Updating prompt\");\n await this.client.updatePrompt(this.streamId, prompt);\n this.logger.info(\"Prompt updated\");\n }\n\n /**\n * Stop the vision stream and clean up resources\n */\n async stop(): Promise<void> {\n this.logger.info(\"Stopping stream\");\n await this.cleanup();\n this.isRunning = false;\n }\n\n /**\n * Submit feedback for the stream\n */\n async submitFeedback(feedback: {\n rating: number;\n category: string;\n feedback?: string;\n }): Promise<void> {\n if (!this.streamId) {\n throw new Error(\"No active stream\");\n }\n\n if (\n feedback.rating < CONSTRAINTS.RATING.min ||\n feedback.rating > CONSTRAINTS.RATING.max\n ) {\n throw new ValidationError(\n `rating must be between ${CONSTRAINTS.RATING.min} and ${CONSTRAINTS.RATING.max}`,\n );\n }\n\n if (!feedback.category || typeof feedback.category !== \"string\") {\n throw new ValidationError(\"category must be a non-empty string\");\n }\n\n this.logger.debug(\"Submitting feedback\");\n await this.client.submitFeedback(this.streamId, {\n rating: feedback.rating,\n category: feedback.category,\n feedback: feedback.feedback ?? \"\",\n });\n this.logger.info(\"Feedback submitted\");\n }\n\n /**\n * Get the current stream ID\n */\n getStreamId(): string | null {\n return this.streamId;\n }\n\n /**\n * Get the media stream (for displaying video preview)\n */\n getMediaStream(): MediaStream | null {\n return this.mediaStream;\n }\n\n /**\n * Check if the stream is running\n */\n isActive(): boolean {\n return this.isRunning;\n }\n\n private async cleanup(): Promise<void> {\n this.logger.debug(\"Cleaning up resources\");\n\n if (this.keepaliveInterval) {\n window.clearInterval(this.keepaliveInterval);\n this.keepaliveInterval = null;\n }\n\n if (this.webSocket) {\n this.webSocket.close();\n this.webSocket = null;\n }\n\n if (this.peerConnection) {\n this.peerConnection.close();\n this.peerConnection = null;\n }\n\n if (this.mediaStream) {\n this.mediaStream.getTracks().forEach((track) => track.stop());\n this.mediaStream = null;\n }\n\n if (this.videoElement) {\n this.videoElement.pause();\n URL.revokeObjectURL(this.videoElement.src);\n this.videoElement.remove();\n this.videoElement = null;\n }\n\n this.streamId = null;\n this.logger.debug(\"Cleanup complete\");\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/client/errors.ts","../src/client/client.ts","../src/client/RealtimeVision.ts"],"names":["ValidationError"],"mappings":";AAAO,IAAM,QAAA,GAAN,cAAuB,KAAA,CAAM;AAAA,EAKlC,WAAA,CACE,OAAA,EACA,UAAA,EACA,SAAA,EACA,OAAA,EACA;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,UAAA;AACZ,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAClB,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AACjB,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AAAA,EACjB;AACF;AAEO,IAAM,iBAAA,GAAN,cAAgC,QAAA,CAAS;AAAA,EAC9C,WAAA,CAAY,SAAiB,SAAA,EAAoB;AAC/C,IAAA,KAAA,CAAM,OAAA,EAAS,KAAK,SAAS,CAAA;AAC7B,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AAAA,EACd;AACF;AAEO,IAAM,eAAA,GAAN,cAA8B,QAAA,CAAS;AAAA,EAC5C,WAAA,CAAY,OAAA,EAAiB,SAAA,EAAoB,OAAA,EAAe;AAC9D,IAAA,KAAA,CAAM,OAAA,EAAS,GAAA,EAAK,SAAA,EAAW,OAAO,CAAA;AACtC,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EACd;AACF;AAEO,IAAM,aAAA,GAAN,cAA4B,QAAA,CAAS;AAAA,EAC1C,WAAA,CAAY,SAAiB,SAAA,EAAoB;AAC/C,IAAA,KAAA,CAAM,OAAA,EAAS,KAAK,SAAS,CAAA;AAC7B,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AAAA,EACd;AACF;AAEO,IAAM,YAAA,GAAN,cAA2B,QAAA,CAAS;AAAA,EAGzC,WAAA,CAAY,SAAiB,KAAA,EAAe;AAC1C,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AACZ,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AAAA,EACf;AACF;AAEO,IAAM,WAAA,GAAN,cAA0B,QAAA,CAAS;AAAA,EACxC,WAAA,CAAY,OAAA,EAAiB,SAAA,EAAoB,OAAA,EAAe;AAC9D,IAAA,KAAA,CAAM,OAAA,EAAS,GAAA,EAAK,SAAA,EAAW,OAAO,CAAA;AACtC,IAAA,IAAA,CAAK,IAAA,GAAO,aAAA;AAAA,EACd;AACF;;;AClCO,IAAM,eAAN,MAAmB;AAAA,EAIxB,YAAY,MAAA,EAAsB;AAChC,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,IAAU,OAAO,MAAA,CAAO,WAAW,QAAA,EAAU;AACvD,MAAA,MAAM,IAAI,MAAM,yCAAyC,CAAA;AAAA,IAC3D;AAEA,IAAA,IAAA,CAAK,UAAU,MAAA,CAAO,OAAA;AACtB,IAAA,IAAA,CAAK,SAAS,MAAA,CAAO,MAAA;AAAA,EACvB;AAAA,EAEA,MAAc,OAAA,CACZ,IAAA,EACA,OAAA,GAAuB,EAAC,EACZ;AACZ,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAO,GAAG,IAAI,CAAA,CAAA;AAClC,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AAEvC,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,QAChC,GAAG,OAAA;AAAA,QACH,QAAQ,UAAA,CAAW,MAAA;AAAA,QACnB,WAAA,EAAa,SAAA;AAAA,QACb,OAAA,EAAS;AAAA,UACP,cAAA,EAAgB,kBAAA;AAAA,UAChB,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,UACpC,GAAG,OAAA,CAAQ;AAAA;AACb,OACD,CAAA;AAED,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,MAAM,YAA2B,MAAM,QAAA,CAAS,IAAA,EAAK,CAAE,MAAM,OAAO;AAAA,UAClE,KAAA,EAAO,eAAA;AAAA,UACP,SAAS,QAAA,CAAS;AAAA,SACpB,CAAE,CAAA;AAEF,QAAA,MAAM,OAAA,GAAU,SAAA,CAAU,OAAA,IAAW,SAAA,CAAU,KAAA;AAE/C,QAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,UAAA,MAAM,IAAI,iBAAA;AAAA,YACR,OAAA,IAAW,4BAAA;AAAA,YACX,SAAA,CAAU;AAAA,WACZ;AAAA,QACF;AACA,QAAA,IAAI,QAAA,CAAS,MAAA,KAAW,GAAA,IAAO,QAAA,CAAS,WAAW,GAAA,EAAK;AACtD,UAAA,MAAM,IAAI,eAAA;AAAA,YACR,OAAA;AAAA,YACA,SAAA,CAAU,UAAA;AAAA,YACV,SAAA,CAAU;AAAA,WACZ;AAAA,QACF;AACA,QAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,UAAA,MAAM,IAAI,aAAA,CAAc,OAAA,EAAS,SAAA,CAAU,UAAU,CAAA;AAAA,QACvD;AACA,QAAA,IAAI,QAAA,CAAS,UAAU,GAAA,EAAK;AAC1B,UAAA,MAAM,IAAI,WAAA;AAAA,YACR,OAAA;AAAA,YACA,SAAA,CAAU,UAAA;AAAA,YACV,SAAA,CAAU;AAAA,WACZ;AAAA,QACF;AAEA,QAAA,MAAM,IAAI,QAAA;AAAA,UACR,OAAA;AAAA,UACA,QAAA,CAAS,MAAA;AAAA,UACT,SAAA,CAAU,UAAA;AAAA,UACV,SAAA,CAAU;AAAA,SACZ;AAAA,MACF;AAEA,MAAA,OAAO,MAAM,SAAS,IAAA,EAAK;AAAA,IAC7B,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,iBAAiB,QAAA,EAAU;AAC7B,QAAA,MAAM,KAAA;AAAA,MACR;AAEA,MAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,QAAA,MAAM,IAAI,YAAA,CAAa,CAAA,eAAA,EAAkB,KAAA,CAAM,OAAO,IAAI,KAAK,CAAA;AAAA,MACjE;AAEA,MAAA,MAAM,IAAI,aAAa,uBAAuB,CAAA;AAAA,IAChD;AAAA,EACF;AAAA,EACA,MAAM,aACJ,OAAA,EAC+B;AAC/B,IAAA,OAAO,IAAA,CAAK,QAA8B,UAAA,EAAY;AAAA,MACpD,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,OAAO;AAAA,KAC7B,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,WAAW,QAAA,EAA8C;AAC7D,IAAA,OAAO,IAAA,CAAK,OAAA,CAA2B,CAAA,SAAA,EAAY,QAAQ,CAAA,UAAA,CAAA,EAAc;AAAA,MACvE,MAAA,EAAQ;AAAA,KACT,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,YAAA,CACJ,QAAA,EACA,MAAA,EAC+B;AAC/B,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,MACV,YAAY,QAAQ,CAAA,cAAA,CAAA;AAAA,MACpB;AAAA,QACE,MAAA,EAAQ,OAAA;AAAA,QACR,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,EAAE,QAAQ;AAAA;AACjC,KACF;AAAA,EACF;AAAA,EAEA,iBAAiB,QAAA,EAA6B;AAC5C,IAAA,MAAM,KAAA,GAAQ,KAAK,OAAA,CAChB,OAAA,CAAQ,WAAW,OAAO,CAAA,CAC1B,OAAA,CAAQ,UAAA,EAAY,QAAQ,CAAA;AAC/B,IAAA,OAAO,IAAI,SAAA,CAAU,CAAA,EAAG,KAAK,CAAA,YAAA,EAAe,QAAQ,CAAA,CAAE,CAAA;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WAAA,GAA+B;AACnC,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,QAAA,CAAA;AAC3B,IAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,MAChC,WAAA,EAAa;AAAA,KACd,CAAA;AACD,IAAA,OAAO,SAAS,IAAA,EAAK;AAAA,EACvB;AACF;;;AC7IA,IAAM,QAAA,GAAW;AAAA,EACf,OAAA,EAAS,WAAA;AAAA,EACT,KAAA,EAAO,gCAAA;AAAA,EACP,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAU,cAAc,aAAA,EAAc;AAAA,EACtD,cAAA,EAAgB,GAAA;AAAA,EAChB,mBAAA,EAAqB,CAAA;AAAA,EACrB,aAAA,EAAe,CAAA;AAAA,EACf,YAAA,EAAc,EAAA;AAAA,EACd,WAAA,EAAa;AAAA,IACX;AAAA,MACE,IAAA,EAAM,2CAAA;AAAA,MACN,QAAA,EAAU,iDAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA;AAAA,MACE,IAAA,EAAM,2CAAA;AAAA,MACN,QAAA,EAAU,iDAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA;AAAA,MACE,IAAA,EAAM,2CAAA;AAAA,MACN,QAAA,EAAU,iDAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA;AAAA,MACE,IAAA,EAAM,2CAAA;AAAA,MACN,QAAA,EAAU,iDAAA;AAAA,MACV,UAAA,EAAY;AAAA;AACd;AAEJ,CAAA;AAKA,IAAM,WAAA,GAAc;AAAA,EAClB,cAAA,EAAgB,EAAE,GAAA,EAAK,CAAA,EAAG,KAAK,CAAA,EAAE;AAAA,EACjC,GAAA,EAAK,EAAE,GAAA,EAAK,CAAA,EAAG,KAAK,GAAA,EAAI;AAAA,EACxB,mBAAA,EAAqB,EAAE,GAAA,EAAK,GAAA,EAAK,KAAK,EAAA,EAAG;AAAA,EACzC,aAAA,EAAe,EAAE,GAAA,EAAK,CAAA,EAAG,KAAK,EAAA;AAChC,CAAA;AAKA,IAAM,SAAN,MAAa;AAAA,EAGX,WAAA,CAAY,eAAwB,KAAA,EAAO;AACzC,IAAA,IAAA,CAAK,YAAA,GAAe,YAAA;AAAA,EACtB;AAAA,EAEA,SAAS,IAAA,EAAmB;AAC1B,IAAA,IAAI,KAAK,YAAA,EAAc;AACrB,MAAA,OAAA,CAAQ,GAAA,CAAI,wBAAA,EAA0B,GAAG,IAAI,CAAA;AAAA,IAC/C;AAAA,EACF;AAAA,EAEA,QAAQ,IAAA,EAAmB;AACzB,IAAA,OAAA,CAAQ,GAAA,CAAI,kBAAA,EAAoB,GAAG,IAAI,CAAA;AAAA,EACzC;AAAA,EAEA,QAAQ,IAAA,EAAmB;AACzB,IAAA,OAAA,CAAQ,IAAA,CAAK,kBAAA,EAAoB,GAAG,IAAI,CAAA;AAAA,EAC1C;AAAA,EAEA,SAAS,IAAA,EAAmB;AAC1B,IAAA,OAAA,CAAQ,KAAA,CAAM,kBAAA,EAAoB,GAAG,IAAI,CAAA;AAAA,EAC3C;AACF,CAAA;AA4FA,IAAMA,gBAAAA,GAAN,cAA8B,KAAA,CAAM;AAAA,EAClC,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EACd;AACF,CAAA;AAEO,IAAM,iBAAN,MAAqB;AAAA,EAgB1B,YAAY,MAAA,EAA8B;AAX1C,IAAA,IAAA,CAAQ,WAAA,GAAkC,IAAA;AAC1C,IAAA,IAAA,CAAQ,cAAA,GAA2C,IAAA;AACnD,IAAA,IAAA,CAAQ,SAAA,GAA8B,IAAA;AACtC,IAAA,IAAA,CAAQ,QAAA,GAA0B,IAAA;AAClC,IAAA,IAAA,CAAQ,iBAAA,GAAmC,IAAA;AAC3C,IAAA,IAAA,CAAQ,YAAA,GAAwC,IAAA;AAChD,IAAA,IAAA,CAAQ,aAAA,GAA0C,IAAA;AAClD,IAAA,IAAA,CAAQ,sBAAA,GAAwC,IAAA;AAEhD,IAAA,IAAA,CAAQ,SAAA,GAAY,KAAA;AAGlB,IAAA,IAAA,CAAK,eAAe,MAAM,CAAA;AAC1B,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,MAAA,CAAO,MAAA,CAAO,SAAS,KAAK,CAAA;AAC9C,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,YAAA,CAAa;AAAA,MAC7B,SAAS,MAAA,CAAO,MAAA;AAAA,MAChB,QAAQ,MAAA,CAAO;AAAA,KAChB,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,MAAA,EAAoC;AACzD,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,IAAU,OAAO,MAAA,CAAO,WAAW,QAAA,EAAU;AACvD,MAAA,MAAM,IAAIA,iBAAgB,yCAAyC,CAAA;AAAA,IACrE;AAEA,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,IAAU,OAAO,MAAA,CAAO,WAAW,QAAA,EAAU;AACvD,MAAA,MAAM,IAAIA,iBAAgB,yCAAyC,CAAA;AAAA,IACrE;AAEA,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,IAAU,OAAO,MAAA,CAAO,WAAW,QAAA,EAAU;AACvD,MAAA,MAAM,IAAIA,iBAAgB,yCAAyC,CAAA;AAAA,IACrE;AAEA,IAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,MAAA,IAAI,MAAA,CAAO,MAAA,CAAO,IAAA,KAAS,QAAA,EAAU;AACnC,QAAA,IACE,OAAO,MAAA,CAAO,YAAA,KAAiB,UAC/B,MAAA,CAAO,MAAA,CAAO,iBAAiB,aAAA,EAC/B;AACA,UAAA,MAAM,IAAIA,gBAAAA;AAAA,YACR;AAAA,WACF;AAAA,QACF;AAAA,MACF,CAAA,MAAA,IAAW,MAAA,CAAO,MAAA,CAAO,IAAA,KAAS,OAAA,EAAS;AACzC,QAAA,IAAI,EAAE,MAAA,CAAO,MAAA,CAAO,IAAA,YAAgB,IAAA,CAAA,EAAO;AACzC,UAAA,MAAM,IAAIA,iBAAgB,yCAAyC,CAAA;AAAA,QACrE;AAAA,MACF,CAAA,MAAO;AACL,QAAA,MAAM,IAAIA,iBAAgB,yCAAyC,CAAA;AAAA,MACrE;AAAA,IACF;AAEA,IAAA,IAAI,MAAA,CAAO,UAAA,EAAY,cAAA,KAAmB,MAAA,EAAW;AACnD,MAAA,MAAM,KAAA,GAAQ,OAAO,UAAA,CAAW,cAAA;AAChC,MAAA,IACE,QAAQ,WAAA,CAAY,cAAA,CAAe,OACnC,KAAA,GAAQ,WAAA,CAAY,eAAe,GAAA,EACnC;AACA,QAAA,MAAM,IAAIA,gBAAAA;AAAA,UACR,kCAAkC,WAAA,CAAY,cAAA,CAAe,GAAG,CAAA,KAAA,EAAQ,WAAA,CAAY,eAAe,GAAG,CAAA;AAAA,SACxG;AAAA,MACF;AAAA,IACF;AAEA,IAAA,IAAI,MAAA,CAAO,UAAA,EAAY,GAAA,KAAQ,MAAA,EAAW;AACxC,MAAA,MAAM,GAAA,GAAM,OAAO,UAAA,CAAW,GAAA;AAC9B,MAAA,IAAI,MAAM,WAAA,CAAY,GAAA,CAAI,OAAO,GAAA,GAAM,WAAA,CAAY,IAAI,GAAA,EAAK;AAC1D,QAAA,MAAM,IAAIA,gBAAAA;AAAA,UACR,uBAAuB,WAAA,CAAY,GAAA,CAAI,GAAG,CAAA,KAAA,EAAQ,WAAA,CAAY,IAAI,GAAG,CAAA;AAAA,SACvE;AAAA,MACF;AAAA,IACF;AAEA,IAAA,IAAI,MAAA,CAAO,UAAA,EAAY,mBAAA,KAAwB,MAAA,EAAW;AACxD,MAAA,MAAM,IAAA,GAAO,OAAO,UAAA,CAAW,mBAAA;AAC/B,MAAA,IACE,OAAO,WAAA,CAAY,mBAAA,CAAoB,OACvC,IAAA,GAAO,WAAA,CAAY,oBAAoB,GAAA,EACvC;AACA,QAAA,MAAM,IAAIA,gBAAAA;AAAA,UACR,uCAAuC,WAAA,CAAY,mBAAA,CAAoB,GAAG,CAAA,KAAA,EAAQ,WAAA,CAAY,oBAAoB,GAAG,CAAA;AAAA,SACvH;AAAA,MACF;AAAA,IACF;AAEA,IAAA,IAAI,MAAA,CAAO,UAAA,EAAY,aAAA,KAAkB,MAAA,EAAW;AAClD,MAAA,MAAM,KAAA,GAAQ,OAAO,UAAA,CAAW,aAAA;AAChC,MAAA,IACE,QAAQ,WAAA,CAAY,aAAA,CAAc,OAClC,KAAA,GAAQ,WAAA,CAAY,cAAc,GAAA,EAClC;AACA,QAAA,MAAM,IAAIA,gBAAAA;AAAA,UACR,iCAAiC,WAAA,CAAY,aAAA,CAAc,GAAG,CAAA,KAAA,EAAQ,WAAA,CAAY,cAAc,GAAG,CAAA;AAAA,SACrG;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,kBAAkB,MAAA,EAA4C;AAC1E,IAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,oCAAA,EAAsC,MAAA,CAAO,IAAI,CAAA;AAEnE,IAAA,QAAQ,OAAO,IAAA;AAAM,MACnB,KAAK,QAAA;AACH,QAAA,OAAO,MAAM,SAAA,CAAU,YAAA,CAAa,YAAA,CAAa;AAAA,UAC/C,OAAO,EAAE,UAAA,EAAY,EAAE,KAAA,EAAO,MAAA,CAAO,cAAa,EAAE;AAAA,UACpD,KAAA,EAAO;AAAA,SACR,CAAA;AAAA,MAEH,KAAK,OAAA;AACH,QAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AAC5C,QAAA,KAAA,CAAM,GAAA,GAAM,GAAA,CAAI,eAAA,CAAgB,MAAA,CAAO,IAAI,CAAA;AAC3C,QAAA,KAAA,CAAM,KAAA,GAAQ,IAAA;AACd,QAAA,KAAA,CAAM,IAAA,GAAO,IAAA;AACb,QAAA,KAAA,CAAM,WAAA,GAAc,IAAA;AAEpB,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,qBAAA,EAAuB,MAAA,CAAO,KAAK,IAAI,CAAA;AAGzD,QAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AAC3C,UAAA,MAAM,OAAA,GAAU,WAAW,MAAM;AAC/B,YAAA,MAAA,CAAO,IAAI,KAAA,CAAM,wCAAwC,CAAC,CAAA;AAAA,UAC5D,GAAG,GAAK,CAAA;AAER,UAAA,KAAA,CAAM,mBAAmB,MAAM;AAC7B,YAAA,YAAA,CAAa,OAAO,CAAA;AACpB,YAAA,IAAA,CAAK,MAAA,CAAO,MAAM,uBAAuB,CAAA;AACzC,YAAA,OAAA,EAAQ;AAAA,UACV,CAAA;AAEA,UAAA,KAAA,CAAM,OAAA,GAAU,CAAC,CAAA,KAAM;AACrB,YAAA,YAAA,CAAa,OAAO,CAAA;AACpB,YAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,sBAAA,EAAwB,CAAC,CAAA;AAC3C,YAAA,MAAA,CAAO,IAAI,KAAA,CAAM,2BAA2B,CAAC,CAAA;AAAA,UAC/C,CAAA;AAEA,UAAA,IAAI,KAAA,CAAM,cAAc,CAAA,EAAG;AACzB,YAAA,YAAA,CAAa,OAAO,CAAA;AACpB,YAAA,OAAA,EAAQ;AAAA,UACV;AAAA,QACF,CAAC,CAAA;AAED,QAAA,MAAM,MAAM,IAAA,EAAK;AACjB,QAAA,IAAA,CAAK,MAAA,CAAO,MAAM,wBAAwB,CAAA;AAE1C,QAAA,IAAI,MAAA;AAGJ,QAAA,IAAI,OAAO,KAAA,CAAM,aAAA,KAAkB,UAAA,EAAY;AAC7C,UAAA,IAAA,CAAK,MAAA,CAAO,MAAM,oCAAoC,CAAA;AACtD,UAAA,MAAA,GAAS,MAAM,aAAA,EAAc;AAAA,QAC/B,CAAA,MAAO;AAEL,UAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,YACV;AAAA,WACF;AAEA,UAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,UAAA,MAAA,CAAO,KAAA,GAAQ,MAAM,UAAA,IAAc,GAAA;AACnC,UAAA,MAAA,CAAO,MAAA,GAAS,MAAM,WAAA,IAAe,GAAA;AACrC,UAAA,MAAM,GAAA,GAAM,MAAA,CAAO,UAAA,CAAW,IAAI,CAAA;AAElC,UAAA,IAAI,CAAC,GAAA,EAAK;AACR,YAAA,MAAM,IAAI,MAAM,iCAAiC,CAAA;AAAA,UACnD;AAGA,UAAA,MAAM,YAAY,MAAM;AACtB,YAAA,IAAI,CAAC,KAAA,CAAM,MAAA,IAAU,CAAC,MAAM,KAAA,EAAO;AACjC,cAAA,GAAA,CAAI,UAAU,KAAA,EAAO,CAAA,EAAG,GAAG,MAAA,CAAO,KAAA,EAAO,OAAO,MAAM,CAAA;AACtD,cAAA,IAAA,CAAK,sBAAA,GAAyB,sBAAsB,SAAS,CAAA;AAAA,YAC/D;AAAA,UACF,CAAA;AACA,UAAA,SAAA,EAAU;AAGV,UAAA,MAAA,GAAS,MAAA,CAAO,cAAc,EAAE,CAAA;AAChC,UAAA,IAAA,CAAK,aAAA,GAAgB,MAAA;AAAA,QACvB;AAEA,QAAA,IAAI,CAAC,MAAA,EAAQ;AACX,UAAA,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAAA,QAClD;AAEA,QAAA,MAAM,WAAA,GAAc,OAAO,cAAA,EAAe;AAC1C,QAAA,IAAI,WAAA,CAAY,WAAW,CAAA,EAAG;AAC5B,UAAA,MAAM,IAAI,MAAM,kCAAkC,CAAA;AAAA,QACpD;AAEA,QAAA,IAAA,CAAK,YAAA,GAAe,KAAA;AACpB,QAAA,OAAO,MAAA;AAAA,MAET;AACE,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,qBAAA,EAAyB,MAAA,CAAe,IAAI,CAAA,CAAE,CAAA;AAAA;AAClE,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,YAAA,CACZ,MAAA,EACA,MAAA,EACiB;AACjB,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,IAAA,CAAK,MAAA,CAAO,KAAK,oCAAoC,CAAA;AACrD,MAAA,OAAO,QAAA,CAAS,YAAA;AAAA,IAClB;AAEA,IAAA,MAAM,WAAA,GAAc,OAAO,cAAA,EAAe;AAC1C,IAAA,IAAI,CAAC,WAAA,IAAe,WAAA,CAAY,MAAA,KAAW,CAAA,EAAG;AAC5C,MAAA,IAAA,CAAK,MAAA,CAAO,KAAK,2CAA2C,CAAA;AAC5D,MAAA,OAAO,QAAA,CAAS,YAAA;AAAA,IAClB;AAEA,IAAA,MAAM,UAAA,GAAa,YAAY,CAAC,CAAA;AAChC,IAAA,IAAI,CAAC,UAAA,EAAY;AACf,MAAA,IAAA,CAAK,MAAA,CAAO,KAAK,+CAA+C,CAAA;AAChE,MAAA,OAAO,QAAA,CAAS,YAAA;AAAA,IAClB;AAGA,IAAA,IAAI,MAAA,CAAO,SAAS,QAAA,EAAU;AAC5B,MAAA,MAAM,QAAA,GAAW,WAAW,WAAA,EAAY;AACxC,MAAA,MAAM,GAAA,GAAM,QAAA,CAAS,SAAA,IAAa,QAAA,CAAS,YAAA;AAC3C,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,sBAAA,EAAwB,GAAG,CAAA;AAC7C,MAAA,OAAO,GAAA;AAAA,IACT;AAGA,IAAA,IAAI,MAAA,CAAO,IAAA,KAAS,OAAA,IAAW,IAAA,CAAK,YAAA,EAAc;AAChD,MAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AAC3C,QAAA,IAAI,IAAA,CAAK,YAAA,CAAc,UAAA,IAAc,CAAA,EAAG;AACtC,UAAA,OAAA,EAAQ;AAAA,QACV,CAAA,MAAO;AACL,UAAA,IAAA,CAAK,YAAA,CAAc,gBAAA,GAAmB,MAAM,OAAA,EAAQ;AACpD,UAAA,IAAA,CAAK,aAAc,OAAA,GAAU,MAC3B,OAAO,IAAI,KAAA,CAAM,+BAA+B,CAAC,CAAA;AAAA,QACrD;AAAA,MACF,CAAC,CAAA;AAGD,MAAA,IAAA,CAAK,MAAA,CAAO,MAAM,mCAAmC,CAAA;AACrD,MAAA,OAAO,QAAA,CAAS,YAAA;AAAA,IAClB;AAEA,IAAA,OAAO,QAAA,CAAS,YAAA;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAoB,WAAA,EAA6C;AACvE,IAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,MAAA,CAAO,UAAA,IAAc,EAAC;AAElD,IAAA,OAAO;AAAA,MACL,cAAA,EAAgB,cAAA,CAAe,cAAA,IAAkB,QAAA,CAAS,cAAA;AAAA,MAC1D,GAAA,EAAK,eAAe,GAAA,IAAO,WAAA;AAAA,MAC3B,mBAAA,EACE,cAAA,CAAe,mBAAA,IAAuB,QAAA,CAAS,mBAAA;AAAA,MACjD,aAAA,EAAe,cAAA,CAAe,aAAA,IAAiB,QAAA,CAAS;AAAA,KAC1D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,SAAA,GAA0B;AAChC,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,MAAA,IAAU,QAAA,CAAS,MAAA;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KAAA,GAAuB;AAC3B,IAAA,IAAI,KAAK,SAAA,EAAW;AAClB,MAAA,MAAM,IAAI,MAAM,+BAA+B,CAAA;AAAA,IACjD;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,KAAK,SAAA,EAAU;AAC9B,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,mCAAA,EAAqC,MAAA,CAAO,IAAI,CAAA;AAElE,MAAA,IAAI,MAAA,CAAO,SAAS,OAAA,EAAS;AAC3B,QAAA,IAAA,CAAK,MAAA,CAAO,MAAM,aAAA,EAAe;AAAA,UAC/B,IAAA,EAAM,OAAO,IAAA,CAAK,IAAA;AAAA,UAClB,IAAA,EAAM,OAAO,IAAA,CAAK,IAAA;AAAA,UAClB,IAAA,EAAM,OAAO,IAAA,CAAK;AAAA,SACnB,CAAA;AAED,QAAA,IAAI,CAAC,MAAA,CAAO,IAAA,IAAQ,EAAE,MAAA,CAAO,gBAAgB,IAAA,CAAA,EAAO;AAClD,UAAA,MAAM,IAAI,MAAM,oBAAoB,CAAA;AAAA,QACtC;AAAA,MACF;AAGA,MAAA,IAAA,CAAK,WAAA,GAAc,MAAM,IAAA,CAAK,iBAAA,CAAkB,MAAM,CAAA;AACtD,MAAA,MAAM,UAAA,GAAa,IAAA,CAAK,WAAA,CAAY,cAAA,GAAiB,CAAC,CAAA;AACtD,MAAA,IAAI,CAAC,UAAA,EAAY;AACf,QAAA,MAAM,IAAI,MAAM,0BAA0B,CAAA;AAAA,MAC5C;AAGA,MAAA,MAAM,cAAc,MAAM,IAAA,CAAK,YAAA,CAAa,IAAA,CAAK,aAAa,MAAM,CAAA;AAGpE,MAAA,MAAM,UAAA,GAAa,IAAA,CAAK,MAAA,CAAO,UAAA,IAAc,QAAA,CAAS,WAAA;AACtD,MAAA,IAAA,CAAK,MAAA,CAAO,MAAM,2CAA2C,CAAA;AAC7D,MAAA,IAAA,CAAK,cAAA,GAAiB,IAAI,iBAAA,CAAkB,EAAE,YAAY,CAAA;AAG1D,MAAA,IAAA,CAAK,cAAA,CAAe,cAAA,GAAiB,CAAC,KAAA,KAAU;AAC9C,QAAA,IAAI,MAAM,SAAA,EAAW;AACnB,UAAA,IAAA,CAAK,MAAA,CAAO,MAAM,gBAAA,EAAkB;AAAA,YAClC,IAAA,EAAM,MAAM,SAAA,CAAU,IAAA;AAAA,YACtB,QAAA,EAAU,MAAM,SAAA,CAAU;AAAA,WAC3B,CAAA;AAAA,QACH,CAAA,MAAO;AACL,UAAA,IAAA,CAAK,MAAA,CAAO,MAAM,wBAAwB,CAAA;AAAA,QAC5C;AAAA,MACF,CAAA;AAEA,MAAA,IAAA,CAAK,cAAA,CAAe,6BAA6B,MAAM;AACrD,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,UACV,uBAAA;AAAA,UACA,KAAK,cAAA,EAAgB;AAAA,SACvB;AAAA,MACF,CAAA;AAEA,MAAA,IAAA,CAAK,cAAA,CAAe,QAAA,CAAS,UAAA,EAAY,IAAA,CAAK,WAAW,CAAA;AAGzD,MAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,cAAA,CAAe,WAAA,EAAY;AACpD,MAAA,MAAM,IAAA,CAAK,cAAA,CAAe,mBAAA,CAAoB,KAAK,CAAA;AAEnD,MAAA,IAAI,CAAC,IAAA,CAAK,cAAA,CAAe,gBAAA,EAAkB;AACzC,QAAA,MAAM,IAAI,MAAM,oCAAoC,CAAA;AAAA,MACtD;AAGA,MAAA,IAAA,CAAK,MAAA,CAAO,MAAM,2BAA2B,CAAA;AAC7C,MAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,MAAA,CAAO,YAAA,CAAa;AAAA,QAC9C,MAAA,EAAQ;AAAA,UACN,IAAA,EAAM,OAAA;AAAA,UACN,GAAA,EAAK,IAAA,CAAK,cAAA,CAAe,gBAAA,CAAiB;AAAA,SAC5C;AAAA,QACA,UAAA,EAAY,IAAA,CAAK,mBAAA,CAAoB,WAAW,CAAA;AAAA,QAChD,SAAA,EAAW;AAAA,UACT,MAAA,EAAQ,KAAK,MAAA,CAAO,MAAA;AAAA,UACpB,OAAA,EAAS,IAAA,CAAK,MAAA,CAAO,OAAA,IAAW,QAAA,CAAS,OAAA;AAAA,UACzC,KAAA,EAAO,IAAA,CAAK,MAAA,CAAO,KAAA,IAAS,QAAA,CAAS,KAAA;AAAA,UACrC,kBAAA,EAAoB,KAAK,MAAA,CAAO;AAAA;AAClC,OACD,CAAA;AAED,MAAA,IAAA,CAAK,MAAA,CAAO,MAAM,4BAAA,EAA8B;AAAA,QAC9C,WAAW,QAAA,CAAS,SAAA;AAAA,QACpB,gBAAA,EAAkB,CAAC,CAAC,QAAA,CAAS;AAAA,OAC9B,CAAA;AAGD,MAAA,MAAM,IAAA,CAAK,cAAA,CAAe,oBAAA,CAAqB,QAAA,CAAS,MAAM,CAAA;AAE9D,MAAA,IAAA,CAAK,WAAW,QAAA,CAAS,SAAA;AACzB,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,iBAAA,EAAmB,IAAA,CAAK,QAAQ,CAAA;AAGjD,MAAA,IAAA,CAAK,cAAA,CAAe,QAAA,CAAS,KAAA,EAAO,WAAW,CAAA;AAG/C,MAAA,IAAA,CAAK,cAAA,CAAe,SAAS,SAAS,CAAA;AAEtC,MAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AAAA,IACnB,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAA,CAAK,iBAAiB,KAAK,CAAA;AACjC,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,UAAA,EAAsC;AAC3D,IAAA,IAAI,CAAC,UAAA,EAAY;AACf,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,UAAA,GAAc,aAAa,CAAA,GAAK,GAAA;AACtC,IAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,qCAAA,EAAuC,UAAA,EAAY,IAAI,CAAA;AAEzE,IAAA,IAAA,CAAK,iBAAA,GAAoB,MAAA,CAAO,WAAA,CAAY,YAAY;AACtD,MAAA,IAAI;AACF,QAAA,IAAI,KAAK,QAAA,EAAU;AACjB,UAAA,MAAM,IAAA,CAAK,MAAA,CAAO,UAAA,CAAW,IAAA,CAAK,QAAQ,CAAA;AAC1C,UAAA,IAAA,CAAK,MAAA,CAAO,MAAM,eAAe,CAAA;AAAA,QACnC;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,mBAAA,EAAqB,KAAK,CAAA;AAC5C,QAAA,MAAM,iBAAiB,IAAI,KAAA;AAAA,UACzB,qBAAqB,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,SAC7E;AACA,QAAA,MAAM,IAAA,CAAK,iBAAiB,cAAc,CAAA;AAAA,MAC5C;AAAA,IACF,GAAG,UAAU,CAAA;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,QAAA,EAAwB;AAC7C,IAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,kCAAA,EAAoC,QAAQ,CAAA;AAC9D,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,gBAAA,CAAiB,QAAQ,CAAA;AAEtD,IAAA,IAAA,CAAK,SAAA,CAAU,SAAS,MAAM;AAC5B,MAAA,IAAA,CAAK,MAAA,CAAO,MAAM,qBAAqB,CAAA;AACvC,MAAA,IAAI,KAAK,SAAA,EAAW;AAClB,QAAA,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,IAAA,CAAK,SAAA,CAAU,EAAE,SAAS,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,CAAC,CAAA;AAAA,MACrE;AAAA,IACF,CAAA;AAEA,IAAA,IAAA,CAAK,SAAA,CAAU,SAAA,GAAY,CAAC,KAAA,KAAU;AACpC,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAgC,IAAA,CAAK,KAAA,CAAM,KAAA,CAAM,IAAI,CAAA;AAC3D,QAAA,IAAA,CAAK,MAAA,CAAO,SAAS,MAAM,CAAA;AAAA,MAC7B,SAAS,KAAA,EAAO;AACd,QAAA,MAAM,aAAa,IAAI,KAAA;AAAA,UACrB,sCAAsC,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,SAC9F;AACA,QAAA,IAAA,CAAK,oBAAoB,UAAU,CAAA;AAAA,MACrC;AAAA,IACF,CAAA;AAEA,IAAA,IAAA,CAAK,SAAA,CAAU,UAAU,MAAM;AAC7B,MAAA,IAAA,CAAK,MAAA,CAAO,MAAM,0BAA0B,CAAA;AAC5C,MAAA,MAAM,KAAA,GAAQ,IAAI,KAAA,CAAM,0BAA0B,CAAA;AAClD,MAAA,IAAA,CAAK,iBAAiB,KAAK,CAAA;AAAA,IAC7B,CAAA;AAEA,IAAA,IAAA,CAAK,SAAA,CAAU,OAAA,GAAU,CAAC,KAAA,KAAU;AAClC,MAAA,IAAI,KAAK,SAAA,EAAW;AAClB,QAAA,IAAI,KAAA,CAAM,SAAS,IAAA,EAAM;AACvB,UAAA,IAAA,CAAK,MAAA,CAAO,MAAM,iCAAiC,CAAA;AACnD,UAAA,MAAM,QAAQ,IAAI,KAAA;AAAA,YAChB;AAAA,WACF;AACA,UAAA,IAAA,CAAK,iBAAiB,KAAK,CAAA;AAAA,QAC7B,CAAA,MAAO;AACL,UAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,gCAAA,EAAkC,KAAA,CAAM,IAAI,CAAA;AAC7D,UAAA,MAAM,KAAA,GAAQ,IAAI,KAAA,CAAM,+BAA+B,CAAA;AACvD,UAAA,IAAA,CAAK,iBAAiB,KAAK,CAAA;AAAA,QAC7B;AAAA,MACF,CAAA,MAAO;AACL,QAAA,IAAA,CAAK,MAAA,CAAO,MAAM,kBAAkB,CAAA;AAAA,MACtC;AAAA,IACF,CAAA;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAoB,KAAA,EAAoB;AAC9C,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,kBAAA,EAAoB,KAAA,CAAM,OAAO,CAAA;AAClD,IAAA,IAAI,IAAA,CAAK,OAAO,OAAA,EAAS;AACvB,MAAA,IAAA,CAAK,MAAA,CAAO,QAAQ,KAAK,CAAA;AAAA,IAC3B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBAAiB,KAAA,EAA+B;AAC5D,IAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,cAAA,EAAgB,KAAK,CAAA;AACvC,IAAA,MAAM,KAAK,OAAA,EAAQ;AACnB,IAAA,IAAA,CAAK,SAAA,GAAY,KAAA;AAEjB,IAAA,MAAM,eAAA,GACJ,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAE1D,IAAA,IAAI,IAAA,CAAK,OAAO,OAAA,EAAS;AACvB,MAAA,IAAA,CAAK,MAAA,CAAO,QAAQ,eAAe,CAAA;AAAA,IACrC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,MAAA,EAA+B;AAChD,IAAA,IAAI,CAAC,IAAA,CAAK,SAAA,IAAa,CAAC,KAAK,QAAA,EAAU;AACrC,MAAA,MAAM,IAAI,MAAM,2BAA2B,CAAA;AAAA,IAC7C;AAEA,IAAA,IAAI,CAAC,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,EAAU;AACzC,MAAA,MAAM,IAAIA,iBAAgB,mCAAmC,CAAA;AAAA,IAC/D;AAEA,IAAA,IAAA,CAAK,MAAA,CAAO,MAAM,iBAAiB,CAAA;AACnC,IAAA,MAAM,IAAA,CAAK,MAAA,CAAO,YAAA,CAAa,IAAA,CAAK,UAAU,MAAM,CAAA;AACpD,IAAA,IAAA,CAAK,MAAA,CAAO,KAAK,gBAAgB,CAAA;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAA,GAAsB;AAC1B,IAAA,IAAA,CAAK,MAAA,CAAO,KAAK,iBAAiB,CAAA;AAClC,IAAA,MAAM,KAAK,OAAA,EAAQ;AACnB,IAAA,IAAA,CAAK,SAAA,GAAY,KAAA;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,WAAA,GAA6B;AAC3B,IAAA,OAAO,IAAA,CAAK,QAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,cAAA,GAAqC;AACnC,IAAA,OAAO,IAAA,CAAK,WAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,QAAA,GAAoB;AAClB,IAAA,OAAO,IAAA,CAAK,SAAA;AAAA,EACd;AAAA,EAEA,MAAc,OAAA,GAAyB;AACrC,IAAA,IAAA,CAAK,MAAA,CAAO,MAAM,uBAAuB,CAAA;AAEzC,IAAA,IAAI,KAAK,iBAAA,EAAmB;AAC1B,MAAA,MAAA,CAAO,aAAA,CAAc,KAAK,iBAAiB,CAAA;AAC3C,MAAA,IAAA,CAAK,iBAAA,GAAoB,IAAA;AAAA,IAC3B;AAEA,IAAA,IAAI,KAAK,SAAA,EAAW;AAClB,MAAA,IAAA,CAAK,UAAU,KAAA,EAAM;AACrB,MAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AAAA,IACnB;AAEA,IAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,MAAA,IAAA,CAAK,eAAe,KAAA,EAAM;AAC1B,MAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AAAA,IACxB;AAEA,IAAA,IAAI,KAAK,WAAA,EAAa;AACpB,MAAA,IAAA,CAAK,WAAA,CAAY,WAAU,CAAE,OAAA,CAAQ,CAAC,KAAA,KAAU,KAAA,CAAM,MAAM,CAAA;AAC5D,MAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AAAA,IACrB;AAEA,IAAA,IAAI,KAAK,sBAAA,EAAwB;AAC/B,MAAA,oBAAA,CAAqB,KAAK,sBAAsB,CAAA;AAChD,MAAA,IAAA,CAAK,sBAAA,GAAyB,IAAA;AAAA,IAChC;AAEA,IAAA,IAAI,KAAK,aAAA,EAAe;AACtB,MAAA,IAAA,CAAK,cAAc,MAAA,EAAO;AAC1B,MAAA,IAAA,CAAK,aAAA,GAAgB,IAAA;AAAA,IACvB;AAEA,IAAA,IAAI,KAAK,YAAA,EAAc;AACrB,MAAA,IAAA,CAAK,aAAa,KAAA,EAAM;AACxB,MAAA,GAAA,CAAI,eAAA,CAAgB,IAAA,CAAK,YAAA,CAAa,GAAG,CAAA;AACzC,MAAA,IAAA,CAAK,aAAa,MAAA,EAAO;AACzB,MAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AAAA,IACtB;AAEA,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAChB,IAAA,IAAA,CAAK,MAAA,CAAO,MAAM,kBAAkB,CAAA;AAAA,EACtC;AACF","file":"index.mjs","sourcesContent":["export class ApiError extends Error {\n readonly statusCode?: number;\n readonly requestId?: string;\n readonly details?: any;\n\n constructor(\n message: string,\n statusCode?: number,\n requestId?: string,\n details?: any,\n ) {\n super(message);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.requestId = requestId;\n this.details = details;\n }\n}\n\nexport class UnauthorizedError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 401, requestId);\n this.name = \"UnauthorizedError\";\n }\n}\n\nexport class ValidationError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 422, requestId, details);\n this.name = \"ValidationError\";\n }\n}\n\nexport class NotFoundError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 404, requestId);\n this.name = \"NotFoundError\";\n }\n}\n\nexport class NetworkError extends ApiError {\n readonly cause?: Error;\n\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"NetworkError\";\n this.cause = cause;\n }\n}\n\nexport class ServerError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 500, requestId, details);\n this.name = \"ServerError\";\n }\n}\n","import type {\n StreamCreateRequest,\n StreamCreateResponse,\n KeepaliveResponse,\n StreamConfigResponse,\n ErrorResponse,\n} from \"./types\";\nimport {\n ApiError,\n ValidationError,\n NotFoundError,\n NetworkError,\n ServerError,\n UnauthorizedError,\n} from \"./errors\";\n\ntype ClientConfig = {\n baseUrl: string;\n apiKey: string;\n};\n\nexport class StreamClient {\n private baseUrl: string;\n private apiKey: string;\n\n constructor(config: ClientConfig) {\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new Error(\"apiKey is required and must be a string\");\n }\n\n this.baseUrl = config.baseUrl;\n this.apiKey = config.apiKey;\n }\n\n private async request<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n credentials: \"include\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const errorData: ErrorResponse = await response.json().catch(() => ({\n error: \"unknown_error\",\n message: response.statusText,\n }));\n\n const message = errorData.message || errorData.error;\n\n if (response.status === 401) {\n throw new UnauthorizedError(\n message || \"Invalid or revoked API key\",\n errorData.request_id,\n );\n }\n if (response.status === 422 || response.status === 400) {\n throw new ValidationError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n if (response.status === 404) {\n throw new NotFoundError(message, errorData.request_id);\n }\n if (response.status >= 500) {\n throw new ServerError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n\n throw new ApiError(\n message,\n response.status,\n errorData.request_id,\n errorData.details,\n );\n }\n\n return await response.json();\n } catch (error) {\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n throw new NetworkError(`Network error: ${error.message}`, error);\n }\n\n throw new NetworkError(\"Unknown network error\");\n }\n }\n async createStream(\n request: StreamCreateRequest,\n ): Promise<StreamCreateResponse> {\n return this.request<StreamCreateResponse>(\"/streams\", {\n method: \"POST\",\n body: JSON.stringify(request),\n });\n }\n\n async renewLease(streamId: string): Promise<KeepaliveResponse> {\n return this.request<KeepaliveResponse>(`/streams/${streamId}/keepalive`, {\n method: \"POST\",\n });\n }\n\n async updatePrompt(\n streamId: string,\n prompt: string,\n ): Promise<StreamConfigResponse> {\n return this.request<StreamConfigResponse>(\n `/streams/${streamId}/config/prompt`,\n {\n method: \"PATCH\",\n body: JSON.stringify({ prompt }),\n },\n );\n }\n\n connectWebSocket(streamId: string): WebSocket {\n const wsUrl = this.baseUrl\n .replace(\"http://\", \"ws://\")\n .replace(\"https://\", \"wss://\");\n return new WebSocket(`${wsUrl}/ws/streams/${streamId}`);\n }\n\n /**\n * Health check endpoint (for testing, uses internal port if available)\n * Note: This endpoint may not be available via the main API\n */\n async healthCheck(): Promise<string> {\n const url = `${this.baseUrl}/healthz`;\n const response = await fetch(url, {\n credentials: \"include\",\n });\n return response.text();\n }\n}\n","import { StreamClient } from \"./client\";\n\nimport {\n type StreamInferenceResult,\n type StreamProcessingConfig,\n type StreamSource,\n} from \"./types\";\n\n/**\n * Default configuration values for RealtimeVision\n */\nconst DEFAULTS = {\n BACKEND: \"overshoot\" as const,\n MODEL: \"Qwen/Qwen3-VL-30B-A3B-Instruct\",\n SOURCE: { type: \"camera\", cameraFacing: \"environment\" } as const,\n SAMPLING_RATIO: 0.1,\n CLIP_LENGTH_SECONDS: 1.0,\n DELAY_SECONDS: 1.0,\n FALLBACK_FPS: 30,\n ICE_SERVERS: [\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n ] as RTCIceServer[],\n} as const;\n\n/**\n * Validation constraints\n */\nconst CONSTRAINTS = {\n SAMPLING_RATIO: { min: 0, max: 1 },\n FPS: { min: 1, max: 120 },\n CLIP_LENGTH_SECONDS: { min: 0.1, max: 60 },\n DELAY_SECONDS: { min: 0, max: 60 },\n} as const;\n\n/**\n * Logger utility for controlled logging\n */\nclass Logger {\n private debugEnabled: boolean;\n\n constructor(debugEnabled: boolean = false) {\n this.debugEnabled = debugEnabled;\n }\n\n debug(...args: any[]): void {\n if (this.debugEnabled) {\n console.log(\"[RealtimeVision Debug]\", ...args);\n }\n }\n\n info(...args: any[]): void {\n console.log(\"[RealtimeVision]\", ...args);\n }\n\n warn(...args: any[]): void {\n console.warn(\"[RealtimeVision]\", ...args);\n }\n\n error(...args: any[]): void {\n console.error(\"[RealtimeVision]\", ...args);\n }\n}\n\nexport interface RealtimeVisionConfig {\n /**\n * Base URL for the API (e.g., \"https://api.example.com\")\n */\n apiUrl: string;\n\n /**\n * API key for authentication\n * Required for all API requests\n */\n apiKey: string;\n\n /**\n * The prompt/task to run on window segments of the stream.\n * This runs continuously (at a defined window interval).\n *\n * Examples:\n * - \"Read any visible text\"\n * - \"Detect objects and return as JSON array\"\n * - \"Describe facial expression\"\n */\n prompt: string;\n\n /**\n * Video source configuration\n * Defaults to camera with environment facing if not specified\n */\n source?: StreamSource;\n\n /**\n * Model backend to use\n */\n backend?: \"overshoot\";\n\n /**\n * Model name to use for inference\n */\n model?: string;\n\n /**\n * Optional JSON schema for structured output\n */\n outputSchema?: Record<string, any>;\n\n /**\n * Called when a new inference result arrives (~1 per second)\n */\n onResult: (result: StreamInferenceResult) => void;\n\n /**\n * Called when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Custom processing configuration\n * All fields are optional and will use defaults if not provided\n */\n processing?: {\n /**\n * Sampling ratio (0-1). Controls what fraction of frames are processed.\n */\n sampling_ratio?: number;\n /**\n * Frames per second (1-120)\n */\n fps?: number;\n /**\n * Clip length in seconds (0.1-60)\n */\n clip_length_seconds?: number;\n /**\n * Delay in seconds (0-60)\n */\n delay_seconds?: number;\n };\n\n /**\n * ICE servers for WebRTC connection\n * If not provided, uses default TURN servers\n */\n iceServers?: RTCIceServer[];\n\n /**\n * Enable debug logging\n * @default false\n */\n debug?: boolean;\n}\n\nclass ValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ValidationError\";\n }\n}\n\nexport class RealtimeVision {\n private config: RealtimeVisionConfig;\n private client: StreamClient;\n private logger: Logger;\n\n private mediaStream: MediaStream | null = null;\n private peerConnection: RTCPeerConnection | null = null;\n private webSocket: WebSocket | null = null;\n private streamId: string | null = null;\n private keepaliveInterval: number | null = null;\n private videoElement: HTMLVideoElement | null = null;\n private canvasElement: HTMLCanvasElement | null = null;\n private canvasAnimationFrameId: number | null = null;\n\n private isRunning = false;\n\n constructor(config: RealtimeVisionConfig) {\n this.validateConfig(config);\n this.config = config;\n this.logger = new Logger(config.debug ?? false);\n this.client = new StreamClient({\n baseUrl: config.apiUrl,\n apiKey: config.apiKey,\n });\n }\n\n /**\n * Validate configuration values\n */\n private validateConfig(config: RealtimeVisionConfig): void {\n if (!config.apiUrl || typeof config.apiUrl !== \"string\") {\n throw new ValidationError(\"apiUrl is required and must be a string\");\n }\n\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new ValidationError(\"apiKey is required and must be a string\");\n }\n\n if (!config.prompt || typeof config.prompt !== \"string\") {\n throw new ValidationError(\"prompt is required and must be a string\");\n }\n\n if (config.source) {\n if (config.source.type === \"camera\") {\n if (\n config.source.cameraFacing !== \"user\" &&\n config.source.cameraFacing !== \"environment\"\n ) {\n throw new ValidationError(\n 'cameraFacing must be \"user\" or \"environment\"',\n );\n }\n } else if (config.source.type === \"video\") {\n if (!(config.source.file instanceof File)) {\n throw new ValidationError(\"video source must provide a File object\");\n }\n } else {\n throw new ValidationError('source.type must be \"camera\" or \"video\"');\n }\n }\n\n if (config.processing?.sampling_ratio !== undefined) {\n const ratio = config.processing.sampling_ratio;\n if (\n ratio < CONSTRAINTS.SAMPLING_RATIO.min ||\n ratio > CONSTRAINTS.SAMPLING_RATIO.max\n ) {\n throw new ValidationError(\n `sampling_ratio must be between ${CONSTRAINTS.SAMPLING_RATIO.min} and ${CONSTRAINTS.SAMPLING_RATIO.max}`,\n );\n }\n }\n\n if (config.processing?.fps !== undefined) {\n const fps = config.processing.fps;\n if (fps < CONSTRAINTS.FPS.min || fps > CONSTRAINTS.FPS.max) {\n throw new ValidationError(\n `fps must be between ${CONSTRAINTS.FPS.min} and ${CONSTRAINTS.FPS.max}`,\n );\n }\n }\n\n if (config.processing?.clip_length_seconds !== undefined) {\n const clip = config.processing.clip_length_seconds;\n if (\n clip < CONSTRAINTS.CLIP_LENGTH_SECONDS.min ||\n clip > CONSTRAINTS.CLIP_LENGTH_SECONDS.max\n ) {\n throw new ValidationError(\n `clip_length_seconds must be between ${CONSTRAINTS.CLIP_LENGTH_SECONDS.min} and ${CONSTRAINTS.CLIP_LENGTH_SECONDS.max}`,\n );\n }\n }\n\n if (config.processing?.delay_seconds !== undefined) {\n const delay = config.processing.delay_seconds;\n if (\n delay < CONSTRAINTS.DELAY_SECONDS.min ||\n delay > CONSTRAINTS.DELAY_SECONDS.max\n ) {\n throw new ValidationError(\n `delay_seconds must be between ${CONSTRAINTS.DELAY_SECONDS.min} and ${CONSTRAINTS.DELAY_SECONDS.max}`,\n );\n }\n }\n }\n\n /**\n * Create media stream from the configured source\n */\n private async createMediaStream(source: StreamSource): Promise<MediaStream> {\n this.logger.debug(\"Creating media stream from source:\", source.type);\n\n switch (source.type) {\n case \"camera\":\n return await navigator.mediaDevices.getUserMedia({\n video: { facingMode: { ideal: source.cameraFacing } },\n audio: false,\n });\n\n case \"video\":\n const video = document.createElement(\"video\");\n video.src = URL.createObjectURL(source.file);\n video.muted = true;\n video.loop = true;\n video.playsInline = true;\n\n this.logger.debug(\"Loading video file:\", source.file.name);\n\n // Wait for video to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(\"Video loading timeout after 10 seconds\"));\n }, 10000);\n\n video.onloadedmetadata = () => {\n clearTimeout(timeout);\n this.logger.debug(\"Video metadata loaded\");\n resolve();\n };\n\n video.onerror = (e) => {\n clearTimeout(timeout);\n this.logger.error(\"Video loading error:\", e);\n reject(new Error(\"Failed to load video file\"));\n };\n\n if (video.readyState >= 1) {\n clearTimeout(timeout);\n resolve();\n }\n });\n\n await video.play();\n this.logger.debug(\"Video playback started\");\n\n let stream: MediaStream;\n\n // Check if captureStream is supported (Chrome, Firefox)\n if (typeof video.captureStream === \"function\") {\n this.logger.debug(\"Using native video.captureStream()\");\n stream = video.captureStream();\n } else {\n // Safari fallback: use canvas to capture the video stream\n this.logger.debug(\n \"captureStream not supported, using canvas fallback for Safari\",\n );\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = video.videoWidth || 640;\n canvas.height = video.videoHeight || 480;\n const ctx = canvas.getContext(\"2d\");\n\n if (!ctx) {\n throw new Error(\"Failed to get canvas 2D context\");\n }\n\n // Draw video frames to canvas continuously\n const drawFrame = () => {\n if (!video.paused && !video.ended) {\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n this.canvasAnimationFrameId = requestAnimationFrame(drawFrame);\n }\n };\n drawFrame();\n\n // Capture stream from canvas (30 fps)\n stream = canvas.captureStream(30);\n this.canvasElement = canvas;\n }\n\n if (!stream) {\n throw new Error(\"Failed to capture video stream\");\n }\n\n const videoTracks = stream.getVideoTracks();\n if (videoTracks.length === 0) {\n throw new Error(\"Video stream has no video tracks\");\n }\n\n this.videoElement = video;\n return stream;\n\n default:\n throw new Error(`Unknown source type: ${(source as any).type}`);\n }\n }\n\n /**\n * Get FPS from media stream\n */\n private async getStreamFps(\n stream: MediaStream | null,\n source: StreamSource,\n ): Promise<number> {\n if (!stream) {\n this.logger.warn(\"Stream is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTracks = stream.getVideoTracks();\n if (!videoTracks || videoTracks.length === 0) {\n this.logger.warn(\"No video tracks found, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTrack = videoTracks[0];\n if (!videoTrack) {\n this.logger.warn(\"First video track is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n // For camera sources, get FPS from track settings\n if (source.type === \"camera\") {\n const settings = videoTrack.getSettings();\n const fps = settings.frameRate ?? DEFAULTS.FALLBACK_FPS;\n this.logger.debug(\"Detected camera FPS:\", fps);\n return fps;\n }\n\n // For video file sources, try to get FPS from video element\n if (source.type === \"video\" && this.videoElement) {\n await new Promise<void>((resolve, reject) => {\n if (this.videoElement!.readyState >= 1) {\n resolve();\n } else {\n this.videoElement!.onloadedmetadata = () => resolve();\n this.videoElement!.onerror = () =>\n reject(new Error(\"Failed to load video metadata\"));\n }\n });\n\n // For video files, use fallback FPS or user-specified config\n this.logger.debug(\"Using fallback FPS for video file\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n return DEFAULTS.FALLBACK_FPS;\n }\n\n /**\n * Get processing configuration with defaults applied\n */\n private getProcessingConfig(detectedFps: number): StreamProcessingConfig {\n const userProcessing = this.config.processing || {};\n\n return {\n sampling_ratio: userProcessing.sampling_ratio ?? DEFAULTS.SAMPLING_RATIO,\n fps: userProcessing.fps ?? detectedFps,\n clip_length_seconds:\n userProcessing.clip_length_seconds ?? DEFAULTS.CLIP_LENGTH_SECONDS,\n delay_seconds: userProcessing.delay_seconds ?? DEFAULTS.DELAY_SECONDS,\n };\n }\n\n /**\n * Get the effective source configuration\n */\n private getSource(): StreamSource {\n return this.config.source ?? DEFAULTS.SOURCE;\n }\n\n /**\n * Start the vision stream\n */\n async start(): Promise<void> {\n if (this.isRunning) {\n throw new Error(\"Vision stream already running\");\n }\n\n try {\n const source = this.getSource();\n this.logger.debug(\"Starting stream with source type:\", source.type);\n\n if (source.type === \"video\") {\n this.logger.debug(\"Video file:\", {\n name: source.file.name,\n size: source.file.size,\n type: source.file.type,\n });\n\n if (!source.file || !(source.file instanceof File)) {\n throw new Error(\"Invalid video file\");\n }\n }\n\n // Create media stream\n this.mediaStream = await this.createMediaStream(source);\n const videoTrack = this.mediaStream.getVideoTracks()[0];\n if (!videoTrack) {\n throw new Error(\"No video track available\");\n }\n\n // Get FPS for the stream\n const detectedFps = await this.getStreamFps(this.mediaStream, source);\n\n // Set up WebRTC peer connection\n const iceServers = this.config.iceServers ?? DEFAULTS.ICE_SERVERS;\n this.logger.debug(\"Creating peer connection with ICE servers\");\n this.peerConnection = new RTCPeerConnection({ iceServers });\n\n // Set up ICE logging\n this.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n this.logger.debug(\"ICE candidate:\", {\n type: event.candidate.type,\n protocol: event.candidate.protocol,\n });\n } else {\n this.logger.debug(\"ICE gathering complete\");\n }\n };\n\n this.peerConnection.oniceconnectionstatechange = () => {\n this.logger.debug(\n \"ICE connection state:\",\n this.peerConnection?.iceConnectionState,\n );\n };\n\n this.peerConnection.addTrack(videoTrack, this.mediaStream);\n\n // Create and set local offer\n const offer = await this.peerConnection.createOffer();\n await this.peerConnection.setLocalDescription(offer);\n\n if (!this.peerConnection.localDescription) {\n throw new Error(\"Failed to create local description\");\n }\n\n // Create stream on server\n this.logger.debug(\"Creating stream on server\");\n const response = await this.client.createStream({\n webrtc: {\n type: \"offer\",\n sdp: this.peerConnection.localDescription.sdp,\n },\n processing: this.getProcessingConfig(detectedFps),\n inference: {\n prompt: this.config.prompt,\n backend: this.config.backend ?? DEFAULTS.BACKEND,\n model: this.config.model ?? DEFAULTS.MODEL,\n output_schema_json: this.config.outputSchema,\n },\n });\n\n this.logger.debug(\"Backend response received:\", {\n stream_id: response.stream_id,\n has_turn_servers: !!response.turn_servers,\n });\n\n // Set remote description\n await this.peerConnection.setRemoteDescription(response.webrtc);\n\n this.streamId = response.stream_id;\n this.logger.info(\"Stream started:\", this.streamId);\n\n // Set up keepalive\n this.setupKeepalive(response.lease?.ttl_seconds);\n\n // Connect WebSocket for results\n this.setupWebSocket(response.stream_id);\n\n this.isRunning = true;\n } catch (error) {\n await this.handleFatalError(error);\n throw error;\n }\n }\n\n /**\n * Set up keepalive interval with error handling\n */\n private setupKeepalive(ttlSeconds: number | undefined): void {\n if (!ttlSeconds) {\n return;\n }\n\n const intervalMs = (ttlSeconds / 2) * 1000;\n this.logger.debug(\"Setting up keepalive with interval:\", intervalMs, \"ms\");\n\n this.keepaliveInterval = window.setInterval(async () => {\n try {\n if (this.streamId) {\n await this.client.renewLease(this.streamId);\n this.logger.debug(\"Lease renewed\");\n }\n } catch (error) {\n this.logger.error(\"Keepalive failed:\", error);\n const keepaliveError = new Error(\n `Keepalive failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n await this.handleFatalError(keepaliveError);\n }\n }, intervalMs);\n }\n\n /**\n * Set up WebSocket connection with error handling\n */\n private setupWebSocket(streamId: string): void {\n this.logger.debug(\"Connecting WebSocket for stream:\", streamId);\n this.webSocket = this.client.connectWebSocket(streamId);\n\n this.webSocket.onopen = () => {\n this.logger.debug(\"WebSocket connected\");\n if (this.webSocket) {\n this.webSocket.send(JSON.stringify({ api_key: this.config.apiKey }));\n }\n };\n\n this.webSocket.onmessage = (event) => {\n try {\n const result: StreamInferenceResult = JSON.parse(event.data);\n this.config.onResult(result);\n } catch (error) {\n const parseError = new Error(\n `Failed to parse WebSocket message: ${error instanceof Error ? error.message : String(error)}`,\n );\n this.handleNonFatalError(parseError);\n }\n };\n\n this.webSocket.onerror = () => {\n this.logger.error(\"WebSocket error occurred\");\n const error = new Error(\"WebSocket error occurred\");\n this.handleFatalError(error);\n };\n\n this.webSocket.onclose = (event) => {\n if (this.isRunning) {\n if (event.code === 1008) {\n this.logger.error(\"WebSocket authentication failed\");\n const error = new Error(\n \"WebSocket authentication failed: Invalid or revoked API key\",\n );\n this.handleFatalError(error);\n } else {\n this.logger.warn(\"WebSocket closed unexpectedly:\", event.code);\n const error = new Error(\"WebSocket closed unexpectedly\");\n this.handleFatalError(error);\n }\n } else {\n this.logger.debug(\"WebSocket closed\");\n }\n };\n }\n\n /**\n * Handle non-fatal errors (report but don't stop stream)\n */\n private handleNonFatalError(error: Error): void {\n this.logger.warn(\"Non-fatal error:\", error.message);\n if (this.config.onError) {\n this.config.onError(error);\n }\n }\n\n /**\n * Handle fatal errors (stop stream and report)\n */\n private async handleFatalError(error: unknown): Promise<void> {\n this.logger.error(\"Fatal error:\", error);\n await this.cleanup();\n this.isRunning = false;\n\n const normalizedError =\n error instanceof Error ? error : new Error(String(error));\n\n if (this.config.onError) {\n this.config.onError(normalizedError);\n }\n }\n\n /**\n * Update the prompt/task while stream is running\n */\n async updatePrompt(prompt: string): Promise<void> {\n if (!this.isRunning || !this.streamId) {\n throw new Error(\"Vision stream not running\");\n }\n\n if (!prompt || typeof prompt !== \"string\") {\n throw new ValidationError(\"prompt must be a non-empty string\");\n }\n\n this.logger.debug(\"Updating prompt\");\n await this.client.updatePrompt(this.streamId, prompt);\n this.logger.info(\"Prompt updated\");\n }\n\n /**\n * Stop the vision stream and clean up resources\n */\n async stop(): Promise<void> {\n this.logger.info(\"Stopping stream\");\n await this.cleanup();\n this.isRunning = false;\n }\n\n /**\n * Get the current stream ID\n */\n getStreamId(): string | null {\n return this.streamId;\n }\n\n /**\n * Get the media stream (for displaying video preview)\n */\n getMediaStream(): MediaStream | null {\n return this.mediaStream;\n }\n\n /**\n * Check if the stream is running\n */\n isActive(): boolean {\n return this.isRunning;\n }\n\n private async cleanup(): Promise<void> {\n this.logger.debug(\"Cleaning up resources\");\n\n if (this.keepaliveInterval) {\n window.clearInterval(this.keepaliveInterval);\n this.keepaliveInterval = null;\n }\n\n if (this.webSocket) {\n this.webSocket.close();\n this.webSocket = null;\n }\n\n if (this.peerConnection) {\n this.peerConnection.close();\n this.peerConnection = null;\n }\n\n if (this.mediaStream) {\n this.mediaStream.getTracks().forEach((track) => track.stop());\n this.mediaStream = null;\n }\n\n if (this.canvasAnimationFrameId) {\n cancelAnimationFrame(this.canvasAnimationFrameId);\n this.canvasAnimationFrameId = null;\n }\n\n if (this.canvasElement) {\n this.canvasElement.remove();\n this.canvasElement = null;\n }\n\n if (this.videoElement) {\n this.videoElement.pause();\n URL.revokeObjectURL(this.videoElement.src);\n this.videoElement.remove();\n this.videoElement = null;\n }\n\n this.streamId = null;\n this.logger.debug(\"Cleanup complete\");\n }\n}\n"]}
|