free-coding-models 0.3.9 β 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.
- package/CHANGELOG.md +21 -0
- package/bin/free-coding-models.js +31 -31
- package/package.json +1 -1
- package/src/cli-help.js +1 -1
- package/src/config.js +18 -240
- package/src/error-classifier.js +4 -1
- package/src/favorites.js +0 -14
- package/src/key-handler.js +26 -212
- package/src/overlays.js +4 -34
- package/src/proxy-foreground.js +234 -0
- package/src/proxy-server.js +41 -12
- package/src/proxy-sync.js +28 -2
- package/src/render-table.js +6 -17
- package/src/utils.js +7 -11
package/src/key-handler.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
968
|
-
|
|
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
|
|
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
|
|
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
|
|
1119
|
+
saveConfig(state.config)
|
|
1194
1120
|
return
|
|
1195
1121
|
}
|
|
1196
|
-
// π Profile
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
// π
|
|
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
|
-
// π
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
// π
|
|
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 β’
|
|
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
|
|
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
|
-
|
|
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`)
|