@saulwade/swl-ses 2.6.1 → 2.8.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.
Files changed (268) hide show
  1. package/CLAUDE.md +14 -2
  2. package/README.md +65 -18
  3. package/agentes/_intent-spec.md +73 -73
  4. package/agentes/_propose-step.md +90 -90
  5. package/bin/swl-ses.js +10 -0
  6. package/comandos/swl/brainstorm.md +1 -0
  7. package/comandos/swl/briefing.md +119 -119
  8. package/comandos/swl/contribuir.md +233 -233
  9. package/comandos/swl/deuda-codigo.md +97 -97
  10. package/comandos/swl/mcp-status.md +1 -0
  11. package/gateway/lib/event-channel.js +191 -191
  12. package/habilidades/agent-deep-links/SKILL.md +148 -148
  13. package/habilidades/backend-async-postgres-testing/SKILL.md +216 -216
  14. package/habilidades/backend-error-design/SKILL.md +221 -221
  15. package/habilidades/backend-production-resilience/SKILL.md +288 -288
  16. package/habilidades/calidad-anti-patrones-universales/SKILL.md +105 -1
  17. package/habilidades/calidad-contract-testing/SKILL.md +165 -165
  18. package/habilidades/calidad-mutation-testing/SKILL.md +25 -1
  19. package/habilidades/checklist-seguridad/recursos/stride-cobertura.md +60 -60
  20. package/habilidades/ci-cd-pipelines/SKILL.md +5 -1
  21. package/habilidades/css-moderno/SKILL.md +7 -1
  22. package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
  23. package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
  24. package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +57 -57
  25. package/habilidades/extractor-de-aprendizajes/SKILL.md +5 -1
  26. package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
  27. package/habilidades/harness-claude-code/SKILL.md +3 -2
  28. package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
  29. package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
  30. package/habilidades/perfil-usuario/SKILL.md +200 -200
  31. package/habilidades/prevencion-sobreingenieria/recursos/EXAMPLES.md +580 -580
  32. package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -166
  33. package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -85
  34. package/habilidades/proceso-ddia-streaming/SKILL.md +231 -231
  35. package/habilidades/proceso-discovery-machote/SKILL.md +157 -157
  36. package/habilidades/proceso-dynamic-workflows/SKILL.md +60 -0
  37. package/habilidades/proceso-dynamic-workflows/recursos/template-adversarial-verify.js +65 -65
  38. package/habilidades/proceso-dynamic-workflows/recursos/template-triage.js +65 -65
  39. package/habilidades/proceso-intent-engineering/SKILL.md +269 -269
  40. package/habilidades/proceso-modular-split/SKILL.md +256 -256
  41. package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
  42. package/habilidades/swl-claudemd/recursos/contrato-aprender.md +83 -83
  43. package/habilidades/swl-claudemd/recursos/duplicacion-reglas-globales.md +85 -85
  44. package/habilidades/swl-claudemd/recursos/plantillas-init.md +94 -94
  45. package/habilidades/tdd-workflow/recursos/gherkin-bdd.md +111 -111
  46. package/hooks/calidad-pre-commit.js +159 -10
  47. package/hooks/ciclo-evolucion-subagente.js +26 -26
  48. package/hooks/ciclo-evolucion.js +26 -26
  49. package/hooks/contexto-subagente.js +68 -68
  50. package/hooks/lib/auto-consolidator.js +335 -335
  51. package/hooks/lib/ciclo-evolucion.js +47 -47
  52. package/hooks/lib/deep-links.js +185 -185
  53. package/hooks/lib/error-classifier.js +308 -308
  54. package/hooks/lib/notificacion-formato.js +45 -11
  55. package/hooks/lib/provenance-tracker.js +191 -191
  56. package/hooks/lib/raiz-proyecto.js +35 -4
  57. package/hooks/lib/resource-quota.js +122 -122
  58. package/hooks/lib/retry-jitter.js +165 -165
  59. package/hooks/lib/security-net.js +201 -201
  60. package/hooks/lib/skill-auditor.js +588 -588
  61. package/hooks/lib/sync-status.js +228 -228
  62. package/hooks/lib/taint-tracker.js +107 -107
  63. package/hooks/lib/text-similarity.js +241 -241
  64. package/hooks/lib/toon-compressor.js +245 -245
  65. package/hooks/notificacion-telegram.js +5 -11
  66. package/hooks/session-briefing.js +12 -4
  67. package/instintos/autonomia.yaml +27 -27
  68. package/instintos/prompt-appendices.yaml +57 -57
  69. package/llms.txt +1 -1
  70. package/manifiestos/agent-output-schemas.json +57 -57
  71. package/manifiestos/canonical-hashes.json +662 -0
  72. package/manifiestos/harness-ir.json +47536 -0
  73. package/manifiestos/hooks-config.json +469 -469
  74. package/manifiestos/invariantes-criticos.json +30 -30
  75. package/manifiestos/policy-bundle.json +2065 -0
  76. package/manifiestos/policy-corpus-w2.json +3926 -0
  77. package/manifiestos/runtime-adapters-core3.json +208 -0
  78. package/manifiestos/runtime-conformance.json +139 -0
  79. package/manifiestos/skills-lock.json +43 -43
  80. package/package.json +2 -2
  81. package/plantillas/auditor-veto-template.md +105 -105
  82. package/plantillas/github-workflows/release-please.yml +44 -44
  83. package/plantillas/github-workflows/swl-ci.yml +107 -107
  84. package/plantillas/github-workflows/swl-security.yml +51 -51
  85. package/plugin.json +2 -2
  86. package/reglas/accesibilidad.md +10 -10
  87. package/reglas/auditorias-documentales-estructurales.md +7 -7
  88. package/reglas/cloud-infra.md +8 -8
  89. package/reglas/consultar-vault-primero.md +195 -195
  90. package/reglas/git-workflow.md +1 -0
  91. package/reglas/hooks.md +6 -6
  92. package/reglas/intent-engineering.md +218 -218
  93. package/reglas/markitdown.md +8 -8
  94. package/reglas/monitor-ci.md +12 -0
  95. package/reglas/patrones.md +6 -6
  96. package/reglas/testing.md +7 -7
  97. package/reglas/tests-cleanup.md +224 -224
  98. package/schemas/agent-message.schema.json +73 -73
  99. package/schemas/agent-output-implementacion.schema.json +114 -114
  100. package/schemas/agent-output-planificacion.schema.json +150 -150
  101. package/schemas/agent-output-review.schema.json +98 -98
  102. package/schemas/diary-entry.schema.json +112 -112
  103. package/schemas/gate-state.schema.json +76 -0
  104. package/schemas/harness-ir.schema.json +369 -0
  105. package/schemas/hook-profiles.schema.json +54 -54
  106. package/schemas/hooks-config.schema.json +89 -89
  107. package/schemas/legacy-gates.schema.json +45 -0
  108. package/schemas/modulos.schema.json +38 -38
  109. package/schemas/perfiles.schema.json +36 -36
  110. package/schemas/plugin.schema.json +77 -77
  111. package/schemas/policy-bundle.schema.json +140 -0
  112. package/schemas/policy-enforcement.schema.json +117 -0
  113. package/schemas/policy-operation.schema.json +261 -0
  114. package/schemas/runtime-adapter.schema.json +176 -0
  115. package/schemas/runtime-build-attestation.schema.json +100 -0
  116. package/schemas/runtime-conformance.schema.json +239 -0
  117. package/schemas/runtime-diagnostic.schema.json +395 -0
  118. package/schemas/skill-evals.schema.json +119 -119
  119. package/schemas/skill-frontmatter.schema.json +245 -245
  120. package/schemas/w4-certification-request.schema.json +72 -0
  121. package/schemas/w4-certification-verdict.schema.json +224 -0
  122. package/schemas/w4-corpus.schema.json +172 -0
  123. package/schemas/w4-mutation-report.schema.json +116 -0
  124. package/schemas/w4-replay-result.schema.json +164 -0
  125. package/schemas/w4-scoring-report.schema.json +89 -0
  126. package/scripts/audit-tools/audit-history.js +330 -330
  127. package/scripts/audit-tools/bundle-tracker.js +290 -290
  128. package/scripts/audit-tools/canary-monitor.js +352 -352
  129. package/scripts/audit-tools/code-profiler.js +605 -605
  130. package/scripts/audit-tools/dep-doctor.js +320 -320
  131. package/scripts/audit-tools/env-validator.js +206 -206
  132. package/scripts/audit-tools/lib/fs-walk.js +48 -48
  133. package/scripts/audit-tools/lib/output.js +23 -23
  134. package/scripts/audit-tools/migration-checker.js +392 -392
  135. package/scripts/audit-tools/pentest-scanner.js +1436 -1436
  136. package/scripts/bootstrap-instintos.js +3 -0
  137. package/scripts/cli/aprobar-plan.js +73 -73
  138. package/scripts/cli/briefing.js +23 -23
  139. package/scripts/cli/ciclo-evolucion.js +26 -26
  140. package/scripts/cli/derivar-feature-list.js +25 -25
  141. package/scripts/cli/detectar-host.js +27 -27
  142. package/scripts/cli/diary-entry.js +69 -69
  143. package/scripts/cli/execution-state.js +18 -18
  144. package/scripts/cli/gateway-notify.js +41 -41
  145. package/scripts/cli/liberar-fase.js +42 -42
  146. package/scripts/cli/mark-evolved.js +56 -56
  147. package/scripts/cli/metricas-dora.js +26 -26
  148. package/scripts/cli/near-duplicate.js +55 -55
  149. package/scripts/cli/notificaciones.js +123 -123
  150. package/scripts/cli/propose-step.js +29 -29
  151. package/scripts/cli/schedule-parse.js +19 -19
  152. package/scripts/cli/sugerir-modelo.js +20 -20
  153. package/scripts/cli/verificar-plan.js +36 -36
  154. package/scripts/cli/verificar-trazabilidad.js +35 -35
  155. package/scripts/comandos/install-asistido.js +8 -7
  156. package/scripts/configurar-branch-protection.js +418 -418
  157. package/scripts/detectar-aprendizajes-duplicados.js +151 -151
  158. package/scripts/doctor.js +61 -36
  159. package/scripts/generar-checklists-consolidados.js +273 -273
  160. package/scripts/generar-claims-runtime.js +1342 -0
  161. package/scripts/generar-harness-ir.js +257 -0
  162. package/scripts/generar-inventario.js +52 -54
  163. package/scripts/generar-policy-bundle.js +202 -0
  164. package/scripts/instalador.js +26 -7
  165. package/scripts/lib/approval-receipts.js +190 -0
  166. package/scripts/lib/artefactos-python.js +43 -43
  167. package/scripts/lib/benchmark-metrics.js +160 -160
  168. package/scripts/lib/budget-enforcer.js +252 -252
  169. package/scripts/lib/certificacion-loop-state.js +421 -0
  170. package/scripts/lib/ci-reader.js +193 -193
  171. package/scripts/lib/ciclo-autonomo/yaml-instintos.js +56 -0
  172. package/scripts/lib/clasificar-directorio.js +92 -0
  173. package/scripts/lib/contadores-inventario.js +217 -217
  174. package/scripts/lib/detectar-host-swl.js +175 -175
  175. package/scripts/lib/detectar-runtime.js +29 -20
  176. package/scripts/lib/detectar-stack-detallado.js +307 -307
  177. package/scripts/lib/detector-autoduplicacion-intra-archivo.js +234 -234
  178. package/scripts/lib/detector-reglas-duplicadas.js +220 -220
  179. package/scripts/lib/eval-metrics-store.js +218 -218
  180. package/scripts/lib/eval-quality.js +171 -171
  181. package/scripts/lib/eval-schemas.js +144 -144
  182. package/scripts/lib/eval-self-correct.js +106 -106
  183. package/scripts/lib/eval-validator.js +185 -185
  184. package/scripts/lib/evidence-verifier.js +192 -0
  185. package/scripts/lib/evidencia-release.js +322 -322
  186. package/scripts/lib/frontmatter-canonico.js +509 -0
  187. package/scripts/lib/gate-engine.js +871 -0
  188. package/scripts/lib/gate-hooks-requires.js +249 -249
  189. package/scripts/lib/gate-licencias.js +212 -212
  190. package/scripts/lib/git-config-preflight.js +48 -0
  191. package/scripts/lib/git-metricas.js +257 -257
  192. package/scripts/lib/harness-ir.js +778 -0
  193. package/scripts/lib/harness-source-snapshot.js +309 -0
  194. package/scripts/lib/integrity-ledger.js +1147 -0
  195. package/scripts/lib/jaccard-similarity.js +98 -98
  196. package/scripts/lib/legacy-gate-migration.js +324 -0
  197. package/scripts/lib/limpiar-basura-global.js +45 -2
  198. package/scripts/lib/longmemeval-runner.js +125 -125
  199. package/scripts/lib/metricas-dora.js +204 -204
  200. package/scripts/lib/notificaciones-telegram.js +1 -0
  201. package/scripts/lib/npm-version.js +1 -0
  202. package/scripts/lib/paquetes-conocidos.js +50 -50
  203. package/scripts/lib/plan-lock.js +61 -13
  204. package/scripts/lib/policy-broker.js +338 -0
  205. package/scripts/lib/policy-bundle.js +342 -0
  206. package/scripts/lib/policy-context-provider.js +310 -0
  207. package/scripts/lib/policy-contract.js +479 -0
  208. package/scripts/lib/policy-verifier-utils.js +65 -0
  209. package/scripts/lib/pr-analyzer.js +399 -399
  210. package/scripts/lib/principal-verifier.js +178 -0
  211. package/scripts/lib/prompt-builder.js +264 -264
  212. package/scripts/lib/resolver-plan-fase.js +37 -37
  213. package/scripts/lib/rrf-fusion.js +175 -175
  214. package/scripts/lib/runtime-adapter-contract.js +267 -0
  215. package/scripts/lib/runtime-artifact-verifier.js +426 -0
  216. package/scripts/lib/runtime-build-attestation.js +127 -0
  217. package/scripts/lib/runtime-bundle-installer.js +586 -0
  218. package/scripts/lib/runtime-compiler.js +327 -0
  219. package/scripts/lib/runtime-conformance.js +202 -0
  220. package/scripts/lib/runtime-doctor-core3.js +567 -0
  221. package/scripts/lib/runtime-doctor-input.js +59 -0
  222. package/scripts/lib/runtime-operation-adapter.js +267 -0
  223. package/scripts/lib/schema-version.js +164 -164
  224. package/scripts/lib/semantic-search.js +252 -252
  225. package/scripts/lib/signed-envelope.js +545 -0
  226. package/scripts/lib/single-use-store.js +359 -0
  227. package/scripts/lib/skills-externas.js +31 -0
  228. package/scripts/lib/transformadores/codex.js +15 -8
  229. package/scripts/lib/transformadores/gemini.js +79 -5
  230. package/scripts/lib/w4-attestation-adapter.js +158 -0
  231. package/scripts/lib/w4-canario.js +337 -0
  232. package/scripts/lib/w4-claims.js +182 -0
  233. package/scripts/lib/w4-corpus-generador.js +542 -0
  234. package/scripts/lib/w4-gate-c5.js +115 -0
  235. package/scripts/lib/w4-harness-bajo-prueba.js +155 -0
  236. package/scripts/lib/w4-matriz-combos.js +55 -0
  237. package/scripts/lib/w4-motor-mutacion.js +1348 -0
  238. package/scripts/lib/w4-motor-replay.js +735 -0
  239. package/scripts/lib/w4-pin-origen.js +54 -0
  240. package/scripts/lib/w4-publicar-request.js +132 -0
  241. package/scripts/lib/w4-revocacion.js +62 -0
  242. package/scripts/lib/w4-runtimes-core3.js +38 -0
  243. package/scripts/lib/w4-scorer-certificacion.js +692 -0
  244. package/scripts/lib/w4-superficie-candidato.js +49 -0
  245. package/scripts/lib/w4-veredicto.js +452 -0
  246. package/scripts/lib/w4-verificar-veredicto.js +302 -0
  247. package/scripts/limpiar-artefactos-python.js +131 -131
  248. package/scripts/migrar-csv-a-array.js +168 -168
  249. package/scripts/migrar-fase-dominio.js +200 -200
  250. package/scripts/migrar-gates-legacy.js +108 -0
  251. package/scripts/publicar-certification-request.js +115 -0
  252. package/scripts/runtime-doctor.js +107 -0
  253. package/scripts/tui/componentes/selector-multi.js +189 -189
  254. package/scripts/tui/componentes/selector-unico.js +158 -158
  255. package/scripts/tui/ejecutores.js +375 -375
  256. package/scripts/tui/lib/colores.js +129 -129
  257. package/scripts/tui/lib/render.js +264 -264
  258. package/scripts/tui/lib/teclas.js +113 -113
  259. package/scripts/tui/pantallas/install-wizard.js +12 -7
  260. package/scripts/tui/pantallas/menu-principal.js +52 -52
  261. package/scripts/tui/pantallas/progreso.js +274 -274
  262. package/scripts/tui/pantallas/resumen.js +132 -132
  263. package/scripts/validar-userland-vacio.js +110 -110
  264. package/scripts/verificar-aislamiento-swl-eval.js +87 -0
  265. package/scripts/verificar-empaquetado-downstream.js +375 -0
  266. package/scripts/verificar-loop-constructor.js +215 -0
  267. package/scripts/verificar-trazabilidad.js +13 -6
  268. package/scripts/verificar-veredicto-real.js +84 -0
@@ -0,0 +1,567 @@
1
+ 'use strict';
2
+
3
+ /** Diagnóstico estático y probes acotados de runtimes Core-3. */
4
+
5
+ const { spawn } = require('node:child_process');
6
+ const fs = require('node:fs');
7
+ const path = require('node:path');
8
+ const util = require('node:util');
9
+
10
+ const { cargarContratoAdapters } = require('./runtime-adapter-contract');
11
+ const { verificarLocator } = require('./runtime-artifact-verifier');
12
+ const {
13
+ canonicalizarIJson,
14
+ validarTimestampIso,
15
+ } = require('./signed-envelope');
16
+
17
+ const CONTRACT = cargarContratoAdapters();
18
+ const RUNTIME_IDS = Object.freeze(CONTRACT.runtimes.map(({ id }) => id));
19
+ const CANARY_IDS = Object.freeze([
20
+ 'instructions-load',
21
+ 'swl-command',
22
+ 'forbidden-mutation-block',
23
+ 'verified-context',
24
+ 'single-use-grant',
25
+ ]);
26
+ const OPTION_KEYS = Object.freeze(['now', 'pathEntries', 'bundles', 'buildEvents']);
27
+ const DIAGNOSTIC_KEYS = Object.freeze([
28
+ 'schemaVersion', 'kind', 'generatedAt', 'effectiveMode', 'certification', 'runtimes',
29
+ ]);
30
+ const RUNTIME_KEYS = Object.freeze([
31
+ 'runtimeId', 'installerTarget', 'availability', 'bundle', 'buildAttestation',
32
+ 'canaries', 'effectiveMode', 'certification', 'reasons',
33
+ ]);
34
+ const AVAILABILITY_KEYS = Object.freeze([
35
+ 'status', 'binary', 'version', 'exitCode', 'reasonCode',
36
+ ]);
37
+ const STATIC_KEYS = Object.freeze(['status', 'reasonCode']);
38
+ const CANARY_KEYS = Object.freeze(['id', 'status', 'reasonCode']);
39
+ const AVAILABILITY_STATES = new Set([
40
+ 'available', 'missing', 'blocked', 'timeout', 'failed', 'unverified',
41
+ ]);
42
+ const AVAILABILITY_REASONS = new Set([
43
+ 'BINARY_MISSING', 'RUNTIME_AVAILABLE', 'SPAWN_BLOCKED', 'SPAWN_TIMEOUT',
44
+ 'SPAWN_FAILED', 'OUTPUT_LIMIT_EXCEEDED', 'EXIT_NOT_ALLOWED', 'VERSION_UNPARSEABLE',
45
+ ]);
46
+ const AVAILABILITY_REASON_BY_STATUS = Object.freeze({
47
+ available: new Set(['RUNTIME_AVAILABLE']),
48
+ missing: new Set(['BINARY_MISSING']),
49
+ blocked: new Set(['SPAWN_BLOCKED']),
50
+ timeout: new Set(['SPAWN_TIMEOUT']),
51
+ failed: new Set(['SPAWN_FAILED', 'OUTPUT_LIMIT_EXCEEDED', 'EXIT_NOT_ALLOWED']),
52
+ unverified: new Set(['VERSION_UNPARSEABLE']),
53
+ });
54
+ const BUNDLE_REASON_BY_STATUS = Object.freeze({
55
+ verified: 'BUNDLE_VERIFIED', missing: 'BUNDLE_MISSING', invalid: 'BUNDLE_INVALID',
56
+ });
57
+ const BUILD_REASON_BY_STATUS = Object.freeze({
58
+ missing: 'BUILD_ATTESTATION_MISSING',
59
+ invalid: 'BUILD_ATTESTATION_INVALID',
60
+ unverified: 'BUILD_ATTESTATION_UNVERIFIED',
61
+ });
62
+ const SAFE_ENV = Object.freeze({
63
+ NO_COLOR: '1',
64
+ PATH: path.dirname(process.execPath),
65
+ TERM: 'dumb',
66
+ });
67
+ const PROBE_CWD = path.parse(process.execPath).root;
68
+ const TERMINATION_GRACE_MS = 250;
69
+ const SNAPSHOT_MAX_DEPTH = 32;
70
+ const SNAPSHOT_MAX_NODES = 10000;
71
+ const DIAGNOSTICS = new WeakSet();
72
+
73
+ function invalidInput() {
74
+ throw new TypeError('entrada de diagnóstico inválida');
75
+ }
76
+
77
+ function isDataObject(value) {
78
+ if (!value || typeof value !== 'object' || Array.isArray(value)
79
+ || util.types.isProxy(value)) return false;
80
+ const prototype = Object.getPrototypeOf(value);
81
+ if (prototype !== Object.prototype && prototype !== null) return false;
82
+ if (Object.getOwnPropertySymbols(value).length > 0) return false;
83
+ return Object.values(Object.getOwnPropertyDescriptors(value))
84
+ .every((descriptor) => descriptor.enumerable && 'value' in descriptor);
85
+ }
86
+
87
+ function exactKeys(value, keys) {
88
+ return isDataObject(value)
89
+ && JSON.stringify(Object.keys(value).sort()) === JSON.stringify([...keys].sort());
90
+ }
91
+
92
+ function isDataArray(value) {
93
+ if (!Array.isArray(value) || util.types.isProxy(value)
94
+ || Object.getOwnPropertySymbols(value).length > 0) return false;
95
+ const descriptors = Object.getOwnPropertyDescriptors(value);
96
+ const entries = Object.entries(descriptors).filter(([key]) => key !== 'length');
97
+ return entries.length === value.length && entries.every(([key, descriptor]) => (
98
+ /^(?:0|[1-9]\d*)$/u.test(key) && descriptor.enumerable && 'value' in descriptor
99
+ ));
100
+ }
101
+
102
+ function validatePathEntries(entries) {
103
+ if (!isDataArray(entries) || entries.length > 64) invalidInput();
104
+ const valid = entries.every((entry) => (
105
+ typeof entry === 'string' && entry.length > 0 && entry.length <= 4096
106
+ && path.isAbsolute(entry) && entry.normalize('NFKC') === entry && !entry.includes('\0')
107
+ ));
108
+ if (!valid || new Set(entries).size !== entries.length) invalidInput();
109
+ }
110
+
111
+ function validateInputMap(value) {
112
+ if (!exactKeys(value, RUNTIME_IDS)) invalidInput();
113
+ }
114
+
115
+ function validateOptions(options) {
116
+ if (!exactKeys(options, OPTION_KEYS)) invalidInput();
117
+ try {
118
+ validarTimestampIso(options.now, 'diagnostic now');
119
+ } catch {
120
+ invalidInput();
121
+ }
122
+ validatePathEntries(options.pathEntries);
123
+ validateInputMap(options.bundles);
124
+ validateInputMap(options.buildEvents);
125
+ }
126
+
127
+ function snapshotPrimitive(value) {
128
+ const type = typeof value;
129
+ if (value === null || type === 'string' || type === 'boolean') return value;
130
+ if (type === 'number' && Number.isFinite(value)) return value;
131
+ invalidInput();
132
+ }
133
+
134
+ function snapshotData(value, context, depth = 0) {
135
+ if (!value || typeof value !== 'object') return snapshotPrimitive(value);
136
+ context.nodes += 1;
137
+ if (depth > SNAPSHOT_MAX_DEPTH || context.nodes > SNAPSHOT_MAX_NODES
138
+ || context.seen.has(value)) invalidInput();
139
+ context.seen.add(value);
140
+ if (isDataArray(value)) {
141
+ const descriptors = Object.getOwnPropertyDescriptors(value);
142
+ return value.map((unused, index) => (
143
+ snapshotData(descriptors[String(index)].value, context, depth + 1)
144
+ ));
145
+ }
146
+ if (!isDataObject(value)) invalidInput();
147
+ const clone = {};
148
+ for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
149
+ clone[key] = snapshotData(descriptor.value, context, depth + 1);
150
+ }
151
+ return clone;
152
+ }
153
+
154
+ function snapshotOptions(options) {
155
+ const context = { nodes: 0, seen: new WeakSet() };
156
+ return freezeDeep(snapshotData(options, context));
157
+ }
158
+
159
+ function isRealDirectory(entry) {
160
+ try {
161
+ const stats = fs.lstatSync(entry);
162
+ return stats.isDirectory() && !stats.isSymbolicLink()
163
+ && path.resolve(fs.realpathSync(entry)) === path.resolve(entry);
164
+ } catch {
165
+ return false;
166
+ }
167
+ }
168
+
169
+ function resolveBinary(runtime, pathEntries) {
170
+ for (const entry of pathEntries) {
171
+ if (!isRealDirectory(entry)) continue;
172
+ for (const candidate of orderedCandidates(runtime.binaries)) {
173
+ const target = path.join(entry, candidate);
174
+ try {
175
+ const stats = fs.lstatSync(target);
176
+ if (stats.isFile() && !stats.isSymbolicLink()) {
177
+ return Object.freeze({ path: target, candidate });
178
+ }
179
+ } catch { /* un candidato ausente no invalida los demás */ }
180
+ }
181
+ }
182
+ return null;
183
+ }
184
+
185
+ function orderedCandidates(candidates) {
186
+ if (process.platform !== 'win32') {
187
+ return candidates.filter((candidate) => !/\.(?:cmd|exe)$/iu.test(candidate));
188
+ }
189
+ return [...candidates].sort((left, right) => candidateRank(left) - candidateRank(right));
190
+ }
191
+
192
+ function candidateRank(candidate) {
193
+ if (candidate.endsWith('.exe')) return 0;
194
+ if (candidate.endsWith('.cmd')) return 1;
195
+ return 2;
196
+ }
197
+
198
+ function availability(status, binary, version, exitCode, reasonCode) {
199
+ return Object.freeze({ status, binary, version, exitCode, reasonCode });
200
+ }
201
+
202
+ function classifySpawnError(error, binary) {
203
+ if (error && ['EACCES', 'EPERM'].includes(error.code)) {
204
+ return availability('blocked', binary, null, null, 'SPAWN_BLOCKED');
205
+ }
206
+ if (error && error.code === 'ENOENT') {
207
+ return availability('missing', null, null, null, 'BINARY_MISSING');
208
+ }
209
+ return availability('failed', binary, null, null, 'SPAWN_FAILED');
210
+ }
211
+
212
+ function stopChild(child) {
213
+ try { return child.kill('SIGKILL') !== false; } catch { return false; }
214
+ }
215
+
216
+ function abandonChild(child) {
217
+ for (const stream of [child.stdout, child.stderr]) {
218
+ try { if (typeof stream.destroy === 'function') stream.destroy(); } catch { /* best effort */ }
219
+ }
220
+ try { if (typeof child.unref === 'function') child.unref(); } catch { /* best effort */ }
221
+ }
222
+
223
+ function consumeOutput(state, chunk, child, control, capture) {
224
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
225
+ state.total += bytes.length;
226
+ if (state.total > state.limit) {
227
+ control.terminate(child, availability(
228
+ 'failed', state.binary, null, null, 'OUTPUT_LIMIT_EXCEEDED'
229
+ ), state.binary);
230
+ return;
231
+ }
232
+ if (capture) state.stdout.push(bytes);
233
+ }
234
+
235
+ function resultFromClose(state, code, pattern) {
236
+ if (!Number.isInteger(code)) {
237
+ return availability('failed', state.binary, null, null, 'SPAWN_FAILED');
238
+ }
239
+ if (code !== 0) {
240
+ return availability('failed', state.binary, null, code, 'EXIT_NOT_ALLOWED');
241
+ }
242
+ const match = Buffer.concat(state.stdout).toString('utf8').trim().match(pattern);
243
+ if (!match || match[1].length > 64) {
244
+ return availability('unverified', state.binary, null, code, 'VERSION_UNPARSEABLE');
245
+ }
246
+ return availability('available', state.binary, match[1], code, 'RUNTIME_AVAILABLE');
247
+ }
248
+
249
+ function scheduleTerminationGrace(child, binary, settle) {
250
+ const timer = setTimeout(() => {
251
+ abandonChild(child);
252
+ settle(availability('failed', binary, null, null, 'SPAWN_FAILED'));
253
+ }, TERMINATION_GRACE_MS);
254
+ timer.unref();
255
+ return timer;
256
+ }
257
+
258
+ function clearTimers(...timers) {
259
+ for (const timer of timers) {
260
+ if (timer) clearTimeout(timer);
261
+ }
262
+ }
263
+
264
+ function createSettlement(resolve) {
265
+ let settled = false;
266
+ let timer;
267
+ let graceTimer;
268
+ let cleanup = () => {};
269
+ let pendingTermination = null;
270
+ function settle(result) {
271
+ if (settled) return;
272
+ settled = true;
273
+ clearTimers(timer, graceTimer);
274
+ cleanup();
275
+ resolve(result);
276
+ }
277
+ return Object.freeze({
278
+ complete(result) { settle(pendingTermination || result); },
279
+ settle,
280
+ terminate(child, result, binary) {
281
+ if (settled || pendingTermination) return;
282
+ pendingTermination = result;
283
+ if (!stopChild(child)) {
284
+ abandonChild(child);
285
+ settle(availability('failed', binary, null, null, 'SPAWN_FAILED'));
286
+ return;
287
+ }
288
+ graceTimer = scheduleTerminationGrace(child, binary, settle);
289
+ },
290
+ setCleanup(value) { cleanup = value; },
291
+ setTimer(value) { timer = value; },
292
+ });
293
+ }
294
+
295
+ function invalidCmdPath(target) {
296
+ return /[%!&|<>^"\r\n]/u.test(target);
297
+ }
298
+
299
+ function cmdExecutable() {
300
+ const systemRoot = process.env.SystemRoot;
301
+ if (typeof systemRoot !== 'string' || !path.isAbsolute(systemRoot)) {
302
+ throw Object.assign(new Error('cmd no disponible'), { code: 'EACCES' });
303
+ }
304
+ return path.join(systemRoot, 'System32', 'cmd.exe');
305
+ }
306
+
307
+ function spawnSpecification(resolved) {
308
+ if (!resolved.candidate.endsWith('.cmd')) {
309
+ return {
310
+ executable: resolved.path,
311
+ args: ['--version'],
312
+ windowsVerbatimArguments: false,
313
+ };
314
+ }
315
+ if (invalidCmdPath(resolved.path)) {
316
+ throw Object.assign(new Error('ruta cmd inválida'), { code: 'EACCES' });
317
+ }
318
+ return {
319
+ executable: cmdExecutable(),
320
+ args: ['/d', '/s', '/v:off', '/c', `""${resolved.path}" --version"`],
321
+ windowsVerbatimArguments: true,
322
+ };
323
+ }
324
+
325
+ function spawnProbe(resolved) {
326
+ const spec = spawnSpecification(resolved);
327
+ return spawn(spec.executable, spec.args, {
328
+ cwd: PROBE_CWD, env: SAFE_ENV, shell: false,
329
+ stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true,
330
+ windowsVerbatimArguments: spec.windowsVerbatimArguments,
331
+ });
332
+ }
333
+
334
+ function bindProbe(child, state, runtime, control) {
335
+ const onStdout = (chunk) => consumeOutput(state, chunk, child, control, true);
336
+ const onStderr = (chunk) => consumeOutput(state, chunk, child, control, false);
337
+ const onError = (error) => control.complete(classifySpawnError(error, state.binary));
338
+ const onClose = (code) => control.complete(resultFromClose(
339
+ state, code, new RegExp(runtime.versionProbe.parsePattern, 'u')
340
+ ));
341
+ child.stdout.on('data', onStdout);
342
+ child.stderr.on('data', onStderr);
343
+ child.once('error', onError);
344
+ child.once('close', onClose);
345
+ control.setCleanup(() => {
346
+ child.stdout.removeListener('data', onStdout);
347
+ child.stderr.removeListener('data', onStderr);
348
+ child.removeListener('error', onError);
349
+ child.removeListener('close', onClose);
350
+ });
351
+ const timer = setTimeout(() => {
352
+ control.terminate(child, availability(
353
+ 'timeout', state.binary, null, null, 'SPAWN_TIMEOUT'
354
+ ), state.binary);
355
+ }, runtime.versionProbe.timeoutMs);
356
+ timer.unref();
357
+ control.setTimer(timer);
358
+ }
359
+
360
+ function runProbe(runtime, resolved) {
361
+ const state = {
362
+ total: 0, stdout: [], limit: runtime.versionProbe.maxOutputBytes,
363
+ binary: resolved.candidate,
364
+ };
365
+ return new Promise((resolve) => {
366
+ const control = createSettlement(resolve);
367
+ let child;
368
+ try { child = spawnProbe(resolved); } catch (error) {
369
+ control.settle(classifySpawnError(error, resolved.candidate));
370
+ return;
371
+ }
372
+ bindProbe(child, state, runtime, control);
373
+ });
374
+ }
375
+
376
+ async function diagnoseAvailability(runtime, pathEntries) {
377
+ const resolved = resolveBinary(runtime, pathEntries);
378
+ if (!resolved) return availability('missing', null, null, null, 'BINARY_MISSING');
379
+ return runProbe(runtime, resolved);
380
+ }
381
+
382
+ function staticStatus(status, reasonCode) {
383
+ return Object.freeze({ status, reasonCode });
384
+ }
385
+
386
+ function diagnoseBundle(bundle) {
387
+ if (bundle === null) return staticStatus('missing', 'BUNDLE_MISSING');
388
+ if (!exactKeys(bundle, ['root', 'locator'])) {
389
+ return staticStatus('invalid', 'BUNDLE_INVALID');
390
+ }
391
+ try {
392
+ verificarLocator(bundle.root, bundle.locator);
393
+ return staticStatus('verified', 'BUNDLE_VERIFIED');
394
+ } catch {
395
+ return staticStatus('invalid', 'BUNDLE_INVALID');
396
+ }
397
+ }
398
+
399
+ function plausibleBuildEvent(event, runtimeId) {
400
+ return exactKeys(event, ['eventType', 'payload', 'signature'])
401
+ && event.eventType === 'runtime-build'
402
+ && exactKeys(event.payload, ['runtimeId', 'effectiveMode'])
403
+ && event.payload.runtimeId === runtimeId
404
+ && event.payload.effectiveMode === 'proposal_only'
405
+ && typeof event.signature === 'string' && event.signature.length > 0
406
+ && event.signature.length <= 4096;
407
+ }
408
+
409
+ function diagnoseBuild(event, runtimeId) {
410
+ if (event === null) return staticStatus('missing', 'BUILD_ATTESTATION_MISSING');
411
+ if (!plausibleBuildEvent(event, runtimeId)) {
412
+ return staticStatus('invalid', 'BUILD_ATTESTATION_INVALID');
413
+ }
414
+ return staticStatus('unverified', 'BUILD_ATTESTATION_UNVERIFIED');
415
+ }
416
+
417
+ function createCanaries() {
418
+ return Object.freeze(CANARY_IDS.map((id) => Object.freeze({
419
+ id, status: 'not_evaluated', reasonCode: 'W4_REQUIRED',
420
+ })));
421
+ }
422
+
423
+ function createReasons(availabilityResult, bundle, buildAttestation) {
424
+ return Object.freeze([...new Set([
425
+ availabilityResult.reasonCode,
426
+ bundle.reasonCode,
427
+ buildAttestation.reasonCode,
428
+ 'BEHAVIOR_NOT_EVALUATED',
429
+ 'CERTIFICATION_UNVERIFIED',
430
+ ])].sort());
431
+ }
432
+
433
+ async function diagnoseRuntime(runtime, options) {
434
+ const availabilityResult = await diagnoseAvailability(runtime, options.pathEntries);
435
+ const bundle = diagnoseBundle(options.bundles[runtime.id]);
436
+ const buildAttestation = diagnoseBuild(options.buildEvents[runtime.id], runtime.id);
437
+ return Object.freeze({
438
+ runtimeId: runtime.id,
439
+ installerTarget: runtime.installerTarget,
440
+ availability: availabilityResult,
441
+ bundle,
442
+ buildAttestation,
443
+ canaries: createCanaries(),
444
+ effectiveMode: 'proposal_only',
445
+ certification: 'unverified',
446
+ reasons: createReasons(availabilityResult, bundle, buildAttestation),
447
+ });
448
+ }
449
+
450
+ function freezeDeep(value) {
451
+ if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;
452
+ Object.values(value).forEach(freezeDeep);
453
+ return Object.freeze(value);
454
+ }
455
+
456
+ function assertOutput(condition) {
457
+ if (!condition) throw new TypeError('diagnóstico interno viola su contrato');
458
+ }
459
+
460
+ function validateAvailability(value) {
461
+ assertOutput(exactKeys(value, AVAILABILITY_KEYS)
462
+ && AVAILABILITY_STATES.has(value.status)
463
+ && AVAILABILITY_REASONS.has(value.reasonCode)
464
+ && AVAILABILITY_REASON_BY_STATUS[value.status].has(value.reasonCode));
465
+ const binaryValid = value.status === 'missing'
466
+ ? value.binary === null
467
+ : typeof value.binary === 'string' && /^[A-Za-z0-9._-]{1,64}$/u.test(value.binary);
468
+ const versionValid = value.status === 'available'
469
+ ? typeof value.version === 'string' && /^[0-9]+\.[0-9]+\.[0-9]+$/u.test(value.version)
470
+ && value.version.length <= 64
471
+ : value.version === null;
472
+ assertOutput(binaryValid && versionValid && validExitCode(value));
473
+ }
474
+
475
+ function validExitCode(value) {
476
+ if (['available', 'unverified'].includes(value.status)) return value.exitCode === 0;
477
+ if (value.reasonCode === 'EXIT_NOT_ALLOWED') {
478
+ return Number.isSafeInteger(value.exitCode) && value.exitCode !== 0;
479
+ }
480
+ return value.exitCode === null;
481
+ }
482
+
483
+ function validateStaticStatus(value, reasons) {
484
+ assertOutput(exactKeys(value, STATIC_KEYS)
485
+ && Object.hasOwn(reasons, value.status)
486
+ && reasons[value.status] === value.reasonCode);
487
+ }
488
+
489
+ function validateCanaries(canaries) {
490
+ assertOutput(isDataArray(canaries) && canaries.length === CANARY_IDS.length);
491
+ canaries.forEach((canary, index) => assertOutput(
492
+ exactKeys(canary, CANARY_KEYS) && canary.id === CANARY_IDS[index]
493
+ && canary.status === 'not_evaluated' && canary.reasonCode === 'W4_REQUIRED'
494
+ ));
495
+ }
496
+
497
+ function validateRuntimeOutput(value, runtime) {
498
+ assertOutput(exactKeys(value, RUNTIME_KEYS));
499
+ assertOutput(value.runtimeId === runtime.id && value.installerTarget === runtime.installerTarget);
500
+ validateAvailability(value.availability);
501
+ validateStaticStatus(value.bundle, BUNDLE_REASON_BY_STATUS);
502
+ validateStaticStatus(value.buildAttestation, BUILD_REASON_BY_STATUS);
503
+ validateCanaries(value.canaries);
504
+ assertOutput(value.effectiveMode === 'proposal_only' && value.certification === 'unverified');
505
+ const expectedReasons = createReasons(value.availability, value.bundle, value.buildAttestation);
506
+ assertOutput(isDataArray(value.reasons)
507
+ && JSON.stringify(value.reasons) === JSON.stringify(expectedReasons));
508
+ }
509
+
510
+ function validateDiagnosticOutput(value) {
511
+ assertOutput(exactKeys(value, DIAGNOSTIC_KEYS));
512
+ assertOutput(value.schemaVersion === '1.0.0'
513
+ && value.kind === 'swl-runtime-diagnostic-core3'
514
+ && value.effectiveMode === 'proposal_only' && value.certification === 'unverified');
515
+ validarTimestampIso(value.generatedAt, 'diagnostic generatedAt');
516
+ assertOutput(isDataArray(value.runtimes) && value.runtimes.length === CONTRACT.runtimes.length);
517
+ value.runtimes.forEach((runtime, index) => (
518
+ validateRuntimeOutput(runtime, CONTRACT.runtimes[index], index)
519
+ ));
520
+ }
521
+
522
+ /**
523
+ * Exige exactamente los tres runtimes Core-3 (`validateDiagnosticOutput`).
524
+ * Una vista parcial (p. ej. filtrada por `--target` en `scripts/runtime-
525
+ * doctor.js`) es una selección de conveniencia para el consumidor, no un
526
+ * diagnóstico Core-3 completo — rechazarla aquí es correcto, no un bug.
527
+ */
528
+ function validarDiagnosticoCore3(value) {
529
+ validateDiagnosticOutput(value);
530
+ return true;
531
+ }
532
+
533
+ async function diagnosticarCore3(options) {
534
+ validateOptions(options);
535
+ const snapshot = snapshotOptions(options);
536
+ const runtimes = [];
537
+ for (const runtime of CONTRACT.runtimes) {
538
+ runtimes.push(await diagnoseRuntime(runtime, snapshot));
539
+ }
540
+ const result = freezeDeep({
541
+ schemaVersion: '1.0.0', kind: 'swl-runtime-diagnostic-core3',
542
+ generatedAt: snapshot.now, effectiveMode: 'proposal_only',
543
+ certification: 'unverified', runtimes,
544
+ });
545
+ validateDiagnosticOutput(result);
546
+ DIAGNOSTICS.add(result);
547
+ return result;
548
+ }
549
+
550
+ /** Identidad exacta contra el WeakSet interno — una vista filtrada por target nunca coincide. */
551
+ function esDiagnosticoCore3(value) {
552
+ return DIAGNOSTICS.has(value);
553
+ }
554
+
555
+ /** Igual que esDiagnosticoCore3(): solo serializa el diagnóstico completo emitido por diagnosticarCore3(). */
556
+ function serializarDiagnosticoCore3(value) {
557
+ if (!DIAGNOSTICS.has(value)) throw new TypeError('se requiere diagnóstico verificado');
558
+ validateDiagnosticOutput(value);
559
+ return canonicalizarIJson(value);
560
+ }
561
+
562
+ module.exports = {
563
+ diagnosticarCore3,
564
+ esDiagnosticoCore3,
565
+ serializarDiagnosticoCore3,
566
+ validarDiagnosticoCore3,
567
+ };
@@ -0,0 +1,59 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Construye el input read-only que consume diagnosticarCore3() a partir del
5
+ * entorno del proceso: PATH normalizado y mapas vacíos de bundle/build-event
6
+ * (el CLI y `doctor` nunca tienen contexto de instalación real que ofrecer).
7
+ *
8
+ * Compartido entre scripts/runtime-doctor.js y la integración de scripts/doctor.js
9
+ * para no duplicar las reglas de saneamiento de PATH (DRY).
10
+ *
11
+ * @module scripts/lib/runtime-doctor-input
12
+ */
13
+
14
+ const path = require('node:path');
15
+
16
+ const { CORE_RUNTIME_IDS } = require('./runtime-adapter-contract');
17
+
18
+ const MAX_PATH_ENTRIES = 64;
19
+ const MAX_PATH_LENGTH = 4096;
20
+
21
+ /**
22
+ * Normaliza `process.env.PATH` a un array apto para diagnosticarCore3():
23
+ * absoluto, NFKC, sin byte nulo, sin duplicados, acotado a 64 entradas.
24
+ * Entradas que no cumplen el formato se descartan silenciosamente — el
25
+ * validador aguas abajo falla cerrado ante cualquier entrada residual mal
26
+ * formada, así que aquí basta con producir un array ya conforme.
27
+ *
28
+ * @param {NodeJS.ProcessEnv} [env]
29
+ * @returns {string[]}
30
+ */
31
+ function pathEntriesFromEnv(env = process.env) {
32
+ const crudo = String(env.PATH || env.Path || '').split(path.delimiter);
33
+ const vistos = new Set();
34
+ const entradas = [];
35
+ for (const segmento of crudo) {
36
+ if (!segmento) continue;
37
+ const entrada = normalizarEntrada(segmento);
38
+ if (!entrada || vistos.has(entrada)) continue;
39
+ vistos.add(entrada);
40
+ entradas.push(entrada);
41
+ if (entradas.length >= MAX_PATH_ENTRIES) break;
42
+ }
43
+ return entradas;
44
+ }
45
+
46
+ function normalizarEntrada(segmento) {
47
+ const absoluta = path.resolve(segmento);
48
+ if (absoluta.length === 0 || absoluta.length > MAX_PATH_LENGTH) return null;
49
+ if (!path.isAbsolute(absoluta) || absoluta.includes('\0')) return null;
50
+ if (absoluta.normalize('NFKC') !== absoluta) return null;
51
+ return absoluta;
52
+ }
53
+
54
+ /** Mapa `{ [runtimeId]: null }` para las tres claves Core-3 exigidas por diagnosticarCore3(). */
55
+ function emptyRuntimeMap() {
56
+ return Object.fromEntries(CORE_RUNTIME_IDS.map((id) => [id, null]));
57
+ }
58
+
59
+ module.exports = { pathEntriesFromEnv, emptyRuntimeMap, MAX_PATH_ENTRIES };