yymaxapi 1.0.33 → 1.0.35
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 +215 -6
- package/package.json +1 -1
package/bin/yymaxapi.js
CHANGED
|
@@ -640,6 +640,10 @@ function writeCodexConfig(baseUrl, apiKey) {
|
|
|
640
640
|
const configPath = path.join(codexDir, 'config.toml');
|
|
641
641
|
const marker = '# >>> maxapi codex >>>';
|
|
642
642
|
const markerEnd = '# <<< maxapi codex <<<';
|
|
643
|
+
const providerKey = 'openclaw-relay';
|
|
644
|
+
// 确保 base_url 以 /v1 结尾(Codex CLI 要求)
|
|
645
|
+
let normalizedUrl = baseUrl.replace(/\/+$/, '');
|
|
646
|
+
if (!normalizedUrl.endsWith('/v1')) normalizedUrl += '/v1';
|
|
643
647
|
try {
|
|
644
648
|
let existing = '';
|
|
645
649
|
if (fs.existsSync(configPath)) {
|
|
@@ -650,12 +654,14 @@ function writeCodexConfig(baseUrl, apiKey) {
|
|
|
650
654
|
}
|
|
651
655
|
const section = [
|
|
652
656
|
marker,
|
|
653
|
-
`model = "
|
|
654
|
-
`
|
|
657
|
+
`model = "gpt-5.3-codex"`,
|
|
658
|
+
`model_provider = "${providerKey}"`,
|
|
655
659
|
``,
|
|
656
|
-
`[
|
|
657
|
-
`
|
|
658
|
-
`base_url = "${
|
|
660
|
+
`[model_providers.${providerKey}]`,
|
|
661
|
+
`name = "OpenClaw Relay"`,
|
|
662
|
+
`base_url = "${normalizedUrl}"`,
|
|
663
|
+
`wire_api = "responses"`,
|
|
664
|
+
`env_key = "OPENAI_API_KEY"`,
|
|
659
665
|
markerEnd
|
|
660
666
|
].join('\n');
|
|
661
667
|
const content = existing ? `${existing}\n\n${section}\n` : `${section}\n`;
|
|
@@ -2711,9 +2717,183 @@ async function autoActivate(paths, args = {}) {
|
|
|
2711
2717
|
}
|
|
2712
2718
|
}
|
|
2713
2719
|
|
|
2720
|
+
// ============ yycode 精简模式(零交互一键配置) ============
|
|
2721
|
+
async function yycodeQuickSetup(paths) {
|
|
2722
|
+
console.log(chalk.cyan.bold('\n⚡ yycode 一键配置\n'));
|
|
2723
|
+
|
|
2724
|
+
const claudeApiConfig = API_CONFIG.claude;
|
|
2725
|
+
const codexApiConfig = API_CONFIG.codex;
|
|
2726
|
+
const claudeProviderName = claudeApiConfig.providerName;
|
|
2727
|
+
const codexProviderName = codexApiConfig.providerName;
|
|
2728
|
+
|
|
2729
|
+
// ---- 探测已有 key ----
|
|
2730
|
+
let apiKey = '';
|
|
2731
|
+
let keySource = '';
|
|
2732
|
+
|
|
2733
|
+
// 1. CLI 参数
|
|
2734
|
+
const args = parseArgs(process.argv.slice(2));
|
|
2735
|
+
const directKey = (args['api-key'] || args.apiKey || args.key || '').toString().trim();
|
|
2736
|
+
if (directKey) {
|
|
2737
|
+
apiKey = directKey;
|
|
2738
|
+
keySource = '命令行参数';
|
|
2739
|
+
}
|
|
2740
|
+
|
|
2741
|
+
// 2. 环境变量
|
|
2742
|
+
if (!apiKey) {
|
|
2743
|
+
const envKeys = ['OPENCLAW_CLAUDE_KEY', 'OPENCLAW_CODEX_KEY', 'CLAUDE_API_KEY', 'OPENAI_API_KEY', 'OPENCLAW_API_KEY'];
|
|
2744
|
+
for (const k of envKeys) {
|
|
2745
|
+
if (process.env[k] && process.env[k].trim()) {
|
|
2746
|
+
apiKey = process.env[k].trim();
|
|
2747
|
+
keySource = `环境变量 ${k}`;
|
|
2748
|
+
break;
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
// 3. 已有 OpenClaw 配置(云翼 Claude Code 密钥)
|
|
2754
|
+
if (!apiKey) {
|
|
2755
|
+
try {
|
|
2756
|
+
const config = readConfig(paths.openclawConfig);
|
|
2757
|
+
if (config && config.models && config.models.providers) {
|
|
2758
|
+
// 优先取 claude-yunyi 的 key
|
|
2759
|
+
const preferredOrder = [claudeProviderName, codexProviderName];
|
|
2760
|
+
for (const name of preferredOrder) {
|
|
2761
|
+
const p = config.models.providers[name];
|
|
2762
|
+
if (p && p.apiKey && p.apiKey.trim()) {
|
|
2763
|
+
apiKey = p.apiKey.trim();
|
|
2764
|
+
keySource = `已有配置 (${name})`;
|
|
2765
|
+
break;
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
// 其他 provider 的 key
|
|
2769
|
+
if (!apiKey) {
|
|
2770
|
+
for (const [name, p] of Object.entries(config.models.providers)) {
|
|
2771
|
+
if (p.apiKey && p.apiKey.trim()) {
|
|
2772
|
+
apiKey = p.apiKey.trim();
|
|
2773
|
+
keySource = `已有配置 (${name})`;
|
|
2774
|
+
break;
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
} catch { /* ignore */ }
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2782
|
+
// 4. 都没有,提示输入
|
|
2783
|
+
if (apiKey) {
|
|
2784
|
+
const masked = apiKey.length > 8 ? apiKey.slice(0, 5) + '***' + apiKey.slice(-3) : '***';
|
|
2785
|
+
console.log(chalk.green(`✓ 已检测到 API Key: ${masked} (来源: ${keySource})`));
|
|
2786
|
+
} else {
|
|
2787
|
+
apiKey = await promptApiKey('请输入 API Key:', '');
|
|
2788
|
+
if (!apiKey) { console.log(chalk.gray('已取消')); return; }
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
// ---- 静默测速选最快节点 ----
|
|
2792
|
+
const speedSpinner = ora({ text: '正在测速选择最快节点...', spinner: 'dots' }).start();
|
|
2793
|
+
let selectedEndpoint = ENDPOINTS[0];
|
|
2794
|
+
try {
|
|
2795
|
+
const speedResult = await testAllEndpoints(ENDPOINTS, { autoFallback: true });
|
|
2796
|
+
if (speedResult.ranked && speedResult.ranked.length > 0) {
|
|
2797
|
+
selectedEndpoint = speedResult.ranked[0];
|
|
2798
|
+
}
|
|
2799
|
+
speedSpinner.succeed(`节点: ${selectedEndpoint.name}`);
|
|
2800
|
+
} catch {
|
|
2801
|
+
speedSpinner.succeed(`节点: ${selectedEndpoint.name} (默认)`);
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
// ---- 验证 key ----
|
|
2805
|
+
const validation = await validateApiKey(selectedEndpoint.url, apiKey);
|
|
2806
|
+
if (!validation.valid) {
|
|
2807
|
+
console.log(chalk.yellow('⚠ API Key 验证失败,仍将写入配置'));
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2810
|
+
// ---- 写入两套配置 ----
|
|
2811
|
+
const config = ensureConfigStructure(readConfig(paths.openclawConfig) || {});
|
|
2812
|
+
|
|
2813
|
+
// 清理旧 provider
|
|
2814
|
+
const existingProviders = Object.keys(config.models.providers || {});
|
|
2815
|
+
const toRemove = existingProviders.filter(n => n !== claudeProviderName && n !== codexProviderName);
|
|
2816
|
+
if (toRemove.length > 0) {
|
|
2817
|
+
pruneProvidersExcept(config, [claudeProviderName, codexProviderName]);
|
|
2818
|
+
pruneAuthProfilesExcept(paths.authProfiles, [claudeProviderName, codexProviderName]);
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
// Claude 侧
|
|
2822
|
+
const claudeBaseUrl = buildFullUrl(selectedEndpoint.url, 'claude');
|
|
2823
|
+
const claudeModelId = 'claude-opus-4-6';
|
|
2824
|
+
const claudeModel = CLAUDE_MODELS.find(m => m.id === claudeModelId) || { id: claudeModelId, name: 'Claude Opus 4.6' };
|
|
2825
|
+
const claudeModelKey = `${claudeProviderName}/${claudeModelId}`;
|
|
2826
|
+
|
|
2827
|
+
config.models.providers[claudeProviderName] = {
|
|
2828
|
+
baseUrl: claudeBaseUrl,
|
|
2829
|
+
auth: DEFAULT_AUTH_MODE,
|
|
2830
|
+
api: claudeApiConfig.api,
|
|
2831
|
+
headers: {},
|
|
2832
|
+
authHeader: false,
|
|
2833
|
+
apiKey: apiKey.trim(),
|
|
2834
|
+
models: [{ id: claudeModel.id, name: claudeModel.name, contextWindow: claudeApiConfig.contextWindow, maxTokens: claudeApiConfig.maxTokens }]
|
|
2835
|
+
};
|
|
2836
|
+
config.auth.profiles[`${claudeProviderName}:default`] = { provider: claudeProviderName, mode: 'api_key' };
|
|
2837
|
+
config.agents.defaults.models[claudeModelKey] = { alias: claudeProviderName };
|
|
2838
|
+
|
|
2839
|
+
// Codex 侧
|
|
2840
|
+
const codexBaseUrl = buildFullUrl(selectedEndpoint.url, 'codex');
|
|
2841
|
+
const codexModelId = 'gpt-5.3-codex';
|
|
2842
|
+
const codexModel = CODEX_MODELS.find(m => m.id === codexModelId) || { id: codexModelId, name: 'GPT 5.3 Codex' };
|
|
2843
|
+
const codexModelKey = `${codexProviderName}/${codexModelId}`;
|
|
2844
|
+
|
|
2845
|
+
config.models.providers[codexProviderName] = {
|
|
2846
|
+
baseUrl: codexBaseUrl,
|
|
2847
|
+
auth: DEFAULT_AUTH_MODE,
|
|
2848
|
+
api: codexApiConfig.api,
|
|
2849
|
+
headers: {},
|
|
2850
|
+
authHeader: false,
|
|
2851
|
+
apiKey: apiKey.trim(),
|
|
2852
|
+
models: [{ id: codexModel.id, name: codexModel.name, contextWindow: codexApiConfig.contextWindow, maxTokens: codexApiConfig.maxTokens }]
|
|
2853
|
+
};
|
|
2854
|
+
config.auth.profiles[`${codexProviderName}:default`] = { provider: codexProviderName, mode: 'api_key' };
|
|
2855
|
+
config.agents.defaults.models[codexModelKey] = { alias: codexProviderName };
|
|
2856
|
+
|
|
2857
|
+
// 默认主力: Codex, 备用: Claude
|
|
2858
|
+
config.agents.defaults.model.primary = codexModelKey;
|
|
2859
|
+
config.agents.defaults.model.fallbacks = [claudeModelKey];
|
|
2860
|
+
|
|
2861
|
+
// ---- 写入 ----
|
|
2862
|
+
const writeSpinner = ora({ text: '正在写入配置...', spinner: 'dots' }).start();
|
|
2863
|
+
createTimestampedBackup(paths.openclawConfig, paths.configDir, 'yycode');
|
|
2864
|
+
ensureGatewaySettings(config);
|
|
2865
|
+
writeConfigWithSync(paths, config);
|
|
2866
|
+
updateAuthProfiles(paths.authProfiles, claudeProviderName, apiKey);
|
|
2867
|
+
updateAuthProfiles(paths.authProfiles, codexProviderName, apiKey);
|
|
2868
|
+
try { syncExternalTools('claude', claudeBaseUrl, apiKey); } catch { /* ignore */ }
|
|
2869
|
+
try { syncExternalTools('codex', codexBaseUrl, apiKey); } catch { /* ignore */ }
|
|
2870
|
+
writeSpinner.succeed('配置写入完成');
|
|
2871
|
+
|
|
2872
|
+
// ---- 结果 ----
|
|
2873
|
+
console.log(chalk.green('\n✅ 配置完成!'));
|
|
2874
|
+
console.log(chalk.cyan(` Claude Code: ${claudeBaseUrl}`));
|
|
2875
|
+
console.log(chalk.gray(` 模型: ${claudeModel.name}`));
|
|
2876
|
+
console.log(chalk.cyan(` Codex (主): ${codexBaseUrl}`));
|
|
2877
|
+
console.log(chalk.gray(` 模型: ${codexModel.name}`));
|
|
2878
|
+
console.log(chalk.gray(' API Key: 已设置'));
|
|
2879
|
+
console.log(chalk.gray(' 同步: Claude Code settings, Codex CLI config'));
|
|
2880
|
+
console.log('');
|
|
2881
|
+
}
|
|
2882
|
+
|
|
2883
|
+
|
|
2714
2884
|
// ============ 主程序 ============
|
|
2715
2885
|
async function main() {
|
|
2716
2886
|
console.clear();
|
|
2887
|
+
|
|
2888
|
+
// yycode 精简模式:检测到 yycode CLI 时直接走零交互流程
|
|
2889
|
+
const isYYCode = path.basename(process.argv[1] || '').replace(/\.js$/, '') === 'yycode';
|
|
2890
|
+
if (isYYCode) {
|
|
2891
|
+
const paths = getConfigPath();
|
|
2892
|
+
backupOriginalConfig(paths.openclawConfig, paths.configDir);
|
|
2893
|
+
await yycodeQuickSetup(paths);
|
|
2894
|
+
return;
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2717
2897
|
console.log(chalk.cyan.bold('\n🔧 OpenClaw API 配置工具\n'));
|
|
2718
2898
|
|
|
2719
2899
|
const paths = getConfigPath();
|
|
@@ -2989,7 +3169,7 @@ async function testConnection(paths, args = {}) {
|
|
|
2989
3169
|
}
|
|
2990
3170
|
|
|
2991
3171
|
// 检查当前激活的是哪个
|
|
2992
|
-
|
|
3172
|
+
let primary = config.agents?.defaults?.model?.primary || '';
|
|
2993
3173
|
if (!primary.includes('/')) {
|
|
2994
3174
|
console.log(chalk.yellow('⚠️ 请先设置主模型'));
|
|
2995
3175
|
return;
|
|
@@ -3020,6 +3200,35 @@ async function testConnection(paths, args = {}) {
|
|
|
3020
3200
|
console.log(chalk.gray(`模型: ${primary}`));
|
|
3021
3201
|
console.log(chalk.gray(`Gateway: http://localhost:${gatewayPort}\n`));
|
|
3022
3202
|
|
|
3203
|
+
// 模型切换(仅 Claude)
|
|
3204
|
+
if (apiType.startsWith('anthropic')) {
|
|
3205
|
+
const currentModelId = primary.split('/')[1] || '';
|
|
3206
|
+
const switchModels = [
|
|
3207
|
+
{ id: 'claude-opus-4-6', name: 'Claude Opus 4.6' },
|
|
3208
|
+
{ id: 'claude-sonnet-4-6', name: 'Claude Sonnet 4.6' },
|
|
3209
|
+
];
|
|
3210
|
+
const { switchModel } = await inquirer.prompt([{
|
|
3211
|
+
type: 'list',
|
|
3212
|
+
name: 'switchModel',
|
|
3213
|
+
message: '选择测试模型:',
|
|
3214
|
+
default: currentModelId,
|
|
3215
|
+
choices: switchModels.map(m => ({
|
|
3216
|
+
name: m.id === currentModelId ? `${m.name} (当前)` : m.name,
|
|
3217
|
+
value: m.id,
|
|
3218
|
+
})),
|
|
3219
|
+
}]);
|
|
3220
|
+
if (switchModel !== currentModelId) {
|
|
3221
|
+
primary = `${providerName}/${switchModel}`;
|
|
3222
|
+
config.agents.defaults.model.primary = primary;
|
|
3223
|
+
if (!config.agents.defaults.models) config.agents.defaults.models = {};
|
|
3224
|
+
if (!config.agents.defaults.models[primary]) {
|
|
3225
|
+
config.agents.defaults.models[primary] = { alias: providerName };
|
|
3226
|
+
}
|
|
3227
|
+
writeConfigWithSync(paths, config);
|
|
3228
|
+
console.log(chalk.green(` ✅ 已切换到 ${switchModels.find(m => m.id === switchModel).name}\n`));
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3023
3232
|
// 获取 Gateway token
|
|
3024
3233
|
const gatewayToken = config.gateway?.auth?.token;
|
|
3025
3234
|
if (!gatewayToken) {
|