yymaxapi 1.0.83 → 1.0.85
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 +471 -11
- 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,383 @@ 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 hasManagedYunyiFootprint(config) {
|
|
2029
|
+
const providers = config?.models?.providers || {};
|
|
2030
|
+
if (Object.entries(providers).some(([name, provider]) => isYunyiProviderEntry(name, provider, 'claude') || isYunyiProviderEntry(name, provider, 'codex'))) {
|
|
2031
|
+
return true;
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
const defaultsModel = config?.agents?.defaults?.model || {};
|
|
2035
|
+
if (isManagedYunyiModelKey(defaultsModel.primary)) return true;
|
|
2036
|
+
if (Array.isArray(defaultsModel.fallbacks) && defaultsModel.fallbacks.some(isManagedYunyiModelKey)) return true;
|
|
2037
|
+
|
|
2038
|
+
if (Array.isArray(config?.agents?.list)) {
|
|
2039
|
+
return config.agents.list.some(agent => {
|
|
2040
|
+
if (!agent || typeof agent !== 'object') return false;
|
|
2041
|
+
const name = String(agent.name || '').trim();
|
|
2042
|
+
const { primary, fallbacks } = getAgentModelState(agent);
|
|
2043
|
+
if (agent.id === YYMAXAPI_OPENCLAW_GPT_AGENT_ID || agent.id === YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID) {
|
|
2044
|
+
return true;
|
|
2045
|
+
}
|
|
2046
|
+
if (agent.id === YYMAXAPI_OPENCLAW_MAIN_AGENT_ID && YYMAXAPI_MANAGED_MAIN_NAMES.has(name)) {
|
|
2047
|
+
return true;
|
|
2048
|
+
}
|
|
2049
|
+
return isManagedYunyiModelKey(primary) || fallbacks.some(isManagedYunyiModelKey);
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
return false;
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
function inferManagedYunyiEndpointUrl(config, explicitEndpointUrl = '') {
|
|
2057
|
+
const direct = stripManagedYunyiSuffix(explicitEndpointUrl);
|
|
2058
|
+
if (direct && isLikelyYunyiBaseUrl(direct)) return direct;
|
|
2059
|
+
|
|
2060
|
+
const providers = config?.models?.providers || {};
|
|
2061
|
+
for (const [name, provider] of Object.entries(providers)) {
|
|
2062
|
+
if (!isYunyiProviderEntry(name, provider, 'claude') && !isYunyiProviderEntry(name, provider, 'codex')) continue;
|
|
2063
|
+
const candidate = stripManagedYunyiSuffix(provider?.baseUrl || provider?.base_url || '');
|
|
2064
|
+
if (candidate && isLikelyYunyiBaseUrl(candidate)) return candidate;
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
return ENDPOINTS.find(item => isLikelyYunyiBaseUrl(item.url))?.url || ENDPOINTS[0]?.url || '';
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
function inferManagedYunyiApiKey(config, explicitApiKey = '') {
|
|
2071
|
+
const direct = String(explicitApiKey || '').trim();
|
|
2072
|
+
if (direct) return direct;
|
|
2073
|
+
|
|
2074
|
+
const providers = config?.models?.providers || {};
|
|
2075
|
+
for (const [name, provider] of Object.entries(providers)) {
|
|
2076
|
+
if (!isYunyiProviderEntry(name, provider, 'claude') && !isYunyiProviderEntry(name, provider, 'codex')) continue;
|
|
2077
|
+
const apiKey = String(provider?.apiKey || '').trim();
|
|
2078
|
+
if (apiKey) return apiKey;
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
return '';
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
function buildManagedYunyiProviderConfig(type, endpointUrl, apiKey, existingProvider = {}) {
|
|
2085
|
+
const providerName = type === 'claude' ? YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER : YYMAXAPI_OPENCLAW_GPT_PROVIDER;
|
|
2086
|
+
const nextApiKey = String(apiKey || existingProvider.apiKey || '').trim();
|
|
2087
|
+
const next = {
|
|
2088
|
+
...existingProvider,
|
|
2089
|
+
baseUrl: buildFullUrl(endpointUrl, type),
|
|
2090
|
+
auth: DEFAULT_AUTH_MODE,
|
|
2091
|
+
api: type === 'claude' ? 'anthropic-messages' : 'openai-completions',
|
|
2092
|
+
headers: {},
|
|
2093
|
+
authHeader: type !== 'claude',
|
|
2094
|
+
models: getManagedYunyiProviderModels(type)
|
|
2095
|
+
};
|
|
2096
|
+
|
|
2097
|
+
if (nextApiKey) next.apiKey = nextApiKey;
|
|
2098
|
+
if (!next.auth) next.auth = DEFAULT_AUTH_MODE;
|
|
2099
|
+
if (!next.baseUrl) next.baseUrl = buildFullUrl(endpointUrl, type);
|
|
2100
|
+
if (!next.api) next.api = type === 'claude' ? 'anthropic-messages' : 'openai-completions';
|
|
2101
|
+
if (!Array.isArray(next.models)) next.models = getManagedYunyiProviderModels(type);
|
|
2102
|
+
if (!providerName) return existingProvider;
|
|
2103
|
+
return next;
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
function ensureAgentList(config) {
|
|
2107
|
+
if (!config.agents) config.agents = {};
|
|
2108
|
+
if (!Array.isArray(config.agents.list)) config.agents.list = [];
|
|
2109
|
+
return config.agents.list;
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
function findAgentIndex(agentList, agentId) {
|
|
2113
|
+
return agentList.findIndex(agent => agent && typeof agent === 'object' && agent.id === agentId);
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
function mergeManagedAgent(existingAgent, nextAgent) {
|
|
2117
|
+
const existing = existingAgent && typeof existingAgent === 'object' ? existingAgent : {};
|
|
2118
|
+
return {
|
|
2119
|
+
...existing,
|
|
2120
|
+
...nextAgent,
|
|
2121
|
+
model: {
|
|
2122
|
+
...(existing.model && typeof existing.model === 'object' ? existing.model : {}),
|
|
2123
|
+
...(nextAgent.model || {})
|
|
2124
|
+
}
|
|
2125
|
+
};
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
function isManagedMainAgent(agent) {
|
|
2129
|
+
if (!agent || typeof agent !== 'object') return true;
|
|
2130
|
+
const name = String(agent.name || '').trim();
|
|
2131
|
+
const { primary, fallbacks } = getAgentModelState(agent);
|
|
2132
|
+
if (!primary) return true;
|
|
2133
|
+
if (YYMAXAPI_MANAGED_MAIN_NAMES.has(name)) return true;
|
|
2134
|
+
if (isManagedYunyiClaudeModelKey(primary) || isManagedYunyiGptModelKey(primary)) return true;
|
|
2135
|
+
return fallbacks.some(modelKey => isManagedYunyiClaudeModelKey(modelKey) || isManagedYunyiGptModelKey(modelKey));
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
function isManagedClaudeSideAgent(agent) {
|
|
2139
|
+
if (!agent || typeof agent !== 'object') return true;
|
|
2140
|
+
const name = String(agent.name || '').trim();
|
|
2141
|
+
const { primary, fallbacks } = getAgentModelState(agent);
|
|
2142
|
+
if (!primary) return true;
|
|
2143
|
+
if (YYMAXAPI_MANAGED_MAIN_NAMES.has(name)) return true;
|
|
2144
|
+
if (isManagedYunyiClaudeModelKey(primary)) return true;
|
|
2145
|
+
return fallbacks.some(isManagedYunyiClaudeModelKey);
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
function isManagedGptAgent(agent) {
|
|
2149
|
+
if (!agent || typeof agent !== 'object') return true;
|
|
2150
|
+
const name = String(agent.name || '').trim();
|
|
2151
|
+
const { primary, fallbacks } = getAgentModelState(agent);
|
|
2152
|
+
if (!primary) return true;
|
|
2153
|
+
if (YYMAXAPI_MANAGED_GPT_NAMES.has(name)) return true;
|
|
2154
|
+
if (isManagedYunyiGptModelKey(primary)) return true;
|
|
2155
|
+
return fallbacks.some(isManagedYunyiGptModelKey);
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
function upsertManagedAgent(agentList, nextAgent, shouldManage) {
|
|
2159
|
+
const index = findAgentIndex(agentList, nextAgent.id);
|
|
2160
|
+
if (index === -1) {
|
|
2161
|
+
agentList.push(nextAgent);
|
|
2162
|
+
return { changed: true, managed: true, created: true };
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
const currentAgent = agentList[index];
|
|
2166
|
+
if (!shouldManage(currentAgent)) {
|
|
2167
|
+
return { changed: false, managed: false, preserved: true };
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
const mergedAgent = mergeManagedAgent(currentAgent, nextAgent);
|
|
2171
|
+
if (JSON.stringify(mergedAgent) === JSON.stringify(currentAgent)) {
|
|
2172
|
+
return { changed: false, managed: true };
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
agentList[index] = mergedAgent;
|
|
2176
|
+
return { changed: true, managed: true };
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
function removeManagedAgent(agentList, agentId, shouldManage) {
|
|
2180
|
+
const index = findAgentIndex(agentList, agentId);
|
|
2181
|
+
if (index === -1) return false;
|
|
2182
|
+
if (!shouldManage(agentList[index])) return false;
|
|
2183
|
+
agentList.splice(index, 1);
|
|
2184
|
+
return true;
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
function shouldManageYunyiDefaults(config) {
|
|
2188
|
+
const modelState = config?.agents?.defaults?.model || {};
|
|
2189
|
+
const primary = typeof modelState.primary === 'string' ? modelState.primary.trim() : '';
|
|
2190
|
+
const fallbacks = Array.isArray(modelState.fallbacks) ? modelState.fallbacks : [];
|
|
2191
|
+
if (!primary) return true;
|
|
2192
|
+
if (isManagedYunyiModelKey(primary)) return true;
|
|
2193
|
+
return fallbacks.some(isManagedYunyiModelKey);
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
function applyManagedYunyiOpenClawLayout(config, options = {}) {
|
|
2197
|
+
if (!isYymaxapiOpenClawBuild()) {
|
|
2198
|
+
return { changed: false, applied: false, claudeAgentId: null, preservedMain: false };
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
if (!options.force && !hasManagedYunyiFootprint(config)) {
|
|
2202
|
+
return { changed: false, applied: false, claudeAgentId: null, preservedMain: false };
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
ensureConfigStructure(config);
|
|
2206
|
+
|
|
2207
|
+
const endpointUrl = inferManagedYunyiEndpointUrl(config, options.endpointUrl);
|
|
2208
|
+
const apiKey = inferManagedYunyiApiKey(config, options.apiKey);
|
|
2209
|
+
if (!endpointUrl) {
|
|
2210
|
+
return { changed: false, applied: false, claudeAgentId: null, preservedMain: false };
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
let changed = false;
|
|
2214
|
+
const providers = config.models.providers;
|
|
2215
|
+
|
|
2216
|
+
const nextClaudeProvider = buildManagedYunyiProviderConfig('claude', endpointUrl, apiKey, providers[YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER] || {});
|
|
2217
|
+
if (JSON.stringify(nextClaudeProvider) !== JSON.stringify(providers[YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER] || {})) {
|
|
2218
|
+
providers[YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER] = nextClaudeProvider;
|
|
2219
|
+
changed = true;
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
const nextGptProvider = buildManagedYunyiProviderConfig('codex', endpointUrl, apiKey, providers[YYMAXAPI_OPENCLAW_GPT_PROVIDER] || {});
|
|
2223
|
+
if (JSON.stringify(nextGptProvider) !== JSON.stringify(providers[YYMAXAPI_OPENCLAW_GPT_PROVIDER] || {})) {
|
|
2224
|
+
providers[YYMAXAPI_OPENCLAW_GPT_PROVIDER] = nextGptProvider;
|
|
2225
|
+
changed = true;
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
const nextClaudeAuthProfile = { provider: YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER, mode: 'api_key' };
|
|
2229
|
+
if (JSON.stringify(config.auth.profiles[`${YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER}:default`] || {}) !== JSON.stringify(nextClaudeAuthProfile)) {
|
|
2230
|
+
config.auth.profiles[`${YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER}:default`] = nextClaudeAuthProfile;
|
|
2231
|
+
changed = true;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
const nextGptAuthProfile = { provider: YYMAXAPI_OPENCLAW_GPT_PROVIDER, mode: 'api_key' };
|
|
2235
|
+
if (JSON.stringify(config.auth.profiles[`${YYMAXAPI_OPENCLAW_GPT_PROVIDER}:default`] || {}) !== JSON.stringify(nextGptAuthProfile)) {
|
|
2236
|
+
config.auth.profiles[`${YYMAXAPI_OPENCLAW_GPT_PROVIDER}:default`] = nextGptAuthProfile;
|
|
2237
|
+
changed = true;
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
const defaultsModels = config.agents.defaults.models;
|
|
2241
|
+
const managedModelAliases = {
|
|
2242
|
+
[YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY]: YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER,
|
|
2243
|
+
[YYMAXAPI_OPENCLAW_CLAUDE_FALLBACK]: YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER,
|
|
2244
|
+
[YYMAXAPI_OPENCLAW_GPT_PRIMARY]: YYMAXAPI_OPENCLAW_GPT_PROVIDER,
|
|
2245
|
+
[YYMAXAPI_OPENCLAW_GPT_FALLBACK]: YYMAXAPI_OPENCLAW_GPT_PROVIDER
|
|
2246
|
+
};
|
|
2247
|
+
for (const [modelKey, alias] of Object.entries(managedModelAliases)) {
|
|
2248
|
+
const nextEntry = { alias };
|
|
2249
|
+
if (JSON.stringify(defaultsModels[modelKey] || {}) !== JSON.stringify(nextEntry)) {
|
|
2250
|
+
defaultsModels[modelKey] = nextEntry;
|
|
2251
|
+
changed = true;
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
const agentList = ensureAgentList(config);
|
|
2256
|
+
const mainAgentResult = upsertManagedAgent(agentList, {
|
|
2257
|
+
id: YYMAXAPI_OPENCLAW_MAIN_AGENT_ID,
|
|
2258
|
+
default: true,
|
|
2259
|
+
name: 'yunyi-claude',
|
|
2260
|
+
model: {
|
|
2261
|
+
primary: YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY,
|
|
2262
|
+
fallbacks: [YYMAXAPI_OPENCLAW_CLAUDE_FALLBACK]
|
|
2263
|
+
}
|
|
2264
|
+
}, isManagedMainAgent);
|
|
2265
|
+
if (mainAgentResult.changed) changed = true;
|
|
2266
|
+
|
|
2267
|
+
let claudeAgentId = YYMAXAPI_OPENCLAW_MAIN_AGENT_ID;
|
|
2268
|
+
let preservedMain = !mainAgentResult.managed;
|
|
2269
|
+
if (!mainAgentResult.managed) {
|
|
2270
|
+
const fallbackClaudeAgentResult = upsertManagedAgent(agentList, {
|
|
2271
|
+
id: YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID,
|
|
2272
|
+
default: false,
|
|
2273
|
+
name: 'yunyi-claude',
|
|
2274
|
+
model: {
|
|
2275
|
+
primary: YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY,
|
|
2276
|
+
fallbacks: [YYMAXAPI_OPENCLAW_CLAUDE_FALLBACK]
|
|
2277
|
+
}
|
|
2278
|
+
}, isManagedClaudeSideAgent);
|
|
2279
|
+
if (fallbackClaudeAgentResult.changed) changed = true;
|
|
2280
|
+
claudeAgentId = fallbackClaudeAgentResult.managed ? YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID : null;
|
|
2281
|
+
} else if (removeManagedAgent(agentList, YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID, isManagedClaudeSideAgent)) {
|
|
2282
|
+
changed = true;
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
const gptAgentResult = upsertManagedAgent(agentList, {
|
|
2286
|
+
id: YYMAXAPI_OPENCLAW_GPT_AGENT_ID,
|
|
2287
|
+
default: false,
|
|
2288
|
+
name: 'yunyi-gpt',
|
|
2289
|
+
model: {
|
|
2290
|
+
primary: YYMAXAPI_OPENCLAW_GPT_PRIMARY,
|
|
2291
|
+
fallbacks: [YYMAXAPI_OPENCLAW_GPT_FALLBACK]
|
|
2292
|
+
}
|
|
2293
|
+
}, isManagedGptAgent);
|
|
2294
|
+
if (gptAgentResult.changed) changed = true;
|
|
2295
|
+
|
|
2296
|
+
if (shouldManageYunyiDefaults(config)) {
|
|
2297
|
+
const nextDefaultsModel = {
|
|
2298
|
+
primary: YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY,
|
|
2299
|
+
fallbacks: [YYMAXAPI_OPENCLAW_GPT_PRIMARY]
|
|
2300
|
+
};
|
|
2301
|
+
if (JSON.stringify(config.agents.defaults.model) !== JSON.stringify(nextDefaultsModel)) {
|
|
2302
|
+
config.agents.defaults.model = nextDefaultsModel;
|
|
2303
|
+
changed = true;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
return { changed, applied: true, claudeAgentId, preservedMain };
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
function printYunyiOpenClawSwitchHint(result = {}) {
|
|
2311
|
+
if (!result?.applied) return;
|
|
2312
|
+
if (result.preservedMain && result.claudeAgentId === YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID) {
|
|
2313
|
+
console.log(chalk.gray(' 已保留你现有的 main;Yunyi Claude 入口为 yunyi-claude'));
|
|
2314
|
+
} else {
|
|
2315
|
+
console.log(chalk.gray(' 默认主入口是 yunyi-claude'));
|
|
2316
|
+
}
|
|
2317
|
+
console.log(chalk.gray(' GPT 入口是 yunyi-gpt'));
|
|
2318
|
+
console.log(chalk.yellow(' 提示: 当前 OpenClaw Web 顶部模型下拉跨 provider 切换可能报错,建议通过左侧会话/agent 切换 Claude 和 GPT'));
|
|
2319
|
+
}
|
|
2320
|
+
|
|
1923
2321
|
function pruneProvidersByPrefix(config, prefixBase, keepProviders = []) {
|
|
1924
2322
|
if (!config?.models?.providers) return [];
|
|
1925
2323
|
const removed = [];
|
|
@@ -2260,12 +2658,14 @@ function isNodeShebang(filePath) {
|
|
|
2260
2658
|
return true;
|
|
2261
2659
|
}
|
|
2262
2660
|
const fd = fs.openSync(filePath, 'r');
|
|
2263
|
-
const buffer = Buffer.alloc(
|
|
2661
|
+
const buffer = Buffer.alloc(256);
|
|
2264
2662
|
const bytes = fs.readSync(fd, buffer, 0, buffer.length, 0);
|
|
2265
2663
|
fs.closeSync(fd);
|
|
2266
2664
|
if (bytes <= 0) return false;
|
|
2267
2665
|
const header = buffer.toString('utf8', 0, bytes);
|
|
2268
|
-
|
|
2666
|
+
const firstLine = header.split(/\r?\n/, 1)[0] || '';
|
|
2667
|
+
if (!firstLine.startsWith('#!')) return false;
|
|
2668
|
+
return /\bnode(?:js)?\b/.test(firstLine);
|
|
2269
2669
|
} catch {
|
|
2270
2670
|
return false;
|
|
2271
2671
|
}
|
|
@@ -2747,6 +3147,8 @@ function autoFixConfig(paths) {
|
|
|
2747
3147
|
sanitizeDefaultModelSelection(config);
|
|
2748
3148
|
const repairResult = applyConfigRepairsWithSync(config, paths);
|
|
2749
3149
|
if (repairResult.changed) changed = true;
|
|
3150
|
+
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config);
|
|
3151
|
+
if (yunyiLayoutResult.changed) changed = true;
|
|
2750
3152
|
const codexProvider = codexProviderName && config.models.providers?.[codexProviderName];
|
|
2751
3153
|
const nextModelJson = JSON.stringify(config.agents?.defaults?.model ?? null);
|
|
2752
3154
|
if (originalModelJson !== nextModelJson) {
|
|
@@ -2766,8 +3168,12 @@ function autoFixConfig(paths) {
|
|
|
2766
3168
|
}
|
|
2767
3169
|
|
|
2768
3170
|
if (changed) {
|
|
2769
|
-
|
|
2770
|
-
|
|
3171
|
+
writeConfigWithSync(paths, config);
|
|
3172
|
+
const managedClaudeKey = String(config.models.providers?.[YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER]?.apiKey || '').trim();
|
|
3173
|
+
const managedGptKey = String(config.models.providers?.[YYMAXAPI_OPENCLAW_GPT_PROVIDER]?.apiKey || '').trim();
|
|
3174
|
+
if (managedClaudeKey) updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER, managedClaudeKey);
|
|
3175
|
+
if (managedGptKey) updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_GPT_PROVIDER, managedGptKey);
|
|
3176
|
+
console.log(chalk.green('✓ 已自动修复配置(Yunyi providers / agents / API 协议)'));
|
|
2771
3177
|
}
|
|
2772
3178
|
} catch { /* ignore */ }
|
|
2773
3179
|
}
|
|
@@ -3468,12 +3874,21 @@ async function presetClaude(paths, args = {}) {
|
|
|
3468
3874
|
config.agents.defaults.model.primary = modelKey;
|
|
3469
3875
|
config.agents.defaults.model.fallbacks = [];
|
|
3470
3876
|
}
|
|
3877
|
+
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
|
|
3878
|
+
force: true,
|
|
3879
|
+
endpointUrl: selectedEndpoint.url,
|
|
3880
|
+
apiKey
|
|
3881
|
+
});
|
|
3471
3882
|
|
|
3472
3883
|
const writeSpinner = ora({ text: '正在写入配置...', spinner: 'dots' }).start();
|
|
3473
3884
|
createTimestampedBackup(paths.openclawConfig, paths.configDir, 'claude');
|
|
3474
3885
|
ensureGatewaySettings(config);
|
|
3475
3886
|
writeConfigWithSync(paths, config);
|
|
3476
3887
|
updateAuthProfilesWithSync(paths, providerName, apiKey);
|
|
3888
|
+
if (yunyiLayoutResult.applied) {
|
|
3889
|
+
updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER, apiKey);
|
|
3890
|
+
updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_GPT_PROVIDER, apiKey);
|
|
3891
|
+
}
|
|
3477
3892
|
const extSynced = syncExternalTools('claude', baseUrl, apiKey, { claudeModelId: modelId, opencodeDefaultModelKey: `yunyi-claude/${modelId}` });
|
|
3478
3893
|
writeSpinner.succeed('配置写入完成');
|
|
3479
3894
|
|
|
@@ -3486,6 +3901,7 @@ async function presetClaude(paths, args = {}) {
|
|
|
3486
3901
|
if (repairResult.renamedProviders.length > 0) {
|
|
3487
3902
|
console.log(chalk.gray(` 已保留并修复冲突 provider: ${repairResult.renamedProviders.map(item => `${item.from}→${item.to}`).join(', ')}`));
|
|
3488
3903
|
}
|
|
3904
|
+
printYunyiOpenClawSwitchHint(yunyiLayoutResult);
|
|
3489
3905
|
|
|
3490
3906
|
const shouldTestGateway = args.test !== undefined
|
|
3491
3907
|
? !['false', '0', 'no'].includes(String(args.test).toLowerCase())
|
|
@@ -3644,6 +4060,11 @@ async function presetCodex(paths, args = {}) {
|
|
|
3644
4060
|
config.agents.defaults.model.primary = modelKey;
|
|
3645
4061
|
config.agents.defaults.model.fallbacks = [];
|
|
3646
4062
|
}
|
|
4063
|
+
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
|
|
4064
|
+
force: true,
|
|
4065
|
+
endpointUrl: selectedEndpoint.url,
|
|
4066
|
+
apiKey
|
|
4067
|
+
});
|
|
3647
4068
|
|
|
3648
4069
|
const writeSpinner2 = ora({ text: '正在写入配置...', spinner: 'dots' }).start();
|
|
3649
4070
|
createTimestampedBackup(paths.openclawConfig, paths.configDir, 'codex');
|
|
@@ -3651,6 +4072,10 @@ async function presetCodex(paths, args = {}) {
|
|
|
3651
4072
|
cleanupConflictingEnvVars(config, baseUrl, apiKey);
|
|
3652
4073
|
writeConfigWithSync(paths, config);
|
|
3653
4074
|
updateAuthProfilesWithSync(paths, providerName, apiKey);
|
|
4075
|
+
if (yunyiLayoutResult.applied) {
|
|
4076
|
+
updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER, apiKey);
|
|
4077
|
+
updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_GPT_PROVIDER, apiKey);
|
|
4078
|
+
}
|
|
3654
4079
|
const extSynced2 = syncExternalTools('codex', baseUrl, apiKey, { modelId });
|
|
3655
4080
|
writeSpinner2.succeed('配置写入完成');
|
|
3656
4081
|
|
|
@@ -3663,6 +4088,7 @@ async function presetCodex(paths, args = {}) {
|
|
|
3663
4088
|
if (repairResult.renamedProviders.length > 0) {
|
|
3664
4089
|
console.log(chalk.gray(` 已保留并修复冲突 provider: ${repairResult.renamedProviders.map(item => `${item.from}→${item.to}`).join(', ')}`));
|
|
3665
4090
|
}
|
|
4091
|
+
printYunyiOpenClawSwitchHint(yunyiLayoutResult);
|
|
3666
4092
|
|
|
3667
4093
|
const shouldTestGateway = args.test !== undefined
|
|
3668
4094
|
? !['false', '0', 'no'].includes(String(args.test).toLowerCase())
|
|
@@ -3831,6 +4257,11 @@ async function autoActivate(paths, args = {}) {
|
|
|
3831
4257
|
const fallbackModelKey = isClaudePrimary ? codexModelKey : claudeModelKey;
|
|
3832
4258
|
config.agents.defaults.model.primary = primaryModelKey;
|
|
3833
4259
|
config.agents.defaults.model.fallbacks = [fallbackModelKey];
|
|
4260
|
+
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
|
|
4261
|
+
force: true,
|
|
4262
|
+
endpointUrl: selectedEndpoint.url,
|
|
4263
|
+
apiKey
|
|
4264
|
+
});
|
|
3834
4265
|
|
|
3835
4266
|
// ---- 写入配置 ----
|
|
3836
4267
|
const writeSpinner = ora({ text: '正在写入配置...', spinner: 'dots' }).start();
|
|
@@ -3850,18 +4281,19 @@ async function autoActivate(paths, args = {}) {
|
|
|
3850
4281
|
const primaryLabel = isClaudePrimary ? 'Claude' : 'GPT';
|
|
3851
4282
|
const selectedModel = isClaudePrimary ? claudeModel : codexModel;
|
|
3852
4283
|
console.log(chalk.green('\n✅ 配置完成!'));
|
|
3853
|
-
console.log(chalk.cyan(`
|
|
4284
|
+
console.log(chalk.cyan(` 外部工具默认: ${selectedModel.name} (${primaryLabel})`));
|
|
3854
4285
|
console.log(chalk.gray(` 节点: ${selectedEndpoint.url} (${selectedEndpoint.name})`));
|
|
3855
4286
|
console.log(chalk.gray(' API Key: 已设置'));
|
|
3856
4287
|
if (extSynced.length > 0) console.log(chalk.gray(` 同步: ${extSynced.join(', ')}`));
|
|
3857
4288
|
console.log(chalk.gray(' 若遇 certificate 报错,请新开终端或执行 source ~/.zshrc 后重试(已放宽 TLS 校验)'));
|
|
3858
4289
|
console.log(chalk.gray(` 使用 OpenCode 时可在界面中切换 ${getOpencodeSwitchHint()};Codex 仅支持 GPT`));
|
|
4290
|
+
printYunyiOpenClawSwitchHint(yunyiLayoutResult);
|
|
3859
4291
|
|
|
3860
4292
|
const gwPort = config.gateway?.port || 18789;
|
|
3861
4293
|
const gwToken = config.gateway?.auth?.token;
|
|
3862
4294
|
if (gwToken) {
|
|
3863
4295
|
console.log(chalk.green(`\n🌐 Web Dashboard:`));
|
|
3864
|
-
console.log(chalk.cyan(` http://127.0.0.1:${gwPort}
|
|
4296
|
+
console.log(chalk.cyan(` http://127.0.0.1:${gwPort}/#token=${gwToken}`));
|
|
3865
4297
|
}
|
|
3866
4298
|
|
|
3867
4299
|
// ---- 配置完成后:测试连接 or 退出 ----
|
|
@@ -4276,6 +4708,11 @@ async function yycodeQuickSetup(paths) {
|
|
|
4276
4708
|
// 默认主力: Codex, 备用: Claude
|
|
4277
4709
|
config.agents.defaults.model.primary = codexModelKey;
|
|
4278
4710
|
config.agents.defaults.model.fallbacks = [claudeModelKey];
|
|
4711
|
+
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
|
|
4712
|
+
force: true,
|
|
4713
|
+
endpointUrl: selectedEndpoint.url,
|
|
4714
|
+
apiKey
|
|
4715
|
+
});
|
|
4279
4716
|
|
|
4280
4717
|
// ---- 写入 ----
|
|
4281
4718
|
const writeSpinner = ora({ text: '正在写入配置...', spinner: 'dots' }).start();
|
|
@@ -4293,10 +4730,11 @@ async function yycodeQuickSetup(paths) {
|
|
|
4293
4730
|
console.log(chalk.green('\n✅ 配置完成!'));
|
|
4294
4731
|
console.log(chalk.cyan(` Claude Code: ${claudeBaseUrl}`));
|
|
4295
4732
|
console.log(chalk.gray(` 模型: ${claudeModel.name}`));
|
|
4296
|
-
console.log(chalk.cyan(` Codex
|
|
4733
|
+
console.log(chalk.cyan(` Codex CLI: ${codexBaseUrl}`));
|
|
4297
4734
|
console.log(chalk.gray(` 模型: ${codexModel.name}`));
|
|
4298
4735
|
console.log(chalk.gray(' API Key: 已设置'));
|
|
4299
4736
|
console.log(chalk.gray(' 同步: Claude Code settings, Opencode config, Codex CLI config'));
|
|
4737
|
+
printYunyiOpenClawSwitchHint(yunyiLayoutResult);
|
|
4300
4738
|
console.log('');
|
|
4301
4739
|
}
|
|
4302
4740
|
|
|
@@ -4436,6 +4874,7 @@ function getConfigStatusLine(paths) {
|
|
|
4436
4874
|
const primaryProvider = primary.split('/')[0] || '';
|
|
4437
4875
|
|
|
4438
4876
|
const KNOWN_PROVIDERS = {
|
|
4877
|
+
'yunyi-claude': 'Claude(包月)',
|
|
4439
4878
|
'claude-yunyi': 'Claude(包月)',
|
|
4440
4879
|
'yunyi': 'Codex(包月)',
|
|
4441
4880
|
'heibai': 'MAXAPI(按量)',
|
|
@@ -4555,14 +4994,24 @@ async function selectNode(paths, type) {
|
|
|
4555
4994
|
config.agents.defaults.models[modelKey] = { alias: apiConfig.providerName };
|
|
4556
4995
|
config.agents.defaults.model.primary = modelKey;
|
|
4557
4996
|
config.agents.defaults.model.fallbacks = (config.agents.defaults.model.fallbacks || []).filter(k => k !== modelKey);
|
|
4997
|
+
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
|
|
4998
|
+
force: true,
|
|
4999
|
+
endpointUrl: selectedEndpoint.url,
|
|
5000
|
+
apiKey: oldApiKey || ''
|
|
5001
|
+
});
|
|
4558
5002
|
|
|
4559
5003
|
ensureGatewaySettings(config);
|
|
4560
5004
|
writeConfigWithSync(paths, config);
|
|
5005
|
+
const managedClaudeKey = String(config.models.providers?.[YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER]?.apiKey || '').trim();
|
|
5006
|
+
const managedGptKey = String(config.models.providers?.[YYMAXAPI_OPENCLAW_GPT_PROVIDER]?.apiKey || '').trim();
|
|
5007
|
+
if (managedClaudeKey) updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER, managedClaudeKey);
|
|
5008
|
+
if (managedGptKey) updateAuthProfilesWithSync(paths, YYMAXAPI_OPENCLAW_GPT_PROVIDER, managedGptKey);
|
|
4561
5009
|
|
|
4562
5010
|
console.log(chalk.green(`\n✅ ${typeLabel} 节点配置完成!`));
|
|
4563
5011
|
console.log(chalk.cyan(` 节点: ${selectedEndpoint.name} (${selectedEndpoint.url})`));
|
|
4564
5012
|
console.log(chalk.gray(` 模型: ${modelConfig.name} (主模型)`));
|
|
4565
5013
|
console.log(chalk.gray(` API Key: ${oldApiKey ? '已设置' : '未设置'}`));
|
|
5014
|
+
printYunyiOpenClawSwitchHint(yunyiLayoutResult);
|
|
4566
5015
|
}
|
|
4567
5016
|
|
|
4568
5017
|
// ============ 激活 (Claude/Codex) ============
|
|
@@ -4593,6 +5042,11 @@ async function activate(paths, type) {
|
|
|
4593
5042
|
const modelKey = `${apiConfig.providerName}/${modelConfig.id}`;
|
|
4594
5043
|
config.agents.defaults.model.primary = modelKey;
|
|
4595
5044
|
config.agents.defaults.model.fallbacks = [];
|
|
5045
|
+
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
|
|
5046
|
+
force: true,
|
|
5047
|
+
endpointUrl: stripManagedYunyiSuffix(provider.baseUrl),
|
|
5048
|
+
apiKey
|
|
5049
|
+
});
|
|
4596
5050
|
|
|
4597
5051
|
writeConfigWithSync(paths, config);
|
|
4598
5052
|
|
|
@@ -4604,12 +5058,17 @@ async function activate(paths, type) {
|
|
|
4604
5058
|
|
|
4605
5059
|
const authStore = readAuthStore(paths.authProfiles);
|
|
4606
5060
|
authStore.profiles[`${apiConfig.providerName}:default`] = { type: 'api_key', key: apiKey.trim(), provider: apiConfig.providerName };
|
|
5061
|
+
if (yunyiLayoutResult.applied) {
|
|
5062
|
+
authStore.profiles[`${YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER}:default`] = { type: 'api_key', key: apiKey.trim(), provider: YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER };
|
|
5063
|
+
authStore.profiles[`${YYMAXAPI_OPENCLAW_GPT_PROVIDER}:default`] = { type: 'api_key', key: apiKey.trim(), provider: YYMAXAPI_OPENCLAW_GPT_PROVIDER };
|
|
5064
|
+
}
|
|
4607
5065
|
writeAuthStore(paths.authProfiles, authStore);
|
|
4608
5066
|
|
|
4609
5067
|
console.log(chalk.green(`\n✅ 已激活 ${typeLabel}`));
|
|
4610
5068
|
console.log(chalk.cyan(` 节点: ${provider.baseUrl}`));
|
|
4611
5069
|
console.log(chalk.gray(` 模型: ${modelConfig.name}`));
|
|
4612
5070
|
console.log(chalk.gray(` API Key: 已设置`));
|
|
5071
|
+
printYunyiOpenClawSwitchHint(yunyiLayoutResult);
|
|
4613
5072
|
}
|
|
4614
5073
|
|
|
4615
5074
|
|
|
@@ -4628,6 +5087,7 @@ async function switchModel(paths) {
|
|
|
4628
5087
|
}
|
|
4629
5088
|
|
|
4630
5089
|
const PROVIDER_LABELS = {
|
|
5090
|
+
'yunyi-claude': '云翼 Claude (包月)',
|
|
4631
5091
|
'claude-yunyi': '云翼 Claude (包月)',
|
|
4632
5092
|
'yunyi': '云翼 Codex (包月)',
|
|
4633
5093
|
'heibai': 'MAXAPI (按量)',
|
|
@@ -5105,7 +5565,7 @@ async function testConnection(paths, args = {}) {
|
|
|
5105
5565
|
console.log(chalk.cyan(` CLI 对话正常,Gateway 可达`));
|
|
5106
5566
|
console.log(chalk.gray(` 注: Gateway Dashboard 通过 WebSocket 通信,REST 端点返回 405 属正常现象`));
|
|
5107
5567
|
console.log(chalk.green(`\n🌐 Web Dashboard 访问地址:`));
|
|
5108
|
-
console.log(chalk.cyan(` http://127.0.0.1:${gatewayPort}
|
|
5568
|
+
console.log(chalk.cyan(` http://127.0.0.1:${gatewayPort}/#token=${gatewayToken}`));
|
|
5109
5569
|
} else if (result.success) {
|
|
5110
5570
|
console.log(chalk.green(`\n✅ Gateway 测试成功!Web Dashboard 可正常使用`));
|
|
5111
5571
|
console.log(chalk.cyan(` 响应时间: ${latency}ms`));
|
|
@@ -5116,7 +5576,7 @@ async function testConnection(paths, args = {}) {
|
|
|
5116
5576
|
});
|
|
5117
5577
|
console.log(chalk.yellow(` 模型回复: ${reply}`));
|
|
5118
5578
|
console.log(chalk.green(`\n🌐 Web Dashboard 访问地址:`));
|
|
5119
|
-
console.log(chalk.cyan(` http://127.0.0.1:${gatewayPort}
|
|
5579
|
+
console.log(chalk.cyan(` http://127.0.0.1:${gatewayPort}/#token=${gatewayToken}`));
|
|
5120
5580
|
} else {
|
|
5121
5581
|
if (result.reachable) {
|
|
5122
5582
|
console.log(chalk.yellow(`\n⚠ Gateway HTTP 可达,但接口返回限制`));
|
|
@@ -5130,7 +5590,7 @@ async function testConnection(paths, args = {}) {
|
|
|
5130
5590
|
console.log(chalk.yellow(` 这通常会导致 Dashboard 网页对话返回 401。`));
|
|
5131
5591
|
console.log(chalk.gray(`\n 建议操作:`));
|
|
5132
5592
|
console.log(chalk.gray(` 1) 复制最新地址并重新打开浏览器(不要用旧书签)`));
|
|
5133
|
-
console.log(chalk.cyan(` http://127.0.0.1:${gatewayPort}
|
|
5593
|
+
console.log(chalk.cyan(` http://127.0.0.1:${gatewayPort}/#token=${gatewayToken}`));
|
|
5134
5594
|
console.log(chalk.gray(` 2) 执行 Gateway 重启:openclaw gateway restart / clawdbot gateway restart`));
|
|
5135
5595
|
console.log(chalk.gray(` 3) 若仍 401,检查是否存在多个配置目录(.openclaw 与 .clawdbot)`));
|
|
5136
5596
|
}
|