yymaxapi 1.0.43 → 1.0.45

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.
Files changed (2) hide show
  1. package/bin/yymaxapi.js +196 -6
  2. package/package.json +1 -1
package/bin/yymaxapi.js CHANGED
@@ -723,6 +723,31 @@ function writeCodexConfig(baseUrl, apiKey) {
723
723
  } catch { /* 非关键,静默失败 */ }
724
724
  }
725
725
 
726
+ function writeOpencodeConfig(baseUrl, apiKey, modelId) {
727
+ const home = os.homedir();
728
+ const configDir = process.platform === 'win32'
729
+ ? path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'opencode')
730
+ : path.join(home, '.config', 'opencode');
731
+ const configPath = path.join(configDir, 'config.json');
732
+ try {
733
+ if (!fs.existsSync(configDir)) fs.mkdirSync(configDir, { recursive: true });
734
+ const cleanUrl = baseUrl.replace(/\/+$/, '');
735
+ const config = {
736
+ provider: {
737
+ anthropic: {
738
+ apiKey: apiKey,
739
+ baseURL: cleanUrl
740
+ }
741
+ },
742
+ model: modelId || 'claude-sonnet-4-6'
743
+ };
744
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
745
+ return configPath;
746
+ } catch {
747
+ return null;
748
+ }
749
+ }
750
+
726
751
  function syncExternalTools(type, baseUrl, apiKey) {
727
752
  const synced = [];
728
753
  try {
@@ -2699,7 +2724,7 @@ async function autoActivate(paths, args = {}) {
2699
2724
  config.agents.defaults.models[codexModelKey] = { alias: codexProviderName };
2700
2725
 
2701
2726
  // ---- 选择主力 ----
2702
- let primaryType = 'codex';
2727
+ let primaryType = 'claude';
2703
2728
  if (args.primary === 'claude') {
2704
2729
  primaryType = 'claude';
2705
2730
  } else if (args.primary === 'codex') {
@@ -2710,8 +2735,8 @@ async function autoActivate(paths, args = {}) {
2710
2735
  name: 'picked',
2711
2736
  message: '选择主力工具(默认启动哪个):',
2712
2737
  choices: [
2713
- { name: 'Codex (GPT)', value: 'codex' },
2714
- { name: 'Claude Code', value: 'claude' }
2738
+ { name: 'Claude Code', value: 'claude' },
2739
+ { name: 'Codex (GPT)', value: 'codex' }
2715
2740
  ]
2716
2741
  }]);
2717
2742
  primaryType = picked;
@@ -2767,6 +2792,163 @@ async function autoActivate(paths, args = {}) {
2767
2792
  }
2768
2793
  }
2769
2794
 
2795
+ // ============ 单独配置 Claude Code CLI ============
2796
+ async function activateClaudeCode(paths, args = {}) {
2797
+ console.log(chalk.cyan.bold('\n🔧 配置 Claude Code CLI\n'));
2798
+
2799
+ const claudeApiConfig = API_CONFIG.claude;
2800
+
2801
+ // ---- 测速选节点 ----
2802
+ const shouldTest = !(args['no-test'] || args.noTest);
2803
+ let selectedEndpoint = ENDPOINTS[0];
2804
+
2805
+ if (shouldTest) {
2806
+ console.log(chalk.cyan('📡 开始测速节点...\n'));
2807
+ const speedResult = await testAllEndpoints(ENDPOINTS, { autoFallback: true });
2808
+ const sorted = speedResult.ranked || [];
2809
+ if (sorted.length > 0) {
2810
+ const defaultEp = ENDPOINTS[0];
2811
+ const { selectedIndex } = await inquirer.prompt([{
2812
+ type: 'list',
2813
+ name: 'selectedIndex',
2814
+ message: '选择节点:',
2815
+ choices: [
2816
+ { name: `* 使用默认节点 (${defaultEp.name})`, value: -1 },
2817
+ new inquirer.Separator(' ---- 或按测速结果选择 ----'),
2818
+ ...sorted.map((e, i) => ({
2819
+ name: `${e.name} - ${e.latency}ms (评分:${e.score})`,
2820
+ value: i
2821
+ }))
2822
+ ]
2823
+ }]);
2824
+ selectedEndpoint = selectedIndex === -1 ? defaultEp : sorted[selectedIndex];
2825
+ } else {
2826
+ console.log(chalk.red('\n⚠️ 所有节点均不可达'));
2827
+ const { proceed } = await inquirer.prompt([{
2828
+ type: 'confirm', name: 'proceed',
2829
+ message: '仍要使用默认节点配置吗?', default: false
2830
+ }]);
2831
+ if (!proceed) { console.log(chalk.gray('已取消')); return; }
2832
+ }
2833
+ }
2834
+
2835
+ // ---- API Key ----
2836
+ const directKey = (args['api-key'] || args.apiKey || args.key || '').toString().trim();
2837
+ let apiKey;
2838
+ if (directKey) {
2839
+ apiKey = directKey;
2840
+ } else {
2841
+ const envKey = process.env.ANTHROPIC_AUTH_TOKEN || process.env.CLAUDE_API_KEY || '';
2842
+ apiKey = await promptApiKey('请输入 API Key:', envKey);
2843
+ }
2844
+ if (!apiKey) { console.log(chalk.gray('已取消')); return; }
2845
+
2846
+ // ---- 验证 ----
2847
+ console.log('');
2848
+ const validation = await validateApiKey(selectedEndpoint.url, apiKey);
2849
+ if (!validation.valid) {
2850
+ const { continueAnyway } = await inquirer.prompt([{
2851
+ type: 'confirm', name: 'continueAnyway',
2852
+ message: 'API Key 验证失败,是否仍然继续写入配置?', default: false
2853
+ }]);
2854
+ if (!continueAnyway) { console.log(chalk.gray('已取消')); return; }
2855
+ }
2856
+
2857
+ // ---- 写入配置 ----
2858
+ const claudeBaseUrl = buildFullUrl(selectedEndpoint.url, 'claude');
2859
+ const writeSpinner = ora({ text: '正在写入 Claude Code 配置...', spinner: 'dots' }).start();
2860
+ writeClaudeCodeSettings(claudeBaseUrl, apiKey);
2861
+ writeSpinner.succeed('Claude Code 配置写入完成');
2862
+
2863
+ console.log(chalk.green('\n✅ Claude Code CLI 配置完成!'));
2864
+ console.log(chalk.cyan(` Base URL: ${claudeBaseUrl}`));
2865
+ console.log(chalk.gray(` 模型: claude-sonnet-4-6`));
2866
+ console.log(chalk.gray(' API Key: 已设置'));
2867
+ console.log(chalk.gray('\n 已写入:'));
2868
+ console.log(chalk.gray(' • ~/.claude/settings.json'));
2869
+ console.log(chalk.gray(' • ~/.claude.json (跳过 onboarding)'));
2870
+ console.log(chalk.gray(' • shell 环境变量 (ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN)'));
2871
+ console.log(chalk.yellow('\n 提示: 请重新打开终端或执行 source ~/.zshrc 使环境变量生效'));
2872
+ }
2873
+
2874
+ // ============ 单独配置 Opencode ============
2875
+ async function activateOpencode(paths, args = {}) {
2876
+ console.log(chalk.cyan.bold('\n🔧 配置 Opencode\n'));
2877
+
2878
+ const claudeApiConfig = API_CONFIG.claude;
2879
+
2880
+ // ---- 测速选节点 ----
2881
+ const shouldTest = !(args['no-test'] || args.noTest);
2882
+ let selectedEndpoint = ENDPOINTS[0];
2883
+
2884
+ if (shouldTest) {
2885
+ console.log(chalk.cyan('📡 开始测速节点...\n'));
2886
+ const speedResult = await testAllEndpoints(ENDPOINTS, { autoFallback: true });
2887
+ const sorted = speedResult.ranked || [];
2888
+ if (sorted.length > 0) {
2889
+ const defaultEp = ENDPOINTS[0];
2890
+ const { selectedIndex } = await inquirer.prompt([{
2891
+ type: 'list',
2892
+ name: 'selectedIndex',
2893
+ message: '选择节点:',
2894
+ choices: [
2895
+ { name: `* 使用默认节点 (${defaultEp.name})`, value: -1 },
2896
+ new inquirer.Separator(' ---- 或按测速结果选择 ----'),
2897
+ ...sorted.map((e, i) => ({
2898
+ name: `${e.name} - ${e.latency}ms (评分:${e.score})`,
2899
+ value: i
2900
+ }))
2901
+ ]
2902
+ }]);
2903
+ selectedEndpoint = selectedIndex === -1 ? defaultEp : sorted[selectedIndex];
2904
+ } else {
2905
+ console.log(chalk.red('\n⚠️ 所有节点均不可达'));
2906
+ const { proceed } = await inquirer.prompt([{
2907
+ type: 'confirm', name: 'proceed',
2908
+ message: '仍要使用默认节点配置吗?', default: false
2909
+ }]);
2910
+ if (!proceed) { console.log(chalk.gray('已取消')); return; }
2911
+ }
2912
+ }
2913
+
2914
+ // ---- API Key ----
2915
+ const directKey = (args['api-key'] || args.apiKey || args.key || '').toString().trim();
2916
+ let apiKey;
2917
+ if (directKey) {
2918
+ apiKey = directKey;
2919
+ } else {
2920
+ const envKey = process.env.ANTHROPIC_AUTH_TOKEN || process.env.CLAUDE_API_KEY || '';
2921
+ apiKey = await promptApiKey('请输入 API Key:', envKey);
2922
+ }
2923
+ if (!apiKey) { console.log(chalk.gray('已取消')); return; }
2924
+
2925
+ // ---- 验证 ----
2926
+ console.log('');
2927
+ const validation = await validateApiKey(selectedEndpoint.url, apiKey);
2928
+ if (!validation.valid) {
2929
+ const { continueAnyway } = await inquirer.prompt([{
2930
+ type: 'confirm', name: 'continueAnyway',
2931
+ message: 'API Key 验证失败,是否仍然继续写入配置?', default: false
2932
+ }]);
2933
+ if (!continueAnyway) { console.log(chalk.gray('已取消')); return; }
2934
+ }
2935
+
2936
+ // ---- 写入配置 ----
2937
+ const claudeBaseUrl = buildFullUrl(selectedEndpoint.url, 'claude');
2938
+ const modelId = 'claude-sonnet-4-6';
2939
+ const writeSpinner = ora({ text: '正在写入 Opencode 配置...', spinner: 'dots' }).start();
2940
+ const configPath = writeOpencodeConfig(claudeBaseUrl, apiKey, modelId);
2941
+ writeSpinner.succeed('Opencode 配置写入完成');
2942
+
2943
+ console.log(chalk.green('\n✅ Opencode 配置完成!'));
2944
+ console.log(chalk.cyan(` Base URL: ${claudeBaseUrl}`));
2945
+ console.log(chalk.gray(` 模型: ${modelId}`));
2946
+ console.log(chalk.gray(' API Key: 已设置'));
2947
+ if (configPath) {
2948
+ console.log(chalk.gray(` 配置文件: ${configPath}`));
2949
+ }
2950
+ }
2951
+
2770
2952
  // ============ yycode 精简模式(零交互一键配置) ============
2771
2953
  async function yycodeQuickSetup(paths) {
2772
2954
  console.log(chalk.cyan.bold('\n⚡ yycode 一键配置\n'));
@@ -2985,11 +3167,13 @@ async function main() {
2985
3167
  type: 'list',
2986
3168
  name: 'action',
2987
3169
  message: '请选择操作:',
2988
- pageSize: 10,
3170
+ pageSize: 15,
2989
3171
  loop: false,
2990
3172
  choices: [
2991
- new inquirer.Separator(' -- 配置模型 --'),
2992
- { name: ' 一键激活', value: 'auto_activate' },
3173
+ new inquirer.Separator(' -- 一键配置 --'),
3174
+ { name: ' 配置 OpenClaw(Claude + Codex)', value: 'auto_activate' },
3175
+ { name: ' 配置 Claude Code', value: 'activate_claude_code' },
3176
+ { name: ' 配置 Opencode', value: 'activate_opencode' },
2993
3177
  new inquirer.Separator(' -- 工具 --'),
2994
3178
  { name: ' 切换模型', value: 'switch_model' },
2995
3179
  { name: ' 测试连接', value: 'test_connection' },
@@ -3029,6 +3213,12 @@ async function main() {
3029
3213
  case 'install_openclaw':
3030
3214
  await installOrUpdateOpenClaw();
3031
3215
  break;
3216
+ case 'activate_claude_code':
3217
+ await activateClaudeCode(paths);
3218
+ break;
3219
+ case 'activate_opencode':
3220
+ await activateOpencode(paths);
3221
+ break;
3032
3222
  }
3033
3223
  } catch (error) {
3034
3224
  console.log(chalk.red(`\n错误: ${error.message}\n`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yymaxapi",
3
- "version": "1.0.43",
3
+ "version": "1.0.45",
4
4
  "description": "跨平台 OpenClaw/Clawdbot 配置管理工具 - 管理中转地址、模型切换、API Keys、测速优化",
5
5
  "main": "bin/yymaxapi.js",
6
6
  "bin": {