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.
Files changed (2) hide show
  1. package/bin/yymaxapi.js +559 -26
  2. 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-yunyi"
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(128);
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
- return header.startsWith('#!') && header.includes('node');
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
- writeConfig(paths.openclawConfig, config);
2770
- console.log(chalk.green('✓ 已自动修复配置(模型结构 / API 协议)'));
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(` 模型: ${selectedModel.name} (${primaryLabel})`));
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
- await testConnection(paths, args);
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 (主): ${codexBaseUrl}`));
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
- let primary = config.agents?.defaults?.model?.primary || '';
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 = primary.split('/')[0];
4900
- const provider = config.models?.providers?.[providerName];
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 apiType = provider.api || '';
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(`当前激活: ${typeLabel}`));
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 = config.agents?.defaults?.model?.fallbacks || [];
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} agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120`;
6052
+ const agentCmd = `${wslCli} ${subcommand}`;
5524
6053
  cmd = `wsl -- bash -c '${agentCmd.replace(/'/g, "'\\''")}'`;
5525
6054
  } else {
5526
- const agentCmd = `openclaw agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120 2>/dev/null || clawdbot agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120 2>/dev/null || moltbot agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120`;
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 = useNode
5545
- ? `"${nodeInfo.path}" "${cliBinary}" agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120`
5546
- : `"${cliBinary}" agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120`;
6073
+ cmd = buildGatewayAgentCliCommand({
6074
+ cliBinary,
6075
+ nodeInfo,
6076
+ useNode,
6077
+ sessionId,
6078
+ agentId
6079
+ });
5547
6080
  execOpts = { timeout: 120000, env };
5548
6081
  }
5549
6082
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yymaxapi",
3
- "version": "1.0.84",
3
+ "version": "1.0.86",
4
4
  "description": "跨平台 OpenClaw/Clawdbot 配置管理工具 - 管理中转地址、模型切换、API Keys、测速优化",
5
5
  "main": "bin/yymaxapi.js",
6
6
  "bin": {