yymaxapi 1.0.88 → 1.0.89

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 +207 -49
  2. package/package.json +1 -1
package/bin/yymaxapi.js CHANGED
@@ -2011,6 +2011,150 @@ function findAgentById(config, agentId) {
2011
2011
  return config.agents.list.find(agent => agent && typeof agent === 'object' && agent.id === agentId) || null;
2012
2012
  }
2013
2013
 
2014
+ function canonicalizeManagedYunyiModelKey(modelKey, preferredType = '') {
2015
+ const text = String(modelKey || '').trim();
2016
+ if (!text.includes('/')) return text;
2017
+ const [, modelId = ''] = text.split('/');
2018
+ if (!modelId) return text;
2019
+ if (preferredType === 'claude' || (!preferredType && isManagedYunyiClaudeModelKey(text))) {
2020
+ return `${YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER}/${modelId}`;
2021
+ }
2022
+ if (preferredType === 'codex' || preferredType === 'gpt' || (!preferredType && isManagedYunyiGptModelKey(text))) {
2023
+ return `${YYMAXAPI_OPENCLAW_GPT_PROVIDER}/${modelId}`;
2024
+ }
2025
+ return text;
2026
+ }
2027
+
2028
+ function getManagedYunyiKnownModelKeys(type) {
2029
+ return type === 'claude'
2030
+ ? [YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY, YYMAXAPI_OPENCLAW_CLAUDE_FALLBACK]
2031
+ : [YYMAXAPI_OPENCLAW_GPT_PRIMARY, YYMAXAPI_OPENCLAW_GPT_FALLBACK];
2032
+ }
2033
+
2034
+ function normalizeManagedYunyiModelState(type, modelState = {}, options = {}) {
2035
+ const knownKeys = getManagedYunyiKnownModelKeys(type);
2036
+ const fallbackPrimary = knownKeys[0];
2037
+ const currentPrimary = canonicalizeManagedYunyiModelKey(modelState.primary || '', type);
2038
+ const canPreserve = !options.force && currentPrimary && knownKeys.includes(currentPrimary);
2039
+ const primary = canPreserve ? currentPrimary : fallbackPrimary;
2040
+
2041
+ let fallbacks = Array.isArray(modelState.fallbacks) ? modelState.fallbacks : [];
2042
+ fallbacks = [...new Set(
2043
+ fallbacks
2044
+ .map(item => canonicalizeManagedYunyiModelKey(item, type))
2045
+ .filter(item => item && item !== primary && knownKeys.includes(item))
2046
+ )];
2047
+
2048
+ if (fallbacks.length === 0) {
2049
+ fallbacks = knownKeys.filter(item => item !== primary);
2050
+ }
2051
+
2052
+ return { primary, fallbacks };
2053
+ }
2054
+
2055
+ function normalizeManagedYunyiDefaultsModel(modelState = {}, options = {}) {
2056
+ const currentPrimary = String(modelState.primary || '').trim();
2057
+ const canPreserve = !options.force && currentPrimary && isManagedYunyiModelKey(currentPrimary);
2058
+ const primary = canPreserve
2059
+ ? canonicalizeManagedYunyiModelKey(currentPrimary)
2060
+ : YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY;
2061
+
2062
+ let fallbacks = Array.isArray(modelState.fallbacks) ? modelState.fallbacks : [];
2063
+ fallbacks = [...new Set(
2064
+ fallbacks
2065
+ .map(item => {
2066
+ const text = String(item || '').trim();
2067
+ return isManagedYunyiModelKey(text) ? canonicalizeManagedYunyiModelKey(text) : text;
2068
+ })
2069
+ .filter(item => item && item !== primary)
2070
+ )];
2071
+
2072
+ if (fallbacks.length === 0) {
2073
+ fallbacks = [isManagedYunyiClaudeModelKey(primary) ? YYMAXAPI_OPENCLAW_GPT_PRIMARY : YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY];
2074
+ }
2075
+
2076
+ return { primary, fallbacks };
2077
+ }
2078
+
2079
+ function getManagedClaudeAgentId(config) {
2080
+ const mainAgent = findAgentById(config, YYMAXAPI_OPENCLAW_MAIN_AGENT_ID);
2081
+ if (mainAgent && isManagedMainAgent(mainAgent)) {
2082
+ return YYMAXAPI_OPENCLAW_MAIN_AGENT_ID;
2083
+ }
2084
+
2085
+ const sideAgent = findAgentById(config, YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID);
2086
+ if (sideAgent && isManagedClaudeSideAgent(sideAgent)) {
2087
+ return YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID;
2088
+ }
2089
+
2090
+ return mainAgent ? YYMAXAPI_OPENCLAW_MAIN_AGENT_ID : (sideAgent ? YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID : null);
2091
+ }
2092
+
2093
+ function inferManagedYunyiAgentIdForModelKey(config, modelKey) {
2094
+ if (isManagedYunyiGptModelKey(modelKey)) return YYMAXAPI_OPENCLAW_GPT_AGENT_ID;
2095
+ if (isManagedYunyiClaudeModelKey(modelKey)) return getManagedClaudeAgentId(config);
2096
+ return null;
2097
+ }
2098
+
2099
+ function applyManagedYunyiModelSelection(config, selectedModelKey) {
2100
+ ensureConfigStructure(config);
2101
+
2102
+ const normalizedSelected = canonicalizeManagedYunyiModelKey(selectedModelKey);
2103
+ const selectedType = isManagedYunyiClaudeModelKey(normalizedSelected)
2104
+ ? 'claude'
2105
+ : (isManagedYunyiGptModelKey(normalizedSelected) ? 'codex' : '');
2106
+
2107
+ if (!selectedType) {
2108
+ return { changed: false, selectedModelKey: normalizedSelected, agentId: null };
2109
+ }
2110
+
2111
+ let changed = false;
2112
+ const previousPrimary = String(config.agents?.defaults?.model?.primary || '').trim();
2113
+ const previousCanonical = isManagedYunyiModelKey(previousPrimary)
2114
+ ? canonicalizeManagedYunyiModelKey(previousPrimary)
2115
+ : previousPrimary;
2116
+ const nextDefaultsModel = {
2117
+ primary: normalizedSelected,
2118
+ fallbacks: previousCanonical && previousCanonical !== normalizedSelected ? [previousCanonical] : []
2119
+ };
2120
+
2121
+ if (JSON.stringify(config.agents.defaults.model) !== JSON.stringify(nextDefaultsModel)) {
2122
+ config.agents.defaults.model = nextDefaultsModel;
2123
+ changed = true;
2124
+ }
2125
+
2126
+ const managedModelAliases = {
2127
+ [YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY]: YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER,
2128
+ [YYMAXAPI_OPENCLAW_CLAUDE_FALLBACK]: YYMAXAPI_OPENCLAW_CLAUDE_PROVIDER,
2129
+ [YYMAXAPI_OPENCLAW_GPT_PRIMARY]: YYMAXAPI_OPENCLAW_GPT_PROVIDER,
2130
+ [YYMAXAPI_OPENCLAW_GPT_FALLBACK]: YYMAXAPI_OPENCLAW_GPT_PROVIDER
2131
+ };
2132
+ for (const [modelKey, alias] of Object.entries(managedModelAliases)) {
2133
+ const nextEntry = { alias };
2134
+ if (JSON.stringify(config.agents.defaults.models[modelKey] || {}) !== JSON.stringify(nextEntry)) {
2135
+ config.agents.defaults.models[modelKey] = nextEntry;
2136
+ changed = true;
2137
+ }
2138
+ }
2139
+
2140
+ const agentId = inferManagedYunyiAgentIdForModelKey(config, normalizedSelected)
2141
+ || (selectedType === 'codex' ? YYMAXAPI_OPENCLAW_GPT_AGENT_ID : YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID);
2142
+ const agentList = ensureAgentList(config);
2143
+ const nextAgent = {
2144
+ id: agentId,
2145
+ default: agentId === YYMAXAPI_OPENCLAW_MAIN_AGENT_ID,
2146
+ name: selectedType === 'codex' ? 'yunyi-gpt' : 'yunyi-claude',
2147
+ model: normalizeManagedYunyiModelState(selectedType, { primary: normalizedSelected })
2148
+ };
2149
+ const shouldManage = selectedType === 'codex'
2150
+ ? isManagedGptAgent
2151
+ : (agentId === YYMAXAPI_OPENCLAW_MAIN_AGENT_ID ? isManagedMainAgent : isManagedClaudeSideAgent);
2152
+ const updateResult = upsertManagedAgent(agentList, nextAgent, shouldManage);
2153
+ if (updateResult.changed) changed = true;
2154
+
2155
+ return { changed, selectedModelKey: normalizedSelected, agentId };
2156
+ }
2157
+
2014
2158
  function resolveOpenClawTestTarget(config, args = {}) {
2015
2159
  const requestedAgentId = String(args.agent || args['agent-id'] || '').trim();
2016
2160
  const requestedAgent = requestedAgentId ? findAgentById(config, requestedAgentId) : null;
@@ -2027,11 +2171,23 @@ function resolveOpenClawTestTarget(config, args = {}) {
2027
2171
  if (requestedAgent) {
2028
2172
  const agentState = getAgentModelState(requestedAgent);
2029
2173
  if (agentState.primary) {
2030
- primary = agentState.primary;
2174
+ primary = canonicalizeManagedYunyiModelKey(agentState.primary);
2031
2175
  fallbacks = agentState.fallbacks;
2032
2176
  agentId = requestedAgent.id;
2033
2177
  agentName = String(requestedAgent.name || '').trim();
2034
2178
  }
2179
+ } else {
2180
+ const inferredAgentId = inferManagedYunyiAgentIdForModelKey(config, primary);
2181
+ const inferredAgent = inferredAgentId ? findAgentById(config, inferredAgentId) : null;
2182
+ if (inferredAgent) {
2183
+ const agentState = getAgentModelState(inferredAgent);
2184
+ if (agentState.primary) {
2185
+ primary = canonicalizeManagedYunyiModelKey(agentState.primary);
2186
+ fallbacks = agentState.fallbacks.map(item => isManagedYunyiModelKey(item) ? canonicalizeManagedYunyiModelKey(item) : item);
2187
+ }
2188
+ agentId = inferredAgent.id;
2189
+ agentName = String(inferredAgent.name || '').trim();
2190
+ }
2035
2191
  }
2036
2192
 
2037
2193
  const providerName = primary.includes('/') ? primary.split('/')[0] : '';
@@ -2293,28 +2449,24 @@ function applyManagedYunyiOpenClawLayout(config, options = {}) {
2293
2449
  }
2294
2450
 
2295
2451
  const agentList = ensureAgentList(config);
2452
+ const currentMainAgentState = getAgentModelState(findAgentById(config, YYMAXAPI_OPENCLAW_MAIN_AGENT_ID));
2296
2453
  const mainAgentResult = upsertManagedAgent(agentList, {
2297
2454
  id: YYMAXAPI_OPENCLAW_MAIN_AGENT_ID,
2298
2455
  default: true,
2299
2456
  name: 'yunyi-claude',
2300
- model: {
2301
- primary: YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY,
2302
- fallbacks: [YYMAXAPI_OPENCLAW_CLAUDE_FALLBACK]
2303
- }
2457
+ model: normalizeManagedYunyiModelState('claude', currentMainAgentState, options)
2304
2458
  }, isManagedMainAgent);
2305
2459
  if (mainAgentResult.changed) changed = true;
2306
2460
 
2307
2461
  let claudeAgentId = YYMAXAPI_OPENCLAW_MAIN_AGENT_ID;
2308
2462
  let preservedMain = !mainAgentResult.managed;
2309
2463
  if (!mainAgentResult.managed) {
2464
+ const currentClaudeSideState = getAgentModelState(findAgentById(config, YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID));
2310
2465
  const fallbackClaudeAgentResult = upsertManagedAgent(agentList, {
2311
2466
  id: YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID,
2312
2467
  default: false,
2313
2468
  name: 'yunyi-claude',
2314
- model: {
2315
- primary: YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY,
2316
- fallbacks: [YYMAXAPI_OPENCLAW_CLAUDE_FALLBACK]
2317
- }
2469
+ model: normalizeManagedYunyiModelState('claude', currentClaudeSideState, options)
2318
2470
  }, isManagedClaudeSideAgent);
2319
2471
  if (fallbackClaudeAgentResult.changed) changed = true;
2320
2472
  claudeAgentId = fallbackClaudeAgentResult.managed ? YYMAXAPI_OPENCLAW_ALT_CLAUDE_AGENT_ID : null;
@@ -2322,22 +2474,17 @@ function applyManagedYunyiOpenClawLayout(config, options = {}) {
2322
2474
  changed = true;
2323
2475
  }
2324
2476
 
2477
+ const currentGptAgentState = getAgentModelState(findAgentById(config, YYMAXAPI_OPENCLAW_GPT_AGENT_ID));
2325
2478
  const gptAgentResult = upsertManagedAgent(agentList, {
2326
2479
  id: YYMAXAPI_OPENCLAW_GPT_AGENT_ID,
2327
2480
  default: false,
2328
2481
  name: 'yunyi-gpt',
2329
- model: {
2330
- primary: YYMAXAPI_OPENCLAW_GPT_PRIMARY,
2331
- fallbacks: [YYMAXAPI_OPENCLAW_GPT_FALLBACK]
2332
- }
2482
+ model: normalizeManagedYunyiModelState('codex', currentGptAgentState, options)
2333
2483
  }, isManagedGptAgent);
2334
2484
  if (gptAgentResult.changed) changed = true;
2335
2485
 
2336
2486
  if (shouldManageYunyiDefaults(config)) {
2337
- const nextDefaultsModel = {
2338
- primary: YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY,
2339
- fallbacks: [YYMAXAPI_OPENCLAW_GPT_PRIMARY]
2340
- };
2487
+ const nextDefaultsModel = normalizeManagedYunyiDefaultsModel(config.agents.defaults.model, options);
2341
2488
  if (JSON.stringify(config.agents.defaults.model) !== JSON.stringify(nextDefaultsModel)) {
2342
2489
  config.agents.defaults.model = nextDefaultsModel;
2343
2490
  changed = true;
@@ -5097,7 +5244,8 @@ async function switchModel(paths) {
5097
5244
 
5098
5245
  const config = ensureConfigStructure(readConfig(paths.openclawConfig) || {});
5099
5246
  applyConfigRepairsWithSync(config, paths);
5100
- const primary = config.agents?.defaults?.model?.primary || '';
5247
+ applyManagedYunyiOpenClawLayout(config);
5248
+ let primary = config.agents?.defaults?.model?.primary || '';
5101
5249
  const providers = config.models?.providers || {};
5102
5250
 
5103
5251
  if (Object.keys(providers).length === 0) {
@@ -5195,6 +5343,7 @@ async function switchModel(paths) {
5195
5343
  }]);
5196
5344
 
5197
5345
  let finalSelected = selected;
5346
+ let selectedAgentId = '';
5198
5347
 
5199
5348
  if (selected === '__other__') {
5200
5349
  if (otherModels.length === 0) {
@@ -5225,42 +5374,51 @@ async function switchModel(paths) {
5225
5374
  return;
5226
5375
  }
5227
5376
 
5228
- const selectedProviderKey = finalSelected.split('/')[0];
5229
- const selectedModelId = finalSelected.split('/')[1];
5230
- config.agents.defaults.model.primary = finalSelected;
5231
- if (!config.agents.defaults.models[finalSelected]) {
5232
- config.agents.defaults.models[finalSelected] = { alias: selectedProviderKey };
5233
- }
5234
-
5235
- // 同步更新 provider.models
5236
- const providerConfig = providers[selectedProviderKey];
5237
- if (providerConfig && selectedModelId) {
5238
- const existingModel = (providerConfig.models || []).find(m => m.id === selectedModelId);
5239
- if (!existingModel) {
5240
- const allModels = [...CLAUDE_MODELS, ...CODEX_MODELS];
5241
- const knownModel = allModels.find(m => m.id === selectedModelId);
5242
- const apiType = providerConfig.api || '';
5243
- const isAnthropic = apiType.startsWith('anthropic');
5244
- const defaultCtx = isAnthropic ? (API_CONFIG.claude?.contextWindow || 200000) : (API_CONFIG.codex?.contextWindow || 200000);
5245
- const defaultMax = isAnthropic ? (API_CONFIG.claude?.maxTokens || 8192) : (API_CONFIG.codex?.maxTokens || 8192);
5246
- providerConfig.models = [{
5247
- id: selectedModelId,
5248
- name: knownModel ? knownModel.name : selectedModelId,
5249
- contextWindow: defaultCtx,
5250
- maxTokens: defaultMax
5251
- }];
5252
- } else {
5253
- providerConfig.models = [existingModel];
5377
+ if (isManagedYunyiModelKey(finalSelected)) {
5378
+ const selectionResult = applyManagedYunyiModelSelection(config, finalSelected);
5379
+ finalSelected = selectionResult.selectedModelKey;
5380
+ selectedAgentId = selectionResult.agentId || '';
5381
+ } else {
5382
+ const selectedProviderKey = finalSelected.split('/')[0];
5383
+ const selectedModelId = finalSelected.split('/')[1];
5384
+ config.agents.defaults.model.primary = finalSelected;
5385
+ if (!config.agents.defaults.models[finalSelected]) {
5386
+ config.agents.defaults.models[finalSelected] = { alias: selectedProviderKey };
5387
+ }
5388
+
5389
+ // 同步更新 provider.models
5390
+ const providerConfig = providers[selectedProviderKey];
5391
+ if (providerConfig && selectedModelId) {
5392
+ const existingModel = (providerConfig.models || []).find(m => m.id === selectedModelId);
5393
+ if (!existingModel) {
5394
+ const allModels = [...CLAUDE_MODELS, ...CODEX_MODELS];
5395
+ const knownModel = allModels.find(m => m.id === selectedModelId);
5396
+ const apiType = providerConfig.api || '';
5397
+ const isAnthropic = apiType.startsWith('anthropic');
5398
+ const defaultCtx = isAnthropic ? (API_CONFIG.claude?.contextWindow || 200000) : (API_CONFIG.codex?.contextWindow || 200000);
5399
+ const defaultMax = isAnthropic ? (API_CONFIG.claude?.maxTokens || 8192) : (API_CONFIG.codex?.maxTokens || 8192);
5400
+ providerConfig.models = [{
5401
+ id: selectedModelId,
5402
+ name: knownModel ? knownModel.name : selectedModelId,
5403
+ contextWindow: defaultCtx,
5404
+ maxTokens: defaultMax
5405
+ }];
5406
+ } else {
5407
+ providerConfig.models = [existingModel];
5408
+ }
5254
5409
  }
5255
- }
5256
5410
 
5257
- config.agents.defaults.model.fallbacks = (config.agents.defaults.model.fallbacks || []).filter(k => k !== finalSelected);
5258
- if (primary && primary !== finalSelected && !config.agents.defaults.model.fallbacks.includes(primary)) {
5259
- config.agents.defaults.model.fallbacks.push(primary);
5411
+ config.agents.defaults.model.fallbacks = (config.agents.defaults.model.fallbacks || []).filter(k => k !== finalSelected);
5412
+ if (primary && primary !== finalSelected && !config.agents.defaults.model.fallbacks.includes(primary)) {
5413
+ config.agents.defaults.model.fallbacks.push(primary);
5414
+ }
5260
5415
  }
5416
+
5417
+ primary = finalSelected;
5261
5418
  ensureGatewaySettings(config);
5262
5419
  writeConfigWithSync(paths, config);
5263
5420
 
5421
+ const selectedProviderKey = finalSelected.split('/')[0];
5264
5422
  const selectedProviderConfig = providers[selectedProviderKey];
5265
5423
  const selectedLabel = PROVIDER_LABELS[selectedProviderKey] || selectedProviderKey;
5266
5424
  console.log(chalk.green(`\n✅ 已切换到 ${finalSelected}`));
@@ -5286,7 +5444,7 @@ async function switchModel(paths) {
5286
5444
 
5287
5445
  // 重启后直接测试连接
5288
5446
  console.log('');
5289
- await testConnection(paths, {});
5447
+ await testConnection(paths, selectedAgentId ? { agent: selectedAgentId } : {});
5290
5448
  }
5291
5449
  // ============ 权限管理 (tools.profile) ============
5292
5450
  const TOOLS_PROFILES = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yymaxapi",
3
- "version": "1.0.88",
3
+ "version": "1.0.89",
4
4
  "description": "跨平台 OpenClaw/Clawdbot 配置管理工具 - 管理中转地址、模型切换、API Keys、测速优化",
5
5
  "main": "bin/yymaxapi.js",
6
6
  "bin": {