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.
- package/README.md +8 -3
- package/changelog/v0.5.76.md +16 -0
- package/changelog/v0.5.77.md +11 -0
- package/changelog/v0.5.78.md +17 -0
- package/package.json +1 -1
- package/src/core/config.js +71 -10
- package/src/core/endpoint-installer.js +4 -2
- package/src/core/router-daemon.js +68 -16
- package/src/core/router-dashboard.js +23 -3
- package/src/core/schema-normalizer.js +18 -1
- package/src/core/tool-launchers.js +6 -0
- package/src/tui/command-palette.js +19 -2
- package/src/tui/key-handler.js +1 -3
- package/src/tui/overlays.js +2 -0
- package/src/tui/tui-state.js +18 -4
- package/web/dist/assets/{index-wI9xrm0w.css → index-C5-Ywvv3.css} +1 -1
- package/web/dist/assets/{index-C98mFTET.js → index-DV-1tGVj.js} +3 -3
- package/web/dist/index.html +2 -2
- package/web/server.js +4 -3
- package/web/src/components/router/RouterView.jsx +65 -2
- package/web/src/components/router/RouterView.module.css +109 -0
- package/web/src/hooks/useToolMode.js +1 -1
- package/web/src/utils/m3.js +2 -1
package/web/dist/index.html
CHANGED
|
@@ -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-
|
|
52
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
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,
|
|
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
|
-
|
|
611
|
-
|
|
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
|
-
},
|
|
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
|
-
|
|
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 =
|
|
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])
|
package/web/src/utils/m3.js
CHANGED