jetic-cli 0.2.6 → 0.2.7
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-DizUZ0Rd.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 +853 -96
- 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
|
@@ -272674,6 +272674,21 @@ var require_workflow_simulator = __commonJS({
|
|
|
272674
272674
|
var memory_1 = require_dist12();
|
|
272675
272675
|
var faker_1 = require("@faker-js/faker");
|
|
272676
272676
|
var conditions_1 = require_conditions();
|
|
272677
|
+
var CONNECTION_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
272678
|
+
"ECONNREFUSED",
|
|
272679
|
+
"ECONNRESET",
|
|
272680
|
+
"ENOTFOUND",
|
|
272681
|
+
"EAI_AGAIN",
|
|
272682
|
+
"ETIMEDOUT",
|
|
272683
|
+
"EHOSTUNREACH",
|
|
272684
|
+
"ENETUNREACH",
|
|
272685
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
272686
|
+
"UND_ERR_SOCKET"
|
|
272687
|
+
]);
|
|
272688
|
+
function networkErrorCode(error) {
|
|
272689
|
+
const raw = error?.cause?.code ?? error?.cause?.errno ?? error?.code ?? error?.errno;
|
|
272690
|
+
return typeof raw === "string" ? raw : void 0;
|
|
272691
|
+
}
|
|
272677
272692
|
function deepGet2(obj, dotPath) {
|
|
272678
272693
|
if (obj === null || obj === void 0)
|
|
272679
272694
|
return void 0;
|
|
@@ -272690,6 +272705,8 @@ var require_workflow_simulator = __commonJS({
|
|
|
272690
272705
|
model;
|
|
272691
272706
|
environment;
|
|
272692
272707
|
runtime;
|
|
272708
|
+
strictTemplates = true;
|
|
272709
|
+
requestTimeoutMs = 3e4;
|
|
272693
272710
|
constructor(model, environment, runtime = {}) {
|
|
272694
272711
|
this.model = model;
|
|
272695
272712
|
this.environment = environment;
|
|
@@ -272702,7 +272719,36 @@ var require_workflow_simulator = __commonJS({
|
|
|
272702
272719
|
if (options?.clearMemory) {
|
|
272703
272720
|
memory_1.JeticMemory.clearAllMemory(this.runtime.projectRoot);
|
|
272704
272721
|
}
|
|
272722
|
+
this.strictTemplates = options?.strictTemplates ?? true;
|
|
272723
|
+
this.requestTimeoutMs = options?.requestTimeoutMs ?? 3e4;
|
|
272705
272724
|
const startTime = Date.now();
|
|
272725
|
+
const preflightMode = options?.preflight ?? "off";
|
|
272726
|
+
const preflight = preflightMode !== "off" && workflow.steps.length > 0 ? await this.runPreflight(this.environment.baseUrl, options?.preflightTimeoutMs ?? 3e3) : void 0;
|
|
272727
|
+
if (preflight && preflightMode === "abort" && preflight.verdict === "unreachable") {
|
|
272728
|
+
preflight.aborted = true;
|
|
272729
|
+
return {
|
|
272730
|
+
success: false,
|
|
272731
|
+
workflow: {
|
|
272732
|
+
name: workflow.name,
|
|
272733
|
+
description: workflow.description,
|
|
272734
|
+
stepsCount: workflow.steps.length
|
|
272735
|
+
},
|
|
272736
|
+
environment: {
|
|
272737
|
+
name: this.environment.name,
|
|
272738
|
+
baseUrl: this.environment.baseUrl
|
|
272739
|
+
},
|
|
272740
|
+
stats: {
|
|
272741
|
+
totalSteps: workflow.steps.length,
|
|
272742
|
+
executedSteps: 0,
|
|
272743
|
+
passedSteps: 0,
|
|
272744
|
+
failedSteps: 0,
|
|
272745
|
+
totalTimeMs: Date.now() - startTime
|
|
272746
|
+
},
|
|
272747
|
+
results: [],
|
|
272748
|
+
preflight,
|
|
272749
|
+
abortReason: preflight.message
|
|
272750
|
+
};
|
|
272751
|
+
}
|
|
272706
272752
|
const state = { results: [], allPassed: true, halted: false };
|
|
272707
272753
|
await this.runSteps(workflow, options, state, [workflow.name]);
|
|
272708
272754
|
const stepResults = state.results;
|
|
@@ -272725,7 +272771,8 @@ var require_workflow_simulator = __commonJS({
|
|
|
272725
272771
|
failedSteps: stepResults.filter((r) => !r.passed).length,
|
|
272726
272772
|
totalTimeMs
|
|
272727
272773
|
},
|
|
272728
|
-
results: stepResults
|
|
272774
|
+
results: stepResults,
|
|
272775
|
+
...preflight ? { preflight } : {}
|
|
272729
272776
|
};
|
|
272730
272777
|
}
|
|
272731
272778
|
/**
|
|
@@ -272735,7 +272782,7 @@ var require_workflow_simulator = __commonJS({
|
|
|
272735
272782
|
*/
|
|
272736
272783
|
async runSteps(workflow, options, state, lineage) {
|
|
272737
272784
|
const baseUrl = this.environment.baseUrl;
|
|
272738
|
-
const resolve2 = (value) => this.resolveTemplateString(value);
|
|
272785
|
+
const resolve2 = (value) => this.resolveTemplateString(value, false);
|
|
272739
272786
|
for (let i = 0; i < workflow.steps.length; i++) {
|
|
272740
272787
|
const step = workflow.steps[i];
|
|
272741
272788
|
const stepStartTime = Date.now();
|
|
@@ -272789,12 +272836,44 @@ var require_workflow_simulator = __commonJS({
|
|
|
272789
272836
|
continue;
|
|
272790
272837
|
}
|
|
272791
272838
|
}
|
|
272792
|
-
|
|
272793
|
-
|
|
272794
|
-
|
|
272795
|
-
|
|
272796
|
-
|
|
272797
|
-
|
|
272839
|
+
let injectedHeaders = {};
|
|
272840
|
+
let finalBody = {};
|
|
272841
|
+
let resolvedQuery = {};
|
|
272842
|
+
let resolvedPath = step.path;
|
|
272843
|
+
let fullUrl = "";
|
|
272844
|
+
let inputCaptured = [];
|
|
272845
|
+
try {
|
|
272846
|
+
const injections = await this.resolveInjections(step.inject);
|
|
272847
|
+
injectedHeaders = injections.headers;
|
|
272848
|
+
const resolvedBody = await this.resolveBodyTemplates(step.body || {});
|
|
272849
|
+
finalBody = { ...injections.body, ...resolvedBody };
|
|
272850
|
+
resolvedQuery = await this.resolveBodyTemplates(step.query || {});
|
|
272851
|
+
inputCaptured = await this.captureInputToMemory(step.captureInput, finalBody);
|
|
272852
|
+
resolvedPath = await this.resolvePathParams(step.path, finalBody);
|
|
272853
|
+
fullUrl = `${baseUrl.replace(/\/$/, "")}${resolvedPath}`;
|
|
272854
|
+
} catch (err) {
|
|
272855
|
+
state.results.push({
|
|
272856
|
+
stepIndex: i + 1,
|
|
272857
|
+
name: stepName,
|
|
272858
|
+
method: step.method,
|
|
272859
|
+
path: step.path,
|
|
272860
|
+
targetUrl: "",
|
|
272861
|
+
expectedStatus: step.expectStatus ?? 200,
|
|
272862
|
+
actualStatus: 0,
|
|
272863
|
+
durationMs: Date.now() - stepStartTime,
|
|
272864
|
+
passed: false,
|
|
272865
|
+
skipped: false,
|
|
272866
|
+
retryCount: 0,
|
|
272867
|
+
requestHeaders: {},
|
|
272868
|
+
requestBody: {},
|
|
272869
|
+
responseBody: null,
|
|
272870
|
+
capturedMemory: [],
|
|
272871
|
+
error: `Step ${i + 1} "${stepName}": ${err?.message || err}`
|
|
272872
|
+
});
|
|
272873
|
+
state.allPassed = false;
|
|
272874
|
+
state.halted = true;
|
|
272875
|
+
return "stop";
|
|
272876
|
+
}
|
|
272798
272877
|
const expectedStatus = step.expectStatus ?? 200;
|
|
272799
272878
|
const maxAttempts = Math.max(1, step.retry?.times ?? workflow.retry?.times ?? 1);
|
|
272800
272879
|
const retryDelayMs = step.retry?.delayMs ?? workflow.retry?.delayMs ?? 0;
|
|
@@ -272803,6 +272882,7 @@ var require_workflow_simulator = __commonJS({
|
|
|
272803
272882
|
let responseBody = null;
|
|
272804
272883
|
let stepPassed = false;
|
|
272805
272884
|
let errorMsg;
|
|
272885
|
+
let connectionCode;
|
|
272806
272886
|
while (attempts < maxAttempts) {
|
|
272807
272887
|
attempts++;
|
|
272808
272888
|
if (attempts > 1 && retryDelayMs > 0) {
|
|
@@ -272811,22 +272891,42 @@ var require_workflow_simulator = __commonJS({
|
|
|
272811
272891
|
responseStatus = 0;
|
|
272812
272892
|
responseBody = null;
|
|
272813
272893
|
errorMsg = void 0;
|
|
272894
|
+
connectionCode = void 0;
|
|
272895
|
+
const timeoutMs = step.timeoutMs ?? this.requestTimeoutMs;
|
|
272814
272896
|
try {
|
|
272897
|
+
const upperMethod = step.method.toUpperCase();
|
|
272815
272898
|
const fetchOptions = {
|
|
272816
|
-
method:
|
|
272899
|
+
method: upperMethod,
|
|
272817
272900
|
headers: { "Content-Type": "application/json", ...injectedHeaders }
|
|
272818
272901
|
};
|
|
272819
|
-
if (!["GET", "HEAD"].includes(
|
|
272902
|
+
if (!["GET", "HEAD"].includes(upperMethod) && Object.keys(finalBody).length > 0) {
|
|
272820
272903
|
fetchOptions.body = JSON.stringify(finalBody);
|
|
272821
272904
|
}
|
|
272905
|
+
if (timeoutMs > 0) {
|
|
272906
|
+
fetchOptions.signal = AbortSignal.timeout(timeoutMs);
|
|
272907
|
+
}
|
|
272822
272908
|
let finalUrl = fullUrl;
|
|
272823
|
-
|
|
272909
|
+
const bodyAsQuery = ["GET", "HEAD"].includes(upperMethod) && Object.keys(finalBody).length > 0;
|
|
272910
|
+
if (Object.keys(resolvedQuery).length > 0 || bodyAsQuery) {
|
|
272824
272911
|
const params = new URLSearchParams();
|
|
272825
|
-
|
|
272826
|
-
|
|
272827
|
-
|
|
272828
|
-
|
|
272829
|
-
|
|
272912
|
+
const append = (source) => {
|
|
272913
|
+
for (const [k, v] of Object.entries(source)) {
|
|
272914
|
+
if (v === void 0 || v === null)
|
|
272915
|
+
continue;
|
|
272916
|
+
if (Array.isArray(v)) {
|
|
272917
|
+
for (const item of v)
|
|
272918
|
+
params.append(k, typeof item === "object" ? JSON.stringify(item) : String(item));
|
|
272919
|
+
} else {
|
|
272920
|
+
params.append(k, typeof v === "object" ? JSON.stringify(v) : String(v));
|
|
272921
|
+
}
|
|
272922
|
+
}
|
|
272923
|
+
};
|
|
272924
|
+
append(resolvedQuery);
|
|
272925
|
+
if (bodyAsQuery)
|
|
272926
|
+
append(finalBody);
|
|
272927
|
+
const qs = params.toString();
|
|
272928
|
+
if (qs)
|
|
272929
|
+
finalUrl = `${fullUrl}${fullUrl.includes("?") ? "&" : "?"}${qs}`;
|
|
272830
272930
|
}
|
|
272831
272931
|
const resp = await fetch(finalUrl, fetchOptions);
|
|
272832
272932
|
responseStatus = resp.status;
|
|
@@ -272846,7 +272946,14 @@ var require_workflow_simulator = __commonJS({
|
|
|
272846
272946
|
break;
|
|
272847
272947
|
} catch (err) {
|
|
272848
272948
|
stepPassed = false;
|
|
272849
|
-
|
|
272949
|
+
connectionCode = networkErrorCode(err);
|
|
272950
|
+
if (err?.name === "TimeoutError" || connectionCode === "ETIMEDOUT") {
|
|
272951
|
+
errorMsg = `Request timed out after ${timeoutMs}ms (${step.method} ${resolvedPath}).`;
|
|
272952
|
+
} else if (connectionCode === "ECONNREFUSED") {
|
|
272953
|
+
errorMsg = `Connection refused (ECONNREFUSED) at ${baseUrl} \u2014 the server is not accepting connections.`;
|
|
272954
|
+
} else {
|
|
272955
|
+
errorMsg = err?.message || String(err);
|
|
272956
|
+
}
|
|
272850
272957
|
}
|
|
272851
272958
|
}
|
|
272852
272959
|
const responseCaptured = stepPassed ? await this.captureToMemory(step.capture, responseBody) : [];
|
|
@@ -272865,6 +272972,7 @@ var require_workflow_simulator = __commonJS({
|
|
|
272865
272972
|
retryCount: attempts - 1,
|
|
272866
272973
|
requestHeaders: injectedHeaders,
|
|
272867
272974
|
requestBody: finalBody,
|
|
272975
|
+
...Object.keys(resolvedQuery).length > 0 ? { requestQuery: resolvedQuery } : {},
|
|
272868
272976
|
responseBody,
|
|
272869
272977
|
capturedMemory: stepPassed ? [...inputCaptured, ...responseCaptured] : [...inputCaptured],
|
|
272870
272978
|
...stepPassed ? {} : {
|
|
@@ -272875,6 +272983,13 @@ var require_workflow_simulator = __commonJS({
|
|
|
272875
272983
|
state.results.push(result);
|
|
272876
272984
|
if (!stepPassed) {
|
|
272877
272985
|
state.allPassed = false;
|
|
272986
|
+
if (connectionCode && CONNECTION_ERROR_CODES.has(connectionCode)) {
|
|
272987
|
+
const remaining = workflow.steps.length - (i + 1);
|
|
272988
|
+
result.failureHandling = "aborted";
|
|
272989
|
+
result.error = `${result.error || "Connection failed"}; aborting early because the target is unreachable${remaining > 0 ? ` (${remaining} step(s) not executed)` : ""}.`;
|
|
272990
|
+
state.halted = true;
|
|
272991
|
+
return "stop";
|
|
272992
|
+
}
|
|
272878
272993
|
if (onFailure === "abort") {
|
|
272879
272994
|
state.halted = true;
|
|
272880
272995
|
return "stop";
|
|
@@ -272917,55 +273032,120 @@ var require_workflow_simulator = __commonJS({
|
|
|
272917
273032
|
}
|
|
272918
273033
|
return "done";
|
|
272919
273034
|
}
|
|
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) : "" });
|
|
273035
|
+
findPlaceholders(value) {
|
|
273036
|
+
const placeholders = [];
|
|
273037
|
+
let i = 0;
|
|
273038
|
+
while (i < value.length - 1) {
|
|
273039
|
+
if (value[i] !== "{" || value[i + 1] !== "{") {
|
|
273040
|
+
i++;
|
|
272940
273041
|
continue;
|
|
272941
273042
|
}
|
|
272942
|
-
|
|
272943
|
-
|
|
272944
|
-
|
|
272945
|
-
|
|
272946
|
-
|
|
272947
|
-
|
|
272948
|
-
|
|
272949
|
-
|
|
272950
|
-
|
|
272951
|
-
|
|
273043
|
+
let depth = 0;
|
|
273044
|
+
let closed = -1;
|
|
273045
|
+
let j = i + 2;
|
|
273046
|
+
while (j < value.length) {
|
|
273047
|
+
const ch = value[j];
|
|
273048
|
+
if (ch === "{")
|
|
273049
|
+
depth++;
|
|
273050
|
+
else if (ch === "}") {
|
|
273051
|
+
if (depth > 0)
|
|
273052
|
+
depth--;
|
|
273053
|
+
else if (value[j + 1] === "}") {
|
|
273054
|
+
closed = j;
|
|
273055
|
+
break;
|
|
273056
|
+
}
|
|
272952
273057
|
}
|
|
272953
|
-
|
|
273058
|
+
j++;
|
|
272954
273059
|
}
|
|
272955
|
-
|
|
273060
|
+
if (closed === -1)
|
|
273061
|
+
break;
|
|
273062
|
+
placeholders.push({ raw: value.slice(i, closed + 2), expr: value.slice(i + 2, closed).trim(), index: i });
|
|
273063
|
+
i = closed + 2;
|
|
273064
|
+
}
|
|
273065
|
+
return placeholders;
|
|
273066
|
+
}
|
|
273067
|
+
async resolveTemplateString(value, strict = this.strictTemplates) {
|
|
273068
|
+
const placeholders = this.findPlaceholders(value);
|
|
273069
|
+
if (placeholders.length === 0)
|
|
273070
|
+
return value;
|
|
273071
|
+
let result = "";
|
|
273072
|
+
let cursor = 0;
|
|
273073
|
+
for (const placeholder of placeholders) {
|
|
273074
|
+
result += value.slice(cursor, placeholder.index);
|
|
273075
|
+
result += await this.resolveExpression(placeholder.expr, strict, placeholder.raw);
|
|
273076
|
+
cursor = placeholder.index + placeholder.raw.length;
|
|
272956
273077
|
}
|
|
272957
|
-
|
|
272958
|
-
|
|
272959
|
-
|
|
273078
|
+
return result + value.slice(cursor);
|
|
273079
|
+
}
|
|
273080
|
+
async resolveExpression(expr, strict, raw) {
|
|
273081
|
+
if (expr.startsWith("faker.")) {
|
|
273082
|
+
return this.resolveFakerExpression(expr, strict, raw);
|
|
273083
|
+
}
|
|
273084
|
+
if (expr.includes(":")) {
|
|
273085
|
+
const colonIdx = expr.indexOf(":");
|
|
273086
|
+
const scope = expr.slice(0, colonIdx);
|
|
273087
|
+
const key = expr.slice(colonIdx + 1);
|
|
273088
|
+
const envKey = `JETIC_HUMAN_${key.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
273089
|
+
if (scope === "human") {
|
|
273090
|
+
const envVal = process.env[envKey];
|
|
273091
|
+
if (envVal !== void 0 && envVal !== "")
|
|
273092
|
+
return envVal;
|
|
273093
|
+
}
|
|
273094
|
+
const memVal = await this.memory(scope).get(key);
|
|
273095
|
+
if (memVal !== null && memVal !== void 0) {
|
|
273096
|
+
return typeof memVal === "string" ? memVal : JSON.stringify(memVal);
|
|
273097
|
+
}
|
|
273098
|
+
if (!strict)
|
|
273099
|
+
return "";
|
|
273100
|
+
if (scope === "human") {
|
|
273101
|
+
throw new Error(`missing human input ${raw} \u2014 set ${envKey} or seed "human:${key}" memory (non-interactive runners cannot prompt)`);
|
|
273102
|
+
}
|
|
273103
|
+
throw new Error(`unresolved template ${raw} \u2014 no value for "${scope}:${key}"; capture it in an earlier step or seed it first`);
|
|
273104
|
+
}
|
|
273105
|
+
return raw;
|
|
273106
|
+
}
|
|
273107
|
+
resolveFakerExpression(expr, strict, raw) {
|
|
273108
|
+
if (/[({]/.test(expr)) {
|
|
273109
|
+
if (!strict)
|
|
273110
|
+
return raw;
|
|
273111
|
+
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`);
|
|
273112
|
+
}
|
|
273113
|
+
let fn = faker_1.faker;
|
|
273114
|
+
for (const part of expr.split(".").slice(1))
|
|
273115
|
+
fn = fn?.[part];
|
|
273116
|
+
if (typeof fn !== "function") {
|
|
273117
|
+
if (!strict)
|
|
273118
|
+
return raw;
|
|
273119
|
+
throw new Error(`unknown faker path ${raw} \u2014 use a complete parameterless path like {{faker.internet.email}} or {{faker.string.uuid}}`);
|
|
273120
|
+
}
|
|
273121
|
+
try {
|
|
273122
|
+
return String(fn());
|
|
273123
|
+
} catch (err) {
|
|
273124
|
+
if (!strict)
|
|
273125
|
+
return raw;
|
|
273126
|
+
throw new Error(`faker expression ${raw} failed: ${err?.message || err} \u2014 this faker path needs arguments; parameterized faker calls are not supported`);
|
|
272960
273127
|
}
|
|
272961
|
-
return result;
|
|
272962
273128
|
}
|
|
272963
273129
|
async resolveBodyTemplates(body) {
|
|
272964
|
-
const resolved =
|
|
272965
|
-
|
|
272966
|
-
|
|
273130
|
+
const resolved = await this.resolveDeep(body);
|
|
273131
|
+
return resolved && typeof resolved === "object" && !Array.isArray(resolved) ? resolved : {};
|
|
273132
|
+
}
|
|
273133
|
+
async resolveDeep(value) {
|
|
273134
|
+
if (typeof value === "string")
|
|
273135
|
+
return this.resolveTemplateString(value);
|
|
273136
|
+
if (Array.isArray(value)) {
|
|
273137
|
+
const out = [];
|
|
273138
|
+
for (const item of value)
|
|
273139
|
+
out.push(await this.resolveDeep(item));
|
|
273140
|
+
return out;
|
|
273141
|
+
}
|
|
273142
|
+
if (value && typeof value === "object") {
|
|
273143
|
+
const out = {};
|
|
273144
|
+
for (const [key, item] of Object.entries(value))
|
|
273145
|
+
out[key] = await this.resolveDeep(item);
|
|
273146
|
+
return out;
|
|
272967
273147
|
}
|
|
272968
|
-
return
|
|
273148
|
+
return value;
|
|
272969
273149
|
}
|
|
272970
273150
|
async resolveInjections(inject) {
|
|
272971
273151
|
const headers = {};
|
|
@@ -272980,10 +273160,18 @@ var require_workflow_simulator = __commonJS({
|
|
|
272980
273160
|
const [scope, key] = memKeyOrTemplate.includes(":") ? memKeyOrTemplate.split(":", 2) : ["workflow", memKeyOrTemplate];
|
|
272981
273161
|
const scopedMemory = this.memory(scope);
|
|
272982
273162
|
const value = await scopedMemory.get(key);
|
|
272983
|
-
if (value === null)
|
|
273163
|
+
if (value === null || value === void 0) {
|
|
273164
|
+
if (this.strictTemplates) {
|
|
273165
|
+
throw new Error(`inject target "${target}" references "${scope}:${key}", which is not in memory \u2014 capture it earlier or seed it first`);
|
|
273166
|
+
}
|
|
272984
273167
|
continue;
|
|
273168
|
+
}
|
|
272985
273169
|
strValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
272986
273170
|
}
|
|
273171
|
+
const headerName = target.startsWith("header:") ? target.slice("header:".length) : target.startsWith("body:") ? null : target;
|
|
273172
|
+
if (headerName && headerName.toLowerCase() === "authorization" && strValue && !/^[A-Za-z][A-Za-z0-9-]*\s/.test(strValue)) {
|
|
273173
|
+
strValue = `Bearer ${strValue}`;
|
|
273174
|
+
}
|
|
272987
273175
|
if (target.startsWith("body:")) {
|
|
272988
273176
|
body[target.slice(5)] = strValue;
|
|
272989
273177
|
} else if (target.startsWith("header:")) {
|
|
@@ -273025,24 +273213,57 @@ var require_workflow_simulator = __commonJS({
|
|
|
273025
273213
|
return captured;
|
|
273026
273214
|
}
|
|
273027
273215
|
async resolvePathParams(urlPath, body) {
|
|
273028
|
-
let result = urlPath;
|
|
273029
|
-
const paramMatches =
|
|
273216
|
+
let result = await this.resolveTemplateString(urlPath);
|
|
273217
|
+
const paramMatches = result.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g);
|
|
273030
273218
|
if (!paramMatches)
|
|
273031
273219
|
return result;
|
|
273032
|
-
for (const p of paramMatches) {
|
|
273220
|
+
for (const p of [...new Set(paramMatches)].sort((a, b) => b.length - a.length)) {
|
|
273033
273221
|
const paramName = p.substring(1);
|
|
273034
|
-
|
|
273035
|
-
|
|
273036
|
-
|
|
273037
|
-
|
|
273038
|
-
|
|
273039
|
-
|
|
273040
|
-
|
|
273041
|
-
|
|
273222
|
+
let value = body[paramName];
|
|
273223
|
+
if (value === void 0 || value === null) {
|
|
273224
|
+
value = await this.memory("workflow").get(paramName);
|
|
273225
|
+
}
|
|
273226
|
+
if (value !== void 0 && value !== null) {
|
|
273227
|
+
result = result.split(p).join(typeof value === "string" ? value : String(value));
|
|
273228
|
+
continue;
|
|
273229
|
+
}
|
|
273230
|
+
if (this.strictTemplates) {
|
|
273231
|
+
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
273232
|
}
|
|
273043
273233
|
}
|
|
273044
273234
|
return result;
|
|
273045
273235
|
}
|
|
273236
|
+
async runPreflight(baseUrl, timeoutMs) {
|
|
273237
|
+
const probe = { url: baseUrl, verdict: "unreachable", message: "", aborted: false };
|
|
273238
|
+
try {
|
|
273239
|
+
const resp = await fetch(baseUrl, {
|
|
273240
|
+
method: "GET",
|
|
273241
|
+
redirect: "manual",
|
|
273242
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
273243
|
+
});
|
|
273244
|
+
probe.status = resp.status;
|
|
273245
|
+
if (resp.status >= 500) {
|
|
273246
|
+
probe.verdict = "server-error";
|
|
273247
|
+
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.`;
|
|
273248
|
+
} else {
|
|
273249
|
+
probe.verdict = "healthy";
|
|
273250
|
+
probe.message = `Server reachable at ${baseUrl} (HTTP ${resp.status}).`;
|
|
273251
|
+
}
|
|
273252
|
+
} catch (err) {
|
|
273253
|
+
const code = networkErrorCode(err);
|
|
273254
|
+
probe.status = void 0;
|
|
273255
|
+
if (err?.name === "TimeoutError" || code === "ETIMEDOUT") {
|
|
273256
|
+
probe.message = `No response from ${baseUrl} within ${timeoutMs}ms \u2014 the server may be down or hanging.`;
|
|
273257
|
+
} else if (code === "ECONNREFUSED") {
|
|
273258
|
+
probe.message = `Connection refused at ${baseUrl} \u2014 nothing is listening there. Start the app (check its PORT/.env) and retry.`;
|
|
273259
|
+
} else if (code === "ENOTFOUND" || code === "EAI_AGAIN") {
|
|
273260
|
+
probe.message = `DNS lookup failed for ${baseUrl}${code ? ` (${code})` : ""} \u2014 check the host name.`;
|
|
273261
|
+
} else {
|
|
273262
|
+
probe.message = `Cannot reach ${baseUrl}${code ? ` (${code})` : ""}: ${err?.message || err}`;
|
|
273263
|
+
}
|
|
273264
|
+
}
|
|
273265
|
+
return probe;
|
|
273266
|
+
}
|
|
273046
273267
|
};
|
|
273047
273268
|
exports2.WorkflowSimulator = WorkflowSimulator2;
|
|
273048
273269
|
}
|
|
@@ -273242,6 +273463,243 @@ var require_environment_tools = __commonJS({
|
|
|
273242
273463
|
}
|
|
273243
273464
|
});
|
|
273244
273465
|
|
|
273466
|
+
// ../../packages/mcp-server/dist/run-store.js
|
|
273467
|
+
var require_run_store = __commonJS({
|
|
273468
|
+
"../../packages/mcp-server/dist/run-store.js"(exports2) {
|
|
273469
|
+
"use strict";
|
|
273470
|
+
init_cjs_shims();
|
|
273471
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
273472
|
+
if (k2 === void 0) k2 = k;
|
|
273473
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
273474
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
273475
|
+
desc = { enumerable: true, get: function() {
|
|
273476
|
+
return m[k];
|
|
273477
|
+
} };
|
|
273478
|
+
}
|
|
273479
|
+
Object.defineProperty(o, k2, desc);
|
|
273480
|
+
}) : (function(o, m, k, k2) {
|
|
273481
|
+
if (k2 === void 0) k2 = k;
|
|
273482
|
+
o[k2] = m[k];
|
|
273483
|
+
}));
|
|
273484
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
273485
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
273486
|
+
}) : function(o, v) {
|
|
273487
|
+
o["default"] = v;
|
|
273488
|
+
});
|
|
273489
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
273490
|
+
var ownKeys = function(o) {
|
|
273491
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
273492
|
+
var ar = [];
|
|
273493
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
273494
|
+
return ar;
|
|
273495
|
+
};
|
|
273496
|
+
return ownKeys(o);
|
|
273497
|
+
};
|
|
273498
|
+
return function(mod) {
|
|
273499
|
+
if (mod && mod.__esModule) return mod;
|
|
273500
|
+
var result = {};
|
|
273501
|
+
if (mod != null) {
|
|
273502
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
273503
|
+
}
|
|
273504
|
+
__setModuleDefault(result, mod);
|
|
273505
|
+
return result;
|
|
273506
|
+
};
|
|
273507
|
+
})();
|
|
273508
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
273509
|
+
exports2.newRunId = newRunId;
|
|
273510
|
+
exports2.runsDir = runsDir;
|
|
273511
|
+
exports2.runDir = runDir;
|
|
273512
|
+
exports2.deriveRunStatus = deriveRunStatus;
|
|
273513
|
+
exports2.writeSimulationRun = writeSimulationRun;
|
|
273514
|
+
exports2.readSimulationRun = readSimulationRun;
|
|
273515
|
+
exports2.readRunStatus = readRunStatus;
|
|
273516
|
+
exports2.writeRunningStatus = writeRunningStatus;
|
|
273517
|
+
exports2.writeRunError = writeRunError;
|
|
273518
|
+
exports2.listRuns = listRuns;
|
|
273519
|
+
exports2.compactSimulationResult = compactSimulationResult;
|
|
273520
|
+
var fs10 = __importStar(require("fs"));
|
|
273521
|
+
var path15 = __importStar(require("path"));
|
|
273522
|
+
var core_1 = require_dist();
|
|
273523
|
+
function newRunId() {
|
|
273524
|
+
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
273525
|
+
}
|
|
273526
|
+
function runsDir(jeticDir) {
|
|
273527
|
+
return path15.join(jeticDir, "runs");
|
|
273528
|
+
}
|
|
273529
|
+
function runDir(jeticDir, runId) {
|
|
273530
|
+
return path15.join(runsDir(jeticDir), runId);
|
|
273531
|
+
}
|
|
273532
|
+
function reportPath(jeticDir, runId) {
|
|
273533
|
+
return path15.join(runDir(jeticDir, runId), "simulation-report.json");
|
|
273534
|
+
}
|
|
273535
|
+
function statusPath(jeticDir, runId) {
|
|
273536
|
+
return path15.join(runDir(jeticDir, runId), "simulation-status.json");
|
|
273537
|
+
}
|
|
273538
|
+
function deriveRunStatus(result) {
|
|
273539
|
+
if (result.success)
|
|
273540
|
+
return "passed";
|
|
273541
|
+
if (result.abortReason)
|
|
273542
|
+
return "aborted";
|
|
273543
|
+
return "failed";
|
|
273544
|
+
}
|
|
273545
|
+
function writeSimulationRun(jeticDir, runId, result, meta) {
|
|
273546
|
+
const status = deriveRunStatus(result);
|
|
273547
|
+
const durationMs = result.stats?.totalTimeMs ?? 0;
|
|
273548
|
+
const report = {
|
|
273549
|
+
type: "simulation",
|
|
273550
|
+
runId,
|
|
273551
|
+
workflowName: result.workflow?.name || "workflow",
|
|
273552
|
+
workflowFile: meta.workflowFile,
|
|
273553
|
+
environment: result.environment?.name || "unknown",
|
|
273554
|
+
baseUrl: result.environment?.baseUrl || "",
|
|
273555
|
+
status,
|
|
273556
|
+
startedAt: meta.startedAt,
|
|
273557
|
+
finishedAt: meta.finishedAt,
|
|
273558
|
+
durationMs,
|
|
273559
|
+
result
|
|
273560
|
+
};
|
|
273561
|
+
const rPath = reportPath(jeticDir, runId);
|
|
273562
|
+
(0, core_1.writeJsonSync)(rPath, report);
|
|
273563
|
+
const sPath = statusPath(jeticDir, runId);
|
|
273564
|
+
const statusDoc = {
|
|
273565
|
+
type: "simulation",
|
|
273566
|
+
runId,
|
|
273567
|
+
workflowName: report.workflowName,
|
|
273568
|
+
environment: report.environment,
|
|
273569
|
+
baseUrl: report.baseUrl,
|
|
273570
|
+
status,
|
|
273571
|
+
startedAt: meta.startedAt,
|
|
273572
|
+
finishedAt: meta.finishedAt,
|
|
273573
|
+
durationMs,
|
|
273574
|
+
stats: {
|
|
273575
|
+
totalSteps: result.stats?.totalSteps ?? result.results.length,
|
|
273576
|
+
executedSteps: result.stats?.executedSteps ?? 0,
|
|
273577
|
+
passedSteps: result.stats?.passedSteps ?? 0,
|
|
273578
|
+
failedSteps: result.stats?.failedSteps ?? 0,
|
|
273579
|
+
skippedSteps: result.results.filter((r) => r.skipped).length
|
|
273580
|
+
},
|
|
273581
|
+
reportPath: rPath
|
|
273582
|
+
};
|
|
273583
|
+
(0, core_1.writeJsonSync)(sPath, statusDoc);
|
|
273584
|
+
return { runDir: runDir(jeticDir, runId), reportPath: rPath };
|
|
273585
|
+
}
|
|
273586
|
+
function readSimulationRun(jeticDir, runId) {
|
|
273587
|
+
return (0, core_1.readJsonSync)(reportPath(jeticDir, runId));
|
|
273588
|
+
}
|
|
273589
|
+
function readRunStatus(jeticDir, runId) {
|
|
273590
|
+
return (0, core_1.readJsonSync)(statusPath(jeticDir, runId));
|
|
273591
|
+
}
|
|
273592
|
+
function writeRunningStatus(jeticDir, runId, meta) {
|
|
273593
|
+
const doc = {
|
|
273594
|
+
type: "simulation",
|
|
273595
|
+
runId,
|
|
273596
|
+
workflowName: meta.workflowName,
|
|
273597
|
+
environment: meta.environment,
|
|
273598
|
+
baseUrl: meta.baseUrl,
|
|
273599
|
+
status: "running",
|
|
273600
|
+
startedAt: meta.startedAt,
|
|
273601
|
+
stats: { totalSteps: 0, executedSteps: 0, passedSteps: 0, failedSteps: 0, skippedSteps: 0 },
|
|
273602
|
+
reportPath: reportPath(jeticDir, runId)
|
|
273603
|
+
};
|
|
273604
|
+
(0, core_1.writeJsonSync)(statusPath(jeticDir, runId), doc);
|
|
273605
|
+
}
|
|
273606
|
+
function writeRunError(jeticDir, runId, error, meta) {
|
|
273607
|
+
const doc = {
|
|
273608
|
+
type: "simulation",
|
|
273609
|
+
runId,
|
|
273610
|
+
workflowName: meta.workflowName,
|
|
273611
|
+
environment: meta.environment,
|
|
273612
|
+
baseUrl: meta.baseUrl,
|
|
273613
|
+
status: "error",
|
|
273614
|
+
startedAt: meta.startedAt,
|
|
273615
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
273616
|
+
error: error instanceof Error ? error.message : String(error),
|
|
273617
|
+
stats: { totalSteps: 0, executedSteps: 0, passedSteps: 0, failedSteps: 0, skippedSteps: 0 },
|
|
273618
|
+
reportPath: reportPath(jeticDir, runId)
|
|
273619
|
+
};
|
|
273620
|
+
(0, core_1.writeJsonSync)(statusPath(jeticDir, runId), doc);
|
|
273621
|
+
}
|
|
273622
|
+
function listRuns(jeticDir, limit = 20) {
|
|
273623
|
+
const dir = runsDir(jeticDir);
|
|
273624
|
+
if (!fs10.existsSync(dir))
|
|
273625
|
+
return [];
|
|
273626
|
+
const items = [];
|
|
273627
|
+
for (const entry of fs10.readdirSync(dir, { withFileTypes: true })) {
|
|
273628
|
+
if (!entry.isDirectory())
|
|
273629
|
+
continue;
|
|
273630
|
+
const runId = entry.name;
|
|
273631
|
+
const simStatus = (0, core_1.readJsonSync)(statusPath(jeticDir, runId));
|
|
273632
|
+
if (simStatus) {
|
|
273633
|
+
items.push({
|
|
273634
|
+
runId,
|
|
273635
|
+
type: "simulation",
|
|
273636
|
+
name: simStatus.workflowName,
|
|
273637
|
+
status: simStatus.status,
|
|
273638
|
+
startedAt: simStatus.startedAt,
|
|
273639
|
+
durationMs: simStatus.durationMs,
|
|
273640
|
+
reportPath: simStatus.reportPath
|
|
273641
|
+
});
|
|
273642
|
+
continue;
|
|
273643
|
+
}
|
|
273644
|
+
const agentReport = (0, core_1.readJsonSync)(path15.join(runDir(jeticDir, runId), "agent-report.json"));
|
|
273645
|
+
if (agentReport) {
|
|
273646
|
+
items.push({
|
|
273647
|
+
runId,
|
|
273648
|
+
type: "agent",
|
|
273649
|
+
name: agentReport.agentId || "agent",
|
|
273650
|
+
status: agentReport.status || "completed",
|
|
273651
|
+
reportPath: path15.join(runDir(jeticDir, runId), "agent-report.json")
|
|
273652
|
+
});
|
|
273653
|
+
}
|
|
273654
|
+
}
|
|
273655
|
+
items.sort((a, b) => b.runId.localeCompare(a.runId));
|
|
273656
|
+
return items.slice(0, limit);
|
|
273657
|
+
}
|
|
273658
|
+
function compactSimulationResult(result, runId, reportFilePath) {
|
|
273659
|
+
const steps = result.results.map((r) => {
|
|
273660
|
+
const summary = {
|
|
273661
|
+
// stepIndex is already 1-based (set as i + 1 by the simulator).
|
|
273662
|
+
step: r.stepIndex,
|
|
273663
|
+
name: r.name,
|
|
273664
|
+
method: r.method,
|
|
273665
|
+
path: r.path,
|
|
273666
|
+
targetUrl: r.targetUrl,
|
|
273667
|
+
status: r.skipped ? "skipped" : r.passed ? "passed" : "failed",
|
|
273668
|
+
expectedStatus: r.expectedStatus,
|
|
273669
|
+
durationMs: r.durationMs
|
|
273670
|
+
};
|
|
273671
|
+
if (!r.skipped)
|
|
273672
|
+
summary.actualStatus = r.actualStatus;
|
|
273673
|
+
if (r.retryCount > 0)
|
|
273674
|
+
summary.retryCount = r.retryCount;
|
|
273675
|
+
if (r.conditionResult) {
|
|
273676
|
+
summary.condition = `${r.conditionResult.evaluated} \u2192 ${r.conditionResult.matched ? "matched" : "not matched"}`;
|
|
273677
|
+
}
|
|
273678
|
+
if (r.error)
|
|
273679
|
+
summary.error = r.error;
|
|
273680
|
+
return summary;
|
|
273681
|
+
});
|
|
273682
|
+
return {
|
|
273683
|
+
ok: result.success,
|
|
273684
|
+
success: result.success,
|
|
273685
|
+
runId,
|
|
273686
|
+
status: deriveRunStatus(result),
|
|
273687
|
+
workflow: result.workflow?.name || "workflow",
|
|
273688
|
+
environment: {
|
|
273689
|
+
name: result.environment?.name || "unknown",
|
|
273690
|
+
baseUrl: result.environment?.baseUrl || ""
|
|
273691
|
+
},
|
|
273692
|
+
stats: result.stats,
|
|
273693
|
+
...result.preflight ? { preflight: result.preflight } : {},
|
|
273694
|
+
...result.abortReason ? { abortReason: result.abortReason } : {},
|
|
273695
|
+
steps,
|
|
273696
|
+
reportPath: reportFilePath,
|
|
273697
|
+
next: result.success ? `Full request/response detail: jetic_get_run { "runId": "${runId}" }` : `Inspect the failing step in full detail: jetic_get_run { "runId": "${runId}" }`
|
|
273698
|
+
};
|
|
273699
|
+
}
|
|
273700
|
+
}
|
|
273701
|
+
});
|
|
273702
|
+
|
|
273245
273703
|
// ../../packages/mcp-server/dist/tools/workflow-tools.js
|
|
273246
273704
|
var require_workflow_tools = __commonJS({
|
|
273247
273705
|
"../../packages/mcp-server/dist/tools/workflow-tools.js"(exports2) {
|
|
@@ -273294,6 +273752,7 @@ var require_workflow_tools = __commonJS({
|
|
|
273294
273752
|
var types_1 = require_types();
|
|
273295
273753
|
var model_1 = require_dist2();
|
|
273296
273754
|
var simulator_1 = require_dist13();
|
|
273755
|
+
var run_store_1 = require_run_store();
|
|
273297
273756
|
exports2.listWorkflowsSchema = zod_1.z.object({
|
|
273298
273757
|
projectPath: zod_1.z.string().optional().describe("Path to project root or .jetic folder")
|
|
273299
273758
|
});
|
|
@@ -273385,7 +273844,12 @@ var require_workflow_tools = __commonJS({
|
|
|
273385
273844
|
workflow: zod_1.z.string().optional().describe("Workflow slug, name, or JSON file path to execute. If omitted, defaults to available workflow"),
|
|
273386
273845
|
envName: zod_1.z.string().optional().describe("Target environment name (e.g., local, staging)"),
|
|
273387
273846
|
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")
|
|
273847
|
+
clearMemory: zod_1.z.boolean().optional().default(false).describe("If true, clears Jetic memory before execution"),
|
|
273848
|
+
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."),
|
|
273849
|
+
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."),
|
|
273850
|
+
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.'),
|
|
273851
|
+
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."),
|
|
273852
|
+
requestTimeoutMs: zod_1.z.number().int().positive().max(12e4).optional().default(3e4).describe("Per-request timeout in ms (default 30000, max 120000).")
|
|
273389
273853
|
});
|
|
273390
273854
|
async function handleSimulateWorkflow(args) {
|
|
273391
273855
|
const { model, filePath } = (0, types_1.loadModel)(args.projectPath);
|
|
@@ -273472,14 +273936,62 @@ var require_workflow_tools = __commonJS({
|
|
|
273472
273936
|
baseUrl: args.baseUrl || targetEnv.baseUrl
|
|
273473
273937
|
};
|
|
273474
273938
|
const projectRoot = (0, types_1.projectRootOfModel)(filePath);
|
|
273475
|
-
const
|
|
273939
|
+
const jeticDir = path15.dirname(filePath);
|
|
273940
|
+
const workflowsDir = path15.join(jeticDir, "workflows");
|
|
273476
273941
|
const simulator = new simulator_1.WorkflowSimulator(model, effectiveEnv, { projectRoot });
|
|
273477
|
-
const
|
|
273478
|
-
|
|
273479
|
-
|
|
273480
|
-
|
|
273942
|
+
const runId = (0, run_store_1.newRunId)();
|
|
273943
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
273944
|
+
const runMeta = {
|
|
273945
|
+
workflowName: workflowDef.name || selectedItem.name,
|
|
273946
|
+
environment: effectiveEnv.name,
|
|
273947
|
+
baseUrl: effectiveEnv.baseUrl,
|
|
273948
|
+
startedAt
|
|
273949
|
+
};
|
|
273950
|
+
const execute = async () => {
|
|
273951
|
+
const result2 = await simulator.simulateWorkflow(workflowDef, {
|
|
273952
|
+
clearMemory: args.clearMemory ?? false,
|
|
273953
|
+
loadWorkflow: (slugOrName) => (0, model_1.loadWorkflowByName)(workflowsDir, slugOrName),
|
|
273954
|
+
strictTemplates: args.strictTemplates ?? true,
|
|
273955
|
+
preflight: args.preflight ?? "abort",
|
|
273956
|
+
requestTimeoutMs: args.requestTimeoutMs ?? 3e4
|
|
273957
|
+
});
|
|
273958
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
273959
|
+
const { reportPath: reportPath2 } = (0, run_store_1.writeSimulationRun)(jeticDir, runId, result2, {
|
|
273960
|
+
startedAt,
|
|
273961
|
+
finishedAt,
|
|
273962
|
+
workflowFile: selectedItem.filePath
|
|
273963
|
+
});
|
|
273964
|
+
return { result: result2, reportPath: reportPath2 };
|
|
273965
|
+
};
|
|
273966
|
+
if (args.async) {
|
|
273967
|
+
(0, run_store_1.writeRunningStatus)(jeticDir, runId, runMeta);
|
|
273968
|
+
execute().then(() => void 0).catch((err) => {
|
|
273969
|
+
try {
|
|
273970
|
+
(0, run_store_1.writeRunError)(jeticDir, runId, err, runMeta);
|
|
273971
|
+
} catch {
|
|
273972
|
+
}
|
|
273973
|
+
});
|
|
273974
|
+
return {
|
|
273975
|
+
status: "running",
|
|
273976
|
+
runId,
|
|
273977
|
+
workflowName: runMeta.workflowName,
|
|
273978
|
+
environment: { name: effectiveEnv.name, baseUrl: effectiveEnv.baseUrl },
|
|
273979
|
+
message: `Run started in the background against ${effectiveEnv.baseUrl}. Poll with jetic_get_run { "runId": "${runId}" } or jetic_list_runs.`
|
|
273980
|
+
};
|
|
273981
|
+
}
|
|
273982
|
+
const { result, reportPath } = await execute();
|
|
273983
|
+
if (args.includeStepResults) {
|
|
273984
|
+
return {
|
|
273985
|
+
runId,
|
|
273986
|
+
status: (0, run_store_1.deriveRunStatus)(result),
|
|
273987
|
+
reportPath,
|
|
273988
|
+
filePath,
|
|
273989
|
+
workflowFile: selectedItem.filePath,
|
|
273990
|
+
...result
|
|
273991
|
+
};
|
|
273992
|
+
}
|
|
273481
273993
|
return {
|
|
273482
|
-
...result,
|
|
273994
|
+
...(0, run_store_1.compactSimulationResult)(result, runId, reportPath),
|
|
273483
273995
|
filePath,
|
|
273484
273996
|
workflowFile: selectedItem.filePath
|
|
273485
273997
|
};
|
|
@@ -273576,9 +274088,11 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273576
274088
|
exports2.workflowStepSchema = zod_1.z.object({
|
|
273577
274089
|
name: zod_1.z.string().min(1).describe("Step name (must be unique within the workflow)"),
|
|
273578
274090
|
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
|
|
274091
|
+
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
274092
|
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"),
|
|
274093
|
+
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"),
|
|
274094
|
+
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."),
|
|
274095
|
+
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
274096
|
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
274097
|
capture: zod_1.z.record(zod_1.z.string()).optional().describe('Save RESPONSE fields to memory: "scope:key" \u2192 "dot.path.in.response"'),
|
|
273584
274098
|
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 +274112,6 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273598
274112
|
retry: stepRetrySchema.optional().describe("Global retry default applied to all steps (overridden per step)"),
|
|
273599
274113
|
steps: zod_1.z.array(exports2.workflowStepSchema).min(1).describe("Ordered steps; later steps can use memory captured by earlier ones")
|
|
273600
274114
|
}).passthrough();
|
|
273601
|
-
var TEMPLATE_RE = /\{\{\s*([^}]+?)\s*\}\}/g;
|
|
273602
274115
|
var KNOWN_WORKFLOW_KEYS = /* @__PURE__ */ new Set(["name", "description", "environment", "steps", "generatedAt", "retry"]);
|
|
273603
274116
|
var KNOWN_STEP_KEYS = /* @__PURE__ */ new Set([
|
|
273604
274117
|
"name",
|
|
@@ -273610,6 +274123,8 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273610
274123
|
"captureInput",
|
|
273611
274124
|
"expectStatus",
|
|
273612
274125
|
"body",
|
|
274126
|
+
"query",
|
|
274127
|
+
"timeoutMs",
|
|
273613
274128
|
"condition",
|
|
273614
274129
|
"onFailure",
|
|
273615
274130
|
"continueOnStatus",
|
|
@@ -273647,10 +274162,35 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273647
274162
|
}
|
|
273648
274163
|
function collectTemplateRefs(value, out) {
|
|
273649
274164
|
if (typeof value === "string") {
|
|
273650
|
-
|
|
273651
|
-
|
|
273652
|
-
|
|
273653
|
-
|
|
274165
|
+
let i = 0;
|
|
274166
|
+
while (i < value.length) {
|
|
274167
|
+
const open = value.indexOf("{{", i);
|
|
274168
|
+
if (open === -1)
|
|
274169
|
+
break;
|
|
274170
|
+
let depth = 0;
|
|
274171
|
+
let end = -1;
|
|
274172
|
+
for (let j = open + 2; j < value.length; j++) {
|
|
274173
|
+
const ch = value[j];
|
|
274174
|
+
if (ch === "{") {
|
|
274175
|
+
depth++;
|
|
274176
|
+
} else if (ch === "}") {
|
|
274177
|
+
if (depth > 0) {
|
|
274178
|
+
depth--;
|
|
274179
|
+
} else if (value[j + 1] === "}") {
|
|
274180
|
+
end = j;
|
|
274181
|
+
break;
|
|
274182
|
+
} else {
|
|
274183
|
+
break;
|
|
274184
|
+
}
|
|
274185
|
+
}
|
|
274186
|
+
}
|
|
274187
|
+
if (end === -1) {
|
|
274188
|
+
i = open + 2;
|
|
274189
|
+
continue;
|
|
274190
|
+
}
|
|
274191
|
+
out.push(value.slice(open + 2, end).trim());
|
|
274192
|
+
i = end + 2;
|
|
274193
|
+
}
|
|
273654
274194
|
} else if (Array.isArray(value)) {
|
|
273655
274195
|
for (const v of value)
|
|
273656
274196
|
collectTemplateRefs(v, out);
|
|
@@ -273746,6 +274286,25 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273746
274286
|
const seenNames = /* @__PURE__ */ new Set();
|
|
273747
274287
|
const defined = /* @__PURE__ */ new Set();
|
|
273748
274288
|
const humanKeys = /* @__PURE__ */ new Set();
|
|
274289
|
+
const refsUsedAnywhere = /* @__PURE__ */ new Set();
|
|
274290
|
+
for (const rawStep of wf.steps) {
|
|
274291
|
+
const stepRefs = [];
|
|
274292
|
+
collectTemplateRefs(rawStep?.body, stepRefs);
|
|
274293
|
+
collectTemplateRefs(rawStep?.query, stepRefs);
|
|
274294
|
+
collectTemplateRefs(rawStep?.path, stepRefs);
|
|
274295
|
+
const inject = rawStep?.inject;
|
|
274296
|
+
if (inject && typeof inject === "object" && !Array.isArray(inject)) {
|
|
274297
|
+
for (const v of Object.values(inject))
|
|
274298
|
+
collectTemplateRefs(v, stepRefs);
|
|
274299
|
+
}
|
|
274300
|
+
for (const ref of stepRefs) {
|
|
274301
|
+
if (ref.startsWith("faker.") || !ref.includes(":"))
|
|
274302
|
+
continue;
|
|
274303
|
+
const { scope, key } = splitMemKey(ref);
|
|
274304
|
+
if (key)
|
|
274305
|
+
refsUsedAnywhere.add(`${scope}:${key}`);
|
|
274306
|
+
}
|
|
274307
|
+
}
|
|
273749
274308
|
wf.steps.forEach((rawStep, i) => {
|
|
273750
274309
|
const stepNo = i + 1;
|
|
273751
274310
|
const sName = typeof rawStep?.name === "string" ? rawStep.name : void 0;
|
|
@@ -273759,7 +274318,7 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273759
274318
|
if (MODEL_DIALECT_KEYS.has(key)) {
|
|
273760
274319
|
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
274320
|
} 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).");
|
|
274321
|
+
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
274322
|
}
|
|
273764
274323
|
}
|
|
273765
274324
|
if ("condition" in step && step.condition !== void 0) {
|
|
@@ -273839,30 +274398,46 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273839
274398
|
if (step.body !== void 0 && (typeof step.body !== "object" || step.body === null || Array.isArray(step.body))) {
|
|
273840
274399
|
err(errors, stepNo, sName, "BAD_BODY", `Step ${stepNo} "body" must be an object of field -> value.`, 'Use { "email": "{{faker.internet.email}}" }.');
|
|
273841
274400
|
}
|
|
273842
|
-
if (
|
|
273843
|
-
|
|
274401
|
+
if (step.query !== void 0 && (typeof step.query !== "object" || step.query === null || Array.isArray(step.query))) {
|
|
274402
|
+
err(errors, stepNo, sName, "BAD_QUERY", `Step ${stepNo} "query" must be an object of param -> value.`, 'Use { "query": { "page": 1, "search": "{{workflow:term}}" } }.');
|
|
274403
|
+
}
|
|
274404
|
+
if ((method === "GET" || method === "HEAD") && step.body && typeof step.body === "object" && !Array.isArray(step.body)) {
|
|
274405
|
+
const nested = Object.values(step.body).some((v) => v !== null && typeof v === "object");
|
|
274406
|
+
if (nested) {
|
|
274407
|
+
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.');
|
|
274408
|
+
}
|
|
273844
274409
|
}
|
|
273845
|
-
if (endpoint?.requestBody?.fields && step.body && typeof step.body === "object") {
|
|
274410
|
+
if (endpoint?.requestBody?.fields && step.body && typeof step.body === "object" && method !== "GET" && method !== "HEAD") {
|
|
273846
274411
|
for (const field of Object.keys(step.body)) {
|
|
273847
274412
|
if (!(field in endpoint.requestBody.fields)) {
|
|
273848
274413
|
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
274414
|
}
|
|
273850
274415
|
}
|
|
273851
274416
|
}
|
|
274417
|
+
if (step.query && typeof step.query === "object" && !Array.isArray(step.query) && endpoint) {
|
|
274418
|
+
const declaredQuery = (endpoint.parameters || []).filter((p) => p.in === "query");
|
|
274419
|
+
if (declaredQuery.length > 0) {
|
|
274420
|
+
for (const param of Object.keys(step.query)) {
|
|
274421
|
+
if (!declaredQuery.some((p) => p.name === param)) {
|
|
274422
|
+
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.");
|
|
274423
|
+
}
|
|
274424
|
+
}
|
|
274425
|
+
}
|
|
274426
|
+
}
|
|
273852
274427
|
const pathParams = wfPath.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g) || [];
|
|
273853
274428
|
for (const p of pathParams) {
|
|
273854
274429
|
const pname = p.substring(1);
|
|
273855
274430
|
const inBody = step.body && typeof step.body === "object" && pname in step.body;
|
|
274431
|
+
const inInjectBody = step.inject && typeof step.inject === "object" && !Array.isArray(step.inject) && Object.keys(step.inject).includes(`body:${pname}`);
|
|
273856
274432
|
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
|
|
274433
|
+
if (!inBody && !inInjectBody && !inMemory) {
|
|
274434
|
+
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
274435
|
}
|
|
273860
274436
|
}
|
|
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
274437
|
const refs = [];
|
|
273865
274438
|
collectTemplateRefs(step.body, refs);
|
|
274439
|
+
collectTemplateRefs(step.query, refs);
|
|
274440
|
+
collectTemplateRefs(step.path, refs);
|
|
273866
274441
|
if (step.inject && typeof step.inject === "object") {
|
|
273867
274442
|
for (const v of Object.values(step.inject))
|
|
273868
274443
|
collectTemplateRefs(v, refs);
|
|
@@ -273871,6 +274446,8 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273871
274446
|
if (ref.startsWith("faker.")) {
|
|
273872
274447
|
if (ref.split(".").length < 3) {
|
|
273873
274448
|
warn(warnings, stepNo, sName, "SUSPICIOUS_FAKER", `"{{${ref}}}" looks like an incomplete faker path.`, "Use full paths like {{faker.internet.email}} or {{faker.string.uuid}}.");
|
|
274449
|
+
} else if (ref.includes("(") || ref.includes("{") || ref.includes("[")) {
|
|
274450
|
+
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
274451
|
}
|
|
273875
274452
|
continue;
|
|
273876
274453
|
}
|
|
@@ -273929,7 +274506,13 @@ var require_workflow_authoring_tools = __commonJS({
|
|
|
273929
274506
|
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
274507
|
}
|
|
273931
274508
|
if (step.onFailure === "continue" && step.capture && Object.keys(step.capture).length > 0) {
|
|
273932
|
-
|
|
274509
|
+
const consumedDownstream = Object.keys(step.capture).some((k) => {
|
|
274510
|
+
const { scope, key } = splitMemKey(k);
|
|
274511
|
+
return key && refsUsedAnywhere.has(`${scope}:${key}`);
|
|
274512
|
+
});
|
|
274513
|
+
if (consumedDownstream) {
|
|
274514
|
+
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.");
|
|
274515
|
+
}
|
|
273933
274516
|
}
|
|
273934
274517
|
}
|
|
273935
274518
|
if (step.continueOnStatus !== void 0) {
|
|
@@ -288652,6 +289235,7 @@ var require_response_envelope = __commonJS({
|
|
|
288652
289235
|
"WRITE_DISABLED",
|
|
288653
289236
|
"INTERNAL"
|
|
288654
289237
|
];
|
|
289238
|
+
var MAX_MESSAGE_LENGTH = 500;
|
|
288655
289239
|
function errorCodeFrom(error) {
|
|
288656
289240
|
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
288657
289241
|
return "INTERNAL";
|
|
@@ -288659,6 +289243,28 @@ var require_response_envelope = __commonJS({
|
|
|
288659
289243
|
const code = error.code;
|
|
288660
289244
|
return typeof code === "string" && ERROR_CODES.includes(code) ? code : "INTERNAL";
|
|
288661
289245
|
}
|
|
289246
|
+
function describeError(error) {
|
|
289247
|
+
const record = typeof error === "object" && error !== null ? error : {};
|
|
289248
|
+
const issues = Array.isArray(record.issues) ? record.issues : void 0;
|
|
289249
|
+
if (issues && issues.length > 0) {
|
|
289250
|
+
const parts = issues.slice(0, 5).map((issue) => {
|
|
289251
|
+
const path15 = Array.isArray(issue?.path) ? issue.path.join(".") : "";
|
|
289252
|
+
return `${path15 ? `${path15}: ` : ""}${issue?.message ?? "invalid"}`;
|
|
289253
|
+
});
|
|
289254
|
+
const more = issues.length > 5 ? ` (+${issues.length - 5} more)` : "";
|
|
289255
|
+
return {
|
|
289256
|
+
message: `Invalid input \u2014 ${parts.join("; ")}${more}`,
|
|
289257
|
+
hint: "Fix the flagged argument(s) and retry. Use the tool schema for exact field types."
|
|
289258
|
+
};
|
|
289259
|
+
}
|
|
289260
|
+
const rawMessage = typeof record.message === "string" ? record.message.trim() : "";
|
|
289261
|
+
const message = rawMessage ? rawMessage.length > MAX_MESSAGE_LENGTH ? `${rawMessage.slice(0, MAX_MESSAGE_LENGTH)}\u2026` : rawMessage : "Tool execution failed.";
|
|
289262
|
+
const customHint = typeof record.hint === "string" && record.hint.trim() ? record.hint.trim() : "";
|
|
289263
|
+
return {
|
|
289264
|
+
message,
|
|
289265
|
+
hint: customHint || "Check the project configuration and arguments, then retry."
|
|
289266
|
+
};
|
|
289267
|
+
}
|
|
288662
289268
|
function toTextResult(data) {
|
|
288663
289269
|
return {
|
|
288664
289270
|
content: [{
|
|
@@ -288668,6 +289274,7 @@ var require_response_envelope = __commonJS({
|
|
|
288668
289274
|
};
|
|
288669
289275
|
}
|
|
288670
289276
|
function toErrorResult(error) {
|
|
289277
|
+
const { message, hint } = describeError(error);
|
|
288671
289278
|
return {
|
|
288672
289279
|
isError: true,
|
|
288673
289280
|
content: [{
|
|
@@ -288676,8 +289283,8 @@ var require_response_envelope = __commonJS({
|
|
|
288676
289283
|
ok: false,
|
|
288677
289284
|
error: {
|
|
288678
289285
|
code: errorCodeFrom(error),
|
|
288679
|
-
message
|
|
288680
|
-
hint
|
|
289286
|
+
message,
|
|
289287
|
+
hint
|
|
288681
289288
|
}
|
|
288682
289289
|
}, null, 2)
|
|
288683
289290
|
}]
|
|
@@ -289020,6 +289627,47 @@ var require_workflow_designer_tools = __commonJS({
|
|
|
289020
289627
|
if (Object.keys(inject).length > 0)
|
|
289021
289628
|
step.inject = inject;
|
|
289022
289629
|
}
|
|
289630
|
+
const pathParams = [...new Set(endpoint.path.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g) || [])].map((p) => p.slice(1));
|
|
289631
|
+
for (const pname of pathParams) {
|
|
289632
|
+
const inBody = step.body && typeof step.body === "object" && pname in step.body;
|
|
289633
|
+
if (inBody)
|
|
289634
|
+
continue;
|
|
289635
|
+
const memKey = [...producedKeys].find((k) => (k.split(":").pop() || "").toLowerCase() === pname.toLowerCase());
|
|
289636
|
+
if (memKey) {
|
|
289637
|
+
step.path = step.path.split(`:${pname}`).join(`{{${memKey}}}`);
|
|
289638
|
+
} else {
|
|
289639
|
+
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.`);
|
|
289640
|
+
}
|
|
289641
|
+
}
|
|
289642
|
+
const queryParams = (endpoint.parameters || []).filter((p) => p.in === "query");
|
|
289643
|
+
if (queryParams.length > 0) {
|
|
289644
|
+
const query = {};
|
|
289645
|
+
for (const p of queryParams) {
|
|
289646
|
+
const memKey = [...producedKeys].find((k) => (k.split(":").pop() || "").toLowerCase() === p.name.toLowerCase());
|
|
289647
|
+
if (memKey) {
|
|
289648
|
+
query[p.name] = `{{${memKey}}}`;
|
|
289649
|
+
continue;
|
|
289650
|
+
}
|
|
289651
|
+
if (p.required !== true)
|
|
289652
|
+
continue;
|
|
289653
|
+
const val = fieldToFakerValue(p.name, {
|
|
289654
|
+
type: p.type || "string",
|
|
289655
|
+
format: p.format,
|
|
289656
|
+
min: p.min,
|
|
289657
|
+
max: p.max,
|
|
289658
|
+
enum: p.enum
|
|
289659
|
+
});
|
|
289660
|
+
if (val !== null) {
|
|
289661
|
+
query[p.name] = val;
|
|
289662
|
+
} else {
|
|
289663
|
+
query[p.name] = `{{human:${p.name}}}`;
|
|
289664
|
+
allHumanKeys.add(p.name);
|
|
289665
|
+
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).`);
|
|
289666
|
+
}
|
|
289667
|
+
}
|
|
289668
|
+
if (Object.keys(query).length > 0)
|
|
289669
|
+
step.query = query;
|
|
289670
|
+
}
|
|
289023
289671
|
const capture = {};
|
|
289024
289672
|
if (endpoint.produces) {
|
|
289025
289673
|
for (const p of endpoint.produces) {
|
|
@@ -289070,6 +289718,109 @@ var require_workflow_designer_tools = __commonJS({
|
|
|
289070
289718
|
}
|
|
289071
289719
|
});
|
|
289072
289720
|
|
|
289721
|
+
// ../../packages/mcp-server/dist/tools/run-tools.js
|
|
289722
|
+
var require_run_tools = __commonJS({
|
|
289723
|
+
"../../packages/mcp-server/dist/tools/run-tools.js"(exports2) {
|
|
289724
|
+
"use strict";
|
|
289725
|
+
init_cjs_shims();
|
|
289726
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
289727
|
+
if (k2 === void 0) k2 = k;
|
|
289728
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
289729
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
289730
|
+
desc = { enumerable: true, get: function() {
|
|
289731
|
+
return m[k];
|
|
289732
|
+
} };
|
|
289733
|
+
}
|
|
289734
|
+
Object.defineProperty(o, k2, desc);
|
|
289735
|
+
}) : (function(o, m, k, k2) {
|
|
289736
|
+
if (k2 === void 0) k2 = k;
|
|
289737
|
+
o[k2] = m[k];
|
|
289738
|
+
}));
|
|
289739
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
289740
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
289741
|
+
}) : function(o, v) {
|
|
289742
|
+
o["default"] = v;
|
|
289743
|
+
});
|
|
289744
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
289745
|
+
var ownKeys = function(o) {
|
|
289746
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
289747
|
+
var ar = [];
|
|
289748
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
289749
|
+
return ar;
|
|
289750
|
+
};
|
|
289751
|
+
return ownKeys(o);
|
|
289752
|
+
};
|
|
289753
|
+
return function(mod) {
|
|
289754
|
+
if (mod && mod.__esModule) return mod;
|
|
289755
|
+
var result = {};
|
|
289756
|
+
if (mod != null) {
|
|
289757
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
289758
|
+
}
|
|
289759
|
+
__setModuleDefault(result, mod);
|
|
289760
|
+
return result;
|
|
289761
|
+
};
|
|
289762
|
+
})();
|
|
289763
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
289764
|
+
exports2.listRunsSchema = exports2.getRunSchema = void 0;
|
|
289765
|
+
exports2.handleGetRun = handleGetRun;
|
|
289766
|
+
exports2.handleListRuns = handleListRuns;
|
|
289767
|
+
var zod_1 = require("zod");
|
|
289768
|
+
var path15 = __importStar(require("path"));
|
|
289769
|
+
var core_1 = require_dist();
|
|
289770
|
+
var types_1 = require_types();
|
|
289771
|
+
var run_store_1 = require_run_store();
|
|
289772
|
+
exports2.getRunSchema = zod_1.z.object({
|
|
289773
|
+
projectPath: zod_1.z.string().optional().describe("Path to project root or .jetic folder"),
|
|
289774
|
+
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')
|
|
289775
|
+
});
|
|
289776
|
+
exports2.listRunsSchema = zod_1.z.object({
|
|
289777
|
+
projectPath: zod_1.z.string().optional().describe("Path to project root or .jetic folder"),
|
|
289778
|
+
limit: zod_1.z.number().int().positive().max(100).optional().default(20).describe("Max runs to return, newest first (default 20)")
|
|
289779
|
+
});
|
|
289780
|
+
function handleGetRun(args) {
|
|
289781
|
+
const projectRoot = path15.resolve((0, types_1.resolveProjectInput)(args.projectPath));
|
|
289782
|
+
const config = (0, core_1.loadConfig)(projectRoot);
|
|
289783
|
+
const report = (0, run_store_1.readSimulationRun)(config.jeticDir, args.runId);
|
|
289784
|
+
if (report)
|
|
289785
|
+
return { found: true, ...report };
|
|
289786
|
+
const status = (0, run_store_1.readRunStatus)(config.jeticDir, args.runId);
|
|
289787
|
+
if (status) {
|
|
289788
|
+
return {
|
|
289789
|
+
found: true,
|
|
289790
|
+
type: "simulation",
|
|
289791
|
+
runId: args.runId,
|
|
289792
|
+
status: status.status,
|
|
289793
|
+
workflowName: status.workflowName,
|
|
289794
|
+
environment: status.environment,
|
|
289795
|
+
baseUrl: status.baseUrl,
|
|
289796
|
+
startedAt: status.startedAt,
|
|
289797
|
+
finishedAt: status.finishedAt,
|
|
289798
|
+
stats: status.stats,
|
|
289799
|
+
reportPath: status.reportPath,
|
|
289800
|
+
...status.error ? { error: status.error } : {},
|
|
289801
|
+
...status.status === "running" ? { message: "Run still in progress \u2014 poll again shortly." } : {}
|
|
289802
|
+
};
|
|
289803
|
+
}
|
|
289804
|
+
const agentReport = (0, core_1.readJsonSync)(path15.join(config.jeticDir, "runs", args.runId, "agent-report.json"));
|
|
289805
|
+
if (agentReport)
|
|
289806
|
+
return { found: true, type: "agent", runId: args.runId, ...agentReport };
|
|
289807
|
+
const available = (0, run_store_1.listRuns)(config.jeticDir, 10).map((r) => `${r.runId} (${r.type}: ${r.status})`);
|
|
289808
|
+
const detail = available.length > 0 ? ` Recent runs: ${available.join(", ")}.` : " No runs found yet.";
|
|
289809
|
+
throw new Error(`Run "${args.runId}" not found under .jetic/runs/.${detail}`);
|
|
289810
|
+
}
|
|
289811
|
+
function handleListRuns(args) {
|
|
289812
|
+
const projectRoot = path15.resolve((0, types_1.resolveProjectInput)(args.projectPath));
|
|
289813
|
+
const config = (0, core_1.loadConfig)(projectRoot);
|
|
289814
|
+
const runs = (0, run_store_1.listRuns)(config.jeticDir, args.limit ?? 20);
|
|
289815
|
+
return {
|
|
289816
|
+
runsDir: path15.join(config.jeticDir, "runs"),
|
|
289817
|
+
totalCount: runs.length,
|
|
289818
|
+
runs
|
|
289819
|
+
};
|
|
289820
|
+
}
|
|
289821
|
+
}
|
|
289822
|
+
});
|
|
289823
|
+
|
|
289073
289824
|
// ../../packages/mcp-server/dist/server.js
|
|
289074
289825
|
var require_server3 = __commonJS({
|
|
289075
289826
|
"../../packages/mcp-server/dist/server.js"(exports2) {
|
|
@@ -289134,6 +289885,7 @@ var require_server3 = __commonJS({
|
|
|
289134
289885
|
var session_tools_1 = require_session_tools();
|
|
289135
289886
|
var workflow_designer_tools_1 = require_workflow_designer_tools();
|
|
289136
289887
|
var agent_tools_1 = require_agent_tools();
|
|
289888
|
+
var run_tools_1 = require_run_tools();
|
|
289137
289889
|
exports2.JETIC_MCP_SERVER_NAME = "jetic-mcp-server";
|
|
289138
289890
|
function getJeticMcpVersion() {
|
|
289139
289891
|
try {
|
|
@@ -289222,6 +289974,9 @@ var require_server3 = __commonJS({
|
|
|
289222
289974
|
"",
|
|
289223
289975
|
"STEP 4g \u2014 RUN",
|
|
289224
289976
|
' jetic_simulate_workflow { "workflow": "<slug>" }',
|
|
289977
|
+
" It preflights the target first and fails fast with a clear reason when the app is unreachable.",
|
|
289978
|
+
' The response is a compact summary + runId. Full step detail: jetic_get_run { "runId": "..." };',
|
|
289979
|
+
" recent runs: jetic_list_runs. For long journeys pass async:true, then poll jetic_get_run.",
|
|
289225
289980
|
"",
|
|
289226
289981
|
"\u2501\u2501 GENERAL RULES \u2501\u2501",
|
|
289227
289982
|
"No AI-provider setup is needed: never ask for OpenRouter/OpenAI keys and never use `jetic config ai`.",
|
|
@@ -289289,6 +290044,8 @@ var require_server3 = __commonJS({
|
|
|
289289
290044
|
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
290045
|
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
290046
|
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);
|
|
290047
|
+
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);
|
|
290048
|
+
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
290049
|
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
290050
|
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
290051
|
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);
|
|
@@ -292564,7 +293321,7 @@ var mcpCommand = new import_commander18.Command("mcp").description("Launch Model
|
|
|
292564
293321
|
|
|
292565
293322
|
// src/index.ts
|
|
292566
293323
|
var program = new import_commander19.Command();
|
|
292567
|
-
program.name("jetic").description("AI-Native API Behavior Testing").version("0.2.
|
|
293324
|
+
program.name("jetic").description("AI-Native API Behavior Testing").version("0.2.6");
|
|
292568
293325
|
program.addCommand(initCommand);
|
|
292569
293326
|
program.addCommand(scanCommand);
|
|
292570
293327
|
program.addCommand(inspectCommand);
|