neuron-inspector 0.8.0 → 1.0.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/README.md +345 -42
- package/dist/automations/executor.d.ts +21 -0
- package/dist/automations/executor.js +835 -0
- package/dist/automations/executor.js.map +1 -0
- package/dist/automations/index.d.ts +7 -0
- package/dist/automations/index.js +21 -0
- package/dist/automations/index.js.map +1 -0
- package/dist/automations/store.d.ts +15 -0
- package/dist/automations/store.js +193 -0
- package/dist/automations/store.js.map +1 -0
- package/dist/automations/tools.d.ts +14 -0
- package/dist/automations/tools.js +735 -0
- package/dist/automations/tools.js.map +1 -0
- package/dist/automations/types.d.ts +211 -0
- package/dist/automations/types.js +4 -0
- package/dist/automations/types.js.map +1 -0
- package/dist/business-tools.d.ts +17 -0
- package/dist/business-tools.js +3065 -0
- package/dist/business-tools.js.map +1 -0
- package/dist/collections/__tests__/assertions.test.js +203 -0
- package/dist/collections/__tests__/assertions.test.js.map +1 -0
- package/dist/collections/__tests__/dynamic-vars.test.d.ts +1 -0
- package/dist/collections/__tests__/dynamic-vars.test.js +169 -0
- package/dist/collections/__tests__/dynamic-vars.test.js.map +1 -0
- package/dist/collections/__tests__/execution.test.d.ts +1 -0
- package/dist/collections/__tests__/execution.test.js +271 -0
- package/dist/collections/__tests__/execution.test.js.map +1 -0
- package/dist/collections/__tests__/integration.test.d.ts +1 -0
- package/dist/collections/__tests__/integration.test.js +478 -0
- package/dist/collections/__tests__/integration.test.js.map +1 -0
- package/dist/collections/__tests__/resolver.test.d.ts +1 -0
- package/dist/collections/__tests__/resolver.test.js +282 -0
- package/dist/collections/__tests__/resolver.test.js.map +1 -0
- package/dist/collections/__tests__/scenario.test.d.ts +1 -0
- package/dist/collections/__tests__/scenario.test.js +367 -0
- package/dist/collections/__tests__/scenario.test.js.map +1 -0
- package/dist/collections/__tests__/scripts.test.d.ts +1 -0
- package/dist/collections/__tests__/scripts.test.js +245 -0
- package/dist/collections/__tests__/scripts.test.js.map +1 -0
- package/dist/collections/__tests__/store.test.d.ts +1 -0
- package/dist/collections/__tests__/store.test.js +313 -0
- package/dist/collections/__tests__/store.test.js.map +1 -0
- package/dist/collections/assertions.d.ts +16 -0
- package/dist/collections/assertions.js +107 -0
- package/dist/collections/assertions.js.map +1 -0
- package/dist/collections/dynamic-vars.d.ts +75 -0
- package/dist/collections/dynamic-vars.js +187 -0
- package/dist/collections/dynamic-vars.js.map +1 -0
- package/dist/collections/executor.d.ts +100 -0
- package/dist/collections/executor.js +301 -0
- package/dist/collections/executor.js.map +1 -0
- package/dist/collections/http.d.ts +9 -0
- package/dist/collections/http.js +194 -0
- package/dist/collections/http.js.map +1 -0
- package/dist/collections/index.d.ts +12 -0
- package/dist/collections/index.js +27 -0
- package/dist/collections/index.js.map +1 -0
- package/dist/collections/postman.d.ts +10 -0
- package/dist/collections/postman.js +288 -0
- package/dist/collections/postman.js.map +1 -0
- package/dist/collections/resolver.d.ts +74 -0
- package/dist/collections/resolver.js +330 -0
- package/dist/collections/resolver.js.map +1 -0
- package/dist/collections/scripts.d.ts +20 -0
- package/dist/collections/scripts.js +225 -0
- package/dist/collections/scripts.js.map +1 -0
- package/dist/collections/store.d.ts +68 -0
- package/dist/collections/store.js +507 -0
- package/dist/collections/store.js.map +1 -0
- package/dist/collections/tools.d.ts +8 -0
- package/dist/collections/tools.js +1276 -0
- package/dist/collections/tools.js.map +1 -0
- package/dist/collections/types.d.ts +226 -0
- package/dist/collections/types.js +3 -0
- package/dist/collections/types.js.map +1 -0
- package/dist/compound-tools.js +136 -2
- package/dist/compound-tools.js.map +1 -1
- package/dist/killswitch.d.ts +8 -0
- package/dist/killswitch.js +304 -0
- package/dist/killswitch.js.map +1 -0
- package/dist/monitor.d.ts +36 -2
- package/dist/monitor.js +213 -38
- package/dist/monitor.js.map +1 -1
- package/dist/paths.d.ts +16 -0
- package/dist/paths.js +39 -0
- package/dist/paths.js.map +1 -0
- package/dist/recipe-tools.js +67 -9
- package/dist/recipe-tools.js.map +1 -1
- package/dist/recipes.d.ts +6 -0
- package/dist/recipes.js +83 -8
- package/dist/recipes.js.map +1 -1
- package/dist/resilience.d.ts +112 -0
- package/dist/resilience.js +319 -0
- package/dist/resilience.js.map +1 -0
- package/dist/scheduler.js +4 -8
- package/dist/scheduler.js.map +1 -1
- package/dist/server.js +221 -60
- package/dist/server.js.map +1 -1
- package/dist/session-state.js +2 -2
- package/dist/session-state.js.map +1 -1
- package/dist/tools.js +54 -0
- package/dist/tools.js.map +1 -1
- package/package.json +4 -3
- package/recipes/autopilot/agent.md +316 -0
- package/recipes/autopilot/learnings.md +33 -0
- package/recipes/autopilot/recipe.yaml +96 -0
- package/recipes/inbox-responder/agent.md +268 -0
- package/recipes/inbox-responder/learnings.md +25 -0
- package/recipes/inbox-responder/recipe.yaml +82 -0
- package/recipes/linkedin-job-scraper/agent.md +144 -0
- package/recipes/linkedin-job-scraper/learnings.md +3 -0
- package/recipes/linkedin-job-scraper/recipe.yaml +71 -0
- package/recipes/linkedin-outreach/learnings.md +17 -1
- package/recipes/planner/agent.md +66 -1
- package/recipes/planner/learnings.md +49 -1
- package/recipes/sheets-data-entry/agent.md +166 -0
- package/recipes/sheets-data-entry/learnings.md +3 -0
- package/recipes/sheets-data-entry/recipe.yaml +78 -0
- package/recipes/social-ops/learnings.md +33 -13
- package/recipes/x-job-scraper/agent.md +156 -0
- package/recipes/x-job-scraper/learnings.md +3 -0
- package/recipes/x-job-scraper/recipe.yaml +72 -0
- package/dist/__tests__/correlation.test.js +0 -53
- package/dist/__tests__/correlation.test.js.map +0 -1
- /package/dist/{__tests__/correlation.test.d.ts → collections/__tests__/assertions.test.d.ts} +0 -0
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
import { slugify } from "./store.js";
|
|
2
|
+
// ── Tool Definitions ────────────────────────────────────────────────
|
|
3
|
+
export function buildAutomationTools() {
|
|
4
|
+
return [
|
|
5
|
+
{
|
|
6
|
+
name: "neuron_auto_list",
|
|
7
|
+
description: "List all automation suites with their step counts, base URL, and setup/teardown status.",
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {},
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: "neuron_auto_get",
|
|
15
|
+
description: "Get the full structure of an automation suite including all steps, setup, teardown, variables, and defaults.",
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {
|
|
19
|
+
suite: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "Suite id (filename stem, e.g. 'onboarding-flow')",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
required: ["suite"],
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "neuron_auto_create",
|
|
29
|
+
description: "Create a new automation suite with optional initial configuration. Returns error if a suite with the same slug already exists.",
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: {
|
|
33
|
+
name: {
|
|
34
|
+
type: "string",
|
|
35
|
+
description: "Human-readable suite name (e.g. 'User Onboarding Flow')",
|
|
36
|
+
},
|
|
37
|
+
description: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Optional description of what this suite automates",
|
|
40
|
+
},
|
|
41
|
+
base_url: {
|
|
42
|
+
type: "string",
|
|
43
|
+
description: "Optional base URL for all navigation steps",
|
|
44
|
+
},
|
|
45
|
+
variables: {
|
|
46
|
+
type: "object",
|
|
47
|
+
description: "Optional variables available to all steps (key-value pairs)",
|
|
48
|
+
},
|
|
49
|
+
defaults: {
|
|
50
|
+
type: "object",
|
|
51
|
+
description: "Optional default settings for all steps",
|
|
52
|
+
properties: {
|
|
53
|
+
timeout_ms: { type: "number" },
|
|
54
|
+
on_error: { type: "string", enum: ["stop", "continue", "retry"] },
|
|
55
|
+
wait_between_ms: { type: "number" },
|
|
56
|
+
viewport: {
|
|
57
|
+
type: "object",
|
|
58
|
+
properties: {
|
|
59
|
+
width: { type: "number" },
|
|
60
|
+
height: { type: "number" },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
enabled: {
|
|
66
|
+
type: "boolean",
|
|
67
|
+
description: "Whether the suite is enabled (default: true). Disabled suites cannot be run.",
|
|
68
|
+
},
|
|
69
|
+
setup_recipe: {
|
|
70
|
+
type: "object",
|
|
71
|
+
description: "Recipe to run before setup steps (AI-driven goal-based automation)",
|
|
72
|
+
properties: {
|
|
73
|
+
slug: { type: "string", description: "Recipe slug (e.g. 'login-flow')" },
|
|
74
|
+
variables: { type: "object", description: "Variable overrides for the recipe" },
|
|
75
|
+
},
|
|
76
|
+
required: ["slug"],
|
|
77
|
+
},
|
|
78
|
+
teardown_recipe: {
|
|
79
|
+
type: "object",
|
|
80
|
+
description: "Recipe to run after teardown steps (AI-driven cleanup)",
|
|
81
|
+
properties: {
|
|
82
|
+
slug: { type: "string", description: "Recipe slug (e.g. 'cleanup')" },
|
|
83
|
+
variables: { type: "object", description: "Variable overrides for the recipe" },
|
|
84
|
+
},
|
|
85
|
+
required: ["slug"],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
required: ["name"],
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "neuron_auto_update",
|
|
93
|
+
description: "Update a suite's configuration, steps, setup, or teardown. Pass null to clear a field. Omit fields to leave them unchanged.",
|
|
94
|
+
inputSchema: {
|
|
95
|
+
type: "object",
|
|
96
|
+
properties: {
|
|
97
|
+
suite: { type: "string", description: "Suite id" },
|
|
98
|
+
name: { type: "string", description: "New suite name" },
|
|
99
|
+
enabled: { type: "boolean", description: "Enable or disable the suite" },
|
|
100
|
+
description: { type: "string", description: "New description (empty string to clear)" },
|
|
101
|
+
base_url: { type: "string", description: "New base URL (empty string to clear)" },
|
|
102
|
+
variables: { type: "object", description: "New variables (replaces existing)" },
|
|
103
|
+
defaults: {
|
|
104
|
+
type: "object",
|
|
105
|
+
description: "New defaults (merges with existing). Pass null to clear.",
|
|
106
|
+
properties: {
|
|
107
|
+
timeout_ms: { type: "number" },
|
|
108
|
+
on_error: { type: "string", enum: ["stop", "continue", "retry"] },
|
|
109
|
+
wait_between_ms: { type: "number" },
|
|
110
|
+
viewport: {
|
|
111
|
+
type: "object",
|
|
112
|
+
properties: {
|
|
113
|
+
width: { type: "number" },
|
|
114
|
+
height: { type: "number" },
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
steps: {
|
|
120
|
+
type: "array",
|
|
121
|
+
description: "New steps array (replaces existing). Pass empty array to clear.",
|
|
122
|
+
items: { type: "object" },
|
|
123
|
+
},
|
|
124
|
+
setup: {
|
|
125
|
+
type: "array",
|
|
126
|
+
description: "New setup steps (replaces existing). Pass empty array to clear.",
|
|
127
|
+
items: { type: "object" },
|
|
128
|
+
},
|
|
129
|
+
teardown: {
|
|
130
|
+
type: "array",
|
|
131
|
+
description: "New teardown steps (replaces existing). Pass empty array to clear.",
|
|
132
|
+
items: { type: "object" },
|
|
133
|
+
},
|
|
134
|
+
setup_recipe: {
|
|
135
|
+
type: ["object", "null"],
|
|
136
|
+
description: "Recipe to run before setup steps. Pass null to clear.",
|
|
137
|
+
properties: {
|
|
138
|
+
slug: { type: "string", description: "Recipe slug" },
|
|
139
|
+
variables: { type: "object", description: "Variable overrides" },
|
|
140
|
+
},
|
|
141
|
+
required: ["slug"],
|
|
142
|
+
},
|
|
143
|
+
teardown_recipe: {
|
|
144
|
+
type: ["object", "null"],
|
|
145
|
+
description: "Recipe to run after teardown steps. Pass null to clear.",
|
|
146
|
+
properties: {
|
|
147
|
+
slug: { type: "string", description: "Recipe slug" },
|
|
148
|
+
variables: { type: "object", description: "Variable overrides" },
|
|
149
|
+
},
|
|
150
|
+
required: ["slug"],
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
required: ["suite"],
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: "neuron_auto_delete",
|
|
158
|
+
description: "Delete an automation suite file.",
|
|
159
|
+
inputSchema: {
|
|
160
|
+
type: "object",
|
|
161
|
+
properties: {
|
|
162
|
+
suite: { type: "string", description: "Suite id to delete" },
|
|
163
|
+
},
|
|
164
|
+
required: ["suite"],
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: "neuron_auto_run",
|
|
169
|
+
description: "Run a full automation suite including setup, all steps, and teardown. " +
|
|
170
|
+
"Requires active browser connection. Returns step-by-step results with assertions, extracted variables, timing, and screenshots.",
|
|
171
|
+
inputSchema: {
|
|
172
|
+
type: "object",
|
|
173
|
+
properties: {
|
|
174
|
+
suite: { type: "string", description: "Suite id to run" },
|
|
175
|
+
variables: {
|
|
176
|
+
type: "object",
|
|
177
|
+
description: "Optional runtime variable overrides (merged with suite variables)",
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
required: ["suite"],
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "neuron_auto_run_step",
|
|
185
|
+
description: "Run a single step from a suite for testing and debugging purposes. " +
|
|
186
|
+
"Requires active browser connection. Does not run setup/teardown. Returns step result with assertions and extracted data.",
|
|
187
|
+
inputSchema: {
|
|
188
|
+
type: "object",
|
|
189
|
+
properties: {
|
|
190
|
+
suite: { type: "string", description: "Suite id" },
|
|
191
|
+
step_id: { type: "string", description: "Step id to run" },
|
|
192
|
+
variables: {
|
|
193
|
+
type: "object",
|
|
194
|
+
description: "Optional runtime variables available to this step",
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
required: ["suite", "step_id"],
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: "neuron_auto_add_step",
|
|
202
|
+
description: "Add a new step to a suite. If index is provided, inserts at that position; otherwise appends to the end.",
|
|
203
|
+
inputSchema: {
|
|
204
|
+
type: "object",
|
|
205
|
+
properties: {
|
|
206
|
+
suite: { type: "string", description: "Suite id" },
|
|
207
|
+
step: {
|
|
208
|
+
type: "object",
|
|
209
|
+
description: "Step object with id, action, config, and optional assertions/extract/retry/timeout",
|
|
210
|
+
properties: {
|
|
211
|
+
id: { type: "string" },
|
|
212
|
+
name: { type: "string" },
|
|
213
|
+
action: { type: "string" },
|
|
214
|
+
config: { type: "object" },
|
|
215
|
+
assertions: { type: "array" },
|
|
216
|
+
extract: { type: "object" },
|
|
217
|
+
on_error: { type: "string", enum: ["stop", "continue", "retry"] },
|
|
218
|
+
retry: {
|
|
219
|
+
type: "object",
|
|
220
|
+
properties: {
|
|
221
|
+
max: { type: "number" },
|
|
222
|
+
delay_ms: { type: "number" },
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
wait_before_ms: { type: "number" },
|
|
226
|
+
wait_after_ms: { type: "number" },
|
|
227
|
+
timeout_ms: { type: "number" },
|
|
228
|
+
disabled: { type: "boolean" },
|
|
229
|
+
},
|
|
230
|
+
required: ["id", "action", "config"],
|
|
231
|
+
},
|
|
232
|
+
index: {
|
|
233
|
+
type: "number",
|
|
234
|
+
description: "Optional index to insert at. Omit to append.",
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
required: ["suite", "step"],
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: "neuron_auto_remove_step",
|
|
242
|
+
description: "Remove a step from a suite by its step_id.",
|
|
243
|
+
inputSchema: {
|
|
244
|
+
type: "object",
|
|
245
|
+
properties: {
|
|
246
|
+
suite: { type: "string", description: "Suite id" },
|
|
247
|
+
step_id: { type: "string", description: "Step id to remove" },
|
|
248
|
+
},
|
|
249
|
+
required: ["suite", "step_id"],
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
name: "neuron_auto_reorder",
|
|
254
|
+
description: "Reorder steps in a suite by providing a new ordered array of step IDs. " +
|
|
255
|
+
"All existing step IDs must be included.",
|
|
256
|
+
inputSchema: {
|
|
257
|
+
type: "object",
|
|
258
|
+
properties: {
|
|
259
|
+
suite: { type: "string", description: "Suite id" },
|
|
260
|
+
step_ids: {
|
|
261
|
+
type: "array",
|
|
262
|
+
items: { type: "string" },
|
|
263
|
+
description: "Ordered array of all step IDs in the new order",
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
required: ["suite", "step_ids"],
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "neuron_auto_import",
|
|
271
|
+
description: "Import an automation suite from a simple JSON format. " +
|
|
272
|
+
"Accepts { name, description?, base_url?, steps: [{ action, config, ... }] }. " +
|
|
273
|
+
"Converts to Neuron suite format and saves. Step IDs are auto-generated if missing.",
|
|
274
|
+
inputSchema: {
|
|
275
|
+
type: "object",
|
|
276
|
+
properties: {
|
|
277
|
+
format: {
|
|
278
|
+
type: "string",
|
|
279
|
+
enum: ["simple"],
|
|
280
|
+
description: "Import format. Currently only 'simple' is supported.",
|
|
281
|
+
},
|
|
282
|
+
data: {
|
|
283
|
+
type: "object",
|
|
284
|
+
description: "Suite data with name, optional description/base_url, and steps array",
|
|
285
|
+
properties: {
|
|
286
|
+
name: { type: "string" },
|
|
287
|
+
description: { type: "string" },
|
|
288
|
+
base_url: { type: "string" },
|
|
289
|
+
steps: { type: "array", items: { type: "object" } },
|
|
290
|
+
},
|
|
291
|
+
required: ["name", "steps"],
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
required: ["format", "data"],
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
name: "neuron_auto_export",
|
|
299
|
+
description: "Export a suite as a Playwright-style JavaScript test script. " +
|
|
300
|
+
"Returns executable code using @playwright/test conventions. " +
|
|
301
|
+
"Useful for migrating to Playwright or generating test templates.",
|
|
302
|
+
inputSchema: {
|
|
303
|
+
type: "object",
|
|
304
|
+
properties: {
|
|
305
|
+
suite: { type: "string", description: "Suite id to export" },
|
|
306
|
+
},
|
|
307
|
+
required: ["suite"],
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
];
|
|
311
|
+
}
|
|
312
|
+
// ── Handler ─────────────────────────────────────────────────────────
|
|
313
|
+
export function buildAutomationHandler(store, executeSuite, executeStep) {
|
|
314
|
+
return async (name, args, ctx) => {
|
|
315
|
+
switch (name) {
|
|
316
|
+
case "neuron_auto_list": {
|
|
317
|
+
return store.list();
|
|
318
|
+
}
|
|
319
|
+
case "neuron_auto_get": {
|
|
320
|
+
const id = args.suite;
|
|
321
|
+
const result = await store.get(id);
|
|
322
|
+
if (!result)
|
|
323
|
+
return { error: `Suite '${id}' not found` };
|
|
324
|
+
return result.suite;
|
|
325
|
+
}
|
|
326
|
+
case "neuron_auto_create": {
|
|
327
|
+
const suiteName = args.name;
|
|
328
|
+
const id = slugify(suiteName);
|
|
329
|
+
if (!id)
|
|
330
|
+
return { error: "Name produces an empty slug" };
|
|
331
|
+
// Check if suite already exists
|
|
332
|
+
const existing = await store.get(id);
|
|
333
|
+
if (existing) {
|
|
334
|
+
return {
|
|
335
|
+
error: `Suite '${id}' already exists at ${existing.location}`,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
// Build initial suite
|
|
339
|
+
const suite = {
|
|
340
|
+
name: suiteName,
|
|
341
|
+
steps: [],
|
|
342
|
+
};
|
|
343
|
+
if (args.enabled !== undefined)
|
|
344
|
+
suite.enabled = args.enabled;
|
|
345
|
+
if (args.description)
|
|
346
|
+
suite.description = args.description;
|
|
347
|
+
if (args.base_url)
|
|
348
|
+
suite.base_url = args.base_url;
|
|
349
|
+
if (args.variables)
|
|
350
|
+
suite.variables = args.variables;
|
|
351
|
+
if (args.defaults)
|
|
352
|
+
suite.defaults = args.defaults;
|
|
353
|
+
if (args.setup_recipe)
|
|
354
|
+
suite.setup_recipe = args.setup_recipe;
|
|
355
|
+
if (args.teardown_recipe)
|
|
356
|
+
suite.teardown_recipe = args.teardown_recipe;
|
|
357
|
+
await store.save(id, suite);
|
|
358
|
+
return { id, name: suiteName, created: true };
|
|
359
|
+
}
|
|
360
|
+
case "neuron_auto_update": {
|
|
361
|
+
const id = args.suite;
|
|
362
|
+
const result = await store.get(id);
|
|
363
|
+
if (!result)
|
|
364
|
+
return { error: `Suite '${id}' not found` };
|
|
365
|
+
const suite = { ...result.suite };
|
|
366
|
+
const updates = [];
|
|
367
|
+
if (args.name !== undefined) {
|
|
368
|
+
suite.name = args.name;
|
|
369
|
+
updates.push("name");
|
|
370
|
+
}
|
|
371
|
+
if (args.enabled !== undefined) {
|
|
372
|
+
suite.enabled = args.enabled;
|
|
373
|
+
updates.push("enabled");
|
|
374
|
+
}
|
|
375
|
+
if (args.description !== undefined) {
|
|
376
|
+
suite.description =
|
|
377
|
+
args.description || undefined;
|
|
378
|
+
updates.push("description");
|
|
379
|
+
}
|
|
380
|
+
if (args.base_url !== undefined) {
|
|
381
|
+
suite.base_url = args.base_url || undefined;
|
|
382
|
+
updates.push("base_url");
|
|
383
|
+
}
|
|
384
|
+
if (args.variables !== undefined) {
|
|
385
|
+
suite.variables = args.variables;
|
|
386
|
+
updates.push("variables");
|
|
387
|
+
}
|
|
388
|
+
if (args.defaults !== undefined) {
|
|
389
|
+
suite.defaults =
|
|
390
|
+
args.defaults === null
|
|
391
|
+
? undefined
|
|
392
|
+
: args.defaults;
|
|
393
|
+
updates.push("defaults");
|
|
394
|
+
}
|
|
395
|
+
if (args.steps !== undefined) {
|
|
396
|
+
suite.steps = args.steps ?? [];
|
|
397
|
+
updates.push("steps");
|
|
398
|
+
}
|
|
399
|
+
if (args.setup !== undefined) {
|
|
400
|
+
suite.setup = args.setup ?? undefined;
|
|
401
|
+
updates.push("setup");
|
|
402
|
+
}
|
|
403
|
+
if (args.teardown !== undefined) {
|
|
404
|
+
suite.teardown = args.teardown ?? undefined;
|
|
405
|
+
updates.push("teardown");
|
|
406
|
+
}
|
|
407
|
+
if (args.setup_recipe !== undefined) {
|
|
408
|
+
suite.setup_recipe =
|
|
409
|
+
args.setup_recipe === null
|
|
410
|
+
? undefined
|
|
411
|
+
: args.setup_recipe;
|
|
412
|
+
updates.push("setup_recipe");
|
|
413
|
+
}
|
|
414
|
+
if (args.teardown_recipe !== undefined) {
|
|
415
|
+
suite.teardown_recipe =
|
|
416
|
+
args.teardown_recipe === null
|
|
417
|
+
? undefined
|
|
418
|
+
: args.teardown_recipe;
|
|
419
|
+
updates.push("teardown_recipe");
|
|
420
|
+
}
|
|
421
|
+
if (updates.length === 0) {
|
|
422
|
+
return { error: "No updates provided" };
|
|
423
|
+
}
|
|
424
|
+
await store.save(id, suite);
|
|
425
|
+
return { updated: true, suite: id, fields: updates };
|
|
426
|
+
}
|
|
427
|
+
case "neuron_auto_delete": {
|
|
428
|
+
const id = args.suite;
|
|
429
|
+
const deleted = await store.delete(id);
|
|
430
|
+
if (!deleted)
|
|
431
|
+
return { error: `Suite '${id}' not found` };
|
|
432
|
+
return { deleted: true, id };
|
|
433
|
+
}
|
|
434
|
+
case "neuron_auto_run": {
|
|
435
|
+
if (!executeSuite) {
|
|
436
|
+
return { error: "Suite execution not configured" };
|
|
437
|
+
}
|
|
438
|
+
if (!ctx) {
|
|
439
|
+
return {
|
|
440
|
+
error: "Browser connection required. Ensure an active browser session is connected.",
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
const id = args.suite;
|
|
444
|
+
const result = await store.get(id);
|
|
445
|
+
if (!result)
|
|
446
|
+
return { error: `Suite '${id}' not found` };
|
|
447
|
+
if (result.suite.enabled === false) {
|
|
448
|
+
return { error: "Suite is disabled" };
|
|
449
|
+
}
|
|
450
|
+
const variables = args.variables;
|
|
451
|
+
return executeSuite(result.suite, ctx, variables);
|
|
452
|
+
}
|
|
453
|
+
case "neuron_auto_run_step": {
|
|
454
|
+
if (!executeStep) {
|
|
455
|
+
return { error: "Step execution not configured" };
|
|
456
|
+
}
|
|
457
|
+
if (!ctx) {
|
|
458
|
+
return {
|
|
459
|
+
error: "Browser connection required. Ensure an active browser session is connected.",
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
const id = args.suite;
|
|
463
|
+
const stepId = args.step_id;
|
|
464
|
+
const result = await store.get(id);
|
|
465
|
+
if (!result)
|
|
466
|
+
return { error: `Suite '${id}' not found` };
|
|
467
|
+
// Find the step
|
|
468
|
+
const allSteps = [
|
|
469
|
+
...(result.suite.setup ?? []),
|
|
470
|
+
...result.suite.steps,
|
|
471
|
+
...(result.suite.teardown ?? []),
|
|
472
|
+
];
|
|
473
|
+
const step = allSteps.find((s) => s.id === stepId);
|
|
474
|
+
if (!step) {
|
|
475
|
+
return { error: `Step '${stepId}' not found in suite '${id}'` };
|
|
476
|
+
}
|
|
477
|
+
const variables = args.variables;
|
|
478
|
+
return executeStep(step, ctx, variables);
|
|
479
|
+
}
|
|
480
|
+
case "neuron_auto_add_step": {
|
|
481
|
+
const id = args.suite;
|
|
482
|
+
const result = await store.get(id);
|
|
483
|
+
if (!result)
|
|
484
|
+
return { error: `Suite '${id}' not found` };
|
|
485
|
+
const suite = { ...result.suite };
|
|
486
|
+
const newStep = args.step;
|
|
487
|
+
const index = args.index;
|
|
488
|
+
// Validate step has required fields
|
|
489
|
+
if (!newStep.id || !newStep.action || !newStep.config) {
|
|
490
|
+
return {
|
|
491
|
+
error: "Step must have id, action, and config fields",
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
// Check for duplicate step ID
|
|
495
|
+
if (suite.steps.some((s) => s.id === newStep.id)) {
|
|
496
|
+
return {
|
|
497
|
+
error: `Step with id '${newStep.id}' already exists in suite`,
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
// Insert or append
|
|
501
|
+
if (index !== undefined) {
|
|
502
|
+
suite.steps.splice(index, 0, newStep);
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
suite.steps.push(newStep);
|
|
506
|
+
}
|
|
507
|
+
await store.save(id, suite);
|
|
508
|
+
return {
|
|
509
|
+
added: true,
|
|
510
|
+
suite: id,
|
|
511
|
+
step_id: newStep.id,
|
|
512
|
+
index: index ?? suite.steps.length - 1,
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
case "neuron_auto_remove_step": {
|
|
516
|
+
const id = args.suite;
|
|
517
|
+
const stepId = args.step_id;
|
|
518
|
+
const result = await store.get(id);
|
|
519
|
+
if (!result)
|
|
520
|
+
return { error: `Suite '${id}' not found` };
|
|
521
|
+
const suite = { ...result.suite };
|
|
522
|
+
const initialLength = suite.steps.length;
|
|
523
|
+
suite.steps = suite.steps.filter((s) => s.id !== stepId);
|
|
524
|
+
if (suite.steps.length === initialLength) {
|
|
525
|
+
return { error: `Step '${stepId}' not found in suite '${id}'` };
|
|
526
|
+
}
|
|
527
|
+
await store.save(id, suite);
|
|
528
|
+
return { removed: true, suite: id, step_id: stepId };
|
|
529
|
+
}
|
|
530
|
+
case "neuron_auto_reorder": {
|
|
531
|
+
const id = args.suite;
|
|
532
|
+
const newOrder = args.step_ids;
|
|
533
|
+
const result = await store.get(id);
|
|
534
|
+
if (!result)
|
|
535
|
+
return { error: `Suite '${id}' not found` };
|
|
536
|
+
const suite = { ...result.suite };
|
|
537
|
+
const currentSteps = suite.steps;
|
|
538
|
+
// Validate that all step IDs are present
|
|
539
|
+
if (newOrder.length !== currentSteps.length) {
|
|
540
|
+
return {
|
|
541
|
+
error: `step_ids length (${newOrder.length}) does not match current steps count (${currentSteps.length})`,
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
const currentIds = new Set(currentSteps.map((s) => s.id));
|
|
545
|
+
for (const id of newOrder) {
|
|
546
|
+
if (!currentIds.has(id)) {
|
|
547
|
+
return {
|
|
548
|
+
error: `Step '${id}' not found in current steps`,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
// Reorder steps
|
|
553
|
+
const stepMap = new Map(currentSteps.map((s) => [s.id, s]));
|
|
554
|
+
suite.steps = newOrder.map((id) => stepMap.get(id));
|
|
555
|
+
await store.save(id, suite);
|
|
556
|
+
return { reordered: true, suite: id, new_order: newOrder };
|
|
557
|
+
}
|
|
558
|
+
case "neuron_auto_import": {
|
|
559
|
+
const format = args.format;
|
|
560
|
+
if (format !== "simple") {
|
|
561
|
+
return { error: `Unsupported import format: ${format}` };
|
|
562
|
+
}
|
|
563
|
+
const data = args.data;
|
|
564
|
+
// Validate input
|
|
565
|
+
if (!data.name || !Array.isArray(data.steps)) {
|
|
566
|
+
return {
|
|
567
|
+
error: "Import data must have 'name' string and 'steps' array",
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
// Build suite from imported data
|
|
571
|
+
const suite = {
|
|
572
|
+
name: data.name,
|
|
573
|
+
steps: data.steps.map((s, i) => ({
|
|
574
|
+
id: s.id ?? `step-${i + 1}`,
|
|
575
|
+
name: s.name,
|
|
576
|
+
action: s.action ?? "evaluate",
|
|
577
|
+
config: s.config ?? {},
|
|
578
|
+
assertions: s.assertions,
|
|
579
|
+
extract: s.extract,
|
|
580
|
+
on_error: s.on_error,
|
|
581
|
+
retry: s.retry,
|
|
582
|
+
wait_before_ms: s.wait_before_ms,
|
|
583
|
+
wait_after_ms: s.wait_after_ms,
|
|
584
|
+
timeout_ms: s.timeout_ms,
|
|
585
|
+
disabled: s.disabled,
|
|
586
|
+
})),
|
|
587
|
+
};
|
|
588
|
+
if (data.description)
|
|
589
|
+
suite.description = data.description;
|
|
590
|
+
if (data.base_url)
|
|
591
|
+
suite.base_url = data.base_url;
|
|
592
|
+
const id = slugify(data.name);
|
|
593
|
+
if (!id)
|
|
594
|
+
return { error: "Name produces an empty slug" };
|
|
595
|
+
await store.save(id, suite);
|
|
596
|
+
return {
|
|
597
|
+
imported: true,
|
|
598
|
+
id,
|
|
599
|
+
name: suite.name,
|
|
600
|
+
step_count: suite.steps.length,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
case "neuron_auto_export": {
|
|
604
|
+
const id = args.suite;
|
|
605
|
+
const result = await store.get(id);
|
|
606
|
+
if (!result)
|
|
607
|
+
return { error: `Suite '${id}' not found` };
|
|
608
|
+
const suite = result.suite;
|
|
609
|
+
const lines = [];
|
|
610
|
+
// Header
|
|
611
|
+
lines.push("const { test, expect } = require('@playwright/test');");
|
|
612
|
+
lines.push("");
|
|
613
|
+
lines.push(`test('${suite.name}', async ({ page }) => {`);
|
|
614
|
+
// Helper to convert a step to Playwright code
|
|
615
|
+
const convertStep = (step, indent = " ") => {
|
|
616
|
+
const stepLines = [];
|
|
617
|
+
// Add name comment if present
|
|
618
|
+
if (step.name) {
|
|
619
|
+
stepLines.push(`${indent}// ${step.name}`);
|
|
620
|
+
}
|
|
621
|
+
switch (step.action) {
|
|
622
|
+
case "navigate":
|
|
623
|
+
{
|
|
624
|
+
const cfg = step.config;
|
|
625
|
+
stepLines.push(`${indent}await page.goto('${cfg.url}');`);
|
|
626
|
+
}
|
|
627
|
+
break;
|
|
628
|
+
case "click":
|
|
629
|
+
{
|
|
630
|
+
const cfg = step.config;
|
|
631
|
+
if (cfg.text) {
|
|
632
|
+
stepLines.push(`${indent}await page.click('text=${cfg.text}');`);
|
|
633
|
+
}
|
|
634
|
+
else {
|
|
635
|
+
stepLines.push(`${indent}await page.click('${cfg.selector}');`);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
break;
|
|
639
|
+
case "type":
|
|
640
|
+
{
|
|
641
|
+
const cfg = step.config;
|
|
642
|
+
if (cfg.clear) {
|
|
643
|
+
stepLines.push(`${indent}await page.fill('${cfg.selector}', '${cfg.text}');`);
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
stepLines.push(`${indent}await page.type('${cfg.selector}', '${cfg.text}');`);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
break;
|
|
650
|
+
case "wait":
|
|
651
|
+
{
|
|
652
|
+
const cfg = step.config;
|
|
653
|
+
if (cfg.type === "selector") {
|
|
654
|
+
stepLines.push(`${indent}await page.waitForSelector('${cfg.value}');`);
|
|
655
|
+
}
|
|
656
|
+
else if (cfg.type === "time") {
|
|
657
|
+
stepLines.push(`${indent}await page.waitForTimeout(${cfg.value});`);
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
stepLines.push(`${indent}// wait (${cfg.type}): ${cfg.value}`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
break;
|
|
664
|
+
case "screenshot":
|
|
665
|
+
{
|
|
666
|
+
const cfg = step.config;
|
|
667
|
+
const path = cfg.name ?? "screenshot.png";
|
|
668
|
+
const options = cfg.full_page ? ", { fullPage: true }" : "";
|
|
669
|
+
stepLines.push(`${indent}await page.screenshot({ path: '${path}'${options} });`);
|
|
670
|
+
}
|
|
671
|
+
break;
|
|
672
|
+
case "evaluate":
|
|
673
|
+
{
|
|
674
|
+
const cfg = step.config;
|
|
675
|
+
stepLines.push(`${indent}await page.evaluate(() => ${cfg.expression});`);
|
|
676
|
+
}
|
|
677
|
+
break;
|
|
678
|
+
case "assert":
|
|
679
|
+
{
|
|
680
|
+
const cfg = step.config;
|
|
681
|
+
if (cfg.check === "selector_exists") {
|
|
682
|
+
stepLines.push(`${indent}await expect(page.locator('${cfg.value}')).toBeVisible();`);
|
|
683
|
+
}
|
|
684
|
+
else if (cfg.check === "text_contains") {
|
|
685
|
+
stepLines.push(`${indent}await expect(page).toHaveText('${cfg.value}');`);
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
stepLines.push(`${indent}// assert (${cfg.check}): ${cfg.value}`);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
break;
|
|
692
|
+
default:
|
|
693
|
+
stepLines.push(`${indent}// ${step.action}: ${JSON.stringify(step.config)}`);
|
|
694
|
+
}
|
|
695
|
+
return stepLines;
|
|
696
|
+
};
|
|
697
|
+
// Setup steps
|
|
698
|
+
if (suite.setup?.length) {
|
|
699
|
+
lines.push(" // Setup");
|
|
700
|
+
for (const step of suite.setup) {
|
|
701
|
+
lines.push(...convertStep(step));
|
|
702
|
+
}
|
|
703
|
+
lines.push("");
|
|
704
|
+
}
|
|
705
|
+
// Main steps
|
|
706
|
+
for (const step of suite.steps) {
|
|
707
|
+
if (step.disabled) {
|
|
708
|
+
lines.push(` // DISABLED: ${step.name ?? step.id}`);
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
lines.push(...convertStep(step));
|
|
712
|
+
}
|
|
713
|
+
// Teardown steps
|
|
714
|
+
if (suite.teardown?.length) {
|
|
715
|
+
lines.push("");
|
|
716
|
+
lines.push(" // Teardown");
|
|
717
|
+
for (const step of suite.teardown) {
|
|
718
|
+
lines.push(...convertStep(step));
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
lines.push("});");
|
|
722
|
+
const script = lines.join("\n");
|
|
723
|
+
return {
|
|
724
|
+
suite: id,
|
|
725
|
+
format: "playwright",
|
|
726
|
+
script,
|
|
727
|
+
filename: `${id}.spec.js`,
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
default:
|
|
731
|
+
return { error: `Unknown automation tool: ${name}` };
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
//# sourceMappingURL=tools.js.map
|