@lexq/cli 0.1.30 → 0.1.32
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/CONTEXT.md +1 -1
- package/dist/index.js +104 -65
- package/dist/mcp/register.js +49 -44
- package/package.json +1 -1
- package/skills/lexq-simulation/SKILL.md +1 -1
package/CONTEXT.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -300,13 +300,14 @@ function registerGroupCommands(program) {
|
|
|
300
300
|
get Get group detail by ID
|
|
301
301
|
create Create a new group
|
|
302
302
|
update Update group settings
|
|
303
|
+
reorder Change group priorities (drag & drag equivalent)
|
|
303
304
|
delete Archive a group
|
|
304
305
|
ab-test Manage A/B tests (start, stop, adjust)
|
|
305
306
|
|
|
306
307
|
Statuses: ACTIVE, DISABLED (emergency stop), ARCHIVED (soft delete)
|
|
307
308
|
`
|
|
308
309
|
);
|
|
309
|
-
groups.command("list").description("List all policy groups").
|
|
310
|
+
groups.command("list").description("List all policy groups").action(async () => {
|
|
310
311
|
try {
|
|
311
312
|
const globalOpts = program.opts();
|
|
312
313
|
const format = globalOpts.format ?? "json";
|
|
@@ -314,13 +315,12 @@ function registerGroupCommands(program) {
|
|
|
314
315
|
apiKey: globalOpts.apiKey,
|
|
315
316
|
baseUrl: globalOpts.baseUrl,
|
|
316
317
|
dryRun: globalOpts.dryRun,
|
|
317
|
-
verbose: globalOpts.verbose
|
|
318
|
-
params: { page: opts.page, size: opts.size }
|
|
318
|
+
verbose: globalOpts.verbose
|
|
319
319
|
});
|
|
320
320
|
if (format === "table") {
|
|
321
321
|
printTable(
|
|
322
322
|
["ID", "Name", "Status", "Priority", "Version", "Updated"],
|
|
323
|
-
data.
|
|
323
|
+
data.map((g) => [
|
|
324
324
|
g.id,
|
|
325
325
|
g.name,
|
|
326
326
|
g.status,
|
|
@@ -331,7 +331,7 @@ function registerGroupCommands(program) {
|
|
|
331
331
|
{ truncate: 24 }
|
|
332
332
|
);
|
|
333
333
|
console.log(`
|
|
334
|
-
${data.
|
|
334
|
+
${data.length} total`);
|
|
335
335
|
} else {
|
|
336
336
|
printJson(data);
|
|
337
337
|
}
|
|
@@ -362,19 +362,20 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
362
362
|
Example:
|
|
363
363
|
$ lexq groups create --json '{
|
|
364
364
|
"name": "Payment Policy",
|
|
365
|
-
"description": "Rules for payment processing"
|
|
366
|
-
"priority": 0
|
|
365
|
+
"description": "Rules for payment processing"
|
|
367
366
|
}'
|
|
368
367
|
|
|
369
368
|
Fields:
|
|
370
369
|
name string Group name (required, unique per tenant)
|
|
371
370
|
description string Description (optional, max 255 chars)
|
|
372
|
-
|
|
373
|
-
activationGroup string Cross-group conflict resolution key (optional)
|
|
371
|
+
activationGroup string Execution Group — conflict-resolution cluster key (optional)
|
|
374
372
|
activationMode string NONE | EXCLUSIVE | MAX_N [default: NONE]
|
|
375
373
|
activationStrategy string FIRST_MATCH | HIGHEST_PRIORITY | MAX_BENEFIT [default: FIRST_MATCH]
|
|
376
374
|
executionLimit number Max rules to fire in MAX_N mode (optional)
|
|
377
375
|
status string ACTIVE | DISABLED [default: ACTIVE]
|
|
376
|
+
|
|
377
|
+
priority is auto-assigned (appended last, tenant-wide). Use 'lexq groups reorder' to change order.
|
|
378
|
+
Groups sharing an activationGroup must share the same activationMode / activationStrategy / executionLimit.
|
|
378
379
|
`
|
|
379
380
|
).action(async (opts) => {
|
|
380
381
|
try {
|
|
@@ -398,9 +399,9 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
398
399
|
dedent3`
|
|
399
400
|
|
|
400
401
|
Example:
|
|
401
|
-
$ lexq groups update --id <groupId> --json '{"description": "Updated", "
|
|
402
|
+
$ lexq groups update --id <groupId> --json '{"description": "Updated", "status": "DISABLED"}'
|
|
402
403
|
|
|
403
|
-
All fields are optional — only provided fields are updated.
|
|
404
|
+
All fields are optional — only provided fields are updated. priority is not settable here — use 'lexq groups reorder'.
|
|
404
405
|
`
|
|
405
406
|
).action(async (opts) => {
|
|
406
407
|
try {
|
|
@@ -434,7 +435,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
434
435
|
const answer = await rl.question(`Delete group ${opts.id}? [y/N] `);
|
|
435
436
|
rl.close();
|
|
436
437
|
if (answer.toLowerCase() !== "y") {
|
|
437
|
-
console.log("
|
|
438
|
+
console.log("Canceled.");
|
|
438
439
|
return;
|
|
439
440
|
}
|
|
440
441
|
}
|
|
@@ -450,6 +451,41 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
450
451
|
process.exit(1);
|
|
451
452
|
}
|
|
452
453
|
});
|
|
454
|
+
groups.command("reorder").description("Reorder policy groups by priority (drag & drop equivalent)").requiredOption("--group-ids <ids>", "Comma-separated group IDs in desired order").addHelpText(
|
|
455
|
+
"after",
|
|
456
|
+
dedent3`
|
|
457
|
+
|
|
458
|
+
Assigns priority 1, 2, 3, ... to groups in the order given (tenant-wide, 1...N continuous).
|
|
459
|
+
activationGroup (Execution Group) is NOT affected — reorder only changes priority.
|
|
460
|
+
|
|
461
|
+
Example:
|
|
462
|
+
$ lexq groups reorder --group-ids "id3,id1,id2"
|
|
463
|
+
|
|
464
|
+
Result: id3 → priority 1, id1 → priority 2, id2 → priority 3
|
|
465
|
+
`
|
|
466
|
+
).action(async (opts) => {
|
|
467
|
+
try {
|
|
468
|
+
const globalOpts = program.opts();
|
|
469
|
+
const groupIds = opts.groupIds.split(",").map((id) => id.trim());
|
|
470
|
+
const body = {
|
|
471
|
+
groups: groupIds.map((groupId, index) => ({
|
|
472
|
+
groupId,
|
|
473
|
+
priority: index + 1
|
|
474
|
+
}))
|
|
475
|
+
};
|
|
476
|
+
await apiRequest("PATCH", "policy-groups/reorder", {
|
|
477
|
+
apiKey: globalOpts.apiKey,
|
|
478
|
+
baseUrl: globalOpts.baseUrl,
|
|
479
|
+
dryRun: globalOpts.dryRun,
|
|
480
|
+
verbose: globalOpts.verbose,
|
|
481
|
+
body
|
|
482
|
+
});
|
|
483
|
+
console.log(`\u2713 ${groupIds.length} groups reordered.`);
|
|
484
|
+
} catch (error) {
|
|
485
|
+
printError(error);
|
|
486
|
+
process.exit(1);
|
|
487
|
+
}
|
|
488
|
+
});
|
|
453
489
|
const abTest = groups.command("ab-test").description("A/B test management").addHelpText(
|
|
454
490
|
"after",
|
|
455
491
|
dedent3`
|
|
@@ -507,7 +543,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
507
543
|
const answer = await rl.question(`Stop A/B test for group ${opts.groupId}? [y/N] `);
|
|
508
544
|
rl.close();
|
|
509
545
|
if (answer.toLowerCase() !== "y") {
|
|
510
|
-
console.log("
|
|
546
|
+
console.log("Canceled.");
|
|
511
547
|
return;
|
|
512
548
|
}
|
|
513
549
|
}
|
|
@@ -719,7 +755,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
719
755
|
const answer = await rl.question(`Delete version ${opts.id}? [y/N] `);
|
|
720
756
|
rl.close();
|
|
721
757
|
if (answer.toLowerCase() !== "y") {
|
|
722
|
-
console.log("
|
|
758
|
+
console.log("Canceled.");
|
|
723
759
|
return;
|
|
724
760
|
}
|
|
725
761
|
}
|
|
@@ -803,7 +839,7 @@ function registerRuleCommands(program) {
|
|
|
803
839
|
Only DRAFT versions allow rule modifications.
|
|
804
840
|
`
|
|
805
841
|
);
|
|
806
|
-
rules.command("list").description("List rules for a policy version").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Policy version ID").
|
|
842
|
+
rules.command("list").description("List rules for a policy version").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Policy version ID").action(async (opts) => {
|
|
807
843
|
try {
|
|
808
844
|
const globalOpts = program.opts();
|
|
809
845
|
const format = globalOpts.format ?? "json";
|
|
@@ -814,14 +850,13 @@ function registerRuleCommands(program) {
|
|
|
814
850
|
apiKey: globalOpts.apiKey,
|
|
815
851
|
baseUrl: globalOpts.baseUrl,
|
|
816
852
|
dryRun: globalOpts.dryRun,
|
|
817
|
-
verbose: globalOpts.verbose
|
|
818
|
-
params: { page: opts.page, size: opts.size }
|
|
853
|
+
verbose: globalOpts.verbose
|
|
819
854
|
}
|
|
820
855
|
);
|
|
821
856
|
if (format === "table") {
|
|
822
857
|
printTable(
|
|
823
858
|
["ID", "Name", "Priority", "Conditions", "Actions", "Enabled"],
|
|
824
|
-
data.
|
|
859
|
+
data.map((r) => [
|
|
825
860
|
r.id,
|
|
826
861
|
r.name,
|
|
827
862
|
String(r.priority),
|
|
@@ -832,7 +867,7 @@ function registerRuleCommands(program) {
|
|
|
832
867
|
{ truncate: 24 }
|
|
833
868
|
);
|
|
834
869
|
console.log(`
|
|
835
|
-
${data.
|
|
870
|
+
${data.length} total`);
|
|
836
871
|
} else {
|
|
837
872
|
printJson(data);
|
|
838
873
|
}
|
|
@@ -867,7 +902,6 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
867
902
|
Example:
|
|
868
903
|
$ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
869
904
|
"name": "VIP 20% Discount",
|
|
870
|
-
"priority": 0,
|
|
871
905
|
"condition": {
|
|
872
906
|
"type": "SINGLE",
|
|
873
907
|
"field": "customer_tier",
|
|
@@ -891,8 +925,11 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
891
925
|
|
|
892
926
|
Mutex (optional — rule-level conflict resolution):
|
|
893
927
|
mutexGroup string Logical grouping key (e.g., "best-discount")
|
|
894
|
-
mutexMode string NONE | EXCLUSIVE [default: NONE]
|
|
928
|
+
mutexMode string NONE | EXCLUSIVE | MAX_N [default: NONE]
|
|
895
929
|
mutexStrategy string FIRST_MATCH | HIGHEST_PRIORITY | MAX_BENEFIT
|
|
930
|
+
mutexLimit number Max rules to fire in MAX_N mode (required when MAX_N)
|
|
931
|
+
|
|
932
|
+
priority is auto-assigned (appended last). Use 'lexq rules reorder' to change order.
|
|
896
933
|
`
|
|
897
934
|
).action(async (opts) => {
|
|
898
935
|
try {
|
|
@@ -959,7 +996,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
959
996
|
const answer = await rl.question(`Delete rule ${opts.id}? [y/N] `);
|
|
960
997
|
rl.close();
|
|
961
998
|
if (answer.toLowerCase() !== "y") {
|
|
962
|
-
console.log("
|
|
999
|
+
console.log("Canceled.");
|
|
963
1000
|
return;
|
|
964
1001
|
}
|
|
965
1002
|
}
|
|
@@ -983,12 +1020,12 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
983
1020
|
"after",
|
|
984
1021
|
dedent5`
|
|
985
1022
|
|
|
986
|
-
Assigns priority
|
|
1023
|
+
Assigns priority 1, 2, 3, ... to rules in the order given (1-based, 1...N continuous).
|
|
987
1024
|
|
|
988
1025
|
Example:
|
|
989
1026
|
$ lexq rules reorder --group-id <gid> --version-id <vid> --rule-ids "id3,id1,id2"
|
|
990
1027
|
|
|
991
|
-
Result: id3 → priority
|
|
1028
|
+
Result: id3 → priority 1, id1 → priority 2, id2 → priority 3
|
|
992
1029
|
`
|
|
993
1030
|
).action(async (opts) => {
|
|
994
1031
|
try {
|
|
@@ -997,7 +1034,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
997
1034
|
const body = {
|
|
998
1035
|
rules: ruleIds.map((ruleId, index) => ({
|
|
999
1036
|
ruleId,
|
|
1000
|
-
priority: index
|
|
1037
|
+
priority: index + 1
|
|
1001
1038
|
}))
|
|
1002
1039
|
};
|
|
1003
1040
|
await apiRequest(
|
|
@@ -1183,7 +1220,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
1183
1220
|
const answer = await rl.question(`Delete fact ${opts.id}? [y/N] `);
|
|
1184
1221
|
rl.close();
|
|
1185
1222
|
if (answer.toLowerCase() !== "y") {
|
|
1186
|
-
console.log("
|
|
1223
|
+
console.log("Canceled.");
|
|
1187
1224
|
return;
|
|
1188
1225
|
}
|
|
1189
1226
|
}
|
|
@@ -1340,7 +1377,7 @@ function registerDeployCommands(program) {
|
|
|
1340
1377
|
const answer = await rl.question(`Rollback group ${opts.groupId}? [y/N] `);
|
|
1341
1378
|
rl.close();
|
|
1342
1379
|
if (answer.toLowerCase() !== "y") {
|
|
1343
|
-
console.log("
|
|
1380
|
+
console.log("Canceled.");
|
|
1344
1381
|
return;
|
|
1345
1382
|
}
|
|
1346
1383
|
}
|
|
@@ -1375,7 +1412,7 @@ function registerDeployCommands(program) {
|
|
|
1375
1412
|
const answer = await rl.question(`Undeploy group ${opts.groupId}? [y/N] `);
|
|
1376
1413
|
rl.close();
|
|
1377
1414
|
if (answer.toLowerCase() !== "y") {
|
|
1378
|
-
console.log("
|
|
1415
|
+
console.log("Canceled.");
|
|
1379
1416
|
return;
|
|
1380
1417
|
}
|
|
1381
1418
|
}
|
|
@@ -1830,10 +1867,7 @@ function registerAnalyticsCommands(program) {
|
|
|
1830
1867
|
process.exit(1);
|
|
1831
1868
|
}
|
|
1832
1869
|
});
|
|
1833
|
-
sim.command("list").description("List simulation history").option(
|
|
1834
|
-
"--status <status>",
|
|
1835
|
-
"Filter by status (PENDING, RUNNING, COMPLETED, FAILED, CANCELLED)"
|
|
1836
|
-
).option("--from <date>", "Start date (yyyy-MM-dd)").option("--to <date>", "End date (yyyy-MM-dd)").option("--page <number>", "Page number", "0").option("--size <number>", "Page size", "20").action(async (opts) => {
|
|
1870
|
+
sim.command("list").description("List simulation history").option("--status <status>", "Filter by status (PENDING, RUNNING, COMPLETED, FAILED, CANCELED)").option("--from <date>", "Start date (yyyy-MM-dd)").option("--to <date>", "End date (yyyy-MM-dd)").option("--page <number>", "Page number", "0").option("--size <number>", "Page size", "20").action(async (opts) => {
|
|
1837
1871
|
try {
|
|
1838
1872
|
const globalOpts = program.opts();
|
|
1839
1873
|
const format = globalOpts.format ?? "json";
|
|
@@ -1880,7 +1914,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
1880
1914
|
"after",
|
|
1881
1915
|
dedent8`
|
|
1882
1916
|
|
|
1883
|
-
Only PENDING or RUNNING simulations can be
|
|
1917
|
+
Only PENDING or RUNNING simulations can be canceled.
|
|
1884
1918
|
`
|
|
1885
1919
|
).action(async (opts) => {
|
|
1886
1920
|
try {
|
|
@@ -1891,7 +1925,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
1891
1925
|
const answer = await rl.question(`Cancel simulation ${opts.id}? [y/N] `);
|
|
1892
1926
|
rl.close();
|
|
1893
1927
|
if (answer.toLowerCase() !== "y") {
|
|
1894
|
-
console.log("
|
|
1928
|
+
console.log("Canceled.");
|
|
1895
1929
|
return;
|
|
1896
1930
|
}
|
|
1897
1931
|
}
|
|
@@ -1901,7 +1935,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
1901
1935
|
dryRun: globalOpts.dryRun,
|
|
1902
1936
|
verbose: globalOpts.verbose
|
|
1903
1937
|
});
|
|
1904
|
-
console.log(`\u2713 Simulation ${opts.id}
|
|
1938
|
+
console.log(`\u2713 Simulation ${opts.id} canceled.`);
|
|
1905
1939
|
} catch (error) {
|
|
1906
1940
|
printError(error);
|
|
1907
1941
|
process.exit(1);
|
|
@@ -2351,7 +2385,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
2351
2385
|
const answer = await rl.question(`Delete integration ${opts.id}? [y/N] `);
|
|
2352
2386
|
rl.close();
|
|
2353
2387
|
if (answer.toLowerCase() !== "y") {
|
|
2354
|
-
console.log("
|
|
2388
|
+
console.log("Canceled.");
|
|
2355
2389
|
return;
|
|
2356
2390
|
}
|
|
2357
2391
|
}
|
|
@@ -2728,7 +2762,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
2728
2762
|
const answer = await rl.question(`Delete webhook subscription ${opts.id}? [y/N] `);
|
|
2729
2763
|
rl.close();
|
|
2730
2764
|
if (answer.toLowerCase() !== "y") {
|
|
2731
|
-
console.log("
|
|
2765
|
+
console.log("Canceled.");
|
|
2732
2766
|
return;
|
|
2733
2767
|
}
|
|
2734
2768
|
}
|
|
@@ -2868,16 +2902,10 @@ function registerGroupTools(server, callApi) {
|
|
|
2868
2902
|
"lexq_groups_list",
|
|
2869
2903
|
{
|
|
2870
2904
|
title: "List Policy Groups",
|
|
2871
|
-
description: "List all policy groups
|
|
2872
|
-
inputSchema: {
|
|
2873
|
-
page: z.number().int().min(0).default(0).describe("Page number (0-indexed)"),
|
|
2874
|
-
size: z.number().int().min(1).max(100).default(20).describe("Page size")
|
|
2875
|
-
}
|
|
2905
|
+
description: "List all policy groups (tenant-wide, priority ASC).",
|
|
2906
|
+
inputSchema: {}
|
|
2876
2907
|
},
|
|
2877
|
-
async (
|
|
2878
|
-
const params = paginationParams(page, size);
|
|
2879
|
-
return callApi("GET", "policy-groups", { params });
|
|
2880
|
-
}
|
|
2908
|
+
async () => callApi("GET", "policy-groups")
|
|
2881
2909
|
);
|
|
2882
2910
|
server.registerTool(
|
|
2883
2911
|
"lexq_groups_get",
|
|
@@ -2894,10 +2922,9 @@ function registerGroupTools(server, callApi) {
|
|
|
2894
2922
|
"lexq_groups_create",
|
|
2895
2923
|
{
|
|
2896
2924
|
title: "Create Policy Group",
|
|
2897
|
-
description: "Create a new policy group. Requires name
|
|
2925
|
+
description: "Create a new policy group. Requires name. Priority is auto-assigned (appended last, tenant-wide); use lexq_groups_reorder to change order. Optionally set conflict resolution, activation group, and description. Groups sharing an activationGroup must share the same activationMode / activationStrategy / executionLimit.",
|
|
2898
2926
|
inputSchema: {
|
|
2899
2927
|
name: z.string().describe("Group name (unique among non-ARCHIVED)"),
|
|
2900
|
-
priority: z.number().int().min(0).describe("Execution priority (lower = higher)"),
|
|
2901
2928
|
description: z.string().optional().describe("Group description"),
|
|
2902
2929
|
activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
|
|
2903
2930
|
activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
|
|
@@ -2907,8 +2934,7 @@ function registerGroupTools(server, callApi) {
|
|
|
2907
2934
|
},
|
|
2908
2935
|
async (args) => {
|
|
2909
2936
|
const body = {
|
|
2910
|
-
name: args.name
|
|
2911
|
-
priority: args.priority
|
|
2937
|
+
name: args.name
|
|
2912
2938
|
};
|
|
2913
2939
|
if (args.description !== void 0) body.description = args.description;
|
|
2914
2940
|
if (args.activationMode !== void 0) body.activationMode = args.activationMode;
|
|
@@ -2926,9 +2952,9 @@ function registerGroupTools(server, callApi) {
|
|
|
2926
2952
|
inputSchema: {
|
|
2927
2953
|
groupId: z.string().uuid().describe("Policy group ID"),
|
|
2928
2954
|
name: z.string().optional().describe("New name"),
|
|
2929
|
-
priority: z.number().int().min(0).optional().describe("New priority"),
|
|
2930
2955
|
description: z.string().optional().describe("New description"),
|
|
2931
2956
|
status: z.enum(["ACTIVE", "DISABLED"]).optional().describe("Status (DISABLED = emergency stop)"),
|
|
2957
|
+
activationGroup: z.string().optional().describe("Activation group (Execution Group) cluster key"),
|
|
2932
2958
|
activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
|
|
2933
2959
|
activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
|
|
2934
2960
|
executionLimit: z.number().int().min(1).optional().describe("Max rule executions")
|
|
@@ -2947,6 +2973,23 @@ function registerGroupTools(server, callApi) {
|
|
|
2947
2973
|
},
|
|
2948
2974
|
async ({ groupId }) => callApi("DELETE", `policy-groups/${groupId}`)
|
|
2949
2975
|
);
|
|
2976
|
+
server.registerTool(
|
|
2977
|
+
"lexq_groups_reorder",
|
|
2978
|
+
{
|
|
2979
|
+
title: "Reorder Policy Groups",
|
|
2980
|
+
description: "Reorder policy groups by priority. Priority is tenant-wide and flat (1...N continuous); array index 0 = priority 1 (highest precedence). activationGroup is not affected \u2014 this only changes priority.",
|
|
2981
|
+
inputSchema: {
|
|
2982
|
+
groupIds: z.array(z.string().uuid()).describe("Group IDs in desired priority order (index 0 = priority 1)")
|
|
2983
|
+
}
|
|
2984
|
+
},
|
|
2985
|
+
async ({ groupIds }) => {
|
|
2986
|
+
const groups = groupIds.map((groupId, index) => ({
|
|
2987
|
+
groupId,
|
|
2988
|
+
priority: index + 1
|
|
2989
|
+
}));
|
|
2990
|
+
return callApi("PATCH", "policy-groups/reorder", { body: { groups } });
|
|
2991
|
+
}
|
|
2992
|
+
);
|
|
2950
2993
|
server.registerTool(
|
|
2951
2994
|
"lexq_ab_test_start",
|
|
2952
2995
|
{
|
|
@@ -3082,17 +3125,13 @@ function registerRuleTools(server, callApi) {
|
|
|
3082
3125
|
"lexq_rules_list",
|
|
3083
3126
|
{
|
|
3084
3127
|
title: "List Rules",
|
|
3085
|
-
description: "List all rules in a version. Returns summary with conditionSummary and actionSummary.",
|
|
3128
|
+
description: "List all rules in a version (priority ASC). Returns summary with conditionSummary and actionSummary.",
|
|
3086
3129
|
inputSchema: {
|
|
3087
3130
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
3088
|
-
versionId: z3.string().uuid().describe("Version ID")
|
|
3089
|
-
page: z3.number().int().min(0).default(0).describe("Page number"),
|
|
3090
|
-
size: z3.number().int().min(1).max(100).default(20).describe("Page size")
|
|
3131
|
+
versionId: z3.string().uuid().describe("Version ID")
|
|
3091
3132
|
}
|
|
3092
3133
|
},
|
|
3093
|
-
async ({ groupId, versionId
|
|
3094
|
-
params: paginationParams(page, size)
|
|
3095
|
-
})
|
|
3134
|
+
async ({ groupId, versionId }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}/rules`)
|
|
3096
3135
|
);
|
|
3097
3136
|
server.registerTool(
|
|
3098
3137
|
"lexq_rules_get",
|
|
@@ -3112,7 +3151,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3112
3151
|
{
|
|
3113
3152
|
title: "Create Rule",
|
|
3114
3153
|
description: dedent13`
|
|
3115
|
-
Create a rule in a DRAFT version. Requires name,
|
|
3154
|
+
Create a rule in a DRAFT version. Requires name, condition tree, and actions array. priority is auto-assigned (appended last); use lexq_rules_reorder to change order.
|
|
3116
3155
|
|
|
3117
3156
|
Before creating rules with new fact keys, call lexq_facts_list to check existing facts.
|
|
3118
3157
|
If a required key is missing, ask the user to confirm the type, isRequired, and description
|
|
@@ -3145,7 +3184,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3145
3184
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
3146
3185
|
versionId: z3.string().uuid().describe("Version ID"),
|
|
3147
3186
|
rule: z3.string().describe(
|
|
3148
|
-
"JSON string of CreateRuleRequest: { name,
|
|
3187
|
+
"JSON string of CreateRuleRequest: { name, condition, actions, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
|
|
3149
3188
|
)
|
|
3150
3189
|
}
|
|
3151
3190
|
},
|
|
@@ -3164,7 +3203,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3164
3203
|
versionId: z3.string().uuid().describe("Version ID"),
|
|
3165
3204
|
ruleId: z3.string().uuid().describe("Rule ID"),
|
|
3166
3205
|
rule: z3.string().describe(
|
|
3167
|
-
"JSON string of UpdateRuleRequest: { name?,
|
|
3206
|
+
"JSON string of UpdateRuleRequest: { name?, condition?, actions?, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
|
|
3168
3207
|
)
|
|
3169
3208
|
}
|
|
3170
3209
|
},
|
|
@@ -3199,7 +3238,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3199
3238
|
"lexq_rules_reorder",
|
|
3200
3239
|
{
|
|
3201
3240
|
title: "Reorder Rules",
|
|
3202
|
-
description: "Reorder rules by specifying rule IDs
|
|
3241
|
+
description: "Reorder rules by specifying rule IDs in desired order. Priorities are assigned 1...N (1-based, continuous); array index 0 = priority 1 (highest precedence).",
|
|
3203
3242
|
inputSchema: {
|
|
3204
3243
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
3205
3244
|
versionId: z3.string().uuid().describe("Version ID"),
|
|
@@ -3209,7 +3248,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3209
3248
|
async ({ groupId, versionId, ruleIds }) => {
|
|
3210
3249
|
const rules = ruleIds.map((ruleId, index) => ({
|
|
3211
3250
|
ruleId,
|
|
3212
|
-
priority: index
|
|
3251
|
+
priority: index + 1
|
|
3213
3252
|
}));
|
|
3214
3253
|
return callApi("PATCH", `policy-groups/${groupId}/versions/${versionId}/rules/reorder`, {
|
|
3215
3254
|
body: { rules }
|
|
@@ -3555,7 +3594,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
3555
3594
|
inputSchema: {
|
|
3556
3595
|
page: z6.number().int().min(0).default(0).describe("Page number"),
|
|
3557
3596
|
size: z6.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
3558
|
-
status: z6.enum(["PENDING", "RUNNING", "COMPLETED", "FAILED", "
|
|
3597
|
+
status: z6.enum(["PENDING", "RUNNING", "COMPLETED", "FAILED", "CANCELED"]).optional().describe("Filter by status"),
|
|
3559
3598
|
from: z6.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
3560
3599
|
to: z6.string().optional().describe("End date (yyyy-MM-dd)")
|
|
3561
3600
|
}
|
|
@@ -4133,7 +4172,7 @@ function registerDomainTemplateCommands(program) {
|
|
|
4133
4172
|
);
|
|
4134
4173
|
rl.close();
|
|
4135
4174
|
if (answer.toLowerCase() !== "y") {
|
|
4136
|
-
console.log("
|
|
4175
|
+
console.log("Canceled.");
|
|
4137
4176
|
return;
|
|
4138
4177
|
}
|
|
4139
4178
|
}
|
package/dist/mcp/register.js
CHANGED
|
@@ -13,38 +13,15 @@ function registerStatusTools(server, callApi) {
|
|
|
13
13
|
|
|
14
14
|
// src/mcp/tools/groups.ts
|
|
15
15
|
import { z } from "zod";
|
|
16
|
-
|
|
17
|
-
// src/lib/config.ts
|
|
18
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
19
|
-
import { homedir } from "os";
|
|
20
|
-
import { join } from "path";
|
|
21
|
-
var CONFIG_DIR = join(homedir(), ".lexq");
|
|
22
|
-
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
23
|
-
|
|
24
|
-
// src/mcp/tools/_shared.ts
|
|
25
|
-
function paginationParams(page, size) {
|
|
26
|
-
const params = {};
|
|
27
|
-
if (page !== void 0) params.page = String(page);
|
|
28
|
-
if (size !== void 0) params.size = String(size);
|
|
29
|
-
return params;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// src/mcp/tools/groups.ts
|
|
33
16
|
function registerGroupTools(server, callApi) {
|
|
34
17
|
server.registerTool(
|
|
35
18
|
"lexq_groups_list",
|
|
36
19
|
{
|
|
37
20
|
title: "List Policy Groups",
|
|
38
|
-
description: "List all policy groups
|
|
39
|
-
inputSchema: {
|
|
40
|
-
page: z.number().int().min(0).default(0).describe("Page number (0-indexed)"),
|
|
41
|
-
size: z.number().int().min(1).max(100).default(20).describe("Page size")
|
|
42
|
-
}
|
|
21
|
+
description: "List all policy groups (tenant-wide, priority ASC).",
|
|
22
|
+
inputSchema: {}
|
|
43
23
|
},
|
|
44
|
-
async (
|
|
45
|
-
const params = paginationParams(page, size);
|
|
46
|
-
return callApi("GET", "policy-groups", { params });
|
|
47
|
-
}
|
|
24
|
+
async () => callApi("GET", "policy-groups")
|
|
48
25
|
);
|
|
49
26
|
server.registerTool(
|
|
50
27
|
"lexq_groups_get",
|
|
@@ -61,10 +38,9 @@ function registerGroupTools(server, callApi) {
|
|
|
61
38
|
"lexq_groups_create",
|
|
62
39
|
{
|
|
63
40
|
title: "Create Policy Group",
|
|
64
|
-
description: "Create a new policy group. Requires name
|
|
41
|
+
description: "Create a new policy group. Requires name. Priority is auto-assigned (appended last, tenant-wide); use lexq_groups_reorder to change order. Optionally set conflict resolution, activation group, and description. Groups sharing an activationGroup must share the same activationMode / activationStrategy / executionLimit.",
|
|
65
42
|
inputSchema: {
|
|
66
43
|
name: z.string().describe("Group name (unique among non-ARCHIVED)"),
|
|
67
|
-
priority: z.number().int().min(0).describe("Execution priority (lower = higher)"),
|
|
68
44
|
description: z.string().optional().describe("Group description"),
|
|
69
45
|
activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
|
|
70
46
|
activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
|
|
@@ -74,8 +50,7 @@ function registerGroupTools(server, callApi) {
|
|
|
74
50
|
},
|
|
75
51
|
async (args) => {
|
|
76
52
|
const body = {
|
|
77
|
-
name: args.name
|
|
78
|
-
priority: args.priority
|
|
53
|
+
name: args.name
|
|
79
54
|
};
|
|
80
55
|
if (args.description !== void 0) body.description = args.description;
|
|
81
56
|
if (args.activationMode !== void 0) body.activationMode = args.activationMode;
|
|
@@ -93,9 +68,9 @@ function registerGroupTools(server, callApi) {
|
|
|
93
68
|
inputSchema: {
|
|
94
69
|
groupId: z.string().uuid().describe("Policy group ID"),
|
|
95
70
|
name: z.string().optional().describe("New name"),
|
|
96
|
-
priority: z.number().int().min(0).optional().describe("New priority"),
|
|
97
71
|
description: z.string().optional().describe("New description"),
|
|
98
72
|
status: z.enum(["ACTIVE", "DISABLED"]).optional().describe("Status (DISABLED = emergency stop)"),
|
|
73
|
+
activationGroup: z.string().optional().describe("Activation group (Execution Group) cluster key"),
|
|
99
74
|
activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
|
|
100
75
|
activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
|
|
101
76
|
executionLimit: z.number().int().min(1).optional().describe("Max rule executions")
|
|
@@ -114,6 +89,23 @@ function registerGroupTools(server, callApi) {
|
|
|
114
89
|
},
|
|
115
90
|
async ({ groupId }) => callApi("DELETE", `policy-groups/${groupId}`)
|
|
116
91
|
);
|
|
92
|
+
server.registerTool(
|
|
93
|
+
"lexq_groups_reorder",
|
|
94
|
+
{
|
|
95
|
+
title: "Reorder Policy Groups",
|
|
96
|
+
description: "Reorder policy groups by priority. Priority is tenant-wide and flat (1...N continuous); array index 0 = priority 1 (highest precedence). activationGroup is not affected \u2014 this only changes priority.",
|
|
97
|
+
inputSchema: {
|
|
98
|
+
groupIds: z.array(z.string().uuid()).describe("Group IDs in desired priority order (index 0 = priority 1)")
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
async ({ groupIds }) => {
|
|
102
|
+
const groups = groupIds.map((groupId, index) => ({
|
|
103
|
+
groupId,
|
|
104
|
+
priority: index + 1
|
|
105
|
+
}));
|
|
106
|
+
return callApi("PATCH", "policy-groups/reorder", { body: { groups } });
|
|
107
|
+
}
|
|
108
|
+
);
|
|
117
109
|
server.registerTool(
|
|
118
110
|
"lexq_ab_test_start",
|
|
119
111
|
{
|
|
@@ -154,6 +146,23 @@ function registerGroupTools(server, callApi) {
|
|
|
154
146
|
|
|
155
147
|
// src/mcp/tools/versions.ts
|
|
156
148
|
import { z as z2 } from "zod";
|
|
149
|
+
|
|
150
|
+
// src/lib/config.ts
|
|
151
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
152
|
+
import { homedir } from "os";
|
|
153
|
+
import { join } from "path";
|
|
154
|
+
var CONFIG_DIR = join(homedir(), ".lexq");
|
|
155
|
+
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
156
|
+
|
|
157
|
+
// src/mcp/tools/_shared.ts
|
|
158
|
+
function paginationParams(page, size) {
|
|
159
|
+
const params = {};
|
|
160
|
+
if (page !== void 0) params.page = String(page);
|
|
161
|
+
if (size !== void 0) params.size = String(size);
|
|
162
|
+
return params;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/mcp/tools/versions.ts
|
|
157
166
|
function registerVersionTools(server, callApi) {
|
|
158
167
|
server.registerTool(
|
|
159
168
|
"lexq_versions_list",
|
|
@@ -249,17 +258,13 @@ function registerRuleTools(server, callApi) {
|
|
|
249
258
|
"lexq_rules_list",
|
|
250
259
|
{
|
|
251
260
|
title: "List Rules",
|
|
252
|
-
description: "List all rules in a version. Returns summary with conditionSummary and actionSummary.",
|
|
261
|
+
description: "List all rules in a version (priority ASC). Returns summary with conditionSummary and actionSummary.",
|
|
253
262
|
inputSchema: {
|
|
254
263
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
255
|
-
versionId: z3.string().uuid().describe("Version ID")
|
|
256
|
-
page: z3.number().int().min(0).default(0).describe("Page number"),
|
|
257
|
-
size: z3.number().int().min(1).max(100).default(20).describe("Page size")
|
|
264
|
+
versionId: z3.string().uuid().describe("Version ID")
|
|
258
265
|
}
|
|
259
266
|
},
|
|
260
|
-
async ({ groupId, versionId
|
|
261
|
-
params: paginationParams(page, size)
|
|
262
|
-
})
|
|
267
|
+
async ({ groupId, versionId }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}/rules`)
|
|
263
268
|
);
|
|
264
269
|
server.registerTool(
|
|
265
270
|
"lexq_rules_get",
|
|
@@ -279,7 +284,7 @@ function registerRuleTools(server, callApi) {
|
|
|
279
284
|
{
|
|
280
285
|
title: "Create Rule",
|
|
281
286
|
description: dedent`
|
|
282
|
-
Create a rule in a DRAFT version. Requires name,
|
|
287
|
+
Create a rule in a DRAFT version. Requires name, condition tree, and actions array. priority is auto-assigned (appended last); use lexq_rules_reorder to change order.
|
|
283
288
|
|
|
284
289
|
Before creating rules with new fact keys, call lexq_facts_list to check existing facts.
|
|
285
290
|
If a required key is missing, ask the user to confirm the type, isRequired, and description
|
|
@@ -312,7 +317,7 @@ function registerRuleTools(server, callApi) {
|
|
|
312
317
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
313
318
|
versionId: z3.string().uuid().describe("Version ID"),
|
|
314
319
|
rule: z3.string().describe(
|
|
315
|
-
"JSON string of CreateRuleRequest: { name,
|
|
320
|
+
"JSON string of CreateRuleRequest: { name, condition, actions, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
|
|
316
321
|
)
|
|
317
322
|
}
|
|
318
323
|
},
|
|
@@ -331,7 +336,7 @@ function registerRuleTools(server, callApi) {
|
|
|
331
336
|
versionId: z3.string().uuid().describe("Version ID"),
|
|
332
337
|
ruleId: z3.string().uuid().describe("Rule ID"),
|
|
333
338
|
rule: z3.string().describe(
|
|
334
|
-
"JSON string of UpdateRuleRequest: { name?,
|
|
339
|
+
"JSON string of UpdateRuleRequest: { name?, condition?, actions?, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
|
|
335
340
|
)
|
|
336
341
|
}
|
|
337
342
|
},
|
|
@@ -366,7 +371,7 @@ function registerRuleTools(server, callApi) {
|
|
|
366
371
|
"lexq_rules_reorder",
|
|
367
372
|
{
|
|
368
373
|
title: "Reorder Rules",
|
|
369
|
-
description: "Reorder rules by specifying rule IDs
|
|
374
|
+
description: "Reorder rules by specifying rule IDs in desired order. Priorities are assigned 1...N (1-based, continuous); array index 0 = priority 1 (highest precedence).",
|
|
370
375
|
inputSchema: {
|
|
371
376
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
372
377
|
versionId: z3.string().uuid().describe("Version ID"),
|
|
@@ -376,7 +381,7 @@ function registerRuleTools(server, callApi) {
|
|
|
376
381
|
async ({ groupId, versionId, ruleIds }) => {
|
|
377
382
|
const rules = ruleIds.map((ruleId, index) => ({
|
|
378
383
|
ruleId,
|
|
379
|
-
priority: index
|
|
384
|
+
priority: index + 1
|
|
380
385
|
}));
|
|
381
386
|
return callApi("PATCH", `policy-groups/${groupId}/versions/${versionId}/rules/reorder`, {
|
|
382
387
|
body: { rules }
|
|
@@ -722,7 +727,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
722
727
|
inputSchema: {
|
|
723
728
|
page: z6.number().int().min(0).default(0).describe("Page number"),
|
|
724
729
|
size: z6.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
725
|
-
status: z6.enum(["PENDING", "RUNNING", "COMPLETED", "FAILED", "
|
|
730
|
+
status: z6.enum(["PENDING", "RUNNING", "COMPLETED", "FAILED", "CANCELED"]).optional().describe("Filter by status"),
|
|
726
731
|
from: z6.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
727
732
|
to: z6.string().optional().describe("End date (yyyy-MM-dd)")
|
|
728
733
|
}
|
package/package.json
CHANGED
|
@@ -260,7 +260,7 @@ Simulation is async. Poll until `status` is `COMPLETED` or `FAILED`.
|
|
|
260
260
|
| `RUNNING` | In progress (`progress` field shows 0–100) |
|
|
261
261
|
| `COMPLETED` | Done — results available |
|
|
262
262
|
| `FAILED` | Error occurred |
|
|
263
|
-
| `
|
|
263
|
+
| `CANCELED` | Manually canceled |
|
|
264
264
|
|
|
265
265
|
### List Simulations
|
|
266
266
|
|