jetic-cli 0.1.7 → 0.1.9
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/index.js +718 -24
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -268509,7 +268509,7 @@ var require_endpoint_tools = __commonJS({
|
|
|
268509
268509
|
"use strict";
|
|
268510
268510
|
init_cjs_shims();
|
|
268511
268511
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
268512
|
-
exports2.deleteEndpointSchema = exports2.updateEndpointSchema = exports2.addEndpointSchema = void 0;
|
|
268512
|
+
exports2.deleteEndpointSchema = exports2.updateEndpointSchema = exports2.addEndpointSchema = exports2.constraintSchema = void 0;
|
|
268513
268513
|
exports2.handleAddEndpoint = handleAddEndpoint;
|
|
268514
268514
|
exports2.handleUpdateEndpoint = handleUpdateEndpoint;
|
|
268515
268515
|
exports2.handleDeleteEndpoint = handleDeleteEndpoint;
|
|
@@ -268517,11 +268517,13 @@ var require_endpoint_tools = __commonJS({
|
|
|
268517
268517
|
var crypto_1 = require("crypto");
|
|
268518
268518
|
var types_1 = require_types();
|
|
268519
268519
|
var parameterSchema = zod_1.z.object({
|
|
268520
|
-
name: zod_1.z.string().describe("Parameter name"),
|
|
268520
|
+
name: zod_1.z.string().min(1).describe("Parameter name"),
|
|
268521
268521
|
in: zod_1.z.enum(["query", "path", "header", "cookie", "body"]).optional().describe("Parameter location in HTTP request"),
|
|
268522
268522
|
type: zod_1.z.string().default("string").describe("Data type (string, number, boolean, array, object)"),
|
|
268523
268523
|
format: zod_1.z.string().optional().describe("Format specifier (e.g. uuid, email, date-time, int64)"),
|
|
268524
268524
|
required: zod_1.z.boolean().optional().describe("Whether parameter is required"),
|
|
268525
|
+
min: zod_1.z.number().optional().describe("Minimum numeric value"),
|
|
268526
|
+
max: zod_1.z.number().optional().describe("Maximum numeric value"),
|
|
268525
268527
|
default: zod_1.z.any().optional().describe("Default value"),
|
|
268526
268528
|
example: zod_1.z.any().optional().describe("Example value"),
|
|
268527
268529
|
description: zod_1.z.string().optional().describe("Description")
|
|
@@ -268530,46 +268532,115 @@ var require_endpoint_tools = __commonJS({
|
|
|
268530
268532
|
type: zod_1.z.string().describe("Data type (string, number, boolean, array, object)"),
|
|
268531
268533
|
format: zod_1.z.string().optional().describe("Format specifier"),
|
|
268532
268534
|
required: zod_1.z.boolean().optional().describe("Required flag"),
|
|
268535
|
+
min: zod_1.z.number().optional().describe("Minimum numeric value"),
|
|
268536
|
+
max: zod_1.z.number().optional().describe("Maximum numeric value"),
|
|
268537
|
+
minLength: zod_1.z.number().optional().describe("Minimum string length"),
|
|
268538
|
+
maxLength: zod_1.z.number().optional().describe("Maximum string length"),
|
|
268533
268539
|
description: zod_1.z.string().optional().describe("Description"),
|
|
268534
268540
|
example: zod_1.z.any().optional().describe("Example value"),
|
|
268535
268541
|
default: zod_1.z.any().optional().describe("Default value"),
|
|
268536
268542
|
enum: zod_1.z.array(zod_1.z.string()).optional().describe("Allowed enum values")
|
|
268537
268543
|
});
|
|
268544
|
+
exports2.constraintSchema = zod_1.z.object({
|
|
268545
|
+
field: zod_1.z.string().optional().describe("Field this constraint applies to"),
|
|
268546
|
+
rule: zod_1.z.string().optional().describe("Rule name (e.g. minLength, matches, requiredIf)"),
|
|
268547
|
+
value: zod_1.z.any().optional().describe("Rule value / threshold"),
|
|
268548
|
+
conflictStatus: zod_1.z.number().int().optional().describe("HTTP status on conflict"),
|
|
268549
|
+
failureStatus: zod_1.z.number().int().optional().describe("HTTP status on validation failure"),
|
|
268550
|
+
when: zod_1.z.object({
|
|
268551
|
+
field: zod_1.z.string().describe("Condition field"),
|
|
268552
|
+
present: zod_1.z.boolean().optional(),
|
|
268553
|
+
equals: zod_1.z.any().optional()
|
|
268554
|
+
}).optional().describe("Conditional constraint trigger"),
|
|
268555
|
+
then: zod_1.z.object({
|
|
268556
|
+
field: zod_1.z.string().describe("Field to constrain"),
|
|
268557
|
+
type: zod_1.z.string().describe("Expected type when condition holds")
|
|
268558
|
+
}).optional().describe("Conditional constraint effect")
|
|
268559
|
+
});
|
|
268538
268560
|
var requestBodySchema = zod_1.z.object({
|
|
268539
268561
|
contentType: zod_1.z.string().nullable().default("application/json").describe("Content-Type header (e.g. application/json, multipart/form-data)"),
|
|
268540
268562
|
required: zod_1.z.boolean().optional().describe("Whether request body is required"),
|
|
268541
|
-
fields: zod_1.z.record(fieldDefinitionSchema).default({}).describe("Key-value record of payload fields")
|
|
268563
|
+
fields: zod_1.z.record(fieldDefinitionSchema).default({}).describe("Key-value record of payload fields"),
|
|
268564
|
+
constraints: zod_1.z.array(exports2.constraintSchema).optional().describe("Business-rule constraints extracted from handler logic (e.g. password minLength)")
|
|
268542
268565
|
});
|
|
268543
268566
|
var responseDefinitionSchema = zod_1.z.object({
|
|
268544
268567
|
contentType: zod_1.z.string().optional().default("application/json"),
|
|
268545
268568
|
description: zod_1.z.string().optional().describe("Human readable response description"),
|
|
268546
|
-
schema: zod_1.z.record(zod_1.z.string()).optional().describe("Response body schema description (field -> type)"),
|
|
268547
|
-
example: zod_1.z.any().optional().describe("Response payload example")
|
|
268569
|
+
schema: zod_1.z.record(zod_1.z.string()).optional().describe("Response body schema description (field -> type, dot paths allowed)"),
|
|
268570
|
+
example: zod_1.z.any().optional().describe("Response payload example"),
|
|
268571
|
+
condition: zod_1.z.string().optional().describe("Condition under which this response occurs"),
|
|
268572
|
+
ownershipCheck: zod_1.z.boolean().optional().describe("Whether this response enforces an ownership check")
|
|
268548
268573
|
});
|
|
268549
268574
|
var endpointSecuritySchema = zod_1.z.object({
|
|
268550
|
-
scheme: zod_1.z.string().describe("Name of security scheme (e.g. bearerAuth, apiKey)"),
|
|
268575
|
+
scheme: zod_1.z.string().describe("Name of security scheme declared in top-level securitySchemes (e.g. bearerAuth, apiKey)"),
|
|
268551
268576
|
required: zod_1.z.boolean().optional().default(true),
|
|
268552
|
-
scopes: zod_1.z.array(zod_1.z.string()).optional()
|
|
268577
|
+
scopes: zod_1.z.array(zod_1.z.string()).optional().describe("OAuth2 scopes (only for oauth2 schemes)")
|
|
268553
268578
|
});
|
|
268554
268579
|
var sourceReferenceSchema = zod_1.z.object({
|
|
268555
268580
|
file: zod_1.z.string().describe("Relative or absolute path to source file containing handler/route"),
|
|
268556
268581
|
line: zod_1.z.number().describe("Line number in source file"),
|
|
268557
268582
|
column: zod_1.z.number().optional()
|
|
268558
268583
|
});
|
|
268584
|
+
var middlewareSchema = zod_1.z.object({
|
|
268585
|
+
name: zod_1.z.string().min(1).describe('Middleware name as in code (e.g. "auth", "cors", "express.json", "rateLimit")'),
|
|
268586
|
+
type: zod_1.z.string().optional().describe('Middleware category (e.g. "auth", "parser", "cors", "rate-limit", "validation", "logging")'),
|
|
268587
|
+
scheme: zod_1.z.string().optional().describe("Security scheme this middleware enforces (e.g. bearerAuth)"),
|
|
268588
|
+
config: zod_1.z.record(zod_1.z.any()).optional().describe('Middleware options (e.g. { "max": 100, "windowMs": 60000 })'),
|
|
268589
|
+
source: sourceReferenceSchema.optional().describe("Where the middleware is applied in source")
|
|
268590
|
+
});
|
|
268591
|
+
var paginationSchema = zod_1.z.object({
|
|
268592
|
+
type: zod_1.z.enum(["offset", "cursor", "page"]).describe("Pagination strategy"),
|
|
268593
|
+
pageParam: zod_1.z.string().optional().describe('Query param for page number/offset (e.g. "page")'),
|
|
268594
|
+
limitParam: zod_1.z.string().optional().describe('Query param for page size (e.g. "limit")'),
|
|
268595
|
+
cursorParam: zod_1.z.string().optional().describe("Query param for cursor (cursor strategy)"),
|
|
268596
|
+
totalPath: zod_1.z.string().optional().describe('Response path to total count (e.g. "meta.total")')
|
|
268597
|
+
});
|
|
268598
|
+
var rateLimitSchema = zod_1.z.object({
|
|
268599
|
+
windowMs: zod_1.z.number().int().positive().describe("Rate-limit window in milliseconds"),
|
|
268600
|
+
max: zod_1.z.number().int().positive().describe("Max requests per window"),
|
|
268601
|
+
scope: zod_1.z.string().optional().describe('Limit scope (e.g. "ip", "user", "global")')
|
|
268602
|
+
});
|
|
268603
|
+
var ownershipSchema = zod_1.z.object({
|
|
268604
|
+
ownerField: zod_1.z.string().describe('Resource field holding the owner id (e.g. "userId")'),
|
|
268605
|
+
mustMatchAuthClaim: zod_1.z.string().describe('Auth claim it must equal (e.g. "sub")')
|
|
268606
|
+
});
|
|
268607
|
+
var producesSchema = zod_1.z.object({
|
|
268608
|
+
variable: zod_1.z.string().optional().describe('Memory variable name produced (e.g. "accessToken")'),
|
|
268609
|
+
responseField: zod_1.z.string().optional().describe('Response path the value comes from (e.g. "data.accessToken")'),
|
|
268610
|
+
resourceStateChange: zod_1.z.object({
|
|
268611
|
+
resource: zod_1.z.string(),
|
|
268612
|
+
field: zod_1.z.string(),
|
|
268613
|
+
setFrom: zod_1.z.string()
|
|
268614
|
+
}).optional().describe("Resource state mutation caused by this endpoint")
|
|
268615
|
+
});
|
|
268616
|
+
var consumesSchema = zod_1.z.object({
|
|
268617
|
+
variable: zod_1.z.string().describe('Memory variable consumed (e.g. "accessToken")'),
|
|
268618
|
+
usedAs: zod_1.z.string().describe('How it is used: "header:<Name>" (Bearer prefix auto-added for Authorization) or "body:<field>" / "query:<param>"'),
|
|
268619
|
+
producedBy: zod_1.z.string().optional().describe('"METHOD /path" of the endpoint that produces it')
|
|
268620
|
+
});
|
|
268559
268621
|
exports2.addEndpointSchema = zod_1.z.object({
|
|
268560
268622
|
projectPath: zod_1.z.string().optional().describe("Path to project root or .jetic folder"),
|
|
268561
268623
|
method: zod_1.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]).describe("HTTP method"),
|
|
268562
268624
|
path: zod_1.z.string().describe("Route path (e.g., /api/v1/users or /api/users/:id)"),
|
|
268563
268625
|
name: zod_1.z.string().optional().describe('Human-readable endpoint name (e.g., "Create User")'),
|
|
268626
|
+
summary: zod_1.z.string().optional().describe("Short one-line summary"),
|
|
268564
268627
|
description: zod_1.z.string().optional().describe("Detailed description of endpoint functionality"),
|
|
268565
268628
|
tags: zod_1.z.array(zod_1.z.string()).optional().describe('Category tags (e.g., ["users", "auth"])'),
|
|
268629
|
+
deprecated: zod_1.z.boolean().optional().describe("Mark endpoint as deprecated"),
|
|
268630
|
+
timeout: zod_1.z.number().int().positive().optional().describe("Request timeout in milliseconds"),
|
|
268566
268631
|
handlerName: zod_1.z.string().optional().describe("Handler / controller function name (e.g. UserController.create)"),
|
|
268567
268632
|
resource: zod_1.z.string().optional().describe("Associated domain resource (e.g. User, Order)"),
|
|
268568
268633
|
source: sourceReferenceSchema.optional().describe("Source code provenance location"),
|
|
268569
|
-
security: zod_1.z.array(endpointSecuritySchema).optional().describe("Security requirements"),
|
|
268634
|
+
security: zod_1.z.array(endpointSecuritySchema).optional().describe("Security requirements (schemes must exist in top-level securitySchemes)"),
|
|
268635
|
+
middleware: zod_1.z.array(middlewareSchema).optional().describe("Middleware chain IN EXECUTION ORDER (e.g. cors \u2192 json parser \u2192 auth \u2192 rateLimit \u2192 validator)"),
|
|
268570
268636
|
parameters: zod_1.z.array(parameterSchema).optional().describe("Query, path, header, or cookie parameters"),
|
|
268571
|
-
requestBody: requestBodySchema.optional().describe("Request body specification"),
|
|
268637
|
+
requestBody: requestBodySchema.optional().describe("Request body specification incl. business-rule constraints"),
|
|
268572
268638
|
responses: zod_1.z.record(responseDefinitionSchema).optional().describe('Response definitions keyed by status code (e.g. "200", "400")'),
|
|
268639
|
+
pagination: paginationSchema.optional().describe("Pagination config for list endpoints"),
|
|
268640
|
+
rateLimit: rateLimitSchema.optional().describe("Rate-limit config"),
|
|
268641
|
+
ownership: ownershipSchema.optional().describe("Ownership / authorization rule"),
|
|
268642
|
+
produces: zod_1.z.array(producesSchema).optional().describe("Memory variables produced from responses (feeds workflows)"),
|
|
268643
|
+
consumes: zod_1.z.array(consumesSchema).optional().describe("Memory variables consumed as headers/body (chained auth)"),
|
|
268573
268644
|
overwriteIfExists: zod_1.z.boolean().optional().default(true).describe("If endpoint with same method and path exists, update it instead of erroring")
|
|
268574
268645
|
});
|
|
268575
268646
|
function handleAddEndpoint(args) {
|
|
@@ -268581,6 +268652,11 @@ var require_endpoint_tools = __commonJS({
|
|
|
268581
268652
|
if (existingIndex >= 0 && !overwriteIfExists) {
|
|
268582
268653
|
throw new Error(`Endpoint ${methodUpper} ${normalizedPath} already exists in ${filePath}. Set overwriteIfExists: true to update it.`);
|
|
268583
268654
|
}
|
|
268655
|
+
for (const mw of args.middleware || []) {
|
|
268656
|
+
if (!mw.name || !mw.name.trim()) {
|
|
268657
|
+
throw new Error('Middleware entries must have a non-empty "name".');
|
|
268658
|
+
}
|
|
268659
|
+
}
|
|
268584
268660
|
const source = args.source || {
|
|
268585
268661
|
file: "manual-mcp",
|
|
268586
268662
|
line: 1
|
|
@@ -268599,21 +268675,31 @@ var require_endpoint_tools = __commonJS({
|
|
|
268599
268675
|
description: "Internal server error"
|
|
268600
268676
|
}
|
|
268601
268677
|
};
|
|
268678
|
+
const previous = existingIndex >= 0 ? model.endpoints[existingIndex] : void 0;
|
|
268602
268679
|
const newEndpoint = {
|
|
268603
|
-
id:
|
|
268680
|
+
id: previous?.id || (0, crypto_1.randomUUID)(),
|
|
268604
268681
|
method: methodUpper,
|
|
268605
268682
|
path: normalizedPath,
|
|
268606
268683
|
name: args.name || `${methodUpper} ${normalizedPath}`,
|
|
268684
|
+
summary: args.summary,
|
|
268607
268685
|
description: args.description,
|
|
268608
268686
|
tags: args.tags || [],
|
|
268687
|
+
deprecated: args.deprecated,
|
|
268688
|
+
timeout: args.timeout,
|
|
268609
268689
|
handlerName: args.handlerName,
|
|
268610
268690
|
resource: args.resource,
|
|
268611
268691
|
source,
|
|
268612
268692
|
security: args.security,
|
|
268693
|
+
// Explicit middleware wins; otherwise preserve the previous chain (never silently drop it).
|
|
268694
|
+
middleware: args.middleware || previous?.middleware || [],
|
|
268613
268695
|
parameters: args.parameters,
|
|
268614
268696
|
requestBody: args.requestBody,
|
|
268615
268697
|
responses,
|
|
268616
|
-
|
|
268698
|
+
pagination: args.pagination,
|
|
268699
|
+
rateLimit: args.rateLimit || previous?.rateLimit,
|
|
268700
|
+
ownership: args.ownership,
|
|
268701
|
+
produces: args.produces,
|
|
268702
|
+
consumes: args.consumes
|
|
268617
268703
|
};
|
|
268618
268704
|
if (existingIndex >= 0) {
|
|
268619
268705
|
model.endpoints[existingIndex] = newEndpoint;
|
|
@@ -268621,31 +268707,45 @@ var require_endpoint_tools = __commonJS({
|
|
|
268621
268707
|
model.endpoints.push(newEndpoint);
|
|
268622
268708
|
}
|
|
268623
268709
|
(0, types_1.saveModel)(filePath, model);
|
|
268710
|
+
const unknownSchemes = (args.security || []).map((s) => s.scheme).filter((scheme) => model.securitySchemes && !(scheme in (model.securitySchemes || {})));
|
|
268624
268711
|
return {
|
|
268625
268712
|
success: true,
|
|
268626
268713
|
action: existingIndex >= 0 ? "updated" : "added",
|
|
268627
268714
|
filePath,
|
|
268628
268715
|
endpointId: newEndpoint.id,
|
|
268716
|
+
...unknownSchemes.length > 0 ? {
|
|
268717
|
+
warning: `Security scheme(s) not declared in top-level securitySchemes: ${unknownSchemes.join(", ")}. Declare them or run jetic_verify_model for details.`
|
|
268718
|
+
} : {},
|
|
268629
268719
|
endpoint: newEndpoint
|
|
268630
268720
|
};
|
|
268631
268721
|
}
|
|
268722
|
+
var endpointUpdatesSchema = zod_1.z.object({
|
|
268723
|
+
name: zod_1.z.string().optional(),
|
|
268724
|
+
summary: zod_1.z.string().optional(),
|
|
268725
|
+
description: zod_1.z.string().optional(),
|
|
268726
|
+
tags: zod_1.z.array(zod_1.z.string()).optional(),
|
|
268727
|
+
deprecated: zod_1.z.boolean().optional(),
|
|
268728
|
+
timeout: zod_1.z.number().int().positive().optional(),
|
|
268729
|
+
handlerName: zod_1.z.string().optional(),
|
|
268730
|
+
resource: zod_1.z.string().optional(),
|
|
268731
|
+
source: sourceReferenceSchema.optional(),
|
|
268732
|
+
security: zod_1.z.array(endpointSecuritySchema).optional(),
|
|
268733
|
+
middleware: zod_1.z.array(middlewareSchema).optional().describe("Replaces the whole middleware chain (order = execution order)"),
|
|
268734
|
+
parameters: zod_1.z.array(parameterSchema).optional(),
|
|
268735
|
+
requestBody: requestBodySchema.optional(),
|
|
268736
|
+
responses: zod_1.z.record(responseDefinitionSchema).optional(),
|
|
268737
|
+
pagination: paginationSchema.optional(),
|
|
268738
|
+
rateLimit: rateLimitSchema.nullable().optional().describe("Set to null to remove rate limiting"),
|
|
268739
|
+
ownership: ownershipSchema.optional(),
|
|
268740
|
+
produces: zod_1.z.array(producesSchema).optional(),
|
|
268741
|
+
consumes: zod_1.z.array(consumesSchema).optional()
|
|
268742
|
+
});
|
|
268632
268743
|
exports2.updateEndpointSchema = zod_1.z.object({
|
|
268633
268744
|
projectPath: zod_1.z.string().optional().describe("Path to project root or .jetic folder"),
|
|
268634
268745
|
id: zod_1.z.string().optional().describe("Target endpoint UUID"),
|
|
268635
268746
|
method: zod_1.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]).optional().describe("HTTP method"),
|
|
268636
268747
|
path: zod_1.z.string().optional().describe("Route path"),
|
|
268637
|
-
updates:
|
|
268638
|
-
name: zod_1.z.string().optional(),
|
|
268639
|
-
description: zod_1.z.string().optional(),
|
|
268640
|
-
tags: zod_1.z.array(zod_1.z.string()).optional(),
|
|
268641
|
-
handlerName: zod_1.z.string().optional(),
|
|
268642
|
-
resource: zod_1.z.string().optional(),
|
|
268643
|
-
source: sourceReferenceSchema.optional(),
|
|
268644
|
-
security: zod_1.z.array(endpointSecuritySchema).optional(),
|
|
268645
|
-
parameters: zod_1.z.array(parameterSchema).optional(),
|
|
268646
|
-
requestBody: requestBodySchema.optional(),
|
|
268647
|
-
responses: zod_1.z.record(responseDefinitionSchema).optional()
|
|
268648
|
-
}).describe("Partial endpoint properties to update")
|
|
268748
|
+
updates: endpointUpdatesSchema.describe("Partial endpoint properties to update (middleware replaces the whole chain)")
|
|
268649
268749
|
});
|
|
268650
268750
|
function handleUpdateEndpoint(args) {
|
|
268651
268751
|
const { model, filePath } = (0, types_1.loadModel)(args.projectPath);
|
|
@@ -268666,10 +268766,16 @@ var require_endpoint_tools = __commonJS({
|
|
|
268666
268766
|
const { updates } = args;
|
|
268667
268767
|
if (updates.name !== void 0)
|
|
268668
268768
|
endpoint.name = updates.name;
|
|
268769
|
+
if (updates.summary !== void 0)
|
|
268770
|
+
endpoint.summary = updates.summary;
|
|
268669
268771
|
if (updates.description !== void 0)
|
|
268670
268772
|
endpoint.description = updates.description;
|
|
268671
268773
|
if (updates.tags !== void 0)
|
|
268672
268774
|
endpoint.tags = updates.tags;
|
|
268775
|
+
if (updates.deprecated !== void 0)
|
|
268776
|
+
endpoint.deprecated = updates.deprecated;
|
|
268777
|
+
if (updates.timeout !== void 0)
|
|
268778
|
+
endpoint.timeout = updates.timeout;
|
|
268673
268779
|
if (updates.handlerName !== void 0)
|
|
268674
268780
|
endpoint.handlerName = updates.handlerName;
|
|
268675
268781
|
if (updates.resource !== void 0)
|
|
@@ -268678,12 +268784,29 @@ var require_endpoint_tools = __commonJS({
|
|
|
268678
268784
|
endpoint.source = updates.source;
|
|
268679
268785
|
if (updates.security !== void 0)
|
|
268680
268786
|
endpoint.security = updates.security;
|
|
268787
|
+
if (updates.middleware !== void 0) {
|
|
268788
|
+
for (const mw of updates.middleware) {
|
|
268789
|
+
if (!mw.name || !mw.name.trim())
|
|
268790
|
+
throw new Error('Middleware entries must have a non-empty "name".');
|
|
268791
|
+
}
|
|
268792
|
+
endpoint.middleware = updates.middleware;
|
|
268793
|
+
}
|
|
268681
268794
|
if (updates.parameters !== void 0)
|
|
268682
268795
|
endpoint.parameters = updates.parameters;
|
|
268683
268796
|
if (updates.requestBody !== void 0)
|
|
268684
268797
|
endpoint.requestBody = updates.requestBody;
|
|
268685
268798
|
if (updates.responses !== void 0)
|
|
268686
268799
|
endpoint.responses = updates.responses;
|
|
268800
|
+
if (updates.pagination !== void 0)
|
|
268801
|
+
endpoint.pagination = updates.pagination;
|
|
268802
|
+
if (updates.rateLimit !== void 0)
|
|
268803
|
+
endpoint.rateLimit = updates.rateLimit;
|
|
268804
|
+
if (updates.ownership !== void 0)
|
|
268805
|
+
endpoint.ownership = updates.ownership;
|
|
268806
|
+
if (updates.produces !== void 0)
|
|
268807
|
+
endpoint.produces = updates.produces;
|
|
268808
|
+
if (updates.consumes !== void 0)
|
|
268809
|
+
endpoint.consumes = updates.consumes;
|
|
268687
268810
|
(0, types_1.saveModel)(filePath, model);
|
|
268688
268811
|
return {
|
|
268689
268812
|
success: true,
|
|
@@ -269294,6 +269417,570 @@ var require_workflow_tools = __commonJS({
|
|
|
269294
269417
|
}
|
|
269295
269418
|
});
|
|
269296
269419
|
|
|
269420
|
+
// ../../packages/mcp-server/dist/tools/workflow-authoring-tools.js
|
|
269421
|
+
var require_workflow_authoring_tools = __commonJS({
|
|
269422
|
+
"../../packages/mcp-server/dist/tools/workflow-authoring-tools.js"(exports2) {
|
|
269423
|
+
"use strict";
|
|
269424
|
+
init_cjs_shims();
|
|
269425
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
269426
|
+
if (k2 === void 0) k2 = k;
|
|
269427
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
269428
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
269429
|
+
desc = { enumerable: true, get: function() {
|
|
269430
|
+
return m[k];
|
|
269431
|
+
} };
|
|
269432
|
+
}
|
|
269433
|
+
Object.defineProperty(o, k2, desc);
|
|
269434
|
+
}) : (function(o, m, k, k2) {
|
|
269435
|
+
if (k2 === void 0) k2 = k;
|
|
269436
|
+
o[k2] = m[k];
|
|
269437
|
+
}));
|
|
269438
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
269439
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
269440
|
+
}) : function(o, v) {
|
|
269441
|
+
o["default"] = v;
|
|
269442
|
+
});
|
|
269443
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
269444
|
+
var ownKeys = function(o) {
|
|
269445
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
269446
|
+
var ar = [];
|
|
269447
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
269448
|
+
return ar;
|
|
269449
|
+
};
|
|
269450
|
+
return ownKeys(o);
|
|
269451
|
+
};
|
|
269452
|
+
return function(mod) {
|
|
269453
|
+
if (mod && mod.__esModule) return mod;
|
|
269454
|
+
var result = {};
|
|
269455
|
+
if (mod != null) {
|
|
269456
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
269457
|
+
}
|
|
269458
|
+
__setModuleDefault(result, mod);
|
|
269459
|
+
return result;
|
|
269460
|
+
};
|
|
269461
|
+
})();
|
|
269462
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
269463
|
+
exports2.deleteWorkflowSchema = exports2.updateWorkflowSchema = exports2.createWorkflowSchema = exports2.validateWorkflowSchema = exports2.workflowStepSchema = void 0;
|
|
269464
|
+
exports2.validateWorkflowDefinition = validateWorkflowDefinition;
|
|
269465
|
+
exports2.slugifyWorkflowName = slugifyWorkflowName;
|
|
269466
|
+
exports2.handleValidateWorkflow = handleValidateWorkflow;
|
|
269467
|
+
exports2.handleCreateWorkflow = handleCreateWorkflow;
|
|
269468
|
+
exports2.handleUpdateWorkflow = handleUpdateWorkflow;
|
|
269469
|
+
exports2.handleDeleteWorkflow = handleDeleteWorkflow;
|
|
269470
|
+
var zod_1 = require("zod");
|
|
269471
|
+
var fs4 = __importStar(require("fs"));
|
|
269472
|
+
var path7 = __importStar(require("path"));
|
|
269473
|
+
var types_1 = require_types();
|
|
269474
|
+
var workflow_tools_1 = require_workflow_tools();
|
|
269475
|
+
exports2.workflowStepSchema = zod_1.z.object({
|
|
269476
|
+
name: zod_1.z.string().min(1).describe("Step name (must be unique within the workflow)"),
|
|
269477
|
+
method: zod_1.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]).describe("HTTP method"),
|
|
269478
|
+
path: zod_1.z.string().min(1).describe("Route path; :params resolve from body/memory (NOT from {{templates}} \u2014 those only work in body/inject)"),
|
|
269479
|
+
description: zod_1.z.string().optional().describe("What this step does"),
|
|
269480
|
+
body: zod_1.z.record(zod_1.z.any()).optional().describe("Request payload. Values support {{faker.*}}, {{scope:key}} and {{workflow:key}} templates"),
|
|
269481
|
+
inject: zod_1.z.record(zod_1.z.string()).optional().describe('Memory/header injection: "header:Name" or "body:field" (or bare header name) -> "scope:key" or "{{template}}"'),
|
|
269482
|
+
capture: zod_1.z.record(zod_1.z.string()).optional().describe('Save RESPONSE fields to memory: "scope:key" -> "dot.path.in.response"'),
|
|
269483
|
+
captureInput: zod_1.z.record(zod_1.z.string()).optional().describe('Save REQUEST body fields to memory BEFORE sending: "scope:key" -> "bodyField"'),
|
|
269484
|
+
expectStatus: zod_1.z.number().int().positive().optional().describe("Expected HTTP status (default 200; any 2xx passes when expecting 2xx)")
|
|
269485
|
+
// NOTE: .passthrough() is intentional — unknown keys must REACH the validator
|
|
269486
|
+
// (MODEL_DIALECT_KEY / UNKNOWN_STEP_KEY diagnostics). Zod's default strip
|
|
269487
|
+
// behavior would silently swallow the exact mistakes we need to report.
|
|
269488
|
+
}).passthrough();
|
|
269489
|
+
var inlineWorkflowSchema = zod_1.z.object({
|
|
269490
|
+
name: zod_1.z.string().min(1).describe("Workflow name (also used to derive the file slug)"),
|
|
269491
|
+
description: zod_1.z.string().optional(),
|
|
269492
|
+
environment: zod_1.z.string().optional().describe("Target environment name from the model (e.g. local)"),
|
|
269493
|
+
steps: zod_1.z.array(exports2.workflowStepSchema).min(1).describe("Ordered steps; later steps can use memory captured by earlier ones")
|
|
269494
|
+
}).passthrough();
|
|
269495
|
+
var TEMPLATE_RE = /\{\{\s*([^}]+?)\s*\}\}/g;
|
|
269496
|
+
var KNOWN_WORKFLOW_KEYS = /* @__PURE__ */ new Set(["name", "description", "environment", "steps", "generatedAt"]);
|
|
269497
|
+
var KNOWN_STEP_KEYS = /* @__PURE__ */ new Set([
|
|
269498
|
+
"name",
|
|
269499
|
+
"method",
|
|
269500
|
+
"path",
|
|
269501
|
+
"description",
|
|
269502
|
+
"inject",
|
|
269503
|
+
"capture",
|
|
269504
|
+
"captureInput",
|
|
269505
|
+
"expectStatus",
|
|
269506
|
+
"body",
|
|
269507
|
+
"condition"
|
|
269508
|
+
]);
|
|
269509
|
+
var MODEL_DIALECT_KEYS = /* @__PURE__ */ new Set(["call", "auth", "bind"]);
|
|
269510
|
+
function err(list, stepIndex, stepName, code, message, fix) {
|
|
269511
|
+
list.push({ severity: "error", stepIndex, stepName, code, message, fix });
|
|
269512
|
+
}
|
|
269513
|
+
function warn(list, stepIndex, stepName, code, message, fix) {
|
|
269514
|
+
list.push({ severity: "warning", stepIndex, stepName, code, message, fix });
|
|
269515
|
+
}
|
|
269516
|
+
function splitMemKey(raw) {
|
|
269517
|
+
const idx = raw.indexOf(":");
|
|
269518
|
+
if (idx <= 0)
|
|
269519
|
+
return { scope: "workflow", key: raw };
|
|
269520
|
+
return { scope: raw.slice(0, idx), key: raw.slice(idx + 1) };
|
|
269521
|
+
}
|
|
269522
|
+
function readMemoryKeys(projectRoot) {
|
|
269523
|
+
const keys = /* @__PURE__ */ new Set();
|
|
269524
|
+
try {
|
|
269525
|
+
const memPath = path7.join(projectRoot, ".jetic", "memory.json");
|
|
269526
|
+
if (!fs4.existsSync(memPath))
|
|
269527
|
+
return keys;
|
|
269528
|
+
const data = JSON.parse(fs4.readFileSync(memPath, "utf-8"));
|
|
269529
|
+
for (const [scope, entries] of Object.entries(data || {})) {
|
|
269530
|
+
if (!entries || typeof entries !== "object")
|
|
269531
|
+
continue;
|
|
269532
|
+
for (const key of Object.keys(entries))
|
|
269533
|
+
keys.add(`${scope}:${key}`);
|
|
269534
|
+
}
|
|
269535
|
+
} catch {
|
|
269536
|
+
}
|
|
269537
|
+
return keys;
|
|
269538
|
+
}
|
|
269539
|
+
function collectTemplateRefs(value, out) {
|
|
269540
|
+
if (typeof value === "string") {
|
|
269541
|
+
TEMPLATE_RE.lastIndex = 0;
|
|
269542
|
+
let m;
|
|
269543
|
+
while ((m = TEMPLATE_RE.exec(value)) !== null)
|
|
269544
|
+
out.push(m[1].trim());
|
|
269545
|
+
} else if (Array.isArray(value)) {
|
|
269546
|
+
for (const v of value)
|
|
269547
|
+
collectTemplateRefs(v, out);
|
|
269548
|
+
} else if (value && typeof value === "object") {
|
|
269549
|
+
for (const v of Object.values(value))
|
|
269550
|
+
collectTemplateRefs(v, out);
|
|
269551
|
+
}
|
|
269552
|
+
}
|
|
269553
|
+
function suggestEndpoints(model, method, wfPath) {
|
|
269554
|
+
const samePath = model.filter((e) => e.path === wfPath).map((e) => `${e.method} ${e.path}`);
|
|
269555
|
+
if (samePath.length > 0)
|
|
269556
|
+
return samePath.slice(0, 3);
|
|
269557
|
+
const prefix = wfPath.split("/").slice(0, 3).join("/");
|
|
269558
|
+
return model.filter((e) => e.path.startsWith(prefix)).map((e) => `${e.method} ${e.path}`).slice(0, 3);
|
|
269559
|
+
}
|
|
269560
|
+
function validateWorkflowDefinition(wf, opts) {
|
|
269561
|
+
const errors = [];
|
|
269562
|
+
const warnings = [];
|
|
269563
|
+
const { model } = (0, types_1.loadModel)(opts?.projectPath);
|
|
269564
|
+
const projectRoot = (0, types_1.projectRootOfModel)((0, types_1.resolveModelPath)(opts?.projectPath));
|
|
269565
|
+
const persistedMemory = readMemoryKeys(projectRoot);
|
|
269566
|
+
if (!wf || typeof wf !== "object") {
|
|
269567
|
+
err(errors, void 0, void 0, "NOT_AN_OBJECT", "Workflow must be an object with name + steps.", "Pass { name, steps: [...] }.");
|
|
269568
|
+
return toReport(errors, warnings);
|
|
269569
|
+
}
|
|
269570
|
+
if (!wf.name || typeof wf.name !== "string" || !wf.name.trim()) {
|
|
269571
|
+
err(errors, void 0, void 0, "MISSING_NAME", 'Workflow needs a non-empty "name".', 'Set a descriptive name like "User onboarding".');
|
|
269572
|
+
}
|
|
269573
|
+
if (!Array.isArray(wf.steps) || wf.steps.length === 0) {
|
|
269574
|
+
err(errors, void 0, void 0, "NO_STEPS", "Workflow needs at least one step.", "Add steps with { name, method, path }.");
|
|
269575
|
+
return toReport(errors, warnings);
|
|
269576
|
+
}
|
|
269577
|
+
if (wf.environment && !(model.environments || []).some((e) => e.name === wf.environment)) {
|
|
269578
|
+
warn(warnings, void 0, void 0, "UNKNOWN_ENVIRONMENT", `Environment "${wf.environment}" is not declared in model.json.`, `Use one of: ${(model.environments || []).map((e) => e.name).join(", ") || "(none declared)"}, or add it with jetic_manage_environment.`);
|
|
269579
|
+
}
|
|
269580
|
+
for (const key of Object.keys(wf)) {
|
|
269581
|
+
if (!KNOWN_WORKFLOW_KEYS.has(key)) {
|
|
269582
|
+
warn(warnings, void 0, void 0, "UNKNOWN_WORKFLOW_KEY", `Top-level key "${key}" is not part of the file-workflow format and will be ignored.`, "Valid keys: name, description, environment, steps.");
|
|
269583
|
+
}
|
|
269584
|
+
}
|
|
269585
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
269586
|
+
const defined = /* @__PURE__ */ new Set();
|
|
269587
|
+
wf.steps.forEach((rawStep, i) => {
|
|
269588
|
+
const stepNo = i + 1;
|
|
269589
|
+
const sName = typeof rawStep?.name === "string" ? rawStep.name : void 0;
|
|
269590
|
+
if (!rawStep || typeof rawStep !== "object" || Array.isArray(rawStep)) {
|
|
269591
|
+
err(errors, stepNo, void 0, "STEP_NOT_AN_OBJECT", `Step ${stepNo} must be an object.`, "Use { name, method, path, ... }.");
|
|
269592
|
+
return;
|
|
269593
|
+
}
|
|
269594
|
+
const step = rawStep;
|
|
269595
|
+
for (const key of Object.keys(step)) {
|
|
269596
|
+
if (MODEL_DIALECT_KEYS.has(key)) {
|
|
269597
|
+
err(errors, stepNo, sName, "MODEL_DIALECT_KEY", `Step ${stepNo} uses "${key}" from the model-embedded workflow dialect, which file workflows ignore.`, 'File-workflow equivalent: split "call" into method+path; replace auth/bind with inject/capture.');
|
|
269598
|
+
} else if (!KNOWN_STEP_KEYS.has(key)) {
|
|
269599
|
+
warn(warnings, stepNo, sName, "UNKNOWN_STEP_KEY", `Step ${stepNo} has unknown key "${key}" which the simulator ignores.`, "Remove it or check the field name (valid: name, method, path, description, body, inject, capture, captureInput, expectStatus).");
|
|
269600
|
+
}
|
|
269601
|
+
}
|
|
269602
|
+
if ("condition" in step) {
|
|
269603
|
+
warn(warnings, stepNo, sName, "CONDITION_IGNORED", `"condition" is accepted but currently ignored by the simulator \u2014 the step always runs.`, "Encode branching as separate workflows, or gate with expectStatus per step.");
|
|
269604
|
+
}
|
|
269605
|
+
if (!sName || !sName.trim()) {
|
|
269606
|
+
err(errors, stepNo, void 0, "STEP_MISSING_NAME", `Step ${stepNo} needs a non-empty "name".`, "Give every step a unique descriptive name.");
|
|
269607
|
+
} else if (seenNames.has(sName)) {
|
|
269608
|
+
warn(warnings, stepNo, sName, "DUPLICATE_STEP_NAME", `Duplicate step name "${sName}".`, "Rename steps so logs stay readable.");
|
|
269609
|
+
} else {
|
|
269610
|
+
seenNames.add(sName);
|
|
269611
|
+
}
|
|
269612
|
+
const method = typeof step.method === "string" ? step.method.toUpperCase() : void 0;
|
|
269613
|
+
const validMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"];
|
|
269614
|
+
if (!method || !validMethods.includes(method)) {
|
|
269615
|
+
err(errors, stepNo, sName, "BAD_METHOD", `Step ${stepNo} has invalid method ${JSON.stringify(step.method)}.`, `Use one of: ${validMethods.join(", ")}.`);
|
|
269616
|
+
return;
|
|
269617
|
+
}
|
|
269618
|
+
if (typeof step.path !== "string" || !step.path.trim()) {
|
|
269619
|
+
err(errors, stepNo, sName, "BAD_PATH", `Step ${stepNo} needs a "path" string.`, 'Use the route path, e.g. "/api/users/:id".');
|
|
269620
|
+
return;
|
|
269621
|
+
}
|
|
269622
|
+
const wfPath = step.path.startsWith("/") ? step.path : `/${step.path}`;
|
|
269623
|
+
const endpoint = (model.endpoints || []).find((e) => e.method.toUpperCase() === method && e.path === wfPath);
|
|
269624
|
+
if (!endpoint) {
|
|
269625
|
+
const suggestions = suggestEndpoints(model.endpoints || [], method, wfPath);
|
|
269626
|
+
const msg = `Step ${stepNo} (${method} ${wfPath}) matches no endpoint in model.json.` + (suggestions.length > 0 ? ` Did you mean: ${suggestions.join(" | ")}?` : "");
|
|
269627
|
+
if (opts?.allowUnknownEndpoints) {
|
|
269628
|
+
warn(warnings, stepNo, sName, "UNKNOWN_ENDPOINT", msg, "Add the endpoint with jetic_add_endpoint so future runs stay verifiable.");
|
|
269629
|
+
} else {
|
|
269630
|
+
err(errors, stepNo, sName, "UNKNOWN_ENDPOINT", msg, "Add it with jetic_add_endpoint, fix the method/path, or re-run with allowUnknownEndpoints.");
|
|
269631
|
+
}
|
|
269632
|
+
} else if (step.expectStatus !== void 0) {
|
|
269633
|
+
const codes = Object.keys(endpoint.responses || {});
|
|
269634
|
+
const exp = Number(step.expectStatus);
|
|
269635
|
+
const in2xx = exp >= 200 && exp < 300;
|
|
269636
|
+
const modelHas2xx = codes.some((c3) => Number(c3) >= 200 && Number(c3) < 300);
|
|
269637
|
+
if (!codes.includes(String(exp)) && !(in2xx && modelHas2xx)) {
|
|
269638
|
+
warn(warnings, stepNo, sName, "UNEXPECTED_STATUS", `Step ${stepNo} expects ${exp} but the model declares [${codes.join(", ") || "none"}] for ${method} ${wfPath}.`, "Align expectStatus with the model, or update the endpoint responses.");
|
|
269639
|
+
}
|
|
269640
|
+
}
|
|
269641
|
+
if (step.body !== void 0 && (typeof step.body !== "object" || step.body === null || Array.isArray(step.body))) {
|
|
269642
|
+
err(errors, stepNo, sName, "BAD_BODY", `Step ${stepNo} "body" must be an object of field -> value.`, 'Use { "email": "{{faker.internet.email}}" }.');
|
|
269643
|
+
}
|
|
269644
|
+
if ((method === "GET" || method === "HEAD") && step.body && Object.keys(step.body).length > 0) {
|
|
269645
|
+
warn(warnings, stepNo, sName, "GET_WITH_BODY", `Step ${stepNo} is ${method} with a body \u2014 it will be sent as URL query params, not a JSON body.`, "This is usually what you want for filters; otherwise switch to POST.");
|
|
269646
|
+
}
|
|
269647
|
+
if (endpoint?.requestBody?.fields && step.body && typeof step.body === "object") {
|
|
269648
|
+
for (const field of Object.keys(step.body)) {
|
|
269649
|
+
if (!(field in endpoint.requestBody.fields)) {
|
|
269650
|
+
warn(warnings, stepNo, sName, "UNKNOWN_BODY_FIELD", `Step ${stepNo} sends body field "${field}" not declared on ${method} ${wfPath}.`, "Check the spelling, or extend the endpoint with jetic_update_endpoint.");
|
|
269651
|
+
}
|
|
269652
|
+
}
|
|
269653
|
+
}
|
|
269654
|
+
const pathParams = wfPath.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g) || [];
|
|
269655
|
+
for (const p of pathParams) {
|
|
269656
|
+
const pname = p.substring(1);
|
|
269657
|
+
const inBody = step.body && typeof step.body === "object" && pname in step.body;
|
|
269658
|
+
const inMemory = [...defined].some((d) => d.split(":")[1] === pname) || [...persistedMemory].some((d) => d.split(":")[1] === pname);
|
|
269659
|
+
if (!inBody && !inMemory) {
|
|
269660
|
+
err(errors, stepNo, sName, "UNRESOLVED_PATH_PARAM", `Path param ":${pname}" in step ${stepNo} has no body field or memory source \u2014 the URL will literally contain ":${pname}" and fail.`, `Add "${pname}" to this step's body, or capture it as memory in an earlier step (capture: { "workflow:${pname}": "data.${pname}" }).`);
|
|
269661
|
+
}
|
|
269662
|
+
}
|
|
269663
|
+
if (/\{\{[^}]+\}\}/.test(wfPath)) {
|
|
269664
|
+
warn(warnings, stepNo, sName, "PATH_TEMPLATE", `Step ${stepNo} path contains a {{template}} \u2014 paths only resolve :params, templates work in body/inject.`, 'Use "/api/users/:id" with an "id" body field or captured memory key.');
|
|
269665
|
+
}
|
|
269666
|
+
const refs = [];
|
|
269667
|
+
collectTemplateRefs(step.body, refs);
|
|
269668
|
+
if (step.inject && typeof step.inject === "object") {
|
|
269669
|
+
for (const v of Object.values(step.inject))
|
|
269670
|
+
collectTemplateRefs(v, refs);
|
|
269671
|
+
}
|
|
269672
|
+
for (const ref of refs) {
|
|
269673
|
+
if (ref.startsWith("faker.")) {
|
|
269674
|
+
if (ref.split(".").length < 3) {
|
|
269675
|
+
warn(warnings, stepNo, sName, "SUSPICIOUS_FAKER", `"{{${ref}}}" looks like an incomplete faker path.`, "Use full paths like {{faker.internet.email}} or {{faker.string.uuid}}.");
|
|
269676
|
+
}
|
|
269677
|
+
continue;
|
|
269678
|
+
}
|
|
269679
|
+
if (!ref.includes(":")) {
|
|
269680
|
+
err(errors, stepNo, sName, "BAD_TEMPLATE", `"{{${ref}}}" in step ${stepNo} needs a scope ("{{workflow:key}}", "{{scope:key}}") or faker ("{{faker.x.y}}").`, "Bare {{key}} is left literal \u2014 add the scope prefix.");
|
|
269681
|
+
continue;
|
|
269682
|
+
}
|
|
269683
|
+
const { scope, key } = splitMemKey(ref);
|
|
269684
|
+
if (!key) {
|
|
269685
|
+
err(errors, stepNo, sName, "BAD_TEMPLATE", `"{{${ref}}}" has an empty key.`, "Use {{scope:key}} with a real key.");
|
|
269686
|
+
continue;
|
|
269687
|
+
}
|
|
269688
|
+
const full = `${scope}:${key}`;
|
|
269689
|
+
if (!defined.has(full) && !persistedMemory.has(full)) {
|
|
269690
|
+
err(errors, stepNo, sName, "UNDEFINED_MEMORY_REF", `"{{${ref}}}" in step ${stepNo} is never captured by an earlier step and is not in memory.json \u2014 it resolves to "".`, `Capture "${full}" in an earlier step (capture/captureInput), or seed it first.`);
|
|
269691
|
+
} else if (!defined.has(full)) {
|
|
269692
|
+
warn(warnings, stepNo, sName, "MEMORY_FILE_DEPENDENT", `"{{${ref}}}" in step ${stepNo} only exists in the current memory.json, not from an earlier step \u2014 reruns after memory clear will break.`, `Capture "${full}" inside this workflow instead.`);
|
|
269693
|
+
}
|
|
269694
|
+
}
|
|
269695
|
+
if (step.inject !== void 0) {
|
|
269696
|
+
if (typeof step.inject !== "object" || step.inject === null || Array.isArray(step.inject)) {
|
|
269697
|
+
err(errors, stepNo, sName, "BAD_INJECT", `Step ${stepNo} "inject" must be an object.`, 'Use { "header:Authorization": "Bearer {{workflow:accessToken}}" }.');
|
|
269698
|
+
} else {
|
|
269699
|
+
for (const [target, memRef] of Object.entries(step.inject)) {
|
|
269700
|
+
if (!target.trim() || typeof memRef !== "string" || !memRef.trim()) {
|
|
269701
|
+
err(errors, stepNo, sName, "BAD_INJECT", `Step ${stepNo} has an empty inject target or value.`, 'Use "header:Name"/"body:field" -> "scope:key".');
|
|
269702
|
+
}
|
|
269703
|
+
}
|
|
269704
|
+
}
|
|
269705
|
+
}
|
|
269706
|
+
for (const field of ["capture", "captureInput"]) {
|
|
269707
|
+
const cap = step[field];
|
|
269708
|
+
if (cap === void 0)
|
|
269709
|
+
continue;
|
|
269710
|
+
if (typeof cap !== "object" || cap === null || Array.isArray(cap)) {
|
|
269711
|
+
err(errors, stepNo, sName, "BAD_CAPTURE", `Step ${stepNo} "${field}" must be an object.`, `Use { "workflow:myVar": "${field === "capture" ? "data.id" : "bodyField"}" }.`);
|
|
269712
|
+
continue;
|
|
269713
|
+
}
|
|
269714
|
+
for (const [memKey, srcPath] of Object.entries(cap)) {
|
|
269715
|
+
if (!memKey.trim() || typeof srcPath !== "string" || !srcPath.trim()) {
|
|
269716
|
+
err(errors, stepNo, sName, "BAD_CAPTURE", `Step ${stepNo} "${field}" has an empty key or path.`, 'Use { "workflow:myVar": "data.id" }.');
|
|
269717
|
+
continue;
|
|
269718
|
+
}
|
|
269719
|
+
if (field === "captureInput" && (!step.body || typeof step.body !== "object" || !(srcPath in step.body))) {
|
|
269720
|
+
warn(warnings, stepNo, sName, "CAPTURE_INPUT_MISSING_FIELD", `captureInput "${memKey}" reads body field "${srcPath}" which step ${stepNo} does not send.`, 'Add the field to body, or capture from the response with "capture" instead.');
|
|
269721
|
+
}
|
|
269722
|
+
}
|
|
269723
|
+
}
|
|
269724
|
+
for (const field of ["capture", "captureInput"]) {
|
|
269725
|
+
const cap = step[field];
|
|
269726
|
+
if (cap && typeof cap === "object" && !Array.isArray(cap)) {
|
|
269727
|
+
for (const memKey of Object.keys(cap)) {
|
|
269728
|
+
const { scope, key } = splitMemKey(memKey);
|
|
269729
|
+
if (key)
|
|
269730
|
+
defined.add(`${scope}:${key}`);
|
|
269731
|
+
}
|
|
269732
|
+
}
|
|
269733
|
+
}
|
|
269734
|
+
});
|
|
269735
|
+
return toReport(errors, warnings);
|
|
269736
|
+
}
|
|
269737
|
+
function toReport(errors, warnings) {
|
|
269738
|
+
return {
|
|
269739
|
+
valid: errors.length === 0,
|
|
269740
|
+
errorsCount: errors.length,
|
|
269741
|
+
warningsCount: warnings.length,
|
|
269742
|
+
errors,
|
|
269743
|
+
warnings
|
|
269744
|
+
};
|
|
269745
|
+
}
|
|
269746
|
+
function slugifyWorkflowName(name) {
|
|
269747
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
269748
|
+
return slug || "workflow";
|
|
269749
|
+
}
|
|
269750
|
+
function workflowsDir(projectPath) {
|
|
269751
|
+
return path7.join(path7.dirname((0, types_1.resolveModelPath)(projectPath)), "workflows");
|
|
269752
|
+
}
|
|
269753
|
+
function workflowFilePath(projectPath, slug) {
|
|
269754
|
+
return path7.join(workflowsDir(projectPath), `${slug}.json`);
|
|
269755
|
+
}
|
|
269756
|
+
function resolveFileWorkflow(projectPath, ref) {
|
|
269757
|
+
const listing = (0, workflow_tools_1.handleListWorkflows)({ projectPath });
|
|
269758
|
+
const search = ref.toLowerCase();
|
|
269759
|
+
const item = listing.workflows.find((w) => w.id.toLowerCase() === search || w.name.toLowerCase() === search || w.filePath && path7.resolve(w.filePath).toLowerCase() === path7.resolve(ref).toLowerCase());
|
|
269760
|
+
if (!item) {
|
|
269761
|
+
throw new Error(`Workflow "${ref}" not found. Available: ${listing.workflows.map((w) => w.id).join(", ") || "(none)"}.`);
|
|
269762
|
+
}
|
|
269763
|
+
if (item.source === "model_json" || !item.filePath) {
|
|
269764
|
+
throw new Error(`Workflow "${ref}" lives inside model.json, which this tool cannot edit. Migrate it with jetic_create_workflow { fromModelWorkflow: "${item.name}" } first.`);
|
|
269765
|
+
}
|
|
269766
|
+
let def;
|
|
269767
|
+
try {
|
|
269768
|
+
def = JSON.parse(fs4.readFileSync(item.filePath, "utf-8"));
|
|
269769
|
+
} catch (e) {
|
|
269770
|
+
throw new Error(`Cannot read workflow file at ${item.filePath}: ${e?.message || e}`);
|
|
269771
|
+
}
|
|
269772
|
+
return { slug: item.id, filePath: item.filePath, def };
|
|
269773
|
+
}
|
|
269774
|
+
function migrateModelWorkflow(modelWorkflow) {
|
|
269775
|
+
const notes = [];
|
|
269776
|
+
const steps = (modelWorkflow.steps || []).map((s, i) => {
|
|
269777
|
+
let method = "GET";
|
|
269778
|
+
let reqPath = "/";
|
|
269779
|
+
if (typeof s.call === "string" && s.call.includes(" ")) {
|
|
269780
|
+
const parts = s.call.split(" ");
|
|
269781
|
+
method = parts[0].toUpperCase();
|
|
269782
|
+
reqPath = parts.slice(1).join(" ");
|
|
269783
|
+
} else if (typeof s.call === "string") {
|
|
269784
|
+
reqPath = s.call;
|
|
269785
|
+
}
|
|
269786
|
+
if (s.auth)
|
|
269787
|
+
notes.push(`Step ${i + 1}: model "auth: ${s.auth}" has no file equivalent \u2014 express it with inject (header:Authorization).`);
|
|
269788
|
+
if (s.bind)
|
|
269789
|
+
notes.push(`Step ${i + 1}: model "bind" has no file equivalent \u2014 express it with capture/captureInput.`);
|
|
269790
|
+
return {
|
|
269791
|
+
name: s.description || `${method} ${reqPath}`,
|
|
269792
|
+
method,
|
|
269793
|
+
path: reqPath,
|
|
269794
|
+
description: s.description,
|
|
269795
|
+
body: s.body,
|
|
269796
|
+
expectStatus: s.expectStatus
|
|
269797
|
+
};
|
|
269798
|
+
});
|
|
269799
|
+
return { def: { name: modelWorkflow.name, description: modelWorkflow.description, steps }, notes };
|
|
269800
|
+
}
|
|
269801
|
+
function writeWorkflowFile(filePath, def) {
|
|
269802
|
+
fs4.mkdirSync(path7.dirname(filePath), { recursive: true });
|
|
269803
|
+
const payload = { ...def, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
269804
|
+
const tmp = `${filePath}.tmp`;
|
|
269805
|
+
fs4.writeFileSync(tmp, JSON.stringify(payload, null, 2), "utf-8");
|
|
269806
|
+
fs4.renameSync(tmp, filePath);
|
|
269807
|
+
}
|
|
269808
|
+
exports2.validateWorkflowSchema = zod_1.z.object({
|
|
269809
|
+
projectPath: zod_1.z.string().optional().describe("Path to project root or .jetic folder"),
|
|
269810
|
+
workflow: zod_1.z.string().optional().describe('Workflow slug, name, or file path to validate (omit when passing inline "definition")'),
|
|
269811
|
+
definition: inlineWorkflowSchema.optional().describe("Inline workflow to dry-run validate WITHOUT saving (for pre-save checks)"),
|
|
269812
|
+
allowUnknownEndpoints: zod_1.z.boolean().optional().default(false).describe("Downgrade unknown-endpoint errors to warnings (endpoint exists on server but not in model yet)")
|
|
269813
|
+
});
|
|
269814
|
+
function handleValidateWorkflow(args) {
|
|
269815
|
+
if (!args.workflow && !args.definition) {
|
|
269816
|
+
throw new Error('Provide either "workflow" (slug/name/file) or an inline "definition" to validate.');
|
|
269817
|
+
}
|
|
269818
|
+
let def;
|
|
269819
|
+
let filePath;
|
|
269820
|
+
if (args.definition) {
|
|
269821
|
+
def = args.definition;
|
|
269822
|
+
} else {
|
|
269823
|
+
const resolved = resolveFileWorkflow(args.projectPath, args.workflow);
|
|
269824
|
+
def = resolved.def;
|
|
269825
|
+
filePath = resolved.filePath;
|
|
269826
|
+
}
|
|
269827
|
+
const report = validateWorkflowDefinition(def, {
|
|
269828
|
+
projectPath: args.projectPath,
|
|
269829
|
+
allowUnknownEndpoints: args.allowUnknownEndpoints === true
|
|
269830
|
+
});
|
|
269831
|
+
return { ...filePath ? { filePath } : { saved: false }, ...report };
|
|
269832
|
+
}
|
|
269833
|
+
exports2.createWorkflowSchema = zod_1.z.object({
|
|
269834
|
+
projectPath: zod_1.z.string().optional().describe("Path to project root or .jetic folder"),
|
|
269835
|
+
name: zod_1.z.string().min(1).optional().describe("Workflow name (required unless fromModelWorkflow is used)"),
|
|
269836
|
+
slug: zod_1.z.string().optional().describe('File slug override (default: derived from name, e.g. "User Onboarding" -> "user-onboarding")'),
|
|
269837
|
+
description: zod_1.z.string().optional(),
|
|
269838
|
+
environment: zod_1.z.string().optional().describe("Target environment name from the model (e.g. local)"),
|
|
269839
|
+
steps: zod_1.z.array(exports2.workflowStepSchema).min(1).optional().describe("Ordered steps (required unless fromModelWorkflow is used)"),
|
|
269840
|
+
fromModelWorkflow: zod_1.z.string().optional().describe("Migrate an embedded model.json workflow by name instead of authoring steps manually"),
|
|
269841
|
+
overwrite: zod_1.z.boolean().optional().default(false).describe("Overwrite the file if the slug already exists (default false \u2014 safe creation)"),
|
|
269842
|
+
validateOnly: zod_1.z.boolean().optional().default(false).describe("Only validate, do not write any file (dry run)"),
|
|
269843
|
+
allowUnknownEndpoints: zod_1.z.boolean().optional().default(false).describe("Save even when steps reference endpoints missing from model.json (recorded as warnings)")
|
|
269844
|
+
});
|
|
269845
|
+
function handleCreateWorkflow(args) {
|
|
269846
|
+
let def;
|
|
269847
|
+
const migrationNotes = [];
|
|
269848
|
+
if (args.fromModelWorkflow) {
|
|
269849
|
+
const { model } = (0, types_1.loadModel)(args.projectPath);
|
|
269850
|
+
const raw = (model.workflows || []).find((w) => w.name === args.fromModelWorkflow);
|
|
269851
|
+
if (!raw) {
|
|
269852
|
+
throw new Error(`Embedded workflow "${args.fromModelWorkflow}" not found in model.json. Available: ${(model.workflows || []).map((w) => w.name).join(", ") || "(none)"}.`);
|
|
269853
|
+
}
|
|
269854
|
+
const migrated = migrateModelWorkflow(raw);
|
|
269855
|
+
def = {
|
|
269856
|
+
...args.name ? { name: args.name } : {},
|
|
269857
|
+
...migrated.def,
|
|
269858
|
+
...args.description ? { description: args.description } : {},
|
|
269859
|
+
...args.environment ? { environment: args.environment } : {},
|
|
269860
|
+
...args.steps ? { steps: args.steps } : {}
|
|
269861
|
+
};
|
|
269862
|
+
migrationNotes.push(...migrated.notes);
|
|
269863
|
+
} else {
|
|
269864
|
+
if (!args.name)
|
|
269865
|
+
throw new Error('"name" is required to create a workflow (or use fromModelWorkflow to migrate one).');
|
|
269866
|
+
if (!args.steps || args.steps.length === 0) {
|
|
269867
|
+
throw new Error('"steps" with at least one step is required (or use fromModelWorkflow to migrate one).');
|
|
269868
|
+
}
|
|
269869
|
+
def = {
|
|
269870
|
+
name: args.name,
|
|
269871
|
+
...args.description ? { description: args.description } : {},
|
|
269872
|
+
...args.environment ? { environment: args.environment } : {},
|
|
269873
|
+
steps: args.steps
|
|
269874
|
+
};
|
|
269875
|
+
}
|
|
269876
|
+
const report = validateWorkflowDefinition(def, {
|
|
269877
|
+
projectPath: args.projectPath,
|
|
269878
|
+
allowUnknownEndpoints: args.allowUnknownEndpoints === true
|
|
269879
|
+
});
|
|
269880
|
+
const warnings = [...report.warnings];
|
|
269881
|
+
for (const note of migrationNotes) {
|
|
269882
|
+
warnings.push({ severity: "warning", code: "MIGRATION_NOTE", message: note, fix: "Adjust the saved file with jetic_update_workflow." });
|
|
269883
|
+
}
|
|
269884
|
+
if (!report.valid) {
|
|
269885
|
+
throw new Error(`Workflow "${def.name}" has ${report.errorsCount} error(s) and was NOT saved:
|
|
269886
|
+
` + report.errors.map((e) => `- [step ${e.stepIndex ?? "-"}] ${e.message} Fix: ${e.fix}`).join("\n"));
|
|
269887
|
+
}
|
|
269888
|
+
const slug = args.slug?.trim() ? slugifyWorkflowName(args.slug) : slugifyWorkflowName(def.name);
|
|
269889
|
+
const filePath = workflowFilePath(args.projectPath, slug);
|
|
269890
|
+
const overwrite = args.overwrite === true;
|
|
269891
|
+
const validateOnly = args.validateOnly === true;
|
|
269892
|
+
if (validateOnly) {
|
|
269893
|
+
return {
|
|
269894
|
+
success: true,
|
|
269895
|
+
action: "validated",
|
|
269896
|
+
saved: false,
|
|
269897
|
+
slug,
|
|
269898
|
+
filePath,
|
|
269899
|
+
stepsCount: def.steps.length,
|
|
269900
|
+
validation: { ...report, warnings }
|
|
269901
|
+
};
|
|
269902
|
+
}
|
|
269903
|
+
if (fs4.existsSync(filePath) && !overwrite) {
|
|
269904
|
+
throw new Error(`Workflow file already exists at ${filePath}. Pass overwrite: true to replace it, use jetic_update_workflow to edit it, or pick another slug.`);
|
|
269905
|
+
}
|
|
269906
|
+
const existedBefore = fs4.existsSync(filePath);
|
|
269907
|
+
writeWorkflowFile(filePath, def);
|
|
269908
|
+
return {
|
|
269909
|
+
success: true,
|
|
269910
|
+
action: existedBefore ? "overwritten" : "created",
|
|
269911
|
+
slug,
|
|
269912
|
+
filePath,
|
|
269913
|
+
stepsCount: def.steps.length,
|
|
269914
|
+
validation: { ...report, warnings },
|
|
269915
|
+
next: `Run it with jetic_simulate_workflow { "workflow": "${slug}" }.`
|
|
269916
|
+
};
|
|
269917
|
+
}
|
|
269918
|
+
exports2.updateWorkflowSchema = zod_1.z.object({
|
|
269919
|
+
projectPath: zod_1.z.string().optional().describe("Path to project root or .jetic folder"),
|
|
269920
|
+
workflow: zod_1.z.string().describe("Workflow slug, name, or file path to update"),
|
|
269921
|
+
name: zod_1.z.string().min(1).optional().describe("Rename the workflow (file slug stays the same)"),
|
|
269922
|
+
description: zod_1.z.string().optional().describe("Replace the description"),
|
|
269923
|
+
environment: zod_1.z.string().optional().describe("Replace the target environment"),
|
|
269924
|
+
steps: zod_1.z.array(exports2.workflowStepSchema).min(1).optional().describe("REPLACE all steps (use appendSteps to only add)"),
|
|
269925
|
+
appendSteps: zod_1.z.array(exports2.workflowStepSchema).optional().describe("Append steps to the end (memory from existing steps stays usable)"),
|
|
269926
|
+
validateOnly: zod_1.z.boolean().optional().default(false).describe("Only validate the result, do not write"),
|
|
269927
|
+
allowUnknownEndpoints: zod_1.z.boolean().optional().default(false)
|
|
269928
|
+
});
|
|
269929
|
+
function handleUpdateWorkflow(args) {
|
|
269930
|
+
const resolved = resolveFileWorkflow(args.projectPath, args.workflow);
|
|
269931
|
+
const def = {
|
|
269932
|
+
...resolved.def,
|
|
269933
|
+
...args.name ? { name: args.name } : {},
|
|
269934
|
+
...args.description !== void 0 ? { description: args.description } : {},
|
|
269935
|
+
...args.environment !== void 0 ? { environment: args.environment } : {},
|
|
269936
|
+
steps: [
|
|
269937
|
+
...args.steps ? [] : resolved.def.steps || [],
|
|
269938
|
+
...args.steps || [],
|
|
269939
|
+
...args.appendSteps || []
|
|
269940
|
+
]
|
|
269941
|
+
};
|
|
269942
|
+
const report = validateWorkflowDefinition(def, {
|
|
269943
|
+
projectPath: args.projectPath,
|
|
269944
|
+
allowUnknownEndpoints: args.allowUnknownEndpoints === true
|
|
269945
|
+
});
|
|
269946
|
+
if (!report.valid) {
|
|
269947
|
+
throw new Error(`Updated workflow has ${report.errorsCount} error(s) and was NOT saved:
|
|
269948
|
+
` + report.errors.map((e) => `- [step ${e.stepIndex ?? "-"}] ${e.message} Fix: ${e.fix}`).join("\n"));
|
|
269949
|
+
}
|
|
269950
|
+
if (args.validateOnly === true) {
|
|
269951
|
+
return { success: true, action: "validated", saved: false, slug: resolved.slug, filePath: resolved.filePath, stepsCount: def.steps.length, validation: report };
|
|
269952
|
+
}
|
|
269953
|
+
writeWorkflowFile(resolved.filePath, def);
|
|
269954
|
+
return {
|
|
269955
|
+
success: true,
|
|
269956
|
+
action: "updated",
|
|
269957
|
+
slug: resolved.slug,
|
|
269958
|
+
filePath: resolved.filePath,
|
|
269959
|
+
stepsCount: def.steps.length,
|
|
269960
|
+
validation: report,
|
|
269961
|
+
next: `Run it with jetic_simulate_workflow { "workflow": "${resolved.slug}" }.`
|
|
269962
|
+
};
|
|
269963
|
+
}
|
|
269964
|
+
exports2.deleteWorkflowSchema = zod_1.z.object({
|
|
269965
|
+
projectPath: zod_1.z.string().optional().describe("Path to project root or .jetic folder"),
|
|
269966
|
+
workflow: zod_1.z.string().describe("Workflow slug, name, or file path to delete")
|
|
269967
|
+
});
|
|
269968
|
+
function handleDeleteWorkflow(args) {
|
|
269969
|
+
const listing = (0, workflow_tools_1.handleListWorkflows)({ projectPath: args.projectPath });
|
|
269970
|
+
const search = args.workflow.toLowerCase();
|
|
269971
|
+
const item = listing.workflows.find((w) => w.id.toLowerCase() === search || w.name.toLowerCase() === search || w.filePath && path7.resolve(w.filePath).toLowerCase() === path7.resolve(args.workflow).toLowerCase());
|
|
269972
|
+
if (!item) {
|
|
269973
|
+
return { success: false, message: `Workflow "${args.workflow}" not found.` };
|
|
269974
|
+
}
|
|
269975
|
+
if (item.source === "model_json" || !item.filePath) {
|
|
269976
|
+
throw new Error(`Workflow "${args.workflow}" is embedded in model.json and cannot be deleted by this tool. Remove it by editing .jetic/model.json directly.`);
|
|
269977
|
+
}
|
|
269978
|
+
fs4.rmSync(item.filePath, { force: true });
|
|
269979
|
+
return { success: true, action: "deleted", slug: item.id, filePath: item.filePath };
|
|
269980
|
+
}
|
|
269981
|
+
}
|
|
269982
|
+
});
|
|
269983
|
+
|
|
269297
269984
|
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/zod-compat.js
|
|
269298
269985
|
var require_zod_compat = __commonJS({
|
|
269299
269986
|
"../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/zod-compat.js"(exports2) {
|
|
@@ -283362,6 +284049,7 @@ var require_server3 = __commonJS({
|
|
|
283362
284049
|
var testing_tools_1 = require_testing_tools();
|
|
283363
284050
|
var environment_tools_1 = require_environment_tools();
|
|
283364
284051
|
var workflow_tools_1 = require_workflow_tools();
|
|
284052
|
+
var workflow_authoring_tools_1 = require_workflow_authoring_tools();
|
|
283365
284053
|
exports2.JETIC_MCP_SERVER_NAME = "jetic-mcp-server";
|
|
283366
284054
|
function getJeticMcpVersion() {
|
|
283367
284055
|
try {
|
|
@@ -283381,6 +284069,7 @@ var require_server3 = __commonJS({
|
|
|
283381
284069
|
"3. Mutating tools (`jetic_add_endpoint`, `jetic_update_endpoint`, `jetic_delete_endpoint`, `jetic_manage_environment`) write to disk \u2014 confirm intent with the user first when the change is destructive (delete/overwrite).",
|
|
283382
284070
|
"4. After edits, run `jetic_verify_model` and fix reported errors.",
|
|
283383
284071
|
"5. Use `jetic_test_endpoint` / `jetic_simulate_workflow` only against a running server (local/staging); pass `baseUrl` to override the stored environment URL.",
|
|
284072
|
+
"Authoring workflows: NEVER hand-write workflow files blind. Build the steps, then run `jetic_validate_workflow` with the inline definition (dry run), fix reported errors, save with `jetic_create_workflow`, and finally execute with `jetic_simulate_workflow`. Chain steps with capture/captureInput (producer) + inject or {{workflow:key}} templates (consumer).",
|
|
283384
284073
|
"Project resolution: every tool accepts an optional `projectPath`. If omitted, the server uses the `JETIC_PROJECT_PATH` environment variable, falling back to its own working directory. Set one of them to the project root so the server edits the right `.jetic/model.json`."
|
|
283385
284074
|
].join("\n");
|
|
283386
284075
|
var READ_ONLY = {
|
|
@@ -283441,6 +284130,10 @@ var require_server3 = __commonJS({
|
|
|
283441
284130
|
register("jetic_list_workflows", "List workflows", "Lists multi-step integration test workflows from .jetic/workflows/*.json, .jetic/workflow.json, or embedded model.json workflows.", workflow_tools_1.listWorkflowsSchema.shape, READ_ONLY, workflow_tools_1.handleListWorkflows);
|
|
283442
284131
|
register("jetic_simulate_workflow", "Simulate workflow", "Executes a full end-to-end multi-step workflow simulation with dynamic memory capture, header injection, and schema validation against the live server.", workflow_tools_1.simulateWorkflowSchema.shape, READ_LIVE, workflow_tools_1.handleSimulateWorkflow);
|
|
283443
284132
|
register("jetic_simulate", "Simulate workflow (alias)", "Alias of jetic_simulate_workflow: runs a multi-step user-journey workflow against the live server. Prefer jetic_simulate_workflow in new prompts.", workflow_tools_1.simulateWorkflowSchema.shape, READ_LIVE, workflow_tools_1.handleSimulateWorkflow);
|
|
284133
|
+
register("jetic_validate_workflow", "Validate workflow", "Deep-validates a workflow WITHOUT saving or running it: step structure, endpoint cross-checks against model.json, and memory data-flow analysis (every {{scope:key}} must be captured by an earlier step). Accepts a saved workflow reference or an inline definition for pre-save dry runs.", workflow_authoring_tools_1.validateWorkflowSchema.shape, READ_ONLY, workflow_authoring_tools_1.handleValidateWorkflow);
|
|
284134
|
+
register("jetic_create_workflow", "Create workflow", "Creates a .jetic/workflows/<slug>.json workflow from ordered steps with capture/inject memory chaining. Refuses to save when validation errors exist (error messages say exactly how to fix); use validateOnly for a dry run. Can also migrate an embedded model.json workflow via fromModelWorkflow.", workflow_authoring_tools_1.createWorkflowSchema.shape, WRITES_MODEL, workflow_authoring_tools_1.handleCreateWorkflow);
|
|
284135
|
+
register("jetic_update_workflow", "Update workflow", "Updates a saved file workflow: rename, description/environment, full steps replacement, or appendSteps. Validates before writing and refuses to save on errors.", workflow_authoring_tools_1.updateWorkflowSchema.shape, WRITES_MODEL, workflow_authoring_tools_1.handleUpdateWorkflow);
|
|
284136
|
+
register("jetic_delete_workflow", "Delete workflow", "Deletes a saved .jetic/workflows/*.json workflow file by slug, name, or path. Destructive: confirm with the user before calling. Cannot delete model.json-embedded workflows.", workflow_authoring_tools_1.deleteWorkflowSchema.shape, DESTRUCTIVE, workflow_authoring_tools_1.handleDeleteWorkflow);
|
|
283444
284137
|
return server;
|
|
283445
284138
|
}
|
|
283446
284139
|
async function runMcpServer2() {
|
|
@@ -283506,6 +284199,7 @@ var require_dist7 = __commonJS({
|
|
|
283506
284199
|
__exportStar(require_testing_tools(), exports2);
|
|
283507
284200
|
__exportStar(require_environment_tools(), exports2);
|
|
283508
284201
|
__exportStar(require_workflow_tools(), exports2);
|
|
284202
|
+
__exportStar(require_workflow_authoring_tools(), exports2);
|
|
283509
284203
|
__exportStar(require_server3(), exports2);
|
|
283510
284204
|
__exportStar(require_serial_transport(), exports2);
|
|
283511
284205
|
var fs4 = __importStar(require("fs"));
|
|
@@ -286107,7 +286801,7 @@ var mcpCommand = new import_commander10.Command("mcp").description("Launch Model
|
|
|
286107
286801
|
|
|
286108
286802
|
// src/index.ts
|
|
286109
286803
|
var program = new import_commander11.Command();
|
|
286110
|
-
program.name("jetic").description("AI-Native API Behavior Testing").version("0.1.
|
|
286804
|
+
program.name("jetic").description("AI-Native API Behavior Testing").version("0.1.8");
|
|
286111
286805
|
program.addCommand(initCommand);
|
|
286112
286806
|
program.addCommand(scanCommand);
|
|
286113
286807
|
program.addCommand(inspectCommand);
|