@reactor-team/js-sdk 2.4.0 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/core/types.ts","../src/utils/webrtc.ts","../src/core/CoordinatorClient.ts","../src/core/LocalCoordinatorClient.ts","../src/core/GPUMachineClient.ts","../src/core/Reactor.ts","../src/react/ReactorProvider.tsx","../src/core/store.ts","../src/react/hooks.ts","../src/react/ReactorView.tsx","../src/react/ReactorController.tsx","../src/react/WebcamStream.tsx","../src/utils/tokens.ts"],"sourcesContent":["export * from \"./core/Reactor\";\nexport * from \"./react/ReactorProvider\";\nexport * from \"./react/ReactorView\";\nexport * from \"./react/ReactorController\";\nexport * from \"./react/WebcamStream\";\nexport * from \"./react/hooks\";\nexport * from \"./types\";\nexport * from \"./utils/tokens\";\n","export type ReactorStatus =\n | \"disconnected\" // Not connected to anything\n | \"connecting\" // Establishing connection to coordinator\n | \"waiting\" // Connected to coordinator, waiting for GPU assignment\n | \"ready\"; // Connected to GPU machine, can send/receive messages\n\n/**\n * The message scope identifies the envelope layer a data channel message belongs to.\n * - \"application\": model-defined commands (client->runtime) and model-emitted payloads (runtime->client).\n * - \"runtime\": platform-level control messages (e.g., capabilities exchange).\n */\nexport type MessageScope = \"application\" | \"runtime\";\n\n// Error information\nexport interface ReactorError {\n code: string;\n message: string;\n timestamp: number;\n recoverable: boolean;\n component: \"coordinator\" | \"gpu\" | \"livekit\";\n retryAfter?: number; // Suggested retry delay in seconds\n}\n\nexport class ConflictError extends Error {\n constructor(message: string) {\n super(message);\n }\n}\n\n// Enhanced state with metadata\nexport interface ReactorState {\n status: ReactorStatus;\n lastError?: ReactorError; // Most recent error\n}\n\n/**\n * Options for configuring the connect polling behavior.\n */\nexport interface ConnectOptions {\n /** Maximum number of SDP polling attempts before giving up. Default: 6. */\n maxAttempts?: number;\n}\n\nexport interface ConnectionStats {\n /** ICE candidate-pair round-trip time in milliseconds */\n rtt?: number;\n /** ICE candidate type: \"host\", \"srflx\", \"prflx\", or \"relay\" (TURN) */\n candidateType?: string;\n /** Estimated available outgoing bitrate in bits/second */\n availableOutgoingBitrate?: number;\n /** Received video frames per second */\n framesPerSecond?: number;\n /** Ratio of packets lost (0-1) */\n packetLossRatio?: number;\n /** Network jitter in seconds (from inbound-rtp) */\n jitter?: number;\n timestamp: number;\n}\n\nexport type ReactorEvent =\n | \"statusChanged\" //updates on the reactor status\n | \"sessionIdChanged\" //updates on the session ID.\n | \"message\" //application-scoped messages from the model\n | \"runtimeMessage\" //internal platform-level control messages (e.g. capabilities)\n | \"streamChanged\" //video stream has changed (LiveKit)\n | \"error\" //error events with ReactorError details\n | \"sessionExpirationChanged\" //session expiration has changed\n | \"statsUpdate\"; //WebRTC stats update (RTT, etc.)\n","/**\n * Internal types for the Reactor SDK.\n */\n\nimport { z } from \"zod\";\n\nexport enum SessionState {\n CREATED,\n PENDING,\n SUSPENDED,\n WAITING,\n ACTIVE,\n INACTIVE,\n CLOSED,\n}\n\nexport const ModelSchema = z.object({\n name: z.string(),\n});\n\n// Schema used for the HTTP POST request to start a session from the CLIENT to the COORDINATOR.\nexport const CreateSessionRequestSchema = z.object({\n model: ModelSchema,\n sdp_offer: z.string(),\n extra_args: z.record(z.string(), z.any()), // Dictionary\n});\n\n// Schema used to return the session ID that was created.\nexport const CreateSessionResponseSchema = z.object({\n session_id: z.uuidv4(),\n});\n\n// GET /sessions/{session_id}/info\nexport const SessionStatusResponseSchema = z.object({\n session_id: z.uuidv4(),\n state: SessionState,\n});\n\n// Response to GET /session/{session_id} request\nexport const SessionInfoResponseSchema = SessionStatusResponseSchema.extend({\n session_info: CreateSessionRequestSchema.extend({\n session_id: z.uuidv4(),\n }),\n});\n\n// SDPParamsRequest is the request body for PUT /sessions/{session_id}/sdp_params\nexport const SDPParamsRequestSchema = z.object({\n sdp_offer: z.string(),\n extra_args: z.record(z.string(), z.any()), // Dictionary\n});\n\n// SDPParamsResponse is the response for GET /sessions/{session_id}/sdp_params\n// and for for PUT /sessions/{session_id}/sdp_params.\nexport const SDPParamsResponseSchema = z.object({\n sdp_answer: z.string(),\n extra_args: z.record(z.string(), z.any()), // Dictionary\n});\n\n// Response from GET /ice_servers endpoint (coordinator)\nexport const IceServersResponseSchema = z.object({\n ice_servers: z.array(\n z.object({\n uris: z.array(z.string()),\n credentials: z\n .object({\n username: z.string(),\n password: z.string(),\n })\n .optional(),\n })\n ),\n});\n\n// Internal connection status for individual components\nexport type InternalConnectionStatus =\n | \"disconnected\"\n | \"connecting\"\n | \"connected\"\n | \"error\";\n\n// Inferred types from Zod schemas\nexport type CreateSessionRequest = z.infer<typeof CreateSessionRequestSchema>;\nexport type CreateSessionResponse = z.infer<typeof CreateSessionResponseSchema>;\n\nexport type SDPParamsResponse = z.infer<typeof SDPParamsResponseSchema>;\nexport type SDPParamsRequest = z.infer<typeof SDPParamsRequestSchema>;\n\nexport type SessionInfoResponse = z.infer<typeof SessionInfoResponseSchema>;\nexport type SessionStatusResponse = z.infer<typeof SessionStatusResponseSchema>;\n\nexport type IceServersResponse = z.infer<typeof IceServersResponseSchema>;\n","/**\n * Stateless WebRTC utility functions for SDP exchange and peer connection management.\n * Uses @roamhq/wrtc for stable Node.js WebRTC support.\n */\n\nimport { IceServersResponse } from \"../core/types\";\nimport type { MessageScope, ConnectionStats } from \"../types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Configuration\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface WebRTCConfig {\n iceServers: RTCIceServer[];\n dataChannelLabel?: string;\n}\n\nconst DEFAULT_DATA_CHANNEL_LABEL = \"data\";\n\n// Force relay mode for testing TURN servers - set to true to force all traffic through TURN\nconst FORCE_RELAY_MODE = false;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Peer Connection Creation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Creates a new RTCPeerConnection with the specified configuration.\n * @param config WebRTC configuration with required iceServers\n */\nexport function createPeerConnection(config: WebRTCConfig): RTCPeerConnection {\n return new RTCPeerConnection({\n iceServers: config.iceServers,\n iceTransportPolicy: FORCE_RELAY_MODE ? \"relay\" : \"all\",\n });\n}\n\n/**\n * Creates a data channel on the peer connection.\n */\nexport function createDataChannel(\n pc: RTCPeerConnection,\n label?: string\n): RTCDataChannel {\n return pc.createDataChannel(label ?? DEFAULT_DATA_CHANNEL_LABEL);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// SDP Offer/Answer\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Creates an SDP offer on the peer connection.\n * Waits for ICE gathering to complete before returning.\n */\nexport async function createOffer(pc: RTCPeerConnection): Promise<string> {\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n\n await waitForIceGathering(pc);\n\n const localDescription = pc.localDescription;\n if (!localDescription) {\n throw new Error(\"Failed to create local description\");\n }\n\n return localDescription.sdp;\n}\n\n/**\n * Creates an SDP answer in response to a received offer.\n * Waits for ICE gathering to complete before returning.\n */\nexport async function createAnswer(\n pc: RTCPeerConnection,\n offer: string\n): Promise<string> {\n await setRemoteDescription(pc, offer);\n\n const answer = await pc.createAnswer();\n await pc.setLocalDescription(answer);\n\n await waitForIceGathering(pc);\n\n const localDescription = pc.localDescription;\n if (!localDescription) {\n throw new Error(\"Failed to create local description\");\n }\n\n return localDescription.sdp;\n}\n\n/**\n * Sets the remote description on the peer connection.\n */\nexport async function setRemoteDescription(\n pc: RTCPeerConnection,\n sdp: string\n): Promise<void> {\n const sessionDescription = new RTCSessionDescription({\n sdp: sdp,\n type: \"answer\",\n });\n await pc.setRemoteDescription(sessionDescription);\n}\n\n/**\n * Gets the local SDP description from the peer connection.\n */\nexport function getLocalDescription(pc: RTCPeerConnection): string | undefined {\n const desc = pc.localDescription;\n if (!desc) return undefined;\n return desc.sdp;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ICE Handling\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Transforms ICE servers from the coordinator API format to RTCIceServer format.\n * @param response The parsed IceServersResponse from the coordinator\n * @returns Array of RTCIceServer objects for WebRTC peer connection configuration\n */\nexport function transformIceServers(\n response: IceServersResponse\n): RTCIceServer[] {\n return response.ice_servers.map((server) => {\n const rtcServer: RTCIceServer = {\n urls: server.uris,\n };\n if (server.credentials) {\n rtcServer.username = server.credentials.username;\n rtcServer.credential = server.credentials.password;\n }\n return rtcServer;\n });\n}\n\n/**\n * Adds an ICE candidate to the peer connection.\n */\nexport async function addIceCandidate(\n pc: RTCPeerConnection,\n candidate: RTCIceCandidateInit\n): Promise<void> {\n await pc.addIceCandidate(new RTCIceCandidate(candidate));\n}\n\n/**\n * Waits for ICE gathering to complete with a timeout.\n */\nexport function waitForIceGathering(\n pc: RTCPeerConnection,\n timeoutMs: number = 5000\n): Promise<void> {\n return new Promise((resolve) => {\n if (pc.iceGatheringState === \"complete\") {\n resolve();\n return;\n }\n\n const onGatheringStateChange = () => {\n if (pc.iceGatheringState === \"complete\") {\n pc.removeEventListener(\n \"icegatheringstatechange\",\n onGatheringStateChange\n );\n resolve();\n }\n };\n\n pc.addEventListener(\"icegatheringstatechange\", onGatheringStateChange);\n\n // Timeout to prevent hanging forever\n setTimeout(() => {\n pc.removeEventListener(\"icegatheringstatechange\", onGatheringStateChange);\n resolve();\n }, timeoutMs);\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Track Management\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Adds a track to the peer connection.\n */\nexport function addTrack(\n pc: RTCPeerConnection,\n track: MediaStreamTrack,\n stream?: MediaStream\n): RTCRtpSender {\n const mediaStream = stream ?? new MediaStream([track]);\n return pc.addTrack(track, mediaStream);\n}\n\n/**\n * Removes a track from the peer connection by its sender.\n */\nexport function removeTrack(pc: RTCPeerConnection, sender: RTCRtpSender): void {\n pc.removeTrack(sender);\n}\n\n/**\n * Finds the sender for a specific track.\n */\nexport function findSenderForTrack(\n pc: RTCPeerConnection,\n track: MediaStreamTrack\n): RTCRtpSender | undefined {\n return pc.getSenders().find((sender) => sender.track === track);\n}\n\n/**\n * Removes all tracks from the peer connection.\n */\nexport function removeAllTracks(pc: RTCPeerConnection): void {\n for (const sender of pc.getSenders()) {\n pc.removeTrack(sender);\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Data Channel Messaging\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Sends a message through a data channel wrapped in the two-level envelope.\n *\n * Wire format:\n * { scope: \"application\"|\"runtime\", data: { type: <command>, data: <payload> } }\n *\n * @param channel The RTCDataChannel to send on.\n * @param command Inner command/message type (e.g. \"set_prompt\", \"requestCapabilities\").\n * @param data Payload for the command.\n * @param scope Outer envelope scope – defaults to \"application\".\n */\nexport function sendMessage(\n channel: RTCDataChannel,\n command: string,\n data: any,\n scope: MessageScope = \"application\"\n): void {\n if (channel.readyState !== \"open\") {\n throw new Error(`Data channel not open: ${channel.readyState}`);\n }\n const jsonData = typeof data === \"string\" ? JSON.parse(data) : data;\n const inner = { type: command, data: jsonData };\n const payload = { scope, data: inner };\n channel.send(JSON.stringify(payload));\n}\n\n/**\n * Parses a received data channel message, attempting JSON parse.\n */\nexport function parseMessage(data: unknown): unknown {\n if (typeof data === \"string\") {\n try {\n return JSON.parse(data);\n } catch {\n return data;\n }\n }\n return data;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Connection State\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Checks if the peer connection is in a connected state.\n */\nexport function isConnected(pc: RTCPeerConnection): boolean {\n return pc.connectionState === \"connected\";\n}\n\n/**\n * Checks if the peer connection is closed or failed.\n */\nexport function isClosed(pc: RTCPeerConnection): boolean {\n return pc.connectionState === \"closed\" || pc.connectionState === \"failed\";\n}\n\n/**\n * Closes the peer connection and cleans up.\n */\nexport function closePeerConnection(pc: RTCPeerConnection): void {\n pc.close();\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Stats Extraction\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Extracts ConnectionStats from an RTCStatsReport.\n * Reads candidate-pair, local-candidate, and inbound-rtp (video) reports.\n */\nexport function extractConnectionStats(\n report: RTCStatsReport\n): ConnectionStats {\n let rtt: number | undefined;\n let availableOutgoingBitrate: number | undefined;\n let localCandidateId: string | undefined;\n let framesPerSecond: number | undefined;\n let jitter: number | undefined;\n let packetLossRatio: number | undefined;\n\n report.forEach((stat) => {\n if (stat.type === \"candidate-pair\" && stat.state === \"succeeded\") {\n if (stat.currentRoundTripTime !== undefined) {\n rtt = stat.currentRoundTripTime * 1000;\n }\n if (stat.availableOutgoingBitrate !== undefined) {\n availableOutgoingBitrate = stat.availableOutgoingBitrate;\n }\n localCandidateId = stat.localCandidateId;\n }\n\n if (stat.type === \"inbound-rtp\" && stat.kind === \"video\") {\n if (stat.framesPerSecond !== undefined) {\n framesPerSecond = stat.framesPerSecond;\n }\n if (stat.jitter !== undefined) {\n jitter = stat.jitter;\n }\n if (\n stat.packetsReceived !== undefined &&\n stat.packetsLost !== undefined &&\n stat.packetsReceived + stat.packetsLost > 0\n ) {\n packetLossRatio =\n stat.packetsLost / (stat.packetsReceived + stat.packetsLost);\n }\n }\n });\n\n let candidateType: string | undefined;\n if (localCandidateId) {\n const localCandidate = report.get(localCandidateId);\n if (localCandidate?.candidateType) {\n candidateType = localCandidate.candidateType;\n }\n }\n\n return {\n rtt,\n candidateType,\n availableOutgoingBitrate,\n framesPerSecond,\n packetLossRatio,\n jitter,\n timestamp: Date.now(),\n };\n}\n","/**\n * The CoordinatorClient is responsible for handling the connection to the coordinator\n * via HTTP requests and WebRTC signaling.\n */\n\nimport {\n CreateSessionRequest,\n CreateSessionResponse,\n IceServersResponseSchema,\n SDPParamsRequest,\n SDPParamsResponse,\n SessionInfoResponse,\n} from \"./types\";\nimport { transformIceServers } from \"../utils/webrtc\";\n\nexport interface CoordinatorClientOptions {\n baseUrl: string;\n jwtToken: string;\n model: string;\n}\n\n// Polling configuration\nconst INITIAL_BACKOFF_MS = 500;\nconst MAX_BACKOFF_MS = 15000;\nconst BACKOFF_MULTIPLIER = 2;\nconst DEFAULT_MAX_ATTEMPTS = 6;\n\nexport class CoordinatorClient {\n private baseUrl: string;\n private jwtToken: string;\n private model: string;\n private currentSessionId?: string;\n\n constructor(options: CoordinatorClientOptions) {\n this.baseUrl = options.baseUrl;\n this.jwtToken = options.jwtToken;\n this.model = options.model;\n }\n\n /**\n * Returns the authorization header with JWT Bearer token\n */\n private getAuthHeaders(): HeadersInit {\n return {\n Authorization: `Bearer ${this.jwtToken}`,\n };\n }\n\n /**\n * Fetches ICE servers from the coordinator.\n * @returns Array of RTCIceServer objects for WebRTC peer connection configuration\n */\n async getIceServers(): Promise<RTCIceServer[]> {\n console.debug(\"[CoordinatorClient] Fetching ICE servers...\");\n\n const response = await fetch(\n `${this.baseUrl}/ice_servers?model=${this.model}`,\n {\n method: \"GET\",\n headers: this.getAuthHeaders(),\n }\n );\n\n if (!response.ok) {\n throw new Error(`Failed to fetch ICE servers: ${response.status}`);\n }\n\n const data = await response.json();\n const parsed = IceServersResponseSchema.parse(data);\n const iceServers = transformIceServers(parsed);\n\n console.debug(\n \"[CoordinatorClient] Received ICE servers:\",\n iceServers.length\n );\n return iceServers;\n }\n\n /**\n * Creates a new session with the coordinator.\n * Expects a 200 response and stores the session ID.\n * @returns The session ID\n */\n async createSession(sdp_offer: string): Promise<string> {\n console.debug(\"[CoordinatorClient] Creating session...\");\n\n const requestBody: CreateSessionRequest = {\n model: { name: this.model },\n sdp_offer: sdp_offer,\n extra_args: {},\n };\n\n const response = await fetch(`${this.baseUrl}/sessions`, {\n method: \"POST\",\n headers: {\n ...this.getAuthHeaders(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to create session: ${response.status} ${errorText}`\n );\n }\n\n const data: CreateSessionResponse = await response.json();\n this.currentSessionId = data.session_id;\n\n console.debug(\n \"[CoordinatorClient] Session created with ID:\",\n this.currentSessionId\n );\n\n return data.session_id;\n }\n\n /**\n * Gets the current session information from the coordinator.\n * @returns The session data (untyped for now)\n */\n async getSession(): Promise<SessionInfoResponse> {\n if (!this.currentSessionId) {\n throw new Error(\"No active session. Call createSession() first.\");\n }\n\n console.debug(\n \"[CoordinatorClient] Getting session info for:\",\n this.currentSessionId\n );\n\n const response = await fetch(\n `${this.baseUrl}/sessions/${this.currentSessionId}`,\n {\n method: \"GET\",\n headers: this.getAuthHeaders(),\n }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to get session: ${response.status} ${errorText}`);\n }\n\n const data: SessionInfoResponse = await response.json();\n\n return data;\n }\n\n /**\n * Terminates the current session by sending a DELETE request to the coordinator.\n * @throws Error if no active session exists or if the request fails (except for 404)\n */\n async terminateSession(): Promise<void> {\n if (!this.currentSessionId) {\n throw new Error(\"No active session. Call createSession() first.\");\n }\n\n console.debug(\n \"[CoordinatorClient] Terminating session:\",\n this.currentSessionId\n );\n\n const response = await fetch(\n `${this.baseUrl}/sessions/${this.currentSessionId}`,\n {\n method: \"DELETE\",\n headers: this.getAuthHeaders(),\n }\n );\n\n if (response.ok) {\n this.currentSessionId = undefined;\n return;\n }\n\n if (response.status === 404) {\n // Session doesn't exist on server, clear local state to stay consistent\n console.debug(\n \"[CoordinatorClient] Session not found on server, clearing local state:\",\n this.currentSessionId\n );\n this.currentSessionId = undefined;\n return;\n }\n\n // For other error codes, throw without clearing state (might warrant retry)\n const errorText = await response.text();\n throw new Error(\n `Failed to terminate session: ${response.status} ${errorText}`\n );\n }\n\n /**\n * Get the current session ID\n */\n getSessionId(): string | undefined {\n return this.currentSessionId;\n }\n\n /**\n * Sends an SDP offer to the server for reconnection.\n * @param sessionId - The session ID to connect to\n * @param sdpOffer - The SDP offer from the local WebRTC peer connection\n * @returns The SDP answer if ready (200), or null if pending (202)\n */\n private async sendSdpOffer(\n sessionId: string,\n sdpOffer: string\n ): Promise<string | null> {\n console.debug(\n \"[CoordinatorClient] Sending SDP offer for session:\",\n sessionId\n );\n\n const requestBody: SDPParamsRequest = {\n sdp_offer: sdpOffer,\n extra_args: {},\n };\n\n const response = await fetch(\n `${this.baseUrl}/sessions/${sessionId}/sdp_params`,\n {\n method: \"PUT\",\n headers: {\n ...this.getAuthHeaders(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(requestBody),\n }\n );\n\n if (response.status === 200) {\n const answerData: SDPParamsResponse = await response.json();\n console.debug(\"[CoordinatorClient] Received SDP answer immediately\");\n return answerData.sdp_answer;\n }\n\n if (response.status === 202) {\n console.debug(\n \"[CoordinatorClient] SDP offer accepted, answer pending (202)\"\n );\n return null;\n }\n\n const errorText = await response.text();\n throw new Error(\n `Failed to send SDP offer: ${response.status} ${errorText}`\n );\n }\n\n /**\n * Polls for the SDP answer with exponential backoff.\n * Used for async reconnection when the answer is not immediately available.\n * @param sessionId - The session ID to poll for\n * @param maxAttempts - Optional maximum number of polling attempts before giving up\n * @returns The SDP answer from the server\n */\n private async pollSdpAnswer(\n sessionId: string,\n maxAttempts: number = DEFAULT_MAX_ATTEMPTS\n ): Promise<string> {\n console.debug(\n \"[CoordinatorClient] Polling for SDP answer for session:\",\n sessionId\n );\n\n let backoffMs = INITIAL_BACKOFF_MS;\n let attempt = 0;\n\n while (true) {\n if (attempt >= maxAttempts) {\n throw new Error(\n `SDP polling exceeded maximum attempts (${maxAttempts}) for session ${sessionId}`\n );\n }\n\n attempt++;\n console.debug(\n `[CoordinatorClient] SDP poll attempt ${attempt}/${maxAttempts} for session ${sessionId}`\n );\n\n const response = await fetch(\n `${this.baseUrl}/sessions/${sessionId}/sdp_params`,\n {\n method: \"GET\",\n headers: {\n ...this.getAuthHeaders(),\n \"Content-Type\": \"application/json\",\n },\n }\n );\n\n if (response.status === 200) {\n const answerData: SDPParamsResponse = await response.json();\n console.debug(\"[CoordinatorClient] Received SDP answer via polling\");\n return answerData.sdp_answer;\n }\n\n if (response.status === 202) {\n console.warn(\n `[CoordinatorClient] SDP answer pending (202), retrying in ${backoffMs}ms...`\n );\n\n await this.sleep(backoffMs);\n\n // Exponential backoff capped at MAX_BACKOFF_MS (15s)\n backoffMs = Math.min(backoffMs * BACKOFF_MULTIPLIER, MAX_BACKOFF_MS);\n continue;\n }\n\n // For other error codes, throw immediately\n const errorText = await response.text();\n throw new Error(\n `Failed to poll SDP answer: ${response.status} ${errorText}`\n );\n }\n }\n\n /**\n * Connects to the session by sending an SDP offer and receiving an SDP answer.\n * If sdpOffer is provided, sends it first. If the answer is pending (202),\n * falls back to polling. If no sdpOffer is provided, goes directly to polling.\n * @param sessionId - The session ID to connect to\n * @param sdpOffer - Optional SDP offer from the local WebRTC peer connection\n * @param maxAttempts - Optional maximum number of polling attempts before giving up\n * @returns The SDP answer from the server\n */\n async connect(\n sessionId: string,\n sdpOffer?: string,\n maxAttempts?: number\n ): Promise<string> {\n console.debug(\"[CoordinatorClient] Connecting to session:\", sessionId);\n\n if (sdpOffer) {\n // Reconnection: we have a new SDP offer (recalculated after ICE restart)\n // Try to send it and get an immediate answer\n const answer = await this.sendSdpOffer(sessionId, sdpOffer);\n if (answer !== null) {\n return answer;\n }\n // Server accepted but answer not ready yet (202), fall back to polling\n }\n\n // No SDP offer = async reconnection, poll until server has the answer\n return this.pollSdpAnswer(sessionId, maxAttempts);\n }\n\n /**\n * Utility function to sleep for a given number of milliseconds\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","/**\n * LocalCoordinatorClient is a client for connecting to a local coordinator.\n * It extends CoordinatorClient and overrides methods for local development.\n */\n\nimport { ConflictError } from \"../types\";\nimport { transformIceServers } from \"../utils/webrtc\";\nimport { CoordinatorClient } from \"./CoordinatorClient\";\nimport { IceServersResponseSchema } from \"./types\";\n\nexport class LocalCoordinatorClient extends CoordinatorClient {\n private localBaseUrl: string;\n private sdpOffer: string | undefined;\n\n constructor(baseUrl: string) {\n // Pass dummy values to parent - they won't be used for local\n super({\n baseUrl: baseUrl,\n jwtToken: \"local\",\n model: \"local\",\n });\n this.localBaseUrl = baseUrl;\n }\n\n /**\n * Gets ICE servers from the local HTTP runtime.\n * @returns The ICE server configuration\n */\n async getIceServers(): Promise<RTCIceServer[]> {\n console.debug(\"[LocalCoordinatorClient] Fetching ICE servers...\");\n const response = await fetch(`${this.localBaseUrl}/ice_servers`, {\n method: \"GET\",\n });\n\n if (!response.ok) {\n throw new Error(\"Failed to get ICE servers from local coordinator.\");\n }\n\n const data = await response.json();\n const parsed = IceServersResponseSchema.parse(data);\n const iceServers = transformIceServers(parsed);\n\n console.debug(\n \"[LocalCoordinatorClient] Received ICE servers:\",\n iceServers.length\n );\n return iceServers;\n }\n\n /**\n * Creates a local session by posting to /start_session.\n * @returns always \"local\"\n */\n async createSession(sdpOffer: string): Promise<string> {\n console.debug(\"[LocalCoordinatorClient] Creating local session...\");\n this.sdpOffer = sdpOffer;\n const response = await fetch(`${this.localBaseUrl}/start_session`, {\n method: \"POST\",\n });\n\n if (!response.ok) {\n throw new Error(\"Failed to send local start session command.\");\n }\n\n console.debug(\"[LocalCoordinatorClient] Local session created\");\n return \"local\";\n }\n\n /**\n * Connects to the local session by posting SDP params to /sdp_params.\n * @param sessionId - The session ID (ignored for local)\n * @param sdpMessage - The SDP offer from the local WebRTC peer connection\n * @returns The SDP answer from the server\n */\n async connect(sessionId: string, sdpMessage: string): Promise<string> {\n this.sdpOffer = sdpMessage || this.sdpOffer;\n console.debug(\"[LocalCoordinatorClient] Connecting to local session...\");\n const sdpBody = {\n sdp: this.sdpOffer,\n type: \"offer\",\n };\n const response = await fetch(`${this.localBaseUrl}/sdp_params`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(sdpBody),\n });\n\n if (!response.ok) {\n if (response.status === 409) {\n throw new ConflictError(\"Connection superseded by newer request\");\n }\n throw new Error(\"Failed to get SDP answer from local coordinator.\");\n }\n\n const sdpAnswer: { sdp: string; type: \"answer\" } = await response.json();\n console.debug(\"[LocalCoordinatorClient] Received SDP answer\");\n return sdpAnswer.sdp;\n }\n\n async terminateSession(): Promise<void> {\n console.debug(\"[LocalCoordinatorClient] Stopping local session...\");\n await fetch(`${this.localBaseUrl}/stop_session`, {\n method: \"POST\",\n });\n }\n}\n","/**\n * The GPUMachineClient is responsible for handling the direct connection to the machine instance\n * after the coordinator has assigned a machine.\n */\n\nimport * as webrtc from \"../utils/webrtc\";\nimport type { MessageScope, ConnectionStats } from \"../types\";\n\ntype EventHandler = (...args: any[]) => void;\n\nexport type GPUMachineEvent =\n | \"statusChanged\"\n | \"trackReceived\"\n | \"trackRemoved\"\n | \"message\"\n | \"statsUpdate\";\n\nexport type GPUMachineStatus =\n | \"disconnected\"\n | \"connecting\"\n | \"connected\"\n | \"error\";\n\n/**\n * Interval (ms) at which the client sends runtime-channel \"ping\" messages\n * to the server so the runtime can detect stale connections quickly.\n */\nconst PING_INTERVAL_MS = 5_000;\nconst STATS_INTERVAL_MS = 2_000;\n\nexport class GPUMachineClient {\n private eventListeners: Map<GPUMachineEvent, Set<EventHandler>> = new Map();\n private peerConnection: RTCPeerConnection | undefined;\n private dataChannel: RTCDataChannel | undefined;\n private status: GPUMachineStatus = \"disconnected\";\n private publishedTrack: MediaStreamTrack | undefined;\n private videoTransceiver: RTCRtpTransceiver | undefined;\n private config: webrtc.WebRTCConfig;\n private pingInterval: ReturnType<typeof setInterval> | undefined;\n private statsInterval: ReturnType<typeof setInterval> | undefined;\n private stats: ConnectionStats | undefined;\n\n constructor(config: webrtc.WebRTCConfig) {\n this.config = config;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Event Emitter API\n // ─────────────────────────────────────────────────────────────────────────────\n\n on(event: GPUMachineEvent, handler: EventHandler): void {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners.get(event)!.add(handler);\n }\n\n off(event: GPUMachineEvent, handler: EventHandler): void {\n this.eventListeners.get(event)?.delete(handler);\n }\n\n private emit(event: GPUMachineEvent, ...args: unknown[]): void {\n this.eventListeners.get(event)?.forEach((handler) => handler(...args));\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // SDP & Connection\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Creates an SDP offer for initiating a connection.\n * Must be called before connect().\n */\n async createOffer(): Promise<string> {\n // Create peer connection if not exists\n if (!this.peerConnection) {\n this.peerConnection = webrtc.createPeerConnection(this.config);\n this.setupPeerConnectionHandlers();\n }\n\n // Create data channel before offer (offerer creates the channel)\n this.dataChannel = webrtc.createDataChannel(\n this.peerConnection,\n this.config.dataChannelLabel\n );\n this.setupDataChannelHandlers();\n\n // Add sendrecv video transceiver for bidirectional video\n this.videoTransceiver = this.peerConnection.addTransceiver(\"video\", {\n direction: \"sendrecv\",\n });\n\n const offer = await webrtc.createOffer(this.peerConnection);\n console.debug(\"[GPUMachineClient] Created SDP offer\");\n return offer;\n }\n\n /**\n * Connects to the GPU machine using the provided SDP answer.\n * createOffer() must be called first.\n * @param sdpAnswer The SDP answer from the GPU machine\n */\n async connect(sdpAnswer: string): Promise<void> {\n if (!this.peerConnection) {\n throw new Error(\n \"[GPUMachineClient] Cannot connect - call createOffer() first\"\n );\n }\n\n if (this.peerConnection.signalingState !== \"have-local-offer\") {\n throw new Error(\n `[GPUMachineClient] Invalid signaling state: ${this.peerConnection.signalingState}`\n );\n }\n\n this.setStatus(\"connecting\");\n\n try {\n await webrtc.setRemoteDescription(this.peerConnection, sdpAnswer);\n console.debug(\"[GPUMachineClient] Remote description set\");\n } catch (error) {\n console.error(\"[GPUMachineClient] Failed to connect:\", error);\n this.setStatus(\"error\");\n throw error;\n }\n }\n\n /**\n * Disconnects from the GPU machine and cleans up resources.\n */\n async disconnect(): Promise<void> {\n this.stopPing();\n this.stopStatsPolling();\n\n if (this.publishedTrack) {\n await this.unpublishTrack();\n }\n\n if (this.dataChannel) {\n this.dataChannel.close();\n this.dataChannel = undefined;\n }\n\n if (this.peerConnection) {\n webrtc.closePeerConnection(this.peerConnection);\n this.peerConnection = undefined;\n }\n\n this.videoTransceiver = undefined;\n this.setStatus(\"disconnected\");\n console.debug(\"[GPUMachineClient] Disconnected\");\n }\n\n /**\n * Returns the current connection status.\n */\n getStatus(): GPUMachineStatus {\n return this.status;\n }\n\n /**\n * Gets the current local SDP description.\n */\n getLocalSDP(): string | undefined {\n if (!this.peerConnection) return undefined;\n return webrtc.getLocalDescription(this.peerConnection);\n }\n\n isOfferStillValid(): boolean {\n if (!this.peerConnection) return false;\n return this.peerConnection.signalingState === \"have-local-offer\";\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Messaging\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Sends a command to the GPU machine via the data channel.\n * @param command The command to send\n * @param data The data to send with the command. These are the parameters for the command, matching the scheme in the capabilities dictionary.\n * @param scope The message scope – \"application\" (default) for model commands, \"runtime\" for platform-level messages.\n */\n sendCommand(\n command: string,\n data: any,\n scope: MessageScope = \"application\"\n ): void {\n if (!this.dataChannel) {\n throw new Error(\"[GPUMachineClient] Data channel not available\");\n }\n\n try {\n webrtc.sendMessage(this.dataChannel, command, data, scope);\n } catch (error) {\n console.warn(\"[GPUMachineClient] Failed to send message:\", error);\n }\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Track Publishing\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Publishes a track to the GPU machine.\n * Only one track can be published at a time.\n * Uses the existing transceiver's sender to replace the track.\n * @param track The MediaStreamTrack to publish\n */\n async publishTrack(track: MediaStreamTrack): Promise<void> {\n if (!this.peerConnection) {\n throw new Error(\n \"[GPUMachineClient] Cannot publish track - not initialized\"\n );\n }\n\n if (this.status !== \"connected\") {\n throw new Error(\n \"[GPUMachineClient] Cannot publish track - not connected\"\n );\n }\n\n if (!this.videoTransceiver) {\n throw new Error(\n \"[GPUMachineClient] Cannot publish track - no video transceiver\"\n );\n }\n\n try {\n // Use replaceTrack on the existing transceiver's sender\n // This doesn't require renegotiation\n await this.videoTransceiver.sender.replaceTrack(track);\n this.publishedTrack = track;\n console.debug(\n \"[GPUMachineClient] Track published successfully:\",\n track.kind\n );\n } catch (error) {\n console.error(\"[GPUMachineClient] Failed to publish track:\", error);\n throw error;\n }\n }\n\n /**\n * Unpublishes the currently published track.\n */\n async unpublishTrack(): Promise<void> {\n if (!this.videoTransceiver || !this.publishedTrack) return;\n\n try {\n // Replace with null to stop sending without renegotiation\n await this.videoTransceiver.sender.replaceTrack(null);\n console.debug(\"[GPUMachineClient] Track unpublished successfully\");\n } catch (error) {\n console.error(\"[GPUMachineClient] Failed to unpublish track:\", error);\n throw error;\n } finally {\n this.publishedTrack = undefined;\n }\n }\n\n /**\n * Returns the currently published track.\n */\n getPublishedTrack(): MediaStreamTrack | undefined {\n return this.publishedTrack;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Getters\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Returns the remote media stream from the GPU machine.\n */\n getRemoteStream(): MediaStream | undefined {\n if (!this.peerConnection) return undefined;\n\n const receivers = this.peerConnection.getReceivers();\n const tracks = receivers\n .map((r) => r.track)\n .filter((t): t is MediaStreamTrack => t !== null);\n\n if (tracks.length === 0) return undefined;\n return new MediaStream(tracks);\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Ping (Client Liveness)\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Starts sending periodic \"ping\" messages on the runtime channel so the\n * server can detect stale connections quickly.\n */\n private startPing(): void {\n this.stopPing();\n this.pingInterval = setInterval(() => {\n if (this.dataChannel?.readyState === \"open\") {\n try {\n webrtc.sendMessage(this.dataChannel, \"ping\", {}, \"runtime\");\n } catch {\n // Silently ignore – data channel may be closing\n }\n }\n }, PING_INTERVAL_MS);\n }\n\n /**\n * Stops the periodic ping.\n */\n private stopPing(): void {\n if (this.pingInterval !== undefined) {\n clearInterval(this.pingInterval);\n this.pingInterval = undefined;\n }\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Stats Polling (RTT)\n // ─────────────────────────────────────────────────────────────────────────────\n\n getStats(): ConnectionStats | undefined {\n return this.stats;\n }\n\n private startStatsPolling(): void {\n this.stopStatsPolling();\n this.statsInterval = setInterval(async () => {\n if (!this.peerConnection) return;\n try {\n const report = await this.peerConnection.getStats();\n this.stats = webrtc.extractConnectionStats(report);\n this.emit(\"statsUpdate\", this.stats);\n } catch {\n // Silently ignore – connection may be closing\n }\n }, STATS_INTERVAL_MS);\n }\n\n private stopStatsPolling(): void {\n if (this.statsInterval !== undefined) {\n clearInterval(this.statsInterval);\n this.statsInterval = undefined;\n }\n this.stats = undefined;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Private Helpers\n // ─────────────────────────────────────────────────────────────────────────────\n\n private setStatus(newStatus: GPUMachineStatus): void {\n if (this.status !== newStatus) {\n this.status = newStatus;\n this.emit(\"statusChanged\", newStatus);\n }\n }\n\n private setupPeerConnectionHandlers(): void {\n if (!this.peerConnection) return;\n\n this.peerConnection.onconnectionstatechange = () => {\n const state = this.peerConnection?.connectionState;\n console.debug(\"[GPUMachineClient] Connection state:\", state);\n\n if (state) {\n switch (state) {\n case \"connected\":\n this.setStatus(\"connected\");\n this.startStatsPolling();\n break;\n case \"disconnected\":\n case \"closed\":\n this.setStatus(\"disconnected\");\n break;\n case \"failed\":\n this.setStatus(\"error\");\n break;\n }\n }\n };\n\n this.peerConnection.ontrack = (event) => {\n console.debug(\"[GPUMachineClient] Track received:\", event.track.kind);\n const stream = event.streams[0] ?? new MediaStream([event.track]);\n this.emit(\"trackReceived\", event.track, stream);\n };\n\n this.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n console.debug(\"[GPUMachineClient] ICE candidate:\", event.candidate);\n }\n };\n\n this.peerConnection.onicecandidateerror = (event) => {\n console.warn(\"[GPUMachineClient] ICE candidate error:\", event);\n };\n\n // Handle data channel created by remote peer (if we're the answerer)\n this.peerConnection.ondatachannel = (event) => {\n console.debug(\"[GPUMachineClient] Data channel received from remote\");\n this.dataChannel = event.channel;\n this.setupDataChannelHandlers();\n };\n }\n\n private setupDataChannelHandlers(): void {\n if (!this.dataChannel) return;\n\n this.dataChannel.onopen = () => {\n console.debug(\"[GPUMachineClient] Data channel open\");\n this.startPing();\n };\n\n this.dataChannel.onclose = () => {\n console.debug(\"[GPUMachineClient] Data channel closed\");\n this.stopPing();\n };\n\n this.dataChannel.onerror = (error) => {\n console.error(\"[GPUMachineClient] Data channel error:\", error);\n };\n\n this.dataChannel.onmessage = (event) => {\n const rawData = webrtc.parseMessage(event.data) as any;\n console.debug(\"[GPUMachineClient] Received message:\", rawData);\n\n try {\n // Parse the outer envelope { scope: \"application\"|\"runtime\", data: ... }\n if (rawData?.scope === \"application\" && rawData?.data !== undefined) {\n this.emit(\"message\", rawData.data, \"application\" as MessageScope);\n } else if (\n rawData?.scope === \"runtime\" &&\n rawData?.data !== undefined\n ) {\n this.emit(\"message\", rawData.data, \"runtime\" as MessageScope);\n } else {\n // Legacy / unknown format – treat as application\n console.warn(\n \"[GPUMachineClient] Received message without envelope, treating as application\"\n );\n this.emit(\"message\", rawData, \"application\" as MessageScope);\n }\n } catch (error) {\n console.error(\n \"[GPUMachineClient] Failed to parse/validate message:\",\n error\n );\n }\n };\n }\n}\n","import {\n type ReactorEvent,\n type ReactorStatus,\n type ReactorState,\n type ReactorError,\n type MessageScope,\n type ConnectOptions,\n type ConnectionStats,\n ConflictError,\n} from \"../types\";\nimport { CoordinatorClient } from \"./CoordinatorClient\";\nimport { LocalCoordinatorClient } from \"./LocalCoordinatorClient\";\nimport { GPUMachineClient, GPUMachineStatus } from \"./GPUMachineClient\";\nimport { z } from \"zod\";\n\nconst LOCAL_COORDINATOR_URL = \"http://localhost:8080\";\nexport const PROD_COORDINATOR_URL = \"https://api.reactor.inc\";\n\nconst OptionsSchema = z.object({\n coordinatorUrl: z.string().default(PROD_COORDINATOR_URL),\n modelName: z.string(),\n local: z.boolean().default(false),\n});\nexport type Options = z.input<typeof OptionsSchema>;\n\ntype EventHandler = (...args: any[]) => void;\n\nexport class Reactor {\n private coordinatorClient: CoordinatorClient | undefined;\n private machineClient: GPUMachineClient | undefined;\n private status: ReactorStatus = \"disconnected\";\n private coordinatorUrl: string;\n private lastError?: ReactorError;\n private model: string;\n private sessionExpiration?: number;\n private local: boolean;\n private sessionId?: string;\n\n constructor(options: Options) {\n const validatedOptions = OptionsSchema.parse(options);\n this.coordinatorUrl = validatedOptions.coordinatorUrl;\n\n // TODO(REA-146) Properly accept version from parameter.\n this.model = validatedOptions.modelName;\n this.local = validatedOptions.local;\n if (this.local) {\n this.coordinatorUrl = LOCAL_COORDINATOR_URL;\n }\n }\n\n // Generic event map\n private eventListeners: Map<ReactorEvent, Set<EventHandler>> = new Map();\n\n // Event Emitter API\n on(event: ReactorEvent, handler: EventHandler) {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners.get(event)!.add(handler);\n }\n\n off(event: ReactorEvent, handler: EventHandler) {\n this.eventListeners.get(event)?.delete(handler);\n }\n\n emit(event: ReactorEvent, ...args: any[]) {\n this.eventListeners.get(event)?.forEach((handler) => handler(...args));\n }\n\n /**\n * Public method to send a message to the machine.\n * Wraps the message in the specified channel envelope (defaults to \"application\").\n * @param command The command name to send.\n * @param data The command payload.\n * @param scope The envelope scope – \"application\" (default) for model commands,\n * \"runtime\" for platform-level messages (e.g. requestCapabilities).\n * @throws Error if not in ready state\n */\n async sendCommand(\n command: string,\n data: any,\n scope: MessageScope = \"application\"\n ): Promise<void> {\n // Synchronous validation - throw immediately\n if (process.env.NODE_ENV !== \"development\" && this.status !== \"ready\") {\n const errorMessage = `Cannot send message, status is ${this.status}`;\n console.warn(\"[Reactor]\", errorMessage);\n return;\n }\n\n try {\n this.machineClient?.sendCommand(command, data, scope);\n } catch (error) {\n // Async operational error - emit event only\n console.error(\"[Reactor] Failed to send message:\", error);\n this.createError(\n \"MESSAGE_SEND_FAILED\",\n `Failed to send message: ${error}`,\n \"gpu\",\n true\n );\n // Don't re-throw - let the error event handle it\n }\n }\n\n /**\n * Public method to publish a track to the machine.\n * @param track The track to send to the machine.\n */\n async publishTrack(track: MediaStreamTrack): Promise<void> {\n // Synchronous validation - throw immediately\n if (process.env.NODE_ENV !== \"development\" && this.status !== \"ready\") {\n const errorMessage = `Cannot publish track, status is ${this.status}`;\n console.warn(\"[Reactor]\", errorMessage);\n return;\n }\n\n try {\n await this.machineClient?.publishTrack(track);\n } catch (error) {\n console.error(\"[Reactor] Failed to publish track:\", error);\n this.createError(\n \"TRACK_PUBLISH_FAILED\",\n `Failed to publish track: ${error}`,\n \"gpu\",\n true\n );\n }\n }\n\n /**\n * Public method to unpublish the currently published track.\n */\n async unpublishTrack(): Promise<void> {\n try {\n await this.machineClient?.unpublishTrack();\n } catch (error) {\n console.error(\"[Reactor] Failed to unpublish track:\", error);\n this.createError(\n \"TRACK_UNPUBLISH_FAILED\",\n `Failed to unpublish track: ${error}`,\n \"gpu\",\n true\n );\n }\n }\n\n /**\n * Public method for reconnecting to an existing session, that may have been interrupted but can be recovered.\n * @param options Optional connect options (e.g. maxAttempts for SDP polling)\n */\n async reconnect(options?: ConnectOptions): Promise<void> {\n if (!this.sessionId || !this.coordinatorClient) {\n console.warn(\"[Reactor] No active session to reconnect to.\");\n return;\n }\n\n if (this.status === \"ready\") {\n console.warn(\"[Reactor] Already connected, no need to reconnect.\");\n return;\n }\n\n this.setStatus(\"connecting\");\n\n if (!this.machineClient) {\n // Get ICE servers from coordinator\n const iceServers = await this.coordinatorClient.getIceServers();\n\n this.machineClient = new GPUMachineClient({ iceServers });\n this.setupMachineClientHandlers();\n }\n\n // We always calculate a new offer for reconnection.\n const sdpOffer = await this.machineClient.createOffer();\n\n // Send offer to coordinator and get answer.\n try {\n const sdpAnswer = await this.coordinatorClient.connect(\n this.sessionId,\n sdpOffer,\n options?.maxAttempts\n );\n\n // Connect to GPU machine with the answer\n await this.machineClient.connect(sdpAnswer);\n this.setStatus(\"ready\");\n } catch (error) {\n let recoverable = false;\n if (error instanceof ConflictError) {\n recoverable = true;\n }\n console.error(\"[Reactor] Failed to reconnect:\", error);\n // disconnect without recovery, as the session \"connect\" call on the coordinator failed\n this.disconnect(recoverable);\n this.createError(\n \"RECONNECTION_FAILED\",\n `Failed to reconnect: ${error}`,\n \"coordinator\",\n true\n );\n }\n }\n\n /**\n * Connects to the coordinator and waits for a GPU to be assigned.\n * Once a GPU is assigned, the Reactor will connect to the gpu machine via WebRTC.\n * If no authentication is provided and not in local mode, an error is thrown.\n * @param jwtToken Optional JWT token for authentication\n * @param options Optional connect options (e.g. maxAttempts for SDP polling)\n */\n async connect(jwtToken?: string, options?: ConnectOptions): Promise<void> {\n console.debug(\"[Reactor] Connecting, status:\", this.status);\n\n if (jwtToken == undefined && !this.local) {\n throw new Error(\"No authentication provided and not in local mode\");\n }\n\n // Synchronous validation - throw immediately\n if (this.status !== \"disconnected\") {\n throw new Error(\"Already connected or connecting\");\n }\n this.setStatus(\"connecting\");\n\n try {\n console.debug(\n \"[Reactor] Connecting to coordinator with authenticated URL\"\n );\n\n this.coordinatorClient = this.local\n ? new LocalCoordinatorClient(this.coordinatorUrl)\n : new CoordinatorClient({\n baseUrl: this.coordinatorUrl,\n jwtToken: jwtToken!, // Safe: validated above\n model: this.model,\n });\n\n // Get ICE servers from coordinator\n const iceServers = await this.coordinatorClient.getIceServers();\n\n // Create GPUMachineClient and generate SDP offer\n this.machineClient = new GPUMachineClient({ iceServers });\n this.setupMachineClientHandlers();\n\n const sdpOffer = await this.machineClient.createOffer();\n\n // Create session passing sdp offer. We will get the answer polling the sdp_offer endpoint.\n const sessionId = await this.coordinatorClient.createSession(sdpOffer);\n this.setSessionId(sessionId);\n\n // Connect to coordinator and get SDP Answer.\n // We don't pass the sdp offer here because we passed it already when creating the session.\n const sdpAnswer = await this.coordinatorClient.connect(\n sessionId,\n undefined,\n options?.maxAttempts\n );\n\n // Connect to GPU machine with the answer\n await this.machineClient.connect(sdpAnswer);\n } catch (error) {\n console.error(\"[Reactor] Connection failed:\", error);\n this.createError(\n \"CONNECTION_FAILED\",\n `Connection failed: ${error}`,\n \"coordinator\",\n true\n );\n // Non-recoverable disconnect: terminates the server-side session (DELETE)\n // and cleans up all local state (machine client, session ID, etc.)\n try {\n await this.disconnect(false);\n } catch (disconnectError) {\n console.error(\n \"[Reactor] Failed to clean up after connection failure:\",\n disconnectError\n );\n }\n throw error;\n }\n }\n\n /**\n * Sets up event handlers for the machine client.\n */\n private setupMachineClientHandlers(): void {\n if (!this.machineClient) return;\n\n this.machineClient.on(\"message\", (message: any, scope: MessageScope) => {\n if (scope === \"application\") {\n this.emit(\"message\", message);\n } else if (scope === \"runtime\") {\n this.emit(\"runtimeMessage\", message);\n }\n });\n\n this.machineClient.on(\"statusChanged\", (status: GPUMachineStatus) => {\n switch (status) {\n case \"connected\":\n this.setStatus(\"ready\");\n break;\n case \"disconnected\":\n this.disconnect(true);\n break;\n case \"error\":\n this.createError(\n \"GPU_CONNECTION_ERROR\",\n \"GPU machine connection failed\",\n \"gpu\",\n true\n );\n this.disconnect();\n break;\n }\n });\n\n this.machineClient.on(\n \"trackReceived\",\n (track: MediaStreamTrack, stream: MediaStream) => {\n this.emit(\"streamChanged\", track, stream);\n }\n );\n\n this.machineClient.on(\"statsUpdate\", (stats: ConnectionStats) => {\n this.emit(\"statsUpdate\", stats);\n });\n }\n\n /**\n * Disconnects from the coordinator and the gpu machine.\n * Ensures cleanup completes even if individual disconnections fail.\n */\n async disconnect(recoverable: boolean = false) {\n if (this.status === \"disconnected\" && !this.sessionId) {\n console.warn(\"[Reactor] Already disconnected\");\n return;\n }\n\n if (this.coordinatorClient && !recoverable) {\n await this.coordinatorClient.terminateSession();\n this.coordinatorClient = undefined;\n }\n\n // Disconnect machine client with error handling\n if (this.machineClient) {\n try {\n await this.machineClient.disconnect();\n } catch (error) {\n console.error(\"[Reactor] Error disconnecting from GPU machine:\", error);\n // Continue with cleanup even if machine disconnect fails\n }\n if (!recoverable) {\n this.machineClient = undefined;\n }\n }\n\n this.setStatus(\"disconnected\");\n if (!recoverable) {\n this.setSessionExpiration(undefined);\n this.setSessionId(undefined);\n }\n }\n\n private setSessionId(newSessionId: string | undefined) {\n console.debug(\n \"[Reactor] Setting session ID:\",\n newSessionId,\n \"from\",\n this.sessionId\n );\n if (this.sessionId !== newSessionId) {\n this.sessionId = newSessionId;\n this.emit(\"sessionIdChanged\", newSessionId);\n }\n }\n\n getSessionId(): string | undefined {\n return this.sessionId;\n }\n\n private setStatus(newStatus: ReactorStatus) {\n console.debug(\"[Reactor] Setting status:\", newStatus, \"from\", this.status);\n if (this.status !== newStatus) {\n this.status = newStatus;\n this.emit(\"statusChanged\", newStatus);\n }\n }\n\n getStatus(): ReactorStatus {\n return this.status;\n }\n\n /**\n * Set the session expiration time.\n * @param newSessionExpiration The new session expiration time in seconds.\n */\n private setSessionExpiration(newSessionExpiration: number | undefined) {\n console.debug(\n \"[Reactor] Setting session expiration:\",\n newSessionExpiration\n );\n if (this.sessionExpiration !== newSessionExpiration) {\n this.sessionExpiration = newSessionExpiration;\n this.emit(\"sessionExpirationChanged\", newSessionExpiration);\n }\n }\n\n /**\n * Get the current state including status, error, and waiting info\n */\n getState(): ReactorState {\n return {\n status: this.status,\n lastError: this.lastError,\n };\n }\n\n /**\n * Get the last error that occurred\n */\n getLastError(): ReactorError | undefined {\n return this.lastError;\n }\n\n getStats(): ConnectionStats | undefined {\n return this.machineClient?.getStats();\n }\n\n /**\n * Create and store an error\n */\n private createError(\n code: string,\n message: string,\n component: \"coordinator\" | \"gpu\" | \"livekit\",\n recoverable: boolean,\n retryAfter?: number\n ) {\n this.lastError = {\n code,\n message,\n timestamp: Date.now(),\n recoverable,\n component,\n retryAfter,\n };\n\n // Emit error event\n this.emit(\"error\", this.lastError);\n }\n}\n","\"use client\";\n\nimport { ReactNode, useContext, useEffect, useRef, useState } from \"react\";\nimport {\n createReactorStore,\n initReactorStore,\n ReactorContext,\n ReactorStore,\n ReactorStoreApi,\n type ReactorInitializationProps,\n} from \"../core/store\";\nimport { useStore } from \"zustand\";\nimport type { ConnectOptions } from \"../types\";\n\n/**\n * Options for the React provider's connect behavior.\n * Extends the core ConnectOptions with autoConnect for the React lifecycle.\n */\nexport interface ReactorConnectOptions extends ConnectOptions {\n /** Whether to automatically connect when the provider mounts. Default: false. */\n autoConnect?: boolean;\n}\n\n// Provider props\ninterface ReactorProviderProps extends ReactorInitializationProps {\n connectOptions?: ReactorConnectOptions;\n jwtToken?: string;\n children: ReactNode;\n}\n\n// tsx component\nexport function ReactorProvider({\n children,\n connectOptions,\n jwtToken,\n ...props\n}: ReactorProviderProps) {\n // Stable Reactor instance\n const storeRef = useRef<ReactorStoreApi | undefined>(undefined);\n const firstRender = useRef(true);\n // State to trigger re-renders when store changes\n const [_storeVersion, setStoreVersion] = useState(0);\n\n if (storeRef.current === undefined) {\n console.debug(\"[ReactorProvider] Creating new reactor store\");\n // We create the store without autoconnecting, to avoid duplicate connections.\n // We actually connect when the component is mounted, to be on sync with the react component lifecycle.\n storeRef.current = createReactorStore(\n initReactorStore({\n ...props,\n jwtToken,\n })\n );\n console.debug(\"[ReactorProvider] Reactor store created successfully\");\n }\n\n // Destructure connectOptions with defaults\n const { autoConnect = false, ...pollingOptions } = connectOptions ?? {};\n\n // Fan out props to individual variables so that the\n // useEffect hook can be optimized by only re-running when the\n // props that actually change.\n const { coordinatorUrl, modelName, local } = props;\n const maxAttempts = pollingOptions.maxAttempts;\n\n // Handle page unload (refresh, close, navigate away) with non-recoverable disconnect\n useEffect(() => {\n const handleBeforeUnload = () => {\n console.debug(\n \"[ReactorProvider] Page unloading, performing non-recoverable disconnect\"\n );\n // Call disconnect synchronously - we can't await here as the page is unloading\n // The disconnect(false) ensures non-recoverable cleanup (stops session, clears state)\n storeRef.current?.getState().internal.reactor.disconnect(false);\n };\n\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\n\n return () => {\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\n };\n }, []);\n\n useEffect(() => {\n if (firstRender.current) {\n firstRender.current = false;\n\n // We know as a fact that the store is not undefined at this point\n const current = storeRef.current!;\n if (\n autoConnect &&\n current.getState().status === \"disconnected\" &&\n jwtToken\n ) {\n console.debug(\n \"[ReactorProvider] Starting autoconnect in first render...\"\n );\n current\n .getState()\n .connect(jwtToken, pollingOptions)\n .then(() => {\n console.debug(\n \"[ReactorProvider] Autoconnect successful in first render\"\n );\n })\n .catch((error) => {\n console.error(\n \"[ReactorProvider] Failed to autoconnect in first render:\",\n error\n );\n });\n }\n return () => {\n console.debug(\n \"[ReactorProvider] Disconnecting in cleanup for first render\"\n );\n current\n .getState()\n .disconnect()\n .then(() => {\n console.debug(\n \"[ReactorProvider] Disconnect completed successfully in cleanup for first render\"\n );\n })\n .catch((error) => {\n console.error(\n \"[ReactorProvider] Failed to disconnect in cleanup for first render:\",\n error\n );\n });\n };\n }\n\n console.debug(\"[ReactorProvider] Updating reactor store\");\n storeRef.current = createReactorStore(\n initReactorStore({\n coordinatorUrl,\n modelName,\n local,\n jwtToken,\n } satisfies ReactorInitializationProps)\n );\n\n // Store current reference to the store in the return\n const current = storeRef.current!;\n\n // Increment version to trigger re-render and propagate new store to Provider\n setStoreVersion((v) => v + 1);\n console.debug(\n \"[ReactorProvider] Reactor store updated successfully, and increased version\"\n );\n\n if (\n autoConnect &&\n current.getState().status === \"disconnected\" &&\n jwtToken\n ) {\n console.debug(\"[ReactorProvider] Starting autoconnect...\");\n current\n .getState()\n .connect(jwtToken, pollingOptions)\n .then(() => {\n console.debug(\"[ReactorProvider] Autoconnect successful\");\n })\n .catch((error) => {\n console.error(\"[ReactorProvider] Failed to autoconnect:\", error);\n });\n }\n\n return () => {\n console.debug(\"[ReactorProvider] Disconnecting in cleanup\");\n current\n .getState()\n .disconnect()\n .then(() => {\n console.debug(\n \"[ReactorProvider] Disconnect completed successfully in cleanup\"\n );\n })\n .catch((error) => {\n console.error(\"[ReactorProvider] Failed to disconnect:\", error);\n });\n };\n }, [coordinatorUrl, modelName, autoConnect, local, jwtToken, maxAttempts]);\n\n return (\n <ReactorContext.Provider value={storeRef.current}>\n {children}\n </ReactorContext.Provider>\n );\n}\n\nexport function useReactorStore<T = ReactorStore>(\n selector: (state: ReactorStore) => T\n): T {\n const ctx = useContext(ReactorContext);\n if (!ctx) {\n throw new Error(\"useReactor must be used within a ReactorProvider\");\n }\n\n return useStore(ctx, selector);\n}\n","import { StoreApi } from \"zustand\";\nimport type {\n ReactorStatus,\n ReactorError,\n MessageScope,\n ConnectOptions,\n} from \"../types\";\nimport { Reactor, type Options as ReactorOptions } from \"./Reactor\";\nimport { create } from \"zustand/react\";\nimport { createContext } from \"react\";\n\nexport type ReactorStoreApi = ReturnType<typeof createReactorStore>;\n\nexport interface ReactorState {\n status: ReactorStatus;\n videoTrack: MediaStreamTrack | null;\n lastError?: ReactorError;\n sessionId?: string;\n sessionExpiration?: number;\n insecureApiKey?: string;\n jwtToken?: string;\n}\n\nexport interface ReactorActions {\n sendCommand(command: string, data: any, scope?: MessageScope): Promise<void>;\n connect(jwtToken?: string, options?: ConnectOptions): Promise<void>;\n disconnect(recoverable?: boolean): Promise<void>;\n publishVideoStream(stream: MediaStream): Promise<void>;\n unpublishVideoStream(): Promise<void>;\n reconnect(options?: ConnectOptions): Promise<void>;\n}\n\n// Internal state not exposed to components\ninterface ReactorInternalState {\n reactor: Reactor;\n}\n\nexport const ReactorContext = createContext<ReactorStoreApi | undefined>(\n undefined\n);\n\nexport type ReactorStore = ReactorState &\n ReactorActions & {\n internal: ReactorInternalState;\n };\n\n// We introduce two methods to perform authentication:\n// - putting the auth information inside of the ReactorProvider props, and then calling connect() without arguments.\n// - not putting the anything in the props, and then calling connect() passing as arguments the auth information.\n// When in the first case, the auth information is saved in the STATE. Then, when you call connect() without arguments, the actual auth information is fetched\n// from that STATE. In the second case, you pass the auth information directly into the function in the Reactor core.\nexport const defaultInitState: ReactorState = {\n status: \"disconnected\",\n videoTrack: null,\n lastError: undefined,\n sessionExpiration: undefined,\n insecureApiKey: undefined,\n jwtToken: undefined,\n sessionId: undefined,\n};\n\nexport interface ReactorInitializationProps extends ReactorOptions {\n jwtToken?: string;\n}\n\nexport const initReactorStore = (\n props: ReactorInitializationProps\n): ReactorState & ReactorInitializationProps => {\n return {\n ...defaultInitState,\n // These are only used for dev initialization, not exposed in the store\n ...props,\n };\n};\n\nexport const createReactorStore = (\n initProps: ReactorInitializationProps,\n publicState: ReactorState = defaultInitState\n): StoreApi<ReactorStore> => {\n console.debug(\"[ReactorStore] Creating store\", {\n coordinatorUrl: initProps.coordinatorUrl,\n jwtToken: initProps.jwtToken,\n initialState: publicState,\n });\n\n return create<ReactorStore>()((set, get) => {\n const reactor = new Reactor(initProps);\n\n console.debug(\"[ReactorStore] Setting up event listeners\");\n\n reactor.on(\"statusChanged\", (newStatus: ReactorStatus) => {\n console.debug(\"[ReactorStore] Status changed\", {\n oldStatus: get().status,\n newStatus,\n });\n set({ status: newStatus });\n });\n\n reactor.on(\n \"sessionExpirationChanged\",\n (newSessionExpiration: number | undefined) => {\n console.debug(\"[ReactorStore] Session expiration changed\", {\n oldSessionExpiration: get().sessionExpiration,\n newSessionExpiration: newSessionExpiration,\n });\n set({ sessionExpiration: newSessionExpiration });\n }\n );\n\n reactor.on(\"streamChanged\", (videoTrack: MediaStreamTrack | null) => {\n console.debug(\"[ReactorStore] Stream changed\", {\n hasVideoTrack: !!videoTrack,\n videoTrackKind: videoTrack?.kind,\n videoTrackId: videoTrack?.id,\n });\n set({ videoTrack: videoTrack });\n });\n\n reactor.on(\"error\", (error: ReactorError) => {\n console.debug(\"[ReactorStore] Error occurred\", error);\n set({ lastError: error });\n });\n\n reactor.on(\"sessionIdChanged\", (newSessionId: string | undefined) => {\n console.debug(\"[ReactorStore] Session ID changed\", {\n oldSessionId: get().sessionId,\n newSessionId: newSessionId,\n });\n set({ sessionId: newSessionId });\n });\n\n return {\n ...publicState,\n jwtToken: initProps.jwtToken,\n internal: { reactor },\n\n // actions\n onMessage: (handler: (message: any) => void) => {\n console.debug(\"[ReactorStore] Registering message handler\");\n\n get().internal.reactor.on(\"message\", handler);\n\n return () => {\n console.debug(\"[ReactorStore] Cleaning up message handler\");\n get().internal.reactor.off(\"message\", handler);\n };\n },\n sendCommand: async (command: string, data: any, scope?: MessageScope) => {\n console.debug(\"[ReactorStore] Sending command\", {\n command,\n data,\n scope,\n });\n try {\n await get().internal.reactor.sendCommand(command, data, scope);\n console.debug(\"[ReactorStore] Command sent successfully\");\n } catch (error) {\n console.error(\"[ReactorStore] Failed to send command:\", error);\n throw error;\n }\n },\n connect: async (jwtToken?: string, options?: ConnectOptions) => {\n if (jwtToken === undefined) {\n // If no JWT Token, it might have been passed in the constructor props. So read from it.\n jwtToken = get().jwtToken;\n }\n\n console.debug(\"[ReactorStore] Connect called.\");\n\n try {\n await get().internal.reactor.connect(jwtToken, options);\n console.debug(\"[ReactorStore] Connect completed successfully\");\n } catch (error) {\n console.error(\"[ReactorStore] Connect failed:\", error);\n throw error;\n }\n },\n disconnect: async (recoverable: boolean = false) => {\n console.debug(\"[ReactorStore] Disconnect called\", {\n currentStatus: get().status,\n });\n\n try {\n await get().internal.reactor.disconnect(recoverable);\n console.debug(\"[ReactorStore] Disconnect completed successfully\");\n } catch (error) {\n console.error(\"[ReactorStore] Disconnect failed:\", error);\n throw error;\n }\n },\n publishVideoStream: async (stream: MediaStream) => {\n console.debug(\"[ReactorStore] Publishing video stream\");\n\n try {\n await get().internal.reactor.publishTrack(stream.getVideoTracks()[0]);\n console.debug(\"[ReactorStore] Video stream published successfully\");\n } catch (error) {\n console.error(\n \"[ReactorStore] Failed to publish video stream:\",\n error\n );\n throw error;\n }\n },\n unpublishVideoStream: async () => {\n console.debug(\"[ReactorStore] Unpublishing video stream\");\n\n try {\n await get().internal.reactor.unpublishTrack();\n console.debug(\"[ReactorStore] Video stream unpublished successfully\");\n } catch (error) {\n console.error(\n \"[ReactorStore] Failed to unpublish video stream:\",\n error\n );\n throw error;\n }\n },\n reconnect: async (options?: ConnectOptions) => {\n console.debug(\"[ReactorStore] Reconnecting\");\n try {\n await get().internal.reactor.reconnect(options);\n console.debug(\"[ReactorStore] Reconnect completed successfully\");\n } catch (error) {\n console.error(\"[ReactorStore] Failed to reconnect:\", error);\n throw error;\n }\n },\n };\n });\n};\n","import { useReactorStore } from \"./ReactorProvider\";\nimport type { ReactorStore } from \"../core/store\";\nimport type { ConnectionStats } from \"../types\";\nimport { useShallow } from \"zustand/shallow\";\nimport { useEffect, useRef, useState } from \"react\";\n\n/**\n * Generic hook for accessing selected parts of the Reactor store.\n *\n * @param selector - A function that selects part of the store state.\n * @returns The selected slice from the store.\n */\nexport function useReactor<T>(selector: (state: ReactorStore) => T): T {\n return useReactorStore(useShallow(selector));\n}\n\n/**\n * Hook for receiving model application messages.\n *\n * Only fires for messages sent by the model via `get_ctx().send()`.\n * Internal platform-level messages (e.g. capabilities) are NOT delivered here.\n *\n * @param handler - Callback invoked with each application message payload.\n */\nexport function useReactorMessage(handler: (message: any) => void): void {\n const reactor = useReactor((state) => state.internal.reactor);\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n }, [handler]);\n\n useEffect(() => {\n const stableHandler = (message: any) => {\n handlerRef.current(message);\n };\n\n reactor.on(\"message\", stableHandler);\n\n return () => {\n reactor.off(\"message\", stableHandler);\n };\n }, [reactor]);\n}\n\n/**\n * Hook for receiving internal platform-level (runtime) messages.\n *\n * This is intended for advanced use cases that need access to the runtime\n * control layer, such as capabilities negotiation. Model application messages\n * sent via `get_ctx().send()` are NOT delivered through this hook — use\n * {@link useReactorMessage} for those.\n *\n * @param handler - Callback invoked with each runtime message payload.\n */\nexport function useReactorInternalMessage(\n handler: (message: any) => void\n): void {\n const reactor = useReactor((state) => state.internal.reactor);\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n }, [handler]);\n\n useEffect(() => {\n const stableHandler = (message: any) => {\n handlerRef.current(message);\n };\n\n reactor.on(\"runtimeMessage\", stableHandler);\n\n return () => {\n reactor.off(\"runtimeMessage\", stableHandler);\n };\n }, [reactor]);\n}\n\n/**\n * Hook that returns the current connection stats (RTT, etc.).\n * Updates every ~2s while connected. Returns undefined when disconnected.\n */\nexport function useStats(): ConnectionStats | undefined {\n const reactor = useReactor((state) => state.internal.reactor);\n const [stats, setStats] = useState<ConnectionStats | undefined>(undefined);\n\n useEffect(() => {\n const handler = (newStats: ConnectionStats) => {\n setStats(newStats);\n };\n\n reactor.on(\"statsUpdate\", handler);\n\n return () => {\n reactor.off(\"statsUpdate\", handler);\n setStats(undefined);\n };\n }, [reactor]);\n\n return stats;\n}\n","\"use client\";\n\nimport { useReactor } from \"./hooks\";\nimport { useEffect, useRef } from \"react\";\nimport React from \"react\";\n\nexport interface ReactorViewProps {\n width?: number;\n height?: number;\n className?: string;\n style?: React.CSSProperties;\n videoObjectFit?: NonNullable<\n React.VideoHTMLAttributes<HTMLVideoElement>[\"style\"]\n >[\"objectFit\"];\n}\n\nexport function ReactorView({\n width,\n height,\n className,\n style,\n videoObjectFit = \"contain\",\n}: ReactorViewProps) {\n const { videoTrack, status } = useReactor((state) => ({\n videoTrack: state.videoTrack,\n status: state.status,\n }));\n\n const videoRef = useRef<HTMLVideoElement>(null);\n\n useEffect(() => {\n console.debug(\"[ReactorView] Video track effect triggered\", {\n hasVideoElement: !!videoRef.current,\n hasVideoTrack: !!videoTrack,\n videoTrackKind: videoTrack?.kind,\n });\n\n if (videoRef.current && videoTrack) {\n console.debug(\"[ReactorView] Attaching video track to element\");\n try {\n // Create a MediaStream from the track and attach to video element\n const stream = new MediaStream([videoTrack]);\n videoRef.current.srcObject = stream;\n videoRef.current.play().catch((e) => {\n console.warn(\"[ReactorView] Auto-play failed:\", e);\n });\n console.debug(\"[ReactorView] Video track attached successfully\");\n } catch (error) {\n console.error(\"[ReactorView] Failed to attach video track:\", error);\n }\n\n // Cleanup: remove srcObject when track changes or component unmounts\n return () => {\n console.debug(\"[ReactorView] Detaching video track from element\");\n if (videoRef.current) {\n videoRef.current.srcObject = null;\n console.debug(\"[ReactorView] Video track detached successfully\");\n }\n };\n } else {\n console.debug(\"[ReactorView] No video track or element to attach\");\n }\n }, [videoTrack]);\n\n const showPlaceholder = !videoTrack;\n\n return (\n <div\n style={{\n position: \"relative\",\n background: \"#000\",\n ...(width && { width }),\n ...(height && { height }),\n ...style,\n }}\n className={className}\n >\n <video\n ref={videoRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n objectFit: videoObjectFit,\n display: showPlaceholder ? \"none\" : \"block\",\n }}\n muted\n playsInline\n />\n {showPlaceholder && (\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n color: \"#fff\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: \"16px\",\n fontFamily: \"monospace\",\n textAlign: \"center\",\n padding: \"20px\",\n boxSizing: \"border-box\",\n }}\n >\n {status}\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useReactor, useReactorInternalMessage } from \"./hooks\";\nimport React, { useState, useCallback } from \"react\";\n\nexport interface ReactorControllerProps {\n className?: string;\n style?: React.CSSProperties;\n}\n\ninterface CommandSchema {\n description: string;\n schema: Record<\n string,\n {\n description?: string;\n type: string;\n minimum?: number;\n maximum?: number;\n required?: boolean;\n enum?: string[];\n }\n >;\n}\n\ninterface CommandsMessage {\n commands: Record<string, CommandSchema>;\n}\n\nexport function ReactorController({\n className,\n style,\n}: ReactorControllerProps) {\n const { sendCommand, status } = useReactor((state) => ({\n sendCommand: state.sendCommand,\n status: state.status,\n }));\n const [commands, setCommands] = useState<Record<string, CommandSchema>>({});\n const [formValues, setFormValues] = useState<\n Record<string, Record<string, any>>\n >({});\n const [expandedCommands, setExpandedCommands] = useState<\n Record<string, boolean>\n >({});\n\n // Reset commands when disconnected\n React.useEffect(() => {\n if (status === \"disconnected\") {\n setCommands({});\n setFormValues({});\n setExpandedCommands({});\n }\n }, [status]);\n\n // Function to request capabilities (sent on the \"runtime\" channel)\n const requestCapabilities = useCallback(() => {\n if (status === \"ready\") {\n sendCommand(\"requestCapabilities\", {}, \"runtime\");\n }\n }, [status, sendCommand]);\n\n // Send requestCapabilities when ready\n React.useEffect(() => {\n if (status === \"ready\") {\n requestCapabilities();\n }\n }, [status, requestCapabilities]);\n\n // Retry every 5 seconds if capabilities not set\n React.useEffect(() => {\n // Only set up interval if status is ready and commands are empty\n if (status !== \"ready\" || Object.keys(commands).length > 0) {\n return;\n }\n\n const interval = setInterval(() => {\n requestCapabilities();\n }, 5000);\n\n return () => clearInterval(interval);\n }, [status, commands, requestCapabilities]);\n\n useReactorInternalMessage((message) => {\n if (\n message &&\n typeof message === \"object\" &&\n message.type === \"modelCapabilities\" &&\n message.data &&\n \"commands\" in message.data\n ) {\n const commandsMessage = message.data as CommandsMessage;\n setCommands(commandsMessage.commands);\n\n // Initialize form values for each command\n const initialValues: Record<string, Record<string, any>> = {};\n const initialExpanded: Record<string, boolean> = {};\n\n Object.entries(commandsMessage.commands).forEach(\n ([commandName, commandSchema]) => {\n initialValues[commandName] = {};\n initialExpanded[commandName] = false; // Start collapsed by default\n\n Object.entries(commandSchema.schema).forEach(\n ([paramName, paramSchema]) => {\n if (paramSchema.type === \"number\") {\n initialValues[commandName][paramName] =\n paramSchema.minimum ?? 0;\n } else if (paramSchema.type === \"string\") {\n initialValues[commandName][paramName] = \"\";\n } else if (paramSchema.type === \"boolean\") {\n initialValues[commandName][paramName] = false;\n } else if (paramSchema.type === \"integer\") {\n initialValues[commandName][paramName] =\n paramSchema.minimum ?? 0;\n }\n }\n );\n }\n );\n setFormValues(initialValues);\n setExpandedCommands(initialExpanded);\n }\n });\n\n const handleInputChange = useCallback(\n (commandName: string, paramName: string, value: any) => {\n setFormValues((prev) => ({\n ...prev,\n [commandName]: {\n ...prev[commandName],\n [paramName]: value,\n },\n }));\n },\n []\n );\n\n const toggleCommandExpanded = useCallback((commandName: string) => {\n setExpandedCommands((prev) => ({\n ...prev,\n [commandName]: !prev[commandName],\n }));\n }, []);\n\n const handleCommandSubmit = useCallback(\n async (commandName: string) => {\n const commandSchema = commands[commandName];\n const formData = formValues[commandName] || {};\n\n // Build the data object according to the schema structure\n const data: Record<string, any> = {};\n\n // Only include parameters that are defined in the schema\n Object.keys(commandSchema.schema).forEach((paramName) => {\n const paramSchema = commandSchema.schema[paramName];\n let value = formData[paramName];\n\n // Type conversion based on schema\n if (paramSchema.type === \"number\" && typeof value === \"string\") {\n value = parseFloat(value) || 0;\n } else if (\n paramSchema.type === \"integer\" &&\n typeof value === \"string\"\n ) {\n value = parseInt(value) || 0;\n } else if (\n paramSchema.type === \"boolean\" &&\n typeof value !== \"boolean\"\n ) {\n value = Boolean(value);\n }\n\n // Only include the parameter if it has a value or is required\n if (value !== undefined && value !== \"\" && value !== null) {\n data[paramName] = value;\n } else if (paramSchema.required) {\n // Set default values for required parameters\n if (paramSchema.type === \"number\") {\n data[paramName] = paramSchema.minimum ?? 0;\n } else if (paramSchema.type === \"integer\") {\n data[paramName] = paramSchema.minimum ?? 0;\n } else if (paramSchema.type === \"string\") {\n data[paramName] = \"\";\n } else if (paramSchema.type === \"boolean\") {\n data[paramName] = false;\n }\n }\n });\n\n console.log(`Executing command: ${commandName}`, data);\n\n await sendCommand(commandName, data);\n },\n [formValues, sendCommand, commands]\n );\n\n const renderInput = (\n commandName: string,\n paramName: string,\n paramSchema: any\n ) => {\n const value = formValues[commandName]?.[paramName] ?? \"\";\n\n if (paramSchema.type === \"number\" || paramSchema.type === \"integer\") {\n const isInteger = paramSchema.type === \"integer\";\n const step = isInteger ? 1 : 0.1;\n const parseValue = isInteger ? parseInt : parseFloat;\n\n // Use slider if min/max are defined, otherwise use number input\n if (\n typeof paramSchema.minimum === \"number\" &&\n typeof paramSchema.maximum === \"number\"\n ) {\n return (\n <div style={{ marginBottom: \"8px\" }}>\n <label\n style={{ fontSize: \"12px\", color: \"#666\", display: \"block\" }}\n >\n {paramName} ({paramSchema.minimum} - {paramSchema.maximum})\n {paramSchema.description && ` - ${paramSchema.description}`}\n {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n </label>\n <input\n type=\"range\"\n min={paramSchema.minimum}\n max={paramSchema.maximum}\n step={step}\n value={value}\n onChange={(e) => {\n const newValue = parseValue(e.target.value) || 0;\n handleInputChange(commandName, paramName, newValue);\n // Execute command immediately for sliders\n handleCommandSubmit(commandName);\n }}\n style={{ width: \"100%\", marginBottom: \"4px\" }}\n />\n <div style={{ fontSize: \"11px\", color: \"#888\" }}>\n Value: {value}\n </div>\n </div>\n );\n } else {\n return (\n <div style={{ marginBottom: \"8px\" }}>\n <label\n style={{ fontSize: \"12px\", color: \"#666\", display: \"block\" }}\n >\n {paramName}\n {paramSchema.description && ` - ${paramSchema.description}`}\n {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n </label>\n <input\n type=\"number\"\n value={value}\n min={paramSchema.minimum}\n max={paramSchema.maximum}\n step={step}\n inputMode=\"numeric\"\n onChange={(e) => {\n const val = e.target.value;\n if (val === \"\" || val === \"-\") {\n // Allow empty or just minus sign while typing\n handleInputChange(commandName, paramName, val);\n } else {\n const parsed = parseValue(val);\n if (!isNaN(parsed)) {\n handleInputChange(commandName, paramName, parsed);\n }\n }\n }}\n onBlur={(e) => {\n // On blur, ensure we have a valid number\n const val = e.target.value;\n if (val === \"\" || val === \"-\") {\n handleInputChange(commandName, paramName, 0);\n }\n }}\n style={{\n width: \"100%\",\n padding: \"4px\",\n fontSize: \"12px\",\n border: \"1px solid #ccc\",\n borderRadius: \"2px\",\n }}\n />\n </div>\n );\n }\n } else if (paramSchema.type === \"string\") {\n if (paramSchema.enum) {\n // Dropdown for enum values\n return (\n <div style={{ marginBottom: \"8px\" }}>\n <label\n style={{ fontSize: \"12px\", color: \"#666\", display: \"block\" }}\n >\n {paramName}\n {paramSchema.description && ` - ${paramSchema.description}`}\n {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n </label>\n <select\n value={value}\n onChange={(e) =>\n handleInputChange(commandName, paramName, e.target.value)\n }\n style={{\n width: \"100%\",\n padding: \"4px\",\n fontSize: \"12px\",\n border: \"1px solid #ccc\",\n borderRadius: \"2px\",\n }}\n >\n <option value=\"\">Select...</option>\n {paramSchema.enum.map((option: string) => (\n <option key={option} value={option}>\n {option}\n </option>\n ))}\n </select>\n </div>\n );\n } else {\n // Text input\n return (\n <div style={{ marginBottom: \"8px\" }}>\n <label\n style={{ fontSize: \"12px\", color: \"#666\", display: \"block\" }}\n >\n {paramName}\n {paramSchema.description && ` - ${paramSchema.description}`}\n {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n </label>\n <input\n type=\"text\"\n value={value}\n onChange={(e) =>\n handleInputChange(commandName, paramName, e.target.value)\n }\n style={{\n width: \"100%\",\n padding: \"4px\",\n fontSize: \"12px\",\n border: \"1px solid #ccc\",\n borderRadius: \"2px\",\n }}\n />\n </div>\n );\n }\n } else if (paramSchema.type === \"boolean\") {\n return (\n <div style={{ marginBottom: \"8px\" }}>\n <label\n style={{\n fontSize: \"12px\",\n color: \"#666\",\n display: \"flex\",\n alignItems: \"center\",\n }}\n >\n <input\n type=\"checkbox\"\n checked={value}\n onChange={(e) =>\n handleInputChange(commandName, paramName, e.target.checked)\n }\n style={{ marginRight: \"6px\" }}\n />\n {paramName}\n {paramSchema.description && ` - ${paramSchema.description}`}\n {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n </label>\n </div>\n );\n }\n\n return null;\n };\n\n const renderCommand = (commandName: string, commandSchema: CommandSchema) => {\n const hasParams = Object.keys(commandSchema.schema).length > 0;\n const isExpanded = expandedCommands[commandName];\n\n // Check if this command has any slider inputs (number/integer with min/max)\n const hasSliderInputs = Object.values(commandSchema.schema).some(\n (paramSchema) =>\n (paramSchema.type === \"number\" || paramSchema.type === \"integer\") &&\n typeof paramSchema.minimum === \"number\" &&\n typeof paramSchema.maximum === \"number\"\n );\n\n // Don't show execute button if command has slider inputs (they execute automatically)\n const showExecuteButton = !hasSliderInputs;\n\n return (\n <div\n key={commandName}\n style={{\n border: \"1px solid #ddd\",\n borderRadius: \"4px\",\n marginBottom: \"8px\",\n backgroundColor: \"#fafafa\",\n }}\n >\n {/* Command Header - Always Visible */}\n <div\n onClick={() => toggleCommandExpanded(commandName)}\n style={{\n padding: \"8px 12px\",\n cursor: \"pointer\",\n borderBottom: isExpanded ? \"1px solid #ddd\" : \"none\",\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n }}\n >\n <div>\n <h4\n style={{\n margin: \"0\",\n fontSize: \"13px\",\n fontWeight: \"bold\",\n }}\n >\n {commandName}\n </h4>\n {isExpanded && commandSchema.description && (\n <p\n style={{ margin: \"4px 0 0 0\", fontSize: \"11px\", color: \"#666\" }}\n >\n {commandSchema.description}\n </p>\n )}\n </div>\n <div\n style={{\n fontSize: \"10px\",\n color: \"#999\",\n transform: isExpanded ? \"rotate(180deg)\" : \"rotate(0deg)\",\n transition: \"transform 0.2s\",\n }}\n >\n ▼\n </div>\n </div>\n\n {/* Command Content - Collapsible */}\n {isExpanded && (\n <div style={{ padding: \"12px\", paddingTop: \"0\" }}>\n {hasParams && (\n <div style={{ marginBottom: showExecuteButton ? \"12px\" : \"0\" }}>\n <div\n style={{\n marginBottom: \"8px\",\n fontSize: \"12px\",\n fontWeight: \"bold\",\n color: \"#555\",\n }}\n >\n Parameters:\n </div>\n {Object.entries(commandSchema.schema).map(\n ([paramName, paramSchema]) => (\n <div\n key={`${commandName}-${paramName}`}\n style={{ marginLeft: \"8px\" }}\n >\n {renderInput(commandName, paramName, paramSchema)}\n </div>\n )\n )}\n </div>\n )}\n\n {!hasParams && (\n <div\n style={{\n marginBottom: showExecuteButton ? \"12px\" : \"0\",\n marginTop: \"2px\",\n fontSize: \"11px\",\n color: \"#666\",\n fontStyle: \"italic\",\n }}\n >\n No parameters required\n </div>\n )}\n\n {showExecuteButton && (\n <button\n onClick={() => handleCommandSubmit(commandName)}\n style={{\n padding: \"8px 16px\",\n fontSize: \"12px\",\n backgroundColor: \"#007bff\",\n color: \"white\",\n border: \"none\",\n borderRadius: \"4px\",\n cursor: \"pointer\",\n fontWeight: \"bold\",\n }}\n >\n Execute {commandName}\n </button>\n )}\n </div>\n )}\n </div>\n );\n };\n\n return (\n <div className={className} style={style}>\n <div style={{ fontFamily: \"monospace\", fontSize: \"12px\" }}>\n {Object.keys(commands).length === 0 ? (\n <div style={{ padding: \"12px\", color: \"#666\", fontStyle: \"italic\" }}>\n Waiting for commands schema...\n </div>\n ) : (\n <div>\n <h3\n style={{\n margin: \"0 0 16px 0\",\n fontSize: \"16px\",\n fontWeight: \"bold\",\n }}\n >\n Reactor Commands\n </h3>\n {Object.entries(commands).map(([commandName, commandSchema]) => (\n <div key={commandName}>\n {renderCommand(commandName, commandSchema)}\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { useReactor } from \"./hooks\";\nimport { useEffect, useRef, useState } from \"react\";\nimport React from \"react\";\n\ninterface Props {\n className?: string;\n style?: React.CSSProperties;\n videoConstraints?: MediaTrackConstraints;\n showWebcam?: boolean;\n videoObjectFit?: NonNullable<\n React.VideoHTMLAttributes<HTMLVideoElement>[\"style\"]\n >[\"objectFit\"];\n}\n\nexport function WebcamStream({\n className,\n style,\n videoConstraints = {\n width: { ideal: 1280 },\n height: { ideal: 720 },\n },\n showWebcam = true,\n videoObjectFit = \"contain\",\n}: Props) {\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [isPublishing, setIsPublishing] = useState(false);\n const [permissionDenied, setPermissionDenied] = useState(false);\n\n const { status, publishVideoStream, unpublishVideoStream, reactor } =\n useReactor((state) => ({\n status: state.status,\n publishVideoStream: state.publishVideoStream,\n unpublishVideoStream: state.unpublishVideoStream,\n reactor: state.internal.reactor,\n }));\n\n const videoRef = useRef<HTMLVideoElement>(null);\n\n // Start webcam\n const startWebcam = async () => {\n console.debug(\"[WebcamPublisher] Starting webcam\");\n\n try {\n const mediaStream = await navigator.mediaDevices.getUserMedia({\n video: videoConstraints,\n audio: false,\n });\n\n console.debug(\"[WebcamPublisher] Webcam started successfully\");\n setStream(mediaStream);\n setPermissionDenied(false);\n } catch (err) {\n console.error(\"[WebcamPublisher] Failed to start webcam:\", err);\n\n // Check if the error is a permission denial\n if (\n err instanceof DOMException &&\n (err.name === \"NotAllowedError\" || err.name === \"PermissionDeniedError\")\n ) {\n console.debug(\"[WebcamPublisher] Camera permission denied\");\n setPermissionDenied(true);\n }\n }\n };\n\n // Stop webcam\n const stopWebcam = async () => {\n console.debug(\"[WebcamPublisher] Stopping webcam\");\n\n // Unpublish if currently publishing\n try {\n await unpublishVideoStream();\n console.debug(\"[WebcamPublisher] Unpublished before stopping\");\n } catch (err) {\n console.error(\"[WebcamPublisher] Error unpublishing before stop:\", err);\n }\n\n setIsPublishing(false);\n\n // Stop all tracks\n stream?.getTracks().forEach((track) => {\n track.stop();\n console.debug(\"[WebcamPublisher] Stopped track:\", track.kind);\n });\n setStream(null);\n\n console.debug(\"[WebcamPublisher] Webcam stopped\");\n };\n\n // Attach stream to video element\n useEffect(() => {\n console.debug(\"[WebcamPublisher] Stream effect triggered\", {\n hasVideoElement: !!videoRef.current,\n hasStream: !!stream,\n });\n\n if (!videoRef.current) {\n return;\n }\n\n if (stream) {\n console.debug(\"[WebcamPublisher] Attaching stream to video element\");\n videoRef.current.srcObject = stream;\n console.debug(\"[WebcamPublisher] Stream attached successfully\");\n } else {\n console.debug(\"[WebcamPublisher] Clearing video element\");\n videoRef.current.srcObject = null;\n }\n }, [stream]);\n\n // Auto-publish when reactor is ready and webcam is active\n useEffect(() => {\n if (!stream) {\n return;\n }\n\n if (status === \"ready\" && !isPublishing) {\n console.debug(\n \"[WebcamPublisher] Reactor ready, auto-publishing webcam stream\"\n );\n publishVideoStream(stream)\n .then(() => {\n console.debug(\"[WebcamPublisher] Auto-publish successful\");\n setIsPublishing(true);\n })\n .catch((err) => {\n console.error(\"[WebcamPublisher] Auto-publish failed:\", err);\n });\n } else if (status !== \"ready\" && isPublishing) {\n console.debug(\"[WebcamPublisher] Reactor not ready, auto-unpublishing\");\n unpublishVideoStream()\n .then(() => {\n console.debug(\"[WebcamPublisher] Auto-unpublish successful\");\n setIsPublishing(false);\n })\n .catch((err) => {\n console.error(\"[WebcamPublisher] Auto-unpublish failed:\", err);\n });\n }\n }, [status, stream, isPublishing, publishVideoStream, unpublishVideoStream]);\n\n // Listen for error events from Reactor\n useEffect(() => {\n const handleError = (error: any) => {\n console.debug(\"[WebcamPublisher] Received error event:\", error);\n\n // Handle video publish failures by resetting state\n if (error.code === \"VIDEO_PUBLISH_FAILED\") {\n console.debug(\n \"[WebcamPublisher] Video publish failed, resetting isPublishing state\"\n );\n setIsPublishing(false);\n }\n };\n\n reactor.on(\"error\", handleError);\n\n return () => {\n reactor.off(\"error\", handleError);\n };\n }, [reactor]);\n\n // Reset publishing state when status changes away from ready\n useEffect(() => {\n if (status !== \"ready\") {\n console.debug(\n \"[WebcamPublisher] Status changed to\",\n status,\n \"- resetting isPublishing state\"\n );\n setIsPublishing(false);\n }\n }, [status, isPublishing]);\n\n // Auto-start webcam on mount and cleanup on unmount\n useEffect(() => {\n console.debug(\"[WebcamPublisher] Auto-starting webcam\");\n startWebcam();\n\n return () => {\n console.debug(\"[WebcamPublisher] Cleanup on unmount\");\n stopWebcam();\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n const showPlaceholder = !stream;\n\n return (\n <div\n style={{\n display: showWebcam ? \"block\" : \"none\",\n position: \"relative\",\n background: \"#000\",\n ...style,\n }}\n className={className}\n >\n <video\n ref={videoRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n objectFit: videoObjectFit,\n display: showPlaceholder ? \"none\" : \"block\",\n }}\n muted\n playsInline\n autoPlay\n />\n {showPlaceholder && (\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n color: \"#fff\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: \"16px\",\n fontFamily: \"monospace\",\n textAlign: \"center\",\n padding: \"20px\",\n boxSizing: \"border-box\",\n flexDirection: \"column\",\n gap: \"12px\",\n }}\n >\n {permissionDenied ? (\n <div style={{ fontSize: \"12px\", fontFamily: \"monospace\" }}>\n Camera access denied.\n <br />\n Please allow access in your browser settings.\n </div>\n ) : (\n <div style={{ fontSize: \"12px\", fontFamily: \"monospace\" }}>\n Starting camera...\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n","import { PROD_COORDINATOR_URL } from \"../core/Reactor\";\n\n/**\n * ⚠️ INSECURE: Fetches a JWT token directly from the client.\n *\n * WARNING: This function exposes your API key in client-side code.\n * Only use this for local development or testing purposes.\n * In production, call /tokens from your server and pass the JWT to your frontend.\n *\n * @param apiKey - Your Reactor API key (will be exposed in client code!)\n * @param coordinatorUrl - Optional coordinator URL, defaults to production\n * @returns string containing the JWT token\n */\nexport async function fetchInsecureJwtToken(\n apiKey: string,\n coordinatorUrl: string = PROD_COORDINATOR_URL\n): Promise<string> {\n console.warn(\n \"[Reactor] ⚠️ SECURITY WARNING: fetchInsecureJwtToken() exposes your API key in client-side code. \" +\n \"This should ONLY be used for local development or testing. \" +\n \"In production, fetch tokens from your server instead.\"\n );\n\n const response = await fetch(`${coordinatorUrl}/tokens`, {\n method: \"GET\",\n headers: {\n \"X-API-Key\": apiKey,\n },\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to create token: ${response.status} ${error}`);\n }\n\n const { jwt } = await response.json();\n\n return jwt;\n}\n"],"mappings":"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","names":["SessionState","import_zod","import_react","import_react","_a","current","import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime","React","import_react","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/core/types.ts","../src/utils/webrtc.ts","../src/core/CoordinatorClient.ts","../src/core/LocalCoordinatorClient.ts","../src/core/GPUMachineClient.ts","../src/core/Reactor.ts","../src/react/ReactorProvider.tsx","../src/core/store.ts","../src/react/hooks.ts","../src/react/ReactorView.tsx","../src/react/ReactorController.tsx","../src/react/WebcamStream.tsx","../src/utils/tokens.ts"],"sourcesContent":["export * from \"./core/Reactor\";\nexport * from \"./react/ReactorProvider\";\nexport * from \"./react/ReactorView\";\nexport * from \"./react/ReactorController\";\nexport * from \"./react/WebcamStream\";\nexport * from \"./react/hooks\";\nexport * from \"./types\";\nexport * from \"./utils/tokens\";\n","// Copyright (c) 2026 Reactor Technologies, Inc. All rights reserved.\n\nexport type ReactorStatus =\n | \"disconnected\" // Not connected to anything\n | \"connecting\" // Establishing connection to coordinator\n | \"waiting\" // Connected to coordinator, waiting for GPU assignment\n | \"ready\"; // Connected to GPU machine, can send/receive messages\n\n/**\n * The message scope identifies the envelope layer a data channel message belongs to.\n * - \"application\": model-defined commands (client->runtime) and model-emitted payloads (runtime->client).\n * - \"runtime\": platform-level control messages (e.g., capabilities exchange).\n */\nexport type MessageScope = \"application\" | \"runtime\";\n\n/**\n * Describes a single named media track for SDP negotiation.\n *\n * Track names must exactly match the class attribute names defined on the\n * model's Python code. The name is encoded as the SDP MID so both sides\n * can route media by name rather than by positional index.\n *\n * Use the {@link video} and {@link audio} helper functions to create\n * instances instead of constructing this interface directly.\n */\nexport interface TrackConfig {\n name: string;\n kind: \"audio\" | \"video\";\n}\n\n/**\n * Optional configuration for a video track (reserved for future use).\n */\nexport interface VideoTrackOptions {\n /** Maximum framerate constraint for the video track. */\n maxFramerate?: number;\n}\n\n/**\n * Optional configuration for an audio track (reserved for future use).\n */\nexport interface AudioTrackOptions {\n /** Sample rate constraint for the audio track. */\n sampleRate?: number;\n}\n\n/**\n * Creates a **video** {@link TrackConfig}.\n *\n * A track declared in the **`receive`** array means the client will\n * **RECEIVE** video frames **from the model** (model → client).\n *\n * A track declared in the **`send`** array means the client will\n * **SEND** video frames **to the model** (client → model).\n *\n * Track names must be unique across both arrays — the same name cannot\n * appear in `receive` and `send`.\n *\n * @param name - The track name. Must match the model's declared track attribute name.\n * @param options - Reserved for future constraints (e.g. `maxFramerate`).\n *\n * When no `receive` array is provided to the Reactor constructor, a single\n * `video(\"main_video\")` track is used by default.\n *\n * @example\n * ```ts\n * receive: [video(\"main_video\")] // receive video from the model\n * send: [video(\"webcam\")] // send webcam video to the model\n * ```\n */\nexport function video(name: string, _options?: VideoTrackOptions): TrackConfig {\n return { name, kind: \"video\" };\n}\n\n/**\n * Creates an **audio** {@link TrackConfig}.\n *\n * A track declared in the **`receive`** array means the client will\n * **RECEIVE** audio samples **from the model** (model → client).\n *\n * A track declared in the **`send`** array means the client will\n * **SEND** audio samples **to the model** (client → model).\n *\n * Track names must be unique across both arrays — the same name cannot\n * appear in `receive` and `send`.\n *\n * @param name - The track name. Must match the model's declared track attribute name.\n * @param options - Reserved for future constraints (e.g. `sampleRate`).\n *\n * @example\n * ```ts\n * receive: [audio(\"main_audio\")] // receive audio from the model\n * send: [audio(\"mic\")] // send microphone audio to the model\n * ```\n */\nexport function audio(name: string, _options?: AudioTrackOptions): TrackConfig {\n return { name, kind: \"audio\" };\n}\n\nexport interface ReactorError {\n code: string;\n message: string;\n timestamp: number;\n recoverable: boolean;\n component: \"coordinator\" | \"gpu\" | \"livekit\";\n retryAfter?: number;\n}\n\nexport class ConflictError extends Error {\n constructor(message: string) {\n super(message);\n }\n}\n\nexport class AbortError extends Error {\n constructor(message: string) {\n super(message);\n }\n}\n\n/** Matches both our custom AbortError and the native DOMException thrown by fetch(). */\nexport function isAbortError(error: unknown): boolean {\n return (\n error instanceof AbortError ||\n (error instanceof Error && error.name === \"AbortError\")\n );\n}\n\nexport interface ReactorState {\n status: ReactorStatus;\n lastError?: ReactorError;\n}\n\n/**\n * Options for configuring the connect polling behavior.\n */\nexport interface ConnectOptions {\n /** Maximum number of SDP polling attempts before giving up. Default: 6. */\n maxAttempts?: number;\n}\n\nexport interface ConnectionStats {\n /** ICE candidate-pair round-trip time in milliseconds */\n rtt?: number;\n /** ICE candidate type: \"host\", \"srflx\", \"prflx\", or \"relay\" (TURN) */\n candidateType?: string;\n /** Estimated available outgoing bitrate in bits/second */\n availableOutgoingBitrate?: number;\n /** Received video frames per second */\n framesPerSecond?: number;\n /** Ratio of packets lost (0-1) */\n packetLossRatio?: number;\n /** Network jitter in seconds (from inbound-rtp) */\n jitter?: number;\n timestamp: number;\n}\n\nexport type ReactorEvent =\n | \"statusChanged\" //updates on the reactor status\n | \"sessionIdChanged\" //updates on the session ID.\n | \"message\" //application-scoped messages from the model\n | \"runtimeMessage\" //internal platform-level control messages (e.g. capabilities)\n | \"trackReceived\" // (name: string, track: MediaStreamTrack, stream: MediaStream)\n | \"error\" //error events with ReactorError details\n | \"sessionExpirationChanged\" //session expiration has changed\n | \"statsUpdate\"; //WebRTC stats update (RTT, etc.)\n","/**\n * Internal types for the Reactor SDK.\n */\n\nimport { z } from \"zod\";\n\nexport enum SessionState {\n CREATED,\n PENDING,\n SUSPENDED,\n WAITING,\n ACTIVE,\n INACTIVE,\n CLOSED,\n}\n\nexport const ModelSchema = z.object({\n name: z.string(),\n});\n\n// Schema used for the HTTP POST request to start a session from the CLIENT to the COORDINATOR.\nexport const CreateSessionRequestSchema = z.object({\n model: ModelSchema,\n sdp_offer: z.string(),\n extra_args: z.record(z.string(), z.any()), // Dictionary\n});\n\n// Schema used to return the session ID that was created.\nexport const CreateSessionResponseSchema = z.object({\n session_id: z.uuidv4(),\n});\n\n// GET /sessions/{session_id}/info\nexport const SessionStatusResponseSchema = z.object({\n session_id: z.uuidv4(),\n state: SessionState,\n});\n\n// Response to GET /session/{session_id} request\nexport const SessionInfoResponseSchema = SessionStatusResponseSchema.extend({\n session_info: CreateSessionRequestSchema.extend({\n session_id: z.uuidv4(),\n }),\n});\n\n// SDPParamsRequest is the request body for PUT /sessions/{session_id}/sdp_params\nexport const SDPParamsRequestSchema = z.object({\n sdp_offer: z.string(),\n extra_args: z.record(z.string(), z.any()), // Dictionary\n});\n\n// SDPParamsResponse is the response for GET /sessions/{session_id}/sdp_params\n// and for for PUT /sessions/{session_id}/sdp_params.\nexport const SDPParamsResponseSchema = z.object({\n sdp_answer: z.string(),\n extra_args: z.record(z.string(), z.any()), // Dictionary\n});\n\n// Response from GET /ice_servers endpoint (coordinator)\nexport const IceServersResponseSchema = z.object({\n ice_servers: z.array(\n z.object({\n uris: z.array(z.string()),\n credentials: z\n .object({\n username: z.string(),\n password: z.string(),\n })\n .optional(),\n })\n ),\n});\n\n// Internal connection status for individual components\nexport type InternalConnectionStatus =\n | \"disconnected\"\n | \"connecting\"\n | \"connected\"\n | \"error\";\n\n// Inferred types from Zod schemas\nexport type CreateSessionRequest = z.infer<typeof CreateSessionRequestSchema>;\nexport type CreateSessionResponse = z.infer<typeof CreateSessionResponseSchema>;\n\nexport type SDPParamsResponse = z.infer<typeof SDPParamsResponseSchema>;\nexport type SDPParamsRequest = z.infer<typeof SDPParamsRequestSchema>;\n\nexport type SessionInfoResponse = z.infer<typeof SessionInfoResponseSchema>;\nexport type SessionStatusResponse = z.infer<typeof SessionStatusResponseSchema>;\n\nexport type IceServersResponse = z.infer<typeof IceServersResponseSchema>;\n","/**\n * Stateless WebRTC utility functions for SDP exchange and peer connection management.\n * Uses @roamhq/wrtc for stable Node.js WebRTC support.\n */\n\nimport { IceServersResponse } from \"../core/types\";\nimport type { MessageScope, ConnectionStats } from \"../types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Configuration\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface WebRTCConfig {\n iceServers: RTCIceServer[];\n dataChannelLabel?: string;\n}\n\nconst DEFAULT_DATA_CHANNEL_LABEL = \"data\";\n\n// Force relay mode for testing TURN servers - set to true to force all traffic through TURN\nconst FORCE_RELAY_MODE = false;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Peer Connection Creation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Creates a new RTCPeerConnection with the specified configuration.\n * @param config WebRTC configuration with required iceServers\n */\nexport function createPeerConnection(config: WebRTCConfig): RTCPeerConnection {\n return new RTCPeerConnection({\n iceServers: config.iceServers,\n iceTransportPolicy: FORCE_RELAY_MODE ? \"relay\" : \"all\",\n });\n}\n\n/**\n * Creates a data channel on the peer connection.\n */\nexport function createDataChannel(\n pc: RTCPeerConnection,\n label?: string\n): RTCDataChannel {\n return pc.createDataChannel(label ?? DEFAULT_DATA_CHANNEL_LABEL);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// SDP Offer/Answer\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Rewrites the `a=mid:` values in an SDP string so that each media\n * transceiver uses the corresponding track name as its MID.\n *\n * The data-channel `m=application` section is left untouched.\n * The `a=group:BUNDLE` line is updated to reflect the new MIDs.\n */\nexport function rewriteMids(sdp: string, trackNames: string[]): string {\n const lines = sdp.split(\"\\r\\n\");\n let mediaIdx = 0;\n const replacements = new Map<string, string>();\n let inApplication = false;\n\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].startsWith(\"m=\")) {\n inApplication = lines[i].startsWith(\"m=application\");\n }\n if (!inApplication && lines[i].startsWith(\"a=mid:\")) {\n const oldMid = lines[i].substring(\"a=mid:\".length);\n if (mediaIdx < trackNames.length) {\n const newMid = trackNames[mediaIdx];\n replacements.set(oldMid, newMid);\n lines[i] = `a=mid:${newMid}`;\n mediaIdx++;\n }\n }\n }\n\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].startsWith(\"a=group:BUNDLE \")) {\n const parts = lines[i].split(\" \");\n for (let j = 1; j < parts.length; j++) {\n const replacement = replacements.get(parts[j]);\n if (replacement !== undefined) {\n parts[j] = replacement;\n }\n }\n lines[i] = parts.join(\" \");\n break;\n }\n }\n\n return lines.join(\"\\r\\n\");\n}\n\n/**\n * Creates an SDP offer on the peer connection.\n *\n * When `trackNames` is provided the media MIDs in the SDP are rewritten\n * to use those names so the remote side identifies transceivers by name.\n *\n * **Strategy** – try to apply the rewrite *before* `setLocalDescription`\n * (Chrome accepts this; it means the browser's internal MIDs match the\n * server's, so RTP MID header extensions agree). If the browser rejects\n * the modification (Firefox throws `InvalidModificationError`), fall\n * back to setting the original offer and rewriting only the outgoing SDP\n * string. In the fallback case the caller **must** translate MIDs on\n * the answer via {@link restoreAnswerMids} before `setRemoteDescription`.\n *\n * @returns An object with the SDP string and a flag indicating whether\n * MID munging was applied natively (`needsAnswerRestore = false`)\n * or only on the wire (`needsAnswerRestore = true`).\n */\nexport async function createOffer(\n pc: RTCPeerConnection,\n trackNames?: string[]\n): Promise<{ sdp: string; needsAnswerRestore: boolean }> {\n const offer = await pc.createOffer();\n\n let needsAnswerRestore = false;\n\n if (trackNames && trackNames.length > 0 && offer.sdp) {\n const munged = rewriteMids(offer.sdp, trackNames);\n try {\n await pc.setLocalDescription(\n new RTCSessionDescription({ type: \"offer\", sdp: munged })\n );\n } catch {\n // Firefox: \"Changing the mid of m-sections is not allowed\"\n await pc.setLocalDescription(offer);\n needsAnswerRestore = true;\n }\n } else {\n await pc.setLocalDescription(offer);\n }\n\n await waitForIceGathering(pc);\n\n const localDescription = pc.localDescription;\n if (!localDescription) {\n throw new Error(\"Failed to create local description\");\n }\n\n let sdp = localDescription.sdp;\n\n if (needsAnswerRestore && trackNames && trackNames.length > 0) {\n sdp = rewriteMids(sdp, trackNames);\n }\n\n return { sdp, needsAnswerRestore };\n}\n\n/**\n * Creates an SDP answer in response to a received offer.\n * Waits for ICE gathering to complete before returning.\n */\nexport async function createAnswer(\n pc: RTCPeerConnection,\n offer: string\n): Promise<string> {\n await setRemoteDescription(pc, offer);\n\n const answer = await pc.createAnswer();\n await pc.setLocalDescription(answer);\n\n await waitForIceGathering(pc);\n\n const localDescription = pc.localDescription;\n if (!localDescription) {\n throw new Error(\"Failed to create local description\");\n }\n\n return localDescription.sdp;\n}\n\nexport interface MidMapping {\n localToRemote: Map<string, string>;\n remoteToLocal: Map<string, string>;\n}\n\n/**\n * Builds bidirectional maps between browser-assigned numeric MIDs and\n * the human-readable track names used by the server.\n *\n * Must be called after `setLocalDescription` so that each transceiver\n * has a non-null `mid`.\n *\n * @param transceivers Ordered list of `{ name, transceiver }` pairs\n * matching the declared track entries.\n */\nexport function buildMidMapping(\n transceivers: { name: string; transceiver?: RTCRtpTransceiver }[]\n): MidMapping {\n const localToRemote = new Map<string, string>();\n const remoteToLocal = new Map<string, string>();\n for (const entry of transceivers) {\n const mid = entry.transceiver?.mid;\n if (mid) {\n localToRemote.set(mid, entry.name);\n remoteToLocal.set(entry.name, mid);\n }\n }\n return { localToRemote, remoteToLocal };\n}\n\n/**\n * Translates named MIDs in an SDP answer back to the browser-assigned\n * numeric MIDs. This is the inverse of the {@link rewriteMids}\n * transformation applied to the outgoing offer.\n *\n * @param sdp The SDP answer from the remote peer.\n * @param remoteToLocal Map from server-side MID names to the browser's\n * original numeric MID values.\n */\nexport function restoreAnswerMids(\n sdp: string,\n remoteToLocal: Map<string, string>\n): string {\n const lines = sdp.split(\"\\r\\n\");\n\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].startsWith(\"a=mid:\")) {\n const remoteMid = lines[i].substring(\"a=mid:\".length);\n const localMid = remoteToLocal.get(remoteMid);\n if (localMid !== undefined) {\n lines[i] = `a=mid:${localMid}`;\n }\n }\n\n if (lines[i].startsWith(\"a=group:BUNDLE \")) {\n const parts = lines[i].split(\" \");\n for (let j = 1; j < parts.length; j++) {\n const localMid = remoteToLocal.get(parts[j]);\n if (localMid !== undefined) {\n parts[j] = localMid;\n }\n }\n lines[i] = parts.join(\" \");\n }\n }\n\n return lines.join(\"\\r\\n\");\n}\n\n/**\n * Sets the remote description on the peer connection.\n */\nexport async function setRemoteDescription(\n pc: RTCPeerConnection,\n sdp: string\n): Promise<void> {\n const sessionDescription = new RTCSessionDescription({\n sdp: sdp,\n type: \"answer\",\n });\n await pc.setRemoteDescription(sessionDescription);\n}\n\n/**\n * Gets the local SDP description from the peer connection.\n */\nexport function getLocalDescription(pc: RTCPeerConnection): string | undefined {\n const desc = pc.localDescription;\n if (!desc) return undefined;\n return desc.sdp;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ICE Handling\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Transforms ICE servers from the coordinator API format to RTCIceServer format.\n * @param response The parsed IceServersResponse from the coordinator\n * @returns Array of RTCIceServer objects for WebRTC peer connection configuration\n */\nexport function transformIceServers(\n response: IceServersResponse\n): RTCIceServer[] {\n return response.ice_servers.map((server) => {\n const rtcServer: RTCIceServer = {\n urls: server.uris,\n };\n if (server.credentials) {\n rtcServer.username = server.credentials.username;\n rtcServer.credential = server.credentials.password;\n }\n return rtcServer;\n });\n}\n\n/**\n * Adds an ICE candidate to the peer connection.\n */\nexport async function addIceCandidate(\n pc: RTCPeerConnection,\n candidate: RTCIceCandidateInit\n): Promise<void> {\n await pc.addIceCandidate(new RTCIceCandidate(candidate));\n}\n\n/**\n * Waits for ICE gathering to complete with a timeout.\n */\nexport function waitForIceGathering(\n pc: RTCPeerConnection,\n timeoutMs: number = 5000\n): Promise<void> {\n return new Promise((resolve) => {\n if (pc.iceGatheringState === \"complete\") {\n resolve();\n return;\n }\n\n const onGatheringStateChange = () => {\n if (pc.iceGatheringState === \"complete\") {\n pc.removeEventListener(\n \"icegatheringstatechange\",\n onGatheringStateChange\n );\n resolve();\n }\n };\n\n pc.addEventListener(\"icegatheringstatechange\", onGatheringStateChange);\n\n // Timeout to prevent hanging forever\n setTimeout(() => {\n pc.removeEventListener(\"icegatheringstatechange\", onGatheringStateChange);\n resolve();\n }, timeoutMs);\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Track Management\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Adds a track to the peer connection.\n */\nexport function addTrack(\n pc: RTCPeerConnection,\n track: MediaStreamTrack,\n stream?: MediaStream\n): RTCRtpSender {\n const mediaStream = stream ?? new MediaStream([track]);\n return pc.addTrack(track, mediaStream);\n}\n\n/**\n * Removes a track from the peer connection by its sender.\n */\nexport function removeTrack(pc: RTCPeerConnection, sender: RTCRtpSender): void {\n pc.removeTrack(sender);\n}\n\n/**\n * Finds the sender for a specific track.\n */\nexport function findSenderForTrack(\n pc: RTCPeerConnection,\n track: MediaStreamTrack\n): RTCRtpSender | undefined {\n return pc.getSenders().find((sender) => sender.track === track);\n}\n\n/**\n * Removes all tracks from the peer connection.\n */\nexport function removeAllTracks(pc: RTCPeerConnection): void {\n for (const sender of pc.getSenders()) {\n pc.removeTrack(sender);\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Data Channel Messaging\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Sends a message through a data channel wrapped in the two-level envelope.\n *\n * Wire format:\n * { scope: \"application\"|\"runtime\", data: { type: <command>, data: <payload> } }\n *\n * @param channel The RTCDataChannel to send on.\n * @param command Inner command/message type (e.g. \"set_prompt\", \"requestCapabilities\").\n * @param data Payload for the command.\n * @param scope Outer envelope scope – defaults to \"application\".\n */\nexport function sendMessage(\n channel: RTCDataChannel,\n command: string,\n data: any,\n scope: MessageScope = \"application\"\n): void {\n if (channel.readyState !== \"open\") {\n throw new Error(`Data channel not open: ${channel.readyState}`);\n }\n const jsonData = typeof data === \"string\" ? JSON.parse(data) : data;\n const inner = { type: command, data: jsonData };\n const payload = { scope, data: inner };\n channel.send(JSON.stringify(payload));\n}\n\n/**\n * Parses a received data channel message, attempting JSON parse.\n */\nexport function parseMessage(data: unknown): unknown {\n if (typeof data === \"string\") {\n try {\n return JSON.parse(data);\n } catch {\n return data;\n }\n }\n return data;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Connection State\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Checks if the peer connection is in a connected state.\n */\nexport function isConnected(pc: RTCPeerConnection): boolean {\n return pc.connectionState === \"connected\";\n}\n\n/**\n * Checks if the peer connection is closed or failed.\n */\nexport function isClosed(pc: RTCPeerConnection): boolean {\n return pc.connectionState === \"closed\" || pc.connectionState === \"failed\";\n}\n\n/**\n * Closes the peer connection and cleans up.\n */\nexport function closePeerConnection(pc: RTCPeerConnection): void {\n pc.close();\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Stats Extraction\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Extracts ConnectionStats from an RTCStatsReport.\n * Reads candidate-pair, local-candidate, and inbound-rtp (video) reports.\n */\nexport function extractConnectionStats(\n report: RTCStatsReport\n): ConnectionStats {\n let rtt: number | undefined;\n let availableOutgoingBitrate: number | undefined;\n let localCandidateId: string | undefined;\n let framesPerSecond: number | undefined;\n let jitter: number | undefined;\n let packetLossRatio: number | undefined;\n\n report.forEach((stat) => {\n if (stat.type === \"candidate-pair\" && stat.state === \"succeeded\") {\n if (stat.currentRoundTripTime !== undefined) {\n rtt = stat.currentRoundTripTime * 1000;\n }\n if (stat.availableOutgoingBitrate !== undefined) {\n availableOutgoingBitrate = stat.availableOutgoingBitrate;\n }\n localCandidateId = stat.localCandidateId;\n }\n\n if (stat.type === \"inbound-rtp\" && stat.kind === \"video\") {\n if (stat.framesPerSecond !== undefined) {\n framesPerSecond = stat.framesPerSecond;\n }\n if (stat.jitter !== undefined) {\n jitter = stat.jitter;\n }\n if (\n stat.packetsReceived !== undefined &&\n stat.packetsLost !== undefined &&\n stat.packetsReceived + stat.packetsLost > 0\n ) {\n packetLossRatio =\n stat.packetsLost / (stat.packetsReceived + stat.packetsLost);\n }\n }\n });\n\n let candidateType: string | undefined;\n if (localCandidateId) {\n const localCandidate = report.get(localCandidateId);\n if (localCandidate?.candidateType) {\n candidateType = localCandidate.candidateType;\n }\n }\n\n return {\n rtt,\n candidateType,\n availableOutgoingBitrate,\n framesPerSecond,\n packetLossRatio,\n jitter,\n timestamp: Date.now(),\n };\n}\n","/**\n * The CoordinatorClient is responsible for handling the connection to the coordinator\n * via HTTP requests and WebRTC signaling.\n */\n\nimport {\n CreateSessionRequest,\n CreateSessionResponse,\n IceServersResponseSchema,\n SDPParamsRequest,\n SDPParamsResponse,\n SessionInfoResponse,\n} from \"./types\";\nimport { AbortError } from \"../types\";\nimport { transformIceServers } from \"../utils/webrtc\";\n\nexport interface CoordinatorClientOptions {\n baseUrl: string;\n jwtToken: string;\n model: string;\n}\n\n// Polling configuration\nconst INITIAL_BACKOFF_MS = 500;\nconst MAX_BACKOFF_MS = 15000;\nconst BACKOFF_MULTIPLIER = 2;\nconst DEFAULT_MAX_ATTEMPTS = 6;\n\nexport class CoordinatorClient {\n private baseUrl: string;\n private jwtToken: string;\n private model: string;\n private currentSessionId?: string;\n private abortController: AbortController;\n\n constructor(options: CoordinatorClientOptions) {\n this.baseUrl = options.baseUrl;\n this.jwtToken = options.jwtToken;\n this.model = options.model;\n this.abortController = new AbortController();\n }\n\n /**\n * Aborts any in-flight HTTP requests and polling loops.\n * A fresh AbortController is created so the client remains reusable.\n */\n abort(): void {\n this.abortController.abort();\n this.abortController = new AbortController();\n }\n\n /**\n * The current abort signal, passed to every fetch() and sleep() call.\n * Protected so subclasses can forward it to their own fetch calls.\n */\n protected get signal(): AbortSignal {\n return this.abortController.signal;\n }\n\n /**\n * Returns the authorization header with JWT Bearer token\n */\n private getAuthHeaders(): HeadersInit {\n return {\n Authorization: `Bearer ${this.jwtToken}`,\n };\n }\n\n /**\n * Fetches ICE servers from the coordinator.\n * @returns Array of RTCIceServer objects for WebRTC peer connection configuration\n */\n async getIceServers(): Promise<RTCIceServer[]> {\n console.debug(\"[CoordinatorClient] Fetching ICE servers...\");\n\n const response = await fetch(\n `${this.baseUrl}/ice_servers?model=${this.model}`,\n {\n method: \"GET\",\n headers: this.getAuthHeaders(),\n signal: this.signal,\n }\n );\n\n if (!response.ok) {\n throw new Error(`Failed to fetch ICE servers: ${response.status}`);\n }\n\n const data = await response.json();\n const parsed = IceServersResponseSchema.parse(data);\n const iceServers = transformIceServers(parsed);\n\n console.debug(\n \"[CoordinatorClient] Received ICE servers:\",\n iceServers.length\n );\n return iceServers;\n }\n\n /**\n * Creates a new session with the coordinator.\n * Expects a 200 response and stores the session ID.\n * @returns The session ID\n */\n async createSession(sdp_offer: string): Promise<string> {\n console.debug(\"[CoordinatorClient] Creating session...\");\n\n const requestBody: CreateSessionRequest = {\n model: { name: this.model },\n sdp_offer: sdp_offer,\n extra_args: {},\n };\n\n const response = await fetch(`${this.baseUrl}/sessions`, {\n method: \"POST\",\n headers: {\n ...this.getAuthHeaders(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(requestBody),\n signal: this.signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to create session: ${response.status} ${errorText}`\n );\n }\n\n const data: CreateSessionResponse = await response.json();\n this.currentSessionId = data.session_id;\n\n console.debug(\n \"[CoordinatorClient] Session created with ID:\",\n this.currentSessionId\n );\n\n return data.session_id;\n }\n\n /**\n * Gets the current session information from the coordinator.\n * @returns The session data (untyped for now)\n */\n async getSession(): Promise<SessionInfoResponse> {\n if (!this.currentSessionId) {\n throw new Error(\"No active session. Call createSession() first.\");\n }\n\n console.debug(\n \"[CoordinatorClient] Getting session info for:\",\n this.currentSessionId\n );\n\n const response = await fetch(\n `${this.baseUrl}/sessions/${this.currentSessionId}`,\n {\n method: \"GET\",\n headers: this.getAuthHeaders(),\n signal: this.signal,\n }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to get session: ${response.status} ${errorText}`);\n }\n\n const data: SessionInfoResponse = await response.json();\n\n return data;\n }\n\n /**\n * Terminates the current session by sending a DELETE request to the coordinator.\n * No-op if no session has been created yet.\n * @throws Error if the request fails (except for 404, which clears local state)\n */\n async terminateSession(): Promise<void> {\n if (!this.currentSessionId) {\n return;\n }\n\n console.debug(\n \"[CoordinatorClient] Terminating session:\",\n this.currentSessionId\n );\n\n const response = await fetch(\n `${this.baseUrl}/sessions/${this.currentSessionId}`,\n {\n method: \"DELETE\",\n headers: this.getAuthHeaders(),\n signal: this.signal,\n }\n );\n\n if (response.ok) {\n this.currentSessionId = undefined;\n return;\n }\n\n if (response.status === 404) {\n // Session doesn't exist on server, clear local state to stay consistent\n console.debug(\n \"[CoordinatorClient] Session not found on server, clearing local state:\",\n this.currentSessionId\n );\n this.currentSessionId = undefined;\n return;\n }\n\n // For other error codes, throw without clearing state (might warrant retry)\n const errorText = await response.text();\n throw new Error(\n `Failed to terminate session: ${response.status} ${errorText}`\n );\n }\n\n /**\n * Get the current session ID\n */\n getSessionId(): string | undefined {\n return this.currentSessionId;\n }\n\n /**\n * Sends an SDP offer to the server for reconnection.\n * @param sessionId - The session ID to connect to\n * @param sdpOffer - The SDP offer from the local WebRTC peer connection\n * @returns The SDP answer if ready (200), or null if pending (202)\n */\n private async sendSdpOffer(\n sessionId: string,\n sdpOffer: string\n ): Promise<string | null> {\n console.debug(\n \"[CoordinatorClient] Sending SDP offer for session:\",\n sessionId\n );\n\n const requestBody: SDPParamsRequest = {\n sdp_offer: sdpOffer,\n extra_args: {},\n };\n\n const response = await fetch(\n `${this.baseUrl}/sessions/${sessionId}/sdp_params`,\n {\n method: \"PUT\",\n headers: {\n ...this.getAuthHeaders(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(requestBody),\n signal: this.signal,\n }\n );\n\n if (response.status === 200) {\n const answerData: SDPParamsResponse = await response.json();\n console.debug(\"[CoordinatorClient] Received SDP answer immediately\");\n return answerData.sdp_answer;\n }\n\n if (response.status === 202) {\n console.debug(\n \"[CoordinatorClient] SDP offer accepted, answer pending (202)\"\n );\n return null;\n }\n\n const errorText = await response.text();\n throw new Error(\n `Failed to send SDP offer: ${response.status} ${errorText}`\n );\n }\n\n /**\n * Polls for the SDP answer with exponential backoff.\n * Used for async reconnection when the answer is not immediately available.\n * @param sessionId - The session ID to poll for\n * @param maxAttempts - Optional maximum number of polling attempts before giving up\n * @returns The SDP answer from the server\n */\n private async pollSdpAnswer(\n sessionId: string,\n maxAttempts: number = DEFAULT_MAX_ATTEMPTS\n ): Promise<string> {\n console.debug(\n \"[CoordinatorClient] Polling for SDP answer for session:\",\n sessionId\n );\n\n let backoffMs = INITIAL_BACKOFF_MS;\n let attempt = 0;\n\n while (true) {\n if (this.signal.aborted) {\n throw new AbortError(\"SDP polling aborted\");\n }\n\n if (attempt >= maxAttempts) {\n throw new Error(\n `SDP polling exceeded maximum attempts (${maxAttempts}) for session ${sessionId}`\n );\n }\n\n attempt++;\n console.debug(\n `[CoordinatorClient] SDP poll attempt ${attempt}/${maxAttempts} for session ${sessionId}`\n );\n\n const response = await fetch(\n `${this.baseUrl}/sessions/${sessionId}/sdp_params`,\n {\n method: \"GET\",\n headers: {\n ...this.getAuthHeaders(),\n \"Content-Type\": \"application/json\",\n },\n signal: this.signal,\n }\n );\n\n if (response.status === 200) {\n const answerData: SDPParamsResponse = await response.json();\n console.debug(\"[CoordinatorClient] Received SDP answer via polling\");\n return answerData.sdp_answer;\n }\n\n if (response.status === 202) {\n console.warn(\n `[CoordinatorClient] SDP answer pending (202), retrying in ${backoffMs}ms...`\n );\n\n await this.sleep(backoffMs);\n\n // Exponential backoff capped at MAX_BACKOFF_MS (15s)\n backoffMs = Math.min(backoffMs * BACKOFF_MULTIPLIER, MAX_BACKOFF_MS);\n continue;\n }\n\n // For other error codes, throw immediately\n const errorText = await response.text();\n throw new Error(\n `Failed to poll SDP answer: ${response.status} ${errorText}`\n );\n }\n }\n\n /**\n * Connects to the session by sending an SDP offer and receiving an SDP answer.\n * If sdpOffer is provided, sends it first. If the answer is pending (202),\n * falls back to polling. If no sdpOffer is provided, goes directly to polling.\n * @param sessionId - The session ID to connect to\n * @param sdpOffer - Optional SDP offer from the local WebRTC peer connection\n * @param maxAttempts - Optional maximum number of polling attempts before giving up\n * @returns The SDP answer from the server\n */\n async connect(\n sessionId: string,\n sdpOffer?: string,\n maxAttempts?: number\n ): Promise<string> {\n console.debug(\"[CoordinatorClient] Connecting to session:\", sessionId);\n\n if (sdpOffer) {\n // Reconnection: we have a new SDP offer (recalculated after ICE restart)\n // Try to send it and get an immediate answer\n const answer = await this.sendSdpOffer(sessionId, sdpOffer);\n if (answer !== null) {\n return answer;\n }\n // Server accepted but answer not ready yet (202), fall back to polling\n }\n\n // No SDP offer = async reconnection, poll until server has the answer\n return this.pollSdpAnswer(sessionId, maxAttempts);\n }\n\n /**\n * Abort-aware sleep. Resolves after `ms` milliseconds unless the\n * abort signal fires first, in which case it rejects with AbortError.\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const { signal } = this;\n if (signal.aborted) {\n reject(new AbortError(\"Sleep aborted\"));\n return;\n }\n const timer = setTimeout(() => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(new AbortError(\"Sleep aborted\"));\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n }\n}\n","/**\n * LocalCoordinatorClient is a client for connecting to a local coordinator.\n * It extends CoordinatorClient and overrides methods for local development.\n */\n\nimport { ConflictError } from \"../types\";\nimport { transformIceServers } from \"../utils/webrtc\";\nimport { CoordinatorClient } from \"./CoordinatorClient\";\nimport { IceServersResponseSchema } from \"./types\";\n\nexport class LocalCoordinatorClient extends CoordinatorClient {\n private localBaseUrl: string;\n private sdpOffer: string | undefined;\n\n constructor(baseUrl: string) {\n // Pass dummy values to parent - they won't be used for local\n super({\n baseUrl: baseUrl,\n jwtToken: \"local\",\n model: \"local\",\n });\n this.localBaseUrl = baseUrl;\n }\n\n /**\n * Gets ICE servers from the local HTTP runtime.\n * @returns The ICE server configuration\n */\n async getIceServers(): Promise<RTCIceServer[]> {\n console.debug(\"[LocalCoordinatorClient] Fetching ICE servers...\");\n const response = await fetch(`${this.localBaseUrl}/ice_servers`, {\n method: \"GET\",\n signal: this.signal,\n });\n\n if (!response.ok) {\n throw new Error(\"Failed to get ICE servers from local coordinator.\");\n }\n\n const data = await response.json();\n const parsed = IceServersResponseSchema.parse(data);\n const iceServers = transformIceServers(parsed);\n\n console.debug(\n \"[LocalCoordinatorClient] Received ICE servers:\",\n iceServers.length\n );\n return iceServers;\n }\n\n /**\n * Creates a local session by posting to /start_session.\n * @returns always \"local\"\n */\n async createSession(sdpOffer: string): Promise<string> {\n console.debug(\"[LocalCoordinatorClient] Creating local session...\");\n this.sdpOffer = sdpOffer;\n const response = await fetch(`${this.localBaseUrl}/start_session`, {\n method: \"POST\",\n signal: this.signal,\n });\n\n if (!response.ok) {\n throw new Error(\"Failed to send local start session command.\");\n }\n\n console.debug(\"[LocalCoordinatorClient] Local session created\");\n return \"local\";\n }\n\n /**\n * Connects to the local session by posting SDP params to /sdp_params.\n * @param sessionId - The session ID (ignored for local)\n * @param sdpMessage - The SDP offer from the local WebRTC peer connection\n * @returns The SDP answer from the server\n */\n async connect(sessionId: string, sdpMessage: string): Promise<string> {\n this.sdpOffer = sdpMessage || this.sdpOffer;\n console.debug(\"[LocalCoordinatorClient] Connecting to local session...\");\n const sdpBody = {\n sdp: this.sdpOffer,\n type: \"offer\",\n };\n const response = await fetch(`${this.localBaseUrl}/sdp_params`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(sdpBody),\n signal: this.signal,\n });\n\n if (!response.ok) {\n if (response.status === 409) {\n throw new ConflictError(\"Connection superseded by newer request\");\n }\n throw new Error(\"Failed to get SDP answer from local coordinator.\");\n }\n\n const sdpAnswer: { sdp: string; type: \"answer\" } = await response.json();\n console.debug(\"[LocalCoordinatorClient] Received SDP answer\");\n return sdpAnswer.sdp;\n }\n\n async terminateSession(): Promise<void> {\n console.debug(\"[LocalCoordinatorClient] Stopping local session...\");\n await fetch(`${this.localBaseUrl}/stop_session`, {\n method: \"POST\",\n signal: this.signal,\n });\n }\n}\n","/**\n * Handles the direct WebRTC connection to a GPU machine instance.\n *\n * Transceivers are created from the declared `receive` and `send` track\n * arrays and keyed by track name so that publish/unpublish/receive all\n * route by name.\n */\n\nimport * as webrtc from \"../utils/webrtc\";\nimport type { MessageScope, TrackConfig, ConnectionStats } from \"../types\";\n\ntype EventHandler = (...args: any[]) => void;\n\nexport type GPUMachineEvent =\n | \"statusChanged\"\n | \"trackReceived\"\n | \"trackRemoved\"\n | \"message\"\n | \"statsUpdate\";\n\nexport type GPUMachineStatus =\n | \"disconnected\"\n | \"connecting\"\n | \"connected\"\n | \"error\";\n\n/**\n * Interval (ms) at which the client sends runtime-channel \"ping\" messages\n * to the server so the runtime can detect stale connections quickly.\n */\nconst PING_INTERVAL_MS = 5_000;\n\ninterface TransceiverEntry {\n name: string;\n kind: \"audio\" | \"video\";\n direction: RTCRtpTransceiverDirection;\n transceiver?: RTCRtpTransceiver;\n}\nconst STATS_INTERVAL_MS = 2_000;\n\nexport class GPUMachineClient {\n private eventListeners: Map<GPUMachineEvent, Set<EventHandler>> = new Map();\n private peerConnection: RTCPeerConnection | undefined;\n private dataChannel: RTCDataChannel | undefined;\n private status: GPUMachineStatus = \"disconnected\";\n private config: webrtc.WebRTCConfig;\n private pingInterval: ReturnType<typeof setInterval> | undefined;\n\n private transceiverMap: Map<string, TransceiverEntry> = new Map();\n private publishedTracks: Map<string, MediaStreamTrack> = new Map();\n private statsInterval: ReturnType<typeof setInterval> | undefined;\n private stats: ConnectionStats | undefined;\n private peerConnected = false;\n private dataChannelOpen = false;\n\n /**\n * Browser-assigned MID ↔ track-name translation tables.\n * Populated after {@link createOffer} so that:\n * - the outgoing offer carries track names as MIDs (server expectation)\n * - the incoming answer can be translated back to browser MIDs\n */\n private midMapping?: webrtc.MidMapping;\n\n constructor(config: webrtc.WebRTCConfig) {\n this.config = config;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Event Emitter API\n // ─────────────────────────────────────────────────────────────────────────────\n\n on(event: GPUMachineEvent, handler: EventHandler): void {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners.get(event)!.add(handler);\n }\n\n off(event: GPUMachineEvent, handler: EventHandler): void {\n this.eventListeners.get(event)?.delete(handler);\n }\n\n private emit(event: GPUMachineEvent, ...args: unknown[]): void {\n this.eventListeners.get(event)?.forEach((handler) => handler(...args));\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // SDP & Connection\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Creates an SDP offer based on the declared tracks.\n *\n * **RECEIVE** = client receives from the model (model → client) → `recvonly`\n * **SEND** = client sends to the model (client → model) → `sendonly`\n *\n * Track names must be unique across both arrays. A name that appears in\n * both `receive` and `send` will throw — use distinct names instead.\n *\n * The data channel is always created first (before transceivers).\n * Must be called before connect().\n */\n async createOffer(tracks: {\n send: TrackConfig[];\n receive: TrackConfig[];\n }): Promise<string> {\n // Create peer connection if not exists\n if (!this.peerConnection) {\n this.peerConnection = webrtc.createPeerConnection(this.config);\n this.setupPeerConnectionHandlers();\n }\n\n // Create data channel before offer (offerer creates the channel)\n this.dataChannel = webrtc.createDataChannel(\n this.peerConnection,\n this.config.dataChannelLabel\n );\n this.setupDataChannelHandlers();\n\n this.transceiverMap.clear();\n\n const entries = this.buildTransceiverEntries(tracks);\n\n for (const entry of entries) {\n const transceiver = this.peerConnection.addTransceiver(entry.kind, {\n direction: entry.direction,\n });\n entry.transceiver = transceiver;\n this.transceiverMap.set(entry.name, entry);\n\n console.debug(\n `[GPUMachineClient] Transceiver added: \"${entry.name}\" (${entry.kind}, ${entry.direction})`\n );\n }\n\n const trackNames = entries.map((e) => e.name);\n const { sdp, needsAnswerRestore } = await webrtc.createOffer(\n this.peerConnection,\n trackNames\n );\n\n if (needsAnswerRestore) {\n this.midMapping = webrtc.buildMidMapping(entries);\n } else {\n this.midMapping = undefined;\n }\n\n console.debug(\n \"[GPUMachineClient] Created SDP offer with MIDs:\",\n trackNames,\n needsAnswerRestore ? \"(needs answer restore)\" : \"(native munging)\"\n );\n return sdp;\n }\n\n /**\n * Builds an ordered list of transceiver entries from the receive/send arrays.\n *\n * Each track produces exactly one transceiver — `recvonly` for receive,\n * `sendonly` for send. Bidirectional (`sendrecv`) transceivers are not\n * supported; the same track name in both arrays is an error.\n */\n private buildTransceiverEntries(tracks: {\n send: TrackConfig[];\n receive: TrackConfig[];\n }): TransceiverEntry[] {\n const map = new Map<string, TransceiverEntry>();\n\n for (const t of tracks.receive) {\n if (map.has(t.name)) {\n throw new Error(\n `Duplicate receive track name \"${t.name}\". Track names must be unique.`\n );\n }\n map.set(t.name, { name: t.name, kind: t.kind, direction: \"recvonly\" });\n }\n\n for (const t of tracks.send) {\n if (map.has(t.name)) {\n throw new Error(\n `Track name \"${t.name}\" appears in both receive and send. ` +\n `Bidirectional tracks are not supported — use distinct names ` +\n `for the inbound and outbound directions (e.g. \"${t.name}_in\" and \"${t.name}_out\").`\n );\n }\n map.set(t.name, { name: t.name, kind: t.kind, direction: \"sendonly\" });\n }\n\n return Array.from(map.values());\n }\n\n /**\n * Connects to the GPU machine using the provided SDP answer.\n * createOffer() must be called first.\n * @param sdpAnswer The SDP answer from the GPU machine\n */\n async connect(sdpAnswer: string): Promise<void> {\n if (!this.peerConnection) {\n throw new Error(\n \"[GPUMachineClient] Cannot connect - call createOffer() first\"\n );\n }\n\n if (this.peerConnection.signalingState !== \"have-local-offer\") {\n throw new Error(\n `[GPUMachineClient] Invalid signaling state: ${this.peerConnection.signalingState}`\n );\n }\n\n this.setStatus(\"connecting\");\n\n try {\n let answer = sdpAnswer;\n if (this.midMapping) {\n answer = webrtc.restoreAnswerMids(\n answer,\n this.midMapping.remoteToLocal\n );\n }\n await webrtc.setRemoteDescription(this.peerConnection, answer);\n console.debug(\"[GPUMachineClient] Remote description set\");\n } catch (error) {\n console.error(\"[GPUMachineClient] Failed to connect:\", error);\n this.setStatus(\"error\");\n throw error;\n }\n }\n\n /**\n * Disconnects from the GPU machine and cleans up resources.\n */\n async disconnect(): Promise<void> {\n this.stopPing();\n this.stopStatsPolling();\n\n for (const name of Array.from(this.publishedTracks.keys())) {\n await this.unpublishTrack(name);\n }\n\n if (this.dataChannel) {\n this.dataChannel.close();\n this.dataChannel = undefined;\n }\n\n if (this.peerConnection) {\n webrtc.closePeerConnection(this.peerConnection);\n this.peerConnection = undefined;\n }\n\n this.transceiverMap.clear();\n this.midMapping = undefined;\n this.peerConnected = false;\n this.dataChannelOpen = false;\n this.setStatus(\"disconnected\");\n console.debug(\"[GPUMachineClient] Disconnected\");\n }\n\n /**\n * Returns the current connection status.\n */\n getStatus(): GPUMachineStatus {\n return this.status;\n }\n\n /**\n * Gets the current local SDP description.\n */\n getLocalSDP(): string | undefined {\n if (!this.peerConnection) return undefined;\n return webrtc.getLocalDescription(this.peerConnection);\n }\n\n isOfferStillValid(): boolean {\n if (!this.peerConnection) return false;\n return this.peerConnection.signalingState === \"have-local-offer\";\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Messaging\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Sends a command to the GPU machine via the data channel.\n * @param command The command to send\n * @param data The data to send with the command. These are the parameters for the command, matching the schema in the capabilities dictionary.\n * @param scope The message scope – \"application\" (default) for model commands, \"runtime\" for platform-level messages.\n */\n sendCommand(\n command: string,\n data: any,\n scope: MessageScope = \"application\"\n ): void {\n if (!this.dataChannel) {\n throw new Error(\"[GPUMachineClient] Data channel not available\");\n }\n\n try {\n webrtc.sendMessage(this.dataChannel, command, data, scope);\n } catch (error) {\n console.warn(\"[GPUMachineClient] Failed to send message:\", error);\n }\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Track Publishing\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Publishes a MediaStreamTrack to the named send track.\n *\n * @param name The declared track name (must exist in transceiverMap with a sendable direction).\n * @param track The MediaStreamTrack to publish.\n */\n async publishTrack(name: string, track: MediaStreamTrack): Promise<void> {\n if (!this.peerConnection) {\n throw new Error(\n `[GPUMachineClient] Cannot publish track \"${name}\" - not initialized`\n );\n }\n\n if (this.status !== \"connected\") {\n throw new Error(\n `[GPUMachineClient] Cannot publish track \"${name}\" - not connected`\n );\n }\n\n const entry = this.transceiverMap.get(name);\n if (!entry || !entry.transceiver) {\n throw new Error(\n `[GPUMachineClient] Cannot publish track \"${name}\" - no transceiver (was it declared in tracks.send?)`\n );\n }\n\n if (entry.direction === \"recvonly\") {\n throw new Error(\n `[GPUMachineClient] Cannot publish track \"${name}\" - transceiver is recvonly`\n );\n }\n\n try {\n // Use replaceTrack on the existing transceiver's sender.\n // This doesn't require renegotiation.\n await entry.transceiver.sender.replaceTrack(track);\n this.publishedTracks.set(name, track);\n console.debug(\n `[GPUMachineClient] Track \"${name}\" published successfully`\n );\n } catch (error) {\n console.error(\n `[GPUMachineClient] Failed to publish track \"${name}\":`,\n error\n );\n throw error;\n }\n }\n\n /**\n * Unpublishes the track with the given name.\n */\n async unpublishTrack(name: string): Promise<void> {\n const entry = this.transceiverMap.get(name);\n if (!entry?.transceiver || !this.publishedTracks.has(name)) return;\n\n try {\n // Replace with null to stop sending without renegotiation\n await entry.transceiver.sender.replaceTrack(null);\n console.debug(\n `[GPUMachineClient] Track \"${name}\" unpublished successfully`\n );\n } catch (error) {\n console.error(\n `[GPUMachineClient] Failed to unpublish track \"${name}\":`,\n error\n );\n throw error;\n } finally {\n this.publishedTracks.delete(name);\n }\n }\n\n /**\n * Returns the currently published track for the given name.\n */\n getPublishedTrack(name: string): MediaStreamTrack | undefined {\n return this.publishedTracks.get(name);\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Getters\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Returns the remote media stream from the GPU machine.\n */\n getRemoteStream(): MediaStream | undefined {\n if (!this.peerConnection) return undefined;\n\n const receivers = this.peerConnection.getReceivers();\n const tracks = receivers\n .map((r) => r.track)\n .filter((t): t is MediaStreamTrack => t !== null);\n\n if (tracks.length === 0) return undefined;\n return new MediaStream(tracks);\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Ping (Client Liveness)\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Starts sending periodic \"ping\" messages on the runtime channel so the\n * server can detect stale connections quickly.\n */\n private startPing(): void {\n this.stopPing();\n this.pingInterval = setInterval(() => {\n if (this.dataChannel?.readyState === \"open\") {\n try {\n webrtc.sendMessage(this.dataChannel, \"ping\", {}, \"runtime\");\n } catch {\n // Silently ignore -- data channel may be closing\n }\n }\n }, PING_INTERVAL_MS);\n }\n\n /**\n * Stops the periodic ping.\n */\n private stopPing(): void {\n if (this.pingInterval !== undefined) {\n clearInterval(this.pingInterval);\n this.pingInterval = undefined;\n }\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Stats Polling (RTT)\n // ─────────────────────────────────────────────────────────────────────────────\n\n getStats(): ConnectionStats | undefined {\n return this.stats;\n }\n\n private startStatsPolling(): void {\n this.stopStatsPolling();\n this.statsInterval = setInterval(async () => {\n if (!this.peerConnection) return;\n try {\n const report = await this.peerConnection.getStats();\n this.stats = webrtc.extractConnectionStats(report);\n this.emit(\"statsUpdate\", this.stats);\n } catch {\n // Silently ignore – connection may be closing\n }\n }, STATS_INTERVAL_MS);\n }\n\n private stopStatsPolling(): void {\n if (this.statsInterval !== undefined) {\n clearInterval(this.statsInterval);\n this.statsInterval = undefined;\n }\n this.stats = undefined;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Private Helpers\n // ─────────────────────────────────────────────────────────────────────────────\n\n private checkFullyConnected(): void {\n if (this.peerConnected && this.dataChannelOpen) {\n this.setStatus(\"connected\");\n this.startStatsPolling();\n }\n }\n\n private setStatus(newStatus: GPUMachineStatus): void {\n if (this.status !== newStatus) {\n this.status = newStatus;\n this.emit(\"statusChanged\", newStatus);\n }\n }\n\n private setupPeerConnectionHandlers(): void {\n if (!this.peerConnection) return;\n\n this.peerConnection.onconnectionstatechange = () => {\n const state = this.peerConnection?.connectionState;\n console.debug(\"[GPUMachineClient] Connection state:\", state);\n\n if (state) {\n switch (state) {\n case \"connected\":\n this.peerConnected = true;\n this.checkFullyConnected();\n break;\n case \"disconnected\":\n case \"closed\":\n this.peerConnected = false;\n this.setStatus(\"disconnected\");\n break;\n case \"failed\":\n this.peerConnected = false;\n this.setStatus(\"error\");\n break;\n }\n }\n };\n\n this.peerConnection.ontrack = (event) => {\n // Resolve track name via transceiver object identity (reliable\n // across browsers regardless of MID rewriting).\n let trackName: string | undefined;\n for (const [name, entry] of this.transceiverMap) {\n if (entry.transceiver === event.transceiver) {\n trackName = name;\n break;\n }\n }\n trackName ??= event.transceiver.mid ?? `unknown-${event.track.id}`;\n\n console.debug(\n `[GPUMachineClient] Track received: \"${trackName}\" (${event.track.kind}, mid=${event.transceiver.mid})`\n );\n const stream = event.streams[0] ?? new MediaStream([event.track]);\n this.emit(\"trackReceived\", trackName, event.track, stream);\n };\n\n this.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n console.debug(\"[GPUMachineClient] ICE candidate:\", event.candidate);\n }\n };\n\n this.peerConnection.onicecandidateerror = (event) => {\n console.warn(\"[GPUMachineClient] ICE candidate error:\", event);\n };\n\n // Handle data channel created by remote peer (if we're the answerer)\n this.peerConnection.ondatachannel = (event) => {\n console.debug(\"[GPUMachineClient] Data channel received from remote\");\n this.dataChannel = event.channel;\n this.setupDataChannelHandlers();\n };\n }\n\n private setupDataChannelHandlers(): void {\n if (!this.dataChannel) return;\n\n this.dataChannel.onopen = () => {\n console.debug(\"[GPUMachineClient] Data channel open\");\n this.dataChannelOpen = true;\n this.startPing();\n this.checkFullyConnected();\n };\n\n this.dataChannel.onclose = () => {\n console.debug(\"[GPUMachineClient] Data channel closed\");\n this.dataChannelOpen = false;\n this.stopPing();\n };\n\n this.dataChannel.onerror = (error) => {\n console.error(\"[GPUMachineClient] Data channel error:\", error);\n };\n\n this.dataChannel.onmessage = (event) => {\n const rawData = webrtc.parseMessage(event.data) as any;\n console.debug(\"[GPUMachineClient] Received message:\", rawData);\n\n try {\n // Parse the outer envelope { scope: \"application\"|\"runtime\", data: ... }\n if (rawData?.scope === \"application\" && rawData?.data !== undefined) {\n this.emit(\"message\", rawData.data, \"application\" as MessageScope);\n } else if (\n rawData?.scope === \"runtime\" &&\n rawData?.data !== undefined\n ) {\n this.emit(\"message\", rawData.data, \"runtime\" as MessageScope);\n } else {\n // Legacy / unknown format – treat as application\n console.warn(\n \"[GPUMachineClient] Received message without envelope, treating as application\"\n );\n this.emit(\"message\", rawData, \"application\" as MessageScope);\n }\n } catch (error) {\n console.error(\n \"[GPUMachineClient] Failed to parse/validate message:\",\n error\n );\n }\n };\n }\n}\n","import {\n type ReactorEvent,\n type ReactorStatus,\n type ReactorState,\n type ReactorError,\n type MessageScope,\n type ConnectOptions,\n type TrackConfig,\n type ConnectionStats,\n isAbortError,\n ConflictError,\n} from \"../types\";\nimport { CoordinatorClient } from \"./CoordinatorClient\";\nimport { LocalCoordinatorClient } from \"./LocalCoordinatorClient\";\nimport { GPUMachineClient, GPUMachineStatus } from \"./GPUMachineClient\";\nimport { z } from \"zod\";\n\nconst LOCAL_COORDINATOR_URL = \"http://localhost:8080\";\nexport const PROD_COORDINATOR_URL = \"https://api.reactor.inc\";\n\nconst TrackConfigSchema = z.object({\n name: z.string(),\n kind: z.enum([\"audio\", \"video\"]),\n});\n\nconst OptionsSchema = z.object({\n coordinatorUrl: z.string().default(PROD_COORDINATOR_URL),\n modelName: z.string(),\n local: z.boolean().default(false),\n /**\n * Tracks the client **RECEIVES** from the model (model → client).\n * Each entry produces a `recvonly` transceiver.\n * Names must be unique across both `receive` and `send`.\n *\n * When omitted, defaults to a single video track named `\"main_video\"`.\n * Pass an explicit empty array to opt out of the default.\n */\n receive: z\n .array(TrackConfigSchema)\n .default([{ name: \"main_video\", kind: \"video\" }]),\n /**\n * Tracks the client **SENDS** to the model (client → model).\n * Each entry produces a `sendonly` transceiver.\n * Names must be unique across both `receive` and `send`.\n */\n send: z.array(TrackConfigSchema).default([]),\n});\nexport type Options = z.input<typeof OptionsSchema>;\n\ntype EventHandler = (...args: any[]) => void;\n\nexport class Reactor {\n private coordinatorClient: CoordinatorClient | undefined;\n private machineClient: GPUMachineClient | undefined;\n private status: ReactorStatus = \"disconnected\";\n private coordinatorUrl: string;\n private lastError?: ReactorError;\n private model: string;\n private sessionExpiration?: number;\n private local: boolean;\n /** Tracks the client RECEIVES from the model (model → client). */\n private receive: TrackConfig[];\n /** Tracks the client SENDS to the model (client → model). */\n private send: TrackConfig[];\n private sessionId?: string;\n\n constructor(options: Options) {\n const validatedOptions = OptionsSchema.parse(options);\n this.coordinatorUrl = validatedOptions.coordinatorUrl;\n\n // TODO(REA-146) Properly accept version from parameter.\n this.model = validatedOptions.modelName;\n this.local = validatedOptions.local;\n this.receive = validatedOptions.receive;\n this.send = validatedOptions.send;\n if (this.local && options.coordinatorUrl === undefined) {\n this.coordinatorUrl = LOCAL_COORDINATOR_URL;\n }\n }\n\n // Generic event map\n private eventListeners: Map<ReactorEvent, Set<EventHandler>> = new Map();\n\n // Event Emitter API\n on(event: ReactorEvent, handler: EventHandler) {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners.get(event)!.add(handler);\n }\n\n off(event: ReactorEvent, handler: EventHandler) {\n this.eventListeners.get(event)?.delete(handler);\n }\n\n emit(event: ReactorEvent, ...args: any[]) {\n this.eventListeners.get(event)?.forEach((handler) => handler(...args));\n }\n\n /**\n * Sends a command to the model via the data channel.\n *\n * @param command The command name.\n * @param data The command payload.\n * @param scope \"application\" (default) for model commands, \"runtime\" for platform messages.\n */\n async sendCommand(\n command: string,\n data: any,\n scope: MessageScope = \"application\"\n ): Promise<void> {\n // Synchronous validation - throw immediately\n if (process.env.NODE_ENV !== \"development\" && this.status !== \"ready\") {\n const errorMessage = `Cannot send message, status is ${this.status}`;\n console.warn(\"[Reactor]\", errorMessage);\n return;\n }\n\n try {\n this.machineClient?.sendCommand(command, data, scope);\n } catch (error) {\n // Async operational error - emit event only\n console.error(\"[Reactor] Failed to send message:\", error);\n this.createError(\n \"MESSAGE_SEND_FAILED\",\n `Failed to send message: ${error}`,\n \"gpu\",\n true\n );\n // Don't re-throw - let the error event handle it\n }\n }\n\n /**\n * Publishes a MediaStreamTrack to a named send track.\n *\n * @param name The declared send track name (e.g. \"webcam\").\n * @param track The MediaStreamTrack to publish.\n */\n async publishTrack(name: string, track: MediaStreamTrack): Promise<void> {\n if (process.env.NODE_ENV !== \"development\" && this.status !== \"ready\") {\n console.warn(\n `[Reactor] Cannot publish track \"${name}\", status is ${this.status}`\n );\n return;\n }\n\n try {\n await this.machineClient?.publishTrack(name, track);\n } catch (error) {\n console.error(`[Reactor] Failed to publish track \"${name}\":`, error);\n this.createError(\n \"TRACK_PUBLISH_FAILED\",\n `Failed to publish track \"${name}\": ${error}`,\n \"gpu\",\n true\n );\n }\n }\n\n /**\n * Unpublishes the track with the given name.\n *\n * @param name The declared send track name to unpublish.\n */\n async unpublishTrack(name: string): Promise<void> {\n try {\n await this.machineClient?.unpublishTrack(name);\n } catch (error) {\n console.error(`[Reactor] Failed to unpublish track \"${name}\":`, error);\n this.createError(\n \"TRACK_UNPUBLISH_FAILED\",\n `Failed to unpublish track \"${name}\": ${error}`,\n \"gpu\",\n true\n );\n }\n }\n\n /**\n * Public method for reconnecting to an existing session, that may have been interrupted but can be recovered.\n * @param options Optional connect options (e.g. maxAttempts for SDP polling)\n */\n async reconnect(options?: ConnectOptions): Promise<void> {\n if (!this.sessionId || !this.coordinatorClient) {\n console.warn(\"[Reactor] No active session to reconnect to.\");\n return;\n }\n\n if (this.status === \"ready\") {\n console.warn(\"[Reactor] Already connected, no need to reconnect.\");\n return;\n }\n\n this.setStatus(\"connecting\");\n\n if (!this.machineClient) {\n // Get ICE servers from coordinator\n const iceServers = await this.coordinatorClient.getIceServers();\n this.machineClient = new GPUMachineClient({ iceServers });\n this.setupMachineClientHandlers();\n }\n\n const sdpOffer = await this.machineClient.createOffer({\n send: this.send,\n receive: this.receive,\n });\n\n // Send offer to coordinator and get answer.\n try {\n const sdpAnswer = await this.coordinatorClient.connect(\n this.sessionId,\n sdpOffer,\n options?.maxAttempts\n );\n // Connect to GPU machine with the answer.\n // Status transitions to \"ready\" via the statusChanged handler once\n // the peer connection and data channel are fully open.\n await this.machineClient.connect(sdpAnswer);\n } catch (error) {\n // disconnect() already aborted the polling and cleaned up state — nothing to do.\n if (isAbortError(error)) return;\n\n let recoverable = false;\n if (error instanceof ConflictError) {\n recoverable = true;\n }\n console.error(\"[Reactor] Failed to reconnect:\", error);\n // Disconnect without recovery, as the session \"connect\" call on the coordinator failed\n this.disconnect(recoverable);\n this.createError(\n \"RECONNECTION_FAILED\",\n `Failed to reconnect: ${error}`,\n \"coordinator\",\n true\n );\n }\n }\n\n /**\n * Connects to the coordinator and waits for a GPU to be assigned.\n * Once a GPU is assigned, the Reactor will connect to the gpu machine via WebRTC.\n * If no authentication is provided and not in local mode, an error is thrown.\n * @param jwtToken Optional JWT token for authentication\n * @param options Optional connect options (e.g. maxAttempts for SDP polling)\n */\n async connect(jwtToken?: string, options?: ConnectOptions): Promise<void> {\n console.debug(\"[Reactor] Connecting, status:\", this.status);\n\n if (jwtToken == undefined && !this.local) {\n throw new Error(\"No authentication provided and not in local mode\");\n }\n\n if (this.status !== \"disconnected\") {\n throw new Error(\"Already connected or connecting\");\n }\n this.setStatus(\"connecting\");\n\n try {\n console.debug(\n \"[Reactor] Connecting to coordinator with authenticated URL\"\n );\n\n this.coordinatorClient = this.local\n ? new LocalCoordinatorClient(this.coordinatorUrl)\n : new CoordinatorClient({\n baseUrl: this.coordinatorUrl,\n jwtToken: jwtToken!, // Safe: validated above\n model: this.model,\n });\n\n // Get ICE servers from coordinator\n const iceServers = await this.coordinatorClient.getIceServers();\n\n // Create GPUMachineClient and generate SDP offer\n this.machineClient = new GPUMachineClient({ iceServers });\n this.setupMachineClientHandlers();\n\n const sdpOffer = await this.machineClient.createOffer({\n send: this.send,\n receive: this.receive,\n });\n\n // Create session passing SDP offer. We will get the answer polling the sdp_offer endpoint.\n const sessionId = await this.coordinatorClient.createSession(sdpOffer);\n this.setSessionId(sessionId);\n\n // Connect to coordinator and get SDP Answer.\n // We don't pass the sdp offer here because we passed it already when creating the session.\n const sdpAnswer = await this.coordinatorClient.connect(\n sessionId,\n undefined,\n options?.maxAttempts\n );\n\n // Connect to GPU machine with the answer\n await this.machineClient.connect(sdpAnswer);\n } catch (error) {\n // disconnect() already aborted the polling and cleaned up state — nothing to do.\n if (isAbortError(error)) return;\n\n console.error(\"[Reactor] Connection failed:\", error);\n this.createError(\n \"CONNECTION_FAILED\",\n `Connection failed: ${error}`,\n \"coordinator\",\n true\n );\n // Non-recoverable disconnect: terminates the server-side session (DELETE)\n // and cleans up all local state (machine client, session ID, etc.)\n try {\n await this.disconnect(false);\n } catch (disconnectError) {\n console.error(\n \"[Reactor] Failed to clean up after connection failure:\",\n disconnectError\n );\n }\n throw error;\n }\n }\n\n /**\n * Sets up event handlers for the machine client.\n *\n * Each handler captures the client reference at registration time and\n * ignores events if this.machineClient has since changed (e.g. after\n * disconnect + reconnect), preventing stale WebRTC teardown events from\n * interfering with a new connection.\n */\n private setupMachineClientHandlers(): void {\n if (!this.machineClient) return;\n const client = this.machineClient;\n\n client.on(\"message\", (message: any, scope: MessageScope) => {\n if (this.machineClient !== client) return;\n if (scope === \"application\") {\n this.emit(\"message\", message);\n } else if (scope === \"runtime\") {\n this.emit(\"runtimeMessage\", message);\n }\n });\n\n client.on(\"statusChanged\", (status: GPUMachineStatus) => {\n if (this.machineClient !== client) return;\n switch (status) {\n case \"connected\":\n this.setStatus(\"ready\");\n break;\n case \"disconnected\":\n this.disconnect(true);\n break;\n case \"error\":\n this.createError(\n \"GPU_CONNECTION_ERROR\",\n \"GPU machine connection failed\",\n \"gpu\",\n true\n );\n this.disconnect();\n break;\n }\n });\n\n client.on(\n \"trackReceived\",\n (name: string, track: MediaStreamTrack, stream: MediaStream) => {\n if (this.machineClient !== client) return;\n this.emit(\"trackReceived\", name, track, stream);\n }\n );\n\n client.on(\"statsUpdate\", (stats: ConnectionStats) => {\n if (this.machineClient !== client) return;\n this.emit(\"statsUpdate\", stats);\n });\n }\n\n /**\n * Disconnects from the coordinator and the gpu machine.\n * Ensures cleanup completes even if individual disconnections fail.\n */\n async disconnect(recoverable: boolean = false) {\n if (this.status === \"disconnected\" && !this.sessionId) {\n console.warn(\"[Reactor] Already disconnected\");\n return;\n }\n\n // Abort any in-flight coordinator requests (SDP polling, pending fetches)\n // before tearing down. abort() resets the controller so terminateSession()\n // below can still make its own HTTP call.\n this.coordinatorClient?.abort();\n\n if (this.coordinatorClient && !recoverable) {\n try {\n await this.coordinatorClient.terminateSession();\n } catch (error) {\n console.error(\"[Reactor] Error terminating session:\", error);\n }\n this.coordinatorClient = undefined;\n }\n\n // Disconnect machine client with error handling\n if (this.machineClient) {\n try {\n await this.machineClient.disconnect();\n } catch (error) {\n console.error(\"[Reactor] Error disconnecting from GPU machine:\", error);\n // Continue with cleanup even if machine disconnect fails\n }\n if (!recoverable) {\n this.machineClient = undefined;\n }\n }\n\n this.setStatus(\"disconnected\");\n if (!recoverable) {\n this.setSessionExpiration(undefined);\n this.setSessionId(undefined);\n }\n }\n\n private setSessionId(newSessionId: string | undefined) {\n console.debug(\n \"[Reactor] Setting session ID:\",\n newSessionId,\n \"from\",\n this.sessionId\n );\n if (this.sessionId !== newSessionId) {\n this.sessionId = newSessionId;\n this.emit(\"sessionIdChanged\", newSessionId);\n }\n }\n\n getSessionId(): string | undefined {\n return this.sessionId;\n }\n\n private setStatus(newStatus: ReactorStatus) {\n console.debug(\"[Reactor] Setting status:\", newStatus, \"from\", this.status);\n if (this.status !== newStatus) {\n this.status = newStatus;\n this.emit(\"statusChanged\", newStatus);\n }\n }\n\n getStatus(): ReactorStatus {\n return this.status;\n }\n\n /**\n * Set the session expiration time.\n * @param newSessionExpiration The new session expiration time in seconds.\n */\n private setSessionExpiration(newSessionExpiration: number | undefined) {\n console.debug(\n \"[Reactor] Setting session expiration:\",\n newSessionExpiration\n );\n if (this.sessionExpiration !== newSessionExpiration) {\n this.sessionExpiration = newSessionExpiration;\n this.emit(\"sessionExpirationChanged\", newSessionExpiration);\n }\n }\n\n /**\n * Get the current state including status, error, and waiting info\n */\n getState(): ReactorState {\n return {\n status: this.status,\n lastError: this.lastError,\n };\n }\n\n /**\n * Get the last error that occurred\n */\n getLastError(): ReactorError | undefined {\n return this.lastError;\n }\n\n getStats(): ConnectionStats | undefined {\n return this.machineClient?.getStats();\n }\n\n /**\n * Create and store an error\n */\n private createError(\n code: string,\n message: string,\n component: \"coordinator\" | \"gpu\" | \"livekit\",\n recoverable: boolean,\n retryAfter?: number\n ) {\n this.lastError = {\n code,\n message,\n timestamp: Date.now(),\n recoverable,\n component,\n retryAfter,\n };\n\n // Emit error event\n this.emit(\"error\", this.lastError);\n }\n}\n","\"use client\";\n\nimport { ReactNode, useContext, useEffect, useRef, useState } from \"react\";\nimport {\n createReactorStore,\n initReactorStore,\n ReactorContext,\n ReactorStore,\n ReactorStoreApi,\n type ReactorInitializationProps,\n} from \"../core/store\";\nimport { useStore } from \"zustand\";\nimport type { ConnectOptions } from \"../types\";\n\n/**\n * Options for the React provider's connect behavior.\n * Extends the core ConnectOptions with autoConnect for the React lifecycle.\n */\nexport interface ReactorConnectOptions extends ConnectOptions {\n /** Whether to automatically connect when the provider mounts. Default: false. */\n autoConnect?: boolean;\n}\n\n// Provider props\ninterface ReactorProviderProps extends ReactorInitializationProps {\n connectOptions?: ReactorConnectOptions;\n jwtToken?: string;\n children: ReactNode;\n}\n\n// tsx component\nexport function ReactorProvider({\n children,\n connectOptions,\n jwtToken,\n ...props\n}: ReactorProviderProps) {\n // Stable Reactor instance\n const storeRef = useRef<ReactorStoreApi | undefined>(undefined);\n const firstRender = useRef(true);\n // State to trigger re-renders when store changes\n const [_storeVersion, setStoreVersion] = useState(0);\n\n if (storeRef.current === undefined) {\n console.debug(\"[ReactorProvider] Creating new reactor store\");\n // We create the store without autoconnecting, to avoid duplicate connections.\n // We actually connect when the component is mounted, to be on sync with the react component lifecycle.\n storeRef.current = createReactorStore(\n initReactorStore({\n ...props,\n jwtToken,\n })\n );\n console.debug(\"[ReactorProvider] Reactor store created successfully\");\n }\n\n // Destructure connectOptions with defaults\n const { autoConnect = false, ...pollingOptions } = connectOptions ?? {};\n\n const { coordinatorUrl, modelName, local, receive, send } = props;\n const maxAttempts = pollingOptions.maxAttempts;\n\n // Handle page unload (refresh, close, navigate away) with non-recoverable disconnect\n useEffect(() => {\n const handleBeforeUnload = () => {\n console.debug(\n \"[ReactorProvider] Page unloading, performing non-recoverable disconnect\"\n );\n // Call disconnect synchronously - we can't await here as the page is unloading\n // The disconnect(false) ensures non-recoverable cleanup (stops session, clears state)\n storeRef.current?.getState().internal.reactor.disconnect(false);\n };\n\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\n\n return () => {\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\n };\n }, []);\n\n useEffect(() => {\n if (firstRender.current) {\n firstRender.current = false;\n\n // We know as a fact that the store is not undefined at this point\n const current = storeRef.current!;\n if (\n autoConnect &&\n current.getState().status === \"disconnected\" &&\n jwtToken\n ) {\n console.debug(\n \"[ReactorProvider] Starting autoconnect in first render...\"\n );\n current\n .getState()\n .connect(jwtToken, pollingOptions)\n .then(() => {\n console.debug(\n \"[ReactorProvider] Autoconnect successful in first render\"\n );\n })\n .catch((error) => {\n console.error(\n \"[ReactorProvider] Failed to autoconnect in first render:\",\n error\n );\n });\n }\n return () => {\n console.debug(\n \"[ReactorProvider] Disconnecting in cleanup for first render\"\n );\n current\n .getState()\n .disconnect()\n .then(() => {\n console.debug(\n \"[ReactorProvider] Disconnect completed successfully in cleanup for first render\"\n );\n })\n .catch((error) => {\n console.error(\n \"[ReactorProvider] Failed to disconnect in cleanup for first render:\",\n error\n );\n });\n };\n }\n\n console.debug(\"[ReactorProvider] Updating reactor store\");\n storeRef.current = createReactorStore(\n initReactorStore({\n coordinatorUrl,\n modelName,\n local,\n receive,\n send,\n jwtToken,\n } satisfies ReactorInitializationProps)\n );\n\n // Store current reference to the store in the return\n const current = storeRef.current!;\n\n // Increment version to trigger re-render and propagate new store to Provider\n setStoreVersion((v) => v + 1);\n console.debug(\n \"[ReactorProvider] Reactor store updated successfully, and increased version\"\n );\n\n if (\n autoConnect &&\n current.getState().status === \"disconnected\" &&\n jwtToken\n ) {\n console.debug(\"[ReactorProvider] Starting autoconnect...\");\n current\n .getState()\n .connect(jwtToken, pollingOptions)\n .then(() => {\n console.debug(\"[ReactorProvider] Autoconnect successful\");\n })\n .catch((error) => {\n console.error(\"[ReactorProvider] Failed to autoconnect:\", error);\n });\n }\n\n return () => {\n console.debug(\"[ReactorProvider] Disconnecting in cleanup\");\n current\n .getState()\n .disconnect()\n .then(() => {\n console.debug(\n \"[ReactorProvider] Disconnect completed successfully in cleanup\"\n );\n })\n .catch((error) => {\n console.error(\"[ReactorProvider] Failed to disconnect:\", error);\n });\n };\n }, [\n coordinatorUrl,\n modelName,\n autoConnect,\n local,\n receive,\n send,\n jwtToken,\n maxAttempts,\n ]);\n\n return (\n <ReactorContext.Provider value={storeRef.current}>\n {children}\n </ReactorContext.Provider>\n );\n}\n\nexport function useReactorStore<T = ReactorStore>(\n selector: (state: ReactorStore) => T\n): T {\n const ctx = useContext(ReactorContext);\n if (!ctx) {\n throw new Error(\"useReactor must be used within a ReactorProvider\");\n }\n\n return useStore(ctx, selector);\n}\n","import { StoreApi } from \"zustand\";\nimport type {\n ReactorStatus,\n ReactorError,\n MessageScope,\n ConnectOptions,\n} from \"../types\";\nimport { Reactor, type Options as ReactorOptions } from \"./Reactor\";\nimport { create } from \"zustand/react\";\nimport { createContext } from \"react\";\n\nexport type ReactorStoreApi = ReturnType<typeof createReactorStore>;\n\nexport interface ReactorState {\n status: ReactorStatus;\n /**\n * Media tracks received from the model, keyed by track name.\n *\n * Each entry maps a declared **receive** track name (e.g. `\"main_video\"`,\n * `\"main_audio\"`) to the live `MediaStreamTrack` delivered by the model.\n */\n tracks: Record<string, MediaStreamTrack>;\n lastError?: ReactorError;\n sessionId?: string;\n sessionExpiration?: number;\n jwtToken?: string;\n}\n\nexport interface ReactorActions {\n sendCommand(command: string, data: any, scope?: MessageScope): Promise<void>;\n connect(jwtToken?: string, options?: ConnectOptions): Promise<void>;\n disconnect(recoverable?: boolean): Promise<void>;\n publish(name: string, track: MediaStreamTrack): Promise<void>;\n unpublish(name: string): Promise<void>;\n reconnect(options?: ConnectOptions): Promise<void>;\n}\n\n// Internal state not exposed to components\ninterface ReactorInternalState {\n reactor: Reactor;\n}\n\nexport const ReactorContext = createContext<ReactorStoreApi | undefined>(\n undefined\n);\n\nexport type ReactorStore = ReactorState &\n ReactorActions & {\n internal: ReactorInternalState;\n };\n\n// We introduce two methods to perform authentication:\n// - putting the auth information inside of the ReactorProvider props, and then calling connect() without arguments.\n// - not putting anything in the props, and then calling connect() passing as arguments the auth information.\n// When in the first case, the auth information is saved in the STATE. Then, when you call connect() without arguments,\n// the actual auth information is fetched from that STATE.\n// In the second case, you pass the auth information directly into the function in the Reactor core.\nexport const defaultInitState: ReactorState = {\n status: \"disconnected\",\n tracks: {},\n lastError: undefined,\n sessionExpiration: undefined,\n jwtToken: undefined,\n sessionId: undefined,\n};\n\nexport interface ReactorInitializationProps extends ReactorOptions {\n jwtToken?: string;\n}\n\nexport const initReactorStore = (\n props: ReactorInitializationProps\n): ReactorState & ReactorInitializationProps => {\n return {\n ...defaultInitState,\n // These are only used for dev initialization, not exposed in the store\n ...props,\n };\n};\n\nexport const createReactorStore = (\n initProps: ReactorInitializationProps,\n publicState: ReactorState = defaultInitState\n): StoreApi<ReactorStore> => {\n console.debug(\"[ReactorStore] Creating store\", {\n coordinatorUrl: initProps.coordinatorUrl,\n jwtToken: initProps.jwtToken,\n initialState: publicState,\n });\n\n return create<ReactorStore>()((set, get) => {\n const reactor = new Reactor(initProps);\n\n console.debug(\"[ReactorStore] Setting up event listeners\");\n\n reactor.on(\"statusChanged\", (newStatus: ReactorStatus) => {\n console.debug(\"[ReactorStore] Status changed\", {\n oldStatus: get().status,\n newStatus,\n });\n if (newStatus === \"disconnected\") {\n set({ status: newStatus, tracks: {} });\n } else {\n set({ status: newStatus });\n }\n });\n\n reactor.on(\n \"sessionExpirationChanged\",\n (newSessionExpiration: number | undefined) => {\n console.debug(\"[ReactorStore] Session expiration changed\", {\n oldSessionExpiration: get().sessionExpiration,\n newSessionExpiration: newSessionExpiration,\n });\n set({ sessionExpiration: newSessionExpiration });\n }\n );\n\n reactor.on(\"trackReceived\", (name: string, track: MediaStreamTrack) => {\n console.debug(\"[ReactorStore] Track received\", {\n name,\n kind: track.kind,\n id: track.id,\n });\n set({ tracks: { ...get().tracks, [name]: track } });\n });\n\n reactor.on(\"error\", (error: ReactorError) => {\n console.debug(\"[ReactorStore] Error occurred\", error);\n set({ lastError: error });\n });\n\n reactor.on(\"sessionIdChanged\", (newSessionId: string | undefined) => {\n console.debug(\"[ReactorStore] Session ID changed\", {\n oldSessionId: get().sessionId,\n newSessionId: newSessionId,\n });\n set({ sessionId: newSessionId });\n });\n\n return {\n ...publicState,\n jwtToken: initProps.jwtToken,\n internal: { reactor },\n\n // actions\n onMessage: (handler: (message: any) => void) => {\n console.debug(\"[ReactorStore] Registering message handler\");\n\n get().internal.reactor.on(\"message\", handler);\n\n return () => {\n console.debug(\"[ReactorStore] Cleaning up message handler\");\n get().internal.reactor.off(\"message\", handler);\n };\n },\n sendCommand: async (command: string, data: any, scope?: MessageScope) => {\n console.debug(\"[ReactorStore] Sending command\", {\n command,\n data,\n scope,\n });\n try {\n await get().internal.reactor.sendCommand(command, data, scope);\n console.debug(\"[ReactorStore] Command sent successfully\");\n } catch (error) {\n console.error(\"[ReactorStore] Failed to send command:\", error);\n throw error;\n }\n },\n connect: async (jwtToken?: string, options?: ConnectOptions) => {\n if (jwtToken === undefined) {\n // If no JWT Token, it might have been passed in the constructor props. So read from it.\n jwtToken = get().jwtToken;\n }\n\n console.debug(\"[ReactorStore] Connect called.\");\n\n try {\n await get().internal.reactor.connect(jwtToken, options);\n console.debug(\"[ReactorStore] Connect completed successfully\");\n } catch (error) {\n console.error(\"[ReactorStore] Connect failed:\", error);\n throw error;\n }\n },\n disconnect: async (recoverable: boolean = false) => {\n console.debug(\"[ReactorStore] Disconnect called\", {\n currentStatus: get().status,\n });\n\n try {\n await get().internal.reactor.disconnect(recoverable);\n console.debug(\"[ReactorStore] Disconnect completed successfully\");\n } catch (error) {\n console.error(\"[ReactorStore] Disconnect failed:\", error);\n throw error;\n }\n },\n publish: async (name: string, track: MediaStreamTrack) => {\n console.debug(`[ReactorStore] Publishing track \"${name}\"`);\n\n try {\n await get().internal.reactor.publishTrack(name, track);\n console.debug(\n `[ReactorStore] Track \"${name}\" published successfully`\n );\n } catch (error) {\n console.error(\n `[ReactorStore] Failed to publish track \"${name}\":`,\n error\n );\n throw error;\n }\n },\n unpublish: async (name: string) => {\n console.debug(`[ReactorStore] Unpublishing track \"${name}\"`);\n\n try {\n await get().internal.reactor.unpublishTrack(name);\n console.debug(\n `[ReactorStore] Track \"${name}\" unpublished successfully`\n );\n } catch (error) {\n console.error(\n `[ReactorStore] Failed to unpublish track \"${name}\":`,\n error\n );\n throw error;\n }\n },\n reconnect: async (options?: ConnectOptions) => {\n console.debug(\"[ReactorStore] Reconnecting\");\n try {\n await get().internal.reactor.reconnect(options);\n console.debug(\"[ReactorStore] Reconnect completed successfully\");\n } catch (error) {\n console.error(\"[ReactorStore] Failed to reconnect:\", error);\n throw error;\n }\n },\n };\n });\n};\n","import { useReactorStore } from \"./ReactorProvider\";\nimport type { ReactorStore } from \"../core/store\";\nimport type { ConnectionStats } from \"../types\";\nimport { useShallow } from \"zustand/shallow\";\nimport { useEffect, useRef, useState } from \"react\";\n\n/**\n * Generic hook for accessing selected parts of the Reactor store.\n *\n * @param selector - A function that selects part of the store state.\n * @returns The selected slice from the store.\n */\nexport function useReactor<T>(selector: (state: ReactorStore) => T): T {\n return useReactorStore(useShallow(selector));\n}\n\n/**\n * Hook for receiving model application messages.\n *\n * Only fires for messages sent by the model via `get_ctx().send()`.\n * Internal platform-level messages (e.g. capabilities) are NOT delivered here.\n *\n * @param handler - Callback invoked with each application message payload.\n */\nexport function useReactorMessage(handler: (message: any) => void): void {\n const reactor = useReactor((state) => state.internal.reactor);\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n }, [handler]);\n\n useEffect(() => {\n const stableHandler = (message: any) => {\n handlerRef.current(message);\n };\n\n reactor.on(\"message\", stableHandler);\n\n return () => {\n reactor.off(\"message\", stableHandler);\n };\n }, [reactor]);\n}\n\n/**\n * Hook for receiving internal platform-level (runtime) messages.\n *\n * This is intended for advanced use cases that need access to the runtime\n * control layer, such as capabilities negotiation. Model application messages\n * sent via `get_ctx().send()` are NOT delivered through this hook — use\n * {@link useReactorMessage} for those.\n *\n * @param handler - Callback invoked with each runtime message payload.\n */\nexport function useReactorInternalMessage(\n handler: (message: any) => void\n): void {\n const reactor = useReactor((state) => state.internal.reactor);\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n }, [handler]);\n\n useEffect(() => {\n const stableHandler = (message: any) => {\n handlerRef.current(message);\n };\n\n reactor.on(\"runtimeMessage\", stableHandler);\n\n return () => {\n reactor.off(\"runtimeMessage\", stableHandler);\n };\n }, [reactor]);\n}\n\n/**\n * Hook that returns the current connection stats (RTT, etc.).\n * Updates every ~2s while connected. Returns undefined when disconnected.\n */\nexport function useStats(): ConnectionStats | undefined {\n const reactor = useReactor((state) => state.internal.reactor);\n const [stats, setStats] = useState<ConnectionStats | undefined>(undefined);\n\n useEffect(() => {\n const handler = (newStats: ConnectionStats) => {\n setStats(newStats);\n };\n\n reactor.on(\"statsUpdate\", handler);\n\n return () => {\n reactor.off(\"statsUpdate\", handler);\n setStats(undefined);\n };\n }, [reactor]);\n\n return stats;\n}\n","\"use client\";\n\nimport { useReactor } from \"./hooks\";\nimport { useEffect, useMemo, useRef } from \"react\";\nimport React from \"react\";\n\nexport interface ReactorViewProps {\n /**\n * The name of the **receive** track to render.\n * Must match a track name declared in the `receive` array (model → client).\n * Defaults to `\"main_video\"`.\n */\n track?: string;\n /**\n * Optional name of a **receive** audio track to play alongside the video\n * (e.g. `\"main_audio\"`). The audio is mixed into the same `<video>` element.\n */\n audioTrack?: string;\n width?: number;\n height?: number;\n className?: string;\n style?: React.CSSProperties;\n videoObjectFit?: NonNullable<\n React.VideoHTMLAttributes<HTMLVideoElement>[\"style\"]\n >[\"objectFit\"];\n /** Controls whether inbound audio plays. Default true (muted) to satisfy browser autoplay policies. */\n muted?: boolean;\n}\n\nexport function ReactorView({\n track = \"main_video\",\n audioTrack,\n width,\n height,\n className,\n style,\n videoObjectFit = \"contain\",\n muted = true,\n}: ReactorViewProps) {\n const { videoMediaTrack, audioMediaTrack, status } = useReactor((state) => ({\n videoMediaTrack: state.tracks[track] ?? null,\n audioMediaTrack: audioTrack ? (state.tracks[audioTrack] ?? null) : null,\n status: state.status,\n }));\n\n const videoRef = useRef<HTMLVideoElement>(null);\n\n const mediaStream = useMemo(() => {\n const tracks: MediaStreamTrack[] = [];\n if (videoMediaTrack) tracks.push(videoMediaTrack);\n if (audioMediaTrack) tracks.push(audioMediaTrack);\n if (tracks.length === 0) return null;\n return new MediaStream(tracks);\n }, [videoMediaTrack, audioMediaTrack]);\n\n useEffect(() => {\n console.debug(\"[ReactorView] Media track effect triggered\", {\n track,\n hasVideoElement: !!videoRef.current,\n hasVideoTrack: !!videoMediaTrack,\n hasAudioTrack: !!audioMediaTrack,\n });\n\n if (videoRef.current && mediaStream) {\n console.debug(\"[ReactorView] Attaching media stream to element\");\n try {\n videoRef.current.srcObject = mediaStream;\n videoRef.current.play().catch((e) => {\n console.warn(\"[ReactorView] Auto-play failed:\", e);\n });\n console.debug(\"[ReactorView] Media stream attached successfully\");\n } catch (error) {\n console.error(\"[ReactorView] Failed to attach media stream:\", error);\n }\n\n return () => {\n console.debug(\"[ReactorView] Detaching media stream from element\");\n if (videoRef.current) {\n videoRef.current.srcObject = null;\n }\n };\n } else {\n console.debug(\"[ReactorView] No tracks or element to attach\");\n }\n }, [mediaStream]);\n\n const showPlaceholder = !videoMediaTrack;\n\n return (\n <div\n style={{\n position: \"relative\",\n background: \"#000\",\n ...(width && { width }),\n ...(height && { height }),\n ...style,\n }}\n className={className}\n >\n <video\n ref={videoRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n objectFit: videoObjectFit,\n display: showPlaceholder ? \"none\" : \"block\",\n }}\n muted={muted}\n playsInline\n />\n {showPlaceholder && (\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n color: \"#fff\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: \"16px\",\n fontFamily: \"monospace\",\n textAlign: \"center\",\n padding: \"20px\",\n boxSizing: \"border-box\",\n }}\n >\n {status}\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useReactor, useReactorInternalMessage } from \"./hooks\";\nimport React, { useState, useCallback } from \"react\";\n\nexport interface ReactorControllerProps {\n className?: string;\n style?: React.CSSProperties;\n}\n\ninterface CommandSchema {\n description: string;\n schema: Record<\n string,\n {\n description?: string;\n type: string;\n minimum?: number;\n maximum?: number;\n required?: boolean;\n enum?: string[];\n }\n >;\n}\n\ninterface CommandsMessage {\n commands: Record<string, CommandSchema>;\n}\n\nexport function ReactorController({\n className,\n style,\n}: ReactorControllerProps) {\n const { sendCommand, status } = useReactor((state) => ({\n sendCommand: state.sendCommand,\n status: state.status,\n }));\n const [commands, setCommands] = useState<Record<string, CommandSchema>>({});\n const [formValues, setFormValues] = useState<\n Record<string, Record<string, any>>\n >({});\n const [expandedCommands, setExpandedCommands] = useState<\n Record<string, boolean>\n >({});\n\n // Reset commands when disconnected\n React.useEffect(() => {\n if (status === \"disconnected\") {\n setCommands({});\n setFormValues({});\n setExpandedCommands({});\n }\n }, [status]);\n\n // Function to request capabilities (sent on the \"runtime\" channel)\n const requestCapabilities = useCallback(() => {\n if (status === \"ready\") {\n sendCommand(\"requestCapabilities\", {}, \"runtime\");\n }\n }, [status, sendCommand]);\n\n // Send requestCapabilities when ready\n React.useEffect(() => {\n if (status === \"ready\") {\n requestCapabilities();\n }\n }, [status, requestCapabilities]);\n\n // Retry every 5 seconds if capabilities not set\n React.useEffect(() => {\n // Only set up interval if status is ready and commands are empty\n if (status !== \"ready\" || Object.keys(commands).length > 0) {\n return;\n }\n\n const interval = setInterval(() => {\n requestCapabilities();\n }, 5000);\n\n return () => clearInterval(interval);\n }, [status, commands, requestCapabilities]);\n\n useReactorInternalMessage((message) => {\n if (\n message &&\n typeof message === \"object\" &&\n message.type === \"modelCapabilities\" &&\n message.data &&\n \"commands\" in message.data\n ) {\n const commandsMessage = message.data as CommandsMessage;\n setCommands(commandsMessage.commands);\n\n // Initialize form values for each command\n const initialValues: Record<string, Record<string, any>> = {};\n const initialExpanded: Record<string, boolean> = {};\n\n Object.entries(commandsMessage.commands).forEach(\n ([commandName, commandSchema]) => {\n initialValues[commandName] = {};\n initialExpanded[commandName] = false; // Start collapsed by default\n\n Object.entries(commandSchema.schema).forEach(\n ([paramName, paramSchema]) => {\n if (paramSchema.type === \"number\") {\n initialValues[commandName][paramName] =\n paramSchema.minimum ?? 0;\n } else if (paramSchema.type === \"string\") {\n initialValues[commandName][paramName] = \"\";\n } else if (paramSchema.type === \"boolean\") {\n initialValues[commandName][paramName] = false;\n } else if (paramSchema.type === \"integer\") {\n initialValues[commandName][paramName] =\n paramSchema.minimum ?? 0;\n }\n }\n );\n }\n );\n setFormValues(initialValues);\n setExpandedCommands(initialExpanded);\n }\n });\n\n const handleInputChange = useCallback(\n (commandName: string, paramName: string, value: any) => {\n setFormValues((prev) => ({\n ...prev,\n [commandName]: {\n ...prev[commandName],\n [paramName]: value,\n },\n }));\n },\n []\n );\n\n const toggleCommandExpanded = useCallback((commandName: string) => {\n setExpandedCommands((prev) => ({\n ...prev,\n [commandName]: !prev[commandName],\n }));\n }, []);\n\n const handleCommandSubmit = useCallback(\n async (commandName: string) => {\n const commandSchema = commands[commandName];\n const formData = formValues[commandName] || {};\n\n // Build the data object according to the schema structure\n const data: Record<string, any> = {};\n\n // Only include parameters that are defined in the schema\n Object.keys(commandSchema.schema).forEach((paramName) => {\n const paramSchema = commandSchema.schema[paramName];\n let value = formData[paramName];\n\n // Type conversion based on schema\n if (paramSchema.type === \"number\" && typeof value === \"string\") {\n value = parseFloat(value) || 0;\n } else if (\n paramSchema.type === \"integer\" &&\n typeof value === \"string\"\n ) {\n value = parseInt(value) || 0;\n } else if (\n paramSchema.type === \"boolean\" &&\n typeof value !== \"boolean\"\n ) {\n value = Boolean(value);\n }\n\n // Only include the parameter if it has a value or is required\n if (value !== undefined && value !== \"\" && value !== null) {\n data[paramName] = value;\n } else if (paramSchema.required) {\n // Set default values for required parameters\n if (paramSchema.type === \"number\") {\n data[paramName] = paramSchema.minimum ?? 0;\n } else if (paramSchema.type === \"integer\") {\n data[paramName] = paramSchema.minimum ?? 0;\n } else if (paramSchema.type === \"string\") {\n data[paramName] = \"\";\n } else if (paramSchema.type === \"boolean\") {\n data[paramName] = false;\n }\n }\n });\n\n console.log(`Executing command: ${commandName}`, data);\n\n await sendCommand(commandName, data);\n },\n [formValues, sendCommand, commands]\n );\n\n const renderInput = (\n commandName: string,\n paramName: string,\n paramSchema: any\n ) => {\n const value = formValues[commandName]?.[paramName] ?? \"\";\n\n if (paramSchema.type === \"number\" || paramSchema.type === \"integer\") {\n const isInteger = paramSchema.type === \"integer\";\n const step = isInteger ? 1 : 0.1;\n const parseValue = isInteger ? parseInt : parseFloat;\n\n // Use slider if min/max are defined, otherwise use number input\n if (\n typeof paramSchema.minimum === \"number\" &&\n typeof paramSchema.maximum === \"number\"\n ) {\n return (\n <div style={{ marginBottom: \"8px\" }}>\n <label\n style={{ fontSize: \"12px\", color: \"#666\", display: \"block\" }}\n >\n {paramName} ({paramSchema.minimum} - {paramSchema.maximum})\n {paramSchema.description && ` - ${paramSchema.description}`}\n {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n </label>\n <input\n type=\"range\"\n min={paramSchema.minimum}\n max={paramSchema.maximum}\n step={step}\n value={value}\n onChange={(e) => {\n const newValue = parseValue(e.target.value) || 0;\n handleInputChange(commandName, paramName, newValue);\n // Execute command immediately for sliders\n handleCommandSubmit(commandName);\n }}\n style={{ width: \"100%\", marginBottom: \"4px\" }}\n />\n <div style={{ fontSize: \"11px\", color: \"#888\" }}>\n Value: {value}\n </div>\n </div>\n );\n } else {\n return (\n <div style={{ marginBottom: \"8px\" }}>\n <label\n style={{ fontSize: \"12px\", color: \"#666\", display: \"block\" }}\n >\n {paramName}\n {paramSchema.description && ` - ${paramSchema.description}`}\n {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n </label>\n <input\n type=\"number\"\n value={value}\n min={paramSchema.minimum}\n max={paramSchema.maximum}\n step={step}\n inputMode=\"numeric\"\n onChange={(e) => {\n const val = e.target.value;\n if (val === \"\" || val === \"-\") {\n // Allow empty or just minus sign while typing\n handleInputChange(commandName, paramName, val);\n } else {\n const parsed = parseValue(val);\n if (!isNaN(parsed)) {\n handleInputChange(commandName, paramName, parsed);\n }\n }\n }}\n onBlur={(e) => {\n // On blur, ensure we have a valid number\n const val = e.target.value;\n if (val === \"\" || val === \"-\") {\n handleInputChange(commandName, paramName, 0);\n }\n }}\n style={{\n width: \"100%\",\n padding: \"4px\",\n fontSize: \"12px\",\n border: \"1px solid #ccc\",\n borderRadius: \"2px\",\n }}\n />\n </div>\n );\n }\n } else if (paramSchema.type === \"string\") {\n if (paramSchema.enum) {\n // Dropdown for enum values\n return (\n <div style={{ marginBottom: \"8px\" }}>\n <label\n style={{ fontSize: \"12px\", color: \"#666\", display: \"block\" }}\n >\n {paramName}\n {paramSchema.description && ` - ${paramSchema.description}`}\n {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n </label>\n <select\n value={value}\n onChange={(e) =>\n handleInputChange(commandName, paramName, e.target.value)\n }\n style={{\n width: \"100%\",\n padding: \"4px\",\n fontSize: \"12px\",\n border: \"1px solid #ccc\",\n borderRadius: \"2px\",\n }}\n >\n <option value=\"\">Select...</option>\n {paramSchema.enum.map((option: string) => (\n <option key={option} value={option}>\n {option}\n </option>\n ))}\n </select>\n </div>\n );\n } else {\n // Text input\n return (\n <div style={{ marginBottom: \"8px\" }}>\n <label\n style={{ fontSize: \"12px\", color: \"#666\", display: \"block\" }}\n >\n {paramName}\n {paramSchema.description && ` - ${paramSchema.description}`}\n {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n </label>\n <input\n type=\"text\"\n value={value}\n onChange={(e) =>\n handleInputChange(commandName, paramName, e.target.value)\n }\n style={{\n width: \"100%\",\n padding: \"4px\",\n fontSize: \"12px\",\n border: \"1px solid #ccc\",\n borderRadius: \"2px\",\n }}\n />\n </div>\n );\n }\n } else if (paramSchema.type === \"boolean\") {\n return (\n <div style={{ marginBottom: \"8px\" }}>\n <label\n style={{\n fontSize: \"12px\",\n color: \"#666\",\n display: \"flex\",\n alignItems: \"center\",\n }}\n >\n <input\n type=\"checkbox\"\n checked={value}\n onChange={(e) =>\n handleInputChange(commandName, paramName, e.target.checked)\n }\n style={{ marginRight: \"6px\" }}\n />\n {paramName}\n {paramSchema.description && ` - ${paramSchema.description}`}\n {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n </label>\n </div>\n );\n }\n\n return null;\n };\n\n const renderCommand = (commandName: string, commandSchema: CommandSchema) => {\n const hasParams = Object.keys(commandSchema.schema).length > 0;\n const isExpanded = expandedCommands[commandName];\n\n // Check if this command has any slider inputs (number/integer with min/max)\n const hasSliderInputs = Object.values(commandSchema.schema).some(\n (paramSchema) =>\n (paramSchema.type === \"number\" || paramSchema.type === \"integer\") &&\n typeof paramSchema.minimum === \"number\" &&\n typeof paramSchema.maximum === \"number\"\n );\n\n // Don't show execute button if command has slider inputs (they execute automatically)\n const showExecuteButton = !hasSliderInputs;\n\n return (\n <div\n key={commandName}\n style={{\n border: \"1px solid #ddd\",\n borderRadius: \"4px\",\n marginBottom: \"8px\",\n backgroundColor: \"#fafafa\",\n }}\n >\n {/* Command Header - Always Visible */}\n <div\n onClick={() => toggleCommandExpanded(commandName)}\n style={{\n padding: \"8px 12px\",\n cursor: \"pointer\",\n borderBottom: isExpanded ? \"1px solid #ddd\" : \"none\",\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n }}\n >\n <div>\n <h4\n style={{\n margin: \"0\",\n fontSize: \"13px\",\n fontWeight: \"bold\",\n }}\n >\n {commandName}\n </h4>\n {isExpanded && commandSchema.description && (\n <p\n style={{ margin: \"4px 0 0 0\", fontSize: \"11px\", color: \"#666\" }}\n >\n {commandSchema.description}\n </p>\n )}\n </div>\n <div\n style={{\n fontSize: \"10px\",\n color: \"#999\",\n transform: isExpanded ? \"rotate(180deg)\" : \"rotate(0deg)\",\n transition: \"transform 0.2s\",\n }}\n >\n ▼\n </div>\n </div>\n\n {/* Command Content - Collapsible */}\n {isExpanded && (\n <div style={{ padding: \"12px\", paddingTop: \"0\" }}>\n {hasParams && (\n <div style={{ marginBottom: showExecuteButton ? \"12px\" : \"0\" }}>\n <div\n style={{\n marginBottom: \"8px\",\n fontSize: \"12px\",\n fontWeight: \"bold\",\n color: \"#555\",\n }}\n >\n Parameters:\n </div>\n {Object.entries(commandSchema.schema).map(\n ([paramName, paramSchema]) => (\n <div\n key={`${commandName}-${paramName}`}\n style={{ marginLeft: \"8px\" }}\n >\n {renderInput(commandName, paramName, paramSchema)}\n </div>\n )\n )}\n </div>\n )}\n\n {!hasParams && (\n <div\n style={{\n marginBottom: showExecuteButton ? \"12px\" : \"0\",\n marginTop: \"2px\",\n fontSize: \"11px\",\n color: \"#666\",\n fontStyle: \"italic\",\n }}\n >\n No parameters required\n </div>\n )}\n\n {showExecuteButton && (\n <button\n onClick={() => handleCommandSubmit(commandName)}\n style={{\n padding: \"8px 16px\",\n fontSize: \"12px\",\n backgroundColor: \"#007bff\",\n color: \"white\",\n border: \"none\",\n borderRadius: \"4px\",\n cursor: \"pointer\",\n fontWeight: \"bold\",\n }}\n >\n Execute {commandName}\n </button>\n )}\n </div>\n )}\n </div>\n );\n };\n\n return (\n <div className={className} style={style}>\n <div style={{ fontFamily: \"monospace\", fontSize: \"12px\" }}>\n {Object.keys(commands).length === 0 ? (\n <div style={{ padding: \"12px\", color: \"#666\", fontStyle: \"italic\" }}>\n Waiting for commands schema...\n </div>\n ) : (\n <div>\n <h3\n style={{\n margin: \"0 0 16px 0\",\n fontSize: \"16px\",\n fontWeight: \"bold\",\n }}\n >\n Reactor Commands\n </h3>\n {Object.entries(commands).map(([commandName, commandSchema]) => (\n <div key={commandName}>\n {renderCommand(commandName, commandSchema)}\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { useReactor } from \"./hooks\";\nimport { useEffect, useRef, useState } from \"react\";\nimport React from \"react\";\n\nexport interface WebcamStreamProps {\n /**\n * The name of the **send** track to publish the webcam to.\n * Must match a track name declared in the `send` array (client → model).\n */\n track: string;\n className?: string;\n style?: React.CSSProperties;\n videoConstraints?: MediaTrackConstraints;\n showWebcam?: boolean;\n videoObjectFit?: NonNullable<\n React.VideoHTMLAttributes<HTMLVideoElement>[\"style\"]\n >[\"objectFit\"];\n}\n\nexport function WebcamStream({\n track,\n className,\n style,\n videoConstraints = {\n width: { ideal: 1280 },\n height: { ideal: 720 },\n },\n showWebcam = true,\n videoObjectFit = \"contain\",\n}: WebcamStreamProps) {\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [isPublishing, setIsPublishing] = useState(false);\n const [permissionDenied, setPermissionDenied] = useState(false);\n\n const { status, publish, unpublish, reactor } = useReactor((state) => ({\n status: state.status,\n publish: state.publish,\n unpublish: state.unpublish,\n reactor: state.internal.reactor,\n }));\n\n const videoRef = useRef<HTMLVideoElement>(null);\n\n // Start webcam\n const startWebcam = async () => {\n console.debug(\"[WebcamPublisher] Starting webcam\");\n\n try {\n const mediaStream = await navigator.mediaDevices.getUserMedia({\n video: videoConstraints,\n audio: false,\n });\n\n console.debug(\"[WebcamPublisher] Webcam started successfully\");\n setStream(mediaStream);\n setPermissionDenied(false);\n } catch (err) {\n console.error(\"[WebcamPublisher] Failed to start webcam:\", err);\n\n // Check if the error is a permission denial\n if (\n err instanceof DOMException &&\n (err.name === \"NotAllowedError\" || err.name === \"PermissionDeniedError\")\n ) {\n console.debug(\"[WebcamPublisher] Camera permission denied\");\n setPermissionDenied(true);\n }\n }\n };\n\n // Stop webcam\n const stopWebcam = async () => {\n console.debug(\"[WebcamPublisher] Stopping webcam\");\n\n // Unpublish if currently publishing\n try {\n await unpublish(track);\n console.debug(\"[WebcamPublisher] Unpublished before stopping\");\n } catch (err) {\n console.error(\"[WebcamPublisher] Error unpublishing before stop:\", err);\n }\n\n setIsPublishing(false);\n\n // Stop all tracks\n stream?.getTracks().forEach((t) => {\n t.stop();\n console.debug(\"[WebcamPublisher] Stopped track:\", t.kind);\n });\n setStream(null);\n\n console.debug(\"[WebcamPublisher] Webcam stopped\");\n };\n\n // Attach stream to video element\n useEffect(() => {\n console.debug(\"[WebcamPublisher] Stream effect triggered\", {\n hasVideoElement: !!videoRef.current,\n hasStream: !!stream,\n });\n\n if (!videoRef.current) {\n return;\n }\n\n if (stream) {\n console.debug(\"[WebcamPublisher] Attaching stream to video element\");\n videoRef.current.srcObject = stream;\n console.debug(\"[WebcamPublisher] Stream attached successfully\");\n } else {\n console.debug(\"[WebcamPublisher] Clearing video element\");\n videoRef.current.srcObject = null;\n }\n }, [stream]);\n\n // Auto-publish when reactor is ready and webcam is active\n useEffect(() => {\n if (!stream) {\n return;\n }\n\n if (status === \"ready\" && !isPublishing) {\n console.debug(\n \"[WebcamPublisher] Reactor ready, auto-publishing webcam stream\"\n );\n const videoTrack = stream.getVideoTracks()[0];\n if (videoTrack) {\n publish(track, videoTrack)\n .then(() => {\n console.debug(\"[WebcamPublisher] Auto-publish successful\");\n setIsPublishing(true);\n })\n .catch((err) => {\n console.error(\"[WebcamPublisher] Auto-publish failed:\", err);\n });\n }\n } else if (status !== \"ready\" && isPublishing) {\n console.debug(\"[WebcamPublisher] Reactor not ready, auto-unpublishing\");\n unpublish(track)\n .then(() => {\n console.debug(\"[WebcamPublisher] Auto-unpublish successful\");\n setIsPublishing(false);\n })\n .catch((err) => {\n console.error(\"[WebcamPublisher] Auto-unpublish failed:\", err);\n });\n }\n }, [status, stream, isPublishing, publish, unpublish, track]);\n\n // Listen for error events from Reactor\n useEffect(() => {\n const handleError = (error: any) => {\n console.debug(\"[WebcamPublisher] Received error event:\", error);\n\n // Handle track publish failures by resetting state\n if (error.code === \"TRACK_PUBLISH_FAILED\") {\n console.debug(\n \"[WebcamPublisher] Track publish failed, resetting isPublishing state\"\n );\n setIsPublishing(false);\n }\n };\n\n reactor.on(\"error\", handleError);\n\n return () => {\n reactor.off(\"error\", handleError);\n };\n }, [reactor]);\n\n // Reset publishing state when status changes away from ready\n useEffect(() => {\n if (status !== \"ready\") {\n console.debug(\n \"[WebcamPublisher] Status changed to\",\n status,\n \"- resetting isPublishing state\"\n );\n setIsPublishing(false);\n }\n }, [status, isPublishing]);\n\n // Auto-start webcam on mount and cleanup on unmount\n useEffect(() => {\n console.debug(\"[WebcamPublisher] Auto-starting webcam\");\n startWebcam();\n\n return () => {\n console.debug(\"[WebcamPublisher] Cleanup on unmount\");\n stopWebcam();\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n const showPlaceholder = !stream;\n\n return (\n <div\n style={{\n display: showWebcam ? \"block\" : \"none\",\n position: \"relative\",\n background: \"#000\",\n ...style,\n }}\n className={className}\n >\n <video\n ref={videoRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n objectFit: videoObjectFit,\n display: showPlaceholder ? \"none\" : \"block\",\n }}\n muted\n playsInline\n autoPlay\n />\n {showPlaceholder && (\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n color: \"#fff\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: \"16px\",\n fontFamily: \"monospace\",\n textAlign: \"center\",\n padding: \"20px\",\n boxSizing: \"border-box\",\n flexDirection: \"column\",\n gap: \"12px\",\n }}\n >\n {permissionDenied ? (\n <div style={{ fontSize: \"12px\", fontFamily: \"monospace\" }}>\n Camera access denied.\n <br />\n Please allow access in your browser settings.\n </div>\n ) : (\n <div style={{ fontSize: \"12px\", fontFamily: \"monospace\" }}>\n Starting camera...\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n","import { PROD_COORDINATOR_URL } from \"../core/Reactor\";\n\n/**\n * ⚠️ INSECURE: Fetches a JWT token directly from the client.\n *\n * WARNING: This function exposes your API key in client-side code.\n * Only use this for local development or testing purposes.\n * In production, call /tokens from your server and pass the JWT to your frontend.\n *\n * @param apiKey - Your Reactor API key (will be exposed in client code!)\n * @param coordinatorUrl - Optional coordinator URL, defaults to production\n * @returns string containing the JWT token\n */\nexport async function fetchInsecureJwtToken(\n apiKey: string,\n coordinatorUrl: string = PROD_COORDINATOR_URL\n): Promise<string> {\n console.warn(\n \"[Reactor] ⚠️ SECURITY WARNING: fetchInsecureJwtToken() exposes your API key in client-side code. \" +\n \"This should ONLY be used for local development or testing. \" +\n \"In production, fetch tokens from your server instead.\"\n );\n\n const response = await fetch(`${coordinatorUrl}/tokens`, {\n method: \"GET\",\n headers: {\n \"X-API-Key\": apiKey,\n },\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to create token: ${response.status} ${error}`);\n }\n\n const { jwt } = await response.json();\n\n return jwt;\n}\n"],"mappings":"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","names":["SessionState","import_zod","import_react","import_react","_a","current","import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime","React","import_react","import_jsx_runtime"]}