roboticus-api 0.11.3

HTTP routes, WebSocket, auth, rate limiting, and dashboard for the Roboticus agent runtime
Documentation
App.renderWallet = function() {
      var self = this;
      return Promise.all([
        api('/api/wallet/balance').catch(function() { return { balance: '0', currency: 'ETH', tokens: [], network: 'Unknown' }; }),
        api('/api/wallet/address').catch(function() { return { address: '', chain_id: 1, network: 'Unknown' }; }),
        api('/api/stats/transactions?hours=168').catch(function() { return { transactions: [] }; }),
        api('/api/services/swaps?limit=10').catch(function() { return { swap_tasks: [] }; }),
        api('/api/services/tax-payouts?limit=10').catch(function() { return { tax_tasks: [] }; })
      ]).then(function(arr) {
        var bal = arr[0], addr = arr[1], txs = (arr[2].transactions || []), swaps = (arr[3].swap_tasks || []), taxTasks = (arr[4].tax_tasks || []);
        var addrStr = (addr.address != null && addr.address !== '') ? addr.address : (addr.note || '\u2014');
        var network = bal.network || addr.network || 'Unknown';
        var chainId = addr.chain_id || bal.chain_id || 1;
        var tokens = bal.tokens || [];

        txs = txs.filter(function(t) { return t.amount && t.amount !== 0; });
        var txRows = txs.map(function(t) {
          var meta = {};
          try { if (t.metadata_json) meta = JSON.parse(t.metadata_json); } catch(e) {}
          var label = t.tx_type || '';
          if (meta.category) label += ' \u00b7 ' + meta.category;
          var amtStr = (typeof t.amount === 'number' ? t.amount.toFixed(2) : t.amount) + ' ' + (t.currency || '');
          var dateStr = t.created_at || '';
          if (dateStr.length > 19) dateStr = dateStr.substring(0, 19).replace('T', ' ');
          return '<tr><td>' + esc(label) + '</td><td style="font-family:var(--mono)">' + esc(amtStr) + '</td><td style="color:var(--muted)">' + esc(dateStr) + '</td></tr>';
        }).join('');

        // Network & address card
        var networkBadge = '<span class="badge success" style="font-size:0.625rem;vertical-align:middle">' + esc(network) + '</span>';
        var addrSection = '<div class="card" style="margin-bottom:1rem"><div class="card-title">Wallet ' + networkBadge + '</div><div style="font-size:0.6875rem;color:var(--muted);margin-bottom:0.5rem">Chain ID: ' + chainId + '</div><div class="card-mono" style="word-break:break-all" id="wallet-addr">' + esc(addrStr) + '</div><div style="display:flex;gap:0.5rem;margin-top:8px"><button class="btn secondary" id="copy-address" style="font-size:0.75rem;padding:0.3rem 0.75rem">Copy</button></div><p style="font-size:0.6875rem;color:var(--muted);margin-top:0.5rem">Wallet settings are immutable at runtime. Update `roboticus.toml` and restart to change address or chain.</p></div>';

        // Token balances card
        var tokenRows = '';
        if (tokens.length > 0) {
          tokens.forEach(function(t) {
            var icon = self._TOKEN_ICONS[t.symbol] || '\u25cb';
            var bal = t.balance || 0;
            var formatted = t.formatted || (bal === 0 ? '0' : String(bal));
            var nativeBadge = t.is_native ? ' <span class="badge" style="font-size:0.5rem;padding:1px 4px;opacity:0.6">gas</span>' : '';
            var contractLink = '';
            if (t.contract) {
              var explorerUrl = self._explorerUrl(chainId, t.contract);
              contractLink = ' <a href="' + explorerUrl + '" target="_blank" rel="noopener" style="font-size:0.5625rem;color:var(--accent);text-decoration:none" title="' + esc(t.contract) + '">\u2197</a>';
            }
            var dimClass = bal === 0 ? ' style="opacity:0.4"' : '';
            tokenRows += '<div class="settings-row"' + dimClass + '><div style="display:flex;align-items:center;gap:0.5rem;min-width:120px"><span style="font-size:1.1rem;width:1.5rem;text-align:center">' + icon + '</span><div><div style="font-weight:600;font-size:0.8125rem">' + esc(t.symbol) + nativeBadge + contractLink + '</div><div style="font-size:0.625rem;color:var(--muted)">' + esc(t.name) + '</div></div></div><div style="text-align:right;font-family:var(--font-mono);font-size:0.875rem;font-weight:500">' + esc(formatted) + '</div></div>';
          });
        } else {
          tokenRows = '<div style="color:var(--muted);font-size:0.75rem;padding:0.5rem 0">No token data available</div>';
        }
        var balancesCard = '<div class="card" style="margin-bottom:1rem"><div class="card-title">Balances</div>' + tokenRows + '</div>';

        // Treasury card
        var treasury = bal.treasury || {};
        var treasuryCard = '<div class="card" style="margin-bottom:1rem"><div class="card-title">Treasury Policy</div>' +
          '<div class="settings-row"><div class="settings-label">Per-payment cap</div><div style="font-family:var(--font-mono)">$' + (treasury.per_payment_cap || 0).toFixed(2) + '</div></div>' +
          '<div style="font-size:0.75rem;color:var(--muted);padding:0 0 0.5rem 0">Maximum amount deducted per single service call or tool execution</div>' +
          '<div class="settings-row"><div class="settings-label">Daily inference budget</div><div style="font-family:var(--font-mono)">$' + (treasury.daily_inference_budget || 0).toFixed(2) + '</div></div>' +
          '<div style="font-size:0.75rem;color:var(--muted);padding:0 0 0.5rem 0">Total daily spend limit across all LLM inference calls</div>' +
          '<div class="settings-row"><div class="settings-label">Minimum reserve</div><div style="font-family:var(--font-mono)">$' + (treasury.minimum_reserve || 0).toFixed(2) + '</div></div>' +
          '<div style="font-size:0.75rem;color:var(--muted);padding:0 0 0.5rem 0">Balance threshold below which the agent pauses spending</div>' +
          '</div>';
        var revenueSwapCard = self._renderRevenueSwapSummaryCard(treasury.revenue_swap || {});
        var seedReadinessCard = self._renderSeedExerciseReadinessCard(bal.seed_exercise_readiness || {});
        var seedProgressCard = self._renderSeedExerciseProgressCard(bal.seed_exercise_progress || {});
        var seedPlanCard = self._renderSeedExercisePlanCard(bal.seed_exercise_plan || {});
        var strategySummaryCard = self._renderRevenueStrategySummaryCard(bal.revenue_strategy_summary || []);
        var feedbackSummaryCard = self._renderRevenueFeedbackSummaryCard(bal.revenue_feedback_summary || []);
        var swapTasksCard = self._renderRevenueSwapTasksCard(swaps);
        var taxTasksCard = self._renderRevenueTaxTasksCard(taxTasks);

        var guidanceBanner = '<div class="card" style="margin-bottom:1rem;background:linear-gradient(135deg,var(--surface) 0%,rgba(96,165,250,0.08) 100%);border-left:3px solid var(--accent)">'
            + '<div style="font-weight:600;margin-bottom:0.5rem">Wallet Overview</div>'
            + '<div style="font-size:0.8125rem;color:var(--muted);line-height:1.5">'
            + 'This page shows your agent\'s treasury balance and transaction history. '
            + 'The wallet is used for on-chain operations and service payments. '
            + '<strong>Balance</strong> reflects current holdings across configured networks. '
            + '<strong>Transactions</strong> show recent on-chain activity. '
            + 'To adjust spending limits, visit <a href="#settings" data-page="settings" style="color:var(--accent)">Settings</a> \u2192 Wallet section.'
            + '</div></div>';
        return guidanceBanner + addrSection + balancesCard + treasuryCard + revenueSwapCard + seedReadinessCard + seedProgressCard + seedPlanCard + strategySummaryCard + feedbackSummaryCard + swapTasksCard + taxTasksCard +
          '<div class="card-title">Transaction history</div><div class="table-wrap"><table><thead><tr><th>Type</th><th>Amount</th><th>Date</th></tr></thead><tbody>' + txRows + '</tbody></table></div>';
      });
};
App._explorerUrl = function(chainId, contract) {
      var base = { 1: 'https://etherscan.io', 8453: 'https://basescan.org', 42161: 'https://arbiscan.io', 10: 'https://optimistic.etherscan.io', 137: 'https://polygonscan.com' };
      return (base[chainId] || 'https://basescan.org') + '/token/' + contract;
};
App._explorerUrl = function(chainId, contract) {
      var base = { 1: 'https://etherscan.io', 8453: 'https://basescan.org', 42161: 'https://arbiscan.io', 10: 'https://optimistic.etherscan.io', 137: 'https://polygonscan.com' };
      return (base[chainId] || 'https://basescan.org') + '/token/' + contract;
};