yymaxapi 1.0.34 → 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 +204 -1
- package/package.json +1 -1
package/bin/yymaxapi.js
CHANGED
|
@@ -2717,9 +2717,183 @@ async function autoActivate(paths, args = {}) {
|
|
|
2717
2717
|
}
|
|
2718
2718
|
}
|
|
2719
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
|
+
|
|
2720
2884
|
// ============ 主程序 ============
|
|
2721
2885
|
async function main() {
|
|
2722
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
|
+
|
|
2723
2897
|
console.log(chalk.cyan.bold('\n🔧 OpenClaw API 配置工具\n'));
|
|
2724
2898
|
|
|
2725
2899
|
const paths = getConfigPath();
|
|
@@ -2995,7 +3169,7 @@ async function testConnection(paths, args = {}) {
|
|
|
2995
3169
|
}
|
|
2996
3170
|
|
|
2997
3171
|
// 检查当前激活的是哪个
|
|
2998
|
-
|
|
3172
|
+
let primary = config.agents?.defaults?.model?.primary || '';
|
|
2999
3173
|
if (!primary.includes('/')) {
|
|
3000
3174
|
console.log(chalk.yellow('⚠️ 请先设置主模型'));
|
|
3001
3175
|
return;
|
|
@@ -3026,6 +3200,35 @@ async function testConnection(paths, args = {}) {
|
|
|
3026
3200
|
console.log(chalk.gray(`模型: ${primary}`));
|
|
3027
3201
|
console.log(chalk.gray(`Gateway: http://localhost:${gatewayPort}\n`));
|
|
3028
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
|
+
|
|
3029
3232
|
// 获取 Gateway token
|
|
3030
3233
|
const gatewayToken = config.gateway?.auth?.token;
|
|
3031
3234
|
if (!gatewayToken) {
|