1129 lines
55 KiB
JavaScript
1129 lines
55 KiB
JavaScript
/* ===================================================
|
||
JAESWIFT.XYZ — JAE-AI CLI Easter Egg
|
||
Local pseudo-shell handled in the JAE-AI chat panel.
|
||
Intercepts messages starting with '/'.
|
||
=================================================== */
|
||
(function () {
|
||
'use strict';
|
||
|
||
const MAX_HISTORY = 50;
|
||
const HIST_KEY = 'jaeCliHist';
|
||
let cmdHistory = [];
|
||
let devMode = false;
|
||
let sessionCmdCount = 0;
|
||
let sessionPerCmd = {};
|
||
let mode = null; // null | 'ssh' | 'adventure' | 'ttyper'
|
||
let modeState = {};
|
||
const ACH_KEY = 'jae-cli-achievements-v1';
|
||
const STATS_KEY = 'jae-cli-stats-v1';
|
||
const TTYPER_KEY = 'jae-cli-ttyper-best-v1';
|
||
let achievements = new Set();
|
||
let allTimeStats = { total: 0, perCmd: {} };
|
||
try {
|
||
const ar = localStorage.getItem(ACH_KEY);
|
||
if (ar) { const a = JSON.parse(ar); if (Array.isArray(a)) achievements = new Set(a); }
|
||
const sr = localStorage.getItem(STATS_KEY);
|
||
if (sr) allTimeStats = Object.assign({ total: 0, perCmd: {} }, JSON.parse(sr));
|
||
} catch (e) {}
|
||
function saveAch() { try { localStorage.setItem(ACH_KEY, JSON.stringify([...achievements])); } catch(e){} }
|
||
function saveStats() { try { localStorage.setItem(STATS_KEY, JSON.stringify(allTimeStats)); } catch(e){} }
|
||
function unlockAchievement(cmd) {
|
||
if (achievements.has(cmd)) return false;
|
||
achievements.add(cmd);
|
||
saveAch();
|
||
try {
|
||
const t = document.createElement('div');
|
||
t.className = 'jae-cli-toast';
|
||
t.textContent = '🏆 NEW: Discovered /' + cmd + ' command';
|
||
Object.assign(t.style, {
|
||
position: 'fixed', bottom: '24px', right: '24px', zIndex: '9999',
|
||
background: 'var(--bg-panel, #0d1810)', border: '1px solid var(--warning, #c9a227)',
|
||
color: 'var(--warning, #c9a227)', padding: '10px 16px',
|
||
fontFamily: 'var(--font-mono, monospace)', fontSize: '12px', letterSpacing: '1px',
|
||
boxShadow: '0 0 18px rgba(201,162,39,0.45)', transition: 'opacity 0.4s',
|
||
borderRadius: '2px', pointerEvents: 'none',
|
||
});
|
||
document.body.appendChild(t);
|
||
setTimeout(() => { t.style.opacity = '0'; }, 3400);
|
||
setTimeout(() => { t.remove(); }, 4000);
|
||
} catch (e) {}
|
||
return true;
|
||
}
|
||
|
||
// Load history
|
||
try {
|
||
const raw = localStorage.getItem(HIST_KEY);
|
||
if (raw) cmdHistory = JSON.parse(raw) || [];
|
||
} catch (e) {}
|
||
|
||
function saveHistory() {
|
||
try {
|
||
localStorage.setItem(HIST_KEY, JSON.stringify(cmdHistory.slice(-MAX_HISTORY)));
|
||
} catch (e) {}
|
||
}
|
||
|
||
// ─── Virtual FS ──────────────────────────────────
|
||
const VFS = {
|
||
'about.md': [
|
||
'# JAESWIFT // OPERATOR FILE',
|
||
'',
|
||
'Codename: JAE',
|
||
'Location: Manchester, United Kingdom',
|
||
'Operator since: 2008',
|
||
'',
|
||
'Full-stack engineer, cyber-curious, builds things.',
|
||
'Specialisms: Node / Python / Linux / nginx / Solana',
|
||
'',
|
||
'This site is my HUD. Built by hand, deployed by git, watched by me.',
|
||
].join('\n'),
|
||
|
||
'projects.json': JSON.stringify({
|
||
'jaeswift.xyz': 'This site — military CRT HUD, Flask+vanilla',
|
||
'matty.lol': 'Crypto/Solana toolkit (Node/Express)',
|
||
'contraband-depot': 'FMHY mirror, 24 categories, 16k+ links',
|
||
'recon-awesomelist': '28 sectors / 135k items, weekly sync',
|
||
'unredacted-vault': '114+ declassified PDFs (MOD/CIA/etc)',
|
||
'radar': 'RSS aggregator (HN/Reddit/Lobsters)',
|
||
'sitrep': 'Daily AI briefing generator',
|
||
'telemetry': 'Live ops command centre'
|
||
}, null, 2),
|
||
|
||
'skills.md': [
|
||
'# FIELD LOADOUT',
|
||
'',
|
||
'## LANGS',
|
||
' - Python, JavaScript/Node, TypeScript, Rust, Bash, SQL',
|
||
'',
|
||
'## STACKS',
|
||
' - Flask, Express, React, Solana/Anchor, Docker, systemd',
|
||
'',
|
||
'## INFRA',
|
||
' - nginx, Debian, PostgreSQL, Redis, cron, fail2ban',
|
||
'',
|
||
'## OPS',
|
||
' - VPS ops, CI/CD via Gitea, observability, hardening'
|
||
].join('\n'),
|
||
|
||
'secrets.enc': [
|
||
'',
|
||
' ░█████╗░░█████╗░░█████╗░███████╗░██████╗░██████╗',
|
||
' ██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔════╝██╔════╝',
|
||
' ███████║██║░░╚═╝██║░░╚═╝█████╗░░╚█████╗░╚█████╗░',
|
||
' ██╔══██║██║░░██╗██║░░██╗██╔══╝░░░╚═══██╗░╚═══██╗',
|
||
' ██║░░██║╚█████╔╝╚█████╔╝███████╗██████╔╝██████╔╝',
|
||
' ╚═╝░░╚═╝░╚════╝░░╚════╝░╚══════╝╚═════╝░╚═════╝░',
|
||
'',
|
||
' ▓▒░ ACCESS DENIED ░▒▓',
|
||
' File encrypted with AES-4096-QUANTUM-ROT13.',
|
||
' Authorisation required. >> /sudo',
|
||
].join('\n'),
|
||
|
||
'mascot.txt': [
|
||
' ___ ___',
|
||
' ,-" `. ," `-,',
|
||
' / _ \\_____/ _ \\',
|
||
' | (o) ` ` (o) |',
|
||
' \\ \\_._ _._/ /',
|
||
' `._ `----\' _.\'',
|
||
' `~---------~\'',
|
||
' // JAE MASCOT //',
|
||
' Black dragon, on duty.'
|
||
].join('\n'),
|
||
|
||
'whoami.txt': '$USER — check /whoami for your details.'
|
||
};
|
||
|
||
// ─── Helpers ─────────────────────────────────────
|
||
function fmt(lines) { return Array.isArray(lines) ? lines.join('\n') : lines; }
|
||
|
||
function pad(n, len) {
|
||
const s = String(n);
|
||
return s.length >= len ? s : ' '.repeat(len - s.length) + s;
|
||
}
|
||
|
||
async function fakeProgress(label, steps, delay) {
|
||
const out = [];
|
||
for (let i = 1; i <= steps; i++) {
|
||
const pct = Math.floor((i / steps) * 100);
|
||
const filled = '█'.repeat(Math.floor(pct / 5));
|
||
const empty = '░'.repeat(20 - filled.length);
|
||
out.push(`${label} [${filled}${empty}] ${pct}%`);
|
||
// intentionally not awaited — output comes as a block
|
||
}
|
||
return out;
|
||
}
|
||
|
||
// ─── Commands ────────────────────────────────────
|
||
const commands = {};
|
||
|
||
commands.help = function () {
|
||
const base = [
|
||
'JAE-AI TERMINAL — available commands:',
|
||
'',
|
||
'— SYSTEM —',
|
||
' /help show this help',
|
||
' /ls list virtual files',
|
||
' /cat <file> print file contents',
|
||
' /whoami show your visitor fingerprint',
|
||
' /uptime show site uptime',
|
||
' /uname kernel/system info',
|
||
' /date current date/time',
|
||
' /ping <host> fake ping sweep',
|
||
' /sudo <cmd> attempt privilege escalation',
|
||
' /rm -rf / don\'t do it',
|
||
' /clear clear the chat',
|
||
' /exit try to leave',
|
||
' /history show last commands',
|
||
' /neofetch system summary ASCII',
|
||
'',
|
||
'— FUN —',
|
||
' /hack <target> hollywood hacking simulator',
|
||
' /matrix toggle matrix rain',
|
||
' /cmatrix enhanced matrix (sound + ramp)',
|
||
' /fortune [-o] random quip (-o for offensive)',
|
||
' /cowsay <msg> a cow says something',
|
||
' /figlet <text> big ASCII banner (<=20 chars)',
|
||
'',
|
||
'— MODES —',
|
||
' /ssh <user>@<host> enter fake SSH session',
|
||
' /adventure cyberpunk text adventure',
|
||
' /ttyper typing speed test',
|
||
'',
|
||
'— EFFECTS (sitewide visual overlays) —',
|
||
' /crt /vhs /glitch /redalert /invert /blueprint /typewriter',
|
||
' /gravity /earthquake /lowgravity /melt /shuffle',
|
||
' /rain /snow /fog /night /underwater',
|
||
' /dimensions /portal /retro <1995|2005|2015|now>',
|
||
' /partymode /ghostmode /quantum /sneak /hacker',
|
||
' /matrix /cmatrix matrix rain overlays',
|
||
' /effects show active · /effects off · /effects all (chaos)',
|
||
'',
|
||
'— STATS —',
|
||
' /achievements your unlocked commands',
|
||
' /leaderboard session & all-time stats',
|
||
];
|
||
if (devMode) {
|
||
base.push('');
|
||
base.push(' [DEVELOPER MODE ACTIVE]');
|
||
base.push(' /api list real API endpoints');
|
||
base.push(' /curl <url> actually fetch a URL');
|
||
base.push(' /geo show GEO INTEL from telemetry');
|
||
}
|
||
base.push('');
|
||
base.push('Tip: Press Escape (or Ctrl+Shift+B) as a boss key to hide everything.');
|
||
base.push('Anything not prefixed with / goes to JAE-AI.');
|
||
return fmt(base);
|
||
};
|
||
|
||
commands.ls = function () {
|
||
return fmt([
|
||
'total 6',
|
||
'-rw-r--r-- 1 jae jae 412 apr 20 01:24 about.md',
|
||
'-rw-r--r-- 1 jae jae 528 apr 20 01:24 projects.json',
|
||
'-rw-r--r-- 1 jae jae 394 apr 20 01:24 skills.md',
|
||
'-r-------- 1 jae jae 999 apr 20 01:24 secrets.enc',
|
||
'-rw-r--r-- 1 jae jae 210 apr 20 01:24 mascot.txt',
|
||
'-rw-r--r-- 1 jae jae 42 apr 20 01:24 whoami.txt'
|
||
]);
|
||
};
|
||
|
||
commands.cat = function (args) {
|
||
const name = args[0];
|
||
if (!name) return 'cat: missing operand — try /cat about.md';
|
||
if (VFS[name] != null) return VFS[name];
|
||
return `cat: ${name}: No such file or directory`;
|
||
};
|
||
|
||
commands.whoami = async function () {
|
||
try {
|
||
const r = await fetch('/api/visitor/scan');
|
||
if (!r.ok) throw new Error('scan unavailable');
|
||
const d = await r.json();
|
||
return fmt([
|
||
`operator: ${d.ip_masked}`,
|
||
`geolocation: ${d.city ? d.city + ', ' : ''}${d.country} ${d.country_flag || ''}`,
|
||
`network: ${d.isp}`,
|
||
`browser: ${d.browser} ${d.browser_version || ''}`,
|
||
`os: ${d.os} ${d.os_version || ''}`,
|
||
`device: ${d.device}`,
|
||
`threat: ${d.threat_level} — ${d.threat_reason}`,
|
||
'',
|
||
'you are a visitor. welcome.'
|
||
]);
|
||
} catch (e) {
|
||
return 'scan service unavailable. try again in a moment.';
|
||
}
|
||
};
|
||
|
||
commands.uptime = async function () {
|
||
try {
|
||
const r = await fetch('/api/telemetry/overview');
|
||
const d = await r.json();
|
||
const up = (d.system && d.system.uptime) || d.uptime || 'unknown';
|
||
const load = (d.system && d.system.load_1) || '—';
|
||
const users = 'many';
|
||
const now = new Date().toLocaleTimeString('en-GB');
|
||
return fmt([
|
||
` ${now} up ${up}, ${users} users, load average: ${load}`,
|
||
` HQ: Manchester // jaeswift-api.service ● ONLINE`
|
||
]);
|
||
} catch (e) {
|
||
return `up since boot — tired but operational`;
|
||
}
|
||
};
|
||
|
||
commands.uname = function (args) {
|
||
if (args[0] === '-a') {
|
||
return 'Linux jaeswift 6.1.0-jaeswift #1 SMP PREEMPT_DYNAMIC 2026-04-20 x86_64 GNU/Linux';
|
||
}
|
||
return 'Linux';
|
||
};
|
||
|
||
commands.date = function () {
|
||
return new Date().toUTCString();
|
||
};
|
||
|
||
commands.ping = function (args) {
|
||
const host = args[0] || 'jaeswift.xyz';
|
||
const out = [`PING ${host} (10.0.0.1) 56(84) bytes of data.`];
|
||
for (let i = 0; i < 4; i++) {
|
||
const t = (10 + Math.random() * 40).toFixed(2);
|
||
out.push(`64 bytes from ${host}: icmp_seq=${i+1} ttl=54 time=${t} ms`);
|
||
}
|
||
out.push('');
|
||
out.push(`--- ${host} ping statistics ---`);
|
||
out.push('4 packets transmitted, 4 received, 0% packet loss, time 3006ms');
|
||
return fmt(out);
|
||
};
|
||
|
||
commands.sudo = function (args) {
|
||
if (!args.length) return 'usage: /sudo <command>';
|
||
return fmt([
|
||
`[sudo] password for operator: ****`,
|
||
``,
|
||
`sudo: ACCESS DENIED — nice try.`,
|
||
`This incident will be reported... actually, no it won\'t.`
|
||
]);
|
||
};
|
||
|
||
commands.rm = function (args) {
|
||
if (args.join(' ') === '-rf /' || args.join(' ').startsWith('-rf /')) {
|
||
return fmt([
|
||
' __ __',
|
||
' / \\_/ \\ 💥 FORMATTING /dev/sda ...',
|
||
' \\_( • )_/ 💥 WIPING /home ...',
|
||
' //( )\\\\ 💥 DELETING /etc/passwd ...',
|
||
'',
|
||
' ERROR: The file system is screaming.',
|
||
' ███████████░░░░░░░░░ 58%',
|
||
'',
|
||
' ... just kidding.',
|
||
' SYSTEM RESTORED FROM BACKUP. Nice try, operator.'
|
||
]);
|
||
}
|
||
return `rm: refuse to do whatever that is`;
|
||
};
|
||
|
||
commands.hack = function (args) {
|
||
const target = args.join(' ') || 'the-gibson';
|
||
return fmt([
|
||
`>>> TARGETING ${target}...`,
|
||
`>>> BYPASSING FIREWALL... [████████████░░░░] 72%`,
|
||
`>>> CRACKING PASSWORD... [████████████████] 100%`,
|
||
`>>> INJECTING PAYLOAD... [████████████████] 100%`,
|
||
`>>> COVERING TRACKS... [████████████████] 100%`,
|
||
``,
|
||
`>>> CONNECTION ESTABLISHED.`,
|
||
`>>> ACCESS GRANTED TO ${target.toUpperCase()}.`,
|
||
``,
|
||
`... just kidding lol. nothing was hacked.`,
|
||
`If you came here looking for real offsec — go read /cat skills.md and touch grass.`
|
||
]);
|
||
};
|
||
|
||
commands.matrix = function () {
|
||
if (!window.__jaeEffects) return 'effects module not loaded.';
|
||
const on = window.__jaeEffects.toggle('matrix');
|
||
return on ? 'Matrix rain enabled. Follow the white rabbit.'
|
||
: 'Matrix rain disabled. Back to the desert of the real.';
|
||
};
|
||
|
||
commands.clear = function () {
|
||
const cm = document.getElementById('chatMessages');
|
||
if (cm) cm.innerHTML = '';
|
||
return null; // nothing to print
|
||
};
|
||
|
||
commands.exit = function () {
|
||
return fmt([
|
||
'logout',
|
||
'',
|
||
'NICE TRY. You don\'t leave that easily.',
|
||
'JAE-AI resumes normal operation.'
|
||
]);
|
||
};
|
||
|
||
const FORTUNES = [
|
||
'SITREP: quiet on the wire. unusual.',
|
||
'SITREP: nothing is fine. everything is broken. proceed.',
|
||
'SITREP: coffee low. deploy anyway.',
|
||
'SITREP: the logs know. the logs always know.',
|
||
'SITREP: git status is a lifestyle.',
|
||
'SITREP: ship it friday. regret it monday.',
|
||
'SITREP: 99 bugs on the wall, patch one down, 117 on the wall.',
|
||
'SITREP: if it works on the VPS, the VPS is now production.',
|
||
'SITREP: the only safe deploy is no deploy.',
|
||
'SITREP: trust nothing. grep everything.',
|
||
];
|
||
|
||
commands.fortune = function () {
|
||
return FORTUNES[Math.floor(Math.random() * FORTUNES.length)];
|
||
};
|
||
|
||
commands.history = function () {
|
||
if (!cmdHistory.length) return '(no history yet)';
|
||
const recent = cmdHistory.slice(-10);
|
||
return recent.map((c, i) => ` ${pad(cmdHistory.length - recent.length + i + 1, 4)} ${c}`).join('\n');
|
||
};
|
||
|
||
commands.neofetch = function () {
|
||
const cc = (navigator.language || 'en').toUpperCase();
|
||
const ua = navigator.userAgent || '';
|
||
let browser = 'Unknown';
|
||
if (/firefox/i.test(ua)) browser = 'Firefox';
|
||
else if (/edg/i.test(ua)) browser = 'Edge';
|
||
else if (/chrome/i.test(ua)) browser = 'Chrome';
|
||
else if (/safari/i.test(ua)) browser = 'Safari';
|
||
return fmt([
|
||
' ▄███████▄ operator@jaeswift.xyz',
|
||
' ██░░░░░░░██ ----------------------',
|
||
' ██░░░░░░░██ OS: JAESWIFT/6.1.0 x86_64',
|
||
' ██░██░██░██ Host: Manchester HQ',
|
||
' ██░░░░░░░██ Kernel: CRT-tactical-v2',
|
||
' ██░░░░░██ Uptime: 24/7/365',
|
||
' ███████ Shell: /bin/jaesh',
|
||
' █ █ Resolution: ' + screen.width + 'x' + screen.height,
|
||
' ██ ██ Terminal: JAE-AI',
|
||
' ██ ██ Browser: ' + browser,
|
||
' Locale: ' + cc,
|
||
' CPU: 18-core @ spicy GHz',
|
||
' Memory: 96GB tactical',
|
||
' Theme: mil-green/CRT/cyberpunk',
|
||
]);
|
||
};
|
||
|
||
// ─── Dev-mode commands ──────────────────────────
|
||
commands.api = async function () {
|
||
if (!devMode) return 'command not found. try /help';
|
||
const endpoints = [
|
||
'/api/visitor/scan',
|
||
'/api/visitor/recent-arcs',
|
||
'/api/leaderboards',
|
||
'/api/telemetry/overview',
|
||
'/api/telemetry/history',
|
||
'/api/telemetry/geo',
|
||
'/api/telemetry/visitors',
|
||
'/api/telemetry/nginx-tail',
|
||
'/api/telemetry/alerts',
|
||
'/api/stats',
|
||
'/api/navigation',
|
||
'/api/changelog',
|
||
'/api/chat (POST)',
|
||
'/api/contraband',
|
||
'/api/sitrep/latest',
|
||
'/api/radar'
|
||
];
|
||
return 'JAESWIFT API endpoints (dev-mode):\n\n ' + endpoints.join('\n ');
|
||
};
|
||
|
||
commands.curl = async function (args) {
|
||
if (!devMode) return 'command not found. try /help';
|
||
const url = args[0];
|
||
if (!url) return 'usage: /curl <url>';
|
||
try {
|
||
const r = await fetch(url);
|
||
const text = (await r.text()).slice(0, 2000);
|
||
return `HTTP ${r.status} ${r.statusText}\ncontent-type: ${r.headers.get('content-type') || 'n/a'}\n\n${text}${text.length >= 2000 ? '\n...[truncated]' : ''}`;
|
||
} catch (e) {
|
||
return `curl: (6) ${e.message}`;
|
||
}
|
||
};
|
||
|
||
commands.geo = async function () {
|
||
if (!devMode) return 'command not found. try /help';
|
||
try {
|
||
const r = await fetch('/api/telemetry/geo');
|
||
const d = await r.json();
|
||
if (!Array.isArray(d) || !d.length) return 'GEO INTEL: no data.';
|
||
return 'GEO INTEL (24h):\n\n' + d.slice(0, 15).map((row, i) =>
|
||
` ${pad(i+1, 2)}. ${(row.country_code || 'XX').padEnd(3)} ${(row.country_name || 'Unknown').padEnd(22)} ${pad(row.count || 0, 5)}`
|
||
).join('\n');
|
||
} catch (e) {
|
||
return 'telemetry unavailable.';
|
||
}
|
||
};
|
||
|
||
// ─── /cowsay ─────────────────────────────────────
|
||
commands.cowsay = function (args) {
|
||
const msg = args.join(' ') || 'moo.';
|
||
const text = msg.slice(0, 200);
|
||
const top = ' ' + '_'.repeat(text.length + 2);
|
||
const bot = ' ' + '-'.repeat(text.length + 2);
|
||
return fmt([
|
||
top,
|
||
'< ' + text + ' >',
|
||
bot,
|
||
' \\ ^__^',
|
||
' \\ (oo)\\_______',
|
||
' (__)\\ )\\/\\',
|
||
' ||----w |',
|
||
' || ||'
|
||
]);
|
||
};
|
||
|
||
// ─── /fortune (tame + offensive) ─────────────────
|
||
const FORTUNES_TAME = [
|
||
'SITREP: the compiler whispers. listen.',
|
||
'SITREP: your branch is ahead by one regret.',
|
||
'SITREP: staging is production if staging is where the users are.',
|
||
'SITREP: tests pass locally is not a deployment strategy.',
|
||
'SITREP: cache invalidation is a personality trait.',
|
||
'SITREP: the database is fine. the database is never fine.',
|
||
'SITREP: write it down. future-you is stupider than you think.',
|
||
'SITREP: uptime is measured in coffee.',
|
||
'SITREP: legacy code is just working code you hate.',
|
||
'SITREP: every bug is someone else\'s feature.',
|
||
'SITREP: the stack trace knows. trust the stack trace.',
|
||
'SITREP: container orchestration is a synonym for chaos.',
|
||
'SITREP: there are two kinds of backup: tested and imaginary.',
|
||
'SITREP: TODO comments age like milk.',
|
||
'SITREP: ship small, ship often, ship sober.',
|
||
'SITREP: rm is a lifestyle. ls is religion.',
|
||
'SITREP: root access is a personality disorder.',
|
||
'SITREP: the terminal is the only honest relationship.',
|
||
'SITREP: `it compiles` is not a vibe.',
|
||
'SITREP: operator status: caffeinated. proceed.'
|
||
];
|
||
const FORTUNES_OFFENSIVE = [
|
||
'SITREP: your code smells like a fucking landfill. grep deeper.',
|
||
'SITREP: whoever wrote this legacy shit owes the team a pint and a resignation letter.',
|
||
'SITREP: you can\'t centralise a garbage fire. you can only photograph it for post-mortem.',
|
||
'SITREP: touch grass, operator. the leaves are greener than your terminal.',
|
||
'SITREP: the sysadmin is tired. the sysadmin is always fucking tired.',
|
||
'SITREP: if it works, don\'t touch it. if it doesn\'t, blame the intern.',
|
||
'SITREP: your git history is a crime scene. get a lawyer.',
|
||
'SITREP: nginx config is 80% vibes and 20% stack overflow.',
|
||
'SITREP: the only thing more fragile than your ego is this regex.',
|
||
'SITREP: deploy on friday. embrace the chaos. live deliciously.',
|
||
'SITREP: your tests are flaky because YOU are flaky.',
|
||
'SITREP: merge conflicts are the universe\'s way of saying grow up.',
|
||
'SITREP: chmod 777 is a cry for help.',
|
||
'SITREP: whoever invented JavaScript owes humanity a bloody apology.',
|
||
'SITREP: your Docker image is 4GB because you don\'t know what you\'re doing. fix it.',
|
||
'SITREP: if you ssh into prod to "just check something", you are the incident.',
|
||
'SITREP: that variable name is an act of violence against future maintainers.',
|
||
'SITREP: you didn\'t break the build. the build broke you.',
|
||
'SITREP: 3am deploys are for masochists and idiots. which one are you?',
|
||
'SITREP: the documentation is a lie. it has always been a lie.',
|
||
'SITREP: `works on my machine` is a confession, not a defence.',
|
||
'SITREP: your monorepo is a cry for therapy.',
|
||
'SITREP: stop copy-pasting from stack overflow you absolute muppet.',
|
||
'SITREP: curl | bash is a religious experience and a mistake.',
|
||
'SITREP: kubernetes is just containers with extra steps and extra crying.',
|
||
'SITREP: the only good password is the one you forgot.',
|
||
'SITREP: your side project is a liability, not a portfolio.',
|
||
'SITREP: linters are for people who can\'t spell. spell better.',
|
||
'SITREP: shit deploys fast. good shit deploys slower. great shit doesn\'t deploy on fridays.',
|
||
'SITREP: if the server is smoking, that\'s your problem now.'
|
||
];
|
||
commands.fortune = function (args) {
|
||
const offensive = args && (args[0] === '-o' || args[0] === '--offensive');
|
||
const pool = offensive ? FORTUNES_OFFENSIVE : FORTUNES_TAME;
|
||
return pool[Math.floor(Math.random() * pool.length)];
|
||
};
|
||
|
||
// ─── /figlet — block-font ASCII banner ─<><E29480><EFBFBD>─────────
|
||
const FIGLET_FONT = {
|
||
'A': [' ██ ','█ █ ','████ ','█ █ ','█ █ '],
|
||
'B': ['███ ','█ █ ','███ ','█ █ ','███ '],
|
||
'C': [' ███ ','█ ','█ ','█ ',' ███ '],
|
||
'D': ['███ ','█ █ ','█ █ ','█ █ ','███ '],
|
||
'E': ['████ ','█ ','███ ','█ ','████ '],
|
||
'F': ['████ ','█ ','███ ','█ ','█ '],
|
||
'G': [' ███ ','█ ','█ ██ ','█ █ ',' ███ '],
|
||
'H': ['█ █ ','█ █ ','████ ','█ █ ','█ █ '],
|
||
'I': ['███ ',' █ ',' █ ',' █ ','███ '],
|
||
'J': [' ██ ',' █ ',' █ ','█ █ ',' ██ '],
|
||
'K': ['█ █ ','█ █ ','██ ','█ █ ','█ █ '],
|
||
'L': ['█ ','█ ','█ ','█ ','████ '],
|
||
'M': ['█ █','██ ██','█ █ █','█ █','█ █'],
|
||
'N': ['█ █ ','██ █ ','█ ██ ','█ █ ','█ █ '],
|
||
'O': [' ██ ','█ █ ','█ █ ','█ █ ',' ██ '],
|
||
'P': ['███ ','█ █ ','███ ','█ ','█ '],
|
||
'Q': [' ██ ','█ █ ','█ █ ','█ █ ',' █ █ '],
|
||
'R': ['███ ','█ █ ','███ ','█ █ ','█ █ '],
|
||
'S': [' ███ ','█ ',' ██ ',' █ ','███ '],
|
||
'T': ['█████',' █ ',' █ ',' █ ',' █ '],
|
||
'U': ['█ █ ','█ █ ','█ █ ','█ █ ',' ██ '],
|
||
'V': ['█ █ ','█ █ ','█ █ ',' ██ ',' █ '],
|
||
'W': ['█ █','█ █','█ █ █','██ ██','█ █'],
|
||
'X': ['█ █ ',' ██ ',' █ ',' ██ ','█ █ '],
|
||
'Y': ['█ █ ',' ██ ',' █ ',' █ ',' █ '],
|
||
'Z': ['████ ',' █ ',' █ ',' █ ','████ '],
|
||
'0': [' ██ ','█ █ ','█ █ ','█ █ ',' ██ '],
|
||
'1': [' █ ','██ ',' █ ',' █ ','███ '],
|
||
'2': [' ██ ','█ █ ',' █ ',' █ ','████ '],
|
||
'3': ['███ ',' █ ',' ██ ',' █ ','███ '],
|
||
'4': ['█ █ ','█ █ ','████ ',' █ ',' █ '],
|
||
'5': ['████ ','█ ','███ ',' █ ','███ '],
|
||
'6': [' ██ ','█ ','███ ','█ █ ',' ██ '],
|
||
'7': ['████ ',' █ ',' █ ',' █ ','█ '],
|
||
'8': [' ██ ','█ █ ',' ██ ','█ █ ',' ██ '],
|
||
'9': [' ██ ','█ █ ',' ███ ',' █ ',' ██ '],
|
||
' ': [' ',' ',' ',' ',' '],
|
||
'!': [' █ ',' █ ',' █ ',' ',' █ '],
|
||
'?': [' ██ ','█ █ ',' █ ',' ',' █ '],
|
||
'.': [' ',' ',' ',' ',' █ '],
|
||
'-': [' ',' ','████ ',' ',' '],
|
||
'_': [' ',' ',' ',' ','████ '],
|
||
'/': [' █ ',' █ ',' █ ','█ ',' '],
|
||
':': [' ',' █ ',' ',' █ ',' ']
|
||
};
|
||
commands.figlet = function (args) {
|
||
const text = (args.join(' ') || 'JAE').toUpperCase().slice(0, 20);
|
||
const rows = ['', '', '', '', ''];
|
||
for (const ch of text) {
|
||
const glyph = FIGLET_FONT[ch] || FIGLET_FONT['?'];
|
||
for (let i = 0; i < 5; i++) rows[i] += glyph[i] + ' ';
|
||
}
|
||
return rows.join('\n');
|
||
};
|
||
|
||
// ─── /ssh fake session ───────────────────────────
|
||
const SSH_FS = {
|
||
'/': ['home', 'etc', 'var', 'opt', 'tmp'],
|
||
'/home': ['operator', 'guest'],
|
||
'/etc': ['hostname', 'motd', 'shadow'],
|
||
'/var': ['log'],
|
||
'/var/log': ['auth.log', 'syslog'],
|
||
};
|
||
const SSH_FILES = {
|
||
'/etc/hostname': 'jaeswift-prod-01',
|
||
'/etc/motd': '\n WELCOME TO JAESWIFT PROD — authorised operators only.\n All activity logged. Press Ctrl+C to get off my lawn.\n',
|
||
'/etc/shadow': 'nice try. permission denied.',
|
||
'/home/operator/notes.txt': 'remember: the password is NOT hunter2.',
|
||
'/var/log/auth.log': '[' + new Date().toISOString() + '] sshd: Accepted publickey for operator from 127.0.0.1',
|
||
};
|
||
commands.ssh = function (args) {
|
||
const target = args[0] || '';
|
||
const m = target.match(/^([a-zA-Z0-9_-]+)@([a-zA-Z0-9.\-_]+)$/);
|
||
if (!m) return 'usage: /ssh <user>@<host> e.g. /ssh operator@jaeswift.xyz';
|
||
const user = m[1], host = m[2];
|
||
mode = 'ssh';
|
||
modeState = { user, host, cwd: '/home/' + user };
|
||
return fmt([
|
||
`Connecting to ${host} on port 22...`,
|
||
`The authenticity of host '${host}' can\'t be established.`,
|
||
`ED25519 key fingerprint is SHA256:J4Esw1FtX${Math.random().toString(36).slice(2,14)}.`,
|
||
`Warning: Permanently added '${host}' (ED25519) to the list of known hosts.`,
|
||
`${user}@${host}'s password: ****`,
|
||
`Authenticating...`,
|
||
`Last login: ${new Date().toUTCString()} from 10.0.0.1`,
|
||
``,
|
||
`Welcome to ${host} — type 'help' for commands, '/exit-ssh' to disconnect.`
|
||
]);
|
||
};
|
||
function sshHandle(line) {
|
||
const raw = line.trim();
|
||
if (!raw) return '';
|
||
if (raw === '/exit-ssh' || raw === 'exit' || raw === 'logout') {
|
||
const h = modeState.host;
|
||
mode = null; modeState = {};
|
||
return `Connection to ${h} closed.`;
|
||
}
|
||
const parts = raw.split(/\s+/);
|
||
const cmd = parts[0].toLowerCase();
|
||
const arg = parts.slice(1).join(' ');
|
||
switch (cmd) {
|
||
case 'help':
|
||
return fmt(['available: ls, cat <f>, pwd, cd <d>, whoami, hostname, uname, date, exit']);
|
||
case 'whoami': return modeState.user;
|
||
case 'hostname': return modeState.host;
|
||
case 'pwd': return modeState.cwd;
|
||
case 'uname': return 'Linux';
|
||
case 'date': return new Date().toString();
|
||
case 'ls': {
|
||
const dir = SSH_FS[modeState.cwd];
|
||
if (dir) return dir.join(' ');
|
||
if (modeState.cwd === '/home/' + modeState.user) return 'notes.txt .bashrc .ssh';
|
||
return '';
|
||
}
|
||
case 'cd': {
|
||
if (!arg || arg === '~') { modeState.cwd = '/home/' + modeState.user; return ''; }
|
||
if (arg.startsWith('/')) modeState.cwd = arg;
|
||
else modeState.cwd = (modeState.cwd + '/' + arg).replace(/\/+/g, '/');
|
||
return '';
|
||
}
|
||
case 'cat': {
|
||
if (!arg) return 'cat: missing operand';
|
||
const full = arg.startsWith('/') ? arg : (modeState.cwd + '/' + arg).replace(/\/+/g, '/');
|
||
if (SSH_FILES[full] != null) return SSH_FILES[full];
|
||
return `cat: ${arg}: No such file or directory`;
|
||
}
|
||
default:
|
||
return `${cmd}: command not found`;
|
||
}
|
||
}
|
||
|
||
// ─── /adventure — cyberpunk text adventure ───────
|
||
const ADV_ROOMS = {
|
||
'alley': { name: 'DARK ALLEY', desc: 'Rain slicks the neon puddles. A fire-escape ladder climbs north to a rooftop. East is a buzzing arcade door.', exits: { n: 'rooftop_low', e: 'arcade' }, items: ['rusty knife'] },
|
||
'arcade': { name: 'NEON ARCADE', desc: 'Machines scream colour. A bouncer eyes you. A back door leads south to a hacker den. West back to the alley.', exits: { w: 'alley', s: 'hacker_den' }, items: ['token'] },
|
||
'hacker_den': { name: 'HACKER DEN', desc: 'Ten monitors, one cat. A hacker types without looking. They might talk. North back to arcade.', exits: { n: 'arcade' }, items: [], npc: 'hacker' },
|
||
'rooftop_low': { name: 'LOW ROOFTOP', desc: 'Gravel, pigeons, a distant drone. A ladder climbs east to the high neon rooftop.', exits: { s: 'alley', e: 'rooftop_high' }, items: [] },
|
||
'rooftop_high': { name: 'NEON ROOFTOP', desc: 'The city spreads below. A corporate drone blocks the datalink pylon. BOSS FIGHT.', exits: { w: 'rooftop_low' }, items: [], boss: 'drone' }
|
||
};
|
||
commands.adventure = function () {
|
||
mode = 'adventure';
|
||
modeState = { room: 'alley', inv: [], hp: 100, bossHp: 60, talked: false, victory: false };
|
||
return fmt([
|
||
'== NEON/NULL — a very short cyberpunk — ==',
|
||
'',
|
||
'You are an operator looking to upload the encryption key to the city.',
|
||
'Commands: look, n/s/e/w (or north/south/east/west), take <item>,',
|
||
' use <item>, inventory (i), attack, talk, /exit-adventure',
|
||
'',
|
||
advDescribe()
|
||
]);
|
||
};
|
||
function advDescribe() {
|
||
const r = ADV_ROOMS[modeState.room];
|
||
const out = [`[${r.name}]`, r.desc];
|
||
if (r.items && r.items.length) out.push('You see: ' + r.items.join(', '));
|
||
if (r.boss && modeState.bossHp > 0) out.push('A CORPORATE DRONE is here. HP: ' + modeState.bossHp);
|
||
const exits = Object.keys(r.exits).map(k => ({n:'north',s:'south',e:'east',w:'west'})[k]).join(', ');
|
||
out.push('Exits: ' + exits);
|
||
return out.join('\n');
|
||
}
|
||
function adventureHandle(line) {
|
||
const raw = line.trim().toLowerCase();
|
||
if (!raw) return '';
|
||
if (raw === '/exit-adventure' || raw === 'exit-adventure' || raw === 'quit') {
|
||
mode = null; modeState = {};
|
||
return 'You jack out. Back to JAE-AI.';
|
||
}
|
||
const dirMap = { n: 'n', north: 'n', s: 's', south: 's', e: 'e', east: 'e', w: 'w', west: 'w' };
|
||
if (dirMap[raw]) {
|
||
const r = ADV_ROOMS[modeState.room];
|
||
const d = dirMap[raw];
|
||
if (r.boss && modeState.bossHp > 0) return 'The drone blocks your path. ATTACK it first.';
|
||
if (!r.exits[d]) return 'You can\'t go that way.';
|
||
modeState.room = r.exits[d];
|
||
return advDescribe();
|
||
}
|
||
if (raw === 'look' || raw === 'l') return advDescribe();
|
||
if (raw === 'inventory' || raw === 'i' || raw === 'inv') {
|
||
return modeState.inv.length ? 'You carry: ' + modeState.inv.join(', ') : 'Your pockets are empty.';
|
||
}
|
||
if (raw.startsWith('take ')) {
|
||
const item = raw.slice(5).trim();
|
||
const r = ADV_ROOMS[modeState.room];
|
||
const idx = r.items.indexOf(item);
|
||
if (idx === -1) return `There is no ${item} here.`;
|
||
r.items.splice(idx, 1);
|
||
modeState.inv.push(item);
|
||
return `You take the ${item}.`;
|
||
}
|
||
if (raw.startsWith('use ')) {
|
||
const item = raw.slice(4).trim();
|
||
if (!modeState.inv.includes(item)) return `You don't have a ${item}.`;
|
||
if (item === 'token' && modeState.room === 'arcade') {
|
||
modeState.inv.push('encryption key');
|
||
return 'The arcade machine clunks. A secret panel opens — you palm an ENCRYPTION KEY.';
|
||
}
|
||
if (item === 'encryption key' && modeState.room === 'rooftop_high' && modeState.bossHp <= 0) {
|
||
modeState.victory = true;
|
||
mode = null; modeState = {};
|
||
return fmt([
|
||
'You jack the ENCRYPTION KEY into the pylon.',
|
||
'The city\'s surveillance grid goes dark for 11 seconds.',
|
||
'That\'s all you needed.',
|
||
'',
|
||
'>> YOU WIN. Mission complete. Exiting adventure. <<'
|
||
]);
|
||
}
|
||
return 'Nothing happens.';
|
||
}
|
||
if (raw === 'talk') {
|
||
const r = ADV_ROOMS[modeState.room];
|
||
if (r.npc === 'hacker' && !modeState.talked) {
|
||
modeState.talked = true;
|
||
if (!modeState.inv.includes('token')) return 'The hacker: "Arcade machine. Back one. Use the token. Trust me."';
|
||
if (!modeState.inv.includes('encryption key')) return 'The hacker: "Use what you have. The arcade remembers."';
|
||
return 'The hacker: "Rooftop. Pylon. Key. Go."';
|
||
}
|
||
return 'No one to talk to.';
|
||
}
|
||
if (raw === 'attack') {
|
||
const r = ADV_ROOMS[modeState.room];
|
||
if (!r.boss || modeState.bossHp <= 0) return 'Nothing to attack.';
|
||
const weapon = modeState.inv.includes('rusty knife') ? 'knife' : 'fist';
|
||
const dmg = weapon === 'knife' ? (8 + Math.floor(Math.random()*8)) : (3 + Math.floor(Math.random()*4));
|
||
modeState.bossHp -= dmg;
|
||
modeState.hp -= (3 + Math.floor(Math.random()*6));
|
||
let out = `You attack with your ${weapon} for ${dmg} damage. Drone HP: ${Math.max(0,modeState.bossHp)}.`;
|
||
if (modeState.hp <= 0) {
|
||
mode = null; modeState = {};
|
||
return out + '\nThe drone flatlines you. GAME OVER.';
|
||
}
|
||
if (modeState.bossHp <= 0) out += '\nThe drone crashes to the gravel. The pylon is exposed.';
|
||
out += ` Your HP: ${modeState.hp}.`;
|
||
return out;
|
||
}
|
||
return `Unknown command: '${raw}'. Try look, directions, take, use, attack, talk.`;
|
||
}
|
||
|
||
// ─── /ttyper — typing speed test ─────────────────
|
||
const TTYPER_QUOTES = [
|
||
'the quick brown fox jumps over the lazy dog while the sysadmin sips cold coffee and watches nginx logs scroll endlessly',
|
||
'code is read many more times than it is written so leave comments that future you will not curse at',
|
||
'in the terminal no one can hear you scream but the bash history remembers every single thing you have ever typed',
|
||
'deploy on friday and spend the weekend rolling back changes like a very unfortunate software archaeologist',
|
||
'the best debugger is still a print statement followed by staring at the output for twenty minutes in silence'
|
||
];
|
||
commands.ttyper = function () {
|
||
const q = TTYPER_QUOTES[Math.floor(Math.random() * TTYPER_QUOTES.length)];
|
||
mode = 'ttyper';
|
||
modeState = { quote: q, start: 0 };
|
||
return fmt([
|
||
'=== TTYPER — typing speed test ===',
|
||
'Type the quote below exactly as shown, then press Enter.',
|
||
'Type "/exit-ttyper" to cancel.',
|
||
'',
|
||
'> ' + q,
|
||
'',
|
||
'(timer starts on your first submission)'
|
||
]);
|
||
};
|
||
function ttyperHandle(line) {
|
||
const raw = line.trim();
|
||
if (raw === '/exit-ttyper' || raw === 'exit-ttyper' || raw === 'quit') {
|
||
mode = null; modeState = {};
|
||
return 'TTYPER cancelled.';
|
||
}
|
||
if (!modeState.start) modeState.start = Date.now();
|
||
const elapsed = (Date.now() - modeState.start) / 1000;
|
||
const target = modeState.quote;
|
||
const a = raw, b = target;
|
||
const maxLen = Math.max(a.length, b.length);
|
||
let correct = 0;
|
||
for (let i = 0; i < Math.min(a.length, b.length); i++) if (a[i] === b[i]) correct++;
|
||
const accuracy = maxLen === 0 ? 100 : Math.round((correct / maxLen) * 100);
|
||
const words = target.split(/\s+/).length;
|
||
const wpm = elapsed > 0 ? Math.round((words / elapsed) * 60) : 0;
|
||
let best = null;
|
||
try {
|
||
const raw2 = localStorage.getItem(TTYPER_KEY);
|
||
if (raw2) best = JSON.parse(raw2);
|
||
} catch (e) {}
|
||
let bestNote = '';
|
||
if (!best || (wpm > (best.wpm || 0) && accuracy >= 90)) {
|
||
const rec = { wpm, accuracy, ts: Date.now() };
|
||
try { localStorage.setItem(TTYPER_KEY, JSON.stringify(rec)); } catch(e){}
|
||
bestNote = '\n*** NEW PERSONAL BEST ***';
|
||
best = rec;
|
||
}
|
||
mode = null; modeState = {};
|
||
return fmt([
|
||
'=== TTYPER RESULT ===',
|
||
`time: ${elapsed.toFixed(2)}s`,
|
||
`WPM: ${wpm}`,
|
||
`accuracy: ${accuracy}%`,
|
||
best ? `all-time best: ${best.wpm} WPM @ ${best.accuracy}%` : '',
|
||
bestNote
|
||
].filter(Boolean));
|
||
}
|
||
|
||
// ─── /cmatrix — migrated to sitewide-effects.js ──
|
||
commands.cmatrix = function () {
|
||
if (!window.__jaeEffects) return 'effects module not loaded.';
|
||
const on = window.__jaeEffects.toggle('cmatrix');
|
||
return on ? 'CMATRIX engaged. Dual-colour rain with audio. Toggle off with /cmatrix.'
|
||
: 'CMATRIX disengaged.';
|
||
};
|
||
|
||
// ─── EFFECTS SUITE — thin wrappers over window.__jaeEffects ──
|
||
function fx(name, onMsg, offMsg) {
|
||
return function () {
|
||
if (!window.__jaeEffects) return 'effects module not loaded.';
|
||
const on = window.__jaeEffects.toggle(name);
|
||
return on ? onMsg : offMsg;
|
||
};
|
||
}
|
||
commands.crt = fx('crt', 'CRT mode engaged. [VINTAGE BEAM ONLINE]', 'CRT mode disengaged.');
|
||
commands.vhs = fx('vhs', 'VHS tape loaded. Tracking...', 'VHS ejected.');
|
||
commands.glitch = fx('glitch', 'Reality corrupted. Good luck.', 'Reality stabilised.');
|
||
commands.redalert = fx('redalert', '⚠ DEFCON 1. Battle stations. ⚠', 'Red alert stood down.');
|
||
commands.red = commands.redalert;
|
||
commands.invert = fx('invert', 'Polarity inverted.', 'Polarity restored.');
|
||
commands.blueprint = fx('blueprint', 'Blueprint mode. Architect vision enabled.', 'Blueprint mode disabled.');
|
||
commands.typewriter = fx('typewriter', 'Typewriter engaged. Retyping...', 'Typewriter reset.');
|
||
commands.gravity = fx('gravity', '🌍 Gravity enabled. Everything falls.', '🌍 Gravity disabled. Restored.');
|
||
commands.earthquake = fx('earthquake', '🌋 7.5 magnitude — hold on (10s).', '🌋 Earthquake over.');
|
||
commands.lowgravity = fx('lowgravity', '🚀 Low-gravity environment active.', '🚀 Gravity normalised.');
|
||
commands.melt = fx('melt', 'Pixels liquifying...', 'Reformed.');
|
||
commands.shuffle = fx('shuffle', 'Words scrambled.', 'Words restored.');
|
||
commands.rain = fx('rain', '🌧️ Rain enabled. Manchester vibes.', '🌧️ Rain stopped.');
|
||
commands.snow = fx('snow', '❄️ Snow enabled. Winter is here.', '❄️ Thaw complete.');
|
||
commands.fog = fx('fog', '🌫️ Fog of war. Mind the torchlight.', '🌫️ Fog lifted.');
|
||
commands.night = fx('night', '🌙 Night-vision enabled. Spotlight tracking cursor.', '🌙 Dawn.');
|
||
commands.underwater = fx('underwater', '🌊 Submerged. Pressure nominal.', '🌊 Surfaced.');
|
||
commands.dimensions = fx('dimensions', '🔺 3D tilt engaged. Follow the cursor.', '🔺 2D restored.');
|
||
commands.portal = fx('portal', '🌀 Portal opened. Cursor is now a window.', '🌀 Portal closed.');
|
||
commands.partymode = fx('partymode', '🎉 PARTY MODE. Can someone get the disco ball?', '🎉 Party over. Clean-up on aisle 4.');
|
||
commands.ghostmode = fx('ghostmode', '👻 Ghost mode. Only the brave shall see.', '👻 Visible again.');
|
||
commands.quantum = fx('quantum', '⚛️ Quantum uncertainty enabled. Panels teleporting.', '⚛️ Observation collapsed. Panels reset.');
|
||
commands.sneak = fx('sneak', 'All labels redacted. █████.', 'Labels restored.');
|
||
commands.hacker = fx('hacker', 'H4X0R M0D3 3NG4G3D. We are in.', 'Exited.');
|
||
|
||
commands.retro = function (args) {
|
||
if (!window.__jaeEffects) return 'effects module not loaded.';
|
||
const year = (args[0] || '').toLowerCase();
|
||
const fx = window.__jaeEffects.registry.retro;
|
||
if (!year || year === 'now' || year === 'off') {
|
||
fx.disable();
|
||
return 'Retro mode disabled. Welcome back to 2026.';
|
||
}
|
||
if (!['1995','2005','2015'].includes(year)) {
|
||
return 'usage: /retro <1995|2005|2015|now>';
|
||
}
|
||
fx.disable();
|
||
fx.enable(year);
|
||
const flavour = {
|
||
'1995': 'Welcome to the World Wide Web! 🌐 Best viewed in Netscape Navigator.',
|
||
'2005': 'Web 2.0, baby. Glossy gradients incoming.',
|
||
'2015': 'Material Design. Clean. Flat. Safe.',
|
||
};
|
||
return flavour[year];
|
||
};
|
||
|
||
commands.effects = function (args) {
|
||
if (!window.__jaeEffects) return 'effects module not loaded.';
|
||
const sub = (args[0] || '').toLowerCase();
|
||
if (sub === 'off') {
|
||
const n = window.__jaeEffects.active().length;
|
||
window.__jaeEffects.disableAll();
|
||
return `Disabled ${n} effect(s).`;
|
||
}
|
||
if (sub === 'all') {
|
||
const list = window.__jaeEffects.list().filter(n => n !== 'retro');
|
||
list.forEach(n => { try { window.__jaeEffects.enable(n); } catch(e){} });
|
||
return '⚠ CHAOS MODE. ' + list.length + ' effects enabled simultaneously. RIP your CPU.';
|
||
}
|
||
const active = window.__jaeEffects.active();
|
||
if (!active.length) return 'No effects currently active. Try /help for the EFFECTS list.';
|
||
const lines = [
|
||
'═══ ACTIVE EFFECTS ═══',
|
||
'┌─────────────────────┬────────┐',
|
||
'│ NAME │ STATUS │',
|
||
'├─────────────────────┼────────┤',
|
||
];
|
||
active.forEach(n => {
|
||
lines.push('│ ' + n.padEnd(20) + '│ ACTIVE │');
|
||
});
|
||
lines.push('└─────────────────────┴────────┘');
|
||
lines.push(`Total: ${active.length} · /effects off to disable all`);
|
||
return lines.join('\n');
|
||
};
|
||
|
||
// ─── /achievements ───────────────────────────────
|
||
const TOTAL_COMMANDS = 50;
|
||
commands.achievements = function () {
|
||
const unlocked = [...achievements].sort();
|
||
const n = unlocked.length;
|
||
const pct = Math.round((n / TOTAL_COMMANDS) * 100);
|
||
const filled = Math.min(10, Math.round((n / TOTAL_COMMANDS) * 10));
|
||
const bar = '[' + '█'.repeat(filled) + '░'.repeat(10 - filled) + ']';
|
||
const out = [
|
||
'=== ACHIEVEMENTS ===',
|
||
'',
|
||
`Progress: ${bar} ${n}/${TOTAL_COMMANDS} (${pct}%)`,
|
||
''
|
||
];
|
||
if (n === 0) out.push('No commands discovered yet. Try /help to start.');
|
||
else {
|
||
out.push('Unlocked:');
|
||
unlocked.forEach(c => out.push(' ✓ /' + c));
|
||
}
|
||
out.push('');
|
||
out.push('Keep exploring, operator.');
|
||
return fmt(out);
|
||
};
|
||
|
||
// ─── /leaderboard ────────────────────────────────
|
||
function rankFor(total) {
|
||
if (total >= 2000) return 'general';
|
||
if (total >= 1000) return 'colonel';
|
||
if (total >= 500) return 'major';
|
||
if (total >= 200) return 'captain';
|
||
if (total >= 100) return 'lieutenant';
|
||
if (total >= 50) return 'sergeant';
|
||
if (total >= 20) return 'corporal';
|
||
return 'recruit';
|
||
}
|
||
commands.leaderboard = function () {
|
||
const entries = Object.entries(allTimeStats.perCmd || {}).sort((a,b) => b[1] - a[1]);
|
||
const mostUsed = entries.length ? entries[0][0] : '—';
|
||
const sessionEntries = Object.entries(sessionPerCmd).sort((a,b) => b[1] - a[1]);
|
||
return fmt([
|
||
'=== LEADERBOARD (this browser) ===',
|
||
'',
|
||
`Session commands: ${sessionCmdCount}`,
|
||
`All-time commands: ${allTimeStats.total || 0}`,
|
||
`Commands unlocked: ${achievements.size}/${TOTAL_COMMANDS}`,
|
||
`Most-used (all-time): /${mostUsed}`,
|
||
`Rank: ${rankFor(allTimeStats.total || 0).toUpperCase()}`,
|
||
'',
|
||
'Session top 5:',
|
||
...sessionEntries.slice(0, 5).map(([c, n]) => ` /${c} ×${n}`),
|
||
'',
|
||
'All-time top 5:',
|
||
...entries.slice(0, 5).map(([c, n]) => ` /${c} ×${n}`)
|
||
]);
|
||
};
|
||
|
||
// ─── Matrix rain migrated to sitewide-effects.js ──
|
||
|
||
|
||
// ─── Konami Code → Developer Mode ────────────────
|
||
const KONAMI = ['ArrowUp','ArrowUp','ArrowDown','ArrowDown','ArrowLeft','ArrowRight','ArrowLeft','ArrowRight','b','a'];
|
||
let kBuf = [];
|
||
document.addEventListener('keydown', function (e) {
|
||
kBuf.push(e.key);
|
||
if (kBuf.length > KONAMI.length) kBuf.shift();
|
||
const match = kBuf.length === KONAMI.length && kBuf.every((k, i) => k.toLowerCase() === KONAMI[i].toLowerCase());
|
||
if (match && !devMode) {
|
||
devMode = true;
|
||
// Add a floating dev badge
|
||
const badge = document.createElement('div');
|
||
badge.id = 'devModeBadge';
|
||
Object.assign(badge.style, {
|
||
position: 'fixed', top: '68px', right: '16px', zIndex: '900',
|
||
background: 'var(--bg-panel)', border: '1px solid var(--warning)',
|
||
color: 'var(--warning)', padding: '6px 12px', fontFamily: 'var(--font-mono)',
|
||
fontSize: '11px', letterSpacing: '1.5px', cursor: 'pointer',
|
||
boxShadow: '0 0 14px rgba(201,162,39,0.4)',
|
||
});
|
||
badge.innerHTML = '⚡ DEVELOPER MODE // UNLOCKED';
|
||
badge.title = 'Click to disable';
|
||
badge.addEventListener('click', () => {
|
||
devMode = false; badge.remove();
|
||
});
|
||
document.body.appendChild(badge);
|
||
try { window.dispatchEvent(new CustomEvent('jae-dev-mode-on')); } catch (err) {}
|
||
}
|
||
});
|
||
|
||
// ─── Entry point — handle a /-command ───────────
|
||
async function handle(raw) {
|
||
const trimmed = (raw || '').trim();
|
||
if (!trimmed) return { handled: false };
|
||
|
||
// If we are inside a nested mode, route input there regardless of '/' prefix,
|
||
// except for an explicit /exit-<mode> escape (handled inside each).
|
||
if (mode === 'ssh') {
|
||
try { const out = sshHandle(trimmed); return { handled: true, output: out == null ? '' : String(out) }; }
|
||
catch (e) { return { handled: true, output: 'ssh error: ' + e.message }; }
|
||
}
|
||
if (mode === 'adventure') {
|
||
try { const out = adventureHandle(trimmed); return { handled: true, output: out == null ? '' : String(out) }; }
|
||
catch (e) { return { handled: true, output: 'adventure error: ' + e.message }; }
|
||
}
|
||
if (mode === 'ttyper') {
|
||
try { const out = ttyperHandle(trimmed); return { handled: true, output: out == null ? '' : String(out) }; }
|
||
catch (e) { return { handled: true, output: 'ttyper error: ' + e.message }; }
|
||
}
|
||
|
||
if (!trimmed.startsWith('/')) return { handled: false };
|
||
|
||
const parts = trimmed.slice(1).split(/\s+/);
|
||
const cmd = (parts[0] || '').toLowerCase();
|
||
const args = parts.slice(1);
|
||
|
||
cmdHistory.push(trimmed);
|
||
if (cmdHistory.length > MAX_HISTORY) cmdHistory = cmdHistory.slice(-MAX_HISTORY);
|
||
saveHistory();
|
||
|
||
if (!cmd) return { handled: true, output: '' };
|
||
|
||
const fn = commands[cmd];
|
||
if (!fn) {
|
||
return { handled: true, output: `command not found: /${cmd}. type /help for available commands.` };
|
||
}
|
||
|
||
// Track stats + unlock achievements for known commands
|
||
try {
|
||
sessionCmdCount++;
|
||
sessionPerCmd[cmd] = (sessionPerCmd[cmd] || 0) + 1;
|
||
allTimeStats.total = (allTimeStats.total || 0) + 1;
|
||
allTimeStats.perCmd = allTimeStats.perCmd || {};
|
||
allTimeStats.perCmd[cmd] = (allTimeStats.perCmd[cmd] || 0) + 1;
|
||
saveStats();
|
||
unlockAchievement(cmd);
|
||
} catch (e) {}
|
||
|
||
try {
|
||
const result = await fn(args);
|
||
return { handled: true, output: (result == null ? '' : String(result)) };
|
||
} catch (e) {
|
||
return { handled: true, output: `error: ${e.message}` };
|
||
}
|
||
}
|
||
|
||
// ─── Boss key — Escape / Ctrl+Shift+B ────────────
|
||
function bossKeyActivate() {
|
||
try {
|
||
if (window.__jaeEffects) { try { window.__jaeEffects.disableAll(); } catch(e){} }
|
||
const badge = document.getElementById('devModeBadge');
|
||
if (badge) badge.style.display = 'none';
|
||
const toasts = document.querySelectorAll('.jae-cli-toast');
|
||
toasts.forEach(t => t.remove());
|
||
const ci = document.getElementById('chatInput');
|
||
if (ci) { ci.value = ''; try { ci.blur(); } catch(e){} }
|
||
const tip = document.createElement('div');
|
||
tip.textContent = 'NORMAL MODE';
|
||
Object.assign(tip.style, {
|
||
position: 'fixed', top: '12px', left: '50%', transform: 'translateX(-50%)',
|
||
background: '#111', color: '#9aa', border: '1px solid #333',
|
||
padding: '4px 10px', fontFamily: 'monospace', fontSize: '10px',
|
||
letterSpacing: '2px', zIndex: '99999', opacity: '0.85',
|
||
borderRadius: '2px', pointerEvents: 'none',
|
||
});
|
||
document.body.appendChild(tip);
|
||
setTimeout(() => { tip.style.transition = 'opacity 0.5s'; tip.style.opacity = '0'; }, 1500);
|
||
setTimeout(() => tip.remove(), 2200);
|
||
} catch (e) {}
|
||
}
|
||
document.addEventListener('keydown', function (e) {
|
||
const isEsc = (e.key === 'Escape');
|
||
const isCtrlShiftB = (e.ctrlKey && e.shiftKey && (e.key === 'B' || e.key === 'b'));
|
||
if (isEsc || isCtrlShiftB) {
|
||
// Don't nuke on Escape while user is typing in chat unless something is actually "hot"
|
||
const anyFx = window.__jaeEffects && window.__jaeEffects.active().length > 0;
|
||
const hot = anyFx || document.getElementById('devModeBadge') || document.querySelector('.jae-cli-toast');
|
||
if (isCtrlShiftB || (isEsc && hot)) {
|
||
bossKeyActivate();
|
||
if (isCtrlShiftB) e.preventDefault();
|
||
}
|
||
}
|
||
});
|
||
|
||
window.__jaeCLI = {
|
||
handle: handle,
|
||
commands: commands,
|
||
isDev: function () { return devMode; },
|
||
isInMode: function () { return mode !== null; },
|
||
currentMode: function () { return mode; },
|
||
bossKey: bossKeyActivate,
|
||
};
|
||
})();
|