| <!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> |