@moxxy/plugin-stt-whisper-codex 0.27.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Moxxy (moxxy.ai)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,23 @@
1
+ import { type Plugin } from '@moxxy/sdk';
2
+ import { type CodexOAuthVault } from './transcriber.js';
3
+ export { CodexOAuthTranscriber, DEFAULT_CODEX_TRANSCRIBE_BASE_URL, MOXXY_PCM16_24KHZ_MIME, OPENAI_CODEX_TRANSCRIBER_NAME, buildCodexTranscribeUrl, type CodexOAuthTranscriberOptions, type CodexOAuthVault, } from './transcriber.js';
4
+ export { pcm16MonoToWav } from '@moxxy/plugin-stt-whisper';
5
+ export interface BuildWhisperCodexPluginOptions {
6
+ readonly vault: CodexOAuthVault;
7
+ readonly baseUrl?: string;
8
+ readonly fetch?: typeof fetch;
9
+ readonly sessionIdProvider?: () => string;
10
+ /** Whole-request deadline in ms; defaults to 60s. `<= 0` disables it. */
11
+ readonly requestTimeoutMs?: number;
12
+ }
13
+ export declare function buildWhisperCodexPlugin(opts: BuildWhisperCodexPluginOptions): Plugin;
14
+ /**
15
+ * Discovery-loadable default export: resolves the vault from the inter-plugin
16
+ * service registry in `onInit`. Requires `@moxxy/plugin-vault` to load first
17
+ * (declared in `package.json` `moxxy.requirements`). `baseUrl`/`fetch`/
18
+ * `sessionIdProvider`/`requestTimeoutMs` fall back to their `createClient`
19
+ * config/defaults when the host doesn't inject them.
20
+ */
21
+ export declare const whisperCodexPlugin: Plugin;
22
+ export default whisperCodexPlugin;
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwD,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,qBAAqB,EACrB,iCAAiC,EACjC,sBAAsB,EACtB,6BAA6B,EAC7B,uBAAuB,EACvB,KAAK,4BAA4B,EACjC,KAAK,eAAe,GACrB,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAC;IAC1C,yEAAyE;IACzE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,8BAA8B,GACnC,MAAM,CAGR;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAgB7B,CAAC;AAkDL,eAAe,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,72 @@
1
+ import { definePlugin, defineTranscriber } from '@moxxy/sdk';
2
+ import { CodexOAuthTranscriber, OPENAI_CODEX_TRANSCRIBER_NAME, } from './transcriber.js';
3
+ export { CodexOAuthTranscriber, DEFAULT_CODEX_TRANSCRIBE_BASE_URL, MOXXY_PCM16_24KHZ_MIME, OPENAI_CODEX_TRANSCRIBER_NAME, buildCodexTranscribeUrl, } from './transcriber.js';
4
+ // pcm16MonoToWav was moved into @moxxy/plugin-stt-whisper alongside the
5
+ // rest of the shared audio helpers; re-export it here so older callers
6
+ // that import it from this package keep compiling.
7
+ export { pcm16MonoToWav } from '@moxxy/plugin-stt-whisper';
8
+ export function buildWhisperCodexPlugin(opts) {
9
+ const { vault, ...rest } = opts;
10
+ return makeWhisperCodexPlugin(() => vault, rest);
11
+ }
12
+ /**
13
+ * Discovery-loadable default export: resolves the vault from the inter-plugin
14
+ * service registry in `onInit`. Requires `@moxxy/plugin-vault` to load first
15
+ * (declared in `package.json` `moxxy.requirements`). `baseUrl`/`fetch`/
16
+ * `sessionIdProvider`/`requestTimeoutMs` fall back to their `createClient`
17
+ * config/defaults when the host doesn't inject them.
18
+ */
19
+ export const whisperCodexPlugin = (() => {
20
+ let resolved = null;
21
+ const getVault = () => {
22
+ if (!resolved) {
23
+ throw new Error('@moxxy/plugin-stt-whisper-codex: the "vault" service is unavailable — @moxxy/plugin-vault must load first');
24
+ }
25
+ return resolved;
26
+ };
27
+ const hooks = {
28
+ onInit: (ctx) => {
29
+ resolved = ctx.services.require('vault');
30
+ },
31
+ };
32
+ return makeWhisperCodexPlugin(getVault, {}, hooks);
33
+ })();
34
+ function makeWhisperCodexPlugin(getVault, opts, hooks) {
35
+ return definePlugin({
36
+ name: '@moxxy/plugin-stt-whisper-codex',
37
+ version: '0.0.0',
38
+ ...(hooks ? { hooks } : {}),
39
+ transcribers: [
40
+ defineTranscriber({
41
+ name: OPENAI_CODEX_TRANSCRIBER_NAME,
42
+ displayName: 'OpenAI Codex transcription (OAuth)',
43
+ createClient: (config) => {
44
+ // Only `baseUrl`/`sessionIdProvider` are caller-overridable; the
45
+ // host-wired `vault`/`fetch` stay authoritative so a stray config
46
+ // key cannot shadow a required dependency. Read narrow, typed keys
47
+ // off the untrusted `config` rather than spreading it wholesale.
48
+ const cfg = (config ?? {});
49
+ const configBaseUrl = typeof cfg.baseUrl === 'string' ? cfg.baseUrl : undefined;
50
+ const configSessionIdProvider = typeof cfg.sessionIdProvider === 'function'
51
+ ? cfg.sessionIdProvider
52
+ : undefined;
53
+ const baseUrl = configBaseUrl ?? opts.baseUrl;
54
+ const sessionIdProvider = configSessionIdProvider ?? opts.sessionIdProvider;
55
+ const merged = {
56
+ vault: getVault(),
57
+ ...(baseUrl ? { baseUrl } : {}),
58
+ ...(opts.fetch ? { fetch: opts.fetch } : {}),
59
+ ...(sessionIdProvider ? { sessionIdProvider } : {}),
60
+ ...(opts.requestTimeoutMs !== undefined
61
+ ? { requestTimeoutMs: opts.requestTimeoutMs }
62
+ : {}),
63
+ };
64
+ return new CodexOAuthTranscriber(merged);
65
+ },
66
+ }),
67
+ ],
68
+ });
69
+ }
70
+ // Discovery entry: `createPluginLoader` requires a default Plugin export.
71
+ export default whisperCodexPlugin;
72
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { MOXXY_PCM16_24KHZ_MIME } from '@moxxy/plugin-stt-whisper';
2
+ import { type Transcriber, type TranscribeOptions, type TranscriptionResult } from '@moxxy/sdk';
3
+ export declare const OPENAI_CODEX_TRANSCRIBER_NAME = "openai-codex-transcribe";
4
+ export declare const DEFAULT_CODEX_TRANSCRIBE_BASE_URL = "https://chatgpt.com";
5
+ export { MOXXY_PCM16_24KHZ_MIME };
6
+ export interface CodexOAuthVault {
7
+ get(key: string): Promise<string | null>;
8
+ set(key: string, value: string, tags?: ReadonlyArray<string>): Promise<void>;
9
+ delete?(key: string): Promise<boolean>;
10
+ }
11
+ export interface CodexOAuthTranscriberOptions {
12
+ readonly vault: CodexOAuthVault;
13
+ readonly baseUrl?: string;
14
+ readonly fetch?: typeof fetch;
15
+ readonly sessionIdProvider?: () => string;
16
+ /** Whole-request deadline in ms; defaults to 60s. `<= 0` disables it. */
17
+ readonly requestTimeoutMs?: number;
18
+ }
19
+ export declare class CodexOAuthTranscriber implements Transcriber {
20
+ readonly name = "openai-codex-transcribe";
21
+ private readonly vault;
22
+ private readonly endpoint;
23
+ private readonly fetchImpl;
24
+ private readonly sessionIdProvider;
25
+ private readonly requestTimeoutMs;
26
+ constructor(opts: CodexOAuthTranscriberOptions);
27
+ transcribe(audio: Uint8Array | ArrayBuffer, opts?: TranscribeOptions): Promise<TranscriptionResult>;
28
+ private parseResponse;
29
+ /**
30
+ * Arm an internal abort timer for the request deadline. Returns `undefined`
31
+ * when the timeout is disabled (`<= 0`). The caller MUST `clear()` it once the
32
+ * response (or rejection) is in hand so a bare timer can't keep the process
33
+ * alive.
34
+ */
35
+ private armTimeout;
36
+ private loadTokens;
37
+ }
38
+ export declare function buildCodexTranscribeUrl(baseUrl?: string): string;
39
+ //# sourceMappingURL=transcriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcriber.d.ts","sourceRoot":"","sources":["../src/transcriber.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,sBAAsB,EAEvB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACzB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,6BAA6B,4BAA4B,CAAC;AACvE,eAAO,MAAM,iCAAiC,wBAAwB,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAiClC,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAC;IAC1C,yEAAyE;IACzE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,qBAAa,qBAAsB,YAAW,WAAW;IACvD,QAAQ,CAAC,IAAI,6BAAiC;IAC9C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAe;IACjD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;gBAE9B,IAAI,EAAE,4BAA4B;IASxC,UAAU,CACd,KAAK,EAAE,UAAU,GAAG,WAAW,EAC/B,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,mBAAmB,CAAC;IA6E/B,OAAO,CAAC,aAAa;IA+FrB;;;;;OAKG;IACH,OAAO,CAAC,UAAU;YAgBJ,UAAU;CAmBzB;AAQD,wBAAgB,uBAAuB,CAAC,OAAO,SAAoC,GAAG,MAAM,CAiD3F"}
@@ -0,0 +1,400 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { CODEX_PROVIDER_ID, ensureFreshCodexTokens, } from '@moxxy/plugin-provider-openai-codex';
3
+ import { MOXXY_PCM16_24KHZ_MIME, normalizeWhisperUpload, } from '@moxxy/plugin-stt-whisper';
4
+ import { classifyHttpStatus, classifyNetworkError, MoxxyError, } from '@moxxy/sdk';
5
+ export const OPENAI_CODEX_TRANSCRIBER_NAME = 'openai-codex-transcribe';
6
+ export const DEFAULT_CODEX_TRANSCRIBE_BASE_URL = 'https://chatgpt.com';
7
+ export { MOXXY_PCM16_24KHZ_MIME };
8
+ const CODEX_TRANSCRIBE_ORIGINATOR = 'Codex Desktop';
9
+ const CODEX_TRANSCRIBE_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36';
10
+ // Deadline for the whole transcribe round-trip. Several callers pass no
11
+ // AbortSignal at all, so without this a half-open proxy / slow-loris peer that
12
+ // accepts the TCP connection but never responds would leave the promise (and
13
+ // the in-flight audio buffer) pending forever.
14
+ const DEFAULT_CODEX_TRANSCRIBE_TIMEOUT_MS = 60_000;
15
+ // The success body is a tiny `{ text }` JSON; error bodies (HTML pages) are the
16
+ // only large ones. Cap the buffered read so a hostile/broken backend can't
17
+ // balloon memory by streaming a multi-GB body before we even inspect it.
18
+ const MAX_CODEX_TRANSCRIBE_BODY_BYTES = 4 * 1024 * 1024;
19
+ // Upper bound on any raw upstream body we embed in a thrown error's cause, so
20
+ // an unmapped status can't smuggle an arbitrarily long (or PII-bearing) body
21
+ // into stack traces / debug logs.
22
+ const MAX_CODEX_ERROR_CAUSE_CHARS = 500;
23
+ /** Merge an optional caller signal with an optional internal one. */
24
+ function combineSignals(a, b) {
25
+ if (a && b)
26
+ return AbortSignal.any([a, b]);
27
+ return a ?? b;
28
+ }
29
+ export class CodexOAuthTranscriber {
30
+ name = OPENAI_CODEX_TRANSCRIBER_NAME;
31
+ vault;
32
+ endpoint;
33
+ fetchImpl;
34
+ sessionIdProvider;
35
+ requestTimeoutMs;
36
+ constructor(opts) {
37
+ this.vault = opts.vault;
38
+ this.endpoint = buildCodexTranscribeUrl(opts.baseUrl);
39
+ this.fetchImpl = opts.fetch ?? fetch;
40
+ this.sessionIdProvider = opts.sessionIdProvider ?? randomUUID;
41
+ this.requestTimeoutMs =
42
+ opts.requestTimeoutMs ?? DEFAULT_CODEX_TRANSCRIBE_TIMEOUT_MS;
43
+ }
44
+ async transcribe(audio, opts = {}) {
45
+ // Empty audio can never yield a transcript: fast-fail before loading tokens
46
+ // (which may trigger an OAuth refresh) or hitting the network.
47
+ if (audio.byteLength === 0)
48
+ return { text: '' };
49
+ const tokens = await this.loadTokens();
50
+ const sessionId = this.sessionIdProvider();
51
+ // NB: `opts.language` / `opts.prompt` are intentionally NOT forwarded.
52
+ // This hits the undocumented, reverse-engineered ChatGPT `backend-api/
53
+ // transcribe` endpoint (mimicking Codex Desktop), which only accepts the
54
+ // audio file part — sending extra multipart fields risks a rejection or
55
+ // silent behavior change. The OpenAI Whisper sibling (which does support
56
+ // language/prompt) is the path for callers that need those hints.
57
+ const upload = normalizeWhisperUpload(audio, opts.mimeType, 'moxxy');
58
+ const form = new FormData();
59
+ form.append('file', new File([upload.bytes], upload.filename, { type: upload.mimeType }));
60
+ // Always race the request against an internal deadline (combined with any
61
+ // caller signal) so a connection the peer accepts but never answers still
62
+ // rejects instead of hanging forever. We own the timeout controller so we
63
+ // can tell our deadline apart from a caller abort and surface NETWORK_TIMEOUT
64
+ // explicitly (classifyNetworkError keys off `AbortError`, not the
65
+ // `TimeoutError` an AbortSignal.timeout would raise).
66
+ //
67
+ // The single deadline spans BOTH the fetch AND the subsequent body read: a
68
+ // slow-loris peer can send response *headers* promptly (resolving the fetch
69
+ // promise) and then dribble the *body* one byte at a time, so capping only
70
+ // the body SIZE isn't enough — the body read must also abort on the same
71
+ // deadline. We therefore keep the timer armed through the body read and only
72
+ // clear it in the outer `finally`.
73
+ const timeout = this.armTimeout();
74
+ const signal = combineSignals(opts.signal, timeout?.signal);
75
+ const timedOutError = (err) => new MoxxyError({
76
+ code: 'NETWORK_TIMEOUT',
77
+ message: `Codex transcription timed out after ${this.requestTimeoutMs}ms.`,
78
+ context: { provider: CODEX_PROVIDER_ID, url: this.endpoint },
79
+ cause: err,
80
+ });
81
+ try {
82
+ let response;
83
+ try {
84
+ response = await this.fetchImpl(this.endpoint, {
85
+ method: 'POST',
86
+ headers: buildCodexTranscribeHeaders(tokens, sessionId),
87
+ body: form,
88
+ ...(signal ? { signal } : {}),
89
+ });
90
+ }
91
+ catch (err) {
92
+ if (timeout?.timedOut() && !opts.signal?.aborted)
93
+ throw timedOutError(err);
94
+ const network = classifyNetworkError(err, { url: this.endpoint, provider: CODEX_PROVIDER_ID });
95
+ if (network)
96
+ throw network;
97
+ throw err;
98
+ }
99
+ const raw = await readCappedBody(response, MAX_CODEX_TRANSCRIBE_BODY_BYTES, signal);
100
+ // A body read that stalled past the deadline aborts via `signal`; surface
101
+ // it as a timeout (our deadline) or a NETWORK_ABORTED (caller cancelled)
102
+ // the same way a hung fetch is, instead of silently parsing a truncated body.
103
+ if (timeout?.timedOut() && !opts.signal?.aborted)
104
+ throw timedOutError(undefined);
105
+ if (opts.signal?.aborted) {
106
+ const aborted = classifyNetworkError(Object.assign(new Error('Request aborted while reading the response body.'), {
107
+ name: 'AbortError',
108
+ }), { url: this.endpoint, provider: CODEX_PROVIDER_ID });
109
+ if (aborted)
110
+ throw aborted;
111
+ }
112
+ return this.parseResponse(response, raw);
113
+ }
114
+ finally {
115
+ timeout?.clear();
116
+ }
117
+ }
118
+ parseResponse(response, raw) {
119
+ if (!response.ok) {
120
+ const summary = summarizeCodexErrorBody(raw);
121
+ const classified = classifyHttpStatus(response.status, {
122
+ provider: CODEX_PROVIDER_ID,
123
+ url: this.endpoint,
124
+ body: summary,
125
+ });
126
+ if (classified)
127
+ throw classified;
128
+ // Mirror the classified path: never embed the raw, untruncated body
129
+ // verbatim into the error cause/context — collapse HTML pages and bound
130
+ // the length so an unmapped status can't leak an arbitrary body.
131
+ const summarized = summary?.slice(0, MAX_CODEX_ERROR_CAUSE_CHARS);
132
+ throw new MoxxyError({
133
+ code: 'PROVIDER_BAD_REQUEST',
134
+ message: `Codex transcription returned HTTP ${response.status}.`,
135
+ context: {
136
+ provider: CODEX_PROVIDER_ID,
137
+ url: this.endpoint,
138
+ status: response.status,
139
+ ...(summarized ? { body: summarized } : {}),
140
+ },
141
+ ...(summarized ? { cause: new Error(summarized) } : {}),
142
+ });
143
+ }
144
+ let payload;
145
+ try {
146
+ payload = JSON.parse(raw);
147
+ }
148
+ catch (err) {
149
+ throw new MoxxyError({
150
+ code: 'PROVIDER_UNKNOWN_RESPONSE',
151
+ message: 'Codex transcription returned invalid JSON.',
152
+ context: { provider: CODEX_PROVIDER_ID, url: this.endpoint },
153
+ cause: err,
154
+ });
155
+ }
156
+ // A 200 with a string `text` field is a SUCCESS even when that string is
157
+ // empty/whitespace — that just means the clip held no intelligible speech
158
+ // (silence, a clipped tap, a muted mic). Return `{ text: '' }` so callers
159
+ // take their graceful "no speech" path (the TUI shows a notice, the desktop
160
+ // a hint, Telegram/HTTP their own empty-text replies) instead of treating a
161
+ // normal outcome as a provider failure — every caller already guards on an
162
+ // empty transcript, and this is the one backend that used to throw past
163
+ // them. Only a response MISSING the `text` field (or whose `text` isn't a
164
+ // string) is a real contract violation worth throwing on.
165
+ if (!payload ||
166
+ typeof payload !== 'object' ||
167
+ typeof payload.text !== 'string') {
168
+ throw new MoxxyError({
169
+ code: 'PROVIDER_UNKNOWN_RESPONSE',
170
+ message: 'Codex transcription response was missing a text field.',
171
+ context: { provider: CODEX_PROVIDER_ID, url: this.endpoint },
172
+ });
173
+ }
174
+ // Surface the richer fields when the backend reports them (it returns only
175
+ // `{ text }` today, so these are normally absent and the result is
176
+ // unchanged). Guarding each field keeps a schema drift from corrupting the
177
+ // result while letting diarization/segment-aware consumers benefit if the
178
+ // endpoint ever starts emitting them.
179
+ const obj = payload;
180
+ const result = { text: obj.text.trim() };
181
+ if (typeof obj.language === 'string')
182
+ result.language = obj.language;
183
+ if (typeof obj.duration === 'number')
184
+ result.durationSec = obj.duration;
185
+ if (Array.isArray(obj.segments)) {
186
+ const segments = obj.segments
187
+ .filter((s) => !!s &&
188
+ typeof s === 'object' &&
189
+ typeof s.start === 'number' &&
190
+ typeof s.end === 'number' &&
191
+ typeof s.text === 'string')
192
+ .map((s) => ({ start: s.start, end: s.end, text: s.text }));
193
+ if (segments.length > 0)
194
+ result.segments = segments;
195
+ }
196
+ return result;
197
+ }
198
+ /**
199
+ * Arm an internal abort timer for the request deadline. Returns `undefined`
200
+ * when the timeout is disabled (`<= 0`). The caller MUST `clear()` it once the
201
+ * response (or rejection) is in hand so a bare timer can't keep the process
202
+ * alive.
203
+ */
204
+ armTimeout() {
205
+ if (!(this.requestTimeoutMs > 0))
206
+ return undefined;
207
+ const controller = new AbortController();
208
+ let fired = false;
209
+ const timer = setTimeout(() => {
210
+ fired = true;
211
+ controller.abort();
212
+ }, this.requestTimeoutMs);
213
+ timer.unref?.();
214
+ return {
215
+ signal: controller.signal,
216
+ timedOut: () => fired,
217
+ clear: () => clearTimeout(timer),
218
+ };
219
+ }
220
+ async loadTokens() {
221
+ try {
222
+ return await ensureFreshCodexTokens(this.vault);
223
+ }
224
+ catch (err) {
225
+ if (MoxxyError.isMoxxyError(err) &&
226
+ (err.code === 'AUTH_NO_CREDENTIALS' || err.code === 'AUTH_EXPIRED')) {
227
+ throw new MoxxyError({
228
+ code: err.code,
229
+ message: `No OpenAI Codex OAuth credentials available. Run \`moxxy login openai-codex\` to sign in.`,
230
+ hint: err.hint ?? 'Run `moxxy login openai-codex` to sign in.',
231
+ context: { provider: CODEX_PROVIDER_ID },
232
+ cause: err,
233
+ });
234
+ }
235
+ throw err;
236
+ }
237
+ }
238
+ }
239
+ function isLoopbackHostname(hostname) {
240
+ // URL() wraps IPv6 literals in brackets.
241
+ const host = hostname.replace(/^\[|\]$/g, '').toLowerCase();
242
+ return host === 'localhost' || host === '127.0.0.1' || host === '::1';
243
+ }
244
+ export function buildCodexTranscribeUrl(baseUrl = DEFAULT_CODEX_TRANSCRIBE_BASE_URL) {
245
+ let url;
246
+ try {
247
+ url = new URL(baseUrl);
248
+ }
249
+ catch (cause) {
250
+ throw new MoxxyError({
251
+ code: 'CONFIG_INVALID',
252
+ message: `Invalid Codex transcribe base URL: ${baseUrl}`,
253
+ context: { baseUrl: String(baseUrl) },
254
+ cause,
255
+ });
256
+ }
257
+ // The transcriber attaches a live `Authorization: Bearer <access-token>`
258
+ // header to this endpoint, so a config-controlled baseUrl must not be able to
259
+ // redirect that credential to an arbitrary origin. Pin to chatgpt.com over
260
+ // https, and allow loopback (the test/local seam) over http or https. Reject
261
+ // everything else as a misconfiguration rather than exfiltrating the token.
262
+ const loopback = isLoopbackHostname(url.hostname);
263
+ const httpsChatgpt = url.protocol === 'https:' && url.hostname === 'chatgpt.com';
264
+ if (!httpsChatgpt && !loopback) {
265
+ throw new MoxxyError({
266
+ code: 'CONFIG_INVALID',
267
+ message: `Refusing to send Codex OAuth credentials to ${url.origin}. ` +
268
+ 'The Codex transcribe base URL must be https://chatgpt.com (or a loopback host for local testing).',
269
+ context: { baseUrl: String(baseUrl), origin: url.origin },
270
+ });
271
+ }
272
+ if (loopback && url.protocol !== 'http:' && url.protocol !== 'https:') {
273
+ throw new MoxxyError({
274
+ code: 'CONFIG_INVALID',
275
+ message: `Unsupported scheme for Codex transcribe base URL: ${url.protocol}`,
276
+ context: { baseUrl: String(baseUrl), protocol: url.protocol },
277
+ });
278
+ }
279
+ let pathname = url.pathname.replace(/\/+$/, '');
280
+ if (!pathname || pathname === '/')
281
+ pathname = '';
282
+ if (url.hostname === 'chatgpt.com' && !pathname.endsWith('/backend-api')) {
283
+ pathname = `${pathname}/backend-api`;
284
+ }
285
+ if (!pathname.endsWith('/transcribe')) {
286
+ pathname = `${pathname}/transcribe`;
287
+ }
288
+ url.pathname = pathname;
289
+ url.search = '';
290
+ url.hash = '';
291
+ return url.toString();
292
+ }
293
+ function buildCodexTranscribeHeaders(tokens, sessionId) {
294
+ const headers = new Headers({
295
+ accept: 'application/json',
296
+ authorization: `Bearer ${tokens.access}`,
297
+ originator: CODEX_TRANSCRIBE_ORIGINATOR,
298
+ origin: 'https://chatgpt.com',
299
+ referer: 'https://chatgpt.com/',
300
+ 'user-agent': CODEX_TRANSCRIBE_USER_AGENT,
301
+ session_id: sessionId,
302
+ });
303
+ if (tokens.accountId)
304
+ headers.set('ChatGPT-Account-Id', tokens.accountId);
305
+ return headers;
306
+ }
307
+ /**
308
+ * Read a response body to text, but stop once `maxBytes` have been buffered and
309
+ * cancel the rest of the stream. A success body is a tiny `{ text }` JSON; only
310
+ * a hostile/broken backend would stream more, so a tight cap is safe and keeps a
311
+ * multi-GB body from ballooning memory before we parse/inspect it.
312
+ *
313
+ * `signal` carries the same request deadline (and caller abort) the fetch used:
314
+ * a slow-loris peer that returns headers promptly then dribbles the body would
315
+ * otherwise hang this read forever (the size cap doesn't help when bytes arrive
316
+ * slowly, not all at once), so the read also aborts on the deadline. A missing
317
+ * body, an abort, or a decode/read failure yields whatever was read so far (or
318
+ * `''`); the caller checks the timeout flag to decide whether to surface it as
319
+ * NETWORK_TIMEOUT.
320
+ */
321
+ async function readCappedBody(response, maxBytes, signal) {
322
+ const body = response.body;
323
+ if (!body) {
324
+ // No stream (e.g. some fetch polyfills / mocks); fall back to text() — those
325
+ // bodies are already in memory so there's nothing to cap. Race it against the
326
+ // deadline so even a polyfill that buffers slowly can't hang us.
327
+ try {
328
+ return await abortable(response.text(), signal);
329
+ }
330
+ catch {
331
+ return '';
332
+ }
333
+ }
334
+ const reader = body.getReader();
335
+ const decoder = new TextDecoder('utf-8');
336
+ let received = 0;
337
+ let out = '';
338
+ const onAbort = () => {
339
+ // Unblock a pending reader.read() on the deadline / caller abort.
340
+ reader.cancel().catch(() => { });
341
+ };
342
+ if (signal) {
343
+ if (signal.aborted)
344
+ onAbort();
345
+ else
346
+ signal.addEventListener('abort', onAbort, { once: true });
347
+ }
348
+ try {
349
+ while (received < maxBytes) {
350
+ const { done, value } = await reader.read();
351
+ if (done)
352
+ break;
353
+ if (value) {
354
+ received += value.byteLength;
355
+ out += decoder.decode(value, { stream: true });
356
+ }
357
+ }
358
+ return out;
359
+ }
360
+ catch {
361
+ return out;
362
+ }
363
+ finally {
364
+ signal?.removeEventListener('abort', onAbort);
365
+ reader.cancel().catch(() => { });
366
+ }
367
+ }
368
+ /**
369
+ * Reject `promise` as soon as `signal` aborts, so a body that can't be streamed
370
+ * (e.g. a mock's `text()` that never settles) still honours the deadline. The
371
+ * underlying work is left to settle/garbage-collect on its own — we only stop
372
+ * awaiting it.
373
+ */
374
+ function abortable(promise, signal) {
375
+ if (!signal)
376
+ return promise;
377
+ if (signal.aborted)
378
+ return Promise.reject(new Error('aborted'));
379
+ return new Promise((resolve, reject) => {
380
+ const onAbort = () => reject(new Error('aborted'));
381
+ signal.addEventListener('abort', onAbort, { once: true });
382
+ promise.then((v) => {
383
+ signal.removeEventListener('abort', onAbort);
384
+ resolve(v);
385
+ }, (e) => {
386
+ signal.removeEventListener('abort', onAbort);
387
+ reject(e instanceof Error ? e : new Error(String(e)));
388
+ });
389
+ });
390
+ }
391
+ function summarizeCodexErrorBody(raw) {
392
+ const body = raw.trim();
393
+ if (!body)
394
+ return undefined;
395
+ if (/^(?:<!doctype html|<html[\s>])/i.test(body)) {
396
+ return 'HTML error page from ChatGPT backend';
397
+ }
398
+ return body;
399
+ }
400
+ //# sourceMappingURL=transcriber.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcriber.js","sourceRoot":"","sources":["../src/transcriber.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,iBAAiB,EACjB,sBAAsB,GAEvB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,UAAU,GAIX,MAAM,YAAY,CAAC;AAEpB,MAAM,CAAC,MAAM,6BAA6B,GAAG,yBAAyB,CAAC;AACvE,MAAM,CAAC,MAAM,iCAAiC,GAAG,qBAAqB,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAClC,MAAM,2BAA2B,GAAG,eAAe,CAAC;AACpD,MAAM,2BAA2B,GAC/B,uHAAuH,CAAC;AAC1H,wEAAwE;AACxE,+EAA+E;AAC/E,6EAA6E;AAC7E,+CAA+C;AAC/C,MAAM,mCAAmC,GAAG,MAAM,CAAC;AACnD,gFAAgF;AAChF,2EAA2E;AAC3E,yEAAyE;AACzE,MAAM,+BAA+B,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AACxD,8EAA8E;AAC9E,6EAA6E;AAC7E,kCAAkC;AAClC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAQxC,qEAAqE;AACrE,SAAS,cAAc,CACrB,CAA0B,EAC1B,CAA0B;IAE1B,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3C,OAAO,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAiBD,MAAM,OAAO,qBAAqB;IACvB,IAAI,GAAG,6BAA6B,CAAC;IAC7B,KAAK,CAAkB;IACvB,QAAQ,CAAS;IACjB,SAAS,CAAe;IACxB,iBAAiB,CAAe;IAChC,gBAAgB,CAAS;IAE1C,YAAY,IAAkC;QAC5C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,QAAQ,GAAG,uBAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC;QACrC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,IAAI,UAAU,CAAC;QAC9D,IAAI,CAAC,gBAAgB;YACnB,IAAI,CAAC,gBAAgB,IAAI,mCAAmC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,UAAU,CACd,KAA+B,EAC/B,OAA0B,EAAE;QAE5B,4EAA4E;QAC5E,+DAA+D;QAC/D,IAAI,KAAK,CAAC,UAAU,KAAK,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QAEhD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3C,uEAAuE;QACvE,uEAAuE;QACvE,yEAAyE;QACzE,wEAAwE;QACxE,yEAAyE;QACzE,kEAAkE;QAClE,MAAM,MAAM,GAAG,sBAAsB,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACrE,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAE1F,0EAA0E;QAC1E,0EAA0E;QAC1E,0EAA0E;QAC1E,8EAA8E;QAC9E,kEAAkE;QAClE,sDAAsD;QACtD,EAAE;QACF,2EAA2E;QAC3E,4EAA4E;QAC5E,2EAA2E;QAC3E,yEAAyE;QACzE,6EAA6E;QAC7E,mCAAmC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAE5D,MAAM,aAAa,GAAG,CAAC,GAAY,EAAc,EAAE,CACjD,IAAI,UAAU,CAAC;YACb,IAAI,EAAE,iBAAiB;YACvB,OAAO,EAAE,uCAAuC,IAAI,CAAC,gBAAgB,KAAK;YAC1E,OAAO,EAAE,EAAE,QAAQ,EAAE,iBAAiB,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE;YAC5D,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;QAEL,IAAI,CAAC;YACH,IAAI,QAAkB,CAAC;YACvB,IAAI,CAAC;gBACH,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE;oBAC7C,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,2BAA2B,CAAC,MAAM,EAAE,SAAS,CAAC;oBACvD,IAAI,EAAE,IAAI;oBACV,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC9B,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO;oBAAE,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;gBAC3E,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC,CAAC;gBAC/F,IAAI,OAAO;oBAAE,MAAM,OAAO,CAAC;gBAC3B,MAAM,GAAG,CAAC;YACZ,CAAC;YAED,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,+BAA+B,EAAE,MAAM,CAAC,CAAC;YACpF,0EAA0E;YAC1E,yEAAyE;YACzE,8EAA8E;YAC9E,IAAI,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO;gBAAE,MAAM,aAAa,CAAC,SAAS,CAAC,CAAC;YACjF,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;gBACzB,MAAM,OAAO,GAAG,oBAAoB,CAClC,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,kDAAkD,CAAC,EAAE;oBAC3E,IAAI,EAAE,YAAY;iBACnB,CAAC,EACF,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CACpD,CAAC;gBACF,IAAI,OAAO;oBAAE,MAAM,OAAO,CAAC;YAC7B,CAAC;YACD,OAAO,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAC3C,CAAC;gBAAS,CAAC;YACT,OAAO,EAAE,KAAK,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;IAEO,aAAa,CAAC,QAAkB,EAAE,GAAW;QACnD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAC;YAC7C,MAAM,UAAU,GAAG,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAE;gBACrD,QAAQ,EAAE,iBAAiB;gBAC3B,GAAG,EAAE,IAAI,CAAC,QAAQ;gBAClB,IAAI,EAAE,OAAO;aACd,CAAC,CAAC;YACH,IAAI,UAAU;gBAAE,MAAM,UAAU,CAAC;YAEjC,oEAAoE;YACpE,wEAAwE;YACxE,iEAAiE;YACjE,MAAM,UAAU,GAAG,OAAO,EAAE,KAAK,CAAC,CAAC,EAAE,2BAA2B,CAAC,CAAC;YAClE,MAAM,IAAI,UAAU,CAAC;gBACnB,IAAI,EAAE,sBAAsB;gBAC5B,OAAO,EAAE,qCAAqC,QAAQ,CAAC,MAAM,GAAG;gBAChE,OAAO,EAAE;oBACP,QAAQ,EAAE,iBAAiB;oBAC3B,GAAG,EAAE,IAAI,CAAC,QAAQ;oBAClB,MAAM,EAAE,QAAQ,CAAC,MAAM;oBACvB,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC5C;gBACD,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxD,CAAC,CAAC;QACL,CAAC;QAED,IAAI,OAAgB,CAAC;QACrB,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;QACvC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,UAAU,CAAC;gBACnB,IAAI,EAAE,2BAA2B;gBACjC,OAAO,EAAE,4CAA4C;gBACrD,OAAO,EAAE,EAAE,QAAQ,EAAE,iBAAiB,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE;gBAC5D,KAAK,EAAE,GAAG;aACX,CAAC,CAAC;QACL,CAAC;QAED,yEAAyE;QACzE,0EAA0E;QAC1E,0EAA0E;QAC1E,4EAA4E;QAC5E,4EAA4E;QAC5E,2EAA2E;QAC3E,wEAAwE;QACxE,0EAA0E;QAC1E,0DAA0D;QAC1D,IACE,CAAC,OAAO;YACR,OAAO,OAAO,KAAK,QAAQ;YAC3B,OAAQ,OAA8B,CAAC,IAAI,KAAK,QAAQ,EACxD,CAAC;YACD,MAAM,IAAI,UAAU,CAAC;gBACnB,IAAI,EAAE,2BAA2B;gBACjC,OAAO,EAAE,wDAAwD;gBACjE,OAAO,EAAE,EAAE,QAAQ,EAAE,iBAAiB,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE;aAC7D,CAAC,CAAC;QACL,CAAC;QAED,2EAA2E;QAC3E,mEAAmE;QACnE,2EAA2E;QAC3E,0EAA0E;QAC1E,sCAAsC;QACtC,MAAM,GAAG,GAAG,OAKX,CAAC;QACF,MAAM,MAAM,GAKR,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QAC9B,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ;YAAE,MAAM,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;QACrE,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ;YAAE,MAAM,CAAC,WAAW,GAAG,GAAG,CAAC,QAAQ,CAAC;QACxE,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAChC,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ;iBAC1B,MAAM,CACL,CAAC,CAAC,EAAqD,EAAE,CACvD,CAAC,CAAC,CAAC;gBACH,OAAO,CAAC,KAAK,QAAQ;gBACrB,OAAQ,CAAyB,CAAC,KAAK,KAAK,QAAQ;gBACpD,OAAQ,CAAuB,CAAC,GAAG,KAAK,QAAQ;gBAChD,OAAQ,CAAwB,CAAC,IAAI,KAAK,QAAQ,CACrD;iBACA,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC9D,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACtD,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;OAKG;IACK,UAAU;QAChB,IAAI,CAAC,CAAC,IAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC;YAAE,OAAO,SAAS,CAAC;QACnD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,IAAI,KAAK,GAAG,KAAK,CAAC;QAClB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,KAAK,GAAG,IAAI,CAAC;YACb,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC1B,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;QAChB,OAAO;YACL,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK;YACrB,KAAK,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC;SACjC,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,UAAU;QACtB,IAAI,CAAC;YACH,OAAO,MAAM,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IACE,UAAU,CAAC,YAAY,CAAC,GAAG,CAAC;gBAC5B,CAAC,GAAG,CAAC,IAAI,KAAK,qBAAqB,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,CAAC,EACnE,CAAC;gBACD,MAAM,IAAI,UAAU,CAAC;oBACnB,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,OAAO,EAAE,2FAA2F;oBACpG,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,4CAA4C;oBAC9D,OAAO,EAAE,EAAE,QAAQ,EAAE,iBAAiB,EAAE;oBACxC,KAAK,EAAE,GAAG;iBACX,CAAC,CAAC;YACL,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;CACF;AAED,SAAS,kBAAkB,CAAC,QAAgB;IAC1C,yCAAyC;IACzC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC5D,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,KAAK,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,OAAO,GAAG,iCAAiC;IACjF,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,UAAU,CAAC;YACnB,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,sCAAsC,OAAO,EAAE;YACxD,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE;YACrC,KAAK;SACN,CAAC,CAAC;IACL,CAAC;IAED,yEAAyE;IACzE,8EAA8E;IAC9E,2EAA2E;IAC3E,6EAA6E;IAC7E,4EAA4E;IAC5E,MAAM,QAAQ,GAAG,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC;IACjF,IAAI,CAAC,YAAY,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC/B,MAAM,IAAI,UAAU,CAAC;YACnB,IAAI,EAAE,gBAAgB;YACtB,OAAO,EACL,+CAA+C,GAAG,CAAC,MAAM,IAAI;gBAC7D,mGAAmG;YACrG,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE;SAC1D,CAAC,CAAC;IACL,CAAC;IACD,IAAI,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACtE,MAAM,IAAI,UAAU,CAAC;YACnB,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,qDAAqD,GAAG,CAAC,QAAQ,EAAE;YAC5E,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE;SAC9D,CAAC,CAAC;IACL,CAAC;IAED,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC,QAAQ,IAAI,QAAQ,KAAK,GAAG;QAAE,QAAQ,GAAG,EAAE,CAAC;IACjD,IAAI,GAAG,CAAC,QAAQ,KAAK,aAAa,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QACzE,QAAQ,GAAG,GAAG,QAAQ,cAAc,CAAC;IACvC,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QACtC,QAAQ,GAAG,GAAG,QAAQ,aAAa,CAAC;IACtC,CAAC;IACD,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACxB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC;IAChB,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC;IACd,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC;AAED,SAAS,2BAA2B,CAAC,MAAmB,EAAE,SAAiB;IACzE,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC;QAC1B,MAAM,EAAE,kBAAkB;QAC1B,aAAa,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;QACxC,UAAU,EAAE,2BAA2B;QACvC,MAAM,EAAE,qBAAqB;QAC7B,OAAO,EAAE,sBAAsB;QAC/B,YAAY,EAAE,2BAA2B;QACzC,UAAU,EAAE,SAAS;KACtB,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,SAAS;QAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAC1E,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,KAAK,UAAU,cAAc,CAC3B,QAAkB,EAClB,QAAgB,EAChB,MAAoB;IAEpB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;IAC3B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,6EAA6E;QAC7E,8EAA8E;QAC9E,iEAAiE;QACjE,IAAI,CAAC;YACH,OAAO,MAAM,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IAChC,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,kEAAkE;QAClE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC;IACF,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,MAAM,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;;YACzB,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,CAAC;QACH,OAAO,QAAQ,GAAG,QAAQ,EAAE,CAAC;YAC3B,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5C,IAAI,IAAI;gBAAE,MAAM;YAChB,IAAI,KAAK,EAAE,CAAC;gBACV,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC;gBAC7B,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAClC,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,SAAS,CAAI,OAAmB,EAAE,MAAoB;IAC7D,IAAI,CAAC,MAAM;QAAE,OAAO,OAAO,CAAC;IAC5B,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAChE,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACxC,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;QACnD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,OAAO,CAAC,IAAI,CACV,CAAC,CAAC,EAAE,EAAE;YACJ,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7C,OAAO,CAAC,CAAC,CAAC,CAAC;QACb,CAAC,EACD,CAAC,CAAC,EAAE,EAAE;YACJ,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7C,MAAM,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,uBAAuB,CAAC,GAAW;IAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACxB,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,IAAI,iCAAiC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACjD,OAAO,sCAAsC,CAAC;IAChD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}