@hostwebhook/node-types 1.69.0 → 1.71.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.
Files changed (101) hide show
  1. package/dist/bucket-operations.d.ts +88 -0
  2. package/dist/bucket-operations.js +140 -0
  3. package/dist/calendar-toolkit.d.ts +1 -1
  4. package/dist/connections.d.ts +1 -1
  5. package/dist/connections.js +1 -0
  6. package/dist/discord-operations.d.ts +1 -1
  7. package/dist/discord-operations.js +4 -4
  8. package/dist/discord-toolkit.d.ts +1 -1
  9. package/dist/discord-toolkit.js +3 -3
  10. package/dist/dispatch.d.ts +1 -1
  11. package/dist/dispatch.js +1 -0
  12. package/dist/docs-toolkit.d.ts +1 -1
  13. package/dist/drive-toolkit.d.ts +1 -1
  14. package/dist/esm/bucket-operations.d.ts +88 -0
  15. package/dist/esm/bucket-operations.js +136 -0
  16. package/dist/esm/calendar-operations.d.ts +67 -0
  17. package/dist/esm/calendar-operations.js +176 -0
  18. package/dist/esm/calendar-toolkit.d.ts +50 -0
  19. package/dist/esm/calendar-toolkit.js +152 -0
  20. package/dist/esm/capacidad-de-credencial.d.ts +75 -0
  21. package/dist/esm/capacidad-de-credencial.js +68 -0
  22. package/dist/esm/connections.d.ts +17 -0
  23. package/dist/esm/connections.js +107 -0
  24. package/dist/esm/credentials.d.ts +144 -0
  25. package/dist/esm/credentials.js +199 -0
  26. package/dist/esm/discord-operations.d.ts +104 -0
  27. package/dist/esm/discord-operations.js +407 -0
  28. package/dist/esm/discord-toolkit.d.ts +64 -0
  29. package/dist/esm/discord-toolkit.js +260 -0
  30. package/dist/esm/dispatch.d.ts +16 -0
  31. package/dist/esm/dispatch.js +77 -0
  32. package/dist/esm/docs-operations.d.ts +99 -0
  33. package/dist/esm/docs-operations.js +127 -0
  34. package/dist/esm/docs-toolkit.d.ts +42 -0
  35. package/dist/esm/docs-toolkit.js +97 -0
  36. package/dist/esm/drive-operations.d.ts +103 -0
  37. package/dist/esm/drive-operations.js +319 -0
  38. package/dist/esm/drive-toolkit.d.ts +54 -0
  39. package/dist/esm/drive-toolkit.js +177 -0
  40. package/dist/esm/github-operations.d.ts +147 -0
  41. package/dist/esm/github-operations.js +551 -0
  42. package/dist/esm/gmail-operations.d.ts +190 -0
  43. package/dist/esm/gmail-operations.js +560 -0
  44. package/dist/esm/google-analytics-operations.d.ts +99 -0
  45. package/dist/esm/google-analytics-operations.js +222 -0
  46. package/dist/esm/google-contacts-operations.d.ts +93 -0
  47. package/dist/esm/google-contacts-operations.js +363 -0
  48. package/dist/esm/index.d.ts +68 -0
  49. package/dist/esm/index.js +51 -0
  50. package/dist/esm/jira-operations.d.ts +164 -0
  51. package/dist/esm/jira-operations.js +470 -0
  52. package/dist/esm/llm-models.d.ts +101 -0
  53. package/dist/esm/llm-models.js +230 -0
  54. package/dist/esm/mailchimp-operations.d.ts +56 -0
  55. package/dist/esm/mailchimp-operations.js +155 -0
  56. package/dist/esm/mongo-operations.d.ts +61 -0
  57. package/dist/esm/mongo-operations.js +120 -0
  58. package/dist/esm/notion-operations.d.ts +125 -0
  59. package/dist/esm/notion-operations.js +511 -0
  60. package/dist/esm/openrouter.d.ts +142 -0
  61. package/dist/esm/openrouter.js +99 -0
  62. package/dist/esm/package.json +4 -0
  63. package/dist/esm/postgres-operations.d.ts +60 -0
  64. package/dist/esm/postgres-operations.js +75 -0
  65. package/dist/esm/registry.d.ts +160 -0
  66. package/dist/esm/registry.js +885 -0
  67. package/dist/esm/sheets-operations.d.ts +78 -0
  68. package/dist/esm/sheets-operations.js +150 -0
  69. package/dist/esm/sheets-toolkit.d.ts +49 -0
  70. package/dist/esm/sheets-toolkit.js +127 -0
  71. package/dist/esm/shopify-operations.d.ts +95 -0
  72. package/dist/esm/shopify-operations.js +259 -0
  73. package/dist/esm/slack-operations.d.ts +81 -0
  74. package/dist/esm/slack-operations.js +244 -0
  75. package/dist/esm/slack-toolkit.d.ts +60 -0
  76. package/dist/esm/slack-toolkit.js +235 -0
  77. package/dist/esm/telegram-operations.d.ts +104 -0
  78. package/dist/esm/telegram-operations.js +351 -0
  79. package/dist/esm/telegram-toolkit.d.ts +52 -0
  80. package/dist/esm/telegram-toolkit.js +183 -0
  81. package/dist/esm/types.d.ts +149 -0
  82. package/dist/esm/types.js +8 -0
  83. package/dist/esm/ui.d.ts +13 -0
  84. package/dist/esm/ui.js +152 -0
  85. package/dist/esm/whatsapp-operations.d.ts +23 -0
  86. package/dist/esm/whatsapp-operations.js +36 -0
  87. package/dist/index.d.ts +68 -66
  88. package/dist/index.js +193 -187
  89. package/dist/openrouter.d.ts +1 -1
  90. package/dist/registry.d.ts +4 -4
  91. package/dist/registry.js +540 -210
  92. package/dist/sheets-toolkit.d.ts +1 -1
  93. package/dist/slack-operations.d.ts +1 -1
  94. package/dist/slack-operations.js +4 -4
  95. package/dist/slack-toolkit.d.ts +1 -1
  96. package/dist/slack-toolkit.js +3 -3
  97. package/dist/telegram-toolkit.d.ts +1 -1
  98. package/dist/types.d.ts +1 -1
  99. package/dist/ui.d.ts +1 -1
  100. package/dist/ui.js +2 -0
  101. package/package.json +21 -3
@@ -0,0 +1,107 @@
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
+ bucketAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
77
+ googleContactsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
78
+ googleAnalyticsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
79
+ notionAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
80
+ vectorStore: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
81
+ rssAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
82
+ socialMediaAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
83
+ // ── Processing (transform-like) ──
84
+ markdown: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
85
+ fileTransform: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
86
+ limit: { acceptsInputFrom: STANDARD_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
87
+ // ── Annotations (visual only, no pipeline processing) ──
88
+ stickyNote: { acceptsInputFrom: [], canOutputTo: [] },
89
+ };
90
+ // ── Helper functions ──
91
+ export function canReceiveFrom(targetType, sourceType) {
92
+ return NODE_CONNECTIONS[targetType]?.acceptsInputFrom.includes(sourceType) ?? false;
93
+ }
94
+ export function canReceiveFromNodes(nodeType) {
95
+ return (NODE_CONNECTIONS[nodeType]?.acceptsInputFrom.length ?? 0) > 0;
96
+ }
97
+ export function canSendToNodes(nodeType) {
98
+ return (NODE_CONNECTIONS[nodeType]?.canOutputTo.length ?? 0) > 0;
99
+ }
100
+ export function isTerminal(nodeType) {
101
+ return NODE_CONNECTIONS[nodeType]?.special?.isTerminal ?? false;
102
+ }
103
+ export function isNodeType(type) {
104
+ return type in NODE_CONNECTIONS;
105
+ }
106
+ /** All valid node type strings */
107
+ 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>;