@ssolis-ti/mcp-compra-agil 2.4.0 → 2.5.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 (72) hide show
  1. package/README.md +21 -6
  2. package/dist/api/compra-agil-client.d.ts +29 -0
  3. package/dist/api/compra-agil-client.d.ts.map +1 -1
  4. package/dist/api/compra-agil-client.js +35 -0
  5. package/dist/api/compra-agil-client.js.map +1 -1
  6. package/dist/index.js +19 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/services/monitor.js +11 -1
  9. package/dist/services/monitor.js.map +1 -1
  10. package/dist/tools/analizar-precios-mercado.d.ts.map +1 -1
  11. package/dist/tools/analizar-precios-mercado.js +68 -14
  12. package/dist/tools/analizar-precios-mercado.js.map +1 -1
  13. package/dist/tools/auditar-desiertas.d.ts.map +1 -1
  14. package/dist/tools/auditar-desiertas.js +31 -8
  15. package/dist/tools/auditar-desiertas.js.map +1 -1
  16. package/dist/tools/buscar-compras.d.ts.map +1 -1
  17. package/dist/tools/buscar-compras.js +2 -0
  18. package/dist/tools/buscar-compras.js.map +1 -1
  19. package/dist/tools/detalle-compra.d.ts.map +1 -1
  20. package/dist/tools/detalle-compra.js +2 -0
  21. package/dist/tools/detalle-compra.js.map +1 -1
  22. package/dist/tools/detalle-oc.d.ts.map +1 -1
  23. package/dist/tools/detalle-oc.js +2 -0
  24. package/dist/tools/detalle-oc.js.map +1 -1
  25. package/dist/tools/documentos.d.ts.map +1 -1
  26. package/dist/tools/documentos.js +6 -0
  27. package/dist/tools/documentos.js.map +1 -1
  28. package/dist/tools/estadisticas-uso.d.ts.map +1 -1
  29. package/dist/tools/estadisticas-uso.js +2 -0
  30. package/dist/tools/estadisticas-uso.js.map +1 -1
  31. package/dist/tools/generar-borrador.d.ts +1 -1
  32. package/dist/tools/generar-borrador.d.ts.map +1 -1
  33. package/dist/tools/generar-borrador.js +35 -18
  34. package/dist/tools/generar-borrador.js.map +1 -1
  35. package/dist/tools/generar-informe.d.ts.map +1 -1
  36. package/dist/tools/generar-informe.js +5 -1
  37. package/dist/tools/generar-informe.js.map +1 -1
  38. package/dist/tools/monitorear-cambios.d.ts.map +1 -1
  39. package/dist/tools/monitorear-cambios.js +2 -0
  40. package/dist/tools/monitorear-cambios.js.map +1 -1
  41. package/dist/tools/radar-oportunidades.d.ts +5 -0
  42. package/dist/tools/radar-oportunidades.d.ts.map +1 -1
  43. package/dist/tools/radar-oportunidades.js +18 -4
  44. package/dist/tools/radar-oportunidades.js.map +1 -1
  45. package/dist/tools/verificar-hora.d.ts +13 -0
  46. package/dist/tools/verificar-hora.d.ts.map +1 -0
  47. package/dist/tools/verificar-hora.js +71 -0
  48. package/dist/tools/verificar-hora.js.map +1 -0
  49. package/dist/tools/verificar-oc.d.ts.map +1 -1
  50. package/dist/tools/verificar-oc.js +2 -0
  51. package/dist/tools/verificar-oc.js.map +1 -1
  52. package/dist/tools/verificar-ticket.js +1 -1
  53. package/dist/tools/verificar-ticket.js.map +1 -1
  54. package/dist/utils/concurrencia.d.ts +63 -0
  55. package/dist/utils/concurrencia.d.ts.map +1 -0
  56. package/dist/utils/concurrencia.js +122 -0
  57. package/dist/utils/concurrencia.js.map +1 -0
  58. package/dist/utils/error-handler.d.ts.map +1 -1
  59. package/dist/utils/error-handler.js +8 -0
  60. package/dist/utils/error-handler.js.map +1 -1
  61. package/dist/utils/fechas.d.ts +50 -0
  62. package/dist/utils/fechas.d.ts.map +1 -0
  63. package/dist/utils/fechas.js +77 -0
  64. package/dist/utils/fechas.js.map +1 -0
  65. package/dist/utils/logger.d.ts.map +1 -1
  66. package/dist/utils/logger.js +13 -2
  67. package/dist/utils/logger.js.map +1 -1
  68. package/dist/utils/ntp.d.ts +51 -0
  69. package/dist/utils/ntp.d.ts.map +1 -0
  70. package/dist/utils/ntp.js +121 -0
  71. package/dist/utils/ntp.js.map +1 -0
  72. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fechas.js","sourceRoot":"","sources":["../../src/utils/fechas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,wEAAwE;AACxE,MAAM,QAAQ,GAAG,mDAAmD,CAAC;AAErE;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAgC;IAC9D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IAEnC,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,CAAC,EAAE,CAAC;QACN,sEAAsE;QACtE,sEAAsE;QACtE,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;QAC9B,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAC;QACnD,OAAO,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC;IAED,6DAA6D;IAC7D,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,OAAO,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,cAAc,CAAC,KAAgC;IAC7D,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAC5B,qFAAqF;IACrF,uGAAuG;IACvG,wGAAwG,CAAC;AAE3G;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAgC;IAC5D,MAAM,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,wEAAwE;IACxE,OAAO,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,kBAAkB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAqBH,wBAAgB,YAAY,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAE9C;AA2BD,eAAO,MAAM,MAAM;iBACJ,MAAM,SAAS,OAAO;gBACvB,MAAM,SAAS,OAAO;gBACtB,MAAM,SAAS,OAAO;iBACrB,MAAM,SAAS,OAAO;CACpC,CAAC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAuBH,wBAAgB,YAAY,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAE9C;AAsCD,eAAO,MAAM,MAAM;iBACJ,MAAM,SAAS,OAAO;gBACvB,MAAM,SAAS,OAAO;gBACtB,MAAM,SAAS,OAAO;iBACrB,MAAM,SAAS,OAAO;CACpC,CAAC"}
@@ -17,6 +17,8 @@ function formatTimestamp() {
17
17
  return new Date().toISOString();
18
18
  }
19
19
  let mcpServer = null;
20
+ /** Evita repetir el aviso de fallo de envío en cada línea de log. */
21
+ let falloDeEnvioAvisado = false;
20
22
  export function setMcpServer(server) {
21
23
  mcpServer = server;
22
24
  }
@@ -37,8 +39,17 @@ function log(level, message, data) {
37
39
  level,
38
40
  logger: 'mcp-compra-agil',
39
41
  data: seguro,
40
- }).catch(() => {
41
- // Ignorar fallos silenciosamente para no interrumpir el flujo principal
42
+ }).catch((e) => {
43
+ // No se interrumpe el flujo principal por un log que no se pudo enviar,
44
+ // pero tampoco se calla del todo: este `catch` mudo fue lo que ocultó
45
+ // durante meses que la capacidad `logging` no estaba declarada y que
46
+ // NINGUNA notificación llegaba al cliente. Se avisa una sola vez, a
47
+ // stderr, para que el aviso no se convierta en el ruido que denuncia.
48
+ if (!falloDeEnvioAvisado) {
49
+ falloDeEnvioAvisado = true;
50
+ console.error(`[${formatTimestamp()}] [WARN] No se pudieron enviar logs al cliente MCP ` +
51
+ `(se seguirá escribiendo a stderr). Causa: ${redact(String(e))}`);
52
+ }
42
53
  });
43
54
  }
44
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,MAAM,UAAU,GAA6B;IAC3C,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AAEF,MAAM,YAAY,GAAc,OAAO,CAAC,GAAG,CAAC,SAAsB,IAAI,MAAM,CAAC;AAE7E,SAAS,eAAe;IACtB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,IAAI,SAAS,GAAQ,IAAI,CAAC;AAE1B,MAAM,UAAU,YAAY,CAAC,MAAW;IACtC,SAAS,GAAG,MAAM,CAAC;AACrB,CAAC;AAED,SAAS,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,IAAc;IAC3D,IAAI,UAAU,CAAC,KAAK,CAAC,GAAG,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO;IAEzD,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,MAAM,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC;IACjE,yEAAyE;IACzE,gDAAgD;IAChD,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IACrE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAE9B,+BAA+B;IAC/B,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC,CAAC;IAErC,6EAA6E;IAC7E,kEAAkE;IAClE,IAAI,SAAS,EAAE,CAAC;QACd,SAAS,CAAC,kBAAkB,CAAC;YAC3B,KAAK;YACL,MAAM,EAAE,iBAAiB;YACzB,IAAI,EAAE,MAAM;SACb,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YACZ,wEAAwE;QAC1E,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,KAAK,EAAE,CAAC,GAAW,EAAE,IAAc,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;IAC/D,IAAI,EAAE,CAAC,GAAW,EAAE,IAAc,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;IAC7D,IAAI,EAAE,CAAC,GAAW,EAAE,IAAc,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;IAC7D,KAAK,EAAE,CAAC,GAAW,EAAE,IAAc,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;CAChE,CAAC"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,MAAM,UAAU,GAA6B;IAC3C,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AAEF,MAAM,YAAY,GAAc,OAAO,CAAC,GAAG,CAAC,SAAsB,IAAI,MAAM,CAAC;AAE7E,SAAS,eAAe;IACtB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,IAAI,SAAS,GAAQ,IAAI,CAAC;AAC1B,qEAAqE;AACrE,IAAI,mBAAmB,GAAG,KAAK,CAAC;AAEhC,MAAM,UAAU,YAAY,CAAC,MAAW;IACtC,SAAS,GAAG,MAAM,CAAC;AACrB,CAAC;AAED,SAAS,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,IAAc;IAC3D,IAAI,UAAU,CAAC,KAAK,CAAC,GAAG,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO;IAEzD,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,MAAM,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC;IACjE,yEAAyE;IACzE,gDAAgD;IAChD,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IACrE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAE9B,+BAA+B;IAC/B,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC,CAAC;IAErC,6EAA6E;IAC7E,kEAAkE;IAClE,IAAI,SAAS,EAAE,CAAC;QACd,SAAS,CAAC,kBAAkB,CAAC;YAC3B,KAAK;YACL,MAAM,EAAE,iBAAiB;YACzB,IAAI,EAAE,MAAM;SACb,CAAC,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;YACtB,wEAAwE;YACxE,sEAAsE;YACtE,qEAAqE;YACrE,oEAAoE;YACpE,sEAAsE;YACtE,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBACzB,mBAAmB,GAAG,IAAI,CAAC;gBAC3B,OAAO,CAAC,KAAK,CACX,IAAI,eAAe,EAAE,qDAAqD;oBAC1E,6CAA6C,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CACjE,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,KAAK,EAAE,CAAC,GAAW,EAAE,IAAc,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;IAC/D,IAAI,EAAE,CAAC,GAAW,EAAE,IAAc,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;IAC7D,IAAI,EAAE,CAAC,GAAW,EAAE,IAAc,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;IAC7D,KAAK,EAAE,CAAC,GAAW,EAAE,IAAc,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;CAChE,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Consulta la hora oficial de Chile contra el SHOA (Servicio Hidrográfico y
3
+ * Oceanográfico de la Armada), que es el organismo que la fija legalmente.
4
+ *
5
+ * ⚠ QUÉ PROBLEMA RESUELVE Y CUÁL NO: no dice nada sobre cómo interpretar las
6
+ * fechas ambiguas que entrega la API —de eso se encarga `utils/fechas.ts`—.
7
+ * Resuelve algo distinto y anterior: si el reloj de esta máquina está
8
+ * desviado, entonces `horas_restantes` y el puntaje de urgencia del radar
9
+ * están mal aunque la interpretación de la fecha sea perfecta, porque ambos
10
+ * se calculan restando la hora local. Un reloj adelantado media hora hace
11
+ * creer que un plazo está más cerca de lo que está; uno atrasado, lo
12
+ * contrario, y eso sí cuesta una licitación.
13
+ *
14
+ * Implementa lo mínimo de NTP (RFC 5905) sobre `dgram`, sin dependencias. El
15
+ * desfase se calcula con la fórmula del protocolo, que descuenta el viaje de
16
+ * ida y vuelta en vez de atribuirlo todo al reloj:
17
+ *
18
+ * desfase = ((T2 − T1) + (T3 − T4)) / 2
19
+ * demora = (T4 − T1) − (T3 − T2)
20
+ *
21
+ * T1 salida del cliente · T2 llegada al servidor
22
+ * T3 salida del servidor · T4 llegada al cliente
23
+ */
24
+ /** Servidor oficial de la hora en Chile. */
25
+ export declare const SERVIDOR_NTP_CHILE = "ntp.shoa.cl";
26
+ export interface ResultadoNtp {
27
+ ok: true;
28
+ servidor: string;
29
+ /** Diferencia del reloj local respecto del oficial, en ms. Positivo = local atrasado. */
30
+ desfaseMs: number;
31
+ /** Ida y vuelta de la consulta, en ms. Una demora alta resta precisión al desfase. */
32
+ demoraMs: number;
33
+ horaOficial: string;
34
+ horaLocal: string;
35
+ }
36
+ export interface FalloNtp {
37
+ ok: false;
38
+ servidor: string;
39
+ motivo: string;
40
+ }
41
+ /**
42
+ * Pregunta la hora al SHOA. Nunca lanza: un fallo de red se devuelve como
43
+ * `{ ok: false }` porque muchas redes corporativas bloquean el UDP 123, y eso
44
+ * no debe impedir que el servidor MCP funcione.
45
+ */
46
+ export declare function consultarHoraOficial(servidor?: string, timeoutMs?: number): Promise<ResultadoNtp | FalloNtp>;
47
+ /** Umbral a partir del cual el desfase puede alterar una decisión de plazo. */
48
+ export declare const DESFASE_PREOCUPANTE_MS = 60000;
49
+ /** Traduce el desfase a lo que significa para quien va a cotizar. */
50
+ export declare function interpretarDesfase(desfaseMs: number): string;
51
+ //# sourceMappingURL=ntp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ntp.d.ts","sourceRoot":"","sources":["../../src/utils/ntp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,4CAA4C;AAC5C,eAAO,MAAM,kBAAkB,gBAAgB,CAAC;AAKhD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,IAAI,CAAC;IACT,QAAQ,EAAE,MAAM,CAAC;IACjB,yFAAyF;IACzF,SAAS,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,KAAK,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AASD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,GAAE,MAA2B,EACrC,SAAS,SAAO,GACf,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAC,CAmElC;AAED,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C,qEAAqE;AACrE,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAY5D"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Consulta la hora oficial de Chile contra el SHOA (Servicio Hidrográfico y
3
+ * Oceanográfico de la Armada), que es el organismo que la fija legalmente.
4
+ *
5
+ * ⚠ QUÉ PROBLEMA RESUELVE Y CUÁL NO: no dice nada sobre cómo interpretar las
6
+ * fechas ambiguas que entrega la API —de eso se encarga `utils/fechas.ts`—.
7
+ * Resuelve algo distinto y anterior: si el reloj de esta máquina está
8
+ * desviado, entonces `horas_restantes` y el puntaje de urgencia del radar
9
+ * están mal aunque la interpretación de la fecha sea perfecta, porque ambos
10
+ * se calculan restando la hora local. Un reloj adelantado media hora hace
11
+ * creer que un plazo está más cerca de lo que está; uno atrasado, lo
12
+ * contrario, y eso sí cuesta una licitación.
13
+ *
14
+ * Implementa lo mínimo de NTP (RFC 5905) sobre `dgram`, sin dependencias. El
15
+ * desfase se calcula con la fórmula del protocolo, que descuenta el viaje de
16
+ * ida y vuelta en vez de atribuirlo todo al reloj:
17
+ *
18
+ * desfase = ((T2 − T1) + (T3 − T4)) / 2
19
+ * demora = (T4 − T1) − (T3 − T2)
20
+ *
21
+ * T1 salida del cliente · T2 llegada al servidor
22
+ * T3 salida del servidor · T4 llegada al cliente
23
+ */
24
+ import dgram from 'dgram';
25
+ /** Servidor oficial de la hora en Chile. */
26
+ export const SERVIDOR_NTP_CHILE = 'ntp.shoa.cl';
27
+ /** Segundos entre la época NTP (1900) y la de Unix (1970). */
28
+ const EPOCA_NTP_A_UNIX = 2_208_988_800;
29
+ /** Lee un timestamp NTP de 64 bits (32 de segundos + 32 de fracción) como ms Unix. */
30
+ function leerTimestamp(buf, offset) {
31
+ const segundos = buf.readUInt32BE(offset);
32
+ const fraccion = buf.readUInt32BE(offset + 4);
33
+ return (segundos - EPOCA_NTP_A_UNIX) * 1000 + Math.round((fraccion / 4_294_967_296) * 1000);
34
+ }
35
+ /**
36
+ * Pregunta la hora al SHOA. Nunca lanza: un fallo de red se devuelve como
37
+ * `{ ok: false }` porque muchas redes corporativas bloquean el UDP 123, y eso
38
+ * no debe impedir que el servidor MCP funcione.
39
+ */
40
+ export function consultarHoraOficial(servidor = SERVIDOR_NTP_CHILE, timeoutMs = 5000) {
41
+ return new Promise((resolve) => {
42
+ const socket = dgram.createSocket('udp4');
43
+ let resuelto = false;
44
+ const terminar = (r) => {
45
+ if (resuelto)
46
+ return;
47
+ resuelto = true;
48
+ try {
49
+ socket.close();
50
+ }
51
+ catch { /* ya cerrado */ }
52
+ resolve(r);
53
+ };
54
+ const temporizador = setTimeout(() => terminar({
55
+ ok: false,
56
+ servidor,
57
+ motivo: `El servidor no respondió en ${timeoutMs} ms. Muchas redes corporativas bloquean el puerto UDP 123, que es el que usa NTP.`,
58
+ }), timeoutMs);
59
+ socket.on('message', (msg) => {
60
+ clearTimeout(temporizador);
61
+ const t4 = Date.now();
62
+ if (msg.length < 48) {
63
+ return terminar({ ok: false, servidor, motivo: 'La respuesta no tiene el tamaño de un paquete NTP.' });
64
+ }
65
+ try {
66
+ const t1 = leerTimestamp(msg, 24); // Originate: el T1 que enviamos, devuelto por el servidor
67
+ const t2 = leerTimestamp(msg, 32); // Receive
68
+ const t3 = leerTimestamp(msg, 40); // Transmit
69
+ const desfaseMs = Math.round(((t2 - t1) + (t3 - t4)) / 2);
70
+ const demoraMs = Math.max(0, Math.round((t4 - t1) - (t3 - t2)));
71
+ terminar({
72
+ ok: true,
73
+ servidor,
74
+ desfaseMs,
75
+ demoraMs,
76
+ horaOficial: new Date(t4 + desfaseMs).toISOString(),
77
+ horaLocal: new Date(t4).toISOString(),
78
+ });
79
+ }
80
+ catch (e) {
81
+ terminar({ ok: false, servidor, motivo: `No se pudo leer la respuesta: ${String(e)}` });
82
+ }
83
+ });
84
+ socket.on('error', (e) => {
85
+ clearTimeout(temporizador);
86
+ terminar({ ok: false, servidor, motivo: `Error de red: ${e.message}` });
87
+ });
88
+ // Paquete de consulta: LI=0, VN=4, Mode=3 (cliente). El resto en cero.
89
+ const paquete = Buffer.alloc(48);
90
+ paquete[0] = 0x23;
91
+ // Se escribe T1 en Originate para que el servidor lo devuelva y el cálculo
92
+ // del desfase no dependa del reloj local más de lo imprescindible.
93
+ const t1 = Date.now();
94
+ paquete.writeUInt32BE(Math.floor(t1 / 1000) + EPOCA_NTP_A_UNIX, 40);
95
+ paquete.writeUInt32BE(Math.round(((t1 % 1000) / 1000) * 4_294_967_296), 44);
96
+ socket.send(paquete, 0, 48, 123, servidor, (e) => {
97
+ if (e) {
98
+ clearTimeout(temporizador);
99
+ terminar({ ok: false, servidor, motivo: `No se pudo enviar la consulta: ${e.message}` });
100
+ }
101
+ });
102
+ });
103
+ }
104
+ /** Umbral a partir del cual el desfase puede alterar una decisión de plazo. */
105
+ export const DESFASE_PREOCUPANTE_MS = 60_000;
106
+ /** Traduce el desfase a lo que significa para quien va a cotizar. */
107
+ export function interpretarDesfase(desfaseMs) {
108
+ const abs = Math.abs(desfaseMs);
109
+ if (abs < 1000)
110
+ return 'El reloj de esta máquina está sincronizado con la hora oficial de Chile.';
111
+ if (abs < DESFASE_PREOCUPANTE_MS) {
112
+ return `El reloj está desviado ${(abs / 1000).toFixed(1)} s respecto de la hora oficial. Es una diferencia irrelevante para calcular plazos de cierre.`;
113
+ }
114
+ const minutos = (abs / 60_000).toFixed(1);
115
+ const sentido = desfaseMs > 0 ? 'atrasado' : 'adelantado';
116
+ const consecuencia = desfaseMs > 0
117
+ ? 'las horas restantes que informa el radar son MAYORES que las reales, así que un plazo puede vencer antes de lo que parece'
118
+ : 'las horas restantes que informa el radar son MENORES que las reales';
119
+ return `⚠ El reloj de esta máquina está ${sentido} ${minutos} min respecto de la hora oficial de Chile. Como los plazos se calculan restando la hora local, ${consecuencia}. Conviene sincronizar el reloj del sistema antes de fiarse de "horas_restantes".`;
120
+ }
121
+ //# sourceMappingURL=ntp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ntp.js","sourceRoot":"","sources":["../../src/utils/ntp.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ssolis-ti/mcp-compra-agil",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "Servidor MCP para la API Compra Ágil v2 de Mercado Público (Chile)",
5
5
  "type": "module",
6
6
  "bin": {