paperclip-plugin-autopilot-ops 0.1.43 → 0.1.45

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/dist/manifest.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/manifest.ts
2
2
  var PLUGIN_ID = "alephoo.autopilot-ops";
3
- var PLUGIN_VERSION = "0.1.43";
3
+ var PLUGIN_VERSION = "0.1.45";
4
4
  var DB_NAMESPACE_SLUG = "autopilot_ops";
5
5
  var PAGE_ROUTE = "autopilot";
6
6
  var TOOL_NAMES = {
package/dist/ui/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
 
9
9
  // src/manifest.ts
10
10
  var PLUGIN_ID = "alephoo.autopilot-ops";
11
- var PLUGIN_VERSION = "0.1.43";
11
+ var PLUGIN_VERSION = "0.1.45";
12
12
  var DB_NAMESPACE_SLUG = "autopilot_ops";
13
13
  var PAGE_ROUTE = "autopilot";
14
14
  var TOOL_NAMES = {
package/dist/worker.js CHANGED
@@ -39932,7 +39932,7 @@ function startWorkerRpcHost(options) {
39932
39932
 
39933
39933
  // src/manifest.ts
39934
39934
  var PLUGIN_ID = "alephoo.autopilot-ops";
39935
- var PLUGIN_VERSION = "0.1.43";
39935
+ var PLUGIN_VERSION = "0.1.45";
39936
39936
  var DB_NAMESPACE_SLUG = "autopilot_ops";
39937
39937
  var PAGE_ROUTE = "autopilot";
39938
39938
  var TOOL_NAMES = {
@@ -46995,7 +46995,31 @@ function controlarTabla(ref, columnas, filas, problemas) {
46995
46995
  }
46996
46996
  }
46997
46997
  function jergaEn(texto10) {
46998
- const m = /\b[A-Z]{2,5}-\d+\b|\b[a-z]+_[a-z0-9_]+\b|\bv\d+\.\d+(\.\d+)?\b/.exec(texto10) ?? /\bAlephoo\b|\bcapa sem[aá]ntica\b|\brecetas? sem[aá]nticas?\b/i.exec(texto10);
46998
+ const m = /\b[A-Z]{2,5}-\d+\b|\b[a-z]+_[a-z0-9_]+\b|\bv\d+\.\d+(\.\d+)?\b|\b[Ii][Dd] \d+\b/.exec(texto10) ?? /\bAlephoo\b|\bcapa sem[aá]ntica\b|\brecetas? sem[aá]nticas?\b|\bborrad[oa]s? l[oó]gicamente\b|\bSELECT\b/i.exec(texto10);
46999
+ return m ? m[0] : null;
47000
+ }
47001
+ var MAX_TEXTOS = 1;
47002
+ var MAX_LARGO_LECTURA = 900;
47003
+ var MAX_LARGO_PERIODO = 60;
47004
+ var MAX_LARGO_DESCRIPCION = 160;
47005
+ var MAX_LARGO_CONTEXTO = 120;
47006
+ var MAX_LARGO_TITULO = 60;
47007
+ var MAX_PRIMERA_ORACION = 160;
47008
+ var MESES_RE = "(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|setiembre|octubre|noviembre|diciembre) \\d{4}";
47009
+ var PERIODO_RE = new RegExp(`^(${MESES_RE}( a ${MESES_RE})?|al \\d{2}/\\d{2}/\\d{4})$`, "i");
47010
+ function primeraOracion(texto10) {
47011
+ const re = /\.\s+(?=[A-ZÁÉÍÓÚÑ¿¡«(])/g;
47012
+ let m;
47013
+ while (m = re.exec(texto10)) {
47014
+ const ultima = texto10.slice(0, m.index).split(/\s/).pop() ?? "";
47015
+ if (/^(dr|dra|sr|sra|lic|ing|aprox|etc|nro|art|av|[a-záéíóúñ])$/i.test(ultima) || m.index < 20) continue;
47016
+ return texto10.slice(0, m.index + 1);
47017
+ }
47018
+ return texto10;
47019
+ }
47020
+ var sinApertura = (c) => JSON.stringify(Object.fromEntries(Object.entries(c.parametros).filter(([k]) => k !== "por").sort(([a], [b]) => a.localeCompare(b))));
47021
+ function marcaDelSistemaEn(texto10) {
47022
+ const m = /\bAlephoo\b|\b[Ii][Dd] \d+\b/.exec(texto10);
46999
47023
  return m ? m[0] : null;
47000
47024
  }
47001
47025
  var MAX_BARRAS = 12;
@@ -47023,7 +47047,7 @@ function validarWidgets(entrada) {
47023
47047
  const unidad = texto9(o.unidad, 16, `${ref}.unidad`, problemas);
47024
47048
  if (unidad === "%" && !ES_VARIACION.test(etiqueta)) controlarPorcentaje(valor, `${ref}.valor`, problemas);
47025
47049
  const { calculo, origen } = leerCalculo(o.calculo, ref, problemas);
47026
- salida.push({ tipo, etiqueta, valor, unidad, contexto: texto9(o.contexto, 160, `${ref}.contexto`, problemas), tono, ancho: a, ...calculo ? { calculo } : {}, origen });
47050
+ salida.push({ tipo, etiqueta, valor, unidad, contexto: texto9(o.contexto, MAX_LARGO_CONTEXTO, `${ref}.contexto`, problemas), tono, ancho: a, ...calculo ? { calculo } : {}, origen });
47027
47051
  break;
47028
47052
  }
47029
47053
  case "barras": {
@@ -47050,6 +47074,7 @@ function validarWidgets(entrada) {
47050
47074
  return { x: texto9(po.x, 40, `${ref}.puntos[${j}].x`, problemas, true) ?? "", y: numero2(po.y, `${ref}.puntos[${j}].y`, problemas) };
47051
47075
  });
47052
47076
  if (unidad === "%" && !ES_VARIACION.test(titulo)) serie2.forEach((p, j) => controlarPorcentaje(p.y, `${ref}.puntos[${j}].y`, problemas));
47077
+ if (!unidad) problemas.push(`${ref}.unidad: una curva lleva su unidad (\xABturnos\xBB, \xAB$\xBB, \xAB%\xBB)`);
47053
47078
  const { calculo, origen } = leerCalculo(o.calculo, ref, problemas);
47054
47079
  salida.push({ tipo, titulo, unidad, puntos: serie2, ancho: a, ...calculo ? { calculo } : {}, origen });
47055
47080
  break;
@@ -47076,21 +47101,54 @@ function validarWidgets(entrada) {
47076
47101
  break;
47077
47102
  }
47078
47103
  case "texto": {
47079
- salida.push({ tipo, titulo: texto9(o.titulo, 120, `${ref}.titulo`, problemas), texto: texto9(o.texto, 4e3, `${ref}.texto`, problemas, true) ?? "", ancho: a });
47104
+ const lectura = (texto9(o.texto, 4e3, `${ref}.texto`, problemas, true) ?? "").replace(/\*\*/g, "");
47105
+ const primera = primeraOracion(lectura.split(/\n{2,}/)[0]?.trim() ?? "");
47106
+ if (primera.length > MAX_PRIMERA_ORACION) problemas.push(`${ref}.texto: la primera oraci\xF3n tiene ${primera.length} caracteres: hasta ${MAX_PRIMERA_ORACION} (una acci\xF3n para la direcci\xF3n con un n\xFAmero; lo que la sostiene, despu\xE9s)`);
47107
+ salida.push({ tipo, titulo: texto9(o.titulo, 120, `${ref}.titulo`, problemas), texto: lectura, ancho: a });
47080
47108
  break;
47081
47109
  }
47082
47110
  }
47083
47111
  });
47084
47112
  for (const [k, w] of salida.entries()) {
47085
- const textos = [w.tipo === "texto" ? w.texto : null, w.tipo === "texto" ? w.titulo : null, "titulo" in w ? w.titulo : null, w.tipo === "indicador" ? w.contexto : null, w.tipo === "indicador" ? w.etiqueta : null].filter((x) => typeof x === "string");
47086
- for (const x of textos) {
47113
+ const textos2 = [w.tipo === "texto" ? w.texto : null, w.tipo === "texto" ? w.titulo : null, "titulo" in w ? w.titulo : null, w.tipo === "indicador" ? w.contexto : null, w.tipo === "indicador" ? w.etiqueta : null].filter((x) => typeof x === "string");
47114
+ for (const x of textos2) {
47087
47115
  const jerga = jergaEn(x);
47088
47116
  if (jerga) problemas.push(`widgets[${k}]: \xAB${jerga}\xBB es un t\xE9rmino interno; la direcci\xF3n no lo entiende (us\xE1 \xABel HIS\xBB y nombres de negocio; el detalle t\xE9cnico va en fuentes o en el comentario de la tarea)`);
47089
47117
  }
47090
47118
  }
47119
+ const vistos = /* @__PURE__ */ new Map();
47120
+ for (const [k, w] of salida.entries()) {
47121
+ if (w.tipo !== "indicador" || w.valor === "" || w.valor === 0) continue;
47122
+ const llave = `${w.valor}|${w.unidad ?? ""}`;
47123
+ if (vistos.has(llave)) problemas.push(`widgets[${k}]: repite el n\xFAmero de widgets[${vistos.get(llave)}] (${w.valor}${w.unidad ? ` ${w.unidad}` : ""}): dej\xE1 uno solo y llev\xE1 el otro dato al contexto`);
47124
+ else vistos.set(llave, k);
47125
+ }
47126
+ for (const [k, w] of salida.entries()) {
47127
+ const titulo = "titulo" in w ? w.titulo : void 0;
47128
+ if (titulo && /\$\s?\d|\d{1,3}(\.\d{3})+/.test(titulo)) problemas.push(`widgets[${k}]: el t\xEDtulo lleva una cifra (\xAB${titulo}\xBB): el t\xEDtulo dice qu\xE9 muestra; la cifra va en un indicador`);
47129
+ }
47130
+ for (const [k, w] of salida.entries()) {
47131
+ if (w.tipo === "indicador" && (w.tono === "ambar" || w.tono === "corte") && !w.contexto) problemas.push(`widgets[${k}]: un n\xFAmero en ${w.tono === "corte" ? "rojo" : "\xE1mbar"} lleva en \xABcontexto\xBB por qu\xE9 pide atenci\xF3n (una l\xEDnea)`);
47132
+ }
47133
+ for (const [k, w] of salida.entries()) {
47134
+ const nombres = w.tipo === "barras" ? w.series.map((x) => x.etiqueta) : w.tipo === "tabla" ? w.filas.flatMap((f) => f.filter((c) => typeof c === "string")) : [];
47135
+ const marca = nombres.map(marcaDelSistemaEn).find(Boolean);
47136
+ if (marca) problemas.push(`widgets[${k}]: \xAB${marca}\xBB no es un dato de la instituci\xF3n (un usuario o la marca del sistema): sac\xE1 esa fila o agrupala en \xABSin asignar\xBB`);
47137
+ }
47138
+ for (const [k, w] of salida.entries()) {
47139
+ if (w.tipo !== "barras" || !w.calculo || w.unidad && /%|promedio|tasa/i.test(w.unidad)) continue;
47140
+ const metrica = w.calculo.metricas[0]?.id;
47141
+ const ind = salida.find((x) => x.tipo === "indicador" && typeof x.valor === "number" && !!x.calculo && x.calculo.metricas[0]?.id === metrica && sinApertura(x.calculo) === sinApertura(w.calculo));
47142
+ if (!ind) continue;
47143
+ const suma = w.series.reduce((a, s) => a + s.valor, 0);
47144
+ const valor = ind.valor;
47145
+ if (Math.abs(suma - valor) > Math.max(0.5, Math.abs(valor) * 5e-3)) problemas.push(`widgets[${k}]: las barras suman ${Math.round(suma * 100) / 100} y el indicador \xAB${ind.etiqueta}\xBB (mismo c\xE1lculo) dice ${valor}: agreg\xE1 la barra que falta (\xABSin \u2026\xBB u \xABOtros\xBB) o us\xE1 el mismo per\xEDodo`);
47146
+ }
47147
+ const textos = salida.filter((w) => w.tipo === "texto");
47148
+ if (textos.length > MAX_TEXTOS) problemas.push(`un solo widget de texto (\xABLectura\xBB): lo que cambi\xF3 va en una frase al final de ella, el resto en el comentario de la tarea`);
47149
+ for (const t6 of textos) if (t6.texto.length > MAX_LARGO_LECTURA) problemas.push(`la Lectura tiene ${t6.texto.length} caracteres: hasta ${MAX_LARGO_LECTURA} (2 o 3 p\xE1rrafos breves; el primero, qu\xE9 conviene hacer)`);
47091
47150
  const tonos = (t6) => salida.filter((w) => w.tipo === "indicador" && w.tono === t6).length;
47092
- if (tonos("ambar") > 1) problemas.push("como m\xE1ximo un indicador en \xE1mbar por tablero: el que pide una acci\xF3n de la direcci\xF3n (los dem\xE1s, neutro)");
47093
- if (tonos("corte") > 1) problemas.push("como m\xE1ximo un indicador en rojo (corte) por tablero: solo lo urgente");
47151
+ if (tonos("ambar") + tonos("corte") > 1) problemas.push("como m\xE1ximo un indicador en \xE1mbar o rojo por tablero: el que pide una acci\xF3n de la direcci\xF3n (rojo solo si es urgente); los dem\xE1s, neutro");
47094
47152
  if (problemas.length) throw new OpsError("tablero_invalido", `el tablero no se puede publicar: ${problemas.slice(0, 12).join("; ")}${problemas.length > 12 ? ` (y ${problemas.length - 12} m\xE1s)` : ""}`, { problemas });
47095
47153
  return salida;
47096
47154
  }
@@ -47135,6 +47193,18 @@ async function publicarTablero(ctx, input2) {
47135
47193
  const jerga = typeof v === "string" ? jergaEn(v) : null;
47136
47194
  if (jerga) throw new OpsError("tablero_invalido", `${campo}: \xAB${jerga}\xBB es un t\xE9rmino interno; la direcci\xF3n no lo entiende (us\xE1 \xABel HIS\xBB y nombres de negocio)`);
47137
47195
  }
47196
+ if (typeof input2.descripcion === "string" && input2.descripcion.trim().length > MAX_LARGO_DESCRIPCION) {
47197
+ throw new OpsError("tablero_invalido", `descripcion: hasta ${MAX_LARGO_DESCRIPCION} caracteres (qu\xE9 muestra el tablero, en una o dos frases); el m\xE9todo va en fuentes o en el comentario de la tarea`);
47198
+ }
47199
+ if (typeof input2.periodo === "string" && input2.periodo.trim().length > MAX_LARGO_PERIODO) {
47200
+ throw new OpsError("tablero_invalido", `periodo: hasta ${MAX_LARGO_PERIODO} caracteres (p. ej. \xABoctubre 2025 a septiembre 2026\xBB); las aclaraciones van en la descripci\xF3n`);
47201
+ }
47202
+ if (typeof input2.periodo === "string" && input2.periodo.trim() && !PERIODO_RE.test(input2.periodo.trim())) {
47203
+ throw new OpsError("tablero_invalido", `periodo: \xAB${input2.periodo.trim()}\xBB no tiene la forma de siempre: \xABseptiembre 2026\xBB, \xABoctubre 2025 a septiembre 2026\xBB o \xABal 24/10/2026\xBB (lo dem\xE1s va en la descripci\xF3n)`);
47204
+ }
47205
+ if (titulo.length > MAX_LARGO_TITULO) {
47206
+ throw new OpsError("tablero_invalido", `titulo: hasta ${MAX_LARGO_TITULO} caracteres (entra en dos renglones de la tarjeta); el detalle va en la descripci\xF3n`);
47207
+ }
47138
47208
  const widgets = validarWidgets(input2.widgets);
47139
47209
  const fuentes = arr(input2.fuentes).filter((f) => typeof f === "string" && f.trim()).map((f) => String(f).trim().slice(0, 120)).slice(0, 12);
47140
47210
  const limpio = (v, max) => typeof v === "string" && v.trim() ? v.trim().slice(0, max) : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paperclip-plugin-autopilot-ops",
3
- "version": "0.1.43",
3
+ "version": "0.1.45",
4
4
  "type": "module",
5
5
  "description": "Autopilot Care · operación autónoma gobernada para instituciones de salud: workflows determinísticos, validaciones, registro de negocio, políticas (observa/propone/ejecuta/corte) y tablero del director, como plugin de Paperclip.",
6
6
  "files": [