hci-atrium 0.1.0

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.
Files changed (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +182 -0
  3. package/dist/atrium.d.ts +522 -0
  4. package/dist/atrium.d.ts.map +1 -0
  5. package/dist/atrium.js +806 -0
  6. package/dist/atrium.js.map +1 -0
  7. package/dist/auth.d.ts +85 -0
  8. package/dist/auth.d.ts.map +1 -0
  9. package/dist/auth.js +369 -0
  10. package/dist/auth.js.map +1 -0
  11. package/dist/by.d.ts +84 -0
  12. package/dist/by.d.ts.map +1 -0
  13. package/dist/by.js +87 -0
  14. package/dist/by.js.map +1 -0
  15. package/dist/decider.d.ts +40 -0
  16. package/dist/decider.d.ts.map +1 -0
  17. package/dist/decider.js +76 -0
  18. package/dist/decider.js.map +1 -0
  19. package/dist/discovery.d.ts +15 -0
  20. package/dist/discovery.d.ts.map +1 -0
  21. package/dist/discovery.js +38 -0
  22. package/dist/discovery.js.map +1 -0
  23. package/dist/env.d.ts +11 -0
  24. package/dist/env.d.ts.map +1 -0
  25. package/dist/env.js +28 -0
  26. package/dist/env.js.map +1 -0
  27. package/dist/errors.d.ts +67 -0
  28. package/dist/errors.d.ts.map +1 -0
  29. package/dist/errors.js +86 -0
  30. package/dist/errors.js.map +1 -0
  31. package/dist/files.d.ts +7 -0
  32. package/dist/files.d.ts.map +1 -0
  33. package/dist/files.js +2 -0
  34. package/dist/files.js.map +1 -0
  35. package/dist/http.d.ts +11 -0
  36. package/dist/http.d.ts.map +1 -0
  37. package/dist/http.js +21 -0
  38. package/dist/http.js.map +1 -0
  39. package/dist/index.d.ts +17 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +14 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/models.d.ts +456 -0
  44. package/dist/models.d.ts.map +1 -0
  45. package/dist/models.js +487 -0
  46. package/dist/models.js.map +1 -0
  47. package/dist/pkce.d.ts +16 -0
  48. package/dist/pkce.d.ts.map +1 -0
  49. package/dist/pkce.js +40 -0
  50. package/dist/pkce.js.map +1 -0
  51. package/dist/react/index.d.ts +37 -0
  52. package/dist/react/index.d.ts.map +1 -0
  53. package/dist/react/index.js +89 -0
  54. package/dist/react/index.js.map +1 -0
  55. package/dist/rooms/armed-set.d.ts +33 -0
  56. package/dist/rooms/armed-set.d.ts.map +1 -0
  57. package/dist/rooms/armed-set.js +65 -0
  58. package/dist/rooms/armed-set.js.map +1 -0
  59. package/dist/rooms/brokers.d.ts +62 -0
  60. package/dist/rooms/brokers.d.ts.map +1 -0
  61. package/dist/rooms/brokers.js +109 -0
  62. package/dist/rooms/brokers.js.map +1 -0
  63. package/dist/rooms/codec.d.ts +100 -0
  64. package/dist/rooms/codec.d.ts.map +1 -0
  65. package/dist/rooms/codec.js +101 -0
  66. package/dist/rooms/codec.js.map +1 -0
  67. package/dist/rooms/index.d.ts +40 -0
  68. package/dist/rooms/index.d.ts.map +1 -0
  69. package/dist/rooms/index.js +45 -0
  70. package/dist/rooms/index.js.map +1 -0
  71. package/dist/rooms/offer-capture.d.ts +96 -0
  72. package/dist/rooms/offer-capture.d.ts.map +1 -0
  73. package/dist/rooms/offer-capture.js +338 -0
  74. package/dist/rooms/offer-capture.js.map +1 -0
  75. package/dist/rooms/offer-transcription.d.ts +63 -0
  76. package/dist/rooms/offer-transcription.d.ts.map +1 -0
  77. package/dist/rooms/offer-transcription.js +231 -0
  78. package/dist/rooms/offer-transcription.js.map +1 -0
  79. package/dist/rooms/participant.d.ts +35 -0
  80. package/dist/rooms/participant.d.ts.map +1 -0
  81. package/dist/rooms/participant.js +53 -0
  82. package/dist/rooms/participant.js.map +1 -0
  83. package/dist/rooms/recorder.d.ts +74 -0
  84. package/dist/rooms/recorder.d.ts.map +1 -0
  85. package/dist/rooms/recorder.js +157 -0
  86. package/dist/rooms/recorder.js.map +1 -0
  87. package/dist/rooms/room.d.ts +542 -0
  88. package/dist/rooms/room.d.ts.map +1 -0
  89. package/dist/rooms/room.js +1357 -0
  90. package/dist/rooms/room.js.map +1 -0
  91. package/dist/rooms/snippet.d.ts +48 -0
  92. package/dist/rooms/snippet.d.ts.map +1 -0
  93. package/dist/rooms/snippet.js +70 -0
  94. package/dist/rooms/snippet.js.map +1 -0
  95. package/dist/rooms/sse.d.ts +102 -0
  96. package/dist/rooms/sse.d.ts.map +1 -0
  97. package/dist/rooms/sse.js +347 -0
  98. package/dist/rooms/sse.js.map +1 -0
  99. package/dist/rooms/standing-source.d.ts +190 -0
  100. package/dist/rooms/standing-source.d.ts.map +1 -0
  101. package/dist/rooms/standing-source.js +431 -0
  102. package/dist/rooms/standing-source.js.map +1 -0
  103. package/dist/rooms/transcriber.d.ts +81 -0
  104. package/dist/rooms/transcriber.d.ts.map +1 -0
  105. package/dist/rooms/transcriber.js +111 -0
  106. package/dist/rooms/transcriber.js.map +1 -0
  107. package/dist/rooms/transport-shared.d.ts +28 -0
  108. package/dist/rooms/transport-shared.d.ts.map +1 -0
  109. package/dist/rooms/transport-shared.js +42 -0
  110. package/dist/rooms/transport-shared.js.map +1 -0
  111. package/dist/rooms/transport.d.ts +161 -0
  112. package/dist/rooms/transport.d.ts.map +1 -0
  113. package/dist/rooms/transport.js +258 -0
  114. package/dist/rooms/transport.js.map +1 -0
  115. package/dist/shaping/assets.d.ts +42 -0
  116. package/dist/shaping/assets.d.ts.map +1 -0
  117. package/dist/shaping/assets.js +77 -0
  118. package/dist/shaping/assets.js.map +1 -0
  119. package/dist/shaping/chat.d.ts +39 -0
  120. package/dist/shaping/chat.d.ts.map +1 -0
  121. package/dist/shaping/chat.js +62 -0
  122. package/dist/shaping/chat.js.map +1 -0
  123. package/dist/shaping/index.d.ts +14 -0
  124. package/dist/shaping/index.d.ts.map +1 -0
  125. package/dist/shaping/index.js +13 -0
  126. package/dist/shaping/index.js.map +1 -0
  127. package/dist/shaping/library.d.ts +84 -0
  128. package/dist/shaping/library.d.ts.map +1 -0
  129. package/dist/shaping/library.js +288 -0
  130. package/dist/shaping/library.js.map +1 -0
  131. package/dist/shaping/request.d.ts +47 -0
  132. package/dist/shaping/request.d.ts.map +1 -0
  133. package/dist/shaping/request.js +25 -0
  134. package/dist/shaping/request.js.map +1 -0
  135. package/dist/shaping/rooms.d.ts +128 -0
  136. package/dist/shaping/rooms.d.ts.map +1 -0
  137. package/dist/shaping/rooms.js +330 -0
  138. package/dist/shaping/rooms.js.map +1 -0
  139. package/dist/shaping/sources.d.ts +29 -0
  140. package/dist/shaping/sources.d.ts.map +1 -0
  141. package/dist/shaping/sources.js +51 -0
  142. package/dist/shaping/sources.js.map +1 -0
  143. package/dist/shaping/structured.d.ts +47 -0
  144. package/dist/shaping/structured.d.ts.map +1 -0
  145. package/dist/shaping/structured.js +115 -0
  146. package/dist/shaping/structured.js.map +1 -0
  147. package/dist/storage.d.ts +36 -0
  148. package/dist/storage.d.ts.map +1 -0
  149. package/dist/storage.js +86 -0
  150. package/dist/storage.js.map +1 -0
  151. package/dist/transcribe-stream.d.ts +81 -0
  152. package/dist/transcribe-stream.d.ts.map +1 -0
  153. package/dist/transcribe-stream.js +253 -0
  154. package/dist/transcribe-stream.js.map +1 -0
  155. package/dist/transcribe.d.ts +57 -0
  156. package/dist/transcribe.d.ts.map +1 -0
  157. package/dist/transcribe.js +267 -0
  158. package/dist/transcribe.js.map +1 -0
  159. package/dist/types.d.ts +20 -0
  160. package/dist/types.d.ts.map +1 -0
  161. package/dist/types.js +3 -0
  162. package/dist/types.js.map +1 -0
  163. package/package.json +56 -0
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Structured-output plumbing — the mirror of ``_structured.py``, using **Zod** as the OpenAI TS SDK
3
+ * does (decision 3). Zod is an *optional* peer dependency: nothing here imports it at module top;
4
+ * a schema is duck-typed at runtime and the module is loaded lazily only to derive its JSON Schema.
5
+ *
6
+ * The flow (client-orchestrated, like Python's): send the schema as OpenAI's ``json_schema``
7
+ * response_format, validate the reply, and on failure run ONE repair turn before throwing
8
+ * {@link StructuredOutputError}. The pure pieces (JSON extraction, validation, repair messages) live
9
+ * here; the client owns the two ``chat`` calls.
10
+ */
11
+ import { InvalidArgumentError, StructuredOutputError } from "../errors.js";
12
+ /** A safe, universally-supported response_format for the repair turn: plain JSON mode. */
13
+ export const JSON_OBJECT = { type: "json_object" };
14
+ // Sentinel: no JSON value at all in the reply — distinct from a parsed literal ``null``.
15
+ const NO_JSON = Symbol("no-json");
16
+ /** True when ``value`` looks like a Zod schema (the structured-output path). */
17
+ export function isZodSchema(value) {
18
+ return (typeof value === "object" &&
19
+ value !== null &&
20
+ "safeParse" in value &&
21
+ typeof value.safeParse === "function");
22
+ }
23
+ /**
24
+ * Build the OpenAI ``json_schema`` response_format for a Zod schema. Async because it lazily loads
25
+ * Zod (v4's ``z.toJSONSchema``) — never imported at module top so the SDK installs without Zod.
26
+ * Throws {@link InvalidArgumentError} when Zod (v4+) isn't resolvable.
27
+ */
28
+ export async function responseFormatParam(schema, name = "response") {
29
+ // Non-literal specifier: TS treats the import as ``any`` and skips resolving "zod" at build time,
30
+ // so the optional peer dep need not be installed to compile the SDK.
31
+ const specifier = "zod";
32
+ let jsonSchema;
33
+ try {
34
+ const zod = (await import(specifier));
35
+ if (typeof zod.toJSONSchema !== "function") {
36
+ throw new Error("z.toJSONSchema is unavailable (Zod v4+ required)");
37
+ }
38
+ jsonSchema = zod.toJSONSchema(schema);
39
+ }
40
+ catch (err) {
41
+ throw new InvalidArgumentError("structured output needs Zod v4+ installed (npm i zod); " +
42
+ `could not derive a JSON schema: ${err.message}`);
43
+ }
44
+ return { type: "json_schema", json_schema: { name, schema: jsonSchema } };
45
+ }
46
+ /**
47
+ * Parse a JSON value out of a model reply — the whole string, else the outermost ``{…}`` or
48
+ * ``[…]`` (tolerating code fences / surrounding chatter). {@link NO_JSON} when nothing parses.
49
+ */
50
+ function extractJson(text) {
51
+ const stripped = text.trim();
52
+ try {
53
+ return JSON.parse(stripped);
54
+ }
55
+ catch {
56
+ // fall through to the bracket-slice fallback
57
+ }
58
+ // Try the EARLIEST opener first (it's the outermost structure).
59
+ const slices = [];
60
+ for (const [opener, closer] of [
61
+ ["{", "}"],
62
+ ["[", "]"],
63
+ ]) {
64
+ const start = stripped.indexOf(opener);
65
+ const end = stripped.lastIndexOf(closer);
66
+ if (start !== -1 && end > start) {
67
+ slices.push([start, stripped.slice(start, end + 1)]);
68
+ }
69
+ }
70
+ slices.sort((a, b) => a[0] - b[0]);
71
+ for (const [, blob] of slices) {
72
+ try {
73
+ return JSON.parse(blob);
74
+ }
75
+ catch {
76
+ // try the next slice
77
+ }
78
+ }
79
+ return NO_JSON;
80
+ }
81
+ /**
82
+ * Validate ``text`` into ``schema``. Returns ``[value, ""]`` on success or ``[null, detail]`` with
83
+ * a human-readable reason on failure (never throws).
84
+ */
85
+ export function tryParse(schema, text) {
86
+ const payload = extractJson(text);
87
+ if (payload === NO_JSON) {
88
+ return [null, "no JSON value found in the reply"];
89
+ }
90
+ const result = schema.safeParse(payload);
91
+ if (result.success) {
92
+ return [result.data, ""];
93
+ }
94
+ return [null, String(result.error?.message ?? result.error)];
95
+ }
96
+ /**
97
+ * The message list for the single repair turn: the prior turns, the model's bad reply, and a
98
+ * correction quoting the error and the JSON schema to conform to.
99
+ */
100
+ export function repairMessages(messages, badReply, detail, jsonSchema) {
101
+ const correction = "Your previous reply was not valid JSON for the required schema.\n" +
102
+ `Error: ${detail}\n` +
103
+ "Return ONLY a JSON object matching this schema — no prose, no code fences:\n" +
104
+ JSON.stringify(jsonSchema);
105
+ return [
106
+ ...messages,
107
+ { role: "assistant", content: badReply },
108
+ { role: "user", content: correction },
109
+ ];
110
+ }
111
+ /** Build the terminal error raised when the repair turn still didn't yield valid output. */
112
+ export function structuredError(schemaName, raw, detail) {
113
+ return new StructuredOutputError(schemaName, raw, detail);
114
+ }
115
+ //# sourceMappingURL=structured.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structured.js","sourceRoot":"","sources":["../../src/shaping/structured.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Browser-storage helpers, namespaced per ``(baseUrl, clientId)`` so multiple ``Atrium`` instances
3
+ * never collide.
4
+ *
5
+ * Every access is guarded: on a server, in private-mode browsers, or wherever storage throws, the
6
+ * stores degrade to no-ops rather than throwing — importing and constructing the SDK stays safe.
7
+ */
8
+ /** Persisted OAuth tokens. ``expiresAt`` is an epoch-millisecond timestamp. */
9
+ export interface StoredTokens {
10
+ accessToken: string;
11
+ refreshToken: string | null;
12
+ expiresAt: number;
13
+ }
14
+ /** The transient PKCE state stashed across the authorize redirect. */
15
+ export interface PkceStash {
16
+ verifier: string;
17
+ state: string;
18
+ redirectUri: string;
19
+ }
20
+ /** Long-lived token cache in ``localStorage``. */
21
+ export declare class TokenStore {
22
+ private readonly key;
23
+ constructor(baseUrl: string, clientId: string);
24
+ load(): StoredTokens | null;
25
+ save(tokens: StoredTokens): void;
26
+ clear(): void;
27
+ }
28
+ /** Short-lived PKCE stash in ``sessionStorage`` (survives the authorize redirect, not a new tab). */
29
+ export declare class PkceStore {
30
+ private readonly key;
31
+ constructor(baseUrl: string, clientId: string);
32
+ save(stash: PkceStash): void;
33
+ load(): PkceStash | null;
34
+ clear(): void;
35
+ }
36
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,+EAA+E;AAC/E,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,sEAAsE;AACtE,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;CACpB;AAqBD,kDAAkD;AAClD,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;gBAEhB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAI7C,IAAI,IAAI,YAAY,GAAG,IAAI;IAiB3B,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAIhC,KAAK,IAAI,IAAI;CAGd;AAED,qGAAqG;AACrG,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;gBAEhB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAI7C,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAI5B,IAAI,IAAI,SAAS,GAAG,IAAI;IAiBxB,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Browser-storage helpers, namespaced per ``(baseUrl, clientId)`` so multiple ``Atrium`` instances
3
+ * never collide.
4
+ *
5
+ * Every access is guarded: on a server, in private-mode browsers, or wherever storage throws, the
6
+ * stores degrade to no-ops rather than throwing — importing and constructing the SDK stays safe.
7
+ */
8
+ function safeStorage(kind) {
9
+ try {
10
+ const store = kind === "local" ? globalThis.localStorage : globalThis.sessionStorage;
11
+ if (!store) {
12
+ return null;
13
+ }
14
+ const probe = "__atrium_probe__";
15
+ store.setItem(probe, "1");
16
+ store.removeItem(probe);
17
+ return store;
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ function namespaced(prefix, baseUrl, clientId) {
24
+ return `${prefix}:${baseUrl}|${clientId}`;
25
+ }
26
+ /** Long-lived token cache in ``localStorage``. */
27
+ export class TokenStore {
28
+ key;
29
+ constructor(baseUrl, clientId) {
30
+ this.key = namespaced("atrium.tokens", baseUrl, clientId);
31
+ }
32
+ load() {
33
+ const store = safeStorage("local");
34
+ if (!store) {
35
+ return null;
36
+ }
37
+ const raw = store.getItem(this.key);
38
+ if (!raw) {
39
+ return null;
40
+ }
41
+ try {
42
+ const parsed = JSON.parse(raw);
43
+ return typeof parsed?.accessToken === "string" ? parsed : null;
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ }
49
+ save(tokens) {
50
+ safeStorage("local")?.setItem(this.key, JSON.stringify(tokens));
51
+ }
52
+ clear() {
53
+ safeStorage("local")?.removeItem(this.key);
54
+ }
55
+ }
56
+ /** Short-lived PKCE stash in ``sessionStorage`` (survives the authorize redirect, not a new tab). */
57
+ export class PkceStore {
58
+ key;
59
+ constructor(baseUrl, clientId) {
60
+ this.key = namespaced("atrium.pkce", baseUrl, clientId);
61
+ }
62
+ save(stash) {
63
+ safeStorage("session")?.setItem(this.key, JSON.stringify(stash));
64
+ }
65
+ load() {
66
+ const store = safeStorage("session");
67
+ if (!store) {
68
+ return null;
69
+ }
70
+ const raw = store.getItem(this.key);
71
+ if (!raw) {
72
+ return null;
73
+ }
74
+ try {
75
+ const parsed = JSON.parse(raw);
76
+ return typeof parsed?.verifier === "string" ? parsed : null;
77
+ }
78
+ catch {
79
+ return null;
80
+ }
81
+ }
82
+ clear() {
83
+ safeStorage("session")?.removeItem(this.key);
84
+ }
85
+ }
86
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * A narrow WebSocket client for the live transcription route (``WS /v1/audio/transcriptions/stream``)
3
+ * — the TypeScript port of Python's ``hci_atrium._transcribe_stream``.
4
+ *
5
+ * That route speaks the ``atrium.stt.v1`` *superset* dialect: a JSON ``start`` handshake, binary audio
6
+ * frames, a ``stop``, and ``ready`` / ``partial`` / ``final`` / ``done`` / ``error`` events down. The
7
+ * dispatch brain (streaming relay vs rolling-buffer re-transcription) lives in the backend; this
8
+ * client is dumb by design — connect, ``start``, send bytes / stop, iterate events, close.
9
+ *
10
+ * **Auth divergence from Python (browser-safe):** Python rides an ``Authorization: Bearer`` header on
11
+ * the handshake, which browsers can't set on a ``WebSocket``. So this client carries the credential
12
+ * in-band as an ``api_key`` field on the ``start`` frame — the backend's browser-safe first-frame path:
13
+ * it accepts the socket, reads ``start``, pops ``api_key`` (never logged), and validates it exactly
14
+ * like the header path. This keeps the long-lived credential out of the URL (and so out of every proxy
15
+ * and access log on the path), unlike the legacy ``?api_key=`` query the backend still tolerates. An
16
+ * invalid/missing key closes ``4401`` — arriving *after* the socket opened, but still surfaced as
17
+ * {@link AuthError}, never a silent fallback.
18
+ */
19
+ import type { WebSocketCtor } from "./rooms/transport.js";
20
+ /**
21
+ * Internal signal: the stream route isn't reachable *as a stream* for this caller — a refused
22
+ * connection, a proxy that strips the WebSocket upgrade, a non-``ready`` first frame, or a handshake
23
+ * timeout. The caller degrades to per-clip batch transcription. **Not** raised for an auth rejection
24
+ * ({@link AUTH_CLOSE_CODE}), which surfaces as {@link AuthError} since a silent fallback would just
25
+ * mask a fixable credential problem.
26
+ */
27
+ export declare class StreamUnavailable extends Error {
28
+ constructor(message: string);
29
+ }
30
+ /** Options for opening a {@link TranscriptionStream}. */
31
+ export interface TranscriptionStreamOptions {
32
+ /** Bearer/API-key credential — rides the ``start`` frame's ``api_key`` field (browsers can't set WS
33
+ * headers; the in-band path keeps it out of the URL and so out of proxy/access logs). */
34
+ token: string;
35
+ model?: string;
36
+ language?: string;
37
+ /** ``"pcm16"`` for a raw headerless stream (needs ``sampleRate``), else a container format. */
38
+ format: string;
39
+ sampleRate?: number;
40
+ /** Injected ``WebSocket`` constructor (defaults to the runtime global) — the test seam / Node <22. */
41
+ WebSocketCtor?: WebSocketCtor;
42
+ }
43
+ /** One open live-transcription WebSocket. Construct via {@link TranscriptionStream.open}. */
44
+ export declare class TranscriptionStream {
45
+ private readonly ws;
46
+ private readonly inbox;
47
+ private closedItem;
48
+ private notify;
49
+ private opened;
50
+ private openResolve;
51
+ private openReject;
52
+ private constructor();
53
+ /**
54
+ * Connect, send ``start``, and wait for the server's ``ready`` frame. Rejects with {@link AuthError}
55
+ * on a rejected credential ({@link AUTH_CLOSE_CODE}) and {@link StreamUnavailable} for every other
56
+ * handshake failure — the caller turns the latter into a per-clip batch fallback.
57
+ */
58
+ static open(baseUrl: string, opts: TranscriptionStreamOptions): Promise<TranscriptionStream>;
59
+ /** Send one binary audio frame (a PCM16 slice, or a self-contained container clip). */
60
+ sendBytes(data: ArrayBuffer): void;
61
+ /** Signal end-of-input — the server flushes, sends ``done``, and closes. */
62
+ sendStop(): void;
63
+ /**
64
+ * Iterate inbound JSON events until the connection closes. Binary frames and unparseable text are
65
+ * skipped; unknown ``type``s are yielded as-is (the caller ignores what it doesn't know).
66
+ *
67
+ * The close code is *not* swallowed: a mid-session {@link AUTH_CLOSE_CODE} throws {@link AuthError},
68
+ * a clean ``1000`` returns normally (the caller then checks whether a ``done`` actually arrived), and
69
+ * any other (abnormal) close throws {@link AtriumError} — a server crash / proxy kill / dropped
70
+ * connection must never read as a clean end.
71
+ */
72
+ events(): AsyncGenerator<Record<string, unknown>>;
73
+ /** Close the underlying socket (never throws). */
74
+ close(): void;
75
+ private attach;
76
+ private push;
77
+ private recv;
78
+ private recvWithin;
79
+ private waitOpen;
80
+ }
81
+ //# sourceMappingURL=transcribe-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcribe-stream.d.ts","sourceRoot":"","sources":["../src/transcribe-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,sBAAsB,CAAA;AAYxE;;;;;;GAMG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAK5B;AAED,yDAAyD;AACzD,MAAM,WAAW,0BAA0B;IACzC;6FACyF;IACzF,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+FAA+F;IAC/F,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,sGAAsG;IACtG,aAAa,CAAC,EAAE,aAAa,CAAA;CAC9B;AAaD,6FAA6F;AAC7F,qBAAa,mBAAmB;IAQV,OAAO,CAAC,QAAQ,CAAC,EAAE;IAPvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,UAAU,CAA2C;IAE7D,OAAO;IAEP;;;;OAIG;WACU,IAAI,CACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,0BAA0B,GAC/B,OAAO,CAAC,mBAAmB,CAAC;IAkD/B,uFAAuF;IACvF,SAAS,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAIlC,4EAA4E;IAC5E,QAAQ,IAAI,IAAI;IAIhB;;;;;;;;OAQG;IACI,MAAM,IAAI,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAgBxD,kDAAkD;IAClD,KAAK,IAAI,IAAI;IAUb,OAAO,CAAC,MAAM;IAwBd,OAAO,CAAC,IAAI;YAUE,IAAI;IASlB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,QAAQ;CAajB"}
@@ -0,0 +1,253 @@
1
+ /**
2
+ * A narrow WebSocket client for the live transcription route (``WS /v1/audio/transcriptions/stream``)
3
+ * — the TypeScript port of Python's ``hci_atrium._transcribe_stream``.
4
+ *
5
+ * That route speaks the ``atrium.stt.v1`` *superset* dialect: a JSON ``start`` handshake, binary audio
6
+ * frames, a ``stop``, and ``ready`` / ``partial`` / ``final`` / ``done`` / ``error`` events down. The
7
+ * dispatch brain (streaming relay vs rolling-buffer re-transcription) lives in the backend; this
8
+ * client is dumb by design — connect, ``start``, send bytes / stop, iterate events, close.
9
+ *
10
+ * **Auth divergence from Python (browser-safe):** Python rides an ``Authorization: Bearer`` header on
11
+ * the handshake, which browsers can't set on a ``WebSocket``. So this client carries the credential
12
+ * in-band as an ``api_key`` field on the ``start`` frame — the backend's browser-safe first-frame path:
13
+ * it accepts the socket, reads ``start``, pops ``api_key`` (never logged), and validates it exactly
14
+ * like the header path. This keeps the long-lived credential out of the URL (and so out of every proxy
15
+ * and access log on the path), unlike the legacy ``?api_key=`` query the backend still tolerates. An
16
+ * invalid/missing key closes ``4401`` — arriving *after* the socket opened, but still surfaced as
17
+ * {@link AuthError}, never a silent fallback.
18
+ */
19
+ import { AtriumError, AuthError } from "./errors.js";
20
+ const STREAM_PATH = "/v1/audio/transcriptions/stream";
21
+ /** The server closes with this application code when the credential is rejected — distinct from a
22
+ * "route not here" failure because retrying won't heal it (so it surfaces as {@link AuthError}, not a
23
+ * {@link StreamUnavailable} fallback). */
24
+ const AUTH_CLOSE_CODE = 4401;
25
+ /** Budget for the whole handshake (connect + the ``ready`` reply). Python leans on ``websockets``'
26
+ * default ``open_timeout`` (~10s); a browser ``WebSocket`` can't separate connect from the first read,
27
+ * so one budget covers both — a hung route degrades to the per-clip fallback rather than hanging. */
28
+ const HANDSHAKE_TIMEOUT_MS = 10_000;
29
+ /**
30
+ * Internal signal: the stream route isn't reachable *as a stream* for this caller — a refused
31
+ * connection, a proxy that strips the WebSocket upgrade, a non-``ready`` first frame, or a handshake
32
+ * timeout. The caller degrades to per-clip batch transcription. **Not** raised for an auth rejection
33
+ * ({@link AUTH_CLOSE_CODE}), which surfaces as {@link AuthError} since a silent fallback would just
34
+ * mask a fixable credential problem.
35
+ */
36
+ export class StreamUnavailable extends Error {
37
+ constructor(message) {
38
+ super(message);
39
+ this.name = "StreamUnavailable";
40
+ Object.setPrototypeOf(this, StreamUnavailable.prototype);
41
+ }
42
+ }
43
+ function toWsScheme(baseUrl) {
44
+ return baseUrl.replace(/^http/, "ws").replace(/\/+$/, "");
45
+ }
46
+ function streamUrl(baseUrl) {
47
+ // No query params: the credential rides the ``start`` frame, everything else the ``start`` message.
48
+ return `${toWsScheme(baseUrl)}${STREAM_PATH}`;
49
+ }
50
+ /** One open live-transcription WebSocket. Construct via {@link TranscriptionStream.open}. */
51
+ export class TranscriptionStream {
52
+ ws;
53
+ inbox = [];
54
+ closedItem = null;
55
+ notify = null;
56
+ opened = false;
57
+ openResolve = null;
58
+ openReject = null;
59
+ constructor(ws) {
60
+ this.ws = ws;
61
+ }
62
+ /**
63
+ * Connect, send ``start``, and wait for the server's ``ready`` frame. Rejects with {@link AuthError}
64
+ * on a rejected credential ({@link AUTH_CLOSE_CODE}) and {@link StreamUnavailable} for every other
65
+ * handshake failure — the caller turns the latter into a per-clip batch fallback.
66
+ */
67
+ static async open(baseUrl, opts) {
68
+ const Ctor = opts.WebSocketCtor ?? globalThis.WebSocket;
69
+ if (!Ctor) {
70
+ throw new Error("Atrium: no WebSocket implementation — pass { webSocketCtor } (Node <22 has none global)");
71
+ }
72
+ const ws = new Ctor(streamUrl(baseUrl));
73
+ const stream = new TranscriptionStream(ws);
74
+ stream.attach();
75
+ try {
76
+ await stream.waitOpen();
77
+ }
78
+ catch (reason) {
79
+ stream.close();
80
+ if (isCloseItem(reason) && reason.code === AUTH_CLOSE_CODE) {
81
+ throw new AuthError(401, "transcription stream rejected the credential (close 4401)");
82
+ }
83
+ throw new StreamUnavailable(`transcription stream handshake failed: ${describe(reason)}`);
84
+ }
85
+ // The credential rides here (the browser-safe first-frame path), so the server pops it before any
86
+ // processing. A rejected key closes 4401 *after* the socket opened — caught below as AuthError.
87
+ const start = {
88
+ type: "start",
89
+ protocol: 1,
90
+ format: opts.format,
91
+ api_key: opts.token,
92
+ };
93
+ if (opts.language != null)
94
+ start.language = opts.language;
95
+ if (opts.model != null)
96
+ start.model = opts.model;
97
+ if (opts.format === "pcm16")
98
+ start.sample_rate = opts.sampleRate;
99
+ ws.send(JSON.stringify(start));
100
+ const first = await stream.recvWithin(HANDSHAKE_TIMEOUT_MS);
101
+ if (first.kind === "close") {
102
+ stream.close();
103
+ // A post-open 4401 is the rejected first-frame credential — AuthError, never a degrade-to-batch
104
+ // fallback (a fixable credential problem must not silently mask itself).
105
+ if (first.code === AUTH_CLOSE_CODE) {
106
+ throw new AuthError(401, "transcription stream rejected the credential (close 4401)");
107
+ }
108
+ throw new StreamUnavailable(`stream closed during handshake (close ${first.code ?? "?"})`);
109
+ }
110
+ const msg = parseJson(first.data);
111
+ if (msg && msg.type === "ready")
112
+ return stream;
113
+ stream.close();
114
+ throw new StreamUnavailable(`expected a 'ready' frame, got: ${JSON.stringify(first.data)}`);
115
+ }
116
+ /** Send one binary audio frame (a PCM16 slice, or a self-contained container clip). */
117
+ sendBytes(data) {
118
+ this.ws.send(data);
119
+ }
120
+ /** Signal end-of-input — the server flushes, sends ``done``, and closes. */
121
+ sendStop() {
122
+ this.ws.send(JSON.stringify({ type: "stop" }));
123
+ }
124
+ /**
125
+ * Iterate inbound JSON events until the connection closes. Binary frames and unparseable text are
126
+ * skipped; unknown ``type``s are yielded as-is (the caller ignores what it doesn't know).
127
+ *
128
+ * The close code is *not* swallowed: a mid-session {@link AUTH_CLOSE_CODE} throws {@link AuthError},
129
+ * a clean ``1000`` returns normally (the caller then checks whether a ``done`` actually arrived), and
130
+ * any other (abnormal) close throws {@link AtriumError} — a server crash / proxy kill / dropped
131
+ * connection must never read as a clean end.
132
+ */
133
+ async *events() {
134
+ while (true) {
135
+ const item = await this.recv();
136
+ if (item.kind === "close") {
137
+ if (item.code === AUTH_CLOSE_CODE) {
138
+ throw new AuthError(401, "transcription stream rejected the credential (close 4401)");
139
+ }
140
+ if (item.code === 1000)
141
+ return;
142
+ throw new AtriumError(`transcription stream closed abnormally (close ${item.code ?? "?"})`);
143
+ }
144
+ if (typeof item.data !== "string")
145
+ continue; // binary — skip
146
+ const msg = parseJson(item.data);
147
+ if (msg)
148
+ yield msg;
149
+ }
150
+ }
151
+ /** Close the underlying socket (never throws). */
152
+ close() {
153
+ try {
154
+ this.ws.close();
155
+ }
156
+ catch {
157
+ // Closing a half-broken socket must never mask the real error.
158
+ }
159
+ }
160
+ // --- internals ---------------------------------------------------------------------------------
161
+ attach() {
162
+ this.ws.binaryType = "arraybuffer";
163
+ this.ws.onopen = () => {
164
+ this.opened = true;
165
+ const resolve = this.openResolve;
166
+ this.openResolve = null;
167
+ this.openReject = null;
168
+ resolve?.();
169
+ };
170
+ this.ws.onmessage = (ev) => this.push({ kind: "message", data: ev.data });
171
+ this.ws.onclose = (ev) => {
172
+ const code = ev?.code;
173
+ const item = { kind: "close", code };
174
+ this.push(item);
175
+ if (!this.opened) {
176
+ const reject = this.openReject;
177
+ this.openResolve = null;
178
+ this.openReject = null;
179
+ reject?.(item);
180
+ }
181
+ };
182
+ this.ws.onerror = () => { };
183
+ }
184
+ push(item) {
185
+ // A close is sticky (delivered once buffered messages drain, then repeatedly) so a late reader
186
+ // still sees the end; messages queue in order.
187
+ if (item.kind === "close")
188
+ this.closedItem = item;
189
+ else
190
+ this.inbox.push(item);
191
+ const wake = this.notify;
192
+ this.notify = null;
193
+ wake?.();
194
+ }
195
+ async recv() {
196
+ while (this.inbox.length === 0 && this.closedItem === null) {
197
+ await new Promise((resolve) => {
198
+ this.notify = resolve;
199
+ });
200
+ }
201
+ return this.inbox.shift() ?? this.closedItem;
202
+ }
203
+ recvWithin(ms) {
204
+ return withTimeout(this.recv(), ms, () => new StreamUnavailable("handshake timed out"));
205
+ }
206
+ waitOpen() {
207
+ if (this.opened)
208
+ return Promise.resolve();
209
+ if (this.closedItem)
210
+ return Promise.reject(this.closedItem);
211
+ const gate = new Promise((resolve, reject) => {
212
+ this.openResolve = resolve;
213
+ this.openReject = reject;
214
+ });
215
+ return withTimeout(gate, HANDSHAKE_TIMEOUT_MS, () => new StreamUnavailable("handshake timed out"));
216
+ }
217
+ }
218
+ function withTimeout(promise, ms, onTimeout) {
219
+ return new Promise((resolve, reject) => {
220
+ const timer = setTimeout(() => reject(onTimeout()), ms);
221
+ promise.then((value) => {
222
+ clearTimeout(timer);
223
+ resolve(value);
224
+ }, (reason) => {
225
+ clearTimeout(timer);
226
+ reject(reason);
227
+ });
228
+ });
229
+ }
230
+ function isCloseItem(value) {
231
+ return typeof value === "object" && value !== null && value.kind === "close";
232
+ }
233
+ function describe(reason) {
234
+ if (isCloseItem(reason))
235
+ return `closed before ready (close ${reason.code ?? "?"})`;
236
+ if (reason instanceof Error)
237
+ return reason.message;
238
+ return String(reason);
239
+ }
240
+ function parseJson(raw) {
241
+ if (typeof raw !== "string")
242
+ return null;
243
+ try {
244
+ const value = JSON.parse(raw);
245
+ return value && typeof value === "object" && !Array.isArray(value)
246
+ ? value
247
+ : null;
248
+ }
249
+ catch {
250
+ return null;
251
+ }
252
+ }
253
+ //# sourceMappingURL=transcribe-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcribe-stream.js","sourceRoot":"","sources":["../src/transcribe-stream.ts"],"names":[],"mappings":"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"}