'use strict';
let _ecosystemBuilt = false;
let _ecosystemLoaded = false;
let _ecosystemActiveTab = 'overview';
let _ecosystemEtaHash = null;
let _ecosystemEtaProposed = null;
let _ecosystemCheatTimer = null;
let _ecosystemCheatOffset = 0;
let _ecosystemCheatRequest = 0;
const ECOSYSTEM_CHEAT_PAGE_SIZE = 50;
let _timelineFilterTimer = null;
let _ecosystemSaveCapabilities = null;
let _gameUpdatePlan = null;
let _gameUpdateRefreshTimer = null;
let _websrvFsPath = '/';
let _websrvSmbPath = '';
let _pacbrewPacket = null;
let _itemzflowPacket = null;
let _remotePlayPacket = null;
let _remotePlayPollTimer = null;
let _ecosystemComponentsPacket = null;
function ecosystemDate(value) {
if (!value) return 'Not yet';
try { return new Date(value).toLocaleString(window.i18n?.current || 'en'); } catch (_) { return 'Unknown'; }
}
function ecosystemBytes(value) {
const bytes = Number(value || 0);
if (!bytes) return '0 B';
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let amount = bytes; let unit = 0;
while (amount >= 1024 && unit < units.length - 1) { amount /= 1024; unit += 1; }
return `${amount >= 10 ? amount.toFixed(0) : amount.toFixed(1)} ${units[unit]}`;
}
function ecosystemStatus(status, label = null) {
const normalized = ['compatible', 'online', 'ok'].includes(status) ? 'compatible'
: ['incompatible', 'offline', 'error'].includes(status) ? 'warning'
: status || 'unknown';
const text = label || ({ compatible: 'Compatible', warning: 'Warning', unknown: 'Unknown' }[normalized] || status || 'Unknown');
return `${escHtml(text)} `;
}
function buildEcosystemPage() {
if (_ecosystemBuilt) return;
const page = $('page-ecosystem');
page.innerHTML = `
${[
['overview', 'Command Center'], ['components', 'Components'], ['updates', 'Payload updates'], ['game-updates', 'Game updates'], ['etahen', 'Console profile'],
['live', 'Live Session'], ['remote-play', 'Remote Play'], ['files', 'Files'], ['homebrew', 'Homebrew'], ['sources', 'Sources'], ['plugins', 'Plugins'],
['cheats', 'Cheats & patches'], ['saves', 'Save Vault'], ['itemzflow', 'Itemzflow'], ['platform-lab', window.t('platformLab.tab', 'Platform Lab')], ['research', 'Research Lab'], ['timeline', 'Timeline'],
].map(([id, label], index) => `${label} `).join('')}
Loading runtime status…
Not checked yet. Automatic execution always remains disabled, regardless of these settings.
Loading update catalog…
No manifest inspected yet. Inspection downloads only bounded JSON metadata and checks the host, offsets, part hashes, package hash and free space.
1 Choose Preset or known setting
2 Review Exact diff and risks
3 Apply Backup, write and verify
PresetNo preset Preview Apply safely
Connect through FTP first.
View raw config.ini
1 ps5debug-NG 1.3.0 Required on PS5 port 744
2 Read-only handshake Runs only when requested
3 Session context Firmware and foreground title
Start Remote Play Wake PS5 All safety and connectivity checks run first.
Three-step installation Extract the ZIP → run install.ps1 → return here and click Refresh. The short-lived enrollment token is erased after the first HTTPS connection.
Diagnose Open / register client Install chiaki-ng Update chiaki-ng Stop session
No Remote Play action has run yet.
SMB sourceChoose a source
Choose a saved SMB source.
All types Applications Services Libraries Toolchain
Open this tab to load the catalog.
Passwords are encrypted with AES-256-GCM and are never returned to the browser.
Loading status…
Port 9090 is only the ezRemote Client WebUI. etaHEN DPI can use the same port. Chimera uses only ezRemote Server on 6701 for downloads and starts or stops neither service.
No download status loaded yet.
DestinationInternal · /data/etaHEN/plugins ${Array.from({ length: 7 }, (_, index) => `USB ${index} · /mnt/usb${index}/etaHEN/plugins `).join('')}
Choose and install ELF / PRX
Explicit and reversible An existing file with the same name is backed up first. Reload etaHEN or use Toolbox after installation.
Console saves not scanned yet Garlic Save Manager must be running on port 8082.
Loading restore safeguards…
Loading Itemzflow status…
LogAll logs LevelAll Errors Warnings Info Jobs and signals only
Review-first integration layer. This area observes upstreams and prepares inert plans. It never deploys payloads or mutates a console.
Preview Y2JB, BD-JB, and Lua profiles without writing removable media.
Preview
Read-only inventory of emulator copies and ZArchive candidates.
Scan
Inspect a root-relative payload, review its exact hash, then stage a complete profile atomically.
Inspect
Stage reviewed profile
Save Delete Recovery manifest
A dedicated no-shell adapter accepts only an indexed game and an exact confirmation.
Configure
Refresh Platform Lab
Expert area without execution controls. Chimera checks only the profile, upstream compatibility and safety requirements. Sending a payload, running an exploit, entering rest mode, writing USB/M.2 storage and disabling etaHEN/kstuff are technically unavailable here.
All levels Errors Warnings Info
`;
bindEcosystemEvents();
window.ChimeraLinkUx?.mount({ root: $('etahen-link-root'), loadStatus: () => window.pork.etahenLinkInventoryStatus(), translate: (key, fallback) => window.t?.(key, fallback) || fallback });
_ecosystemBuilt = true;
}
async function ecosystemBusy(button, task) {
const prior = button?.textContent;
if (button) { button.disabled = true; button.textContent = window.t('common.loading', 'Loading…'); }
try { return await task(); }
finally { if (button) { button.disabled = false; button.textContent = prior; } }
}
function localizedReadinessStep(step) {
if (window.i18n?.current === 'nl') return step;
const english = {
profile: { label: 'Console profile', detail: step.state === 'ready' ? step.detail : 'IP address not configured' },
transport: { label: 'File transport', detail: step.state === 'ready' ? 'FTP transport reachable' : 'etaHEN FTP or ftpsrv is required for files' },
elfldr: { label: 'ELF Loader', detail: 'Foundation for Payload Manager and multiple homebrew services' },
'payload-manager': { label: 'Payload Manager', detail: 'Catalog, imports and explicit payload launch' },
websrv: { label: 'websrv', detail: 'Homebrew, filesystem and additional network services' },
diagnostics: { label: 'Diagnostics', detail: 'Klog is optional and only needed during investigation' },
};
return { ...step, ...(english[step.id] || {}) };
}
function localizedEcosystemAlert(alert, field) {
const fallback = alert?.[field] || '';
const count = String(alert?.title || '').match(/\d+(?:\/\d+)?/)?.[0] || '';
return window.tf?.(`navigation.alerts.${alert?.id}.${field}`, fallback, { count }) || fallback;
}
function localizedEcosystemError(value) {
const parts = [...new Set(String(value || '').split(/\s+·\s+/).map(part => part.trim()).filter(Boolean))];
const message = parts.join(' · ');
if (window.i18n?.current === 'nl') return message;
return message
.replace(/Configureer eerst een geldig PS5-hostadres(?: in Instellingen)?\.?/gi, 'Configure a valid PS5 host in Settings first.') // i18n: normalize persisted server copy
.replace(/Maak eerst verbinding met de PS5 via FTP\.?/gi, 'Connect to the PS5 through FTP first.') // i18n: normalize persisted server copy
.replace(/Start ([^.]+) op de PS5\.?/gi, 'Start $1 on the PS5.');
}
function localizedEcosystemActivity(value) {
const message = String(value || '');
if (window.i18n?.current === 'nl') return message;
return message
.replace('Console Health wacht op configuratie', 'Console Health is waiting for configuration')
.replace(/(\d+) aandachtspunt\(en\) gevonden\./, '$1 attention item(s) found.'); // i18n: normalize persisted activity copy.
}
function ecosystemCapabilityText(key, fallback) {
return window.t?.(`ecosystem.capabilities.${key}`, fallback) || fallback;
}
function renderCapabilityGraph(graph) {
if (!graph?.features) return '';
const groups = ['core', 'connected', 'planned'];
return `
${[
['available', graph.summary?.available || 0], ['conditional', graph.summary?.conditional || 0], ['blocked', graph.summary?.blocked || 0], ['bindings', graph.bindings?.length || 0],
].map(([label, count]) => `
${count} ${escHtml(ecosystemCapabilityText(`status.${label}`, label))}
`).join('')}
${groups.map(group => `
${escHtml(ecosystemCapabilityText(`group.${group}`, group))} ${graph.features.filter(item => item.group === group).map(item => `
${escHtml(ecosystemCapabilityText(`feature.${item.id}`, item.id))} ${escHtml(ecosystemCapabilityText(`reason.${item.reasonCode}`, item.reasonCode))} · ${escHtml(ecosystemCapabilityText(`mode.${item.mode}`, item.mode))}
${ecosystemStatus(item.status === 'available' ? 'compatible' : item.status === 'conditional' ? 'warning' : item.status === 'blocked' ? 'offline' : 'unknown', ecosystemCapabilityText(`status.${item.status}`, item.status))}
`).join('')}
`).join('')}
${graph.freshness?.stale ? ecosystemCapabilityText('stale', 'Upstream evidence is stale; refresh components before relying on version-sensitive actions.') : ecosystemCapabilityText('fresh', 'Upstream evidence is current.')}
`;
}
function renderEcosystemOverview(data, healthPacket = {}, activity = [], healthHistory = [], capabilityGraph = null) {
const services = data.services?.services || [];
const online = services.filter(service => service.reachable).length;
const report = healthPacket.report || null;
const healthAge = report ? Math.max(0, Date.now() - Number(report.generatedAt || 0)) : null;
const healthStale = healthAge !== null && healthAge > 5 * 60 * 1000;
const alerts = report?.alerts || [];
const openAlerts = alerts.filter(alert => !alert.acknowledged);
const readiness = data.readiness || { state: 'not-configured', steps: [] };
const readinessSteps = (readiness.steps || []).map(localizedReadinessStep);
const isDutch = window.i18n?.current === 'nl';
const readinessLabels = new Map(readinessSteps.map(step => [step.id, step.label]));
$('ecosystem-overview').innerHTML = `
${[
[isDutch ? 'Kernverbinding' : 'Core connection', readinessSteps.filter(step => step.required)],
[isDutch ? 'Optionele diensten' : 'Optional services', readinessSteps.filter(step => !step.required)],
].map(([label, steps]) => `
${label}${steps.filter(step => step.state === 'ready').length}/${steps.length} ${steps.map((step, index) => `${step.state === 'ready' ? '✓' : step.state === 'attention' ? '!' : index + 1} ${escHtml(step.label)} ${escHtml(step.detail)} ${step.dependsOn?.length ? `${isDutch ? 'Afhankelijk van' : 'Depends on'} ${step.dependsOn.map(id => escHtml(readinessLabels.get(id) || id)).join(', ')} ` : ''} `).join('')}
`).join('')}
${renderCapabilityGraph(capabilityGraph)}
${[
['Console Health', report ? (report.score == null ? 'Not configured' : `${report.score}/100`) : 'Not yet'],
['Services online', `${online}/${services.length}`],
['Managed assets', (data.homebrew?.count || 0) + (data.plugins?.count || 0) + (data.cheats?.count || 0)],
['Protected data', `${data.saveVault?.snapshots || 0} snapshots`],
].map(([label, value]) => `
${escHtml(value)} ${escHtml(label)}
`).join('')}
${data.services?.error ? `
${escHtml(localizedEcosystemError(data.services.error))}
` : services.map(service => `
${escHtml(service.label)} ${isDutch ? 'Poort' : 'Port'} ${service.port}${service.error ? ` · ${escHtml(service.error)}` : ''}
${ecosystemStatus(service.reachable ? 'online' : 'offline', service.reachable ? 'Online' : 'Offline')}
`).join('')}
No automatic execution Updates are never started immediately on the PS5.
Source and checksum Official upstream and published digests remain visible.
Expert isolation Shell and GDB are not proxied through the public Chimera interface.
${alerts.length ? alerts.map(alert => `
${escHtml(localizedEcosystemAlert(alert, 'title'))} ${escHtml(localizedEcosystemAlert(alert, 'detail'))}
${ecosystemStatus(alert.severity === 'critical' ? 'error' : alert.severity === 'warning' ? 'warning' : 'unknown', alert.severity)}${alert.action ? `Open ` : ''}${alert.acknowledged ? 'Read ' : 'Mark seen '}
`).join('') : '
✓ No known issues.
'}
${healthHistory.some(item => item.score != null) ? `
${healthHistory.filter(item => item.score != null).slice(0, 20).reverse().map(item => `
${escHtml(item.score)}
`).join('')}
` : '
The trend appears after multiple configured health scans.
'}
${activity.length ? activity.slice(0, 12).map(item => `
${ecosystemDate(item.at)} ${escHtml(localizedEcosystemActivity(item.title))} ${escHtml(localizedEcosystemActivity(item.detail || item.type))}
`).join('') : '
No management activity recorded yet.
'}
`;
const health = $('ecosystem-health');
health.textContent = report ? (report.score == null ? 'Health · not configured' : `Health ${report.score}/100 · ${report.grade}${healthStale ? ' · stale' : ''}`) : (services.length ? `${online}/${services.length} PS5 services online` : (data.services?.error || 'PS5 not configured'));
health.className = `operations-health ${report?.score == null ? '' : !healthStale && (report?.score >= 75 || (!report && online)) ? 'healthy' : 'warning'}`;
const badge = $('nav-ecosystem-badge');
const issues = openAlerts.filter(alert => ['critical', 'warning'].includes(alert.severity)).length || Number(data.plugins?.conflicts || 0);
if (badge) { badge.hidden = issues === 0; badge.textContent = issues || ''; }
}
async function loadEcosystemOverview() {
const [data, healthPacket, activity, healthHistory, capabilityGraph] = await Promise.all([window.pork.ecosystemOverview(), window.pork.ecosystemHealthLatest(), window.pork.ecosystemActivity(50), window.pork.ecosystemHealthHistory(30), window.pork.ecosystemCapabilityGraph()]);
renderEcosystemOverview(data, healthPacket, activity, healthHistory, capabilityGraph);
_ecosystemLoaded = true;
}
function ecosystemComponentGroup(component) {
if (['experimental-os', 'expert', 'kernel'].includes(component.kind) || component.risk === 'expert') return 'Experimental & expert';
if (['catalog', 'plugin', 'plugin-catalog', 'database'].includes(component.kind)) return 'Catalogs, plugins & data';
if (['homebrew', 'client-companion', 'download-tool', 'remote-play-helper', 'account-tool', 'diagnostic'].includes(component.kind)) return 'Applications & tools';
return 'Core services';
}
function renderEcosystemComponents() {
const result = _ecosystemComponentsPacket;
if (!result) return;
const query = String($('ecosystem-components-search')?.value || '').trim().toLowerCase();
const platform = $('ecosystem-components-platform')?.value || 'all';
const risk = $('ecosystem-components-risk')?.value || 'all';
const filtered = result.components.filter(component => {
const haystack = `${component.name} ${component.kind} ${component.platform} ${(component.dependencies || []).join(' ')}`.toLowerCase();
return (!query || haystack.includes(query))
&& (platform === 'all' || component.platform === platform)
&& (risk === 'all' || component.risk === risk);
});
const groups = new Map();
for (const component of filtered) {
const group = ecosystemComponentGroup(component);
if (!groups.has(group)) groups.set(group, []);
groups.get(group).push(component);
}
const counts = {
visible: filtered.length,
total: result.components.length,
compatible: filtered.filter(component => component.compatibility?.status === 'compatible').length,
attention: filtered.filter(component => component.compatibility?.status === 'incompatible' || component.risk === 'expert').length,
};
counts.sourceDependent = Math.max(0, counts.visible - counts.compatible - counts.attention);
$('ecosystem-components').innerHTML = `
${counts.visible}/${counts.total} Visible components
${counts.compatible} Compatible
${counts.attention} Needs attention
${counts.sourceDependent} Source-dependent
${ecosystemDate(result.checkedAt)} Last upstream check
${groups.size ? [...groups.entries()].map(([label, components]) => `${components.map(component => `
${escHtml(component.name)} ${escHtml(component.kind)} · ${escHtml(component.platform)} · ${escHtml(component.trust)}
${escHtml(component.release?.tag || 'Unknown version')}${ecosystemStatus(component.compatibility?.status, component.compatibility?.status === 'incompatible' ? 'Incompatible' : component.compatibility?.status === 'compatible' ? 'Compatible' : 'Source-dependent')}
${escHtml(component.compatibility?.reason || '')}${component.dependencies?.length ? ` Depends on: ${component.dependencies.map(escHtml).join(', ')}.` : ''}
Open upstream
`).join('')}
`).join('') : 'No matching components Clear one or more filters to expand the catalog.
'}`;
}
async function loadEcosystemComponents() {
_ecosystemComponentsPacket = await window.pork.ecosystemComponents();
renderEcosystemComponents();
}
function ecosystemUpdateState(item) {
const labels = { current: 'Current', ahead: 'Console newer', 'version-different': 'Manual review', 'update-available': 'Update', staged: 'Staged', 'installed-unknown': 'Release unknown', 'not-tracked': 'Not tracked' };
const status = ['current', 'ahead'].includes(item.state) ? 'compatible' : ['update-available', 'version-different'].includes(item.state) ? 'warning' : 'unknown';
return ecosystemStatus(status, labels[item.state] || item.state);
}
function ecosystemEvidenceSummary(item) {
if (!item.installedSource) return ` ${window.t('ecosystem.payloadEvidence.unavailable', 'evidence unavailable')}`;
const confidence = item.installedSourceConfidence || 'unknown';
const seenAt = item.installedSourceSeenAt ? ecosystemDate(item.installedSourceSeenAt) : window.t('ecosystem.payloadEvidence.unknownTime', 'unknown time');
return ` ${'\u00B7'} ${escHtml(window.t('ecosystem.payloadEvidence.source', 'source'))}: ${escHtml(item.installedSource)} ${'\u00B7'} ${escHtml(window.t('ecosystem.payloadEvidence.confidence', 'confidence'))}: ${escHtml(confidence)} ${'\u00B7'} ${escHtml(window.t('ecosystem.payloadEvidence.seen', 'seen'))}: ${escHtml(seenAt)}`;
}
function ecosystemEvidenceConflictSummary(item) {
if (!item.installedVersionConflicts?.length) return '';
return ` ${'\u2014'} conflicts ${item.installedVersionConflicts.map(conflict => `${escHtml(conflict.version)} (${escHtml(conflict.sources.join(', '))})`).join(' ; ')}`;
}
const ecosystemBullet = '\u00B7';
function renderEcosystemUpdates(inventory, plan) {
$('ecosystem-update-summary').innerHTML = `${[
['Tracked', inventory.stats.total], ['Current', inventory.stats.current], ['Detected', inventory.stats.detected], ['Console newer', inventory.stats.ahead], ['Updates ready', inventory.stats.updates], ['Blocked steps', plan.blocked],
].map(([label, value]) => `
${escHtml(value)} ${escHtml(label)}
`).join('')}
Safe dependency order ${plan.steps.length ? plan.steps.map(step => step.component.name).join(' → ') : 'No tracked updates yet. Record the installed console version below to enable reliable comparison.'}
`;
const groups = [
{ id: 'ready', title: 'Ready to prepare', description: 'A newer upstream asset can be downloaded and verified locally.', items: inventory.components.filter(item => item.state === 'update-available') },
{ id: 'version', title: 'Version required', description: 'Enter the version currently installed on your console.', items: inventory.components.filter(item => ['installed-unknown', 'not-tracked', 'staged'].includes(item.state)) },
{ id: 'review', title: 'Manual review', description: 'Potential downgrade or incomparable version; Chimera will not prepare it automatically.', items: inventory.components.filter(item => ['ahead', 'version-different'].includes(item.state)) },
{ id: 'current', title: 'Current', description: 'Installed and upstream versions match.', items: inventory.components.filter(item => item.state === 'current') },
];
const renderRow = item => {
const step = plan.steps.find(entry => entry.component.id === item.id);
const conflicts = ecosystemEvidenceConflictSummary(item);
const summaryLine = `${escHtml(item.kind)} ${'\u00B7'} console ${escHtml(item.installedVersion || 'unknown')}${ecosystemEvidenceSummary(item)} ${'\u00B7'} upstream ${escHtml(item.latestVersion || 'unknown')} ${'\u00B7'} locally staged ${escHtml(item.stagedVersion || 'no')}`;
const details = `${conflicts}${conflicts && (item.dependencies?.length || step?.blockers?.length || item.compatibility?.reason) ? ' \u00B7 ' : ''}` +
`${item.dependencies?.length ? `Depends on ${item.dependencies.map(escHtml).join(', ')}` : ''}${conflicts && item.dependencies?.length ? '. ' : ''}` +
`${step?.blockers?.length ? `Blocked: ${step.blockers.map(escHtml).join(' ')}` : item.compatibility?.reason || ''}`;
const actions = item.assets.length
? `${item.assets.map(asset => `${escHtml(asset.name)}${asset.verified ? ` ${ecosystemBullet} checksum` : ''}${asset.downloaded ? ` ${ecosystemBullet} local` : ''} `).join('')} ${['ahead', 'version-different'].includes(item.state) ? 'Downgrade blocked ' : 'Prepare safely '}`
: 'Sync to discover assets ';
const tracking = window.t(`ecosystem.payloadEvidence.mode.${item.trackingMode}`, item.trackingMode);
const automaticButton = item.manualEvidence ? `${escHtml(window.t('ecosystem.payloadEvidence.followAutomatic', 'Follow automatic'))} ` : '';
return `${escHtml(item.name)} ${ecosystemUpdateState(item)}
${summaryLine} ${escHtml(tracking)}${details ? ` ${ecosystemBullet} ${details}` : ''} ${escHtml(window.t('ecosystem.payloadEvidence.manualVersion', 'Manual version override'))} ${escHtml(window.t('ecosystem.payloadEvidence.useManual', 'Use manual'))} ${automaticButton}${actions}
`;
};
$('ecosystem-update-plan').innerHTML = groups.map(group => `
${escHtml(group.title)} ${escHtml(group.description)} ${group.items.length}
${group.items.length ? `${group.items.map(renderRow).join('')}
` : 'No components in this section.
'}
`).join('');
}
async function loadEcosystemUpdates() {
const [inventory, plan, policy, beaconArtifact] = await Promise.all([
window.pork.ecosystemInventory(),
window.pork.ecosystemUpdatePlan(),
window.pork.ecosystemSupplyPolicy(),
window.pork.chimeraBeaconArtifact(),
]);
$('ecosystem-repository-url').value = `${window.location.origin}/payloads.json`;
renderEcosystemUpdates(inventory, plan);
$('ecosystem-supply-official').checked = policy.officialSourcesOnly;
$('ecosystem-supply-https').checked = policy.requireHttps;
$('ecosystem-supply-hash').checked = policy.requirePublishedHashForExecution;
if (!state.connected) {
renderPayloadRuntimeUnavailable('The PS5 target is offline. Local update planning remains available above.');
return;
}
const timeout = (promise, ms) => Promise.race([
promise,
new Promise((_, reject) => setTimeout(() => reject(new Error('The PS5 runtime check timed out.')), ms)),
]);
try {
const [watchdog, runtimeUpdates] = await Promise.all([
timeout(window.pork.ecosystemPayloadWatchdog(), 4_000),
timeout(window.pork.ecosystemPayloadUpdates(), 4_000),
]);
renderPayloadRuntime(watchdog, runtimeUpdates, beaconArtifact);
} catch (error) {
renderPayloadRuntimeUnavailable(localizedEcosystemError(error.message));
}
}
function renderPayloadRuntimeUnavailable(message) {
$('ecosystem-watchdog').innerHTML = `Runtime status unavailable ${escHtml(message)} Review PS5 target
`;
$('ecosystem-runtime-updates').innerHTML = 'Safety boundary Catalog synchronization, version comparison and verified staging remain available locally. Runtime installation is never attempted while the PS5 target is offline.
';
}
function renderPayloadRuntime(watchdog, updates, beaconArtifact) {
$('ecosystem-watchdog').innerHTML = `${[
['Library', watchdog.manager?.installed || 0], ['Running', watchdog.stats?.running || 0], ['Autoload offline', watchdog.stats?.autoloadOffline || 0], ['Issues', watchdog.stats?.issues || 0], ['Updates', updates.stats?.updates || 0],
].map(([label, value]) => `
${escHtml(value)} ${escHtml(label)}
`).join('')}
${watchdog.services.map(service => `
${escHtml(service.name)} ${escHtml(service.filename || 'no payload file')} · port ${escHtml(service.port)}${service.installedVersion ? ` · file ${escHtml(service.installedVersion)}` : ''}${service.runtimeVersion ? ` · runtime ${escHtml(service.runtimeVersion)}` : ''}
${ecosystemStatus(service.running ? 'online' : service.state === 'autoload-offline' ? 'warning' : 'unknown', service.running ? 'Running' : service.autoload ? 'Autoload · offline' : service.installed ? 'Installed' : 'Not present')}
`).join('')}
${watchdog.issues?.length ? `Attention required ${watchdog.issues.map(issue => `${escHtml(issue.message)} `).join('')}
` : '✓ No autoload or runtime issues found.
'}`;
const actionable = updates.candidates.filter(item => item.status === 'update-available');
$('ecosystem-runtime-updates').innerHTML = `Chimera Sentinel ${escHtml(beaconArtifact.version)} · ${beaconArtifact.built ? 'build ready' : 'not built'} Paired read-only UDP ${beaconArtifact.port}: HMAC-SHA256, fixed bounded telemetry and visible legacy Beacon 0.1 detection. No writes, persistence, serial number, memory access or command execution. ${beaconArtifact.sha256 ? `SHA-256 ${escHtml(beaconArtifact.sha256)}` : 'The ELF appears after a complete container build.'} Safe Payload Manager update flow ${updates.stats.current} current · ${updates.stats.review} need manual review · ${updates.stats.newer} locally newer. Installing only adds the selected file; starting and restarting remain disabled.
${actionable.length ? `${actionable.map(item => `
${escHtml(item.installed.name)} ${escHtml(item.installed.version || 'unknown')} → ${escHtml(item.latest.version || 'unknown')} · ${escHtml(item.latest.sourceName)} ${escHtml(item.latest.description || '')}
Install selected update
`).join('')}
` : 'No safely comparable newer Payload Manager versions found.
'}${updates.recommendations?.length ? `Matching extensions · never installed automatically ${updates.recommendations.map(item => `
${escHtml(item.name)} ${escHtml(item.version || 'unknown version')} · ${escHtml(item.risk)} · ${escHtml(item.reason)}
${ecosystemStatus(item.risk === 'expert' ? 'warning' : 'unknown', 'Via Payload Manager')}
`).join('')}
` : ''}`;
}
function etaPatchFromForm() {
const patch = {};
$('ecosystem-etahen-fields').querySelectorAll('[data-eta-key]').forEach(input => { patch[input.dataset.etaKey] = input.type === 'checkbox' ? (input.checked ? 1 : 0) : Number(input.value); });
return patch;
}
function renderEtaDiff(result) {
_ecosystemEtaProposed = result.proposed;
$('ecosystem-etahen-diff').innerHTML = `${(result.warnings || []).map(warning => `âš ${escHtml(warning)}
`).join('')}${result.diff?.length ? result.diff.map(item => `${escHtml(item.label)} ${escHtml(item.before ?? '—')} → ${escHtml(item.after ?? '—')}
`).join('') : '✓ No changes.
'}`;
$('ecosystem-etahen-apply').disabled = !result.diff?.length;
}
async function loadEtaHen() {
const [config, history] = await Promise.all([window.pork.ecosystemEtaHenGet(), window.pork.ecosystemEtaHenHistory()]);
_ecosystemEtaHash = config.hash;
_ecosystemEtaProposed = null;
$('ecosystem-etahen-raw').value = config.raw;
const preset = $('ecosystem-etahen-preset');
preset.innerHTML = 'No preset ' + Object.entries(config.presets || {}).map(([id, value]) => `${escHtml(value.label)} `).join('');
$('ecosystem-etahen-fields').innerHTML = Object.entries(config.schema || {}).map(([key, definition]) => {
const value = config.values?.[key] ?? '0';
return `${escHtml(definition.label)}${definition.risk ? ` ${escHtml(definition.risk)} ` : ''} ${definition.type === 'bool' ? ` ` : ` `} `;
}).join('');
$('ecosystem-etahen-history').innerHTML = history.length ? `Restore points ${history.slice(0, 20).map(item => `${escHtml(item.label)} ${ecosystemDate(item.createdAt)} · ${item.diff?.length || 0} change(s)
Restore `).join('')}` : 'No configuration restore points yet.
';
$('ecosystem-etahen-diff').innerHTML = 'Choose a preset or change known fields, then create a preview.
';
$('ecosystem-etahen-apply').disabled = true;
}
function renderHomebrew(result) {
$('ecosystem-homebrew').innerHTML = result.items?.length ? result.items.map(item => `${item.iconUrl ? ` ` : 'HB
'}${escHtml(item.name)} ${escHtml(item.location)} · ${escHtml(item.executable)}${item.duplicate ? ' · duplicate' : ''} ${escHtml(item.path)}
Launch `).join('') : 'No homebrew found Start websrv or FTP and run a new scan.
';
}
function renderPacbrew(packet) {
_pacbrewPacket = packet;
const query = String($('ecosystem-pacbrew-search').value || '').trim().toLowerCase();
const kind = $('ecosystem-pacbrew-kind').value;
const filtered = (packet.packages || []).filter(item => (!query || item.name.toLowerCase().includes(query)) && (kind === 'all' || item.kind === kind));
const counts = Object.fromEntries(['application', 'service', 'library', 'toolchain'].map(type => [type, (packet.packages || []).filter(item => item.kind === type).length]));
$('ecosystem-pacbrew').innerHTML = `
${packet.warning ? `${packet.stale ? 'Cached catalog' : 'Fallback transport active'} ${escHtml(packet.warning)}
` : ''}
${filtered.length}/${packet.packages?.length || 0} Visible recipes
${counts.application || 0} Applications
${counts.library || 0} Libraries
${counts.service || 0} Services
Release ${escHtml(packet.release?.tag || 'unknown')} · checked ${ecosystemDate(packet.checkedAt)} · ${escHtml(packet.transport || 'cached')} · recipes are never executed
${filtered.slice(0, 500).map(item => `
${escHtml(item.name)} ${escHtml(item.kind)} · source ${escHtml(String(item.sourceSha || '').slice(0, 12))}
${ecosystemStatus('unknown', 'Recipe only')}Inspect metadata Open recipe
`).join('')}
`;
}
async function loadHomebrew() {
const [homebrew, pacbrew] = await Promise.all([window.pork.ecosystemHomebrewList(), window.pork.ecosystemPacbrewCatalog()]);
renderHomebrew(homebrew);
renderPacbrew(pacbrew);
}
function renderWebsrvDiscovery(result) {
const services = result.services || [];
$('ecosystem-websrv-discovery').innerHTML = services.length ? `${services.length} mDNS service(s) discovered by websrv ${services.map(service => {
const name = service.name || service.instance || service.hostname || service.host || 'Onbekende dienst';
const detail = [service.type, service.address || service.host, service.port].filter(Boolean).join(' · ');
return `${escHtml(name)} ${detail ? ` ${escHtml(detail)}` : ''} `;
}).join('')}
` : 'websrv found no mDNS services on the PS5 network.
';
}
function renderSources(sources) {
$('ecosystem-sources').innerHTML = sources.length ? sources.map(source => `${escHtml(source.label)} ${escHtml(source.protocol.toUpperCase())} · ${escHtml(source.host)}:${source.port}${escHtml(source.path)} · password ${source.passwordConfigured ? 'configured' : 'not configured'}
Test Edit Delete
`).join('') : 'No remote sources configured yet.
';
}
function renderEzRemote(packet) {
$('ezremote-status').innerHTML = packet.reachable
? `Online · ${escHtml(packet.version || 'unknown version')} ${escHtml(packet.host)}:${packet.port} · resume after server restart supported · pause/cancel unsupported `
: `Offline op ${escHtml(packet.host || 'PS5')}:${packet.port || 6701} ${escHtml(packet.error || 'Start ezRemote Server op de PS5.')} `;
const rows = packet.downloads || [];
$('ezremote-downloads').innerHTML = rows.length ? `${rows.slice(0, 100).map(item => `
${escHtml(item.path || 'Unknown destination')} ${ecosystemBytes(item.transferred)} / ${item.size ? ecosystemBytes(item.size) : '?'} · ${item.percent}%
${ecosystemStatus(item.state === 'success' ? 'online' : item.state === 'failed' ? 'offline' : 'warning', item.state)}
`).join('')}
` : 'ezRemote Server reports no downloads yet.
';
}
async function loadSources() {
const [sources, status] = await Promise.all([window.pork.ecosystemSources(), window.pork.ezRemoteStatus()]);
renderSources(sources);
const select = $('ezremote-source'); const selected = select.value;
select.innerHTML = 'Choose a source ' + sources.filter(source => source.enabled !== false).map(source => `${escHtml(source.label)} · ${escHtml(source.protocol.toUpperCase())} `).join('');
if (sources.some(source => source.id === selected)) select.value = selected;
renderEzRemote(status);
}
function renderPlugins(result, catalog = []) {
const summary = `${result.items?.length || 0} plugins · ${(result.roots || []).length} available locations Last scan: ${result.scannedAt ? ecosystemDate(result.scannedAt) : 'not yet'}. ${result.skippedUnmountedUsbRoots || 0} unmounted USB locations were skipped without generating errors.
`;
const installed = result.items?.length
? result.items.map(item => `${escHtml(item.name)} ${escHtml(item.location)} · ${ecosystemBytes(item.size)}${item.duplicate ? ' · duplicate' : ''}${item.effective ? ' · highest active priority' : ''} ${escHtml(item.path)}
${ecosystemStatus(item.enabled ? 'compatible' : 'unknown', item.enabled ? 'Enabled' : 'Disabled')}Inspect ${item.enabled ? 'Disable' : 'Enable'}
`).join('')
: 'No installed plugins found Choose a reviewed source below or upload a local ELF/PRX through the guarded installer.
';
const reviewed = catalog.length
? `${catalog.map(item => `
${escHtml(item.name)} ${escHtml(item.platform)} · ${escHtml(item.trust)} · ${escHtml(item.release?.tag || 'version unknown')}
${escHtml(item.compatibility?.reason || 'Review the upstream release before installing.')}
Review upstream `).join('')}
`
: 'No reviewed plugin sources are present in the component catalog.
';
$('ecosystem-plugins').innerHTML = `${summary}Installed plugins ${installed}${reviewed}`;
}
async function loadPlugins() {
const [plugins, components] = await Promise.all([
window.pork.ecosystemPlugins(),
window.pork.ecosystemComponents().catch(() => ({ components: [] })),
]);
const catalog = (components.components || []).filter(item => /plugin/i.test(`${item.kind} ${item.name}`));
renderPlugins(plugins, catalog);
}
async function loadCheatsV2() {
const gameContext = consumeGameWorkspaceContext('cheats');
if (gameContext) { $('ecosystem-cheats-search').value = gameContext; _ecosystemCheatOffset = 0; }
const request = ++_ecosystemCheatRequest;
const filters = { query: $('ecosystem-cheats-search').value, platform: $('ecosystem-cheats-platform').value, kind: $('ecosystem-cheats-kind').value, installedOnly: $('ecosystem-cheats-installed').checked, limit: ECOSYSTEM_CHEAT_PAGE_SIZE, offset: _ecosystemCheatOffset };
const result = await window.pork.ecosystemCheats(filters);
if (request !== _ecosystemCheatRequest) return;
_ecosystemCheatOffset = result.offset || 0;
const first = result.total ? _ecosystemCheatOffset + 1 : 0;
const last = _ecosystemCheatOffset + result.items.length;
const list = result.items.length
? `${result.items.map(item => {
const match = item.compatibility === 'exact'
? ecosystemStatus('compatible', 'Exact version')
: item.compatibility === 'title-match'
? ecosystemStatus('compatible', 'Title match')
: item.compatibility === 'version-mismatch'
? ecosystemStatus('warning', 'Version mismatch')
: item.compatibility === 'version-unknown'
? ecosystemStatus('unknown', 'Version unknown')
: ecosystemStatus('unknown', 'Not local');
return `
${escHtml(item.installedTitle || item.title)} ${escHtml(item.gameId || 'Unlinked')} · ${escHtml(item.platform)} · ${escHtml(item.kind)} · ${escHtml(item.format.toUpperCase())} · ${escHtml(item.sourceName)} ${item.version ? `Target version ${escHtml(item.version)}` : 'No target version supplied'}${item.installedVersion ? ` · installed ${escHtml(item.installedVersion)}` : ''}${item.conflict ? ' · multiple formats available' : ''}
${match}Source ${item.deployable ? 'Install ' : `View only `}
`;
}).join('')}
`
: 'No cheats or patches match Adjust the filters or synchronize the reviewed sources to refresh the catalog.
';
const compatibility = result.compatibility || {};
$('ecosystem-cheats').innerHTML = `${first}–${last} of ${result.total} results · ${Number(compatibility.exact || 0) + Number(compatibility.titleMatch || 0)} recommended · ${Number(compatibility.versionMismatch || 0)} mismatched · synced ${ecosystemDate(result.syncedAt)}
Previous Next
${(result.errors || []).map(error => `${escHtml(error.sourceId)}: ${escHtml(error.error)}
`).join('')}${list}`;
}
function renderRemoteSaves(result) {
const groups = new Map();
for (const save of result.saves || []) { const id = String(save.title_id || 'UNKNOWN'); if (!groups.has(id)) groups.set(id, { titleId: id, title: save.title_name || id, saves: [] }); groups.get(id).saves.push(save); }
$('ecosystem-remote-saves').innerHTML = groups.size ? [...groups.values()].map(group => `${escHtml(group.title)} ${escHtml(group.titleId)} · ${group.saves.length} save(s) · ${group.saves.some(save => save.type === 'ps5') ? 'PS5' : 'PS4'}
Snapshot `).join('') : 'No saves found.
';
}
async function loadSaveVault() {
const gameContext = consumeGameWorkspaceContext('saves');
const [vaultResult, capabilitiesResult, gamesResult] = await Promise.allSettled([
window.pork.ecosystemSaveVault(),
window.pork.ecosystemSaveRestoreCapabilities(),
window.pork.listGames({}),
]);
const allSnapshots = vaultResult.status === 'fulfilled' && Array.isArray(vaultResult.value) ? vaultResult.value : [];
const capabilities = capabilitiesResult.status === 'fulfilled' && capabilitiesResult.value
? capabilitiesResult.value
: { automatedRestore: false, error: capabilitiesResult.reason?.message || 'Restore capabilities could not be verified.' };
const snapshots = gameContext ? allSnapshots.filter(item => item.titleId === gameContext) : allSnapshots;
const gamesById = new Map((gamesResult.status === 'fulfilled' ? gamesResult.value : []).map(game => [game.game_id, game]));
_ecosystemSaveCapabilities = capabilities;
$('ecosystem-save-restore-safety').innerHTML = capabilities.automatedRestore
? 'Protected PS5 restore available PPSA archives only. Mandatory SHA-256 and CRC dry run, target-user validation, pre-restore snapshot, exact confirmation and byte-for-byte post-check. Resign, forced import and automatic rollback remain disabled. '
: `Restore remains blocked ${escHtml(localizedEcosystemError(capabilities.reason || capabilities.error || 'Garlic does not prove the required import capabilities.'))} `;
const vaultFailure = vaultResult.status === 'rejected'
? `Local vault unavailable ${escHtml(localizedEcosystemError(vaultResult.reason?.message || 'The local snapshot index could not be loaded.'))}
`
: '';
$('ecosystem-save-vault').innerHTML = `${vaultFailure}${gameContext ? `Game workspace ${escHtml(gameContext)} Only snapshots for this game are shown. Refresh the page to see the complete Save Vault.
` : ''}${snapshots.length ? snapshots.map(item => {
const game = gamesById.get(item.titleId);
const title = game?.prospero_name || game?.title || item.titleId;
return `${game?.prospero_icon_url ? `
` : ''}
${escHtml(title)} ${escHtml(item.titleId)} · ${ecosystemDate(item.createdAt)} · ${ecosystemBytes(item.bytes)}${item.purpose ? ` · ${escHtml(item.purpose)}` : ''} ${escHtml(String(item.sha256).slice(0, 18))}…
Verify Restore plan Download Delete
`;
}).join('') : 'No snapshots yet Create a verified snapshot after console saves have been discovered.
'}`;
}
function renderTimeline(result) {
const state = result.state || {};
const options = result.options || {};
$('ecosystem-timeline-persist').checked = options.enabled === true;
$('ecosystem-timeline-retention').value = options.retentionDays || 14;
$('ecosystem-timeline-max').value = options.maxEvents || 5000;
$('ecosystem-timeline-state').innerHTML = `${state.connected ? `Connected on port ${state.port} since ${ecosystemDate(state.startedAt)}` : state.error ? `Not connected: ${escHtml(state.error)}` : 'Not connected.'} · ${result.filtered || 0}/${result.total || 0} events · persistence ${options.enabled ? 'on' : 'off'}
`;
$('ecosystem-timeline-events').innerHTML = (result.events || []).slice().reverse().map(event => `${new Date(event.at).toLocaleTimeString()} ${escHtml(event.source)} ${escHtml(event.message)}
`).join('') || 'No events yet.
';
}
function renderGameUpdateJobs(jobs) {
$('game-updates-jobs').innerHTML = jobs.length ? jobs.map(job => `${escHtml(job.manifest.gameId)} · ${escHtml(job.manifest.filename)} ${escHtml(job.status)} · ${Number(job.progress || 0)}% · ${ecosystemBytes(job.bytesDownloaded || job.manifest.originalFileSize)} ${job.error ? `${escHtml(job.error)} ` : job.remotePath ? `${escHtml(job.remotePath)}` : job.localPath ? `${escHtml(job.localPath)}` : ''}
${ecosystemStatus(job.status === 'completed' ? 'compatible' : job.status === 'failed' ? 'warning' : 'unknown', job.status)}${['queued', 'downloading', 'verifying'].includes(job.status) ? 'Cancel ' : ''}${job.status === 'completed' ? 'Create HTTPS link Stage for PS5 ' : ''}${!['queued', 'downloading', 'verifying', 'staging'].includes(job.status) ? 'Delete ' : ''}
`).join('') : 'No game update jobs yet.
';
}
async function loadGameUpdates() {
const [catalog, jobs] = await Promise.all([window.pork.gameUpdatesCatalog(), window.pork.gameUpdatesJobs()]);
const discovered = catalog.games.filter(game => game.discovery?.status === 'available').length;
const updateStateLabel = state => window.t(`ecosystem.gameUpdates.state.${state}`, state);
const evidenceRows = catalog.games.map(game => {
const locations = (game.locations || []).map(location => {
const observed = location.observedAt ? new Date(location.observedAt).toLocaleString() : window.t('ecosystem.gameUpdates.evidenceUnknown', 'evidence time unknown');
const label = location.volumeLabel || location.location || location.volumeId || window.t('ecosystem.gameUpdates.locationUnknown', 'unknown location');
return `${escHtml(label)} · ${escHtml(location.version || '?')} · ${escHtml(location.source || 'unknown')} (${escHtml(location.confidence || 'unknown')}) · ${escHtml(observed)} · ${escHtml(updateStateLabel(location.status))} `;
}).join('');
return `${escHtml(game.title)} ${escHtml(game.gameId)} · ${escHtml(game.latestVersion || '?')} · ${escHtml(game.sourceEvidence?.source || 'unknown')}${game.discovery?.reason ? ` · ${escHtml(game.discovery.reason)}` : ''} ${locations || `${escHtml(window.t('ecosystem.gameUpdates.noConfirmedLocations', 'No confirmed console locations'))} `}
${ecosystemStatus(game.updateAvailable ? 'warning' : game.status === 'current' ? 'compatible' : 'unknown', updateStateLabel(game.status))}
`;
}).join('');
$('game-updates-summary').innerHTML = `${[[window.t('ecosystem.gameUpdates.gameCount', 'Games'), catalog.stats.total], [window.t('ecosystem.gameUpdates.officialManifests', 'Official manifests'), discovered], [window.t('ecosystem.gameUpdates.available', 'Possible updates'), catalog.stats.updates]].map(([label, value]) => `
${escHtml(value)} ${escHtml(label)}
`).join('')}
${escHtml(window.t('ecosystem.gameUpdates.discoveryEvidence', 'Library discovery evidence'))} · ${catalog.games.length} ${evidenceRows}
`;
$('game-updates-game').innerHTML = `${escHtml(window.t('ecosystem.gameUpdates.selectGame', 'Select a library game'))} ` + catalog.games.map(game => `${escHtml(game.gameId)} · ${escHtml(game.title)} · ${escHtml(game.discovery?.status || 'not checked')} `).join('');
renderGameUpdateJobs(jobs);
}
function renderGameUpdatePlan(plan) {
_gameUpdatePlan = plan;
$('game-updates-plan').innerHTML = `${escHtml(plan.manifest.gameId)} · ${escHtml(plan.manifest.filename)} ${ecosystemBytes(plan.manifest.originalFileSize)} · ${plan.manifest.pieces.length} part(s) · free space ${ecosystemBytes(plan.availableBytes)} · ${plan.manifest.packageDigest ? 'complete SHA-256 available' : 'no complete SHA-256 in manifest'} Start verified download `;
$('game-updates-download-confirm')?.addEventListener('click', async click => {
if (!await showConfirm('Download this official Sony package to Chimera backup storage? Every part and the complete package will be verified. Nothing is installed on the PS5.')) return;
try { await ecosystemBusy(click.currentTarget, () => window.pork.gameUpdatesDownload(plan.token)); _gameUpdatePlan = null; showToast('Game update job started.', 'ok'); await loadGameUpdates(); }
catch (error) { showToast(error.message, 'error', 10000); }
});
}
function renderReadonlyFiles(targetId, packet, kind) {
const rows = packet.roots?.length ? packet.roots : packet.items || [];
$(targetId).innerHTML = rows.length ? rows.map(item => `${item.isDirectory ? 'ðŸ“' : '📄'} ${escHtml(item.name)} ${item.isDirectory ? escHtml(item.mode || 'folder') : ecosystemBytes(item.size)}${item.mtime ? ` · ${ecosystemDate(item.mtime * 1000)}` : ''}
${item.isDirectory ? 'Open' : 'Download'} `).join('') : 'This folder is empty.
';
const pathTarget = kind === 'smb' ? 'websrv-smb-path' : 'websrv-files-path';
$(pathTarget).innerHTML = `${escHtml(packet.path || (kind === 'smb' ? 'SMB-root' : '/'))} Read-only · ${rows.length} item(s) ${packet.parent !== null && packet.parent !== undefined ? `Omhoog ` : ''}`;
}
async function loadWebsrvFiles(remotePath = _websrvFsPath) {
const packet = await window.pork.ecosystemWebsrvFiles(remotePath);
_websrvFsPath = packet.path;
renderReadonlyFiles('websrv-files-list', packet, 'fs');
}
async function loadWebsrvSmb(smbPath = _websrvSmbPath) {
const sourceId = $('websrv-smb-source').value;
if (!sourceId) return;
const packet = await window.pork.ecosystemWebsrvSmb(sourceId, smbPath);
_websrvSmbPath = packet.path;
renderReadonlyFiles('websrv-smb-list', packet, 'smb');
}
async function loadFilesTab() {
const sources = (await window.pork.ecosystemSources()).filter(source => source.protocol === 'smb');
const select = $('websrv-smb-source'); const selected = select.value;
select.innerHTML = 'Choose a source ' + sources.map(source => `${escHtml(source.label)} · ${escHtml(source.host)} `).join('');
if (sources.some(source => source.id === selected)) select.value = selected;
if (!sources.length) {
$('websrv-smb-path').innerHTML = 'No SMB sources configured Add a reviewed network source before browsing it through the PS5. ';
$('websrv-smb-list').innerHTML = 'Connect a storage source Sources are managed centrally so arbitrary hosts never appear here. Configure Sources
';
}
await loadWebsrvFiles(_websrvFsPath);
}
async function loadLiveSession() {
const packet = await window.pork.ecosystemLiveSession();
$('ecosystem-live-session').innerHTML = packet.online ? `Live telemetry available ${escHtml(packet.branding || 'ps5debug-NG')} · ${escHtml(packet.host)}:${escHtml(packet.port)} · checked ${ecosystemDate(packet.checkedAt)}
${ecosystemStatus('online', 'Read-only')}
${[['Protocol', packet.protocol || '?'], ['Firmware', packet.firmware || '?'], ['Platform', packet.platform === 5 ? 'PS5' : packet.platform || '?']].map(([label, value]) => `
${escHtml(value)} ${escHtml(label)}
`).join('')}
${packet.foreground ? `
Foreground application ${escHtml(packet.foreground.game?.title || packet.foreground.titleId)} ${escHtml(packet.foreground.titleId)} · v${escHtml(packet.foreground.version || '?')} · PID ${escHtml(packet.foreground.pid)}
${escHtml(packet.foreground.contentId || packet.foreground.processName)}` : '
Foreground application No game active The console service is online and ready to report the next foreground title.
'}
✓ Only five reviewed read-only commands are registered. Memory, attach, kernel, notification and reboot are not exposed.
` : `1 ps5debug-NG 1.3.0 installed Verified official release staged through Payload Manager
2 Start payload when needed Port ${escHtml(packet.port)} must be reachable
3 Read session Use the button above to retry
Live telemetry unavailable ${escHtml(packet.error || 'Service offline.')}
`;
}
function remotePlaySelectedWorker(packet) {
return (packet.workers || []).find(worker => worker.id === ($('remote-play-worker')?.value || packet.selectedWorkerId)) || null;
}
function renderRemotePlay(packet, companion) {
_remotePlayPacket = packet;
const discovery = packet.lastDiscovery || {};
const workers = packet.workers || [];
const selectedId = $('remote-play-worker')?.value || packet.selectedWorkerId || '';
const selected = workers.find(worker => worker.id === selectedId) || workers[0] || null;
const health = selected?.streamHealth || null;
const clientName = packet.clientNickname || discovery.console?.name || '';
const registered = Boolean(selected?.registeredHosts?.includes(clientName));
const consoleReady = discovery.state === 'ready';
const consoleStandby = discovery.state === 'standby';
const checks = [
{ label: 'PS5', ok: consoleReady, partial: consoleStandby, value: consoleReady ? window.t('remotePlay.state.ready', 'Ready') : consoleStandby ? window.t('remotePlay.state.standby', 'Rest mode') : discovery.online ? discovery.state : window.t('remotePlay.state.notDiscovered', 'Not discovered') },
{ label: 'Worker', ok: selected?.online, value: selected?.online ? `${selected.label} online` : selected ? 'Offline' : window.t('remotePlay.state.notPaired', 'Not paired') },
{ label: 'chiaki-ng', ok: selected?.client?.installed, value: selected?.client?.installed ? `${selected.client.version || window.t('remotePlay.state.installed', 'Installed')}${selected.client.managed ? ` · ${window.t('remotePlay.state.managed', 'Chimera-managed')}` : ''}` : window.t('remotePlay.state.notFound', 'Not found') },
{ label: window.t('remotePlay.registration', 'Registration'), ok: registered, value: registered ? clientName : clientName ? window.tf('remotePlay.registrationMissing', '{name} is not registered locally', { name: clientName }) : window.t('remotePlay.consoleNameMissing', 'Console name missing') },
];
$('remote-play-readiness').innerHTML = checks.map(check => `${escHtml(check.label)} ${escHtml(check.value)}
`).join('');
if (document.activeElement !== $('remote-play-nickname')) $('remote-play-nickname').value = packet.nickname || '';
if (document.activeElement !== $('remote-play-client-nickname')) $('remote-play-client-nickname').value = clientName;
$('remote-play-key').value = '';
$('remote-play-display-mode').value = packet.displayMode || 'fullscreen';
$('remote-play-worker').innerHTML = workers.length ? workers.map(worker => `${escHtml(worker.label)} · ${worker.online ? 'online' : 'offline'} `).join('') : `${escHtml(window.t('remotePlay.worker.none', 'No worker yet'))} `;
$('remote-play-worker').value = selected?.id || '';
$('remote-play-status').innerHTML = `${escHtml(packet.registrationKeyConfigured ? window.t('remotePlay.profile.secured', 'Wake profile secured') : window.t('remotePlay.profile.keyMissing', 'Wake key missing'))} ${escHtml(packet.nickname)} · ${escHtml(packet.host || window.t('remotePlay.profile.noHost', 'no PS5 host'))} · UDP ${packet.port} · ${escHtml(window.t('remotePlay.profile.sessionPorts', 'session ports'))} ${(packet.sessionPorts || []).join(', ')} ${escHtml(window.t('remotePlay.profile.registrationKey', 'registration key'))} ${escHtml(packet.registrationKeyConfigured ? window.t('remotePlay.profile.keyEncrypted', 'AES-256-GCM encrypted and never returned') : window.t('remotePlay.profile.keyWakeOnly', 'needed only to wake from rest mode'))} `;
$('remote-play-workers').innerHTML = workers.length ? workers.map(worker => `${escHtml(worker.label)} ${worker.online ? `${escHtml(worker.hostname || worker.platform || 'worker')} · ${worker.client?.installed ? `chiaki-ng ${escHtml(worker.client.version || 'found')}${worker.client.managed ? ' · managed' : ''}` : 'chiaki-ng missing'}` : worker.enrollmentPending ? 'Waiting for installation and first pairing' : `Offline${worker.lastSeenAt ? ` · last ${ecosystemDate(worker.lastSeenAt)}` : ''}`}
${ecosystemStatus(worker.online ? 'online' : worker.enrollmentPending ? 'unknown' : 'offline', worker.online ? 'Online' : worker.enrollmentPending ? 'Install' : 'Offline')}Delete
`).join('') : 'No local worker yet Download the package above on the device where you want to play.
';
const healthLabel = health?.status === 'healthy' ? window.t('remotePlay.health.healthy', 'Healthy') : health?.status === 'degraded' ? window.t('remotePlay.health.degraded', 'Degraded') : window.t('remotePlay.health.attention', 'Attention');
const healthTone = health?.status === 'healthy' ? 'online' : health?.status === 'degraded' ? 'error' : 'warning';
const healthMarkup = health ? `Last stream · ${escHtml(healthLabel)} ${escHtml(health.resolution || 'resolution unknown')} · ${escHtml(health.decoder || 'decoder unknown')}${health.rttMs != null ? ` · RTT ${escHtml(health.rttMs)} ms` : ''} · ${ecosystemDate(health.checkedAt)} Packet/FEC errors ${escHtml(health.packetErrors)} · duplicates ${escHtml(health.duplicateUnits)} · decoder buffer drops ${escHtml(health.decoderDrops)} ${ecosystemStatus(healthTone, healthLabel)}
` : '';
const workflowCopy = {
'Console ontdekken': [window.t('remotePlay.workflow.discovery', 'Discover console'), window.t('remotePlay.workflow.discoveryDetail', 'LAN discovery through the upstream PS5 protocol on UDP 9302.')],
Wakeprofiel: [window.t('remotePlay.workflow.wakeProfile', 'Wake profile'), window.t('remotePlay.workflow.wakeDetail', 'Wake uses only the encrypted rp_regist_key.')],
'Lokale worker': [window.t('remotePlay.workflow.worker', 'Local worker'), window.t('remotePlay.workflow.workerDetail', 'The HMAC-paired worker manages and starts only a hash-verified chiaki-ng client.')],
'chiaki-ng geregistreerd': [window.t('remotePlay.workflow.registered', 'chiaki-ng registered'), window.t('remotePlay.workflow.registeredDetail', 'The PSN Account ID and Link Device PIN remain only in the local chiaki-ng configuration.')],
};
$('remote-play-companion').innerHTML = `${(companion.workflow || []).map(step => {
const translated = workflowCopy[step.id] || [step.id, step.detail];
return `
${escHtml(translated[0])} ${escHtml(translated[1])}
${ecosystemStatus(step.ready ? 'online' : 'unknown', step.ready ? window.t('remotePlay.state.ready', 'Ready') : window.t('remotePlay.state.configure', 'Configure'))}
`;
}).join('')}
${healthMarkup}${escHtml(window.t('remotePlay.native.title', 'Why native?'))} ${escHtml(window.t('remotePlay.native.reason', 'Video, audio, controller and haptics run natively on the device where chiaki-ng and the controller are present; Chimera orchestrates and monitors the session.'))} ${escHtml(window.t('remotePlay.native.detail', 'The worker has no shell, receives no PS5 registration key and can perform only fixed chiaki-ng actions.'))}
`;
const jobStatuses = { queued: window.t('remotePlay.jobs.queued', 'Queued'), dispatched: window.t('remotePlay.jobs.dispatched', 'Dispatched…'), completed: window.t('remotePlay.jobs.completed', 'Completed'), failed: window.t('remotePlay.jobs.failed', 'Failed'), expired: window.t('remotePlay.jobs.expired', 'Expired'), cancelled: window.t('remotePlay.jobs.cancelled', 'Cancelled') };
$('remote-play-jobs').innerHTML = packet.jobs?.length ? `${packet.jobs.map(job => `
${escHtml(job.action)} ${ecosystemDate(job.createdAt)}${job.result?.message ? ` · ${escHtml(job.result.message)}` : job.error ? ` · ${escHtml(job.error)}` : ''}
${ecosystemStatus(job.status === 'completed' ? 'online' : job.status === 'failed' ? 'error' : job.status === 'expired' ? 'offline' : 'unknown', jobStatuses[job.status] || job.status)}
`).join('')}
` : `${escHtml(window.t('remotePlay.sessions.empty', 'No Remote Play action has run yet.'))}
`;
const blockers = [];
if (!selected?.online) blockers.push('worker offline');
if (!selected?.client?.installed) blockers.push(window.t('remotePlay.blocker.clientMissing', 'chiaki-ng missing'));
if (!registered) blockers.push(window.t('remotePlay.blocker.notRegistered', 'console not registered locally'));
if (!consoleReady && !(consoleStandby && packet.registrationKeyConfigured)) blockers.push(window.t('remotePlay.blocker.notReady', 'PS5 not ready'));
$('remote-play-start').disabled = blockers.length > 0;
$('remote-play-start-hint').textContent = blockers.length
? window.tf('remotePlay.blocker.prefix', 'Still needed: {items}.', { items: blockers.join(' · ') })
: consoleStandby
? window.t('remotePlay.ready.wakeFirst', 'Chimera will confirm wake first and then start the local stream.')
: window.t('remotePlay.ready.start', 'All checks passed; the stream opens locally on the selected worker.');
$('remote-play-wake').disabled = !packet.registrationKeyConfigured || consoleReady;
$('remote-play-diagnose').disabled = !selected?.online;
$('remote-play-open-client').disabled = !selected?.online || !selected?.client?.installed;
$('remote-play-install-client').disabled = !selected?.online || selected?.client?.installed;
$('remote-play-update-client').disabled = !selected?.online || !selected?.client?.installed;
$('remote-play-stop').disabled = !selected?.online || !selected?.session?.active;
}
async function loadRemotePlay({ discover = false, schedule = true } = {}) {
if (discover) await window.pork.remotePlayDiscover().catch(() => null);
const [packet, companion] = await Promise.all([window.pork.remotePlayStatus(), window.pork.remotePlayCompanion()]);
renderRemotePlay(packet, companion);
clearTimeout(_remotePlayPollTimer);
if (schedule && _ecosystemActiveTab === 'remote-play') _remotePlayPollTimer = setTimeout(() => loadRemotePlay({ schedule: true }).catch(error => showToast(error.message, 'error')), 3_000);
}
async function loadItemzflow() {
_itemzflowPacket = await window.pork.ecosystemItemzflowStatus();
const packet = _itemzflowPacket;
$('itemzflow-summary').innerHTML = `${[['Logs', `${packet.health?.available || 0}/${packet.logs?.length || 0}`], ['Errors', packet.health?.errors || 0], ['Warnings', packet.health?.warnings || 0], ['Games', packet.games?.installed || 0], ['Progress', packet.latestProgress?.progress != null ? `${packet.latestProgress.progress}%` : 'None active']].map(([label, value]) => `
${escHtml(value)} ${escHtml(label)}
`).join('')}
${packet.error ? `${escHtml(localizedEcosystemError(packet.error))}
` : `${packet.lines} lines from ${packet.health?.available || 0} logs · ${ecosystemDate(packet.checkedAt)}
`}${(packet.logs || []).map(log => `
${escHtml(log.label)} ${escHtml(log.path)} · ${log.lines || 0} lines
${ecosystemStatus(log.online ? (log.errorCount ? 'warning' : 'online') : 'offline', log.online ? `${log.errorCount || 0} errors · ${log.warningCount || 0} warnings` : 'Not present')}
`).join('')}
${packet.nfsSources?.length ? `NFS profiles ${packet.nfsSources.map(source => `${escHtml(source.label)} · ${escHtml(source.host)}:${source.port}${escHtml(source.path)}`).join(' · ')}
` : ''}`;
const selected = $('itemzflow-log-select').value;
$('itemzflow-log-select').innerHTML = 'All logs ' + (packet.logs || []).map(log => `${escHtml(log.label)}${log.online ? '' : ' · missing'} `).join('');
if ((packet.logs || []).some(log => log.id === selected)) $('itemzflow-log-select').value = selected;
renderItemzflowEvents();
}
function renderItemzflowEvents() {
if (!_itemzflowPacket) return;
const source = $('itemzflow-log-select').value;
const level = $('itemzflow-level').value;
const jobsOnly = $('itemzflow-jobs-only').checked;
const pool = jobsOnly ? _itemzflowPacket.events || [] : _itemzflowPacket.recentLogs || [];
const events = pool.filter(event => (source === 'all' || event.source === source) && (level === 'all' || event.level === level));
$('itemzflow-events').innerHTML = events.map(event => `${escHtml(event.sourceLabel || event.source)} ${escHtml(event.type)} · ${escHtml(event.message)} ${event.progress != null ? `${event.progress}%` : `${escHtml(event.level)}`}
`).join('') || 'No events match these filters.
';
}
async function loadLinuxLab() {
const packet = await window.pork.ecosystemLinuxLab();
const state = packet.status === 'compatible' ? 'Firmware explicitly supported' : packet.status === 'incompatible' ? 'Firmware unsupported' : 'Firmware unknown';
$('ecosystem-linux-summary').innerHTML = `${[['Status', state], ['Profile', packet.firmware || 'Not entered'], ['Models', packet.supportedModels.join(' + ')], ['Execution', 'Blocked']].map(([label, value]) => `
${escHtml(value)} ${escHtml(label)}
`).join('')}
Source reviewed ${escHtml(packet.sourceReviewedAt)} · supported firmware: ${packet.supportedFirmwares.map(escHtml).join(', ')}
`;
$('ecosystem-linux-checks').innerHTML = (packet.requirements || []).map(check => `${escHtml(check.label)} ${escHtml(check.detail)}
${ecosystemStatus(check.status, check.status === 'compatible' ? 'Satisfied' : check.status === 'incompatible' ? 'Incompatible' : check.status === 'warning' ? 'Disable manually' : 'Check manually')}
`).join('');
$('ecosystem-linux-components').innerHTML = (packet.components || []).map(component => `${escHtml(component.name)} ${escHtml(component.release?.tag || 'release unknown')} · ${escHtml(component.risk)}
Upstream `).join('');
$('ecosystem-linux-policy').innerHTML = 'All dangerous actions are intentionally absent No download, payload sender, exploit, boot, block-device write or automatic HEN/kstuff change. Use the official upstream guide outside Chimera and only after every preflight condition has been verified manually. ';
}
async function loadTimeline() { renderTimeline(await window.pork.ecosystemTimeline({ search: $('ecosystem-timeline-search')?.value || '', level: $('ecosystem-timeline-level')?.value || '', limit: 1000 })); }
let _platformLabState = { artifacts: [], launchPlan: null };
function platformLabText(key, fallback) { return window.t(`platformLab.${key}`, fallback); }
function platformLabError(id, error) {
const target = $(id);
if (target) target.innerHTML = `${escHtml(platformLabText('unavailable', 'Unavailable'))} ${escHtml(error?.message || String(error))}
`;
}
function renderPlatformBindings(data) {
$('platform-bindings').innerHTML = (data.bindings || []).map(binding => `${escHtml(binding.label)} ${escHtml(binding.repository)} · ${escHtml(binding.latestTag || platformLabText('notChecked', 'not checked'))} · ${binding.assets?.length || 0} assets
${escHtml(binding.state)} ${binding.latestTag ? `${escHtml(platformLabText('review', 'Review'))} ` : ''}
`).join('') || `${escHtml(platformLabText('empty', 'No results.'))}
`;
}
function renderAutoloader(data) {
const profiles = [...(data.presets || []), ...(data.profiles || [])];
_platformLabState.profiles = profiles;
$('platform-autoloader-profile').innerHTML = profiles.map(profile => `${escHtml(profile.name)} · ${escHtml((profile.entrypoints || []).join('/'))} `).join('');
const selected = profiles[0] || { id: 'custom-profile', name: 'Custom profile', entrypoints: ['y2jb'], titleIds: [], payloads: ['pldmgr.elf'], firmware: null, youtubeVersion: null };
$('platform-profile-json').value = JSON.stringify(selected, null, 2);
$('platform-autoloader-output').innerHTML = `${escHtml(platformLabText('autoloader.safe', 'Profiles are preview-only; no USB or console writes are performed.'))}
`;
}
function renderAutoloaderPreview(data) {
$('platform-autoloader-output').innerHTML = `${escHtml(data.profile.name)} ${escHtml(data.directory)} · ${escHtml((data.profile.entrypoints || []).join(', '))}
${escHtml(data.blocked ? platformLabText('blocked', 'blocked') : platformLabText('previewOnly', 'preview only'))} ${escHtml(data.autoloadText)} ${(data.compatibility || []).map(item => `${escHtml(item.message)}
${escHtml(item.state)} `).join('')}${(data.warnings || []).map(warning => `${escHtml(warning)}
`).join('')}`;
}
function discoveryCount(value) { return Array.isArray(value) ? value.length : value && typeof value === 'object' ? Object.keys(value).length : 0; }
function renderPlatformWebsrv(data) {
$('platform-websrv').innerHTML = `${discoveryCount(data.mdns)} mDNS
${data.capabilities?.smb ? '✓' : '−'} SMB
${data.capabilities?.filesystem ? '✓' : '−'} Filesystem
${(data.errors || []).map(error => `${escHtml(error)}
`).join('')}${(data.blockedExternal || []).map(item => `${escHtml(item.capability)}: ${escHtml(item.reason)}
`).join('')}${escHtml(platformLabText('readOnly', 'Read-only discovery'))}
`;
}
function renderRemoteIntelligence(data) {
const workers = data.workers || [];
$('platform-remote').innerHTML = `${data.counts?.online || 0} ${escHtml(platformLabText('online', 'online'))}
${data.counts?.updates || 0} ${escHtml(platformLabText('updates', 'updates'))}
${data.counts?.degraded || 0} ${escHtml(platformLabText('attention', 'attention'))}
${workers.map(worker => `${escHtml(worker.name || worker.id)} ${escHtml(worker.version || platformLabText('unknownVersion', 'unknown version'))} · ${escHtml((worker.recommendations || []).join(', ') || platformLabText('healthy', 'healthy'))}
${escHtml(worker.updateState)} `).join('') || `${escHtml(platformLabText('noWorkers', 'No Remote Play workers paired.'))}
`}`;
}
function renderShadps4Roots(data) {
$('platform-shadps4-root').innerHTML = `${escHtml(platformLabText('selectRoot', 'Select library root'))} ${(data.roots || []).map(root => `${escHtml(root.label)} `).join('')}`;
}
function renderShadps4Scan(data) {
if (data.status === 'not-configured') { $('platform-shadps4').innerHTML = `${escHtml(platformLabText('selectRootHint', 'Select a semantic root to scan.'))}
`; return; }
const entries = [...(data.copies || []), ...(data.archives || [])];
$('platform-shadps4').innerHTML = `${(data.copies || []).length} ${escHtml(platformLabText('copies', 'copies'))}
${(data.archives || []).length} ${escHtml(platformLabText('archiveCandidates', 'archive candidates'))}
${entries.slice(0, 50).map(item => `${escHtml(item.titleId || item.name)} ${escHtml(item.relativePath || item.name)} · ${escHtml(item.format)}
${escHtml(platformLabText('emulatorCopy', 'emulator copy'))} ${item.format === 'directory' ? `${escHtml(platformLabText('launchPlan', 'Launch plan'))} ` : ''}
`).join('')}${escHtml(platformLabText('notConsoleInstall', 'These records never imply a console installation.'))}
`;
}
function renderArtifactRoots(data) {
const options = `${escHtml(platformLabText('selectRoot', 'Select library root'))} ${(data.roots || []).map(root => `${escHtml(root.label)} `).join('')}`;
$('platform-artifact-root').innerHTML = options;
$('platform-stage-root').innerHTML = options;
}
function renderArtifacts(data) {
_platformLabState.artifacts = data.artifacts || [];
$('platform-artifacts').innerHTML = _platformLabState.artifacts.map(item => ` ${escHtml(item.fileName)} ${escHtml(item.lifecycleState)} · ${escHtml(item.sha256.slice(0, 16))}…
${item.lifecycleState === 'hashed' ? `
${escHtml(platformLabText('reviewHash', 'Review hash'))} ` : `
${escHtml(item.lifecycleState)} `}
`).join('') || `${escHtml(platformLabText('noArtifacts', 'No inspected artifacts.'))}
`;
}
function renderContracts(data) {
$('platform-contracts').innerHTML = (data.contracts || []).map(item => `${escHtml(item.label)} ${escHtml(item.mode)}${item.missing.length ? ` · ${escHtml(item.missing.join(', '))}` : ''}
${escHtml(item.state)} `).join('');
}
function renderNotifications(data) {
$('platform-notifications').innerHTML = (data.items || []).map(item => `${escHtml(item.category)} ${escHtml(item.message)}
${escHtml(item.severity)} `).join('') || `${escHtml(platformLabText('noNotifications', 'No current recommendations.'))}
`;
}
function renderOperations(data) {
$('platform-operations').innerHTML = (data.jobs || []).slice(0, 10).map(job => `${escHtml(job.type)} ${escHtml(job.error || job.updatedAt)}
${escHtml(job.state)} · ${job.progress}% `).join('') || `${escHtml(platformLabText('noOperations', 'No background operations.'))}
`;
}
function renderLaunchRoots(data) {
$('platform-shadps4-exe-root').innerHTML = `${escHtml(platformLabText('selectExecutableRoot', 'Select executable root'))} ${(data.roots || []).map(root => `${escHtml(root.label)} `).join('')}`;
}
function renderLaunchProbe(data) {
$('platform-shadps4-launch').innerHTML = `${data.available ? `${escHtml(data.executableLabel)} · ${escHtml(platformLabText('ready', 'ready'))}` : escHtml(platformLabText('launcherNotConfigured', 'Launcher not configured.'))} · shell=false
`;
}
function renderSearch(data) {
$('platform-search-results').innerHTML = (data.results || []).map(item => `${escHtml(item.title)} ${escHtml(item.kind)} · ${escHtml(item.detail)}
`).join('') || `${escHtml(platformLabText('empty', 'No results.'))}
`;
}
async function loadPlatformLab() {
const jobs = [
['platform-bindings', window.pork.platformBindings(), renderPlatformBindings],
['platform-autoloader-output', window.pork.unifiedAutoloaderList(), renderAutoloader],
['platform-websrv', window.pork.platformWebsrvDiscover(), renderPlatformWebsrv],
['platform-remote', window.pork.remotePlayIntelligence(), renderRemoteIntelligence],
['platform-shadps4', window.pork.shadps4Roots(), renderShadps4Roots],
['platform-shadps4', window.pork.shadps4Scan(), renderShadps4Scan],
['platform-artifacts', window.pork.platformArtifactRoots(), renderArtifactRoots],
['platform-artifacts', window.pork.platformArtifacts(), renderArtifacts],
['platform-contracts', window.pork.platformContracts(), renderContracts],
['platform-notifications', window.pork.platformNotifications(), renderNotifications],
['platform-operations', window.pork.platformOperations(), renderOperations],
['platform-shadps4-launch', window.pork.shadps4LaunchRoots(), renderLaunchRoots],
['platform-shadps4-launch', window.pork.shadps4LaunchProbe(), renderLaunchProbe],
];
const results = await Promise.allSettled(jobs.map(job => job[1]));
results.forEach((result, index) => result.status === 'fulfilled' ? jobs[index][2](result.value) : platformLabError(jobs[index][0], result.reason));
}
async function loadEcosystemTab(tab) {
if (tab === 'overview') return loadEcosystemOverview();
if (tab === 'components') return loadEcosystemComponents();
if (tab === 'updates') return loadEcosystemUpdates();
if (tab === 'game-updates') return loadGameUpdates();
if (tab === 'etahen') return loadEtaHen();
if (tab === 'live') return loadLiveSession();
if (tab === 'remote-play') return loadRemotePlay({ discover: !_remotePlayPacket });
if (tab === 'files') return loadFilesTab();
if (tab === 'homebrew') return loadHomebrew();
if (tab === 'sources') return loadSources();
if (tab === 'plugins') return loadPlugins();
if (tab === 'cheats') return loadCheatsV2();
if (tab === 'saves') return loadSaveVault();
if (tab === 'itemzflow') return loadItemzflow();
if (tab === 'platform-lab') return loadPlatformLab();
if (tab === 'research') return loadLinuxLab();
if (tab === 'timeline') return loadTimeline();
}
function activateEcosystemTab(tab) {
_ecosystemActiveTab = tab;
document.querySelectorAll('.ecosystem-tab').forEach(button => {
const active = button.dataset.ecosystemTab === tab;
button.classList.toggle('active', active);
button.setAttribute('aria-selected', String(active));
button.tabIndex = active ? 0 : -1;
});
document.querySelectorAll('.ecosystem-panel').forEach(panel => { const active = panel.dataset.ecosystemPanel === tab; panel.classList.toggle('active', active); panel.hidden = !active; });
loadEcosystemTab(tab).catch(error => showToast(error.message, 'error'));
}
function navigateEcosystemTab(tab) {
const route = window.ChimeraNavigationContract?.routes?.find(item => item.page === 'ecosystem' && item.tab === tab);
if (route && typeof window.chimeraNavigate === 'function') return window.chimeraNavigate(route.id);
return activateEcosystemTab(tab);
}
function bindEcosystemEvents() {
document.querySelectorAll('.ecosystem-tab').forEach(button => button.addEventListener('click', () => navigateEcosystemTab(button.dataset.ecosystemTab)));
$('ecosystem-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, () => loadEcosystemTab(_ecosystemActiveTab)).catch(error => showToast(error.message, 'error')));
$('ecosystem-health-run').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => {
await window.pork.ecosystemHealthRun(true);
await loadEcosystemOverview();
showToast('Deep console health scan completed.', 'ok');
}).catch(error => showToast(error.message, 'error', 9000)));
$('ecosystem-overview').addEventListener('click', async event => {
const alert = event.target.closest('.ecosystem-alert');
if (!alert) return;
try {
if (event.target.closest('.ecosystem-alert-ack')) {
await ecosystemBusy(event.target.closest('button'), () => window.pork.ecosystemHealthAcknowledge(alert.dataset.id));
await loadEcosystemOverview();
}
const open = event.target.closest('.ecosystem-alert-open');
if (open?.dataset.tab) navigateEcosystemTab(open.dataset.tab);
if (open?.dataset.page) navigate(open.dataset.page);
} catch (error) { showToast(error.message, 'error'); }
});
$('ecosystem-overview').addEventListener('change', async event => {
if (event.target.id !== 'ecosystem-health-notifications') return;
try {
const packet = await window.pork.ecosystemHealthOptions(event.target.checked);
event.target.checked = Boolean(packet.notificationsEnabled);
showToast(packet.notificationsEnabled ? 'Health notifications enabled.' : 'Health notifications disabled.', 'ok');
} catch (error) { event.target.checked = !event.target.checked; showToast(error.message, 'error'); }
});
$('ecosystem-components-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => { await window.pork.ecosystemRefreshComponents(true); await loadEcosystemComponents(); showToast('Upstreamcomponenten bijgewerkt.', 'ok'); }).catch(error => showToast(error.message, 'error')));
$('ecosystem-components-search').addEventListener('input', renderEcosystemComponents);
$('ecosystem-components-platform').addEventListener('change', renderEcosystemComponents);
$('ecosystem-components-risk').addEventListener('change', renderEcosystemComponents);
$('ecosystem-components').addEventListener('click', event => { const button = event.target.closest('.ecosystem-open-upstream'); if (button) window.pork.openShell(button.dataset.url); });
$('ecosystem-updates-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => {
const result = await window.pork.ecosystemUpdatesRefresh();
await loadEcosystemUpdates();
showToast(`${result.stats?.total || 0} upstreamcomponenten en payloadcatalogi gesynchroniseerd.`, result.blocked ? 'info' : 'ok', 9000);
}).catch(error => showToast(error.message, 'error', 9000)));
$('ecosystem-watchdog-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, loadEcosystemUpdates).catch(error => showToast(error.message, 'error', 10000)));
$('ecosystem-manager-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => { await window.pork.ecosystemPayloadRefresh(); await loadEcosystemUpdates(); showToast('Payload Manager sources refreshed; nothing was installed or started.', 'ok', 9000); }).catch(error => showToast(error.message, 'error', 10000)));
$('ecosystem-runtime-updates').addEventListener('click', async event => {
const pairButton = event.target.closest('.chimera-sentinel-pair');
if (pairButton) {
const confirmation = await showExactConfirmation(window.t('ecosystem.sentinel.pairWarning', 'Sentinel pairs only briefly after payload start; the key stays in RAM.'), 'PAIR SENTINEL');
if (!confirmation) return;
try {
await ecosystemBusy(pairButton, () => window.pork.chimeraSentinelPair({ confirmation, rotate: false }));
showToast('Sentinel 0.2 paired. The console keeps the key only until the payload stops.', 'ok', 10000);
await loadEcosystemUpdates();
} catch (error) { showToast(error.message, 'error', 10000); }
return;
}
const button = event.target.closest('.ecosystem-runtime-install');
const row = event.target.closest('[data-runtime-filename]');
if (!button || !row) return;
const required = `UPDATE ${row.dataset.runtimeFilename}`;
const confirmation = await showExactConfirmation(`This installs only ${row.dataset.runtimeFilename} in Payload Manager and starts nothing.`, required);
if (!confirmation) return;
try {
const result = await ecosystemBusy(button, () => window.pork.ecosystemPayloadInstall({ filename: row.dataset.runtimeFilename, sourceId: row.dataset.runtimeSource, confirmation }));
showToast(`${result.filename} installed. The payload was not started.`, 'ok', 10000);
await loadEcosystemUpdates();
} catch (error) { showToast(error.message, 'error', 10000); }
});
$('ecosystem-supply-policy').addEventListener('submit', async event => {
event.preventDefault();
try {
await ecosystemBusy(event.submitter, () => window.pork.ecosystemSupplyPolicySave({ officialSourcesOnly: $('ecosystem-supply-official').checked, requireHttps: $('ecosystem-supply-https').checked, requirePublishedHashForExecution: $('ecosystem-supply-hash').checked }));
showToast('Payload trust policy saved. Automatic execution remains disabled.', 'ok');
} catch (error) { showToast(error.message, 'error'); }
});
$('ecosystem-supply-audit').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => {
const report = await window.pork.ecosystemSupplyAudit();
const blocked = report.assets.filter(asset => asset.downloaded && asset.issues.length);
$('ecosystem-supply-report').innerHTML = `${report.stats.verified} verified · ${report.stats.blocked} blocked ${report.stats.assets} release assets from ${report.stats.sources} sources · checked ${ecosystemDate(report.checkedAt)} ${blocked.length ? `${blocked.slice(0, 30).map(asset => `
${escHtml(asset.source)} · ${escHtml(asset.name)} — ${asset.issues.map(escHtml).join(' ')}
`).join('')}
` : '✓ Every downloaded payload satisfies the active policy.
'}`;
showToast(`Payload Vault: ${report.stats.verified} verified, ${report.stats.blocked} blocked.`, report.stats.blocked ? 'info' : 'ok', 10000);
}).catch(error => showToast(error.message, 'error', 10000)));
$('ecosystem-repository-copy').addEventListener('click', async () => {
const input = $('ecosystem-repository-url');
try {
if (!navigator.clipboard?.writeText) throw new Error('Clipboard API unavailable');
await navigator.clipboard.writeText(input.value);
showToast('Payload Manager source URL copied.', 'ok');
} catch (_) {
input.focus(); input.select(); input.setSelectionRange(0, input.value.length);
const copied = typeof document.execCommand === 'function' && document.execCommand('copy');
showToast(copied ? 'Payload Manager source URL copied.' : 'The URL is selected; copy it with Ctrl+C.', copied ? 'ok' : 'info');
}
});
$('ecosystem-payload-manager-open').addEventListener('click', () => navigate('jailbreak'));
$('game-updates-manifest-form').addEventListener('submit', async event => {
event.preventDefault();
try {
const plan = await ecosystemBusy(event.submitter, () => window.pork.gameUpdatesAutoInspect($('game-updates-game').value, true));
renderGameUpdatePlan(plan);
} catch (error) { _gameUpdatePlan = null; $('game-updates-plan').innerHTML = `Manifest afgekeurd ${escHtml(error.message)} `; showToast(error.message, 'error', 10000); }
});
$('game-updates-manual-inspect').addEventListener('click', async event => {
try {
const url = $('game-updates-manifest-url').value.trim();
if (!url) throw new Error('Paste an official Sony JSON manifest URL first.');
renderGameUpdatePlan(await ecosystemBusy(event.currentTarget, () => window.pork.gameUpdatesInspect(url, $('game-updates-game').value)));
} catch (error) { _gameUpdatePlan = null; $('game-updates-plan').innerHTML = `Manifest rejected ${escHtml(error.message)} `; }
});
$('game-updates-scan-all').addEventListener('click', async event => {
try {
const result = await ecosystemBusy(event.currentTarget, () => window.pork.gameUpdatesRefreshAll());
showToast(`${result.available}/${result.total} official manifests available.`, result.failed ? 'info' : 'ok', 9000);
await loadGameUpdates();
} catch (error) { showToast(error.message, 'error', 10000); }
});
$('game-updates-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, loadGameUpdates).catch(error => showToast(error.message, 'error')));
$('game-updates-jobs').addEventListener('click', async event => {
const row = event.target.closest('.ecosystem-update-row'); const button = event.target.closest('button');
if (!row || !button) return;
try {
if (button.classList.contains('game-update-cancel') && await showConfirm('Cancel this download and remove the incomplete file?')) await window.pork.gameUpdatesCancel(row.dataset.id);
if (button.classList.contains('game-update-stage')) {
if (!await showConfirm('Copy the fully verified package to /data/chimera-updates on the PS5? This installs or starts nothing.')) return;
const result = await ecosystemBusy(button, () => window.pork.gameUpdatesStage(row.dataset.id));
showToast(`Package verified and copied to ${result.remotePath}; installation was not performed.`, 'ok', 12000);
}
if (button.classList.contains('game-update-handoff')) {
const result = await ecosystemBusy(button, () => window.pork.gameUpdatesHttpsHandoff(row.dataset.id));
await navigator.clipboard.writeText(result.url);
showToast('Temporary verified HTTPS package URL copied; it expires in 30 minutes.', 'ok', 10000);
}
if (button.classList.contains('game-update-delete') && await showExactConfirmation('Permanently delete this job and local package file?', 'DELETE')) await window.pork.gameUpdatesDelete(row.dataset.id);
await loadGameUpdates();
} catch (error) { showToast(error.message, 'error', 10000); }
});
$('ecosystem-update-plan').addEventListener('click', async event => {
const row = event.target.closest('.ecosystem-update-row');
if (!row) return;
const button = event.target.closest('button');
try {
if (button?.classList.contains('ecosystem-version-save')) {
const version = row.querySelector('.ecosystem-version-input').value.trim();
await ecosystemBusy(button, () => window.pork.ecosystemTrackVersion(row.dataset.id, version));
await loadEcosystemUpdates();
showToast(window.t(version ? 'ecosystem.payloadEvidence.manualSaved' : 'ecosystem.payloadEvidence.manualCleared', version ? 'Manual override saved.' : 'Manual tracking cleared.'), 'ok');
}
if (button?.classList.contains('ecosystem-follow-automatic')) {
await ecosystemBusy(button, () => window.pork.ecosystemFollowAutomatic(row.dataset.id));
await loadEcosystemUpdates();
showToast(window.t('ecosystem.payloadEvidence.automaticSaved', 'Automatic evidence following enabled.'), 'ok');
}
if (button?.classList.contains('ecosystem-update-prepare')) {
const asset = row.querySelector('.ecosystem-asset-select')?.value;
if (!await showConfirm('This only downloads the selected official release asset to Chimera and verifies available checksums. Nothing executes on the PS5. Continue?')) return;
const result = await ecosystemBusy(button, () => window.pork.ecosystemUpdatePrepare({ componentId: row.dataset.id, assetName: asset }));
await loadEcosystemUpdates();
showToast(`Update voorbereid: ${result.file?.name || result.asset?.name || asset}${result.file?.verified || result.verified ? ' · checksum geverifieerd' : ''}.`, result.file?.verified || result.verified ? 'ok' : 'info', 9000);
}
} catch (error) { showToast(error.message, 'error', 9000); }
});
$('ecosystem-etahen-reload').addEventListener('click', event => ecosystemBusy(event.currentTarget, loadEtaHen).catch(error => showToast(error.message, 'error')));
$('ecosystem-etahen-preview').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => renderEtaDiff(await window.pork.ecosystemEtaHenPreview({ raw: $('ecosystem-etahen-raw').value, preset: $('ecosystem-etahen-preset').value || null, patch: etaPatchFromForm() }))).catch(error => showToast(error.message, 'error')));
$('ecosystem-etahen-apply').addEventListener('click', async event => {
if (!_ecosystemEtaProposed || !await showExactConfirmation('Apply this etaHEN configuration? Chimera creates a restore point first and verifies the upload.', 'PAS ETAHEN TOE')) return;
try { await ecosystemBusy(event.currentTarget, () => window.pork.ecosystemEtaHenApply({ proposed: _ecosystemEtaProposed, expectedHash: _ecosystemEtaHash, label: $('ecosystem-etahen-preset').selectedOptions[0]?.textContent || 'Manual change' }, 'PAS ETAHEN TOE')); showToast('etaHEN configuration applied safely.', 'ok'); await loadEtaHen(); } catch (error) { showToast(error.message, 'error', 9000); }
});
$('ecosystem-etahen-history').addEventListener('click', async event => { const row = event.target.closest('.ecosystem-history-row'); if (!row || !event.target.closest('.ecosystem-etahen-restore') || !await showExactConfirmation('Restore this etaHEN restore point?', 'HERSTEL')) return; try { await ecosystemBusy(event.target.closest('button'), () => window.pork.ecosystemEtaHenRestore(row.dataset.id)); showToast('etaHEN configuration restored.', 'ok'); await loadEtaHen(); } catch (error) { showToast(error.message, 'error'); } });
$('ecosystem-live-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, loadLiveSession).catch(error => showToast(error.message, 'error')));
$('remote-play-profile').addEventListener('submit', async event => {
event.preventDefault();
try {
await ecosystemBusy(event.submitter, () => window.pork.remotePlaySave({ nickname: $('remote-play-nickname').value, clientNickname: $('remote-play-client-nickname').value, workerId: $('remote-play-worker').value, displayMode: $('remote-play-display-mode').value, registrationKey: $('remote-play-key').value || undefined }));
$('remote-play-key').value = '';
showToast('Remote Play profile saved safely.', 'ok');
await loadRemotePlay();
} catch (error) { showToast(error.message, 'error', 10000); }
});
$('remote-play-worker').addEventListener('change', () => { if (_remotePlayPacket) loadRemotePlay().catch(error => showToast(error.message, 'error')); });
$('remote-play-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, () => loadRemotePlay({ schedule: true })).catch(error => showToast(error.message, 'error')));
$('remote-play-discover').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => { const result = await window.pork.remotePlayDiscover(); await loadRemotePlay(); showToast(result.online ? `PS5 ${result.console?.name || ''} is ${result.state}.` : 'The PS5 did not answer LAN discovery.', result.online ? 'ok' : 'error'); }).catch(error => showToast(error.message, 'error')));
$('remote-play-wake').addEventListener('click', async event => {
if (!await showExactConfirmation('This sends a wake packet to the PS5 and waits up to 30 seconds for the console to become ready.', 'WEK PS5')) return;
try { const result = await ecosystemBusy(event.currentTarget, () => window.pork.remotePlayWake()); showToast(result.ready ? (result.alreadyReady ? 'The PS5 was already ready.' : 'The PS5 is awake and ready.') : 'Wake packet sent, but the PS5 did not become ready in time.', result.ready ? 'ok' : 'error', 10000); await loadRemotePlay(); } catch (error) { showToast(error.message, 'error', 10000); }
});
$('remote-play-start').addEventListener('click', async event => {
const worker = remotePlaySelectedWorker(_remotePlayPacket || {});
if (!worker || !await showExactConfirmation(`Dit ${_remotePlayPacket?.lastDiscovery?.state === 'standby' ? 'wekt de PS5 en ' : ''}opent chiaki-ng op ${worker.label}.`, 'START REMOTE PLAY')) return;
try {
const result = await ecosystemBusy(event.currentTarget, () => window.pork.remotePlayStart({ workerId: worker.id, clientNickname: $('remote-play-client-nickname').value.trim(), displayMode: $('remote-play-display-mode').value, wakeIfNeeded: _remotePlayPacket?.lastDiscovery?.state === 'standby' }));
showToast(`Remote Play-job ${result.job.id.slice(0, 8)} staat klaar voor ${worker.label}.`, 'ok', 9000);
await loadRemotePlay();
} catch (error) { showToast(error.message, 'error', 12000); }
});
$('remote-play-worker-package-form').addEventListener('submit', async event => {
event.preventDefault();
try { await ecosystemBusy(event.submitter, () => window.pork.remotePlayWorkerPackage({ label: $('remote-play-worker-label').value.trim(), serverOrigin: window.location.origin })); showToast('Worker package created. The enrollment token expires in 30 minutes.', 'ok', 10000); await loadRemotePlay(); }
catch (error) { showToast(error.message, 'error', 10000); }
});
$('remote-play-workers').addEventListener('click', async event => {
const row = event.target.closest('[data-remote-worker-id]');
if (!row || !event.target.closest('.remote-play-worker-remove') || !await showExactConfirmation('Revoke this worker, erase its HMAC key and cancel open jobs?', 'VERWIJDER')) return;
try { await ecosystemBusy(event.target.closest('button'), () => window.pork.remotePlayWorkerRemove(row.dataset.remoteWorkerId)); showToast('Remote Play worker revoked.', 'ok'); await loadRemotePlay(); } catch (error) { showToast(error.message, 'error'); }
});
const runWorkerAction = async (button, action, phrase = null) => {
const worker = remotePlaySelectedWorker(_remotePlayPacket || {});
if (!worker) return showToast('Select a Remote Play worker first.', 'error');
if (phrase && !await showExactConfirmation(`Deze vaste actie wordt uitgevoerd op ${worker.label}.`, phrase)) return;
try { const job = await ecosystemBusy(button, () => window.pork.remotePlayWorkerAction({ workerId: worker.id, action, confirmation: phrase || undefined })); showToast(`${action} is queued securely for the worker (${job.id.slice(0, 8)}).`, 'ok', 9000); await loadRemotePlay(); } catch (error) { showToast(error.message, 'error', 12000); }
};
$('remote-play-diagnose').addEventListener('click', event => runWorkerAction(event.currentTarget, 'diagnose'));
$('remote-play-open-client').addEventListener('click', event => runWorkerAction(event.currentTarget, 'open-client', 'OPEN CHIAKI'));
$('remote-play-install-client').addEventListener('click', event => runWorkerAction(event.currentTarget, 'install-client', 'INSTALL CHIAKI'));
$('remote-play-update-client').addEventListener('click', event => runWorkerAction(event.currentTarget, 'update-client', 'UPDATE CHIAKI'));
$('remote-play-stop').addEventListener('click', event => runWorkerAction(event.currentTarget, 'stop-session', 'STOP REMOTE PLAY'));
$('remote-play-profile-export').addEventListener('click', event => ecosystemBusy(event.currentTarget, () => window.pork.remotePlayProfileExport()).catch(error => showToast(error.message, 'error')));
$('remote-play-upstream').addEventListener('click', () => window.pork.openShell('https://streetpea.github.io/chiaki-ng/setup/configuration/'));
$('websrv-files-root').addEventListener('click', event => ecosystemBusy(event.currentTarget, () => loadWebsrvFiles('/')).catch(error => showToast(error.message, 'error')));
$('websrv-files-path').addEventListener('click', event => { const button = event.target.closest('.websrv-files-back'); if (button) loadWebsrvFiles(button.dataset.parent).catch(error => showToast(error.message, 'error')); });
$('websrv-files-list').addEventListener('click', async event => { const row = event.target.closest('.websrv-file-row'); if (!row) return; try { if (event.target.closest('.websrv-file-open')) await loadWebsrvFiles(row.dataset.path); if (event.target.closest('.websrv-file-download')) await ecosystemBusy(event.target.closest('button'), () => window.pork.ecosystemWebsrvDownload(row.dataset.path)); } catch (error) { showToast(error.message, 'error', 10000); } });
$('websrv-smb-source').addEventListener('change', () => { _websrvSmbPath = ''; if ($('websrv-smb-source').value) loadWebsrvSmb('').catch(error => showToast(error.message, 'error')); else { $('websrv-smb-list').innerHTML = 'Choose a saved SMB source.
'; $('websrv-smb-path').innerHTML = ''; } });
$('websrv-smb-path').addEventListener('click', event => { const button = event.target.closest('.websrv-smb-back'); if (button) loadWebsrvSmb(button.dataset.parent).catch(error => showToast(error.message, 'error')); });
$('websrv-smb-list').addEventListener('click', async event => { const row = event.target.closest('.websrv-file-row'); if (!row) return; try { if (event.target.closest('.websrv-file-open')) await loadWebsrvSmb(row.dataset.path); if (event.target.closest('.websrv-file-download')) await ecosystemBusy(event.target.closest('button'), () => window.pork.ecosystemWebsrvSmbDownload($('websrv-smb-source').value, row.dataset.path)); } catch (error) { showToast(error.message, 'error', 10000); } });
$('ecosystem-homebrew-scan').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => renderHomebrew(await window.pork.ecosystemHomebrewScan())).catch(error => showToast(error.message, 'error')));
$('ecosystem-websrv-discover').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => {
const result = await window.pork.ecosystemWebsrvDiscover();
renderWebsrvDiscovery(result);
showToast(`${result.services?.length || 0} mDNS service(s) found.`, 'ok');
}).catch(error => showToast(error.message, 'error')));
$('ecosystem-homebrew-webui').addEventListener('click', async () => { try { const urls = await window.pork.ecosystemSourceWebUis(); window.pork.openShell(urls.websrv); } catch (error) { showToast(error.message, 'error'); } });
$('ecosystem-homebrew').addEventListener('click', async event => { const row = event.target.closest('.ecosystem-homebrew-card'); if (!row || !event.target.closest('.ecosystem-homebrew-launch')) return; if (!await showExactConfirmation('websrv warns that launching can crash when other homebrew is already active.', 'START HOMEBREW')) return; try { await ecosystemBusy(event.target.closest('button'), () => window.pork.ecosystemHomebrewLaunch(row.dataset.id, 'START HOMEBREW')); showToast('Homebrew launch sent to websrv.', 'ok'); } catch (error) { showToast(error.message, 'error'); } });
$('ecosystem-pacbrew-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => renderPacbrew(await window.pork.ecosystemPacbrewRefresh())).catch(error => showToast(error.message, 'error')));
$('ecosystem-pacbrew-search').addEventListener('input', () => { if (_pacbrewPacket) renderPacbrew(_pacbrewPacket); });
$('ecosystem-pacbrew-kind').addEventListener('change', () => { if (_pacbrewPacket) renderPacbrew(_pacbrewPacket); });
$('ecosystem-pacbrew').addEventListener('click', async event => {
const upstream = event.target.closest('.ecosystem-open-upstream');
if (upstream) { window.pork.openShell(upstream.dataset.url); return; }
const button = event.target.closest('.ecosystem-pacbrew-inspect');
const row = event.target.closest('[data-pacbrew-name]');
if (!button || !row) return;
try {
const item = await ecosystemBusy(button, () => window.pork.ecosystemPacbrewPackage(row.dataset.pacbrewName));
$('ecosystem-pacbrew-detail').innerHTML = `${escHtml(item.pkgname || item.name)} · ${escHtml(item.version || 'git')} ${escHtml(item.description || 'No description in PKGBUILD.')} Runtime: ${(item.dependencies || []).map(escHtml).join(', ') || 'none'} · build: ${(item.buildDependencies || []).map(escHtml).join(', ') || 'none'} · license: ${(item.licenses || []).map(escHtml).join(', ') || 'unknown'} Recipe ${escHtml(String(item.sourceSha || '').slice(0, 12))} was parsed as text only; nothing was executed.
`;
} catch (error) { showToast(error.message, 'error', 10000); }
});
$('ecosystem-source-form').addEventListener('submit', async event => { event.preventDefault(); const button = event.submitter; try { const sources = await ecosystemBusy(button, () => window.pork.ecosystemSourceSave({ id: $('ecosystem-source-id').value || undefined, label: $('ecosystem-source-label').value, protocol: $('ecosystem-source-protocol').value, host: $('ecosystem-source-host').value, port: $('ecosystem-source-port').value || undefined, path: $('ecosystem-source-path').value, username: $('ecosystem-source-user').value, password: $('ecosystem-source-password').value || undefined })); renderSources(sources); event.target.reset(); $('ecosystem-source-path').value = '/'; $('ecosystem-source-id').value = ''; showToast('Remote source saved with encryption.', 'ok'); } catch (error) { showToast(error.message, 'error'); } });
$('ecosystem-sources').addEventListener('click', async event => {
const row = event.target.closest('.ecosystem-source-row');
if (!row) return;
const sources = await window.pork.ecosystemSources();
const source = sources.find(item => item.id === row.dataset.id);
if (!source) return;
try {
if (event.target.closest('.ecosystem-source-test')) {
const result = await ecosystemBusy(event.target.closest('button'), () => window.pork.ecosystemSourceTest(row.dataset.id));
showToast(result.ok ? `Source reachable in ${result.latencyMs} ms.` : `Source unreachable: ${result.error || result.status}`, result.ok ? 'ok' : 'error');
}
if (event.target.closest('.ecosystem-source-edit')) {
$('ecosystem-source-id').value = source.id;
$('ecosystem-source-label').value = source.label;
$('ecosystem-source-protocol').value = source.protocol;
$('ecosystem-source-host').value = source.host;
$('ecosystem-source-port').value = source.port;
$('ecosystem-source-path').value = source.path;
$('ecosystem-source-user').value = source.username || '';
$('ecosystem-source-password').value = '';
}
if (event.target.closest('.ecosystem-source-delete') && await showExactConfirmation(`Delete ${source.label}?`, 'DELETE')) {
renderSources(await window.pork.ecosystemSourceRemove(source.id));
}
} catch (error) { showToast(error.message, 'error'); }
});
$('ezremote-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, loadSources).catch(error => showToast(error.message, 'error')));
$('ezremote-download-form').addEventListener('submit', async event => {
event.preventDefault();
const destinationPath = $('ezremote-destination').value.trim();
const required = `DOWNLOAD ${destinationPath}`;
const confirmation = await showExactConfirmation('ezRemote writes to the PS5 and cannot safely cancel an active download.', required);
if (!confirmation) return;
try {
const result = await ecosystemBusy(event.submitter, () => window.pork.ezRemoteDownload({
sourceId: $('ezremote-source').value,
sourcePath: $('ezremote-source-path').value.trim(),
destinationPath,
size: Number($('ezremote-size').value || 0),
confirmation,
}));
showToast(result.deduplicated ? 'Reused the existing ezRemote task.' : 'ezRemote download safely queued in the durable transfer queue.', 'ok', 9000);
} catch (error) { showToast(error.message, 'error', 10000); }
});
$('ecosystem-ezremote-webui').addEventListener('click', async () => { try { window.pork.openShell((await window.pork.ecosystemSourceWebUis()).ezremote); } catch (error) { showToast(error.message, 'error'); } });
$('ecosystem-dpi-webui').addEventListener('click', async () => { try { window.pork.openShell((await window.pork.ecosystemSourceWebUis()).dpiV2); } catch (error) { showToast(error.message, 'error'); } });
$('ecosystem-plugins-scan').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => renderPlugins(await window.pork.ecosystemPluginsScan())).catch(error => showToast(error.message, 'error')));
$('ecosystem-plugin-install').addEventListener('click', async event => {
const targetRoot = $('ecosystem-plugin-target').value;
const confirmation = await showExactConfirmation(`Install one selected ELF or PRX into ${targetRoot}? An existing file is backed up and the upload is verified.`, 'INSTALL PLUGIN');
if (!confirmation) return;
try {
const result = await ecosystemBusy(event.currentTarget, () => window.pork.ecosystemPluginInstall(targetRoot, confirmation));
if (!result) return;
renderPlugins(result.inventory);
showToast(`${result.filename} installed and verified · ${String(result.sha256).slice(0, 12)}…`, 'ok', 10000);
} catch (error) { showToast(error.message, 'error', 10000); }
});
$('ecosystem-plugins').addEventListener('click', async event => {
const upstream = event.target.closest('.ecosystem-open-upstream');
if (upstream) {
window.pork.openShell(upstream.dataset.url);
return;
}
const row = event.target.closest('.ecosystem-plugin-row');
if (!row) return;
try {
if (event.target.closest('.ecosystem-plugin-inspect')) {
const info = await ecosystemBusy(event.target.closest('button'), () => window.pork.ecosystemPluginInspect(row.dataset.id));
let output = row.nextElementSibling?.classList.contains('ecosystem-plugin-inspection') ? row.nextElementSibling : null;
if (!output) {
output = document.createElement('pre');
output.className = 'ecosystem-plugin-inspection';
row.insertAdjacentElement('afterend', output);
}
output.textContent = `SHA-256: ${info.sha256}\nTitle IDs: ${info.titleIds.join(', ') || 'none reliably detected'}\nVersion candidates: ${info.versions.join(', ') || 'none detected'}`;
}
if (event.target.closest('.ecosystem-plugin-toggle')) {
const currentEnabled = event.target.closest('button').textContent.includes('Disable');
if (!await showExactConfirmation(`${currentEnabled ? 'Disable' : 'Enable'} this plugin? Chimera creates a backup first and etaHEN must then be reloaded.`, 'WIJZIG PLUGIN')) return;
const result = await ecosystemBusy(event.target.closest('button'), () => window.pork.ecosystemPluginToggle(row.dataset.id, !currentEnabled, 'WIJZIG PLUGIN'));
renderPlugins(result.inventory);
}
} catch (error) { showToast(error.message, 'error'); }
});
const scheduleCheatLoad = () => { _ecosystemCheatOffset = 0; clearTimeout(_ecosystemCheatTimer); _ecosystemCheatTimer = setTimeout(() => loadCheatsV2().catch(error => showToast(error.message, 'error')), 250); };
$('ecosystem-cheats-search').addEventListener('input', scheduleCheatLoad); $('ecosystem-cheats-platform').addEventListener('change', scheduleCheatLoad); $('ecosystem-cheats-kind').addEventListener('change', scheduleCheatLoad); $('ecosystem-cheats-installed').addEventListener('change', scheduleCheatLoad);
$('ecosystem-cheats-sync').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => { await window.pork.ecosystemCheatsSync(); _ecosystemCheatOffset = 0; await loadCheatsV2(); showToast('Cheat and patch sources synchronized.', 'ok'); }).catch(error => showToast(error.message, 'error', 9000)));
$('ecosystem-cheats').addEventListener('click', async event => { const pager = event.target.closest('.ecosystem-cheat-page'); if (pager && !pager.disabled) { _ecosystemCheatOffset = Number(pager.dataset.offset) || 0; await loadCheatsV2(); return; } const source = event.target.closest('.ecosystem-open-upstream'); if (source) return window.pork.openShell(source.dataset.url); const row = event.target.closest('.ecosystem-cheat-row'); if (!row || !event.target.closest('.ecosystem-cheat-deploy')) return; const versionWarning = row.querySelector('.operations-status-warning')?.textContent.includes('Version mismatch'); const conflict = row.classList.contains('conflict'); if (!await showExactConfirmation(`Send this cheat or patch to the documented PS5 folder?${versionWarning ? '\n\nThe game version differs; you explicitly confirm that you want to continue.' : ''}${conflict ? '\n\nOther formats for the same version will be replaced safely.' : ''}`, 'INSTALLEER CHEAT')) return; try { await ecosystemBusy(event.target.closest('button'), () => window.pork.ecosystemCheatDeploy({ key: row.dataset.key, forceVersion: versionWarning, replaceConflicts: conflict, confirmation: 'INSTALLEER CHEAT' })); showToast('Cheat or patch installed with backup and checksum verification.', 'ok'); } catch (error) { showToast(error.message, 'error', 9000); } });
$('ecosystem-saves-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => renderRemoteSaves(await window.pork.ecosystemRemoteSaves())).catch(error => showToast(error.message, 'error')));
$('ecosystem-saves-all').addEventListener('click', async event => { if (!await showConfirm('Export every discovered title to Save Vault one by one? This can take a long time and use substantial storage.')) return; try { const result = await ecosystemBusy(event.currentTarget, () => window.pork.ecosystemSaveSnapshotAll()); showToast(`${result.succeeded}/${result.total} save backups created.`, result.failed ? 'info' : 'ok', 9000); await loadSaveVault(); } catch (error) { showToast(error.message, 'error'); } });
$('ecosystem-remote-saves').addEventListener('click', async event => { const row = event.target.closest('.ecosystem-save-row'); if (!row || !event.target.closest('.ecosystem-save-snapshot')) return; try { await ecosystemBusy(event.target.closest('button'), () => window.pork.ecosystemSaveSnapshot(row.dataset.titleId)); showToast('Save Vault-snapshot gemaakt en geverifieerd.', 'ok'); await loadSaveVault(); } catch (error) { showToast(error.message, 'error'); } });
$('ecosystem-save-vault').addEventListener('click', async event => {
const row = event.target.closest('.ecosystem-vault-row');
if (!row) return;
const button = event.target.closest('button');
try {
if (button?.classList.contains('ecosystem-save-verify')) {
const result = await ecosystemBusy(button, () => window.pork.ecosystemSaveVerify(row.dataset.id));
showToast(result.ok ? `${result.titleId}: SHA-256, ZIP CRC and ${result.archive.entries.length} save file(s) are valid.` : `Snapshot rejected: ${result.error}`, result.ok ? 'ok' : 'error', 10000);
}
if (button?.classList.contains('ecosystem-save-restore')) {
if (!_ecosystemSaveCapabilities?.automatedRestore) throw new Error('Restore capability is not active.');
let plan = await ecosystemBusy(button, () => window.pork.ecosystemSaveRestorePlan(row.dataset.id));
if (plan.requiresTargetUser) {
const choices = plan.users.map(user => `${user.uid} (${user.name || 'name unknown'})`).join('\n');
const targetUid = await showPrompt(`Choose the exact target UID for ${plan.titleId}:\n\n${choices}`, plan.users[0]?.uid || '');
if (!targetUid) return;
plan = await ecosystemBusy(button, () => window.pork.ecosystemSaveRestorePlan(row.dataset.id, targetUid.trim()));
}
const incoming = plan.impact.incomingSaves.map(save => `• ${save.name} — ${ecosystemBytes(save.bytes)} — ${String(save.sha256).slice(0, 12)}…`).join('\n');
const approved = await showConfirm(
`DRY RUN — nothing has changed on the PS5 yet.\n\n` +
`Title: ${plan.titleId}\nTarget user: ${plan.targetUid}\nExisting saves: ${plan.impact.existingSaves.length}\nIncoming saves: ${plan.impact.incomingSaves.length}\n\n${incoming}\n\n` +
'If you continue, Chimera first creates a new verified snapshot. Account mismatch, zeroed param.sfo or changed source or destination saves stop the process. A byte-for-byte export check follows. Automatic rollback is intentionally not performed. Continue to the exact confirmation?'
);
if (!approved) return;
const phrase = await showExactConfirmation('The dry run was approved. Now confirm the actual restore.', plan.confirmationPhrase);
if (!phrase) return;
const result = await ecosystemBusy(button, () => window.pork.ecosystemSaveRestore(plan.token, phrase));
showToast(`${result.titleId} restored safely: ${result.verifiedEntries} file(s) verified byte-for-byte. Pre- and post-snapshots were retained.`, 'ok', 15000);
await loadSaveVault();
}
if (button?.classList.contains('ecosystem-save-download')) await ecosystemBusy(button, () => window.pork.ecosystemSaveDownload(row.dataset.id));
if (button?.classList.contains('ecosystem-save-delete') && await showExactConfirmation('Permanently delete this local save backup?', 'DELETE')) { await window.pork.ecosystemSaveDelete(row.dataset.id); await loadSaveVault(); }
} catch (error) { showToast(error.message, 'error', 15000); }
});
$('itemzflow-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, loadItemzflow).catch(error => showToast(error.message, 'error')));
$('itemzflow-homebrew-open').addEventListener('click', () => navigateEcosystemTab('homebrew'));
$('itemzflow-log-select').addEventListener('change', renderItemzflowEvents);
$('itemzflow-level').addEventListener('change', renderItemzflowEvents);
$('itemzflow-jobs-only').addEventListener('change', renderItemzflowEvents);
$('platform-lab-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, loadPlatformLab).catch(error => showToast(error.message, 'error')));
$('platform-bindings-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => renderPlatformBindings(await window.pork.platformBindingsRefresh())).catch(error => showToast(error.message, 'error')));
$('platform-bindings').addEventListener('click', event => {
const button = event.target.closest('.platform-binding-review');
if (!button) return;
ecosystemBusy(button, async () => { await window.pork.platformBindingReview({ id: button.dataset.id, tag: button.dataset.tag }); renderPlatformBindings(await window.pork.platformBindings()); }).then(() => showToast(platformLabText('reviewed', 'Upstream evidence reviewed.'), 'ok')).catch(error => showToast(error.message, 'error'));
});
$('platform-autoloader-preview').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => renderAutoloaderPreview(await window.pork.unifiedAutoloaderPreview({ id: $('platform-autoloader-profile').value }))).catch(error => showToast(error.message, 'error')));
$('platform-autoloader-profile').addEventListener('change', () => {
const profile = (_platformLabState.profiles || []).find(item => item.id === $('platform-autoloader-profile').value);
if (profile) $('platform-profile-json').value = JSON.stringify(profile, null, 2);
});
$('platform-profile-save').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => {
const profile = JSON.parse($('platform-profile-json').value);
await window.pork.unifiedAutoloaderSave(profile);
renderAutoloader(await window.pork.unifiedAutoloaderList());
}).then(() => showToast(platformLabText('profileSaved', 'Profile saved.'), 'ok')).catch(error => showToast(error.message, 'error')));
$('platform-profile-delete').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => {
await window.pork.unifiedAutoloaderRemove({ id: $('platform-autoloader-profile').value });
renderAutoloader(await window.pork.unifiedAutoloaderList());
}).then(() => showToast(platformLabText('profileDeleted', 'Profile deleted.'), 'ok')).catch(error => showToast(error.message, 'error')));
$('platform-profile-recovery').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => {
const result = await window.pork.unifiedAutoloaderRecovery({ id: $('platform-autoloader-profile').value });
$('platform-profile-output').innerHTML = `${escHtml(JSON.stringify(result, null, 2))} `;
}).catch(error => showToast(error.message, 'error')));
$('platform-websrv-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => renderPlatformWebsrv(await window.pork.platformWebsrvDiscover())).catch(error => showToast(error.message, 'error')));
$('platform-remote-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => renderRemoteIntelligence(await window.pork.remotePlayIntelligence())).catch(error => showToast(error.message, 'error')));
$('platform-shadps4-scan').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => { const rootId = $('platform-shadps4-root').value; if (rootId) await window.pork.shadps4Select({ rootId }); renderShadps4Scan(await window.pork.shadps4Scan()); }).catch(error => showToast(error.message, 'error')));
$('platform-artifact-inspect').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => {
await window.pork.platformArtifactInspect({ rootId: $('platform-artifact-root').value, relativePath: $('platform-artifact-relative').value });
renderArtifacts(await window.pork.platformArtifacts());
}).then(() => showToast(platformLabText('artifactHashed', 'Artifact hashed.'), 'ok')).catch(error => showToast(error.message, 'error')));
$('platform-artifacts').addEventListener('click', event => {
const button = event.target.closest('.platform-artifact-review');
if (!button) return;
ecosystemBusy(button, async () => { await window.pork.platformArtifactReview({ artifactId: button.dataset.id, sha256: button.dataset.sha }); renderArtifacts(await window.pork.platformArtifacts()); }).then(() => showToast(platformLabText('hashReviewed', 'Exact hash reviewed.'), 'ok')).catch(error => showToast(error.message, 'error'));
});
$('platform-autoloader-stage').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => {
const artifactIds = [...document.querySelectorAll('.platform-artifact-select:checked')].map(input => input.value);
const result = await window.pork.platformAutoloaderStage({ profileId: $('platform-autoloader-profile').value, targetRootId: $('platform-stage-root').value, artifactIds });
$('platform-stage-output').innerHTML = `${escHtml(result.lifecycleState)} · ${escHtml(result.destinationLabel)}
`;
renderArtifacts(await window.pork.platformArtifacts());
}).then(() => showToast(platformLabText('staged', 'Profile staged atomically.'), 'ok')).catch(error => showToast(error.message, 'error')));
$('platform-search-form').addEventListener('submit', event => { event.preventDefault(); ecosystemBusy(event.submitter, async () => renderSearch(await window.pork.platformSearch({ query: $('platform-search-query').value }))).catch(error => showToast(error.message, 'error')); });
$('platform-shadps4-configure').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => {
await window.pork.shadps4LaunchConfigure({ rootId: $('platform-shadps4-exe-root').value, relativePath: $('platform-shadps4-exe-relative').value });
renderLaunchProbe(await window.pork.shadps4LaunchProbe());
}).then(() => showToast(platformLabText('launcherConfigured', 'Launcher configured.'), 'ok')).catch(error => showToast(error.message, 'error')));
$('platform-shadps4').addEventListener('click', event => {
const button = event.target.closest('.platform-shadps4-plan');
if (!button) return;
ecosystemBusy(button, async () => {
_platformLabState.launchPlan = await window.pork.shadps4LaunchPlan({ itemId: button.dataset.id });
$('platform-shadps4-launch').innerHTML = ``;
}).catch(error => showToast(error.message, 'error'));
});
$('platform-shadps4-launch').addEventListener('click', event => {
const button = event.target.closest('#platform-shadps4-launch-confirm');
if (!button || !_platformLabState.launchPlan) return;
ecosystemBusy(button, async () => {
const result = await window.pork.shadps4Launch({ token: _platformLabState.launchPlan.token, confirmation: $('platform-shadps4-confirmation').value });
_platformLabState.launchPlan = null;
$('platform-shadps4-launch').innerHTML = `${escHtml(platformLabText('started', 'Started'))} · PID ${result.pid}
`;
}).catch(error => showToast(error.message, 'error'));
});
$('ecosystem-linux-refresh').addEventListener('click', event => ecosystemBusy(event.currentTarget, loadLinuxLab).catch(error => showToast(error.message, 'error')));
$('ecosystem-linux-components').addEventListener('click', event => { const button = event.target.closest('.ecosystem-open-upstream'); if (button) window.pork.openShell(button.dataset.url); });
$('ecosystem-timeline-start').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => renderTimeline(await window.pork.ecosystemTimelineStart(Number($('ecosystem-timeline-port').value)))).catch(error => showToast(error.message, 'error')));
$('ecosystem-timeline-stop').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => renderTimeline(await window.pork.ecosystemTimelineStop())).catch(error => showToast(error.message, 'error')));
$('ecosystem-timeline-import').addEventListener('click', event => ecosystemBusy(event.currentTarget, async () => renderTimeline(await window.pork.ecosystemTimelineImport())).catch(error => showToast(error.message, 'error')));
$('ecosystem-timeline-options').addEventListener('submit', event => { event.preventDefault(); ecosystemBusy(event.submitter, async () => renderTimeline(await window.pork.ecosystemTimelineConfigure({ enabled: $('ecosystem-timeline-persist').checked, retentionDays: Number($('ecosystem-timeline-retention').value), maxEvents: Number($('ecosystem-timeline-max').value) }))).then(() => showToast('Timeline policy saved; exports remain redacted by default.', 'ok')).catch(error => showToast(error.message, 'error')); });
$('ecosystem-timeline-export').addEventListener('click', event => ecosystemBusy(event.currentTarget, () => window.pork.ecosystemTimelineExport({ search: $('ecosystem-timeline-search').value, level: $('ecosystem-timeline-level').value })).catch(error => showToast(error.message, 'error')));
const scheduleTimelineFilter = () => { clearTimeout(_timelineFilterTimer); _timelineFilterTimer = setTimeout(() => loadTimeline().catch(error => showToast(error.message, 'error')), 250); };
$('ecosystem-timeline-search').addEventListener('input', scheduleTimelineFilter);
$('ecosystem-timeline-level').addEventListener('change', scheduleTimelineFilter);
window.pork?.on?.('ecosystem:timeline', () => { if (_ecosystemActiveTab === 'timeline' && document.querySelector('#page-ecosystem.active')) loadTimeline().catch(() => {}); });
window.pork?.on?.('ecosystem:health', () => { if (_ecosystemActiveTab === 'overview' && document.querySelector('#page-ecosystem.active')) loadEcosystemOverview().catch(() => {}); });
window.pork?.on?.('platform:operation', () => { if (_ecosystemActiveTab === 'platform-lab') window.pork.platformOperations().then(renderOperations).catch(() => {}); });
window.pork?.on?.('game-updates:progress', () => {
if (_ecosystemActiveTab !== 'game-updates' || !document.querySelector('#page-ecosystem.active')) return;
clearTimeout(_gameUpdateRefreshTimer); _gameUpdateRefreshTimer = setTimeout(() => loadGameUpdates().catch(() => {}), 500);
});
}
async function loadEcosystem({ background = false } = {}) {
buildEcosystemPage();
if (background) return loadEcosystemOverview();
const requestedTab = window.chimeraRequestedEcosystemTab;
if (requestedTab) {
_ecosystemActiveTab = requestedTab;
window.chimeraRequestedEcosystemTab = null;
document.querySelectorAll('.ecosystem-tab').forEach(button => {
const active = button.dataset.ecosystemTab === requestedTab;
button.classList.toggle('active', active);
button.setAttribute('aria-selected', String(active));
button.tabIndex = active ? 0 : -1;
});
document.querySelectorAll('.ecosystem-panel').forEach(panel => {
const active = panel.dataset.ecosystemPanel === requestedTab;
panel.classList.toggle('active', active);
panel.hidden = !active;
});
}
const tabLoad = loadEcosystemTab(_ecosystemActiveTab);
if (_ecosystemActiveTab !== 'overview' && !_ecosystemLoaded) {
const headerLoad = loadEcosystemOverview().catch(error => {
$('ecosystem-health').textContent = localizedEcosystemError(error.message || 'Status unavailable');
$('ecosystem-health').className = 'operations-health warning';
});
await Promise.all([tabLoad, headerLoad]);
return;
}
await tabLoad;
}
// end