
    Njc                    4   d Z ddlmZmZ ddlmZ ddlZddlZddlZddl	Z	ddl
Z
ddlmZmZ ddlZej                  j                  dd       ddlmZ dZd	 Z e       Zd
Zd Z ed      Z ed      xs dZ e ed      xs d      ZdZdZ ed      j=                  d      ZdZ dZ! G d de      Z" G d dee      Z#e$dk(  rTdZ%ejL                  jO                  dd      Z( e)de( d e%         e)d!e% d"e         e#e(e%fe"      jU                          yy)#z(Aria Control Panel - Dashboard fuer Kais    )
HTTPServerBaseHTTPRequestHandler)ThreadingMixInNdatetime	timedeltaz/root/aria/scripts)import_modulez/root/.aria-control-tokenc                     t         j                  j                  t              r7t	        t              5 } | j                         j                         cd d d        S t        j                  d      }t	        t        d      5 } | j                  |       d d d        t        j                  t        d       |S # 1 sw Y   dxY w# 1 sw Y   1xY w)N   wi  )ospathexists
TOKEN_FILEopenreadstripsecretstoken_urlsafewritechmod)ftokens     "/root/aria/scripts/aria-control.py	get_tokenr      s    	ww~~j!* 	$668>>#	$ 	$!!"%E	j#	 !	HHZL	$ 	$ s   B6 C6B?Cz/root/.aria-planer-tasks.jsonc                    dD ]  }	 t        |      5 }|D ]t  }|j                         }|j                  | dz         s(|j                  dd      d   j                         j                  d      j                  d      c cd d d        c S  	 d d d         t
        j                  j                  | d      S # 1 sw Y   +xY w# t        $ r Y w xY w)N)/root/aria/.envz$/root/.claude/channels/telegram/.env=   "' )r   r   
startswithsplit	Exceptionr   environget)keyr   r   lines       r   	_read_envr*      s    K 	d Sq SD::<DsSy1#zz#q1!4::<BB3GMMcRR	S SSS ::>>#r""S S
  		s:   C*B7AB7?	CB7C7C 	<C	CCTELEGRAM_BOT_TOKENLINEAR_TEAM_KEYARITELEGRAM_CHAT_ID0uL  <!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no,viewport-fit=cover">
<title>Aria Planer</title>
<meta name="theme-color" content="#000000">
<meta name="apple-mobile-web-app-capable" content="yes">
<style>
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
:root{
  --bg:#14141a;--card:#1c1c1e;--card2:#2c2c2e;--sep:rgba(84,84,88,.65);
  --text:#fff;--t2:rgba(235,235,245,.6);--t3:rgba(235,235,245,.3);
  --accent:#1EF0A1;--green:#30d158;--amber:#ffd60a;--red:#ff453a;--cyan:#1EF0A1;
  --safe-top:env(safe-area-inset-top,0px);--safe-bot:env(safe-area-inset-bottom,0px);
  --hour-h:80px;--gutter:52px;
}
body{font-family:-apple-system,'Inter',sans-serif;background:var(--bg);color:var(--text);min-height:100vh;overflow-x:hidden;-webkit-font-smoothing:antialiased}

/* Header */
.hdr{position:sticky;top:0;z-index:100;background:rgba(20,20,26,.92);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
  padding:max(12px,var(--safe-top)) 12px 12px;display:flex;align-items:center;justify-content:space-between;gap:8px;border-bottom:.5px solid var(--sep);flex-wrap:nowrap}
.hdr-nav{display:flex;align-items:center;gap:8px;flex-shrink:1;min-width:0}
.nav-btn{background:var(--card);border:none;color:var(--text);font-size:18px;width:34px;height:34px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.nav-btn:active{opacity:.6}
.date-label{font-size:14px;font-weight:700;min-width:0;flex-shrink:1;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.back-link{font-size:13px;color:var(--accent);text-decoration:none;padding:8px 0;white-space:nowrap;flex-shrink:0}
@media (max-width:480px){
  .back-link{font-size:0}
  .back-link::before{content:"←";font-size:18px}
}

/* Planer body */
.planer-wrap{display:flex;flex-direction:column;height:calc(100vh - 56px - var(--safe-bot));overflow:hidden}
.agent-headers{display:flex;margin-left:var(--gutter);border-bottom:.5px solid var(--sep);background:var(--bg);position:sticky;top:56px;z-index:90}
.agent-hdr{flex:1;text-align:center;padding:8px 4px;font-size:12px;font-weight:600;color:var(--t2);border-right:.5px solid var(--sep)}
.agent-hdr:last-child{border-right:none}

/* Timeline scroll */
.timeline-scroll{flex:1;overflow-y:auto;overflow-x:hidden;position:relative}
.timeline-inner{display:flex;position:relative}

/* Time gutter */
.gutter{width:var(--gutter);flex-shrink:0;position:sticky;left:0;background:var(--bg);z-index:80}
.gutter-hour{height:var(--hour-h);display:flex;align-items:flex-start;justify-content:flex-end;padding:2px 8px 0 0;font-size:10px;font-weight:500;color:var(--t3)}

/* Lanes */
.lanes{flex:1;display:flex;position:relative}
.lane{flex:1;position:relative;border-right:.5px solid var(--sep);min-width:120px}
.lane:last-child{border-right:none}
.lane-hour{height:var(--hour-h);border-bottom:.5px solid rgba(84,84,88,.3);position:relative}
.lane-hour:nth-child(2n){background:rgba(255,255,255,.01)}
.hour-line{position:absolute;top:50%;left:0;right:0;height:.5px;background:rgba(84,84,88,.15)}

/* Task blocks */
.task-block{position:absolute;left:4px;right:4px;border-radius:8px;padding:6px 8px;cursor:grab;user-select:none;touch-action:none;z-index:10;min-height:20px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,.4)}
.task-block:active{cursor:grabbing;opacity:.85}
.task-title{font-size:11px;font-weight:600;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.task-time{font-size:10px;opacity:.75;margin-top:1px}
.task-inactive{opacity:.4}
.t-research{background:linear-gradient(135deg,#1EF0A1,#009F6B)}
.t-social{background:linear-gradient(135deg,#9333ea,#c084fc)}
.t-code{background:linear-gradient(135deg,#0e7490,#64d2ff20);border:1px solid #64d2ff40}
.t-content{background:linear-gradient(135deg,#92400e,#fbbf24)}
.t-sync{background:linear-gradient(135deg,#166534,#30d158)}
.t-custom{background:linear-gradient(135deg,#374151,#6b7280)}

/* Current time line */
.now-line{position:absolute;left:0;right:0;height:2px;background:var(--accent);z-index:50;pointer-events:none;box-shadow:0 0 6px rgba(30,240,161,0.5)}
.now-dot{position:absolute;left:-4px;top:-4px;width:10px;height:10px;border-radius:50%;background:var(--accent);box-shadow:0 0 8px rgba(30,240,161,0.7)}

/* FAB */
.fab{position:fixed;bottom:calc(24px + var(--safe-bot));right:20px;width:52px;height:52px;border-radius:50%;border:none;background:var(--accent);color:#000;font-size:26px;cursor:pointer;box-shadow:0 4px 20px rgba(30,240,161,.4);z-index:200;display:flex;align-items:center;justify-content:center;transition:transform .15s,box-shadow .15s}
.fab:active{transform:scale(.92);box-shadow:0 2px 10px rgba(30,240,161,.3)}

/* Desktop: center planer content */
@media(min-width:960px){
  body{max-width:1200px;margin:0 auto;border-left:.5px solid rgba(255,255,255,.05);border-right:.5px solid rgba(255,255,255,.05)}
  .fab{right:calc(max(20px,(100vw - 1200px)/2 + 20px))}
  .agent-hdr{font-size:13px;padding:10px 8px}
}

/* Modal */
.overlay{position:fixed;inset:0;background:rgba(0,0,0,.7);z-index:300;display:flex;align-items:flex-end;justify-content:center}
.overlay.hidden{display:none}
.modal{background:var(--card);border-radius:20px 20px 0 0;width:100%;max-width:600px;padding:20px 20px calc(20px + var(--safe-bot));max-height:90vh;overflow-y:auto}
.modal-handle{width:36px;height:4px;background:var(--sep);border-radius:2px;margin:0 auto 16px}
.modal-title{font-size:17px;font-weight:700;margin-bottom:20px}
.form-row{margin-bottom:16px}
.form-label{font-size:12px;color:var(--t2);font-weight:600;margin-bottom:6px;display:block}
.form-input,.form-select{width:100%;background:var(--card2);border:.5px solid var(--sep);border-radius:10px;padding:12px;color:var(--text);font-size:15px;font-family:inherit;-webkit-appearance:none;appearance:none}
.form-select option{background:#1c1c1e}
.form-row-2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.form-toggle{display:flex;align-items:center;justify-content:space-between}
.toggle{width:51px;height:31px;background:var(--sep);border-radius:16px;position:relative;cursor:pointer;transition:background .2s;flex-shrink:0}
.toggle.on{background:var(--green)}
.toggle-knob{position:absolute;top:2px;left:2px;width:27px;height:27px;border-radius:50%;background:#fff;transition:transform .2s;box-shadow:0 1px 4px rgba(0,0,0,.4)}
.toggle.on .toggle-knob{transform:translateX(20px)}
.modal-btns{display:flex;gap:10px;margin-top:20px}
.btn-cancel{flex:1;padding:14px;border:none;border-radius:12px;background:var(--card2);color:var(--t2);font-size:15px;font-weight:600;cursor:pointer}
.btn-save{flex:2;padding:14px;border:none;border-radius:12px;background:var(--accent);color:#000;font-size:15px;font-weight:600;cursor:pointer}
.btn-del{flex:1;padding:14px;border:none;border-radius:12px;background:rgba(255,69,58,.15);color:var(--red);font-size:15px;font-weight:600;cursor:pointer}
</style>
</head>
<body>
<div class="hdr">
  <a href="/?t=__TOKEN__" class="back-link">← Dashboard</a>
  <div class="hdr-nav">
    <button class="nav-btn" onclick="changeDate(-1)">‹</button>
    <div class="date-label" id="date-label">Heute</div>
    <button class="nav-btn" onclick="changeDate(1)">›</button>
  </div>
  <div style="width:80px;text-align:right;font-size:12px;color:var(--t3)" id="task-count">0 Tasks</div>
</div>

<div class="planer-wrap">
  <div class="agent-headers" id="agent-headers">
    <div class="agent-hdr">Aria</div>
  </div>
  <div class="timeline-scroll" id="timeline-scroll">
    <div class="timeline-inner">
      <div class="gutter" id="gutter"></div>
      <div class="lanes" id="lanes">
        <div class="lane" id="lane-aria" data-agent="aria"></div>
      </div>
    </div>
  </div>
</div>

<button class="fab" onclick="openModal(null)">+</button>

<div class="overlay hidden" id="overlay" onclick="overlayClick(event)">
  <div class="modal" id="modal">
    <div class="modal-handle"></div>
    <div class="modal-title" id="modal-title">Neuer Task</div>
    <div class="form-row">
      <label class="form-label">Aufgabe</label>
      <input class="form-input" id="f-title" placeholder="Was soll Aria tun?">
    </div>
    <div class="form-row">
      <label class="form-label">Typ</label>
      <select class="form-select" id="f-type">
        <option value="research">🔬 Recherche / Analyse</option>
        <option value="social">📱 Social Media</option>
        <option value="code">💻 Code / Deploy</option>
        <option value="content">✍️ Content erstellen</option>
        <option value="sync">🔄 Sync / Backup</option>
        <option value="custom">📋 Custom</option>
      </select>
    </div>
    <div class="form-row form-row-2">
      <div>
        <label class="form-label">Startzeit</label>
        <input class="form-input" id="f-start" type="time" value="02:00">
      </div>
      <div>
        <label class="form-label">Dauer (Min)</label>
        <input class="form-input" id="f-dur" type="number" value="30" min="15" step="15">
      </div>
    </div>
    <div class="form-row">
      <label class="form-label">Notizen</label>
      <input class="form-input" id="f-notes" placeholder="Optional">
    </div>
    <div class="form-row form-toggle">
      <span class="form-label" style="margin:0">Aktiv</span>
      <div class="toggle on" id="f-toggle" onclick="toggleActive()"><div class="toggle-knob"></div></div>
    </div>
    <div class="modal-btns">
      <button class="btn-del" id="btn-del" style="display:none" onclick="deleteTask()">Löschen</button>
      <button class="btn-cancel" onclick="closeModal()">Abbrechen</button>
      <button class="btn-save" onclick="saveTask()">Speichern</button>
    </div>
  </div>
</div>

<script>
const TOKEN = new URLSearchParams(window.location.search).get('t') || '';
const HOUR_H = 80;
const TYPES = {research:'t-research',social:'t-social',code:'t-code',content:'t-content',sync:'t-sync',custom:'t-custom'};
const TYPE_LABELS = {research:'Recherche',social:'Social',code:'Code',content:'Content',sync:'Sync',custom:'Custom'};

let currentDate = new Date();
let tasks = [];
let editId = null;
let activeToggle = true;
let dragState = null;

// Format date
function fmtDate(d) {
  return d.toLocaleDateString('de-DE',{weekday:'short',day:'numeric',month:'long',year:'numeric'});
}
function fmtDateKey(d) {
  return d.toISOString().slice(0,10);
}

// Time helpers
function timeToMin(t) { const [h,m] = t.split(':').map(Number); return h*60+m; }
function minToTime(m) { return String(Math.floor(m/60)).padStart(2,'0')+':'+String(m%60).padStart(2,'0'); }
function minToY(m) { return m * HOUR_H / 60; }
function yToMin(y) { return Math.round(y * 60 / HOUR_H / 15) * 15; }

// API
async function apiFetch(path, opts) {
  const sep = path.includes('?') ? '&' : '?';
  return fetch(path + sep + 't=' + TOKEN, opts).then(r => r.json());
}

// Build timeline structure
function buildTimeline() {
  const gutter = document.getElementById('gutter');
  const lane = document.getElementById('lane-aria');
  gutter.innerHTML = '';
  lane.innerHTML = '';
  for (let h = 0; h < 24; h++) {
    const g = document.createElement('div');
    g.className = 'gutter-hour';
    g.textContent = String(h).padStart(2,'0') + ':00';
    gutter.appendChild(g);
    const row = document.createElement('div');
    row.className = 'lane-hour';
    row.dataset.hour = h;
    const hl = document.createElement('div');
    hl.className = 'hour-line';
    row.appendChild(hl);
    lane.appendChild(row);
  }
  // Tap on lane to create
  lane.addEventListener('pointerup', onLaneTap);
}

function onLaneTap(e) {
  if (dragState) return;
  if (e.target.closest('.task-block')) return;
  const rect = e.currentTarget.getBoundingClientRect();
  const y = e.clientY - rect.top;
  const minutes = yToMin(y);
  openModal(null, minToTime(Math.max(0, Math.min(1410, minutes))));
}

// Render tasks for current date
function renderTasks() {
  const lane = document.getElementById('lane-aria');
  lane.querySelectorAll('.task-block').forEach(el => el.remove());
  const dateKey = fmtDateKey(currentDate);
  const dayTasks = tasks.filter(t => t.date === dateKey && t.agent === 'aria');
  document.getElementById('task-count').textContent = dayTasks.length + ' Task' + (dayTasks.length !== 1 ? 's' : '');
  dayTasks.forEach(task => {
    const block = document.createElement('div');
    block.className = 'task-block ' + (TYPES[task.type] || 't-custom') + (task.active ? '' : ' task-inactive');
    block.dataset.id = task.id;
    const startMin = timeToMin(task.start);
    const topPx = minToY(startMin);
    const hPx = Math.max(24, minToY(task.duration));
    block.style.top = topPx + 'px';
    block.style.height = hPx + 'px';
    block.innerHTML = '<div class="task-title">' + escHtml(task.title) + '</div>' +
      (hPx > 36 ? '<div class="task-time">' + task.start + ' · ' + task.duration + 'min</div>' : '');
    // Drag (also handles tap-to-edit at end of pointerup if no movement)
    initDrag(block, task);
    lane.appendChild(block);
  });
  updateNowLine();
}

function escHtml(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }

// Current time indicator
function updateNowLine() {
  const lane = document.getElementById('lane-aria');
  const old = lane.querySelector('.now-line');
  if (old) old.remove();
  const today = new Date();
  if (fmtDateKey(today) !== fmtDateKey(currentDate)) return;
  const mins = today.getHours()*60 + today.getMinutes();
  const line = document.createElement('div');
  line.className = 'now-line';
  line.style.top = minToY(mins) + 'px';
  line.innerHTML = '<div class="now-dot"></div>';
  lane.appendChild(line);
}

// Drag & Drop
function initDrag(el, task) {
  let startY, startTop, startMin;
  el.addEventListener('pointerdown', e => {
    if (e.button && e.button !== 0) return;
    e.preventDefault();
    el.setPointerCapture(e.pointerId);
    const rect = el.getBoundingClientRect();
    startY = e.clientY;
    startTop = parseInt(el.style.top);
    startMin = timeToMin(task.start);
    dragState = {id: task.id, moving: false};
  });
  el.addEventListener('pointermove', e => {
    if (!dragState || dragState.id !== task.id) return;
    const dy = e.clientY - startY;
    if (Math.abs(dy) > 5) dragState.moving = true;
    if (!dragState.moving) return;
    const newTop = Math.max(0, Math.min(minToY(24*60 - task.duration), startTop + dy));
    el.style.top = newTop + 'px';
  });
  el.addEventListener('pointerup', e => {
    if (!dragState || dragState.id !== task.id) return;
    const wasMoving = dragState.moving;
    dragState = null;
    if (!wasMoving) {
      // Tap (no drag) = open edit modal
      e.stopPropagation();
      openModal(task.id);
      return;
    }
    const newMin = yToMin(parseInt(el.style.top));
    const clamped = Math.max(0, Math.min(1410, newMin));
    task.start = minToTime(clamped);
    apiFetch('/api/tasks', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(task)});
    renderTasks();
  });
}

// Modal
function openModal(id, presetTime) {
  editId = id;
  activeToggle = true;
  const isEdit = id !== null;
  document.getElementById('modal-title').textContent = isEdit ? 'Task bearbeiten' : 'Neuer Task';
  document.getElementById('btn-del').style.display = isEdit ? '' : 'none';
  if (isEdit) {
    const t = tasks.find(x => x.id === id);
    if (!t) return;
    document.getElementById('f-title').value = t.title;
    document.getElementById('f-type').value = t.type;
    document.getElementById('f-start').value = t.start;
    document.getElementById('f-dur').value = t.duration;
    document.getElementById('f-notes').value = t.notes || '';
    activeToggle = t.active;
  } else {
    document.getElementById('f-title').value = '';
    document.getElementById('f-type').value = 'research';
    document.getElementById('f-start').value = presetTime || '02:00';
    document.getElementById('f-dur').value = '30';
    document.getElementById('f-notes').value = '';
    activeToggle = true;
  }
  setToggleUI(activeToggle);
  document.getElementById('overlay').classList.remove('hidden');
  setTimeout(() => document.getElementById('f-title').focus(), 100);
}

function closeModal() { document.getElementById('overlay').classList.add('hidden'); editId = null; }
function overlayClick(e) { if (e.target === document.getElementById('overlay')) closeModal(); }
function toggleActive() { activeToggle = !activeToggle; setToggleUI(activeToggle); }
function setToggleUI(v) { document.getElementById('f-toggle').className = 'toggle' + (v ? ' on' : ''); }

async function saveTask() {
  try {
    const titleEl = document.getElementById('f-title');
    const title = (titleEl && titleEl.value || '').trim();
    if (!title) {
      alert('Bitte Aufgabe eingeben');
      if (titleEl) titleEl.focus();
      return;
    }
    const newId = (typeof crypto !== 'undefined' && crypto.randomUUID)
      ? crypto.randomUUID()
      : 't_' + Date.now() + '_' + Math.random().toString(36).slice(2, 8);
    const task = {
      id: editId || newId,
      agent: 'aria',
      title,
      type: (document.getElementById('f-type') || {}).value || 'custom',
      date: fmtDateKey(currentDate),
      start: (document.getElementById('f-start') || {}).value || '08:00',
      duration: parseInt((document.getElementById('f-dur') || {}).value) || 30,
      active: activeToggle,
      status: 'pending',
      notes: ((document.getElementById('f-notes') || {}).value || '').trim()
    };
    const res = await apiFetch('/api/tasks', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(task)});
    if (res && res.ok === false) {
      alert('Fehler beim Speichern: ' + (res.message || res.error || 'unbekannt'));
      return;
    }
    closeModal();
    await loadTasks();
  } catch (err) {
    alert('Speichern fehlgeschlagen: ' + (err && err.message ? err.message : err));
    console.error('saveTask error', err);
  }
}

async function deleteTask() {
  try {
    if (!editId) {
      alert('Kein Task ausgewaehlt. Tippe auf einen bestehenden Task in der Timeline um ihn zu bearbeiten.');
      return;
    }
    if (!confirm('Task wirklich loeschen?')) return;
    const res = await apiFetch('/api/tasks/delete', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({id:editId,token:TOKEN})});
    if (res && res.ok === false) {
      alert('Fehler beim Loeschen: ' + (res.message || res.error || 'unbekannt'));
      return;
    }
    closeModal();
    await loadTasks();
  } catch (err) {
    alert('Loeschen fehlgeschlagen: ' + (err && err.message ? err.message : err));
    console.error('deleteTask error', err);
  }
}

// Load tasks
async function loadTasks() {
  const data = await apiFetch('/api/tasks');
  tasks = data.tasks || [];
  renderTasks();
}

// Date nav
function changeDate(dir) {
  currentDate = new Date(currentDate.getTime() + dir * 86400000);
  document.getElementById('date-label').textContent = fmtDate(currentDate);
  renderTasks();
}

// Scroll to current time or 22:00 if evening
function scrollToNow() {
  const now = new Date();
  const h = now.getHours();
  const targetHour = h >= 20 ? 22 : (h >= 8 ? h - 1 : 0);
  document.getElementById('timeline-scroll').scrollTop = targetHour * HOUR_H;
}

// Init
buildTimeline();
document.getElementById('date-label').textContent = fmtDate(currentDate);
loadTasks().then(scrollToNow);
setInterval(updateNowLine, 60000);
</script>
</body>
</html>z/root/aria/state/workersrez^[a-zA-Z0-9][a-zA-Z0-9-]*$urA  <!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover,user-scalable=no">
<title>Aria Team</title>
<meta name="theme-color" content="#080808">
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
:root{
  --bg:#14141a;--card:rgba(255,255,255,0.045);--card-solid:#1c1d25;
  --text:#F0EEE8;--t2:rgba(240,238,232,0.85);--t3:rgba(240,238,232,0.55);--t4:rgba(240,238,232,0.28);
  --accent:#1EF0A1;--amber:#ffd60a;--red:#ff453a;--green:#1EF0A1;
  --border:rgba(255,255,255,0.08);--sep:rgba(255,255,255,0.07);
  --r:12px;--r-sm:8px;--safe-bot:env(safe-area-inset-bottom,0px);
}
body{font-family:'IBM Plex Sans',-apple-system,sans-serif;background:var(--bg);color:var(--text);min-height:100vh;overflow-x:hidden;-webkit-font-smoothing:antialiased}
.app{max-width:960px;margin:0 auto;padding-bottom:calc(80px + var(--safe-bot))}
.hdr{position:sticky;top:0;z-index:50;background:rgba(15,15,22,0.92);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);
  border-bottom:1px solid var(--sep);padding:14px 20px;padding-top:max(14px,env(safe-area-inset-top));
  display:flex;align-items:center;justify-content:space-between}
.hdr-left{display:flex;align-items:center;gap:12px}
.hdr-title{font-size:16px;font-weight:700}
.back-link{font-size:13px;color:var(--accent);text-decoration:none;padding:8px 0}
.sec-hdr{font-size:10px;font-weight:700;color:var(--t4);text-transform:uppercase;letter-spacing:0.9px;
  padding:14px 20px 6px;display:flex;align-items:center;gap:8px}
.sec-hdr::before{content:'';width:2px;height:12px;background:linear-gradient(to bottom,var(--accent),var(--accent));border-radius:1px;flex-shrink:0}
.workers-list{padding:0 16px;display:flex;flex-direction:column;gap:10px}
.worker-card{background:var(--card);border:1px solid var(--border);border-radius:var(--r);overflow:hidden}
.worker-card.ready{border-color:rgba(30,240,161,0.2)}
.worker-card.dead{border-color:rgba(255,69,58,0.18);opacity:0.7}
.worker-head{padding:10px 14px;display:flex;align-items:center;gap:10px;cursor:pointer;user-select:none}
.worker-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
.worker-dot.ready{background:var(--green);box-shadow:0 0 0 3px rgba(30,240,161,0.15);animation:pulse 2.4s ease-in-out infinite}
.worker-dot.busy{background:var(--amber);box-shadow:0 0 0 3px rgba(255,214,10,0.15)}
.worker-dot.dead{background:var(--red)}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.3}}
.worker-name{font-size:14px;font-weight:700;letter-spacing:-0.2px}
.worker-role{font-size:11px;color:var(--t3);margin-top:1px}
.worker-meta{display:flex;align-items:center;gap:8px;margin-left:auto;flex-shrink:0}
.mode-badge{font-size:10px;padding:2px 7px;border-radius:5px;font-weight:700;
  background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.08);color:var(--t2)}
.mode-badge.yolo{background:rgba(255,214,10,0.12);border-color:rgba(255,214,10,0.2);color:var(--amber)}
.mode-badge.off{background:rgba(255,69,58,0.08);border-color:rgba(255,69,58,0.15);color:var(--red)}
.ago-label{font-size:11px;color:var(--t4);font-variant-numeric:tabular-nums;white-space:nowrap}
.worker-body{display:none;border-top:1px solid var(--sep)}
.worker-body.open{display:block}
.pane-box{margin:10px 14px;background:rgba(0,0,0,0.45);border:1px solid rgba(255,255,255,0.04);
  border-radius:8px;padding:10px 12px;font-family:'JetBrains Mono',monospace;font-size:11px;
  color:var(--t3);max-height:180px;overflow-y:auto;white-space:pre-wrap;word-break:break-all;line-height:1.65}
.worker-info{padding:4px 14px 8px;display:flex;flex-wrap:wrap;gap:6px}
.info-chip{font-size:10px;color:var(--t4);background:rgba(255,255,255,0.04);
  border:1px solid rgba(255,255,255,0.06);border-radius:5px;padding:2px 7px;font-family:'JetBrains Mono',monospace}
.worker-controls{padding:8px 14px 12px;display:flex;flex-wrap:wrap;gap:6px}
.wbtn{padding:6px 11px;border-radius:7px;border:1px solid;font-size:12px;font-weight:600;
  font-family:inherit;cursor:pointer;transition:all 0.15s;background:transparent}
.wbtn:active{transform:scale(0.96);opacity:0.8}
.wbtn:disabled{opacity:0.3;cursor:wait}
.wbtn-view{border-color:rgba(100,210,255,0.3);color:#64d2ff}
.wbtn-send{border-color:rgba(30,240,161,0.3);color:var(--accent)}
.wbtn-stop{border-color:rgba(255,69,58,0.25);color:var(--red)}
.wbtn-mode{border-color:rgba(255,255,255,0.12);color:var(--t2)}
.wbtn-refresh{border-color:rgba(255,255,255,0.1);color:var(--t3)}
.prompt-row{display:flex;gap:6px;padding:0 14px 10px}
.prompt-input{flex:1;background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.1);
  border-radius:10px;color:var(--text);font-family:inherit;font-size:13px;padding:7px 12px;
  outline:none;transition:border-color 0.2s}
.prompt-input:focus{border-color:rgba(30,240,161,0.35)}
.prompt-input::placeholder{color:var(--t4)}
.prompt-fb{font-size:11px;color:var(--t4);padding:0 14px 8px;height:20px;transition:color 0.3s}
.prompt-fb.ok{color:var(--accent)}
.prompt-fb.err{color:var(--red)}
.spawn-form{margin:0 16px 10px;background:var(--card);border:1px solid var(--border);
  border-radius:var(--r);padding:14px}
.spawn-form .form-row{margin-bottom:10px}
.spawn-form label{font-size:11px;color:var(--t3);font-weight:600;display:block;margin-bottom:4px;text-transform:uppercase;letter-spacing:0.4px}
.spawn-form input{width:100%;background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.1);
  border-radius:8px;color:var(--text);font-family:inherit;font-size:14px;padding:9px 12px;outline:none;transition:border-color 0.2s}
.spawn-form input:focus{border-color:rgba(30,240,161,0.35)}
.spawn-btn{width:100%;padding:10px;border:none;border-radius:10px;background:var(--accent);
  color:#000;font-family:inherit;font-size:14px;font-weight:700;cursor:pointer;transition:opacity 0.15s}
.spawn-btn:active{opacity:0.8}
.spawn-btn:disabled{opacity:0.4;cursor:wait}
.spawn-fb{font-size:12px;color:var(--t4);text-align:center;margin-top:8px;min-height:18px;transition:color 0.3s}
.spawn-fb.ok{color:var(--accent)}
.spawn-fb.err{color:var(--red)}
.empty-state{padding:40px 20px;text-align:center;color:var(--t4);font-size:13px}
.refresh-bar{display:flex;align-items:center;gap:8px;padding:6px 20px;font-size:11px;color:var(--t4)}
.refresh-bar button{background:none;border:1px solid var(--border);border-radius:6px;
  color:var(--t3);font-family:inherit;font-size:11px;padding:4px 10px;cursor:pointer}
.refresh-bar button:active{opacity:0.7}
@media(min-width:640px){
  .workers-list{padding:0 24px}
  .spawn-form{margin:0 24px 10px}
  .hdr{padding:14px 24px}
  .sec-hdr{padding:14px 24px 6px}
  .refresh-bar{padding:6px 24px}
}
</style>
</head>
<body>
<div class="app">
<div class="hdr">
  <div class="hdr-left">
    <a href="/?t=__TOKEN__" class="back-link">&#8592; Dashboard</a>
    <div class="hdr-title">Team</div>
  </div>
  <div style="font-size:11px;color:var(--t4)" id="hdr-count">&#8212; workers</div>
</div>
<div class="sec-hdr">Aktive Worker</div>
<div class="refresh-bar">
  <span id="refresh-ts">&#8212;</span>
  <button onclick="loadWorkers()">Refresh</button>
  <span style="margin-left:auto;color:var(--t4);font-size:10px">Auto 10s</span>
</div>
<div class="workers-list" id="workers-list">
  <div class="empty-state">Lade...</div>
</div>
<div class="sec-hdr" style="margin-top:16px">Worker starten</div>
<div class="spawn-form">
  <div class="form-row">
    <label>Name</label>
    <input id="sp-name" placeholder="kadi-dev" pattern="[a-zA-Z0-9][a-zA-Z0-9-]*">
  </div>
  <div class="form-row">
    <label>Projekt-Pfad</label>
    <input id="sp-project" placeholder="/root/projekte/Kadi-v2">
  </div>
  <div class="form-row" style="margin-bottom:14px">
    <label>Rolle</label>
    <input id="sp-role" placeholder="frontend">
  </div>
  <button class="spawn-btn" id="spawn-btn" onclick="spawnWorker()">Worker starten</button>
  <div class="spawn-fb" id="spawn-fb"></div>
</div>
</div>
<script>
const TOKEN = new URLSearchParams(window.location.search).get('t') || '';
let _workers = [];
function escH(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
async function apiFetch(path, opts) {
  const sep = path.includes('?') ? '&' : '?';
  return fetch(path + sep + 't=' + TOKEN, opts).then(r => r.json());
}
async function loadWorkers() {
  try {
    const d = await apiFetch('/api/workers');
    if (!d.ok) throw new Error(d.error || 'unknown');
    _workers = d.workers || [];
    renderWorkers(_workers);
    const ts = new Date().toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit',second:'2-digit'});
    document.getElementById('refresh-ts').textContent = 'Stand ' + ts;
    document.getElementById('hdr-count').textContent = _workers.length + ' worker' + (_workers.length !== 1 ? 's' : '');
  } catch(e) {
    document.getElementById('workers-list').innerHTML =
      '<div class="empty-state" style="color:var(--red)">Fehler: ' + escH(e.message) + '</div>';
  }
}
function renderWorkers(workers) {
  const list = document.getElementById('workers-list');
  if (!workers.length) {
    list.innerHTML = '<div class="empty-state">Keine aktiven Worker. Worker oben starten.</div>';
    return;
  }
  const openSet = new Set([...list.querySelectorAll('.worker-body.open')].map(el => el.dataset.name));
  list.innerHTML = workers.map(w => workerCard(w, openSet.has(w.name))).join('');
}
function dotClass(w) {
  if (!w.alive) return 'dead';
  if (w.ready) return 'ready';
  return 'busy';
}
function workerCard(w, open) {
  const dc = dotClass(w);
  const mc = w.mode === 'yolo' ? 'yolo' : (w.mode === 'off' ? 'off' : '');
  const branch = w.branch ? '<span class="info-chip">' + escH(w.branch) + '</span>' : '';
  const workdir = w.workdir ? '<span class="info-chip">' + escH(w.workdir) + '</span>' : '';
  const pid = w.pid && w.pid !== 'unknown' ? '<span class="info-chip">PID ' + escH(w.pid) + '</span>' : '';
  return `
<div class="worker-card ${dc}" id="wcard-${escH(w.name)}">
  <div class="worker-head" onclick="toggleCard('${escH(w.name)}')">
    <span class="worker-dot ${dc}"></span>
    <div style="flex:1;min-width:0">
      <div class="worker-name">${escH(w.name)}</div>
      <div class="worker-role">${escH(w.role || '—')} &middot; ${escH(w.scope || '—')}</div>
    </div>
    <div class="worker-meta">
      <span class="mode-badge ${mc}">${escH(w.mode || 'assist')}</span>
      <span class="ago-label">${escH(w.last_activity_ago || '—')}</span>
      <span style="color:var(--t4);font-size:14px">${open ? '▾' : '›'}</span>
    </div>
  </div>
  <div class="worker-body${open ? ' open' : ''}" data-name="${escH(w.name)}">
    <div class="pane-box" id="pane-${escH(w.name)}">${escH(w.pane_snapshot || '—')}</div>
    <div class="worker-info">
      ${workdir}${branch}${pid}
    </div>
    <div class="worker-controls">
      <button class="wbtn wbtn-view" onclick="attachWorker('${escH(w.name)}')">Attach (info)</button>
      <button class="wbtn wbtn-refresh" onclick="refreshPane('${escH(w.name)}')">Pane</button>
      <button class="wbtn wbtn-mode" onclick="cycleMode('${escH(w.name)}','${escH(w.mode || 'assist')}')">Mode</button>
      <button class="wbtn wbtn-stop" onclick="stopWorker('${escH(w.name)}')">Stop</button>
    </div>
    <div class="prompt-row">
      <input class="prompt-input" id="pinput-${escH(w.name)}" placeholder="Prompt an ${escH(w.name)} senden…"
        onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();sendPrompt('${escH(w.name)}');}">
      <button class="wbtn wbtn-send" onclick="sendPrompt('${escH(w.name)}')">Send</button>
    </div>
    <div class="prompt-fb" id="pfb-${escH(w.name)}"></div>
  </div>
</div>`;
}
function toggleCard(name) {
  const body = document.querySelector(`#wcard-${name} .worker-body`);
  const arrow = document.querySelector(`#wcard-${name} .worker-head span:last-child`);
  if (!body) return;
  body.classList.toggle('open');
  if (arrow) arrow.textContent = body.classList.contains('open') ? '▾' : '›';
}
async function refreshPane(name) {
  const box = document.getElementById('pane-' + name);
  if (!box) return;
  box.textContent = 'Lade…';
  try {
    const d = await apiFetch('/api/worker-pane?name=' + encodeURIComponent(name));
    box.textContent = d.ok ? (d.pane || '(leer)') : ('Fehler: ' + (d.message || 'unknown'));
    box.scrollTop = box.scrollHeight;
  } catch(e) {
    box.textContent = 'Fehler: ' + e.message;
  }
}
async function sendPrompt(name) {
  const input = document.getElementById('pinput-' + name);
  const fb = document.getElementById('pfb-' + name);
  const text = (input && input.value || '').trim();
  if (!text) return;
  input.disabled = true;
  if (fb) { fb.textContent = 'Sende…'; fb.className = 'prompt-fb'; }
  try {
    const d = await apiFetch('/api/worker-send', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({name, text})
    });
    if (d.ok) {
      input.value = '';
      if (fb) { fb.textContent = '✓ Gesendet'; fb.className = 'prompt-fb ok'; }
      setTimeout(() => { if (fb) { fb.textContent = ''; fb.className = 'prompt-fb'; } }, 2500);
      setTimeout(() => refreshPane(name), 4000);
    } else {
      if (fb) { fb.textContent = '✗ ' + (d.message || 'Fehler'); fb.className = 'prompt-fb err'; }
    }
  } catch(e) {
    if (fb) { fb.textContent = '✗ Verbindungsfehler'; fb.className = 'prompt-fb err'; }
  }
  input.disabled = false;
  if (input) input.focus();
}
function attachWorker(name) {
  alert('tmux attach -t aria-' + name + '\n\nIn Terminal einfügen zum live Attach.');
}
async function cycleMode(name, current) {
  const modes = ['assist', 'auto', 'yolo'];
  const next = modes[(modes.indexOf(current) + 1) % modes.length];
  if (!confirm('Mode von "' + current + '" auf "' + next + '" ändern?')) return;
  try {
    const d = await apiFetch('/api/worker-mode', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({name, mode: next})
    });
    if (d.ok) { loadWorkers(); }
    else { alert('Fehler: ' + (d.message || 'unknown')); }
  } catch(e) { alert('Fehler: ' + e.message); }
}
async function stopWorker(name) {
  if (!confirm('Worker "' + name + '" stoppen?')) return;
  const card = document.getElementById('wcard-' + name);
  if (card) card.style.opacity = '0.4';
  try {
    const d = await apiFetch('/api/worker-stop', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({name})
    });
    setTimeout(loadWorkers, 1500);
    if (!d.ok) alert('Stop-Fehler: ' + (d.message || 'unknown'));
  } catch(e) {
    alert('Fehler: ' + e.message);
    if (card) card.style.opacity = '';
  }
}
async function spawnWorker() {
  const name    = (document.getElementById('sp-name').value || '').trim();
  const project = (document.getElementById('sp-project').value || '').trim();
  const role    = (document.getElementById('sp-role').value || '').trim();
  const fb = document.getElementById('spawn-fb');
  const btn = document.getElementById('spawn-btn');
  if (!name || !project || !role) {
    fb.textContent = 'Alle 3 Felder ausfüllen';
    fb.className = 'spawn-fb err';
    return;
  }
  btn.disabled = true;
  fb.textContent = 'Starte… (~30s)';
  fb.className = 'spawn-fb';
  try {
    const d = await apiFetch('/api/worker-start', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({name, project, role})
    });
    if (d.ok) {
      fb.textContent = '✓ Spawn gestartet für ' + name + '. Erscheint in ~30s.';
      fb.className = 'spawn-fb ok';
      document.getElementById('sp-name').value = '';
      document.getElementById('sp-role').value = '';
      let polls = 0;
      const poll = setInterval(async () => {
        polls++;
        await loadWorkers();
        const found = _workers.some(w => w.name === name);
        if (found || polls > 24) {
          clearInterval(poll);
          if (found) {
            fb.textContent = '✓ ' + name + ' ist online';
          } else {
            fb.textContent = 'Spawn läuft (prüfe /var/log/aria-worker-spawn.log)';
          }
        }
      }, 5000);
    } else {
      fb.textContent = '✗ ' + (d.message || 'Fehler');
      fb.className = 'spawn-fb err';
    }
  } catch(e) {
    fb.textContent = '✗ ' + e.message;
    fb.className = 'spawn-fb err';
  }
  btn.disabled = false;
}
loadWorkers();
setInterval(loadWorkers, 10000);
</script>
</body>
</html>ux <!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, user-scalable=no">
<title>Aria Control</title>
<meta name="theme-color" content="#080808">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="Aria">
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' fill='none' stroke='%231EF0A1' stroke-width='1.5'><circle cx='16' cy='16' r='13' opacity='0.3'/><path d='M16 6 L24 23 L8 23 Z'/><circle cx='16' cy='17' r='2' fill='%231EF0A1'/></svg>">
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
:root {
    --bg: #14141a; --bg-card: rgba(255,255,255,0.045); --bg-card-solid: #1c1d25;
    --text: #F0EEE8; --text-2: rgba(240,238,232,0.85); --text-3: rgba(240,238,232,0.55); --text-4: rgba(240,238,232,0.28);
    --accent: #1EF0A1; --cyan: #1EF0A1; --green: #1EF0A1; --amber: #ffd60a; --red: #ff453a;
    --border: rgba(255,255,255,0.08); --separator: rgba(255,255,255,0.07);
    --safe-bottom: env(safe-area-inset-bottom, 0px); --r: 12px; --r-sm: 8px;
}
body { font-family: 'IBM Plex Sans', -apple-system, sans-serif; background: var(--bg); color: var(--text); font-size: 15px; line-height: 1.47; margin: 0; -webkit-font-smoothing: antialiased; min-height: 100vh; overflow-x: hidden; }
.app { max-width: 960px; margin: 0 auto; padding-bottom: calc(180px + var(--safe-bottom)); overflow-x: hidden; }

/* Header */
.header {
    padding: 14px 20px; padding-top: max(14px, env(safe-area-inset-top));
    display: flex; align-items: center; justify-content: space-between;
    background: rgba(15,15,22,0.92); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px);
    position: sticky; top: 0; z-index: 50; border-bottom: 1px solid var(--separator);
    position: relative;
}
.header::after { content: ''; position: absolute; bottom: -1px; left: 0; right: 0; height: 1px; background: linear-gradient(90deg, transparent 5%, rgba(30,240,161,0.4) 35%, rgba(100,210,255,0.4) 65%, transparent 95%); }
.header { position: sticky; }
.header-left { display: flex; align-items: center; gap: 12px; }
.header-icon { width: 30px; height: 34px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.header-title { font-size: 16px; font-weight: 700; letter-spacing: -0.3px; }
.header-status { display: flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 500; color: var(--text-3); margin-top: 1px; }
.status-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--green); animation: pulse 2s ease-in-out infinite; flex-shrink: 0; }
.status-dot--off { background: var(--red); animation: none; }
.status-dot--check { background: var(--cyan); }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }

/* Sections */
.section { padding: 0 16px; margin-bottom: 16px; }
.section-header { font-size: 10px; font-weight: 700; color: var(--text-4); text-transform: uppercase; letter-spacing: 0.9px; padding: 10px 16px 4px; display: flex; align-items: center; gap: 8px; }
.section-header::before { content: ''; width: 2px; height: 12px; background: linear-gradient(to bottom, var(--accent), var(--cyan)); border-radius: 1px; flex-shrink: 0; }
.card-group { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; }
.card-row { display: flex; align-items: center; justify-content: space-between; padding: 7px 14px; min-height: 32px; border-bottom: 1px solid var(--separator); }
.card-row:last-child { border-bottom: none; }
.card-row-left { display: flex; align-items: center; gap: 8px; flex: 1; }
.card-row-label { font-size: 12px; }
.card-row-value { font-size: 11px; color: var(--text-3); }

/* Buttons */
.btn-row { display: flex; gap: 6px; padding: 0 14px; margin-bottom: 10px; }
.btn { flex: 1; padding: 7px; border-radius: var(--r-sm); cursor: pointer; font-size: 12px; font-weight: 600; font-family: inherit; transition: all 0.15s; min-height: 32px; }
.btn:active { transform: scale(0.97); opacity: 0.8; }
.btn-restart { background: transparent; border: 1px solid rgba(255,69,58,0.25); color: var(--red); }
.btn-refresh { background: transparent; border: 1px solid rgba(30,240,161,0.2); color: var(--accent); }
.btn-git { background: transparent; border: 1px solid rgba(48,209,88,0.25); color: var(--green); }
.btn:disabled { opacity: 0.3; cursor: wait; transform: none; }

/* Stats tiles */
.sys-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; padding: 0 14px; margin-bottom: 10px; }
.sys-tile { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 8px 10px; }
.sys-tile-label { font-size: 9px; color: var(--text-4); font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }
.sys-tile-value { font-size: 14px; font-weight: 700; margin-top: 2px; letter-spacing: -0.3px; }
.stat-bar { height: 2px; background: rgba(255,255,255,0.06); border-radius: 2px; margin-top: 8px; overflow: hidden; }
.stat-bar-fill { height: 100%; border-radius: 2px; transition: width 0.5s; }
.bar-green { background: linear-gradient(90deg, var(--green), #34d399); }
.bar-yellow { background: linear-gradient(90deg, var(--amber), #fb923c); }
.bar-red { background: linear-gradient(90deg, var(--red), #f87171); }

/* Live banner */
.live-banner { background: linear-gradient(135deg, rgba(30,240,161,0.06), rgba(100,210,255,0.03)); border: 1px solid rgba(30,240,161,0.12); border-radius: var(--r); padding: 14px 16px; margin: 12px 16px; display: flex; align-items: center; gap: 12px; }
.live-spinner { width: 16px; height: 16px; border: 1.5px solid rgba(30,240,161,0.2); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.8s linear infinite; flex-shrink: 0; }
@keyframes spin { to { transform: rotate(360deg); } }
.live-label { font-size: 13px; color: var(--text-3); }
.live-label strong { color: var(--text); font-weight: 600; }

/* Log box */
.log-box { background: rgba(0,0,0,0.5); border: 1px solid rgba(255,255,255,0.04); border-radius: 8px; padding: 12px; font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--text-3); max-height: 200px; overflow-y: auto; overflow-x: hidden; line-height: 1.7; white-space: pre-wrap; word-break: break-all; width: 100%; }

/* Badges */
.badge { font-size: 11px; padding: 2px 8px; border-radius: 6px; font-weight: 600; letter-spacing: 0.2px; }
.badge-g { background: rgba(48,209,88,0.1); color: var(--green); border: 1px solid rgba(48,209,88,0.2); }
.badge-r { background: rgba(255,69,58,0.1); color: var(--red); border: 1px solid rgba(255,69,58,0.2); }

/* Tasks */
.task-item { font-size: 13px; padding: 6px 0; display: flex; align-items: center; gap: 8px; }
.task-done { color: var(--green); } .task-active { color: var(--cyan); } .task-pending { color: var(--text-4); }

/* Action log */
.action-log { margin: 0 16px; padding: 10px 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; font-family: monospace; font-size: 11px; color: var(--text-4); max-height: 80px; overflow-y: auto; display: none; }

/* Dot */
.dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 6px; }
.dot-g { background: var(--green); box-shadow: 0 0 5px rgba(48,209,88,0.5); }
.dot-a { background: var(--amber); }
.dot-r { background: var(--red); box-shadow: 0 0 5px rgba(255,69,58,0.5); }

/* Tab Bar */
.tab-bar { position: fixed; bottom: 0; left: 0; right: 0; background: rgba(8,8,12,0.95); backdrop-filter: saturate(180%) blur(24px); -webkit-backdrop-filter: saturate(180%) blur(24px); border-top: 0.5px solid rgba(255,255,255,0.07); display: flex; align-items: center; padding: 8px 0; padding-bottom: max(8px, var(--safe-bottom)); z-index: 100; justify-content: space-around; scrollbar-width: none; }
.tab-bar::-webkit-scrollbar { display: none; }
.tab-item { display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 4px 8px; border: none; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent; flex: 1; min-width: 0; position: relative; transition: opacity 0.15s; }
.tab-item svg { width: 22px; height: 22px; color: var(--text-4); transition: color 0.2s; }
.tab-item span { font-size: 9px; font-weight: 600; color: var(--text-4); transition: color 0.2s; letter-spacing: 0.4px; text-transform: uppercase; }
.tab-item.active svg { color: var(--accent); }
.tab-item.active span { color: var(--accent); }
.tab-item.active::after { content: ''; position: absolute; top: -8px; left: 50%; transform: translateX(-50%); width: 20px; height: 2px; background: var(--accent); border-radius: 0 0 3px 3px; box-shadow: 0 0 8px rgba(30,240,161,0.7); }

/* Issue / Calendar items */
.issue-item { padding: 7px 14px; border-bottom: 1px solid var(--separator); }
.issue-item:last-child { border-bottom: none; }
.issue-tappable { cursor: pointer; -webkit-tap-highlight-color: transparent; }
.issue-id { font-size: 9px; font-weight: 700; color: var(--text-4); margin-bottom: 1px; letter-spacing: 0.5px; text-transform: uppercase; }
.issue-title { font-size: 12px; color: var(--text); line-height: 1.35; }
.issue-sub { font-size: 11px; color: var(--amber); margin-top: 1px; }
.issue-row { display: flex; align-items: center; gap: 6px; }
.issue-row .issue-content { flex: 1; min-width: 0; }
.issue-actions { display: flex; gap: 4px; flex-shrink: 0; }
.issue-btn { height: 24px; border: 1px solid; border-radius: 6px; background: transparent; cursor: pointer; font-size: 10px; font-weight: 700; padding: 0 7px; transition: all 0.15s; white-space: nowrap; }
.issue-btn.start { border-color: rgba(147,197,253,0.4); color: var(--cyan); }
.issue-btn.start:hover { background: rgba(100,210,255,0.1); border-color: var(--cyan); }
.issue-btn.done { border-color: rgba(30,240,161,0.35); color: var(--accent); }
.issue-btn.done:hover { background: rgba(30,240,161,0.1); border-color: var(--accent); }
.issue-btn:disabled { opacity: 0.35; cursor: default; }
.cal-time { font-size: 13px; font-weight: 700; color: var(--cyan); white-space: nowrap; padding-left: 12px; }

/* Responsive */
@media (min-width: 640px) {
    .sys-grid { grid-template-columns: 1fr 1fr 1fr; padding: 0 24px; gap: 10px; }
    .section { padding: 0 24px; }
    .section-header { padding: 16px 24px 6px; }
    .btn-row { padding: 0 24px; }
    .header { padding: 14px 24px; }
    .live-banner { margin: 12px 24px; }
    .action-log { margin: 0 24px; }
}
@media (min-width: 960px) {
    .tab-bar { left: 50%; right: auto; transform: translateX(-50%); width: 960px; border-left: 0.5px solid rgba(255,255,255,0.07); border-right: 0.5px solid rgba(255,255,255,0.07); justify-content: center; }
}
/* Summary grid */
.sum-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; padding: 0 14px; margin-bottom: 4px; width: 100%; }
.sum-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 9px 11px 9px; overflow: hidden; min-width: 0; }
.sum-label { font-size: 9px; color: var(--text-4); font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 3px; }
.sum-val { font-size: 14px; font-weight: 700; letter-spacing: -0.3px; line-height: 1.1; }
.sum-sub { font-size: 10px; color: var(--text-4); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (min-width: 640px) { .sum-grid { grid-template-columns: repeat(4,1fr); padding: 0 24px; } }

/* Logo */
#aria-logo { filter: invert(1); transition: filter 0.2s; }

/* Theme toggle button */
#theme-btn, #brain-btn { background: none; border: none; cursor: pointer; padding: 6px; border-radius: 8px; color: var(--text-4); font-size: 16px; line-height: 1; display: flex; align-items: center; justify-content: center; transition: color 0.2s; }
#theme-btn:active, #brain-btn:active { opacity: 0.7; }
#brain-btn:hover { color: var(--accent); }
.qs-btn { background: var(--bg-card-solid); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 12px 8px; color: var(--text); font-family: inherit; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 6px; transition: border-color 0.2s, transform 0.1s; -webkit-tap-highlight-color: transparent; }
.qs-btn:hover { border-color: var(--accent); }
.qs-btn:active { transform: scale(0.97); }
.qs-btn:disabled { cursor: wait; }
.qs-label { font-size: 11px; color: var(--text-2); font-weight: 500; text-align: center; line-height: 1.2; }

/* Light mode */
body.light {
    --bg: #F2F2F7; --bg-card: rgba(0,0,0,0.04); --bg-card-solid: #FFFFFF;
    --text: #1C1C1E; --text-2: rgba(28,28,30,0.75); --text-3: rgba(28,28,30,0.45); --text-4: rgba(28,28,30,0.22);
    --accent: #059669; --cyan: #0891B2; --green: #059669; --amber: #D97706; --red: #DC2626;
    --border: rgba(0,0,0,0.08); --separator: rgba(0,0,0,0.05);
}
body.light .header { background: rgba(242,242,247,0.92); }
body.light .tab-bar { background: rgba(242,242,247,0.95); border-top-color: rgba(0,0,0,0.08); }
body.light .log-box { background: rgba(0,0,0,0.04); border-color: rgba(0,0,0,0.06); }
body.light .stat-bar { background: rgba(0,0,0,0.06); }
body.light .btn-restart { border-color: rgba(220,38,38,0.2); }
body.light .btn-refresh { border-color: rgba(5,150,105,0.2); }
body.light .btn-git { border-color: rgba(5,150,105,0.2); }
body.light .live-banner { background: linear-gradient(135deg, rgba(5,150,105,0.06), rgba(8,145,178,0.03)); border-color: rgba(5,150,105,0.12); }
body.light #aria-logo { filter: none; }
body.light .header::after { background: linear-gradient(90deg, transparent 5%, rgba(5,150,105,0.4) 35%, rgba(8,145,178,0.4) 65%, transparent 95%); }

/* Aria Inbox */
.aria-inbox { position: fixed; bottom: calc(56px + max(8px, var(--safe-bottom))); left: 0; right: 0; z-index: 99; background: rgba(8,8,12,0.97); backdrop-filter: saturate(180%) blur(24px); -webkit-backdrop-filter: saturate(180%) blur(24px); border-top: 1px solid rgba(30,240,161,0.18); padding: 8px 12px; }
@media (min-width: 960px) { .aria-inbox { left: 50%; right: auto; transform: translateX(-50%); width: 960px; border-left: 0.5px solid rgba(255,255,255,0.07); border-right: 0.5px solid rgba(255,255,255,0.07); } }
.inbox-row { display: flex; align-items: flex-end; gap: 8px; }
.inbox-input { flex: 1; background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.1); border-radius: 18px; color: var(--text); font-family: inherit; font-size: 14px; line-height: 1.4; padding: 8px 14px; resize: none; max-height: 100px; overflow-y: auto; outline: none; transition: border-color 0.2s; }
.inbox-input:focus { border-color: rgba(30,240,161,0.35); }
.inbox-input::placeholder { color: var(--text-4); }
.inbox-send { width: 36px; height: 36px; border-radius: 50%; border: none; background: var(--accent); color: #000; cursor: pointer; display: flex; align-items: center; justify-content: center; flex-shrink: 0; transition: opacity 0.15s, transform 0.1s; font-size: 14px; }
.inbox-send:active { transform: scale(0.92); opacity: 0.8; }
.inbox-send svg { width: 16px; height: 16px; }
.inbox-feedback { font-size: 11px; color: var(--text-4); text-align: center; padding-top: 4px; height: 16px; transition: color 0.3s; }
.inbox-feedback.ok { color: var(--accent); }
.inbox-feedback.err { color: var(--red); }
/* Effort quick-set — Buttons senden /effort <level> an die Aria-Session */
.effort-row { display: flex; align-items: center; gap: 6px; padding: 0 2px 6px; }
.effort-label { font-size: 10px; color: var(--text-4); text-transform: uppercase; letter-spacing: 0.06em; flex-shrink: 0; }
.effort-chip { flex: 1; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); border-radius: 12px; color: var(--text-3); font-family: inherit; font-size: 11px; padding: 4px 0; cursor: pointer; transition: background 0.15s, border-color 0.15s, color 0.15s; }
.effort-chip:active { transform: scale(0.94); }
.effort-chip.active { background: rgba(30,240,161,0.15); border-color: rgba(30,240,161,0.5); color: var(--accent); }
body.light .effort-chip { background: rgba(0,0,0,0.04); border-color: rgba(0,0,0,0.1); }
body.light .aria-inbox { background: rgba(242,242,247,0.97); border-top-color: rgba(5,150,105,0.2); }
body.light .inbox-input { background: rgba(0,0,0,0.04); border-color: rgba(0,0,0,0.1); color: var(--text); }
body.light .inbox-input:focus { border-color: rgba(5,150,105,0.4); }

/* ====== BRAIN BROWSER ====== */
.brain-layout { display: flex; gap: 0; height: calc(100vh - 56px - 80px); min-height: 480px; }
.brain-sidebar { width: 280px; flex-shrink: 0; overflow-y: auto; border-right: 1px solid var(--separator); padding: 8px 4px 60px 8px; background: var(--bg-card); }
.brain-reader { flex: 1; overflow-y: auto; padding: 18px 22px 80px 22px; min-width: 0; }
.brain-tree-folder { user-select: none; }
.brain-tree-row { display: flex; align-items: center; gap: 6px; padding: 4px 6px; border-radius: 6px; cursor: pointer; font-size: 13px; color: var(--text-2); }
.brain-tree-row:hover { background: rgba(255,255,255,0.05); color: var(--text); }
.brain-tree-row.active { background: rgba(30,240,161,0.12); color: var(--accent); }
.brain-tree-row .caret { width: 12px; display: inline-block; text-align: center; color: var(--text-4); font-size: 10px; transition: transform 0.15s; }
.brain-tree-row.collapsed .caret { transform: rotate(-90deg); }
.brain-tree-row .icon { font-size: 12px; opacity: 0.7; flex-shrink: 0; }
.brain-tree-row .name { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.brain-tree-children { padding-left: 14px; border-left: 1px dashed rgba(255,255,255,0.05); margin-left: 8px; }
.brain-tree-children.collapsed { display: none; }

.brain-reader-title { font-size: 22px; font-weight: 700; letter-spacing: -0.4px; margin-bottom: 4px; color: var(--text); }
.brain-reader-meta { font-size: 11px; color: var(--text-4); margin-bottom: 16px; font-family: 'JetBrains Mono', monospace; }
.brain-reader-frontmatter { background: rgba(255,255,255,0.03); border: 1px solid var(--separator); border-radius: 8px; padding: 10px 12px; margin-bottom: 16px; font-size: 11px; font-family: 'JetBrains Mono', monospace; color: var(--text-3); white-space: pre-wrap; }
.brain-reader-content { font-size: 15px; line-height: 1.65; color: var(--text); }
.brain-reader-content h1 { font-size: 22px; font-weight: 700; margin: 24px 0 12px; letter-spacing: -0.3px; }
.brain-reader-content h2 { font-size: 18px; font-weight: 600; margin: 20px 0 10px; color: var(--text); border-bottom: 1px solid var(--separator); padding-bottom: 4px; }
.brain-reader-content h3 { font-size: 15px; font-weight: 600; margin: 16px 0 8px; color: var(--text-2); }
.brain-reader-content h4 { font-size: 14px; font-weight: 600; margin: 12px 0 6px; color: var(--text-2); }
.brain-reader-content p { margin: 8px 0; }
.brain-reader-content ul, .brain-reader-content ol { margin: 8px 0 8px 22px; }
.brain-reader-content li { margin: 4px 0; }
.brain-reader-content code { background: rgba(255,255,255,0.06); padding: 1px 5px; border-radius: 4px; font-family: 'JetBrains Mono', monospace; font-size: 12.5px; color: var(--accent); }
.brain-reader-content pre { background: rgba(0,0,0,0.4); border: 1px solid var(--separator); border-radius: 8px; padding: 12px; margin: 12px 0; overflow-x: auto; }
.brain-reader-content pre code { background: none; padding: 0; color: var(--text); font-size: 12px; }
.brain-reader-content blockquote { border-left: 3px solid var(--accent); padding: 4px 0 4px 14px; margin: 12px 0; color: var(--text-2); font-style: italic; }
.brain-reader-content a { color: var(--cyan); text-decoration: none; border-bottom: 1px dotted rgba(100,210,255,0.4); }
.brain-reader-content a:hover { border-bottom-style: solid; }
.brain-reader-content .wikilink { color: var(--accent); cursor: pointer; border-bottom: 1px dotted rgba(30,240,161,0.4); padding: 1px 3px; border-radius: 3px; }
.brain-reader-content .wikilink:hover { background: rgba(30,240,161,0.1); border-bottom-style: solid; }
.brain-reader-content .wikilink.broken { color: var(--text-4); border-bottom-color: rgba(255,255,255,0.1); }
.brain-reader-content table { border-collapse: collapse; margin: 12px 0; width: 100%; font-size: 13px; }
.brain-reader-content th, .brain-reader-content td { border: 1px solid var(--separator); padding: 6px 10px; text-align: left; }
.brain-reader-content th { background: rgba(255,255,255,0.04); font-weight: 600; }
.brain-reader-content hr { border: none; border-top: 1px solid var(--separator); margin: 18px 0; }
.brain-reader-empty { color: var(--text-4); font-size: 13px; text-align: center; padding: 60px 20px; }

.brain-mobile-toggle { display: none; align-items: center; gap: 8px; position: sticky; top: 0; padding: 8px 12px; background: var(--bg-card-solid); border-bottom: 1px solid var(--separator); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); z-index: 5; }
.brain-mobile-toggle button { background: var(--bg-card); border: 1px solid var(--border); color: var(--text); padding: 6px 12px; border-radius: 6px; font-size: 13px; cursor: pointer; font-family: inherit; }
.brain-mobile-toggle button.active { background: rgba(30,240,161,0.15); border-color: rgba(30,240,161,0.35); color: var(--accent); }
#brain-graph-svg .node { cursor: pointer; transition: r 0.15s; }
#brain-graph-svg .node:hover { stroke: var(--accent); stroke-width: 2px; }
#brain-graph-svg .node.active { stroke: var(--accent); stroke-width: 3px; }
#brain-graph-svg .link { stroke: rgba(255,255,255,0.12); stroke-width: 1px; }
#brain-graph-svg .label { font-size: 10px; fill: var(--text-3); pointer-events: none; font-family: 'IBM Plex Sans', sans-serif; }

@media (max-width: 768px) {
    .brain-layout { flex-direction: column; height: calc(100vh - 56px - 80px - 56px); min-height: 480px; }
    .brain-sidebar { width: 100%; max-height: 50vh; border-right: none; border-bottom: 1px solid var(--separator); display: none; flex-shrink: 0; }
    .brain-sidebar.open { display: block; }
    .brain-reader { padding: 14px 16px 80px 16px; flex: 1; min-height: 0; }
    #brain-graph-view { flex: 1; min-height: 60vh; }
    #brain-graph-svg { min-height: 60vh; }
    .brain-mobile-toggle { display: flex !important; }
}

body.light .brain-sidebar { background: rgba(0,0,0,0.025); }
body.light .brain-sidebar-header { background: rgba(242,242,247,0.97) !important; }
body.light .brain-tree-row { color: var(--text-2); }
body.light .brain-tree-row:hover { background: rgba(0,0,0,0.04); color: var(--text); }
body.light .brain-tree-row.active { background: rgba(5,150,105,0.1); color: var(--accent); }
body.light .brain-reader-content code { background: rgba(0,0,0,0.06); color: #047857; }
body.light .brain-reader-content pre { background: rgba(0,0,0,0.04); }
body.light .brain-reader-content pre code { background: none; color: var(--text); }
body.light #brain-graph-svg .link { stroke: rgba(0,0,0,0.1); }
body.light #brain-graph-svg .label { fill: var(--text-2); }

/* ============================================================
   STATUSLINE — slim, terminal-style header (Home tab top).
   Mirrors Claude Code's /statusline: folder · branch · model · bar.
   ============================================================ */
.statusline {
    margin: 12px 16px 10px;
    padding: 12px 14px 10px;
    background: rgba(255,255,255,0.025);
    border: 1px solid var(--border);
    border-radius: 10px;
    position: relative;
    overflow: hidden;
    font-family: 'JetBrains Mono', monospace;
    font-size: 12.5px;
    line-height: 1.45;
    color: var(--text-2);
}
.statusline::before {
    content: '';
    position: absolute; left: 0; right: 0; top: 0;
    height: 1px;
    background: linear-gradient(90deg, transparent 0%, rgba(30,240,161,0.55) 15%, rgba(30,240,161,0.1) 60%, transparent 100%);
}
.sl-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.sl-row + .sl-row { margin-top: 6px; }
.sl-dot {
    width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
    background: var(--green);
    box-shadow: 0 0 0 4px rgba(30,240,161,0.12);
    animation: pulse 2.4s ease-in-out infinite;
}
.sl-dot.idle    { background: var(--amber); box-shadow: 0 0 0 4px rgba(255,214,10,0.10); animation: none; }
.sl-dot.offline { background: var(--red);   box-shadow: 0 0 0 4px rgba(255,69,58,0.12);  animation: none; }
.sl-folder { color: #5fd5ff; font-weight: 600; }
.sl-branch { color: #ffd56b; font-weight: 600; }
.sl-model  { color: #d896ff; font-weight: 600; white-space: nowrap; }
.sl-sep    { color: var(--text-4); }
.sl-bar    { color: var(--accent); letter-spacing: 0; font-size: 12.5px; white-space: nowrap; }
.sl-bar.warn { color: var(--amber); }
.sl-bar.crit { color: var(--red); }
.sl-pct    { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.sl-meta   { color: var(--text-3); font-size: 11.5px; }
.sl-meta .sl-key { color: var(--text-4); margin-right: 4px; }
.sl-meta .sl-val { color: var(--text-2); }
.sl-meta .branch-state { color: var(--text-4); margin-left: 4px; }

.sl-today {
    display: flex; gap: 14px;
    padding-top: 8px; margin-top: 8px;
    border-top: 1px dashed var(--separator);
    font-size: 11.5px; color: var(--text-3);
}
.sl-today .sl-key { color: var(--text-4); margin-right: 4px; text-transform: uppercase; letter-spacing: 0.4px; }
.sl-today b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }

/* Light-mode overrides */
body.light .statusline { background: rgba(0,0,0,0.025); }
body.light .statusline::before { background: linear-gradient(90deg, transparent 0%, rgba(5,150,105,0.5) 15%, rgba(5,150,105,0.1) 60%, transparent 100%); }
body.light .sl-folder { color: #0891b2; }
body.light .sl-branch { color: #b45309; }
body.light .sl-model  { color: #6d28d9; }

/* News (Aria's own) — list style, not card-grid */
.news-list { padding: 0 16px; }
.news-item {
    display: flex; flex-direction: column; gap: 4px;
    padding: 12px 0;
    border-bottom: 1px solid var(--separator);
}
.news-item:last-child { border-bottom: none; }
.news-item-meta {
    display: flex; align-items: center; gap: 8px;
    font-size: 10px; color: var(--text-4);
    text-transform: uppercase; letter-spacing: 0.6px; font-weight: 600;
}
.news-item-meta .news-source { color: var(--accent); }
.news-item-title { font-size: 14px; font-weight: 600; color: var(--text); line-height: 1.35; }
.news-item-summary { font-size: 12.5px; color: var(--text-3); line-height: 1.5; }

/* Footer actions — Restart/Git collapsed */
.footer-actions {
    display: flex; gap: 8px;
    margin: 6px 16px 0;
}
.footer-actions .action-btn {
    flex: 1;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 9px 12px;
    color: var(--text-3);
    font-family: inherit; font-size: 12px; font-weight: 500;
    cursor: pointer;
    transition: border-color 0.2s, color 0.2s;
}
.footer-actions .action-btn:hover { color: var(--text); border-color: rgba(255,255,255,0.18); }
.footer-actions .action-btn.danger:hover { color: var(--red); border-color: rgba(255,69,58,0.3); }

/* Session-log details (collapsible) */
.session-log-block {
    margin: 0 16px 14px;
    border: 1px solid var(--border);
    border-radius: 10px;
    overflow: hidden;
}
.session-log-block summary {
    cursor: pointer;
    padding: 10px 14px;
    font-size: 11px; color: var(--text-3);
    text-transform: uppercase; letter-spacing: 0.7px; font-weight: 600;
    user-select: none;
    list-style: none;
    display: flex; align-items: center; justify-content: space-between;
}
.session-log-block summary::-webkit-details-marker { display: none; }
.session-log-block summary::after {
    content: '+'; color: var(--text-4); font-size: 14px; font-weight: 400;
}
.session-log-block[open] summary::after { content: '−'; }
.session-log-block .log-box { border: none; border-radius: 0; max-height: 320px; }

@media (min-width: 640px) {
    .hero-card { margin: 18px 24px 22px; }
    .news-list { padding: 0 24px; }
    .footer-actions { margin: 6px 24px 0; }
    .session-log-block { margin: 0 24px 14px; }
}
</style>
</head>
<body>
<div class="app">
    <div class="header">
        <div class="header-left">
            <div class="header-icon">
                <!-- Inline SVG mark (no external host) -->
                <svg id="aria-logo" viewBox="0 0 32 32" width="28" height="28" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="color:var(--accent)" aria-label="Aria">
                    <circle cx="16" cy="16" r="13" opacity="0.25"/>
                    <path d="M16 6 L24 23 L8 23 Z"/>
                    <circle cx="16" cy="17" r="2" fill="currentColor" stroke="none"/>
                </svg>
            </div>
            <div>
                <div class="header-title">Aria Control</div>
                <div class="header-status" id="header-status"><span class="status-dot status-dot--check"></span> Prüfe...</div>
            </div>
        </div>
        <div style="display:flex;align-items:center;gap:6px;">
            <span style="font-size:11px;color:var(--text-4);font-variant-numeric:tabular-nums" id="uptime"></span>
            <button id="planer-btn" onclick="openPlaner()" title="Planer öffnen" style="background:none;border:none;cursor:pointer;padding:6px;border-radius:8px;color:var(--text-4);display:flex;align-items:center;justify-content:center;transition:color 0.2s">
                <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="3" y1="10" x2="21" y2="10"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="16" y1="2" x2="16" y2="6"/></svg>
            </button>
            <button id="theme-btn" onclick="toggleTheme()" title="Hell/Dunkel">🌙</button>
        </div>
    </div>

    <!-- ====== STATUS / ÜBERSICHT ====== -->
    <div id="view-home">
        <!-- Statusline (folder · branch · model · context bar) -->
        <section class="statusline" id="statusline">
            <div class="sl-row">
                <span class="sl-dot" id="sl-dot"></span>
                <span class="sl-folder" id="sl-folder">—</span>
                <span class="sl-sep">·</span>
                <span class="sl-branch" id="sl-branch">—</span>
                <span class="sl-sep">·</span>
                <span class="sl-model" id="sl-model">—</span>
            </div>
            <div class="sl-row">
                <span class="sl-bar" id="sl-bar">▱▱▱▱▱▱▱▱▱▱</span>
                <span class="sl-pct" id="sl-pct">—%</span>
                <span class="sl-sep">·</span>
                <span class="sl-meta" id="sl-tokens">— / —</span>
                <span class="sl-sep">·</span>
                <span class="sl-meta" id="sl-updated">—</span>
            </div>
            <div class="sl-row sl-meta">
                <span class="sl-key">cwd</span>
                <span class="sl-val" id="sl-cwd">—</span>
                <span class="branch-state" id="sl-branch-state"></span>
            </div>
            <div class="sl-row sl-meta">
                <span class="sl-key">last</span>
                <span class="sl-val" id="sl-last">—</span>
            </div>
            <div class="sl-today">
                <span><span class="sl-key">heute</span> <b id="today-commits">—</b> commits</span>
                <span><b id="today-tasks">—</b> tasks</span>
                <span><b id="today-telegrams">—</b> telegrams</span>
                <span style="margin-left:auto"><span class="sl-key">uptime</span> <b id="sl-uptime">—</b></span>
            </div>
        </section>

        <!-- Aktive Aria-Tasks -->
        <div id="aria-tasks" class="section" style="margin: 0 16px 12px;"></div>

        <!-- Session-Output (always open) -->
        <div class="section-header">Session-Output</div>
        <div class="section">
            <div class="card-group">
                <div style="padding:12px"><div class="log-box" id="session-log" style="max-height:340px;">Lade…</div></div>
            </div>
        </div>

        <!-- Aria-Erkenntnisse — eigene Funde, in einfachen Worten -->
        <div class="section-header">Aria-Erkenntnisse</div>
        <div class="news-list" id="home-erkenntnisse">
            <div style="padding:14px 0;color:var(--text-4);font-size:12px">Lade…</div>
        </div>

        <!-- KI-News — externe Headlines + Tipps -->
        <div class="section-header">KI-News &amp; Tipps</div>
        <div class="news-list" id="home-news-items">
            <div style="padding:14px 0;color:var(--text-4);font-size:12px">Lade…</div>
        </div>

        <!-- Sekundäre Aktionen (Restart, Git Pull) — bewusst klein -->
        <div class="footer-actions">
            <button class="action-btn danger" id="restart-btn" onclick="restart()">Restart</button>
            <button class="action-btn" onclick="gitPull()">Git Pull</button>
            <button class="action-btn" onclick="loadAll()">Refresh</button>
        </div>

        <div class="action-log" id="action-log"></div>
    </div>

    <!-- ====== INFRA (System + Services + Logs) ====== -->
    <div id="view-infra" style="display:none;">
        <div class="section-header" style="margin-top:12px">Hardware</div>
        <div class="sys-grid">
            <div class="sys-tile"><div class="sys-tile-label">CPU</div><div class="sys-tile-value" id="cpu">-</div><div class="stat-bar"><div class="stat-bar-fill" id="cpu-bar" style="width:0%"></div></div></div>
            <div class="sys-tile"><div class="sys-tile-label">RAM</div><div class="sys-tile-value" id="ram">-</div><div class="stat-bar"><div class="stat-bar-fill" id="ram-bar" style="width:0%"></div></div></div>
            <div class="sys-tile"><div class="sys-tile-label">Disk</div><div class="sys-tile-value" id="disk">-</div><div class="stat-bar"><div class="stat-bar-fill" id="disk-bar" style="width:0%"></div></div></div>
            <div class="sys-tile"><div class="sys-tile-label">Load</div><div class="sys-tile-value" id="load">-</div></div>
        </div>

        <div class="section-header">Max Plan Nutzung</div>
        <div class="sys-grid">
            <div class="sys-tile"><div class="sys-tile-label">Session</div><div class="sys-tile-value" id="usage-current">-</div></div>
            <div class="sys-tile"><div class="sys-tile-label">Uptime</div><div class="sys-tile-value" id="usage-duration">-</div></div>
            <div class="sys-tile">
                <div class="sys-tile-label">Heute</div>
                <div class="sys-tile-value" id="usage-today">-</div>
                <div style="font-size:10px;color:var(--text-4);margin-top:2px">Limit ~900M/Tag</div>
                <div class="stat-bar"><div class="stat-bar-fill" id="usage-today-bar" style="width:0%"></div></div>
            </div>
            <div class="sys-tile"><div class="sys-tile-label">Sessions heute</div><div class="sys-tile-value" id="usage-today-sessions">-</div></div>
            <div class="sys-tile">
                <div class="sys-tile-label">Diese Woche</div>
                <div class="sys-tile-value" id="usage-week">-</div>
                <div style="font-size:10px;color:var(--text-4);margin-top:2px">Limit ~6300M/Wo</div>
                <div class="stat-bar"><div class="stat-bar-fill" id="usage-week-bar" style="width:0%"></div></div>
            </div>
            <div class="sys-tile"><div class="sys-tile-label">Claude CPU</div><div class="sys-tile-value" id="usage-cpu">-</div></div>
            <div class="sys-tile"><div class="sys-tile-label">Claude RAM</div><div class="sys-tile-value" id="usage-ram">-</div></div>
        </div>

        <div class="section-header">Dienste</div>
        <div class="section">
            <div class="card-group" id="services"><div style="padding:16px;color:var(--text-4)">Lade...</div></div>
        </div>

        <div class="section-header">n8n Workflows</div>
        <div class="section"><div class="card-group" id="n8n-active"><div style="padding:16px;color:var(--text-4)">Lade...</div></div></div>
        <div class="section-header">n8n Inaktiv</div>
        <div class="section"><div class="card-group" id="n8n-inactive"></div></div>

        <div class="section-header">Watchdog Log</div>
        <div class="section">
            <div class="card-group"><div style="padding:12px"><div class="log-box" id="watchdog-log" style="max-height:500px;">Lade...</div></div></div>
        </div>

        <div class="section-header">Plugins <span id="capabilities-count-plugins" style="color:var(--text-4);font-weight:400;margin-left:6px"></span></div>
        <div class="section">
            <div class="card-group" id="capabilities-plugins"><div style="padding:16px;color:var(--text-4)">Lade...</div></div>
        </div>
        <div class="section-header">Skills <span id="capabilities-count-skills" style="color:var(--text-4);font-weight:400;margin-left:6px"></span></div>
        <div class="section">
            <div class="card-group" id="capabilities-skills"><div style="padding:16px;color:var(--text-4)">Lade...</div></div>
        </div>
        <div class="section-header">MCP Server <span id="capabilities-count-mcp" style="color:var(--text-4);font-weight:400;margin-left:6px"></span></div>
        <div class="section">
            <div class="card-group" id="capabilities-mcp"><div style="padding:16px;color:var(--text-4)">Lade...</div></div>
        </div>
        <div class="section-header">Inaktive Skill-Ordner</div>
        <div class="section">
            <div class="card-group" id="capabilities-inactive"><div style="padding:16px;color:var(--text-4)">Lade...</div></div>
        </div>

        <div class="section-header">Tool Failures (letzte 24h) <span id="tf-total" style="color:var(--text-4);font-weight:400;margin-left:6px"></span></div>
        <div class="section">
            <div class="card-group" id="tool-failures"><div style="padding:16px;color:var(--text-4)">Lade...</div></div>
        </div>

        <div class="section-header">Cost Overview</div>
        <div class="section">
            <div class="card-group" id="cost-overview"><div style="padding:16px;color:var(--text-4)">Lade...</div></div>
        </div>
    </div>

    <!-- ====== WORK (Kalender + Tasks) ====== -->
    <div id="view-work" style="display:none;">
        <div class="section-header" style="margin-top:12px" id="cal-header">Termine heute</div>
        <div class="section"><div class="card-group" id="calendar-events"><div style="padding:16px;color:var(--text-4)">Lade...</div></div></div>

        <div id="linear-overdue-section" style="display:none;">
            <div class="section-header" style="color:var(--red)">&#9888; Überfällig</div>
            <div class="section"><div class="card-group" id="linear-overdue"></div></div>
        </div>
        <div class="section-header">&#128308; In Arbeit</div>
        <div class="section"><div class="card-group" id="linear-progress"><div style="padding:16px;color:var(--text-4)">Lade...</div></div></div>
        <div class="section-header">&#128993; Offen</div>
        <div class="section"><div class="card-group" id="linear-todo"></div></div>
        <div id="linear-done-section" style="display:none;">
            <div class="section-header" style="color:var(--green)">&#10003; Zuletzt erledigt</div>
            <div class="section"><div class="card-group" id="linear-done"></div></div>
        </div>
    </div>

    <!-- ====== SOCIAL ====== -->
    <div id="view-social" style="display:none;">
        <div class="section-header" style="margin-top:12px">&#9989; Zuletzt gepostet</div>
        <div class="section"><div class="card-group" id="social-posted"><div style="padding:16px;color:var(--text-4)">Lade...</div></div></div>
        <div class="section-header">&#9201; Nächste Posts</div>
        <div class="section"><div class="card-group" id="social-upcoming"></div></div>
    </div>

    <!-- ====== BRAIN BROWSER ====== -->
    <div id="view-brain" style="display:none;">
        <div class="brain-mobile-toggle">
            <button onclick="toggleBrainSidebar()">&#9776; Dateien</button>
            <span style="font-size:11px;color:var(--text-4);margin-left:10px;flex:1" id="brain-current-path">aria-brain</span>
            <button onclick="toggleBrainGraph()" id="brain-graph-toggle" style="margin-left:auto">Graph</button>
        </div>
        <div class="brain-layout">
            <aside class="brain-sidebar" id="brain-sidebar">
                <div class="brain-sidebar-header" style="padding:6px 6px 8px 6px;position:sticky;top:0;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);z-index:2;background:var(--bg-card-solid);border-bottom:1px solid var(--separator)">
                    <input id="brain-search" type="text" placeholder="Suchen oder filtern..."
                        style="width:100%;padding:7px 10px;background:rgba(255,255,255,0.05);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px;outline:none"
                        oninput="onBrainSearchInput()">
                    <div id="brain-search-mode" style="font-size:10px;color:var(--text-4);margin-top:4px;display:flex;gap:8px;align-items:center">
                        <span id="brain-stats">- Files</span>
                        <span style="margin-left:auto;display:flex;gap:6px">
                            <button onclick="brainCollapseAll()" title="Alle zuklappen" style="background:rgba(255,255,255,0.04);border:1px solid var(--border);color:var(--text-3);padding:2px 7px;border-radius:4px;font-size:10px;cursor:pointer;font-family:inherit">▸ Close all</button>
                            <button onclick="brainExpandAll()" title="Alle aufklappen" style="background:rgba(255,255,255,0.04);border:1px solid var(--border);color:var(--text-3);padding:2px 7px;border-radius:4px;font-size:10px;cursor:pointer;font-family:inherit">▾ Open all</button>
                        </span>
                    </div>
                </div>
                <div id="brain-tree-root" style="padding:4px 4px;color:var(--text-4);font-size:12px">Lade...</div>
                <div id="brain-search-results" style="padding:4px 4px;display:none"></div>
            </aside>
            <main class="brain-reader" id="brain-reader">
                <div class="brain-reader-empty">Wähle links eine Datei oder suche.</div>
            </main>
            <main class="brain-reader" id="brain-graph-view" style="display:none;padding:0;position:relative">
                <div id="brain-graph-status" style="position:absolute;top:10px;left:14px;font-size:11px;color:var(--text-4);font-family:'JetBrains Mono',monospace;z-index:5">Lade Graph...</div>
                <svg id="brain-graph-svg" style="width:100%;height:100%;display:block;cursor:grab;"></svg>
            </main>
        </div>
    </div>

    <!-- Aria Inbox -->
    <div class="aria-inbox">
        <div class="effort-row" id="effortRow">
            <span class="effort-label">Effort</span>
            <button class="effort-chip" data-level="low" onclick="setEffort('low', this)">Low</button>
            <button class="effort-chip" data-level="medium" onclick="setEffort('medium', this)">Med</button>
            <button class="effort-chip" data-level="high" onclick="setEffort('high', this)">High</button>
            <button class="effort-chip" data-level="xhigh" onclick="setEffort('xhigh', this)">Extra</button>
            <button class="effort-chip" data-level="max" onclick="setEffort('max', this)">Max</button>
        </div>
        <div class="inbox-row">
            <textarea class="inbox-input" id="inboxInput" placeholder="Schreib Aria…" rows="1"
                oninput="resizeInbox(this)" onkeydown="handleInboxKey(event)"></textarea>
            <button class="inbox-send" id="inboxSend" onclick="sendToAria()">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
            </button>
        </div>
        <div class="inbox-feedback" id="inboxFeedback"></div>
    </div>

    <!-- Tab Bar — 4 tabs (Social hidden bis aktiv genutzt; Planer als Header-Icon) -->
    <nav class="tab-bar">
        <button class="tab-item active" onclick="switchTab('home')" id="tab-home">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 8v4l3 3"/></svg>
            <span>Status</span>
        </button>
        <button class="tab-item" onclick="switchTab('work')" id="tab-work">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11"/></svg>
            <span>Arbeit</span>
        </button>
        <button class="tab-item" onclick="switchTab('brain')" id="tab-brain">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2Z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2Z"/></svg>
            <span>Brain</span>
        </button>
        <button class="tab-item" onclick="switchTab('infra')" id="tab-infra">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/><line x1="6" y1="6" x2="6.01" y2="6"/><line x1="6" y1="18" x2="6.01" y2="18"/></svg>
            <span>Infra</span>
        </button>
        <!-- ADDITIVE: Team tab (KAR-679) — navigates to separate TEAM_PAGE -->
        <button class="tab-item" onclick="window.location='/team?t='+TOKEN" id="tab-team">
            <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="7" r="3"/><path d="M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/><path d="M21 21v-2a4 4 0 0 0-3-3.87"/></svg>
            <span>Team</span>
        </button>
        <!-- Social-Tab versteckt (Memory: nicht aktiv genutzt, später aktivieren) -->
        <button class="tab-item" onclick="switchTab('social')" id="tab-social" style="display:none">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/></svg>
            <span>Social</span>
        </button>
        <button class="tab-item" onclick="openPlaner()" id="tab-planer" style="display:none">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="3" y1="10" x2="21" y2="10"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="15" x2="8" y2="18"/><line x1="12" y1="15" x2="12" y2="18"/></svg>
            <span>Planer</span>
        </button>
    </nav>
</div>

<script>
const TOKEN = new URLSearchParams(window.location.search).get('t') || '';
const TABS = ['home','infra','work','social','brain'];

function switchTab(tab) {
    TABS.forEach(id => {
        const el = document.getElementById('view-' + id);
        if (el) el.style.display = id === tab ? 'block' : 'none';
    });
    document.querySelectorAll('.tab-item').forEach(b => b.classList.remove('active'));
    const btn = document.getElementById('tab-' + tab);
    if (btn) btn.classList.add('active');
    window.scrollTo(0, 0);
    if (tab === 'brain' && !window._brainTreeLoaded) {
        window._brainTreeLoaded = true;
        loadBrainTree();
    }
}

function alog(msg) {
    const el = document.getElementById('action-log');
    el.style.display = 'block';
    el.textContent += new Date().toLocaleTimeString('de') + ' ' + msg + '\n';
    el.scrollTop = el.scrollHeight;
}

function barColor(pct) {
    if (pct > 85) return 'bar-red';
    if (pct > 60) return 'bar-yellow';
    return 'bar-green';
}

async function api(path) {
    const r = await fetch(path + (path.includes('?') ? '&' : '?') + 't=' + TOKEN);
    return r.json();
}

async function loadStatus() {
    try {
        const d = await api('/status');
        const hs = document.getElementById('header-status');
        const up = document.getElementById('uptime');
        if (d.status === 'online') {
            hs.innerHTML = '<span class="status-dot"></span> Aria Online';
        } else {
            hs.innerHTML = '<span class="status-dot status-dot--off"></span> Aria Offline';
        }
        up.textContent = d.uptime || '';
    } catch(e) {
        document.getElementById('header-status').innerHTML = '<span class="status-dot status-dot--off"></span> Verbindung fehlgeschlagen';
    }
}

async function loadSystem() {
    try {
        const d = await api('/system');
        document.getElementById('cpu').textContent = d.cpu_pct + '%';
        document.getElementById('cpu-bar').style.width = d.cpu_pct + '%';
        document.getElementById('cpu-bar').className = 'stat-bar-fill ' + barColor(d.cpu_pct);

        document.getElementById('ram').textContent = d.ram_used + ' / ' + d.ram_total;
        document.getElementById('ram-bar').style.width = d.ram_pct + '%';
        document.getElementById('ram-bar').className = 'stat-bar-fill ' + barColor(d.ram_pct);

        document.getElementById('disk').textContent = d.disk_used + ' / ' + d.disk_total;
        document.getElementById('disk-bar').style.width = d.disk_pct + '%';
        document.getElementById('disk-bar').className = 'stat-bar-fill ' + barColor(d.disk_pct);

        document.getElementById('load').textContent = d.load;

        // Summary cards retired with the home-tab refactor (10.05.2026); skip if absent.
        const sysVal = document.getElementById('sum-sys-val');
        const sysSub = document.getElementById('sum-sys-sub');
        if (sysVal && sysSub) {
            const sysColor = d.cpu_pct > 85 ? 'var(--red)' : d.cpu_pct > 60 ? 'var(--amber)' : 'var(--green)';
            sysVal.textContent = d.cpu_pct + '%';
            sysVal.style.color = sysColor;
            sysSub.textContent = 'CPU · RAM ' + d.ram_used;
        }
    } catch(e) {}
}

async function loadServices() {
    try {
        const d = await api('/services');
        const el = document.getElementById('services');
        el.innerHTML = d.map(s =>
            '<div class="card-row">' +
            '<div class="card-row-left"><span class="dot ' + (s.active ? 'dot-g' : 'dot-r') + '"></span><span class="card-row-label">' + s.name + '</span></div>' +
            '<span class="badge ' + (s.active ? 'badge-g' : 'badge-r') + '">' +
            (s.info || (s.active ? 'aktiv' : 'inaktiv')) + '</span></div>'
        ).join('');
    } catch(e) {}
}

async function loadLogs() {
    try {
        const d = await api('/logs');
        document.getElementById('watchdog-log').textContent = d.watchdog || 'Keine Eintraege';
    } catch(e) {}
}

async function loadUsage() {
    try {
        const d = await api('/usage');
        document.getElementById('usage-duration').textContent = d.duration || '-';
        document.getElementById('usage-procs').textContent = d.processes || '-';
        document.getElementById('usage-cpu').textContent = d.claude_cpu || '-';
        document.getElementById('usage-ram').textContent = d.claude_ram || '-';
        document.getElementById('usage-today').textContent = d.today_tokens || '-';
        document.getElementById('usage-today-sessions').textContent = d.today_sessions || '-';
        document.getElementById('usage-week').textContent = d.week_tokens || '-';
        // Usage bars (approximate limits: 900M/day, 6300M/week)
        if (d.today_tokens && d.today_tokens !== '-') {
            const todayM = parseFloat(d.today_tokens);
            const todayPct = Math.min(100, Math.round(todayM / 900 * 100));
            const bar = document.getElementById('usage-today-bar');
            if (bar) { bar.style.width = todayPct + '%'; bar.className = 'stat-bar-fill ' + barColor(todayPct); }
        }
        if (d.week_tokens && d.week_tokens !== '-') {
            const weekM = parseFloat(d.week_tokens);
            const weekPct = Math.min(100, Math.round(weekM / 6300 * 100));
            const bar = document.getElementById('usage-week-bar');
            if (bar) { bar.style.width = weekPct + '%'; bar.className = 'stat-bar-fill ' + barColor(weekPct); }
        }
        document.getElementById('usage-week-sessions').textContent = d.week_sessions || '-';
        document.getElementById('usage-current').textContent = d.current_tokens || '-';
        document.getElementById('usage-total').textContent = d.total_tokens || '-';
    } catch(e) {}
}

function renderSession(data, tasksId, logId, pulseId, cardId) {
    if (cardId) {
        const card = document.getElementById(cardId);
        if (!data || !data.session || data.session === 'Keine aktive Session') {
            card.style.display = 'none'; return;
        }
        card.style.display = 'block';
    }
    const tasksEl = document.getElementById(tasksId);
    if (data.tasks && data.tasks.length > 0) {
        tasksEl.innerHTML = data.tasks.map(t => {
            let cls = 'task-pending', icon = '&#9723;';
            if (t.status === 'done') { cls = 'task-done'; icon = '&#10004;'; }
            else if (t.status === 'active') { cls = 'task-active'; icon = '&#9654;'; }
            return '<div class="task-item ' + cls + '"><span class="task-icon">' + icon + '</span>' + t.text + '</div>';
        }).join('');
    } else { tasksEl.innerHTML = ''; }
    document.getElementById(logId).textContent = data.session || 'Keine aktive Session';
    const pulse = document.getElementById(pulseId);
    pulse.style.color = data.thinking ? '#93c5fd' : '#4ade80';

    // Note: prior live-banner (current-task / current-task-icon) was retired in the
    // home-tab refactor. Hero-Session-Card now carries the active state via loadHero().
}

async function loadActivity() {
    try {
        const d = await api('/activity');
        renderSession(d, 'aria-tasks', 'session-log', 'activity-pulse', null);
    } catch(e) {}
}

function fmtTokens(n) {
    if (!n) return '0';
    if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
    if (n >= 1000) return (n / 1000).toFixed(0) + 'k';
    return String(n);
}

function setHeaderStatus(state, label) {
    const hs = document.getElementById('header-status');
    if (!hs) return;
    while (hs.firstChild) hs.removeChild(hs.firstChild);
    const dot = document.createElement('span');
    dot.className = 'status-dot' + (state === 'online' ? '' : (state === 'idle' ? ' status-dot--check' : ' status-dot--off'));
    hs.appendChild(dot);
    hs.appendChild(document.createTextNode(' ' + (label || 'Aria')));
}

function buildBar(pct) {
    // 10-cell unicode bar matching the Claude Code /statusline.
    const filled = Math.max(0, Math.min(10, Math.round((pct || 0) / 10)));
    return '▰'.repeat(filled) + '▱'.repeat(10 - filled);
}

function basenameOf(p) {
    if (!p) return '—';
    const parts = p.replace(/\/+$/, '').split('/');
    return parts[parts.length - 1] || p;
}

async function loadHero() {
    let h;
    try { h = await api('/session-hero'); } catch(e) { return; }

    // State dot
    const dot = document.getElementById('sl-dot');
    if (dot) dot.className = 'sl-dot ' + (h.state || 'offline');

    setHeaderStatus(h.state, h.state_label);

    // Folder · branch · model
    const folderEl = document.getElementById('sl-folder');
    if (folderEl) folderEl.textContent = basenameOf(h.cwd);

    const branchEl = document.getElementById('sl-branch');
    if (branchEl) branchEl.textContent = h.branch || '—';

    const modelEl = document.getElementById('sl-model');
    if (modelEl) modelEl.textContent = h.model_short || h.model_full || '—';

    // Context bar
    const pct = (h.context && h.context.percent) || 0;
    const tokens = (h.context && h.context.tokens) || 0;
    const max = (h.context && h.context.max) || 0;
    const barEl = document.getElementById('sl-bar');
    if (barEl) {
        barEl.textContent = buildBar(pct);
        barEl.className = 'sl-bar' + (pct >= 90 ? ' crit' : (pct >= 70 ? ' warn' : ''));
    }
    const pctEl = document.getElementById('sl-pct');
    if (pctEl) pctEl.textContent = pct.toFixed(0) + '%';
    const tokEl = document.getElementById('sl-tokens');
    if (tokEl) tokEl.textContent = fmtTokens(tokens) + ' / ' + fmtTokens(max);
    const updEl = document.getElementById('sl-updated');
    if (updEl) updEl.textContent = (h.updated_ago && h.updated_ago !== '-') ? h.updated_ago : '—';

    // Meta lines
    const cwdEl = document.getElementById('sl-cwd');
    if (cwdEl) cwdEl.textContent = h.cwd || '—';
    const branchStateEl = document.getElementById('sl-branch-state');
    if (branchStateEl) branchStateEl.textContent = h.branch_state ? '(' + h.branch_state + ')' : '';

    const lastEl = document.getElementById('sl-last');
    if (lastEl) {
        if (h.last_action && h.last_action.text) {
            lastEl.textContent = h.last_action.ago + ' · ' + h.last_action.text;
        } else {
            lastEl.textContent = '—';
        }
    }

    // Today + uptime
    const tc = document.getElementById('today-commits');
    const tt = document.getElementById('today-tasks');
    const tg = document.getElementById('today-telegrams');
    const tu = document.getElementById('sl-uptime');
    if (tc) tc.textContent = (h.today && h.today.commits != null) ? h.today.commits : '0';
    if (tt) tt.textContent = (h.today && h.today.tasks_done) ? h.today.tasks_done : '—';
    if (tg) tg.textContent = (h.today && h.today.telegrams != null) ? h.today.telegrams : '—';
    if (tu) tu.textContent = h.uptime || '—';
}

async function loadLinear() {
    try {
        const d = await api('/linear');
        const renderItems = (items, showActions) => items.length > 0 ? items.map(i => {
            const title = i.title.replace(/^ARI-\d+:\s*/, '');
            const actions = showActions && i.internal_id
                ? '<div class="issue-actions">' +
                  '<button class="issue-btn start" data-lid="' + i.internal_id + '" data-did="' + i.id + '" onclick="handleLinearStart(this)">&#9654; Start</button>' +
                  '<button class="issue-btn done" data-lid="' + i.internal_id + '" data-did="' + i.id + '" onclick="handleLinearDone(this)">&#10003;</button>' +
                  '</div>'
                : '';
            return '<div class="issue-item">' +
            '<div class="issue-row">' +
            '<div class="issue-content">' +
            '<div class="issue-id">' + i.id + '</div>' +
            '<div class="issue-title">' + title + '</div>' +
            (i.extra ? '<div class="issue-sub">&#9888; ' + i.extra + '</div>' : '') +
            '</div>' + actions +
            '</div></div>';
        }).join('') : '<div style="padding:10px 14px;color:var(--text-4);font-size:12px;">Keine Einträge</div>';

        document.getElementById('linear-progress').innerHTML = renderItems(d.in_progress || [], true);
        document.getElementById('linear-todo').innerHTML = renderItems(d.todo || [], true);

        const overdueSection = document.getElementById('linear-overdue-section');
        if (d.overdue && d.overdue.length > 0) {
            overdueSection.style.display = 'block';
            document.getElementById('linear-overdue').innerHTML = renderItems(d.overdue);
        } else {
            overdueSection.style.display = 'none';
        }

        const doneSection = document.getElementById('linear-done-section');
        if (d.done && d.done.length > 0) {
            doneSection.style.display = 'block';
            document.getElementById('linear-done').innerHTML = renderItems(d.done);
        } else {
            doneSection.style.display = 'none';
        }

        // Summary cards retired with the home-tab refactor (10.05.2026); skip if absent.
        const tCount = document.getElementById('sum-tasks-count');
        const tSub = document.getElementById('sum-tasks-sub');
        if (tCount && tSub) {
            const prog = (d.in_progress || []).length;
            const ovd = (d.overdue || []).length;
            tCount.textContent = prog + (ovd > 0 ? ' / ' + ovd + ' !' : '');
            tCount.style.color = ovd > 0 ? 'var(--red)' : 'var(--text)';
            tSub.textContent = prog + ' aktiv' + (ovd > 0 ? ' · ' + ovd + ' überfällig' : '');
        }
    } catch(e) {
        document.getElementById('linear-progress').innerHTML = '<div style="padding:16px;color:var(--red)">Ladefehler</div>';
    }
}

function handleLinearDone(btn) {
    completeLinear(btn.dataset.lid, btn.dataset.did, btn);
}

async function handleLinearStart(btn) {
    btn.disabled = true;
    btn.textContent = '...';
    try {
        const r = await fetch('/api/linear/start', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({token: TOKEN, internal_id: btn.dataset.lid, display_id: btn.dataset.did})
        });
        const d = await r.json();
        if (d.ok) {
            btn.textContent = '▶ Gestartet';
            btn.style.borderColor = 'var(--cyan)';
            setTimeout(() => loadLinear(), 800);
        } else {
            btn.textContent = '!';
            btn.style.color = 'var(--red)';
            setTimeout(() => { btn.disabled = false; btn.textContent = '&#9654; Start'; btn.style.color = ''; }, 2000);
        }
    } catch(e) { btn.disabled = false; btn.textContent = '&#9654; Start'; }
}

async function completeLinear(internalId, displayId, btn) {
    btn.disabled = true;
    btn.textContent = '...';
    try {
        const r = await fetch('/api/linear/complete', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({token: TOKEN, internal_id: internalId, display_id: displayId})
        });
        const d = await r.json();
        if (d.ok) {
            btn.textContent = '✓';
            btn.classList.add('done');
            const item = btn.closest('.issue-item');
            item.style.opacity = '0.4';
            item.style.transition = 'opacity 0.4s';
            setTimeout(() => { item.remove(); loadLinear(); }, 800);
        } else {
            btn.textContent = '!';
            btn.style.borderColor = 'var(--red)';
            btn.style.color = 'var(--red)';
            setTimeout(() => { btn.disabled = false; btn.textContent = '✓'; btn.style.borderColor = ''; btn.style.color = ''; }, 2000);
        }
    } catch(e) {
        btn.disabled = false; btn.textContent = '✓';
    }
}

async function loadCalendar() {
    try {
        const d = await api('/calendar');
        if (d.date) document.getElementById('cal-header').textContent = 'Termine ' + d.date;
        const el = document.getElementById('calendar-events');
        if (d.error) {
            el.innerHTML = '<div style="padding:16px;color:var(--red)">Fehler: ' + d.error.substring(0, 80) + '</div>';
            return;
        }
        if (d.events && d.events.length > 0) {
            el.innerHTML = d.events.map(e =>
                '<div class="issue-item"><div class="issue-row">' +
                '<div class="issue-content">' +
                '<div class="issue-title">' + e.title + '</div>' +
                (e.location ? '<div class="issue-sub" style="color:var(--text-4)">&#128205; ' + e.location.substring(0,40) + '</div>' : '') +
                '</div><span style="font-size:11px;font-weight:700;color:var(--cyan);white-space:nowrap;padding-left:8px">' + e.time + '</span>' +
                '</div></div>'
            ).join('');

            // Summary card retired with the home-tab refactor (10.05.2026); skip if absent.
            const calTime = document.getElementById('sum-cal-time');
            const calTitle = document.getElementById('sum-cal-title');
            if (calTime && calTitle) {
                const next = d.events[0];
                calTime.textContent = next.time || '??:??';
                calTitle.textContent = next.title || '-';
            }
        } else {
            el.innerHTML = '<div style="padding:10px 14px;color:var(--text-4);font-size:12px;">Keine Termine heute</div>';
            const calTime = document.getElementById('sum-cal-time');
            const calTitle = document.getElementById('sum-cal-title');
            if (calTime) calTime.textContent = '--';
            if (calTitle) calTitle.textContent = 'Keine Termine';
        }
    } catch(e) {
        document.getElementById('calendar-events').innerHTML = '<div style="padding:16px;color:var(--red)">Ladefehler</div>';
    }
}

async function loadN8n() {
    try {
        const d = await api('/n8n');
        const render = (items) => items.length > 0 ? items.map(w =>
            '<div class="issue-item"><div class="issue-row"><div class="issue-content">' +
            '<div class="issue-title" style="display:flex;align-items:center;gap:6px">' +
            '<span style="width:7px;height:7px;border-radius:50%;background:' + (w.active ? 'var(--accent)' : 'var(--red)') + ';flex-shrink:0;display:inline-block"></span>' +
            w.name + '</div>' +
            (w.last_run ? '<div class="issue-id" style="margin-top:1px">' + w.last_run + '</div>' : '') +
            '</div></div></div>'
        ).join('') : '<div style="padding:10px 14px;color:var(--text-4);font-size:12px;">Keine</div>';

        const active = (d.workflows || []).filter(w => w.active);
        const inactive = (d.workflows || []).filter(w => !w.active);
        document.getElementById('n8n-active').innerHTML = render(active);
        document.getElementById('n8n-inactive').innerHTML = render(inactive);

        // Summary cards retired with the home-tab refactor (10.05.2026); skip if absent.
        const n8nVal = document.getElementById('sum-n8n-val');
        const n8nSub = document.getElementById('sum-n8n-sub');
        if (n8nVal) n8nVal.textContent = active.length;
        if (n8nSub) n8nSub.textContent = active.length + ' aktiv · ' + inactive.length + ' inaktiv';
    } catch(e) {
        document.getElementById('n8n-active').innerHTML = '<div style="padding:16px;color:var(--red)">Ladefehler</div>';
    }
}

async function loadSocial() {
    try {
        const d = await api('/social');
        const posted = document.getElementById('social-posted');
        const upcoming = document.getElementById('social-upcoming');

        const renderPost = (p) =>
            '<div class="issue-item"><div class="issue-row"><div class="issue-content">' +
            '<div class="issue-id">' + (p.platform || '') + (p.date ? ' · ' + p.date : '') + (p.time ? ' ' + p.time : '') + '</div>' +
            '<div class="issue-title">' + (p.content || '').substring(0, 80) + '</div>' +
            '</div></div></div>';

        posted.innerHTML = (d.posted || []).length > 0
            ? (d.posted || []).map(renderPost).join('')
            : '<div style="padding:16px;color:var(--text-4)">Noch keine Posts</div>';
        upcoming.innerHTML = (d.upcoming || []).length > 0
            ? (d.upcoming || []).map(renderPost).join('')
            : '<div style="padding:16px;color:var(--text-4)">Keine geplanten Posts</div>';
    } catch(e) {
        document.getElementById('social-posted').innerHTML = '<div style="padding:16px;color:var(--red)">Ladefehler</div>';
    }
}

function renderNewsList(elId, items, emptyMsg) {
    const el = document.getElementById(elId);
    if (!el) return;
    while (el.firstChild) el.removeChild(el.firstChild);
    if (!items || items.length === 0) {
        const empty = document.createElement('div');
        empty.style.cssText = 'padding:14px 0;color:var(--text-4);font-size:12px';
        empty.textContent = emptyMsg || '—';
        el.appendChild(empty);
        return;
    }
    for (const n of items) {
        const item = document.createElement('div');
        item.className = 'news-item';
        const meta = document.createElement('div');
        meta.className = 'news-item-meta';
        const src = document.createElement('span');
        src.className = 'news-source';
        src.textContent = n.source || '';
        meta.appendChild(src);
        if (n.date) meta.appendChild(document.createTextNode(' · ' + n.date));
        item.appendChild(meta);
        const title = document.createElement('div');
        title.className = 'news-item-title';
        title.textContent = n.title || '';
        item.appendChild(title);
        if (n.summary) {
            const sum = document.createElement('div');
            sum.className = 'news-item-summary';
            sum.textContent = n.summary;
            item.appendChild(sum);
        }
        el.appendChild(item);
    }
}

async function loadNews() {
    try {
        const d = await api('/news');
        renderNewsList('home-news-items', (d.items || []).slice(0, 5), 'Keine externen Headlines geladen.');
    } catch(e) {}
}

async function loadErkenntnisse() {
    try {
        const d = await api('/erkenntnisse');
        renderNewsList('home-erkenntnisse', (d.items || []).slice(0, 5), 'Noch keine Erkenntnisse.');
    } catch(e) {}
}

function escHtml(s) {
    return String(s || '').replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
}

function badgeHtml(active, labelOn, labelOff) {
    const on = !!active;
    const color = on ? 'var(--green)' : 'rgba(235,235,245,0.3)';
    const bg = on ? 'rgba(48,209,88,0.15)' : 'rgba(120,120,128,0.16)';
    const text = on ? (labelOn || 'aktiv') : (labelOff || 'inaktiv');
    return '<span style="font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;padding:3px 8px;border-radius:999px;color:' + color + ';background:' + bg + '">' + text + '</span>';
}

async function loadCapabilities() {
    try {
        const d = await api('/capabilities');
        const plugins = d.plugins || [];
        const globalSkills = d.global_skills || [];
        const mcps = d.mcp_servers || [];
        const inactive = d.inactive_skill_repos || [];

        // === PLUGINS ===
        const plEl = document.getElementById('capabilities-plugins');
        document.getElementById('capabilities-count-plugins').textContent = plugins.length ? '· ' + plugins.length : '';
        if (plugins.length === 0) {
            plEl.innerHTML = '<div style="padding:16px;color:var(--text-4)">Keine Plugins installiert.</div>';
        } else {
            plEl.innerHTML = plugins.map(p =>
                '<div class="card-row"><div class="card-row-left" style="flex-direction:column;align-items:flex-start;gap:1px;min-width:0;flex:1">' +
                '<div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap"><span style="font-weight:600;color:var(--text);font-size:12px">' + escHtml(p.name) + '</span>' + badgeHtml(true, 'aktiv') + '</div>' +
                '<div style="font-size:10px;color:var(--text-4)">' + escHtml(p.marketplace) + ' · v' + escHtml(p.version) + (p.last_updated ? ' · ' + escHtml(p.last_updated) : '') + ' · ' + p.skill_count + ' Skills</div>' +
                '</div></div>'
            ).join('');
        }

        // === SKILLS (flach, alle aktiv geladenen) ===
        const allSkills = [];
        plugins.forEach(p => (p.skills || []).forEach(s => allSkills.push({
            name: s.name, description: s.description, source: p.name + ' (plugin)', active: true
        })));
        globalSkills.forEach(s => allSkills.push({
            name: s.name, description: s.description, source: 'user (~/.claude/skills)', active: true
        }));
        // inaktive Skill-Repos mit reinziehen damit Kais den Gesamt-Überblick hat
        inactive.forEach(r => (r.skills || []).forEach(name => allSkills.push({
            name: name, description: '', source: r.repo + ' (' + r.path + ')', active: false
        })));
        allSkills.sort((a, b) => {
            if (a.active !== b.active) return a.active ? -1 : 1;
            return a.name.localeCompare(b.name);
        });
        const skEl = document.getElementById('capabilities-skills');
        document.getElementById('capabilities-count-skills').textContent = allSkills.length ? '· ' + allSkills.filter(s => s.active).length + ' aktiv / ' + allSkills.length + ' total' : '';
        if (allSkills.length === 0) {
            skEl.innerHTML = '<div style="padding:16px;color:var(--text-4)">Keine Skills gefunden.</div>';
        } else {
            skEl.innerHTML = allSkills.map(s => {
                const dim = s.active ? '' : 'opacity:0.45;';
                return '<div class="card-row" style="' + dim + '"><div class="card-row-left" style="flex-direction:column;align-items:flex-start;gap:1px;flex:1;min-width:0">' +
                    '<div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap"><span style="font-weight:600;color:var(--text);font-size:12px">' + escHtml(s.name) + '</span>' + badgeHtml(s.active, 'aktiv', 'inaktiv') + '<span style="font-size:9px;color:var(--text-4)">' + escHtml(s.source) + '</span></div>' +
                    (s.description ? '<div style="font-size:10px;color:var(--text-3);line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden">' + escHtml(s.description) + '</div>' : '') +
                    '</div></div>';
            }).join('');
        }

        // === MCP SERVER ===
        const mcpEl = document.getElementById('capabilities-mcp');
        document.getElementById('capabilities-count-mcp').textContent = mcps.length ? '· ' + mcps.filter(m => m.active).length + ' aktiv / ' + mcps.length + ' total' : '';
        if (mcps.length === 0) {
            mcpEl.innerHTML = '<div style="padding:16px;color:var(--text-4)">Keine MCP-Server konfiguriert.</div>';
        } else {
            mcpEl.innerHTML = mcps.map(m => {
                const dim = m.active ? '' : 'opacity:0.45;';
                const detail = m.url || m.command || m.source;
                return '<div class="card-row" style="' + dim + '"><div class="card-row-left" style="flex-direction:column;align-items:flex-start;gap:1px;flex:1;min-width:0">' +
                    '<div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap"><span style="font-weight:600;color:var(--text);font-size:12px">' + escHtml(m.name) + '</span>' + badgeHtml(m.active, 'online', 'offline') + '</div>' +
                    '<div style="font-size:10px;color:var(--text-4);word-break:break-all">' + escHtml(m.type || '') + (detail ? ' · ' + escHtml(detail) : '') + '</div>' +
                    '</div></div>';
            }).join('');
        }

        // === INAKTIVE SKILL-ORDNER ===
        const inaEl = document.getElementById('capabilities-inactive');
        if (inactive.length === 0) {
            inaEl.innerHTML = '<div style="padding:16px;color:var(--text-4)">Keine.</div>';
        } else {
            inaEl.innerHTML = inactive.map(r =>
                '<details style="border-top:1px solid rgba(255,255,255,0.06)"><summary style="padding:12px 16px;cursor:pointer;display:flex;justify-content:space-between;align-items:center;opacity:0.7">' +
                '<div><div style="font-weight:600">' + escHtml(r.repo) + '</div>' +
                '<div style="font-size:11px;color:var(--text-4);margin-top:2px">' + escHtml(r.path) + '</div></div>' +
                '<span style="font-size:11px;color:var(--text-3)">' + r.skill_count + ' Skills</span>' +
                '</summary>' +
                (r.skills && r.skills.length
                    ? '<div style="padding:8px 16px;font-size:12px;color:var(--text-3);line-height:1.7">' + r.skills.map(escHtml).join(', ') + '</div>'
                    : '') +
                '</details>'
            ).join('');
        }
    } catch(e) {
        document.getElementById('capabilities-plugins').innerHTML = '<div style="padding:16px;color:var(--red)">Fehler: ' + escHtml(e.message || e) + '</div>';
    }
}

// Brain modal removed 2026-05-04 — replaced by Brain tab

async function loadToolFailures() {
    try {
        const r = await fetch('/tool-failures?t=' + TOKEN + '&limit=20');
        const d = await r.json();
        const totalEl = document.getElementById('tf-total');
        const wrap = document.getElementById('tool-failures');
        if (!d.ok) {
            if (totalEl) totalEl.textContent = '';
            wrap.innerHTML = '<div style="padding:16px;color:var(--red)">Fehler: ' + escHtml(d.error || 'unknown') + '</div>';
            return;
        }
        const total = d.total_24h || 0;
        if (totalEl) totalEl.textContent = total === 0 ? '(0)' : '(' + total + ')';
        if (total === 0 && (!d.recent || d.recent.length === 0)) {
            wrap.innerHTML = '<div style="padding:16px;color:var(--text-4)">Keine Tool-Failures in den letzten 24h. Sauber.</div>';
            return;
        }
        let html = '';
        if (d.by_tool && d.by_tool.length) {
            html += '<div style="padding:12px 16px;border-bottom:1px solid rgba(255,255,255,0.06);font-size:11px;color:var(--text-3);text-transform:uppercase;letter-spacing:0.05em">Top Tools</div>';
            d.by_tool.slice(0, 8).forEach(function(t) {
                html += '<div class="card-row"><div class="card-row-left"><span class="card-row-label">' + escHtml(t.tool_name) + '</span></div>';
                html += '<div class="card-row-right" style="color:var(--text-3)">' + t.count + 'x</div></div>';
            });
        }
        if (d.recent && d.recent.length) {
            html += '<div style="padding:12px 16px;border-top:1px solid rgba(255,255,255,0.06);border-bottom:1px solid rgba(255,255,255,0.06);font-size:11px;color:var(--text-3);text-transform:uppercase;letter-spacing:0.05em">Letzte Failures</div>';
            d.recent.slice(0, 10).forEach(function(r) {
                const tn = r.tool_name || '?';
                const tt = (r.created_at || '').slice(0, 16).replace('T', ' ');
                const ex = (r.error_excerpt || '').slice(0, 140);
                html += '<div class="card-row" style="flex-direction:column;align-items:flex-start;gap:2px">';
                html += '<div style="display:flex;justify-content:space-between;width:100%"><span class="card-row-label">' + escHtml(tn) + '</span><span style="color:var(--text-4);font-size:10px">' + escHtml(tt) + '</span></div>';
                html += '<div style="font-size:11px;color:var(--text-3);font-family:monospace;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + escHtml(ex) + '</div>';
                html += '</div>';
            });
        }
        wrap.innerHTML = html;
    } catch(e) {
        document.getElementById('tool-failures').innerHTML = '<div style="padding:16px;color:var(--red)">Fehler: ' + escHtml(e.message || e) + '</div>';
    }
}

async function loadCostOverview() {
    try {
        const r = await fetch('/cost-overview?t=' + TOKEN);
        const d = await r.json();
        const wrap = document.getElementById('cost-overview');
        const fmt = function(v) {
            if (v === null || v === undefined) return '-';
            if (typeof v === 'number') return v.toFixed(2) + ' EUR';
            return String(v);
        };
        const s = d.stripe || {};
        const e = d.estimated_costs_per_month || {};
        let html = '';
        // Stripe section
        html += '<div style="padding:12px 16px;border-bottom:1px solid rgba(255,255,255,0.06);font-size:11px;color:var(--text-3);text-transform:uppercase;letter-spacing:0.05em">Stripe (Live)</div>';
        html += '<div class="card-row"><div class="card-row-left"><span class="card-row-label">Aktive Subscriptions</span></div><div class="card-row-right">' + (s.active_subscriptions !== null ? s.active_subscriptions : '-') + '</div></div>';
        html += '<div class="card-row"><div class="card-row-left"><span class="card-row-label">MRR</span></div><div class="card-row-right" style="color:var(--green)">' + fmt(s.active_mrr_eur) + '</div></div>';
        html += '<div class="card-row"><div class="card-row-left"><span class="card-row-label">Available Balance</span></div><div class="card-row-right">' + fmt(s.available_eur) + '</div></div>';
        html += '<div class="card-row"><div class="card-row-left"><span class="card-row-label">Pending Balance</span></div><div class="card-row-right">' + fmt(s.pending_eur) + '</div></div>';
        html += '<div class="card-row"><div class="card-row-left"><span class="card-row-label">Charges 30d (succeeded)</span></div><div class="card-row-right">' + fmt(s.charges_30d_succeeded_eur) + ' / ' + (s.charges_30d_count || 0) + '</div></div>';
        // Costs section
        html += '<div style="padding:12px 16px;border-top:1px solid rgba(255,255,255,0.06);border-bottom:1px solid rgba(255,255,255,0.06);font-size:11px;color:var(--text-3);text-transform:uppercase;letter-spacing:0.05em">Geschaetzte Kosten / Monat</div>';
        html += '<div class="card-row"><div class="card-row-left"><span class="card-row-label">Subscriptions (Software)</span></div><div class="card-row-right" style="color:var(--text-3)">' + fmt(e.subscriptions_total_eur) + '</div></div>';
        html += '<div class="card-row"><div class="card-row-left"><span class="card-row-label">VPS Hostinger</span></div><div class="card-row-right" style="color:var(--text-3)">' + fmt(e.vps_hostinger_eur) + '</div></div>';
        html += '<div class="card-row"><div class="card-row-left"><span class="card-row-label">Anthropic</span></div><div class="card-row-right" style="color:var(--text-4);font-size:10px">' + escHtml(String(e.anthropic_max_plan_eur || '-')) + '</div></div>';
        if (d.errors && d.errors.length) {
            html += '<div style="padding:8px 16px;border-top:1px solid rgba(255,255,255,0.06);color:var(--orange);font-size:10px">Hinweise: ' + escHtml(d.errors.join(' | ')) + '</div>';
        }
        wrap.innerHTML = html;
    } catch(e) {
        document.getElementById('cost-overview').innerHTML = '<div style="padding:16px;color:var(--red)">Fehler: ' + escHtml(e.message || e) + '</div>';
    }
}

async function loadQuickSkills() {
    try {
        const r = await fetch('/quick-skills?t=' + TOKEN);
        const d = await r.json();
        const wrap = document.getElementById('quick-skills');
        if (!d.ok || !d.skills || d.skills.length === 0) {
            wrap.innerHTML = '<div style="padding:16px;color:var(--text-4);grid-column:1/-1">Keine Skills definiert. Datei: /root/.aria-quick-skills.json</div>';
            return;
        }
        let html = '';
        d.skills.forEach(function(s) {
            html += '<button class="qs-btn" data-skill-id="' + escHtml(s.id) + '" onclick="triggerQuickSkill(this.dataset.skillId, this)" title="' + escHtml(s.description || '') + '">';
            html += '<span style="font-size:18px">' + escHtml(s.icon || '⚡') + '</span>';
            html += '<span class="qs-label">' + escHtml(s.label) + '</span>';
            html += '</button>';
        });
        wrap.innerHTML = html;
    } catch(e) {
        document.getElementById('quick-skills').innerHTML = '<div style="padding:16px;color:var(--red);grid-column:1/-1">Fehler: ' + escHtml(e.message || e) + '</div>';
    }
}

async function triggerQuickSkill(id, btn) {
    if (btn) { btn.disabled = true; btn.style.opacity = '0.5'; }
    alog('Quick Skill ' + id + ' getriggert...');
    try {
        const r = await fetch('/api/quick-skill-trigger', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({token: TOKEN, skill_id: id})
        });
        const d = await r.json();
        alog(d.message || 'unbekannt');
    } catch(e) {
        alog('Fehler: ' + (e.message || e));
    } finally {
        if (btn) { setTimeout(function() { btn.disabled = false; btn.style.opacity = '1'; }, 800); }
    }
}

async function loadAll() {
    await Promise.all([loadHero(), loadStatus(), loadSystem(), loadServices(), loadActivity(), loadUsage(), loadLogs(), loadLinear(), loadCalendar(), loadN8n(), loadSocial(), loadNews(), loadErkenntnisse(), loadCapabilities(), loadToolFailures(), loadCostOverview(), loadQuickSkills(), loadEffort()]);
}

async function restart() {
    const btn = document.getElementById('restart-btn');
    btn.disabled = true; btn.textContent = 'Startet...';
    alog('Restart angefordert...');
    try {
        const r = await fetch('/restart', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({token: TOKEN})
        });
        const d = await r.json();
        alog(d.message);
        if (d.ok) {
            alog('Warte auf Aria...');
            setTimeout(async () => {
                await loadAll();
                btn.disabled = false; btn.textContent = 'Restart';
            }, 8000);
        } else {
            btn.disabled = false; btn.textContent = 'Restart';
        }
    } catch(e) {
        alog('Fehler: ' + e.message);
        btn.disabled = false; btn.textContent = 'Restart';
    }
}

async function gitPull() {
    alog('Git Pull aria-brain...');
    try {
        const r = await fetch('/git-pull', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({token: TOKEN})
        });
        const d = await r.json();
        alog(d.message);
    } catch(e) { alog('Fehler: ' + e.message); }
}

function openPlaner() {
    window.location.href = '/planer?t=' + TOKEN;
}

function toggleTheme() {
    const isLight = document.body.classList.toggle('light');
    document.getElementById('theme-btn').textContent = isLight ? '☀️' : '🌙';
    localStorage.setItem('aria-theme', isLight ? 'light' : 'dark');
}
(function() {
    if (localStorage.getItem('aria-theme') === 'light') {
        document.body.classList.add('light');
        document.getElementById('theme-btn').textContent = '☀️';
    }
})();

loadAll();
setInterval(loadHero, 10000);
setInterval(loadActivity, 15000);
setInterval(loadStatus, 30000);
setInterval(loadSystem, 60000);
setInterval(loadUsage, 60000);
setInterval(loadServices, 60000);
setInterval(loadLinear, 120000);
setInterval(loadCalendar, 120000);
setInterval(loadN8n, 120000);
setInterval(loadSocial, 180000);
setInterval(loadNews, 300000);
setInterval(loadErkenntnisse, 300000);

// ===== ARIA INBOX =====
document.addEventListener('click', function(e) {
    const item = e.target.closest('.issue-tappable');
    if (item && item.dataset.inbox) fillInbox(item.dataset.inbox);
});

function fillInbox(text) {
    const input = document.getElementById('inboxInput');
    input.value = text;
    resizeInbox(input);
    input.focus();
    input.setSelectionRange(text.length, text.length);
    // Flash feedback
    input.style.borderColor = 'rgba(30,240,161,0.5)';
    setTimeout(() => { input.style.borderColor = ''; }, 600);
}

function resizeInbox(el) {
    el.style.height = 'auto';
    el.style.height = Math.min(el.scrollHeight, 100) + 'px';
}

function handleInboxKey(e) {
    if (e.key === 'Enter' && !e.shiftKey) {
        e.preventDefault();
        sendToAria();
    }
}

async function sendToAria() {
    const input = document.getElementById('inboxInput');
    const btn = document.getElementById('inboxSend');
    const fb = document.getElementById('inboxFeedback');
    const text = input.value.trim();
    if (!text) return;

    input.value = '';
    input.style.height = 'auto';
    input.disabled = true;
    btn.style.opacity = '0.5';
    fb.textContent = 'Sende…';
    fb.className = 'inbox-feedback';

    try {
        const r = await fetch('/api/send-message?t=' + TOKEN, {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({text, token: TOKEN})
        });
        const d = await r.json();
        if (d.ok) {
            fb.textContent = '✓ Gesendet an Aria';
            fb.className = 'inbox-feedback ok';
        } else {
            fb.textContent = '✗ ' + (d.message || 'Fehler');
            fb.className = 'inbox-feedback err';
            input.value = text;
        }
    } catch(e) {
        fb.textContent = '✗ Verbindungsfehler';
        fb.className = 'inbox-feedback err';
        input.value = text;
    }

    input.disabled = false;
    btn.style.opacity = '1';
    input.focus();
    setTimeout(() => { fb.textContent = ''; fb.className = 'inbox-feedback'; }, 3000);
}

// ====== EFFORT QUICK-SET ======
function markEffortChip(level) {
    document.querySelectorAll('#effortRow .effort-chip').forEach(function(c) {
        c.classList.toggle('active', c.dataset.level === level);
    });
}

async function setEffort(level, btn) {
    const fb = document.getElementById('inboxFeedback');
    fb.textContent = 'Setze Effort ' + level + '…';
    fb.className = 'inbox-feedback';
    try {
        const r = await fetch('/api/effort?t=' + TOKEN, {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({level: level, token: TOKEN})
        });
        const d = await r.json();
        if (d.ok) {
            fb.textContent = '✓ /effort ' + level + ' an Session gesendet';
            fb.className = 'inbox-feedback ok';
            markEffortChip(level);
        } else {
            fb.textContent = '✗ ' + (d.message || 'Fehler');
            fb.className = 'inbox-feedback err';
        }
    } catch(e) {
        fb.textContent = '✗ Verbindungsfehler';
        fb.className = 'inbox-feedback err';
    }
    setTimeout(() => { fb.textContent = ''; fb.className = 'inbox-feedback'; }, 4000);
}

async function loadEffort() {
    try {
        const r = await fetch('/api/effort?t=' + TOKEN);
        const d = await r.json();
        if (d.ok && d.level) markEffortChip(d.level);
    } catch(e) {}
}

// ====== BRAIN BROWSER ======
window._brainFiles = [];
window._brainCurrentPath = null;

async function loadBrainTree() {
    const root = document.getElementById('brain-tree-root');
    root.innerHTML = '<div style="padding:8px;color:var(--text-4);font-size:12px">Lade Vault...</div>';
    try {
        const r = await fetch('/brain-files-tree?t=' + TOKEN);
        const d = await r.json();
        if (!d.ok) throw new Error(d.error || 'unknown');
        window._brainFiles = [];
        collectBrainFiles(d.tree);
        root.innerHTML = '';
        renderBrainNode(d.tree, root, 0, true);
        const stats = document.getElementById('brain-stats');
        if (stats) stats.textContent = window._brainFiles.length + ' Files';
    } catch (e) {
        root.innerHTML = '<div style="padding:8px;color:var(--red);font-size:12px">Fehler: ' + escHtml(e.message) + '</div>';
    }
}

let _brainSearchTimer = null;
function onBrainSearchInput() {
    const q = (document.getElementById('brain-search').value || '').trim();
    const tree = document.getElementById('brain-tree-root');
    const results = document.getElementById('brain-search-results');
    const modeLbl = document.getElementById('brain-mode-label');
    if (_brainSearchTimer) clearTimeout(_brainSearchTimer);
    if (q.length === 0) {
        tree.style.display = '';
        results.style.display = 'none';
        if (modeLbl) modeLbl.textContent = '';
        return;
    }
    // First: instant client-side filter on file paths/names
    const lq = q.toLowerCase();
    const matches = window._brainFiles.filter(p => p.toLowerCase().includes(lq));
    if (modeLbl) modeLbl.textContent = matches.length + ' Filter';
    tree.style.display = 'none';
    results.style.display = '';
    results.innerHTML = matches.slice(0, 60).map(p => {
        const name = p.split('/').pop().replace(/\.md$/i, '');
        const dir = p.split('/').slice(0, -1).join('/');
        return '<div class="brain-tree-row" data-path="' + escAttr(p) + '" onclick="openBrainFile(this.dataset.path)" style="padding:5px 8px"><span class="caret"></span><span class="icon">&#128196;</span><span class="name">' + escHtml(name) + '<span style="color:var(--text-4);font-size:10px;display:block;margin-top:2px;font-family:monospace">' + escHtml(dir) + '</span></span></div>';
    }).join('') || '<div style="padding:8px;color:var(--text-4);font-size:11px">Keine Datei-Treffer. Semantic-Suche läuft...</div>';
    // After 400ms idle, also fire semantic search and append below client filter
    _brainSearchTimer = setTimeout(() => runBrainSemantic(q), 400);
}

async function runBrainSemantic(q) {
    if (!q || q.length < 2) return;
    try {
        const r = await fetch('/brain-search?t=' + TOKEN + '&q=' + encodeURIComponent(q));
        const d = await r.json();
        if (!d.ok || !d.hits || !d.hits.length) return;
        const results = document.getElementById('brain-search-results');
        const sep = '<div style="padding:6px 8px;font-size:10px;color:var(--text-4);text-transform:uppercase;letter-spacing:0.05em;border-top:1px dashed rgba(255,255,255,0.06);margin-top:8px">Semantic (' + d.hits.length + (d.fallback ? ' · ' + d.fallback : '') + ')</div>';
        const items = d.hits.map(h => {
            const path = (h.path || '').replace('/root/aria/brain/', '');
            const name = (h.title || path.split('/').pop() || '').replace(/\.md$/i, '');
            const score = h.score ? Math.round(h.score * 100) + '%' : '';
            const snippet = h.snippet ? '<div style="font-size:10px;color:var(--text-3);line-height:1.35;margin-top:2px;max-height:36px;overflow:hidden">' + escHtml(h.snippet) + '</div>' : '';
            return '<div class="brain-tree-row" data-path="' + escAttr(path) + '" onclick="openBrainFile(this.dataset.path)" style="padding:6px 8px;flex-direction:column;align-items:flex-start"><div style="display:flex;justify-content:space-between;width:100%;gap:6px"><span class="name">' + escHtml(name) + '</span><span style="font-size:10px;color:var(--accent)">' + score + '</span></div>' + snippet + '</div>';
        }).join('');
        results.insertAdjacentHTML('beforeend', sep + items);
    } catch (e) {}
}

function collectBrainFiles(node) {
    if (!node) return;
    if (node.type === 'file') {
        window._brainFiles.push(node.path);
    } else if (node.children) {
        node.children.forEach(collectBrainFiles);
    }
}

function renderBrainNode(node, container, depth, isRoot) {
    if (!node) return;
    if (node.type === 'folder') {
        if (!isRoot) {
            const row = document.createElement('div');
            row.className = 'brain-tree-row brain-tree-folder';
            row.innerHTML = '<span class="caret">&#9662;</span><span class="icon">&#128193;</span><span class="name">' + escHtml(node.name) + '</span>';
            const childrenWrap = document.createElement('div');
            childrenWrap.className = 'brain-tree-children';
            row.onclick = (e) => {
                e.stopPropagation();
                row.classList.toggle('collapsed');
                childrenWrap.classList.toggle('collapsed');
            };
            container.appendChild(row);
            container.appendChild(childrenWrap);
            (node.children || []).forEach(c => renderBrainNode(c, childrenWrap, depth + 1, false));
        } else {
            (node.children || []).forEach(c => renderBrainNode(c, container, depth, false));
        }
    } else if (node.type === 'file') {
        const row = document.createElement('div');
        row.className = 'brain-tree-row';
        row.dataset.path = node.path;
        row.innerHTML = '<span class="caret"></span><span class="icon">&#128196;</span><span class="name">' + escHtml(node.name.replace(/\.md$/i, '')) + '</span>';
        row.onclick = (e) => { e.stopPropagation(); openBrainFile(node.path); };
        container.appendChild(row);
    }
}

async function openBrainFile(relPath) {
    window._brainCurrentPath = relPath;
    document.querySelectorAll('#brain-tree-root .brain-tree-row').forEach(r => {
        r.classList.toggle('active', r.dataset.path === relPath);
    });
    const reader = document.getElementById('brain-reader');
    reader.innerHTML = '<div class="brain-reader-empty">Lade...</div>';
    const pathEl = document.getElementById('brain-current-path');
    if (pathEl) pathEl.textContent = relPath;
    const sidebar = document.getElementById('brain-sidebar');
    if (sidebar && window.innerWidth <= 768) sidebar.classList.remove('open');
    try {
        const r = await fetch('/brain-file?t=' + TOKEN + '&path=' + encodeURIComponent(relPath));
        const d = await r.json();
        if (!d.ok) throw new Error(d.error || 'unknown');
        renderBrainFile(d, relPath);
    } catch (e) {
        reader.innerHTML = '<div class="brain-reader-empty" style="color:var(--red)">Fehler: ' + escHtml(e.message) + '</div>';
    }
}

function renderBrainFile(d, relPath) {
    const reader = document.getElementById('brain-reader');
    let content = d.content || '';
    let frontmatter = '';
    const fm = content.match(/^---\n([\s\S]*?)\n---\n/);
    if (fm) {
        frontmatter = fm[1];
        content = content.slice(fm[0].length);
    }
    const date = d.mtime ? new Date(d.mtime * 1000).toLocaleString('de-DE') : '';
    const html = renderMarkdown(content);
    reader.innerHTML =
        '<div class="brain-reader-title">' + escHtml(d.title || relPath) + '</div>' +
        '<div class="brain-reader-meta">' + escHtml(relPath) + ' &middot; ' + (d.size || 0) + ' Bytes &middot; ' + escHtml(date) + '</div>' +
        (frontmatter ? '<details class="brain-reader-frontmatter"><summary style="cursor:pointer;color:var(--accent);font-size:11px">Frontmatter</summary>' + escHtml(frontmatter) + '</details>' : '') +
        '<div class="brain-reader-content">' + html + '</div>';
    reader.scrollTop = 0;
    reader.querySelectorAll('.wikilink').forEach(link => {
        link.onclick = (e) => {
            e.preventDefault();
            const target = link.dataset.target;
            const match = resolveWikilink(target);
            if (match) openBrainFile(match);
            else alog('Brain: Wikilink "' + target + '" nicht gefunden');
        };
    });
}

function resolveWikilink(name) {
    if (!name) return null;
    const norm = name.toLowerCase().replace(/\.md$/i, '');
    return window._brainFiles.find(p => {
        const base = p.split('/').pop().toLowerCase().replace(/\.md$/i, '');
        return base === norm;
    }) || window._brainFiles.find(p => p.toLowerCase().replace(/\.md$/i, '').endsWith('/' + norm)) || null;
}

function renderMarkdown(src) {
    if (!src) return '';
    // Escape HTML first
    let s = src.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    // Code fences
    s = s.replace(/```([a-zA-Z0-9_-]*)\n([\s\S]*?)```/g, (_, lang, code) =>
        '<pre><code class="lang-' + lang + '">' + code + '</code></pre>'
    );
    // Inline code
    s = s.replace(/`([^`\n]+)`/g, '<code>$1</code>');
    // Headings
    s = s.replace(/^######\s+(.+)$/gm, '<h6>$1</h6>');
    s = s.replace(/^#####\s+(.+)$/gm, '<h5>$1</h5>');
    s = s.replace(/^####\s+(.+)$/gm, '<h4>$1</h4>');
    s = s.replace(/^###\s+(.+)$/gm, '<h3>$1</h3>');
    s = s.replace(/^##\s+(.+)$/gm, '<h2>$1</h2>');
    s = s.replace(/^#\s+(.+)$/gm, '<h1>$1</h1>');
    // HR
    s = s.replace(/^---+$/gm, '<hr>');
    // Blockquote
    s = s.replace(/^>\s?(.+)$/gm, '<blockquote>$1</blockquote>');
    // Wikilinks [[Note|alias]] or [[Note]]
    s = s.replace(/\[\[([^\]\|\n]+)(?:\|([^\]\n]+))?\]\]/g, (_, target, alias) => {
        const label = alias || target;
        return '<span class="wikilink" data-target="' + escAttr(target.trim()) + '">' + label + '</span>';
    });
    // Markdown links [text](url)
    s = s.replace(/\[([^\]\n]+)\]\(([^\)\n]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
    // Bold + italic
    s = s.replace(/\*\*([^\*\n]+)\*\*/g, '<strong>$1</strong>');
    s = s.replace(/(^|[^\*])\*([^\*\n]+)\*/g, '$1<em>$2</em>');
    // Tables (very simple)
    s = s.replace(/((?:^\|.+\|\s*\n)+)/gm, (block) => {
        const rows = block.trim().split('\n').map(r => r.trim());
        if (rows.length < 2) return block;
        const sep = rows[1];
        if (!/^\|?[\s\-\|:]+\|?$/.test(sep)) return block;
        const head = rows[0].split('|').slice(1, -1).map(c => '<th>' + c.trim() + '</th>').join('');
        const body = rows.slice(2).map(r => '<tr>' + r.split('|').slice(1, -1).map(c => '<td>' + c.trim() + '</td>').join('') + '</tr>').join('');
        return '<table><thead><tr>' + head + '</tr></thead><tbody>' + body + '</tbody></table>';
    });
    // Lists (unordered)
    s = s.replace(/(^[ \t]*[-*+]\s+.+(?:\n[ \t]*[-*+]\s+.+)*)/gm, (block) => {
        const items = block.split('\n').map(l => l.replace(/^[ \t]*[-*+]\s+/, '')).map(l => '<li>' + l + '</li>').join('');
        return '<ul>' + items + '</ul>';
    });
    // Lists (ordered)
    s = s.replace(/(^[ \t]*\d+\.\s+.+(?:\n[ \t]*\d+\.\s+.+)*)/gm, (block) => {
        const items = block.split('\n').map(l => l.replace(/^[ \t]*\d+\.\s+/, '')).map(l => '<li>' + l + '</li>').join('');
        return '<ol>' + items + '</ol>';
    });
    // Paragraphs (lines that are not block-level already)
    s = s.split(/\n{2,}/).map(block => {
        if (/^<(h\d|ul|ol|li|pre|table|blockquote|hr|p)/.test(block.trim())) return block;
        if (!block.trim()) return '';
        return '<p>' + block.trim().replace(/\n/g, '<br>') + '</p>';
    }).join('\n');
    return s;
}

function escAttr(s) {
    return String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}

function toggleBrainSidebar() {
    const sb = document.getElementById('brain-sidebar');
    if (sb) sb.classList.toggle('open');
}

function brainCollapseAll() {
    const root = document.getElementById('brain-tree-root');
    if (!root) return;
    root.querySelectorAll('.brain-tree-folder').forEach(r => r.classList.add('collapsed'));
    root.querySelectorAll('.brain-tree-children').forEach(c => c.classList.add('collapsed'));
}

function brainExpandAll() {
    const root = document.getElementById('brain-tree-root');
    if (!root) return;
    root.querySelectorAll('.brain-tree-folder').forEach(r => r.classList.remove('collapsed'));
    root.querySelectorAll('.brain-tree-children').forEach(c => c.classList.remove('collapsed'));
}

window._brainGraphLoaded = false;
window._brainGraphMode = false;

async function toggleBrainGraph() {
    const reader = document.getElementById('brain-reader');
    const graphView = document.getElementById('brain-graph-view');
    const btn = document.getElementById('brain-graph-toggle');
    window._brainGraphMode = !window._brainGraphMode;
    if (window._brainGraphMode) {
        reader.style.display = 'none';
        graphView.style.display = 'block';
        btn.classList.add('active');
        if (!window._brainGraphLoaded) {
            await loadD3();
            await loadBrainGraph();
            window._brainGraphLoaded = true;
        }
    } else {
        reader.style.display = '';
        graphView.style.display = 'none';
        btn.classList.remove('active');
    }
}

function loadD3() {
    return new Promise((resolve, reject) => {
        if (window.d3) return resolve();
        const s = document.createElement('script');
        s.src = 'https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js';
        s.onload = resolve;
        s.onerror = () => reject(new Error('D3 konnte nicht geladen werden'));
        document.head.appendChild(s);
    });
}

async function loadBrainGraph() {
    const status = document.getElementById('brain-graph-status');
    status.textContent = 'Lade Knoten und Kanten...';
    try {
        const r = await fetch('/brain-graph?t=' + TOKEN);
        const d = await r.json();
        if (!d.ok) throw new Error(d.error || 'unknown');
        renderBrainGraph(d.nodes || [], d.edges || []);
        status.textContent = d.nodes.length + ' Notes · ' + d.edges.length + ' Wikilinks';
    } catch (e) {
        status.textContent = 'Fehler: ' + (e.message || e);
        status.style.color = 'var(--red)';
    }
}

function renderBrainGraph(nodes, links) {
    const svg = d3.select('#brain-graph-svg');
    svg.selectAll('*').remove();
    const wrap = document.getElementById('brain-graph-view');
    const w = wrap.clientWidth || 800;
    const h = wrap.clientHeight || 600;
    svg.attr('viewBox', [0, 0, w, h]);

    const g = svg.append('g');
    const zoom = d3.zoom().scaleExtent([0.2, 5]).on('zoom', (event) => g.attr('transform', event.transform));
    svg.call(zoom);

    const groupColors = {};
    const palette = ['#1EF0A1', '#64D2FF', '#FFD60A', '#FF9F0A', '#FF453A', '#BF5AF2', '#5E9FFF', '#5AC8FA', '#FF6E91', '#A8E063'];
    const groups = Array.from(new Set(nodes.map(n => n.group)));
    groups.forEach((g, i) => groupColors[g] = palette[i % palette.length]);

    // Linked nodes have id-based references for d3-force
    const linkData = links.map(l => Object.assign({}, l));

    const link = g.append('g')
        .attr('class', 'links')
        .selectAll('line')
        .data(linkData)
        .enter().append('line')
        .attr('class', 'link');

    const node = g.append('g')
        .attr('class', 'nodes')
        .selectAll('circle')
        .data(nodes)
        .enter().append('circle')
        .attr('class', 'node')
        .attr('r', 4)
        .attr('fill', n => groupColors[n.group] || '#888')
        .attr('stroke', 'rgba(0,0,0,0.4)')
        .attr('stroke-width', 1)
        .on('click', (event, n) => {
            window._brainGraphMode = false;
            document.getElementById('brain-graph-view').style.display = 'none';
            document.getElementById('brain-reader').style.display = '';
            document.getElementById('brain-graph-toggle').classList.remove('active');
            openBrainFile(n.id);
        })
        .call(d3.drag()
            .on('start', (event, d) => { if (!event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; })
            .on('drag', (event, d) => { d.fx = event.x; d.fy = event.y; })
            .on('end', (event, d) => { if (!event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; }));

    node.append('title').text(n => n.label + '  [' + n.group + ']');

    const label = g.append('g')
        .attr('class', 'labels')
        .selectAll('text')
        .data(nodes.filter((_, i) => i % 1 === 0))
        .enter().append('text')
        .attr('class', 'label')
        .attr('dx', 6)
        .attr('dy', 3)
        .text(n => n.label.length > 28 ? n.label.slice(0, 26) + '…' : n.label)
        .style('opacity', 0);

    const simulation = d3.forceSimulation(nodes)
        .force('link', d3.forceLink(linkData).id(d => d.id).distance(40).strength(0.4))
        .force('charge', d3.forceManyBody().strength(-60))
        .force('center', d3.forceCenter(w / 2, h / 2))
        .force('collide', d3.forceCollide(8))
        .on('tick', () => {
            link.attr('x1', d => d.source.x).attr('y1', d => d.source.y)
                .attr('x2', d => d.target.x).attr('y2', d => d.target.y);
            node.attr('cx', d => d.x).attr('cy', d => d.y);
            label.attr('x', d => d.x).attr('y', d => d.y);
        });

    // Show labels at higher zoom
    svg.on('wheel.zoom-show-labels', () => {
        const t = d3.zoomTransform(svg.node());
        label.style('opacity', t.k > 1.5 ? 1 : 0);
    });
}

</script>
</body>
</html>c                   B   e Zd Zd Zd Zd5dZd ZdZdZd Z	d Z
d	 Zd
 Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd6dZd Zd Zd Zd Zd Zd Zd Z d Z!d  Z"d! Z#d" Z$d# Z%d$ Z&d% Z'd& Z(d' Z)d( Z*d7d)Z+d* Z,d+ Z-d, Z.d- Z/d8d.Z0d/ Z1d0 Z2d1 Z3d2 Z4d3 Z5y4)9Handlerc                     |t         k(  S N)TOKEN)selfr   s     r   check_tokenzHandler.check_token  s    ~    c                     i }d| j                   v rQ| j                   j                  d      d   j                  d      D ]!  }d|v s|j                  dd      \  }}|||<   # |S )N?r   &r   )r   r$   )r6   paramspkvs        r   _parse_paramszHandler._parse_params  si    $))YY__S)!,2237 "!8773?DAq !F1I" r8   c                     | j                  |       | j                  dd       | j                          | j                  j	                  t        j                  |      j                                y )NContent-Typeapplication/json)send_responsesend_headerend_headerswfiler   jsondumpsencode)r6   datacodes      r   
_send_jsonzHandler._send_json  sQ    4 );<

D)0023r8   c                    ddl ddl}ddl}t        |t              sdddS |j                  dd|      }t        |      dkD  r|dd }|j                         sdd	dS 	 t        j                         j                         d
z   }t        | d      r| j                  d   nd}|dd j                  dd      }t        dd      5 }|j                  d| d| dt        |       d|d	       ddd       	 t#        j$                  g ddd       t#        j$                  g ddd       t#        j$                  ddddd|gd !       t#        j$                  g d"dd       t&        rD|dd t        |      dkD  rd$ndz   }	d%|	 fd&}
|j)                  |
d'      j+                          d(diS # 1 sw Y   xY w# t         $ r Y w xY w# t         $ r}dd#| dcY d}~S d}~ww xY w))u  Route an inbox message to the Aria tmux session.

        Phase 0 refactor (2026-05-17): tmux send-keys is the critical path and
        determines success. The Telegram confirmation `📥 Aria erhält: …` runs
        fire-and-forget in a daemon thread — its failures are logged but never
        leak to the browser. Prior version returned `HTTP Error 400: Bad Request`
        to the panel when Telegram's API rejected the confirmation, even though
        the message had already reached Aria.
        r   NFzinvalid inputokmessage[\x00-\x08\x0b-\x1f\x7f-\x9f]r"     zempty after sanitizeZclient_address-P   
z\n/var/log/aria-control-send.loga[]  len=	 preview=)tmux	send-keys-tariaC-cT   capture_outputtimeoutr_   r`   ra   rb   C-ur_   r`   -lra   rb      rg   r_   r`   ra   rb   Entertmux send failed: u   …u   📥 Aria erhält: c            	         	 t        j                  t        d      j                         } j                  j                  dt         d| ddi      }j                  j                  |d       y # t        $ rz}	 t        d	d
      5 }|j                  dt        j                         j                          d| d       d d d        n# 1 sw Y   nxY wn# t        $ r Y nw xY wY d }~y Y d }~y d }~ww xY w)N)chat_idtextzhttps://api.telegram.org/botz/sendMessagerB   rC   rK   headers   rl   rY   rZ   r[   zZ] confirm-fail: rX   )rH   rI   OWNER_CHAT_IDrJ   requestRequestr+   urlopenr%   r   r   r   utcnow	isoformat)payloadreqtg_er   confirmurllibs       r   _send_tg_confirmz0Handler._send_telegram.<locals>._send_tg_confirmJ  s    "jj]G)TU\\^G ..0067I6J,W$~?Q.R 1 TC NN**3*:  !"BCH bAGGa(9(C(C(E'FFWX\W]]_$`ab b b$ sN   A/A3 3	C6=C	9C	CC	CC1	C$!C1#C$$C11C6)targetdaemonrP   )urllib.requestr0   	threading
isinstancestrsublenr   r   rz   r{   hasattrrU   replacer   r   r%   
subprocessrunr+   Threadstart)r6   rr   r0   r   tsippreviewr   e	preview_tr   r   r   s              @@r   _send_telegramzHandler._send_telegram!  s    	$$O<<vv6DAt9t;Dzz|,BCC	",,.4B+249I+J$$Q'PSB3Bi''e4G6< N!B4r"U3t9+YwkLMN
	FNNEVZdefNNEVZdefNNFKtVTJTUVNNGX\fgh Sb	c$i"nU"EI+I;7G $4TBHHJd|=N N 		  	F.@,DEE	FsJ    A#F9 (F-+F9 4A+G -F62F9 9	GG	G#GG#G#)lowmediumhighxhighmaxz/root/aria/state/effortc                     	 t        | j                        5 }|j                         j                         }ddd       d| j                  v r|dS ddS # 1 sw Y    xY w# t
        $ r dddcY S w xY w)uU   Zuletzt ueber das Panel gesetztes Level — Anzeige-State, kein Live-Read aus Claude.NTrP   level)r   EFFORT_STATE_FILEr   r   EFFORT_LEVELSr%   )r6   r   r   s      r   _get_effortzHandler._get_effort^  sz    	/d,,- )()%4;M;M2MXXSWXX) )  	/..	/s-   A! AA! A! AA! !A21A2c           
         |xs dj                         j                         }|| j                  vrdd|dS 	 t        j                         j                         dz   }t        | d      r| j                  d   nd}t        d	d
      5 }|j                  d| d| d| d       ddd       	 t        d      D ]  }t        j                  g ddd        t        j                  dddddd| gd       t        j                  d       t        j                  g ddd       	 t!        j"                  t         j$                  j'                  | j(                        d        t        | j(                  d!      5 }|j                  |       ddd       d|d"S # 1 sw Y   xY w# t        $ r Y w xY w# t        $ r}dd| dcY d}~S d}~ww xY w# 1 sw Y   HxY w# t        $ r Y Vw xY w)#zTippt /effort <level> in die Aria-Session.

        Bewusst OHNE C-c (anders als _send_telegram): ein laufender Turn darf
        durch einen Effort-Wechsel nicht abgebrochen werden. C-u raeumt nur
        die Eingabezeile.
        r"   FzUngueltiges Level: rO   rT   rU   r   rV   rY   rZ   r[   r\   z effort=rX   N   rh   Trd   re   r_   r`   rj   ra   rb   z/effort rk   rl   g333333?rm   ro   )exist_okr   r   )r   lowerr   r   rz   r{   r   rU   r   r   r%   ranger   r   timesleepr   makedirsr   dirnamer   )r6   r   r   r   r   _r   s          r   _set_effortzHandler._set_effortg  s    "##%++-***.A%,KLL	",,.4B+249I+J$$Q'PSB6< 9!B4r"XeWB789		F 1X kIZ^hijkNNFKtVxPUwEWXbcdJJsONNGX\fgh	KK(>(>?$Od,,c2 a U++)9 9 		  	F.@,DEE	F  		st   AF& F$F& -A9F6 'AG  ;GG  F#F& &	F32F36	G?GGGGG   	G,+G,c                     	 t        |      5 }|j                         j                         cddd       S # 1 sw Y   yxY w# t        $ r Y yw xY w)z<Read a single-line state file, return stripped string or ''.Nr"   )r   r   r   r%   )r6   r   r   s      r   
_safe_readzHandler._safe_read  sF    	d (qvvx~~'( ( ( 		s(   A 5	A >A A 	AAc                 .   dg d}	 t        j                  g dddd      }g }t        j                         }|j                  j	                         D ]g  }|j                  d      }t        |      dk  r$|d   |d	   |d
   }	}}|j                  d      sE|dd  }
|
sMt        j                  j                  t        |
      }d}	 t        j                  ddd|ddgddd
      }|j                  j                  d      D cg c]  }|j                         s| }}dj                  |dd        }d|v }	 t        |	      }t        dt        ||z
              }|dk  r| d}n$|dk  r	|dz   d}n|dk  r	|dz   d}n|dz   d}|j!                  |
|| j#                  t        j                  j                  |d            | j#                  t        j                  j                  |d            | j#                  t        j                  j                  |d             | j#                  t        j                  j                  |d!            xs d"d|||| j#                  t        j                  j                  |d#            | j#                  t        j                  j                  |d$            |d%       j d|d&<   ||d'<   |S c c}w # t        $ r Y w xY w# t        $ r d}d}Y ew xY w# t        $ r}t%        |      |d(<   Y d }~|S d }~ww xY w))NF)rP   workers)r_   list-sessionsz-Fz6#{session_name}	#{session_created}	#{session_activity}Tr   rf   rr   rg   	r   r   rd   aria-rk   r"   r_   capture-panera   -p-JrX   iu   ❯<   s  mQ hd   —rolescopeworkdirmodeassistbranchpid)namesessionr   r   r   r   alivereadylast_activity_tslast_activity_agor   r   pane_snapshotrP   r   error)r   r   r   stdout
splitlinesr$   r   r#   r   r   joinWORKERS_STATEr   r%   intr   appendr   r   )r6   resultrr   now_tsr)   partssession_name
created_tsactivity_tsworker_name	state_dirpaneprllinesr   act_tsago_sagor   s                        r   _list_workerszHandler._list_workers  s   "-:	%S#$A
 GYY[F++- .

4(u:>8=a%(ERSH+j#..w7*12."GGLLD	#|T4P'+$B )+		(=K1QKEK99U34[1D 	 -F3v#78ErzE7!3}A->s$q.A#(E>"2! 4# *5*6*.//"'',,yRX:Y*Z*.//"'',,yRY:Z*[*.//"'',,yR[:\*]*.//"'',,yRX:Y*Z*f^f*.*/*0*-*.//"'',,yRZ:[*\*.//"'',,yRW:X*Y*.  A.^  F4L 'F9 E L   ! CF(  	%!!fF7O	%sz   B>K2 <KK	K	K2K2 7AK	D>K2 	K	KK2 KK2 K/+K2 .K//K2 2	L;LLc                 z   |rt         j                  |      sdddS d| }	 t        j                  ddd|dd	gd
d
d      }|j                  j                  d      D cg c]  }|j                         s| }}d
||dj                  |dd        dS c c}w # t        $ r}dt        |      dcY d }~S d }~ww xY w)NFinvalid namerO   r   r_   r   ra   r   r   Tr   r   rX   i8)rP   r   r   r   )
_WORKER_NAME_REmatchr   r   r   r$   r   r   r%   r   )r6   r   r   r   r   r   r   s          r   _worker_panezHandler._worker_pane  s    ?006N;;$.	4wdC#$A !"t 4B1	QBEB$))TYZ^Z_T`Jabb C 	4CF33	4s5   <B B5B9B B 	B:"B5/B:5B:c                    dd l }|rt        j                  |      sdddS d| }|dk(  rdddS |sdddS |j                  d	d
|      }t	        |      dkD  r|d d }	 t        j                         j                         dz   }t        dd      5 }|j                  d| d| dt	        |       d|d d d	       d d d        	 t        j                  ddd|dgdd       t        j                  ddd|dgdd       t        j                  dddd||gd       t        j                  ddd|d gdd       d!diS # 1 sw Y   xY w# t        $ r Y w xY w# t        $ r}dd"| dcY d }~S d }~ww xY w)#Nr   Fr   rO   r   rb   z+HARD CONSTRAINT: cannot target aria sessionz
empty textrR   r"   rS   rT   z/var/log/aria-worker-send.logrZ   r[   r\   r]   r^   rW   rX   r_   r`   ra   rc   Trd   re   ri   rj   rk   rl   rn   rP   ro   )r0   r   r   r   r   r   rz   r{   r   r   r%   r   r   )r6   r   rr   _rer   r   r   r   s           r   _worker_sendzHandler._worker_send  s   ?006N;;$.f,YZZL99ww7TBt9t;D	",,.4B5s; Rq!B4r$uSYKycrRPQR	FNNFKwF*.;NNFKwF*.;NNFKtWdK#$&NNFKwH*.;$<R R 		  	F.@,DEE	FsI   1E +D;:E A7E ;E E 	EE	E1E,&E1,E1c                 f   dd l }|rt        j                  |      sdddS |j                  d      sdddS |r|j                  d|      sdddS 	 dd l}|j                  d	d
|||gt        dd      t        j                         ddd| dS # t        $ r}dt        |      dcY d }~S d }~ww xY w)Nr   Fr   rO   z/root/zproject must be under /root/z^[a-zA-Z0-9][a-zA-Z0-9_-]*$zinvalid rolebashz!/root/aria/scripts/aria-worker.shz/var/log/aria-worker-spawn.logrZ   )r   stderrTr   )rP   spawningr   )
r0   r   r   r#   r   Popenr   STDOUTr%   r   )r6   r   projectr   r   _spr   s          r   _worker_startzHandler._worker_start  s    ?006N;;!!(+,JKK399%CTJN;;		4$II<dGTR<cB!((  
 DuTF^LL 	4CF33	4s   =B 	B0B+%B0+B0c                 F   |rt         j                  |      sdddS d| }|dk(  rdddS d}	 t        j                  j	                  |      ret        j                  d||gd	d	d
      }|j                  dk(  }|j                  |j                  z   j                         j                  d      d   }n.t        j                  ddd|gd	d	d      }|j                  dk(  }d}||dS # t
        j                  $ r dddcY S t        $ r}dt        |      dcY d }~S d }~ww xY w)NFr   rO   r   rb   zHARD CONSTRAINTz&/root/aria/scripts/aria-worker-stop.shr   T   r   r   rX   r_   zkill-sessionra   rk   zkilled (no stop script)zstop timeout (30s))r   r   r   r   isfiler   r   
returncoder   r   r   r$   TimeoutExpiredr%   r   )r6   r   r   stop_scriptr   rP   msgr   s           r   _worker_stopzHandler._worker_stop  s+   ?006N;;$.f,=>>>	4ww~~k*NN[$/#'dB \\Q&xx!((*11399$?CNN^T7;#'dA \\Q&/--(( 	B,@AA 	4CF33	4s$   B6C& &D  D DD D c                 V   |rt         j                  |      sdddS |dvrdddS t        j                  j	                  t
        |d      }	 t        |d      5 }|j                  |dz          d d d        d	||d
S # 1 sw Y   xY w# t        $ r}dt        |      dcY d }~S d }~ww xY w)NFr   rO   )r   autoyolou?   mode must be assist|auto|yolo — to stop, use /api/worker-stopr   r   rX   T)rP   r   r   )
r   r   r   r   r   r   r   r   r%   r   )r6   r   r   	mode_pathr   r   s         r   _worker_set_modezHandler._worker_set_mode.  s    ?006N;;11`b bGGLLf=		4i% %t$%d;;% %  	4CF33	4s6   B A;-B ;B B 	B(B#B(#B(c                 $   | j                   j                  d      d   }| j                         }|dk(  rp| j                         }| j	                  d       | j                  dd       | j                          | j                  j                  |d   j                                y | j                  |j                  dd	            s| j                  d
did       y |dk(  rs| j	                  d       | j                  dd       | j                  dd       | j                          | j                  j                  t        j                                y |dk(  r | j                  | j                                y |dk(  r | j                  | j                                y |dk(  r | j                  | j                                y |dk(  r | j                  | j!                                y |dk(  r3|j                  dd      }| j                  | j#                  |             y |dk(  r | j                  | j%                                y |dk(  r | j                  | j'                                y |dk(  r | j                  | j)                                y |dk(  r | j                  | j+                                y |dk(  r | j                  | j-                                y |dk(  r | j                  | j/                                y |dk(  rt0        j3                  dt4              }| j	                  d       | j                  dd       | j                  dd       | j                  d d!       | j                  d"d#       | j                          | j                  j                  |j                                y |d$k(  r | j                  | j7                                y |d%k(  r | j                  | j9                                y |d&k(  r | j                  | j;                                y |d'k(  r | j                  | j=                                y |d(k(  r | j                  | j?                                y |d)k(  r=tA        |j                  d*d+            }| j                  | jC                  |,             y |d-k(  r | j                  | jE                                y |d.k(  r5|j                  d/d	      }| j                  | jG                  |d01             y |d2k(  r | j                  | jI                                y |d3k(  r | j                  | jK                                y |d4k(  r | j                  | jM                                y |d5k(  r3|j                  d6d	      }| j                  | jO                  |             y |d7k(  r | j                  | jQ                                y |d8k(  r | j                  | jS                                y |d9k(  r| j	                  d       | j                  dd       | j                  dd:       | j                          | j                  j                  tT        j3                  dt4              j                                y |d;k(  r | j                  | jW                                y |d<k(  rA|j                  d=d	      jY                         }	| j                  | j[                  |	             y | j	                  d>       | j                          y )?Nr:   r   z/health   rB   z
text/plainstatustr"   r   Zugriff verweigert  /ztext/html; charset=utf-8zCache-Controlz#no-store, no-cache, must-revalidatez/statusz/systemz	/servicesz/logsz	/activityr   rb   z/usagez/contextz/session-heroz/erkenntnissez/linearz	/calendarz/planer	__TOKEN__Pragmazno-cacheExpiresr/   
/api/tasksz/n8nz/socialz/newsz/capabilitiesz/tool-failureslimit50r  z/cost-overviewz/brain-searchqru   top_kz/brain-treez/brain-files-treez/brain-graphz/brain-filer   z/quick-skills/api/effortz/teamzno-storez/api/workersz/api/worker-paner     ).r   r$   r@   _get_statusrD   rE   rF   rG   r   rJ   r7   r'   rM   	HTML_PAGE_get_system_get_services	_get_logs_get_activity
_get_usage_get_context_get_session_hero_get_erkenntnisse_get_linear_get_calendarPLANER_PAGEr   r5   
_get_tasks_get_n8n_get_social	_get_news_get_capabilitiesr   _get_tool_failures_get_cost_overview_get_brain_search_get_brain_tree_get_brain_files_tree_get_brain_graph_get_brain_file_content_get_quick_skillsr   	TEAM_PAGEr   r   r   )
r6   r   r<   r  r   htmlr  r  relr   s
             r   do_GETzHandler.do_GET<  s   yys#A&##%9%%'Fs#^\:JJVH-4467

3 34OOW&:;SA3;s#^-GH_.STJJY--/0YOOD,,./YOOD,,./[ OOD..01W_OODNN,-[ !::i8LOOD..|<=XOODOO-.ZOOD--/0_$OOD2245_$OOD2245YOOD,,./[ OOD..01Y&&{E:Ds#^-GH_.STXz2Y,JJT[[]+\!OODOO-.V^OODMMO,YOOD,,./W_OODNN,-_$OOD2245%%

7D12EOOD33%3@A%%OOD3356_$

3#AOOD221A2>?]"OOD0023((OOD6689^#OOD1134]"**VR(COOD88=>_$OOD2245]"OOD,,./W_s#^-GH_j9JJY..{EBIIKL^#OOD..01''::fb)//1DOOD--d34 s#r8   c                 
   | j                   j                  d      d   }| j                         }t        | j                  j                  dd            }|r.t        j                  | j                  j                  |            ni }|j                  dd      xs |j                  dd      }| j                  |      s| j                  ddd	d
       y |dk(  r | j                  | j                                y |dk(  r | j                  | j                                y |dk(  r!| j                  | j                  |             y |dk(  r1| j                  | j                  |j                  dd                   y |dk(  rX|j                  dd      j!                         }|s| j                  ddd	       y | j                  | j#                  |             y |dk(  r1| j                  | j%                  |j                  dd                   y |dk(  rX|j                  dd      j!                         }|s| j                  ddd	       y | j                  | j'                  |             y |dk(  rX|j                  dd      j!                         }|s| j                  ddd	       y | j                  | j)                  |             y |dk(  ry|j                  dd      j!                         }|j                  dd      j!                         }	|s| j                  ddd	       y | j                  | j+                  ||	             y |dk(  rb|j                  dd      j!                         }
|j                  dd      j!                         }| j                  | j-                  |
|             y |dk(  r|j                  dd      j!                         }
|j                  d d      j!                         }|j                  d!d      j!                         }| j                  | j/                  |
||             y |d"k(  rA|j                  dd      j!                         }
| j                  | j1                  |
             y |d#k(  rb|j                  dd      j!                         }
|j                  d$d      j!                         }| j                  | j3                  |
|             y | j5                  d%       | j7                          y )&Nr:   r   zContent-Lengthr  r"   r   Fr  rO   r	  z/restartz	/git-pullr  z/api/tasks/deleteidz/api/send-messagerr   z	Kein Textr  r   z/api/quick-skill-triggerskill_idzKein skill_idz/api/linear/completeinternal_idzKein internal_idz/api/linear/start
display_idz/api/worker-sendr   z/api/worker-startr   r   z/api/worker-stopz/api/worker-moder   r  )r   r$   r@   r   rt   r'   rH   loadsrfiler   r7   rM   _restart_session	_git_pull
_save_task_delete_taskr   r   r   _trigger_quick_skill_complete_linear_issue_start_linear_issuer   r   r   r  rD   rF   )r6   r   r<   lengthbodyr   rr   r7  r8  r9  r   r   r   r   s                 r   do_POSTzHandler.do_POST  s   yys#A&##%T\\%%&6:;6<tzz$**//&12" 

3#<txx'<&OO55IJCP:OOD1134[ OODNN,-\!OODOOD12((OOD--dhhtR.@AB((88FB'--/Du EFOOD//56]"OOD,,TXXgr-BCD//xx
B/557Hu IJOOD55h?@++((="5;;=Ku9K LMOOD77DE((((="5;;=K,399;Ju9K LMOOD44[*MN''88FB'--/D88FB'--/DOOD--dD9:((hhvr*002Dhhy"-335Ghhvr*002DOOD..tWdCD''88FB'--/DOOD--d34''88FB'--/D88FB'--/DOOD11$=>s#r8   c                 \   ddddddddddddddddddddd	dd
d}	 | j                         }| j                         }|j                  d      dk(  rdnd|d<   |j                  dd      |d<   |j                  dd      |d<   |j                  dd      |j                  dd      |j                  dd      |j                  dd      |j                  dd      d|d<   |j                  dd      |d<   |j                  dd
      |d<   |d   xs dj                         }d}d|v rd}n8d|v rd}n1d|v rd}n*d |v rd!}n#d"|v rd#}nd$|v rd%}nd&|v rd'}nd(|v rd)}n|r|d   }|r+|d   d*   r#|d   d*   d+k\  rd,n|d   d*   d-z   d.}| d/| d0}||d1<   |d   dk(  r\|d   d2vrU|d   }|j	                  d3      s|j	                  d4      rd5|d<   n(|j	                  d6      r	 t        |d7d8       d9k\  rd5|d<   d:d;d<d=j                  |d   d      |d><   	 dd7l}|j                  d?      }	|	r*t        |	t        j                  j                  @      }
d|dA<   dBD ]  }	 t        j                  j                  t        j                  j                  |dC            sCt        j                   dDdE|dFdGgdHdHdIJ      }|j"                  xs dj%                         }|s||dA<   ||dF<   t        j                   dDdE|dKdLdMgdHdHdIJ      }t        |j"                  xs dNj%                         xs d      }t        j                   dDdE|ddOgdHdHdIJ      }t'        dP |j"                  xs dj)                         D              }g }|r|j+                  | dQ       |r|j+                  | dR       |rdSj                  |      ndT|dU<   t        j                   dDdE|dVdWdXgdHdHdIJ      }t'        dY |j"                  xs dj)                         D              |dZ   d[<    n 	 d\}t        j                  j-                  |      rkdd7l}|j1                  |d]^      }|j3                         }	 |j5                  d_       |j7                         }|r|\  }}}|xs dj%                         j9                  d`da      d   d7db }d}	 ddclm} |j=                  |dd      }t        dt        t?        j>                         |jA                         z
              }|dbk  r| de}n$|dfk  r	|dbz   d6}n|dgk  r	|dfz   d3}n|dgz   d4}|dhk(  rdindj}| dS| |d|dk<   t?        jB                  dl      }|j5                  dm|dnz   f       |j7                         d   xs d|dZ   do<   |jE                          |S # t        $ r Y w xY w# t        $ r Y lw xY w# t        $ r Y zw xY w# t        $ r Y w xY w# |jE                          w xY w# t        $ r Y |S w xY w# t        $ r} tG        |       |dp<   Y d7} ~ |S d7} ~ ww xY w)qzOne-shot endpoint for the Hero-Session-Card. Composes data from
        existing helpers (status, context) plus git/cwd/today-stats. Designed
        to be the single call the Home tab makes for the hero card.offliner   r"           r   )percenttokensr   inputoutputz/root)rr   r   )commits
tasks_done	telegramsrV   )statestate_labeluptime
model_fullmodel_shortcontextcwdr   branch_statelast_actiontoday
session_idupdated_agor  onlinerP  rR  modelrS  rI  rJ  
max_tokensrK  rL  rU  rZ  r[  zfable-5zFable 5zmythos-5zMythos 5zopus-4-8zOpus 4.8zopus-4-7zOpus 4.7zopus-4-6zOpus 4.6z
sonnet-4-6z
Sonnet 4.6z
sonnet-4-5z
Sonnet 4.5z	haiku-4-5z	Haiku 4.5r   @B 1Mi  r>   z []rT  )rV   r"   r   r   idler   Nr   rk   u   Aria · liveu   Aria · idleu   Aria · offline)r\  rb  rG  rQ  z$/root/.claude/projects/-root/*.jsonlr(   rV  )/root/aria/brainz/root/aria-v4z/root/projekte/Kadi-v2z.gitgit-Cr   z--show-currentTr   r   zrev-listz--countz
@{u}..HEADr/   z--porcelainc              3   B   K   | ]  }|j                         sd   ywr   Nr   .0r)   s     r   	<genexpr>z,Handler._get_session_hero.<locals>.<genexpr>g  s     ]dPTPZPZP\]   z aheadz modu    · cleanrW  logz--since=midnightz	--onelinec              3   B   K   | ]  }|j                         sd   ywrh  ri  rj  s     r   rl  z,Handler._get_session_hero.<locals>.<genexpr>q  s     1obfblblbn!1orm  rY  rM  z/root/.aria-chat.dbrd   rl   zVSELECT direction, message_text, created_at FROM aria_chat_log ORDER BY id DESC LIMIT 1rX   r   r   )r   z%Y-%m-%d %H:%M:%Sr   r   r   outu   → Kaisu   ← KaisrX  %Y-%m-%dz8SELECT COUNT(*) FROM aria_chat_log WHERE created_at >= ?z	 00:00:00rO  r   )$r  r  r'   r   endswithr   
ValueErrorglobr   r   r   getmtimer%   isdirr   r   r   r   r   sumr   r   r   sqlite3connectcursorexecutefetchoner$   r   strptimer   	timestampstrftimecloser   )!r6   rq  stctxr   labelsizer   ru  filesnewestrepobrr   ahaheaddirtybitscmdb_pathry  conncurrow	directionmsg_text
created_atrr   _dtts_dtr   	today_strr   s!                                    r   r  zHandler._get_session_hero  s   
  #&!AUVW$&r2!"!!D
K	"!!#B##%C')vvh'78'C8CLFF8R0CM # 4C779c2''(A.ww|Q/!,''(A.C	N !$b 9C!$!<C \"(b//1AEA~yuq*%q*%q*%q*%"LE"LE!;5C-EY."9~e4	At#i.Y^J_aeJeIffgGh 'D6+!&C 7|x'C,>i,O-(<<$S(9#)CL\\#&s3Bx=A-+1CL )(+" c#g,&	 			"HI BGG,<,<=F ")CJ
 X 77==dF)CD #dHFV(W7;$PQSB iio2446F! !%CJ$*CM#dJ	S_(`7;$PQSB!1c 8 8 : ?a@E#dHm(T7;$PQSB]ryyB.J.J.L]]EDdkkUG6*:;dkkUG4.9?C&++d*;C'#dE3E{S'+$B /21ob@\@\@^1o.oCL+9B'/77>>'*""??7A?>D++-C%J "lln>A;Ix$,N#9#9#;#A#A$#J1#Mcr#RD"'C	% D(+ZAT(U(+As499;AR3R/S(T#(2:wa[s%*T\ERK=9J3%*U]Ud]O1:MC/4~.>a,@c 3<u2DJ*E=BG4v:NWZ1[C. %)MM*$=	V&46 58LLN14E4JG[1

 
C & "  B ! 8 $- % $% 

  
  	"q6CL
	"s   GX	 !V% 7X	 AV5 X	 =WX	 >WX	 D9WX	 A	W9  AW$ 6A?W 5AW$ W9 %	V2.X	 1V22X	 5	W>X	 WX	 	WX	 WX	 	W!W$  W!!W$ $W66W9 9	XX	 XX	 		X+X&&X+c                    	 t        j                  ddgddd      }d|j                  v rt        j                  g dddd      }|j                  dk(  rd	}	 d
|j                  v r0|j                  j	                  d
      d   j	                  d      d   nd	}|rt        j                  |j                         d      }t        j                         |z
  }t        t        |j                               dz  d      \  }}|dz  }	|dz  }|	dkD  r|	 d| d| d}n|dkD  r	| d| d}n| d}d|dS dddS dd	dS # t        $ r Y w xY w# t        $ r}
dt        |
      dcY d }
~
S d }
~
ww xY w)Nr_   r   Tr   r   rb   )pgrepz-xclauder   r"   	(created r   )%a %b %d %H:%M:%S %Yr   r   d h r   r\  )r  rR  rG  zSession ohne Prozess)r  r   )r   r   r   r   r$   r   r~  r   nowdivmodr   total_secondsr%   r   )r6   r   r2rR  r   createddeltar   r   r   r   s              r   r  zHandler._get_status  s   	:8TXbcdA!^^$=dY]ghi==A%FP[_`_g_gPg{ ;A > D DS I! Lmo &.&7&7G]&^G$,LLNW$<E#)#e.A.A.C*D*JB#ODAq !RA !BA 1u,-3b2aS):!"Q,-3b1,-3a '/&AA"+7MNN'266	 % 
  	:'#a&99	:sO   AE CD< -E 2E 7E <	EE EE 	E,E'!E,'E,c           	         ddddddddd}	 t        d      5 }|j                         j                         }t        |d         }t	        j
                         xs d}|d    d|d    d|d    |d<   t        d	t        ||z  d	z              |d
<   d d d        i }t        d      5 }|D ]D  }|j                         }t        |      dk\  s"t        |d         ||d   j                  d      <   F 	 d d d        |j                  dd      }	|j                  dd      }
|	|
z
  }|	dz   d|d<   |dz   d|d<   |	rt        ||	z  d	z        nd|d<   t	        j                  d      }|j                  |j                  z  }|j                  |j                  z  }||z
  }|dz   d|d<   |dz   d|d<   |rt        ||z  d	z        nd|d<   |S # 1 sw Y   =xY w# 1 sw Y   xY w# t        $ r Y |S w xY w)Nr   rV   )cpu_pctram_used	ram_totalram_pct	disk_used
disk_totaldisk_pctloadz/proc/loadavgrd   z / r   r  d   r  z/proc/meminfo:MemTotalMemAvailable   Mr  r  r  r
  i   @Gr  r  r  )r   r   r$   floatr   	cpu_countminr   r   rstripr'   statvfsf_blocksf_frsizef_bavailr%   )r6   r   r   
load_partsload1ncpumeminfor)   r   totalavailusedr  r  	disk_freer  s                   r   r  zHandler._get_system  sD   Cca"#1cS!	o& F!VVX^^-
jm,||~*$.qM?#jm_C
ST!Wv$'S1C-D$Ey!F Go& F! FD JJLE5zQ8;E!Ha 45FF
 KK
A.EKK2E5=D%*d]O1"5F;$(DL>!3F:;@D5L3$6 7aF9 CBr{{2Jbkk1I"Y.I&0W&=%>a#@F< %.7%;$<A">F;FPY%;c%A!BVWF: AF FF F*  		sH   G% A9GG% &$G%G1CG% GG% G"G% %	G21G2c                 n   g d}	 t        j                  g dddd      }d|j                  v }g }|D ]S  \  }}	 t        j                  dd	|gddd
      }|j                  j	                         dk(  }|j                  ||d       U |j                  d|d       d}	 t        d      }	|	r|dd lm}
 |
j                  dd|	i      }|
j                  |d      5 }t        j                  |j                               j                  dg       }t        d |D              }d d d        |j                  d|d       d}	 t!        d      5 }t        j"                  |      }d d d        j                  di       }t        d |j%                         D              }|j                  d|d       dd l}d}	 g }dD ]"  }|j)                  |j'                  |             $ |rA|j+                          t,        j.                  j1                  |d   j3                  d            }|j                  dt5        |      |xs d d!       |S # t        $ r d}Y %w xY w# t        $ r d}Y w xY w# 1 sw Y   KxY w# t        $ r d}Y \w xY w# 1 sw Y   *xY w# t        $ r d}Y 	w xY w# t        $ r Y w xY w)"N))zaria-watchdog.timerzWatchdog Timer)zaria-api.servicez	API (n8n))zaria-control.servicezControl Panel)zaria-health.servicezHealth Check)zaria-git-sync.timerzGit Sync (5min))dockerpsz--filterzname=n8nz--formatz{{.Status}}Trk   r   UpF	systemctlz	is-activer   active)r   r  zn8n (Docker)N8N_API_KEYr   z2http://localhost:5678/api/v1/workflows?active=trueX-N8N-API-KEYrt   rl   rK   c              3   h   K   | ]*  }d |j                  dd      xs dj                         v  , yw)zerror handlerr   r"   N)r'   r   )rk  _wfs     r   rl  z(Handler._get_services.<locals>.<genexpr>  s6      / (CGGFB,?,E2+L+L+NN/s   02zn8n Error Handlerz/root/.claude/settings.jsonhooksc              3      K   | ]r  }t        |t              r|ng D ]Y  }d t        |      j                         v xs8 dt        |      j                         v xs dt        |      j                         v  [ t yw)supabasechat_logztelegram-logN)r   listr   r   )rk  _event_entries_hs      r   rl  z(Handler._get_services.<locals>.<genexpr>!  s~       #-7-M>SU	  c"gmmo- 5s2w}}1N 5!SW]]_455s   A8A:zSupabase Chat-Logr"   )z/root/aria/n8n/backups/*/z%/root/aria-v3/n8n-backup/workflows/*/r   r
  z
n8n Backupzkein Backup)r   r  info)r   r   r   r%   r   r   r*   r   rw   rx   ry   rH   r:  r   r'   anyr   r  valuesru  extendsortr   r   basenamer  bool)r6   servicesr   n8n_runningr   unitr  r  error_handler_activeapi_key_ur_req_resp_wfschat_log_ok_sf	_settings_hooks_glbackup_date_candidates_patterns                         r   r  zHandler._get_services  s   
	 S#$A !((*K # 		=KD%NN +t4#'dA )X5 MM5F;<		= 	~EF  %	).G,{{H,g6 #  [[q[1 U::ejjl377CD+. /#'/ ,( 	2>RST 	 34 + IIcN	+]]7B/F  '-mmo K 	2kJK 		Kb 7""388H#567  " gg..{2/E/Ec/JK 	|tK7H*;m= 	> E  	 K	      	)#( 	)+ +  	 K	   		s   )I 9I ;I7 AI*!I7  J J	!:J 6A,J( III'&I'*I4/I7 7JJ	JJ J%$J%(	J43J4c                    i }	 t        j                  g dddd      }|j                  j                         xs d|d<   	 t        j                  g dddd      }|j                  j                  d	      D cg c]  }|j                         s| }}d	j                  |d
d        xs d|d<   |S # t        $ r d|d<   Y w xY wc c}w # t        $ r	 d|d<   Y |S w xY w)N)tailz-15z/var/log/aria-watchdog.logTr   r   LeerwatchdogzNicht lesbar)r_   r   ra   rb   r   rX   ir   Keine aktive Session)r   r   r   r   r%   r$   r   )r6   logsr   r   r   s        r   r  zHandler._get_logs<  s    	.=#$A  !xx~~/96D
		5<#$A
 !"t 4B1	QBEB"iicd4>DO   	.-D	. C 	54DO	5s;   <B2 8C 9CCC 2CCC CCc                    dg dd}	 t        j                  ddd|dgddd	
      }|j                  j                  d      D cg c]  }|j	                         s| }}g }|D ]  }|j	                         }|j                  d      s|j                  d      r$|j                  |j                  d      dd       Y|j                  d      s|j                  d      s|d|v rdnd}	|j                  |j                  d      |	d        ||d<   g d}
t        fd|
D              |d<   dj                  |dd        xs d|d<   |S c c}w # t        $ r	 d|d<   Y |S w xY w)Nr"   F)r   tasksthinkingr_   r   ra   r   Tr   r   rX   u   ✔u   ✓u   ✔✓ done)rr   r  u   ◼u   ◻r  pendingu   ◼◻ r  )r  	PutteringFlibbertigibbeting
ZigzaggingRunningc              3   &   K   | ]  }|v  
 y wr4    )rk  indrL  s     r   rl  z(Handler._get_activity.<locals>.<genexpr>n  s     $RsSF]$Rs   r  ir  r   r  )r   r   r   r$   r   r#   r   lstripr  r   r%   )r6   r   r   r   r   r   r  r)   strippedr  thinking_indicatorsrL  s              @r   r  zHandler._get_activityV  sz   "%@	7|TB#$A XXF &T 2@1aggiQ@E@ E Y::<&&u-1D1DU1KLL(//)*DPV!WX((/83F3Fu3M).():X	FLL(//)*DPV!WXY $F7O #k!$$R>Q$R!RF: !%		%+ 6 @&F9 - A(  	7 6F9	7s1   =E EE BE "A(E E E#"E#c                    ddddddddddd
}	 dd l }d}d}d }t        j                  j                  |      rnt	        |      j                         j                         }|D ]B  }|j                  | d	| d
      }|st        |t        j                  j                        } n |sUg }	|D ]%  }|	j                  |j                  | d             ' |	s|S t        |	t        j                  j                        }d }
d}t	        |d      5 }|j                  dd       |j                         }t        |d      }|j                  ||z
         |j                         j                  dd      }d d d        t        j                               D ]}  }|j                         }|s	 t!        j"                  |      }|j'                  d      xs i }|j'                  d      }|sUt)        |t*              sf|}
|j'                  d      xs d} n ddddddddd}|j-                         D ]  \  }}||v s||d<    n ||d<   |
rt/        |
j'                  d      xs d      }t/        |
j'                  d      xs d      }t/        |
j'                  d      xs d      }t/        |
j'                  d      xs d      }||z   |z   }||d<   ||d<   ||d<   ||d <   ||d!<   t        d"t1        ||d   z  d#z  d$            |d%<   t        j                  j3                  |      j5                  d&d      d d' |d(<   t/        t7        j6                         t        j                  j                  |      z
        }|d)k  r
| d*|d+<   |S |d,k  r|d)z   d-|d+<   |S |d,z   d.|d+<   	 |S # 1 sw Y   4xY w# t$        $ r Y *w xY w# t$        $ r}t9        |      |d/<   Y d }~|S d }~ww xY w)0Nr   r_  rH  r"   rV   )
rJ  r^  rI  rZ  r]  r[  rK  
cache_readcache_createrL  z /root/.claude/current_session_id)z/root/.claude/projects/-z/root/.claude/projects/-rootr
  z*.jsonlrc  z/*.jsonlrbrd   i   utf-8r   )errorsrQ   usager]  i@ )zclaude-fable-5zclaude-mythos-5zclaude-opus-4-8zclaude-opus-4-7zclaude-opus-4-6zclaude-sonnet-4-6zclaude-sonnet-4-5zclaude-haiku-4-5r^  input_tokenscache_creation_input_tokenscache_read_input_tokensoutput_tokensrJ  rK  r  r  rL        Y@r  r   rI  z.jsonlru   rZ  r   r   r[  r   r   r   r   )ru  r   r   r   r   r   r   r   rv  r  seektellr  decodereversedr   rH   r:  r%   r'   r   dictitemsr   roundr  r   r   r   )r6   r   ru  r  session_dirsr  sidr   candsr  
last_usage
model_namer   r  chunkr  r)   r   u	model_maxr(   mxinpcccrrq  rJ  r  r   s                                r   r  zHandler._get_contextv  s    W "RAqAOS	%7FWLFww~~f%6l'')//1% A II1SE&9:E!$U0@0@!A	
 % <ALLaS>!:;<!MU(8(89 JJfd# Bqq!vvxD),te|$vvxwyAB !!23 zz|

4(A eeI&,"GGG$At,!"J!$!1!7RJ" #,#,#,#,#,%.%.$+	I %??, R*$+-F<( )F7O*..8=A>(EFK!L(ABGaH*..9>Q?rB#)x "%w)+~&')|$#&x $'uVf\>R5RUX5XZ[/\$]y!#%77#3#3F#;#C#CHb#QRTST#UF< 		bgg&6&6v&>>?Erz+0'}%  +0B;-q(9}%
  ,1D=/(;}% {B B ! ^  	%!!fF7O	%s   A.O  >AO  5O  A$N#17O  )N0>(O  'O  8?O  8E	O  O  O  #N-(O  0	N=9O  <N==O   	O"	OO"c                 r   ddddddddddd
}	 t        j                  ddgddd      }d|j                  v rd	|j                  v r|j                  j                  d	      d
   j                  d      d   }t	        j
                  |j                         d      }t	        j                         |z
  }t        t        |j                               dz  d      \  }}|dz  }|dz  }|dkD  r| d| d| d|d<   n|dkD  r| d| d|d<   n| d|d<   t        j                  g dddd      }|j                  j                         xs d|d<   t        j                  g dddd      }|j                  j                         rd}	d}
|j                  j                         j                  d      D ]C  }|j                         }t        |      dk\  s"|	t        |d         z  }	|
t        |d         z  }
E |	dd|d<   |
dz   d|d <   	 dd l}|j                  d!      }|j                         }|r|d"    d#|d$<   |j!                  d
%      }|d&    d|d'<   t#        |d(         |d)<   |j!                  d*%      }|d&    d|d+<   t#        |d(         |d,<   |j!                         }|d&    d|d-<   |S # t$        $ r 	 t        j                  g d.ddd/      }|j                  j                         rt'        j(                  |j                  j                               }|j+                  d0      r|d0   d"    d#|d$<   |d1   d&    d|d'<   t#        |d1   d(         |d)<   |d2   d&    d|d+<   t#        |d2   d(         |d,<   |d3   d&    d|d-<   n# t$        $ r Y nw xY wY |S w xY w# t$        $ r Y |S w xY w)4NrV   )
duration	processes
claude_cpu
claude_ramtoday_tokenstoday_sessionsweek_tokensweek_sessionscurrent_tokenstotal_tokensr_   r   Tr   r   rb   r  r   r  r   r  r   r   r  r  r   r&  )r  -cr  r/   r'  )r  rf  r  z-oz%cpu,%mem,rssz--no-headersrH  rX   rd   z.1f%r(  r  r  r)  z
aria-usagetotal_tokens_kr>   r.  daystotal_tokens_mr*  sessionsr+     r,  r-  r/  )python3r0  a$  
import json, sys
sys.path.insert(0, '/root/aria/scripts')
exec(open('/root/aria/scripts/aria-usage.py').read())
d = {
    "current": get_current_session_usage(),
    "today": aggregate_usage(days=1),
    "week": aggregate_usage(days=7),
    "total": aggregate_usage()
}
print(json.dumps(d))

   currentrY  weekr  )r   r   r   r$   r   r~  r   r  r  r   r  r   r  	importlibr	   get_current_session_usageaggregate_usager   r%   rH   r:  r'   )r6   r   r   r   r  r  r   r   r   	total_cpu	total_ramr)   r<  	usage_modr:  rY  r;  all_times                     r   r  zHandler._get_usage  sN   !3VY"%S#&#sTV	8TXbcdA!kQXX&={3A6<<SA!D"++EKKM;QR 0c%"5"5"78B>C1GFq5,-3b2aS):F:&U,-3b1F:&,-3aF:& 8TXbcdA"#((.."2"9cF; M#$A xx~~		HHNN,2248 3D JJLE5zQ!U58_4	!Sq]2		3
 +4C':|$*3t*;)<A'>|$. %33LA	#==?29:J2K1LA/NF+,!11q19,12B,C+DA)F~&+.uZ/@+A'( 00a08+/0@+A*B!(D}%*-d:.>*?'$446,45E,F+Gq)I~&@ ?  " (,$A xx~~' JJqxx~~'7855+:;I,GW:X9YYZ7[F#3445gJ?O4P3QQR1S~.36qz*7M3N/034V9=M3N2Oq0Q}-25ai
6K2L/45gJ?O4P3QQR1S~.   ?:  		s\   GN) 8N) 
B)J5 5	N&?CNN&	NN&NN&"N) %N&&N) )	N65N6c                 H   g g g g d}	 dd l m} ddlm}m} t	        d      }|sd|d<   |S t        j                  ddt         d	t         d
i      }|j                  d|j                         |dd      }|j                  |d      5 }t        j                  |j                               }	d d d        	j                  di       j                  di       j                  dg       }
|	j                  di       j                  di       j                  dg       }|
|z   } |j                  |j                        j!                         }t#        dd      D ch c]  }t         d|  }}|D ]  }|d   }||v r|d   }|d   d   }|j                  d      }d}|r+|dk7  r&	  |j$                  |      j!                         }||k  }|r
|dk7  rd| nd }||||j                  d!d       d"}|r|d#   j)                  |       |d$k(  r|d%   j)                  |       |d&v r|d'   j)                  |       |dk(  s|d(   j)                  |        |d(   d d) |d(<   |S # 1 sw Y   xY wc c}w # t&        $ r Y w xY w# t&        $ r}t+        |      |d<   Y d }~|S d }~ww xY w)*N)in_progresstodooverduer  r   )r   timezoneLINEAR_API_KEYLINEAR_API_KEY fehltr   queryz-
{
  open: issues(filter: {team: {key: {eq: "z"}}, state: {type: {nin: ["completed","canceled"]}}}, first: 100, orderBy: updatedAt) {
    nodes { id identifier title state { name type } dueDate updatedAt }
  }
  recentDone: issues(filter: {team: {key: {eq: "z"}}, state: {type: {eq: "completed"}}}, first: 15, orderBy: updatedAt) {
    nodes { id identifier title state { name type } dueDate updatedAt }
  }
}https://api.linear.app/graphqlrC   AuthorizationrB   rs   ru   rl   rK   r   nodes
recentDoner   rk   rV   
identifiertitlerP  typedueDateF	completedz
Deadline: r"   r6  )r6  rQ  extrar8  rF  startedrD  )	unstartedbacklogrE  r  r9  )r   rw   r   rG  r*   rH   rI   r,   rx   rJ   ry   r:  r   r'   r  utcdater   fromisoformatr%   r   r   )r6   r   urr   rG  
linear_keyrJ  r}   resprK   open_issuesdone_issuesissuesrY  iskip_idsissueiidrQ  stypedue
is_overduedue_daterU  itemr   s                             r   r!  zHandler._get_linear0  s   !#RBKC	%'3"#34J"8wJJ ...=-> ?4 5D3D E*   E **0\\^*4FXY  C
 C+ /tzz$))+./ ((62.2262>BB7BOK((62.22<DHHRTUK ;.F HLL.335E ;@1+FQ?+1QC0FHF 0L)(?ggv.ii	*"
5K/#98#9#9##>#C#C#E%-%5
 /2e{6J*SE*PR!EERWR[R[\`bdRef9%,,T2i'=)006666N))$/k)6N))$/50: $F^CR0F6N Y/ / G % $  	%!!fF7O	%sm   !I? AI? 	$I-B)I? I+(8I? !%I0A4I? ;!I? I(#I? 0	I<9I? ;I<<I? ?	J!JJ!c                    	 dd l m} t        d      }|sdddS t        j                  ddt
         di      }|j                  d	|j                         |d
d      }|j                  |d      5 }t        j                  |j                               }d d d        j                  di       j                  di       j                  dg       }	|	sdddS |	d   d   }
t        j                  dd| d|
 di      }|j                  d	|j                         |d
d      }|j                  |d      5 }t        j                  |j                               }d d d        j                  di       j                  di       j                  dd      }|r| j                  d| d| d       ||rddS ddS # 1 sw Y   $xY w# 1 sw Y   mxY w# t        $ r}dt        |      dcY d }~S d }~ww xY w)Nr   rH  FrI  rO   rJ  ,{ workflowStates(filter: {team: {key: {eq: "z4"}}, type: {eq: "started"}}) { nodes { id name } } }rK  rC   rL  rs   ru   rl   rK   workflowStatesrN  z In-Progress-State nicht gefundenr6  mutation { issueUpdate(id: "", input: {stateId: ""}) { success } }issueUpdatesuccessu   ▶ Task gestartet: z-
Schreib mir was ich tun soll oder sag "mach r    	GestartetUpdate fehlgeschlagen)r   rw   r*   rH   rI   r,   rx   rJ   ry   r:  r   r'   r   r%   r   )r6   r8  r9  r\  r]  q1r}   r^  statesrN  started_state_idmutationreq2resp2r   rr  r   s                    r   rB  zHandler._start_linear_issuex  s1   	4'"#34J#0FGGW(XYhXi  jh  'i  j  kB**=BIIK7ASe%f  hCC+ 1tDIIK01JJvr*../?DHHRTUE#0RSS$Qx~zz7.KK=Xno  oA  AV  -W  #X  YH::>X__EV8BTf&g  iDD!, 2EJJL12jj,00CGG	SXYG##&::,Fu  wA  vB  BD  %E  F!WkbbJabb1 12 2  	4CF33	4sb   G AG 1$F7A G AG 4$GAG 3G 7G<G G	G 	G1G,&G1,G1c                    	 dd l m} t        d      }|sdddS t        j                  ddt
         di      }|j                  d	|j                         |d
d      }|j                  |d      5 }t        j                  |j                               }d d d        j                  di       j                  di       j                  dg       }|sdddS |d   d   }	t        j                  dd| d|	 di      }
|j                  d	|
j                         |d
d      }|j                  |d      5 }t        j                  |j                               }d d d        j                  di       j                  di       j                  dd      }||rddS ddS # 1 sw Y   
xY w# 1 sw Y   SxY w# t        $ r}dt        |      dcY d }~S d }~ww xY w)Nr   rH  FrI  rO   rJ  rl  z6"}}, type: {eq: "completed"}}) { nodes { id name } } }rK  rC   rL  rs   ru   rl   rK   rm  rN  zDone-State nicht gefundenr6  rn  ro  rp  rq  rr  Donert  )r   rw   r*   rH   rI   r,   rx   rJ   ry   r:  r   r'   r%   r   )r6   r8  r\  r]  ru  r}   r^  rv  rN  done_state_idrx  ry  rz  r   rr  r   s                   r   rA  zHandler._complete_linear_issue  s   	4'"#34J#0FGG W(XYhXi  jj  'k  l  mB**=BIIK7ASe%f  hCC+ 1tDIIK01JJvr*../?DHHRTUE#0KLL!!HTNM zz7.KK=Xno|n}  ~S  -T  #U  VH::>X__EV8BTf&g  iDD!, 2EJJL12jj,00CGG	SXYG!f]]E\]]1 12 2  	4CF33	4sb   F6 AF6 1$FA F6 AF6 4$F*A F6 F6 F'"F6 *F3/F6 6	G?GGGc           	         g dd}	 dd l m} ddlm}m}m} t        d      5 }t        j                  |      }d d d        j                  dd      } |j                  |j                        }	|	j                  d      }
|
dz   }|
d	z   }|
|d
<   d| d| d}|j                  |dd| i      }|j                  |d      5 }t        j                  |j                               }d d d        j                  dg       D ]  }|j                  di       }|j                  d|j                  d
d            }d|v rA	  |j                   |j#                  dd            }| |d      z   }|j                  d      }nd}|d   j'                  |j                  d d!      ||j                  d"d      d#        	 |S # 1 sw Y   xY w# 1 sw Y   xY w# t$        $ r |dd }Y fw xY w# t$        $ r}t)        |      |d$<   Y d }~|S d }~ww xY w)%Nr"   )eventsrZ  r   )r   rG  r   z/root/.google_credentials.jsonaccess_tokenrr  z
T00:00:00Zz
T23:59:59ZrZ  zHhttps://www.googleapis.com/calendar/v3/calendars/primary/events?timeMin=z	&timeMax=z$&singleEvents=true&orderBy=startTimerM  Bearer r  ru   rl   r  r   dateTimeTrT   z+00:00rd   hoursz%H:%M      ganztagsr  summaryz
Kein Titellocation)rQ  r   r  r   )r   rw   r   rG  r   r   rH   r  r'   r  rY  r  rx   ry   r:  r   r[  r   r%   r   r   )r6   r   r\  r   rG  r   r   credsr  r  rY  time_mintime_maxurlr}   r^  rK   rj  r   	start_strdtdt_localtime_fmtr   s                           r   r"  zHandler._get_calendar  s&   +*	%'>>67 %1		!% 99^R8L(,,x||,CLL,E|+H|+H"F6N$:Yxj8\^  **S?gl^<T*U*VCC+ /tzz$))+./ "- "-!IIj%))FB2GH	)#43X33I4E4Ec84TU#%	(:#:#+#4#4W#=  *Hx ''!XXi>$ $R 8) * O% %/ / % 4#,R#34  	%!!fF7O	%sk   G( F;B
G( $G&AG( <?G;=G( ;G G( GG( G%"G( $G%%G( (	H
1HH
c                     	 t         j                  j                  t              r2t	        t        d      5 }dt        j                  |      icd d d        S dg iS # 1 sw Y   xY w# t        $ r Y dg iS w xY w)Nr
  encodingr  )r   r   r   PLANER_TASKS_FILEr   rH   r  r%   )r6   r   s     r   r$  zHandler._get_tasks  sv    	ww~~/0+g> 3!#TYYq\23 3 }	3 3 	}	s(   4A' A	A' A$ A' '	A65A6c                 
   	 g }t         j                  j                  t              r/t	        t        d      5 }t        j                  |      }d d d        t        fdt        |      D        d      }|dk\  r||<   n|j                         t	        t        dd      5 }t        j                  ||dd	       d d d        d
diS # 1 sw Y   {xY w# 1 sw Y   xY w# t        $ r}dt        |      dcY d }~S d }~ww xY w)Nr
  r  c              3   p   K   | ]-  \  }}|j                  d       j                  d       k(  s*| / yw)r6  Nr'   )rk  rb  r  tasks      r   rl  z%Handler._save_task.<locals>.<genexpr>  s-     Xda!%%+RV:WXs   +66r   r   r   Frd   ensure_asciiindentrP   TrP   r   )r   r   r   r  r   rH   r  next	enumerater   dumpr%   r   )r6   r  r  r   idxr   s    `    r   r>  zHandler._save_task  s    	2Eww~~/0+g> )! IIaLE) Xi&6XZ\]Cax!c
T"'w? B1		%qAB$<) )B B  	2#a&11	2sM   6C! C	AC! #C=C! 	CC! CC! !	D*C=7D=Dc                    	 g }t         j                  j                  t              r/t	        t        d      5 }t        j                  |      }d d d        |D cg c]  }|j                  d      |k7  s| }}t	        t        dd      5 }t        j                  ||dd       d d d        dd	iS # 1 sw Y   exY wc c}w # 1 sw Y   xY w# t        $ r}dt        |      d
cY d }~S d }~ww xY w)Nr
  r  r6  r   Frd   r  rP   Tr  )r   r   r   r  r   rH   r  r'   r  r%   r   )r6   task_idr  r   r  r   s         r   r?  zHandler._delete_task  s    
	2Eww~~/0+g> )! IIaLE) %@1t)?Q@E@'w? B1		%qAB$<) )@B B  	2#a&11	2s^   6C B2C B>4B>8C C&C 2B;7C CC 	C0C+%C0+C0c           	          dg i}	 d}t        d      5 }|D ]f  }|j                         j                  d      }|j                  d      s4|j	                  dd      d   j                         j                  d      } n d d d        d	d lm} |j                  d
d|i      }|j                  |d      5 }t        j                  |j                               }d d d        j                  dg       D ]J  }	|d   j                  |	j                  dd      |	j                  dd      |	j                  dd      d       L 	 |S # 1 sw Y   xY w# 1 sw Y   wxY w# t        $ r}
t        |
      |d<   Y d }
~
|S d }
~
ww xY w)N	workflowsr"   r   zexport zN8N_API_KEY=r   r   z"'r   z6https://n8n-vlfw.${VPS_HOST}/api/v1/workflows?limit=50r  r  ru   rl   rK   r   r  Fr6  )r   r  r6  r   )r   r   r  r#   r$   r   rw   rx   ry   rH   r:  r   r'   r   r%   r   )r6   r   r  r   r)   r\  r}   r^  rK   wfr   s              r   r%  zHandler._get_n8n	  sy   r"	%G'( A D::<..y9D~6"&**S!"4Q"7"="="?"E"Ee"L	 (**H('2  C C+ /tzz$))+./hhvr* {#**FF62. ffXu5&&r*,  + / /  	%!!fF7O	%sM   E 6E
5E?6E 5$EA'E EE EE 	E=$E88E=c                    g g d}	 dd l }d}t        |d      5 }|j                         }d d d        j                  d      D ]  }|j	                  d      sd|vsd	|vsd
|vs$|j                  d      D cg c]#  }|j                         s|j                         % }}t        |      dk\  sp|d   |d   |d   |d   |d   f\  }	}
}}}t        |      dkD  r|d   nd}|	s|sd|v sd|v s	d|v sd|v s|	|
|||d}d|v sd|v r|d   j                  |       d|v sd|v s|d   j                  |        |d   dd  |d<   |d   d d |d<   |S # 1 sw Y   /xY wc c}w # t        $ r}t        |      |d<   Y d }~|S d }~ww xY w)N)postedupcomingr   z=/root/aria/brain/01-Projekte/Social-Media-Content-Kalender.mdr
  r  rX   |---DatumStatusrk   r   rd   r      r"   u   ✅u   ⏳Gepostet
Ausstehend)rZ  r   platformcontentr  r  r  r   )
r0   r   r   r$   r#   r   r   r   r%   r   )r6   r   r0   cal_pathr   r  r)   ccellsrZ  time_strr  post_numpost_contentr  entryr   s                    r   r&  zHandler._get_social$  s   B/	%VHh1 #Q&&(#  d+ A??3'E,='QUBUZbjnZn04

3M11779QWWYMEM5zQKPQR8UZ[\U]_def_ginopiqsxyzs{K{Hh(L-0Z!^qH%6/Uf_XbflXlp|  AG  qG-18QYfr  E  %FE$*2F &x 0 7 7 >!&&LF4J &z 2 9 9% @A  &h/4F8!'
!3BQ!7F: )# #
 N  	%!!fF7O	%su   E* E-E* E* E* #E* (E* ;E%E%#E* 43E* (E* +E* <-E* *,E* E"E* *	F3FFc                 V   dg i}	 ddl m} ddlmc m} ddg}g }|D ]  \  }}	 |j                  |ddi      }|j                  |d	
      5 }	|	j                         }
ddd       |j                  
      }ddi}|j                  d      xs% |j                  d|      xs |j                  d      }|dd D ]  }|j                  d      xs |j                  d      xs dj                         }|j                  d      xs( |j                  d      xs |j                  d      xs d}d}|r|d   j                         r|dd }nf|j                         }t        |      dk\  rHddddddddd d!d"d#d$}|j                  |d%   |d%         }|d&   j!                  d%      }|d'    d(| d(| }|j#                  |||d)         |j'                  d* d+,       |dd- |d<   |S # 1 sw Y   xY w# t$        $ r Y w xY w# t$        $ r}t)        |      |d.<   Y d}~|S d}~ww xY w)/u   External AI-news headlines from RSS feeds (TechCrunch AI, HuggingFace).
        Aria's own findings live in `_get_erkenntnisse()` instead — Kais wants
        the two streams visually separate (10.05.2026).r  r   N)zTechCrunch AIz=https://techcrunch.com/category/artificial-intelligence/feed/)HuggingFacez$https://huggingface.co/blog/feed.xmlz
User-AgentzMozilla/5.0r     rl   atomzhttp://www.w3.org/2005/Atomz.//itemz.//atom:entryz.//entryr  rQ  z"{http://www.w3.org/2005/Atom}titler"   pubDate	publishedz&{http://www.w3.org/2005/Atom}publishedr9  010203040506070809101112)JanFebMarAprMayJunJulAugSepOctNovDecrd   r   r   rV   )sourcerQ  rZ  c                 &    | j                  dd      S NrZ  r"   r  xs    r   <lambda>z#Handler._get_news.<locals>.<lambda>k  s    vr): r8   Tr(   reverseru   r   )r   rw   xml.etree.ElementTreeetreeElementTreerx   ry   r   
fromstringfindallfindtextr   isdigitr$   r   r'   zfillr   r%   r  r   )r6   r   r\  ETfeeds	all_itemsr  r  r}   r^  xml_datarootnsr  rj  rQ  pubdate_strr   monthsmodayr   s                          r   r'  zHandler._get_news?  s    2*	%'.. cIE I$ **S<2O*PCC3 /t#'99;/==2D "?@B LL3tt||OUW7Xt\`\h\his\tE %bq	 _!%w!7 "^$(MM2V$W"^[]_d_d_f #}}Y7  `"&--"< `"&--0X"Y `]_  $&"1v~~/+.s8(+		#&u:?48t$UY`dko48t$UY`dko.qF)/E!HeAh)GB*/(..*;C27(1RD#/GH!((FUT\)]^%_< NN:DNI'mF7O ?/ /0 ! 
  	%!!fF7O	%sS   H (G6G)E*G6H )G3	.G66	H?H HH 	H(H##H(c                 t   dg i}g }	 ddl }d}t        j                  j                  |      rg }t        j                  |      D ]u  }|j                  d      r|j                  d      r&t        j                  j                  ||      }|j                  |t        j                  j                  |      f       w |j                  d d	       |dd
 D ]p  \  }}	 t        |dd      5 }	|	j                  d      }
ddd       d}d}d}
j                  d      r8|
j                  dd      }|dkD  r |
d| }|j                  d      D ]  }|j                         }|j                  d      rB|j                  dd      d   j                         j                  d      j                  d      }f|j                  d      r$|j                  dd      d   j                         }|j                  d      s|j                  d      s|j                  dd      d   j                         j                  d      j                  d      } |
|dz   d }
|s>|j!                  d|
|j"                        }|r|j%                  d      j                         }|sB|j'                  dt        j                  j)                  |            }|r|j%                  d      }|sS|
j                  d      D ]?  }|j                         }|s|j                  d      r(|j                  d      r:|dd  } n |r|j                  d!|||d"       s t-        |      dk  rd#}t        j                  j                  |      rg }t        j                  |      D ]v  }|j                  d$      s|j                  d      s't        j                  j                  ||      }|j                  |t        j                  j                  |      f       x |j                  d% d	       |dd D ]  \  }}	 t        |dd      5 }	|	j                  d&      }
ddd       t        j                  j)                  |      }|j'                  d'|      }|r|j%                  d      nd}
}|j                  d      r|j                  dd      }|dkD  r||dz   d }d}|j                  d      D ]-  }|j                         }|s|j                  d      r(|dd  } n |r|j                  d(d)| ||d"        |j                  d* d	       |dd
 |d<   |d   sd!d+d,dd"g|d<   |S # 1 sw Y   _xY w# t*        $ r Y w xY w# 1 sw Y   .xY w# t*        $ r Y mw xY w# t*        $ r}t/        |      |d-<   Y d}~|S d}~ww xY w).u  Aria's own findings, in plain words. Sources (priority order):
        1. AI-Radar funde — `02-Wissen/ai-radar/funde/YYYY-MM-DD-*.md` with frontmatter
        2. Recent Daily-Logs — last 3 days from `06-Daily/`
        Items have shape: {source, title, summary, date}.
        r  r   Nz)/root/aria/brain/02-Wissen/ai-radar/funde.mdr   c                     | d   S Nr   r  r  s    r   r  z+Handler._get_erkenntnisse.<locals>.<lambda>  s
    1 r8   Tr  r  r   r
  r     r"   r  
---r   rX   ztitle:r  r   r    r!   zdate:zsummary:description:r  z
^#\s+(.+)$z(\d{4}-\d{2}-\d{2})#   zAI-Radar)r  rQ  r  rZ  z/root/aria/brain/06-Dailyz
Daily-Log-c                     | d   S r  r  r  s    r   r  z+Handler._get_erkenntnisse.<locals>.<lambda>  s
    1Q4 r8   i   zDaily-Log-(\d{4}-\d{2}-\d{2})z	Daily-LogzWas lief am c                 &    | j                  dd      S r  r  r  s    r   r  z+Handler._get_erkenntnisse.<locals>.<lambda>  s    QUU62%6 r8   u=   Noch keine Erkenntnisse — KAR-38 Sprint B baut die PipelinezWSobald AI-Radar Funde erzeugt (X / Reddit / GitHub-Trending / HF), erscheinen sie hier.r   )r0   r   r   rw  listdirrs  r#   r   r   rv  r  r   r   findr$   r   search	MULTILINEgroupr   r  r%   r   r   )r6   r   r  r0   	funde_dirr  r   r   _mtimer   r  rQ  rZ  r  endheadrawr)   r   mname	daily_dirdaily_filesbaserD  r  r   s                             r   r   zHandler._get_erkenntnisseq  sk    2f	%CIww}}Y'JJy1 AD==/4??33G 77<<	48DLL$(8(8(>!?@	A
 

~t
<$)"1I *!LD&)!!$g> 3!&'ffTlG3 "!"$"--e4"),,w":C"Qw'.q~+/::d+; !fC+.99;D'+x'@04

30B10E0K0K0M0S0STW0X0^0^_b0c)-)A/3zz#q/A!/D/J/J/L)-)DXfHg26**S!2DQ2G2M2M2O2U2UVY2Z2`2`ad2e!f +2#'(*;$ "		-",, OA ()
(8(8(:#$&HH-CRWWEUEUVZE[$\E$',{{1~&(/d(; *'+zz|#'0DT__]bMc.24CjG$)	*
 !!LL*4).+2(,	* G*!X 5zA~7	77==+"$K "

9 5 O??<8T]]5=Q#%77<<	4#@D'..bgg6F6Ft6L/MNO  $$$F(3BQ %f%!%dC'!B 7a*+&&,7#%77#3#3D#9D$&HH-Mt$TE5:5;;q>D#*D#u5&*ii&;#&7+/a>D&(G(,

4(8 *+/::<#+H4G4G4L.6tnG$)	*
  ' %.9/;D6-B/6,0	." !#'%8 JJ6JE#BQiF7O'?(\x	$ #w u3 3N % ! !7 7.  ) %$%  	%!!fF7O	%s   CV U(,U>DU(
C8U(U(U(' U(AV 'V 9A2V ,V:U8B(V5V#V*/V U%	 U((	U51V 4U55V 8V	=V	VV VV 	V7V22V7c                 6   	 t        |dd      5 }|j                  d      }d d d        j                  d      sy|j                  dd      }|d	k  ry|d| j	                  d
      D ]  }|j                         }|j                  d      s%|t        d      d  j                         }|j                  d      r|j                  d      r|dd }|j                  d      r|j                  d      r|dd }|d d c S  y# 1 sw Y   xY w# t        $ r Y yw xY w)Nr   r
  r  r  r  r"   r  r   r   rX   r  r    r   r   r!      )	r   r   r#   r  r$   r   r   rs  r%   )r6   r   r   r  r  r	  r)   vals           r   _parse_skill_frontmatterz Handler._parse_skill_frontmatter  s   	dC'2 $avvd|$??5)))GQ'CaxAc{((. %yy{??>2s>234::<C~~c*s||C/@!!Bi~~c*s||C/@!!Bit9$% !$ $"  		s?   D D D D 8D A0D >D  D	D 	DDc                     	 t        d      5 }t        j                  |      }ddd       ddS # 1 sw Y   xY w# t        $ r}dt	        |      g dcY d}~S d}~ww xY w)zFLiest /root/.aria-quick-skills.json. Kais kann Datei direkt editieren.z/root/.aria-quick-skills.jsonNT)rP   skillsF)rP   r   r  )r   rH   r  r%   r   )r6   r   r  r   s       r   r0  zHandler._get_quick_skills  sd    	@56 &!1&&11& &  	@#a&B??	@s+   < 0< 9< 	AAAAc                     dddS )NFzQuick Skills sind deaktiviert.rO   )r   rH   r  r  r'   r   r   r   r   r   r   r   r   	_sh_quoter   r   r   r   DEVNULLsetpgrpr%   r   )r6   r7  r   r  skillpromptr  ack_textr   log_dirr   log_filewrapperwrapper_filer   s    `             r   r@  zHandler._trigger_quick_skill  s     (HIIr8   c                 2    d|j                  dd      z   dz   S )z/Bash single-quote-escape for arbitrary strings.r!   z'\'')r   )r6   r   s     r   r  zHandler._sh_quoteI  s    QYYsG,,s22r8   c                 0   d|g dd}|rt        |      dk  rd|d<   |S 	 ddl}d|j                  vr|j                  j                  dd       ddl}|j                         }	 |j                  |||	      }|D cg c]  }t        |j                  d
d            t        |j                  dd            t        |j                  d      xs |j                  d      xs d      dd t        |j                  dd            d c}|d<   d|d<   |S c c}w # t        $ r}		 |j                  ||      }
|
D cg c]m  }t        |j                  d
d            t        |j                  dd            t        |j                  d      xs |j                  d      xs d      dd ddo nc c}w c}|d<   d|d<   n)# t        $ r}d|	 d| d|d<   |cY d}~cY d}	~	S d}~ww xY wY d}	~	d}	~	ww xY w# t        $ r}t        |      |d<   Y d}~|S d}~ww xY w)zSemantic search auf aria-brain via aria-brain-mcp (Ollama+sqlite-vec).
        Eingefuehrt 2026-05-04. Direkt-Import des Servers, kein stdio.
        FN)rP   rJ  hitsr   rd   zquery zu kurzr   r   z/root/aria/brain-mcpr  r   r"   rQ  rr   snippeti,  score)r   rQ  r"  r#  r!  r  r)   rH  grep_vault_contentfallbackzsemantic_search failed (z), grep_vault failed (r  TrP   )r   sysr   insertserverget_connsearch_vault_semanticr   r'   r  r%   r$  )r6   rJ  r  r   _sysbrain_serverr  r!  r   e_inner	hits_grepe_grepr   s                r   r+  zHandler._get_brain_searchM  s/    rDIE
Q-F7OM%	%%TYY6		  $:;)((*D"#99$U9S ""  !$AEE&"$5 6!$QUU7B%7!8#&quuV}'Ni8H'NB#OPTQT#U!&quuWa'8!9	"v4  F4L ;"  "" , ? ?U ? SI "+&  %(fb(9$:%(w);%<'*155=+OAEE&M+OR'PQURU'V%(	& &F6N *>F:&  "(@	I_`f_ggh&iF7O!MM" '"$  	%!!fF7O	%s   AG3 #D ;BDD G3 D 	G0"F=9A2F,+F=<G+=	G#GG#G+G0G3 G##G+&G3 +G00G3 3	H<HHc                 N   dg ddd}	 d}t         j                  j                  |      sd|d<   |S g }d}t        t        j                  |            D ]  }|j                  d      rt         j                  j                  ||      }t         j                  j                  |      rud}t        j                  |      D ]@  \  }}	}
|	D cg c]  }|j                  d      r| c}|	dd |t        d	 |
D              z  }B |j                  ||d
d       ||z  }t         j                  j                  |      s|j                  d      s|j                  |ddd       |dz  } ||d<   ||d<   d|d<   |S c c}w # t        $ r}t        |      |d<   Y d}~|S d}~ww xY w)zAListe der Top-Level-Folders im aria-brain Vault mit Datei-Counts.Fr   rd  )rP   folderstotal_files
vault_rootVault nicht gefundenr   .Nc              3   D   K   | ]  }|j                  d       sd  yw)r  r   N)rs  )rk  r   s     r   rl  z*Handler._get_brain_tree.<locals>.<genexpr>  s     $K1E9JQ$Ks     folder)r   md_countrR  r  r   filer1  r2  TrP   )r   r   rw  sortedr   r#   r   walkrx  r   r   rs  r%   r   )r6   r   vaultentriesr  r   r   countroot_dirdirsr  r   r   s                r   r,  zHandler._get_brain_tree}  s   "QN`a	%&E77=='"8wGErzz%01 ??3'ww||E4077==&E13 L-$.2"L!,,s:K1"LQ$K$K!KKL NNDeX#VWUNEWW^^D)dmmE.BNNDa#PQQJE !(F9$)F=!F4L  #M  	%!!fF7O	%sB   'F BF 
E=!E=%AF =F ,F =F 	F$FF$c                 H   ddl }d}dg g d}	 t        j                  j                  |      sd|d<   |S g }t        j                  |      D ]  \  }}}|D cg c]  }|j                  d      r| c}|dd |D ]u  }	|	j                         j                  d	      s#t        j                  j                  ||	      }
t        j                  j                  |
|      }|j                  ||
f       w  i }|D ]O  \  }}t        j                  j                  |      j                  dd
      d   j                         }||vsK|||<   Q |j                  d      }g }g }|D ]i  \  }}
d|v r|j                  d      d   nd}t        j                  j                  |      j                  dd
      d   }|j                  |||d       	 t        |
ddd      5 }|j!                         }ddd       t%               }|j'                        D ]  }|j)                  d
      j+                         }|j                         j                  d	      r#|j                  dd
      d   j                         n|j                         }|j-                  |      }|s||k7  s||f|vs|j                  ||d       |j/                  ||f        l ||d<   ||d<   d|d<   |S c c}w # 1 sw Y   xY w# t"        $ r Y w xY w# t"        $ r}t1        |      |d<   Y d}~|S d}~ww xY w)zBuild a graph from Wikilinks across all .md files in /root/aria/brain.
        Nodes = files (path, title, group=top-level folder).
        Edges = wikilink references (source -> target if target file exists).
        r   Nrd  F)rP   rN  edgesr4  r   r5  r  r   z#\[\[([^\]\|\n]+)(?:\|[^\]\n]+)?\]\]r
  ROOT)r6  r  r  r   r
  r   r  r  )r  r   rN  rB  TrP   )r0   r   r   rw  r;  r#   r   rs  r   relpathr   r  rsplitcompiler$   r   r   r%   setfinditerr  r   r'   addr   )r6   r   r<  r   r  r?  r@  fnamesr   fnfullr3  base_lookupr   r  wikilink_rerN  rB  toprQ  r   rr   seenr   r   norm
target_relr   s                               r   r.  zHandler._get_brain_graph  s   
 	"R8*	%77=='"8wE*,''%. 2&$&*D!,,s2C1DQ  2Bxxz**51!ww||Hb9 ggoodE:c4[1	22 K ,Qww'',33C;A>DDF{*(+K%, ++&LMKEE" 4	T+.#:ciinQ'6((-44S!<Q?C%#FGdC')L (PQ vvx( u$--d3 4AWWQZ--/F?E||~?V?VW\?]6==a0399;cicococqD!,!6J!jC&7S*<MUY<Yz%JK#z!2344" $F7O#F7OF4L K E(( (    	%!!fF7O	%s   %K? "K? K-K1*K? B%K? BK? 	K/K#)K/1B"K? K? K? !;K? K? #K,	(K//	K<8K? ;K<<K? ?	L!LL!c                     dddd}	 t         j                  j                        sd|d<   |S fd       |d<   d	|d
<   |S # t        $ r}t	        |      |d<   Y d}~|S d}~ww xY w)zRecursive tree of all .md files under /root/aria/brain.
        Returns nested folders with files. Skips hidden dirs and non-md files.
        rd  FN)rP   treer3  r4  r   c                    t         j                  j                  |       xs ddg d}	 t        t        j                  |             }|D ]  }|j                  d      rt         j                  j                  | |      }t         j                  j                  |      r# |      }|d   sb|d   j                  |       wt         j                  j                  |      s|j                         j                  d      st         j                  j                  |      }|d   j                  |d|d        |S # t
        $ r |cY S w xY w)	Nz
aria-brainr7  )r   rR  childrenr5  rW  r  r9  )r   rR  r   )r   r   r  r:  r   r%   r#   r   rw  r   r   r   rs  rE  )	r   noder=  r   rM  r   r3  buildr<  s	          r   rY  z,Handler._get_brain_files_tree.<locals>.build  s    " 0 0 6 F,PXfhi $RZZ%56G $ 
]Ds+ 77<<d3Dww}}T*#Dkz? ,33C8-$**,2G2G2N ggoodE:Z(//vWZ0[\
]  !  K s   D= =E
ErU  TrP   )r   r   rw  r%   r   )r6   r   r   rY  r<  s      @@r   r-  zHandler._get_brain_files_tree  s     #t5A	%77=='"8w& #5\F6NF4L   	%!!fF7O	%s   %A
 A
 
	A,A''A,c                 ~   ddl m} 	 |j                  |xs d      }t        j
                  j                  d      }d|d}	 |rd|j                  d      v rd	|d
<   |S t        j
                  j                  t        j
                  j                  ||            }|j                  |t        j                  z         s||k7  rd|d
<   |S |j                         j                  d      sd|d
<   |S t        j
                  j                  |      sd|d
<   |S t        |ddd      5 }|j                         }ddd       t	        j                   |      }d}	}
|j                  d      r3|j#                  dd      }|dk7  r|d| }	||dz   d j%                  d      }
d|d<   |
|d<   |	|d<   |j&                  |d<   |j(                  |d<   t        j
                  j+                  |      j-                  dd       d   |d!<   |S # t        $ r Y w xY w# 1 sw Y   xY w# t        $ r}t/        |      |d
<   Y d}~|S d}~ww xY w)"zRead a markdown file from /root/aria/brain. Path is relative to vault root.
        Strict whitelist: no path traversal, must end .md.
        r   Nr"   rd  F)rP   r   z..r
  zinvalid pathr   zoutside vaultr  znot a markdown filezfile not foundr   r
  r   rD  z---
z
---
r  r   rk   rX   TrP   r  frontmatterr  mtimer5  r   rQ  )urllib.parseparseunquoter%   r   r   realpathr$   r   r#   sepr   rs  r   r   r   statr  r  st_sizest_mtimer  rF  r   )r6   rel_path_upr<  r   r   r   r  rb  frontmatter_rawrD  r  r   s                r   r/  zHandler._get_brain_file_content  s&    	#	{{8>r2H   !34x0$	%tx~~c'::"0wWW%%bggll5(&CDF$$URVV^45"1w<<>**51"7w77>>&)"2wfcGIF #!&&(#776?D !OD!!'*ll9a0"9&-anO"378,33D9DF4L $F9$3F=!!\\F6N"mmF7O gg..v6==c1EaHF7O S  		$# #*  	%!!fF7O	%s[   G> H A*H 	%H /%H H $H5CH >	H
HHH 	H<#H77H<c           
         ddg g dd}	 t        d      }t        d      }|r|sd|d<   |S ddl}ddl}| d	t        |       }|j                  j                  ||d
| d      }|j                  j                  |d      5 }t        j                  |j                               }	|	d| |d<   ddd       ddl
m
}
m}m}  |
j                  |j                         |d      z
  j                         }| d|j                   j#                  |       }|j                  j                  ||d
| d      }|j                  j                  |d      5 }t        j                  |j                               }t%        |      |d<   i }|D ],  }|j'                  dd      }|j'                  |d      dz   ||<   . t)        |j+                         D cg c]
  \  }}||d c}}d       dd |d<   ddd       d|d<   |S # 1 sw Y   TxY wc c}}w # 1 sw Y   #xY w# t,        $ r}t/        |      |d<   Y d}~|S d}~ww xY w)zRead recent tool failures from Supabase aria_tool_failures.
        Eingefuehrt 2026-05-04 nach Architektur-Audit + PostToolUseFailure-Hook.
        Fr   N)rP   	total_24hby_toolrecentr   SUPABASE_URLSUPABASE_SERVICE_ROLE_KEYzSupabase credentials missingr   zi/rest/v1/aria_tool_failures?select=created_at,tool_name,error_excerpt,is_mcp&order=created_at.desc&limit=r  )apikeyrM  r  rk   rl   rk  )r   r   rG  r   r  z</rest/v1/aria_tool_failures?select=tool_name&created_at=gte.ri  	tool_nameunknownr   )ro  r>  c                     | d    S )Nr>  r  r  s    r   r  z,Handler._get_tool_failures.<locals>.<lambda>U  s    1W:+ r8   rc     rj  TrP   )r*   r   r]  r   rw   rx   ry   rH   r:  r   r   r   rG  r  rY  r{   r^  quoter   r'   r:  r  r%   r   )r6   r  r   supa_urlsupa_keyr   r  r}   r^  rowsr   r   rG  cutoffagg_qagg_reqrows_24hcountsr   nr>   r?   r   s                          r   r)  zHandler._get_tool_failures(  s   
 
&	% 0H !<=H8"@w!*  F  GJ  KP  GQ  FR  SA..(("#*8*!55( C ''Q'7 04zz$))+.#'<x 0 ?>"hll8<<0923FFQQSFj \]c]i]i]o]opv]w\xyEnn,,U"#*8*!5=, G '''; 
t::diik2&)(m{#! 5Ak95A &

1a 01 4F1I5 %+>DllnMda1q1M-% 2%y!
  F4L 30 0& N
 
  	%!!fF7O	%sa    H  AH  ,H/B&H  A=HH!H2H  HH  HHH   	I)H==Ic                 p   ddddddddddddg dg d	}	 t        d
      s|d   j                  d       |S ddlddlm}m} d0fd	}	  |d      }t        d |j                  dg       D              }t        d |j                  dg       D              }|dz  |d   d<   |dz  |d   d<   	  |d      }	|	j                  dg       }
t        |
      |d   d<   d}|
D ]|  }|j                  di       j                  dg       D ]U  }|j                  di       xs i }|j                  dd      }|j                  d d!      }|d!k(  r||z  }H|d"k(  sN||d#z  z  }W ~ t        |dz  d$      |d   d%<   	 t         |j                          |d'(      z
  j                               } |d)| d*      }|j                  dg       }t        |      |d   d+<   t        d, |D              }t        |dz  d$      |d   d-<   |S # t        $ r"}|d   j                  d|        Y d}~ud}~ww xY w# t        $ r!}|d   j                  d&|        Y d}~d}~ww xY w# t        $ r"}|d   j                  d.|        Y d}~|S d}~ww xY w# t        $ r"}|d   j                  d/|        Y d}~|S d}~ww xY w)1zAggregate Aria cost + revenue overview.
        Eingefuehrt 2026-05-04 nach Architektur-Audit. Pragmatisch: zeigt was wir aus
        verfuegbaren Quellen ziehen koennen, Anthropic-API-Cost ist N/A wegen Max-Plan.
        EURN)available_eurpending_euractive_subscriptionsactive_mrr_eurcharges_30d_countcharges_30d_succeeded_eur   	   zPauschal (nicht trackbar))subscriptions_total_eurvps_hostinger_euranthropic_max_plan_eur)zGAnthropic Max Plan ist Pauschal, keine per-call Cost-Tracking moeglich.zESubscriptions-Schaetzung 174 EUR aus 08-Finanzen/06-Gewerbe-Software.zFStripe-Daten sind live aus Stripe-API (sofern STRIPE_API_KEY gesetzt).)currencystripeestimated_costs_per_monthnotesr  STRIPE_API_KEYr  zSTRIPE_API_KEY missingr   r   c                     j                   j                  d|  dd i      }j                   j                  ||      5 }t        j                  |j                               cd d d        S # 1 sw Y   y xY w)Nzhttps://api.stripe.com/v1rM  r  r  rl   )rw   rx   ry   rH   r:  r   )r   rg   r}   r^  
stripe_keyr   s       r   
stripe_getz.Handler._get_cost_overview.<locals>.stripe_get  sw    nn,,/v6,
|.DE -  ^^++C+A 3T::diik23 3 3s   #A11A:z/balancec              3   @   K   | ]  }|j                  d d        ywamountr   Nr  rk  r  s     r   rl  z-Handler._get_cost_overview.<locals>.<genexpr>  s     O1AEE(A.O   	availablec              3   @   K   | ]  }|j                  d d        ywr  r  r  s     r   rl  z-Handler._get_cost_overview.<locals>.<genexpr>  s     L!1551-Lr  r  r  r  r  r  z	balance: z&/subscriptions?status=active&limit=100rK   r  r  planr  intervalmonthyear   rd   r  zsubscriptions: r   r3  z/charges?created[gte]=z
&limit=100r  c              3      K   | ];  }|j                  d       r(|j                  d      dk(  r|j                  dd       = yw)paidr  	succeededr  r   Nr  )rk  r  s     r   rl  z-Handler._get_cost_overview.<locals>.<genexpr>  s=       uuV}xK)G EE(A& s   AAr  z	charges: zouter: )rk   )r*   r   r   r   r   rx  r'   r%   r   r  r   r  r  )r6   r   r   r   r  br  pendr   r   subsmrrr   itr  r  r  created_afterchchargesr  r  r   s                        @@r   r*  zHandler._get_cost_overview\  s/    !%#(,"&%)-1 ,/%&*E*

 )
,>	3"#34Jx ''(@A!439z*Ok28NOOLquuY7KLL49EMx 126,x /
?GHuuVR(;>t9x !78 /C!gggr266vrB /!vvfb17R!%(A!6#'88J#@#w.6MC%/6B;.C// 6;3;5Jx !12
9 #\X\\^iR6H%H$S$S$U V"8z RS&&,8;Gx !45  $  	
 AFiRWFWYZ@[x !<= S  9x '')A3889&  ?x ''/!(=>>?   9x '')A388
 9  	38##gaSM22	3s   "J
 J
 A"H 3B!H/ !H/ 7BI 	H,
H'!J
 'H,,J
 /	I8IJ
 IJ
 	J%J<J
 JJ
 
	J5J00J5c                   % dd l }g g g g d}	 t        d      5 }t        j                  |      }d d d        j	                  di       j                         D ]y  \  }}|j                  d      d   }d|v r|j                  dd      d   nd}|D ]>  }	|	j	                  dd      }
g }t        j                  j                  |
d	      }t        j                  j                  |      rt        t        j                  |            D ]f  }t        j                  j                  ||d
      }t        j                  j                  |      sD|j                  || j                  |      d       h |	j	                  dd      xs |	j	                  dd      }|d   j                  |||	j	                  dd      |r|d d ndt!        |      |d       A | |d   j#                  d        	 d}t        j                  j                  |      rt        t        j                  |            D ]i  }t        j                  j                  ||d
      }t        j                  j                  |      sD|d   j                  || j                  |      d       k 	 i }	 t        d      5 }t        j                  |      }d d d        j	                  d      xs i j                         D ]W  \  }}t)        |t*              s||j	                  dd      |j	                  dd      |j	                  d      xs dd d dd||<   Y 	 g d }t-               }	 t        d      5 }t        j                  |      }d d d        j	                  di       j/                         D ]%  }|j1                  |j                  d      d          ' 	 |D ]  }||v sd!| }||vs|d"ddd"d||<    dd lm} dd l}|j9                         D ]  }	d }|	j	                  dd      }|	j	                  d#      d"k(  rd$}nl|j;                  d%      rE	 |j=                  |d&d'd(d)d*+      }|j?                  |d,-      5 } | j@                  d.k  }d d d        n|	j	                  d      rd$}nd5}tE        |      |	d6<    t        |j9                         d7       |d8<   	 t        |j                  d:            D ]  %t        j                  jG                  t        j                  jG                  %            }"t        j                  jI                  |"      }#|#d;k(  rd	 t        %fd<t        j                  %      D              }$|d=   j                  |#%t!        |$      |$d d> d?        	 |S # 1 sw Y   jxY w# t$        $ r}t'        |      |d<   Y d }~d }~ww xY w# t$        $ r}t'        |      |d<   Y d }~Pd }~ww xY w# 1 sw Y   5xY w# t$        $ r Y w xY w# 1 sw Y   xY w# t$        $ r Y bw xY w# 1 sw Y   xY w# t$        $ rD}t'        |      jC                         }!d/|!v sd0|!v sd1|!v rd$}nd2|!v sd3|!v sd4|!v rd$}nd5}Y d }~d }~ww xY w# t$        $ r}t'        |      |d9<   Y d }~d }~ww xY w# t$        $ r g }$Y Kw xY w# t$        $ r}t'        |      |d@<   Y d }~|S d }~ww xY w)ANr   )pluginsglobal_skillsmcp_serversinactive_skill_reposz,/root/.claude/plugins/installed_plugins.jsonr  @r   r"   installPathr  zSKILL.md)r   descriptionlastUpdatedinstalledAtversionr:   r9  )r   marketplacer  last_updatedskill_countr  c                 (    | d   j                         S Nr   r   )r=   s    r   r  z+Handler._get_capabilities.<locals>.<lambda>  s    61B r8   rc  plugins_errorz/root/.claude/skillsr  global_skills_errorz/root/.claude.json
mcpServersrR  r  commandx   z.claude.json)r   rR  r  r  r  )telegramr  vercelgithubfigmalinearr  zplugin:pluginr  ThttpPOSTs(   {"jsonrpc":"2.0","id":1,"method":"ping"}rC   z#application/json, text/event-stream)rB   Accept)methodrK   rt   rd   rl   i  401403404400405406Fr  c                 (    | d   j                         S r  r  )r   s    r   r  z+Handler._get_capabilities.<locals>.<lambda>9  s    qQWyO` r8   r  mcp_servers_errorz/root/*/.claude/skillsz.claudec              3      K   | ]C  }t         j                  j                  t         j                  j                  |            r| E y wr4   )r   r   rw  r   )rk  r   r   s     r   rl  z,Handler._get_capabilities.<locals>.<genexpr>E  s8      )77==dA)>? )s   A	Ar  2   )r  r   r  r  inactive_skill_repos_error)%ru  r   rH   r  r'   r  r$   r   r   r   rw  r:  r   r   r   r  r   r  r%   r   r   r  rH  keysrJ  r   rw   socketr  r#   rx   ry   r  r   r  r   r  )&r6   ru  r   r   rK   r(   r=  r   r  r  install_pathr  
skills_dir
skill_nameskill_mdr  r   gmcp_entriescfgr  plugin_mcp_hintsinstalled_keyspdatar>   hintr  r\  r  r  r  r}   r^  r   repo_dirr  skill_namesr   s&                                        @r   r(  zHandler._get_capabilities  s   $&	
	-DE $yy|$ $B 7 = = ? Wyy~a(69SjciiQ/2b$ E#(99]B#?LF!#lH!EJww}}Z0*0J1G*H #J')ww||J
J'WH!ww~~h7 &,6373P3PQY3Z/" !## $)99]B#?#_599]\^C_L9%,, $'2#(99Y#<=ISb(9r'*6{"(. 0 9""'B"C
	3&Aww}}Q"(A"7 J!ww||Az:FHww~~h//66$.+/+H+H+R8 J	1K./ '1))A,C'!$!6!<" C C E 	HC%c40  # # 3"wwub1$'GGI$6$<"ds#C"0(K$	  i UNHI )Q IIaLE)9b1668 8A"&&qwws|A78 ) 
>)%dV,EK/$)$,#%')&..E*	
 ($++- /iir*99X&(2!F^^F++ jjVBmBT<a2c ) d  ZZQZ7 74%)[[3%6F7 YYy)!F"F"&v,h1/4 %+;+=+=+?E`$aF=!
	:tyy)ABC 77??277??4+@Aww''19$%"( )#%::d#3) #K -.55  #&{#3)#2.	7 * a$ $6  	-&)!fF?#	-  	3,/FF()	3' '  ) )  67 7$ +!!flln C<5C<5C<%)F"c\Uc\Uc\%)F%*F+  	1*-a&F&'	1 ! %"$K%  	:36q6F/0	:ss  V$ VDV$ B"V$ +BW	 .(W	 Z W; %W.;BW;  Z X X1AX 
Z 	Z A&Z =+X5(X(8X5 AZ A>Z< 'Z*-'Z< V!V$ $	W-WW		W+W&&W+.W83W; ;	XZ XZ XX 	X%!Z $X%%Z (X2	-X55	Z>9Y=7Z =ZZ 	Z'Z""Z'*Z95Z< 8Z99Z< <	[[[c                    	 t        j                  dt        j                  j	                  d      gddd      }|j
                  dk(  rdddS |j                  r,|j                  j                         j                  d	      d
   n|j                  j                         }d|xs ddS # t         j                  $ r dddcY S t        $ r}dd| dcY d }~S d }~ww xY w)Nr   z~/aria/scripts/aria-restart.shTr  r   r   zAria neu gestartetrO   rX   r   FzRestart fehlgeschlagenzRestart Timeout (120s)zFehler: )r   r   r   r   
expanduserr   r   r   r$   r   r   r%   )r6   r   r   r   s       r   r<  zHandler._restart_sessionV  s    	<^^++,LMN#$F   A%"/CDD?E}}fmm))+11$7;RXR_R_ReReRg#0O7OPP(( 	F,DEE 	<hqcN;;	<s+   AB) AB) )CCCCCc                 P   	 t        j                  ddt        j                  j	                  d      dgddd      }|j
                  j                         xs |j                  j                         }|j                  dk(  |d	S # t        $ r}d
t        |      d	cY d }~S d }~ww xY w)Nre  rf  z~/aria/brainpullT   r   r   rO   F)r   r   r   r   r  r   r   r   r   r%   r   )r6   r   r   r   s       r   r=  zHandler._git_pullg  s    	4bgg00@&I#$A ((.."6ahhnn&6C,,!+<< 	4CF33	4s   BB 	B%B B% B%c                      y r4   r  )r6   formatargss      r   log_messagezHandler.log_messager  s    r8   N)r  )rb   )ru   )r  )6__name__
__module____qualname__r7   r@   rM   r   r   r   r   r   r   r   r   r   r   r   r  r4  rE  r  r  r  r  r  r  r  r  r!  rB  rA  r"  r$  r>  r?  r%  r&  r'  r   r  r0  r@  r  r+  r,  r.  r-  r/  r)  r*  r(  r<  r=  r  r  r8   r   r2   r2     s)   47t >M1/!,H=~4F>4&484@DAJ_B:<%NQf4@[z[zFP4:4:-^2$2660dob,@F4P3.`@3j"H0d2h[z[z<"	4r8   r2   c                       e Zd ZdZy)ThreadedHTTPServerTN)r  r  r  daemon_threadsr  r8   r   r  r  v  s    Nr8   r  __main__i'  ARIA_CONTROL_BINDz0.0.0.0zAria Control Panel: r  zLocal URL: http://localhost:z/?t=)+__doc__http.serverr   r   socketserverr   r   rH   r   r   r   r   r   r&  r   r'  r<  r	   r   r   r5   r  r*   r+   r,   r   rv   r#  r   
__import__rG  r   r1  r  r2   r  r  portr&   r'   bindprintserve_foreverr  r8   r   <module>r     sO   . : '   	   ( 
 ' ( #(
 	3 
# 34 -.7% I018S9yx +T"**+HIZ	x
}	@>f!$ f!RC  zD::>>-y9D	 av
./	(d5'
:;d|W-;;= r8   