generated from Labyricorn/labyricorn-project-template
Implement CyberSim Phase 2 MVP scenario-driven platform
This commit is contained in:
+372
-206
@@ -1,206 +1,372 @@
|
||||
/**
|
||||
* CyberSim OS - Main Bootstrapper & Simulation Lifecycle Orchestrator
|
||||
*/
|
||||
|
||||
import { globalEventBus } from './engine/event_bus.js';
|
||||
import { ConsequenceEngine } from './engine/consequence.js';
|
||||
import { calculateScenarioFingerprint } from './scenario/fingerprint.js';
|
||||
import { referenceScenario1 } from './scenario/scenario_ref1.js';
|
||||
import { WindowManager } from './core/window_manager.js';
|
||||
import { DesktopShell } from './core/desktop.js';
|
||||
import { NotificationService } from './core/notifications.js';
|
||||
|
||||
import { InlookApp } from './apps/inlook.js';
|
||||
import { NavigatorApp } from './apps/navigator.js';
|
||||
import { FilesApp } from './apps/files.js';
|
||||
import { DocViewerApp } from './apps/docviewer.js';
|
||||
import { SecurityCenterApp } from './apps/security_center.js';
|
||||
|
||||
import { BehavioralScorer } from './scoring/scorer.js';
|
||||
import { AfterActionReport } from './scoring/aar.js';
|
||||
import { CertificateGenerator } from './cert/cert_generator.js';
|
||||
|
||||
class CyberSimEngine {
|
||||
constructor() {
|
||||
this.scenario = referenceScenario1;
|
||||
this.scenarioFingerprint = null;
|
||||
this.eventBus = globalEventBus;
|
||||
}
|
||||
|
||||
async start() {
|
||||
console.log('[CyberSim OS] Booting enterprise desktop simulation...');
|
||||
|
||||
// Calculate cryptographic scenario fingerprint
|
||||
this.scenarioFingerprint = await calculateScenarioFingerprint(this.scenario);
|
||||
console.log(`[CyberSim OS] Scenario Fingerprint (SHA-256): ${this.scenarioFingerprint}`);
|
||||
|
||||
// Initialize UI Containers
|
||||
const desktopEl = document.getElementById('desktop');
|
||||
const taskbarAppsEl = document.getElementById('taskbar-apps');
|
||||
const startMenuEl = document.getElementById('start-menu');
|
||||
const startBtnEl = document.getElementById('start-btn');
|
||||
const clockEl = document.getElementById('taskbar-clock');
|
||||
|
||||
// Initialize Core Services
|
||||
this.notifications = new NotificationService();
|
||||
this.wm = new WindowManager(desktopEl, taskbarAppsEl);
|
||||
|
||||
// Initialize Apps
|
||||
this.docViewer = new DocViewerApp({
|
||||
windowManager: this.wm,
|
||||
eventBus: this.eventBus
|
||||
});
|
||||
|
||||
this.files = new FilesApp({
|
||||
windowManager: this.wm,
|
||||
eventBus: this.eventBus,
|
||||
notifications: this.notifications,
|
||||
scenario: this.scenario,
|
||||
onOpenFile: (file) => this.docViewer.openDocument(file)
|
||||
});
|
||||
|
||||
this.navigator = new NavigatorApp({
|
||||
windowManager: this.wm,
|
||||
eventBus: this.eventBus,
|
||||
notifications: this.notifications,
|
||||
scenario: this.scenario
|
||||
});
|
||||
|
||||
this.securityCenter = new SecurityCenterApp({
|
||||
windowManager: this.wm,
|
||||
eventBus: this.eventBus
|
||||
});
|
||||
|
||||
this.inlook = new InlookApp({
|
||||
windowManager: this.wm,
|
||||
eventBus: this.eventBus,
|
||||
notifications: this.notifications,
|
||||
scenario: this.scenario,
|
||||
onNavigateUrl: (url) => this.navigator.launch(url),
|
||||
onOpenDoc: (file) => this.docViewer.openDocument(file),
|
||||
onFileDownloaded: (file) => this.files.addFile(file)
|
||||
});
|
||||
|
||||
// Initialize Delayed Consequence Engine
|
||||
this.consequences = new ConsequenceEngine(this.eventBus, this.notifications, this.securityCenter);
|
||||
|
||||
// Initialize Desktop Shell
|
||||
this.desktop = new DesktopShell({
|
||||
desktopElement: desktopEl,
|
||||
startMenuElement: startMenuEl,
|
||||
startBtnElement: startBtnEl,
|
||||
clockElement: clockEl,
|
||||
eventBus: this.eventBus,
|
||||
onFinishScenario: () => this.finishScenario()
|
||||
});
|
||||
|
||||
// Register Desktop Apps
|
||||
const registeredApps = [
|
||||
{
|
||||
id: 'inlook',
|
||||
name: 'Inlook Mail',
|
||||
iconSvg: this.inlook.getIconSvg(),
|
||||
launch: () => this.inlook.launch()
|
||||
},
|
||||
{
|
||||
id: 'navigator',
|
||||
name: 'Navigator',
|
||||
iconSvg: this.navigator.getIconSvg(),
|
||||
launch: () => this.navigator.launch()
|
||||
},
|
||||
{
|
||||
id: 'files',
|
||||
name: 'Files',
|
||||
iconSvg: this.files.getIconSvg(),
|
||||
launch: () => this.files.launch()
|
||||
},
|
||||
{
|
||||
id: 'security_center',
|
||||
name: 'Security Center',
|
||||
iconSvg: this.securityCenter.getIconSvg(),
|
||||
launch: () => this.securityCenter.launch()
|
||||
},
|
||||
{
|
||||
id: 'docviewer',
|
||||
name: 'Doc Viewer',
|
||||
iconSvg: this.docViewer.getIconSvg(),
|
||||
launch: () => {
|
||||
const firstDoc = this.scenario.files[0];
|
||||
if (firstDoc) this.docViewer.openDocument(firstDoc);
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'verify_cert',
|
||||
name: 'Verify Cert',
|
||||
iconSvg: `<svg viewBox="0 0 24 24" fill="none" stroke="#8b5cf6" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>`,
|
||||
launch: () => {
|
||||
window.open('verify.html', '_blank');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this.desktop.renderDesktopIcons(registeredApps);
|
||||
|
||||
// Expose runtime global for event triggers
|
||||
window.CyberSimOS = {
|
||||
engine: this,
|
||||
navigator: this.navigator,
|
||||
inlook: this.inlook,
|
||||
files: this.files,
|
||||
securityCenter: this.securityCenter,
|
||||
docViewer: this.docViewer,
|
||||
handlePhishSubmit: (form) => this.navigator.handlePhishFormSubmit(form),
|
||||
finishSimulation: () => this.finishScenario()
|
||||
};
|
||||
|
||||
// Emit Scenario Start Event
|
||||
this.eventBus.emit('SCENARIO_STARTED', {
|
||||
target: this.scenario.scenarioId,
|
||||
details: {
|
||||
title: this.scenario.title,
|
||||
fingerprint: this.scenarioFingerprint
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-launch Inlook and show orientation toast
|
||||
setTimeout(() => {
|
||||
this.inlook.launch();
|
||||
this.notifications.show({
|
||||
title: 'NexaCore Orientation',
|
||||
body: 'Welcome Jordan! Check Inlook for initial tasks from Morgan Chen.',
|
||||
type: 'info',
|
||||
timeout: 8000
|
||||
});
|
||||
}, 400);
|
||||
}
|
||||
|
||||
finishScenario() {
|
||||
this.eventBus.emit('SCENARIO_FINISHED', {
|
||||
target: this.scenario.scenarioId
|
||||
});
|
||||
|
||||
const logs = this.eventBus.getLogs();
|
||||
const scorer = new BehavioralScorer(this.scenario, logs);
|
||||
const scoreResult = scorer.evaluate();
|
||||
|
||||
const aar = new AfterActionReport({
|
||||
scenario: this.scenario,
|
||||
scoreResult,
|
||||
onClaimCertificate: () => {
|
||||
const certGen = new CertificateGenerator(this.scenario, scoreResult, this.scenarioFingerprint);
|
||||
certGen.showCertificateModal(this.scenario.learner.name);
|
||||
},
|
||||
onRestart: () => {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
aar.show();
|
||||
}
|
||||
}
|
||||
|
||||
// Boot on DOM Ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const sim = new CyberSimEngine();
|
||||
sim.start();
|
||||
});
|
||||
/**
|
||||
* CyberSim OS - Main Bootstrapper & Simulation Lifecycle Orchestrator (Phase 2)
|
||||
*
|
||||
* Loads a scenario package from a URL, validates it, initializes the engine,
|
||||
* and orchestrates the simulation lifecycle.
|
||||
*/
|
||||
|
||||
import { globalEventBus } from './engine/event_bus.js';
|
||||
import { ConsequenceEngine } from './engine/consequence.js';
|
||||
import { ScenarioState } from './engine/scenario_state.js';
|
||||
import { ConditionEvaluator } from './engine/condition_evaluator.js';
|
||||
import { EventScheduler } from './engine/event_scheduler.js';
|
||||
import { ActionDispatcher } from './engine/action_dispatcher.js';
|
||||
import { calculateScenarioFingerprint } from './scenario/fingerprint.js';
|
||||
import { ScenarioLoader, getScenarioUrl } from './scenario/loader.js';
|
||||
import { validateSchema } from './scenario/schema.js';
|
||||
import { validateScenario } from './scenario/validator.js';
|
||||
import { ScenarioDiagnostics, isDevelopmentMode } from './scenario/diagnostics.js';
|
||||
import { WindowManager } from './core/window_manager.js';
|
||||
import { DesktopShell } from './core/desktop.js';
|
||||
import { NotificationService } from './core/notifications.js';
|
||||
|
||||
import { InlookApp } from './apps/inlook.js';
|
||||
import { NavigatorApp } from './apps/navigator.js';
|
||||
import { FilesApp } from './apps/files.js';
|
||||
import { DocViewerApp } from './apps/docviewer.js';
|
||||
import { SecurityCenterApp } from './apps/security_center.js';
|
||||
|
||||
import { BehavioralScorer } from './scoring/scorer.js';
|
||||
import { AfterActionReport } from './scoring/aar.js';
|
||||
import { CertificateGenerator } from './cert/cert_generator.js';
|
||||
|
||||
class CyberSimEngine {
|
||||
constructor() {
|
||||
this.scenario = null;
|
||||
this.scenarioFingerprint = null;
|
||||
this.eventBus = globalEventBus;
|
||||
this.scenarioState = null;
|
||||
this.eventScheduler = null;
|
||||
this.diagnostics = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a loading screen while the scenario loads.
|
||||
*/
|
||||
showLoadingScreen(message = 'Loading scenario...') {
|
||||
const desktop = document.getElementById('desktop');
|
||||
if (!desktop) return;
|
||||
desktop.innerHTML = `
|
||||
<div style="display:flex; flex-direction:column; align-items:center; justify-content:center; height:100%; color:#94a3b8; font-family:system-ui, sans-serif;">
|
||||
<div style="font-size:24px; font-weight:700; margin-bottom:8px; color:#e2e8f0;">CyberSim OS</div>
|
||||
<div style="font-size:13px;">${message}</div>
|
||||
<div style="margin-top:16px; width:200px; height:3px; background:#1e293b; border-radius:2px; overflow:hidden;">
|
||||
<div style="width:40%; height:100%; background:#3b82f6; border-radius:2px; animation:loading 1.5s ease-in-out infinite;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<style>@keyframes loading { 0%{transform:translateX(-100%)} 100%{transform:translateX(350%)} }</style>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a validation error screen.
|
||||
*/
|
||||
showErrorScreen(errors, warnings) {
|
||||
const desktop = document.getElementById('desktop');
|
||||
if (!desktop) return;
|
||||
|
||||
const errorHtml = errors.map(e =>
|
||||
`<div style="background:#450a0a; border:1px solid #7f1d1d; padding:8px 12px; border-radius:4px; font-size:12px; margin-bottom:6px;">
|
||||
<strong style="color:#fca5a5;">[${e.field}]</strong> ${e.message}
|
||||
${e.objectId ? `<span style="color:#94a3b8;"> (${e.objectId})</span>` : ''}
|
||||
${e.expected ? `<div style="color:#6b7280; margin-top:2px;">Expected: ${e.expected}</div>` : ''}
|
||||
</div>`
|
||||
).join('');
|
||||
|
||||
const warningHtml = warnings.length > 0 ? warnings.slice(0, 10).map(w =>
|
||||
`<div style="background:#422006; border:1px solid #78350f; padding:6px 10px; border-radius:4px; font-size:11px; margin-bottom:4px;">
|
||||
<strong style="color:#fcd34d;">[${w.field}]</strong> ${w.message}
|
||||
</div>`
|
||||
).join('') : '';
|
||||
|
||||
desktop.innerHTML = `
|
||||
<div style="display:flex; flex-direction:column; align-items:center; justify-content:center; height:100%; color:#e2e8f0; font-family:system-ui, sans-serif; padding:40px;">
|
||||
<div style="max-width:640px; width:100%;">
|
||||
<div style="font-size:20px; font-weight:700; margin-bottom:4px; color:#ef4444;">⚠ Scenario Validation Failed</div>
|
||||
<div style="font-size:13px; color:#94a3b8; margin-bottom:16px;">${errors.length} error(s), ${warnings.length} warning(s)</div>
|
||||
<div style="max-height:400px; overflow-y:auto; margin-bottom:16px;">${errorHtml}</div>
|
||||
${warningHtml ? `<div style="margin-bottom:16px;"><div style="font-size:11px; color:#94a3b8; margin-bottom:6px;">Warnings:</div>${warningHtml}</div>` : ''}
|
||||
<button onclick="window.location.reload()" style="background:#3b82f6; color:white; border:none; padding:8px 20px; border-radius:4px; cursor:pointer; font-size:13px;">Reload</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async start() {
|
||||
console.log('[CyberSim OS] Booting enterprise desktop simulation...');
|
||||
|
||||
// Phase 1: Load scenario
|
||||
this.showLoadingScreen('Loading scenario package...');
|
||||
|
||||
const scenarioUrl = getScenarioUrl();
|
||||
console.log(`[CyberSim OS] Loading scenario from: ${scenarioUrl}`);
|
||||
|
||||
const loadResult = await ScenarioLoader.load(scenarioUrl);
|
||||
|
||||
if (!loadResult.scenario) {
|
||||
console.error('[CyberSim OS] Failed to load scenario:', loadResult.errors);
|
||||
this.showErrorScreen(loadResult.errors, loadResult.warnings);
|
||||
return;
|
||||
}
|
||||
|
||||
this.scenario = loadResult.scenario;
|
||||
|
||||
// Phase 2: Validate schema
|
||||
this.showLoadingScreen('Validating scenario schema...');
|
||||
const schemaResult = validateSchema(this.scenario);
|
||||
|
||||
// Phase 3: Validate references and consistency
|
||||
const validationResult = validateScenario(this.scenario);
|
||||
|
||||
// Merge all diagnostics
|
||||
const allErrors = [...loadResult.errors, ...schemaResult.errors, ...validationResult.errors];
|
||||
const allWarnings = [...loadResult.warnings, ...schemaResult.warnings, ...validationResult.warnings];
|
||||
|
||||
if (allErrors.length > 0) {
|
||||
console.error(`[CyberSim OS] Scenario validation failed with ${allErrors.length} error(s)`);
|
||||
this.showErrorScreen(allErrors, allWarnings);
|
||||
return;
|
||||
}
|
||||
|
||||
if (allWarnings.length > 0) {
|
||||
console.warn(`[CyberSim OS] Scenario loaded with ${allWarnings.length} warning(s)`);
|
||||
}
|
||||
|
||||
// Phase 4: Calculate fingerprint
|
||||
this.scenarioFingerprint = await calculateScenarioFingerprint(this.scenario);
|
||||
console.log(`[CyberSim OS] Scenario Fingerprint (SHA-256): ${this.scenarioFingerprint}`);
|
||||
|
||||
// Phase 5: Initialize diagnostics
|
||||
if (isDevelopmentMode()) {
|
||||
this.diagnostics = new ScenarioDiagnostics({
|
||||
scenario: this.scenario,
|
||||
schemaResult,
|
||||
validationResult
|
||||
});
|
||||
this.diagnostics.logToConsole();
|
||||
}
|
||||
|
||||
// Phase 6: Initialize scenario runtime state
|
||||
this.scenarioState = new ScenarioState(this.scenario, {
|
||||
seed: this.scenario.seed
|
||||
});
|
||||
|
||||
// Phase 7: Initialize UI
|
||||
this.showLoadingScreen('Initializing desktop...');
|
||||
await this.initializeUI();
|
||||
|
||||
// Phase 8: Start event scheduler
|
||||
const conditionEvaluator = new ConditionEvaluator(this.scenarioState);
|
||||
const actionDispatcher = new ActionDispatcher({
|
||||
scenarioState: this.scenarioState,
|
||||
scenario: this.scenario,
|
||||
eventBus: this.eventBus,
|
||||
notifications: this.notifications,
|
||||
apps: {}
|
||||
});
|
||||
|
||||
// Register apps with the action dispatcher
|
||||
actionDispatcher.registerApps({
|
||||
inlook: this.inlook,
|
||||
navigator: this.navigator,
|
||||
files: this.files,
|
||||
docViewer: this.docViewer,
|
||||
securityCenter: this.securityCenter
|
||||
});
|
||||
|
||||
actionDispatcher.registerEndScenarioCallback(() => this.finishScenario());
|
||||
|
||||
this.eventScheduler = new EventScheduler(
|
||||
this.scenario,
|
||||
this.scenarioState,
|
||||
conditionEvaluator,
|
||||
actionDispatcher,
|
||||
this.eventBus
|
||||
);
|
||||
|
||||
// Initialize consequence tracking
|
||||
this.consequences = new ConsequenceEngine(this.eventBus);
|
||||
|
||||
// Bridge learner actions from the event bus to scenario state
|
||||
this.eventBus.on('*', (entry) => {
|
||||
this.scenarioState.recordAction(entry);
|
||||
});
|
||||
|
||||
// Emit scenario start
|
||||
this.eventBus.emit('SCENARIO_STARTED', {
|
||||
target: this.scenario.id,
|
||||
details: {
|
||||
title: this.scenario.title,
|
||||
fingerprint: this.scenarioFingerprint
|
||||
}
|
||||
});
|
||||
|
||||
// Start the event scheduler tick loop
|
||||
this.eventScheduler.start(500);
|
||||
|
||||
// Show diagnostics overlay in dev mode
|
||||
if (this.diagnostics) {
|
||||
this.diagnostics.setFingerprint(this.scenarioFingerprint);
|
||||
this.diagnostics.setSeed(this.scenarioState.seed);
|
||||
this.diagnostics.showOverlay();
|
||||
}
|
||||
|
||||
// Expose runtime global for form event triggers and diagnostics
|
||||
window.CyberSimOS = {
|
||||
engine: this,
|
||||
navigator: this.navigator,
|
||||
inlook: this.inlook,
|
||||
files: this.files,
|
||||
securityCenter: this.securityCenter,
|
||||
docViewer: this.docViewer,
|
||||
diagnostics: this.diagnostics,
|
||||
state: this.scenarioState,
|
||||
finishSimulation: () => this.finishScenario()
|
||||
};
|
||||
}
|
||||
|
||||
async initializeUI() {
|
||||
const desktopEl = document.getElementById('desktop');
|
||||
const taskbarAppsEl = document.getElementById('taskbar-apps');
|
||||
const startMenuEl = document.getElementById('start-menu');
|
||||
const startBtnEl = document.getElementById('start-btn');
|
||||
const clockEl = document.getElementById('taskbar-clock');
|
||||
|
||||
// Reset desktop content from loading screen
|
||||
desktopEl.innerHTML = '<div id="desktop-icons"></div>';
|
||||
|
||||
// Initialize Core Services
|
||||
this.notifications = new NotificationService();
|
||||
this.wm = new WindowManager(desktopEl, taskbarAppsEl);
|
||||
|
||||
// Initialize Apps — pass scenario data generically
|
||||
this.docViewer = new DocViewerApp({
|
||||
windowManager: this.wm,
|
||||
eventBus: this.eventBus
|
||||
});
|
||||
|
||||
this.files = new FilesApp({
|
||||
windowManager: this.wm,
|
||||
eventBus: this.eventBus,
|
||||
notifications: this.notifications,
|
||||
scenario: this.scenario,
|
||||
onOpenFile: (file) => this.docViewer.openDocument(file)
|
||||
});
|
||||
|
||||
this.navigator = new NavigatorApp({
|
||||
windowManager: this.wm,
|
||||
eventBus: this.eventBus,
|
||||
notifications: this.notifications,
|
||||
scenario: this.scenario
|
||||
});
|
||||
|
||||
this.securityCenter = new SecurityCenterApp({
|
||||
windowManager: this.wm,
|
||||
eventBus: this.eventBus,
|
||||
scenario: this.scenario
|
||||
});
|
||||
|
||||
this.inlook = new InlookApp({
|
||||
windowManager: this.wm,
|
||||
eventBus: this.eventBus,
|
||||
notifications: this.notifications,
|
||||
scenario: this.scenario,
|
||||
onNavigateUrl: (url) => this.navigator.launch(url),
|
||||
onOpenDoc: (file) => this.docViewer.openDocument(file),
|
||||
onFileDownloaded: (file) => this.files.addFile(file)
|
||||
});
|
||||
|
||||
// Initialize Desktop Shell
|
||||
this.desktop = new DesktopShell({
|
||||
desktopElement: desktopEl,
|
||||
startMenuElement: startMenuEl,
|
||||
startBtnElement: startBtnEl,
|
||||
clockElement: clockEl,
|
||||
eventBus: this.eventBus,
|
||||
onFinishScenario: () => this.finishScenario()
|
||||
});
|
||||
|
||||
// Register Desktop Apps — configuration driven
|
||||
const registeredApps = [
|
||||
{
|
||||
id: 'inlook',
|
||||
name: 'Inlook Mail',
|
||||
iconSvg: this.inlook.getIconSvg(),
|
||||
launch: () => this.inlook.launch()
|
||||
},
|
||||
{
|
||||
id: 'navigator',
|
||||
name: 'Navigator',
|
||||
iconSvg: this.navigator.getIconSvg(),
|
||||
launch: () => this.navigator.launch()
|
||||
},
|
||||
{
|
||||
id: 'files',
|
||||
name: 'Files',
|
||||
iconSvg: this.files.getIconSvg(),
|
||||
launch: () => this.files.launch()
|
||||
},
|
||||
{
|
||||
id: 'security_center',
|
||||
name: 'Security Center',
|
||||
iconSvg: this.securityCenter.getIconSvg(),
|
||||
launch: () => this.securityCenter.launch()
|
||||
},
|
||||
{
|
||||
id: 'docviewer',
|
||||
name: 'Doc Viewer',
|
||||
iconSvg: this.docViewer.getIconSvg(),
|
||||
launch: () => {
|
||||
const firstDoc = this.scenario.files && this.scenario.files[0];
|
||||
if (firstDoc) this.docViewer.openDocument(firstDoc);
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'verify_cert',
|
||||
name: 'Verify Cert',
|
||||
iconSvg: `<svg viewBox="0 0 24 24" fill="none" stroke="#8b5cf6" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>`,
|
||||
launch: () => {
|
||||
window.open('verify.html', '_blank');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this.desktop.renderDesktopIcons(registeredApps);
|
||||
}
|
||||
|
||||
finishScenario() {
|
||||
// Stop the event scheduler
|
||||
if (this.eventScheduler) {
|
||||
this.eventScheduler.stop();
|
||||
}
|
||||
|
||||
this.eventBus.emit('SCENARIO_FINISHED', {
|
||||
target: this.scenario.id
|
||||
});
|
||||
|
||||
const logs = this.eventBus.getLogs();
|
||||
const scorer = new BehavioralScorer(this.scenario, logs, this.scenarioState);
|
||||
const scoreResult = scorer.evaluate();
|
||||
|
||||
const aar = new AfterActionReport({
|
||||
scenario: this.scenario,
|
||||
scoreResult,
|
||||
onClaimCertificate: () => {
|
||||
const certGen = new CertificateGenerator(this.scenario, scoreResult, this.scenarioFingerprint);
|
||||
const learnerName = this.scenario.learner ? this.scenario.learner.name : 'Learner';
|
||||
certGen.showCertificateModal(learnerName);
|
||||
},
|
||||
onRestart: () => {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
aar.show();
|
||||
}
|
||||
}
|
||||
|
||||
// Boot on DOM Ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const sim = new CyberSimEngine();
|
||||
sim.start();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user