@hostwebhook/node-types 1.68.0 → 1.70.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 +1 -1
- package/dist/connections.d.ts +1 -1
- package/dist/credentials.d.ts +89 -2
- package/dist/credentials.js +31 -4
- package/dist/discord-operations.d.ts +1 -1
- package/dist/discord-operations.js +4 -4
- package/dist/discord-toolkit.d.ts +1 -1
- package/dist/discord-toolkit.js +3 -3
- package/dist/dispatch.d.ts +1 -1
- package/dist/docs-toolkit.d.ts +1 -1
- package/dist/drive-toolkit.d.ts +1 -1
- package/dist/esm/calendar-operations.d.ts +67 -0
- package/dist/esm/calendar-operations.js +176 -0
- package/dist/esm/calendar-toolkit.d.ts +50 -0
- package/dist/esm/calendar-toolkit.js +152 -0
- package/dist/esm/capacidad-de-credencial.d.ts +75 -0
- package/dist/esm/capacidad-de-credencial.js +68 -0
- package/dist/esm/connections.d.ts +17 -0
- package/dist/esm/connections.js +106 -0
- package/dist/esm/credentials.d.ts +144 -0
- package/dist/esm/credentials.js +199 -0
- package/dist/esm/discord-operations.d.ts +104 -0
- package/dist/esm/discord-operations.js +407 -0
- package/dist/esm/discord-toolkit.d.ts +64 -0
- package/dist/esm/discord-toolkit.js +260 -0
- package/dist/esm/dispatch.d.ts +16 -0
- package/dist/esm/dispatch.js +76 -0
- package/dist/esm/docs-operations.d.ts +99 -0
- package/dist/esm/docs-operations.js +127 -0
- package/dist/esm/docs-toolkit.d.ts +42 -0
- package/dist/esm/docs-toolkit.js +97 -0
- package/dist/esm/drive-operations.d.ts +103 -0
- package/dist/esm/drive-operations.js +319 -0
- package/dist/esm/drive-toolkit.d.ts +54 -0
- package/dist/esm/drive-toolkit.js +177 -0
- package/dist/esm/github-operations.d.ts +147 -0
- package/dist/esm/github-operations.js +551 -0
- package/dist/esm/gmail-operations.d.ts +190 -0
- package/dist/esm/gmail-operations.js +560 -0
- package/dist/esm/google-analytics-operations.d.ts +99 -0
- package/dist/esm/google-analytics-operations.js +222 -0
- package/dist/esm/google-contacts-operations.d.ts +93 -0
- package/dist/esm/google-contacts-operations.js +363 -0
- package/dist/esm/index.d.ts +66 -0
- package/dist/esm/index.js +50 -0
- package/dist/esm/jira-operations.d.ts +164 -0
- package/dist/esm/jira-operations.js +470 -0
- package/dist/esm/llm-models.d.ts +101 -0
- package/dist/esm/llm-models.js +230 -0
- package/dist/esm/mailchimp-operations.d.ts +56 -0
- package/dist/esm/mailchimp-operations.js +155 -0
- package/dist/esm/mongo-operations.d.ts +61 -0
- package/dist/esm/mongo-operations.js +120 -0
- package/dist/esm/notion-operations.d.ts +125 -0
- package/dist/esm/notion-operations.js +511 -0
- package/dist/esm/openrouter.d.ts +142 -0
- package/dist/esm/openrouter.js +99 -0
- package/dist/esm/package.json +4 -0
- package/dist/esm/postgres-operations.d.ts +60 -0
- package/dist/esm/postgres-operations.js +75 -0
- package/dist/esm/registry.d.ts +160 -0
- package/dist/esm/registry.js +555 -0
- package/dist/esm/sheets-operations.d.ts +78 -0
- package/dist/esm/sheets-operations.js +150 -0
- package/dist/esm/sheets-toolkit.d.ts +49 -0
- package/dist/esm/sheets-toolkit.js +127 -0
- package/dist/esm/shopify-operations.d.ts +95 -0
- package/dist/esm/shopify-operations.js +259 -0
- package/dist/esm/slack-operations.d.ts +81 -0
- package/dist/esm/slack-operations.js +244 -0
- package/dist/esm/slack-toolkit.d.ts +60 -0
- package/dist/esm/slack-toolkit.js +235 -0
- package/dist/esm/telegram-operations.d.ts +104 -0
- package/dist/esm/telegram-operations.js +351 -0
- package/dist/esm/telegram-toolkit.d.ts +52 -0
- package/dist/esm/telegram-toolkit.js +183 -0
- package/dist/esm/types.d.ts +149 -0
- package/dist/esm/types.js +8 -0
- package/dist/esm/ui.d.ts +13 -0
- package/dist/esm/ui.js +150 -0
- package/dist/esm/whatsapp-operations.d.ts +23 -0
- package/dist/esm/whatsapp-operations.js +36 -0
- package/dist/index.d.ts +66 -66
- package/dist/index.js +185 -185
- package/dist/openrouter.d.ts +1 -1
- package/dist/registry.d.ts +1 -1
- package/dist/sheets-toolkit.d.ts +1 -1
- package/dist/slack-operations.d.ts +1 -1
- package/dist/slack-operations.js +4 -4
- package/dist/slack-toolkit.d.ts +1 -1
- package/dist/slack-toolkit.js +3 -3
- package/dist/telegram-toolkit.d.ts +1 -1
- package/dist/ui.d.ts +1 -1
- package/package.json +21 -3
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* El catálogo VIVO de OpenRouter: la forma de lo que devuelve su API y cómo se
|
|
3
|
+
* convierte en las mismas opciones que ya pintan los desplegables.
|
|
4
|
+
*
|
|
5
|
+
* ## Por qué este proveedor no tiene lista en `llm-models.ts`
|
|
6
|
+
*
|
|
7
|
+
* Porque no es un laboratorio, es un router: revende ~425 modelos de otros
|
|
8
|
+
* detrás de una sola clave, y el conjunto se mueve solo. La lista entera está
|
|
9
|
+
* en `GET https://openrouter.ai/api/v1/models`, que es **abierto y sin clave**
|
|
10
|
+
* —se puede pedir antes de que el usuario haya pegado ninguna— y cada entrada
|
|
11
|
+
* trae su `context_length` y **su propio precio**. Copiar eso a mano a
|
|
12
|
+
* `LLM_MODELS` y a `MODEL_PRICING` sería mantener a mano lo que la API ya da,
|
|
13
|
+
* con 425 oportunidades de que se quede viejo en silencio.
|
|
14
|
+
*
|
|
15
|
+
* ## El reparto, que es lo que hay que entender antes de tocar esto
|
|
16
|
+
*
|
|
17
|
+
* De la MISMA respuesta salen dos cosas, y viven en paquetes distintos porque
|
|
18
|
+
* son de dueños distintos:
|
|
19
|
+
*
|
|
20
|
+
* - **el catálogo** (id, nombre, ventana de contexto) → aquí, porque el
|
|
21
|
+
* catálogo de modelos es de este fichero desde siempre;
|
|
22
|
+
* - **la tarifa** (`pricing`) → `tarifaDeOpenRouter` en
|
|
23
|
+
* `@hostwebhook/platform-contracts`, porque el precio de un token es de
|
|
24
|
+
* ese paquete y no de éste.
|
|
25
|
+
*
|
|
26
|
+
* Y no al revés: este paquete NO puede importar `ModelPricing`
|
|
27
|
+
* —`platform-contracts` depende de éste, no al contrario—, así que el tipo que
|
|
28
|
+
* cruza es el crudo `ModeloDeOpenRouter`. Quien pinte el selector hace UN solo
|
|
29
|
+
* fetch y pasa cada entrada por las dos funciones.
|
|
30
|
+
*
|
|
31
|
+
* ## ⚠️ Esto es una respuesta de red, no un fichero nuestro
|
|
32
|
+
*
|
|
33
|
+
* Todo lo que no sea `id` va opcional a propósito. No porque OpenRouter lo
|
|
34
|
+
* omita hoy, sino porque nada nuestro impide que lo omita mañana, y un campo
|
|
35
|
+
* declarado obligatorio que llega `undefined` no falla al parsear —TypeScript
|
|
36
|
+
* no valida en runtime—: falla más tarde y más lejos, leyendo una propiedad de
|
|
37
|
+
* algo que no está. `id` es la única excepción porque sin él la entrada no
|
|
38
|
+
* identifica a ningún modelo y no hay nada que ofrecer.
|
|
39
|
+
*
|
|
40
|
+
* Verificado contra la respuesta real el 2026-08-31: 425 modelos, los 425 con
|
|
41
|
+
* `pricing.prompt` y `pricing.completion`, y algunos con un
|
|
42
|
+
* `top_provider.context_length` MENOR que su `context_length` anunciado.
|
|
43
|
+
*/
|
|
44
|
+
import type { LlmModelOption } from './llm-models.js';
|
|
45
|
+
/** El endpoint del catálogo. Abierto: no lleva `Authorization`. */
|
|
46
|
+
export declare const URL_DE_MODELOS_DE_OPENROUTER = "https://openrouter.ai/api/v1/models";
|
|
47
|
+
/**
|
|
48
|
+
* El bloque `pricing` de un modelo.
|
|
49
|
+
*
|
|
50
|
+
* ⚠️ Dos trampas, las dos verificadas contra la respuesta real:
|
|
51
|
+
*
|
|
52
|
+
* 1. Los valores son **cadenas**, no números: `"0.0000001"`.
|
|
53
|
+
* 2. Son **por token**, no por millón. `"0.0000001"` = $0,10 por millón.
|
|
54
|
+
*
|
|
55
|
+
* O sea que ninguno de estos números se puede meter en un `ModelPricing` sin
|
|
56
|
+
* pasar por `tarifaDeOpenRouter`, que hace las dos conversiones.
|
|
57
|
+
*
|
|
58
|
+
* Se tipa como índice abierto porque OpenRouter publica más claves de las que
|
|
59
|
+
* usamos (`request`, `image`, `web_search`, `internal_reasoning`, y las de
|
|
60
|
+
* caché) y añade más sin avisar.
|
|
61
|
+
*/
|
|
62
|
+
export interface PrecioDeOpenRouter {
|
|
63
|
+
/** USD por token de ENTRADA, como cadena. */
|
|
64
|
+
prompt?: string | null;
|
|
65
|
+
/** USD por token de SALIDA, como cadena. */
|
|
66
|
+
completion?: string | null;
|
|
67
|
+
/** USD por token leído de caché. Hoy no se usa: ver `tarifaDeOpenRouter`. */
|
|
68
|
+
input_cache_read?: string | null;
|
|
69
|
+
/** USD por token escrito en caché. Hoy no se usa. */
|
|
70
|
+
input_cache_write?: string | null;
|
|
71
|
+
/** Caché de una hora, cuando el modelo la ofrece. Hoy no se usa. */
|
|
72
|
+
input_cache_write_1h?: string | null;
|
|
73
|
+
[otras: string]: unknown;
|
|
74
|
+
}
|
|
75
|
+
/** Una entrada del catálogo de OpenRouter. */
|
|
76
|
+
export interface ModeloDeOpenRouter {
|
|
77
|
+
/**
|
|
78
|
+
* Lo que se le manda a la API como `model`, con el laboratorio delante:
|
|
79
|
+
* `"anthropic/claude-sonnet-5"`, `"openai/gpt-5.6-luna"`.
|
|
80
|
+
*
|
|
81
|
+
* ⚠️ Ese prefijo es parte del id y NO se quita. Un `"anthropic/..."`
|
|
82
|
+
* recortado a `"claude-sonnet-5"` cobraría la tarifa de la tabla estática
|
|
83
|
+
* —la de comprarle a Anthropic directamente— y OpenRouter no cobra eso.
|
|
84
|
+
*/
|
|
85
|
+
id: string;
|
|
86
|
+
/** El id sin el sufijo de variante (`:free`, `:nitro`, `:floor`). */
|
|
87
|
+
canonical_slug?: string;
|
|
88
|
+
/** Nombre para humanos: `"Anthropic: Claude Sonnet 5"`. */
|
|
89
|
+
name?: string;
|
|
90
|
+
/** Ventana efectiva en TOKENS, tal y como la publica el proveedor. */
|
|
91
|
+
context_length?: number | null;
|
|
92
|
+
pricing?: PrecioDeOpenRouter | null;
|
|
93
|
+
architecture?: {
|
|
94
|
+
input_modalities?: string[];
|
|
95
|
+
output_modalities?: string[];
|
|
96
|
+
tokenizer?: string;
|
|
97
|
+
[otras: string]: unknown;
|
|
98
|
+
} | null;
|
|
99
|
+
top_provider?: {
|
|
100
|
+
context_length?: number | null;
|
|
101
|
+
max_completion_tokens?: number | null;
|
|
102
|
+
is_moderated?: boolean;
|
|
103
|
+
[otras: string]: unknown;
|
|
104
|
+
} | null;
|
|
105
|
+
/** Qué parámetros acepta: `"tools"`, `"reasoning"`, `"structured_outputs"`… */
|
|
106
|
+
supported_parameters?: string[] | null;
|
|
107
|
+
[otras: string]: unknown;
|
|
108
|
+
}
|
|
109
|
+
/** El sobre: `{ data: [...] }`. */
|
|
110
|
+
export interface RespuestaDeModelosDeOpenRouter {
|
|
111
|
+
data?: ModeloDeOpenRouter[] | null;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Las opciones del desplegable a partir de la respuesta cruda.
|
|
115
|
+
*
|
|
116
|
+
* Devuelve `LlmModelOption`, el MISMO tipo que `LLM_MODELS`, para que el
|
|
117
|
+
* selector no tenga dos formas de modelo según el proveedor: la única
|
|
118
|
+
* diferencia entre un catálogo y el otro es de dónde salió la lista.
|
|
119
|
+
*
|
|
120
|
+
* ⚠️ Ninguna opción sale `recommended`. En los catálogos estáticos esa marca
|
|
121
|
+
* la pone una persona que ha mirado los precios; aquí no hay nadie mirando, y
|
|
122
|
+
* una recomendación automática —«el primero», «el más barato»— sería una
|
|
123
|
+
* decisión de producto tomada por accidente. Sin `recommended`,
|
|
124
|
+
* `getDefaultModel('openrouter')` sigue siendo `''` y elegir es del usuario.
|
|
125
|
+
*
|
|
126
|
+
* Acepta el sobre entero o el array suelto porque quien llama a veces ya lo ha
|
|
127
|
+
* desenvuelto, y tolera basura (`null`, entradas sin `id`) devolviendo lo que
|
|
128
|
+
* sí sirve: un catálogo a medias es utilizable, una excepción a media pantalla
|
|
129
|
+
* no.
|
|
130
|
+
*/
|
|
131
|
+
export declare function opcionesDeModelosDeOpenRouter(respuesta: RespuestaDeModelosDeOpenRouter | ModeloDeOpenRouter[] | null | undefined): LlmModelOption[];
|
|
132
|
+
/**
|
|
133
|
+
* La ventana de contexto de un modelo de OpenRouter, en tokens, o `undefined`.
|
|
134
|
+
*
|
|
135
|
+
* Aparte y con nombre propio porque hay DOS sitios donde viene y no siempre
|
|
136
|
+
* coinciden: `context_length` es lo que anuncia el modelo, y
|
|
137
|
+
* `top_provider.context_length` lo que sirve de verdad el proveedor que
|
|
138
|
+
* OpenRouter va a usar. Gana el más PEQUEÑO de los dos: quien trocea para
|
|
139
|
+
* resumir prefiere quedarse corto —más llamadas— a que el proveedor le
|
|
140
|
+
* rechace la petición por pasarse.
|
|
141
|
+
*/
|
|
142
|
+
export declare function ventanaDeContextoDeOpenRouter(modelo: ModeloDeOpenRouter | null | undefined): number | undefined;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* El catálogo VIVO de OpenRouter: la forma de lo que devuelve su API y cómo se
|
|
3
|
+
* convierte en las mismas opciones que ya pintan los desplegables.
|
|
4
|
+
*
|
|
5
|
+
* ## Por qué este proveedor no tiene lista en `llm-models.ts`
|
|
6
|
+
*
|
|
7
|
+
* Porque no es un laboratorio, es un router: revende ~425 modelos de otros
|
|
8
|
+
* detrás de una sola clave, y el conjunto se mueve solo. La lista entera está
|
|
9
|
+
* en `GET https://openrouter.ai/api/v1/models`, que es **abierto y sin clave**
|
|
10
|
+
* —se puede pedir antes de que el usuario haya pegado ninguna— y cada entrada
|
|
11
|
+
* trae su `context_length` y **su propio precio**. Copiar eso a mano a
|
|
12
|
+
* `LLM_MODELS` y a `MODEL_PRICING` sería mantener a mano lo que la API ya da,
|
|
13
|
+
* con 425 oportunidades de que se quede viejo en silencio.
|
|
14
|
+
*
|
|
15
|
+
* ## El reparto, que es lo que hay que entender antes de tocar esto
|
|
16
|
+
*
|
|
17
|
+
* De la MISMA respuesta salen dos cosas, y viven en paquetes distintos porque
|
|
18
|
+
* son de dueños distintos:
|
|
19
|
+
*
|
|
20
|
+
* - **el catálogo** (id, nombre, ventana de contexto) → aquí, porque el
|
|
21
|
+
* catálogo de modelos es de este fichero desde siempre;
|
|
22
|
+
* - **la tarifa** (`pricing`) → `tarifaDeOpenRouter` en
|
|
23
|
+
* `@hostwebhook/platform-contracts`, porque el precio de un token es de
|
|
24
|
+
* ese paquete y no de éste.
|
|
25
|
+
*
|
|
26
|
+
* Y no al revés: este paquete NO puede importar `ModelPricing`
|
|
27
|
+
* —`platform-contracts` depende de éste, no al contrario—, así que el tipo que
|
|
28
|
+
* cruza es el crudo `ModeloDeOpenRouter`. Quien pinte el selector hace UN solo
|
|
29
|
+
* fetch y pasa cada entrada por las dos funciones.
|
|
30
|
+
*
|
|
31
|
+
* ## ⚠️ Esto es una respuesta de red, no un fichero nuestro
|
|
32
|
+
*
|
|
33
|
+
* Todo lo que no sea `id` va opcional a propósito. No porque OpenRouter lo
|
|
34
|
+
* omita hoy, sino porque nada nuestro impide que lo omita mañana, y un campo
|
|
35
|
+
* declarado obligatorio que llega `undefined` no falla al parsear —TypeScript
|
|
36
|
+
* no valida en runtime—: falla más tarde y más lejos, leyendo una propiedad de
|
|
37
|
+
* algo que no está. `id` es la única excepción porque sin él la entrada no
|
|
38
|
+
* identifica a ningún modelo y no hay nada que ofrecer.
|
|
39
|
+
*
|
|
40
|
+
* Verificado contra la respuesta real el 2026-08-31: 425 modelos, los 425 con
|
|
41
|
+
* `pricing.prompt` y `pricing.completion`, y algunos con un
|
|
42
|
+
* `top_provider.context_length` MENOR que su `context_length` anunciado.
|
|
43
|
+
*/
|
|
44
|
+
/** El endpoint del catálogo. Abierto: no lleva `Authorization`. */
|
|
45
|
+
export const URL_DE_MODELOS_DE_OPENROUTER = 'https://openrouter.ai/api/v1/models';
|
|
46
|
+
/**
|
|
47
|
+
* Las opciones del desplegable a partir de la respuesta cruda.
|
|
48
|
+
*
|
|
49
|
+
* Devuelve `LlmModelOption`, el MISMO tipo que `LLM_MODELS`, para que el
|
|
50
|
+
* selector no tenga dos formas de modelo según el proveedor: la única
|
|
51
|
+
* diferencia entre un catálogo y el otro es de dónde salió la lista.
|
|
52
|
+
*
|
|
53
|
+
* ⚠️ Ninguna opción sale `recommended`. En los catálogos estáticos esa marca
|
|
54
|
+
* la pone una persona que ha mirado los precios; aquí no hay nadie mirando, y
|
|
55
|
+
* una recomendación automática —«el primero», «el más barato»— sería una
|
|
56
|
+
* decisión de producto tomada por accidente. Sin `recommended`,
|
|
57
|
+
* `getDefaultModel('openrouter')` sigue siendo `''` y elegir es del usuario.
|
|
58
|
+
*
|
|
59
|
+
* Acepta el sobre entero o el array suelto porque quien llama a veces ya lo ha
|
|
60
|
+
* desenvuelto, y tolera basura (`null`, entradas sin `id`) devolviendo lo que
|
|
61
|
+
* sí sirve: un catálogo a medias es utilizable, una excepción a media pantalla
|
|
62
|
+
* no.
|
|
63
|
+
*/
|
|
64
|
+
export function opcionesDeModelosDeOpenRouter(respuesta) {
|
|
65
|
+
const modelos = Array.isArray(respuesta) ? respuesta : respuesta?.data;
|
|
66
|
+
if (!Array.isArray(modelos))
|
|
67
|
+
return [];
|
|
68
|
+
const opciones = [];
|
|
69
|
+
for (const modelo of modelos) {
|
|
70
|
+
if (!modelo || typeof modelo.id !== 'string' || modelo.id === '')
|
|
71
|
+
continue;
|
|
72
|
+
opciones.push({
|
|
73
|
+
value: modelo.id,
|
|
74
|
+
label: typeof modelo.name === 'string' && modelo.name !== ''
|
|
75
|
+
? modelo.name
|
|
76
|
+
: modelo.id,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return opciones;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* La ventana de contexto de un modelo de OpenRouter, en tokens, o `undefined`.
|
|
83
|
+
*
|
|
84
|
+
* Aparte y con nombre propio porque hay DOS sitios donde viene y no siempre
|
|
85
|
+
* coinciden: `context_length` es lo que anuncia el modelo, y
|
|
86
|
+
* `top_provider.context_length` lo que sirve de verdad el proveedor que
|
|
87
|
+
* OpenRouter va a usar. Gana el más PEQUEÑO de los dos: quien trocea para
|
|
88
|
+
* resumir prefiere quedarse corto —más llamadas— a que el proveedor le
|
|
89
|
+
* rechace la petición por pasarse.
|
|
90
|
+
*/
|
|
91
|
+
export function ventanaDeContextoDeOpenRouter(modelo) {
|
|
92
|
+
const candidatas = [
|
|
93
|
+
modelo?.context_length,
|
|
94
|
+
modelo?.top_provider?.context_length,
|
|
95
|
+
].filter((n) => typeof n === 'number' && Number.isFinite(n) && n > 0);
|
|
96
|
+
if (candidatas.length === 0)
|
|
97
|
+
return undefined;
|
|
98
|
+
return Math.min(...candidatas);
|
|
99
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres operation enum + metadatos por operación — una sola copia para la
|
|
3
|
+
* api, el Dashboard y el broker.
|
|
4
|
+
*
|
|
5
|
+
* ── Por qué esto no trae un esquema de formulario ──
|
|
6
|
+
* A diferencia de Slack, Discord, Sheets, Telegram, Contacts y Drive,
|
|
7
|
+
* `postgresAction` **no tiene campos por operación**: el mismo formulario —SQL,
|
|
8
|
+
* Parameters, Statement timeout— se pinta para las cinco. Su eje real es `mode`
|
|
9
|
+
* (`single` / `transaction`). Así que aquí no hay `*_OPERATION_SPECS` de params,
|
|
10
|
+
* porque saldrían idénticos cinco veces; lo que sí había era la lista escrita a
|
|
11
|
+
* mano **nueve veces**, y eso es lo que esto quita:
|
|
12
|
+
*
|
|
13
|
+
* api dto: @ApiProperty enum, @IsIn, la unión TS (3)
|
|
14
|
+
* api entity: @Prop enum, la unión TS (2)
|
|
15
|
+
* dash lib/api.ts: la unión TS (1)
|
|
16
|
+
* dash PostgresTablePicker: la unión en un prop (1)
|
|
17
|
+
* dash PostgresActionDetail: OPERATIONS (1)
|
|
18
|
+
* dash PostgresNode + FlowPanel: opLabels idénticos (2)
|
|
19
|
+
*
|
|
20
|
+
* Las **clases de color no están aquí**: son presentación (Tailwind), y se
|
|
21
|
+
* quedan en el dashboard, igual que los `ICONOS` de Telegram y de Drive.
|
|
22
|
+
*/
|
|
23
|
+
export declare const POSTGRES_OPERATIONS: readonly ["query", "insertOne", "update", "delete", "execute"];
|
|
24
|
+
export type PostgresOperation = (typeof POSTGRES_OPERATIONS)[number];
|
|
25
|
+
/** Type guard — útil al validar entrada que no controlas (DTOs, tool calls). */
|
|
26
|
+
export declare function isPostgresOperation(value: unknown): value is PostgresOperation;
|
|
27
|
+
/**
|
|
28
|
+
* Cómo se ejecuta el nodo. `single` es una sentencia; `transaction` es una lista
|
|
29
|
+
* ordenada dentro de BEGIN / COMMIT con rollback condicional.
|
|
30
|
+
*/
|
|
31
|
+
export declare const POSTGRES_MODES: readonly ["single", "transaction"];
|
|
32
|
+
export type PostgresMode = (typeof POSTGRES_MODES)[number];
|
|
33
|
+
export interface PostgresOperationSpec {
|
|
34
|
+
/**
|
|
35
|
+
* Etiqueta corta, para la píldora estrecha del lienzo y del panel. Estaba
|
|
36
|
+
* escrita **idéntica** en `PostgresNode.tsx` y en `FlowPanel.tsx`.
|
|
37
|
+
*/
|
|
38
|
+
label: string;
|
|
39
|
+
/**
|
|
40
|
+
* La etiqueta larga del título del editor de SQL en la página de detalle,
|
|
41
|
+
* donde hay sitio para el paréntesis aclaratorio. **No es la misma que
|
|
42
|
+
* `label` y no debe unificarse**: son dos anchos distintos y las dos se ven.
|
|
43
|
+
*/
|
|
44
|
+
labelLong: string;
|
|
45
|
+
/** Una línea de cuándo usar esta operación. */
|
|
46
|
+
description: string;
|
|
47
|
+
/** SQL de ejemplo, el `placeholder` del editor. */
|
|
48
|
+
sqlPlaceholder: string;
|
|
49
|
+
/**
|
|
50
|
+
* Si la operación devuelve filas **siempre**. Sólo `execute` no: ahí depende
|
|
51
|
+
* de `returnRows` en la entidad, porque puede ser DDL.
|
|
52
|
+
*
|
|
53
|
+
* Este invariante vivía en dos sitios y en uno era un comentario: la api lo
|
|
54
|
+
* calculaba con `entity.operation !== 'execute' || entity.returnRows`, y el
|
|
55
|
+
* dashboard lo expresaba como `localOperation === "execute"` para decidir si
|
|
56
|
+
* enseñaba el interruptor. Es el mismo hecho, dicho aquí una vez.
|
|
57
|
+
*/
|
|
58
|
+
returnsRowsAlways: boolean;
|
|
59
|
+
}
|
|
60
|
+
export declare const POSTGRES_OPERATION_SPECS: Record<PostgresOperation, PostgresOperationSpec>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres operation enum + metadatos por operación — una sola copia para la
|
|
3
|
+
* api, el Dashboard y el broker.
|
|
4
|
+
*
|
|
5
|
+
* ── Por qué esto no trae un esquema de formulario ──
|
|
6
|
+
* A diferencia de Slack, Discord, Sheets, Telegram, Contacts y Drive,
|
|
7
|
+
* `postgresAction` **no tiene campos por operación**: el mismo formulario —SQL,
|
|
8
|
+
* Parameters, Statement timeout— se pinta para las cinco. Su eje real es `mode`
|
|
9
|
+
* (`single` / `transaction`). Así que aquí no hay `*_OPERATION_SPECS` de params,
|
|
10
|
+
* porque saldrían idénticos cinco veces; lo que sí había era la lista escrita a
|
|
11
|
+
* mano **nueve veces**, y eso es lo que esto quita:
|
|
12
|
+
*
|
|
13
|
+
* api dto: @ApiProperty enum, @IsIn, la unión TS (3)
|
|
14
|
+
* api entity: @Prop enum, la unión TS (2)
|
|
15
|
+
* dash lib/api.ts: la unión TS (1)
|
|
16
|
+
* dash PostgresTablePicker: la unión en un prop (1)
|
|
17
|
+
* dash PostgresActionDetail: OPERATIONS (1)
|
|
18
|
+
* dash PostgresNode + FlowPanel: opLabels idénticos (2)
|
|
19
|
+
*
|
|
20
|
+
* Las **clases de color no están aquí**: son presentación (Tailwind), y se
|
|
21
|
+
* quedan en el dashboard, igual que los `ICONOS` de Telegram y de Drive.
|
|
22
|
+
*/
|
|
23
|
+
export const POSTGRES_OPERATIONS = [
|
|
24
|
+
'query', // SELECT — devuelve filas
|
|
25
|
+
'insertOne', // INSERT, normalmente con RETURNING *
|
|
26
|
+
'update', // UPDATE ... WHERE
|
|
27
|
+
'delete', // DELETE ... WHERE
|
|
28
|
+
'execute', // SQL cualquiera: DDL, transacciones, multi-sentencia
|
|
29
|
+
];
|
|
30
|
+
/** Type guard — útil al validar entrada que no controlas (DTOs, tool calls). */
|
|
31
|
+
export function isPostgresOperation(value) {
|
|
32
|
+
return typeof value === 'string' && POSTGRES_OPERATIONS.includes(value);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Cómo se ejecuta el nodo. `single` es una sentencia; `transaction` es una lista
|
|
36
|
+
* ordenada dentro de BEGIN / COMMIT con rollback condicional.
|
|
37
|
+
*/
|
|
38
|
+
export const POSTGRES_MODES = ['single', 'transaction'];
|
|
39
|
+
export const POSTGRES_OPERATION_SPECS = {
|
|
40
|
+
query: {
|
|
41
|
+
label: 'SELECT',
|
|
42
|
+
labelLong: 'SELECT (query)',
|
|
43
|
+
description: 'Run a SELECT and return rows',
|
|
44
|
+
sqlPlaceholder: 'SELECT id, email FROM users WHERE org_id = {{payload.orgId}} ORDER BY created_at DESC LIMIT 50',
|
|
45
|
+
returnsRowsAlways: true,
|
|
46
|
+
},
|
|
47
|
+
insertOne: {
|
|
48
|
+
label: 'INSERT',
|
|
49
|
+
labelLong: 'INSERT',
|
|
50
|
+
description: 'Insert a row (with RETURNING * recommended)',
|
|
51
|
+
sqlPlaceholder: 'INSERT INTO events (kind, payload) VALUES ({{payload.kind}}, {{payload.data}}) RETURNING *',
|
|
52
|
+
returnsRowsAlways: true,
|
|
53
|
+
},
|
|
54
|
+
update: {
|
|
55
|
+
label: 'UPDATE',
|
|
56
|
+
labelLong: 'UPDATE',
|
|
57
|
+
description: 'Update rows matching WHERE',
|
|
58
|
+
sqlPlaceholder: 'UPDATE users SET last_seen = NOW() WHERE id = {{payload.userId}} RETURNING id',
|
|
59
|
+
returnsRowsAlways: true,
|
|
60
|
+
},
|
|
61
|
+
delete: {
|
|
62
|
+
label: 'DELETE',
|
|
63
|
+
labelLong: 'DELETE',
|
|
64
|
+
description: 'Delete rows matching WHERE',
|
|
65
|
+
sqlPlaceholder: 'DELETE FROM sessions WHERE expires_at < NOW() RETURNING id',
|
|
66
|
+
returnsRowsAlways: true,
|
|
67
|
+
},
|
|
68
|
+
execute: {
|
|
69
|
+
label: 'EXEC',
|
|
70
|
+
labelLong: 'EXEC (raw)',
|
|
71
|
+
description: 'Run any SQL — DDL, transactions, or multi-stmt. Toggle returnRows for INSERT ... RETURNING.',
|
|
72
|
+
sqlPlaceholder: 'CREATE TEMP TABLE x AS SELECT 1; ANALYZE; -- DDL or batch',
|
|
73
|
+
returnsRowsAlways: false,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized NODE_REGISTRY — single source of truth for all node metadata.
|
|
3
|
+
*
|
|
4
|
+
* When adding a new node type, add ONE entry here. The dashboard derives
|
|
5
|
+
* all per-node maps (colors, URLs, Redux keys, palette, delete, etc.)
|
|
6
|
+
* from this registry. No more 18+ touch points.
|
|
7
|
+
*/
|
|
8
|
+
import type { NodeType } from './types.js';
|
|
9
|
+
export type NodeGroup = 'Sources' | 'Flow Control' | 'Actions' | 'Annotations';
|
|
10
|
+
export interface NodeRegistryEntry {
|
|
11
|
+
/** Canonical type (e.g., "httpAction") */
|
|
12
|
+
type: NodeType;
|
|
13
|
+
/** Canvas ID prefix (e.g., "ha") */
|
|
14
|
+
prefix: string;
|
|
15
|
+
/** Display label (e.g., "HTTP Action") */
|
|
16
|
+
label: string;
|
|
17
|
+
/** Group in palette */
|
|
18
|
+
group: NodeGroup;
|
|
19
|
+
/** Detail page path (e.g., "/dashboard/http-actions") */
|
|
20
|
+
detailPath: string;
|
|
21
|
+
/** API endpoint base (e.g., "/http-actions") */
|
|
22
|
+
apiPath: string;
|
|
23
|
+
/** Redux state key for filtered array (e.g., "httpActions") */
|
|
24
|
+
stateKey: string;
|
|
25
|
+
/** Redux state key for all array (e.g., "allHttpActions") */
|
|
26
|
+
allStateKey: string;
|
|
27
|
+
/** MiniMap hex color */
|
|
28
|
+
color: string;
|
|
29
|
+
/** Whether this node supports test execution */
|
|
30
|
+
testable: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Optional per-instance predicate marking a node as "tool-only".
|
|
33
|
+
* When this returns true, the canvas should hide every connectable
|
|
34
|
+
* handle so the user cannot draw pipeline edges to/from this node —
|
|
35
|
+
* it lives instead inside an upstream AI Node's tools[] list.
|
|
36
|
+
*
|
|
37
|
+
* Lets new toolkit-style nodes opt-in without per-component hardcoding
|
|
38
|
+
* in the dashboard. Currently only emailAction(aiEnabled=true) uses
|
|
39
|
+
* this; trigger or future Slack/Discord toolkits could too.
|
|
40
|
+
*
|
|
41
|
+
* Predicate must be pure (no side effects) — called on every render.
|
|
42
|
+
*/
|
|
43
|
+
isToolOnly?: (entity: Record<string, unknown>) => boolean;
|
|
44
|
+
/**
|
|
45
|
+
* The versions this node type ships, when it ships more than one.
|
|
46
|
+
*
|
|
47
|
+
* Absent means the type does not version, which is true of every type
|
|
48
|
+
* today. A node type only declares this the day its provider deprecates
|
|
49
|
+
* something and a second version has to live alongside the first.
|
|
50
|
+
*
|
|
51
|
+
* See `docs/ADR-0002-node-versioning.md` in the api repo.
|
|
52
|
+
*/
|
|
53
|
+
versions?: NodeVersionSpec[];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* One version of a node type.
|
|
57
|
+
*
|
|
58
|
+
* Deliberately NOT a schema. The api's DTO stays the validator; all this
|
|
59
|
+
* needs to know is WHICH config fields a version understands, because that
|
|
60
|
+
* is what answers the only question the UI has to ask: switching from here
|
|
61
|
+
* to there, what does the user lose?
|
|
62
|
+
*/
|
|
63
|
+
export interface NodeVersionSpec {
|
|
64
|
+
/** The number the user sees. Unique within a type. */
|
|
65
|
+
v: number;
|
|
66
|
+
/** Shown next to the number in the picker, e.g. "Discord API v10". */
|
|
67
|
+
label?: string;
|
|
68
|
+
/**
|
|
69
|
+
* `current` is what a new node is born on — exactly one per type.
|
|
70
|
+
* `legacy` still runs but is on its way out. `beta` is opt-in.
|
|
71
|
+
*/
|
|
72
|
+
status: 'current' | 'legacy' | 'beta';
|
|
73
|
+
/**
|
|
74
|
+
* The config fields this version understands.
|
|
75
|
+
*
|
|
76
|
+
* Only fields the user configures. Identity and topology — name,
|
|
77
|
+
* inputNodes, outputNodes, isActive, canvas position — never belong
|
|
78
|
+
* here: they survive a version change untouched, and listing them
|
|
79
|
+
* would put them at risk of being unset.
|
|
80
|
+
*/
|
|
81
|
+
fields: string[];
|
|
82
|
+
/**
|
|
83
|
+
* Entering this version keeps nothing. For the case where "v2" is
|
|
84
|
+
* really a different integration wearing the same node's name.
|
|
85
|
+
*/
|
|
86
|
+
cleanBreak?: boolean;
|
|
87
|
+
/** Why it was retired, or what it brings. Shown in picker and dialog. */
|
|
88
|
+
note?: string;
|
|
89
|
+
}
|
|
90
|
+
export declare const NODE_REGISTRY: Record<NodeType, NodeRegistryEntry>;
|
|
91
|
+
/** Get registry entry by canonical type */
|
|
92
|
+
export declare function getNodeRegistryEntry(type: string): NodeRegistryEntry | undefined;
|
|
93
|
+
/** type → detail page path */
|
|
94
|
+
export declare const NODE_DETAIL_PATHS: Record<string, string>;
|
|
95
|
+
/** type → minimap color */
|
|
96
|
+
export declare const NODE_COLORS: Record<string, string>;
|
|
97
|
+
/** type → [stateKey, allStateKey] */
|
|
98
|
+
export declare const NODE_STATE_KEYS: Record<string, [string, string]>;
|
|
99
|
+
/** prefix → type */
|
|
100
|
+
export declare const PREFIX_TO_NODE_TYPE: Record<string, NodeType>;
|
|
101
|
+
/** type → prefix */
|
|
102
|
+
export declare const NODE_TYPE_TO_PREFIX: Record<string, string>;
|
|
103
|
+
/**
|
|
104
|
+
* The catalog these helpers read. Every one of them takes it as an
|
|
105
|
+
* optional last argument defaulting to the real registry: consumers never
|
|
106
|
+
* pass it, and the tests can exercise a versioned type without waiting
|
|
107
|
+
* for a real one to exist.
|
|
108
|
+
*/
|
|
109
|
+
export type VersionCatalog = Record<string, Pick<NodeRegistryEntry, 'type' | 'versions'>>;
|
|
110
|
+
/** True when this type ships more than one version. False for every type today. */
|
|
111
|
+
export declare function isVersioned(type: string, catalog?: VersionCatalog): boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Every declared version, newest first — the order a picker wants.
|
|
114
|
+
* Empty for a type that does not version.
|
|
115
|
+
*/
|
|
116
|
+
export declare function versionsOf(type: string, catalog?: VersionCatalog): NodeVersionSpec[];
|
|
117
|
+
/** One version's spec, or undefined when the type or the number is unknown. */
|
|
118
|
+
export declare function versionSpec(type: string, v: number, catalog?: VersionCatalog): NodeVersionSpec | undefined;
|
|
119
|
+
/**
|
|
120
|
+
* The version a new node of this type is born on.
|
|
121
|
+
*
|
|
122
|
+
* Returns 1 for a type that does not version, which is also how an entity
|
|
123
|
+
* with no `nodeVersion` is read. A node created before its type ever
|
|
124
|
+
* versioned was calling the original endpoints, so calling it version 1
|
|
125
|
+
* is not a convention — it is the truth.
|
|
126
|
+
*/
|
|
127
|
+
export declare function currentVersion(type: string, catalog?: VersionCatalog): number;
|
|
128
|
+
/**
|
|
129
|
+
* Config fields present in `from` that `to` does not declare — the exact
|
|
130
|
+
* list a confirmation dialog should name, and the exact list the server
|
|
131
|
+
* should unset.
|
|
132
|
+
*
|
|
133
|
+
* Returns `null` when the question cannot be answered: the type does not
|
|
134
|
+
* version, or one of the two versions is not declared. `null` is not the
|
|
135
|
+
* same as "nothing is lost", and callers must not treat it that way — a
|
|
136
|
+
* dialog should refuse to offer the switch, and the api should reject the
|
|
137
|
+
* request. Returning `null` rather than `[]` is what stops "I don't know"
|
|
138
|
+
* from being read as "nothing".
|
|
139
|
+
*
|
|
140
|
+
* This lives here rather than in each consumer because the dialog that
|
|
141
|
+
* ANNOUNCES the loss and the server that PERFORMS it have to give the
|
|
142
|
+
* same answer. Two implementations would agree right until the day they
|
|
143
|
+
* did not, and that day the dialog lies.
|
|
144
|
+
*/
|
|
145
|
+
export declare function fieldsLostBetween(type: string, from: number, to: number, catalog?: VersionCatalog): string[] | null;
|
|
146
|
+
/**
|
|
147
|
+
* The same answer for two specs already in hand — what `fieldsLostBetween`
|
|
148
|
+
* delegates to once it has resolved both, and what a caller that already
|
|
149
|
+
* holds them from `versionsOf()` can use without a second lookup.
|
|
150
|
+
*/
|
|
151
|
+
export declare function fieldsLost(from: NodeVersionSpec, to: NodeVersionSpec): string[];
|
|
152
|
+
/**
|
|
153
|
+
* Declaration mistakes in the version catalogs, as readable lines.
|
|
154
|
+
*
|
|
155
|
+
* A guard rather than a line in a document: what this codebase learned
|
|
156
|
+
* from fifteen nodes skipping a checklist point everyone had read is that
|
|
157
|
+
* the written rule prevented nothing and the guard did. Empty means every
|
|
158
|
+
* catalog is well-formed.
|
|
159
|
+
*/
|
|
160
|
+
export declare function versionCatalogErrors(catalog?: VersionCatalog): string[];
|