jaeswift-website/js/chat-cli.js

1129 lines
55 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ===================================================
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,
};
})();