baozang-activator 1.0.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/README.md +71 -0
- package/bin/cli.js +271 -0
- package/lib/configurator.js +262 -0
- package/lib/index.js +17 -0
- package/lib/keyDetector.js +139 -0
- package/package.json +26 -0
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# baozang-activator
|
|
2
|
+
|
|
3
|
+
API 激活工具 - 支持 Claude Code, Codex, Gemini CLI 的一键配置
|
|
4
|
+
|
|
5
|
+
## 安装使用
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 直接使用 npx(推荐)
|
|
9
|
+
npx baozang-activator
|
|
10
|
+
|
|
11
|
+
# 或全局安装
|
|
12
|
+
npm install -g baozang-activator
|
|
13
|
+
baozang-activator
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 使用方式
|
|
17
|
+
|
|
18
|
+
### 交互式配置
|
|
19
|
+
```bash
|
|
20
|
+
npx baozang-activator
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 直接传入 API Key(自动识别类型)
|
|
24
|
+
```bash
|
|
25
|
+
npx baozang-activator sk-ant-xxx # 自动识别为 Claude Code
|
|
26
|
+
npx baozang-activator sk-xxx # 自动识别为 Codex
|
|
27
|
+
npx baozang-activator AIzaXXX # 自动识别为 Gemini CLI
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 指定工具类型
|
|
31
|
+
```bash
|
|
32
|
+
npx baozang-activator -t claude sk-ant-xxx
|
|
33
|
+
npx baozang-activator -t codex sk-xxx
|
|
34
|
+
npx baozang-activator -t gemini AIzaXXX
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## API Key 自动识别规则
|
|
38
|
+
|
|
39
|
+
| 前缀 | 工具类型 |
|
|
40
|
+
|------|----------|
|
|
41
|
+
| `sk-ant-*` | Claude Code |
|
|
42
|
+
| `sk-*` | Codex (OpenAI) |
|
|
43
|
+
| `AIza*` | Gemini CLI |
|
|
44
|
+
|
|
45
|
+
## 配置内容
|
|
46
|
+
|
|
47
|
+
工具会自动配置以下内容:
|
|
48
|
+
|
|
49
|
+
### Claude Code
|
|
50
|
+
- 环境变量: `ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN`
|
|
51
|
+
- 配置文件: `~/.config/claude-code/config.json`
|
|
52
|
+
|
|
53
|
+
### Codex
|
|
54
|
+
- 环境变量: `OPENAI_BASE_URL`, `OPENAI_API_KEY`
|
|
55
|
+
- 配置文件: `~/.config/codex/config.toml`
|
|
56
|
+
- 认证文件: `~/.config/codex/auth.json`
|
|
57
|
+
|
|
58
|
+
### Gemini CLI
|
|
59
|
+
- 环境变量: `GOOGLE_GEMINI_BASE_URL`, `GEMINI_API_KEY`
|
|
60
|
+
|
|
61
|
+
## 命令行选项
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
-t, --type <type> 指定工具类型 (claude/codex/gemini)
|
|
65
|
+
-h, --help 显示帮助信息
|
|
66
|
+
-v, --version 显示版本号
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* baozang-activator CLI
|
|
5
|
+
* API 激活工具 - 支持 Claude Code, Codex, Gemini CLI
|
|
6
|
+
*
|
|
7
|
+
* 使用方式:
|
|
8
|
+
* npx baozang-activator
|
|
9
|
+
* npx baozang-activator <api-key>
|
|
10
|
+
* npx baozang-activator --type claude <api-key>
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const chalk = require('chalk');
|
|
14
|
+
const inquirer = require('inquirer');
|
|
15
|
+
const readline = require('readline');
|
|
16
|
+
const { detectKeyType, getAllToolTypes, getToolConfig } = require('../lib/keyDetector');
|
|
17
|
+
const { configure, isToolInstalled, getShellConfigPath } = require('../lib/configurator');
|
|
18
|
+
|
|
19
|
+
const VERSION = '1.0.0';
|
|
20
|
+
const BANNER = `
|
|
21
|
+
${chalk.cyan('╔════════════════════════════════════════════╗')}
|
|
22
|
+
${chalk.cyan('║')} ${chalk.yellow.bold('baozang-activator')} ${chalk.gray(`v${VERSION}`)} ${chalk.cyan('║')}
|
|
23
|
+
${chalk.cyan('║')} ${chalk.white('baozang.duckdns.org 激活工具')} ${chalk.cyan('║')}
|
|
24
|
+
${chalk.cyan('║')} ${chalk.gray('支持 Claude Code / Codex / Gemini CLI')} ${chalk.cyan('║')}
|
|
25
|
+
${chalk.cyan('╚════════════════════════════════════════════╝')}
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 打印信息
|
|
30
|
+
*/
|
|
31
|
+
const log = {
|
|
32
|
+
info: (msg) => console.log(chalk.blue('[信息]'), msg),
|
|
33
|
+
success: (msg) => console.log(chalk.green('[成功]'), msg),
|
|
34
|
+
warning: (msg) => console.log(chalk.yellow('[警告]'), msg),
|
|
35
|
+
error: (msg) => console.log(chalk.red('[错误]'), msg)
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 解析命令行参数
|
|
40
|
+
*/
|
|
41
|
+
function parseArgs() {
|
|
42
|
+
const args = process.argv.slice(2);
|
|
43
|
+
const result = { apiKey: null, type: null, help: false, version: false, yes: false };
|
|
44
|
+
|
|
45
|
+
for (let i = 0; i < args.length; i++) {
|
|
46
|
+
const arg = args[i];
|
|
47
|
+
if (arg === '--help' || arg === '-h') {
|
|
48
|
+
result.help = true;
|
|
49
|
+
} else if (arg === '--version' || arg === '-v') {
|
|
50
|
+
result.version = true;
|
|
51
|
+
} else if (arg === '--type' || arg === '-t') {
|
|
52
|
+
result.type = args[++i];
|
|
53
|
+
} else if (arg === '--yes' || arg === '-y') {
|
|
54
|
+
result.yes = true;
|
|
55
|
+
} else if (!arg.startsWith('-')) {
|
|
56
|
+
result.apiKey = arg;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 显示帮助信息
|
|
65
|
+
*/
|
|
66
|
+
function showHelp() {
|
|
67
|
+
console.log(`
|
|
68
|
+
${chalk.yellow('使用方式:')}
|
|
69
|
+
npx baozang-activator 交互式配置
|
|
70
|
+
npx baozang-activator <api-key> 自动识别并配置
|
|
71
|
+
npx baozang-activator -t <type> <key> 指定类型配置
|
|
72
|
+
|
|
73
|
+
${chalk.yellow('选项:')}
|
|
74
|
+
-t, --type <type> 指定工具类型 (claude/codex/gemini)
|
|
75
|
+
-y, --yes 跳过确认,直接配置
|
|
76
|
+
-h, --help 显示帮助信息
|
|
77
|
+
-v, --version 显示版本号
|
|
78
|
+
|
|
79
|
+
${chalk.yellow('API Key 自动识别规则:')}
|
|
80
|
+
${chalk.cyan('sk-ant-*')} → Claude Code
|
|
81
|
+
${chalk.cyan('sk-*')} → Codex (OpenAI)
|
|
82
|
+
${chalk.cyan('AIza*')} → Gemini CLI
|
|
83
|
+
|
|
84
|
+
${chalk.yellow('示例:')}
|
|
85
|
+
npx baozang-activator sk-ant-xxx 配置 Claude Code
|
|
86
|
+
npx baozang-activator sk-xxx 配置 Codex
|
|
87
|
+
npx baozang-activator AIzaXXX 配置 Gemini CLI
|
|
88
|
+
npx baozang-activator -y sk-ant-xxx 跳过确认直接配置
|
|
89
|
+
npx baozang-activator -t codex sk-xxx 强制配置为 Codex
|
|
90
|
+
`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 检查是否为交互式终端
|
|
95
|
+
*/
|
|
96
|
+
function isInteractive() {
|
|
97
|
+
return process.stdin.isTTY;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 让用户选择工具类型
|
|
102
|
+
*/
|
|
103
|
+
async function selectToolType() {
|
|
104
|
+
const tools = getAllToolTypes();
|
|
105
|
+
const choices = Object.entries(tools).map(([key, config]) => ({
|
|
106
|
+
name: `${config.name} (${config.command})`,
|
|
107
|
+
value: key
|
|
108
|
+
}));
|
|
109
|
+
|
|
110
|
+
const { type } = await inquirer.prompt([{
|
|
111
|
+
type: 'list',
|
|
112
|
+
name: 'type',
|
|
113
|
+
message: '请选择要配置的工具:',
|
|
114
|
+
choices
|
|
115
|
+
}]);
|
|
116
|
+
|
|
117
|
+
return type;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 获取 API Key
|
|
122
|
+
*/
|
|
123
|
+
async function getApiKey() {
|
|
124
|
+
const { apiKey } = await inquirer.prompt([{
|
|
125
|
+
type: 'password',
|
|
126
|
+
name: 'apiKey',
|
|
127
|
+
message: '请输入您的 API Key:',
|
|
128
|
+
mask: '*',
|
|
129
|
+
validate: (input) => input.trim() ? true : 'API Key 不能为空'
|
|
130
|
+
}]);
|
|
131
|
+
|
|
132
|
+
return apiKey.trim();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 确认配置
|
|
137
|
+
*/
|
|
138
|
+
async function confirmConfig(type, config, skipConfirm) {
|
|
139
|
+
console.log('');
|
|
140
|
+
log.info(`检测到工具类型: ${chalk.cyan(config.name)}`);
|
|
141
|
+
log.info(`Base URL: ${chalk.gray(config.baseUrl)}`);
|
|
142
|
+
|
|
143
|
+
const installed = isToolInstalled(config.command);
|
|
144
|
+
if (installed) {
|
|
145
|
+
log.success(`${config.command} 已安装`);
|
|
146
|
+
} else {
|
|
147
|
+
log.warning(`${config.command} 未安装,配置后请先安装工具`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 如果跳过确认或非交互式模式,直接返回 true
|
|
151
|
+
if (skipConfirm || !isInteractive()) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const { confirm } = await inquirer.prompt([{
|
|
156
|
+
type: 'confirm',
|
|
157
|
+
name: 'confirm',
|
|
158
|
+
message: '确认进行配置?',
|
|
159
|
+
default: true
|
|
160
|
+
}]);
|
|
161
|
+
|
|
162
|
+
return confirm;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 执行配置
|
|
167
|
+
*/
|
|
168
|
+
async function doConfig(type, apiKey, config) {
|
|
169
|
+
try {
|
|
170
|
+
log.info('正在配置...');
|
|
171
|
+
const result = configure(type, apiKey, config);
|
|
172
|
+
|
|
173
|
+
console.log('');
|
|
174
|
+
log.success(`${config.name} 配置完成!`);
|
|
175
|
+
console.log('');
|
|
176
|
+
console.log(chalk.yellow('已配置:'));
|
|
177
|
+
console.log(` - 环境变量: ${chalk.gray(result.shellConfig)}`);
|
|
178
|
+
if (result.configFile) {
|
|
179
|
+
console.log(` - 配置文件: ${chalk.gray(result.configFile)}`);
|
|
180
|
+
}
|
|
181
|
+
if (result.authFile) {
|
|
182
|
+
console.log(` - 认证文件: ${chalk.gray(result.authFile)}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
console.log('');
|
|
186
|
+
console.log(chalk.yellow('请运行以下命令使配置生效:'));
|
|
187
|
+
console.log(chalk.cyan(` source ${result.shellConfig}`));
|
|
188
|
+
console.log('');
|
|
189
|
+
console.log(chalk.yellow(`然后运行 '${config.command}' 启动工具`));
|
|
190
|
+
|
|
191
|
+
return true;
|
|
192
|
+
} catch (err) {
|
|
193
|
+
log.error(`配置失败: ${err.message}`);
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 主函数
|
|
200
|
+
*/
|
|
201
|
+
async function main() {
|
|
202
|
+
console.log(BANNER);
|
|
203
|
+
|
|
204
|
+
const args = parseArgs();
|
|
205
|
+
|
|
206
|
+
if (args.help) {
|
|
207
|
+
showHelp();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (args.version) {
|
|
212
|
+
console.log(`v${VERSION}`);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
let apiKey = args.apiKey;
|
|
217
|
+
let type = args.type;
|
|
218
|
+
let config;
|
|
219
|
+
|
|
220
|
+
// 如果没有提供 API Key
|
|
221
|
+
if (!apiKey) {
|
|
222
|
+
if (!isInteractive()) {
|
|
223
|
+
log.error('非交互式模式下必须提供 API Key');
|
|
224
|
+
log.info('用法: npx baozang-activator <api-key>');
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
apiKey = await getApiKey();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 如果没有指定类型,尝试自动检测
|
|
231
|
+
if (!type) {
|
|
232
|
+
const detected = detectKeyType(apiKey);
|
|
233
|
+
if (detected) {
|
|
234
|
+
type = detected.type;
|
|
235
|
+
config = detected.config;
|
|
236
|
+
log.info(`自动识别为: ${chalk.cyan(config.name)}`);
|
|
237
|
+
} else {
|
|
238
|
+
if (!isInteractive()) {
|
|
239
|
+
log.error('无法自动识别 API Key 类型,请使用 -t 参数指定');
|
|
240
|
+
log.info('支持的类型: claude, codex, gemini');
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
log.warning('无法自动识别 API Key 类型,请手动选择');
|
|
244
|
+
type = await selectToolType();
|
|
245
|
+
config = getToolConfig(type);
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
config = getToolConfig(type);
|
|
249
|
+
if (!config) {
|
|
250
|
+
log.error(`不支持的工具类型: ${type}`);
|
|
251
|
+
log.info('支持的类型: claude, codex, gemini');
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// 确认配置
|
|
257
|
+
const confirmed = await confirmConfig(type, config, args.yes);
|
|
258
|
+
if (!confirmed) {
|
|
259
|
+
log.info('已取消配置');
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// 执行配置
|
|
264
|
+
await doConfig(type, apiKey, config);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 运行
|
|
268
|
+
main().catch(err => {
|
|
269
|
+
log.error(err.message);
|
|
270
|
+
process.exit(1);
|
|
271
|
+
});
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置器 - 负责写入配置文件和环境变量
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
|
|
9
|
+
const HOME = os.homedir();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 获取 shell 配置文件路径
|
|
13
|
+
* @returns {string} - shell 配置文件路径
|
|
14
|
+
*/
|
|
15
|
+
function getShellConfigPath() {
|
|
16
|
+
const shell = process.env.SHELL || '';
|
|
17
|
+
|
|
18
|
+
if (shell.includes('zsh')) {
|
|
19
|
+
return path.join(HOME, '.zshrc');
|
|
20
|
+
} else if (shell.includes('bash')) {
|
|
21
|
+
const bashrc = path.join(HOME, '.bashrc');
|
|
22
|
+
if (fs.existsSync(bashrc)) {
|
|
23
|
+
return bashrc;
|
|
24
|
+
}
|
|
25
|
+
return path.join(HOME, '.bash_profile');
|
|
26
|
+
}
|
|
27
|
+
return path.join(HOME, '.profile');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 检查配置文件中是否存在某个环境变量
|
|
32
|
+
* @param {string} filePath - 配置文件路径
|
|
33
|
+
* @param {string} varName - 环境变量名
|
|
34
|
+
* @returns {boolean}
|
|
35
|
+
*/
|
|
36
|
+
function hasEnvVar(filePath, varName) {
|
|
37
|
+
if (!fs.existsSync(filePath)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
41
|
+
return content.includes(`export ${varName}=`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 添加环境变量到配置文件
|
|
46
|
+
* @param {string} filePath - 配置文件路径
|
|
47
|
+
* @param {string} varName - 环境变量名
|
|
48
|
+
* @param {string} varValue - 环境变量值
|
|
49
|
+
*/
|
|
50
|
+
function addEnvVar(filePath, varName, varValue) {
|
|
51
|
+
const line = `\n# baozang.duckdns.org 配置 - ${new Date().toISOString()}\nexport ${varName}="${varValue}"\n`;
|
|
52
|
+
fs.appendFileSync(filePath, line);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 更新环境变量(如果存在则更新,不存在则添加)
|
|
57
|
+
* @param {string} filePath - 配置文件路径
|
|
58
|
+
* @param {string} varName - 环境变量名
|
|
59
|
+
* @param {string} varValue - 环境变量值
|
|
60
|
+
*/
|
|
61
|
+
function updateEnvVar(filePath, varName, varValue) {
|
|
62
|
+
if (!fs.existsSync(filePath)) {
|
|
63
|
+
addEnvVar(filePath, varName, varValue);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let content = fs.readFileSync(filePath, 'utf-8');
|
|
68
|
+
const regex = new RegExp(`export ${varName}=.*`, 'g');
|
|
69
|
+
|
|
70
|
+
if (regex.test(content)) {
|
|
71
|
+
content = content.replace(regex, `export ${varName}="${varValue}"`);
|
|
72
|
+
fs.writeFileSync(filePath, content);
|
|
73
|
+
} else {
|
|
74
|
+
addEnvVar(filePath, varName, varValue);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 确保目录存在
|
|
80
|
+
* @param {string} dirPath - 目录路径
|
|
81
|
+
*/
|
|
82
|
+
function ensureDir(dirPath) {
|
|
83
|
+
if (!fs.existsSync(dirPath)) {
|
|
84
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 更新 JSON 配置文件
|
|
90
|
+
* @param {string} filePath - 文件路径
|
|
91
|
+
* @param {object} updates - 要更新的键值对
|
|
92
|
+
*/
|
|
93
|
+
function updateJsonConfig(filePath, updates) {
|
|
94
|
+
let data = {};
|
|
95
|
+
|
|
96
|
+
if (fs.existsSync(filePath)) {
|
|
97
|
+
try {
|
|
98
|
+
data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
99
|
+
} catch (e) {
|
|
100
|
+
data = {};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
Object.assign(data, updates);
|
|
105
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 更新 TOML 配置文件
|
|
110
|
+
* @param {string} filePath - 文件路径
|
|
111
|
+
* @param {object} updates - 要更新的键值对
|
|
112
|
+
*/
|
|
113
|
+
function updateTomlConfig(filePath, updates) {
|
|
114
|
+
let lines = [];
|
|
115
|
+
|
|
116
|
+
if (fs.existsSync(filePath)) {
|
|
117
|
+
lines = fs.readFileSync(filePath, 'utf-8').split('\n');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
121
|
+
const regex = new RegExp(`^${key}\\s*=`);
|
|
122
|
+
const newLine = `${key} = "${value}"`;
|
|
123
|
+
const index = lines.findIndex(line => regex.test(line));
|
|
124
|
+
|
|
125
|
+
if (index >= 0) {
|
|
126
|
+
lines[index] = newLine;
|
|
127
|
+
} else {
|
|
128
|
+
lines.push(newLine);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
fs.writeFileSync(filePath, lines.join('\n'));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 配置 Claude Code
|
|
137
|
+
* @param {string} apiKey - API Key
|
|
138
|
+
* @param {object} config - 工具配置
|
|
139
|
+
* @returns {object} - 配置结果
|
|
140
|
+
*/
|
|
141
|
+
function configureClaude(apiKey, config) {
|
|
142
|
+
const shellConfig = getShellConfigPath();
|
|
143
|
+
const configDir = path.join(HOME, config.configDir);
|
|
144
|
+
const configFile = path.join(configDir, config.configFile);
|
|
145
|
+
|
|
146
|
+
// 更新环境变量
|
|
147
|
+
updateEnvVar(shellConfig, config.envVars.baseUrl, config.baseUrl);
|
|
148
|
+
updateEnvVar(shellConfig, config.envVars.apiKey, apiKey);
|
|
149
|
+
|
|
150
|
+
// 更新配置文件
|
|
151
|
+
ensureDir(configDir);
|
|
152
|
+
updateJsonConfig(configFile, {
|
|
153
|
+
baseUrl: config.baseUrl,
|
|
154
|
+
apiKey: apiKey
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
shellConfig,
|
|
159
|
+
configFile,
|
|
160
|
+
envVars: [config.envVars.baseUrl, config.envVars.apiKey]
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* 配置 Codex
|
|
166
|
+
* @param {string} apiKey - API Key
|
|
167
|
+
* @param {object} config - 工具配置
|
|
168
|
+
* @returns {object} - 配置结果
|
|
169
|
+
*/
|
|
170
|
+
function configureCodex(apiKey, config) {
|
|
171
|
+
const shellConfig = getShellConfigPath();
|
|
172
|
+
const configDir = path.join(HOME, config.configDir);
|
|
173
|
+
const configFile = path.join(configDir, config.configFile);
|
|
174
|
+
const authFile = path.join(configDir, config.authFile);
|
|
175
|
+
|
|
176
|
+
// 更新环境变量
|
|
177
|
+
updateEnvVar(shellConfig, config.envVars.baseUrl, config.baseUrl);
|
|
178
|
+
updateEnvVar(shellConfig, config.envVars.apiKey, apiKey);
|
|
179
|
+
|
|
180
|
+
// 更新配置文件
|
|
181
|
+
ensureDir(configDir);
|
|
182
|
+
updateTomlConfig(configFile, {
|
|
183
|
+
base_url: config.baseUrl,
|
|
184
|
+
api_key: apiKey
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// 更新认证文件
|
|
188
|
+
updateJsonConfig(authFile, {
|
|
189
|
+
baseUrl: config.baseUrl,
|
|
190
|
+
apiKey: apiKey
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
shellConfig,
|
|
195
|
+
configFile,
|
|
196
|
+
authFile,
|
|
197
|
+
envVars: [config.envVars.baseUrl, config.envVars.apiKey]
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 配置 Gemini CLI
|
|
203
|
+
* @param {string} apiKey - API Key
|
|
204
|
+
* @param {object} config - 工具配置
|
|
205
|
+
* @returns {object} - 配置结果
|
|
206
|
+
*/
|
|
207
|
+
function configureGemini(apiKey, config) {
|
|
208
|
+
const shellConfig = getShellConfigPath();
|
|
209
|
+
|
|
210
|
+
// 更新环境变量
|
|
211
|
+
updateEnvVar(shellConfig, config.envVars.baseUrl, config.baseUrl);
|
|
212
|
+
updateEnvVar(shellConfig, config.envVars.apiKey, apiKey);
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
shellConfig,
|
|
216
|
+
envVars: [config.envVars.baseUrl, config.envVars.apiKey]
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* 根据类型配置工具
|
|
222
|
+
* @param {string} type - 工具类型
|
|
223
|
+
* @param {string} apiKey - API Key
|
|
224
|
+
* @param {object} config - 工具配置
|
|
225
|
+
* @returns {object} - 配置结果
|
|
226
|
+
*/
|
|
227
|
+
function configure(type, apiKey, config) {
|
|
228
|
+
switch (type) {
|
|
229
|
+
case 'claude':
|
|
230
|
+
return configureClaude(apiKey, config);
|
|
231
|
+
case 'codex':
|
|
232
|
+
return configureCodex(apiKey, config);
|
|
233
|
+
case 'gemini':
|
|
234
|
+
return configureGemini(apiKey, config);
|
|
235
|
+
default:
|
|
236
|
+
throw new Error(`不支持的工具类型: ${type}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* 检查工具是否已安装
|
|
242
|
+
* @param {string} command - 命令名
|
|
243
|
+
* @returns {boolean}
|
|
244
|
+
*/
|
|
245
|
+
function isToolInstalled(command) {
|
|
246
|
+
const { execSync } = require('child_process');
|
|
247
|
+
try {
|
|
248
|
+
execSync(`which ${command}`, { stdio: 'ignore' });
|
|
249
|
+
return true;
|
|
250
|
+
} catch (e) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
module.exports = {
|
|
256
|
+
getShellConfigPath,
|
|
257
|
+
configure,
|
|
258
|
+
isToolInstalled,
|
|
259
|
+
ensureDir,
|
|
260
|
+
updateJsonConfig,
|
|
261
|
+
updateTomlConfig
|
|
262
|
+
};
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* yunyi-activator
|
|
3
|
+
* API 激活工具库
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { detectKeyType, getAllToolTypes, getToolConfig, KEY_PATTERNS } = require('./keyDetector');
|
|
7
|
+
const { configure, isToolInstalled, getShellConfigPath } = require('./configurator');
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
detectKeyType,
|
|
11
|
+
getAllToolTypes,
|
|
12
|
+
getToolConfig,
|
|
13
|
+
KEY_PATTERNS,
|
|
14
|
+
configure,
|
|
15
|
+
isToolInstalled,
|
|
16
|
+
getShellConfigPath
|
|
17
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Key 类型检测器
|
|
3
|
+
* 根据 API Key 前缀自动识别工具类型
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// 环境配置
|
|
7
|
+
const ENVIRONMENTS = {
|
|
8
|
+
test: {
|
|
9
|
+
name: 'Test (baozang.duckdns.org)',
|
|
10
|
+
baseUrl: 'https://baozang.duckdns.org/',
|
|
11
|
+
baseUrlV1: 'https://baozang.duckdns.org/v1'
|
|
12
|
+
},
|
|
13
|
+
prod: {
|
|
14
|
+
name: 'Production (baozangaizhongzhuan.net)',
|
|
15
|
+
baseUrl: 'https://www.baozangaizhongzhuan.net/',
|
|
16
|
+
baseUrlV1: 'https://www.baozangaizhongzhuan.net/v1'
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// 默认环境由包名决定,这里设置为占位符,会在构建时替换
|
|
21
|
+
let DEFAULT_ENV = 'prod';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 获取当前环境配置
|
|
25
|
+
*/
|
|
26
|
+
function getEnvConfig() {
|
|
27
|
+
return ENVIRONMENTS[DEFAULT_ENV] || ENVIRONMENTS.test;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 设置环境
|
|
32
|
+
*/
|
|
33
|
+
function setEnvironment(env) {
|
|
34
|
+
if (ENVIRONMENTS[env]) {
|
|
35
|
+
DEFAULT_ENV = env;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const KEY_PATTERNS = {
|
|
40
|
+
claude: {
|
|
41
|
+
prefixes: ['sk-ant-', 'claude-', 'anthropic-'],
|
|
42
|
+
name: 'Claude Code',
|
|
43
|
+
command: 'claude',
|
|
44
|
+
envVars: {
|
|
45
|
+
baseUrl: 'ANTHROPIC_BASE_URL',
|
|
46
|
+
apiKey: 'ANTHROPIC_AUTH_TOKEN'
|
|
47
|
+
},
|
|
48
|
+
get baseUrl() { return getEnvConfig().baseUrl; },
|
|
49
|
+
configDir: '.config/claude-code',
|
|
50
|
+
configFile: 'config.json'
|
|
51
|
+
},
|
|
52
|
+
codex: {
|
|
53
|
+
prefixes: ['sk-', 'openai-', 'codex-'],
|
|
54
|
+
name: 'Codex',
|
|
55
|
+
command: 'codex',
|
|
56
|
+
envVars: {
|
|
57
|
+
baseUrl: 'OPENAI_BASE_URL',
|
|
58
|
+
apiKey: 'OPENAI_API_KEY'
|
|
59
|
+
},
|
|
60
|
+
get baseUrl() { return getEnvConfig().baseUrlV1; },
|
|
61
|
+
configDir: '.config/codex',
|
|
62
|
+
configFile: 'config.toml',
|
|
63
|
+
authFile: 'auth.json'
|
|
64
|
+
},
|
|
65
|
+
gemini: {
|
|
66
|
+
prefixes: ['AIza', 'gemini-', 'google-'],
|
|
67
|
+
name: 'Gemini CLI',
|
|
68
|
+
command: 'gemini',
|
|
69
|
+
envVars: {
|
|
70
|
+
baseUrl: 'GOOGLE_GEMINI_BASE_URL',
|
|
71
|
+
apiKey: 'GEMINI_API_KEY'
|
|
72
|
+
},
|
|
73
|
+
get baseUrl() { return getEnvConfig().baseUrl; },
|
|
74
|
+
configDir: '.config/gemini',
|
|
75
|
+
configFile: 'config.json'
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 检测 API Key 类型
|
|
81
|
+
* @param {string} apiKey - API Key
|
|
82
|
+
* @returns {object|null} - 匹配的工具配置,或 null
|
|
83
|
+
*/
|
|
84
|
+
function detectKeyType(apiKey) {
|
|
85
|
+
if (!apiKey || typeof apiKey !== 'string') {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const key = apiKey.trim();
|
|
90
|
+
|
|
91
|
+
// 优先检查 Claude (sk-ant- 开头)
|
|
92
|
+
if (key.startsWith('sk-ant-')) {
|
|
93
|
+
return { type: 'claude', config: KEY_PATTERNS.claude };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 检查 Gemini (AIza 开头)
|
|
97
|
+
if (key.startsWith('AIza')) {
|
|
98
|
+
return { type: 'gemini', config: KEY_PATTERNS.gemini };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 检查其他前缀
|
|
102
|
+
for (const [type, config] of Object.entries(KEY_PATTERNS)) {
|
|
103
|
+
for (const prefix of config.prefixes) {
|
|
104
|
+
if (key.startsWith(prefix)) {
|
|
105
|
+
return { type, config };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 默认返回 null,让用户手动选择
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 获取所有支持的工具类型
|
|
116
|
+
* @returns {object} - 工具配置映射
|
|
117
|
+
*/
|
|
118
|
+
function getAllToolTypes() {
|
|
119
|
+
return KEY_PATTERNS;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 根据类型获取工具配置
|
|
124
|
+
* @param {string} type - 工具类型 (claude/codex/gemini)
|
|
125
|
+
* @returns {object|null} - 工具配置
|
|
126
|
+
*/
|
|
127
|
+
function getToolConfig(type) {
|
|
128
|
+
return KEY_PATTERNS[type] || null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = {
|
|
132
|
+
detectKeyType,
|
|
133
|
+
getAllToolTypes,
|
|
134
|
+
getToolConfig,
|
|
135
|
+
getEnvConfig,
|
|
136
|
+
setEnvironment,
|
|
137
|
+
ENVIRONMENTS,
|
|
138
|
+
KEY_PATTERNS
|
|
139
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "baozang-activator",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "API 激活工具 (生产环境 - baozangaizhongzhuan.net)",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"baozang-activator": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"claude",
|
|
11
|
+
"codex",
|
|
12
|
+
"gemini",
|
|
13
|
+
"api",
|
|
14
|
+
"activator",
|
|
15
|
+
"cli"
|
|
16
|
+
],
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"chalk": "^4.1.2",
|
|
21
|
+
"inquirer": "^8.2.6"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=14.0.0"
|
|
25
|
+
}
|
|
26
|
+
}
|