yymaxapi 1.0.102 → 1.0.103

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.
Files changed (2) hide show
  1. package/bin/yymaxapi.js +288 -155
  2. package/package.json +1 -1
package/bin/yymaxapi.js CHANGED
@@ -1075,6 +1075,55 @@ function writeOpencodeConfig(claudeBaseUrl, codexBaseUrl, apiKey, defaultModelKe
1075
1075
  return configPath;
1076
1076
  }
1077
1077
 
1078
+ function writeHermesConfig(baseUrl, apiKey, modelId = getDefaultClaudeModel().id) {
1079
+ const dataDir = getHermesDataDir();
1080
+ const configPath = path.join(dataDir, 'config.yaml');
1081
+ const envPath = getHermesEnvPath();
1082
+ const existingConfigPath = getHermesConfigPath();
1083
+ const wslMirror = getHermesWslMirrorInfo();
1084
+
1085
+ let existingConfigRaw = readTextIfExists(existingConfigPath);
1086
+ if (!existingConfigRaw && wslMirror.sourceConfigPath) {
1087
+ existingConfigRaw = readWslTextFile(wslMirror.sourceConfigPath);
1088
+ }
1089
+
1090
+ let existingEnvRaw = readTextIfExists(envPath);
1091
+ if (!existingEnvRaw && wslMirror.envPath) {
1092
+ existingEnvRaw = readWslTextFile(wslMirror.envPath);
1093
+ }
1094
+
1095
+ const normalizedBaseUrl = trimClaudeMessagesSuffix(String(baseUrl || '').trim()).replace(/\/+$/, '');
1096
+ const nextConfigRaw = upsertSimpleYamlConfig(existingConfigRaw, {
1097
+ provider: 'anthropic',
1098
+ model: modelId,
1099
+ base_url: normalizedBaseUrl
1100
+ });
1101
+ const nextEnvRaw = upsertEnvFile(existingEnvRaw, {
1102
+ ANTHROPIC_API_KEY: String(apiKey || '').trim()
1103
+ });
1104
+
1105
+ if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
1106
+ fs.writeFileSync(configPath, nextConfigRaw, 'utf8');
1107
+ fs.writeFileSync(envPath, nextEnvRaw, 'utf8');
1108
+
1109
+ if (wslMirror.configPath) {
1110
+ try { syncFileToWsl(configPath, wslMirror.configPath); } catch { /* best-effort */ }
1111
+ }
1112
+ if (wslMirror.envPath) {
1113
+ try { syncFileToWsl(envPath, wslMirror.envPath); } catch { /* best-effort */ }
1114
+ }
1115
+
1116
+ return {
1117
+ dataDir,
1118
+ configPath,
1119
+ envPath,
1120
+ wslConfigPath: wslMirror.configPath,
1121
+ wslEnvPath: wslMirror.envPath,
1122
+ modelId,
1123
+ baseUrl: normalizedBaseUrl
1124
+ };
1125
+ }
1126
+
1078
1127
  function syncExternalTools(type, baseUrl, apiKey, extra = {}) {
1079
1128
  const synced = [];
1080
1129
  try {
@@ -3400,6 +3449,177 @@ function getClaudeCodeSettingsPath() {
3400
3449
  return path.join(os.homedir(), '.claude', 'settings.json');
3401
3450
  }
3402
3451
 
3452
+ function readTextIfExists(filePath) {
3453
+ if (!filePath || !fs.existsSync(filePath)) return '';
3454
+ try {
3455
+ return fs.readFileSync(filePath, 'utf8');
3456
+ } catch {
3457
+ return '';
3458
+ }
3459
+ }
3460
+
3461
+ function getHermesDataDir() {
3462
+ const envDir = String(process.env.HERMES_DATA_DIR || '').trim();
3463
+ return envDir || path.join(os.homedir(), '.hermes');
3464
+ }
3465
+
3466
+ function getHermesConfigPath() {
3467
+ const dataDir = getHermesDataDir();
3468
+ const candidates = [
3469
+ path.join(dataDir, 'config.yaml'),
3470
+ path.join(dataDir, 'config.yml')
3471
+ ];
3472
+ return candidates.find(filePath => fs.existsSync(filePath)) || candidates[0];
3473
+ }
3474
+
3475
+ function getHermesEnvPath() {
3476
+ return path.join(getHermesDataDir(), '.env');
3477
+ }
3478
+
3479
+ function getHermesWslMirrorInfo() {
3480
+ if (process.platform !== 'win32' || !isWslAvailable()) {
3481
+ return {
3482
+ dataDir: null,
3483
+ sourceConfigPath: null,
3484
+ configPath: null,
3485
+ envPath: null
3486
+ };
3487
+ }
3488
+
3489
+ const wslHome = getWslHome() || '/root';
3490
+ const dataDir = path.posix.join(wslHome, '.hermes');
3491
+ const sourceConfigPath = findExistingWslFile([
3492
+ path.posix.join(dataDir, 'config.yaml'),
3493
+ path.posix.join(dataDir, 'config.yml')
3494
+ ]);
3495
+
3496
+ return {
3497
+ dataDir,
3498
+ sourceConfigPath,
3499
+ configPath: path.posix.join(dataDir, 'config.yaml'),
3500
+ envPath: path.posix.join(dataDir, '.env')
3501
+ };
3502
+ }
3503
+
3504
+ function readWslTextFile(filePath) {
3505
+ if (process.platform !== 'win32' || !filePath) return '';
3506
+ const quoted = shellQuote(filePath);
3507
+ const result = safeExec(`wsl -- bash -lc "cat ${quoted} 2>/dev/null"`, { timeout: 10000 });
3508
+ return result.ok ? (result.output || result.stdout || '') : '';
3509
+ }
3510
+
3511
+ function parseSimpleYamlConfig(text) {
3512
+ const config = {};
3513
+ for (const line of String(text || '').split(/\r?\n/)) {
3514
+ const match = line.match(/^([A-Za-z0-9_-]+)\s*:\s*(.*?)\s*$/);
3515
+ if (!match) continue;
3516
+ let value = match[2].trim();
3517
+ if (
3518
+ (value.startsWith('"') && value.endsWith('"'))
3519
+ || (value.startsWith('\'') && value.endsWith('\''))
3520
+ ) {
3521
+ try {
3522
+ value = JSON.parse(value);
3523
+ } catch {
3524
+ value = value.slice(1, -1);
3525
+ }
3526
+ }
3527
+ config[match[1]] = value;
3528
+ }
3529
+ return config;
3530
+ }
3531
+
3532
+ function serializeYamlScalar(value) {
3533
+ return JSON.stringify(String(value ?? ''));
3534
+ }
3535
+
3536
+ function upsertSimpleYamlConfig(text, entries) {
3537
+ const lines = String(text || '').replace(/\r\n/g, '\n').split('\n');
3538
+ const normalized = lines.length === 1 && lines[0] === '' ? [] : [...lines];
3539
+ const seen = new Set();
3540
+
3541
+ for (let i = 0; i < normalized.length; i += 1) {
3542
+ const match = normalized[i].match(/^([A-Za-z0-9_-]+)\s*:\s*.*$/);
3543
+ if (!match) continue;
3544
+ const key = match[1];
3545
+ if (!Object.prototype.hasOwnProperty.call(entries, key)) continue;
3546
+ normalized[i] = `${key}: ${serializeYamlScalar(entries[key])}`;
3547
+ seen.add(key);
3548
+ }
3549
+
3550
+ for (const key of Object.keys(entries)) {
3551
+ if (!seen.has(key)) {
3552
+ normalized.push(`${key}: ${serializeYamlScalar(entries[key])}`);
3553
+ }
3554
+ }
3555
+
3556
+ while (normalized.length > 0 && normalized[normalized.length - 1] === '') {
3557
+ normalized.pop();
3558
+ }
3559
+
3560
+ return normalized.join('\n') + '\n';
3561
+ }
3562
+
3563
+ function parseEnvFile(text) {
3564
+ const entries = {};
3565
+ for (const line of String(text || '').split(/\r?\n/)) {
3566
+ const trimmed = line.trim();
3567
+ if (!trimmed || trimmed.startsWith('#')) continue;
3568
+ const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
3569
+ if (!match) continue;
3570
+ let value = match[2].trim();
3571
+ if (
3572
+ (value.startsWith('"') && value.endsWith('"'))
3573
+ || (value.startsWith('\'') && value.endsWith('\''))
3574
+ ) {
3575
+ try {
3576
+ value = JSON.parse(value);
3577
+ } catch {
3578
+ value = value.slice(1, -1);
3579
+ }
3580
+ }
3581
+ entries[match[1]] = value;
3582
+ }
3583
+ return entries;
3584
+ }
3585
+
3586
+ function upsertEnvFile(text, entries) {
3587
+ const lines = String(text || '').replace(/\r\n/g, '\n').split('\n');
3588
+ const normalized = lines.length === 1 && lines[0] === '' ? [] : [...lines];
3589
+ const seen = new Set();
3590
+
3591
+ for (let i = 0; i < normalized.length; i += 1) {
3592
+ const match = normalized[i].match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=/);
3593
+ if (!match) continue;
3594
+ const key = match[1];
3595
+ if (!Object.prototype.hasOwnProperty.call(entries, key)) continue;
3596
+ normalized[i] = `${key}=${JSON.stringify(String(entries[key] ?? ''))}`;
3597
+ seen.add(key);
3598
+ }
3599
+
3600
+ for (const key of Object.keys(entries)) {
3601
+ if (!seen.has(key)) {
3602
+ normalized.push(`${key}=${JSON.stringify(String(entries[key] ?? ''))}`);
3603
+ }
3604
+ }
3605
+
3606
+ while (normalized.length > 0 && normalized[normalized.length - 1] === '') {
3607
+ normalized.pop();
3608
+ }
3609
+
3610
+ return normalized.join('\n') + '\n';
3611
+ }
3612
+
3613
+ function readHermesYamlConfig(configPath = getHermesConfigPath()) {
3614
+ const raw = readTextIfExists(configPath);
3615
+ return {
3616
+ configPath,
3617
+ configured: !!raw || fs.existsSync(configPath),
3618
+ config: parseSimpleYamlConfig(raw),
3619
+ raw
3620
+ };
3621
+ }
3622
+
3403
3623
  function getOpencodeConfigPath() {
3404
3624
  const home = os.homedir();
3405
3625
  return process.platform === 'win32'
@@ -5096,183 +5316,88 @@ async function activateCodex(paths, args = {}) {
5096
5316
  }
5097
5317
  }
5098
5318
 
5099
- // ============ yycode 精简模式(零交互一键配置) ============
5100
- async function yycodeQuickSetup(paths) {
5101
- console.log(chalk.cyan.bold('\n yycode 一键配置\n'));
5319
+ // ============ 单独配置 Hermes ============
5320
+ async function activateHermes(paths, args = {}) {
5321
+ console.log(chalk.cyan.bold('\n🔧 配置 Hermes\n'));
5322
+ const selectedModel = await promptClaudeModelSelection(args, '选择 Hermes 默认 Claude 模型:');
5102
5323
 
5103
- const claudeApiConfig = API_CONFIG.claude;
5104
- const codexApiConfig = API_CONFIG.codex;
5105
- const claudeProviderName = claudeApiConfig.providerName;
5106
- const codexProviderName = codexApiConfig.providerName;
5324
+ const shouldTest = !(args['no-test'] || args.noTest);
5325
+ let selectedEndpoint = ENDPOINTS[0];
5107
5326
 
5108
- // ---- 探测已有 key ----
5109
- let apiKey = '';
5110
- let keySource = '';
5327
+ if (shouldTest) {
5328
+ console.log(chalk.cyan('📡 开始测速节点...\n'));
5329
+ const speedResult = await testAllEndpoints(ENDPOINTS, { autoFallback: true });
5330
+ const sorted = speedResult.ranked || [];
5331
+ if (sorted.length > 0) {
5332
+ const defaultEp = ENDPOINTS[0];
5333
+ const { selectedIndex } = await inquirer.prompt([{
5334
+ type: 'list',
5335
+ name: 'selectedIndex',
5336
+ message: '选择节点:',
5337
+ choices: [
5338
+ { name: `* 使用默认节点 (${defaultEp.name})`, value: -1 },
5339
+ new inquirer.Separator(' ---- 或按测速结果选择 ----'),
5340
+ ...sorted.map((e, i) => ({
5341
+ name: `${e.name} - ${e.latency}ms (评分:${e.score})`,
5342
+ value: i
5343
+ }))
5344
+ ]
5345
+ }]);
5346
+ selectedEndpoint = selectedIndex === -1 ? defaultEp : sorted[selectedIndex];
5347
+ } else {
5348
+ console.log(chalk.red('\n⚠️ 所有节点均不可达'));
5349
+ const { proceed } = await inquirer.prompt([{
5350
+ type: 'confirm', name: 'proceed',
5351
+ message: '仍要使用默认节点配置吗?', default: false
5352
+ }]);
5353
+ if (!proceed) { console.log(chalk.gray('已取消')); return; }
5354
+ }
5355
+ }
5111
5356
 
5112
- // 1. CLI 参数
5113
- const args = parseArgs(process.argv.slice(2));
5114
5357
  const directKey = (args['api-key'] || args.apiKey || args.key || '').toString().trim();
5358
+ let apiKey;
5115
5359
  if (directKey) {
5116
5360
  apiKey = directKey;
5117
- keySource = '命令行参数';
5118
- }
5119
-
5120
- // 2. 环境变量
5121
- if (!apiKey) {
5122
- const envKeys = ['OPENCLAW_CLAUDE_KEY', 'OPENCLAW_CODEX_KEY', 'CLAUDE_API_KEY', 'OPENAI_API_KEY', 'OPENCLAW_API_KEY'];
5123
- for (const k of envKeys) {
5124
- if (process.env[k] && process.env[k].trim()) {
5125
- apiKey = process.env[k].trim();
5126
- keySource = `环境变量 ${k}`;
5127
- break;
5128
- }
5129
- }
5130
- }
5131
-
5132
- // 3. 已有 OpenClaw 配置(云翼 Claude Code 密钥)
5133
- if (!apiKey) {
5134
- try {
5135
- const config = readConfig(paths.openclawConfig);
5136
- if (config && config.models && config.models.providers) {
5137
- // 优先取 claude-yunyi 的 key
5138
- const preferredOrder = [claudeProviderName, codexProviderName];
5139
- for (const name of preferredOrder) {
5140
- const p = config.models.providers[name];
5141
- if (p && p.apiKey && p.apiKey.trim()) {
5142
- apiKey = p.apiKey.trim();
5143
- keySource = `已有配置 (${name})`;
5144
- break;
5145
- }
5146
- }
5147
- // 其他 provider 的 key
5148
- if (!apiKey) {
5149
- for (const [name, p] of Object.entries(config.models.providers)) {
5150
- if (p.apiKey && p.apiKey.trim()) {
5151
- apiKey = p.apiKey.trim();
5152
- keySource = `已有配置 (${name})`;
5153
- break;
5154
- }
5155
- }
5156
- }
5157
- }
5158
- } catch { /* ignore */ }
5159
- }
5160
-
5161
- // 4. 都没有,提示输入
5162
- if (apiKey) {
5163
- const masked = apiKey.length > 8 ? apiKey.slice(0, 5) + '***' + apiKey.slice(-3) : '***';
5164
- console.log(chalk.green(`✓ 已检测到 API Key: ${masked} (来源: ${keySource})`));
5165
5361
  } else {
5166
- apiKey = await promptApiKey('请输入 API Key:', '');
5167
- if (!apiKey) { console.log(chalk.gray('已取消')); return; }
5168
- }
5169
-
5170
- // ---- 静默测速选最快节点 ----
5171
- const speedSpinner = ora({ text: '正在测速选择最快节点...', spinner: 'dots' }).start();
5172
- let selectedEndpoint = ENDPOINTS[0];
5173
- try {
5174
- const speedResult = await testAllEndpoints(ENDPOINTS, { autoFallback: true });
5175
- if (speedResult.ranked && speedResult.ranked.length > 0) {
5176
- selectedEndpoint = speedResult.ranked[0];
5177
- }
5178
- speedSpinner.succeed(`节点: ${selectedEndpoint.name}`);
5179
- } catch {
5180
- speedSpinner.succeed(`节点: ${selectedEndpoint.name} (默认)`);
5362
+ const envKey = process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN || process.env.CLAUDE_API_KEY || '';
5363
+ apiKey = await promptApiKey('请输入 API Key:', envKey);
5181
5364
  }
5365
+ if (!apiKey) { console.log(chalk.gray('已取消')); return; }
5182
5366
 
5183
- // ---- 验证 key ----
5367
+ console.log('');
5184
5368
  const validation = await validateApiKey(selectedEndpoint.url, apiKey);
5185
5369
  if (!validation.valid) {
5186
- console.log(chalk.yellow('⚠ API Key 验证失败,仍将写入配置'));
5370
+ const { continueAnyway } = await inquirer.prompt([{
5371
+ type: 'confirm', name: 'continueAnyway',
5372
+ message: 'API Key 验证失败,是否仍然继续写入配置?', default: false
5373
+ }]);
5374
+ if (!continueAnyway) { console.log(chalk.gray('已取消')); return; }
5187
5375
  }
5188
5376
 
5189
- // ---- 写入两套配置 ----
5190
- const config = ensureConfigStructure(readConfig(paths.openclawConfig) || {});
5191
-
5192
- // 增量写入: 只添加/更新云翼 provider,保留其他已有配置
5193
-
5194
- // Claude 侧
5195
5377
  const claudeBaseUrl = buildFullUrl(selectedEndpoint.url, 'claude');
5196
- const claudeModelId = 'claude-sonnet-4-6';
5197
- const claudeModel = CLAUDE_MODELS.find(m => m.id === claudeModelId) || { id: claudeModelId, name: 'Claude Sonnet 4.6' };
5198
- const claudeModelKey = `${claudeProviderName}/${claudeModelId}`;
5199
-
5200
- config.models.providers[claudeProviderName] = {
5201
- baseUrl: claudeBaseUrl,
5202
- auth: DEFAULT_AUTH_MODE,
5203
- api: claudeApiConfig.api,
5204
- headers: {},
5205
- authHeader: false,
5206
- apiKey: apiKey.trim(),
5207
- models: [{ id: claudeModel.id, name: claudeModel.name, contextWindow: claudeApiConfig.contextWindow, maxTokens: claudeApiConfig.maxTokens }]
5208
- };
5209
- config.auth.profiles[`${claudeProviderName}:default`] = { provider: claudeProviderName, mode: 'api_key' };
5210
- config.agents.defaults.models[claudeModelKey] = { alias: claudeProviderName };
5211
-
5212
- // Codex 侧
5213
- const codexBaseUrl = buildFullUrl(selectedEndpoint.url, 'codex');
5214
- const codexModelId = CODEX_MODELS[0]?.id || 'gpt-5.4';
5215
- const codexModel = CODEX_MODELS.find(m => m.id === codexModelId) || { id: codexModelId, name: 'GPT 5.4' };
5216
- const codexModelKey = `${codexProviderName}/${codexModelId}`;
5217
-
5218
- config.models.providers[codexProviderName] = {
5219
- baseUrl: codexBaseUrl,
5220
- auth: DEFAULT_AUTH_MODE,
5221
- api: codexApiConfig.api,
5222
- headers: {},
5223
- authHeader: codexApiConfig.api.startsWith('openai'),
5224
- apiKey: apiKey.trim(),
5225
- models: [{ id: codexModel.id, name: codexModel.name, contextWindow: codexApiConfig.contextWindow, maxTokens: codexApiConfig.maxTokens }]
5226
- };
5227
- config.auth.profiles[`${codexProviderName}:default`] = { provider: codexProviderName, mode: 'api_key' };
5228
- config.agents.defaults.models[codexModelKey] = { alias: codexProviderName };
5229
-
5230
- // 默认主力: Codex, 备用: Claude
5231
- config.agents.defaults.model.primary = codexModelKey;
5232
- config.agents.defaults.model.fallbacks = [claudeModelKey];
5233
- const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
5234
- force: true,
5235
- endpointUrl: selectedEndpoint.url,
5236
- apiKey
5237
- });
5378
+ const writeSpinner = ora({ text: '正在写入 Hermes 配置...', spinner: 'dots' }).start();
5379
+ const hermesPaths = writeHermesConfig(claudeBaseUrl, apiKey, selectedModel.id);
5380
+ writeSpinner.succeed('Hermes 配置写入完成');
5238
5381
 
5239
- // ---- 写入 ----
5240
- const writeSpinner = ora({ text: '正在写入配置...', spinner: 'dots' }).start();
5241
- createTimestampedBackup(paths.openclawConfig, paths.configDir, 'yycode');
5242
- ensureGatewaySettings(config);
5243
- cleanupConflictingEnvVars(config, codexBaseUrl, apiKey);
5244
- writeConfigWithSync(paths, config);
5245
- updateAuthProfilesWithSync(paths, claudeProviderName, apiKey);
5246
- updateAuthProfilesWithSync(paths, codexProviderName, apiKey);
5247
- if (yunyiLayoutResult.applied) {
5248
- syncManagedYunyiAuthProfiles(paths, config);
5382
+ console.log(chalk.green('\n✅ Hermes 配置完成!'));
5383
+ console.log(chalk.cyan(` Base URL: ${hermesPaths.baseUrl}`));
5384
+ console.log(chalk.gray(` 模型: ${selectedModel.name} (${selectedModel.id})`));
5385
+ console.log(chalk.gray(' Provider: anthropic'));
5386
+ console.log(chalk.gray(' API Key: 已设置'));
5387
+ console.log(chalk.gray('\n 已写入:'));
5388
+ console.log(chalk.gray(` • ${hermesPaths.configPath}`));
5389
+ console.log(chalk.gray(` • ${hermesPaths.envPath}`));
5390
+ if (hermesPaths.wslConfigPath && hermesPaths.wslEnvPath) {
5391
+ console.log(chalk.gray(' • 已额外同步到 WSL ~/.hermes'));
5249
5392
  }
5250
- try { syncExternalTools('claude', claudeBaseUrl, apiKey, { codexBaseUrl, claudeModelId, opencodeDefaultModelKey: getExternalModelKey('codex', codexModelId) }); } catch { /* ignore */ }
5251
- try { syncExternalTools('codex', codexBaseUrl, apiKey, { modelId: codexModelId }); } catch { /* ignore */ }
5252
- writeSpinner.succeed('配置写入完成');
5253
-
5254
- // ---- 结果 ----
5255
- console.log(chalk.green('\n✅ 配置完成!'));
5256
- console.log(chalk.cyan(` Claude Code: ${claudeModel.name}`));
5257
- console.log(chalk.cyan(` Codex CLI: ${codexModel.name}`));
5258
- printYunyiOpenClawSwitchHint(yunyiLayoutResult);
5259
- console.log('');
5393
+ console.log(chalk.yellow('\n 提示: Hermes Desktop 的“测试连接”按钮当前可能仍按 /v1/models 检测;按钮不绿,不代表 runtime 不能正常对话'));
5394
+ console.log(chalk.yellow(' 建议: 重启 Hermes Desktop,或手动执行 hermes server --port 8787 验证 Hermes runtime'));
5260
5395
  }
5261
5396
 
5262
-
5263
5397
  // ============ 主程序 ============
5264
5398
  async function main() {
5265
5399
  console.clear();
5266
5400
 
5267
- // yycode 精简模式:检测到 yycode CLI 时直接走零交互流程
5268
- const isYYCode = path.basename(process.argv[1] || '').replace(/\.js$/, '') === 'yycode';
5269
- if (isYYCode) {
5270
- const paths = getConfigPath();
5271
- backupOriginalConfig(paths.openclawConfig, paths.configDir);
5272
- await yycodeQuickSetup(paths);
5273
- return;
5274
- }
5275
-
5276
5401
  console.log(chalk.cyan.bold('\n🔧 OpenClaw API 配置工具\n'));
5277
5402
 
5278
5403
  const paths = getConfigPath();
@@ -5296,6 +5421,10 @@ async function main() {
5296
5421
  await activateClaudeCode(paths, args);
5297
5422
  return;
5298
5423
  }
5424
+ if (args.preset === 'hermes' || args._.includes('preset-hermes') || args._.includes('hermes-preset')) {
5425
+ await activateHermes(paths, args);
5426
+ return;
5427
+ }
5299
5428
  if (args.preset === 'codex' || args._.includes('preset-codex') || args._.includes('codex-preset')) {
5300
5429
  await autoActivate(paths, { ...args, primary: 'codex' });
5301
5430
  return;
@@ -5324,6 +5453,7 @@ async function main() {
5324
5453
  new inquirer.Separator(' -- 一键配置 --'),
5325
5454
  { name: ' 配置 OpenClaw(Claude + Codex)', value: 'auto_activate' },
5326
5455
  { name: ' 配置 Claude Code', value: 'activate_claude_code' },
5456
+ { name: ' 配置 Hermes', value: 'activate_hermes' },
5327
5457
  { name: ' 配置 Opencode', value: 'activate_opencode' },
5328
5458
  { name: ' 配置 Codex CLI', value: 'activate_codex' },
5329
5459
  new inquirer.Separator(' -- 工具 --'),
@@ -5367,6 +5497,9 @@ async function main() {
5367
5497
  case 'activate_claude_code':
5368
5498
  await activateClaudeCode(paths);
5369
5499
  break;
5500
+ case 'activate_hermes':
5501
+ await activateHermes(paths);
5502
+ break;
5370
5503
  case 'activate_opencode':
5371
5504
  await activateOpencode(paths);
5372
5505
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yymaxapi",
3
- "version": "1.0.102",
3
+ "version": "1.0.103",
4
4
  "description": "跨平台 OpenClaw/Clawdbot 配置管理工具 - 管理中转地址、模型切换、API Keys、测速优化",
5
5
  "main": "bin/yymaxapi.js",
6
6
  "bin": {