@hostwebhook/node-types 1.85.0 → 1.87.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/dist/calendar-toolkit.d.ts +3 -0
- package/dist/calendar-toolkit.js +3 -2
- package/dist/clase-de-herramienta.d.ts +61 -0
- package/dist/clase-de-herramienta.js +153 -0
- package/dist/discord-toolkit.d.ts +2 -3
- package/dist/discord-toolkit.js +3 -6
- package/dist/docs-toolkit.d.ts +2 -1
- package/dist/docs-toolkit.js +3 -6
- package/dist/drive-toolkit.d.ts +9 -4
- package/dist/drive-toolkit.js +3 -4
- package/dist/esm/calendar-toolkit.d.ts +3 -0
- package/dist/esm/calendar-toolkit.js +3 -2
- package/dist/esm/clase-de-herramienta.d.ts +61 -0
- package/dist/esm/clase-de-herramienta.js +148 -0
- package/dist/esm/discord-toolkit.d.ts +2 -3
- package/dist/esm/discord-toolkit.js +3 -6
- package/dist/esm/docs-toolkit.d.ts +2 -1
- package/dist/esm/docs-toolkit.js +3 -6
- package/dist/esm/drive-toolkit.d.ts +9 -4
- package/dist/esm/drive-toolkit.js +3 -4
- package/dist/esm/gmail-operations.d.ts +2 -0
- package/dist/esm/gmail-operations.js +7 -6
- package/dist/esm/index.d.ts +12 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/mongo-toolkit.d.ts +47 -0
- package/dist/esm/mongo-toolkit.js +98 -0
- package/dist/esm/postgres-toolkit.d.ts +52 -0
- package/dist/esm/postgres-toolkit.js +85 -0
- package/dist/esm/registry.js +10 -0
- package/dist/esm/sentiment.d.ts +39 -0
- package/dist/esm/sentiment.js +53 -0
- package/dist/esm/sheets-toolkit.d.ts +2 -1
- package/dist/esm/sheets-toolkit.js +3 -6
- package/dist/esm/slack-toolkit.d.ts +2 -3
- package/dist/esm/slack-toolkit.js +3 -4
- package/dist/esm/telegram-toolkit.d.ts +3 -0
- package/dist/esm/telegram-toolkit.js +3 -2
- package/dist/esm/toolkits.d.ts +47 -0
- package/dist/esm/toolkits.js +74 -0
- package/dist/esm/types.d.ts +111 -76
- package/dist/esm/types.js +26 -0
- package/dist/esm/ui.js +10 -1
- package/dist/gmail-operations.d.ts +2 -0
- package/dist/gmail-operations.js +11 -10
- package/dist/index.d.ts +12 -0
- package/dist/index.js +29 -4
- package/dist/mongo-toolkit.d.ts +47 -0
- package/dist/mongo-toolkit.js +101 -0
- package/dist/postgres-toolkit.d.ts +52 -0
- package/dist/postgres-toolkit.js +88 -0
- package/dist/registry.js +10 -0
- package/dist/sentiment.d.ts +39 -0
- package/dist/sentiment.js +57 -0
- package/dist/sheets-toolkit.d.ts +2 -1
- package/dist/sheets-toolkit.js +3 -6
- package/dist/slack-toolkit.d.ts +2 -3
- package/dist/slack-toolkit.js +3 -4
- package/dist/telegram-toolkit.d.ts +3 -0
- package/dist/telegram-toolkit.js +3 -2
- package/dist/toolkits.d.ts +47 -0
- package/dist/toolkits.js +79 -0
- package/dist/types.d.ts +111 -76
- package/dist/types.js +28 -0
- package/dist/ui.js +10 -1
- package/package.json +1 -1
|
@@ -39,6 +39,9 @@ export interface TelegramToolkitSpec {
|
|
|
39
39
|
/** Descripción (más reglas de uso) que ve el LLM. */
|
|
40
40
|
description: string;
|
|
41
41
|
parameters: TelegramToolkitParameter[];
|
|
42
|
+
/** Pisa o borra. Lo pone `marcarDestructivas` leyendo el verbo del
|
|
43
|
+
* nombre (ver `clase-de-herramienta.ts`), no se escribe a mano. */
|
|
44
|
+
destructive?: boolean;
|
|
42
45
|
/**
|
|
43
46
|
* Encabezado de grupo para la lista de la página de detalle. Las filas
|
|
44
47
|
* consecutivas que comparten grupo se pintan bajo un único título, así que el
|
package/dist/telegram-toolkit.js
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
24
|
exports.TELEGRAM_TOOLKIT_BY_TOOL_NAME = exports.TELEGRAM_TOOLKIT_SPECS = void 0;
|
|
25
|
+
const clase_de_herramienta_js_1 = require("./clase-de-herramienta.js");
|
|
25
26
|
const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
|
|
26
27
|
/*
|
|
27
28
|
* `sendAndWaitForResponse` NO está aquí, y es una decisión, no un olvido: la op
|
|
@@ -36,7 +37,7 @@ const p = (name, description, required = true, type = 'string') => ({ name, type
|
|
|
36
37
|
* campo en ninguna de las dos capas, y añadirlo cambiaría cuándo el AI Node pide
|
|
37
38
|
* confirmación. Eso es otra decisión, no parte de mudar el esquema de sitio.
|
|
38
39
|
*/
|
|
39
|
-
exports.TELEGRAM_TOOLKIT_SPECS = [
|
|
40
|
+
exports.TELEGRAM_TOOLKIT_SPECS = (0, clase_de_herramienta_js_1.marcarDestructivas)([
|
|
40
41
|
{
|
|
41
42
|
operation: 'sendMessage',
|
|
42
43
|
label: 'Send message',
|
|
@@ -181,6 +182,6 @@ exports.TELEGRAM_TOOLKIT_SPECS = [
|
|
|
181
182
|
p('showAlert', 'Optional. true = modal alert; false (default) = toast notification.', false, 'boolean'),
|
|
182
183
|
],
|
|
183
184
|
},
|
|
184
|
-
];
|
|
185
|
+
]);
|
|
185
186
|
/** Las herramientas por nombre, para despachar una llamada del modelo. */
|
|
186
187
|
exports.TELEGRAM_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(exports.TELEGRAM_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Los toolkits por tipo de nodo: qué herramientas expone cada nodo en modo
|
|
3
|
+
* AI toolkit, en un solo sitio.
|
|
4
|
+
*
|
|
5
|
+
* ## Para qué (2026-09-24)
|
|
6
|
+
*
|
|
7
|
+
* Para que el gate «Block destructive tool calls» del AI Node pueda preguntar
|
|
8
|
+
* por la OPERACIÓN y no sólo por el nombre. Hasta hoy sólo miraba el nombre
|
|
9
|
+
* contra unos patrones (`delete_*`, `drop_*`…), así que `update_row` de Sheets
|
|
10
|
+
* —que pisa una fila— pasaba con el gate encendido. Con esto, la herramienta
|
|
11
|
+
* que el AI Node guardó al conectar (`nodeRefType` + `operationOverride`) se
|
|
12
|
+
* resuelve aquí a su operación, y su `destructive` —que sale del verbo, ver
|
|
13
|
+
* `clase-de-herramienta.ts`— decide.
|
|
14
|
+
*
|
|
15
|
+
* ## Lo que falta aquí, a propósito
|
|
16
|
+
*
|
|
17
|
+
* WhatsApp y Contacts siguen con sus specs escritos a mano en la api y en el
|
|
18
|
+
* dashboard, no en este paquete. Hasta que se muden, quien consulte este
|
|
19
|
+
* registro tiene que componerlos por su lado — {@link esOperacionDestructiva}
|
|
20
|
+
* devuelve `null` para ellos, que significa «no lo sé», no «no es
|
|
21
|
+
* destructiva».
|
|
22
|
+
*/
|
|
23
|
+
/** Lo mínimo de una herramienta de toolkit que hace falta para decidir. */
|
|
24
|
+
export interface OperacionDeToolkit {
|
|
25
|
+
operation: string;
|
|
26
|
+
toolName: string;
|
|
27
|
+
label: string;
|
|
28
|
+
destructive?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare const TOOLKITS_POR_TIPO: Readonly<Record<string, readonly OperacionDeToolkit[]>>;
|
|
31
|
+
/**
|
|
32
|
+
* La operación de un toolkit, buscada por su nombre interno (`updateRow`) O
|
|
33
|
+
* por el nombre de su herramienta (`update_row`).
|
|
34
|
+
*
|
|
35
|
+
* Las dos formas porque las dos llegan: el AI Node guarda `operationOverride`
|
|
36
|
+
* con el nombre interno, y el servidor MCP recibe el nombre de la herramienta.
|
|
37
|
+
*/
|
|
38
|
+
export declare function operacionDeToolkit(nodeType: string | undefined | null, operacionONombre: string | undefined | null): OperacionDeToolkit | null;
|
|
39
|
+
/**
|
|
40
|
+
* ¿Es destructiva esta operación de toolkit?
|
|
41
|
+
*
|
|
42
|
+
* `null` cuando no se sabe —el tipo no es un toolkit de este paquete, o la
|
|
43
|
+
* operación no existe en él—. Quien llama decide qué hace con el «no sé»; el
|
|
44
|
+
* gate del AI Node lo combina con los patrones de nombre, que siguen ahí para
|
|
45
|
+
* las herramientas que no son de toolkit (MCP, HTTP…).
|
|
46
|
+
*/
|
|
47
|
+
export declare function esOperacionDestructiva(nodeType: string | undefined | null, operacionONombre: string | undefined | null): boolean | null;
|
package/dist/toolkits.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Los toolkits por tipo de nodo: qué herramientas expone cada nodo en modo
|
|
4
|
+
* AI toolkit, en un solo sitio.
|
|
5
|
+
*
|
|
6
|
+
* ## Para qué (2026-09-24)
|
|
7
|
+
*
|
|
8
|
+
* Para que el gate «Block destructive tool calls» del AI Node pueda preguntar
|
|
9
|
+
* por la OPERACIÓN y no sólo por el nombre. Hasta hoy sólo miraba el nombre
|
|
10
|
+
* contra unos patrones (`delete_*`, `drop_*`…), así que `update_row` de Sheets
|
|
11
|
+
* —que pisa una fila— pasaba con el gate encendido. Con esto, la herramienta
|
|
12
|
+
* que el AI Node guardó al conectar (`nodeRefType` + `operationOverride`) se
|
|
13
|
+
* resuelve aquí a su operación, y su `destructive` —que sale del verbo, ver
|
|
14
|
+
* `clase-de-herramienta.ts`— decide.
|
|
15
|
+
*
|
|
16
|
+
* ## Lo que falta aquí, a propósito
|
|
17
|
+
*
|
|
18
|
+
* WhatsApp y Contacts siguen con sus specs escritos a mano en la api y en el
|
|
19
|
+
* dashboard, no en este paquete. Hasta que se muden, quien consulte este
|
|
20
|
+
* registro tiene que componerlos por su lado — {@link esOperacionDestructiva}
|
|
21
|
+
* devuelve `null` para ellos, que significa «no lo sé», no «no es
|
|
22
|
+
* destructiva».
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.TOOLKITS_POR_TIPO = void 0;
|
|
26
|
+
exports.operacionDeToolkit = operacionDeToolkit;
|
|
27
|
+
exports.esOperacionDestructiva = esOperacionDestructiva;
|
|
28
|
+
const gmail_operations_js_1 = require("./gmail-operations.js");
|
|
29
|
+
const calendar_toolkit_js_1 = require("./calendar-toolkit.js");
|
|
30
|
+
const drive_toolkit_js_1 = require("./drive-toolkit.js");
|
|
31
|
+
const telegram_toolkit_js_1 = require("./telegram-toolkit.js");
|
|
32
|
+
const discord_toolkit_js_1 = require("./discord-toolkit.js");
|
|
33
|
+
const slack_toolkit_js_1 = require("./slack-toolkit.js");
|
|
34
|
+
const sheets_toolkit_js_1 = require("./sheets-toolkit.js");
|
|
35
|
+
const docs_toolkit_js_1 = require("./docs-toolkit.js");
|
|
36
|
+
const mongo_toolkit_js_1 = require("./mongo-toolkit.js");
|
|
37
|
+
const postgres_toolkit_js_1 = require("./postgres-toolkit.js");
|
|
38
|
+
exports.TOOLKITS_POR_TIPO = Object.freeze({
|
|
39
|
+
gmailAction: gmail_operations_js_1.GMAIL_ALL_TOOLKIT_SPECS,
|
|
40
|
+
emailAction: gmail_operations_js_1.NATIVE_EMAIL_TOOLKIT_SPECS,
|
|
41
|
+
calendarAction: calendar_toolkit_js_1.GOOGLE_CALENDAR_TOOLKIT_SPECS,
|
|
42
|
+
driveAction: drive_toolkit_js_1.DRIVE_TOOLKIT_SPECS,
|
|
43
|
+
telegramAction: telegram_toolkit_js_1.TELEGRAM_TOOLKIT_SPECS,
|
|
44
|
+
discordAction: discord_toolkit_js_1.DISCORD_TOOLKIT_SPECS,
|
|
45
|
+
slackAction: slack_toolkit_js_1.SLACK_TOOLKIT_SPECS,
|
|
46
|
+
sheetsAction: sheets_toolkit_js_1.SHEETS_TOOLKIT_SPECS,
|
|
47
|
+
docsAction: docs_toolkit_js_1.DOCS_TOOLKIT_SPECS,
|
|
48
|
+
mongoAction: mongo_toolkit_js_1.MONGO_TOOLKIT_SPECS,
|
|
49
|
+
postgresAction: postgres_toolkit_js_1.POSTGRES_TOOLKIT_SPECS,
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* La operación de un toolkit, buscada por su nombre interno (`updateRow`) O
|
|
53
|
+
* por el nombre de su herramienta (`update_row`).
|
|
54
|
+
*
|
|
55
|
+
* Las dos formas porque las dos llegan: el AI Node guarda `operationOverride`
|
|
56
|
+
* con el nombre interno, y el servidor MCP recibe el nombre de la herramienta.
|
|
57
|
+
*/
|
|
58
|
+
function operacionDeToolkit(nodeType, operacionONombre) {
|
|
59
|
+
if (!nodeType || !operacionONombre)
|
|
60
|
+
return null;
|
|
61
|
+
const toolkit = exports.TOOLKITS_POR_TIPO[nodeType];
|
|
62
|
+
if (!toolkit)
|
|
63
|
+
return null;
|
|
64
|
+
return (toolkit.find((s) => s.operation === operacionONombre) ??
|
|
65
|
+
toolkit.find((s) => s.toolName === operacionONombre) ??
|
|
66
|
+
null);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* ¿Es destructiva esta operación de toolkit?
|
|
70
|
+
*
|
|
71
|
+
* `null` cuando no se sabe —el tipo no es un toolkit de este paquete, o la
|
|
72
|
+
* operación no existe en él—. Quien llama decide qué hace con el «no sé»; el
|
|
73
|
+
* gate del AI Node lo combina con los patrones de nombre, que siguen ahí para
|
|
74
|
+
* las herramientas que no son de toolkit (MCP, HTTP…).
|
|
75
|
+
*/
|
|
76
|
+
function esOperacionDestructiva(nodeType, operacionONombre) {
|
|
77
|
+
const op = operacionDeToolkit(nodeType, operacionONombre);
|
|
78
|
+
return op ? op.destructive === true : null;
|
|
79
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -54,83 +54,15 @@ export interface NodeUIConfig {
|
|
|
54
54
|
connectModal?: 'branch' | 'output-group';
|
|
55
55
|
routerTargetLabel?: string;
|
|
56
56
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
57
|
+
* Los juegos de salidas de un nodo que ramifica: UNO, o VARIOS con su
|
|
58
|
+
* `gate` cada uno [2026-09-22]. Con varios, quien pregunta se queda con
|
|
59
|
+
* el PRIMERO cuya puerta pasa — ver `outputGroupsFor`. El AI Node los
|
|
60
|
+
* necesita porque ramifica de dos maneras según el modo: por las
|
|
61
|
+
* categorías que escribe el usuario (`classifier`) o por los cuatro
|
|
62
|
+
* puertos fijos del análisis de sentimiento (`sentiment`). La forma
|
|
63
|
+
* vieja (un objeto suelto) sigue valiendo y significa «sin alternativas».
|
|
62
64
|
*/
|
|
63
|
-
outputGroups?:
|
|
64
|
-
/** Entity field containing the array of groups (e.g. 'outputs', 'branches') */
|
|
65
|
-
field?: string;
|
|
66
|
-
/** Field within each group for display name (e.g. 'name') */
|
|
67
|
-
nameField?: string;
|
|
68
|
-
/** Field within each group for summary/description (e.g. 'field', 'filterMode') */
|
|
69
|
-
summaryField?: string;
|
|
70
|
-
/**
|
|
71
|
-
* Varios campos del grupo, unidos por espacios, cuando el resumen que
|
|
72
|
-
* de verdad identifica al grupo no cabe en uno solo.
|
|
73
|
-
*
|
|
74
|
-
* Existe por las reglas del router: su condición son TRES campos
|
|
75
|
-
* (`field`, `operator`, `value`) y el modal a medida que se retiró las
|
|
76
|
-
* pintaba juntas — «status eq active». Con un único `summaryField` una
|
|
77
|
-
* regla sin `label` quedaba en «rule 1» y «status», que no dice a qué
|
|
78
|
-
* se está conectando uno.
|
|
79
|
-
*
|
|
80
|
-
* Los campos vacíos se saltan, para que una regla con operador
|
|
81
|
-
* `exists` no salga con un espacio colgando al final.
|
|
82
|
-
*/
|
|
83
|
-
summaryFields?: string[];
|
|
84
|
-
/** Label shown in the modal (e.g. 'output', 'branch') */
|
|
85
|
-
label: string;
|
|
86
|
-
/** Entity field for the fallback/else path (e.g. 'elseOutputNodes') */
|
|
87
|
-
elseField?: string;
|
|
88
|
-
/**
|
|
89
|
-
* Fixed output groups — for nodes with a static set of outputs (e.g. approval: approve/reject).
|
|
90
|
-
* Each entry maps to a top-level entity field that holds outputNodes[].
|
|
91
|
-
* When set, `field`/`nameField` are ignored — modal shows these fixed options.
|
|
92
|
-
*/
|
|
93
|
-
fixedGroups?: Array<{
|
|
94
|
-
name: string;
|
|
95
|
-
field: string;
|
|
96
|
-
}>;
|
|
97
|
-
/**
|
|
98
|
-
* Optional content-aware gate — the output-group modal only opens
|
|
99
|
-
* when `entity[gate.field] === gate.value`. Lets a node opt into
|
|
100
|
-
* multi-branch routing only in some modes (e.g. AI Node in
|
|
101
|
-
* classifier mode) without trapping every connection in the
|
|
102
|
-
* picker the rest of the time. When the gate fails, the
|
|
103
|
-
* FlowCanvas falls through to the universal
|
|
104
|
-
* `entity.outputNodes` connect path.
|
|
105
|
-
*/
|
|
106
|
-
gate?: {
|
|
107
|
-
field: string;
|
|
108
|
-
value: unknown;
|
|
109
|
-
};
|
|
110
|
-
/**
|
|
111
|
-
* Qué es el puerto `main` de este nodo MIENTRAS su contenedor está
|
|
112
|
-
* vacío. Sin esto, conectar a un nodo que ramifica y todavía no tiene
|
|
113
|
-
* ninguna salida escribía una arista en `main` **en silencio**, y el
|
|
114
|
-
* usuario no tenía forma de saber qué había pasado con ella.
|
|
115
|
-
*
|
|
116
|
-
* No hay una respuesta única, y por eso es un dato y no una regla.
|
|
117
|
-
* Medido en `api/src/common/pipeline-run.service.ts`:
|
|
118
|
-
*
|
|
119
|
-
* 'passthrough' — router sin reglas: `rules.length === 0
|
|
120
|
-
* ? refsForPort(entity, MAIN_PORT) : []`. La
|
|
121
|
-
* arista SÍ dispara... hasta que se cree la
|
|
122
|
-
* primera regla, y entonces deja de hacerlo
|
|
123
|
-
* sin avisar.
|
|
124
|
-
* 'never-dispatched' — split: no tiene salida `main`, y el propio
|
|
125
|
-
* despachador registra un warning por cada
|
|
126
|
-
* arista que se quedó en ese puerto.
|
|
127
|
-
*
|
|
128
|
-
* Se declara sólo donde se ha medido. Sin declarar, el lienzo se
|
|
129
|
-
* comporta como siempre: conecta por el camino universal y no dice
|
|
130
|
-
* nada, que es lo que hacía antes de existir este campo.
|
|
131
|
-
*/
|
|
132
|
-
emptyContainerMain?: 'passthrough' | 'never-dispatched';
|
|
133
|
-
};
|
|
65
|
+
outputGroups?: OutputGroupsConfig | OutputGroupsConfig[];
|
|
134
66
|
/**
|
|
135
67
|
* Maps output fields to alternate payload fields on the source entity.
|
|
136
68
|
* When a downstream node is connected via a specific output field,
|
|
@@ -224,3 +156,106 @@ export interface PrefixMapping {
|
|
|
224
156
|
/** Canvas node type used in React Flow nodeTypes map (e.g. "cache-node", "email-action") */
|
|
225
157
|
canvasType: string;
|
|
226
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Un juego de salidas de un nodo que ramifica.
|
|
161
|
+
*
|
|
162
|
+
* Vivía dentro de `NodeUIConfig.special` como un objeto anónimo; sale aquí
|
|
163
|
+
* [2026-09-22] para poder declarar varios por nodo y para que quien los lea
|
|
164
|
+
* nombre el tipo en vez de repetir su forma.
|
|
165
|
+
*/
|
|
166
|
+
export interface OutputGroupsConfig {
|
|
167
|
+
/** Entity field containing the array of groups (e.g. 'outputs', 'branches') */
|
|
168
|
+
field?: string;
|
|
169
|
+
/** Field within each group for display name (e.g. 'name') */
|
|
170
|
+
nameField?: string;
|
|
171
|
+
/** Field within each group for summary/description (e.g. 'field', 'filterMode') */
|
|
172
|
+
summaryField?: string;
|
|
173
|
+
/**
|
|
174
|
+
* Varios campos del grupo, unidos por espacios, cuando el resumen que
|
|
175
|
+
* de verdad identifica al grupo no cabe en uno solo.
|
|
176
|
+
*
|
|
177
|
+
* Existe por las reglas del router: su condición son TRES campos
|
|
178
|
+
* (`field`, `operator`, `value`) y el modal a medida que se retiró las
|
|
179
|
+
* pintaba juntas — «status eq active». Con un único `summaryField` una
|
|
180
|
+
* regla sin `label` quedaba en «rule 1» y «status», que no dice a qué
|
|
181
|
+
* se está conectando uno.
|
|
182
|
+
*
|
|
183
|
+
* Los campos vacíos se saltan, para que una regla con operador
|
|
184
|
+
* `exists` no salga con un espacio colgando al final.
|
|
185
|
+
*/
|
|
186
|
+
summaryFields?: string[];
|
|
187
|
+
/** Label shown in the modal (e.g. 'output', 'branch') */
|
|
188
|
+
label: string;
|
|
189
|
+
/** Entity field for the fallback/else path (e.g. 'elseOutputNodes') */
|
|
190
|
+
elseField?: string;
|
|
191
|
+
/**
|
|
192
|
+
* Fixed output groups — for nodes with a static set of outputs (e.g. approval: approve/reject).
|
|
193
|
+
* When set, `field`/`nameField` are ignored — modal shows these fixed options.
|
|
194
|
+
*
|
|
195
|
+
* Cada entrada nombra su puerto de una de dos formas, y sólo una:
|
|
196
|
+
* · `field` — un campo de primer nivel de los de ANTES de que los puertos
|
|
197
|
+
* existieran (`rejectionOutputNodes` → puerto `rejected`); se traduce
|
|
198
|
+
* con el mapa de campos heredados.
|
|
199
|
+
* · `port` — el puerto, tal cual. Es lo que usan las salidas nacidas ya
|
|
200
|
+
* con puertos (las cuatro de sentiment): inventarles un campo heredado
|
|
201
|
+
* que nunca existió sería mentir en el registro.
|
|
202
|
+
*/
|
|
203
|
+
fixedGroups?: Array<{
|
|
204
|
+
name: string;
|
|
205
|
+
field?: string;
|
|
206
|
+
port?: string;
|
|
207
|
+
/** Una línea de qué entrega ese puerto, para el modal de conexión. */
|
|
208
|
+
summary?: string;
|
|
209
|
+
}>;
|
|
210
|
+
/**
|
|
211
|
+
* Optional content-aware gate — the output-group modal only opens
|
|
212
|
+
* when `entity[gate.field] === gate.value`. Lets a node opt into
|
|
213
|
+
* multi-branch routing only in some modes (e.g. AI Node in
|
|
214
|
+
* classifier mode) without trapping every connection in the
|
|
215
|
+
* picker the rest of the time. When the gate fails, the
|
|
216
|
+
* FlowCanvas falls through to the universal
|
|
217
|
+
* `entity.outputNodes` connect path.
|
|
218
|
+
*/
|
|
219
|
+
gate?: {
|
|
220
|
+
field: string;
|
|
221
|
+
value: unknown;
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Qué es el puerto `main` de este nodo MIENTRAS su contenedor está
|
|
225
|
+
* vacío. Sin esto, conectar a un nodo que ramifica y todavía no tiene
|
|
226
|
+
* ninguna salida escribía una arista en `main` **en silencio**, y el
|
|
227
|
+
* usuario no tenía forma de saber qué había pasado con ella.
|
|
228
|
+
*
|
|
229
|
+
* No hay una respuesta única, y por eso es un dato y no una regla.
|
|
230
|
+
* Medido en `api/src/common/pipeline-run.service.ts`:
|
|
231
|
+
*
|
|
232
|
+
* 'passthrough' — router sin reglas: `rules.length === 0
|
|
233
|
+
* ? refsForPort(entity, MAIN_PORT) : []`. La
|
|
234
|
+
* arista SÍ dispara... hasta que se cree la
|
|
235
|
+
* primera regla, y entonces deja de hacerlo
|
|
236
|
+
* sin avisar.
|
|
237
|
+
* 'never-dispatched' — split: no tiene salida `main`, y el propio
|
|
238
|
+
* despachador registra un warning por cada
|
|
239
|
+
* arista que se quedó en ese puerto.
|
|
240
|
+
*
|
|
241
|
+
* Se declara sólo donde se ha medido. Sin declarar, el lienzo se
|
|
242
|
+
* comporta como siempre: conecta por el camino universal y no dice
|
|
243
|
+
* nada, que es lo que hacía antes de existir este campo.
|
|
244
|
+
*/
|
|
245
|
+
emptyContainerMain?: 'passthrough' | 'never-dispatched';
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* El juego de salidas que aplica AHORA a esta entidad, o `undefined` si el
|
|
249
|
+
* nodo no ramifica en este momento.
|
|
250
|
+
*
|
|
251
|
+
* Es la única lectura de `special.outputGroups` que entiende las dos formas
|
|
252
|
+
* (uno o varios) y las dos puertas (con `gate` o sin ella). Todo el que
|
|
253
|
+
* pregunte tiene que pasar por aquí: el lienzo, el modal de conexión y el
|
|
254
|
+
* despachador. Escribirlo cada uno a su manera es como el router acabó con un
|
|
255
|
+
* camino propio que nadie arregló al arreglar el de los demás.
|
|
256
|
+
*/
|
|
257
|
+
export declare function outputGroupsFor(config: NodeUIConfig | undefined | null, entity: Record<string, unknown> | undefined | null): OutputGroupsConfig | undefined;
|
|
258
|
+
/** Todos los juegos declarados, pasen o no su puerta. Para quien necesita el
|
|
259
|
+
* UNIVERSO de puertos que un tipo puede emitir (la auditoría de puertos),
|
|
260
|
+
* no el de ahora mismo. */
|
|
261
|
+
export declare function todosLosOutputGroups(config: NodeUIConfig | undefined | null): OutputGroupsConfig[];
|
package/dist/types.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.singleMeta = singleMeta;
|
|
4
4
|
exports.iterableMeta = iterableMeta;
|
|
5
|
+
exports.outputGroupsFor = outputGroupsFor;
|
|
6
|
+
exports.todosLosOutputGroups = todosLosOutputGroups;
|
|
5
7
|
/** Helper to create _meta for single-object outputs */
|
|
6
8
|
function singleMeta() {
|
|
7
9
|
return { iterable: false, count: 1 };
|
|
@@ -10,3 +12,29 @@ function singleMeta() {
|
|
|
10
12
|
function iterableMeta(field, count) {
|
|
11
13
|
return { iterable: true, iterateField: field, count };
|
|
12
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* El juego de salidas que aplica AHORA a esta entidad, o `undefined` si el
|
|
17
|
+
* nodo no ramifica en este momento.
|
|
18
|
+
*
|
|
19
|
+
* Es la única lectura de `special.outputGroups` que entiende las dos formas
|
|
20
|
+
* (uno o varios) y las dos puertas (con `gate` o sin ella). Todo el que
|
|
21
|
+
* pregunte tiene que pasar por aquí: el lienzo, el modal de conexión y el
|
|
22
|
+
* despachador. Escribirlo cada uno a su manera es como el router acabó con un
|
|
23
|
+
* camino propio que nadie arregló al arreglar el de los demás.
|
|
24
|
+
*/
|
|
25
|
+
function outputGroupsFor(config, entity) {
|
|
26
|
+
const declarado = config?.special?.outputGroups;
|
|
27
|
+
if (!declarado)
|
|
28
|
+
return undefined;
|
|
29
|
+
const lista = Array.isArray(declarado) ? declarado : [declarado];
|
|
30
|
+
return lista.find((c) => !c.gate || entity?.[c.gate.field] === c.gate.value);
|
|
31
|
+
}
|
|
32
|
+
/** Todos los juegos declarados, pasen o no su puerta. Para quien necesita el
|
|
33
|
+
* UNIVERSO de puertos que un tipo puede emitir (la auditoría de puertos),
|
|
34
|
+
* no el de ahora mismo. */
|
|
35
|
+
function todosLosOutputGroups(config) {
|
|
36
|
+
const declarado = config?.special?.outputGroups;
|
|
37
|
+
if (!declarado)
|
|
38
|
+
return [];
|
|
39
|
+
return Array.isArray(declarado) ? declarado : [declarado];
|
|
40
|
+
}
|
package/dist/ui.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.PREFIX_TO_TYPE = exports.NODE_UI = void 0;
|
|
8
8
|
exports.resolveNodeId = resolveNodeId;
|
|
9
|
+
const sentiment_js_1 = require("./sentiment.js");
|
|
9
10
|
exports.NODE_UI = {
|
|
10
11
|
// ── Source nodes ──
|
|
11
12
|
// Una fuente arranca el flujo: `fromNodes: false` significa que nada puede
|
|
@@ -49,7 +50,15 @@ exports.NODE_UI = {
|
|
|
49
50
|
// no handle and no route. In any other outputMode the node falls back to
|
|
50
51
|
// the single right-out handle NodeShell renders when no dynamic outputs
|
|
51
52
|
// materialize.
|
|
52
|
-
|
|
53
|
+
// En modo `sentiment` (2026-09-22) el abanico son CUATRO puertos fijos
|
|
54
|
+
// —negative / neutral / positive / unclear— y no un contenedor del usuario:
|
|
55
|
+
// el modelo elige entre las tres primeras y el nodo manda a `unclear` lo que
|
|
56
|
+
// no llega al umbral de confianza. Por eso el AI Node declara DOS juegos de
|
|
57
|
+
// salidas, uno por modo; `outputGroupsFor` elige el que toca.
|
|
58
|
+
ai: { fromNodes: true, toNodes: true, outputHandles: ['right'], special: { loopBackAllowed: true, connectModal: 'output-group', outputGroups: [
|
|
59
|
+
{ field: 'categories', nameField: 'name', summaryField: 'description', label: 'category', gate: { field: 'outputMode', value: 'classifier' } },
|
|
60
|
+
{ label: 'sentiment', gate: { field: 'outputMode', value: 'sentiment' }, fixedGroups: sentiment_js_1.SENTIMENT_PORTS.map((p) => ({ name: sentiment_js_1.SENTIMENT_PORT_LABEL[p], port: p, summary: sentiment_js_1.SENTIMENT_PORT_SUMMARY[p] })) },
|
|
61
|
+
] } },
|
|
53
62
|
markdown: { fromNodes: true, toNodes: true, special: { loopBackAllowed: true } },
|
|
54
63
|
fileTransform: { fromNodes: true, toNodes: true },
|
|
55
64
|
limit: { fromNodes: true, toNodes: true, special: { loopBackAllowed: true } },
|
package/package.json
CHANGED