@hostwebhook/platform-contracts 0.1.0 → 0.3.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 +64 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/operadores.d.ts +60 -0
- package/dist/operadores.js +123 -0
- package/dist/servicios/almacenes-vectoriales.d.ts +105 -0
- package/dist/servicios/almacenes-vectoriales.js +26 -0
- package/dist/servicios/aprobaciones-pendientes.d.ts +207 -0
- package/dist/servicios/aprobaciones-pendientes.js +51 -0
- package/dist/servicios/avisos-en-vivo.d.ts +106 -0
- package/dist/servicios/avisos-en-vivo.js +60 -0
- package/dist/servicios/canal-de-stream.d.ts +78 -0
- package/dist/servicios/canal-de-stream.js +5 -0
- package/dist/servicios/conversaciones-del-chat.d.ts +139 -0
- package/dist/servicios/conversaciones-del-chat.js +51 -0
- package/dist/servicios/corridas-programadas.d.ts +29 -0
- package/dist/servicios/corridas-programadas.js +5 -0
- package/dist/servicios/credencial/permiso-sobre-credencial.d.ts +61 -0
- package/dist/servicios/credencial/permiso-sobre-credencial.js +42 -0
- package/dist/servicios/credenciales-del-gateway.d.ts +722 -0
- package/dist/servicios/credenciales-del-gateway.js +114 -0
- package/dist/servicios/cuentas-de-atlassian.d.ts +22 -0
- package/dist/servicios/cuentas-de-atlassian.js +5 -0
- package/dist/servicios/descarga-de-drive.d.ts +74 -0
- package/dist/servicios/descarga-de-drive.js +41 -0
- package/dist/servicios/ejecucion-de-ia.d.ts +81 -0
- package/dist/servicios/ejecucion-de-ia.js +39 -0
- package/dist/servicios/enlaces-de-flujo.d.ts +59 -0
- package/dist/servicios/enlaces-de-flujo.js +27 -0
- package/dist/servicios/ficheros-del-gateway.d.ts +210 -0
- package/dist/servicios/ficheros-del-gateway.js +8 -0
- package/dist/servicios/formas-compartidas.d.ts +57 -0
- package/dist/servicios/formas-compartidas.js +22 -0
- package/dist/servicios/historial-de-corridas.d.ts +75 -0
- package/dist/servicios/historial-de-corridas.js +7 -0
- package/dist/servicios/index.d.ts +57 -0
- package/dist/servicios/index.js +44 -0
- package/dist/servicios/limites-del-plan.d.ts +82 -0
- package/dist/servicios/limites-del-plan.js +41 -0
- package/dist/servicios/motor-de-ejecucion.d.ts +197 -0
- package/dist/servicios/motor-de-ejecucion.js +11 -0
- package/dist/servicios/plantillas-de-origen.d.ts +25 -0
- package/dist/servicios/plantillas-de-origen.js +5 -0
- package/dist/servicios/posts-sociales.d.ts +69 -0
- package/dist/servicios/posts-sociales.js +23 -0
- package/dist/servicios/registro-de-entregas.d.ts +93 -0
- package/dist/servicios/registro-de-entregas.js +46 -0
- package/dist/servicios/registro-de-eventos.d.ts +135 -0
- package/dist/servicios/registro-de-eventos.js +62 -0
- package/dist/servicios/restauracion-de-nodos.d.ts +53 -0
- package/dist/servicios/restauracion-de-nodos.js +4 -0
- package/dist/servicios/salud-del-webhook.d.ts +26 -0
- package/dist/servicios/salud-del-webhook.js +6 -0
- package/dist/servicios/secretos-de-firma.d.ts +26 -0
- package/dist/servicios/secretos-de-firma.js +5 -0
- package/dist/servicios/telemetria.d.ts +114 -0
- package/dist/servicios/telemetria.js +60 -0
- package/dist/servicios/trazas-de-llm.d.ts +79 -0
- package/dist/servicios/trazas-de-llm.js +23 -0
- package/dist/servicios/tuneles.d.ts +106 -0
- package/dist/servicios/tuneles.js +87 -0
- package/dist/servicios/workspace/acceso-al-recurso.d.ts +39 -0
- package/dist/servicios/workspace/acceso-al-recurso.js +5 -0
- package/dist/servicios/zona-horaria-del-usuario.d.ts +40 -0
- package/dist/servicios/zona-horaria-del-usuario.js +7 -0
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -3,20 +3,80 @@
|
|
|
3
3
|
Contratos compartidos entre los servicios de HostWebhook: lo que cruza una
|
|
4
4
|
frontera y no puede estar escrito dos veces.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
Cero dependencias de runtime, a propósito — el dashboard lo carga en el
|
|
7
|
+
navegador.
|
|
8
|
+
|
|
9
|
+
## Addons
|
|
10
|
+
|
|
11
|
+
Los complementos que se venden aparte del plan. La api decide si una petición
|
|
12
|
+
pasa; el dashboard decide si pinta la pantalla o nada en absoluto. Los dos leen
|
|
13
|
+
de aquí.
|
|
9
14
|
|
|
10
15
|
```ts
|
|
11
16
|
import { ADDONS, tieneAddon, normalizarAddons } from '@hostwebhook/platform-contracts';
|
|
12
17
|
|
|
13
|
-
tieneAddon(user.plan.addons, 'social'); // → boolean, falla
|
|
18
|
+
tieneAddon(user.plan.addons, 'social'); // → boolean, falla CERRADO
|
|
14
19
|
normalizarAddons(['social', 'social']); // → ['social']
|
|
15
20
|
```
|
|
16
21
|
|
|
22
|
+
⚠️ `tieneAddon` falla **cerrado**: ante una entrada que no entiende, dice que
|
|
23
|
+
no. Es lo contrario que `llevaValor` de abajo, y la diferencia es deliberada —
|
|
24
|
+
lo seguro al conceder acceso es negar; lo seguro al pintar un formulario es
|
|
25
|
+
enseñar el campo.
|
|
26
|
+
|
|
27
|
+
## Operadores
|
|
28
|
+
|
|
29
|
+
Las dos listas de operadores, con los tipos derivados de ellas.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import {
|
|
33
|
+
FILTER_OPERATORS, // 30 — los que evalúa `filter-utils` del node-sdk
|
|
34
|
+
ROUTER_OPERATORS, // 9 — los que evalúa `RoutersService` de la api
|
|
35
|
+
llevaValor,
|
|
36
|
+
type FilterOperator,
|
|
37
|
+
type RouterOperator,
|
|
38
|
+
} from '@hostwebhook/platform-contracts';
|
|
39
|
+
|
|
40
|
+
llevaValor('exists'); // → false: el formulario esconde el campo del valor
|
|
41
|
+
llevaValor('eq'); // → true
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
⚠️ **`ROUTER_OPERATORS` no es `FILTER_OPERATORS` recortada.** Tiene `in` y
|
|
45
|
+
`not_in`, que `filter-utils` no sabe resolver, y le faltan los otros 23. Son
|
|
46
|
+
dos evaluadores distintos. Darle los 30 al router permitiría guardar reglas que
|
|
47
|
+
su `switch` no resuelve: caerían en el `default: false` y la regla nunca
|
|
48
|
+
casaría — sin dar error, que es lo que lo hace difícil de ver.
|
|
49
|
+
|
|
50
|
+
### Por qué las listas están aquí y no junto a sus evaluadores
|
|
51
|
+
|
|
52
|
+
La regla natural sería «la lista vive pegada al `switch` que la resuelve». Pero
|
|
53
|
+
el dashboard también las necesita, para pintar los desplegables, y el evaluador
|
|
54
|
+
de filtros vive en `@hostwebhook/node-sdk`, que arrastra `re2` y medio runtime
|
|
55
|
+
de nodos. Importarlo desde el navegador para leer treinta cadenas sería pagar
|
|
56
|
+
un bundle entero por una constante.
|
|
57
|
+
|
|
58
|
+
Así que la regla se afina:
|
|
59
|
+
|
|
60
|
+
> la **definición** vive aquí, donde no hay dependencias de runtime; el **test**
|
|
61
|
+
> que la ata a su evaluador vive con el evaluador.
|
|
62
|
+
|
|
63
|
+
Quién ata cada una:
|
|
64
|
+
|
|
65
|
+
| lista | quién la ata | contra qué |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| `FILTER_OPERATORS` | `node-sdk/__tests__/una-sola-lista-de-operadores` | los `case` de `filter-utils.ts` |
|
|
68
|
+
| `ROUTER_OPERATORS` | `api/src/nodes/una-sola-lista-de-operadores.spec` | los `case` de `routers.service.ts` |
|
|
69
|
+
|
|
70
|
+
⚠️ Si un evaluador se muda, **su test se muda con él**. Una lista aquí sin
|
|
71
|
+
nadie que la ate al otro lado es una lista que vuelve a divergir — que es
|
|
72
|
+
exactamente de donde se venía: había seis copias de los operadores de filtro y
|
|
73
|
+
ninguna igual a otra.
|
|
74
|
+
|
|
17
75
|
## Qué no va aquí
|
|
18
76
|
|
|
19
77
|
- Precios e ids de Stripe: viven en la configuración de la api, que es la única
|
|
20
78
|
que habla con Stripe. Cambiarlos no puede obligar a publicar un paquete.
|
|
21
79
|
- Límites por plan: `plan.constants.ts` en la api.
|
|
80
|
+
- Etiquetas de pantalla: cómo se llama un operador para el usuario es cosa del
|
|
81
|
+
dashboard (`lib/operadores.ts`). Aquí van las claves, no los textos.
|
|
22
82
|
- Cualquier cosa que sólo lea un servicio.
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Los operadores de filtro y de router.
|
|
3
|
+
*
|
|
4
|
+
* ## Por qué están AQUÍ y no donde vive el evaluador
|
|
5
|
+
*
|
|
6
|
+
* La regla natural sería «la lista vive pegada al `switch` que la resuelve»,
|
|
7
|
+
* y durante un rato lo estuvo. Pero el dashboard también necesita las listas
|
|
8
|
+
* —para pintar los desplegables— y el evaluador de filtros vive en
|
|
9
|
+
* `@hostwebhook/node-sdk`, que arrastra `re2` y medio runtime de nodos.
|
|
10
|
+
* Importarlo desde el navegador para leer treinta cadenas sería pagar un
|
|
11
|
+
* bundle entero por una constante.
|
|
12
|
+
*
|
|
13
|
+
* Así que la regla se afina: **la definición vive aquí, en el paquete que no
|
|
14
|
+
* tiene dependencias de runtime; el test que la ata a su evaluador vive con
|
|
15
|
+
* el evaluador.** La garantía es la misma —la lista no puede separarse de lo
|
|
16
|
+
* que la resuelve— y quien sólo necesita leerla no se trae el motor.
|
|
17
|
+
*
|
|
18
|
+
* Quién ata cada una:
|
|
19
|
+
*
|
|
20
|
+
* `FILTER_OPERATORS` → `node-sdk/__tests__/una-sola-lista-de-operadores`
|
|
21
|
+
* contra los `case` de `filter-utils.ts`
|
|
22
|
+
* `ROUTER_OPERATORS` → `api/src/nodes/una-sola-lista-de-operadores.spec`
|
|
23
|
+
* contra los `case` de `routers.service.ts`
|
|
24
|
+
*
|
|
25
|
+
* ⚠️ Si alguna de esas dos casas se muda, el test se muda con ella. Una lista
|
|
26
|
+
* aquí sin nadie que la ate al otro lado es una lista que vuelve a divergir.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Lo que `filter-utils` sabe evaluar. Treinta.
|
|
30
|
+
*
|
|
31
|
+
* ⚠️ No incluye `in` ni `not_in`: esos son del router, que tiene otro
|
|
32
|
+
* evaluador. Ver `ROUTER_OPERATORS` abajo.
|
|
33
|
+
*/
|
|
34
|
+
export declare const FILTER_OPERATORS: readonly ["eq", "neq", "gt", "gte", "lt", "lte", "contains", "not_contains", "starts_with", "not_starts_with", "ends_with", "not_ends_with", "matches_regex", "is_empty", "is_not_empty", "exists", "not_exists", "has_key", "is_true", "is_false", "array_contains", "array_not_contains", "array_empty", "array_not_empty", "array_length_eq", "array_length_gt", "array_length_lt", "date_eq", "date_before", "date_after"];
|
|
35
|
+
export type FilterOperator = (typeof FILTER_OPERATORS)[number];
|
|
36
|
+
/**
|
|
37
|
+
* Lo que `RoutersService.evaluateRule` sabe resolver. Nueve.
|
|
38
|
+
*
|
|
39
|
+
* ⚠️ **Parece la lista de arriba recortada y no lo es.** Tiene dos que allí no
|
|
40
|
+
* existen —`in` y `not_in`, que parten el valor por comas y miran si el del
|
|
41
|
+
* campo está dentro— y le faltan los otros veintitrés.
|
|
42
|
+
*
|
|
43
|
+
* Por eso no se unifican. Si el router heredara los treinta, se podrían
|
|
44
|
+
* guardar reglas que su `switch` no resuelve: caerían en el `default: false`
|
|
45
|
+
* y la regla nunca casaría. No daría error — sencillamente no enrutaría, que
|
|
46
|
+
* es más difícil de ver que un fallo.
|
|
47
|
+
*
|
|
48
|
+
* Quien quiera unificarlas de verdad tiene que escribir los `case` primero.
|
|
49
|
+
*/
|
|
50
|
+
export declare const ROUTER_OPERATORS: readonly ["eq", "neq", "contains", "gt", "lt", "exists", "not_exists", "in", "not_in"];
|
|
51
|
+
export type RouterOperator = (typeof ROUTER_OPERATORS)[number];
|
|
52
|
+
/**
|
|
53
|
+
* Los operadores que no llevan valor.
|
|
54
|
+
*
|
|
55
|
+
* Sirve a los dos lados: el formulario esconde el campo del valor y el
|
|
56
|
+
* resumen de un vistazo no escribe «existe undefined». Es una propiedad del
|
|
57
|
+
* operador, no de la pantalla, así que se declara una vez.
|
|
58
|
+
*/
|
|
59
|
+
export declare const OPERADORES_SIN_VALOR: readonly ["exists", "not_exists", "is_empty", "is_not_empty", "is_true", "is_false", "array_empty", "array_not_empty"];
|
|
60
|
+
export declare function llevaValor(operador: string): boolean;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Los operadores de filtro y de router.
|
|
4
|
+
*
|
|
5
|
+
* ## Por qué están AQUÍ y no donde vive el evaluador
|
|
6
|
+
*
|
|
7
|
+
* La regla natural sería «la lista vive pegada al `switch` que la resuelve»,
|
|
8
|
+
* y durante un rato lo estuvo. Pero el dashboard también necesita las listas
|
|
9
|
+
* —para pintar los desplegables— y el evaluador de filtros vive en
|
|
10
|
+
* `@hostwebhook/node-sdk`, que arrastra `re2` y medio runtime de nodos.
|
|
11
|
+
* Importarlo desde el navegador para leer treinta cadenas sería pagar un
|
|
12
|
+
* bundle entero por una constante.
|
|
13
|
+
*
|
|
14
|
+
* Así que la regla se afina: **la definición vive aquí, en el paquete que no
|
|
15
|
+
* tiene dependencias de runtime; el test que la ata a su evaluador vive con
|
|
16
|
+
* el evaluador.** La garantía es la misma —la lista no puede separarse de lo
|
|
17
|
+
* que la resuelve— y quien sólo necesita leerla no se trae el motor.
|
|
18
|
+
*
|
|
19
|
+
* Quién ata cada una:
|
|
20
|
+
*
|
|
21
|
+
* `FILTER_OPERATORS` → `node-sdk/__tests__/una-sola-lista-de-operadores`
|
|
22
|
+
* contra los `case` de `filter-utils.ts`
|
|
23
|
+
* `ROUTER_OPERATORS` → `api/src/nodes/una-sola-lista-de-operadores.spec`
|
|
24
|
+
* contra los `case` de `routers.service.ts`
|
|
25
|
+
*
|
|
26
|
+
* ⚠️ Si alguna de esas dos casas se muda, el test se muda con ella. Una lista
|
|
27
|
+
* aquí sin nadie que la ate al otro lado es una lista que vuelve a divergir.
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.OPERADORES_SIN_VALOR = exports.ROUTER_OPERATORS = exports.FILTER_OPERATORS = void 0;
|
|
31
|
+
exports.llevaValor = llevaValor;
|
|
32
|
+
/* ── Filtros ─────────────────────────────────────────────────────── */
|
|
33
|
+
/**
|
|
34
|
+
* Lo que `filter-utils` sabe evaluar. Treinta.
|
|
35
|
+
*
|
|
36
|
+
* ⚠️ No incluye `in` ni `not_in`: esos son del router, que tiene otro
|
|
37
|
+
* evaluador. Ver `ROUTER_OPERATORS` abajo.
|
|
38
|
+
*/
|
|
39
|
+
exports.FILTER_OPERATORS = [
|
|
40
|
+
/* Comparación */
|
|
41
|
+
'eq',
|
|
42
|
+
'neq',
|
|
43
|
+
'gt',
|
|
44
|
+
'gte',
|
|
45
|
+
'lt',
|
|
46
|
+
'lte',
|
|
47
|
+
/* Texto */
|
|
48
|
+
'contains',
|
|
49
|
+
'not_contains',
|
|
50
|
+
'starts_with',
|
|
51
|
+
'not_starts_with',
|
|
52
|
+
'ends_with',
|
|
53
|
+
'not_ends_with',
|
|
54
|
+
'matches_regex',
|
|
55
|
+
'is_empty',
|
|
56
|
+
'is_not_empty',
|
|
57
|
+
/* Presencia */
|
|
58
|
+
'exists',
|
|
59
|
+
'not_exists',
|
|
60
|
+
'has_key',
|
|
61
|
+
/* Booleanos */
|
|
62
|
+
'is_true',
|
|
63
|
+
'is_false',
|
|
64
|
+
/* Listas */
|
|
65
|
+
'array_contains',
|
|
66
|
+
'array_not_contains',
|
|
67
|
+
'array_empty',
|
|
68
|
+
'array_not_empty',
|
|
69
|
+
'array_length_eq',
|
|
70
|
+
'array_length_gt',
|
|
71
|
+
'array_length_lt',
|
|
72
|
+
/* Fechas */
|
|
73
|
+
'date_eq',
|
|
74
|
+
'date_before',
|
|
75
|
+
'date_after',
|
|
76
|
+
];
|
|
77
|
+
/* ── Router ──────────────────────────────────────────────────────── */
|
|
78
|
+
/**
|
|
79
|
+
* Lo que `RoutersService.evaluateRule` sabe resolver. Nueve.
|
|
80
|
+
*
|
|
81
|
+
* ⚠️ **Parece la lista de arriba recortada y no lo es.** Tiene dos que allí no
|
|
82
|
+
* existen —`in` y `not_in`, que parten el valor por comas y miran si el del
|
|
83
|
+
* campo está dentro— y le faltan los otros veintitrés.
|
|
84
|
+
*
|
|
85
|
+
* Por eso no se unifican. Si el router heredara los treinta, se podrían
|
|
86
|
+
* guardar reglas que su `switch` no resuelve: caerían en el `default: false`
|
|
87
|
+
* y la regla nunca casaría. No daría error — sencillamente no enrutaría, que
|
|
88
|
+
* es más difícil de ver que un fallo.
|
|
89
|
+
*
|
|
90
|
+
* Quien quiera unificarlas de verdad tiene que escribir los `case` primero.
|
|
91
|
+
*/
|
|
92
|
+
exports.ROUTER_OPERATORS = [
|
|
93
|
+
'eq',
|
|
94
|
+
'neq',
|
|
95
|
+
'contains',
|
|
96
|
+
'gt',
|
|
97
|
+
'lt',
|
|
98
|
+
'exists',
|
|
99
|
+
'not_exists',
|
|
100
|
+
'in',
|
|
101
|
+
'not_in',
|
|
102
|
+
];
|
|
103
|
+
/* ── Lo que tienen en común ──────────────────────────────────────── */
|
|
104
|
+
/**
|
|
105
|
+
* Los operadores que no llevan valor.
|
|
106
|
+
*
|
|
107
|
+
* Sirve a los dos lados: el formulario esconde el campo del valor y el
|
|
108
|
+
* resumen de un vistazo no escribe «existe undefined». Es una propiedad del
|
|
109
|
+
* operador, no de la pantalla, así que se declara una vez.
|
|
110
|
+
*/
|
|
111
|
+
exports.OPERADORES_SIN_VALOR = [
|
|
112
|
+
'exists',
|
|
113
|
+
'not_exists',
|
|
114
|
+
'is_empty',
|
|
115
|
+
'is_not_empty',
|
|
116
|
+
'is_true',
|
|
117
|
+
'is_false',
|
|
118
|
+
'array_empty',
|
|
119
|
+
'array_not_empty',
|
|
120
|
+
];
|
|
121
|
+
function llevaValor(operador) {
|
|
122
|
+
return !exports.OPERADORES_SIN_VALOR.includes(operador);
|
|
123
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lo que un nodo le pide a un almacén vectorial.
|
|
3
|
+
*
|
|
4
|
+
* Costura de la Fase 2, del lote de las pequeñas. `VectorStoresService`
|
|
5
|
+
* arrastra tres colecciones, los backends de vectores —Atlas, pgvector,
|
|
6
|
+
* Pinecone—, el cifrado de credenciales y el registro de accesos. El nodo
|
|
7
|
+
* sólo consulta, inserta y borra.
|
|
8
|
+
*
|
|
9
|
+
* ## ⚠️ Lo que NO puede perderse: la marca de quién
|
|
10
|
+
*
|
|
11
|
+
* Los tres métodos que TOCAN datos llevan `userId`, `sourceIp` y
|
|
12
|
+
* `relatedEntity`, y no son telemetría de adorno: son lo que el registro de
|
|
13
|
+
* accesos del almacén guarda para poder decir quién consultó qué. Hay un
|
|
14
|
+
* guardián dedicado a eso (`el-nodo-se-firma-en-el-registro.spec.ts`) porque
|
|
15
|
+
* un nodo que se cuela sin firmar deja el registro mintiendo.
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ Y `relatedEntity` es lo que distingue a un nodo de su DUEÑO: un nodo
|
|
18
|
+
* corre con el `userId` del usuario que lo creó, así que sin la entidad
|
|
19
|
+
* relacionada la fila del registro dice «lo consultó Ariel» cuando lo
|
|
20
|
+
* consultó un flujo. Ver `project_el_registro_dice_quien`.
|
|
21
|
+
*/
|
|
22
|
+
/** Quién —o qué— está detrás de una operación sobre el almacén. */
|
|
23
|
+
export interface EntidadRelacionada {
|
|
24
|
+
type: string;
|
|
25
|
+
id: string;
|
|
26
|
+
name?: string;
|
|
27
|
+
}
|
|
28
|
+
/** Un trozo devuelto por una consulta. */
|
|
29
|
+
export interface TrozoEncontrado {
|
|
30
|
+
chunkId: string;
|
|
31
|
+
content: string;
|
|
32
|
+
score: number;
|
|
33
|
+
sourceDocId?: string;
|
|
34
|
+
sourceDocName?: string;
|
|
35
|
+
sourceDocType?: string;
|
|
36
|
+
metadata?: Record<string, unknown>;
|
|
37
|
+
createdAt?: Date;
|
|
38
|
+
}
|
|
39
|
+
import type { ChunkingStrategy } from './formas-compartidas';
|
|
40
|
+
/**
|
|
41
|
+
* El troceo que el almacén trae de fábrica.
|
|
42
|
+
*
|
|
43
|
+
* ⚠️ `chunkSize` y `chunkOverlap` son en CARACTERES, no en tokens. Está en el
|
|
44
|
+
* contrato porque es justo lo que se malinterpreta al leerlo de fuera; ver
|
|
45
|
+
* `project_vector_store_troceo`.
|
|
46
|
+
*/
|
|
47
|
+
export interface TroceoPorDefecto {
|
|
48
|
+
strategy: ChunkingStrategy;
|
|
49
|
+
chunkSize: number;
|
|
50
|
+
chunkOverlap: number;
|
|
51
|
+
separators?: string[] | null;
|
|
52
|
+
}
|
|
53
|
+
export interface ConsultaAlAlmacen {
|
|
54
|
+
query: string;
|
|
55
|
+
topK?: number;
|
|
56
|
+
filter?: Record<string, unknown>;
|
|
57
|
+
minScore?: number;
|
|
58
|
+
}
|
|
59
|
+
export interface BorradoDeTrozos {
|
|
60
|
+
chunkIds?: string[];
|
|
61
|
+
sourceDocId?: string;
|
|
62
|
+
metadata?: Record<string, unknown>;
|
|
63
|
+
}
|
|
64
|
+
export interface AlmacenesVectoriales {
|
|
65
|
+
query(storeId: string, orgId: string, dto: ConsultaAlAlmacen, userId: string, sourceIp: string, relatedEntity?: EntidadRelacionada): Promise<{
|
|
66
|
+
matches: TrozoEncontrado[];
|
|
67
|
+
tookMs: number;
|
|
68
|
+
storeName: string;
|
|
69
|
+
storeId: string;
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* El almacén, para leerle la configuración de troceo por defecto.
|
|
73
|
+
*
|
|
74
|
+
* ⚠️ Devuelve SÓLO `chunking`, que es lo único que su llamante lee — se
|
|
75
|
+
* comprobó campo por campo. El documento entero trae además el backend, sus
|
|
76
|
+
* credenciales cifradas y el recuento de vectores; nada de eso tiene por
|
|
77
|
+
* qué cruzar.
|
|
78
|
+
*
|
|
79
|
+
* ⚠️ Y lanza si el almacén no es de esa organización. Eso hay que
|
|
80
|
+
* conservarlo: es la comprobación de propiedad, no un detalle.
|
|
81
|
+
*/
|
|
82
|
+
getStore(id: string, orgId: string): Promise<{
|
|
83
|
+
chunking: TroceoPorDefecto;
|
|
84
|
+
}>;
|
|
85
|
+
findExistingHashes(storeId: string, orgId: string, hashes: string[]): Promise<string[]>;
|
|
86
|
+
deleteChunks(storeId: string, orgId: string, dto: BorradoDeTrozos, userId: string, sourceIp: string, relatedEntity?: EntidadRelacionada): Promise<{
|
|
87
|
+
deletedCount: number;
|
|
88
|
+
}>;
|
|
89
|
+
ingestPreChunked(storeId: string, orgId: string, opts: {
|
|
90
|
+
chunks: Array<{
|
|
91
|
+
content: string;
|
|
92
|
+
metadata?: Record<string, unknown>;
|
|
93
|
+
}>;
|
|
94
|
+
sourceDocId?: string;
|
|
95
|
+
sourceDocName?: string;
|
|
96
|
+
sourceDocType?: string;
|
|
97
|
+
baseMetadata?: Record<string, unknown>;
|
|
98
|
+
}, userId: string, sourceIp: string, relatedEntity?: EntidadRelacionada): Promise<{
|
|
99
|
+
chunksInserted: number;
|
|
100
|
+
skipped: number;
|
|
101
|
+
bytes: number;
|
|
102
|
+
}>;
|
|
103
|
+
}
|
|
104
|
+
/** ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2. */
|
|
105
|
+
export declare const ALMACENES_VECTORIALES = "ALMACENES_VECTORIALES";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Lo que un nodo le pide a un almacén vectorial.
|
|
4
|
+
*
|
|
5
|
+
* Costura de la Fase 2, del lote de las pequeñas. `VectorStoresService`
|
|
6
|
+
* arrastra tres colecciones, los backends de vectores —Atlas, pgvector,
|
|
7
|
+
* Pinecone—, el cifrado de credenciales y el registro de accesos. El nodo
|
|
8
|
+
* sólo consulta, inserta y borra.
|
|
9
|
+
*
|
|
10
|
+
* ## ⚠️ Lo que NO puede perderse: la marca de quién
|
|
11
|
+
*
|
|
12
|
+
* Los tres métodos que TOCAN datos llevan `userId`, `sourceIp` y
|
|
13
|
+
* `relatedEntity`, y no son telemetría de adorno: son lo que el registro de
|
|
14
|
+
* accesos del almacén guarda para poder decir quién consultó qué. Hay un
|
|
15
|
+
* guardián dedicado a eso (`el-nodo-se-firma-en-el-registro.spec.ts`) porque
|
|
16
|
+
* un nodo que se cuela sin firmar deja el registro mintiendo.
|
|
17
|
+
*
|
|
18
|
+
* ⚠️ Y `relatedEntity` es lo que distingue a un nodo de su DUEÑO: un nodo
|
|
19
|
+
* corre con el `userId` del usuario que lo creó, así que sin la entidad
|
|
20
|
+
* relacionada la fila del registro dice «lo consultó Ariel» cuando lo
|
|
21
|
+
* consultó un flujo. Ver `project_el_registro_dice_quien`.
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.ALMACENES_VECTORIALES = void 0;
|
|
25
|
+
/** ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2. */
|
|
26
|
+
exports.ALMACENES_VECTORIALES = 'ALMACENES_VECTORIALES';
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lo que una PUERTA del gateway necesita de las aprobaciones pendientes.
|
|
3
|
+
*
|
|
4
|
+
* ## Por qué existe
|
|
5
|
+
*
|
|
6
|
+
* `source-nodes/triggers` —el trigger de Telegram— y `social-posts` viven en el
|
|
7
|
+
* gateway; `approval-nodes` se muda a hw-nodes. Entre los dos había cuatro
|
|
8
|
+
* llamadas directas a `ApprovalNodesService`, y una de ellas pasaba un
|
|
9
|
+
* **documento de Mongoose vivo** al motor.
|
|
10
|
+
*
|
|
11
|
+
* ## 🔥 Por qué el contrato NO es «los mismos cuatro métodos»
|
|
12
|
+
*
|
|
13
|
+
* El código de hoy hace esto:
|
|
14
|
+
*
|
|
15
|
+
* const fila = await pendingService.resolvePendingByToken({…});
|
|
16
|
+
* await eventPipeline.resumeAfterSendAndWait(fila, {…});
|
|
17
|
+
*
|
|
18
|
+
* O sea: la puerta resuelve la fila y le pasa el DOCUMENTO al motor, que le lee
|
|
19
|
+
* seis campos más (`eventId`, `organizationId`, `webhookId`, `payload`,
|
|
20
|
+
* `eventHeaders`, `id`). Un documento de Mongoose no cruza una red, así que
|
|
21
|
+
* copiar esos métodos tal cual obligaría a serializar la fila entera y a
|
|
22
|
+
* mantener las dos formas en paralelo.
|
|
23
|
+
*
|
|
24
|
+
* Pero es que no hace falta: **el motor y las aprobaciones están del MISMO
|
|
25
|
+
* lado**. Los dos se van a hw-nodes. Lo único que cruza es la decisión —quién
|
|
26
|
+
* pulsó, cuándo, qué escribió—, y el documento se queda donde vive.
|
|
27
|
+
*
|
|
28
|
+
* Por eso hay un solo método para las dos cosas, `resolverYReanudar`.
|
|
29
|
+
*
|
|
30
|
+
* ## ⚠️ La distinción que hay que conservar
|
|
31
|
+
*
|
|
32
|
+
* Hoy los dos fallos NO se tratan igual, y eso es visible para el usuario:
|
|
33
|
+
*
|
|
34
|
+
* - si no se puede RESOLVER la fila, a Telegram le vuelve «No pude registrar
|
|
35
|
+
* tu respuesta. Inténtalo de nuevo»;
|
|
36
|
+
* - si falla el REANUDADO, se registra y ya: la fila está resuelta, el
|
|
37
|
+
* usuario ve su confirmación, y volver a lanzar aguas abajo necesitaría a
|
|
38
|
+
* un operador de todas formas.
|
|
39
|
+
*
|
|
40
|
+
* Por eso `resolverYReanudar` devuelve un booleano que habla SÓLO de la
|
|
41
|
+
* resolución. Fundir los dos fallos en uno cambiaría lo que ve el usuario sin
|
|
42
|
+
* tocar una línea de la UI.
|
|
43
|
+
*/
|
|
44
|
+
/** La fila pendiente, en lo que la puerta lee de ella. Seis campos, medidos. */
|
|
45
|
+
export interface AprobacionPendiente {
|
|
46
|
+
/** El `id` virtual de Mongoose, ya en cadena. */
|
|
47
|
+
id: string;
|
|
48
|
+
/** `pending` | `approved` | `rejected` | `expired`. */
|
|
49
|
+
status: string;
|
|
50
|
+
/** De dónde salió: `telegramSendAndWait`, `approval`, `socialMedia`… */
|
|
51
|
+
source: string;
|
|
52
|
+
/** El contexto con el que se creó. La puerta lo lee entero y opaco. */
|
|
53
|
+
pipelineContext: Record<string, unknown>;
|
|
54
|
+
/** El nodo que la pidió, o `null` si no hubo uno. */
|
|
55
|
+
sourceNodeId: string | null;
|
|
56
|
+
/** El token con el que se resuelve. */
|
|
57
|
+
resolveToken: string;
|
|
58
|
+
/** De quién es. Las puertas públicas lo necesitan para acotar sus lecturas. */
|
|
59
|
+
organizationId: string;
|
|
60
|
+
/** El evento que la creó, si lo hubo. */
|
|
61
|
+
eventId: string | null;
|
|
62
|
+
/** El webhook que lo trajo, si lo hubo. */
|
|
63
|
+
webhookId: string | null;
|
|
64
|
+
/** Cuándo se pidió y cuándo caduca — la página se lo dice a quien llega
|
|
65
|
+
* tarde. */
|
|
66
|
+
requestedAt: Date | null;
|
|
67
|
+
expiresAt: Date | null;
|
|
68
|
+
}
|
|
69
|
+
/** Quién decidió, cuándo y qué dijo. Es lo único que cruza. */
|
|
70
|
+
export interface DecisionSobrePendiente {
|
|
71
|
+
token: string;
|
|
72
|
+
aprobada: boolean;
|
|
73
|
+
decidedAt: Date;
|
|
74
|
+
/** Etiqueta legible: `telegram:ariel`, `telegram-callback`… */
|
|
75
|
+
decidedBy: string;
|
|
76
|
+
/** De dónde vino la decisión, para la auditoría. */
|
|
77
|
+
ipAddress: string;
|
|
78
|
+
comment?: string;
|
|
79
|
+
/**
|
|
80
|
+
* La respuesta escrita a mano, cuando la hay.
|
|
81
|
+
*
|
|
82
|
+
* ⚠️ Sólo el camino de texto libre de Telegram la trae; un botón no. Sale en
|
|
83
|
+
* `_waitResponse.responseText` para que un Conditional o un nodo de IA de
|
|
84
|
+
* abajo puedan leerla.
|
|
85
|
+
*/
|
|
86
|
+
responseText?: string;
|
|
87
|
+
}
|
|
88
|
+
export interface AprobacionesPendientes {
|
|
89
|
+
/** La fila de ese token, o `null` si no existe. */
|
|
90
|
+
porToken(token: string): Promise<AprobacionPendiente | null>;
|
|
91
|
+
/**
|
|
92
|
+
* La pendiente MÁS ANTIGUA de ese chat de Telegram que admita texto libre.
|
|
93
|
+
*
|
|
94
|
+
* ⚠️ Primero en llegar, primero en resolverse: si un chat tiene dos
|
|
95
|
+
* esperando, la respuesta escrita resuelve la de arriba. Y la comparación va
|
|
96
|
+
* por tipo exacto —`telegramChatId` se guarda como NÚMERO y `allowFreeText`
|
|
97
|
+
* como BOOLEANO—, o una coerción de cadena a número deja de encontrar la
|
|
98
|
+
* fila sin decir nada.
|
|
99
|
+
*/
|
|
100
|
+
deTelegram(orgId: string, chatId: number | string): Promise<AprobacionPendiente | null>;
|
|
101
|
+
/**
|
|
102
|
+
* Resuelve la fila y reanuda el flujo, del otro lado y en proceso.
|
|
103
|
+
*
|
|
104
|
+
* ⚠️ El booleano habla SÓLO de la resolución. Un fallo al reanudar se
|
|
105
|
+
* registra y devuelve `true`, porque la fila ya está resuelta y el usuario ya
|
|
106
|
+
* tiene su confirmación. Ver la cabecera.
|
|
107
|
+
*/
|
|
108
|
+
resolverYReanudar(decision: DecisionSobrePendiente): Promise<boolean>;
|
|
109
|
+
/**
|
|
110
|
+
* Resuelve por ID y reanuda. Hermana de `resolverYReanudar`.
|
|
111
|
+
*
|
|
112
|
+
* ⚠️ Son DOS y no una porque abajo hay dos métodos distintos, no dos
|
|
113
|
+
* nombres del mismo: `resolvePendingByToken` empareja por token, y `resolve`
|
|
114
|
+
* empareja por id Y comprueba `allowedApproverIds` cuando hay un usuario
|
|
115
|
+
* detrás. Fundirlas aquí escondería esa comprobación —una aprobación
|
|
116
|
+
* restringida al director la resolvía cualquier empleado hasta que se
|
|
117
|
+
* arregló—, así que cada puerta llama a la suya.
|
|
118
|
+
*
|
|
119
|
+
* El enlace de un clic no trae identidad: resuelve con una etiqueta, así que
|
|
120
|
+
* la comprobación de aprobador no le aplica. Pero el método sí es el otro.
|
|
121
|
+
*/
|
|
122
|
+
resolverPorIdYReanudar(decision: {
|
|
123
|
+
pendingId: string;
|
|
124
|
+
orgId: string;
|
|
125
|
+
aprobada: boolean;
|
|
126
|
+
decidedAt: Date;
|
|
127
|
+
/** Etiqueta, no identidad: en un enlace de un clic sólo hay una IP. */
|
|
128
|
+
decidedBy: string;
|
|
129
|
+
ipAddress?: string;
|
|
130
|
+
comment?: string;
|
|
131
|
+
}): Promise<boolean>;
|
|
132
|
+
/**
|
|
133
|
+
* Resuelve y NO encadena nada. Devuelve la fila resuelta, aplanada.
|
|
134
|
+
*
|
|
135
|
+
* ⚠️ Es la tercera resolución del contrato, y las tres existen porque lo que
|
|
136
|
+
* SIGUE a una resolución es distinto en cada puerta:
|
|
137
|
+
*
|
|
138
|
+
* `resolverYReanudar` resuelve por token → reanuda el flujo
|
|
139
|
+
* `resolverPorIdYReanudar` resuelve por id → reanuda el flujo
|
|
140
|
+
* `resolverAprobacionDeRedes` resuelve por token → nada, el que llama decide
|
|
141
|
+
*
|
|
142
|
+
* La de redes se bifurca DESPUÉS: un post del calendario se publica por su
|
|
143
|
+
* camino, que es del gateway, y el plan de un nodo por el suyo, que es del
|
|
144
|
+
* ejecutor. Fundir eso aquí obligaría al contrato a saber de calendarios.
|
|
145
|
+
*
|
|
146
|
+
* ⚠️ Devuelve la fila porque quien llama necesita su `pipelineContext` para
|
|
147
|
+
* elegir la rama — y `null` cuando no ganó la reclamación, que es lo que
|
|
148
|
+
* distingue «resuelta por mí» de «alguien se me adelantó».
|
|
149
|
+
*/
|
|
150
|
+
resolverAprobacionDeRedes(decision: {
|
|
151
|
+
token: string;
|
|
152
|
+
aprobada: boolean;
|
|
153
|
+
decidedBy: string;
|
|
154
|
+
comment?: string | null;
|
|
155
|
+
}): Promise<AprobacionPendiente | null>;
|
|
156
|
+
/**
|
|
157
|
+
* Publica el plan de una fila que QUIEN LLAMA acaba de reclamar.
|
|
158
|
+
*
|
|
159
|
+
* ⚠️ El nombre lo dice a propósito: la garantía de una sola publicación no la
|
|
160
|
+
* da el estado de la fila, la da haber ganado la reclamación. Llamar a esto
|
|
161
|
+
* sin haberla ganado es un error, y del otro lado se rechaza.
|
|
162
|
+
*/
|
|
163
|
+
publicarPlanReclamado(pendingId: string, orgId: string): Promise<{
|
|
164
|
+
statusCode: number;
|
|
165
|
+
responseBody: string;
|
|
166
|
+
}>;
|
|
167
|
+
/**
|
|
168
|
+
* ¿La página de rechazo tiene que pedir un comentario?
|
|
169
|
+
*
|
|
170
|
+
* ⚠️ Sale de `operationConfig.requireDisapproveComment` del nodo que creó la
|
|
171
|
+
* pendiente —Gmail primero, Email de respaldo, porque `sendAndWaitForResponse`
|
|
172
|
+
* pasó al nodo de Gmail al repartirse y los pendientes de antes apuntan a la
|
|
173
|
+
* fila vieja—. Es UN booleano, así que se pide en vez de traerse el nodo: el
|
|
174
|
+
* gateway sólo tiene que pintar la página.
|
|
175
|
+
*
|
|
176
|
+
* ⚠️ Y va en su propio método, no en `AprobacionPendiente`: sólo hace falta
|
|
177
|
+
* en el camino de rechazo, y meterlo en la fila obligaría a dos búsquedas
|
|
178
|
+
* extra en CADA lectura.
|
|
179
|
+
*/
|
|
180
|
+
pideComentarioAlRechazar(pendingId: string): Promise<{
|
|
181
|
+
/** Si es `false`, el rechazo sigue siendo de un clic. */
|
|
182
|
+
pide: boolean;
|
|
183
|
+
/** Los dos textos de la página, tal y como los configuró quien monta el
|
|
184
|
+
* flujo. `undefined` deja el de por defecto. */
|
|
185
|
+
label?: string;
|
|
186
|
+
placeholder?: string;
|
|
187
|
+
}>;
|
|
188
|
+
/** Da de alta la aprobación de una publicación en redes. */
|
|
189
|
+
crearParaAprobacionDeRedes(params: {
|
|
190
|
+
sourceNodeId: string;
|
|
191
|
+
eventId: string;
|
|
192
|
+
webhookId: string;
|
|
193
|
+
orgId: string;
|
|
194
|
+
payload: Record<string, unknown>;
|
|
195
|
+
headers: Record<string, unknown>;
|
|
196
|
+
plan: Record<string, unknown>;
|
|
197
|
+
timeoutMinutes: number;
|
|
198
|
+
}): Promise<{
|
|
199
|
+
id: string;
|
|
200
|
+
resolveToken: string;
|
|
201
|
+
}>;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* ⚠️ Cadena y no `Symbol`, igual que los otros tokens de esta fase: un
|
|
205
|
+
* `Symbol` no sobrevive a una frontera de módulo duplicada.
|
|
206
|
+
*/
|
|
207
|
+
export declare const APROBACIONES_PENDIENTES = "APROBACIONES_PENDIENTES";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Lo que una PUERTA del gateway necesita de las aprobaciones pendientes.
|
|
4
|
+
*
|
|
5
|
+
* ## Por qué existe
|
|
6
|
+
*
|
|
7
|
+
* `source-nodes/triggers` —el trigger de Telegram— y `social-posts` viven en el
|
|
8
|
+
* gateway; `approval-nodes` se muda a hw-nodes. Entre los dos había cuatro
|
|
9
|
+
* llamadas directas a `ApprovalNodesService`, y una de ellas pasaba un
|
|
10
|
+
* **documento de Mongoose vivo** al motor.
|
|
11
|
+
*
|
|
12
|
+
* ## 🔥 Por qué el contrato NO es «los mismos cuatro métodos»
|
|
13
|
+
*
|
|
14
|
+
* El código de hoy hace esto:
|
|
15
|
+
*
|
|
16
|
+
* const fila = await pendingService.resolvePendingByToken({…});
|
|
17
|
+
* await eventPipeline.resumeAfterSendAndWait(fila, {…});
|
|
18
|
+
*
|
|
19
|
+
* O sea: la puerta resuelve la fila y le pasa el DOCUMENTO al motor, que le lee
|
|
20
|
+
* seis campos más (`eventId`, `organizationId`, `webhookId`, `payload`,
|
|
21
|
+
* `eventHeaders`, `id`). Un documento de Mongoose no cruza una red, así que
|
|
22
|
+
* copiar esos métodos tal cual obligaría a serializar la fila entera y a
|
|
23
|
+
* mantener las dos formas en paralelo.
|
|
24
|
+
*
|
|
25
|
+
* Pero es que no hace falta: **el motor y las aprobaciones están del MISMO
|
|
26
|
+
* lado**. Los dos se van a hw-nodes. Lo único que cruza es la decisión —quién
|
|
27
|
+
* pulsó, cuándo, qué escribió—, y el documento se queda donde vive.
|
|
28
|
+
*
|
|
29
|
+
* Por eso hay un solo método para las dos cosas, `resolverYReanudar`.
|
|
30
|
+
*
|
|
31
|
+
* ## ⚠️ La distinción que hay que conservar
|
|
32
|
+
*
|
|
33
|
+
* Hoy los dos fallos NO se tratan igual, y eso es visible para el usuario:
|
|
34
|
+
*
|
|
35
|
+
* - si no se puede RESOLVER la fila, a Telegram le vuelve «No pude registrar
|
|
36
|
+
* tu respuesta. Inténtalo de nuevo»;
|
|
37
|
+
* - si falla el REANUDADO, se registra y ya: la fila está resuelta, el
|
|
38
|
+
* usuario ve su confirmación, y volver a lanzar aguas abajo necesitaría a
|
|
39
|
+
* un operador de todas formas.
|
|
40
|
+
*
|
|
41
|
+
* Por eso `resolverYReanudar` devuelve un booleano que habla SÓLO de la
|
|
42
|
+
* resolución. Fundir los dos fallos en uno cambiaría lo que ve el usuario sin
|
|
43
|
+
* tocar una línea de la UI.
|
|
44
|
+
*/
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.APROBACIONES_PENDIENTES = void 0;
|
|
47
|
+
/**
|
|
48
|
+
* ⚠️ Cadena y no `Symbol`, igual que los otros tokens de esta fase: un
|
|
49
|
+
* `Symbol` no sobrevive a una frontera de módulo duplicada.
|
|
50
|
+
*/
|
|
51
|
+
exports.APROBACIONES_PENDIENTES = 'APROBACIONES_PENDIENTES';
|