free-coding-models 0.4.3 → 0.5.1

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 (142) hide show
  1. package/README.md +9 -1
  2. package/bin/free-coding-models.js +19 -9
  3. package/changelog/v0.5.0.md +15 -0
  4. package/changelog/v0.5.1.md +24 -0
  5. package/package.json +7 -2
  6. package/src/{analysis.js → core/analysis.js} +5 -5
  7. package/src/{constants.js → core/constants.js} +1 -1
  8. package/src/{endpoint-installer.js → core/endpoint-installer.js} +1 -1
  9. package/src/{installed-models-manager.js → core/installed-models-manager.js} +1 -1
  10. package/src/{kilo.js → core/kilo.js} +1 -2
  11. package/src/{openclaw.js → core/openclaw.js} +1 -1
  12. package/src/{opencode.js → core/opencode.js} +2 -1
  13. package/src/{ping-loop.js → core/ping-loop.js} +1 -1
  14. package/src/{router-daemon.js → core/router-daemon.js} +169 -4
  15. package/src/{router-dashboard.js → core/router-dashboard.js} +2 -2
  16. package/src/{setup.js → core/setup.js} +1 -1
  17. package/src/{sync-set.js → core/sync-set.js} +1 -1
  18. package/src/{telemetry.js → core/telemetry.js} +1 -1
  19. package/src/{tool-launchers.js → core/tool-launchers.js} +2 -2
  20. package/src/{updater.js → core/updater.js} +1 -1
  21. package/src/{utils.js → core/utils.js} +2 -0
  22. package/src/{app.js → tui/app.js} +38 -38
  23. package/src/{cli-help.js → tui/cli-help.js} +3 -1
  24. package/src/{command-palette.js → tui/command-palette.js} +2 -2
  25. package/src/{key-handler.js → tui/key-handler.js} +11 -11
  26. package/src/{overlays.js → tui/overlays.js} +2 -2
  27. package/src/{render-helpers.js → tui/render-helpers.js} +2 -2
  28. package/src/{render-table.js → tui/render-table.js} +9 -9
  29. package/src/{tui-filters.js → tui/tui-filters.js} +3 -3
  30. package/src/{tui-state.js → tui/tui-state.js} +1 -1
  31. package/web/README.md +46 -0
  32. package/web/dist/assets/index-ByGf4Kq-.js +14 -0
  33. package/web/dist/assets/index-Ds7wmHBv.css +1 -0
  34. package/web/dist/index.html +3 -6
  35. package/web/index.html +1 -4
  36. package/web/package.json +11 -0
  37. package/web/server.js +609 -214
  38. package/web/src/App.jsx +54 -12
  39. package/web/src/components/analytics/AnalyticsView.jsx +10 -4
  40. package/web/src/components/atoms/AILatencyCell.jsx +38 -0
  41. package/web/src/components/atoms/AILatencyCell.module.css +43 -0
  42. package/web/src/components/atoms/HealthCell.jsx +53 -0
  43. package/web/src/components/atoms/HealthCell.module.css +15 -0
  44. package/web/src/components/atoms/LastPingCell.jsx +35 -0
  45. package/web/src/components/atoms/LastPingCell.module.css +35 -0
  46. package/web/src/components/atoms/MoodCell.jsx +25 -0
  47. package/web/src/components/atoms/MoodCell.module.css +6 -0
  48. package/web/src/components/atoms/RankCell.jsx +9 -0
  49. package/web/src/components/atoms/RankCell.module.css +9 -0
  50. package/web/src/components/atoms/TPSCell.jsx +36 -0
  51. package/web/src/components/atoms/TPSCell.module.css +38 -0
  52. package/web/src/components/atoms/VerdictBadge.jsx +30 -7
  53. package/web/src/components/atoms/VerdictBadge.module.css +24 -15
  54. package/web/src/components/dashboard/ExportModal.jsx +9 -4
  55. package/web/src/components/dashboard/FilterBar.jsx +112 -10
  56. package/web/src/components/dashboard/FilterBar.module.css +86 -1
  57. package/web/src/components/dashboard/ModelTable.jsx +293 -52
  58. package/web/src/components/dashboard/ModelTable.module.css +131 -33
  59. package/web/src/components/dashboard/StatsBar.jsx +7 -5
  60. package/web/src/components/layout/Footer.jsx +1 -1
  61. package/web/src/components/layout/Header.jsx +43 -9
  62. package/web/src/components/layout/Header.module.css +38 -4
  63. package/web/src/components/layout/Sidebar.jsx +19 -11
  64. package/web/src/components/layout/Sidebar.module.css +15 -5
  65. package/web/src/components/settings/SettingsView.jsx +24 -6
  66. package/web/src/components/settings/SettingsView.module.css +0 -1
  67. package/web/src/global.css +70 -73
  68. package/web/src/hooks/useFilter.js +117 -25
  69. package/web/src/hooks/useSSE.js +33 -9
  70. package/web/src/hooks/useSocket.js +200 -0
  71. package/web/vite.config.js +41 -0
  72. package/src/graphify-out/cache/089db1c1def873cf6d112f1590da4490e61e691aff0db41e006aa2fb15ba0656.json +0 -1
  73. package/src/graphify-out/cache/0b510b53cf1a1393fb52b1fc3bbbf88b63938e961ec5b82119a2e9715fee8bd7.json +0 -1
  74. package/src/graphify-out/cache/0ec9a95a326bde58e0316889018b278062d06d494d0f31ba177c9de71e5fed2d.json +0 -1
  75. package/src/graphify-out/cache/1548663a24a68dce740ebab1bd1d3091048c9604e9d067a1650a42a6d82541d4.json +0 -1
  76. package/src/graphify-out/cache/1783af63cb6d0dfb4d469009f71ac83a74ba0b33d48186ff2c6e63f9429e900a.json +0 -1
  77. package/src/graphify-out/cache/1e109f5eb5dc4fd285871c3613e32b6b14a8c225f4080ee34b51c7e1a1764571.json +0 -1
  78. package/src/graphify-out/cache/1eb24dbeb69b46c8bc1caf925df2f2a964af0f33aea143adf8ddf88e017db6ca.json +0 -1
  79. package/src/graphify-out/cache/21e1bcfed11685e8347243f9d8516072dda183266a4bfe22c52fb31753a446c8.json +0 -1
  80. package/src/graphify-out/cache/2327473478b9c4b1940bf7ef66c9ee960b3cba8d5302e56b625df8274246e0b4.json +0 -1
  81. package/src/graphify-out/cache/25955b81fd25454c8fa90fb71a47db8d1215cf621beb8ff3cbd580aaf011b4f3.json +0 -1
  82. package/src/graphify-out/cache/2739677f19c702f88f3de0a0bac475066adbda98709907ad3de967aef689f86d.json +0 -1
  83. package/src/graphify-out/cache/2bba03422f6b3ee7f5b5d29cc90314a064d259e5822a176657bda3e04505cf00.json +0 -1
  84. package/src/graphify-out/cache/2ddf1d2c6d10147b0402446bc71a7988187b79b6210dd7e7250be8c555b9ff35.json +0 -1
  85. package/src/graphify-out/cache/2ee07457a5767c95a57f8e9eb95b28f800044f35666e0715e9d88ad1103a092e.json +0 -1
  86. package/src/graphify-out/cache/2fe9f75dc2951c417f2c8dd22749092cf550dc67599f1c8d1866900dc6e9154e.json +0 -1
  87. package/src/graphify-out/cache/41c4b7c27e7fc3e2948d3a4bf95a72de2ed9a6f0463994babdce8ed2cc84598c.json +0 -1
  88. package/src/graphify-out/cache/5028defd54b7fbd3c7e444973e493de036e097e9b1d2a7cae7f19b88d68aacde.json +0 -1
  89. package/src/graphify-out/cache/5b133aba3fb16410c5b1fdbd1730039fc7fa1ac93abd99d7be08f60da70fc8d4.json +0 -1
  90. package/src/graphify-out/cache/74252e5b0978d85ab3421a3de1a9384aa282ffd2be2cfe7db2530139089f4275.json +0 -1
  91. package/src/graphify-out/cache/7695ebeea056095edd14332963cc43354ef3a097caf46f1e28d0f01369642901.json +0 -1
  92. package/src/graphify-out/cache/777aa7085c395a935c6556bbde182cd871edb61f3a685ed8068ec0c8f6fb0075.json +0 -1
  93. package/src/graphify-out/cache/82a723881980e82273c113def8315533d7da28827e300413d9ad30f27b7407df.json +0 -1
  94. package/src/graphify-out/cache/86b87c9603e6cd188f42c7eed3b86c291d48a781c223a707e74f3e7ed0c02a21.json +0 -1
  95. package/src/graphify-out/cache/890fead9a78cadaed560a2d2453916121fa605c3e43a334910ac4bc951a9ef6d.json +0 -1
  96. package/src/graphify-out/cache/89d3ea66f52783caa775ef9a30923d7d6225e1d8ae9e962f4741b8c7785dab1e.json +0 -1
  97. package/src/graphify-out/cache/8cc82cd9edce41f0e1c092f14a94fd52bf847addf3237b616dc5a9e505bd05bd.json +0 -1
  98. package/src/graphify-out/cache/93ba2e25e3ff7ad525f397902345fbd375df7315de7b402e20cc803c14eccde8.json +0 -1
  99. package/src/graphify-out/cache/99beed29580b9c7bfecfee794cb3d8e535fcf0eb3b92113108f88bdd0a8e79b3.json +0 -1
  100. package/src/graphify-out/cache/aeeb931fa477c65ce2e51d8149957350fa54225c613222bbbe8448998d1afd3d.json +0 -1
  101. package/src/graphify-out/cache/baf91bef5b5ecb2a476433b6cc0c48c563c54ee2d07fc3c192e543685e3e7222.json +0 -1
  102. package/src/graphify-out/cache/bd98b94ac4e9b92b6336d47b26e0366b51a4eaf0711d722f05f98dfae23ab42b.json +0 -1
  103. package/src/graphify-out/cache/bfcb51e9328e9cbfbee4f6fee0f56635d7b03488addc9f6c4e4b190b70a73362.json +0 -1
  104. package/src/graphify-out/cache/c0d3dabeb093aa758c49eadf41b87ecc96a16c1449c2670aaf48cbfc891d8da6.json +0 -1
  105. package/src/graphify-out/cache/c20d6630236f473c1406068c3ae205853e649b216495c93dfec055dd222c55cf.json +0 -1
  106. package/src/graphify-out/cache/c22b9122816bebce0a2f79af41a986559d01e00163dbcd579c5755621b4cb483.json +0 -1
  107. package/src/graphify-out/cache/ca556ec14453ddb8f9e0c5a832dac90d77111b9bad5f8c2d80d272e2e7a06371.json +0 -1
  108. package/src/graphify-out/cache/d6dbc9135dfa35a756b3b09b06700e4bc229fdccba11bb963f2ba44028e0bbae.json +0 -1
  109. package/src/graphify-out/cache/e1cf71276f1779d0fa075f79bd7c8a9fd0b8eef6932ac043137451b7c7fa7cbe.json +0 -1
  110. package/src/graphify-out/cache/e4b3be14494467df2d2ed389bc4f18f099021cb5bc355b901fa88387b2d8b8a2.json +0 -1
  111. package/src/graphify-out/cache/eaea0dded097f6f9553b654220046c6ec0c9be592a5973d906564ee60af34e0d.json +0 -1
  112. package/src/graphify-out/cache/ef07d0cd2675d1f79d2a2fdbf3bc3319687638751e9ce89b0d0d97ed1cd9f7e1.json +0 -1
  113. package/src/graphify-out/cache/f81272d6eb8aaff9e96d5a1d9f06777db70ac3652a646b951ded51f79871d733.json +0 -1
  114. package/src/graphify-out/cache/f9619dd92186f75a6dbda937e0c606647153918524cdb5763f956e6ec2a9e386.json +0 -1
  115. package/src/graphify-out/cache/fd88b1b2ff4bfcae08559d9c2aaeeb9a3f1e2f5cd8928762c311196956c170a5.json +0 -1
  116. package/web/dist/assets/index-CGN-0_A0.css +0 -1
  117. package/web/dist/assets/index-Czwis3ab.js +0 -11
  118. /package/src/{benchmark.js → core/benchmark.js} +0 -0
  119. /package/src/{cache.js → core/cache.js} +0 -0
  120. /package/src/{changelog-loader.js → core/changelog-loader.js} +0 -0
  121. /package/src/{config.js → core/config.js} +0 -0
  122. /package/src/{favorites.js → core/favorites.js} +0 -0
  123. /package/src/{kilo-config.js → core/kilo-config.js} +0 -0
  124. /package/src/{legacy-proxy-cleanup.js → core/legacy-proxy-cleanup.js} +0 -0
  125. /package/src/{model-merger.js → core/model-merger.js} +0 -0
  126. /package/src/{opencode-config.js → core/opencode-config.js} +0 -0
  127. /package/src/{ping.js → core/ping.js} +0 -0
  128. /package/src/{product-flags.js → core/product-flags.js} +0 -0
  129. /package/src/{provider-metadata.js → core/provider-metadata.js} +0 -0
  130. /package/src/{provider-quota-fetchers.js → core/provider-quota-fetchers.js} +0 -0
  131. /package/src/{quota-capabilities.js → core/quota-capabilities.js} +0 -0
  132. /package/src/{security.js → core/security.js} +0 -0
  133. /package/src/{shell-env.js → core/shell-env.js} +0 -0
  134. /package/src/{testfcm.js → core/testfcm.js} +0 -0
  135. /package/src/{token-usage-reader.js → core/token-usage-reader.js} +0 -0
  136. /package/src/{tool-bootstrap.js → core/tool-bootstrap.js} +0 -0
  137. /package/src/{tool-metadata.js → core/tool-metadata.js} +0 -0
  138. /package/src/{usage-reader.js → core/usage-reader.js} +0 -0
  139. /package/src/{mouse.js → tui/mouse.js} +0 -0
  140. /package/src/{theme.js → tui/theme.js} +0 -0
  141. /package/src/{tier-colors.js → tui/tier-colors.js} +0 -0
  142. /package/src/{ui-config.js → tui/ui-config.js} +0 -0
@@ -96,45 +96,45 @@ import { readFileSync, writeFileSync, existsSync, copyFileSync, mkdirSync } from
96
96
  import { randomUUID } from 'crypto'
97
97
  import { homedir } from 'os'
98
98
  import { join, dirname } from 'path'
99
- import { MODELS, sources } from '../sources.js'
100
- import { getAvg, getVerdict, getUptime, getP95, getJitter, getStabilityScore, sortResults, filterByTier, findBestModel, parseArgs, TIER_ORDER, VERDICT_ORDER, TIER_LETTER_MAP, scoreModelForTask, getTopRecommendations, TASK_TYPES, PRIORITY_TYPES, CONTEXT_BUDGETS, formatCtxWindow, labelFromId, formatResultsAsJSON } from '../src/utils.js'
101
- import { loadConfig, saveConfig, getApiKey, resolveApiKeys, addApiKey, removeApiKey, isProviderEnabled, persistApiKeysForProvider } from '../src/config.js'
102
- import { buildMergedModels } from '../src/model-merger.js'
103
- import { loadOpenCodeConfig, saveOpenCodeConfig } from '../src/opencode-config.js'
104
- import { usageForRow as _usageForRow } from '../src/usage-reader.js'
105
- import { buildProviderModelTokenKey, loadTokenUsageByProviderModel } from '../src/token-usage-reader.js'
106
- import { parseOpenRouterResponse, fetchProviderQuota as _fetchProviderQuotaFromModule } from '../src/provider-quota-fetchers.js'
107
- import { isKnownQuotaTelemetry } from '../src/quota-capabilities.js'
108
- import { ALT_ENTER, ALT_LEAVE, ALT_HOME, PING_TIMEOUT, PING_INTERVAL, FPS, COL_MODEL, COL_MS, CELL_W, FRAMES, TIER_CYCLE, VERDICT_CYCLE, HEALTH_CYCLE, SETTINGS_OVERLAY_BG, HELP_OVERLAY_BG, RECOMMEND_OVERLAY_BG, OVERLAY_PANEL_WIDTH, TABLE_HEADER_LINES, TABLE_FOOTER_LINES, TABLE_FIXED_LINES, WIDTH_WARNING_MIN_COLS, msCell, spinCell } from '../src/constants.js'
109
- import { TIER_COLOR } from '../src/tier-colors.js'
110
- import { resolveCloudflareUrl, buildPingRequest, ping, extractQuotaPercent, getProviderQuotaPercentCached, usagePlaceholderForProvider } from '../src/ping.js'
111
- import { runFiableMode, filterByTierOrExit, fetchOpenRouterFreeModels } from '../src/analysis.js'
112
- import { PROVIDER_METADATA, ENV_VAR_NAMES, isWindows, isMac } from '../src/provider-metadata.js'
113
- import { parseTelemetryEnv, isTelemetryDebugEnabled, telemetryDebug, ensureTelemetryConfig, getTelemetryDistinctId, getTelemetrySystem, getTelemetryTerminal, isTelemetryEnabled, sendUsageTelemetry } from '../src/telemetry.js'
114
- import { ensureFavoritesConfig, toFavoriteKey, syncFavoriteFlags, toggleFavoriteModel, reorderFavorite, pruneOrphanedFavorites } from '../src/favorites.js'
115
- import { checkForUpdateDetailed, checkForUpdate, runUpdate, fetchLastReleaseDate } from './updater.js'
99
+ import { MODELS, sources } from '../../sources.js'
100
+ import { getAvg, getVerdict, getUptime, getP95, getJitter, getStabilityScore, sortResults, filterByTier, findBestModel, parseArgs, TIER_ORDER, VERDICT_ORDER, TIER_LETTER_MAP, scoreModelForTask, getTopRecommendations, TASK_TYPES, PRIORITY_TYPES, CONTEXT_BUDGETS, formatCtxWindow, labelFromId, formatResultsAsJSON } from '../core/utils.js'
101
+ import { loadConfig, saveConfig, getApiKey, resolveApiKeys, addApiKey, removeApiKey, isProviderEnabled, persistApiKeysForProvider } from '../core/config.js'
102
+ import { buildMergedModels } from '../core/model-merger.js'
103
+ import { loadOpenCodeConfig, saveOpenCodeConfig } from '../core/opencode-config.js'
104
+ import { usageForRow as _usageForRow } from '../core/usage-reader.js'
105
+ import { buildProviderModelTokenKey, loadTokenUsageByProviderModel } from '../core/token-usage-reader.js'
106
+ import { parseOpenRouterResponse, fetchProviderQuota as _fetchProviderQuotaFromModule } from '../core/provider-quota-fetchers.js'
107
+ import { isKnownQuotaTelemetry } from '../core/quota-capabilities.js'
108
+ import { ALT_ENTER, ALT_LEAVE, ALT_HOME, PING_TIMEOUT, PING_INTERVAL, FPS, COL_MODEL, COL_MS, CELL_W, FRAMES, TIER_CYCLE, VERDICT_CYCLE, HEALTH_CYCLE, SETTINGS_OVERLAY_BG, HELP_OVERLAY_BG, RECOMMEND_OVERLAY_BG, OVERLAY_PANEL_WIDTH, TABLE_HEADER_LINES, TABLE_FOOTER_LINES, TABLE_FIXED_LINES, WIDTH_WARNING_MIN_COLS, msCell, spinCell } from '../core/constants.js'
109
+ import { TIER_COLOR } from './tier-colors.js'
110
+ import { resolveCloudflareUrl, buildPingRequest, ping, extractQuotaPercent, getProviderQuotaPercentCached, usagePlaceholderForProvider } from '../core/ping.js'
111
+ import { runFiableMode, filterByTierOrExit, fetchOpenRouterFreeModels } from '../core/analysis.js'
112
+ import { PROVIDER_METADATA, ENV_VAR_NAMES, isWindows, isMac } from '../core/provider-metadata.js'
113
+ import { parseTelemetryEnv, isTelemetryDebugEnabled, telemetryDebug, ensureTelemetryConfig, getTelemetryDistinctId, getTelemetrySystem, getTelemetryTerminal, isTelemetryEnabled, sendUsageTelemetry } from '../core/telemetry.js'
114
+ import { ensureFavoritesConfig, toFavoriteKey, syncFavoriteFlags, toggleFavoriteModel, reorderFavorite, pruneOrphanedFavorites } from '../core/favorites.js'
115
+ import { checkForUpdateDetailed, checkForUpdate, runUpdate, fetchLastReleaseDate } from '../core/updater.js'
116
116
  import { createTuiState, PING_MODE_INTERVALS, PING_MODE_CYCLE, SPEED_MODE_DURATION_MS, IDLE_SLOW_AFTER_MS, intervalToPingMode } from './tui-state.js'
117
- import { createPingLoop } from './ping-loop.js'
117
+ import { createPingLoop } from '../core/ping-loop.js'
118
118
  import { createTuiFilters } from './tui-filters.js'
119
- import { promptApiKey } from '../src/setup.js'
120
- import { syncShellEnv, ensureShellRcSource, removeShellEnv } from '../src/shell-env.js'
121
- import { stripAnsi, maskApiKey, displayWidth, padEndDisplay, tintOverlayLines, keepOverlayTargetVisible, sliceOverlayLines, calculateViewport, sortResultsWithPinnedFavorites, adjustScrollOffset } from '../src/render-helpers.js'
122
- import { renderTable, PROVIDER_COLOR } from '../src/render-table.js'
123
- import { setOpenCodeModelData, startOpenCode, startOpenCodeDesktop, startOpenCodeWeb } from '../src/opencode.js'
124
- import { startKilo } from '../src/kilo.js'
125
- import { startOpenClaw } from '../src/openclaw.js'
126
- import { createOverlayRenderers } from '../src/overlays.js'
127
- import { createKeyHandler, createMouseEventHandler } from '../src/key-handler.js'
128
- import { createMouseHandler, containsMouseSequence } from '../src/mouse.js'
129
- import { stopRouterDashboardClient } from '../src/router-dashboard.js'
130
- import { getToolModeOrder, getToolMeta } from '../src/tool-metadata.js'
131
- import { startExternalTool } from '../src/tool-launchers.js'
132
- import { getToolInstallPlan, installToolWithPlan, isToolInstalled } from '../src/tool-bootstrap.js'
133
- import { getConfiguredInstallableProviders, installProviderEndpoints, refreshInstalledEndpoints, getInstallTargetModes, getProviderCatalogModels } from '../src/endpoint-installer.js'
134
- import { loadCache, saveCache, clearCache, getCacheAge } from '../src/cache.js'
135
- import { checkConfigSecurity } from '../src/security.js'
136
- import { buildCliHelpText } from '../src/cli-help.js'
137
- import { detectActiveTheme } from '../src/theme.js'
119
+ import { promptApiKey } from '../core/setup.js'
120
+ import { syncShellEnv, ensureShellRcSource, removeShellEnv } from '../core/shell-env.js'
121
+ import { stripAnsi, maskApiKey, displayWidth, padEndDisplay, tintOverlayLines, keepOverlayTargetVisible, sliceOverlayLines, calculateViewport, sortResultsWithPinnedFavorites, adjustScrollOffset } from './render-helpers.js'
122
+ import { renderTable, PROVIDER_COLOR } from './render-table.js'
123
+ import { setOpenCodeModelData, startOpenCode, startOpenCodeDesktop, startOpenCodeWeb } from '../core/opencode.js'
124
+ import { startKilo } from '../core/kilo.js'
125
+ import { startOpenClaw } from '../core/openclaw.js'
126
+ import { createOverlayRenderers } from './overlays.js'
127
+ import { createKeyHandler, createMouseEventHandler } from './key-handler.js'
128
+ import { createMouseHandler, containsMouseSequence } from './mouse.js'
129
+ import { stopRouterDashboardClient } from '../core/router-dashboard.js'
130
+ import { getToolModeOrder, getToolMeta } from '../core/tool-metadata.js'
131
+ import { startExternalTool } from '../core/tool-launchers.js'
132
+ import { getToolInstallPlan, installToolWithPlan, isToolInstalled } from '../core/tool-bootstrap.js'
133
+ import { getConfiguredInstallableProviders, installProviderEndpoints, refreshInstalledEndpoints, getInstallTargetModes, getProviderCatalogModels } from '../core/endpoint-installer.js'
134
+ import { loadCache, saveCache, clearCache, getCacheAge } from '../core/cache.js'
135
+ import { checkConfigSecurity } from '../core/security.js'
136
+ import { buildCliHelpText } from './cli-help.js'
137
+ import { detectActiveTheme } from './theme.js'
138
138
 
139
139
  // 📖 mergedModels: cross-provider grouped model list (one entry per label, N providers each)
140
140
  // 📖 mergedModelByLabel: fast lookup map from display label → merged model entry
@@ -149,7 +149,7 @@ const require = createRequire(import.meta.url)
149
149
  const readline = require('readline')
150
150
 
151
151
  // ─── Version check ────────────────────────────────────────────────────────────
152
- const pkg = require('../package.json')
152
+ const pkg = require('../../package.json')
153
153
  const LOCAL_VERSION = pkg.version
154
154
 
155
155
  // 📖 sendBugReport → imported from ../src/telemetry.js
@@ -18,7 +18,7 @@
18
18
  * @see ./tool-metadata.js — source of truth for launcher modes and their CLI flags
19
19
  */
20
20
 
21
- import { getToolModeOrder, getToolMeta } from './tool-metadata.js'
21
+ import { getToolModeOrder, getToolMeta } from '../core/tool-metadata.js'
22
22
 
23
23
  const ANALYSIS_FLAGS = [
24
24
  { flag: '--best', description: 'Show only top tiers (A+, S, S+)' },
@@ -36,6 +36,7 @@ const ANALYSIS_FLAGS = [
36
36
  ]
37
37
 
38
38
  const CONFIG_FLAGS = [
39
+ { flag: 'web | --web | --gui', description: 'Start the full-catalog realtime Web Dashboard' },
39
40
  { flag: '--daemon', description: 'Start the FCM Router daemon + web dashboard (same port)' },
40
41
  { flag: '--daemon-bg', description: 'Start the FCM Router daemon in the background' },
41
42
  { flag: '--daemon-status', description: 'Print FCM Router daemon status JSON' },
@@ -47,6 +48,7 @@ const CONFIG_FLAGS = [
47
48
 
48
49
  const EXAMPLES = [
49
50
  'free-coding-models --help',
51
+ 'free-coding-models web',
50
52
  'free-coding-models --daemon',
51
53
  'free-coding-models --daemon-bg',
52
54
  'free-coding-models --daemon-status',
@@ -15,8 +15,8 @@
15
15
  * @see src/overlays.js
16
16
  */
17
17
 
18
- import { TOOL_METADATA, TOOL_MODE_ORDER } from './tool-metadata.js'
19
- import { sources } from '../sources.js'
18
+ import { TOOL_METADATA, TOOL_MODE_ORDER } from '../core/tool-metadata.js'
19
+ import { sources } from '../../sources.js'
20
20
 
21
21
  const PROVIDER_FILTER_COMMANDS = Object.entries(sources).map(([providerKey, source]) => {
22
22
  const label = source?.name || providerKey
@@ -31,21 +31,21 @@
31
31
  * @exports { buildProviderModelsUrl, parseProviderModelIds, listProviderTestModels, classifyProviderTestOutcome, buildProviderTestDetail, createKeyHandler }
32
32
  */
33
33
 
34
- import { loadChangelog } from './changelog-loader.js'
35
- import { getToolMeta, isModelCompatibleWithTool, getCompatibleTools, findSimilarCompatibleModels } from './tool-metadata.js'
36
- import { loadConfig, saveConfig, replaceConfigContents, getApiKey } from './config.js'
37
- import { sources } from '../sources.js'
34
+ import { loadChangelog } from '../core/changelog-loader.js'
35
+ import { getToolMeta, isModelCompatibleWithTool, getCompatibleTools, findSimilarCompatibleModels } from '../core/tool-metadata.js'
36
+ import { loadConfig, saveConfig, replaceConfigContents, getApiKey } from '../core/config.js'
37
+ import { sources } from '../../sources.js'
38
38
  import { join, dirname } from 'node:path'
39
39
  import { fileURLToPath } from 'node:url'
40
40
  import { spawn } from 'node:child_process'
41
- import { cleanupLegacyProxyArtifacts } from './legacy-proxy-cleanup.js'
41
+ import { cleanupLegacyProxyArtifacts } from '../core/legacy-proxy-cleanup.js'
42
42
  import { getLastLayout, COLUMN_SORT_MAP } from './render-table.js'
43
43
  import { cycleThemeSetting, detectActiveTheme } from './theme.js'
44
- import { syncShellEnv, ensureShellRcSource, removeShellEnv } from './shell-env.js'
44
+ import { syncShellEnv, ensureShellRcSource, removeShellEnv } from '../core/shell-env.js'
45
45
  import { buildCommandPaletteTree, flattenCommandTree, filterCommandPaletteEntries } from './command-palette.js'
46
- import { WIDTH_WARNING_MIN_COLS, VERDICT_CYCLE, HEALTH_CYCLE } from './constants.js'
47
- import { scanAllToolConfigs, softDeleteModel } from './installed-models-manager.js'
48
- import { startExternalTool } from './tool-launchers.js'
46
+ import { WIDTH_WARNING_MIN_COLS, VERDICT_CYCLE, HEALTH_CYCLE } from '../core/constants.js'
47
+ import { scanAllToolConfigs, softDeleteModel } from '../core/installed-models-manager.js'
48
+ import { startExternalTool } from '../core/tool-launchers.js'
49
49
  import {
50
50
  clearRouterDashboardRequestLog,
51
51
  closeRouterDashboardOverlay,
@@ -54,8 +54,8 @@ import {
54
54
  openRouterDashboardOverlay,
55
55
  restartRouterDashboardDaemon,
56
56
  toggleRouterDashboardProbePause,
57
- } from './router-dashboard.js'
58
- import { benchmarkModel } from './benchmark.js'
57
+ } from '../core/router-dashboard.js'
58
+ import { benchmarkModel } from '../core/benchmark.js'
59
59
 
60
60
  // 📖 Some providers need an explicit probe model because the first catalog entry
61
61
  // 📖 is not guaranteed to be accepted by their chat endpoint.
@@ -19,9 +19,9 @@
19
19
  * @see ./key-handler.js — handles keypresses for all overlay interactions
20
20
  */
21
21
 
22
- import { loadChangelog } from './changelog-loader.js'
22
+ import { loadChangelog } from '../core/changelog-loader.js'
23
23
  import { buildCliHelpLines } from './cli-help.js'
24
- import { renderRouterDashboard as renderRouterDashboardOverlay } from './router-dashboard.js'
24
+ import { renderRouterDashboard as renderRouterDashboardOverlay } from '../core/router-dashboard.js'
25
25
  import { themeColors, getThemeStatusLabel, getProviderRgb } from './theme.js'
26
26
 
27
27
  export function createOverlayRenderers(state, deps) {
@@ -47,8 +47,8 @@
47
47
  */
48
48
 
49
49
  import chalk from 'chalk'
50
- import { OVERLAY_PANEL_WIDTH, TABLE_FIXED_LINES, TABLE_HEADER_LINES, TABLE_FOOTER_LINES } from './constants.js'
51
- import { sortResults } from './utils.js'
50
+ import { OVERLAY_PANEL_WIDTH, TABLE_FIXED_LINES, TABLE_HEADER_LINES, TABLE_FOOTER_LINES } from '../core/constants.js'
51
+ import { sortResults } from '../core/utils.js'
52
52
 
53
53
  // 📖 stripAnsi: Remove ANSI color/control sequences to estimate visible text width before padding.
54
54
  // 📖 Strips CSI sequences (SGR colors) and OSC sequences (hyperlinks).
@@ -34,7 +34,7 @@
34
34
 
35
35
  import chalk from 'chalk'
36
36
  import { createRequire } from 'module'
37
- import { sources } from '../sources.js'
37
+ import { sources } from '../../sources.js'
38
38
  import {
39
39
  COL_MODEL,
40
40
  TIER_CYCLE,
@@ -44,19 +44,19 @@ import {
44
44
  WIDTH_WARNING_MIN_COLS,
45
45
  TABLE_FOOTER_LINES,
46
46
  FRAMES
47
- } from './constants.js'
47
+ } from '../core/constants.js'
48
48
  import { themeColors, currentPalette, getProviderRgb, getTierRgb, getReadableTextRgb, getTheme } from './theme.js'
49
49
  import { TIER_COLOR } from './tier-colors.js'
50
- import { getAvg, getVerdict, getUptime, getStabilityScore, getVersionStatusInfo } from './utils.js'
51
- import { usagePlaceholderForProvider } from './ping.js'
52
- import { formatBenchmarkLatency, formatBenchmarkTps } from './benchmark.js'
50
+ import { getAvg, getVerdict, getUptime, getStabilityScore, getVersionStatusInfo } from '../core/utils.js'
51
+ import { usagePlaceholderForProvider } from '../core/ping.js'
52
+ import { formatBenchmarkLatency, formatBenchmarkTps } from '../core/benchmark.js'
53
53
  import { calculateViewport, sortResultsWithPinnedFavorites, padEndDisplay, displayWidth, stripAnsi } from './render-helpers.js'
54
- import { getToolMeta, TOOL_METADATA, TOOL_MODE_ORDER, isModelCompatibleWithTool } from './tool-metadata.js'
54
+ import { getToolMeta, TOOL_METADATA, TOOL_MODE_ORDER, isModelCompatibleWithTool } from '../core/tool-metadata.js'
55
55
  import { getColumnSpacing } from './ui-config.js'
56
- import { detectPackageManager, getManualInstallCmd } from './updater.js'
56
+ import { detectPackageManager, getManualInstallCmd } from '../core/updater.js'
57
57
 
58
58
  const require = createRequire(import.meta.url)
59
- const { version: LOCAL_VERSION } = require('../package.json')
59
+ const { version: LOCAL_VERSION } = require('../../package.json')
60
60
 
61
61
  // 📖 Mouse support: column boundary map updated every frame by renderTable().
62
62
  // 📖 Each entry maps a column name to its display X-start and X-end (1-based, inclusive).
@@ -412,7 +412,7 @@ export function renderTable({
412
412
  })
413
413
 
414
414
  const lines = [
415
- ` ${themeColors.accentBold(`🚀 free-coding-models v${LOCAL_VERSION}`)}${modeBadge}${pingControlBadge}${tierBadge}${originBadge}${chalk.reset('')} ` +
415
+ ` ${chalk.rgb(118, 185, 0).bgRgb(0, 0, 0).bold(' > ')}${chalk.rgb(118, 185, 0).bgRgb(0, 0, 0).bold('free')}${chalk.rgb(255, 255, 255).bgRgb(0, 0, 0).bold('-coding-models')}${chalk.rgb(118, 185, 0).bgRgb(0, 0, 0).bold('_ ')} ${themeColors.dim(`v${LOCAL_VERSION}`)}${modeBadge}${pingControlBadge}${tierBadge}${originBadge}${chalk.reset('')} ` +
416
416
  themeColors.dim('📦 ') + themeColors.accentBold(`${completedPings}/${totalVisible}`) + themeColors.dim(' ') +
417
417
  themeColors.success(`✅ ${up}`) + themeColors.dim(' up ') +
418
418
  themeColors.warning(`⏳ ${timeout}`) + themeColors.dim(' timeout ') +
@@ -26,9 +26,9 @@
26
26
  * @see src/tui-state.js — state shape for filter mode indices
27
27
  */
28
28
 
29
- import { TIER_CYCLE, VERDICT_CYCLE, HEALTH_CYCLE } from './constants.js'
30
- import { TIER_LETTER_MAP } from './utils.js'
31
- import { getVerdict } from './utils.js'
29
+ import { TIER_CYCLE, VERDICT_CYCLE, HEALTH_CYCLE } from '../core/constants.js'
30
+ import { TIER_LETTER_MAP } from '../core/utils.js'
31
+ import { getVerdict } from '../core/utils.js'
32
32
 
33
33
  /**
34
34
  * 📖 createTuiFilters: Build the filter functions for a given TUI state + dependencies.
@@ -24,7 +24,7 @@
24
24
  * @see src/ping-loop.js — reads ping mode fields from the state
25
25
  */
26
26
 
27
- import { WIDTH_WARNING_MIN_COLS } from './constants.js'
27
+ import { WIDTH_WARNING_MIN_COLS } from '../core/constants.js'
28
28
 
29
29
  // 📖 Ping cadence intervals per mode (ms). Speed = startup burst, normal = steady,
30
30
  // 📖 slow = idle throttle, forced = user-triggered fast burst.
package/web/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # 📦 free-coding-models-web
2
+
3
+ This directory contains the shared React SPA (Single Page Application) dashboard for `free-coding-models`.
4
+
5
+ ## 🌐 Architecture
6
+
7
+ The frontend is a single SPA that is served in two distinct scenarios:
8
+ 1. **Web Dashboard / Docker Mode (`--daemon`):** Served directly by the local Node.js `router-daemon` process on `http://localhost:19280/`.
9
+ 2. **Desktop Mode (Tauri App):** Loaded locally inside Tauri's native webview from embedded assets in `web/dist/`, communicating via HTTP fetch to the background engine.
10
+
11
+ To maintain maximum code sharing, **95%+ of all components and logic are kept completely identical** between the two distributions.
12
+
13
+ ---
14
+
15
+ ## ⚡ API & Event Integration
16
+
17
+ The React app uses a realtime-first connection strategy against the local engine:
18
+ * **Socket.IO** is preferred in dev/web-server mode for instant per-model ping and benchmark updates.
19
+ * **`GET /api/events` / `EventSource`** is the streaming fallback used by daemon/Docker surfaces.
20
+ * **`GET /api/state`** returns the wrapped live dashboard state for REST fallback polling.
21
+ * **`GET /api/models`** remains the legacy flat model catalog endpoint for simple clients.
22
+ * **`GET /api/config`** retrieves active provider toggles (keys are masked).
23
+ * **`POST /api/settings`** updates API keys and provider preferences.
24
+ * **`POST /api/global-benchmark`** benchmarks only the models currently visible in the web table, so filters/search control the benchmark scope.
25
+
26
+ ---
27
+
28
+ ## 🛠️ Development & Building
29
+
30
+ ### Prerequisites
31
+ Make sure you have `pnpm` installed and dependencies initialized at the root of the project.
32
+
33
+ ### 1. Dev Server (HMR)
34
+ To start the React frontend with Vite HMR (Hot Module Replacement):
35
+ ```bash
36
+ cd web
37
+ pnpm dev
38
+ ```
39
+ By default, the dev server runs on `http://localhost:5173/`. Ensure a background daemon is running on port `19280` so the API requests proxy correctly.
40
+
41
+ ### 2. Production Build
42
+ To compile the production-ready SPA:
43
+ ```bash
44
+ pnpm build
45
+ ```
46
+ This bundles the HTML, JS, and CSS assets into the `web/dist/` directory, which is then embedded inside both the CLI daemon and the Tauri desktop app binary.