@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
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CREDENCIALES_DEL_GATEWAY = exports.CredencialDeTipoEquivocado = exports.MOTIVO_TIPO_EQUIVOCADO = void 0;
|
|
4
|
+
exports.comoAtribuir = comoAtribuir;
|
|
5
|
+
exports.tunelDe = tunelDe;
|
|
6
|
+
const common_1 = require("@nestjs/common");
|
|
7
|
+
/**
|
|
8
|
+
* Traduce `QuienPide` a lo que el registro de accesos entiende hoy.
|
|
9
|
+
*
|
|
10
|
+
* ⚠️ Y comprueba el id vacío, que es el agujero que el tipo no puede cerrar.
|
|
11
|
+
* Devuelve también si la atribución es utilizable, para que el adaptador lo
|
|
12
|
+
* apunte en vez de dejarlo pasar callando.
|
|
13
|
+
*/
|
|
14
|
+
function comoAtribuir(quien) {
|
|
15
|
+
if (quien.modo === 'usuario') {
|
|
16
|
+
return {
|
|
17
|
+
userId: quien.userId,
|
|
18
|
+
sourceIp: quien.sourceIp,
|
|
19
|
+
sirve: Boolean(quien.userId),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const sirve = Boolean(quien.type) && Boolean(quien.id);
|
|
23
|
+
return {
|
|
24
|
+
relatedEntity: { type: quien.type, id: quien.id },
|
|
25
|
+
/* ⚠️ En modo `nodo` el `userId` va TAMBIÉN si lo hay: quien disparó y sobre
|
|
26
|
+
qué son dos columnas distintas del registro, y quedarse con una era
|
|
27
|
+
perder la otra en silencio. */
|
|
28
|
+
userId: quien.userId,
|
|
29
|
+
sourceIp: quien.sourceIp,
|
|
30
|
+
motivo: quien.motivo,
|
|
31
|
+
sirve,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2. */
|
|
35
|
+
/**
|
|
36
|
+
* La credencial existe, pero no es del tipo que el llamante pide.
|
|
37
|
+
*
|
|
38
|
+
* ⚠️ Extiende `BadRequestException` A PROPÓSITO: mismo estado y mismo texto que
|
|
39
|
+
* lanzaba el adaptador, así que las familias que YA pasan `tipoEsperado` —mongo,
|
|
40
|
+
* postgres, las memorias del AI Node— no cambian ni una letra. Lo que añade es
|
|
41
|
+
* un `instanceof` con el que un llamante puede volver a poner SU mensaje.
|
|
42
|
+
*
|
|
43
|
+
* ## Por qué hacía falta
|
|
44
|
+
*
|
|
45
|
+
* `tipoEsperado` no es una comodidad: es dónde se comprueba, y comprobar en el
|
|
46
|
+
* gateway es lo que evita descifrar una credencial que se va a rechazar. Pero
|
|
47
|
+
* los helpers de Mailchimp y Shopify lanzaban un `ForbiddenException` con un
|
|
48
|
+
* mensaje accionable en inglés —«Connect a Shopify store from Settings →
|
|
49
|
+
* Credentials»— que SALE POR LA API al usuario. Pasar `tipoEsperado` cambiaba
|
|
50
|
+
* el mensaje y el código HTTP; no pasarlo obligaba a descifrar primero, que es
|
|
51
|
+
* peor que hoy. Con el `instanceof`, el llamante recupera su mensaje sin
|
|
52
|
+
* renunciar a la comprobación temprana.
|
|
53
|
+
*
|
|
54
|
+
* ⚠️ Lo que NUNCA hay que hacer es olfatear la prosa del mensaje con un regex:
|
|
55
|
+
* el texto del adaptador no es contrato, y el día que se reescriba, el `catch`
|
|
56
|
+
* deja de reconocerlo EN SILENCIO.
|
|
57
|
+
*
|
|
58
|
+
* ⚠️ Y `instanceof` no cruza HTTP. El día que esta costura sea red, la clase
|
|
59
|
+
* hay que rehidratarla desde el código de estado en el cliente del transporte.
|
|
60
|
+
* Es UN sitio y está a la vista; olfatear la prosa serían N escondidos.
|
|
61
|
+
*/
|
|
62
|
+
/**
|
|
63
|
+
* 🔥 El discriminador que hace que la clase sobreviva al cable.
|
|
64
|
+
*
|
|
65
|
+
* `instanceof` no cruza HTTP, y el código de estado tampoco basta: esta
|
|
66
|
+
* excepción es un **400**, igual que la validación de entrada y media docena
|
|
67
|
+
* de errores más de hw-credentials. Rehidratar «por el 400» convertiría
|
|
68
|
+
* cualquier petición mal formada en «la credencial es de otro tipo».
|
|
69
|
+
*
|
|
70
|
+
* ⚠️ Y lo que NUNCA hay que hacer es olfatear la prosa del mensaje: el texto
|
|
71
|
+
* no es contrato, y el día que se reescriba el `catch` deja de reconocerlo EN
|
|
72
|
+
* SILENCIO. Por eso va un campo propio en el CUERPO.
|
|
73
|
+
*/
|
|
74
|
+
exports.MOTIVO_TIPO_EQUIVOCADO = 'credencial-de-tipo-equivocado';
|
|
75
|
+
class CredencialDeTipoEquivocado extends common_1.BadRequestException {
|
|
76
|
+
constructor(credentialId, tipoReal, tiposEsperados) {
|
|
77
|
+
/* Cuerpo estructurado y no una cadena: los tres campos de abajo son
|
|
78
|
+
propiedades de INSTANCIA, y una instancia no viaja por una red. Sin
|
|
79
|
+
ponerlos en el cuerpo, el otro lado no puede reconstruir la excepción
|
|
80
|
+
aunque sepa que es ésta. */
|
|
81
|
+
super({
|
|
82
|
+
statusCode: 400,
|
|
83
|
+
error: 'Bad Request',
|
|
84
|
+
message: `La credencial ${credentialId} es de tipo "${tipoReal}" y aquí se esperaba ` +
|
|
85
|
+
`${tiposEsperados.map((t) => `"${t}"`).join(' o ')}.`,
|
|
86
|
+
motivo: exports.MOTIVO_TIPO_EQUIVOCADO,
|
|
87
|
+
credentialId,
|
|
88
|
+
tipoReal,
|
|
89
|
+
tiposEsperados,
|
|
90
|
+
});
|
|
91
|
+
this.credentialId = credentialId;
|
|
92
|
+
this.tipoReal = tipoReal;
|
|
93
|
+
this.tiposEsperados = tiposEsperados;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.CredencialDeTipoEquivocado = CredencialDeTipoEquivocado;
|
|
97
|
+
exports.CREDENCIALES_DEL_GATEWAY = 'CREDENCIALES_DEL_GATEWAY';
|
|
98
|
+
/**
|
|
99
|
+
* El túnel por el que hay que salir para esta credencial, si lo hay.
|
|
100
|
+
*
|
|
101
|
+
* ⚠️ Deliberadamente NO es un método del contrato. `getTunnelIdForCredential`
|
|
102
|
+
* lo era en el gateway, y al medirlo resultó ser `metadata.tunnelId` y nada
|
|
103
|
+
* más — y `describir()` ya trae `metadata`. Añadir un método habría sido una
|
|
104
|
+
* llamada de red por cada consulta para leer un campo que ya venía en la
|
|
105
|
+
* anterior.
|
|
106
|
+
*
|
|
107
|
+
* Vive aquí y no repetido en los cinco sitios que lo derivan porque «leer un
|
|
108
|
+
* campo de un mapa» es exactamente donde una copia se queda con el nombre
|
|
109
|
+
* viejo el día que el campo cambie.
|
|
110
|
+
*/
|
|
111
|
+
function tunelDe(cred) {
|
|
112
|
+
const t = cred.metadata?.tunnelId;
|
|
113
|
+
return typeof t === 'string' && t.length > 0 ? t : null;
|
|
114
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lo que un trigger de Atlassian le pide al gateway. Un método.
|
|
3
|
+
*
|
|
4
|
+
* Costura de la Fase 2, del lote de las pequeñas. `triggers.service` llamaba a
|
|
5
|
+
* `AtlassianAccountsService` para una sola cosa: apuntar las cuentas que
|
|
6
|
+
* aparecen en un payload entrante.
|
|
7
|
+
*
|
|
8
|
+
* ⚠️ Y no es un detalle administrativo. Esa lista es lo que sostiene la
|
|
9
|
+
* Personal Data Reporting API que Atlassian OBLIGA a exponer: si deja de
|
|
10
|
+
* escribirse, la app deja de poder responder a quién tiene datos de quién.
|
|
11
|
+
* Ver `project_datos_personales_atlassian`.
|
|
12
|
+
*
|
|
13
|
+
* ## Cómo viaja
|
|
14
|
+
*
|
|
15
|
+
* Bien: `unknown` entra y un número sale. Nada que aplanar.
|
|
16
|
+
*/
|
|
17
|
+
export interface CuentasDeAtlassian {
|
|
18
|
+
/** Devuelve cuántas cuentas nuevas se apuntaron. */
|
|
19
|
+
registrarDesdePayload(payload: unknown, organizationId: string): Promise<number>;
|
|
20
|
+
}
|
|
21
|
+
/** ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2. */
|
|
22
|
+
export declare const CUENTAS_DE_ATLASSIAN = "CUENTAS_DE_ATLASSIAN";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bajar un fichero de Drive y guardarlo — la petición, sin el guardado.
|
|
3
|
+
*
|
|
4
|
+
* ## Por qué
|
|
5
|
+
*
|
|
6
|
+
* `DriveDownloadHelper` lo usan dos servicios de nodo —`drive-actions` y
|
|
7
|
+
* `triggers`— y arrastra 23 ficheros del gateway: `FilesService` (24 por su
|
|
8
|
+
* cuenta), `PlansService` (13) y el adaptador de almacenamiento. Con eso,
|
|
9
|
+
* esos dos servicios no se pueden mudar a `hw-nodes` sin llevarse el
|
|
10
|
+
* almacenamiento y los planes.
|
|
11
|
+
*
|
|
12
|
+
* Medido antes de escribir esto: de todo el helper se llama **un método**,
|
|
13
|
+
* `downloadToR2`, desde dos sitios. Nada más.
|
|
14
|
+
*
|
|
15
|
+
* ⚠️ Y hubo que medirlo dos veces. El primer barrido —`this.driveDownload.` en
|
|
16
|
+
* una línea— no encontró NINGUNA llamada, porque están partidas en varias
|
|
17
|
+
* líneas. Un contrato basado en ese cero habría salido vacío.
|
|
18
|
+
*
|
|
19
|
+
* ## ⚠️ Lo que este contrato NO resuelve, y hay que saberlo
|
|
20
|
+
*
|
|
21
|
+
* `auth` es un cliente de OAuth de googleapis: un OBJETO CON MÉTODOS. Cruza
|
|
22
|
+
* bien mientras esto siga en el mismo proceso, y **no cruza una red**.
|
|
23
|
+
*
|
|
24
|
+
* Cuando `hw-nodes` sea un servicio de verdad, esta costura no se resuelve
|
|
25
|
+
* pasando el mismo argumento por HTTP: hay que decidir DÓNDE ocurre la
|
|
26
|
+
* descarga. Bajar de Drive puede hacerlo el nodo —tiene la credencial
|
|
27
|
+
* resuelta, según el plan— pero guardar en R2 es del gateway. O sea que este
|
|
28
|
+
* método se parte en dos, o el nodo sube el resultado.
|
|
29
|
+
*
|
|
30
|
+
* No es trabajo de hoy, pero está escrito aquí para que no se descubra en
|
|
31
|
+
* mitad de la mudanza. Hoy lo que se gana es que los dos servicios de nodo
|
|
32
|
+
* dejen de conocer `FilesService` y `PlansService`.
|
|
33
|
+
*/
|
|
34
|
+
/** Lo que sale. Unión discriminada, datos puros: esto sí viaja. */
|
|
35
|
+
export type ResultadoDeDescarga = {
|
|
36
|
+
ok: true;
|
|
37
|
+
fileRef: {
|
|
38
|
+
id: string;
|
|
39
|
+
key: string;
|
|
40
|
+
originalName: string;
|
|
41
|
+
mimeType: string;
|
|
42
|
+
size: number;
|
|
43
|
+
downloadUrl: string;
|
|
44
|
+
};
|
|
45
|
+
} | {
|
|
46
|
+
ok: false;
|
|
47
|
+
skipReason: 'file_too_large' | 'storage_quota_reached' | 'unsupported_workspace_type' | 'download_failed';
|
|
48
|
+
message: string;
|
|
49
|
+
sizeBytes?: number;
|
|
50
|
+
};
|
|
51
|
+
export interface PeticionDeDescarga {
|
|
52
|
+
/**
|
|
53
|
+
* ⚠️ Opaco, y es el que no viaja. Un `OAuth2Client` de googleapis es un
|
|
54
|
+
* objeto con métodos; sobrevive a una llamada en proceso y no a un
|
|
55
|
+
* `JSON.stringify`. Ver la nota de arriba.
|
|
56
|
+
*/
|
|
57
|
+
auth: unknown;
|
|
58
|
+
fileId: string;
|
|
59
|
+
orgId: string;
|
|
60
|
+
userId: string;
|
|
61
|
+
workspaceId?: string;
|
|
62
|
+
/** Metadatos que el llamante ya tenía: ahorra una llamada a Drive. */
|
|
63
|
+
knownName?: string;
|
|
64
|
+
knownMimeType?: string;
|
|
65
|
+
knownSize?: number;
|
|
66
|
+
}
|
|
67
|
+
export interface DescargaDeDrive {
|
|
68
|
+
downloadToR2(peticion: PeticionDeDescarga): Promise<ResultadoDeDescarga>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* ⚠️ Cadena y no `Symbol`, por lo mismo que los otros tokens de la Fase 2: un
|
|
72
|
+
* `Symbol` no sobrevive a una frontera de módulo duplicada.
|
|
73
|
+
*/
|
|
74
|
+
export declare const DESCARGA_DE_DRIVE = "DESCARGA_DE_DRIVE";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Bajar un fichero de Drive y guardarlo — la petición, sin el guardado.
|
|
4
|
+
*
|
|
5
|
+
* ## Por qué
|
|
6
|
+
*
|
|
7
|
+
* `DriveDownloadHelper` lo usan dos servicios de nodo —`drive-actions` y
|
|
8
|
+
* `triggers`— y arrastra 23 ficheros del gateway: `FilesService` (24 por su
|
|
9
|
+
* cuenta), `PlansService` (13) y el adaptador de almacenamiento. Con eso,
|
|
10
|
+
* esos dos servicios no se pueden mudar a `hw-nodes` sin llevarse el
|
|
11
|
+
* almacenamiento y los planes.
|
|
12
|
+
*
|
|
13
|
+
* Medido antes de escribir esto: de todo el helper se llama **un método**,
|
|
14
|
+
* `downloadToR2`, desde dos sitios. Nada más.
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ Y hubo que medirlo dos veces. El primer barrido —`this.driveDownload.` en
|
|
17
|
+
* una línea— no encontró NINGUNA llamada, porque están partidas en varias
|
|
18
|
+
* líneas. Un contrato basado en ese cero habría salido vacío.
|
|
19
|
+
*
|
|
20
|
+
* ## ⚠️ Lo que este contrato NO resuelve, y hay que saberlo
|
|
21
|
+
*
|
|
22
|
+
* `auth` es un cliente de OAuth de googleapis: un OBJETO CON MÉTODOS. Cruza
|
|
23
|
+
* bien mientras esto siga en el mismo proceso, y **no cruza una red**.
|
|
24
|
+
*
|
|
25
|
+
* Cuando `hw-nodes` sea un servicio de verdad, esta costura no se resuelve
|
|
26
|
+
* pasando el mismo argumento por HTTP: hay que decidir DÓNDE ocurre la
|
|
27
|
+
* descarga. Bajar de Drive puede hacerlo el nodo —tiene la credencial
|
|
28
|
+
* resuelta, según el plan— pero guardar en R2 es del gateway. O sea que este
|
|
29
|
+
* método se parte en dos, o el nodo sube el resultado.
|
|
30
|
+
*
|
|
31
|
+
* No es trabajo de hoy, pero está escrito aquí para que no se descubra en
|
|
32
|
+
* mitad de la mudanza. Hoy lo que se gana es que los dos servicios de nodo
|
|
33
|
+
* dejen de conocer `FilesService` y `PlansService`.
|
|
34
|
+
*/
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DESCARGA_DE_DRIVE = void 0;
|
|
37
|
+
/**
|
|
38
|
+
* ⚠️ Cadena y no `Symbol`, por lo mismo que los otros tokens de la Fase 2: un
|
|
39
|
+
* `Symbol` no sobrevive a una frontera de módulo duplicada.
|
|
40
|
+
*/
|
|
41
|
+
exports.DESCARGA_DE_DRIVE = 'DESCARGA_DE_DRIVE';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lo que el Chat Trigger necesita del nodo de IA.
|
|
3
|
+
*
|
|
4
|
+
* ## Por qué existe
|
|
5
|
+
*
|
|
6
|
+
* `chat-triggers` vive en el gateway —es uno de los cinco nodos de ORIGEN— y
|
|
7
|
+
* `ai-nodes` se muda a hw-nodes. Era el último cruce de la puerta hacia el
|
|
8
|
+
* ejecutor, y el más difícil de los tres: los otros dos eran filas de base de
|
|
9
|
+
* datos; éste lleva STREAMING.
|
|
10
|
+
*
|
|
11
|
+
* ⚠️ Y no se resolvía con el traspaso del motor. El chat NO pasa por el motor,
|
|
12
|
+
* y está escrito en tres sitios de su propio fichero: «chatTrigger is an
|
|
13
|
+
* ingress, not dispatched in-pipeline».
|
|
14
|
+
*
|
|
15
|
+
* ## 🔥 Lo que NO puede cruzar: el documento mutado
|
|
16
|
+
*
|
|
17
|
+
* El código de antes hacía esto:
|
|
18
|
+
*
|
|
19
|
+
* const aiNode = await this.aiNodeModel.findOne({…});
|
|
20
|
+
* if (trigger.systemPromptOverride) an.systemPrompt = trigger.systemPromptOverride;
|
|
21
|
+
* if (an.memoryEnabled) an.memoryUserId = sessionId;
|
|
22
|
+
* await this.aiNodesService.executeForEvent(aiNode, payload, {…});
|
|
23
|
+
*
|
|
24
|
+
* O sea: la puerta cargaba el documento del nodo, le PARCHEABA dos campos en
|
|
25
|
+
* memoria y se lo pasaba al ejecutor. Un documento de Mongoose parcheado no
|
|
26
|
+
* cruza una red.
|
|
27
|
+
*
|
|
28
|
+
* Así que la puerta deja de cargar el nodo. Manda el id y los dos ajustes, y
|
|
29
|
+
* quien tiene el nodo los aplica.
|
|
30
|
+
*
|
|
31
|
+
* ⚠️ El `sessionId` viaja SIEMPRE y se aplica sólo si el nodo tiene memoria.
|
|
32
|
+
* Antes la puerta leía `memoryEnabled` del documento para decidir; esa decisión
|
|
33
|
+
* se va con el dato, que es donde puede comprobarse.
|
|
34
|
+
*/
|
|
35
|
+
/** El nodo conectado, en lo que la puerta lee de él. Cuatro campos, medidos. */
|
|
36
|
+
export interface NodoDeIaConectado {
|
|
37
|
+
id: string;
|
|
38
|
+
/** Para el paso del historial y para decirle al widget con quién habla. */
|
|
39
|
+
name: string;
|
|
40
|
+
workspaceId: string | null;
|
|
41
|
+
/** Si el widget enseña las llamadas a herramientas mientras ocurren. */
|
|
42
|
+
muestraLasHerramientas: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface EjecucionDeIa {
|
|
45
|
+
/** El nodo conectado a ese chat, o `null` si ya no existe. */
|
|
46
|
+
nodoConectado(aiNodeId: string, orgId: string): Promise<NodoDeIaConectado | null>;
|
|
47
|
+
/**
|
|
48
|
+
* Ejecuta el nodo y va emitiendo por `CANAL_DE_STREAM`.
|
|
49
|
+
*
|
|
50
|
+
* ⚠️ El emisor NO viaja como argumento: un callback no cruza una red. Lo que
|
|
51
|
+
* viaja es el `streamId`, y el ejecutor emite contra el canal — que del otro
|
|
52
|
+
* lado es el gateway sosteniendo el SSE. Ver `canal-de-stream.ts`.
|
|
53
|
+
*/
|
|
54
|
+
ejecutarParaEvento(params: {
|
|
55
|
+
aiNodeId: string;
|
|
56
|
+
orgId: string;
|
|
57
|
+
payload: Record<string, unknown>;
|
|
58
|
+
/** Contra qué stream se emiten los trozos. */
|
|
59
|
+
streamId: string;
|
|
60
|
+
/** El prompt del trigger, si trae uno, en vez del del nodo. */
|
|
61
|
+
systemPromptOverride?: string;
|
|
62
|
+
/** ⚠️ Se aplica SÓLO si el nodo tiene la memoria puesta. Lo decide quien
|
|
63
|
+
* tiene el nodo. */
|
|
64
|
+
sessionId?: string;
|
|
65
|
+
/** Para que lo que el agente ejecute como tool caiga DENTRO de la corrida
|
|
66
|
+
* de este turno, y con el Chat Trigger de origen. */
|
|
67
|
+
correlationId?: string;
|
|
68
|
+
eventId?: string;
|
|
69
|
+
origen?: {
|
|
70
|
+
id: string;
|
|
71
|
+
name: string;
|
|
72
|
+
};
|
|
73
|
+
}): Promise<{
|
|
74
|
+
statusCode: number;
|
|
75
|
+
responseBody: string;
|
|
76
|
+
/** Lo que tardó. El paso del historial lo necesita. */
|
|
77
|
+
latencyMs: number;
|
|
78
|
+
}>;
|
|
79
|
+
}
|
|
80
|
+
/** ⚠️ Cadena y no `Symbol`. */
|
|
81
|
+
export declare const EJECUCION_DE_IA = "EJECUCION_DE_IA";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Lo que el Chat Trigger necesita del nodo de IA.
|
|
4
|
+
*
|
|
5
|
+
* ## Por qué existe
|
|
6
|
+
*
|
|
7
|
+
* `chat-triggers` vive en el gateway —es uno de los cinco nodos de ORIGEN— y
|
|
8
|
+
* `ai-nodes` se muda a hw-nodes. Era el último cruce de la puerta hacia el
|
|
9
|
+
* ejecutor, y el más difícil de los tres: los otros dos eran filas de base de
|
|
10
|
+
* datos; éste lleva STREAMING.
|
|
11
|
+
*
|
|
12
|
+
* ⚠️ Y no se resolvía con el traspaso del motor. El chat NO pasa por el motor,
|
|
13
|
+
* y está escrito en tres sitios de su propio fichero: «chatTrigger is an
|
|
14
|
+
* ingress, not dispatched in-pipeline».
|
|
15
|
+
*
|
|
16
|
+
* ## 🔥 Lo que NO puede cruzar: el documento mutado
|
|
17
|
+
*
|
|
18
|
+
* El código de antes hacía esto:
|
|
19
|
+
*
|
|
20
|
+
* const aiNode = await this.aiNodeModel.findOne({…});
|
|
21
|
+
* if (trigger.systemPromptOverride) an.systemPrompt = trigger.systemPromptOverride;
|
|
22
|
+
* if (an.memoryEnabled) an.memoryUserId = sessionId;
|
|
23
|
+
* await this.aiNodesService.executeForEvent(aiNode, payload, {…});
|
|
24
|
+
*
|
|
25
|
+
* O sea: la puerta cargaba el documento del nodo, le PARCHEABA dos campos en
|
|
26
|
+
* memoria y se lo pasaba al ejecutor. Un documento de Mongoose parcheado no
|
|
27
|
+
* cruza una red.
|
|
28
|
+
*
|
|
29
|
+
* Así que la puerta deja de cargar el nodo. Manda el id y los dos ajustes, y
|
|
30
|
+
* quien tiene el nodo los aplica.
|
|
31
|
+
*
|
|
32
|
+
* ⚠️ El `sessionId` viaja SIEMPRE y se aplica sólo si el nodo tiene memoria.
|
|
33
|
+
* Antes la puerta leía `memoryEnabled` del documento para decidir; esa decisión
|
|
34
|
+
* se va con el dato, que es donde puede comprobarse.
|
|
35
|
+
*/
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.EJECUCION_DE_IA = void 0;
|
|
38
|
+
/** ⚠️ Cadena y no `Symbol`. */
|
|
39
|
+
exports.EJECUCION_DE_IA = 'EJECUCION_DE_IA';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lo que un nodo usa para saltar a otro workspace.
|
|
3
|
+
*
|
|
4
|
+
* Costura de la Fase 2, del lote de las pequeñas. `FlowLinksService` hace dos
|
|
5
|
+
* cosas —el CRUD de los enlaces y ejecutarlos— y los nodos sólo usan la
|
|
6
|
+
* segunda. El CRUD, con su colección y su cuota, se queda en el gateway.
|
|
7
|
+
*
|
|
8
|
+
* ## ⚠️ Lo que NO se puede perder
|
|
9
|
+
*
|
|
10
|
+
* Los dos métodos llevan `callStack` y `correlationId`, y ninguno es
|
|
11
|
+
* decorativo:
|
|
12
|
+
*
|
|
13
|
+
* `callStack` es la DETECCIÓN DE CICLOS. El despachador va añadiendo
|
|
14
|
+
* cada destino; si uno ya está, aborta. Perderlo por el
|
|
15
|
+
* camino convierte un enlace circular en un bucle
|
|
16
|
+
* infinito entre workspaces.
|
|
17
|
+
* `correlationId` es lo que hace que lo de al otro lado sea la MISMA
|
|
18
|
+
* corrida y no una suelta. Sin él el historial se parte
|
|
19
|
+
* en dos justo donde más se mira.
|
|
20
|
+
*
|
|
21
|
+
* Quien implemente esto por red tiene que propagar los dos.
|
|
22
|
+
*/
|
|
23
|
+
export interface DespachoDeEnlace {
|
|
24
|
+
sourceWorkspaceId: string;
|
|
25
|
+
sourceNodeType: string;
|
|
26
|
+
sourceNodeId: string;
|
|
27
|
+
payload: Record<string, unknown>;
|
|
28
|
+
/** ⚠️ La detección de ciclos. Ver la cabecera. */
|
|
29
|
+
callStack?: string[];
|
|
30
|
+
/** ⚠️ La corrida de la que sale. Ver la cabecera. */
|
|
31
|
+
correlationId?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface ResumenDeDespacho {
|
|
34
|
+
invoked: number;
|
|
35
|
+
skipped: number;
|
|
36
|
+
errors: number;
|
|
37
|
+
}
|
|
38
|
+
export interface EnlacesDeFlujo {
|
|
39
|
+
dispatchOutgoing(orgId: string, opts: DespachoDeEnlace): Promise<ResumenDeDespacho>;
|
|
40
|
+
invokeWebhookAsTool(params: {
|
|
41
|
+
orgId: string;
|
|
42
|
+
webhookId: string;
|
|
43
|
+
payload: Record<string, unknown>;
|
|
44
|
+
callStack?: string[];
|
|
45
|
+
timeoutMs?: number;
|
|
46
|
+
source?: {
|
|
47
|
+
nodeType: string;
|
|
48
|
+
nodeId: string;
|
|
49
|
+
};
|
|
50
|
+
correlationId?: string;
|
|
51
|
+
}): Promise<{
|
|
52
|
+
ok: boolean;
|
|
53
|
+
statusCode: number;
|
|
54
|
+
body: string;
|
|
55
|
+
reason?: string;
|
|
56
|
+
}>;
|
|
57
|
+
}
|
|
58
|
+
/** ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2. */
|
|
59
|
+
export declare const ENLACES_DE_FLUJO = "ENLACES_DE_FLUJO";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Lo que un nodo usa para saltar a otro workspace.
|
|
4
|
+
*
|
|
5
|
+
* Costura de la Fase 2, del lote de las pequeñas. `FlowLinksService` hace dos
|
|
6
|
+
* cosas —el CRUD de los enlaces y ejecutarlos— y los nodos sólo usan la
|
|
7
|
+
* segunda. El CRUD, con su colección y su cuota, se queda en el gateway.
|
|
8
|
+
*
|
|
9
|
+
* ## ⚠️ Lo que NO se puede perder
|
|
10
|
+
*
|
|
11
|
+
* Los dos métodos llevan `callStack` y `correlationId`, y ninguno es
|
|
12
|
+
* decorativo:
|
|
13
|
+
*
|
|
14
|
+
* `callStack` es la DETECCIÓN DE CICLOS. El despachador va añadiendo
|
|
15
|
+
* cada destino; si uno ya está, aborta. Perderlo por el
|
|
16
|
+
* camino convierte un enlace circular en un bucle
|
|
17
|
+
* infinito entre workspaces.
|
|
18
|
+
* `correlationId` es lo que hace que lo de al otro lado sea la MISMA
|
|
19
|
+
* corrida y no una suelta. Sin él el historial se parte
|
|
20
|
+
* en dos justo donde más se mira.
|
|
21
|
+
*
|
|
22
|
+
* Quien implemente esto por red tiene que propagar los dos.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.ENLACES_DE_FLUJO = void 0;
|
|
26
|
+
/** ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2. */
|
|
27
|
+
exports.ENLACES_DE_FLUJO = 'ENLACES_DE_FLUJO';
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import type { Readable } from 'node:stream';
|
|
2
|
+
import type { FileRef } from './formas-compartidas';
|
|
3
|
+
/**
|
|
4
|
+
* Lo que un nodo le pide al gateway sobre ficheros. Nada más.
|
|
5
|
+
*
|
|
6
|
+
* ## Por qué
|
|
7
|
+
*
|
|
8
|
+
* Costura de la Fase 2. `src/nodes/` importaba `files/` 28 veces:
|
|
9
|
+
*
|
|
10
|
+
* 11 `FilesModule` cableado de DI, desaparece con la mudanza
|
|
11
|
+
* 11 `FilesService` el registro de ficheros
|
|
12
|
+
* 6 `StorageAdapter` el almacén crudo
|
|
13
|
+
*
|
|
14
|
+
* `FilesService` arrastra el cliente de S3 y la colección de ficheros. El
|
|
15
|
+
* plan lo dice claro: *«la primitiva de ficheros se queda en el gateway:
|
|
16
|
+
* sirve a Gmail, Drive y al chat»*. Así que el nodo no la lleva — la pide.
|
|
17
|
+
*
|
|
18
|
+
* ## Dos contratos y no uno, a propósito
|
|
19
|
+
*
|
|
20
|
+
* Son dos abstracciones distintas y hoy se inyectan por separado: el REGISTRO
|
|
21
|
+
* sabe de refs, permisos y URLs firmadas; el ALMACÉN sólo mueve bytes.
|
|
22
|
+
* Juntarlas obligaría a un adaptador que no aporta nada, y en la Fase 4 puede
|
|
23
|
+
* que no acaben en el mismo sitio.
|
|
24
|
+
*
|
|
25
|
+
* ## ⚠️ La superficie es la MEDIDA, no la que yo supuse
|
|
26
|
+
*
|
|
27
|
+
* Mi primera versión de este fichero declaraba 7 métodos y 2. Los de verdad
|
|
28
|
+
* son 16 y 4. La diferencia no la vio ninguna revisión: la vio un inventario
|
|
29
|
+
* que buscaba las llamadas por el NOMBRE de la propiedad inyectada, y que
|
|
30
|
+
* antes de dar un número tuvo que aprender a saltarse los literales de
|
|
31
|
+
* expresión regular —un `.replace(/"/g, ...)` en `email-actions` hacía
|
|
32
|
+
* desaparecer el fichero entero del recuento, sin error—. Si esta lista
|
|
33
|
+
* cambia, se vuelve a medir; no se estima.
|
|
34
|
+
*
|
|
35
|
+
* ## Lo que cruza bien, y lo que no va a cruzar
|
|
36
|
+
*
|
|
37
|
+
* Casi todo son datos planos o **URLs firmadas**, que es el patrón bueno para
|
|
38
|
+
* una frontera de red: el nodo recibe una URL y habla con R2 directamente,
|
|
39
|
+
* sin que los bytes pasen por el gateway.
|
|
40
|
+
*
|
|
41
|
+
* ⚠️ Tres no encajan en eso, y conviene tenerlas escritas antes de que la
|
|
42
|
+
* costura sea HTTP y se descubran midiendo latencia:
|
|
43
|
+
*
|
|
44
|
+
* `getObjectBuffer` devuelve el fichero ENTERO en memoria
|
|
45
|
+
* `registerDirectFile` lo recibe entero, en un `Buffer`
|
|
46
|
+
* `ingestStreamToLibrary` recibe un `Readable`, que ni siquiera se puede
|
|
47
|
+
* serializar
|
|
48
|
+
*
|
|
49
|
+
* Las tres tienen la misma salida el día que esto sea una llamada de red:
|
|
50
|
+
* pedir una URL firmada y que el nodo suba o baje por su cuenta, como ya hace
|
|
51
|
+
* el resto. No es trabajo de hoy.
|
|
52
|
+
*/
|
|
53
|
+
/**
|
|
54
|
+
* Lo que un nodo lee de un fichero recién registrado.
|
|
55
|
+
*
|
|
56
|
+
* ⚠️ Plano y con `id: string`, no el documento de Mongoose. Los cuatro
|
|
57
|
+
* métodos que devolvían `FileDocument` se usaban SÓLO para estos cinco
|
|
58
|
+
* campos —se comprobó llamante por llamante, no de memoria—, y un documento
|
|
59
|
+
* vivo no sobrevive a un `JSON.stringify`: pierde los métodos y `_id` se
|
|
60
|
+
* convierte en otra cosa. Aplanarlo aquí cuesta cuatro `_id.toString()`
|
|
61
|
+
* menos en los nodos; no aplanarlo cuesta un fallo en la Fase 4.
|
|
62
|
+
*/
|
|
63
|
+
export interface FicheroRegistrado {
|
|
64
|
+
id: string;
|
|
65
|
+
key: string;
|
|
66
|
+
originalName: string;
|
|
67
|
+
mimeType: string;
|
|
68
|
+
size: number;
|
|
69
|
+
}
|
|
70
|
+
/** El registro: qué ficheros hay, quién puede verlos, y por dónde bajarlos. */
|
|
71
|
+
export interface RegistroDeFicheros {
|
|
72
|
+
/** URL firmada para bajar por clave, acotada a la organización. */
|
|
73
|
+
/**
|
|
74
|
+
* Que el payload no lleve un fichero que el antivirus marcó.
|
|
75
|
+
*
|
|
76
|
+
* ⚠️ Es lo ÚNICO que el motor le pide a ficheros —un método de dieciséis—,
|
|
77
|
+
* y por eso vale un token: inyectar `FilesService` entero para esto ataba el
|
|
78
|
+
* motor a un módulo que no se muda con él.
|
|
79
|
+
*/
|
|
80
|
+
assertPayloadClean(payload: unknown): Promise<{
|
|
81
|
+
blocked: false;
|
|
82
|
+
} | {
|
|
83
|
+
blocked: true;
|
|
84
|
+
reason: string;
|
|
85
|
+
fileId: string;
|
|
86
|
+
fileName?: string;
|
|
87
|
+
}>;
|
|
88
|
+
getDownloadUrlByKey(key: string, orgId: string, expiresIn?: number): Promise<string>;
|
|
89
|
+
/**
|
|
90
|
+
* URL firmada para bajar por id.
|
|
91
|
+
*
|
|
92
|
+
* ⚠️ El servicio real devuelve además el documento; aquí no. Ninguno de sus
|
|
93
|
+
* dos llamantes lo mira: los dos desestructuran `downloadUrl` y ya.
|
|
94
|
+
*/
|
|
95
|
+
getDownloadUrl(fileId: string, orgId: string): Promise<{
|
|
96
|
+
downloadUrl: string;
|
|
97
|
+
}>;
|
|
98
|
+
/**
|
|
99
|
+
* URL firmada para subir, más el id y la clave con que confirmarlo.
|
|
100
|
+
*
|
|
101
|
+
* ⚠️ Cinco parámetros, no los ocho del servicio. Los tres que faltan
|
|
102
|
+
* —`social`, `folderId`, `paraOrganizacion`— son cosas del gateway: la
|
|
103
|
+
* biblioteca de media y la foto de la organización. Ningún nodo los pasa, y
|
|
104
|
+
* se comprobaron las llamadas una a una. Un contrato es lo que hace falta,
|
|
105
|
+
* no lo que el servicio ofrece.
|
|
106
|
+
*/
|
|
107
|
+
getUploadUrl(orgId: string, userId: string, fileName: string, mimeType: string, workspaceId?: string): Promise<{
|
|
108
|
+
fileId: string;
|
|
109
|
+
uploadUrl: string;
|
|
110
|
+
key: string;
|
|
111
|
+
}>;
|
|
112
|
+
/** Cierra una subida. Sólo se lee `size` del resultado. */
|
|
113
|
+
confirmUpload(fileId: string, orgId: string, medidas?: {
|
|
114
|
+
width?: number;
|
|
115
|
+
height?: number;
|
|
116
|
+
durationSec?: number;
|
|
117
|
+
}): Promise<FicheroRegistrado>;
|
|
118
|
+
/**
|
|
119
|
+
* Registra bytes que el nodo ya tiene: audio de un TTS, la respuesta de un
|
|
120
|
+
* HTTP, un adjunto de Gmail o de Telegram.
|
|
121
|
+
*
|
|
122
|
+
* ⚠️ Recibe el fichero ENTERO en memoria. Ver la nota de arriba.
|
|
123
|
+
*/
|
|
124
|
+
registerDirectFile(params: {
|
|
125
|
+
orgId: string;
|
|
126
|
+
workspaceId?: string;
|
|
127
|
+
ownerUserId: string;
|
|
128
|
+
buffer: Buffer;
|
|
129
|
+
mimeType: string;
|
|
130
|
+
originalName: string;
|
|
131
|
+
source: string;
|
|
132
|
+
sourceMetadata?: Record<string, unknown>;
|
|
133
|
+
skipScan?: boolean;
|
|
134
|
+
keyPrefix?: string;
|
|
135
|
+
}): Promise<FicheroRegistrado>;
|
|
136
|
+
/**
|
|
137
|
+
* Mete un flujo en la biblioteca sin pasarlo entero por memoria. Lo usa la
|
|
138
|
+
* importación de Drive.
|
|
139
|
+
*
|
|
140
|
+
* ⚠️ Un `Readable` NO se serializa. Éste es el método que obliga a rehacer
|
|
141
|
+
* el camino cuando la costura sea HTTP, no a adaptarlo.
|
|
142
|
+
*/
|
|
143
|
+
ingestStreamToLibrary(params: {
|
|
144
|
+
orgId: string;
|
|
145
|
+
userId: string;
|
|
146
|
+
originalName: string;
|
|
147
|
+
mimeType: string;
|
|
148
|
+
body: Readable;
|
|
149
|
+
folderId?: string | null;
|
|
150
|
+
source: string;
|
|
151
|
+
sourceMetadata?: Record<string, unknown>;
|
|
152
|
+
}): Promise<FicheroRegistrado>;
|
|
153
|
+
/** Un fichero por id, dentro de la organización. Lanza si no existe. */
|
|
154
|
+
findOne(fileId: string, orgId: string): Promise<FicheroRegistrado>;
|
|
155
|
+
/** Marca que se ha vuelto a usar, para que la limpieza no se lo lleve. */
|
|
156
|
+
touchFile(fileId: string, orgId: string): Promise<void>;
|
|
157
|
+
deleteFile(fileId: string, orgId: string): Promise<void>;
|
|
158
|
+
deleteByKey(key: string): Promise<void>;
|
|
159
|
+
/** Cuánto ocupa la organización. Lo mira el trigger antes de ingerir. */
|
|
160
|
+
getOrgStorageUsageBytes(orgId: string): Promise<number>;
|
|
161
|
+
/** Rellena las referencias de fichero que trae un payload. */
|
|
162
|
+
resolvePayloadFileRefs(refs: FileRef[], orgId: string): Promise<FileRef[]>;
|
|
163
|
+
getChatUploadUrl(params: {
|
|
164
|
+
chatTriggerId: string;
|
|
165
|
+
orgId: string;
|
|
166
|
+
ownerUserId: string;
|
|
167
|
+
workspaceId?: string;
|
|
168
|
+
sessionId: string;
|
|
169
|
+
fileName: string;
|
|
170
|
+
mimeType: string;
|
|
171
|
+
}): Promise<{
|
|
172
|
+
fileId: string;
|
|
173
|
+
uploadUrl: string;
|
|
174
|
+
key: string;
|
|
175
|
+
}>;
|
|
176
|
+
confirmChatUpload(fileId: string, chatTriggerId: string, sessionId: string): Promise<{
|
|
177
|
+
fileId: string;
|
|
178
|
+
mimeType: string;
|
|
179
|
+
size: number;
|
|
180
|
+
originalName: string;
|
|
181
|
+
scanStatus: string;
|
|
182
|
+
}>;
|
|
183
|
+
getChatFileStatus(fileId: string, chatTriggerId: string, sessionId: string): Promise<{
|
|
184
|
+
fileId: string;
|
|
185
|
+
scanStatus: string;
|
|
186
|
+
scanMessage?: string;
|
|
187
|
+
originalName: string;
|
|
188
|
+
mimeType: string;
|
|
189
|
+
}>;
|
|
190
|
+
/** Un adjunto de chat, comprobando que es de esa sesión y está limpio. */
|
|
191
|
+
resolveChatFileRef(fileId: string, chatTriggerId: string, sessionId: string): Promise<FileRef>;
|
|
192
|
+
}
|
|
193
|
+
/** El almacén crudo. Sólo mueve bytes. */
|
|
194
|
+
export interface AlmacenDeObjetos {
|
|
195
|
+
getDownloadUrl(key: string, expiresIn?: number): Promise<string>;
|
|
196
|
+
deleteObject(key: string): Promise<void>;
|
|
197
|
+
/**
|
|
198
|
+
* ⚠️ Opcionales las dos, igual que en el adaptador real — y no por copiar:
|
|
199
|
+
* los cinco llamantes comprueban `if (!storage.getObjectBuffer) throw`
|
|
200
|
+
* antes de usarlas. Declararlas obligatorias convertiría esas cinco
|
|
201
|
+
* guardas en código muerto y taparía el día que un almacén no las traiga.
|
|
202
|
+
*/
|
|
203
|
+
getObjectBuffer?(key: string): Promise<Buffer>;
|
|
204
|
+
uploadStream?(key: string, mimeType: string, body: Readable): Promise<void>;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* ⚠️ Cadenas y no `Symbol`, igual que el resto de tokens de la Fase 2.
|
|
208
|
+
*/
|
|
209
|
+
export declare const REGISTRO_DE_FICHEROS = "REGISTRO_DE_FICHEROS";
|
|
210
|
+
export declare const ALMACEN_DE_OBJETOS = "ALMACEN_DE_OBJETOS";
|