yymaxapi 1.0.11 → 1.0.13
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 +191 -44
- package/package.json +1 -1
package/bin/yymaxapi.js
CHANGED
|
@@ -552,6 +552,126 @@ function syncClawdbotConfigs(paths, config) {
|
|
|
552
552
|
function writeConfigWithSync(paths, config) {
|
|
553
553
|
writeConfig(paths.openclawConfig, config);
|
|
554
554
|
syncClawdbotConfigs(paths, config);
|
|
555
|
+
// 如果 Gateway 在 WSL,自动同步配置过去
|
|
556
|
+
const gwEnv = detectGatewayEnv();
|
|
557
|
+
if (gwEnv === 'wsl') {
|
|
558
|
+
syncConfigToWsl(paths.openclawConfig);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// ============ Gateway 环境检测(集中化) ============
|
|
563
|
+
// 所有 WSL 相关逻辑统一通过 detectGatewayEnv() 路由
|
|
564
|
+
// 返回 'native' | 'wsl',结果缓存,整个进程生命周期只检测一次
|
|
565
|
+
|
|
566
|
+
let _gwEnvCache = null;
|
|
567
|
+
let _wslAvailCache = null;
|
|
568
|
+
let _wslHomeCache = undefined; // undefined = 未检测, null = 检测失败
|
|
569
|
+
|
|
570
|
+
function isWslAvailable() {
|
|
571
|
+
if (process.platform !== 'win32') return false;
|
|
572
|
+
if (_wslAvailCache !== null) return _wslAvailCache;
|
|
573
|
+
try {
|
|
574
|
+
execFileSync('wsl', ['echo', 'ok'], { encoding: 'utf8', timeout: 5000, stdio: 'pipe' });
|
|
575
|
+
_wslAvailCache = true;
|
|
576
|
+
} catch {
|
|
577
|
+
_wslAvailCache = false;
|
|
578
|
+
}
|
|
579
|
+
return _wslAvailCache;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function getWslHome() {
|
|
583
|
+
if (_wslHomeCache !== undefined) return _wslHomeCache;
|
|
584
|
+
try {
|
|
585
|
+
_wslHomeCache = execFileSync('wsl', ['bash', '-c', 'echo $HOME'], { encoding: 'utf8', timeout: 5000 }).trim() || null;
|
|
586
|
+
} catch { _wslHomeCache = null; }
|
|
587
|
+
return _wslHomeCache;
|
|
588
|
+
}
|
|
589
|
+
|
|
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
|
|
597
|
+
try {
|
|
598
|
+
const r = safeExec(`netstat -ano | findstr ":${port}"`, { timeout: 5000 });
|
|
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
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
} catch {}
|
|
613
|
+
_gwEnvCache = 'native';
|
|
614
|
+
return 'native';
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// 在 Gateway 环境中执行命令(WSL 自动包裹 wsl -- bash -c)
|
|
618
|
+
function execInGatewayEnv(cmd, options = {}) {
|
|
619
|
+
if (detectGatewayEnv() === 'wsl') {
|
|
620
|
+
const escaped = cmd.replace(/'/g, "'\\''");
|
|
621
|
+
return safeExec(`wsl -- bash -c '${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 -c '${escaped}'`, options };
|
|
631
|
+
}
|
|
632
|
+
return { cmd, options };
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// 同步配置到 WSL(仅在 Gateway 环境为 WSL 时调用)
|
|
636
|
+
function syncConfigToWsl(windowsConfigPath) {
|
|
637
|
+
try {
|
|
638
|
+
const wslHome = getWslHome();
|
|
639
|
+
if (!wslHome) return;
|
|
640
|
+
const winNorm = windowsConfigPath.replace(/\\/g, '/');
|
|
641
|
+
const match = winNorm.match(/^([A-Za-z]):\/(.*)/);
|
|
642
|
+
if (!match) return;
|
|
643
|
+
const wslSrc = `/mnt/${match[1].toLowerCase()}/${match[2]}`;
|
|
644
|
+
const wslDest = `${wslHome}/.openclaw/openclaw.json`;
|
|
645
|
+
execFileSync('wsl', ['bash', '-c', `mkdir -p "${wslHome}/.openclaw" && cp "${wslSrc}" "${wslDest}"`], { timeout: 10000, stdio: 'ignore' });
|
|
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 -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 });
|
|
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 */ }
|
|
555
675
|
}
|
|
556
676
|
|
|
557
677
|
function coerceModelsRecord(value) {
|
|
@@ -2013,7 +2133,9 @@ function getConfigStatusLine(paths) {
|
|
|
2013
2133
|
return chalk.gray('当前状态: 未配置任何模型');
|
|
2014
2134
|
}
|
|
2015
2135
|
|
|
2016
|
-
|
|
2136
|
+
const gwEnv = detectGatewayEnv();
|
|
2137
|
+
const envTag = gwEnv === 'wsl' ? chalk.cyan(' [WSL]') : '';
|
|
2138
|
+
return chalk.gray('当前状态: ') + parts.join(' ') + chalk.gray(' (✓ 主模型 ○ 已配置)') + envTag;
|
|
2017
2139
|
} catch {
|
|
2018
2140
|
return null;
|
|
2019
2141
|
}
|
|
@@ -2205,29 +2327,8 @@ async function testConnection(paths, args = {}) {
|
|
|
2205
2327
|
|
|
2206
2328
|
const allowAutoDaemon = !(args['no-daemon'] || args.noDaemon);
|
|
2207
2329
|
|
|
2208
|
-
// 步骤0: 清理所有挂死的 agent
|
|
2209
|
-
|
|
2210
|
-
const { execSync } = require('child_process');
|
|
2211
|
-
const platform = process.platform;
|
|
2212
|
-
if (platform === 'win32') {
|
|
2213
|
-
// Windows: 用 wmic 精确匹配 agent 子进程(排除 gateway)
|
|
2214
|
-
for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
|
|
2215
|
-
try {
|
|
2216
|
-
execSync(`wmic process where "commandline like '%${name}%agent%' and not commandline like '%gateway%'" call terminate 2>nul`, { stdio: 'ignore', timeout: 10000 });
|
|
2217
|
-
} catch { /* ignore */ }
|
|
2218
|
-
}
|
|
2219
|
-
// fallback: 杀掉所有 Not Responding 的 node 进程
|
|
2220
|
-
try {
|
|
2221
|
-
execSync('taskkill /F /FI "IMAGENAME eq node.exe" /FI "STATUS eq Not Responding" 2>nul', { stdio: 'ignore' });
|
|
2222
|
-
} catch { /* ignore */ }
|
|
2223
|
-
} else {
|
|
2224
|
-
// Unix: 杀掉所有 openclaw/clawdbot/moltbot agent 子进程(排除 gateway)
|
|
2225
|
-
for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
|
|
2226
|
-
execSync(`pkill -f '${name}.*agent' 2>/dev/null || true`, { stdio: 'ignore' });
|
|
2227
|
-
}
|
|
2228
|
-
}
|
|
2229
|
-
console.log(chalk.gray('已清理残留 agent 进程'));
|
|
2230
|
-
} catch { /* ignore */ }
|
|
2330
|
+
// 步骤0: 清理所有挂死的 agent 进程
|
|
2331
|
+
cleanupAgentProcesses();
|
|
2231
2332
|
|
|
2232
2333
|
// 步骤1: 先重启 Gateway 使配置生效
|
|
2233
2334
|
console.log(chalk.cyan('步骤 1/2: 重启 Gateway 使配置生效...'));
|
|
@@ -2371,8 +2472,43 @@ async function testConnection(paths, args = {}) {
|
|
|
2371
2472
|
|
|
2372
2473
|
// ============ 重启 Gateway ============
|
|
2373
2474
|
async function restartGateway() {
|
|
2374
|
-
console.log(chalk.cyan('\n
|
|
2475
|
+
console.log(chalk.cyan('\n正在重启 OpenClaw Gateway...'));
|
|
2476
|
+
|
|
2477
|
+
const gwEnv = detectGatewayEnv();
|
|
2478
|
+
|
|
2479
|
+
// 如果 Gateway 在 WSL 里,优先用 wsl -- 重启
|
|
2480
|
+
if (gwEnv === 'wsl') {
|
|
2481
|
+
console.log(chalk.gray(' [检测] Gateway 运行在 WSL 中'));
|
|
2482
|
+
return new Promise((resolve) => {
|
|
2483
|
+
const wslCmds = [
|
|
2484
|
+
'wsl -- bash -c "openclaw gateway restart"',
|
|
2485
|
+
'wsl -- bash -c "clawdbot gateway restart"',
|
|
2486
|
+
'wsl -- bash -c "moltbot gateway restart"',
|
|
2487
|
+
];
|
|
2488
|
+
let tried = 0;
|
|
2489
|
+
const tryNext = () => {
|
|
2490
|
+
if (tried >= wslCmds.length) {
|
|
2491
|
+
console.log(chalk.yellow('WSL 内 Gateway 重启失败,尝试 Windows 原生重启...'));
|
|
2492
|
+
restartGatewayNative().then(resolve);
|
|
2493
|
+
return;
|
|
2494
|
+
}
|
|
2495
|
+
const cmd = wslCmds[tried++];
|
|
2496
|
+
exec(cmd, { timeout: 30000 }, (error) => {
|
|
2497
|
+
if (error) { tryNext(); }
|
|
2498
|
+
else {
|
|
2499
|
+
console.log(chalk.green('Gateway 已重启 (WSL)'));
|
|
2500
|
+
resolve();
|
|
2501
|
+
}
|
|
2502
|
+
});
|
|
2503
|
+
};
|
|
2504
|
+
tryNext();
|
|
2505
|
+
});
|
|
2506
|
+
}
|
|
2375
2507
|
|
|
2508
|
+
return restartGatewayNative();
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
async function restartGatewayNative() {
|
|
2376
2512
|
const { cliBinary: resolved, nodeMajor } = getCliMeta();
|
|
2377
2513
|
const nodeInfo = findCompatibleNode(nodeMajor);
|
|
2378
2514
|
const env = { ...process.env, PATH: extendPathEnv(nodeInfo ? nodeInfo.path : null) };
|
|
@@ -2516,27 +2652,38 @@ function testGatewayApi(port, token, model, endpoint = '/v1/responses') {
|
|
|
2516
2652
|
|
|
2517
2653
|
function testGatewayViaAgent(model) {
|
|
2518
2654
|
return new Promise((resolve) => {
|
|
2519
|
-
const
|
|
2520
|
-
if (!cliBinary) {
|
|
2521
|
-
resolve({ success: false, usedCli: false, error: '未找到 openclaw/clawdbot/moltbot 命令' });
|
|
2522
|
-
return;
|
|
2523
|
-
}
|
|
2524
|
-
|
|
2525
|
-
const nodeInfo = findCompatibleNode(nodeMajor);
|
|
2526
|
-
// 清除可能覆盖配置文件 apiKey 的环境变量,避免 Gateway 使用错误的 key
|
|
2527
|
-
const env = { ...process.env, PATH: extendPathEnv(nodeInfo ? nodeInfo.path : null), NODE_NO_WARNINGS: '1' };
|
|
2528
|
-
delete env.CLAUDE_API_KEY;
|
|
2529
|
-
delete env.OPENCLAW_CLAUDE_KEY;
|
|
2530
|
-
delete env.OPENCLAW_API_KEY;
|
|
2531
|
-
delete env.OPENAI_API_KEY;
|
|
2532
|
-
delete env.OPENCLAW_CODEX_KEY;
|
|
2533
|
-
const useNode = nodeInfo && isNodeShebang(cliBinary);
|
|
2655
|
+
const gwEnv = detectGatewayEnv();
|
|
2534
2656
|
const sessionId = `yymaxapi-test-${Date.now()}`;
|
|
2535
|
-
const cmd = useNode
|
|
2536
|
-
? `"${nodeInfo.path}" "${cliBinary}" agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120`
|
|
2537
|
-
: `"${cliBinary}" agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120`;
|
|
2538
2657
|
|
|
2539
|
-
|
|
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 -c '${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) => {
|
|
2540
2687
|
// 过滤 stderr 中的 Node.js DeprecationWarning 噪音
|
|
2541
2688
|
const cleanStderr = (stderr || '').replace(/\(node:\d+\) \[DEP\d+\] DeprecationWarning:.*(\n.*trace-deprecation.*)?/g, '').trim();
|
|
2542
2689
|
|