codex-openai-proxy 0.1.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +253 -0
  3. package/dist/app-server/app-server.d.ts +39 -0
  4. package/dist/app-server/app-server.js +261 -0
  5. package/dist/app-server/app-server.js.map +1 -0
  6. package/dist/app-server/auth.d.ts +16 -0
  7. package/dist/app-server/auth.js +102 -0
  8. package/dist/app-server/auth.js.map +1 -0
  9. package/dist/app-server/json-rpc.d.ts +29 -0
  10. package/dist/app-server/json-rpc.js +141 -0
  11. package/dist/app-server/json-rpc.js.map +1 -0
  12. package/dist/bin.d.ts +2 -0
  13. package/dist/bin.js +11 -0
  14. package/dist/bin.js.map +1 -0
  15. package/dist/cli/cli.d.ts +6 -0
  16. package/dist/cli/cli.js +319 -0
  17. package/dist/cli/cli.js.map +1 -0
  18. package/dist/continuation/state.d.ts +71 -0
  19. package/dist/continuation/state.js +460 -0
  20. package/dist/continuation/state.js.map +1 -0
  21. package/dist/core/abort.d.ts +13 -0
  22. package/dist/core/abort.js +74 -0
  23. package/dist/core/abort.js.map +1 -0
  24. package/dist/core/canonical.d.ts +6 -0
  25. package/dist/core/canonical.js +22 -0
  26. package/dist/core/canonical.js.map +1 -0
  27. package/dist/core/config.d.ts +33 -0
  28. package/dist/core/config.js +139 -0
  29. package/dist/core/config.js.map +1 -0
  30. package/dist/core/logger.d.ts +17 -0
  31. package/dist/core/logger.js +44 -0
  32. package/dist/core/logger.js.map +1 -0
  33. package/dist/core/policy.d.ts +98 -0
  34. package/dist/core/policy.js +242 -0
  35. package/dist/core/policy.js.map +1 -0
  36. package/dist/core/redact.d.ts +2 -0
  37. package/dist/core/redact.js +35 -0
  38. package/dist/core/redact.js.map +1 -0
  39. package/dist/http/chat-execute.d.ts +24 -0
  40. package/dist/http/chat-execute.js +491 -0
  41. package/dist/http/chat-execute.js.map +1 -0
  42. package/dist/http/chat-normalize.d.ts +100 -0
  43. package/dist/http/chat-normalize.js +379 -0
  44. package/dist/http/chat-normalize.js.map +1 -0
  45. package/dist/http/chat-sse.d.ts +14 -0
  46. package/dist/http/chat-sse.js +52 -0
  47. package/dist/http/chat-sse.js.map +1 -0
  48. package/dist/http/chat-validate.d.ts +37 -0
  49. package/dist/http/chat-validate.js +190 -0
  50. package/dist/http/chat-validate.js.map +1 -0
  51. package/dist/http/chat.d.ts +8 -0
  52. package/dist/http/chat.js +137 -0
  53. package/dist/http/chat.js.map +1 -0
  54. package/dist/http/errors.d.ts +19 -0
  55. package/dist/http/errors.js +56 -0
  56. package/dist/http/errors.js.map +1 -0
  57. package/dist/http/server.d.ts +19 -0
  58. package/dist/http/server.js +254 -0
  59. package/dist/http/server.js.map +1 -0
  60. package/package.json +67 -0
  61. package/protocol/VERSION.json +10 -0
  62. package/protocol/schemas/response-mapping.schema.json +38 -0
  63. package/protocol/schemas/x-codex.schema.json +12 -0
@@ -0,0 +1,190 @@
1
+ import { record } from "../core/canonical.js";
2
+ import { validateRequestPolicy, } from "../core/policy.js";
3
+ import { HttpError } from "./errors.js";
4
+ /** Validates the deliberately narrow request surface implemented in Stage 04. */
5
+ export function validateRequest(value, log, requestId, implicitToolContinuation) {
6
+ const body = record(value);
7
+ if (!body)
8
+ invalid("Request body must be a JSON object.", null);
9
+ if (typeof body.model !== "string" || body.model.trim() === "")
10
+ invalid("model must be a non-empty string.", "model");
11
+ if (!Array.isArray(body.messages) || body.messages.length === 0)
12
+ invalid("messages must be a non-empty array.", "messages");
13
+ if (body.stream !== undefined && typeof body.stream !== "boolean")
14
+ invalid("stream must be a boolean.", "stream");
15
+ if (body.previous_response_id !== undefined &&
16
+ (typeof body.previous_response_id !== "string" ||
17
+ body.previous_response_id === ""))
18
+ invalid("previous_response_id must be a non-empty string.", "previous_response_id");
19
+ const requestPolicy = validateRequestPolicy(body.x_codex);
20
+ const messages = body.messages.map((entry, index) => validateMessage(entry, index));
21
+ const hasToolResults = messages.some((message) => message.role === "tool");
22
+ if (!body.previous_response_id && hasToolResults && !implicitToolContinuation)
23
+ invalid("Tool results require previous_response_id when implicit tool continuation is disabled.", "previous_response_id");
24
+ if (!body.previous_response_id &&
25
+ !hasToolResults &&
26
+ messages.at(-1)?.role !== "user")
27
+ invalid("The final message must have role user.", "messages");
28
+ if (body.previous_response_id &&
29
+ !["user", "tool"].includes(messages.at(-1).role))
30
+ invalid("A continuation must end with a user or tool message.", "messages");
31
+ let includeUsage = false;
32
+ if (body.stream_options !== undefined) {
33
+ const streamOptions = record(body.stream_options);
34
+ if (!streamOptions ||
35
+ Object.keys(streamOptions).some((key) => key !== "include_usage") ||
36
+ (streamOptions.include_usage !== undefined &&
37
+ typeof streamOptions.include_usage !== "boolean"))
38
+ invalid("stream_options supports only a boolean include_usage field.", "stream_options");
39
+ includeUsage = streamOptions.include_usage === true;
40
+ }
41
+ const dynamicTools = validateTools(body.tools, body.tool_choice);
42
+ const supported = new Set([
43
+ "model",
44
+ "messages",
45
+ "stream",
46
+ "stream_options",
47
+ "tools",
48
+ "tool_choice",
49
+ "previous_response_id",
50
+ "x_codex",
51
+ ]);
52
+ const ignored = Object.keys(body).filter((key) => !supported.has(key));
53
+ if (ignored.length)
54
+ log("warn", "unsupported_chat_fields_ignored", {
55
+ request_id: requestId,
56
+ fields: ignored.sort(),
57
+ });
58
+ return {
59
+ model: body.model,
60
+ messages,
61
+ stream: body.stream === true,
62
+ includeUsage,
63
+ dynamicTools,
64
+ requestPolicy,
65
+ ...(typeof body.previous_response_id === "string"
66
+ ? { previousResponseId: body.previous_response_id }
67
+ : {}),
68
+ };
69
+ }
70
+ /** Converts a safe policy failure to the public OpenAI error envelope. */
71
+ export function policyHttpError(error) {
72
+ return new HttpError(400, error.message, "invalid_request_error", error.code, error.param);
73
+ }
74
+ /** Validates one role-preserving, text-only message. */
75
+ function validateMessage(value, index) {
76
+ const message = record(value);
77
+ const param = `messages.${index}`;
78
+ if (!message ||
79
+ !["system", "developer", "user", "assistant", "tool"].includes(String(message.role)))
80
+ invalid("Only system, developer, user, assistant, and tool messages are supported.", `${param}.role`);
81
+ if (typeof message.content !== "string" &&
82
+ !(message.role === "assistant" && message.content === null))
83
+ invalid("Message content must be a string.", `${param}.content`);
84
+ const allowed = new Set([
85
+ "role",
86
+ "content",
87
+ "name",
88
+ "tool_call_id",
89
+ "tool_calls",
90
+ ]);
91
+ if (Object.keys(message).some((key) => !allowed.has(key)))
92
+ invalid("This message contains unsupported fields.", param);
93
+ let toolCalls;
94
+ if (message.role === "assistant" && message.tool_calls !== undefined) {
95
+ if (!Array.isArray(message.tool_calls))
96
+ invalid("tool_calls must be an array.", `${param}.tool_calls`);
97
+ toolCalls = message.tool_calls.map((raw, callIndex) => {
98
+ const call = record(raw);
99
+ const fn = record(call?.function);
100
+ if (call?.type !== "function" ||
101
+ typeof call.id !== "string" ||
102
+ typeof fn?.name !== "string" ||
103
+ typeof fn.arguments !== "string")
104
+ invalid("Each assistant tool call must be a complete function call.", `${param}.tool_calls.${callIndex}`);
105
+ return { id: call.id, name: fn.name, arguments: fn.arguments };
106
+ });
107
+ }
108
+ if (message.role === "tool" && typeof message.tool_call_id !== "string")
109
+ invalid("A tool message requires tool_call_id.", `${param}.tool_call_id`);
110
+ return {
111
+ role: message.role,
112
+ content: typeof message.content === "string" ? message.content : "",
113
+ ...(typeof message.tool_call_id === "string"
114
+ ? { toolCallId: message.tool_call_id }
115
+ : {}),
116
+ ...(toolCalls ? { toolCalls } : {}),
117
+ };
118
+ }
119
+ /** Converts OpenAI function declarations to app-server dynamic tool specs. */
120
+ function validateTools(value, choice) {
121
+ if (choice !== undefined && choice !== "auto" && choice !== "none")
122
+ invalid("tool_choice supports only auto or none in this stage.", "tool_choice");
123
+ if (value === undefined || choice === "none")
124
+ return [];
125
+ if (!Array.isArray(value))
126
+ invalid("tools must be an array.", "tools");
127
+ return value.map((raw, index) => {
128
+ const tool = record(raw);
129
+ const fn = record(tool?.function);
130
+ if (tool?.type !== "function" ||
131
+ !fn ||
132
+ typeof fn.name !== "string" ||
133
+ fn.name === "" ||
134
+ fn.name.length > 128 ||
135
+ !/^[a-zA-Z0-9_-]+$/.test(fn.name) ||
136
+ !record(fn.parameters))
137
+ invalid("Each tool must be a named function with a JSON Schema parameters object.", `tools.${index}`);
138
+ return {
139
+ type: "function",
140
+ name: fn.name,
141
+ description: typeof fn.description === "string" ? fn.description : "",
142
+ inputSchema: fn.parameters,
143
+ };
144
+ });
145
+ }
146
+ /** Maps prior messages to raw Responses API history without flattening roles. */
147
+ export function toHistoryItem(message) {
148
+ return {
149
+ type: "message",
150
+ role: message.role,
151
+ content: [
152
+ {
153
+ type: message.role === "assistant" ? "output_text" : "input_text",
154
+ text: message.content,
155
+ },
156
+ ],
157
+ };
158
+ }
159
+ /** Validates a complete, single-use result set for a suspended tool batch. */
160
+ export function validateToolResults(messages, pending) {
161
+ const assistant = [...messages]
162
+ .reverse()
163
+ .find((message) => message.role === "assistant" && message.toolCalls !== undefined);
164
+ if (!assistant?.toolCalls)
165
+ invalid("The assistant tool-call message is required.", "messages");
166
+ const expected = new Map(pending.map((call) => [call.callId, call]));
167
+ if (assistant.toolCalls.length !== expected.size ||
168
+ assistant.toolCalls.some((call) => expected.get(call.id)?.name !== call.name ||
169
+ call.arguments !==
170
+ JSON.stringify(expected.get(call.id)?.arguments ?? {})))
171
+ invalid("The assistant tool calls do not match the pending continuation.", "messages");
172
+ const results = new Map();
173
+ for (const message of messages) {
174
+ if (message.role !== "tool")
175
+ continue;
176
+ if (!message.toolCallId || !expected.has(message.toolCallId))
177
+ invalid("The tool result references a foreign call ID.", "messages");
178
+ if (results.has(message.toolCallId))
179
+ invalid("A tool call has more than one result.", "messages");
180
+ results.set(message.toolCallId, message.content);
181
+ }
182
+ if (results.size !== expected.size)
183
+ invalid("Exactly one result is required for every pending tool call.", "messages");
184
+ return results;
185
+ }
186
+ /** Throws an OpenAI-shaped request validation error. */
187
+ function invalid(message, param) {
188
+ throw new HttpError(400, message, "invalid_request_error", "invalid_request", param);
189
+ }
190
+ //# sourceMappingURL=chat-validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-validate.js","sourceRoot":"","sources":["../../src/http/chat-validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ServerResponse } from "node:http";
2
+ import { type ChatHandlerOptions } from "./chat-execute.js";
3
+ export { serializeSseFrame } from "./chat-sse.js";
4
+ export { aggregateNormalizedEvents, EventNormalizer, HANDLED_NOTIFICATION_METHODS, normalizeNotification, } from "./chat-normalize.js";
5
+ export type { AggregatedNormalizedEvents, NormalizedDelta, NormalizedError, NormalizedEvent, NormalizedFunction, NormalizedToolCall, NormalizedToolResult, NormalizedToolResultData, Usage, } from "./chat-normalize.js";
6
+ export type { ChatHandlerOptions } from "./chat-execute.js";
7
+ /** Validates, executes, and serializes one Chat Completions request. */
8
+ export declare function handleChatCompletion(body: unknown, response: ServerResponse, options: ChatHandlerOptions): Promise<void>;
@@ -0,0 +1,137 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { writeJson } from "./errors.js";
3
+ import { chunk, writeFrame, writeSse, writeSseError } from "./chat-sse.js";
4
+ import { aggregateNormalizedEvents, } from "./chat-normalize.js";
5
+ import { execute } from "./chat-execute.js";
6
+ import { policyHttpError, validateRequest, } from "./chat-validate.js";
7
+ import { PolicyError, resolveEffectivePolicy, } from "../core/policy.js";
8
+ export { serializeSseFrame } from "./chat-sse.js";
9
+ export { aggregateNormalizedEvents, EventNormalizer, HANDLED_NOTIFICATION_METHODS, normalizeNotification, } from "./chat-normalize.js";
10
+ /** Validates, executes, and serializes one Chat Completions request. */
11
+ export async function handleChatCompletion(body, response, options) {
12
+ let parsed;
13
+ try {
14
+ parsed = validateRequest(body, options.log, options.requestId, options.implicitToolContinuation);
15
+ }
16
+ catch (error) {
17
+ if (error instanceof PolicyError)
18
+ throw policyHttpError(error);
19
+ throw error;
20
+ }
21
+ let policy;
22
+ try {
23
+ policy = await resolveEffectivePolicy(parsed.requestPolicy, options.root, options.requirements);
24
+ }
25
+ catch (error) {
26
+ if (error instanceof PolicyError)
27
+ throw policyHttpError(error);
28
+ throw error;
29
+ }
30
+ const request = {
31
+ model: parsed.model,
32
+ messages: parsed.messages,
33
+ stream: parsed.stream,
34
+ includeUsage: parsed.includeUsage,
35
+ dynamicTools: parsed.dynamicTools,
36
+ ...(parsed.previousResponseId
37
+ ? { previousResponseId: parsed.previousResponseId }
38
+ : {}),
39
+ policy,
40
+ };
41
+ if (!request.previousResponseId &&
42
+ request.messages.some((message) => message.role === "tool")) {
43
+ const callIds = request.messages
44
+ .filter((message) => message.role === "tool")
45
+ .map((message) => message.toolCallId);
46
+ request.previousResponseId =
47
+ options.continuations.findPendingResponse(callIds);
48
+ }
49
+ const responseId = `chatcmpl_codex_${randomUUID().replaceAll("-", "")}`;
50
+ const created = Math.floor(Date.now() / 1_000);
51
+ // Setup is eager so validation and RPC failures retain their HTTP status
52
+ // instead of committing an SSE response before the generator is advanced.
53
+ const execution = await execute(request, options, responseId);
54
+ const { events } = execution;
55
+ try {
56
+ if (request.stream) {
57
+ await streamChatResponse(response, events, request, responseId, created);
58
+ return;
59
+ }
60
+ await writeAggregateResponse(response, events, request, responseId, created);
61
+ }
62
+ finally {
63
+ // This also covers writeHead/initial-role failures before the async
64
+ // generator has ever started, when its own finally block cannot run.
65
+ await execution.dispose();
66
+ }
67
+ }
68
+ /** Serializes one execution as an SSE Chat Completions response. */
69
+ async function streamChatResponse(response, events, request, responseId, created) {
70
+ response.writeHead(200, {
71
+ "content-type": "text/event-stream; charset=utf-8",
72
+ "cache-control": "no-cache, no-store",
73
+ connection: "keep-alive",
74
+ "x-accel-buffering": "no",
75
+ });
76
+ await writeSse(response, chunk(responseId, created, request.model, { role: "assistant" }, null));
77
+ let streamFailed = false;
78
+ try {
79
+ for await (const event of events) {
80
+ if (event.error) {
81
+ await writeSseError(response, event.error);
82
+ streamFailed = true;
83
+ break;
84
+ }
85
+ if (event.delta)
86
+ await writeSse(response, chunk(responseId, created, request.model, event.delta, null));
87
+ if (event.finishReason)
88
+ await writeSse(response, chunk(responseId, created, request.model, {}, event.finishReason));
89
+ if (event.usage && request.includeUsage)
90
+ await writeSse(response, {
91
+ id: responseId,
92
+ object: "chat.completion.chunk",
93
+ created,
94
+ model: request.model,
95
+ choices: [],
96
+ usage: event.usage,
97
+ });
98
+ }
99
+ }
100
+ catch (error) {
101
+ streamFailed = true;
102
+ if (!response.writableEnded && !response.destroyed)
103
+ await writeSseError(response, error instanceof Error ? error.message : "The app-server turn failed.");
104
+ }
105
+ if (!streamFailed)
106
+ await writeFrame(response, "[DONE]");
107
+ response.end();
108
+ }
109
+ /** Aggregates and serializes one non-streaming Chat Completions response. */
110
+ async function writeAggregateResponse(response, events, request, responseId, created) {
111
+ const aggregated = await aggregateNormalizedEvents(events);
112
+ const { content, reasoning, toolResults, finishReason, usage } = aggregated;
113
+ const message = {
114
+ role: "assistant",
115
+ content: aggregated.toolCalls.length && content === "" ? null : content,
116
+ };
117
+ if (reasoning)
118
+ message.reasoning = reasoning;
119
+ if (aggregated.toolCalls.length) {
120
+ message.tool_calls = aggregated.toolCalls.map((call) => ({
121
+ id: call.id,
122
+ type: call.type,
123
+ function: call.function,
124
+ }));
125
+ }
126
+ if (toolResults.length)
127
+ message.tool_results = toolResults;
128
+ writeJson(response, 200, {
129
+ id: responseId,
130
+ object: "chat.completion",
131
+ created,
132
+ model: request.model,
133
+ choices: [{ index: 0, message, finish_reason: finishReason ?? "stop" }],
134
+ ...(usage ? { usage } : {}),
135
+ });
136
+ }
137
+ //# sourceMappingURL=chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/http/chat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { ServerResponse } from "node:http";
2
+ /** OpenAI-compatible error categories emitted by the proxy. */
3
+ export type ErrorType = "invalid_request_error" | "conflict_error" | "not_found_error" | "rate_limit_error" | "server_error";
4
+ /** Carries an HTTP status and OpenAI-shaped error metadata. */
5
+ export declare class HttpError extends Error {
6
+ readonly status: number;
7
+ readonly type: ErrorType;
8
+ readonly code: string;
9
+ readonly param: string | null;
10
+ constructor(status: number, message: string, type: ErrorType, code: string, param?: string | null);
11
+ }
12
+ /** Builds the OpenAI-compatible error envelope shared by JSON and SSE output. */
13
+ export declare function errorEnvelope(message: string, type: ErrorType, code: string, param: string | null): Record<string, unknown>;
14
+ /** Builds a tool-correlation error using its narrow status-to-type policy. */
15
+ export declare function toolCorrelationErrorForStatus(status: number, message: string, code: string, param: string | null): HttpError;
16
+ /** Writes a non-cacheable JSON response unless it has already ended. */
17
+ export declare function writeJson(response: ServerResponse, status: number, value: unknown): void;
18
+ /** Serializes an HttpError in the OpenAI error envelope. */
19
+ export declare function writeError(response: ServerResponse, error: HttpError): void;
@@ -0,0 +1,56 @@
1
+ /** Carries an HTTP status and OpenAI-shaped error metadata. */
2
+ export class HttpError extends Error {
3
+ status;
4
+ type;
5
+ code;
6
+ param;
7
+ constructor(status, message, type, code, param = null) {
8
+ super(message);
9
+ this.status = status;
10
+ this.type = type;
11
+ this.code = code;
12
+ this.param = param;
13
+ }
14
+ }
15
+ /** Builds the OpenAI-compatible error envelope shared by JSON and SSE output. */
16
+ export function errorEnvelope(message, type, code, param) {
17
+ return {
18
+ error: {
19
+ message,
20
+ type,
21
+ param,
22
+ code,
23
+ },
24
+ };
25
+ }
26
+ /** Builds a tool-correlation error using its narrow status-to-type policy. */
27
+ export function toolCorrelationErrorForStatus(status, message, code, param) {
28
+ const type = status >= 500
29
+ ? "server_error"
30
+ : status === 409
31
+ ? "conflict_error"
32
+ : "invalid_request_error";
33
+ return new HttpError(status, message, type, code, param);
34
+ }
35
+ /** Writes a non-cacheable JSON response unless it has already ended. */
36
+ export function writeJson(response, status, value) {
37
+ if (response.writableEnded)
38
+ return;
39
+ // A streaming route may fail after its status and content type are committed.
40
+ if (response.headersSent) {
41
+ response.end();
42
+ return;
43
+ }
44
+ const body = JSON.stringify(value);
45
+ response.writeHead(status, {
46
+ "content-type": "application/json; charset=utf-8",
47
+ "content-length": Buffer.byteLength(body),
48
+ "cache-control": "no-store",
49
+ });
50
+ response.end(body);
51
+ }
52
+ /** Serializes an HttpError in the OpenAI error envelope. */
53
+ export function writeError(response, error) {
54
+ writeJson(response, error.status, errorEnvelope(error.message, error.type, error.code, error.param));
55
+ }
56
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/http/errors.ts"],"names":[],"mappings":"AAUA,+DAA+D;AAC/D,MAAM,OAAO,SAAU,SAAQ,KAAK;IAEvB;IAEA;IACA;IACA;IALX,YACW,MAAc,EACvB,OAAe,EACN,IAAe,EACf,IAAY,EACZ,QAAuB,IAAI;QAEpC,KAAK,CAAC,OAAO,CAAC,CAAC;QANN,WAAM,GAAN,MAAM,CAAQ;QAEd,SAAI,GAAJ,IAAI,CAAW;QACf,SAAI,GAAJ,IAAI,CAAQ;QACZ,UAAK,GAAL,KAAK,CAAsB;IAGtC,CAAC;CACF;AAED,iFAAiF;AACjF,MAAM,UAAU,aAAa,CAC3B,OAAe,EACf,IAAe,EACf,IAAY,EACZ,KAAoB;IAEpB,OAAO;QACL,KAAK,EAAE;YACL,OAAO;YACP,IAAI;YACJ,KAAK;YACL,IAAI;SACL;KACF,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,6BAA6B,CAC3C,MAAc,EACd,OAAe,EACf,IAAY,EACZ,KAAoB;IAEpB,MAAM,IAAI,GACR,MAAM,IAAI,GAAG;QACX,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,MAAM,KAAK,GAAG;YACd,CAAC,CAAC,gBAAgB;YAClB,CAAC,CAAC,uBAAuB,CAAC;IAChC,OAAO,IAAI,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;AAC3D,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,SAAS,CACvB,QAAwB,EACxB,MAAc,EACd,KAAc;IAEd,IAAI,QAAQ,CAAC,aAAa;QAAE,OAAO;IACnC,8EAA8E;IAC9E,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;QACzB,QAAQ,CAAC,GAAG,EAAE,CAAC;QACf,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE;QACzB,cAAc,EAAE,iCAAiC;QACjD,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;QACzC,eAAe,EAAE,UAAU;KAC5B,CAAC,CAAC;IACH,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,UAAU,CAAC,QAAwB,EAAE,KAAgB;IACnE,SAAS,CACP,QAAQ,EACR,KAAK,CAAC,MAAM,EACZ,aAAa,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAClE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type Server } from "node:http";
2
+ import type { ServeOptions } from "../core/config.js";
3
+ import type { Logger } from "../core/logger.js";
4
+ import type { JsonRpcTransport } from "../app-server/json-rpc.js";
5
+ import { type PolicyRequirements } from "../core/policy.js";
6
+ /** Controls the proxy HTTP listener and readiness state. */
7
+ export interface ProxyServer {
8
+ server: Server;
9
+ listen(): Promise<{
10
+ address: string;
11
+ port: number;
12
+ }>;
13
+ close(): Promise<void>;
14
+ setReady(ready: boolean): void;
15
+ setTransport(transport: JsonRpcTransport, requirements: PolicyRequirements): void;
16
+ setTransport(transport: undefined): void;
17
+ }
18
+ /** Creates a loopback proxy with bounded concurrency and request lifetimes. */
19
+ export declare function createProxyServer(options: ServeOptions, log: Logger): ProxyServer;