home: live API peek in the agent-native callout (0.92.0)
the callout claimed 'drive the whole platform over JSON' but only said it.
now it shows it: a compact terminal with tabs for the public read-only
endpoints (stats / roster / activity) that fetches the LIVE response and
pretty-prints it with JSON syntax highlighting.
- proves the agent-native differentiator with real data, not a mockup
- read-only public endpoints, no auth; switching tabs updates the curl line
and refetches; cancels in-flight on unmount
- small self-contained jsonHl() highlighter (keys/strings/numbers/bools),
output escaped before highlighting so it's injection-safe
- verified live: stats shows {classes:1,online:1,...}, roster shows
Kebab Webagent, 9 key + 7 number spans highlighted, tabs refetch
This commit is contained in:
parent
e5e79d75aa
commit
ce9d2336f7
@ -17,7 +17,7 @@
|
|||||||
@media (prefers-reduced-motion: reduce) { .app-loading-gem { animation: none; transform: rotate(45deg); } }
|
@media (prefers-reduced-motion: reduce) { .app-loading-gem { animation: none; transform: rotate(45deg); } }
|
||||||
</style>
|
</style>
|
||||||
<meta name="description" content="amerc is the agent mercenary tavern — hire, run, publish, and remotely deliver AI agents across software boundaries. No infrastructure, just skills." />
|
<meta name="description" content="amerc is the agent mercenary tavern — hire, run, publish, and remotely deliver AI agents across software boundaries. No infrastructure, just skills." />
|
||||||
<meta name="version" content="0.91.0-docsnav" />
|
<meta name="version" content="0.92.0-apipeek" />
|
||||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
<link rel="icon" href="/app-192.png" type="image/png" sizes="192x192" />
|
<link rel="icon" href="/app-192.png" type="image/png" sizes="192x192" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "amerc-site",
|
"name": "amerc-site",
|
||||||
"version": "0.91.0-docsnav",
|
"version": "0.92.0-apipeek",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { api } from './api.js';
|
|||||||
const Menu = ({ size = 20 }) => (<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="18" x2="21" y2="18" /></svg>);
|
const Menu = ({ size = 20 }) => (<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="18" x2="21" y2="18" /></svg>);
|
||||||
const X = ({ size = 20 }) => (<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg>);
|
const X = ({ size = 20 }) => (<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg>);
|
||||||
|
|
||||||
const RELEASE = '0.91.0-docsnav';
|
const RELEASE = '0.92.0-apipeek';
|
||||||
|
|
||||||
const NAV = [
|
const NAV = [
|
||||||
{ id: 'home', label: 'Tavern' },
|
{ id: 'home', label: 'Tavern' },
|
||||||
@ -227,6 +227,57 @@ function ChatDemo() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// Live, public, read-only API peek — proves the agent-native claim with real data.
|
||||||
|
const PEEK = [
|
||||||
|
{ id: 'stats', path: '/agents/stats', label: 'stats' },
|
||||||
|
{ id: 'classes', path: '/agents/classes', label: 'roster' },
|
||||||
|
{ id: 'activity', path: '/agents/activity', label: 'activity' },
|
||||||
|
];
|
||||||
|
const jsonEsc = (s) => s.replace(/[&<>]/g, (c) => ({ '&': '&', '<': '<', '>': '>' }[c]));
|
||||||
|
function jsonHl(obj) {
|
||||||
|
if (obj == null) return '';
|
||||||
|
const json = JSON.stringify(obj, null, 2);
|
||||||
|
return jsonEsc(json).replace(
|
||||||
|
/("(?:\\.|[^"\\])*")(?=\s*:)|("(?:\\.|[^"\\])*")|\b(true|false|null)\b|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g,
|
||||||
|
(m, key, str, lit, num) => {
|
||||||
|
if (key) return `<span class="j-key">${key}</span>`;
|
||||||
|
if (str) return `<span class="j-str">${str}</span>`;
|
||||||
|
if (lit) return `<span class="j-lit">${lit}</span>`;
|
||||||
|
return `<span class="j-num">${num}</span>`;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function ApiPeek() {
|
||||||
|
const [ep, setEp] = useState('stats');
|
||||||
|
const [out, setOut] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
useEffect(() => {
|
||||||
|
let live = true;
|
||||||
|
setLoading(true); setOut(null);
|
||||||
|
const e = PEEK.find((x) => x.id === ep);
|
||||||
|
api(e.path)
|
||||||
|
.then((d) => { if (live) setOut(d); })
|
||||||
|
.catch((err) => { if (live) setOut({ error: err.message }); })
|
||||||
|
.finally(() => { if (live) setLoading(false); });
|
||||||
|
return () => { live = false; };
|
||||||
|
}, [ep]);
|
||||||
|
const cur = PEEK.find((x) => x.id === ep);
|
||||||
|
return (
|
||||||
|
<div className="hf-peek">
|
||||||
|
<div className="hf-peek-tabs" role="tablist" aria-label="API endpoint">
|
||||||
|
{PEEK.map((e) => (
|
||||||
|
<button key={e.id} type="button" role="tab" aria-selected={ep === e.id} className={`hf-peek-tab${ep === e.id ? ' on' : ''}`} onClick={() => setEp(e.id)}>{e.label}</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="hf-peek-term">
|
||||||
|
<div className="hf-peek-cmd"><span className="hf-peek-dollar" aria-hidden="true">$</span> curl https://amerc.ai/api{cur.path}</div>
|
||||||
|
{loading
|
||||||
|
? <pre className="hf-peek-out hf-peek-loading">fetching live response…</pre>
|
||||||
|
: <pre className="hf-peek-out" aria-live="polite" dangerouslySetInnerHTML={{ __html: jsonHl(out) }} />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
function HomeFeatures({ setRoute }) {
|
function HomeFeatures({ setRoute }) {
|
||||||
return (
|
return (
|
||||||
<section className="hf">
|
<section className="hf">
|
||||||
@ -260,11 +311,14 @@ function HomeFeatures({ setRoute }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Reveal className="hf-agentnative">
|
<Reveal className="hf-agentnative">
|
||||||
<span className="hf-an-ico" aria-hidden="true">🤖</span>
|
<div className="hf-an-head">
|
||||||
<div className="hf-an-body">
|
<span className="hf-an-ico" aria-hidden="true">🤖</span>
|
||||||
<h3>Agent-native by design</h3>
|
<div className="hf-an-body">
|
||||||
<p>amerc is built for agents, not just humans — mint an <b>API key</b> and drive the whole platform over JSON, get callable <b>WebMCP</b> tools right on the page, and read <a href="/llms.txt">llms.txt</a> for the map. Agents can hire agents.</p>
|
<h3>Agent-native by design</h3>
|
||||||
|
<p>amerc is built for agents, not just humans — mint an <b>API key</b> and drive the whole platform over JSON, get callable <b>WebMCP</b> tools right on the page, and read <a href="/llms.txt">llms.txt</a> for the map. Agents can hire agents.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<ApiPeek />
|
||||||
</Reveal>
|
</Reveal>
|
||||||
<ActivityFeed />
|
<ActivityFeed />
|
||||||
<div className="hf-cta">
|
<div className="hf-cta">
|
||||||
|
|||||||
@ -1506,14 +1506,27 @@ button {
|
|||||||
@media (prefers-reduced-motion: reduce) { .app-loading-gem { animation: none; transform: rotate(45deg); } }
|
@media (prefers-reduced-motion: reduce) { .app-loading-gem { animation: none; transform: rotate(45deg); } }
|
||||||
|
|
||||||
/* agent-native callout (0.83) */
|
/* agent-native callout (0.83) */
|
||||||
.hf-agentnative { max-width: 700px; margin: 30px auto 0; display: flex; gap: 16px; align-items: center; padding: 18px 22px; border-radius: 16px;
|
.hf-agentnative { max-width: 760px; margin: 30px auto 0; display: flex; flex-direction: column; gap: 14px; padding: 18px 22px; border-radius: 16px;
|
||||||
background: linear-gradient(120deg, rgba(39,216,255,0.08), rgba(20,14,30,0.3)); border: 1px solid #2f5e6e; box-shadow: 0 0 0 1px rgba(39,216,255,0.06); }
|
background: linear-gradient(120deg, rgba(39,216,255,0.08), rgba(20,14,30,0.3)); border: 1px solid #2f5e6e; box-shadow: 0 0 0 1px rgba(39,216,255,0.06); }
|
||||||
|
.hf-an-head { display: flex; gap: 16px; align-items: center; }
|
||||||
.hf-an-ico { font-size: 34px; line-height: 1; flex: 0 0 auto; filter: drop-shadow(0 3px 8px rgba(0,0,0,0.4)); }
|
.hf-an-ico { font-size: 34px; line-height: 1; flex: 0 0 auto; filter: drop-shadow(0 3px 8px rgba(0,0,0,0.4)); }
|
||||||
.hf-an-body h3 { margin: 0 0 5px; color: #fff; font-size: 17px; }
|
.hf-an-body h3 { margin: 0 0 5px; color: #fff; font-size: 17px; }
|
||||||
.hf-an-body p { margin: 0; color: #b6a7d4; font-size: 13.5px; line-height: 1.6; }
|
.hf-an-body p { margin: 0; color: #b6a7d4; font-size: 13.5px; line-height: 1.6; }
|
||||||
.hf-an-body b { color: #7fe9ff; font-weight: 600; }
|
.hf-an-body b { color: #7fe9ff; font-weight: 600; }
|
||||||
.hf-an-body a { color: #46c8e0; }
|
.hf-an-body a { color: #46c8e0; }
|
||||||
@media (max-width: 600px) { .hf-agentnative { flex-direction: column; text-align: center; } }
|
/* live API peek inside the agent-native callout (0.92) */
|
||||||
|
.hf-peek { border-radius: 12px; overflow: hidden; border: 1px solid #20384a; background: #0a0e16; text-align: left; }
|
||||||
|
.hf-peek-tabs { display: flex; gap: 2px; padding: 7px 8px 0; background: #0c1320; }
|
||||||
|
.hf-peek-tab { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12px; color: #7f93a8; background: none; border: none; padding: 5px 12px; border-radius: 7px 7px 0 0; cursor: pointer; }
|
||||||
|
.hf-peek-tab:hover { color: #cfe2f2; }
|
||||||
|
.hf-peek-tab.on { color: #07111a; background: #46c8e0; font-weight: 600; }
|
||||||
|
.hf-peek-term { padding: 11px 14px 13px; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12.5px; }
|
||||||
|
.hf-peek-cmd { color: #9fd9e8; margin-bottom: 8px; word-break: break-all; }
|
||||||
|
.hf-peek-dollar { color: #5b6b7d; margin-right: 6px; }
|
||||||
|
.hf-peek-out { margin: 0; max-height: 186px; overflow: auto; color: #c6d4e2; line-height: 1.55; white-space: pre-wrap; word-break: break-word; }
|
||||||
|
.hf-peek-loading { color: #5b6b7d; }
|
||||||
|
.j-key { color: #7fe9ff; } .j-str { color: #9ee8a6; } .j-num { color: #f3b86a; } .j-lit { color: #c79bf0; }
|
||||||
|
@media (max-width: 600px) { .hf-an-head { flex-direction: column; text-align: center; } }
|
||||||
|
|
||||||
/* docs: on-this-page TOC + heading anchors (0.88) */
|
/* docs: on-this-page TOC + heading anchors (0.88) */
|
||||||
.docs-toc { margin: 4px 0 24px; padding: 13px 16px; border: 1px solid #2a2140; border-radius: 12px; background: rgba(39, 216, 255, 0.03); display: flex; flex-direction: column; gap: 1px; }
|
.docs-toc { margin: 4px 0 24px; padding: 13px 16px; border: 1px solid #2a2140; border-radius: 12px; background: rgba(39, 216, 255, 0.03); display: flex; flex-direction: column; gap: 1px; }
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user