| |
| |
| |
| |
| |
|
|
|
|
| import 'dotenv/config';
|
| import { createRequire } from 'module';
|
| import express from 'express';
|
| import { getConfig } from './config.js';
|
| import { handleMessages, listModels, countTokens } from './handler.js';
|
| import { handleOpenAIChatCompletions, handleOpenAIResponses } from './openai-handler.js';
|
|
|
|
|
| const require = createRequire(import.meta.url);
|
| const { version: VERSION } = require('../package.json') as { version: string };
|
|
|
|
|
| const app = express();
|
| const config = getConfig();
|
|
|
|
|
| app.use(express.json({ limit: '50mb' }));
|
|
|
|
|
| app.use((_req, res, next) => {
|
| res.header('Access-Control-Allow-Origin', '*');
|
| res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
| res.header('Access-Control-Allow-Headers', '*');
|
| if (_req.method === 'OPTIONS') {
|
| res.sendStatus(200);
|
| return;
|
| }
|
| next();
|
| });
|
|
|
|
|
|
|
|
|
|
|
|
|
| const API_KEY = (process.env.API_KEY || '').trim();
|
| if (API_KEY) {
|
| app.use((req, res, next) => {
|
|
|
| if (req.path === '/' || req.path === '/health') return next();
|
|
|
| const headerKey = (req.header('x-api-key') || '').trim();
|
| const auth = (req.header('authorization') || '').trim();
|
|
|
| let provided = headerKey;
|
| if (!provided && auth.toLowerCase().startsWith('bearer ')) {
|
| provided = auth.slice('bearer '.length).trim();
|
| }
|
|
|
| if (provided && provided === API_KEY) return next();
|
|
|
| res.status(401).json({ error: { message: 'Unauthorized', type: 'auth_error' } });
|
| });
|
| }
|
|
|
|
|
|
|
|
|
| app.post('/v1/messages', handleMessages);
|
| app.post('/messages', handleMessages);
|
|
|
|
|
| app.post('/v1/chat/completions', handleOpenAIChatCompletions);
|
| app.post('/chat/completions', handleOpenAIChatCompletions);
|
|
|
|
|
| app.post('/v1/responses', handleOpenAIResponses);
|
| app.post('/responses', handleOpenAIResponses);
|
|
|
|
|
| app.post('/v1/messages/count_tokens', countTokens);
|
| app.post('/messages/count_tokens', countTokens);
|
|
|
|
|
| app.get('/v1/models', listModels);
|
|
|
|
|
| app.get('/health', (_req, res) => {
|
| res.json({ status: 'ok', version: VERSION });
|
| });
|
|
|
|
|
| app.get('/', (_req, res) => {
|
| res.json({
|
| name: 'cursor2api',
|
| version: VERSION,
|
| description: 'Cursor Docs AI โ Anthropic & OpenAI & Cursor IDE API Proxy',
|
| endpoints: {
|
| anthropic_messages: 'POST /v1/messages',
|
| openai_chat: 'POST /v1/chat/completions',
|
| openai_responses: 'POST /v1/responses',
|
| models: 'GET /v1/models',
|
| health: 'GET /health',
|
| },
|
| auth: API_KEY
|
| ? { required: true, headers: ['x-api-key', 'Authorization: Bearer'] }
|
| : { required: false },
|
| usage: {
|
| claude_code: 'export ANTHROPIC_BASE_URL=http://localhost:' + config.port,
|
| openai_compatible: 'OPENAI_BASE_URL=http://localhost:' + config.port + '/v1',
|
| cursor_ide: 'OPENAI_BASE_URL=http://localhost:' + config.port + '/v1 (้็จ Claude ๆจกๅ)',
|
| },
|
| });
|
| });
|
|
|
|
|
|
|
| const server = app.listen(config.port, '0.0.0.0', () => {
|
| console.log('');
|
| console.log(' โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
|
| console.log(` โ Cursor2API v${VERSION.padEnd(21)}โ`);
|
| console.log(' โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ');
|
| console.log(` โ Server: http://localhost:${config.port} โ`);
|
| console.log(' โ Model: ' + config.cursorModel.padEnd(26) + 'โ');
|
| console.log(' โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ');
|
| console.log(' โ API Endpoints: โ');
|
| console.log(' โ โข Anthropic: /v1/messages โ');
|
| console.log(' โ โข OpenAI: /v1/chat/completions โ');
|
| console.log(' โ โข Cursor: /v1/responses โ');
|
| console.log(' โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ');
|
| console.log(' โ Claude Code: โ');
|
| console.log(` โ export ANTHROPIC_BASE_URL= โ`);
|
| console.log(` โ http://localhost:${config.port} โ`);
|
| console.log(' โ OpenAI / Cursor IDE: โ');
|
| console.log(` โ OPENAI_BASE_URL= โ`);
|
| console.log(` โ http://localhost:${config.port}/v1 โ`);
|
| console.log(' โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
|
| console.log('');
|
| });
|
|
|
|
|
| server.timeout = 0;
|
| server.keepAliveTimeout = 120 * 1000;
|
| server.headersTimeout = 125 * 1000;
|
|
|