yymaxapi 1.0.67 → 1.0.68

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 +92 -44
  2. package/package.json +1 -1
package/bin/yymaxapi.js CHANGED
@@ -726,6 +726,8 @@ function writeCodexConfig(baseUrl, apiKey) {
726
726
 
727
727
  function writeOpencodeConfig(claudeBaseUrl, codexBaseUrl, apiKey, modelId) {
728
728
  const home = os.homedir();
729
+ const claudeUrl = claudeBaseUrl.replace(/\/+$/, '');
730
+ const codexUrl = (codexBaseUrl || '').replace(/\/+$/, '');
729
731
 
730
732
  // ---- 1. opencode.json (CLI + 桌面版) ----
731
733
  const configDir = process.platform === 'win32'
@@ -738,21 +740,42 @@ function writeOpencodeConfig(claudeBaseUrl, codexBaseUrl, apiKey, modelId) {
738
740
  if (fs.existsSync(configPath)) {
739
741
  try { existing = JSON.parse(fs.readFileSync(configPath, 'utf8')); } catch { existing = {}; }
740
742
  }
741
- const cleanClaudeUrl = claudeBaseUrl.replace(/\/+$/, '');
742
- const cleanCodexUrl = (codexBaseUrl || '').replace(/\/+$/, '');
743
743
  if (!existing.provider) existing.provider = {};
744
- existing.provider.anthropic = {
745
- options: { apiKey: apiKey, baseURL: cleanClaudeUrl }
744
+
745
+ // Claude provider (@ai-sdk/anthropic)
746
+ existing.provider['yunyi-claude'] = {
747
+ name: '云翼 Claude',
748
+ npm: '@ai-sdk/anthropic',
749
+ models: { 'claude-sonnet-4-6': { name: 'Claude Sonnet 4.6' } },
750
+ options: { apiKey, baseURL: `${claudeUrl}/v1` }
746
751
  };
747
- if (cleanCodexUrl) {
748
- existing.provider.openai = {
749
- options: { apiKey: apiKey, baseURL: cleanCodexUrl }
752
+
753
+ // Codex provider (@ai-sdk/openai)
754
+ if (codexUrl) {
755
+ existing.provider['yunyi-codex'] = {
756
+ name: '云翼 Codex',
757
+ npm: '@ai-sdk/openai',
758
+ models: { 'gpt-5.4': { name: 'GPT 5.4' } },
759
+ options: { apiKey, baseURL: codexUrl }
750
760
  };
751
761
  }
762
+
763
+ // 清理旧版写入的通用 provider 名(之前的 bug)
764
+ if (existing.provider.anthropic && existing.provider.anthropic.options
765
+ && existing.provider.anthropic.options.baseURL && existing.provider.anthropic.options.baseURL.includes('yunyi')) {
766
+ delete existing.provider.anthropic;
767
+ }
768
+
769
+ // 设置默认模型
752
770
  const rawModelId = modelId || 'claude-sonnet-4-6';
753
- existing.model = rawModelId.startsWith('anthropic/') || rawModelId.startsWith('openai/')
754
- ? rawModelId
755
- : `anthropic/${rawModelId}`;
771
+ existing.model = `yunyi-claude/${rawModelId}`;
772
+
773
+ // 从 disabled_providers 中移除我们的 provider
774
+ if (Array.isArray(existing.disabled_providers)) {
775
+ existing.disabled_providers = existing.disabled_providers.filter(p => p !== 'yunyi-claude' && p !== 'yunyi-codex');
776
+ if (existing.disabled_providers.length === 0) delete existing.disabled_providers;
777
+ }
778
+
756
779
  if (!existing.$schema) existing.$schema = 'https://opencode.ai/config.json';
757
780
  fs.writeFileSync(configPath, JSON.stringify(existing, null, 2), 'utf8');
758
781
  } catch { /* 非关键,静默失败 */ }
@@ -763,49 +786,74 @@ function writeOpencodeConfig(claudeBaseUrl, codexBaseUrl, apiKey, modelId) {
763
786
  try {
764
787
  if (!fs.existsSync(codexDir)) fs.mkdirSync(codexDir, { recursive: true });
765
788
 
766
- const claudeUrl = claudeBaseUrl.replace(/\/+$/, '');
767
- const codexUrl = (codexBaseUrl || '').replace(/\/+$/, '');
768
-
769
- const marker = '# >>> yunyi opencode >>>';
770
- const markerEnd = '# <<< yunyi opencode <<<';
771
- let existing = '';
789
+ let content = '';
772
790
  if (fs.existsSync(codexConfigPath)) {
773
- existing = fs.readFileSync(codexConfigPath, 'utf8');
774
- const re = new RegExp(`${marker.replace(/[.*+?^${}()|[\\]\\]/g, '\\$&')}[\\s\\S]*?${markerEnd.replace(/[.*+?^${}()|[\\]\\]/g, '\\$&')}\\n?`, 'g');
775
- existing = existing.replace(re, '').trim();
791
+ content = fs.readFileSync(codexConfigPath, 'utf8');
776
792
  }
777
793
 
778
- const section = [
779
- marker,
780
- `model = "yunyi-claude/claude-sonnet-4-6"`,
781
- `model_provider = "yunyi-claude"`,
782
- `model_context_window = 1000000`,
783
- `model_auto_compact_token_limit = 900000`,
784
- `model_reasoning_effort = "xhigh"`,
794
+ // 移除旧标记块(yunyi opencode / maxapi codex)
795
+ content = content.replace(/# >>> yunyi opencode >>>[\s\S]*?# <<< yunyi opencode <<<\n?/g, '');
796
+ content = content.replace(/# >>> maxapi codex >>>[\s\S]*?# <<< maxapi codex <<<\n?/g, '');
797
+
798
+ // 移除旧的 yunyi model_providers 段落(包括 yunyi-cli 写入的 [model_providers.yunyi])
799
+ content = content.replace(/\[model_providers\.yunyi[^\]]*\]\n(?:(?!\[)[^\n]*\n?)*/g, '');
800
+
801
+ // 替换或插入顶层设置
802
+ const topSettings = {
803
+ model_provider: '"yunyi-claude"',
804
+ model: '"claude-sonnet-4-6"',
805
+ model_context_window: '1000000',
806
+ model_auto_compact_token_limit: '900000',
807
+ model_reasoning_effort: '"xhigh"',
808
+ disable_response_storage: 'true',
809
+ };
810
+ for (const [key, value] of Object.entries(topSettings)) {
811
+ const re = new RegExp(`^${key}\\s*=\\s*[^\\n]*`, 'm');
812
+ if (re.test(content)) {
813
+ content = content.replace(re, `${key} = ${value}`);
814
+ } else {
815
+ // 插入到第一个 section 之前
816
+ const firstSection = content.search(/^\[/m);
817
+ if (firstSection > 0) {
818
+ content = content.slice(0, firstSection) + `${key} = ${value}\n` + content.slice(firstSection);
819
+ } else if (firstSection === 0) {
820
+ content = `${key} = ${value}\n` + content;
821
+ } else {
822
+ content += `${key} = ${value}\n`;
823
+ }
824
+ }
825
+ }
826
+
827
+ // 清理多余空行
828
+ content = content.replace(/\n{3,}/g, '\n\n').trim();
829
+
830
+ // 追加 model_providers 段落
831
+ const providers = [
785
832
  '',
786
- `[model_providers.yunyi-claude]`,
787
- `name = "云翼 Claude"`,
833
+ '# >>> yunyi opencode >>>',
834
+ '[model_providers.yunyi-claude]',
835
+ 'name = "云翼 Claude"',
788
836
  `base_url = "${claudeUrl}"`,
789
- `wire_api = "anthropic"`,
790
- `env_key = "ANTHROPIC_API_KEY"`,
837
+ 'wire_api = "anthropic"',
838
+ `experimental_bearer_token = "${apiKey}"`,
791
839
  ];
792
-
793
840
  if (codexUrl) {
794
- section.push(
841
+ providers.push(
795
842
  '',
796
- `[model_providers.yunyi-codex]`,
797
- `name = "云翼 Codex"`,
843
+ '[model_providers.yunyi-codex]',
844
+ 'name = "云翼 Codex"',
798
845
  `base_url = "${codexUrl}"`,
799
- `wire_api = "responses"`,
800
- `requires_openai_auth = true`,
801
- `env_key = "OPENAI_API_KEY"`,
846
+ 'wire_api = "responses"',
847
+ `experimental_bearer_token = "${apiKey}"`,
848
+ 'requires_openai_auth = true',
802
849
  );
803
850
  }
851
+ providers.push('# <<< yunyi opencode <<<');
852
+ content += '\n\n' + providers.join('\n') + '\n';
804
853
 
805
- section.push(markerEnd);
806
- fs.writeFileSync(codexConfigPath, existing ? `${existing}\n\n${section.join('\n')}\n` : `${section.join('\n')}\n`, 'utf8');
854
+ fs.writeFileSync(codexConfigPath, content, 'utf8');
807
855
 
808
- // ~/.codex/auth.json
856
+ // ---- 3. ~/.codex/auth.json ----
809
857
  const authPath = path.join(codexDir, 'auth.json');
810
858
  let auth = {};
811
859
  if (fs.existsSync(authPath)) {
@@ -3018,15 +3066,15 @@ async function activateOpencode(paths, args = {}) {
3018
3066
  writeSpinner.succeed('Opencode 配置写入完成');
3019
3067
 
3020
3068
  console.log(chalk.green('\n✅ Opencode 配置完成!'));
3021
- console.log(chalk.cyan(` Claude: ${claudeBaseUrl}`));
3022
- console.log(chalk.cyan(` Codex: ${codexBaseUrl}`));
3023
- console.log(chalk.gray(` 默认模型: anthropic/${modelId}`));
3069
+ console.log(chalk.cyan(` Claude: ${claudeBaseUrl} → claude-sonnet-4-6`));
3070
+ console.log(chalk.cyan(` Codex: ${codexBaseUrl} → gpt-5.4`));
3071
+ console.log(chalk.gray(` 默认模型: yunyi-claude/${modelId}`));
3024
3072
  console.log(chalk.gray(' API Key: 已设置'));
3025
3073
  console.log(chalk.gray('\n 已写入:'));
3026
3074
  console.log(chalk.gray(' • ~/.config/opencode/opencode.json (CLI + 桌面版)'));
3027
3075
  console.log(chalk.gray(' • ~/.codex/config.toml (model_providers)'));
3028
3076
  console.log(chalk.gray(' • ~/.codex/auth.json (API Keys)'));
3029
- console.log(chalk.yellow('\n 切换模型: 在 opencode 内使用 /model 命令切换 claude/gpt'));
3077
+ console.log(chalk.yellow('\n 切换模型: 在 opencode 内使用 /model 命令切换 yunyi-claude / yunyi-codex'));
3030
3078
  }
3031
3079
 
3032
3080
  // ============ yycode 精简模式(零交互一键配置) ============
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yymaxapi",
3
- "version": "1.0.67",
3
+ "version": "1.0.68",
4
4
  "description": "跨平台 OpenClaw/Clawdbot 配置管理工具 - 管理中转地址、模型切换、API Keys、测速优化",
5
5
  "main": "bin/yymaxapi.js",
6
6
  "bin": {