@lexq/cli 0.1.39 → 0.1.41
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/AGENTS.md +34 -32
- package/CONTEXT.md +41 -47
- package/README.md +8 -6
- package/dist/index.js +156 -389
- package/dist/mcp/register.js +102 -167
- package/package.json +1 -1
- package/skills/lexq-execution/SKILL.md +120 -65
- package/skills/lexq-groups/SKILL.md +78 -38
- package/skills/lexq-recipes/SKILL.md +122 -149
- package/skills/lexq-rules/SKILL.md +154 -42
- package/skills/lexq-shared/SKILL.md +48 -28
- package/skills/lexq-simulation/SKILL.md +47 -27
package/dist/index.js
CHANGED
|
@@ -389,7 +389,7 @@ ${data.length} total`);
|
|
|
389
389
|
description string Description (optional, max 255 chars)
|
|
390
390
|
activationGroup string Execution Group — conflict-resolution cluster key (optional)
|
|
391
391
|
activationMode string NONE | EXCLUSIVE | MAX_N [default: NONE]
|
|
392
|
-
activationStrategy string
|
|
392
|
+
activationStrategy string HIGHEST_PRIORITY [default, and currently the only value]
|
|
393
393
|
executionLimit number Max rules to fire in MAX_N mode (optional)
|
|
394
394
|
status string ACTIVE | DISABLED [default: ACTIVE]
|
|
395
395
|
|
|
@@ -929,23 +929,46 @@ ${data.length} total`);
|
|
|
929
929
|
"valueType": "STRING"
|
|
930
930
|
},
|
|
931
931
|
"actions": [
|
|
932
|
-
{ "type": "MUTATE_FACT", "parameters": { "
|
|
932
|
+
{ "type": "MUTATE_FACT", "parameters": { "targetVar": "payment_amount", "operator": "SUB", "method": "PERCENTAGE", "operand": 20 } }
|
|
933
933
|
]
|
|
934
934
|
}'
|
|
935
935
|
|
|
936
|
-
Condition Operators:
|
|
937
|
-
EQUALS, NOT_EQUALS,
|
|
938
|
-
|
|
936
|
+
Condition Operators (by fact type):
|
|
937
|
+
STRING EQUALS, NOT_EQUALS, CONTAINS, IN, NOT_IN
|
|
938
|
+
NUMBER EQUALS, NOT_EQUALS, GREATER_THAN, GREATER_THAN_OR_EQUAL,
|
|
939
|
+
LESS_THAN, LESS_THAN_OR_EQUAL, IN, NOT_IN
|
|
940
|
+
BOOLEAN EQUALS, NOT_EQUALS
|
|
941
|
+
LIST_STRING HAS_ANY, HAS_ALL, HAS_NONE
|
|
942
|
+
LIST_NUMBER HAS_ANY, HAS_ALL, HAS_NONE
|
|
943
|
+
|
|
944
|
+
Using an operator outside its fact type is rejected by the server.
|
|
945
|
+
|
|
946
|
+
List-typed facts (HAS_*) — the value is always an array:
|
|
947
|
+
HAS_ANY fact has at least one of the given values
|
|
948
|
+
HAS_ALL fact has all of the given values
|
|
949
|
+
HAS_NONE fact has none of the given values
|
|
950
|
+
|
|
951
|
+
Example:
|
|
952
|
+
{ "type": "SINGLE", "field": "user_tags", "operator": "HAS_ANY",
|
|
953
|
+
"value": ["VIP", "GOLD"], "valueType": "LIST_STRING" }
|
|
954
|
+
|
|
955
|
+
CONTAINS is substring match on STRING facts, not list membership.
|
|
956
|
+
IN is the mirror of HAS_*: scalar fact, list value.
|
|
939
957
|
|
|
940
958
|
Action Types:
|
|
941
|
-
MUTATE_FACT
|
|
959
|
+
MUTATE_FACT { targetVar, operator, method, operand, refVar?, rounding? }
|
|
960
|
+
SET_FACT { targetVar, value }
|
|
961
|
+
BLOCK { reason }
|
|
962
|
+
|
|
963
|
+
refVar is the percentage base and is optional — omit to use targetVar itself.
|
|
964
|
+
It is only valid in PERCENTAGE × {ASSIGN, ADD, SUB}.
|
|
942
965
|
|
|
943
966
|
Value Types: STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER
|
|
944
967
|
|
|
945
968
|
Mutex (optional — rule-level conflict resolution):
|
|
946
969
|
mutexGroup string Logical grouping key (e.g., "best-discount")
|
|
947
970
|
mutexMode string NONE | EXCLUSIVE | MAX_N [default: NONE]
|
|
948
|
-
mutexStrategy string
|
|
971
|
+
mutexStrategy string HIGHEST_PRIORITY (currently the only value)
|
|
949
972
|
mutexLimit number Max rules to fire in MAX_N mode (required when MAX_N)
|
|
950
973
|
|
|
951
974
|
priority is auto-assigned (appended last). Use 'lexq rules reorder' to change order.
|
|
@@ -982,7 +1005,7 @@ ${data.length} total`);
|
|
|
982
1005
|
$ lexq rules update --group-id <gid> --version-id <vid> --id <rid> --json '{
|
|
983
1006
|
"name": "VIP 25% Discount",
|
|
984
1007
|
"actions": [
|
|
985
|
-
{ "type": "MUTATE_FACT", "parameters": { "
|
|
1008
|
+
{ "type": "MUTATE_FACT", "parameters": { "targetVar": "payment_amount", "operator": "SUB", "method": "PERCENTAGE", "operand": 25 } }
|
|
986
1009
|
]
|
|
987
1010
|
}'
|
|
988
1011
|
`
|
|
@@ -1808,18 +1831,17 @@ function registerAnalyticsCommands(program) {
|
|
|
1808
1831
|
Workflow: facts check → dry-run → publish → simulation → deploy
|
|
1809
1832
|
`
|
|
1810
1833
|
);
|
|
1811
|
-
analytics.command("dry-run").description("Execute a single dry run against a version").requiredOption("--version-id <versionId>", "Policy version ID").option("--json <body>", "Request body as JSON string").option("--file <path>", "Read request body from a JSON file").option("--debug", "Include debug traces", false).
|
|
1834
|
+
analytics.command("dry-run").description("Execute a single dry run against a version").requiredOption("--version-id <versionId>", "Policy version ID").option("--json <body>", "Request body as JSON string").option("--file <path>", "Read request body from a JSON file").option("--debug", "Include debug traces", false).addHelpText(
|
|
1812
1835
|
"after",
|
|
1813
1836
|
dedent8`
|
|
1814
1837
|
|
|
1815
1838
|
Examples:
|
|
1816
|
-
$ lexq analytics dry-run --version-id <vid> --debug
|
|
1839
|
+
$ lexq analytics dry-run --version-id <vid> --debug \\
|
|
1817
1840
|
--json '{"facts": {"payment_amount": 150000, "customer_tier": "VIP"}}'
|
|
1818
1841
|
|
|
1819
1842
|
$ lexq analytics dry-run --version-id <vid> --file test-input.json
|
|
1820
1843
|
|
|
1821
1844
|
The request body must include a "facts" object. Use --debug for execution traces
|
|
1822
|
-
and --mock to skip external service calls (webhooks, coupons, etc.).
|
|
1823
1845
|
`
|
|
1824
1846
|
).action(async (opts) => {
|
|
1825
1847
|
try {
|
|
@@ -1829,7 +1851,6 @@ function registerAnalyticsCommands(program) {
|
|
|
1829
1851
|
throw new Error('Request body must include "facts". Use --json or --file.');
|
|
1830
1852
|
}
|
|
1831
1853
|
body.includeDebugInfo = opts.debug;
|
|
1832
|
-
body.mockExternalCalls = opts.mock;
|
|
1833
1854
|
const data = await apiRequest(
|
|
1834
1855
|
"POST",
|
|
1835
1856
|
`analytics/dry-run/versions/${opts.versionId}`,
|
|
@@ -1939,7 +1960,7 @@ function registerAnalyticsCommands(program) {
|
|
|
1939
1960
|
cancel Cancel a running simulation
|
|
1940
1961
|
export Export results as CSV or JSON
|
|
1941
1962
|
|
|
1942
|
-
|
|
1963
|
+
Use --format table for summary view.
|
|
1943
1964
|
`
|
|
1944
1965
|
);
|
|
1945
1966
|
sim.command("start").description("Start a new batch simulation").requiredOption("--json <body>", "Simulation request body as JSON").option("--file <path>", "Read request body from a JSON file").addHelpText(
|
|
@@ -2301,27 +2322,12 @@ import "commander";
|
|
|
2301
2322
|
import dedent9 from "dedent";
|
|
2302
2323
|
|
|
2303
2324
|
// src/types/enums.ts
|
|
2325
|
+
var ConflictResolutionMode = ["NONE", "EXCLUSIVE", "MAX_N"];
|
|
2326
|
+
var ConflictResolutionStrategy = ["HIGHEST_PRIORITY"];
|
|
2304
2327
|
var ProfileCacheState = ["HIT", "MISS"];
|
|
2305
2328
|
var FailureStatus = ["PENDING", "RESOLVED", "IGNORED"];
|
|
2306
|
-
var FailureAction = ["
|
|
2307
|
-
var
|
|
2308
|
-
var TaskType = [
|
|
2309
|
-
// Integration
|
|
2310
|
-
"COUPON_ISSUE",
|
|
2311
|
-
"COUPON_CANCEL",
|
|
2312
|
-
"POINT_EARN",
|
|
2313
|
-
"POINT_USE",
|
|
2314
|
-
"POINT_REFUND",
|
|
2315
|
-
"NOTIFICATION_SEND",
|
|
2316
|
-
"CRM_SYNC_USER",
|
|
2317
|
-
"CRM_ADD_TAG",
|
|
2318
|
-
"WEBHOOK_EXECUTE",
|
|
2319
|
-
// Internal
|
|
2320
|
-
"IMAGE_PROCESSING",
|
|
2321
|
-
"DAILY_SETTLEMENT",
|
|
2322
|
-
"PLATFORM_WEBHOOK",
|
|
2323
|
-
"SCHEDULED_DEPLOYMENT"
|
|
2324
|
-
];
|
|
2329
|
+
var FailureAction = ["IGNORE", "RESOLVE"];
|
|
2330
|
+
var TaskType = ["PLATFORM_WEBHOOK", "SCHEDULED_DEPLOYMENT"];
|
|
2325
2331
|
var PlatformEventType = [
|
|
2326
2332
|
"VERSION_PUBLISHED",
|
|
2327
2333
|
"DEPLOYED",
|
|
@@ -2801,216 +2807,32 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
2801
2807
|
});
|
|
2802
2808
|
}
|
|
2803
2809
|
|
|
2804
|
-
// src/commands/integrations.ts
|
|
2805
|
-
import "commander";
|
|
2806
|
-
import dedent13 from "dedent";
|
|
2807
|
-
function registerIntegrationCommands(program) {
|
|
2808
|
-
const integrations = program.command("integrations").description("Manage external integrations").addHelpText(
|
|
2809
|
-
"after",
|
|
2810
|
-
dedent13`
|
|
2811
|
-
|
|
2812
|
-
Integrations connect rule actions to external services (webhooks, coupons,
|
|
2813
|
-
points, notifications, CRM, messengers).
|
|
2814
|
-
|
|
2815
|
-
Commands:
|
|
2816
|
-
list List all integrations
|
|
2817
|
-
get Get integration detail
|
|
2818
|
-
save Create or update an integration
|
|
2819
|
-
delete Delete an integration
|
|
2820
|
-
config-spec Show required configuration fields per type
|
|
2821
|
-
|
|
2822
|
-
Types: COUPON, POINT, NOTIFICATION, CRM, MESSENGER, WEBHOOK
|
|
2823
|
-
`
|
|
2824
|
-
);
|
|
2825
|
-
integrations.command("list").description("List integrations").option(
|
|
2826
|
-
"--type <type>",
|
|
2827
|
-
"Filter by type (COUPON, POINT, NOTIFICATION, CRM, MESSENGER, WEBHOOK)"
|
|
2828
|
-
).option("--page <number>", "Page number", "0").option("--size <number>", "Page size", "20").action(async (opts) => {
|
|
2829
|
-
try {
|
|
2830
|
-
const globalOpts = program.opts();
|
|
2831
|
-
const format = globalOpts.format ?? "json";
|
|
2832
|
-
const params = { page: opts.page, size: opts.size };
|
|
2833
|
-
if (opts.type) params.type = opts.type;
|
|
2834
|
-
const data = await apiRequest("GET", "integrations", {
|
|
2835
|
-
apiKey: globalOpts.apiKey,
|
|
2836
|
-
baseUrl: globalOpts.baseUrl,
|
|
2837
|
-
dryRun: globalOpts.dryRun,
|
|
2838
|
-
verbose: globalOpts.verbose,
|
|
2839
|
-
params
|
|
2840
|
-
});
|
|
2841
|
-
if (format === "table") {
|
|
2842
|
-
printTable(
|
|
2843
|
-
["ID", "Name", "Type", "URL", "Active"],
|
|
2844
|
-
data.content.map((i) => [
|
|
2845
|
-
i.id.substring(0, 8),
|
|
2846
|
-
i.name,
|
|
2847
|
-
i.type,
|
|
2848
|
-
i.baseUrl,
|
|
2849
|
-
i.isActive ? "\u2713" : "\u2717"
|
|
2850
|
-
]),
|
|
2851
|
-
{ truncate: 28 }
|
|
2852
|
-
);
|
|
2853
|
-
console.log(`
|
|
2854
|
-
${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
2855
|
-
} else {
|
|
2856
|
-
printJson(data);
|
|
2857
|
-
}
|
|
2858
|
-
} catch (error) {
|
|
2859
|
-
printError(error);
|
|
2860
|
-
process.exit(1);
|
|
2861
|
-
}
|
|
2862
|
-
});
|
|
2863
|
-
integrations.command("get").description("Get integration detail").requiredOption("--id <integrationId>", "Integration ID").action(async (opts) => {
|
|
2864
|
-
try {
|
|
2865
|
-
const globalOpts = program.opts();
|
|
2866
|
-
const data = await apiRequest("GET", `integrations/${opts.id}`, {
|
|
2867
|
-
apiKey: globalOpts.apiKey,
|
|
2868
|
-
baseUrl: globalOpts.baseUrl,
|
|
2869
|
-
dryRun: globalOpts.dryRun,
|
|
2870
|
-
verbose: globalOpts.verbose
|
|
2871
|
-
});
|
|
2872
|
-
printJson(data);
|
|
2873
|
-
} catch (error) {
|
|
2874
|
-
printError(error);
|
|
2875
|
-
process.exit(1);
|
|
2876
|
-
}
|
|
2877
|
-
});
|
|
2878
|
-
integrations.command("save").description("Create or update an integration").requiredOption("--json <body>", "Request body as JSON string").addHelpText(
|
|
2879
|
-
"after",
|
|
2880
|
-
dedent13`
|
|
2881
|
-
|
|
2882
|
-
Examples:
|
|
2883
|
-
# Create
|
|
2884
|
-
$ lexq integrations save --json '{
|
|
2885
|
-
"type": "WEBHOOK",
|
|
2886
|
-
"name": "Order Processing",
|
|
2887
|
-
"baseUrl": "https://api.example.com/webhooks/orders",
|
|
2888
|
-
"isActive": true
|
|
2889
|
-
}'
|
|
2890
|
-
|
|
2891
|
-
# Update (provide id)
|
|
2892
|
-
$ lexq integrations save --json '{
|
|
2893
|
-
"id": "<existing-id>",
|
|
2894
|
-
"type": "WEBHOOK",
|
|
2895
|
-
"name": "Order Processing (v2)",
|
|
2896
|
-
"baseUrl": "https://api.example.com/v2/webhooks/orders",
|
|
2897
|
-
"isActive": true
|
|
2898
|
-
}'
|
|
2899
|
-
|
|
2900
|
-
Fields:
|
|
2901
|
-
id string Provide to update, omit to create
|
|
2902
|
-
type string COUPON | POINT | NOTIFICATION | CRM | MESSENGER | WEBHOOK (required)
|
|
2903
|
-
name string Integration name (required, unique per tenant)
|
|
2904
|
-
baseUrl string Base URL of the external service (required)
|
|
2905
|
-
credential string API key or token (optional, write-only)
|
|
2906
|
-
additionalConfig object Extra config key-value pairs (optional)
|
|
2907
|
-
isActive boolean Enable/disable [default: true]
|
|
2908
|
-
|
|
2909
|
-
Use "lexq integrations config-spec" to see required fields per type.
|
|
2910
|
-
`
|
|
2911
|
-
).action(async (opts) => {
|
|
2912
|
-
try {
|
|
2913
|
-
const globalOpts = program.opts();
|
|
2914
|
-
const body = JSON.parse(opts.json);
|
|
2915
|
-
const data = await apiRequest("POST", "integrations", {
|
|
2916
|
-
apiKey: globalOpts.apiKey,
|
|
2917
|
-
baseUrl: globalOpts.baseUrl,
|
|
2918
|
-
dryRun: globalOpts.dryRun,
|
|
2919
|
-
verbose: globalOpts.verbose,
|
|
2920
|
-
body
|
|
2921
|
-
});
|
|
2922
|
-
printJson(data);
|
|
2923
|
-
} catch (error) {
|
|
2924
|
-
printError(error);
|
|
2925
|
-
process.exit(1);
|
|
2926
|
-
}
|
|
2927
|
-
});
|
|
2928
|
-
integrations.command("delete").description("Delete an integration").requiredOption("--id <integrationId>", "Integration ID").option("--force", "Skip confirmation prompt").addHelpText(
|
|
2929
|
-
"after",
|
|
2930
|
-
dedent13`
|
|
2931
|
-
|
|
2932
|
-
Rules referencing this integration will fail at execution time.
|
|
2933
|
-
Use --force to skip confirmation.
|
|
2934
|
-
`
|
|
2935
|
-
).action(async (opts) => {
|
|
2936
|
-
try {
|
|
2937
|
-
const globalOpts = program.opts();
|
|
2938
|
-
if (!opts.force) {
|
|
2939
|
-
const { createInterface: createInterface2 } = await import("readline/promises");
|
|
2940
|
-
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
2941
|
-
const answer = await rl.question(`Delete integration ${opts.id}? [y/N] `);
|
|
2942
|
-
rl.close();
|
|
2943
|
-
if (answer.toLowerCase() !== "y") {
|
|
2944
|
-
console.log("Canceled.");
|
|
2945
|
-
return;
|
|
2946
|
-
}
|
|
2947
|
-
}
|
|
2948
|
-
await apiRequest("DELETE", `integrations/${opts.id}`, {
|
|
2949
|
-
apiKey: globalOpts.apiKey,
|
|
2950
|
-
baseUrl: globalOpts.baseUrl,
|
|
2951
|
-
dryRun: globalOpts.dryRun,
|
|
2952
|
-
verbose: globalOpts.verbose
|
|
2953
|
-
});
|
|
2954
|
-
console.log(`\u2713 Integration ${opts.id} deleted.`);
|
|
2955
|
-
} catch (error) {
|
|
2956
|
-
printError(error);
|
|
2957
|
-
process.exit(1);
|
|
2958
|
-
}
|
|
2959
|
-
});
|
|
2960
|
-
integrations.command("config-spec").description("Get integration configuration field specs").addHelpText(
|
|
2961
|
-
"after",
|
|
2962
|
-
dedent13`
|
|
2963
|
-
|
|
2964
|
-
Shows required and optional configuration fields for each integration type.
|
|
2965
|
-
|
|
2966
|
-
Example:
|
|
2967
|
-
$ lexq integrations config-spec
|
|
2968
|
-
`
|
|
2969
|
-
).action(async () => {
|
|
2970
|
-
try {
|
|
2971
|
-
const globalOpts = program.opts();
|
|
2972
|
-
const data = await apiRequest("GET", "integrations/config-spec", {
|
|
2973
|
-
apiKey: globalOpts.apiKey,
|
|
2974
|
-
baseUrl: globalOpts.baseUrl,
|
|
2975
|
-
dryRun: globalOpts.dryRun,
|
|
2976
|
-
verbose: globalOpts.verbose
|
|
2977
|
-
});
|
|
2978
|
-
printJson(data);
|
|
2979
|
-
} catch (error) {
|
|
2980
|
-
printError(error);
|
|
2981
|
-
process.exit(1);
|
|
2982
|
-
}
|
|
2983
|
-
});
|
|
2984
|
-
}
|
|
2985
|
-
|
|
2986
2810
|
// src/commands/logs.ts
|
|
2987
2811
|
import "commander";
|
|
2988
|
-
import
|
|
2812
|
+
import dedent13 from "dedent";
|
|
2989
2813
|
function registerLogCommands(program) {
|
|
2990
2814
|
const logs = program.command("logs").description("Failure logs").addHelpText(
|
|
2991
2815
|
"after",
|
|
2992
|
-
|
|
2816
|
+
dedent13`
|
|
2993
2817
|
|
|
2994
|
-
System failure logs (DLQ) for background tasks —
|
|
2995
|
-
point operations, notifications, and platform event webhooks.
|
|
2818
|
+
System failure logs (DLQ) for background tasks — platform event webhooks and scheduled deployments.
|
|
2996
2819
|
|
|
2997
2820
|
Commands:
|
|
2998
2821
|
list List failure logs with filters
|
|
2999
|
-
get Get failure log detail (includes
|
|
3000
|
-
action Process a single log (
|
|
2822
|
+
get Get failure log detail (includes the original payload)
|
|
2823
|
+
action Process a single log (IGNORE, RESOLVE)
|
|
3001
2824
|
bulk-action Process multiple logs at once
|
|
3002
2825
|
|
|
3003
2826
|
Statuses: PENDING (needs attention), RESOLVED, IGNORED
|
|
3004
|
-
Categories: INTEGRATION (external), INTERNAL (system)
|
|
3005
2827
|
`
|
|
3006
2828
|
);
|
|
3007
|
-
logs.command("list").description("List failure logs").option("--
|
|
2829
|
+
logs.command("list").description("List failure logs").option("--task-type <taskType>", `Filter by task type (${TaskType.join(", ")})`).option("--status <status>", "Filter by status (PENDING, RESOLVED, IGNORED)").option("--keyword <keyword>", "Search keyword").option("--start-date <date>", "Start date (yyyy-MM-dd)").option("--end-date <date>", "End date (yyyy-MM-dd)").option("--page <number>", "Page number", "0").option("--size <number>", "Page size", "20").addHelpText(
|
|
3008
2830
|
"after",
|
|
3009
|
-
|
|
2831
|
+
dedent13`
|
|
3010
2832
|
|
|
3011
2833
|
Examples:
|
|
3012
2834
|
$ lexq logs list --status PENDING --format table
|
|
3013
|
-
$ lexq logs list --task-type PLATFORM_WEBHOOK
|
|
2835
|
+
$ lexq logs list --task-type PLATFORM_WEBHOOK
|
|
3014
2836
|
$ lexq logs list --keyword "timeout" --start-date 2026-04-01
|
|
3015
2837
|
`
|
|
3016
2838
|
).action(async (opts) => {
|
|
@@ -3018,7 +2840,6 @@ function registerLogCommands(program) {
|
|
|
3018
2840
|
const globalOpts = program.opts();
|
|
3019
2841
|
const format = globalOpts.format ?? "json";
|
|
3020
2842
|
const params = { page: opts.page, size: opts.size };
|
|
3021
|
-
if (opts.category) params.category = opts.category;
|
|
3022
2843
|
if (opts.taskType) params.taskType = opts.taskType;
|
|
3023
2844
|
if (opts.status) params.status = opts.status;
|
|
3024
2845
|
if (opts.keyword) params.searchKeyword = opts.keyword;
|
|
@@ -3033,13 +2854,11 @@ function registerLogCommands(program) {
|
|
|
3033
2854
|
});
|
|
3034
2855
|
if (format === "table") {
|
|
3035
2856
|
printTable(
|
|
3036
|
-
["ID", "
|
|
2857
|
+
["ID", "Task", "Status", "Error", "Created"],
|
|
3037
2858
|
data.content.map((l) => [
|
|
3038
2859
|
l.id.substring(0, 8),
|
|
3039
|
-
l.category,
|
|
3040
2860
|
l.taskType,
|
|
3041
2861
|
l.status,
|
|
3042
|
-
String(l.retryCount),
|
|
3043
2862
|
l.errorMessage?.substring(0, 24) ?? "\u2013",
|
|
3044
2863
|
l.createdAt.substring(0, 16)
|
|
3045
2864
|
]),
|
|
@@ -3057,10 +2876,10 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
3057
2876
|
});
|
|
3058
2877
|
logs.command("get").description("Get failure log detail").requiredOption("--id <logId>", "Log ID").addHelpText(
|
|
3059
2878
|
"after",
|
|
3060
|
-
|
|
2879
|
+
dedent13`
|
|
3061
2880
|
|
|
3062
2881
|
Includes the full payload that was used for the failed operation.
|
|
3063
|
-
Use this to inspect what went wrong before deciding to
|
|
2882
|
+
Use this to inspect what went wrong before deciding to IGNORE or RESOLVE.
|
|
3064
2883
|
`
|
|
3065
2884
|
).action(async (opts) => {
|
|
3066
2885
|
try {
|
|
@@ -3077,17 +2896,16 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
3077
2896
|
process.exit(1);
|
|
3078
2897
|
}
|
|
3079
2898
|
});
|
|
3080
|
-
logs.command("action").description("Process a failure log action (
|
|
2899
|
+
logs.command("action").description("Process a failure log action (IGNORE, RESOLVE)").requiredOption("--id <logId>", "Log ID").requiredOption("--action <action>", "Action: IGNORE or RESOLVE").addHelpText(
|
|
3081
2900
|
"after",
|
|
3082
|
-
|
|
2901
|
+
dedent13`
|
|
3083
2902
|
|
|
3084
2903
|
Actions:
|
|
3085
|
-
RETRY Re-execute the failed operation with the original payload
|
|
3086
2904
|
IGNORE Mark as intentionally skipped (won't appear in PENDING)
|
|
3087
2905
|
RESOLVE Mark as manually resolved (e.g., fixed via external system)
|
|
3088
2906
|
|
|
3089
2907
|
Example:
|
|
3090
|
-
$ lexq logs action --id <logId> --action
|
|
2908
|
+
$ lexq logs action --id <logId> --action RESOLVE
|
|
3091
2909
|
`
|
|
3092
2910
|
).action(async (opts) => {
|
|
3093
2911
|
try {
|
|
@@ -3109,9 +2927,9 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
3109
2927
|
process.exit(1);
|
|
3110
2928
|
}
|
|
3111
2929
|
});
|
|
3112
|
-
logs.command("bulk-action").description("Bulk process failure logs").requiredOption("--ids <logIds>", "Comma-separated log IDs").requiredOption("--action <action>", "Action:
|
|
2930
|
+
logs.command("bulk-action").description("Bulk process failure logs").requiredOption("--ids <logIds>", "Comma-separated log IDs").requiredOption("--action <action>", "Action: IGNORE or RESOLVE").addHelpText(
|
|
3113
2931
|
"after",
|
|
3114
|
-
|
|
2932
|
+
dedent13`
|
|
3115
2933
|
|
|
3116
2934
|
Processes each log individually. Failures are skipped with a warning.
|
|
3117
2935
|
|
|
@@ -3139,11 +2957,11 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
3139
2957
|
|
|
3140
2958
|
// src/commands/webhook-subscriptions.ts
|
|
3141
2959
|
import "commander";
|
|
3142
|
-
import
|
|
2960
|
+
import dedent14 from "dedent";
|
|
3143
2961
|
function registerWebhookSubscriptionCommands(program) {
|
|
3144
2962
|
const webhooks = program.command("webhook-subscriptions").description("Manage platform event webhook subscriptions").addHelpText(
|
|
3145
2963
|
"after",
|
|
3146
|
-
|
|
2964
|
+
dedent14`
|
|
3147
2965
|
|
|
3148
2966
|
Receive notifications when deployment lifecycle events occur
|
|
3149
2967
|
(publish, deploy, rollback, undeploy).
|
|
@@ -3217,7 +3035,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
3217
3035
|
});
|
|
3218
3036
|
webhooks.command("save").description("Create or update a webhook subscription").requiredOption("--json <body>", "Request body as JSON string").addHelpText(
|
|
3219
3037
|
"after",
|
|
3220
|
-
|
|
3038
|
+
dedent14`
|
|
3221
3039
|
|
|
3222
3040
|
Examples:
|
|
3223
3041
|
# Create (Slack format)
|
|
@@ -3272,7 +3090,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
3272
3090
|
});
|
|
3273
3091
|
webhooks.command("delete").description("Delete a webhook subscription").requiredOption("--id <subscriptionId>", "Subscription ID").option("--force", "Skip confirmation prompt").addHelpText(
|
|
3274
3092
|
"after",
|
|
3275
|
-
|
|
3093
|
+
dedent14`
|
|
3276
3094
|
|
|
3277
3095
|
Use --force to skip the confirmation prompt.
|
|
3278
3096
|
|
|
@@ -3306,7 +3124,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
3306
3124
|
});
|
|
3307
3125
|
webhooks.command("test").description("Send a test event to verify webhook connectivity").requiredOption("--id <subscriptionId>", "Subscription ID").addHelpText(
|
|
3308
3126
|
"after",
|
|
3309
|
-
|
|
3127
|
+
dedent14`
|
|
3310
3128
|
|
|
3311
3129
|
Sends a test event to the webhook URL and reports the HTTP status code.
|
|
3312
3130
|
Does not record failures in the failure log.
|
|
@@ -3342,7 +3160,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
3342
3160
|
|
|
3343
3161
|
// src/commands/serve.ts
|
|
3344
3162
|
import "commander";
|
|
3345
|
-
import
|
|
3163
|
+
import dedent17 from "dedent";
|
|
3346
3164
|
|
|
3347
3165
|
// src/mcp/server.ts
|
|
3348
3166
|
import { readFileSync as readFileSync3 } from "fs";
|
|
@@ -3476,8 +3294,8 @@ function registerGroupTools(server, callApi) {
|
|
|
3476
3294
|
inputSchema: {
|
|
3477
3295
|
name: z.string().describe("Group name (unique among non-ARCHIVED)"),
|
|
3478
3296
|
description: z.string().optional().describe("Group description"),
|
|
3479
|
-
activationMode: z.enum(
|
|
3480
|
-
activationStrategy: z.enum(
|
|
3297
|
+
activationMode: z.enum(ConflictResolutionMode).optional().describe("Conflict resolution mode"),
|
|
3298
|
+
activationStrategy: z.enum(ConflictResolutionStrategy).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
|
|
3481
3299
|
executionLimit: z.number().int().min(1).optional().describe("Max rule executions (required when mode is MAX_N)"),
|
|
3482
3300
|
activationGroup: z.string().optional().describe("Activation group name")
|
|
3483
3301
|
}
|
|
@@ -3505,8 +3323,8 @@ function registerGroupTools(server, callApi) {
|
|
|
3505
3323
|
description: z.string().optional().describe("New description"),
|
|
3506
3324
|
status: z.enum(["ACTIVE", "DISABLED"]).optional().describe("Status (DISABLED = emergency stop)"),
|
|
3507
3325
|
activationGroup: z.string().optional().describe("Activation group (Execution Group) cluster key"),
|
|
3508
|
-
activationMode: z.enum(
|
|
3509
|
-
activationStrategy: z.enum(
|
|
3326
|
+
activationMode: z.enum(ConflictResolutionMode).optional().describe("Conflict resolution mode"),
|
|
3327
|
+
activationStrategy: z.enum(ConflictResolutionStrategy).optional().describe("Strategy"),
|
|
3510
3328
|
executionLimit: z.number().int().min(1).optional().describe("Max rule executions")
|
|
3511
3329
|
}
|
|
3512
3330
|
},
|
|
@@ -3669,7 +3487,7 @@ function registerVersionTools(server, callApi) {
|
|
|
3669
3487
|
|
|
3670
3488
|
// src/mcp/tools/rules.ts
|
|
3671
3489
|
import { z as z3 } from "zod";
|
|
3672
|
-
import
|
|
3490
|
+
import dedent15 from "dedent";
|
|
3673
3491
|
function registerRuleTools(server, callApi) {
|
|
3674
3492
|
server.registerTool(
|
|
3675
3493
|
"lexq_rules_list",
|
|
@@ -3700,7 +3518,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3700
3518
|
"lexq_rules_create",
|
|
3701
3519
|
{
|
|
3702
3520
|
title: "Create Rule",
|
|
3703
|
-
description:
|
|
3521
|
+
description: dedent15`
|
|
3704
3522
|
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.
|
|
3705
3523
|
|
|
3706
3524
|
Before creating rules with new fact keys, call lexq_facts_list to check existing facts.
|
|
@@ -3712,26 +3530,54 @@ function registerRuleTools(server, callApi) {
|
|
|
3712
3530
|
defined (non-blocking, version-wide) — use it to decide what to register.
|
|
3713
3531
|
|
|
3714
3532
|
Condition: { type: "SINGLE", field, operator, value, valueType } or { type: "GROUP", operator: "AND"|"OR", children: [...] }
|
|
3715
|
-
Operators: EQUALS, NOT_EQUALS, GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, CONTAINS, IN, NOT_IN
|
|
3716
3533
|
Value types: STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER
|
|
3534
|
+
|
|
3535
|
+
Operators are constrained by the LEFT fact's type (from lexq_facts_list). Using one outside
|
|
3536
|
+
its type is rejected by the server — check the fact type before choosing an operator.
|
|
3537
|
+
- STRING fact: EQUALS, NOT_EQUALS, CONTAINS, IN, NOT_IN
|
|
3538
|
+
- NUMBER fact: EQUALS, NOT_EQUALS, GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, IN, NOT_IN
|
|
3539
|
+
- BOOLEAN fact: EQUALS, NOT_EQUALS
|
|
3540
|
+
- LIST_* fact: HAS_ANY, HAS_ALL, HAS_NONE (only these)
|
|
3541
|
+
|
|
3542
|
+
HAS_* query list-typed facts. Value is always an array whose element type matches the fact:
|
|
3543
|
+
- HAS_ANY: fact has at least one of the given values
|
|
3544
|
+
- HAS_ALL: fact has all of the given values
|
|
3545
|
+
- HAS_NONE: fact has none of the given values
|
|
3546
|
+
Example: { "type": "SINGLE", "field": "user_tags", "operator": "HAS_ANY", "value": ["VIP","GOLD"], "valueType": "LIST_STRING" }
|
|
3547
|
+
|
|
3548
|
+
Do NOT use CONTAINS on a list fact — CONTAINS is substring match on STRING facts only.
|
|
3549
|
+
IN is the mirror of HAS_*: IN takes a scalar fact with a list value; HAS_* takes lists on both sides.
|
|
3717
3550
|
|
|
3718
3551
|
Actions: [{ type, parameters }]
|
|
3719
3552
|
|
|
3720
3553
|
Action parameter schemas:
|
|
3721
|
-
- MUTATE_FACT: {
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3554
|
+
- MUTATE_FACT: { targetVar: string, operator: "ASSIGN"|"ADD"|"SUB"|"MUL"|"DIV", method: "PERCENTAGE"|"AMOUNT", operand: number, refVar?: string, rounding?: RoundingOption }
|
|
3555
|
+
targetVar is the fact this action reads and writes. It must exist in facts at execution
|
|
3556
|
+
time as a number — supplied as an input fact or written by a prior action in this rule.
|
|
3557
|
+
A missing required fact throws (no 0 default).
|
|
3558
|
+
operand is the arithmetic operand; the unit is dictated by method (percent when
|
|
3559
|
+
PERCENTAGE, absolute amount when AMOUNT). Ranges are not constrained — negative values
|
|
3560
|
+
and >100 percentages are valid (refunds, surcharges).
|
|
3561
|
+
refVar is the base for percentage calculation and is OPTIONAL — omit it to use targetVar
|
|
3562
|
+
itself. It is only meaningful in PERCENTAGE × {ASSIGN, ADD, SUB}; specifying it in any
|
|
3563
|
+
other cell is an error. Use it when the base differs from the target, e.g.
|
|
3564
|
+
"points += order_total × 5%" → { targetVar: "points", refVar: "order_total",
|
|
3565
|
+
operator: "ADD", method: "PERCENTAGE", operand: 5 }.
|
|
3566
|
+
operator × method matrix:
|
|
3567
|
+
ASSIGN targetVar = operand | targetVar = refVar × operand/100
|
|
3568
|
+
ADD targetVar += operand | targetVar += refVar × operand/100
|
|
3569
|
+
SUB targetVar -= operand | targetVar -= refVar × operand/100
|
|
3570
|
+
MUL targetVar *= operand | targetVar *= (operand/100 + 1)
|
|
3571
|
+
DIV targetVar /= operand | invalid
|
|
3572
|
+
Constraints: DIV + PERCENTAGE is invalid (use MUL with the inverse). DIV + AMOUNT
|
|
3573
|
+
requires operand !== 0.
|
|
3574
|
+
- SET_FACT: { targetVar: string, value: string|number|boolean } Creates the fact if absent
|
|
3575
|
+
— this is the only action that does. MUTATE_FACT requires the target to already exist.
|
|
3576
|
+
- BLOCK: { reason: string } Records a rejection decision. It does NOT halt rule execution —
|
|
3577
|
+
subsequent actions and subsequent winning rules still run. Enforcement is the caller's
|
|
3578
|
+
responsibility; the decision surfaces as the is_blocked fact.
|
|
3579
|
+
|
|
3580
|
+
RoundingOption (optional, MUTATE_FACT only): { scale: integer (0..16), mode?: "HALF_UP"|"HALF_DOWN"|"HALF_EVEN"|"FLOOR"|"CEILING"|"DOWN"|"UP" } mode defaults to HALF_UP. When omitted, calculator output is preserved at full precision (lossless).
|
|
3735
3581
|
`,
|
|
3736
3582
|
inputSchema: {
|
|
3737
3583
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
@@ -3894,7 +3740,7 @@ function registerFactTools(server, callApi) {
|
|
|
3894
3740
|
"lexq_facts_action_metadata",
|
|
3895
3741
|
{
|
|
3896
3742
|
title: "Get Action Runtime Fact Metadata",
|
|
3897
|
-
description: "Retrieve runtime fact requirements per Action type. For each Action, shows which input facts must be present in the execution payload
|
|
3743
|
+
description: "Retrieve runtime fact requirements per Action type. For each Action, shows which input facts must be present in the execution payload \u2014 e.g. MUTATE_FACT always requires its targetVar fact, plus refVar when one is specified. The factRequired flag describes the FACT, not the parameter: refVar is an optional parameter, but if you specify it the named fact must exist. A required fact absent at runtime throws \u2014 the engine never defaults to 0. Facts are supplied as input or written by a prior action in the same rule; only SET_FACT creates a fact from nothing. Static data, safe to cache in-session.",
|
|
3898
3744
|
inputSchema: {}
|
|
3899
3745
|
},
|
|
3900
3746
|
async () => callApi("GET", "schema/action-metadata")
|
|
@@ -4082,13 +3928,13 @@ function registerDeployTools(server, callApi) {
|
|
|
4082
3928
|
|
|
4083
3929
|
// src/mcp/tools/analytics.ts
|
|
4084
3930
|
import { z as z6 } from "zod";
|
|
4085
|
-
import
|
|
3931
|
+
import dedent16 from "dedent";
|
|
4086
3932
|
function registerAnalyticsTools(server, callApi) {
|
|
4087
3933
|
server.registerTool(
|
|
4088
3934
|
"lexq_dry_run",
|
|
4089
3935
|
{
|
|
4090
3936
|
title: "Dry Run",
|
|
4091
|
-
description:
|
|
3937
|
+
description: dedent16`
|
|
4092
3938
|
Execute a single dry run against a version. Tests how rules evaluate given input facts without side effects.
|
|
4093
3939
|
|
|
4094
3940
|
Returns:
|
|
@@ -4103,14 +3949,13 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4103
3949
|
inputSchema: {
|
|
4104
3950
|
versionId: z6.string().uuid().describe("Policy version ID to test against"),
|
|
4105
3951
|
facts: z6.string().describe('JSON string of facts object, e.g. {"payment_amount":100000}'),
|
|
4106
|
-
includeDebugInfo: z6.boolean().default(true).describe("Include execution and decision traces")
|
|
4107
|
-
mockExternalCalls: z6.boolean().default(true).describe("Mock external integration calls")
|
|
3952
|
+
includeDebugInfo: z6.boolean().default(true).describe("Include execution and decision traces")
|
|
4108
3953
|
}
|
|
4109
3954
|
},
|
|
4110
|
-
async ({ versionId, facts, includeDebugInfo
|
|
3955
|
+
async ({ versionId, facts, includeDebugInfo }) => {
|
|
4111
3956
|
const parsedFacts = JSON.parse(facts);
|
|
4112
3957
|
return callApi("POST", `analytics/dry-run/versions/${versionId}`, {
|
|
4113
|
-
body: { facts: parsedFacts, includeDebugInfo
|
|
3958
|
+
body: { facts: parsedFacts, includeDebugInfo }
|
|
4114
3959
|
});
|
|
4115
3960
|
}
|
|
4116
3961
|
);
|
|
@@ -4118,7 +3963,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4118
3963
|
"lexq_dry_run_compare",
|
|
4119
3964
|
{
|
|
4120
3965
|
title: "Dry Run Compare",
|
|
4121
|
-
description:
|
|
3966
|
+
description: dedent16`
|
|
4122
3967
|
Compare dry run results between two versions using the same input facts. Useful for validating changes.
|
|
4123
3968
|
|
|
4124
3969
|
Returns:
|
|
@@ -4154,7 +3999,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4154
3999
|
"lexq_simulation_start",
|
|
4155
4000
|
{
|
|
4156
4001
|
title: "Start Simulation",
|
|
4157
|
-
description:
|
|
4002
|
+
description: dedent16`
|
|
4158
4003
|
Start an Impact Simulation against historical, uploaded, or inline data.
|
|
4159
4004
|
|
|
4160
4005
|
dataset.type and dataset.source are BOTH required, and must be paired:
|
|
@@ -4252,7 +4097,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4252
4097
|
"lexq_dataset_upload",
|
|
4253
4098
|
{
|
|
4254
4099
|
title: "Upload Dataset",
|
|
4255
|
-
description:
|
|
4100
|
+
description: dedent16`
|
|
4256
4101
|
Upload inline CSV or JSON content as a simulation dataset.
|
|
4257
4102
|
The content is uploaded to S3 and a path is returned in the "path" field.
|
|
4258
4103
|
|
|
@@ -4524,102 +4369,26 @@ function registerProvenanceTools(server, callApi) {
|
|
|
4524
4369
|
);
|
|
4525
4370
|
}
|
|
4526
4371
|
|
|
4527
|
-
// src/mcp/tools/integrations.ts
|
|
4528
|
-
import { z as z11 } from "zod";
|
|
4529
|
-
function registerIntegrationTools(server, callApi) {
|
|
4530
|
-
server.registerTool(
|
|
4531
|
-
"lexq_integrations_list",
|
|
4532
|
-
{
|
|
4533
|
-
title: "List Integrations",
|
|
4534
|
-
description: "List all external integrations (webhooks, CRM, notification, etc.).",
|
|
4535
|
-
inputSchema: {
|
|
4536
|
-
page: z11.number().int().min(0).default(0).describe("Page number"),
|
|
4537
|
-
size: z11.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
4538
|
-
type: z11.enum(["COUPON", "POINT", "NOTIFICATION", "CRM", "MESSENGER", "WEBHOOK"]).optional().describe("Filter by integration type")
|
|
4539
|
-
}
|
|
4540
|
-
},
|
|
4541
|
-
async ({ page, size, type }) => {
|
|
4542
|
-
const params = paginationParams(page, size);
|
|
4543
|
-
if (type) params.type = type;
|
|
4544
|
-
return callApi("GET", "integrations", { params });
|
|
4545
|
-
}
|
|
4546
|
-
);
|
|
4547
|
-
server.registerTool(
|
|
4548
|
-
"lexq_integrations_get",
|
|
4549
|
-
{
|
|
4550
|
-
title: "Get Integration",
|
|
4551
|
-
description: "Get integration detail by ID.",
|
|
4552
|
-
inputSchema: {
|
|
4553
|
-
integrationId: z11.string().uuid().describe("Integration ID")
|
|
4554
|
-
}
|
|
4555
|
-
},
|
|
4556
|
-
async ({ integrationId }) => callApi("GET", `integrations/${integrationId}`)
|
|
4557
|
-
);
|
|
4558
|
-
server.registerTool(
|
|
4559
|
-
"lexq_integrations_save",
|
|
4560
|
-
{
|
|
4561
|
-
title: "Save Integration",
|
|
4562
|
-
description: "Create or update an integration. Provide id to update an existing one; omit id to create new. Types: COUPON, POINT, NOTIFICATION, CRM, MESSENGER, WEBHOOK.",
|
|
4563
|
-
inputSchema: {
|
|
4564
|
-
id: z11.string().uuid().optional().describe("Integration ID (omit to create, provide to update)"),
|
|
4565
|
-
type: z11.enum(["COUPON", "POINT", "NOTIFICATION", "CRM", "MESSENGER", "WEBHOOK"]).describe("Integration type"),
|
|
4566
|
-
name: z11.string().describe("Integration name"),
|
|
4567
|
-
baseUrl: z11.string().describe("Base URL of the external service"),
|
|
4568
|
-
credential: z11.string().optional().describe("API key or token for the service"),
|
|
4569
|
-
additionalConfig: z11.string().optional().describe("JSON string of additional config key-value pairs"),
|
|
4570
|
-
isActive: z11.boolean().default(true).describe("Whether the integration is active")
|
|
4571
|
-
}
|
|
4572
|
-
},
|
|
4573
|
-
async ({ additionalConfig, ...rest }) => {
|
|
4574
|
-
const body = { ...rest };
|
|
4575
|
-
if (additionalConfig) body.additionalConfig = JSON.parse(additionalConfig);
|
|
4576
|
-
return callApi("POST", "integrations", { body });
|
|
4577
|
-
}
|
|
4578
|
-
);
|
|
4579
|
-
server.registerTool(
|
|
4580
|
-
"lexq_integrations_delete",
|
|
4581
|
-
{
|
|
4582
|
-
title: "Delete Integration",
|
|
4583
|
-
description: "Delete an integration by ID.",
|
|
4584
|
-
inputSchema: {
|
|
4585
|
-
integrationId: z11.string().uuid().describe("Integration ID")
|
|
4586
|
-
}
|
|
4587
|
-
},
|
|
4588
|
-
async ({ integrationId }) => callApi("DELETE", `integrations/${integrationId}`)
|
|
4589
|
-
);
|
|
4590
|
-
server.registerTool(
|
|
4591
|
-
"lexq_integrations_config_spec",
|
|
4592
|
-
{
|
|
4593
|
-
title: "Integration Config Spec",
|
|
4594
|
-
description: "Show available integration types and their required configuration fields.",
|
|
4595
|
-
inputSchema: {}
|
|
4596
|
-
},
|
|
4597
|
-
async () => callApi("GET", "integrations/config-spec")
|
|
4598
|
-
);
|
|
4599
|
-
}
|
|
4600
|
-
|
|
4601
4372
|
// src/mcp/tools/logs.ts
|
|
4602
|
-
import { z as
|
|
4373
|
+
import { z as z11 } from "zod";
|
|
4603
4374
|
function registerLogTools(server, callApi) {
|
|
4604
4375
|
server.registerTool(
|
|
4605
4376
|
"lexq_logs_list",
|
|
4606
4377
|
{
|
|
4607
4378
|
title: "List Failure Logs",
|
|
4608
|
-
description: "List system failure logs from background tasks (
|
|
4379
|
+
description: "List system failure logs from background tasks (platform event webhooks, scheduled deployments).",
|
|
4609
4380
|
inputSchema: {
|
|
4610
|
-
page:
|
|
4611
|
-
size:
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
endDate: z12.string().optional().describe("End date (yyyy-MM-dd)")
|
|
4381
|
+
page: z11.number().int().min(0).default(0).describe("Page number"),
|
|
4382
|
+
size: z11.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
4383
|
+
taskType: z11.enum(TaskType).optional().describe("Task type"),
|
|
4384
|
+
status: z11.enum(FailureStatus).optional().describe("Log status"),
|
|
4385
|
+
keyword: z11.string().optional().describe("Search in refId, refSubId, errorMessage"),
|
|
4386
|
+
startDate: z11.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
4387
|
+
endDate: z11.string().optional().describe("End date (yyyy-MM-dd)")
|
|
4618
4388
|
}
|
|
4619
4389
|
},
|
|
4620
|
-
async ({ page, size,
|
|
4390
|
+
async ({ page, size, taskType, status, keyword, startDate, endDate }) => {
|
|
4621
4391
|
const params = paginationParams(page, size);
|
|
4622
|
-
if (category) params.category = category;
|
|
4623
4392
|
if (taskType) params.taskType = taskType;
|
|
4624
4393
|
if (status) params.status = status;
|
|
4625
4394
|
if (keyword) params.keyword = keyword;
|
|
@@ -4634,7 +4403,7 @@ function registerLogTools(server, callApi) {
|
|
|
4634
4403
|
title: "Get Failure Log",
|
|
4635
4404
|
description: "Get failure log detail by ID.",
|
|
4636
4405
|
inputSchema: {
|
|
4637
|
-
logId:
|
|
4406
|
+
logId: z11.string().uuid().describe("Failure log ID")
|
|
4638
4407
|
}
|
|
4639
4408
|
},
|
|
4640
4409
|
async ({ logId }) => callApi("GET", `failure-logs/${logId}`)
|
|
@@ -4643,10 +4412,10 @@ function registerLogTools(server, callApi) {
|
|
|
4643
4412
|
"lexq_logs_action",
|
|
4644
4413
|
{
|
|
4645
4414
|
title: "Process Failure Log",
|
|
4646
|
-
description: "Process a single failure log:
|
|
4415
|
+
description: "Process a single failure log: RESOLVE (mark as manually fixed) or IGNORE (skip intentionally).",
|
|
4647
4416
|
inputSchema: {
|
|
4648
|
-
logId:
|
|
4649
|
-
action:
|
|
4417
|
+
logId: z11.string().uuid().describe("Failure log ID"),
|
|
4418
|
+
action: z11.enum(FailureAction).describe("Action to take")
|
|
4650
4419
|
}
|
|
4651
4420
|
},
|
|
4652
4421
|
async ({ logId, action }) => callApi("POST", `failure-logs/${logId}/actions`, {
|
|
@@ -4659,8 +4428,8 @@ function registerLogTools(server, callApi) {
|
|
|
4659
4428
|
title: "Bulk Process Failure Logs",
|
|
4660
4429
|
description: "Process multiple failure logs at once. Provide an array of log IDs and the action.",
|
|
4661
4430
|
inputSchema: {
|
|
4662
|
-
logIds:
|
|
4663
|
-
action:
|
|
4431
|
+
logIds: z11.array(z11.string().uuid()).describe("Array of failure log IDs"),
|
|
4432
|
+
action: z11.enum(FailureAction).describe("Action to apply to all logs")
|
|
4664
4433
|
}
|
|
4665
4434
|
},
|
|
4666
4435
|
async ({ logIds, action }) => callApi("POST", "failure-logs/bulk-actions", {
|
|
@@ -4670,7 +4439,7 @@ function registerLogTools(server, callApi) {
|
|
|
4670
4439
|
}
|
|
4671
4440
|
|
|
4672
4441
|
// src/mcp/tools/webhook-subscriptions.ts
|
|
4673
|
-
import { z as
|
|
4442
|
+
import { z as z12 } from "zod";
|
|
4674
4443
|
function registerWebhookSubscriptionTools(server, callApi) {
|
|
4675
4444
|
server.registerTool(
|
|
4676
4445
|
"lexq_webhook_subscriptions_list",
|
|
@@ -4678,8 +4447,8 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4678
4447
|
title: "List Webhook Subscriptions",
|
|
4679
4448
|
description: "List platform event webhook subscriptions. These receive deployment lifecycle notifications (publish, deploy, rollback, undeploy).",
|
|
4680
4449
|
inputSchema: {
|
|
4681
|
-
page:
|
|
4682
|
-
size:
|
|
4450
|
+
page: z12.number().int().min(0).default(0).describe("Page number"),
|
|
4451
|
+
size: z12.number().int().min(1).max(100).default(20).describe("Page size")
|
|
4683
4452
|
}
|
|
4684
4453
|
},
|
|
4685
4454
|
async ({ page, size }) => {
|
|
@@ -4693,7 +4462,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4693
4462
|
title: "Get Webhook Subscription",
|
|
4694
4463
|
description: "Get webhook subscription detail by ID.",
|
|
4695
4464
|
inputSchema: {
|
|
4696
|
-
id:
|
|
4465
|
+
id: z12.string().uuid().describe("Webhook subscription ID")
|
|
4697
4466
|
}
|
|
4698
4467
|
},
|
|
4699
4468
|
async ({ id }) => callApi("GET", `webhook-subscriptions/${id}`)
|
|
@@ -4704,13 +4473,13 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4704
4473
|
title: "Save Webhook Subscription",
|
|
4705
4474
|
description: 'Create or update a webhook subscription. Omit id to create, provide id to update. Events: VERSION_PUBLISHED, DEPLOYED, ROLLED_BACK, UNDEPLOYED. Formats: GENERIC (full JSON), SLACK ({"text": "..."}).',
|
|
4706
4475
|
inputSchema: {
|
|
4707
|
-
id:
|
|
4708
|
-
name:
|
|
4709
|
-
webhookUrl:
|
|
4710
|
-
subscribedEvents:
|
|
4711
|
-
payloadFormat:
|
|
4712
|
-
secret:
|
|
4713
|
-
isActive:
|
|
4476
|
+
id: z12.string().uuid().optional().describe("Subscription ID (omit to create, provide to update)"),
|
|
4477
|
+
name: z12.string().min(1).describe("Subscription name (unique per tenant)"),
|
|
4478
|
+
webhookUrl: z12.string().url().describe("Webhook endpoint URL"),
|
|
4479
|
+
subscribedEvents: z12.array(z12.enum(PlatformEventType)).min(1).describe("Events to subscribe to"),
|
|
4480
|
+
payloadFormat: z12.enum(WebhookPayloadFormat).optional().default("GENERIC").describe("Payload format"),
|
|
4481
|
+
secret: z12.string().optional().describe("HMAC-SHA256 signing secret"),
|
|
4482
|
+
isActive: z12.boolean().optional().default(true).describe("Whether the subscription is active")
|
|
4714
4483
|
}
|
|
4715
4484
|
},
|
|
4716
4485
|
async ({ ...body }) => callApi("POST", "webhook-subscriptions", { body })
|
|
@@ -4721,7 +4490,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4721
4490
|
title: "Delete Webhook Subscription",
|
|
4722
4491
|
description: "Delete a webhook subscription by ID.",
|
|
4723
4492
|
inputSchema: {
|
|
4724
|
-
id:
|
|
4493
|
+
id: z12.string().uuid().describe("Webhook subscription ID")
|
|
4725
4494
|
}
|
|
4726
4495
|
},
|
|
4727
4496
|
async ({ id }) => callApi("DELETE", `webhook-subscriptions/${id}`)
|
|
@@ -4732,7 +4501,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4732
4501
|
title: "Test Webhook Subscription",
|
|
4733
4502
|
description: "Send a test event to verify webhook connectivity. Returns the HTTP status code and success/failure message.",
|
|
4734
4503
|
inputSchema: {
|
|
4735
|
-
id:
|
|
4504
|
+
id: z12.string().uuid().describe("Webhook subscription ID")
|
|
4736
4505
|
}
|
|
4737
4506
|
},
|
|
4738
4507
|
async ({ id }) => callApi("POST", `webhook-subscriptions/${id}/test`)
|
|
@@ -4740,7 +4509,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4740
4509
|
}
|
|
4741
4510
|
|
|
4742
4511
|
// src/mcp/tools/domain-templates.ts
|
|
4743
|
-
import { z as
|
|
4512
|
+
import { z as z13 } from "zod";
|
|
4744
4513
|
function registerDomainTemplateTools(server, callApi) {
|
|
4745
4514
|
server.registerTool(
|
|
4746
4515
|
"lexq_domain_templates_list",
|
|
@@ -4757,7 +4526,7 @@ function registerDomainTemplateTools(server, callApi) {
|
|
|
4757
4526
|
title: "Preview Domain Template",
|
|
4758
4527
|
description: "Preview exactly what a domain template will provision before applying it: the fact definitions it registers, the sample rules it creates, and an apply plan. This is a read-only dry run \u2014 nothing is created. Only ACTIVE templates can be previewed.",
|
|
4759
4528
|
inputSchema: {
|
|
4760
|
-
template:
|
|
4529
|
+
template: z13.string().describe(
|
|
4761
4530
|
"Domain template key (e.g. ECOMMERCE). Use lexq_domain_templates_list to see available keys \u2014 currently only ECOMMERCE is ACTIVE."
|
|
4762
4531
|
)
|
|
4763
4532
|
}
|
|
@@ -4770,8 +4539,8 @@ function registerDomainTemplateTools(server, callApi) {
|
|
|
4770
4539
|
title: "Apply Domain Template",
|
|
4771
4540
|
description: "Apply a domain template to the current tenant. Creates the template's fact definitions and a new policy group pre-populated with its sample rules as a DRAFT version. Existing facts are skipped \u2014 apply is additive and never overwrites existing schema. Run lexq_domain_templates_preview first to review what will be created. Only ACTIVE templates can be applied.",
|
|
4772
4541
|
inputSchema: {
|
|
4773
|
-
template:
|
|
4774
|
-
customName:
|
|
4542
|
+
template: z13.string().describe("Domain template key to apply (e.g. ECOMMERCE)."),
|
|
4543
|
+
customName: z13.string().optional().describe(
|
|
4775
4544
|
"Optional custom name for the policy group that gets created. If omitted, the template's default name is used."
|
|
4776
4545
|
)
|
|
4777
4546
|
}
|
|
@@ -4797,7 +4566,6 @@ function registerAllTools(server, callApi) {
|
|
|
4797
4566
|
registerReplayTools(server, callApi);
|
|
4798
4567
|
registerHistoryTools(server, callApi);
|
|
4799
4568
|
registerProvenanceTools(server, callApi);
|
|
4800
|
-
registerIntegrationTools(server, callApi);
|
|
4801
4569
|
registerLogTools(server, callApi);
|
|
4802
4570
|
registerDomainTemplateTools(server, callApi);
|
|
4803
4571
|
registerWebhookSubscriptionTools(server, callApi);
|
|
@@ -4828,7 +4596,7 @@ async function startMcpServer() {
|
|
|
4828
4596
|
function registerServeCommand(program) {
|
|
4829
4597
|
program.command("serve").description("Start LexQ as a server for AI agent integrations").option("--mcp", "Start as MCP (Model Context Protocol) server over stdio").addHelpText(
|
|
4830
4598
|
"after",
|
|
4831
|
-
|
|
4599
|
+
dedent17`
|
|
4832
4600
|
|
|
4833
4601
|
Example:
|
|
4834
4602
|
$ lexq serve --mcp
|
|
@@ -4859,11 +4627,11 @@ function registerServeCommand(program) {
|
|
|
4859
4627
|
|
|
4860
4628
|
// src/commands/domain-templates.ts
|
|
4861
4629
|
import "commander";
|
|
4862
|
-
import
|
|
4630
|
+
import dedent18 from "dedent";
|
|
4863
4631
|
function registerDomainTemplateCommands(program) {
|
|
4864
4632
|
const templates = program.command("domain-templates").description("Browse and apply domain templates").addHelpText(
|
|
4865
4633
|
"after",
|
|
4866
|
-
|
|
4634
|
+
dedent18`
|
|
4867
4635
|
|
|
4868
4636
|
A domain template is an industry-specific starter pack of fact
|
|
4869
4637
|
definitions and sample rules. Applying one provisions a ready-to-use
|
|
@@ -4911,7 +4679,7 @@ function registerDomainTemplateCommands(program) {
|
|
|
4911
4679
|
});
|
|
4912
4680
|
templates.command("preview").description("Preview what a domain template provisions").requiredOption("--template <key>", "Domain template key (e.g. ECOMMERCE)").addHelpText(
|
|
4913
4681
|
"after",
|
|
4914
|
-
|
|
4682
|
+
dedent18`
|
|
4915
4683
|
|
|
4916
4684
|
Read-only dry run — shows the fact definitions and sample rules the
|
|
4917
4685
|
template will create. Nothing is provisioned.
|
|
@@ -4940,7 +4708,7 @@ function registerDomainTemplateCommands(program) {
|
|
|
4940
4708
|
});
|
|
4941
4709
|
templates.command("apply").description("Apply a domain template to the current tenant").requiredOption("--template <key>", "Domain template key (e.g. ECOMMERCE)").option("--name <n>", "Custom name for the policy group that gets created").option("--force", "Skip confirmation prompt").addHelpText(
|
|
4942
4710
|
"after",
|
|
4943
|
-
|
|
4711
|
+
dedent18`
|
|
4944
4712
|
|
|
4945
4713
|
Creates the template's fact definitions and a new DRAFT policy group
|
|
4946
4714
|
populated with its sample rules. Existing facts are skipped — apply is
|
|
@@ -5019,7 +4787,6 @@ function createCli() {
|
|
|
5019
4787
|
registerHistoryCommands(program);
|
|
5020
4788
|
registerReplayCommands(program);
|
|
5021
4789
|
registerProvenanceCommands(program);
|
|
5022
|
-
registerIntegrationCommands(program);
|
|
5023
4790
|
registerLogCommands(program);
|
|
5024
4791
|
registerWebhookSubscriptionCommands(program);
|
|
5025
4792
|
registerServeCommand(program);
|