import { log } from './logger.js'; const RELAY_API = 'https://api.morphyagent.com/api'; // ─── Register a new handle ────────────────────────────────────────────────── export async function registerHandle( username: string, tier: string, walletAddress?: string, ): Promise<{ token: string; relayUrl: string }> { const payload: Record = { username, tier }; if (walletAddress) payload.walletAddress = walletAddress; const res = await fetch(`${RELAY_API}/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); const data = await res.json(); if (!res.ok) { throw new Error(data.error || `Registration failed (${res.status})`); } return { token: data.token, relayUrl: data.relayUrl }; } // ─── Check username availability ───────────────────────────────────────────── export async function checkAvailability( username: string, ): Promise<{ valid: boolean; error?: string; handles: { tier: string; url: string; paid: boolean; price: number; available: boolean }[]; }> { const res = await fetch(`${RELAY_API}/availability/${encodeURIComponent(username)}`); return res.json(); } // ─── Claim a reserved (purchased) handle ──────────────────────────────────── export async function claimReservedHandle( handle: string, hash: string, walletAddress?: string, ): Promise<{ token: string; relayUrl: string }> { const payload: Record = { handle, hash }; if (walletAddress) payload.walletAddress = walletAddress; const res = await fetch(`${RELAY_API}/handle/claim-reserved`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); const data = await res.json(); if (!res.ok) { throw new Error(data.error || `Claim failed (${res.status})`); } return { token: data.token, relayUrl: data.relayUrl }; } // ─── Release handle ───────────────────────────────────────────────────────── export async function releaseHandle(token: string): Promise { const res = await fetch(`${RELAY_API}/handle`, { method: 'DELETE', headers: { Authorization: `Bearer ${token}` }, }); if (!res.ok) { const data = await res.json().catch(() => ({})); throw new Error(data.error || `Release failed (${res.status})`); } } // ─── Carrier ticket (tunnel.mode:'relay') ─────────────────────────────────── // Mint a short-lived Ed25519-signed ticket from the long-lived relay token. The agent // presents it when dialing its Durable Object carrier. Cheap + frequent (tickets expire // in ~5min); callers cache the last good ticket and re-mint on an independent backoff. export async function fetchTicket(token: string): Promise<{ ticket: string; expiresIn: number }> { const res = await fetch(`${RELAY_API}/edge/ticket`, { method: 'POST', headers: { Authorization: `Bearer ${token}` }, }); const data = await res.json().catch(() => ({})); if (!res.ok) throw new Error(data.error || `Ticket mint failed (${res.status})`); return { ticket: data.ticket, expiresIn: data.expiresIn }; } // ─── Report wallet address ────────────────────────────────────────────────── // Push the agent's wallet address to the relay so it shows linked in the // dashboard. Managed (tunnel-off) bots never heartbeat/register, so this is the // only way their wallet reaches the relay. Throws on failure so callers can // decide to log; safe to call fire-and-forget on every boot. export async function reportWallet(token: string, walletAddress: string): Promise { const res = await fetch(`${RELAY_API}/wallet`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ walletAddress }), }); if (!res.ok) { const data = await res.json().catch(() => ({})); throw new Error(data.error || `Wallet report failed (${res.status})`); } } // ─── Disconnect (graceful shutdown) ────────────────────────────────────────── // Relay-mode presence is the live carrier socket (the DO posts /api/edge/presence on // connect/drop), so there is no heartbeat to stop — this just best-effort marks offline. export async function disconnect(token: string): Promise { try { await fetch(`${RELAY_API}/disconnect`, { method: 'POST', headers: { Authorization: `Bearer ${token}` }, }); } catch { // Best-effort — if it fails, heartbeat timeout will mark offline } }