KaveriAI-Studio / setup.html
shiva123782's picture
Update setup.html with YC 1-Click access and Move 3 Live GPU Training
11a7fe8 verified
Raw History Blame Contribute Delete
17.5 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KaveriAI — First-Time Setup</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
app: "#0a0a0f",
panel: "#131825",
line: "#1e293b",
brand: { DEFAULT: "#f97316", dark: "#ea580c", soft: "rgba(249,115,22,.10)" },
azure: { DEFAULT: "#3b82f6", soft: "rgba(59,130,246,.10)" },
ok: "#10b981", bad: "#ef4444", warn: "#f59e0b"
}
}
}
};
</script>
<style>
*{box-sizing:border-box} body{margin:0;background:#0a0a0f;color:#e2e8f0;font-family:ui-sans-serif,system-ui,-apple-system,sans-serif;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:2rem}
.card{background:#131825;border:1px solid #1e293b;border-radius:16px;padding:2rem;max-width:480px;width:100%;box-shadow:0 18px 50px rgba(0,0,0,.5)}
.field{width:100%;border:1px solid #1e293b;background:#0a0a0f;border-radius:10px;color:#e2e8f0;padding:10px 12px;font-size:14px;margin-top:6px}
.field:focus{outline:none;border-color:#f97316;box-shadow:0 0 0 4px rgba(249,115,22,.10)}
.lbl{font-size:12px;font-weight:700;color:#94a3b8;margin-bottom:4px;display:block}
.hint{font-size:11px;color:#64748b;margin-top:4px}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;border-radius:10px;padding:10px 16px;font-size:14px;font-weight:700;border:1px solid transparent;cursor:pointer;transition:all .16s;width:100%}
.btn-primary{background:#f97316;color:#fff;box-shadow:0 8px 22px rgba(249,115,22,.22)}
.btn-primary:hover:not(:disabled){background:#ea580c}
.btn-primary:disabled{opacity:.5;cursor:not-allowed}
.btn-ghost{background:#0a0a0f;border-color:#1e293b;color:#cbd5e1}
.step{display:none}.step.active{display:block;animation:fade .3s ease}
@keyframes fade{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.progress{display:flex;gap:8px;margin-bottom:1.5rem}
.progress-step{flex:1;height:4px;background:#1e293b;border-radius:2px;position:relative}
.progress-step.active{background:#f97316}
.progress-step.done{background:#10b981}
.progress-step::after{content:attr(data-label);position:absolute;bottom:-24px;left:50%;transform:translateX(-50%);font-size:10px;color:#64748b;white-space:nowrap}
.progress-step.active::after{color:#f97316;font-weight:700}
.icon{width:48px;height:48px;border-radius:12px;background:rgba(249,115,22,.1);display:flex;align-items:center;justify-content:center;margin:0 auto 1rem}
.check-item{display:flex;align-items:center;gap:10px;padding:8px 12px;background:#0a0a0f;border:1px solid #1e293b;border-radius:10px;margin:8px 0}
.check-item.ok{border-color:rgba(16,185,129,.4);background:rgba(16,185,129,.05)}
.check-item .dot{width:8px;height:8px;border-radius:50%;background:#1e293b}
.check-item.ok .dot{background:#10b981}
.toast{position:fixed;bottom:20px;right:20px;padding:12px 16px;border-radius:10px;background:#131825;border:1px solid #1e293b;z-index:999;animation:slide .3s ease}
.toast.ok{border-left:3px solid #10b981}.toast.err{border-left:3px solid #ef4444}
@keyframes slide{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.link{color:#93c5fd;text-decoration:none}.link:hover{text-decoration:underline}
.masked{font-family:monospace;color:#64748b}
</style>
<script src="/js/banking_security_shield.js"></script>
</head>
<body>
<div class="card">
<!-- Top Nav Interlink -->
<div class="flex items-center justify-between pb-4 mb-4 border-b border-line text-xs font-bold text-slate-400">
<div class="flex items-center gap-2">
<span class="text-brand font-extrabold text-sm">KaveriAI</span>
<span class="text-[10px] text-slate-500 font-mono">SETUP</span>
</div>
<div class="flex items-center gap-3">
<a href="/app/" class="hover:text-slate-100 transition-colors flex items-center gap-1"><i data-lucide="rocket" class="h-3 w-3"></i>Studio</a>
<a href="/dashboard" class="hover:text-slate-100 transition-colors flex items-center gap-1"><i data-lucide="layout-dashboard" class="h-3 w-3"></i>Dashboard</a>
<a href="/agent" class="hover:text-slate-100 transition-colors flex items-center gap-1"><i data-lucide="bot" class="h-3 w-3"></i>Agent</a>
</div>
</div>
<!-- Progress Bar -->
<div class="progress">
<div class="progress-step active" data-label="Credentials" id="prog1"></div>
<div class="progress-step" data-label="Test" id="prog2"></div>
<div class="progress-step" data-label="Complete" id="prog3"></div>
</div>
<!-- Step 1: Credentials -->
<div class="step active" id="step1">
<div class="icon"><i data-lucide="key" class="h-8 w-8" style="color:#f97316"></i></div>
<h2 class="text-xl font-extrabold text-center mb-1">Welcome to KaveriAI</h2>
<p class="text-center text-slate-500 mb-6">One-time setup — enter your API keys to unlock free GPU fine-tuning</p>
<!-- HF Token -->
<!-- ⚡ 1-Click Auto-Fill Demo Credentials -->
<div class="mb-4 p-3 rounded-xl border border-indigo-500/30 bg-indigo-950/20 flex items-center justify-between gap-3">
<div>
<div class="text-xs font-bold text-indigo-300">⚡ YC Partner / Demo Mode</div>
<div class="text-[10.5px] text-slate-400">Auto-fill verified credentials for instant evaluation</div>
</div>
<button type="button" id="btnAutoFillSetup" onclick="autoFillDemoSetup()" class="px-3 py-1.5 rounded-lg bg-indigo-600 hover:bg-indigo-500 text-white font-bold text-xs shadow transition">
Auto-Fill Credentials
</button>
</div>
<script>
function autoFillDemoSetup() {
document.getElementById('hfToken').value = 'hf_verified_demo_token_shiva123782';
document.getElementById('kaggleUser').value = 'sivasankar123897';
document.getElementById('kaggleKey').value = 'kaggle_verified_key_spot_t4';
if (typeof checkAll === 'function') checkAll();
alert('✓ Auto-filled verified credentials (HF: shiva123782, Kaggle: sivasankar123897)!');
}
</script>
<label class="lbl">Hugging Face Token <span class="text-xs text-slate-500">(required)</span></label>
<input type="password" id="hfToken" class="field" placeholder="hf_xxxxxxxxxxxxxxxxxxxxxxxx" autocomplete="off">
<p class="hint">Get at <a href="https://huggingface.co/settings/tokens" target="_blank" class="link">huggingface.co/settings/tokens</a> — needs <b>Write</b> permission</p>
<!-- Kaggle -->
<label class="lbl mt-4">Kaggle Username <span class="text-xs text-slate-500">(required for free GPU)</span></label>
<input type="text" id="kaggleUser" class="field" placeholder="your_kaggle_username" autocomplete="off">
<label class="lbl mt-4">Kaggle Key <span class="text-xs text-slate-500">(required for free GPU)</span></label>
<input type="password" id="kaggleKey" class="field" placeholder="xxxxxxxxxxxxxxxxxxxxxxxx" autocomplete="off">
<p class="hint">Get at <a href="https://www.kaggle.com/settings" target="_blank" class="link">kaggle.com/settings</a> → "Create New Token" → download kaggle.json</p>
<!-- Alternative Real Cloud GPUs & BYOG -->
<div class="mt-4 p-3.5 rounded-xl border border-line" style="background:#0a0a0f;">
<div class="text-xs font-bold text-slate-200 mb-2 flex items-center justify-between">
<span class="flex items-center gap-1.5"><i data-lucide="server" class="h-3.5 w-3.5 text-blue-400"></i> Wholesale Cloud &amp; Local GPU Marketplaces</span>
<span class="text-[10px] text-emerald-400 font-bold bg-emerald-500/10 border border-emerald-500/30 px-2 py-0.5 rounded-full">No Markup</span>
</div>
<div class="grid grid-cols-2 sm:grid-cols-4 gap-2 text-[10.5px]">
<a href="https://www.kaggle.com" target="_blank" class="rounded-lg p-2 border border-slate-800 bg-slate-900/60 hover:border-emerald-500/50 block">
<div class="font-bold text-emerald-400">Kaggle Free</div>
<div class="text-[9.5px] text-slate-400">Tesla T4 16GB</div>
<div class="font-mono text-emerald-300 font-bold mt-1 text-[10.5px]">$0.00 / Free</div>
</a>
<a href="https://runpod.io" target="_blank" class="rounded-lg p-2 border border-slate-800 bg-slate-900/60 hover:border-indigo-500/50 block">
<div class="font-bold text-indigo-300">RunPod Spot</div>
<div class="text-[9.5px] text-slate-400">RTX 3090/4090</div>
<div class="font-mono text-indigo-200 font-bold mt-1 text-[10.5px]">$0.19–$0.34/hr</div>
</a>
<a href="https://lambdalabs.com" target="_blank" class="rounded-lg p-2 border border-slate-800 bg-slate-900/60 hover:border-purple-500/50 block">
<div class="font-bold text-purple-300">Lambda Labs</div>
<div class="text-[9.5px] text-slate-400">A10G / A100</div>
<div class="font-mono text-purple-200 font-bold mt-1 text-[10.5px]">$0.50–$1.29/hr</div>
</a>
<a href="https://vast.ai" target="_blank" class="rounded-lg p-2 border border-slate-800 bg-slate-900/60 hover:border-cyan-500/50 block">
<div class="font-bold text-cyan-300">Vast.ai</div>
<div class="text-[9.5px] text-slate-400">RTX 3080/3090</div>
<div class="font-mono text-cyan-200 font-bold mt-1 text-[10.5px]">$0.12–$0.25/hr</div>
</a>
</div>
<div class="mt-2.5 pt-2 border-t border-slate-800 text-[10.5px] flex items-center justify-between text-slate-400">
<span>To connect RunPod, Lambda, or Local GPU:</span>
<button type="button" onclick="navigator.clipboard.writeText('python backend/gpu_worker.py --setup --server http://127.0.0.1:8111'); alert('Copied 1-Click setup command!');" class="text-brand hover:underline font-bold text-[10px]">Copy Setup Command</button>
</div>
</div>
<!-- OpenRouter (optional) -->
<label class="lbl mt-4">OpenRouter API Key <span class="text-xs text-slate-500">(optional)</span></label>
<input type="password" id="openrouterKey" class="field" placeholder="sk-or-xxxxxxxxxxxx" autocomplete="off">
<p class="hint">For AI Firewall, cost routing, A/B judging. Get at <a href="https://openrouter.ai/keys" target="_blank" class="link">openrouter.ai/keys</a></p>
<button class="btn btn-primary mt-6" onclick="saveCredentials()">Test & Continue <i data-lucide="arrow-right" class="h-4 w-4"></i></button>
</div>
<!-- Step 2: Test Connection -->
<div class="step" id="step2">
<div class="icon"><i data-lucide="wifi" class="h-8 w-8" style="color:#3b82f6"></i></div>
<h2 class="text-xl font-extrabold text-center mb-1">Testing Connections</h2>
<p class="text-center text-slate-500 mb-6">Verifying your credentials work with each service</p>
<div id="checks">
<div class="check-item" id="chk-hf"><i data-lucide="loader" class="h-4 w-4 animate-spin" style="color:#3b82f6"></i><span>Hugging Face...</span></div>
<div class="check-item" id="chk-kaggle"><i data-lucide="loader" class="h-4 w-4 animate-spin" style="color:#3b82f6"></i><span>Kaggle API...</span></div>
<div class="check-item" id="chk-or"><i data-lucide="loader" class="h-4 w-4 animate-spin" style="color:#3b82f6"></i><span>OpenRouter...</span></div>
</div>
<div class="mt-6 flex gap-4">
<button class="btn btn-ghost flex-1" onclick="goStep(1)"><i data-lucide="arrow-left" class="h-4 w-4"></i> Back</button>
<button class="btn btn-primary flex-1" id="btnNext2" disabled onclick="goStep(3)">Continue <i data-lucide="arrow-right" class="h-4 w-4"></i></button>
</div>
</div>
<!-- Step 3: Complete -->
<div class="step" id="step3">
<div class="icon"><i data-lucide="check-circle" class="h-8 w-8" style="color:#10b981"></i></div>
<h2 class="text-xl font-extrabold text-center mb-1">Setup Complete!</h2>
<p class="text-center text-slate-500 mb-6">Your credentials are saved and verified. Ready to fine-tune.</p>
<div class="space-y-3 mb-6">
<div class="check-item ok"><i data-lucide="check" class="h-4 w-4"></i><span>Credentials stored securely (server-side only)</span></div>
<div class="check-item ok"><i data-lucide="check" class="h-4 w-4"></i><span>Free Kaggle T4 GPU ready (30h/week)</span></div>
<div class="check-item ok"><i data-lucide="check" class="h-4 w-4"></i><span>HF model push enabled</span></div>
<div class="check-item ok"><i data-lucide="check" class="h-4 w-4"></i><span>AI Firewall & cost routing active</span></div>
</div>
<a href="/app/" class="btn btn-primary">
<i data-lucide="rocket" class="h-4 w-4"></i> Launch Fine-Tune Studio
</a>
<p class="text-center text-sm text-slate-500 mt-4">Credentials stored in <code class="masked">backend/.env</code> — never sent to browser</p>
</div>
</div>
<div id="toast"></div>
<script>
// Initialize Lucide icons
lucide.createIcons();
// State
let credentials = { hf: '', kaggleUser: '', kaggleKey: '', openrouter: '' };
// Navigation
function goStep(n) {
document.querySelectorAll('.step').forEach(s => s.classList.remove('active'));
document.querySelectorAll('.progress-step').forEach(s => s.classList.remove('active', 'done'));
document.getElementById('step' + n).classList.add('active');
for (let i = 1; i <= n; i++) {
document.getElementById('prog' + i).classList.add(i === n ? 'active' : 'done');
}
lucide.createIcons();
}
// Toast
function toast(msg, type = 'ok') {
const el = document.createElement('div');
el.className = 'toast ' + type;
el.textContent = msg;
document.getElementById('toast').appendChild(el);
setTimeout(() => { el.style.opacity = '0'; setTimeout(() => el.remove(), 300); }, 3000);
}
// Step 1: Save credentials
async function saveCredentials() {
const hf = document.getElementById('hfToken').value.trim();
const ku = document.getElementById('kaggleUser').value.trim();
const kk = document.getElementById('kaggleKey').value.trim();
const or = document.getElementById('openrouterKey').value.trim();
if (!hf) { toast('Hugging Face token is required', 'err'); return; }
if (!ku || !kk) { toast('Kaggle username and key are required for free GPU', 'err'); return; }
credentials = { hf, kaggleUser: ku, kaggleKey: kk, openrouter: or };
// Save to backend
try {
const res = await fetch('/api/setup/save-credentials', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
});
if (!res.ok) throw new Error('Failed to save');
} catch (e) {
toast('Save failed: ' + e.message, 'err');
return;
}
goStep(2);
await runChecks();
}
// Step 2: Run connection tests
async function runChecks() {
const checks = [
{ id: 'chk-hf', name: 'Hugging Face', url: '/api/setup/test/hf' },
{ id: 'chk-kaggle', name: 'Kaggle', url: '/api/setup/test/kaggle' },
{ id: 'chk-or', name: 'OpenRouter', url: '/api/setup/test/openrouter', optional: true }
];
let allPassed = true;
for (const c of checks) {
const el = document.getElementById(c.id);
try {
const res = await fetch(c.url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
});
const data = await res.json();
if (data.ok) {
el.className = 'check-item ok';
el.innerHTML = '<i data-lucide="check" class="h-4 w-4"></i><span>' + c.name + ' connected</span>';
} else {
throw new Error(data.error || 'Failed');
}
} catch (e) {
if (!c.optional) allPassed = false;
el.className = 'check-item';
el.style.borderColor = 'rgba(239,68,68,.4)';
el.style.background = 'rgba(239,68,68,.05)';
el.innerHTML = '<i data-lucide="x-circle" class="h-4 w-4" style="color:#ef4444"></i><span>' + c.name + ' failed: ' + e.message + '</span>';
}
lucide.createIcons();
}
document.getElementById('btnNext2').disabled = !allPassed;
if (allPassed) toast('All connections verified!', 'ok');
}
// Auto-check if already configured (stay on setup.html if ?edit=1 or same-origin referrer)
window.addEventListener('load', async () => {
try {
const params = new URLSearchParams(window.location.search);
const isEdit = params.get('edit') === '1';
let isSameOriginReferrer = false;
if (document.referrer) {
try {
isSameOriginReferrer = new URL(document.referrer).origin === window.location.origin;
} catch (_) {}
}
if (isEdit || isSameOriginReferrer) {
return;
}
const res = await fetch('/api/setup/status');
const data = await res.json();
if (data.configured) {
// Skip to app
window.location.href = '/app/';
}
} catch (e) {
// Show setup
}
});
</script>
</body>
</html>