@speechos/core 0.2.5 → 0.2.7

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.d.cts CHANGED
@@ -12,5 +12,5 @@ export { livekit, Deferred } from "./livekit.js";
12
12
  export { websocket } from "./websocket.js";
13
13
  export { getBackend } from "./backend.js";
14
14
  export type { VoiceBackend } from "./backend.js";
15
- export type { SpeechOSCoreConfig, SpeechOSState, SpeechOSAction, SpeechOSEventMap, StateChangeCallback, UnsubscribeFn, RecordingState, LiveKitTokenResponse, ServerErrorMessage, ErrorSource, UserVocabularyData, CommandArgument, CommandDefinition, CommandResult, SessionSettings, VoiceSessionOptions, } from "./types.js";
15
+ export type { SpeechOSCoreConfig, SpeechOSState, SpeechOSAction, SpeechOSEventMap, StateChangeCallback, UnsubscribeFn, RecordingState, LiveKitTokenResponse, ServerErrorMessage, ErrorSource, UserVocabularyData, CommandArgument, CommandDefinition, CommandResult, SessionSettings, VoiceSessionOptions, WebSocketLike, WebSocketFactory, } from "./types.js";
16
16
  export declare const VERSION = "0.1.0";
package/dist/index.d.ts CHANGED
@@ -12,5 +12,5 @@ export { livekit, Deferred } from "./livekit.js";
12
12
  export { websocket } from "./websocket.js";
13
13
  export { getBackend } from "./backend.js";
14
14
  export type { VoiceBackend } from "./backend.js";
15
- export type { SpeechOSCoreConfig, SpeechOSState, SpeechOSAction, SpeechOSEventMap, StateChangeCallback, UnsubscribeFn, RecordingState, LiveKitTokenResponse, ServerErrorMessage, ErrorSource, UserVocabularyData, CommandArgument, CommandDefinition, CommandResult, SessionSettings, VoiceSessionOptions, } from "./types.js";
15
+ export type { SpeechOSCoreConfig, SpeechOSState, SpeechOSAction, SpeechOSEventMap, StateChangeCallback, UnsubscribeFn, RecordingState, LiveKitTokenResponse, ServerErrorMessage, ErrorSource, UserVocabularyData, CommandArgument, CommandDefinition, CommandResult, SessionSettings, VoiceSessionOptions, WebSocketLike, WebSocketFactory, } from "./types.js";
16
16
  export declare const VERSION = "0.1.0";
package/dist/index.js CHANGED
@@ -12,7 +12,8 @@ const defaultConfig = {
12
12
  apiKey: "",
13
13
  userId: "",
14
14
  host: DEFAULT_HOST,
15
- debug: false
15
+ debug: false,
16
+ webSocketFactory: void 0
16
17
  };
17
18
  /**
18
19
  * Validates and merges user config with defaults
@@ -25,7 +26,8 @@ function validateConfig(userConfig) {
25
26
  apiKey: userConfig.apiKey,
26
27
  userId: userConfig.userId ?? defaultConfig.userId,
27
28
  host: userConfig.host ?? defaultConfig.host,
28
- debug: userConfig.debug ?? defaultConfig.debug
29
+ debug: userConfig.debug ?? defaultConfig.debug,
30
+ webSocketFactory: userConfig.webSocketFactory ?? defaultConfig.webSocketFactory
29
31
  };
30
32
  }
31
33
  /**
@@ -314,14 +316,16 @@ var StateManager = class {
314
316
  });
315
317
  }
316
318
  /**
317
- * Complete the recording flow and return to idle
319
+ * Complete the recording flow and return to idle.
320
+ * Keeps widget visible but collapsed (just mic button, no action bubbles).
318
321
  */
319
322
  completeRecording() {
320
323
  this.setState({
321
324
  recordingState: "idle",
322
325
  activeAction: null,
323
326
  isConnected: false,
324
- isMicEnabled: false
327
+ isMicEnabled: false,
328
+ isExpanded: false
325
329
  });
326
330
  }
327
331
  /**
@@ -1395,6 +1399,8 @@ const MESSAGE_TYPE_EDITED_TEXT = "edited_text";
1395
1399
  const MESSAGE_TYPE_EXECUTE_COMMAND = "execute_command";
1396
1400
  const MESSAGE_TYPE_COMMAND_RESULT = "command_result";
1397
1401
  const MESSAGE_TYPE_ERROR = "error";
1402
+ const WS_OPEN = 1;
1403
+ const WS_CLOSED = 3;
1398
1404
  /**
1399
1405
  * Response timeout in milliseconds.
1400
1406
  */
@@ -1513,7 +1519,10 @@ var WebSocketManager = class {
1513
1519
  state.setMicEnabled(true);
1514
1520
  const wsUrl = this.getWebSocketUrl();
1515
1521
  if (config.debug) console.log("[SpeechOS] Connecting to WebSocket:", wsUrl);
1516
- this.ws = new WebSocket(wsUrl);
1522
+ this.pendingAuth = new Deferred$1();
1523
+ this.pendingAuth.setTimeout(RESPONSE_TIMEOUT_MS, "Connection timed out", "connection_timeout", "connection");
1524
+ const factory = config.webSocketFactory ?? ((url) => new WebSocket(url));
1525
+ this.ws = factory(wsUrl);
1517
1526
  this.ws.onopen = () => {
1518
1527
  if (config.debug) console.log("[SpeechOS] WebSocket connected, authenticating...");
1519
1528
  this.authenticate();
@@ -1522,19 +1531,21 @@ var WebSocketManager = class {
1522
1531
  this.handleMessage(event.data);
1523
1532
  };
1524
1533
  this.ws.onerror = (event) => {
1525
- console.error("[SpeechOS] WebSocket error:", event);
1534
+ const isConnectionBlocked = this.ws?.readyState === WS_CLOSED;
1535
+ const errorCode = isConnectionBlocked ? "connection_blocked" : "websocket_error";
1536
+ const errorMessage = isConnectionBlocked ? "This site's CSP blocks the extension. Try embedded mode instead." : "WebSocket connection error";
1537
+ console.error("[SpeechOS] WebSocket error:", event, { isConnectionBlocked });
1526
1538
  events.emit("error", {
1527
- code: "websocket_error",
1528
- message: "WebSocket connection error",
1539
+ code: errorCode,
1540
+ message: errorMessage,
1529
1541
  source: "connection"
1530
1542
  });
1543
+ if (this.pendingAuth) this.pendingAuth.reject(new Error(errorMessage));
1531
1544
  };
1532
1545
  this.ws.onclose = (event) => {
1533
1546
  if (config.debug) console.log("[SpeechOS] WebSocket closed:", event.code, event.reason);
1534
1547
  state.setConnected(false);
1535
1548
  };
1536
- this.pendingAuth = new Deferred$1();
1537
- this.pendingAuth.setTimeout(RESPONSE_TIMEOUT_MS, "Connection timed out", "connection_timeout", "connection");
1538
1549
  await this.pendingAuth.promise;
1539
1550
  this.pendingAuth = null;
1540
1551
  if (this.audioCapture) this.audioCapture.setReady();
@@ -1583,7 +1594,7 @@ var WebSocketManager = class {
1583
1594
  * Actually send the audio chunk (async operation).
1584
1595
  */
1585
1596
  async doSendAudioChunk(chunk) {
1586
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
1597
+ if (this.ws && this.ws.readyState === WS_OPEN) {
1587
1598
  const arrayBuffer = await chunk.arrayBuffer();
1588
1599
  this.ws.send(arrayBuffer);
1589
1600
  }
@@ -1769,7 +1780,7 @@ var WebSocketManager = class {
1769
1780
  * the transcript. Uses the same pattern as LiveKit's ReadableStream approach.
1770
1781
  */
1771
1782
  async waitForBufferDrain() {
1772
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
1783
+ if (!this.ws || this.ws.readyState !== WS_OPEN) return;
1773
1784
  const config = getConfig();
1774
1785
  const startTime = Date.now();
1775
1786
  while (this.ws.bufferedAmount > 0) {
@@ -1785,7 +1796,7 @@ var WebSocketManager = class {
1785
1796
  * Send a JSON message over the WebSocket.
1786
1797
  */
1787
1798
  sendMessage(message) {
1788
- if (this.ws && this.ws.readyState === WebSocket.OPEN) this.ws.send(JSON.stringify(message));
1799
+ if (this.ws && this.ws.readyState === WS_OPEN) this.ws.send(JSON.stringify(message));
1789
1800
  }
1790
1801
  /**
1791
1802
  * Disconnect from the WebSocket.
@@ -1827,7 +1838,7 @@ var WebSocketManager = class {
1827
1838
  * Check if connected to WebSocket.
1828
1839
  */
1829
1840
  isConnected() {
1830
- return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
1841
+ return this.ws !== null && this.ws.readyState === WS_OPEN;
1831
1842
  }
1832
1843
  /**
1833
1844
  * Get the last input text from a command result.