@aigentyc/mcp 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +3 -1
- package/context7.json +1 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1 -0
- package/dist/client.js.map +1 -1
- package/dist/llms-full.txt +74 -12
- package/dist/tools/backups.d.ts.map +1 -1
- package/dist/tools/backups.js +17 -12
- package/dist/tools/backups.js.map +1 -1
- package/dist/tools/extract.d.ts.map +1 -1
- package/dist/tools/extract.js +2 -6
- package/dist/tools/extract.js.map +1 -1
- package/dist/tools/form_option_sources.d.ts +4 -0
- package/dist/tools/form_option_sources.d.ts.map +1 -0
- package/dist/tools/form_option_sources.js +251 -0
- package/dist/tools/form_option_sources.js.map +1 -0
- package/dist/tools/forms.d.ts +4 -0
- package/dist/tools/forms.d.ts.map +1 -0
- package/dist/tools/forms.js +833 -0
- package/dist/tools/forms.js.map +1 -0
- package/dist/tools/golden_answers.d.ts.map +1 -1
- package/dist/tools/golden_answers.js +32 -8
- package/dist/tools/golden_answers.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +7 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/integrations.d.ts +4 -0
- package/dist/tools/integrations.d.ts.map +1 -0
- package/dist/tools/integrations.js +225 -0
- package/dist/tools/integrations.js.map +1 -0
- package/dist/tools/link_sources.d.ts.map +1 -1
- package/dist/tools/link_sources.js +196 -10
- package/dist/tools/link_sources.js.map +1 -1
- package/dist/tools/page_questions.d.ts.map +1 -1
- package/dist/tools/page_questions.js +3 -1
- package/dist/tools/page_questions.js.map +1 -1
- package/dist/tools/tools_crud.d.ts.map +1 -1
- package/dist/tools/tools_crud.js +121 -6
- package/dist/tools/tools_crud.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,833 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { pid, projectIdArg } from "./helpers.js";
|
|
3
|
+
import { defineTool } from "./types.js";
|
|
4
|
+
// ── Field-level shapes ─────────────────────────────────────────────
|
|
5
|
+
//
|
|
6
|
+
// Mirrors `FormField` in the frontend (`src/types/forms.ts`). Kept as
|
|
7
|
+
// passthrough on the JSON-shaped inner objects so the backend stays the source
|
|
8
|
+
// of truth — we just block obvious typos at the MCP boundary.
|
|
9
|
+
const formFieldType = z.enum([
|
|
10
|
+
"text",
|
|
11
|
+
"textarea",
|
|
12
|
+
"number",
|
|
13
|
+
"email",
|
|
14
|
+
"phone",
|
|
15
|
+
"date",
|
|
16
|
+
"time",
|
|
17
|
+
"select",
|
|
18
|
+
"multi_select",
|
|
19
|
+
"checkbox",
|
|
20
|
+
"radio",
|
|
21
|
+
"file",
|
|
22
|
+
]);
|
|
23
|
+
const formFieldOption = z.object({
|
|
24
|
+
value: z.string(),
|
|
25
|
+
label: z.string(),
|
|
26
|
+
});
|
|
27
|
+
const formFieldOptionsSource = z
|
|
28
|
+
.object({
|
|
29
|
+
sourceId: z
|
|
30
|
+
.string()
|
|
31
|
+
.min(1)
|
|
32
|
+
.describe("ID of a row in form_option_sources (see form_option_sources_list / form_option_sources_create)."),
|
|
33
|
+
dependsOn: z
|
|
34
|
+
.array(z.object({
|
|
35
|
+
fieldKey: z.string().min(1),
|
|
36
|
+
paramName: z.string().min(1),
|
|
37
|
+
}))
|
|
38
|
+
.optional(),
|
|
39
|
+
searchMode: z.enum(["server", "client", "off"]).optional(),
|
|
40
|
+
})
|
|
41
|
+
.passthrough();
|
|
42
|
+
const formField = z
|
|
43
|
+
.object({
|
|
44
|
+
id: z.string().min(1),
|
|
45
|
+
key: z
|
|
46
|
+
.string()
|
|
47
|
+
.min(1)
|
|
48
|
+
.describe("camelCase or snake_case field key. Used in templates as `{{field.key}}`."),
|
|
49
|
+
label: z.string().min(1),
|
|
50
|
+
type: formFieldType,
|
|
51
|
+
required: z.boolean().default(false),
|
|
52
|
+
placeholder: z.string().optional(),
|
|
53
|
+
helpText: z.string().optional(),
|
|
54
|
+
defaultValue: z.unknown().optional(),
|
|
55
|
+
options: z.array(formFieldOption).optional(),
|
|
56
|
+
optionsSource: formFieldOptionsSource.optional(),
|
|
57
|
+
validation: z
|
|
58
|
+
.object({
|
|
59
|
+
minLength: z.number().int().optional(),
|
|
60
|
+
maxLength: z.number().int().optional(),
|
|
61
|
+
min: z.number().optional(),
|
|
62
|
+
max: z.number().optional(),
|
|
63
|
+
pattern: z.string().optional(),
|
|
64
|
+
message: z.string().optional(),
|
|
65
|
+
maxFileSize: z
|
|
66
|
+
.number()
|
|
67
|
+
.int()
|
|
68
|
+
.optional()
|
|
69
|
+
.describe("Bytes. File fields only."),
|
|
70
|
+
allowedMimeTypes: z
|
|
71
|
+
.array(z.string())
|
|
72
|
+
.optional()
|
|
73
|
+
.describe("File fields only. Empty/omit = any."),
|
|
74
|
+
})
|
|
75
|
+
.partial()
|
|
76
|
+
.passthrough()
|
|
77
|
+
.optional(),
|
|
78
|
+
})
|
|
79
|
+
.passthrough();
|
|
80
|
+
// ── Action shapes ──────────────────────────────────────────────────
|
|
81
|
+
//
|
|
82
|
+
// Post-submit actions run in `order` on each submission. They form a chain:
|
|
83
|
+
// every action with a `name` exposes its output under `{{steps.<name>.<path>}}`
|
|
84
|
+
// for later actions. All string values flow through the template engine, which
|
|
85
|
+
// understands `{{field.key}}` (submitted values), `{{meta.x}}` (submission
|
|
86
|
+
// metadata), and `{{steps.<name>.x}}` (a prior action's output).
|
|
87
|
+
//
|
|
88
|
+
// The schema is a discriminated union on `type` so each action's `config` is
|
|
89
|
+
// validated against its own shape. Every config object is `.passthrough()` so
|
|
90
|
+
// any new key the backend introduces still flows through unblocked.
|
|
91
|
+
const formActionRunIf = z
|
|
92
|
+
.object({
|
|
93
|
+
fieldKey: z.string(),
|
|
94
|
+
operator: z.enum([
|
|
95
|
+
"eq",
|
|
96
|
+
"neq",
|
|
97
|
+
"contains",
|
|
98
|
+
"exists",
|
|
99
|
+
"not_exists",
|
|
100
|
+
"lt",
|
|
101
|
+
"lte",
|
|
102
|
+
"gt",
|
|
103
|
+
"gte",
|
|
104
|
+
]),
|
|
105
|
+
value: z.union([z.string(), z.number(), z.boolean()]).optional(),
|
|
106
|
+
})
|
|
107
|
+
.describe("Run this action only when the named submitted field matches.");
|
|
108
|
+
const emailActionConfig = z
|
|
109
|
+
.object({
|
|
110
|
+
to: z.string().min(1),
|
|
111
|
+
cc: z.string().optional(),
|
|
112
|
+
bcc: z.string().optional(),
|
|
113
|
+
replyTo: z.string().optional(),
|
|
114
|
+
subject: z.string(),
|
|
115
|
+
bodyTemplate: z.string(),
|
|
116
|
+
bodyType: z.enum(["html", "text"]).optional(),
|
|
117
|
+
attachments: z
|
|
118
|
+
.array(z.object({
|
|
119
|
+
fieldKey: z.string().min(1).describe("File field whose file to send."),
|
|
120
|
+
mode: z
|
|
121
|
+
.enum(["attach", "link"])
|
|
122
|
+
.describe("'attach' = binary attachment; 'link' = signed URL in body."),
|
|
123
|
+
}))
|
|
124
|
+
.optional()
|
|
125
|
+
.describe("Files to ship with the email, sourced from file fields."),
|
|
126
|
+
})
|
|
127
|
+
.passthrough();
|
|
128
|
+
const webhookActionConfig = z
|
|
129
|
+
.object({
|
|
130
|
+
url: z.string().url(),
|
|
131
|
+
method: z.enum(["POST", "PUT", "PATCH"]),
|
|
132
|
+
headers: z.record(z.string()).optional(),
|
|
133
|
+
bodyMode: z
|
|
134
|
+
.enum(["template", "auto_json", "multipart"])
|
|
135
|
+
.optional()
|
|
136
|
+
.describe("'auto_json' sends { form, meta, fields } (ignores bodyTemplate); 'template' renders bodyTemplate; 'multipart' sends form-data with bodyTemplate as the JSON part + binary parts from attachmentFieldKeys."),
|
|
137
|
+
bodyTemplate: z.string(),
|
|
138
|
+
attachmentFieldKeys: z
|
|
139
|
+
.array(z.string())
|
|
140
|
+
.optional()
|
|
141
|
+
.describe("File field keys sent as binary parts in 'multipart' mode."),
|
|
142
|
+
hmacSecret: z.string().optional(),
|
|
143
|
+
hmacHeaderName: z.string().optional(),
|
|
144
|
+
})
|
|
145
|
+
.passthrough();
|
|
146
|
+
const dataStoreActionConfig = z
|
|
147
|
+
.object({
|
|
148
|
+
storeId: z.string().min(1),
|
|
149
|
+
fieldMapping: z
|
|
150
|
+
.record(z.string())
|
|
151
|
+
.describe("Map of {data-store column → templated value}."),
|
|
152
|
+
})
|
|
153
|
+
.passthrough();
|
|
154
|
+
const toolCallActionConfig = z
|
|
155
|
+
.object({
|
|
156
|
+
toolId: z.string().min(1),
|
|
157
|
+
inputMapping: z
|
|
158
|
+
.record(z.string())
|
|
159
|
+
.describe("Map of {tool input param → templated value}."),
|
|
160
|
+
})
|
|
161
|
+
.passthrough();
|
|
162
|
+
const showMessageActionConfig = z
|
|
163
|
+
.object({ message: z.string() })
|
|
164
|
+
.passthrough();
|
|
165
|
+
const salesforceActionConfig = z
|
|
166
|
+
.object({
|
|
167
|
+
integrationId: z
|
|
168
|
+
.string()
|
|
169
|
+
.min(1)
|
|
170
|
+
.describe("provider_integrations id (provider='salesforce')."),
|
|
171
|
+
sobject: z.string().min(1).describe("Target sObject, e.g. 'Lead'."),
|
|
172
|
+
fieldMapping: z
|
|
173
|
+
.record(z.string())
|
|
174
|
+
.describe("Map of {SF field name → templated value}."),
|
|
175
|
+
updatePolicy: z
|
|
176
|
+
.enum(["create_only", "upsert"])
|
|
177
|
+
.optional()
|
|
178
|
+
.describe("'create_only' (default) always inserts; 'upsert' matches via matchField."),
|
|
179
|
+
matchField: z
|
|
180
|
+
.string()
|
|
181
|
+
.optional()
|
|
182
|
+
.describe("For upsert: SF field to find an existing row (e.g. 'Email')."),
|
|
183
|
+
matchFieldIsExternalId: z
|
|
184
|
+
.boolean()
|
|
185
|
+
.optional()
|
|
186
|
+
.describe("Set true when matchField is a SF external ID (native PATCH-by-extId)."),
|
|
187
|
+
attachFileFieldKeys: z
|
|
188
|
+
.array(z.string())
|
|
189
|
+
.optional()
|
|
190
|
+
.describe("File field keys uploaded as ContentVersion + linked to the record."),
|
|
191
|
+
})
|
|
192
|
+
.passthrough();
|
|
193
|
+
const salesforceAttachActionConfig = z
|
|
194
|
+
.object({
|
|
195
|
+
integrationId: z.string().min(1),
|
|
196
|
+
targetRecordIdTemplate: z
|
|
197
|
+
.string()
|
|
198
|
+
.min(1)
|
|
199
|
+
.describe("Template resolving to the LinkedEntityId, e.g. '{{steps.case.id}}'."),
|
|
200
|
+
attachFileFieldKeys: z
|
|
201
|
+
.array(z.string())
|
|
202
|
+
.describe("File field keys whose files are attached to the target record."),
|
|
203
|
+
})
|
|
204
|
+
.passthrough();
|
|
205
|
+
const salesforceLookupActionConfig = z
|
|
206
|
+
.object({
|
|
207
|
+
integrationId: z.string().min(1),
|
|
208
|
+
soqlTemplate: z
|
|
209
|
+
.string()
|
|
210
|
+
.min(1)
|
|
211
|
+
.describe("SOQL with {{field.x}}/{{steps.y.z}} tokens (SOQL-escaped). LIMIT 1 enforced; first row exposed at {{steps.<name>.record.<field>}}."),
|
|
212
|
+
onNotFound: z
|
|
213
|
+
.enum(["fail", "continue"])
|
|
214
|
+
.optional()
|
|
215
|
+
.describe("Behavior when zero rows match. Defaults to 'fail'."),
|
|
216
|
+
})
|
|
217
|
+
.passthrough();
|
|
218
|
+
// Fields common to every action, regardless of type.
|
|
219
|
+
const actionBase = {
|
|
220
|
+
id: z
|
|
221
|
+
.string()
|
|
222
|
+
.min(1)
|
|
223
|
+
.describe("Stable unique id for this action (generate one, e.g. a uuid)."),
|
|
224
|
+
order: z.number().int().min(0).describe("Execution order, ascending."),
|
|
225
|
+
continueOnError: z
|
|
226
|
+
.boolean()
|
|
227
|
+
.default(false)
|
|
228
|
+
.describe("When true, a failure here doesn't abort later actions."),
|
|
229
|
+
isActive: z.boolean().default(true),
|
|
230
|
+
name: z
|
|
231
|
+
.string()
|
|
232
|
+
.optional()
|
|
233
|
+
.describe("Slug used as the `{{steps.<name>}}` key for downstream actions. Keep it stable — renaming breaks references. Falls back to `step_<index+1>` when blank."),
|
|
234
|
+
runIf: formActionRunIf.optional(),
|
|
235
|
+
discoveredOutputPaths: z
|
|
236
|
+
.array(z.string())
|
|
237
|
+
.optional()
|
|
238
|
+
.describe("Dot-paths discovered by the last successful test run; powers `{{steps.<name>.<path>}}` hints. Normally written by forms_actions_test_run — rarely set by hand."),
|
|
239
|
+
};
|
|
240
|
+
const formAction = z.discriminatedUnion("type", [
|
|
241
|
+
z.object({ ...actionBase, type: z.literal("email"), config: emailActionConfig }),
|
|
242
|
+
z.object({
|
|
243
|
+
...actionBase,
|
|
244
|
+
type: z.literal("webhook"),
|
|
245
|
+
config: webhookActionConfig,
|
|
246
|
+
}),
|
|
247
|
+
z.object({
|
|
248
|
+
...actionBase,
|
|
249
|
+
type: z.literal("data_store"),
|
|
250
|
+
config: dataStoreActionConfig,
|
|
251
|
+
}),
|
|
252
|
+
z.object({
|
|
253
|
+
...actionBase,
|
|
254
|
+
type: z.literal("tool_call"),
|
|
255
|
+
config: toolCallActionConfig,
|
|
256
|
+
}),
|
|
257
|
+
z.object({
|
|
258
|
+
...actionBase,
|
|
259
|
+
type: z.literal("show_message"),
|
|
260
|
+
config: showMessageActionConfig,
|
|
261
|
+
}),
|
|
262
|
+
z.object({
|
|
263
|
+
...actionBase,
|
|
264
|
+
type: z.literal("salesforce"),
|
|
265
|
+
config: salesforceActionConfig,
|
|
266
|
+
}),
|
|
267
|
+
z.object({
|
|
268
|
+
...actionBase,
|
|
269
|
+
type: z.literal("salesforce_attach"),
|
|
270
|
+
config: salesforceAttachActionConfig,
|
|
271
|
+
}),
|
|
272
|
+
z.object({
|
|
273
|
+
...actionBase,
|
|
274
|
+
type: z.literal("salesforce_lookup"),
|
|
275
|
+
config: salesforceLookupActionConfig,
|
|
276
|
+
}),
|
|
277
|
+
]);
|
|
278
|
+
// ── Settings ───────────────────────────────────────────────────────
|
|
279
|
+
const formSettings = z
|
|
280
|
+
.object({
|
|
281
|
+
submitButtonText: z.string().optional(),
|
|
282
|
+
successMessage: z.string().optional(),
|
|
283
|
+
errorMessage: z.string().optional(),
|
|
284
|
+
direction: z.enum(["ltr", "rtl"]).optional(),
|
|
285
|
+
theme: z.enum(["default", "compact"]).optional(),
|
|
286
|
+
messages: z.record(z.string()).optional(),
|
|
287
|
+
rules: z.array(z.record(z.unknown())).optional(),
|
|
288
|
+
})
|
|
289
|
+
.partial()
|
|
290
|
+
.passthrough();
|
|
291
|
+
// ── Tool schemas ───────────────────────────────────────────────────
|
|
292
|
+
const createFormSchema = z.object({
|
|
293
|
+
projectId: projectIdArg,
|
|
294
|
+
name: z.string().min(1).max(255),
|
|
295
|
+
description: z.string().optional(),
|
|
296
|
+
fields: z.array(formField).default([]),
|
|
297
|
+
settings: formSettings.default({}),
|
|
298
|
+
postSubmitActions: z.array(formAction).default([]),
|
|
299
|
+
});
|
|
300
|
+
const updateFormSchema = z.object({
|
|
301
|
+
projectId: projectIdArg,
|
|
302
|
+
formId: z.string().min(1),
|
|
303
|
+
patch: z
|
|
304
|
+
.object({
|
|
305
|
+
name: z.string().min(1).max(255).optional(),
|
|
306
|
+
description: z.string().nullable().optional(),
|
|
307
|
+
fields: z.array(formField).optional(),
|
|
308
|
+
settings: formSettings.optional(),
|
|
309
|
+
postSubmitActions: z.array(formAction).optional(),
|
|
310
|
+
isActive: z.boolean().optional(),
|
|
311
|
+
})
|
|
312
|
+
.describe("Any subset of form fields to update."),
|
|
313
|
+
});
|
|
314
|
+
export function formsTools(client) {
|
|
315
|
+
return [
|
|
316
|
+
defineTool({
|
|
317
|
+
name: "forms_list",
|
|
318
|
+
title: "List forms",
|
|
319
|
+
description: "List every form in the project — id, name, description, field count, action count, isActive, submission count.",
|
|
320
|
+
inputSchema: z.object({ projectId: projectIdArg }),
|
|
321
|
+
handler: async (args) => {
|
|
322
|
+
const projectId = pid(client, args.projectId);
|
|
323
|
+
return client.get(`/api/dashboard/${projectId}/forms`);
|
|
324
|
+
},
|
|
325
|
+
}),
|
|
326
|
+
defineTool({
|
|
327
|
+
name: "forms_get",
|
|
328
|
+
title: "Get a form",
|
|
329
|
+
description: "Fetch a single form with its full definition: fields, settings, post-submit actions.",
|
|
330
|
+
inputSchema: z.object({
|
|
331
|
+
projectId: projectIdArg,
|
|
332
|
+
formId: z.string().min(1),
|
|
333
|
+
}),
|
|
334
|
+
handler: async (args) => {
|
|
335
|
+
const projectId = pid(client, args.projectId);
|
|
336
|
+
return client.get(`/api/dashboard/${projectId}/forms/${encodeURIComponent(args.formId)}`);
|
|
337
|
+
},
|
|
338
|
+
}),
|
|
339
|
+
defineTool({
|
|
340
|
+
name: "forms_create",
|
|
341
|
+
title: "Create a form",
|
|
342
|
+
description: [
|
|
343
|
+
"Create a form. Fields, post-submit actions, and conditional rules are all set up here.",
|
|
344
|
+
"",
|
|
345
|
+
"**Field types**: text, textarea, number, email, phone, date, time, select, multi_select, checkbox, radio, file.",
|
|
346
|
+
"Select-style fields can either inline `options` OR pull `optionsSource` from a form_option_sources row (HTTP or Salesforce-driven).",
|
|
347
|
+
"",
|
|
348
|
+
"**Actions** run on submission. Use `salesforce` to push to a configured provider integration (see integrations_list).",
|
|
349
|
+
"",
|
|
350
|
+
"**Linking to a chat tool**: create the form, then call `forms_link_to_tool` to expose it as a renderable tool inside the assistant.",
|
|
351
|
+
].join("\n"),
|
|
352
|
+
inputSchema: createFormSchema,
|
|
353
|
+
handler: async (args) => {
|
|
354
|
+
const { projectId: rawPid, ...body } = args;
|
|
355
|
+
const projectId = pid(client, rawPid);
|
|
356
|
+
return client.post(`/api/dashboard/${projectId}/forms`, body);
|
|
357
|
+
},
|
|
358
|
+
}),
|
|
359
|
+
defineTool({
|
|
360
|
+
name: "forms_update",
|
|
361
|
+
title: "Update a form",
|
|
362
|
+
description: "Patch any subset of form fields, settings, or actions. Send the full new array for `fields`/`postSubmitActions` — they're not deep-merged.",
|
|
363
|
+
inputSchema: updateFormSchema,
|
|
364
|
+
handler: async (args) => {
|
|
365
|
+
const projectId = pid(client, args.projectId);
|
|
366
|
+
return client.patch(`/api/dashboard/${projectId}/forms/${encodeURIComponent(args.formId)}`, args.patch);
|
|
367
|
+
},
|
|
368
|
+
}),
|
|
369
|
+
defineTool({
|
|
370
|
+
name: "forms_delete",
|
|
371
|
+
title: "Delete a form",
|
|
372
|
+
description: "Delete a form. Destructive. Submissions and post-submit-action history are lost. By default refuses if the form is linked to a tool — pass `force=true` to override.",
|
|
373
|
+
inputSchema: z.object({
|
|
374
|
+
projectId: projectIdArg,
|
|
375
|
+
formId: z.string().min(1),
|
|
376
|
+
force: z.boolean().default(false),
|
|
377
|
+
confirm: z.boolean().default(false),
|
|
378
|
+
}),
|
|
379
|
+
handler: async (args) => {
|
|
380
|
+
if (!args.confirm) {
|
|
381
|
+
return {
|
|
382
|
+
deleted: false,
|
|
383
|
+
reason: "Destructive. Re-invoke with confirm=true.",
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
const projectId = pid(client, args.projectId);
|
|
387
|
+
const path = args.force
|
|
388
|
+
? `/api/dashboard/${projectId}/forms/${encodeURIComponent(args.formId)}?force=true`
|
|
389
|
+
: `/api/dashboard/${projectId}/forms/${encodeURIComponent(args.formId)}`;
|
|
390
|
+
return client.delete(path);
|
|
391
|
+
},
|
|
392
|
+
}),
|
|
393
|
+
defineTool({
|
|
394
|
+
name: "forms_export",
|
|
395
|
+
title: "Export a form bundle",
|
|
396
|
+
description: "Export a self-contained JSON bundle of the form plus every option source it references and every provider integration those sources point at. Secrets (auth headers, integration credentials) are STRIPPED — re-enter them in the target project after import.",
|
|
397
|
+
inputSchema: z.object({
|
|
398
|
+
projectId: projectIdArg,
|
|
399
|
+
formId: z.string().min(1),
|
|
400
|
+
}),
|
|
401
|
+
handler: async (args) => {
|
|
402
|
+
const projectId = pid(client, args.projectId);
|
|
403
|
+
return client.get(`/api/dashboard/${projectId}/forms/${encodeURIComponent(args.formId)}/export`);
|
|
404
|
+
},
|
|
405
|
+
}),
|
|
406
|
+
defineTool({
|
|
407
|
+
name: "forms_import",
|
|
408
|
+
title: "Import a form bundle",
|
|
409
|
+
description: "Import a form bundle (from forms_export) into a project. New IDs are generated for the form, every option source, and every provider integration. Returns warnings for any references that couldn't be remapped (data stores, tool calls, missing integrations) — review before relying on the form.",
|
|
410
|
+
inputSchema: z.object({
|
|
411
|
+
projectId: projectIdArg,
|
|
412
|
+
bundle: z
|
|
413
|
+
.record(z.unknown())
|
|
414
|
+
.describe("Bundle object as returned by forms_export."),
|
|
415
|
+
nameSuffix: z
|
|
416
|
+
.string()
|
|
417
|
+
.optional()
|
|
418
|
+
.describe("Appended to the imported form's name (e.g. ' (imported)') to avoid collisions."),
|
|
419
|
+
}),
|
|
420
|
+
handler: async (args) => {
|
|
421
|
+
const projectId = pid(client, args.projectId);
|
|
422
|
+
return client.post(`/api/dashboard/${projectId}/forms/import`, {
|
|
423
|
+
bundle: args.bundle,
|
|
424
|
+
nameSuffix: args.nameSuffix,
|
|
425
|
+
});
|
|
426
|
+
},
|
|
427
|
+
}),
|
|
428
|
+
defineTool({
|
|
429
|
+
name: "forms_linked_tools",
|
|
430
|
+
title: "List tools linked to a form",
|
|
431
|
+
description: "Find every chat tool whose `formConfig.formId` references this form. Use before deleting to know what would break.",
|
|
432
|
+
inputSchema: z.object({
|
|
433
|
+
projectId: projectIdArg,
|
|
434
|
+
formId: z.string().min(1),
|
|
435
|
+
}),
|
|
436
|
+
handler: async (args) => {
|
|
437
|
+
const projectId = pid(client, args.projectId);
|
|
438
|
+
return client.get(`/api/dashboard/${projectId}/forms/${encodeURIComponent(args.formId)}/linked-tools`);
|
|
439
|
+
},
|
|
440
|
+
}),
|
|
441
|
+
defineTool({
|
|
442
|
+
name: "forms_link_to_tool",
|
|
443
|
+
title: "Wire a form into a chat tool",
|
|
444
|
+
description: [
|
|
445
|
+
"Create a chat tool that renders the form inside the assistant.",
|
|
446
|
+
"Internally this is a `data_store` tool with `displayType: json_render` and a `_formConfig.formId` set to the form. Call this instead of building the tool by hand.",
|
|
447
|
+
"Returns the new tool row.",
|
|
448
|
+
].join("\n"),
|
|
449
|
+
inputSchema: z.object({
|
|
450
|
+
projectId: projectIdArg,
|
|
451
|
+
formId: z.string().min(1),
|
|
452
|
+
toolName: z
|
|
453
|
+
.string()
|
|
454
|
+
.min(1)
|
|
455
|
+
.max(100)
|
|
456
|
+
.describe("camelCase name the AI sees (e.g. `bookDemo`)."),
|
|
457
|
+
displayName: z.string().min(1).max(255),
|
|
458
|
+
description: z
|
|
459
|
+
.string()
|
|
460
|
+
.min(1)
|
|
461
|
+
.describe("How the AI should understand when to surface this form (e.g. 'Use when the user asks to schedule a demo')."),
|
|
462
|
+
prefillFromInput: z
|
|
463
|
+
.record(z.string())
|
|
464
|
+
.optional()
|
|
465
|
+
.describe("Map of {form field key → tool input parameter name}. The AI fills the form with values it has in scope."),
|
|
466
|
+
postSubmitMessage: z
|
|
467
|
+
.string()
|
|
468
|
+
.optional()
|
|
469
|
+
.describe("Free-text shown to the user (and to the AI) right after a successful submission."),
|
|
470
|
+
}),
|
|
471
|
+
handler: async (args) => {
|
|
472
|
+
const projectId = pid(client, args.projectId);
|
|
473
|
+
const formConfig = {
|
|
474
|
+
formId: args.formId,
|
|
475
|
+
...(args.prefillFromInput
|
|
476
|
+
? { prefillFromInput: args.prefillFromInput }
|
|
477
|
+
: {}),
|
|
478
|
+
...(args.postSubmitMessage
|
|
479
|
+
? { postSubmitMessage: args.postSubmitMessage }
|
|
480
|
+
: {}),
|
|
481
|
+
};
|
|
482
|
+
const inputSchema = args.prefillFromInput
|
|
483
|
+
? Array.from(new Set(Object.values(args.prefillFromInput))).map((paramName) => ({
|
|
484
|
+
name: paramName,
|
|
485
|
+
description: `Value to prefill into a form field. Source: tool param ${paramName}.`,
|
|
486
|
+
type: "string",
|
|
487
|
+
required: false,
|
|
488
|
+
}))
|
|
489
|
+
: [];
|
|
490
|
+
return client.post(`/api/dashboard/${projectId}/tools`, {
|
|
491
|
+
name: args.toolName,
|
|
492
|
+
displayName: args.displayName,
|
|
493
|
+
description: args.description,
|
|
494
|
+
toolType: "form",
|
|
495
|
+
inputSchema,
|
|
496
|
+
formConfig,
|
|
497
|
+
stopWhen: 1,
|
|
498
|
+
timeoutMs: 5000,
|
|
499
|
+
category: "forms",
|
|
500
|
+
});
|
|
501
|
+
},
|
|
502
|
+
}),
|
|
503
|
+
defineTool({
|
|
504
|
+
name: "forms_submissions_list",
|
|
505
|
+
title: "List form submissions",
|
|
506
|
+
description: "Paginated list of submissions for a form. Returns decrypted submission data + per-action results.",
|
|
507
|
+
inputSchema: z.object({
|
|
508
|
+
projectId: projectIdArg,
|
|
509
|
+
formId: z.string().min(1),
|
|
510
|
+
page: z.number().int().min(1).default(1),
|
|
511
|
+
pageSize: z.number().int().min(1).max(200).default(50),
|
|
512
|
+
}),
|
|
513
|
+
handler: async (args) => {
|
|
514
|
+
const projectId = pid(client, args.projectId);
|
|
515
|
+
return client.get(`/api/dashboard/${projectId}/forms/${encodeURIComponent(args.formId)}/submissions`, { page: args.page, pageSize: args.pageSize });
|
|
516
|
+
},
|
|
517
|
+
}),
|
|
518
|
+
// ── Action lifecycle: test, retry, skip, replay ──────────────────
|
|
519
|
+
defineTool({
|
|
520
|
+
name: "forms_actions_test_run",
|
|
521
|
+
title: "Test-run a form's post-submit actions",
|
|
522
|
+
description: [
|
|
523
|
+
"Run a form's post-submit actions against sample data and return per-action results, rendered payloads, and the dot-paths discovered from each action's output (used for `{{steps.<name>.<path>}}` chaining).",
|
|
524
|
+
"",
|
|
525
|
+
"⚠️ This executes against LIVE integrations — Salesforce records, webhooks, and emails it produces are REAL, not mocked. Requires `confirm=true`.",
|
|
526
|
+
"",
|
|
527
|
+
"Pass `actions` to test an unsaved draft array (otherwise the form's saved actions run). `sampleData` is a `{ fieldKey: value }` map fed in as the submission. `untilActionId` stops the chain after that action. Discovered paths are persisted onto saved actions automatically.",
|
|
528
|
+
].join("\n"),
|
|
529
|
+
inputSchema: z.object({
|
|
530
|
+
projectId: projectIdArg,
|
|
531
|
+
formId: z.string().min(1),
|
|
532
|
+
actions: z
|
|
533
|
+
.array(formAction)
|
|
534
|
+
.optional()
|
|
535
|
+
.describe("Draft action array to test. Omit to run the form's saved actions."),
|
|
536
|
+
sampleData: z
|
|
537
|
+
.record(z.unknown())
|
|
538
|
+
.optional()
|
|
539
|
+
.describe("Map of { form field key → value } used as the test submission."),
|
|
540
|
+
untilActionId: z
|
|
541
|
+
.string()
|
|
542
|
+
.optional()
|
|
543
|
+
.describe("Stop after running the action with this id."),
|
|
544
|
+
confirm: z.boolean().default(false),
|
|
545
|
+
}),
|
|
546
|
+
handler: async (args) => {
|
|
547
|
+
if (!args.confirm) {
|
|
548
|
+
return {
|
|
549
|
+
ran: false,
|
|
550
|
+
reason: "Runs against LIVE integrations (real Salesforce records / webhooks / emails). Re-invoke with confirm=true.",
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
const projectId = pid(client, args.projectId);
|
|
554
|
+
return client.post(`/api/dashboard/${projectId}/forms/${encodeURIComponent(args.formId)}/actions/test-run`, {
|
|
555
|
+
actions: args.actions,
|
|
556
|
+
sampleData: args.sampleData,
|
|
557
|
+
untilActionId: args.untilActionId,
|
|
558
|
+
});
|
|
559
|
+
},
|
|
560
|
+
}),
|
|
561
|
+
defineTool({
|
|
562
|
+
name: "forms_submission_action_retry",
|
|
563
|
+
title: "Retry one action on a submission",
|
|
564
|
+
description: "Re-run a single failed/skipped action for a specific submission. Returns the updated submission status and action results.",
|
|
565
|
+
inputSchema: z.object({
|
|
566
|
+
projectId: projectIdArg,
|
|
567
|
+
formId: z.string().min(1),
|
|
568
|
+
submissionId: z.string().min(1),
|
|
569
|
+
actionId: z.string().min(1),
|
|
570
|
+
}),
|
|
571
|
+
handler: async (args) => {
|
|
572
|
+
const projectId = pid(client, args.projectId);
|
|
573
|
+
return client.post(`/api/dashboard/${projectId}/forms/${encodeURIComponent(args.formId)}/submissions/${encodeURIComponent(args.submissionId)}/actions/${encodeURIComponent(args.actionId)}/retry`);
|
|
574
|
+
},
|
|
575
|
+
}),
|
|
576
|
+
defineTool({
|
|
577
|
+
name: "forms_submission_action_skip",
|
|
578
|
+
title: "Skip one action on a submission",
|
|
579
|
+
description: "Mark a single action as skipped for a submission so it no longer blocks the submission from being considered processed.",
|
|
580
|
+
inputSchema: z.object({
|
|
581
|
+
projectId: projectIdArg,
|
|
582
|
+
formId: z.string().min(1),
|
|
583
|
+
submissionId: z.string().min(1),
|
|
584
|
+
actionId: z.string().min(1),
|
|
585
|
+
}),
|
|
586
|
+
handler: async (args) => {
|
|
587
|
+
const projectId = pid(client, args.projectId);
|
|
588
|
+
return client.post(`/api/dashboard/${projectId}/forms/${encodeURIComponent(args.formId)}/submissions/${encodeURIComponent(args.submissionId)}/actions/${encodeURIComponent(args.actionId)}/skip`);
|
|
589
|
+
},
|
|
590
|
+
}),
|
|
591
|
+
defineTool({
|
|
592
|
+
name: "forms_submission_replay",
|
|
593
|
+
title: "Replay a submission's actions",
|
|
594
|
+
description: "Re-run a submission's full action chain. Pass `fromActionId` to resume from a specific action instead of the start (earlier successful steps are reused).",
|
|
595
|
+
inputSchema: z.object({
|
|
596
|
+
projectId: projectIdArg,
|
|
597
|
+
formId: z.string().min(1),
|
|
598
|
+
submissionId: z.string().min(1),
|
|
599
|
+
fromActionId: z
|
|
600
|
+
.string()
|
|
601
|
+
.optional()
|
|
602
|
+
.describe("Resume from this action id. Omit to replay the whole chain."),
|
|
603
|
+
}),
|
|
604
|
+
handler: async (args) => {
|
|
605
|
+
const projectId = pid(client, args.projectId);
|
|
606
|
+
return client.post(`/api/dashboard/${projectId}/forms/${encodeURIComponent(args.formId)}/submissions/${encodeURIComponent(args.submissionId)}/replay`, { fromActionId: args.fromActionId ?? null });
|
|
607
|
+
},
|
|
608
|
+
}),
|
|
609
|
+
defineTool({
|
|
610
|
+
name: "forms_submissions_bulk_retry",
|
|
611
|
+
title: "Bulk-retry recent failed submissions",
|
|
612
|
+
description: "Re-queue every submission with failed actions in the last `withinHours`. Defaults to a DRY RUN that only counts candidates — pass `dryRun=false` to actually enqueue the retries.",
|
|
613
|
+
inputSchema: z.object({
|
|
614
|
+
projectId: projectIdArg,
|
|
615
|
+
formId: z.string().min(1),
|
|
616
|
+
withinHours: z.number().int().min(1).max(168).default(24),
|
|
617
|
+
dryRun: z
|
|
618
|
+
.boolean()
|
|
619
|
+
.default(true)
|
|
620
|
+
.describe("True (default) only counts candidates. Set false to enqueue retries."),
|
|
621
|
+
}),
|
|
622
|
+
handler: async (args) => {
|
|
623
|
+
const projectId = pid(client, args.projectId);
|
|
624
|
+
return client.post(`/api/dashboard/${projectId}/forms/${encodeURIComponent(args.formId)}/submissions/bulk-retry`, { withinHours: args.withinHours, dryRun: args.dryRun });
|
|
625
|
+
},
|
|
626
|
+
}),
|
|
627
|
+
// ── Retry queue ──────────────────────────────────────────────────
|
|
628
|
+
defineTool({
|
|
629
|
+
name: "forms_retry_queue_list",
|
|
630
|
+
title: "List the action retry queue",
|
|
631
|
+
description: "List queued action retries across the project, with attempt counts, last error class/message, and next-retry time. Filter by `state`.",
|
|
632
|
+
inputSchema: z.object({
|
|
633
|
+
projectId: projectIdArg,
|
|
634
|
+
state: z
|
|
635
|
+
.string()
|
|
636
|
+
.default("pending,in_progress,exhausted")
|
|
637
|
+
.describe("Comma-separated states: pending, in_progress, succeeded, exhausted, cancelled."),
|
|
638
|
+
limit: z.number().int().min(1).max(200).default(50),
|
|
639
|
+
}),
|
|
640
|
+
handler: async (args) => {
|
|
641
|
+
const projectId = pid(client, args.projectId);
|
|
642
|
+
return client.get(`/api/dashboard/${projectId}/forms/retry-queue`, {
|
|
643
|
+
state: args.state,
|
|
644
|
+
limit: args.limit,
|
|
645
|
+
});
|
|
646
|
+
},
|
|
647
|
+
}),
|
|
648
|
+
defineTool({
|
|
649
|
+
name: "forms_retry_queue_item",
|
|
650
|
+
title: "Act on a retry-queue item",
|
|
651
|
+
description: "Operate on a single retry-queue item: `retry-now` schedules an immediate retry; `cancel` removes it from the queue.",
|
|
652
|
+
inputSchema: z.object({
|
|
653
|
+
projectId: projectIdArg,
|
|
654
|
+
queueId: z.string().min(1),
|
|
655
|
+
op: z.enum(["retry-now", "cancel"]),
|
|
656
|
+
}),
|
|
657
|
+
handler: async (args) => {
|
|
658
|
+
const projectId = pid(client, args.projectId);
|
|
659
|
+
return client.patch(`/api/dashboard/${projectId}/forms/retry-queue/${encodeURIComponent(args.queueId)}`, { op: args.op });
|
|
660
|
+
},
|
|
661
|
+
}),
|
|
662
|
+
// ── Observability & alerting ─────────────────────────────────────
|
|
663
|
+
defineTool({
|
|
664
|
+
name: "forms_observability",
|
|
665
|
+
title: "Forms action health dashboard",
|
|
666
|
+
description: "Project-wide forms health: per-form 24h/7d success rates, needs_manual / needs_retry counts, top failure error codes (7d), retry-queue totals by state, and the recent alert dispatch summary.",
|
|
667
|
+
inputSchema: z.object({ projectId: projectIdArg }),
|
|
668
|
+
handler: async (args) => {
|
|
669
|
+
const projectId = pid(client, args.projectId);
|
|
670
|
+
return client.get(`/api/dashboard/${projectId}/forms/observability`);
|
|
671
|
+
},
|
|
672
|
+
}),
|
|
673
|
+
defineTool({
|
|
674
|
+
name: "forms_alert_rules_list",
|
|
675
|
+
title: "List form alert rules",
|
|
676
|
+
description: "List alert rules. Pass `formId` to scope to one form; omit for project-wide rules too.",
|
|
677
|
+
inputSchema: z.object({
|
|
678
|
+
projectId: projectIdArg,
|
|
679
|
+
formId: z
|
|
680
|
+
.string()
|
|
681
|
+
.optional()
|
|
682
|
+
.describe("Filter to rules scoped to this form."),
|
|
683
|
+
}),
|
|
684
|
+
handler: async (args) => {
|
|
685
|
+
const projectId = pid(client, args.projectId);
|
|
686
|
+
return client.get(`/api/dashboard/${projectId}/forms/alert-rules`, {
|
|
687
|
+
formId: args.formId,
|
|
688
|
+
});
|
|
689
|
+
},
|
|
690
|
+
}),
|
|
691
|
+
defineTool({
|
|
692
|
+
name: "forms_alert_rules_create",
|
|
693
|
+
title: "Create a form alert rule",
|
|
694
|
+
description: [
|
|
695
|
+
"Create an alert rule that fires on form action events. At least one channel is required.",
|
|
696
|
+
"",
|
|
697
|
+
"Events: action_failed, submission_needs_manual, submission_needs_retry, retry_exhausted, rate_drop, critical_action_failed.",
|
|
698
|
+
"Channels: email (list of addresses), webhook ({ url, hmacSecret? }), inApp (boolean). `throttleMs` suppresses duplicates within the window (default 900000 = 15m). Omit `formId` to scope to the whole project.",
|
|
699
|
+
].join("\n"),
|
|
700
|
+
inputSchema: z.object({
|
|
701
|
+
projectId: projectIdArg,
|
|
702
|
+
name: z.string().min(1),
|
|
703
|
+
formId: z.string().nullable().optional(),
|
|
704
|
+
eventType: z.enum([
|
|
705
|
+
"action_failed",
|
|
706
|
+
"submission_needs_manual",
|
|
707
|
+
"submission_needs_retry",
|
|
708
|
+
"retry_exhausted",
|
|
709
|
+
"rate_drop",
|
|
710
|
+
"critical_action_failed",
|
|
711
|
+
]),
|
|
712
|
+
filters: z
|
|
713
|
+
.record(z.unknown())
|
|
714
|
+
.nullable()
|
|
715
|
+
.optional()
|
|
716
|
+
.describe("Optional event-specific match filters."),
|
|
717
|
+
channels: z
|
|
718
|
+
.object({
|
|
719
|
+
email: z.array(z.string()).optional(),
|
|
720
|
+
webhook: z
|
|
721
|
+
.object({ url: z.string().url(), hmacSecret: z.string().optional() })
|
|
722
|
+
.optional(),
|
|
723
|
+
inApp: z.boolean().optional(),
|
|
724
|
+
})
|
|
725
|
+
.describe("At least one of email / webhook / inApp must be set."),
|
|
726
|
+
throttleMs: z.number().int().min(0).optional(),
|
|
727
|
+
isActive: z.boolean().optional(),
|
|
728
|
+
}),
|
|
729
|
+
handler: async (args) => {
|
|
730
|
+
const { projectId: rawPid, ...body } = args;
|
|
731
|
+
const projectId = pid(client, rawPid);
|
|
732
|
+
return client.post(`/api/dashboard/${projectId}/forms/alert-rules`, body);
|
|
733
|
+
},
|
|
734
|
+
}),
|
|
735
|
+
defineTool({
|
|
736
|
+
name: "forms_alert_rules_update",
|
|
737
|
+
title: "Update a form alert rule",
|
|
738
|
+
description: "Patch any subset of an alert rule's fields. `channels` and `filters` replace wholesale (not deep-merged).",
|
|
739
|
+
inputSchema: z.object({
|
|
740
|
+
projectId: projectIdArg,
|
|
741
|
+
ruleId: z.string().min(1),
|
|
742
|
+
patch: z
|
|
743
|
+
.object({
|
|
744
|
+
name: z.string().min(1).optional(),
|
|
745
|
+
formId: z.string().nullable().optional(),
|
|
746
|
+
eventType: z
|
|
747
|
+
.enum([
|
|
748
|
+
"action_failed",
|
|
749
|
+
"submission_needs_manual",
|
|
750
|
+
"submission_needs_retry",
|
|
751
|
+
"retry_exhausted",
|
|
752
|
+
"rate_drop",
|
|
753
|
+
"critical_action_failed",
|
|
754
|
+
])
|
|
755
|
+
.optional(),
|
|
756
|
+
filters: z.record(z.unknown()).nullable().optional(),
|
|
757
|
+
channels: z
|
|
758
|
+
.object({
|
|
759
|
+
email: z.array(z.string()).optional(),
|
|
760
|
+
webhook: z
|
|
761
|
+
.object({
|
|
762
|
+
url: z.string().url(),
|
|
763
|
+
hmacSecret: z.string().optional(),
|
|
764
|
+
})
|
|
765
|
+
.optional(),
|
|
766
|
+
inApp: z.boolean().optional(),
|
|
767
|
+
})
|
|
768
|
+
.optional(),
|
|
769
|
+
throttleMs: z.number().int().min(0).optional(),
|
|
770
|
+
isActive: z.boolean().optional(),
|
|
771
|
+
})
|
|
772
|
+
.describe("Subset of fields to update."),
|
|
773
|
+
}),
|
|
774
|
+
handler: async (args) => {
|
|
775
|
+
const projectId = pid(client, args.projectId);
|
|
776
|
+
return client.patch(`/api/dashboard/${projectId}/forms/alert-rules/${encodeURIComponent(args.ruleId)}`, args.patch);
|
|
777
|
+
},
|
|
778
|
+
}),
|
|
779
|
+
defineTool({
|
|
780
|
+
name: "forms_alert_rules_delete",
|
|
781
|
+
title: "Delete a form alert rule",
|
|
782
|
+
description: "Delete an alert rule. Destructive — requires confirm=true.",
|
|
783
|
+
inputSchema: z.object({
|
|
784
|
+
projectId: projectIdArg,
|
|
785
|
+
ruleId: z.string().min(1),
|
|
786
|
+
confirm: z.boolean().default(false),
|
|
787
|
+
}),
|
|
788
|
+
handler: async (args) => {
|
|
789
|
+
if (!args.confirm) {
|
|
790
|
+
return {
|
|
791
|
+
deleted: false,
|
|
792
|
+
reason: "Destructive. Re-invoke with confirm=true.",
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
const projectId = pid(client, args.projectId);
|
|
796
|
+
return client.delete(`/api/dashboard/${projectId}/forms/alert-rules/${encodeURIComponent(args.ruleId)}`);
|
|
797
|
+
},
|
|
798
|
+
}),
|
|
799
|
+
defineTool({
|
|
800
|
+
name: "forms_alert_rule_test",
|
|
801
|
+
title: "Test-fire an alert rule",
|
|
802
|
+
description: "Send a synthetic alert through a rule's channels, bypassing the throttle and event filter — confirms email/webhook delivery actually works. Returns the dispatch outcome per channel.",
|
|
803
|
+
inputSchema: z.object({
|
|
804
|
+
projectId: projectIdArg,
|
|
805
|
+
ruleId: z.string().min(1),
|
|
806
|
+
}),
|
|
807
|
+
handler: async (args) => {
|
|
808
|
+
const projectId = pid(client, args.projectId);
|
|
809
|
+
return client.post(`/api/dashboard/${projectId}/forms/alert-rules/${encodeURIComponent(args.ruleId)}/test`);
|
|
810
|
+
},
|
|
811
|
+
}),
|
|
812
|
+
defineTool({
|
|
813
|
+
name: "forms_alert_rules_dispatch_log",
|
|
814
|
+
title: "Alert dispatch log",
|
|
815
|
+
description: "Recent alert dispatch attempts (delivered, throttled, failed). Filter by `ruleId` and look-back window.",
|
|
816
|
+
inputSchema: z.object({
|
|
817
|
+
projectId: projectIdArg,
|
|
818
|
+
ruleId: z.string().optional(),
|
|
819
|
+
sinceHours: z.number().int().min(1).default(24),
|
|
820
|
+
limit: z.number().int().min(1).max(200).default(50),
|
|
821
|
+
}),
|
|
822
|
+
handler: async (args) => {
|
|
823
|
+
const projectId = pid(client, args.projectId);
|
|
824
|
+
return client.get(`/api/dashboard/${projectId}/forms/alert-rules/dispatch-log`, {
|
|
825
|
+
ruleId: args.ruleId,
|
|
826
|
+
sinceHours: args.sinceHours,
|
|
827
|
+
limit: args.limit,
|
|
828
|
+
});
|
|
829
|
+
},
|
|
830
|
+
}),
|
|
831
|
+
];
|
|
832
|
+
}
|
|
833
|
+
//# sourceMappingURL=forms.js.map
|