@hostwebhook/node-types 1.52.11 → 1.52.13
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/drive-operations.d.ts +81 -0
- package/dist/drive-operations.js +284 -1
- package/dist/drive-toolkit.d.ts +54 -0
- package/dist/drive-toolkit.js +176 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +11 -2
- package/dist/postgres-operations.d.ts +60 -0
- package/dist/postgres-operations.js +79 -0
- package/package.json +1 -1
|
@@ -20,3 +20,84 @@ export declare const DRIVE_OPERATIONS: readonly ["upload", "download", "copy", "
|
|
|
20
20
|
export type DriveOperation = (typeof DRIVE_OPERATIONS)[number];
|
|
21
21
|
/** Type guard — useful when validating untrusted input (DTOs, tool calls). */
|
|
22
22
|
export declare function isDriveOperation(value: unknown): value is DriveOperation;
|
|
23
|
+
export type DriveParamType =
|
|
24
|
+
/** Live file picker (needs a credential) + templated paste. */
|
|
25
|
+
'driveFile'
|
|
26
|
+
/** Text input with `{{payload.*}}` autocomplete. */
|
|
27
|
+
| 'template'
|
|
28
|
+
/** Plain `<select>`; the labels are not the values, so `options` is required. */
|
|
29
|
+
| 'select'
|
|
30
|
+
/**
|
|
31
|
+
* Searchable mime-type dropdown. The option list is long and shared with
|
|
32
|
+
* other screens, so it stays in the dashboard (`lib/drive-mime-options`)
|
|
33
|
+
* and the schema only says "this field is a mime picker".
|
|
34
|
+
*/
|
|
35
|
+
| 'mimeSelect' | 'number'
|
|
36
|
+
/**
|
|
37
|
+
* A `<select>` whose values are `''` / `'true'` / `'false'` but which is
|
|
38
|
+
* stored as a BOOLEAN, or omitted entirely when `''`.
|
|
39
|
+
*
|
|
40
|
+
* This is the shape that keeps `starred` honest: the api applies it with
|
|
41
|
+
* `typeof config.starred === 'boolean'`, so "— don't change —" has to mean
|
|
42
|
+
* *no key at all*. A plain Switch cannot express that third state, and
|
|
43
|
+
* storing `''` or `'false'` would silently stop the star from ever changing.
|
|
44
|
+
*/
|
|
45
|
+
| 'booleanSelect'
|
|
46
|
+
/**
|
|
47
|
+
* Full-width explainer card that toggles: icon, title, prose and bullets,
|
|
48
|
+
* with the switch on the right. The card's rich body is JSX, so it lives in
|
|
49
|
+
* the page keyed by param name — the schema only declares the field.
|
|
50
|
+
*/
|
|
51
|
+
| 'switchCard'
|
|
52
|
+
/** Live multi-select of the starting folder's immediate children. */
|
|
53
|
+
| 'driveSubfolders';
|
|
54
|
+
export interface DriveParamSpec {
|
|
55
|
+
/** The `operationConfig` key. This is what the api reads — not a UI name. */
|
|
56
|
+
name: string;
|
|
57
|
+
label: string;
|
|
58
|
+
type: DriveParamType;
|
|
59
|
+
required?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Help text under the field. Plain text on purpose: the package holds no
|
|
62
|
+
* React, so the `<code>` spans the page used to have are gone. Deliberate.
|
|
63
|
+
*/
|
|
64
|
+
description?: string;
|
|
65
|
+
placeholder?: string;
|
|
66
|
+
/** Prefilled when the saved config has no value for this key. */
|
|
67
|
+
default?: string | number;
|
|
68
|
+
/** Required for `select` and `booleanSelect` — label and value differ. */
|
|
69
|
+
options?: ReadonlyArray<{
|
|
70
|
+
value: string;
|
|
71
|
+
label: string;
|
|
72
|
+
}>;
|
|
73
|
+
min?: number;
|
|
74
|
+
max?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Renders in the "Advanced" tab instead of "Config". The page used to keep
|
|
77
|
+
* a two-name `ADVANCED_FIELDS` array while the advanced *tab* actually held
|
|
78
|
+
* four fields, so `recursive` and `subfolderIds` only showed up because
|
|
79
|
+
* `list` happened to also carry one of the two named fields. Marking all
|
|
80
|
+
* four here removes that coincidence.
|
|
81
|
+
*/
|
|
82
|
+
advanced?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Only shown when a sibling field holds one of these values. No other node's
|
|
85
|
+
* schema needed this yet; Drive has three cases (the two share targets and
|
|
86
|
+
* the subfolder scope).
|
|
87
|
+
*/
|
|
88
|
+
showWhen?: {
|
|
89
|
+
field: string;
|
|
90
|
+
in: ReadonlyArray<string>;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export interface DriveOperationSpec {
|
|
94
|
+
/** UI label for the operation dropdown row. */
|
|
95
|
+
label: string;
|
|
96
|
+
/** Sub-line under the label in the dropdown. */
|
|
97
|
+
description: string;
|
|
98
|
+
/** Drive API method it maps to (developers.google.com/drive/api/reference). */
|
|
99
|
+
apiMethod: string;
|
|
100
|
+
/** Parameter schema. Order matters: it is the on-screen order. */
|
|
101
|
+
params: DriveParamSpec[];
|
|
102
|
+
}
|
|
103
|
+
export declare const DRIVE_OPERATION_SPECS: Record<DriveOperation, DriveOperationSpec>;
|
package/dist/drive-operations.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DRIVE_OPERATIONS = void 0;
|
|
3
|
+
exports.DRIVE_OPERATION_SPECS = exports.DRIVE_OPERATIONS = void 0;
|
|
4
4
|
exports.isDriveOperation = isDriveOperation;
|
|
5
5
|
/**
|
|
6
6
|
* Google Drive operation enum — single source of truth across the API,
|
|
@@ -38,3 +38,286 @@ exports.DRIVE_OPERATIONS = [
|
|
|
38
38
|
function isDriveOperation(value) {
|
|
39
39
|
return typeof value === 'string' && exports.DRIVE_OPERATIONS.includes(value);
|
|
40
40
|
}
|
|
41
|
+
/* Fábricas de los campos que se repiten, para que un cambio de copy no haya
|
|
42
|
+
que perseguirlo por doce operaciones. */
|
|
43
|
+
const fileId = () => ({
|
|
44
|
+
name: 'fileId',
|
|
45
|
+
label: 'Drive File',
|
|
46
|
+
type: 'driveFile',
|
|
47
|
+
required: true,
|
|
48
|
+
description: 'Pick a file from Drive or paste a templated value like {{payload.driveFileId}}.',
|
|
49
|
+
});
|
|
50
|
+
const parentFolderId = () => ({
|
|
51
|
+
name: 'parentFolderId',
|
|
52
|
+
label: 'Parent Folder ID (override)',
|
|
53
|
+
type: 'template',
|
|
54
|
+
advanced: true,
|
|
55
|
+
placeholder: 'Empty = use Default Folder above',
|
|
56
|
+
description: "Folder id used for this operation only. Leave empty to use the action's Default Folder. Templates work — e.g. {{payload.folderId}}.",
|
|
57
|
+
});
|
|
58
|
+
/** El filtro por tipo de upload/list. En `export` el mismo `mimeType` es otra
|
|
59
|
+
cosa y se declara aparte. */
|
|
60
|
+
const mimeTypeFilter = () => ({
|
|
61
|
+
name: 'mimeType',
|
|
62
|
+
label: 'File type filter (optional)',
|
|
63
|
+
type: 'mimeSelect',
|
|
64
|
+
description: 'Restrict the operation to files of this type. Leave as "Any" to match every file.',
|
|
65
|
+
});
|
|
66
|
+
const ORDER_BY_OPTIONS = [
|
|
67
|
+
{ value: 'modifiedTime desc', label: 'Modified time (newest first)' },
|
|
68
|
+
{ value: 'modifiedTime', label: 'Modified time (oldest first)' },
|
|
69
|
+
{ value: 'createdTime desc', label: 'Created time (newest first)' },
|
|
70
|
+
{ value: 'createdTime', label: 'Created time (oldest first)' },
|
|
71
|
+
{ value: 'name', label: 'Name (A→Z)' },
|
|
72
|
+
{ value: 'name desc', label: 'Name (Z→A)' },
|
|
73
|
+
];
|
|
74
|
+
const EXPORT_FORMAT_OPTIONS = [
|
|
75
|
+
{ value: 'application/pdf', label: 'PDF (.pdf)' },
|
|
76
|
+
{
|
|
77
|
+
value: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
78
|
+
label: 'Word (.docx) — for Google Docs',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
value: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
82
|
+
label: 'Excel (.xlsx) — for Google Sheets',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
value: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
86
|
+
label: 'PowerPoint (.pptx) — for Google Slides',
|
|
87
|
+
},
|
|
88
|
+
{ value: 'text/plain', label: 'Plain text (.txt)' },
|
|
89
|
+
{ value: 'text/csv', label: 'CSV (.csv) — for Google Sheets' },
|
|
90
|
+
{ value: 'text/html', label: 'HTML (.html)' },
|
|
91
|
+
];
|
|
92
|
+
exports.DRIVE_OPERATION_SPECS = {
|
|
93
|
+
upload: {
|
|
94
|
+
label: 'Upload File',
|
|
95
|
+
description: 'Stream a file from a payload _file ref into Drive',
|
|
96
|
+
apiMethod: 'files.create',
|
|
97
|
+
params: [
|
|
98
|
+
{
|
|
99
|
+
name: 'fileName',
|
|
100
|
+
label: 'Destination File Name',
|
|
101
|
+
type: 'template',
|
|
102
|
+
placeholder: '{{payload.name}} — leave blank to use _file.originalName',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'sourceField',
|
|
106
|
+
label: 'Source Field (payload path)',
|
|
107
|
+
type: 'template',
|
|
108
|
+
placeholder: '_file',
|
|
109
|
+
default: '_file',
|
|
110
|
+
description: 'Where the _file ref lives in the incoming payload. Default _file. For nested refs use a dotted path (e.g. doc if upstream is { doc: { _file: ... } }).',
|
|
111
|
+
},
|
|
112
|
+
mimeTypeFilter(),
|
|
113
|
+
parentFolderId(),
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
download: {
|
|
117
|
+
label: 'Download File',
|
|
118
|
+
description: 'Fetch a Drive file → payload _file ref for downstream nodes',
|
|
119
|
+
apiMethod: 'files.get (alt=media)',
|
|
120
|
+
params: [fileId()],
|
|
121
|
+
},
|
|
122
|
+
list: {
|
|
123
|
+
label: 'List / Search',
|
|
124
|
+
description: 'List or search files (iterable output)',
|
|
125
|
+
apiMethod: 'files.list',
|
|
126
|
+
params: [
|
|
127
|
+
{
|
|
128
|
+
name: 'query',
|
|
129
|
+
label: 'Search Query',
|
|
130
|
+
type: 'template',
|
|
131
|
+
placeholder: 'Substring to match against file names — empty = list all',
|
|
132
|
+
},
|
|
133
|
+
mimeTypeFilter(),
|
|
134
|
+
{
|
|
135
|
+
name: 'pageSize',
|
|
136
|
+
label: 'Max Results',
|
|
137
|
+
type: 'number',
|
|
138
|
+
min: 1,
|
|
139
|
+
max: 100,
|
|
140
|
+
default: 50,
|
|
141
|
+
description: 'Up to 100 per call. Output is iterable — downstream nodes process each file independently.',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: 'orderBy',
|
|
145
|
+
label: 'Order By',
|
|
146
|
+
type: 'select',
|
|
147
|
+
default: 'modifiedTime desc',
|
|
148
|
+
options: ORDER_BY_OPTIONS,
|
|
149
|
+
},
|
|
150
|
+
parentFolderId(),
|
|
151
|
+
{ name: 'recursive', label: 'Search subfolders too', type: 'switchCard', advanced: true },
|
|
152
|
+
{
|
|
153
|
+
name: 'subfolderIds',
|
|
154
|
+
label: 'Limit to specific subfolders',
|
|
155
|
+
type: 'driveSubfolders',
|
|
156
|
+
advanced: true,
|
|
157
|
+
showWhen: { field: 'recursive', in: ['true'] },
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'downloadResults',
|
|
161
|
+
label: 'Auto-download matching files',
|
|
162
|
+
type: 'switchCard',
|
|
163
|
+
advanced: true,
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
get: {
|
|
168
|
+
label: 'Get Metadata',
|
|
169
|
+
description: 'Read metadata for one file (no bytes)',
|
|
170
|
+
apiMethod: 'files.get',
|
|
171
|
+
params: [fileId()],
|
|
172
|
+
},
|
|
173
|
+
copy: {
|
|
174
|
+
label: 'Copy File',
|
|
175
|
+
description: 'Duplicate a file, optionally rename and/or move',
|
|
176
|
+
apiMethod: 'files.copy',
|
|
177
|
+
params: [
|
|
178
|
+
fileId(),
|
|
179
|
+
{
|
|
180
|
+
name: 'newName',
|
|
181
|
+
label: 'New File Name',
|
|
182
|
+
type: 'template',
|
|
183
|
+
placeholder: "Optional — defaults to 'Copy of <original>'",
|
|
184
|
+
},
|
|
185
|
+
parentFolderId(),
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
move: {
|
|
189
|
+
label: 'Move File',
|
|
190
|
+
description: 'Move a file to a different folder',
|
|
191
|
+
apiMethod: 'files.update (addParents / removeParents)',
|
|
192
|
+
params: [fileId(), parentFolderId()],
|
|
193
|
+
},
|
|
194
|
+
delete: {
|
|
195
|
+
label: 'Delete (Trash)',
|
|
196
|
+
description: 'Move to trash — recoverable for 30 days',
|
|
197
|
+
apiMethod: 'files.update (trashed) / files.delete when permanent',
|
|
198
|
+
params: [
|
|
199
|
+
fileId(),
|
|
200
|
+
{
|
|
201
|
+
name: 'permanent',
|
|
202
|
+
label: 'Delete Permanently?',
|
|
203
|
+
type: 'booleanSelect',
|
|
204
|
+
options: [
|
|
205
|
+
{ value: '', label: 'No — move to trash (recoverable)' },
|
|
206
|
+
{ value: 'true', label: '⚠️ Yes — erase forever (irreversible)' },
|
|
207
|
+
],
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
share: {
|
|
212
|
+
label: 'Share File',
|
|
213
|
+
description: 'Grant viewer / commenter / editor permission',
|
|
214
|
+
apiMethod: 'permissions.create',
|
|
215
|
+
params: [
|
|
216
|
+
fileId(),
|
|
217
|
+
{
|
|
218
|
+
name: 'type',
|
|
219
|
+
label: 'Share With',
|
|
220
|
+
type: 'select',
|
|
221
|
+
default: 'user',
|
|
222
|
+
options: [
|
|
223
|
+
{ value: 'user', label: 'Specific user (email)' },
|
|
224
|
+
{ value: 'group', label: 'Group (email)' },
|
|
225
|
+
{ value: 'domain', label: 'Entire domain' },
|
|
226
|
+
{ value: 'anyone', label: '⚠️ Anyone with the link (public)' },
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
name: 'role',
|
|
231
|
+
label: 'Permission Role',
|
|
232
|
+
type: 'select',
|
|
233
|
+
default: 'reader',
|
|
234
|
+
options: [
|
|
235
|
+
{ value: 'reader', label: 'Viewer (read-only)' },
|
|
236
|
+
{ value: 'commenter', label: 'Commenter' },
|
|
237
|
+
{ value: 'writer', label: 'Editor' },
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: 'emailAddress',
|
|
242
|
+
label: 'Email Address',
|
|
243
|
+
type: 'template',
|
|
244
|
+
placeholder: 'user@example.com or {{payload.email}}',
|
|
245
|
+
showWhen: { field: 'type', in: ['user', 'group'] },
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: 'domain',
|
|
249
|
+
label: 'Domain',
|
|
250
|
+
type: 'template',
|
|
251
|
+
placeholder: 'example.com',
|
|
252
|
+
showWhen: { field: 'type', in: ['domain'] },
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: 'sendNotificationEmail',
|
|
256
|
+
label: 'Notify Recipient',
|
|
257
|
+
type: 'booleanSelect',
|
|
258
|
+
options: [
|
|
259
|
+
{ value: '', label: "No — don't send email" },
|
|
260
|
+
{ value: 'true', label: 'Yes — send share notification email' },
|
|
261
|
+
],
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
},
|
|
265
|
+
update: {
|
|
266
|
+
label: 'Update Metadata',
|
|
267
|
+
description: 'Rename, set description, or star/unstar',
|
|
268
|
+
apiMethod: 'files.update',
|
|
269
|
+
params: [
|
|
270
|
+
fileId(),
|
|
271
|
+
{ name: 'name', label: 'New Name', type: 'template', placeholder: '{{payload.title}}' },
|
|
272
|
+
{
|
|
273
|
+
name: 'description',
|
|
274
|
+
label: 'Description (optional)',
|
|
275
|
+
type: 'template',
|
|
276
|
+
placeholder: '{{payload.description}}',
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: 'starred',
|
|
280
|
+
label: 'Starred (optional)',
|
|
281
|
+
type: 'booleanSelect',
|
|
282
|
+
options: [
|
|
283
|
+
{ value: '', label: "— don't change —" },
|
|
284
|
+
{ value: 'true', label: '⭐ Star' },
|
|
285
|
+
{ value: 'false', label: 'Un-star' },
|
|
286
|
+
],
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
},
|
|
290
|
+
createFolder: {
|
|
291
|
+
label: 'Create Folder',
|
|
292
|
+
description: 'Create a new folder, optionally inside a parent',
|
|
293
|
+
apiMethod: 'files.create (folder mimeType)',
|
|
294
|
+
params: [
|
|
295
|
+
/* Mismo `name` que en update, con otra etiqueta y otro placeholder: por
|
|
296
|
+
eso los params son por operación y no un registro global de campos. */
|
|
297
|
+
{ name: 'name', label: 'Folder Name', type: 'template', placeholder: 'Reports' },
|
|
298
|
+
parentFolderId(),
|
|
299
|
+
],
|
|
300
|
+
},
|
|
301
|
+
export: {
|
|
302
|
+
label: 'Export Google Doc',
|
|
303
|
+
description: 'Export a Doc / Sheet / Slide to PDF / DOCX / XLSX',
|
|
304
|
+
apiMethod: 'files.export',
|
|
305
|
+
params: [
|
|
306
|
+
fileId(),
|
|
307
|
+
{
|
|
308
|
+
name: 'mimeType',
|
|
309
|
+
label: 'Export Format',
|
|
310
|
+
type: 'select',
|
|
311
|
+
default: 'application/pdf',
|
|
312
|
+
options: EXPORT_FORMAT_OPTIONS,
|
|
313
|
+
description: 'Export only works on Google native docs (Docs, Sheets, Slides, Drawings). For regular files use Download instead.',
|
|
314
|
+
},
|
|
315
|
+
],
|
|
316
|
+
},
|
|
317
|
+
getPermissions: {
|
|
318
|
+
label: 'List Permissions',
|
|
319
|
+
description: 'List who has access (audit before changing)',
|
|
320
|
+
apiMethod: 'permissions.list',
|
|
321
|
+
params: [fileId()],
|
|
322
|
+
},
|
|
323
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drive AI Toolkit — las herramientas que un driveAction expone cuando
|
|
3
|
+
* `aiEnabled` está encendido.
|
|
4
|
+
*
|
|
5
|
+
* **Se escribe una sola vez**, como el de Sheets y el de Telegram. Antes vivía
|
|
6
|
+
* TRES veces, y las tres a mano:
|
|
7
|
+
*
|
|
8
|
+
* 1. `dashboard/components/drive-actions/drive-operations-schemas.ts`
|
|
9
|
+
* 2. `api/src/mcp-servers/toolkit-specs.ts`
|
|
10
|
+
* 3. la página de detalle, que pintaba las doce herramientas como doce
|
|
11
|
+
* `<li>` escritos a mano, con el número `12` también a mano en tres sitios
|
|
12
|
+
*
|
|
13
|
+
* La 1 y la 2 no habían derivado todavía (medido el 2026-08-10: cero
|
|
14
|
+
* diferencias, al contrario que Slack con 13 y WhatsApp con 4). Esto es lo que
|
|
15
|
+
* hace que sigan sin derivar.
|
|
16
|
+
*
|
|
17
|
+
* El ORDEN de este array importa: `AiToolkitPreview` pinta el encabezado de
|
|
18
|
+
* grupo sólo cuando cambia respecto a la fila anterior, así que las
|
|
19
|
+
* herramientas del mismo grupo tienen que ir seguidas. Los tres grupos son los
|
|
20
|
+
* que ya inventaba el JSX que sustituye.
|
|
21
|
+
*/
|
|
22
|
+
import type { DriveOperation } from './drive-operations';
|
|
23
|
+
export interface DriveToolkitParameter {
|
|
24
|
+
name: string;
|
|
25
|
+
type: 'string' | 'number' | 'boolean';
|
|
26
|
+
description: string;
|
|
27
|
+
required: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface DriveToolkitSpec {
|
|
30
|
+
operation: DriveOperation;
|
|
31
|
+
/** Etiqueta corta de la fila en la lista de herramientas. */
|
|
32
|
+
label: string;
|
|
33
|
+
/**
|
|
34
|
+
* Nombre con el que el LLM llama a la herramienta. Todas llevan `_drive_` o
|
|
35
|
+
* `_to_drive` para no chocar con las de Gmail, Sheets o Calendar cuando un
|
|
36
|
+
* mismo AI Node tiene varios toolkits encendidos.
|
|
37
|
+
*/
|
|
38
|
+
toolName: string;
|
|
39
|
+
/** Descripción (más reglas de uso) que ve el LLM. */
|
|
40
|
+
description: string;
|
|
41
|
+
parameters: DriveToolkitParameter[];
|
|
42
|
+
/** Encabezado de grupo de la página de detalle. El orden es el que agrupa. */
|
|
43
|
+
group?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Marca que la herramienta cambia algo difícil de deshacer, para que la página
|
|
46
|
+
* lo enseñe y el AI Node pida confirmación. Son **dos**, las mismas que ya
|
|
47
|
+
* estaban marcadas: `delete_drive_file` y `share_drive_file`. `move` no lo
|
|
48
|
+
* lleva, y es defendible — deshacerlo es otro move.
|
|
49
|
+
*/
|
|
50
|
+
destructive?: boolean;
|
|
51
|
+
}
|
|
52
|
+
export declare const DRIVE_TOOLKIT_SPECS: DriveToolkitSpec[];
|
|
53
|
+
/** Índice por nombre de herramienta, para enrutar un `tools/call` sin recorrer. */
|
|
54
|
+
export declare const DRIVE_TOOLKIT_BY_TOOL_NAME: Record<string, DriveToolkitSpec>;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Drive AI Toolkit — las herramientas que un driveAction expone cuando
|
|
4
|
+
* `aiEnabled` está encendido.
|
|
5
|
+
*
|
|
6
|
+
* **Se escribe una sola vez**, como el de Sheets y el de Telegram. Antes vivía
|
|
7
|
+
* TRES veces, y las tres a mano:
|
|
8
|
+
*
|
|
9
|
+
* 1. `dashboard/components/drive-actions/drive-operations-schemas.ts`
|
|
10
|
+
* 2. `api/src/mcp-servers/toolkit-specs.ts`
|
|
11
|
+
* 3. la página de detalle, que pintaba las doce herramientas como doce
|
|
12
|
+
* `<li>` escritos a mano, con el número `12` también a mano en tres sitios
|
|
13
|
+
*
|
|
14
|
+
* La 1 y la 2 no habían derivado todavía (medido el 2026-08-10: cero
|
|
15
|
+
* diferencias, al contrario que Slack con 13 y WhatsApp con 4). Esto es lo que
|
|
16
|
+
* hace que sigan sin derivar.
|
|
17
|
+
*
|
|
18
|
+
* El ORDEN de este array importa: `AiToolkitPreview` pinta el encabezado de
|
|
19
|
+
* grupo sólo cuando cambia respecto a la fila anterior, así que las
|
|
20
|
+
* herramientas del mismo grupo tienen que ir seguidas. Los tres grupos son los
|
|
21
|
+
* que ya inventaba el JSX que sustituye.
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.DRIVE_TOOLKIT_BY_TOOL_NAME = exports.DRIVE_TOOLKIT_SPECS = void 0;
|
|
25
|
+
const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
|
|
26
|
+
const ENTRAN_Y_SALEN = 'Files in / out';
|
|
27
|
+
const DESCUBRIR = 'Discovery';
|
|
28
|
+
const MUTACIONES = 'Mutations (LLM confirms first)';
|
|
29
|
+
exports.DRIVE_TOOLKIT_SPECS = [
|
|
30
|
+
/* ── Files in / out ─────────────────────────────────────────────────── */
|
|
31
|
+
{
|
|
32
|
+
operation: 'upload',
|
|
33
|
+
label: 'Upload file',
|
|
34
|
+
toolName: 'upload_to_drive',
|
|
35
|
+
group: ENTRAN_Y_SALEN,
|
|
36
|
+
description: 'Upload a file from the current event payload into Google Drive. Requires an `_file` ref in the payload (created by an upstream node — multipart ingress, File Transform, or another Drive download). Streams from R2; never holds the full file in memory.',
|
|
37
|
+
parameters: [
|
|
38
|
+
p('fileName', 'Optional. Override the destination file name. Defaults to the _file ref originalName.', false),
|
|
39
|
+
p('sourceField', 'Optional. Payload field path holding the _file ref. Defaults to "_file".', false),
|
|
40
|
+
p('parentFolderId', 'Optional. Drive folder ID to upload into. Empty = root.', false),
|
|
41
|
+
p('mimeType', 'Optional. Override the MIME type. Defaults to the _file ref mimeType.', false),
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
operation: 'download',
|
|
46
|
+
label: 'Download file',
|
|
47
|
+
toolName: 'download_from_drive',
|
|
48
|
+
group: ENTRAN_Y_SALEN,
|
|
49
|
+
description: 'Download a Drive file to HostWebhook storage and return a `_file` ref the next pipeline node can consume (FileTransform, AI vision, HTTP body). Use the file ID from list_drive_files / search.',
|
|
50
|
+
parameters: [p('fileId', 'The Drive file ID to download.')],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
operation: 'export',
|
|
54
|
+
label: 'Export Google doc',
|
|
55
|
+
toolName: 'export_drive_file',
|
|
56
|
+
group: ENTRAN_Y_SALEN,
|
|
57
|
+
description: 'Export a Google native document (Doc / Sheet / Slide / Drawing) to a downloadable format like PDF or DOCX, returning a `_file` ref the next pipeline node can consume. Does NOT work on regular files (those use download_from_drive).',
|
|
58
|
+
parameters: [
|
|
59
|
+
p('fileId', 'The Drive file ID (must be a Google native doc).'),
|
|
60
|
+
/* De las dos copias me quedo con la de la api, que es la que dice qué
|
|
61
|
+
formato de origen va a cada destino. La del dashboard se había quedado
|
|
62
|
+
sin esa parte. Es la única deriva que tenían Drive y, como estaba en la
|
|
63
|
+
descripción de un PARÁMETRO, ningún conteo la miraba. */
|
|
64
|
+
p('mimeType', 'Target MIME type. Common: "application/pdf" (Doc/Slide → PDF), "application/vnd.openxmlformats-officedocument.wordprocessingml.document" (Doc → DOCX), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" (Sheet → XLSX), "text/plain", "text/csv".'),
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
/* ── Discovery ──────────────────────────────────────────────────────── */
|
|
68
|
+
{
|
|
69
|
+
operation: 'list',
|
|
70
|
+
label: 'List / search files',
|
|
71
|
+
toolName: 'list_drive_files',
|
|
72
|
+
group: DESCUBRIR,
|
|
73
|
+
description: "Search the user's Drive (or shared drive). Returns up to N matching files with id, name, mimeType, size, modifiedTime. Use this BEFORE any operation that needs a fileId — never invent IDs.",
|
|
74
|
+
parameters: [
|
|
75
|
+
p('query', 'Optional. Substring to match against file names.', false),
|
|
76
|
+
p('parentFolderId', 'Optional. Restrict search to a single folder.', false),
|
|
77
|
+
p('mimeType', 'Optional. Filter by MIME type (e.g. "application/pdf", "application/vnd.google-apps.folder").', false),
|
|
78
|
+
p('pageSize', 'Optional. Max results (default 50, max 100).', false, 'number'),
|
|
79
|
+
p('orderBy', 'Optional. Drive ordering string (default "modifiedTime desc").', false),
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
operation: 'get',
|
|
84
|
+
label: 'Get file metadata',
|
|
85
|
+
toolName: 'get_drive_file',
|
|
86
|
+
group: DESCUBRIR,
|
|
87
|
+
description: 'Read full metadata for one Drive file (name, mimeType, size, parents, owners, starred, trashed). Does NOT download the bytes — use download_from_drive for that.',
|
|
88
|
+
parameters: [p('fileId', 'The Drive file ID.')],
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
operation: 'getPermissions',
|
|
92
|
+
label: 'List file permissions',
|
|
93
|
+
toolName: 'get_drive_permissions',
|
|
94
|
+
group: DESCUBRIR,
|
|
95
|
+
description: 'List who has access to a Drive file (id, type, role, emailAddress/domain, displayName). Use to audit sharing before changing permissions.',
|
|
96
|
+
parameters: [p('fileId', 'The Drive file ID.')],
|
|
97
|
+
},
|
|
98
|
+
/* ── Mutations ──────────────────────────────────────────────────────── */
|
|
99
|
+
{
|
|
100
|
+
operation: 'copy',
|
|
101
|
+
label: 'Copy file',
|
|
102
|
+
toolName: 'copy_drive_file',
|
|
103
|
+
group: MUTACIONES,
|
|
104
|
+
description: 'Duplicate a Drive file. Optionally rename and/or move into a different folder in one step.',
|
|
105
|
+
parameters: [
|
|
106
|
+
p('fileId', 'The source Drive file ID.'),
|
|
107
|
+
p('newName', 'Optional. Name for the copy. Defaults to "Copy of <original>".', false),
|
|
108
|
+
p('parentFolderId', 'Optional. Folder ID to place the copy into. Defaults to the source folder.', false),
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
operation: 'move',
|
|
113
|
+
label: 'Move file',
|
|
114
|
+
toolName: 'move_drive_file',
|
|
115
|
+
group: MUTACIONES,
|
|
116
|
+
description: 'Move a Drive file to a different folder. The current parents are removed; the file is added to the new parent.',
|
|
117
|
+
parameters: [
|
|
118
|
+
p('fileId', 'The Drive file ID to move.'),
|
|
119
|
+
p('parentFolderId', 'Destination folder ID.'),
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
operation: 'update',
|
|
124
|
+
label: 'Update file metadata',
|
|
125
|
+
toolName: 'update_drive_file',
|
|
126
|
+
group: MUTACIONES,
|
|
127
|
+
description: 'Rename a Drive file or change its description / starred status. Use list_drive_files first to find the fileId.',
|
|
128
|
+
parameters: [
|
|
129
|
+
p('fileId', 'The Drive file ID.'),
|
|
130
|
+
p('name', 'Optional. New name.', false),
|
|
131
|
+
p('description', 'Optional. New description.', false),
|
|
132
|
+
p('starred', 'Optional. Star/unstar.', false, 'boolean'),
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
operation: 'createFolder',
|
|
137
|
+
label: 'Create folder',
|
|
138
|
+
toolName: 'create_drive_folder',
|
|
139
|
+
group: MUTACIONES,
|
|
140
|
+
description: 'Create a new folder in Drive. Use this BEFORE upload_to_drive when the user asks to put a file in a specific named folder that does not yet exist.',
|
|
141
|
+
parameters: [
|
|
142
|
+
p('name', 'Folder name.'),
|
|
143
|
+
p('parentFolderId', 'Optional. Parent folder ID. Empty = root.', false),
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
operation: 'delete',
|
|
148
|
+
label: 'Delete file (trash)',
|
|
149
|
+
toolName: 'delete_drive_file',
|
|
150
|
+
group: MUTACIONES,
|
|
151
|
+
description: 'Move a Drive file to trash (recoverable for 30 days). Set permanent=true ONLY when the user explicitly confirms a permanent delete — irreversible. ALWAYS confirm with the user before calling this with permanent=true.',
|
|
152
|
+
parameters: [
|
|
153
|
+
p('fileId', 'The Drive file ID to delete.'),
|
|
154
|
+
p('permanent', 'Optional. true = bypass trash and erase forever (irreversible). Default false.', false, 'boolean'),
|
|
155
|
+
],
|
|
156
|
+
destructive: true,
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
operation: 'share',
|
|
160
|
+
label: 'Share file',
|
|
161
|
+
toolName: 'share_drive_file',
|
|
162
|
+
group: MUTACIONES,
|
|
163
|
+
description: 'Grant a permission on a Drive file. CONFIRM with the user before calling this — sharing externally is sensitive. Public sharing (type="anyone") is especially dangerous; only use when the user explicitly says "make it public" or similar.',
|
|
164
|
+
parameters: [
|
|
165
|
+
p('fileId', 'The Drive file ID to share.'),
|
|
166
|
+
p('type', 'One of: "user" | "group" | "domain" | "anyone". Use "anyone" only with explicit user confirmation.'),
|
|
167
|
+
p('role', 'One of: "reader" | "commenter" | "writer".'),
|
|
168
|
+
p('emailAddress', 'Required when type=user|group.', false),
|
|
169
|
+
p('domain', 'Required when type=domain.', false),
|
|
170
|
+
p('sendNotificationEmail', 'Optional. true = email the recipient about the share.', false, 'boolean'),
|
|
171
|
+
],
|
|
172
|
+
destructive: true,
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
/** Índice por nombre de herramienta, para enrutar un `tools/call` sin recorrer. */
|
|
176
|
+
exports.DRIVE_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(exports.DRIVE_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
|
package/dist/index.d.ts
CHANGED
|
@@ -9,8 +9,10 @@ export { GMAIL_OPERATIONS, isGmailOperation, } from './gmail-operations';
|
|
|
9
9
|
export type { GmailOperation } from './gmail-operations';
|
|
10
10
|
export { GOOGLE_CALENDAR_OPERATIONS, isGoogleCalendarOperation, } from './calendar-operations';
|
|
11
11
|
export type { GoogleCalendarOperation } from './calendar-operations';
|
|
12
|
-
export { DRIVE_OPERATIONS, isDriveOperation, } from './drive-operations';
|
|
13
|
-
export type { DriveOperation } from './drive-operations';
|
|
12
|
+
export { DRIVE_OPERATIONS, DRIVE_OPERATION_SPECS, isDriveOperation, } from './drive-operations';
|
|
13
|
+
export type { DriveOperation, DriveParamType, DriveParamSpec, DriveOperationSpec, } from './drive-operations';
|
|
14
|
+
export { DRIVE_TOOLKIT_SPECS, DRIVE_TOOLKIT_BY_TOOL_NAME, } from './drive-toolkit';
|
|
15
|
+
export type { DriveToolkitSpec, DriveToolkitParameter, } from './drive-toolkit';
|
|
14
16
|
export { TELEGRAM_OPERATIONS, TELEGRAM_OPERATION_SPECS, isTelegramOperation, } from './telegram-operations';
|
|
15
17
|
export type { TelegramOperation, TelegramParamSpec, TelegramOperationSpec, } from './telegram-operations';
|
|
16
18
|
export { TELEGRAM_TOOLKIT_SPECS, TELEGRAM_TOOLKIT_BY_TOOL_NAME, } from './telegram-toolkit';
|
|
@@ -27,5 +29,7 @@ export { SHEETS_TOOLKIT_SPECS, SHEETS_TOOLKIT_BY_TOOL_NAME, SHEETS_TOOLKIT_DEFAU
|
|
|
27
29
|
export type { SheetsToolkitSpec, SheetsToolkitParameter, } from './sheets-toolkit';
|
|
28
30
|
export { GOOGLE_CONTACTS_OPERATIONS, GOOGLE_CONTACTS_OPERATIONS_V1, GOOGLE_CONTACTS_OPERATIONS_V2, GOOGLE_CONTACTS_OPERATION_SPECS, GOOGLE_CONTACTS_OPERATION_GROUPS, GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS, isGoogleContactsOperation, } from './google-contacts-operations';
|
|
29
31
|
export type { GoogleContactsOperation, GoogleContactsParamSpec, GoogleContactsOperationSpec, GoogleContactsOperationGroup, } from './google-contacts-operations';
|
|
32
|
+
export { POSTGRES_OPERATIONS, POSTGRES_MODES, POSTGRES_OPERATION_SPECS, isPostgresOperation, } from './postgres-operations';
|
|
33
|
+
export type { PostgresOperation, PostgresMode, PostgresOperationSpec, } from './postgres-operations';
|
|
30
34
|
export type { CredentialTypeRegistration, CredentialType } from './credentials';
|
|
31
35
|
export { CREDENTIAL_TYPES, CREDENTIAL_TYPE_VALUES, credentialTypeValues, getCredentialType, isCredentialType, } from './credentials';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.isGoogleContactsOperation = exports.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = exports.GOOGLE_CONTACTS_OPERATION_GROUPS = exports.GOOGLE_CONTACTS_OPERATION_SPECS = void 0;
|
|
3
|
+
exports.SHEETS_TOOLKIT_DEFAULTABLE = exports.SHEETS_TOOLKIT_BY_TOOL_NAME = exports.SHEETS_TOOLKIT_SPECS = exports.isSheetsOperation = exports.SHEETS_OPERATION_SPECS = exports.SHEETS_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.TELEGRAM_TOOLKIT_BY_TOOL_NAME = exports.TELEGRAM_TOOLKIT_SPECS = exports.isTelegramOperation = exports.TELEGRAM_OPERATION_SPECS = exports.TELEGRAM_OPERATIONS = exports.DRIVE_TOOLKIT_BY_TOOL_NAME = exports.DRIVE_TOOLKIT_SPECS = exports.isDriveOperation = exports.DRIVE_OPERATION_SPECS = 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
|
+
exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.isPostgresOperation = exports.POSTGRES_OPERATION_SPECS = exports.POSTGRES_MODES = exports.POSTGRES_OPERATIONS = exports.isGoogleContactsOperation = exports.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = exports.GOOGLE_CONTACTS_OPERATION_GROUPS = exports.GOOGLE_CONTACTS_OPERATION_SPECS = exports.GOOGLE_CONTACTS_OPERATIONS_V2 = exports.GOOGLE_CONTACTS_OPERATIONS_V1 = exports.GOOGLE_CONTACTS_OPERATIONS = void 0;
|
|
5
5
|
var types_1 = require("./types");
|
|
6
6
|
Object.defineProperty(exports, "singleMeta", { enumerable: true, get: function () { return types_1.singleMeta; } });
|
|
7
7
|
Object.defineProperty(exports, "iterableMeta", { enumerable: true, get: function () { return types_1.iterableMeta; } });
|
|
@@ -41,7 +41,11 @@ Object.defineProperty(exports, "GOOGLE_CALENDAR_OPERATIONS", { enumerable: true,
|
|
|
41
41
|
Object.defineProperty(exports, "isGoogleCalendarOperation", { enumerable: true, get: function () { return calendar_operations_1.isGoogleCalendarOperation; } });
|
|
42
42
|
var drive_operations_1 = require("./drive-operations");
|
|
43
43
|
Object.defineProperty(exports, "DRIVE_OPERATIONS", { enumerable: true, get: function () { return drive_operations_1.DRIVE_OPERATIONS; } });
|
|
44
|
+
Object.defineProperty(exports, "DRIVE_OPERATION_SPECS", { enumerable: true, get: function () { return drive_operations_1.DRIVE_OPERATION_SPECS; } });
|
|
44
45
|
Object.defineProperty(exports, "isDriveOperation", { enumerable: true, get: function () { return drive_operations_1.isDriveOperation; } });
|
|
46
|
+
var drive_toolkit_1 = require("./drive-toolkit");
|
|
47
|
+
Object.defineProperty(exports, "DRIVE_TOOLKIT_SPECS", { enumerable: true, get: function () { return drive_toolkit_1.DRIVE_TOOLKIT_SPECS; } });
|
|
48
|
+
Object.defineProperty(exports, "DRIVE_TOOLKIT_BY_TOOL_NAME", { enumerable: true, get: function () { return drive_toolkit_1.DRIVE_TOOLKIT_BY_TOOL_NAME; } });
|
|
45
49
|
var telegram_operations_1 = require("./telegram-operations");
|
|
46
50
|
Object.defineProperty(exports, "TELEGRAM_OPERATIONS", { enumerable: true, get: function () { return telegram_operations_1.TELEGRAM_OPERATIONS; } });
|
|
47
51
|
Object.defineProperty(exports, "TELEGRAM_OPERATION_SPECS", { enumerable: true, get: function () { return telegram_operations_1.TELEGRAM_OPERATION_SPECS; } });
|
|
@@ -76,6 +80,11 @@ Object.defineProperty(exports, "GOOGLE_CONTACTS_OPERATION_SPECS", { enumerable:
|
|
|
76
80
|
Object.defineProperty(exports, "GOOGLE_CONTACTS_OPERATION_GROUPS", { enumerable: true, get: function () { return google_contacts_operations_1.GOOGLE_CONTACTS_OPERATION_GROUPS; } });
|
|
77
81
|
Object.defineProperty(exports, "GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS", { enumerable: true, get: function () { return google_contacts_operations_1.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS; } });
|
|
78
82
|
Object.defineProperty(exports, "isGoogleContactsOperation", { enumerable: true, get: function () { return google_contacts_operations_1.isGoogleContactsOperation; } });
|
|
83
|
+
var postgres_operations_1 = require("./postgres-operations");
|
|
84
|
+
Object.defineProperty(exports, "POSTGRES_OPERATIONS", { enumerable: true, get: function () { return postgres_operations_1.POSTGRES_OPERATIONS; } });
|
|
85
|
+
Object.defineProperty(exports, "POSTGRES_MODES", { enumerable: true, get: function () { return postgres_operations_1.POSTGRES_MODES; } });
|
|
86
|
+
Object.defineProperty(exports, "POSTGRES_OPERATION_SPECS", { enumerable: true, get: function () { return postgres_operations_1.POSTGRES_OPERATION_SPECS; } });
|
|
87
|
+
Object.defineProperty(exports, "isPostgresOperation", { enumerable: true, get: function () { return postgres_operations_1.isPostgresOperation; } });
|
|
79
88
|
var credentials_1 = require("./credentials");
|
|
80
89
|
Object.defineProperty(exports, "CREDENTIAL_TYPES", { enumerable: true, get: function () { return credentials_1.CREDENTIAL_TYPES; } });
|
|
81
90
|
Object.defineProperty(exports, "CREDENTIAL_TYPE_VALUES", { enumerable: true, get: function () { return credentials_1.CREDENTIAL_TYPE_VALUES; } });
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres operation enum + metadatos por operación — una sola copia para la
|
|
3
|
+
* api, el Dashboard y el broker.
|
|
4
|
+
*
|
|
5
|
+
* ── Por qué esto no trae un esquema de formulario ──
|
|
6
|
+
* A diferencia de Slack, Discord, Sheets, Telegram, Contacts y Drive,
|
|
7
|
+
* `postgresAction` **no tiene campos por operación**: el mismo formulario —SQL,
|
|
8
|
+
* Parameters, Statement timeout— se pinta para las cinco. Su eje real es `mode`
|
|
9
|
+
* (`single` / `transaction`). Así que aquí no hay `*_OPERATION_SPECS` de params,
|
|
10
|
+
* porque saldrían idénticos cinco veces; lo que sí había era la lista escrita a
|
|
11
|
+
* mano **nueve veces**, y eso es lo que esto quita:
|
|
12
|
+
*
|
|
13
|
+
* api dto: @ApiProperty enum, @IsIn, la unión TS (3)
|
|
14
|
+
* api entity: @Prop enum, la unión TS (2)
|
|
15
|
+
* dash lib/api.ts: la unión TS (1)
|
|
16
|
+
* dash PostgresTablePicker: la unión en un prop (1)
|
|
17
|
+
* dash PostgresActionDetail: OPERATIONS (1)
|
|
18
|
+
* dash PostgresNode + FlowPanel: opLabels idénticos (2)
|
|
19
|
+
*
|
|
20
|
+
* Las **clases de color no están aquí**: son presentación (Tailwind), y se
|
|
21
|
+
* quedan en el dashboard, igual que los `ICONOS` de Telegram y de Drive.
|
|
22
|
+
*/
|
|
23
|
+
export declare const POSTGRES_OPERATIONS: readonly ["query", "insertOne", "update", "delete", "execute"];
|
|
24
|
+
export type PostgresOperation = (typeof POSTGRES_OPERATIONS)[number];
|
|
25
|
+
/** Type guard — útil al validar entrada que no controlas (DTOs, tool calls). */
|
|
26
|
+
export declare function isPostgresOperation(value: unknown): value is PostgresOperation;
|
|
27
|
+
/**
|
|
28
|
+
* Cómo se ejecuta el nodo. `single` es una sentencia; `transaction` es una lista
|
|
29
|
+
* ordenada dentro de BEGIN / COMMIT con rollback condicional.
|
|
30
|
+
*/
|
|
31
|
+
export declare const POSTGRES_MODES: readonly ["single", "transaction"];
|
|
32
|
+
export type PostgresMode = (typeof POSTGRES_MODES)[number];
|
|
33
|
+
export interface PostgresOperationSpec {
|
|
34
|
+
/**
|
|
35
|
+
* Etiqueta corta, para la píldora estrecha del lienzo y del panel. Estaba
|
|
36
|
+
* escrita **idéntica** en `PostgresNode.tsx` y en `FlowPanel.tsx`.
|
|
37
|
+
*/
|
|
38
|
+
label: string;
|
|
39
|
+
/**
|
|
40
|
+
* La etiqueta larga del título del editor de SQL en la página de detalle,
|
|
41
|
+
* donde hay sitio para el paréntesis aclaratorio. **No es la misma que
|
|
42
|
+
* `label` y no debe unificarse**: son dos anchos distintos y las dos se ven.
|
|
43
|
+
*/
|
|
44
|
+
labelLong: string;
|
|
45
|
+
/** Una línea de cuándo usar esta operación. */
|
|
46
|
+
description: string;
|
|
47
|
+
/** SQL de ejemplo, el `placeholder` del editor. */
|
|
48
|
+
sqlPlaceholder: string;
|
|
49
|
+
/**
|
|
50
|
+
* Si la operación devuelve filas **siempre**. Sólo `execute` no: ahí depende
|
|
51
|
+
* de `returnRows` en la entidad, porque puede ser DDL.
|
|
52
|
+
*
|
|
53
|
+
* Este invariante vivía en dos sitios y en uno era un comentario: la api lo
|
|
54
|
+
* calculaba con `entity.operation !== 'execute' || entity.returnRows`, y el
|
|
55
|
+
* dashboard lo expresaba como `localOperation === "execute"` para decidir si
|
|
56
|
+
* enseñaba el interruptor. Es el mismo hecho, dicho aquí una vez.
|
|
57
|
+
*/
|
|
58
|
+
returnsRowsAlways: boolean;
|
|
59
|
+
}
|
|
60
|
+
export declare const POSTGRES_OPERATION_SPECS: Record<PostgresOperation, PostgresOperationSpec>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Postgres operation enum + metadatos por operación — una sola copia para la
|
|
4
|
+
* api, el Dashboard y el broker.
|
|
5
|
+
*
|
|
6
|
+
* ── Por qué esto no trae un esquema de formulario ──
|
|
7
|
+
* A diferencia de Slack, Discord, Sheets, Telegram, Contacts y Drive,
|
|
8
|
+
* `postgresAction` **no tiene campos por operación**: el mismo formulario —SQL,
|
|
9
|
+
* Parameters, Statement timeout— se pinta para las cinco. Su eje real es `mode`
|
|
10
|
+
* (`single` / `transaction`). Así que aquí no hay `*_OPERATION_SPECS` de params,
|
|
11
|
+
* porque saldrían idénticos cinco veces; lo que sí había era la lista escrita a
|
|
12
|
+
* mano **nueve veces**, y eso es lo que esto quita:
|
|
13
|
+
*
|
|
14
|
+
* api dto: @ApiProperty enum, @IsIn, la unión TS (3)
|
|
15
|
+
* api entity: @Prop enum, la unión TS (2)
|
|
16
|
+
* dash lib/api.ts: la unión TS (1)
|
|
17
|
+
* dash PostgresTablePicker: la unión en un prop (1)
|
|
18
|
+
* dash PostgresActionDetail: OPERATIONS (1)
|
|
19
|
+
* dash PostgresNode + FlowPanel: opLabels idénticos (2)
|
|
20
|
+
*
|
|
21
|
+
* Las **clases de color no están aquí**: son presentación (Tailwind), y se
|
|
22
|
+
* quedan en el dashboard, igual que los `ICONOS` de Telegram y de Drive.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.POSTGRES_OPERATION_SPECS = exports.POSTGRES_MODES = exports.POSTGRES_OPERATIONS = void 0;
|
|
26
|
+
exports.isPostgresOperation = isPostgresOperation;
|
|
27
|
+
exports.POSTGRES_OPERATIONS = [
|
|
28
|
+
'query', // SELECT — devuelve filas
|
|
29
|
+
'insertOne', // INSERT, normalmente con RETURNING *
|
|
30
|
+
'update', // UPDATE ... WHERE
|
|
31
|
+
'delete', // DELETE ... WHERE
|
|
32
|
+
'execute', // SQL cualquiera: DDL, transacciones, multi-sentencia
|
|
33
|
+
];
|
|
34
|
+
/** Type guard — útil al validar entrada que no controlas (DTOs, tool calls). */
|
|
35
|
+
function isPostgresOperation(value) {
|
|
36
|
+
return typeof value === 'string' && exports.POSTGRES_OPERATIONS.includes(value);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Cómo se ejecuta el nodo. `single` es una sentencia; `transaction` es una lista
|
|
40
|
+
* ordenada dentro de BEGIN / COMMIT con rollback condicional.
|
|
41
|
+
*/
|
|
42
|
+
exports.POSTGRES_MODES = ['single', 'transaction'];
|
|
43
|
+
exports.POSTGRES_OPERATION_SPECS = {
|
|
44
|
+
query: {
|
|
45
|
+
label: 'SELECT',
|
|
46
|
+
labelLong: 'SELECT (query)',
|
|
47
|
+
description: 'Run a SELECT and return rows',
|
|
48
|
+
sqlPlaceholder: 'SELECT id, email FROM users WHERE org_id = {{payload.orgId}} ORDER BY created_at DESC LIMIT 50',
|
|
49
|
+
returnsRowsAlways: true,
|
|
50
|
+
},
|
|
51
|
+
insertOne: {
|
|
52
|
+
label: 'INSERT',
|
|
53
|
+
labelLong: 'INSERT',
|
|
54
|
+
description: 'Insert a row (with RETURNING * recommended)',
|
|
55
|
+
sqlPlaceholder: 'INSERT INTO events (kind, payload) VALUES ({{payload.kind}}, {{payload.data}}) RETURNING *',
|
|
56
|
+
returnsRowsAlways: true,
|
|
57
|
+
},
|
|
58
|
+
update: {
|
|
59
|
+
label: 'UPDATE',
|
|
60
|
+
labelLong: 'UPDATE',
|
|
61
|
+
description: 'Update rows matching WHERE',
|
|
62
|
+
sqlPlaceholder: 'UPDATE users SET last_seen = NOW() WHERE id = {{payload.userId}} RETURNING id',
|
|
63
|
+
returnsRowsAlways: true,
|
|
64
|
+
},
|
|
65
|
+
delete: {
|
|
66
|
+
label: 'DELETE',
|
|
67
|
+
labelLong: 'DELETE',
|
|
68
|
+
description: 'Delete rows matching WHERE',
|
|
69
|
+
sqlPlaceholder: 'DELETE FROM sessions WHERE expires_at < NOW() RETURNING id',
|
|
70
|
+
returnsRowsAlways: true,
|
|
71
|
+
},
|
|
72
|
+
execute: {
|
|
73
|
+
label: 'EXEC',
|
|
74
|
+
labelLong: 'EXEC (raw)',
|
|
75
|
+
description: 'Run any SQL — DDL, transactions, or multi-stmt. Toggle returnRows for INSERT ... RETURNING.',
|
|
76
|
+
sqlPlaceholder: 'CREATE TEMP TABLE x AS SELECT 1; ANALYZE; -- DDL or batch',
|
|
77
|
+
returnsRowsAlways: false,
|
|
78
|
+
},
|
|
79
|
+
};
|
package/package.json
CHANGED