|
|
<!doctype html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="utf-8"/> |
|
|
<meta name="viewport" content="width=device-width,initial-scale=1"/> |
|
|
<title>Axis · Client</title> |
|
|
<style> |
|
|
:root { |
|
|
--bg:#0b0f17; --fg:#e6eef8; --muted:#a9b7c6; --card:#121825; --accent:#7aa2f7; |
|
|
--ok:#38d39f; --bad:#ff6b6b; --line:#1a2336; --ink:#0e1422; |
|
|
} |
|
|
*{box-sizing:border-box} |
|
|
body{ |
|
|
margin:0;font:15px/1.55 system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,'Helvetica Neue',Arial; |
|
|
background: radial-gradient(1200px 600px at 10% -10%,#122034,transparent),var(--bg); |
|
|
color:var(--fg) |
|
|
} |
|
|
header{padding:36px 20px 10px;max-width:1100px;margin:0 auto} |
|
|
.badge{display:inline-block;padding:4px 10px;border:1px solid #2c3650;border-radius:999px;font-size:12px;color:var(--muted)} |
|
|
h1{margin:12px 0 8px;font-size:28px;letter-spacing:.2px} |
|
|
.sub{color:var(--muted);margin:0 0 18px} |
|
|
.row{display:flex;gap:10px;align-items:center;flex-wrap:wrap;color:var(--muted)} |
|
|
.grid{display:grid;grid-template-columns:1.2fr 1fr;gap:18px;max-width:1100px;padding:0 20px 36px;margin:0 auto} |
|
|
.grid-1{display:grid;grid-template-columns:1fr;gap:18px;max-width:1100px;padding:0 20px 36px;margin:0 auto} |
|
|
.card{background:linear-gradient(180deg,rgba(255,255,255,.02),rgba(255,255,255,0));border:1px solid var(--line);border-radius:14px;padding:16px} |
|
|
.card h3{margin:0 0 8px;font-size:16px} |
|
|
.muted{color:var(--muted)} |
|
|
a{color:#9ec1ff;text-decoration:none} |
|
|
.ok{color:var(--ok)} .bad{color:var(--bad)} |
|
|
pre{position:relative;background:var(--ink);color:#cfe3ff;padding:12px;border-radius:10px;overflow:auto;border:1px solid var(--line);margin:0 0 10px} |
|
|
code,pre,textarea{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace} |
|
|
.btn{background:var(--accent);color:#0b0f17;border:none;padding:10px 14px;border-radius:10px;cursor:pointer;font-weight:600} |
|
|
.btn.secondary{background:transparent;color:#cfe3ff;border:1px solid var(--line)} |
|
|
.btn:disabled{filter:grayscale(.5);opacity:.7;cursor:not-allowed} |
|
|
.btns{display:flex;gap:10px;flex-wrap:wrap} |
|
|
.copy{position:absolute;top:8px;right:8px;background:transparent;border:1px solid var(--line);color:var(--fg);border-radius:8px;padding:6px 8px;cursor:pointer} |
|
|
ul.check{list-style:none;padding:0;margin:8px 0 0} |
|
|
ul.check li{margin:6px 0;padding-left:24px;position:relative} |
|
|
ul.check li:before{content:"✓";position:absolute;left:0;top:0;color:var(--ok)} |
|
|
.pill{display:inline-block;padding:2px 8px;border:1px solid var(--line);border-radius:999px;font-size:12px;color:var(--muted)} |
|
|
footer{max-width:1100px;margin:0 auto 32px;padding:0 20px;color:var(--muted)} |
|
|
.kvs{display:flex;gap:10px;flex-wrap:wrap;margin-top:8px} |
|
|
.kvs .k{padding:6px 10px;border:1px solid var(--line);border-radius:8px;background:rgba(255,255,255,.02)} |
|
|
@media (max-width:1000px){.grid{grid-template-columns:1fr}} |
|
|
|
|
|
|
|
|
figure.shoot{ |
|
|
margin:16px 0 0;padding:10px;border:1px solid var(--line);border-radius:14px; |
|
|
background:linear-gradient(180deg,rgba(255,255,255,.015),rgba(255,255,255,0)); |
|
|
} |
|
|
figure.shoot .shot-wrap{ |
|
|
position:relative;overflow:hidden;border-radius:12px; |
|
|
} |
|
|
figure.shoot img{ |
|
|
display:block;width:100%;height:auto;transform:scale(1.001); |
|
|
transition:transform .35s ease, filter .35s ease; |
|
|
filter:saturate(1.05) contrast(1.02); |
|
|
background:#0c111b; |
|
|
} |
|
|
figure.shoot:hover img{transform:scale(1.02)} |
|
|
figure.shoot figcaption{margin-top:8px;font-size:13px;color:var(--muted)} |
|
|
.chip{display:inline-block;font-size:11px;border:1px solid var(--line);border-radius:999px;padding:2px 8px;margin-right:6px;color:var(--muted)} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<header> |
|
|
<span class="badge">Axis · Agent (Client-First)</span> |
|
|
<h1>TanDev Axis</h1> |
|
|
<p class="sub"> |
|
|
Use the <b>client</b> folder ( <code>/client/</code> ) to plan and run tasks locally with a safe, step-by-step UX. |
|
|
The server <b>does not execute commands</b> — it only plans/assists. |
|
|
</p> |
|
|
<div class="row"> |
|
|
<div>Live status: <span id="live" class="ok">Checking…</span></div> |
|
|
<div>•</div> |
|
|
<div>Model: <span id="modelName" class="pill">—</span></div> |
|
|
<div>•</div> |
|
|
<div><a href="/status">/status</a></div> |
|
|
</div> |
|
|
</header> |
|
|
|
|
|
<section class="grid"> |
|
|
|
|
|
<div class="card"> |
|
|
<h3>Download the client folder</h3> |
|
|
<p class="muted">Grab the folder that includes <code>client.py</code> and <code>key.json</code>.</p> |
|
|
|
|
|
<div class="btns" style="margin:10px 0 6px"> |
|
|
<a id="dlZip" class="btn" target="_blank">📦 Download client.zip</a> |
|
|
<a id="dlPy" class="btn secondary" target="_blank">🧠 client.py</a> |
|
|
<a id="dlKey" class="btn secondary" target="_blank">🔑 key.json</a> |
|
|
<a id="browseClient" class="btn secondary" target="_blank">📁 Browse /client/</a> |
|
|
</div> |
|
|
|
|
|
<div class="kvs" style="margin-top:6px;font-size:14px"> |
|
|
<div class="k"><b>Folder:</b> <code>/client/</code></div> |
|
|
<div class="k"><b>Script:</b> <code>client.py</code></div> |
|
|
<div class="k"><b>Key file:</b> <code>key.json</code></div> |
|
|
</div> |
|
|
|
|
|
<p class="muted" style="margin-top:10px">Prefer CLI? Clone or fetch files directly:</p> |
|
|
<pre style="margin-top:6px"> |
|
|
git clone https://huggingface.co/spaces/tandevllc/axis |
|
|
cd axis/client |
|
|
python3 client.py --server https://tandevllc-axis.hf.space |
|
|
</pre> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="card"> |
|
|
<h3>Why this client?</h3> |
|
|
<ul class="check"> |
|
|
<li>Local-only execution with danger checks (no server-side command execution)</li> |
|
|
<li>Plan preview, dry-run, and per-step confirmation</li> |
|
|
<li>Rich UI + transcripts saved to <code>~/.llama3_agent/sessions/</code></li> |
|
|
<li>Edit the JSON plan in your <code>$EDITOR</code> before running</li> |
|
|
</ul> |
|
|
<p class="muted" style="margin-top:8px">Built by TanDev · Axis.</p> |
|
|
</div> |
|
|
</section> |
|
|
|
|
|
<section class="grid-1"> |
|
|
|
|
|
<div class="card"> |
|
|
<h3>Install & Run</h3> |
|
|
|
|
|
<p class="muted"><b>macOS/Linux</b></p> |
|
|
<pre data-os="unix"><button class="copy" aria-label="Copy">Copy</button><code id="unixCmds"> |
|
|
# 1) Python deps |
|
|
python3 -m pip install --upgrade pip |
|
|
pip install rich click requests |
|
|
|
|
|
# 2) Download client files (buttons above also work) |
|
|
# curl -O {BASE}/client/client.py |
|
|
# curl -O {BASE}/client/key.json |
|
|
# or: |
|
|
# wget {BASE}/client/client.py && wget {BASE}/client/key.json |
|
|
|
|
|
# 3) Run |
|
|
python3 client.py |
|
|
</code></pre> |
|
|
|
|
|
<p class="muted" style="margin-top:8px"><b>Windows (PowerShell)</b></p> |
|
|
<pre data-os="win"><button class="copy" aria-label="Copy">Copy</button><code id="winCmds"> |
|
|
py -m pip install --upgrade pip |
|
|
py -m pip install rich click requests |
|
|
Invoke-WebRequest -Uri "{BASE}/client/client.py" -OutFile "client.py" |
|
|
Invoke-WebRequest -Uri "{BASE}/client/key.json" -OutFile "key.json" |
|
|
py client.py |
|
|
</code></pre> |
|
|
|
|
|
<p class="muted" style="margin-top:10px"> |
|
|
<span class="chip">API key</span> Set <code>LLAMA_API_KEY</code> or edit <code>key.json</code> → <code>{"api_key":"YOUR_KEY"}</code>. |
|
|
In the client: <code>:apikey set</code> / <code>:apikey clear</code>. |
|
|
</p> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="card"> |
|
|
<h3>Usage cheatsheet</h3> |
|
|
<pre><button class="copy" aria-label="Copy">Copy</button><code> |
|
|
# Start |
|
|
python3 client.py |
|
|
|
|
|
# Plan hotkeys: |
|
|
# [a] Execute all [s] Step-by-step [e] Edit plan |
|
|
# [d] Toggle dry-run [c] Cancel |
|
|
|
|
|
# Danger gate: warns on risky shell like `rm -rf /`, `curl | sh`, `mkfs.*`, etc. |
|
|
|
|
|
# Logs: |
|
|
# ~/.llama3_agent/sessions/<timestamp>/ |
|
|
</code></pre> |
|
|
</div> |
|
|
</section> |
|
|
|
|
|
|
|
|
<section class="grid-1"> |
|
|
<div class="card"> |
|
|
<h3>Tutorial: Plan → Review → Execute (client-side)</h3> |
|
|
<p class="muted"> |
|
|
The Axis client asks the server to <b>plan</b>, lets you <b>review</b> the steps, |
|
|
and then <b>runs locally</b> with safety checks. The server <u>never</u> executes shell commands. |
|
|
</p> |
|
|
|
|
|
<ol style="margin:0 0 12px 18px"> |
|
|
<li><b>Launch</b> the client: |
|
|
<pre><button class="copy" aria-label="Copy">Copy</button><code> |
|
|
python3 client.py |
|
|
</code></pre> |
|
|
</li> |
|
|
<li><b>Describe the task</b> (“scaffold a Flask app and run it”). Client calls <code>/infer</code> and shows a plan.</li> |
|
|
<li><b>Review</b> and choose: <b>[a]</b> all • <b>[s]</b> step-by-step • <b>[e]</b> edit • <b>[d]</b> dry-run • <b>[c]</b> cancel.</li> |
|
|
<li><b>Danger gate</b> prompts for risky shell; type <code>proceed</code> to continue.</li> |
|
|
<li><b>Inspect results</b> (stdout/stderr, bytes written, previews). Saved under: |
|
|
<pre><button class="copy" aria-label="Copy">Copy</button><code>~/.llama3_agent/sessions/<timestamp>/</code></pre> |
|
|
</li> |
|
|
</ol> |
|
|
|
|
|
|
|
|
<figure class="shoot"> |
|
|
<div class="shot-wrap"> |
|
|
<img id="shotA" alt="Plan & Execute: Flask scaffold, venv install, local run" loading="lazy"/> |
|
|
</div> |
|
|
<figcaption> |
|
|
<b>Screenshot A — Plan & Execute:</b> planned a Flask scaffold, created files, installed deps in a venv, |
|
|
and ran commands locally with rich stdout/stderr panels. |
|
|
</figcaption> |
|
|
</figure> |
|
|
|
|
|
<figure class="shoot"> |
|
|
<div class="shot-wrap"> |
|
|
<img id="shotB" alt="LLM Respond: pure text response step inside the flow" loading="lazy"/> |
|
|
</div> |
|
|
<figcaption> |
|
|
<b>Screenshot B — LLM Respond:</b> use <code>respond</code>/<code>respond_llm</code> when you just need |
|
|
narrative guidance or an answer—no shell required. |
|
|
</figcaption> |
|
|
</figure> |
|
|
|
|
|
<figure class="shoot"> |
|
|
<div class="shot-wrap"> |
|
|
<img id="shotC" alt="Generate File: wrote gallery.html then explained how to open it" loading="lazy"/> |
|
|
</div> |
|
|
<figcaption> |
|
|
<b>Screenshot C — Generate File:</b> wrote <code>gallery.html</code> with <code>generate_file</code> and |
|
|
confirmed how to open it. Great for content, stubs, and docs. |
|
|
</figcaption> |
|
|
</figure> |
|
|
|
|
|
<p class="muted" style="margin-top:12px"> |
|
|
<b>Meta commands</b>: <code>:server</code>, <code>:apikey set|clear</code>, <code>:history</code>, |
|
|
<code>:open <path></code>, <code>:clear</code>, <code>:help</code>. |
|
|
</p> |
|
|
<p class="muted" style="margin-top:4px"> |
|
|
<b>Tip:</b> Best with Llama-3-8B; TinyLlama on HF is fine for light tasks. Scaffold apps, write articles, |
|
|
manipulate files/dirs, and run OS-aware shell—locally and safely. |
|
|
</p> |
|
|
</div> |
|
|
</section> |
|
|
|
|
|
<footer> |
|
|
<p>Axis · Agent — Client delivery by TanDev. Status: <a href="/status">/status</a></p> |
|
|
</footer> |
|
|
|
|
|
<script> |
|
|
const BASE = window.location.origin; |
|
|
|
|
|
|
|
|
(async function ping(){ |
|
|
try { |
|
|
const r = await fetch('/status'); |
|
|
const j = await r.json(); |
|
|
document.getElementById('live').textContent = r.ok ? 'Running' : ('HTTP '+r.status); |
|
|
if (j && j.model) document.getElementById('modelName').textContent = j.model; |
|
|
} catch { document.getElementById('live').textContent = 'Unknown'; } |
|
|
})(); |
|
|
|
|
|
|
|
|
const HF_TREE_CLIENT = "https://huggingface.co/spaces/tandevllc/axis/tree/main/client/"; |
|
|
document.getElementById('dlZip').href = HF_TREE_CLIENT; |
|
|
document.getElementById('dlPy').href = HF_TREE_CLIENT; |
|
|
document.getElementById('dlKey').href = HF_TREE_CLIENT; |
|
|
document.getElementById('browseClient').href = HF_TREE_CLIENT; |
|
|
|
|
|
|
|
|
function fill(id){ |
|
|
const el = document.getElementById(id); |
|
|
if(!el) return; |
|
|
el.textContent = el.textContent.replaceAll('{BASE}', BASE); |
|
|
} |
|
|
fill('unixCmds'); fill('winCmds'); |
|
|
|
|
|
|
|
|
for (const pre of document.querySelectorAll('pre')) { |
|
|
const btn = pre.querySelector('.copy'); if (!btn) continue; |
|
|
btn.addEventListener('click', async () => { |
|
|
const code = pre.querySelector('code').innerText; |
|
|
try { await navigator.clipboard.writeText(code); btn.textContent='Copied'; } |
|
|
catch { btn.textContent='Failed'; } |
|
|
setTimeout(()=>btn.textContent='Copy', 1200); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const HF_STATIC_BASE = "https://huggingface.co/spaces/tandevllc/axis/resolve/main/static"; |
|
|
const SHOT_A = HF_STATIC_BASE + "/tutorial-plan-flask.png"; |
|
|
const SHOT_B = HF_STATIC_BASE + "/tutorial-llm-respond.png"; |
|
|
const SHOT_C = HF_STATIC_BASE + "/tutorial-generate-file.png"; |
|
|
|
|
|
document.getElementById('shotA').src = SHOT_A; |
|
|
document.getElementById('shotB').src = SHOT_B; |
|
|
document.getElementById('shotC').src = SHOT_C; |
|
|
|
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
|