Full security platform with web dashboard, 16 Flask blueprints, 26 modules, autonomous AI agent, WebUSB hardware support, and Archon Android companion app. Includes Hash Toolkit, debug console, anti-stalkerware shield, Metasploit/RouterSploit integration, WireGuard VPN, OSINT reconnaissance, and multi-backend LLM support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
278 lines
10 KiB
HTML
278 lines
10 KiB
HTML
{% extends "base.html" %}
|
||
{% block title %}Legendary Creator - AUTARCH{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="page-header" style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap">
|
||
<div>
|
||
<h1>Legendary Creator</h1>
|
||
<p style="margin:0;font-size:0.85rem;color:var(--text-secondary)">
|
||
Generate deeply detailed synthetic personas for testing, simulation, and red-team social engineering drills.
|
||
</p>
|
||
</div>
|
||
<a href="{{ url_for('simulate.index') }}" class="btn btn-sm" style="margin-left:auto">← Simulate</a>
|
||
</div>
|
||
|
||
<!-- Seed Parameters -->
|
||
<div class="section">
|
||
<h2>Legend Seeds <span style="font-size:0.78rem;font-weight:400;color:var(--text-secondary)">(all optional — leave blank for fully random)</span></h2>
|
||
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:0.75rem 1rem">
|
||
|
||
<div class="form-group">
|
||
<label for="seed-gender">Gender</label>
|
||
<select id="seed-gender">
|
||
<option value="">Random</option>
|
||
<option>Male</option>
|
||
<option>Female</option>
|
||
<option>Non-binary</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="seed-nationality">Nationality / Country</label>
|
||
<input type="text" id="seed-nationality" placeholder="e.g. American, British, Korean">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="seed-ethnicity">Ethnicity</label>
|
||
<input type="text" id="seed-ethnicity" placeholder="e.g. Hispanic, White, Japanese-American">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="seed-age">Age or Range</label>
|
||
<input type="text" id="seed-age" placeholder="e.g. 28, or 25–35">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="seed-profession">Profession / Industry</label>
|
||
<input type="text" id="seed-profession" placeholder="e.g. Software engineer, Nurse, Teacher">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="seed-city">City / Region</label>
|
||
<input type="text" id="seed-city" placeholder="e.g. Austin TX, Pacific Northwest">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="seed-education">Education Level</label>
|
||
<select id="seed-education">
|
||
<option value="">Random</option>
|
||
<option>High school diploma</option>
|
||
<option>Some college</option>
|
||
<option>Associate's degree</option>
|
||
<option>Bachelor's degree</option>
|
||
<option>Master's degree</option>
|
||
<option>PhD / Doctorate</option>
|
||
<option>Trade / vocational</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="seed-interests">Interests / Hobbies</label>
|
||
<input type="text" id="seed-interests" placeholder="e.g. hiking, photography, gaming">
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="form-group" style="margin-top:0.25rem">
|
||
<label for="seed-notes">Additional Notes / Constraints</label>
|
||
<input type="text" id="seed-notes"
|
||
placeholder="e.g. Has a dog, grew up in a small town, is introverted, recently divorced">
|
||
</div>
|
||
|
||
<div style="display:flex;gap:0.75rem;align-items:center;margin-top:1rem;flex-wrap:wrap">
|
||
<button id="btn-generate" class="btn btn-primary" onclick="generateLegend()">
|
||
▶ Generate Legend
|
||
</button>
|
||
<button class="btn btn-sm" onclick="clearSeeds()">Clear Seeds</button>
|
||
<span id="gen-status" style="font-size:0.82rem;color:var(--text-secondary)"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Output -->
|
||
<div class="section" id="output-section" style="display:none">
|
||
<div style="display:flex;align-items:center;gap:0.75rem;flex-wrap:wrap;margin-bottom:0.75rem">
|
||
<h2 style="margin:0">Generated Legend</h2>
|
||
<div style="margin-left:auto;display:flex;gap:0.5rem;flex-wrap:wrap">
|
||
<button class="btn btn-sm" onclick="copyLegend()">Copy Text</button>
|
||
<button class="btn btn-sm" onclick="exportLegend()">Export .txt</button>
|
||
<button class="btn btn-sm" onclick="generateLegend()">Regenerate</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Streaming raw output (hidden, used as source for copy/export) -->
|
||
<pre id="legend-raw" style="display:none"></pre>
|
||
|
||
<!-- Rendered display -->
|
||
<div id="legend-display" style="
|
||
font-family: var(--font-mono, monospace);
|
||
font-size: 0.83rem;
|
||
line-height: 1.65;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius);
|
||
padding: 1.25rem 1.5rem;
|
||
max-height: 72vh;
|
||
overflow-y: auto;
|
||
color: var(--text-primary);
|
||
"></div>
|
||
</div>
|
||
|
||
<style>
|
||
/* Section headings inside the legend output */
|
||
#legend-display .leg-section {
|
||
color: var(--simulate, #f59e0b);
|
||
font-weight: 700;
|
||
font-size: 0.88rem;
|
||
letter-spacing: 0.04em;
|
||
margin-top: 1.25em;
|
||
display: block;
|
||
}
|
||
#legend-display .leg-label {
|
||
color: var(--text-secondary);
|
||
font-weight: 600;
|
||
}
|
||
#legend-display .leg-cursor {
|
||
display: inline-block;
|
||
width: 8px;
|
||
height: 1em;
|
||
background: var(--accent);
|
||
animation: blink 0.9s step-end infinite;
|
||
vertical-align: text-bottom;
|
||
margin-left: 1px;
|
||
}
|
||
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:0} }
|
||
</style>
|
||
|
||
<script>
|
||
var _legendText = '';
|
||
var _legendDone = false;
|
||
var _legendReader = null;
|
||
|
||
function generateLegend() {
|
||
var status = document.getElementById('gen-status');
|
||
var btn = document.getElementById('btn-generate');
|
||
var sec = document.getElementById('output-section');
|
||
var disp = document.getElementById('legend-display');
|
||
var raw = document.getElementById('legend-raw');
|
||
|
||
// Abort previous stream if any
|
||
if (_legendReader) { try { _legendReader.cancel(); } catch(e){} _legendReader = null; }
|
||
|
||
_legendText = '';
|
||
_legendDone = false;
|
||
disp.innerHTML = '<span class="leg-cursor"></span>';
|
||
raw.textContent = '';
|
||
sec.style.display = '';
|
||
btn.disabled = true;
|
||
status.textContent = 'Generating…';
|
||
|
||
var seeds = {
|
||
gender: document.getElementById('seed-gender').value,
|
||
nationality: document.getElementById('seed-nationality').value,
|
||
ethnicity: document.getElementById('seed-ethnicity').value,
|
||
age: document.getElementById('seed-age').value,
|
||
profession: document.getElementById('seed-profession').value,
|
||
city: document.getElementById('seed-city').value,
|
||
education: document.getElementById('seed-education').value,
|
||
interests: document.getElementById('seed-interests').value,
|
||
notes: document.getElementById('seed-notes').value,
|
||
};
|
||
|
||
fetch('/simulate/legendary/generate', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify(seeds)
|
||
}).then(function(res) {
|
||
if (!res.ok) { finishGen('HTTP ' + res.status, true); return; }
|
||
_legendReader = res.body.getReader();
|
||
var dec = new TextDecoder();
|
||
var buf = '';
|
||
|
||
function pump() {
|
||
_legendReader.read().then(function(chunk) {
|
||
if (chunk.done) { finishGen(''); return; }
|
||
buf += dec.decode(chunk.value, {stream: true});
|
||
var parts = buf.split('\n\n');
|
||
buf = parts.pop();
|
||
parts.forEach(function(part) {
|
||
var line = part.replace(/^data:\s*/, '').trim();
|
||
if (!line) return;
|
||
try {
|
||
var d = JSON.parse(line);
|
||
if (d.error) { finishGen('Error: ' + d.error, true); return; }
|
||
if (d.token) { appendToken(d.token); }
|
||
if (d.done) { finishGen('Done.'); }
|
||
} catch(e) {}
|
||
});
|
||
pump();
|
||
}).catch(function(e) { finishGen('Stream error: ' + e.message, true); });
|
||
}
|
||
pump();
|
||
}).catch(function(e) { finishGen('Request failed: ' + e.message, true); });
|
||
}
|
||
|
||
function appendToken(token) {
|
||
_legendText += token;
|
||
renderLegend(_legendText, false);
|
||
}
|
||
|
||
function finishGen(msg, isErr) {
|
||
_legendDone = true;
|
||
_legendReader = null;
|
||
document.getElementById('btn-generate').disabled = false;
|
||
document.getElementById('gen-status').textContent = msg || '';
|
||
document.getElementById('legend-raw').textContent = _legendText;
|
||
renderLegend(_legendText, true);
|
||
}
|
||
|
||
/* Render the accumulated text into the display div with light formatting */
|
||
function renderLegend(text, done) {
|
||
var disp = document.getElementById('legend-display');
|
||
|
||
// Convert ## SECTION headings to highlighted spans, bold KEY: labels
|
||
var html = text
|
||
.replace(/&/g, '&')
|
||
.replace(/</g, '<')
|
||
.replace(/>/g, '>')
|
||
// ## SECTION header
|
||
.replace(/^(## .+)$/gm, '<span class="leg-section">$1</span>')
|
||
// Bold "Key Label:" at start of line or after whitespace
|
||
.replace(/^([A-Za-z][A-Za-z /&'-]+:)(?= )/gm, '<span class="leg-label">$1</span>');
|
||
|
||
disp.innerHTML = html + (done ? '' : '<span class="leg-cursor"></span>');
|
||
disp.scrollTop = disp.scrollHeight;
|
||
}
|
||
|
||
function copyLegend() {
|
||
if (!_legendText) return;
|
||
navigator.clipboard.writeText(_legendText).then(function() {
|
||
var btn = event.target;
|
||
btn.textContent = 'Copied!';
|
||
setTimeout(function() { btn.textContent = 'Copy Text'; }, 1500);
|
||
});
|
||
}
|
||
|
||
function exportLegend() {
|
||
if (!_legendText) return;
|
||
var blob = new Blob([_legendText], {type: 'text/plain'});
|
||
var url = URL.createObjectURL(blob);
|
||
var a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = 'legend_' + Date.now() + '.txt';
|
||
a.click();
|
||
URL.revokeObjectURL(url);
|
||
}
|
||
|
||
function clearSeeds() {
|
||
['seed-nationality','seed-ethnicity','seed-age','seed-profession',
|
||
'seed-city','seed-interests','seed-notes'].forEach(function(id) {
|
||
document.getElementById(id).value = '';
|
||
});
|
||
document.getElementById('seed-gender').value = '';
|
||
document.getElementById('seed-education').value = '';
|
||
}
|
||
</script>
|
||
{% endblock %}
|