yymaxapi 1.0.111 → 1.0.116
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/README.md
CHANGED
package/bin/yymaxapi.js
CHANGED
|
@@ -73,19 +73,11 @@ if (!process.__yymaxapiWarningFilterInstalled && typeof process.emitWarning ===
|
|
|
73
73
|
// ============ 预设 (由 build.js 从 provider config 生成) ============
|
|
74
74
|
const DEFAULT_ENDPOINTS = [
|
|
75
75
|
{
|
|
76
|
-
"name": "
|
|
77
|
-
"url": "https://yunyi.rdzhvip.com"
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
"name": "CF国外节点1",
|
|
76
|
+
"name": "默认主节点",
|
|
81
77
|
"url": "https://yunyi.cfd"
|
|
82
78
|
},
|
|
83
79
|
{
|
|
84
|
-
"name": "CF国外节点
|
|
85
|
-
"url": "https://cdn1.yunyi.cfd"
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
"name": "CF国外节点3",
|
|
80
|
+
"name": "CF国外节点1",
|
|
89
81
|
"url": "https://cdn2.yunyi.cfd"
|
|
90
82
|
}
|
|
91
83
|
];
|
|
@@ -616,9 +608,73 @@ function shouldRunEndpointSpeedTest(args = {}) {
|
|
|
616
608
|
return !!raw;
|
|
617
609
|
}
|
|
618
610
|
|
|
611
|
+
function getEndpointOverrideInput(args = {}) {
|
|
612
|
+
const raw = args['endpoint-url']
|
|
613
|
+
?? args.endpointUrl
|
|
614
|
+
?? args.endpoint
|
|
615
|
+
?? args.domain
|
|
616
|
+
?? args['base-url']
|
|
617
|
+
?? args.baseUrl
|
|
618
|
+
?? '';
|
|
619
|
+
return String(raw || '').trim();
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function normalizeEndpointBaseUrl(baseUrl) {
|
|
623
|
+
let trimmed = String(baseUrl || '').trim();
|
|
624
|
+
if (!trimmed) return '';
|
|
625
|
+
|
|
626
|
+
trimmed = trimClaudeMessagesSuffix(trimmed);
|
|
627
|
+
trimmed = trimOpenAiEndpointSuffix(trimmed);
|
|
628
|
+
trimmed = stripManagedYunyiSuffix(trimmed);
|
|
629
|
+
|
|
630
|
+
try {
|
|
631
|
+
const urlObj = new URL(trimmed);
|
|
632
|
+
urlObj.search = '';
|
|
633
|
+
urlObj.hash = '';
|
|
634
|
+
const normalizedPath = urlObj.pathname.replace(/\/+$/, '');
|
|
635
|
+
return `${urlObj.origin}${normalizedPath && normalizedPath !== '/' ? normalizedPath : ''}`;
|
|
636
|
+
} catch {
|
|
637
|
+
return trimmed.replace(/\/+$/, '');
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function resolveEndpointOverride(args = {}) {
|
|
642
|
+
const raw = getEndpointOverrideInput(args);
|
|
643
|
+
if (!raw) return null;
|
|
644
|
+
|
|
645
|
+
const normalized = normalizeEndpointBaseUrl(raw);
|
|
646
|
+
if (!normalized || !isValidUrl(normalized)) {
|
|
647
|
+
throw new Error('自定义节点 URL 无效,请使用 http(s):// 域名,支持填写根域名、/claude 或 /codex 地址');
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
return { name: '自定义节点', url: normalized };
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
async function promptCustomEndpointSelection(args = {}, options = {}) {
|
|
654
|
+
const defaultInput = getEndpointOverrideInput(args);
|
|
655
|
+
const promptMessage = options.customEndpointMessage || '请输入节点域名或 Base URL(支持根域名、/claude、/codex):';
|
|
656
|
+
const { customEndpointInput } = await inquirer.prompt([{
|
|
657
|
+
type: 'input',
|
|
658
|
+
name: 'customEndpointInput',
|
|
659
|
+
message: promptMessage,
|
|
660
|
+
default: defaultInput,
|
|
661
|
+
validate: input => {
|
|
662
|
+
const normalized = normalizeEndpointBaseUrl(input);
|
|
663
|
+
return Boolean(normalized && isValidUrl(normalized)) || '请输入有效的 URL(http:// 或 https://)';
|
|
664
|
+
}
|
|
665
|
+
}]);
|
|
666
|
+
|
|
667
|
+
return {
|
|
668
|
+
name: options.customEndpointName || '自定义节点',
|
|
669
|
+
url: normalizeEndpointBaseUrl(customEndpointInput)
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
|
|
619
673
|
async function resolveEndpointSelection(args = {}, options = {}) {
|
|
620
674
|
const defaultEndpoint = options.defaultEndpoint || ENDPOINTS[0];
|
|
621
675
|
if (!defaultEndpoint) return null;
|
|
676
|
+
const directOverride = resolveEndpointOverride(args);
|
|
677
|
+
if (directOverride) return directOverride;
|
|
622
678
|
if (!shouldRunEndpointSpeedTest(args)) return defaultEndpoint;
|
|
623
679
|
|
|
624
680
|
const speedIntro = options.speedIntro || '📡 开始测速节点...\n';
|
|
@@ -636,6 +692,7 @@ async function resolveEndpointSelection(args = {}, options = {}) {
|
|
|
636
692
|
message: chooseMessage,
|
|
637
693
|
choices: [
|
|
638
694
|
{ name: `* 使用默认节点 (${defaultEndpoint.name})`, value: -1 },
|
|
695
|
+
{ name: '* 手动输入节点域名', value: '__custom__' },
|
|
639
696
|
new inquirer.Separator(' ---- 或按测速结果选择 ----'),
|
|
640
697
|
...sorted.map((endpoint, index) => ({
|
|
641
698
|
name: `${endpoint.name} - ${endpoint.latency}ms (评分:${endpoint.score})`,
|
|
@@ -644,7 +701,9 @@ async function resolveEndpointSelection(args = {}, options = {}) {
|
|
|
644
701
|
]
|
|
645
702
|
}]);
|
|
646
703
|
|
|
647
|
-
const selectedEndpoint = selectedIndex ===
|
|
704
|
+
const selectedEndpoint = selectedIndex === '__custom__'
|
|
705
|
+
? await promptCustomEndpointSelection(args, options)
|
|
706
|
+
: (selectedIndex === -1 ? defaultEndpoint : sorted[selectedIndex]);
|
|
648
707
|
if (speedResult.usedFallback) {
|
|
649
708
|
console.log(chalk.yellow('\n⚠ 当前使用备用节点\n'));
|
|
650
709
|
}
|
|
@@ -652,13 +711,21 @@ async function resolveEndpointSelection(args = {}, options = {}) {
|
|
|
652
711
|
}
|
|
653
712
|
|
|
654
713
|
console.log(chalk.red('\n⚠️ 所有节点(含备用)均不可达'));
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
714
|
+
const proceedChoiceLabel = options.proceedChoiceLabel || `${proceedMessage.replace(/[??]\s*$/, '')} (${defaultEndpoint.name})`;
|
|
715
|
+
const { nextAction } = await inquirer.prompt([{
|
|
716
|
+
type: 'list',
|
|
717
|
+
name: 'nextAction',
|
|
718
|
+
message: '测速未找到可用节点,接下来怎么做?',
|
|
719
|
+
choices: [
|
|
720
|
+
{ name: proceedChoiceLabel, value: 'default' },
|
|
721
|
+
{ name: '手动输入节点域名', value: 'custom' },
|
|
722
|
+
{ name: '取消', value: 'cancel' }
|
|
723
|
+
]
|
|
660
724
|
}]);
|
|
661
|
-
if (
|
|
725
|
+
if (nextAction === 'custom') {
|
|
726
|
+
return promptCustomEndpointSelection(args, options);
|
|
727
|
+
}
|
|
728
|
+
if (nextAction !== 'default') {
|
|
662
729
|
console.log(chalk.gray('已取消'));
|
|
663
730
|
return null;
|
|
664
731
|
}
|
|
@@ -1046,12 +1113,12 @@ function writeClaudeCodeSettings(baseUrl, apiKey, modelId = getDefaultClaudeMode
|
|
|
1046
1113
|
settings.apiBaseUrl = normalizedBaseUrl;
|
|
1047
1114
|
settings.model = normalizedModelId;
|
|
1048
1115
|
if (!settings.env) settings.env = {};
|
|
1049
|
-
settings.env.
|
|
1116
|
+
settings.env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
1050
1117
|
settings.env.ANTHROPIC_BASE_URL = normalizedBaseUrl;
|
|
1051
1118
|
settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL = pinnedOpusModel;
|
|
1052
1119
|
settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL = pinnedSonnetModel;
|
|
1053
1120
|
settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = pinnedHaikuModel;
|
|
1054
|
-
delete settings.env.
|
|
1121
|
+
delete settings.env.ANTHROPIC_API_KEY;
|
|
1055
1122
|
delete settings.availableModels;
|
|
1056
1123
|
delete settings.env.ANTHROPIC_MODEL;
|
|
1057
1124
|
if (!fs.existsSync(claudeDir)) fs.mkdirSync(claudeDir, { recursive: true });
|
|
@@ -2671,17 +2738,17 @@ function hasManagedYunyiFootprint(config) {
|
|
|
2671
2738
|
}
|
|
2672
2739
|
|
|
2673
2740
|
function inferManagedYunyiEndpointUrl(config, explicitEndpointUrl = '') {
|
|
2674
|
-
const direct =
|
|
2675
|
-
if (direct &&
|
|
2741
|
+
const direct = normalizeEndpointBaseUrl(explicitEndpointUrl);
|
|
2742
|
+
if (direct && isValidUrl(direct)) return direct;
|
|
2676
2743
|
|
|
2677
2744
|
const providers = config?.models?.providers || {};
|
|
2678
2745
|
for (const [name, provider] of Object.entries(providers)) {
|
|
2679
2746
|
if (!isYunyiProviderEntry(name, provider, 'claude') && !isYunyiProviderEntry(name, provider, 'codex')) continue;
|
|
2680
|
-
const candidate =
|
|
2681
|
-
if (candidate &&
|
|
2747
|
+
const candidate = normalizeEndpointBaseUrl(provider?.baseUrl || provider?.base_url || '');
|
|
2748
|
+
if (candidate && isValidUrl(candidate)) return candidate;
|
|
2682
2749
|
}
|
|
2683
2750
|
|
|
2684
|
-
return ENDPOINTS.find(item =>
|
|
2751
|
+
return ENDPOINTS.find(item => isValidUrl(item.url))?.url || ENDPOINTS[0]?.url || '';
|
|
2685
2752
|
}
|
|
2686
2753
|
|
|
2687
2754
|
function inferManagedYunyiApiKey(config, explicitApiKey = '') {
|
|
@@ -3450,6 +3517,10 @@ function shellQuote(value) {
|
|
|
3450
3517
|
return `"${str.replace(/(["\\$`])/g, '\\$1')}"`;
|
|
3451
3518
|
}
|
|
3452
3519
|
|
|
3520
|
+
function getShellNullInputRedirect() {
|
|
3521
|
+
return process.platform === 'win32' ? ' < NUL' : ' < /dev/null';
|
|
3522
|
+
}
|
|
3523
|
+
|
|
3453
3524
|
function escapeSingleQuotedShell(value) {
|
|
3454
3525
|
return String(value || '').replace(/'/g, "'\\''");
|
|
3455
3526
|
}
|
|
@@ -3931,18 +4002,17 @@ function readClaudeCodeCliConfig() {
|
|
|
3931
4002
|
const settings = readJsonIfExists(settingsPath) || {};
|
|
3932
4003
|
const settingsEnv = settings.env && typeof settings.env === 'object' ? settings.env : {};
|
|
3933
4004
|
const settingsApiKey = String(settingsEnv.ANTHROPIC_API_KEY || '').trim();
|
|
3934
|
-
const
|
|
4005
|
+
const settingsAuthToken = String(settingsEnv.ANTHROPIC_AUTH_TOKEN || '').trim();
|
|
3935
4006
|
const settingsBaseUrl = String(settingsEnv.ANTHROPIC_BASE_URL || settings.apiBaseUrl || '').trim();
|
|
3936
4007
|
return {
|
|
3937
4008
|
settingsPath,
|
|
3938
4009
|
modelId: settings.model || settingsEnv.ANTHROPIC_MODEL || process.env.ANTHROPIC_MODEL || getDefaultClaudeModel().id,
|
|
3939
4010
|
baseUrl: settingsBaseUrl || process.env.ANTHROPIC_BASE_URL || '',
|
|
3940
|
-
apiKey:
|
|
4011
|
+
apiKey: settingsAuthToken || settingsApiKey || process.env.ANTHROPIC_AUTH_TOKEN || process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_API_KEY || '',
|
|
3941
4012
|
settingsBaseUrl,
|
|
3942
4013
|
settingsApiKey,
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
usesLegacyAuthTokenOnly: !settingsApiKey && !!settingsLegacyAuthToken,
|
|
4014
|
+
settingsAuthToken,
|
|
4015
|
+
settingsAuthMode: settingsAuthToken ? 'auth_token' : (settingsApiKey ? 'api_key' : ''),
|
|
3946
4016
|
configured: fs.existsSync(settingsPath)
|
|
3947
4017
|
};
|
|
3948
4018
|
}
|
|
@@ -4022,6 +4092,41 @@ function buildReadableAgentError(text, fallback = '') {
|
|
|
4022
4092
|
return (preferred || lines[0] || fallback).slice(0, 300);
|
|
4023
4093
|
}
|
|
4024
4094
|
|
|
4095
|
+
function scoreCliErrorDetail(text) {
|
|
4096
|
+
const lower = String(text || '').toLowerCase();
|
|
4097
|
+
if (!lower) return -1;
|
|
4098
|
+
let score = 0;
|
|
4099
|
+
if ([
|
|
4100
|
+
'forbidden',
|
|
4101
|
+
'unauthorized',
|
|
4102
|
+
'permission to access',
|
|
4103
|
+
'authentication_error',
|
|
4104
|
+
'invalid bearer token',
|
|
4105
|
+
'invalid api key',
|
|
4106
|
+
'all models failed',
|
|
4107
|
+
'auth issue',
|
|
4108
|
+
'auth_permanent',
|
|
4109
|
+
'unexpected status',
|
|
4110
|
+
'gateway agent failed'
|
|
4111
|
+
].some(pattern => lower.includes(pattern))) {
|
|
4112
|
+
score += 5;
|
|
4113
|
+
}
|
|
4114
|
+
if ([
|
|
4115
|
+
'failed',
|
|
4116
|
+
'error:',
|
|
4117
|
+
'missing environment variable'
|
|
4118
|
+
].some(pattern => lower.includes(pattern))) {
|
|
4119
|
+
score += 2;
|
|
4120
|
+
}
|
|
4121
|
+
if ([
|
|
4122
|
+
'reading additional input from stdin',
|
|
4123
|
+
'reconnecting'
|
|
4124
|
+
].some(pattern => lower.includes(pattern))) {
|
|
4125
|
+
score -= 2;
|
|
4126
|
+
}
|
|
4127
|
+
return score;
|
|
4128
|
+
}
|
|
4129
|
+
|
|
4025
4130
|
function looksLikeCliTestError(text) {
|
|
4026
4131
|
const lower = cleanCliTestOutput(text).toLowerCase();
|
|
4027
4132
|
if (!lower) return false;
|
|
@@ -4044,13 +4149,19 @@ function looksLikeCliTestError(text) {
|
|
|
4044
4149
|
|
|
4045
4150
|
function runCliTestCandidates(name, commands, env) {
|
|
4046
4151
|
let lastError = '命令执行失败';
|
|
4152
|
+
let lastErrorScore = scoreCliErrorDetail(lastError);
|
|
4047
4153
|
for (const command of commands) {
|
|
4048
4154
|
const result = safeExec(command, { timeout: 120000, env, maxBuffer: 1024 * 1024 });
|
|
4049
4155
|
const combined = cleanCliTestOutput(`${result.output || ''}\n${result.stdout || ''}\n${result.stderr || ''}`);
|
|
4050
4156
|
if (result.ok && !looksLikeCliTestError(combined)) {
|
|
4051
4157
|
return { name, status: 'success', detail: summarizeCliTestOutput(combined) || '连接成功' };
|
|
4052
4158
|
}
|
|
4053
|
-
|
|
4159
|
+
const candidateError = buildReadableAgentError(combined, result.error || lastError);
|
|
4160
|
+
const candidateScore = scoreCliErrorDetail(candidateError);
|
|
4161
|
+
if (candidateScore >= lastErrorScore) {
|
|
4162
|
+
lastError = candidateError;
|
|
4163
|
+
lastErrorScore = candidateScore;
|
|
4164
|
+
}
|
|
4054
4165
|
}
|
|
4055
4166
|
return { name, status: 'failed', detail: lastError };
|
|
4056
4167
|
}
|
|
@@ -4061,15 +4172,8 @@ function testClaudeCodeCliConnection() {
|
|
|
4061
4172
|
|
|
4062
4173
|
const config = readClaudeCodeCliConfig();
|
|
4063
4174
|
if (!config.configured) return { name: 'Claude Code CLI', status: 'skipped', detail: '未检测到 ~/.claude/settings.json' };
|
|
4064
|
-
if (config.
|
|
4065
|
-
return {
|
|
4066
|
-
name: 'Claude Code CLI',
|
|
4067
|
-
status: 'failed',
|
|
4068
|
-
detail: '检测到旧版 Claude Code 配置字段 ANTHROPIC_AUTH_TOKEN,请重新运行 yymaxapi 以改写为 ANTHROPIC_API_KEY'
|
|
4069
|
-
};
|
|
4070
|
-
}
|
|
4071
|
-
if (!config.settingsBaseUrl || !config.settingsApiKey) {
|
|
4072
|
-
return { name: 'Claude Code CLI', status: 'failed', detail: 'Claude Code 配置缺少 settings.json 中的 Base URL 或 API Key' };
|
|
4175
|
+
if (!config.settingsBaseUrl || !config.apiKey) {
|
|
4176
|
+
return { name: 'Claude Code CLI', status: 'failed', detail: 'Claude Code 配置缺少 settings.json 中的 Base URL 或鉴权字段' };
|
|
4073
4177
|
}
|
|
4074
4178
|
|
|
4075
4179
|
const env = {
|
|
@@ -4127,10 +4231,11 @@ function testCodexCliConnection() {
|
|
|
4127
4231
|
NODE_TLS_REJECT_UNAUTHORIZED: '0',
|
|
4128
4232
|
NODE_NO_WARNINGS: '1'
|
|
4129
4233
|
};
|
|
4234
|
+
const nullRedirect = getShellNullInputRedirect();
|
|
4130
4235
|
|
|
4131
4236
|
return runCliTestCandidates('Codex CLI', [
|
|
4132
|
-
`${shellQuote(cliBinary)} exec --skip-git-repo-check ${shellQuote('请只回复 OK')}`,
|
|
4133
|
-
`${shellQuote(cliBinary)} exec ${shellQuote('请只回复 OK')}`
|
|
4237
|
+
`${shellQuote(cliBinary)} exec --skip-git-repo-check ${shellQuote('请只回复 OK')}${nullRedirect}`,
|
|
4238
|
+
`${shellQuote(cliBinary)} exec ${shellQuote('请只回复 OK')}${nullRedirect}`
|
|
4134
4239
|
], env);
|
|
4135
4240
|
}
|
|
4136
4241
|
|
|
@@ -5186,7 +5291,11 @@ async function autoActivate(paths, args = {}) {
|
|
|
5186
5291
|
const codexApiConfig = API_CONFIG.codex;
|
|
5187
5292
|
const claudeProviderName = claudeApiConfig.providerName;
|
|
5188
5293
|
const codexProviderName = codexApiConfig.providerName;
|
|
5189
|
-
const selectedEndpoint =
|
|
5294
|
+
const selectedEndpoint = await resolveEndpointSelection(args, {
|
|
5295
|
+
speedIntro: '📡 开始测速云翼节点...\n',
|
|
5296
|
+
proceedMessage: '仍要写入默认节点配置吗?'
|
|
5297
|
+
});
|
|
5298
|
+
if (!selectedEndpoint) return;
|
|
5190
5299
|
|
|
5191
5300
|
// ---- API Key ----
|
|
5192
5301
|
const apiKeyEnvFallbacks = [
|
|
@@ -5443,7 +5552,8 @@ async function activateClaudeCode(paths, args = {}) {
|
|
|
5443
5552
|
console.log(chalk.gray(' API Key: 已设置'));
|
|
5444
5553
|
console.log(chalk.gray('\n 已写入:'));
|
|
5445
5554
|
console.log(chalk.gray(' • ~/.claude/settings.json'));
|
|
5446
|
-
console.log(chalk.gray(' • 关键字段: apiBaseUrl + model + env.
|
|
5555
|
+
console.log(chalk.gray(' • 关键字段: apiBaseUrl + model + env.ANTHROPIC_AUTH_TOKEN + env.ANTHROPIC_BASE_URL'));
|
|
5556
|
+
console.log(chalk.gray(' • Claude Code 走 Bearer Token 网关鉴权,避免落入官方登录 / 批准自定义 API Key 流程'));
|
|
5447
5557
|
console.log(chalk.gray(' • 已钉住 Claude alias 默认版本,避免第三方中转回落到官方旧别名'));
|
|
5448
5558
|
console.log(chalk.yellow('\n 提示: 如果 Claude Code 已在运行,重新打开一个会话即可读取新配置'));
|
|
5449
5559
|
|
|
@@ -13,10 +13,8 @@
|
|
|
13
13
|
```json5
|
|
14
14
|
{
|
|
15
15
|
"endpoints": [
|
|
16
|
-
{ "name": "
|
|
17
|
-
{ "name": "CF国外节点1", "url": "https://yunyi.cfd" }
|
|
18
|
-
{ "name": "CF国外节点2", "url": "https://cdn1.yunyi.cfd" },
|
|
19
|
-
{ "name": "CF国外节点3", "url": "https://cdn2.yunyi.cfd" }
|
|
16
|
+
{ "name": "默认主节点", "url": "https://yunyi.cfd" },
|
|
17
|
+
{ "name": "CF国外节点1", "url": "https://cdn2.yunyi.cfd" }
|
|
20
18
|
],
|
|
21
19
|
"fallbackEndpoints": [
|
|
22
20
|
{ "name": "备用节点1", "url": "http://47.99.42.193" },
|
|
@@ -77,7 +75,7 @@ npx yymaxapi@latest
|
|
|
77
75
|
```json
|
|
78
76
|
{
|
|
79
77
|
"provider": "anthropic",
|
|
80
|
-
"base_url": "https://yunyi.
|
|
78
|
+
"base_url": "https://yunyi.cfd/claude",
|
|
81
79
|
"api": "anthropic-messages",
|
|
82
80
|
"api_key": "<你的云翼 API Key>",
|
|
83
81
|
"model": {
|
|
@@ -92,7 +90,7 @@ npx yymaxapi@latest
|
|
|
92
90
|
```json
|
|
93
91
|
{
|
|
94
92
|
"provider": "openai",
|
|
95
|
-
"base_url": "https://yunyi.
|
|
93
|
+
"base_url": "https://yunyi.cfd/codex",
|
|
96
94
|
"api": "openai-completions",
|
|
97
95
|
"api_key": "<你的云翼 API Key>",
|
|
98
96
|
"model": {
|
|
@@ -104,6 +102,7 @@ npx yymaxapi@latest
|
|
|
104
102
|
|
|
105
103
|
**注意事项:**
|
|
106
104
|
- `base_url` 不要加 `/v1`,平台会自动拼接路径
|
|
105
|
+
- 如需临时切到其他云翼域名,可直接把域名部分替换掉;`yymaxapi` 也支持在预设/一键激活命令中传 `--endpoint-url https://你的域名` 或 `--base-url https://你的域名/claude`
|
|
107
106
|
- Claude 可用模型:`claude-sonnet-4-6`、`claude-opus-4-7`、`claude-haiku-4-5`
|
|
108
107
|
- GPT 可用模型:`gpt-5.4`
|
|
109
108
|
- 已验证环境:腾讯云 OpenCloudOS,OpenClaw `2026.2.3-1`
|
|
@@ -122,7 +121,7 @@ npx yymaxapi@latest
|
|
|
122
121
|
```json
|
|
123
122
|
{
|
|
124
123
|
"provider": "anthropic",
|
|
125
|
-
"base_url": "https://yunyi.
|
|
124
|
+
"base_url": "https://yunyi.cfd/claude",
|
|
126
125
|
"api": "anthropic-messages",
|
|
127
126
|
"api_key": "<你的云翼 API Key>",
|
|
128
127
|
"model": {
|
|
@@ -137,7 +136,7 @@ npx yymaxapi@latest
|
|
|
137
136
|
```json
|
|
138
137
|
{
|
|
139
138
|
"provider": "openai",
|
|
140
|
-
"base_url": "https://yunyi.
|
|
139
|
+
"base_url": "https://yunyi.cfd/codex",
|
|
141
140
|
"api": "openai-completions",
|
|
142
141
|
"api_key": "<你的云翼 API Key>",
|
|
143
142
|
"model": {
|
|
@@ -153,10 +152,10 @@ npx yymaxapi@latest
|
|
|
153
152
|
|
|
154
153
|
```json
|
|
155
154
|
{
|
|
156
|
-
"apiBaseUrl": "https://yunyi.
|
|
155
|
+
"apiBaseUrl": "https://yunyi.cfd/claude",
|
|
157
156
|
"env": {
|
|
158
157
|
"ANTHROPIC_API_KEY": "<你的云翼 API Key>",
|
|
159
|
-
"ANTHROPIC_BASE_URL": "https://yunyi.
|
|
158
|
+
"ANTHROPIC_BASE_URL": "https://yunyi.cfd/claude"
|
|
160
159
|
}
|
|
161
160
|
}
|
|
162
161
|
```
|