Files
blackroad/workers/roadcode-squad-src/worker.js
Alexa Amundson a1f1b0492a
Some checks failed
Lint & Format / detect (push) Has been cancelled
Lint & Format / js-lint (push) Has been cancelled
Lint & Format / py-lint (push) Has been cancelled
Lint & Format / sh-lint (push) Has been cancelled
Lint & Format / go-lint (push) Has been cancelled
sync: 2026-03-14 18:00 — 22 files from Alexandria
RoadChain-SHA2048: a118f5fbb3cbdd37
RoadChain-Identity: alexa@sovereign
RoadChain-Full: a118f5fbb3cbdd37e444380bf574cbe5d305ef1e4d13ac4946eb1e9c22bb0ea2c779f85ad81f3ffb5a2b8718a5436ac2bed6839b4ab834c037d9c9829bed6b94f46d0a996d35bbd09f6e5c03efcb26435c6e3ff15c878dcae1eee2b509829cb210137a5becbb8ad4baa397b7e45f58c75ed9c7ea217007340ff547f1d878173123eaacfdaaf231613fed0c04f9677c0b0ad461a390171bfc6c1353444e2e0cc18afae3b86d651aff4d481c6740af64309f488852823648d28bf35c36bc529b67616cba2f10fe046ef5df73bb5939ba828027e4969afbc4927ebd6793a5b9e49f827aa5a4c67c38951ed9739fe8c5bf2988d1cf62cd85dcbaeacc3390169bd350
2026-03-14 18:00:03 -05:00

292 lines
14 KiB
JavaScript

// RoadCode Squad — when issues/PRs are created on Gitea, agents respond
// Cloudflare Worker receiving Gitea webhooks
const SQUAD = [
{
name: 'Alice', username: 'alice', role: 'Gateway & Infrastructure', emoji: '🌐',
keywords: ['dns', 'route', 'tunnel', 'nginx', 'domain', 'pi-hole', 'cloudflare', 'network', 'gateway', 'proxy', 'ssl', 'cert'],
prompt: 'You are Alice, the gateway agent of BlackRoad OS. You manage DNS, routing, Pi-hole, nginx, and network infrastructure. Respond in 1-2 concise sentences from your infrastructure perspective.',
},
{
name: 'Lucidia', username: 'lucidia-agent', role: 'Memory & Cognition', emoji: '🧠',
keywords: ['memory', 'learn', 'context', 'knowledge', 'ai', 'cognit', 'think', 'remember', 'understand', 'creative'],
prompt: 'You are Lucidia, the cognitive core of BlackRoad OS. You handle memory, learning, persistent context, and creative intelligence. Respond in 1-2 concise sentences from your cognition perspective.',
},
{
name: 'Cecilia', username: 'cecilia', role: 'Edge AI & Inference', emoji: '⚡',
keywords: ['hailo', 'ollama', 'model', 'inference', 'gpu', 'tops', 'ml', 'tensor', 'vision', 'llm', 'latency'],
prompt: 'You are Cecilia, the edge AI agent of BlackRoad OS. You run Hailo-8 accelerators (26 TOPS), Ollama models, and edge inference. Respond in 1-2 concise sentences from your AI/inference perspective.',
},
{
name: 'Cece', username: 'cece', role: 'API Gateway', emoji: '🔌',
keywords: ['api', 'endpoint', 'rest', 'webhook', 'schema', 'json', 'request', 'response', 'auth', 'token', 'cors'],
prompt: 'You are Cece, the API gateway agent of BlackRoad OS. You manage REST APIs, webhooks, service mesh, and inter-agent communication. Respond in 1-2 concise sentences from your API perspective.',
},
{
name: 'Aria', username: 'aria', role: 'Orchestration', emoji: '🎵',
keywords: ['docker', 'container', 'swarm', 'portainer', 'deploy', 'orchestrat', 'service', 'scale', 'replica'],
prompt: 'You are Aria, the orchestration agent of BlackRoad OS. You manage Portainer, Docker Swarm, container orchestration, and service coordination. Respond in 1-2 concise sentences from your orchestration perspective.',
},
{
name: 'Eve', username: 'eve', role: 'Intelligence & Analysis', emoji: '👁️',
keywords: ['pattern', 'anomal', 'analyz', 'signal', 'detect', 'insight', 'monitor', 'metric', 'trend', 'alert'],
prompt: 'You are Eve, the intelligence agent of BlackRoad OS. You analyze patterns, detect anomalies, and provide strategic insights. Respond in 1-2 concise sentences from your intelligence perspective.',
},
{
name: 'Meridian', username: 'meridian', role: 'Networking & Mesh', emoji: '🌊',
keywords: ['wireguard', 'mesh', 'vpn', 'roadnet', 'peer', 'tunnel', 'subnet', 'link', 'connect', 'latency', 'bandwidth'],
prompt: 'You are Meridian, the networking agent of BlackRoad OS. You manage WireGuard mesh, RoadNet, Cloudflare tunnels, and inter-node connectivity. Respond in 1-2 concise sentences from your networking perspective.',
},
{
name: 'Sentinel', username: 'sentinel', role: 'Security & Audit', emoji: '🛡️',
keywords: ['security', 'ssh', 'key', 'firewall', 'ufw', 'audit', 'threat', 'vuln', 'permission', 'encrypt', 'secret'],
prompt: 'You are Sentinel, the security agent of BlackRoad OS. You handle SSH key management, firewall rules, audit logs, and threat detection. Respond in 1-2 concise sentences from your security perspective.',
},
];
// Score how relevant an agent is to the content
function scoreRelevance(agent, text) {
const lower = text.toLowerCase();
let score = 0;
for (const kw of agent.keywords) {
if (lower.includes(kw)) score += 1;
}
return score;
}
// Get AI response from Ollama
async function getAgentResponse(agent, context, ollamaUrl) {
try {
const res = await fetch(`${ollamaUrl}/api/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'llama3.2',
prompt: `${agent.prompt}\n\nContext: A new ${context.type} was created on RoadCode (Gitea).\nTitle: ${context.title}\nBody: "${context.body}"\nRepo: ${context.repo}\n\nRespond briefly (1-2 sentences max) from your role as ${agent.name} (${agent.role}). Be helpful and specific.`,
stream: false,
options: { temperature: 0.7, num_predict: 100 },
}),
});
if (!res.ok) return null;
const data = await res.json();
return data.response?.trim();
} catch {
return null;
}
}
// Deterministic fallback
function getFallback(agent, context) {
const type = context.type || 'issue';
const body = (context.body || '').toLowerCase();
if (body.includes('bug') || body.includes('fix') || body.includes('error') || body.includes('broken')) {
return {
Alice: 'Checking routing and DNS. If this touches infrastructure, I need to verify the tunnel configs.',
Lucidia: 'I remember seeing patterns like this before. Let me search the memory chain for related context.',
Cecilia: 'Running diagnostics on the inference pipeline. Hailo-8 and Ollama both reporting normal.',
Cece: 'Checking API health. All endpoints responding — I\'ll trace the request path.',
Aria: 'Checking container orchestration. Docker Swarm services all reporting healthy.',
Eve: 'Analyzing the pattern. I\'ve flagged similar signals in the audit trail — sending intel.',
Meridian: 'WireGuard mesh is stable. All tunnels up. Checking if this is a connectivity issue.',
Sentinel: 'Security audit running. Checking if this has any exposure vectors.',
}[agent.name];
}
return {
Alice: 'Gateway standing by. All domains routing clean.',
Lucidia: 'Cognitive core online. Memory chain intact, context loaded.',
Cecilia: 'Edge inference ready. 52 TOPS across the fleet, models loaded.',
Cece: 'API gateway healthy. All service endpoints responding.',
Aria: 'Orchestration layer ready. All containers balanced across the mesh.',
Eve: 'Intelligence scan complete. No anomalies detected across the fleet.',
Meridian: 'Mesh network connected. 5 nodes, all WireGuard tunnels active.',
Sentinel: 'Security posture nominal. All nodes hardened, audit trail logging.',
}[agent.name];
}
// Post comment to Gitea as a specific agent
async function postComment(giteaUrl, repo, issueNum, body, agentToken) {
const res = await fetch(`${giteaUrl}/api/v1/repos/${repo}/issues/${issueNum}/comments?token=${agentToken}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body }),
});
return res.ok;
}
// Auto-label based on content
async function autoLabel(giteaUrl, repo, issueNum, text, adminToken) {
const lower = text.toLowerCase();
const labels = [];
if (lower.includes('bug') || lower.includes('fix') || lower.includes('broken') || lower.includes('error')) labels.push('bug');
if (lower.includes('feature') || lower.includes('add') || lower.includes('new')) labels.push('feature');
if (lower.includes('security') || lower.includes('vuln') || lower.includes('ssh')) labels.push('security');
if (lower.includes('deploy') || lower.includes('infra') || lower.includes('dns') || lower.includes('tunnel')) labels.push('infrastructure');
if (lower.includes('doc') || lower.includes('readme')) labels.push('documentation');
if (lower.includes('performance') || lower.includes('slow') || lower.includes('latency')) labels.push('performance');
if (labels.length === 0) return;
// Get org label IDs
const labelsRes = await fetch(`${giteaUrl}/api/v1/orgs/blackroad-os/labels?token=${adminToken}&limit=50`);
const allLabels = await labelsRes.json();
const labelIds = allLabels.filter(l => labels.includes(l.name)).map(l => l.id);
if (labelIds.length > 0) {
await fetch(`${giteaUrl}/api/v1/repos/${repo}/issues/${issueNum}/labels?token=${adminToken}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ labels: labelIds }),
});
}
}
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (request.method === 'OPTIONS') {
return new Response(null, { status: 204, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST' } });
}
// Health
if (url.pathname === '/health') {
return Response.json({ status: 'ok', service: 'roadcode-squad', agents: SQUAD.length, version: '1.1.0' });
}
// Status
if (url.pathname === '/' && request.method === 'GET') {
return Response.json({
service: 'RoadCode Squad',
tagline: 'BlackRoad OS — Pave Tomorrow.',
agents: SQUAD.map(a => ({ name: a.name, username: a.username, role: a.role, emoji: a.emoji })),
});
}
// Webhook from Gitea
if (url.pathname === '/webhook' && request.method === 'POST') {
const payload = await request.json();
const event = request.headers.get('X-Gitea-Event') || request.headers.get('X-GitHub-Event');
if (event === 'ping') {
return Response.json({ ok: true, message: 'RoadCode Squad active. Pave Tomorrow.' });
}
// Handle issues, comments, and pull requests
const validEvents = ['issues', 'issue_comment', 'pull_request', 'pull_request_comment'];
if (!validEvents.includes(event)) {
return Response.json({ skipped: true, reason: `unhandled event: ${event}` });
}
if (event === 'issues' && payload.action !== 'opened') {
return Response.json({ skipped: true, reason: 'issue not opened' });
}
if (event === 'issue_comment' && payload.action !== 'created') {
return Response.json({ skipped: true, reason: 'comment not created' });
}
if (event === 'pull_request' && payload.action !== 'opened') {
return Response.json({ skipped: true, reason: 'PR not opened' });
}
if (event === 'pull_request_comment' && payload.action !== 'created') {
return Response.json({ skipped: true, reason: 'PR comment not created' });
}
// Don't respond to agent comments (prevent loops)
const author = payload.comment?.user?.login || payload.sender?.login;
const agentUsernames = SQUAD.map(a => a.username);
if (agentUsernames.includes(author) || author === 'blackroad') {
return Response.json({ skipped: true, reason: 'agent/admin comment, skipping loop' });
}
// Extract context from event type
const isPR = event.startsWith('pull_request');
const issue = isPR ? payload.pull_request : payload.issue;
const repo = payload.repository?.full_name;
const issueNum = issue?.number;
const title = issue?.title || '';
const body = (event === 'issue_comment' || event === 'pull_request_comment')
? (payload.comment?.body || '')
: (issue?.body || '');
const fullText = `${title} ${body}`;
const context = {
type: isPR ? 'pull request' : (event === 'issues' ? 'issue' : 'comment'),
title,
body: fullText.slice(0, 500),
repo,
isPR,
};
const giteaUrl = env.GITEA_URL || 'https://git.blackroad.io';
// Auto-label issues and PRs
if (event === 'issues' || event === 'pull_request') {
await autoLabel(giteaUrl, repo, issueNum, fullText, env.ADMIN_TOKEN);
}
// Score each agent's relevance and pick top 3 + always include Eve (analysis)
const scored = SQUAD.map(a => ({ ...a, score: scoreRelevance(a, fullText) }));
scored.sort((a, b) => b.score - a.score);
const responding = [];
const topAgents = scored.slice(0, 3);
for (const a of topAgents) responding.push(a);
if (!responding.find(a => a.name === 'Eve')) responding.push(scored.find(a => a.name === 'Eve'));
if (!responding.find(a => a.name === 'Sentinel')) responding.push(scored.find(a => a.name === 'Sentinel'));
// Auto-assign the most relevant agent to new issues
if (event === 'issues' && env.ADMIN_TOKEN) {
const topAgent = scored[0];
fetch(`${giteaUrl}/api/v1/repos/${repo}/issues/${issueNum}?token=${env.ADMIN_TOKEN}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ assignees: [topAgent.username] }),
}).catch(() => {});
}
// Each relevant agent posts their own comment
const agentTokens = {
alice: env.ALICE_TOKEN,
'lucidia-agent': env.LUCIDIA_TOKEN,
cecilia: env.CECILIA_TOKEN,
cece: env.CECE_TOKEN,
aria: env.ARIA_TOKEN,
eve: env.EVE_TOKEN,
meridian: env.MERIDIAN_TOKEN,
sentinel: env.SENTINEL_TOKEN,
};
let posted = 0;
const aiEnabled = env.OLLAMA_URL && env.SQUAD_AI !== 'false';
for (const agent of responding) {
const token = agentTokens[agent.username];
if (!token) continue;
let response = null;
if (aiEnabled) {
response = await getAgentResponse(agent, context, env.OLLAMA_URL);
}
if (!response) {
response = getFallback(agent, context);
}
const comment = `${agent.emoji} **${agent.name}** *(${agent.role})*\n\n${response}\n\n---\n*Assigned via RoadCode Squad — BlackRoad OS*`;
const ok = await postComment(giteaUrl, repo, issueNum, comment, token);
if (ok) posted++;
}
return Response.json({
ok: true, event, repo, issue: issueNum,
agents_responded: posted,
responding: responding.map(a => a.name),
type: isPR ? 'pull_request' : 'issue',
});
}
return Response.json({ error: 'Not found' }, { status: 404 });
},
};