Skip to content

Commit 5d18d14

Browse files
committed
feat: show bridge version in dashboard header
1 parent 27092ab commit 5d18d14

4 files changed

Lines changed: 22 additions & 3 deletions

File tree

src/dashboard.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { BRIDGE_VERSION } from "./version.js";
2+
13
function scriptJson(value: unknown): string {
24
return JSON.stringify(value).replace(/</g, "\\u003c");
35
}
@@ -29,7 +31,7 @@ export function dashboardHtml(initialConfig: unknown = null): string {
2931
<main class="wrap">
3032
<section class="top">
3133
<div class="brand"><a class="eyebrow" href="https://github.com/yelixir-dev" target="_blank" rel="noopener noreferrer">github.com/yelixir-dev</a><h1>CommandCode Bridge Console</h1></div>
32-
<div class="status"><span class="pill"><i id="dot" class="dot"></i><span id="online">checking</span></span><div id="endpoint" class="small"></div></div>
34+
<div class="status"><span class="pill"><i id="dot" class="dot"></i><span id="online">checking</span></span><div id="bridgeVersion" class="small">v${BRIDGE_VERSION}</div></div>
3335
</section>
3436
<section class="grid">
3537
<div class="card wide"><h2>Server Bind <span class="sub">after restart</span></h2><div class="bind-grid"><div class="field"><label>Bind host</label><select id="bindHost"><option value="127.0.0.1">127.0.0.1 · local only</option><option value="0.0.0.0">0.0.0.0 · LAN/Tailscale</option></select></div><div class="field"><label>Port</label><input id="bindPort" type="number" min="1" max="65535" /></div></div><div id="bridgeKeyWrap" class="field bridge-key"><label>Admin API Key</label><div class="bridge-key-row"><span class="bridge-key-prefix">sk-</span><input id="bridgeApiKey" type="text" autocomplete="off" spellcheck="false" placeholder="cmdbridge-랜덤6 · 복사/수정 가능" /></div><div class="bridge-key-help-row"><span class="bridge-key-help">0.0.0.0/LAN 공개 시 필요</span><button id="copyBridgeKey" class="secondary" type="button" aria-label="Copy Admin API Key">📋</button><button id="saveBridgeKey" class="secondary" type="button" aria-label="Save Admin API Key">💾</button></div></div></div>
@@ -55,7 +57,7 @@ function updateRestart(){const online=$('online').textContent==='online'; $('res
5557
async function fetchJson(path,opt={}){const init={...opt,cache:'no-store',headers:{...auth(),...(opt.headers||{})}}; try{const r=await fetch(path,init); if(!r.ok) throw new Error(await r.text()); return r.json();}catch(e){if(location.hostname&&!location.port){const r=await fetch('http://'+location.hostname+':9992'+path,init); if(!r.ok) throw new Error(await r.text()); return r.json();}throw e;}}
5658
async function api(path,opt={}){return fetchJson(path,opt)}
5759
async function health(){return fetchJson('/health')}
58-
function setOnline(h){$('dot').className='dot on'; $('online').textContent='online'; $('endpoint').textContent=(h.endpoint||((location.hostname||'127.0.0.1')+':'+(location.port||'9992')))}
60+
function setOnline(h){$('dot').className='dot on'; $('online').textContent='online'; $('bridgeVersion').textContent='v'+(h.version||'${BRIDGE_VERSION}')}
5961
async function load(){if(cfg){if(cfg.bridge) setOnline(cfg.bridge); render(); setDirty(cfg.dirty||false);} try{const h=await health(); setOnline(h);}catch{ if(!cfg){$('dot').className='dot off'; $('online').textContent='offline';} } updateRestart(); try{cfg=await api('/admin/config'); if(cfg.bridge) setOnline(cfg.bridge); try{const m=await api('/admin/commandcode/credentials?refresh=true'); const byId=new Map((m.credentials||[]).map(x=>[x.id,x])); cfg.credentials.forEach(c=>c.metrics=byId.get(c.id));}catch{} render(); setDirty(cfg.dirty||false);}catch(e){if(!cfg) toast('설정 로드 실패: 포트 9992 주소로 다시 열어주세요.');}}
6062
function render(){if(!cfg)return; $('bindHost').value=cfg.server.host; $('bindPort').value=cfg.server.port; $('maxPer').value=cfg.routing.maxInFlightPerCredential||4; syncBridgeKey();
6163
$('policies').innerHTML=policies.map(p=>'<label class="policy"><input type="radio" name="policy" value="'+esc(p[0])+'" '+(cfg.routing.policy===p[0]?'checked':'')+'><b>'+esc(p[1])+'</b><span class="info" tabindex="0" aria-label="'+esc(p[1])+' 설명">ℹ️<span class="tip">'+esc(p[2])+'<br><span class="token">'+esc(p[0])+'</span></span></span></label>').join(''); document.querySelectorAll('input[name=policy]').forEach(el=>el.onchange=()=>{cfg.routing.policy=el.value;setDirty();});

src/server.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
} from "./credential-router.js";
2424
import { CommandCodeBalanceAlertManager } from "./balance-alerts.js";
2525
import { buildCommandCodeGenerateBody, isSupportedToolChoice } from "./converter.js";
26+
import { BRIDGE_VERSION } from "./version.js";
2627
import {
2728
collectOpenAICompletion,
2829
CommandCodeEmptyResponseError,
@@ -220,6 +221,7 @@ function dashboardConfigResponse(
220221
})),
221222
bridge: {
222223
online: true,
224+
version: BRIDGE_VERSION,
223225
endpoint: `${config.host}:${config.port}`,
224226
port: config.port,
225227
models: publicModelList(config),
@@ -343,7 +345,7 @@ export async function createApp(options: CreateAppOptions = {}): Promise<Fastify
343345
app.get("/health", async () => ({
344346
status: "ok",
345347
service: "commandcode-bridge",
346-
version: "0.1.0",
348+
version: BRIDGE_VERSION,
347349
upstream: "commandcode-alpha-generate",
348350
endpoint: `${config.host}:${config.port}`,
349351
port: config.port,

src/version.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const BRIDGE_VERSION = "0.1.0";

tests/dashboard-ui.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@ import { describe, expect, it } from "vitest";
33
import { dashboardHtml } from "../src/dashboard.js";
44

55
describe("dashboard UI", () => {
6+
it("shows the bridge version in the header instead of the endpoint", () => {
7+
const html = dashboardHtml({
8+
server: { host: "0.0.0.0", port: 9992 },
9+
routing: { policy: "daily_burn_priority", maxInFlightPerCredential: 4 },
10+
credentials: [],
11+
models: [],
12+
bridge: { online: true, endpoint: "0.0.0.0:9992", version: "0.1.0" },
13+
});
14+
15+
expect(html).toContain('id="bridgeVersion"');
16+
expect(html).toContain("v0.1.0");
17+
expect(html).not.toContain('id="endpoint"');
18+
});
19+
620
it("shows only the key-level concurrency field, not total concurrency controls", () => {
721
const html = dashboardHtml({
822
server: { host: "127.0.0.1", port: 9992 },

0 commit comments

Comments
 (0)