// modules/data/gitea.js — All Gitea API calls // Writes to S: _activeAgentCount, _matrixCommitHashes, agentStatus import { S } from '../state.js'; const GITEA_BASE = 'http://143.198.27.163:3000/api/v1'; const GITEA_TOKEN = 'dc0517a965226b7a0c5ffdd961b1ba26521ac592'; const GITEA_REPOS = ['Timmy_Foundation/the-nexus', 'Timmy_Foundation/hermes-agent']; const AGENT_NAMES = ['Claude', 'Kimi', 'Perplexity', 'Groq', 'Grok', 'Ollama']; const DAY_MS = 86400000; const HOUR_MS = 3600000; const CACHE_MS = 5 * 60 * 1000; let _agentStatusCache = null; let _agentStatusCacheTime = 0; let _commitsCache = null; let _commitsCacheTime = 0; // --- Core fetchers --- export async function fetchNexusCommits(limit = 50) { const now = Date.now(); if (_commitsCache && (now - _commitsCacheTime < CACHE_MS)) return _commitsCache; try { const res = await fetch( `${GITEA_BASE}/repos/Timmy_Foundation/the-nexus/commits?limit=${limit}`, { headers: { 'Authorization': `token ${GITEA_TOKEN}` } } ); if (!res.ok) return []; _commitsCache = await res.json(); _commitsCacheTime = now; return _commitsCache; } catch { return []; } } async function fetchRepoCommits(repo, limit = 30) { try { const res = await fetch( `${GITEA_BASE}/repos/${repo}/commits?sha=main&limit=${limit}&token=${GITEA_TOKEN}` ); if (!res.ok) return []; return await res.json(); } catch { return []; } } async function fetchOpenPRs() { try { const res = await fetch( `${GITEA_BASE}/repos/Timmy_Foundation/the-nexus/pulls?state=open&limit=50&token=${GITEA_TOKEN}` ); if (res.ok) return await res.json(); } catch { /* ignore */ } return []; } export async function fetchAgentStatus() { const now = Date.now(); if (_agentStatusCache && (now - _agentStatusCacheTime < CACHE_MS)) return _agentStatusCache; const allRepoCommits = await Promise.all(GITEA_REPOS.map(r => fetchRepoCommits(r))); const openPRs = await fetchOpenPRs(); const agents = []; for (const agentName of AGENT_NAMES) { const nameLower = agentName.toLowerCase(); const allCommits = []; for (const repoCommits of allRepoCommits) { if (!Array.isArray(repoCommits)) continue; const matching = repoCommits.filter(c => (c.commit?.author?.name || '').toLowerCase().includes(nameLower) ); allCommits.push(...matching); } let status = 'dormant'; let lastSeen = null; let currentWork = null; if (allCommits.length > 0) { allCommits.sort((a, b) => new Date(b.commit.author.date) - new Date(a.commit.author.date) ); const latest = allCommits[0]; const commitTime = new Date(latest.commit.author.date).getTime(); lastSeen = latest.commit.author.date; currentWork = latest.commit.message.split('\n')[0]; if (now - commitTime < HOUR_MS) status = 'working'; else if (now - commitTime < DAY_MS) status = 'idle'; else status = 'dormant'; } const agentPRs = openPRs.filter(pr => (pr.user?.login || '').toLowerCase().includes(nameLower) || (pr.head?.label || '').toLowerCase().includes(nameLower) ); agents.push({ name: nameLower, status, issue: currentWork, prs_today: agentPRs.length, local: nameLower === 'ollama', }); } _agentStatusCache = { agents }; _agentStatusCacheTime = now; return _agentStatusCache; } // --- State updaters --- export async function refreshCommitData() { const commits = await fetchNexusCommits(); S._matrixCommitHashes = commits.slice(0, 20) .map(c => (c.sha || '').slice(0, 7)) .filter(h => h.length > 0); return commits; } export async function refreshAgentData() { try { const data = await fetchAgentStatus(); S._activeAgentCount = data.agents.filter(a => a.status === 'working').length; return data; } catch { const fallback = { agents: AGENT_NAMES.map(n => ({ name: n.toLowerCase(), status: 'unreachable', issue: null, prs_today: 0, local: false, })) }; S._activeAgentCount = 0; return fallback; } } export async function fetchMergedPRs(limit = 20) { try { const res = await fetch( `${GITEA_BASE}/repos/Timmy_Foundation/the-nexus/pulls?state=closed&limit=${limit}`, { headers: { 'Authorization': `token ${GITEA_TOKEN}` } } ); if (!res.ok) return []; const data = await res.json(); return data .filter(p => p.merged) .map(p => ({ prNum: p.number, title: p.title .replace(/^\[[\w\s]+\]\s*/i, '') .replace(/\s*\(#\d+\)\s*$/, ''), })); } catch { return []; } } export { GITEA_BASE, GITEA_TOKEN, GITEA_REPOS, AGENT_NAMES, CACHE_MS as AGENT_STATUS_CACHE_MS };