free-coding-models 0.5.75 → 0.5.78

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.
@@ -48,8 +48,8 @@
48
48
  <link rel="preconnect" href="https://fonts.googleapis.com">
49
49
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
50
50
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
51
- <script type="module" crossorigin src="/assets/index-C98mFTET.js"></script>
52
- <link rel="stylesheet" crossorigin href="/assets/index-wI9xrm0w.css">
51
+ <script type="module" crossorigin src="/assets/index-DV-1tGVj.js"></script>
52
+ <link rel="stylesheet" crossorigin href="/assets/index-C5-Ywvv3.css">
53
53
  </head>
54
54
  <body>
55
55
  <div id="root"></div>
package/web/server.js CHANGED
@@ -51,7 +51,7 @@ import { benchmarkModel, BENCHMARK_TIMEOUT_MS, BENCHMARK_PROMPT } from '../src/c
51
51
  import { getInstallTargetModes, installProviderEndpoints, getConfiguredInstallableProviders, getProviderCatalogModels } from '../src/core/endpoint-installer.js'
52
52
  import { isModelCompatibleWithTool } from '../src/core/tool-metadata.js'
53
53
  import { sendUsageTelemetry } from '../src/core/telemetry.js'
54
- import { getRouterDaemonStatus, startRouterDaemonBackground, stopRouterDaemon, ROUTER_TOKENS_PATH, getRouterPortPath } from '../src/core/router-daemon.js'
54
+ import { getRouterDaemonStatus, startRouterDaemonBackground, stopRouterDaemon, getRouterTokensPath, getRouterPortPath } from '../src/core/router-daemon.js'
55
55
  import { scanAllToolConfigs, softDeleteModel } from '../src/core/installed-models-manager.js'
56
56
  import {
57
57
  TASK_TYPES,
@@ -607,8 +607,9 @@ async function proxyToDaemon(path, options = {}) {
607
607
 
608
608
  function readTokenFile() {
609
609
  try {
610
- if (!existsSync(ROUTER_TOKENS_PATH)) return null
611
- return JSON.parse(readFileSync(ROUTER_TOKENS_PATH, 'utf8'))
610
+ const tokensPath = getRouterTokensPath()
611
+ if (!existsSync(tokensPath)) return null
612
+ return JSON.parse(readFileSync(tokensPath, 'utf8'))
612
613
  } catch { return null }
613
614
  }
614
615
 
@@ -25,6 +25,16 @@ function formatUptime(seconds) {
25
25
  return `${s}s`
26
26
  }
27
27
 
28
+ function formatTime(isoString) {
29
+ if (!isoString) return '—'
30
+ try {
31
+ const d = new Date(isoString)
32
+ return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false })
33
+ } catch {
34
+ return '—'
35
+ }
36
+ }
37
+
28
38
  function formatNumber(n) {
29
39
  if (typeof n !== 'number' || !Number.isFinite(n)) return '0'
30
40
  if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`
@@ -119,7 +129,7 @@ export default function RouterView({ onClose, onToast, favorites }) {
119
129
  const interval = setInterval(() => {
120
130
  void fetchStatus()
121
131
  void fetchSets()
122
- }, 5000)
132
+ }, 2000)
123
133
  return () => clearInterval(interval)
124
134
  }, [fetchStatus, fetchSets, fetchCatalog])
125
135
 
@@ -528,6 +538,7 @@ export default function RouterView({ onClose, onToast, favorites }) {
528
538
  const running = status?.ok
529
539
  const circuitBreakers = stats?.circuitBreakers || {}
530
540
  const requestLog = stats?.requestLog || []
541
+ const activeRequests = status?.activeRequests || []
531
542
 
532
543
  // 📖 routingOrder — the exact attempt order the daemon will use for the next
533
544
  // 📖 request (priority-first among healthy models). routingOrder[0] is the
@@ -957,6 +968,46 @@ export default function RouterView({ onClose, onToast, favorites }) {
957
968
  </div>
958
969
  )}
959
970
 
971
+ {/* Active Requests */}
972
+ {running && (
973
+ <div className={styles.section}>
974
+ <h3 className={styles.sectionTitle}>
975
+ <IconBolt size={14} className={activeRequests.length > 0 ? styles.pulse : ''} />
976
+ Active Requests ({activeRequests.length})
977
+ </h3>
978
+ {activeRequests.length > 0 ? (
979
+ <div className={styles.activeList}>
980
+ {activeRequests.map((req) => (
981
+ <div key={req.request_id} className={styles.activeRow}>
982
+ <div className={styles.activeMain}>
983
+ <span className={styles.activeModel}>
984
+ <span className={styles.activeId} title={`Request ID: ${req.request_id}`}>
985
+ {req.request_id.slice(-4)}
986
+ </span>
987
+ {req.model}
988
+ </span>
989
+ <span className={styles.activeStatus}>
990
+ {req.stalled ? (
991
+ <span className={styles.stalledText}>Stalled?</span>
992
+ ) : (
993
+ <span className={styles.processingText}>Processing...</span>
994
+ )}
995
+ </span>
996
+ </div>
997
+ <div className={styles.activeDetails}>
998
+ <span className={styles.activeCurrentModel}>{req.current_model || 'Routing...'}</span>
999
+ {req.stream && <span className={styles.activeTokens}>{req.tokens} tokens</span>}
1000
+ <span className={styles.activeUptime}>{formatUptime(Math.floor((Date.now() - req.started_at) / 1000))}</span>
1001
+ </div>
1002
+ </div>
1003
+ ))}
1004
+ </div>
1005
+ ) : (
1006
+ <div className={styles.logEmpty}>No active requests.</div>
1007
+ )}
1008
+ </div>
1009
+ )}
1010
+
960
1011
  {/* Request Log — always visible when running */}
961
1012
  {running && (
962
1013
  <div className={styles.section}>
@@ -972,6 +1023,12 @@ export default function RouterView({ onClose, onToast, favorites }) {
972
1023
  <div className={styles.logList}>
973
1024
  {requestLog.map((entry, i) => (
974
1025
  <div key={i} className={styles.logRow}>
1026
+ <span className={styles.logId} title={`Request ID: ${entry.request_id || '—'}`}>
1027
+ {entry.request_id ? entry.request_id.slice(-4) : '—'}
1028
+ </span>
1029
+ <span className={styles.logTime}>
1030
+ {formatTime(entry.at)}
1031
+ </span>
975
1032
  <span className={entry.error ? styles.logErr : styles.logOk}>
976
1033
  {entry.status || '—'}
977
1034
  </span>
@@ -982,7 +1039,13 @@ export default function RouterView({ onClose, onToast, favorites }) {
982
1039
  <span className={styles.logTokens}>
983
1040
  {entry.tokens > 0 ? formatNumber(entry.tokens) + ' tok' : ''}
984
1041
  </span>
985
- {entry.failover && <span className={styles.logFailover}>failover</span>}
1042
+ <span className={styles.logDetail}>
1043
+ {[
1044
+ entry.failover ? 'failover' : '',
1045
+ entry.stream ? 'stream' : '',
1046
+ entry.error || '',
1047
+ ].filter(Boolean).join(', ')}
1048
+ </span>
986
1049
  </div>
987
1050
  ))}
988
1051
  </div>
@@ -355,6 +355,95 @@
355
355
  .circuitAuth { background: rgba(255, 140, 0, 0.15); color: #ff8c00; }
356
356
  .circuitUnknown { background: rgba(128, 128, 128, 0.15); color: #888; }
357
357
 
358
+ .pulse {
359
+ color: #fbbf24;
360
+ animation: pulse 2s infinite;
361
+ }
362
+
363
+ @keyframes pulse {
364
+ 0% { opacity: 0.4; }
365
+ 50% { opacity: 1; }
366
+ 100% { opacity: 0.4; }
367
+ }
368
+
369
+ .activeList {
370
+ display: flex;
371
+ flex-direction: column;
372
+ gap: 8px;
373
+ margin-top: 8px;
374
+ }
375
+
376
+ .activeRow {
377
+ background: rgba(251, 191, 36, 0.05);
378
+ border: 1px solid rgba(251, 191, 36, 0.2);
379
+ border-radius: 8px;
380
+ padding: 10px 12px;
381
+ }
382
+
383
+ .activeMain {
384
+ display: flex;
385
+ justify-content: space-between;
386
+ align-items: center;
387
+ margin-bottom: 4px;
388
+ }
389
+
390
+ .activeModel {
391
+ font-weight: 600;
392
+ font-size: 14px;
393
+ color: var(--text-primary, #e0e0e0);
394
+ display: flex;
395
+ align-items: center;
396
+ gap: 8px;
397
+ }
398
+
399
+ .activeId {
400
+ color: var(--text-muted, #666);
401
+ font-size: 10px;
402
+ background: rgba(255, 255, 255, 0.05);
403
+ padding: 1px 4px;
404
+ border-radius: 4px;
405
+ font-family: 'SF Mono', monospace;
406
+ font-weight: normal;
407
+ }
408
+
409
+ .activeStatus {
410
+ font-size: 12px;
411
+ }
412
+
413
+ .processingText {
414
+ color: #10b981;
415
+ font-weight: 500;
416
+ }
417
+
418
+ .stalledText {
419
+ color: #ef4444;
420
+ font-weight: 700;
421
+ animation: flash 1s infinite;
422
+ }
423
+
424
+ @keyframes flash {
425
+ 0% { opacity: 1; }
426
+ 50% { opacity: 0.5; }
427
+ 100% { opacity: 1; }
428
+ }
429
+
430
+ .activeDetails {
431
+ display: flex;
432
+ gap: 12px;
433
+ font-size: 11px;
434
+ color: var(--text-muted, #888);
435
+ }
436
+
437
+ .activeCurrentModel {
438
+ color: var(--text-secondary, #b0b0b0);
439
+ }
440
+
441
+ .activeTokens {
442
+ background: rgba(255, 255, 255, 0.05);
443
+ padding: 0 4px;
444
+ border-radius: 3px;
445
+ }
446
+
358
447
  /* Request Log */
359
448
  .logList {
360
449
  display: flex;
@@ -371,11 +460,31 @@
371
460
  font-family: 'SF Mono', monospace;
372
461
  }
373
462
 
463
+ .logTime { color: var(--text-muted, #555); min-width: 60px; }
464
+ .logId {
465
+ color: var(--text-muted, #666);
466
+ font-size: 9px;
467
+ min-width: 32px;
468
+ background: rgba(255, 255, 255, 0.03);
469
+ padding: 1px 3px;
470
+ border-radius: 3px;
471
+ text-align: center;
472
+ font-family: 'SF Mono', monospace;
473
+ }
374
474
  .logOk { color: #00c864; min-width: 28px; }
375
475
  .logErr { color: #dc3545; min-width: 28px; }
376
476
  .logModel { color: var(--text-secondary, #aaa); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
377
477
  .logLatency { color: var(--text-muted, #888); min-width: 50px; text-align: right; }
378
478
  .logTokens { color: var(--text-muted, #888); min-width: 60px; text-align: right; }
479
+ .logDetail {
480
+ color: var(--text-muted, #666);
481
+ font-size: 10px;
482
+ flex: 1;
483
+ text-align: right;
484
+ overflow: hidden;
485
+ text-overflow: ellipsis;
486
+ white-space: nowrap;
487
+ }
379
488
  .logFailover {
380
489
  font-size: 9px;
381
490
  padding: 1px 4px;
@@ -61,7 +61,7 @@ export function useToolMode({ onToast } = {}) {
61
61
  }, [onToast])
62
62
 
63
63
  const cycleToolMode = useCallback(() => {
64
- const currentIndex = TOOL_MODE_ORDER.indexOf(toolModeState)
64
+ const currentIndex = INSTALL_ENDPOINT_TOOL_MODES.indexOf(toolModeState)
65
65
  const next = INSTALL_ENDPOINT_TOOL_MODES[(currentIndex + 1) % INSTALL_ENDPOINT_TOOL_MODES.length] || DEFAULT_TOOL_MODE
66
66
  return persistToolMode(next)
67
67
  }, [persistToolMode, toolModeState])
@@ -11,12 +11,13 @@
11
11
 
12
12
  export const INSTALL_ENDPOINT_TOOL_MODES = [
13
13
  'opencode',
14
+ 'pi',
15
+ 'jcode',
14
16
  'opencode-desktop',
15
17
  'opencode-web',
16
18
  'openclaw',
17
19
  'crush',
18
20
  'goose',
19
- 'pi',
20
21
  'aider',
21
22
  'qwen',
22
23
  'openhands',