Bump to 0.1.0:1 — portability + endpoint display

- configureSparks.ts: generic placeholders (e.g. 192.168.1.10), no Alice-specific IPs; descriptions explain the role of each node instead of naming his hardware
- showPublicKey.ts: reads sparkConfig.yaml; emits a ready-to-paste one-liner (KEY='...' followed by 'ssh user@host "echo $KEY >> authorized_keys"' for each configured Spark). Falls back to generic instructions if Configure Sparks hasn't been run yet.
- /api/status now includes vllm.base_url for the OpenAI endpoint
- New endpoint panel in UI: base URL + model ID rows with copy buttons + collapsible curl example
- Bump version to 0.1.0:1
This commit is contained in:
Grant
2026-05-12 10:38:18 -05:00
parent 87334f85f0
commit 0ddab99468
7 changed files with 202 additions and 22 deletions
+48
View File
@@ -83,6 +83,52 @@ function renderCurrent(status) {
c.innerHTML = `<strong>${label}</strong>`;
}
function renderEndpoint(status) {
const v = status.vllm || {};
const panel = el('#endpoint-panel');
const ready = v.ok && v.current_model && v.base_url;
panel.classList.toggle('hidden', !ready);
if (!ready) return;
el('#ep-url').textContent = v.base_url;
el('#ep-model').textContent = v.current_model;
const snippet =
`curl -s ${v.base_url}/chat/completions \\
-H 'content-type: application/json' \\
-d '{
"model": "${v.current_model}",
"messages": [{"role": "user", "content": "Hello"}]
}'`;
el('#ep-curl-snippet').textContent = snippet;
}
function setupCopyButtons() {
document.body.addEventListener('click', async (e) => {
const btn = e.target.closest('.copy-btn');
if (!btn) return;
const targetSel = btn.dataset.copy;
if (!targetSel) return;
const target = el(targetSel);
if (!target) return;
const text = target.textContent;
try {
await navigator.clipboard.writeText(text);
const original = btn.textContent;
btn.classList.add('copied');
btn.textContent = 'Copied';
setTimeout(() => {
btn.classList.remove('copied');
btn.textContent = original;
}, 1400);
} catch {
// Clipboard API may fail over plain HTTP; fall back to selection
const range = document.createRange();
range.selectNode(target);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
}
});
}
function renderHealth(status) {
function setDot(id, ok, payload) {
const item = el(id);
@@ -221,6 +267,7 @@ async function pollStatus() {
state.configured = status.configured;
renderBanner(status);
renderCurrent(status);
renderEndpoint(status);
renderHealth(status);
if (status.current_swap_job && status.current_swap_job !== state.swap_job_id) {
attachToSwap(status.current_swap_job, /*needsBackfill=*/true);
@@ -342,6 +389,7 @@ function appendLog(line) {
}
async function init() {
setupCopyButtons();
await loadModels();
await pollStatus();
setInterval(pollStatus, 5000);