free-coding-models 0.3.6 β†’ 0.3.11

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.
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * @details
6
6
  * This module encapsulates the full onKeyPress switch used by the TUI,
7
- * including settings navigation, install-endpoint flow, overlays, profile management, and
7
+ * including settings navigation, install-endpoint flow, overlays, and
8
8
  * tool launch actions. It also keeps the live key bindings aligned with the
9
9
  * highlighted letters shown in the table headers.
10
10
  *
@@ -153,7 +153,7 @@ export function buildProviderTestDetail(providerLabel, outcome, attempts = [], d
153
153
  }
154
154
 
155
155
  export function createKeyHandler(ctx) {
156
- const {
156
+ const {
157
157
  state,
158
158
  exit,
159
159
  cliArgs,
@@ -165,11 +165,6 @@ export function createKeyHandler(ctx) {
165
165
  addApiKey,
166
166
  removeApiKey,
167
167
  isProviderEnabled,
168
- listProfiles,
169
- loadProfile,
170
- deleteProfile,
171
- saveAsProfile,
172
- setActiveProfile,
173
168
  saveConfig,
174
169
  persistApiKeysForProvider,
175
170
  getConfiguredInstallableProviders,
@@ -382,46 +377,7 @@ export function createKeyHandler(ctx) {
382
377
  if (!key) return
383
378
  noteUserActivity()
384
379
 
385
- // πŸ“– Profile save mode: intercept ALL keys while inline name input is active.
386
- // πŸ“– Enter β†’ save, Esc β†’ cancel, Backspace β†’ delete char, printable β†’ append to buffer.
387
- if (state.profileSaveMode) {
388
- if (key.ctrl && key.name === 'c') { exit(0); return }
389
- if (key.name === 'escape') {
390
- // πŸ“– Cancel profile save β€” discard typed name
391
- state.profileSaveMode = false
392
- state.profileSaveBuffer = ''
393
- return
394
- }
395
- if (key.name === 'return') {
396
- // πŸ“– Confirm profile save β€” persist current TUI settings under typed name
397
- const name = state.profileSaveBuffer.trim()
398
- if (name.length > 0) {
399
- saveAsProfile(state.config, name, {
400
- tierFilter: TIER_CYCLE[state.tierFilterMode],
401
- sortColumn: state.sortColumn,
402
- sortAsc: state.sortDirection === 'asc',
403
- pingInterval: state.pingInterval,
404
- hideUnconfiguredModels: state.hideUnconfiguredModels,
405
- proxy: getProxySettings(state.config),
406
- })
407
- setActiveProfile(state.config, name)
408
- state.activeProfile = name
409
- saveConfig(state.config, { replaceProfileNames: [name] })
410
- }
411
- state.profileSaveMode = false
412
- state.profileSaveBuffer = ''
413
- return
414
- }
415
- if (key.name === 'backspace') {
416
- state.profileSaveBuffer = state.profileSaveBuffer.slice(0, -1)
417
- return
418
- }
419
- // πŸ“– Append printable characters (str is the raw character typed)
420
- if (str && str.length === 1 && !key.ctrl && !key.meta) {
421
- state.profileSaveBuffer += str
422
- }
423
- return
424
- }
380
+ // πŸ“– Profile system removed - API keys now persist permanently across all sessions
425
381
 
426
382
  // πŸ“– Install Endpoints overlay: provider β†’ tool β†’ connection β†’ scope β†’ optional model subset.
427
383
  if (state.installEndpointsOpen) {
@@ -964,10 +920,8 @@ const updateRowIdx = providerKeys.length
964
920
  const widthWarningRowIdx = updateRowIdx + 1
965
921
  const proxyDaemonRowIdx = widthWarningRowIdx + 1
966
922
  const changelogViewRowIdx = proxyDaemonRowIdx + 1
967
- // πŸ“– Profile rows start after maintenance + width warning + proxy/daemon + changelog
968
- const savedProfiles = listProfiles(state.config)
969
- const profileStartIdx = updateRowIdx + 5
970
- const maxRowIdx = savedProfiles.length > 0 ? profileStartIdx + savedProfiles.length - 1 : changelogViewRowIdx
923
+ // πŸ“– Profile system removed - API keys now persist permanently across all sessions
924
+ const maxRowIdx = changelogViewRowIdx
971
925
 
972
926
  // πŸ“– Edit/Add-key mode: capture typed characters for the API key
973
927
  if (state.settingsEditMode || state.settingsAddKeyMode) {
@@ -1114,7 +1068,7 @@ const updateRowIdx = providerKeys.length
1114
1068
  if (state.settingsCursor === widthWarningRowIdx) {
1115
1069
  if (!state.config.settings) state.config.settings = {}
1116
1070
  state.config.settings.disableWidthsWarning = !state.config.settings.disableWidthsWarning
1117
- saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1071
+ saveConfig(state.config)
1118
1072
  return
1119
1073
  }
1120
1074
 
@@ -1146,35 +1100,7 @@ const updateRowIdx = providerKeys.length
1146
1100
  return
1147
1101
  }
1148
1102
 
1149
- // πŸ“– Profile row: Enter β†’ load the selected profile (apply its settings live)
1150
- if (state.settingsCursor >= profileStartIdx && savedProfiles.length > 0) {
1151
- const profileIdx = state.settingsCursor - profileStartIdx
1152
- const profileName = savedProfiles[profileIdx]
1153
- if (profileName) {
1154
- const settings = loadProfile(state.config, profileName)
1155
- if (settings) {
1156
- state.sortColumn = settings.sortColumn || 'avg'
1157
- state.sortDirection = settings.sortAsc ? 'asc' : 'desc'
1158
- setPingMode(intervalToPingMode(settings.pingInterval || PING_INTERVAL), 'manual')
1159
- if (settings.tierFilter) {
1160
- const tierIdx = TIER_CYCLE.indexOf(settings.tierFilter)
1161
- if (tierIdx >= 0) state.tierFilterMode = tierIdx
1162
- } else {
1163
- state.tierFilterMode = 0
1164
- }
1165
- state.activeProfile = profileName
1166
- syncFavoriteFlags(state.results, state.config)
1167
- applyTierFilter()
1168
- const visible = state.results.filter(r => !r.hidden)
1169
- state.visibleSorted = sortResultsWithPinnedFavorites(visible, state.sortColumn, state.sortDirection)
1170
- saveConfig(state.config, {
1171
- replaceApiKeys: true,
1172
- replaceFavorites: true,
1173
- })
1174
- }
1175
- }
1176
- return
1177
- }
1103
+ // πŸ“– Profile system removed - API keys now persist permanently across all sessions
1178
1104
 
1179
1105
  // πŸ“– Enter edit mode for the selected provider's key
1180
1106
  const pk = providerKeys[state.settingsCursor]
@@ -1190,25 +1116,23 @@ const updateRowIdx = providerKeys.length
1190
1116
  if (state.settingsCursor === widthWarningRowIdx) {
1191
1117
  if (!state.config.settings) state.config.settings = {}
1192
1118
  state.config.settings.disableWidthsWarning = !state.config.settings.disableWidthsWarning
1193
- saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1119
+ saveConfig(state.config)
1194
1120
  return
1195
1121
  }
1196
- // πŸ“– Profile rows don't respond to Space
1197
- if (state.settingsCursor >= profileStartIdx) return
1122
+ // πŸ“– Profile system removed - API keys now persist permanently across all sessions
1198
1123
 
1199
1124
  // πŸ“– Toggle enabled/disabled for selected provider
1200
1125
  const pk = providerKeys[state.settingsCursor]
1201
1126
  if (!state.config.providers) state.config.providers = {}
1202
1127
  if (!state.config.providers[pk]) state.config.providers[pk] = { enabled: true }
1203
1128
  state.config.providers[pk].enabled = !isProviderEnabled(state.config, pk)
1204
- saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1129
+ saveConfig(state.config)
1205
1130
  return
1206
1131
  }
1207
1132
 
1208
1133
  if (key.name === 't') {
1209
1134
  if (state.settingsCursor === updateRowIdx || state.settingsCursor === proxyDaemonRowIdx || state.settingsCursor === changelogViewRowIdx) return
1210
- // πŸ“– Profile rows don't respond to T (test key)
1211
- if (state.settingsCursor >= profileStartIdx) return
1135
+ // πŸ“– Profile system removed - API keys now persist permanently across all sessions
1212
1136
 
1213
1137
  // πŸ“– Test the selected provider's key (fires a real ping)
1214
1138
  const pk = providerKeys[state.settingsCursor]
@@ -1221,27 +1145,7 @@ const updateRowIdx = providerKeys.length
1221
1145
  return
1222
1146
  }
1223
1147
 
1224
- // πŸ“– Backspace on a profile row β†’ delete that profile
1225
- if (key.name === 'backspace' && state.settingsCursor >= profileStartIdx && savedProfiles.length > 0) {
1226
- const profileIdx = state.settingsCursor - profileStartIdx
1227
- const profileName = savedProfiles[profileIdx]
1228
- if (profileName) {
1229
- deleteProfile(state.config, profileName)
1230
- // πŸ“– If the deleted profile was active, clear active state
1231
- if (state.activeProfile === profileName) {
1232
- setActiveProfile(state.config, null)
1233
- state.activeProfile = null
1234
- }
1235
- saveConfig(state.config, { removedProfileNames: [profileName] })
1236
- // πŸ“– Re-clamp cursor after deletion (profile list just got shorter)
1237
- const newProfiles = listProfiles(state.config)
1238
- const newMaxRowIdx = newProfiles.length > 0 ? profileStartIdx + newProfiles.length - 1 : changelogViewRowIdx
1239
- if (state.settingsCursor > newMaxRowIdx) {
1240
- state.settingsCursor = Math.max(0, newMaxRowIdx)
1241
- }
1242
- }
1243
- return
1244
- }
1148
+ // πŸ“– Profile system removed - API keys now persist permanently across all sessions
1245
1149
 
1246
1150
  if (key.ctrl && key.name === 'c') { exit(0); return }
1247
1151
 
@@ -1343,7 +1247,7 @@ const updateRowIdx = providerKeys.length
1343
1247
  }
1344
1248
  if (!state.config.settings) state.config.settings = {}
1345
1249
  state.config.settings.proxy = { ...proxySettings, preferredPort: parsed }
1346
- saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1250
+ saveConfig(state.config)
1347
1251
  state.settingsProxyPortEditMode = false
1348
1252
  state.settingsProxyPortBuffer = ''
1349
1253
  state.proxyDaemonMessage = { type: 'success', msg: `βœ… Preferred port saved: ${parsed === 0 ? 'auto' : parsed}`, ts: Date.now() }
@@ -1384,7 +1288,7 @@ const updateRowIdx = providerKeys.length
1384
1288
  if (state.proxyDaemonCursor === ROW_PROXY_ENABLED) {
1385
1289
  if (!state.config.settings) state.config.settings = {}
1386
1290
  state.config.settings.proxy = { ...proxySettings, enabled: !proxySettings.enabled }
1387
- saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1291
+ saveConfig(state.config)
1388
1292
  state.proxyDaemonMessage = { type: 'success', msg: `βœ… Proxy mode ${state.config.settings.proxy.enabled ? 'enabled' : 'disabled'}`, ts: Date.now() }
1389
1293
  return
1390
1294
  }
@@ -1395,7 +1299,7 @@ const updateRowIdx = providerKeys.length
1395
1299
  }
1396
1300
  if (!state.config.settings) state.config.settings = {}
1397
1301
  state.config.settings.proxy = { ...proxySettings, syncToOpenCode: !proxySettings.syncToOpenCode }
1398
- saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1302
+ saveConfig(state.config)
1399
1303
  const { getToolMeta } = await import('./tool-metadata.js')
1400
1304
  const toolLabel = getToolMeta(currentProxyTool).label
1401
1305
  state.proxyDaemonMessage = { type: 'success', msg: `βœ… Auto-sync to ${toolLabel} ${state.config.settings.proxy.syncToOpenCode ? 'enabled' : 'disabled'}`, ts: Date.now() }
@@ -1411,7 +1315,7 @@ const updateRowIdx = providerKeys.length
1411
1315
  if (state.proxyDaemonCursor === ROW_PROXY_ENABLED) {
1412
1316
  if (!state.config.settings) state.config.settings = {}
1413
1317
  state.config.settings.proxy = { ...proxySettings, enabled: !proxySettings.enabled }
1414
- saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1318
+ saveConfig(state.config)
1415
1319
  state.proxyDaemonMessage = { type: 'success', msg: `βœ… Proxy mode ${state.config.settings.proxy.enabled ? 'enabled' : 'disabled'}`, ts: Date.now() }
1416
1320
  return
1417
1321
  }
@@ -1424,7 +1328,7 @@ const updateRowIdx = providerKeys.length
1424
1328
  }
1425
1329
  if (!state.config.settings) state.config.settings = {}
1426
1330
  state.config.settings.proxy = { ...proxySettings, syncToOpenCode: !proxySettings.syncToOpenCode }
1427
- saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1331
+ saveConfig(state.config)
1428
1332
  const { getToolMeta } = await import('./tool-metadata.js')
1429
1333
  const toolLabel = getToolMeta(currentProxyTool).label
1430
1334
  state.proxyDaemonMessage = { type: 'success', msg: `βœ… Auto-sync to ${toolLabel} ${state.config.settings.proxy.syncToOpenCode ? 'enabled' : 'disabled'}`, ts: Date.now() }
@@ -1474,7 +1378,7 @@ const updateRowIdx = providerKeys.length
1474
1378
  if (!state.config.settings.proxy.preferredPort || state.config.settings.proxy.preferredPort === 0) {
1475
1379
  state.config.settings.proxy.preferredPort = 18045
1476
1380
  }
1477
- saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1381
+ saveConfig(state.config)
1478
1382
  const result = installDaemon()
1479
1383
  if (result.success) {
1480
1384
  state.proxyDaemonMessage = { type: 'success', msg: 'βœ… FCM Proxy V2 background service installed and started!', ts: Date.now() }
@@ -1488,7 +1392,7 @@ const updateRowIdx = providerKeys.length
1488
1392
  // πŸ“– Uninstall daemon
1489
1393
  const result = uninstallDaemon()
1490
1394
  state.config.settings.proxy.daemonEnabled = false
1491
- saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1395
+ saveConfig(state.config)
1492
1396
  if (result.success) {
1493
1397
  state.proxyDaemonMessage = { type: 'success', msg: 'βœ… FCM Proxy V2 background service uninstalled.', ts: Date.now() }
1494
1398
  state.daemonStatus = 'not-installed'
@@ -1632,70 +1536,9 @@ const updateRowIdx = providerKeys.length
1632
1536
  return
1633
1537
  }
1634
1538
 
1635
- // πŸ“– Shift+P: cycle through profiles (or show profile picker)
1636
- if (key.name === 'p' && key.shift) {
1637
- const profiles = listProfiles(state.config)
1638
- if (profiles.length === 0) {
1639
- // πŸ“– No profiles saved β€” save current config as 'default' profile
1640
- saveAsProfile(state.config, 'default', {
1641
- tierFilter: TIER_CYCLE[state.tierFilterMode],
1642
- sortColumn: state.sortColumn,
1643
- sortAsc: state.sortDirection === 'asc',
1644
- pingInterval: state.pingInterval,
1645
- hideUnconfiguredModels: state.hideUnconfiguredModels,
1646
- proxy: getProxySettings(state.config),
1647
- })
1648
- setActiveProfile(state.config, 'default')
1649
- state.activeProfile = 'default'
1650
- saveConfig(state.config, { replaceProfileNames: ['default'] })
1651
- } else {
1652
- // πŸ“– Cycle to next profile (or back to null = raw config)
1653
- const currentIdx = state.activeProfile ? profiles.indexOf(state.activeProfile) : -1
1654
- const nextIdx = (currentIdx + 1) % (profiles.length + 1) // +1 for "no profile"
1655
- if (nextIdx === profiles.length) {
1656
- // πŸ“– Back to raw config (no profile)
1657
- setActiveProfile(state.config, null)
1658
- state.activeProfile = null
1659
- saveConfig(state.config)
1660
- } else {
1661
- const nextProfile = profiles[nextIdx]
1662
- const settings = loadProfile(state.config, nextProfile)
1663
- if (settings) {
1664
- // πŸ“– Apply profile's TUI settings to live state
1665
- state.sortColumn = settings.sortColumn || 'avg'
1666
- state.sortDirection = settings.sortAsc ? 'asc' : 'desc'
1667
- setPingMode(intervalToPingMode(settings.pingInterval || PING_INTERVAL), 'manual')
1668
- if (settings.tierFilter) {
1669
- const tierIdx = TIER_CYCLE.indexOf(settings.tierFilter)
1670
- if (tierIdx >= 0) state.tierFilterMode = tierIdx
1671
- } else {
1672
- state.tierFilterMode = 0
1673
- }
1674
- state.hideUnconfiguredModels = settings.hideUnconfiguredModels === true
1675
- state.activeProfile = nextProfile
1676
- // πŸ“– Rebuild favorites from profile data
1677
- syncFavoriteFlags(state.results, state.config)
1678
- applyTierFilter()
1679
- const visible = state.results.filter(r => !r.hidden)
1680
- state.visibleSorted = sortResultsWithPinnedFavorites(visible, state.sortColumn, state.sortDirection)
1681
- state.cursor = 0
1682
- state.scrollOffset = 0
1683
- saveConfig(state.config, {
1684
- replaceApiKeys: true,
1685
- replaceFavorites: true,
1686
- })
1687
- }
1688
- }
1689
- }
1690
- return
1691
- }
1539
+ // πŸ“– Profile system removed - API keys now persist permanently across all sessions
1692
1540
 
1693
- // πŸ“– Shift+S: enter profile save mode β€” inline text prompt for typing a profile name
1694
- if (key.name === 's' && key.shift) {
1695
- state.profileSaveMode = true
1696
- state.profileSaveBuffer = ''
1697
- return
1698
- }
1541
+ // πŸ“– Profile system removed - API keys now persist permanently across all sessions
1699
1542
 
1700
1543
  // πŸ“– Helper: persist current UI view settings (tier, provider, sort) to config.settings
1701
1544
  // πŸ“– Called after every T / D / sort key so preferences survive session restarts.
@@ -1705,16 +1548,7 @@ const updateRowIdx = providerKeys.length
1705
1548
  state.config.settings.originFilter = ORIGIN_CYCLE[state.originFilterMode] ?? null
1706
1549
  state.config.settings.sortColumn = state.sortColumn
1707
1550
  state.config.settings.sortAsc = state.sortDirection === 'asc'
1708
- // πŸ“– Mirror into active profile too so profile captures live preferences
1709
- if (state.activeProfile && state.config.profiles?.[state.activeProfile]) {
1710
- const profile = state.config.profiles[state.activeProfile]
1711
- if (!profile.settings || typeof profile.settings !== 'object') profile.settings = {}
1712
- profile.settings.tierFilter = state.config.settings.tierFilter
1713
- profile.settings.originFilter = state.config.settings.originFilter
1714
- profile.settings.sortColumn = state.config.settings.sortColumn
1715
- profile.settings.sortAsc = state.config.settings.sortAsc
1716
- }
1717
- saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1551
+ saveConfig(state.config)
1718
1552
  }
1719
1553
 
1720
1554
  // πŸ“– Shift+R: reset all UI view settings to defaults (tier, sort, provider) and clear persisted config
@@ -1728,17 +1562,7 @@ const updateRowIdx = providerKeys.length
1728
1562
  delete state.config.settings.originFilter
1729
1563
  delete state.config.settings.sortColumn
1730
1564
  delete state.config.settings.sortAsc
1731
- // πŸ“– Also clear in active profile if loaded
1732
- if (state.activeProfile && state.config.profiles?.[state.activeProfile]) {
1733
- const profile = state.config.profiles[state.activeProfile]
1734
- if (profile.settings) {
1735
- delete profile.settings.tierFilter
1736
- delete profile.settings.originFilter
1737
- delete profile.settings.sortColumn
1738
- delete profile.settings.sortAsc
1739
- }
1740
- }
1741
- saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1565
+ saveConfig(state.config)
1742
1566
  applyTierFilter()
1743
1567
  const visible = state.results.filter(r => !r.hidden)
1744
1568
  state.visibleSorted = sortResultsWithPinnedFavorites(visible, state.sortColumn, state.sortDirection)
@@ -1835,17 +1659,12 @@ const updateRowIdx = providerKeys.length
1835
1659
  }
1836
1660
 
1837
1661
  // πŸ“– E toggles hiding models whose provider has no configured API key.
1838
- // πŸ“– The preference is saved globally and mirrored into the active profile.
1662
+ // πŸ“– The preference is saved globally.
1839
1663
  if (key.name === 'e') {
1840
1664
  state.hideUnconfiguredModels = !state.hideUnconfiguredModels
1841
1665
  if (!state.config.settings || typeof state.config.settings !== 'object') state.config.settings = {}
1842
1666
  state.config.settings.hideUnconfiguredModels = state.hideUnconfiguredModels
1843
- if (state.activeProfile && state.config.profiles?.[state.activeProfile]) {
1844
- const profile = state.config.profiles[state.activeProfile]
1845
- if (!profile.settings || typeof profile.settings !== 'object') profile.settings = {}
1846
- profile.settings.hideUnconfiguredModels = state.hideUnconfiguredModels
1847
- }
1848
- saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1667
+ saveConfig(state.config)
1849
1668
  applyTierFilter()
1850
1669
  const visible = state.results.filter(r => !r.hidden)
1851
1670
  state.visibleSorted = sortResultsWithPinnedFavorites(visible, state.sortColumn, state.sortDirection)
@@ -1907,12 +1726,7 @@ const updateRowIdx = providerKeys.length
1907
1726
  state.mode = modeOrder[nextIndex]
1908
1727
  if (!state.config.settings || typeof state.config.settings !== 'object') state.config.settings = {}
1909
1728
  state.config.settings.preferredToolMode = state.mode
1910
- if (state.activeProfile && state.config.profiles?.[state.activeProfile]) {
1911
- const profile = state.config.profiles[state.activeProfile]
1912
- if (!profile.settings || typeof profile.settings !== 'object') profile.settings = {}
1913
- profile.settings.preferredToolMode = state.mode
1914
- }
1915
- saveConfig(state.config, { replaceProfileNames: state.activeProfile ? [state.activeProfile] : [] })
1729
+ saveConfig(state.config)
1916
1730
  return
1917
1731
  }
1918
1732
 
package/src/overlays.js CHANGED
@@ -38,7 +38,6 @@ export function createOverlayRenderers(state, deps) {
38
38
  getProxySettings,
39
39
  resolveApiKeys,
40
40
  isProviderEnabled,
41
- listProfiles,
42
41
  TIER_CYCLE,
43
42
  SETTINGS_OVERLAY_BG,
44
43
  HELP_OVERLAY_BG,
@@ -310,33 +309,7 @@ const updateRowIdx = providerKeys.length
310
309
  cursorLineByRow[changelogViewRowIdx] = lines.length
311
310
  lines.push(state.settingsCursor === changelogViewRowIdx ? chalk.bgRgb(30, 45, 30)(changelogViewRow) : changelogViewRow)
312
311
 
313
- // πŸ“– Profiles section β€” list saved profiles with active indicator + delete support
314
- const savedProfiles = listProfiles(state.config)
315
- const profileStartIdx = updateRowIdx + 5
316
- const maxRowIdx = savedProfiles.length > 0 ? profileStartIdx + savedProfiles.length - 1 : changelogViewRowIdx
317
-
318
- lines.push('')
319
- lines.push(` ${chalk.bold('πŸ“‹ Profiles')} ${chalk.dim(savedProfiles.length > 0 ? `(${savedProfiles.length} saved)` : '(none β€” press Shift+S in main view to save)')}`)
320
- lines.push(` ${chalk.dim(' ' + '─'.repeat(separatorWidth))}`)
321
- lines.push('')
322
-
323
- if (savedProfiles.length === 0) {
324
- lines.push(chalk.dim(' No saved profiles. Press Shift+S in the main table to save your current settings as a profile.'))
325
- } else {
326
- for (let i = 0; i < savedProfiles.length; i++) {
327
- const pName = savedProfiles[i]
328
- const rowIdx = profileStartIdx + i
329
- const isCursor = state.settingsCursor === rowIdx
330
- const isActive = state.activeProfile === pName
331
- const activeBadge = isActive ? chalk.greenBright(' βœ… active') : ''
332
- const bullet = isCursor ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
333
- const profileLabel = chalk.rgb(200, 150, 255).bold(pName.padEnd(30))
334
- const deleteHint = isCursor ? chalk.dim(' Enter→Load ‒ Backspace→Delete') : ''
335
- const row = `${bullet}${profileLabel}${activeBadge}${deleteHint}`
336
- cursorLineByRow[rowIdx] = lines.length
337
- lines.push(isCursor ? chalk.bgRgb(40, 20, 60)(row) : row)
338
- }
339
- }
312
+ // πŸ“– Profile system removed - API keys now persist permanently across all sessions
340
313
 
341
314
  lines.push('')
342
315
  if (state.settingsEditMode) {
@@ -344,7 +317,7 @@ const profileStartIdx = updateRowIdx + 5
344
317
  } else if (state.settingsProxyPortEditMode) {
345
318
  lines.push(chalk.dim(' Type proxy port (0 = auto) β€’ Enter Save β€’ Esc Cancel'))
346
319
  } else {
347
- lines.push(chalk.dim(' ↑↓ Navigate β€’ Enter Edit/Run β€’ + Add key β€’ - Remove key β€’ Space Toggle β€’ T Test key β€’ S Syncβ†’OpenCode β€’ R Restore backup β€’ U Updates β€’ ⌫ Delete profile β€’ Esc Close'))
320
+ lines.push(chalk.dim(' ↑↓ Navigate β€’ Enter Edit/Run β€’ + Add key β€’ - Remove key β€’ Space Toggle β€’ T Test key β€’ S Syncβ†’OpenCode β€’ R Restore backup β€’ U Updates β€’ Esc Close'))
348
321
  }
349
322
  // πŸ“– Show sync/restore status message if set
350
323
  if (state.settingsSyncStatus) {
@@ -630,7 +603,7 @@ const profileStartIdx = updateRowIdx + 5
630
603
  lines.push('')
631
604
  lines.push(` ${chalk.bold('Controls')}`)
632
605
  lines.push(` ${chalk.yellow('W')} Toggle ping mode ${chalk.dim('(speed 2s β†’ normal 10s β†’ slow 30s β†’ forced 4s)')}`)
633
- lines.push(` ${chalk.yellow('E')} Toggle configured models only ${chalk.dim('(enabled by default, persisted globally + in profiles)')}`)
606
+ lines.push(` ${chalk.yellow('E')} Toggle configured models only ${chalk.dim('(enabled by default)')}`)
634
607
  lines.push(` ${chalk.yellow('X')} Toggle token log page ${chalk.dim('(shows recent request usage from request-log.jsonl)')}`)
635
608
  lines.push(` ${chalk.yellow('Z')} Cycle tool mode ${chalk.dim('(OpenCode β†’ Desktop β†’ OpenClaw β†’ Crush β†’ Goose β†’ Pi β†’ Aider β†’ Claude Code β†’ Codex β†’ Gemini β†’ Qwen β†’ OpenHands β†’ Amp)')}`)
636
609
  lines.push(` ${chalk.yellow('F')} Toggle favorite on selected row ${chalk.dim('(⭐ pinned at top, persisted)')}`)
@@ -639,10 +612,7 @@ const profileStartIdx = updateRowIdx + 5
639
612
  lines.push(` ${chalk.rgb(255, 87, 51).bold('I')} Feedback, bugs & requests ${chalk.dim('(πŸ“ send anonymous feedback, bug reports, or feature requests)')}`)
640
613
  lines.push(` ${chalk.yellow('J')} FCM Proxy V2 settings ${chalk.dim('(πŸ“‘ open proxy configuration and background service management)')}`)
641
614
  lines.push(` ${chalk.yellow('P')} Open settings ${chalk.dim('(manage API keys, provider toggles, proxy, manual update)')}`)
642
- lines.push(` ${chalk.yellow('Shift+P')} Cycle config profile ${chalk.dim('(switch between saved profiles live)')}`)
643
- lines.push(` ${chalk.yellow('Shift+S')} Save current config as a named profile ${chalk.dim('(inline prompt β€” type name + Enter)')}`)
644
- lines.push(` ${chalk.dim('Profiles store: favorites, sort, tier filter, ping interval, configured-only filter, API keys.')}`)
645
- lines.push(` ${chalk.dim('Use --profile <name> to load a profile on startup.')}`)
615
+ // πŸ“– Profile system removed - API keys now persist permanently across all sessions
646
616
  lines.push(` ${chalk.yellow('Shift+R')} Reset view settings ${chalk.dim('(tier filter, sort, provider filter β†’ defaults)')}`)
647
617
  lines.push(` ${chalk.yellow('N')} Changelog ${chalk.dim('(πŸ“‹ browse all versions, Enter to view details)')}`)
648
618
  lines.push(` ${chalk.yellow('K')} / ${chalk.yellow('Esc')} Show/hide this help`)
@@ -736,7 +706,8 @@ const profileStartIdx = updateRowIdx + 5
736
706
  } catch { /* keep raw */ }
737
707
 
738
708
  const requestedModelLabel = row.requestedModel || ''
739
- const displayModel = row.switched && requestedModelLabel && requestedModelLabel !== row.model
709
+ // πŸ“– Always show "requested β†’ actual" if they differ, not just when switched
710
+ const displayModel = requestedModelLabel && requestedModelLabel !== row.model
740
711
  ? `${requestedModelLabel} β†’ ${row.model}`
741
712
  : row.model
742
713
 
@@ -781,9 +752,11 @@ const profileStartIdx = updateRowIdx + 5
781
752
  const isFailedWithZeroTokens = row.status !== '200' && (!row.tokens || Number(row.tokens) === 0)
782
753
 
783
754
  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")
755
+ // πŸ“– Provider display: Use pretty label if available, otherwise raw key.
756
+ // πŸ“– All these logs are from FCM Proxy V2.
757
+ const providerLabel = PROVIDER_METADATA[row.provider]?.label || row.provider
785
758
  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))
759
+ const provCell = chalk.bold.rgb(...providerRgb)(providerLabel.slice(0, W_PROV).padEnd(W_PROV))
787
760
 
788
761
  // πŸ“– Color model based on status - red for failed requests with zero tokens
789
762
  let modelCell