neuron-inspector 0.7.0 → 0.8.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/dist/compound-tools.js +2 -136
- package/dist/compound-tools.js.map +1 -1
- package/dist/monitor.js +208 -7
- package/dist/monitor.js.map +1 -1
- package/dist/recipe-tools.js +6 -39
- package/dist/recipe-tools.js.map +1 -1
- package/dist/recipes.d.ts +0 -4
- package/dist/recipes.js +3 -49
- package/dist/recipes.js.map +1 -1
- package/dist/server.js +53 -166
- package/dist/server.js.map +1 -1
- package/dist/tools.js +0 -26
- package/dist/tools.js.map +1 -1
- package/package.json +3 -4
- package/recipes/linkedin-outreach/learnings.md +1 -17
- package/recipes/planner/agent.md +1 -66
- package/recipes/planner/learnings.md +1 -49
- package/recipes/social-ops/learnings.md +13 -33
- package/dist/__tests__/monitor.test.d.ts +0 -1
- package/dist/__tests__/monitor.test.js +0 -286
- package/dist/__tests__/monitor.test.js.map +0 -1
- package/dist/__tests__/recipes.test.d.ts +0 -1
- package/dist/__tests__/recipes.test.js +0 -415
- package/dist/__tests__/recipes.test.js.map +0 -1
- package/dist/__tests__/scheduler.test.d.ts +0 -1
- package/dist/__tests__/scheduler.test.js +0 -316
- package/dist/__tests__/scheduler.test.js.map +0 -1
- package/dist/__tests__/session-state.test.d.ts +0 -1
- package/dist/__tests__/session-state.test.js +0 -335
- package/dist/__tests__/session-state.test.js.map +0 -1
- package/dist/collections/__tests__/assertions.test.d.ts +0 -1
- package/dist/collections/__tests__/assertions.test.js +0 -203
- package/dist/collections/__tests__/assertions.test.js.map +0 -1
- package/dist/collections/__tests__/dynamic-vars.test.d.ts +0 -1
- package/dist/collections/__tests__/dynamic-vars.test.js +0 -169
- package/dist/collections/__tests__/dynamic-vars.test.js.map +0 -1
- package/dist/collections/__tests__/execution.test.d.ts +0 -1
- package/dist/collections/__tests__/execution.test.js +0 -271
- package/dist/collections/__tests__/execution.test.js.map +0 -1
- package/dist/collections/__tests__/integration.test.d.ts +0 -1
- package/dist/collections/__tests__/integration.test.js +0 -478
- package/dist/collections/__tests__/integration.test.js.map +0 -1
- package/dist/collections/__tests__/resolver.test.d.ts +0 -1
- package/dist/collections/__tests__/resolver.test.js +0 -282
- package/dist/collections/__tests__/resolver.test.js.map +0 -1
- package/dist/collections/__tests__/scenario.test.d.ts +0 -1
- package/dist/collections/__tests__/scenario.test.js +0 -367
- package/dist/collections/__tests__/scenario.test.js.map +0 -1
- package/dist/collections/__tests__/scripts.test.d.ts +0 -1
- package/dist/collections/__tests__/scripts.test.js +0 -245
- package/dist/collections/__tests__/scripts.test.js.map +0 -1
- package/dist/collections/__tests__/store.test.d.ts +0 -1
- package/dist/collections/__tests__/store.test.js +0 -313
- package/dist/collections/__tests__/store.test.js.map +0 -1
- package/dist/collections/assertions.d.ts +0 -16
- package/dist/collections/assertions.js +0 -107
- package/dist/collections/assertions.js.map +0 -1
- package/dist/collections/dynamic-vars.d.ts +0 -75
- package/dist/collections/dynamic-vars.js +0 -187
- package/dist/collections/dynamic-vars.js.map +0 -1
- package/dist/collections/executor.d.ts +0 -100
- package/dist/collections/executor.js +0 -301
- package/dist/collections/executor.js.map +0 -1
- package/dist/collections/http.d.ts +0 -9
- package/dist/collections/http.js +0 -194
- package/dist/collections/http.js.map +0 -1
- package/dist/collections/index.d.ts +0 -12
- package/dist/collections/index.js +0 -27
- package/dist/collections/index.js.map +0 -1
- package/dist/collections/postman.d.ts +0 -10
- package/dist/collections/postman.js +0 -288
- package/dist/collections/postman.js.map +0 -1
- package/dist/collections/resolver.d.ts +0 -74
- package/dist/collections/resolver.js +0 -330
- package/dist/collections/resolver.js.map +0 -1
- package/dist/collections/scripts.d.ts +0 -20
- package/dist/collections/scripts.js +0 -225
- package/dist/collections/scripts.js.map +0 -1
- package/dist/collections/store.d.ts +0 -68
- package/dist/collections/store.js +0 -507
- package/dist/collections/store.js.map +0 -1
- package/dist/collections/tools.d.ts +0 -8
- package/dist/collections/tools.js +0 -1276
- package/dist/collections/tools.js.map +0 -1
- package/dist/collections/types.d.ts +0 -226
- package/dist/collections/types.js +0 -3
- package/dist/collections/types.js.map +0 -1
- package/dist/killswitch.d.ts +0 -8
- package/dist/killswitch.js +0 -304
- package/dist/killswitch.js.map +0 -1
- package/dist/resilience.d.ts +0 -112
- package/dist/resilience.js +0 -319
- package/dist/resilience.js.map +0 -1
- package/recipes/autopilot/agent.md +0 -316
- package/recipes/autopilot/learnings.md +0 -33
- package/recipes/autopilot/recipe.yaml +0 -96
- package/recipes/inbox-responder/agent.md +0 -268
- package/recipes/inbox-responder/learnings.md +0 -25
- package/recipes/inbox-responder/recipe.yaml +0 -82
|
@@ -1,1276 +0,0 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import * as YAML from "yaml";
|
|
3
|
-
import { slugify } from "./store.js";
|
|
4
|
-
import { executeEndpoint, executeFolder, executeCollection, executeScenario } from "./executor.js";
|
|
5
|
-
import { fromPostmanV21, toPostmanV21 } from "./postman.js";
|
|
6
|
-
export function buildCollectionTools() {
|
|
7
|
-
return [
|
|
8
|
-
{
|
|
9
|
-
name: "neuron_api_list_collections",
|
|
10
|
-
description: "List all API collections (global + project-local) with folder, endpoint, environment, and scenario counts.",
|
|
11
|
-
inputSchema: {
|
|
12
|
-
type: "object",
|
|
13
|
-
properties: {},
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
name: "neuron_api_get_collection",
|
|
18
|
-
description: "Get the full structure of an API collection: folders, endpoints, environments, and scenarios.",
|
|
19
|
-
inputSchema: {
|
|
20
|
-
type: "object",
|
|
21
|
-
properties: {
|
|
22
|
-
collection: {
|
|
23
|
-
type: "string",
|
|
24
|
-
description: "Collection id (filename stem, e.g. 'neuron-api')",
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
required: ["collection"],
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
name: "neuron_api_create_collection",
|
|
32
|
-
description: "Create a new empty API collection. Returns error if a collection with the same slug already exists.",
|
|
33
|
-
inputSchema: {
|
|
34
|
-
type: "object",
|
|
35
|
-
properties: {
|
|
36
|
-
name: {
|
|
37
|
-
type: "string",
|
|
38
|
-
description: "Human-readable collection name (e.g. 'Neuron API')",
|
|
39
|
-
},
|
|
40
|
-
description: {
|
|
41
|
-
type: "string",
|
|
42
|
-
description: "Optional description of the collection",
|
|
43
|
-
},
|
|
44
|
-
base_url: {
|
|
45
|
-
type: "string",
|
|
46
|
-
description: "Optional default base URL. If provided, creates a 'default' environment with this URL.",
|
|
47
|
-
},
|
|
48
|
-
location: {
|
|
49
|
-
type: "string",
|
|
50
|
-
enum: ["global", "project"],
|
|
51
|
-
description: "Where to store: 'global' (~/.neuron/collections/) or 'project' (.neuron/collections/ in cwd). Default: global.",
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
required: ["name"],
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
name: "neuron_api_delete_collection",
|
|
59
|
-
description: "Delete an API collection file.",
|
|
60
|
-
inputSchema: {
|
|
61
|
-
type: "object",
|
|
62
|
-
properties: {
|
|
63
|
-
collection: {
|
|
64
|
-
type: "string",
|
|
65
|
-
description: "Collection id to delete",
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
required: ["collection"],
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
name: "neuron_api_upsert_endpoint",
|
|
73
|
-
description: "Add or update an endpoint in a collection folder. Creates the folder if it does not exist. " +
|
|
74
|
-
"Matches existing endpoints by method + path within the folder. If a match is found, the endpoint is updated; otherwise it is appended. " +
|
|
75
|
-
"If 'name' is omitted, it is auto-generated from the method and path.",
|
|
76
|
-
inputSchema: {
|
|
77
|
-
type: "object",
|
|
78
|
-
properties: {
|
|
79
|
-
collection: {
|
|
80
|
-
type: "string",
|
|
81
|
-
description: "Collection id",
|
|
82
|
-
},
|
|
83
|
-
folder: {
|
|
84
|
-
type: "string",
|
|
85
|
-
description: "Folder name (e.g. 'auth', 'orders'). Will be slugified as the YAML key.",
|
|
86
|
-
},
|
|
87
|
-
method: {
|
|
88
|
-
type: "string",
|
|
89
|
-
description: "HTTP method (GET, POST, PUT, PATCH, DELETE)",
|
|
90
|
-
},
|
|
91
|
-
path: {
|
|
92
|
-
type: "string",
|
|
93
|
-
description: "Endpoint path (e.g. '/api/v1/bots'). May contain {{variables}}.",
|
|
94
|
-
},
|
|
95
|
-
name: {
|
|
96
|
-
type: "string",
|
|
97
|
-
description: "Human-readable name. Auto-generated from method+path if omitted.",
|
|
98
|
-
},
|
|
99
|
-
description: {
|
|
100
|
-
type: "string",
|
|
101
|
-
description: "Endpoint description",
|
|
102
|
-
},
|
|
103
|
-
headers: {
|
|
104
|
-
type: "object",
|
|
105
|
-
description: "Request-level header overrides",
|
|
106
|
-
},
|
|
107
|
-
body: {
|
|
108
|
-
description: "Sample request body (any JSON value)",
|
|
109
|
-
},
|
|
110
|
-
tests: {
|
|
111
|
-
type: "array",
|
|
112
|
-
description: "Test assertions, e.g. [{ check: 'status', expect: 200 }, { check: 'body.id', op: 'exists' }]",
|
|
113
|
-
items: {
|
|
114
|
-
type: "object",
|
|
115
|
-
properties: {
|
|
116
|
-
check: { type: "string" },
|
|
117
|
-
op: { type: "string" },
|
|
118
|
-
expect: {},
|
|
119
|
-
},
|
|
120
|
-
required: ["check"],
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
extract: {
|
|
124
|
-
type: "object",
|
|
125
|
-
description: "Variables to extract from the response, e.g. { bot_id: 'body.id' }",
|
|
126
|
-
},
|
|
127
|
-
body_mode: {
|
|
128
|
-
type: "string",
|
|
129
|
-
enum: ["json", "text", "form-data", "urlencoded"],
|
|
130
|
-
description: "Body encoding mode. Default: json",
|
|
131
|
-
},
|
|
132
|
-
cookies: {
|
|
133
|
-
type: "object",
|
|
134
|
-
description: "Request cookies as key-value pairs, merged into Cookie header on run",
|
|
135
|
-
},
|
|
136
|
-
variables: {
|
|
137
|
-
type: "object",
|
|
138
|
-
description: "Request-level variable overrides (key-value pairs)",
|
|
139
|
-
},
|
|
140
|
-
auth: {
|
|
141
|
-
type: "object",
|
|
142
|
-
description: "Auth config: { type: 'bearer'|'basic'|'api-key'|'none', token?, username?, password?, header?, value? }",
|
|
143
|
-
properties: {
|
|
144
|
-
type: { type: "string", enum: ["bearer", "basic", "api-key", "none"] },
|
|
145
|
-
token: { type: "string" },
|
|
146
|
-
username: { type: "string" },
|
|
147
|
-
password: { type: "string" },
|
|
148
|
-
header: { type: "string" },
|
|
149
|
-
value: { type: "string" },
|
|
150
|
-
},
|
|
151
|
-
required: ["type"],
|
|
152
|
-
},
|
|
153
|
-
pre_request: {
|
|
154
|
-
type: "object",
|
|
155
|
-
description: "Pre-request script: { set?: {var: value}, extract?: {var: expression} }",
|
|
156
|
-
properties: {
|
|
157
|
-
set: { type: "object" },
|
|
158
|
-
extract: { type: "object" },
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
post_response: {
|
|
162
|
-
type: "object",
|
|
163
|
-
description: "Post-response script: { set?: {var: value}, extract?: {var: expression} }",
|
|
164
|
-
properties: {
|
|
165
|
-
set: { type: "object" },
|
|
166
|
-
extract: { type: "object" },
|
|
167
|
-
},
|
|
168
|
-
},
|
|
169
|
-
tags: {
|
|
170
|
-
type: "array",
|
|
171
|
-
items: { type: "string" },
|
|
172
|
-
description: "Tags for filtering/querying",
|
|
173
|
-
},
|
|
174
|
-
timeout_ms: {
|
|
175
|
-
type: "number",
|
|
176
|
-
description: "Per-endpoint timeout override (ms)",
|
|
177
|
-
},
|
|
178
|
-
settings: {
|
|
179
|
-
type: "object",
|
|
180
|
-
description: "Request settings: timeout, redirects, SSL, URL encoding",
|
|
181
|
-
properties: {
|
|
182
|
-
timeout_ms: { type: "number" },
|
|
183
|
-
follow_redirects: { type: "boolean" },
|
|
184
|
-
max_redirects: { type: "number" },
|
|
185
|
-
verify_ssl: { type: "boolean" },
|
|
186
|
-
follow_original_method: { type: "boolean", description: "Redirect with original HTTP method instead of GET" },
|
|
187
|
-
follow_authorization: { type: "boolean", description: "Retain Authorization header on cross-host redirects" },
|
|
188
|
-
remove_referer: { type: "boolean", description: "Remove Referer header on redirect" },
|
|
189
|
-
encode_url: { type: "boolean", description: "Encode URL automatically" },
|
|
190
|
-
},
|
|
191
|
-
},
|
|
192
|
-
},
|
|
193
|
-
required: ["collection", "folder", "method", "path"],
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
name: "neuron_api_remove_endpoint",
|
|
198
|
-
description: "Remove an endpoint from a collection folder by method + path.",
|
|
199
|
-
inputSchema: {
|
|
200
|
-
type: "object",
|
|
201
|
-
properties: {
|
|
202
|
-
collection: {
|
|
203
|
-
type: "string",
|
|
204
|
-
description: "Collection id",
|
|
205
|
-
},
|
|
206
|
-
folder: {
|
|
207
|
-
type: "string",
|
|
208
|
-
description: "Folder name",
|
|
209
|
-
},
|
|
210
|
-
method: {
|
|
211
|
-
type: "string",
|
|
212
|
-
description: "HTTP method of the endpoint to remove",
|
|
213
|
-
},
|
|
214
|
-
path: {
|
|
215
|
-
type: "string",
|
|
216
|
-
description: "Path of the endpoint to remove",
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
required: ["collection", "folder", "method", "path"],
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
{
|
|
223
|
-
name: "neuron_api_move_endpoint",
|
|
224
|
-
description: "Move an endpoint from one folder to another.",
|
|
225
|
-
inputSchema: {
|
|
226
|
-
type: "object",
|
|
227
|
-
properties: {
|
|
228
|
-
collection: {
|
|
229
|
-
type: "string",
|
|
230
|
-
description: "Collection id",
|
|
231
|
-
},
|
|
232
|
-
from_folder: {
|
|
233
|
-
type: "string",
|
|
234
|
-
description: "Source folder name",
|
|
235
|
-
},
|
|
236
|
-
to_folder: {
|
|
237
|
-
type: "string",
|
|
238
|
-
description: "Destination folder name (created if absent)",
|
|
239
|
-
},
|
|
240
|
-
method: {
|
|
241
|
-
type: "string",
|
|
242
|
-
description: "HTTP method of the endpoint to move",
|
|
243
|
-
},
|
|
244
|
-
path: {
|
|
245
|
-
type: "string",
|
|
246
|
-
description: "Path of the endpoint to move",
|
|
247
|
-
},
|
|
248
|
-
},
|
|
249
|
-
required: ["collection", "from_folder", "to_folder", "method", "path"],
|
|
250
|
-
},
|
|
251
|
-
},
|
|
252
|
-
// ── Environment management ───────────────────────────────
|
|
253
|
-
{
|
|
254
|
-
name: "neuron_api_set_env",
|
|
255
|
-
description: "Create or update an environment in a collection with base_url, variables, and headers.",
|
|
256
|
-
inputSchema: {
|
|
257
|
-
type: "object",
|
|
258
|
-
properties: {
|
|
259
|
-
collection: { type: "string", description: "Collection id" },
|
|
260
|
-
name: { type: "string", description: "Environment name (e.g. 'staging', 'production')" },
|
|
261
|
-
base_url: { type: "string", description: "Base URL for this environment" },
|
|
262
|
-
variables: { type: "object", description: "Environment variables (key-value pairs)" },
|
|
263
|
-
headers: { type: "object", description: "Environment-specific header overrides" },
|
|
264
|
-
timeout_ms: { type: "number", description: "Environment-specific timeout" },
|
|
265
|
-
},
|
|
266
|
-
required: ["collection", "name", "base_url"],
|
|
267
|
-
},
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
name: "neuron_api_remove_env",
|
|
271
|
-
description: "Remove an environment from a collection.",
|
|
272
|
-
inputSchema: {
|
|
273
|
-
type: "object",
|
|
274
|
-
properties: {
|
|
275
|
-
collection: { type: "string", description: "Collection id" },
|
|
276
|
-
name: { type: "string", description: "Environment name to remove" },
|
|
277
|
-
},
|
|
278
|
-
required: ["collection", "name"],
|
|
279
|
-
},
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
name: "neuron_api_list_envs",
|
|
283
|
-
description: "List all environments for a collection with their base URLs and variable counts.",
|
|
284
|
-
inputSchema: {
|
|
285
|
-
type: "object",
|
|
286
|
-
properties: {
|
|
287
|
-
collection: { type: "string", description: "Collection id" },
|
|
288
|
-
},
|
|
289
|
-
required: ["collection"],
|
|
290
|
-
},
|
|
291
|
-
},
|
|
292
|
-
{
|
|
293
|
-
name: "neuron_api_switch_env",
|
|
294
|
-
description: "Set the active environment for a collection. Persisted to the collection file so subsequent runs use it by default.",
|
|
295
|
-
inputSchema: {
|
|
296
|
-
type: "object",
|
|
297
|
-
properties: {
|
|
298
|
-
collection: { type: "string", description: "Collection id" },
|
|
299
|
-
name: { type: "string", description: "Environment name to activate" },
|
|
300
|
-
},
|
|
301
|
-
required: ["collection", "name"],
|
|
302
|
-
},
|
|
303
|
-
},
|
|
304
|
-
// ── Collection metadata ─────────────────────────────────
|
|
305
|
-
{
|
|
306
|
-
name: "neuron_api_update_collection",
|
|
307
|
-
description: "Update a collection's name, description, or version.",
|
|
308
|
-
inputSchema: {
|
|
309
|
-
type: "object",
|
|
310
|
-
properties: {
|
|
311
|
-
collection: { type: "string", description: "Collection id" },
|
|
312
|
-
name: { type: "string", description: "New collection name" },
|
|
313
|
-
description: { type: "string", description: "New description (empty string to clear)" },
|
|
314
|
-
version: { type: "string", description: "New version string" },
|
|
315
|
-
},
|
|
316
|
-
required: ["collection"],
|
|
317
|
-
},
|
|
318
|
-
},
|
|
319
|
-
// ── Defaults management ───────────────────────────────────
|
|
320
|
-
{
|
|
321
|
-
name: "neuron_api_set_defaults",
|
|
322
|
-
description: "Set or update collection-level defaults. Merges with existing defaults. " +
|
|
323
|
-
"Pass null for auth to clear it. Omit fields to leave them unchanged.",
|
|
324
|
-
inputSchema: {
|
|
325
|
-
type: "object",
|
|
326
|
-
properties: {
|
|
327
|
-
collection: { type: "string", description: "Collection id" },
|
|
328
|
-
headers: { type: "object", description: "Default headers (key-value pairs)" },
|
|
329
|
-
variables: { type: "object", description: "Default variables (key-value pairs)" },
|
|
330
|
-
timeout_ms: { type: "number", description: "Default timeout (ms)" },
|
|
331
|
-
auth: {
|
|
332
|
-
type: "object",
|
|
333
|
-
description: "Default auth: { type: 'bearer'|'basic'|'api-key'|'none', token?, username?, password?, header?, value? }. Pass null to clear.",
|
|
334
|
-
properties: {
|
|
335
|
-
type: { type: "string", enum: ["bearer", "basic", "api-key", "none"] },
|
|
336
|
-
token: { type: "string" },
|
|
337
|
-
username: { type: "string" },
|
|
338
|
-
password: { type: "string" },
|
|
339
|
-
header: { type: "string" },
|
|
340
|
-
value: { type: "string" },
|
|
341
|
-
},
|
|
342
|
-
},
|
|
343
|
-
pre_request: {
|
|
344
|
-
type: "object",
|
|
345
|
-
description: "Pre-request script that runs before every endpoint. { code?: string, set?: {}, extract?: {} }. Pass null to clear.",
|
|
346
|
-
properties: {
|
|
347
|
-
code: { type: "string" },
|
|
348
|
-
set: { type: "object" },
|
|
349
|
-
extract: { type: "object" },
|
|
350
|
-
},
|
|
351
|
-
},
|
|
352
|
-
post_response: {
|
|
353
|
-
type: "object",
|
|
354
|
-
description: "Post-response script that runs after every endpoint. { code?: string, set?: {}, extract?: {} }. Pass null to clear.",
|
|
355
|
-
properties: {
|
|
356
|
-
code: { type: "string" },
|
|
357
|
-
set: { type: "object" },
|
|
358
|
-
extract: { type: "object" },
|
|
359
|
-
},
|
|
360
|
-
},
|
|
361
|
-
},
|
|
362
|
-
required: ["collection"],
|
|
363
|
-
},
|
|
364
|
-
},
|
|
365
|
-
// ── Folder management ─────────────────────────────────────
|
|
366
|
-
{
|
|
367
|
-
name: "neuron_api_create_folder",
|
|
368
|
-
description: "Create a new folder in a collection with optional description, headers, variables, and auth.",
|
|
369
|
-
inputSchema: {
|
|
370
|
-
type: "object",
|
|
371
|
-
properties: {
|
|
372
|
-
collection: { type: "string", description: "Collection id" },
|
|
373
|
-
name: { type: "string", description: "Folder name" },
|
|
374
|
-
description: { type: "string", description: "Folder description" },
|
|
375
|
-
headers: { type: "object", description: "Folder-level default headers" },
|
|
376
|
-
variables: { type: "object", description: "Folder-level default variables" },
|
|
377
|
-
auth: {
|
|
378
|
-
type: "object",
|
|
379
|
-
description: "Folder-level auth config",
|
|
380
|
-
properties: {
|
|
381
|
-
type: { type: "string", enum: ["bearer", "basic", "api-key", "none"] },
|
|
382
|
-
token: { type: "string" },
|
|
383
|
-
username: { type: "string" },
|
|
384
|
-
password: { type: "string" },
|
|
385
|
-
header: { type: "string" },
|
|
386
|
-
value: { type: "string" },
|
|
387
|
-
},
|
|
388
|
-
},
|
|
389
|
-
},
|
|
390
|
-
required: ["collection", "name"],
|
|
391
|
-
},
|
|
392
|
-
},
|
|
393
|
-
{
|
|
394
|
-
name: "neuron_api_update_folder",
|
|
395
|
-
description: "Update a folder's description, headers, variables, or auth. Pass null for auth to clear it.",
|
|
396
|
-
inputSchema: {
|
|
397
|
-
type: "object",
|
|
398
|
-
properties: {
|
|
399
|
-
collection: { type: "string", description: "Collection id" },
|
|
400
|
-
folder: { type: "string", description: "Folder name" },
|
|
401
|
-
description: { type: "string", description: "New description (empty string to clear)" },
|
|
402
|
-
headers: { type: "object", description: "New folder-level headers (replaces existing)" },
|
|
403
|
-
variables: { type: "object", description: "New folder-level variables (replaces existing)" },
|
|
404
|
-
auth: {
|
|
405
|
-
type: "object",
|
|
406
|
-
description: "New folder-level auth config. Pass null to clear.",
|
|
407
|
-
properties: {
|
|
408
|
-
type: { type: "string", enum: ["bearer", "basic", "api-key", "none"] },
|
|
409
|
-
token: { type: "string" },
|
|
410
|
-
username: { type: "string" },
|
|
411
|
-
password: { type: "string" },
|
|
412
|
-
header: { type: "string" },
|
|
413
|
-
value: { type: "string" },
|
|
414
|
-
},
|
|
415
|
-
},
|
|
416
|
-
},
|
|
417
|
-
required: ["collection", "folder"],
|
|
418
|
-
},
|
|
419
|
-
},
|
|
420
|
-
{
|
|
421
|
-
name: "neuron_api_delete_folder",
|
|
422
|
-
description: "Delete a folder and all its endpoints from a collection.",
|
|
423
|
-
inputSchema: {
|
|
424
|
-
type: "object",
|
|
425
|
-
properties: {
|
|
426
|
-
collection: { type: "string", description: "Collection id" },
|
|
427
|
-
folder: { type: "string", description: "Folder name to delete" },
|
|
428
|
-
},
|
|
429
|
-
required: ["collection", "folder"],
|
|
430
|
-
},
|
|
431
|
-
},
|
|
432
|
-
{
|
|
433
|
-
name: "neuron_api_list_folders",
|
|
434
|
-
description: "List all folders in a collection with endpoint counts, descriptions, and whether they have custom headers/variables/auth.",
|
|
435
|
-
inputSchema: {
|
|
436
|
-
type: "object",
|
|
437
|
-
properties: {
|
|
438
|
-
collection: { type: "string", description: "Collection id" },
|
|
439
|
-
},
|
|
440
|
-
required: ["collection"],
|
|
441
|
-
},
|
|
442
|
-
},
|
|
443
|
-
// ── Scenario management ───────────────────────────────────
|
|
444
|
-
{
|
|
445
|
-
name: "neuron_api_create_scenario",
|
|
446
|
-
description: "Create a new scenario in a collection. Steps reference endpoints as 'folder/method path' (e.g. 'auth/POST /login').",
|
|
447
|
-
inputSchema: {
|
|
448
|
-
type: "object",
|
|
449
|
-
properties: {
|
|
450
|
-
collection: { type: "string", description: "Collection id" },
|
|
451
|
-
name: { type: "string", description: "Scenario name" },
|
|
452
|
-
description: { type: "string", description: "Scenario description" },
|
|
453
|
-
steps: {
|
|
454
|
-
type: "array",
|
|
455
|
-
description: "Ordered steps. Each step has a 'ref' (folder/METHOD path) and optional overrides.",
|
|
456
|
-
items: {
|
|
457
|
-
type: "object",
|
|
458
|
-
properties: {
|
|
459
|
-
ref: { type: "string", description: "Endpoint reference: 'folder/METHOD path'" },
|
|
460
|
-
overrides: {
|
|
461
|
-
type: "object",
|
|
462
|
-
description: "Per-step overrides for headers, body, tests, path, variables",
|
|
463
|
-
properties: {
|
|
464
|
-
headers: { type: "object" },
|
|
465
|
-
body: {},
|
|
466
|
-
tests: { type: "array" },
|
|
467
|
-
path: { type: "string" },
|
|
468
|
-
variables: { type: "object" },
|
|
469
|
-
},
|
|
470
|
-
},
|
|
471
|
-
},
|
|
472
|
-
required: ["ref"],
|
|
473
|
-
},
|
|
474
|
-
},
|
|
475
|
-
},
|
|
476
|
-
required: ["collection", "name", "steps"],
|
|
477
|
-
},
|
|
478
|
-
},
|
|
479
|
-
{
|
|
480
|
-
name: "neuron_api_update_scenario",
|
|
481
|
-
description: "Update a scenario's description or steps.",
|
|
482
|
-
inputSchema: {
|
|
483
|
-
type: "object",
|
|
484
|
-
properties: {
|
|
485
|
-
collection: { type: "string", description: "Collection id" },
|
|
486
|
-
scenario: { type: "string", description: "Scenario name" },
|
|
487
|
-
description: { type: "string", description: "New description" },
|
|
488
|
-
steps: {
|
|
489
|
-
type: "array",
|
|
490
|
-
description: "New ordered steps (replaces existing)",
|
|
491
|
-
items: {
|
|
492
|
-
type: "object",
|
|
493
|
-
properties: {
|
|
494
|
-
ref: { type: "string" },
|
|
495
|
-
overrides: { type: "object" },
|
|
496
|
-
},
|
|
497
|
-
required: ["ref"],
|
|
498
|
-
},
|
|
499
|
-
},
|
|
500
|
-
},
|
|
501
|
-
required: ["collection", "scenario"],
|
|
502
|
-
},
|
|
503
|
-
},
|
|
504
|
-
{
|
|
505
|
-
name: "neuron_api_delete_scenario",
|
|
506
|
-
description: "Delete a scenario from a collection.",
|
|
507
|
-
inputSchema: {
|
|
508
|
-
type: "object",
|
|
509
|
-
properties: {
|
|
510
|
-
collection: { type: "string", description: "Collection id" },
|
|
511
|
-
scenario: { type: "string", description: "Scenario name to delete" },
|
|
512
|
-
},
|
|
513
|
-
required: ["collection", "scenario"],
|
|
514
|
-
},
|
|
515
|
-
},
|
|
516
|
-
{
|
|
517
|
-
name: "neuron_api_list_scenarios",
|
|
518
|
-
description: "List all scenarios in a collection with their step counts and descriptions.",
|
|
519
|
-
inputSchema: {
|
|
520
|
-
type: "object",
|
|
521
|
-
properties: {
|
|
522
|
-
collection: { type: "string", description: "Collection id" },
|
|
523
|
-
},
|
|
524
|
-
required: ["collection"],
|
|
525
|
-
},
|
|
526
|
-
},
|
|
527
|
-
// ── Execution ────────────────────────────────────────────
|
|
528
|
-
{
|
|
529
|
-
name: "neuron_api_run",
|
|
530
|
-
description: "Run API endpoints against an environment. Three modes:\n" +
|
|
531
|
-
" 1. Single endpoint: provide collection + folder + method + path\n" +
|
|
532
|
-
" 2. Folder: provide collection + folder (runs all endpoints in the folder sequentially)\n" +
|
|
533
|
-
" 3. Collection: provide only collection (runs all folders, all endpoints)\n" +
|
|
534
|
-
"Returns status, headers, parsed body, timing, test assertion results, and extracted variables. " +
|
|
535
|
-
"Folder/collection runs continue on failure. Extracted variables carry between endpoints within a folder.",
|
|
536
|
-
inputSchema: {
|
|
537
|
-
type: "object",
|
|
538
|
-
properties: {
|
|
539
|
-
collection: { type: "string", description: "Collection id" },
|
|
540
|
-
folder: { type: "string", description: "Folder name (omit to run entire collection)" },
|
|
541
|
-
method: { type: "string", description: "HTTP method (required for single-endpoint mode)" },
|
|
542
|
-
path: { type: "string", description: "Endpoint path (required for single-endpoint mode)" },
|
|
543
|
-
environment: { type: "string", description: "Environment name (defaults to active_environment)" },
|
|
544
|
-
cookies: { type: "object", description: "Additional cookies for this run (merged with endpoint cookies)" },
|
|
545
|
-
timeout_ms: { type: "number", description: "Override timeout (ms) for this run" },
|
|
546
|
-
follow_redirects: { type: "boolean", description: "Override follow-redirects setting for this run" },
|
|
547
|
-
max_redirects: { type: "number", description: "Override max redirects for this run" },
|
|
548
|
-
verify_ssl: { type: "boolean", description: "Override SSL verification for this run" },
|
|
549
|
-
follow_original_method: { type: "boolean", description: "Override: redirect with original HTTP method" },
|
|
550
|
-
follow_authorization: { type: "boolean", description: "Override: retain Authorization on cross-host redirects" },
|
|
551
|
-
remove_referer: { type: "boolean", description: "Override: remove Referer header on redirect" },
|
|
552
|
-
encode_url: { type: "boolean", description: "Override: encode URL automatically" },
|
|
553
|
-
},
|
|
554
|
-
required: ["collection"],
|
|
555
|
-
},
|
|
556
|
-
},
|
|
557
|
-
{
|
|
558
|
-
name: "neuron_api_run_scenario",
|
|
559
|
-
description: "Run a named scenario: an ordered sequence of endpoint calls with variable chaining. " +
|
|
560
|
-
"Extracted variables from each step are available to subsequent steps via {{variable}} references. " +
|
|
561
|
-
"Execution halts on the first step that fails (HTTP error or assertion failure). " +
|
|
562
|
-
"Steps can include overrides for headers, body, tests, path, and variables.",
|
|
563
|
-
inputSchema: {
|
|
564
|
-
type: "object",
|
|
565
|
-
properties: {
|
|
566
|
-
collection: { type: "string", description: "Collection id" },
|
|
567
|
-
scenario: { type: "string", description: "Scenario name (key under 'scenarios' in the collection)" },
|
|
568
|
-
environment: { type: "string", description: "Environment name (defaults to active_environment)" },
|
|
569
|
-
},
|
|
570
|
-
required: ["collection", "scenario"],
|
|
571
|
-
},
|
|
572
|
-
},
|
|
573
|
-
// ── Query and generation ─────────────────────────────────
|
|
574
|
-
{
|
|
575
|
-
name: "neuron_api_query",
|
|
576
|
-
description: "Search endpoints across all collections by method, path pattern, folder, tags, or free text. " +
|
|
577
|
-
"Returns matching endpoints with their collection and folder context. " +
|
|
578
|
-
"All filters are optional; omit all for a full listing.",
|
|
579
|
-
inputSchema: {
|
|
580
|
-
type: "object",
|
|
581
|
-
properties: {
|
|
582
|
-
collection: { type: "string", description: "Limit to a specific collection id" },
|
|
583
|
-
method: { type: "string", description: "Filter by HTTP method (GET, POST, etc.)" },
|
|
584
|
-
path: { type: "string", description: "Filter by path pattern (substring match, e.g. '/bots')" },
|
|
585
|
-
folder: { type: "string", description: "Filter by folder name" },
|
|
586
|
-
tags: { type: "array", items: { type: "string" }, description: "Filter by tags (any match)" },
|
|
587
|
-
text: { type: "string", description: "Free-text search across endpoint names, descriptions, and paths" },
|
|
588
|
-
},
|
|
589
|
-
},
|
|
590
|
-
},
|
|
591
|
-
{
|
|
592
|
-
name: "neuron_api_generate_tests",
|
|
593
|
-
description: "Suggest test assertions for an endpoint based on its method, path, existing body, and common REST patterns. " +
|
|
594
|
-
"Returns a YAML-ready assertions array. Does not modify the collection.",
|
|
595
|
-
inputSchema: {
|
|
596
|
-
type: "object",
|
|
597
|
-
properties: {
|
|
598
|
-
collection: { type: "string", description: "Collection id" },
|
|
599
|
-
folder: { type: "string", description: "Folder name" },
|
|
600
|
-
method: { type: "string", description: "HTTP method" },
|
|
601
|
-
path: { type: "string", description: "Endpoint path" },
|
|
602
|
-
},
|
|
603
|
-
required: ["collection", "folder", "method", "path"],
|
|
604
|
-
},
|
|
605
|
-
},
|
|
606
|
-
// ── Import / Export ───────────────────────────────────────
|
|
607
|
-
{
|
|
608
|
-
name: "neuron_api_export_collection",
|
|
609
|
-
description: "Export a collection as YAML or Postman v2.1 JSON. Returns the file content as a string.",
|
|
610
|
-
inputSchema: {
|
|
611
|
-
type: "object",
|
|
612
|
-
properties: {
|
|
613
|
-
collection: { type: "string", description: "Collection id" },
|
|
614
|
-
format: {
|
|
615
|
-
type: "string",
|
|
616
|
-
enum: ["yaml", "postman"],
|
|
617
|
-
description: "Export format: 'yaml' (default) or 'postman' (Postman v2.1 JSON)",
|
|
618
|
-
},
|
|
619
|
-
},
|
|
620
|
-
required: ["collection"],
|
|
621
|
-
},
|
|
622
|
-
},
|
|
623
|
-
{
|
|
624
|
-
name: "neuron_api_import_collection",
|
|
625
|
-
description: "Import a collection from raw YAML or Postman v2.1 JSON content. Auto-detects format unless specified.",
|
|
626
|
-
inputSchema: {
|
|
627
|
-
type: "object",
|
|
628
|
-
properties: {
|
|
629
|
-
content: { type: "string", description: "Raw file content (YAML or JSON)" },
|
|
630
|
-
format: {
|
|
631
|
-
type: "string",
|
|
632
|
-
enum: ["yaml", "postman", "auto"],
|
|
633
|
-
description: "Content format. 'auto' (default) detects from content.",
|
|
634
|
-
},
|
|
635
|
-
name: { type: "string", description: "Optional name override for the imported collection" },
|
|
636
|
-
location: {
|
|
637
|
-
type: "string",
|
|
638
|
-
enum: ["global", "project"],
|
|
639
|
-
description: "Where to store: 'global' (default) or 'project'",
|
|
640
|
-
},
|
|
641
|
-
},
|
|
642
|
-
required: ["content"],
|
|
643
|
-
},
|
|
644
|
-
},
|
|
645
|
-
{
|
|
646
|
-
name: "neuron_api_import_file",
|
|
647
|
-
description: "Import a collection from a file path. Auto-detects format from extension and content.",
|
|
648
|
-
inputSchema: {
|
|
649
|
-
type: "object",
|
|
650
|
-
properties: {
|
|
651
|
-
path: { type: "string", description: "Absolute file path to import" },
|
|
652
|
-
name: { type: "string", description: "Optional name override" },
|
|
653
|
-
location: {
|
|
654
|
-
type: "string",
|
|
655
|
-
enum: ["global", "project"],
|
|
656
|
-
description: "Where to store: 'global' (default) or 'project'",
|
|
657
|
-
},
|
|
658
|
-
},
|
|
659
|
-
required: ["path"],
|
|
660
|
-
},
|
|
661
|
-
},
|
|
662
|
-
];
|
|
663
|
-
}
|
|
664
|
-
// ── Handler ─────────────────────────────────────────────────────
|
|
665
|
-
export function buildCollectionHandler(store, httpExecutor, pipeline, assertionEvaluator, scriptRunner) {
|
|
666
|
-
return async function handleCollectionTool(name, args) {
|
|
667
|
-
switch (name) {
|
|
668
|
-
case "neuron_api_list_collections": {
|
|
669
|
-
return store.list();
|
|
670
|
-
}
|
|
671
|
-
case "neuron_api_get_collection": {
|
|
672
|
-
const id = args.collection;
|
|
673
|
-
const result = await store.get(id);
|
|
674
|
-
if (!result)
|
|
675
|
-
return { error: `Collection '${id}' not found` };
|
|
676
|
-
return result;
|
|
677
|
-
}
|
|
678
|
-
case "neuron_api_create_collection": {
|
|
679
|
-
const collName = args.name;
|
|
680
|
-
const id = slugify(collName);
|
|
681
|
-
if (!id)
|
|
682
|
-
return { error: "Name produces an empty slug" };
|
|
683
|
-
const existing = await store.resolve(id);
|
|
684
|
-
if (existing) {
|
|
685
|
-
return { error: `Collection '${id}' already exists at ${existing.path}` };
|
|
686
|
-
}
|
|
687
|
-
const location = args.location ?? "global";
|
|
688
|
-
const collection = {
|
|
689
|
-
name: collName,
|
|
690
|
-
version: "1.0.0",
|
|
691
|
-
};
|
|
692
|
-
if (args.description)
|
|
693
|
-
collection.description = args.description;
|
|
694
|
-
if (args.base_url) {
|
|
695
|
-
collection.environments = {
|
|
696
|
-
default: { base_url: args.base_url },
|
|
697
|
-
};
|
|
698
|
-
collection.active_environment = "default";
|
|
699
|
-
}
|
|
700
|
-
collection.folders = {};
|
|
701
|
-
const path = await store.save(id, collection, location);
|
|
702
|
-
return { id, path, location };
|
|
703
|
-
}
|
|
704
|
-
case "neuron_api_delete_collection": {
|
|
705
|
-
const id = args.collection;
|
|
706
|
-
const deleted = await store.delete(id);
|
|
707
|
-
if (!deleted)
|
|
708
|
-
return { error: `Collection '${id}' not found` };
|
|
709
|
-
return { deleted: true, id };
|
|
710
|
-
}
|
|
711
|
-
case "neuron_api_upsert_endpoint": {
|
|
712
|
-
const id = args.collection;
|
|
713
|
-
const folder = args.folder;
|
|
714
|
-
const { collection: _c, folder: _f, ...endpointFields } = args;
|
|
715
|
-
const result = await store.upsertEndpoint(id, folder, endpointFields);
|
|
716
|
-
return {
|
|
717
|
-
action: result.action,
|
|
718
|
-
collection: id,
|
|
719
|
-
folder: slugify(folder),
|
|
720
|
-
method: args.method.toUpperCase(),
|
|
721
|
-
path: args.path,
|
|
722
|
-
};
|
|
723
|
-
}
|
|
724
|
-
case "neuron_api_remove_endpoint": {
|
|
725
|
-
const id = args.collection;
|
|
726
|
-
await store.removeEndpoint(id, args.folder, args.method, args.path);
|
|
727
|
-
return {
|
|
728
|
-
removed: true,
|
|
729
|
-
collection: id,
|
|
730
|
-
folder: args.folder,
|
|
731
|
-
method: args.method,
|
|
732
|
-
path: args.path,
|
|
733
|
-
};
|
|
734
|
-
}
|
|
735
|
-
case "neuron_api_move_endpoint": {
|
|
736
|
-
const id = args.collection;
|
|
737
|
-
await store.moveEndpoint(id, args.from_folder, args.to_folder, args.method, args.path);
|
|
738
|
-
return {
|
|
739
|
-
moved: true,
|
|
740
|
-
collection: id,
|
|
741
|
-
from: args.from_folder,
|
|
742
|
-
to: slugify(args.to_folder),
|
|
743
|
-
method: args.method,
|
|
744
|
-
path: args.path,
|
|
745
|
-
};
|
|
746
|
-
}
|
|
747
|
-
// ── Environment management ─────────────────────────────
|
|
748
|
-
case "neuron_api_set_env": {
|
|
749
|
-
const id = args.collection;
|
|
750
|
-
const envName = args.name;
|
|
751
|
-
const env = { base_url: args.base_url };
|
|
752
|
-
if (args.variables)
|
|
753
|
-
env.variables = args.variables;
|
|
754
|
-
if (args.headers)
|
|
755
|
-
env.headers = args.headers;
|
|
756
|
-
if (args.timeout_ms)
|
|
757
|
-
env.timeout_ms = args.timeout_ms;
|
|
758
|
-
await store.setEnvironment(id, envName, env);
|
|
759
|
-
return { set: true, collection: id, environment: slugify(envName) };
|
|
760
|
-
}
|
|
761
|
-
case "neuron_api_remove_env": {
|
|
762
|
-
const id = args.collection;
|
|
763
|
-
await store.removeEnvironment(id, args.name);
|
|
764
|
-
return { removed: true, collection: id, environment: args.name };
|
|
765
|
-
}
|
|
766
|
-
case "neuron_api_list_envs": {
|
|
767
|
-
const id = args.collection;
|
|
768
|
-
const result = await store.get(id);
|
|
769
|
-
if (!result)
|
|
770
|
-
return { error: `Collection '${id}' not found` };
|
|
771
|
-
const envs = result.collection.environments ?? {};
|
|
772
|
-
return {
|
|
773
|
-
collection: id,
|
|
774
|
-
active: result.collection.active_environment ?? null,
|
|
775
|
-
environments: Object.entries(envs).map(([key, env]) => ({
|
|
776
|
-
name: key,
|
|
777
|
-
base_url: env.base_url,
|
|
778
|
-
variable_count: env.variables ? Object.keys(env.variables).length : 0,
|
|
779
|
-
has_headers: !!env.headers && Object.keys(env.headers).length > 0,
|
|
780
|
-
timeout_ms: env.timeout_ms,
|
|
781
|
-
})),
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
case "neuron_api_switch_env": {
|
|
785
|
-
const id = args.collection;
|
|
786
|
-
await store.switchEnvironment(id, args.name);
|
|
787
|
-
return { switched: true, collection: id, active: slugify(args.name) };
|
|
788
|
-
}
|
|
789
|
-
// ── Collection metadata ──────────────────────────────
|
|
790
|
-
case "neuron_api_update_collection": {
|
|
791
|
-
const id = args.collection;
|
|
792
|
-
const updates = {};
|
|
793
|
-
if (args.name !== undefined)
|
|
794
|
-
updates.name = args.name;
|
|
795
|
-
if (args.description !== undefined)
|
|
796
|
-
updates.description = args.description;
|
|
797
|
-
if (args.version !== undefined)
|
|
798
|
-
updates.version = args.version;
|
|
799
|
-
if (Object.keys(updates).length === 0)
|
|
800
|
-
return { error: "No updates provided" };
|
|
801
|
-
await store.updateMetadata(id, updates);
|
|
802
|
-
return { updated: true, collection: id, ...updates };
|
|
803
|
-
}
|
|
804
|
-
// ── Defaults management ─────────────────────────────
|
|
805
|
-
case "neuron_api_set_defaults": {
|
|
806
|
-
const id = args.collection;
|
|
807
|
-
const { collection: _c, ...defaults } = args;
|
|
808
|
-
if (Object.keys(defaults).length === 0)
|
|
809
|
-
return { error: "No defaults provided" };
|
|
810
|
-
await store.setDefaults(id, defaults);
|
|
811
|
-
return { set: true, collection: id, fields: Object.keys(defaults) };
|
|
812
|
-
}
|
|
813
|
-
// ── Folder management ───────────────────────────────
|
|
814
|
-
case "neuron_api_create_folder": {
|
|
815
|
-
const id = args.collection;
|
|
816
|
-
const folderName = args.name;
|
|
817
|
-
const meta = {};
|
|
818
|
-
if (args.description)
|
|
819
|
-
meta.description = args.description;
|
|
820
|
-
if (args.headers)
|
|
821
|
-
meta.headers = args.headers;
|
|
822
|
-
if (args.variables)
|
|
823
|
-
meta.variables = args.variables;
|
|
824
|
-
if (args.auth)
|
|
825
|
-
meta.auth = args.auth;
|
|
826
|
-
await store.createFolder(id, folderName, meta);
|
|
827
|
-
return { created: true, collection: id, folder: slugify(folderName) };
|
|
828
|
-
}
|
|
829
|
-
case "neuron_api_update_folder": {
|
|
830
|
-
const id = args.collection;
|
|
831
|
-
const folderName = args.folder;
|
|
832
|
-
const updates = {};
|
|
833
|
-
if (args.description !== undefined)
|
|
834
|
-
updates.description = args.description;
|
|
835
|
-
if (args.headers !== undefined)
|
|
836
|
-
updates.headers = args.headers;
|
|
837
|
-
if (args.variables !== undefined)
|
|
838
|
-
updates.variables = args.variables;
|
|
839
|
-
if (args.auth !== undefined)
|
|
840
|
-
updates.auth = args.auth;
|
|
841
|
-
if (Object.keys(updates).length === 0)
|
|
842
|
-
return { error: "No updates provided" };
|
|
843
|
-
await store.updateFolder(id, folderName, updates);
|
|
844
|
-
return { updated: true, collection: id, folder: slugify(folderName), fields: Object.keys(updates) };
|
|
845
|
-
}
|
|
846
|
-
case "neuron_api_delete_folder": {
|
|
847
|
-
const id = args.collection;
|
|
848
|
-
await store.deleteFolder(id, args.folder);
|
|
849
|
-
return { deleted: true, collection: id, folder: args.folder };
|
|
850
|
-
}
|
|
851
|
-
case "neuron_api_list_folders": {
|
|
852
|
-
const id = args.collection;
|
|
853
|
-
const result = await store.get(id);
|
|
854
|
-
if (!result)
|
|
855
|
-
return { error: `Collection '${id}' not found` };
|
|
856
|
-
const folders = result.collection.folders ?? {};
|
|
857
|
-
return {
|
|
858
|
-
collection: id,
|
|
859
|
-
folders: Object.entries(folders).map(([key, f]) => ({
|
|
860
|
-
name: key,
|
|
861
|
-
endpoint_count: f.endpoints?.length ?? 0,
|
|
862
|
-
description: f.description ?? null,
|
|
863
|
-
has_headers: !!f.headers && Object.keys(f.headers).length > 0,
|
|
864
|
-
has_variables: !!f.variables && Object.keys(f.variables).length > 0,
|
|
865
|
-
has_auth: !!f.auth && f.auth.type !== "none",
|
|
866
|
-
})),
|
|
867
|
-
};
|
|
868
|
-
}
|
|
869
|
-
// ── Scenario management ─────────────────────────────
|
|
870
|
-
case "neuron_api_create_scenario": {
|
|
871
|
-
const id = args.collection;
|
|
872
|
-
const scenarioName = args.name;
|
|
873
|
-
const scenario = {
|
|
874
|
-
steps: args.steps,
|
|
875
|
-
};
|
|
876
|
-
if (args.description)
|
|
877
|
-
scenario.description = args.description;
|
|
878
|
-
await store.createScenario(id, scenarioName, scenario);
|
|
879
|
-
return { created: true, collection: id, scenario: scenarioName, step_count: scenario.steps.length };
|
|
880
|
-
}
|
|
881
|
-
case "neuron_api_update_scenario": {
|
|
882
|
-
const id = args.collection;
|
|
883
|
-
const scenarioName = args.scenario;
|
|
884
|
-
const updates = {};
|
|
885
|
-
if (args.description !== undefined)
|
|
886
|
-
updates.description = args.description;
|
|
887
|
-
if (args.steps !== undefined)
|
|
888
|
-
updates.steps = args.steps;
|
|
889
|
-
if (Object.keys(updates).length === 0)
|
|
890
|
-
return { error: "No updates provided" };
|
|
891
|
-
await store.updateScenario(id, scenarioName, updates);
|
|
892
|
-
return { updated: true, collection: id, scenario: scenarioName };
|
|
893
|
-
}
|
|
894
|
-
case "neuron_api_delete_scenario": {
|
|
895
|
-
const id = args.collection;
|
|
896
|
-
await store.deleteScenario(id, args.scenario);
|
|
897
|
-
return { deleted: true, collection: id, scenario: args.scenario };
|
|
898
|
-
}
|
|
899
|
-
case "neuron_api_list_scenarios": {
|
|
900
|
-
const id = args.collection;
|
|
901
|
-
const result = await store.get(id);
|
|
902
|
-
if (!result)
|
|
903
|
-
return { error: `Collection '${id}' not found` };
|
|
904
|
-
const scenarios = result.collection.scenarios ?? {};
|
|
905
|
-
return {
|
|
906
|
-
collection: id,
|
|
907
|
-
scenarios: Object.entries(scenarios).map(([key, s]) => ({
|
|
908
|
-
name: key,
|
|
909
|
-
description: s.description ?? null,
|
|
910
|
-
step_count: s.steps?.length ?? 0,
|
|
911
|
-
})),
|
|
912
|
-
};
|
|
913
|
-
}
|
|
914
|
-
// ── Execution ─────────────────────────────────────────
|
|
915
|
-
case "neuron_api_run": {
|
|
916
|
-
if (!httpExecutor || !pipeline || !assertionEvaluator) {
|
|
917
|
-
return { error: "HTTP executor not configured" };
|
|
918
|
-
}
|
|
919
|
-
const id = args.collection;
|
|
920
|
-
const result = await store.get(id);
|
|
921
|
-
if (!result)
|
|
922
|
-
return { error: `Collection '${id}' not found` };
|
|
923
|
-
const { collection } = result;
|
|
924
|
-
// Resolve environment
|
|
925
|
-
const envName = args.environment ?? collection.active_environment;
|
|
926
|
-
if (!envName) {
|
|
927
|
-
return { error: "No environment specified and no active_environment set on the collection" };
|
|
928
|
-
}
|
|
929
|
-
const environment = collection.environments?.[slugify(envName)];
|
|
930
|
-
if (!environment) {
|
|
931
|
-
return { error: `Environment '${envName}' not found in collection '${id}'` };
|
|
932
|
-
}
|
|
933
|
-
const hasFolder = typeof args.folder === "string";
|
|
934
|
-
const hasEndpoint = typeof args.method === "string" && typeof args.path === "string";
|
|
935
|
-
// Mode 3: Collection-wide run (no folder specified)
|
|
936
|
-
if (!hasFolder) {
|
|
937
|
-
return executeCollection({
|
|
938
|
-
collectionId: id,
|
|
939
|
-
collection,
|
|
940
|
-
environment,
|
|
941
|
-
httpExecutor,
|
|
942
|
-
pipeline,
|
|
943
|
-
assertionEvaluator,
|
|
944
|
-
scriptRunner,
|
|
945
|
-
});
|
|
946
|
-
}
|
|
947
|
-
const folderKey = slugify(args.folder);
|
|
948
|
-
const folder = collection.folders?.[folderKey];
|
|
949
|
-
if (!folder) {
|
|
950
|
-
return { error: `Folder '${args.folder}' not found in collection '${id}'` };
|
|
951
|
-
}
|
|
952
|
-
// Mode 2: Folder run (folder but no method/path)
|
|
953
|
-
if (!hasEndpoint) {
|
|
954
|
-
return executeFolder({
|
|
955
|
-
folderKey,
|
|
956
|
-
collection,
|
|
957
|
-
environment,
|
|
958
|
-
folder,
|
|
959
|
-
httpExecutor,
|
|
960
|
-
pipeline,
|
|
961
|
-
assertionEvaluator,
|
|
962
|
-
scriptRunner,
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
// Mode 1: Single endpoint
|
|
966
|
-
const method = args.method.toUpperCase();
|
|
967
|
-
const foundEndpoint = folder.endpoints?.find((ep) => ep.method.toUpperCase() === method && ep.path === args.path);
|
|
968
|
-
if (!foundEndpoint) {
|
|
969
|
-
return { error: `Endpoint ${method} ${args.path} not found in folder '${args.folder}'` };
|
|
970
|
-
}
|
|
971
|
-
// Apply per-run overrides
|
|
972
|
-
const endpoint = { ...foundEndpoint };
|
|
973
|
-
if (args.cookies || args.timeout_ms || args.follow_redirects !== undefined || args.max_redirects || args.verify_ssl !== undefined || args.follow_original_method !== undefined || args.follow_authorization !== undefined || args.remove_referer !== undefined || args.encode_url !== undefined) {
|
|
974
|
-
if (args.cookies) {
|
|
975
|
-
endpoint.cookies = { ...(endpoint.cookies ?? {}), ...args.cookies };
|
|
976
|
-
}
|
|
977
|
-
const runSettings = { ...(endpoint.settings ?? {}) };
|
|
978
|
-
if (args.timeout_ms !== undefined)
|
|
979
|
-
runSettings.timeout_ms = args.timeout_ms;
|
|
980
|
-
if (args.follow_redirects !== undefined)
|
|
981
|
-
runSettings.follow_redirects = args.follow_redirects;
|
|
982
|
-
if (args.max_redirects !== undefined)
|
|
983
|
-
runSettings.max_redirects = args.max_redirects;
|
|
984
|
-
if (args.verify_ssl !== undefined)
|
|
985
|
-
runSettings.verify_ssl = args.verify_ssl;
|
|
986
|
-
if (args.follow_original_method !== undefined)
|
|
987
|
-
runSettings.follow_original_method = args.follow_original_method;
|
|
988
|
-
if (args.follow_authorization !== undefined)
|
|
989
|
-
runSettings.follow_authorization = args.follow_authorization;
|
|
990
|
-
if (args.remove_referer !== undefined)
|
|
991
|
-
runSettings.remove_referer = args.remove_referer;
|
|
992
|
-
if (args.encode_url !== undefined)
|
|
993
|
-
runSettings.encode_url = args.encode_url;
|
|
994
|
-
endpoint.settings = runSettings;
|
|
995
|
-
}
|
|
996
|
-
return executeEndpoint({
|
|
997
|
-
collection,
|
|
998
|
-
environment,
|
|
999
|
-
folder,
|
|
1000
|
-
endpoint,
|
|
1001
|
-
httpExecutor,
|
|
1002
|
-
pipeline,
|
|
1003
|
-
assertionEvaluator,
|
|
1004
|
-
scriptRunner,
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
case "neuron_api_run_scenario": {
|
|
1008
|
-
if (!httpExecutor || !pipeline || !assertionEvaluator) {
|
|
1009
|
-
return { error: "HTTP executor not configured" };
|
|
1010
|
-
}
|
|
1011
|
-
const id = args.collection;
|
|
1012
|
-
const result = await store.get(id);
|
|
1013
|
-
if (!result)
|
|
1014
|
-
return { error: `Collection '${id}' not found` };
|
|
1015
|
-
const { collection } = result;
|
|
1016
|
-
// Resolve environment
|
|
1017
|
-
const envName = args.environment ?? collection.active_environment;
|
|
1018
|
-
if (!envName) {
|
|
1019
|
-
return { error: "No environment specified and no active_environment set on the collection" };
|
|
1020
|
-
}
|
|
1021
|
-
const environment = collection.environments?.[slugify(envName)];
|
|
1022
|
-
if (!environment) {
|
|
1023
|
-
return { error: `Environment '${envName}' not found in collection '${id}'` };
|
|
1024
|
-
}
|
|
1025
|
-
// Find scenario
|
|
1026
|
-
const scenarioKey = args.scenario;
|
|
1027
|
-
const scenario = collection.scenarios?.[scenarioKey];
|
|
1028
|
-
if (!scenario) {
|
|
1029
|
-
return { error: `Scenario '${scenarioKey}' not found in collection '${id}'` };
|
|
1030
|
-
}
|
|
1031
|
-
return executeScenario({
|
|
1032
|
-
scenarioKey,
|
|
1033
|
-
steps: scenario.steps,
|
|
1034
|
-
collectionId: id,
|
|
1035
|
-
collection,
|
|
1036
|
-
environment,
|
|
1037
|
-
httpExecutor,
|
|
1038
|
-
pipeline,
|
|
1039
|
-
assertionEvaluator,
|
|
1040
|
-
scriptRunner,
|
|
1041
|
-
});
|
|
1042
|
-
}
|
|
1043
|
-
// ── Query and generation ─────────────────────────────
|
|
1044
|
-
case "neuron_api_query": {
|
|
1045
|
-
const collections = await store.list();
|
|
1046
|
-
const targetIds = args.collection
|
|
1047
|
-
? [args.collection]
|
|
1048
|
-
: collections.map((c) => c.id);
|
|
1049
|
-
const results = [];
|
|
1050
|
-
const methodFilter = args.method ? args.method.toUpperCase() : undefined;
|
|
1051
|
-
const pathFilter = args.path;
|
|
1052
|
-
const folderFilter = args.folder ? slugify(args.folder) : undefined;
|
|
1053
|
-
const tagsFilter = args.tags;
|
|
1054
|
-
const textFilter = args.text ? args.text.toLowerCase() : undefined;
|
|
1055
|
-
for (const collId of targetIds) {
|
|
1056
|
-
const result = await store.get(collId);
|
|
1057
|
-
if (!result)
|
|
1058
|
-
continue;
|
|
1059
|
-
const { collection } = result;
|
|
1060
|
-
for (const [folderKey, folder] of Object.entries(collection.folders ?? {})) {
|
|
1061
|
-
if (folderFilter && folderKey !== folderFilter)
|
|
1062
|
-
continue;
|
|
1063
|
-
for (const ep of folder.endpoints ?? []) {
|
|
1064
|
-
if (methodFilter && ep.method.toUpperCase() !== methodFilter)
|
|
1065
|
-
continue;
|
|
1066
|
-
if (pathFilter && !ep.path.includes(pathFilter))
|
|
1067
|
-
continue;
|
|
1068
|
-
if (tagsFilter?.length && !tagsFilter.some((t) => ep.tags?.includes(t)))
|
|
1069
|
-
continue;
|
|
1070
|
-
if (textFilter) {
|
|
1071
|
-
const searchable = [ep.name, ep.description ?? "", ep.path].join(" ").toLowerCase();
|
|
1072
|
-
if (!searchable.includes(textFilter))
|
|
1073
|
-
continue;
|
|
1074
|
-
}
|
|
1075
|
-
results.push({
|
|
1076
|
-
collection: collId,
|
|
1077
|
-
folder: folderKey,
|
|
1078
|
-
endpoint: {
|
|
1079
|
-
name: ep.name,
|
|
1080
|
-
method: ep.method,
|
|
1081
|
-
path: ep.path,
|
|
1082
|
-
...(ep.description && { description: ep.description }),
|
|
1083
|
-
...(ep.tags?.length && { tags: ep.tags }),
|
|
1084
|
-
},
|
|
1085
|
-
});
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
return { count: results.length, results };
|
|
1090
|
-
}
|
|
1091
|
-
case "neuron_api_generate_tests": {
|
|
1092
|
-
const id = args.collection;
|
|
1093
|
-
const result = await store.get(id);
|
|
1094
|
-
if (!result)
|
|
1095
|
-
return { error: `Collection '${id}' not found` };
|
|
1096
|
-
const { collection } = result;
|
|
1097
|
-
const folderKey = slugify(args.folder);
|
|
1098
|
-
const folder = collection.folders?.[folderKey];
|
|
1099
|
-
if (!folder)
|
|
1100
|
-
return { error: `Folder '${args.folder}' not found` };
|
|
1101
|
-
const method = args.method.toUpperCase();
|
|
1102
|
-
const endpoint = folder.endpoints?.find((ep) => ep.method.toUpperCase() === method && ep.path === args.path);
|
|
1103
|
-
if (!endpoint)
|
|
1104
|
-
return { error: `Endpoint ${method} ${args.path} not found` };
|
|
1105
|
-
const suggestions = [];
|
|
1106
|
-
// Status code suggestions based on method
|
|
1107
|
-
switch (method) {
|
|
1108
|
-
case "GET":
|
|
1109
|
-
suggestions.push({ check: "status", expect: 200, reason: "GET should return 200 OK" });
|
|
1110
|
-
break;
|
|
1111
|
-
case "POST":
|
|
1112
|
-
suggestions.push({ check: "status", expect: 201, reason: "POST creating a resource should return 201 Created" });
|
|
1113
|
-
break;
|
|
1114
|
-
case "PUT":
|
|
1115
|
-
case "PATCH":
|
|
1116
|
-
suggestions.push({ check: "status", expect: 200, reason: `${method} should return 200 OK` });
|
|
1117
|
-
break;
|
|
1118
|
-
case "DELETE":
|
|
1119
|
-
suggestions.push({ check: "status", expect: 200, reason: "DELETE should return 200 OK or 204 No Content" });
|
|
1120
|
-
break;
|
|
1121
|
-
default:
|
|
1122
|
-
suggestions.push({ check: "status", expect: 200, reason: "Expected success status" });
|
|
1123
|
-
}
|
|
1124
|
-
// Timing suggestion
|
|
1125
|
-
suggestions.push({
|
|
1126
|
-
check: "timing.total",
|
|
1127
|
-
op: "lt",
|
|
1128
|
-
expect: 2000,
|
|
1129
|
-
reason: "Response should complete within 2 seconds",
|
|
1130
|
-
});
|
|
1131
|
-
// Content-type suggestion
|
|
1132
|
-
suggestions.push({
|
|
1133
|
-
check: "header.content-type",
|
|
1134
|
-
op: "contains",
|
|
1135
|
-
expect: "json",
|
|
1136
|
-
reason: "API should return JSON content type",
|
|
1137
|
-
});
|
|
1138
|
-
// ID field suggestion for POST/GET with resource
|
|
1139
|
-
if (method === "POST") {
|
|
1140
|
-
suggestions.push({
|
|
1141
|
-
check: "body.id",
|
|
1142
|
-
op: "exists",
|
|
1143
|
-
reason: "Created resource should return an ID",
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
|
-
// Array suggestion for GET list endpoints (path doesn't end with a variable)
|
|
1147
|
-
if (method === "GET" && !endpoint.path.includes("{{")) {
|
|
1148
|
-
suggestions.push({
|
|
1149
|
-
check: "body",
|
|
1150
|
-
op: "isArray",
|
|
1151
|
-
reason: "GET list endpoint should return an array",
|
|
1152
|
-
});
|
|
1153
|
-
}
|
|
1154
|
-
// 404 error scenario for endpoints with path parameters
|
|
1155
|
-
if (endpoint.path.includes("{{")) {
|
|
1156
|
-
suggestions.push({
|
|
1157
|
-
check: "status",
|
|
1158
|
-
expect: 404,
|
|
1159
|
-
reason: "Error scenario: non-existent resource should return 404 (use with a fake ID)",
|
|
1160
|
-
});
|
|
1161
|
-
}
|
|
1162
|
-
return {
|
|
1163
|
-
endpoint: { method: endpoint.method, path: endpoint.path, name: endpoint.name },
|
|
1164
|
-
existing_tests: endpoint.tests?.length ?? 0,
|
|
1165
|
-
suggestions,
|
|
1166
|
-
};
|
|
1167
|
-
}
|
|
1168
|
-
// ── Import / Export ─────────────────────────────────────
|
|
1169
|
-
case "neuron_api_export_collection": {
|
|
1170
|
-
const id = args.collection;
|
|
1171
|
-
const format = args.format ?? "yaml";
|
|
1172
|
-
const result = await store.get(id);
|
|
1173
|
-
if (!result)
|
|
1174
|
-
return { error: `Collection '${id}' not found` };
|
|
1175
|
-
let content;
|
|
1176
|
-
let filename;
|
|
1177
|
-
if (format === "postman") {
|
|
1178
|
-
const postman = toPostmanV21(result.collection, id);
|
|
1179
|
-
content = JSON.stringify(postman, null, 2);
|
|
1180
|
-
filename = `${id}.postman_collection.json`;
|
|
1181
|
-
}
|
|
1182
|
-
else {
|
|
1183
|
-
content = YAML.stringify(result.collection, { lineWidth: 120 });
|
|
1184
|
-
filename = `${id}.yaml`;
|
|
1185
|
-
}
|
|
1186
|
-
return { content, format, filename };
|
|
1187
|
-
}
|
|
1188
|
-
case "neuron_api_import_collection": {
|
|
1189
|
-
const raw = args.content;
|
|
1190
|
-
const formatHint = args.format ?? "auto";
|
|
1191
|
-
const location = args.location ?? "global";
|
|
1192
|
-
const imported = parseImportContent(raw, formatHint);
|
|
1193
|
-
if (args.name)
|
|
1194
|
-
imported.name = args.name;
|
|
1195
|
-
const id = slugify(imported.name);
|
|
1196
|
-
if (!id)
|
|
1197
|
-
return { error: "Collection name produces an empty slug" };
|
|
1198
|
-
const savePath = await store.save(id, imported, location);
|
|
1199
|
-
const endpointCount = imported.folders
|
|
1200
|
-
? Object.values(imported.folders).reduce((s, f) => s + (f.endpoints?.length ?? 0), 0)
|
|
1201
|
-
: 0;
|
|
1202
|
-
return { id, name: imported.name, endpoint_count: endpointCount, path: savePath };
|
|
1203
|
-
}
|
|
1204
|
-
case "neuron_api_import_file": {
|
|
1205
|
-
const filePath = args.path;
|
|
1206
|
-
const location = args.location ?? "global";
|
|
1207
|
-
let raw;
|
|
1208
|
-
try {
|
|
1209
|
-
raw = await readFile(filePath, "utf-8");
|
|
1210
|
-
}
|
|
1211
|
-
catch (e) {
|
|
1212
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
1213
|
-
return { error: `Cannot read file: ${msg}` };
|
|
1214
|
-
}
|
|
1215
|
-
// Detect format from extension
|
|
1216
|
-
const ext = filePath.toLowerCase();
|
|
1217
|
-
let formatHint = "auto";
|
|
1218
|
-
if (ext.endsWith(".json") || ext.endsWith(".postman_collection.json")) {
|
|
1219
|
-
formatHint = "postman";
|
|
1220
|
-
}
|
|
1221
|
-
else if (ext.endsWith(".yaml") || ext.endsWith(".yml")) {
|
|
1222
|
-
formatHint = "yaml";
|
|
1223
|
-
}
|
|
1224
|
-
const imported = parseImportContent(raw, formatHint);
|
|
1225
|
-
if (args.name)
|
|
1226
|
-
imported.name = args.name;
|
|
1227
|
-
const id = slugify(imported.name);
|
|
1228
|
-
if (!id)
|
|
1229
|
-
return { error: "Collection name produces an empty slug" };
|
|
1230
|
-
const savePath = await store.save(id, imported, location);
|
|
1231
|
-
const endpointCount = imported.folders
|
|
1232
|
-
? Object.values(imported.folders).reduce((s, f) => s + (f.endpoints?.length ?? 0), 0)
|
|
1233
|
-
: 0;
|
|
1234
|
-
return { id, name: imported.name, endpoint_count: endpointCount, path: savePath };
|
|
1235
|
-
}
|
|
1236
|
-
default:
|
|
1237
|
-
return { error: `Unknown collection tool: ${name}` };
|
|
1238
|
-
}
|
|
1239
|
-
};
|
|
1240
|
-
}
|
|
1241
|
-
// ── Import helpers ────────────────────────────────────────────────
|
|
1242
|
-
function isPostmanJson(obj) {
|
|
1243
|
-
if (typeof obj !== "object" || obj === null)
|
|
1244
|
-
return false;
|
|
1245
|
-
const root = obj;
|
|
1246
|
-
const info = root.info;
|
|
1247
|
-
if (!info)
|
|
1248
|
-
return false;
|
|
1249
|
-
const schema = info.schema;
|
|
1250
|
-
return typeof schema === "string" && schema.includes("postman");
|
|
1251
|
-
}
|
|
1252
|
-
function parseImportContent(raw, formatHint) {
|
|
1253
|
-
// Try JSON first if hint suggests Postman or auto
|
|
1254
|
-
if (formatHint === "postman" || formatHint === "auto") {
|
|
1255
|
-
try {
|
|
1256
|
-
const parsed = JSON.parse(raw);
|
|
1257
|
-
if (formatHint === "postman" || isPostmanJson(parsed)) {
|
|
1258
|
-
return fromPostmanV21(parsed);
|
|
1259
|
-
}
|
|
1260
|
-
// Valid JSON but not Postman — fall through to YAML
|
|
1261
|
-
}
|
|
1262
|
-
catch {
|
|
1263
|
-
if (formatHint === "postman") {
|
|
1264
|
-
throw new Error("Content is not valid JSON for Postman format");
|
|
1265
|
-
}
|
|
1266
|
-
// Not JSON — try YAML
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
// Parse as YAML
|
|
1270
|
-
const parsed = YAML.parse(raw);
|
|
1271
|
-
if (typeof parsed !== "object" || parsed === null || typeof parsed.name !== "string") {
|
|
1272
|
-
throw new Error("Invalid collection: must be an object with a 'name' field");
|
|
1273
|
-
}
|
|
1274
|
-
return parsed;
|
|
1275
|
-
}
|
|
1276
|
-
//# sourceMappingURL=tools.js.map
|