@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 +21 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/transcriber.d.ts +39 -0
- package/dist/transcriber.d.ts.map +1 -0
- package/dist/transcriber.js +400 -0
- package/dist/transcriber.js.map +1 -0
- package/package.json +84 -0
- package/src/index.test.ts +609 -0
- package/src/index.ts +112 -0
- package/src/transcriber.ts +468 -0
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.
|
package/dist/index.d.ts
ADDED
|
@@ -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":"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"}
|