@dahawa/hawa-cli-analysis 1.0.7 → 1.0.8
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 +6 -6
- package/.vscode/launch.json +26 -26
- package/_uclaude.js +0 -0
- package/anthropic-transformer.js +986 -986
- package/api-anthropic.js +279 -279
- package/api-openai.js +538 -538
- package/clogger-openai.js +190 -190
- package/clogger.js +318 -318
- package/codex/mcp-client.js +556 -556
- package/codex/mcpclient.js +117 -117
- package/codex/mcpserver.js +374 -374
- package/codex/mcpserverproxy.js +143 -143
- package/codex/test.js +30 -30
- package/mcp/claude-mcpproxy-launcher.js +4 -4
- package/package.json +2 -1
- package/simple-transform-example.js +212 -212
- package/tests/test-lazy-load.js +35 -35
- package/tests/test_mcp_proxy.js +50 -50
- package/tests/test_supabase_mcp.js +41 -41
- package/uclaude.js +3 -3
- package/ucodex-proxy.js +172 -172
- package/ucodex.js +127 -127
|
@@ -1,42 +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
|
-
|
|
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
42
|
testSupabaseMCP();
|
package/uclaude.js
CHANGED
|
@@ -184,11 +184,11 @@ function startMCPServerProxy(){
|
|
|
184
184
|
async function startMCPServerProxy() {
|
|
185
185
|
return new Promise((resolve, reject) => {
|
|
186
186
|
const dir = path.dirname(fileURLToPath(import.meta.url));
|
|
187
|
-
const child = spawn(
|
|
187
|
+
const child = spawn(process.execPath, [path.join(dir, "mcp", "claude-mcpproxy-launcher.js")], {
|
|
188
188
|
stdio: "pipe",
|
|
189
|
-
shell: true,
|
|
190
189
|
env: {
|
|
191
|
-
PIPE_PATH_PRE: process.pid
|
|
190
|
+
PIPE_PATH_PRE: process.pid,
|
|
191
|
+
...process.env // 继承当前环境变量
|
|
192
192
|
}
|
|
193
193
|
});
|
|
194
194
|
|
package/ucodex-proxy.js
CHANGED
|
@@ -1,173 +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
|
-
};
|
|
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
173
|
startServer();
|