openclawapi 1.2.1 → 1.3.1
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/cli.js +135 -139
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -20,13 +20,37 @@ const ENDPOINTS = [
|
|
|
20
20
|
{ name: '备用节点2', url: 'http://47.97.100.10' }
|
|
21
21
|
];
|
|
22
22
|
|
|
23
|
-
// 模型预设
|
|
24
|
-
const
|
|
23
|
+
// Claude 模型预设
|
|
24
|
+
const CLAUDE_MODELS = [
|
|
25
25
|
{ id: 'claude-opus-4-5', name: 'Claude Opus 4.5' },
|
|
26
26
|
{ id: 'claude-sonnet-4-5', name: 'Claude Sonnet 4.5' },
|
|
27
27
|
{ id: 'claude-haiku-4-5', name: 'Claude Haiku 4.5' }
|
|
28
28
|
];
|
|
29
29
|
|
|
30
|
+
// Codex 模型预设
|
|
31
|
+
const CODEX_MODELS = [
|
|
32
|
+
{ id: 'gpt-5.2', name: 'GPT 5.2' },
|
|
33
|
+
{ id: 'gpt-5.2-codex', name: 'GPT 5.2 Codex' }
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
// API 类型配置
|
|
37
|
+
const API_CONFIG = {
|
|
38
|
+
claude: {
|
|
39
|
+
urlSuffix: '/claude/v1/messages',
|
|
40
|
+
api: 'anthropic-messages',
|
|
41
|
+
contextWindow: 200000,
|
|
42
|
+
maxTokens: 8192,
|
|
43
|
+
providerName: 'yunyi-claude'
|
|
44
|
+
},
|
|
45
|
+
codex: {
|
|
46
|
+
urlSuffix: '/codex/response',
|
|
47
|
+
api: 'openai-responses',
|
|
48
|
+
contextWindow: 128000,
|
|
49
|
+
maxTokens: 32768,
|
|
50
|
+
providerName: 'yunyi-codex'
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
30
54
|
// 备份文件名
|
|
31
55
|
const BACKUP_FILENAME = 'openclaw-default.json.bak';
|
|
32
56
|
|
|
@@ -88,7 +112,6 @@ function getConfigPath() {
|
|
|
88
112
|
|
|
89
113
|
const configDir = path.dirname(openclawConfig);
|
|
90
114
|
|
|
91
|
-
// 查找 auth-profiles 路径
|
|
92
115
|
const authCandidates = [
|
|
93
116
|
path.join(openclawStateDir, 'agents', 'main', 'agent', 'auth-profiles.json'),
|
|
94
117
|
path.join(clawdbotStateDir, 'agents', 'main', 'agent', 'auth-profiles.json'),
|
|
@@ -136,17 +159,17 @@ function restoreDefaultConfig(configPath, configDir) {
|
|
|
136
159
|
}
|
|
137
160
|
|
|
138
161
|
// ============ URL 构建 ============
|
|
139
|
-
function buildFullUrl(baseUrl) {
|
|
162
|
+
function buildFullUrl(baseUrl, type) {
|
|
140
163
|
const trimmed = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
|
|
141
|
-
|
|
142
|
-
if (trimmed.includes(
|
|
143
|
-
return trimmed +
|
|
164
|
+
const suffix = API_CONFIG[type].urlSuffix;
|
|
165
|
+
if (trimmed.includes(suffix)) return trimmed;
|
|
166
|
+
return trimmed + suffix;
|
|
144
167
|
}
|
|
145
168
|
|
|
146
169
|
// ============ 主程序 ============
|
|
147
170
|
async function main() {
|
|
148
171
|
console.clear();
|
|
149
|
-
console.log(chalk.cyan.bold('\n🔧 OpenClaw
|
|
172
|
+
console.log(chalk.cyan.bold('\n🔧 OpenClaw 配置工具\n'));
|
|
150
173
|
|
|
151
174
|
const paths = getConfigPath();
|
|
152
175
|
console.log(chalk.gray(`配置文件: ${paths.openclawConfig}\n`));
|
|
@@ -162,12 +185,14 @@ async function main() {
|
|
|
162
185
|
name: 'action',
|
|
163
186
|
message: '请选择操作:',
|
|
164
187
|
choices: [
|
|
165
|
-
{ name: '
|
|
166
|
-
{ name: '
|
|
167
|
-
|
|
188
|
+
{ name: '🔵 选择 Claude 节点', value: 'select_claude' },
|
|
189
|
+
{ name: '🟢 选择 Codex 节点', value: 'select_codex' },
|
|
190
|
+
new inquirer.Separator(),
|
|
191
|
+
{ name: '⚡ 激活 Claude', value: 'activate_claude' },
|
|
192
|
+
{ name: '⚡ 激活 Codex', value: 'activate_codex' },
|
|
193
|
+
new inquirer.Separator(),
|
|
168
194
|
{ name: '📋 查看当前配置', value: 'view_config' },
|
|
169
195
|
{ name: '🔄 恢复默认配置', value: 'restore' },
|
|
170
|
-
new inquirer.Separator(),
|
|
171
196
|
{ name: '❌ 退出', value: 'exit' }
|
|
172
197
|
]
|
|
173
198
|
}]);
|
|
@@ -176,14 +201,17 @@ async function main() {
|
|
|
176
201
|
|
|
177
202
|
try {
|
|
178
203
|
switch (action) {
|
|
179
|
-
case '
|
|
180
|
-
await selectNode(paths);
|
|
204
|
+
case 'select_claude':
|
|
205
|
+
await selectNode(paths, 'claude');
|
|
181
206
|
break;
|
|
182
|
-
case '
|
|
183
|
-
await
|
|
207
|
+
case 'select_codex':
|
|
208
|
+
await selectNode(paths, 'codex');
|
|
184
209
|
break;
|
|
185
|
-
case '
|
|
186
|
-
await
|
|
210
|
+
case 'activate_claude':
|
|
211
|
+
await activate(paths, 'claude');
|
|
212
|
+
break;
|
|
213
|
+
case 'activate_codex':
|
|
214
|
+
await activate(paths, 'codex');
|
|
187
215
|
break;
|
|
188
216
|
case 'view_config':
|
|
189
217
|
await viewConfig(paths);
|
|
@@ -203,13 +231,16 @@ async function main() {
|
|
|
203
231
|
}
|
|
204
232
|
}
|
|
205
233
|
|
|
206
|
-
// ============ 选择节点 ============
|
|
207
|
-
async function selectNode(paths) {
|
|
208
|
-
|
|
234
|
+
// ============ 选择节点 (Claude/Codex) ============
|
|
235
|
+
async function selectNode(paths, type) {
|
|
236
|
+
const typeLabel = type === 'claude' ? 'Claude' : 'Codex';
|
|
237
|
+
const models = type === 'claude' ? CLAUDE_MODELS : CODEX_MODELS;
|
|
238
|
+
const apiConfig = API_CONFIG[type];
|
|
239
|
+
|
|
240
|
+
console.log(chalk.cyan(`📡 ${typeLabel} 节点测速中...\n`));
|
|
209
241
|
|
|
210
242
|
const results = await testAllEndpoints();
|
|
211
243
|
|
|
212
|
-
// 按延迟排序
|
|
213
244
|
const sorted = results
|
|
214
245
|
.filter(r => r.success)
|
|
215
246
|
.sort((a, b) => a.latency - b.latency);
|
|
@@ -239,19 +270,18 @@ async function selectNode(paths) {
|
|
|
239
270
|
const primaryIndex = selectedIndex === -1 ? 0 : selectedIndex;
|
|
240
271
|
const selectedEndpoint = sorted[primaryIndex];
|
|
241
272
|
|
|
242
|
-
//
|
|
243
|
-
|
|
273
|
+
// 选择模型
|
|
274
|
+
const { selectedModel } = await inquirer.prompt([{
|
|
275
|
+
type: 'list',
|
|
276
|
+
name: 'selectedModel',
|
|
277
|
+
message: `选择 ${typeLabel} 模型:`,
|
|
278
|
+
choices: models.map(m => ({ name: m.name, value: m.id }))
|
|
279
|
+
}]);
|
|
244
280
|
|
|
245
|
-
|
|
246
|
-
const currentModelId = config.agents?.defaults?.model?.primary?.split('/')[1] || MODELS[0].id;
|
|
247
|
-
const modelConfig = MODELS.find(m => m.id === currentModelId) || MODELS[0];
|
|
281
|
+
const modelConfig = models.find(m => m.id === selectedModel);
|
|
248
282
|
|
|
249
|
-
//
|
|
250
|
-
|
|
251
|
-
Object.keys(config.models.providers).forEach(key => {
|
|
252
|
-
if (key.startsWith('yunyi-')) delete config.models.providers[key];
|
|
253
|
-
});
|
|
254
|
-
}
|
|
283
|
+
// 读取或创建配置
|
|
284
|
+
let config = readConfig(paths.openclawConfig) || {};
|
|
255
285
|
|
|
256
286
|
// 初始化结构
|
|
257
287
|
if (!config.models) config.models = {};
|
|
@@ -261,119 +291,62 @@ async function selectNode(paths) {
|
|
|
261
291
|
if (!config.agents.defaults.model) config.agents.defaults.model = {};
|
|
262
292
|
if (!config.agents.defaults.models) config.agents.defaults.models = {};
|
|
263
293
|
|
|
264
|
-
//
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
294
|
+
// 保留旧的 API Key
|
|
295
|
+
const oldProvider = config.models.providers[apiConfig.providerName];
|
|
296
|
+
const oldApiKey = oldProvider?.apiKey;
|
|
297
|
+
|
|
298
|
+
// 添加/更新节点
|
|
299
|
+
config.models.providers[apiConfig.providerName] = {
|
|
300
|
+
baseUrl: buildFullUrl(selectedEndpoint.url, type),
|
|
301
|
+
api: apiConfig.api,
|
|
302
|
+
apiKey: oldApiKey,
|
|
269
303
|
models: [{
|
|
270
304
|
id: modelConfig.id,
|
|
271
305
|
name: modelConfig.name,
|
|
272
|
-
contextWindow:
|
|
273
|
-
maxTokens:
|
|
306
|
+
contextWindow: apiConfig.contextWindow,
|
|
307
|
+
maxTokens: apiConfig.maxTokens
|
|
274
308
|
}]
|
|
275
309
|
};
|
|
276
310
|
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
config.agents.defaults.
|
|
280
|
-
config.agents.defaults.models = {
|
|
281
|
-
[`${providerName}/${modelConfig.id}`]: { alias: providerName }
|
|
282
|
-
};
|
|
311
|
+
// 注册模型
|
|
312
|
+
const modelKey = `${apiConfig.providerName}/${modelConfig.id}`;
|
|
313
|
+
config.agents.defaults.models[modelKey] = { alias: apiConfig.providerName };
|
|
283
314
|
|
|
284
315
|
writeConfig(paths.openclawConfig, config);
|
|
285
316
|
|
|
286
|
-
console.log(chalk.green(`\n✅
|
|
317
|
+
console.log(chalk.green(`\n✅ ${typeLabel} 节点配置完成!`));
|
|
287
318
|
console.log(chalk.cyan(` 节点: ${selectedEndpoint.name} (${selectedEndpoint.url})`));
|
|
288
319
|
console.log(chalk.gray(` 模型: ${modelConfig.name}`));
|
|
320
|
+
console.log(chalk.gray(` API Key: ${oldApiKey ? '已设置' : '未设置'}`));
|
|
289
321
|
}
|
|
290
322
|
|
|
291
|
-
// ============
|
|
292
|
-
async function
|
|
293
|
-
|
|
323
|
+
// ============ 激活 (Claude/Codex) ============
|
|
324
|
+
async function activate(paths, type) {
|
|
325
|
+
const typeLabel = type === 'claude' ? 'Claude' : 'Codex';
|
|
326
|
+
const apiConfig = API_CONFIG[type];
|
|
327
|
+
const models = type === 'claude' ? CLAUDE_MODELS : CODEX_MODELS;
|
|
294
328
|
|
|
295
|
-
const { selectedModel } = await inquirer.prompt([{
|
|
296
|
-
type: 'list',
|
|
297
|
-
name: 'selectedModel',
|
|
298
|
-
message: '选择模型:',
|
|
299
|
-
choices: MODELS.map(m => ({ name: m.name, value: m.id }))
|
|
300
|
-
}]);
|
|
301
|
-
|
|
302
|
-
const modelConfig = MODELS.find(m => m.id === selectedModel);
|
|
303
329
|
let config = readConfig(paths.openclawConfig);
|
|
304
330
|
|
|
305
|
-
if (!config?.models?.providers) {
|
|
306
|
-
console.log(chalk.yellow(
|
|
331
|
+
if (!config?.models?.providers?.[apiConfig.providerName]) {
|
|
332
|
+
console.log(chalk.yellow(`⚠️ 请先选择 ${typeLabel} 节点`));
|
|
307
333
|
return;
|
|
308
334
|
}
|
|
309
335
|
|
|
310
|
-
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
config.models.providers[providerName].models = [{
|
|
314
|
-
id: modelConfig.id,
|
|
315
|
-
name: modelConfig.name,
|
|
316
|
-
contextWindow: 200000,
|
|
317
|
-
maxTokens: 8192
|
|
318
|
-
}];
|
|
319
|
-
|
|
320
|
-
// 更新主模型(无备用)
|
|
321
|
-
config.agents.defaults.model.primary = `${providerName}/${modelConfig.id}`;
|
|
322
|
-
config.agents.defaults.model.fallbacks = [];
|
|
323
|
-
config.agents.defaults.models = {
|
|
324
|
-
[`${providerName}/${modelConfig.id}`]: { alias: providerName }
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
writeConfig(paths.openclawConfig, config);
|
|
329
|
-
console.log(chalk.green(`\n✅ 模型已切换为: ${modelConfig.name}`));
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// ============ 设置 API Key ============
|
|
333
|
-
async function setApiKey(paths) {
|
|
334
|
-
console.log(chalk.cyan('🔑 设置 API Key\n'));
|
|
335
|
-
|
|
336
|
-
const { apiKey } = await inquirer.prompt([{
|
|
337
|
-
type: 'password',
|
|
338
|
-
name: 'apiKey',
|
|
339
|
-
message: '请输入 API Key:',
|
|
340
|
-
mask: '*',
|
|
341
|
-
validate: input => input.trim() !== '' || 'API Key 不能为空'
|
|
342
|
-
}]);
|
|
343
|
-
|
|
344
|
-
let config = readConfig(paths.openclawConfig);
|
|
345
|
-
|
|
346
|
-
if (!config?.models?.providers) {
|
|
347
|
-
console.log(chalk.yellow('⚠️ 请先选择节点'));
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
336
|
+
const provider = config.models.providers[apiConfig.providerName];
|
|
337
|
+
const currentModelId = provider.models?.[0]?.id || models[0].id;
|
|
338
|
+
const modelConfig = models.find(m => m.id === currentModelId) || models[0];
|
|
350
339
|
|
|
351
|
-
//
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
340
|
+
// 设置为主模型
|
|
341
|
+
const modelKey = `${apiConfig.providerName}/${modelConfig.id}`;
|
|
342
|
+
config.agents.defaults.model.primary = modelKey;
|
|
343
|
+
config.agents.defaults.model.fallbacks = [];
|
|
356
344
|
|
|
357
345
|
writeConfig(paths.openclawConfig, config);
|
|
358
346
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
fs.mkdirSync(authDir, { recursive: true });
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
let authProfiles = {};
|
|
366
|
-
if (fs.existsSync(paths.authProfiles)) {
|
|
367
|
-
try { authProfiles = JSON.parse(fs.readFileSync(paths.authProfiles, 'utf8')); } catch {}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (providerName) {
|
|
371
|
-
authProfiles[`${providerName}:default`] = { apiKey: apiKey.trim() };
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
fs.writeFileSync(paths.authProfiles, JSON.stringify(authProfiles, null, 2), 'utf8');
|
|
375
|
-
|
|
376
|
-
console.log(chalk.green('\n✅ API Key 已保存'));
|
|
347
|
+
console.log(chalk.green(`✅ 已激活 ${typeLabel}`));
|
|
348
|
+
console.log(chalk.cyan(` 节点: ${provider.baseUrl}`));
|
|
349
|
+
console.log(chalk.gray(` 模型: ${modelConfig.name}`));
|
|
377
350
|
}
|
|
378
351
|
|
|
379
352
|
// ============ 查看配置 ============
|
|
@@ -387,23 +360,46 @@ async function viewConfig(paths) {
|
|
|
387
360
|
return;
|
|
388
361
|
}
|
|
389
362
|
|
|
390
|
-
//
|
|
363
|
+
// 当前激活
|
|
391
364
|
const primary = config.agents?.defaults?.model?.primary || '未设置';
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
365
|
+
const isClaudeActive = primary.startsWith('yunyi-claude');
|
|
366
|
+
const isCodexActive = primary.startsWith('yunyi-codex');
|
|
367
|
+
|
|
368
|
+
console.log(chalk.yellow('当前激活:'));
|
|
369
|
+
if (isClaudeActive) {
|
|
370
|
+
console.log(chalk.blue(` 🔵 Claude: ${primary}`));
|
|
371
|
+
} else if (isCodexActive) {
|
|
372
|
+
console.log(chalk.green(` 🟢 Codex: ${primary}`));
|
|
373
|
+
} else {
|
|
374
|
+
console.log(` ${primary}`);
|
|
375
|
+
}
|
|
376
|
+
console.log('');
|
|
377
|
+
|
|
378
|
+
// Claude 节点
|
|
379
|
+
console.log(chalk.yellow('Claude 节点:'));
|
|
380
|
+
const claudeProvider = config.models?.providers?.['yunyi-claude'];
|
|
381
|
+
if (claudeProvider) {
|
|
382
|
+
const hasKey = claudeProvider.apiKey ? chalk.green('✓') : chalk.red('✗');
|
|
383
|
+
const model = claudeProvider.models?.[0]?.name || 'N/A';
|
|
384
|
+
console.log(` URL: ${claudeProvider.baseUrl}`);
|
|
385
|
+
console.log(` 模型: ${model}`);
|
|
386
|
+
console.log(` API Key: ${hasKey}`);
|
|
387
|
+
} else {
|
|
388
|
+
console.log(chalk.gray(' 未配置'));
|
|
389
|
+
}
|
|
390
|
+
console.log('');
|
|
391
|
+
|
|
392
|
+
// Codex 节点
|
|
393
|
+
console.log(chalk.yellow('Codex 节点:'));
|
|
394
|
+
const codexProvider = config.models?.providers?.['yunyi-codex'];
|
|
395
|
+
if (codexProvider) {
|
|
396
|
+
const hasKey = codexProvider.apiKey ? chalk.green('✓') : chalk.red('✗');
|
|
397
|
+
const model = codexProvider.models?.[0]?.name || 'N/A';
|
|
398
|
+
console.log(` URL: ${codexProvider.baseUrl}`);
|
|
399
|
+
console.log(` 模型: ${model}`);
|
|
400
|
+
console.log(` API Key: ${hasKey}`);
|
|
401
|
+
} else {
|
|
402
|
+
console.log(chalk.gray(' 未配置'));
|
|
407
403
|
}
|
|
408
404
|
console.log('');
|
|
409
405
|
|