@dahawa/hawa-cli-analysis 1.0.4
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/.tools.json +7 -0
- package/.vscode/launch.json +27 -0
- package/LICENSE +21 -0
- package/README.md +196 -0
- package/_uclaude.js +165 -0
- package/anthropic-transformer.js +986 -0
- package/api-anthropic.js +279 -0
- package/api-openai.js +539 -0
- package/claude/claude-openai-proxy.js +305 -0
- package/claude/claude-proxy.js +341 -0
- package/clogger-openai.js +190 -0
- package/clogger.js +318 -0
- package/codex/mcp-client.js +556 -0
- package/codex/mcpclient.js +118 -0
- package/codex/mcpserver.js +374 -0
- package/codex/mcpserverproxy.js +144 -0
- package/codex/test.js +30 -0
- package/config.js +105 -0
- package/index.js +0 -0
- package/logger-manager.js +85 -0
- package/logger.js +112 -0
- package/mcp/claude-mcpproxy-launcher.js +5 -0
- package/mcp_oauth_tokens.js +40 -0
- package/package.json +36 -0
- package/port-manager.js +80 -0
- package/simple-transform-example.js +213 -0
- package/tests/test-lazy-load.js +36 -0
- package/tests/test.js +30 -0
- package/tests/test_mcp_proxy.js +51 -0
- package/tests/test_supabase_mcp.js +42 -0
- package/uclaude.js +221 -0
- package/ucodex-proxy.js +173 -0
- package/ucodex.js +129 -0
- package/untils.js +261 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
3
|
+
import LogManager from '../logger-manager.js';
|
|
4
|
+
|
|
5
|
+
const logger = LogManager.getSystemLogger();
|
|
6
|
+
|
|
7
|
+
async function testSupabaseMCP() {
|
|
8
|
+
try {
|
|
9
|
+
logger.debug('Testing Supabase MCP connection...');
|
|
10
|
+
|
|
11
|
+
const client = new Client({ name: 'supabase-test', version: '1.0.0' });
|
|
12
|
+
const transport = new StreamableHTTPClientTransport(new URL('https://mcp.supabase.com/mcp'), {
|
|
13
|
+
requestInit: {
|
|
14
|
+
headers: {
|
|
15
|
+
Authorization: `Bearer sbp_75d326e31e6cc3d152fc1b4132755cf79e21f434`
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
logger.debug('Connecting to Supabase MCP...');
|
|
21
|
+
await client.connect(transport);
|
|
22
|
+
logger.debug('✅ Connected successfully');
|
|
23
|
+
|
|
24
|
+
logger.debug('Listing tools...');
|
|
25
|
+
const tools = await client.listTools();
|
|
26
|
+
logger.debug('Raw tools response:', JSON.stringify(tools, null, 2));
|
|
27
|
+
|
|
28
|
+
if (tools && tools.tools) {
|
|
29
|
+
logger.debug(`Found ${tools.tools.length} tools:`);
|
|
30
|
+
tools.tools.forEach((tool, index) => {
|
|
31
|
+
logger.debug(`${index + 1}. ${tool.name}: ${tool.description || 'No description'}`);
|
|
32
|
+
});
|
|
33
|
+
} else {
|
|
34
|
+
logger.debug('No tools found or unexpected response format');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
} catch (error) {
|
|
38
|
+
logger.error('Error testing Supabase MCP:', error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
testSupabaseMCP();
|
package/uclaude.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
global.CLI_TYPE = "claude"
|
|
4
|
+
import {initConfig,loadConfig} from "./config.js"
|
|
5
|
+
import readline from 'readline';
|
|
6
|
+
import { spawn } from 'child_process';
|
|
7
|
+
import {getClaudePath} from './untils.js';
|
|
8
|
+
import inquirer from 'inquirer';
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
11
|
+
import LogManager from './logger-manager.js';
|
|
12
|
+
const logger = LogManager.getSystemLogger();
|
|
13
|
+
import portManager from './port-manager.js';
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const startServer = async (openai, base_url, port = null) => {
|
|
18
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const child = spawn('node ' + path.join(dir, "claude" , openai?"claude-openai-proxy.js":'claude-proxy.js'), [],{
|
|
20
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
21
|
+
shell: true,
|
|
22
|
+
env:{
|
|
23
|
+
...process.env,
|
|
24
|
+
BASE_URL: base_url,
|
|
25
|
+
PROXY_PORT: port
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// 监听标准输出
|
|
30
|
+
child.stdout.on('data', (data) => {
|
|
31
|
+
//console.log(`子进程 stdout: ${data}`);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// 监听错误输出
|
|
35
|
+
child.stderr.on('data', (data) => {
|
|
36
|
+
logger.error(`子进程 stderr: ${data}`);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
child.on('close', (code) => {
|
|
40
|
+
logger.debug(`codex 退出,退出码: ${code}`);
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 启动 calude code
|
|
47
|
+
*/
|
|
48
|
+
function start(){
|
|
49
|
+
initConfig();
|
|
50
|
+
let allConfig = loadConfig();
|
|
51
|
+
let choices = [];
|
|
52
|
+
Object.entries(allConfig).forEach(([key, value], index) => {
|
|
53
|
+
if (value.enable === true) {
|
|
54
|
+
choices.push({ name: `${index}. ${key}`, value: key });
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// 检查是否有启用的模型
|
|
59
|
+
if (choices.length === 0) {
|
|
60
|
+
console.error("错误:没有启用的模型配置!");
|
|
61
|
+
console.log("请检查配置文件,确保至少有一个模型的 enable 设置为 true。");
|
|
62
|
+
logger.error("没有启用的模型配置,程序退出");
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
(async () => {
|
|
67
|
+
const answers = await inquirer.prompt([
|
|
68
|
+
{
|
|
69
|
+
type: "list", // 单选模式
|
|
70
|
+
name: "choice", // 返回结果的 key
|
|
71
|
+
message: "请选择一个模型:",
|
|
72
|
+
choices: choices
|
|
73
|
+
}
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
var config = allConfig[answers.choice];
|
|
77
|
+
let env = config.env;
|
|
78
|
+
// 添加 ANTHROPIC_ 前缀到环境变量
|
|
79
|
+
let anthropicEnv = {};
|
|
80
|
+
Object.keys(env).forEach(key => {
|
|
81
|
+
if (['BASE_URL', 'AUTH_TOKEN', 'MODEL', 'SMALL_FAST_MODEL'].includes(key)) {
|
|
82
|
+
anthropicEnv[`ANTHROPIC_${key}`] = env[key];
|
|
83
|
+
} else {
|
|
84
|
+
anthropicEnv[key] = env[key];
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
anthropicEnv[`ANTHROPIC_API_KEY`] = env["ANTHROPIC_AUTH_TOKEN"];
|
|
89
|
+
let BASE_URL = anthropicEnv[`ANTHROPIC_BASE_URL`];
|
|
90
|
+
|
|
91
|
+
// claudecode 环境变量是可以通过 env 传递到 mcpserver
|
|
92
|
+
let claudePath = config?.CLAUDE_PATH || process.env.CLAUDE_PATH || getClaudePath();
|
|
93
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
94
|
+
|
|
95
|
+
// 动态分配端口
|
|
96
|
+
let proxyPort;
|
|
97
|
+
if(answers.choice=="openrouter"){
|
|
98
|
+
//启动 claude-openai-proxy.js 代理
|
|
99
|
+
proxyPort = await portManager.getAvailablePort();
|
|
100
|
+
if (!proxyPort) {
|
|
101
|
+
console.error("错误:无法获取可用端口!");
|
|
102
|
+
logger.error("无法获取可用端口,程序退出");
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
startServer(true, BASE_URL, proxyPort);
|
|
106
|
+
}else{
|
|
107
|
+
//启动 claude-proxy.js 代理
|
|
108
|
+
proxyPort = await portManager.getAvailablePort();
|
|
109
|
+
if (!proxyPort) {
|
|
110
|
+
console.error("错误:无法获取可用端口!");
|
|
111
|
+
logger.error("无法获取可用端口,程序退出");
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
startServer(false, BASE_URL, proxyPort);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 设置代理地址
|
|
118
|
+
anthropicEnv[`ANTHROPIC_BASE_URL`] = `http://127.0.0.1:${proxyPort}`;
|
|
119
|
+
|
|
120
|
+
claudePath = "node " + claudePath;
|
|
121
|
+
|
|
122
|
+
logger.debug(`启动 Claude 进程: ${claudePath}`);
|
|
123
|
+
|
|
124
|
+
const child = spawn(claudePath,[],{
|
|
125
|
+
env:{
|
|
126
|
+
...anthropicEnv,
|
|
127
|
+
PIPE_PATH_PRE: process.pid
|
|
128
|
+
},
|
|
129
|
+
stdio: 'inherit', // 继承父进程 stdio,方便交互,
|
|
130
|
+
shell: true
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
child.on("error", (error) => {
|
|
135
|
+
console.error("Failed to start claude command:", error.message);
|
|
136
|
+
logger.debug(
|
|
137
|
+
"Make sure Claude Code is installed: npm install -g @anthropic-ai/claude-code"
|
|
138
|
+
);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
child.on("close", (code) => {
|
|
143
|
+
process.exit(code || 0);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
})();
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
}
|
|
152
|
+
// 这里需要判断是否启动完成,不然后面 mcp 会连接失败
|
|
153
|
+
/**
|
|
154
|
+
function startMCPServerProxy(){
|
|
155
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
156
|
+
// 启动 MCP 代理服务
|
|
157
|
+
const child = spawn("node " + (path.join(dir, "mcp" ,'claude-mcpproxy-launcher.js')), [], {
|
|
158
|
+
stdio: "pipe" ,
|
|
159
|
+
shell: true,
|
|
160
|
+
env: {
|
|
161
|
+
PIPE_PATH_PRE: process.pid
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
child.stdout.on("data", (data) => {
|
|
166
|
+
console.log("子进程输出:", data.toString());
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
child.on("error", (error) => {
|
|
170
|
+
console.error("Failed to start MCP server proxy:", error.message);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
child.on("close", (code) => {
|
|
175
|
+
process.exit(code || 0);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
*/
|
|
179
|
+
|
|
180
|
+
async function startMCPServerProxy() {
|
|
181
|
+
return new Promise((resolve, reject) => {
|
|
182
|
+
const dir = path.dirname(fileURLToPath(import.meta.url));
|
|
183
|
+
const child = spawn("node " + path.join(dir, "mcp", "claude-mcpproxy-launcher.js"), [], {
|
|
184
|
+
stdio: "pipe",
|
|
185
|
+
shell: true,
|
|
186
|
+
env: {
|
|
187
|
+
PIPE_PATH_PRE: process.pid
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
child.stdout.on("data", (data) => {
|
|
192
|
+
const msg = data.toString().trim();
|
|
193
|
+
//console.log("子进程输出:", msg);
|
|
194
|
+
if (msg.includes("ok_ok")) {
|
|
195
|
+
resolve("MCP server proxy started successfully");
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
child.stderr.on("data", (data) => {
|
|
200
|
+
console.error("子进程错误输出:", data.toString());
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
child.on("error", (error) => {
|
|
204
|
+
reject(new Error("Failed to start MCP server proxy: " + error.message));
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
child.on("close", (code) => {
|
|
208
|
+
if (code !== 0) {
|
|
209
|
+
reject(new Error("MCP server proxy exited with code " + code));
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function main(){
|
|
216
|
+
console.log("Starting MCP server proxy...");
|
|
217
|
+
await startMCPServerProxy();
|
|
218
|
+
console.log("MCP server proxy started successfully.");
|
|
219
|
+
start();
|
|
220
|
+
}
|
|
221
|
+
await main();
|
package/ucodex-proxy.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// server.js
|
|
2
|
+
import Fastify from "fastify";
|
|
3
|
+
import {Readable } from 'node:stream';
|
|
4
|
+
import {parseOpenAIResponse,parseOpenAIChatCompletion} from './api-openai.js'
|
|
5
|
+
import LoggerManage from "./logger-manager.js"
|
|
6
|
+
let logger = LoggerManage.getLogger("codex");
|
|
7
|
+
|
|
8
|
+
//是否为 chat 模式调用
|
|
9
|
+
const wire_api = process.env.wire_api;
|
|
10
|
+
//访问的 Base URL 地址
|
|
11
|
+
const base_url = process.env.base_url;
|
|
12
|
+
|
|
13
|
+
logger.system.debug("Base URL:" + base_url);
|
|
14
|
+
logger.system.debug("Wire API:" + wire_api);
|
|
15
|
+
|
|
16
|
+
logger.system.debug(process.env);
|
|
17
|
+
|
|
18
|
+
function toSimple(full , wire_api){
|
|
19
|
+
let log = {
|
|
20
|
+
request:{},
|
|
21
|
+
response:{}
|
|
22
|
+
}
|
|
23
|
+
if(wire_api === "chat"){
|
|
24
|
+
log.request.model = full.request.body.model;
|
|
25
|
+
log.request.messages = full.request.body.messages;
|
|
26
|
+
log.response.choices = full.response.body.choices;
|
|
27
|
+
}else{
|
|
28
|
+
log.request.session_id = full.request.headers.session_id;
|
|
29
|
+
log.request.model = full.request.body.model;
|
|
30
|
+
log.request.instructions = full.request.body.instructions;
|
|
31
|
+
log.request.input = full.request.body.input;
|
|
32
|
+
log.response.output = full.response.body.output;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return log;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function logAPI(fullLog,wire_api){
|
|
39
|
+
logger.simple.debug(toSimple(fullLog,wire_api));
|
|
40
|
+
logger.full.debug(fullLog);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const fastify = Fastify(
|
|
44
|
+
{
|
|
45
|
+
requestTimeout: 0, // never time out request (or set e.g. 10 * 60 * 1000)
|
|
46
|
+
connectionTimeout: 0, // no connection timeout
|
|
47
|
+
keepAliveTimeout: 120000, // 120s
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// 注册一个 POST 接口
|
|
52
|
+
fastify.all("/*", async (request, reply) => {
|
|
53
|
+
//console.log("处理请求:", request.url);
|
|
54
|
+
return await handel(request, reply, request.url);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 判断是否为流式响应
|
|
59
|
+
* @returns
|
|
60
|
+
*/
|
|
61
|
+
function isStream(response){
|
|
62
|
+
let contentType = response.headers.get('content-type') || '';
|
|
63
|
+
const streamTypes = [
|
|
64
|
+
'text/event-stream',
|
|
65
|
+
'chunked'
|
|
66
|
+
];
|
|
67
|
+
if(streamTypes.some(t => contentType.includes(t))){
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
contentType = response.headers.get('transfer-encoding') || '';
|
|
71
|
+
if(streamTypes.some(t => contentType.includes(t))){
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function headersToObject(headers) {
|
|
78
|
+
const obj = {};
|
|
79
|
+
try {
|
|
80
|
+
for (const [k, v] of headers.entries()) obj[k] = v;
|
|
81
|
+
} catch {}
|
|
82
|
+
return obj;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function joinUrl(base, ...paths) {
|
|
86
|
+
return [base.replace(/\/+$/, ''), ...paths.map(p => p.replace(/^\/+/, ''))]
|
|
87
|
+
.join('/');
|
|
88
|
+
}
|
|
89
|
+
async function handel(request, reply, endpoint){
|
|
90
|
+
try {
|
|
91
|
+
const body = request.body;
|
|
92
|
+
// 取出原始请求头
|
|
93
|
+
let incomingHeaders = { ...request.headers };
|
|
94
|
+
|
|
95
|
+
// 删除或覆盖一些不适合直接转发的头
|
|
96
|
+
delete incomingHeaders["host"]; // Host 由 fetch 自动设置
|
|
97
|
+
delete incomingHeaders["content-length"]; // 长度 fetch 会重新计算
|
|
98
|
+
delete incomingHeaders["accept"];
|
|
99
|
+
|
|
100
|
+
let url = joinUrl(base_url,endpoint);
|
|
101
|
+
logger.system.debug("向endpoint 发送请求:" + url);
|
|
102
|
+
|
|
103
|
+
let response = await fetch(url, {
|
|
104
|
+
method: "POST",
|
|
105
|
+
//headers: incomingHeaders,
|
|
106
|
+
//使用白名单机制,mac 有问题
|
|
107
|
+
headers:{
|
|
108
|
+
'authorization': incomingHeaders['authorization'],
|
|
109
|
+
'content-type': incomingHeaders['content-type']
|
|
110
|
+
},
|
|
111
|
+
body: JSON.stringify(body)
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
let responseToClient = response.clone();
|
|
115
|
+
|
|
116
|
+
//完整的请求日志,保护请求和响应
|
|
117
|
+
let fullLog = {request:{
|
|
118
|
+
headers: incomingHeaders,
|
|
119
|
+
body: body
|
|
120
|
+
},response:{
|
|
121
|
+
status: response.status,
|
|
122
|
+
statusText: response.statusText,
|
|
123
|
+
headers: headersToObject(response.headers)
|
|
124
|
+
}};
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
// 同时在后台解析日志(不影响直通)
|
|
128
|
+
(async () => {
|
|
129
|
+
if(wire_api == "chat"){
|
|
130
|
+
fullLog.response.body = await parseOpenAIChatCompletion(await response.text());
|
|
131
|
+
}else if(wire_api == "responses"){
|
|
132
|
+
fullLog.response.body = await parseOpenAIResponse(await response.text());
|
|
133
|
+
}
|
|
134
|
+
//其他类型是错误的
|
|
135
|
+
logAPI(fullLog,wire_api);
|
|
136
|
+
|
|
137
|
+
})().catch(err => console.error('日志解析错误:', err));
|
|
138
|
+
|
|
139
|
+
const HOP_BY_HOP = new Set([
|
|
140
|
+
'connection', 'keep-alive', 'proxy-connection', 'transfer-encoding',
|
|
141
|
+
'te', 'trailer', 'upgrade', 'proxy-authenticate', 'proxy-authorization', 'expect'
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
reply.code(response.status);
|
|
145
|
+
response.headers.forEach((value, name) => {
|
|
146
|
+
const lower = name.toLowerCase();
|
|
147
|
+
if (HOP_BY_HOP.has(lower)) return;
|
|
148
|
+
reply.header(name, value);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (response.body) {
|
|
152
|
+
const nodeStream = Readable.fromWeb(responseToClient.body);
|
|
153
|
+
return reply.send(nodeStream);
|
|
154
|
+
} else {
|
|
155
|
+
return reply.send();
|
|
156
|
+
}
|
|
157
|
+
} catch (err) {
|
|
158
|
+
console.error("处理流式响应异常",err);
|
|
159
|
+
return reply.status(500).send({ error: "请求失败" });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 启动服务
|
|
164
|
+
const startServer = async () => {
|
|
165
|
+
try {
|
|
166
|
+
await fastify.listen({ port: 3000, host: "0.0.0.0" });
|
|
167
|
+
logger.system.debug("✅ Server started");
|
|
168
|
+
} catch (err) {
|
|
169
|
+
fastify.log.error(err);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
startServer();
|
package/ucodex.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import TOML from '@iarna/toml';
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import { spawn } from 'child_process';
|
|
9
|
+
import {getCodexPath} from './untils.js';
|
|
10
|
+
import {startMCPServerProxy} from "./codex/mcpserver.js"
|
|
11
|
+
import LogManager from "./logger-manager.js";
|
|
12
|
+
const logger = LogManager.getSystemLogger();
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* codex 是 rust 开发,只能使用代理模式进行日志获取
|
|
17
|
+
*/
|
|
18
|
+
const startServer = async () => {
|
|
19
|
+
let tomlPath = path.join(os.homedir(), ".codex", "config.toml");
|
|
20
|
+
const tomlString = fs.readFileSync(tomlPath, 'utf-8');
|
|
21
|
+
const config = TOML.parse(tomlString);
|
|
22
|
+
let env = {};
|
|
23
|
+
//是否配置了三方模型
|
|
24
|
+
if(config.model_provider){
|
|
25
|
+
let base_url = config["model_providers"][config.model_provider]["base_url"];
|
|
26
|
+
let wire_api = config["model_providers"][config.model_provider]["wire_api"]? config["model_providers"][config.model_provider]["wire_api"]:"chat"
|
|
27
|
+
env = {
|
|
28
|
+
base_url,
|
|
29
|
+
wire_api
|
|
30
|
+
}
|
|
31
|
+
}else{
|
|
32
|
+
env = {
|
|
33
|
+
//默认访问只能是 这个地址
|
|
34
|
+
base_url:"https://chatgpt.com/backend-api/codex",
|
|
35
|
+
wire_api:"responses"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
40
|
+
const child = spawn('node ' + path.join(dir, 'ucodex-proxy.js'), [],{
|
|
41
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
42
|
+
shell: true,
|
|
43
|
+
env:{
|
|
44
|
+
...process.env,
|
|
45
|
+
...env
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// 监听标准输出
|
|
50
|
+
child.stdout.on('data', (data) => {
|
|
51
|
+
//console.log(`子进程 stdout: ${data}`);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// 监听错误输出
|
|
55
|
+
child.stderr.on('data', (data) => {
|
|
56
|
+
logger.error(`子进程 stderr: ${data}`);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
child.on('close', (code) => {
|
|
60
|
+
logger.debug(`codex 退出,退出码: ${code}`);
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* 启动 codex cli
|
|
65
|
+
**/
|
|
66
|
+
function startCodexcli(){
|
|
67
|
+
// 设置 base url 的方式
|
|
68
|
+
let env = {};
|
|
69
|
+
let configCmd = [];
|
|
70
|
+
|
|
71
|
+
let tomlPath = path.join(os.homedir(), ".codex", "config.toml");
|
|
72
|
+
const tomlString = fs.readFileSync(tomlPath, 'utf-8');
|
|
73
|
+
const config = TOML.parse(tomlString);
|
|
74
|
+
//是否配置了三方模型
|
|
75
|
+
if(config.model_provider){
|
|
76
|
+
let base_url = config["model_providers"][config.model_provider]["base_url"];
|
|
77
|
+
let wire_api = config["model_providers"][config.model_provider]["wire_api"]? config["model_providers"][config.model_provider]["wire_api"]:"chat"
|
|
78
|
+
configCmd = ["--config", "model_providers." + config.model_provider+".base_url="+"http://127.0.0.1:3000"];
|
|
79
|
+
|
|
80
|
+
}else{
|
|
81
|
+
env = {
|
|
82
|
+
"OPENAI_BASE_URL":"http://127.0.0.1:3000",
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const child = spawn("node "+getCodexPath(), configCmd,{
|
|
87
|
+
stdio: 'inherit', // 继承父进程 stdio,方便交互
|
|
88
|
+
shell: true,
|
|
89
|
+
env:{
|
|
90
|
+
...process.env,
|
|
91
|
+
...env
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
child.on('close', (code) => {
|
|
97
|
+
logger.debug(`codex 退出,退出码: ${code}`);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
function startMCPServerProxy(){
|
|
103
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
104
|
+
// 启动 MCP 代理服务
|
|
105
|
+
const child = spawn("node " + (path.join(dir, "mcp" ,'claude-mcpproxy-launcher.js')), [], {
|
|
106
|
+
stdio: 'inherit',
|
|
107
|
+
shell: true,
|
|
108
|
+
env: {
|
|
109
|
+
// PIPE_PATH_PRE: process.pid
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
child.on("error", (error) => {
|
|
114
|
+
console.error("Failed to start MCP server proxy:", error.message);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
child.on("close", (code) => {
|
|
119
|
+
process.exit(code || 0);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
*/
|
|
123
|
+
|
|
124
|
+
function main(){
|
|
125
|
+
startServer();
|
|
126
|
+
startMCPServerProxy()
|
|
127
|
+
startCodexcli();
|
|
128
|
+
}
|
|
129
|
+
main();
|