paperclip-plugin-autopilot-ops 0.1.40 → 0.1.42
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 +40 -1
- package/dist/ui/index.js +40 -1
- package/dist/worker.js +107 -8
- package/migrations/010_tablero_publicaciones.sql +15 -0
- package/package.json +1 -1
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.
|
|
3
|
+
var PLUGIN_VERSION = "0.1.42";
|
|
4
4
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
5
5
|
var PAGE_ROUTE = "autopilot";
|
|
6
6
|
var TOOL_NAMES = {
|
|
@@ -16,6 +16,7 @@ var TOOL_NAMES = {
|
|
|
16
16
|
consultarIndicador: "ops_consultar_indicador",
|
|
17
17
|
publicarTablero: "ops_publicar_tablero",
|
|
18
18
|
listarTableros: "ops_listar_tableros",
|
|
19
|
+
proponerRetiroTablero: "ops_proponer_retiro_tablero",
|
|
19
20
|
listarReglas: "ops_listar_reglas",
|
|
20
21
|
informarPruebaRegla: "ops_informar_prueba_regla",
|
|
21
22
|
evaluarRentabilidad: "ops_evaluar_rentabilidad"
|
|
@@ -271,6 +272,12 @@ var manifest = {
|
|
|
271
272
|
displayName: "Listar tableros",
|
|
272
273
|
description: "Lista los tableros publicados para esta instituci\xF3n (clave, t\xEDtulo, per\xEDodo, fuentes, cu\xE1ndo se actualiz\xF3 y cu\xE1ntos widgets tiene). Usar antes de publicar para reutilizar la clave de un tablero existente en vez de crear uno nuevo. Solo lectura.",
|
|
273
274
|
parametersSchema: { type: "object", properties: {} }
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
name: TOOL_NAMES.proponerRetiroTablero,
|
|
278
|
+
displayName: "Proponer retirar un tablero",
|
|
279
|
+
description: "Cuando la direcci\xF3n pide dar de baja (eliminar, borrar, sacar) un tablero: propon\xE9 retirarlo con su clave y el id de la tarea. No lo retira: el portal le muestra la propuesta a la direcci\xF3n en ese pedido y lo retira una persona. No publiques nada en esa tarea.",
|
|
280
|
+
parametersSchema: { type: "object", properties: { clave: { type: "string" }, issueId: { type: "string" } }, required: ["clave", "issueId"] }
|
|
274
281
|
}
|
|
275
282
|
],
|
|
276
283
|
apiRoutes: [
|
|
@@ -402,6 +409,38 @@ var manifest = {
|
|
|
402
409
|
capability: "api.routes.register",
|
|
403
410
|
companyResolution: { from: "query", key: "companyId" }
|
|
404
411
|
},
|
|
412
|
+
{
|
|
413
|
+
routeKey: "tableros-publicaciones",
|
|
414
|
+
method: "GET",
|
|
415
|
+
path: "/tableros-publicaciones",
|
|
416
|
+
auth: "board",
|
|
417
|
+
capability: "api.routes.register",
|
|
418
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
routeKey: "descartar-retiro-tablero",
|
|
422
|
+
method: "POST",
|
|
423
|
+
path: "/tableros/:clave/no-retirar",
|
|
424
|
+
auth: "board",
|
|
425
|
+
capability: "api.routes.register",
|
|
426
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
routeKey: "tableros-retirados",
|
|
430
|
+
method: "GET",
|
|
431
|
+
path: "/tableros-retirados",
|
|
432
|
+
auth: "board",
|
|
433
|
+
capability: "api.routes.register",
|
|
434
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
routeKey: "restaurar-tablero",
|
|
438
|
+
method: "POST",
|
|
439
|
+
path: "/tableros/:clave/restaurar",
|
|
440
|
+
auth: "board",
|
|
441
|
+
capability: "api.routes.register",
|
|
442
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
443
|
+
},
|
|
405
444
|
{
|
|
406
445
|
routeKey: "retirar-tablero",
|
|
407
446
|
method: "POST",
|
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.
|
|
11
|
+
var PLUGIN_VERSION = "0.1.42";
|
|
12
12
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
13
13
|
var PAGE_ROUTE = "autopilot";
|
|
14
14
|
var TOOL_NAMES = {
|
|
@@ -24,6 +24,7 @@ var TOOL_NAMES = {
|
|
|
24
24
|
consultarIndicador: "ops_consultar_indicador",
|
|
25
25
|
publicarTablero: "ops_publicar_tablero",
|
|
26
26
|
listarTableros: "ops_listar_tableros",
|
|
27
|
+
proponerRetiroTablero: "ops_proponer_retiro_tablero",
|
|
27
28
|
listarReglas: "ops_listar_reglas",
|
|
28
29
|
informarPruebaRegla: "ops_informar_prueba_regla",
|
|
29
30
|
evaluarRentabilidad: "ops_evaluar_rentabilidad"
|
|
@@ -279,6 +280,12 @@ var manifest = {
|
|
|
279
280
|
displayName: "Listar tableros",
|
|
280
281
|
description: "Lista los tableros publicados para esta instituci\xF3n (clave, t\xEDtulo, per\xEDodo, fuentes, cu\xE1ndo se actualiz\xF3 y cu\xE1ntos widgets tiene). Usar antes de publicar para reutilizar la clave de un tablero existente en vez de crear uno nuevo. Solo lectura.",
|
|
281
282
|
parametersSchema: { type: "object", properties: {} }
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: TOOL_NAMES.proponerRetiroTablero,
|
|
286
|
+
displayName: "Proponer retirar un tablero",
|
|
287
|
+
description: "Cuando la direcci\xF3n pide dar de baja (eliminar, borrar, sacar) un tablero: propon\xE9 retirarlo con su clave y el id de la tarea. No lo retira: el portal le muestra la propuesta a la direcci\xF3n en ese pedido y lo retira una persona. No publiques nada en esa tarea.",
|
|
288
|
+
parametersSchema: { type: "object", properties: { clave: { type: "string" }, issueId: { type: "string" } }, required: ["clave", "issueId"] }
|
|
282
289
|
}
|
|
283
290
|
],
|
|
284
291
|
apiRoutes: [
|
|
@@ -410,6 +417,38 @@ var manifest = {
|
|
|
410
417
|
capability: "api.routes.register",
|
|
411
418
|
companyResolution: { from: "query", key: "companyId" }
|
|
412
419
|
},
|
|
420
|
+
{
|
|
421
|
+
routeKey: "tableros-publicaciones",
|
|
422
|
+
method: "GET",
|
|
423
|
+
path: "/tableros-publicaciones",
|
|
424
|
+
auth: "board",
|
|
425
|
+
capability: "api.routes.register",
|
|
426
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
routeKey: "descartar-retiro-tablero",
|
|
430
|
+
method: "POST",
|
|
431
|
+
path: "/tableros/:clave/no-retirar",
|
|
432
|
+
auth: "board",
|
|
433
|
+
capability: "api.routes.register",
|
|
434
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
routeKey: "tableros-retirados",
|
|
438
|
+
method: "GET",
|
|
439
|
+
path: "/tableros-retirados",
|
|
440
|
+
auth: "board",
|
|
441
|
+
capability: "api.routes.register",
|
|
442
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
routeKey: "restaurar-tablero",
|
|
446
|
+
method: "POST",
|
|
447
|
+
path: "/tableros/:clave/restaurar",
|
|
448
|
+
auth: "board",
|
|
449
|
+
capability: "api.routes.register",
|
|
450
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
451
|
+
},
|
|
413
452
|
{
|
|
414
453
|
routeKey: "retirar-tablero",
|
|
415
454
|
method: "POST",
|
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.
|
|
39935
|
+
var PLUGIN_VERSION = "0.1.42";
|
|
39936
39936
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
39937
39937
|
var PAGE_ROUTE = "autopilot";
|
|
39938
39938
|
var TOOL_NAMES = {
|
|
@@ -39948,6 +39948,7 @@ var TOOL_NAMES = {
|
|
|
39948
39948
|
consultarIndicador: "ops_consultar_indicador",
|
|
39949
39949
|
publicarTablero: "ops_publicar_tablero",
|
|
39950
39950
|
listarTableros: "ops_listar_tableros",
|
|
39951
|
+
proponerRetiroTablero: "ops_proponer_retiro_tablero",
|
|
39951
39952
|
listarReglas: "ops_listar_reglas",
|
|
39952
39953
|
informarPruebaRegla: "ops_informar_prueba_regla",
|
|
39953
39954
|
evaluarRentabilidad: "ops_evaluar_rentabilidad"
|
|
@@ -40203,6 +40204,12 @@ var manifest = {
|
|
|
40203
40204
|
displayName: "Listar tableros",
|
|
40204
40205
|
description: "Lista los tableros publicados para esta instituci\xF3n (clave, t\xEDtulo, per\xEDodo, fuentes, cu\xE1ndo se actualiz\xF3 y cu\xE1ntos widgets tiene). Usar antes de publicar para reutilizar la clave de un tablero existente en vez de crear uno nuevo. Solo lectura.",
|
|
40205
40206
|
parametersSchema: { type: "object", properties: {} }
|
|
40207
|
+
},
|
|
40208
|
+
{
|
|
40209
|
+
name: TOOL_NAMES.proponerRetiroTablero,
|
|
40210
|
+
displayName: "Proponer retirar un tablero",
|
|
40211
|
+
description: "Cuando la direcci\xF3n pide dar de baja (eliminar, borrar, sacar) un tablero: propon\xE9 retirarlo con su clave y el id de la tarea. No lo retira: el portal le muestra la propuesta a la direcci\xF3n en ese pedido y lo retira una persona. No publiques nada en esa tarea.",
|
|
40212
|
+
parametersSchema: { type: "object", properties: { clave: { type: "string" }, issueId: { type: "string" } }, required: ["clave", "issueId"] }
|
|
40206
40213
|
}
|
|
40207
40214
|
],
|
|
40208
40215
|
apiRoutes: [
|
|
@@ -40334,6 +40341,38 @@ var manifest = {
|
|
|
40334
40341
|
capability: "api.routes.register",
|
|
40335
40342
|
companyResolution: { from: "query", key: "companyId" }
|
|
40336
40343
|
},
|
|
40344
|
+
{
|
|
40345
|
+
routeKey: "tableros-publicaciones",
|
|
40346
|
+
method: "GET",
|
|
40347
|
+
path: "/tableros-publicaciones",
|
|
40348
|
+
auth: "board",
|
|
40349
|
+
capability: "api.routes.register",
|
|
40350
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
40351
|
+
},
|
|
40352
|
+
{
|
|
40353
|
+
routeKey: "descartar-retiro-tablero",
|
|
40354
|
+
method: "POST",
|
|
40355
|
+
path: "/tableros/:clave/no-retirar",
|
|
40356
|
+
auth: "board",
|
|
40357
|
+
capability: "api.routes.register",
|
|
40358
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
40359
|
+
},
|
|
40360
|
+
{
|
|
40361
|
+
routeKey: "tableros-retirados",
|
|
40362
|
+
method: "GET",
|
|
40363
|
+
path: "/tableros-retirados",
|
|
40364
|
+
auth: "board",
|
|
40365
|
+
capability: "api.routes.register",
|
|
40366
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
40367
|
+
},
|
|
40368
|
+
{
|
|
40369
|
+
routeKey: "restaurar-tablero",
|
|
40370
|
+
method: "POST",
|
|
40371
|
+
path: "/tableros/:clave/restaurar",
|
|
40372
|
+
auth: "board",
|
|
40373
|
+
capability: "api.routes.register",
|
|
40374
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
40375
|
+
},
|
|
40337
40376
|
{
|
|
40338
40377
|
routeKey: "retirar-tablero",
|
|
40339
40378
|
method: "POST",
|
|
@@ -46955,6 +46994,7 @@ function controlarTabla(ref, columnas, filas, problemas) {
|
|
|
46955
46994
|
}
|
|
46956
46995
|
}
|
|
46957
46996
|
}
|
|
46997
|
+
var MAX_BARRAS = 12;
|
|
46958
46998
|
function validarWidgets(entrada) {
|
|
46959
46999
|
const problemas = [];
|
|
46960
47000
|
const lista3 = Array.isArray(entrada) ? entrada : [];
|
|
@@ -46985,9 +47025,9 @@ function validarWidgets(entrada) {
|
|
|
46985
47025
|
case "barras": {
|
|
46986
47026
|
const titulo = texto9(o.titulo, 120, `${ref}.titulo`, problemas, true) ?? "";
|
|
46987
47027
|
const series = arr(o.series);
|
|
46988
|
-
if (series.length === 0 || series.length >
|
|
47028
|
+
if (series.length === 0 || series.length > MAX_BARRAS) problemas.push(`${ref}.series debe tener entre 1 y ${MAX_BARRAS} barras: mostr\xE1 las principales y sum\xE1 el resto en una barra \xABOtros\xBB (el detalle completo puede ir en una tabla)`);
|
|
46989
47029
|
const unidad = texto9(o.unidad, 16, `${ref}.unidad`, problemas);
|
|
46990
|
-
const barras = series.slice(0,
|
|
47030
|
+
const barras = series.slice(0, MAX_BARRAS).map((s, j) => {
|
|
46991
47031
|
const so = s && typeof s === "object" ? s : {};
|
|
46992
47032
|
return { etiqueta: texto9(so.etiqueta, 60, `${ref}.series[${j}].etiqueta`, problemas, true) ?? "", valor: numero2(so.valor, `${ref}.series[${j}].valor`, problemas) };
|
|
46993
47033
|
});
|
|
@@ -47037,6 +47077,9 @@ function validarWidgets(entrada) {
|
|
|
47037
47077
|
}
|
|
47038
47078
|
}
|
|
47039
47079
|
});
|
|
47080
|
+
const tonos = (t6) => salida.filter((w) => w.tipo === "indicador" && w.tono === t6).length;
|
|
47081
|
+
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)");
|
|
47082
|
+
if (tonos("corte") > 1) problemas.push("como m\xE1ximo un indicador en rojo (corte) por tablero: solo lo urgente");
|
|
47040
47083
|
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 });
|
|
47041
47084
|
return salida;
|
|
47042
47085
|
}
|
|
@@ -47058,10 +47101,11 @@ function mapTablero(r) {
|
|
|
47058
47101
|
versiones: Number(r.versiones ?? 1),
|
|
47059
47102
|
createdAt: iso4(r.created_at),
|
|
47060
47103
|
updatedAt: iso4(r.updated_at),
|
|
47061
|
-
retirado: r.retirado_at ? { en: iso4(r.retirado_at), por: r.retirado_por ?? null, motivo: r.retirado_motivo ?? null } : null
|
|
47104
|
+
retirado: r.retirado_at ? { en: iso4(r.retirado_at), por: r.retirado_por ?? null, motivo: r.retirado_motivo ?? null } : null,
|
|
47105
|
+
retiroPropuesto: r.retiro_propuesto_issue ? { issueId: String(r.retiro_propuesto_issue), en: iso4(r.retiro_propuesto_at) } : null
|
|
47062
47106
|
};
|
|
47063
47107
|
}
|
|
47064
|
-
var COLS2 = "id, company_id, clave, titulo, descripcion, pedido, periodo, fuentes, widgets, agente_id, issue_id, publicado_por_tipo, publicado_por_id, versiones, created_at, updated_at, retirado_at, retirado_por, retirado_motivo";
|
|
47108
|
+
var COLS2 = "id, company_id, clave, titulo, descripcion, pedido, periodo, fuentes, widgets, agente_id, issue_id, publicado_por_tipo, publicado_por_id, versiones, created_at, updated_at, retirado_at, retirado_por, retirado_motivo, retiro_propuesto_issue, retiro_propuesto_at";
|
|
47065
47109
|
var CLAVE_RE2 = /^[a-z0-9][a-z0-9-]{1,60}$/;
|
|
47066
47110
|
async function listarTableros(ctx, companyId) {
|
|
47067
47111
|
const rows = await ctx.db.query(`SELECT ${COLS2} FROM ${t5(ctx, "tablero")} WHERE company_id = $1 AND retirado_at IS NULL ORDER BY updated_at DESC`, [companyId]);
|
|
@@ -47080,12 +47124,16 @@ async function publicarTablero(ctx, input2) {
|
|
|
47080
47124
|
const fuentes = arr(input2.fuentes).filter((f) => typeof f === "string" && f.trim()).map((f) => String(f).trim().slice(0, 120)).slice(0, 12);
|
|
47081
47125
|
const limpio = (v, max) => typeof v === "string" && v.trim() ? v.trim().slice(0, max) : null;
|
|
47082
47126
|
const previo = await leerTablero(ctx, input2.companyId, clave);
|
|
47127
|
+
if (previo?.retirado && input2.actor.tipo !== "humano") throw new OpsError("tablero_retirado", "la direcci\xF3n retir\xF3 este tablero: no se vuelve a publicar con la misma clave; si lo quieren de nuevo, lo vuelve a mostrar una persona desde el portal");
|
|
47128
|
+
input2 = { ...input2, pedido: typeof input2.pedido === "string" ? input2.pedido.replace(/\s*\((pedido (de|por)|pedido el)[^)]*\)\s*[.,;]?\s*$/i, "").trim() : input2.pedido };
|
|
47083
47129
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
47130
|
+
const registrar = async (version2) => ctx.db.execute(`INSERT INTO ${t5(ctx, "tablero_publicacion")} (id, company_id, clave, version, issue_id) VALUES ($1, $2, $3, $4, $5)`, [randomUUID5(), input2.companyId, clave, version2, input2.issueId ?? null]);
|
|
47084
47131
|
if (previo) {
|
|
47085
47132
|
await ctx.db.execute(
|
|
47086
47133
|
`UPDATE ${t5(ctx, "tablero")} SET titulo = $3, descripcion = $4, pedido = COALESCE($5, pedido), periodo = $6, fuentes = $7::jsonb, widgets = $8::jsonb, agente_id = COALESCE($9, agente_id), issue_id = COALESCE($10, issue_id), publicado_por_tipo = $11, publicado_por_id = $12, versiones = versiones + 1, updated_at = now(), retirado_at = NULL, retirado_por = NULL, retirado_motivo = NULL WHERE company_id = $1 AND clave = $2`,
|
|
47087
47134
|
[input2.companyId, clave, titulo, limpio(input2.descripcion, 600), limpio(input2.pedido, 1e3), limpio(input2.periodo, 80), JSON.stringify(fuentes), JSON.stringify(widgets), input2.agenteId ?? null, input2.issueId ?? null, input2.actor.tipo, input2.actor.id]
|
|
47088
47135
|
);
|
|
47136
|
+
await registrar(previo.versiones + 1);
|
|
47089
47137
|
await ctx.activity.log({ companyId: input2.companyId, message: `Autopilot Care: tablero \xAB${titulo}\xBB actualizado (${clave}, versi\xF3n ${previo.versiones + 1})`, metadata: { clave, widgets: widgets.length, agentId: input2.agenteId ?? null, issueId: input2.issueId ?? null } });
|
|
47090
47138
|
return {
|
|
47091
47139
|
creado: false,
|
|
@@ -47097,12 +47145,27 @@ async function publicarTablero(ctx, input2) {
|
|
|
47097
47145
|
`INSERT INTO ${t5(ctx, "tablero")} (id, company_id, clave, titulo, descripcion, pedido, periodo, fuentes, widgets, agente_id, issue_id, publicado_por_tipo, publicado_por_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9::jsonb, $10, $11, $12, $13)`,
|
|
47098
47146
|
[id3, input2.companyId, clave, titulo, limpio(input2.descripcion, 600), limpio(input2.pedido, 1e3), limpio(input2.periodo, 80), JSON.stringify(fuentes), JSON.stringify(widgets), input2.agenteId ?? null, input2.issueId ?? null, input2.actor.tipo, input2.actor.id]
|
|
47099
47147
|
);
|
|
47148
|
+
await registrar(1);
|
|
47100
47149
|
await ctx.activity.log({ companyId: input2.companyId, message: `Autopilot Care: tablero \xAB${titulo}\xBB publicado (${clave})`, metadata: { clave, widgets: widgets.length, agentId: input2.agenteId ?? null, issueId: input2.issueId ?? null } });
|
|
47101
47150
|
return {
|
|
47102
47151
|
creado: true,
|
|
47103
|
-
tablero: { id: id3, companyId: input2.companyId, clave, titulo, descripcion: limpio(input2.descripcion, 600), pedido: limpio(input2.pedido, 1e3), periodo: limpio(input2.periodo, 80), fuentes, widgets, agenteId: input2.agenteId ?? null, issueId: input2.issueId ?? null, publicadoPorTipo: input2.actor.tipo, publicadoPorId: input2.actor.id, versiones: 1, createdAt: now, updatedAt: now, retirado: null }
|
|
47152
|
+
tablero: { id: id3, companyId: input2.companyId, clave, titulo, descripcion: limpio(input2.descripcion, 600), pedido: limpio(input2.pedido, 1e3), periodo: limpio(input2.periodo, 80), fuentes, widgets, agenteId: input2.agenteId ?? null, issueId: input2.issueId ?? null, publicadoPorTipo: input2.actor.tipo, publicadoPorId: input2.actor.id, versiones: 1, createdAt: now, updatedAt: now, retirado: null, retiroPropuesto: null }
|
|
47104
47153
|
};
|
|
47105
47154
|
}
|
|
47155
|
+
async function listarTablerosRetirados(ctx, companyId) {
|
|
47156
|
+
const rows = await ctx.db.query(`SELECT ${COLS2} FROM ${t5(ctx, "tablero")} WHERE company_id = $1 AND retirado_at IS NOT NULL ORDER BY retirado_at DESC`, [companyId]);
|
|
47157
|
+
return rows.map(mapTablero);
|
|
47158
|
+
}
|
|
47159
|
+
async function restaurarTablero(ctx, input2) {
|
|
47160
|
+
if (input2.actor.tipo !== "humano") throw new OpsError("actor_no_permitido", "un tablero retirado lo vuelve a mostrar una persona de la instituci\xF3n");
|
|
47161
|
+
const clave = typeof input2.clave === "string" ? input2.clave.trim().toLowerCase() : "";
|
|
47162
|
+
const previo = CLAVE_RE2.test(clave) ? await leerTablero(ctx, input2.companyId, clave) : null;
|
|
47163
|
+
if (!previo) throw new OpsError("tablero_inexistente", "no hay un tablero con esa clave");
|
|
47164
|
+
if (!previo.retirado) return previo;
|
|
47165
|
+
await ctx.db.execute(`UPDATE ${t5(ctx, "tablero")} SET retirado_at = NULL, retirado_por = NULL, retirado_motivo = NULL, retiro_propuesto_issue = NULL, retiro_propuesto_at = NULL WHERE company_id = $1 AND clave = $2`, [input2.companyId, clave]);
|
|
47166
|
+
await ctx.activity.log({ companyId: input2.companyId, message: `Autopilot Care: tablero \xAB${previo.titulo}\xBB vuelve a mostrarse (${clave})`, metadata: { clave, por: input2.actor.id } });
|
|
47167
|
+
return { ...previo, retirado: null };
|
|
47168
|
+
}
|
|
47106
47169
|
async function retirarTablero(ctx, input2) {
|
|
47107
47170
|
if (input2.actor.tipo !== "humano") throw new OpsError("actor_no_permitido", "un tablero lo da de baja una persona de la instituci\xF3n");
|
|
47108
47171
|
const clave = typeof input2.clave === "string" ? input2.clave.trim().toLowerCase() : "";
|
|
@@ -47110,9 +47173,27 @@ async function retirarTablero(ctx, input2) {
|
|
|
47110
47173
|
if (!previo) throw new OpsError("tablero_inexistente", "no hay un tablero con esa clave");
|
|
47111
47174
|
if (previo.retirado) return previo;
|
|
47112
47175
|
const motivo = typeof input2.motivo === "string" && input2.motivo.trim() ? input2.motivo.trim().slice(0, 300) : null;
|
|
47113
|
-
await ctx.db.execute(`UPDATE ${t5(ctx, "tablero")} SET retirado_at = now(), retirado_por = $3, retirado_motivo = $4 WHERE company_id = $1 AND clave = $2`, [input2.companyId, clave, input2.actor.id, motivo]);
|
|
47176
|
+
await ctx.db.execute(`UPDATE ${t5(ctx, "tablero")} SET retirado_at = now(), retirado_por = $3, retirado_motivo = $4, retiro_propuesto_issue = NULL, retiro_propuesto_at = NULL WHERE company_id = $1 AND clave = $2`, [input2.companyId, clave, input2.actor.id, motivo]);
|
|
47114
47177
|
await ctx.activity.log({ companyId: input2.companyId, message: `Autopilot Care: tablero \xAB${previo.titulo}\xBB retirado (${clave})`, metadata: { clave, por: input2.actor.id, motivo } });
|
|
47115
|
-
return { ...previo, retirado: { en: (/* @__PURE__ */ new Date()).toISOString(), por: input2.actor.id, motivo } };
|
|
47178
|
+
return { ...previo, retirado: { en: (/* @__PURE__ */ new Date()).toISOString(), por: input2.actor.id, motivo }, retiroPropuesto: null };
|
|
47179
|
+
}
|
|
47180
|
+
async function proponerRetiroTablero(ctx, input2) {
|
|
47181
|
+
const clave = typeof input2.clave === "string" ? input2.clave.trim().toLowerCase() : "";
|
|
47182
|
+
const previo = CLAVE_RE2.test(clave) ? await leerTablero(ctx, input2.companyId, clave) : null;
|
|
47183
|
+
if (!previo) throw new OpsError("tablero_inexistente", "no hay un tablero con esa clave (revis\xE1 ops_listar_tableros)");
|
|
47184
|
+
if (previo.retirado) throw new OpsError("tablero_retirado", "ese tablero ya est\xE1 retirado");
|
|
47185
|
+
if (!input2.issueId) throw new OpsError("issue_requerido", "issueId: la tarea en la que la direcci\xF3n pidi\xF3 la baja");
|
|
47186
|
+
await ctx.db.execute(`UPDATE ${t5(ctx, "tablero")} SET retiro_propuesto_issue = $3, retiro_propuesto_at = now() WHERE company_id = $1 AND clave = $2`, [input2.companyId, clave, input2.issueId]);
|
|
47187
|
+
await ctx.activity.log({ companyId: input2.companyId, message: `Autopilot Care: se propone retirar el tablero \xAB${previo.titulo}\xBB (${clave})`, metadata: { clave, issueId: input2.issueId } });
|
|
47188
|
+
return { ...previo, retiroPropuesto: { issueId: input2.issueId, en: (/* @__PURE__ */ new Date()).toISOString() } };
|
|
47189
|
+
}
|
|
47190
|
+
async function descartarRetiroTablero(ctx, input2) {
|
|
47191
|
+
if (input2.actor.tipo !== "humano") throw new OpsError("actor_no_permitido", "lo decide una persona de la instituci\xF3n");
|
|
47192
|
+
await ctx.db.execute(`UPDATE ${t5(ctx, "tablero")} SET retiro_propuesto_issue = NULL, retiro_propuesto_at = NULL WHERE company_id = $1 AND clave = $2`, [input2.companyId, String(input2.clave).toLowerCase()]);
|
|
47193
|
+
}
|
|
47194
|
+
async function listarPublicaciones(ctx, companyId) {
|
|
47195
|
+
const rows = await ctx.db.query(`SELECT clave, version, issue_id, publicado_at FROM ${t5(ctx, "tablero_publicacion")} WHERE company_id = $1 ORDER BY publicado_at DESC LIMIT 500`, [companyId]);
|
|
47196
|
+
return rows.map((r) => ({ clave: String(r.clave), version: Number(r.version), issueId: r.issue_id ?? null, en: iso4(r.publicado_at) }));
|
|
47116
47197
|
}
|
|
47117
47198
|
|
|
47118
47199
|
// src/worker.ts
|
|
@@ -47321,6 +47402,11 @@ Transiciones registradas: ${detalle.transiciones.length}; acciones registradas:
|
|
|
47321
47402
|
data: { tablero: { ...r.tablero, widgets: void 0 }, creado: r.creado }
|
|
47322
47403
|
};
|
|
47323
47404
|
}));
|
|
47405
|
+
ctx.tools.register(TOOL_NAMES.proponerRetiroTablero, toolSchema(TOOL_NAMES.proponerRetiroTablero), async (params, runCtx) => conErrores(async () => {
|
|
47406
|
+
const p = obj(params);
|
|
47407
|
+
const r = await proponerRetiroTablero(ctx, { companyId: runCtx.companyId, clave: str3(p.clave) ?? "", issueId: uuidOrNull(p.issueId), actor: actorAgente(runCtx) });
|
|
47408
|
+
return { content: `Propuesta registrada: la direcci\xF3n ve en el portal \xABretirar ${r.titulo}\xBB en ese pedido y decide. No publiques nada en esa tarea; dejala en revisi\xF3n con el p\xE1rrafo \xABPara la direcci\xF3n\xBB.`, data: { clave: r.clave } };
|
|
47409
|
+
}));
|
|
47324
47410
|
ctx.tools.register(TOOL_NAMES.listarTableros, toolSchema(TOOL_NAMES.listarTableros), async (_params, runCtx) => conErrores(async () => {
|
|
47325
47411
|
const tableros = await listarTableros(ctx, runCtx.companyId);
|
|
47326
47412
|
const resumen = tableros.map((x) => ({ clave: x.clave, titulo: x.titulo, periodo: x.periodo, fuentes: x.fuentes, widgets: x.widgets.length, versiones: x.versiones, actualizadoEn: x.updatedAt, issueId: x.issueId }));
|
|
@@ -47448,6 +47534,19 @@ async function manejarApi(ctx, input2) {
|
|
|
47448
47534
|
const tb = await leerTablero(ctx, companyId, (input2.params.clave ?? "").toLowerCase());
|
|
47449
47535
|
return tb && !tb.retirado ? respuesta(200, tb) : respuesta(404, { error: "tablero_inexistente" });
|
|
47450
47536
|
}
|
|
47537
|
+
case "tableros-publicaciones":
|
|
47538
|
+
return respuesta(200, { publicaciones: await listarPublicaciones(ctx, companyId) });
|
|
47539
|
+
case "descartar-retiro-tablero": {
|
|
47540
|
+
if (actor.tipo !== "humano") return respuesta(403, { error: "actor_no_permitido", motivo: "lo decide una persona de la instituci\xF3n" });
|
|
47541
|
+
await descartarRetiroTablero(ctx, { companyId, clave: input2.params.clave ?? "", actor: { tipo: "humano", id: decididoPor ?? actor.id } });
|
|
47542
|
+
return respuesta(200, { ok: true });
|
|
47543
|
+
}
|
|
47544
|
+
case "tableros-retirados":
|
|
47545
|
+
return respuesta(200, { tableros: await listarTablerosRetirados(ctx, companyId) });
|
|
47546
|
+
case "restaurar-tablero": {
|
|
47547
|
+
if (actor.tipo !== "humano") return respuesta(403, { error: "actor_no_permitido", motivo: "un tablero retirado lo vuelve a mostrar una persona de la instituci\xF3n" });
|
|
47548
|
+
return respuesta(200, { tablero: await restaurarTablero(ctx, { companyId, clave: input2.params.clave ?? "", actor: { tipo: "humano", id: decididoPor ?? actor.id } }) });
|
|
47549
|
+
}
|
|
47451
47550
|
case "retirar-tablero": {
|
|
47452
47551
|
if (actor.tipo !== "humano") return respuesta(403, { error: "actor_no_permitido", motivo: "un tablero lo da de baja una persona de la instituci\xF3n" });
|
|
47453
47552
|
return respuesta(200, { tablero: await retirarTablero(ctx, { companyId, clave: input2.params.clave ?? "", motivo: str3(body.motivo), actor: { tipo: "humano", id: decididoPor ?? actor.id } }) });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- Cada publicación de un tablero, con la tarea (pedido) que la originó: así el portal sabe con certeza qué tablero salió
|
|
2
|
+
-- de qué pedido, sin deducirlo del texto.
|
|
3
|
+
CREATE TABLE plugin_autopilot_ops_1a68482ede.tablero_publicacion (
|
|
4
|
+
id uuid PRIMARY KEY,
|
|
5
|
+
company_id uuid NOT NULL REFERENCES public.companies(id) ON DELETE CASCADE,
|
|
6
|
+
clave text NOT NULL,
|
|
7
|
+
version integer NOT NULL,
|
|
8
|
+
issue_id uuid,
|
|
9
|
+
publicado_at timestamptz NOT NULL DEFAULT now()
|
|
10
|
+
);
|
|
11
|
+
CREATE INDEX tablero_publicacion_empresa ON plugin_autopilot_ops_1a68482ede.tablero_publicacion (company_id, publicado_at DESC);
|
|
12
|
+
|
|
13
|
+
-- El agente propone retirar un tablero (cuando la dirección se lo pide); retirarlo lo decide una persona.
|
|
14
|
+
ALTER TABLE plugin_autopilot_ops_1a68482ede.tablero ADD COLUMN retiro_propuesto_issue uuid;
|
|
15
|
+
ALTER TABLE plugin_autopilot_ops_1a68482ede.tablero ADD COLUMN retiro_propuesto_at timestamptz;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paperclip-plugin-autopilot-ops",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.42",
|
|
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": [
|