@hostwebhook/node-types 1.52.4 → 1.52.6

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.
@@ -30,6 +30,17 @@ exports.CREDENTIAL_TYPES = [
30
30
  { type: 'telegram_bot' },
31
31
  { type: 'whatsapp_business' },
32
32
  { type: 'discord_bot' },
33
+ // Instalar la app de HostWebhook en un servidor de Discord, vía OAuth.
34
+ //
35
+ // Es un tipo aparte de `discord_bot` y no una bandera en metadata porque
36
+ // lo que guarda es DISTINTO: `discord_bot` lleva el token que pegó el
37
+ // usuario; éste no lleva ningún secreto — el bot token es de la aplicación
38
+ // y vive en el entorno. Lo que guarda es a qué servidor se autorizó.
39
+ //
40
+ // Discord no devuelve un bot token por OAuth (es estático de la app), así
41
+ // que los dos caminos coexisten: OAuth para el caso normal, y el token
42
+ // propio para quien quiera un bot con su marca.
43
+ { type: 'discord_oauth' },
33
44
  { type: 'bluesky_app_password' },
34
45
  { type: 'twitter_oauth2' },
35
46
  { type: 'mastodon_oauth2' },
@@ -3,7 +3,7 @@
3
3
  * API, Dashboard, and any future consumers (Message Broker, MCP server).
4
4
  * Used by:
5
5
  * - discordAction entity / DTO (api): operation field + validation
6
- * - dashboard DiscordAction operation picker
6
+ * - dashboard DiscordAction operation picker + form generation
7
7
  * - MCP toolkit-specs expansion (one aiEnabled discordAction → N tools)
8
8
  *
9
9
  * Targets Discord API v10 (the current stable as of 2026). Each op maps
@@ -21,3 +21,53 @@ export declare const DISCORD_OPERATIONS: readonly ["sendMessage", "editMessage",
21
21
  export type DiscordOperation = (typeof DISCORD_OPERATIONS)[number];
22
22
  /** Type guard — useful when validating untrusted input (DTOs, tool calls). */
23
23
  export declare function isDiscordOperation(value: unknown): value is DiscordOperation;
24
+ export interface DiscordParamSpec {
25
+ /** Field key — also the property name on operationConfig / payload. */
26
+ name: string;
27
+ /** UI label. Carries the "(optional)" suffix when the form shows one. */
28
+ label: string;
29
+ /**
30
+ * Param type for form rendering + AI tool schema.
31
+ *
32
+ * `channel`, `string`, `text`, `number`, `boolean` and `emoji` mean the same
33
+ * as in SlackParamSpec. `guild`, `role`, `member` and `select` are Discord's
34
+ * own: the first three map to its resource pickers, and `select` is a fixed
35
+ * option list (channel kind, thread kind, archive window) that Slack has no
36
+ * equivalent for.
37
+ */
38
+ type: 'channel' | 'guild' | 'role' | 'member' | 'string' | 'text' | 'number' | 'boolean' | 'emoji' | 'select';
39
+ /** Required at submit time. */
40
+ required?: boolean;
41
+ /** Human description — also used as the AI tool param description. */
42
+ description: string;
43
+ /** Hint shown inside the input. */
44
+ placeholder?: string;
45
+ /** Value to show when the stored one is undefined. */
46
+ default?: string | number | boolean;
47
+ /** `select` only — the fixed option list, in display order. */
48
+ options?: Array<{
49
+ value: string | number;
50
+ label: string;
51
+ }>;
52
+ /** `number` only. */
53
+ min?: number;
54
+ max?: number;
55
+ /** `text` only — height of the textarea. */
56
+ rows?: number;
57
+ /** `channel` only — let categories be picked, for parent-category fields. */
58
+ allowCategories?: boolean;
59
+ /** `boolean` only — the bold line beside the switch. */
60
+ switchLabel?: string;
61
+ }
62
+ export interface DiscordOperationSpec {
63
+ /** UI label for the operation picker. */
64
+ label: string;
65
+ /** Description shown in the picker + reused by the AI tool description
66
+ * when the user hasn't set a custom one on the entity. */
67
+ description: string;
68
+ /** Route the operation maps to in Discord's docs (discord.com/developers). */
69
+ apiMethod: string;
70
+ /** Parameter schema. Order matters for form rendering. */
71
+ params: DiscordParamSpec[];
72
+ }
73
+ export declare const DISCORD_OPERATION_SPECS: Record<DiscordOperation, DiscordOperationSpec>;
@@ -1,13 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DISCORD_OPERATIONS = void 0;
3
+ exports.DISCORD_OPERATION_SPECS = exports.DISCORD_OPERATIONS = void 0;
4
4
  exports.isDiscordOperation = isDiscordOperation;
5
5
  /**
6
6
  * Discord Bot API operation enum — single source of truth across the
7
7
  * API, Dashboard, and any future consumers (Message Broker, MCP server).
8
8
  * Used by:
9
9
  * - discordAction entity / DTO (api): operation field + validation
10
- * - dashboard DiscordAction operation picker
10
+ * - dashboard DiscordAction operation picker + form generation
11
11
  * - MCP toolkit-specs expansion (one aiEnabled discordAction → N tools)
12
12
  *
13
13
  * Targets Discord API v10 (the current stable as of 2026). Each op maps
@@ -50,3 +50,311 @@ function isDiscordOperation(value) {
50
50
  return (typeof value === 'string' &&
51
51
  exports.DISCORD_OPERATIONS.includes(value));
52
52
  }
53
+ /* Fields several operations share. Each takes overrides because the same key
54
+ can want different wording per operation: messageId is a required "Message
55
+ ID" in six ops and an optional anchor in createThread. */
56
+ const channelId = (over = {}) => ({
57
+ name: 'channelId',
58
+ label: 'Channel ID',
59
+ type: 'channel',
60
+ required: true,
61
+ placeholder: '{{payload.channelId}}',
62
+ description: 'Snowflake of the channel. The picker auto-discovers the channels the bot can see; a template such as {{payload.channelId}} works too.',
63
+ ...over,
64
+ });
65
+ const guildId = (over = {}) => ({
66
+ name: 'guildId',
67
+ label: 'Guild ID',
68
+ type: 'guild',
69
+ required: true,
70
+ description: 'Snowflake of the server. The picker auto-discovers the servers the bot belongs to; a template such as {{payload.guildId}} works too.',
71
+ ...over,
72
+ });
73
+ const messageId = (over = {}) => ({
74
+ name: 'messageId',
75
+ label: 'Message ID',
76
+ type: 'string',
77
+ required: true,
78
+ placeholder: '{{payload.messageId}}',
79
+ description: 'Snowflake of the message.',
80
+ ...over,
81
+ });
82
+ const userId = (over = {}) => ({
83
+ name: 'userId',
84
+ label: 'User ID',
85
+ type: 'member',
86
+ required: true,
87
+ description: 'Snowflake of the user. Listing members needs the Server Members Intent enabled in Developer Portal.',
88
+ ...over,
89
+ });
90
+ const roleId = (over = {}) => ({
91
+ name: 'roleId',
92
+ label: 'Role ID',
93
+ type: 'role',
94
+ required: true,
95
+ description: 'Snowflake of the role. The role of the bot itself must sit above the target role in the hierarchy for grant and remove to work.',
96
+ ...over,
97
+ });
98
+ const emoji = () => ({
99
+ name: 'emoji',
100
+ label: 'Emoji',
101
+ type: 'emoji',
102
+ required: true,
103
+ placeholder: 'name:id',
104
+ description: 'Unicode emoji, or a custom server emoji written as name:id. URL-encoding is handled.',
105
+ });
106
+ const content = () => ({
107
+ name: 'content',
108
+ label: 'Message content',
109
+ type: 'text',
110
+ required: true,
111
+ rows: 4,
112
+ placeholder: 'Hello! Anything I can help with?',
113
+ description: 'Message text. Markdown is supported, up to the 2000 characters Discord allows.',
114
+ });
115
+ const topic = (label) => ({
116
+ name: 'topic',
117
+ label,
118
+ type: 'text',
119
+ rows: 2,
120
+ placeholder: label.startsWith('New') ? 'New description' : 'Channel description',
121
+ description: 'Channel description, shown under its name in Discord.',
122
+ });
123
+ const slowmode = (label) => ({
124
+ name: 'slowmode',
125
+ label,
126
+ type: 'number',
127
+ min: 0,
128
+ max: 21600,
129
+ default: 0,
130
+ description: 'Seconds a member has to wait between messages. 0 turns slowmode off, 21600 is the six-hour maximum.',
131
+ });
132
+ const parentId = (label) => ({
133
+ name: 'parentId',
134
+ label,
135
+ type: 'channel',
136
+ allowCategories: true,
137
+ placeholder: 'Snowflake of a category',
138
+ description: 'Category to nest the channel under. Categories show in the picker with a folder icon.',
139
+ });
140
+ exports.DISCORD_OPERATION_SPECS = {
141
+ // ── Messages ──────────────────────────────────────────────────────
142
+ sendMessage: {
143
+ label: 'Send message',
144
+ description: 'Post text/embed to a channel or thread. Optional reply-to + threadId.',
145
+ apiMethod: 'POST /channels/{channel.id}/messages',
146
+ params: [
147
+ channelId(),
148
+ content(),
149
+ {
150
+ name: 'replyToMessageId',
151
+ label: 'Reply to message ID (optional)',
152
+ type: 'string',
153
+ placeholder: '{{payload.messageId}}',
154
+ description: 'Snowflake of a message to quote-reply to.',
155
+ },
156
+ {
157
+ name: 'threadId',
158
+ label: 'Thread ID (optional)',
159
+ type: 'string',
160
+ placeholder: '{{payload.threadId}}',
161
+ description: 'Thread snowflake. When set, the message is posted into that thread instead of the parent channel.',
162
+ },
163
+ {
164
+ name: 'interactionReply',
165
+ label: 'Reply inline to slash command',
166
+ type: 'boolean',
167
+ /* Absent means on: an action downstream of a slash command should
168
+ answer the command by default, so only an explicit false opts out.
169
+ Renderers must compare against false rather than coerce. */
170
+ default: true,
171
+ switchLabel: 'Auto-reply to slash commands (recommended)',
172
+ description: 'When the run came from a Discord slash command, replace the "Bot is thinking" spinner with this message instead of posting a separate one. Falls back to a normal channel post otherwise.',
173
+ },
174
+ ],
175
+ },
176
+ editMessage: {
177
+ label: 'Edit message',
178
+ description: 'Edit content/embeds of a message the bot owns.',
179
+ apiMethod: 'PATCH /channels/{channel.id}/messages/{message.id}',
180
+ params: [channelId(), messageId(), content()],
181
+ },
182
+ deleteMessage: {
183
+ label: 'Delete message',
184
+ description: 'Delete by id. Messages from other users need Manage Messages.',
185
+ apiMethod: 'DELETE /channels/{channel.id}/messages/{message.id}',
186
+ params: [channelId(), messageId()],
187
+ },
188
+ getMessage: {
189
+ label: 'Get message',
190
+ description: 'Fetch a single message plus its reactions and attachments.',
191
+ apiMethod: 'GET /channels/{channel.id}/messages/{message.id}',
192
+ params: [channelId(), messageId()],
193
+ },
194
+ addReaction: {
195
+ label: 'Add reaction',
196
+ description: 'React as the bot. Custom emoji format: name:id.',
197
+ apiMethod: 'PUT /channels/{channel.id}/messages/{message.id}/reactions/{emoji}/@me',
198
+ params: [channelId(), messageId(), emoji()],
199
+ },
200
+ removeReaction: {
201
+ label: 'Remove reaction',
202
+ description: 'Remove the reaction the bot itself added.',
203
+ apiMethod: 'DELETE /channels/{channel.id}/messages/{message.id}/reactions/{emoji}/@me',
204
+ params: [channelId(), messageId(), emoji()],
205
+ },
206
+ pinMessage: {
207
+ label: 'Pin message',
208
+ description: 'Pin a message in its channel. Needs Manage Messages.',
209
+ apiMethod: 'PUT /channels/{channel.id}/pins/{message.id}',
210
+ params: [channelId(), messageId()],
211
+ },
212
+ // ── Channels ──────────────────────────────────────────────────────
213
+ createChannel: {
214
+ label: 'Create channel',
215
+ description: 'Create text/voice/forum/announcement/category in a guild.',
216
+ apiMethod: 'POST /guilds/{guild.id}/channels',
217
+ params: [
218
+ guildId(),
219
+ {
220
+ name: 'name',
221
+ label: 'Name',
222
+ type: 'string',
223
+ required: true,
224
+ placeholder: 'ticket-1234',
225
+ description: 'Name for the new channel.',
226
+ },
227
+ {
228
+ name: 'type',
229
+ label: 'Channel type',
230
+ type: 'select',
231
+ default: 'text',
232
+ description: 'Kind of channel to create.',
233
+ options: [
234
+ { value: 'text', label: 'Text' },
235
+ { value: 'voice', label: 'Voice' },
236
+ { value: 'forum', label: 'Forum' },
237
+ { value: 'announcement', label: 'Announcement' },
238
+ { value: 'category', label: 'Category' },
239
+ ],
240
+ },
241
+ parentId('Parent category (optional)'),
242
+ topic('Topic (optional)'),
243
+ slowmode('Slowmode seconds (optional)'),
244
+ ],
245
+ },
246
+ editChannel: {
247
+ label: 'Edit channel',
248
+ description: 'Rename, change topic, slowmode, parent category.',
249
+ apiMethod: 'PATCH /channels/{channel.id}',
250
+ params: [
251
+ channelId(),
252
+ {
253
+ name: 'name',
254
+ label: 'New name (optional)',
255
+ type: 'string',
256
+ placeholder: 'renamed-channel',
257
+ description: 'Replacement channel name.',
258
+ },
259
+ topic('New topic (optional)'),
260
+ slowmode('New slowmode seconds (optional)'),
261
+ parentId('New parent category (optional)'),
262
+ ],
263
+ },
264
+ deleteChannel: {
265
+ label: 'Delete channel',
266
+ description: 'Permanently deletes a channel — irreversible.',
267
+ apiMethod: 'DELETE /channels/{channel.id}',
268
+ params: [channelId()],
269
+ },
270
+ getChannel: {
271
+ label: 'Get channel',
272
+ description: 'Fetch the full config and permissions of a channel.',
273
+ apiMethod: 'GET /channels/{channel.id}',
274
+ params: [channelId()],
275
+ },
276
+ listChannels: {
277
+ label: 'List channels',
278
+ description: 'Iterate every channel in a guild — output is iterable.',
279
+ apiMethod: 'GET /guilds/{guild.id}/channels',
280
+ params: [guildId()],
281
+ },
282
+ // ── Threads ───────────────────────────────────────────────────────
283
+ createThread: {
284
+ label: 'Create thread',
285
+ description: 'Off a message OR standalone (public/private/announcement).',
286
+ apiMethod: 'POST /channels/{channel.id}/threads',
287
+ params: [
288
+ channelId(),
289
+ {
290
+ name: 'name',
291
+ label: 'Name',
292
+ type: 'string',
293
+ required: true,
294
+ placeholder: 'Conversation about issue',
295
+ description: 'Name for the new thread.',
296
+ },
297
+ messageId({
298
+ label: 'Anchor message ID (optional)',
299
+ required: false,
300
+ description: 'Snowflake of a message to spawn the thread from. Leave blank for a standalone thread.',
301
+ }),
302
+ {
303
+ name: 'type',
304
+ label: 'Thread type',
305
+ type: 'select',
306
+ default: 'public',
307
+ description: 'Thread visibility. Ignored when an anchor message is set, since the thread then inherits from its parent.',
308
+ options: [
309
+ { value: 'public', label: 'Public' },
310
+ { value: 'private', label: 'Private' },
311
+ { value: 'announcement', label: 'Announcement' },
312
+ ],
313
+ },
314
+ {
315
+ name: 'autoArchiveMinutes',
316
+ label: 'Auto-archive minutes',
317
+ type: 'select',
318
+ default: 1440,
319
+ description: 'Idle time before Discord archives the thread.',
320
+ options: [
321
+ { value: 60, label: '1 hour' },
322
+ { value: 1440, label: '24 hours (default)' },
323
+ { value: 4320, label: '3 days' },
324
+ { value: 10080, label: '1 week' },
325
+ ],
326
+ },
327
+ ],
328
+ },
329
+ // ── DMs ───────────────────────────────────────────────────────────
330
+ sendDM: {
331
+ label: 'Send DM',
332
+ description: 'Direct message a user by id. Silently dropped if DMs disabled.',
333
+ apiMethod: 'POST /users/@me/channels then POST /channels/{channel.id}/messages',
334
+ params: [
335
+ userId({
336
+ description: 'Snowflake of the user to DM. The bot needs a server in common with them.',
337
+ }),
338
+ content(),
339
+ ],
340
+ },
341
+ // ── Members & roles ───────────────────────────────────────────────
342
+ addRole: {
343
+ label: 'Add role to member',
344
+ description: 'The role of the bot must be ABOVE the target role in the hierarchy.',
345
+ apiMethod: 'PUT /guilds/{guild.id}/members/{user.id}/roles/{role.id}',
346
+ params: [guildId(), userId(), roleId()],
347
+ },
348
+ removeRole: {
349
+ label: 'Remove role from member',
350
+ description: 'Same hierarchy rule applies.',
351
+ apiMethod: 'DELETE /guilds/{guild.id}/members/{user.id}/roles/{role.id}',
352
+ params: [guildId(), userId(), roleId()],
353
+ },
354
+ getMember: {
355
+ label: 'Get member',
356
+ description: 'Fetch the nick, roles, joined_at and user object of a member.',
357
+ apiMethod: 'GET /guilds/{guild.id}/members/{user.id}',
358
+ params: [guildId(), userId()],
359
+ },
360
+ };
package/dist/index.d.ts CHANGED
@@ -15,8 +15,8 @@ export { TELEGRAM_OPERATIONS, isTelegramOperation, } from './telegram-operations
15
15
  export type { TelegramOperation } from './telegram-operations';
16
16
  export { WHATSAPP_OPERATIONS, isWhatsAppOperation, } from './whatsapp-operations';
17
17
  export type { WhatsAppOperation } from './whatsapp-operations';
18
- export { DISCORD_OPERATIONS, isDiscordOperation, } from './discord-operations';
19
- export type { DiscordOperation } from './discord-operations';
18
+ export { DISCORD_OPERATIONS, DISCORD_OPERATION_SPECS, isDiscordOperation, } from './discord-operations';
19
+ export type { DiscordOperation, DiscordParamSpec, DiscordOperationSpec, } from './discord-operations';
20
20
  export { SLACK_OPERATIONS, SLACK_OPERATION_SPECS, isSlackOperation, } from './slack-operations';
21
21
  export type { SlackOperation, SlackParamSpec, SlackOperationSpec, } from './slack-operations';
22
22
  export { GOOGLE_CONTACTS_OPERATIONS, GOOGLE_CONTACTS_OPERATIONS_V1, GOOGLE_CONTACTS_OPERATIONS_V2, isGoogleContactsOperation, } from './google-contacts-operations';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.isGoogleContactsOperation = exports.GOOGLE_CONTACTS_OPERATIONS_V2 = exports.GOOGLE_CONTACTS_OPERATIONS_V1 = exports.GOOGLE_CONTACTS_OPERATIONS = exports.isSlackOperation = exports.SLACK_OPERATION_SPECS = exports.SLACK_OPERATIONS = exports.isDiscordOperation = exports.DISCORD_OPERATIONS = exports.isWhatsAppOperation = exports.WHATSAPP_OPERATIONS = exports.isTelegramOperation = exports.TELEGRAM_OPERATIONS = exports.isDriveOperation = exports.DRIVE_OPERATIONS = exports.isGoogleCalendarOperation = exports.GOOGLE_CALENDAR_OPERATIONS = exports.isGmailOperation = exports.GMAIL_OPERATIONS = exports.NODE_TYPE_TO_PREFIX = exports.PREFIX_TO_NODE_TYPE = exports.NODE_STATE_KEYS = exports.NODE_COLORS = exports.NODE_DETAIL_PATHS = exports.getNodeRegistryEntry = exports.NODE_REGISTRY = exports.getNodeDispatchConfig = exports.getAllNodeCollections = exports.NODE_DISPATCH = exports.resolveNodeId = exports.PREFIX_TO_TYPE = exports.NODE_UI = exports.ALL_NODE_TYPES = exports.isNodeType = exports.isTerminal = exports.canSendToNodes = exports.canReceiveFromNodes = exports.canReceiveFrom = exports.NODE_CONNECTIONS = exports.iterableMeta = exports.singleMeta = void 0;
3
+ exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.isGoogleContactsOperation = exports.GOOGLE_CONTACTS_OPERATIONS_V2 = exports.GOOGLE_CONTACTS_OPERATIONS_V1 = exports.GOOGLE_CONTACTS_OPERATIONS = exports.isSlackOperation = exports.SLACK_OPERATION_SPECS = exports.SLACK_OPERATIONS = exports.isDiscordOperation = exports.DISCORD_OPERATION_SPECS = exports.DISCORD_OPERATIONS = exports.isWhatsAppOperation = exports.WHATSAPP_OPERATIONS = exports.isTelegramOperation = exports.TELEGRAM_OPERATIONS = exports.isDriveOperation = exports.DRIVE_OPERATIONS = exports.isGoogleCalendarOperation = exports.GOOGLE_CALENDAR_OPERATIONS = exports.isGmailOperation = exports.GMAIL_OPERATIONS = exports.NODE_TYPE_TO_PREFIX = exports.PREFIX_TO_NODE_TYPE = exports.NODE_STATE_KEYS = exports.NODE_COLORS = exports.NODE_DETAIL_PATHS = exports.getNodeRegistryEntry = exports.NODE_REGISTRY = exports.getNodeDispatchConfig = exports.getAllNodeCollections = exports.NODE_DISPATCH = exports.resolveNodeId = exports.PREFIX_TO_TYPE = exports.NODE_UI = exports.ALL_NODE_TYPES = exports.isNodeType = exports.isTerminal = exports.canSendToNodes = exports.canReceiveFromNodes = exports.canReceiveFrom = exports.NODE_CONNECTIONS = exports.iterableMeta = exports.singleMeta = void 0;
4
4
  var types_1 = require("./types");
5
5
  Object.defineProperty(exports, "singleMeta", { enumerable: true, get: function () { return types_1.singleMeta; } });
6
6
  Object.defineProperty(exports, "iterableMeta", { enumerable: true, get: function () { return types_1.iterableMeta; } });
@@ -49,6 +49,7 @@ Object.defineProperty(exports, "WHATSAPP_OPERATIONS", { enumerable: true, get: f
49
49
  Object.defineProperty(exports, "isWhatsAppOperation", { enumerable: true, get: function () { return whatsapp_operations_1.isWhatsAppOperation; } });
50
50
  var discord_operations_1 = require("./discord-operations");
51
51
  Object.defineProperty(exports, "DISCORD_OPERATIONS", { enumerable: true, get: function () { return discord_operations_1.DISCORD_OPERATIONS; } });
52
+ Object.defineProperty(exports, "DISCORD_OPERATION_SPECS", { enumerable: true, get: function () { return discord_operations_1.DISCORD_OPERATION_SPECS; } });
52
53
  Object.defineProperty(exports, "isDiscordOperation", { enumerable: true, get: function () { return discord_operations_1.isDiscordOperation; } });
53
54
  var slack_operations_1 = require("./slack-operations");
54
55
  Object.defineProperty(exports, "SLACK_OPERATIONS", { enumerable: true, get: function () { return slack_operations_1.SLACK_OPERATIONS; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostwebhook/node-types",
3
- "version": "1.52.4",
3
+ "version": "1.52.6",
4
4
  "description": "Shared node type definitions, connection rules, and dispatch config for HostWebhook",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",