yymaxapi 1.0.66 → 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.
- package/bin/yymaxapi.js +130 -26
- package/package.json +1 -1
package/bin/yymaxapi.js
CHANGED
|
@@ -726,6 +726,10 @@ 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(/\/+$/, '');
|
|
731
|
+
|
|
732
|
+
// ---- 1. opencode.json (CLI + 桌面版) ----
|
|
729
733
|
const configDir = process.platform === 'win32'
|
|
730
734
|
? path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'opencode')
|
|
731
735
|
: path.join(home, '.config', 'opencode');
|
|
@@ -736,33 +740,131 @@ function writeOpencodeConfig(claudeBaseUrl, codexBaseUrl, apiKey, modelId) {
|
|
|
736
740
|
if (fs.existsSync(configPath)) {
|
|
737
741
|
try { existing = JSON.parse(fs.readFileSync(configPath, 'utf8')); } catch { existing = {}; }
|
|
738
742
|
}
|
|
739
|
-
const cleanClaudeUrl = claudeBaseUrl.replace(/\/+$/, '');
|
|
740
|
-
let cleanCodexUrl = (codexBaseUrl || '').replace(/\/+$/, '');
|
|
741
743
|
if (!existing.provider) existing.provider = {};
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
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` }
|
|
747
751
|
};
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
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 }
|
|
754
760
|
};
|
|
755
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
|
+
// 设置默认模型
|
|
756
770
|
const rawModelId = modelId || 'claude-sonnet-4-6';
|
|
757
|
-
existing.model = rawModelId
|
|
758
|
-
|
|
759
|
-
|
|
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
|
+
|
|
760
779
|
if (!existing.$schema) existing.$schema = 'https://opencode.ai/config.json';
|
|
761
780
|
fs.writeFileSync(configPath, JSON.stringify(existing, null, 2), 'utf8');
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
781
|
+
} catch { /* 非关键,静默失败 */ }
|
|
782
|
+
|
|
783
|
+
// ---- 2. ~/.codex/config.toml (opencode 也读此格式) ----
|
|
784
|
+
const codexDir = path.join(home, '.codex');
|
|
785
|
+
const codexConfigPath = path.join(codexDir, 'config.toml');
|
|
786
|
+
try {
|
|
787
|
+
if (!fs.existsSync(codexDir)) fs.mkdirSync(codexDir, { recursive: true });
|
|
788
|
+
|
|
789
|
+
let content = '';
|
|
790
|
+
if (fs.existsSync(codexConfigPath)) {
|
|
791
|
+
content = fs.readFileSync(codexConfigPath, 'utf8');
|
|
792
|
+
}
|
|
793
|
+
|
|
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 = [
|
|
832
|
+
'',
|
|
833
|
+
'# >>> yunyi opencode >>>',
|
|
834
|
+
'[model_providers.yunyi-claude]',
|
|
835
|
+
'name = "云翼 Claude"',
|
|
836
|
+
`base_url = "${claudeUrl}"`,
|
|
837
|
+
'wire_api = "anthropic"',
|
|
838
|
+
`experimental_bearer_token = "${apiKey}"`,
|
|
839
|
+
];
|
|
840
|
+
if (codexUrl) {
|
|
841
|
+
providers.push(
|
|
842
|
+
'',
|
|
843
|
+
'[model_providers.yunyi-codex]',
|
|
844
|
+
'name = "云翼 Codex"',
|
|
845
|
+
`base_url = "${codexUrl}"`,
|
|
846
|
+
'wire_api = "responses"',
|
|
847
|
+
`experimental_bearer_token = "${apiKey}"`,
|
|
848
|
+
'requires_openai_auth = true',
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
providers.push('# <<< yunyi opencode <<<');
|
|
852
|
+
content += '\n\n' + providers.join('\n') + '\n';
|
|
853
|
+
|
|
854
|
+
fs.writeFileSync(codexConfigPath, content, 'utf8');
|
|
855
|
+
|
|
856
|
+
// ---- 3. ~/.codex/auth.json ----
|
|
857
|
+
const authPath = path.join(codexDir, 'auth.json');
|
|
858
|
+
let auth = {};
|
|
859
|
+
if (fs.existsSync(authPath)) {
|
|
860
|
+
try { auth = JSON.parse(fs.readFileSync(authPath, 'utf8')); } catch { auth = {}; }
|
|
861
|
+
}
|
|
862
|
+
auth.ANTHROPIC_API_KEY = apiKey;
|
|
863
|
+
auth.OPENAI_API_KEY = apiKey;
|
|
864
|
+
fs.writeFileSync(authPath, JSON.stringify(auth, null, 2), 'utf8');
|
|
865
|
+
} catch { /* 非关键,静默失败 */ }
|
|
866
|
+
|
|
867
|
+
return configPath;
|
|
766
868
|
}
|
|
767
869
|
|
|
768
870
|
function syncExternalTools(type, baseUrl, apiKey, extra = {}) {
|
|
@@ -2964,13 +3066,15 @@ async function activateOpencode(paths, args = {}) {
|
|
|
2964
3066
|
writeSpinner.succeed('Opencode 配置写入完成');
|
|
2965
3067
|
|
|
2966
3068
|
console.log(chalk.green('\n✅ Opencode 配置完成!'));
|
|
2967
|
-
console.log(chalk.cyan(` Claude: ${claudeBaseUrl}`));
|
|
2968
|
-
console.log(chalk.cyan(` Codex: ${codexBaseUrl}`));
|
|
2969
|
-
console.log(chalk.gray(`
|
|
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}`));
|
|
2970
3072
|
console.log(chalk.gray(' API Key: 已设置'));
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
3073
|
+
console.log(chalk.gray('\n 已写入:'));
|
|
3074
|
+
console.log(chalk.gray(' • ~/.config/opencode/opencode.json (CLI + 桌面版)'));
|
|
3075
|
+
console.log(chalk.gray(' • ~/.codex/config.toml (model_providers)'));
|
|
3076
|
+
console.log(chalk.gray(' • ~/.codex/auth.json (API Keys)'));
|
|
3077
|
+
console.log(chalk.yellow('\n 切换模型: 在 opencode 内使用 /model 命令切换 yunyi-claude / yunyi-codex'));
|
|
2974
3078
|
}
|
|
2975
3079
|
|
|
2976
3080
|
// ============ yycode 精简模式(零交互一键配置) ============
|