yymaxapi 1.0.78 → 1.0.80

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 +172 -65
  2. package/package.json +1 -1
package/bin/yymaxapi.js CHANGED
@@ -468,6 +468,53 @@ function buildAuthCandidates(baseDirs) {
468
468
  return auths;
469
469
  }
470
470
 
471
+ function buildPosixAuthCandidates(baseDirs) {
472
+ const auths = [];
473
+ for (const baseDir of baseDirs) {
474
+ auths.push(
475
+ path.posix.join(baseDir, 'agents', 'main', 'agent', 'auth-profiles.json'),
476
+ path.posix.join(baseDir, 'agent', 'auth-profiles.json')
477
+ );
478
+ }
479
+ return auths;
480
+ }
481
+
482
+ function findExistingWslFile(candidates = []) {
483
+ for (const candidate of candidates) {
484
+ const check = safeExec(`wsl -- bash -c "test -f '${candidate}' && echo yes"`, { timeout: 5000 });
485
+ if (check.ok && check.output.trim() === 'yes') {
486
+ return candidate;
487
+ }
488
+ }
489
+ return null;
490
+ }
491
+
492
+ function getWslMirrorInfo() {
493
+ if (process.platform !== 'win32' || !isWslAvailable()) {
494
+ return { configPath: null, authProfiles: null };
495
+ }
496
+
497
+ const wslHome = getWslHome() || '/root';
498
+ const configCandidates = [
499
+ `${wslHome}/.openclaw/openclaw.json`,
500
+ `${wslHome}/.openclaw/moltbot.json`,
501
+ `${wslHome}/.clawdbot/openclaw.json`,
502
+ `${wslHome}/.clawdbot/clawdbot.json`,
503
+ '/root/.openclaw/openclaw.json',
504
+ '/root/.openclaw/moltbot.json',
505
+ '/root/.clawdbot/openclaw.json',
506
+ '/root/.clawdbot/clawdbot.json'
507
+ ];
508
+
509
+ const configPath = findExistingWslFile(configCandidates);
510
+ const authBases = configPath
511
+ ? [path.posix.dirname(configPath)]
512
+ : [`${wslHome}/.openclaw`, `${wslHome}/.clawdbot`, '/root/.openclaw', '/root/.clawdbot'];
513
+ const authProfiles = findExistingWslFile(buildPosixAuthCandidates(authBases));
514
+
515
+ return { configPath, authProfiles };
516
+ }
517
+
471
518
  function getConfigPath() {
472
519
  const homeDir = os.homedir();
473
520
  const openclawStateDir = process.env.OPENCLAW_STATE_DIR || path.join(homeDir, '.openclaw');
@@ -516,29 +563,24 @@ function getConfigPath() {
516
563
  }
517
564
 
518
565
  // Windows + WSL: 尝试读取 WSL 内的配置文件
566
+ let wslConfigPath = null;
567
+ let wslAuthProfiles = null;
519
568
  if (process.platform === 'win32' && isWslAvailable()) {
520
569
  try {
521
- const wslHome = getWslHome() || '/root';
522
- const wslPaths = [
523
- `${wslHome}/.openclaw/openclaw.json`,
524
- `${wslHome}/.openclaw/moltbot.json`,
525
- `${wslHome}/.clawdbot/openclaw.json`,
526
- '/root/.openclaw/openclaw.json',
527
- '/root/.openclaw/moltbot.json',
528
- ];
529
- for (const wp of wslPaths) {
530
- const check = safeExec(`wsl -- bash -c "test -f '${wp}' && echo yes"`, { timeout: 5000 });
531
- if (check.ok && check.output.trim() === 'yes') {
532
- // 将 WSL 配置复制到 Windows 侧,保持同步
533
- const winDest = path.join(openclawStateDir, path.basename(wp));
534
- try {
535
- const content = execFileSync('wsl', ['bash', '-c', `cat '${wp}'`], { encoding: 'utf8', timeout: 10000, stdio: 'pipe' });
536
- if (!fs.existsSync(openclawStateDir)) fs.mkdirSync(openclawStateDir, { recursive: true });
570
+ const mirrorInfo = getWslMirrorInfo();
571
+ wslConfigPath = mirrorInfo.configPath;
572
+ wslAuthProfiles = mirrorInfo.authProfiles;
573
+
574
+ if (wslConfigPath) {
575
+ const winDest = path.join(openclawStateDir, path.basename(wslConfigPath));
576
+ try {
577
+ if (!fs.existsSync(openclawStateDir)) fs.mkdirSync(openclawStateDir, { recursive: true });
578
+ if (!fs.existsSync(winDest)) {
579
+ const content = execFileSync('wsl', ['bash', '-c', `cat '${wslConfigPath}'`], { encoding: 'utf8', timeout: 10000, stdio: 'pipe' });
537
580
  fs.writeFileSync(winDest, content, 'utf8');
538
- if (!candidates.includes(winDest)) candidates.unshift(winDest);
539
- } catch { }
540
- break;
541
- }
581
+ }
582
+ if (!candidates.includes(winDest)) candidates.unshift(winDest);
583
+ } catch { }
542
584
  }
543
585
  } catch { }
544
586
  }
@@ -559,6 +601,9 @@ function getConfigPath() {
559
601
  : [...baseAuthCandidates, ...moltbotAuthCandidates];
560
602
 
561
603
  const authProfiles = authCandidates.find(p => fs.existsSync(p)) || authCandidates[0];
604
+ const authSyncTargets = process.platform === 'win32'
605
+ ? [...new Set(buildAuthCandidates([openclawStateDir, clawdbotStateDir]))].filter(p => p !== authProfiles)
606
+ : [];
562
607
 
563
608
  const syncTargets = [];
564
609
  if (openclawConfig.startsWith(openclawStateDir) && fs.existsSync(clawdbotStateDir)) {
@@ -568,7 +613,7 @@ function getConfigPath() {
568
613
  );
569
614
  }
570
615
 
571
- return { openclawConfig, authProfiles, configDir, syncTargets };
616
+ return { openclawConfig, authProfiles, configDir, syncTargets, authSyncTargets, wslConfigPath, wslAuthProfiles };
572
617
  }
573
618
 
574
619
  // ============ 配置读写 ============
@@ -688,7 +733,7 @@ function writeCodexConfig(baseUrl, apiKey, modelId = 'gpt-5.4') {
688
733
  const markerEnd = '# <<< maxapi codex <<<';
689
734
  const topMarker = '# >>> maxapi codex top >>>';
690
735
  const topMarkerEnd = '# <<< maxapi codex top <<<';
691
- const providerKey = 'openclaw-relay';
736
+ const providerKey = 'yunyi-codex';
692
737
  // 确保 base_url 以 /v1 结尾(Codex CLI 要求)
693
738
  let normalizedUrl = baseUrl.replace(/\/+$/, '');
694
739
  if (!normalizedUrl.endsWith('/v1')) normalizedUrl += '/v1';
@@ -702,9 +747,11 @@ function writeCodexConfig(baseUrl, apiKey, modelId = 'gpt-5.4') {
702
747
  // 移除旧的 maxapi top-level block
703
748
  const topRe = new RegExp(`${topMarker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?${topMarkerEnd.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\n?`, 'g');
704
749
  existing = existing.replace(topRe, '');
705
- // 兼容旧版:移除散落在 section 内的旧 model/model_provider(来自旧版 marker block 残留)
750
+ // 兼容旧版:移除散落的 model/model_provider、旧 openclaw-relay、旧 yunyi opencode 标记块
706
751
  existing = existing.replace(/^model\s*=\s*"[^"]*"\s*$/gm, '');
707
752
  existing = existing.replace(/^model_provider\s*=\s*"[^"]*"\s*$/gm, '');
753
+ existing = existing.replace(/\[model_providers\.openclaw-relay\]\n(?:(?!\[)[^\n]*\n?)*/g, '');
754
+ existing = existing.replace(/# >>> yunyi opencode >>>[\s\S]*?# <<< yunyi opencode <<<\n?/g, '');
708
755
  existing = existing.replace(/\n{3,}/g, '\n\n').trim();
709
756
  }
710
757
 
@@ -720,7 +767,7 @@ function writeCodexConfig(baseUrl, apiKey, modelId = 'gpt-5.4') {
720
767
  const providerBlock = [
721
768
  marker,
722
769
  `[model_providers.${providerKey}]`,
723
- `name = "OpenClaw Relay"`,
770
+ `name = "云翼 Codex"`,
724
771
  `base_url = "${normalizedUrl}"`,
725
772
  `wire_api = "responses"`,
726
773
  `experimental_bearer_token = "${apiKey}"`,
@@ -819,31 +866,14 @@ function writeOpencodeConfig(claudeBaseUrl, codexBaseUrl, apiKey, modelId) {
819
866
  // 移除旧标记块(仅移除 yunyi opencode 自己的)
820
867
  content = content.replace(/# >>> yunyi opencode >>>[\s\S]*?# <<< yunyi opencode <<<\n?/g, '');
821
868
 
822
- // 移除旧的 yunyi model_providers 段落(包括 yunyi-cli 写入的 [model_providers.yunyi]
823
- content = content.replace(/\[model_providers\.yunyi[^\]]*\]\n(?:(?!\[)[^\n]*\n?)*/g, '');
824
-
825
- // 只追加 yunyi-codex provider(Claude 配置仅在 opencode.json 中,Codex CLI 不支持 wire_api=anthropic)
826
- const providers = [''];
827
- if (codexUrl) {
828
- providers.push(
829
- '# >>> yunyi opencode >>>',
830
- '[model_providers.yunyi-codex]',
831
- 'name = "云翼 Codex"',
832
- `base_url = "${codexUrl}"`,
833
- 'wire_api = "responses"',
834
- `experimental_bearer_token = "${apiKey}"`,
835
- 'requires_openai_auth = false',
836
- '# <<< yunyi opencode <<<'
837
- );
838
- }
869
+ // 移除旧版 yunyi-cli 写入的 [model_providers.yunyi] 和 [model_providers.yunyi-claude](不动 yunyi-codex,由 writeCodexConfig 管理)
870
+ content = content.replace(/\[model_providers\.yunyi\]\n(?:(?!\[)[^\n]*\n?)*/g, '');
871
+ content = content.replace(/\[model_providers\.yunyi-claude\]\n(?:(?!\[)[^\n]*\n?)*/g, '');
839
872
 
840
873
  // 清理多余空行
841
874
  content = content.replace(/\n{3,}/g, '\n\n').trim();
842
875
 
843
- // 追加 provider section
844
- if (providers.length > 1) {
845
- content += '\n\n' + providers.join('\n') + '\n';
846
- }
876
+ // yunyi-codex provider 由 writeCodexConfig 统一管理,此处不再重复写入
847
877
 
848
878
  fs.writeFileSync(codexConfigPath, content, 'utf8');
849
879
 
@@ -894,10 +924,15 @@ function syncClawdbotConfigs(paths, config) {
894
924
  function writeConfigWithSync(paths, config) {
895
925
  writeConfig(paths.openclawConfig, config);
896
926
  syncClawdbotConfigs(paths, config);
927
+ const hasWslMirror = process.platform === 'win32' && !!paths.wslConfigPath;
928
+ if (hasWslMirror) {
929
+ syncConfigToWsl(paths.openclawConfig, paths.wslConfigPath);
930
+ }
931
+ invalidateGatewayEnvCache();
897
932
  // 如果 Gateway 在 WSL,自动同步配置过去
898
933
  const gwEnv = detectGatewayEnv();
899
- if (gwEnv === 'wsl') {
900
- syncConfigToWsl(paths.openclawConfig);
934
+ if (!hasWslMirror && gwEnv === 'wsl') {
935
+ syncConfigToWsl(paths.openclawConfig, paths.wslConfigPath);
901
936
  }
902
937
  // 如果 Gateway 在 Docker 容器内,自动同步配置过去
903
938
  if (gwEnv === 'docker' && _selectedDockerContainer) {
@@ -1096,6 +1131,10 @@ let _gwEnvCache = null;
1096
1131
  let _wslAvailCache = null;
1097
1132
  let _wslHomeCache = undefined; // undefined = 未检测, null = 检测失败
1098
1133
 
1134
+ function invalidateGatewayEnvCache() {
1135
+ _gwEnvCache = null;
1136
+ }
1137
+
1099
1138
  function isWslAvailable() {
1100
1139
  if (process.platform !== 'win32') return false;
1101
1140
  if (_wslAvailCache !== null) return _wslAvailCache;
@@ -1221,16 +1260,12 @@ function execAsyncInGatewayEnv(cmd, options = {}) {
1221
1260
  }
1222
1261
 
1223
1262
  // 同步配置到 WSL(仅在 Gateway 环境为 WSL 时调用)
1224
- function syncConfigToWsl(windowsConfigPath) {
1263
+ function syncConfigToWsl(windowsConfigPath, wslDestPath) {
1225
1264
  try {
1226
1265
  const wslHome = getWslHome();
1227
- if (!wslHome) return;
1228
- const winNorm = windowsConfigPath.replace(/\\/g, '/');
1229
- const match = winNorm.match(/^([A-Za-z]):\/(.*)/);
1230
- if (!match) return;
1231
- const wslSrc = `/mnt/${match[1].toLowerCase()}/${match[2]}`;
1232
- const wslDest = `${wslHome}/.openclaw/openclaw.json`;
1233
- execFileSync('wsl', ['bash', '-c', `mkdir -p "${wslHome}/.openclaw" && cp "${wslSrc}" "${wslDest}"`], { timeout: 10000, stdio: 'pipe' });
1266
+ if (!wslHome && !wslDestPath) return;
1267
+ const target = wslDestPath || `${wslHome}/.openclaw/openclaw.json`;
1268
+ syncFileToWsl(windowsConfigPath, target);
1234
1269
  } catch { /* best-effort */ }
1235
1270
  }
1236
1271
 
@@ -1427,9 +1462,70 @@ function readAuthStore(authProfilesPath) {
1427
1462
  }
1428
1463
 
1429
1464
  function writeAuthStore(authProfilesPath, store) {
1465
+ const authDir = path.dirname(authProfilesPath);
1466
+ if (!fs.existsSync(authDir)) {
1467
+ fs.mkdirSync(authDir, { recursive: true });
1468
+ }
1430
1469
  fs.writeFileSync(authProfilesPath, JSON.stringify(store, null, 2), 'utf8');
1431
1470
  }
1432
1471
 
1472
+ function toWslMountPath(windowsPath) {
1473
+ const winNorm = String(windowsPath || '').replace(/\\/g, '/');
1474
+ const match = winNorm.match(/^([A-Za-z]):\/(.*)/);
1475
+ if (!match) return null;
1476
+ return `/mnt/${match[1].toLowerCase()}/${match[2]}`;
1477
+ }
1478
+
1479
+ function syncFileToWsl(windowsPath, wslDestPath) {
1480
+ if (process.platform !== 'win32' || !wslDestPath) return false;
1481
+ const wslSrc = toWslMountPath(windowsPath);
1482
+ if (!wslSrc) return false;
1483
+
1484
+ const wslDir = path.posix.dirname(wslDestPath);
1485
+ execFileSync('wsl', ['bash', '-lc', `mkdir -p ${shellQuote(wslDir)} && cp ${shellQuote(wslSrc)} ${shellQuote(wslDestPath)}`], {
1486
+ timeout: 10000,
1487
+ stdio: 'pipe'
1488
+ });
1489
+ return true;
1490
+ }
1491
+
1492
+ function syncAuthProfilesToWsl(windowsAuthProfilesPath, wslDestPath) {
1493
+ try {
1494
+ const wslHome = getWslHome();
1495
+ if (!wslHome && !wslDestPath) return;
1496
+ const target = wslDestPath || path.posix.join(wslHome, '.openclaw', 'agents', 'main', 'agent', 'auth-profiles.json');
1497
+ syncFileToWsl(windowsAuthProfilesPath, target);
1498
+ } catch { /* best-effort */ }
1499
+ }
1500
+
1501
+ function syncMirroredAuthStores(paths) {
1502
+ if (!paths?.authProfiles) return;
1503
+
1504
+ const store = readAuthStore(paths.authProfiles);
1505
+ for (const target of [...new Set(paths.authSyncTargets || [])]) {
1506
+ if (!target || target === paths.authProfiles) continue;
1507
+ try {
1508
+ writeAuthStore(target, store);
1509
+ } catch { /* best-effort */ }
1510
+ }
1511
+
1512
+ if (process.platform === 'win32' && (paths.wslAuthProfiles || paths.wslConfigPath)) {
1513
+ const derivedWslAuthPath = paths.wslAuthProfiles || path.posix.join(path.posix.dirname(paths.wslConfigPath), 'agents', 'main', 'agent', 'auth-profiles.json');
1514
+ syncAuthProfilesToWsl(paths.authProfiles, derivedWslAuthPath);
1515
+ }
1516
+ }
1517
+
1518
+ function pruneAuthProfilesExceptWithSync(paths, keepProviders = []) {
1519
+ const removed = pruneAuthProfilesExcept(paths.authProfiles, keepProviders);
1520
+ syncMirroredAuthStores(paths);
1521
+ return removed;
1522
+ }
1523
+
1524
+ function updateAuthProfilesWithSync(paths, providerName, apiKey) {
1525
+ updateAuthProfiles(paths.authProfiles, providerName, apiKey);
1526
+ syncMirroredAuthStores(paths);
1527
+ }
1528
+
1433
1529
  function pruneAuthProfilesByPrefix(authProfilesPath, prefixBase, keepProviders = []) {
1434
1530
  const keepSet = new Set(keepProviders);
1435
1531
  const store = readAuthStore(authProfilesPath);
@@ -2313,7 +2409,7 @@ async function quickSetup(paths, args = {}) {
2313
2409
 
2314
2410
  if (toRemove.length > 0) {
2315
2411
  pruneProvidersExcept(config, [providerName]);
2316
- pruneAuthProfilesExcept(paths.authProfiles, [providerName]);
2412
+ pruneAuthProfilesExceptWithSync(paths, [providerName]);
2317
2413
  }
2318
2414
 
2319
2415
  config.models.providers[providerName] = {
@@ -2351,7 +2447,7 @@ async function quickSetup(paths, args = {}) {
2351
2447
  ensureGatewaySettings(config);
2352
2448
  if (apiConfig.api.startsWith('openai')) cleanupConflictingEnvVars(config, normalizedBaseUrl, apiKey);
2353
2449
  writeConfigWithSync(paths, config);
2354
- updateAuthProfiles(paths.authProfiles, providerName, apiKey);
2450
+ updateAuthProfilesWithSync(paths, providerName, apiKey);
2355
2451
  ws.succeed('配置写入完成');
2356
2452
 
2357
2453
  console.log(chalk.green(`\n✅ ${typeLabel} 中转已配置完成!`));
@@ -2431,7 +2527,7 @@ async function presetClaude(paths, args = {}) {
2431
2527
  ? pruneProvidersExcept(config, [providerName])
2432
2528
  : [];
2433
2529
  if (removedProviders.length > 0) {
2434
- pruneAuthProfilesExcept(paths.authProfiles, [providerName]);
2530
+ pruneAuthProfilesExceptWithSync(paths, [providerName]);
2435
2531
  }
2436
2532
 
2437
2533
  const baseUrl = buildFullUrl(selectedEndpoint.url, 'claude');
@@ -2535,7 +2631,7 @@ async function presetClaude(paths, args = {}) {
2535
2631
  createTimestampedBackup(paths.openclawConfig, paths.configDir, 'claude');
2536
2632
  ensureGatewaySettings(config);
2537
2633
  writeConfigWithSync(paths, config);
2538
- updateAuthProfiles(paths.authProfiles, providerName, apiKey);
2634
+ updateAuthProfilesWithSync(paths, providerName, apiKey);
2539
2635
  const extSynced = syncExternalTools('claude', baseUrl, apiKey);
2540
2636
  writeSpinner.succeed('配置写入完成');
2541
2637
 
@@ -2625,7 +2721,7 @@ async function presetCodex(paths, args = {}) {
2625
2721
  ? pruneProvidersExcept(config, [providerName])
2626
2722
  : [];
2627
2723
  if (removedProviders.length > 0) {
2628
- pruneAuthProfilesExcept(paths.authProfiles, [providerName]);
2724
+ pruneAuthProfilesExceptWithSync(paths, [providerName]);
2629
2725
  }
2630
2726
 
2631
2727
  const baseUrl = buildFullUrl(selectedEndpoint.url, 'codex');
@@ -2729,7 +2825,7 @@ async function presetCodex(paths, args = {}) {
2729
2825
  ensureGatewaySettings(config);
2730
2826
  cleanupConflictingEnvVars(config, baseUrl, apiKey);
2731
2827
  writeConfigWithSync(paths, config);
2732
- updateAuthProfiles(paths.authProfiles, providerName, apiKey);
2828
+ updateAuthProfilesWithSync(paths, providerName, apiKey);
2733
2829
  const extSynced2 = syncExternalTools('codex', baseUrl, apiKey);
2734
2830
  writeSpinner2.succeed('配置写入完成');
2735
2831
 
@@ -2913,8 +3009,8 @@ async function autoActivate(paths, args = {}) {
2913
3009
  ensureGatewaySettings(config);
2914
3010
  cleanupConflictingEnvVars(config, codexBaseUrl, apiKey);
2915
3011
  writeConfigWithSync(paths, config);
2916
- updateAuthProfiles(paths.authProfiles, claudeProviderName, apiKey);
2917
- updateAuthProfiles(paths.authProfiles, codexProviderName, apiKey);
3012
+ updateAuthProfilesWithSync(paths, claudeProviderName, apiKey);
3013
+ updateAuthProfilesWithSync(paths, codexProviderName, apiKey);
2918
3014
  const extSynced = [];
2919
3015
  try { syncExternalTools('claude', claudeBaseUrl, apiKey, { codexBaseUrl }); extSynced.push('Claude Code settings'); } catch { /* ignore */ }
2920
3016
  try { syncExternalTools('codex', codexBaseUrl, apiKey); extSynced.push('Codex CLI config'); } catch { /* ignore */ }
@@ -3348,8 +3444,8 @@ async function yycodeQuickSetup(paths) {
3348
3444
  ensureGatewaySettings(config);
3349
3445
  cleanupConflictingEnvVars(config, codexBaseUrl, apiKey);
3350
3446
  writeConfigWithSync(paths, config);
3351
- updateAuthProfiles(paths.authProfiles, claudeProviderName, apiKey);
3352
- updateAuthProfiles(paths.authProfiles, codexProviderName, apiKey);
3447
+ updateAuthProfilesWithSync(paths, claudeProviderName, apiKey);
3448
+ updateAuthProfilesWithSync(paths, codexProviderName, apiKey);
3353
3449
  try { syncExternalTools('claude', claudeBaseUrl, apiKey, { codexBaseUrl }); } catch { /* ignore */ }
3354
3450
  try { syncExternalTools('codex', codexBaseUrl, apiKey); } catch { /* ignore */ }
3355
3451
  writeSpinner.succeed('配置写入完成');
@@ -3943,6 +4039,7 @@ async function manageToolsProfile(paths) {
3943
4039
  // ============ 测试连接 ============
3944
4040
  async function testConnection(paths, args = {}) {
3945
4041
  console.log(chalk.cyan('🧪 测试 OpenClaw Gateway 连接\n'));
4042
+ invalidateGatewayEnvCache();
3946
4043
 
3947
4044
  const config = readConfig(paths.openclawConfig);
3948
4045
 
@@ -4079,6 +4176,15 @@ async function testConnection(paths, args = {}) {
4079
4176
  console.log(chalk.red(` 期望: ${primary}`));
4080
4177
  console.log(chalk.cyan(` 实际: ${actualModelKey}`));
4081
4178
  console.log(chalk.gray(` 这意味着 ${primary} 无法正常工作,请检查该模型的中转配置`));
4179
+ const configuredFallbacks = config.agents?.defaults?.model?.fallbacks || [];
4180
+ if (configuredFallbacks.length > 0 && !configuredFallbacks.includes(actualModelKey)) {
4181
+ console.log(chalk.yellow(` ⚠️ 实际回退模型不在当前配置的 fallbacks 中,疑似读到了另一份配置`));
4182
+ console.log(chalk.gray(` 当前 fallbacks: ${configuredFallbacks.join(', ')}`));
4183
+ console.log(chalk.gray(` 当前配置文件: ${paths.openclawConfig}`));
4184
+ if (process.platform === 'win32') {
4185
+ console.log(chalk.gray(' Windows 请重点检查: %USERPROFILE%\\.openclaw、%USERPROFILE%\\.clawdbot、WSL ~/.openclaw'));
4186
+ }
4187
+ }
4082
4188
  // 检查是否可能是 api 字段错误导致的 502
4083
4189
  const expectedProvider = primary.split('/')[0];
4084
4190
  const providerCfg = config.models?.providers?.[expectedProvider];
@@ -4200,6 +4306,7 @@ async function testConnection(paths, args = {}) {
4200
4306
  // ============ 重启 Gateway ============
4201
4307
  async function restartGateway({ silent = false } = {}) {
4202
4308
  if (!silent) console.log(chalk.cyan('\n正在重启 OpenClaw Gateway...'));
4309
+ invalidateGatewayEnvCache();
4203
4310
 
4204
4311
  const gwEnv = detectGatewayEnv();
4205
4312
  const configPaths = getConfigPath();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yymaxapi",
3
- "version": "1.0.78",
3
+ "version": "1.0.80",
4
4
  "description": "跨平台 OpenClaw/Clawdbot 配置管理工具 - 管理中转地址、模型切换、API Keys、测速优化",
5
5
  "main": "bin/yymaxapi.js",
6
6
  "bin": {