yymaxapi 1.0.12 → 1.0.14
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 +131 -90
- package/package.json +1 -1
package/bin/yymaxapi.js
CHANGED
|
@@ -552,70 +552,126 @@ function syncClawdbotConfigs(paths, config) {
|
|
|
552
552
|
function writeConfigWithSync(paths, config) {
|
|
553
553
|
writeConfig(paths.openclawConfig, config);
|
|
554
554
|
syncClawdbotConfigs(paths, config);
|
|
555
|
-
|
|
555
|
+
// 如果 Gateway 在 WSL,自动同步配置过去
|
|
556
|
+
const gwEnv = detectGatewayEnv();
|
|
557
|
+
if (gwEnv === 'wsl') {
|
|
558
|
+
syncConfigToWsl(paths.openclawConfig);
|
|
559
|
+
}
|
|
556
560
|
}
|
|
557
561
|
|
|
558
|
-
// ============
|
|
559
|
-
|
|
562
|
+
// ============ Gateway 环境检测(集中化) ============
|
|
563
|
+
// 所有 WSL 相关逻辑统一通过 detectGatewayEnv() 路由
|
|
564
|
+
// 返回 'native' | 'wsl',结果缓存,整个进程生命周期只检测一次
|
|
565
|
+
|
|
566
|
+
let _gwEnvCache = null;
|
|
567
|
+
let _wslAvailCache = null;
|
|
568
|
+
let _wslHomeCache = undefined; // undefined = 未检测, null = 检测失败
|
|
560
569
|
|
|
561
570
|
function isWslAvailable() {
|
|
562
571
|
if (process.platform !== 'win32') return false;
|
|
563
|
-
if (
|
|
572
|
+
if (_wslAvailCache !== null) return _wslAvailCache;
|
|
564
573
|
try {
|
|
565
|
-
|
|
566
|
-
|
|
574
|
+
execFileSync('wsl', ['echo', 'ok'], { encoding: 'utf8', timeout: 5000, stdio: 'pipe' });
|
|
575
|
+
_wslAvailCache = true;
|
|
567
576
|
} catch {
|
|
568
|
-
|
|
569
|
-
try {
|
|
570
|
-
execFileSync('wsl', ['echo', 'ok'], { encoding: 'utf8', timeout: 5000, stdio: 'pipe' });
|
|
571
|
-
_wslDetected = true;
|
|
572
|
-
} catch {
|
|
573
|
-
_wslDetected = false;
|
|
574
|
-
}
|
|
577
|
+
_wslAvailCache = false;
|
|
575
578
|
}
|
|
576
|
-
return
|
|
579
|
+
return _wslAvailCache;
|
|
577
580
|
}
|
|
578
581
|
|
|
579
582
|
function getWslHome() {
|
|
583
|
+
if (_wslHomeCache !== undefined) return _wslHomeCache;
|
|
580
584
|
try {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
585
|
+
_wslHomeCache = execFileSync('wsl', ['bash', '-c', 'echo $HOME'], { encoding: 'utf8', timeout: 5000 }).trim() || null;
|
|
586
|
+
} catch { _wslHomeCache = null; }
|
|
587
|
+
return _wslHomeCache;
|
|
584
588
|
}
|
|
585
589
|
|
|
586
|
-
function
|
|
587
|
-
if (
|
|
590
|
+
function detectGatewayEnv(port = 18789) {
|
|
591
|
+
if (_gwEnvCache !== null) return _gwEnvCache;
|
|
592
|
+
if (process.platform !== 'win32' || !isWslAvailable()) {
|
|
593
|
+
_gwEnvCache = 'native';
|
|
594
|
+
return 'native';
|
|
595
|
+
}
|
|
596
|
+
// 检查占用 gateway 端口的进程是否是 wslrelay
|
|
588
597
|
try {
|
|
589
|
-
// 检查占用 gateway 端口的进程是否是 wslrelay
|
|
590
598
|
const r = safeExec(`netstat -ano | findstr ":${port}"`, { timeout: 5000 });
|
|
591
|
-
if (
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
+
if (r.ok && r.output) {
|
|
600
|
+
const lines = r.output.split('\n').filter(l => l.includes('LISTENING'));
|
|
601
|
+
for (const line of lines) {
|
|
602
|
+
const pid = line.trim().split(/\s+/).pop();
|
|
603
|
+
if (pid) {
|
|
604
|
+
const taskR = safeExec(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { timeout: 5000 });
|
|
605
|
+
if (taskR.ok && taskR.output && taskR.output.toLowerCase().includes('wslrelay')) {
|
|
606
|
+
_gwEnvCache = 'wsl';
|
|
607
|
+
return 'wsl';
|
|
608
|
+
}
|
|
599
609
|
}
|
|
600
610
|
}
|
|
601
611
|
}
|
|
602
612
|
} catch {}
|
|
603
|
-
|
|
613
|
+
_gwEnvCache = 'native';
|
|
614
|
+
return 'native';
|
|
604
615
|
}
|
|
605
616
|
|
|
606
|
-
|
|
607
|
-
|
|
617
|
+
// 在 Gateway 环境中执行命令(WSL 自动包裹 wsl -- bash -lc)
|
|
618
|
+
function execInGatewayEnv(cmd, options = {}) {
|
|
619
|
+
if (detectGatewayEnv() === 'wsl') {
|
|
620
|
+
const escaped = cmd.replace(/'/g, "'\\''");
|
|
621
|
+
return safeExec(`wsl -- bash -lc '${escaped}'`, options);
|
|
622
|
+
}
|
|
623
|
+
return safeExec(cmd, options);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// 在 Gateway 环境中执行命令(异步 exec 版本)
|
|
627
|
+
function execAsyncInGatewayEnv(cmd, options = {}) {
|
|
628
|
+
if (detectGatewayEnv() === 'wsl') {
|
|
629
|
+
const escaped = cmd.replace(/'/g, "'\\''");
|
|
630
|
+
return { cmd: `wsl -- bash -lc '${escaped}'`, options };
|
|
631
|
+
}
|
|
632
|
+
return { cmd, options };
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// 同步配置到 WSL(仅在 Gateway 环境为 WSL 时调用)
|
|
636
|
+
function syncConfigToWsl(windowsConfigPath) {
|
|
608
637
|
try {
|
|
609
638
|
const wslHome = getWslHome();
|
|
610
639
|
if (!wslHome) return;
|
|
611
|
-
// 将 Windows 路径转为 WSL /mnt/c/ 路径
|
|
612
640
|
const winNorm = windowsConfigPath.replace(/\\/g, '/');
|
|
613
641
|
const match = winNorm.match(/^([A-Za-z]):\/(.*)/);
|
|
614
642
|
if (!match) return;
|
|
615
643
|
const wslSrc = `/mnt/${match[1].toLowerCase()}/${match[2]}`;
|
|
616
644
|
const wslDest = `${wslHome}/.openclaw/openclaw.json`;
|
|
617
645
|
execFileSync('wsl', ['bash', '-c', `mkdir -p "${wslHome}/.openclaw" && cp "${wslSrc}" "${wslDest}"`], { timeout: 10000, stdio: 'ignore' });
|
|
618
|
-
} catch { /*
|
|
646
|
+
} catch { /* best-effort */ }
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// 清理 Gateway 环境中的 agent 进程
|
|
650
|
+
function cleanupAgentProcesses() {
|
|
651
|
+
try {
|
|
652
|
+
const { execSync } = require('child_process');
|
|
653
|
+
if (process.platform === 'win32') {
|
|
654
|
+
for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
|
|
655
|
+
try {
|
|
656
|
+
execSync(`wmic process where "commandline like '%${name}%agent%' and not commandline like '%gateway%'" call terminate 2>nul`, { stdio: 'ignore', timeout: 10000 });
|
|
657
|
+
} catch { /* ignore */ }
|
|
658
|
+
}
|
|
659
|
+
try {
|
|
660
|
+
execSync('taskkill /F /FI "IMAGENAME eq node.exe" /FI "STATUS eq Not Responding" 2>nul', { stdio: 'ignore' });
|
|
661
|
+
} catch { /* ignore */ }
|
|
662
|
+
// WSL 内的 agent 也要清理
|
|
663
|
+
if (detectGatewayEnv() === 'wsl') {
|
|
664
|
+
try {
|
|
665
|
+
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 });
|
|
666
|
+
} catch { /* ignore */ }
|
|
667
|
+
}
|
|
668
|
+
} else {
|
|
669
|
+
for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
|
|
670
|
+
execSync(`pkill -f '${name}.*agent' 2>/dev/null || true`, { stdio: 'ignore' });
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
console.log(chalk.gray('已清理残留 agent 进程'));
|
|
674
|
+
} catch { /* ignore */ }
|
|
619
675
|
}
|
|
620
676
|
|
|
621
677
|
function coerceModelsRecord(value) {
|
|
@@ -2077,7 +2133,9 @@ function getConfigStatusLine(paths) {
|
|
|
2077
2133
|
return chalk.gray('当前状态: 未配置任何模型');
|
|
2078
2134
|
}
|
|
2079
2135
|
|
|
2080
|
-
|
|
2136
|
+
const gwEnv = detectGatewayEnv();
|
|
2137
|
+
const envTag = gwEnv === 'wsl' ? chalk.cyan(' [WSL]') : '';
|
|
2138
|
+
return chalk.gray('当前状态: ') + parts.join(' ') + chalk.gray(' (✓ 主模型 ○ 已配置)') + envTag;
|
|
2081
2139
|
} catch {
|
|
2082
2140
|
return null;
|
|
2083
2141
|
}
|
|
@@ -2269,35 +2327,8 @@ async function testConnection(paths, args = {}) {
|
|
|
2269
2327
|
|
|
2270
2328
|
const allowAutoDaemon = !(args['no-daemon'] || args.noDaemon);
|
|
2271
2329
|
|
|
2272
|
-
// 步骤0: 清理所有挂死的 agent
|
|
2273
|
-
|
|
2274
|
-
const { execSync } = require('child_process');
|
|
2275
|
-
const platform = process.platform;
|
|
2276
|
-
if (platform === 'win32') {
|
|
2277
|
-
// Windows: 用 wmic 精确匹配 agent 子进程(排除 gateway)
|
|
2278
|
-
for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
|
|
2279
|
-
try {
|
|
2280
|
-
execSync(`wmic process where "commandline like '%${name}%agent%' and not commandline like '%gateway%'" call terminate 2>nul`, { stdio: 'ignore', timeout: 10000 });
|
|
2281
|
-
} catch { /* ignore */ }
|
|
2282
|
-
}
|
|
2283
|
-
// fallback: 杀掉所有 Not Responding 的 node 进程
|
|
2284
|
-
try {
|
|
2285
|
-
execSync('taskkill /F /FI "IMAGENAME eq node.exe" /FI "STATUS eq Not Responding" 2>nul', { stdio: 'ignore' });
|
|
2286
|
-
} catch { /* ignore */ }
|
|
2287
|
-
// WSL 内的 agent 进程也要清理
|
|
2288
|
-
if (isWslAvailable()) {
|
|
2289
|
-
try {
|
|
2290
|
-
execSync('wsl -- bash -c "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 });
|
|
2291
|
-
} catch { /* ignore */ }
|
|
2292
|
-
}
|
|
2293
|
-
} else {
|
|
2294
|
-
// Unix: 杀掉所有 openclaw/clawdbot/moltbot agent 子进程(排除 gateway)
|
|
2295
|
-
for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
|
|
2296
|
-
execSync(`pkill -f '${name}.*agent' 2>/dev/null || true`, { stdio: 'ignore' });
|
|
2297
|
-
}
|
|
2298
|
-
}
|
|
2299
|
-
console.log(chalk.gray('已清理残留 agent 进程'));
|
|
2300
|
-
} catch { /* ignore */ }
|
|
2330
|
+
// 步骤0: 清理所有挂死的 agent 进程
|
|
2331
|
+
cleanupAgentProcesses();
|
|
2301
2332
|
|
|
2302
2333
|
// 步骤1: 先重启 Gateway 使配置生效
|
|
2303
2334
|
console.log(chalk.cyan('步骤 1/2: 重启 Gateway 使配置生效...'));
|
|
@@ -2443,22 +2474,21 @@ async function testConnection(paths, args = {}) {
|
|
|
2443
2474
|
async function restartGateway() {
|
|
2444
2475
|
console.log(chalk.cyan('\n正在重启 OpenClaw Gateway...'));
|
|
2445
2476
|
|
|
2446
|
-
const
|
|
2447
|
-
const gwInWsl = isGatewayInWsl(gatewayPort);
|
|
2477
|
+
const gwEnv = detectGatewayEnv();
|
|
2448
2478
|
|
|
2449
2479
|
// 如果 Gateway 在 WSL 里,优先用 wsl -- 重启
|
|
2450
|
-
if (
|
|
2480
|
+
if (gwEnv === 'wsl') {
|
|
2451
2481
|
console.log(chalk.gray(' [检测] Gateway 运行在 WSL 中'));
|
|
2452
2482
|
return new Promise((resolve) => {
|
|
2453
2483
|
const wslCmds = [
|
|
2454
|
-
'wsl -- bash -
|
|
2455
|
-
'wsl -- bash -
|
|
2456
|
-
'wsl -- bash -
|
|
2484
|
+
'wsl -- bash -lc "openclaw gateway restart"',
|
|
2485
|
+
'wsl -- bash -lc "clawdbot gateway restart"',
|
|
2486
|
+
'wsl -- bash -lc "moltbot gateway restart"',
|
|
2457
2487
|
];
|
|
2458
2488
|
let tried = 0;
|
|
2459
2489
|
const tryNext = () => {
|
|
2460
2490
|
if (tried >= wslCmds.length) {
|
|
2461
|
-
console.log(chalk.yellow('
|
|
2491
|
+
console.log(chalk.yellow('WSL 内 Gateway 重启失败,尝试 Windows 原生重启...'));
|
|
2462
2492
|
restartGatewayNative().then(resolve);
|
|
2463
2493
|
return;
|
|
2464
2494
|
}
|
|
@@ -2466,7 +2496,7 @@ async function restartGateway() {
|
|
|
2466
2496
|
exec(cmd, { timeout: 30000 }, (error) => {
|
|
2467
2497
|
if (error) { tryNext(); }
|
|
2468
2498
|
else {
|
|
2469
|
-
console.log(chalk.green('
|
|
2499
|
+
console.log(chalk.green('Gateway 已重启 (WSL)'));
|
|
2470
2500
|
resolve();
|
|
2471
2501
|
}
|
|
2472
2502
|
});
|
|
@@ -2622,27 +2652,38 @@ function testGatewayApi(port, token, model, endpoint = '/v1/responses') {
|
|
|
2622
2652
|
|
|
2623
2653
|
function testGatewayViaAgent(model) {
|
|
2624
2654
|
return new Promise((resolve) => {
|
|
2625
|
-
const
|
|
2626
|
-
if (!cliBinary) {
|
|
2627
|
-
resolve({ success: false, usedCli: false, error: '未找到 openclaw/clawdbot/moltbot 命令' });
|
|
2628
|
-
return;
|
|
2629
|
-
}
|
|
2630
|
-
|
|
2631
|
-
const nodeInfo = findCompatibleNode(nodeMajor);
|
|
2632
|
-
// 清除可能覆盖配置文件 apiKey 的环境变量,避免 Gateway 使用错误的 key
|
|
2633
|
-
const env = { ...process.env, PATH: extendPathEnv(nodeInfo ? nodeInfo.path : null), NODE_NO_WARNINGS: '1' };
|
|
2634
|
-
delete env.CLAUDE_API_KEY;
|
|
2635
|
-
delete env.OPENCLAW_CLAUDE_KEY;
|
|
2636
|
-
delete env.OPENCLAW_API_KEY;
|
|
2637
|
-
delete env.OPENAI_API_KEY;
|
|
2638
|
-
delete env.OPENCLAW_CODEX_KEY;
|
|
2639
|
-
const useNode = nodeInfo && isNodeShebang(cliBinary);
|
|
2655
|
+
const gwEnv = detectGatewayEnv();
|
|
2640
2656
|
const sessionId = `yymaxapi-test-${Date.now()}`;
|
|
2641
|
-
const cmd = useNode
|
|
2642
|
-
? `"${nodeInfo.path}" "${cliBinary}" agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120`
|
|
2643
|
-
: `"${cliBinary}" agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120`;
|
|
2644
2657
|
|
|
2645
|
-
|
|
2658
|
+
let cmd;
|
|
2659
|
+
let execOpts;
|
|
2660
|
+
|
|
2661
|
+
if (gwEnv === 'wsl') {
|
|
2662
|
+
// Gateway 在 WSL 中,agent 也要在 WSL 中执行
|
|
2663
|
+
const agentCmd = `openclaw agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120 2>/dev/null || clawdbot agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120 2>/dev/null || moltbot agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120`;
|
|
2664
|
+
cmd = `wsl -- bash -lc '${agentCmd.replace(/'/g, "'\\''")}'`;
|
|
2665
|
+
execOpts = { timeout: 120000 };
|
|
2666
|
+
} else {
|
|
2667
|
+
const { cliBinary, nodeMajor } = getCliMeta();
|
|
2668
|
+
if (!cliBinary) {
|
|
2669
|
+
resolve({ success: false, usedCli: false, error: '未找到 openclaw/clawdbot/moltbot 命令' });
|
|
2670
|
+
return;
|
|
2671
|
+
}
|
|
2672
|
+
const nodeInfo = findCompatibleNode(nodeMajor);
|
|
2673
|
+
const env = { ...process.env, PATH: extendPathEnv(nodeInfo ? nodeInfo.path : null), NODE_NO_WARNINGS: '1' };
|
|
2674
|
+
delete env.CLAUDE_API_KEY;
|
|
2675
|
+
delete env.OPENCLAW_CLAUDE_KEY;
|
|
2676
|
+
delete env.OPENCLAW_API_KEY;
|
|
2677
|
+
delete env.OPENAI_API_KEY;
|
|
2678
|
+
delete env.OPENCLAW_CODEX_KEY;
|
|
2679
|
+
const useNode = nodeInfo && isNodeShebang(cliBinary);
|
|
2680
|
+
cmd = useNode
|
|
2681
|
+
? `"${nodeInfo.path}" "${cliBinary}" agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120`
|
|
2682
|
+
: `"${cliBinary}" agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120`;
|
|
2683
|
+
execOpts = { timeout: 120000, env };
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2686
|
+
exec(cmd, execOpts, (error, stdout, stderr) => {
|
|
2646
2687
|
// 过滤 stderr 中的 Node.js DeprecationWarning 噪音
|
|
2647
2688
|
const cleanStderr = (stderr || '').replace(/\(node:\d+\) \[DEP\d+\] DeprecationWarning:.*(\n.*trace-deprecation.*)?/g, '').trim();
|
|
2648
2689
|
|