anthropic-gateway 1.0.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.
@@ -0,0 +1,258 @@
1
+ "use strict";
2
+ // XML Streaming Converter: OpenAI text stream → Anthropic SSE with XML tool call detection
3
+ // Uses buffered approach: accumulates complete tool calls before emitting
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.streamXmlOpenAIToAnthropic = streamXmlOpenAIToAnthropic;
6
+ const tools_1 = require("./tools");
7
+ const tokenUsage_1 = require("../utils/tokenUsage");
8
+ const errorLog_1 = require("../utils/errorLog");
9
+ // Regex patterns
10
+ const THINK_BLOCK_PATTERN = /<think>[\s\S]*?<\/think>/g;
11
+ const TOOL_CODE_PATTERN = /<tool_code\s+name\s*=\s*"([^"]+)"\s*>([\s\S]*?)<\/\s*tool_code\s*>/i;
12
+ const NESTED_TOOL_PATTERN = /<tool\s+name="[^"]*">\s*/g;
13
+ const CLOSE_TOOL_PATTERN = /<\/tool>\s*/g;
14
+ /**
15
+ * Transform OpenAI streaming response (with XML tool calls) to Anthropic SSE format.
16
+ * Uses BUFFERED approach: waits for complete tool calls before emitting.
17
+ */
18
+ async function streamXmlOpenAIToAnthropic(openaiStream, reply, originalModel, provider = '') {
19
+ const state = {
20
+ messageId: `msg_${Date.now().toString(36)}`,
21
+ model: originalModel,
22
+ responseModel: '',
23
+ provider,
24
+ contentBlockIndex: 0,
25
+ inputTokens: 0,
26
+ outputTokens: 0,
27
+ cachedInputTokens: 0,
28
+ hasStarted: false,
29
+ buffer: '',
30
+ toolCallsEmitted: 0,
31
+ };
32
+ const raw = reply.raw;
33
+ // Set SSE headers
34
+ raw.setHeader('Content-Type', 'text/event-stream');
35
+ raw.setHeader('Cache-Control', 'no-cache');
36
+ raw.setHeader('Connection', 'keep-alive');
37
+ raw.setHeader('X-Accel-Buffering', 'no');
38
+ try {
39
+ for await (const chunk of openaiStream) {
40
+ processChunk(chunk, state, raw);
41
+ }
42
+ // Final flush - emit any remaining text
43
+ flushRemainingContent(state, raw);
44
+ finishStream(state, raw);
45
+ }
46
+ catch (error) {
47
+ sendErrorEvent(normalizeStreamingError(error), state, raw);
48
+ }
49
+ }
50
+ function normalizeStreamingError(error) {
51
+ return error instanceof Error
52
+ ? error
53
+ : new Error(typeof error === 'string' ? error : 'Unknown streaming failure');
54
+ }
55
+ function processChunk(chunk, state, raw) {
56
+ // Update usage if present
57
+ if (chunk.usage) {
58
+ state.inputTokens = chunk.usage.prompt_tokens;
59
+ state.outputTokens = chunk.usage.completion_tokens;
60
+ state.cachedInputTokens = chunk.usage.prompt_tokens_details?.cached_tokens ?? 0;
61
+ }
62
+ // Capture response model
63
+ if (chunk.model && !state.responseModel) {
64
+ state.responseModel = chunk.model;
65
+ }
66
+ const choice = chunk.choices[0];
67
+ if (!choice)
68
+ return;
69
+ // Send message_start on first chunk
70
+ if (!state.hasStarted) {
71
+ sendMessageStart(state, raw);
72
+ state.hasStarted = true;
73
+ }
74
+ const textDelta = choice.delta?.content || '';
75
+ if (!textDelta)
76
+ return;
77
+ // Add to buffer
78
+ state.buffer += textDelta;
79
+ // Process buffer for complete tool calls
80
+ processBuffer(state, raw);
81
+ }
82
+ function processBuffer(state, raw) {
83
+ // Keep processing until no more complete tool calls are found
84
+ while (true) {
85
+ // Remove <think> blocks from consideration
86
+ const cleanBuffer = state.buffer.replace(THINK_BLOCK_PATTERN, '');
87
+ // Check for complete tool call
88
+ const toolMatch = cleanBuffer.match(TOOL_CODE_PATTERN);
89
+ if (!toolMatch) {
90
+ // No complete tool call found, exit loop
91
+ break;
92
+ }
93
+ const [fullMatch, toolName, rawArgs] = toolMatch;
94
+ const matchStart = cleanBuffer.indexOf(fullMatch);
95
+ // Get text BEFORE the tool call
96
+ const textBeforeTool = cleanBuffer.substring(0, matchStart);
97
+ const cleanText = textBeforeTool.trim();
98
+ // Emit text block if there's content
99
+ if (cleanText.length > 0) {
100
+ emitTextBlock(cleanText, state, raw);
101
+ }
102
+ // Clean and emit tool use block
103
+ const cleanArgs = cleanToolArgs(rawArgs);
104
+ emitToolUseBlock(toolName, cleanArgs, state, raw);
105
+ // Update buffer: remove everything up to and including the tool call
106
+ // We need to find the position in the ORIGINAL buffer (with think blocks)
107
+ const originalMatchEnd = state.buffer.indexOf('</tool_code>') + '</tool_code>'.length;
108
+ state.buffer = state.buffer.substring(originalMatchEnd);
109
+ }
110
+ }
111
+ function flushRemainingContent(state, raw) {
112
+ // Clean remaining buffer
113
+ const cleanBuffer = state.buffer.replace(THINK_BLOCK_PATTERN, '').trim();
114
+ // Get any remaining text
115
+ const remainingText = cleanBuffer.trim();
116
+ if (remainingText.length > 0) {
117
+ emitTextBlock(remainingText, state, raw);
118
+ }
119
+ }
120
+ function cleanToolArgs(args) {
121
+ let cleaned = args;
122
+ // Remove nested <tool name="..."> tags
123
+ cleaned = cleaned.replace(NESTED_TOOL_PATTERN, '');
124
+ // Remove </tool> closing tags
125
+ cleaned = cleaned.replace(CLOSE_TOOL_PATTERN, '');
126
+ // Remove any leading ToolName\n pattern
127
+ cleaned = cleaned.replace(/^[A-Za-z_][A-Za-z0-9_]*\s*\n/, '');
128
+ return cleaned.trim();
129
+ }
130
+ function emitTextBlock(text, state, raw) {
131
+ // Start text block
132
+ const startEvent = {
133
+ type: 'content_block_start',
134
+ index: state.contentBlockIndex,
135
+ content_block: { type: 'text', text: '' },
136
+ };
137
+ sendSSE(startEvent, raw);
138
+ // Send text delta
139
+ const deltaEvent = {
140
+ type: 'content_block_delta',
141
+ index: state.contentBlockIndex,
142
+ delta: { type: 'text_delta', text },
143
+ };
144
+ sendSSE(deltaEvent, raw);
145
+ // Stop text block
146
+ const stopEvent = {
147
+ type: 'content_block_stop',
148
+ index: state.contentBlockIndex,
149
+ };
150
+ sendSSE(stopEvent, raw);
151
+ state.contentBlockIndex++;
152
+ }
153
+ function emitToolUseBlock(toolName, args, state, raw) {
154
+ const toolId = (0, tools_1.generateToolUseId)();
155
+ // Start tool_use block
156
+ const startEvent = {
157
+ type: 'content_block_start',
158
+ index: state.contentBlockIndex,
159
+ content_block: {
160
+ type: 'tool_use',
161
+ id: toolId,
162
+ name: toolName,
163
+ input: {},
164
+ },
165
+ };
166
+ sendSSE(startEvent, raw);
167
+ // Send complete input as single delta
168
+ const deltaEvent = {
169
+ type: 'content_block_delta',
170
+ index: state.contentBlockIndex,
171
+ delta: {
172
+ type: 'input_json_delta',
173
+ partial_json: args,
174
+ },
175
+ };
176
+ sendSSE(deltaEvent, raw);
177
+ // Stop tool_use block
178
+ const stopEvent = {
179
+ type: 'content_block_stop',
180
+ index: state.contentBlockIndex,
181
+ };
182
+ sendSSE(stopEvent, raw);
183
+ state.contentBlockIndex++;
184
+ state.toolCallsEmitted++;
185
+ }
186
+ function sendMessageStart(state, raw) {
187
+ const event = {
188
+ type: 'message_start',
189
+ message: {
190
+ id: state.messageId,
191
+ type: 'message',
192
+ role: 'assistant',
193
+ content: [],
194
+ model: state.model,
195
+ stop_reason: null,
196
+ stop_sequence: null,
197
+ usage: {
198
+ input_tokens: state.inputTokens,
199
+ output_tokens: state.outputTokens,
200
+ cache_read_input_tokens: state.cachedInputTokens,
201
+ },
202
+ },
203
+ };
204
+ sendSSE(event, raw);
205
+ }
206
+ function finishStream(state, raw) {
207
+ // Determine stop reason
208
+ const stopReason = state.toolCallsEmitted > 0 ? 'tool_use' : 'end_turn';
209
+ // Record token usage
210
+ (0, tokenUsage_1.recordUsage)({
211
+ provider: state.provider,
212
+ modelName: state.model,
213
+ model: state.responseModel || undefined,
214
+ inputTokens: state.inputTokens,
215
+ outputTokens: state.outputTokens,
216
+ cachedInputTokens: state.cachedInputTokens || undefined,
217
+ streaming: true
218
+ });
219
+ // Send message_delta
220
+ const deltaEvent = {
221
+ type: 'message_delta',
222
+ delta: {
223
+ stop_reason: stopReason,
224
+ stop_sequence: null,
225
+ },
226
+ usage: {
227
+ output_tokens: state.outputTokens,
228
+ cache_read_input_tokens: state.cachedInputTokens,
229
+ },
230
+ };
231
+ sendSSE(deltaEvent, raw);
232
+ // Send message_stop
233
+ sendSSE({ type: 'message_stop' }, raw);
234
+ raw.end();
235
+ }
236
+ function sendErrorEvent(error, state, raw) {
237
+ // Record error to file
238
+ (0, errorLog_1.recordError)(error, {
239
+ requestId: state.messageId,
240
+ provider: state.provider,
241
+ modelName: state.model,
242
+ streaming: true
243
+ });
244
+ const event = {
245
+ type: 'error',
246
+ error: {
247
+ type: 'api_error',
248
+ message: error.message,
249
+ },
250
+ };
251
+ sendSSE(event, raw);
252
+ raw.end();
253
+ }
254
+ function sendSSE(data, raw) {
255
+ raw.write(`event: ${data.type}\n`);
256
+ raw.write(`data: ${JSON.stringify(data)}\n\n`);
257
+ }
258
+ //# sourceMappingURL=xmlStreaming.js.map
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.updateClaudeSettings = exports.updateClaudeJson = exports.configExists = exports.preserveProxyAuthToken = exports.ensureProxyAuthToken = exports.saveConfig = exports.loadConfig = exports.findAvailablePort = exports.createServer = void 0;
18
+ // Main library exports
19
+ __exportStar(require("./types"), exports);
20
+ __exportStar(require("./converters"), exports);
21
+ var server_1 = require("./server");
22
+ Object.defineProperty(exports, "createServer", { enumerable: true, get: function () { return server_1.createServer; } });
23
+ Object.defineProperty(exports, "findAvailablePort", { enumerable: true, get: function () { return server_1.findAvailablePort; } });
24
+ var config_1 = require("./utils/config");
25
+ Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_1.loadConfig; } });
26
+ Object.defineProperty(exports, "saveConfig", { enumerable: true, get: function () { return config_1.saveConfig; } });
27
+ Object.defineProperty(exports, "ensureProxyAuthToken", { enumerable: true, get: function () { return config_1.ensureProxyAuthToken; } });
28
+ Object.defineProperty(exports, "preserveProxyAuthToken", { enumerable: true, get: function () { return config_1.preserveProxyAuthToken; } });
29
+ Object.defineProperty(exports, "configExists", { enumerable: true, get: function () { return config_1.configExists; } });
30
+ Object.defineProperty(exports, "updateClaudeJson", { enumerable: true, get: function () { return config_1.updateClaudeJson; } });
31
+ Object.defineProperty(exports, "updateClaudeSettings", { enumerable: true, get: function () { return config_1.updateClaudeSettings; } });
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createMessagesHandler = createMessagesHandler;
7
+ const openai_1 = __importDefault(require("openai"));
8
+ const crypto_1 = require("crypto");
9
+ const request_1 = require("../converters/request");
10
+ const provider_1 = require("../utils/provider");
11
+ const response_1 = require("../converters/response");
12
+ const streaming_1 = require("../converters/streaming");
13
+ const xmlStreaming_1 = require("../converters/xmlStreaming");
14
+ const validation_1 = require("../utils/validation");
15
+ const logger_1 = require("../utils/logger");
16
+ const tokenUsage_1 = require("../utils/tokenUsage");
17
+ const errorLog_1 = require("../utils/errorLog");
18
+ // Request ID counter for unique identification
19
+ let requestIdCounter = 0;
20
+ function generateRequestId() {
21
+ requestIdCounter++;
22
+ const timestamp = Date.now().toString(36);
23
+ const counter = requestIdCounter.toString(36).padStart(4, '0');
24
+ return `req_${timestamp}_${counter}`;
25
+ }
26
+ function matchesProxyToken(value, expectedToken) {
27
+ if (typeof value !== 'string') {
28
+ return false;
29
+ }
30
+ const received = Buffer.from(value);
31
+ const expected = Buffer.from(expectedToken);
32
+ return received.length === expected.length && (0, crypto_1.timingSafeEqual)(received, expected);
33
+ }
34
+ function isAuthenticated(request, expectedToken) {
35
+ const authorization = request.headers.authorization;
36
+ if (typeof authorization === 'string' && authorization.startsWith('Bearer ')) {
37
+ return matchesProxyToken(authorization.slice('Bearer '.length), expectedToken);
38
+ }
39
+ return matchesProxyToken(request.headers['x-api-key'], expectedToken);
40
+ }
41
+ /**
42
+ * Handle POST /v1/messages requests
43
+ */
44
+ function createMessagesHandler(config) {
45
+ const isAzure = (0, provider_1.isAzureOpenAIEndpoint)(config.baseUrl);
46
+ const openai = new openai_1.default({
47
+ baseURL: config.baseUrl,
48
+ apiKey: config.apiKey,
49
+ });
50
+ return async (request, reply) => {
51
+ const requestId = generateRequestId();
52
+ const log = logger_1.logger.withRequestId(requestId);
53
+ // Add request ID to response headers for client tracing
54
+ reply.header('X-Request-Id', requestId);
55
+ if (!isAuthenticated(request, config.proxyAuthToken)) {
56
+ const errorResponse = (0, response_1.createErrorResponse)(new Error('Invalid proxy authentication token'), 401);
57
+ reply.code(401).send({ error: errorResponse.error });
58
+ return;
59
+ }
60
+ try {
61
+ // Validate request before processing
62
+ const validation = (0, validation_1.validateAnthropicRequest)(request.body);
63
+ if (!validation.valid) {
64
+ const errorMessage = (0, validation_1.formatValidationErrors)(validation.errors);
65
+ log.warn('Invalid request', { errors: validation.errors });
66
+ const errorResponse = (0, response_1.createErrorResponse)(new Error(errorMessage), 400);
67
+ reply.code(400).send({ error: errorResponse.error });
68
+ return;
69
+ }
70
+ const anthropicRequest = request.body;
71
+ const targetModel = anthropicRequest.model;
72
+ const isStreaming = anthropicRequest.stream ?? false;
73
+ log.info(`→ ${targetModel} [sent]`);
74
+ // Determine tool calling style from config
75
+ const toolStyle = config.toolFormat || 'native';
76
+ // Convert request to OpenAI format
77
+ const openaiRequest = (0, request_1.convertRequestToOpenAI)(anthropicRequest, targetModel, toolStyle, isAzure);
78
+ // Log tool calling mode when tools are present
79
+ if (toolStyle === 'xml' && anthropicRequest.tools?.length) {
80
+ log.info(`Using XML tool calling mode (${anthropicRequest.tools.length} tools)`);
81
+ }
82
+ if (isStreaming) {
83
+ if (toolStyle === 'xml') {
84
+ await handleXmlStreamingRequest(openai, openaiRequest, reply, anthropicRequest.model, config.baseUrl, log);
85
+ }
86
+ else {
87
+ await handleStreamingRequest(openai, openaiRequest, reply, anthropicRequest.model, config.baseUrl, log);
88
+ }
89
+ }
90
+ else {
91
+ await handleNonStreamingRequest(openai, openaiRequest, reply, anthropicRequest.model, config.baseUrl, log);
92
+ }
93
+ log.info(`← ${targetModel} [received]`);
94
+ }
95
+ catch (error) {
96
+ const body = request.body;
97
+ handleError(error, reply, log, {
98
+ requestId,
99
+ provider: config.baseUrl,
100
+ modelName: body?.model ?? 'unknown',
101
+ streaming: body?.stream ?? false,
102
+ });
103
+ }
104
+ };
105
+ }
106
+ /**
107
+ * Handle non-streaming API request
108
+ */
109
+ async function handleNonStreamingRequest(openai, openaiRequest, reply, originalModel, provider, log) {
110
+ log.debug('Making non-streaming request');
111
+ const response = await openai.chat.completions.create({
112
+ ...openaiRequest,
113
+ stream: false,
114
+ });
115
+ log.debug('Response received', {
116
+ finishReason: response.choices[0]?.finish_reason,
117
+ usage: response.usage,
118
+ });
119
+ // Record token usage
120
+ if (response.usage) {
121
+ (0, tokenUsage_1.recordUsage)({
122
+ provider,
123
+ modelName: originalModel,
124
+ model: response.model,
125
+ inputTokens: response.usage.prompt_tokens,
126
+ outputTokens: response.usage.completion_tokens,
127
+ cachedInputTokens: response.usage.prompt_tokens_details?.cached_tokens,
128
+ streaming: false,
129
+ });
130
+ }
131
+ const anthropicResponse = (0, response_1.convertResponseToAnthropic)(response, originalModel);
132
+ reply.send(anthropicResponse);
133
+ }
134
+ /**
135
+ * Handle streaming API request
136
+ */
137
+ async function handleStreamingRequest(openai, openaiRequest, reply, originalModel, provider, log) {
138
+ log.debug('Making streaming request');
139
+ const stream = await openai.chat.completions.create({
140
+ ...openaiRequest,
141
+ stream: true,
142
+ });
143
+ reply.hijack();
144
+ await (0, streaming_1.streamOpenAIToAnthropic)(stream, reply, originalModel, provider);
145
+ log.debug('Streaming completed');
146
+ }
147
+ /**
148
+ * Handle XML streaming API request (for models without native tool calling)
149
+ */
150
+ async function handleXmlStreamingRequest(openai, openaiRequest, reply, originalModel, provider, log) {
151
+ log.debug('Making XML streaming request (experimental)');
152
+ const stream = await openai.chat.completions.create({
153
+ ...openaiRequest,
154
+ stream: true,
155
+ });
156
+ reply.hijack();
157
+ await (0, xmlStreaming_1.streamXmlOpenAIToAnthropic)(stream, reply, originalModel, provider);
158
+ log.debug('XML streaming completed');
159
+ }
160
+ /**
161
+ * Handle errors and send appropriate response
162
+ */
163
+ function handleError(caughtError, reply, log, context) {
164
+ const error = caughtError instanceof Error
165
+ ? caughtError
166
+ : new Error(typeof caughtError === 'string' ? caughtError : 'Unknown request failure');
167
+ const rawStatus = typeof caughtError === 'object' && caughtError !== null && 'status' in caughtError
168
+ ? caughtError.status
169
+ : undefined;
170
+ const statusCode = typeof rawStatus === 'number' &&
171
+ Number.isInteger(rawStatus) &&
172
+ rawStatus >= 400 &&
173
+ rawStatus <= 599
174
+ ? rawStatus
175
+ : 500;
176
+ log.error('Request failed', error, { statusCode });
177
+ // Record error to file if context is available
178
+ if (context) {
179
+ (0, errorLog_1.recordError)(error, context);
180
+ }
181
+ const errorResponse = (0, response_1.createErrorResponse)(error, statusCode);
182
+ reply.code(errorResponse.status).send({ error: errorResponse.error });
183
+ }
184
+ //# sourceMappingURL=handlers.js.map
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.createServer = createServer;
40
+ exports.findAvailablePort = findAvailablePort;
41
+ // Fastify proxy server setup
42
+ const fastify_1 = __importDefault(require("fastify"));
43
+ const handlers_1 = require("./handlers");
44
+ const logger_1 = require("../utils/logger");
45
+ // Default graceful shutdown timeout in milliseconds
46
+ const DEFAULT_SHUTDOWN_TIMEOUT = 10000;
47
+ /**
48
+ * Create the proxy server with configured routes
49
+ */
50
+ function createServer(config) {
51
+ if (typeof config.proxyAuthToken !== 'string' || config.proxyAuthToken.length === 0) {
52
+ throw new Error('proxyAuthToken is required to start the Claude Adapter server');
53
+ }
54
+ const app = (0, fastify_1.default)({ logger: false });
55
+ // Health check endpoint
56
+ app.get('/health', async (_request, _reply) => {
57
+ return { status: 'ok', adapter: 'claude-adapter' };
58
+ });
59
+ // Main messages endpoint (matches Anthropic API)
60
+ app.post('/v1/messages', (0, handlers_1.createMessagesHandler)(config));
61
+ return {
62
+ app,
63
+ start: async (port) => {
64
+ try {
65
+ await app.listen({ port, host: '127.0.0.1' });
66
+ const address = app.server.address();
67
+ const actualPort = typeof address === 'object' && address ? address.port : port;
68
+ const url = `http://127.0.0.1:${actualPort}`;
69
+ return url;
70
+ }
71
+ catch (err) {
72
+ if (err.code === 'EADDRINUSE') {
73
+ throw new Error(`Port ${port} is already in use. Try a different port.`);
74
+ }
75
+ throw err;
76
+ }
77
+ },
78
+ stop: async (timeout = DEFAULT_SHUTDOWN_TIMEOUT) => {
79
+ // Create a timeout promise for force shutdown
80
+ let timeoutId;
81
+ const forceShutdown = new Promise((resolve) => {
82
+ timeoutId = setTimeout(() => {
83
+ logger_1.logger.warn('Graceful shutdown timeout exceeded, forcing close');
84
+ resolve();
85
+ }, timeout);
86
+ });
87
+ try {
88
+ // Race between graceful close and timeout
89
+ await Promise.race([app.close(), forceShutdown]);
90
+ }
91
+ finally {
92
+ if (timeoutId)
93
+ clearTimeout(timeoutId);
94
+ }
95
+ },
96
+ };
97
+ }
98
+ /**
99
+ * Find an available port starting from the preferred port
100
+ */
101
+ async function findAvailablePort(preferredPort) {
102
+ const net = await Promise.resolve().then(() => __importStar(require('net')));
103
+ return new Promise((resolve) => {
104
+ const server = net.createServer();
105
+ server.listen(preferredPort, '127.0.0.1', () => {
106
+ const address = server.address();
107
+ const port = typeof address === 'object' && address ? address.port : preferredPort;
108
+ server.close(() => resolve(port));
109
+ });
110
+ server.on('error', () => {
111
+ // Port is in use, try next port
112
+ resolve(findAvailablePort(preferredPort + 1));
113
+ });
114
+ });
115
+ }
116
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ // Anthropic API Types for Messages endpoint
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ //# sourceMappingURL=anthropic.js.map
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ // Configuration types for claude-adapter
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // Type exports
18
+ __exportStar(require("./anthropic"), exports);
19
+ __exportStar(require("./openai"), exports);
20
+ __exportStar(require("./config"), exports);
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ // OpenAI Chat Completions API Types
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ //# sourceMappingURL=openai.js.map