|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8" /> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> |
|
|
<title>T20-MAS CLI Simulation</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"> |
|
|
<style> |
|
|
body { |
|
|
font-family: 'JetBrains Mono', monospace; |
|
|
} |
|
|
.terminal-container { |
|
|
height: 100vh; |
|
|
background-color: #1e293b; |
|
|
color: #e2e8f0; |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
} |
|
|
.output-area { |
|
|
flex: 1; |
|
|
overflow-y: auto; |
|
|
padding: 1rem; |
|
|
white-space: pre-wrap; |
|
|
font-size: 0.95rem; |
|
|
} |
|
|
.input-line { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
padding: 0.5rem 1rem; |
|
|
border-top: 1px solid #334155; |
|
|
background-color: #1e293b; |
|
|
} |
|
|
.prompt { |
|
|
color: #84cc16; |
|
|
font-weight: bold; |
|
|
} |
|
|
.cursor { |
|
|
display: inline-block; |
|
|
width: 8px; |
|
|
height: 1rem; |
|
|
background-color: #e2e8f0; |
|
|
animation: blink 1s step-end infinite; |
|
|
margin-left: 4px; |
|
|
} |
|
|
@keyframes blink { |
|
|
0%, 100% { opacity: 1; } |
|
|
50% { opacity: 0; } |
|
|
} |
|
|
.command-history { |
|
|
color: #64748b; |
|
|
} |
|
|
.error-text { |
|
|
color: #ef4444; |
|
|
} |
|
|
.success-text { |
|
|
color: #84cc16; |
|
|
} |
|
|
.dir-text { |
|
|
color: #38bdf8; |
|
|
} |
|
|
.file-text { |
|
|
color: #e2e8f0; |
|
|
} |
|
|
.help-header { |
|
|
color: #facc15; |
|
|
font-weight: bold; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-slate-900 text-slate-200"> |
|
|
<div class="terminal-container"> |
|
|
|
|
|
<div id="output" class="output-area"></div> |
|
|
|
|
|
|
|
|
<div class="input-line"> |
|
|
<span class="prompt">t20mas@localhost:~$ </span> |
|
|
<input |
|
|
id="cli-input" |
|
|
type="text" |
|
|
class="bg-transparent text-slate-200 outline-none flex-1 font-mono" |
|
|
autofocus |
|
|
autocomplete="off" |
|
|
spellcheck="false" |
|
|
/> |
|
|
<span id="cursor" class="cursor"></span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const vfs = { |
|
|
'/': { |
|
|
type: 'directory', |
|
|
children: { |
|
|
'home': { |
|
|
type: 'directory', |
|
|
children: { |
|
|
'user': { |
|
|
type: 'directory', |
|
|
children: { |
|
|
'projects': { |
|
|
type: 'directory', |
|
|
children: {} |
|
|
}, |
|
|
'config.json': { |
|
|
type: 'file', |
|
|
content: JSON.stringify({ theme: 'dark', autoSave: true }, null, 2) |
|
|
}, |
|
|
'README.txt': { |
|
|
type: 'file', |
|
|
content: 'Welcome to T20-MAS CLI\nA conceptual multi-agent task system running in-browser.\nTry "agents list" or "help" for commands.' |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}, |
|
|
'sys': { |
|
|
type: 'directory', |
|
|
children: { |
|
|
'agents': { |
|
|
type: 'directory', |
|
|
children: { |
|
|
'orchestrator.agent': { |
|
|
type: 'file', |
|
|
content: 'Role: Plan Generation\nModel: WebLLM-7B\nStatus: Active' |
|
|
}, |
|
|
'researcher.agent': { |
|
|
type: 'file', |
|
|
content: 'Role: Information Retrieval\nModel: WebLLM-7B\nStatus: Idle' |
|
|
}, |
|
|
'writer.agent': { |
|
|
type: 'file', |
|
|
content: 'Role: Content Generation\nModel: WebLLM-7B\nStatus: Idle' |
|
|
} |
|
|
} |
|
|
}, |
|
|
'tasks.db': { |
|
|
type: 'file', |
|
|
content: JSON.stringify([ |
|
|
{ id: 't001', agent: 'orchestrator', status: 'completed', desc: 'Initialize system' } |
|
|
]) |
|
|
}, |
|
|
'artifacts': { |
|
|
type: 'directory', |
|
|
children: {} |
|
|
} |
|
|
} |
|
|
}, |
|
|
'bin': { |
|
|
type: 'directory', |
|
|
children: { |
|
|
't20-cli': { |
|
|
type: 'file', |
|
|
content: 'T20-MAS Command Line Interface v0.1.0 (in-browser)' |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
const state = { |
|
|
currentPath: '/home/user', |
|
|
commandHistory: [], |
|
|
historyIndex: -1, |
|
|
agents: new Map([ |
|
|
['orchestrator', { role: 'Plan Orchestration', status: 'active', tasks: [] }], |
|
|
['researcher', { role: 'Research & Retrieval', status: 'idle', tasks: [] }], |
|
|
['writer', { role: 'Content Generation', status: 'idle', tasks: [] }] |
|
|
]), |
|
|
session: { |
|
|
sessionId: 'sess_' + Math.random().toString(36).substr(2, 9), |
|
|
startTime: new Date(), |
|
|
goal: '', |
|
|
status: 'active' |
|
|
}, |
|
|
artifacts: [] |
|
|
}; |
|
|
|
|
|
|
|
|
function initStorage() { |
|
|
if (!localStorage.getItem('t20mas_vfs')) { |
|
|
localStorage.setItem('t20mas_vfs', JSON.stringify(vfs)); |
|
|
localStorage.setItem('t20mas_state', JSON.stringify(state)); |
|
|
} else { |
|
|
Object.assign(vfs, JSON.parse(localStorage.getItem('t20mas_vfs'))); |
|
|
Object.assign(state, JSON.parse(localStorage.getItem('t20mas_state'))); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function saveState() { |
|
|
localStorage.setItem('t20mas_vfs', JSON.stringify(vfs)); |
|
|
localStorage.setItem('t20mas_state', JSON.stringify(state)); |
|
|
} |
|
|
|
|
|
|
|
|
const outputArea = document.getElementById('output'); |
|
|
const cliInput = document.getElementById('cli-input'); |
|
|
const cursor = document.getElementById('cursor'); |
|
|
|
|
|
|
|
|
function log(message, className = '') { |
|
|
const line = document.createElement('div'); |
|
|
line.className = className; |
|
|
line.innerText = message; |
|
|
outputArea.appendChild(line); |
|
|
outputArea.scrollTop = outputArea.scrollHeight; |
|
|
} |
|
|
|
|
|
function getCurrentDir() { |
|
|
let dir = vfs['/']; |
|
|
const parts = state.currentPath.split('/').filter(p => p); |
|
|
for (const part of parts) { |
|
|
if (dir.children[part] && dir.children[part].type === 'directory') { |
|
|
dir = dir.children[part]; |
|
|
} else { |
|
|
return null; |
|
|
} |
|
|
} |
|
|
return dir; |
|
|
} |
|
|
|
|
|
function resolvePath(path) { |
|
|
if (path.startsWith('/')) return path; |
|
|
return `${state.currentPath}/${path}`.replace(/\/+/g, '/').replace(/\/$/, '') || '/'; |
|
|
} |
|
|
|
|
|
function navigateTo(path) { |
|
|
const fullPath = resolvePath(path); |
|
|
const dir = getDirectoryByPath(fullPath); |
|
|
if (dir) { |
|
|
state.currentPath = fullPath; |
|
|
saveState(); |
|
|
return true; |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
function getDirectoryByPath(path) { |
|
|
if (path === '/') return vfs['/']; |
|
|
let dir = vfs['/']; |
|
|
const parts = path.split('/').filter(p => p); |
|
|
for (const part of parts) { |
|
|
if (dir.children[part] && dir.children[part].type === 'directory') { |
|
|
dir = dir.children[part]; |
|
|
} else { |
|
|
return null; |
|
|
} |
|
|
} |
|
|
return dir; |
|
|
} |
|
|
|
|
|
function getFileByPath(path) { |
|
|
const parts = path.split('/').filter(p => p); |
|
|
const filename = parts.pop(); |
|
|
const dirPath = '/' + parts.join('/'); |
|
|
const dir = getDirectoryByPath(dirPath); |
|
|
if (dir && dir.children[filename] && dir.children[filename].type === 'file') { |
|
|
return dir.children[filename]; |
|
|
} |
|
|
return null; |
|
|
} |
|
|
|
|
|
function createFile(path, content = '') { |
|
|
const parts = path.split('/').filter(p => p); |
|
|
const filename = parts.pop(); |
|
|
const dirPath = '/' + parts.join('/'); |
|
|
const dir = getDirectoryByPath(dirPath); |
|
|
if (dir) { |
|
|
dir.children[filename] = { |
|
|
type: 'file', |
|
|
content: content |
|
|
}; |
|
|
saveState(); |
|
|
return true; |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
function deleteFile(path) { |
|
|
const parts = path.split('/').filter(p => p); |
|
|
const filename = parts.pop(); |
|
|
const dirPath = '/' + parts.join('/'); |
|
|
const dir = getDirectoryByPath(dirPath); |
|
|
if (dir && dir.children[filename]) { |
|
|
delete dir.children[filename]; |
|
|
saveState(); |
|
|
return true; |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
function createDirectory(path) { |
|
|
const parts = path.split('/').filter(p => p); |
|
|
const dirname = parts.pop(); |
|
|
const dirPath = '/' + parts.join('/'); |
|
|
const parentDir = getDirectoryByPath(dirPath); |
|
|
if (parentDir) { |
|
|
parentDir.children[dirname] = { |
|
|
type: 'directory', |
|
|
children: {} |
|
|
}; |
|
|
saveState(); |
|
|
return true; |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
|
|
|
const commands = { |
|
|
help: () => { |
|
|
log('T20-MAS CLI - Available Commands:', 'help-header'); |
|
|
log(''); |
|
|
log('System & Navigation:'); |
|
|
log(' help - Show this help'); |
|
|
log(' clear - Clear the screen'); |
|
|
log(' exit - Exit the system'); |
|
|
log(' history - Show command history'); |
|
|
log(' pwd - Print working directory'); |
|
|
log(' ls [dir] - List directory contents'); |
|
|
log(' cd <dir> - Change directory'); |
|
|
log(' cat <file> - Display file content'); |
|
|
log(' mkdir <dir> - Create a directory'); |
|
|
log(' touch <file> - Create an empty file'); |
|
|
log(' rm <file> - Delete a file'); |
|
|
log(' echo <text> - Print text'); |
|
|
log(''); |
|
|
log('Multi-Agent System (T20-MAS):'); |
|
|
log(' agents list - List all agents'); |
|
|
log(' agents status - Show agent statuses'); |
|
|
log(' task run <goal> - Start a new task (e.g., task run write a poem about AI)'); |
|
|
log(' task list - List current tasks'); |
|
|
log(' artifacts list - List generated artifacts'); |
|
|
log(' session info - Show current session info'); |
|
|
}, |
|
|
|
|
|
clear: () => { |
|
|
outputArea.innerHTML = ''; |
|
|
}, |
|
|
|
|
|
exit: () => { |
|
|
log('Shutting down T20-MAS...'); |
|
|
setTimeout(() => { |
|
|
log('Session terminated. See you next time.'); |
|
|
}, 800); |
|
|
}, |
|
|
|
|
|
history: () => { |
|
|
state.commandHistory.forEach((cmd, i) => { |
|
|
log(`${i + 1} ${cmd}`, 'command-history'); |
|
|
}); |
|
|
}, |
|
|
|
|
|
pwd: () => { |
|
|
log(state.currentPath); |
|
|
}, |
|
|
|
|
|
ls: (args) => { |
|
|
const target = args[0] ? resolvePath(args[0]) : state.currentPath; |
|
|
const dir = getDirectoryByPath(target); |
|
|
if (!dir) { |
|
|
log(`ls: cannot access '${target}': No such directory`, 'error-text'); |
|
|
return; |
|
|
} |
|
|
const items = Object.keys(dir.children).sort(); |
|
|
items.forEach(item => { |
|
|
const isDir = dir.children[item].type === 'directory'; |
|
|
log(isDir ? `${item}/` : item, isDir ? 'dir-text' : 'file-text'); |
|
|
}); |
|
|
}, |
|
|
|
|
|
cd: (args) => { |
|
|
if (!args[0]) { |
|
|
log('cd: missing operand'); |
|
|
return; |
|
|
} |
|
|
const dir = args[0]; |
|
|
if (dir === '..') { |
|
|
const parts = state.currentPath.split('/'); |
|
|
parts.pop(); |
|
|
state.currentPath = parts.join('/') || '/'; |
|
|
saveState(); |
|
|
return; |
|
|
} |
|
|
const success = navigateTo(dir); |
|
|
if (!success) { |
|
|
log(`cd: ${dir}: No such directory`, 'error-text'); |
|
|
} |
|
|
}, |
|
|
|
|
|
cat: (args) => { |
|
|
if (!args[0]) { |
|
|
log('cat: missing file operand'); |
|
|
return; |
|
|
} |
|
|
const file = getFileByPath(resolvePath(args[0])); |
|
|
if (file) { |
|
|
log(file.content); |
|
|
} else { |
|
|
log(`cat: ${args[0]}: No such file`, 'error-text'); |
|
|
} |
|
|
}, |
|
|
|
|
|
mkdir: (args) => { |
|
|
if (!args[0]) { |
|
|
log('mkdir: missing operand'); |
|
|
return; |
|
|
} |
|
|
const success = createDirectory(resolvePath(args[0])); |
|
|
if (!success) { |
|
|
log(`mkdir: cannot create directory '${args[0]}': No such directory`, 'error-text'); |
|
|
} else { |
|
|
log(`Directory '${args[0]}' created.`); |
|
|
} |
|
|
}, |
|
|
|
|
|
touch: (args) => { |
|
|
if (!args[0]) { |
|
|
log('touch: missing operand'); |
|
|
return; |
|
|
} |
|
|
const path = resolvePath(args[0]); |
|
|
const parts = path.split('/'); |
|
|
const filename = parts.pop(); |
|
|
const dirPath = '/' + parts.join('/'); |
|
|
if (!getDirectoryByPath(dirPath)) { |
|
|
log(`touch: cannot create '${args[0]}': No such directory`, 'error-text'); |
|
|
return; |
|
|
} |
|
|
if (!getFileByPath(path)) { |
|
|
createFile(path, ''); |
|
|
log(`File '${args[0]}' created.`); |
|
|
} |
|
|
}, |
|
|
|
|
|
rm: (args) => { |
|
|
if (!args[0]) { |
|
|
log('rm: missing operand'); |
|
|
return; |
|
|
} |
|
|
const success = deleteFile(resolvePath(args[0])); |
|
|
if (success) { |
|
|
log(`File '${args[0]}' deleted.`); |
|
|
} else { |
|
|
log(`rm: cannot remove '${args[0]}': No such file`, 'error-text'); |
|
|
} |
|
|
}, |
|
|
|
|
|
echo: (args) => { |
|
|
log(args.join(' ')); |
|
|
}, |
|
|
|
|
|
|
|
|
agents: (args) => { |
|
|
if (args[0] === 'list') { |
|
|
log('Agents in T20-MAS:', 'help-header'); |
|
|
state.agents.forEach((profile, id) => { |
|
|
log(` ${id} [${profile.role}] - ${profile.status}`); |
|
|
}); |
|
|
} else if (args[0] === 'status') { |
|
|
log('Agent Status:', 'help-header'); |
|
|
state.agents.forEach((profile, id) => { |
|
|
log(` ${id}: ${profile.status}`); |
|
|
}); |
|
|
} else { |
|
|
log("Agents subcommands: 'list', 'status'"); |
|
|
} |
|
|
}, |
|
|
|
|
|
task: (args) => { |
|
|
if (args[0] === 'run' && args.length > 1) { |
|
|
const goal = args.slice(1).join(' '); |
|
|
state.session.goal = goal; |
|
|
log(`🎯 Starting new task: "${goal}"`, 'success-text'); |
|
|
log('🧠 Orchestrator: Generating plan...', 'dir-text'); |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
log('✅ Plan created: [Research] → [Write] → [Review]'); |
|
|
log('🔄 Assigning tasks to agents...'); |
|
|
|
|
|
state.agents.get('orchestrator').status = 'busy'; |
|
|
state.agents.get('researcher').status = 'busy'; |
|
|
state.agents.get('writer').status = 'busy'; |
|
|
|
|
|
saveState(); |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
log('🔍 Researcher: Gathering data on "' + goal + '"...'); |
|
|
setTimeout(() => { |
|
|
log('✅ Research completed. Data cached.'); |
|
|
state.agents.get('researcher').status = 'idle'; |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
log('✍️ Writer: Generating content...'); |
|
|
setTimeout(() => { |
|
|
log('✅ Content generated.'); |
|
|
const artifact = { |
|
|
id: 'art_' + Date.now(), |
|
|
name: 'output.txt', |
|
|
content: `Generated document on: ${goal}\n\nThis is a simulation of a multi-agent system running entirely in your browser. No backend. No server. Just JavaScript.\n\nGenerated by T20-MAS v0.1 at ${new Date().toISOString()}`, |
|
|
type: 'text/plain' |
|
|
}; |
|
|
state.artifacts.push(artifact); |
|
|
const artifactPath = `/sys/artifacts/${artifact.id}.txt`; |
|
|
createFile(artifactPath, artifact.content); |
|
|
log(`📎 Artifact saved: ${artifactPath}`); |
|
|
state.agents.get('writer').status = 'idle'; |
|
|
state.session.status = 'completed'; |
|
|
log('🎉 Task completed successfully!', 'success-text'); |
|
|
saveState(); |
|
|
}, 1500); |
|
|
}, 1000); |
|
|
}, 1200); |
|
|
}, 1000); |
|
|
}, 800); |
|
|
} else if (args[0] === 'list') { |
|
|
log('Active Tasks:'); |
|
|
if (state.session.goal) { |
|
|
log(` ID: ${state.session.sessionId}`); |
|
|
log(` Goal: ${state.session.goal}`); |
|
|
log(` Status: ${state.session.status}`); |
|
|
} else { |
|
|
log(' No active tasks.'); |
|
|
} |
|
|
} else { |
|
|
log("Task commands: 'run <goal>', 'list'"); |
|
|
} |
|
|
}, |
|
|
|
|
|
artifacts: (args) => { |
|
|
if (args[0] === 'list') { |
|
|
log('Generated Artifacts:', 'help-header'); |
|
|
if (state.artifacts.length === 0) { |
|
|
log(' No artifacts generated yet.'); |
|
|
} else { |
|
|
state.artifacts.forEach(art => { |
|
|
log(` ${art.id}.txt (${art.type})`); |
|
|
}); |
|
|
} |
|
|
} |
|
|
}, |
|
|
|
|
|
'session info': () => { |
|
|
log('Session Info:', 'help-header'); |
|
|
log(` ID: ${state.session.sessionId}`); |
|
|
log(` Started: ${state.session.startTime.toLocaleString()}`); |
|
|
log(` Goal: ${state.session.goal || 'N/A'}`); |
|
|
log(` Status: ${state.session.status}`); |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
cliInput.addEventListener('keydown', (e) => { |
|
|
if (e.key === 'ArrowUp') { |
|
|
e.preventDefault(); |
|
|
if (state.historyIndex < state.commandHistory.length - 1) { |
|
|
state.historyIndex++; |
|
|
cliInput.value = state.commandHistory[state.commandHistory.length - 1 - state.historyIndex]; |
|
|
} |
|
|
} else if (e.key === 'ArrowDown') { |
|
|
e.preventDefault(); |
|
|
if (state.historyIndex > 0) { |
|
|
state.historyIndex--; |
|
|
cliInput.value = state.historyHistory.length > 0 |
|
|
? state.commandHistory[state.commandHistory.length - 1 - state.historyIndex] |
|
|
: ''; |
|
|
} else if (state.historyIndex === 0) { |
|
|
state.historyIndex = -1; |
|
|
cliInput.value = ''; |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
cliInput.addEventListener('keydown', (e) => { |
|
|
if (e.key === 'Enter') { |
|
|
const command = cliInput.value.trim(); |
|
|
cliInput.value = ''; |
|
|
state.historyIndex = -1; |
|
|
|
|
|
|
|
|
if (command) { |
|
|
state.commandHistory.push(command); |
|
|
if (state.commandHistory.length > 50) { |
|
|
state.commandHistory.shift(); |
|
|
} |
|
|
saveState(); |
|
|
} |
|
|
|
|
|
|
|
|
log(`t20mas@localhost:${state.currentPath}$ ${command}`); |
|
|
|
|
|
if (!command) return; |
|
|
|
|
|
const [cmd, ...args] = command.split(' '); |
|
|
const fullCommand = commands[`${cmd} ${args[0]}`] ? `${cmd} ${args[0]}` : cmd; |
|
|
const commandArgs = commands[`${cmd} ${args[0]}`] ? args.slice(1) : args; |
|
|
|
|
|
if (commands[fullCommand]) { |
|
|
commands[fullCommand](commandArgs); |
|
|
} else if (cmd) { |
|
|
log(`Command not found: ${cmd}. Type 'help' for available commands.`, 'error-text'); |
|
|
} |
|
|
|
|
|
|
|
|
if (cmd === 'exit') { |
|
|
setTimeout(() => { |
|
|
log('🌐 T20-MAS shutdown complete. Reloading in 2s...'); |
|
|
setTimeout(() => { |
|
|
window.location.reload(); |
|
|
}, 2000); |
|
|
}, 500); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
window.addEventListener('load', () => { |
|
|
cliInput.focus(); |
|
|
initStorage(); |
|
|
log(''); |
|
|
log('████████╗ ██████╗ ██╗ ██╗██████╗ ██╗████████╗', 'success-text'); |
|
|
log('╚══██╔══╝██╔═══██╗██║ ██║██╔══██╗██║╚══██╔══╝', 'success-text'); |
|
|
log(' ██║ ██║ ██║██║ ██║██████╔╝██║ ██║', 'success-text'); |
|
|
log(' ██║ ██║ ██║██║ ██║██╔══██╗██║ ██║', 'success-text'); |
|
|
log(' ██║ ╚██████╔╝╚██████╔╝██║ ██║██║ ██║', 'success-text'); |
|
|
log(' ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝', 'success-text'); |
|
|
log(''); |
|
|
log('Welcome to T20-MAS CLI v0.1 (In-Browser Multi-Agent System)', 'dir-text'); |
|
|
log('Type "help" to get started. "agents list" to see agents.', 'command-history'); |
|
|
log(''); |
|
|
}); |
|
|
</script> |
|
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-qwensite.hf.space/logo.svg" alt="qwensite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-qwensite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >QwenSite</a> - 🧬 <a href="https://enzostvs-qwensite.hf.space?remix=dokii/cli-sim-1" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
|
</html> |