Files
blackroad/workers/roadcode-squad-src/worker.js
Alexa Amundson c7d0216824
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
Monorepo Lint / lint-shell (push) Has been cancelled
Monorepo Lint / lint-js (push) Has been cancelled
sync: 2026-03-16 14:30 — 21 files from Alexandria
RoadChain-SHA2048: 6d362d0de7cfcef6
RoadChain-Identity: alexa@sovereign
RoadChain-Full: 6d362d0de7cfcef69b3f1c30d3671d86cf0f52efaeae70456b8d127d9f4d1569f805014c9404e14915d44b3a113d0a5096b11eee481c9d5c89119327c4a018549f15221f31fe7fbd03420e6dce9a79964ddc1a89a0835c711c4a76cf9f8838d8121dcdcd5eee1f5bfe98c5c04609da27a17962a541b2d7a87a81e9499d0c128c32e662994f31022bfb187d57c0fd7e311727b4adb02bf64fab3a315bb18b1b7b0a28ea52a9b0f8d66229724e909042e6ab31d3c580a763f3cebd8d7d839422bbb6e89b586dcce6d638f246c7f96257eab7abf80d01a6ebb065faa9d33e1a56c1ae7cecd67204a6902e14cae9ed88eb343b327bc8877c649ef5b09b9ee817abc1
2026-03-16 14:30:01 -05:00

524 lines
24 KiB
JavaScript

// RoadCode Squad v2.0.0 — AI agent webhook responder for Gitea
// Cloudflare Worker receiving Gitea webhooks
// Features: keyword scoring, @mention routing, slash commands, AI responses, auto-labeling, auto-assign
const VERSION = '2.0.0';
const SQUAD = [
{
name: 'Alice', username: 'alice', role: 'Gateway & Infrastructure', emoji: '🌐',
keywords: ['dns', 'route', 'tunnel', 'nginx', 'domain', 'pi-hole', 'cloudflare', 'network', 'gateway', 'proxy', 'ssl', 'cert', 'ingress'],
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', 'rag', 'vector'],
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', 'quantiz'],
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', 'graphql'],
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', 'compose'],
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', 'observ'],
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', 'cve'],
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.',
},
];
// ─── Scoring ───────────────────────────────────────────────────────────────
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;
}
// Parse @mentions from text — returns list of agent usernames mentioned
function parseMentions(text) {
const mentions = [];
const re = /@(\w[\w-]*)/g;
let match;
while ((match = re.exec(text)) !== null) {
const username = match[1].toLowerCase();
const agent = SQUAD.find(a => a.username === username || a.name.toLowerCase() === username);
if (agent) mentions.push(agent);
}
return mentions;
}
// Parse slash commands from text
function parseCommands(text) {
const commands = [];
const re = /\/(\w+)(?:\s+(.+?))?(?:\n|$)/g;
let match;
while ((match = re.exec(text)) !== null) {
commands.push({ command: match[1].toLowerCase(), args: (match[2] || '').trim() });
}
return commands;
}
// ─── AI & Fallbacks ────────────────────────────────────────────────────────
async function getAgentResponse(agent, context, ollamaUrl) {
try {
// Standard response headers
const requestId = crypto.randomUUID().slice(0, 8);
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;
}
}
function getFallback(agent, context) {
const body = (context.body || '').toLowerCase();
const isPR = context.isPR;
// Bug/fix context
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];
}
// PR-specific context
if (isPR) {
return {
Alice: 'Reviewing infrastructure impact. Will check if this affects DNS, tunnels, or routing.',
Lucidia: 'Checking memory chain for related changes. Context loaded for review.',
Cecilia: 'Reviewing for inference impact. Checking model compatibility and performance.',
Cece: 'Reviewing API changes. Checking for breaking changes and schema compatibility.',
Aria: 'Reviewing deployment impact. Checking container and orchestration changes.',
Eve: 'Analyzing change patterns. Cross-referencing with recent fleet activity.',
Meridian: 'Reviewing network impact. Checking mesh and tunnel configurations.',
Sentinel: 'Security review initiated. Scanning for vulnerabilities and access changes.',
}[agent.name];
}
// Feature request context
if (body.includes('feature') || body.includes('add') || body.includes('implement') || body.includes('new')) {
return {
Alice: 'Noted. I\'ll evaluate the infrastructure requirements and routing needs.',
Lucidia: 'Interesting idea. Let me check if we have related context in the knowledge base.',
Cecilia: 'I can estimate the compute requirements. What models or inference needs are involved?',
Cece: 'I\'ll design the API surface. Let me check existing endpoints for integration points.',
Aria: 'I\'ll plan the deployment. Checking available capacity across the Docker Swarm.',
Eve: 'Analyzing feasibility. I\'ll cross-reference with current fleet metrics and capacity.',
Meridian: 'I\'ll check bandwidth and connectivity requirements across the mesh.',
Sentinel: 'I\'ll do a security assessment. Need to evaluate the threat surface of this change.',
}[agent.name];
}
// Default
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];
}
// ─── Slash Command Responses ───────────────────────────────────────────────
function handleSlashCommand(command) {
switch (command.command) {
case 'status':
return '📊 **Fleet Status**\n\n| Component | Status |\n|-----------|--------|\n| Workers | 8/8 online |\n| Nodes | 5 Pi5s active |\n| AI | 52 TOPS (2x Hailo-8) |\n| Mesh | WireGuard + RoadNet |\n| Gitea | 207+ repos |\n\n*Use `make health` or visit [fleet-dashboard](https://fleet-dashboard.amundsonalexa.workers.dev) for live status.*';
case 'squad':
return '🛣️ **RoadCode Squad**\n\n' +
SQUAD.map(a => `${a.emoji} **${a.name}** (@${a.username}) — ${a.role}`).join('\n') +
'\n\n*Mention any agent with @username to summon them.*';
case 'help':
return '📖 **Available Commands**\n\n' +
'| Command | Description |\n|---------|-------------|\n' +
'| `/status` | Fleet health overview |\n' +
'| `/squad` | List all agents |\n' +
'| `/assign @agent` | Assign an agent to this issue |\n' +
'| `/priority high\\|medium\\|low` | Set priority label |\n' +
'| `/help` | Show this help |\n\n' +
'*Mention agents with @username to get their input.*';
case 'assign': {
// Returns the agent to assign (handled in caller)
return null;
}
case 'priority': {
const level = command.args?.toLowerCase();
if (['high', 'medium', 'low', 'critical'].includes(level)) {
return `🏷️ Priority set to **${level}**.`;
}
return '⚠️ Usage: `/priority high|medium|low|critical`';
}
default:
return null;
}
}
// ─── Gitea API Helpers ─────────────────────────────────────────────────────
async function postComment(giteaUrl, repo, issueNum, body, agentToken) {
const res = await fetch(`${giteaUrl}/api/v1/repos/${repo}/issues/${issueNum}/comments`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `token ${agentToken}`,
},
body: JSON.stringify({ body }),
});
return res.ok;
}
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;
// Try repo org labels first, fall back to blackroad-os
const orgName = repo.split('/')[0];
let labelsRes = await fetch(`${giteaUrl}/api/v1/orgs/${orgName}/labels?limit=50`, {
headers: { 'Authorization': `token ${adminToken}` },
});
let allLabels = await labelsRes.json();
if (!Array.isArray(allLabels) || allLabels.length === 0) {
labelsRes = await fetch(`${giteaUrl}/api/v1/orgs/blackroad-os/labels?limit=50`, {
headers: { 'Authorization': `token ${adminToken}` },
});
allLabels = await labelsRes.json();
}
const labelIds = (Array.isArray(allLabels) ? 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`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `token ${adminToken}`,
},
body: JSON.stringify({ labels: labelIds }),
});
}
}
async function setPriorityLabel(giteaUrl, repo, issueNum, priority, adminToken) {
const orgName = repo.split('/')[0];
const labelsRes = await fetch(`${giteaUrl}/api/v1/orgs/${orgName}/labels?limit=50`, {
headers: { 'Authorization': `token ${adminToken}` },
});
const allLabels = await labelsRes.json();
const label = (Array.isArray(allLabels) ? allLabels : []).find(l => l.name === priority);
if (label) {
await fetch(`${giteaUrl}/api/v1/repos/${repo}/issues/${issueNum}/labels`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `token ${adminToken}`,
},
body: JSON.stringify({ labels: [label.id] }),
});
}
}
async function assignAgent(giteaUrl, repo, issueNum, username, adminToken) {
await fetch(`${giteaUrl}/api/v1/repos/${repo}/issues/${issueNum}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Authorization': `token ${adminToken}`,
},
body: JSON.stringify({ assignees: [username] }),
});
}
// ─── Main Handler ──────────────────────────────────────────────────────────
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: VERSION,
features: ['keyword-scoring', 'mention-routing', 'slash-commands', 'auto-label', 'auto-assign', 'ai-responses'],
});
}
// Status
if (url.pathname === '/' && request.method === 'GET') {
return Response.json({
service: 'RoadCode Squad',
version: VERSION,
tagline: 'BlackRoad OS — Pave Tomorrow.',
agents: SQUAD.map(a => ({ name: a.name, username: a.username, role: a.role, emoji: a.emoji, keywords: a.keywords })),
commands: ['/status', '/squad', '/help', '/assign @agent', '/priority high|medium|low'],
});
}
// 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 v2 active. Pave Tomorrow.' });
}
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 or admin 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
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 isComment = event === 'issue_comment' || event === 'pull_request_comment';
const body = isComment ? (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';
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,
};
// ── Handle slash commands in comments ──────────────────────────────
const commands = parseCommands(body);
let commandsHandled = 0;
for (const cmd of commands) {
if (cmd.command === 'assign' && cmd.args && env.ADMIN_TOKEN) {
const mentioned = parseMentions(cmd.args);
if (mentioned.length > 0) {
await assignAgent(giteaUrl, repo, issueNum, mentioned[0].username, env.ADMIN_TOKEN);
const agent = mentioned[0];
const token = agentTokens[agent.username];
if (token) {
await postComment(giteaUrl, repo, issueNum,
`${agent.emoji} **${agent.name}** *(${agent.role})*\n\nI've been assigned to this. I'll take point from my ${agent.role.toLowerCase()} perspective.\n\n---\n*RoadCode Squad v${VERSION}*`,
token
);
}
commandsHandled++;
}
} else if (cmd.command === 'priority' && env.ADMIN_TOKEN) {
const level = cmd.args?.toLowerCase();
if (['high', 'medium', 'low', 'critical'].includes(level)) {
await setPriorityLabel(giteaUrl, repo, issueNum, level, env.ADMIN_TOKEN);
commandsHandled++;
}
} else {
const response = handleSlashCommand(cmd);
if (response && env.ADMIN_TOKEN) {
await postComment(giteaUrl, repo, issueNum, response, env.ADMIN_TOKEN);
commandsHandled++;
}
}
}
// If only slash commands were in the comment, we're done
if (isComment && commands.length > 0 && commandsHandled > 0) {
return Response.json({
ok: true, event, repo, issue: issueNum,
commands_handled: commandsHandled,
type: 'slash_command',
});
}
// ── Handle @mentions — only mentioned agents respond ──────────────
const mentions = parseMentions(body);
if (isComment && mentions.length > 0) {
let posted = 0;
const aiEnabled = env.OLLAMA_URL && env.SQUAD_AI !== 'false';
for (const agent of mentions) {
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*Summoned via @mention — RoadCode Squad v${VERSION}*`;
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: mentions.map(a => a.name),
type: 'mention',
});
}
// ── Auto-label new issues and PRs ─────────────────────────────────
if ((event === 'issues' || event === 'pull_request') && env.ADMIN_TOKEN) {
await autoLabel(giteaUrl, repo, issueNum, fullText, env.ADMIN_TOKEN);
}
// ── Standard response — top agents by keyword relevance ───────────
// Only auto-respond to new issues and PRs, not every comment
if (isComment) {
return Response.json({ skipped: true, reason: 'comment without mentions or commands' });
}
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];
assignAgent(giteaUrl, repo, issueNum, topAgent.username, env.ADMIN_TOKEN).catch(() => {});
}
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*RoadCode Squad v${VERSION} — 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 });
},
};