yymaxapi 1.0.101 → 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 +333 -228
  2. package/package.json +1 -1
package/bin/yymaxapi.js CHANGED
@@ -412,6 +412,7 @@ async function promptOpencodeDefaultModelSelection(args = {}, message = '选择
412
412
  const BACKUP_FILENAME = 'openclaw-default.json.bak';
413
413
  const BACKUP_DIR_NAME = 'backups';
414
414
  const MAX_BACKUPS = 10;
415
+ const GATEWAY_CLI_NAMES = ['openclaw'];
415
416
  const EXTRA_BIN_DIRS = [
416
417
  path.join(os.homedir(), '.npm-global', 'bin'),
417
418
  path.join(os.homedir(), '.local', 'bin'),
@@ -1074,6 +1075,55 @@ function writeOpencodeConfig(claudeBaseUrl, codexBaseUrl, apiKey, defaultModelKe
1074
1075
  return configPath;
1075
1076
  }
1076
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
+
1077
1127
  function syncExternalTools(type, baseUrl, apiKey, extra = {}) {
1078
1128
  const synced = [];
1079
1129
  try {
@@ -1151,7 +1201,7 @@ function dockerCmd(cmd) {
1151
1201
  return _dockerNeedsSudo ? `sudo -n docker ${cmd}` : `docker ${cmd}`;
1152
1202
  }
1153
1203
 
1154
- // 查找包含 openclaw/clawdbot/moltbot 的运行中容器
1204
+ // 查找包含 openclaw 的运行中容器
1155
1205
  function findOpenclawDockerContainers() {
1156
1206
  if (_dockerContainerCache !== null) return _dockerContainerCache;
1157
1207
  if (!isDockerAvailable()) { _dockerContainerCache = []; return []; }
@@ -1169,7 +1219,7 @@ function findOpenclawDockerContainers() {
1169
1219
  const status = statusParts.join(' ');
1170
1220
  if (!id) continue;
1171
1221
 
1172
- // 检查容器内是否有 openclaw/clawdbot/moltbot(用 command -v 代替 which,兼容精简镜像)
1222
+ // 检查容器内是否有 openclaw(用 command -v 代替 which,兼容精简镜像)
1173
1223
  for (const cli of ['openclaw', 'clawdbot', 'moltbot']) {
1174
1224
  const check = safeExec(dockerCmd(`exec ${id} sh -c "command -v ${cli} 2>/dev/null || which ${cli} 2>/dev/null"`), { timeout: 8000 });
1175
1225
  if (check.ok && check.output && check.output.trim()) {
@@ -1343,7 +1393,7 @@ let _wslCliBinaryCache = undefined;
1343
1393
 
1344
1394
  function getWslCliBinary() {
1345
1395
  if (_wslCliBinaryCache !== undefined) return _wslCliBinaryCache;
1346
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
1396
+ for (const name of GATEWAY_CLI_NAMES) {
1347
1397
  try {
1348
1398
  const result = execFileSync('wsl', ['bash', '-lc', `which ${name} 2>/dev/null`], { encoding: 'utf8', timeout: 5000, stdio: 'pipe' }).trim();
1349
1399
  if (result && result.startsWith('/')) {
@@ -1457,7 +1507,7 @@ function cleanupAgentProcesses() {
1457
1507
  try {
1458
1508
  const { execSync } = require('child_process');
1459
1509
  if (process.platform === 'win32') {
1460
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
1510
+ for (const name of GATEWAY_CLI_NAMES) {
1461
1511
  try {
1462
1512
  execSync(`wmic process where "commandline like '%${name}%agent%' and not commandline like '%gateway%'" call terminate 2>nul`, { stdio: 'ignore', timeout: 10000 });
1463
1513
  } catch { /* ignore */ }
@@ -1468,18 +1518,18 @@ function cleanupAgentProcesses() {
1468
1518
  // WSL 内的 agent 也要清理
1469
1519
  if (detectGatewayEnv() === 'wsl') {
1470
1520
  try {
1471
- execSync('wsl -- bash -lc "pkill -f \'openclaw.*agent\' 2>/dev/null; pkill -f \'clawdbot.*agent\' 2>/dev/null; pkill -f \'moltbot.*agent\' 2>/dev/null; true"', { stdio: 'ignore', timeout: 10000 });
1521
+ execSync('wsl -- bash -lc "pkill -f \'openclaw.*agent\' 2>/dev/null || true"', { stdio: 'ignore', timeout: 10000 });
1472
1522
  } catch { /* ignore */ }
1473
1523
  }
1474
1524
  } else {
1475
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
1525
+ for (const name of GATEWAY_CLI_NAMES) {
1476
1526
  execSync(`pkill -f '${name}.*agent' 2>/dev/null || true`, { stdio: 'ignore' });
1477
1527
  }
1478
1528
  }
1479
1529
  // Docker 容器内的 agent 也要清理
1480
1530
  if (detectGatewayEnv() === 'docker' && _selectedDockerContainer) {
1481
1531
  try {
1482
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
1532
+ for (const name of GATEWAY_CLI_NAMES) {
1483
1533
  safeExec(dockerCmd(`exec ${_selectedDockerContainer.id} sh -c "pkill -f '${name}.*agent' 2>/dev/null || true"`), { timeout: 10000 });
1484
1534
  }
1485
1535
  } catch { /* ignore */ }
@@ -3209,6 +3259,14 @@ function shellQuote(value) {
3209
3259
  return `"${str.replace(/(["\\$`])/g, '\\$1')}"`;
3210
3260
  }
3211
3261
 
3262
+ function escapeSingleQuotedShell(value) {
3263
+ return String(value || '').replace(/'/g, "'\\''");
3264
+ }
3265
+
3266
+ function buildLoginShellCommand(shellPath, command) {
3267
+ return `${shellPath} -lc '${escapeSingleQuotedShell(command)}'`;
3268
+ }
3269
+
3212
3270
  function escapeXml(value) {
3213
3271
  return String(value)
3214
3272
  .replace(/&/g, '&')
@@ -3252,7 +3310,7 @@ function resolveCliBinary() {
3252
3310
  }
3253
3311
  }
3254
3312
 
3255
- const candidates = ['openclaw', 'clawdbot', 'moltbot'];
3313
+ const candidates = GATEWAY_CLI_NAMES;
3256
3314
  const isWin = process.platform === 'win32';
3257
3315
  const searchDirs = (process.env.PATH || '').split(path.delimiter).concat(EXTRA_BIN_DIRS);
3258
3316
  for (const name of candidates) {
@@ -3271,30 +3329,6 @@ function resolveCliBinary() {
3271
3329
  }
3272
3330
  }
3273
3331
 
3274
- const moltbotRoots = [];
3275
- if (process.env.MOLTBOT_ROOT) moltbotRoots.push(process.env.MOLTBOT_ROOT);
3276
- moltbotRoots.push('/opt/moltbot');
3277
- moltbotRoots.push('/opt/moltbot/app');
3278
-
3279
- const scriptCandidates = [];
3280
- for (const root of moltbotRoots) {
3281
- scriptCandidates.push(
3282
- path.join(root, 'moltbot.mjs'),
3283
- path.join(root, 'bin', 'moltbot.mjs'),
3284
- path.join(root, 'bin', 'moltbot.js'),
3285
- path.join(root, 'src', 'moltbot.mjs'),
3286
- path.join(root, 'src', 'moltbot.js')
3287
- );
3288
- }
3289
-
3290
- for (const candidate of scriptCandidates) {
3291
- try {
3292
- if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
3293
- return candidate;
3294
- }
3295
- } catch { }
3296
- }
3297
-
3298
3332
  // Fallback: use login shell to find the binary (loads .zshrc/.bashrc PATH)
3299
3333
  for (const name of candidates) {
3300
3334
  if (process.platform === 'win32') {
@@ -3361,8 +3395,7 @@ function getCliMeta() {
3361
3395
  const cliBinary = resolveCliBinary();
3362
3396
  const cliName = cliBinary ? path.basename(cliBinary) : '';
3363
3397
  const cliLower = cliName.toLowerCase();
3364
- const isMoltbot = cliLower.startsWith('moltbot') || cliLower.includes('moltbot');
3365
- const nodeMajor = isMoltbot ? 24 : 22;
3398
+ const nodeMajor = 22;
3366
3399
  return { cliBinary, cliName, nodeMajor };
3367
3400
  }
3368
3401
 
@@ -3416,6 +3449,177 @@ function getClaudeCodeSettingsPath() {
3416
3449
  return path.join(os.homedir(), '.claude', 'settings.json');
3417
3450
  }
3418
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
+
3419
3623
  function getOpencodeConfigPath() {
3420
3624
  const home = os.homedir();
3421
3625
  return process.platform === 'win32'
@@ -3875,7 +4079,7 @@ async function tryAutoStartGateway(port, allowAutoDaemon) {
3875
4079
  if (container.cliPath) dockerCmds.push(`${container.cliPath} gateway`);
3876
4080
  dockerCmds.push(`${container.cli} gateway`);
3877
4081
  }
3878
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
4082
+ for (const name of GATEWAY_CLI_NAMES) {
3879
4083
  dockerCmds.push(`${name} gateway`);
3880
4084
  }
3881
4085
 
@@ -3907,7 +4111,7 @@ async function tryAutoStartGateway(port, allowAutoDaemon) {
3907
4111
  }
3908
4112
  } else {
3909
4113
  if (daemonResult.reason === 'cli-not-found') {
3910
- console.log(chalk.red('❌ 未找到 openclaw/clawdbot/moltbot 命令,无法自动启动 Gateway'));
4114
+ console.log(chalk.red('❌ 未找到 openclaw 命令,无法自动启动 Gateway'));
3911
4115
  } else {
3912
4116
  console.log(chalk.red(`❌ 自动启动失败: ${daemonResult.reason}`));
3913
4117
  }
@@ -3951,15 +4155,9 @@ async function tryAutoStartGateway(port, allowAutoDaemon) {
3951
4155
  const nodeInfo = findCompatibleNode(nodeMajor);
3952
4156
  const env = { ...process.env, PATH: extendPathEnv(nodeInfo ? nodeInfo.path : null) };
3953
4157
  const useNode = cliBinary && nodeInfo && isNodeShebang(cliBinary);
3954
- const cliCmd = cliBinary
3955
- ? (useNode ? `"${nodeInfo.path}" "${cliBinary}" gateway` : `"${cliBinary}" gateway`)
3956
- : null;
4158
+ const candidates = buildGatewayCommands(cliBinary, nodeInfo, useNode, 'start');
3957
4159
 
3958
- const candidates = [];
3959
- if (cliCmd) candidates.push(cliCmd);
3960
- candidates.push('openclaw gateway', 'clawdbot gateway', 'moltbot gateway');
3961
-
3962
- for (const cmd of [...new Set(candidates)].filter(Boolean)) {
4160
+ for (const cmd of candidates) {
3963
4161
  console.log(chalk.yellow(`⚠️ 尝试启动 Gateway: ${cmd}`));
3964
4162
  if (spawnDetached(cmd, env)) {
3965
4163
  if (await waitForGateway(port, '127.0.0.1', 10000)) {
@@ -3972,8 +4170,8 @@ async function tryAutoStartGateway(port, allowAutoDaemon) {
3972
4170
  if (process.platform !== 'win32') {
3973
4171
  for (const sh of ['/bin/zsh', '/bin/bash']) {
3974
4172
  if (!fs.existsSync(sh)) continue;
3975
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3976
- const loginShellCmd = `${sh} -lc '${name} gateway'`;
4173
+ for (const cmd of candidates) {
4174
+ const loginShellCmd = buildLoginShellCommand(sh, cmd);
3977
4175
  console.log(chalk.yellow(`⚠️ 尝试启动 Gateway: ${loginShellCmd}`));
3978
4176
  if (spawnDetached(loginShellCmd, env)) {
3979
4177
  if (await waitForGateway(port, '127.0.0.1', 15000)) {
@@ -5118,183 +5316,88 @@ async function activateCodex(paths, args = {}) {
5118
5316
  }
5119
5317
  }
5120
5318
 
5121
- // ============ yycode 精简模式(零交互一键配置) ============
5122
- async function yycodeQuickSetup(paths) {
5123
- 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 模型:');
5124
5323
 
5125
- const claudeApiConfig = API_CONFIG.claude;
5126
- const codexApiConfig = API_CONFIG.codex;
5127
- const claudeProviderName = claudeApiConfig.providerName;
5128
- const codexProviderName = codexApiConfig.providerName;
5324
+ const shouldTest = !(args['no-test'] || args.noTest);
5325
+ let selectedEndpoint = ENDPOINTS[0];
5129
5326
 
5130
- // ---- 探测已有 key ----
5131
- let apiKey = '';
5132
- 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
+ }
5133
5356
 
5134
- // 1. CLI 参数
5135
- const args = parseArgs(process.argv.slice(2));
5136
5357
  const directKey = (args['api-key'] || args.apiKey || args.key || '').toString().trim();
5358
+ let apiKey;
5137
5359
  if (directKey) {
5138
5360
  apiKey = directKey;
5139
- keySource = '命令行参数';
5140
- }
5141
-
5142
- // 2. 环境变量
5143
- if (!apiKey) {
5144
- const envKeys = ['OPENCLAW_CLAUDE_KEY', 'OPENCLAW_CODEX_KEY', 'CLAUDE_API_KEY', 'OPENAI_API_KEY', 'OPENCLAW_API_KEY'];
5145
- for (const k of envKeys) {
5146
- if (process.env[k] && process.env[k].trim()) {
5147
- apiKey = process.env[k].trim();
5148
- keySource = `环境变量 ${k}`;
5149
- break;
5150
- }
5151
- }
5152
- }
5153
-
5154
- // 3. 已有 OpenClaw 配置(云翼 Claude Code 密钥)
5155
- if (!apiKey) {
5156
- try {
5157
- const config = readConfig(paths.openclawConfig);
5158
- if (config && config.models && config.models.providers) {
5159
- // 优先取 claude-yunyi 的 key
5160
- const preferredOrder = [claudeProviderName, codexProviderName];
5161
- for (const name of preferredOrder) {
5162
- const p = config.models.providers[name];
5163
- if (p && p.apiKey && p.apiKey.trim()) {
5164
- apiKey = p.apiKey.trim();
5165
- keySource = `已有配置 (${name})`;
5166
- break;
5167
- }
5168
- }
5169
- // 其他 provider 的 key
5170
- if (!apiKey) {
5171
- for (const [name, p] of Object.entries(config.models.providers)) {
5172
- if (p.apiKey && p.apiKey.trim()) {
5173
- apiKey = p.apiKey.trim();
5174
- keySource = `已有配置 (${name})`;
5175
- break;
5176
- }
5177
- }
5178
- }
5179
- }
5180
- } catch { /* ignore */ }
5181
- }
5182
-
5183
- // 4. 都没有,提示输入
5184
- if (apiKey) {
5185
- const masked = apiKey.length > 8 ? apiKey.slice(0, 5) + '***' + apiKey.slice(-3) : '***';
5186
- console.log(chalk.green(`✓ 已检测到 API Key: ${masked} (来源: ${keySource})`));
5187
5361
  } else {
5188
- apiKey = await promptApiKey('请输入 API Key:', '');
5189
- if (!apiKey) { console.log(chalk.gray('已取消')); return; }
5190
- }
5191
-
5192
- // ---- 静默测速选最快节点 ----
5193
- const speedSpinner = ora({ text: '正在测速选择最快节点...', spinner: 'dots' }).start();
5194
- let selectedEndpoint = ENDPOINTS[0];
5195
- try {
5196
- const speedResult = await testAllEndpoints(ENDPOINTS, { autoFallback: true });
5197
- if (speedResult.ranked && speedResult.ranked.length > 0) {
5198
- selectedEndpoint = speedResult.ranked[0];
5199
- }
5200
- speedSpinner.succeed(`节点: ${selectedEndpoint.name}`);
5201
- } catch {
5202
- 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);
5203
5364
  }
5365
+ if (!apiKey) { console.log(chalk.gray('已取消')); return; }
5204
5366
 
5205
- // ---- 验证 key ----
5367
+ console.log('');
5206
5368
  const validation = await validateApiKey(selectedEndpoint.url, apiKey);
5207
5369
  if (!validation.valid) {
5208
- 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; }
5209
5375
  }
5210
5376
 
5211
- // ---- 写入两套配置 ----
5212
- const config = ensureConfigStructure(readConfig(paths.openclawConfig) || {});
5213
-
5214
- // 增量写入: 只添加/更新云翼 provider,保留其他已有配置
5215
-
5216
- // Claude 侧
5217
5377
  const claudeBaseUrl = buildFullUrl(selectedEndpoint.url, 'claude');
5218
- const claudeModelId = 'claude-sonnet-4-6';
5219
- const claudeModel = CLAUDE_MODELS.find(m => m.id === claudeModelId) || { id: claudeModelId, name: 'Claude Sonnet 4.6' };
5220
- const claudeModelKey = `${claudeProviderName}/${claudeModelId}`;
5378
+ const writeSpinner = ora({ text: '正在写入 Hermes 配置...', spinner: 'dots' }).start();
5379
+ const hermesPaths = writeHermesConfig(claudeBaseUrl, apiKey, selectedModel.id);
5380
+ writeSpinner.succeed('Hermes 配置写入完成');
5221
5381
 
5222
- config.models.providers[claudeProviderName] = {
5223
- baseUrl: claudeBaseUrl,
5224
- auth: DEFAULT_AUTH_MODE,
5225
- api: claudeApiConfig.api,
5226
- headers: {},
5227
- authHeader: false,
5228
- apiKey: apiKey.trim(),
5229
- models: [{ id: claudeModel.id, name: claudeModel.name, contextWindow: claudeApiConfig.contextWindow, maxTokens: claudeApiConfig.maxTokens }]
5230
- };
5231
- config.auth.profiles[`${claudeProviderName}:default`] = { provider: claudeProviderName, mode: 'api_key' };
5232
- config.agents.defaults.models[claudeModelKey] = { alias: claudeProviderName };
5233
-
5234
- // Codex 侧
5235
- const codexBaseUrl = buildFullUrl(selectedEndpoint.url, 'codex');
5236
- const codexModelId = CODEX_MODELS[0]?.id || 'gpt-5.4';
5237
- const codexModel = CODEX_MODELS.find(m => m.id === codexModelId) || { id: codexModelId, name: 'GPT 5.4' };
5238
- const codexModelKey = `${codexProviderName}/${codexModelId}`;
5239
-
5240
- config.models.providers[codexProviderName] = {
5241
- baseUrl: codexBaseUrl,
5242
- auth: DEFAULT_AUTH_MODE,
5243
- api: codexApiConfig.api,
5244
- headers: {},
5245
- authHeader: codexApiConfig.api.startsWith('openai'),
5246
- apiKey: apiKey.trim(),
5247
- models: [{ id: codexModel.id, name: codexModel.name, contextWindow: codexApiConfig.contextWindow, maxTokens: codexApiConfig.maxTokens }]
5248
- };
5249
- config.auth.profiles[`${codexProviderName}:default`] = { provider: codexProviderName, mode: 'api_key' };
5250
- config.agents.defaults.models[codexModelKey] = { alias: codexProviderName };
5251
-
5252
- // 默认主力: Codex, 备用: Claude
5253
- config.agents.defaults.model.primary = codexModelKey;
5254
- config.agents.defaults.model.fallbacks = [claudeModelKey];
5255
- const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
5256
- force: true,
5257
- endpointUrl: selectedEndpoint.url,
5258
- apiKey
5259
- });
5260
-
5261
- // ---- 写入 ----
5262
- const writeSpinner = ora({ text: '正在写入配置...', spinner: 'dots' }).start();
5263
- createTimestampedBackup(paths.openclawConfig, paths.configDir, 'yycode');
5264
- ensureGatewaySettings(config);
5265
- cleanupConflictingEnvVars(config, codexBaseUrl, apiKey);
5266
- writeConfigWithSync(paths, config);
5267
- updateAuthProfilesWithSync(paths, claudeProviderName, apiKey);
5268
- updateAuthProfilesWithSync(paths, codexProviderName, apiKey);
5269
- if (yunyiLayoutResult.applied) {
5270
- 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'));
5271
5392
  }
5272
- try { syncExternalTools('claude', claudeBaseUrl, apiKey, { codexBaseUrl, claudeModelId, opencodeDefaultModelKey: getExternalModelKey('codex', codexModelId) }); } catch { /* ignore */ }
5273
- try { syncExternalTools('codex', codexBaseUrl, apiKey, { modelId: codexModelId }); } catch { /* ignore */ }
5274
- writeSpinner.succeed('配置写入完成');
5275
-
5276
- // ---- 结果 ----
5277
- console.log(chalk.green('\n✅ 配置完成!'));
5278
- console.log(chalk.cyan(` Claude Code: ${claudeModel.name}`));
5279
- console.log(chalk.cyan(` Codex CLI: ${codexModel.name}`));
5280
- printYunyiOpenClawSwitchHint(yunyiLayoutResult);
5281
- 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'));
5282
5395
  }
5283
5396
 
5284
-
5285
5397
  // ============ 主程序 ============
5286
5398
  async function main() {
5287
5399
  console.clear();
5288
5400
 
5289
- // yycode 精简模式:检测到 yycode CLI 时直接走零交互流程
5290
- const isYYCode = path.basename(process.argv[1] || '').replace(/\.js$/, '') === 'yycode';
5291
- if (isYYCode) {
5292
- const paths = getConfigPath();
5293
- backupOriginalConfig(paths.openclawConfig, paths.configDir);
5294
- await yycodeQuickSetup(paths);
5295
- return;
5296
- }
5297
-
5298
5401
  console.log(chalk.cyan.bold('\n🔧 OpenClaw API 配置工具\n'));
5299
5402
 
5300
5403
  const paths = getConfigPath();
@@ -5318,6 +5421,10 @@ async function main() {
5318
5421
  await activateClaudeCode(paths, args);
5319
5422
  return;
5320
5423
  }
5424
+ if (args.preset === 'hermes' || args._.includes('preset-hermes') || args._.includes('hermes-preset')) {
5425
+ await activateHermes(paths, args);
5426
+ return;
5427
+ }
5321
5428
  if (args.preset === 'codex' || args._.includes('preset-codex') || args._.includes('codex-preset')) {
5322
5429
  await autoActivate(paths, { ...args, primary: 'codex' });
5323
5430
  return;
@@ -5346,6 +5453,7 @@ async function main() {
5346
5453
  new inquirer.Separator(' -- 一键配置 --'),
5347
5454
  { name: ' 配置 OpenClaw(Claude + Codex)', value: 'auto_activate' },
5348
5455
  { name: ' 配置 Claude Code', value: 'activate_claude_code' },
5456
+ { name: ' 配置 Hermes', value: 'activate_hermes' },
5349
5457
  { name: ' 配置 Opencode', value: 'activate_opencode' },
5350
5458
  { name: ' 配置 Codex CLI', value: 'activate_codex' },
5351
5459
  new inquirer.Separator(' -- 工具 --'),
@@ -5389,6 +5497,9 @@ async function main() {
5389
5497
  case 'activate_claude_code':
5390
5498
  await activateClaudeCode(paths);
5391
5499
  break;
5500
+ case 'activate_hermes':
5501
+ await activateHermes(paths);
5502
+ break;
5392
5503
  case 'activate_opencode':
5393
5504
  await activateOpencode(paths);
5394
5505
  break;
@@ -5984,7 +6095,7 @@ async function testConnection(paths, args = {}) {
5984
6095
  const container = await selectDockerContainer();
5985
6096
  if (!container) {
5986
6097
  console.log(chalk.red('❌ 未找到包含 OpenClaw/Clawdbot/Moltbot 的 Docker 容器'));
5987
- console.log(chalk.gray(' 请确保容器正在运行且已安装 openclaw/clawdbot/moltbot'));
6098
+ console.log(chalk.gray(' 请确保容器正在运行且已安装 openclaw'));
5988
6099
  return;
5989
6100
  }
5990
6101
  }
@@ -6024,8 +6135,6 @@ async function testConnection(paths, args = {}) {
6024
6135
  console.log(chalk.gray(` 然后执行: ${_selectedDockerContainer.cli === 'node' ? `node ${_selectedDockerContainer.cliPath}` : _selectedDockerContainer.cli} gateway`));
6025
6136
  } else {
6026
6137
  console.log(chalk.gray(' 请在新的终端执行: openclaw gateway'));
6027
- console.log(chalk.gray(' 或: clawdbot gateway'));
6028
- console.log(chalk.gray(' 或: moltbot gateway'));
6029
6138
  }
6030
6139
  return;
6031
6140
  }
@@ -6035,7 +6144,7 @@ async function testConnection(paths, args = {}) {
6035
6144
  if (!restartOk) {
6036
6145
  console.log(chalk.yellow('⚠️ Gateway 未能通过常规方式重启,当前使用的可能是之前的 Gateway 进程'));
6037
6146
  console.log(chalk.yellow(' 新配置可能未生效。如 bot 不回复,请手动重启 Gateway:'));
6038
- console.log(chalk.gray(' openclaw gateway restart 或 clawdbot gateway restart'));
6147
+ console.log(chalk.gray(' openclaw gateway restart'));
6039
6148
  }
6040
6149
 
6041
6150
  // 步骤2: 通过 Gateway 端点测试(优先使用 CLI agent)
@@ -6160,7 +6269,7 @@ async function testConnection(paths, args = {}) {
6160
6269
  console.log(chalk.gray(`\n 建议操作:`));
6161
6270
  console.log(chalk.gray(` 1) 复制最新地址并重新打开浏览器(不要用旧书签)`));
6162
6271
  console.log(chalk.cyan(` http://127.0.0.1:${gatewayPort}/#token=${gatewayToken}`));
6163
- console.log(chalk.gray(` 2) 执行 Gateway 重启:openclaw gateway restart / clawdbot gateway restart`));
6272
+ console.log(chalk.gray(` 2) 执行 Gateway 重启:openclaw gateway restart`));
6164
6273
  console.log(chalk.gray(` 3) 若仍 401,检查是否存在多个配置目录(.openclaw 与 .clawdbot)`));
6165
6274
  }
6166
6275
 
@@ -6170,7 +6279,7 @@ async function testConnection(paths, args = {}) {
6170
6279
  console.log(chalk.gray(` 如遇问题,尝试更新 Gateway: npm install -g openclaw@latest && openclaw gateway restart`));
6171
6280
  }
6172
6281
 
6173
- console.log(chalk.gray(`\n 提示: 如果 Gateway 未运行,请执行: openclaw gateway / clawdbot gateway / moltbot gateway`));
6282
+ console.log(chalk.gray(`\n 提示: 如果 Gateway 未运行,请执行: openclaw gateway`));
6174
6283
  }
6175
6284
  } catch (error) {
6176
6285
  console.log(chalk.red(`❌ 测试失败: ${error.message}`));
@@ -6214,7 +6323,7 @@ function buildDockerInnerCmds(container, verb) {
6214
6323
  if (container.cliPath) cmds.push(`${container.cliPath} ${verb}`);
6215
6324
  cmds.push(`${container.cli} ${verb}`);
6216
6325
  }
6217
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
6326
+ for (const name of GATEWAY_CLI_NAMES) {
6218
6327
  cmds.push(`${name} ${verb}`);
6219
6328
  }
6220
6329
  return [...new Set(cmds)].filter(Boolean);
@@ -6247,7 +6356,7 @@ async function restartGatewayDocker(gatewayPort, silent = false) {
6247
6356
  // 策略 B:杀容器内旧进程 → spawn 启动新 Gateway → 端口探测
6248
6357
  if (!silent) console.log(chalk.gray(' Docker 内常规重启未生效,尝试杀进程后重新启动...'));
6249
6358
  try {
6250
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
6359
+ for (const name of GATEWAY_CLI_NAMES) {
6251
6360
  safeExec(dockerCmd(`exec ${cid} sh -c "pkill -f '${name}.*gateway' 2>/dev/null || true"`), { timeout: 5000 });
6252
6361
  }
6253
6362
  safeExec(dockerCmd(`exec ${cid} sh -c "lsof -ti :${gatewayPort} 2>/dev/null | xargs -r kill -9 2>/dev/null || true"`), { timeout: 5000 });
@@ -6271,7 +6380,7 @@ async function restartGatewayDocker(gatewayPort, silent = false) {
6271
6380
  async function restartGatewayWsl(gatewayPort, silent = false) {
6272
6381
  if (!silent) console.log(chalk.gray(' [检测] Gateway 运行在 WSL 中'));
6273
6382
  const wslCli = getWslCliBinary();
6274
- const names = ['openclaw', 'clawdbot', 'moltbot'];
6383
+ const names = GATEWAY_CLI_NAMES;
6275
6384
  const portWasOpenBefore = await isPortOpen(gatewayPort, '127.0.0.1', 500);
6276
6385
  const beforeSignature = portWasOpenBefore ? getGatewayProcessSignature(gatewayPort, 'wsl') : '';
6277
6386
 
@@ -6364,13 +6473,11 @@ async function restartGatewayNative(silent = false) {
6364
6473
  // 策略 C:用 login shell 启动(加载 nvm/fnm 等 PATH)
6365
6474
  if (process.platform !== 'win32') {
6366
6475
  if (!silent) console.log(chalk.gray(' 尝试通过 login shell 启动...'));
6367
- let launched = false;
6368
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
6369
- if (launched) break;
6370
- for (const sh of ['/bin/zsh', '/bin/bash']) {
6371
- if (!fs.existsSync(sh)) continue;
6372
- if (spawnDetached(`${sh} -lc '${name} gateway'`, env)) {
6373
- launched = true;
6476
+ for (const sh of ['/bin/zsh', '/bin/bash']) {
6477
+ if (!fs.existsSync(sh)) continue;
6478
+ for (const cmd of startCmds) {
6479
+ const loginShellCmd = buildLoginShellCommand(sh, cmd);
6480
+ if (spawnDetached(loginShellCmd, env)) {
6374
6481
  if (await waitForGateway(gatewayPort, '127.0.0.1', 10000)) {
6375
6482
  if (!silent) console.log(chalk.green('✅ Gateway 已重启 (login shell)'));
6376
6483
  return true;
@@ -6383,10 +6490,8 @@ async function restartGatewayNative(silent = false) {
6383
6490
 
6384
6491
  // 全部失败,输出诊断
6385
6492
  if (!silent) {
6386
- console.log(chalk.red(`❌ 重启失败: 找不到 openclaw/clawdbot/moltbot 命令`));
6493
+ console.log(chalk.red('❌ 重启失败: 找不到 openclaw 命令'));
6387
6494
  console.log(chalk.gray(` 请手动运行: openclaw gateway restart`));
6388
- console.log(chalk.gray(` 或: clawdbot gateway restart`));
6389
- console.log(chalk.gray(` 或: moltbot gateway restart`));
6390
6495
  printGatewayDiagnostics(resolved);
6391
6496
  }
6392
6497
  return false;
@@ -6404,7 +6509,7 @@ function buildGatewayCommands(resolved, nodeInfo, useNode, action) {
6404
6509
  }
6405
6510
  }
6406
6511
 
6407
- const names = ['openclaw', 'clawdbot', 'moltbot'];
6512
+ const names = GATEWAY_CLI_NAMES;
6408
6513
  for (const name of names) commands.push(`${name} ${verb}`);
6409
6514
 
6410
6515
  return [...new Set(commands)].filter(Boolean);
@@ -6421,13 +6526,13 @@ async function killGatewayProcesses(gatewayPort = 18789) {
6421
6526
  }
6422
6527
  }
6423
6528
  if (isWslAvailable()) {
6424
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
6529
+ for (const name of GATEWAY_CLI_NAMES) {
6425
6530
  safeExec(`wsl -- bash -c "pkill -f '${name}.*gateway' 2>/dev/null || true"`, { timeout: 5000 });
6426
6531
  }
6427
6532
  safeExec(`wsl -- bash -c "lsof -ti :${gatewayPort} 2>/dev/null | xargs -r kill -9 2>/dev/null || true"`, { timeout: 5000 });
6428
6533
  }
6429
6534
  } else {
6430
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
6535
+ for (const name of GATEWAY_CLI_NAMES) {
6431
6536
  safeExec(`pkill -f '${name}.*gateway' 2>/dev/null || true`);
6432
6537
  }
6433
6538
  const lsof = safeExec(`lsof -ti :${gatewayPort} 2>/dev/null`);
@@ -6444,14 +6549,14 @@ function printGatewayDiagnostics(resolved) {
6444
6549
  console.log(chalk.gray(`\n [诊断] resolveCliBinary = ${resolved || 'null'}`));
6445
6550
  const npmPrefix = safeExec('npm prefix -g');
6446
6551
  if (npmPrefix.ok) console.log(chalk.gray(` [诊断] npm prefix -g = ${npmPrefix.output}`));
6447
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
6552
+ for (const name of GATEWAY_CLI_NAMES) {
6448
6553
  const which = safeExec(process.platform === 'win32' ? `where ${name} 2>nul` : `/bin/zsh -lc "command -v ${name}" 2>/dev/null || /bin/bash -lc "command -v ${name}" 2>/dev/null`);
6449
6554
  if (which.ok && which.output) console.log(chalk.gray(` [诊断] ${name} -> ${which.output.split('\n')[0].trim()}`));
6450
6555
  }
6451
6556
  if (isDockerAvailable()) {
6452
6557
  const dContainers = findOpenclawDockerContainers();
6453
6558
  if (dContainers.length > 0) {
6454
- console.log(chalk.gray(` [诊断] Docker 容器 (含 openclaw/clawdbot/moltbot):`));
6559
+ console.log(chalk.gray(' [诊断] Docker 容器 (含 openclaw):'));
6455
6560
  for (const c of dContainers) {
6456
6561
  console.log(chalk.gray(` - ${c.name} (${c.image}) [${c.cli}: ${c.cliPath}]`));
6457
6562
  }
@@ -6542,7 +6647,7 @@ function testGatewayApi(port, token, model, endpoint = '/v1/chat/completions') {
6542
6647
 
6543
6648
  req.on('error', (e) => {
6544
6649
  if (e.code === 'ECONNREFUSED') {
6545
- resolve({ success: false, error: 'Gateway 未运行,请先启动: openclaw gateway / clawdbot gateway / moltbot gateway' });
6650
+ resolve({ success: false, error: 'Gateway 未运行,请先启动: openclaw gateway' });
6546
6651
  } else {
6547
6652
  resolve({ success: false, error: e.message });
6548
6653
  }
@@ -6569,14 +6674,14 @@ function testGatewayViaAgent(model, agentId = '') {
6569
6674
  const agentCmd = `${wslCli} ${subcommand}`;
6570
6675
  cmd = `wsl -- bash -c '${agentCmd.replace(/'/g, "'\\''")}'`;
6571
6676
  } else {
6572
- const agentCmd = `openclaw ${subcommand} 2>/dev/null || clawdbot ${subcommand} 2>/dev/null || moltbot ${subcommand}`;
6677
+ const agentCmd = `openclaw ${subcommand}`;
6573
6678
  cmd = `wsl -- bash -lc '${agentCmd.replace(/'/g, "'\\''")}'`;
6574
6679
  }
6575
6680
  execOpts = { timeout: 120000 };
6576
6681
  } else {
6577
6682
  const { cliBinary, nodeMajor } = getCliMeta();
6578
6683
  if (!cliBinary) {
6579
- resolve({ success: false, usedCli: false, error: '未找到 openclaw/clawdbot/moltbot 命令' });
6684
+ resolve({ success: false, usedCli: false, error: '未找到 openclaw 命令' });
6580
6685
  return;
6581
6686
  }
6582
6687
  const nodeInfo = findCompatibleNode(nodeMajor);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yymaxapi",
3
- "version": "1.0.101",
3
+ "version": "1.0.103",
4
4
  "description": "跨平台 OpenClaw/Clawdbot 配置管理工具 - 管理中转地址、模型切换、API Keys、测速优化",
5
5
  "main": "bin/yymaxapi.js",
6
6
  "bin": {