@ssolis-ti/mcp-compra-agil 1.0.2 → 2.1.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 (110) hide show
  1. package/README.md +113 -25
  2. package/dist/api/compra-agil-client.d.ts +2 -1
  3. package/dist/api/compra-agil-client.d.ts.map +1 -1
  4. package/dist/api/compra-agil-client.js +12 -2
  5. package/dist/api/compra-agil-client.js.map +1 -1
  6. package/dist/index.js +31 -7
  7. package/dist/index.js.map +1 -1
  8. package/dist/reports/components.d.ts +59 -0
  9. package/dist/reports/components.d.ts.map +1 -0
  10. package/dist/reports/components.js +125 -0
  11. package/dist/reports/components.js.map +1 -0
  12. package/dist/reports/export.d.ts +27 -0
  13. package/dist/reports/export.d.ts.map +1 -0
  14. package/dist/reports/export.js +47 -0
  15. package/dist/reports/export.js.map +1 -0
  16. package/dist/reports/format.d.ts +24 -0
  17. package/dist/reports/format.d.ts.map +1 -0
  18. package/dist/reports/format.js +74 -0
  19. package/dist/reports/format.js.map +1 -0
  20. package/dist/reports/render.d.ts +18 -0
  21. package/dist/reports/render.d.ts.map +1 -0
  22. package/dist/reports/render.js +30 -0
  23. package/dist/reports/render.js.map +1 -0
  24. package/dist/reports/templates/radar-oportunidades.d.ts +41 -0
  25. package/dist/reports/templates/radar-oportunidades.d.ts.map +1 -0
  26. package/dist/reports/templates/radar-oportunidades.js +170 -0
  27. package/dist/reports/templates/radar-oportunidades.js.map +1 -0
  28. package/dist/reports/theme.d.ts +83 -0
  29. package/dist/reports/theme.d.ts.map +1 -0
  30. package/dist/reports/theme.js +284 -0
  31. package/dist/reports/theme.js.map +1 -0
  32. package/dist/resources/compras-template.d.ts.map +1 -1
  33. package/dist/resources/compras-template.js +2 -1
  34. package/dist/resources/compras-template.js.map +1 -1
  35. package/dist/resources/documentacion.d.ts.map +1 -1
  36. package/dist/resources/documentacion.js +5 -36
  37. package/dist/resources/documentacion.js.map +1 -1
  38. package/dist/resources/estados.d.ts.map +1 -1
  39. package/dist/resources/estados.js +24 -10
  40. package/dist/resources/estados.js.map +1 -1
  41. package/dist/services/monitor.js +37 -2
  42. package/dist/services/monitor.js.map +1 -1
  43. package/dist/tools/analizar-precios-mercado.d.ts +21 -0
  44. package/dist/tools/analizar-precios-mercado.d.ts.map +1 -0
  45. package/dist/tools/analizar-precios-mercado.js +217 -0
  46. package/dist/tools/analizar-precios-mercado.js.map +1 -0
  47. package/dist/tools/auditar-desiertas.d.ts.map +1 -1
  48. package/dist/tools/auditar-desiertas.js +64 -46
  49. package/dist/tools/auditar-desiertas.js.map +1 -1
  50. package/dist/tools/buscar-compras.d.ts.map +1 -1
  51. package/dist/tools/buscar-compras.js +2 -1
  52. package/dist/tools/buscar-compras.js.map +1 -1
  53. package/dist/tools/detalle-compra.d.ts.map +1 -1
  54. package/dist/tools/detalle-compra.js +6 -5
  55. package/dist/tools/detalle-compra.js.map +1 -1
  56. package/dist/tools/detalle-oc.d.ts.map +1 -1
  57. package/dist/tools/detalle-oc.js +6 -5
  58. package/dist/tools/detalle-oc.js.map +1 -1
  59. package/dist/tools/documentos.d.ts.map +1 -1
  60. package/dist/tools/documentos.js +51 -50
  61. package/dist/tools/documentos.js.map +1 -1
  62. package/dist/tools/generar-borrador.d.ts.map +1 -1
  63. package/dist/tools/generar-borrador.js +37 -26
  64. package/dist/tools/generar-borrador.js.map +1 -1
  65. package/dist/tools/generar-informe.d.ts +13 -0
  66. package/dist/tools/generar-informe.d.ts.map +1 -0
  67. package/dist/tools/generar-informe.js +111 -0
  68. package/dist/tools/generar-informe.js.map +1 -0
  69. package/dist/tools/monitorear-cambios.d.ts +32 -2
  70. package/dist/tools/monitorear-cambios.d.ts.map +1 -1
  71. package/dist/tools/monitorear-cambios.js +79 -10
  72. package/dist/tools/monitorear-cambios.js.map +1 -1
  73. package/dist/tools/radar-oportunidades.d.ts +42 -1
  74. package/dist/tools/radar-oportunidades.d.ts.map +1 -1
  75. package/dist/tools/radar-oportunidades.js +123 -103
  76. package/dist/tools/radar-oportunidades.js.map +1 -1
  77. package/dist/tools/recomendar-precio.d.ts +13 -0
  78. package/dist/tools/recomendar-precio.d.ts.map +1 -1
  79. package/dist/tools/recomendar-precio.js +54 -58
  80. package/dist/tools/recomendar-precio.js.map +1 -1
  81. package/dist/tools/verificar-oc.d.ts.map +1 -1
  82. package/dist/tools/verificar-oc.js +9 -7
  83. package/dist/tools/verificar-oc.js.map +1 -1
  84. package/dist/tools/verificar-ticket.d.ts +13 -0
  85. package/dist/tools/verificar-ticket.d.ts.map +1 -0
  86. package/dist/tools/verificar-ticket.js +79 -0
  87. package/dist/tools/verificar-ticket.js.map +1 -0
  88. package/dist/utils/docs-locator.d.ts +41 -0
  89. package/dist/utils/docs-locator.d.ts.map +1 -0
  90. package/dist/utils/docs-locator.js +82 -0
  91. package/dist/utils/docs-locator.js.map +1 -0
  92. package/dist/utils/error-handler.d.ts.map +1 -1
  93. package/dist/utils/error-handler.js +7 -4
  94. package/dist/utils/error-handler.js.map +1 -1
  95. package/dist/utils/logger.d.ts.map +1 -1
  96. package/dist/utils/logger.js +9 -6
  97. package/dist/utils/logger.js.map +1 -1
  98. package/dist/utils/quotation.d.ts +71 -0
  99. package/dist/utils/quotation.d.ts.map +1 -0
  100. package/dist/utils/quotation.js +140 -0
  101. package/dist/utils/quotation.js.map +1 -0
  102. package/dist/utils/rate-limiter.d.ts +8 -1
  103. package/dist/utils/rate-limiter.d.ts.map +1 -1
  104. package/dist/utils/rate-limiter.js +25 -1
  105. package/dist/utils/rate-limiter.js.map +1 -1
  106. package/dist/utils/redact.d.ts +48 -0
  107. package/dist/utils/redact.d.ts.map +1 -0
  108. package/dist/utils/redact.js +100 -0
  109. package/dist/utils/redact.js.map +1 -0
  110. package/package.json +55 -53
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Componentes de informe: funciones puras que emiten fragmentos de HTML.
3
+ *
4
+ * REGLA DE SEGURIDAD: todo dato que provenga de la API (nombres de organismos,
5
+ * razones sociales, descripciones) DEBE pasar por `esc()`. Un nombre con "&" o
6
+ * "<" rompería el documento — y en un HTML que el usuario abre en su navegador,
7
+ * inyectar marcado no escapado es una vulnerabilidad, no solo un bug visual.
8
+ */
9
+ import { TOKENS } from './theme.js';
10
+ /** Escapa texto para interpolación segura en HTML. */
11
+ export function esc(valor) {
12
+ if (valor === null || valor === undefined)
13
+ return '';
14
+ return String(valor)
15
+ .replace(/&/g, '&amp;')
16
+ .replace(/</g, '&lt;')
17
+ .replace(/>/g, '&gt;')
18
+ .replace(/"/g, '&quot;')
19
+ .replace(/'/g, '&#39;');
20
+ }
21
+ export function portada(o) {
22
+ const meta = (o.meta ?? [])
23
+ .map((m) => `<span>${esc(m.label)}: <b>${esc(m.valor)}</b></span>`)
24
+ .join('');
25
+ return `
26
+ <header class="portada">
27
+ <div class="eyebrow">${esc(o.eyebrow)}</div>
28
+ <h1 class="titulo">${esc(o.titulo)}</h1>
29
+ ${o.subtitulo ? `<p class="subtitulo">${esc(o.subtitulo)}</p>` : ''}
30
+ ${meta ? `<div class="meta">${meta}</div>` : ''}
31
+ </header>`.trim();
32
+ }
33
+ export function kpiRow(kpis) {
34
+ const items = kpis
35
+ .map((k) => `
36
+ <div class="kpi">
37
+ <div class="label">${esc(k.label)}</div>
38
+ <div class="valor">${esc(k.valor)}</div>
39
+ ${k.nota ? `<div class="nota">${esc(k.nota)}</div>` : ''}
40
+ </div>`)
41
+ .join('');
42
+ return `<div class="kpi-row">${items}</div>`;
43
+ }
44
+ export function tabla(filas, columnas) {
45
+ if (filas.length === 0) {
46
+ return `<p><small>Sin datos para mostrar.</small></p>`;
47
+ }
48
+ const head = columnas
49
+ .map((c) => `<th${c.ancho ? ` style="width:${c.ancho}"` : ''}>${esc(c.header)}</th>`)
50
+ .join('');
51
+ const body = filas
52
+ .map((fila, i) => {
53
+ const celdas = columnas
54
+ .map((c) => `<td${c.numerica ? ' class="num"' : ''}>${c.celda(fila, i)}</td>`)
55
+ .join('');
56
+ return `<tr>${celdas}</tr>`;
57
+ })
58
+ .join('');
59
+ return `<table><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table>`;
60
+ }
61
+ export function badge(texto, tono = 'neutro') {
62
+ return `<span class="badge badge-${tono}">${esc(texto)}</span>`;
63
+ }
64
+ // ─── Callout ──────────────────────────────────────────────────────────
65
+ export function callout(titulo, cuerpoHtml, tono = 'neutro') {
66
+ const clase = tono === 'neutro' ? 'callout' : `callout callout-${tono}`;
67
+ return `
68
+ <div class="${clase}">
69
+ <div class="titulo">${esc(titulo)}</div>
70
+ <div>${cuerpoHtml}</div>
71
+ </div>`.trim();
72
+ }
73
+ export function lista(items) {
74
+ if (items.length === 0)
75
+ return '';
76
+ return `<ul style="margin:0;padding-left:4mm">${items.map((i) => `<li>${esc(i)}</li>`).join('')}</ul>`;
77
+ }
78
+ /**
79
+ * Gráfico de barras horizontal en SVG inline.
80
+ *
81
+ * SVG en vez de una librería de charts: es vectorial (imprime nítido a cualquier
82
+ * DPI), no requiere JS en tiempo de impresión y no añade dependencias.
83
+ */
84
+ export function barChartSVG(datos, opts = {}) {
85
+ if (datos.length === 0)
86
+ return '';
87
+ const ancho = opts.ancho ?? 680;
88
+ const alturaBarra = opts.alturaBarra ?? 22;
89
+ const gap = 8;
90
+ const anchoEtiqueta = opts.anchoEtiqueta ?? 190;
91
+ const margenDerecho = 90;
92
+ const alto = datos.length * (alturaBarra + gap) + gap;
93
+ const anchoUtil = ancho - anchoEtiqueta - margenDerecho;
94
+ const maxValor = Math.max(...datos.map((d) => d.valor), 1);
95
+ const barras = datos
96
+ .map((d, i) => {
97
+ const y = gap + i * (alturaBarra + gap);
98
+ const w = Math.max(1, (d.valor / maxValor) * anchoUtil);
99
+ const color = d.color ?? TOKENS.color.marca;
100
+ const texto = d.valorTexto ?? String(d.valor);
101
+ // Etiqueta truncada para no romper el layout
102
+ const etiqueta = d.etiqueta.length > 30 ? `${d.etiqueta.slice(0, 29)}…` : d.etiqueta;
103
+ return `
104
+ <g>
105
+ <text x="${anchoEtiqueta - 6}" y="${y + alturaBarra / 2}" text-anchor="end" dominant-baseline="central"
106
+ font-size="10" fill="${TOKENS.color.tintaSuave}">${esc(etiqueta)}</text>
107
+ <rect x="${anchoEtiqueta}" y="${y}" width="${anchoUtil}" height="${alturaBarra}" fill="#f1f4f7" rx="2"/>
108
+ <rect x="${anchoEtiqueta}" y="${y}" width="${w.toFixed(1)}" height="${alturaBarra}" fill="${color}" rx="2"/>
109
+ <text x="${anchoEtiqueta + w + 6}" y="${y + alturaBarra / 2}" dominant-baseline="central"
110
+ font-size="10" font-weight="600" fill="${TOKENS.color.tinta}">${esc(texto)}</text>
111
+ </g>`;
112
+ })
113
+ .join('');
114
+ return `
115
+ <div class="chart">
116
+ <svg viewBox="0 0 ${ancho} ${alto}" role="img" xmlns="http://www.w3.org/2000/svg">
117
+ ${barras}
118
+ </svg>
119
+ </div>`.trim();
120
+ }
121
+ // ─── Pie de documento ─────────────────────────────────────────────────
122
+ export function pieDoc(texto) {
123
+ return `<div class="pie-doc">${esc(texto)}</div>`;
124
+ }
125
+ //# sourceMappingURL=components.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.js","sourceRoot":"","sources":["../../src/reports/components.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Escritura de informes a disco.
3
+ *
4
+ * DECISIÓN CLAVE DE DISEÑO: el HTML NUNCA se devuelve al LLM.
5
+ * Un informe pesa decenas de KB; retornarlo como texto consumiría miles de
6
+ * tokens de contexto por cada llamada. La tool escribe el archivo y devuelve
7
+ * solo la ruta más un resumen breve.
8
+ */
9
+ /** Directorio de salida por defecto: ./informes en el cwd del servidor. */
10
+ export declare function defaultOutputDir(): string;
11
+ /**
12
+ * Convierte un texto en un fragmento seguro para nombre de archivo.
13
+ * Ej: "Radar RM / julio" → "radar-rm-julio"
14
+ */
15
+ export declare function slug(texto: string): string;
16
+ /** Marca temporal compacta para nombres de archivo: 20260715-0930 */
17
+ export declare function stamp(d?: Date): string;
18
+ export interface EscrituraResultado {
19
+ ruta: string;
20
+ bytes: number;
21
+ }
22
+ /**
23
+ * Escribe el HTML en disco creando el directorio si no existe.
24
+ * Retorna la ruta absoluta y el tamaño resultante.
25
+ */
26
+ export declare function escribirInforme(html: string, nombreArchivo: string, dir?: string): EscrituraResultado;
27
+ //# sourceMappingURL=export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/reports/export.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,2EAA2E;AAC3E,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;GAGG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ1C;AAED,qEAAqE;AACrE,wBAAgB,KAAK,CAAC,CAAC,GAAE,IAAiB,GAAG,MAAM,CAGlD;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAUrG"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Escritura de informes a disco.
3
+ *
4
+ * DECISIÓN CLAVE DE DISEÑO: el HTML NUNCA se devuelve al LLM.
5
+ * Un informe pesa decenas de KB; retornarlo como texto consumiría miles de
6
+ * tokens de contexto por cada llamada. La tool escribe el archivo y devuelve
7
+ * solo la ruta más un resumen breve.
8
+ */
9
+ import fs from 'fs';
10
+ import path from 'path';
11
+ import { logger } from '../utils/logger.js';
12
+ /** Directorio de salida por defecto: ./informes en el cwd del servidor. */
13
+ export function defaultOutputDir() {
14
+ return path.resolve(process.cwd(), 'informes');
15
+ }
16
+ /**
17
+ * Convierte un texto en un fragmento seguro para nombre de archivo.
18
+ * Ej: "Radar RM / julio" → "radar-rm-julio"
19
+ */
20
+ export function slug(texto) {
21
+ return texto
22
+ .normalize('NFD')
23
+ .replace(/[\u0300-\u036f]/g, '') // quitar diacríticos combinantes
24
+ .toLowerCase()
25
+ .replace(/[^a-z0-9]+/g, '-')
26
+ .replace(/^-+|-+$/g, '')
27
+ .slice(0, 60) || 'informe';
28
+ }
29
+ /** Marca temporal compacta para nombres de archivo: 20260715-0930 */
30
+ export function stamp(d = new Date()) {
31
+ const p = (n) => String(n).padStart(2, '0');
32
+ return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}`;
33
+ }
34
+ /**
35
+ * Escribe el HTML en disco creando el directorio si no existe.
36
+ * Retorna la ruta absoluta y el tamaño resultante.
37
+ */
38
+ export function escribirInforme(html, nombreArchivo, dir) {
39
+ const destinoDir = dir ? path.resolve(dir) : defaultOutputDir();
40
+ fs.mkdirSync(destinoDir, { recursive: true });
41
+ const ruta = path.join(destinoDir, nombreArchivo);
42
+ fs.writeFileSync(ruta, html, 'utf8');
43
+ const bytes = Buffer.byteLength(html, 'utf8');
44
+ logger.info(`Informe generado: ${ruta} (${(bytes / 1024).toFixed(1)} KB)`);
45
+ return { ruta, bytes };
46
+ }
47
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/reports/export.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,2EAA2E;AAC3E,MAAM,UAAU,gBAAgB;IAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC;AACjD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,IAAI,CAAC,KAAa;IAChC,OAAO,KAAK;SACT,SAAS,CAAC,KAAK,CAAC;SAChB,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,iCAAiC;SACjE,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC;AAC/B,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,KAAK,CAAC,IAAU,IAAI,IAAI,EAAE;IACxC,MAAM,CAAC,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,OAAO,GAAG,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC;AAC5G,CAAC;AAOD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,aAAqB,EAAE,GAAY;IAC/E,MAAM,UAAU,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAChE,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE9C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IAClD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAErC,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9C,MAAM,CAAC,IAAI,CAAC,qBAAqB,IAAI,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC3E,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Formateo localizado para informes (Chile).
3
+ *
4
+ * Fuente única de verdad para representar montos, fechas y RUTs.
5
+ * Evita que cada template invente su propio formato.
6
+ */
7
+ /** Formatea un monto en pesos chilenos. Ej: 1250000 → "$1.250.000" */
8
+ export declare function clp(monto: number | null | undefined): string;
9
+ /** Formatea un número con separador de miles chileno. */
10
+ export declare function numero(valor: number | null | undefined, decimales?: number): string;
11
+ /** Formatea un porcentaje. Ej: 12.3 → "12,3%" */
12
+ export declare function porcentaje(valor: number | null | undefined, decimales?: number): string;
13
+ /** Formatea una fecha ISO a formato legible chileno. Ej: "15-07-2026 09:30" */
14
+ export declare function fecha(iso: string | null | undefined, conHora?: boolean): string;
15
+ /** Fecha larga para portadas. Ej: "15 de julio de 2026" */
16
+ export declare function fechaLarga(d?: Date): string;
17
+ /**
18
+ * Formatea un RUT chileno con puntos y guion. Ej: "761234567" → "76.123.456-7"
19
+ * Si el valor no parece un RUT, se devuelve tal cual.
20
+ */
21
+ export declare function rut(valor: string | null | undefined): string;
22
+ /** Convierte horas a una glosa humana. Ej: 30.5 → "1d 6h" */
23
+ export declare function horasRestantes(horas: number | null | undefined): string;
24
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/reports/format.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,sEAAsE;AACtE,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAG5D;AAED,yDAAyD;AACzD,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,SAAS,SAAI,GAAG,MAAM,CAM9E;AAED,iDAAiD;AACjD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,SAAS,SAAI,GAAG,MAAM,CAGlF;AAED,+EAA+E;AAC/E,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,UAAO,GAAG,MAAM,CAW5E;AAED,2DAA2D;AAC3D,wBAAgB,UAAU,CAAC,CAAC,GAAE,IAAiB,GAAG,MAAM,CAEvD;AAED;;;GAGG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAO5D;AAED,6DAA6D;AAC7D,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAOvE"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Formateo localizado para informes (Chile).
3
+ *
4
+ * Fuente única de verdad para representar montos, fechas y RUTs.
5
+ * Evita que cada template invente su propio formato.
6
+ */
7
+ /** Formatea un monto en pesos chilenos. Ej: 1250000 → "$1.250.000" */
8
+ export function clp(monto) {
9
+ if (monto === null || monto === undefined || !Number.isFinite(monto))
10
+ return '—';
11
+ return `$${Math.round(monto).toLocaleString('es-CL')}`;
12
+ }
13
+ /** Formatea un número con separador de miles chileno. */
14
+ export function numero(valor, decimales = 0) {
15
+ if (valor === null || valor === undefined || !Number.isFinite(valor))
16
+ return '—';
17
+ return valor.toLocaleString('es-CL', {
18
+ minimumFractionDigits: decimales,
19
+ maximumFractionDigits: decimales,
20
+ });
21
+ }
22
+ /** Formatea un porcentaje. Ej: 12.3 → "12,3%" */
23
+ export function porcentaje(valor, decimales = 1) {
24
+ if (valor === null || valor === undefined || !Number.isFinite(valor))
25
+ return '—';
26
+ return `${numero(valor, decimales)}%`;
27
+ }
28
+ /** Formatea una fecha ISO a formato legible chileno. Ej: "15-07-2026 09:30" */
29
+ export function fecha(iso, conHora = true) {
30
+ if (!iso)
31
+ return '—';
32
+ const d = new Date(iso);
33
+ if (Number.isNaN(d.getTime()))
34
+ return '—';
35
+ const dia = String(d.getDate()).padStart(2, '0');
36
+ const mes = String(d.getMonth() + 1).padStart(2, '0');
37
+ const anio = d.getFullYear();
38
+ if (!conHora)
39
+ return `${dia}-${mes}-${anio}`;
40
+ const hh = String(d.getHours()).padStart(2, '0');
41
+ const mm = String(d.getMinutes()).padStart(2, '0');
42
+ return `${dia}-${mes}-${anio} ${hh}:${mm}`;
43
+ }
44
+ /** Fecha larga para portadas. Ej: "15 de julio de 2026" */
45
+ export function fechaLarga(d = new Date()) {
46
+ return d.toLocaleDateString('es-CL', { day: 'numeric', month: 'long', year: 'numeric' });
47
+ }
48
+ /**
49
+ * Formatea un RUT chileno con puntos y guion. Ej: "761234567" → "76.123.456-7"
50
+ * Si el valor no parece un RUT, se devuelve tal cual.
51
+ */
52
+ export function rut(valor) {
53
+ if (!valor)
54
+ return '—';
55
+ const limpio = valor.replace(/[.\-\s]/g, '').toUpperCase();
56
+ if (!/^\d{7,8}[\dK]$/.test(limpio))
57
+ return valor;
58
+ const cuerpo = limpio.slice(0, -1);
59
+ const dv = limpio.slice(-1);
60
+ return `${Number(cuerpo).toLocaleString('es-CL')}-${dv}`;
61
+ }
62
+ /** Convierte horas a una glosa humana. Ej: 30.5 → "1d 6h" */
63
+ export function horasRestantes(horas) {
64
+ if (horas === null || horas === undefined || !Number.isFinite(horas) || horas <= 0)
65
+ return '—';
66
+ if (horas < 1)
67
+ return `${Math.round(horas * 60)} min`;
68
+ if (horas < 24)
69
+ return `${Math.round(horas)} h`;
70
+ const dias = Math.floor(horas / 24);
71
+ const resto = Math.round(horas % 24);
72
+ return resto > 0 ? `${dias}d ${resto}h` : `${dias}d`;
73
+ }
74
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/reports/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Shell del documento: envuelve el cuerpo de un template en un HTML
3
+ * autocontenido y portable (CSS inline, sin recursos externos).
4
+ *
5
+ * Autocontenido a propósito: el informe debe poder moverse por correo o abrirse
6
+ * sin red y verse idéntico. Nada de CDNs ni fuentes remotas.
7
+ */
8
+ import { type FormatoPapel } from './theme.js';
9
+ export interface DocumentoOpts {
10
+ titulo: string;
11
+ cuerpo: string;
12
+ /** Tamaño de papel. Por defecto Carta (estándar de oficina en Chile). */
13
+ formato?: FormatoPapel;
14
+ /** CSS adicional específico del template. */
15
+ cssExtra?: string;
16
+ }
17
+ export declare function renderDocumento(o: DocumentoOpts): string;
18
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/reports/render.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAuC,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAGpF,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAmBxD"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Shell del documento: envuelve el cuerpo de un template en un HTML
3
+ * autocontenido y portable (CSS inline, sin recursos externos).
4
+ *
5
+ * Autocontenido a propósito: el informe debe poder moverse por correo o abrirse
6
+ * sin red y verse idéntico. Nada de CDNs ni fuentes remotas.
7
+ */
8
+ import { baseCSS, PAPEL, FORMATO_POR_DEFECTO } from './theme.js';
9
+ import { esc } from './components.js';
10
+ export function renderDocumento(o) {
11
+ const formato = o.formato ?? FORMATO_POR_DEFECTO;
12
+ return `<!DOCTYPE html>
13
+ <html lang="es-CL">
14
+ <head>
15
+ <meta charset="utf-8">
16
+ <meta name="viewport" content="width=device-width, initial-scale=1">
17
+ <title>${esc(o.titulo)}</title>
18
+ <meta name="generator" content="mcp-compra-agil">
19
+ <meta name="formato-papel" content="${esc(PAPEL[formato].glosa)}">
20
+ <style>
21
+ ${baseCSS(formato)}
22
+ ${o.cssExtra ?? ''}
23
+ </style>
24
+ </head>
25
+ <body>
26
+ ${o.cuerpo}
27
+ </body>
28
+ </html>`;
29
+ }
30
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/reports/render.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAqB,MAAM,YAAY,CAAC;AACpF,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAWtC,MAAM,UAAU,eAAe,CAAC,CAAgB;IAC9C,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI,mBAAmB,CAAC;IACjD,OAAO;;;;;SAKA,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;;sCAEgB,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC;;EAE7D,OAAO,CAAC,OAAO,CAAC;EAChB,CAAC,CAAC,QAAQ,IAAI,EAAE;;;;EAIhB,CAAC,CAAC,MAAM;;QAEF,CAAC;AACT,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Template: Informe de Radar de Oportunidades.
3
+ *
4
+ * Recibe datos ya recolectados (no consulta la API) — así el mismo dataset
5
+ * alimenta tanto la respuesta JSON de la tool como este informe.
6
+ */
7
+ import { type TonoBadge } from '../components.js';
8
+ import { type FormatoPapel } from '../theme.js';
9
+ /** Forma mínima que el informe necesita de cada oportunidad. */
10
+ export interface OportunidadInforme {
11
+ codigo: string;
12
+ nombre: string;
13
+ organismo: string;
14
+ region: string;
15
+ presupuesto_disponible: number;
16
+ ofertas_recibidas: number;
17
+ horas_restantes: number;
18
+ fecha_cierre: string;
19
+ puntuacion_caliente: number;
20
+ factores_calificacion: string[];
21
+ }
22
+ export interface RadarInformeData {
23
+ oportunidades: OportunidadInforme[];
24
+ totalAnalizadas: number;
25
+ filtros: {
26
+ region?: string;
27
+ q?: string;
28
+ presupuestoMinimo?: number;
29
+ paginasEscaneadas: number;
30
+ };
31
+ generadoEn: Date;
32
+ /** Tamaño de papel del informe. Por defecto Carta. */
33
+ formato?: FormatoPapel;
34
+ }
35
+ /** Clasifica el hot score en un tono semántico. Máximo teórico: 105 pts. */
36
+ export declare function tonoScore(score: number): {
37
+ tono: TonoBadge;
38
+ glosa: string;
39
+ };
40
+ export declare function renderRadarInforme(data: RadarInformeData): string;
41
+ //# sourceMappingURL=radar-oportunidades.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radar-oportunidades.d.ts","sourceRoot":"","sources":["../../../src/reports/templates/radar-oportunidades.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAEL,KAAK,SAAS,EACf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAsC,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAEpF,gEAAgE;AAChE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB,EAAE,MAAM,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,kBAAkB,EAAE,CAAC;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,CAAC,CAAC,EAAE,MAAM,CAAC;QACX,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,UAAU,EAAE,IAAI,CAAC;IACjB,sDAAsD;IACtD,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAED,4EAA4E;AAC5E,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAK3E;AAQD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CA4JjE"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Template: Informe de Radar de Oportunidades.
3
+ *
4
+ * Recibe datos ya recolectados (no consulta la API) — así el mismo dataset
5
+ * alimenta tanto la respuesta JSON de la tool como este informe.
6
+ */
7
+ import { renderDocumento } from '../render.js';
8
+ import { portada, kpiRow, tabla, badge, callout, barChartSVG, pieDoc, esc, } from '../components.js';
9
+ import { clp, numero, fecha, fechaLarga, horasRestantes } from '../format.js';
10
+ import { TOKENS, PAPEL, FORMATO_POR_DEFECTO } from '../theme.js';
11
+ /** Clasifica el hot score en un tono semántico. Máximo teórico: 105 pts. */
12
+ export function tonoScore(score) {
13
+ if (score >= 80)
14
+ return { tono: 'critico', glosa: 'Muy caliente' };
15
+ if (score >= 55)
16
+ return { tono: 'alerta', glosa: 'Caliente' };
17
+ if (score >= 30)
18
+ return { tono: 'neutro', glosa: 'Templada' };
19
+ return { tono: 'neutro', glosa: 'Fría' };
20
+ }
21
+ function colorScore(score) {
22
+ if (score >= 80)
23
+ return TOKENS.color.critico;
24
+ if (score >= 55)
25
+ return TOKENS.color.alerta;
26
+ return TOKENS.color.marca;
27
+ }
28
+ export function renderRadarInforme(data) {
29
+ const { filtros } = data;
30
+ const formato = data.formato ?? FORMATO_POR_DEFECTO;
31
+ // Orden defensivo: aunque `recolectarDatosRadar` ya entrega los datos rankeados,
32
+ // el template no debe confiar en el orden de su entrada — un gráfico "Top N"
33
+ // desordenado es un error visible y silencioso. Ordenar aquí es O(n log n) sobre
34
+ // decenas de filas y hace al template correcto ante cualquier llamador.
35
+ const ops = [...data.oportunidades].sort((a, b) => b.puntuacion_caliente - a.puntuacion_caliente);
36
+ // ── KPIs de cabecera ──
37
+ const sinOferentes = ops.filter((o) => o.ofertas_recibidas === 0).length;
38
+ const cierran24h = ops.filter((o) => o.horas_restantes <= 24).length;
39
+ const montoTotal = ops.reduce((acc, o) => acc + (o.presupuesto_disponible || 0), 0);
40
+ const kpis = kpiRow([
41
+ { label: 'Oportunidades', valor: numero(ops.length), nota: `de ${numero(data.totalAnalizadas)} analizadas` },
42
+ { label: 'Sin oferentes', valor: numero(sinOferentes), nota: 'competencia cero' },
43
+ { label: 'Cierran en 24h', valor: numero(cierran24h), nota: 'requieren acción' },
44
+ { label: 'Monto en juego', valor: clp(montoTotal), nota: 'presupuesto sumado' },
45
+ ]);
46
+ // ── Filtros aplicados ──
47
+ const filtrosTexto = [
48
+ filtros.region ? `Región ${filtros.region}` : 'Todas las regiones',
49
+ filtros.q ? `Búsqueda: “${filtros.q}”` : 'Sin filtro de rubro',
50
+ filtros.presupuestoMinimo ? `Presupuesto ≥ ${clp(filtros.presupuestoMinimo)}` : 'Sin mínimo de presupuesto',
51
+ `${filtros.paginasEscaneadas} página(s) escaneada(s)`,
52
+ ].join(' · ');
53
+ // ── Gráfico: top 10 por puntuación ──
54
+ const top = ops.slice(0, 10);
55
+ const grafico = top.length > 0
56
+ ? barChartSVG(top.map((o) => ({
57
+ etiqueta: o.nombre,
58
+ valor: o.puntuacion_caliente,
59
+ valorTexto: `${o.puntuacion_caliente} pts`,
60
+ color: colorScore(o.puntuacion_caliente),
61
+ })))
62
+ : '';
63
+ // ── Tabla principal ──
64
+ const tablaOps = tabla(ops, [
65
+ {
66
+ header: '#',
67
+ ancho: '6mm',
68
+ celda: (_o, i) => `<span class="num">${i + 1}</span>`,
69
+ },
70
+ {
71
+ header: 'Código',
72
+ ancho: '28mm',
73
+ celda: (o) => `<span class="mono">${esc(o.codigo)}</span>`,
74
+ },
75
+ {
76
+ header: 'Proceso',
77
+ celda: (o) => `${esc(o.nombre)}<br><small>${esc(o.organismo)}</small>`,
78
+ },
79
+ {
80
+ header: 'Región',
81
+ ancho: '20mm',
82
+ celda: (o) => esc(o.region),
83
+ },
84
+ {
85
+ header: 'Presupuesto',
86
+ ancho: '22mm',
87
+ numerica: true,
88
+ celda: (o) => clp(o.presupuesto_disponible),
89
+ },
90
+ {
91
+ header: 'Ofertas',
92
+ ancho: '14mm',
93
+ numerica: true,
94
+ celda: (o) => o.ofertas_recibidas === 0
95
+ ? badge('0', 'exito')
96
+ : `${numero(o.ofertas_recibidas)}`,
97
+ },
98
+ {
99
+ header: 'Cierra en',
100
+ ancho: '18mm',
101
+ numerica: true,
102
+ celda: (o) => {
103
+ const txt = horasRestantes(o.horas_restantes);
104
+ return o.horas_restantes <= 12 ? badge(txt, 'critico') : esc(txt);
105
+ },
106
+ },
107
+ {
108
+ header: 'Score',
109
+ ancho: '22mm',
110
+ numerica: true,
111
+ celda: (o) => {
112
+ const { tono, glosa } = tonoScore(o.puntuacion_caliente);
113
+ return `${badge(`${o.puntuacion_caliente} · ${glosa}`, tono)}`;
114
+ },
115
+ },
116
+ ]);
117
+ // ── Fichas detalladas del top 3 ──
118
+ const fichas = ops.slice(0, 3).map((o, i) => {
119
+ const { tono } = tonoScore(o.puntuacion_caliente);
120
+ const cuerpo = `
121
+ <p style="margin-bottom:2mm"><b>${esc(o.organismo)}</b> — ${esc(o.region)}<br>
122
+ <span class="mono">${esc(o.codigo)}</span></p>
123
+ <p style="margin-bottom:2mm">
124
+ Presupuesto <b>${clp(o.presupuesto_disponible)}</b> ·
125
+ Ofertas recibidas: <b>${numero(o.ofertas_recibidas)}</b> ·
126
+ Cierra el <b>${fecha(o.fecha_cierre)}</b> (${esc(horasRestantes(o.horas_restantes))})
127
+ </p>
128
+ <div><small>Factores de puntuación:</small>
129
+ <ul style="margin:1mm 0 0;padding-left:4mm">
130
+ ${o.factores_calificacion.map((f) => `<li><small>${esc(f)}</small></li>`).join('')}
131
+ </ul>
132
+ </div>`;
133
+ return callout(`${i + 1}. ${o.nombre} — ${o.puntuacion_caliente} pts`, cuerpo, tono === 'exito' ? 'neutro' : tono);
134
+ }).join('');
135
+ // ── Ensamblado ──
136
+ const cuerpo = `
137
+ ${portada({
138
+ eyebrow: 'Mercado Público · Compra Ágil',
139
+ titulo: 'Radar de Oportunidades',
140
+ subtitulo: 'Procesos activos priorizados por facilidad de adjudicación',
141
+ meta: [
142
+ { label: 'Generado', valor: fechaLarga(data.generadoEn) },
143
+ { label: 'Alcance', valor: filtros.region ? `Región ${filtros.region}` : 'Nacional' },
144
+ { label: 'Oportunidades', valor: String(ops.length) },
145
+ ],
146
+ })}
147
+
148
+ ${kpis}
149
+
150
+ <p><small>${esc(filtrosTexto)}</small></p>
151
+
152
+ ${ops.length === 0 ? callout('Sin resultados', '<p>No se encontraron Compras Ágiles activas que coincidan con los filtros aplicados.</p>', 'alerta') : ''}
153
+
154
+ ${top.length > 0 ? `<h2>Top ${top.length} por puntuación</h2>${grafico}` : ''}
155
+
156
+ ${fichas ? `<h2>Oportunidades destacadas</h2>${fichas}` : ''}
157
+
158
+ ${ops.length > 0 ? `<h2>Listado completo</h2>${tablaOps}` : ''}
159
+
160
+ ${callout('Cómo se calcula la puntuación', `<p style="margin:0">Fórmula ponderada sobre 105 puntos: baja competencia (hasta 50 pts) + urgencia de cierre (hasta 30 pts) + tamaño de presupuesto (hasta 20 pts) + ausencia de bases adjuntas (5 pts). Una puntuación alta indica un proceso con pocos oferentes, cierre próximo y monto atractivo.</p>`)}
161
+
162
+ ${pieDoc(`Informe generado automáticamente por mcp-compra-agil el ${fecha(data.generadoEn.toISOString())} a partir de datos públicos de la API Compra Ágil v2 de Mercado Público (ChileCompra). Los datos reflejan el estado al momento de la consulta y pueden variar. · Formato de impresión: ${PAPEL[formato].glosa}`)}
163
+ `.trim();
164
+ return renderDocumento({
165
+ titulo: `Radar de Oportunidades — ${fechaLarga(data.generadoEn)}`,
166
+ cuerpo,
167
+ formato,
168
+ });
169
+ }
170
+ //# sourceMappingURL=radar-oportunidades.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radar-oportunidades.js","sourceRoot":"","sources":["../../../src/reports/templates/radar-oportunidades.ts"],"names":[],"mappings":"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"}