[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:
@@ -33,6 +33,7 @@ class SubcomandoAyuda
|
||||
salud [--dashboard] [--json] Ver salud general del sistema ADN
|
||||
generar bitacora [fecha] Generar nueva bitácora (fecha: YYYY-MM-DD)
|
||||
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}tailscale <acción>#{Color::RESET} Gestión de identidades Tailscale
|
||||
#{Color::YELLOW}backup <nodo> [--mode MODO]#{Color::RESET} Ejecutar backup seguro de un nodo
|
||||
@@ -161,6 +162,22 @@ class SubcomandoAyuda
|
||||
puts "Ejemplos:"
|
||||
puts " ./adn/tools/run tailscale switch dasuten"
|
||||
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
|
||||
puts "#{Color::RED}✗ Subcomando desconocido: #{subcomando}#{Color::RESET}"
|
||||
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_relative '../core/colores'
|
||||
require_relative '../core/nodos_info'
|
||||
|
||||
class SubcomandoContexto
|
||||
def initialize(args, logger)
|
||||
@@ -60,77 +61,55 @@ class SubcomandoContexto
|
||||
exit 1
|
||||
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::DIM}#{'-' * 60}#{Color::RESET}"
|
||||
|
||||
# Buscar IP en la tabla
|
||||
if contenido.match(/\|\s*IP (?:LAN|Pública)?\s*\|\s*([^\|]+)\s*\|/)
|
||||
ip = $1.strip
|
||||
puts " #{Color::GREEN}IP:#{Color::RESET} #{ip}"
|
||||
if metadata
|
||||
puts " #{Color::GREEN}IP:#{Color::RESET} #{metadata[:ip] || '-'}"
|
||||
puts " #{Color::GREEN}Rol:#{Color::RESET} #{metadata[:host] ? 'VM' : (metadata[:so] || '-')}"
|
||||
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]
|
||||
generar_comando_ssh(nombre_nodo, ip)
|
||||
generar_comando_ssh(nombre_nodo, metadata[:ip], metadata[:usuario])
|
||||
end
|
||||
end
|
||||
|
||||
# Buscar rol
|
||||
if contenido.match(/\|\s*Rol(?: Principal)?\s*\|\s*([^\|]+)\s*\|/)
|
||||
rol = $1.strip
|
||||
puts " #{Color::GREEN}Rol:#{Color::RESET} #{rol}"
|
||||
end
|
||||
|
||||
# Buscar sistema operativo
|
||||
if contenido.match(/\|\s*Sistema Operativo\s*\|\s*([^\|]+)\s*\|/)
|
||||
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-')
|
||||
# Buscar en ontología para contexto de red
|
||||
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)
|
||||
clusters = ['srv-pmox1', 'srv-pmox2', 'srv-pmox3', 'srv-xen1', 'srv-dasu']
|
||||
cluster = clusters.find { |c| ontologia.include?("**#{c}**") && ontologia.include?(nombre_nodo) }
|
||||
if cluster && cluster != 'srv-ns8'
|
||||
puts " #{Color::YELLOW}🔧 Acceso probable via ProxyJump:#{Color::RESET}"
|
||||
puts " ssh -J root@#{cluster} rmonla@#{nombre_nodo}"
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
puts "#{Color::YELLOW}⚠ No se pudo extraer metadata del nodo#{Color::RESET}"
|
||||
end
|
||||
|
||||
puts "#{Color::DIM}#{'-' * 60}#{Color::RESET}"
|
||||
@logger.exito("Contexto obtenido para nodo: #{nombre_nodo}")
|
||||
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}"
|
||||
|
||||
# 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}"
|
||||
|
||||
# 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')
|
||||
puts " #{Color::DIM}O usando nombre (si está en ~/.ssh/config):#{Color::RESET}"
|
||||
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_relative '../core/colores'
|
||||
require_relative '../core/nodos_info'
|
||||
require_relative '../core/help_formatter'
|
||||
|
||||
module ADN
|
||||
class SubcomandoNodos
|
||||
@@ -89,7 +91,8 @@ module ADN
|
||||
nodo_nombre = File.basename(archivo, '.md')
|
||||
next if nodo_nombre.start_with?('_')
|
||||
|
||||
host = detectar_host(archivo)
|
||||
metadata = ADN::NodosInfo.extraer_metadata(nodo_nombre)
|
||||
host = metadata ? metadata[:host] : nil
|
||||
|
||||
if host
|
||||
servidores[host] << nodo_nombre
|
||||
@@ -127,28 +130,6 @@ module ADN
|
||||
puts ""
|
||||
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)
|
||||
nombre.start_with?('srv-') || nombre.start_with?('pc-') ||
|
||||
nombre == 'dtic-bitacoras' || nombre == 'cam-hikvision-24'
|
||||
@@ -180,15 +161,8 @@ module ADN
|
||||
end
|
||||
|
||||
nodos.each do |nodo|
|
||||
archivo = File.join(ADN::NODOS_DIR, "#{nodo}.md")
|
||||
contenido = File.read(archivo, encoding: 'UTF-8')
|
||||
|
||||
# Extraer rol si existe
|
||||
rol = '-'
|
||||
if contenido.match(/\*\*Rol(?:\s*Principal)?\*\*\s*[:\|]\s*([^\|\n]+)/)
|
||||
rol = $1.strip[0..40]
|
||||
end
|
||||
|
||||
metadata = ADN::NodosInfo.extraer_metadata(nodo)
|
||||
rol = metadata && metadata[:host] ? 'VM' : (metadata && metadata[:so] ? metadata[:so].to_s : '-')
|
||||
puts " 💻 #{nodo.ljust(22)} #{Color::DIM}#{rol}#{Color::RESET}"
|
||||
end
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# 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_relative '../core/colores'
|
||||
require_relative '../core/validador'
|
||||
|
||||
Reference in New Issue
Block a user