@saulwade/swl-ses 2.4.2 → 2.5.1
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 +194 -241
- package/README.md +600 -597
- package/agentes/_intent-spec.md +73 -73
- package/agentes/_propose-step.md +90 -90
- package/agentes/abogado-diablo-swl.md +145 -0
- package/agentes/accesibilidad-wcag-swl.md +690 -690
- package/agentes/arquitecto-swl.md +267 -267
- package/agentes/auto-evolucion-swl.md +908 -908
- package/agentes/backend-api-swl.md +1 -1
- package/agentes/backend-csharp-swl.md +420 -420
- package/agentes/backend-go-swl.md +390 -390
- package/agentes/backend-java-swl.md +281 -281
- package/agentes/backend-node-swl.md +1 -1
- package/agentes/backend-python-swl.md +1 -1
- package/agentes/backend-rust-swl.md +364 -364
- package/agentes/backend-workers-swl.md +482 -482
- package/agentes/cloud-infra-swl.md +509 -509
- package/agentes/consolidador-swl.md +541 -541
- package/agentes/datos-swl.md +1 -1
- package/agentes/depurador-swl.md +352 -352
- package/agentes/devops-ci-swl.md +400 -400
- package/agentes/disenador-ui-swl.md +569 -569
- package/agentes/documentador-swl.md +345 -345
- package/agentes/frontend-angular-swl.md +621 -621
- package/agentes/frontend-css-swl.md +716 -716
- package/agentes/frontend-react-swl.md +692 -692
- package/agentes/frontend-swl.md +496 -496
- package/agentes/frontend-tailwind-swl.md +826 -826
- package/agentes/gh-fix-ci-swl.md +6 -1
- package/agentes/implementador-swl.md +1 -1
- package/agentes/investigador-swl.md +432 -432
- package/agentes/investigador-ux-swl.md +505 -505
- package/agentes/llm-apps-swl.md +1 -1
- package/agentes/migrador-swl.md +442 -442
- package/agentes/mobile-android-swl.md +511 -511
- package/agentes/mobile-cross-swl.md +541 -541
- package/agentes/mobile-ios-swl.md +502 -502
- package/agentes/mobile-testing-swl.md +302 -302
- package/agentes/nemesis-auditor-swl.md +285 -285
- package/agentes/notificador-swl.md +1 -1
- package/agentes/observabilidad-swl.md +438 -438
- package/agentes/pagos-swl.md +310 -310
- package/agentes/perfilador-usuario-swl.md +321 -321
- package/agentes/planificador-swl.md +399 -399
- package/agentes/producto-prd-swl.md +589 -589
- package/agentes/red-team-swl.md +218 -218
- package/agentes/release-manager-swl.md +590 -590
- package/agentes/rendimiento-swl.md +713 -713
- package/agentes/resolutor-build-swl.md +10 -1
- package/agentes/revisor-angular-swl.md +278 -278
- package/agentes/revisor-codigo-swl.md +1 -1
- package/agentes/revisor-csharp-swl.md +264 -264
- package/agentes/revisor-go-swl.md +259 -259
- package/agentes/revisor-java-swl.md +257 -257
- package/agentes/revisor-kotlin-swl.md +273 -273
- package/agentes/revisor-nextjs-swl.md +281 -281
- package/agentes/revisor-php-swl.md +271 -271
- package/agentes/revisor-react-swl.md +278 -278
- package/agentes/revisor-rust-swl.md +346 -346
- package/agentes/revisor-seguridad-swl.md +399 -399
- package/agentes/revisor-swift-swl.md +268 -268
- package/agentes/revisor-typescript-swl.md +346 -346
- package/agentes/sre-swl.md +1 -1
- package/agentes/tdd-qa-swl.md +393 -393
- package/bin/lib/bot-comandos.js +1 -1
- package/bin/swl-ses.js +6 -0
- package/comandos/swl/adoptar-proyecto.md +14 -2
- package/comandos/swl/configurar-ci.md +8 -1
- package/comandos/swl/deuda-codigo.md +97 -97
- package/comandos/swl/discutir-fase.md +22 -118
- package/comandos/swl/fix.md +118 -0
- package/comandos/swl/nuevo-proyecto.md +54 -3
- package/comandos/swl/predecir.md +32 -2
- package/comandos/swl/seguridad.md +189 -0
- package/comandos/swl/status.md +5 -3
- package/habilidades/aprendizaje-continuo/SKILL.md +3 -1
- package/habilidades/discutir-fase/SKILL.md +84 -81
- package/habilidades/discutir-fase/recursos/plantilla-contexto.md +136 -0
- package/habilidades/doc-sync/SKILL.md +3 -1
- package/habilidades/doubt-driven-review/SKILL.md +15 -1
- package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
- package/habilidades/estructura-proyecto-claude/SKILL.md +11 -2
- package/habilidades/harness-claude-code/SKILL.md +3 -1
- package/habilidades/instalar-sistema/SKILL.md +3 -1
- package/habilidades/meta-reglas-extendido/SKILL.md +92 -0
- package/habilidades/meta-reglas-extendido/recursos/analisis-previo-tareas-grandes.md +186 -0
- package/habilidades/meta-reglas-extendido/recursos/analizar-directorios-antes-de-escribir.md +235 -0
- package/habilidades/meta-reglas-extendido/recursos/api-diseno.md +413 -0
- package/habilidades/meta-reglas-extendido/recursos/arquitectura.md +491 -0
- package/habilidades/meta-reglas-extendido/recursos/arreglar-al-detectar.md +264 -0
- package/habilidades/meta-reglas-extendido/recursos/debatir-antes-de-aceptar.md +152 -0
- package/habilidades/meta-reglas-extendido/recursos/git-workflow.md +259 -0
- package/habilidades/meta-reglas-extendido/recursos/gobernanza.md +291 -0
- package/habilidades/meta-reglas-extendido/recursos/memoria-consolidada.md +263 -0
- package/habilidades/meta-reglas-extendido/recursos/seguridad-agentes.md +443 -0
- package/habilidades/meta-reglas-extendido/recursos/sesiones-paralelas.md +190 -0
- package/habilidades/meta-reglas-extendido/recursos/sin-duplicacion-reglas-globales.md +179 -0
- package/habilidades/meta-reglas-extendido/recursos/skills-estandar.md +394 -0
- package/habilidades/meta-reglas-extendido/recursos/usar-code-review-graph.md +156 -0
- package/habilidades/meta-reglas-extendido/recursos/usar-context7.md +236 -0
- package/habilidades/meta-reglas-extendido/recursos/usar-sistema-swl.md +253 -0
- package/habilidades/meta-reglas-extendido/recursos/verificar-citas-normativas.md +527 -0
- package/habilidades/meta-skills-estandar/SKILL.md +3 -1
- package/habilidades/nuevo-proyecto/SKILL.md +20 -3
- package/habilidades/php-experto/SKILL.md +10 -3
- package/habilidades/{filament-admin/SKILL.md → php-experto/recursos/filament-admin.md} +23 -39
- package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -166
- package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -85
- package/habilidades/proceso-debate-adversarial/recursos/personas.md +5 -4
- package/habilidades/proceso-ingenieria-requerimientos/SKILL.md +147 -0
- package/hooks/check-update.js +19 -10
- package/hooks/contexto-subagente.js +68 -68
- package/hooks/degradacion-instintos.js +1 -1
- package/hooks/extraccion-aprendizajes.js +2 -2
- package/hooks/lib/briefing.js +3 -3
- package/hooks/lib/nudge-tracker.js +1 -1
- package/hooks/lib/otlp-exporter.js +1 -1
- package/hooks/lib/webhook-dedup.js +1 -1
- package/hooks/session-briefing.js +1 -1
- package/llms.txt +6 -6
- package/manifiestos/canonical-hashes.json +989 -0
- package/manifiestos/hooks-config.json +469 -469
- package/manifiestos/invariantes-criticos.json +30 -30
- package/manifiestos/modulos.json +168 -135
- package/manifiestos/perfiles.json +0 -2
- package/manifiestos/skills-lock.json +52 -59
- package/package.json +7 -5
- package/plantillas/github-workflows/README.md +15 -1
- package/plantillas/github-workflows/swl-devsecops.yml +70 -0
- package/plugin.json +5 -5
- package/reglas/analisis-previo-tareas-grandes.md +30 -156
- package/reglas/analizar-directorios-antes-de-escribir.md +30 -211
- package/reglas/api-diseno.md +28 -398
- package/reglas/arquitectura.md +35 -456
- package/reglas/arreglar-al-detectar.md +30 -230
- package/reglas/debatir-antes-de-aceptar.md +30 -143
- package/reglas/docs.md +7 -0
- package/reglas/estilo-codigo.md +9 -0
- package/reglas/fragmentos-compartidos.md +6 -0
- package/reglas/git-workflow.md +44 -240
- package/reglas/gobernanza.md +23 -262
- package/reglas/memoria-consolidada.md +34 -228
- package/reglas/performance.md +8 -0
- package/reglas/pruebas.md +12 -0
- package/reglas/seguridad-agentes.md +37 -418
- package/reglas/seguridad.md +12 -0
- package/reglas/sesiones-paralelas.md +29 -162
- package/reglas/sin-duplicacion-reglas-globales.md +25 -166
- package/reglas/skills-estandar.md +23 -373
- package/reglas/usar-code-review-graph.md +31 -140
- package/reglas/usar-context7.md +30 -208
- package/reglas/usar-sistema-swl.md +47 -242
- package/reglas/verificar-citas-normativas.md +47 -537
- package/scripts/actualizar.js +253 -253
- package/scripts/audit-tools/auditar-relleno-inventario.js +145 -0
- package/scripts/auditar-clases-conocidas.js +106 -0
- package/scripts/bootstrap-instintos.js +2 -2
- package/scripts/canario-hooks.js +166 -0
- package/scripts/cli/configurar-ci.js +2 -1
- package/scripts/evidencia-valor.js +93 -0
- package/scripts/field-report.js +1 -1
- package/scripts/generar-comandos.js +143 -0
- package/scripts/generar-inventario.js +236 -23
- package/scripts/generar-matriz-lenguajes.js +1 -1
- package/scripts/instalador.js +15 -1
- package/scripts/lib/configurar-ci.js +10 -3
- package/scripts/lib/detectar-runtime.js +12 -3
- package/scripts/lib/diary-entry.js +3 -1
- package/scripts/lib/drift-detector.js +1 -1
- package/scripts/lib/evidencia-valor.js +189 -0
- package/scripts/lib/expandir-targets.js +71 -71
- package/scripts/lib/frontmatter-md.js +63 -0
- package/scripts/lib/parsear-opciones.js +2 -0
- package/scripts/lib/prune-componentes.js +180 -0
- package/scripts/lib/reglas-globales-conocidas.json +16 -2
- package/scripts/lib/scoring-instintos.js +2 -2
- package/scripts/lib/toml-merge.js +204 -204
- package/scripts/lib/transformadores/claude.js +1 -1
- package/scripts/lib/transformadores/codex.js +1 -1
- package/scripts/lib/transformadores/copilot.js +1 -1
- package/scripts/lib/transformadores/cursor.js +1 -1
- package/scripts/lib/transformadores/gemini.js +22 -2
- package/scripts/lib/transformadores/opencode.js +1 -1
- package/scripts/mcp-server/auth.js +105 -105
- package/scripts/mcp-server/cache.js +106 -106
- package/scripts/prune.js +102 -0
- package/scripts/publicar.js +18 -2
- package/scripts/tui/pantallas/inspect.js +175 -175
- package/scripts/tui/pantallas/uninstall-wizard.js +210 -210
- package/scripts/tui/pantallas/update-wizard.js +234 -234
- package/scripts/tui/pantallas/welcome.js +189 -189
- package/habilidades/paid-media-tracking/SKILL.md +0 -269
- package/habilidades/paid-media-tracking/recursos/auditoria-tracking.md +0 -220
- package/habilidades/paid-media-tracking/recursos/google-ads-api.md +0 -215
- package/habilidades/tracking-measurement/SKILL.md +0 -239
- package/habilidades/tracking-measurement/recursos/consent-mode.md +0 -231
- package/habilidades/tracking-measurement/recursos/gtm-datalayer.md +0 -216
- package/habilidades/tracking-measurement/recursos/meta-capi.md +0 -262
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Auth opt-in para swl-mcp-server v1.0.0 (ADR-0019 Sub-fase 3).
|
|
5
|
-
*
|
|
6
|
-
* El stub experimental v0.1.x corría sin auth. Cualquier proceso con acceso al
|
|
7
|
-
* stdio podía leer toda la memoria SWL. Esa decisión era aceptable solo en
|
|
8
|
-
* single-user local — no en multi-usuario, no expuesto a red, no compartido.
|
|
9
|
-
*
|
|
10
|
-
* v1.0.0 introduce auth **opt-in**:
|
|
11
|
-
* - Si `SWL_MCP_API_KEY` NO está definida en el entorno del server, el comportamiento
|
|
12
|
-
* es idéntico al stub (sin auth). Compatibilidad total con clientes existentes.
|
|
13
|
-
* - Si `SWL_MCP_API_KEY` ESTÁ definida, cada `tools/call` debe incluir
|
|
14
|
-
* `params._auth = "<api-key>"`. Sin el token o con token incorrecto, se devuelve
|
|
15
|
-
* `-32001 Unauthorized`.
|
|
16
|
-
*
|
|
17
|
-
* Decisión deliberada de NO usar header `Authorization: Bearer ...`:
|
|
18
|
-
* - El protocolo MCP sobre stdio NO transporta headers HTTP.
|
|
19
|
-
* - Inventar un campo en `initialize.capabilities` específico para swl-ses
|
|
20
|
-
* rompería la spec del protocolo.
|
|
21
|
-
* - `_auth` como campo en `params` es el patrón de menor fricción.
|
|
22
|
-
*
|
|
23
|
-
* Limitaciones aceptadas:
|
|
24
|
-
* - Token estático sin rotación automática — el operador rota manualmente
|
|
25
|
-
* cambiando la env var del proceso.
|
|
26
|
-
* - Sin scopes (read/write) porque v1 sigue siendo read-only.
|
|
27
|
-
*
|
|
28
|
-
* @module scripts/mcp-server/auth
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
const ENV_VAR_NAME = 'SWL_MCP_API_KEY';
|
|
32
|
-
const ERROR_CODE_UNAUTHORIZED = -32001;
|
|
33
|
-
const ERROR_CODE_FORBIDDEN = -32002;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Construye un validador de auth desde el entorno actual.
|
|
37
|
-
*
|
|
38
|
-
* Pattern de "construct once, use many": leer la env var una sola vez al arranque
|
|
39
|
-
* y devolver una función pura que valida cada request. Evita race conditions con
|
|
40
|
-
* tests que mutan process.env.
|
|
41
|
-
*
|
|
42
|
-
* @param {object} [opciones] - { env: NodeJS.ProcessEnv } sustituible para tests.
|
|
43
|
-
* @returns {{ requerida: boolean, validar: (request: object) => { ok: boolean, code?: number, message?: string } }}
|
|
44
|
-
*/
|
|
45
|
-
function construirValidador(opciones = {}) {
|
|
46
|
-
const env = opciones.env || process.env;
|
|
47
|
-
const apiKey = env[ENV_VAR_NAME];
|
|
48
|
-
const requerida = typeof apiKey === 'string' && apiKey.length > 0;
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
requerida,
|
|
52
|
-
validar: (request) => {
|
|
53
|
-
if (!requerida) return { ok: true };
|
|
54
|
-
// Solo validar tools/call — initialize, ping, tools/list son metadata pública.
|
|
55
|
-
const metodo = request && request.method;
|
|
56
|
-
if (metodo !== 'tools/call') return { ok: true };
|
|
57
|
-
|
|
58
|
-
const params = request.params || {};
|
|
59
|
-
const tokenCliente = params._auth;
|
|
60
|
-
|
|
61
|
-
if (typeof tokenCliente !== 'string' || tokenCliente.length === 0) {
|
|
62
|
-
return {
|
|
63
|
-
ok: false,
|
|
64
|
-
code: ERROR_CODE_UNAUTHORIZED,
|
|
65
|
-
message: 'swl-mcp-server requiere autenticación: SWL_MCP_API_KEY está configurada en el server. El cliente debe enviar params._auth con el API key.',
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
if (!comparacionConstante(tokenCliente, apiKey)) {
|
|
69
|
-
return {
|
|
70
|
-
ok: false,
|
|
71
|
-
code: ERROR_CODE_FORBIDDEN,
|
|
72
|
-
message: 'Token inválido.',
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
return { ok: true };
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Comparación de strings en tiempo constante para evitar timing attacks
|
|
82
|
-
* sobre el API key. Implementación zero-deps simple.
|
|
83
|
-
*
|
|
84
|
-
* Si las longitudes difieren, igual se itera la longitud máxima para no filtrar
|
|
85
|
-
* información sobre el largo del secreto vía duración de la comparación.
|
|
86
|
-
*/
|
|
87
|
-
function comparacionConstante(a, b) {
|
|
88
|
-
if (typeof a !== 'string' || typeof b !== 'string') return false;
|
|
89
|
-
const len = Math.max(a.length, b.length);
|
|
90
|
-
let diff = a.length ^ b.length;
|
|
91
|
-
for (let i = 0; i < len; i++) {
|
|
92
|
-
const ca = i < a.length ? a.charCodeAt(i) : 0;
|
|
93
|
-
const cb = i < b.length ? b.charCodeAt(i) : 0;
|
|
94
|
-
diff |= ca ^ cb;
|
|
95
|
-
}
|
|
96
|
-
return diff === 0;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
module.exports = {
|
|
100
|
-
construirValidador,
|
|
101
|
-
comparacionConstante,
|
|
102
|
-
ENV_VAR_NAME,
|
|
103
|
-
ERROR_CODE_UNAUTHORIZED,
|
|
104
|
-
ERROR_CODE_FORBIDDEN,
|
|
105
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Auth opt-in para swl-mcp-server v1.0.0 (ADR-0019 Sub-fase 3).
|
|
5
|
+
*
|
|
6
|
+
* El stub experimental v0.1.x corría sin auth. Cualquier proceso con acceso al
|
|
7
|
+
* stdio podía leer toda la memoria SWL. Esa decisión era aceptable solo en
|
|
8
|
+
* single-user local — no en multi-usuario, no expuesto a red, no compartido.
|
|
9
|
+
*
|
|
10
|
+
* v1.0.0 introduce auth **opt-in**:
|
|
11
|
+
* - Si `SWL_MCP_API_KEY` NO está definida en el entorno del server, el comportamiento
|
|
12
|
+
* es idéntico al stub (sin auth). Compatibilidad total con clientes existentes.
|
|
13
|
+
* - Si `SWL_MCP_API_KEY` ESTÁ definida, cada `tools/call` debe incluir
|
|
14
|
+
* `params._auth = "<api-key>"`. Sin el token o con token incorrecto, se devuelve
|
|
15
|
+
* `-32001 Unauthorized`.
|
|
16
|
+
*
|
|
17
|
+
* Decisión deliberada de NO usar header `Authorization: Bearer ...`:
|
|
18
|
+
* - El protocolo MCP sobre stdio NO transporta headers HTTP.
|
|
19
|
+
* - Inventar un campo en `initialize.capabilities` específico para swl-ses
|
|
20
|
+
* rompería la spec del protocolo.
|
|
21
|
+
* - `_auth` como campo en `params` es el patrón de menor fricción.
|
|
22
|
+
*
|
|
23
|
+
* Limitaciones aceptadas:
|
|
24
|
+
* - Token estático sin rotación automática — el operador rota manualmente
|
|
25
|
+
* cambiando la env var del proceso.
|
|
26
|
+
* - Sin scopes (read/write) porque v1 sigue siendo read-only.
|
|
27
|
+
*
|
|
28
|
+
* @module scripts/mcp-server/auth
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
const ENV_VAR_NAME = 'SWL_MCP_API_KEY';
|
|
32
|
+
const ERROR_CODE_UNAUTHORIZED = -32001;
|
|
33
|
+
const ERROR_CODE_FORBIDDEN = -32002;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Construye un validador de auth desde el entorno actual.
|
|
37
|
+
*
|
|
38
|
+
* Pattern de "construct once, use many": leer la env var una sola vez al arranque
|
|
39
|
+
* y devolver una función pura que valida cada request. Evita race conditions con
|
|
40
|
+
* tests que mutan process.env.
|
|
41
|
+
*
|
|
42
|
+
* @param {object} [opciones] - { env: NodeJS.ProcessEnv } sustituible para tests.
|
|
43
|
+
* @returns {{ requerida: boolean, validar: (request: object) => { ok: boolean, code?: number, message?: string } }}
|
|
44
|
+
*/
|
|
45
|
+
function construirValidador(opciones = {}) {
|
|
46
|
+
const env = opciones.env || process.env;
|
|
47
|
+
const apiKey = env[ENV_VAR_NAME];
|
|
48
|
+
const requerida = typeof apiKey === 'string' && apiKey.length > 0;
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
requerida,
|
|
52
|
+
validar: (request) => {
|
|
53
|
+
if (!requerida) return { ok: true };
|
|
54
|
+
// Solo validar tools/call — initialize, ping, tools/list son metadata pública.
|
|
55
|
+
const metodo = request && request.method;
|
|
56
|
+
if (metodo !== 'tools/call') return { ok: true };
|
|
57
|
+
|
|
58
|
+
const params = request.params || {};
|
|
59
|
+
const tokenCliente = params._auth;
|
|
60
|
+
|
|
61
|
+
if (typeof tokenCliente !== 'string' || tokenCliente.length === 0) {
|
|
62
|
+
return {
|
|
63
|
+
ok: false,
|
|
64
|
+
code: ERROR_CODE_UNAUTHORIZED,
|
|
65
|
+
message: 'swl-mcp-server requiere autenticación: SWL_MCP_API_KEY está configurada en el server. El cliente debe enviar params._auth con el API key.',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (!comparacionConstante(tokenCliente, apiKey)) {
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
code: ERROR_CODE_FORBIDDEN,
|
|
72
|
+
message: 'Token inválido.',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return { ok: true };
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Comparación de strings en tiempo constante para evitar timing attacks
|
|
82
|
+
* sobre el API key. Implementación zero-deps simple.
|
|
83
|
+
*
|
|
84
|
+
* Si las longitudes difieren, igual se itera la longitud máxima para no filtrar
|
|
85
|
+
* información sobre el largo del secreto vía duración de la comparación.
|
|
86
|
+
*/
|
|
87
|
+
function comparacionConstante(a, b) {
|
|
88
|
+
if (typeof a !== 'string' || typeof b !== 'string') return false;
|
|
89
|
+
const len = Math.max(a.length, b.length);
|
|
90
|
+
let diff = a.length ^ b.length;
|
|
91
|
+
for (let i = 0; i < len; i++) {
|
|
92
|
+
const ca = i < a.length ? a.charCodeAt(i) : 0;
|
|
93
|
+
const cb = i < b.length ? b.charCodeAt(i) : 0;
|
|
94
|
+
diff |= ca ^ cb;
|
|
95
|
+
}
|
|
96
|
+
return diff === 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = {
|
|
100
|
+
construirValidador,
|
|
101
|
+
comparacionConstante,
|
|
102
|
+
ENV_VAR_NAME,
|
|
103
|
+
ERROR_CODE_UNAUTHORIZED,
|
|
104
|
+
ERROR_CODE_FORBIDDEN,
|
|
105
|
+
};
|
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Cache mtime-based para handlers del swl-mcp-server v1.0.0 (ADR-0019 Sub-fase 3).
|
|
5
|
-
*
|
|
6
|
-
* El stub experimental leía disco en cada call (latencia medida ~10ms con
|
|
7
|
-
* APRENDIZAJES.md de 1172 líneas). Para datasets más grandes (>10k líneas) o
|
|
8
|
-
* múltiples clientes concurrentes, el caching reduce sustancialmente la carga.
|
|
9
|
-
*
|
|
10
|
-
* Patrón:
|
|
11
|
-
* - Key = path absoluto del archivo.
|
|
12
|
-
* - Value = { mtime, contenido, parsed }.
|
|
13
|
-
* - Invalidación: si fs.stat().mtime > value.mtime → recargar y reparsear.
|
|
14
|
-
* - TTL opcional vía `SWL_MCP_CACHE_TTL_MS` (default 60000 ms): aunque mtime no
|
|
15
|
-
* haya cambiado, recargar tras TTL para defensa contra clock skew o ediciones
|
|
16
|
-
* que no actualizan mtime (raras pero posibles en filesystems exóticos).
|
|
17
|
-
*
|
|
18
|
-
* Zero-deps. NO usar para escritura — sigue read-only.
|
|
19
|
-
*
|
|
20
|
-
* @module scripts/mcp-server/cache
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
const fs = require('fs');
|
|
24
|
-
|
|
25
|
-
const DEFAULT_TTL_MS = 60 * 1000;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Crea una instancia de cache mtime-based.
|
|
29
|
-
*
|
|
30
|
-
* @param {object} [opciones]
|
|
31
|
-
* @param {number} [opciones.ttlMs] - TTL en milisegundos. Default 60000.
|
|
32
|
-
* @param {object} [opciones.env] - Para leer SWL_MCP_CACHE_TTL_MS en tests.
|
|
33
|
-
* @returns {{ get: Function, invalidate: Function, stats: Function, _store: Map }}
|
|
34
|
-
*/
|
|
35
|
-
function crearCache(opciones = {}) {
|
|
36
|
-
const env = opciones.env || process.env;
|
|
37
|
-
const ttlMs = opciones.ttlMs !== undefined
|
|
38
|
-
? opciones.ttlMs
|
|
39
|
-
: (parseInt(env.SWL_MCP_CACHE_TTL_MS, 10) || DEFAULT_TTL_MS);
|
|
40
|
-
|
|
41
|
-
const store = new Map();
|
|
42
|
-
const stats = { hits: 0, misses: 0, invalidations: 0 };
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Obtiene el valor cacheado para `path`. Si el archivo cambió (mtime) o el TTL
|
|
46
|
-
* expiró, vuelve a leer y parsear con `parser(contenido, path)`.
|
|
47
|
-
*
|
|
48
|
-
* @param {string} ruta - Path absoluto del archivo a cachear.
|
|
49
|
-
* @param {(contenido: string, ruta: string) => any} parser - Función que transforma
|
|
50
|
-
* el contenido del archivo en el objeto a cachear. DEBE ser pura.
|
|
51
|
-
* @returns {{ data: any, hit: boolean } | null} null si el archivo no existe.
|
|
52
|
-
*/
|
|
53
|
-
function get(ruta, parser) {
|
|
54
|
-
let stat;
|
|
55
|
-
try {
|
|
56
|
-
stat = fs.statSync(ruta);
|
|
57
|
-
} catch (err) {
|
|
58
|
-
if (err.code === 'ENOENT') return null;
|
|
59
|
-
throw err;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const mtimeMs = stat.mtimeMs;
|
|
63
|
-
const ahora = Date.now();
|
|
64
|
-
const cached = store.get(ruta);
|
|
65
|
-
|
|
66
|
-
if (cached && cached.mtimeMs === mtimeMs && (ahora - cached.cargadoEn) < ttlMs) {
|
|
67
|
-
stats.hits++;
|
|
68
|
-
return { data: cached.data, hit: true };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (cached) stats.invalidations++;
|
|
72
|
-
else stats.misses++;
|
|
73
|
-
|
|
74
|
-
const contenido = fs.readFileSync(ruta, 'utf-8');
|
|
75
|
-
const data = parser(contenido, ruta);
|
|
76
|
-
store.set(ruta, { mtimeMs, cargadoEn: ahora, data });
|
|
77
|
-
return { data, hit: false };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Invalida una entrada o todo el cache.
|
|
82
|
-
* @param {string} [ruta] - Si se omite, vacía todo.
|
|
83
|
-
*/
|
|
84
|
-
function invalidate(ruta) {
|
|
85
|
-
if (ruta === undefined) {
|
|
86
|
-
const n = store.size;
|
|
87
|
-
store.clear();
|
|
88
|
-
stats.invalidations += n;
|
|
89
|
-
return n;
|
|
90
|
-
}
|
|
91
|
-
if (store.delete(ruta)) stats.invalidations++;
|
|
92
|
-
return 0;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
get,
|
|
97
|
-
invalidate,
|
|
98
|
-
stats: () => ({ ...stats, size: store.size, ttlMs }),
|
|
99
|
-
_store: store, // exposed para tests
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
module.exports = {
|
|
104
|
-
crearCache,
|
|
105
|
-
DEFAULT_TTL_MS,
|
|
106
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cache mtime-based para handlers del swl-mcp-server v1.0.0 (ADR-0019 Sub-fase 3).
|
|
5
|
+
*
|
|
6
|
+
* El stub experimental leía disco en cada call (latencia medida ~10ms con
|
|
7
|
+
* APRENDIZAJES.md de 1172 líneas). Para datasets más grandes (>10k líneas) o
|
|
8
|
+
* múltiples clientes concurrentes, el caching reduce sustancialmente la carga.
|
|
9
|
+
*
|
|
10
|
+
* Patrón:
|
|
11
|
+
* - Key = path absoluto del archivo.
|
|
12
|
+
* - Value = { mtime, contenido, parsed }.
|
|
13
|
+
* - Invalidación: si fs.stat().mtime > value.mtime → recargar y reparsear.
|
|
14
|
+
* - TTL opcional vía `SWL_MCP_CACHE_TTL_MS` (default 60000 ms): aunque mtime no
|
|
15
|
+
* haya cambiado, recargar tras TTL para defensa contra clock skew o ediciones
|
|
16
|
+
* que no actualizan mtime (raras pero posibles en filesystems exóticos).
|
|
17
|
+
*
|
|
18
|
+
* Zero-deps. NO usar para escritura — sigue read-only.
|
|
19
|
+
*
|
|
20
|
+
* @module scripts/mcp-server/cache
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
|
|
25
|
+
const DEFAULT_TTL_MS = 60 * 1000;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Crea una instancia de cache mtime-based.
|
|
29
|
+
*
|
|
30
|
+
* @param {object} [opciones]
|
|
31
|
+
* @param {number} [opciones.ttlMs] - TTL en milisegundos. Default 60000.
|
|
32
|
+
* @param {object} [opciones.env] - Para leer SWL_MCP_CACHE_TTL_MS en tests.
|
|
33
|
+
* @returns {{ get: Function, invalidate: Function, stats: Function, _store: Map }}
|
|
34
|
+
*/
|
|
35
|
+
function crearCache(opciones = {}) {
|
|
36
|
+
const env = opciones.env || process.env;
|
|
37
|
+
const ttlMs = opciones.ttlMs !== undefined
|
|
38
|
+
? opciones.ttlMs
|
|
39
|
+
: (parseInt(env.SWL_MCP_CACHE_TTL_MS, 10) || DEFAULT_TTL_MS);
|
|
40
|
+
|
|
41
|
+
const store = new Map();
|
|
42
|
+
const stats = { hits: 0, misses: 0, invalidations: 0 };
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Obtiene el valor cacheado para `path`. Si el archivo cambió (mtime) o el TTL
|
|
46
|
+
* expiró, vuelve a leer y parsear con `parser(contenido, path)`.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} ruta - Path absoluto del archivo a cachear.
|
|
49
|
+
* @param {(contenido: string, ruta: string) => any} parser - Función que transforma
|
|
50
|
+
* el contenido del archivo en el objeto a cachear. DEBE ser pura.
|
|
51
|
+
* @returns {{ data: any, hit: boolean } | null} null si el archivo no existe.
|
|
52
|
+
*/
|
|
53
|
+
function get(ruta, parser) {
|
|
54
|
+
let stat;
|
|
55
|
+
try {
|
|
56
|
+
stat = fs.statSync(ruta);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
if (err.code === 'ENOENT') return null;
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const mtimeMs = stat.mtimeMs;
|
|
63
|
+
const ahora = Date.now();
|
|
64
|
+
const cached = store.get(ruta);
|
|
65
|
+
|
|
66
|
+
if (cached && cached.mtimeMs === mtimeMs && (ahora - cached.cargadoEn) < ttlMs) {
|
|
67
|
+
stats.hits++;
|
|
68
|
+
return { data: cached.data, hit: true };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (cached) stats.invalidations++;
|
|
72
|
+
else stats.misses++;
|
|
73
|
+
|
|
74
|
+
const contenido = fs.readFileSync(ruta, 'utf-8');
|
|
75
|
+
const data = parser(contenido, ruta);
|
|
76
|
+
store.set(ruta, { mtimeMs, cargadoEn: ahora, data });
|
|
77
|
+
return { data, hit: false };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Invalida una entrada o todo el cache.
|
|
82
|
+
* @param {string} [ruta] - Si se omite, vacía todo.
|
|
83
|
+
*/
|
|
84
|
+
function invalidate(ruta) {
|
|
85
|
+
if (ruta === undefined) {
|
|
86
|
+
const n = store.size;
|
|
87
|
+
store.clear();
|
|
88
|
+
stats.invalidations += n;
|
|
89
|
+
return n;
|
|
90
|
+
}
|
|
91
|
+
if (store.delete(ruta)) stats.invalidations++;
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
get,
|
|
97
|
+
invalidate,
|
|
98
|
+
stats: () => ({ ...stats, size: store.size, ttlMs }),
|
|
99
|
+
_store: store, // exposed para tests
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = {
|
|
104
|
+
crearCache,
|
|
105
|
+
DEFAULT_TTL_MS,
|
|
106
|
+
};
|
package/scripts/prune.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* prune.js — CLI `swl-ses prune`: purga componentes retirados de una
|
|
6
|
+
* instalación SWL existente.
|
|
7
|
+
*
|
|
8
|
+
* Cierra DT-INSTALADOR-PRUNE. Sin flags es DRY-RUN (solo reporta el plan);
|
|
9
|
+
* `--confirmar` ejecuta el borrado. Nunca toca contenido evolucionado por el
|
|
10
|
+
* usuario ni archivos sin hash canónico conocido — esos se listan como
|
|
11
|
+
* "preservados" (detalle en scripts/lib/prune-componentes.js).
|
|
12
|
+
*
|
|
13
|
+
* Uso:
|
|
14
|
+
* swl-ses prune # dry-run del scope local (o global con --global)
|
|
15
|
+
* swl-ses prune --confirmar # ejecuta el borrado del plan
|
|
16
|
+
* swl-ses prune --global # opera sobre la instalación global del runtime
|
|
17
|
+
* swl-ses prune --json # salida como JSON (para tooling)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const { planPrune, ejecutarPrune } = require('./lib/prune-componentes.js');
|
|
22
|
+
|
|
23
|
+
const RAIZ_PAQUETE = path.resolve(__dirname, '..');
|
|
24
|
+
|
|
25
|
+
function resolverRutaBase(opciones) {
|
|
26
|
+
if (opciones._rutaBaseOverride) return opciones._rutaBaseOverride; // tests
|
|
27
|
+
try {
|
|
28
|
+
const { obtenerRuntime, scopesReales } = require('./lib/detectar-runtime.js');
|
|
29
|
+
const runtime = obtenerRuntime(opciones.target || 'claude');
|
|
30
|
+
const scopes = scopesReales(runtime); // [{dir, esGlobal}] — regla scopesReales (CLAUDE.md)
|
|
31
|
+
const scope = opciones.global
|
|
32
|
+
? scopes.find(s => s.esGlobal)
|
|
33
|
+
: (scopes.find(s => !s.esGlobal) || scopes[0]);
|
|
34
|
+
return scope.dir;
|
|
35
|
+
} catch {
|
|
36
|
+
// Fallback: convención Claude Code
|
|
37
|
+
return opciones.global
|
|
38
|
+
? path.join(require('os').homedir(), '.claude')
|
|
39
|
+
: path.join(process.cwd(), '.claude');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function prune(opciones = {}) {
|
|
44
|
+
const rutaBase = resolverRutaBase(opciones);
|
|
45
|
+
const plan = planPrune({ rutaBase, raizPaquete: opciones._raizPaqueteOverride || RAIZ_PAQUETE });
|
|
46
|
+
|
|
47
|
+
if (plan.error) {
|
|
48
|
+
console.error(plan.error);
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
return plan;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const resumen = {
|
|
54
|
+
rutaBase,
|
|
55
|
+
vigentes: plan.vigentes,
|
|
56
|
+
eliminables: plan.eliminables,
|
|
57
|
+
preservados: plan.preservados,
|
|
58
|
+
yaAusentes: plan.yaAusentes,
|
|
59
|
+
modo: opciones.confirmar ? 'ejecutado' : 'dry-run',
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
if (opciones.confirmar) {
|
|
63
|
+
const resultado = ejecutarPrune(plan, { rutaBase });
|
|
64
|
+
resumen.eliminados = resultado.eliminados;
|
|
65
|
+
resumen.erroresBorrado = resultado.erroresBorrado;
|
|
66
|
+
resumen.entradasLimpiadas = resultado.entradasLimpiadas;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (opciones.json) {
|
|
70
|
+
console.log(JSON.stringify(resumen, null, 2));
|
|
71
|
+
return resumen;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(`Prune SWL — ${rutaBase} [${resumen.modo}]`);
|
|
75
|
+
console.log(` Componentes vigentes: ${plan.vigentes}`);
|
|
76
|
+
console.log(` Retirados eliminables (fábrica sin tocar): ${plan.eliminables.length}`);
|
|
77
|
+
for (const e of plan.eliminables) console.log(` - ${e.origen}`);
|
|
78
|
+
console.log(` Preservados (evolución del usuario o sin hash — revisar manual): ${plan.preservados.length}`);
|
|
79
|
+
for (const p of plan.preservados) console.log(` - ${p.origen} [${p.razon}]`);
|
|
80
|
+
if (plan.yaAusentes.length > 0) {
|
|
81
|
+
console.log(` Entradas del estado ya ausentes en disco: ${plan.yaAusentes.length}`);
|
|
82
|
+
}
|
|
83
|
+
if (opciones.confirmar) {
|
|
84
|
+
console.log(` Eliminados: ${resumen.eliminados.length} | Entradas de estado limpiadas: ${resumen.entradasLimpiadas}`);
|
|
85
|
+
for (const err of resumen.erroresBorrado || []) console.log(` ERROR ${err.destino}: ${err.error}`);
|
|
86
|
+
} else if (plan.eliminables.length > 0) {
|
|
87
|
+
console.log('');
|
|
88
|
+
console.log('Dry-run: nada se borró. Ejecuta con --confirmar para aplicar el plan.');
|
|
89
|
+
} else {
|
|
90
|
+
console.log('');
|
|
91
|
+
console.log('Nada que podar.');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return resumen;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = prune;
|
|
98
|
+
|
|
99
|
+
if (require.main === module) {
|
|
100
|
+
const { parsearOpciones } = require('./lib/parsear-opciones.js');
|
|
101
|
+
prune(parsearOpciones(process.argv.slice(2)));
|
|
102
|
+
}
|
package/scripts/publicar.js
CHANGED
|
@@ -469,8 +469,24 @@ function main() {
|
|
|
469
469
|
imprimirEncabezado(pkg.version, opts.dryRun);
|
|
470
470
|
avisarSiHayCambiosSinCommit(opts.dryRun);
|
|
471
471
|
|
|
472
|
-
const npmjsOk
|
|
473
|
-
|
|
472
|
+
const npmjsOk = opts.soloGithub ? true : publicarNpmjs(pkg, opts.dryRun);
|
|
473
|
+
|
|
474
|
+
// En dual, GitHub SOLO publica si npmjs pasó: el paso GitHub publica desde
|
|
475
|
+
// un directorio temporal preparado donde los gates (prepublishOnly/
|
|
476
|
+
// test:release) NUNCA corren — publicar tras un fallo de npmjs sube un
|
|
477
|
+
// tarball sin validar y deja los registries desalineados. Casos reales:
|
|
478
|
+
// 2.5.0 y 2.5.1 quedaron huérfanos en el mirror por esto.
|
|
479
|
+
let githubOk;
|
|
480
|
+
if (opts.soloNpmjs) {
|
|
481
|
+
githubOk = true;
|
|
482
|
+
} else if (!opts.soloGithub && !npmjsOk) {
|
|
483
|
+
console.error('\n=== 2/2 — GitHub Packages (mirror) ===');
|
|
484
|
+
console.error('OMITIDO: npmjs (canónico) falló y el paso GitHub no corre gates.');
|
|
485
|
+
console.error('Corrige la causa del fallo y reintenta `npm run publish:all`.');
|
|
486
|
+
githubOk = false;
|
|
487
|
+
} else {
|
|
488
|
+
githubOk = publicarGitHub(pkg, opts.dryRun);
|
|
489
|
+
}
|
|
474
490
|
|
|
475
491
|
reportarResultado(pkg, opts, npmjsOk, githubOk);
|
|
476
492
|
if (LOG_PATH) process.stdout.write(`\nLog de la corrida: ${path.relative(ROOT, LOG_PATH).replace(/\\/g, '/')}\n`);
|