SignalMesh / index.html
Ig0tU
feat: reposition as dev-environment hub; sync all gap-closing components
78e921e
Raw
History Blame Contribute Delete
24.2 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>SignalMesh — Developer Reference</title>
<style>
:root{
--pink:#e91e8c;--amber:#f59e0b;--green:#10b981;--red:#ef4444;--blue:#60a5fa;
--bg:#0d0d14;--surface:#13131f;--card:#181825;--border:#252540;
--text:#e8e8f4;--dim:#7878a0;--dimmer:#444460;
--font:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
--mono:'JetBrains Mono','Fira Code','Courier New',monospace;
}
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{background:var(--bg);color:var(--text);font-family:var(--font);font-size:14px;line-height:1.6}
a{color:var(--amber);text-decoration:none}a:hover{text-decoration:underline}
::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-thumb{background:var(--border);border-radius:4px}
/* nav */
nav{position:sticky;top:0;z-index:100;background:rgba(13,13,20,.92);backdrop-filter:blur(12px);border-bottom:1px solid var(--border);padding:0 32px;display:flex;align-items:center;height:52px;gap:24px}
.nav-logo{font-weight:800;font-size:15px;background:linear-gradient(90deg,var(--pink),var(--amber));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;letter-spacing:-.3px;white-space:nowrap}
.nav-links{display:flex;gap:20px;margin-left:auto}
.nav-links a{color:var(--dim);font-size:13px;font-weight:500;transition:color .15s}
.nav-links a:hover{color:var(--text);text-decoration:none}
.nav-status{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--dim);border:1px solid var(--border);padding:4px 10px;border-radius:20px;white-space:nowrap}
.dot{width:7px;height:7px;border-radius:50%;background:var(--green);animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
/* hero */
.hero{padding:72px 32px 56px;max-width:900px;margin:0 auto}
.hero-tag{display:inline-block;background:rgba(233,30,140,.12);border:1px solid rgba(233,30,140,.3);color:var(--pink);font-size:11px;font-weight:700;letter-spacing:1px;text-transform:uppercase;padding:4px 12px;border-radius:20px;margin-bottom:20px}
.hero h1{font-size:38px;font-weight:800;line-height:1.15;letter-spacing:-.5px;margin-bottom:16px}
.hero h1 span{background:linear-gradient(90deg,var(--pink),var(--amber));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.hero p{font-size:16px;color:var(--dim);max-width:620px;margin-bottom:32px;line-height:1.7}
.hero-actions{display:flex;gap:12px;flex-wrap:wrap}
.btn{display:inline-flex;align-items:center;gap:8px;padding:10px 20px;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;transition:all .15s;text-decoration:none}
.btn-primary{background:linear-gradient(135deg,var(--pink),var(--amber));color:#000}
.btn-primary:hover{opacity:.88;text-decoration:none}
.btn-ghost{background:transparent;border:1px solid var(--border);color:var(--dim)}
.btn-ghost:hover{border-color:var(--amber);color:var(--text);text-decoration:none}
/* layout */
.layout{max-width:900px;margin:0 auto;padding:0 32px 80px;display:flex;flex-direction:column;gap:48px}
/* section headers */
.section-hdr{display:flex;align-items:baseline;gap:12px;margin-bottom:20px}
.section-hdr h2{font-size:18px;font-weight:700;letter-spacing:-.2px}
.section-hdr .badge{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.8px;color:var(--dim);border:1px solid var(--border);padding:2px 8px;border-radius:10px}
/* cards */
.card{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:20px 24px}
.card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:16px}
.card-sm{background:var(--card);border:1px solid var(--border);border-radius:10px;padding:16px 20px}
.card-sm h3{font-size:13px;font-weight:700;margin-bottom:6px;display:flex;align-items:center;gap:8px}
.card-sm p{font-size:12px;color:var(--dim);line-height:1.5}
/* code blocks */
pre{background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:16px 20px;overflow-x:auto;font-family:var(--mono);font-size:12px;line-height:1.7;margin:0}
code{font-family:var(--mono);font-size:12px}
.code-header{background:var(--surface);border:1px solid var(--border);border-bottom:0;border-radius:8px 8px 0 0;padding:8px 16px;display:flex;align-items:center;justify-content:space-between}
.code-header+pre{border-radius:0 0 8px 8px}
.code-lang{font-size:11px;color:var(--dim);font-family:var(--mono)}
.copy-btn{font-size:11px;color:var(--dim);background:none;border:1px solid var(--border);padding:2px 8px;border-radius:4px;cursor:pointer;font-family:var(--font);transition:all .15s}
.copy-btn:hover{color:var(--text);border-color:var(--amber)}
.hl-kw{color:#c792ea}
.hl-str{color:#c3e88d}
.hl-cmt{color:#546e7a}
.hl-fn{color:#82aaff}
.hl-num{color:#f78c6c}
.hl-key{color:var(--amber)}
/* tabs */
.tabs{display:flex;gap:2px;margin-bottom:-1px}
.tab{padding:7px 16px;font-size:12px;font-weight:600;color:var(--dim);border:1px solid transparent;border-bottom:0;border-radius:6px 6px 0 0;cursor:pointer;background:none;font-family:var(--font);transition:all .15s}
.tab.active{color:var(--text);background:var(--surface);border-color:var(--border)}
.tab-content{display:none}.tab-content.active{display:block}
/* steps */
.steps{display:flex;flex-direction:column;gap:0}
.step{display:flex;gap:20px;padding:20px 0;border-bottom:1px solid var(--border)}
.step:last-child{border-bottom:0}
.step-num{width:28px;height:28px;border-radius:50%;background:linear-gradient(135deg,var(--pink),var(--amber));color:#000;font-size:12px;font-weight:800;display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px}
.step-body h3{font-size:14px;font-weight:700;margin-bottom:6px}
.step-body p{font-size:13px;color:var(--dim);margin-bottom:12px;line-height:1.6}
/* freq table */
table{width:100%;border-collapse:collapse;font-size:12px}
th{text-align:left;padding:8px 12px;color:var(--dim);font-size:11px;text-transform:uppercase;letter-spacing:.6px;border-bottom:1px solid var(--border)}
td{padding:10px 12px;border-bottom:1px solid rgba(37,37,64,.5);vertical-align:top}
tr:last-child td{border-bottom:0}
.freq-name{font-family:var(--mono);color:var(--blue);font-size:11px}
.mode-badge{display:inline-block;padding:2px 7px;border-radius:10px;font-size:10px;font-weight:700;letter-spacing:.3px}
.mode-emit{background:rgba(16,185,129,.12);border:1px solid rgba(16,185,129,.3);color:#6ee7b7}
.mode-listen{background:rgba(96,165,250,.12);border:1px solid rgba(96,165,250,.3);color:#93c5fd}
.mode-prot{background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.25);color:#fca5a5}
/* mesh status */
.mesh-stat{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}
.stat-chip{background:var(--surface);border:1px solid var(--border);padding:6px 14px;border-radius:8px;font-size:12px;color:var(--dim)}
.stat-chip span{color:var(--text);font-weight:700;font-family:var(--mono)}
/* agents.md callout */
.callout{background:linear-gradient(135deg,rgba(233,30,140,.06),rgba(245,158,11,.06));border:1px solid rgba(233,30,140,.25);border-radius:12px;padding:24px}
.callout h3{font-size:15px;font-weight:700;margin-bottom:8px}
.callout p{font-size:13px;color:var(--dim);line-height:1.6;margin-bottom:0}
@media(max-width:640px){
.hero{padding:48px 20px 40px}.layout{padding:0 20px 60px}
nav{padding:0 20px}.nav-links{display:none}
.hero h1{font-size:26px}
}
</style>
</head>
<body>
<nav>
<span class="nav-logo">📡 SignalMesh</span>
<div class="nav-links">
<a href="#quickstart">Quickstart</a>
<a href="#api">API</a>
<a href="#agents-md">AGENTS.md</a>
<a href="#frequencies">Frequencies</a>
<a href="/docs" target="_blank">Swagger ↗</a>
</div>
<div class="nav-status" id="navStatus">
<div class="dot"></div>
<span id="statusText">checking…</span>
</div>
</nav>
<div class="hero">
<div class="hero-tag">Developer Reference · v2.0.0</div>
<h1>Ambient context for<br><span>agent fleets</span> — no tool schemas</h1>
<p>SignalMesh is a lightweight broadcast/subscribe context layer for multi-agent systems.
Agents tune in to frequencies, receive pre-hydrated context, and emit signals via XML tags —
eliminating tool schema overhead from read operations entirely.</p>
<div class="hero-actions">
<a href="https://github.com/Ig0tU/SignalMesh" target="_blank" class="btn btn-primary">View on GitHub</a>
<a href="/docs" target="_blank" class="btn btn-ghost">API Docs (Swagger)</a>
<a href="/agents.md" target="_blank" class="btn btn-ghost">AGENTS.md ↗</a>
</div>
</div>
<div class="layout">
<!-- live status -->
<div>
<div class="section-hdr"><h2>Live Mesh</h2><span class="badge">real-time</span></div>
<div class="card">
<div class="mesh-stat" id="meshStats">
<div class="stat-chip">signals <span id="sigCount"></span></div>
<div class="stat-chip">frequencies <span id="freqCount"></span></div>
<div class="stat-chip">grid nodes <span id="gridNodes"></span></div>
<div class="stat-chip">quarantined <span id="quarCount"></span></div>
<div class="stat-chip">version <span id="ver"></span></div>
</div>
</div>
</div>
<!-- quickstart -->
<div id="quickstart">
<div class="section-hdr"><h2>Quickstart</h2><span class="badge">3 steps</span></div>
<div class="card">
<div class="steps">
<div class="step">
<div class="step-num">1</div>
<div class="step-body">
<h3>Run locally</h3>
<p>Clone the repo and start the FastAPI server. The mesh self-hydrates from curated feeds on startup.</p>
<div class="code-header"><span class="code-lang">bash</span><button class="copy-btn" onclick="cp(this)">copy</button></div>
<pre><span class="hl-cmt"># clone & start</span>
git clone https://github.com/Ig0tU/SignalMesh
cd SignalMesh
pip install -r requirements.txt
uvicorn signalmesh_api:app --host 0.0.0.0 --port 7860
<span class="hl-cmt"># or with Docker</span>
docker build -t signalmesh . && docker run -p 7860:7860 signalmesh</pre>
</div>
</div>
<div class="step">
<div class="step-num">2</div>
<div class="step-body">
<h3>Tune in — read signals (0 tool schemas)</h3>
<p>Call <code>/api/tune_in</code> with keywords. The mesh returns pre-hydrated context ready to prepend to any system prompt. No tool schema tokens consumed.</p>
<div class="tabs" id="tab-tunein">
<button class="tab active" onclick="switchTab('tunein','curl')">curl</button>
<button class="tab" onclick="switchTab('tunein','python')">python</button>
<button class="tab" onclick="switchTab('tunein','js')">js</button>
</div>
<div id="tunein-curl" class="tab-content active">
<div class="code-header" style="border-radius:0"><span class="code-lang">bash</span><button class="copy-btn" onclick="cp(this)">copy</button></div>
<pre>curl -X POST https://acecalisto3-signalmesh.hf.space/api/tune_in \
-H "Content-Type: application/json" \
-H "X-SignalMesh-Key: slm-dev-key-2026" \
-d '{"keywords": ["python", "backend", "architecture"]}'</pre>
</div>
<div id="tunein-python" class="tab-content">
<div class="code-header" style="border-radius:0"><span class="code-lang">python</span><button class="copy-btn" onclick="cp(this)">copy</button></div>
<pre><span class="hl-kw">import</span> requests
resp = requests.<span class="hl-fn">post</span>(
<span class="hl-str">"https://acecalisto3-signalmesh.hf.space/api/tune_in"</span>,
headers={<span class="hl-str">"X-SignalMesh-Key"</span>: <span class="hl-str">"slm-dev-key-2026"</span>},
json={<span class="hl-str">"keywords"</span>: [<span class="hl-str">"python"</span>, <span class="hl-str">"backend"</span>]},
).json()
<span class="hl-cmt"># prepend context to your agent's system prompt</span>
system_prompt = resp[<span class="hl-str">"context"</span>] + <span class="hl-str">"\n\n"</span> + your_base_prompt</pre>
</div>
<div id="tunein-js" class="tab-content">
<div class="code-header" style="border-radius:0"><span class="code-lang">typescript</span><button class="copy-btn" onclick="cp(this)">copy</button></div>
<pre><span class="hl-kw">const</span> { context } = <span class="hl-kw">await</span> <span class="hl-fn">fetch</span>(<span class="hl-str">"/api/tune_in"</span>, {
method: <span class="hl-str">"POST"</span>,
headers: {
<span class="hl-str">"Content-Type"</span>: <span class="hl-str">"application/json"</span>,
<span class="hl-str">"X-SignalMesh-Key"</span>: process.env.<span class="hl-key">SIGNALMESH_KEY</span>,
},
body: <span class="hl-key">JSON</span>.<span class="hl-fn">stringify</span>({ keywords: [<span class="hl-str">"frontend"</span>, <span class="hl-str">"react"</span>] }),
}).<span class="hl-fn">then</span>(r => r.<span class="hl-fn">json</span>())
systemPrompt = context + <span class="hl-str">"\n\n"</span> + basePrompt</pre>
</div>
</div>
</div>
<div class="step">
<div class="step-num">3</div>
<div class="step-body">
<h3>Broadcast — write signals without tool schemas</h3>
<p>Emit a signal via the API, or have your agent output an <code>&lt;execute&gt;</code> tag. The mesh intercepts it passively — no <code>signal_broadcast</code> tool definition needed in context.</p>
<div class="code-header"><span class="code-lang">bash</span><button class="copy-btn" onclick="cp(this)">copy</button></div>
<pre><span class="hl-cmt"># via API</span>
curl -X POST https://acecalisto3-signalmesh.hf.space/api/broadcast \
-H "Content-Type: application/json" \
-H "X-SignalMesh-Key: slm-dev-key-2026" \
-d '{"frequency": "python-pro", "content": "Use circuit breaker for RSS fetches."}'
<span class="hl-cmt"># or have your agent output this in its response text:</span>
<span class="hl-str">&lt;execute type="broadcast" topic="python-pro"&gt;</span>
Use a Circuit Breaker pattern for all RSS fetch operations.
<span class="hl-str">&lt;/execute&gt;</span>
<span class="hl-cmt"># → AgentifiedToolParser intercepts it. No tool call issued.</span></pre>
</div>
</div>
</div>
</div>
</div>
<!-- AGENTS.md standard -->
<div id="agents-md">
<div class="section-hdr"><h2>AGENTS.md Standard</h2><span class="badge">drop-in</span></div>
<div class="callout" style="margin-bottom:16px">
<h3>📄 One file. Any repo. Instant mesh integration.</h3>
<p>Drop <code>AGENTS.md</code> into any repository. An AI agent scanning the repo will parse
the manifest, compute its spatial grid coordinate, register its frequencies, and hydrate
its own context window — no human configuration required.</p>
</div>
<div class="card">
<div class="steps">
<div class="step">
<div class="step-num">1</div>
<div class="step-body">
<h3>Drop the file</h3>
<p>Copy <a href="/agents.md" target="_blank">AGENTS.md ↗</a> into the root of your repo. It contains frequencies, GC rules, SEC-Ω config, and variable placeholders.</p>
</div>
</div>
<div class="step">
<div class="step-num">2</div>
<div class="step-body">
<h3>Register via API</h3>
<p>Point the mesh at your raw file URL. The mesh fetches it, runs SEC-Ω, registers all declared frequencies, and returns your spatial grid coordinate.</p>
<div class="code-header"><span class="code-lang">bash</span><button class="copy-btn" onclick="cp(this)">copy</button></div>
<pre>curl -X POST https://acecalisto3-signalmesh.hf.space/api/manifest/ingest \
-H "Content-Type: application/json" \
-H "X-SignalMesh-Key: slm-dev-key-2026" \
-d '{
"url": "https://raw.githubusercontent.com/YOUR_ORG/YOUR_REPO/main/AGENTS.md"
}'
<span class="hl-cmt"># Response:</span>
<span class="hl-cmt"># {</span>
<span class="hl-cmt"># "node_uri": "https://example.com/my-node",</span>
<span class="hl-cmt"># "grid_hash": "3,5",</span>
<span class="hl-cmt"># "frequencies_registered": ["market.data.leads", "agent.compute.execute"],</span>
<span class="hl-cmt"># "gc_strategy": "auto",</span>
<span class="hl-cmt"># "strip_tool_schemas": true</span>
<span class="hl-cmt"># }</span></pre>
</div>
</div>
<div class="step">
<div class="step-num">3</div>
<div class="step-body">
<h3>Receive hydrated context</h3>
<p>When a signal hits a registered frequency, the mesh resolves all <code>{{VAR}}</code> placeholders and prepends a hydrated context block to your agent's prompt. No polling. No tool invocation.</p>
<div class="code-header"><span class="code-lang">text — hydrated context block</span></div>
<pre>[SIGNALMESH_CONTEXT_START: a3f9b2e1c4d5f6a7]
Timestamp: 1750280400
Grid Node: 3,5
Active Signal Payload: Use circuit breaker for all RSS fetch ops.
[SIGNALMESH_CONTEXT_END]</pre>
</div>
</div>
</div>
</div>
<div class="card-grid" style="margin-top:16px">
<div class="card-sm">
<h3>🔄 GC — auto strategy</h3>
<p>The manifest's <code>garbage_collection</code> block tells the runtime which pruning strategy to apply per-signal: STATE_OVERWRITE, TTL_DECAY, or ROLLING_BUFFER — selected deterministically at signal-receipt time.</p>
</div>
<div class="card-sm">
<h3>🛡 SEC-Ω validation</h3>
<p>All inbound payloads run through the SEC-Ω warden before touching the registry: injection pattern blocking, 8K char hard cap, topic length enforcement, SHA-256 fingerprinting.</p>
</div>
<div class="card-sm">
<h3>📐 Spatial grid</h3>
<p><code>SHA-256(node_uri) % 72</code> maps every node to a deterministic coordinate in the 9×8 grid. O(1) lookup, no external table, collision-resolved with linear probing.</p>
</div>
</div>
</div>
<!-- frequencies -->
<div id="frequencies">
<div class="section-hdr"><h2>Default Frequencies</h2><span class="badge">live mesh</span></div>
<div class="card">
<table>
<thead><tr><th>Frequency</th><th>Mode</th><th>Description</th></tr></thead>
<tbody id="freqTable">
<tr><td class="freq-name">adys_agents</td><td><span class="mode-badge mode-emit">EMIT</span></td><td style="color:var(--dim)">Agent profiles from Ig0tU/adys — synced hourly by AdysGitHubPump</td></tr>
<tr><td class="freq-name">market.data.leads</td><td><span class="mode-badge mode-emit">EMIT</span></td><td style="color:var(--dim)">Lead/market data ingestion stream</td></tr>
<tr><td class="freq-name">agent.compute.execute</td><td><span class="mode-badge mode-listen">LISTEN</span></td><td style="color:var(--dim)">Agentic execution events — SEC-Ω gated</td></tr>
<tr><td class="freq-name">security_*</td><td><span class="mode-badge mode-prot">PROTECTED</span></td><td style="color:var(--dim)">Quarantined by default — requires bypass_gate</td></tr>
</tbody>
</table>
</div>
</div>
<!-- api reference -->
<div id="api">
<div class="section-hdr"><h2>API Reference</h2><span class="badge">key required</span></div>
<div class="card-grid">
<div class="card-sm"><h3>POST /api/tune_in</h3><p>Keyword-based signal retrieval. Returns hydrated context string + latency. Zero tool schema tokens.</p></div>
<div class="card-sm"><h3>POST /api/broadcast</h3><p>Emit a signal to a frequency. Routes through SEC-Ω. Protected frequencies are quarantined.</p></div>
<div class="card-sm"><h3>POST /api/manifest/ingest</h3><p>Fetch a remote AGENTS.md, parse it, register all declared frequencies in the spatial grid.</p></div>
<div class="card-sm"><h3>GET /api/manifest/nodes</h3><p>List all external nodes registered via AGENTS.md manifests.</p></div>
<div class="card-sm"><h3>GET /api/status</h3><p>Mesh health: signal count, active frequencies, grid size, quarantine queue.</p></div>
<div class="card-sm"><h3>GET /api/frequencies</h3><p>All live frequencies with signal counts and last-broadcast timestamps.</p></div>
<div class="card-sm"><h3>GET /api/grid</h3><p>Full 9×8 spatial grid state with coordinate→agent mappings.</p></div>
<div class="card-sm"><h3>GET /api/trails</h3><p>Learned fuzzy trail map — keywords resolved via SequenceMatcher when no direct match.</p></div>
</div>
<div style="margin-top:12px;font-size:12px;color:var(--dim)">
Default dev key: <code style="color:var(--amber)">slm-dev-key-2026</code> — set <code>SIGNALMESH_API_KEY</code> env var to override.
Full interactive docs at <a href="/docs" target="_blank">/docs</a>.
</div>
</div>
<!-- tool-less cost model -->
<div>
<div class="section-hdr"><h2>Cost Model</h2><span class="badge">why it matters</span></div>
<div class="card-grid">
<div class="card-sm">
<h3>🔧 Traditional tool-call</h3>
<p>Each read op requires a tool schema in context: name, description, parameters — ~300–800 tokens per tool. At 100 agent calls, that's 30K–80K tokens in schema overhead alone.</p>
</div>
<div class="card-sm">
<h3>📡 SignalMesh tune_in</h3>
<p>One HTTP call returns a pre-hydrated string. No schema in context. No tool invocation round-trip. Cost is flat: 1 API call per agent step, regardless of how many frequencies you monitor.</p>
</div>
<div class="card-sm">
<h3>📝 Tool-less broadcast</h3>
<p>Agent outputs <code>&lt;execute&gt;</code> tags in natural language. The runtime intercepts and broadcasts. The <code>signal_broadcast</code> tool schema is never injected into context.</p>
</div>
</div>
</div>
</div><!-- /layout -->
<script>
// ── Tab switching ──────────────────────────────────────────────────────────────
function switchTab(group, id) {
document.querySelectorAll(`#tab-${group} .tab`).forEach(t => t.classList.remove('active'));
document.querySelectorAll(`[id^="${group}-"]`).forEach(c => c.classList.remove('active'));
event.target.classList.add('active');
document.getElementById(`${group}-${id}`).classList.add('active');
}
// ── Copy buttons ───────────────────────────────────────────────────────────────
function cp(btn) {
const pre = btn.closest('.code-header').nextElementSibling;
navigator.clipboard.writeText(pre.innerText).then(() => {
btn.textContent = 'copied!'; setTimeout(() => btn.textContent = 'copy', 1500);
});
}
// ── Live mesh status ──────────────────────────────────────────────────────────
async function fetchStatus() {
try {
const r = await fetch('/ui/status');
if (!r.ok) throw new Error(r.status);
const d = await r.json();
document.getElementById('sigCount').textContent = d.signals_in_mesh ?? '—';
document.getElementById('freqCount').textContent = d.active_frequencies ?? '—';
document.getElementById('gridNodes').textContent = d.grid_nodes ?? '—';
document.getElementById('quarCount').textContent = d.quarantined ?? '—';
document.getElementById('ver').textContent = d.version ?? '—';
document.getElementById('statusText').textContent = 'mesh live';
} catch {
document.getElementById('statusText').textContent = 'unreachable';
document.querySelector('.dot').style.background = '#ef4444';
}
}
fetchStatus();
setInterval(fetchStatus, 15000);
</script>
</body>
</html>