claude-gateway 2.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.
- package/CHANGELOG.md +329 -0
- package/LICENSE +21 -0
- package/README.md +272 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +409 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +30 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +106 -0
- package/dist/client.js.map +1 -0
- package/dist/oauth.d.ts +44 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +135 -0
- package/dist/oauth.js.map +1 -0
- package/dist/router/logger.d.ts +19 -0
- package/dist/router/logger.d.ts.map +1 -0
- package/dist/router/logger.js +104 -0
- package/dist/router/logger.js.map +1 -0
- package/dist/router/middleware.d.ts +9 -0
- package/dist/router/middleware.d.ts.map +1 -0
- package/dist/router/middleware.js +46 -0
- package/dist/router/middleware.js.map +1 -0
- package/dist/router/models.d.ts +11 -0
- package/dist/router/models.d.ts.map +1 -0
- package/dist/router/models.js +61 -0
- package/dist/router/models.js.map +1 -0
- package/dist/router/server.d.ts +14 -0
- package/dist/router/server.d.ts.map +1 -0
- package/dist/router/server.js +423 -0
- package/dist/router/server.js.map +1 -0
- package/dist/router/translator.d.ts +33 -0
- package/dist/router/translator.d.ts.map +1 -0
- package/dist/router/translator.js +302 -0
- package/dist/router/translator.js.map +1 -0
- package/dist/token-manager.d.ts +32 -0
- package/dist/token-manager.d.ts.map +1 -0
- package/dist/token-manager.js +79 -0
- package/dist/token-manager.js.map +1 -0
- package/dist/types.d.ts +247 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Gateway Server - OpenAI-compatible proxy for Claude Code
|
|
3
|
+
*
|
|
4
|
+
* Exports a startServer function that can be called programmatically
|
|
5
|
+
* or from the CLI entry point.
|
|
6
|
+
*/
|
|
7
|
+
import express from 'express';
|
|
8
|
+
import { getValidAccessToken, loadTokens, saveTokens } from '../token-manager.js';
|
|
9
|
+
import { startOAuthFlow, exchangeCodeForTokens } from '../oauth.js';
|
|
10
|
+
import { ensureRequiredSystemPrompt } from './middleware.js';
|
|
11
|
+
import { logger } from './logger.js';
|
|
12
|
+
import { translateOpenAIToAnthropic, translateAnthropicToOpenAI, translateAnthropicStreamToOpenAI, translateAnthropicErrorToOpenAI, validateOpenAIRequest, } from './translator.js';
|
|
13
|
+
import { ANTHROPIC_MODELS } from './models.js';
|
|
14
|
+
// Anthropic API configuration
|
|
15
|
+
const ANTHROPIC_API_URL = 'https://api.anthropic.com/v1/messages';
|
|
16
|
+
const ANTHROPIC_VERSION = '2023-06-01';
|
|
17
|
+
const ANTHROPIC_BETA = 'oauth-2025-04-20,claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14';
|
|
18
|
+
/**
|
|
19
|
+
* Start the router server
|
|
20
|
+
* @param port Port to listen on
|
|
21
|
+
* @param askQuestion Function to ask user questions (for OAuth flow)
|
|
22
|
+
* @param onReadyCallback Optional callback when server is ready and no longer needs user input
|
|
23
|
+
*/
|
|
24
|
+
export async function startServer(port, askQuestion, onReadyCallback) {
|
|
25
|
+
const app = express();
|
|
26
|
+
// CORS middleware - allow cross-origin requests from anywhere
|
|
27
|
+
app.use((req, res, next) => {
|
|
28
|
+
res.header('Access-Control-Allow-Origin', '*');
|
|
29
|
+
res.header('Access-Control-Allow-Headers', '*');
|
|
30
|
+
res.header('Access-Control-Allow-Methods', 'GET, HEAD, PUT, PATCH, POST, DELETE');
|
|
31
|
+
// Handle preflight requests
|
|
32
|
+
if (req.method === 'OPTIONS') {
|
|
33
|
+
return res.sendStatus(200);
|
|
34
|
+
}
|
|
35
|
+
next();
|
|
36
|
+
});
|
|
37
|
+
// Parse JSON request bodies with increased limit for large payloads
|
|
38
|
+
app.use(express.json({ limit: '50mb' }));
|
|
39
|
+
// Root endpoint - landing page
|
|
40
|
+
app.get('/', (_req, res) => {
|
|
41
|
+
res.setHeader('Content-Type', 'text/html');
|
|
42
|
+
res.send(`
|
|
43
|
+
<!DOCTYPE html>
|
|
44
|
+
<html lang="en">
|
|
45
|
+
<head>
|
|
46
|
+
<meta charset="UTF-8">
|
|
47
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
48
|
+
<title>Claude Gateway</title>
|
|
49
|
+
<style>
|
|
50
|
+
* {
|
|
51
|
+
margin: 0;
|
|
52
|
+
padding: 0;
|
|
53
|
+
box-sizing: border-box;
|
|
54
|
+
}
|
|
55
|
+
body {
|
|
56
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
57
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
58
|
+
color: #333;
|
|
59
|
+
min-height: 100vh;
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
padding: 20px;
|
|
64
|
+
}
|
|
65
|
+
.container {
|
|
66
|
+
background: white;
|
|
67
|
+
border-radius: 16px;
|
|
68
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
69
|
+
max-width: 800px;
|
|
70
|
+
width: 100%;
|
|
71
|
+
padding: 48px;
|
|
72
|
+
}
|
|
73
|
+
h1 {
|
|
74
|
+
font-size: 2.5em;
|
|
75
|
+
margin-bottom: 8px;
|
|
76
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
77
|
+
-webkit-background-clip: text;
|
|
78
|
+
-webkit-text-fill-color: transparent;
|
|
79
|
+
background-clip: text;
|
|
80
|
+
}
|
|
81
|
+
.subtitle {
|
|
82
|
+
color: #666;
|
|
83
|
+
font-size: 1.1em;
|
|
84
|
+
margin-bottom: 32px;
|
|
85
|
+
}
|
|
86
|
+
.status {
|
|
87
|
+
background: #e8f5e9;
|
|
88
|
+
border-left: 4px solid #4caf50;
|
|
89
|
+
padding: 16px;
|
|
90
|
+
border-radius: 4px;
|
|
91
|
+
margin-bottom: 32px;
|
|
92
|
+
}
|
|
93
|
+
.status-title {
|
|
94
|
+
font-weight: 600;
|
|
95
|
+
color: #2e7d32;
|
|
96
|
+
margin-bottom: 8px;
|
|
97
|
+
font-size: 1.1em;
|
|
98
|
+
}
|
|
99
|
+
.status-text {
|
|
100
|
+
color: #1b5e20;
|
|
101
|
+
}
|
|
102
|
+
.section {
|
|
103
|
+
margin-bottom: 32px;
|
|
104
|
+
}
|
|
105
|
+
h2 {
|
|
106
|
+
color: #333;
|
|
107
|
+
font-size: 1.5em;
|
|
108
|
+
margin-bottom: 16px;
|
|
109
|
+
border-bottom: 2px solid #f0f0f0;
|
|
110
|
+
padding-bottom: 8px;
|
|
111
|
+
}
|
|
112
|
+
.endpoint {
|
|
113
|
+
background: #f8f9fa;
|
|
114
|
+
border-radius: 8px;
|
|
115
|
+
padding: 16px;
|
|
116
|
+
margin-bottom: 12px;
|
|
117
|
+
font-family: 'Monaco', 'Courier New', monospace;
|
|
118
|
+
}
|
|
119
|
+
.method {
|
|
120
|
+
display: inline-block;
|
|
121
|
+
padding: 4px 12px;
|
|
122
|
+
border-radius: 4px;
|
|
123
|
+
font-weight: 600;
|
|
124
|
+
font-size: 0.85em;
|
|
125
|
+
margin-right: 12px;
|
|
126
|
+
}
|
|
127
|
+
.method.post {
|
|
128
|
+
background: #ffc107;
|
|
129
|
+
color: #333;
|
|
130
|
+
}
|
|
131
|
+
.method.get {
|
|
132
|
+
background: #2196f3;
|
|
133
|
+
color: white;
|
|
134
|
+
}
|
|
135
|
+
.url {
|
|
136
|
+
color: #333;
|
|
137
|
+
font-size: 0.95em;
|
|
138
|
+
}
|
|
139
|
+
.code-block {
|
|
140
|
+
background: #1e1e1e;
|
|
141
|
+
color: #d4d4d4;
|
|
142
|
+
padding: 20px;
|
|
143
|
+
border-radius: 8px;
|
|
144
|
+
overflow-x: auto;
|
|
145
|
+
font-family: 'Monaco', 'Courier New', monospace;
|
|
146
|
+
font-size: 0.9em;
|
|
147
|
+
line-height: 1.6;
|
|
148
|
+
white-space: pre;
|
|
149
|
+
}
|
|
150
|
+
.code-block .keyword {
|
|
151
|
+
color: #569cd6;
|
|
152
|
+
}
|
|
153
|
+
.code-block .string {
|
|
154
|
+
color: #ce9178;
|
|
155
|
+
}
|
|
156
|
+
.code-block .comment {
|
|
157
|
+
color: #6a9955;
|
|
158
|
+
}
|
|
159
|
+
.features {
|
|
160
|
+
display: grid;
|
|
161
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
162
|
+
gap: 16px;
|
|
163
|
+
margin-top: 16px;
|
|
164
|
+
}
|
|
165
|
+
.feature {
|
|
166
|
+
padding: 16px;
|
|
167
|
+
background: #f8f9fa;
|
|
168
|
+
border-radius: 8px;
|
|
169
|
+
border-left: 3px solid #667eea;
|
|
170
|
+
}
|
|
171
|
+
.feature-title {
|
|
172
|
+
font-weight: 600;
|
|
173
|
+
margin-bottom: 4px;
|
|
174
|
+
color: #333;
|
|
175
|
+
}
|
|
176
|
+
.feature-text {
|
|
177
|
+
font-size: 0.9em;
|
|
178
|
+
color: #666;
|
|
179
|
+
}
|
|
180
|
+
footer {
|
|
181
|
+
margin-top: 32px;
|
|
182
|
+
padding-top: 24px;
|
|
183
|
+
border-top: 2px solid #f0f0f0;
|
|
184
|
+
text-align: center;
|
|
185
|
+
color: #666;
|
|
186
|
+
font-size: 0.9em;
|
|
187
|
+
}
|
|
188
|
+
a {
|
|
189
|
+
color: #667eea;
|
|
190
|
+
text-decoration: none;
|
|
191
|
+
}
|
|
192
|
+
a:hover {
|
|
193
|
+
text-decoration: underline;
|
|
194
|
+
}
|
|
195
|
+
</style>
|
|
196
|
+
</head>
|
|
197
|
+
<body>
|
|
198
|
+
<div class="container">
|
|
199
|
+
<h1><span style="display: inline-block;">🚀</span> Claude Gateway</h1>
|
|
200
|
+
<p class="subtitle">OpenAI-compatible proxy for Claude Code subscriptions</p>
|
|
201
|
+
|
|
202
|
+
<div class="status">
|
|
203
|
+
<div class="status-title">✅ Gateway is running</div>
|
|
204
|
+
<div class="status-text">The server is ready to accept requests</div>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<div class="section">
|
|
208
|
+
<h2>📋 Available Endpoints</h2>
|
|
209
|
+
<div class="endpoint">
|
|
210
|
+
<span class="method post">POST</span>
|
|
211
|
+
<span class="url">http://localhost:${port}/v1/chat/completions</span>
|
|
212
|
+
</div>
|
|
213
|
+
<div class="endpoint">
|
|
214
|
+
<span class="method get">GET</span>
|
|
215
|
+
<span class="url">http://localhost:${port}/v1/models</span>
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
|
|
219
|
+
<div class="section">
|
|
220
|
+
<h2>💡 Quick Start</h2>
|
|
221
|
+
<p style="margin-bottom: 16px;">Use with any OpenAI-compatible tool:</p>
|
|
222
|
+
<div class="code-block">
|
|
223
|
+
<span class="keyword">from</span> openai <span class="keyword">import</span> OpenAI
|
|
224
|
+
|
|
225
|
+
client = OpenAI(
|
|
226
|
+
api_key=<span class="string">"not-used"</span>, <span class="comment"># Gateway handles auth</span>
|
|
227
|
+
base_url=<span class="string">"http://localhost:${port}/v1"</span>,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
response = client.chat.completions.create(
|
|
231
|
+
model=<span class="string">"claude-sonnet-4-5-20250929"</span>,
|
|
232
|
+
messages=[{<span class="string">"role"</span>: <span class="string">"user"</span>, <span class="string">"content"</span>: <span class="string">"Hello!"</span>}]
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
<span class="keyword">print</span>(response.choices[0].message.content)
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
<div class="section">
|
|
240
|
+
<h2>✨ Features</h2>
|
|
241
|
+
<div class="features">
|
|
242
|
+
<div class="feature">
|
|
243
|
+
<div class="feature-title">OpenAI Compatible</div>
|
|
244
|
+
<div class="feature-text">Drop-in replacement for OpenAI API</div>
|
|
245
|
+
</div>
|
|
246
|
+
<div class="feature">
|
|
247
|
+
<div class="feature-title">Auto Authentication</div>
|
|
248
|
+
<div class="feature-text">OAuth flow handled automatically</div>
|
|
249
|
+
</div>
|
|
250
|
+
<div class="feature">
|
|
251
|
+
<div class="feature-title">Streaming Support</div>
|
|
252
|
+
<div class="feature-text">Full streaming response support</div>
|
|
253
|
+
</div>
|
|
254
|
+
<div class="feature">
|
|
255
|
+
<div class="feature-title">Token Auto-Refresh</div>
|
|
256
|
+
<div class="feature-text">Handles token expiration seamlessly</div>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<footer>
|
|
262
|
+
<p>Claude Gateway | <a href="https://gitlab.com/soapbox-pub/claude-gateway" target="_blank">Documentation</a> | MIT License</p>
|
|
263
|
+
</footer>
|
|
264
|
+
</div>
|
|
265
|
+
</body>
|
|
266
|
+
</html>
|
|
267
|
+
`);
|
|
268
|
+
});
|
|
269
|
+
// OpenAI Models endpoint - return static model list
|
|
270
|
+
app.get('/v1/models', (_req, res) => {
|
|
271
|
+
res.status(200).json({
|
|
272
|
+
object: 'list',
|
|
273
|
+
data: ANTHROPIC_MODELS,
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
// OpenAI Chat Completions endpoint handler
|
|
277
|
+
const handleChatCompletionsRequest = async (req, res) => {
|
|
278
|
+
const requestId = Math.random().toString(36).substring(7);
|
|
279
|
+
const timestamp = new Date().toISOString();
|
|
280
|
+
try {
|
|
281
|
+
// Get the request body as an OpenAI request
|
|
282
|
+
const openaiRequest = req.body;
|
|
283
|
+
// Validate the request
|
|
284
|
+
validateOpenAIRequest(openaiRequest);
|
|
285
|
+
// Translate OpenAI request to Anthropic format
|
|
286
|
+
const anthropicRequest = translateOpenAIToAnthropic(openaiRequest);
|
|
287
|
+
const hadSystemPrompt = !!(anthropicRequest.system && anthropicRequest.system.length > 0);
|
|
288
|
+
// Ensure the required system prompt is present
|
|
289
|
+
const modifiedRequest = ensureRequiredSystemPrompt(anthropicRequest);
|
|
290
|
+
// Get a valid OAuth access token (auto-refreshes if needed)
|
|
291
|
+
const accessToken = await getValidAccessToken();
|
|
292
|
+
// Forward the request to Anthropic API
|
|
293
|
+
const response = await fetch(ANTHROPIC_API_URL, {
|
|
294
|
+
method: 'POST',
|
|
295
|
+
headers: {
|
|
296
|
+
'Content-Type': 'application/json',
|
|
297
|
+
Authorization: `Bearer ${accessToken}`,
|
|
298
|
+
'anthropic-version': ANTHROPIC_VERSION,
|
|
299
|
+
'anthropic-beta': ANTHROPIC_BETA,
|
|
300
|
+
},
|
|
301
|
+
body: JSON.stringify(modifiedRequest),
|
|
302
|
+
});
|
|
303
|
+
// Handle streaming responses
|
|
304
|
+
if (openaiRequest.stream &&
|
|
305
|
+
response.headers.get('content-type')?.includes('text/event-stream')) {
|
|
306
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
307
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
308
|
+
res.setHeader('Connection', 'keep-alive');
|
|
309
|
+
res.status(response.status);
|
|
310
|
+
// Generate a message ID for the stream
|
|
311
|
+
const messageId = `chatcmpl-${requestId}`;
|
|
312
|
+
// Translate Anthropic stream to OpenAI format
|
|
313
|
+
for await (const chunk of translateAnthropicStreamToOpenAI(response.body, openaiRequest.model, messageId)) {
|
|
314
|
+
res.write(chunk);
|
|
315
|
+
}
|
|
316
|
+
res.end();
|
|
317
|
+
// Log streaming response
|
|
318
|
+
logger.logRequest(requestId, timestamp, modifiedRequest, hadSystemPrompt, {
|
|
319
|
+
status: response.status,
|
|
320
|
+
data: undefined,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
// Handle non-streaming response
|
|
325
|
+
if (!response.ok) {
|
|
326
|
+
const errorData = await response.json();
|
|
327
|
+
const openaiError = translateAnthropicErrorToOpenAI(errorData);
|
|
328
|
+
logger.logRequest(requestId, timestamp, modifiedRequest, hadSystemPrompt, {
|
|
329
|
+
status: response.status,
|
|
330
|
+
data: errorData,
|
|
331
|
+
});
|
|
332
|
+
res.status(response.status).json(openaiError);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
const anthropicResponse = (await response.json());
|
|
336
|
+
const openaiResponse = translateAnthropicToOpenAI(anthropicResponse, openaiRequest.model);
|
|
337
|
+
logger.logRequest(requestId, timestamp, modifiedRequest, hadSystemPrompt, {
|
|
338
|
+
status: response.status,
|
|
339
|
+
data: anthropicResponse,
|
|
340
|
+
});
|
|
341
|
+
res.status(response.status).json(openaiResponse);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
// Log the error
|
|
346
|
+
logger.logRequest(requestId, timestamp, req.body, false, undefined, error instanceof Error ? error : new Error('Unknown error'));
|
|
347
|
+
// If headers were already sent (e.g., streaming response in progress),
|
|
348
|
+
// we cannot send an error response - just log and return
|
|
349
|
+
if (res.headersSent) {
|
|
350
|
+
logger.error(`[${requestId}] Error occurred after headers sent:`, error);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
// Return OpenAI-format error
|
|
354
|
+
const openaiError = translateAnthropicErrorToOpenAI(error instanceof Error ? { message: error.message } : { message: 'Unknown error' });
|
|
355
|
+
res.status(500).json(openaiError);
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
// OpenAI Chat Completions endpoint
|
|
359
|
+
app.post('/v1/chat/completions', handleChatCompletionsRequest);
|
|
360
|
+
// Startup sequence
|
|
361
|
+
logger.startup('');
|
|
362
|
+
logger.startup(' ██████╗██╗ █████╗ ██╗ ██╗██████╗ ███████╗');
|
|
363
|
+
logger.startup('██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔════╝');
|
|
364
|
+
logger.startup('██║ ██║ ███████║██║ ██║██║ ██║█████╗ ');
|
|
365
|
+
logger.startup('██║ ██║ ██╔══██║██║ ██║██║ ██║██╔══╝ ');
|
|
366
|
+
logger.startup('╚██████╗███████╗██║ ██║╚██████╔╝██████╔╝███████╗');
|
|
367
|
+
logger.startup(' ╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝');
|
|
368
|
+
logger.startup(' ═══════ Gateway ═══════ ');
|
|
369
|
+
logger.startup('');
|
|
370
|
+
// Check if we have tokens
|
|
371
|
+
let tokens = await loadTokens();
|
|
372
|
+
if (!tokens) {
|
|
373
|
+
// No OAuth tokens found - prompt user for authentication
|
|
374
|
+
logger.startup('No OAuth tokens found. Starting authentication...');
|
|
375
|
+
logger.startup('');
|
|
376
|
+
try {
|
|
377
|
+
const { code, verifier, state } = await startOAuthFlow(askQuestion);
|
|
378
|
+
logger.startup('✅ Authorization received');
|
|
379
|
+
logger.startup('🔄 Exchanging for tokens...\n');
|
|
380
|
+
const newTokens = await exchangeCodeForTokens(code, verifier, state);
|
|
381
|
+
await saveTokens(newTokens);
|
|
382
|
+
tokens = newTokens;
|
|
383
|
+
logger.startup('✅ Authentication successful!');
|
|
384
|
+
logger.startup('');
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
logger.error('❌ Authentication failed:', error instanceof Error ? error.message : error);
|
|
388
|
+
process.exit(1);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
logger.startup('✅ OAuth tokens found.');
|
|
393
|
+
}
|
|
394
|
+
// Validate/refresh token
|
|
395
|
+
if (tokens) {
|
|
396
|
+
try {
|
|
397
|
+
await getValidAccessToken();
|
|
398
|
+
logger.startup('✅ Token validated.');
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
logger.error('❌ Token validation failed:', error);
|
|
402
|
+
logger.info('Please logout and restart.');
|
|
403
|
+
process.exit(1);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
logger.startup('');
|
|
407
|
+
// Start the server
|
|
408
|
+
app.listen(port, () => {
|
|
409
|
+
logger.startup(`🚀 Gateway running on http://localhost:${port}`);
|
|
410
|
+
logger.startup('');
|
|
411
|
+
logger.startup('📋 Endpoints:');
|
|
412
|
+
logger.startup(` POST http://localhost:${port}/v1/chat/completions (OpenAI-compatible)`);
|
|
413
|
+
logger.startup(` GET http://localhost:${port}/v1/models`);
|
|
414
|
+
logger.startup('');
|
|
415
|
+
logger.startup('💡 OpenAI compatibility mode - configure tools to use OpenAI Chat Completions API');
|
|
416
|
+
logger.startup('');
|
|
417
|
+
// Notify that we're ready and no longer need user input
|
|
418
|
+
if (onReadyCallback) {
|
|
419
|
+
onReadyCallback();
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/router/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EDUCATIONAL AND ENTERTAINMENT PURPOSES ONLY
|
|
3
|
+
*
|
|
4
|
+
* This software is provided for educational, research, and entertainment purposes only.
|
|
5
|
+
* It is not affiliated with, endorsed by, or sponsored by Anthropic PBC.
|
|
6
|
+
* Use at your own risk. No warranties provided. Users are solely responsible for
|
|
7
|
+
* ensuring compliance with Anthropic's Terms of Service and all applicable laws.
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2025 - Licensed under MIT License
|
|
10
|
+
*/
|
|
11
|
+
import { OpenAIChatCompletionRequest, OpenAIChatCompletionResponse, OpenAIErrorResponse, AnthropicRequest, AnthropicResponse } from '../types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Translate OpenAI Chat Completion request to Anthropic Messages API request
|
|
14
|
+
*/
|
|
15
|
+
export declare function translateOpenAIToAnthropic(openaiRequest: OpenAIChatCompletionRequest): AnthropicRequest;
|
|
16
|
+
/**
|
|
17
|
+
* Translate Anthropic response to OpenAI Chat Completion response
|
|
18
|
+
*/
|
|
19
|
+
export declare function translateAnthropicToOpenAI(anthropicResponse: AnthropicResponse, originalModel: string): OpenAIChatCompletionResponse;
|
|
20
|
+
/**
|
|
21
|
+
* Translate Anthropic streaming events to OpenAI streaming format
|
|
22
|
+
* This returns a generator that yields OpenAI-formatted SSE strings
|
|
23
|
+
*/
|
|
24
|
+
export declare function translateAnthropicStreamToOpenAI(anthropicStream: AsyncIterable<Uint8Array>, originalModel: string, messageId: string): AsyncGenerator<string, void, unknown>;
|
|
25
|
+
/**
|
|
26
|
+
* Translate Anthropic error to OpenAI error format
|
|
27
|
+
*/
|
|
28
|
+
export declare function translateAnthropicErrorToOpenAI(error: unknown): OpenAIErrorResponse;
|
|
29
|
+
/**
|
|
30
|
+
* Validate OpenAI request and throw errors for unsupported features
|
|
31
|
+
*/
|
|
32
|
+
export declare function validateOpenAIRequest(request: OpenAIChatCompletionRequest): void;
|
|
33
|
+
//# sourceMappingURL=translator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translator.d.ts","sourceRoot":"","sources":["../../src/router/translator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,2BAA2B,EAG3B,4BAA4B,EAC5B,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EAIlB,MAAM,aAAa,CAAC;AAmBrB;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,2BAA2B,GACzC,gBAAgB,CAgFlB;AAiBD;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,iBAAiB,EAAE,iBAAiB,EACpC,aAAa,EAAE,MAAM,GACpB,4BAA4B,CAwD9B;AAED;;;GAGG;AACH,wBAAuB,gCAAgC,CACrD,eAAe,EAAE,aAAa,CAAC,UAAU,CAAC,EAC1C,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,GAChB,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAsFvC;AAED;;GAEG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAuBnF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,2BAA2B,GAAG,IAAI,CAwBhF"}
|