@hostwebhook/node-types 1.69.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/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,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qué puede hacer un nodo según el TIPO de credencial que lleva puesta.
|
|
3
|
+
*
|
|
4
|
+
* Hasta ahora un nodo ofrecía siempre todas sus operaciones, porque todas sus
|
|
5
|
+
* credenciales podían hacerlas todas. Con los webhooks deja de ser cierto, y de
|
|
6
|
+
* una forma muy desigual:
|
|
7
|
+
*
|
|
8
|
+
* - Un webhook de Discord ejecuta 4 de las 17 —publicar, y leer/editar/borrar
|
|
9
|
+
* **sus propios** mensajes—, siempre en el único canal para el que se creó.
|
|
10
|
+
* - Un webhook de Slack ejecuta **1 de 16**, y ni siquiera puede elegir el
|
|
11
|
+
* canal: lo fija quien instala la credencial.
|
|
12
|
+
*
|
|
13
|
+
* Esa desigualdad es la que decide la forma de este fichero. No basta con
|
|
14
|
+
* esconder operaciones: hay que esconder **campos dentro de** una operación,
|
|
15
|
+
* porque el destino deja de ser una pregunta. Por eso son dos listas y no una.
|
|
16
|
+
*
|
|
17
|
+
* ── Dónde vive esto y por qué ──
|
|
18
|
+
*
|
|
19
|
+
* Aquí, en el paquete, y no en cada consumidor. Lo mismo tienen que saberlo
|
|
20
|
+
* CUATRO sitios, y si cada uno lo deduce por su cuenta acaban discrepando sin
|
|
21
|
+
* que nada falle al compilar:
|
|
22
|
+
*
|
|
23
|
+
* 1. el selector de operación de la pantalla de detalle;
|
|
24
|
+
* 2. el renderizador de campos de esa misma pantalla;
|
|
25
|
+
* 3. la validación al guardar, en la api;
|
|
26
|
+
* 4. **la expansión del toolkit de IA** — que es el que se rompe más callado:
|
|
27
|
+
* sin esto, a un nodo con credencial de webhook se le entregan al modelo
|
|
28
|
+
* 17 herramientas de las que 13 fallan siempre.
|
|
29
|
+
*
|
|
30
|
+
* Es la misma razón por la que los toolkits se mudaron aquí en 1.66.0. Tener el
|
|
31
|
+
* dato dos veces es tenerlo mal a la vuelta de unas semanas.
|
|
32
|
+
*/
|
|
33
|
+
/** Ninguna restricción: el caso del bot, y el de cualquier tipo sin entrada. */
|
|
34
|
+
const SIN_LIMITE = {};
|
|
35
|
+
/**
|
|
36
|
+
* Qué operaciones ofrecer para esta credencial.
|
|
37
|
+
*
|
|
38
|
+
* Sin tipo de credencial —el nodo todavía no tiene ninguna elegida— se
|
|
39
|
+
* devuelven todas: es un hueco por rellenar, no una restricción, y esconder
|
|
40
|
+
* operaciones ahí haría creer que el nodo no sabe hacerlas.
|
|
41
|
+
*/
|
|
42
|
+
export function operacionesPara(todas, mapa, tipoDeCredencial) {
|
|
43
|
+
if (!tipoDeCredencial)
|
|
44
|
+
return todas;
|
|
45
|
+
const cap = mapa[tipoDeCredencial] ?? SIN_LIMITE;
|
|
46
|
+
if (!cap.operaciones)
|
|
47
|
+
return todas;
|
|
48
|
+
/* Se filtra `todas` en vez de devolver la lista del mapa para que el ORDEN
|
|
49
|
+
sea siempre el del nodo. Si no, la pantalla reordenaría sus operaciones al
|
|
50
|
+
cambiar de credencial, que se lee como si hubieran cambiado otras cosas. */
|
|
51
|
+
const permitidas = new Set(cap.operaciones);
|
|
52
|
+
return todas.filter((op) => permitidas.has(op));
|
|
53
|
+
}
|
|
54
|
+
/** Si esta credencial puede ejecutar esta operación. */
|
|
55
|
+
export function puedeEjecutar(todas, mapa, tipoDeCredencial, operacion) {
|
|
56
|
+
return operacionesPara(todas, mapa, tipoDeCredencial).includes(operacion);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Campos que esta credencial no deja elegir.
|
|
60
|
+
*
|
|
61
|
+
* Devuelve un `Set` porque quien lo usa siempre pregunta «¿este campo sí o
|
|
62
|
+
* no?», nunca recorre la lista.
|
|
63
|
+
*/
|
|
64
|
+
export function camposNoDisponiblesPara(mapa, tipoDeCredencial) {
|
|
65
|
+
if (!tipoDeCredencial)
|
|
66
|
+
return new Set();
|
|
67
|
+
return new Set(mapa[tipoDeCredencial]?.camposNoDisponibles ?? []);
|
|
68
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node Connection Registry — THE single source of truth for what each node
|
|
3
|
+
* type can receive from and send to.
|
|
4
|
+
*
|
|
5
|
+
* Used by: API (pipeline dispatch), Broker (agent wiring), Dashboard (canvas validation)
|
|
6
|
+
*
|
|
7
|
+
* Adding a new node type = add 1 entry here. All repos import from this package.
|
|
8
|
+
*/
|
|
9
|
+
import type { NodeType, NodeConnectionConfig } from './types.js';
|
|
10
|
+
export declare const NODE_CONNECTIONS: Record<NodeType, NodeConnectionConfig>;
|
|
11
|
+
export declare function canReceiveFrom(targetType: NodeType, sourceType: NodeType): boolean;
|
|
12
|
+
export declare function canReceiveFromNodes(nodeType: NodeType): boolean;
|
|
13
|
+
export declare function canSendToNodes(nodeType: NodeType): boolean;
|
|
14
|
+
export declare function isTerminal(nodeType: NodeType): boolean;
|
|
15
|
+
export declare function isNodeType(type: string): type is NodeType;
|
|
16
|
+
/** All valid node type strings */
|
|
17
|
+
export declare const ALL_NODE_TYPES: NodeType[];
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node Connection Registry — THE single source of truth for what each node
|
|
3
|
+
* type can receive from and send to.
|
|
4
|
+
*
|
|
5
|
+
* Used by: API (pipeline dispatch), Broker (agent wiring), Dashboard (canvas validation)
|
|
6
|
+
*
|
|
7
|
+
* Adding a new node type = add 1 entry here. All repos import from this package.
|
|
8
|
+
*/
|
|
9
|
+
/** All node types that can appear as pipeline outputs */
|
|
10
|
+
const PROCESSING_OUTPUTS = [
|
|
11
|
+
'webhook', 'router', 'filter', 'transform', 'cache', 'code', 'rateLimiter',
|
|
12
|
+
'aggregator', 'conditional', 'delay', 'schemaValidator', 'split', 'markdown', 'fileTransform', 'limit',
|
|
13
|
+
'emailAction', 'gmailAction', 'httpAction', 'mongoAction', 'postgresAction', 'notificationAction', 'sheetsAction', 'calendarAction', 'docsAction', 'driveAction', 'firecrawlAction', 'telegramAction', 'whatsappAction', 'discordAction', 'slackAction', 'googleContactsAction', 'googleAnalyticsAction', 'notionAction', 'vectorStore', 'rssAction', 'socialMediaAction', 'mailchimpAction', 'shopifyAction', 'githubAction', 'loop',
|
|
14
|
+
];
|
|
15
|
+
/** Standard processing input sources */
|
|
16
|
+
const STANDARD_INPUTS = [
|
|
17
|
+
'webhook', 'scheduledWorkflow', 'trigger', 'voiceAgent', 'router', 'filter', 'transform',
|
|
18
|
+
'cache', 'code', 'rateLimiter', 'aggregator', 'conditional', 'delay', 'schemaValidator', 'split', 'loop', 'markdown', 'fileTransform', 'limit',
|
|
19
|
+
];
|
|
20
|
+
/** Action nodes that can chain (non-terminal) */
|
|
21
|
+
const CHAINABLE_ACTIONS = ['httpAction', 'mongoAction', 'postgresAction', 'sheetsAction', 'calendarAction', 'docsAction', 'driveAction', 'firecrawlAction', 'telegramAction', 'whatsappAction', 'discordAction', 'slackAction', 'googleContactsAction', 'googleAnalyticsAction', 'notionAction', 'vectorStore', 'rssAction', 'socialMediaAction', 'mailchimpAction', 'shopifyAction', 'githubAction'];
|
|
22
|
+
/** Full input list for action nodes (standard + chainable actions) */
|
|
23
|
+
const ACTION_INPUTS = [...STANDARD_INPUTS, ...CHAINABLE_ACTIONS];
|
|
24
|
+
export const NODE_CONNECTIONS = {
|
|
25
|
+
// ── Source nodes ──
|
|
26
|
+
webhook: { acceptsInputFrom: [], canOutputTo: PROCESSING_OUTPUTS },
|
|
27
|
+
scheduledWorkflow: { acceptsInputFrom: [], canOutputTo: PROCESSING_OUTPUTS },
|
|
28
|
+
chatTrigger: { acceptsInputFrom: [], canOutputTo: PROCESSING_OUTPUTS },
|
|
29
|
+
trigger: { acceptsInputFrom: [], canOutputTo: PROCESSING_OUTPUTS },
|
|
30
|
+
voiceAgent: { acceptsInputFrom: [], canOutputTo: PROCESSING_OUTPUTS },
|
|
31
|
+
// ── Pipeline processing nodes ──
|
|
32
|
+
filter: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
33
|
+
transform: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
34
|
+
schemaValidator: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS, special: { hasRejectionOutputNodes: true } },
|
|
35
|
+
conditional: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS, special: { hasBranches: true } },
|
|
36
|
+
delay: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
37
|
+
rateLimiter: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
38
|
+
aggregator: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
39
|
+
cache: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
40
|
+
code: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
41
|
+
ai: { acceptsInputFrom: [...ACTION_INPUTS], canOutputTo: PROCESSING_OUTPUTS },
|
|
42
|
+
// ── Flow control nodes ──
|
|
43
|
+
split: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
44
|
+
// merge takes the same input set as any STANDARD pipeline node — the
|
|
45
|
+
// narrower hand-curated list it used to carry got stale every time a
|
|
46
|
+
// new processing node landed (code, markdown, fileTransform, loop)
|
|
47
|
+
// and silently blocked obvious pipelines like `code → merge`.
|
|
48
|
+
merge: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
49
|
+
// approval is the generic "human-in-the-loop gate" — it should be
|
|
50
|
+
// able to gate ANY action, not the hand-picked subset that existed
|
|
51
|
+
// when this entry was last touched. Same staleness pattern as merge.
|
|
52
|
+
approval: { acceptsInputFrom: ['webhook', 'filter', 'transform'], canOutputTo: ['webhook', 'filter', 'transform', 'emailAction', 'httpAction', 'mongoAction', 'postgresAction', 'notificationAction', 'sheetsAction', 'calendarAction', 'docsAction', 'driveAction', 'firecrawlAction', 'telegramAction', 'whatsappAction', 'discordAction', 'slackAction', 'googleContactsAction', 'vectorStore'], special: { hasRejectionOutputNodes: true } },
|
|
53
|
+
loop: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS, special: { hasLoopBack: true } },
|
|
54
|
+
// ── Routing nodes ──
|
|
55
|
+
router: { acceptsInputFrom: ['webhook', 'scheduledWorkflow', 'filter', 'transform'], canOutputTo: PROCESSING_OUTPUTS.filter(t => t !== 'webhook') },
|
|
56
|
+
// ── Action nodes ──
|
|
57
|
+
emailAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
58
|
+
gmailAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
59
|
+
httpAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
60
|
+
mongoAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
61
|
+
postgresAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
62
|
+
notificationAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: [], special: { isTerminal: true } },
|
|
63
|
+
sheetsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
64
|
+
calendarAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
65
|
+
docsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
66
|
+
driveAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
67
|
+
firecrawlAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
68
|
+
telegramAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
69
|
+
whatsappAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
70
|
+
discordAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
71
|
+
slackAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
72
|
+
mailchimpAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
73
|
+
shopifyAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
74
|
+
githubAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
75
|
+
jiraAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
76
|
+
googleContactsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
77
|
+
googleAnalyticsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
78
|
+
notionAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
79
|
+
vectorStore: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
80
|
+
rssAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
81
|
+
socialMediaAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
82
|
+
// ── Processing (transform-like) ──
|
|
83
|
+
markdown: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
84
|
+
fileTransform: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
85
|
+
limit: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
86
|
+
// ── Annotations (visual only, no pipeline processing) ──
|
|
87
|
+
stickyNote: { acceptsInputFrom: [], canOutputTo: [] },
|
|
88
|
+
};
|
|
89
|
+
// ── Helper functions ──
|
|
90
|
+
export function canReceiveFrom(targetType, sourceType) {
|
|
91
|
+
return NODE_CONNECTIONS[targetType]?.acceptsInputFrom.includes(sourceType) ?? false;
|
|
92
|
+
}
|
|
93
|
+
export function canReceiveFromNodes(nodeType) {
|
|
94
|
+
return (NODE_CONNECTIONS[nodeType]?.acceptsInputFrom.length ?? 0) > 0;
|
|
95
|
+
}
|
|
96
|
+
export function canSendToNodes(nodeType) {
|
|
97
|
+
return (NODE_CONNECTIONS[nodeType]?.canOutputTo.length ?? 0) > 0;
|
|
98
|
+
}
|
|
99
|
+
export function isTerminal(nodeType) {
|
|
100
|
+
return NODE_CONNECTIONS[nodeType]?.special?.isTerminal ?? false;
|
|
101
|
+
}
|
|
102
|
+
export function isNodeType(type) {
|
|
103
|
+
return type in NODE_CONNECTIONS;
|
|
104
|
+
}
|
|
105
|
+
/** All valid node type strings */
|
|
106
|
+
export const ALL_NODE_TYPES = Object.keys(NODE_CONNECTIONS);
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical credential-type registry. Single source of truth for the
|
|
3
|
+
* list of credential types HostWebhook supports — used by:
|
|
4
|
+
*
|
|
5
|
+
* - api Mongoose schema enum (`credential.entity.ts`)
|
|
6
|
+
* - api class-validator DTO (`create-credential.dto.ts`)
|
|
7
|
+
* - dashboard display + form metadata (`lib/credentials/display.ts`)
|
|
8
|
+
* - any future consumer that needs to enumerate types
|
|
9
|
+
*
|
|
10
|
+
* Adding a new credential type happens HERE first; both repos pick it
|
|
11
|
+
* up on the next `@hostwebhook/node-types` bump. The bug we kept
|
|
12
|
+
* hitting (entity accepting "postgres" but the DTO rejecting it)
|
|
13
|
+
* stops being possible — both sides import from this list.
|
|
14
|
+
*
|
|
15
|
+
* Each entry is **deliberately minimal**: just the `type` string +
|
|
16
|
+
* a couple of cross-repo flags that affect runtime behavior.
|
|
17
|
+
* UI-specific metadata (icons, labels, replace-form copy) lives in
|
|
18
|
+
* the dashboard's `display.ts`, where it can evolve without
|
|
19
|
+
* forcing an api-side npm bump.
|
|
20
|
+
*/
|
|
21
|
+
export interface CredentialTypeRegistration {
|
|
22
|
+
/** Stable id stored in `credentials.type`. Never rename — it's the
|
|
23
|
+
* primary key for joins from nodes that reference credentials. */
|
|
24
|
+
type: string;
|
|
25
|
+
/** When true, the URI inside `data` is validated against the SSRF
|
|
26
|
+
* guard before storage. Tunneled credentials skip the guard since
|
|
27
|
+
* the agent reaches the customer's own private network on their
|
|
28
|
+
* behalf. Currently honored by `mongodb` and `postgres`. */
|
|
29
|
+
ssrfValidated?: boolean;
|
|
30
|
+
/** When true, the underlying connection can be routed via a reverse
|
|
31
|
+
* TCP tunnel. The credential carries `metadata.tunnelId` and the
|
|
32
|
+
* runtime/test paths register a forwarder via `TcpTunnelPool`. */
|
|
33
|
+
tunnelable?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Registry — order matters for fallthrough behavior in pickers (most
|
|
37
|
+
* common types first), but not for correctness.
|
|
38
|
+
*/
|
|
39
|
+
export declare const CREDENTIAL_TYPES: readonly [{
|
|
40
|
+
readonly type: "google_service_account";
|
|
41
|
+
}, {
|
|
42
|
+
readonly type: "oauth2_google";
|
|
43
|
+
}, {
|
|
44
|
+
readonly type: "oauth2_atlassian";
|
|
45
|
+
}, {
|
|
46
|
+
readonly type: "mongodb";
|
|
47
|
+
readonly ssrfValidated: true;
|
|
48
|
+
readonly tunnelable: true;
|
|
49
|
+
}, {
|
|
50
|
+
readonly type: "postgres";
|
|
51
|
+
readonly ssrfValidated: true;
|
|
52
|
+
readonly tunnelable: true;
|
|
53
|
+
}, {
|
|
54
|
+
readonly type: "slack_webhook";
|
|
55
|
+
}, {
|
|
56
|
+
readonly type: "slack_oauth";
|
|
57
|
+
}, {
|
|
58
|
+
readonly type: "discord_webhook";
|
|
59
|
+
}, {
|
|
60
|
+
readonly type: "llm_anthropic";
|
|
61
|
+
}, {
|
|
62
|
+
readonly type: "llm_openai";
|
|
63
|
+
}, {
|
|
64
|
+
readonly type: "llm_google";
|
|
65
|
+
}, {
|
|
66
|
+
readonly type: "llm_groq";
|
|
67
|
+
}, {
|
|
68
|
+
readonly type: "llm_openrouter";
|
|
69
|
+
}, {
|
|
70
|
+
readonly type: "llm_ollama";
|
|
71
|
+
}, {
|
|
72
|
+
readonly type: "firecrawl";
|
|
73
|
+
}, {
|
|
74
|
+
readonly type: "http_auth";
|
|
75
|
+
}, {
|
|
76
|
+
readonly type: "aws_s3";
|
|
77
|
+
}, {
|
|
78
|
+
readonly type: "memory_contextwindow";
|
|
79
|
+
}, {
|
|
80
|
+
readonly type: "memory_mongodb";
|
|
81
|
+
readonly ssrfValidated: true;
|
|
82
|
+
readonly tunnelable: true;
|
|
83
|
+
}, {
|
|
84
|
+
readonly type: "mcp_server";
|
|
85
|
+
}, {
|
|
86
|
+
readonly type: "voice_agent";
|
|
87
|
+
}, {
|
|
88
|
+
readonly type: "telegram_bot";
|
|
89
|
+
}, {
|
|
90
|
+
readonly type: "whatsapp_business";
|
|
91
|
+
}, {
|
|
92
|
+
readonly type: "discord_bot";
|
|
93
|
+
}, {
|
|
94
|
+
readonly type: "discord_oauth";
|
|
95
|
+
}, {
|
|
96
|
+
readonly type: "bluesky_app_password";
|
|
97
|
+
}, {
|
|
98
|
+
readonly type: "twitter_oauth2";
|
|
99
|
+
}, {
|
|
100
|
+
readonly type: "mastodon_oauth2";
|
|
101
|
+
}, {
|
|
102
|
+
readonly type: "linkedin_oauth2";
|
|
103
|
+
}, {
|
|
104
|
+
readonly type: "threads_oauth2";
|
|
105
|
+
}, {
|
|
106
|
+
readonly type: "instagram_oauth2";
|
|
107
|
+
}, {
|
|
108
|
+
readonly type: "facebook_oauth2";
|
|
109
|
+
}, {
|
|
110
|
+
readonly type: "notion_internal";
|
|
111
|
+
}, {
|
|
112
|
+
readonly type: "notion_oauth";
|
|
113
|
+
}, {
|
|
114
|
+
readonly type: "mailchimp_oauth2";
|
|
115
|
+
}, {
|
|
116
|
+
readonly type: "shopify_oauth2";
|
|
117
|
+
}, {
|
|
118
|
+
readonly type: "github_app";
|
|
119
|
+
}, {
|
|
120
|
+
readonly type: "github_pat";
|
|
121
|
+
}];
|
|
122
|
+
/**
|
|
123
|
+
* Plain string array — what the Mongoose `enum` field and the
|
|
124
|
+
* class-validator `@IsIn(...)` decorator both consume. Keep it as a
|
|
125
|
+
* readonly array so misuse (push/pop) breaks at compile time.
|
|
126
|
+
*/
|
|
127
|
+
export declare const CREDENTIAL_TYPE_VALUES: readonly string[];
|
|
128
|
+
/** Mutable copy for APIs that take `string[]` (Mongoose `enum`,
|
|
129
|
+
* class-validator). The array itself is freshly built each access
|
|
130
|
+
* so callers can't mutate the canonical list. */
|
|
131
|
+
export declare const credentialTypeValues: () => string[];
|
|
132
|
+
/**
|
|
133
|
+
* Discriminated union of every supported type. Use as the field type
|
|
134
|
+
* on entities + DTOs so a typo at the call site fails at compile
|
|
135
|
+
* time, not at runtime via a Mongoose validation error.
|
|
136
|
+
*
|
|
137
|
+
* ⚠️ Y ahora ES una unión. Hasta la 1.69.0 esto valía `string` —la anotación
|
|
138
|
+
* de `CREDENTIAL_TYPES` ensanchaba los literales— así que la frase de arriba
|
|
139
|
+
* describía una protección que no existía. Ver el comentario del `satisfies`
|
|
140
|
+
* al final del registro.
|
|
141
|
+
*/
|
|
142
|
+
export type CredentialType = (typeof CREDENTIAL_TYPES)[number]['type'];
|
|
143
|
+
export declare function getCredentialType(type: string): CredentialTypeRegistration | undefined;
|
|
144
|
+
export declare function isCredentialType(type: string): type is CredentialType;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry — order matters for fallthrough behavior in pickers (most
|
|
3
|
+
* common types first), but not for correctness.
|
|
4
|
+
*/
|
|
5
|
+
export const CREDENTIAL_TYPES = [
|
|
6
|
+
{ type: 'google_service_account' },
|
|
7
|
+
{ type: 'oauth2_google' },
|
|
8
|
+
{ type: 'oauth2_atlassian' },
|
|
9
|
+
{ type: 'mongodb', ssrfValidated: true, tunnelable: true },
|
|
10
|
+
{ type: 'postgres', ssrfValidated: true, tunnelable: true },
|
|
11
|
+
{ type: 'slack_webhook' },
|
|
12
|
+
{ type: 'slack_oauth' },
|
|
13
|
+
{ type: 'discord_webhook' },
|
|
14
|
+
{ type: 'llm_anthropic' },
|
|
15
|
+
{ type: 'llm_openai' },
|
|
16
|
+
{ type: 'llm_google' },
|
|
17
|
+
// La clave de API de Groq (`gsk_…`), que el usuario saca de
|
|
18
|
+
// https://console.groq.com/keys y pega. Un secreto suyo, sin OAuth de por
|
|
19
|
+
// medio, igual que los tres de arriba y que `llm_openrouter` de abajo.
|
|
20
|
+
//
|
|
21
|
+
// El sufijo es `groq` por la misma regla que explica el bloque de
|
|
22
|
+
// OpenRouter: de aquí salen `getModelsFor`/`getDefaultModel` quitando el
|
|
23
|
+
// prefijo `llm_`, así que lo que quede detrás tiene que ser LITERALMENTE el
|
|
24
|
+
// `LlmProvider`, y ése es `'groq'`.
|
|
25
|
+
//
|
|
26
|
+
// 🔥 Llega TARDE, y el agujero que tapa no era teórico: `groq` es un
|
|
27
|
+
// `LlmProvider` de pleno derecho desde hace tiempo —está en `LLM_PROVIDERS`,
|
|
28
|
+
// con sus cinco modelos en `LLM_MODELS.groq`— pero no tenía tipo de
|
|
29
|
+
// credencial. O sea que el proveedor se podía ELEGIR en el selector y no
|
|
30
|
+
// había dónde guardar su clave: una rama del catálogo inalcanzable, sin un
|
|
31
|
+
// solo error que lo dijera. El resto de la lista se anota antes de que la
|
|
32
|
+
// api exista precisamente para no llegar a esto.
|
|
33
|
+
{ type: 'llm_groq' },
|
|
34
|
+
// La clave de API de OpenRouter (`sk-or-v1-…`). Un secreto pegado por el
|
|
35
|
+
// usuario, igual que los tres de arriba: no hay OAuth de por medio.
|
|
36
|
+
//
|
|
37
|
+
// El sufijo es `openrouter` y no `open_router` porque de aquí sale
|
|
38
|
+
// `getModelsFor`/`getDefaultModel` quitando el prefijo `llm_` — el resto
|
|
39
|
+
// tiene que ser LITERALMENTE el `LlmProvider`, y ése es `'openrouter'`.
|
|
40
|
+
//
|
|
41
|
+
// ⚠️ Se anota aquí ANTES de que exista nada en la api, por lo que cuenta el
|
|
42
|
+
// bloque de Mailchimp más abajo: de esta lista salen el `enum` de Mongoose y
|
|
43
|
+
// el validador del DTO, y un tipo que falte ahí no rompe al compilar — mata
|
|
44
|
+
// el `create` de la credencial en runtime, con el usuario delante.
|
|
45
|
+
//
|
|
46
|
+
// ⚠️ El vecino que faltaba y que este bloque dejó escrito —`groq` sin su
|
|
47
|
+
// `llm_groq`— ya está arriba. Lo que queda de aquel aviso es la regla que lo
|
|
48
|
+
// hizo posible, y por eso la cubre ahora un test: cada `LlmProvider` de
|
|
49
|
+
// `LLM_PROVIDERS` tiene que tener su `llm_*` en esta lista.
|
|
50
|
+
{ type: 'llm_openrouter' },
|
|
51
|
+
{ type: 'llm_ollama' },
|
|
52
|
+
{ type: 'firecrawl' },
|
|
53
|
+
{ type: 'http_auth' },
|
|
54
|
+
{ type: 'aws_s3' },
|
|
55
|
+
{ type: 'memory_contextwindow' },
|
|
56
|
+
{ type: 'memory_mongodb', ssrfValidated: true, tunnelable: true },
|
|
57
|
+
{ type: 'mcp_server' },
|
|
58
|
+
{ type: 'voice_agent' },
|
|
59
|
+
{ type: 'telegram_bot' },
|
|
60
|
+
{ type: 'whatsapp_business' },
|
|
61
|
+
{ type: 'discord_bot' },
|
|
62
|
+
// Instalar la app de HostWebhook en un servidor de Discord, vía OAuth.
|
|
63
|
+
//
|
|
64
|
+
// Es un tipo aparte de `discord_bot` y no una bandera en metadata porque
|
|
65
|
+
// lo que guarda es DISTINTO: `discord_bot` lleva el token que pegó el
|
|
66
|
+
// usuario; éste no lleva ningún secreto — el bot token es de la aplicación
|
|
67
|
+
// y vive en el entorno. Lo que guarda es a qué servidor se autorizó.
|
|
68
|
+
//
|
|
69
|
+
// Discord no devuelve un bot token por OAuth (es estático de la app), así
|
|
70
|
+
// que los dos caminos coexisten: OAuth para el caso normal, y el token
|
|
71
|
+
// propio para quien quiera un bot con su marca.
|
|
72
|
+
{ type: 'discord_oauth' },
|
|
73
|
+
{ type: 'bluesky_app_password' },
|
|
74
|
+
{ type: 'twitter_oauth2' },
|
|
75
|
+
{ type: 'mastodon_oauth2' },
|
|
76
|
+
{ type: 'linkedin_oauth2' },
|
|
77
|
+
{ type: 'threads_oauth2' },
|
|
78
|
+
{ type: 'instagram_oauth2' },
|
|
79
|
+
{ type: 'facebook_oauth2' },
|
|
80
|
+
// El secreto de una integración INTERNA de Notion (empieza por `ntn_`),
|
|
81
|
+
// pegado por el usuario. Un solo workspace, no caduca.
|
|
82
|
+
//
|
|
83
|
+
// Va aparte de un futuro `notion_oauth` por la misma razón que
|
|
84
|
+
// `discord_bot` va aparte de `discord_oauth`: lo que guardan es distinto.
|
|
85
|
+
// Aquí hay un secreto del usuario; en OAuth habría un access_token y un
|
|
86
|
+
// refresh_token de una app de HostWebhook.
|
|
87
|
+
//
|
|
88
|
+
// Y el orden importa: la URL de autorización de una integración PÚBLICA de
|
|
89
|
+
// Notion sólo se rellena tras mandarla a revisión de Notion, así que el
|
|
90
|
+
// camino interno es el único que funciona el primer día.
|
|
91
|
+
{ type: 'notion_internal' },
|
|
92
|
+
// El OAuth de HostWebhook contra Notion: el usuario autoriza y elige qué
|
|
93
|
+
// páginas comparte, sin salir del producto.
|
|
94
|
+
//
|
|
95
|
+
// Va aparte de `notion_internal` por la misma razón que `discord_oauth` va
|
|
96
|
+
// aparte de `discord_bot`: lo que guardan es distinto. Ahí hay un secreto
|
|
97
|
+
// del usuario; aquí, un access_token y un refresh_token de NUESTRA app, más
|
|
98
|
+
// el `workspace_id` que Notion devuelve en el mismo intercambio.
|
|
99
|
+
//
|
|
100
|
+
// Y para que conste, porque estuvo escrito al revés en el ADR-0005 antes de
|
|
101
|
+
// corregirlo: montar esto NO espera a ninguna revisión de Notion. El
|
|
102
|
+
// formulario de Distribution entrega client id, secret y URL de
|
|
103
|
+
// autorización al pulsar Submit; la revisión de 5-10 días es para salir en
|
|
104
|
+
// su galería, o sea para que te encuentren, no para que funcione.
|
|
105
|
+
{ type: 'notion_oauth' },
|
|
106
|
+
// El OAuth de HostWebhook contra Mailchimp.
|
|
107
|
+
//
|
|
108
|
+
// Faltaba, y el síntoma no señalaba aquí. La api ya tenía el flujo entero
|
|
109
|
+
// —`mailchimp-oauth.service.ts`, su controlador, su DTO y sus presets— y el
|
|
110
|
+
// usuario autorizaba bien; lo que moría era el `create` de la credencial:
|
|
111
|
+
//
|
|
112
|
+
// ERROR [CredentialsController] Mailchimp OAuth callback failed:
|
|
113
|
+
// Credential validation failed: type: `mailchimp_oauth2` is not a valid
|
|
114
|
+
// enum value for path `type`.
|
|
115
|
+
//
|
|
116
|
+
// Porque de esta lista salen las DOS validaciones que ve ese documento: el
|
|
117
|
+
// `enum` de Mongoose en `credential.entity.ts` y el `@IsIn` del DTO. La api
|
|
118
|
+
// declaraba el string por su cuenta (`MAILCHIMP_CREDENTIAL_TYPE`) sin
|
|
119
|
+
// anotarlo `: CredentialType`, así que TypeScript lo dio por bueno y sólo
|
|
120
|
+
// reventó en producción, contra un cliente que no podía conectar su cuenta.
|
|
121
|
+
//
|
|
122
|
+
// Lleva `_oauth2` y no `_oauth` —al revés que `notion_oauth`— porque ése es
|
|
123
|
+
// el string que la api ya guarda y que el dashboard ya filtra en
|
|
124
|
+
// `components/triggers/registry.tsx` (`scopeFilter: "mailchimp_oauth2"`).
|
|
125
|
+
// Renombrarlo ahora rompería las credenciales que hayan llegado a existir:
|
|
126
|
+
// el `type` es la clave por la que los nodos las referencian.
|
|
127
|
+
{ type: 'mailchimp_oauth2' },
|
|
128
|
+
// El OAuth de HostWebhook contra Shopify.
|
|
129
|
+
//
|
|
130
|
+
// Se anota aquí ANTES de escribir el servicio, precisamente por lo que
|
|
131
|
+
// cuenta el bloque de Mailchimp de arriba: la api puede tener el flujo
|
|
132
|
+
// entero y bien, y aun así el `create` de la credencial muere con un enum
|
|
133
|
+
// de Mongoose que TypeScript no ve. Es el mismo agujero, y ya sabemos que
|
|
134
|
+
// no avisa al compilar.
|
|
135
|
+
//
|
|
136
|
+
// Lleva `_oauth2` por coherencia con `mailchimp_oauth2`, y porque el
|
|
137
|
+
// `scopeFilter` del dashboard filtra por esta cadena exacta.
|
|
138
|
+
//
|
|
139
|
+
// Una nota sobre lo que este tipo GUARDA, que aquí no se ve: el dominio de
|
|
140
|
+
// la tienda (`{shop}.myshopify.com`) viaja DENTRO del blob cifrado, igual
|
|
141
|
+
// que el `dc` de Mailchimp. Es parte del host de toda llamada y de la URL
|
|
142
|
+
// de autorización, así que una credencial de Shopify sin su dominio es
|
|
143
|
+
// inservible — no un detalle de presentación.
|
|
144
|
+
{ type: 'shopify_oauth2' },
|
|
145
|
+
// La instalación de la GitHub App de HostWebhook. El camino PRINCIPAL: el
|
|
146
|
+
// usuario pulsa "Connect GitHub", elige en qué repos entra, y vuelve.
|
|
147
|
+
//
|
|
148
|
+
// Lo que guarda es un `installationId` y NINGÚN secreto — la private key es
|
|
149
|
+
// de la app y vive en el entorno (`GITHUB_APP_PRIVATE_KEY_BASE64`). El token
|
|
150
|
+
// de instalación es corto y se re-emite firmando con esa llave, así que aquí
|
|
151
|
+
// no hay nada que caduque en la base de datos.
|
|
152
|
+
//
|
|
153
|
+
// Va aparte de `github_pat` por lo mismo que `discord_oauth` va aparte de
|
|
154
|
+
// `discord_bot`: lo que guardan es distinto.
|
|
155
|
+
//
|
|
156
|
+
// ⚠️ No confundir con `GITHUB_OAUTH_CLIENT_ID`/`_SECRET`, que ya existían y
|
|
157
|
+
// son de una OAuth App DISTINTA — la que usa `MCP_OAUTH_PRESETS.github` para
|
|
158
|
+
// llegar al servidor MCP de GitHub. Dos registros, dos pares de credenciales,
|
|
159
|
+
// y conviven a propósito: el MCP es la cola larga dentro del AI Node, esto
|
|
160
|
+
// son los rieles del nodo.
|
|
161
|
+
{ type: 'github_app' },
|
|
162
|
+
// Un fine-grained personal access token pegado por el usuario. La salida de
|
|
163
|
+
// emergencia, detrás de "Use a personal access token instead".
|
|
164
|
+
//
|
|
165
|
+
// Aquí SÍ hay un secreto del usuario, y además caduca: GitHub obliga a
|
|
166
|
+
// ponerles fecha. Un aviso antes de que muera es trabajo pendiente, no de
|
|
167
|
+
// esta versión.
|
|
168
|
+
//
|
|
169
|
+
// Ejecuta exactamente las mismas 14 operaciones que `github_app`. Lo único
|
|
170
|
+
// que cambia es de dónde sale la lista de repositorios del formulario: los
|
|
171
|
+
// del usuario, en vez de los que abarca una instalación.
|
|
172
|
+
{ type: 'github_pat' },
|
|
173
|
+
/* ⚠️ `as const satisfies …` y no `: readonly CredentialTypeRegistration[]`.
|
|
174
|
+
Los dos comprueban lo mismo —que cada entrada tenga la forma del registro—,
|
|
175
|
+
pero la ANOTACIÓN además ENSANCHA lo que se guarda: con ella cada `type` se
|
|
176
|
+
recuerda como `string`, y entonces `CredentialType` de más abajo es
|
|
177
|
+
literalmente `string`. O sea que la promesa de su comentario («un typo al
|
|
178
|
+
asignar falla al compilar») era falsa desde el primer día: `const t:
|
|
179
|
+
CredentialType = 'llm_openroutr'` compilaba sin rechistar.
|
|
180
|
+
|
|
181
|
+
`satisfies` comprueba y NO ensancha, así que los literales sobreviven y la
|
|
182
|
+
unión es de verdad. El control está en `tipos-de-credencial.test.ts`. */
|
|
183
|
+
];
|
|
184
|
+
/**
|
|
185
|
+
* Plain string array — what the Mongoose `enum` field and the
|
|
186
|
+
* class-validator `@IsIn(...)` decorator both consume. Keep it as a
|
|
187
|
+
* readonly array so misuse (push/pop) breaks at compile time.
|
|
188
|
+
*/
|
|
189
|
+
export const CREDENTIAL_TYPE_VALUES = CREDENTIAL_TYPES.map((c) => c.type);
|
|
190
|
+
/** Mutable copy for APIs that take `string[]` (Mongoose `enum`,
|
|
191
|
+
* class-validator). The array itself is freshly built each access
|
|
192
|
+
* so callers can't mutate the canonical list. */
|
|
193
|
+
export const credentialTypeValues = () => [...CREDENTIAL_TYPE_VALUES];
|
|
194
|
+
export function getCredentialType(type) {
|
|
195
|
+
return CREDENTIAL_TYPES.find((c) => c.type === type);
|
|
196
|
+
}
|
|
197
|
+
export function isCredentialType(type) {
|
|
198
|
+
return CREDENTIAL_TYPE_VALUES.includes(type);
|
|
199
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discord Bot API operation enum — single source of truth across the
|
|
3
|
+
* API, Dashboard, and any future consumers (Message Broker, MCP server).
|
|
4
|
+
* Used by:
|
|
5
|
+
* - discordAction entity / DTO (api): operation field + validation
|
|
6
|
+
* - dashboard DiscordAction operation picker + form generation
|
|
7
|
+
* - MCP toolkit-specs expansion (one aiEnabled discordAction → N tools)
|
|
8
|
+
*
|
|
9
|
+
* Targets Discord API v10 (the current stable as of 2026). Each op maps
|
|
10
|
+
* to a single REST call — most go through the Bot Token to the channel/
|
|
11
|
+
* guild/user resource. createThread + createChannel + sendDM each have
|
|
12
|
+
* their own multi-step flows handled inside the dispatcher.
|
|
13
|
+
*
|
|
14
|
+
* Phase 1 covers 17 ops that handle the bulk of bot automation
|
|
15
|
+
* (messaging, channel CRUD, threads, DMs, role membership, lookups).
|
|
16
|
+
* Phase 1.5 candidates (kick / ban / timeout / role CRUD / bulk delete /
|
|
17
|
+
* forum-post create) extend this list — bump the package and re-install
|
|
18
|
+
* in consumers, same pattern as Gmail / Calendar / Drive / Telegram.
|
|
19
|
+
*/
|
|
20
|
+
import { type CapacidadesPorCredencial } from './capacidad-de-credencial.js';
|
|
21
|
+
export declare const DISCORD_OPERATIONS: readonly ["sendMessage", "editMessage", "deleteMessage", "getMessage", "addReaction", "removeReaction", "pinMessage", "createChannel", "editChannel", "deleteChannel", "getChannel", "listChannels", "createThread", "sendDM", "addRole", "removeRole", "getMember"];
|
|
22
|
+
export type DiscordOperation = (typeof DISCORD_OPERATIONS)[number];
|
|
23
|
+
/** Type guard — useful when validating untrusted input (DTOs, tool calls). */
|
|
24
|
+
export declare function isDiscordOperation(value: unknown): value is DiscordOperation;
|
|
25
|
+
export interface DiscordParamSpec {
|
|
26
|
+
/** Field key — also the property name on operationConfig / payload. */
|
|
27
|
+
name: string;
|
|
28
|
+
/** UI label. Carries the "(optional)" suffix when the form shows one. */
|
|
29
|
+
label: string;
|
|
30
|
+
/**
|
|
31
|
+
* Param type for form rendering + AI tool schema.
|
|
32
|
+
*
|
|
33
|
+
* `channel`, `string`, `text`, `number`, `boolean` and `emoji` mean the same
|
|
34
|
+
* as in SlackParamSpec. `guild`, `role`, `member` and `select` are Discord's
|
|
35
|
+
* own: the first three map to its resource pickers, and `select` is a fixed
|
|
36
|
+
* option list (channel kind, thread kind, archive window) that Slack has no
|
|
37
|
+
* equivalent for.
|
|
38
|
+
*/
|
|
39
|
+
type: 'channel' | 'guild' | 'role' | 'member' | 'string' | 'text' | 'number' | 'boolean' | 'emoji' | 'select';
|
|
40
|
+
/** Required at submit time. */
|
|
41
|
+
required?: boolean;
|
|
42
|
+
/** Human description — also used as the AI tool param description. */
|
|
43
|
+
description: string;
|
|
44
|
+
/** Hint shown inside the input. */
|
|
45
|
+
placeholder?: string;
|
|
46
|
+
/** Value to show when the stored one is undefined. */
|
|
47
|
+
default?: string | number | boolean;
|
|
48
|
+
/** `select` only — the fixed option list, in display order. */
|
|
49
|
+
options?: Array<{
|
|
50
|
+
value: string | number;
|
|
51
|
+
label: string;
|
|
52
|
+
}>;
|
|
53
|
+
/** `number` only. */
|
|
54
|
+
min?: number;
|
|
55
|
+
max?: number;
|
|
56
|
+
/** `text` only — height of the textarea. */
|
|
57
|
+
rows?: number;
|
|
58
|
+
/** `channel` only — let categories be picked, for parent-category fields. */
|
|
59
|
+
allowCategories?: boolean;
|
|
60
|
+
/** `boolean` only — the bold line beside the switch. */
|
|
61
|
+
switchLabel?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface DiscordOperationSpec {
|
|
64
|
+
/** UI label for the operation picker. */
|
|
65
|
+
label: string;
|
|
66
|
+
/** Description shown in the picker + reused by the AI tool description
|
|
67
|
+
* when the user hasn't set a custom one on the entity. */
|
|
68
|
+
description: string;
|
|
69
|
+
/** Route the operation maps to in Discord's docs (discord.com/developers). */
|
|
70
|
+
apiMethod: string;
|
|
71
|
+
/** Parameter schema. Order matters for form rendering. */
|
|
72
|
+
params: DiscordParamSpec[];
|
|
73
|
+
}
|
|
74
|
+
export declare const DISCORD_OPERATION_SPECS: Record<DiscordOperation, DiscordOperationSpec>;
|
|
75
|
+
/**
|
|
76
|
+
* Lo que puede hacer una credencial de Discord que NO es de bot.
|
|
77
|
+
*
|
|
78
|
+
* Medido contra la documentación de Discord, no de memoria:
|
|
79
|
+
* https://docs.discord.com/developers/resources/webhook
|
|
80
|
+
*
|
|
81
|
+
* `Execute Webhook` publica, y `Get/Edit/Delete Webhook Message` leen, editan y
|
|
82
|
+
* borran mensajes — pero **sólo los que envió ese mismo webhook**, y sólo en el
|
|
83
|
+
* canal para el que se creó. Las otras 13 (reacciones, pins, CRUD de canales,
|
|
84
|
+
* crear hilos, DMs, roles, miembros) piden un Bot Token: la propia doc las
|
|
85
|
+
* marca con `MANAGE_WEBHOOKS` o con permisos de guild.
|
|
86
|
+
*
|
|
87
|
+
* Los tres campos que se caen de `sendMessage`, uno por uno:
|
|
88
|
+
*
|
|
89
|
+
* - `channelId`: la URL del webhook YA dice el canal. El endpoint ni siquiera
|
|
90
|
+
* acepta `channel_id`.
|
|
91
|
+
* - `replyToMessageId`: `Execute Webhook` no acepta `message_reference`. No
|
|
92
|
+
* es que venga fijado — es que responder no existe por esta vía.
|
|
93
|
+
* - `interactionReply`: contestar a un slash command es cosa de la
|
|
94
|
+
* aplicación, no de un webhook.
|
|
95
|
+
*
|
|
96
|
+
* `threadId` SÍ se queda: `?thread_id=` es parámetro de query del endpoint.
|
|
97
|
+
*/
|
|
98
|
+
export declare const DISCORD_CAPACIDADES_POR_CREDENCIAL: CapacidadesPorCredencial<DiscordOperation>;
|
|
99
|
+
/** Las operaciones que ofrecer para una credencial de Discord. */
|
|
100
|
+
export declare function operacionesDeDiscordPara(tipoDeCredencial: string | null | undefined): readonly DiscordOperation[];
|
|
101
|
+
/** Si esa credencial puede ejecutar esa operación de Discord. */
|
|
102
|
+
export declare function discordPuedeEjecutar(tipoDeCredencial: string | null | undefined, operacion: DiscordOperation): boolean;
|
|
103
|
+
/** Los campos que esa credencial de Discord no deja elegir. */
|
|
104
|
+
export declare function camposDeDiscordNoDisponibles(tipoDeCredencial: string | null | undefined): ReadonlySet<string>;
|