feat: pulse-dev — dev environment full-stack com hot reload + agentes + taskboard + vault Obsidian
- Stack Swarm 'dev': redis + taskboard + frontend HMR (Vite) + backend HMR (tsx) + 5 agents - TaskBoard standalone React com Redis pub/sub e BLPOP queue - Backend API Express: /tasks /agents /health com hot reload - Agentes: 2 FE (frontend), 2 BE (backend), 1 DevOps (parallel workers) - Vault Obsidian: /root/Obsidian-Pulse/ com estrutura Inbox/Projetos/Docker/Dev/Codex/Logs/Memorias/Templates - Skill obsidian-vault-linker instalada e documentada - Caddy labels: board.octal.tec.br + api.octal.tec.br + frontend.octal.tec.br - Protocolo task queue Redis documentado em MEMORY.md
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import Redis from 'ioredis'
|
||||
const redis = new Redis({ host: 'redis', port: 6379, lazyConnect: true })
|
||||
const QUEUE = 'dev-tasks'
|
||||
const LOG = 'dev-logs'
|
||||
|
||||
function log(level, msg) {
|
||||
const line = JSON.stringify({ ts: new Date().toISOString(), level, msg })
|
||||
redis.publish(LOG, line).catch(() => {})
|
||||
console.log(`[${level}] ${msg}`)
|
||||
}
|
||||
|
||||
async function claimTask(task) {
|
||||
await redis.hset(`agent:agent-backend:task`, task.id, JSON.stringify(task))
|
||||
await redis.hset(`agent:agent-backend`, 'status', 'busy')
|
||||
await redis.hset(`agent:agent-backend`, 'current_task', task.title)
|
||||
await redis.publish(LOG, JSON.stringify({ ts: new Date().toISOString(), level: 'AGENT', msg: `Backend pegou: "${task.title}"` }))
|
||||
task.status = 'in_progress'
|
||||
task.assignee = 'agent-backend'
|
||||
await redis.set(`task:${task.id}`, JSON.stringify(task))
|
||||
log('AGENT', `▶️ Processando: ${task.title}`)
|
||||
await new Promise(r => setTimeout(r, Math.random() * 6000 + 3000))
|
||||
task.status = 'done'
|
||||
task.done_at = new Date().toISOString()
|
||||
await redis.set(`task:${task.id}`, JSON.stringify(task))
|
||||
await redis.hset(`agent:agent-backend`, 'status', 'idle')
|
||||
await redis.hdel(`agent:agent-backend:task`, task.id)
|
||||
await redis.hincrby('agent:agent-backend', 'tasks_done', 1)
|
||||
await awaitingTask(task)
|
||||
}
|
||||
|
||||
async function awaitingTask(task) {
|
||||
await redis.hset(`agent:agent-backend`, 'status', 'idle')
|
||||
await redis.hdel(`agent:agent-backend:task`, task.id)
|
||||
log('AGENT', `✔️ Concluído: ${task.title}`)
|
||||
}
|
||||
|
||||
async function run() {
|
||||
await redis.connect()
|
||||
await redis.set('agent:agent-backend', JSON.stringify({
|
||||
id: 'agent-backend', role: 'backend', status: 'online',
|
||||
started_at: new Date().toISOString(), tasks_done: 0,
|
||||
}))
|
||||
log('AGENT', 'Backend Agent online — aguardando tarefas')
|
||||
while (true) {
|
||||
const [_, taskId] = await redis.blpop(QUEUE, 60)
|
||||
if (!taskId) { await new Promise(r => setTimeout(r, 1000)); continue }
|
||||
const raw = await redis.get(`task:${taskId}`)
|
||||
if (!raw) continue
|
||||
const task = JSON.parse(raw)
|
||||
if (task.domain !== 'backend' && task.domain !== 'fullstack') {
|
||||
await redis.rpush(QUEUE, taskId); continue
|
||||
}
|
||||
await claimTask(task)
|
||||
}
|
||||
}
|
||||
run().catch(e => { console.error(e); process.exit(1) })
|
||||
@@ -0,0 +1,52 @@
|
||||
import Redis from 'ioredis'
|
||||
import { execSync } from 'child_process'
|
||||
const redis = new Redis({ host: 'redis', port: 6379, lazyConnect: true })
|
||||
const QUEUE = 'dev-tasks'
|
||||
const LOG = 'dev-logs'
|
||||
|
||||
function log(level, msg) {
|
||||
const line = JSON.stringify({ ts: new Date().toISOString(), level, msg })
|
||||
redis.publish(LOG, line).catch(() => {})
|
||||
console.log(`[${level}] ${msg}`)
|
||||
}
|
||||
|
||||
function exec(cmd) {
|
||||
try { return execSync(cmd, { encoding: 'utf8', timeout: 30000 }).trim() }
|
||||
catch(e) { return `ERROR: ${e.message}` }
|
||||
}
|
||||
|
||||
async function processTask(task) {
|
||||
task.status = 'in_progress'
|
||||
task.assignee = 'agent-devops'
|
||||
await redis.set(`task:${task.id}`, JSON.stringify(task))
|
||||
log('AGENT', `▶️ Processando: ${task.title}`)
|
||||
const result = exec(task.command || `echo "${task.title}"`)
|
||||
task.result = result
|
||||
task.status = 'done'
|
||||
task.done_at = new Date().toISOString()
|
||||
await redis.set(`task:${task.id}`, JSON.stringify(task))
|
||||
log('AGENT', `✔️ Concluído: ${task.title}`)
|
||||
return result
|
||||
}
|
||||
|
||||
async function run() {
|
||||
await redis.connect()
|
||||
await redis.set('agent:agent-devops', JSON.stringify({
|
||||
id: 'agent-devops', role: 'devops', status: 'online',
|
||||
started_at: new Date().toISOString(), tasks_done: 0,
|
||||
}))
|
||||
log('AGENT', 'DevOps Agent online — aguardando tarefas')
|
||||
while (true) {
|
||||
const [_, tid] = await redis.blpop(QUEUE, 60)
|
||||
if (!tid) { await new Promise(r => setTimeout(r, 1000)); continue }
|
||||
const raw = await redis.get(`task:${tid}`)
|
||||
if (!raw) continue
|
||||
const task = JSON.parse(raw)
|
||||
if (task.domain !== 'devops') {
|
||||
await redis.rpush(QUEUE, tid); continue
|
||||
}
|
||||
await processTask(task)
|
||||
await redis.hincrby('agent:agent-devops', 'tasks_done', 1)
|
||||
}
|
||||
}
|
||||
run().catch(e => { console.error(e); process.exit(1) })
|
||||
@@ -0,0 +1,55 @@
|
||||
import Redis from 'ioredis'
|
||||
const redis = new Redis({ host: 'redis', port: 6379, lazyConnect: true })
|
||||
const QUEUE = 'dev-tasks'
|
||||
const LOG = 'dev-logs'
|
||||
|
||||
function log(level, msg) {
|
||||
const line = JSON.stringify({ ts: new Date().toISOString(), level, msg })
|
||||
redis.publish(LOG, line).catch(() => {})
|
||||
console.log(`[${level}] ${msg}`)
|
||||
}
|
||||
|
||||
async function claimTask(task) {
|
||||
await redis.hset(`agent:agent-frontend:task`, task.id, JSON.stringify(task))
|
||||
await redis.hset(`agent:agent-frontend`, 'status', 'busy')
|
||||
await redis.hset(`agent:agent-frontend`, 'current_task', task.title)
|
||||
await redis.publish(LOG, JSON.stringify({ ts: new Date().toISOString(), level: 'AGENT', msg: `Frontend pegou: "${task.title}"` }))
|
||||
task.status = 'in_progress'
|
||||
task.assignee = 'agent-frontend'
|
||||
await redis.set(`task:${task.id}`, JSON.stringify(task))
|
||||
log('AGENT', `▶️ Processando: ${task.title}`)
|
||||
await new Promise(r => setTimeout(r, Math.random() * 5000 + 2000))
|
||||
task.status = 'done'
|
||||
task.done_at = new Date().toISOString()
|
||||
await redis.set(`task:${task.id}`, JSON.stringify(task))
|
||||
await redis.hset(`agent:agent-frontend`, 'status', 'idle')
|
||||
await redis.hdel(`agent:agent-frontend:task`, task.id)
|
||||
awaitingTask(task)
|
||||
}
|
||||
|
||||
async function awaitingTask(task) {
|
||||
await redis.hset(`agent:agent-frontend`, 'status', 'idle')
|
||||
await redis.hdel(`agent:agent-frontend:task`, task.id)
|
||||
log('AGENT', `✔️ Concluído: ${task.title}`)
|
||||
}
|
||||
|
||||
async function run() {
|
||||
await redis.connect()
|
||||
await redis.set('agent:agent-frontend', JSON.stringify({
|
||||
id: 'agent-frontend', role: 'frontend', status: 'online',
|
||||
started_at: new Date().toISOString(), tasks_done: 0,
|
||||
}))
|
||||
log('AGENT', 'Frontend Agent online — aguardando tarefas')
|
||||
while (true) {
|
||||
const [_, taskId] = await redis.blpop(QUEUE, 60)
|
||||
if (!taskId) { await new Promise(r => setTimeout(r, 1000)); continue }
|
||||
const raw = await redis.get(`task:${taskId}`)
|
||||
if (!raw) continue
|
||||
const task = JSON.parse(raw)
|
||||
if (task.domain !== 'frontend' && task.domain !== 'fullstack') {
|
||||
await redis.rpush(QUEUE, taskId); continue
|
||||
}
|
||||
await claimTask(task)
|
||||
}
|
||||
}
|
||||
run().catch(e => { console.error(e); process.exit(1) })
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "pulse-agents",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"agent-frontend": "node /app/agents/frontend/agent.js",
|
||||
"agent-backend": "node /app/agents/backend/agent.js",
|
||||
"agent-devops": "node /app/agents/devops/agent.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"ioredis": "^5.4.1",
|
||||
"uuid": "^10.0.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user