yymaxapi 1.0.47 → 1.0.49

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
@@ -1659,6 +1659,7 @@ function ensureGatewaySettings(config) {
1659
1659
  if (isLocal && gateway.remote.token !== gateway.auth.token) {
1660
1660
  gateway.remote.token = gateway.auth.token;
1661
1661
  }
1662
+
1662
1663
  }
1663
1664
 
1664
1665
  function isPortOpen(port, host = '127.0.0.1', timeoutMs = 800) {
@@ -2662,10 +2663,21 @@ async function autoActivate(paths, args = {}) {
2662
2663
 
2663
2664
  // 增量写入: 只添加/更新云翼 provider,保留其他已有配置
2664
2665
 
2665
- // Claude 侧
2666
+ // Claude 侧 — 让用户选模型
2666
2667
  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' };
2668
+ let claudeModelId = (args['claude-model'] || '').toString().trim();
2669
+ if (!claudeModelId && CLAUDE_MODELS.length > 1) {
2670
+ const { picked } = await inquirer.prompt([{
2671
+ type: 'list',
2672
+ name: 'picked',
2673
+ message: '选择 Claude 模型:',
2674
+ choices: CLAUDE_MODELS.map(m => ({ name: m.name, value: m.id })),
2675
+ default: CLAUDE_MODELS[0].id
2676
+ }]);
2677
+ claudeModelId = picked;
2678
+ }
2679
+ if (!claudeModelId) claudeModelId = CLAUDE_MODELS[0]?.id || 'claude-sonnet-4-6';
2680
+ const claudeModel = CLAUDE_MODELS.find(m => m.id === claudeModelId) || { id: claudeModelId, name: claudeModelId };
2669
2681
  const claudeModelKey = `${claudeProviderName}/${claudeModelId}`;
2670
2682
 
2671
2683
  config.models.providers[claudeProviderName] = {
@@ -2685,10 +2697,21 @@ async function autoActivate(paths, args = {}) {
2685
2697
  config.auth.profiles[`${claudeProviderName}:default`] = { provider: claudeProviderName, mode: 'api_key' };
2686
2698
  config.agents.defaults.models[claudeModelKey] = { alias: claudeProviderName };
2687
2699
 
2688
- // Codex 侧
2700
+ // Codex 侧 — 让用户选模型
2689
2701
  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' };
2702
+ let codexModelId = (args['codex-model'] || '').toString().trim();
2703
+ if (!codexModelId && CODEX_MODELS.length > 1) {
2704
+ const { pickedCodex } = await inquirer.prompt([{
2705
+ type: 'list',
2706
+ name: 'pickedCodex',
2707
+ message: '选择 Codex 模型:',
2708
+ choices: CODEX_MODELS.map(m => ({ name: m.name, value: m.id })),
2709
+ default: CODEX_MODELS[0].id
2710
+ }]);
2711
+ codexModelId = pickedCodex;
2712
+ }
2713
+ if (!codexModelId) codexModelId = CODEX_MODELS[0]?.id || 'gpt-5.3-codex';
2714
+ const codexModel = CODEX_MODELS.find(m => m.id === codexModelId) || { id: codexModelId, name: codexModelId };
2692
2715
  const codexModelKey = `${codexProviderName}/${codexModelId}`;
2693
2716
 
2694
2717
  config.models.providers[codexProviderName] = {
@@ -3155,6 +3178,7 @@ async function main() {
3155
3178
  { name: ' 配置 Opencode', value: 'activate_opencode' },
3156
3179
  new inquirer.Separator(' -- 工具 --'),
3157
3180
  { name: ' 切换模型', value: 'switch_model' },
3181
+ { name: ` 权限管理${getToolsProfileTag(paths)}`, value: 'tools_profile' },
3158
3182
  { name: ' 测试连接', value: 'test_connection' },
3159
3183
  { name: ' 查看配置', value: 'view_config' },
3160
3184
  { name: ' 恢复默认', value: 'restore' },
@@ -3183,6 +3207,9 @@ async function main() {
3183
3207
  case 'switch_model':
3184
3208
  await switchModel(paths);
3185
3209
  break;
3210
+ case 'tools_profile':
3211
+ await manageToolsProfile(paths);
3212
+ break;
3186
3213
  case 'view_config':
3187
3214
  await viewConfig(paths);
3188
3215
  break;
@@ -3219,20 +3246,32 @@ function getConfigStatusLine(paths) {
3219
3246
  const primary = config?.agents?.defaults?.model?.primary || '';
3220
3247
  const primaryProvider = primary.split('/')[0] || '';
3221
3248
 
3222
- const PROVIDER_LABELS = {
3249
+ const KNOWN_PROVIDERS = {
3223
3250
  'claude-yunyi': 'Claude(包月)',
3224
3251
  'yunyi': 'Codex(包月)',
3225
3252
  'heibai': 'MAXAPI(按量)',
3226
3253
  };
3227
3254
 
3228
3255
  const parts = [];
3256
+ let otherCount = 0;
3229
3257
  for (const p of providers) {
3230
- const label = PROVIDER_LABELS[p] || p;
3231
- const isActive = p === primaryProvider;
3232
- parts.push(isActive ? chalk.green(`${label} ✓`) : chalk.yellow(`${label} ○`));
3258
+ if (KNOWN_PROVIDERS[p]) {
3259
+ const label = KNOWN_PROVIDERS[p];
3260
+ const isActive = p === primaryProvider;
3261
+ parts.push(isActive ? chalk.green(`${label} ✓`) : chalk.yellow(`${label} ○`));
3262
+ } else if (p === primaryProvider) {
3263
+ // 未知 provider 但是当前主力,显示名称
3264
+ parts.push(chalk.green(`${p} ✓`));
3265
+ } else {
3266
+ otherCount++;
3267
+ }
3233
3268
  }
3234
3269
 
3235
- if (parts.length === 0) {
3270
+ if (otherCount > 0) {
3271
+ parts.push(chalk.gray(`+${otherCount} 其他`));
3272
+ }
3273
+
3274
+ if (parts.length === 0 && otherCount === 0) {
3236
3275
  return chalk.gray('当前状态: 未配置任何模型');
3237
3276
  }
3238
3277
 
@@ -3322,15 +3361,18 @@ async function selectNode(paths, type) {
3322
3361
  }]
3323
3362
  };
3324
3363
 
3325
- // 注册模型
3364
+ // 注册模型并设为主模型
3326
3365
  const modelKey = `${apiConfig.providerName}/${modelConfig.id}`;
3327
3366
  config.agents.defaults.models[modelKey] = { alias: apiConfig.providerName };
3367
+ config.agents.defaults.model.primary = modelKey;
3368
+ config.agents.defaults.model.fallbacks = (config.agents.defaults.model.fallbacks || []).filter(k => k !== modelKey);
3328
3369
 
3370
+ ensureGatewaySettings(config);
3329
3371
  writeConfigWithSync(paths, config);
3330
3372
 
3331
3373
  console.log(chalk.green(`\n✅ ${typeLabel} 节点配置完成!`));
3332
3374
  console.log(chalk.cyan(` 节点: ${selectedEndpoint.name} (${selectedEndpoint.url})`));
3333
- console.log(chalk.gray(` 模型: ${modelConfig.name}`));
3375
+ console.log(chalk.gray(` 模型: ${modelConfig.name} (主模型)`));
3334
3376
  console.log(chalk.gray(` API Key: ${oldApiKey ? '已设置' : '未设置'}`));
3335
3377
  }
3336
3378
 
@@ -3463,15 +3505,39 @@ async function switchModel(paths) {
3463
3505
  }
3464
3506
 
3465
3507
  const selectedProvider = selected.split('/')[0];
3508
+ const selectedModelId = selected.split('/')[1];
3466
3509
  config.agents.defaults.model.primary = selected;
3467
3510
  if (!config.agents.defaults.models[selected]) {
3468
3511
  config.agents.defaults.models[selected] = { alias: selectedProvider };
3469
3512
  }
3513
+
3514
+ // 同步更新 provider.models,确保 primary 指向的模型在列表中
3515
+ const providerConfig = providers[selectedProvider];
3516
+ if (providerConfig && selectedModelId) {
3517
+ const existingModel = (providerConfig.models || []).find(m => m.id === selectedModelId);
3518
+ if (!existingModel) {
3519
+ const allModels = [...CLAUDE_MODELS, ...CODEX_MODELS];
3520
+ const knownModel = allModels.find(m => m.id === selectedModelId);
3521
+ const apiType = providerConfig.api || '';
3522
+ const isAnthropic = apiType.startsWith('anthropic');
3523
+ const defaultCtx = isAnthropic ? (API_CONFIG.claude?.contextWindow || 200000) : (API_CONFIG.codex?.contextWindow || 200000);
3524
+ const defaultMax = isAnthropic ? (API_CONFIG.claude?.maxTokens || 8192) : (API_CONFIG.codex?.maxTokens || 8192);
3525
+ providerConfig.models = [{
3526
+ id: selectedModelId,
3527
+ name: knownModel ? knownModel.name : selectedModelId,
3528
+ contextWindow: defaultCtx,
3529
+ maxTokens: defaultMax
3530
+ }];
3531
+ } else {
3532
+ providerConfig.models = [existingModel];
3533
+ }
3534
+ }
3535
+
3470
3536
  config.agents.defaults.model.fallbacks = (config.agents.defaults.model.fallbacks || []).filter(k => k !== selected);
3471
- // 把旧的 primary 加入 fallbacks (如果存在且不同)
3472
3537
  if (primary && primary !== selected && !config.agents.defaults.model.fallbacks.includes(primary)) {
3473
3538
  config.agents.defaults.model.fallbacks.push(primary);
3474
3539
  }
3540
+ ensureGatewaySettings(config);
3475
3541
  writeConfigWithSync(paths, config);
3476
3542
 
3477
3543
  const selectedProviderConfig = providers[selectedProvider];
@@ -3481,15 +3547,96 @@ async function switchModel(paths) {
3481
3547
  if (selectedProviderConfig) {
3482
3548
  console.log(chalk.gray(` 节点: ${selectedProviderConfig.baseUrl}`));
3483
3549
  }
3484
- console.log(chalk.yellow('\n💡 切换后建议重启 Gateway: openclaw gateway restart'));
3485
3550
 
3486
3551
  const gwPort = config.gateway?.port || 18789;
3487
3552
  const gwToken = config.gateway?.auth?.token;
3553
+
3554
+ // 自动重启 Gateway 使切换立即生效
3555
+ if (await isPortOpen(gwPort)) {
3556
+ const gwSpinner = ora({ text: '重启 Gateway 使模型切换生效...', spinner: 'dots' }).start();
3557
+ const ok = await restartGateway({ silent: true });
3558
+ if (ok) {
3559
+ gwSpinner.succeed('Gateway 已重启,模型切换已生效');
3560
+ } else {
3561
+ gwSpinner.fail('Gateway 重启失败,请手动重启: openclaw gateway restart');
3562
+ }
3563
+ } else {
3564
+ console.log(chalk.yellow('\n⚠️ Gateway 未运行,模型切换将在下次启动 Gateway 时生效'));
3565
+ console.log(chalk.gray(' 启动 Gateway: openclaw gateway'));
3566
+ }
3567
+
3488
3568
  if (gwToken) {
3489
3569
  console.log(chalk.green(`\n🌐 Web Dashboard:`));
3490
3570
  console.log(chalk.cyan(` http://localhost:${gwPort}/?token=${gwToken}`));
3491
3571
  }
3492
3572
  }
3573
+ // ============ 权限管理 (tools.profile) ============
3574
+ const TOOLS_PROFILES = [
3575
+ { id: 'full', name: '完整模式', desc: '编码 + 系统工具 + 文件操作(Claude Code / Codex 必需)' },
3576
+ { id: 'messaging', name: '聊天模式', desc: '仅对话,无文件/系统工具(安全但功能受限)' },
3577
+ ];
3578
+
3579
+ function getToolsProfileTag(paths) {
3580
+ try {
3581
+ const config = readConfig(paths.openclawConfig);
3582
+ const profile = config?.tools?.profile || 'messaging';
3583
+ if (profile === 'full') return chalk.green(' [完整]');
3584
+ return chalk.yellow(' [仅聊天]');
3585
+ } catch {
3586
+ return '';
3587
+ }
3588
+ }
3589
+
3590
+ async function manageToolsProfile(paths) {
3591
+ console.log(chalk.cyan('🔐 权限管理\n'));
3592
+ console.log(chalk.gray('OpenClaw 3.2+ 新增 tools.profile 控制:'));
3593
+ console.log(chalk.gray(' full = 完整编码能力(读写文件、执行命令、系统操作)'));
3594
+ console.log(chalk.gray(' messaging = 仅聊天(无法操作文件和系统,适合纯对话场景)'));
3595
+ console.log(chalk.yellow('\n⚠️ 使用 Claude Code / Codex 编码,必须选择「完整模式」\n'));
3596
+
3597
+ const config = readConfig(paths.openclawConfig) || {};
3598
+ const current = config?.tools?.profile || 'messaging';
3599
+ const currentLabel = current === 'full' ? '完整模式' : current === 'messaging' ? '聊天模式' : current;
3600
+ console.log(chalk.gray(`当前: ${currentLabel}\n`));
3601
+
3602
+ const { selected } = await inquirer.prompt([{
3603
+ type: 'list',
3604
+ name: 'selected',
3605
+ message: '选择权限模式:',
3606
+ choices: TOOLS_PROFILES.map(p => ({
3607
+ name: p.id === current ? `${p.name} — ${p.desc} (当前)` : `${p.name} — ${p.desc}`,
3608
+ value: p.id,
3609
+ })),
3610
+ default: current,
3611
+ }]);
3612
+
3613
+ if (selected === current) {
3614
+ console.log(chalk.gray('\n权限模式未变更'));
3615
+ return;
3616
+ }
3617
+
3618
+ if (!config.tools) config.tools = {};
3619
+ config.tools.profile = selected;
3620
+ ensureGatewaySettings(config);
3621
+ writeConfigWithSync(paths, config);
3622
+
3623
+ const label = TOOLS_PROFILES.find(p => p.id === selected)?.name || selected;
3624
+ console.log(chalk.green(`\n✅ 已切换到: ${label}`));
3625
+
3626
+ const gwPort = config.gateway?.port || 18789;
3627
+ if (await isPortOpen(gwPort)) {
3628
+ const gwSpinner = ora({ text: '重启 Gateway 使权限变更生效...', spinner: 'dots' }).start();
3629
+ const ok = await restartGateway({ silent: true });
3630
+ if (ok) {
3631
+ gwSpinner.succeed('Gateway 已重启,权限变更已生效');
3632
+ } else {
3633
+ gwSpinner.fail('Gateway 重启失败,请手动重启: openclaw gateway restart');
3634
+ }
3635
+ } else {
3636
+ console.log(chalk.gray(' Gateway 未运行,变更将在下次启动时生效'));
3637
+ }
3638
+ }
3639
+
3493
3640
  // ============ 测试连接 ============
3494
3641
  async function testConnection(paths, args = {}) {
3495
3642
  console.log(chalk.cyan('🧪 测试 OpenClaw Gateway 连接\n'));
@@ -3511,9 +3658,10 @@ async function testConnection(paths, args = {}) {
3511
3658
  console.log(chalk.yellow('⚠️ 请先设置主模型'));
3512
3659
  return;
3513
3660
  }
3514
- // 优先选 claude provider
3515
- const claudeP = providerNames.find(n => n.includes('claude'));
3516
- const firstP = claudeP || providerNames[0];
3661
+ // 优先选云翼/maxapi provider
3662
+ const preferOrder = ['claude-yunyi', 'yunyi', 'heibai'];
3663
+ const preferred = preferOrder.find(n => providerNames.includes(n));
3664
+ const firstP = preferred || providerNames.find(n => n.includes('claude')) || providerNames[0];
3517
3665
  const firstModels = providers[firstP]?.models || [];
3518
3666
  if (firstModels.length > 0) {
3519
3667
  primary = `${firstP}/${firstModels[0].id}`;
@@ -3571,11 +3719,8 @@ async function testConnection(paths, args = {}) {
3571
3719
  cleanupAgentProcesses();
3572
3720
 
3573
3721
  // 步骤1: 先重启 Gateway 使配置生效
3574
- console.log(chalk.cyan('步骤 1/2: 重启 Gateway 使配置生效...'));
3575
- const restartOk = await restartGateway();
3576
-
3577
- // 等待 Gateway 启动
3578
- const gwSpinner = ora({ text: '等待 Gateway 启动...', spinner: 'dots' }).start();
3722
+ const gwSpinner = ora({ text: '步骤 1/2: 重启 Gateway 使配置生效...', spinner: 'dots' }).start();
3723
+ const restartOk = await restartGateway({ silent: true });
3579
3724
 
3580
3725
  let gatewayRunning = false;
3581
3726
 
@@ -3735,342 +3880,288 @@ async function testConnection(paths, args = {}) {
3735
3880
  }
3736
3881
 
3737
3882
  // ============ 重启 Gateway ============
3738
- async function restartGateway() {
3739
- console.log(chalk.cyan('\n正在重启 OpenClaw Gateway...'));
3883
+ async function restartGateway({ silent = false } = {}) {
3884
+ if (!silent) console.log(chalk.cyan('\n正在重启 OpenClaw Gateway...'));
3740
3885
 
3741
3886
  const gwEnv = detectGatewayEnv();
3887
+ const configPaths = getConfigPath();
3888
+ const gwConfig = readConfig(configPaths.openclawConfig);
3889
+ const gatewayPort = gwConfig?.gateway?.port || 18789;
3742
3890
 
3743
- // 如果 Gateway 在 Docker 容器里,通过 docker exec 重启
3891
+ // Docker 容器内重启
3744
3892
  if (gwEnv === 'docker') {
3745
- const container = await selectDockerContainer();
3746
- if (!container) {
3747
- console.log(chalk.red('❌ 未找到可用的 Docker 容器'));
3748
- return restartGatewayNative();
3749
- }
3750
- console.log(chalk.gray(` [检测] Gateway 运行在 Docker 容器: ${container.name} (${container.image})`));
3751
-
3752
- const dockerCmds = [];
3753
- if (container.cli === 'node') {
3754
- dockerCmds.push(`node ${container.cliPath} gateway restart`);
3755
- } else {
3756
- if (container.cliPath) dockerCmds.push(`${container.cliPath} gateway restart`);
3757
- dockerCmds.push(`${container.cli} gateway restart`);
3758
- }
3759
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3760
- dockerCmds.push(`${name} gateway restart`);
3761
- }
3762
-
3763
- // 每个命令尝试多种 shell(sh -c / bash -lc / bash -c)
3764
- const shellVariants = ['sh -c', 'bash -lc', 'bash -c'];
3765
-
3766
- return new Promise((resolve) => {
3767
- // 展开为 [cmd1+sh, cmd1+bash-lc, cmd1+bash-c, cmd2+sh, ...]
3768
- const allAttempts = [];
3769
- for (const cmd of [...new Set(dockerCmds)].filter(Boolean)) {
3770
- for (const shell of shellVariants) {
3771
- allAttempts.push(dockerCmd(`exec ${container.id} ${shell} "${cmd}"`));
3772
- }
3773
- }
3774
- let tried = 0;
3775
- const tryNext = () => {
3776
- if (tried >= allAttempts.length) {
3777
- console.log(chalk.yellow('Docker 容器内 Gateway 重启失败,尝试本地重启...'));
3778
- restartGatewayNative().then(resolve);
3779
- return;
3780
- }
3781
- const fullCmd = allAttempts[tried++];
3782
- exec(fullCmd, { timeout: 30000 }, (error) => {
3783
- if (error) {
3784
- tryNext();
3785
- } else {
3786
- console.log(chalk.green(`✅ Gateway 已重启 (Docker: ${container.name})`));
3787
- resolve(true);
3788
- }
3789
- });
3790
- };
3791
- tryNext();
3792
- });
3893
+ const result = await restartGatewayDocker(gatewayPort, silent);
3894
+ if (result) return true;
3895
+ if (!silent) console.log(chalk.yellow('Docker 容器内重启失败,尝试本地重启...'));
3793
3896
  }
3794
3897
 
3795
- // 如果 Gateway 在 WSL 里,优先用 wsl -- 重启
3898
+ // WSL 内重启
3796
3899
  if (gwEnv === 'wsl') {
3797
- console.log(chalk.gray(' [检测] Gateway 运行在 WSL 中'));
3798
- const wslCli = getWslCliBinary();
3799
- return new Promise((resolve) => {
3800
- const wslCmds = wslCli
3801
- ? [`wsl -- bash -lc "${wslCli} gateway restart"`]
3802
- : [
3803
- 'wsl -- bash -lc "openclaw gateway restart"',
3804
- 'wsl -- bash -lc "clawdbot gateway restart"',
3805
- 'wsl -- bash -lc "moltbot gateway restart"',
3806
- ];
3807
- let tried = 0;
3808
- const tryNext = () => {
3809
- if (tried >= wslCmds.length) {
3810
- console.log(chalk.yellow('WSL 内 Gateway 重启失败,尝试 Windows 原生重启...'));
3811
- restartGatewayNative().then(resolve);
3812
- return;
3813
- }
3814
- const cmd = wslCmds[tried++];
3815
- exec(cmd, { timeout: 30000 }, (error, stdout, stderr) => {
3816
- const errStr = (stderr || '').toLowerCase();
3817
- const wslGarbled = errStr.includes('wsl:') && errStr.includes('localhost') && errStr.includes('nat');
3818
-
3819
- if (error || errStr.includes('/usr/bin/env:') || wslGarbled) {
3820
- tryNext();
3821
- } else {
3822
- console.log(chalk.green('Gateway 已重启 (WSL)'));
3823
- resolve(true);
3824
- }
3825
- });
3826
- };
3827
- tryNext();
3828
- });
3900
+ const result = await restartGatewayWsl(gatewayPort, silent);
3901
+ if (result) return true;
3902
+ if (!silent) console.log(chalk.yellow('WSL 内重启失败,尝试 Windows 原生重启...'));
3829
3903
  }
3830
3904
 
3831
- return restartGatewayNative();
3905
+ return restartGatewayNative(silent);
3832
3906
  }
3833
3907
 
3834
- // 强制重启 Gateway:杀掉旧进程,再用已找到的 CLI 路径启动新 Gateway
3835
- async function forceRestartGateway(resolved, nodeInfo, useNode, env, gatewayPort = 18789) {
3836
- console.log(chalk.yellow('\n🔄 尝试强制重启 Gateway(杀旧进程 → 启动新进程)...'));
3837
-
3838
- // Docker 容器内强制重启
3839
- if (detectGatewayEnv() === 'docker' && _selectedDockerContainer) {
3840
- const cid = _selectedDockerContainer.id;
3841
- const cName = _selectedDockerContainer.name;
3842
- console.log(chalk.gray(` [Docker] 在容器 ${cName} 内强制重启...`));
3908
+ function buildDockerInnerCmds(container, verb) {
3909
+ const cmds = [];
3910
+ if (container.cli === 'node') {
3911
+ cmds.push(`node ${container.cliPath} ${verb}`);
3912
+ } else {
3913
+ if (container.cliPath) cmds.push(`${container.cliPath} ${verb}`);
3914
+ cmds.push(`${container.cli} ${verb}`);
3915
+ }
3916
+ for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3917
+ cmds.push(`${name} ${verb}`);
3918
+ }
3919
+ return [...new Set(cmds)].filter(Boolean);
3920
+ }
3843
3921
 
3844
- // 1. 杀掉容器内旧 Gateway 进程
3845
- try {
3846
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3847
- safeExec(dockerCmd(`exec ${cid} sh -c "pkill -f '${name}.*gateway' 2>/dev/null || true"`), { timeout: 5000 });
3848
- }
3849
- safeExec(dockerCmd(`exec ${cid} sh -c "lsof -ti :${gatewayPort} 2>/dev/null | xargs -r kill -9 2>/dev/null || true"`), { timeout: 5000 });
3850
- console.log(chalk.gray(' 已尝试清理容器内旧 Gateway 进程'));
3851
- } catch { /* ignore */ }
3922
+ async function restartGatewayDocker(gatewayPort, silent = false) {
3923
+ const container = await selectDockerContainer();
3924
+ if (!container) return false;
3925
+ if (!silent) console.log(chalk.gray(` [检测] Gateway 运行在 Docker 容器: ${container.name} (${container.image})`));
3852
3926
 
3853
- await new Promise(resolve => setTimeout(resolve, 2000));
3927
+ const cid = container.id;
3928
+ const shellVariants = ['sh -c', 'bash -lc', 'bash -c'];
3854
3929
 
3855
- // 2. 启动新 Gateway
3856
- const dockerCmds = [];
3857
- if (_selectedDockerContainer.cli === 'node') {
3858
- dockerCmds.push(`node ${_selectedDockerContainer.cliPath} gateway`);
3859
- } else {
3860
- if (_selectedDockerContainer.cliPath) dockerCmds.push(`${_selectedDockerContainer.cliPath} gateway`);
3861
- dockerCmds.push(`${_selectedDockerContainer.cli} gateway`);
3930
+ // 策略 A:exec restart 命令(短超时),然后端口探测
3931
+ for (const cmd of buildDockerInnerCmds(container, 'gateway restart')) {
3932
+ for (const shell of shellVariants) {
3933
+ safeExec(dockerCmd(`exec ${cid} ${shell} "${cmd}"`), { timeout: 8000 });
3934
+ if (await waitForGateway(gatewayPort, '127.0.0.1', 5000)) {
3935
+ if (!silent) console.log(chalk.green(`✅ Gateway 已重启 (Docker: ${container.name})`));
3936
+ return true;
3937
+ }
3862
3938
  }
3939
+ }
3940
+
3941
+ // 策略 B:杀容器内旧进程 → spawn 启动新 Gateway → 端口探测
3942
+ if (!silent) console.log(chalk.gray(' Docker 内常规重启未生效,尝试杀进程后重新启动...'));
3943
+ try {
3863
3944
  for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3864
- dockerCmds.push(`${name} gateway`);
3945
+ safeExec(dockerCmd(`exec ${cid} sh -c "pkill -f '${name}.*gateway' 2>/dev/null || true"`), { timeout: 5000 });
3865
3946
  }
3947
+ safeExec(dockerCmd(`exec ${cid} sh -c "lsof -ti :${gatewayPort} 2>/dev/null | xargs -r kill -9 2>/dev/null || true"`), { timeout: 5000 });
3948
+ } catch { /* ignore */ }
3949
+ await new Promise(resolve => setTimeout(resolve, 2000));
3866
3950
 
3867
- for (const cmd of [...new Set(dockerCmds)].filter(Boolean)) {
3868
- console.log(chalk.gray(` 尝试在容器内启动: ${cmd}`));
3869
- for (const shell of ['sh -c', 'bash -lc', 'bash -c']) {
3870
- if (spawnDetachedInDocker(cid, cmd, shell)) {
3871
- if (await waitForGateway(gatewayPort, '127.0.0.1', 12000)) {
3872
- console.log(chalk.green(`✅ Gateway 已在 Docker 容器 ${cName} 内强制重启成功`));
3873
- return true;
3874
- }
3951
+ for (const cmd of buildDockerInnerCmds(container, 'gateway')) {
3952
+ for (const shell of shellVariants) {
3953
+ if (spawnDetachedInDocker(cid, cmd, shell)) {
3954
+ if (await waitForGateway(gatewayPort, '127.0.0.1', 12000)) {
3955
+ if (!silent) console.log(chalk.green(`✅ Gateway 已重启 (Docker: ${container.name})`));
3956
+ return true;
3875
3957
  }
3876
3958
  }
3877
3959
  }
3960
+ }
3878
3961
 
3879
- console.log(chalk.red('❌ Docker 容器内强制重启也失败了'));
3880
- console.log(chalk.gray(` 请手动进入容器重启: docker exec -it ${cid} bash`));
3881
- console.log(chalk.gray(` 然后执行: openclaw gateway / clawdbot gateway / moltbot gateway`));
3882
- return false;
3962
+ return false;
3963
+ }
3964
+
3965
+ async function restartGatewayWsl(gatewayPort, silent = false) {
3966
+ if (!silent) console.log(chalk.gray(' [检测] Gateway 运行在 WSL 中'));
3967
+ const wslCli = getWslCliBinary();
3968
+ const names = ['openclaw', 'clawdbot', 'moltbot'];
3969
+
3970
+ // 构建 WSL 重启命令
3971
+ const wslRestartCmds = [];
3972
+ if (wslCli) wslRestartCmds.push(`wsl -- bash -lc "${wslCli} gateway restart"`);
3973
+ for (const name of names) wslRestartCmds.push(`wsl -- bash -lc "${name} gateway restart"`);
3974
+
3975
+ // 策略 A:exec restart(短超时)+ 端口探测
3976
+ for (const cmd of wslRestartCmds) {
3977
+ safeExec(cmd, { timeout: 8000 });
3978
+ if (await waitForGateway(gatewayPort, '127.0.0.1', 5000)) {
3979
+ if (!silent) console.log(chalk.green('✅ Gateway 已重启 (WSL)'));
3980
+ return true;
3981
+ }
3883
3982
  }
3884
3983
 
3885
- // 1. 杀掉旧 Gateway 进程
3984
+ // 策略 B:杀 WSL 内旧进程 → spawn 启动 → 端口探测
3985
+ if (!silent) console.log(chalk.gray(' WSL 内常规重启未生效,尝试杀进程后重新启动...'));
3886
3986
  try {
3887
- if (process.platform === 'win32') {
3888
- // Windows: 通过 taskkill 杀掉占用端口的 node 进程
3889
- const findPid = safeExec(`netstat -ano | findstr ":${gatewayPort}"`, { timeout: 5000 });
3890
- if (findPid.ok && findPid.output) {
3891
- const lines = findPid.output.split('\n').filter(l => l.includes('LISTENING'));
3892
- for (const line of lines) {
3893
- const pid = line.trim().split(/\s+/).pop();
3894
- if (pid && /^\d+$/.test(pid)) {
3895
- safeExec(`taskkill /F /PID ${pid}`, { timeout: 5000 });
3896
- }
3897
- }
3898
- }
3899
- // Windows + WSL: 也清理 WSL 内的 gateway 进程
3900
- if (isWslAvailable()) {
3901
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3902
- safeExec(`wsl -- bash -c "pkill -f '${name}.*gateway' 2>/dev/null || true"`, { timeout: 5000 });
3903
- }
3904
- safeExec(`wsl -- bash -c "lsof -ti :${gatewayPort} 2>/dev/null | xargs -r kill -9 2>/dev/null || true"`, { timeout: 5000 });
3905
- }
3906
- } else {
3907
- // Linux/macOS: pkill gateway 相关进程
3908
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3909
- safeExec(`pkill -f '${name}.*gateway' 2>/dev/null || true`);
3910
- }
3911
- // 备用:通过端口找进程并杀掉
3912
- const lsof = safeExec(`lsof -ti :${gatewayPort} 2>/dev/null`);
3913
- if (lsof.ok && lsof.output) {
3914
- for (const pid of lsof.output.trim().split('\n').filter(Boolean)) {
3915
- if (/^\d+$/.test(pid.trim())) {
3916
- safeExec(`kill -9 ${pid.trim()} 2>/dev/null || true`);
3917
- }
3918
- }
3919
- }
3987
+ for (const name of names) {
3988
+ safeExec(`wsl -- bash -c "pkill -f '${name}.*gateway' 2>/dev/null || true"`, { timeout: 5000 });
3920
3989
  }
3921
- console.log(chalk.gray(' 已尝试清理旧 Gateway 进程'));
3990
+ safeExec(`wsl -- bash -c "lsof -ti :${gatewayPort} 2>/dev/null | xargs -r kill -9 2>/dev/null || true"`, { timeout: 5000 });
3922
3991
  } catch { /* ignore */ }
3923
-
3924
- // 2. 等待端口释放
3925
3992
  await new Promise(resolve => setTimeout(resolve, 2000));
3926
3993
 
3927
- // 3. 用已找到的 CLI 路径启动新 Gateway
3928
- const startCmds = [];
3994
+ const wslStartCmds = [];
3995
+ if (wslCli) wslStartCmds.push(`wsl -- bash -lc "${wslCli} gateway"`);
3996
+ for (const name of names) wslStartCmds.push(`wsl -- bash -lc "${name} gateway"`);
3929
3997
 
3930
- // 优先用已解析的完整路径
3931
- if (resolved) {
3932
- if (useNode && nodeInfo) {
3933
- startCmds.push(`"${nodeInfo.path}" "${resolved}" gateway`);
3934
- } else {
3935
- startCmds.push(`"${resolved}" gateway`);
3998
+ for (const cmd of wslStartCmds) {
3999
+ if (spawnDetached(cmd, process.env)) {
4000
+ if (await waitForGateway(gatewayPort, '127.0.0.1', 12000)) {
4001
+ if (!silent) console.log(chalk.green('✅ Gateway 已重启 (WSL)'));
4002
+ return true;
4003
+ }
3936
4004
  }
3937
4005
  }
3938
4006
 
3939
- // Fallback: login shell 方式(加载 nvm 等 PATH)
3940
- if (process.platform !== 'win32') {
3941
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3942
- startCmds.push(`bash -lc '${name} gateway'`);
3943
- }
3944
- } else {
3945
- // Windows: 先尝试原生命令
3946
- startCmds.push('openclaw gateway', 'clawdbot gateway', 'moltbot gateway');
3947
- // Windows + WSL: 也尝试通过 WSL 启动 gateway
3948
- if (isWslAvailable()) {
3949
- const wslCli = getWslCliBinary();
3950
- if (wslCli) {
3951
- startCmds.push(`wsl -- bash -lc "${wslCli} gateway"`);
3952
- }
3953
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
3954
- startCmds.push(`wsl -- bash -lc "${name} gateway"`);
3955
- }
4007
+ return false;
4008
+ }
4009
+
4010
+ async function restartGatewayNative(silent = false) {
4011
+ const { cliBinary: resolved, nodeMajor } = getCliMeta();
4012
+ const nodeInfo = findCompatibleNode(nodeMajor);
4013
+ const env = { ...process.env, PATH: extendPathEnv(nodeInfo ? nodeInfo.path : null) };
4014
+ const useNode = resolved && nodeInfo && isNodeShebang(resolved);
4015
+
4016
+ const configPaths = getConfigPath();
4017
+ const gwConfig = readConfig(configPaths.openclawConfig);
4018
+ const gatewayPort = gwConfig?.gateway?.port || 18789;
4019
+
4020
+ // 策略 A:先尝试 exec "gateway restart"(给 8s 超时——足够杀旧进程)
4021
+ // 注意:该命令通常会启动前台 daemon 不退出,超时是正常的
4022
+ const restartCmds = buildGatewayCommands(resolved, nodeInfo, useNode, 'restart');
4023
+
4024
+ let execAttempted = false;
4025
+ for (const cmd of restartCmds) {
4026
+ execAttempted = true;
4027
+ const result = safeExec(cmd, { timeout: 8000, env });
4028
+ // 不管是否 "成功"(通常会超时),检查端口是否可达
4029
+ if (await waitForGateway(gatewayPort, '127.0.0.1', 5000)) {
4030
+ if (!silent) console.log(chalk.green(`✅ Gateway 已重启`));
4031
+ return true;
3956
4032
  }
3957
4033
  }
3958
4034
 
3959
- for (const cmd of [...new Set(startCmds)].filter(Boolean)) {
3960
- console.log(chalk.gray(` 尝试启动: ${cmd}`));
4035
+ // 策略 B:杀旧进程 spawn 后台启动新 Gateway → 端口探测
4036
+ if (!silent) console.log(chalk.gray(' 常规重启未生效,尝试杀进程后重新启动...'));
4037
+ await killGatewayProcesses(gatewayPort);
4038
+ await new Promise(resolve => setTimeout(resolve, 1500));
4039
+
4040
+ const startCmds = buildGatewayCommands(resolved, nodeInfo, useNode, 'start');
4041
+ for (const cmd of startCmds) {
3961
4042
  if (spawnDetached(cmd, env)) {
3962
- // 等待新 Gateway 启动
3963
4043
  if (await waitForGateway(gatewayPort, '127.0.0.1', 12000)) {
3964
- console.log(chalk.green('✅ Gateway 已强制重启成功'));
3965
- console.log(chalk.gray(' 现在可以在 Web/Telegram/Discord 等渠道测试对话了'));
4044
+ if (!silent) console.log(chalk.green('✅ Gateway 已重启'));
3966
4045
  return true;
3967
4046
  }
3968
4047
  }
3969
4048
  }
3970
4049
 
3971
- console.log(chalk.red('❌ 强制重启也失败了'));
3972
- console.log(chalk.gray(' 请手动重启 Gateway:'));
3973
- console.log(chalk.gray(' 1. 关闭旧进程: pkill -f "gateway" 或 taskkill'));
3974
- console.log(chalk.gray(' 2. 启动新进程: openclaw gateway / clawdbot gateway / moltbot gateway'));
4050
+ // 策略 C:用 login shell 启动(加载 nvm/fnm 等 PATH)
4051
+ if (process.platform !== 'win32') {
4052
+ if (!silent) console.log(chalk.gray(' 尝试通过 login shell 启动...'));
4053
+ for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
4054
+ for (const sh of ['/bin/zsh', '/bin/bash']) {
4055
+ if (!fs.existsSync(sh)) continue;
4056
+ if (spawnDetached(`${sh} -lc '${name} gateway'`, env)) {
4057
+ if (await waitForGateway(gatewayPort, '127.0.0.1', 12000)) {
4058
+ if (!silent) console.log(chalk.green('✅ Gateway 已重启 (login shell)'));
4059
+ return true;
4060
+ }
4061
+ }
4062
+ }
4063
+ }
4064
+ }
4065
+
4066
+ // 全部失败,输出诊断
4067
+ if (!silent) {
4068
+ console.log(chalk.red(`❌ 重启失败: 找不到 openclaw/clawdbot/moltbot 命令`));
4069
+ console.log(chalk.gray(` 请手动运行: openclaw gateway restart`));
4070
+ console.log(chalk.gray(` 或: clawdbot gateway restart`));
4071
+ console.log(chalk.gray(` 或: moltbot gateway restart`));
4072
+ printGatewayDiagnostics(resolved);
4073
+ }
3975
4074
  return false;
3976
4075
  }
3977
4076
 
3978
- async function restartGatewayNative() {
3979
- const { cliBinary: resolved, nodeMajor } = getCliMeta();
3980
- const nodeInfo = findCompatibleNode(nodeMajor);
3981
- const env = { ...process.env, PATH: extendPathEnv(nodeInfo ? nodeInfo.path : null) };
3982
- const useNode = resolved && nodeInfo && isNodeShebang(resolved);
4077
+ function buildGatewayCommands(resolved, nodeInfo, useNode, action) {
4078
+ const verb = action === 'start' ? 'gateway' : 'gateway restart';
4079
+ const commands = [];
3983
4080
 
3984
- // 尝试多种命令
3985
- const commands = resolved
3986
- ? [
3987
- useNode ? `"${nodeInfo.path}" "${resolved}" gateway restart` : `"${resolved}" gateway restart`
3988
- ]
3989
- : [
3990
- 'openclaw gateway restart',
3991
- 'clawdbot gateway restart',
3992
- 'moltbot gateway restart',
3993
- 'npx openclaw gateway restart',
3994
- 'npx clawdbot gateway restart',
3995
- 'npx moltbot gateway restart'
3996
- ];
3997
-
3998
- // Windows + WSL: 追加 WSL 命令作为额外回退
3999
- if (process.platform === 'win32' && isWslAvailable()) {
4000
- const wslCli = getWslCliBinary();
4001
- if (wslCli) {
4002
- commands.push(`wsl -- bash -lc "${wslCli} gateway restart"`);
4081
+ if (resolved) {
4082
+ if (useNode && nodeInfo) {
4083
+ commands.push(`"${nodeInfo.path}" "${resolved}" ${verb}`);
4084
+ } else {
4085
+ commands.push(`"${resolved}" ${verb}`);
4003
4086
  }
4004
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
4005
- commands.push(`wsl -- bash -lc "${name} gateway restart"`);
4087
+ }
4088
+
4089
+ const names = ['openclaw', 'clawdbot', 'moltbot'];
4090
+ if (process.platform === 'win32') {
4091
+ for (const name of names) commands.push(`${name} ${verb}`);
4092
+ if (isWslAvailable()) {
4093
+ const wslCli = getWslCliBinary();
4094
+ if (wslCli) commands.push(`wsl -- bash -lc "${wslCli} ${verb}"`);
4095
+ for (const name of names) commands.push(`wsl -- bash -lc "${name} ${verb}"`);
4006
4096
  }
4097
+ } else {
4098
+ for (const name of names) commands.push(`${name} ${verb}`);
4007
4099
  }
4008
4100
 
4009
- // Docker: 追加 Docker 容器命令作为额外回退
4010
4101
  if (isDockerAvailable()) {
4011
- const dockerContainers = findOpenclawDockerContainers();
4012
- for (const c of dockerContainers) {
4102
+ const containers = findOpenclawDockerContainers();
4103
+ for (const c of containers) {
4013
4104
  if (c.cli === 'node') {
4014
- commands.push(dockerCmd(`exec ${c.id} bash -lc "node ${c.cliPath} gateway restart"`));
4105
+ commands.push(dockerCmd(`exec ${c.id} bash -lc "node ${c.cliPath} ${verb}"`));
4015
4106
  } else if (c.cli !== 'unknown') {
4016
- commands.push(dockerCmd(`exec ${c.id} bash -lc "${c.cli} gateway restart"`));
4107
+ commands.push(dockerCmd(`exec ${c.id} bash -lc "${c.cli} ${verb}"`));
4017
4108
  }
4018
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
4019
- commands.push(dockerCmd(`exec ${c.id} bash -lc "${name} gateway restart"`));
4109
+ for (const name of names) {
4110
+ commands.push(dockerCmd(`exec ${c.id} bash -lc "${name} ${verb}"`));
4020
4111
  }
4021
4112
  }
4022
4113
  }
4023
4114
 
4024
- return new Promise((resolve) => {
4025
- let tried = 0;
4026
-
4027
- const tryNext = () => {
4028
- if (tried >= commands.length) {
4029
- console.log(chalk.red(`❌ 重启失败: 找不到 openclaw/clawdbot/moltbot 命令`));
4030
- console.log(chalk.gray(` 请手动运行: openclaw gateway restart`));
4031
- console.log(chalk.gray(` 或: clawdbot gateway restart`));
4032
- console.log(chalk.gray(` 或: moltbot gateway restart`));
4033
- // 诊断信息
4034
- console.log(chalk.gray(`\n [诊断] resolveCliBinary = ${resolved || 'null'}`));
4035
- const npmPrefix = safeExec('npm prefix -g');
4036
- if (npmPrefix.ok) console.log(chalk.gray(` [诊断] npm prefix -g = ${npmPrefix.output}`));
4037
- for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
4038
- 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`);
4039
- if (which.ok && which.output) console.log(chalk.gray(` [诊断] ${name} -> ${which.output.split('\n')[0].trim()}`));
4115
+ return [...new Set(commands)].filter(Boolean);
4116
+ }
4117
+
4118
+ async function killGatewayProcesses(gatewayPort = 18789) {
4119
+ try {
4120
+ if (process.platform === 'win32') {
4121
+ const findPid = safeExec(`netstat -ano | findstr ":${gatewayPort}"`, { timeout: 5000 });
4122
+ if (findPid.ok && findPid.output) {
4123
+ for (const line of findPid.output.split('\n').filter(l => l.includes('LISTENING'))) {
4124
+ const pid = line.trim().split(/\s+/).pop();
4125
+ if (pid && /^\d+$/.test(pid)) safeExec(`taskkill /F /PID ${pid}`, { timeout: 5000 });
4040
4126
  }
4041
- // Docker 诊断
4042
- if (isDockerAvailable()) {
4043
- const dContainers = findOpenclawDockerContainers();
4044
- if (dContainers.length > 0) {
4045
- console.log(chalk.gray(` [诊断] Docker 容器 (含 openclaw/clawdbot/moltbot):`));
4046
- for (const c of dContainers) {
4047
- console.log(chalk.gray(` - ${c.name} (${c.image}) [${c.cli}: ${c.cliPath}]`));
4048
- }
4049
- } else {
4050
- console.log(chalk.gray(` [诊断] Docker 可用,但未找到含 openclaw/clawdbot/moltbot 的容器`));
4051
- }
4127
+ }
4128
+ if (isWslAvailable()) {
4129
+ for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
4130
+ safeExec(`wsl -- bash -c "pkill -f '${name}.*gateway' 2>/dev/null || true"`, { timeout: 5000 });
4052
4131
  }
4053
- // 尝试强制重启
4054
- forceRestartGateway(resolved, nodeInfo, useNode, env).then(resolve);
4055
- return;
4132
+ safeExec(`wsl -- bash -c "lsof -ti :${gatewayPort} 2>/dev/null | xargs -r kill -9 2>/dev/null || true"`, { timeout: 5000 });
4056
4133
  }
4057
-
4058
- const cmd = commands[tried];
4059
- tried++;
4060
-
4061
- exec(cmd, { timeout: 30000, env }, (error) => {
4062
- if (error) {
4063
- tryNext();
4064
- } else {
4065
- console.log(chalk.green(`✅ Gateway 已重启`));
4066
- console.log(chalk.gray(` 现在可以在 Web/Telegram/Discord 等渠道测试对话了`));
4067
- resolve(true);
4134
+ } else {
4135
+ for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
4136
+ safeExec(`pkill -f '${name}.*gateway' 2>/dev/null || true`);
4137
+ }
4138
+ const lsof = safeExec(`lsof -ti :${gatewayPort} 2>/dev/null`);
4139
+ if (lsof.ok && lsof.output) {
4140
+ for (const pid of lsof.output.trim().split('\n').filter(Boolean)) {
4141
+ if (/^\d+$/.test(pid.trim())) safeExec(`kill -9 ${pid.trim()} 2>/dev/null || true`);
4068
4142
  }
4069
- });
4070
- };
4143
+ }
4144
+ }
4145
+ } catch { /* ignore */ }
4146
+ }
4071
4147
 
4072
- tryNext();
4073
- });
4148
+ function printGatewayDiagnostics(resolved) {
4149
+ console.log(chalk.gray(`\n [诊断] resolveCliBinary = ${resolved || 'null'}`));
4150
+ const npmPrefix = safeExec('npm prefix -g');
4151
+ if (npmPrefix.ok) console.log(chalk.gray(` [诊断] npm prefix -g = ${npmPrefix.output}`));
4152
+ for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
4153
+ 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`);
4154
+ if (which.ok && which.output) console.log(chalk.gray(` [诊断] ${name} -> ${which.output.split('\n')[0].trim()}`));
4155
+ }
4156
+ if (isDockerAvailable()) {
4157
+ const dContainers = findOpenclawDockerContainers();
4158
+ if (dContainers.length > 0) {
4159
+ console.log(chalk.gray(` [诊断] Docker 容器 (含 openclaw/clawdbot/moltbot):`));
4160
+ for (const c of dContainers) {
4161
+ console.log(chalk.gray(` - ${c.name} (${c.image}) [${c.cli}: ${c.cliPath}]`));
4162
+ }
4163
+ }
4164
+ }
4074
4165
  }
4075
4166
 
4076
4167
  // 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.47",
3
+ "version": "1.0.49",
4
4
  "description": "跨平台 OpenClaw/Clawdbot 配置管理工具 - 管理中转地址、模型切换、API Keys、测速优化",
5
5
  "main": "bin/yymaxapi.js",
6
6
  "bin": {