@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/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 FIRST_MATCH | HIGHEST_PRIORITY | MAX_BENEFIT [default: FIRST_MATCH]
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": { "refVar": "payment_amount", "operator": "SUB", "method": "PERCENTAGE", "rate": 20 } }
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, GREATER_THAN, GREATER_THAN_OR_EQUAL,
938
- LESS_THAN, LESS_THAN_OR_EQUAL, CONTAINS, IN, NOT_IN
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, INCREMENT_FACT, EMIT_EVENT, BLOCK, EMIT_NOTIFICATION, EMIT_WEBHOOK, SET_FACT, ADD_TAG
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 FIRST_MATCH | HIGHEST_PRIORITY | MAX_BENEFIT
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": { "refVar": "payment_amount", "operator": "SUB", "method": "PERCENTAGE", "rate": 25 } }
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).option("--mock", "Mock external calls", false).addHelpText(
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 --mock \\
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
- Simulations always mock external calls. Use --format table for summary view.
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 = ["RETRY", "IGNORE", "RESOLVE"];
2307
- var TaskCategory = ["INTEGRATION", "INTERNAL"];
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 dedent14 from "dedent";
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
- dedent14`
2816
+ dedent13`
2993
2817
 
2994
- System failure logs (DLQ) for background tasks — webhook calls, coupon issuance,
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 payload for retry)
3000
- action Process a single log (RETRY, IGNORE, RESOLVE)
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("--category <category>", "Filter by category (INTEGRATION, INTERNAL)").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(
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
- dedent14`
2831
+ dedent13`
3010
2832
 
3011
2833
  Examples:
3012
2834
  $ lexq logs list --status PENDING --format table
3013
- $ lexq logs list --task-type PLATFORM_WEBHOOK --category INTERNAL
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", "Category", "Task", "Status", "Retries", "Error", "Created"],
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
- dedent14`
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 RETRY or RESOLVE.
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 (RETRY, IGNORE, RESOLVE)").requiredOption("--id <logId>", "Log ID").requiredOption("--action <action>", "Action: RETRY, IGNORE, or RESOLVE").addHelpText(
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
- dedent14`
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 RETRY
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: RETRY, IGNORE, or RESOLVE").addHelpText(
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
- dedent14`
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 dedent15 from "dedent";
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
- dedent15`
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
- dedent15`
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
- dedent15`
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
- dedent15`
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 dedent18 from "dedent";
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(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
3480
- activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
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(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
3509
- activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
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 dedent16 from "dedent";
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: dedent16`
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: { refVar: string, operator: "ASSIGN"|"ADD"|"SUB"|"MUL"|"DIV", method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT), rounding?: RoundingOption } Constraints: DIV + PERCENTAGE is invalid (use MUL with rate/100 inverse). DIV + AMOUNT requires value !== 0.
3722
- - INCREMENT_FACT: { targetVar: string, method: "PERCENTAGE"|"AMOUNT", refVar?: string (required when PERCENTAGE), rate?: number (when PERCENTAGE), value?: number (when AMOUNT), rounding?: RoundingOption } targetVar (accumulation target) must exist at execution; refVar (PERCENTAGE source) must exist when method is PERCENTAGE. Each is supplied as an input fact or written by a prior action in this rule — a missing required fact throws (no 0 default). Note: external system call (e.g. point system sync) is NOT a primitive responsibility. Compose [INCREMENT_FACT, EMIT_EVENT] chain instead.
3723
- - EMIT_EVENT: { integrationId: uuid, eventPayload: object (Map<string,unknown>, ≥1 entry) } eventPayload is passed through to the integration provider as-is. Domain-specific keys (couponId, ticketId, etc.) are routed by the provider, not validated by the engine.
3724
- - BLOCK: { reason: string }
3725
- - EMIT_NOTIFICATION: { integrationId: uuid, targetVar: string, notificationPayload: object (Map<string,unknown>, ≥1 entry) } targetVar identifies the recipient fact (e.g. phone_number / email / device_token). notificationPayload (channel, templateId, body, variables, etc.) is passed through to the provider.
3726
- - EMIT_WEBHOOK: { url: string, method: "POST", payloadTemplate?: object } payloadTemplate is optional. Without it, all facts are sent as-is. With it, the object is sent as the HTTP body with {{variables}} replaced at execution time. Variables: {{fact.xxx}}, {{output.xxx}}, {{timestamp}}, {{ruleName}}, {{groupName}}, {{versionNo}}, {{xxx}} (shorthand).
3727
- Platform examples:
3728
- Slack: { "text": "Rule {{ruleName}} fired{{fact.customer_tier}}" }
3729
- Discord: { "content": "Rule {{ruleName}} fired {{fact.customer_tier}}" }
3730
- Generic: { "event": "rule_matched", "rule": "{{ruleName}}", "amount": "{{output.payment_amount}}" }
3731
- - SET_FACT: { key: string, value: string|number|boolean }
3732
- - ADD_TAG: { tag: string, targetVar: string }
3733
-
3734
- RoundingOption (optional, MUTATE_FACT / INCREMENT_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).
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 (e.g. MUTATE_FACT requires its refVar fact; INCREMENT_FACT always requires targetVar, plus refVar when method is PERCENTAGE). 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; Actions never create a fact from nothing. Static data, safe to cache in-session.",
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 dedent17 from "dedent";
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: dedent17`
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, mockExternalCalls }) => {
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, mockExternalCalls }
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: dedent17`
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: dedent17`
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: dedent17`
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 z12 } from "zod";
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 (webhook calls, coupon issuance, etc.).",
4379
+ description: "List system failure logs from background tasks (platform event webhooks, scheduled deployments).",
4609
4380
  inputSchema: {
4610
- page: z12.number().int().min(0).default(0).describe("Page number"),
4611
- size: z12.number().int().min(1).max(100).default(20).describe("Page size"),
4612
- category: z12.enum(TaskCategory).optional().describe("Task category"),
4613
- taskType: z12.enum(TaskType).optional().describe("Task type"),
4614
- status: z12.enum(FailureStatus).optional().describe("Log status"),
4615
- keyword: z12.string().optional().describe("Search in refId, refSubId, errorMessage"),
4616
- startDate: z12.string().optional().describe("Start date (yyyy-MM-dd)"),
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, category, taskType, status, keyword, startDate, endDate }) => {
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: z12.string().uuid().describe("Failure log ID")
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: RETRY (re-execute with original payload), RESOLVE (mark as manually fixed), or IGNORE (skip intentionally).",
4415
+ description: "Process a single failure log: RESOLVE (mark as manually fixed) or IGNORE (skip intentionally).",
4647
4416
  inputSchema: {
4648
- logId: z12.string().uuid().describe("Failure log ID"),
4649
- action: z12.enum(FailureAction).describe("Action to take")
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: z12.array(z12.string().uuid()).describe("Array of failure log IDs"),
4663
- action: z12.enum(FailureAction).describe("Action to apply to all logs")
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 z13 } from "zod";
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: z13.number().int().min(0).default(0).describe("Page number"),
4682
- size: z13.number().int().min(1).max(100).default(20).describe("Page 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: z13.string().uuid().describe("Webhook subscription 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: z13.string().uuid().optional().describe("Subscription ID (omit to create, provide to update)"),
4708
- name: z13.string().min(1).describe("Subscription name (unique per tenant)"),
4709
- webhookUrl: z13.string().url().describe("Webhook endpoint URL"),
4710
- subscribedEvents: z13.array(z13.enum(PlatformEventType)).min(1).describe("Events to subscribe to"),
4711
- payloadFormat: z13.enum(WebhookPayloadFormat).optional().default("GENERIC").describe("Payload format"),
4712
- secret: z13.string().optional().describe("HMAC-SHA256 signing secret"),
4713
- isActive: z13.boolean().optional().default(true).describe("Whether the subscription is active")
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: z13.string().uuid().describe("Webhook subscription 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: z13.string().uuid().describe("Webhook subscription 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 z14 } from "zod";
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: z14.string().describe(
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: z14.string().describe("Domain template key to apply (e.g. ECOMMERCE)."),
4774
- customName: z14.string().optional().describe(
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
- dedent18`
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 dedent19 from "dedent";
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
- dedent19`
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
- dedent19`
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
- dedent19`
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);