@ssolis-ti/mcp-compra-agil 2.3.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.
- package/README.md +47 -13
- package/dist/api/compra-agil-client.d.ts +29 -0
- package/dist/api/compra-agil-client.d.ts.map +1 -1
- package/dist/api/compra-agil-client.js +35 -0
- package/dist/api/compra-agil-client.js.map +1 -1
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/reports/templates/radar-oportunidades.d.ts +1 -1
- package/dist/reports/templates/radar-oportunidades.js +2 -2
- package/dist/reports/templates/radar-oportunidades.js.map +1 -1
- package/dist/resources/glosario.d.ts.map +1 -1
- package/dist/resources/glosario.js +6 -2
- package/dist/resources/glosario.js.map +1 -1
- package/dist/services/monitor.js +11 -1
- package/dist/services/monitor.js.map +1 -1
- package/dist/tools/analizar-precios-mercado.d.ts.map +1 -1
- package/dist/tools/analizar-precios-mercado.js +68 -14
- package/dist/tools/analizar-precios-mercado.js.map +1 -1
- package/dist/tools/auditar-desiertas.d.ts.map +1 -1
- package/dist/tools/auditar-desiertas.js +31 -8
- package/dist/tools/auditar-desiertas.js.map +1 -1
- package/dist/tools/buscar-compras.d.ts.map +1 -1
- package/dist/tools/buscar-compras.js +2 -0
- package/dist/tools/buscar-compras.js.map +1 -1
- package/dist/tools/detalle-compra.d.ts.map +1 -1
- package/dist/tools/detalle-compra.js +2 -0
- package/dist/tools/detalle-compra.js.map +1 -1
- package/dist/tools/detalle-oc.d.ts.map +1 -1
- package/dist/tools/detalle-oc.js +2 -0
- package/dist/tools/detalle-oc.js.map +1 -1
- package/dist/tools/documentos.d.ts.map +1 -1
- package/dist/tools/documentos.js +6 -0
- package/dist/tools/documentos.js.map +1 -1
- package/dist/tools/estadisticas-uso.d.ts.map +1 -1
- package/dist/tools/estadisticas-uso.js +2 -0
- package/dist/tools/estadisticas-uso.js.map +1 -1
- package/dist/tools/generar-borrador.d.ts +1 -1
- package/dist/tools/generar-borrador.d.ts.map +1 -1
- package/dist/tools/generar-borrador.js +35 -18
- package/dist/tools/generar-borrador.js.map +1 -1
- package/dist/tools/generar-informe.d.ts.map +1 -1
- package/dist/tools/generar-informe.js +5 -1
- package/dist/tools/generar-informe.js.map +1 -1
- package/dist/tools/monitorear-cambios.d.ts.map +1 -1
- package/dist/tools/monitorear-cambios.js +2 -0
- package/dist/tools/monitorear-cambios.js.map +1 -1
- package/dist/tools/radar-oportunidades.d.ts +9 -1
- package/dist/tools/radar-oportunidades.d.ts.map +1 -1
- package/dist/tools/radar-oportunidades.js +42 -6
- package/dist/tools/radar-oportunidades.js.map +1 -1
- package/dist/tools/verificar-hora.d.ts +13 -0
- package/dist/tools/verificar-hora.d.ts.map +1 -0
- package/dist/tools/verificar-hora.js +71 -0
- package/dist/tools/verificar-hora.js.map +1 -0
- package/dist/tools/verificar-oc.d.ts.map +1 -1
- package/dist/tools/verificar-oc.js +2 -0
- package/dist/tools/verificar-oc.js.map +1 -1
- package/dist/tools/verificar-ticket.js +1 -1
- package/dist/tools/verificar-ticket.js.map +1 -1
- package/dist/utils/concurrencia.d.ts +63 -0
- package/dist/utils/concurrencia.d.ts.map +1 -0
- package/dist/utils/concurrencia.js +122 -0
- package/dist/utils/concurrencia.js.map +1 -0
- package/dist/utils/error-handler.d.ts.map +1 -1
- package/dist/utils/error-handler.js +8 -0
- package/dist/utils/error-handler.js.map +1 -1
- package/dist/utils/fechas.d.ts +50 -0
- package/dist/utils/fechas.d.ts.map +1 -0
- package/dist/utils/fechas.js +77 -0
- package/dist/utils/fechas.js.map +1 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +13 -2
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/ntp.d.ts +51 -0
- package/dist/utils/ntp.d.ts.map +1 -0
- package/dist/utils/ntp.js +121 -0
- package/dist/utils/ntp.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Límite de concurrencia adaptativo para las consultas a la API.
|
|
3
|
+
*
|
|
4
|
+
* ⚠ POR QUÉ EXISTE: las herramientas de análisis piden varios detalles a la vez
|
|
5
|
+
* —en serie tardaban más de 105 s y ningún cliente MCP espera tanto—, pero un
|
|
6
|
+
* paralelismo fijo es igual de ingenuo en el otro sentido. Medido en
|
|
7
|
+
* producción el 8 de septiembre de 2026, el endpoint de detalle tardaba 20-25 s
|
|
8
|
+
* y devolvía HTTP 504 en 1 de cada 3 consultas: cuando el servicio está así,
|
|
9
|
+
* insistir con la misma tanda solo produce más 504 y gasta cuota en llamadas
|
|
10
|
+
* condenadas.
|
|
11
|
+
*
|
|
12
|
+
* La política es la de control de congestión de TCP (AIMD): ante una señal de
|
|
13
|
+
* saturación se baja a la mitad de inmediato, y se sube de a uno solo cuando
|
|
14
|
+
* una tanda completa sale limpia. Bajar rápido y subir despacio es lo correcto
|
|
15
|
+
* aquí porque el costo de los errores es asimétrico — un 504 desperdicia 30
|
|
16
|
+
* segundos de espera, mientras que ir un poco lento solo cuesta unos segundos.
|
|
17
|
+
*
|
|
18
|
+
* Lo que NO cuenta como congestión: 400, 404 y demás errores por consulta.
|
|
19
|
+
* Esos hablan del ítem pedido, no del estado del servicio, y reaccionar a
|
|
20
|
+
* ellos bajaría el paralelismo por motivos equivocados. El 429 tampoco: de la
|
|
21
|
+
* cuota se encarga el RateLimiter, que sabe esperar el `Retry-After`.
|
|
22
|
+
*/
|
|
23
|
+
import { logger } from './logger.js';
|
|
24
|
+
/** Códigos que indican que la pasarela no dio abasto, no que la consulta fuera inválida. */
|
|
25
|
+
const CODIGOS_DE_CONGESTION = new Set([502, 503, 504]);
|
|
26
|
+
/** ¿El fallo habla del servicio saturado o del ítem que se pidió? */
|
|
27
|
+
export function esSenalDeCongestion(error) {
|
|
28
|
+
const e = error;
|
|
29
|
+
if (!e)
|
|
30
|
+
return false;
|
|
31
|
+
if (typeof e.httpStatus === 'number' && CODIGOS_DE_CONGESTION.has(e.httpStatus))
|
|
32
|
+
return true;
|
|
33
|
+
// Los cortes de red y timeouts cuentan igual: el servicio no alcanzó a responder.
|
|
34
|
+
return /timeout|ETIMEDOUT|ECONNRESET|socket hang up|fetch failed/i.test(String(e.message ?? ''));
|
|
35
|
+
}
|
|
36
|
+
export class LimitadorConcurrencia {
|
|
37
|
+
limite;
|
|
38
|
+
minimo;
|
|
39
|
+
maximo;
|
|
40
|
+
tandas = 0;
|
|
41
|
+
congestiones = 0;
|
|
42
|
+
constructor(opciones = {}) {
|
|
43
|
+
this.maximo = Math.max(1, opciones.maximo ?? 5);
|
|
44
|
+
this.minimo = Math.max(1, Math.min(opciones.minimo ?? 1, this.maximo));
|
|
45
|
+
this.limite = Math.min(this.maximo, Math.max(this.minimo, opciones.inicial ?? this.maximo));
|
|
46
|
+
}
|
|
47
|
+
get limiteActual() {
|
|
48
|
+
return this.limite;
|
|
49
|
+
}
|
|
50
|
+
estadisticas() {
|
|
51
|
+
return {
|
|
52
|
+
limiteActual: this.limite,
|
|
53
|
+
minimo: this.minimo,
|
|
54
|
+
maximo: this.maximo,
|
|
55
|
+
tandas: this.tandas,
|
|
56
|
+
congestiones: this.congestiones,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Disminución multiplicativa: a la mitad, en cuanto aparece la primera señal. */
|
|
60
|
+
reducir() {
|
|
61
|
+
const antes = this.limite;
|
|
62
|
+
this.limite = Math.max(this.minimo, Math.floor(this.limite / 2));
|
|
63
|
+
this.congestiones++;
|
|
64
|
+
if (this.limite !== antes) {
|
|
65
|
+
logger.warn(`Concurrencia: señal de saturación, se baja de ${antes} a ${this.limite} consultas simultáneas.`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Aumento aditivo: de a uno, y solo tras una tanda entera sin congestión. */
|
|
69
|
+
aumentar() {
|
|
70
|
+
if (this.limite >= this.maximo)
|
|
71
|
+
return;
|
|
72
|
+
this.limite++;
|
|
73
|
+
logger.debug(`Concurrencia: tanda limpia, se sube a ${this.limite} consultas simultáneas.`);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Ejecuta las tareas respetando el límite vigente, que puede bajar A MITAD DE
|
|
77
|
+
* TANDA: si las primeras consultas ya vienen con 504, las que quedan salen con
|
|
78
|
+
* menos paralelismo en vez de repetir el error en bloque.
|
|
79
|
+
*
|
|
80
|
+
* Devuelve un arreglo del mismo largo y en el mismo orden que `tareas`, con
|
|
81
|
+
* `null` en las que fallaron. Nunca lanza: quien llama distingue el fallo por
|
|
82
|
+
* el `null`, que es lo que las herramientas de análisis ya esperan.
|
|
83
|
+
*/
|
|
84
|
+
async ejecutar(tareas) {
|
|
85
|
+
const resultados = new Array(tareas.length).fill(null);
|
|
86
|
+
if (tareas.length === 0)
|
|
87
|
+
return resultados;
|
|
88
|
+
this.tandas++;
|
|
89
|
+
let huboCongestion = false;
|
|
90
|
+
let siguiente = 0;
|
|
91
|
+
let enVuelo = 0;
|
|
92
|
+
await new Promise((resolve) => {
|
|
93
|
+
const lanzar = () => {
|
|
94
|
+
while (enVuelo < this.limite && siguiente < tareas.length) {
|
|
95
|
+
const i = siguiente++;
|
|
96
|
+
enVuelo++;
|
|
97
|
+
tareas[i]()
|
|
98
|
+
.then((valor) => { resultados[i] = valor; })
|
|
99
|
+
.catch((e) => {
|
|
100
|
+
resultados[i] = null;
|
|
101
|
+
if (esSenalDeCongestion(e)) {
|
|
102
|
+
huboCongestion = true;
|
|
103
|
+
this.reducir(); // efecto inmediato sobre lo que queda por lanzar
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
.finally(() => {
|
|
107
|
+
enVuelo--;
|
|
108
|
+
if (siguiente >= tareas.length && enVuelo === 0)
|
|
109
|
+
resolve();
|
|
110
|
+
else
|
|
111
|
+
lanzar();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
lanzar();
|
|
116
|
+
});
|
|
117
|
+
if (!huboCongestion)
|
|
118
|
+
this.aumentar();
|
|
119
|
+
return resultados;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=concurrencia.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrencia.js","sourceRoot":"","sources":["../../src/utils/concurrencia.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../src/utils/error-handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,QAAQ,EAAE,CAAC;CACpB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtC,SAAgB,iBAAiB,EAAE,MAAM,CAAC;gBAE9B,UAAU,EAAE,MAAM,EAAE,SAAS,GAAE,QAAQ,EAAO;CAQ3D;
|
|
1
|
+
{"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../src/utils/error-handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,QAAQ,EAAE,CAAC;CACpB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtC,SAAgB,iBAAiB,EAAE,MAAM,CAAC;gBAE9B,UAAU,EAAE,MAAM,EAAE,SAAS,GAAE,QAAQ,EAAO;CAQ3D;AA4CD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAoB5E"}
|
|
@@ -39,6 +39,14 @@ function getActionableMessage(httpStatus, apiErrors) {
|
|
|
39
39
|
return `Error interno del servidor de Mercado Público (api2.mercadopublico.cl). Esto no es un problema de tu consulta. Reintenta en unos minutos.${detail}`;
|
|
40
40
|
case 503:
|
|
41
41
|
return `El servicio de Mercado Público está temporalmente no disponible (posible mantenimiento). Reintenta más tarde.${detail}`;
|
|
42
|
+
// 502 y 504 no están en la tabla de errores de la guía oficial, pero la API
|
|
43
|
+
// los devuelve: se observó un 504 sistemático (septiembre 2026) al pedir
|
|
44
|
+
// tamano_pagina=50 sobre `estado=desierta` con búsqueda de texto — la
|
|
45
|
+
// pasarela corta a los ~30 s. Sin este caso caían en "Error inesperado", que
|
|
46
|
+
// hacía parecer un fallo del cliente lo que es una lentitud del servicio.
|
|
47
|
+
case 502:
|
|
48
|
+
case 504:
|
|
49
|
+
return `La pasarela de Mercado Público cortó la conexión antes de que la API respondiera (HTTP ${httpStatus}): la consulta tardó demasiado, no es un error de tus parámetros. Reintenta, y si se repite reduce el trabajo por consulta — un 'tamano_pagina' más chico, menos 'limite_analisis' o un filtro más específico. Las búsquedas por texto sobre 'estado=desierta' son las más lentas.${detail}`;
|
|
42
50
|
default:
|
|
43
51
|
return `Error inesperado HTTP ${httpStatus} de la API de Compra Ágil.${detail}`;
|
|
44
52
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/utils/error-handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAerC,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3B,UAAU,CAAS;IACnB,SAAS,CAAa;IACtB,iBAAiB,CAAS;IAE1C,YAAY,UAAkB,EAAE,YAAwB,EAAE;QACxD,MAAM,UAAU,GAAG,oBAAoB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QAC/D,KAAK,CAAC,UAAU,CAAC,CAAC;QAClB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,iBAAiB,GAAG,UAAU,CAAC;IACtC,CAAC;CACF;AAED,SAAS,oBAAoB,CAAC,UAAkB,EAAE,SAAqB;IACrE,6EAA6E;IAC7E,gFAAgF;IAChF,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC;QACjC,CAAC,CAAC,wBAAwB,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,GAAG;QAC/D,CAAC,CAAC,EAAE,CAAC;IAEP,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,GAAG;YACN,OAAO,qLAAqL,MAAM,EAAE,CAAC;QACvM,KAAK,GAAG;YACN,OAAO,2JAA2J,MAAM,EAAE,CAAC;QAC7K,KAAK,GAAG;YACN,OAAO,gLAAgL,MAAM,EAAE,CAAC;QAClM,KAAK,GAAG;YACN,OAAO,mKAAmK,MAAM,EAAE,CAAC;QACrL,KAAK,GAAG;YACN,OAAO,sBAAsB,CAAC,MAAM,CAAC,CAAC;QACxC,KAAK,GAAG;YACN,OAAO,4IAA4I,MAAM,EAAE,CAAC;QAC9J,KAAK,GAAG;YACN,OAAO,gHAAgH,MAAM,EAAE,CAAC;QAClI;YACE,OAAO,yBAAyB,UAAU,6BAA6B,MAAM,EAAE,CAAC;IACpF,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB,CAAC,MAAc;IAC5C,6EAA6E;IAC7E,4EAA4E;IAC5E,sEAAsE;IACtE,OAAO,0jBAA0jB,MAAM,EAAE,CAAC;AAC5kB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,QAAkB;IACxD,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA6C,CAAC;QAC9E,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;YACnC,MAAM,IAAI,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAG,IAAoC,CAAC,MAAM,CAAC,CAAC;QAC9F,CAAC;QACD,OAAO,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACvF,CAAC;IAED,IAAI,SAAS,GAAe,EAAE,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAsB,CAAC;QAC5D,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;YACrB,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC;QAC/B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,4DAA4D;IAC9D,CAAC;IAED,MAAM,IAAI,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAC3D,CAAC"}
|
|
1
|
+
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/utils/error-handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAerC,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3B,UAAU,CAAS;IACnB,SAAS,CAAa;IACtB,iBAAiB,CAAS;IAE1C,YAAY,UAAkB,EAAE,YAAwB,EAAE;QACxD,MAAM,UAAU,GAAG,oBAAoB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QAC/D,KAAK,CAAC,UAAU,CAAC,CAAC;QAClB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,iBAAiB,GAAG,UAAU,CAAC;IACtC,CAAC;CACF;AAED,SAAS,oBAAoB,CAAC,UAAkB,EAAE,SAAqB;IACrE,6EAA6E;IAC7E,gFAAgF;IAChF,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC;QACjC,CAAC,CAAC,wBAAwB,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,GAAG;QAC/D,CAAC,CAAC,EAAE,CAAC;IAEP,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,GAAG;YACN,OAAO,qLAAqL,MAAM,EAAE,CAAC;QACvM,KAAK,GAAG;YACN,OAAO,2JAA2J,MAAM,EAAE,CAAC;QAC7K,KAAK,GAAG;YACN,OAAO,gLAAgL,MAAM,EAAE,CAAC;QAClM,KAAK,GAAG;YACN,OAAO,mKAAmK,MAAM,EAAE,CAAC;QACrL,KAAK,GAAG;YACN,OAAO,sBAAsB,CAAC,MAAM,CAAC,CAAC;QACxC,KAAK,GAAG;YACN,OAAO,4IAA4I,MAAM,EAAE,CAAC;QAC9J,KAAK,GAAG;YACN,OAAO,gHAAgH,MAAM,EAAE,CAAC;QAClI,4EAA4E;QAC5E,yEAAyE;QACzE,sEAAsE;QACtE,6EAA6E;QAC7E,0EAA0E;QAC1E,KAAK,GAAG,CAAC;QACT,KAAK,GAAG;YACN,OAAO,0FAA0F,UAAU,qRAAqR,MAAM,EAAE,CAAC;QAC3Y;YACE,OAAO,yBAAyB,UAAU,6BAA6B,MAAM,EAAE,CAAC;IACpF,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB,CAAC,MAAc;IAC5C,6EAA6E;IAC7E,4EAA4E;IAC5E,sEAAsE;IACtE,OAAO,0jBAA0jB,MAAM,EAAE,CAAC;AAC5kB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,QAAkB;IACxD,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA6C,CAAC;QAC9E,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;YACnC,MAAM,IAAI,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAG,IAAoC,CAAC,MAAM,CAAC,CAAC;QAC9F,CAAC;QACD,OAAO,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACvF,CAAC;IAED,IAAI,SAAS,GAAe,EAAE,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAsB,CAAC;QAC5D,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;YACrB,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC;QAC/B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,4DAA4D;IAC9D,CAAC;IAED,MAAM,IAAI,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interpretación de las fechas que entrega la API.
|
|
3
|
+
*
|
|
4
|
+
* ⚠ EL PROBLEMA: la API mezcla dos formatos para el mismo instante.
|
|
5
|
+
*
|
|
6
|
+
* fechas.fecha_cierre "2026-09-11 12:00" ← sin zona
|
|
7
|
+
* convocatoria.fecha_cierre_primer_llamado "2026-09-11T12:00:00Z" ← UTC
|
|
8
|
+
*
|
|
9
|
+
* Verificado en 8 de 8 procesos: el valor es idéntico, pero solo uno declara
|
|
10
|
+
* su zona horaria. Los campos sin marca son justamente los que un proveedor
|
|
11
|
+
* mira para saber cuándo cierra un llamado.
|
|
12
|
+
*
|
|
13
|
+
* ⚠ POR QUÉ NO BASTA CON `new Date()`: ante un string sin zona y con espacio en
|
|
14
|
+
* vez de "T", V8 lo interpreta en la zona horaria DEL SERVIDOR. Medido: el
|
|
15
|
+
* mismo "2026-09-11 12:00" se convierte en 15:00Z si el proceso corre en
|
|
16
|
+
* Chile y en 12:00Z si corre en UTC. Es decir, el radar calculaba
|
|
17
|
+
* `horas_restantes` —y con ello su puntaje de urgencia— con tres horas de
|
|
18
|
+
* diferencia según dónde estuviera desplegado, con los mismos datos.
|
|
19
|
+
*
|
|
20
|
+
* ⚠ POR QUÉ SE ASUME UTC: no se pudo determinar con certeza si esos valores son
|
|
21
|
+
* UTC u hora de Chile. La evidencia está dividida — el filtro `ttl_cambio_ms`
|
|
22
|
+
* de la API trata las marcas `Z` como UTC real, pero la ficha del portal
|
|
23
|
+
* muestra ese mismo "12:00" a usuarios chilenos sin convertir.
|
|
24
|
+
*
|
|
25
|
+
* Ante la duda se elige UTC porque el error es asimétrico. Si el valor fuera
|
|
26
|
+
* hora de Chile y lo leemos como UTC, mostramos el cierre TRES HORAS ANTES de
|
|
27
|
+
* lo real: el proveedor se apura de más. Al revés —leerlo como hora local
|
|
28
|
+
* siendo UTC— le haría creer que tiene tres horas extra y perdería el plazo.
|
|
29
|
+
* Entre apurarse de más y llegar tarde a una licitación, la elección es clara.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Convierte una fecha de la API a un instante absoluto, sin depender de la zona
|
|
33
|
+
* horaria del servidor. Devuelve `null` si el valor falta o no se entiende, para
|
|
34
|
+
* que quien llame distinga "no hay fecha" de "medianoche".
|
|
35
|
+
*/
|
|
36
|
+
export declare function parsearFechaApi(valor: string | null | undefined): Date | null;
|
|
37
|
+
/** ¿El valor viene sin declarar su zona horaria? Entonces se asumió UTC. */
|
|
38
|
+
export declare function esFechaAmbigua(valor: string | null | undefined): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Texto que acompaña a una fecha ambigua para que ni el modelo ni la persona
|
|
41
|
+
* asuman hora local. Va en la salida de las herramientas, no en los logs:
|
|
42
|
+
* quien decide si alcanza a cotizar necesita verlo.
|
|
43
|
+
*/
|
|
44
|
+
export declare const NOTA_ZONA_HORARIA: string;
|
|
45
|
+
/**
|
|
46
|
+
* Misma fecha, expresada también en hora de Chile, para que el usuario no tenga
|
|
47
|
+
* que hacer la resta mental. Devuelve `null` si la fecha no se pudo interpretar.
|
|
48
|
+
*/
|
|
49
|
+
export declare function enHoraDeChile(valor: string | null | undefined): string | null;
|
|
50
|
+
//# sourceMappingURL=fechas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fechas.d.ts","sourceRoot":"","sources":["../../src/utils/fechas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAKH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,CAgB7E;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAExE;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,QAG4E,CAAC;AAE3G;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAK7E"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interpretación de las fechas que entrega la API.
|
|
3
|
+
*
|
|
4
|
+
* ⚠ EL PROBLEMA: la API mezcla dos formatos para el mismo instante.
|
|
5
|
+
*
|
|
6
|
+
* fechas.fecha_cierre "2026-09-11 12:00" ← sin zona
|
|
7
|
+
* convocatoria.fecha_cierre_primer_llamado "2026-09-11T12:00:00Z" ← UTC
|
|
8
|
+
*
|
|
9
|
+
* Verificado en 8 de 8 procesos: el valor es idéntico, pero solo uno declara
|
|
10
|
+
* su zona horaria. Los campos sin marca son justamente los que un proveedor
|
|
11
|
+
* mira para saber cuándo cierra un llamado.
|
|
12
|
+
*
|
|
13
|
+
* ⚠ POR QUÉ NO BASTA CON `new Date()`: ante un string sin zona y con espacio en
|
|
14
|
+
* vez de "T", V8 lo interpreta en la zona horaria DEL SERVIDOR. Medido: el
|
|
15
|
+
* mismo "2026-09-11 12:00" se convierte en 15:00Z si el proceso corre en
|
|
16
|
+
* Chile y en 12:00Z si corre en UTC. Es decir, el radar calculaba
|
|
17
|
+
* `horas_restantes` —y con ello su puntaje de urgencia— con tres horas de
|
|
18
|
+
* diferencia según dónde estuviera desplegado, con los mismos datos.
|
|
19
|
+
*
|
|
20
|
+
* ⚠ POR QUÉ SE ASUME UTC: no se pudo determinar con certeza si esos valores son
|
|
21
|
+
* UTC u hora de Chile. La evidencia está dividida — el filtro `ttl_cambio_ms`
|
|
22
|
+
* de la API trata las marcas `Z` como UTC real, pero la ficha del portal
|
|
23
|
+
* muestra ese mismo "12:00" a usuarios chilenos sin convertir.
|
|
24
|
+
*
|
|
25
|
+
* Ante la duda se elige UTC porque el error es asimétrico. Si el valor fuera
|
|
26
|
+
* hora de Chile y lo leemos como UTC, mostramos el cierre TRES HORAS ANTES de
|
|
27
|
+
* lo real: el proveedor se apura de más. Al revés —leerlo como hora local
|
|
28
|
+
* siendo UTC— le haría creer que tiene tres horas extra y perdería el plazo.
|
|
29
|
+
* Entre apurarse de más y llegar tarde a una licitación, la elección es clara.
|
|
30
|
+
*/
|
|
31
|
+
/** Formato sin zona horaria que devuelve la API: "2026-09-11 12:00". */
|
|
32
|
+
const SIN_ZONA = /^(\d{4}-\d{2}-\d{2})[ T](\d{2}:\d{2})(:(\d{2}))?$/;
|
|
33
|
+
/**
|
|
34
|
+
* Convierte una fecha de la API a un instante absoluto, sin depender de la zona
|
|
35
|
+
* horaria del servidor. Devuelve `null` si el valor falta o no se entiende, para
|
|
36
|
+
* que quien llame distinga "no hay fecha" de "medianoche".
|
|
37
|
+
*/
|
|
38
|
+
export function parsearFechaApi(valor) {
|
|
39
|
+
if (!valor)
|
|
40
|
+
return null;
|
|
41
|
+
const texto = String(valor).trim();
|
|
42
|
+
const m = SIN_ZONA.exec(texto);
|
|
43
|
+
if (m) {
|
|
44
|
+
// Se le añade la Z que la API omitió: así el resultado es el mismo en
|
|
45
|
+
// cualquier servidor, que es la mitad del problema que esto resuelve.
|
|
46
|
+
const segundos = m[4] ?? '00';
|
|
47
|
+
const d = new Date(`${m[1]}T${m[2]}:${segundos}Z`);
|
|
48
|
+
return isNaN(d.getTime()) ? null : d;
|
|
49
|
+
}
|
|
50
|
+
// Ya trae zona horaria (Z u offset): se respeta lo que dice.
|
|
51
|
+
const d = new Date(texto);
|
|
52
|
+
return isNaN(d.getTime()) ? null : d;
|
|
53
|
+
}
|
|
54
|
+
/** ¿El valor viene sin declarar su zona horaria? Entonces se asumió UTC. */
|
|
55
|
+
export function esFechaAmbigua(valor) {
|
|
56
|
+
return Boolean(valor) && SIN_ZONA.test(String(valor).trim());
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Texto que acompaña a una fecha ambigua para que ni el modelo ni la persona
|
|
60
|
+
* asuman hora local. Va en la salida de las herramientas, no en los logs:
|
|
61
|
+
* quien decide si alcanza a cotizar necesita verlo.
|
|
62
|
+
*/
|
|
63
|
+
export const NOTA_ZONA_HORARIA = 'Las horas provienen de la API sin declarar zona horaria y se interpretan como UTC. ' +
|
|
64
|
+
'En Chile continental (UTC-3) resta 3 horas: un cierre a las "12:00" corresponde a las 09:00 locales. ' +
|
|
65
|
+
'Como la API no lo especifica, confirma el plazo exacto en la ficha del proceso antes de comprometerte.';
|
|
66
|
+
/**
|
|
67
|
+
* Misma fecha, expresada también en hora de Chile, para que el usuario no tenga
|
|
68
|
+
* que hacer la resta mental. Devuelve `null` si la fecha no se pudo interpretar.
|
|
69
|
+
*/
|
|
70
|
+
export function enHoraDeChile(valor) {
|
|
71
|
+
const d = parsearFechaApi(valor);
|
|
72
|
+
if (!d)
|
|
73
|
+
return null;
|
|
74
|
+
// sv-SE da el formato ISO "YYYY-MM-DD HH:MM", legible y sin ambigüedad.
|
|
75
|
+
return d.toLocaleString('sv-SE', { timeZone: 'America/Santiago' }).slice(0, 16);
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=fechas.js.map
|
|
@@ -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;
|
|
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"}
|
package/dist/utils/logger.js
CHANGED
|
@@ -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
|
-
//
|
|
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
|
}
|
package/dist/utils/logger.js.map
CHANGED
|
@@ -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;
|
|
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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"}
|