jetic-cli 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -153
- package/dist/dashboard/assets/index-DfTWnEz_.js +430 -0
- package/dist/dashboard/assets/index-Dz50r4Cq.css +1 -0
- package/dist/dashboard/brand/JeticLogo.tsx +79 -0
- package/dist/dashboard/brand/jetic-animated.svg +33 -0
- package/dist/dashboard/brand/jetic-black.svg +5 -0
- package/dist/dashboard/brand/jetic-intro.svg +38 -0
- package/dist/dashboard/brand/jetic.svg +5 -0
- package/dist/dashboard/index.html +12 -3
- package/dist/index.js +1102 -123
- package/package.json +5 -4
- package/public/brand/JeticLogo.tsx +79 -0
- package/public/brand/jetic-animated.svg +33 -0
- package/public/brand/jetic-black.svg +5 -0
- package/public/brand/jetic-intro.svg +38 -0
- package/public/brand/jetic.svg +5 -0
- package/public/jetic_logo.png +0 -0
- package/dist/dashboard/assets/index-BxpFwhwt.css +0 -1
- package/dist/dashboard/assets/index-CBkHSFmz.js +0 -409
package/dist/index.js
CHANGED
|
@@ -272670,10 +272670,27 @@ var require_workflow_simulator = __commonJS({
|
|
|
272670
272670
|
"use strict";
|
|
272671
272671
|
init_cjs_shims();
|
|
272672
272672
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
272673
|
-
exports2.WorkflowSimulator = void 0;
|
|
272673
|
+
exports2.WorkflowSimulator = exports2.CONNECTION_ERROR_CODES = void 0;
|
|
272674
|
+
exports2.networkErrorCode = networkErrorCode2;
|
|
272675
|
+
exports2.probeTarget = probeTarget2;
|
|
272674
272676
|
var memory_1 = require_dist12();
|
|
272675
272677
|
var faker_1 = require("@faker-js/faker");
|
|
272676
272678
|
var conditions_1 = require_conditions();
|
|
272679
|
+
exports2.CONNECTION_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
272680
|
+
"ECONNREFUSED",
|
|
272681
|
+
"ECONNRESET",
|
|
272682
|
+
"ENOTFOUND",
|
|
272683
|
+
"EAI_AGAIN",
|
|
272684
|
+
"ETIMEDOUT",
|
|
272685
|
+
"EHOSTUNREACH",
|
|
272686
|
+
"ENETUNREACH",
|
|
272687
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
272688
|
+
"UND_ERR_SOCKET"
|
|
272689
|
+
]);
|
|
272690
|
+
function networkErrorCode2(error) {
|
|
272691
|
+
const raw = error?.cause?.code ?? error?.cause?.errno ?? error?.code ?? error?.errno;
|
|
272692
|
+
return typeof raw === "string" ? raw : void 0;
|
|
272693
|
+
}
|
|
272677
272694
|
function deepGet2(obj, dotPath) {
|
|
272678
272695
|
if (obj === null || obj === void 0)
|
|
272679
272696
|
return void 0;
|
|
@@ -272690,6 +272707,8 @@ var require_workflow_simulator = __commonJS({
|
|
|
272690
272707
|
model;
|
|
272691
272708
|
environment;
|
|
272692
272709
|
runtime;
|
|
272710
|
+
strictTemplates = true;
|
|
272711
|
+
requestTimeoutMs = 3e4;
|
|
272693
272712
|
constructor(model, environment, runtime = {}) {
|
|
272694
272713
|
this.model = model;
|
|
272695
272714
|
this.environment = environment;
|
|
@@ -272702,7 +272721,36 @@ var require_workflow_simulator = __commonJS({
|
|
|
272702
272721
|
if (options?.clearMemory) {
|
|
272703
272722
|
memory_1.JeticMemory.clearAllMemory(this.runtime.projectRoot);
|
|
272704
272723
|
}
|
|
272724
|
+
this.strictTemplates = options?.strictTemplates ?? true;
|
|
272725
|
+
this.requestTimeoutMs = options?.requestTimeoutMs ?? 3e4;
|
|
272705
272726
|
const startTime = Date.now();
|
|
272727
|
+
const preflightMode = options?.preflight ?? "off";
|
|
272728
|
+
const preflight = preflightMode !== "off" && workflow.steps.length > 0 ? await probeTarget2(this.environment.baseUrl, options?.preflightTimeoutMs ?? 3e3) : void 0;
|
|
272729
|
+
if (preflight && preflightMode === "abort" && preflight.verdict === "unreachable") {
|
|
272730
|
+
preflight.aborted = true;
|
|
272731
|
+
return {
|
|
272732
|
+
success: false,
|
|
272733
|
+
workflow: {
|
|
272734
|
+
name: workflow.name,
|
|
272735
|
+
description: workflow.description,
|
|
272736
|
+
stepsCount: workflow.steps.length
|
|
272737
|
+
},
|
|
272738
|
+
environment: {
|
|
272739
|
+
name: this.environment.name,
|
|
272740
|
+
baseUrl: this.environment.baseUrl
|
|
272741
|
+
},
|
|
272742
|
+
stats: {
|
|
272743
|
+
totalSteps: workflow.steps.length,
|
|
272744
|
+
executedSteps: 0,
|
|
272745
|
+
passedSteps: 0,
|
|
272746
|
+
failedSteps: 0,
|
|
272747
|
+
totalTimeMs: Date.now() - startTime
|
|
272748
|
+
},
|
|
272749
|
+
results: [],
|
|
272750
|
+
preflight,
|
|
272751
|
+
abortReason: preflight.message
|
|
272752
|
+
};
|
|
272753
|
+
}
|
|
272706
272754
|
const state = { results: [], allPassed: true, halted: false };
|
|
272707
272755
|
await this.runSteps(workflow, options, state, [workflow.name]);
|
|
272708
272756
|
const stepResults = state.results;
|
|
@@ -272725,7 +272773,8 @@ var require_workflow_simulator = __commonJS({
|
|
|
272725
272773
|
failedSteps: stepResults.filter((r) => !r.passed).length,
|
|
272726
272774
|
totalTimeMs
|
|
272727
272775
|
},
|
|
272728
|
-
results: stepResults
|
|
272776
|
+
results: stepResults,
|
|
272777
|
+
...preflight ? { preflight } : {}
|
|
272729
272778
|
};
|
|
272730
272779
|
}
|
|
272731
272780
|
/**
|
|
@@ -272735,7 +272784,22 @@ var require_workflow_simulator = __commonJS({
|
|
|
272735
272784
|
*/
|
|
272736
272785
|
async runSteps(workflow, options, state, lineage) {
|
|
272737
272786
|
const baseUrl = this.environment.baseUrl;
|
|
272738
|
-
const resolve2 = (value) => this.resolveTemplateString(value);
|
|
272787
|
+
const resolve2 = (value) => this.resolveTemplateString(value, false);
|
|
272788
|
+
const record = async (result) => {
|
|
272789
|
+
state.results.push(result);
|
|
272790
|
+
if (!options?.onStepResult)
|
|
272791
|
+
return;
|
|
272792
|
+
try {
|
|
272793
|
+
await options.onStepResult(result, {
|
|
272794
|
+
totalSteps: workflow.steps.length,
|
|
272795
|
+
executedSteps: state.results.length,
|
|
272796
|
+
passedSteps: state.results.filter((r) => r.passed).length,
|
|
272797
|
+
failedSteps: state.results.filter((r) => !r.passed).length,
|
|
272798
|
+
skippedSteps: state.results.filter((r) => r.skipped).length
|
|
272799
|
+
});
|
|
272800
|
+
} catch {
|
|
272801
|
+
}
|
|
272802
|
+
};
|
|
272739
272803
|
for (let i = 0; i < workflow.steps.length; i++) {
|
|
272740
272804
|
const step = workflow.steps[i];
|
|
272741
272805
|
const stepStartTime = Date.now();
|
|
@@ -272743,7 +272807,7 @@ var require_workflow_simulator = __commonJS({
|
|
|
272743
272807
|
const kind = step.condition === void 0 ? void 0 : (0, conditions_1.conditionKind)(step.condition);
|
|
272744
272808
|
if (kind === "invalid") {
|
|
272745
272809
|
const reason = (0, conditions_1.describeInvalidCondition)(step.condition);
|
|
272746
|
-
|
|
272810
|
+
await record({
|
|
272747
272811
|
stepIndex: i + 1,
|
|
272748
272812
|
name: stepName,
|
|
272749
272813
|
method: step.method,
|
|
@@ -272752,6 +272816,8 @@ var require_workflow_simulator = __commonJS({
|
|
|
272752
272816
|
expectedStatus: step.expectStatus ?? 200,
|
|
272753
272817
|
actualStatus: 0,
|
|
272754
272818
|
durationMs: Date.now() - stepStartTime,
|
|
272819
|
+
startedAt: new Date(stepStartTime).toISOString(),
|
|
272820
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
272755
272821
|
passed: false,
|
|
272756
272822
|
skipped: false,
|
|
272757
272823
|
retryCount: 0,
|
|
@@ -272768,7 +272834,7 @@ var require_workflow_simulator = __commonJS({
|
|
|
272768
272834
|
if (kind === "legacy") {
|
|
272769
272835
|
const condResult = await (0, conditions_1.evaluateLegacyCondition)(step.condition, resolve2);
|
|
272770
272836
|
if (!condResult.matched) {
|
|
272771
|
-
|
|
272837
|
+
await record({
|
|
272772
272838
|
stepIndex: i + 1,
|
|
272773
272839
|
name: stepName,
|
|
272774
272840
|
method: step.method,
|
|
@@ -272777,6 +272843,8 @@ var require_workflow_simulator = __commonJS({
|
|
|
272777
272843
|
expectedStatus: step.expectStatus ?? 200,
|
|
272778
272844
|
actualStatus: 0,
|
|
272779
272845
|
durationMs: Date.now() - stepStartTime,
|
|
272846
|
+
startedAt: new Date(stepStartTime).toISOString(),
|
|
272847
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
272780
272848
|
passed: true,
|
|
272781
272849
|
skipped: true,
|
|
272782
272850
|
retryCount: 0,
|
|
@@ -272789,20 +272857,86 @@ var require_workflow_simulator = __commonJS({
|
|
|
272789
272857
|
continue;
|
|
272790
272858
|
}
|
|
272791
272859
|
}
|
|
272792
|
-
|
|
272793
|
-
|
|
272794
|
-
|
|
272795
|
-
|
|
272796
|
-
|
|
272797
|
-
|
|
272860
|
+
let injectedHeaders = {};
|
|
272861
|
+
let finalBody = {};
|
|
272862
|
+
let resolvedQuery = {};
|
|
272863
|
+
let resolvedPath = step.path;
|
|
272864
|
+
let fullUrl = "";
|
|
272865
|
+
let inputCaptured = [];
|
|
272866
|
+
try {
|
|
272867
|
+
const injections = await this.resolveInjections(step.inject);
|
|
272868
|
+
injectedHeaders = injections.headers;
|
|
272869
|
+
const resolvedBody = await this.resolveBodyTemplates(step.body || {});
|
|
272870
|
+
finalBody = { ...injections.body, ...resolvedBody };
|
|
272871
|
+
resolvedQuery = await this.resolveBodyTemplates(step.query || {});
|
|
272872
|
+
inputCaptured = await this.captureInputToMemory(step.captureInput, finalBody);
|
|
272873
|
+
resolvedPath = await this.resolvePathParams(step.path, finalBody);
|
|
272874
|
+
fullUrl = `${baseUrl.replace(/\/$/, "")}${resolvedPath}`;
|
|
272875
|
+
} catch (err) {
|
|
272876
|
+
await record({
|
|
272877
|
+
stepIndex: i + 1,
|
|
272878
|
+
name: stepName,
|
|
272879
|
+
method: step.method,
|
|
272880
|
+
path: step.path,
|
|
272881
|
+
targetUrl: "",
|
|
272882
|
+
expectedStatus: step.expectStatus ?? 200,
|
|
272883
|
+
actualStatus: 0,
|
|
272884
|
+
durationMs: Date.now() - stepStartTime,
|
|
272885
|
+
startedAt: new Date(stepStartTime).toISOString(),
|
|
272886
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
272887
|
+
passed: false,
|
|
272888
|
+
skipped: false,
|
|
272889
|
+
retryCount: 0,
|
|
272890
|
+
requestHeaders: {},
|
|
272891
|
+
requestBody: {},
|
|
272892
|
+
responseBody: null,
|
|
272893
|
+
capturedMemory: [],
|
|
272894
|
+
error: `Step ${i + 1} "${stepName}": ${err?.message || err}`
|
|
272895
|
+
});
|
|
272896
|
+
state.allPassed = false;
|
|
272897
|
+
state.halted = true;
|
|
272898
|
+
return "stop";
|
|
272899
|
+
}
|
|
272798
272900
|
const expectedStatus = step.expectStatus ?? 200;
|
|
272901
|
+
let requestUrl = fullUrl;
|
|
272902
|
+
{
|
|
272903
|
+
const upperMethod = step.method.toUpperCase();
|
|
272904
|
+
const bodyAsQuery = ["GET", "HEAD"].includes(upperMethod) && Object.keys(finalBody).length > 0;
|
|
272905
|
+
if (Object.keys(resolvedQuery).length > 0 || bodyAsQuery) {
|
|
272906
|
+
const params = new URLSearchParams();
|
|
272907
|
+
const append = (source) => {
|
|
272908
|
+
for (const [k, v] of Object.entries(source)) {
|
|
272909
|
+
if (v === void 0 || v === null)
|
|
272910
|
+
continue;
|
|
272911
|
+
if (Array.isArray(v)) {
|
|
272912
|
+
for (const item of v)
|
|
272913
|
+
params.append(k, typeof item === "object" ? JSON.stringify(item) : String(item));
|
|
272914
|
+
} else {
|
|
272915
|
+
params.append(k, typeof v === "object" ? JSON.stringify(v) : String(v));
|
|
272916
|
+
}
|
|
272917
|
+
}
|
|
272918
|
+
};
|
|
272919
|
+
append(resolvedQuery);
|
|
272920
|
+
if (bodyAsQuery)
|
|
272921
|
+
append(finalBody);
|
|
272922
|
+
const qs = params.toString();
|
|
272923
|
+
if (qs)
|
|
272924
|
+
requestUrl = `${fullUrl}${fullUrl.includes("?") ? "&" : "?"}${qs}`;
|
|
272925
|
+
}
|
|
272926
|
+
}
|
|
272927
|
+
const actualRequestHeaders = {
|
|
272928
|
+
"Content-Type": "application/json",
|
|
272929
|
+
...injectedHeaders
|
|
272930
|
+
};
|
|
272799
272931
|
const maxAttempts = Math.max(1, step.retry?.times ?? workflow.retry?.times ?? 1);
|
|
272800
272932
|
const retryDelayMs = step.retry?.delayMs ?? workflow.retry?.delayMs ?? 0;
|
|
272801
272933
|
let attempts = 0;
|
|
272802
272934
|
let responseStatus = 0;
|
|
272803
272935
|
let responseBody = null;
|
|
272936
|
+
let responseHeaders;
|
|
272804
272937
|
let stepPassed = false;
|
|
272805
272938
|
let errorMsg;
|
|
272939
|
+
let connectionCode;
|
|
272806
272940
|
while (attempts < maxAttempts) {
|
|
272807
272941
|
attempts++;
|
|
272808
272942
|
if (attempts > 1 && retryDelayMs > 0) {
|
|
@@ -272810,26 +272944,25 @@ var require_workflow_simulator = __commonJS({
|
|
|
272810
272944
|
}
|
|
272811
272945
|
responseStatus = 0;
|
|
272812
272946
|
responseBody = null;
|
|
272947
|
+
responseHeaders = void 0;
|
|
272813
272948
|
errorMsg = void 0;
|
|
272949
|
+
connectionCode = void 0;
|
|
272950
|
+
const timeoutMs = step.timeoutMs ?? this.requestTimeoutMs;
|
|
272814
272951
|
try {
|
|
272952
|
+
const upperMethod = step.method.toUpperCase();
|
|
272815
272953
|
const fetchOptions = {
|
|
272816
|
-
method:
|
|
272817
|
-
headers:
|
|
272954
|
+
method: upperMethod,
|
|
272955
|
+
headers: actualRequestHeaders
|
|
272818
272956
|
};
|
|
272819
|
-
if (!["GET", "HEAD"].includes(
|
|
272957
|
+
if (!["GET", "HEAD"].includes(upperMethod) && Object.keys(finalBody).length > 0) {
|
|
272820
272958
|
fetchOptions.body = JSON.stringify(finalBody);
|
|
272821
272959
|
}
|
|
272822
|
-
|
|
272823
|
-
|
|
272824
|
-
const params = new URLSearchParams();
|
|
272825
|
-
for (const [k, v] of Object.entries(finalBody)) {
|
|
272826
|
-
if (v !== void 0 && v !== null)
|
|
272827
|
-
params.set(k, String(v));
|
|
272828
|
-
}
|
|
272829
|
-
finalUrl = `${fullUrl}?${params.toString()}`;
|
|
272960
|
+
if (timeoutMs > 0) {
|
|
272961
|
+
fetchOptions.signal = AbortSignal.timeout(timeoutMs);
|
|
272830
272962
|
}
|
|
272831
|
-
const resp = await fetch(
|
|
272963
|
+
const resp = await fetch(requestUrl, fetchOptions);
|
|
272832
272964
|
responseStatus = resp.status;
|
|
272965
|
+
responseHeaders = Object.fromEntries(resp.headers.entries());
|
|
272833
272966
|
const ct = resp.headers.get("content-type") || "";
|
|
272834
272967
|
if (ct.includes("application/json")) {
|
|
272835
272968
|
try {
|
|
@@ -272846,7 +272979,14 @@ var require_workflow_simulator = __commonJS({
|
|
|
272846
272979
|
break;
|
|
272847
272980
|
} catch (err) {
|
|
272848
272981
|
stepPassed = false;
|
|
272849
|
-
|
|
272982
|
+
connectionCode = networkErrorCode2(err);
|
|
272983
|
+
if (err?.name === "TimeoutError" || connectionCode === "ETIMEDOUT") {
|
|
272984
|
+
errorMsg = `Request timed out after ${timeoutMs}ms (${step.method} ${resolvedPath}).`;
|
|
272985
|
+
} else if (connectionCode === "ECONNREFUSED") {
|
|
272986
|
+
errorMsg = `Connection refused (ECONNREFUSED) at ${baseUrl} \u2014 the server is not accepting connections.`;
|
|
272987
|
+
} else {
|
|
272988
|
+
errorMsg = err?.message || String(err);
|
|
272989
|
+
}
|
|
272850
272990
|
}
|
|
272851
272991
|
}
|
|
272852
272992
|
const responseCaptured = stepPassed ? await this.captureToMemory(step.capture, responseBody) : [];
|
|
@@ -272857,24 +272997,36 @@ var require_workflow_simulator = __commonJS({
|
|
|
272857
272997
|
method: step.method,
|
|
272858
272998
|
path: step.path,
|
|
272859
272999
|
targetUrl: fullUrl,
|
|
273000
|
+
resolvedUrl: requestUrl,
|
|
272860
273001
|
expectedStatus,
|
|
272861
273002
|
actualStatus: responseStatus,
|
|
272862
273003
|
durationMs: Date.now() - stepStartTime,
|
|
273004
|
+
startedAt: new Date(stepStartTime).toISOString(),
|
|
273005
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
272863
273006
|
passed: stepPassed,
|
|
272864
273007
|
skipped: false,
|
|
272865
273008
|
retryCount: attempts - 1,
|
|
272866
|
-
requestHeaders:
|
|
273009
|
+
requestHeaders: actualRequestHeaders,
|
|
272867
273010
|
requestBody: finalBody,
|
|
273011
|
+
...Object.keys(resolvedQuery).length > 0 ? { requestQuery: resolvedQuery } : {},
|
|
272868
273012
|
responseBody,
|
|
273013
|
+
...responseHeaders ? { responseHeaders } : {},
|
|
272869
273014
|
capturedMemory: stepPassed ? [...inputCaptured, ...responseCaptured] : [...inputCaptured],
|
|
272870
273015
|
...stepPassed ? {} : {
|
|
272871
273016
|
failureHandling: onFailure === "continue" ? "continued" : "aborted",
|
|
272872
273017
|
error: errorMsg
|
|
272873
273018
|
}
|
|
272874
273019
|
};
|
|
272875
|
-
|
|
273020
|
+
await record(result);
|
|
272876
273021
|
if (!stepPassed) {
|
|
272877
273022
|
state.allPassed = false;
|
|
273023
|
+
if (connectionCode && exports2.CONNECTION_ERROR_CODES.has(connectionCode)) {
|
|
273024
|
+
const remaining = workflow.steps.length - (i + 1);
|
|
273025
|
+
result.failureHandling = "aborted";
|
|
273026
|
+
result.error = `${result.error || "Connection failed"}; aborting early because the target is unreachable${remaining > 0 ? ` (${remaining} step(s) not executed)` : ""}.`;
|
|
273027
|
+
state.halted = true;
|
|
273028
|
+
return "stop";
|
|
273029
|
+
}
|
|
272878
273030
|
if (onFailure === "abort") {
|
|
272879
273031
|
state.halted = true;
|
|
272880
273032
|
return "stop";
|
|
@@ -272917,55 +273069,120 @@ var require_workflow_simulator = __commonJS({
|
|
|
272917
273069
|
}
|
|
272918
273070
|
return "done";
|
|
272919
273071
|
}
|
|
272920
|
-
|
|
272921
|
-
const
|
|
272922
|
-
let
|
|
272923
|
-
|
|
272924
|
-
|
|
272925
|
-
|
|
272926
|
-
if (expr.includes(":") && !expr.startsWith("faker.")) {
|
|
272927
|
-
const colonIdx = expr.indexOf(":");
|
|
272928
|
-
const scope = expr.slice(0, colonIdx);
|
|
272929
|
-
const key = expr.slice(colonIdx + 1);
|
|
272930
|
-
if (scope === "human") {
|
|
272931
|
-
const envVal = process.env[`JETIC_HUMAN_${key.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`];
|
|
272932
|
-
if (envVal !== void 0 && envVal !== "") {
|
|
272933
|
-
replacements.push({ placeholder: match[0], resolved: envVal });
|
|
272934
|
-
continue;
|
|
272935
|
-
}
|
|
272936
|
-
}
|
|
272937
|
-
const mem = this.memory(scope);
|
|
272938
|
-
const memVal = await mem.get(key);
|
|
272939
|
-
replacements.push({ placeholder: match[0], resolved: memVal != null ? String(memVal) : "" });
|
|
273072
|
+
findPlaceholders(value) {
|
|
273073
|
+
const placeholders = [];
|
|
273074
|
+
let i = 0;
|
|
273075
|
+
while (i < value.length - 1) {
|
|
273076
|
+
if (value[i] !== "{" || value[i + 1] !== "{") {
|
|
273077
|
+
i++;
|
|
272940
273078
|
continue;
|
|
272941
273079
|
}
|
|
272942
|
-
|
|
272943
|
-
|
|
272944
|
-
|
|
272945
|
-
|
|
272946
|
-
|
|
272947
|
-
|
|
272948
|
-
|
|
272949
|
-
|
|
272950
|
-
|
|
272951
|
-
|
|
273080
|
+
let depth = 0;
|
|
273081
|
+
let closed = -1;
|
|
273082
|
+
let j = i + 2;
|
|
273083
|
+
while (j < value.length) {
|
|
273084
|
+
const ch = value[j];
|
|
273085
|
+
if (ch === "{")
|
|
273086
|
+
depth++;
|
|
273087
|
+
else if (ch === "}") {
|
|
273088
|
+
if (depth > 0)
|
|
273089
|
+
depth--;
|
|
273090
|
+
else if (value[j + 1] === "}") {
|
|
273091
|
+
closed = j;
|
|
273092
|
+
break;
|
|
273093
|
+
}
|
|
272952
273094
|
}
|
|
272953
|
-
|
|
273095
|
+
j++;
|
|
273096
|
+
}
|
|
273097
|
+
if (closed === -1)
|
|
273098
|
+
break;
|
|
273099
|
+
placeholders.push({ raw: value.slice(i, closed + 2), expr: value.slice(i + 2, closed).trim(), index: i });
|
|
273100
|
+
i = closed + 2;
|
|
273101
|
+
}
|
|
273102
|
+
return placeholders;
|
|
273103
|
+
}
|
|
273104
|
+
async resolveTemplateString(value, strict = this.strictTemplates) {
|
|
273105
|
+
const placeholders = this.findPlaceholders(value);
|
|
273106
|
+
if (placeholders.length === 0)
|
|
273107
|
+
return value;
|
|
273108
|
+
let result = "";
|
|
273109
|
+
let cursor = 0;
|
|
273110
|
+
for (const placeholder of placeholders) {
|
|
273111
|
+
result += value.slice(cursor, placeholder.index);
|
|
273112
|
+
result += await this.resolveExpression(placeholder.expr, strict, placeholder.raw);
|
|
273113
|
+
cursor = placeholder.index + placeholder.raw.length;
|
|
273114
|
+
}
|
|
273115
|
+
return result + value.slice(cursor);
|
|
273116
|
+
}
|
|
273117
|
+
async resolveExpression(expr, strict, raw) {
|
|
273118
|
+
if (expr.startsWith("faker.")) {
|
|
273119
|
+
return this.resolveFakerExpression(expr, strict, raw);
|
|
273120
|
+
}
|
|
273121
|
+
if (expr.includes(":")) {
|
|
273122
|
+
const colonIdx = expr.indexOf(":");
|
|
273123
|
+
const scope = expr.slice(0, colonIdx);
|
|
273124
|
+
const key = expr.slice(colonIdx + 1);
|
|
273125
|
+
const envKey = `JETIC_HUMAN_${key.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
273126
|
+
if (scope === "human") {
|
|
273127
|
+
const envVal = process.env[envKey];
|
|
273128
|
+
if (envVal !== void 0 && envVal !== "")
|
|
273129
|
+
return envVal;
|
|
273130
|
+
}
|
|
273131
|
+
const memVal = await this.memory(scope).get(key);
|
|
273132
|
+
if (memVal !== null && memVal !== void 0) {
|
|
273133
|
+
return typeof memVal === "string" ? memVal : JSON.stringify(memVal);
|
|
273134
|
+
}
|
|
273135
|
+
if (!strict)
|
|
273136
|
+
return "";
|
|
273137
|
+
if (scope === "human") {
|
|
273138
|
+
throw new Error(`missing human input ${raw} \u2014 set ${envKey} or seed "human:${key}" memory (non-interactive runners cannot prompt)`);
|
|
272954
273139
|
}
|
|
272955
|
-
|
|
273140
|
+
throw new Error(`unresolved template ${raw} \u2014 no value for "${scope}:${key}"; capture it in an earlier step or seed it first`);
|
|
272956
273141
|
}
|
|
272957
|
-
|
|
272958
|
-
|
|
272959
|
-
|
|
273142
|
+
return raw;
|
|
273143
|
+
}
|
|
273144
|
+
resolveFakerExpression(expr, strict, raw) {
|
|
273145
|
+
if (/[({]/.test(expr)) {
|
|
273146
|
+
if (!strict)
|
|
273147
|
+
return raw;
|
|
273148
|
+
throw new Error(`unsupported faker expression ${raw} \u2014 parameterized calls are not supported; use a parameterless path like {{faker.internet.email}} or a literal value`);
|
|
273149
|
+
}
|
|
273150
|
+
let fn = faker_1.faker;
|
|
273151
|
+
for (const part of expr.split(".").slice(1))
|
|
273152
|
+
fn = fn?.[part];
|
|
273153
|
+
if (typeof fn !== "function") {
|
|
273154
|
+
if (!strict)
|
|
273155
|
+
return raw;
|
|
273156
|
+
throw new Error(`unknown faker path ${raw} \u2014 use a complete parameterless path like {{faker.internet.email}} or {{faker.string.uuid}}`);
|
|
273157
|
+
}
|
|
273158
|
+
try {
|
|
273159
|
+
return String(fn());
|
|
273160
|
+
} catch (err) {
|
|
273161
|
+
if (!strict)
|
|
273162
|
+
return raw;
|
|
273163
|
+
throw new Error(`faker expression ${raw} failed: ${err?.message || err} \u2014 this faker path needs arguments; parameterized faker calls are not supported`);
|
|
272960
273164
|
}
|
|
272961
|
-
return result;
|
|
272962
273165
|
}
|
|
272963
273166
|
async resolveBodyTemplates(body) {
|
|
272964
|
-
const resolved =
|
|
272965
|
-
|
|
272966
|
-
|
|
273167
|
+
const resolved = await this.resolveDeep(body);
|
|
273168
|
+
return resolved && typeof resolved === "object" && !Array.isArray(resolved) ? resolved : {};
|
|
273169
|
+
}
|
|
273170
|
+
async resolveDeep(value) {
|
|
273171
|
+
if (typeof value === "string")
|
|
273172
|
+
return this.resolveTemplateString(value);
|
|
273173
|
+
if (Array.isArray(value)) {
|
|
273174
|
+
const out = [];
|
|
273175
|
+
for (const item of value)
|
|
273176
|
+
out.push(await this.resolveDeep(item));
|
|
273177
|
+
return out;
|
|
273178
|
+
}
|
|
273179
|
+
if (value && typeof value === "object") {
|
|
273180
|
+
const out = {};
|
|
273181
|
+
for (const [key, item] of Object.entries(value))
|
|
273182
|
+
out[key] = await this.resolveDeep(item);
|
|
273183
|
+
return out;
|
|
272967
273184
|
}
|
|
272968
|
-
return
|
|
273185
|
+
return value;
|
|
272969
273186
|
}
|
|
272970
273187
|
async resolveInjections(inject) {
|
|
272971
273188
|
const headers = {};
|
|
@@ -272980,10 +273197,18 @@ var require_workflow_simulator = __commonJS({
|
|
|
272980
273197
|
const [scope, key] = memKeyOrTemplate.includes(":") ? memKeyOrTemplate.split(":", 2) : ["workflow", memKeyOrTemplate];
|
|
272981
273198
|
const scopedMemory = this.memory(scope);
|
|
272982
273199
|
const value = await scopedMemory.get(key);
|
|
272983
|
-
if (value === null)
|
|
273200
|
+
if (value === null || value === void 0) {
|
|
273201
|
+
if (this.strictTemplates) {
|
|
273202
|
+
throw new Error(`inject target "${target}" references "${scope}:${key}", which is not in memory \u2014 capture it earlier or seed it first`);
|
|
273203
|
+
}
|
|
272984
273204
|
continue;
|
|
273205
|
+
}
|
|
272985
273206
|
strValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
272986
273207
|
}
|
|
273208
|
+
const headerName = target.startsWith("header:") ? target.slice("header:".length) : target.startsWith("body:") ? null : target;
|
|
273209
|
+
if (headerName && headerName.toLowerCase() === "authorization" && strValue && !/^[A-Za-z][A-Za-z0-9-]*\s/.test(strValue)) {
|
|
273210
|
+
strValue = `Bearer ${strValue}`;
|
|
273211
|
+
}
|
|
272987
273212
|
if (target.startsWith("body:")) {
|
|
272988
273213
|
body[target.slice(5)] = strValue;
|
|
272989
273214
|
} else if (target.startsWith("header:")) {
|
|
@@ -273025,26 +273250,59 @@ var require_workflow_simulator = __commonJS({
|
|
|
273025
273250
|
return captured;
|
|
273026
273251
|
}
|
|
273027
273252
|
async resolvePathParams(urlPath, body) {
|
|
273028
|
-
let result = urlPath;
|
|
273029
|
-
const paramMatches =
|
|
273253
|
+
let result = await this.resolveTemplateString(urlPath);
|
|
273254
|
+
const paramMatches = result.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g);
|
|
273030
273255
|
if (!paramMatches)
|
|
273031
273256
|
return result;
|
|
273032
|
-
for (const p of paramMatches) {
|
|
273257
|
+
for (const p of [...new Set(paramMatches)].sort((a, b) => b.length - a.length)) {
|
|
273033
273258
|
const paramName = p.substring(1);
|
|
273034
|
-
|
|
273035
|
-
|
|
273036
|
-
|
|
273037
|
-
|
|
273038
|
-
|
|
273039
|
-
|
|
273040
|
-
|
|
273041
|
-
|
|
273259
|
+
let value = body[paramName];
|
|
273260
|
+
if (value === void 0 || value === null) {
|
|
273261
|
+
value = await this.memory("workflow").get(paramName);
|
|
273262
|
+
}
|
|
273263
|
+
if (value !== void 0 && value !== null) {
|
|
273264
|
+
result = result.split(p).join(typeof value === "string" ? value : String(value));
|
|
273265
|
+
continue;
|
|
273266
|
+
}
|
|
273267
|
+
if (this.strictTemplates) {
|
|
273268
|
+
throw new Error(`unresolved path parameter "${p}" in "${urlPath}" \u2014 provide it through a body field, a {{template}}, or a memory key named "${paramName}"`);
|
|
273042
273269
|
}
|
|
273043
273270
|
}
|
|
273044
273271
|
return result;
|
|
273045
273272
|
}
|
|
273046
273273
|
};
|
|
273047
273274
|
exports2.WorkflowSimulator = WorkflowSimulator2;
|
|
273275
|
+
async function probeTarget2(baseUrl, timeoutMs = 3e3) {
|
|
273276
|
+
const probe = { url: baseUrl, verdict: "unreachable", message: "", aborted: false };
|
|
273277
|
+
try {
|
|
273278
|
+
const resp = await fetch(baseUrl, {
|
|
273279
|
+
method: "GET",
|
|
273280
|
+
redirect: "manual",
|
|
273281
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
273282
|
+
});
|
|
273283
|
+
probe.status = resp.status;
|
|
273284
|
+
if (resp.status >= 500) {
|
|
273285
|
+
probe.verdict = "server-error";
|
|
273286
|
+
probe.message = `Server is reachable at ${baseUrl} but responded HTTP ${resp.status} \u2014 the app is likely failing (database/init error). Check the server logs before simulating.`;
|
|
273287
|
+
} else {
|
|
273288
|
+
probe.verdict = "healthy";
|
|
273289
|
+
probe.message = `Server reachable at ${baseUrl} (HTTP ${resp.status}).`;
|
|
273290
|
+
}
|
|
273291
|
+
} catch (err) {
|
|
273292
|
+
const code = networkErrorCode2(err);
|
|
273293
|
+
probe.status = void 0;
|
|
273294
|
+
if (err?.name === "TimeoutError" || code === "ETIMEDOUT") {
|
|
273295
|
+
probe.message = `No response from ${baseUrl} within ${timeoutMs}ms \u2014 the server may be down or hanging.`;
|
|
273296
|
+
} else if (code === "ECONNREFUSED") {
|
|
273297
|
+
probe.message = `Connection refused at ${baseUrl} \u2014 nothing is listening there. Start the app (check its PORT/.env) and retry.`;
|
|
273298
|
+
} else if (code === "ENOTFOUND" || code === "EAI_AGAIN") {
|
|
273299
|
+
probe.message = `DNS lookup failed for ${baseUrl}${code ? ` (${code})` : ""} \u2014 check the host name.`;
|
|
273300
|
+
} else {
|
|
273301
|
+
probe.message = `Cannot reach ${baseUrl}${code ? ` (${code})` : ""}: ${err?.message || err}`;
|
|
273302
|
+
}
|
|
273303
|
+
}
|
|
273304
|
+
return probe;
|
|
273305
|
+
}
|
|
273048
273306
|
}
|
|
273049
273307
|
});
|
|
273050
273308
|
|
|
@@ -273070,7 +273328,7 @@ var require_dist13 = __commonJS({
|
|
|
273070
273328
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
|
|
273071
273329
|
};
|
|
273072
273330
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
273073
|
-
exports2.WorkflowSimulator = exports2.ResponseValidator = exports2.DataGenerator = exports2.EndpointSimulator = void 0;
|
|
273331
|
+
exports2.CONNECTION_ERROR_CODES = exports2.networkErrorCode = exports2.probeTarget = exports2.WorkflowSimulator = exports2.ResponseValidator = exports2.DataGenerator = exports2.EndpointSimulator = void 0;
|
|
273074
273332
|
var simulator_1 = require_simulator();
|
|
273075
273333
|
Object.defineProperty(exports2, "EndpointSimulator", { enumerable: true, get: function() {
|
|
273076
273334
|
return simulator_1.EndpointSimulator;
|
|
@@ -273087,6 +273345,15 @@ var require_dist13 = __commonJS({
|
|
|
273087
273345
|
Object.defineProperty(exports2, "WorkflowSimulator", { enumerable: true, get: function() {
|
|
273088
273346
|
return workflow_simulator_1.WorkflowSimulator;
|
|
273089
273347
|
} });
|
|
273348
|
+
Object.defineProperty(exports2, "probeTarget", { enumerable: true, get: function() {
|
|
273349
|
+
return workflow_simulator_1.probeTarget;
|
|
273350
|
+
} });
|
|
273351
|
+
Object.defineProperty(exports2, "networkErrorCode", { enumerable: true, get: function() {
|
|
273352
|
+
return workflow_simulator_1.networkErrorCode;
|
|
273353
|
+
} });
|
|
273354
|
+
Object.defineProperty(exports2, "CONNECTION_ERROR_CODES", { enumerable: true, get: function() {
|
|
273355
|
+
return workflow_simulator_1.CONNECTION_ERROR_CODES;
|
|
273356
|
+
} });
|
|
273090
273357
|
__exportStar(require_conditions(), exports2);
|
|
273091
273358
|
}
|
|
273092
273359
|
});
|
|
@@ -273242,6 +273509,314 @@ var require_environment_tools = __commonJS({
|
|
|
273242
273509
|
}
|
|
273243
273510
|
});
|
|
273244
273511
|
|
|
273512
|
+
// ../../packages/mcp-server/dist/run-store.js
|
|
273513
|
+
var require_run_store = __commonJS({
|
|
273514
|
+
"../../packages/mcp-server/dist/run-store.js"(exports2) {
|
|
273515
|
+
"use strict";
|
|
273516
|
+
init_cjs_shims();
|
|
273517
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
273518
|
+
if (k2 === void 0) k2 = k;
|
|
273519
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
273520
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
273521
|
+
desc = { enumerable: true, get: function() {
|
|
273522
|
+
return m[k];
|
|
273523
|
+
} };
|
|
273524
|
+
}
|
|
273525
|
+
Object.defineProperty(o, k2, desc);
|
|
273526
|
+
}) : (function(o, m, k, k2) {
|
|
273527
|
+
if (k2 === void 0) k2 = k;
|
|
273528
|
+
o[k2] = m[k];
|
|
273529
|
+
}));
|
|
273530
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
273531
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
273532
|
+
}) : function(o, v) {
|
|
273533
|
+
o["default"] = v;
|
|
273534
|
+
});
|
|
273535
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
273536
|
+
var ownKeys = function(o) {
|
|
273537
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
273538
|
+
var ar = [];
|
|
273539
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
273540
|
+
return ar;
|
|
273541
|
+
};
|
|
273542
|
+
return ownKeys(o);
|
|
273543
|
+
};
|
|
273544
|
+
return function(mod) {
|
|
273545
|
+
if (mod && mod.__esModule) return mod;
|
|
273546
|
+
var result = {};
|
|
273547
|
+
if (mod != null) {
|
|
273548
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
273549
|
+
}
|
|
273550
|
+
__setModuleDefault(result, mod);
|
|
273551
|
+
return result;
|
|
273552
|
+
};
|
|
273553
|
+
})();
|
|
273554
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
273555
|
+
exports2.MAX_PERSISTED_BODY_CHARS = void 0;
|
|
273556
|
+
exports2.newRunId = newRunId;
|
|
273557
|
+
exports2.runsDir = runsDir;
|
|
273558
|
+
exports2.runDir = runDir;
|
|
273559
|
+
exports2.deriveRunStatus = deriveRunStatus;
|
|
273560
|
+
exports2.stepToProgress = stepToProgress;
|
|
273561
|
+
exports2.statsFromSteps = statsFromSteps;
|
|
273562
|
+
exports2.truncateResultForStorage = truncateResultForStorage;
|
|
273563
|
+
exports2.writeSimulationRun = writeSimulationRun;
|
|
273564
|
+
exports2.readSimulationRun = readSimulationRun;
|
|
273565
|
+
exports2.readRunStatus = readRunStatus;
|
|
273566
|
+
exports2.writeRunningStatus = writeRunningStatus;
|
|
273567
|
+
exports2.updateRunStatus = updateRunStatus;
|
|
273568
|
+
exports2.writeRunError = writeRunError;
|
|
273569
|
+
exports2.listRuns = listRuns;
|
|
273570
|
+
exports2.compactSimulationResult = compactSimulationResult;
|
|
273571
|
+
var fs10 = __importStar(require("fs"));
|
|
273572
|
+
var path15 = __importStar(require("path"));
|
|
273573
|
+
var core_1 = require_dist();
|
|
273574
|
+
function newRunId() {
|
|
273575
|
+
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
273576
|
+
}
|
|
273577
|
+
function runsDir(jeticDir) {
|
|
273578
|
+
return path15.join(jeticDir, "runs");
|
|
273579
|
+
}
|
|
273580
|
+
function runDir(jeticDir, runId) {
|
|
273581
|
+
return path15.join(runsDir(jeticDir), runId);
|
|
273582
|
+
}
|
|
273583
|
+
function reportPath(jeticDir, runId) {
|
|
273584
|
+
return path15.join(runDir(jeticDir, runId), "simulation-report.json");
|
|
273585
|
+
}
|
|
273586
|
+
function statusPath(jeticDir, runId) {
|
|
273587
|
+
return path15.join(runDir(jeticDir, runId), "simulation-status.json");
|
|
273588
|
+
}
|
|
273589
|
+
function deriveRunStatus(result) {
|
|
273590
|
+
if (result.success)
|
|
273591
|
+
return "passed";
|
|
273592
|
+
if (result.abortReason)
|
|
273593
|
+
return "aborted";
|
|
273594
|
+
return "failed";
|
|
273595
|
+
}
|
|
273596
|
+
function stepToProgress(r) {
|
|
273597
|
+
return {
|
|
273598
|
+
index: r.stepIndex,
|
|
273599
|
+
name: r.name,
|
|
273600
|
+
method: r.method,
|
|
273601
|
+
path: r.path,
|
|
273602
|
+
status: r.skipped ? "skipped" : r.passed ? "passed" : "failed",
|
|
273603
|
+
expectedStatus: r.expectedStatus,
|
|
273604
|
+
...r.skipped ? {} : { actualStatus: r.actualStatus },
|
|
273605
|
+
passed: r.passed,
|
|
273606
|
+
skipped: r.skipped,
|
|
273607
|
+
...r.retryCount > 0 ? { retryCount: r.retryCount } : {},
|
|
273608
|
+
durationMs: r.durationMs,
|
|
273609
|
+
startedAt: r.startedAt,
|
|
273610
|
+
finishedAt: r.finishedAt,
|
|
273611
|
+
...r.error ? { error: r.error } : {}
|
|
273612
|
+
};
|
|
273613
|
+
}
|
|
273614
|
+
function statsFromSteps(steps) {
|
|
273615
|
+
const completed = steps.filter((s) => s.status !== "pending");
|
|
273616
|
+
return {
|
|
273617
|
+
totalSteps: steps.length,
|
|
273618
|
+
executedSteps: completed.length,
|
|
273619
|
+
passedSteps: completed.filter((s) => s.status === "passed" || s.status === "skipped").length,
|
|
273620
|
+
failedSteps: completed.filter((s) => s.status === "failed").length,
|
|
273621
|
+
skippedSteps: completed.filter((s) => s.status === "skipped").length
|
|
273622
|
+
};
|
|
273623
|
+
}
|
|
273624
|
+
exports2.MAX_PERSISTED_BODY_CHARS = 16384;
|
|
273625
|
+
function truncateResultForStorage(result, maxChars = exports2.MAX_PERSISTED_BODY_CHARS) {
|
|
273626
|
+
const clone = JSON.parse(JSON.stringify(result));
|
|
273627
|
+
for (const step of clone.results) {
|
|
273628
|
+
if (step.responseBody === void 0 || step.responseBody === null)
|
|
273629
|
+
continue;
|
|
273630
|
+
const serialized = typeof step.responseBody === "string" ? step.responseBody : JSON.stringify(step.responseBody);
|
|
273631
|
+
if (serialized.length <= maxChars)
|
|
273632
|
+
continue;
|
|
273633
|
+
const removed = serialized.length - maxChars;
|
|
273634
|
+
step.responseBody = typeof step.responseBody === "string" ? `${step.responseBody.slice(0, maxChars)}
|
|
273635
|
+
\u2026[truncated: ${removed} more characters]` : { _truncated: true, preview: serialized.slice(0, maxChars), fullLengthChars: serialized.length };
|
|
273636
|
+
step.responseBodyTruncated = true;
|
|
273637
|
+
}
|
|
273638
|
+
return clone;
|
|
273639
|
+
}
|
|
273640
|
+
function writeSimulationRun(jeticDir, runId, result, meta) {
|
|
273641
|
+
const status = deriveRunStatus(result);
|
|
273642
|
+
const durationMs = result.stats?.totalTimeMs ?? 0;
|
|
273643
|
+
const report = {
|
|
273644
|
+
type: "simulation",
|
|
273645
|
+
runId,
|
|
273646
|
+
workflowName: result.workflow?.name || "workflow",
|
|
273647
|
+
workflowFile: meta.workflowFile,
|
|
273648
|
+
environment: result.environment?.name || "unknown",
|
|
273649
|
+
baseUrl: result.environment?.baseUrl || "",
|
|
273650
|
+
status,
|
|
273651
|
+
startedAt: meta.startedAt,
|
|
273652
|
+
finishedAt: meta.finishedAt,
|
|
273653
|
+
durationMs,
|
|
273654
|
+
result: truncateResultForStorage(result)
|
|
273655
|
+
};
|
|
273656
|
+
const rPath = reportPath(jeticDir, runId);
|
|
273657
|
+
(0, core_1.writeJsonSync)(rPath, report);
|
|
273658
|
+
const sPath = statusPath(jeticDir, runId);
|
|
273659
|
+
const statusDoc = {
|
|
273660
|
+
type: "simulation",
|
|
273661
|
+
runId,
|
|
273662
|
+
workflowName: report.workflowName,
|
|
273663
|
+
environment: report.environment,
|
|
273664
|
+
baseUrl: report.baseUrl,
|
|
273665
|
+
status,
|
|
273666
|
+
startedAt: meta.startedAt,
|
|
273667
|
+
finishedAt: meta.finishedAt,
|
|
273668
|
+
durationMs,
|
|
273669
|
+
stats: {
|
|
273670
|
+
totalSteps: result.stats?.totalSteps ?? result.results.length,
|
|
273671
|
+
executedSteps: result.stats?.executedSteps ?? 0,
|
|
273672
|
+
passedSteps: result.stats?.passedSteps ?? 0,
|
|
273673
|
+
failedSteps: result.stats?.failedSteps ?? 0,
|
|
273674
|
+
skippedSteps: result.results.filter((r) => r.skipped).length
|
|
273675
|
+
},
|
|
273676
|
+
steps: result.results.map(stepToProgress),
|
|
273677
|
+
reportPath: rPath
|
|
273678
|
+
};
|
|
273679
|
+
(0, core_1.writeJsonSync)(sPath, statusDoc);
|
|
273680
|
+
return { runDir: runDir(jeticDir, runId), reportPath: rPath };
|
|
273681
|
+
}
|
|
273682
|
+
function readSimulationRun(jeticDir, runId) {
|
|
273683
|
+
return (0, core_1.readJsonSync)(reportPath(jeticDir, runId));
|
|
273684
|
+
}
|
|
273685
|
+
function readRunStatus(jeticDir, runId) {
|
|
273686
|
+
return (0, core_1.readJsonSync)(statusPath(jeticDir, runId));
|
|
273687
|
+
}
|
|
273688
|
+
function writeRunningStatus(jeticDir, runId, meta) {
|
|
273689
|
+
const steps = meta.steps?.map((s, i) => ({
|
|
273690
|
+
index: i + 1,
|
|
273691
|
+
name: s.name,
|
|
273692
|
+
method: s.method,
|
|
273693
|
+
path: s.path,
|
|
273694
|
+
status: "pending"
|
|
273695
|
+
}));
|
|
273696
|
+
const doc = {
|
|
273697
|
+
type: "simulation",
|
|
273698
|
+
runId,
|
|
273699
|
+
workflowName: meta.workflowName,
|
|
273700
|
+
environment: meta.environment,
|
|
273701
|
+
baseUrl: meta.baseUrl,
|
|
273702
|
+
status: "running",
|
|
273703
|
+
startedAt: meta.startedAt,
|
|
273704
|
+
stats: {
|
|
273705
|
+
totalSteps: steps?.length ?? 0,
|
|
273706
|
+
executedSteps: 0,
|
|
273707
|
+
passedSteps: 0,
|
|
273708
|
+
failedSteps: 0,
|
|
273709
|
+
skippedSteps: 0
|
|
273710
|
+
},
|
|
273711
|
+
...steps ? { steps } : {},
|
|
273712
|
+
reportPath: reportPath(jeticDir, runId)
|
|
273713
|
+
};
|
|
273714
|
+
(0, core_1.writeJsonSync)(statusPath(jeticDir, runId), doc);
|
|
273715
|
+
}
|
|
273716
|
+
function updateRunStatus(jeticDir, runId, update) {
|
|
273717
|
+
const doc = (0, core_1.readJsonSync)(statusPath(jeticDir, runId));
|
|
273718
|
+
if (!doc)
|
|
273719
|
+
return;
|
|
273720
|
+
update(doc);
|
|
273721
|
+
(0, core_1.writeJsonSync)(statusPath(jeticDir, runId), doc);
|
|
273722
|
+
}
|
|
273723
|
+
function writeRunError(jeticDir, runId, error, meta) {
|
|
273724
|
+
const doc = {
|
|
273725
|
+
type: "simulation",
|
|
273726
|
+
runId,
|
|
273727
|
+
workflowName: meta.workflowName,
|
|
273728
|
+
environment: meta.environment,
|
|
273729
|
+
baseUrl: meta.baseUrl,
|
|
273730
|
+
status: "error",
|
|
273731
|
+
startedAt: meta.startedAt,
|
|
273732
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
273733
|
+
error: error instanceof Error ? error.message : String(error),
|
|
273734
|
+
stats: { totalSteps: 0, executedSteps: 0, passedSteps: 0, failedSteps: 0, skippedSteps: 0 },
|
|
273735
|
+
reportPath: reportPath(jeticDir, runId)
|
|
273736
|
+
};
|
|
273737
|
+
(0, core_1.writeJsonSync)(statusPath(jeticDir, runId), doc);
|
|
273738
|
+
}
|
|
273739
|
+
function listRuns(jeticDir, limit = 20) {
|
|
273740
|
+
const dir = runsDir(jeticDir);
|
|
273741
|
+
if (!fs10.existsSync(dir))
|
|
273742
|
+
return [];
|
|
273743
|
+
const items = [];
|
|
273744
|
+
for (const entry of fs10.readdirSync(dir, { withFileTypes: true })) {
|
|
273745
|
+
if (!entry.isDirectory())
|
|
273746
|
+
continue;
|
|
273747
|
+
const runId = entry.name;
|
|
273748
|
+
const simStatus = (0, core_1.readJsonSync)(statusPath(jeticDir, runId));
|
|
273749
|
+
if (simStatus) {
|
|
273750
|
+
items.push({
|
|
273751
|
+
runId,
|
|
273752
|
+
type: "simulation",
|
|
273753
|
+
name: simStatus.workflowName,
|
|
273754
|
+
status: simStatus.status,
|
|
273755
|
+
startedAt: simStatus.startedAt,
|
|
273756
|
+
durationMs: simStatus.durationMs,
|
|
273757
|
+
reportPath: simStatus.reportPath
|
|
273758
|
+
});
|
|
273759
|
+
continue;
|
|
273760
|
+
}
|
|
273761
|
+
const agentReport = (0, core_1.readJsonSync)(path15.join(runDir(jeticDir, runId), "agent-report.json"));
|
|
273762
|
+
if (agentReport) {
|
|
273763
|
+
items.push({
|
|
273764
|
+
runId,
|
|
273765
|
+
type: "agent",
|
|
273766
|
+
name: agentReport.agentId || "agent",
|
|
273767
|
+
status: agentReport.status || "completed",
|
|
273768
|
+
reportPath: path15.join(runDir(jeticDir, runId), "agent-report.json")
|
|
273769
|
+
});
|
|
273770
|
+
}
|
|
273771
|
+
}
|
|
273772
|
+
items.sort((a, b) => b.runId.localeCompare(a.runId));
|
|
273773
|
+
return items.slice(0, limit);
|
|
273774
|
+
}
|
|
273775
|
+
function compactSimulationResult(result, runId, reportFilePath) {
|
|
273776
|
+
const steps = result.results.map((r) => {
|
|
273777
|
+
const summary = {
|
|
273778
|
+
// stepIndex is already 1-based (set as i + 1 by the simulator).
|
|
273779
|
+
step: r.stepIndex,
|
|
273780
|
+
name: r.name,
|
|
273781
|
+
method: r.method,
|
|
273782
|
+
path: r.path,
|
|
273783
|
+
targetUrl: r.targetUrl,
|
|
273784
|
+
status: r.skipped ? "skipped" : r.passed ? "passed" : "failed",
|
|
273785
|
+
expectedStatus: r.expectedStatus,
|
|
273786
|
+
durationMs: r.durationMs
|
|
273787
|
+
};
|
|
273788
|
+
if (!r.skipped)
|
|
273789
|
+
summary.actualStatus = r.actualStatus;
|
|
273790
|
+
if (r.retryCount > 0)
|
|
273791
|
+
summary.retryCount = r.retryCount;
|
|
273792
|
+
if (r.conditionResult) {
|
|
273793
|
+
summary.condition = `${r.conditionResult.evaluated} \u2192 ${r.conditionResult.matched ? "matched" : "not matched"}`;
|
|
273794
|
+
}
|
|
273795
|
+
if (r.error)
|
|
273796
|
+
summary.error = r.error;
|
|
273797
|
+
return summary;
|
|
273798
|
+
});
|
|
273799
|
+
return {
|
|
273800
|
+
ok: result.success,
|
|
273801
|
+
success: result.success,
|
|
273802
|
+
runId,
|
|
273803
|
+
status: deriveRunStatus(result),
|
|
273804
|
+
workflow: result.workflow?.name || "workflow",
|
|
273805
|
+
environment: {
|
|
273806
|
+
name: result.environment?.name || "unknown",
|
|
273807
|
+
baseUrl: result.environment?.baseUrl || ""
|
|
273808
|
+
},
|
|
273809
|
+
stats: result.stats,
|
|
273810
|
+
...result.preflight ? { preflight: result.preflight } : {},
|
|
273811
|
+
...result.abortReason ? { abortReason: result.abortReason } : {},
|
|
273812
|
+
steps,
|
|
273813
|
+
reportPath: reportFilePath,
|
|
273814
|
+
next: result.success ? `Full request/response detail: jetic_get_run { "runId": "${runId}" }` : `Inspect the failing step in full detail: jetic_get_run { "runId": "${runId}" }`
|
|
273815
|
+
};
|
|
273816
|
+
}
|
|
273817
|
+
}
|
|
273818
|
+
});
|
|
273819
|
+
|
|
273245
273820
|
// ../../packages/mcp-server/dist/tools/workflow-tools.js
|
|
273246
273821
|
var require_workflow_tools = __commonJS({
|
|
273247
273822
|
"../../packages/mcp-server/dist/tools/workflow-tools.js"(exports2) {
|
|
@@ -273294,6 +273869,7 @@ var require_workflow_tools = __commonJS({
|
|
|
273294
273869
|
var types_1 = require_types();
|
|
273295
273870
|
var model_1 = require_dist2();
|
|
273296
273871
|
var simulator_1 = require_dist13();
|
|
273872
|
+
var run_store_1 = require_run_store();
|
|
273297
273873
|
exports2.listWorkflowsSchema = zod_1.z.object({
|
|
273298
273874
|
projectPath: zod_1.z.string().optional().describe("Path to project root or .jetic folder")
|
|
273299
273875
|
});
|
|
@@ -273385,7 +273961,12 @@ var require_workflow_tools = __commonJS({
|
|
|
273385
273961
|
workflow: zod_1.z.string().optional().describe("Workflow slug, name, or JSON file path to execute. If omitted, defaults to available workflow"),
|
|
273386
273962
|
envName: zod_1.z.string().optional().describe("Target environment name (e.g., local, staging)"),
|
|
273387
273963
|
baseUrl: zod_1.z.string().optional().describe("Override target base URL (e.g. http://localhost:3000)"),
|
|
273388
|
-
clearMemory: zod_1.z.boolean().optional().default(false).describe("If true, clears Jetic memory before execution")
|
|
273964
|
+
clearMemory: zod_1.z.boolean().optional().default(false).describe("If true, clears Jetic memory before execution"),
|
|
273965
|
+
includeStepResults: zod_1.z.boolean().optional().default(false).describe("Return every step's full request/response detail (large payload). Default false returns a compact per-step summary plus a runId \u2014 use jetic_get_run for full detail."),
|
|
273966
|
+
async: zod_1.z.boolean().optional().default(false).describe("Start the run in the background and return immediately with a runId. Poll it with jetic_get_run or jetic_list_runs."),
|
|
273967
|
+
preflight: zod_1.z.enum(["abort", "warn", "off"]).optional().default("abort").describe('Probe the target baseUrl before step 1. "abort" (default) fails fast when the server is unreachable; "warn" records the probe and continues; "off" skips it.'),
|
|
273968
|
+
strictTemplates: zod_1.z.boolean().optional().default(true).describe("Fail a step before sending when a {{template}} or :path param has no value (default true). Set false to restore legacy empty-string behaviour."),
|
|
273969
|
+
requestTimeoutMs: zod_1.z.number().int().positive().max(12e4).optional().default(3e4).describe("Per-request timeout in ms (default 30000, max 120000).")
|
|
273389
273970
|
});
|
|
273390
273971
|
async function handleSimulateWorkflow(args) {
|
|
273391
273972
|
const { model, filePath } = (0, types_1.loadModel)(args.projectPath);
|
|
@@ -273472,14 +274053,82 @@ var require_workflow_tools = __commonJS({
|
|
|
273472
274053
|
baseUrl: args.baseUrl || targetEnv.baseUrl
|
|
273473
274054
|
};
|
|
273474
274055
|
const projectRoot = (0, types_1.projectRootOfModel)(filePath);
|
|
273475
|
-
const
|
|
274056
|
+
const jeticDir = path15.dirname(filePath);
|
|
274057
|
+
const workflowsDir = path15.join(jeticDir, "workflows");
|
|
273476
274058
|
const simulator = new simulator_1.WorkflowSimulator(model, effectiveEnv, { projectRoot });
|
|
273477
|
-
const
|
|
273478
|
-
|
|
273479
|
-
|
|
273480
|
-
|
|
274059
|
+
const runId = (0, run_store_1.newRunId)();
|
|
274060
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
274061
|
+
const plannedSteps = workflowDef.steps.map((s) => ({
|
|
274062
|
+
name: s.name || `${s.method} ${s.path}`,
|
|
274063
|
+
method: s.method,
|
|
274064
|
+
path: s.path
|
|
274065
|
+
}));
|
|
274066
|
+
const runMeta = {
|
|
274067
|
+
workflowName: workflowDef.name || selectedItem.name,
|
|
274068
|
+
environment: effectiveEnv.name,
|
|
274069
|
+
baseUrl: effectiveEnv.baseUrl,
|
|
274070
|
+
startedAt,
|
|
274071
|
+
steps: plannedSteps
|
|
274072
|
+
};
|
|
274073
|
+
const execute = async () => {
|
|
274074
|
+
const result2 = await simulator.simulateWorkflow(workflowDef, {
|
|
274075
|
+
clearMemory: args.clearMemory ?? false,
|
|
274076
|
+
loadWorkflow: (slugOrName) => (0, model_1.loadWorkflowByName)(workflowsDir, slugOrName),
|
|
274077
|
+
strictTemplates: args.strictTemplates ?? true,
|
|
274078
|
+
preflight: args.preflight ?? "abort",
|
|
274079
|
+
requestTimeoutMs: args.requestTimeoutMs ?? 3e4,
|
|
274080
|
+
// Persist per-step progress so jetic_get_run can be polled while the run is in flight.
|
|
274081
|
+
onStepResult: (step) => {
|
|
274082
|
+
(0, run_store_1.updateRunStatus)(jeticDir, runId, (doc) => {
|
|
274083
|
+
if (!doc.steps)
|
|
274084
|
+
doc.steps = [];
|
|
274085
|
+
const progress = (0, run_store_1.stepToProgress)(step);
|
|
274086
|
+
const at = doc.steps.findIndex((s) => s.index === step.stepIndex && s.name === step.name);
|
|
274087
|
+
if (at >= 0)
|
|
274088
|
+
doc.steps[at] = progress;
|
|
274089
|
+
else
|
|
274090
|
+
doc.steps.push(progress);
|
|
274091
|
+
doc.stats = (0, run_store_1.statsFromSteps)(doc.steps);
|
|
274092
|
+
});
|
|
274093
|
+
}
|
|
274094
|
+
});
|
|
274095
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
274096
|
+
const { reportPath: reportPath2 } = (0, run_store_1.writeSimulationRun)(jeticDir, runId, result2, {
|
|
274097
|
+
startedAt,
|
|
274098
|
+
finishedAt,
|
|
274099
|
+
workflowFile: selectedItem.filePath
|
|
274100
|
+
});
|
|
274101
|
+
return { result: result2, reportPath: reportPath2 };
|
|
274102
|
+
};
|
|
274103
|
+
if (args.async) {
|
|
274104
|
+
(0, run_store_1.writeRunningStatus)(jeticDir, runId, runMeta);
|
|
274105
|
+
execute().then(() => void 0).catch((err) => {
|
|
274106
|
+
try {
|
|
274107
|
+
(0, run_store_1.writeRunError)(jeticDir, runId, err, runMeta);
|
|
274108
|
+
} catch {
|
|
274109
|
+
}
|
|
274110
|
+
});
|
|
274111
|
+
return {
|
|
274112
|
+
status: "running",
|
|
274113
|
+
runId,
|
|
274114
|
+
workflowName: runMeta.workflowName,
|
|
274115
|
+
environment: { name: effectiveEnv.name, baseUrl: effectiveEnv.baseUrl },
|
|
274116
|
+
message: `Run started in the background against ${effectiveEnv.baseUrl}. Poll with jetic_get_run { "runId": "${runId}" } or jetic_list_runs.`
|
|
274117
|
+
};
|
|
274118
|
+
}
|
|
274119
|
+
const { result, reportPath } = await execute();
|
|
274120
|
+
if (args.includeStepResults) {
|
|
274121
|
+
return {
|
|
274122
|
+
runId,
|
|
274123
|
+
status: (0, run_store_1.deriveRunStatus)(result),
|
|
274124
|
+
reportPath,
|
|
274125
|
+
filePath,
|
|
274126
|
+
workflowFile: selectedItem.filePath,
|
|
274127
|
+
...result
|
|
274128
|
+
};
|
|
274129
|
+
}
|
|
273481
274130
|
return {
|
|
273482
|
-
...result,
|
|
274131
|
+
...(0, run_store_1.compactSimulationResult)(result, runId, reportPath),
|
|
273483
274132
|
filePath,
|
|
273484
274133
|
workflowFile: selectedItem.filePath
|
|
273485
274134
|
};
|
|
@@ -273576,9 +274225,11 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273576
274225
|
exports2.workflowStepSchema = zod_1.z.object({
|
|
273577
274226
|
name: zod_1.z.string().min(1).describe("Step name (must be unique within the workflow)"),
|
|
273578
274227
|
method: zod_1.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]).describe("HTTP method"),
|
|
273579
|
-
path: zod_1.z.string().min(1).describe("Route path
|
|
274228
|
+
path: zod_1.z.string().min(1).describe("Route path. :params resolve from a body field, or a memory key of the same name; {{templates}} are also resolved in the path."),
|
|
273580
274229
|
description: zod_1.z.string().optional().describe("What this step does"),
|
|
273581
|
-
body: zod_1.z.record(zod_1.z.any()).optional().describe("Request payload. Values support {{faker.*}}, {{scope:key}}, {{workflow:key}} and {{human:key}} templates"),
|
|
274230
|
+
body: zod_1.z.record(zod_1.z.any()).optional().describe("Request payload (nested objects/arrays are templated too). Values support {{faker.*}}, {{scope:key}}, {{workflow:key}} and {{human:key}} templates"),
|
|
274231
|
+
query: zod_1.z.record(zod_1.z.any()).optional().describe("URL query parameters (deep-resolved templates). Use this instead of a body on GET/HEAD steps."),
|
|
274232
|
+
timeoutMs: zod_1.z.number().int().positive().optional().describe("Per-step request timeout in ms (overrides the run default, which is 30000; max 120000)."),
|
|
273582
274233
|
inject: zod_1.z.record(zod_1.z.string()).optional().describe('Header/body injection: "header:Name" or "body:field" \u2192 "scope:key" or "{{template}}"'),
|
|
273583
274234
|
capture: zod_1.z.record(zod_1.z.string()).optional().describe('Save RESPONSE fields to memory: "scope:key" \u2192 "dot.path.in.response"'),
|
|
273584
274235
|
captureInput: zod_1.z.record(zod_1.z.string()).optional().describe('Save REQUEST body fields to memory BEFORE sending: "scope:key" \u2192 "bodyField"'),
|
|
@@ -273598,7 +274249,6 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273598
274249
|
retry: stepRetrySchema.optional().describe("Global retry default applied to all steps (overridden per step)"),
|
|
273599
274250
|
steps: zod_1.z.array(exports2.workflowStepSchema).min(1).describe("Ordered steps; later steps can use memory captured by earlier ones")
|
|
273600
274251
|
}).passthrough();
|
|
273601
|
-
var TEMPLATE_RE = /\{\{\s*([^}]+?)\s*\}\}/g;
|
|
273602
274252
|
var KNOWN_WORKFLOW_KEYS = /* @__PURE__ */ new Set(["name", "description", "environment", "steps", "generatedAt", "retry"]);
|
|
273603
274253
|
var KNOWN_STEP_KEYS = /* @__PURE__ */ new Set([
|
|
273604
274254
|
"name",
|
|
@@ -273610,6 +274260,8 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273610
274260
|
"captureInput",
|
|
273611
274261
|
"expectStatus",
|
|
273612
274262
|
"body",
|
|
274263
|
+
"query",
|
|
274264
|
+
"timeoutMs",
|
|
273613
274265
|
"condition",
|
|
273614
274266
|
"onFailure",
|
|
273615
274267
|
"continueOnStatus",
|
|
@@ -273647,10 +274299,35 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273647
274299
|
}
|
|
273648
274300
|
function collectTemplateRefs(value, out) {
|
|
273649
274301
|
if (typeof value === "string") {
|
|
273650
|
-
|
|
273651
|
-
|
|
273652
|
-
|
|
273653
|
-
|
|
274302
|
+
let i = 0;
|
|
274303
|
+
while (i < value.length) {
|
|
274304
|
+
const open = value.indexOf("{{", i);
|
|
274305
|
+
if (open === -1)
|
|
274306
|
+
break;
|
|
274307
|
+
let depth = 0;
|
|
274308
|
+
let end = -1;
|
|
274309
|
+
for (let j = open + 2; j < value.length; j++) {
|
|
274310
|
+
const ch = value[j];
|
|
274311
|
+
if (ch === "{") {
|
|
274312
|
+
depth++;
|
|
274313
|
+
} else if (ch === "}") {
|
|
274314
|
+
if (depth > 0) {
|
|
274315
|
+
depth--;
|
|
274316
|
+
} else if (value[j + 1] === "}") {
|
|
274317
|
+
end = j;
|
|
274318
|
+
break;
|
|
274319
|
+
} else {
|
|
274320
|
+
break;
|
|
274321
|
+
}
|
|
274322
|
+
}
|
|
274323
|
+
}
|
|
274324
|
+
if (end === -1) {
|
|
274325
|
+
i = open + 2;
|
|
274326
|
+
continue;
|
|
274327
|
+
}
|
|
274328
|
+
out.push(value.slice(open + 2, end).trim());
|
|
274329
|
+
i = end + 2;
|
|
274330
|
+
}
|
|
273654
274331
|
} else if (Array.isArray(value)) {
|
|
273655
274332
|
for (const v of value)
|
|
273656
274333
|
collectTemplateRefs(v, out);
|
|
@@ -273746,6 +274423,25 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273746
274423
|
const seenNames = /* @__PURE__ */ new Set();
|
|
273747
274424
|
const defined = /* @__PURE__ */ new Set();
|
|
273748
274425
|
const humanKeys = /* @__PURE__ */ new Set();
|
|
274426
|
+
const refsUsedAnywhere = /* @__PURE__ */ new Set();
|
|
274427
|
+
for (const rawStep of wf.steps) {
|
|
274428
|
+
const stepRefs = [];
|
|
274429
|
+
collectTemplateRefs(rawStep?.body, stepRefs);
|
|
274430
|
+
collectTemplateRefs(rawStep?.query, stepRefs);
|
|
274431
|
+
collectTemplateRefs(rawStep?.path, stepRefs);
|
|
274432
|
+
const inject = rawStep?.inject;
|
|
274433
|
+
if (inject && typeof inject === "object" && !Array.isArray(inject)) {
|
|
274434
|
+
for (const v of Object.values(inject))
|
|
274435
|
+
collectTemplateRefs(v, stepRefs);
|
|
274436
|
+
}
|
|
274437
|
+
for (const ref of stepRefs) {
|
|
274438
|
+
if (ref.startsWith("faker.") || !ref.includes(":"))
|
|
274439
|
+
continue;
|
|
274440
|
+
const { scope, key } = splitMemKey(ref);
|
|
274441
|
+
if (key)
|
|
274442
|
+
refsUsedAnywhere.add(`${scope}:${key}`);
|
|
274443
|
+
}
|
|
274444
|
+
}
|
|
273749
274445
|
wf.steps.forEach((rawStep, i) => {
|
|
273750
274446
|
const stepNo = i + 1;
|
|
273751
274447
|
const sName = typeof rawStep?.name === "string" ? rawStep.name : void 0;
|
|
@@ -273759,7 +274455,7 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273759
274455
|
if (MODEL_DIALECT_KEYS.has(key)) {
|
|
273760
274456
|
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.');
|
|
273761
274457
|
} else if (!KNOWN_STEP_KEYS.has(key)) {
|
|
273762
|
-
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).");
|
|
274458
|
+
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, query, timeoutMs, inject, capture, captureInput, expectStatus, condition, onFailure, continueOnStatus, retry).");
|
|
273763
274459
|
}
|
|
273764
274460
|
}
|
|
273765
274461
|
if ("condition" in step && step.condition !== void 0) {
|
|
@@ -273839,30 +274535,46 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273839
274535
|
if (step.body !== void 0 && (typeof step.body !== "object" || step.body === null || Array.isArray(step.body))) {
|
|
273840
274536
|
err(errors, stepNo, sName, "BAD_BODY", `Step ${stepNo} "body" must be an object of field -> value.`, 'Use { "email": "{{faker.internet.email}}" }.');
|
|
273841
274537
|
}
|
|
273842
|
-
if (
|
|
273843
|
-
|
|
274538
|
+
if (step.query !== void 0 && (typeof step.query !== "object" || step.query === null || Array.isArray(step.query))) {
|
|
274539
|
+
err(errors, stepNo, sName, "BAD_QUERY", `Step ${stepNo} "query" must be an object of param -> value.`, 'Use { "query": { "page": 1, "search": "{{workflow:term}}" } }.');
|
|
274540
|
+
}
|
|
274541
|
+
if ((method === "GET" || method === "HEAD") && step.body && typeof step.body === "object" && !Array.isArray(step.body)) {
|
|
274542
|
+
const nested = Object.values(step.body).some((v) => v !== null && typeof v === "object");
|
|
274543
|
+
if (nested) {
|
|
274544
|
+
warn(warnings, stepNo, sName, "GET_WITH_BODY", `Step ${stepNo} is ${method} with nested body values \u2014 they are stringified into the URL query string.`, 'Move flat filters to the "query" field instead.');
|
|
274545
|
+
}
|
|
273844
274546
|
}
|
|
273845
|
-
if (endpoint?.requestBody?.fields && step.body && typeof step.body === "object") {
|
|
274547
|
+
if (endpoint?.requestBody?.fields && step.body && typeof step.body === "object" && method !== "GET" && method !== "HEAD") {
|
|
273846
274548
|
for (const field of Object.keys(step.body)) {
|
|
273847
274549
|
if (!(field in endpoint.requestBody.fields)) {
|
|
273848
274550
|
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.");
|
|
273849
274551
|
}
|
|
273850
274552
|
}
|
|
273851
274553
|
}
|
|
274554
|
+
if (step.query && typeof step.query === "object" && !Array.isArray(step.query) && endpoint) {
|
|
274555
|
+
const declaredQuery = (endpoint.parameters || []).filter((p) => p.in === "query");
|
|
274556
|
+
if (declaredQuery.length > 0) {
|
|
274557
|
+
for (const param of Object.keys(step.query)) {
|
|
274558
|
+
if (!declaredQuery.some((p) => p.name === param)) {
|
|
274559
|
+
warn(warnings, stepNo, sName, "UNKNOWN_QUERY_PARAM", `Step ${stepNo} sends query param "${param}" not declared on ${method} ${wfPath}.`, "Check the spelling, or extend the endpoint with jetic_update_endpoint.");
|
|
274560
|
+
}
|
|
274561
|
+
}
|
|
274562
|
+
}
|
|
274563
|
+
}
|
|
273852
274564
|
const pathParams = wfPath.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g) || [];
|
|
273853
274565
|
for (const p of pathParams) {
|
|
273854
274566
|
const pname = p.substring(1);
|
|
273855
274567
|
const inBody = step.body && typeof step.body === "object" && pname in step.body;
|
|
274568
|
+
const inInjectBody = step.inject && typeof step.inject === "object" && !Array.isArray(step.inject) && Object.keys(step.inject).includes(`body:${pname}`);
|
|
273856
274569
|
const inMemory = [...defined].some((d) => d.split(":")[1] === pname) || [...persistedMemory].some((d) => d.split(":")[1] === pname);
|
|
273857
|
-
if (!inBody && !inMemory) {
|
|
273858
|
-
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
|
|
274570
|
+
if (!inBody && !inInjectBody && !inMemory) {
|
|
274571
|
+
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 inject { "body:${pname}": "<scope:key>" }), capture it as memory in an earlier step (capture: { "workflow:${pname}": "data.${pname}" }), or inline it as "/users/{{workflow:${pname}}}".`);
|
|
273859
274572
|
}
|
|
273860
274573
|
}
|
|
273861
|
-
if (/\{\{[^}]+\}\}/.test(wfPath)) {
|
|
273862
|
-
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.');
|
|
273863
|
-
}
|
|
273864
274574
|
const refs = [];
|
|
273865
274575
|
collectTemplateRefs(step.body, refs);
|
|
274576
|
+
collectTemplateRefs(step.query, refs);
|
|
274577
|
+
collectTemplateRefs(step.path, refs);
|
|
273866
274578
|
if (step.inject && typeof step.inject === "object") {
|
|
273867
274579
|
for (const v of Object.values(step.inject))
|
|
273868
274580
|
collectTemplateRefs(v, refs);
|
|
@@ -273871,6 +274583,8 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273871
274583
|
if (ref.startsWith("faker.")) {
|
|
273872
274584
|
if (ref.split(".").length < 3) {
|
|
273873
274585
|
warn(warnings, stepNo, sName, "SUSPICIOUS_FAKER", `"{{${ref}}}" looks like an incomplete faker path.`, "Use full paths like {{faker.internet.email}} or {{faker.string.uuid}}.");
|
|
274586
|
+
} else if (ref.includes("(") || ref.includes("{") || ref.includes("[")) {
|
|
274587
|
+
err(errors, stepNo, sName, "UNSUPPORTED_FAKER_CALL", `"{{${ref}}}" in step ${stepNo} passes arguments \u2014 the simulator only supports parameterless faker paths.`, "Use a plain path like {{faker.internet.email}} or {{faker.number.int}}, or hard-code the value.");
|
|
273874
274588
|
}
|
|
273875
274589
|
continue;
|
|
273876
274590
|
}
|
|
@@ -273929,7 +274643,13 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273929
274643
|
err(errors, stepNo, sName, "BAD_ON_FAILURE", `Step ${stepNo} "onFailure" must be "abort" or "continue" (got "${step.onFailure}").`, 'Use "abort" (default, stops the workflow) or "continue" (logs failure, proceeds).');
|
|
273930
274644
|
}
|
|
273931
274645
|
if (step.onFailure === "continue" && step.capture && Object.keys(step.capture).length > 0) {
|
|
273932
|
-
|
|
274646
|
+
const consumedDownstream = Object.keys(step.capture).some((k) => {
|
|
274647
|
+
const { scope, key } = splitMemKey(k);
|
|
274648
|
+
return key && refsUsedAnywhere.has(`${scope}:${key}`);
|
|
274649
|
+
});
|
|
274650
|
+
if (consumedDownstream) {
|
|
274651
|
+
warn(warnings, stepNo, sName, "CONTINUE_WITH_CAPTURE", `Step ${stepNo} uses onFailure:"continue" but later steps depend on its captures \u2014 if the request fails, those captures are skipped and downstream steps will fail or be skipped.`, "Make the run conditional on the capture, or move the capture to a step that must pass.");
|
|
274652
|
+
}
|
|
273933
274653
|
}
|
|
273934
274654
|
}
|
|
273935
274655
|
if (step.continueOnStatus !== void 0) {
|
|
@@ -288652,6 +289372,7 @@ var require_response_envelope = __commonJS({
|
|
|
288652
289372
|
"WRITE_DISABLED",
|
|
288653
289373
|
"INTERNAL"
|
|
288654
289374
|
];
|
|
289375
|
+
var MAX_MESSAGE_LENGTH = 500;
|
|
288655
289376
|
function errorCodeFrom(error) {
|
|
288656
289377
|
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
288657
289378
|
return "INTERNAL";
|
|
@@ -288659,6 +289380,28 @@ var require_response_envelope = __commonJS({
|
|
|
288659
289380
|
const code = error.code;
|
|
288660
289381
|
return typeof code === "string" && ERROR_CODES.includes(code) ? code : "INTERNAL";
|
|
288661
289382
|
}
|
|
289383
|
+
function describeError(error) {
|
|
289384
|
+
const record = typeof error === "object" && error !== null ? error : {};
|
|
289385
|
+
const issues = Array.isArray(record.issues) ? record.issues : void 0;
|
|
289386
|
+
if (issues && issues.length > 0) {
|
|
289387
|
+
const parts = issues.slice(0, 5).map((issue) => {
|
|
289388
|
+
const path15 = Array.isArray(issue?.path) ? issue.path.join(".") : "";
|
|
289389
|
+
return `${path15 ? `${path15}: ` : ""}${issue?.message ?? "invalid"}`;
|
|
289390
|
+
});
|
|
289391
|
+
const more = issues.length > 5 ? ` (+${issues.length - 5} more)` : "";
|
|
289392
|
+
return {
|
|
289393
|
+
message: `Invalid input \u2014 ${parts.join("; ")}${more}`,
|
|
289394
|
+
hint: "Fix the flagged argument(s) and retry. Use the tool schema for exact field types."
|
|
289395
|
+
};
|
|
289396
|
+
}
|
|
289397
|
+
const rawMessage = typeof record.message === "string" ? record.message.trim() : "";
|
|
289398
|
+
const message = rawMessage ? rawMessage.length > MAX_MESSAGE_LENGTH ? `${rawMessage.slice(0, MAX_MESSAGE_LENGTH)}\u2026` : rawMessage : "Tool execution failed.";
|
|
289399
|
+
const customHint = typeof record.hint === "string" && record.hint.trim() ? record.hint.trim() : "";
|
|
289400
|
+
return {
|
|
289401
|
+
message,
|
|
289402
|
+
hint: customHint || "Check the project configuration and arguments, then retry."
|
|
289403
|
+
};
|
|
289404
|
+
}
|
|
288662
289405
|
function toTextResult(data) {
|
|
288663
289406
|
return {
|
|
288664
289407
|
content: [{
|
|
@@ -288668,6 +289411,7 @@ var require_response_envelope = __commonJS({
|
|
|
288668
289411
|
};
|
|
288669
289412
|
}
|
|
288670
289413
|
function toErrorResult(error) {
|
|
289414
|
+
const { message, hint } = describeError(error);
|
|
288671
289415
|
return {
|
|
288672
289416
|
isError: true,
|
|
288673
289417
|
content: [{
|
|
@@ -288676,8 +289420,8 @@ var require_response_envelope = __commonJS({
|
|
|
288676
289420
|
ok: false,
|
|
288677
289421
|
error: {
|
|
288678
289422
|
code: errorCodeFrom(error),
|
|
288679
|
-
message
|
|
288680
|
-
hint
|
|
289423
|
+
message,
|
|
289424
|
+
hint
|
|
288681
289425
|
}
|
|
288682
289426
|
}, null, 2)
|
|
288683
289427
|
}]
|
|
@@ -289020,6 +289764,47 @@ var require_workflow_designer_tools = __commonJS({
|
|
|
289020
289764
|
if (Object.keys(inject).length > 0)
|
|
289021
289765
|
step.inject = inject;
|
|
289022
289766
|
}
|
|
289767
|
+
const pathParams = [...new Set(endpoint.path.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g) || [])].map((p) => p.slice(1));
|
|
289768
|
+
for (const pname of pathParams) {
|
|
289769
|
+
const inBody = step.body && typeof step.body === "object" && pname in step.body;
|
|
289770
|
+
if (inBody)
|
|
289771
|
+
continue;
|
|
289772
|
+
const memKey = [...producedKeys].find((k) => (k.split(":").pop() || "").toLowerCase() === pname.toLowerCase());
|
|
289773
|
+
if (memKey) {
|
|
289774
|
+
step.path = step.path.split(`:${pname}`).join(`{{${memKey}}}`);
|
|
289775
|
+
} else {
|
|
289776
|
+
missing.push(`Path param ":${pname}" has no source \u2014 add a body field "${pname}", capture a memory key named "${pname}" in an earlier step, or inline {{workflow:${pname}}} in the path.`);
|
|
289777
|
+
}
|
|
289778
|
+
}
|
|
289779
|
+
const queryParams = (endpoint.parameters || []).filter((p) => p.in === "query");
|
|
289780
|
+
if (queryParams.length > 0) {
|
|
289781
|
+
const query = {};
|
|
289782
|
+
for (const p of queryParams) {
|
|
289783
|
+
const memKey = [...producedKeys].find((k) => (k.split(":").pop() || "").toLowerCase() === p.name.toLowerCase());
|
|
289784
|
+
if (memKey) {
|
|
289785
|
+
query[p.name] = `{{${memKey}}}`;
|
|
289786
|
+
continue;
|
|
289787
|
+
}
|
|
289788
|
+
if (p.required !== true)
|
|
289789
|
+
continue;
|
|
289790
|
+
const val = fieldToFakerValue(p.name, {
|
|
289791
|
+
type: p.type || "string",
|
|
289792
|
+
format: p.format,
|
|
289793
|
+
min: p.min,
|
|
289794
|
+
max: p.max,
|
|
289795
|
+
enum: p.enum
|
|
289796
|
+
});
|
|
289797
|
+
if (val !== null) {
|
|
289798
|
+
query[p.name] = val;
|
|
289799
|
+
} else {
|
|
289800
|
+
query[p.name] = `{{human:${p.name}}}`;
|
|
289801
|
+
allHumanKeys.add(p.name);
|
|
289802
|
+
missing.push(`Query param "${p.name}" (required, type: ${p.type || "string"}) \u2014 no auto-mapping. Using {{human:${p.name}}} (will pause for interactive input at runtime).`);
|
|
289803
|
+
}
|
|
289804
|
+
}
|
|
289805
|
+
if (Object.keys(query).length > 0)
|
|
289806
|
+
step.query = query;
|
|
289807
|
+
}
|
|
289023
289808
|
const capture = {};
|
|
289024
289809
|
if (endpoint.produces) {
|
|
289025
289810
|
for (const p of endpoint.produces) {
|
|
@@ -289070,6 +289855,115 @@ var require_workflow_designer_tools = __commonJS({
|
|
|
289070
289855
|
}
|
|
289071
289856
|
});
|
|
289072
289857
|
|
|
289858
|
+
// ../../packages/mcp-server/dist/tools/run-tools.js
|
|
289859
|
+
var require_run_tools = __commonJS({
|
|
289860
|
+
"../../packages/mcp-server/dist/tools/run-tools.js"(exports2) {
|
|
289861
|
+
"use strict";
|
|
289862
|
+
init_cjs_shims();
|
|
289863
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
289864
|
+
if (k2 === void 0) k2 = k;
|
|
289865
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
289866
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
289867
|
+
desc = { enumerable: true, get: function() {
|
|
289868
|
+
return m[k];
|
|
289869
|
+
} };
|
|
289870
|
+
}
|
|
289871
|
+
Object.defineProperty(o, k2, desc);
|
|
289872
|
+
}) : (function(o, m, k, k2) {
|
|
289873
|
+
if (k2 === void 0) k2 = k;
|
|
289874
|
+
o[k2] = m[k];
|
|
289875
|
+
}));
|
|
289876
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
289877
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
289878
|
+
}) : function(o, v) {
|
|
289879
|
+
o["default"] = v;
|
|
289880
|
+
});
|
|
289881
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
289882
|
+
var ownKeys = function(o) {
|
|
289883
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
289884
|
+
var ar = [];
|
|
289885
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
289886
|
+
return ar;
|
|
289887
|
+
};
|
|
289888
|
+
return ownKeys(o);
|
|
289889
|
+
};
|
|
289890
|
+
return function(mod) {
|
|
289891
|
+
if (mod && mod.__esModule) return mod;
|
|
289892
|
+
var result = {};
|
|
289893
|
+
if (mod != null) {
|
|
289894
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
289895
|
+
}
|
|
289896
|
+
__setModuleDefault(result, mod);
|
|
289897
|
+
return result;
|
|
289898
|
+
};
|
|
289899
|
+
})();
|
|
289900
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
289901
|
+
exports2.listRunsSchema = exports2.getRunSchema = void 0;
|
|
289902
|
+
exports2.handleGetRun = handleGetRun;
|
|
289903
|
+
exports2.handleListRuns = handleListRuns;
|
|
289904
|
+
var zod_1 = require("zod");
|
|
289905
|
+
var path15 = __importStar(require("path"));
|
|
289906
|
+
var core_1 = require_dist();
|
|
289907
|
+
var types_1 = require_types();
|
|
289908
|
+
var run_store_1 = require_run_store();
|
|
289909
|
+
exports2.getRunSchema = zod_1.z.object({
|
|
289910
|
+
projectPath: zod_1.z.string().optional().describe("Path to project root or .jetic folder"),
|
|
289911
|
+
runId: zod_1.z.string().describe('Run id returned by jetic_simulate_workflow (e.g. "2026-10-19T12-34-56-789Z") or jetic_run_agent')
|
|
289912
|
+
});
|
|
289913
|
+
exports2.listRunsSchema = zod_1.z.object({
|
|
289914
|
+
projectPath: zod_1.z.string().optional().describe("Path to project root or .jetic folder"),
|
|
289915
|
+
limit: zod_1.z.number().int().positive().max(100).optional().default(20).describe("Max runs to return, newest first (default 20)")
|
|
289916
|
+
});
|
|
289917
|
+
function handleGetRun(args) {
|
|
289918
|
+
const projectRoot = path15.resolve((0, types_1.resolveProjectInput)(args.projectPath));
|
|
289919
|
+
const config = (0, core_1.loadConfig)(projectRoot);
|
|
289920
|
+
const report = (0, run_store_1.readSimulationRun)(config.jeticDir, args.runId);
|
|
289921
|
+
if (report) {
|
|
289922
|
+
return {
|
|
289923
|
+
found: true,
|
|
289924
|
+
...report,
|
|
289925
|
+
steps: report.result?.results ? report.result.results.map(run_store_1.stepToProgress) : []
|
|
289926
|
+
};
|
|
289927
|
+
}
|
|
289928
|
+
const status = (0, run_store_1.readRunStatus)(config.jeticDir, args.runId);
|
|
289929
|
+
if (status) {
|
|
289930
|
+
return {
|
|
289931
|
+
found: true,
|
|
289932
|
+
type: "simulation",
|
|
289933
|
+
runId: args.runId,
|
|
289934
|
+
status: status.status,
|
|
289935
|
+
workflowName: status.workflowName,
|
|
289936
|
+
environment: status.environment,
|
|
289937
|
+
baseUrl: status.baseUrl,
|
|
289938
|
+
startedAt: status.startedAt,
|
|
289939
|
+
finishedAt: status.finishedAt,
|
|
289940
|
+
stats: status.stats,
|
|
289941
|
+
...status.steps ? { steps: status.steps } : {},
|
|
289942
|
+
reportPath: status.reportPath,
|
|
289943
|
+
...status.error ? { error: status.error } : {},
|
|
289944
|
+
...status.status === "running" ? { message: "Run still in progress \u2014 poll again shortly." } : {}
|
|
289945
|
+
};
|
|
289946
|
+
}
|
|
289947
|
+
const agentReport = (0, core_1.readJsonSync)(path15.join(config.jeticDir, "runs", args.runId, "agent-report.json"));
|
|
289948
|
+
if (agentReport)
|
|
289949
|
+
return { found: true, type: "agent", runId: args.runId, ...agentReport };
|
|
289950
|
+
const available = (0, run_store_1.listRuns)(config.jeticDir, 10).map((r) => `${r.runId} (${r.type}: ${r.status})`);
|
|
289951
|
+
const detail = available.length > 0 ? ` Recent runs: ${available.join(", ")}.` : " No runs found yet.";
|
|
289952
|
+
throw new Error(`Run "${args.runId}" not found under .jetic/runs/.${detail}`);
|
|
289953
|
+
}
|
|
289954
|
+
function handleListRuns(args) {
|
|
289955
|
+
const projectRoot = path15.resolve((0, types_1.resolveProjectInput)(args.projectPath));
|
|
289956
|
+
const config = (0, core_1.loadConfig)(projectRoot);
|
|
289957
|
+
const runs = (0, run_store_1.listRuns)(config.jeticDir, args.limit ?? 20);
|
|
289958
|
+
return {
|
|
289959
|
+
runsDir: path15.join(config.jeticDir, "runs"),
|
|
289960
|
+
totalCount: runs.length,
|
|
289961
|
+
runs
|
|
289962
|
+
};
|
|
289963
|
+
}
|
|
289964
|
+
}
|
|
289965
|
+
});
|
|
289966
|
+
|
|
289073
289967
|
// ../../packages/mcp-server/dist/server.js
|
|
289074
289968
|
var require_server3 = __commonJS({
|
|
289075
289969
|
"../../packages/mcp-server/dist/server.js"(exports2) {
|
|
@@ -289134,6 +290028,7 @@ var require_server3 = __commonJS({
|
|
|
289134
290028
|
var session_tools_1 = require_session_tools();
|
|
289135
290029
|
var workflow_designer_tools_1 = require_workflow_designer_tools();
|
|
289136
290030
|
var agent_tools_1 = require_agent_tools();
|
|
290031
|
+
var run_tools_1 = require_run_tools();
|
|
289137
290032
|
exports2.JETIC_MCP_SERVER_NAME = "jetic-mcp-server";
|
|
289138
290033
|
function getJeticMcpVersion() {
|
|
289139
290034
|
try {
|
|
@@ -289222,6 +290117,9 @@ var require_server3 = __commonJS({
|
|
|
289222
290117
|
"",
|
|
289223
290118
|
"STEP 4g \u2014 RUN",
|
|
289224
290119
|
' jetic_simulate_workflow { "workflow": "<slug>" }',
|
|
290120
|
+
" It preflights the target first and fails fast with a clear reason when the app is unreachable.",
|
|
290121
|
+
' The response is a compact summary + runId. Full step detail: jetic_get_run { "runId": "..." };',
|
|
290122
|
+
" recent runs: jetic_list_runs. For long journeys pass async:true, then poll jetic_get_run.",
|
|
289225
290123
|
"",
|
|
289226
290124
|
"\u2501\u2501 GENERAL RULES \u2501\u2501",
|
|
289227
290125
|
"No AI-provider setup is needed: never ask for OpenRouter/OpenAI keys and never use `jetic config ai`.",
|
|
@@ -289289,6 +290187,8 @@ var require_server3 = __commonJS({
|
|
|
289289
290187
|
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);
|
|
289290
290188
|
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);
|
|
289291
290189
|
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);
|
|
290190
|
+
register("jetic_get_run", "Get run report", "Returns the full stored report of a previous run \u2014 every step's request, response, captures and errors \u2014 from .jetic/runs/<runId>/. Use the runId returned by jetic_simulate_workflow or jetic_list_runs.", run_tools_1.getRunSchema.shape, READ_ONLY, run_tools_1.handleGetRun);
|
|
290191
|
+
register("jetic_list_runs", "List runs", "Lists recent simulation and agent runs from .jetic/runs/, newest first, with status, step counts and duration. Use jetic_get_run for the full report of one run.", run_tools_1.listRunsSchema.shape, READ_ONLY, run_tools_1.handleListRuns);
|
|
289292
290192
|
register("jetic_scaffold_workflow", "Scaffold workflow steps from model", "PHASE 4a \u2014 FIRST STEP when building a workflow. Given an ordered endpoint list, reads model.json and returns ready-made `draftSteps`: body fields faker-mapped by type/format/name, inject derived from securitySchemes.obtainedFrom token-producer chains, capture from endpoint.produces, captureInput for reusable credentials. Returns missingFields (fields needing human input or manual fix) and securityChain analysis. Does NOT save. Review draftSteps, then call jetic_validate_workflow \u2192 jetic_create_workflow.", workflow_designer_tools_1.scaffoldWorkflowSchema.shape, READ_ONLY, workflow_designer_tools_1.handleScaffoldWorkflow);
|
|
289293
290193
|
register("jetic_validate_workflow", "Validate workflow (dry run)", "Deep-validates a workflow WITHOUT saving: step structure, endpoint cross-checks against model.json, full memory data-flow analysis. Also validates conditions (rules + onFail/switch targets, or legacy if guards; template refs), onFailure, continueOnStatus, and retry fields. Accepts a saved workflow slug/name or an inline definition.", workflow_authoring_tools_1.validateWorkflowSchema.shape, READ_ONLY, workflow_authoring_tools_1.handleValidateWorkflow);
|
|
289294
290194
|
register("jetic_create_workflow", "Create workflow", "Creates a .jetic/workflows/<slug>.json workflow. Supports: condition (rules with onFail abort|continue|switch, or legacy if-guards), onFailure (abort|continue), continueOnStatus (extra pass codes), retry (per-step or global default). Refuses to save on validation errors.", workflow_authoring_tools_1.createWorkflowSchema.shape, WRITES_MODEL, workflow_authoring_tools_1.handleCreateWorkflow);
|
|
@@ -290240,6 +291140,14 @@ function deepGet(obj, dotPath) {
|
|
|
290240
291140
|
}
|
|
290241
291141
|
return current;
|
|
290242
291142
|
}
|
|
291143
|
+
var TRACE_BODY_CAP = 16384;
|
|
291144
|
+
function truncateTraceBody(value) {
|
|
291145
|
+
if (value === void 0 || value === null) return value;
|
|
291146
|
+
const serialized = typeof value === "string" ? value : JSON.stringify(value);
|
|
291147
|
+
if (serialized.length <= TRACE_BODY_CAP) return value;
|
|
291148
|
+
return typeof value === "string" ? `${value.slice(0, TRACE_BODY_CAP)}
|
|
291149
|
+
\u2026[truncated: ${serialized.length - TRACE_BODY_CAP} more characters]` : { _truncated: true, preview: serialized.slice(0, TRACE_BODY_CAP), fullLengthChars: serialized.length };
|
|
291150
|
+
}
|
|
290243
291151
|
async function resolveConditionRef(ref, context = "condition") {
|
|
290244
291152
|
return ref.replace(/\{\{([^}]+)\}\}/g, () => "").trim() === ref.trim() ? ref : await resolveTemplateString(ref, context);
|
|
290245
291153
|
}
|
|
@@ -290249,7 +291157,21 @@ async function evaluateStepCondition(condition, context = "condition") {
|
|
|
290249
291157
|
async function preStepCondition(step) {
|
|
290250
291158
|
if (!step.condition) return null;
|
|
290251
291159
|
const label = step.name || `${step.method} ${step.path}`;
|
|
290252
|
-
const
|
|
291160
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
291161
|
+
const base = {
|
|
291162
|
+
step,
|
|
291163
|
+
status: 0,
|
|
291164
|
+
responseBody: null,
|
|
291165
|
+
durationMs: 0,
|
|
291166
|
+
startedAt: now,
|
|
291167
|
+
finishedAt: now,
|
|
291168
|
+
passed: false,
|
|
291169
|
+
captured: [],
|
|
291170
|
+
injected: {},
|
|
291171
|
+
requestHeaders: {},
|
|
291172
|
+
requestBody: {},
|
|
291173
|
+
resolvedUrl: ""
|
|
291174
|
+
};
|
|
290253
291175
|
const kind = (0, import_simulator2.conditionKind)(step.condition);
|
|
290254
291176
|
if (kind === "invalid") {
|
|
290255
291177
|
return { ...base, passed: false, error: `Step "${label}": ${(0, import_simulator2.describeInvalidCondition)(step.condition)}` };
|
|
@@ -290382,10 +291304,24 @@ async function resolveTemplateString(value, context) {
|
|
|
290382
291304
|
}
|
|
290383
291305
|
return result;
|
|
290384
291306
|
}
|
|
291307
|
+
async function resolveDeepValue(value, context) {
|
|
291308
|
+
if (typeof value === "string") return resolveTemplateString(value, context);
|
|
291309
|
+
if (Array.isArray(value)) {
|
|
291310
|
+
const out = [];
|
|
291311
|
+
for (const item of value) out.push(await resolveDeepValue(item, context));
|
|
291312
|
+
return out;
|
|
291313
|
+
}
|
|
291314
|
+
if (value && typeof value === "object") {
|
|
291315
|
+
const out = {};
|
|
291316
|
+
for (const [key, item] of Object.entries(value)) out[key] = await resolveDeepValue(item, context);
|
|
291317
|
+
return out;
|
|
291318
|
+
}
|
|
291319
|
+
return value;
|
|
291320
|
+
}
|
|
290385
291321
|
async function resolveBodyTemplates(body, context) {
|
|
290386
291322
|
const resolved = {};
|
|
290387
291323
|
for (const [key, value] of Object.entries(body)) {
|
|
290388
|
-
resolved[key] =
|
|
291324
|
+
resolved[key] = await resolveDeepValue(value, context);
|
|
290389
291325
|
}
|
|
290390
291326
|
return resolved;
|
|
290391
291327
|
}
|
|
@@ -290404,6 +291340,10 @@ async function resolveInjections(inject, memory, allMemory, context) {
|
|
|
290404
291340
|
if (value === null) continue;
|
|
290405
291341
|
strValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
290406
291342
|
}
|
|
291343
|
+
const headerName = target.startsWith("header:") ? target.slice(7) : target.startsWith("body:") ? null : target;
|
|
291344
|
+
if (headerName && headerName.toLowerCase() === "authorization" && strValue && !/^[A-Za-z][A-Za-z0-9-]*\s/.test(strValue)) {
|
|
291345
|
+
strValue = `Bearer ${strValue}`;
|
|
291346
|
+
}
|
|
290407
291347
|
if (target.startsWith("body:")) {
|
|
290408
291348
|
body[target.slice(5)] = strValue;
|
|
290409
291349
|
} else if (target.startsWith("header:")) {
|
|
@@ -290446,8 +291386,10 @@ async function resolvePathParams(urlPath, body) {
|
|
|
290446
291386
|
return `:${param}`;
|
|
290447
291387
|
});
|
|
290448
291388
|
}
|
|
291389
|
+
var REQUEST_TIMEOUT_MS = 3e4;
|
|
290449
291390
|
async function executeStep(step, baseUrl, defaultBody) {
|
|
290450
291391
|
const startTime = Date.now();
|
|
291392
|
+
const startedAt = new Date(startTime).toISOString();
|
|
290451
291393
|
const memory = new import_memory2.JeticMemory({ scope: "workflow" });
|
|
290452
291394
|
const stepLabel = step.name || `${step.method} ${step.path}`;
|
|
290453
291395
|
const { headers, body: injectedBody } = await resolveInjections(step.inject, memory, memory, stepLabel);
|
|
@@ -290457,13 +291399,12 @@ async function executeStep(step, baseUrl, defaultBody) {
|
|
|
290457
291399
|
const resolvedPath = await resolvePathParams(step.path, requestBody);
|
|
290458
291400
|
const url = `${baseUrl.replace(/\/$/, "")}${resolvedPath}`;
|
|
290459
291401
|
const expectedStatus = step.expectStatus ?? 200;
|
|
291402
|
+
const requestHeaders = { "Content-Type": "application/json", ...headers };
|
|
290460
291403
|
try {
|
|
290461
291404
|
const fetchOptions = {
|
|
290462
291405
|
method: step.method.toUpperCase(),
|
|
290463
|
-
headers:
|
|
290464
|
-
|
|
290465
|
-
...headers
|
|
290466
|
-
}
|
|
291406
|
+
headers: requestHeaders,
|
|
291407
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
290467
291408
|
};
|
|
290468
291409
|
if (!["GET", "HEAD"].includes(step.method.toUpperCase()) && Object.keys(requestBody).length > 0) {
|
|
290469
291410
|
fetchOptions.body = JSON.stringify(requestBody);
|
|
@@ -290496,20 +291437,39 @@ async function executeStep(step, baseUrl, defaultBody) {
|
|
|
290496
291437
|
status: response.status,
|
|
290497
291438
|
responseBody,
|
|
290498
291439
|
durationMs,
|
|
291440
|
+
startedAt,
|
|
291441
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
290499
291442
|
passed,
|
|
290500
291443
|
captured: [...inputCaptured, ...captured],
|
|
290501
|
-
injected: headers
|
|
291444
|
+
injected: headers,
|
|
291445
|
+
requestHeaders,
|
|
291446
|
+
requestBody,
|
|
291447
|
+
resolvedUrl: finalUrl,
|
|
291448
|
+
responseHeaders: Object.fromEntries(response.headers.entries())
|
|
290502
291449
|
};
|
|
290503
291450
|
} catch (err) {
|
|
291451
|
+
const code = (0, import_simulator2.networkErrorCode)(err);
|
|
291452
|
+
let message = err.message || String(err);
|
|
291453
|
+
if (err?.name === "TimeoutError" || code === "ETIMEDOUT") {
|
|
291454
|
+
message = `Request timed out after ${REQUEST_TIMEOUT_MS}ms (${step.method} ${resolvedPath}).`;
|
|
291455
|
+
} else if (code === "ECONNREFUSED") {
|
|
291456
|
+
message = `Connection refused (ECONNREFUSED) at ${baseUrl} \u2014 the server is not accepting connections.`;
|
|
291457
|
+
}
|
|
290504
291458
|
return {
|
|
290505
291459
|
step,
|
|
290506
291460
|
status: 0,
|
|
290507
291461
|
responseBody: null,
|
|
290508
291462
|
durationMs: Date.now() - startTime,
|
|
291463
|
+
startedAt,
|
|
291464
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
290509
291465
|
passed: false,
|
|
290510
291466
|
captured: inputCaptured,
|
|
290511
|
-
error:
|
|
290512
|
-
injected: headers
|
|
291467
|
+
error: message,
|
|
291468
|
+
injected: headers,
|
|
291469
|
+
requestHeaders,
|
|
291470
|
+
requestBody,
|
|
291471
|
+
resolvedUrl: url,
|
|
291472
|
+
connectionError: code ? import_simulator2.CONNECTION_ERROR_CODES.has(code) : false
|
|
290513
291473
|
};
|
|
290514
291474
|
}
|
|
290515
291475
|
}
|
|
@@ -290757,17 +291717,23 @@ var simulateWorkflowCommand = new import_commander14.Command("workflow").descrip
|
|
|
290757
291717
|
}
|
|
290758
291718
|
const checkSpinner = new Spinner();
|
|
290759
291719
|
checkSpinner.start(`Checking if backend is active at ${baseUrl}...`);
|
|
290760
|
-
|
|
290761
|
-
|
|
290762
|
-
checkSpinner.stop(` ${c.green}\u2713${c.reset} Backend is active`);
|
|
290763
|
-
console.log("");
|
|
290764
|
-
} catch (e) {
|
|
291720
|
+
const probe = await (0, import_simulator2.probeTarget)(baseUrl, 3e3);
|
|
291721
|
+
if (probe.verdict === "unreachable") {
|
|
290765
291722
|
checkSpinner.stop();
|
|
290766
291723
|
console.log(` ${c.red}\u2717${c.reset} Backend is unreachable at ${c.bold}${baseUrl}${c.reset}`);
|
|
290767
|
-
console.log(` ${c.
|
|
291724
|
+
console.log(` ${c.red}${probe.message}${c.reset}`);
|
|
291725
|
+
console.log(` ${c.yellow}\u26A0${c.reset} Start your backend project and try again.
|
|
290768
291726
|
`);
|
|
290769
291727
|
process.exit(1);
|
|
290770
291728
|
}
|
|
291729
|
+
if (probe.verdict === "server-error") {
|
|
291730
|
+
checkSpinner.stop(` ${c.yellow}\u26A0${c.reset} Backend reachable but failing (HTTP ${probe.status}) at ${baseUrl}`);
|
|
291731
|
+
console.log(` ${c.yellow}${probe.message}${c.reset}
|
|
291732
|
+
`);
|
|
291733
|
+
} else {
|
|
291734
|
+
checkSpinner.stop(` ${c.green}\u2713${c.reset} Backend is active (HTTP ${probe.status})`);
|
|
291735
|
+
console.log("");
|
|
291736
|
+
}
|
|
290771
291737
|
renderWorkflowHeader(workflow);
|
|
290772
291738
|
console.log(` ${c.magenta}\u{1F680}${c.reset} Executing workflow steps...
|
|
290773
291739
|
`);
|
|
@@ -290881,6 +291847,12 @@ var simulateWorkflowCommand = new import_commander14.Command("workflow").descrip
|
|
|
290881
291847
|
const icon = result.skipped ? SKIP : result.passed ? TICK : CROSS;
|
|
290882
291848
|
spinner.stop(` ${icon} ${stepLabel} ${formatStatus(result.status)} ${c.dim}${result.durationMs}ms${c.reset}`);
|
|
290883
291849
|
renderStepResult(result, i, workflow.steps.length);
|
|
291850
|
+
if (result.connectionError) {
|
|
291851
|
+
console.log(` ${c.red}${c.bold}\u26A0 Target unreachable \u2014 aborting early.${c.reset}
|
|
291852
|
+
`);
|
|
291853
|
+
stopOnFailure = true;
|
|
291854
|
+
break;
|
|
291855
|
+
}
|
|
290884
291856
|
if (!result.passed && result.error && !result.status && (0, import_simulator2.conditionKind)(step.condition) === "invalid") {
|
|
290885
291857
|
console.log(` ${c.red}${c.bold}\u26A0 Unrecognized condition \u2014 stopping workflow.${c.reset}
|
|
290886
291858
|
`);
|
|
@@ -290933,12 +291905,19 @@ var simulateWorkflowCommand = new import_commander14.Command("workflow").descrip
|
|
|
290933
291905
|
path: r.step?.path,
|
|
290934
291906
|
description: r.step?.description,
|
|
290935
291907
|
status: r.status,
|
|
291908
|
+
responseStatus: r.status,
|
|
290936
291909
|
passed: r.passed,
|
|
290937
291910
|
durationMs: r.durationMs,
|
|
291911
|
+
startedAt: r.startedAt,
|
|
291912
|
+
finishedAt: r.finishedAt,
|
|
291913
|
+
resolvedUrl: r.resolvedUrl,
|
|
291914
|
+
requestHeaders: r.requestHeaders,
|
|
291915
|
+
requestBody: r.requestBody,
|
|
291916
|
+
requestBodySpec: r.step?.body,
|
|
291917
|
+
responseHeaders: r.responseHeaders,
|
|
291918
|
+
responseBody: truncateTraceBody(r.responseBody),
|
|
290938
291919
|
captured: Object.fromEntries((r.captured ?? []).map((cap) => [cap, "(captured)"])),
|
|
290939
291920
|
injected: r.injected ?? {},
|
|
290940
|
-
requestBody: r.step?.body,
|
|
290941
|
-
responseBody: r.responseBody,
|
|
290942
291921
|
error: r.error ?? null,
|
|
290943
291922
|
expectStatus: r.step?.expectStatus,
|
|
290944
291923
|
captureSpec: r.step?.capture,
|
|
@@ -292564,7 +293543,7 @@ var mcpCommand = new import_commander18.Command("mcp").description("Launch Model
|
|
|
292564
293543
|
|
|
292565
293544
|
// src/index.ts
|
|
292566
293545
|
var program = new import_commander19.Command();
|
|
292567
|
-
program.name("jetic").description("AI-Native API Behavior Testing").version("0.2.
|
|
293546
|
+
program.name("jetic").description("AI-Native API Behavior Testing").version("0.2.7");
|
|
292568
293547
|
program.addCommand(initCommand);
|
|
292569
293548
|
program.addCommand(scanCommand);
|
|
292570
293549
|
program.addCommand(inspectCommand);
|