yunyi-activator 1.0.0 → 1.1.0
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/dist/index.js +76 -275
- package/package.json +5 -2
- package/dist/api.js +0 -75
- package/dist/claude-injector.js +0 -227
- package/dist/config.js +0 -79
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yunyi-activator",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Yunyi API Activator for Claude Code & Codex",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"yunyi": "dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"build": "
|
|
10
|
+
"build": "esbuild src/index.ts --bundle --platform=node --target=node16 --minify --outfile=dist/index.js",
|
|
11
|
+
"build:slim": "esbuild src/index.ts --bundle --platform=node --target=node16 --minify --outfile=dist/index.js --external:chalk --external:inquirer --external:ora",
|
|
12
|
+
"build:dev": "tsc",
|
|
11
13
|
"start": "node dist/index.js",
|
|
12
14
|
"prepublishOnly": "npm run build"
|
|
13
15
|
},
|
|
@@ -34,6 +36,7 @@
|
|
|
34
36
|
"devDependencies": {
|
|
35
37
|
"@types/inquirer": "^9.0.7",
|
|
36
38
|
"@types/node": "^20.10.0",
|
|
39
|
+
"esbuild": "^0.24.0",
|
|
37
40
|
"typescript": "^5.3.3"
|
|
38
41
|
}
|
|
39
42
|
}
|
package/dist/api.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.validateApiKey = validateApiKey;
|
|
7
|
-
exports.getServiceEndpoint = getServiceEndpoint;
|
|
8
|
-
const https_1 = __importDefault(require("https"));
|
|
9
|
-
const http_1 = __importDefault(require("http"));
|
|
10
|
-
const url_1 = require("url");
|
|
11
|
-
// 发起 HTTP 请求
|
|
12
|
-
function request(url, options) {
|
|
13
|
-
return new Promise((resolve, reject) => {
|
|
14
|
-
const parsedUrl = new url_1.URL(url);
|
|
15
|
-
const isHttps = parsedUrl.protocol === 'https:';
|
|
16
|
-
const lib = isHttps ? https_1.default : http_1.default;
|
|
17
|
-
const reqOptions = {
|
|
18
|
-
hostname: parsedUrl.hostname,
|
|
19
|
-
port: parsedUrl.port || (isHttps ? 443 : 80),
|
|
20
|
-
path: parsedUrl.pathname + parsedUrl.search,
|
|
21
|
-
method: options.method || 'GET',
|
|
22
|
-
headers: options.headers || {},
|
|
23
|
-
};
|
|
24
|
-
const req = lib.request(reqOptions, (res) => {
|
|
25
|
-
let data = '';
|
|
26
|
-
res.on('data', (chunk) => {
|
|
27
|
-
data += chunk;
|
|
28
|
-
});
|
|
29
|
-
res.on('end', () => {
|
|
30
|
-
try {
|
|
31
|
-
const json = JSON.parse(data);
|
|
32
|
-
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
33
|
-
resolve(json);
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
reject(new Error(json.message || json.error || `HTTP ${res.statusCode}`));
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
reject(new Error(`Invalid JSON response: ${data.substring(0, 100)}`));
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
req.on('error', (err) => {
|
|
45
|
-
reject(err);
|
|
46
|
-
});
|
|
47
|
-
req.setTimeout(10000, () => {
|
|
48
|
-
req.destroy();
|
|
49
|
-
reject(new Error('Request timeout'));
|
|
50
|
-
});
|
|
51
|
-
req.end();
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
// 验证 API Key 并获取信息
|
|
55
|
-
async function validateApiKey(serverUrl, apiKey) {
|
|
56
|
-
const url = `${serverUrl}/user/api/v1/me`;
|
|
57
|
-
return request(url, {
|
|
58
|
-
method: 'GET',
|
|
59
|
-
headers: {
|
|
60
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
61
|
-
'Content-Type': 'application/json',
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
// 获取服务端点 URL
|
|
66
|
-
function getServiceEndpoint(serverUrl, serviceType) {
|
|
67
|
-
// 移除末尾斜杠
|
|
68
|
-
const baseUrl = serverUrl.replace(/\/+$/, '');
|
|
69
|
-
if (serviceType === 'claude') {
|
|
70
|
-
return `${baseUrl}/claude`;
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
return `${baseUrl}/codex`;
|
|
74
|
-
}
|
|
75
|
-
}
|
package/dist/claude-injector.js
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getClaudeCommandPath = getClaudeCommandPath;
|
|
7
|
-
exports.resolveSymlink = resolveSymlink;
|
|
8
|
-
exports.getClaudeCliJsPath = getClaudeCliJsPath;
|
|
9
|
-
exports.isInjected = isInjected;
|
|
10
|
-
exports.removeOldInjection = removeOldInjection;
|
|
11
|
-
exports.injectCode = injectCode;
|
|
12
|
-
exports.injectClaudeCli = injectClaudeCli;
|
|
13
|
-
const child_process_1 = require("child_process");
|
|
14
|
-
const fs_1 = __importDefault(require("fs"));
|
|
15
|
-
const path_1 = __importDefault(require("path"));
|
|
16
|
-
const os_1 = __importDefault(require("os"));
|
|
17
|
-
// 注入标记
|
|
18
|
-
const MARKER_START = '// YUNYI_INJECTED_START';
|
|
19
|
-
const MARKER_END = '// YUNYI_INJECTED_END';
|
|
20
|
-
/**
|
|
21
|
-
* 获取 claude 命令路径
|
|
22
|
-
* @returns claude 命令的完整路径,未找到返回 null
|
|
23
|
-
*/
|
|
24
|
-
function getClaudeCommandPath() {
|
|
25
|
-
try {
|
|
26
|
-
const isWindows = os_1.default.platform() === 'win32';
|
|
27
|
-
const cmd = isWindows ? 'where claude' : 'which claude';
|
|
28
|
-
const result = (0, child_process_1.execSync)(cmd, { encoding: 'utf-8' }).trim();
|
|
29
|
-
// Windows 的 where 可能返回多行,取第一个
|
|
30
|
-
const firstLine = result.split('\n')[0].trim();
|
|
31
|
-
return firstLine || null;
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* 解析符号链接获取真实路径
|
|
39
|
-
* @param linkPath 符号链接路径
|
|
40
|
-
* @returns 真实文件路径
|
|
41
|
-
*/
|
|
42
|
-
function resolveSymlink(linkPath) {
|
|
43
|
-
try {
|
|
44
|
-
// 使用 fs.realpathSync 解析符号链接
|
|
45
|
-
return fs_1.default.realpathSync(linkPath);
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return linkPath;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* 从 Windows .cmd 文件中解析 Node.js 脚本路径
|
|
53
|
-
* @param cmdPath .cmd 文件路径
|
|
54
|
-
* @returns cli.js 文件路径,未找到返回 null
|
|
55
|
-
*/
|
|
56
|
-
function parseWindowsCmdFile(cmdPath) {
|
|
57
|
-
try {
|
|
58
|
-
const content = fs_1.default.readFileSync(cmdPath, 'utf-8');
|
|
59
|
-
// Windows cmd 文件通常包含类似这样的内容:
|
|
60
|
-
// @"%~dp0\node_modules\@anthropic-ai\claude-code\cli.js" %*
|
|
61
|
-
// 或者
|
|
62
|
-
// @node "%~dp0\node_modules\@anthropic-ai\claude-code\cli.js" %*
|
|
63
|
-
// 尝试提取 cli.js 路径
|
|
64
|
-
const match = content.match(/@anthropic-ai[/\\]claude-code[/\\]cli\.js/i);
|
|
65
|
-
if (match) {
|
|
66
|
-
// 从 cmd 文件目录构建完整路径
|
|
67
|
-
const cmdDir = path_1.default.dirname(cmdPath);
|
|
68
|
-
return path_1.default.join(cmdDir, 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js');
|
|
69
|
-
}
|
|
70
|
-
// 另一种模式:直接在 npm 全局目录
|
|
71
|
-
const npmDir = path_1.default.dirname(cmdPath);
|
|
72
|
-
const possiblePath = path_1.default.join(npmDir, 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js');
|
|
73
|
-
if (fs_1.default.existsSync(possiblePath)) {
|
|
74
|
-
return possiblePath;
|
|
75
|
-
}
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* 获取 cli.js 文件路径
|
|
84
|
-
* @returns cli.js 完整路径,未找到返回 null
|
|
85
|
-
*/
|
|
86
|
-
function getClaudeCliJsPath() {
|
|
87
|
-
const commandPath = getClaudeCommandPath();
|
|
88
|
-
if (!commandPath) {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
const isWindows = os_1.default.platform() === 'win32';
|
|
92
|
-
if (isWindows) {
|
|
93
|
-
// Windows: claude.cmd 文件需要特殊处理
|
|
94
|
-
if (commandPath.endsWith('.cmd')) {
|
|
95
|
-
return parseWindowsCmdFile(commandPath);
|
|
96
|
-
}
|
|
97
|
-
// 如果是直接的 js 文件
|
|
98
|
-
if (commandPath.endsWith('.js')) {
|
|
99
|
-
return commandPath;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
// macOS/Linux: 解析符号链接
|
|
103
|
-
const realPath = resolveSymlink(commandPath);
|
|
104
|
-
// 如果解析后就是 cli.js,直接返回
|
|
105
|
-
if (realPath.endsWith('cli.js')) {
|
|
106
|
-
return realPath;
|
|
107
|
-
}
|
|
108
|
-
// 尝试从 bin 目录推断 node_modules 位置
|
|
109
|
-
// /usr/local/bin/claude -> /usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js
|
|
110
|
-
const possiblePaths = [
|
|
111
|
-
// 从 bin 目录向上查找 lib/node_modules
|
|
112
|
-
path_1.default.join(path_1.default.dirname(realPath), '..', 'lib', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
113
|
-
// 直接在同级 node_modules
|
|
114
|
-
path_1.default.join(path_1.default.dirname(realPath), 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
115
|
-
// npm 全局目录
|
|
116
|
-
path_1.default.join(os_1.default.homedir(), '.npm-global', 'lib', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
117
|
-
// nvm 目录 (常见于使用 nvm 的用户)
|
|
118
|
-
path_1.default.join(os_1.default.homedir(), '.nvm', 'versions', 'node', '*', 'lib', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
119
|
-
];
|
|
120
|
-
for (const possiblePath of possiblePaths) {
|
|
121
|
-
// 处理通配符路径
|
|
122
|
-
if (possiblePath.includes('*')) {
|
|
123
|
-
try {
|
|
124
|
-
const baseDir = possiblePath.split('*')[0];
|
|
125
|
-
if (fs_1.default.existsSync(baseDir)) {
|
|
126
|
-
const dirs = fs_1.default.readdirSync(baseDir);
|
|
127
|
-
for (const dir of dirs) {
|
|
128
|
-
const fullPath = possiblePath.replace('*', dir);
|
|
129
|
-
if (fs_1.default.existsSync(fullPath)) {
|
|
130
|
-
return fullPath;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
catch {
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
else if (fs_1.default.existsSync(possiblePath)) {
|
|
140
|
-
return possiblePath;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* 检测 cli.js 是否已经注入
|
|
147
|
-
* @param content cli.js 文件内容
|
|
148
|
-
* @returns 是否已注入
|
|
149
|
-
*/
|
|
150
|
-
function isInjected(content) {
|
|
151
|
-
return content.includes(MARKER_START);
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* 移除旧的注入代码
|
|
155
|
-
* @param content cli.js 文件内容
|
|
156
|
-
* @returns 清理后的内容
|
|
157
|
-
*/
|
|
158
|
-
function removeOldInjection(content) {
|
|
159
|
-
const regex = new RegExp(`${MARKER_START}[\\s\\S]*?${MARKER_END}\\n?`, 'g');
|
|
160
|
-
return content.replace(regex, '');
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* 生成注入代码
|
|
164
|
-
* @param baseUrl API 基础 URL
|
|
165
|
-
* @param apiKey API Key
|
|
166
|
-
* @returns 注入代码字符串
|
|
167
|
-
*/
|
|
168
|
-
function generateInjectionCode(baseUrl, apiKey) {
|
|
169
|
-
return `${MARKER_START}
|
|
170
|
-
process.env.ANTHROPIC_BASE_URL="${baseUrl}";
|
|
171
|
-
process.env.ANTHROPIC_AUTH_TOKEN="${apiKey}";
|
|
172
|
-
${MARKER_END}
|
|
173
|
-
`;
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* 查找注入位置(在注释后、代码前)
|
|
177
|
-
* @param lines 文件行数组
|
|
178
|
-
* @returns 注入位置索引
|
|
179
|
-
*/
|
|
180
|
-
function findInjectionIndex(lines) {
|
|
181
|
-
let insertIndex = 0;
|
|
182
|
-
for (let i = 0; i < lines.length; i++) {
|
|
183
|
-
const trimmed = lines[i].trim();
|
|
184
|
-
// 跳过 shebang、注释、空行
|
|
185
|
-
if (trimmed.startsWith('#!') ||
|
|
186
|
-
trimmed.startsWith('//') ||
|
|
187
|
-
trimmed === '') {
|
|
188
|
-
insertIndex = i + 1;
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
// 遇到代码行,停止
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
return insertIndex;
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* 注入环境变量代码到 cli.js
|
|
199
|
-
* @param content cli.js 原始内容
|
|
200
|
-
* @param baseUrl API 基础 URL
|
|
201
|
-
* @param apiKey API Key
|
|
202
|
-
* @returns 注入后的内容
|
|
203
|
-
*/
|
|
204
|
-
function injectCode(content, baseUrl, apiKey) {
|
|
205
|
-
// 先移除旧注入
|
|
206
|
-
let cleanContent = removeOldInjection(content);
|
|
207
|
-
const injection = generateInjectionCode(baseUrl, apiKey);
|
|
208
|
-
const lines = cleanContent.split('\n');
|
|
209
|
-
const insertIndex = findInjectionIndex(lines);
|
|
210
|
-
lines.splice(insertIndex, 0, injection);
|
|
211
|
-
return lines.join('\n');
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* 执行 CLI 注入
|
|
215
|
-
* @param cliJsPath cli.js 文件路径
|
|
216
|
-
* @param baseUrl API 基础 URL
|
|
217
|
-
* @param apiKey API Key
|
|
218
|
-
* @throws 文件读写错误
|
|
219
|
-
*/
|
|
220
|
-
function injectClaudeCli(cliJsPath, baseUrl, apiKey) {
|
|
221
|
-
// 读取原始内容
|
|
222
|
-
const originalContent = fs_1.default.readFileSync(cliJsPath, 'utf-8');
|
|
223
|
-
// 注入代码
|
|
224
|
-
const newContent = injectCode(originalContent, baseUrl, apiKey);
|
|
225
|
-
// 写回文件
|
|
226
|
-
fs_1.default.writeFileSync(cliJsPath, newContent, 'utf-8');
|
|
227
|
-
}
|
package/dist/config.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.SERVER_URL = void 0;
|
|
7
|
-
exports.getCodexConfigDir = getCodexConfigDir;
|
|
8
|
-
exports.getCodexConfigPath = getCodexConfigPath;
|
|
9
|
-
exports.getCodexAuthPath = getCodexAuthPath;
|
|
10
|
-
exports.ensureDir = ensureDir;
|
|
11
|
-
exports.loadCodexConfig = loadCodexConfig;
|
|
12
|
-
exports.saveCodexConfig = saveCodexConfig;
|
|
13
|
-
exports.loadCodexAuth = loadCodexAuth;
|
|
14
|
-
exports.saveCodexAuth = saveCodexAuth;
|
|
15
|
-
const os_1 = __importDefault(require("os"));
|
|
16
|
-
const path_1 = __importDefault(require("path"));
|
|
17
|
-
const fs_1 = __importDefault(require("fs"));
|
|
18
|
-
// ============================================
|
|
19
|
-
// 服务器配置 - 在此处配置您的服务器地址
|
|
20
|
-
// ============================================
|
|
21
|
-
exports.SERVER_URL = 'https://yunyi.cfd';
|
|
22
|
-
// ============================================
|
|
23
|
-
// Codex 配置文件路径
|
|
24
|
-
function getCodexConfigDir() {
|
|
25
|
-
return path_1.default.join(os_1.default.homedir(), '.codex');
|
|
26
|
-
}
|
|
27
|
-
function getCodexConfigPath() {
|
|
28
|
-
return path_1.default.join(getCodexConfigDir(), 'config.toml');
|
|
29
|
-
}
|
|
30
|
-
function getCodexAuthPath() {
|
|
31
|
-
return path_1.default.join(getCodexConfigDir(), 'auth.json');
|
|
32
|
-
}
|
|
33
|
-
// 确保目录存在
|
|
34
|
-
function ensureDir(dirPath) {
|
|
35
|
-
if (!fs_1.default.existsSync(dirPath)) {
|
|
36
|
-
fs_1.default.mkdirSync(dirPath, { recursive: true });
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
// 读取 Codex config.toml
|
|
40
|
-
function loadCodexConfig() {
|
|
41
|
-
const configPath = getCodexConfigPath();
|
|
42
|
-
if (fs_1.default.existsSync(configPath)) {
|
|
43
|
-
try {
|
|
44
|
-
return fs_1.default.readFileSync(configPath, 'utf-8');
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
return '';
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return '';
|
|
51
|
-
}
|
|
52
|
-
// 保存 Codex config.toml
|
|
53
|
-
function saveCodexConfig(content) {
|
|
54
|
-
const configDir = getCodexConfigDir();
|
|
55
|
-
const configPath = getCodexConfigPath();
|
|
56
|
-
ensureDir(configDir);
|
|
57
|
-
fs_1.default.writeFileSync(configPath, content, 'utf-8');
|
|
58
|
-
}
|
|
59
|
-
// 读取 Codex auth.json
|
|
60
|
-
function loadCodexAuth() {
|
|
61
|
-
const authPath = getCodexAuthPath();
|
|
62
|
-
if (fs_1.default.existsSync(authPath)) {
|
|
63
|
-
try {
|
|
64
|
-
const content = fs_1.default.readFileSync(authPath, 'utf-8');
|
|
65
|
-
return JSON.parse(content);
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
return {};
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return {};
|
|
72
|
-
}
|
|
73
|
-
// 保存 Codex auth.json
|
|
74
|
-
function saveCodexAuth(auth) {
|
|
75
|
-
const configDir = getCodexConfigDir();
|
|
76
|
-
const authPath = getCodexAuthPath();
|
|
77
|
-
ensureDir(configDir);
|
|
78
|
-
fs_1.default.writeFileSync(authPath, JSON.stringify(auth, null, 2), 'utf-8');
|
|
79
|
-
}
|