cursor-2api / hf.js
isididiidid's picture
Update hf.js
89dc65e verified
const express = require('express');
const morgan = require('morgan');
const { createProxyMiddleware } = require('http-proxy-middleware');
const url = require('url');
const app = express();
app.use(morgan('dev'));
// 从环境变量获取代理配置
const proxyUrl = process.env.PROXY || '';
console.log(`Proxy configuration: ${proxyUrl ? '已配置' : '未配置'}`);
// 解析代理URL
let proxyConfig = null;
if (proxyUrl) {
try {
const parsedUrl = url.parse(proxyUrl);
proxyConfig = {
host: parsedUrl.hostname,
port: parsedUrl.port || 80,
auth: parsedUrl.auth ? {
username: parsedUrl.auth.split(':')[0],
password: parsedUrl.auth.split(':')[1]
} : undefined
};
// 打印代理配置(安全处理密码)
const maskedConfig = {
...proxyConfig,
auth: proxyConfig.auth ? {
username: proxyConfig.auth.username,
password: '******'
} : undefined
};
console.log('Using proxy:', JSON.stringify(maskedConfig));
} catch (error) {
console.error('Failed to parse proxy URL:', error.message);
}
}
// 添加模型列表API
app.get('/hf/v1/models', (req, res) => {
const models = {
"object": "list",
"data": [
{
"id": "claude-3.5-sonnet",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "gpt-4",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "gpt-4o",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "claude-3-opus",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "gpt-3.5-turbo",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "gpt-4-turbo-2024-04-09",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "gpt-4o-128k",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "gemini-1.5-flash-500k",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "claude-3-haiku-200k",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "claude-3-5-sonnet-200k",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "claude-3-5-sonnet-20241022",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "gpt-4o-mini",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "o1-mini",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "o1-preview",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "o1",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "claude-3.5-haiku",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "gemini-exp-1206",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "gemini-2.0-flash-thinking-exp",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "gemini-2.0-flash-exp",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "deepseek-v3",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "deepseek-r1",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
// 新增模型
{
"id": "claude-3.7-sonnet",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
},
{
"id": "claude-3.7-sonnet-thinking",
"object": "model",
"created": 1706745938,
"owned_by": "cursor"
}
]
};
res.json(models);
});
// 配置代理中间件
app.use('/hf/v1/chat/completions', createProxyMiddleware({
target: 'http://localhost:3010/v1/chat/completions',
changeOrigin: true,
// 添加代理配置
proxy: proxyConfig,
// 增加错误处理
onError: (err, req, res) => {
console.error('Proxy error:', err);
res.status(500).send('Proxy error occurred: ' + err.message);
},
onProxyReq: (proxyReq, req, res) => {
console.log(`Proxying request to chat completions ${proxyConfig ? 'using proxy' : 'directly'}`);
},
onProxyRes: (proxyRes, req, res) => {
console.log(`Received response with status: ${proxyRes.statusCode}`);
}
}));
app.get('/', (req, res) => {
const htmlContent = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cursor To OpenAI</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
line-height: 1.6;
}
.container {
background: #f9f9f9;
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.info-item {
margin-bottom: 10px;
}
.status {
background: ${proxyConfig ? '#e1f5e1' : '#fff3cd'};
padding: 10px;
border-radius: 5px;
margin-top: 20px;
border: 1px solid ${proxyConfig ? '#c3e6cb' : '#ffeeba'};
}
.models-container {
margin-top: 20px;
border-top: 1px solid #eee;
padding-top: 20px;
}
.model-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 10px;
margin-top: 15px;
}
.model-item {
background: #f0f0f0;
padding: 8px 12px;
border-radius: 4px;
font-size: 0.9em;
}
</style>
</head>
<body>
<div class="container">
<h1>Cursor To OpenAI Server</h1>
<div class="info-item">
<strong>聊天来源:</strong> 自定义(兼容 OpenAI)
</div>
<div class="info-item">
<strong>自定义端点(基本URL):</strong><span id="endpoint-url"></span>
</div>
<div class="info-item">
<strong>自定义API密钥:</strong>[抓取的Cursor Cookie,格式为user_...]
</div>
<div class="status">
<strong>代理状态:</strong> ${proxyConfig ? '已启用' : '未启用'}
${proxyConfig ? `<p>代理服务器: ${proxyConfig.host}:${proxyConfig.port}</p>` : ''}
</div>
<div class="models-container">
<h3>支持的模型</h3>
<div id="model-list" class="model-list">加载中...</div>
</div>
</div>
<script>
const url = new URL(window.location.href);
const link = url.protocol + '//' + url.host + '/hf/v1';
document.getElementById('endpoint-url').textContent = link;
// 加载模型列表
fetch('/hf/v1/models')
.then(response => response.json())
.then(data => {
const modelListEl = document.getElementById('model-list');
modelListEl.innerHTML = '';
data.data.forEach(model => {
const modelEl = document.createElement('div');
modelEl.className = 'model-item';
modelEl.textContent = model.id;
modelListEl.appendChild(modelEl);
});
})
.catch(err => {
document.getElementById('model-list').textContent = '加载模型失败: ' + err.message;
});
</script>
</body>
</html>
`;
res.send(htmlContent);
});
const port = process.env.HF_PORT || 7860;
app.listen(port, () => {
console.log(`HF Proxy server is running at PORT: ${port}`);
console.log(`Proxy status: ${proxyConfig ? 'Enabled' : 'Disabled'}`);
});