@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
package/untils.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
import psList from 'ps-list';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
8
|
+
import psTree from 'ps-tree';
|
|
9
|
+
import LogManager from "./logger-manager.js";
|
|
10
|
+
import { get } from "node:http";
|
|
11
|
+
const logger = LogManager.getSystemLogger();
|
|
12
|
+
|
|
13
|
+
function getGlobalNpmPath() {
|
|
14
|
+
try {
|
|
15
|
+
const npmRoot = execSync("npm root -g", { encoding: "utf8" }).trim();
|
|
16
|
+
logger.debug("全局模块路径:", npmRoot);
|
|
17
|
+
return npmRoot;
|
|
18
|
+
} catch (err) {
|
|
19
|
+
logger.error("获取 npm root -g 失败:", err.message);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//mcp_oauth_tokens.js
|
|
24
|
+
|
|
25
|
+
export function getMcpOauthTokensPath(){
|
|
26
|
+
let home = os.homedir();
|
|
27
|
+
return path.join(home,'.hawa-cli-analysis',"mcp_oauth_tokens.js");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getCloggerFileURL(){
|
|
31
|
+
return pathToFileURL(path.join(getGlobalNpmPath(),'hawa-cli-analysis',"clogger.js"));
|
|
32
|
+
}
|
|
33
|
+
//C:\Users\gang.ji\AppData\Roaming\npm\node_modules\@anthropic-ai\claude-code
|
|
34
|
+
export function getClaudePath(){
|
|
35
|
+
return path.join(getGlobalNpmPath(),'@anthropic-ai',"claude-code","cli.js");
|
|
36
|
+
}
|
|
37
|
+
//C:\Users\gang.ji\AppData\Roaming\npm\node_modules\@openai\codex\bin\codex.js
|
|
38
|
+
export function getCodexPath(){
|
|
39
|
+
return path.join(getGlobalNpmPath(),'@openai',"codex","bin","codex.js");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 代理 body 对象
|
|
45
|
+
* @param {*} body
|
|
46
|
+
* @returns
|
|
47
|
+
|
|
48
|
+
function proxyBody(body){
|
|
49
|
+
// 初始化 readers 的辅助函数
|
|
50
|
+
function initReaders(target) {
|
|
51
|
+
if(!target["_readers"]){
|
|
52
|
+
const [toClient, toLog] = target.tee();
|
|
53
|
+
target["_readers"] = {
|
|
54
|
+
toClient,
|
|
55
|
+
toLog
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const handler = {
|
|
60
|
+
get(target, prop, receiver) {
|
|
61
|
+
logger.debug(prop);
|
|
62
|
+
const value = Reflect.get(target, prop, receiver);
|
|
63
|
+
if(prop == "getReader"){
|
|
64
|
+
return () =>{
|
|
65
|
+
initReaders(target);
|
|
66
|
+
return target["_readers"].toClient.getReader();
|
|
67
|
+
};
|
|
68
|
+
}else if(prop == "getReaderLog"){
|
|
69
|
+
return() =>{
|
|
70
|
+
initReaders(target);
|
|
71
|
+
return target["_readers"].toLog.getReader();
|
|
72
|
+
};
|
|
73
|
+
}else if(prop == "readAllLog"){
|
|
74
|
+
return async () => {
|
|
75
|
+
//保证被初始化
|
|
76
|
+
initReaders(target);
|
|
77
|
+
let reader = target["_readers"].toLog.getReader();
|
|
78
|
+
const decoder = new TextDecoder('utf-8');
|
|
79
|
+
let buffer = '';
|
|
80
|
+
while (true) {
|
|
81
|
+
const { done, value } = await reader.read();
|
|
82
|
+
if(value){
|
|
83
|
+
buffer += decoder.decode(value, { stream: true });
|
|
84
|
+
}
|
|
85
|
+
if (done) {
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//释放锁
|
|
90
|
+
reader.releaseLock();
|
|
91
|
+
return buffer;
|
|
92
|
+
};
|
|
93
|
+
// 当前 body 自身是不会被锁的,只能锁 tee 流
|
|
94
|
+
}else if(prop == "locked"){
|
|
95
|
+
return false
|
|
96
|
+
}else if(prop ){
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
return value;
|
|
102
|
+
},
|
|
103
|
+
set(obj, prop, value) {
|
|
104
|
+
if(prop == "locked"){
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
obj[prop] = value;
|
|
108
|
+
return true; // 必须返回 true
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
return new Proxy(body, handler);
|
|
112
|
+
}
|
|
113
|
+
*/
|
|
114
|
+
/**
|
|
115
|
+
*
|
|
116
|
+
* @param {*} response
|
|
117
|
+
* @returns
|
|
118
|
+
|
|
119
|
+
// 代理 Response 请求
|
|
120
|
+
export function proxyResponse(response){
|
|
121
|
+
const target = { name: "Alice", age: 25 };
|
|
122
|
+
const handler = {
|
|
123
|
+
get(obj, prop) {
|
|
124
|
+
//console.log(`读取属�? ${prop}`);
|
|
125
|
+
if(prop == "body"){
|
|
126
|
+
// body 可能为空
|
|
127
|
+
if(!obj["body"]){
|
|
128
|
+
return ;
|
|
129
|
+
}
|
|
130
|
+
if(!obj["_body"]){
|
|
131
|
+
obj["_body"] = proxyBody(obj["body"]);
|
|
132
|
+
}
|
|
133
|
+
return obj["_body"];
|
|
134
|
+
}
|
|
135
|
+
return obj[prop];
|
|
136
|
+
},
|
|
137
|
+
set(obj, prop, value) {
|
|
138
|
+
//console.log(`设置属�? ${prop} = ${value}`);
|
|
139
|
+
obj[prop] = value;
|
|
140
|
+
return true; // 必须返回 true
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
return new Proxy(response, handler);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
*/
|
|
147
|
+
/**
|
|
148
|
+
*
|
|
149
|
+
*/
|
|
150
|
+
export function getOptions(){
|
|
151
|
+
const args = process.argv.slice(2);
|
|
152
|
+
const options = {};
|
|
153
|
+
args.forEach(arg => {
|
|
154
|
+
if (arg.startsWith('--')) {
|
|
155
|
+
const [key, value = true] = arg.slice(2).split('=');
|
|
156
|
+
options[key] = value;
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
return options;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
export function psTreeAsync(pid) {
|
|
165
|
+
return new Promise((resolve, reject) => {
|
|
166
|
+
psTree(pid, (err, children) => {
|
|
167
|
+
if (err) reject(err);
|
|
168
|
+
else resolve(children);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
global.PIPE_PATH_PRE = null;
|
|
175
|
+
/**
|
|
176
|
+
* 获取命名管道路径
|
|
177
|
+
* Unix domain socket 通信
|
|
178
|
+
* @returns
|
|
179
|
+
*/
|
|
180
|
+
export async function getPipePath(){
|
|
181
|
+
|
|
182
|
+
let PIPE_NAME ;
|
|
183
|
+
if(process.env.PIPE_PATH_PRE){
|
|
184
|
+
PIPE_NAME = process.env.PIPE_PATH_PRE + 'jsonrpc';
|
|
185
|
+
}else{
|
|
186
|
+
if(global.PIPE_PATH_PRE){
|
|
187
|
+
PIPE_NAME = global.PIPE_PATH_PRE + 'jsonrpc';
|
|
188
|
+
}else{
|
|
189
|
+
let node = await findFirstProcess(process.pid);
|
|
190
|
+
if(node){
|
|
191
|
+
PIPE_NAME = node.pid + 'jsonrpc';
|
|
192
|
+
global.PIPE_PATH_PRE = node.pid+"";
|
|
193
|
+
}else{
|
|
194
|
+
PIPE_NAME = 'jsonrpc';
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//使用第一个 node进程 id
|
|
198
|
+
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let PIPE_PATH;
|
|
202
|
+
if (process.platform === 'win32') {
|
|
203
|
+
// Windows 命名管道
|
|
204
|
+
PIPE_PATH = `\\\\.\\pipe\\${PIPE_NAME}`;
|
|
205
|
+
} else {
|
|
206
|
+
// macOS / Linux 使用 Unix 域套接字路径
|
|
207
|
+
// macOS os.tmpdir() 有时候会返回两种不同的路径
|
|
208
|
+
// /var/folders/82/0y73zsn14ls4cp6g660xb9nm0000gn/T
|
|
209
|
+
// /tmp/
|
|
210
|
+
//PIPE_PATH = path.join(os.tmpdir(), PIPE_NAME + '.sock');
|
|
211
|
+
|
|
212
|
+
//使用写死的方案
|
|
213
|
+
PIPE_PATH = path.join('/tmp', PIPE_NAME + '.sock');
|
|
214
|
+
}
|
|
215
|
+
logger.debug('Pipe path:', PIPE_PATH);
|
|
216
|
+
return PIPE_PATH;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* 获取系统的所有进程的祖先
|
|
220
|
+
* @param {} pid
|
|
221
|
+
* @returns
|
|
222
|
+
*/
|
|
223
|
+
async function getProcessAncestors(pid) {
|
|
224
|
+
const processes = await psList();
|
|
225
|
+
const map = new Map(processes.map(p => [p.pid, p])); // 建立 pid -> 进程映射
|
|
226
|
+
const mapAll = new Map();
|
|
227
|
+
const ancestors = [];
|
|
228
|
+
ancestors.push(map.get(pid));
|
|
229
|
+
let current = map.get(pid);
|
|
230
|
+
while (current && current.ppid && current.ppid !== 0) {
|
|
231
|
+
//已经存在返回,防止循环
|
|
232
|
+
if(mapAll.get(current.ppid)){
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
const parent = map.get(current.ppid);
|
|
236
|
+
logger.debug(`当前进程: ${JSON.stringify(current)}, 父进程: ${JSON.stringify(parent)}`);
|
|
237
|
+
|
|
238
|
+
if (!parent){
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
if(current.pid == parent.pid){
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
mapAll.set(parent.pid, parent);
|
|
245
|
+
ancestors.push(parent);
|
|
246
|
+
current = parent;
|
|
247
|
+
}
|
|
248
|
+
return ancestors;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function findFirstProcess(pid) {
|
|
252
|
+
let process = [...(await getProcessAncestors(pid))].reverse()
|
|
253
|
+
for(let p of process){
|
|
254
|
+
if(p.name === "node.exe" || p.name === "node"){
|
|
255
|
+
return p;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
//console.log(await findFirstProcess(process.pid));
|