yymaxapi 1.0.84 → 1.0.86
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 +559 -26
- package/package.json +1 -1
package/bin/yymaxapi.js
CHANGED
|
@@ -115,7 +115,7 @@ const DEFAULT_API_CONFIG = {
|
|
|
115
115
|
"api": "anthropic-messages",
|
|
116
116
|
"contextWindow": 200000,
|
|
117
117
|
"maxTokens": 8192,
|
|
118
|
-
"providerName": "claude
|
|
118
|
+
"providerName": "yunyi-claude"
|
|
119
119
|
},
|
|
120
120
|
"codex": {
|
|
121
121
|
"urlSuffix": "/codex",
|
|
@@ -1617,6 +1617,27 @@ function renameProviderReferencesInConfig(config, oldName, newName) {
|
|
|
1617
1617
|
}
|
|
1618
1618
|
}
|
|
1619
1619
|
|
|
1620
|
+
if (Array.isArray(config?.agents?.list)) {
|
|
1621
|
+
for (const agent of config.agents.list) {
|
|
1622
|
+
if (!agent || typeof agent !== 'object' || !agent.model || typeof agent.model !== 'object') continue;
|
|
1623
|
+
if (typeof agent.model.primary === 'string' && agent.model.primary.startsWith(`${oldName}/`)) {
|
|
1624
|
+
agent.model.primary = `${newName}/${agent.model.primary.slice(oldName.length + 1)}`;
|
|
1625
|
+
changed = true;
|
|
1626
|
+
}
|
|
1627
|
+
if (Array.isArray(agent.model.fallbacks)) {
|
|
1628
|
+
const nextFallbacks = agent.model.fallbacks.map(modelKey => (
|
|
1629
|
+
typeof modelKey === 'string' && modelKey.startsWith(`${oldName}/`)
|
|
1630
|
+
? `${newName}/${modelKey.slice(oldName.length + 1)}`
|
|
1631
|
+
: modelKey
|
|
1632
|
+
));
|
|
1633
|
+
if (JSON.stringify(nextFallbacks) !== JSON.stringify(agent.model.fallbacks)) {
|
|
1634
|
+
agent.model.fallbacks = nextFallbacks;
|
|
1635
|
+
changed = true;
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1620
1641
|
return changed;
|
|
1621
1642
|
}
|
|
1622
1643
|
|
|
@@ -1920,6 +1941,442 @@ function ensureConfigStructure(config) {
|
|
|
1920
1941
|
return next;
|
|
1921
1942
|
}
|
|
1922
1943
|
|
|
1944
|
+
const YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER = 'yunyi-claude';
|
|
1945
|
+
const YYMAXAPI_OPENCLAW_GPT_PROVIDER = 'yunyi';
|
|
1946
|
+
const YYMAXAPI_OPENCLAW_MAIN_AGENT_ID = 'main';
|
|
1947
|
+
const YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID = 'yunyi-claude';
|
|
1948
|
+
const YYMAXAPI_OPENCLAW_GPT_AGENT_ID = 'yunyi-gpt';
|
|
1949
|
+
const YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY = `${YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER}/claude-sonnet-4-6`;
|
|
1950
|
+
const YYMAXAPI_OPENCLAW_CLAUDE_FALLBACK = `${YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER}/claude-opus-4-6`;
|
|
1951
|
+
const YYMAXAPI_OPENCLAW_GPT_PRIMARY = `${YYMAXAPI_OPENCLAW_GPT_PROVIDER}/gpt-5.4`;
|
|
1952
|
+
const YYMAXAPI_OPENCLAW_GPT_FALLBACK = `${YYMAXAPI_OPENCLAW_GPT_PROVIDER}/gpt-5.3-codex`;
|
|
1953
|
+
const YYMAXAPI_MANAGED_MAIN_NAMES = new Set(['Claude', 'claude', 'yunyi-claude', 'claude-yunyi']);
|
|
1954
|
+
const YYMAXAPI_MANAGED_GPT_NAMES = new Set(['GPT', 'Codex', 'gpt', 'yunyi-gpt', 'yunyi-codex']);
|
|
1955
|
+
|
|
1956
|
+
function isYymaxapiOpenClawBuild() {
|
|
1957
|
+
return isLikelyYunyiBaseUrl(ENDPOINTS[0]?.url || '')
|
|
1958
|
+
&& API_CONFIG?.claude?.api === 'anthropic-messages'
|
|
1959
|
+
&& API_CONFIG?.codex?.api === 'openai-completions';
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
function startsWithProviderAliases(modelKey, aliases) {
|
|
1963
|
+
if (typeof modelKey !== 'string') return false;
|
|
1964
|
+
for (const alias of aliases || []) {
|
|
1965
|
+
if (modelKey.startsWith(`${alias}/`)) return true;
|
|
1966
|
+
}
|
|
1967
|
+
return false;
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
function isManagedYunyiClaudeModelKey(modelKey) {
|
|
1971
|
+
return startsWithProviderAliases(modelKey, YUNYI_PROVIDER_ALIASES.claude);
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
function isManagedYunyiGptModelKey(modelKey) {
|
|
1975
|
+
return startsWithProviderAliases(modelKey, YUNYI_PROVIDER_ALIASES.codex);
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
function isManagedYunyiModelKey(modelKey) {
|
|
1979
|
+
return isManagedYunyiClaudeModelKey(modelKey) || isManagedYunyiGptModelKey(modelKey);
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
function stripManagedYunyiSuffix(baseUrl) {
|
|
1983
|
+
return String(baseUrl || '').trim().replace(/\/(?:claude|codex)\/?$/i, '');
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
function getManagedYunyiModelCatalog(type) {
|
|
1987
|
+
if (type === 'claude') {
|
|
1988
|
+
return [
|
|
1989
|
+
{ id: 'claude-sonnet-4-6', name: 'Claude Sonnet 4.6' },
|
|
1990
|
+
{ id: 'claude-opus-4-6', name: 'Claude Opus 4.6' }
|
|
1991
|
+
];
|
|
1992
|
+
}
|
|
1993
|
+
return [
|
|
1994
|
+
{ id: 'gpt-5.4', name: 'GPT 5.4' },
|
|
1995
|
+
{ id: 'gpt-5.3-codex', name: 'GPT 5.3 Codex' }
|
|
1996
|
+
];
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
function resolveManagedYunyiModelName(type, modelId, fallbackName) {
|
|
2000
|
+
const catalog = type === 'claude' ? CLAUDE_MODELS : CODEX_MODELS;
|
|
2001
|
+
return catalog.find(model => model.id === modelId)?.name || fallbackName;
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
function getManagedYunyiProviderModels(type) {
|
|
2005
|
+
const apiConfig = type === 'claude'
|
|
2006
|
+
? { contextWindow: API_CONFIG.claude?.contextWindow || 200000, maxTokens: API_CONFIG.claude?.maxTokens || 8192 }
|
|
2007
|
+
: { contextWindow: API_CONFIG.codex?.contextWindow || 1050000, maxTokens: API_CONFIG.codex?.maxTokens || 128000 };
|
|
2008
|
+
|
|
2009
|
+
return getManagedYunyiModelCatalog(type).map(model => ({
|
|
2010
|
+
id: model.id,
|
|
2011
|
+
name: resolveManagedYunyiModelName(type, model.id, model.name),
|
|
2012
|
+
contextWindow: apiConfig.contextWindow,
|
|
2013
|
+
maxTokens: apiConfig.maxTokens
|
|
2014
|
+
}));
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
function getAgentModelState(agent) {
|
|
2018
|
+
const model = agent && typeof agent === 'object' && agent.model && typeof agent.model === 'object'
|
|
2019
|
+
? agent.model
|
|
2020
|
+
: {};
|
|
2021
|
+
const primary = typeof model.primary === 'string' ? model.primary.trim() : '';
|
|
2022
|
+
const fallbacks = Array.isArray(model.fallbacks)
|
|
2023
|
+
? [...new Set(model.fallbacks.map(item => String(item || '').trim()).filter(Boolean))]
|
|
2024
|
+
: [];
|
|
2025
|
+
return { primary, fallbacks };
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
function findAgentById(config, agentId) {
|
|
2029
|
+
if (!Array.isArray(config?.agents?.list) || !agentId) return null;
|
|
2030
|
+
return config.agents.list.find(agent => agent && typeof agent === 'object' && agent.id === agentId) || null;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
function resolveOpenClawTestTarget(config, args = {}) {
|
|
2034
|
+
const requestedAgentId = String(args.agent || args['agent-id'] || '').trim();
|
|
2035
|
+
const requestedAgent = requestedAgentId ? findAgentById(config, requestedAgentId) : null;
|
|
2036
|
+
const defaultPrimary = String(config?.agents?.defaults?.model?.primary || '').trim();
|
|
2037
|
+
const defaultFallbacks = Array.isArray(config?.agents?.defaults?.model?.fallbacks)
|
|
2038
|
+
? [...config.agents.defaults.model.fallbacks]
|
|
2039
|
+
: [];
|
|
2040
|
+
|
|
2041
|
+
let primary = defaultPrimary;
|
|
2042
|
+
let fallbacks = defaultFallbacks;
|
|
2043
|
+
let agentId = null;
|
|
2044
|
+
let agentName = '';
|
|
2045
|
+
|
|
2046
|
+
if (requestedAgent) {
|
|
2047
|
+
const agentState = getAgentModelState(requestedAgent);
|
|
2048
|
+
if (agentState.primary) {
|
|
2049
|
+
primary = agentState.primary;
|
|
2050
|
+
fallbacks = agentState.fallbacks;
|
|
2051
|
+
agentId = requestedAgent.id;
|
|
2052
|
+
agentName = String(requestedAgent.name || '').trim();
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
const providerName = primary.includes('/') ? primary.split('/')[0] : '';
|
|
2057
|
+
const provider = providerName ? (config.models?.providers?.[providerName] || null) : null;
|
|
2058
|
+
const apiType = provider?.api || '';
|
|
2059
|
+
const typeLabel = apiType.startsWith('anthropic')
|
|
2060
|
+
? 'Claude'
|
|
2061
|
+
: (apiType.startsWith('openai') ? 'Codex' : '模型');
|
|
2062
|
+
|
|
2063
|
+
return {
|
|
2064
|
+
agentId,
|
|
2065
|
+
agentName,
|
|
2066
|
+
primary,
|
|
2067
|
+
fallbacks,
|
|
2068
|
+
providerName,
|
|
2069
|
+
provider,
|
|
2070
|
+
typeLabel,
|
|
2071
|
+
defaultPrimary
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
function hasManagedYunyiFootprint(config) {
|
|
2076
|
+
const providers = config?.models?.providers || {};
|
|
2077
|
+
if (Object.entries(providers).some(([name, provider]) => isYunyiProviderEntry(name, provider, 'claude') || isYunyiProviderEntry(name, provider, 'codex'))) {
|
|
2078
|
+
return true;
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
const defaultsModel = config?.agents?.defaults?.model || {};
|
|
2082
|
+
if (isManagedYunyiModelKey(defaultsModel.primary)) return true;
|
|
2083
|
+
if (Array.isArray(defaultsModel.fallbacks) && defaultsModel.fallbacks.some(isManagedYunyiModelKey)) return true;
|
|
2084
|
+
|
|
2085
|
+
if (Array.isArray(config?.agents?.list)) {
|
|
2086
|
+
return config.agents.list.some(agent => {
|
|
2087
|
+
if (!agent || typeof agent !== 'object') return false;
|
|
2088
|
+
const name = String(agent.name || '').trim();
|
|
2089
|
+
const { primary, fallbacks } = getAgentModelState(agent);
|
|
2090
|
+
if (agent.id === YYMAXAPI_OPENCLAW_GPT_AGENT_ID || agent.id === YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID) {
|
|
2091
|
+
return true;
|
|
2092
|
+
}
|
|
2093
|
+
if (agent.id === YYMAXAPI_OPENCLAW_MAIN_AGENT_ID && YYMAXAPI_MANAGED_MAIN_NAMES.has(name)) {
|
|
2094
|
+
return true;
|
|
2095
|
+
}
|
|
2096
|
+
return isManagedYunyiModelKey(primary) || fallbacks.some(isManagedYunyiModelKey);
|
|
2097
|
+
});
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
return false;
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
function inferManagedYunyiEndpointUrl(config, explicitEndpointUrl = '') {
|
|
2104
|
+
const direct = stripManagedYunyiSuffix(explicitEndpointUrl);
|
|
2105
|
+
if (direct && isLikelyYunyiBaseUrl(direct)) return direct;
|
|
2106
|
+
|
|
2107
|
+
const providers = config?.models?.providers || {};
|
|
2108
|
+
for (const [name, provider] of Object.entries(providers)) {
|
|
2109
|
+
if (!isYunyiProviderEntry(name, provider, 'claude') && !isYunyiProviderEntry(name, provider, 'codex')) continue;
|
|
2110
|
+
const candidate = stripManagedYunyiSuffix(provider?.baseUrl || provider?.base_url || '');
|
|
2111
|
+
if (candidate && isLikelyYunyiBaseUrl(candidate)) return candidate;
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
return ENDPOINTS.find(item => isLikelyYunyiBaseUrl(item.url))?.url || ENDPOINTS[0]?.url || '';
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
function inferManagedYunyiApiKey(config, explicitApiKey = '') {
|
|
2118
|
+
const direct = String(explicitApiKey || '').trim();
|
|
2119
|
+
if (direct) return direct;
|
|
2120
|
+
|
|
2121
|
+
const providers = config?.models?.providers || {};
|
|
2122
|
+
for (const [name, provider] of Object.entries(providers)) {
|
|
2123
|
+
if (!isYunyiProviderEntry(name, provider, 'claude') && !isYunyiProviderEntry(name, provider, 'codex')) continue;
|
|
2124
|
+
const apiKey = String(provider?.apiKey || '').trim();
|
|
2125
|
+
if (apiKey) return apiKey;
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
return '';
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
function buildManagedYunyiProviderConfig(type, endpointUrl, apiKey, existingProvider = {}) {
|
|
2132
|
+
const providerName = type === 'claude' ? YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER : YYMAXAPI_OPENCLAW_GPT_PROVIDER;
|
|
2133
|
+
const nextApiKey = String(apiKey || existingProvider.apiKey || '').trim();
|
|
2134
|
+
const next = {
|
|
2135
|
+
...existingProvider,
|
|
2136
|
+
baseUrl: buildFullUrl(endpointUrl, type),
|
|
2137
|
+
auth: DEFAULT_AUTH_MODE,
|
|
2138
|
+
api: type === 'claude' ? 'anthropic-messages' : 'openai-completions',
|
|
2139
|
+
headers: {},
|
|
2140
|
+
authHeader: type !== 'claude',
|
|
2141
|
+
models: getManagedYunyiProviderModels(type)
|
|
2142
|
+
};
|
|
2143
|
+
|
|
2144
|
+
if (nextApiKey) next.apiKey = nextApiKey;
|
|
2145
|
+
if (!next.auth) next.auth = DEFAULT_AUTH_MODE;
|
|
2146
|
+
if (!next.baseUrl) next.baseUrl = buildFullUrl(endpointUrl, type);
|
|
2147
|
+
if (!next.api) next.api = type === 'claude' ? 'anthropic-messages' : 'openai-completions';
|
|
2148
|
+
if (!Array.isArray(next.models)) next.models = getManagedYunyiProviderModels(type);
|
|
2149
|
+
if (!providerName) return existingProvider;
|
|
2150
|
+
return next;
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
function buildGatewayAgentSubcommand({ sessionId, agentId, message = '请回复你的模型名称' }) {
|
|
2154
|
+
const agentArg = agentId ? ` --agent ${shellQuote(agentId)}` : '';
|
|
2155
|
+
return `agent${agentArg} --session-id ${shellQuote(sessionId)} --message ${shellQuote(message)} --json --timeout 120`;
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
function buildGatewayAgentCliCommand({ cliBinary, nodeInfo, useNode, sessionId, agentId, message = '请回复你的模型名称' }) {
|
|
2159
|
+
const subcommand = buildGatewayAgentSubcommand({ sessionId, agentId, message });
|
|
2160
|
+
return useNode
|
|
2161
|
+
? `${shellQuote(nodeInfo.path)} ${shellQuote(cliBinary)} ${subcommand}`
|
|
2162
|
+
: `${shellQuote(cliBinary)} ${subcommand}`;
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
function ensureAgentList(config) {
|
|
2166
|
+
if (!config.agents) config.agents = {};
|
|
2167
|
+
if (!Array.isArray(config.agents.list)) config.agents.list = [];
|
|
2168
|
+
return config.agents.list;
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
function findAgentIndex(agentList, agentId) {
|
|
2172
|
+
return agentList.findIndex(agent => agent && typeof agent === 'object' && agent.id === agentId);
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
function mergeManagedAgent(existingAgent, nextAgent) {
|
|
2176
|
+
const existing = existingAgent && typeof existingAgent === 'object' ? existingAgent : {};
|
|
2177
|
+
return {
|
|
2178
|
+
...existing,
|
|
2179
|
+
...nextAgent,
|
|
2180
|
+
model: {
|
|
2181
|
+
...(existing.model && typeof existing.model === 'object' ? existing.model : {}),
|
|
2182
|
+
...(nextAgent.model || {})
|
|
2183
|
+
}
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
function isManagedMainAgent(agent) {
|
|
2188
|
+
if (!agent || typeof agent !== 'object') return true;
|
|
2189
|
+
const name = String(agent.name || '').trim();
|
|
2190
|
+
const { primary, fallbacks } = getAgentModelState(agent);
|
|
2191
|
+
if (!primary) return true;
|
|
2192
|
+
if (YYMAXAPI_MANAGED_MAIN_NAMES.has(name)) return true;
|
|
2193
|
+
if (isManagedYunyiClaudeModelKey(primary) || isManagedYunyiGptModelKey(primary)) return true;
|
|
2194
|
+
return fallbacks.some(modelKey => isManagedYunyiClaudeModelKey(modelKey) || isManagedYunyiGptModelKey(modelKey));
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
function isManagedClaudeSideAgent(agent) {
|
|
2198
|
+
if (!agent || typeof agent !== 'object') return true;
|
|
2199
|
+
const name = String(agent.name || '').trim();
|
|
2200
|
+
const { primary, fallbacks } = getAgentModelState(agent);
|
|
2201
|
+
if (!primary) return true;
|
|
2202
|
+
if (YYMAXAPI_MANAGED_MAIN_NAMES.has(name)) return true;
|
|
2203
|
+
if (isManagedYunyiClaudeModelKey(primary)) return true;
|
|
2204
|
+
return fallbacks.some(isManagedYunyiClaudeModelKey);
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
function isManagedGptAgent(agent) {
|
|
2208
|
+
if (!agent || typeof agent !== 'object') return true;
|
|
2209
|
+
const name = String(agent.name || '').trim();
|
|
2210
|
+
const { primary, fallbacks } = getAgentModelState(agent);
|
|
2211
|
+
if (!primary) return true;
|
|
2212
|
+
if (YYMAXAPI_MANAGED_GPT_NAMES.has(name)) return true;
|
|
2213
|
+
if (isManagedYunyiGptModelKey(primary)) return true;
|
|
2214
|
+
return fallbacks.some(isManagedYunyiGptModelKey);
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
function upsertManagedAgent(agentList, nextAgent, shouldManage) {
|
|
2218
|
+
const index = findAgentIndex(agentList, nextAgent.id);
|
|
2219
|
+
if (index === -1) {
|
|
2220
|
+
agentList.push(nextAgent);
|
|
2221
|
+
return { changed: true, managed: true, created: true };
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
const currentAgent = agentList[index];
|
|
2225
|
+
if (!shouldManage(currentAgent)) {
|
|
2226
|
+
return { changed: false, managed: false, preserved: true };
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
const mergedAgent = mergeManagedAgent(currentAgent, nextAgent);
|
|
2230
|
+
if (JSON.stringify(mergedAgent) === JSON.stringify(currentAgent)) {
|
|
2231
|
+
return { changed: false, managed: true };
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
agentList[index] = mergedAgent;
|
|
2235
|
+
return { changed: true, managed: true };
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
function removeManagedAgent(agentList, agentId, shouldManage) {
|
|
2239
|
+
const index = findAgentIndex(agentList, agentId);
|
|
2240
|
+
if (index === -1) return false;
|
|
2241
|
+
if (!shouldManage(agentList[index])) return false;
|
|
2242
|
+
agentList.splice(index, 1);
|
|
2243
|
+
return true;
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
function shouldManageYunyiDefaults(config) {
|
|
2247
|
+
const modelState = config?.agents?.defaults?.model || {};
|
|
2248
|
+
const primary = typeof modelState.primary === 'string' ? modelState.primary.trim() : '';
|
|
2249
|
+
const fallbacks = Array.isArray(modelState.fallbacks) ? modelState.fallbacks : [];
|
|
2250
|
+
if (!primary) return true;
|
|
2251
|
+
if (isManagedYunyiModelKey(primary)) return true;
|
|
2252
|
+
return fallbacks.some(isManagedYunyiModelKey);
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
function applyManagedYunyiOpenClawLayout(config, options = {}) {
|
|
2256
|
+
if (!isYymaxapiOpenClawBuild()) {
|
|
2257
|
+
return { changed: false, applied: false, claudeAgentId: null, preservedMain: false };
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
if (!options.force && !hasManagedYunyiFootprint(config)) {
|
|
2261
|
+
return { changed: false, applied: false, claudeAgentId: null, preservedMain: false };
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
ensureConfigStructure(config);
|
|
2265
|
+
|
|
2266
|
+
const endpointUrl = inferManagedYunyiEndpointUrl(config, options.endpointUrl);
|
|
2267
|
+
const apiKey = inferManagedYunyiApiKey(config, options.apiKey);
|
|
2268
|
+
if (!endpointUrl) {
|
|
2269
|
+
return { changed: false, applied: false, claudeAgentId: null, preservedMain: false };
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
let changed = false;
|
|
2273
|
+
const providers = config.models.providers;
|
|
2274
|
+
|
|
2275
|
+
const nextClaudeProvider = buildManagedYunyiProviderConfig('claude', endpointUrl, apiKey, providers[YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER] || {});
|
|
2276
|
+
if (JSON.stringify(nextClaudeProvider) !== JSON.stringify(providers[YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER] || {})) {
|
|
2277
|
+
providers[YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER] = nextClaudeProvider;
|
|
2278
|
+
changed = true;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
const nextGptProvider = buildManagedYunyiProviderConfig('codex', endpointUrl, apiKey, providers[YYMAXAPI_OPENCLAW_GPT_PROVIDER] || {});
|
|
2282
|
+
if (JSON.stringify(nextGptProvider) !== JSON.stringify(providers[YYMAXAPI_OPENCLAW_GPT_PROVIDER] || {})) {
|
|
2283
|
+
providers[YYMAXAPI_OPENCLAW_GPT_PROVIDER] = nextGptProvider;
|
|
2284
|
+
changed = true;
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
const nextClaudeAuthProfile = { provider: YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER, mode: 'api_key' };
|
|
2288
|
+
if (JSON.stringify(config.auth.profiles[`${YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER}:default`] || {}) !== JSON.stringify(nextClaudeAuthProfile)) {
|
|
2289
|
+
config.auth.profiles[`${YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER}:default`] = nextClaudeAuthProfile;
|
|
2290
|
+
changed = true;
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
const nextGptAuthProfile = { provider: YYMAXAPI_OPENCLAW_GPT_PROVIDER, mode: 'api_key' };
|
|
2294
|
+
if (JSON.stringify(config.auth.profiles[`${YYMAXAPI_OPENCLAW_GPT_PROVIDER}:default`] || {}) !== JSON.stringify(nextGptAuthProfile)) {
|
|
2295
|
+
config.auth.profiles[`${YYMAXAPI_OPENCLAW_GPT_PROVIDER}:default`] = nextGptAuthProfile;
|
|
2296
|
+
changed = true;
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
const defaultsModels = config.agents.defaults.models;
|
|
2300
|
+
const managedModelAliases = {
|
|
2301
|
+
[YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY]: YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER,
|
|
2302
|
+
[YYMAXAPI_OPENCLAW_CLAUDE_FALLBACK]: YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER,
|
|
2303
|
+
[YYMAXAPI_OPENCLAW_GPT_PRIMARY]: YYMAXAPI_OPENCLAW_GPT_PROVIDER,
|
|
2304
|
+
[YYMAXAPI_OPENCLAW_GPT_FALLBACK]: YYMAXAPI_OPENCLAW_GPT_PROVIDER
|
|
2305
|
+
};
|
|
2306
|
+
for (const [modelKey, alias] of Object.entries(managedModelAliases)) {
|
|
2307
|
+
const nextEntry = { alias };
|
|
2308
|
+
if (JSON.stringify(defaultsModels[modelKey] || {}) !== JSON.stringify(nextEntry)) {
|
|
2309
|
+
defaultsModels[modelKey] = nextEntry;
|
|
2310
|
+
changed = true;
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
const agentList = ensureAgentList(config);
|
|
2315
|
+
const mainAgentResult = upsertManagedAgent(agentList, {
|
|
2316
|
+
id: YYMAXAPI_OPENCLAW_MAIN_AGENT_ID,
|
|
2317
|
+
default: true,
|
|
2318
|
+
name: 'yunyi-claude',
|
|
2319
|
+
model: {
|
|
2320
|
+
primary: YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY,
|
|
2321
|
+
fallbacks: [YYMAXAPI_OPENCLAW_CLAUDE_FALLBACK]
|
|
2322
|
+
}
|
|
2323
|
+
}, isManagedMainAgent);
|
|
2324
|
+
if (mainAgentResult.changed) changed = true;
|
|
2325
|
+
|
|
2326
|
+
let claudeAgentId = YYMAXAPI_OPENCLAW_MAIN_AGENT_ID;
|
|
2327
|
+
let preservedMain = !mainAgentResult.managed;
|
|
2328
|
+
if (!mainAgentResult.managed) {
|
|
2329
|
+
const fallbackClaudeAgentResult = upsertManagedAgent(agentList, {
|
|
2330
|
+
id: YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID,
|
|
2331
|
+
default: false,
|
|
2332
|
+
name: 'yunyi-claude',
|
|
2333
|
+
model: {
|
|
2334
|
+
primary: YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY,
|
|
2335
|
+
fallbacks: [YYMAXAPI_OPENCLAW_CLAUDE_FALLBACK]
|
|
2336
|
+
}
|
|
2337
|
+
}, isManagedClaudeSideAgent);
|
|
2338
|
+
if (fallbackClaudeAgentResult.changed) changed = true;
|
|
2339
|
+
claudeAgentId = fallbackClaudeAgentResult.managed ? YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID : null;
|
|
2340
|
+
} else if (removeManagedAgent(agentList, YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID, isManagedClaudeSideAgent)) {
|
|
2341
|
+
changed = true;
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
const gptAgentResult = upsertManagedAgent(agentList, {
|
|
2345
|
+
id: YYMAXAPI_OPENCLAW_GPT_AGENT_ID,
|
|
2346
|
+
default: false,
|
|
2347
|
+
name: 'yunyi-gpt',
|
|
2348
|
+
model: {
|
|
2349
|
+
primary: YYMAXAPI_OPENCLAW_GPT_PRIMARY,
|
|
2350
|
+
fallbacks: [YYMAXAPI_OPENCLAW_GPT_FALLBACK]
|
|
2351
|
+
}
|
|
2352
|
+
}, isManagedGptAgent);
|
|
2353
|
+
if (gptAgentResult.changed) changed = true;
|
|
2354
|
+
|
|
2355
|
+
if (shouldManageYunyiDefaults(config)) {
|
|
2356
|
+
const nextDefaultsModel = {
|
|
2357
|
+
primary: YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY,
|
|
2358
|
+
fallbacks: [YYMAXAPI_OPENCLAW_GPT_PRIMARY]
|
|
2359
|
+
};
|
|
2360
|
+
if (JSON.stringify(config.agents.defaults.model) !== JSON.stringify(nextDefaultsModel)) {
|
|
2361
|
+
config.agents.defaults.model = nextDefaultsModel;
|
|
2362
|
+
changed = true;
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
return { changed, applied: true, claudeAgentId, preservedMain };
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
function printYunyiOpenClawSwitchHint(result = {}) {
|
|
2370
|
+
if (!result?.applied) return;
|
|
2371
|
+
if (result.preservedMain && result.claudeAgentId === YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID) {
|
|
2372
|
+
console.log(chalk.gray(' 已保留你现有的 main;Yunyi Claude 入口为 yunyi-claude'));
|
|
2373
|
+
} else {
|
|
2374
|
+
console.log(chalk.gray(' 默认主入口是 yunyi-claude'));
|
|
2375
|
+
}
|
|
2376
|
+
console.log(chalk.gray(' GPT 入口是 yunyi-gpt'));
|
|
2377
|
+
console.log(chalk.yellow(' 提示: 当前 OpenClaw Web 顶部模型下拉跨 provider 切换可能报错,建议通过左侧会话/agent 切换 Claude 和 GPT'));
|
|
2378
|
+
}
|
|
2379
|
+
|
|
1923
2380
|
function pruneProvidersByPrefix(config, prefixBase, keepProviders = []) {
|
|
1924
2381
|
if (!config?.models?.providers) return [];
|
|
1925
2382
|
const removed = [];
|
|
@@ -2260,12 +2717,14 @@ function isNodeShebang(filePath) {
|
|
|
2260
2717
|
return true;
|
|
2261
2718
|
}
|
|
2262
2719
|
const fd = fs.openSync(filePath, 'r');
|
|
2263
|
-
const buffer = Buffer.alloc(
|
|
2720
|
+
const buffer = Buffer.alloc(256);
|
|
2264
2721
|
const bytes = fs.readSync(fd, buffer, 0, buffer.length, 0);
|
|
2265
2722
|
fs.closeSync(fd);
|
|
2266
2723
|
if (bytes <= 0) return false;
|
|
2267
2724
|
const header = buffer.toString('utf8', 0, bytes);
|
|
2268
|
-
|
|
2725
|
+
const firstLine = header.split(/\r?\n/, 1)[0] || '';
|
|
2726
|
+
if (!firstLine.startsWith('#!')) return false;
|
|
2727
|
+
return /\bnode(?:js)?\b/.test(firstLine);
|
|
2269
2728
|
} catch {
|
|
2270
2729
|
return false;
|
|
2271
2730
|
}
|
|
@@ -2747,6 +3206,8 @@ function autoFixConfig(paths) {
|
|
|
2747
3206
|
sanitizeDefaultModelSelection(config);
|
|
2748
3207
|
const repairResult = applyConfigRepairsWithSync(config, paths);
|
|
2749
3208
|
if (repairResult.changed) changed = true;
|
|
3209
|
+
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config);
|
|
3210
|
+
if (yunyiLayoutResult.changed) changed = true;
|
|
2750
3211
|
const codexProvider = codexProviderName && config.models.providers?.[codexProviderName];
|
|
2751
3212
|
const nextModelJson = JSON.stringify(config.agents?.defaults?.model ?? null);
|
|
2752
3213
|
if (originalModelJson !== nextModelJson) {
|
|
@@ -2766,8 +3227,12 @@ function autoFixConfig(paths) {
|
|
|
2766
3227
|
}
|
|
2767
3228
|
|
|
2768
3229
|
if (changed) {
|
|
2769
|
-
|
|
2770
|
-
|
|
3230
|
+
writeConfigWithSync(paths, config);
|
|
3231
|
+
const managedClaudeKey = String(config.models.providers?.[YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER]?.apiKey || '').trim();
|
|
3232
|
+
const managedGptKey = String(config.models.providers?.[YYMAXAPI_OPENCLAW_GPT_PROVIDER]?.apiKey || '').trim();
|
|
3233
|
+
if (managedClaudeKey) updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER, managedClaudeKey);
|
|
3234
|
+
if (managedGptKey) updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_GPT_PROVIDER, managedGptKey);
|
|
3235
|
+
console.log(chalk.green('✓ 已自动修复配置(Yunyi providers / agents / API 协议)'));
|
|
2771
3236
|
}
|
|
2772
3237
|
} catch { /* ignore */ }
|
|
2773
3238
|
}
|
|
@@ -3468,12 +3933,21 @@ async function presetClaude(paths, args = {}) {
|
|
|
3468
3933
|
config.agents.defaults.model.primary = modelKey;
|
|
3469
3934
|
config.agents.defaults.model.fallbacks = [];
|
|
3470
3935
|
}
|
|
3936
|
+
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
|
|
3937
|
+
force: true,
|
|
3938
|
+
endpointUrl: selectedEndpoint.url,
|
|
3939
|
+
apiKey
|
|
3940
|
+
});
|
|
3471
3941
|
|
|
3472
3942
|
const writeSpinner = ora({ text: '正在写入配置...', spinner: 'dots' }).start();
|
|
3473
3943
|
createTimestampedBackup(paths.openclawConfig, paths.configDir, 'claude');
|
|
3474
3944
|
ensureGatewaySettings(config);
|
|
3475
3945
|
writeConfigWithSync(paths, config);
|
|
3476
3946
|
updateAuthProfilesWithSync(paths, providerName, apiKey);
|
|
3947
|
+
if (yunyiLayoutResult.applied) {
|
|
3948
|
+
updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER, apiKey);
|
|
3949
|
+
updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_GPT_PROVIDER, apiKey);
|
|
3950
|
+
}
|
|
3477
3951
|
const extSynced = syncExternalTools('claude', baseUrl, apiKey, { claudeModelId: modelId, opencodeDefaultModelKey: `yunyi-claude/${modelId}` });
|
|
3478
3952
|
writeSpinner.succeed('配置写入完成');
|
|
3479
3953
|
|
|
@@ -3486,6 +3960,7 @@ async function presetClaude(paths, args = {}) {
|
|
|
3486
3960
|
if (repairResult.renamedProviders.length > 0) {
|
|
3487
3961
|
console.log(chalk.gray(` 已保留并修复冲突 provider: ${repairResult.renamedProviders.map(item => `${item.from}→${item.to}`).join(', ')}`));
|
|
3488
3962
|
}
|
|
3963
|
+
printYunyiOpenClawSwitchHint(yunyiLayoutResult);
|
|
3489
3964
|
|
|
3490
3965
|
const shouldTestGateway = args.test !== undefined
|
|
3491
3966
|
? !['false', '0', 'no'].includes(String(args.test).toLowerCase())
|
|
@@ -3644,6 +4119,11 @@ async function presetCodex(paths, args = {}) {
|
|
|
3644
4119
|
config.agents.defaults.model.primary = modelKey;
|
|
3645
4120
|
config.agents.defaults.model.fallbacks = [];
|
|
3646
4121
|
}
|
|
4122
|
+
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
|
|
4123
|
+
force: true,
|
|
4124
|
+
endpointUrl: selectedEndpoint.url,
|
|
4125
|
+
apiKey
|
|
4126
|
+
});
|
|
3647
4127
|
|
|
3648
4128
|
const writeSpinner2 = ora({ text: '正在写入配置...', spinner: 'dots' }).start();
|
|
3649
4129
|
createTimestampedBackup(paths.openclawConfig, paths.configDir, 'codex');
|
|
@@ -3651,6 +4131,10 @@ async function presetCodex(paths, args = {}) {
|
|
|
3651
4131
|
cleanupConflictingEnvVars(config, baseUrl, apiKey);
|
|
3652
4132
|
writeConfigWithSync(paths, config);
|
|
3653
4133
|
updateAuthProfilesWithSync(paths, providerName, apiKey);
|
|
4134
|
+
if (yunyiLayoutResult.applied) {
|
|
4135
|
+
updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER, apiKey);
|
|
4136
|
+
updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_GPT_PROVIDER, apiKey);
|
|
4137
|
+
}
|
|
3654
4138
|
const extSynced2 = syncExternalTools('codex', baseUrl, apiKey, { modelId });
|
|
3655
4139
|
writeSpinner2.succeed('配置写入完成');
|
|
3656
4140
|
|
|
@@ -3663,6 +4147,7 @@ async function presetCodex(paths, args = {}) {
|
|
|
3663
4147
|
if (repairResult.renamedProviders.length > 0) {
|
|
3664
4148
|
console.log(chalk.gray(` 已保留并修复冲突 provider: ${repairResult.renamedProviders.map(item => `${item.from}→${item.to}`).join(', ')}`));
|
|
3665
4149
|
}
|
|
4150
|
+
printYunyiOpenClawSwitchHint(yunyiLayoutResult);
|
|
3666
4151
|
|
|
3667
4152
|
const shouldTestGateway = args.test !== undefined
|
|
3668
4153
|
? !['false', '0', 'no'].includes(String(args.test).toLowerCase())
|
|
@@ -3831,6 +4316,11 @@ async function autoActivate(paths, args = {}) {
|
|
|
3831
4316
|
const fallbackModelKey = isClaudePrimary ? codexModelKey : claudeModelKey;
|
|
3832
4317
|
config.agents.defaults.model.primary = primaryModelKey;
|
|
3833
4318
|
config.agents.defaults.model.fallbacks = [fallbackModelKey];
|
|
4319
|
+
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
|
|
4320
|
+
force: true,
|
|
4321
|
+
endpointUrl: selectedEndpoint.url,
|
|
4322
|
+
apiKey
|
|
4323
|
+
});
|
|
3834
4324
|
|
|
3835
4325
|
// ---- 写入配置 ----
|
|
3836
4326
|
const writeSpinner = ora({ text: '正在写入配置...', spinner: 'dots' }).start();
|
|
@@ -3850,12 +4340,13 @@ async function autoActivate(paths, args = {}) {
|
|
|
3850
4340
|
const primaryLabel = isClaudePrimary ? 'Claude' : 'GPT';
|
|
3851
4341
|
const selectedModel = isClaudePrimary ? claudeModel : codexModel;
|
|
3852
4342
|
console.log(chalk.green('\n✅ 配置完成!'));
|
|
3853
|
-
console.log(chalk.cyan(`
|
|
4343
|
+
console.log(chalk.cyan(` 外部工具默认: ${selectedModel.name} (${primaryLabel})`));
|
|
3854
4344
|
console.log(chalk.gray(` 节点: ${selectedEndpoint.url} (${selectedEndpoint.name})`));
|
|
3855
4345
|
console.log(chalk.gray(' API Key: 已设置'));
|
|
3856
4346
|
if (extSynced.length > 0) console.log(chalk.gray(` 同步: ${extSynced.join(', ')}`));
|
|
3857
4347
|
console.log(chalk.gray(' 若遇 certificate 报错,请新开终端或执行 source ~/.zshrc 后重试(已放宽 TLS 校验)'));
|
|
3858
4348
|
console.log(chalk.gray(` 使用 OpenCode 时可在界面中切换 ${getOpencodeSwitchHint()};Codex 仅支持 GPT`));
|
|
4349
|
+
printYunyiOpenClawSwitchHint(yunyiLayoutResult);
|
|
3859
4350
|
|
|
3860
4351
|
const gwPort = config.gateway?.port || 18789;
|
|
3861
4352
|
const gwToken = config.gateway?.auth?.token;
|
|
@@ -3876,7 +4367,10 @@ async function autoActivate(paths, args = {}) {
|
|
|
3876
4367
|
}]);
|
|
3877
4368
|
|
|
3878
4369
|
if (nextAction === 'test') {
|
|
3879
|
-
|
|
4370
|
+
const selectedOpenClawAgentId = isClaudePrimary
|
|
4371
|
+
? (yunyiLayoutResult.claudeAgentId || YYMAXAPI_OPENCLAW_MAIN_AGENT_ID)
|
|
4372
|
+
: YYMAXAPI_OPENCLAW_GPT_AGENT_ID;
|
|
4373
|
+
await testConnection(paths, { ...args, agent: selectedOpenClawAgentId });
|
|
3880
4374
|
} else {
|
|
3881
4375
|
console.log(chalk.cyan('👋 再见!\n'));
|
|
3882
4376
|
process.exit(0);
|
|
@@ -4276,6 +4770,11 @@ async function yycodeQuickSetup(paths) {
|
|
|
4276
4770
|
// 默认主力: Codex, 备用: Claude
|
|
4277
4771
|
config.agents.defaults.model.primary = codexModelKey;
|
|
4278
4772
|
config.agents.defaults.model.fallbacks = [claudeModelKey];
|
|
4773
|
+
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
|
|
4774
|
+
force: true,
|
|
4775
|
+
endpointUrl: selectedEndpoint.url,
|
|
4776
|
+
apiKey
|
|
4777
|
+
});
|
|
4279
4778
|
|
|
4280
4779
|
// ---- 写入 ----
|
|
4281
4780
|
const writeSpinner = ora({ text: '正在写入配置...', spinner: 'dots' }).start();
|
|
@@ -4293,10 +4792,11 @@ async function yycodeQuickSetup(paths) {
|
|
|
4293
4792
|
console.log(chalk.green('\n✅ 配置完成!'));
|
|
4294
4793
|
console.log(chalk.cyan(` Claude Code: ${claudeBaseUrl}`));
|
|
4295
4794
|
console.log(chalk.gray(` 模型: ${claudeModel.name}`));
|
|
4296
|
-
console.log(chalk.cyan(` Codex
|
|
4795
|
+
console.log(chalk.cyan(` Codex CLI: ${codexBaseUrl}`));
|
|
4297
4796
|
console.log(chalk.gray(` 模型: ${codexModel.name}`));
|
|
4298
4797
|
console.log(chalk.gray(' API Key: 已设置'));
|
|
4299
4798
|
console.log(chalk.gray(' 同步: Claude Code settings, Opencode config, Codex CLI config'));
|
|
4799
|
+
printYunyiOpenClawSwitchHint(yunyiLayoutResult);
|
|
4300
4800
|
console.log('');
|
|
4301
4801
|
}
|
|
4302
4802
|
|
|
@@ -4428,7 +4928,9 @@ async function main() {
|
|
|
4428
4928
|
// 获取当前配置状态摘要
|
|
4429
4929
|
function getConfigStatusLine(paths) {
|
|
4430
4930
|
try {
|
|
4431
|
-
const config = readConfig(paths.openclawConfig);
|
|
4931
|
+
const config = ensureConfigStructure(readConfig(paths.openclawConfig) || {});
|
|
4932
|
+
applyConfigRepairsWithSync(config, paths);
|
|
4933
|
+
applyManagedYunyiOpenClawLayout(config);
|
|
4432
4934
|
if (!config?.models?.providers) return null;
|
|
4433
4935
|
|
|
4434
4936
|
const providers = Object.keys(config.models.providers);
|
|
@@ -4436,6 +4938,7 @@ function getConfigStatusLine(paths) {
|
|
|
4436
4938
|
const primaryProvider = primary.split('/')[0] || '';
|
|
4437
4939
|
|
|
4438
4940
|
const KNOWN_PROVIDERS = {
|
|
4941
|
+
'yunyi-claude': 'Claude(包月)',
|
|
4439
4942
|
'claude-yunyi': 'Claude(包月)',
|
|
4440
4943
|
'yunyi': 'Codex(包月)',
|
|
4441
4944
|
'heibai': 'MAXAPI(按量)',
|
|
@@ -4555,14 +5058,24 @@ async function selectNode(paths, type) {
|
|
|
4555
5058
|
config.agents.defaults.models[modelKey] = { alias: apiConfig.providerName };
|
|
4556
5059
|
config.agents.defaults.model.primary = modelKey;
|
|
4557
5060
|
config.agents.defaults.model.fallbacks = (config.agents.defaults.model.fallbacks || []).filter(k => k !== modelKey);
|
|
5061
|
+
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
|
|
5062
|
+
force: true,
|
|
5063
|
+
endpointUrl: selectedEndpoint.url,
|
|
5064
|
+
apiKey: oldApiKey || ''
|
|
5065
|
+
});
|
|
4558
5066
|
|
|
4559
5067
|
ensureGatewaySettings(config);
|
|
4560
5068
|
writeConfigWithSync(paths, config);
|
|
5069
|
+
const managedClaudeKey = String(config.models.providers?.[YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER]?.apiKey || '').trim();
|
|
5070
|
+
const managedGptKey = String(config.models.providers?.[YYMAXAPI_OPENCLAW_GPT_PROVIDER]?.apiKey || '').trim();
|
|
5071
|
+
if (managedClaudeKey) updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER, managedClaudeKey);
|
|
5072
|
+
if (managedGptKey) updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_GPT_PROVIDER, managedGptKey);
|
|
4561
5073
|
|
|
4562
5074
|
console.log(chalk.green(`\n✅ ${typeLabel} 节点配置完成!`));
|
|
4563
5075
|
console.log(chalk.cyan(` 节点: ${selectedEndpoint.name} (${selectedEndpoint.url})`));
|
|
4564
5076
|
console.log(chalk.gray(` 模型: ${modelConfig.name} (主模型)`));
|
|
4565
5077
|
console.log(chalk.gray(` API Key: ${oldApiKey ? '已设置' : '未设置'}`));
|
|
5078
|
+
printYunyiOpenClawSwitchHint(yunyiLayoutResult);
|
|
4566
5079
|
}
|
|
4567
5080
|
|
|
4568
5081
|
// ============ 激活 (Claude/Codex) ============
|
|
@@ -4593,6 +5106,11 @@ async function activate(paths, type) {
|
|
|
4593
5106
|
const modelKey = `${apiConfig.providerName}/${modelConfig.id}`;
|
|
4594
5107
|
config.agents.defaults.model.primary = modelKey;
|
|
4595
5108
|
config.agents.defaults.model.fallbacks = [];
|
|
5109
|
+
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
|
|
5110
|
+
force: true,
|
|
5111
|
+
endpointUrl: stripManagedYunyiSuffix(provider.baseUrl),
|
|
5112
|
+
apiKey
|
|
5113
|
+
});
|
|
4596
5114
|
|
|
4597
5115
|
writeConfigWithSync(paths, config);
|
|
4598
5116
|
|
|
@@ -4604,12 +5122,17 @@ async function activate(paths, type) {
|
|
|
4604
5122
|
|
|
4605
5123
|
const authStore = readAuthStore(paths.authProfiles);
|
|
4606
5124
|
authStore.profiles[`${apiConfig.providerName}:default`] = { type: 'api_key', key: apiKey.trim(), provider: apiConfig.providerName };
|
|
5125
|
+
if (yunyiLayoutResult.applied) {
|
|
5126
|
+
authStore.profiles[`${YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER}:default`] = { type: 'api_key', key: apiKey.trim(), provider: YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER };
|
|
5127
|
+
authStore.profiles[`${YYMAXAPI_OPENCLAW_GPT_PROVIDER}:default`] = { type: 'api_key', key: apiKey.trim(), provider: YYMAXAPI_OPENCLAW_GPT_PROVIDER };
|
|
5128
|
+
}
|
|
4607
5129
|
writeAuthStore(paths.authProfiles, authStore);
|
|
4608
5130
|
|
|
4609
5131
|
console.log(chalk.green(`\n✅ 已激活 ${typeLabel}`));
|
|
4610
5132
|
console.log(chalk.cyan(` 节点: ${provider.baseUrl}`));
|
|
4611
5133
|
console.log(chalk.gray(` 模型: ${modelConfig.name}`));
|
|
4612
5134
|
console.log(chalk.gray(` API Key: 已设置`));
|
|
5135
|
+
printYunyiOpenClawSwitchHint(yunyiLayoutResult);
|
|
4613
5136
|
}
|
|
4614
5137
|
|
|
4615
5138
|
|
|
@@ -4628,6 +5151,7 @@ async function switchModel(paths) {
|
|
|
4628
5151
|
}
|
|
4629
5152
|
|
|
4630
5153
|
const PROVIDER_LABELS = {
|
|
5154
|
+
'yunyi-claude': '云翼 Claude (包月)',
|
|
4631
5155
|
'claude-yunyi': '云翼 Claude (包月)',
|
|
4632
5156
|
'yunyi': '云翼 Codex (包月)',
|
|
4633
5157
|
'heibai': 'MAXAPI (按量)',
|
|
@@ -4883,30 +5407,28 @@ async function testConnection(paths, args = {}) {
|
|
|
4883
5407
|
|
|
4884
5408
|
const config = ensureConfigStructure(readConfig(paths.openclawConfig) || {});
|
|
4885
5409
|
applyConfigRepairsWithSync(config, paths);
|
|
5410
|
+
applyManagedYunyiOpenClawLayout(config);
|
|
4886
5411
|
|
|
4887
5412
|
if (!config || !config.models) {
|
|
4888
5413
|
console.log(chalk.yellow('配置文件不存在,请先选择节点'));
|
|
4889
5414
|
return;
|
|
4890
5415
|
}
|
|
4891
5416
|
|
|
4892
|
-
|
|
4893
|
-
|
|
5417
|
+
const testTarget = resolveOpenClawTestTarget(config, args);
|
|
5418
|
+
const primary = testTarget.primary;
|
|
4894
5419
|
if (!primary.includes('/')) {
|
|
4895
5420
|
console.log(chalk.yellow('⚠️ 主模型未设置,请先通过「切换 OpenClaw 模型」或「一键配置」设置主模型'));
|
|
4896
5421
|
return;
|
|
4897
5422
|
}
|
|
4898
5423
|
|
|
4899
|
-
const providerName =
|
|
4900
|
-
const provider =
|
|
5424
|
+
const providerName = testTarget.providerName;
|
|
5425
|
+
const provider = testTarget.provider;
|
|
4901
5426
|
if (!provider) {
|
|
4902
5427
|
console.log(chalk.yellow(`⚠️ 主模型对应的中转站不存在: ${providerName}`));
|
|
4903
5428
|
return;
|
|
4904
5429
|
}
|
|
4905
5430
|
|
|
4906
|
-
const
|
|
4907
|
-
const typeLabel = apiType.startsWith('anthropic')
|
|
4908
|
-
? 'Claude'
|
|
4909
|
-
: (apiType.startsWith('openai') ? 'Codex' : '模型');
|
|
5431
|
+
const typeLabel = testTarget.typeLabel;
|
|
4910
5432
|
|
|
4911
5433
|
if (!provider.apiKey) {
|
|
4912
5434
|
console.log(chalk.yellow(`⚠️ ${typeLabel} API Key 未设置`));
|
|
@@ -4916,7 +5438,13 @@ async function testConnection(paths, args = {}) {
|
|
|
4916
5438
|
// 获取 Gateway 配置
|
|
4917
5439
|
const gatewayPort = config.gateway?.port || 18789;
|
|
4918
5440
|
|
|
4919
|
-
console.log(chalk.gray(
|
|
5441
|
+
console.log(chalk.gray(`当前测试: ${typeLabel}`));
|
|
5442
|
+
if (testTarget.agentId) {
|
|
5443
|
+
console.log(chalk.gray(`测试入口: ${testTarget.agentId}${testTarget.agentName ? ` (${testTarget.agentName})` : ''}`));
|
|
5444
|
+
}
|
|
5445
|
+
if (testTarget.agentId && testTarget.defaultPrimary && testTarget.defaultPrimary !== primary) {
|
|
5446
|
+
console.log(chalk.gray(`默认主入口: ${testTarget.defaultPrimary}`));
|
|
5447
|
+
}
|
|
4920
5448
|
console.log(chalk.gray(`中转节点: ${provider.baseUrl}`));
|
|
4921
5449
|
console.log(chalk.gray(`模型: ${primary}`));
|
|
4922
5450
|
console.log(chalk.gray(`Gateway: http://127.0.0.1:${gatewayPort}\n`));
|
|
@@ -4992,7 +5520,7 @@ async function testConnection(paths, args = {}) {
|
|
|
4992
5520
|
console.log(chalk.cyan(`\n步骤 2/2: 测试 Gateway API 端点...`));
|
|
4993
5521
|
|
|
4994
5522
|
try {
|
|
4995
|
-
const cliResult = await testGatewayViaAgent(primary);
|
|
5523
|
+
const cliResult = await testGatewayViaAgent(primary, testTarget.agentId);
|
|
4996
5524
|
let cliPassed = false;
|
|
4997
5525
|
|
|
4998
5526
|
if (cliResult.usedCli) {
|
|
@@ -5017,7 +5545,7 @@ async function testConnection(paths, args = {}) {
|
|
|
5017
5545
|
console.log(chalk.red(` 期望: ${primary}`));
|
|
5018
5546
|
console.log(chalk.cyan(` 实际: ${actualModelKey}`));
|
|
5019
5547
|
console.log(chalk.gray(` 这意味着 ${primary} 无法正常工作,请检查该模型的中转配置`));
|
|
5020
|
-
const configuredFallbacks =
|
|
5548
|
+
const configuredFallbacks = testTarget.fallbacks || [];
|
|
5021
5549
|
if (configuredFallbacks.length > 0 && !configuredFallbacks.includes(actualModelKey)) {
|
|
5022
5550
|
console.log(chalk.yellow(` ⚠️ 实际回退模型不在当前配置的 fallbacks 中,疑似读到了另一份配置`));
|
|
5023
5551
|
console.log(chalk.gray(` 当前 fallbacks: ${configuredFallbacks.join(', ')}`));
|
|
@@ -5508,7 +6036,7 @@ function testGatewayApi(port, token, model, endpoint = '/v1/chat/completions') {
|
|
|
5508
6036
|
});
|
|
5509
6037
|
}
|
|
5510
6038
|
|
|
5511
|
-
function testGatewayViaAgent(model) {
|
|
6039
|
+
function testGatewayViaAgent(model, agentId = '') {
|
|
5512
6040
|
return new Promise((resolve) => {
|
|
5513
6041
|
const gwEnv = detectGatewayEnv();
|
|
5514
6042
|
const sessionId = `yymaxapi-test-${Date.now()}`;
|
|
@@ -5519,11 +6047,12 @@ function testGatewayViaAgent(model) {
|
|
|
5519
6047
|
if (gwEnv === 'wsl') {
|
|
5520
6048
|
// Gateway 在 WSL 中,agent 也要在 WSL 中执行
|
|
5521
6049
|
const wslCli = getWslCliBinary();
|
|
6050
|
+
const subcommand = buildGatewayAgentSubcommand({ sessionId, agentId });
|
|
5522
6051
|
if (wslCli) {
|
|
5523
|
-
const agentCmd = `${wslCli}
|
|
6052
|
+
const agentCmd = `${wslCli} ${subcommand}`;
|
|
5524
6053
|
cmd = `wsl -- bash -c '${agentCmd.replace(/'/g, "'\\''")}'`;
|
|
5525
6054
|
} else {
|
|
5526
|
-
const agentCmd = `openclaw
|
|
6055
|
+
const agentCmd = `openclaw ${subcommand} 2>/dev/null || clawdbot ${subcommand} 2>/dev/null || moltbot ${subcommand}`;
|
|
5527
6056
|
cmd = `wsl -- bash -lc '${agentCmd.replace(/'/g, "'\\''")}'`;
|
|
5528
6057
|
}
|
|
5529
6058
|
execOpts = { timeout: 120000 };
|
|
@@ -5541,9 +6070,13 @@ function testGatewayViaAgent(model) {
|
|
|
5541
6070
|
delete env.OPENAI_API_KEY;
|
|
5542
6071
|
delete env.OPENCLAW_CODEX_KEY;
|
|
5543
6072
|
const useNode = nodeInfo && isNodeShebang(cliBinary);
|
|
5544
|
-
cmd =
|
|
5545
|
-
|
|
5546
|
-
|
|
6073
|
+
cmd = buildGatewayAgentCliCommand({
|
|
6074
|
+
cliBinary,
|
|
6075
|
+
nodeInfo,
|
|
6076
|
+
useNode,
|
|
6077
|
+
sessionId,
|
|
6078
|
+
agentId
|
|
6079
|
+
});
|
|
5547
6080
|
execOpts = { timeout: 120000, env };
|
|
5548
6081
|
}
|
|
5549
6082
|
|