yymaxapi 1.0.48 → 1.0.50

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 CHANGED
@@ -138,6 +138,10 @@ const DEFAULT_API_CONFIG = {
138
138
  }
139
139
  };
140
140
 
141
+ const SKIP_API_VALIDATION = false;
142
+
143
+ const SKIP_API_VALIDATION = false;
144
+
141
145
  function normalizeEndpoints(raw, fallback) {
142
146
  if (!Array.isArray(raw)) return fallback;
143
147
  const normalized = raw
@@ -371,6 +375,9 @@ function httpGetJson(url, headers = {}, timeout = 10000) {
371
375
  }
372
376
 
373
377
  async function validateApiKey(nodeUrl, apiKey) {
378
+ if (SKIP_API_VALIDATION) {
379
+ return { valid: true, skipped: true };
380
+ }
374
381
  const verifyUrl = `${nodeUrl.replace(/\/+$/, '')}/user/api/v1/me`;
375
382
  const maxRetries = 3;
376
383
  const spinner = ora({ text: '正在验证 API Key...', spinner: 'dots' }).start();
@@ -1659,6 +1666,7 @@ function ensureGatewaySettings(config) {
1659
1666
  if (isLocal && gateway.remote.token !== gateway.auth.token) {
1660
1667
  gateway.remote.token = gateway.auth.token;
1661
1668
  }
1669
+
1662
1670
  }
1663
1671
 
1664
1672
  function isPortOpen(port, host = '127.0.0.1', timeoutMs = 800) {
@@ -2662,10 +2670,21 @@ async function autoActivate(paths, args = {}) {
2662
2670
 
2663
2671
  // 增量写入: 只添加/更新云翼 provider,保留其他已有配置
2664
2672
 
2665
- // Claude 侧
2673
+ // Claude 侧 — 让用户选模型
2666
2674
  const claudeBaseUrl = buildFullUrl(selectedEndpoint.url, 'claude');
2667
- const claudeModelId = 'claude-sonnet-4-6';
2668
- const claudeModel = CLAUDE_MODELS.find(m => m.id === claudeModelId) || { id: claudeModelId, name: 'Claude Sonnet 4.6' };
2675
+ let claudeModelId = (args['claude-model'] || '').toString().trim();
2676
+ if (!claudeModelId && CLAUDE_MODELS.length > 1) {
2677
+ const { picked } = await inquirer.prompt([{
2678
+ type: 'list',
2679
+ name: 'picked',
2680
+ message: '选择 Claude 模型:',
2681
+ choices: CLAUDE_MODELS.map(m => ({ name: m.name, value: m.id })),
2682
+ default: CLAUDE_MODELS[0].id
2683
+ }]);
2684
+ claudeModelId = picked;
2685
+ }
2686
+ if (!claudeModelId) claudeModelId = CLAUDE_MODELS[0]?.id || 'claude-sonnet-4-6';
2687
+ const claudeModel = CLAUDE_MODELS.find(m => m.id === claudeModelId) || { id: claudeModelId, name: claudeModelId };
2669
2688
  const claudeModelKey = `${claudeProviderName}/${claudeModelId}`;
2670
2689
 
2671
2690
  config.models.providers[claudeProviderName] = {
@@ -2685,10 +2704,21 @@ async function autoActivate(paths, args = {}) {
2685
2704
  config.auth.profiles[`${claudeProviderName}:default`] = { provider: claudeProviderName, mode: 'api_key' };
2686
2705
  config.agents.defaults.models[claudeModelKey] = { alias: claudeProviderName };
2687
2706
 
2688
- // Codex 侧
2707
+ // Codex 侧 — 让用户选模型
2689
2708
  const codexBaseUrl = buildFullUrl(selectedEndpoint.url, 'codex');
2690
- const codexModelId = 'gpt-5.3-codex';
2691
- const codexModel = CODEX_MODELS.find(m => m.id === codexModelId) || { id: codexModelId, name: 'GPT 5.3 Codex' };
2709
+ let codexModelId = (args['codex-model'] || '').toString().trim();
2710
+ if (!codexModelId && CODEX_MODELS.length > 1) {
2711
+ const { pickedCodex } = await inquirer.prompt([{
2712
+ type: 'list',
2713
+ name: 'pickedCodex',
2714
+ message: '选择 Codex 模型:',
2715
+ choices: CODEX_MODELS.map(m => ({ name: m.name, value: m.id })),
2716
+ default: CODEX_MODELS[0].id
2717
+ }]);
2718
+ codexModelId = pickedCodex;
2719
+ }
2720
+ if (!codexModelId) codexModelId = CODEX_MODELS[0]?.id || 'gpt-5.3-codex';
2721
+ const codexModel = CODEX_MODELS.find(m => m.id === codexModelId) || { id: codexModelId, name: codexModelId };
2692
2722
  const codexModelKey = `${codexProviderName}/${codexModelId}`;
2693
2723
 
2694
2724
  config.models.providers[codexProviderName] = {
@@ -2759,7 +2789,7 @@ async function autoActivate(paths, args = {}) {
2759
2789
  const gwToken = config.gateway?.auth?.token;
2760
2790
  if (gwToken) {
2761
2791
  console.log(chalk.green(`\n🌐 Web Dashboard:`));
2762
- console.log(chalk.cyan(` http://localhost:${gwPort}/?token=${gwToken}`));
2792
+ console.log(chalk.cyan(` http://127.0.0.1:${gwPort}/?token=${gwToken}`));
2763
2793
  }
2764
2794
 
2765
2795
  // ---- 测试连接 ----
@@ -3155,6 +3185,7 @@ async function main() {
3155
3185
  { name: ' 配置 Opencode', value: 'activate_opencode' },
3156
3186
  new inquirer.Separator(' -- 工具 --'),
3157
3187
  { name: ' 切换模型', value: 'switch_model' },
3188
+ { name: ` 权限管理${getToolsProfileTag(paths)}`, value: 'tools_profile' },
3158
3189
  { name: ' 测试连接', value: 'test_connection' },
3159
3190
  { name: ' 查看配置', value: 'view_config' },
3160
3191
  { name: ' 恢复默认', value: 'restore' },
@@ -3183,6 +3214,9 @@ async function main() {
3183
3214
  case 'switch_model':
3184
3215
  await switchModel(paths);
3185
3216
  break;
3217
+ case 'tools_profile':
3218
+ await manageToolsProfile(paths);
3219
+ break;
3186
3220
  case 'view_config':
3187
3221
  await viewConfig(paths);
3188
3222
  break;
@@ -3334,15 +3368,18 @@ async function selectNode(paths, type) {
3334
3368
  }]
3335
3369
  };
3336
3370
 
3337
- // 注册模型
3371
+ // 注册模型并设为主模型
3338
3372
  const modelKey = `${apiConfig.providerName}/${modelConfig.id}`;
3339
3373
  config.agents.defaults.models[modelKey] = { alias: apiConfig.providerName };
3374
+ config.agents.defaults.model.primary = modelKey;
3375
+ config.agents.defaults.model.fallbacks = (config.agents.defaults.model.fallbacks || []).filter(k => k !== modelKey);
3340
3376
 
3377
+ ensureGatewaySettings(config);
3341
3378
  writeConfigWithSync(paths, config);
3342
3379
 
3343
3380
  console.log(chalk.green(`\n✅ ${typeLabel} 节点配置完成!`));
3344
3381
  console.log(chalk.cyan(` 节点: ${selectedEndpoint.name} (${selectedEndpoint.url})`));
3345
- console.log(chalk.gray(` 模型: ${modelConfig.name}`));
3382
+ console.log(chalk.gray(` 模型: ${modelConfig.name} (主模型)`));
3346
3383
  console.log(chalk.gray(` API Key: ${oldApiKey ? '已设置' : '未设置'}`));
3347
3384
  }
3348
3385
 
@@ -3475,15 +3512,39 @@ async function switchModel(paths) {
3475
3512
  }
3476
3513
 
3477
3514
  const selectedProvider = selected.split('/')[0];
3515
+ const selectedModelId = selected.split('/')[1];
3478
3516
  config.agents.defaults.model.primary = selected;
3479
3517
  if (!config.agents.defaults.models[selected]) {
3480
3518
  config.agents.defaults.models[selected] = { alias: selectedProvider };
3481
3519
  }
3520
+
3521
+ // 同步更新 provider.models,确保 primary 指向的模型在列表中
3522
+ const providerConfig = providers[selectedProvider];
3523
+ if (providerConfig && selectedModelId) {
3524
+ const existingModel = (providerConfig.models || []).find(m => m.id === selectedModelId);
3525
+ if (!existingModel) {
3526
+ const allModels = [...CLAUDE_MODELS, ...CODEX_MODELS];
3527
+ const knownModel = allModels.find(m => m.id === selectedModelId);
3528
+ const apiType = providerConfig.api || '';
3529
+ const isAnthropic = apiType.startsWith('anthropic');
3530
+ const defaultCtx = isAnthropic ? (API_CONFIG.claude?.contextWindow || 200000) : (API_CONFIG.codex?.contextWindow || 200000);
3531
+ const defaultMax = isAnthropic ? (API_CONFIG.claude?.maxTokens || 8192) : (API_CONFIG.codex?.maxTokens || 8192);
3532
+ providerConfig.models = [{
3533
+ id: selectedModelId,
3534
+ name: knownModel ? knownModel.name : selectedModelId,
3535
+ contextWindow: defaultCtx,
3536
+ maxTokens: defaultMax
3537
+ }];
3538
+ } else {
3539
+ providerConfig.models = [existingModel];
3540
+ }
3541
+ }
3542
+
3482
3543
  config.agents.defaults.model.fallbacks = (config.agents.defaults.model.fallbacks || []).filter(k => k !== selected);
3483
- // 把旧的 primary 加入 fallbacks (如果存在且不同)
3484
3544
  if (primary && primary !== selected && !config.agents.defaults.model.fallbacks.includes(primary)) {
3485
3545
  config.agents.defaults.model.fallbacks.push(primary);
3486
3546
  }
3547
+ ensureGatewaySettings(config);
3487
3548
  writeConfigWithSync(paths, config);
3488
3549
 
3489
3550
  const selectedProviderConfig = providers[selectedProvider];
@@ -3493,15 +3554,96 @@ async function switchModel(paths) {
3493
3554
  if (selectedProviderConfig) {
3494
3555
  console.log(chalk.gray(` 节点: ${selectedProviderConfig.baseUrl}`));
3495
3556
  }
3496
- console.log(chalk.yellow('\n💡 切换后建议重启 Gateway: openclaw gateway restart'));
3497
3557
 
3498
3558
  const gwPort = config.gateway?.port || 18789;
3499
3559
  const gwToken = config.gateway?.auth?.token;
3560
+
3561
+ // 自动重启 Gateway 使切换立即生效
3562
+ if (await isPortOpen(gwPort)) {
3563
+ const gwSpinner = ora({ text: '重启 Gateway 使模型切换生效...', spinner: 'dots' }).start();
3564
+ const ok = await restartGateway({ silent: true });
3565
+ if (ok) {
3566
+ gwSpinner.succeed('Gateway 已重启,模型切换已生效');
3567
+ } else {
3568
+ gwSpinner.fail('Gateway 重启失败,请手动重启: openclaw gateway restart');
3569
+ }
3570
+ } else {
3571
+ console.log(chalk.yellow('\n⚠️ Gateway 未运行,模型切换将在下次启动 Gateway 时生效'));
3572
+ console.log(chalk.gray(' 启动 Gateway: openclaw gateway'));
3573
+ }
3574
+
3500
3575
  if (gwToken) {
3501
3576
  console.log(chalk.green(`\n🌐 Web Dashboard:`));
3502
- console.log(chalk.cyan(` http://localhost:${gwPort}/?token=${gwToken}`));
3577
+ console.log(chalk.cyan(` http://127.0.0.1:${gwPort}/?token=${gwToken}`));
3578
+ }
3579
+ }
3580
+ // ============ 权限管理 (tools.profile) ============
3581
+ const TOOLS_PROFILES = [
3582
+ { id: 'full', name: '完整模式', desc: '编码 + 系统工具 + 文件操作(Claude Code / Codex 必需)' },
3583
+ { id: 'messaging', name: '聊天模式', desc: '仅对话,无文件/系统工具(安全但功能受限)' },
3584
+ ];
3585
+
3586
+ function getToolsProfileTag(paths) {
3587
+ try {
3588
+ const config = readConfig(paths.openclawConfig);
3589
+ const profile = config?.tools?.profile || 'messaging';
3590
+ if (profile === 'full') return chalk.green(' [完整]');
3591
+ return chalk.yellow(' [仅聊天]');
3592
+ } catch {
3593
+ return '';
3594
+ }
3595
+ }
3596
+
3597
+ async function manageToolsProfile(paths) {
3598
+ console.log(chalk.cyan('🔐 权限管理\n'));
3599
+ console.log(chalk.gray('OpenClaw 3.2+ 新增 tools.profile 控制:'));
3600
+ console.log(chalk.gray(' full = 完整编码能力(读写文件、执行命令、系统操作)'));
3601
+ console.log(chalk.gray(' messaging = 仅聊天(无法操作文件和系统,适合纯对话场景)'));
3602
+ console.log(chalk.yellow('\n⚠️ 使用 Claude Code / Codex 编码,必须选择「完整模式」\n'));
3603
+
3604
+ const config = readConfig(paths.openclawConfig) || {};
3605
+ const current = config?.tools?.profile || 'messaging';
3606
+ const currentLabel = current === 'full' ? '完整模式' : current === 'messaging' ? '聊天模式' : current;
3607
+ console.log(chalk.gray(`当前: ${currentLabel}\n`));
3608
+
3609
+ const { selected } = await inquirer.prompt([{
3610
+ type: 'list',
3611
+ name: 'selected',
3612
+ message: '选择权限模式:',
3613
+ choices: TOOLS_PROFILES.map(p => ({
3614
+ name: p.id === current ? `${p.name} — ${p.desc} (当前)` : `${p.name} — ${p.desc}`,
3615
+ value: p.id,
3616
+ })),
3617
+ default: current,
3618
+ }]);
3619
+
3620
+ if (selected === current) {
3621
+ console.log(chalk.gray('\n权限模式未变更'));
3622
+ return;
3623
+ }
3624
+
3625
+ if (!config.tools) config.tools = {};
3626
+ config.tools.profile = selected;
3627
+ ensureGatewaySettings(config);
3628
+ writeConfigWithSync(paths, config);
3629
+
3630
+ const label = TOOLS_PROFILES.find(p => p.id === selected)?.name || selected;
3631
+ console.log(chalk.green(`\n✅ 已切换到: ${label}`));
3632
+
3633
+ const gwPort = config.gateway?.port || 18789;
3634
+ if (await isPortOpen(gwPort)) {
3635
+ const gwSpinner = ora({ text: '重启 Gateway 使权限变更生效...', spinner: 'dots' }).start();
3636
+ const ok = await restartGateway({ silent: true });
3637
+ if (ok) {
3638
+ gwSpinner.succeed('Gateway 已重启,权限变更已生效');
3639
+ } else {
3640
+ gwSpinner.fail('Gateway 重启失败,请手动重启: openclaw gateway restart');
3641
+ }
3642
+ } else {
3643
+ console.log(chalk.gray(' Gateway 未运行,变更将在下次启动时生效'));
3503
3644
  }
3504
3645
  }
3646
+
3505
3647
  // ============ 测试连接 ============
3506
3648
  async function testConnection(paths, args = {}) {
3507
3649
  console.log(chalk.cyan('🧪 测试 OpenClaw Gateway 连接\n'));
@@ -3560,7 +3702,7 @@ async function testConnection(paths, args = {}) {
3560
3702
  console.log(chalk.gray(`当前激活: ${typeLabel}`));
3561
3703
  console.log(chalk.gray(`中转节点: ${provider.baseUrl}`));
3562
3704
  console.log(chalk.gray(`模型: ${primary}`));
3563
- console.log(chalk.gray(`Gateway: http://localhost:${gatewayPort}\n`));
3705
+ console.log(chalk.gray(`Gateway: http://127.0.0.1:${gatewayPort}\n`));
3564
3706
  // 获取 Gateway token
3565
3707
  const gatewayToken = config.gateway?.auth?.token;
3566
3708
  if (!gatewayToken) {
@@ -3584,11 +3726,8 @@ async function testConnection(paths, args = {}) {
3584
3726
  cleanupAgentProcesses();
3585
3727
 
3586
3728
  // 步骤1: 先重启 Gateway 使配置生效
3587
- console.log(chalk.cyan('步骤 1/2: 重启 Gateway 使配置生效...'));
3588
- const restartOk = await restartGateway();
3589
-
3590
- // 等待 Gateway 启动
3591
- const gwSpinner = ora({ text: '等待 Gateway 启动...', spinner: 'dots' }).start();
3729
+ const gwSpinner = ora({ text: '步骤 1/2: 重启 Gateway 使配置生效...', spinner: 'dots' }).start();
3730
+ const restartOk = await restartGateway({ silent: true });
3592
3731
 
3593
3732
  let gatewayRunning = false;
3594
3733
 
@@ -3687,7 +3826,7 @@ async function testConnection(paths, args = {}) {
3687
3826
  }
3688
3827
  }
3689
3828
 
3690
- console.log(chalk.gray(` 端点: http://localhost:${gatewayPort}/v1/responses`));
3829
+ console.log(chalk.gray(` 端点: http://127.0.0.1:${gatewayPort}/v1/responses`));
3691
3830
  const startTime = Date.now();
3692
3831
  let result = await testGatewayApi(gatewayPort, gatewayToken, primary);
3693
3832
  const latency = Date.now() - startTime;
@@ -3748,168 +3887,229 @@ async function testConnection(paths, args = {}) {
3748
3887
  }
3749
3888
 
3750
3889
  // ============ 重启 Gateway ============
3751
- async function restartGateway() {
3752
- console.log(chalk.cyan('\n正在重启 OpenClaw Gateway...'));
3890
+ async function restartGateway({ silent = false } = {}) {
3891
+ if (!silent) console.log(chalk.cyan('\n正在重启 OpenClaw Gateway...'));
3753
3892
 
3754
3893
  const gwEnv = detectGatewayEnv();
3894
+ const configPaths = getConfigPath();
3895
+ const gwConfig = readConfig(configPaths.openclawConfig);
3896
+ const gatewayPort = gwConfig?.gateway?.port || 18789;
3755
3897
 
3756
- // 如果 Gateway 在 Docker 容器里,通过 docker exec 重启
3898
+ // Docker 容器内重启
3757
3899
  if (gwEnv === 'docker') {
3758
- const container = await selectDockerContainer();
3759
- if (!container) {
3760
- console.log(chalk.red('❌ 未找到可用的 Docker 容器'));
3761
- return restartGatewayNative();
3762
- }
3763
- console.log(chalk.gray(` [检测] Gateway 运行在 Docker 容器: ${container.name} (${container.image})`));
3900
+ const result = await restartGatewayDocker(gatewayPort, silent);
3901
+ if (result) return true;
3902
+ if (!silent) console.log(chalk.yellow('Docker 容器内重启失败,尝试本地重启...'));
3903
+ }
3764
3904
 
3765
- const dockerCmds = [];
3766
- if (container.cli === 'node') {
3767
- dockerCmds.push(`node ${container.cliPath} gateway restart`);
3768
- } else {
3769
- if (container.cliPath) dockerCmds.push(`${container.cliPath} gateway restart`);
3770
- dockerCmds.push(`${container.cli} gateway restart`);
3905
+ // WSL 内重启
3906
+ if (gwEnv === 'wsl') {
3907
+ const result = await restartGatewayWsl(gatewayPort, silent);
3908
+ if (result) return true;
3909
+ if (!silent) console.log(chalk.yellow('WSL 内重启失败,尝试 Windows 原生重启...'));
3910
+ }
3911
+
3912
+ return restartGatewayNative(silent);
3913
+ }
3914
+
3915
+ function buildDockerInnerCmds(container, verb) {
3916
+ const cmds = [];
3917
+ if (container.cli === 'node') {
3918
+ cmds.push(`node ${container.cliPath} ${verb}`);
3919
+ } else {
3920
+ if (container.cliPath) cmds.push(`${container.cliPath} ${verb}`);
3921
+ cmds.push(`${container.cli} ${verb}`);
3922
+ }
3923
+ for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3924
+ cmds.push(`${name} ${verb}`);
3925
+ }
3926
+ return [...new Set(cmds)].filter(Boolean);
3927
+ }
3928
+
3929
+ async function restartGatewayDocker(gatewayPort, silent = false) {
3930
+ const container = await selectDockerContainer();
3931
+ if (!container) return false;
3932
+ if (!silent) console.log(chalk.gray(` [检测] Gateway 运行在 Docker 容器: ${container.name} (${container.image})`));
3933
+
3934
+ const cid = container.id;
3935
+ const shellVariants = ['sh -c', 'bash -lc', 'bash -c'];
3936
+
3937
+ // 策略 A:exec restart 命令(短超时),然后端口探测
3938
+ for (const cmd of buildDockerInnerCmds(container, 'gateway restart')) {
3939
+ for (const shell of shellVariants) {
3940
+ safeExec(dockerCmd(`exec ${cid} ${shell} "${cmd}"`), { timeout: 8000 });
3941
+ if (await waitForGateway(gatewayPort, '127.0.0.1', 5000)) {
3942
+ if (!silent) console.log(chalk.green(`✅ Gateway 已重启 (Docker: ${container.name})`));
3943
+ return true;
3944
+ }
3771
3945
  }
3946
+ }
3947
+
3948
+ // 策略 B:杀容器内旧进程 → spawn 启动新 Gateway → 端口探测
3949
+ if (!silent) console.log(chalk.gray(' Docker 内常规重启未生效,尝试杀进程后重新启动...'));
3950
+ try {
3772
3951
  for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3773
- dockerCmds.push(`${name} gateway restart`);
3952
+ safeExec(dockerCmd(`exec ${cid} sh -c "pkill -f '${name}.*gateway' 2>/dev/null || true"`), { timeout: 5000 });
3774
3953
  }
3954
+ safeExec(dockerCmd(`exec ${cid} sh -c "lsof -ti :${gatewayPort} 2>/dev/null | xargs -r kill -9 2>/dev/null || true"`), { timeout: 5000 });
3955
+ } catch { /* ignore */ }
3956
+ await new Promise(resolve => setTimeout(resolve, 2000));
3775
3957
 
3776
- // 每个命令尝试多种 shell(sh -c / bash -lc / bash -c)
3777
- const shellVariants = ['sh -c', 'bash -lc', 'bash -c'];
3778
-
3779
- return new Promise((resolve) => {
3780
- // 展开为 [cmd1+sh, cmd1+bash-lc, cmd1+bash-c, cmd2+sh, ...]
3781
- const allAttempts = [];
3782
- for (const cmd of [...new Set(dockerCmds)].filter(Boolean)) {
3783
- for (const shell of shellVariants) {
3784
- allAttempts.push(dockerCmd(`exec ${container.id} ${shell} "${cmd}"`));
3958
+ for (const cmd of buildDockerInnerCmds(container, 'gateway')) {
3959
+ for (const shell of shellVariants) {
3960
+ if (spawnDetachedInDocker(cid, cmd, shell)) {
3961
+ if (await waitForGateway(gatewayPort, '127.0.0.1', 12000)) {
3962
+ if (!silent) console.log(chalk.green(`✅ Gateway 已重启 (Docker: ${container.name})`));
3963
+ return true;
3785
3964
  }
3786
3965
  }
3787
- let tried = 0;
3788
- const tryNext = () => {
3789
- if (tried >= allAttempts.length) {
3790
- console.log(chalk.yellow('Docker 容器内 Gateway 重启失败,尝试本地重启...'));
3791
- restartGatewayNative().then(resolve);
3792
- return;
3793
- }
3794
- const fullCmd = allAttempts[tried++];
3795
- exec(fullCmd, { timeout: 30000 }, (error) => {
3796
- if (error) {
3797
- tryNext();
3798
- } else {
3799
- console.log(chalk.green(`✅ Gateway 已重启 (Docker: ${container.name})`));
3800
- resolve(true);
3801
- }
3802
- });
3803
- };
3804
- tryNext();
3805
- });
3966
+ }
3806
3967
  }
3807
3968
 
3808
- // 如果 Gateway 在 WSL 里,优先用 wsl -- 重启
3809
- if (gwEnv === 'wsl') {
3810
- console.log(chalk.gray(' [检测] Gateway 运行在 WSL 中'));
3811
- const wslCli = getWslCliBinary();
3812
- return new Promise((resolve) => {
3813
- const wslCmds = wslCli
3814
- ? [`wsl -- bash -lc "${wslCli} gateway restart"`]
3815
- : [
3816
- 'wsl -- bash -lc "openclaw gateway restart"',
3817
- 'wsl -- bash -lc "clawdbot gateway restart"',
3818
- 'wsl -- bash -lc "moltbot gateway restart"',
3819
- ];
3820
- let tried = 0;
3821
- const tryNext = () => {
3822
- if (tried >= wslCmds.length) {
3823
- console.log(chalk.yellow('WSL 内 Gateway 重启失败,尝试 Windows 原生重启...'));
3824
- restartGatewayNative().then(resolve);
3825
- return;
3826
- }
3827
- const cmd = wslCmds[tried++];
3828
- exec(cmd, { timeout: 30000 }, (error, stdout, stderr) => {
3829
- const errStr = (stderr || '').toLowerCase();
3830
- const wslGarbled = errStr.includes('wsl:') && errStr.includes('localhost') && errStr.includes('nat');
3969
+ return false;
3970
+ }
3831
3971
 
3832
- if (error || errStr.includes('/usr/bin/env:') || wslGarbled) {
3833
- tryNext();
3834
- } else {
3835
- console.log(chalk.green('Gateway 已重启 (WSL)'));
3836
- resolve(true);
3837
- }
3838
- });
3839
- };
3840
- tryNext();
3841
- });
3972
+ async function restartGatewayWsl(gatewayPort, silent = false) {
3973
+ if (!silent) console.log(chalk.gray(' [检测] Gateway 运行在 WSL 中'));
3974
+ const wslCli = getWslCliBinary();
3975
+ const names = ['openclaw', 'clawdbot', 'moltbot'];
3976
+
3977
+ // 构建 WSL 重启命令
3978
+ const wslRestartCmds = [];
3979
+ if (wslCli) wslRestartCmds.push(`wsl -- bash -lc "${wslCli} gateway restart"`);
3980
+ for (const name of names) wslRestartCmds.push(`wsl -- bash -lc "${name} gateway restart"`);
3981
+
3982
+ // 策略 A:exec restart(短超时)+ 端口探测
3983
+ for (const cmd of wslRestartCmds) {
3984
+ safeExec(cmd, { timeout: 8000 });
3985
+ if (await waitForGateway(gatewayPort, '127.0.0.1', 5000)) {
3986
+ if (!silent) console.log(chalk.green('✅ Gateway 已重启 (WSL)'));
3987
+ return true;
3988
+ }
3842
3989
  }
3843
3990
 
3844
- return restartGatewayNative();
3845
- }
3846
-
3847
- // 强制重启 Gateway:杀掉旧进程,再用已找到的 CLI 路径启动新 Gateway
3848
- async function forceRestartGateway(resolved, nodeInfo, useNode, env, gatewayPort = 18789) {
3849
- console.log(chalk.yellow('\n🔄 尝试强制重启 Gateway(杀旧进程 → 启动新进程)...'));
3991
+ // 策略 B:杀 WSL 内旧进程 → spawn 启动 → 端口探测
3992
+ if (!silent) console.log(chalk.gray(' WSL 内常规重启未生效,尝试杀进程后重新启动...'));
3993
+ try {
3994
+ for (const name of names) {
3995
+ safeExec(`wsl -- bash -c "pkill -f '${name}.*gateway' 2>/dev/null || true"`, { timeout: 5000 });
3996
+ }
3997
+ safeExec(`wsl -- bash -c "lsof -ti :${gatewayPort} 2>/dev/null | xargs -r kill -9 2>/dev/null || true"`, { timeout: 5000 });
3998
+ } catch { /* ignore */ }
3999
+ await new Promise(resolve => setTimeout(resolve, 2000));
3850
4000
 
3851
- // Docker 容器内强制重启
3852
- if (detectGatewayEnv() === 'docker' && _selectedDockerContainer) {
3853
- const cid = _selectedDockerContainer.id;
3854
- const cName = _selectedDockerContainer.name;
3855
- console.log(chalk.gray(` [Docker] 在容器 ${cName} 内强制重启...`));
4001
+ const wslStartCmds = [];
4002
+ if (wslCli) wslStartCmds.push(`wsl -- bash -lc "${wslCli} gateway"`);
4003
+ for (const name of names) wslStartCmds.push(`wsl -- bash -lc "${name} gateway"`);
3856
4004
 
3857
- // 1. 杀掉容器内旧 Gateway 进程
3858
- try {
3859
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3860
- safeExec(dockerCmd(`exec ${cid} sh -c "pkill -f '${name}.*gateway' 2>/dev/null || true"`), { timeout: 5000 });
4005
+ for (const cmd of wslStartCmds) {
4006
+ if (spawnDetached(cmd, process.env)) {
4007
+ if (await waitForGateway(gatewayPort, '127.0.0.1', 12000)) {
4008
+ if (!silent) console.log(chalk.green(' Gateway 已重启 (WSL)'));
4009
+ return true;
3861
4010
  }
3862
- safeExec(dockerCmd(`exec ${cid} sh -c "lsof -ti :${gatewayPort} 2>/dev/null | xargs -r kill -9 2>/dev/null || true"`), { timeout: 5000 });
3863
- console.log(chalk.gray(' 已尝试清理容器内旧 Gateway 进程'));
3864
- } catch { /* ignore */ }
4011
+ }
4012
+ }
3865
4013
 
3866
- await new Promise(resolve => setTimeout(resolve, 2000));
4014
+ return false;
4015
+ }
3867
4016
 
3868
- // 2. 启动新 Gateway
3869
- const dockerCmds = [];
3870
- if (_selectedDockerContainer.cli === 'node') {
3871
- dockerCmds.push(`node ${_selectedDockerContainer.cliPath} gateway`);
3872
- } else {
3873
- if (_selectedDockerContainer.cliPath) dockerCmds.push(`${_selectedDockerContainer.cliPath} gateway`);
3874
- dockerCmds.push(`${_selectedDockerContainer.cli} gateway`);
4017
+ async function restartGatewayNative(silent = false) {
4018
+ const { cliBinary: resolved, nodeMajor } = getCliMeta();
4019
+ const nodeInfo = findCompatibleNode(nodeMajor);
4020
+ const env = { ...process.env, PATH: extendPathEnv(nodeInfo ? nodeInfo.path : null) };
4021
+ const useNode = resolved && nodeInfo && isNodeShebang(resolved);
4022
+
4023
+ const configPaths = getConfigPath();
4024
+ const gwConfig = readConfig(configPaths.openclawConfig);
4025
+ const gatewayPort = gwConfig?.gateway?.port || 18789;
4026
+
4027
+ // 策略 A:尝试 exec "gateway restart"(短超时),然后端口探测
4028
+ const restartCmds = buildGatewayCommands(resolved, nodeInfo, useNode, 'restart');
4029
+ if (restartCmds.length > 0) {
4030
+ // 只尝试第一条(最精确的路径),避免逐条超时累积
4031
+ safeExec(restartCmds[0], { timeout: 8000, env });
4032
+ if (await waitForGateway(gatewayPort, '127.0.0.1', 5000)) {
4033
+ if (!silent) console.log(chalk.green(`✅ Gateway 已重启`));
4034
+ return true;
3875
4035
  }
3876
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3877
- dockerCmds.push(`${name} gateway`);
4036
+ }
4037
+
4038
+ // 策略 B:杀旧进程 → spawn 后台启动新 Gateway → 端口探测
4039
+ if (!silent) console.log(chalk.gray(' 常规重启未生效,尝试杀进程后重新启动...'));
4040
+ await killGatewayProcesses(gatewayPort);
4041
+ await new Promise(resolve => setTimeout(resolve, 1000));
4042
+
4043
+ const startCmds = buildGatewayCommands(resolved, nodeInfo, useNode, 'start');
4044
+ for (const cmd of startCmds) {
4045
+ if (spawnDetached(cmd, env)) {
4046
+ if (await waitForGateway(gatewayPort, '127.0.0.1', 10000)) {
4047
+ if (!silent) console.log(chalk.green('✅ Gateway 已重启'));
4048
+ return true;
4049
+ }
4050
+ break; // spawn 成功但端口未通,不再逐条重试
3878
4051
  }
4052
+ }
3879
4053
 
3880
- for (const cmd of [...new Set(dockerCmds)].filter(Boolean)) {
3881
- console.log(chalk.gray(` 尝试在容器内启动: ${cmd}`));
3882
- for (const shell of ['sh -c', 'bash -lc', 'bash -c']) {
3883
- if (spawnDetachedInDocker(cid, cmd, shell)) {
3884
- if (await waitForGateway(gatewayPort, '127.0.0.1', 12000)) {
3885
- console.log(chalk.green(`✅ Gateway 已在 Docker 容器 ${cName} 内强制重启成功`));
4054
+ // 策略 C:用 login shell 启动(加载 nvm/fnm 等 PATH)
4055
+ if (process.platform !== 'win32') {
4056
+ if (!silent) console.log(chalk.gray(' 尝试通过 login shell 启动...'));
4057
+ let launched = false;
4058
+ for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
4059
+ if (launched) break;
4060
+ for (const sh of ['/bin/zsh', '/bin/bash']) {
4061
+ if (!fs.existsSync(sh)) continue;
4062
+ if (spawnDetached(`${sh} -lc '${name} gateway'`, env)) {
4063
+ launched = true;
4064
+ if (await waitForGateway(gatewayPort, '127.0.0.1', 10000)) {
4065
+ if (!silent) console.log(chalk.green('✅ Gateway 已重启 (login shell)'));
3886
4066
  return true;
3887
4067
  }
4068
+ break;
3888
4069
  }
3889
4070
  }
3890
4071
  }
4072
+ }
3891
4073
 
3892
- console.log(chalk.red('❌ Docker 容器内强制重启也失败了'));
3893
- console.log(chalk.gray(` 请手动进入容器重启: docker exec -it ${cid} bash`));
3894
- console.log(chalk.gray(` 然后执行: openclaw gateway / clawdbot gateway / moltbot gateway`));
3895
- return false;
4074
+ // 全部失败,输出诊断
4075
+ if (!silent) {
4076
+ console.log(chalk.red(`❌ 重启失败: 找不到 openclaw/clawdbot/moltbot 命令`));
4077
+ console.log(chalk.gray(` 请手动运行: openclaw gateway restart`));
4078
+ console.log(chalk.gray(` 或: clawdbot gateway restart`));
4079
+ console.log(chalk.gray(` 或: moltbot gateway restart`));
4080
+ printGatewayDiagnostics(resolved);
4081
+ }
4082
+ return false;
4083
+ }
4084
+
4085
+ function buildGatewayCommands(resolved, nodeInfo, useNode, action) {
4086
+ const verb = action === 'start' ? 'gateway' : 'gateway restart';
4087
+ const commands = [];
4088
+
4089
+ if (resolved) {
4090
+ if (useNode && nodeInfo) {
4091
+ commands.push(`"${nodeInfo.path}" "${resolved}" ${verb}`);
4092
+ } else {
4093
+ commands.push(`"${resolved}" ${verb}`);
4094
+ }
3896
4095
  }
3897
4096
 
3898
- // 1. 杀掉旧 Gateway 进程
4097
+ const names = ['openclaw', 'clawdbot', 'moltbot'];
4098
+ for (const name of names) commands.push(`${name} ${verb}`);
4099
+
4100
+ return [...new Set(commands)].filter(Boolean);
4101
+ }
4102
+
4103
+ async function killGatewayProcesses(gatewayPort = 18789) {
3899
4104
  try {
3900
4105
  if (process.platform === 'win32') {
3901
- // Windows: 通过 taskkill 杀掉占用端口的 node 进程
3902
4106
  const findPid = safeExec(`netstat -ano | findstr ":${gatewayPort}"`, { timeout: 5000 });
3903
4107
  if (findPid.ok && findPid.output) {
3904
- const lines = findPid.output.split('\n').filter(l => l.includes('LISTENING'));
3905
- for (const line of lines) {
4108
+ for (const line of findPid.output.split('\n').filter(l => l.includes('LISTENING'))) {
3906
4109
  const pid = line.trim().split(/\s+/).pop();
3907
- if (pid && /^\d+$/.test(pid)) {
3908
- safeExec(`taskkill /F /PID ${pid}`, { timeout: 5000 });
3909
- }
4110
+ if (pid && /^\d+$/.test(pid)) safeExec(`taskkill /F /PID ${pid}`, { timeout: 5000 });
3910
4111
  }
3911
4112
  }
3912
- // Windows + WSL: 也清理 WSL 内的 gateway 进程
3913
4113
  if (isWslAvailable()) {
3914
4114
  for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3915
4115
  safeExec(`wsl -- bash -c "pkill -f '${name}.*gateway' 2>/dev/null || true"`, { timeout: 5000 });
@@ -3917,173 +4117,36 @@ async function forceRestartGateway(resolved, nodeInfo, useNode, env, gatewayPort
3917
4117
  safeExec(`wsl -- bash -c "lsof -ti :${gatewayPort} 2>/dev/null | xargs -r kill -9 2>/dev/null || true"`, { timeout: 5000 });
3918
4118
  }
3919
4119
  } else {
3920
- // Linux/macOS: pkill gateway 相关进程
3921
4120
  for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3922
4121
  safeExec(`pkill -f '${name}.*gateway' 2>/dev/null || true`);
3923
4122
  }
3924
- // 备用:通过端口找进程并杀掉
3925
4123
  const lsof = safeExec(`lsof -ti :${gatewayPort} 2>/dev/null`);
3926
4124
  if (lsof.ok && lsof.output) {
3927
4125
  for (const pid of lsof.output.trim().split('\n').filter(Boolean)) {
3928
- if (/^\d+$/.test(pid.trim())) {
3929
- safeExec(`kill -9 ${pid.trim()} 2>/dev/null || true`);
3930
- }
4126
+ if (/^\d+$/.test(pid.trim())) safeExec(`kill -9 ${pid.trim()} 2>/dev/null || true`);
3931
4127
  }
3932
4128
  }
3933
4129
  }
3934
- console.log(chalk.gray(' 已尝试清理旧 Gateway 进程'));
3935
4130
  } catch { /* ignore */ }
3936
-
3937
- // 2. 等待端口释放
3938
- await new Promise(resolve => setTimeout(resolve, 2000));
3939
-
3940
- // 3. 用已找到的 CLI 路径启动新 Gateway
3941
- const startCmds = [];
3942
-
3943
- // 优先用已解析的完整路径
3944
- if (resolved) {
3945
- if (useNode && nodeInfo) {
3946
- startCmds.push(`"${nodeInfo.path}" "${resolved}" gateway`);
3947
- } else {
3948
- startCmds.push(`"${resolved}" gateway`);
3949
- }
3950
- }
3951
-
3952
- // Fallback: login shell 方式(加载 nvm 等 PATH)
3953
- if (process.platform !== 'win32') {
3954
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3955
- startCmds.push(`bash -lc '${name} gateway'`);
3956
- }
3957
- } else {
3958
- // Windows: 先尝试原生命令
3959
- startCmds.push('openclaw gateway', 'clawdbot gateway', 'moltbot gateway');
3960
- // Windows + WSL: 也尝试通过 WSL 启动 gateway
3961
- if (isWslAvailable()) {
3962
- const wslCli = getWslCliBinary();
3963
- if (wslCli) {
3964
- startCmds.push(`wsl -- bash -lc "${wslCli} gateway"`);
3965
- }
3966
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3967
- startCmds.push(`wsl -- bash -lc "${name} gateway"`);
3968
- }
3969
- }
3970
- }
3971
-
3972
- for (const cmd of [...new Set(startCmds)].filter(Boolean)) {
3973
- console.log(chalk.gray(` 尝试启动: ${cmd}`));
3974
- if (spawnDetached(cmd, env)) {
3975
- // 等待新 Gateway 启动
3976
- if (await waitForGateway(gatewayPort, '127.0.0.1', 12000)) {
3977
- console.log(chalk.green('✅ Gateway 已强制重启成功'));
3978
- console.log(chalk.gray(' 现在可以在 Web/Telegram/Discord 等渠道测试对话了'));
3979
- return true;
3980
- }
3981
- }
3982
- }
3983
-
3984
- console.log(chalk.red('❌ 强制重启也失败了'));
3985
- console.log(chalk.gray(' 请手动重启 Gateway:'));
3986
- console.log(chalk.gray(' 1. 关闭旧进程: pkill -f "gateway" 或 taskkill'));
3987
- console.log(chalk.gray(' 2. 启动新进程: openclaw gateway / clawdbot gateway / moltbot gateway'));
3988
- return false;
3989
4131
  }
3990
4132
 
3991
- async function restartGatewayNative() {
3992
- const { cliBinary: resolved, nodeMajor } = getCliMeta();
3993
- const nodeInfo = findCompatibleNode(nodeMajor);
3994
- const env = { ...process.env, PATH: extendPathEnv(nodeInfo ? nodeInfo.path : null) };
3995
- const useNode = resolved && nodeInfo && isNodeShebang(resolved);
3996
-
3997
- // 尝试多种命令
3998
- const commands = resolved
3999
- ? [
4000
- useNode ? `"${nodeInfo.path}" "${resolved}" gateway restart` : `"${resolved}" gateway restart`
4001
- ]
4002
- : [
4003
- 'openclaw gateway restart',
4004
- 'clawdbot gateway restart',
4005
- 'moltbot gateway restart',
4006
- 'npx openclaw gateway restart',
4007
- 'npx clawdbot gateway restart',
4008
- 'npx moltbot gateway restart'
4009
- ];
4010
-
4011
- // Windows + WSL: 追加 WSL 命令作为额外回退
4012
- if (process.platform === 'win32' && isWslAvailable()) {
4013
- const wslCli = getWslCliBinary();
4014
- if (wslCli) {
4015
- commands.push(`wsl -- bash -lc "${wslCli} gateway restart"`);
4016
- }
4017
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
4018
- commands.push(`wsl -- bash -lc "${name} gateway restart"`);
4019
- }
4133
+ function printGatewayDiagnostics(resolved) {
4134
+ console.log(chalk.gray(`\n [诊断] resolveCliBinary = ${resolved || 'null'}`));
4135
+ const npmPrefix = safeExec('npm prefix -g');
4136
+ if (npmPrefix.ok) console.log(chalk.gray(` [诊断] npm prefix -g = ${npmPrefix.output}`));
4137
+ for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
4138
+ 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`);
4139
+ if (which.ok && which.output) console.log(chalk.gray(` [诊断] ${name} -> ${which.output.split('\n')[0].trim()}`));
4020
4140
  }
4021
-
4022
- // Docker: 追加 Docker 容器命令作为额外回退
4023
4141
  if (isDockerAvailable()) {
4024
- const dockerContainers = findOpenclawDockerContainers();
4025
- for (const c of dockerContainers) {
4026
- if (c.cli === 'node') {
4027
- commands.push(dockerCmd(`exec ${c.id} bash -lc "node ${c.cliPath} gateway restart"`));
4028
- } else if (c.cli !== 'unknown') {
4029
- commands.push(dockerCmd(`exec ${c.id} bash -lc "${c.cli} gateway restart"`));
4030
- }
4031
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
4032
- commands.push(dockerCmd(`exec ${c.id} bash -lc "${name} gateway restart"`));
4142
+ const dContainers = findOpenclawDockerContainers();
4143
+ if (dContainers.length > 0) {
4144
+ console.log(chalk.gray(` [诊断] Docker 容器 (含 openclaw/clawdbot/moltbot):`));
4145
+ for (const c of dContainers) {
4146
+ console.log(chalk.gray(` - ${c.name} (${c.image}) [${c.cli}: ${c.cliPath}]`));
4033
4147
  }
4034
4148
  }
4035
4149
  }
4036
-
4037
- return new Promise((resolve) => {
4038
- let tried = 0;
4039
-
4040
- const tryNext = () => {
4041
- if (tried >= commands.length) {
4042
- console.log(chalk.red(`❌ 重启失败: 找不到 openclaw/clawdbot/moltbot 命令`));
4043
- console.log(chalk.gray(` 请手动运行: openclaw gateway restart`));
4044
- console.log(chalk.gray(` 或: clawdbot gateway restart`));
4045
- console.log(chalk.gray(` 或: moltbot gateway restart`));
4046
- // 诊断信息
4047
- console.log(chalk.gray(`\n [诊断] resolveCliBinary = ${resolved || 'null'}`));
4048
- const npmPrefix = safeExec('npm prefix -g');
4049
- if (npmPrefix.ok) console.log(chalk.gray(` [诊断] npm prefix -g = ${npmPrefix.output}`));
4050
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
4051
- 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`);
4052
- if (which.ok && which.output) console.log(chalk.gray(` [诊断] ${name} -> ${which.output.split('\n')[0].trim()}`));
4053
- }
4054
- // Docker 诊断
4055
- if (isDockerAvailable()) {
4056
- const dContainers = findOpenclawDockerContainers();
4057
- if (dContainers.length > 0) {
4058
- console.log(chalk.gray(` [诊断] Docker 容器 (含 openclaw/clawdbot/moltbot):`));
4059
- for (const c of dContainers) {
4060
- console.log(chalk.gray(` - ${c.name} (${c.image}) [${c.cli}: ${c.cliPath}]`));
4061
- }
4062
- } else {
4063
- console.log(chalk.gray(` [诊断] Docker 可用,但未找到含 openclaw/clawdbot/moltbot 的容器`));
4064
- }
4065
- }
4066
- // 尝试强制重启
4067
- forceRestartGateway(resolved, nodeInfo, useNode, env).then(resolve);
4068
- return;
4069
- }
4070
-
4071
- const cmd = commands[tried];
4072
- tried++;
4073
-
4074
- exec(cmd, { timeout: 30000, env }, (error) => {
4075
- if (error) {
4076
- tryNext();
4077
- } else {
4078
- console.log(chalk.green(`✅ Gateway 已重启`));
4079
- console.log(chalk.gray(` 现在可以在 Web/Telegram/Discord 等渠道测试对话了`));
4080
- resolve(true);
4081
- }
4082
- });
4083
- };
4084
-
4085
- tryNext();
4086
- });
4087
4150
  }
4088
4151
 
4089
4152
  // Gateway API 测试 - 通过本地 Gateway 端口测试
package/install.ps1 CHANGED
@@ -2,7 +2,7 @@
2
2
  # maxapi 一键环境安装脚本(Windows PowerShell)
3
3
  # 用法(Node.js 不存在时,先装 Node.js 再 npx):
4
4
  # .\install.ps1
5
- # $env:PACKAGE='llmaxapi'; .\install.ps1
5
+ # $env:PACKAGE='maxai'; .\install.ps1
6
6
  # ============================================================
7
7
 
8
8
  $ErrorActionPreference = "Stop"
package/install.sh CHANGED
@@ -3,7 +3,6 @@
3
3
  # maxapi 一键环境安装脚本(macOS / Linux / WSL)
4
4
  # 用法(Node.js 不存在时,先装 Node.js 再 npx):
5
5
  # bash install.sh
6
- # bash install.sh llmaxapi
7
6
  # bash install.sh --no-run
8
7
  # ============================================================
9
8
  set -euo pipefail
@@ -16,7 +15,7 @@ MIN_NODE_MAJOR=18
16
15
  for arg in "$@"; do
17
16
  case "$arg" in
18
17
  --no-run) NO_RUN=true ;;
19
- yymaxapi|llmaxapi) PACKAGE="$arg" ;;
18
+ yymaxapi) PACKAGE="$arg" ;;
20
19
  esac
21
20
  done
22
21
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yymaxapi",
3
- "version": "1.0.48",
3
+ "version": "1.0.50",
4
4
  "description": "跨平台 OpenClaw/Clawdbot 配置管理工具 - 管理中转地址、模型切换、API Keys、测速优化",
5
5
  "main": "bin/yymaxapi.js",
6
6
  "bin": {