// The page: reads the form, calls loadAgent and renders the result with textContent only (no HTML injection).
import { loadAgent, httpRpc, httpFetchJson, resolveUri, REGISTRIES } from './viewer.js';
import { DEFAULT_IPFS_GATEWAY } from './config.js';
const $ = (id) => document.getElementById(id);
const el = (tag, props = {}, ...kids) => {
const n = document.createElement(tag);
for (const [k, v] of Object.entries(props)) if (v != null) n[k] = v;
for (const k of kids) n.append(k instanceof Node ? k : document.createTextNode(String(k)));
return n;
};
const short = (a) => (a && a.length > 14 ? `${a.slice(0, 8)}...${a.slice(-6)}` : a || '');
const safeHttp = (u) => (/^https?:\/\//i.test(u || '') ? u : null);
const safeImg = (u, gw) => {
const r = resolveUri(u, gw);
return /^https:\/\//i.test(r || '') || /^data:image\/(png|jpe?g|gif|webp|svg\+xml);/i.test(r || '') ? r : null;
};
$('registries').textContent = Object.entries(REGISTRIES).map(([k, v]) => `${k} identity ${short(v.identity)}, reputation ${short(v.reputation)}`).join('; ');
function render(r, net, gw) {
const box = $('result');
box.replaceChildren();
const label = { ok: 'Found', empty: 'No such agent', error: 'Could not load' }[r.status];
const head = el('div', { className: 'card' }, el('div', { className: `status ${r.status}` }, `${label}: agent ${r.agentId ?? ''} on ${net}`));
if (r.error) head.append(el('p', {}, r.error));
box.append(head);
if (r.status === 'empty' || !r.owner) return;
const explorer = REGISTRIES[net]?.explorer;
const reg = r.registration;
const id = el('div', { className: 'card' });
const top = el('div', { className: 'agent' });
const img = reg && safeImg(reg.image, gw);
if (img) {
const pic = el('img', { src: img, alt: '', loading: 'lazy', referrerPolicy: 'no-referrer' });
pic.onerror = () => pic.remove();
top.append(pic);
}
top.append(el('div', {}, el('h2', {}, reg?.name || 'Unnamed agent'), el('p', {}, reg?.description || 'No description.')));
id.append(top);
const dl = el('dl');
const ownerLink = explorer ? el('a', { href: `${explorer}/address/${r.owner}`, target: '_blank', rel: 'noopener' }, r.owner) : r.owner;
dl.append(el('dt', {}, 'Owner'), el('dd', {}, ownerLink));
dl.append(el('dt', {}, 'tokenURI'), el('dd', {}, r.tokenURI && r.tokenURI.startsWith('data:') ? `${r.tokenURI.slice(0, 40)}... (inline data)` : r.tokenURI || ''));
if (reg) {
dl.append(el('dt', {}, 'Supported trust'), el('dd', {}, reg.supportedTrust.length ? reg.supportedTrust.join(', ') : 'none declared'));
}
id.append(dl);
if (reg) {
id.append(el('h2', {}, 'Endpoints'));
if (!reg.endpoints.length) id.append(el('p', { className: 'muted' }, 'No endpoints declared.'));
else {
const ul = el('ul');
for (const e of reg.endpoints) {
const href = safeHttp(e.endpoint);
ul.append(el('li', {}, `${e.name || 'endpoint'}${e.version ? ` v${e.version}` : ''}: `, href ? el('a', { href, target: '_blank', rel: 'noopener nofollow' }, e.endpoint) : e.endpoint));
}
id.append(ul);
}
}
box.append(id);
const rep = el('div', { className: 'card' }, el('h2', {}, 'Reputation'));
const f = r.feedback;
if (!f) rep.append(el('p', { className: 'muted' }, 'No reputation registry configured.'));
else if (f.error) rep.append(el('p', { className: 'status error' }, f.error));
else if (!f.count) rep.append(el('p', {}, 'This agent has no feedback yet.'));
else {
rep.append(el('p', {}, `${f.count} feedback entries, average score ${f.average}.`));
const t = el('table', {}, el('thead', {}, el('tr', {}, el('th', {}, 'Client'), el('th', {}, 'Score'), el('th', {}, 'Tags'))));
const tb = el('tbody');
for (const i of f.items) {
const tags = el('td');
for (const tag of i.tags) tags.append(el('span', { className: 'tag' }, tag));
tb.append(el('tr', {}, el('td', {}, short(i.client)), el('td', {}, String(i.score)), tags));
}
t.append(tb);
rep.append(el('p', { className: 'muted' }, 'Latest entries:'), t);
}
box.append(rep);
}
async function show(net, agentId, rpcUrl, gateway) {
const cfg = REGISTRIES[net];
const gw = gateway || DEFAULT_IPFS_GATEWAY;
$('result').replaceChildren(el('div', { className: 'card muted' }, 'Loading...'));
const r = await loadAgent({
agentId, network: net, identityRegistry: cfg.identity, reputationRegistry: cfg.reputation,
rpc: httpRpc(rpcUrl || cfg.rpcUrl), fetchJson: httpFetchJson(), ipfsGateway: gw,
});
render(r, net, gw);
}
$('lookup').addEventListener('submit', (ev) => {
ev.preventDefault();
const net = $('network').value, id = $('agentId').value.trim();
const u = new URL(location.href);
u.searchParams.set('network', net); u.searchParams.set('agent', id);
history.replaceState(null, '', u);
show(net, id, $('rpcUrl').value.trim(), $('gateway').value.trim());
});
const q = new URLSearchParams(location.search);
if (q.get('network') && REGISTRIES[q.get('network')]) $('network').value = q.get('network');
if (q.get('agent')) { $('agentId').value = q.get('agent'); show($('network').value, q.get('agent'), '', ''); }
agentgate-source: code mirror on Cloudflare Pages. All repos