free-coding-models 0.3.5 → 0.3.9

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.
@@ -171,6 +171,7 @@ export function createKeyHandler(ctx) {
171
171
  saveAsProfile,
172
172
  setActiveProfile,
173
173
  saveConfig,
174
+ persistApiKeysForProvider,
174
175
  getConfiguredInstallableProviders,
175
176
  getInstallTargetModes,
176
177
  getProviderCatalogModels,
@@ -405,7 +406,7 @@ export function createKeyHandler(ctx) {
405
406
  })
406
407
  setActiveProfile(state.config, name)
407
408
  state.activeProfile = name
408
- saveConfig(state.config)
409
+ saveConfig(state.config, { replaceProfileNames: [name] })
409
410
  }
410
411
  state.profileSaveMode = false
411
412
  state.profileSaveBuffer = ''
@@ -985,14 +986,24 @@ const updateRowIdx = providerKeys.length
985
986
  setTimeout(() => { state.settingsErrorMsg = null }, 3000)
986
987
  return
987
988
  }
989
+ if (!state.config.apiKeys || typeof state.config.apiKeys !== 'object' || Array.isArray(state.config.apiKeys)) {
990
+ state.config.apiKeys = {}
991
+ }
988
992
  if (state.settingsAddKeyMode) {
989
993
  // 📖 Add-key mode: append new key (addApiKey handles duplicates/empty)
990
994
  addApiKey(state.config, pk, newKey)
991
995
  } else {
992
- // 📖 Edit mode: replace the primary key (string-level)
993
- state.config.apiKeys[pk] = newKey
996
+ // 📖 Edit mode: replace only the primary key and keep any extra rotated keys intact.
997
+ const existingKeys = resolveApiKeys(state.config, pk)
998
+ state.config.apiKeys[pk] = existingKeys.length > 1
999
+ ? [newKey, ...existingKeys.slice(1)]
1000
+ : newKey
1001
+ }
1002
+ const saveResult = persistApiKeysForProvider(state.config, pk)
1003
+ if (!saveResult.success) {
1004
+ state.settingsErrorMsg = `⚠️ Failed to persist ${pk} API key: ${saveResult.error || 'Unknown error'}`
1005
+ setTimeout(() => { state.settingsErrorMsg = null }, 4000)
994
1006
  }
995
- saveConfig(state.config)
996
1007
  }
997
1008
  state.settingsEditMode = false
998
1009
  state.settingsAddKeyMode = false
@@ -1103,7 +1114,7 @@ const updateRowIdx = providerKeys.length
1103
1114
  if (state.settingsCursor === widthWarningRowIdx) {
1104
1115
  if (!state.config.settings) state.config.settings = {}
1105
1116
  state.config.settings.disableWidthsWarning = !state.config.settings.disableWidthsWarning
1106
- saveConfig(state.config)
1117
+ saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1107
1118
  return
1108
1119
  }
1109
1120
 
@@ -1156,7 +1167,10 @@ const updateRowIdx = providerKeys.length
1156
1167
  applyTierFilter()
1157
1168
  const visible = state.results.filter(r => !r.hidden)
1158
1169
  state.visibleSorted = sortResultsWithPinnedFavorites(visible, state.sortColumn, state.sortDirection)
1159
- saveConfig(state.config)
1170
+ saveConfig(state.config, {
1171
+ replaceApiKeys: true,
1172
+ replaceFavorites: true,
1173
+ })
1160
1174
  }
1161
1175
  }
1162
1176
  return
@@ -1164,7 +1178,7 @@ const updateRowIdx = providerKeys.length
1164
1178
 
1165
1179
  // 📖 Enter edit mode for the selected provider's key
1166
1180
  const pk = providerKeys[state.settingsCursor]
1167
- state.settingsEditBuffer = state.config.apiKeys?.[pk] ?? ''
1181
+ state.settingsEditBuffer = resolveApiKeys(state.config, pk)[0] ?? ''
1168
1182
  state.settingsEditMode = true
1169
1183
  return
1170
1184
  }
@@ -1176,7 +1190,7 @@ const updateRowIdx = providerKeys.length
1176
1190
  if (state.settingsCursor === widthWarningRowIdx) {
1177
1191
  if (!state.config.settings) state.config.settings = {}
1178
1192
  state.config.settings.disableWidthsWarning = !state.config.settings.disableWidthsWarning
1179
- saveConfig(state.config)
1193
+ saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1180
1194
  return
1181
1195
  }
1182
1196
  // 📖 Profile rows don't respond to Space
@@ -1187,7 +1201,7 @@ const updateRowIdx = providerKeys.length
1187
1201
  if (!state.config.providers) state.config.providers = {}
1188
1202
  if (!state.config.providers[pk]) state.config.providers[pk] = { enabled: true }
1189
1203
  state.config.providers[pk].enabled = !isProviderEnabled(state.config, pk)
1190
- saveConfig(state.config)
1204
+ saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1191
1205
  return
1192
1206
  }
1193
1207
 
@@ -1218,7 +1232,7 @@ const updateRowIdx = providerKeys.length
1218
1232
  setActiveProfile(state.config, null)
1219
1233
  state.activeProfile = null
1220
1234
  }
1221
- saveConfig(state.config)
1235
+ saveConfig(state.config, { removedProfileNames: [profileName] })
1222
1236
  // 📖 Re-clamp cursor after deletion (profile list just got shorter)
1223
1237
  const newProfiles = listProfiles(state.config)
1224
1238
  const newMaxRowIdx = newProfiles.length > 0 ? profileStartIdx + newProfiles.length - 1 : changelogViewRowIdx
@@ -1285,7 +1299,11 @@ const updateRowIdx = providerKeys.length
1285
1299
  const pk = providerKeys[state.settingsCursor]
1286
1300
  const removed = removeApiKey(state.config, pk) // removes last key; collapses array-of-1 to string
1287
1301
  if (removed) {
1288
- saveConfig(state.config)
1302
+ const saveResult = persistApiKeysForProvider(state.config, pk)
1303
+ if (!saveResult.success) {
1304
+ state.settingsSyncStatus = { type: 'error', msg: `❌ Failed to save API key changes: ${saveResult.error || 'Unknown error'}` }
1305
+ return
1306
+ }
1289
1307
  const remaining = resolveApiKeys(state.config, pk).length
1290
1308
  const msg = remaining > 0
1291
1309
  ? `✅ Removed one key for ${pk} (${remaining} remaining)`
@@ -1325,7 +1343,7 @@ const updateRowIdx = providerKeys.length
1325
1343
  }
1326
1344
  if (!state.config.settings) state.config.settings = {}
1327
1345
  state.config.settings.proxy = { ...proxySettings, preferredPort: parsed }
1328
- saveConfig(state.config)
1346
+ saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1329
1347
  state.settingsProxyPortEditMode = false
1330
1348
  state.settingsProxyPortBuffer = ''
1331
1349
  state.proxyDaemonMessage = { type: 'success', msg: `✅ Preferred port saved: ${parsed === 0 ? 'auto' : parsed}`, ts: Date.now() }
@@ -1366,7 +1384,7 @@ const updateRowIdx = providerKeys.length
1366
1384
  if (state.proxyDaemonCursor === ROW_PROXY_ENABLED) {
1367
1385
  if (!state.config.settings) state.config.settings = {}
1368
1386
  state.config.settings.proxy = { ...proxySettings, enabled: !proxySettings.enabled }
1369
- saveConfig(state.config)
1387
+ saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1370
1388
  state.proxyDaemonMessage = { type: 'success', msg: `✅ Proxy mode ${state.config.settings.proxy.enabled ? 'enabled' : 'disabled'}`, ts: Date.now() }
1371
1389
  return
1372
1390
  }
@@ -1377,7 +1395,7 @@ const updateRowIdx = providerKeys.length
1377
1395
  }
1378
1396
  if (!state.config.settings) state.config.settings = {}
1379
1397
  state.config.settings.proxy = { ...proxySettings, syncToOpenCode: !proxySettings.syncToOpenCode }
1380
- saveConfig(state.config)
1398
+ saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1381
1399
  const { getToolMeta } = await import('./tool-metadata.js')
1382
1400
  const toolLabel = getToolMeta(currentProxyTool).label
1383
1401
  state.proxyDaemonMessage = { type: 'success', msg: `✅ Auto-sync to ${toolLabel} ${state.config.settings.proxy.syncToOpenCode ? 'enabled' : 'disabled'}`, ts: Date.now() }
@@ -1393,7 +1411,7 @@ const updateRowIdx = providerKeys.length
1393
1411
  if (state.proxyDaemonCursor === ROW_PROXY_ENABLED) {
1394
1412
  if (!state.config.settings) state.config.settings = {}
1395
1413
  state.config.settings.proxy = { ...proxySettings, enabled: !proxySettings.enabled }
1396
- saveConfig(state.config)
1414
+ saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1397
1415
  state.proxyDaemonMessage = { type: 'success', msg: `✅ Proxy mode ${state.config.settings.proxy.enabled ? 'enabled' : 'disabled'}`, ts: Date.now() }
1398
1416
  return
1399
1417
  }
@@ -1406,7 +1424,7 @@ const updateRowIdx = providerKeys.length
1406
1424
  }
1407
1425
  if (!state.config.settings) state.config.settings = {}
1408
1426
  state.config.settings.proxy = { ...proxySettings, syncToOpenCode: !proxySettings.syncToOpenCode }
1409
- saveConfig(state.config)
1427
+ saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1410
1428
  const { getToolMeta } = await import('./tool-metadata.js')
1411
1429
  const toolLabel = getToolMeta(currentProxyTool).label
1412
1430
  state.proxyDaemonMessage = { type: 'success', msg: `✅ Auto-sync to ${toolLabel} ${state.config.settings.proxy.syncToOpenCode ? 'enabled' : 'disabled'}`, ts: Date.now() }
@@ -1456,7 +1474,7 @@ const updateRowIdx = providerKeys.length
1456
1474
  if (!state.config.settings.proxy.preferredPort || state.config.settings.proxy.preferredPort === 0) {
1457
1475
  state.config.settings.proxy.preferredPort = 18045
1458
1476
  }
1459
- saveConfig(state.config)
1477
+ saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1460
1478
  const result = installDaemon()
1461
1479
  if (result.success) {
1462
1480
  state.proxyDaemonMessage = { type: 'success', msg: '✅ FCM Proxy V2 background service installed and started!', ts: Date.now() }
@@ -1470,7 +1488,7 @@ const updateRowIdx = providerKeys.length
1470
1488
  // 📖 Uninstall daemon
1471
1489
  const result = uninstallDaemon()
1472
1490
  state.config.settings.proxy.daemonEnabled = false
1473
- saveConfig(state.config)
1491
+ saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1474
1492
  if (result.success) {
1475
1493
  state.proxyDaemonMessage = { type: 'success', msg: '✅ FCM Proxy V2 background service uninstalled.', ts: Date.now() }
1476
1494
  state.daemonStatus = 'not-installed'
@@ -1629,7 +1647,7 @@ const updateRowIdx = providerKeys.length
1629
1647
  })
1630
1648
  setActiveProfile(state.config, 'default')
1631
1649
  state.activeProfile = 'default'
1632
- saveConfig(state.config)
1650
+ saveConfig(state.config, { replaceProfileNames: ['default'] })
1633
1651
  } else {
1634
1652
  // 📖 Cycle to next profile (or back to null = raw config)
1635
1653
  const currentIdx = state.activeProfile ? profiles.indexOf(state.activeProfile) : -1
@@ -1662,7 +1680,10 @@ const updateRowIdx = providerKeys.length
1662
1680
  state.visibleSorted = sortResultsWithPinnedFavorites(visible, state.sortColumn, state.sortDirection)
1663
1681
  state.cursor = 0
1664
1682
  state.scrollOffset = 0
1665
- saveConfig(state.config)
1683
+ saveConfig(state.config, {
1684
+ replaceApiKeys: true,
1685
+ replaceFavorites: true,
1686
+ })
1666
1687
  }
1667
1688
  }
1668
1689
  }
@@ -1693,7 +1714,7 @@ const updateRowIdx = providerKeys.length
1693
1714
  profile.settings.sortColumn = state.config.settings.sortColumn
1694
1715
  profile.settings.sortAsc = state.config.settings.sortAsc
1695
1716
  }
1696
- saveConfig(state.config)
1717
+ saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1697
1718
  }
1698
1719
 
1699
1720
  // 📖 Shift+R: reset all UI view settings to defaults (tier, sort, provider) and clear persisted config
@@ -1717,7 +1738,7 @@ const updateRowIdx = providerKeys.length
1717
1738
  delete profile.settings.sortAsc
1718
1739
  }
1719
1740
  }
1720
- saveConfig(state.config)
1741
+ saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1721
1742
  applyTierFilter()
1722
1743
  const visible = state.results.filter(r => !r.hidden)
1723
1744
  state.visibleSorted = sortResultsWithPinnedFavorites(visible, state.sortColumn, state.sortDirection)
@@ -1824,7 +1845,7 @@ const updateRowIdx = providerKeys.length
1824
1845
  if (!profile.settings || typeof profile.settings !== 'object') profile.settings = {}
1825
1846
  profile.settings.hideUnconfiguredModels = state.hideUnconfiguredModels
1826
1847
  }
1827
- saveConfig(state.config)
1848
+ saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1828
1849
  applyTierFilter()
1829
1850
  const visible = state.results.filter(r => !r.hidden)
1830
1851
  state.visibleSorted = sortResultsWithPinnedFavorites(visible, state.sortColumn, state.sortDirection)
@@ -1891,7 +1912,7 @@ const updateRowIdx = providerKeys.length
1891
1912
  if (!profile.settings || typeof profile.settings !== 'object') profile.settings = {}
1892
1913
  profile.settings.preferredToolMode = state.mode
1893
1914
  }
1894
- saveConfig(state.config)
1915
+ saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1895
1916
  return
1896
1917
  }
1897
1918
 
package/src/overlays.js CHANGED
@@ -736,7 +736,8 @@ const profileStartIdx = updateRowIdx + 5
736
736
  } catch { /* keep raw */ }
737
737
 
738
738
  const requestedModelLabel = row.requestedModel || ''
739
- const displayModel = row.switched && requestedModelLabel && requestedModelLabel !== row.model
739
+ // 📖 Always show "requested actual" if they differ, not just when switched
740
+ const displayModel = requestedModelLabel && requestedModelLabel !== row.model
740
741
  ? `${requestedModelLabel} → ${row.model}`
741
742
  : row.model
742
743
 
@@ -781,9 +782,11 @@ const profileStartIdx = updateRowIdx + 5
781
782
  const isFailedWithZeroTokens = row.status !== '200' && (!row.tokens || Number(row.tokens) === 0)
782
783
 
783
784
  const timeCell = chalk.dim(timeStr.slice(0, W_TIME).padEnd(W_TIME))
784
- // 📖 Color provider the same way as in the main table (row.provider is already the providerKey, e.g. "nvidia")
785
+ // 📖 Provider display: Use pretty label if available, otherwise raw key.
786
+ // 📖 All these logs are from FCM Proxy V2.
787
+ const providerLabel = PROVIDER_METADATA[row.provider]?.label || row.provider
785
788
  const providerRgb = PROVIDER_COLOR[row.provider] ?? [105, 190, 245]
786
- const provCell = chalk.bold.rgb(...providerRgb)(row.provider.slice(0, W_PROV).padEnd(W_PROV))
789
+ const provCell = chalk.bold.rgb(...providerRgb)(providerLabel.slice(0, W_PROV).padEnd(W_PROV))
787
790
 
788
791
  // 📖 Color model based on status - red for failed requests with zero tokens
789
792
  let modelCell