cpi-saper-dev-mcp 0.9.0-beta.0 → 0.9.0-beta.1
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/CHANGELOG.md +15 -1
- package/README.md +3 -3
- package/bundle/index.js +547 -25
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
All notable changes of the `cpi-saper-dev-mcp` server. Versions follow [semantic versioning](https://semver.org/);
|
|
4
4
|
before 1.0, names and behaviour may still change between versions.
|
|
5
5
|
|
|
6
|
+
## 0.9.0-beta.1 (2026-11-04)
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- `iflow_edit`: local integration processes: `add_local_process`, `remove_local_process`, Process Call and Looping
|
|
11
|
+
Process Call with `insert_step` `process` (and `loop` for the looping one); `layout` handles iFlows with several
|
|
12
|
+
processes (one pool per process, stacked); a branching edit lays out only its own process.
|
|
13
|
+
- `iflow_validate`: calls pointing to no local process, a looping call without its loop, unquoted values in Camel loop
|
|
14
|
+
conditions.
|
|
15
|
+
- `iflow_edit` `set_start`: timer start (scheduled iFlow) or back to a message start. The schedule is always
|
|
16
|
+
externalized (parameter of type `custom:schedule`): run once, every n minutes or hours, or a cron schedule, with
|
|
17
|
+
start, end and time zone. `params_set` and `cpi_config_set` take schedule objects; `iflow_validate` warns about a
|
|
18
|
+
timer whose schedule is not externalized.
|
|
19
|
+
|
|
6
20
|
## 0.9.0-beta.0 (2026-11-02)
|
|
7
21
|
|
|
8
22
|
First public beta. **For development tenants only**: do not connect a production tenant (see the README).
|
|
@@ -39,6 +53,6 @@ Read-only and development roles per tenant, allowed packages, confirmation of ch
|
|
|
39
53
|
|
|
40
54
|
- Tried against one tenant (a BTP trial); other tenants may answer differently in places
|
|
41
55
|
([docs/KNOWN_LIMITATIONS.md](docs/KNOWN_LIMITATIONS.md) lists every difference found).
|
|
42
|
-
- iFlows with local integration processes cannot be laid out or branched by `iflow_edit
|
|
56
|
+
- iFlows with local integration processes cannot be laid out or branched by `iflow_edit` (added in 0.9.0-beta.1).
|
|
43
57
|
- Not offered by design: creating or deleting packages, editing message mappings, managing locks.
|
|
44
58
|
- Planned: `lint`, PNG rendering, user roles for sender authorization.
|
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ adds no generic file tools.
|
|
|
62
62
|
|
|
63
63
|
| Tool | What it does | Status |
|
|
64
64
|
|---|---|---|
|
|
65
|
-
| `iflow_edit` | Semantic edits in one all-or-nothing batch: insert a step from the catalog (with settings, headers, properties; for XSLT and message mappings the mapping file, for a Filter its XPath, for a script step a script from a script collection), remove a step, add or remove an adapter (sender to a start event or Poll Enrich, receiver from Request Reply, Send, Content Enricher or an end event; the Sender/Receiver box is reused or created, receivers drawn below their step), add or remove an exception subprocess (steps go inside like anywhere else), add routers (conditions, default route) and parallel or sequential multicasts with a Join, put steps on their routes, edit or remove them, lay out the
|
|
65
|
+
| `iflow_edit` | Semantic edits in one all-or-nothing batch: insert a step from the catalog (with settings, headers, properties; for XSLT and message mappings the mapping file, for a Filter its XPath, for a script step a script from a script collection), remove a step, add or remove an adapter (sender to a start event or Poll Enrich, receiver from Request Reply, Send, Content Enricher or an end event; the Sender/Receiver box is reused or created, receivers drawn below their step), add or remove an exception subprocess (steps go inside like anywhere else), add routers (conditions, default route) and parallel or sequential multicasts with a Join, put steps on their routes, edit or remove them, add or remove local integration processes and call them (Process Call, Looping Process Call with its loop), make the start a timer (run once, every n minutes or hours, or cron; start and end, time zone; always externalized so the schedule can be configured), lay out the iFlow (branches in rows, exception subprocess below, one pool per process), change settings of steps, adapters or the iFlow (unknown keys refused), rename, edit Content Modifier headers and properties. Keeps SAP's formatting, lays out the diagram, refuses edits that would break the iFlow, supports a dry run | **Available** |
|
|
66
66
|
| `params_set` | Declare, change or remove externalized parameters: `parameters.propdef` and `parameters.prop` kept consistent in the designer's format; shows where each `{{name}}` is used | **Available** |
|
|
67
67
|
|
|
68
68
|
### Check
|
|
@@ -92,7 +92,7 @@ development.**
|
|
|
92
92
|
| `cpi_deploy` | Deploy the current version and wait; SAP's guideline check as an optional gate; errors are reported with step names, one line per problem. A failed deploy also stops the version that was running (Cloud Integration's behaviour), which the confirmation and result point out | **Available** |
|
|
93
93
|
| `cpi_undeploy` | Remove a deployed iFlow or script collection from the runtime | **Available** |
|
|
94
94
|
| `iflow_delete` | Delete an iFlow with its whole version history (for cleaning up test iFlows); refuses deployed iFlows and always asks for confirmation by default | **Available** |
|
|
95
|
-
| `cpi_config_set` | Configure parameter values on the tenant like "Configure" in the designer (no new version; the running iFlow uses them after the next deploy); the local `parameters.prop` follows | **Available** |
|
|
95
|
+
| `cpi_config_set` | Configure parameter values on the tenant like "Configure" in the designer (no new version; the running iFlow uses them after the next deploy), timer schedules as a schedule object; the local `parameters.prop` follows | **Available** |
|
|
96
96
|
|
|
97
97
|
### Script collections (development tenants only)
|
|
98
98
|
|
|
@@ -161,7 +161,7 @@ for every change instead of the service key's technical client.
|
|
|
161
161
|
Requires Node.js 22 or later. The server is published on npm as
|
|
162
162
|
[`cpi-saper-dev-mcp`](https://www.npmjs.com/package/cpi-saper-dev-mcp): your AI client starts it with `npx`, which
|
|
163
163
|
downloads it on first use. During the beta the examples use `cpi-saper-dev-mcp@beta` (the newest beta); to pin one
|
|
164
|
-
version, write for example `cpi-saper-dev-mcp@0.9.0-beta.
|
|
164
|
+
version, write for example `cpi-saper-dev-mcp@0.9.0-beta.1`.
|
|
165
165
|
|
|
166
166
|
Create `.cpi/config.json` in your project folder ([docs/CONFIGURATION.md](docs/CONFIGURATION.md)), then connect the
|
|
167
167
|
server to your AI client as shown below.
|
package/bundle/index.js
CHANGED
|
@@ -849,6 +849,24 @@ function validate(doc, { resourceFiles, declaredParameters } = {}) {
|
|
|
849
849
|
const need = NEEDS_ADAPTER[cname];
|
|
850
850
|
if (need && !adapterOn.has(el.getAttribute("id")))
|
|
851
851
|
add(need.severity, "NO_ADAPTER", `${label} needs ${need.what} (iflow_edit add_adapter)`);
|
|
852
|
+
if (p.activityType === "ProcessCallElement") {
|
|
853
|
+
const target = p.processId ? byTag(doc, "process").find((x) => x.getAttribute("id") === p.processId) : void 0;
|
|
854
|
+
if (!p.processId?.trim())
|
|
855
|
+
add("error", "MISSING_SETTING", `${label}: processId is empty; set the local integration process to call (iflow_edit insert_step "process")`);
|
|
856
|
+
else if (!target)
|
|
857
|
+
add("error", "INVALID_SETTING", `${label}: calls ${p.processId}, which is not a process of this iFlow`);
|
|
858
|
+
else if (readProps(target).processType !== "directCall")
|
|
859
|
+
add("error", "INVALID_SETTING", `${label}: calls ${p.processId}, which is not a local integration process`);
|
|
860
|
+
if (p.subActivityType === "LoopingProcess") {
|
|
861
|
+
const loop = children(el, "standardLoopCharacteristics")[0];
|
|
862
|
+
if (!loop || loop.getAttribute("id") !== p.loopId)
|
|
863
|
+
add("error", "INVALID_SETTING", `${label}: the loop (standardLoopCharacteristics ${p.loopId ?? ""}) is missing; insert the step with "loop"`);
|
|
864
|
+
const cond = loop ? children(loop, "loopCondition")[0]?.textContent?.trim() ?? "" : "";
|
|
865
|
+
if (p.expressionType !== "XML" && /(?:<=|>=|!=|=|<|>)\s*(?!['"$])[^\s')]+/.test(cond)) {
|
|
866
|
+
add("error", "INVALID_SETTING", `${label}: loop condition "${cond}": put the compared value in quotes (e.g. < '3'); SAP's model check refuses it otherwise`);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
852
870
|
const version = parseVariant(p.cmdVariantUri).version ?? "";
|
|
853
871
|
if (cname === "GroovyScript" && version.startsWith("2.") && p.script && !p.scriptBundleId?.trim() && !p.script.startsWith("v2/")) {
|
|
854
872
|
add("error", "INVALID_SETTING", `${label}: a Groovy Script ${version} step takes its script from script/v2/; set script to "v2/${p.script}" and move the file there`);
|
|
@@ -868,6 +886,12 @@ function validate(doc, { resourceFiles, declaredParameters } = {}) {
|
|
|
868
886
|
add("error", "INVALID_SETTING", `route ${f.getAttribute("id")} (${f.getAttribute("name") ?? ""}): condition "${cond.trim()}" uses ==; Camel Simple compares with = (also !=, >, >=, <, <=, contains, in, regex)`);
|
|
869
887
|
}
|
|
870
888
|
}
|
|
889
|
+
for (const t of byTag(doc, "timerEventDefinition")) {
|
|
890
|
+
const key = readProps(t).scheduleKey ?? "";
|
|
891
|
+
if (!/^\{\{[^{}]+\}\}$/.test(key.trim())) {
|
|
892
|
+
add("warning", "SCHEDULE_NOT_EXTERNALIZED", `${t.parentNode.getAttribute("id")}: the timer's schedule is not externalized; iflow_edit set_start writes it as a parameter`);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
871
895
|
const allowed = byTag(doc, "collaboration")[0] ? readProps(byTag(doc, "collaboration")[0]).allowedHeaderList : void 0;
|
|
872
896
|
if (allowed?.includes(","))
|
|
873
897
|
add("warning", "ALLOWED_HEADERS", `allowedHeaderList "${allowed}": headers are separated by | (with commas no header gets through): "${allowed.split(/\s*,\s*/).join("|")}"`);
|
|
@@ -1376,6 +1400,149 @@ async function listSecurityMaterial(client, now = /* @__PURE__ */ new Date()) {
|
|
|
1376
1400
|
};
|
|
1377
1401
|
}
|
|
1378
1402
|
|
|
1403
|
+
// ../core/dist/schedule.js
|
|
1404
|
+
var UTC_LABEL = "( UTC 0:00 ) Greenwich Mean Time(Etc/GMT)";
|
|
1405
|
+
var DESIGNER_ZONES = { NST: "( UTC 12:00 ) New Zealand Standard Time(NST)" };
|
|
1406
|
+
var DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/;
|
|
1407
|
+
function zoneLabel(id) {
|
|
1408
|
+
if (id === "Etc/GMT")
|
|
1409
|
+
return UTC_LABEL;
|
|
1410
|
+
if (DESIGNER_ZONES[id])
|
|
1411
|
+
return DESIGNER_ZONES[id];
|
|
1412
|
+
try {
|
|
1413
|
+
new Intl.DateTimeFormat("en-US", { timeZone: id });
|
|
1414
|
+
} catch {
|
|
1415
|
+
throw new Error(`"${id}" is not a time zone id (use IANA ids such as Europe/Warsaw)`);
|
|
1416
|
+
}
|
|
1417
|
+
const part = (date, style) => new Intl.DateTimeFormat("en-US", { timeZone: id, timeZoneName: style }).formatToParts(date).find((p) => p.type === "timeZoneName").value;
|
|
1418
|
+
const minutes = (o) => {
|
|
1419
|
+
const m = o.match(/GMT([+-])(\d{2}):(\d{2})/);
|
|
1420
|
+
return m ? (m[1] === "-" ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : 0;
|
|
1421
|
+
};
|
|
1422
|
+
const [jan, jul] = [new Date(Date.UTC(2026, 0, 15)), new Date(Date.UTC(2026, 6, 15))];
|
|
1423
|
+
const std = minutes(part(jan, "longOffset")) <= minutes(part(jul, "longOffset")) ? jan : jul;
|
|
1424
|
+
const off = minutes(part(std, "longOffset"));
|
|
1425
|
+
const offText = `${off < 0 ? "-" : ""}${Math.floor(Math.abs(off) / 60)}:${String(Math.abs(off) % 60).padStart(2, "0")}`;
|
|
1426
|
+
return `( UTC ${offText} ) ${part(std, "long")}(${id})`;
|
|
1427
|
+
}
|
|
1428
|
+
var RANGES = [["second", 0, 59], ["minute", 0, 59], ["hour", 0, 23], ["day of month", 1, 31], ["month", 1, 12], ["day of week", 1, 7], ["year", 1970, 2199]];
|
|
1429
|
+
var NAMES = { 4: /^(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)$/, 5: /^(SUN|MON|TUE|WED|THU|FRI|SAT)$/ };
|
|
1430
|
+
function cronFields(cron) {
|
|
1431
|
+
const f = cron.trim().split(/\s+/);
|
|
1432
|
+
if (f.length === 6)
|
|
1433
|
+
f.push("*");
|
|
1434
|
+
if (f.length !== 7)
|
|
1435
|
+
throw new Error(`cron "${cron}" needs 6 or 7 fields: second minute hour day-of-month month day-of-week [year], e.g. "0 15 8 ? * MON-FRI"`);
|
|
1436
|
+
f.forEach((field, i) => {
|
|
1437
|
+
const [name, lo, hi] = RANGES[i];
|
|
1438
|
+
const value = (v) => /^\d+$/.test(v) ? Number(v) >= lo && Number(v) <= hi : !!NAMES[i]?.test(v.toUpperCase());
|
|
1439
|
+
const ok = field === "*" || field === "?" && (i === 3 || i === 5) || field.split(",").every((part) => {
|
|
1440
|
+
const [base, step] = part.split("/");
|
|
1441
|
+
if (step !== void 0 && !/^\d+$/.test(step))
|
|
1442
|
+
return false;
|
|
1443
|
+
if (base === "*")
|
|
1444
|
+
return step !== void 0;
|
|
1445
|
+
const [a, b] = base.split("-");
|
|
1446
|
+
return value(a) && (b === void 0 || value(b)) || i === 3 && /^(L|LW|\d{1,2}W)$/.test(part) || i === 5 && /^[1-7A-Z]{1,3}(#\d|L)$/i.test(part);
|
|
1447
|
+
});
|
|
1448
|
+
if (!ok)
|
|
1449
|
+
throw new Error(`cron "${cron}": "${field}" is not a valid ${name} (${lo}-${hi}, *, lists, ranges, steps like 0/15)`);
|
|
1450
|
+
});
|
|
1451
|
+
if (f[3] === "?" === (f[5] === "?"))
|
|
1452
|
+
throw new Error(`cron "${cron}": exactly one of day-of-month and day-of-week must be "?"`);
|
|
1453
|
+
return f;
|
|
1454
|
+
}
|
|
1455
|
+
function checkSchedule(s) {
|
|
1456
|
+
if (s === "once")
|
|
1457
|
+
return s;
|
|
1458
|
+
const o = s ?? {};
|
|
1459
|
+
if ("every" in o === "cron" in o)
|
|
1460
|
+
throw new Error('a schedule is "once", {every, unit} (simple) or {cron} (advanced)');
|
|
1461
|
+
if ("every" in o && o.unit !== "minutes" && o.unit !== "hours")
|
|
1462
|
+
throw new Error('a simple schedule needs unit "minutes" or "hours"');
|
|
1463
|
+
return o;
|
|
1464
|
+
}
|
|
1465
|
+
function window(s) {
|
|
1466
|
+
for (const [k, v] of [["from", s.from], ["until", s.until]])
|
|
1467
|
+
if (v !== void 0 && !DATE.test(v))
|
|
1468
|
+
throw new Error(`${k} "${v}" must look like 2026-11-05T08:00:00`);
|
|
1469
|
+
if (s.from && s.until && s.from >= s.until)
|
|
1470
|
+
throw new Error(`until ${s.until} is not after from ${s.from}`);
|
|
1471
|
+
const tz = s.timeZone ?? "Etc/GMT";
|
|
1472
|
+
return { tz, label: zoneLabel(tz), from: s.from ?? "", until: s.until ?? "" };
|
|
1473
|
+
}
|
|
1474
|
+
function scheduleTable(input) {
|
|
1475
|
+
const schedule = checkSchedule(input);
|
|
1476
|
+
const esc = (v) => v.replace(/&/g, "&");
|
|
1477
|
+
let rows;
|
|
1478
|
+
if (schedule === "once") {
|
|
1479
|
+
rows = [
|
|
1480
|
+
["dateType", ""],
|
|
1481
|
+
["timeType", ""],
|
|
1482
|
+
["dayValue", ""],
|
|
1483
|
+
["monthValue", ""],
|
|
1484
|
+
["yearValue", ""],
|
|
1485
|
+
["onWeekly", ""],
|
|
1486
|
+
["onMonthly", ""],
|
|
1487
|
+
["OnEveryMinute", ""],
|
|
1488
|
+
["fromInterval", ""],
|
|
1489
|
+
["toInterval", ""],
|
|
1490
|
+
["timeZone", UTC_LABEL],
|
|
1491
|
+
["secondValue", "0"],
|
|
1492
|
+
["minutesValue", ""],
|
|
1493
|
+
["hourValue", ""],
|
|
1494
|
+
["triggerType", "simple"],
|
|
1495
|
+
["noOfSchedules", "1"],
|
|
1496
|
+
["schedule1", "fireNow=true"]
|
|
1497
|
+
];
|
|
1498
|
+
} else {
|
|
1499
|
+
const w = window(schedule);
|
|
1500
|
+
const tail = `&trigger.timeZone=${w.tz}${w.from ? `&trigger.startAt=${w.from}` : ""}${w.until ? `&trigger.endAt=${w.until}` : ""}`;
|
|
1501
|
+
let cron = ["*", "*", "*", "?", "*", "*", "*"];
|
|
1502
|
+
let schedule1;
|
|
1503
|
+
if ("every" in schedule) {
|
|
1504
|
+
const max = schedule.unit === "minutes" ? 59 : 23;
|
|
1505
|
+
if (!Number.isInteger(schedule.every) || schedule.every < 1 || schedule.every > max)
|
|
1506
|
+
throw new Error(`every ${schedule.unit}: a whole number from 1 to ${max}`);
|
|
1507
|
+
schedule1 = `simple.repeat=${schedule.unit === "minutes" ? "MINUTES" : "HOURS"}&simple.every=${schedule.every}${tail}`;
|
|
1508
|
+
} else {
|
|
1509
|
+
cron = cronFields(schedule.cron);
|
|
1510
|
+
schedule1 = `${cron.join("+")}${tail}`;
|
|
1511
|
+
}
|
|
1512
|
+
rows = [
|
|
1513
|
+
["dateType", "every" in schedule ? "SIMPLE" : "ADVANCED"],
|
|
1514
|
+
["secondValue", "0"],
|
|
1515
|
+
["minutesValue", ""],
|
|
1516
|
+
["hourValue", ""],
|
|
1517
|
+
["toInterval", "1"],
|
|
1518
|
+
["fromInterval", "0"],
|
|
1519
|
+
["OnEveryMinute", "1"],
|
|
1520
|
+
["timeType", "TIME_INTERVAL"],
|
|
1521
|
+
["timeZone", w.label],
|
|
1522
|
+
["throwExceptionOnExpiry", "true"],
|
|
1523
|
+
["second", cron[0]],
|
|
1524
|
+
["minute", cron[1]],
|
|
1525
|
+
["hour", cron[2]],
|
|
1526
|
+
["day_of_month", cron[3]],
|
|
1527
|
+
["month", cron[4]],
|
|
1528
|
+
["dayOfWeek", cron[5]],
|
|
1529
|
+
["year", cron[6]],
|
|
1530
|
+
["startAt", w.from],
|
|
1531
|
+
["endAt", w.until],
|
|
1532
|
+
["attributeBehaviour", "isThrowExceptionOnExpiryVisible,isScheduleAdvancedVisible,isScheduleAdvancedStartEndVisible,isScheduleSimpleVisible"],
|
|
1533
|
+
["triggerType", "cron"],
|
|
1534
|
+
["noOfSchedules", "1"],
|
|
1535
|
+
["schedule1", schedule1]
|
|
1536
|
+
];
|
|
1537
|
+
}
|
|
1538
|
+
return rows.map(([k, v]) => `<row><cell>${k}</cell><cell>${esc(v)}</cell></row>`).join("");
|
|
1539
|
+
}
|
|
1540
|
+
function scheduleConfigValue(schedule) {
|
|
1541
|
+
const w = window(schedule);
|
|
1542
|
+
const head = "every" in schedule ? `simple.repeat=${schedule.unit === "minutes" ? "MINUTES" : "HOURS"} --simple.every=${schedule.every}` : cronFields(schedule.cron).join(" ");
|
|
1543
|
+
return `${head} --tz=${w.tz}${w.from ? ` --startAt=${w.from}` : ""}${w.until ? ` --endAt=${w.until}` : ""}`;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1379
1546
|
// ../core/dist/iflow/edit.js
|
|
1380
1547
|
var isEl3 = (n) => !!n && n.nodeType === 1;
|
|
1381
1548
|
var fmt = (n) => Number.isInteger(n) ? `${n}.0` : String(n);
|
|
@@ -2173,6 +2340,153 @@ function removeGateway(doc, ref3) {
|
|
|
2173
2340
|
edge.setAttribute("targetElement", `BPMNShape_${next.getAttribute("id")}`);
|
|
2174
2341
|
return `removed ${gwId}${join15 ? ` and its Join ${join15.getAttribute("id")}` : ""} with ${routes.length} routes; ${inFlow.getAttribute("sourceRef")} leads to ${next.getAttribute("id")} again`;
|
|
2175
2342
|
}
|
|
2343
|
+
function findLocalProcess(doc, ref3) {
|
|
2344
|
+
const p = findProcess(doc, ref3);
|
|
2345
|
+
if (readProps(p).processType !== "directCall")
|
|
2346
|
+
throw new Error(`${p.getAttribute("id")} "${p.getAttribute("name")}" is not a local integration process`);
|
|
2347
|
+
return p;
|
|
2348
|
+
}
|
|
2349
|
+
var plainEvent = (doc, tag2, id, name, flow) => {
|
|
2350
|
+
const el = elementNS(doc, `bpmn2:${tag2}`, { id, name });
|
|
2351
|
+
const ext = elementNS(doc, "bpmn2:extensionElements");
|
|
2352
|
+
const cname = tag2 === "startEvent" ? "StartEvent" : "EndEvent";
|
|
2353
|
+
ext.appendChild(iflProperty(doc, "cmdVariantUri", `ctype::FlowstepVariant/cname::${cname}`));
|
|
2354
|
+
ext.appendChild(iflProperty(doc, "activityType", cname));
|
|
2355
|
+
el.appendChild(ext);
|
|
2356
|
+
el.appendChild(elementNS(doc, tag2 === "startEvent" ? "bpmn2:outgoing" : "bpmn2:incoming", {}, flow));
|
|
2357
|
+
return el;
|
|
2358
|
+
};
|
|
2359
|
+
function addLocalProcess(doc, { name, processProps }) {
|
|
2360
|
+
const index = idIndex(doc);
|
|
2361
|
+
const collab = byTag(doc, "collaboration")[0];
|
|
2362
|
+
const pools = children(collab, "participant").filter((p) => p.getAttribute("ifl:type") === "IntegrationProcess");
|
|
2363
|
+
const names = new Set(byTag(doc, "process").map((p) => p.getAttribute("name")));
|
|
2364
|
+
let procName2 = name;
|
|
2365
|
+
for (let n = 1; !procName2; n++)
|
|
2366
|
+
if (!names.has(`Local Integration Process ${n}`))
|
|
2367
|
+
procName2 = `Local Integration Process ${n}`;
|
|
2368
|
+
if (names.has(procName2))
|
|
2369
|
+
throw new Error(`a process named "${procName2}" exists already`);
|
|
2370
|
+
const processId = newId(index, "Process");
|
|
2371
|
+
const startId = newId(index, "StartEvent");
|
|
2372
|
+
const endId = newId(index, "EndEvent");
|
|
2373
|
+
const flowId = newId(index, "SequenceFlow");
|
|
2374
|
+
const poolId = `Participant_${processId}`;
|
|
2375
|
+
index.set(poolId, null);
|
|
2376
|
+
const participant = elementNS(doc, "bpmn2:participant", { id: poolId, "ifl:type": "IntegrationProcess", name: procName2, processRef: processId });
|
|
2377
|
+
participant.appendChild(elementNS(doc, "bpmn2:extensionElements"));
|
|
2378
|
+
insertAfterEl(collab, participant, pools.at(-1) ?? children(collab, "participant").at(-1));
|
|
2379
|
+
const proc = withProps(doc, elementNS(doc, "bpmn2:process", { id: processId, name: procName2 }), processProps);
|
|
2380
|
+
proc.appendChild(plainEvent(doc, "startEvent", startId, `Start ${procName2}`, flowId));
|
|
2381
|
+
proc.appendChild(plainEvent(doc, "endEvent", endId, `End ${procName2}`, flowId));
|
|
2382
|
+
proc.appendChild(flowEl(doc, flowId, startId, endId));
|
|
2383
|
+
insertAfterEl(doc.documentElement, proc, byTag(doc, "process").at(-1));
|
|
2384
|
+
const shapes = pools.map((p) => shapeOf(doc, p.getAttribute("id"))).filter((x2) => !!x2).map(bounds);
|
|
2385
|
+
const x = shapes.length ? Math.min(...shapes.map((b) => b.x)) : 250;
|
|
2386
|
+
const y = shapes.length ? Math.max(...byTag(doc, "BPMNShape", NS.bpmndi).map(bounds).map((b) => b.y + b.h)) + 60 : 60;
|
|
2387
|
+
const plane = byTag(doc, "BPMNPlane", NS.bpmndi)[0];
|
|
2388
|
+
let last = byTag(doc, "BPMNShape", NS.bpmndi).at(-1);
|
|
2389
|
+
const put = (id, b) => {
|
|
2390
|
+
const sh = elementNS(doc, "bpmndi:BPMNShape", { bpmnElement: id, id: `BPMNShape_${id}` });
|
|
2391
|
+
sh.appendChild(elementNS(doc, "dc:Bounds", { height: fmt(b.h), width: fmt(b.w), x: fmt(b.x), y: fmt(b.y) }));
|
|
2392
|
+
insertAfterEl(plane, sh, last);
|
|
2393
|
+
last = sh;
|
|
2394
|
+
return b;
|
|
2395
|
+
};
|
|
2396
|
+
put(poolId, { x, y, w: 540, h: 220 });
|
|
2397
|
+
const sb = put(startId, { x: x + 42, y: y + 82, w: 32, h: 32 });
|
|
2398
|
+
const eb = put(endId, { x: x + 453, y: y + 82, w: 32, h: 32 });
|
|
2399
|
+
const edge = elementNS(doc, "bpmndi:BPMNEdge", { bpmnElement: flowId, id: `BPMNEdge_${flowId}`, sourceElement: `BPMNShape_${startId}`, targetElement: `BPMNShape_${endId}` });
|
|
2400
|
+
for (const b of [sb, eb])
|
|
2401
|
+
edge.appendChild(elementNS(doc, "di:waypoint", { x: fmt(b.x + b.w / 2), "xsi:type": "dc:Point", y: fmt(b.y + b.h / 2) }));
|
|
2402
|
+
insertAfterEl(plane, edge, byTag(doc, "BPMNEdge", NS.bpmndi).at(-1) ?? last);
|
|
2403
|
+
return { processId, startId, endId, name: procName2 };
|
|
2404
|
+
}
|
|
2405
|
+
function removeLocalProcess(doc, ref3) {
|
|
2406
|
+
const proc = findLocalProcess(doc, ref3);
|
|
2407
|
+
const id = proc.getAttribute("id");
|
|
2408
|
+
const callers = byTag(doc, "callActivity").filter((c) => readProps(c).activityType === "ProcessCallElement" && readProps(c).processId === id);
|
|
2409
|
+
if (callers.length)
|
|
2410
|
+
throw new Error(`${id} is called by ${callers.map((c) => `${c.getAttribute("id")} "${c.getAttribute("name")}"`).join(", ")}; remove those calls first`);
|
|
2411
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2412
|
+
const collect = (el) => {
|
|
2413
|
+
const x = el.getAttribute("id");
|
|
2414
|
+
if (x)
|
|
2415
|
+
ids.add(x);
|
|
2416
|
+
for (const c of children(el))
|
|
2417
|
+
collect(c);
|
|
2418
|
+
};
|
|
2419
|
+
collect(proc);
|
|
2420
|
+
const adapter = byTag(doc, "messageFlow").find((m) => ids.has(m.getAttribute("sourceRef")) || ids.has(m.getAttribute("targetRef")));
|
|
2421
|
+
if (adapter)
|
|
2422
|
+
throw new Error(`a step in ${id} has an adapter (${adapter.getAttribute("id")}); remove it first (remove_adapter)`);
|
|
2423
|
+
const participant = children(byTag(doc, "collaboration")[0], "participant").find((p) => p.getAttribute("processRef") === id);
|
|
2424
|
+
if (participant)
|
|
2425
|
+
ids.add(participant.getAttribute("id"));
|
|
2426
|
+
for (const di of [...byTag(doc, "BPMNShape", NS.bpmndi), ...byTag(doc, "BPMNEdge", NS.bpmndi)])
|
|
2427
|
+
if (ids.has(di.getAttribute("bpmnElement")))
|
|
2428
|
+
removeEl(di);
|
|
2429
|
+
if (participant)
|
|
2430
|
+
removeEl(participant);
|
|
2431
|
+
removeEl(proc);
|
|
2432
|
+
return `removed local integration process ${id} "${proc.getAttribute("name")}" with its content`;
|
|
2433
|
+
}
|
|
2434
|
+
function setLoop(el, { condition: condition2, max = 100 }) {
|
|
2435
|
+
const doc = el.ownerDocument;
|
|
2436
|
+
for (const old of children(el, "standardLoopCharacteristics"))
|
|
2437
|
+
removeEl(old);
|
|
2438
|
+
const stamp2 = Date.now();
|
|
2439
|
+
const id = `StandardLoopCharacteristics_${stamp2}`;
|
|
2440
|
+
const loop = elementNS(doc, "bpmn2:standardLoopCharacteristics", { id, loopMaximum: String(max) });
|
|
2441
|
+
loop.appendChild(elementNS(doc, "bpmn2:loopCondition", { id: `FormalExpression_${stamp2}`, "xsi:type": "bpmn2:tFormalExpression" }, condition2));
|
|
2442
|
+
insertAfterEl(el, loop, Array.from(el.childNodes).filter(isEl3).at(-1));
|
|
2443
|
+
return id;
|
|
2444
|
+
}
|
|
2445
|
+
function setStart(doc, { process: process2, kind, schedule = "once", parameter }) {
|
|
2446
|
+
const proc = findProcess(doc, process2);
|
|
2447
|
+
if (readProps(proc).processType === "directCall")
|
|
2448
|
+
throw new Error(`${proc.getAttribute("id")} is a local integration process: it is started by a Process Call`);
|
|
2449
|
+
const starts = children(proc, "startEvent");
|
|
2450
|
+
if (starts.length !== 1)
|
|
2451
|
+
throw new Error(`${proc.getAttribute("id")} must have exactly one start event (has ${starts.length})`);
|
|
2452
|
+
const start = starts[0];
|
|
2453
|
+
const id = start.getAttribute("id");
|
|
2454
|
+
const index = idIndex(doc);
|
|
2455
|
+
const docEl = start.ownerDocument;
|
|
2456
|
+
const timer = children(start, "timerEventDefinition")[0];
|
|
2457
|
+
if (kind === "timer") {
|
|
2458
|
+
const sender = byTag(doc, "messageFlow").find((m) => m.getAttribute("targetRef") === id);
|
|
2459
|
+
if (sender)
|
|
2460
|
+
throw new Error(`${id} has a sender adapter (${sender.getAttribute("id")} "${sender.getAttribute("name")}"); a scheduled iFlow has none: remove it first (remove_adapter)`);
|
|
2461
|
+
const table = scheduleTable(schedule);
|
|
2462
|
+
const existing = timer ? readProps(timer).scheduleKey?.match(/^\{\{([^{}]+)\}\}$/)?.[1] : void 0;
|
|
2463
|
+
const name = parameter ?? existing ?? "timer_schedule";
|
|
2464
|
+
if (/[{}]/.test(name) || !name.trim())
|
|
2465
|
+
throw new Error(`"${name}" is not a usable parameter name`);
|
|
2466
|
+
const what = schedule === "once" ? "runs once after deploy" : "every" in schedule ? `every ${schedule.every} ${schedule.unit}` : `cron ${schedule.cron}`;
|
|
2467
|
+
const where = schedule !== "once" && schedule.timeZone ? ` (${schedule.timeZone})` : schedule === "once" ? "" : " (UTC)";
|
|
2468
|
+
if (timer) {
|
|
2469
|
+
setProperties(timer, { scheduleKey: `{{${name}}}` });
|
|
2470
|
+
return { message: `${id}: schedule ${what}${where}, parameter ${name}`, parameter: { name, table } };
|
|
2471
|
+
}
|
|
2472
|
+
for (const c of [...children(start, "extensionElements"), ...children(start, "messageEventDefinition")])
|
|
2473
|
+
removeEl(c);
|
|
2474
|
+
const def = elementNS(docEl, "bpmn2:timerEventDefinition", { id: newId(index, "TimerEventDefinition") });
|
|
2475
|
+
withProps(docEl, def, { scheduleKey: `{{${name}}}`, componentVersion: "1.4", cmdVariantUri: "ctype::FlowstepVariant/cname::intermediatetimer/version::1.4.0", activityType: "StartTimerEvent" });
|
|
2476
|
+
insertAfterEl(start, def, Array.from(start.childNodes).filter(isEl3).at(-1));
|
|
2477
|
+
return { message: `${id}: timer start, ${what}${where}, externalized as parameter ${name}`, parameter: { name, table } };
|
|
2478
|
+
}
|
|
2479
|
+
if (!timer)
|
|
2480
|
+
return { message: `${id} is a message start already` };
|
|
2481
|
+
removeEl(timer);
|
|
2482
|
+
withProps(docEl, start, { cmdVariantUri: "ctype::FlowstepVariant/cname::MessageStartEvent/version::1.0", componentVersion: "1.0" });
|
|
2483
|
+
const ext = children(start, "extensionElements")[0];
|
|
2484
|
+
const inner = indentBefore(children(start, "outgoing")[0] ?? start);
|
|
2485
|
+
start.insertBefore(docEl.createTextNode(inner), ext);
|
|
2486
|
+
prettify(ext, inner.replace(/^\n/, ""));
|
|
2487
|
+
insertAfterEl(start, elementNS(docEl, "bpmn2:messageEventDefinition"), Array.from(start.childNodes).filter(isEl3).at(-1));
|
|
2488
|
+
return { message: `${id}: message start again; connect a sender with add_adapter (the schedule parameter stays in parameters.prop)` };
|
|
2489
|
+
}
|
|
2176
2490
|
function findProcess(doc, ref3) {
|
|
2177
2491
|
const processes = byTag(doc, "process");
|
|
2178
2492
|
if (ref3) {
|
|
@@ -2377,11 +2691,129 @@ var isBranching = (doc, flowId) => {
|
|
|
2377
2691
|
const src = f?.getAttribute("sourceRef");
|
|
2378
2692
|
return !!src && byTag(doc, "sequenceFlow").filter((x) => x.getAttribute("sourceRef") === src).length > 1;
|
|
2379
2693
|
};
|
|
2694
|
+
var poolShapeOf = (doc, proc) => {
|
|
2695
|
+
const id = children(byTag(doc, "collaboration")[0], "participant").find((p) => p.getAttribute("processRef") === proc.getAttribute("id"))?.getAttribute("id");
|
|
2696
|
+
return id ? shapeOf(doc, id) : void 0;
|
|
2697
|
+
};
|
|
2698
|
+
var procName = (proc) => `"${proc.getAttribute("name") ?? proc.getAttribute("id")}"`;
|
|
2699
|
+
var processesInOrder = (doc) => {
|
|
2700
|
+
const all = byTag(doc, "process");
|
|
2701
|
+
const local = (p) => readProps(p).processType === "directCall";
|
|
2702
|
+
return [...all.filter((p) => !local(p)), ...all.filter(local)];
|
|
2703
|
+
};
|
|
2704
|
+
function moveProcess(doc, proc, dx, dy) {
|
|
2705
|
+
if (!dx && !dy)
|
|
2706
|
+
return;
|
|
2707
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2708
|
+
const collect = (el) => {
|
|
2709
|
+
const id = el.getAttribute("id");
|
|
2710
|
+
if (id)
|
|
2711
|
+
ids.add(id);
|
|
2712
|
+
for (const c of children(el))
|
|
2713
|
+
collect(c);
|
|
2714
|
+
};
|
|
2715
|
+
collect(proc);
|
|
2716
|
+
const pool = poolShapeOf(doc, proc);
|
|
2717
|
+
for (const sh of byTag(doc, "BPMNShape", NS.bpmndi)) {
|
|
2718
|
+
if (sh !== pool && !ids.has(sh.getAttribute("bpmnElement")))
|
|
2719
|
+
continue;
|
|
2720
|
+
const b = bounds(sh);
|
|
2721
|
+
b.el.setAttribute("x", fmt(b.x + dx));
|
|
2722
|
+
b.el.setAttribute("y", fmt(b.y + dy));
|
|
2723
|
+
}
|
|
2724
|
+
for (const e of byTag(doc, "BPMNEdge", NS.bpmndi)) {
|
|
2725
|
+
if (!ids.has(e.getAttribute("bpmnElement")))
|
|
2726
|
+
continue;
|
|
2727
|
+
for (const wp of children(e, "waypoint", NS.di)) {
|
|
2728
|
+
wp.setAttribute("x", fmt(Number(wp.getAttribute("x")) + dx));
|
|
2729
|
+
wp.setAttribute("y", fmt(Number(wp.getAttribute("y")) + dy));
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2380
2733
|
function layoutStraight(doc) {
|
|
2381
|
-
const
|
|
2382
|
-
if (
|
|
2383
|
-
throw new Error(
|
|
2384
|
-
const
|
|
2734
|
+
const procs = processesInOrder(doc);
|
|
2735
|
+
if (!procs.length)
|
|
2736
|
+
throw new Error("the iFlow has no integration process");
|
|
2737
|
+
const lines = [];
|
|
2738
|
+
let below;
|
|
2739
|
+
let left;
|
|
2740
|
+
for (const proc of procs) {
|
|
2741
|
+
const pool = poolShapeOf(doc, proc);
|
|
2742
|
+
if (!pool)
|
|
2743
|
+
throw new Error(`process ${procName(proc)} has no pool in the diagram`);
|
|
2744
|
+
if (below !== void 0) {
|
|
2745
|
+
const b2 = bounds(pool);
|
|
2746
|
+
moveProcess(doc, proc, left - b2.x, below + 60 - b2.y);
|
|
2747
|
+
}
|
|
2748
|
+
const msg = layoutProcess(doc, proc);
|
|
2749
|
+
lines.push(procs.length > 1 ? `${procName(proc)}: ${msg}` : msg);
|
|
2750
|
+
const b = bounds(pool);
|
|
2751
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2752
|
+
const collect = (el) => {
|
|
2753
|
+
const id = el.getAttribute("id");
|
|
2754
|
+
if (id)
|
|
2755
|
+
ids.add(id);
|
|
2756
|
+
for (const c of children(el))
|
|
2757
|
+
collect(c);
|
|
2758
|
+
};
|
|
2759
|
+
collect(proc);
|
|
2760
|
+
const boxes = byTag(doc, "messageFlow").flatMap((m) => [m.getAttribute("sourceRef"), m.getAttribute("targetRef")].filter((x) => !ids.has(x)).filter(() => ids.has(m.getAttribute("sourceRef")) || ids.has(m.getAttribute("targetRef"))));
|
|
2761
|
+
const boxBottoms = boxes.map((id) => shapeOf(doc, id)).filter((x) => !!x).map(bounds).map((x) => x.y + x.h);
|
|
2762
|
+
below = Math.max(b.y + b.h, ...boxBottoms);
|
|
2763
|
+
left ??= b.x;
|
|
2764
|
+
}
|
|
2765
|
+
routeAdapters(doc);
|
|
2766
|
+
keepOnCanvas(doc);
|
|
2767
|
+
return lines.join("; ");
|
|
2768
|
+
}
|
|
2769
|
+
function layoutAround(doc, elementId) {
|
|
2770
|
+
let proc = idIndex(doc).get(elementId) ?? null;
|
|
2771
|
+
while (proc && proc.localName !== "process")
|
|
2772
|
+
proc = proc.parentNode;
|
|
2773
|
+
if (!proc)
|
|
2774
|
+
throw new Error(`${elementId} is not in a process`);
|
|
2775
|
+
const pool = poolShapeOf(doc, proc);
|
|
2776
|
+
if (!pool)
|
|
2777
|
+
throw new Error(`process ${procName(proc)} has no pool in the diagram`);
|
|
2778
|
+
const before = bounds(pool);
|
|
2779
|
+
const mine = /* @__PURE__ */ new Set();
|
|
2780
|
+
const collect = (el) => {
|
|
2781
|
+
const id = el.getAttribute("id");
|
|
2782
|
+
if (id)
|
|
2783
|
+
mine.add(id);
|
|
2784
|
+
for (const c of children(el))
|
|
2785
|
+
collect(c);
|
|
2786
|
+
};
|
|
2787
|
+
collect(proc);
|
|
2788
|
+
for (const mf of byTag(doc, "messageFlow")) {
|
|
2789
|
+
const [a, b] = [mf.getAttribute("sourceRef"), mf.getAttribute("targetRef")];
|
|
2790
|
+
if (mine.has(a) || mine.has(b)) {
|
|
2791
|
+
mine.add(a);
|
|
2792
|
+
mine.add(b);
|
|
2793
|
+
mine.add(mf.getAttribute("id"));
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
const msg = layoutProcess(doc, proc);
|
|
2797
|
+
const grow = bounds(pool).y + bounds(pool).h - (before.y + before.h);
|
|
2798
|
+
if (grow > 0 && byTag(doc, "process").length > 1) {
|
|
2799
|
+
for (const sh of byTag(doc, "BPMNShape", NS.bpmndi)) {
|
|
2800
|
+
const b = bounds(sh);
|
|
2801
|
+
if (sh !== pool && !mine.has(sh.getAttribute("bpmnElement")) && b.y >= before.y + before.h)
|
|
2802
|
+
b.el.setAttribute("y", fmt(b.y + grow));
|
|
2803
|
+
}
|
|
2804
|
+
for (const e of byTag(doc, "BPMNEdge", NS.bpmndi)) {
|
|
2805
|
+
if (mine.has(e.getAttribute("bpmnElement")))
|
|
2806
|
+
continue;
|
|
2807
|
+
for (const wp of children(e, "waypoint", NS.di))
|
|
2808
|
+
if (Number(wp.getAttribute("y")) >= before.y + before.h)
|
|
2809
|
+
wp.setAttribute("y", fmt(Number(wp.getAttribute("y")) + grow));
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
routeAdapters(doc);
|
|
2813
|
+
keepOnCanvas(doc);
|
|
2814
|
+
return msg;
|
|
2815
|
+
}
|
|
2816
|
+
function layoutProcess(doc, proc) {
|
|
2385
2817
|
const subs = children(proc, "subProcess");
|
|
2386
2818
|
const other = subs.find((x) => !isExceptionSub(x));
|
|
2387
2819
|
if (other)
|
|
@@ -2390,7 +2822,7 @@ function layoutStraight(doc) {
|
|
|
2390
2822
|
const byId = new Map(nodes.map((n) => [n.getAttribute("id"), n]));
|
|
2391
2823
|
const starts = nodes.filter((n) => n.localName === "startEvent");
|
|
2392
2824
|
if (starts.length !== 1)
|
|
2393
|
-
throw new Error(`layout needs exactly one start event (has ${starts.length})`);
|
|
2825
|
+
throw new Error(`layout needs exactly one start event in process ${procName(proc)} (has ${starts.length}): use the designer`);
|
|
2394
2826
|
const flows = children(proc, "sequenceFlow");
|
|
2395
2827
|
const out = (id) => {
|
|
2396
2828
|
const order = children(byId.get(id), "outgoing").map((o) => o.textContent?.trim());
|
|
@@ -2457,10 +2889,9 @@ function layoutStraight(doc) {
|
|
|
2457
2889
|
row2.set(id, used.indexOf(x));
|
|
2458
2890
|
rows = used.length - 1;
|
|
2459
2891
|
const collab = byTag(doc, "collaboration")[0];
|
|
2460
|
-
const
|
|
2461
|
-
const poolShape = poolId ? shapeOf(doc, poolId) : void 0;
|
|
2892
|
+
const poolShape = poolShapeOf(doc, proc);
|
|
2462
2893
|
if (!poolShape)
|
|
2463
|
-
throw new Error(
|
|
2894
|
+
throw new Error(`process ${procName(proc)} has no diagram shape`);
|
|
2464
2895
|
const shapeBounds = new Map(nodes.map((n) => {
|
|
2465
2896
|
const sh = shapeOf(doc, n.getAttribute("id"));
|
|
2466
2897
|
if (!sh)
|
|
@@ -2554,8 +2985,6 @@ function layoutStraight(doc) {
|
|
|
2554
2985
|
placeBox(doc, box, { ...hit[1] }, hit[0].localName === "startEvent" && hit[0].parentNode === proc);
|
|
2555
2986
|
boxes++;
|
|
2556
2987
|
}
|
|
2557
|
-
routeAdapters(doc);
|
|
2558
|
-
keepOnCanvas(doc);
|
|
2559
2988
|
const where = rows ? `in ${cols} columns and ${rows + 1} rows` : "in one row";
|
|
2560
2989
|
return `laid out ${nodes.length} elements ${where}${inner ? ` and ${inner.chain.length} in the exception subprocess below` : ""} (pool width ${right + GAP - pool.x}), ${boxes} Sender/Receiver box(es) placed`;
|
|
2561
2990
|
}
|
|
@@ -2617,7 +3046,7 @@ function expressionSettings(cname, expression, p) {
|
|
|
2617
3046
|
return expression === void 0 ? {} : { wrapContent: expression };
|
|
2618
3047
|
}
|
|
2619
3048
|
var needsMappingFile = (cname, p) => cname === "XSLTMapping" && p.mappingSource === "mappingSrcIflow" || cname === "MessageMapping" && p.mappingReference === "static";
|
|
2620
|
-
function applyEdits(doc, ops, catalog) {
|
|
3049
|
+
function applyEdits(doc, ops, catalog, effects = { params: [] }) {
|
|
2621
3050
|
const log = [];
|
|
2622
3051
|
ops.forEach((op, i) => {
|
|
2623
3052
|
const where = `operation ${i + 1} (${op.op})`;
|
|
@@ -2654,6 +3083,23 @@ function applyEdits(doc, ops, catalog) {
|
|
|
2654
3083
|
throw new Error(`a script from collection ${op.scriptCollection} needs settings.script: its file name in the collection, e.g. "Utils.groovy"`);
|
|
2655
3084
|
properties.scriptBundleId = op.scriptCollection;
|
|
2656
3085
|
}
|
|
3086
|
+
if (cname === "NonLoopingProcess" || cname === "LoopingProcess") {
|
|
3087
|
+
if (!op.process)
|
|
3088
|
+
throw new Error(`${entry.label} needs "process": the local integration process to call (add one with add_local_process)`);
|
|
3089
|
+
const target = findLocalProcess(doc, op.process);
|
|
3090
|
+
let own = findElement(doc, after);
|
|
3091
|
+
while (own && own.localName !== "process")
|
|
3092
|
+
own = own.parentNode;
|
|
3093
|
+
if (own === target)
|
|
3094
|
+
throw new Error(`a step in ${target.getAttribute("id")} cannot call its own process`);
|
|
3095
|
+
properties.processId = target.getAttribute("id");
|
|
3096
|
+
if (cname === "LoopingProcess") {
|
|
3097
|
+
if (!op.loop?.condition)
|
|
3098
|
+
throw new Error(`${entry.label} needs "loop": { condition (repeat while true), max (default 100) }`);
|
|
3099
|
+
properties.expressionType = op.loop.expressionType === "XML" ? "XML" : "Non XML";
|
|
3100
|
+
}
|
|
3101
|
+
} else if (op.process || op.loop)
|
|
3102
|
+
throw new Error('"process" and "loop" apply to Process Call and Looping Process Call steps only');
|
|
2657
3103
|
if (cname === "GroovyScript" && version.startsWith("2.") && properties.script && !op.scriptCollection && !properties.script.startsWith("v2/")) {
|
|
2658
3104
|
throw new Error(`a Groovy Script ${version} step takes its script from script/v2/: use settings.script "v2/${properties.script}" (and put the file there)`);
|
|
2659
3105
|
}
|
|
@@ -2677,6 +3123,8 @@ function applyEdits(doc, ops, catalog) {
|
|
|
2677
3123
|
...onRoute ? { flowId: onRoute.getAttribute("id") } : {}
|
|
2678
3124
|
});
|
|
2679
3125
|
const el = findElement(doc, stepId);
|
|
3126
|
+
if (op.loop && cname === "LoopingProcess")
|
|
3127
|
+
setProperties(el, { loopId: setLoop(el, { condition: op.loop.condition, ...op.loop.max ? { max: op.loop.max } : {} }) });
|
|
2680
3128
|
if (op.headers?.length)
|
|
2681
3129
|
editTable(el, "headerTable", { set: op.headers });
|
|
2682
3130
|
if (op.properties?.length)
|
|
@@ -2684,7 +3132,7 @@ function applyEdits(doc, ops, catalog) {
|
|
|
2684
3132
|
log.push(`inserted ${stepId} "${op.name ?? entry.label}" (${entry.label} ${version}, ${reason}) ${onRoute ? `at the start of route ${onRoute.getAttribute("id")}` : `after ${after}`}`);
|
|
2685
3133
|
if (onRoute) {
|
|
2686
3134
|
try {
|
|
2687
|
-
log.push(
|
|
3135
|
+
log.push(layoutAround(doc, stepId));
|
|
2688
3136
|
} catch (e) {
|
|
2689
3137
|
log.push(`not laid out (${e.message}); the new step sits next to ${after}`);
|
|
2690
3138
|
}
|
|
@@ -2734,6 +3182,22 @@ function applyEdits(doc, ops, catalog) {
|
|
|
2734
3182
|
log.push(`removed exception subprocess ${removeExceptionSubprocess(doc, op.process)} with its content`);
|
|
2735
3183
|
break;
|
|
2736
3184
|
}
|
|
3185
|
+
case "set_start": {
|
|
3186
|
+
const r = setStart(doc, { ...op.process ? { process: op.process } : {}, kind: op.kind, ...op.schedule ? { schedule: op.schedule } : {}, ...op.parameter ? { parameter: op.parameter } : {} });
|
|
3187
|
+
if (r.parameter)
|
|
3188
|
+
effects.params.push({ name: r.parameter.name, value: r.parameter.table, type: "custom:schedule", description: "Schedule of the timer start" });
|
|
3189
|
+
log.push(r.message);
|
|
3190
|
+
break;
|
|
3191
|
+
}
|
|
3192
|
+
case "add_local_process": {
|
|
3193
|
+
const r = addLocalProcess(doc, { ...op.name ? { name: op.name } : {}, processProps: catalogProps(catalog, "LocalIntegrationProcess", doc) });
|
|
3194
|
+
log.push(`added local integration process ${r.processId} "${r.name}": ${r.startId} -> ${r.endId}; insert steps after ${r.startId}, call it with insert_step Process Call "process"`);
|
|
3195
|
+
break;
|
|
3196
|
+
}
|
|
3197
|
+
case "remove_local_process": {
|
|
3198
|
+
log.push(removeLocalProcess(doc, op.process));
|
|
3199
|
+
break;
|
|
3200
|
+
}
|
|
2737
3201
|
case "add_router": {
|
|
2738
3202
|
const after = findElement(doc, op.after).getAttribute("id");
|
|
2739
3203
|
const r = addRouter(doc, after, {
|
|
@@ -2744,7 +3208,7 @@ function applyEdits(doc, ops, catalog) {
|
|
|
2744
3208
|
endProps: catalogProps(catalog, "MessageEndEvent", doc)
|
|
2745
3209
|
});
|
|
2746
3210
|
log.push(`added router ${r.gatewayId} after ${after} with routes ${r.routeIds.join(", ")} (default ${r.routeIds[0]})${r.endIds.length ? `, End events ${r.endIds.join(", ")}` : ""}; insert steps on a route with insert_step "route"`);
|
|
2747
|
-
log.push(
|
|
3211
|
+
log.push(layoutAround(doc, r.gatewayId));
|
|
2748
3212
|
break;
|
|
2749
3213
|
}
|
|
2750
3214
|
case "add_multicast": {
|
|
@@ -2760,7 +3224,7 @@ function applyEdits(doc, ops, catalog) {
|
|
|
2760
3224
|
endProps: catalogProps(catalog, "MessageEndEvent", doc)
|
|
2761
3225
|
});
|
|
2762
3226
|
log.push(`added ${sequential ? "sequential" : "parallel"} multicast ${r.gatewayId} after ${after} with branches ${r.branchIds.join(", ")}${r.joinId ? `, joined in ${r.joinId}` : ""}${r.endIds.length ? `, End events ${r.endIds.join(", ")}` : ""}; insert steps on a branch with insert_step "route"`);
|
|
2763
|
-
log.push(
|
|
3227
|
+
log.push(layoutAround(doc, r.gatewayId));
|
|
2764
3228
|
break;
|
|
2765
3229
|
}
|
|
2766
3230
|
case "set_route": {
|
|
@@ -2768,8 +3232,12 @@ function applyEdits(doc, ops, catalog) {
|
|
|
2768
3232
|
break;
|
|
2769
3233
|
}
|
|
2770
3234
|
case "remove_gateway": {
|
|
3235
|
+
let proc = findElement(doc, op.gateway);
|
|
3236
|
+
while (proc && proc.localName !== "process")
|
|
3237
|
+
proc = proc.parentNode;
|
|
2771
3238
|
log.push(removeGateway(doc, op.gateway));
|
|
2772
|
-
|
|
3239
|
+
if (proc)
|
|
3240
|
+
log.push(layoutAround(doc, proc.getAttribute("id")));
|
|
2773
3241
|
break;
|
|
2774
3242
|
}
|
|
2775
3243
|
case "layout": {
|
|
@@ -3539,7 +4007,7 @@ function readChangelog(path, max = 5) {
|
|
|
3539
4007
|
// ../core/dist/params.js
|
|
3540
4008
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3541
4009
|
import { dirname as dirname5, join as join5 } from "node:path";
|
|
3542
|
-
var PARAM_TYPES = ["xsd:string", "xsd:integer", "xsd:long", "xsd:boolean", "xsd:date", "xsd:time", "xsd:dateTime"];
|
|
4010
|
+
var PARAM_TYPES = ["xsd:string", "xsd:integer", "xsd:long", "xsd:boolean", "xsd:date", "xsd:time", "xsd:dateTime", "custom:schedule"];
|
|
3543
4011
|
var rx = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3544
4012
|
var blockOf = (name) => new RegExp(`<parameter>(?:(?!</parameter>)[\\s\\S])*?<name>${rx(escapeXml(name))}</name>[\\s\\S]*?</parameter>`);
|
|
3545
4013
|
var unescapeXml = (s) => s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
@@ -4526,6 +4994,12 @@ var operation = z4.discriminatedUnion("op", [
|
|
|
4526
4994
|
version: z4.string().optional().describe("Component version; default: the version this iFlow already uses, else the latest"),
|
|
4527
4995
|
name: z4.string().optional().describe("Step name; default: the catalog label"),
|
|
4528
4996
|
settings: kv.optional().describe('Settings to change from the catalog defaults; script steps need "script" (file under script/, v2/<name>.groovy for 2.x; with scriptCollection the file name in the collection)'),
|
|
4997
|
+
process: z4.string().optional().describe("Process Call / Looping Process Call: the local integration process to call (id or name; add_local_process creates one)"),
|
|
4998
|
+
loop: z4.object({
|
|
4999
|
+
condition: z4.string().describe("Repeat while this holds, e.g. ${property.hasMore} = 'true'"),
|
|
5000
|
+
max: z4.number().int().positive().optional().describe("At most this many times (default 100)"),
|
|
5001
|
+
expressionType: z4.enum(["NonXML", "XML"]).optional().describe("NonXML: Camel Simple (default); XML: XPath")
|
|
5002
|
+
}).optional().describe("Looping Process Call: the loop"),
|
|
4529
5003
|
scriptCollection: z4.string().optional().describe('Groovy/JavaScript step: Id of a script collection of the same package the script comes from (settings.script = its file name, e.g. "Utils.groovy"); the manifest requirement is written'),
|
|
4530
5004
|
mapping: z4.string().optional().describe('XSLT Mapping / Message Mapping: the file under src/main/resources, e.g. "mapping/orders.xsl" (a bare name means mapping/); sets mappinguri, mappingpath and mappingname as the designer does'),
|
|
4531
5005
|
expression: z4.string().optional().describe(`Filter: the XPath of the nodes to keep, e.g. "/Orders/Order[region='EU']" (stored as wrapContent; xpathType is only the value type: Nodelist, Node, Boolean, Integer, String)`),
|
|
@@ -4549,6 +5023,22 @@ var operation = z4.discriminatedUnion("op", [
|
|
|
4549
5023
|
name: z4.string().optional().describe('Default "Exception Subprocess 1"'),
|
|
4550
5024
|
end: z4.enum(["error", "message"]).default("error").describe("error: Error Start -> Error End, the message fails and the sender gets the error; message: Error Start -> End, the subprocess sends the reply (set it with steps)")
|
|
4551
5025
|
}).describe("Add an exception subprocess (one per process), drawn below the main flow; insert steps into it with insert_step after its Error Start"),
|
|
5026
|
+
z4.object({ op: z4.literal("add_local_process"), name: z4.string().optional().describe('Default "Local Integration Process <n>"') }).describe('Add a local integration process (a pool below the others, Start -> End); insert steps after its start, call it with a Process Call step ("process")'),
|
|
5027
|
+
z4.object({
|
|
5028
|
+
op: z4.literal("set_start"),
|
|
5029
|
+
process: z4.string().optional().describe("Integration process (id or name); default: the main one"),
|
|
5030
|
+
kind: z4.enum(["timer", "message"]).describe("timer: a scheduled iFlow (no sender adapter; remove it first); message: started by a sender adapter again"),
|
|
5031
|
+
schedule: z4.union([z4.literal("once"), z4.object({
|
|
5032
|
+
every: z4.number().int().positive().optional().describe("Simple schedule: every n minutes (1-59) or hours (1-23)"),
|
|
5033
|
+
unit: z4.enum(["minutes", "hours"]).optional(),
|
|
5034
|
+
cron: z4.string().optional().describe('Advanced schedule instead: Quartz cron "second minute hour day-of-month month day-of-week [year]", e.g. "0 15 8 ? * MON-FRI" (weekdays 08:15)'),
|
|
5035
|
+
from: z4.string().optional().describe("Not before, local time in timeZone: 2026-11-05T08:00:00"),
|
|
5036
|
+
until: z4.string().optional().describe("Not after"),
|
|
5037
|
+
timeZone: z4.string().optional().describe("IANA time zone id, e.g. Europe/Warsaw; default UTC")
|
|
5038
|
+
})]).optional().describe('timer: "once" (run once after deploy, the default), a simple schedule {every, unit} or an advanced one {cron}; always externalized into parameters.prop'),
|
|
5039
|
+
parameter: z4.string().optional().describe("timer: name of the schedule parameter (default: the existing one, else timer_schedule)")
|
|
5040
|
+
}).describe("Make the start event a timer start (scheduled iFlow) or a message start"),
|
|
5041
|
+
z4.object({ op: z4.literal("remove_local_process"), process: z4.string().describe("Id or name; refused while a Process Call calls it") }),
|
|
4552
5042
|
z4.object({ op: z4.literal("remove_exception_subprocess"), process: z4.string().optional() }).describe("Remove the exception subprocess with all steps in it (they must not have adapters)"),
|
|
4553
5043
|
z4.object({
|
|
4554
5044
|
op: z4.literal("add_router"),
|
|
@@ -4614,7 +5104,7 @@ var clip2 = (s, max) => s.length > max ? `${s.slice(0, max)}
|
|
|
4614
5104
|
function registerEditCheckTools(server2) {
|
|
4615
5105
|
server2.registerTool("iflow_edit", {
|
|
4616
5106
|
title: "Edit an iFlow",
|
|
4617
|
-
description: `Apply semantic edits to a local iFlow in one batch: insert a step from the catalog (with settings, headers, properties), remove a step (its neighbours are reconnected), add or remove an adapter (with its Sender/Receiver box), add or remove an exception subprocess (insert steps into it after its Error Start), add a router or a parallel/sequential multicast (with Join) and put steps on its routes (insert_step "route"), change or remove routes and gateways, lay out the process
|
|
5107
|
+
description: `Apply semantic edits to a local iFlow in one batch: insert a step from the catalog (with settings, headers, properties), remove a step (its neighbours are reconnected), add or remove an adapter (with its Sender/Receiver box), add or remove an exception subprocess (insert steps into it after its Error Start), add a router or a parallel/sequential multicast (with Join) and put steps on its routes (insert_step "route"), change or remove routes and gateways, add or remove local integration processes and call them (Process Call / Looping Process Call with "process", "loop"), make the start a timer or a message start (set_start), lay out the iFlow (one pool per process, branches in rows), change settings of a step, adapter or the iFlow, rename, and add/change/remove Content Modifier headers and properties. All operations succeed or nothing is written; edits that would introduce structural errors are refused. New elements are formatted like SAP's designer writes them and the diagram is laid out. Returns what changed (as iflow_diff) and the new step ids. Script and mapping files are not created: write them yourself (missing ones are listed under "filesToCreate").`,
|
|
4618
5108
|
inputSchema: {
|
|
4619
5109
|
iflow: iflowArg2,
|
|
4620
5110
|
operations: z4.array(operation).min(1),
|
|
@@ -4627,7 +5117,10 @@ function registerEditCheckTools(server2) {
|
|
|
4627
5117
|
const { located, doc } = localIflow(ctx, iflow);
|
|
4628
5118
|
const before = parseIflw(serializeIflw(doc));
|
|
4629
5119
|
const known = new Set(validate(before, located).map((i) => `${i.code} ${i.msg}`));
|
|
4630
|
-
const
|
|
5120
|
+
const effects = { params: [] };
|
|
5121
|
+
const log = applyEdits(doc, operations, catalogOf(ctx.config), effects);
|
|
5122
|
+
if (effects.params.length && !located.dir)
|
|
5123
|
+
throw new Error("a timer schedule is externalized into parameters.prop: edit the iFlow folder, not a loose .iflw");
|
|
4631
5124
|
const issues = validate(doc, located).filter((i) => !known.has(`${i.code} ${i.msg}`));
|
|
4632
5125
|
const filesToCreate = issues.filter((i) => i.code === "MISSING_RESOURCE").map((i) => i.msg.replace(/^.* -> /, ""));
|
|
4633
5126
|
const blocking = issues.filter((i) => i.severity === "error" && i.code !== "MISSING_RESOURCE" && !DEPLOY_CHECKS.has(i.code));
|
|
@@ -4638,6 +5131,7 @@ ${blocking.map((i) => ` ${i.code} ${i.msg}`).join("\n")}`);
|
|
|
4638
5131
|
}
|
|
4639
5132
|
if (!dryRun)
|
|
4640
5133
|
writeFileSync6(located.iflwPath, serializeIflw(doc));
|
|
5134
|
+
const paramChanges = !dryRun && effects.params.length ? setParams(located.dir, effects.params.map((p) => ({ name: p.name, value: p.value, type: p.type, description: p.description }))) : [];
|
|
4641
5135
|
const manifest = !dryRun && syncCollectionRequirements(located, doc);
|
|
4642
5136
|
const collections = collectionIssues(located, doc).map((i) => `${i.code} ${i.msg}`);
|
|
4643
5137
|
const changes = diffIflows(describe(before, { maxLen: 0 }), describe(doc, { maxLen: 0 }), { docA: before, docB: doc });
|
|
@@ -4649,6 +5143,7 @@ ${blocking.map((i) => ` ${i.code} ${i.msg}`).join("\n")}`);
|
|
|
4649
5143
|
filesToCreate,
|
|
4650
5144
|
...beforeDeploy.length ? { beforeDeploy } : {},
|
|
4651
5145
|
...manifest ? { manifest: "META-INF/MANIFEST.MF: Require-Capability updated for the script collections used" } : {},
|
|
5146
|
+
...paramChanges.length ? { parameters: paramChanges.map((c) => c.replace(/ = <row>.*$/, " = (schedule table)")) } : {},
|
|
4652
5147
|
warnings: [...issues.filter((i) => i.severity === "warning" && !DEPLOY_CHECKS.has(i.code)).map((i) => `${i.code} ${i.msg}`), ...collections]
|
|
4653
5148
|
});
|
|
4654
5149
|
} catch (e) {
|
|
@@ -5542,9 +6037,17 @@ function registerParamTools(server2) {
|
|
|
5542
6037
|
set: z8.array(z8.object({
|
|
5543
6038
|
name: z8.string(),
|
|
5544
6039
|
value: z8.string().optional(),
|
|
5545
|
-
type: z8.enum(["xsd:string", "xsd:integer", "xsd:long", "xsd:boolean", "xsd:date", "xsd:time", "xsd:dateTime"]).optional().describe("Default xsd:string (kept when changing)"),
|
|
6040
|
+
type: z8.enum(["xsd:string", "xsd:integer", "xsd:long", "xsd:boolean", "xsd:date", "xsd:time", "xsd:dateTime", "custom:schedule"]).optional().describe("Default xsd:string (kept when changing)"),
|
|
5546
6041
|
required: z8.boolean().optional(),
|
|
5547
|
-
description: z8.string().optional()
|
|
6042
|
+
description: z8.string().optional(),
|
|
6043
|
+
schedule: z8.union([z8.literal("once"), z8.object({
|
|
6044
|
+
every: z8.number().int().positive().optional(),
|
|
6045
|
+
unit: z8.enum(["minutes", "hours"]).optional(),
|
|
6046
|
+
cron: z8.string().optional(),
|
|
6047
|
+
from: z8.string().optional(),
|
|
6048
|
+
until: z8.string().optional(),
|
|
6049
|
+
timeZone: z8.string().optional()
|
|
6050
|
+
})]).optional().describe('For a timer schedule parameter (type custom:schedule) instead of value: "once", {every, unit} or {cron}, with from, until, timeZone')
|
|
5548
6051
|
})).default([]),
|
|
5549
6052
|
remove: z8.array(z8.string()).default([]).describe("Parameter names to remove (declaration, value and the designer's references)")
|
|
5550
6053
|
},
|
|
@@ -5555,7 +6058,8 @@ function registerParamTools(server2) {
|
|
|
5555
6058
|
const { located, doc } = localIflow(ctx, iflow);
|
|
5556
6059
|
if (!located.dir)
|
|
5557
6060
|
throw new Error(`${iflow}: parameters belong to an iFlow folder (src/main/resources), not a loose .iflw`);
|
|
5558
|
-
const
|
|
6061
|
+
const input = set.map(({ schedule, ...p }) => schedule !== void 0 ? { ...p, type: "custom:schedule", value: scheduleTable(checkSchedule(schedule)) } : p);
|
|
6062
|
+
const log = (input.length || remove.length ? setParams(located.dir, input, remove) : []).map((l) => l.replace(/ = <row>.*$/, " = (schedule table)"));
|
|
5559
6063
|
const used = uses(doc);
|
|
5560
6064
|
const params = readParams(located.dir).map((p) => ({ ...p, usedBy: used.get(p.name) ?? [] }));
|
|
5561
6065
|
const declared = new Set(params.map((p) => p.name));
|
|
@@ -5601,11 +6105,19 @@ function registerParamTools(server2) {
|
|
|
5601
6105
|
description: `Set configured values of an iFlow's externalized parameters on a development tenant (allowed packages only), like "Configure" in the designer: no new version; the running iFlow uses them after the next deploy. Asks for confirmation when "config_set" is in "confirm". When the iFlow is pulled, its local parameters.prop is updated too (unless it has local changes, which a later cpi_push would upload over these values).`,
|
|
5602
6106
|
inputSchema: {
|
|
5603
6107
|
iflow: z8.string().describe("iFlow Id"),
|
|
5604
|
-
values: z8.record(z8.string(), z8.string()).describe("Parameter name -> value"),
|
|
6108
|
+
values: z8.record(z8.string(), z8.string()).default({}).describe("Parameter name -> value"),
|
|
6109
|
+
schedules: z8.record(z8.string(), z8.object({
|
|
6110
|
+
every: z8.number().int().positive().optional(),
|
|
6111
|
+
unit: z8.enum(["minutes", "hours"]).optional(),
|
|
6112
|
+
cron: z8.string().optional(),
|
|
6113
|
+
from: z8.string().optional(),
|
|
6114
|
+
until: z8.string().optional(),
|
|
6115
|
+
timeZone: z8.string().optional()
|
|
6116
|
+
})).default({}).describe("Schedule parameter name (type custom:schedule) -> {every, unit} or {cron}, with from, until, timeZone"),
|
|
5605
6117
|
tenant: tenantArg5
|
|
5606
6118
|
},
|
|
5607
6119
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true }
|
|
5608
|
-
}, async ({ iflow, values, tenant: tenantName }) => {
|
|
6120
|
+
}, async ({ iflow, values: plain, schedules, tenant: tenantName }) => {
|
|
5609
6121
|
const ctx = await context(server2).catch((e) => e);
|
|
5610
6122
|
if (ctx instanceof Error)
|
|
5611
6123
|
return toolError(ctx);
|
|
@@ -5614,6 +6126,15 @@ function registerParamTools(server2) {
|
|
|
5614
6126
|
const info = await getIflow(api.client, iflow);
|
|
5615
6127
|
await assertMayChange(api.client, api.tenant, info.packageId);
|
|
5616
6128
|
const current = new Map((await getConfigurations(api.client, iflow)).map((v) => [v.key, v]));
|
|
6129
|
+
const values = { ...plain };
|
|
6130
|
+
for (const [k, sch] of Object.entries(schedules)) {
|
|
6131
|
+
if (current.get(k) && current.get(k).type !== "custom:schedule")
|
|
6132
|
+
throw new Error(`${k} is not a schedule parameter (${current.get(k).type})`);
|
|
6133
|
+
values[k] = scheduleConfigValue(checkSchedule(sch));
|
|
6134
|
+
}
|
|
6135
|
+
for (const k of Object.keys(plain))
|
|
6136
|
+
if (current.get(k)?.type === "custom:schedule")
|
|
6137
|
+
throw new Error(`${k} is a schedule: pass it in "schedules" as {every, unit} or {cron}`);
|
|
5617
6138
|
const keys = Object.keys(values);
|
|
5618
6139
|
if (!keys.length)
|
|
5619
6140
|
throw new Error("nothing to set: pass values");
|
|
@@ -5623,7 +6144,8 @@ function registerParamTools(server2) {
|
|
|
5623
6144
|
throw new Error(`${iflow} has no parameter "${k}"; parameters: ${[...current.keys()].filter((x) => !x.startsWith("SAP_")).join(", ") || "none"}`);
|
|
5624
6145
|
if (k.startsWith("SAP_"))
|
|
5625
6146
|
throw new Error(`${k} is set by SAP, not configured`);
|
|
5626
|
-
|
|
6147
|
+
if (c.type !== "custom:schedule")
|
|
6148
|
+
checkValue(k, c.type, values[k]);
|
|
5627
6149
|
}
|
|
5628
6150
|
await confirmAction(server2, ctx.config, "config_set", `Configure ${iflow} on tenant "${api.tenant.name}"?
|
|
5629
6151
|
` + keys.map((k) => `${k}: ${current.get(k).value} -> ${values[k]}`).join("\n"));
|
|
@@ -6077,7 +6599,7 @@ function textconvFile(path) {
|
|
|
6077
6599
|
}
|
|
6078
6600
|
|
|
6079
6601
|
// dist/version.js
|
|
6080
|
-
var VERSION = "0.9.0-beta.
|
|
6602
|
+
var VERSION = "0.9.0-beta.1";
|
|
6081
6603
|
|
|
6082
6604
|
// dist/index.js
|
|
6083
6605
|
if (process.argv[2] === "textconv") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cpi-saper-dev-mcp",
|
|
3
|
-
"version": "0.9.0-beta.
|
|
3
|
+
"version": "0.9.0-beta.1",
|
|
4
4
|
"description": "MCP server that lets an AI agent develop SAP Cloud Integration (CPI) content on development tenants: edit iFlows locally, check, push, deploy and test",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -49,6 +49,6 @@
|
|
|
49
49
|
"saxon-js": "2.7.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@cpi-saper-dev-mcp/core": "0.9.0-beta.
|
|
52
|
+
"@cpi-saper-dev-mcp/core": "0.9.0-beta.1"
|
|
53
53
|
}
|
|
54
54
|
}
|