roboticus-api 0.11.3

HTTP routes, WebSocket, auth, rate limiting, and dashboard for the Roboticus agent runtime
Documentation
  var _cachedConfig = null;
  var _cachedCronJobs = [];
  var _cachedWorkspace = null;

  var App = {
    page: 'overview',
    _activeAgentId: null,
    _memoryTab: 'episodic',
    _skillsTab: 'installed',
    _memorySessionId: '',
    _routingProfileDraft: null,
    _routingProfileDefaults: null,
    _contextBudgetDraft: null,
    _contextBudgetDefaults: null,
    _modelGraphFocusTurn: null,
    _modelGraphFocusModel: null,
    _modelGraphFocusEdge: null,
    _effTab: 'performance',
    ws: null,
    _resolveActiveAgentId: function() {
      var self = this;
      if (self._activeAgentId) return Promise.resolve(self._activeAgentId);
      return api('/api/agent/status')
        .then(function(s) {
          if (s && s.name) setAgentDisplayName(s.name);
          var aid = (s && s.agent_id) ? String(s.agent_id) : 'default';
          self._activeAgentId = aid;
          return aid;
        })
        .catch(function() { return 'default'; });
    },
    setPage: function(p) {
      this.page = p;
      document.querySelectorAll('.sidebar-nav a, .mobile-nav a').forEach(function(a) {
        a.classList.toggle('active', a.getAttribute('data-page') === p);
      });
      var bc = document.getElementById('breadcrumb'); if (bc) bc.textContent = titles[p] || p;
    },
    refreshOverview: function() {
      if (this.page !== 'overview') return;
      var content = document.getElementById('content');
      if (!content) return;
      var self = this;
      this.renderOverview().then(function(cards) {
        self._applyOverviewCards(cards);
      }).catch(function(e) {
        toast(e.message || 'Overview refresh failed');
      });
    },
    _renderOverviewShell: function() {
      return ''
        + '<div class="card overview-status">'
        + '  <div class="overview-meta" id="ov-meta"></div>'
        + '</div>'
        + '<div class="card overview-attention" id="ov-attention" style="display:none"></div>'
        + '<div class="card overview-onboarding" id="ov-onboarding" style="display:none"></div>'
        + '<div class="overview-grid" id="overview-grid">'
        + '  <div id="ov-card-cost"></div>'
        + '  <div id="ov-card-tokens"></div>'
        + '  <div id="ov-card-cache"></div>'
        + '  <div id="ov-card-wallet"></div>'
        + '  <div id="ov-card-latency"></div>'
        + '  <div id="ov-card-sessions"></div>'
        + '  <div id="ov-card-system"></div>'
        + '  <div id="ov-card-errors"></div>'
        + '  <div id="ov-card-fleet" class="overview-fleet-slot" style="display:none"></div>'
        + '</div>'
        + '<div class="card" id="ov-integrations" style="margin-top:1rem"></div>';
    },
    _applyOverviewCards: function(cards) {
      var self = this;
      var slotMap = {
        cost: 'ov-card-cost',
        tokens: 'ov-card-tokens',
        cache: 'ov-card-cache',
        wallet: 'ov-card-wallet',
        latency: 'ov-card-latency',
        sessions: 'ov-card-sessions',
        system: 'ov-card-system',
        errors: 'ov-card-errors',
        fleet: 'ov-card-fleet'
      };
      var meta = cards && cards.__meta ? cards.__meta : {};
      var attention = cards && cards.__attention ? cards.__attention : [];
      var failures = cards && cards.__failures ? cards.__failures : [];
      var ovMeta = document.getElementById('ov-meta');
      if (ovMeta) {
        var staleBadge = failures.length > 0
          ? '<span class="badge warning">Partial data (' + failures.length + ' source' + (failures.length > 1 ? 's' : '') + ' unavailable)</span>'
          : '<span class="badge success">All sources healthy</span>';
        var updated = meta.updated_at ? esc(meta.updated_at) : 'unknown';
        setHtml(ovMeta, staleBadge + '<span class="badge muted">Last updated: ' + updated + '</span>');
      }
      var ovAttention = document.getElementById('ov-attention');
      if (ovAttention) {
        if (attention.length > 0) {
          var items = attention.map(function(item) {
            return '<li>' + esc(item) + '</li>';
          }).join('');
          setHtml(ovAttention, '<strong>Attention needed</strong><ul style="margin:0.5rem 0 0 1rem">' + items + '</ul>');
          ovAttention.style.display = '';
        } else {
          ovAttention.style.display = 'none';
        }
      }
      var ovOnboarding = document.getElementById('ov-onboarding');
      if (ovOnboarding && hintsEnabled() && !window.localStorage.getItem('ic_dash_onboarding_dismissed')) {
        setHtml(
          ovOnboarding,
          '<div style="display:flex;justify-content:space-between;gap:1rem;align-items:flex-start">'
            + '<div><strong>Quick Start</strong><div style="margin-top:0.35rem;color:var(--muted);font-size:0.85rem">1) Open <em>Sessions</em> and start a conversation. 2) Use <em>Context</em> to inspect token and reasoning traces. 3) Use <em>Prompt Performance</em> to tune latency/cost.</div></div>'
            + '<button class="btn secondary" id="dismiss-onboarding" title="Dismiss hint" aria-label="Dismiss hint">x</button>'
            + '</div>'
        );
        ovOnboarding.style.display = '';
      } else if (ovOnboarding) {
        ovOnboarding.style.display = 'none';
      }
      Object.keys(slotMap).forEach(function(key) {
        var el = document.getElementById(slotMap[key]);
        if (!el) return;
        var html = cards && cards[key] ? cards[key] : '';
        if (html) {
          setHtml(el, html);
          el.style.display = '';
        } else {
          setHtml(el, '');
          if (key === 'fleet') el.style.display = 'none';
        }
      });
      // Render integrations panel
      var intEl = document.getElementById('ov-integrations');
      if (intEl) {
        var chStatuses = cards && cards.__channelStatuses ? cards.__channelStatuses : [];
        var intHtml = '<div class="card-title">Integrations</div>';
        if (Array.isArray(chStatuses) && chStatuses.length > 0) {
          intHtml += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:0.75rem">';
          chStatuses.forEach(function(ch) {
            var dot = ch.connected ? 'background:#22c55e' : 'background:#ef4444';
            var statusText = ch.connected ? 'Connected' : (ch.last_error || 'Disconnected');
            var stats = '';
            if (ch.messages_received != null) stats += '<div style="font-size:0.65rem;color:var(--muted)">In: ' + (ch.messages_received || 0) + ' / Out: ' + (ch.messages_sent || 0) + '</div>';
            intHtml += '<div style="padding:0.65rem;border:1px solid var(--border);border-radius:6px">'
              + '<div style="display:flex;align-items:center;gap:0.4rem;margin-bottom:0.3rem">'
              + '<span style="width:8px;height:8px;border-radius:50%;display:inline-block;' + dot + '"></span>'
              + '<span style="font-weight:600;font-size:0.8rem">' + esc(ch.name || 'unknown') + '</span>'
              + '</div>'
              + '<div style="font-size:0.7rem;color:' + (ch.connected ? 'var(--text)' : 'var(--warning, #f59e0b)') + '">' + esc(statusText.substring(0, 80)) + '</div>'
              + stats
              + '<button class="btn secondary channel-test-btn" data-test-channel="' + esc(ch.name || '') + '" style="font-size:0.65rem;padding:0.2rem 0.5rem;margin-top:0.35rem">Test</button>'
              + '</div>';
          });
          intHtml += '</div>';
        } else {
          intHtml += '<div style="font-size:0.75rem;color:var(--muted)">No integrations configured. Add channels in Settings.</div>';
        }
        setHtml(intEl, intHtml);
      }
    },
    navigate: function(page) {
      if (page === 'recommendations') {
        this._effTab = 'recommendations';
        page = 'efficiency';
      }
      this.setPage(page);
      if (page !== 'overview') {
        if (overviewRefreshTimer) clearInterval(overviewRefreshTimer);
      }
      if (page !== 'workspace') stopWorkspaceRefresh();
      var content = document.getElementById('content');
      if (content) {
        if (page === 'workspace' || page === 'scheduler') { content.style.overflow = 'hidden'; content.style.padding = '0'; content.style.display = ''; }
        else if (page === 'settings') { content.style.overflow = 'auto'; content.style.padding = '1.5rem'; content.style.display = 'flex'; content.style.flexDirection = 'column'; }
        else { content.style.overflow = 'auto'; content.style.padding = '1.5rem'; content.style.display = ''; }
        setHtml(content, '<div class="skeleton" style="height:200px"></div>');
      }
      var self = this;
      var renderName = 'render' + page.charAt(0).toUpperCase() + page.slice(1);
      if (this[renderName]) {
        this[renderName]().then(function(html) {
          if (page === 'overview') {
            if (content) setHtml(content, self._renderOverviewShell());
            self._applyOverviewCards(html);
            startOverviewRefresh(self);
            return;
          }
          if (content) {
            var helperDefs = {
              sessions: { id: 'sessions-helper', text: 'Review and continue conversations. Click a row to open details.' },
              context: { id: 'context-helper', text: 'Inspect context budgets, token allocation, and reasoning traces.' },
              memory: { id: 'memory-helper', text: 'Browse memory layers or search by query.' },
              skills: { id: 'skills-helper', text: 'Toggle and manage enabled runtime skills.' },
              agents: { id: 'agents-helper', text: 'Monitor subagents and their current state.' },
              scheduler: { id: 'scheduler-helper', text: 'Manage recurring jobs and inspect run outcomes.' },
              integrations: { id: 'integrations-helper', text: 'View channel health, message stats, and test connectivity.' },
              metrics: { id: 'metrics-helper', text: 'Track costs, throughput, and provider capacity.' },
              efficiency: { id: 'efficiency-helper', text: 'Find prompt and token optimization opportunities.' },
              recommendations: { id: 'recommendations-helper', text: 'Generate and review improvement suggestions.' },
              wallet: { id: 'wallet-helper', text: 'Inspect treasury balances and token positions.' },
              workspace: { id: 'workspace-helper', text: 'See live runtime activity across systems.' },
              settings: { id: 'settings-helper', text: 'Edit runtime configuration safely.' }
            };
            var helperDef = helperDefs[page];
            var showHelper = !!helperDef && hintsEnabled() && !isHintDismissed(helperDef.id);
            if (page === 'sessions') {
              showHelper = showHelper && !self._activeSession;
            }
            var helper = showHelper ? renderHintBanner(helperDef.id, helperDef.text) : '';
            setHtml(content, helper + html);
          }
          if (page === 'settings') setTimeout(syncSettingsHighlight, 0);
          if (page === 'workspace') setTimeout(function() { startWorkspaceEngine(_cachedWorkspace || { agents: [], workstations: [] }); startWorkspaceRefresh(self); }, 0);
          if (page === 'overview') startOverviewRefresh(self);
        }).catch(function(e) {
          if (content) setHtml(content, '<div class="card"><p style="color:var(--error)">' + esc(e.message || 'Failed to load') + '</p></div>');
        });
      }
    },
    refreshSkills: function() {
      if (this.page !== 'skills') return;
      var content = document.getElementById('content');
      if (!content) return;
      setHtml(content, '<div class="skeleton" style="height:200px"></div>');
      this.renderSkills().then(function(html) {
        setHtml(content, html);
      }).catch(function(e) {
        setHtml(content, '<div class="card"><p style="color:var(--error)">' + esc(e.message || 'Failed to load skills') + '</p></div>');
      });
    },
  };