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.
Files changed (44) hide show
  1. package/CHANGELOG.md +329 -0
  2. package/LICENSE +21 -0
  3. package/README.md +272 -0
  4. package/dist/cli.d.ts +13 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +409 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/client.d.ts +30 -0
  9. package/dist/client.d.ts.map +1 -0
  10. package/dist/client.js +106 -0
  11. package/dist/client.js.map +1 -0
  12. package/dist/oauth.d.ts +44 -0
  13. package/dist/oauth.d.ts.map +1 -0
  14. package/dist/oauth.js +135 -0
  15. package/dist/oauth.js.map +1 -0
  16. package/dist/router/logger.d.ts +19 -0
  17. package/dist/router/logger.d.ts.map +1 -0
  18. package/dist/router/logger.js +104 -0
  19. package/dist/router/logger.js.map +1 -0
  20. package/dist/router/middleware.d.ts +9 -0
  21. package/dist/router/middleware.d.ts.map +1 -0
  22. package/dist/router/middleware.js +46 -0
  23. package/dist/router/middleware.js.map +1 -0
  24. package/dist/router/models.d.ts +11 -0
  25. package/dist/router/models.d.ts.map +1 -0
  26. package/dist/router/models.js +61 -0
  27. package/dist/router/models.js.map +1 -0
  28. package/dist/router/server.d.ts +14 -0
  29. package/dist/router/server.d.ts.map +1 -0
  30. package/dist/router/server.js +423 -0
  31. package/dist/router/server.js.map +1 -0
  32. package/dist/router/translator.d.ts +33 -0
  33. package/dist/router/translator.d.ts.map +1 -0
  34. package/dist/router/translator.js +302 -0
  35. package/dist/router/translator.js.map +1 -0
  36. package/dist/token-manager.d.ts +32 -0
  37. package/dist/token-manager.d.ts.map +1 -0
  38. package/dist/token-manager.js +79 -0
  39. package/dist/token-manager.js.map +1 -0
  40. package/dist/types.d.ts +247 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/types.js +12 -0
  43. package/dist/types.js.map +1 -0
  44. 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"}