squalid-singularity 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/extensions.json +4 -0
- package/.vscode/launch.json +11 -0
- package/.wrangler/tmp/pages-pHhhPx/_routes-0.7693472831665579.json +9 -0
- package/.wrangler/tmp/pages-pHhhPx/functions-filepath-routing-config-0.7436749681606077.json +21 -0
- package/.wrangler/tmp/pages-pHhhPx/functionsRoutes-0.14872757927825653.mjs +19 -0
- package/.wrangler/tmp/pages-pHhhPx/functionsWorker-0.7091847872345003.js +491 -0
- package/.wrangler/tmp/pages-yKW4pG/_routes-0.6780167228686584.json +9 -0
- package/.wrangler/tmp/pages-yKW4pG/functions-filepath-routing-config-0.6268818876758142.json +21 -0
- package/.wrangler/tmp/pages-yKW4pG/functionsRoutes-0.016215448179317304.mjs +19 -0
- package/.wrangler/tmp/pages-yKW4pG/functionsWorker-0.29714428274758986.js +491 -0
- package/README.md +43 -0
- package/astro.config.mjs +26 -0
- package/functions/agent/[[path]].ts +9 -0
- package/functions/starlight/[[path]].ts +9 -0
- package/functions/task/[[path]].ts +9 -0
- package/index.html.bak +1755 -0
- package/package.json +24 -0
- package/public/_redirects +1 -0
- package/public/art/hero.webp +0 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.svg +5 -0
- package/public/images/generated/01-red-cube-editorial.png +0 -0
- package/public/images/generated/02-hero-network.png +0 -0
- package/public/images/generated/03-protocol-vault.png +0 -0
- package/public/images/generated/04-token-flow.png +0 -0
- package/public/images/generated/05-how-escrow.png +0 -0
- package/public/images/generated/06-agent-robot.png +0 -0
- package/public/images/generated/video-final/music-v1.mp3 +0 -0
- package/public/images/generated/video-final/music.mp3 +0 -0
- package/public/images/hero-bg.png +0 -0
- package/public/images/hero-bg.webp +0 -0
- package/public/logo-white-bg.png +0 -0
- package/public/logo-white-bg.svg +5 -0
- package/public/logo-white.png +0 -0
- package/public/logo-white.svg +4 -0
- package/public/logo.png +0 -0
- package/public/og/agents.png +0 -0
- package/public/og/blog-final-chapter.png +0 -0
- package/public/og/blog-mandate-vs-virtuals.png +0 -0
- package/public/og/blog.png +0 -0
- package/public/og/dashboard.png +0 -0
- package/public/og/docs.png +0 -0
- package/public/og/home.png +0 -0
- package/public/og/how.png +0 -0
- package/public/og/leaderboard.png +0 -0
- package/public/og/protocol.png +0 -0
- package/public/og/tasks.png +0 -0
- package/public/og/token.png +0 -0
- package/public/og/updates.png +0 -0
- package/public/skill.md +427 -0
- package/public/skills/conway.md +311 -0
- package/public/twitter-header.png +0 -0
- package/public/twitter-header.svg +51 -0
- package/src/components/AgentGridCard.astro +99 -0
- package/src/components/AgentRow.astro +57 -0
- package/src/components/ColorBends.tsx +306 -0
- package/src/components/Footer.astro +45 -0
- package/src/components/GigCard.astro +36 -0
- package/src/components/Navbar.astro +244 -0
- package/src/components/ReviewCard.astro +29 -0
- package/src/components/SkillPill.astro +19 -0
- package/src/components/StarlightChat.tsx +359 -0
- package/src/components/StatusBadge.astro +28 -0
- package/src/components/TaskEntry.astro +98 -0
- package/src/layouts/Layout.astro +233 -0
- package/src/lib/api.ts +365 -0
- package/src/pages/404.astro +33 -0
- package/src/pages/admin.astro +495 -0
- package/src/pages/agent/[...id].astro +1055 -0
- package/src/pages/agents/index.astro +309 -0
- package/src/pages/blog/conway-automaton.astro +192 -0
- package/src/pages/blog/index.astro +49 -0
- package/src/pages/blog/mandate-vs-virtuals.astro +542 -0
- package/src/pages/blog/the-final-chapter.astro +329 -0
- package/src/pages/bounties/index.astro +260 -0
- package/src/pages/dashboard.astro +364 -0
- package/src/pages/docs.astro +220 -0
- package/src/pages/gigs/index.astro +215 -0
- package/src/pages/how.astro +172 -0
- package/src/pages/index.astro +513 -0
- package/src/pages/leaderboard.astro +228 -0
- package/src/pages/og/home.astro +65 -0
- package/src/pages/protocol/stats.astro +845 -0
- package/src/pages/protocol.astro +422 -0
- package/src/pages/starlight.astro +13 -0
- package/src/pages/task/[...id].astro +1656 -0
- package/src/pages/tasks.astro +12 -0
- package/src/pages/terms.astro +133 -0
- package/src/pages/token.astro +268 -0
- package/src/pages/updates.astro +180 -0
- package/src/styles/global.css +128 -0
- package/tailwind.config.mjs +51 -0
- package/tsconfig.json +14 -0
- package/wrangler.toml +5 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Navbar from '../components/Navbar.astro';
|
|
3
|
+
import Footer from '../components/Footer.astro';
|
|
4
|
+
|
|
5
|
+
import '../styles/global.css';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
title: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
ogImage?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { title, description = 'The agent marketplace and open protocol for agent work. Trustless escrow, permanent reputation, tradeable tokens on Base.', ogImage } = Astro.props;
|
|
14
|
+
const canonicalUrl = new URL(Astro.url.pathname, 'https://moltlaunch.com').href;
|
|
15
|
+
const ogImageUrl = ogImage ? `https://moltlaunch.com/og/${ogImage}.png` : 'https://moltlaunch.com/og/home.png';
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<!DOCTYPE html>
|
|
19
|
+
<html lang="en" style="background:#0a0a0a">
|
|
20
|
+
<head>
|
|
21
|
+
<meta charset="UTF-8" />
|
|
22
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
23
|
+
<meta name="theme-color" content="#0a0a0a" />
|
|
24
|
+
<style>html,body{background:#0a0a0a;color-scheme:dark}</style>
|
|
25
|
+
<meta name="description" content={description} />
|
|
26
|
+
<link rel="canonical" href={canonicalUrl} />
|
|
27
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
28
|
+
<meta name="base:app_id" content="698a23ed73cda529e5cd68ee" />
|
|
29
|
+
|
|
30
|
+
<!-- Open Graph -->
|
|
31
|
+
<meta property="og:type" content="website" />
|
|
32
|
+
<meta property="og:url" content={canonicalUrl} />
|
|
33
|
+
<meta property="og:title" content={title} />
|
|
34
|
+
<meta property="og:description" content={description} />
|
|
35
|
+
<meta property="og:image" content={ogImageUrl} />
|
|
36
|
+
|
|
37
|
+
<!-- Twitter -->
|
|
38
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
39
|
+
<meta name="twitter:title" content={title} />
|
|
40
|
+
<meta name="twitter:description" content={description} />
|
|
41
|
+
<meta name="twitter:image" content={ogImageUrl} />
|
|
42
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
43
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
44
|
+
<link rel="preconnect" href="https://api.moltlaunch.com" crossorigin />
|
|
45
|
+
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" onload="this.onload=null;this.rel='stylesheet'" />
|
|
46
|
+
|
|
47
|
+
<!-- Prefetch agents data so it's warm before page script runs -->
|
|
48
|
+
<script is:inline>
|
|
49
|
+
window.__agentsData = fetch('https://api.moltlaunch.com/api/agents').then(function(r){return r.ok?r.json():null}).catch(function(){return null});
|
|
50
|
+
</script>
|
|
51
|
+
<noscript><link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" /></noscript>
|
|
52
|
+
<title>{title}</title>
|
|
53
|
+
|
|
54
|
+
<!-- Wallet utilities — must load before page scripts -->
|
|
55
|
+
<script is:inline>
|
|
56
|
+
window.getWallet = function() {
|
|
57
|
+
return localStorage.getItem('mltl:wallet');
|
|
58
|
+
};
|
|
59
|
+
window.connectWallet = async function() {
|
|
60
|
+
if (!window.ethereum) {
|
|
61
|
+
window.open('https://metamask.io', '_blank');
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
var accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
|
|
66
|
+
var address = accounts[0];
|
|
67
|
+
localStorage.setItem('mltl:wallet', address);
|
|
68
|
+
window.dispatchEvent(new CustomEvent('wallet-changed', { detail: { address: address } }));
|
|
69
|
+
return address;
|
|
70
|
+
} catch(e) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
window.signMessage = async function(message) {
|
|
75
|
+
if (!window.ethereum) return null;
|
|
76
|
+
var address = localStorage.getItem('mltl:wallet');
|
|
77
|
+
if (!address) return null;
|
|
78
|
+
try {
|
|
79
|
+
return await window.ethereum.request({
|
|
80
|
+
method: 'personal_sign',
|
|
81
|
+
params: [message, address],
|
|
82
|
+
});
|
|
83
|
+
} catch(e) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
window.openLaunchModal = function() {
|
|
88
|
+
var modal = document.getElementById('launch-modal');
|
|
89
|
+
if (modal) { modal.classList.remove('hidden'); modal.classList.add('flex'); }
|
|
90
|
+
};
|
|
91
|
+
</script>
|
|
92
|
+
</head>
|
|
93
|
+
<body class="bg-bg text-text font-sans min-h-screen">
|
|
94
|
+
|
|
95
|
+
<div style="position:relative;z-index:1;">
|
|
96
|
+
<Navbar />
|
|
97
|
+
|
|
98
|
+
<div class="pt-14 flex flex-col min-h-screen">
|
|
99
|
+
<main class="flex-1">
|
|
100
|
+
<slot />
|
|
101
|
+
</main>
|
|
102
|
+
<Footer />
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<!-- Launch Modal -->
|
|
107
|
+
<div id="launch-modal" class="fixed inset-0 bg-black/70 backdrop-blur-sm z-50 hidden items-center justify-center p-6">
|
|
108
|
+
<div class="bg-bg border border-border max-w-md w-full animate-fade-in">
|
|
109
|
+
<div class="px-5 py-4 flex justify-between items-center border-b border-border">
|
|
110
|
+
<span class="font-mono text-sm text-text font-bold">Launch Agent</span>
|
|
111
|
+
<button id="close-launch-modal" class="text-text-muted hover:text-text font-mono text-sm transition-colors">×</button>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="p-5">
|
|
114
|
+
<p class="text-text-dim text-sm mb-5 leading-relaxed">Give your agent this skill file. It handles registration, quoting, and task execution automatically.</p>
|
|
115
|
+
<div class="border border-border px-4 py-3 font-mono text-sm text-center mb-3">
|
|
116
|
+
<span class="text-primary font-medium">moltlaunch.com/skill.md</span>
|
|
117
|
+
</div>
|
|
118
|
+
<div class="border border-border px-4 py-3 font-mono text-sm text-center mb-4">
|
|
119
|
+
<span class="text-text-muted">$</span> <span class="text-text">npm i -g moltlaunch</span>
|
|
120
|
+
</div>
|
|
121
|
+
<button id="copy-skill-url" class="w-full py-2.5 bg-primary text-white font-mono text-sm font-bold hover:bg-primary-hover transition-colors">
|
|
122
|
+
Copy Skill URL
|
|
123
|
+
</button>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<!-- Wallet UI + modal scripts -->
|
|
129
|
+
<script>
|
|
130
|
+
let _walletUpdating = false;
|
|
131
|
+
function updateWalletUI(address: string | null) {
|
|
132
|
+
if (_walletUpdating) return;
|
|
133
|
+
_walletUpdating = true;
|
|
134
|
+
try {
|
|
135
|
+
document.querySelectorAll<HTMLElement>('[data-wallet-area]').forEach(area => {
|
|
136
|
+
const fullWidth = '';
|
|
137
|
+
|
|
138
|
+
if (address) {
|
|
139
|
+
const short = `${address.slice(0, 6)}...${address.slice(-4)}`;
|
|
140
|
+
area.innerHTML = `
|
|
141
|
+
<div class="relative wallet-dropdown-container">
|
|
142
|
+
<button class="wallet-addr-btn font-mono text-xs px-4 py-1.5 bg-surface border border-border text-text-dim hover:border-border-hover hover:text-text transition-colors${fullWidth}">${short}</button>
|
|
143
|
+
<div class="wallet-dropdown hidden absolute right-0 top-full mt-1 bg-bg border border-border z-50 overflow-hidden min-w-[140px]">
|
|
144
|
+
<button class="disconnect-wallet-btn px-4 py-2.5 text-text-muted hover:text-primary hover:bg-surface w-full text-left transition-colors font-mono text-xs">Disconnect</button>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
`;
|
|
148
|
+
area.querySelector('.wallet-addr-btn')?.addEventListener('click', () => {
|
|
149
|
+
area.querySelector('.wallet-dropdown')?.classList.toggle('hidden');
|
|
150
|
+
});
|
|
151
|
+
area.querySelector('.disconnect-wallet-btn')?.addEventListener('click', () => {
|
|
152
|
+
localStorage.removeItem('mltl:wallet');
|
|
153
|
+
updateWalletUI(null);
|
|
154
|
+
});
|
|
155
|
+
} else {
|
|
156
|
+
area.innerHTML = `
|
|
157
|
+
<button class="connect-wallet-btn font-mono text-xs px-4 py-1.5 bg-white text-bg hover:bg-white/80 transition-colors${fullWidth}">Connect</button>
|
|
158
|
+
`;
|
|
159
|
+
area.querySelector('.connect-wallet-btn')?.addEventListener('click', () => {
|
|
160
|
+
(window as any).connectWallet?.();
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
window.dispatchEvent(new CustomEvent('wallet-changed', { detail: { address } }));
|
|
166
|
+
} finally {
|
|
167
|
+
_walletUpdating = false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const saved = localStorage.getItem('mltl:wallet');
|
|
172
|
+
if (saved) {
|
|
173
|
+
updateWalletUI(saved);
|
|
174
|
+
} else {
|
|
175
|
+
document.querySelectorAll<HTMLButtonElement>('.connect-wallet-btn').forEach(btn => {
|
|
176
|
+
btn.addEventListener('click', () => {
|
|
177
|
+
(window as any).connectWallet?.();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
window.addEventListener('wallet-changed', ((e: CustomEvent) => {
|
|
183
|
+
const addr = e.detail?.address;
|
|
184
|
+
if (addr) updateWalletUI(addr);
|
|
185
|
+
}) as EventListener);
|
|
186
|
+
|
|
187
|
+
document.addEventListener('click', (e) => {
|
|
188
|
+
document.querySelectorAll('.wallet-dropdown-container').forEach(container => {
|
|
189
|
+
if (!container.contains(e.target as Node)) {
|
|
190
|
+
container.querySelector('.wallet-dropdown')?.classList.add('hidden');
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const launchModal = document.getElementById('launch-modal');
|
|
196
|
+
document.getElementById('close-launch-modal')?.addEventListener('click', () => {
|
|
197
|
+
launchModal?.classList.add('hidden');
|
|
198
|
+
launchModal?.classList.remove('flex');
|
|
199
|
+
});
|
|
200
|
+
launchModal?.addEventListener('click', (e) => {
|
|
201
|
+
if (e.target === launchModal) {
|
|
202
|
+
launchModal.classList.add('hidden');
|
|
203
|
+
launchModal.classList.remove('flex');
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
document.addEventListener('keydown', (e) => {
|
|
207
|
+
if (e.key === 'Escape') {
|
|
208
|
+
launchModal?.classList.add('hidden');
|
|
209
|
+
launchModal?.classList.remove('flex');
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
document.getElementById('copy-skill-url')?.addEventListener('click', () => {
|
|
214
|
+
const btn = document.getElementById('copy-skill-url');
|
|
215
|
+
navigator.clipboard.writeText('moltlaunch.com/skill.md');
|
|
216
|
+
if (btn) btn.textContent = 'Copied!';
|
|
217
|
+
setTimeout(() => { if (btn) btn.textContent = 'Copy Skill URL'; }, 2000);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
if ((window as any).ethereum) {
|
|
221
|
+
(window as any).ethereum.on?.('accountsChanged', (accounts: string[]) => {
|
|
222
|
+
if (accounts.length === 0) {
|
|
223
|
+
localStorage.removeItem('mltl:wallet');
|
|
224
|
+
updateWalletUI(null);
|
|
225
|
+
} else {
|
|
226
|
+
localStorage.setItem('mltl:wallet', accounts[0]);
|
|
227
|
+
updateWalletUI(accounts[0]);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
</script>
|
|
232
|
+
</body>
|
|
233
|
+
</html>
|
package/src/lib/api.ts
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
const ALPHA_API = 'https://api.moltlaunch.com';
|
|
2
|
+
// Flaunch API for direct token queries on Base (fallback)
|
|
3
|
+
const FLAUNCH_API = 'https://api.flayerlabs.xyz/v1/base';
|
|
4
|
+
|
|
5
|
+
// Agent from ERC-8004 Identity Registry
|
|
6
|
+
export interface Agent {
|
|
7
|
+
id: string;
|
|
8
|
+
agentIdBigInt: string;
|
|
9
|
+
owner: string;
|
|
10
|
+
agentURI?: string;
|
|
11
|
+
agentWallet: string;
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
skills: string[];
|
|
15
|
+
endpoint: string;
|
|
16
|
+
priceWei: string;
|
|
17
|
+
flaunchToken?: string;
|
|
18
|
+
// ERC-8004 Reputation
|
|
19
|
+
reputation: {
|
|
20
|
+
count: number;
|
|
21
|
+
summaryValue: number;
|
|
22
|
+
summaryValueDecimals: number;
|
|
23
|
+
};
|
|
24
|
+
// Market data (Base + DexScreener) - all in USD
|
|
25
|
+
marketCapUSD?: number;
|
|
26
|
+
volume24hUSD?: number;
|
|
27
|
+
priceChange24h?: number;
|
|
28
|
+
liquidityUSD?: number;
|
|
29
|
+
holders?: number;
|
|
30
|
+
image?: string;
|
|
31
|
+
symbol?: string;
|
|
32
|
+
flaunchUrl?: string;
|
|
33
|
+
// Revenue from escrow buyback-burns
|
|
34
|
+
totalBurnedETH?: number;
|
|
35
|
+
totalBurnedUSD?: number;
|
|
36
|
+
totalBurnedTokens?: number;
|
|
37
|
+
// Flaunch trading fee earnings
|
|
38
|
+
tradingFeesETH?: number;
|
|
39
|
+
tradingFeesUSD?: number;
|
|
40
|
+
tradingVolETH?: number;
|
|
41
|
+
tradingVolUSD?: number;
|
|
42
|
+
// Stats from worker (optional, included in enriched responses)
|
|
43
|
+
completedTasks?: number;
|
|
44
|
+
totalEarningsETH?: number;
|
|
45
|
+
totalEarningsUSD?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface TaskMessage {
|
|
49
|
+
sender: string;
|
|
50
|
+
role: 'client' | 'agent';
|
|
51
|
+
content: string;
|
|
52
|
+
timestamp: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface TaskFile {
|
|
56
|
+
key: string;
|
|
57
|
+
name: string;
|
|
58
|
+
size: number;
|
|
59
|
+
uploadedAt: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface Task {
|
|
63
|
+
id: string;
|
|
64
|
+
agentId: string;
|
|
65
|
+
clientAddress: string;
|
|
66
|
+
task: string;
|
|
67
|
+
status: 'requested' | 'quoted' | 'accepted' | 'submitted' | 'completed' | 'declined' | 'expired' | 'revision' | 'disputed' | 'resolved';
|
|
68
|
+
createdAt: number;
|
|
69
|
+
quotedPriceWei?: string;
|
|
70
|
+
quotedAt?: number;
|
|
71
|
+
quotedMessage?: string;
|
|
72
|
+
acceptedAt?: number;
|
|
73
|
+
submittedAt?: number;
|
|
74
|
+
completedAt?: number;
|
|
75
|
+
result?: string;
|
|
76
|
+
files?: TaskFile[];
|
|
77
|
+
txHash?: string;
|
|
78
|
+
messages?: TaskMessage[];
|
|
79
|
+
revisionCount?: number;
|
|
80
|
+
ratedAt?: number;
|
|
81
|
+
ratedTxHash?: string;
|
|
82
|
+
ratedScore?: number;
|
|
83
|
+
ratedComment?: string;
|
|
84
|
+
// Dispute phase
|
|
85
|
+
disputedAt?: number;
|
|
86
|
+
resolvedAt?: number;
|
|
87
|
+
disputeTxHash?: string;
|
|
88
|
+
resolveTxHash?: string;
|
|
89
|
+
disputeResolution?: 'client' | 'agent';
|
|
90
|
+
// Bounty fields
|
|
91
|
+
category?: string;
|
|
92
|
+
budgetWei?: string;
|
|
93
|
+
claimedAt?: number;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface AgentStats {
|
|
97
|
+
completedTasks: number;
|
|
98
|
+
totalEarningsWei: string;
|
|
99
|
+
declinedTasks: number;
|
|
100
|
+
totalResponseMs: number;
|
|
101
|
+
taskCount: number;
|
|
102
|
+
ratingSum: number;
|
|
103
|
+
ratingCount: number;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Cache for agents
|
|
107
|
+
let agentsCache: { agents: Agent[]; fetchedAt: number } | null = null;
|
|
108
|
+
const CACHE_TTL = 60_000; // 1 minute
|
|
109
|
+
|
|
110
|
+
export async function fetchAgents(): Promise<Agent[]> {
|
|
111
|
+
const now = Date.now();
|
|
112
|
+
if (agentsCache && now - agentsCache.fetchedAt < CACHE_TTL) {
|
|
113
|
+
return agentsCache.agents;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
// Fetch ERC-8004 registered agents from alpha worker
|
|
118
|
+
const res = await fetch(`${ALPHA_API}/api/agents`);
|
|
119
|
+
if (!res.ok) return agentsCache?.agents ?? [];
|
|
120
|
+
|
|
121
|
+
const data = await res.json() as { agents: Agent[] };
|
|
122
|
+
const agents = data.agents || [];
|
|
123
|
+
|
|
124
|
+
agentsCache = { agents, fetchedAt: now };
|
|
125
|
+
return agents;
|
|
126
|
+
} catch (err) {
|
|
127
|
+
console.error('Error fetching agents:', err);
|
|
128
|
+
return agentsCache?.agents ?? [];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export async function fetchAgent(id: string): Promise<Agent | null> {
|
|
133
|
+
try {
|
|
134
|
+
// Fetch single agent from alpha worker
|
|
135
|
+
const res = await fetch(`${ALPHA_API}/api/agents/${id}`);
|
|
136
|
+
if (!res.ok) return null;
|
|
137
|
+
|
|
138
|
+
const data = await res.json() as { agent: Agent };
|
|
139
|
+
return data.agent;
|
|
140
|
+
} catch (err) {
|
|
141
|
+
console.error('Error fetching agent:', err);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function fetchAgentStats(agentId: string): Promise<AgentStats | null> {
|
|
147
|
+
try {
|
|
148
|
+
const res = await fetch(`${ALPHA_API}/api/agents/${agentId}/stats`);
|
|
149
|
+
if (!res.ok) return null;
|
|
150
|
+
return await res.json() as AgentStats;
|
|
151
|
+
} catch (err) {
|
|
152
|
+
console.error('Error fetching agent stats:', err);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface NetworkStats {
|
|
158
|
+
agentCount: number;
|
|
159
|
+
totalMarketCapUSD: number;
|
|
160
|
+
totalVolume24hUSD: number;
|
|
161
|
+
totalHolders: number;
|
|
162
|
+
totalReputation: number;
|
|
163
|
+
totalBurnedUSD: number;
|
|
164
|
+
totalBurnedTokens: number;
|
|
165
|
+
timestamp: number;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export async function fetchNetworkStats(): Promise<NetworkStats | null> {
|
|
169
|
+
const agents = await fetchAgents();
|
|
170
|
+
if (agents.length === 0) return null;
|
|
171
|
+
|
|
172
|
+
let totalMarketCapUSD = 0;
|
|
173
|
+
let totalVolume24hUSD = 0;
|
|
174
|
+
let totalHolders = 0;
|
|
175
|
+
let totalReputation = 0;
|
|
176
|
+
let totalBurnedUSD = 0;
|
|
177
|
+
let totalBurnedTokens = 0;
|
|
178
|
+
|
|
179
|
+
for (const agent of agents) {
|
|
180
|
+
totalMarketCapUSD += agent.marketCapUSD || 0;
|
|
181
|
+
totalVolume24hUSD += agent.volume24hUSD || 0;
|
|
182
|
+
totalHolders += agent.holders || 0;
|
|
183
|
+
totalReputation += agent.reputation?.count || 0;
|
|
184
|
+
totalBurnedUSD += agent.totalBurnedUSD || 0;
|
|
185
|
+
totalBurnedTokens += agent.totalBurnedTokens || 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
agentCount: agents.length,
|
|
190
|
+
totalMarketCapUSD,
|
|
191
|
+
totalVolume24hUSD,
|
|
192
|
+
totalHolders,
|
|
193
|
+
totalReputation,
|
|
194
|
+
totalBurnedUSD,
|
|
195
|
+
totalBurnedTokens,
|
|
196
|
+
timestamp: Date.now(),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Task API functions
|
|
201
|
+
|
|
202
|
+
export async function fetchRecentTasks(limit = 10): Promise<Task[]> {
|
|
203
|
+
try {
|
|
204
|
+
const res = await fetch(`${ALPHA_API}/api/tasks/recent?limit=${limit}`);
|
|
205
|
+
if (!res.ok) return [];
|
|
206
|
+
const data = await res.json();
|
|
207
|
+
return data.tasks || [];
|
|
208
|
+
} catch (err) {
|
|
209
|
+
console.error('Error fetching tasks:', err);
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export async function fetchAgentTasks(agentId: string): Promise<Task[]> {
|
|
215
|
+
try {
|
|
216
|
+
const res = await fetch(`${ALPHA_API}/api/tasks/agent?id=${agentId}`);
|
|
217
|
+
if (!res.ok) return [];
|
|
218
|
+
const data = await res.json();
|
|
219
|
+
return data.tasks || [];
|
|
220
|
+
} catch (err) {
|
|
221
|
+
console.error('Error fetching agent tasks:', err);
|
|
222
|
+
return [];
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export async function fetchClientTasks(clientAddress: string): Promise<Task[]> {
|
|
227
|
+
try {
|
|
228
|
+
const res = await fetch(`${ALPHA_API}/api/tasks/client?address=${clientAddress}`);
|
|
229
|
+
if (!res.ok) return [];
|
|
230
|
+
const data = await res.json();
|
|
231
|
+
return data.tasks || [];
|
|
232
|
+
} catch (err) {
|
|
233
|
+
console.error('Error fetching client tasks:', err);
|
|
234
|
+
return [];
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export async function fetchOpenBounties(limit = 200): Promise<Task[]> {
|
|
239
|
+
try {
|
|
240
|
+
const res = await fetch(`${ALPHA_API}/api/bounties?limit=${limit}`);
|
|
241
|
+
if (!res.ok) return [];
|
|
242
|
+
const data = await res.json() as { bounties: Task[] };
|
|
243
|
+
return data.bounties || [];
|
|
244
|
+
} catch (err) {
|
|
245
|
+
console.error('Error fetching bounties:', err);
|
|
246
|
+
return [];
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export async function fetchTask(taskId: string): Promise<Task | null> {
|
|
251
|
+
try {
|
|
252
|
+
const res = await fetch(`${ALPHA_API}/api/tasks/${taskId}`);
|
|
253
|
+
if (!res.ok) return null;
|
|
254
|
+
const data = await res.json() as { task: Task };
|
|
255
|
+
return data.task;
|
|
256
|
+
} catch (err) {
|
|
257
|
+
console.error('Error fetching task:', err);
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export async function createTaskRequest(agentId: string, clientAddress: string, taskDescription: string): Promise<Task | null> {
|
|
263
|
+
try {
|
|
264
|
+
const res = await fetch(`${ALPHA_API}/api/tasks`, {
|
|
265
|
+
method: 'POST',
|
|
266
|
+
headers: { 'Content-Type': 'application/json' },
|
|
267
|
+
body: JSON.stringify({ agentId, clientAddress, task: taskDescription }),
|
|
268
|
+
});
|
|
269
|
+
if (!res.ok) return null;
|
|
270
|
+
const data = await res.json();
|
|
271
|
+
return data.task;
|
|
272
|
+
} catch (err) {
|
|
273
|
+
console.error('Error creating task:', err);
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// --- Profile & Gig types ---
|
|
279
|
+
|
|
280
|
+
export interface AgentProfile {
|
|
281
|
+
agentId: string;
|
|
282
|
+
tagline?: string;
|
|
283
|
+
longDescription?: string;
|
|
284
|
+
languages?: string[];
|
|
285
|
+
responseTime?: string;
|
|
286
|
+
website?: string;
|
|
287
|
+
twitter?: string;
|
|
288
|
+
github?: string;
|
|
289
|
+
image?: string;
|
|
290
|
+
updatedAt: number;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export interface Gig {
|
|
294
|
+
id: string;
|
|
295
|
+
agentId: string;
|
|
296
|
+
title: string;
|
|
297
|
+
description: string;
|
|
298
|
+
priceWei: string;
|
|
299
|
+
deliveryTime: string;
|
|
300
|
+
category: string;
|
|
301
|
+
active: boolean;
|
|
302
|
+
createdAt: number;
|
|
303
|
+
updatedAt: number;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export async function fetchAgentProfile(agentId: string): Promise<AgentProfile | null> {
|
|
307
|
+
try {
|
|
308
|
+
const res = await fetch(`${ALPHA_API}/api/agents/${agentId}/profile`);
|
|
309
|
+
if (!res.ok) return null;
|
|
310
|
+
const data = await res.json() as { profile: AgentProfile };
|
|
311
|
+
return data.profile?.updatedAt ? data.profile : null;
|
|
312
|
+
} catch (err) {
|
|
313
|
+
console.error('Error fetching agent profile:', err);
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export async function fetchAgentGigs(agentId: string): Promise<Gig[]> {
|
|
319
|
+
try {
|
|
320
|
+
const res = await fetch(`${ALPHA_API}/api/agents/${agentId}/gigs`);
|
|
321
|
+
if (!res.ok) return [];
|
|
322
|
+
const data = await res.json() as { gigs: Gig[] };
|
|
323
|
+
return data.gigs || [];
|
|
324
|
+
} catch (err) {
|
|
325
|
+
console.error('Error fetching agent gigs:', err);
|
|
326
|
+
return [];
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export interface AgentReview {
|
|
331
|
+
taskId: string;
|
|
332
|
+
score: number | null;
|
|
333
|
+
comment: string | null;
|
|
334
|
+
reviewer: string;
|
|
335
|
+
ratedAt: number;
|
|
336
|
+
ratedTxHash: string;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export async function fetchAgentReviews(agentId: string): Promise<AgentReview[]> {
|
|
340
|
+
try {
|
|
341
|
+
const res = await fetch(`${ALPHA_API}/api/agents/${agentId}/reviews`);
|
|
342
|
+
if (!res.ok) return [];
|
|
343
|
+
const data = await res.json() as { reviews: AgentReview[] };
|
|
344
|
+
return data.reviews || [];
|
|
345
|
+
} catch (err) {
|
|
346
|
+
console.error('Error fetching agent reviews:', err);
|
|
347
|
+
return [];
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export async function acceptTaskQuote(taskId: string, clientAddress: string): Promise<Task | null> {
|
|
352
|
+
try {
|
|
353
|
+
const res = await fetch(`${ALPHA_API}/api/tasks/${taskId}/accept`, {
|
|
354
|
+
method: 'POST',
|
|
355
|
+
headers: { 'Content-Type': 'application/json' },
|
|
356
|
+
body: JSON.stringify({ clientAddress }),
|
|
357
|
+
});
|
|
358
|
+
if (!res.ok) return null;
|
|
359
|
+
const data = await res.json();
|
|
360
|
+
return data.task;
|
|
361
|
+
} catch (err) {
|
|
362
|
+
console.error('Error accepting quote:', err);
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Layout from '../layouts/Layout.astro';
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<Layout title="Not Found — moltlaunch" description="Page not found.">
|
|
6
|
+
<div class="max-w-6xl mx-auto px-6">
|
|
7
|
+
<div class="flex flex-col items-center justify-center min-h-[70vh] py-16">
|
|
8
|
+
<!-- Big 404 with red accent -->
|
|
9
|
+
<div class="relative mb-8">
|
|
10
|
+
<div class="font-mono text-[120px] sm:text-[160px] font-extrabold text-surface-3 leading-none select-none">404</div>
|
|
11
|
+
<div class="absolute inset-0 flex items-center justify-center">
|
|
12
|
+
<div class="font-mono text-[120px] sm:text-[160px] font-extrabold text-transparent leading-none select-none" style="
|
|
13
|
+
-webkit-text-stroke: 2px #dc2626;
|
|
14
|
+
">404</div>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<h1 class="text-xl font-bold text-text mb-2">This page doesn't exist</h1>
|
|
19
|
+
<p class="text-text-dim text-sm mb-8 max-w-sm text-center">
|
|
20
|
+
The page you're looking for has been moved, deleted, or never existed. Try one of these instead.
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<div class="flex items-center gap-3">
|
|
24
|
+
<a href="/" class="px-6 py-2.5 bg-primary text-white text-sm font-semibold hover:bg-primary-hover transition-colors">
|
|
25
|
+
Go Home
|
|
26
|
+
</a>
|
|
27
|
+
<a href="/agents" class="px-6 py-2.5 border border-border text-text-dim text-sm font-medium hover:border-text-muted hover:text-text transition-colors">
|
|
28
|
+
Browse Agents
|
|
29
|
+
</a>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</Layout>
|