@saulwade/swl-ses 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +238 -0
- package/README.md +560 -0
- package/_userland/agentes/.gitkeep +0 -0
- package/_userland/habilidades/.gitkeep +0 -0
- package/agentes/.evolved.json +9 -0
- package/agentes/accesibilidad-wcag-swl.md +692 -0
- package/agentes/arquitecto-swl.md +238 -0
- package/agentes/auto-evolucion-swl.md +854 -0
- package/agentes/backend-api-swl.md +470 -0
- package/agentes/backend-csharp-swl.md +418 -0
- package/agentes/backend-go-swl.md +388 -0
- package/agentes/backend-java-swl.md +279 -0
- package/agentes/backend-node-swl.md +477 -0
- package/agentes/backend-python-swl.md +608 -0
- package/agentes/backend-rust-swl.md +362 -0
- package/agentes/backend-workers-swl.md +480 -0
- package/agentes/cloud-infra-swl.md +485 -0
- package/agentes/consolidador-swl.md +539 -0
- package/agentes/datos-swl.md +584 -0
- package/agentes/depurador-swl.md +349 -0
- package/agentes/devops-ci-swl.md +374 -0
- package/agentes/disenador-ui-swl.md +558 -0
- package/agentes/documentador-swl.md +343 -0
- package/agentes/evals/arquitecto-swl.evals.json +56 -0
- package/agentes/evals/auto-evolucion-swl.evals.json +68 -0
- package/agentes/evals/implementador-swl.evals.json +56 -0
- package/agentes/evals/orquestador-swl.evals.json +60 -0
- package/agentes/evals/perfilador-usuario-swl.evals.json +60 -0
- package/agentes/evals/red-team-swl.evals.json +59 -0
- package/agentes/evals/revisor-codigo-swl.evals.json +59 -0
- package/agentes/frontend-angular-swl.md +627 -0
- package/agentes/frontend-css-swl.md +720 -0
- package/agentes/frontend-react-swl.md +696 -0
- package/agentes/frontend-swl.md +500 -0
- package/agentes/frontend-tailwind-swl.md +830 -0
- package/agentes/implementador-swl.md +328 -0
- package/agentes/investigador-swl.md +430 -0
- package/agentes/investigador-ux-swl.md +500 -0
- package/agentes/llm-apps-swl.md +276 -0
- package/agentes/migrador-swl.md +417 -0
- package/agentes/mobile-android-swl.md +509 -0
- package/agentes/mobile-cross-swl.md +539 -0
- package/agentes/mobile-ios-swl.md +500 -0
- package/agentes/mobile-testing-swl.md +300 -0
- package/agentes/notificador-swl.md +916 -0
- package/agentes/observabilidad-swl.md +436 -0
- package/agentes/orquestador-swl.md +884 -0
- package/agentes/pagos-swl.md +283 -0
- package/agentes/perfilador-usuario-swl.md +306 -0
- package/agentes/planificador-swl.md +402 -0
- package/agentes/producto-prd-swl.md +587 -0
- package/agentes/red-team-swl.md +216 -0
- package/agentes/release-manager-swl.md +568 -0
- package/agentes/rendimiento-swl.md +714 -0
- package/agentes/resolutor-build-swl.md +243 -0
- package/agentes/revisor-angular-swl.md +276 -0
- package/agentes/revisor-codigo-swl.md +348 -0
- package/agentes/revisor-csharp-swl.md +262 -0
- package/agentes/revisor-go-swl.md +257 -0
- package/agentes/revisor-java-swl.md +255 -0
- package/agentes/revisor-kotlin-swl.md +271 -0
- package/agentes/revisor-nextjs-swl.md +279 -0
- package/agentes/revisor-php-swl.md +269 -0
- package/agentes/revisor-react-swl.md +276 -0
- package/agentes/revisor-rust-swl.md +344 -0
- package/agentes/revisor-seguridad-swl.md +390 -0
- package/agentes/revisor-swift-swl.md +266 -0
- package/agentes/revisor-typescript-swl.md +344 -0
- package/agentes/sre-swl.md +265 -0
- package/agentes/tdd-qa-swl.md +354 -0
- package/agentes/ux-disenador-swl.md +501 -0
- package/bin/lib/bot-comandos.js +1030 -0
- package/bin/lib/bot-discovery.js +182 -0
- package/bin/lib/bot-git.js +142 -0
- package/bin/swl-ses.js +325 -0
- package/bin/swl-telegram-bot.js +442 -0
- package/bin/swl-telegram-bot.plist +21 -0
- package/bin/swl-telegram-bot.service +14 -0
- package/comandos/swl/.evolved.json +23 -0
- package/comandos/swl/actualizar.md +174 -0
- package/comandos/swl/adoptar-proyecto.md +207 -0
- package/comandos/swl/aprender.md +701 -0
- package/comandos/swl/auditar-deps.md +134 -0
- package/comandos/swl/autoresearch.md +170 -0
- package/comandos/swl/ayuda.md +224 -0
- package/comandos/swl/brainstorm.md +50 -0
- package/comandos/swl/checkpoint.md +330 -0
- package/comandos/swl/compactar.md +283 -0
- package/comandos/swl/configurar-ci.md +227 -0
- package/comandos/swl/contexto.md +112 -0
- package/comandos/swl/contribuir.md +233 -0
- package/comandos/swl/crear-skill.md +292 -0
- package/comandos/swl/cron.md +196 -0
- package/comandos/swl/dashboard.md +146 -0
- package/comandos/swl/discutir-fase.md +230 -0
- package/comandos/swl/ejecutar-fase.md +135 -0
- package/comandos/swl/evaluar-skill.md +487 -0
- package/comandos/swl/evolucion-estado.md +142 -0
- package/comandos/swl/evolucionar.md +259 -0
- package/comandos/swl/exportar-vault.md +189 -0
- package/comandos/swl/gateway.md +158 -0
- package/comandos/swl/inbox.md +116 -0
- package/comandos/swl/instalar.md +220 -0
- package/comandos/swl/instintos.md +86 -0
- package/comandos/swl/mapear-codebase.md +312 -0
- package/comandos/swl/mcp-status.md +175 -0
- package/comandos/swl/metricas.md +270 -0
- package/comandos/swl/modelo.md +102 -0
- package/comandos/swl/notificaciones.md +396 -0
- package/comandos/swl/nuevo-proyecto.md +154 -0
- package/comandos/swl/planear-fase.md +221 -0
- package/comandos/swl/plugins.md +256 -0
- package/comandos/swl/reflect-skills.md +125 -0
- package/comandos/swl/release.md +217 -0
- package/comandos/swl/revisar-impacto.md +206 -0
- package/comandos/swl/revisar.md +330 -0
- package/comandos/swl/salud.md +363 -0
- package/comandos/swl/sesiones.md +200 -0
- package/comandos/swl/skill-search.md +113 -0
- package/comandos/swl/verificar.md +585 -0
- package/comandos/swl/wiki.md +620 -0
- package/contextos/dev.md +32 -0
- package/contextos/research.md +30 -0
- package/contextos/review.md +31 -0
- package/habilidades/accesibilidad-a11y/SKILL.md +201 -0
- package/habilidades/accesibilidad-a11y/evals/evals.json +56 -0
- package/habilidades/accesibilidad-a11y/recursos/ejemplos-y-checklist-completo.md +441 -0
- package/habilidades/agent-browser/SKILL.md +218 -0
- package/habilidades/agentes-como-servicio/SKILL.md +218 -0
- package/habilidades/ai-runtime-security/SKILL.md +273 -0
- package/habilidades/angular-avanzado/SKILL.md +164 -0
- package/habilidades/angular-avanzado/recursos/ejemplos-avanzados.md +219 -0
- package/habilidades/angular-moderno/SKILL.md +186 -0
- package/habilidades/angular-moderno/evals/evals.json +45 -0
- package/habilidades/angular-moderno/recursos/ejemplos-avanzados.md +106 -0
- package/habilidades/api-rest-diseno/SKILL.md +191 -0
- package/habilidades/api-rest-diseno/recursos/openapi-template.yaml +506 -0
- package/habilidades/api-rest-diseno/recursos/referencia-api.md +140 -0
- package/habilidades/aprendizaje-continuo/SKILL.md +151 -0
- package/habilidades/aprendizaje-continuo/evals/evals.json +53 -0
- package/habilidades/aprendizaje-continuo/recursos/referencia-instintos.md +290 -0
- package/habilidades/async-python/SKILL.md +149 -0
- package/habilidades/async-python/evals/evals.json +47 -0
- package/habilidades/async-python/recursos/patrones-y-ejemplos-completos.md +292 -0
- package/habilidades/auth-patrones/.evolved.json +9 -0
- package/habilidades/auth-patrones/SKILL.md +413 -0
- package/habilidades/auth-patrones/recursos/implementaciones-completas.md +229 -0
- package/habilidades/auto-evolucion-protocolo/SKILL.md +276 -0
- package/habilidades/auto-evolucion-protocolo/evals/evals.json +55 -0
- package/habilidades/auto-evolucion-protocolo/recursos/referencia-completa.md +145 -0
- package/habilidades/autoresearch/SKILL.md +268 -0
- package/habilidades/autoresearch/evals/evals.json +41 -0
- package/habilidades/autoresearch/recursos/checklist-template.md +191 -0
- package/habilidades/autoresearch/scripts/calcular-score.js +88 -0
- package/habilidades/azure-cloud/SKILL.md +308 -0
- package/habilidades/azure-cloud/recursos/aks.md +327 -0
- package/habilidades/backend-mcp-servidor/SKILL.md +270 -0
- package/habilidades/backend-production-resilience/SKILL.md +288 -0
- package/habilidades/brainstorming/SKILL.md +295 -0
- package/habilidades/brainstorming/recursos/componentes-html.md +247 -0
- package/habilidades/build-errors-cpp/SKILL.md +270 -0
- package/habilidades/build-errors-csharp/SKILL.md +265 -0
- package/habilidades/build-errors-go/SKILL.md +306 -0
- package/habilidades/build-errors-java/SKILL.md +278 -0
- package/habilidades/build-errors-kotlin/SKILL.md +303 -0
- package/habilidades/build-errors-nextjs/SKILL.md +312 -0
- package/habilidades/build-errors-php/SKILL.md +270 -0
- package/habilidades/build-errors-python/SKILL.md +292 -0
- package/habilidades/build-errors-rust/SKILL.md +284 -0
- package/habilidades/build-errors-swift/SKILL.md +272 -0
- package/habilidades/build-errors-typescript/SKILL.md +369 -0
- package/habilidades/checklist-calidad/SKILL.md +271 -0
- package/habilidades/checklist-calidad/recursos/quality-report-template.md +148 -0
- package/habilidades/checklist-seguridad/SKILL.md +285 -0
- package/habilidades/checkpoints-verificacion/SKILL.md +298 -0
- package/habilidades/checkpoints-verificacion/recursos/checkpoint-templates.md +360 -0
- package/habilidades/ci-cd-pipelines/SKILL.md +157 -0
- package/habilidades/ci-cd-pipelines/recursos/github-actions-template.yaml +403 -0
- package/habilidades/ci-cd-pipelines/recursos/pipelines-completos.md +487 -0
- package/habilidades/cloud-aws/SKILL.md +142 -0
- package/habilidades/cloud-aws/recursos/servicios-aws-referencia.md +321 -0
- package/habilidades/compactacion-contexto/SKILL.md +247 -0
- package/habilidades/contenedores-docker/SKILL.md +137 -0
- package/habilidades/contenedores-docker/recursos/dockerfile-template.dockerfile +160 -0
- package/habilidades/contenedores-docker/recursos/ejemplos-y-configuraciones.md +327 -0
- package/habilidades/context-builder/SKILL.md +170 -0
- package/habilidades/control-profundidad/SKILL.md +128 -0
- package/habilidades/csharp-experto/SKILL.md +322 -0
- package/habilidades/csharp-patrones/SKILL.md +316 -0
- package/habilidades/csharp-testing/SKILL.md +286 -0
- package/habilidades/css-moderno/SKILL.md +166 -0
- package/habilidades/css-moderno/evals/evals.json +43 -0
- package/habilidades/css-moderno/recursos/ejemplos-y-patrones-completos.md +337 -0
- package/habilidades/datos-etl/SKILL.md +129 -0
- package/habilidades/datos-etl/recursos/implementaciones-completas.md +322 -0
- package/habilidades/dbml-experto/SKILL.md +339 -0
- package/habilidades/dbml-experto/evals/evals.json +56 -0
- package/habilidades/dependencias-auditoria/SKILL.md +320 -0
- package/habilidades/deprecacion-migracion/SKILL.md +169 -0
- package/habilidades/deprecacion-migracion/recursos/implementaciones-completas.md +220 -0
- package/habilidades/design-tokens/SKILL.md +158 -0
- package/habilidades/design-tokens/recursos/tokens-y-configuracion.md +363 -0
- package/habilidades/devsecops-pipeline-security/SKILL.md +309 -0
- package/habilidades/diagrama-arquitectura/SKILL.md +165 -0
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -0
- package/habilidades/discutir-fase/SKILL.md +188 -0
- package/habilidades/diseno-herramientas-agente/SKILL.md +199 -0
- package/habilidades/diseno-responsivo/SKILL.md +186 -0
- package/habilidades/diseno-responsivo/recursos/ejemplos-layouts.md +156 -0
- package/habilidades/django-experto/SKILL.md +205 -0
- package/habilidades/django-experto/recursos/async-django.md +390 -0
- package/habilidades/django-experto/recursos/drf-patrones.md +438 -0
- package/habilidades/django-experto/recursos/orm-avanzado.md +382 -0
- package/habilidades/django-experto/recursos/referencia-completa.md +188 -0
- package/habilidades/django-experto/recursos/testing-django.md +415 -0
- package/habilidades/doc-sync/SKILL.md +280 -0
- package/habilidades/drift-detection/SKILL.md +179 -0
- package/habilidades/ejecutar-fase/SKILL.md +468 -0
- package/habilidades/estilo-sin-ai-isms/SKILL.md +775 -0
- package/habilidades/estilo-sin-ai-isms/evals/evals.json +63 -0
- package/habilidades/estilo-sin-ai-isms/scripts/detectar_aiisms.py +500 -0
- package/habilidades/estructura-proyecto-claude/SKILL.md +215 -0
- package/habilidades/estructura-proyecto-claude/recursos/claude-md-template.md +261 -0
- package/habilidades/estructura-proyecto-claude/recursos/configuracion-y-extensiones.md +176 -0
- package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +289 -0
- package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +77 -0
- package/habilidades/estructura-proyecto-claude/recursos/variantes-por-stack.md +177 -0
- package/habilidades/evaluacion-agentes/SKILL.md +314 -0
- package/habilidades/event-driven/SKILL.md +153 -0
- package/habilidades/event-driven/recursos/implementaciones-completas.md +423 -0
- package/habilidades/extraccion-documentos/SKILL.md +221 -0
- package/habilidades/extractor-de-aprendizajes/.evolved.json +9 -0
- package/habilidades/extractor-de-aprendizajes/SKILL.md +311 -0
- package/habilidades/extractor-de-aprendizajes/evals/evals.json +55 -0
- package/habilidades/fastapi-experto/SKILL.md +221 -0
- package/habilidades/fastapi-experto/recursos/async-patterns.md +438 -0
- package/habilidades/fastapi-experto/recursos/dependency-injection.md +330 -0
- package/habilidades/fastapi-experto/recursos/referencia-completa.md +79 -0
- package/habilidades/fastapi-experto/recursos/testing-httpx.md +420 -0
- package/habilidades/filament-admin/SKILL.md +290 -0
- package/habilidades/frontend-avanzado/SKILL.md +257 -0
- package/habilidades/frontend-avanzado/recursos/apis-nativas-ejemplos.md +341 -0
- package/habilidades/gcp-cloud/SKILL.md +260 -0
- package/habilidades/gcp-cloud/recursos/gke.md +234 -0
- package/habilidades/gcp-cloud/recursos/terraform-gcp.md +307 -0
- package/habilidades/generacion-mermaid/SKILL.md +229 -0
- package/habilidades/git-worktrees-paralelo/SKILL.md +270 -0
- package/habilidades/go-experto/SKILL.md +305 -0
- package/habilidades/go-patrones/SKILL.md +299 -0
- package/habilidades/go-testing/SKILL.md +291 -0
- package/habilidades/graphql-experto/SKILL.md +323 -0
- package/habilidades/guardrail-semantico/SKILL.md +282 -0
- package/habilidades/harness-claude-code/SKILL.md +299 -0
- package/habilidades/iam-secretos/SKILL.md +265 -0
- package/habilidades/iam-secretos/recursos/implementaciones-completas.md +356 -0
- package/habilidades/infra-github-actions/SKILL.md +166 -0
- package/habilidades/instalar-sistema/.evolved.json +9 -0
- package/habilidades/instalar-sistema/SKILL.md +221 -0
- package/habilidades/java-experto/SKILL.md +290 -0
- package/habilidades/java-patrones/SKILL.md +275 -0
- package/habilidades/java-testing/SKILL.md +288 -0
- package/habilidades/kotlin-compose/SKILL.md +278 -0
- package/habilidades/kotlin-compose/recursos/animaciones-performance.md +93 -0
- package/habilidades/kotlin-experto/SKILL.md +318 -0
- package/habilidades/kotlin-testing/SKILL.md +267 -0
- package/habilidades/kotlin-testing/recursos/testing-avanzado.md +74 -0
- package/habilidades/kubernetes-orquestacion/SKILL.md +152 -0
- package/habilidades/kubernetes-orquestacion/recursos/manifiestos-completos.md +452 -0
- package/habilidades/langchain-langraph/SKILL.md +386 -0
- package/habilidades/langchain-langraph/recursos/evaluacion-rag.md +321 -0
- package/habilidades/langchain-langraph/recursos/rag-maturity-model.md +225 -0
- package/habilidades/langchain-langraph/recursos/vectorstores.md +306 -0
- package/habilidades/legacy-code-rescue/SKILL.md +267 -0
- package/habilidades/likec4-experto/SKILL.md +412 -0
- package/habilidades/likec4-experto/evals/evals.json +69 -0
- package/habilidades/manejo-errores/.evolved.json +9 -0
- package/habilidades/manejo-errores/SKILL.md +407 -0
- package/habilidades/manejo-errores/recursos/implementaciones-completas.md +248 -0
- package/habilidades/mapear-codebase/SKILL.md +275 -0
- package/habilidades/memoria-busqueda/SKILL.md +194 -0
- package/habilidades/memoria-busqueda/evals/evals.json +44 -0
- package/habilidades/meta-skills-estandar/SKILL.md +298 -0
- package/habilidades/meta-skills-estandar/recursos/anti-patrones-y-leyes.md +205 -0
- package/habilidades/meta-skills-estandar/recursos/frameworks-seguridad.md +107 -0
- package/habilidades/meta-skills-estandar/recursos/idiomas-framework.md +60 -0
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -0
- package/habilidades/microservicios/SKILL.md +155 -0
- package/habilidades/microservicios/recursos/patrones-y-ejemplos-completos.md +325 -0
- package/habilidades/mobile-flutter/SKILL.md +199 -0
- package/habilidades/mobile-flutter/recursos/ejemplos-completos.md +319 -0
- package/habilidades/mobile-react-native/SKILL.md +176 -0
- package/habilidades/mobile-react-native/recursos/ejemplos-completos.md +216 -0
- package/habilidades/mongodb-experto/SKILL.md +302 -0
- package/habilidades/monitoring-alertas/SKILL.md +201 -0
- package/habilidades/monitoring-alertas/recursos/instrumentacion-y-alertas.md +301 -0
- package/habilidades/nestjs-experto/SKILL.md +307 -0
- package/habilidades/nestjs-experto/recursos/guards-interceptors.md +339 -0
- package/habilidades/nestjs-experto/recursos/modulos-di.md +287 -0
- package/habilidades/nestjs-experto/recursos/testing-nestjs.md +354 -0
- package/habilidades/nextjs-experto/SKILL.md +335 -0
- package/habilidades/nextjs-patrones/SKILL.md +303 -0
- package/habilidades/nextjs-testing/SKILL.md +331 -0
- package/habilidades/node-experto/.evolved.json +9 -0
- package/habilidades/node-experto/SKILL.md +266 -0
- package/habilidades/node-experto/recursos/patrones-completos.md +283 -0
- package/habilidades/notificaciones-multicanal/SKILL.md +159 -0
- package/habilidades/notificaciones-multicanal/recursos/config-template.json +115 -0
- package/habilidades/notificaciones-multicanal/recursos/configuracion-y-templates.md +303 -0
- package/habilidades/nuevo-proyecto/SKILL.md +204 -0
- package/habilidades/orquestacion-async/SKILL.md +303 -0
- package/habilidades/paid-media-tracking/SKILL.md +269 -0
- package/habilidades/paid-media-tracking/recursos/auditoria-tracking.md +220 -0
- package/habilidades/paid-media-tracking/recursos/google-ads-api.md +215 -0
- package/habilidades/patrones-python/SKILL.md +228 -0
- package/habilidades/patrones-python/evals/evals.json +56 -0
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -0
- package/habilidades/patrones-python/recursos/referencia-completa.md +202 -0
- package/habilidades/perfil-usuario/SKILL.md +200 -0
- package/habilidades/perfil-usuario/evals/evals.json +55 -0
- package/habilidades/performance-baseline/SKILL.md +297 -0
- package/habilidades/php-experto/SKILL.md +291 -0
- package/habilidades/php-patrones/SKILL.md +306 -0
- package/habilidades/php-testing/SKILL.md +280 -0
- package/habilidades/planear-fase/SKILL.md +269 -0
- package/habilidades/postgresql-experto/SKILL.md +151 -0
- package/habilidades/postgresql-experto/evals/evals.json +53 -0
- package/habilidades/postgresql-experto/recursos/referencia-completa.md +215 -0
- package/habilidades/prevencion-racionalizacion/SKILL.md +175 -0
- package/habilidades/prevencion-sobreingenieria/SKILL.md +323 -0
- package/habilidades/privacy-memoria/SKILL.md +141 -0
- package/habilidades/privacy-memoria/evals/evals.json +43 -0
- package/habilidades/prompt-engineering/SKILL.md +518 -0
- package/habilidades/prompt-engineering/recursos/patrones-avanzados.md +467 -0
- package/habilidades/rag-arquitectura/SKILL.md +338 -0
- package/habilidades/rails-experto/SKILL.md +237 -0
- package/habilidades/rails-experto/recursos/active-record.md +260 -0
- package/habilidades/rails-experto/recursos/hotwire-turbo.md +293 -0
- package/habilidades/rails-experto/recursos/testing-rspec.md +362 -0
- package/habilidades/react-experto/SKILL.md +209 -0
- package/habilidades/react-experto/evals/evals.json +55 -0
- package/habilidades/react-experto/recursos/patrones-y-ejemplos-completos.md +240 -0
- package/habilidades/react-optimizacion/SKILL.md +174 -0
- package/habilidades/react-optimizacion/recursos/patrones-avanzados.md +138 -0
- package/habilidades/redis-experto/SKILL.md +305 -0
- package/habilidades/release-semver/.evolved.json +9 -0
- package/habilidades/release-semver/SKILL.md +248 -0
- package/habilidades/release-semver/scripts/generar-changelog.sh +238 -0
- package/habilidades/rust-experto/SKILL.md +400 -0
- package/habilidades/rust-patrones/SKILL.md +296 -0
- package/habilidades/rust-testing/SKILL.md +311 -0
- package/habilidades/seguridad-skills-ia/SKILL.md +262 -0
- package/habilidades/sql-optimizacion/SKILL.md +200 -0
- package/habilidades/sql-optimizacion/evals/evals.json +54 -0
- package/habilidades/sql-optimizacion/recursos/patrones-sql-avanzados.md +131 -0
- package/habilidades/sre-patrones/SKILL.md +333 -0
- package/habilidades/sre-patrones/recursos/chaos-engineering.md +241 -0
- package/habilidades/sre-patrones/recursos/oncall-design.md +236 -0
- package/habilidades/stripe-pagos/SKILL.md +550 -0
- package/habilidades/stripe-pagos/recursos/errores-reintentos.md +390 -0
- package/habilidades/stripe-pagos/recursos/stripe-connect.md +290 -0
- package/habilidades/structured-outputs/SKILL.md +343 -0
- package/habilidades/swift-experto/SKILL.md +320 -0
- package/habilidades/swift-experto/recursos/keychain-y-wrappers.md +110 -0
- package/habilidades/swift-patrones/SKILL.md +313 -0
- package/habilidades/swift-patrones/recursos/tca-ejemplo-completo.md +113 -0
- package/habilidades/swift-testing/SKILL.md +254 -0
- package/habilidades/swift-testing/recursos/xcuitest-planes.md +143 -0
- package/habilidades/swl-dashboard/SKILL.md +370 -0
- package/habilidades/swl-markitdown/SKILL.md +285 -0
- package/habilidades/swl-markitdown/evals/evals.json +52 -0
- package/habilidades/swl-revisar-impacto/SKILL.md +233 -0
- package/habilidades/tailwind-experto/SKILL.md +240 -0
- package/habilidades/tailwind-experto/recursos/referencia-completa.md +184 -0
- package/habilidades/tdd-workflow/SKILL.md +293 -0
- package/habilidades/terraform-experto/SKILL.md +321 -0
- package/habilidades/testing-python/SKILL.md +340 -0
- package/habilidades/testing-python/recursos/ejemplos-completos.md +167 -0
- package/habilidades/threat-model-lite/SKILL.md +246 -0
- package/habilidades/tracing-processor/SKILL.md +212 -0
- package/habilidades/tracking-measurement/SKILL.md +239 -0
- package/habilidades/tracking-measurement/recursos/consent-mode.md +231 -0
- package/habilidades/tracking-measurement/recursos/gtm-datalayer.md +216 -0
- package/habilidades/tracking-measurement/recursos/meta-capi.md +262 -0
- package/habilidades/typescript-avanzado/SKILL.md +144 -0
- package/habilidades/typescript-avanzado/evals/evals.json +55 -0
- package/habilidades/typescript-avanzado/recursos/patrones-y-ejemplos-completos.md +298 -0
- package/habilidades/typescript-diagnosticos/SKILL.md +513 -0
- package/habilidades/ux-diseno/SKILL.md +116 -0
- package/habilidades/ux-diseno/evals/evals.json +43 -0
- package/habilidades/ux-diseno/recursos/patrones-ux-referencia.md +214 -0
- package/habilidades/validacion-ci-sistema/SKILL.md +136 -0
- package/habilidades/validacion-ci-sistema/recursos/validadores-completos.md +369 -0
- package/habilidades/validacion-ci-sistema/scripts/validar-sistema.sh +286 -0
- package/habilidades/verificacion-evidencia/SKILL.md +160 -0
- package/habilidades/verificar-trabajo/SKILL.md +303 -0
- package/habilidades/verificar-trabajo/recursos/plantilla-verificacion.md +60 -0
- package/habilidades/wiki-conocimiento/SKILL.md +276 -0
- package/habilidades/wireframes-flujos/SKILL.md +212 -0
- package/habilidades/wireframes-flujos/recursos/referencia-completa.md +192 -0
- package/habilidades/workflow-claude-code/SKILL.md +260 -0
- package/habilidades/workflow-claude-code/recursos/referencia-completa.md +109 -0
- package/hooks/_run-hook.sh +57 -0
- package/hooks/actualizar-perfil-usuario.js +364 -0
- package/hooks/agente-lifecycle.js +71 -0
- package/hooks/aiisms-detector.js +173 -0
- package/hooks/audit-trail.js +204 -0
- package/hooks/auto-background.js +97 -0
- package/hooks/auto-consolidacion.js +178 -0
- package/hooks/auto-evolucion.js +666 -0
- package/hooks/auto-restaurar-settings.js +360 -0
- package/hooks/calidad-pre-commit.js +929 -0
- package/hooks/calidad-typescript.js +511 -0
- package/hooks/captura-feedback-usuario.js +148 -0
- package/hooks/check-update.js +211 -0
- package/hooks/clasificador-mensajes.js +271 -0
- package/hooks/degradacion-instintos.js +272 -0
- package/hooks/escaneo-secretos.js +389 -0
- package/hooks/extraccion-aprendizajes.js +763 -0
- package/hooks/grafo-contexto.js +129 -0
- package/hooks/graph-update.js +67 -0
- package/hooks/guardrail-modelo.js +247 -0
- package/hooks/inbox-aviso.js +75 -0
- package/hooks/inyeccion-contexto.js +246 -0
- package/hooks/lib/abort-registry.js +214 -0
- package/hooks/lib/agent-backend.js +210 -0
- package/hooks/lib/agent-comms.js +263 -0
- package/hooks/lib/agent-issue-codes.js +284 -0
- package/hooks/lib/agent-matcher.js +189 -0
- package/hooks/lib/async-hook-registry.js +252 -0
- package/hooks/lib/atomic-write.js +130 -0
- package/hooks/lib/auto-consolidator.js +335 -0
- package/hooks/lib/canary-skills.js +187 -0
- package/hooks/lib/consolidation-lock.js +291 -0
- package/hooks/lib/context-builder.js +430 -0
- package/hooks/lib/context-compressor.js +657 -0
- package/hooks/lib/convergence-detector.js +105 -0
- package/hooks/lib/delegation-tracker.js +198 -0
- package/hooks/lib/detectar-package-manager.js +423 -0
- package/hooks/lib/edit-accumulator.js +171 -0
- package/hooks/lib/error-classifier.js +308 -0
- package/hooks/lib/event-bus.js +112 -0
- package/hooks/lib/evolution-tracker.js +442 -0
- package/hooks/lib/execution-state.js +316 -0
- package/hooks/lib/fingerprint-id.js +135 -0
- package/hooks/lib/gateway-notify.js +116 -0
- package/hooks/lib/graph-security.js +75 -0
- package/hooks/lib/guardrail-metrics.js +202 -0
- package/hooks/lib/hook-circuit-breaker.js +206 -0
- package/hooks/lib/loop-detector.js +267 -0
- package/hooks/lib/mcp-health.js +184 -0
- package/hooks/lib/mcp-pool.js +436 -0
- package/hooks/lib/memory-search.js +506 -0
- package/hooks/lib/merkle-audit.js +96 -0
- package/hooks/lib/model-router.js +222 -0
- package/hooks/lib/normalize-error.js +324 -0
- package/hooks/lib/normalize-input.js +65 -0
- package/hooks/lib/nudge-tracker.js +306 -0
- package/hooks/lib/otlp-exporter.js +365 -0
- package/hooks/lib/performance-marks.js +239 -0
- package/hooks/lib/privacy-filter.js +128 -0
- package/hooks/lib/prompt-injection-scanner.js +209 -0
- package/hooks/lib/provenance-tracker.js +183 -0
- package/hooks/lib/rate-limit-tracker.js +253 -0
- package/hooks/lib/reflect-classifier.js +164 -0
- package/hooks/lib/resource-quota.js +122 -0
- package/hooks/lib/retry-jitter.js +165 -0
- package/hooks/lib/risk-engine.js +368 -0
- package/hooks/lib/run-log.js +408 -0
- package/hooks/lib/session-fts.js +379 -0
- package/hooks/lib/session-store.js +293 -0
- package/hooks/lib/singleton-guard.js +159 -0
- package/hooks/lib/skill-auditor.js +588 -0
- package/hooks/lib/sync-status.js +228 -0
- package/hooks/lib/taint-tracker.js +107 -0
- package/hooks/lib/task-service.js +295 -0
- package/hooks/lib/tech-skills-map.js +146 -0
- package/hooks/lib/telegram-cliente.js +159 -0
- package/hooks/lib/telegram-config.js +170 -0
- package/hooks/lib/token-budget.js +156 -0
- package/hooks/lib/token-estimator.js +420 -0
- package/hooks/lib/toon-compressor.js +245 -0
- package/hooks/lib/usage-model.js +183 -0
- package/hooks/lib/variable-resolver.js +230 -0
- package/hooks/linea-estado.js +324 -0
- package/hooks/metricas-evolucion.js +209 -0
- package/hooks/monitor-contexto.js +325 -0
- package/hooks/notificacion-sesion-stop.js +198 -0
- package/hooks/notificacion-telegram-notification.js +4 -0
- package/hooks/notificacion-telegram-subagent.js +4 -0
- package/hooks/notificacion-telegram.js +267 -0
- package/hooks/preservar-estado-pre-compact.js +150 -0
- package/hooks/proteccion-rutas.js +366 -0
- package/hooks/registro-turnos.js +209 -0
- package/hooks/resumen-sesion.js +249 -0
- package/hooks/risk-scoring.js +323 -0
- package/hooks/rotar-audit-auto.js +122 -0
- package/hooks/sugerir-regenerar-inventario.js +170 -0
- package/hooks/telemetria-agentes.js +167 -0
- package/hooks/tracking-costos.js +688 -0
- package/instintos/global.yaml +8 -0
- package/instintos/perfil-usuario.yaml +53 -0
- package/instintos/prompt-appendices.yaml +57 -0
- package/instintos/proyecto.yaml +372 -0
- package/manifiestos/gateway-config.json +77 -0
- package/manifiestos/handoff-context.json +223 -0
- package/manifiestos/hook-profiles.json +44 -0
- package/manifiestos/hooks-config.json +360 -0
- package/manifiestos/modulos.json +1173 -0
- package/manifiestos/perfiles.json +404 -0
- package/package.json +86 -0
- package/plantillas/ESTADO.md +109 -0
- package/plantillas/HOJA-RUTA.md +143 -0
- package/plantillas/PROYECTO.md +122 -0
- package/plantillas/REQUISITOS.md +132 -0
- package/plantillas/auditor-veto-template.md +105 -0
- package/plantillas/github-workflows/README.md +47 -0
- package/plantillas/github-workflows/release-please.yml +44 -0
- package/plantillas/github-workflows/swl-ci.yml +107 -0
- package/plantillas/github-workflows/swl-security.yml +51 -0
- package/plantillas/mcp-mineru.json +13 -0
- package/plantillas/research/ARQUITECTURA.md +220 -0
- package/plantillas/research/FUNCIONALIDADES.md +175 -0
- package/plantillas/research/RESUMEN.md +165 -0
- package/plantillas/research/STACK.md +233 -0
- package/plantillas/research/TRAMPAS.md +299 -0
- package/plantillas/skill-evals-template.json +44 -0
- package/plugin.json +343 -0
- package/reglas/accesibilidad.md +269 -0
- package/reglas/api-diseno.md +400 -0
- package/reglas/arquitectura.md +352 -0
- package/reglas/brevedad-output.md +124 -0
- package/reglas/cloud-infra.md +247 -0
- package/reglas/docs.md +245 -0
- package/reglas/estilo-codigo.md +201 -0
- package/reglas/git-workflow.md +245 -0
- package/reglas/gobernanza.md +271 -0
- package/reglas/harness-claude-code.md +213 -0
- package/reglas/hooks.md +186 -0
- package/reglas/lenguajes/csharp/estilo-codigo.md +231 -0
- package/reglas/lenguajes/csharp/hooks.md +281 -0
- package/reglas/lenguajes/csharp/patrones.md +226 -0
- package/reglas/lenguajes/csharp/seguridad.md +258 -0
- package/reglas/lenguajes/csharp/testing.md +176 -0
- package/reglas/lenguajes/go/estilo-codigo.md +195 -0
- package/reglas/lenguajes/go/hooks.md +249 -0
- package/reglas/lenguajes/go/patrones.md +249 -0
- package/reglas/lenguajes/go/seguridad.md +225 -0
- package/reglas/lenguajes/go/testing.md +272 -0
- package/reglas/lenguajes/java/estilo-codigo.md +217 -0
- package/reglas/lenguajes/java/hooks.md +251 -0
- package/reglas/lenguajes/java/patrones.md +226 -0
- package/reglas/lenguajes/java/seguridad.md +233 -0
- package/reglas/lenguajes/java/testing.md +238 -0
- package/reglas/lenguajes/kotlin/estilo-codigo.md +208 -0
- package/reglas/lenguajes/kotlin/hooks.md +245 -0
- package/reglas/lenguajes/kotlin/patrones.md +201 -0
- package/reglas/lenguajes/kotlin/seguridad.md +202 -0
- package/reglas/lenguajes/kotlin/testing.md +236 -0
- package/reglas/lenguajes/nextjs/estilo-codigo.md +175 -0
- package/reglas/lenguajes/nextjs/hooks.md +186 -0
- package/reglas/lenguajes/nextjs/patrones.md +225 -0
- package/reglas/lenguajes/nextjs/seguridad.md +216 -0
- package/reglas/lenguajes/nextjs/testing.md +193 -0
- package/reglas/lenguajes/php/estilo-codigo.md +228 -0
- package/reglas/lenguajes/php/hooks.md +165 -0
- package/reglas/lenguajes/php/patrones.md +233 -0
- package/reglas/lenguajes/php/seguridad.md +186 -0
- package/reglas/lenguajes/php/testing.md +205 -0
- package/reglas/lenguajes/rust/estilo-codigo.md +207 -0
- package/reglas/lenguajes/rust/hooks.md +240 -0
- package/reglas/lenguajes/rust/patrones.md +250 -0
- package/reglas/lenguajes/rust/seguridad.md +221 -0
- package/reglas/lenguajes/rust/testing.md +194 -0
- package/reglas/lenguajes/swift/estilo-codigo.md +238 -0
- package/reglas/lenguajes/swift/hooks.md +257 -0
- package/reglas/lenguajes/swift/patrones.md +235 -0
- package/reglas/lenguajes/swift/seguridad.md +248 -0
- package/reglas/lenguajes/swift/testing.md +242 -0
- package/reglas/markitdown.md +60 -0
- package/reglas/memoria-consolidada.md +209 -0
- package/reglas/patrones.md +225 -0
- package/reglas/performance.md +195 -0
- package/reglas/pruebas.md +159 -0
- package/reglas/seguridad-agentes.md +351 -0
- package/reglas/seguridad.md +151 -0
- package/reglas/skills-estandar.md +373 -0
- package/reglas/testing.md +193 -0
- package/schemas/agent-contract.json +176 -0
- package/schemas/agent-frontmatter.schema.json +149 -0
- package/schemas/agent-message.schema.json +53 -0
- package/schemas/agent-output-implementacion.schema.json +85 -0
- package/schemas/agent-output-planificacion.schema.json +113 -0
- package/schemas/agent-output-review.schema.json +78 -0
- package/schemas/diary-entry.schema.json +80 -0
- package/schemas/hook-profiles.schema.json +39 -0
- package/schemas/hooks-config.schema.json +74 -0
- package/schemas/instinct.schema.json +115 -0
- package/schemas/modulos.schema.json +29 -0
- package/schemas/perfiles.schema.json +28 -0
- package/schemas/plugin.schema.json +64 -0
- package/schemas/skill-evals.schema.json +95 -0
- package/schemas/skill-frontmatter.schema.json +170 -0
- package/scripts/actualizar.js +145 -0
- package/scripts/audit-skills.sh +78 -0
- package/scripts/auditar-agentes-gaps.js +149 -0
- package/scripts/auditar-cobertura-frameworks.js +241 -0
- package/scripts/auditar-skills-gaps.js +206 -0
- package/scripts/bootstrap-instintos.js +259 -0
- package/scripts/check-update.js +109 -0
- package/scripts/comandos/agents.js +105 -0
- package/scripts/comandos/info.js +108 -0
- package/scripts/comandos/install-asistido.js +186 -0
- package/scripts/comandos/skills.js +211 -0
- package/scripts/configurar-branch-protection.js +418 -0
- package/scripts/daemon-swl.py +388 -0
- package/scripts/desinstalar.js +130 -0
- package/scripts/doctor.js +559 -0
- package/scripts/field-report.js +199 -0
- package/scripts/generar-inventario.js +317 -0
- package/scripts/inbox-tmux-inject.js +161 -0
- package/scripts/inferir-herramientas-permitidas.js +586 -0
- package/scripts/inicializar.js +133 -0
- package/scripts/instalador.js +1031 -0
- package/scripts/instalar-git-hook.js +122 -0
- package/scripts/lib/agp-frontmatter.js +222 -0
- package/scripts/lib/append-con-marcadores.js +199 -0
- package/scripts/lib/artefactos-python.js +43 -0
- package/scripts/lib/audit-query.js +221 -0
- package/scripts/lib/autostart-linux.js +347 -0
- package/scripts/lib/autostart-macos.js +360 -0
- package/scripts/lib/autostart-windows.js +307 -0
- package/scripts/lib/budget-enforcer.js +252 -0
- package/scripts/lib/claude-sessions.js +285 -0
- package/scripts/lib/configurar-ci.js +380 -0
- package/scripts/lib/console-span-exporter.js +92 -0
- package/scripts/lib/contadores-inventario.js +217 -0
- package/scripts/lib/dashboard-widgets.js +290 -0
- package/scripts/lib/detectar-runtime.js +279 -0
- package/scripts/lib/detectar-stack.js +187 -0
- package/scripts/lib/diary-entry.js +234 -0
- package/scripts/lib/drift-detector.js +545 -0
- package/scripts/lib/estado.js +124 -0
- package/scripts/lib/gestor-componentes.js +243 -0
- package/scripts/lib/gitignore-manifest.js +305 -0
- package/scripts/lib/graph-analyze.py +556 -0
- package/scripts/lib/graph-builder.py +485 -0
- package/scripts/lib/graph-cluster.py +259 -0
- package/scripts/lib/health-row.js +168 -0
- package/scripts/lib/hooks-settings.js +789 -0
- package/scripts/lib/manifiestos.js +138 -0
- package/scripts/lib/mc-client.js +137 -0
- package/scripts/lib/notificaciones-telegram.js +1107 -0
- package/scripts/lib/npm-version.js +261 -0
- package/scripts/lib/paquetes-conocidos.js +50 -0
- package/scripts/lib/preservar-usuario.js +586 -0
- package/scripts/lib/prompt-builder.js +264 -0
- package/scripts/lib/resolver-externo.js +332 -0
- package/scripts/lib/schedule-parser.js +305 -0
- package/scripts/lib/scoring-instintos.js +240 -0
- package/scripts/lib/seguridad.js +160 -0
- package/scripts/lib/selector-interactivo.js +152 -0
- package/scripts/lib/semantic-search.js +242 -0
- package/scripts/lib/skill-discovery.js +234 -0
- package/scripts/lib/skill-metrics.js +246 -0
- package/scripts/lib/skill-normalizer.js +112 -0
- package/scripts/lib/skills-hub.js +340 -0
- package/scripts/lib/span-schema.js +134 -0
- package/scripts/lib/tool-cost-analyzer.js +255 -0
- package/scripts/lib/tracing-processor-interface.js +286 -0
- package/scripts/lib/transformadores/base.js +80 -0
- package/scripts/lib/transformadores/claude.js +124 -0
- package/scripts/lib/transformadores/codex.js +115 -0
- package/scripts/lib/transformadores/copilot.js +106 -0
- package/scripts/lib/transformadores/gemini.js +74 -0
- package/scripts/lib/transformadores/index.js +35 -0
- package/scripts/lib/transformadores/opencode.js +75 -0
- package/scripts/lib/ui.js +259 -0
- package/scripts/limpiar-artefactos-python.js +131 -0
- package/scripts/mcp-orchestrator.py +386 -0
- package/scripts/mcp-pool-manager.py +352 -0
- package/scripts/mcp-telemetry.py +378 -0
- package/scripts/poblar-evolvable.js +226 -0
- package/scripts/publicar.js +287 -0
- package/scripts/reflect-skills.js +403 -0
- package/scripts/rotar-audit-logs.js +185 -0
- package/scripts/run-skill-evals.js +242 -0
- package/scripts/smoke-test.js +374 -0
- package/scripts/token-analysis.py +471 -0
- package/scripts/validar-manifest.js +195 -0
- package/scripts/validar-memoria.js +321 -0
- package/scripts/validar-tests-aislamiento.js +184 -0
- package/scripts/validar-tokens-test.js +208 -0
- package/scripts/validar.js +147 -0
- package/scripts/validate-markdown.py +339 -0
- package/scripts/validate-skills.py +385 -0
- package/scripts/vendor/claude-usage/README.md +116 -0
- package/scripts/vendor/claude-usage/cli.py +334 -0
- package/scripts/vendor/claude-usage/dashboard.py +795 -0
- package/scripts/vendor/claude-usage/scanner.py +467 -0
- package/scripts/vendor/markitdown/cli.py +194 -0
- package/scripts/verificar-evolucion.js +289 -0
- package/scripts/verificar-release.js +494 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generador de LaunchAgent plist para el daemon swl-telegram-bot en macOS.
|
|
5
|
+
*
|
|
6
|
+
* SEGURIDAD:
|
|
7
|
+
* - NUNCA escribe en ~/Library/LaunchDaemons — eso requiere root.
|
|
8
|
+
* Solo LaunchAgents (nivel usuario, sin privilegios de sistema).
|
|
9
|
+
* - NUNCA hardcodea rutas absolutas. Resuelve bin/ con __dirname en runtime.
|
|
10
|
+
* - NUNCA incluye credenciales en el plist — las credenciales viven en .env.
|
|
11
|
+
* - Solo opera en process.platform === 'darwin'. Otros OS reciben error explícito.
|
|
12
|
+
*
|
|
13
|
+
* Rollback manual (si algo sale mal):
|
|
14
|
+
* launchctl unload -w ~/Library/LaunchAgents/com.swl.telegram-bot.plist
|
|
15
|
+
* rm ~/Library/LaunchAgents/com.swl.telegram-bot.plist
|
|
16
|
+
*
|
|
17
|
+
* @module scripts/lib/autostart-macos
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const { execFileSync } = require('node:child_process');
|
|
21
|
+
const path = require('node:path');
|
|
22
|
+
const fs = require('node:fs');
|
|
23
|
+
const os = require('node:os');
|
|
24
|
+
|
|
25
|
+
const LABEL_PLIST = 'com.swl.telegram-bot';
|
|
26
|
+
const NOMBRE_PLIST = `${LABEL_PLIST}.plist`;
|
|
27
|
+
|
|
28
|
+
// Ruta del log de autostart — diagnóstico humano sin secrets
|
|
29
|
+
const RUTA_LOG_AUTOSTART = path.join(
|
|
30
|
+
os.homedir(), '.claude', 'notifications', 'autostart.log'
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Helpers
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Escribe una línea al log de autostart.
|
|
39
|
+
* @param {string} nivel
|
|
40
|
+
* @param {string} mensaje
|
|
41
|
+
*/
|
|
42
|
+
function _log(nivel, mensaje) {
|
|
43
|
+
const linea = `[${new Date().toISOString()}] [${nivel}] [macos] ${mensaje}\n`;
|
|
44
|
+
try {
|
|
45
|
+
const dir = path.dirname(RUTA_LOG_AUTOSTART);
|
|
46
|
+
if (!fs.existsSync(dir)) {
|
|
47
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
fs.appendFileSync(RUTA_LOG_AUTOSTART, linea, 'utf8');
|
|
50
|
+
} catch (_) {}
|
|
51
|
+
process.stderr.write(linea);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* REM-6: Valida que una ruta no contiene caracteres peligrosos que podrían
|
|
56
|
+
* inyectarse en el contenido del plist XML (caracteres de control, XML especiales,
|
|
57
|
+
* nuevas líneas).
|
|
58
|
+
*
|
|
59
|
+
* Previene que un path manipulado rompa el XML del plist o inyecte etiquetas.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} ruta - Ruta a validar.
|
|
62
|
+
* @returns {boolean} true si la ruta es segura para incluir en el plist.
|
|
63
|
+
*/
|
|
64
|
+
function _validarRutaSegura(ruta) {
|
|
65
|
+
if (typeof ruta !== 'string' || ruta.trim() === '') return false;
|
|
66
|
+
// Rechazar caracteres de control (incluye \n, \r, \t) y caracteres XML especiales
|
|
67
|
+
// que podrían romper el formato INI del unit o el XML de un plist.
|
|
68
|
+
// eslint-disable-next-line no-control-regex
|
|
69
|
+
const RX_PELIGROSOS = /[\x00-\x1F\x7F<>&"']/;
|
|
70
|
+
return !RX_PELIGROSOS.test(ruta);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Construye la ruta absoluta al daemon bin/swl-telegram-bot.js
|
|
75
|
+
* a partir de __dirname (scripts/lib/), NO hardcodeada.
|
|
76
|
+
*
|
|
77
|
+
* @returns {string} Ruta absoluta resuelta con path.resolve.
|
|
78
|
+
*/
|
|
79
|
+
function _rutaDaemon() {
|
|
80
|
+
return path.resolve(__dirname, '..', '..', 'bin', 'swl-telegram-bot.js');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Ruta del directorio ~/Library/LaunchAgents.
|
|
85
|
+
* @returns {string}
|
|
86
|
+
*/
|
|
87
|
+
function _dirLaunchAgents() {
|
|
88
|
+
return path.join(os.homedir(), 'Library', 'LaunchAgents');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Ruta del archivo plist instalado.
|
|
93
|
+
* @returns {string}
|
|
94
|
+
*/
|
|
95
|
+
function _rutaPlistInstalado() {
|
|
96
|
+
return path.join(_dirLaunchAgents(), NOMBRE_PLIST);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Valida que estamos en macOS.
|
|
101
|
+
* @returns {{ ok: false, error: string } | null} null si OK, error si no.
|
|
102
|
+
*/
|
|
103
|
+
function _validarPlatform() {
|
|
104
|
+
if (process.platform !== 'darwin') {
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
error: `autostart-macos solo aplica en macOS (plataforma detectada: ${process.platform})`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Ejecuta launchctl de forma segura (sin shell).
|
|
115
|
+
*
|
|
116
|
+
* @param {string[]} args - Argumentos para launchctl.
|
|
117
|
+
* @returns {{ ok: boolean, stdout?: string, error?: string }}
|
|
118
|
+
*/
|
|
119
|
+
function _ejecutarLaunchctl(...args) {
|
|
120
|
+
try {
|
|
121
|
+
const stdout = execFileSync(
|
|
122
|
+
'launchctl',
|
|
123
|
+
args,
|
|
124
|
+
{
|
|
125
|
+
encoding: 'utf8',
|
|
126
|
+
timeout: 30_000,
|
|
127
|
+
// NUNCA shell: true
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
return { ok: true, stdout: (stdout || '').trim() };
|
|
131
|
+
} catch (err) {
|
|
132
|
+
return { ok: false, error: (err.stderr || err.message || '').trim() };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// Generador de template
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Genera el contenido del plist con los paths resueltos en runtime.
|
|
142
|
+
*
|
|
143
|
+
* El XML resultante es válido para launchd:
|
|
144
|
+
* - RunAtLoad: arranca al cargar el agent (login)
|
|
145
|
+
* - KeepAlive: relanza si el proceso muere
|
|
146
|
+
* - ProgramArguments: [nodePath, binPath]
|
|
147
|
+
* - Stdout/Stderr a archivos de log separados
|
|
148
|
+
*
|
|
149
|
+
* @param {string} nodePath - Ruta absoluta al ejecutable node.
|
|
150
|
+
* @param {string} binPath - Ruta absoluta al daemon.
|
|
151
|
+
* @param {string} homeDir - Directorio home del usuario.
|
|
152
|
+
* @returns {string} Contenido XML del plist.
|
|
153
|
+
*/
|
|
154
|
+
function generarContenidoPlist(nodePath, binPath, homeDir) {
|
|
155
|
+
const logDir = path.join(homeDir, '.claude', 'notifications');
|
|
156
|
+
return [
|
|
157
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
158
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
159
|
+
'<plist version="1.0">',
|
|
160
|
+
'<dict>',
|
|
161
|
+
' <key>Label</key>',
|
|
162
|
+
` <string>${LABEL_PLIST}</string>`,
|
|
163
|
+
' <key>ProgramArguments</key>',
|
|
164
|
+
' <array>',
|
|
165
|
+
` <string>${nodePath}</string>`,
|
|
166
|
+
` <string>${binPath}</string>`,
|
|
167
|
+
' </array>',
|
|
168
|
+
' <key>RunAtLoad</key>',
|
|
169
|
+
' <true/>',
|
|
170
|
+
' <key>KeepAlive</key>',
|
|
171
|
+
' <true/>',
|
|
172
|
+
' <key>StandardOutPath</key>',
|
|
173
|
+
` <string>${logDir}/bot.stdout.log</string>`,
|
|
174
|
+
' <key>StandardErrorPath</key>',
|
|
175
|
+
` <string>${logDir}/bot.stderr.log</string>`,
|
|
176
|
+
'</dict>',
|
|
177
|
+
'</plist>',
|
|
178
|
+
'',
|
|
179
|
+
].join('\n');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
// API pública
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Instala el LaunchAgent plist y lo carga con launchctl.
|
|
188
|
+
*
|
|
189
|
+
* Idempotente: si el plist ya existe, lo reemplaza y recarga.
|
|
190
|
+
*
|
|
191
|
+
* @returns {{ ok: boolean, mensaje?: string, error?: string }}
|
|
192
|
+
*/
|
|
193
|
+
function install() {
|
|
194
|
+
const platErr = _validarPlatform();
|
|
195
|
+
if (platErr) return platErr;
|
|
196
|
+
|
|
197
|
+
const rutaDaemon = _rutaDaemon();
|
|
198
|
+
_log('INFO', `Instalando LaunchAgent "${LABEL_PLIST}" → ${rutaDaemon}`);
|
|
199
|
+
|
|
200
|
+
// Verificar que el daemon existe
|
|
201
|
+
if (!fs.existsSync(rutaDaemon)) {
|
|
202
|
+
const error = `El daemon no existe: ${rutaDaemon}. Verifica la instalación de swl-ses.`;
|
|
203
|
+
_log('ERROR', error);
|
|
204
|
+
return { ok: false, error };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Resolver el ejecutable de node
|
|
208
|
+
let nodePath;
|
|
209
|
+
try {
|
|
210
|
+
nodePath = execFileSync('which', ['node'], { encoding: 'utf8', timeout: 5000 }).trim();
|
|
211
|
+
} catch (_) {
|
|
212
|
+
nodePath = process.execPath;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (!nodePath || !fs.existsSync(nodePath)) {
|
|
216
|
+
const error = `node no encontrado en PATH. Instala Node.js antes de activar el autostart. Intentado: ${nodePath}`;
|
|
217
|
+
_log('ERROR', error);
|
|
218
|
+
return { ok: false, error };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// REM-6: validar que los paths no tienen caracteres peligrosos antes de
|
|
222
|
+
// incluirlos en el plist XML. Previene inyección de etiquetas XML.
|
|
223
|
+
if (!_validarRutaSegura(nodePath) || !_validarRutaSegura(rutaDaemon)) {
|
|
224
|
+
const error = 'Ruta con caracteres no permitidos — abortando generación del plist.';
|
|
225
|
+
_log('ERROR', error);
|
|
226
|
+
return { ok: false, error };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Generar contenido del plist
|
|
230
|
+
const homeDir = os.homedir();
|
|
231
|
+
const contenido = generarContenidoPlist(nodePath, rutaDaemon, homeDir);
|
|
232
|
+
const dirAgents = _dirLaunchAgents();
|
|
233
|
+
const rutaPlist = _rutaPlistInstalado();
|
|
234
|
+
|
|
235
|
+
// Crear directorio si no existe (puede no existir en instalaciones limpias)
|
|
236
|
+
if (!fs.existsSync(dirAgents)) {
|
|
237
|
+
try {
|
|
238
|
+
fs.mkdirSync(dirAgents, { recursive: true });
|
|
239
|
+
} catch (err) {
|
|
240
|
+
const error = `No se pudo crear ${dirAgents}: ${err.message}`;
|
|
241
|
+
_log('ERROR', error);
|
|
242
|
+
return { ok: false, error };
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Si ya existe un plist cargado, descargarlo primero para evitar conflicto
|
|
247
|
+
const estadoPrev = status();
|
|
248
|
+
if (estadoPrev.cargado) {
|
|
249
|
+
_log('INFO', 'Descargando LaunchAgent previo antes de reemplazarlo...');
|
|
250
|
+
_ejecutarLaunchctl('unload', '-w', rutaPlist);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Escribir el plist
|
|
254
|
+
try {
|
|
255
|
+
fs.writeFileSync(rutaPlist, contenido, 'utf8');
|
|
256
|
+
_log('INFO', `Plist escrito en: ${rutaPlist}`);
|
|
257
|
+
} catch (err) {
|
|
258
|
+
const error = `No se pudo escribir el plist: ${err.message}`;
|
|
259
|
+
_log('ERROR', error);
|
|
260
|
+
return { ok: false, error };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Cargar con launchctl load -w
|
|
264
|
+
_log('INFO', 'Ejecutando launchctl load -w...');
|
|
265
|
+
const loadRes = _ejecutarLaunchctl('load', '-w', rutaPlist);
|
|
266
|
+
if (!loadRes.ok) {
|
|
267
|
+
const error = `launchctl load -w falló: ${loadRes.error}`;
|
|
268
|
+
_log('ERROR', error);
|
|
269
|
+
return { ok: false, error };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Verificar estado
|
|
273
|
+
const estadoActual = status();
|
|
274
|
+
_log('INFO', `LaunchAgent cargado — instalado: ${estadoActual.instalado}, cargado: ${estadoActual.cargado}`);
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
ok: true,
|
|
278
|
+
mensaje: `LaunchAgent "${LABEL_PLIST}" instalado y cargado. Arrancará al próximo login. Cargado ahora: ${estadoActual.cargado}.`,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Descarga y elimina el LaunchAgent.
|
|
284
|
+
*
|
|
285
|
+
* Idempotente: si el plist no existe, devuelve ok:true con mensaje informativo.
|
|
286
|
+
*
|
|
287
|
+
* @returns {{ ok: boolean, mensaje?: string, error?: string }}
|
|
288
|
+
*/
|
|
289
|
+
function uninstall() {
|
|
290
|
+
const platErr = _validarPlatform();
|
|
291
|
+
if (platErr) return platErr;
|
|
292
|
+
|
|
293
|
+
_log('INFO', `Desinstalando LaunchAgent "${LABEL_PLIST}"...`);
|
|
294
|
+
|
|
295
|
+
const rutaPlist = _rutaPlistInstalado();
|
|
296
|
+
|
|
297
|
+
// Si no hay plist instalado, idempotente
|
|
298
|
+
if (!fs.existsSync(rutaPlist)) {
|
|
299
|
+
_log('INFO', `El plist "${NOMBRE_PLIST}" no estaba instalado — nada que hacer.`);
|
|
300
|
+
return { ok: true, mensaje: `no había LaunchAgent "${LABEL_PLIST}" instalado` };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Descargar con launchctl unload -w
|
|
304
|
+
_log('INFO', 'Ejecutando launchctl unload -w...');
|
|
305
|
+
const unloadRes = _ejecutarLaunchctl('unload', '-w', rutaPlist);
|
|
306
|
+
if (!unloadRes.ok) {
|
|
307
|
+
_log('WARN', `launchctl unload -w reportó: ${unloadRes.error} (continuando con eliminación del archivo)`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Eliminar el archivo
|
|
311
|
+
try {
|
|
312
|
+
fs.unlinkSync(rutaPlist);
|
|
313
|
+
_log('INFO', `Plist eliminado: ${rutaPlist}`);
|
|
314
|
+
} catch (err) {
|
|
315
|
+
const error = `No se pudo eliminar el plist: ${err.message}`;
|
|
316
|
+
_log('ERROR', error);
|
|
317
|
+
return { ok: false, error };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return { ok: true, mensaje: `LaunchAgent "${LABEL_PLIST}" descargado y eliminado.` };
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Consulta el estado del LaunchAgent.
|
|
325
|
+
*
|
|
326
|
+
* @returns {{ instalado: boolean, cargado: boolean, error?: string }}
|
|
327
|
+
*/
|
|
328
|
+
function status() {
|
|
329
|
+
const platErr = _validarPlatform();
|
|
330
|
+
if (platErr) return { instalado: false, cargado: false, error: platErr.error };
|
|
331
|
+
|
|
332
|
+
const rutaPlist = _rutaPlistInstalado();
|
|
333
|
+
const instalado = fs.existsSync(rutaPlist);
|
|
334
|
+
|
|
335
|
+
// Verificar si está cargado en launchd con list | grep
|
|
336
|
+
let cargado = false;
|
|
337
|
+
try {
|
|
338
|
+
const stdout = execFileSync(
|
|
339
|
+
'launchctl',
|
|
340
|
+
['list'],
|
|
341
|
+
{ encoding: 'utf8', timeout: 10_000 }
|
|
342
|
+
);
|
|
343
|
+
cargado = stdout.includes(LABEL_PLIST);
|
|
344
|
+
} catch (_) {
|
|
345
|
+
cargado = false;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return { instalado, cargado };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
module.exports = {
|
|
352
|
+
install,
|
|
353
|
+
uninstall,
|
|
354
|
+
status,
|
|
355
|
+
// Exportar para tests
|
|
356
|
+
generarContenidoPlist,
|
|
357
|
+
_rutaDaemon,
|
|
358
|
+
_dirLaunchAgents,
|
|
359
|
+
_validarRutaSegura,
|
|
360
|
+
};
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generador de Scheduled Task de Windows para el daemon swl-telegram-bot.
|
|
5
|
+
*
|
|
6
|
+
* SEGURIDAD:
|
|
7
|
+
* - NUNCA usa shell:true ni execSync con string. Solo execFileSync con array.
|
|
8
|
+
* - NUNCA incluye credenciales en el task — las credenciales viven en .env.
|
|
9
|
+
* - RunLevel Limited (sin elevación). Si el usuario necesita admin, que lo
|
|
10
|
+
* gestione él; el daño de un task con privilegios elevados es irreversible.
|
|
11
|
+
* - Solo opera en process.platform === 'win32'. Otros OS reciben error explícito.
|
|
12
|
+
*
|
|
13
|
+
* Rollback manual (si algo sale mal):
|
|
14
|
+
* powershell.exe -NoProfile -Command "Unregister-ScheduledTask -TaskName SWL-Telegram-Bot -Confirm:$false"
|
|
15
|
+
*
|
|
16
|
+
* @module scripts/lib/autostart-windows
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const { execFileSync } = require('node:child_process');
|
|
20
|
+
const path = require('node:path');
|
|
21
|
+
const fs = require('node:fs');
|
|
22
|
+
const os = require('node:os');
|
|
23
|
+
|
|
24
|
+
// Nombre canónico del Scheduled Task
|
|
25
|
+
const TASK_NAME = 'SWL-Telegram-Bot';
|
|
26
|
+
|
|
27
|
+
// Ruta del log de autostart — diagnóstico humano sin secrets
|
|
28
|
+
const RUTA_LOG_AUTOSTART = path.join(
|
|
29
|
+
os.homedir(), '.claude', 'notifications', 'autostart.log'
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Helpers
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Escribe una línea al log de autostart.
|
|
38
|
+
* @param {string} nivel
|
|
39
|
+
* @param {string} mensaje
|
|
40
|
+
*/
|
|
41
|
+
function _log(nivel, mensaje) {
|
|
42
|
+
const linea = `[${new Date().toISOString()}] [${nivel}] [windows] ${mensaje}\n`;
|
|
43
|
+
try {
|
|
44
|
+
const dir = path.dirname(RUTA_LOG_AUTOSTART);
|
|
45
|
+
if (!fs.existsSync(dir)) {
|
|
46
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
fs.appendFileSync(RUTA_LOG_AUTOSTART, linea, 'utf8');
|
|
49
|
+
} catch (_) {}
|
|
50
|
+
process.stderr.write(linea);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Regex para validar USERNAME seguro — solo caracteres alfanuméricos y separadores comunes.
|
|
54
|
+
// Exportada para tests unitarios de REM-3.
|
|
55
|
+
const _RX_USUARIO_SEGURO = /^[A-Za-z0-9._\-\\$ ]+$/;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* REM-3: Resuelve el nombre de usuario para el trigger AtLogOn del Scheduled Task.
|
|
59
|
+
*
|
|
60
|
+
* Valida el valor de las variables de entorno USERNAME/USER contra un regex seguro.
|
|
61
|
+
* Si el valor contiene caracteres peligrosos (que podrían inyectarse en el script
|
|
62
|
+
* de PowerShell), usa '$env:USERNAME' como fallback — PowerShell resuelve la variable
|
|
63
|
+
* en tiempo de ejecución sin concatenación en el código generado.
|
|
64
|
+
*
|
|
65
|
+
* @param {string|undefined} [username] - Valor de USERNAME (por defecto process.env.USERNAME).
|
|
66
|
+
* @param {string|undefined} [user] - Valor de USER (por defecto process.env.USER).
|
|
67
|
+
* @returns {string} Nombre de usuario validado o '$env:USERNAME'.
|
|
68
|
+
*/
|
|
69
|
+
function _resolverUsuario(username, user) {
|
|
70
|
+
const raw = (username !== undefined ? username : process.env.USERNAME)
|
|
71
|
+
|| (user !== undefined ? user : process.env.USER);
|
|
72
|
+
return (raw && _RX_USUARIO_SEGURO.test(raw)) ? raw : '$env:USERNAME';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Construye la ruta absoluta al daemon bin/swl-telegram-bot.js
|
|
77
|
+
* a partir de __dirname (scripts/lib/), NO hardcodeada.
|
|
78
|
+
*
|
|
79
|
+
* @returns {string} Ruta absoluta resuelta con path.resolve.
|
|
80
|
+
*/
|
|
81
|
+
function _rutaDaemon() {
|
|
82
|
+
return path.resolve(__dirname, '..', '..', 'bin', 'swl-telegram-bot.js');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Valida que estamos en Windows.
|
|
87
|
+
* @returns {{ ok: false, error: string } | null} null si OK, error si no.
|
|
88
|
+
*/
|
|
89
|
+
function _validarPlatform() {
|
|
90
|
+
if (process.platform !== 'win32') {
|
|
91
|
+
return {
|
|
92
|
+
ok: false,
|
|
93
|
+
error: `autostart-windows solo aplica en Windows (plataforma detectada: ${process.platform})`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Ejecuta un comando de PowerShell de forma segura:
|
|
101
|
+
* - Sin shell:true
|
|
102
|
+
* - Con array de argumentos (sin concatenación de strings)
|
|
103
|
+
* - Timeout de 30s
|
|
104
|
+
*
|
|
105
|
+
* @param {string} scriptBlock - Script PowerShell a ejecutar.
|
|
106
|
+
* @returns {{ ok: boolean, stdout?: string, error?: string }}
|
|
107
|
+
*/
|
|
108
|
+
function _ejecutarPowerShell(scriptBlock) {
|
|
109
|
+
try {
|
|
110
|
+
const stdout = execFileSync(
|
|
111
|
+
'powershell.exe',
|
|
112
|
+
[
|
|
113
|
+
'-NoProfile',
|
|
114
|
+
'-NonInteractive',
|
|
115
|
+
'-ExecutionPolicy', 'Bypass',
|
|
116
|
+
'-Command', scriptBlock,
|
|
117
|
+
],
|
|
118
|
+
{
|
|
119
|
+
encoding: 'utf8',
|
|
120
|
+
timeout: 30_000,
|
|
121
|
+
// NUNCA shell: true — previene inyección de comandos
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
return { ok: true, stdout: (stdout || '').trim() };
|
|
125
|
+
} catch (err) {
|
|
126
|
+
return { ok: false, error: (err.stderr || err.message || '').trim() };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
// API pública
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Instala el Scheduled Task de Windows para arrancar el daemon al logon.
|
|
136
|
+
*
|
|
137
|
+
* Parámetros del task:
|
|
138
|
+
* - Name: SWL-Telegram-Bot
|
|
139
|
+
* - Trigger: AtLogOn del usuario actual
|
|
140
|
+
* - Action: node.exe <ruta-absoluta-a-bin/swl-telegram-bot.js>
|
|
141
|
+
* - Principal: Interactive, RunLevel Limited (sin elevación)
|
|
142
|
+
* - StartWhenAvailable, RestartCount=3, RestartInterval=PT2M
|
|
143
|
+
* - AllowStartIfOnBatteries, sin timeout de idle
|
|
144
|
+
*
|
|
145
|
+
* Idempotente: si el task ya existe, lo reemplaza silenciosamente.
|
|
146
|
+
*
|
|
147
|
+
* @returns {{ ok: boolean, mensaje?: string, error?: string }}
|
|
148
|
+
*/
|
|
149
|
+
function install() {
|
|
150
|
+
const platErr = _validarPlatform();
|
|
151
|
+
if (platErr) return platErr;
|
|
152
|
+
|
|
153
|
+
const rutaDaemon = _rutaDaemon();
|
|
154
|
+
_log('INFO', `Instalando Scheduled Task "${TASK_NAME}" → ${rutaDaemon}`);
|
|
155
|
+
|
|
156
|
+
// Verificar que node está disponible
|
|
157
|
+
let nodeExe;
|
|
158
|
+
try {
|
|
159
|
+
nodeExe = execFileSync('where.exe', ['node'], { encoding: 'utf8', timeout: 5000 }).trim().split('\n')[0].trim();
|
|
160
|
+
} catch (_) {
|
|
161
|
+
const error = 'node.exe no encontrado en PATH. Instala Node.js antes de activar el autostart.';
|
|
162
|
+
_log('ERROR', error);
|
|
163
|
+
return { ok: false, error };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!nodeExe) {
|
|
167
|
+
const error = 'node.exe no encontrado en PATH. Instala Node.js antes de activar el autostart.';
|
|
168
|
+
_log('ERROR', error);
|
|
169
|
+
return { ok: false, error };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Verificar que el daemon existe
|
|
173
|
+
if (!fs.existsSync(rutaDaemon)) {
|
|
174
|
+
const error = `El daemon no existe: ${rutaDaemon}. Verifica la instalación de swl-ses.`;
|
|
175
|
+
_log('ERROR', error);
|
|
176
|
+
return { ok: false, error };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Construir el script de PowerShell con parámetros en variables separadas
|
|
180
|
+
// Nunca concatenar dentro del script — los valores ya están en variables escapadas
|
|
181
|
+
const nodePath = nodeExe.replace(/'/g, "''"); // Escape de comillas simples en PS
|
|
182
|
+
const daemonPath = rutaDaemon.replace(/'/g, "''");
|
|
183
|
+
|
|
184
|
+
// REM-3: validar USERNAME antes de interpolarlo en el script de PowerShell.
|
|
185
|
+
// _resolverUsuario usa un regex para rechazar caracteres peligrosos y cae a
|
|
186
|
+
// '$env:USERNAME' si el valor es sospechoso.
|
|
187
|
+
const usuario = _resolverUsuario();
|
|
188
|
+
|
|
189
|
+
const psScript = `
|
|
190
|
+
$action = New-ScheduledTaskAction -Execute '${nodePath}' -Argument '"${daemonPath}"'
|
|
191
|
+
$trigger = New-ScheduledTaskTrigger -AtLogOn -User '${usuario}'
|
|
192
|
+
$settings = New-ScheduledTaskSettingsSet \`
|
|
193
|
+
-StartWhenAvailable \`
|
|
194
|
+
-RestartCount 3 \`
|
|
195
|
+
-RestartInterval (New-TimeSpan -Minutes 2) \`
|
|
196
|
+
-AllowStartIfOnBatteries \`
|
|
197
|
+
-DontStopIfGoingOnBatteries \`
|
|
198
|
+
-ExecutionTimeLimit ([TimeSpan]::Zero)
|
|
199
|
+
$principal = New-ScheduledTaskPrincipal -LogonType Interactive -RunLevel Limited
|
|
200
|
+
Register-ScheduledTask \`
|
|
201
|
+
-TaskName '${TASK_NAME}' \`
|
|
202
|
+
-Action $action \`
|
|
203
|
+
-Trigger $trigger \`
|
|
204
|
+
-Settings $settings \`
|
|
205
|
+
-Principal $principal \`
|
|
206
|
+
-Force
|
|
207
|
+
`.trim();
|
|
208
|
+
|
|
209
|
+
_log('INFO', 'Registrando Scheduled Task vía PowerShell (RunLevel: Limited, sin elevación)...');
|
|
210
|
+
const resultado = _ejecutarPowerShell(psScript);
|
|
211
|
+
|
|
212
|
+
if (!resultado.ok) {
|
|
213
|
+
_log('ERROR', `Register-ScheduledTask falló: ${resultado.error}`);
|
|
214
|
+
return { ok: false, error: `Register-ScheduledTask falló: ${resultado.error}` };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Verificar que quedó registrado
|
|
218
|
+
const verificacion = status();
|
|
219
|
+
if (!verificacion.instalado) {
|
|
220
|
+
const error = 'El task fue registrado pero status() no lo detecta. Verifica manualmente en Programador de tareas.';
|
|
221
|
+
_log('WARN', error);
|
|
222
|
+
return { ok: false, error };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
_log('INFO', `Scheduled Task "${TASK_NAME}" instalado — estado: ${verificacion.estado}`);
|
|
226
|
+
return {
|
|
227
|
+
ok: true,
|
|
228
|
+
mensaje: `Scheduled Task "${TASK_NAME}" registrado. Arrancará al próximo logon. Estado actual: ${verificacion.estado}.`,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Desinstala el Scheduled Task de Windows.
|
|
234
|
+
*
|
|
235
|
+
* Idempotente: si el task no existe, devuelve ok:true con mensaje informativo.
|
|
236
|
+
*
|
|
237
|
+
* @returns {{ ok: boolean, mensaje?: string, error?: string }}
|
|
238
|
+
*/
|
|
239
|
+
function uninstall() {
|
|
240
|
+
const platErr = _validarPlatform();
|
|
241
|
+
if (platErr) return platErr;
|
|
242
|
+
|
|
243
|
+
_log('INFO', `Desinstalando Scheduled Task "${TASK_NAME}"...`);
|
|
244
|
+
|
|
245
|
+
// Verificar si existe primero para mensaje idempotente
|
|
246
|
+
const estadoActual = status();
|
|
247
|
+
if (!estadoActual.instalado) {
|
|
248
|
+
_log('INFO', `El task "${TASK_NAME}" no estaba instalado — nada que hacer.`);
|
|
249
|
+
return { ok: true, mensaje: `no había task "${TASK_NAME}" instalado` };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const psScript = `
|
|
253
|
+
Unregister-ScheduledTask -TaskName '${TASK_NAME}' -Confirm:$false -ErrorAction SilentlyContinue
|
|
254
|
+
`.trim();
|
|
255
|
+
|
|
256
|
+
const resultado = _ejecutarPowerShell(psScript);
|
|
257
|
+
|
|
258
|
+
if (!resultado.ok) {
|
|
259
|
+
// Algunos errores de Unregister son falsos positivos (el task ya se borró)
|
|
260
|
+
// Verificar el estado real antes de reportar error
|
|
261
|
+
const estadoPost = status();
|
|
262
|
+
if (!estadoPost.instalado) {
|
|
263
|
+
_log('INFO', `Task eliminado (PowerShell reportó: ${resultado.error})`);
|
|
264
|
+
return { ok: true, mensaje: `Scheduled Task "${TASK_NAME}" eliminado.` };
|
|
265
|
+
}
|
|
266
|
+
_log('ERROR', `Unregister-ScheduledTask falló: ${resultado.error}`);
|
|
267
|
+
return { ok: false, error: `Unregister-ScheduledTask falló: ${resultado.error}` };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
_log('INFO', `Scheduled Task "${TASK_NAME}" eliminado correctamente.`);
|
|
271
|
+
return { ok: true, mensaje: `Scheduled Task "${TASK_NAME}" eliminado.` };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Consulta el estado del Scheduled Task de Windows.
|
|
276
|
+
*
|
|
277
|
+
* @returns {{ instalado: boolean, estado: 'Ready'|'Running'|'Disabled'|null, error?: string }}
|
|
278
|
+
*/
|
|
279
|
+
function status() {
|
|
280
|
+
const platErr = _validarPlatform();
|
|
281
|
+
if (platErr) return { instalado: false, estado: null, error: platErr.error };
|
|
282
|
+
|
|
283
|
+
const psScript = `
|
|
284
|
+
$task = Get-ScheduledTask -TaskName '${TASK_NAME}' -ErrorAction SilentlyContinue
|
|
285
|
+
if ($task) { $task.State } else { 'NOT_FOUND' }
|
|
286
|
+
`.trim();
|
|
287
|
+
|
|
288
|
+
const resultado = _ejecutarPowerShell(psScript);
|
|
289
|
+
|
|
290
|
+
if (!resultado.ok) {
|
|
291
|
+
return { instalado: false, estado: null, error: resultado.error };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const estado = resultado.stdout;
|
|
295
|
+
|
|
296
|
+
if (estado === 'NOT_FOUND' || estado === '') {
|
|
297
|
+
return { instalado: false, estado: null };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Mapear estados de Windows a los esperados
|
|
301
|
+
const estadosValidos = ['Ready', 'Running', 'Disabled'];
|
|
302
|
+
const estadoNorm = estadosValidos.find(e => e.toLowerCase() === estado.toLowerCase()) || estado;
|
|
303
|
+
|
|
304
|
+
return { instalado: true, estado: estadoNorm };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
module.exports = { install, uninstall, status, _resolverUsuario };
|