@moxxy/plugin-provider-openai-codex 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/dist/codex/headers.d.ts +5 -0
  3. package/dist/codex/headers.d.ts.map +1 -0
  4. package/dist/codex/headers.js +39 -0
  5. package/dist/codex/headers.js.map +1 -0
  6. package/dist/codex/sse-event-handler.d.ts +16 -0
  7. package/dist/codex/sse-event-handler.d.ts.map +1 -0
  8. package/dist/codex/sse-event-handler.js +155 -0
  9. package/dist/codex/sse-event-handler.js.map +1 -0
  10. package/dist/codex/stream-consumer.d.ts +4 -0
  11. package/dist/codex/stream-consumer.d.ts.map +1 -0
  12. package/dist/codex/stream-consumer.js +176 -0
  13. package/dist/codex/stream-consumer.js.map +1 -0
  14. package/dist/codex/stream-types.d.ts +61 -0
  15. package/dist/codex/stream-types.d.ts.map +1 -0
  16. package/dist/codex/stream-types.js +19 -0
  17. package/dist/codex/stream-types.js.map +1 -0
  18. package/dist/index.d.ts +11 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +31 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/login.d.ts +33 -0
  23. package/dist/login.d.ts.map +1 -0
  24. package/dist/login.js +94 -0
  25. package/dist/login.js.map +1 -0
  26. package/dist/models.d.ts +10 -0
  27. package/dist/models.d.ts.map +1 -0
  28. package/dist/models.js +19 -0
  29. package/dist/models.js.map +1 -0
  30. package/dist/oauth.d.ts +74 -0
  31. package/dist/oauth.d.ts.map +1 -0
  32. package/dist/oauth.js +170 -0
  33. package/dist/oauth.js.map +1 -0
  34. package/dist/profile.d.ts +13 -0
  35. package/dist/profile.d.ts.map +1 -0
  36. package/dist/profile.js +39 -0
  37. package/dist/profile.js.map +1 -0
  38. package/dist/provider.d.ts +79 -0
  39. package/dist/provider.d.ts.map +1 -0
  40. package/dist/provider.js +306 -0
  41. package/dist/provider.js.map +1 -0
  42. package/dist/translate.d.ts +77 -0
  43. package/dist/translate.d.ts.map +1 -0
  44. package/dist/translate.js +172 -0
  45. package/dist/translate.js.map +1 -0
  46. package/dist/types.d.ts +27 -0
  47. package/dist/types.d.ts.map +1 -0
  48. package/dist/types.js +2 -0
  49. package/dist/types.js.map +1 -0
  50. package/package.json +66 -0
  51. package/src/codex/headers.ts +41 -0
  52. package/src/codex/sse-event-handler.test.ts +85 -0
  53. package/src/codex/sse-event-handler.ts +173 -0
  54. package/src/codex/stream-consumer.test.ts +280 -0
  55. package/src/codex/stream-consumer.ts +181 -0
  56. package/src/codex/stream-types.ts +61 -0
  57. package/src/index.ts +65 -0
  58. package/src/login.ts +121 -0
  59. package/src/models.ts +21 -0
  60. package/src/oauth.test.ts +223 -0
  61. package/src/oauth.ts +200 -0
  62. package/src/profile.ts +52 -0
  63. package/src/provider.test.ts +507 -0
  64. package/src/provider.ts +360 -0
  65. package/src/translate.test.ts +95 -0
  66. package/src/translate.ts +224 -0
  67. package/src/types.ts +28 -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.
@@ -0,0 +1,5 @@
1
+ import type { CodexTokens } from '../types.js';
2
+ export declare const PLUGIN_VERSION: string;
3
+ export declare const CODEX_USER_AGENT: string;
4
+ export declare function buildCodexHeaders(tokens: CodexTokens, sessionId: string): Record<string, string>;
5
+ //# sourceMappingURL=headers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../../src/codex/headers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAuB/C,eAAO,MAAM,cAAc,QAAyB,CAAC;AAErD,eAAO,MAAM,gBAAgB,QAAoC,CAAC;AAElE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWhG"}
@@ -0,0 +1,39 @@
1
+ import { createRequire } from 'node:module';
2
+ import { ORIGINATOR } from '../oauth.js';
3
+ /**
4
+ * Resolve this plugin's real version from its package.json once at module load
5
+ * rather than freezing a stale literal in two places (the UA and the plugin
6
+ * def). The User-Agent is sent on every request to the ChatGPT backend; a
7
+ * permanently-`0.0.0` UA defeats server-side version gating and diverges from
8
+ * the real release. Resolves correctly when the package runs from its own dist
9
+ * (dev, tests, third-party `~/.moxxy/plugins` installs); falls back to `0.0.0`
10
+ * defensively — never throws on a header build. (NOTE: when inlined into the
11
+ * single-file CLI bundle the relative resolve fails and we keep the `0.0.0`
12
+ * fallback; a build-time constant would close that gap — see TECH_DEBT.)
13
+ */
14
+ function resolvePluginVersion() {
15
+ try {
16
+ const require = createRequire(import.meta.url);
17
+ const pkg = require('../../package.json');
18
+ return typeof pkg.version === 'string' && pkg.version ? pkg.version : '0.0.0';
19
+ }
20
+ catch {
21
+ return '0.0.0';
22
+ }
23
+ }
24
+ export const PLUGIN_VERSION = resolvePluginVersion();
25
+ export const CODEX_USER_AGENT = `moxxy/${PLUGIN_VERSION} (codex)`;
26
+ export function buildCodexHeaders(tokens, sessionId) {
27
+ const headers = {
28
+ 'Content-Type': 'application/json',
29
+ Accept: 'text/event-stream',
30
+ Authorization: `Bearer ${tokens.access}`,
31
+ originator: ORIGINATOR,
32
+ 'User-Agent': CODEX_USER_AGENT,
33
+ session_id: sessionId,
34
+ };
35
+ if (tokens.accountId)
36
+ headers['ChatGPT-Account-Id'] = tokens.accountId;
37
+ return headers;
38
+ }
39
+ //# sourceMappingURL=headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.js","sourceRoot":"","sources":["../../src/codex/headers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC;;;;;;;;;;GAUG;AACH,SAAS,oBAAoB;IAC3B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,oBAAoB,CAA0B,CAAC;QACnE,OAAO,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,oBAAoB,EAAE,CAAC;AAErD,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,cAAc,UAAU,CAAC;AAElE,MAAM,UAAU,iBAAiB,CAAC,MAAmB,EAAE,SAAiB;IACtE,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;QAClC,MAAM,EAAE,mBAAmB;QAC3B,aAAa,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;QACxC,UAAU,EAAE,UAAU;QACtB,YAAY,EAAE,gBAAgB;QAC9B,UAAU,EAAE,SAAS;KACtB,CAAC;IACF,IAAI,MAAM,CAAC,SAAS;QAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC;IACvE,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { type PendingFunctionCall, type ResponsesSseEvent, type SseStepResult } from './stream-types.js';
2
+ /**
3
+ * Map a single Responses-API SSE event to zero or more moxxy ProviderEvents.
4
+ * Centralized here so the streaming loop stays a thin "read frame → call
5
+ * this → yield" structure and the event taxonomy is easy to test directly.
6
+ *
7
+ * Events we care about (subset of the full Responses API surface):
8
+ * response.output_text.delta → text_delta
9
+ * response.output_item.added → tool_use_start (if it's a function_call)
10
+ * response.function_call_arguments.delta → tool_use_delta
11
+ * response.function_call_arguments.done → finalize tool_use_end
12
+ * response.completed → message_end (sets stopReason)
13
+ * response.failed / response.error → error
14
+ */
15
+ export declare function handleSseEvent(ev: ResponsesSseEvent, pending: Map<string, PendingFunctionCall>, emitReasoning?: boolean): SseStepResult;
16
+ //# sourceMappingURL=sse-event-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse-event-handler.d.ts","sourceRoot":"","sources":["../../src/codex/sse-event-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AA2B3B;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,iBAAiB,EACrB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,EACzC,aAAa,UAAQ,GACpB,aAAa,CA0Hf"}
@@ -0,0 +1,155 @@
1
+ import { parseToolArgs, } from './stream-types.js';
2
+ /**
3
+ * Hard cap on a single tool call's accumulated argument text. `entry.args` grows
4
+ * by every `function_call_arguments.delta`; unlike the consumer's per-frame
5
+ * reassembly buffer (which shrinks once a frame is parsed), this accumulates
6
+ * ACROSS frames and is otherwise unbounded — a hostile stream of millions of
7
+ * small, individually-valid delta frames would grow it to OOM without ever
8
+ * tripping the frame-buffer cap. 16 MiB is far beyond any legitimate tool-call
9
+ * payload. Exceeding it is treated as a hostile stream (terminal error).
10
+ */
11
+ const MAX_TOOL_ARGS_CHARS = 16 * 1024 * 1024;
12
+ /**
13
+ * Hard cap on the number of concurrently-pending function calls. Each distinct
14
+ * `output_item.added` function_call seeds a `pending` entry that lives until its
15
+ * `.done` (or the post-stream flush); a hostile stream could emit unbounded
16
+ * distinct ids and never finish them, growing the Map without limit. No real
17
+ * turn fans out to anywhere near this many parallel tool calls.
18
+ */
19
+ const MAX_PENDING_TOOL_CALLS = 1024;
20
+ const TOOL_STREAM_LIMIT_ERROR = (what) => ({
21
+ events: [{ type: 'error', message: `Codex tool-call stream exceeded ${what} limit`, retryable: false }],
22
+ terminal: true,
23
+ });
24
+ /**
25
+ * Map a single Responses-API SSE event to zero or more moxxy ProviderEvents.
26
+ * Centralized here so the streaming loop stays a thin "read frame → call
27
+ * this → yield" structure and the event taxonomy is easy to test directly.
28
+ *
29
+ * Events we care about (subset of the full Responses API surface):
30
+ * response.output_text.delta → text_delta
31
+ * response.output_item.added → tool_use_start (if it's a function_call)
32
+ * response.function_call_arguments.delta → tool_use_delta
33
+ * response.function_call_arguments.done → finalize tool_use_end
34
+ * response.completed → message_end (sets stopReason)
35
+ * response.failed / response.error → error
36
+ */
37
+ export function handleSseEvent(ev, pending, emitReasoning = false) {
38
+ const type = ev.type ?? '';
39
+ if (type === 'response.output_text.delta' && typeof ev.delta === 'string' && ev.delta) {
40
+ return { events: [{ type: 'text_delta', delta: ev.delta }] };
41
+ }
42
+ // Reasoning summary text (Codex requests `summary: 'auto'`) → reasoning_delta.
43
+ // The streamed summary is the visible "thinking" between tool calls. Gated on
44
+ // the per-provider reasoning toggle; off → discard as before.
45
+ if (emitReasoning && type === 'response.reasoning_summary_text.delta' && typeof ev.delta === 'string' && ev.delta) {
46
+ return { events: [{ type: 'reasoning_delta', delta: ev.delta }] };
47
+ }
48
+ // A `reasoning` output item carries the encrypted_content we must replay
49
+ // verbatim on the next request (Codex requests `include: ['reasoning.encrypted_content']`).
50
+ if (emitReasoning && type === 'response.output_item.added' && ev.item?.type === 'reasoning') {
51
+ return ev.item.encrypted_content
52
+ ? { events: [{ type: 'reasoning_signature', encrypted: ev.item.encrypted_content }] }
53
+ : {};
54
+ }
55
+ if (type === 'response.output_item.added' && ev.item?.type === 'function_call') {
56
+ const id = ev.item.id ?? ev.item.call_id ?? `call_${pending.size}`;
57
+ // Bound the number of in-flight tool calls: a hostile stream could otherwise
58
+ // seed unbounded distinct ids and never `.done` them, growing the Map to OOM.
59
+ // (A new id that would overflow is rejected; re-using an existing id is fine.)
60
+ if (!pending.has(id) && pending.size >= MAX_PENDING_TOOL_CALLS) {
61
+ return TOOL_STREAM_LIMIT_ERROR('pending-call count');
62
+ }
63
+ const callId = ev.item.call_id ?? id;
64
+ const name = ev.item.name ?? '';
65
+ const entry = {
66
+ id,
67
+ callId,
68
+ name,
69
+ args: ev.item.arguments ?? '',
70
+ emittedStart: false,
71
+ };
72
+ pending.set(id, entry);
73
+ if (name) {
74
+ entry.emittedStart = true;
75
+ return { events: [{ type: 'tool_use_start', id: callId, name }] };
76
+ }
77
+ return {};
78
+ }
79
+ if (type === 'response.function_call_arguments.delta') {
80
+ const id = ev.item_id ?? ev.call_id ?? '';
81
+ const entry = pending.get(id);
82
+ const delta = ev.delta ?? '';
83
+ if (entry && typeof delta === 'string') {
84
+ // Bound the per-call argument accumulation: this grows across frames and
85
+ // is not covered by the consumer's per-frame reassembly cap.
86
+ if (entry.args.length + delta.length > MAX_TOOL_ARGS_CHARS) {
87
+ return TOOL_STREAM_LIMIT_ERROR('argument size');
88
+ }
89
+ entry.args += delta;
90
+ const outId = entry.callId || entry.id;
91
+ // If we hadn't emitted tool_use_start yet (server sent the args
92
+ // before the item.added with a name), do so now using whatever
93
+ // name landed later. Defensive — opencode's pattern.
94
+ const startEvents = [];
95
+ if (!entry.emittedStart && entry.name) {
96
+ entry.emittedStart = true;
97
+ startEvents.push({ type: 'tool_use_start', id: outId, name: entry.name });
98
+ }
99
+ return {
100
+ events: [...startEvents, { type: 'tool_use_delta', id: outId, partialInput: delta }],
101
+ };
102
+ }
103
+ return {};
104
+ }
105
+ if (type === 'response.function_call_arguments.done') {
106
+ const id = ev.item_id ?? ev.call_id ?? '';
107
+ const entry = pending.get(id);
108
+ if (!entry)
109
+ return {};
110
+ pending.delete(id);
111
+ if (typeof ev.arguments === 'string' && ev.arguments)
112
+ entry.args = ev.arguments;
113
+ const input = parseToolArgs(entry.args);
114
+ const outId = entry.callId || entry.id;
115
+ const events = [];
116
+ if (!entry.emittedStart && entry.name) {
117
+ events.push({ type: 'tool_use_start', id: outId, name: entry.name });
118
+ }
119
+ events.push({ type: 'tool_use_end', id: outId, input });
120
+ return { events };
121
+ }
122
+ if (type === 'response.completed') {
123
+ const usage = ev.response?.usage;
124
+ const input = usage?.input_tokens ?? 0;
125
+ const cacheRead = usage?.input_tokens_details?.cached_tokens ?? 0;
126
+ const incomplete = ev.response?.incomplete_details?.reason;
127
+ let stopReason = 'end_turn';
128
+ if (incomplete === 'max_output_tokens')
129
+ stopReason = 'max_tokens';
130
+ else if (incomplete === 'stop_sequence')
131
+ stopReason = 'stop_sequence';
132
+ // The presence of unflushed function calls would already get mapped to
133
+ // tool_use by the post-loop logic; the explicit "completed" event
134
+ // doesn't carry a tool_use stop reason on its own.
135
+ return {
136
+ stopReason,
137
+ ...(usage
138
+ ? {
139
+ usage: {
140
+ input: Math.max(0, input - cacheRead),
141
+ output: usage.output_tokens ?? 0,
142
+ ...(usage.input_tokens_details?.cached_tokens !== undefined ? { cacheRead } : {}),
143
+ },
144
+ }
145
+ : {}),
146
+ terminal: true,
147
+ };
148
+ }
149
+ if (type === 'response.failed' || type === 'response.error' || type === 'error') {
150
+ const msg = ev.error?.message ?? `Codex stream failed: ${type}`;
151
+ return { events: [{ type: 'error', message: msg, retryable: false }], terminal: true };
152
+ }
153
+ return {};
154
+ }
155
+ //# sourceMappingURL=sse-event-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse-event-handler.js","sourceRoot":"","sources":["../../src/codex/sse-event-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type ProviderEvent } from '@moxxy/sdk';
2
+ export declare function toErrorEvent(err: unknown): ProviderEvent;
3
+ export declare function consumeResponsesSse(body: ReadableStream<Uint8Array>, signal: AbortSignal | undefined, emitReasoning?: boolean, onActivity?: () => void): AsyncIterable<ProviderEvent>;
4
+ //# sourceMappingURL=stream-consumer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-consumer.d.ts","sourceRoot":"","sources":["../../src/codex/stream-consumer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,aAAa,EAAmB,MAAM,YAAY,CAAC;AAKlF,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CAKxD;AASD,wBAAuB,mBAAmB,CACxC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,EAChC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,aAAa,UAAQ,EAGrB,UAAU,CAAC,EAAE,MAAM,IAAI,GACtB,aAAa,CAAC,aAAa,CAAC,CA0J9B"}
@@ -0,0 +1,176 @@
1
+ import { toFriendlyError } from '@moxxy/sdk';
2
+ import { handleSseEvent } from './sse-event-handler.js';
3
+ import { CODEX_RESPONSES_URL } from '../oauth.js';
4
+ import { parseToolArgs } from './stream-types.js';
5
+ export function toErrorEvent(err) {
6
+ return {
7
+ type: 'error',
8
+ ...toFriendlyError(err, { provider: 'openai-codex', url: CODEX_RESPONSES_URL }),
9
+ };
10
+ }
11
+ // Hard cap on the unparsed reassembly buffer. A misbehaving/MITM'd endpoint (or
12
+ // a wedged proxy emitting a continuous body with no blank-line frame separator)
13
+ // would otherwise grow `buffer` without bound until OOM, since it's only ever
14
+ // truncated when a separator is found. 8 MiB is far larger than any legitimate
15
+ // single SSE frame; exceeding it with no separator is treated as a hostile stream.
16
+ const MAX_SSE_BUFFER_BYTES = 8 * 1024 * 1024;
17
+ export async function* consumeResponsesSse(body, signal, emitReasoning = false,
18
+ // Called on every successful body read so the caller can reset an idle
19
+ // watchdog (a stalled stream that stops sending bytes must not hang forever).
20
+ onActivity) {
21
+ const reader = body.getReader();
22
+ const decoder = new TextDecoder('utf-8');
23
+ let buffer = '';
24
+ const pending = new Map();
25
+ let stopReason = 'end_turn';
26
+ let usageIn = 0;
27
+ let usageOut = 0;
28
+ let usageCacheRead = 0;
29
+ // Tracks whether ANY tool_use_end was yielded during the stream.
30
+ // The Responses API's `response.completed` event doesn't differentiate
31
+ // text-only vs tool-use turns, so without this we'd report end_turn
32
+ // even when tools were requested — the upstream tool-use loop would
33
+ // then drop the calls without executing them.
34
+ let sawToolCall = false;
35
+ // Set when an error frame (response.failed/error) surfaced the failure, so
36
+ // we don't also flush tool calls / emit a normal message_end on top of it.
37
+ let errored = false;
38
+ try {
39
+ try {
40
+ outer: while (true) {
41
+ if (signal?.aborted) {
42
+ yield { type: 'error', message: 'aborted', retryable: false };
43
+ return;
44
+ }
45
+ const { done, value } = await reader.read();
46
+ if (done)
47
+ break;
48
+ onActivity?.();
49
+ buffer += decoder.decode(value, { stream: true });
50
+ // Bound the unparsed buffer: if it grows past the cap without yielding a
51
+ // single complete frame, the peer is feeding us an unframed/oversized
52
+ // body. Bail rather than accumulate to OOM. (The `finally` cancels the
53
+ // reader so the socket is released.)
54
+ if (buffer.length > MAX_SSE_BUFFER_BYTES) {
55
+ yield {
56
+ type: 'error',
57
+ message: 'Codex stream frame exceeded size limit',
58
+ retryable: false,
59
+ };
60
+ return;
61
+ }
62
+ // SSE frames are separated by blank lines. Some servers emit \r\n\r\n;
63
+ // match either form at the separator and line boundaries rather than
64
+ // rescanning the whole accumulated buffer with a global CRLF replace on
65
+ // every chunk. A `\r` split across two reads is handled because the
66
+ // boundary regexes tolerate the optional `\r`.
67
+ let m;
68
+ const sepRe = /\r?\n\r?\n/g;
69
+ while ((m = sepRe.exec(buffer)) !== null) {
70
+ const frame = buffer.slice(0, m.index);
71
+ buffer = buffer.slice(m.index + m[0].length);
72
+ sepRe.lastIndex = 0;
73
+ for (const line of frame.split(/\r?\n/)) {
74
+ if (!line.startsWith('data:'))
75
+ continue;
76
+ const payload = line.slice(5).trimStart();
77
+ if (!payload || payload === '[DONE]')
78
+ continue;
79
+ let json;
80
+ try {
81
+ json = JSON.parse(payload);
82
+ }
83
+ catch {
84
+ continue;
85
+ }
86
+ const result = handleSseEvent(json, pending, emitReasoning);
87
+ if (result.events) {
88
+ for (const ev of result.events) {
89
+ if (ev.type === 'tool_use_end')
90
+ sawToolCall = true;
91
+ if (ev.type === 'error')
92
+ errored = true;
93
+ yield ev;
94
+ }
95
+ }
96
+ if (result.stopReason)
97
+ stopReason = result.stopReason;
98
+ if (result.usage) {
99
+ usageIn = result.usage.input ?? usageIn;
100
+ usageOut = result.usage.output ?? usageOut;
101
+ usageCacheRead = result.usage.cacheRead ?? usageCacheRead;
102
+ }
103
+ // Terminal frame (response.completed / failed / error): stop
104
+ // consuming. Honoring this prevents a failed turn from emitting
105
+ // both an `error` AND a trailing `message_end`, and ignores any
106
+ // stray frames after completion.
107
+ if (result.terminal)
108
+ break outer;
109
+ }
110
+ }
111
+ }
112
+ }
113
+ catch (err) {
114
+ yield toErrorEvent(err);
115
+ return;
116
+ }
117
+ // The error frame already surfaced the failure; nothing more to emit. Drop
118
+ // any pending function calls so a later code path can't flush a phantom
119
+ // tool_use on top of a failed turn — the error supersedes them.
120
+ if (errored) {
121
+ pending.clear();
122
+ return;
123
+ }
124
+ // Flush any tool_call_end events that didn't have a matching .done frame
125
+ // (defensive — the server normally sends function_call.done, but a
126
+ // truncated stream shouldn't drop the entire tool-use sequence).
127
+ for (const entry of pending.values()) {
128
+ const outId = entry.callId || entry.id;
129
+ // If we have a name but never emitted the start (server sent args.delta
130
+ // for an item whose .added carried no name, then truncated before .done),
131
+ // emit the start first — mirrors the .done branch — so the call isn't
132
+ // dropped just because its start frame never landed.
133
+ if (!entry.emittedStart && entry.name) {
134
+ entry.emittedStart = true;
135
+ yield { type: 'tool_use_start', id: outId, name: entry.name };
136
+ }
137
+ if (entry.emittedStart) {
138
+ sawToolCall = true;
139
+ yield { type: 'tool_use_end', id: outId, input: parseToolArgs(entry.args) };
140
+ }
141
+ else if (process.env.MOXXY_DEBUG) {
142
+ // A pending function call that never carried a name and never started:
143
+ // we cannot synthesize a valid tool_use, so it is dropped — but make
144
+ // it observable rather than silently swallowed.
145
+ console.error(`[openai-codex] dropping a truncated function_call with no name (id=${outId}, args=${entry.args.length}B)`);
146
+ }
147
+ }
148
+ // If we yielded any tool_use_end this stream, the turn IS a tool-use
149
+ // turn regardless of what `response.completed` said. The Responses API
150
+ // sends `completed` with no stop_reason field, so we infer from the
151
+ // events we actually emitted. Without this upgrade, codex turns with
152
+ // tool calls were reported as end_turn and the loop dropped them.
153
+ if (stopReason === 'end_turn' && sawToolCall) {
154
+ stopReason = 'tool_use';
155
+ }
156
+ yield {
157
+ type: 'message_end',
158
+ stopReason,
159
+ ...(usageIn > 0 || usageOut > 0
160
+ ? {
161
+ usage: {
162
+ inputTokens: usageIn,
163
+ outputTokens: usageOut,
164
+ ...(usageCacheRead > 0 ? { cacheReadTokens: usageCacheRead } : {}),
165
+ },
166
+ }
167
+ : {}),
168
+ };
169
+ }
170
+ finally {
171
+ // Always release the HTTP body — on normal completion, error, abort, or
172
+ // the consumer abandoning the stream early — so the socket isn't leaked.
173
+ reader.cancel().catch(() => { });
174
+ }
175
+ }
176
+ //# sourceMappingURL=stream-consumer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-consumer.js","sourceRoot":"","sources":["../../src/codex/stream-consumer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import type { ProviderEvent, StopReason } from '@moxxy/sdk';
2
+ export interface PendingFunctionCall {
3
+ id: string;
4
+ callId: string;
5
+ name: string;
6
+ args: string;
7
+ emittedStart: boolean;
8
+ }
9
+ export interface ResponsesSseEvent {
10
+ type?: string;
11
+ delta?: string;
12
+ item?: {
13
+ type?: string;
14
+ id?: string;
15
+ call_id?: string;
16
+ name?: string;
17
+ arguments?: string;
18
+ /** On a `reasoning` output item: the opaque blob to replay (round-trip). */
19
+ encrypted_content?: string;
20
+ };
21
+ item_id?: string;
22
+ call_id?: string;
23
+ name?: string;
24
+ arguments?: string;
25
+ response?: {
26
+ status?: string;
27
+ incomplete_details?: {
28
+ reason?: string;
29
+ };
30
+ usage?: {
31
+ input_tokens?: number;
32
+ output_tokens?: number;
33
+ input_tokens_details?: {
34
+ cached_tokens?: number;
35
+ };
36
+ };
37
+ };
38
+ error?: {
39
+ message?: string;
40
+ };
41
+ }
42
+ export interface SseStepResult {
43
+ events?: ProviderEvent[];
44
+ stopReason?: StopReason;
45
+ usage?: {
46
+ input?: number;
47
+ output?: number;
48
+ cacheRead?: number;
49
+ };
50
+ terminal?: boolean;
51
+ }
52
+ /**
53
+ * Parse accumulated function-call argument text into the tool input. The
54
+ * server normally sends well-formed JSON, but a truncated stream can leave
55
+ * partial JSON; rather than drop the call we surface the raw text under
56
+ * `_rawPartial` so the upstream loop still sees the tool request. Shared by
57
+ * the per-event handler (`function_call_arguments.done`) and the consumer's
58
+ * truncated-stream flush so the two stay in lockstep.
59
+ */
60
+ export declare function parseToolArgs(args: string): unknown;
61
+ //# sourceMappingURL=stream-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-types.d.ts","sourceRoot":"","sources":["../../src/codex/stream-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE;QACL,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,4EAA4E;QAC5E,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,kBAAkB,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACzC,KAAK,CAAC,EAAE;YACN,YAAY,CAAC,EAAE,MAAM,CAAC;YACtB,aAAa,CAAC,EAAE,MAAM,CAAC;YACvB,oBAAoB,CAAC,EAAE;gBAAE,aAAa,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;SACnD,CAAC;KACH,CAAC;IACF,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOnD"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Parse accumulated function-call argument text into the tool input. The
3
+ * server normally sends well-formed JSON, but a truncated stream can leave
4
+ * partial JSON; rather than drop the call we surface the raw text under
5
+ * `_rawPartial` so the upstream loop still sees the tool request. Shared by
6
+ * the per-event handler (`function_call_arguments.done`) and the consumer's
7
+ * truncated-stream flush so the two stay in lockstep.
8
+ */
9
+ export function parseToolArgs(args) {
10
+ if (!args)
11
+ return {};
12
+ try {
13
+ return JSON.parse(args);
14
+ }
15
+ catch {
16
+ return { _rawPartial: args };
17
+ }
18
+ }
19
+ //# sourceMappingURL=stream-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-types.js","sourceRoot":"","sources":["../../src/codex/stream-types.ts"],"names":[],"mappings":"AA6CA;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAC/B,CAAC;AACH,CAAC"}
@@ -0,0 +1,11 @@
1
+ export declare const openaiCodexProviderDef: import("@moxxy/sdk").ProviderDef;
2
+ export declare const openaiCodexPlugin: import("@moxxy/sdk").Plugin;
3
+ export default openaiCodexPlugin;
4
+ export { CodexProvider } from './provider.js';
5
+ export { codexModels, DEFAULT_CODEX_MODEL } from './models.js';
6
+ export { CLIENT_ID, ISSUER, AUTHORIZE_URL, TOKEN_URL, CODEX_RESPONSES_URL, DEFAULT_CALLBACK_PORT, DEFAULT_REDIRECT_PATH, DEFAULT_REDIRECT_URI, SCOPES, ORIGINATOR, generatePKCE, generateState, buildAuthorizeUrl, parseJwtClaims, extractAccountId, exchangeCodeForTokens, refreshTokens, } from './oauth.js';
7
+ export { CODEX_PROVIDER_ID, codexOauthProfile, } from './profile.js';
8
+ export { codexLogin, codexLogout, codexStatus, ensureFreshCodexTokens, persistCodexTokens, readStoredTokens, readStoredTokens as readCodexStoredTokens, } from './login.js';
9
+ export type { CodexProviderConfig } from './provider.js';
10
+ export type { CodexTokens, PkceCodes, OAuthTokenResponse } from './types.js';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,sBAAsB,kCAajC,CAAC;AAEH,eAAO,MAAM,iBAAiB,6BAI5B,CAAC;AAEH,eAAe,iBAAiB,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EACL,SAAS,EACT,MAAM,EACN,aAAa,EACb,SAAS,EACT,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,MAAM,EACN,UAAU,EACV,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,UAAU,EACV,WAAW,EACX,WAAW,EACX,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,IAAI,qBAAqB,GAC1C,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ import { defineProvider, definePlugin } from '@moxxy/sdk';
2
+ import { CodexProvider } from './provider.js';
3
+ import { codexModels } from './models.js';
4
+ import { codexLogin, codexLogout, codexStatus } from './login.js';
5
+ import { PLUGIN_VERSION } from './codex/headers.js';
6
+ export const openaiCodexProviderDef = defineProvider({
7
+ name: 'openai-codex',
8
+ models: [...codexModels],
9
+ createClient: (config) => new CodexProvider(config),
10
+ // No validateKey: OAuth credentials are validated by the OAuth token
11
+ // exchange itself, not by a synchronous key check.
12
+ auth: {
13
+ kind: 'oauth',
14
+ serviceName: 'ChatGPT Pro/Plus',
15
+ login: codexLogin,
16
+ logout: codexLogout,
17
+ status: codexStatus,
18
+ },
19
+ });
20
+ export const openaiCodexPlugin = definePlugin({
21
+ name: '@moxxy/plugin-provider-openai-codex',
22
+ version: PLUGIN_VERSION,
23
+ providers: [openaiCodexProviderDef],
24
+ });
25
+ export default openaiCodexPlugin;
26
+ export { CodexProvider } from './provider.js';
27
+ export { codexModels, DEFAULT_CODEX_MODEL } from './models.js';
28
+ export { CLIENT_ID, ISSUER, AUTHORIZE_URL, TOKEN_URL, CODEX_RESPONSES_URL, DEFAULT_CALLBACK_PORT, DEFAULT_REDIRECT_PATH, DEFAULT_REDIRECT_URI, SCOPES, ORIGINATOR, generatePKCE, generateState, buildAuthorizeUrl, parseJwtClaims, extractAccountId, exchangeCodeForTokens, refreshTokens, } from './oauth.js';
29
+ export { CODEX_PROVIDER_ID, codexOauthProfile, } from './profile.js';
30
+ export { codexLogin, codexLogout, codexStatus, ensureFreshCodexTokens, persistCodexTokens, readStoredTokens, readStoredTokens as readCodexStoredTokens, } from './login.js';
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,aAAa,EAA4B,MAAM,eAAe,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,MAAM,CAAC,MAAM,sBAAsB,GAAG,cAAc,CAAC;IACnD,IAAI,EAAE,cAAc;IACpB,MAAM,EAAE,CAAC,GAAG,WAAW,CAAC;IACxB,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,aAAa,CAAC,MAA6B,CAAC;IAC1E,qEAAqE;IACrE,mDAAmD;IACnD,IAAI,EAAE;QACJ,IAAI,EAAE,OAAO;QACb,WAAW,EAAE,kBAAkB;QAC/B,KAAK,EAAE,UAAU;QACjB,MAAM,EAAE,WAAW;QACnB,MAAM,EAAE,WAAW;KACpB;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,YAAY,CAAC;IAC5C,IAAI,EAAE,qCAAqC;IAC3C,OAAO,EAAE,cAAc;IACvB,SAAS,EAAE,CAAC,sBAAsB,CAAC;CACpC,CAAC,CAAC;AAEH,eAAe,iBAAiB,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EACL,SAAS,EACT,MAAM,EACN,aAAa,EACb,SAAS,EACT,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,MAAM,EACN,UAAU,EACV,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,UAAU,EACV,WAAW,EACX,WAAW,EACX,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,IAAI,qBAAqB,GAC1C,MAAM,YAAY,CAAC"}