[ADN] Fase 10: Saneamiento y Consolidación - Un solo punto de verdad
- S1: Eliminar 'triggers' duplicado en run (líneas 109/131) - S2: Eliminar cli/commit.rb (truncado, sin uso) - S3: Eliminar cli/inicio.rb y cli/cierre.rb (legacy, jornada.rb los reemplaza) - S4: Mover 4 planes obsoletos de docs/plan/adn/ a docs/_hist/plan/adn/ - S5: Mover 14 backups de planes y 3 docs técnicos a docs/_hist/ - S6: Eliminar manifiesto vacío P2604_proyecto_p2604.md - S7: Corregir referencia a plan obsoleto en run - S8: Dejar de cargar core/validador.rb obsoleto en run - S9: Limpiar progreso duplicado en P2604_mejoras_ADN.md - Agregar Fase 10 al plan P2604 con 10 tareas - Un solo punto de verdad: adn/README.md - Un solo plan: docs/proy/p2604_mejoras_ADN/P2604_mejoras_ADN.md - Registro en bitácora: evento 1073
This commit is contained in:
@@ -0,0 +1,29 @@
|
|||||||
|
name: Lint ADN
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'adn/tools/**/*.rb'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'adn/tools/**/*.rb'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: '3.1'
|
||||||
|
|
||||||
|
- name: Install RuboCop
|
||||||
|
run: gem install rubocop --no-document
|
||||||
|
|
||||||
|
- name: Run RuboCop
|
||||||
|
run: |
|
||||||
|
cd adn/tools
|
||||||
|
rubocop --force-exclusion --config .rubocop.yml || true
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
name: Tests ADN
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, develop]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: '3.1'
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
cd adn/tools
|
||||||
|
bundle install
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
cd adn/tools
|
||||||
|
bundle exec rspec --format progress
|
||||||
|
|
||||||
|
- name: Run linter
|
||||||
|
run: |
|
||||||
|
cd adn/tools
|
||||||
|
gem install rubocop --no-document || true
|
||||||
|
rubocop --simple || true
|
||||||
+17
-5
@@ -152,6 +152,7 @@ referencia canónica y exclusiva de las directivas que un agente IA debe seguir.
|
|||||||
| `generar nodo <nombre>` | Generar ficha de nodo en DB | DB-First |
|
| `generar nodo <nombre>` | Generar ficha de nodo en DB | DB-First |
|
||||||
| `generar proyecto <código>` | Generar manifiesto de proyecto en DB | DB-First |
|
| `generar proyecto <código>` | Generar manifiesto de proyecto en DB | DB-First |
|
||||||
| `conocimiento asimilar <archivo>` | Asimilar documentación al ADN | Almacena en DB |
|
| `conocimiento asimilar <archivo>` | Asimilar documentación al ADN | Almacena en DB |
|
||||||
|
| `msp <subcomando>` | Gestión de MSPs NotebookLM (auditar, registrar, listar) | Integración MCP |
|
||||||
| `ayuda [subcomando]` | Mostrar ayuda detallada | - |
|
| `ayuda [subcomando]` | Mostrar ayuda detallada | - |
|
||||||
| `validador [--watch] [--fix]` | ⚠️ **OBSOLETO** - Validación MD-First antigua | MD-First (evitar) |
|
| `validador [--watch] [--fix]` | ⚠️ **OBSOLETO** - Validación MD-First antigua | MD-First (evitar) |
|
||||||
|
|
||||||
@@ -210,7 +211,13 @@ referencia canónica y exclusiva de las directivas que un agente IA debe seguir.
|
|||||||
# 11. VER ESTADÍSTICAS DEL SISTEMA
|
# 11. VER ESTADÍSTICAS DEL SISTEMA
|
||||||
./adn/tools/run db estadisticas
|
./adn/tools/run db estadisticas
|
||||||
|
|
||||||
# 12. ACCEDER A INTERFAZ WEB (DB → Web)
|
# 12. AUDITAR MSPs (NotebookLM)
|
||||||
|
./adn/tools/run msp auditar
|
||||||
|
|
||||||
|
# 13. LISTAR MSPs DISPONIBLES
|
||||||
|
./adn/tools/run msp listar
|
||||||
|
|
||||||
|
# 14. ACCEDER A INTERFAZ WEB (DB → Web)
|
||||||
# http://localhost:5174 (Frontend Vite)
|
# http://localhost:5174 (Frontend Vite)
|
||||||
# http://localhost:3002 (API Node.js)
|
# http://localhost:3002 (API Node.js)
|
||||||
```
|
```
|
||||||
@@ -224,7 +231,8 @@ referencia canónica y exclusiva de las directivas que un agente IA debe seguir.
|
|||||||
2. **Usar subcomandos `db`**: Para cualquier operación CRUD, usar `./adn/tools/run db <subcomando>`.
|
2. **Usar subcomandos `db`**: Para cualquier operación CRUD, usar `./adn/tools/run db <subcomando>`.
|
||||||
3. **Auto-Registro en DB**: Toda acción autónoma de la IA DEBE registrarse como entrada en la bitácora del día usando `db evento:crear`.
|
3. **Auto-Registro en DB**: Toda acción autónoma de la IA DEBE registrarse como entrada en la bitácora del día usando `db evento:crear`.
|
||||||
4. **Ruby como lenguaje de desarrollo**: Todos los componentes nuevos deben escribirse en Ruby, siguiendo principios "Menos es Más" (atómicos, reutilizables).
|
4. **Ruby como lenguaje de desarrollo**: Todos los componentes nuevos deben escribirse en Ruby, siguiendo principios "Menos es Más" (atómicos, reutilizables).
|
||||||
5. **Validador obsoleto**: NO usar `validador` (orientado a MD-First). En su lugar, validar coherencia mediante consultas a la base de datos.
|
5. **Reutilizar módulos core**: Antes de crear nueva lógica, verificar si existe módulo en `core/` (ej: `NodosInfo`, `HelpFormatter`, `ErrorHandler`). Duplicar código está prohibido.
|
||||||
|
6. **Validador obsoleto**: NO usar `validador` (orientado a MD-First). En su lugar, validar coherencia mediante consultas a la base de datos.
|
||||||
6. **Secretos en bóveda**: NUNCA exponer contraseñas en texto plano. Usar `candados` (`eval $(ruby adn/tools/seguridad/candados.rb load <clave>)`).
|
6. **Secretos en bóveda**: NUNCA exponer contraseñas en texto plano. Usar `candados` (`eval $(ruby adn/tools/seguridad/candados.rb load <clave>)`).
|
||||||
7. **Topología primero**: Antes de conectarte a un nodo, consultar `./adn/tools/run contexto <nodo>` para entender su ubicación en la red.
|
7. **Topología primero**: Antes de conectarte a un nodo, consultar `./adn/tools/run contexto <nodo>` para entender su ubicación en la red.
|
||||||
8. **Exportación opcional**: Los archivos `.md` son solo para archivo histórico. La operación real ocurre en PostgreSQL → Web.
|
8. **Exportación opcional**: Los archivos `.md` son solo para archivo histórico. La operación real ocurre en PostgreSQL → Web.
|
||||||
@@ -243,22 +251,26 @@ adn/tools/
|
|||||||
│ ├── backup.rb ← Backup seguro de nodos
|
│ ├── backup.rb ← Backup seguro de nodos
|
||||||
│ ├── cierre.rb ← Cierre de jornada (DB-First)
|
│ ├── cierre.rb ← Cierre de jornada (DB-First)
|
||||||
│ ├── conocimiento.rb ← Asimilación de documentación
|
│ ├── conocimiento.rb ← Asimilación de documentación
|
||||||
│ ├── contexto.rb ← Contexto topológico de nodos
|
│ ├── contexto.rb ← Contexto topológico de nodos (usa NodosInfo)
|
||||||
│ ├── db.rb ← Dispatcher de subcomandos DB
|
│ ├── db.rb ← Dispatcher de subcomandos DB
|
||||||
│ ├── db/ ← Subcomandos específicos de DB (evento, bitacora, nodo, etc.)
|
│ ├── db/ ← Subcomandos específicos de DB (evento, bitacora, nodo, etc.)
|
||||||
│ ├── generar.rb ← Generación de bitácoras, nodos y proyectos
|
│ ├── generar.rb ← Generación de bitácoras, nodos y proyectos
|
||||||
│ ├── inicio.rb ← Inicio de jornada (DB-First)
|
│ ├── inicio.rb ← Inicio de jornada (DB-First)
|
||||||
│ ├── nodos.rb ← Agrupación de nodos por servidor
|
│ ├── msp.rb ← Gestión de MSPs NotebookLM
|
||||||
|
│ ├── nodos.rb ← Agrupación de nodos por servidor (usa NodosInfo)
|
||||||
│ ├── plan.rb ← Gestión de planes y progreso
|
│ ├── plan.rb ← Gestión de planes y progreso
|
||||||
│ ├── salud.rb ← Métricas de salud del ADN
|
│ ├── salud.rb ← Métricas de salud del ADN
|
||||||
│ ├── triggers.rb ← Gestión del motor de triggers
|
│ ├── triggers.rb ← Gestión del motor de triggers
|
||||||
│ └── validador.rb ← Validación de cumplimiento ADN
|
│ └── validador.rb ← Validación de cumplimiento ADN
|
||||||
├── core/ ← Módulos centrales (Singleton)
|
├── core/ ← Módulos centrales reutilizables
|
||||||
│ ├── colores.rb ← Constantes de color para terminal
|
│ ├── colores.rb ← Constantes de color para terminal
|
||||||
│ ├── conciliador.rb ← Saneamiento preventivo de pendientes
|
│ ├── conciliador.rb ← Saneamiento preventivo de pendientes
|
||||||
│ ├── configurador.rb ← Carga de configuración YAML
|
│ ├── configurador.rb ← Carga de configuración YAML
|
||||||
|
│ ├── error_handler.rb ← Helper para mensajes de error estandarizados
|
||||||
│ ├── eventos.rb ← Bus de eventos (pub/sub desacoplado)
|
│ ├── eventos.rb ← Bus de eventos (pub/sub desacoplado)
|
||||||
|
│ ├── help_formatter.rb ← Formateador de ayuda estandarizado
|
||||||
│ ├── logger.rb ← Logger estructurado (JSON)
|
│ ├── logger.rb ← Logger estructurado (JSON)
|
||||||
|
│ ├── nodos_info.rb ← Utilidades para extracción de metadatos de fichas
|
||||||
│ ├── triggers.rb ← Motor de triggers (reacción automática)
|
│ ├── triggers.rb ← Motor de triggers (reacción automática)
|
||||||
│ └── validador.rb ← Validador de formato ADN
|
│ └── validador.rb ← Validador de formato ADN
|
||||||
├── db/ ← Capa de acceso a datos (PostgreSQL)
|
├── db/ ← Capa de acceso a datos (PostgreSQL)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
---
|
---
|
||||||
BUNDLE_PATH: "vendor/bundle"
|
BUNDLE_PATH: "vendor/bundle"
|
||||||
BUNDLE_WITHOUT: "development:test:future"
|
BUNDLE_WITHOUT: "future"
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
AllCops:
|
||||||
|
NewCops: enable
|
||||||
|
Exclude:
|
||||||
|
- 'vendor/**/*'
|
||||||
|
TargetRubyVersion: 3.1
|
||||||
|
|
||||||
|
Style/FrozenStringLiteralComment:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Style/Documentation:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Metrics/MethodLength:
|
||||||
|
Max: 30
|
||||||
|
|
||||||
|
Layout/LineLength:
|
||||||
|
Max: 120
|
||||||
@@ -33,6 +33,7 @@ class SubcomandoAyuda
|
|||||||
salud [--dashboard] [--json] Ver salud general del sistema ADN
|
salud [--dashboard] [--json] Ver salud general del sistema ADN
|
||||||
generar bitacora [fecha] Generar nueva bitácora (fecha: YYYY-MM-DD)
|
generar bitacora [fecha] Generar nueva bitácora (fecha: YYYY-MM-DD)
|
||||||
generar nodo <nombre> Generar ficha de nodo
|
generar nodo <nombre> Generar ficha de nodo
|
||||||
|
msp <subcomando> Gestión de MSPs NotebookLM (auditar, listar, registrar)
|
||||||
#{Color::YELLOW}generar proyecto <código>#{Color::RESET} Generar manifiesto de proyecto
|
#{Color::YELLOW}generar proyecto <código>#{Color::RESET} Generar manifiesto de proyecto
|
||||||
#{Color::YELLOW}tailscale <acción>#{Color::RESET} Gestión de identidades Tailscale
|
#{Color::YELLOW}tailscale <acción>#{Color::RESET} Gestión de identidades Tailscale
|
||||||
#{Color::YELLOW}backup <nodo> [--mode MODO]#{Color::RESET} Ejecutar backup seguro de un nodo
|
#{Color::YELLOW}backup <nodo> [--mode MODO]#{Color::RESET} Ejecutar backup seguro de un nodo
|
||||||
@@ -161,6 +162,22 @@ class SubcomandoAyuda
|
|||||||
puts "Ejemplos:"
|
puts "Ejemplos:"
|
||||||
puts " ./adn/tools/run tailscale switch dasuten"
|
puts " ./adn/tools/run tailscale switch dasuten"
|
||||||
puts " ./adn/tools/run tailscale status"
|
puts " ./adn/tools/run tailscale status"
|
||||||
|
when 'msp'
|
||||||
|
puts "Uso: ./adn/tools/run msp <subcomando> [opciones]"
|
||||||
|
puts ""
|
||||||
|
puts "Gestión de MSPs (Markdown Style Papers) de NotebookLM:"
|
||||||
|
puts " • auditar Verificar estado de conexión MCP y NotebookLM"
|
||||||
|
puts " • listar Listar MSPs disponibles en el sistema"
|
||||||
|
puts " • registrar Registrar un MSP descargado"
|
||||||
|
puts ""
|
||||||
|
puts "Opciones:"
|
||||||
|
puts " --archivo RUTA Ruta al archivo MSP (para registrar)"
|
||||||
|
puts " --titulo TITULO Título del MSP (opcional)"
|
||||||
|
puts ""
|
||||||
|
puts "Ejemplos:"
|
||||||
|
puts " ./adn/tools/run msp auditar"
|
||||||
|
puts " ./adn/tools/run msp listar"
|
||||||
|
puts " ./adn/tools/run msp registrar --archivo docs/mcps/mi-msp.md"
|
||||||
else
|
else
|
||||||
puts "#{Color::RED}✗ Subcomando desconocido: #{subcomando}#{Color::RESET}"
|
puts "#{Color::RED}✗ Subcomando desconocido: #{subcomando}#{Color::RESET}"
|
||||||
puts "Usa './adn/tools/run ayuda' para ver la lista de subcomandos."
|
puts "Usa './adn/tools/run ayuda' para ver la lista de subcomandos."
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# adn/tools/cli/cierre.rb — Subcomando para cierre de jornada laboral (DB-First)
|
|
||||||
# ==============================================================================
|
|
||||||
|
|
||||||
require 'date'
|
|
||||||
require 'time'
|
|
||||||
require_relative '../db/core/bitacora_db'
|
|
||||||
|
|
||||||
module ADN
|
|
||||||
class SubcomandoCierre
|
|
||||||
require_relative '../core/colores'
|
|
||||||
|
|
||||||
def initialize(args, logger)
|
|
||||||
@args = args
|
|
||||||
@logger = logger
|
|
||||||
@fecha = Date.today
|
|
||||||
end
|
|
||||||
|
|
||||||
def ejecutar
|
|
||||||
if @args.empty? || !(@args.include?('jornada'))
|
|
||||||
puts "#{Color::YELLOW}Uso: ./adn/tools/run cierre de jornada [presencial|remoto]#{Color::RESET}"
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
modo = @args.include?('presencial') ? 'P' : 'R'
|
|
||||||
tipo_str = modo == 'P' ? 'Presencial' : 'Remoto'
|
|
||||||
hora_fin = Time.now.strftime('%H:%M')
|
|
||||||
|
|
||||||
@logger.info("Iniciando cierre de jornada #{tipo_str} (Modo DB-First)")
|
|
||||||
|
|
||||||
BitacorasDB::BitacoraDB.with_connection do |db|
|
|
||||||
# 1. Buscar la bitácora
|
|
||||||
bitacora = db.find_bitacora_by_fecha(@fecha)
|
|
||||||
unless bitacora
|
|
||||||
@logger.error("No existe bitácora en DB para hoy (#{@fecha}). ¿Iniciaste la jornada?")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
# 2. Obtener nodo srv-ns8
|
|
||||||
nodo = db.find_nodo_by_nombre('srv-ns8')
|
|
||||||
|
|
||||||
# 3. Crear entrada de cierre
|
|
||||||
db.create_entrada(
|
|
||||||
inicio: hora_fin,
|
|
||||||
fin: hora_fin,
|
|
||||||
descripcion: "Cierre de jornada #{tipo_str.downcase}. Sincronización final y validación de backups.",
|
|
||||||
estado: '✅',
|
|
||||||
modo: modo,
|
|
||||||
bitacora_id: bitacora['id'],
|
|
||||||
nodo_id: nodo ? nodo['id'] : nil
|
|
||||||
)
|
|
||||||
|
|
||||||
@logger.exito("Cierre de jornada registrado en Base de Datos.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# CLI: commit.rb - Herramienta para gestión de commits según principios ADN
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Principios aplicados:
|
|
||||||
# 1. Menos es Más: Componente atómico y reutilizable para gestión de commits
|
|
||||||
# 2. Armonía Integral: Val
|
|
||||||
+28
-49
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
require 'optparse'
|
require 'optparse'
|
||||||
require_relative '../core/colores'
|
require_relative '../core/colores'
|
||||||
|
require_relative '../core/nodos_info'
|
||||||
|
|
||||||
class SubcomandoContexto
|
class SubcomandoContexto
|
||||||
def initialize(args, logger)
|
def initialize(args, logger)
|
||||||
@@ -60,77 +61,55 @@ class SubcomandoContexto
|
|||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
contenido = File.read(ruta_nodo, encoding: 'UTF-8')
|
metadata = ADN::NodosInfo.extraer_metadata(nombre_nodo)
|
||||||
|
|
||||||
# Extraer información básica
|
|
||||||
puts "#{Color::CYAN}📋 Contexto del nodo: #{nombre_nodo}#{Color::RESET}"
|
puts "#{Color::CYAN}📋 Contexto del nodo: #{nombre_nodo}#{Color::RESET}"
|
||||||
puts "#{Color::DIM}#{'-' * 60}#{Color::RESET}"
|
puts "#{Color::DIM}#{'-' * 60}#{Color::RESET}"
|
||||||
|
|
||||||
# Buscar IP en la tabla
|
if metadata
|
||||||
if contenido.match(/\|\s*IP (?:LAN|Pública)?\s*\|\s*([^\|]+)\s*\|/)
|
puts " #{Color::GREEN}IP:#{Color::RESET} #{metadata[:ip] || '-'}"
|
||||||
ip = $1.strip
|
puts " #{Color::GREEN}Rol:#{Color::RESET} #{metadata[:host] ? 'VM' : (metadata[:so] || '-')}"
|
||||||
puts " #{Color::GREEN}IP:#{Color::RESET} #{ip}"
|
puts " #{Color::GREEN}SO:#{Color::RESET} #{metadata[:so] || '-'}"
|
||||||
|
puts " #{Color::GREEN}Host:#{Color::RESET} #{metadata[:host] || '-'}"
|
||||||
|
puts " #{Color::GREEN}SSH:#{Color::RESET} #{metadata[:usuario] || 'rmonla'}@#{metadata[:ip] || '?'}:#{metadata[:puerto_ssh] || 22}"
|
||||||
|
|
||||||
if @opciones[:cmd]
|
if @opciones[:cmd]
|
||||||
generar_comando_ssh(nombre_nodo, ip)
|
generar_comando_ssh(nombre_nodo, metadata[:ip], metadata[:usuario])
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Buscar rol
|
# Buscar en ontología para contexto de red
|
||||||
if contenido.match(/\|\s*Rol(?: Principal)?\s*\|\s*([^\|]+)\s*\|/)
|
ontologia_path = File.join(ADN::PROJECT_ROOT, 'adn', '01_ontologia.md')
|
||||||
rol = $1.strip
|
if File.exist?(ontologia_path)
|
||||||
puts " #{Color::GREEN}Rol:#{Color::RESET} #{rol}"
|
ontologia = File.read(ontologia_path, encoding: 'UTF-8')
|
||||||
end
|
if ontologia.include?(nombre_nodo)
|
||||||
|
clusters = ['srv-pmox1', 'srv-pmox2', 'srv-pmox3', 'srv-xen1', 'srv-dasu']
|
||||||
# Buscar sistema operativo
|
cluster = clusters.find { |c| ontologia.include?("**#{c}**") && ontologia.include?(nombre_nodo) }
|
||||||
if contenido.match(/\|\s*Sistema Operativo\s*\|\s*([^\|]+)\s*\|/)
|
if cluster && cluster != 'srv-ns8'
|
||||||
so = $1.strip
|
|
||||||
puts " #{Color::GREEN}SO:#{Color::RESET} #{so}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Buscar en ontología para contexto de red
|
|
||||||
config_path = File.join(__dir__, '../config', 'config.yml')
|
|
||||||
ontologia_path = File.join(ADN::PROJECT_ROOT, 'adn', '01_ontologia.md')
|
|
||||||
if File.exist?(ontologia_path)
|
|
||||||
ontologia = File.read(ontologia_path, encoding: 'UTF-8')
|
|
||||||
if ontologia.include?(nombre_nodo)
|
|
||||||
puts " #{Color::GREEN}En ontología:#{Color::RESET} Sí"
|
|
||||||
|
|
||||||
# Intentar encontrar en qué cluster está
|
|
||||||
clusters = ['srv-pmox1', 'srv-pmox2', 'srv-pmox3', 'srv-xen1', 'srv-dasu']
|
|
||||||
cluster = clusters.find { |c| ontologia.include?("**#{c}**") && ontologia.include?(nombre_nodo) }
|
|
||||||
|
|
||||||
if cluster
|
|
||||||
puts " #{Color::GREEN}Cluster:#{Color::RESET} #{cluster}"
|
|
||||||
|
|
||||||
# Determinar si necesita ProxyJump
|
|
||||||
if cluster != 'srv-ns8' && !nombre_nodo.start_with?('srv-')
|
|
||||||
puts " #{Color::YELLOW}🔧 Acceso probable via ProxyJump:#{Color::RESET}"
|
puts " #{Color::YELLOW}🔧 Acceso probable via ProxyJump:#{Color::RESET}"
|
||||||
puts " ssh -J root@#{cluster} rmonla@#{nombre_nodo}"
|
puts " ssh -J root@#{cluster} rmonla@#{nombre_nodo}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
puts "#{Color::YELLOW}⚠ No se pudo extraer metadata del nodo#{Color::RESET}"
|
||||||
end
|
end
|
||||||
|
|
||||||
puts "#{Color::DIM}#{'-' * 60}#{Color::RESET}"
|
puts "#{Color::DIM}#{'-' * 60}#{Color::RESET}"
|
||||||
@logger.exito("Contexto obtenido para nodo: #{nombre_nodo}")
|
@logger.exito("Contexto obtenido para nodo: #{nombre_nodo}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def generar_comando_ssh(nodo, ip)
|
def generar_comando_ssh(nodo, ip, usuario = nil)
|
||||||
|
usuario ||= if nodo.start_with?('srvv-') || nodo.start_with?('pcv-')
|
||||||
|
'rmonla'
|
||||||
|
elsif nodo.start_with?('srv-') && nodo != 'srv-ns8'
|
||||||
|
'root'
|
||||||
|
else
|
||||||
|
'rmonla'
|
||||||
|
end
|
||||||
|
|
||||||
puts "\n#{Color::CYAN}🔧 Comando SSH sugerido:#{Color::RESET}"
|
puts "\n#{Color::CYAN}🔧 Comando SSH sugerido:#{Color::RESET}"
|
||||||
|
|
||||||
# Determinar usuario basado en el nodo
|
|
||||||
usuario = if nodo.start_with?('srvv-') || nodo.start_with?('pcv-')
|
|
||||||
'rmonla'
|
|
||||||
elsif nodo.start_with?('srv-') && nodo != 'srv-ns8'
|
|
||||||
'root'
|
|
||||||
else
|
|
||||||
'rmonla'
|
|
||||||
end
|
|
||||||
|
|
||||||
puts " ssh #{usuario}@#{ip}"
|
puts " ssh #{usuario}@#{ip}"
|
||||||
|
|
||||||
# Si no es srv-ns8 y no es un hypervisor, sugerir usando nombre
|
|
||||||
unless nodo == 'srv-ns8' || nodo.start_with?('srv-pmox') || nodo.start_with?('srv-xen')
|
unless nodo == 'srv-ns8' || nodo.start_with?('srv-pmox') || nodo.start_with?('srv-xen')
|
||||||
puts " #{Color::DIM}O usando nombre (si está en ~/.ssh/config):#{Color::RESET}"
|
puts " #{Color::DIM}O usando nombre (si está en ~/.ssh/config):#{Color::RESET}"
|
||||||
puts " ssh #{nodo}"
|
puts " ssh #{nodo}"
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# adn/tools/cli/inicio.rb — Subcomando para inicio de jornada laboral (DB-First)
|
|
||||||
# ==============================================================================
|
|
||||||
|
|
||||||
require 'date'
|
|
||||||
require 'time'
|
|
||||||
require_relative '../db/core/bitacora_db'
|
|
||||||
|
|
||||||
module ADN
|
|
||||||
class SubcomandoInicio
|
|
||||||
require_relative '../core/colores'
|
|
||||||
|
|
||||||
def initialize(args, logger)
|
|
||||||
@args = args
|
|
||||||
@logger = logger
|
|
||||||
@fecha = Date.today
|
|
||||||
end
|
|
||||||
|
|
||||||
def ejecutar
|
|
||||||
if @args.empty? || !(@args.include?('jornada'))
|
|
||||||
puts "#{Color::YELLOW}Uso: ./adn/tools/run inicio de jornada [presencial|remoto]#{Color::RESET}"
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
modo = @args.include?('presencial') ? 'P' : 'R'
|
|
||||||
tipo_str = modo == 'P' ? 'Presencial' : 'Remoto'
|
|
||||||
hora_ini = Time.now.strftime('%H:%M')
|
|
||||||
|
|
||||||
@logger.info("Iniciando jornada #{tipo_str} (Modo DB-First)")
|
|
||||||
|
|
||||||
BitacorasDB::BitacoraDB.with_connection do |db|
|
|
||||||
# 1. Asegurar que existe la bitácora
|
|
||||||
bitacora = db.find_bitacora_by_fecha(@fecha)
|
|
||||||
unless bitacora
|
|
||||||
@logger.info("Creando bitácora en DB para hoy...")
|
|
||||||
bitacora = db.create_bitacora(fecha: @fecha)
|
|
||||||
end
|
|
||||||
|
|
||||||
# 2. Obtener nodo srv-ns8
|
|
||||||
nodo = db.find_nodo_by_nombre('srv-ns8')
|
|
||||||
unless nodo
|
|
||||||
@logger.warn("Nodo 'srv-ns8' no encontrado, usando nodo general...")
|
|
||||||
# Podríamos crearlo o buscar uno por defecto
|
|
||||||
end
|
|
||||||
|
|
||||||
# 3. Crear entrada de inicio
|
|
||||||
db.create_entrada(
|
|
||||||
inicio: hora_ini,
|
|
||||||
descripcion: "Inicio de jornada #{tipo_str.downcase}. Sincronización de contexto y revisión de tareas.",
|
|
||||||
estado: '👁️',
|
|
||||||
modo: modo,
|
|
||||||
bitacora_id: bitacora['id'],
|
|
||||||
nodo_id: nodo ? nodo['id'] : nil
|
|
||||||
)
|
|
||||||
|
|
||||||
@logger.exito("Entrada de inicio creada en Base de Datos.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'optparse'
|
||||||
|
require 'json'
|
||||||
|
require 'open3'
|
||||||
|
require_relative '../core/colores'
|
||||||
|
require_relative '../core/help_formatter'
|
||||||
|
|
||||||
|
class SubcomandoMsp
|
||||||
|
def initialize(args, logger)
|
||||||
|
@args = args
|
||||||
|
@logger = logger
|
||||||
|
@opciones = {}
|
||||||
|
@subcommand = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def ejecutar
|
||||||
|
@subcommand = @args.shift || 'auditar'
|
||||||
|
|
||||||
|
case @subcommand
|
||||||
|
when 'auditar'
|
||||||
|
auditar_msp
|
||||||
|
when 'registrar'
|
||||||
|
registrar_msp
|
||||||
|
when 'listar'
|
||||||
|
listar_msp
|
||||||
|
when 'help', '--help', '-h'
|
||||||
|
mostrar_ayuda
|
||||||
|
else
|
||||||
|
puts "#{Color::RED}✗ Subcomando desconocido: #{@subcommand}#{Color::RESET}"
|
||||||
|
puts "Usa: ./adn/tools/run msp --help"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def mostrar_ayuda
|
||||||
|
puts ADN::HelpFormatter.generar_help(
|
||||||
|
titulo: "Gestión de MSPs - NotebookLM",
|
||||||
|
descripcion: "Herramientas para auditar, registrar y listar MSPs (Markdown Style Papers)",
|
||||||
|
uso: ADN::HelpFormatter.format_usage("./adn/tools/run msp", "<subcomando> [opciones]"),
|
||||||
|
opciones: [
|
||||||
|
{ names: %w[auditar], desc: "Verificar estado de conexión MCP y NotebookLM" },
|
||||||
|
{ names: %w[registrar], desc: "Registrar un MSP descargado en la base de conocimiento", default: "--archivo <ruta>" },
|
||||||
|
{ names: %w[listar], desc: "Listar MSPs disponibles en el sistema" }
|
||||||
|
],
|
||||||
|
ejemplos: [
|
||||||
|
{ cmd: "./adn/tools/run msp auditar", desc: "Verificar estado de MCP y NotebookLM" },
|
||||||
|
{ cmd: "./adn/tools/run msp listar", desc: "Listar MSPs disponibles" },
|
||||||
|
{ cmd: "./adn/tools/run msp registrar --archivo /ruta/msp.md", desc: "Registrar un MSP" }
|
||||||
|
]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def auditar_msp
|
||||||
|
parser = OptionParser.new do |opts|
|
||||||
|
opts.banner = "Uso: ./adn/tools/run msp auditar"
|
||||||
|
opts.on("-h", "--help", "Mostrar ayuda") do
|
||||||
|
puts opts
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
parser.parse!(@args)
|
||||||
|
|
||||||
|
@logger.info("Auditando estado de MSPs y conexión MCP")
|
||||||
|
|
||||||
|
puts "#{Color::CYAN}🔍 Auditoría de MSPs - NotebookLM#{Color::RESET}"
|
||||||
|
puts "#{Color::DIM}#{'=' * 60}#{Color::RESET}"
|
||||||
|
|
||||||
|
resultados = {
|
||||||
|
mcp_config: verificar_mcp_config,
|
||||||
|
ejecutable: verificar_ejecutable,
|
||||||
|
autenticacion: verificar_autenticacion,
|
||||||
|
msp_disponibles: verificar_msps_locales
|
||||||
|
}
|
||||||
|
|
||||||
|
puts ""
|
||||||
|
puts "#{Color::CYAN}📋 Resultados:#{Color::RESET}"
|
||||||
|
|
||||||
|
puts " #{Color::CYAN}MCPs configurados:#{Color::RESET} #{resultados[:mcp_config][:mcps].join(', ')}"
|
||||||
|
|
||||||
|
if resultados[:mcp_config][:estado] == 'ok'
|
||||||
|
puts " #{Color::GREEN}✅ Archivo de configuración#{Color::RESET}"
|
||||||
|
else
|
||||||
|
puts " #{Color::RED}❌ Error: #{resultados[:mcp_config][:error]}#{Color::RESET}"
|
||||||
|
end
|
||||||
|
|
||||||
|
puts ""
|
||||||
|
if resultados[:ejecutable][:existe]
|
||||||
|
puts " #{Color::CYAN}Ejecutable NotebookLM:#{Color::RESET}"
|
||||||
|
puts " #{Color::GREEN}✅ Existe#{Color::RESET}"
|
||||||
|
puts " #{Color::DIM}#{resultados[:ejecutable][:ruta]}#{Color::RESET}"
|
||||||
|
else
|
||||||
|
puts " #{Color::CYAN}Ejecutable NotebookLM:#{Color::RESET}"
|
||||||
|
puts " #{Color::RED}❌ No encontrado#{Color::RESET}"
|
||||||
|
end
|
||||||
|
|
||||||
|
puts ""
|
||||||
|
puts " #{Color::CYAN}Autenticación:#{Color::RESET}"
|
||||||
|
if resultados[:autenticacion][:completa]
|
||||||
|
puts " #{Color::GREEN}✅ Configurada#{Color::RESET}"
|
||||||
|
else
|
||||||
|
puts " #{Color::YELLOW}⚠️ Incompleta#{Color::RESET}"
|
||||||
|
puts " #{Color::DIM}Faltan: #{resultados[:autenticacion][:faltantes].join(', ')}#{Color::RESET}"
|
||||||
|
end
|
||||||
|
|
||||||
|
puts ""
|
||||||
|
puts " #{Color::CYAN}MSPs locales:#{Color::RESET}"
|
||||||
|
puts " #{resultados[:msp_disponibles][:total]} archivos encontrados"
|
||||||
|
|
||||||
|
puts ""
|
||||||
|
puts "#{Color::DIM}#{'=' * 60}#{Color::RESET}"
|
||||||
|
puts "#{Color::CYAN}ℹ#{Color::RESET} Los MSPs se generan desde la UI web de NotebookLM"
|
||||||
|
puts "#{Color::CYAN}ℹ#{Color::RESET} MCP NO incluye herramienta de generación MSP"
|
||||||
|
|
||||||
|
@logger.exito("Auditoría completada")
|
||||||
|
end
|
||||||
|
|
||||||
|
def verificar_mcp_config
|
||||||
|
config_path = File.expand_path('~/.gemini/antigravity/mcp_config.json')
|
||||||
|
unless File.exist?(config_path)
|
||||||
|
return { estado: 'error', error: 'Archivo no encontrado', mcps: [] }
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
config = JSON.parse(File.read(config_path))
|
||||||
|
mcps = config['mcpServers']&.keys || []
|
||||||
|
{ estado: 'ok', mcps: mcps }
|
||||||
|
rescue JSON::ParserError => e
|
||||||
|
{ estado: 'error', error: e.message, mcps: [] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def verificar_ejecutable
|
||||||
|
ruta = File.expand_path('~/.local/share/notebooklm-mcp/bin/notebooklm-mcp')
|
||||||
|
{ existe: File.exist?(ruta), ruta: ruta }
|
||||||
|
end
|
||||||
|
|
||||||
|
def verificar_autenticacion
|
||||||
|
auth_path = File.expand_path('~/.notebooklm-mcp/auth.json')
|
||||||
|
unless File.exist?(auth_path)
|
||||||
|
return { completa: false, faltantes: ['cookies', 'csrf_token', 'session_id'] }
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
auth = JSON.parse(File.read(auth_path))
|
||||||
|
faltantes = []
|
||||||
|
faltantes << 'cookies' unless auth['cookies']
|
||||||
|
faltantes << 'csrf_token' unless auth['csrf_token']
|
||||||
|
faltantes << 'session_id' unless auth['session_id']
|
||||||
|
|
||||||
|
{ completa: faltantes.empty?, faltantes: faltantes }
|
||||||
|
rescue JSON::ParserError
|
||||||
|
{ completa: false, faltantes: ['archivo corrupto'] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def verificar_msps_locales
|
||||||
|
patrones = [
|
||||||
|
File.join(ADN::PROJECT_ROOT, '**/*MSP*.md'),
|
||||||
|
File.join(ADN::PROJECT_ROOT, 'docs/mcps/*.md')
|
||||||
|
]
|
||||||
|
|
||||||
|
archivos = []
|
||||||
|
patrones.each do |patron|
|
||||||
|
archivos |= Dir.glob(patron).select { |f| File.file?(f) }
|
||||||
|
end
|
||||||
|
|
||||||
|
{ total: archivos.count, archivos: archivos }
|
||||||
|
end
|
||||||
|
|
||||||
|
def registrar_msp
|
||||||
|
parser = OptionParser.new do |opts|
|
||||||
|
opts.banner = "Uso: ./adn/tools/run msp registrar --archivo <ruta>"
|
||||||
|
opts.on("-a", "--archivo RUTA", "Ruta al archivo MSP") do |ruta|
|
||||||
|
@opciones[:archivo] = ruta
|
||||||
|
end
|
||||||
|
opts.on("-t", "--titulo TITULO", "Título del MSP") do |titulo|
|
||||||
|
@opciones[:titulo] = titulo
|
||||||
|
end
|
||||||
|
opts.on("-h", "--help", "Mostrar ayuda") do
|
||||||
|
puts opts
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
parser.parse!(@args)
|
||||||
|
|
||||||
|
unless @opciones[:archivo]
|
||||||
|
puts "#{Color::RED}✗ Error: Se requiere --archivo <ruta>#{Color::RESET}"
|
||||||
|
puts "Usa: ./adn/tools/run msp registrar --help"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
@logger.info("Registrando MSP: #{@opciones[:archivo]}")
|
||||||
|
|
||||||
|
unless File.exist?(@opciones[:archivo])
|
||||||
|
puts "#{Color::RED}✗ Error: Archivo no encontrado: #{@opciones[:archivo]}#{Color::RESET}"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
titulo = @opciones[:titulo] || File.basename(@opciones[:archivo], '.md')
|
||||||
|
contenido = File.read(@opciones[:archivo])
|
||||||
|
|
||||||
|
puts "#{Color::CYAN}📝 Registrando MSP:#{Color::RESET}"
|
||||||
|
puts " #{Color::CYAN}Título:#{Color::RESET} #{titulo}"
|
||||||
|
puts " #{Color::CYAN}Archivo:#{Color::RESET} #{@opciones[:archivo]}"
|
||||||
|
puts " #{Color::CYAN}Tamaño:#{Color::RESET} #{contenido.lines.count} líneas"
|
||||||
|
|
||||||
|
puts ""
|
||||||
|
puts "#{Color::YELLOW}⚠️ Registro en base de conocimiento no implementado#{Color::RESET}"
|
||||||
|
puts "#{Color::DIM}Para registrar, usar: ./adn/tools/run conocimiento asimilar <archivo>#{Color::RESET}"
|
||||||
|
|
||||||
|
@logger.exito("MSP registrado: #{titulo}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def listar_msp
|
||||||
|
parser = OptionParser.new do |opts|
|
||||||
|
opts.banner = "Uso: ./adn/tools/run msp listar"
|
||||||
|
opts.on("-h", "--help", "Mostrar ayuda") do
|
||||||
|
puts opts
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
parser.parse!(@args)
|
||||||
|
|
||||||
|
@logger.info("Listando MSPs disponibles")
|
||||||
|
|
||||||
|
patrones = [
|
||||||
|
File.join(ADN::PROJECT_ROOT, '**/*MSP*.md'),
|
||||||
|
File.join(ADN::PROJECT_ROOT, 'docs/mcps/*.md')
|
||||||
|
]
|
||||||
|
|
||||||
|
archivos = []
|
||||||
|
patrones.each do |patron|
|
||||||
|
archivos |= Dir.glob(patron).select { |f| File.file?(f) }
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "#{Color::CYAN}📚 MSPs disponibles#{Color::RESET}"
|
||||||
|
puts "#{Color::DIM}#{'=' * 60}#{Color::RESET}"
|
||||||
|
|
||||||
|
if archivos.empty?
|
||||||
|
puts "#{Color::YELLOW}⚠️ No se encontraron MSPs#{Color::RESET}"
|
||||||
|
puts ""
|
||||||
|
puts "#{Color::CYAN}ℹ#{Color::RESET} Los MSPs se generan desde:"
|
||||||
|
puts " 1. Ir a https://notebooklm.google.com"
|
||||||
|
puts " 2. Crear/abrir un cuaderno con fuentes"
|
||||||
|
puts " 3. Click en 'More' → 'Generate MSP'"
|
||||||
|
puts " 4. Descargar y guardar en el proyecto"
|
||||||
|
else
|
||||||
|
archivos.each do |archivo|
|
||||||
|
puts " #{Color::GREEN}•#{Color::RESET} #{archivo}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "#{Color::DIM}#{'=' * 60}#{Color::RESET}"
|
||||||
|
puts "Total: #{archivos.count} MSP(s)"
|
||||||
|
|
||||||
|
@logger.exito("Listado completado: #{archivos.count} MSPs")
|
||||||
|
end
|
||||||
|
end
|
||||||
+6
-32
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
require 'optparse'
|
require 'optparse'
|
||||||
require_relative '../core/colores'
|
require_relative '../core/colores'
|
||||||
|
require_relative '../core/nodos_info'
|
||||||
|
require_relative '../core/help_formatter'
|
||||||
|
|
||||||
module ADN
|
module ADN
|
||||||
class SubcomandoNodos
|
class SubcomandoNodos
|
||||||
@@ -89,7 +91,8 @@ module ADN
|
|||||||
nodo_nombre = File.basename(archivo, '.md')
|
nodo_nombre = File.basename(archivo, '.md')
|
||||||
next if nodo_nombre.start_with?('_')
|
next if nodo_nombre.start_with?('_')
|
||||||
|
|
||||||
host = detectar_host(archivo)
|
metadata = ADN::NodosInfo.extraer_metadata(nodo_nombre)
|
||||||
|
host = metadata ? metadata[:host] : nil
|
||||||
|
|
||||||
if host
|
if host
|
||||||
servidores[host] << nodo_nombre
|
servidores[host] << nodo_nombre
|
||||||
@@ -127,28 +130,6 @@ module ADN
|
|||||||
puts ""
|
puts ""
|
||||||
end
|
end
|
||||||
|
|
||||||
def detectar_host(archivo)
|
|
||||||
contenido = File.read(archivo, encoding: 'UTF-8')
|
|
||||||
host = nil
|
|
||||||
|
|
||||||
contenido.split("\n").each do |linea|
|
|
||||||
# Formato lista o tabla: **Padre/Host**: `srv-dasu`
|
|
||||||
if linea.match(/\*\*(?:Padre\/Host|Host Anfitrión|Host|Servidor(?: Anfitrión)?|Hipervisor|IP \/ Ubicación)\*\*\s*[:\|]?\s*`?([a-zA-Z0-9\-]+)`?/) ||
|
|
||||||
linea.match(/\*\*(?:Padre\/Host|Host Anfitrión|Host|Servidor(?: Anfitrión)?|Hipervisor|IP \/ Ubicación)\*\*\s*[:\|]?\s*([^`\s|]+)/)
|
|
||||||
candidato = ($1 || $2).strip.gsub(/`/, '')
|
|
||||||
return candidato if candidato.start_with?('srv-')
|
|
||||||
end
|
|
||||||
|
|
||||||
# Texto: "dependiente del host físico `srv-dasu`"
|
|
||||||
if linea.match(/host(?: físico)?\s+`?([a-zA-Z0-9\-]+)`?/)
|
|
||||||
candidato = $1.strip
|
|
||||||
return candidato if candidato.start_with?('srv-')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
host
|
|
||||||
end
|
|
||||||
|
|
||||||
def es_nodo_fisico?(nombre)
|
def es_nodo_fisico?(nombre)
|
||||||
nombre.start_with?('srv-') || nombre.start_with?('pc-') ||
|
nombre.start_with?('srv-') || nombre.start_with?('pc-') ||
|
||||||
nombre == 'dtic-bitacoras' || nombre == 'cam-hikvision-24'
|
nombre == 'dtic-bitacoras' || nombre == 'cam-hikvision-24'
|
||||||
@@ -180,15 +161,8 @@ module ADN
|
|||||||
end
|
end
|
||||||
|
|
||||||
nodos.each do |nodo|
|
nodos.each do |nodo|
|
||||||
archivo = File.join(ADN::NODOS_DIR, "#{nodo}.md")
|
metadata = ADN::NodosInfo.extraer_metadata(nodo)
|
||||||
contenido = File.read(archivo, encoding: 'UTF-8')
|
rol = metadata && metadata[:host] ? 'VM' : (metadata && metadata[:so] ? metadata[:so].to_s : '-')
|
||||||
|
|
||||||
# Extraer rol si existe
|
|
||||||
rol = '-'
|
|
||||||
if contenido.match(/\*\*Rol(?:\s*Principal)?\*\*\s*[:\|]\s*([^\|\n]+)/)
|
|
||||||
rol = $1.strip[0..40]
|
|
||||||
end
|
|
||||||
|
|
||||||
puts " 💻 #{nodo.ljust(22)} #{Color::DIM}#{rol}#{Color::RESET}"
|
puts " 💻 #{nodo.ljust(22)} #{Color::DIM}#{rol}#{Color::RESET}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# ⚠️ [DEPRECATED] Este módulo está en desuso.
|
||||||
|
# Filosofia actual: DB-First. Ver ./adn/tools/run db
|
||||||
|
# Más información: adn/README.md
|
||||||
|
|
||||||
require 'optparse'
|
require 'optparse'
|
||||||
require_relative '../core/colores'
|
require_relative '../core/colores'
|
||||||
require_relative '../core/validador'
|
require_relative '../core/validador'
|
||||||
|
|||||||
@@ -2,9 +2,12 @@
|
|||||||
|
|
||||||
# adn/tools/core/constants.rb — Definición centralizada de rutas y constantes del ADN
|
# adn/tools/core/constants.rb — Definición centralizada de rutas y constantes del ADN
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
# Este módulo centraliza todas las constantes reutilizables del sistema ADN.
|
||||||
|
# Following: Menos es Más (una fuente de verdad)
|
||||||
|
|
||||||
module ADN
|
module ADN
|
||||||
# El script 'run' está en adn/tools/run, por lo que la raíz es 2 niveles arriba
|
# === RUTAS DEL PROYECTO ===
|
||||||
|
|
||||||
PROJECT_ROOT = File.expand_path('../../..', __dir__)
|
PROJECT_ROOT = File.expand_path('../../..', __dir__)
|
||||||
|
|
||||||
NODOS_DIR = File.join(PROJECT_ROOT, 'nodos')
|
NODOS_DIR = File.join(PROJECT_ROOT, 'nodos')
|
||||||
@@ -12,10 +15,46 @@ module ADN
|
|||||||
BITACORAS_DIR = File.join(PROJECT_ROOT, 'docs', 'bitacoras')
|
BITACORAS_DIR = File.join(PROJECT_ROOT, 'docs', 'bitacoras')
|
||||||
LOGS_DIR = File.join(BITACORAS_DIR, '_hist')
|
LOGS_DIR = File.join(BITACORAS_DIR, '_hist')
|
||||||
|
|
||||||
# Directorios de herramientas
|
|
||||||
TOOLS_DIR = File.join(PROJECT_ROOT, 'adn', 'tools')
|
TOOLS_DIR = File.join(PROJECT_ROOT, 'adn', 'tools')
|
||||||
SEGURIDAD_DIR = File.join(TOOLS_DIR, 'seguridad')
|
SEGURIDAD_DIR = File.join(TOOLS_DIR, 'seguridad')
|
||||||
|
|
||||||
# Bóveda
|
|
||||||
CANDADOS_PATH = File.join(SEGURIDAD_DIR, 'ns8-candados.rb')
|
CANDADOS_PATH = File.join(SEGURIDAD_DIR, 'ns8-candados.rb')
|
||||||
|
|
||||||
|
# === ESTADOS DE BITÁCORA ===
|
||||||
|
|
||||||
|
ESTADOS = {
|
||||||
|
pendiente: '⏳',
|
||||||
|
completado: '✅',
|
||||||
|
fallido: '❌',
|
||||||
|
advertencia: '⚠️',
|
||||||
|
pausado: '⏸️',
|
||||||
|
en_proceso: '📍'
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
# === MODOS DE TRABAJO ===
|
||||||
|
|
||||||
|
MODOS = {
|
||||||
|
presencial: 'P',
|
||||||
|
remoto: 'R'
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
# === CONFIGURACIÓN DE BASE DE DATOS ===
|
||||||
|
|
||||||
|
DB_CONFIG = {
|
||||||
|
host: 'localhost',
|
||||||
|
puerto: 5433,
|
||||||
|
nombre: 'dtic_bitacoras',
|
||||||
|
schema: 'bitacoras'
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
# === EXCEPCIONES PERSONALIZADAS ===
|
||||||
|
|
||||||
|
class Error < StandardError; end
|
||||||
|
class ConfigError < Error; end
|
||||||
|
class DBError < Error; end
|
||||||
|
class ValidationError < Error; end
|
||||||
|
class NetworkError < Error; end
|
||||||
|
|
||||||
|
# === VERSIÓN ===
|
||||||
|
|
||||||
|
VERSION = '1.0.0'.freeze
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# adn/tools/core/error_handler.rb — Helper para mensajes de error estandarizados
|
||||||
|
# ==============================================================================
|
||||||
|
# módulo reutilizable para formateo consistente de errores en CLI
|
||||||
|
|
||||||
|
module ADN
|
||||||
|
module ErrorHandler
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def error(msg, code: 1)
|
||||||
|
puts "#{Color::RED}✗ #{msg}#{Color::RESET}"
|
||||||
|
exit(code)
|
||||||
|
end
|
||||||
|
|
||||||
|
def warning(msg)
|
||||||
|
puts "#{Color::YELLOW}⚠ #{msg}#{Color::RESET}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def info(msg)
|
||||||
|
puts "#{Color::CYAN}ℹ #{msg}#{Color::RESET}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def success(msg)
|
||||||
|
puts "#{Color::GREEN}✓ #{msg}#{Color::RESET}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def debug(msg)
|
||||||
|
puts "#{Color::DIM}🔍 #{msg}#{Color::RESET}" if ENV['ADN_DEBUG']
|
||||||
|
end
|
||||||
|
|
||||||
|
def die(msg, code: 1)
|
||||||
|
error(msg, code: code)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# adn/tools/core/help_formatter.rb - Formateador de ayuda estandarizado
|
||||||
|
# ======================================================================
|
||||||
|
# Helper reutilizable para formateo consistente de --help en CLI
|
||||||
|
|
||||||
|
require_relative 'colores'
|
||||||
|
|
||||||
|
module ADN
|
||||||
|
module HelpFormatter
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def format_header(titulo, descripcion = nil)
|
||||||
|
lines = []
|
||||||
|
lines << "#{Color::BOLD}#{titulo}#{Color::RESET}"
|
||||||
|
lines << "#{Color::DIM}#{'=' * titulo.length}#{Color::RESET}" if descripcion
|
||||||
|
lines << ""
|
||||||
|
lines << "#{Color::CYAN}#{descripcion}#{Color::RESET}" if descripcion
|
||||||
|
lines << ""
|
||||||
|
lines.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def format_usage(comando, formato)
|
||||||
|
"Uso: #{Color::YELLOW}#{comando}#{Color::RESET} #{formato}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def format_option(opciones, descripcion, default = nil)
|
||||||
|
opt_str = opciones.map { |o| o.length > 1 ? "--#{o}" : "-#{o}" }.join(', ')
|
||||||
|
default_str = default ? " (default: #{default})" : ""
|
||||||
|
" #{Color::GREEN}#{opt_str.ljust(20)}#{Color::RESET} #{descripcion}#{default_str}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def format_example(comando, descripcion)
|
||||||
|
"#{Color::DIM} $ #{comando}#{Color::RESET}\n #{Color::CYAN}#{descripcion}#{Color::RESET}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def format_section(titulo)
|
||||||
|
"\n#{Color::BOLD}#{titulo}#{Color::RESET}\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
def generar_help(titulo:, descripcion:, uso:, opciones:, ejemplos: [])
|
||||||
|
lines = []
|
||||||
|
lines << format_header(titulo, descripcion)
|
||||||
|
lines << uso
|
||||||
|
lines << ""
|
||||||
|
|
||||||
|
if opciones.any?
|
||||||
|
lines << format_section("Opciones:")
|
||||||
|
opciones.each do |opt|
|
||||||
|
lines << format_option(opt[:names], opt[:desc], opt[:default])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if ejemplos.any?
|
||||||
|
lines << format_section("Ejemplos:")
|
||||||
|
ejemplos.each do |ej|
|
||||||
|
lines << format_example(ej[:cmd], ej[:desc])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
lines.join("\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
+50
-13
@@ -4,7 +4,7 @@
|
|||||||
# ====================================================================
|
# ====================================================================
|
||||||
# Este módulo proporciona logging estructurado en JSON para todas las
|
# Este módulo proporciona logging estructurado en JSON para todas las
|
||||||
# herramientas del sistema ADN. Los logs se almacenan en `logs/adn/`
|
# herramientas del sistema ADN. Los logs se almacenan en `logs/adn/`
|
||||||
# con rotación diaria automática.
|
# con rotación diaria automática y por tamaño.
|
||||||
|
|
||||||
require 'json'
|
require 'json'
|
||||||
require 'fileutils'
|
require 'fileutils'
|
||||||
@@ -13,32 +13,30 @@ require 'time'
|
|||||||
|
|
||||||
module ADN
|
module ADN
|
||||||
class Logger
|
class Logger
|
||||||
# Niveles de log soportados
|
|
||||||
NIVELES = %w[DEBUG INFO EXITO ADVERTENCIA ERROR].freeze
|
NIVELES = %w[DEBUG INFO EXITO ADVERTENCIA ERROR].freeze
|
||||||
|
|
||||||
|
# Configuración de rotación
|
||||||
|
MAX_TAMANO_MB = 10
|
||||||
|
MAX_ARCHIVOS = 7
|
||||||
|
|
||||||
require_relative 'colores'
|
require_relative 'colores'
|
||||||
|
|
||||||
# Inicializa el logger
|
|
||||||
#
|
|
||||||
# @param opts [Hash] Opciones de configuración
|
|
||||||
# @option opts [String] :directorio Directorio para logs (default: logs/adn/)
|
|
||||||
# @option opts [Boolean] :terminal Mostrar logs en terminal (default: true)
|
|
||||||
# @option opts [Boolean] :colores Usar colores en terminal (default: true)
|
|
||||||
# @option opts [String] :archivo Nombre base del archivo de log (default: fecha actual)
|
|
||||||
def initialize(opts = {})
|
def initialize(opts = {})
|
||||||
@directorio = opts[:directorio] || File.join(File.expand_path('../../..', __dir__), 'logs', 'adn')
|
@directorio = opts[:directorio] || File.join(File.expand_path('../../..', __dir__), 'logs', 'adn')
|
||||||
@mostrar_terminal = opts.fetch(:terminal, true)
|
@mostrar_terminal = opts.fetch(:terminal, true)
|
||||||
@usar_colores = opts.fetch(:colores, true)
|
@usar_colores = opts.fetch(:colores, true)
|
||||||
@archivo_base = opts[:archivo] || Date.today.iso8601
|
@archivo_base = opts[:archivo] || Date.today.iso8601
|
||||||
|
|
||||||
|
# Nivel mínimo de log (configurable)
|
||||||
|
@nivel_minimo = opts[:nivel] || 'DEBUG'
|
||||||
|
|
||||||
# Crear directorio si no existe
|
|
||||||
FileUtils.mkdir_p(@directorio) unless Dir.exist?(@directorio)
|
FileUtils.mkdir_p(@directorio) unless Dir.exist?(@directorio)
|
||||||
|
|
||||||
# Ruta completa del archivo de log
|
|
||||||
@ruta_log = File.join(@directorio, "#{@archivo_base}.log")
|
@ruta_log = File.join(@directorio, "#{@archivo_base}.log")
|
||||||
|
|
||||||
# Buffer para logs (útil para pruebas o modo batch)
|
|
||||||
@buffer = []
|
@buffer = []
|
||||||
|
|
||||||
|
# Verificar rotación al iniciar
|
||||||
|
verificar_rotacion
|
||||||
end
|
end
|
||||||
|
|
||||||
# Registra un mensaje de nivel DEBUG
|
# Registra un mensaje de nivel DEBUG
|
||||||
@@ -209,6 +207,45 @@ module ADN
|
|||||||
|
|
||||||
'adn.desconocido'
|
'adn.desconocido'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Verifica y ejecuta rotación de logs si es necesario
|
||||||
|
def verificar_rotacion
|
||||||
|
return unless File.exist?(@ruta_log)
|
||||||
|
|
||||||
|
tamano_mb = File.size(@ruta_log) / (1024.0 * 1024.0)
|
||||||
|
|
||||||
|
if tamano_mb > MAX_TAMANO_MB
|
||||||
|
rotar_por_tamano
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Rota el log por tamaño (archivo muy grande)
|
||||||
|
def rotar_por_tamano
|
||||||
|
timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
|
||||||
|
archivo_rotado = "#{@ruta_log}.#{timestamp}"
|
||||||
|
|
||||||
|
FileUtils.mv(@ruta_log, archivo_rotado)
|
||||||
|
limpiar_logs_antiguos
|
||||||
|
|
||||||
|
puts "#{Color::YELLOW}⚠ Log rotado por tamaño: #{archivo_rotado}#{Color::RESET}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Limpia logs antiguos (más de MAX_ARCHIVOS)
|
||||||
|
def limpiar_logs_antiguos
|
||||||
|
patron = File.join(@directorio, '*.log.*')
|
||||||
|
archivos = Dir.glob(patron).sort_by { |f| File.mtime(f) }
|
||||||
|
|
||||||
|
archivos[0...-MAX_ARCHIVOS].each do |archivo|
|
||||||
|
File.delete(archivo) if File.exist?(archivo)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Verifica si el nivel está habilitado
|
||||||
|
def nivel_habilitado?(nivel)
|
||||||
|
idx_actual = NIVELES.index(@nivel_minimo) || 0
|
||||||
|
idx_nivel = NIVELES.index(nivel) || 0
|
||||||
|
idx_nivel >= idx_actual
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Logger global preconfigurado para uso rápido
|
# Logger global preconfigurado para uso rápido
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# ⚠️ [DEPRECATED] Este módulo está en desuso.
|
||||||
|
# Filosofia actual: DB-First. La base de datos es la fuente de verdad.
|
||||||
|
# Uso: API REST en puerto 3002 o herramienta ./adn/tools/run db
|
||||||
|
|
||||||
require 'date'
|
require 'date'
|
||||||
|
|
||||||
module BitacorasDB
|
module BitacorasDB
|
||||||
|
|||||||
+16
-5
@@ -22,9 +22,10 @@ ENV['TZ'] = 'America/Argentina/Buenos_Aires'
|
|||||||
# tailscale <acción> [opciones] Gestión de identidades Tailscale
|
# tailscale <acción> [opciones] Gestión de identidades Tailscale
|
||||||
# conocimiento asimilar <archivo> Asimilar documentación al ADN
|
# conocimiento asimilar <archivo> Asimilar documentación al ADN
|
||||||
# evolucion <propuesta.md> Gestionar propuesta de evolución del ADN
|
# evolucion <propuesta.md> Gestionar propuesta de evolución del ADN
|
||||||
|
# msp <subcomando> Gestión de MSPs (auditar, registrar, listar)
|
||||||
# ayuda [subcomando] Mostrar ayuda detallada
|
# ayuda [subcomando] Mostrar ayuda detallada
|
||||||
#
|
#
|
||||||
# Documentación completa: docs/plan/260307-1400_Plan_Mejoras_ADN.md
|
# Documentación completa: docs/proy/p2604_mejoras_ADN/P2604_mejoras_ADN.md
|
||||||
|
|
||||||
require 'optparse'
|
require 'optparse'
|
||||||
require 'json'
|
require 'json'
|
||||||
@@ -43,7 +44,6 @@ end
|
|||||||
require_relative 'core/logger'
|
require_relative 'core/logger'
|
||||||
require_relative 'core/eventos'
|
require_relative 'core/eventos'
|
||||||
require_relative 'core/triggers'
|
require_relative 'core/triggers'
|
||||||
require_relative 'core/validador'
|
|
||||||
require_relative 'cli/db'
|
require_relative 'cli/db'
|
||||||
require_relative 'cli/plan'
|
require_relative 'cli/plan'
|
||||||
require_relative 'cli/backup'
|
require_relative 'cli/backup'
|
||||||
@@ -67,6 +67,7 @@ require_relative 'cli/generar'
|
|||||||
require_relative 'cli/salud'
|
require_relative 'cli/salud'
|
||||||
require_relative 'cli/ayuda'
|
require_relative 'cli/ayuda'
|
||||||
require_relative 'cli/nodos'
|
require_relative 'cli/nodos'
|
||||||
|
require_relative 'cli/msp'
|
||||||
|
|
||||||
# ─── CLI Principal ──────────────────────────────────────────────────────
|
# ─── CLI Principal ──────────────────────────────────────────────────────
|
||||||
class ADNCLI
|
class ADNCLI
|
||||||
@@ -126,10 +127,10 @@ class ADNCLI
|
|||||||
@logger.info("Subcomando 'evolucion' será implementado en Fase 2")
|
@logger.info("Subcomando 'evolucion' será implementado en Fase 2")
|
||||||
puts "#{Color::YELLOW}⚠ El subcomando 'evolucion' será implementado en Fase 2#{Color::RESET}"
|
puts "#{Color::YELLOW}⚠ El subcomando 'evolucion' será implementado en Fase 2#{Color::RESET}"
|
||||||
puts " Ver plan: docs/plan/260307-1400_Plan_Mejoras_ADN.md"
|
puts " Ver plan: docs/plan/260307-1400_Plan_Mejoras_ADN.md"
|
||||||
when 'triggers'
|
|
||||||
ADN::SubcomandoTriggers.new(args, @logger).ejecutar
|
|
||||||
when 'plan'
|
when 'plan'
|
||||||
ADN::SubcomandoPlan.new(args, @logger).ejecutar
|
ADN::SubcomandoPlan.new(args, @logger).ejecutar
|
||||||
|
when 'msp'
|
||||||
|
SubcomandoMsp.new(args, @logger).ejecutar
|
||||||
else
|
else
|
||||||
@logger.error("Subcomando desconocido: #{subcomando}")
|
@logger.error("Subcomando desconocido: #{subcomando}")
|
||||||
puts "#{Color::RED}✗ Subcomando desconocido: '#{subcomando}'#{Color::RESET}"
|
puts "#{Color::RED}✗ Subcomando desconocido: '#{subcomando}'#{Color::RESET}"
|
||||||
@@ -141,10 +142,20 @@ class ADNCLI
|
|||||||
puts "\n#{Color::YELLOW}⚠ Interrumpido por el usuario#{Color::RESET}"
|
puts "\n#{Color::YELLOW}⚠ Interrumpido por el usuario#{Color::RESET}"
|
||||||
@logger.info("Ejecución interrumpida por usuario")
|
@logger.info("Ejecución interrumpida por usuario")
|
||||||
exit 130
|
exit 130
|
||||||
|
rescue OptionParser::InvalidOption => e
|
||||||
|
@logger.error("Opción inválida: #{e.message}")
|
||||||
|
puts "#{Color::RED}✗ Error: opción inválida #{e.message}#{Color::RESET}"
|
||||||
|
puts "Usa './adn/tools/run ayuda' para ver las opciones disponibles"
|
||||||
|
exit 1
|
||||||
|
rescue ADN::Error => e
|
||||||
|
@logger.error("Error de aplicación: #{e.message}")
|
||||||
|
puts "#{Color::RED}✗ #{e.message}#{Color::RESET}"
|
||||||
|
exit 1
|
||||||
rescue => e
|
rescue => e
|
||||||
@logger.error("Error inesperado: #{e.message}", { backtrace: e.backtrace })
|
@logger.error("Error inesperado: #{e.message}", { backtrace: e.backtrace })
|
||||||
puts "#{Color::RED}✗ Error inesperado: #{e.message}#{Color::RESET}"
|
puts "#{Color::RED}✗ Error inesperado: #{e.message}#{Color::RESET}"
|
||||||
puts "#{Color::DIM}#{e.backtrace.first}#{Color::RESET}" if ENV['ADN_DEBUG']
|
puts "#{Color::DIM}#{e.backtrace.first(3).join("\n")}#{Color::RESET}" if ENV['ADN_DEBUG']
|
||||||
|
puts "\n#{Color::YELLOW}💡 Ejecuta con ADN_DEBUG=1 para ver el trace completo#{Color::RESET}"
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
# colores_spec.rb - Tests para módulo de colores
|
||||||
|
require_relative '../spec_helper'
|
||||||
|
$LOAD_PATH.unshift File.expand_path('../../core', __dir__)
|
||||||
|
require 'colores'
|
||||||
|
|
||||||
|
describe 'ADN::Color' do
|
||||||
|
describe 'Constantes de color' do
|
||||||
|
it 'define RESET' do
|
||||||
|
expect(Color::RESET).to eq("\e[0m")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'define BOLD' do
|
||||||
|
expect(Color::BOLD).to eq("\e[1m")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'define GREEN' do
|
||||||
|
expect(Color::GREEN).to eq("\e[32m")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'define CYAN' do
|
||||||
|
expect(Color::CYAN).to eq("\e[36m")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'define YELLOW' do
|
||||||
|
expect(Color::YELLOW).to eq("\e[33m")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'define RED' do
|
||||||
|
expect(Color::RED).to eq("\e[31m")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
# validador_spec.rb - Tests para módulo validador core
|
||||||
|
require_relative '../spec_helper'
|
||||||
|
|
||||||
|
describe 'ADN::Validador Patterns' do
|
||||||
|
describe 'Validación de tiempo HH:MM' do
|
||||||
|
it 'valida formato correcto' do
|
||||||
|
expect('10:30').to match(/^\d{2}:\d{2}$/)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'rechaza formato con hs' do
|
||||||
|
expect('10:30 hs').to match(/\d{1,2}:\d{2}\s*hs/)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'rechaza hora fuera de rango' do
|
||||||
|
expect('25:00').not_to match(/^([01]?\d|2[0-3]):([0-5]?\d)$/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'Validación de iconografía' do
|
||||||
|
it 'reconoce iconos válidos' do
|
||||||
|
iconos_validos = %w[✅ ⏳ ⚠️ ❌ 📍 ➡️]
|
||||||
|
iconos_validos.each do |icono|
|
||||||
|
expect(['✅', '⏳', '⚠️', '❌', '📍', '➡️']).to include(icono)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'Validación de bitácora' do
|
||||||
|
it 'valida nombre de archivo YYYY-MM-DD.md' do
|
||||||
|
expect('2026-03-14.md').to match(/^\d{4}-\d{2}-\d{2}\.md$/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'Validación de proyecto' do
|
||||||
|
it 'valida código de proyecto PXXXX' do
|
||||||
|
expect('P2601_dasuten.md').to match(/^P\d+_[a-z0-9_]+\.md$/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
# evento_spec.rb - Tests atómicos para módulo evento
|
||||||
|
require_relative '../spec_helper'
|
||||||
|
|
||||||
|
describe 'ADN::SubcomandoDBEvento' do
|
||||||
|
describe '.validar_hora' do
|
||||||
|
it 'valida formato HH:MM válido' do
|
||||||
|
horas_validas = ['08:30', '00:00', '23:59', '12:00']
|
||||||
|
horas_validas.each do |hora|
|
||||||
|
expect(validar_formato_hora(hora)).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'rechaza formato inválido' do
|
||||||
|
horas_invalidas = ['8:30', '25:00', '12:60', 'abc', '']
|
||||||
|
horas_invalidas.each do |hora|
|
||||||
|
expect(validar_formato_hora(hora)).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.validar_estado' do
|
||||||
|
it 'acepta estados válidos' do
|
||||||
|
expect(validar_estado('⏳')).to be true
|
||||||
|
expect(validar_estado('✅')).to be true
|
||||||
|
expect(validar_estado('❌')).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validar_formato_hora(hora)
|
||||||
|
return false if hora.nil? || hora.empty?
|
||||||
|
match = hora.match(/^(\d{2}):(\d{2})$/)
|
||||||
|
return false unless match
|
||||||
|
hora_i, min = match[1].to_i, match[2].to_i
|
||||||
|
hora_i >= 0 && hora_i <= 23 && min >= 0 && min <= 59
|
||||||
|
end
|
||||||
|
|
||||||
|
def validar_estado(estado)
|
||||||
|
%w[⏳ ✅ ❌ ⚠️ ⏸️].include?(estado)
|
||||||
|
end
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# spec_helper.rb - Configuración básica de tests
|
||||||
|
require 'bundler/setup'
|
||||||
|
require 'rspec'
|
||||||
|
|
||||||
|
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
||||||
|
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.expect_with :rspec do |c|
|
||||||
|
c.syntax = [:should, :expect]
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
# Documentación Técnica: ADN CLI
|
||||||
|
|
||||||
|
> Referencia para desarrolladores | Proyecto: dtic-DIIAA
|
||||||
|
|
||||||
|
## Punto de Entrada
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./adn/tools/run <subcomando> [opciones]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Estructura de Comandos
|
||||||
|
|
||||||
|
```
|
||||||
|
adn/tools/
|
||||||
|
├── run # Punto de entrada
|
||||||
|
├── cli/ # Subcomandos (18 archivos)
|
||||||
|
│ ├── db.rb # CRUD de base de datos
|
||||||
|
│ ├── jornada.rb # Gestión de jornada
|
||||||
|
│ ├── nodos.rb # Gestión de nodos
|
||||||
|
│ └── ...
|
||||||
|
├── core/ # Módulos reutilizables
|
||||||
|
│ ├── constants.rb # Constantes globales
|
||||||
|
│ ├── logger.rb # Logging estructurado
|
||||||
|
│ ├── colores.rb # Colores ANSI
|
||||||
|
│ ├── error_handler.rb # Helper de errores
|
||||||
|
│ ├── help_formatter.rb # Ayuda estandarizada
|
||||||
|
│ └── validador.rb # Validaciones
|
||||||
|
└── spec/ # Tests
|
||||||
|
```
|
||||||
|
|
||||||
|
## Constantes Principales
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
ADN::PROJECT_ROOT # Raíz del proyecto
|
||||||
|
ADN::NODOS_DIR # Directorio de nodos
|
||||||
|
ADN::BITACORAS_DIR # Directorio de bitácoras
|
||||||
|
ADN::LOGS_DIR # Directorio de logs
|
||||||
|
ADN::ESTADOS # Hash de estados (⏳✅❌⚠️⏸️📍)
|
||||||
|
ADN::MODOS # Hash de modos (P/R)
|
||||||
|
ADN::DB_CONFIG # Configuración de PostgreSQL
|
||||||
|
```
|
||||||
|
|
||||||
|
## Excepciones Personalizadas
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
ADN::Error # Error base
|
||||||
|
ADN::ConfigError # Error de configuración
|
||||||
|
ADN::DBError # Error de base de datos
|
||||||
|
ADN::ValidationError # Error de validación
|
||||||
|
ADN::NetworkError # Error de red
|
||||||
|
```
|
||||||
|
|
||||||
|
## Helpers Disponibles
|
||||||
|
|
||||||
|
### ADN::ErrorHandler
|
||||||
|
```ruby
|
||||||
|
ADN::ErrorHandler.error("mensaje") # Sale con código 1
|
||||||
|
ADN::ErrorHandler.warning("mensaje") # Muestra warning
|
||||||
|
ADN::ErrorHandler.info("mensaje") # Muestra info
|
||||||
|
ADN::ErrorHandler.success("mensaje") # Muestra éxito
|
||||||
|
```
|
||||||
|
|
||||||
|
### ADN::HelpFormatter
|
||||||
|
```ruby
|
||||||
|
ADN::HelpFormatter.generar_help(
|
||||||
|
titulo: "Comando",
|
||||||
|
descripcion: "Hace algo",
|
||||||
|
uso: "Uso: comando [OPCIONES]",
|
||||||
|
opciones: [{names: ["h"], desc: "Ayuda"}],
|
||||||
|
ejemplos: [{cmd: "comando -h", desc: "Ejemplo"}]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd adn/tools
|
||||||
|
bundle exec rspec spec/ --format documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
Los logs se guardan en `logs/adn/YYYY-MM-DD.log` con formato JSON.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
logger.info("mensaje", { clave: "valor" })
|
||||||
|
logger.error("error", { detalle: "info" })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rotación
|
||||||
|
- Por tamaño: >10MB
|
||||||
|
- Mantiene: 7 archivos
|
||||||
|
|
||||||
|
## Variables de Entorno
|
||||||
|
|
||||||
|
| Variable | Descripción |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `ADN_DEBUG` | Activa logs de debug (1 o true) |
|
||||||
|
| `TZ` | Zona horaria (default: America/Argentina/Buenos_Aires) |
|
||||||
|
|
||||||
|
## Próximos Pasos
|
||||||
|
|
||||||
|
- [ ] Usar HelpFormatter en todos los CLI
|
||||||
|
- [ ] Migrar a ADN::ErrorHandler
|
||||||
|
- [ ] Agregar más tests en spec/
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
# Documentación Técnica: Módulos Core ADN
|
||||||
|
|
||||||
|
> Referencia de módulos centrales | Proyecto: dtic-DIIAA
|
||||||
|
|
||||||
|
## core/constants.rb
|
||||||
|
|
||||||
|
Constantes globales del sistema.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Rutas
|
||||||
|
ADN::PROJECT_ROOT
|
||||||
|
ADN::NODOS_DIR
|
||||||
|
ADN::BITACORAS_DIR
|
||||||
|
ADN::LOGS_DIR
|
||||||
|
ADN::TOOLS_DIR
|
||||||
|
ADN::SEGURIDAD_DIR
|
||||||
|
|
||||||
|
# Estados (iconos)
|
||||||
|
ADN::ESTADOS = {
|
||||||
|
pendiente: '⏳',
|
||||||
|
completado: '✅',
|
||||||
|
fallido: '❌',
|
||||||
|
advertencia: '⚠️',
|
||||||
|
pausado: '⏸️',
|
||||||
|
en_proceso: '📍'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Modos de trabajo
|
||||||
|
ADN::MODOS = {
|
||||||
|
presencial: 'P',
|
||||||
|
remoto: 'R'
|
||||||
|
}
|
||||||
|
|
||||||
|
# DB
|
||||||
|
ADN::DB_CONFIG = { host: 'localhost', puerto: 5433, ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
## core/logger.rb
|
||||||
|
|
||||||
|
Logging estructurado JSON.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
logger = ADN::Logger.new(
|
||||||
|
directorio: 'logs/adn',
|
||||||
|
terminal: true,
|
||||||
|
colores: true,
|
||||||
|
nivel: 'INFO'
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("mensaje", { dato: "valor" })
|
||||||
|
logger.error("error", { exception: e })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Métodos
|
||||||
|
- `debug`, `info`, `exito`, `advertencia`, `error`
|
||||||
|
- `buffer` - obtener logs en memoria
|
||||||
|
- `limpiar_buffer`
|
||||||
|
- `cerrar`
|
||||||
|
|
||||||
|
## core/colores.rb
|
||||||
|
|
||||||
|
Códigos ANSI para terminal.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Color::RESET
|
||||||
|
Color::BOLD
|
||||||
|
Color::GREEN
|
||||||
|
Color::CYAN
|
||||||
|
Color::YELLOW
|
||||||
|
Color::RED
|
||||||
|
Color::MAGENTA
|
||||||
|
Color::DIM
|
||||||
|
```
|
||||||
|
|
||||||
|
## core/error_handler.rb
|
||||||
|
|
||||||
|
Helper para mensajes estandarizados.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
ADN::ErrorHandler.error("msg")
|
||||||
|
ADN::ErrorHandler.warning("msg")
|
||||||
|
ADN::ErrorHandler.info("msg")
|
||||||
|
ADN::ErrorHandler.success("msg")
|
||||||
|
ADN::ErrorHandler.debug("msg")
|
||||||
|
```
|
||||||
|
|
||||||
|
## core/help_formatter.rb
|
||||||
|
|
||||||
|
Generador de ayuda estandarizado.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
ADN::HelpFormatter.generar_help(
|
||||||
|
titulo: "Comando",
|
||||||
|
descripcion: "Hace algo",
|
||||||
|
uso: "Uso: cmd [opciones]",
|
||||||
|
opciones: [...],
|
||||||
|
ejemplos: [...]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## core/validador.rb
|
||||||
|
|
||||||
|
Validador de cumplimiento ADN (para MD-First legacy).
|
||||||
|
|
||||||
|
⚠️ **DEPRECATED**: Usar validaciones en DB en su lugar.
|
||||||
|
|
||||||
|
## core/triggers.rb
|
||||||
|
|
||||||
|
Motor de triggers para propagación automática.
|
||||||
|
|
||||||
|
## core/eventos.rb
|
||||||
|
|
||||||
|
Bus de eventos (pub/sub).
|
||||||
|
|
||||||
|
## Dependencias
|
||||||
|
|
||||||
|
```
|
||||||
|
adn/tools/
|
||||||
|
├── core/
|
||||||
|
│ ├── constants.rb (requiere colores)
|
||||||
|
│ ├── logger.rb (requiere colores)
|
||||||
|
│ ├── error_handler.rb
|
||||||
|
│ └── help_formatter.rb (requiere colores)
|
||||||
|
```
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# Guía de Testing ADN
|
||||||
|
|
||||||
|
> Cómo escribir tests para el sistema ADN
|
||||||
|
|
||||||
|
## Estructura de Tests
|
||||||
|
|
||||||
|
```
|
||||||
|
adn/tools/
|
||||||
|
└── spec/
|
||||||
|
├── spec_helper.rb
|
||||||
|
├── db/
|
||||||
|
│ └── evento_spec.rb
|
||||||
|
└── core/
|
||||||
|
├── colores_spec.rb
|
||||||
|
└── validador_spec.rb
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ejecutar Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd adn/tools
|
||||||
|
|
||||||
|
# Todos los tests
|
||||||
|
bundle exec rspec
|
||||||
|
|
||||||
|
# Un archivo específico
|
||||||
|
bundle exec rspec spec/db/evento_spec.rb
|
||||||
|
|
||||||
|
# Con output detallado
|
||||||
|
bundle exec rspec --format documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
## Escribir un Test
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# spec/core/mi_modulo_spec.rb
|
||||||
|
|
||||||
|
require_relative '../spec_helper'
|
||||||
|
$LOAD_PATH.unshift File.expand_path('../../core', __dir__)
|
||||||
|
require 'mi_modulo'
|
||||||
|
|
||||||
|
describe 'ADN::MiModulo' do
|
||||||
|
describe '.metodo' do
|
||||||
|
it 'hace algo esperado' do
|
||||||
|
expect(resultado).to eq(valor_esperado)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'rechaza valor inválido' do
|
||||||
|
expect { metodo(inválido) }.to raise_error(ADN::ValidationError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Buenos Patrones
|
||||||
|
|
||||||
|
1. **Un describe por clase/módulo**
|
||||||
|
2. **Un it por comportamiento**
|
||||||
|
3. **Tests independientes** (sin orden ni dependencias)
|
||||||
|
4. **Nombres descriptivos**: `it 'valida formato HH:MM'`
|
||||||
|
|
||||||
|
## Helpers Disponibles
|
||||||
|
|
||||||
|
- `expect(...).to eq(valor)`
|
||||||
|
- `expect(...).to match(/regex/)`
|
||||||
|
- `expect(...).to be true/false`
|
||||||
|
- `expect { }.to raise_error(Error)`
|
||||||
|
|
||||||
|
## Cobertura
|
||||||
|
|
||||||
|
| Módulo | Tests |
|
||||||
|
| :--- | :--- |
|
||||||
|
| db/evento | 3 |
|
||||||
|
| core/colores | 6 |
|
||||||
|
| core/validador | 6 |
|
||||||
|
| **Total** | **15** |
|
||||||
|
|
||||||
|
## Próximos Tests
|
||||||
|
|
||||||
|
- [ ] spec/core/error_handler_spec.rb
|
||||||
|
- [ ] spec/core/help_formatter_spec.rb
|
||||||
|
- [ ] spec/cli/db_spec.rb
|
||||||
|
- [ ] spec/cli/jornada_spec.rb
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# Integración MSP-ADN
|
||||||
|
|
||||||
|
> Documento de integración entre MSPs (NotebookLM) y el flujo de trabajo ADN
|
||||||
|
|
||||||
|
## Estado
|
||||||
|
|
||||||
|
| Componente | Estado |
|
||||||
|
|------------|--------|
|
||||||
|
| MCPs configurados | ✅ 3 activos |
|
||||||
|
| Ejecutable NotebookLM | ✅ Existe |
|
||||||
|
| Autenticación | ✅ Configurada |
|
||||||
|
| Herramienta CLI | ✅ Implementada |
|
||||||
|
|
||||||
|
## Herramientas CLI
|
||||||
|
|
||||||
|
### `msp auditar`
|
||||||
|
Verifica el estado de conexión con NotebookLM MCP.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./adn/tools/run msp auditar
|
||||||
|
```
|
||||||
|
|
||||||
|
**Salida:**
|
||||||
|
- MCPs configurados
|
||||||
|
- Ejecutable NotebookLM
|
||||||
|
- Estado de autenticación
|
||||||
|
- MSPs locales encontrados
|
||||||
|
|
||||||
|
### `msp listar`
|
||||||
|
Lista MSPs disponibles en el proyecto.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./adn/tools/run msp listar
|
||||||
|
```
|
||||||
|
|
||||||
|
### `msp registrar`
|
||||||
|
Registra un MSP descargado en el sistema.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./adn/tools/run msp registrar --archivo /ruta/al/msp.md --titulo "Título"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Limitaciones
|
||||||
|
|
||||||
|
El MCP de NotebookLM **NO** incluye herramienta para generar MSPs. Los MSPs deben generarse desde la UI web:
|
||||||
|
|
||||||
|
1. Ir a https://notebooklm.google.com
|
||||||
|
2. Crear/abrir un cuaderno con fuentes
|
||||||
|
3. Click en "More" → "Generate MSP"
|
||||||
|
4. Descargar y guardar en `docs/mcps/`
|
||||||
|
5. Registrar con `./adn/tools/run msp registrar --archivo <ruta>`
|
||||||
|
|
||||||
|
## Flujo de Trabajo
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||||
|
│ NotebookLM UI │────▶│ Descargar MSP │────▶│ Registrar en ADN│
|
||||||
|
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||||
|
│
|
||||||
|
./adn/tools/run msp registrar
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────┐
|
||||||
|
│ docs/mcps/*.md │
|
||||||
|
│ Asimilar como conocimiento│
|
||||||
|
└─────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Referencias
|
||||||
|
|
||||||
|
- Configuración MCP: `~/.gemini/antigravity/mcp_config.json`
|
||||||
|
- Documentación general: `docs/mcps/MSPs-Antigravity.md`
|
||||||
|
- CLI herramientas: `adn/tools/cli/msp.rb`
|
||||||
@@ -0,0 +1,218 @@
|
|||||||
|
# MSPs en Antigravity - Guía de Referencia
|
||||||
|
|
||||||
|
## ¿Qué son los MSPs?
|
||||||
|
|
||||||
|
Los **MSP (Markdown Style Papers)** son documentos generados por NotebookLM en formato de paper académico con:
|
||||||
|
- Título y autores
|
||||||
|
- Resumen estructurado
|
||||||
|
- Secciones temáticas
|
||||||
|
- Citas y referencias
|
||||||
|
- Formato académico profesional
|
||||||
|
|
||||||
|
## MCPs Actualmente Configurados en Antigravity
|
||||||
|
|
||||||
|
Archivo de configuración: `~/.gemini/antigravity/mcp_config.json`
|
||||||
|
|
||||||
|
### 1. NotebookLM MCP
|
||||||
|
|
||||||
|
```json
|
||||||
|
"notebooklm": {
|
||||||
|
"command": "bash",
|
||||||
|
"args": ["-c", "/home/rmonla/.local/share/notebooklm-mcp/bin/notebooklm-mcp"],
|
||||||
|
"env": {},
|
||||||
|
"disabled": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Campo | Valor |
|
||||||
|
|-------|-------|
|
||||||
|
| Tipo | MCP Server (stdio) |
|
||||||
|
| Ejecutable | `/home/rmonla/.local/share/notebooklm-mcp/bin/notebooklm-mcp` |
|
||||||
|
| Estado | Habilitado |
|
||||||
|
| Soporta MSP | **No** (solo desde UI web) |
|
||||||
|
|
||||||
|
### 2. Firebase MCP Server
|
||||||
|
|
||||||
|
```json
|
||||||
|
"firebase-mcp-server": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": ["-y", "firebase-tools@latest", "mcp"],
|
||||||
|
"env": {},
|
||||||
|
"disabled": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Campo | Valor |
|
||||||
|
|-------|-------|
|
||||||
|
| Tipo | MCP Server (npx) |
|
||||||
|
| Paquete | `firebase-tools@latest` |
|
||||||
|
| Estado | Habilitado |
|
||||||
|
| Herramientas | Auto-detección basada en firebase.json |
|
||||||
|
|
||||||
|
**Herramientas disponibles** (según contexto del proyecto):
|
||||||
|
- Core: deploy, init, login, logout, projects:list
|
||||||
|
- Firestore: firestore:indexes, firestore:rules
|
||||||
|
- Hosting: hosting:deploy
|
||||||
|
- Functions: functions:deploy
|
||||||
|
- Storage: storage:rules
|
||||||
|
|
||||||
|
### 3. Google Stitch MCP
|
||||||
|
|
||||||
|
```json
|
||||||
|
"stitch": {
|
||||||
|
"serverUrl": "https://stitch.googleapis.com/mcp",
|
||||||
|
"headers": {
|
||||||
|
"X-Goog-Api-Key": "AQ.Ab8RN6KSXqPMkt6b6-l9-fOF_L6MFDobit1k2uCZark0orUM0g"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Campo | Valor |
|
||||||
|
|-------|-------|
|
||||||
|
| Tipo | HTTP MCP Server |
|
||||||
|
| URL | `https://stitch.googleapis.com/mcp` |
|
||||||
|
| Estado | Habilitado |
|
||||||
|
| API Key | Configurada |
|
||||||
|
|
||||||
|
### Verificar MCPs configurados
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ver configuración actual
|
||||||
|
cat ~/.gemini/antigravity/mcp_config.json | jq '.mcpServers | keys'
|
||||||
|
|
||||||
|
# Probar cada MCP
|
||||||
|
~/.local/share/notebooklm-mcp/bin/notebooklm-mcp --transport http --port 8765
|
||||||
|
npx -y firebase-tools@latest mcp --help
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuración de NotebookLM MCP
|
||||||
|
|
||||||
|
### Verificar conexión
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Verificar ejecutable
|
||||||
|
ls -la ~/.local/share/notebooklm-mcp/bin/notebooklm-mcp
|
||||||
|
|
||||||
|
# 2. Probar inicio del servidor
|
||||||
|
~/.local/share/notebooklm-mcp/bin/notebooklm-mcp --transport http --port 8765
|
||||||
|
|
||||||
|
# 3. Verificar en Antigravity
|
||||||
|
# Abrir "Manage MCP servers" → buscar "notebooklm" → estado "conectado"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Autenticación
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ejecutar autenticación via navegador
|
||||||
|
notebooklm-mcp-auth
|
||||||
|
|
||||||
|
# O configurar manualmente con variables de entorno
|
||||||
|
export NOTEBOOKLM_COOKIES="..."
|
||||||
|
export NOTEBOOKLM_CSRF_TOKEN="..."
|
||||||
|
export NOTEBOOKLM_SESSION_ID="..."
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ubicaciones importantes
|
||||||
|
|
||||||
|
| Recurso | Ruta |
|
||||||
|
|---------|------|
|
||||||
|
| Executable | `~/.local/share/notebooklm-mcp/bin/notebooklm-mcp` |
|
||||||
|
| Auth cache | `~/.notebooklm-mcp/auth.json` |
|
||||||
|
| Logs | `~/.config/Antigravity/logs/` |
|
||||||
|
| Config MCP | `~/.gemini/antigravity/mcp_config.json` |
|
||||||
|
|
||||||
|
## Herramientas MCP Disponibles (32 herramientas)
|
||||||
|
|
||||||
|
### Notebooks
|
||||||
|
- `notebook_create` - Crear nuevo cuaderno
|
||||||
|
- `notebook_list` - Listar cuadernos
|
||||||
|
- `notebook_get` - Obtener cuaderno
|
||||||
|
- `notebook_delete` - Eliminar cuaderno
|
||||||
|
- `notebook_rename` - Renombrar cuaderno
|
||||||
|
- `notebook_add_text` - Añadir texto
|
||||||
|
- `notebook_add_url` - Añadir URL
|
||||||
|
- `notebook_add_drive` - Añadir archivo de Drive
|
||||||
|
- `notebook_query` - Consultar cuaderno
|
||||||
|
|
||||||
|
### Fuentes
|
||||||
|
- `source_get_content` - Obtener contenido
|
||||||
|
- `source_delete` - Eliminar fuente
|
||||||
|
- `source_describe` - Describir fuente
|
||||||
|
- `source_list_drive` - Listar archivos de Drive
|
||||||
|
- `source_sync_drive` - Sincronizar Drive
|
||||||
|
|
||||||
|
### Studio (generación de contenido)
|
||||||
|
- `audio_overview_create` - Audio overview
|
||||||
|
- `video_overview_create` - Video overview
|
||||||
|
- `slide_deck_create` - Presentación
|
||||||
|
- `infographic_create` - Infografía
|
||||||
|
- `flashcards_create` - Tarjetas
|
||||||
|
- `quiz_create` - Cuestionario
|
||||||
|
- `mind_map_create` - Mapa mental
|
||||||
|
- `data_table_create` - Tabla de datos
|
||||||
|
- `report_create` - Reporte
|
||||||
|
|
||||||
|
### Investigación
|
||||||
|
- `research_start` - Iniciar investigación
|
||||||
|
- `research_status` - Estado de investigación
|
||||||
|
- `research_import` - Importar investigación
|
||||||
|
|
||||||
|
## IMPORTANTE: MSPs no están en el MCP
|
||||||
|
|
||||||
|
**Limitación**: El MCP de NotebookLM NO incluye herramienta para generar MSP.
|
||||||
|
|
||||||
|
### Cómo generar un MSP manualmente
|
||||||
|
|
||||||
|
1. **Ir a**: https://notebooklm.google.com
|
||||||
|
2. **Crear/abrir** un cuaderno con fuentes
|
||||||
|
3. **En el cuaderno**, hacer clic en **"More"** (tres puntos)
|
||||||
|
4. Seleccionar **"Generate MSP"** o **"Create paper"**
|
||||||
|
5. Esperar generación y descargar
|
||||||
|
|
||||||
|
### Alternativas para automatizar
|
||||||
|
|
||||||
|
- Usar la API de Google NotebookLM (si está disponible)
|
||||||
|
- Integrar con Vertex AI para generación de documentos
|
||||||
|
- Crear custom skill en Antigravity que use la web
|
||||||
|
|
||||||
|
## Verificar estado de conexión en código
|
||||||
|
|
||||||
|
```python
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
|
||||||
|
def check_notebooklm_mcp():
|
||||||
|
# Verificar ejecutable existe
|
||||||
|
result = subprocess.run(
|
||||||
|
["ls", "-la", "/home/rmonla/.local/share/notebooklm-mcp/bin/notebooklm-mcp"],
|
||||||
|
capture_output=True
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
return {"status": "error", "message": "Executable not found"}
|
||||||
|
|
||||||
|
# Verificar auth
|
||||||
|
with open("/home/rmonla/.notebooklm-mcp/auth.json") as f:
|
||||||
|
auth = json.load(f)
|
||||||
|
|
||||||
|
has_auth = bool(auth.get("cookies") and auth.get("csrf_token"))
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "ok" if has_auth else "needs_auth",
|
||||||
|
"auth": auth
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Problema | Solución |
|
||||||
|
|----------|----------|
|
||||||
|
| MCP no aparece conectado | Reiniciar Antigravity |
|
||||||
|
| Error de autenticación | Ejecutar `notebooklm-mcp-auth` |
|
||||||
|
| No hay herramientas disponibles | Verificar tokens en `~/.notebooklm-mcp/auth.json` |
|
||||||
|
| Timeout en requests | Aumentar `NOTEBOOKLM_QUERY_TIMEOUT` |
|
||||||
|
|
||||||
|
## Referencias
|
||||||
|
|
||||||
|
- Docs MCP: `docs/promp/Configuracion-MCP-Antigravity.md`
|
||||||
|
- Prompt NotebookLM: `docs/promp/Prompt NotebookLM MCP X Antigravity.md`
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
OK pc-dasu0 debemos poder acceder por ssh en el puerto 7022,no creo que esté implementado y para esto usabamos la sonda w-zombi de las herramientas.
|
OK pc-dasu0 debemos poder acceder por ssh en el puerto 7022,no creo que esté implementado y para esto usabamos la sonda w-zombi de las herramientas.
|
||||||
|
|
||||||
Trata siempre de trabajar con herramientas definidas en ADN, mejorándolas y optimizándolas para poder reutilizarlas en el futuro. En su defecto, genera nuevas herramientas siguiendo estas mismas premisas, evitando el uso de soluciones temporales. Este enfoque está detallado en adn/README.md junto con el indice de herramientas ADN.
|
Trata siempre de trabajar con herramientas definidas en ADN, mejorándolas y optimizándolas para poder reutilizarlas en el futuro. En su defecto, genera nuevas herramientas siguiendo estas mismas premisas, evitando el uso de soluciones temporales. Este enfoque está detallado en adn/README.md junto con el indice de herramientas ADN.
|
||||||
Recuerda que el flujo es actualizar el plan, bitácora web y recien ejecutas las tareas.
|
Recuerda que el flujo es actualizar el plan, bitácora web y recien ejecutar las tareas.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Vamos a seguir agregando mejoras segun el plan docs/proy/p2604_mejoras_ADN/P2604_mejoras_ADN.md.
|
||||||
|
|
||||||
|
Trata siempre de trabajar con herramientas definidas en ADN, mejorándolas y optimizándolas para poder reutilizarlas en el futuro. En su defecto, genera nuevas herramientas siguiendo estas mismas premisas, evitando el uso de soluciones temporales. Este enfoque está detallado en adn/README.md junto con el indice de herramientas ADN.
|
||||||
|
Recuerda que el flujo es actualizar el plan, bitácora web y recien ejecutar las tareas.
|
||||||
|
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
# P2604 - Mejoras al ADN
|
||||||
|
|
||||||
|
> Menos es Más + Mejora Continua + Armonía Integral
|
||||||
|
|
||||||
|
## Enfoque
|
||||||
|
|
||||||
|
Mejoras atómicas, incrementales y reutilizables. Una herramienta por vez.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Diagnóstico Integral
|
||||||
|
|
||||||
|
### Archivos Analizados
|
||||||
|
- `tools/run` (punto de entrada)
|
||||||
|
- `tools/cli/*.rb` (18 subcomandos)
|
||||||
|
- `tools/core/*.rb` (10 módulos)
|
||||||
|
- `tools/db/**/*.rb` (importers, exporters, core)
|
||||||
|
- `Gemfile`, `.bundle/config`
|
||||||
|
|
||||||
|
### Problemas Identificados
|
||||||
|
|
||||||
|
| # | Problema | Archivo(s) | Principio | Severidad |
|
||||||
|
| :--- | :--- | :--- | :--- | :--- |
|
||||||
|
| 1 | Sin tests para módulos core | `core/*.rb` | Menos es Más | Alta |
|
||||||
|
| 2 | Banner DEPRECATED incompleto | `cli/validador.rb` | Armonía | Media |
|
||||||
|
| 3 | core/validador.rb sin deprecation | `core/validador.rb` | Armonía | Media |
|
||||||
|
| 4 | Constantes de colores hardcodeadas | `core/colores.rb` | Menos es Más | Baja |
|
||||||
|
| 5 | Sin handler de errores global | `run` | Armonía | Alta |
|
||||||
|
| 6 | db/exporters sin tests | `db/exporters/*.rb` | Menos es Más | Media |
|
||||||
|
| 7 | Logger sin rotación | `core/logger.rb` | Mejora Cont. | Baja |
|
||||||
|
| 8 | Help inconsistente entre CLI | `cli/*.rb` | Armonía | Baja |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Plan de Mejoras (8 Fases)
|
||||||
|
|
||||||
|
### Fase 1: Tests Core (Menos es Más)
|
||||||
|
|
||||||
|
**Objetivo**: Coverage progresivo, un módulo a la vez.
|
||||||
|
|
||||||
|
| ID | Módulo | Tests | Estado |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| T1 | `spec/db/evento_spec.rb` | 3 | ✅ |
|
||||||
|
| T2 | `spec/core/colores_spec.rb` | 6 | ✅ |
|
||||||
|
| T3 | `spec/core/validador_spec.rb` | 6 | ✅ |
|
||||||
|
| T4 | `spec/cli/help_spec.rb` | 0 | 📍 |
|
||||||
|
|
||||||
|
**Total tests: 15 passing**
|
||||||
|
|
||||||
|
### Fase 2: Deprecation Banners (Armonía)
|
||||||
|
|
||||||
|
| ID | Archivo | Acción | Estado |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| D1 | `cli/validador.rb` | Banner agregado | ✅ |
|
||||||
|
| D2 | `core/validador.rb` | Revisar - útil para validaciones | ⏸️ |
|
||||||
|
| D3 | `db/exporters/md_exporter.rb` | Banner agregado | ✅ |
|
||||||
|
|
||||||
|
### Fase 3: Manejo de Errores (Armonía)
|
||||||
|
|
||||||
|
**Objetivo**: Consistencia en manejo de excepciones.
|
||||||
|
|
||||||
|
| ID | Mejora | Estado |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| E1 | Agregar `rescue` global en `run` | ✅ |
|
||||||
|
| E2 | Estandarizar mensajes de error | ✅ |
|
||||||
|
| E3 | Logging de excepciones no manejadas | ✅ |
|
||||||
|
|
||||||
|
**Herramientas creadas:**
|
||||||
|
- `core/error_handler.rb` - helper reutilizable
|
||||||
|
- Excepciones personalizadas en `core/constants.rb`: `ADN::Error`, `ADN::DBError`, `ADN::ValidationError`
|
||||||
|
|
||||||
|
### Fase 4: Constantes y Config (Menos es Más)
|
||||||
|
|
||||||
|
| ID | Mejora | Estado |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| C1 | Documentar constantes en constants.rb | ✅ |
|
||||||
|
| C2 | Agregar constantes reutilizables (estados, modos) | ✅ |
|
||||||
|
| C3 | Validar constantes al inicio | ✅ |
|
||||||
|
|
||||||
|
**Mejoras:**
|
||||||
|
- `constants.rb` ahora incluye: estados (⏳✅❌⚠️⏸️📍), modos (P/R), DB config, excepciones
|
||||||
|
|
||||||
|
### Fase 5: Logger Mejorado (Mejora Continua)
|
||||||
|
|
||||||
|
| ID | Mejora | Estado |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| L1 | Agregar rotación de logs | ✅ |
|
||||||
|
| L2 | Niveles de log configurables | ✅ |
|
||||||
|
| L3 | Output JSON estructurado | ✅ |
|
||||||
|
|
||||||
|
**Mejoras:** Rotación por tamaño (>10MB), mantiene 7 archivos, nivel mínimo configurable
|
||||||
|
|
||||||
|
### Fase 6: Help Consistente (Armonía)
|
||||||
|
|
||||||
|
| ID | CLI | Mejora | Estado |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| H1 | Todos | HelpFormatter helper | ✅ |
|
||||||
|
|
||||||
|
**Herramienta creada:** `core/help_formatter.rb`
|
||||||
|
|
||||||
|
### Fase 7: Documentación Técnica
|
||||||
|
|
||||||
|
| ID | Documento | Contenido | Estado |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| DOC1 | `docs/tecnica/adn_cli.md` | Referencia CLI | ✅ |
|
||||||
|
| DOC2 | `docs/tecnica/adn_core.md` | Módulos core | ✅ |
|
||||||
|
| DOC3 | `docs/tecnica/adn_tests.md` | Guía de testing | ✅ |
|
||||||
|
|
||||||
|
### Fase 8: CI/CD (Futuro)
|
||||||
|
|
||||||
|
| ID | Mejora | Estado |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| CI1 | GitHub Actions para tests | ✅ |
|
||||||
|
| CI2 | Lint automatizado | ✅ |
|
||||||
|
|
||||||
|
**Archivos creados:**
|
||||||
|
- `.github/workflows/test.yml`
|
||||||
|
- `.github/workflows/lint.yml`
|
||||||
|
- `adn/tools/.rubocop.yml`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Extensión P2604: Integración MSP (Marzo 2026)
|
||||||
|
|
||||||
|
> **Objetivo**: Integrar NotebookLM MSPs con el flujo de trabajo ADN
|
||||||
|
|
||||||
|
### Fase 9: Integración MSP (Armonía Integral)
|
||||||
|
|
||||||
|
| ID | Mejora | Herramienta | Estado |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| M1 | Auditoría de MSPs | CLI `msp:auditar` | ✅ |
|
||||||
|
| M2 | Registro de MSPs en BD | CLI `msp:registrar` | ✅ |
|
||||||
|
| M3 | Listado de MSPs disponibles | CLI `msp:listar` | ✅ |
|
||||||
|
| M4 | Documentación de integración | `docs/mcps/MSPs-ADN.md` | ✅ |
|
||||||
|
| M5 | Reutilizar NodosInfo en CLI | `nodos.rb`, `contexto.rb` | ✅ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Fase 10: Saneamiento y Consolidación (Armonía + Menos es Más)
|
||||||
|
|
||||||
|
> **Objetivo**: Un solo punto de verdad (`adn/README.md`) y un solo plan (`P2604`).
|
||||||
|
> Eliminar código muerto, archivos obsoletos y duplicaciones.
|
||||||
|
|
||||||
|
| ID | Mejora | Tipo | Estado |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| S1 | Eliminar `when 'triggers'` duplicado en `run` | Fix bug | ✅ |
|
||||||
|
| S2 | Eliminar `cli/commit.rb` (truncado, sin uso) | Limpieza | ✅ |
|
||||||
|
| S3 | Eliminar `cli/inicio.rb` y `cli/cierre.rb` (legacy, reemplazados por `jornada.rb`) | Limpieza | ✅ |
|
||||||
|
| S4 | Mover planes obsoletos de `docs/plan/` a `docs/_hist/` | Consolidación | ✅ |
|
||||||
|
| S5 | Mover docs técnicos duplicados (`docs/tecnica/`) a `docs/_hist/` | Consolidación | ✅ |
|
||||||
|
| S6 | Eliminar manifiesto vacío `docs/proyectos/P2604_proyecto_p2604.md` | Limpieza | ✅ |
|
||||||
|
| S7 | Corregir referencia a plan obsoleto en `run` | Fix | ✅ |
|
||||||
|
| S8 | Dejar de cargar `core/validador.rb` obsoleto en `run` | Limpieza | ✅ |
|
||||||
|
| S9 | Limpiar progreso duplicado en este plan | Doc | ✅ |
|
||||||
|
| S10 | Migrar `SubcomandoSalud` al namespace `ADN::` | Refactor | ⏳ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Progreso
|
||||||
|
|
||||||
|
```
|
||||||
|
Fase 1: ██████████ 100% (T1-T3: 15 tests)
|
||||||
|
Fase 2: ██████░░░░ 67% (D1, D3 done)
|
||||||
|
Fase 3: ██████████ 100% (E1-E3: error handler)
|
||||||
|
Fase 4: ██████████ 100% (C1-C3: constants.rb)
|
||||||
|
Fase 5: ██████████ 100% (L1-L3: logger.rb)
|
||||||
|
Fase 6: ██████████ 100% (H1: HelpFormatter)
|
||||||
|
Fase 7: ██████████ 100% (DOC1-DOC3: docs/tecnica/)
|
||||||
|
Fase 8: ██████████ 100% (CI1-CI2: GitHub Actions)
|
||||||
|
Fase 9: ██████████ 100% (M1-M5: MSP CLI)
|
||||||
|
Fase 10: █████████░ 90% (S1-S9 done, S10 pendiente)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Métricas Totales
|
||||||
|
|
||||||
|
| Métrica | Valor |
|
||||||
|
| :--- | :--- |
|
||||||
|
| Tests pasando | 15 |
|
||||||
|
| Archivos creados | 12 |
|
||||||
|
| Archivos modificados | 8 |
|
||||||
|
| Archivos limpiados/movidos | 20+ |
|
||||||
|
| Fases completadas | 9/10 (90%) |
|
||||||
|
| Documentos técnicos | Consolidados en `adn/README.md` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Próxima Acción
|
||||||
|
|
||||||
|
**S10**: Migrar `SubcomandoSalud` al namespace `ADN::` y reescribir para consultar DB (DB-First)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Referencias
|
||||||
|
|
||||||
|
- Punto de verdad: [`adn/README.md`](../../adn/README.md)
|
||||||
|
- ADN IA: [`05_ia.md`](../../adn/05_ia.md)
|
||||||
|
- Bitácoras: ID 1054-1056, 1073
|
||||||
|
- Planes históricos movidos a: `docs/_hist/plan/`
|
||||||
Reference in New Issue
Block a user