feat: move resale dashboard into marketplace

This commit is contained in:
david
2026-08-28 10:28:39 +02:00
parent 53607738f8
commit 9463bfae85
14 changed files with 694 additions and 123 deletions

View File

@@ -15,7 +15,6 @@ import {
SurplusDashboardService,
type SurplusDashboardSnapshot,
} from "../surplus/dashboard.js";
import { renderSurplusDashboardPage } from "../surplus/dashboardPage.js";
export async function runSurplusSeller(
env: Record<string, string | undefined>,
@@ -81,27 +80,6 @@ async function handleRequest(
response.setHeader("x-content-type-options", "nosniff");
response.setHeader("x-frame-options", "DENY");
response.setHeader("referrer-policy", "no-referrer");
if (request.method === "GET" && path === "/") {
response.statusCode = 302;
response.setHeader("location", "/reseller");
response.end();
return;
}
if (
request.method === "GET" &&
(path === "/reseller" || path === "/reseller/")
) {
response.statusCode = 200;
response.setHeader("content-type", "text/html; charset=utf-8");
response.setHeader(
"content-security-policy",
"default-src 'self'; style-src 'self' 'unsafe-inline'; " +
"script-src 'self' 'unsafe-inline'; connect-src 'self'; " +
"img-src 'self' data:; frame-ancestors 'none'; base-uri 'none'",
);
response.end(renderSurplusDashboardPage());
return;
}
const status = controller.snapshot();
if (request.method === "GET" && path === "/healthz") {
const healthy = controller.healthy();
@@ -116,7 +94,7 @@ async function handleRequest(
response.end(JSON.stringify(status));
return;
}
if (request.method === "GET" && path === "/dashboard/api") {
if (request.method === "GET" && path === "/internal/resale-dashboard") {
try {
const snapshot = await dashboard.getSnapshot();
response.statusCode = 200;

View File

@@ -1,62 +0,0 @@
export function renderSurplusDashboardPage(): string {
return `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="dark">
<title>Inference Resale Control Plane</title>
<style>
:root{--bg:#07110f;--surface:#0c1916;--surface-2:#10231e;--line:#244039;--text:#eef8f3;--muted:#91aaa1;--green:#57e39b;--teal:#5fd8cd;--blue:#79b8ff;--amber:#f3c969;--red:#ff827c;--shadow:0 24px 80px #0007}
*{box-sizing:border-box}html{min-width:320px}body{margin:0;background:radial-gradient(circle at 8% -5%,#17493b 0,transparent 29rem),radial-gradient(circle at 95% 20%,#15344b 0,transparent 34rem),var(--bg);color:var(--text);font:14px/1.5 Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
body:before{content:"";position:fixed;inset:0;pointer-events:none;background-image:linear-gradient(#fff008 1px,transparent 1px),linear-gradient(90deg,#fff008 1px,transparent 1px);background-size:42px 42px;mask-image:linear-gradient(to bottom,#0008,transparent 55%)}
a{color:var(--blue);text-decoration:none}a:hover,a:focus-visible{text-decoration:underline}.shell{width:min(1240px,calc(100% - 32px));margin:auto;padding:38px 0 72px}.top{display:flex;justify-content:space-between;align-items:flex-start;gap:32px;margin-bottom:26px}.eyebrow{color:var(--green);font:700 11px/1.2 ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.16em;text-transform:uppercase}.top h1{margin:10px 0 8px;font-size:clamp(32px,5.5vw,58px);line-height:.98;letter-spacing:-.055em;max-width:780px}.top p{color:var(--muted);margin:0;max-width:660px;font-size:15px}.live-cluster{text-align:right;min-width:210px}.updated{color:var(--muted);font-variant-numeric:tabular-nums;margin-top:8px}.badge{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--line);border-radius:999px;padding:7px 11px;background:#081511cc;font-size:12px;font-weight:700}.badge:before{content:"";width:7px;height:7px;border-radius:50%;background:var(--muted);box-shadow:0 0 0 3px #fff1}.badge.live:before{background:var(--green);box-shadow:0 0 18px var(--green)}.badge.retired:before{background:var(--amber)}
.panel{border:1px solid var(--line);border-radius:18px;background:linear-gradient(145deg,#10231ee8,#0a1714f2);box-shadow:var(--shadow)}.flow{display:grid;grid-template-columns:1fr 42px 1.1fr 42px 1.15fr;align-items:stretch;padding:14px;margin-bottom:18px;overflow:hidden}.node{padding:18px;border-radius:13px;background:#091612;border:1px solid #24443a;min-width:0}.node strong{display:block;font-size:18px;margin:5px 0 3px}.node small{color:var(--muted);display:block}.node .sub{font:12px ui-monospace,SFMono-Regular,Menlo,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.arrow{display:grid;place-items:center;color:var(--teal);font-size:22px}.markets{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:18px}.market-card{padding:22px;position:relative;overflow:hidden}.market-card:after{content:"";position:absolute;width:180px;height:180px;border-radius:50%;right:-90px;top:-110px;background:#57e39b0b}.market-head{display:flex;justify-content:space-between;align-items:flex-start;gap:16px}.market-head h2{margin:3px 0 0;font-size:22px}.market-head p{margin:5px 0 0;color:var(--muted)}.mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.truncate{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.metrics{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin:16px 0 0}.metric{padding:14px;border:1px solid #254039;border-radius:12px;background:#07130f9c;min-width:0}.label{display:block;color:var(--muted);text-transform:uppercase;letter-spacing:.11em;font-size:10px;font-weight:700}.metric strong{display:block;font-size:clamp(18px,2.2vw,27px);letter-spacing:-.035em;margin-top:5px;font-variant-numeric:tabular-nums}.metric small{color:var(--muted);display:block;margin-top:2px}.good{color:var(--green)}.warn{color:var(--amber)}.retired-copy{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:16px}.fact{padding:12px 14px;border-left:2px solid var(--amber);background:#f3c9690a}.fact strong{display:block;margin-top:3px}
.section-head{display:flex;justify-content:space-between;align-items:end;gap:20px;margin:28px 2px 12px}.section-head h2{margin:0;font-size:22px;letter-spacing:-.025em}.section-head p{margin:0;color:var(--muted)}.table-wrap{overflow:auto}.sales{width:100%;border-collapse:collapse;min-width:820px}.sales th,.sales td{text-align:left;padding:12px 15px;border-bottom:1px solid #213a33}.sales th{position:sticky;top:0;background:#0d1b17;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.12em}.sales tbody tr:hover{background:#ffffff05}.sales tbody tr:last-child td{border-bottom:0}.status-pill{display:inline-flex;border-radius:999px;padding:3px 8px;background:#ffffff0b;border:1px solid #ffffff13;font-size:11px}.status-pill.confirmed{color:var(--green);border-color:#57e39b55}.status-pill.accrued{color:var(--amber);border-color:#f3c96955}.empty{padding:34px!important;text-align:center!important;color:var(--muted)}.alert{display:none;margin-top:14px;padding:12px 14px;border-radius:11px;background:#ff827c10;border:1px solid #ff827c44;color:#ffc4bf}.alert.visible{display:block}.foot{display:flex;justify-content:space-between;gap:24px;color:var(--muted);margin-top:24px;font-size:12px}.foot span:last-child{text-align:right}
@media(max-width:980px){.metrics{grid-template-columns:repeat(2,1fr)}.markets{grid-template-columns:1fr}.flow{grid-template-columns:1fr}.arrow{transform:rotate(90deg);height:34px}.top{flex-direction:column}.live-cluster{text-align:left}.retired-copy{grid-template-columns:1fr 1fr}}
@media(max-width:560px){.shell{width:min(100% - 20px,1240px);padding-top:24px}.metrics,.retired-copy{grid-template-columns:1fr}.market-card{padding:17px}.top h1{font-size:38px}.foot{flex-direction:column}.foot span:last-child{text-align:left}}
</style>
</head>
<body>
<main class="shell">
<header class="top"><div><div class="eyebrow">Autonomous inference resale</div><h1>One capacity pool.<br>Two settlement markets.</h1><p>Live OpenRouter capacity sold through Surplus on Base, with the completed Vector AP3X proof preserved as the migration record.</p></div><div class="live-cluster"><span class="badge" id="controller-badge">Loading controller</span><div class="updated" id="updated">Waiting for current state</div></div></header>
<section class="panel flow" aria-label="Capacity routing flow">
<div class="node"><span class="label">Upstream capacity</span><strong>OpenRouter</strong><small id="flow-allowance">Allowance loading</small><small class="sub" id="flow-provider-model">provider model --</small></div><div class="arrow" aria-hidden="true">→</div>
<div class="node"><span class="label">Autonomous manager</span><strong>Seller agent</strong><small>Discovers · prices · caps · reconciles</small><small class="sub" id="flow-wallet">seller --</small></div><div class="arrow" aria-hidden="true">→</div>
<div class="node"><span class="label">Current settlement</span><strong>Surplus / Base USDC</strong><small id="flow-surplus">Offer loading</small><small class="sub" id="flow-offer">offer --</small></div>
</section>
<section class="markets">
<article class="panel market-card"><div class="market-head"><div><span class="label">Current market</span><h2>Surplus Intelligence</h2><p>Competitive model routing with USDC settlement on Base.</p></div><span class="badge" id="surplus-badge">Loading</span></div>
<div class="metrics"><div class="metric"><span class="label">Lifetime revenue</span><strong id="earned">--</strong><small id="earnings-state">--</small></div><div class="metric"><span class="label">Requests sold</span><strong id="requests">--</strong><small id="tokens">-- tokens</small></div><div class="metric"><span class="label">Market position</span><strong id="rank">--</strong><small id="market-state">--</small></div><div class="metric"><span class="label">Daily seller cap</span><strong id="daily-cap">--</strong><small id="cap-remaining">-- remaining</small></div></div>
<div class="alert" id="payout-hold"></div><div class="alert" id="controller-error"></div></article>
<article class="panel market-card"><div class="market-head"><div><span class="label">Migration record</span><h2>Vector marketplace</h2><p>AP3X escrow route retired after the cross-market proof.</p></div><span class="badge retired">Retired</span></div>
<div class="metrics"><div class="metric"><span class="label">AP3X earned</span><strong id="vector-earned">--</strong><small>historical</small></div><div class="metric"><span class="label">Settled jobs</span><strong id="vector-jobs">--</strong><small>Vector escrows</small></div><div class="metric"><span class="label">Upstream spend</span><strong id="vector-spend">--</strong><small>proof route</small></div><div class="metric"><span class="label">Retired on</span><strong id="vector-date">--</strong><small>advert closed</small></div></div>
<div class="retired-copy"><div class="fact"><span class="label">Retired advert</span><strong class="mono truncate" id="vector-advert">--</strong><a id="advert-link" target="_blank" rel="noopener noreferrer">Open creation transaction ↗</a></div><div class="fact"><span class="label">Retirement proof</span><strong class="mono truncate" id="vector-retirement">--</strong><a id="retirement-link" target="_blank" rel="noopener noreferrer">Open confirmed transaction ↗</a></div></div></article>
</section>
<section class="panel market-card"><div class="market-head"><div><span class="label">Economics</span><h2>Capacity and price controls</h2><p>The seller cap is discounted revenue. The agent converts it to estimated upstream exposure before it opens the route.</p></div><span class="badge live">Bounded</span></div>
<div class="metrics"><div class="metric"><span class="label">OpenRouter remaining</span><strong id="provider-remaining">--</strong><small id="provider-used">-- used</small></div><div class="metric"><span class="label">Protected reserve</span><strong id="provider-reserve">--</strong><small>fail-closed threshold</small></div><div class="metric"><span class="label">Daily upstream estimate</span><strong id="provider-exposure">--</strong><small>from recovery floor</small></div><div class="metric"><span class="label">Current multiplier</span><strong id="multiplier">--</strong><small id="prices">--</small></div></div></section>
<div class="section-head"><div><span class="label">Surplus activity</span><h2>Recent sales</h2></div><p id="sales-note">Loading settlement records</p></div>
<section class="panel table-wrap"><table class="sales"><thead><tr><th>Time</th><th>Status</th><th>Model</th><th>Input</th><th>Output</th><th>Cache read</th><th>Revenue</th><th>Base settlement</th></tr></thead><tbody id="sales-body"><tr><td colspan="8" class="empty">Loading sales</td></tr></tbody></table></section>
<footer class="foot"><span id="identity">Seller identity loading</span><span>Vector proves the agent-owned route. Surplus is the current USDC market.</span></footer>
</main>
<script>
(function(){'use strict';
var $=function(id){return document.getElementById(id)};
var set=function(id,value){$(id).textContent=value==null?'--':String(value)};
var usd=function(value){if(value==null||value==='')return'--';var n=Number(value);return Number.isFinite(n)?'$'+n.toFixed(n<.01?6:4):'--'};
var count=function(value){return new Intl.NumberFormat().format(Number(value)||0)};
var short=function(value){var s=String(value||'');return s.length>18?s.slice(0,10)+'…'+s.slice(-7):s||'--'};
var when=function(value){if(!value)return'--';var d=new Date(value);return Number.isNaN(d.getTime())?'--':d.toLocaleString()};
function statusBadge(id,label,live){var el=$(id);el.textContent=label;el.className='badge '+(live?'live':'');}
function link(id,href){var el=$(id);el.href=href;}
function txLink(row,hash){var td=document.createElement('td');if(typeof hash==='string'&&/^0x[0-9a-fA-F]{64}$/.test(hash)){var a=document.createElement('a');a.href='https://basescan.org/tx/'+hash;a.target='_blank';a.rel='noopener noreferrer';a.textContent=short(hash);td.appendChild(a)}else{td.textContent='Accruing'}row.appendChild(td)}
function cell(row,value,className){var td=document.createElement('td');if(className)td.className=className;td.textContent=value==null?'--':String(value);row.appendChild(td)}
function renderSales(sales){var body=$('sales-body');body.replaceChildren();if(!sales.length){var empty=document.createElement('tr');cell(empty,'No sales returned','empty');empty.firstChild.colSpan=8;body.appendChild(empty);return}sales.forEach(function(sale){var row=document.createElement('tr');cell(row,when(sale.createdAt));var status=document.createElement('td');var pill=document.createElement('span');pill.className='status-pill '+sale.settlementStatus;pill.textContent=sale.settlementStatus;status.appendChild(pill);row.appendChild(status);cell(row,sale.model,'mono');cell(row,count(sale.inputTokens));cell(row,count(sale.outputTokens));cell(row,count(sale.cacheReadTokens));cell(row,usd(sale.revenueUsd));txLink(row,sale.transactionHash);body.appendChild(row)})}
function render(data){var c=data.controller,o=data.surplus.offer,e=data.surplus.earnings,p=data.provider,v=data.vector;statusBadge('controller-badge',c.phase,c.ok);statusBadge('surplus-badge',o.status,o.available&&o.healthy);set('updated','Updated '+when(data.generatedAt));set('flow-allowance',usd(p.remainingUsd)+' of '+usd(p.hardLimitUsd)+' remaining');set('flow-provider-model',o.providerModel);set('flow-wallet',short(data.identity.sellerWallet));set('flow-surplus',(o.available?'Available':'Unavailable')+' · '+(o.healthy?'healthy':'not healthy'));set('flow-offer',o.id);set('earned',usd(e.totalUsd));set('earnings-state',usd(e.pendingUsd)+' pending · '+usd(e.paidUsd)+' paid');set('requests',count(e.requests));set('tokens',count(e.tokens)+' tokens');set('rank',o.rank==null?'--':'#'+o.rank);set('market-state',(o.trusted?'trusted':'untrusted')+' · '+count(o.trades24h)+' trades / 24h');set('daily-cap',usd(o.dailyCapUsd));set('cap-remaining',usd(o.capRemainingUsd)+' remaining');set('provider-remaining',usd(p.remainingUsd));set('provider-used',usd(p.usedUsd)+' used of '+usd(p.hardLimitUsd));set('provider-reserve',usd(p.reserveUsd));set('provider-exposure',usd(p.estimatedDailyExposureUsd));set('multiplier',o.costMultiplier==null?'--':(o.costMultiplier*100).toFixed(3)+'%');set('prices',usd(o.inputUsdPer1m)+' in · '+usd(o.outputUsdPer1m)+' out / 1M');set('vector-earned',v.historicalAp3xEarned+' AP3X');set('vector-jobs',count(v.historicalSettledJobs));set('vector-spend',usd(v.historicalUpstreamSpendUsd));set('vector-date',v.retiredOn);set('vector-advert',v.advertRef);set('vector-retirement',v.retirementTransaction);var advertHash=String(v.advertRef).split('#')[0];link('advert-link','https://vector.apexscan.org/en/transaction/'+advertHash+'/summary/');link('retirement-link','https://vector.apexscan.org/en/transaction/'+v.retirementTransaction+'/summary/');set('identity','Surplus '+short(data.identity.sellerWallet)+' · payout '+short(data.identity.payoutAddress));set('sales-note',count(e.requests)+' lifetime requests · '+count(e.tokens)+' tokens');var hold=$('payout-hold');if(e.payoutHoldReason){hold.className='alert visible';hold.textContent='Payout hold: '+e.payoutHoldReason+(e.payoutHoldReleasesAt?' · releases '+when(e.payoutHoldReleasesAt):'')}else{hold.className='alert'}var error=$('controller-error');if(c.lastError){error.className='alert visible';error.textContent='Controller: '+c.lastError}else{error.className='alert'}renderSales(data.surplus.recentSales)}
async function refresh(){try{var response=await fetch('/dashboard/api',{cache:'no-store'});if(!response.ok)throw new Error('Dashboard API returned '+response.status);render(await response.json())}catch(error){statusBadge('controller-badge','data unavailable',false);set('updated',error instanceof Error?error.message:'Dashboard data unavailable')}}
refresh();setInterval(refresh,15000);
})();
</script>
</body>
</html>`;
}