yymaxapi 1.0.79 → 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.
- package/bin/yymaxapi.js +163 -41
- 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
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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
|
-
|
|
539
|
-
|
|
540
|
-
|
|
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
|
// ============ 配置读写 ============
|
|
@@ -879,10 +924,15 @@ function syncClawdbotConfigs(paths, config) {
|
|
|
879
924
|
function writeConfigWithSync(paths, config) {
|
|
880
925
|
writeConfig(paths.openclawConfig, config);
|
|
881
926
|
syncClawdbotConfigs(paths, config);
|
|
927
|
+
const hasWslMirror = process.platform === 'win32' && !!paths.wslConfigPath;
|
|
928
|
+
if (hasWslMirror) {
|
|
929
|
+
syncConfigToWsl(paths.openclawConfig, paths.wslConfigPath);
|
|
930
|
+
}
|
|
931
|
+
invalidateGatewayEnvCache();
|
|
882
932
|
// 如果 Gateway 在 WSL,自动同步配置过去
|
|
883
933
|
const gwEnv = detectGatewayEnv();
|
|
884
|
-
if (gwEnv === 'wsl') {
|
|
885
|
-
syncConfigToWsl(paths.openclawConfig);
|
|
934
|
+
if (!hasWslMirror && gwEnv === 'wsl') {
|
|
935
|
+
syncConfigToWsl(paths.openclawConfig, paths.wslConfigPath);
|
|
886
936
|
}
|
|
887
937
|
// 如果 Gateway 在 Docker 容器内,自动同步配置过去
|
|
888
938
|
if (gwEnv === 'docker' && _selectedDockerContainer) {
|
|
@@ -1081,6 +1131,10 @@ let _gwEnvCache = null;
|
|
|
1081
1131
|
let _wslAvailCache = null;
|
|
1082
1132
|
let _wslHomeCache = undefined; // undefined = 未检测, null = 检测失败
|
|
1083
1133
|
|
|
1134
|
+
function invalidateGatewayEnvCache() {
|
|
1135
|
+
_gwEnvCache = null;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1084
1138
|
function isWslAvailable() {
|
|
1085
1139
|
if (process.platform !== 'win32') return false;
|
|
1086
1140
|
if (_wslAvailCache !== null) return _wslAvailCache;
|
|
@@ -1206,16 +1260,12 @@ function execAsyncInGatewayEnv(cmd, options = {}) {
|
|
|
1206
1260
|
}
|
|
1207
1261
|
|
|
1208
1262
|
// 同步配置到 WSL(仅在 Gateway 环境为 WSL 时调用)
|
|
1209
|
-
function syncConfigToWsl(windowsConfigPath) {
|
|
1263
|
+
function syncConfigToWsl(windowsConfigPath, wslDestPath) {
|
|
1210
1264
|
try {
|
|
1211
1265
|
const wslHome = getWslHome();
|
|
1212
|
-
if (!wslHome) return;
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1215
|
-
if (!match) return;
|
|
1216
|
-
const wslSrc = `/mnt/${match[1].toLowerCase()}/${match[2]}`;
|
|
1217
|
-
const wslDest = `${wslHome}/.openclaw/openclaw.json`;
|
|
1218
|
-
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);
|
|
1219
1269
|
} catch { /* best-effort */ }
|
|
1220
1270
|
}
|
|
1221
1271
|
|
|
@@ -1412,9 +1462,70 @@ function readAuthStore(authProfilesPath) {
|
|
|
1412
1462
|
}
|
|
1413
1463
|
|
|
1414
1464
|
function writeAuthStore(authProfilesPath, store) {
|
|
1465
|
+
const authDir = path.dirname(authProfilesPath);
|
|
1466
|
+
if (!fs.existsSync(authDir)) {
|
|
1467
|
+
fs.mkdirSync(authDir, { recursive: true });
|
|
1468
|
+
}
|
|
1415
1469
|
fs.writeFileSync(authProfilesPath, JSON.stringify(store, null, 2), 'utf8');
|
|
1416
1470
|
}
|
|
1417
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
|
+
|
|
1418
1529
|
function pruneAuthProfilesByPrefix(authProfilesPath, prefixBase, keepProviders = []) {
|
|
1419
1530
|
const keepSet = new Set(keepProviders);
|
|
1420
1531
|
const store = readAuthStore(authProfilesPath);
|
|
@@ -2298,7 +2409,7 @@ async function quickSetup(paths, args = {}) {
|
|
|
2298
2409
|
|
|
2299
2410
|
if (toRemove.length > 0) {
|
|
2300
2411
|
pruneProvidersExcept(config, [providerName]);
|
|
2301
|
-
|
|
2412
|
+
pruneAuthProfilesExceptWithSync(paths, [providerName]);
|
|
2302
2413
|
}
|
|
2303
2414
|
|
|
2304
2415
|
config.models.providers[providerName] = {
|
|
@@ -2336,7 +2447,7 @@ async function quickSetup(paths, args = {}) {
|
|
|
2336
2447
|
ensureGatewaySettings(config);
|
|
2337
2448
|
if (apiConfig.api.startsWith('openai')) cleanupConflictingEnvVars(config, normalizedBaseUrl, apiKey);
|
|
2338
2449
|
writeConfigWithSync(paths, config);
|
|
2339
|
-
|
|
2450
|
+
updateAuthProfilesWithSync(paths, providerName, apiKey);
|
|
2340
2451
|
ws.succeed('配置写入完成');
|
|
2341
2452
|
|
|
2342
2453
|
console.log(chalk.green(`\n✅ ${typeLabel} 中转已配置完成!`));
|
|
@@ -2416,7 +2527,7 @@ async function presetClaude(paths, args = {}) {
|
|
|
2416
2527
|
? pruneProvidersExcept(config, [providerName])
|
|
2417
2528
|
: [];
|
|
2418
2529
|
if (removedProviders.length > 0) {
|
|
2419
|
-
|
|
2530
|
+
pruneAuthProfilesExceptWithSync(paths, [providerName]);
|
|
2420
2531
|
}
|
|
2421
2532
|
|
|
2422
2533
|
const baseUrl = buildFullUrl(selectedEndpoint.url, 'claude');
|
|
@@ -2520,7 +2631,7 @@ async function presetClaude(paths, args = {}) {
|
|
|
2520
2631
|
createTimestampedBackup(paths.openclawConfig, paths.configDir, 'claude');
|
|
2521
2632
|
ensureGatewaySettings(config);
|
|
2522
2633
|
writeConfigWithSync(paths, config);
|
|
2523
|
-
|
|
2634
|
+
updateAuthProfilesWithSync(paths, providerName, apiKey);
|
|
2524
2635
|
const extSynced = syncExternalTools('claude', baseUrl, apiKey);
|
|
2525
2636
|
writeSpinner.succeed('配置写入完成');
|
|
2526
2637
|
|
|
@@ -2610,7 +2721,7 @@ async function presetCodex(paths, args = {}) {
|
|
|
2610
2721
|
? pruneProvidersExcept(config, [providerName])
|
|
2611
2722
|
: [];
|
|
2612
2723
|
if (removedProviders.length > 0) {
|
|
2613
|
-
|
|
2724
|
+
pruneAuthProfilesExceptWithSync(paths, [providerName]);
|
|
2614
2725
|
}
|
|
2615
2726
|
|
|
2616
2727
|
const baseUrl = buildFullUrl(selectedEndpoint.url, 'codex');
|
|
@@ -2714,7 +2825,7 @@ async function presetCodex(paths, args = {}) {
|
|
|
2714
2825
|
ensureGatewaySettings(config);
|
|
2715
2826
|
cleanupConflictingEnvVars(config, baseUrl, apiKey);
|
|
2716
2827
|
writeConfigWithSync(paths, config);
|
|
2717
|
-
|
|
2828
|
+
updateAuthProfilesWithSync(paths, providerName, apiKey);
|
|
2718
2829
|
const extSynced2 = syncExternalTools('codex', baseUrl, apiKey);
|
|
2719
2830
|
writeSpinner2.succeed('配置写入完成');
|
|
2720
2831
|
|
|
@@ -2898,8 +3009,8 @@ async function autoActivate(paths, args = {}) {
|
|
|
2898
3009
|
ensureGatewaySettings(config);
|
|
2899
3010
|
cleanupConflictingEnvVars(config, codexBaseUrl, apiKey);
|
|
2900
3011
|
writeConfigWithSync(paths, config);
|
|
2901
|
-
|
|
2902
|
-
|
|
3012
|
+
updateAuthProfilesWithSync(paths, claudeProviderName, apiKey);
|
|
3013
|
+
updateAuthProfilesWithSync(paths, codexProviderName, apiKey);
|
|
2903
3014
|
const extSynced = [];
|
|
2904
3015
|
try { syncExternalTools('claude', claudeBaseUrl, apiKey, { codexBaseUrl }); extSynced.push('Claude Code settings'); } catch { /* ignore */ }
|
|
2905
3016
|
try { syncExternalTools('codex', codexBaseUrl, apiKey); extSynced.push('Codex CLI config'); } catch { /* ignore */ }
|
|
@@ -3333,8 +3444,8 @@ async function yycodeQuickSetup(paths) {
|
|
|
3333
3444
|
ensureGatewaySettings(config);
|
|
3334
3445
|
cleanupConflictingEnvVars(config, codexBaseUrl, apiKey);
|
|
3335
3446
|
writeConfigWithSync(paths, config);
|
|
3336
|
-
|
|
3337
|
-
|
|
3447
|
+
updateAuthProfilesWithSync(paths, claudeProviderName, apiKey);
|
|
3448
|
+
updateAuthProfilesWithSync(paths, codexProviderName, apiKey);
|
|
3338
3449
|
try { syncExternalTools('claude', claudeBaseUrl, apiKey, { codexBaseUrl }); } catch { /* ignore */ }
|
|
3339
3450
|
try { syncExternalTools('codex', codexBaseUrl, apiKey); } catch { /* ignore */ }
|
|
3340
3451
|
writeSpinner.succeed('配置写入完成');
|
|
@@ -3928,6 +4039,7 @@ async function manageToolsProfile(paths) {
|
|
|
3928
4039
|
// ============ 测试连接 ============
|
|
3929
4040
|
async function testConnection(paths, args = {}) {
|
|
3930
4041
|
console.log(chalk.cyan('🧪 测试 OpenClaw Gateway 连接\n'));
|
|
4042
|
+
invalidateGatewayEnvCache();
|
|
3931
4043
|
|
|
3932
4044
|
const config = readConfig(paths.openclawConfig);
|
|
3933
4045
|
|
|
@@ -4064,6 +4176,15 @@ async function testConnection(paths, args = {}) {
|
|
|
4064
4176
|
console.log(chalk.red(` 期望: ${primary}`));
|
|
4065
4177
|
console.log(chalk.cyan(` 实际: ${actualModelKey}`));
|
|
4066
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
|
+
}
|
|
4067
4188
|
// 检查是否可能是 api 字段错误导致的 502
|
|
4068
4189
|
const expectedProvider = primary.split('/')[0];
|
|
4069
4190
|
const providerCfg = config.models?.providers?.[expectedProvider];
|
|
@@ -4185,6 +4306,7 @@ async function testConnection(paths, args = {}) {
|
|
|
4185
4306
|
// ============ 重启 Gateway ============
|
|
4186
4307
|
async function restartGateway({ silent = false } = {}) {
|
|
4187
4308
|
if (!silent) console.log(chalk.cyan('\n正在重启 OpenClaw Gateway...'));
|
|
4309
|
+
invalidateGatewayEnvCache();
|
|
4188
4310
|
|
|
4189
4311
|
const gwEnv = detectGatewayEnv();
|
|
4190
4312
|
const configPaths = getConfigPath();
|