@lexq/cli 0.1.14 → 0.1.15

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
@@ -910,7 +910,7 @@ function buildUpdateBody2(opts) {
910
910
  import "commander";
911
911
  function registerDeployCommands(program) {
912
912
  const deploy = program.command("deploy").description("Deployment lifecycle and history");
913
- deploy.command("publish").description("Publish a DRAFT version (DRAFT \u2192 ACTIVE)").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Version ID to publish").option("--memo <memo>", "Deployment memo").action(async (opts) => {
913
+ deploy.command("publish").description("Publish a DRAFT version (DRAFT \u2192 ACTIVE)").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Version ID to publish").requiredOption("--memo <memo>", "Publish Deployment memo").action(async (opts) => {
914
914
  try {
915
915
  const globalOpts = program.opts();
916
916
  await apiRequest(
@@ -921,7 +921,7 @@ function registerDeployCommands(program) {
921
921
  baseUrl: globalOpts.baseUrl,
922
922
  dryRun: globalOpts.dryRun,
923
923
  verbose: globalOpts.verbose,
924
- body: { memo: opts.memo ?? "" }
924
+ body: { memo: opts.memo }
925
925
  }
926
926
  );
927
927
  console.log(`\u2713 Version ${opts.versionId} published.`);
@@ -930,7 +930,7 @@ function registerDeployCommands(program) {
930
930
  process.exit(1);
931
931
  }
932
932
  });
933
- deploy.command("live").description("Deploy an ACTIVE version to live traffic").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Version ID to deploy").option("--memo <memo>", "Deployment memo").action(async (opts) => {
933
+ deploy.command("live").description("Deploy an ACTIVE version to live traffic").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Version ID to deploy").requiredOption("--memo <memo>", "Live Deployment memo").action(async (opts) => {
934
934
  try {
935
935
  const globalOpts = program.opts();
936
936
  await apiRequest("POST", `policy-groups/${opts.groupId}/deploy`, {
@@ -938,7 +938,7 @@ function registerDeployCommands(program) {
938
938
  baseUrl: globalOpts.baseUrl,
939
939
  dryRun: globalOpts.dryRun,
940
940
  verbose: globalOpts.verbose,
941
- body: { versionId: opts.versionId, memo: opts.memo ?? "" }
941
+ body: { versionId: opts.versionId, memo: opts.memo }
942
942
  });
943
943
  console.log(`\u2713 Version ${opts.versionId} deployed to live.`);
944
944
  } catch (error) {
@@ -946,7 +946,7 @@ function registerDeployCommands(program) {
946
946
  process.exit(1);
947
947
  }
948
948
  });
949
- deploy.command("rollback").description("Rollback to the previous deployed version").requiredOption("--group-id <groupId>", "Policy group ID").option("--memo <memo>", "Rollback reason").option("--force", "Skip confirmation prompt").action(async (opts) => {
949
+ deploy.command("rollback").description("Rollback to the previous deployed version").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--memo <memo>", "Rollback reason").option("--force", "Skip confirmation prompt").action(async (opts) => {
950
950
  try {
951
951
  const globalOpts = program.opts();
952
952
  if (!opts.force) {
@@ -964,7 +964,7 @@ function registerDeployCommands(program) {
964
964
  baseUrl: globalOpts.baseUrl,
965
965
  dryRun: globalOpts.dryRun,
966
966
  verbose: globalOpts.verbose,
967
- body: { memo: opts.memo ?? "" }
967
+ body: { memo: opts.memo }
968
968
  });
969
969
  console.log(`\u2713 Group ${opts.groupId} rolled back.`);
970
970
  } catch (error) {
@@ -972,7 +972,7 @@ function registerDeployCommands(program) {
972
972
  process.exit(1);
973
973
  }
974
974
  });
975
- deploy.command("undeploy").description("Remove the live version from a group").requiredOption("--group-id <groupId>", "Policy group ID").option("--memo <memo>", "Undeploy reason").option("--force", "Skip confirmation prompt").action(async (opts) => {
975
+ deploy.command("undeploy").description("Remove the live version from a group").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--memo <memo>", "Undeploy reason").option("--force", "Skip confirmation prompt").action(async (opts) => {
976
976
  try {
977
977
  const globalOpts = program.opts();
978
978
  if (!opts.force) {
@@ -990,7 +990,7 @@ function registerDeployCommands(program) {
990
990
  baseUrl: globalOpts.baseUrl,
991
991
  dryRun: globalOpts.dryRun,
992
992
  verbose: globalOpts.verbose,
993
- body: { memo: opts.memo ?? "" }
993
+ body: { memo: opts.memo }
994
994
  });
995
995
  console.log(`\u2713 Group ${opts.groupId} undeployed.`);
996
996
  } catch (error) {
@@ -1553,7 +1553,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
1553
1553
  process.exit(1);
1554
1554
  }
1555
1555
  });
1556
- history.command("get").description("Get execution detail").requiredOption("--id <executionId>", "Execution history ID").action(async (opts) => {
1556
+ history.command("get").description("Get execution detail").requiredOption("--id <traceId>", "Trace ID").action(async (opts) => {
1557
1557
  try {
1558
1558
  const globalOpts = program.opts();
1559
1559
  const data = await apiRequest(
@@ -1973,10 +1973,10 @@ function registerGroupTools(server, callApi) {
1973
1973
  name: z.string().describe("Group name (unique among non-ARCHIVED)"),
1974
1974
  priority: z.number().int().min(0).describe("Execution priority (lower = higher)"),
1975
1975
  description: z.string().optional().describe("Group description"),
1976
- conflictResolutionMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
1977
- conflictResolutionStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
1978
- maxSelections: z.number().int().min(1).optional().describe("Max selections (required when mode is MAX_N)"),
1979
- activationGroupId: z.string().uuid().optional().describe("Activation group ID")
1976
+ activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
1977
+ activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
1978
+ executionLimit: z.number().int().min(1).optional().describe("Max rule executions (required when mode is MAX_N)"),
1979
+ activationGroup: z.string().optional().describe("Activation group name")
1980
1980
  }
1981
1981
  },
1982
1982
  async (args) => {
@@ -1985,12 +1985,10 @@ function registerGroupTools(server, callApi) {
1985
1985
  priority: args.priority
1986
1986
  };
1987
1987
  if (args.description !== void 0) body.description = args.description;
1988
- if (args.conflictResolutionMode !== void 0)
1989
- body.conflictResolutionMode = args.conflictResolutionMode;
1990
- if (args.conflictResolutionStrategy !== void 0)
1991
- body.conflictResolutionStrategy = args.conflictResolutionStrategy;
1992
- if (args.maxSelections !== void 0) body.maxSelections = args.maxSelections;
1993
- if (args.activationGroupId !== void 0) body.activationGroupId = args.activationGroupId;
1988
+ if (args.activationMode !== void 0) body.activationMode = args.activationMode;
1989
+ if (args.activationStrategy !== void 0) body.activationStrategy = args.activationStrategy;
1990
+ if (args.executionLimit !== void 0) body.executionLimit = args.executionLimit;
1991
+ if (args.activationGroup !== void 0) body.activationGroup = args.activationGroup;
1994
1992
  return callApi("POST", "policy-groups", { body });
1995
1993
  }
1996
1994
  );
@@ -1998,16 +1996,16 @@ function registerGroupTools(server, callApi) {
1998
1996
  "lexq_groups_update",
1999
1997
  {
2000
1998
  title: "Update Policy Group",
2001
- description: "Update an existing policy group. Only provided fields are updated.",
1999
+ description: "Update a policy group. This is a full replacement \u2014 omitted optional fields will be set to null on the server.",
2002
2000
  inputSchema: {
2003
2001
  groupId: z.string().uuid().describe("Policy group ID"),
2004
2002
  name: z.string().optional().describe("New name"),
2005
2003
  priority: z.number().int().min(0).optional().describe("New priority"),
2006
2004
  description: z.string().optional().describe("New description"),
2007
2005
  status: z.enum(["ACTIVE", "DISABLED"]).optional().describe("Status (DISABLED = emergency stop)"),
2008
- conflictResolutionMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
2009
- conflictResolutionStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
2010
- maxSelections: z.number().int().min(1).optional().describe("Max selections")
2006
+ activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
2007
+ activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
2008
+ executionLimit: z.number().int().min(1).optional().describe("Max rule executions")
2011
2009
  }
2012
2010
  },
2013
2011
  async ({ groupId, ...body }) => callApi("PUT", `policy-groups/${groupId}`, { body })
@@ -2186,17 +2184,21 @@ function registerRuleTools(server, callApi) {
2186
2184
  "lexq_rules_create",
2187
2185
  {
2188
2186
  title: "Create Rule",
2189
- description: `Create a rule in a DRAFT version. Requires name, priority, condition tree, and actions array.
2187
+ description: `Create a rule in a DRAFT version. Requires name, priority, condition tree, and actions array.
2188
+
2189
+ Note: Rules can reference fact keys that are not yet registered in Fact Definitions. The engine allows this for flexibility. However, registering facts via lexq_facts_create is recommended \u2014 it enables type validation, the Console UI autocomplete, and the dry-run requirements analyzer.
2190
+
2190
2191
  Condition: { type: "SINGLE", field, operator, value, valueType } or { type: "GROUP", operator: "AND"|"OR", children: [...] }
2191
- Operators: EQUALS, NOT_EQUALS, GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, CONTAINS, IN, NOT_IN
2192
+ Operators: EQUALS, NOT_EQUALS, GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, CONTAINS, IN, NOT_IN
2192
2193
  Value types: STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER
2193
-
2194
+
2194
2195
  Actions: [{ type, parameters }]
2195
-
2196
+
2196
2197
  Action parameter schemas:
2197
2198
  - DISCOUNT: { refVar: string, method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT) }
2198
2199
  - POINT: { refVar: string, targetVar: string, method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT), integrationId: uuid }
2199
- - COUPON_ISSUE: { couponId: string, integrationId: uuid }- BLOCK: { reason: string }
2200
+ - COUPON_ISSUE: { couponId: string, integrationId: uuid }
2201
+ - BLOCK: { reason: string }
2200
2202
  - NOTIFICATION: { channel: "SMS"|"EMAIL"|"PUSH"|"KAKAO", targetVar: string, templateId: string, integrationId: uuid }
2201
2203
  - WEBHOOK: { url: string, method: "POST" }
2202
2204
  - SET_FACT: { key: string, value: string|number|boolean }
@@ -2367,7 +2369,7 @@ function registerDeployTools(server, callApi) {
2367
2369
  inputSchema: {
2368
2370
  groupId: z5.string().uuid().describe("Policy group ID"),
2369
2371
  versionId: z5.string().uuid().describe("Version ID to publish"),
2370
- memo: z5.string().default("").describe("Deployment memo")
2372
+ memo: z5.string().min(1).describe("Publish Deployment memo (required)")
2371
2373
  }
2372
2374
  },
2373
2375
  async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/versions/${versionId}/publish`, { body: { memo } })
@@ -2380,7 +2382,7 @@ function registerDeployTools(server, callApi) {
2380
2382
  inputSchema: {
2381
2383
  groupId: z5.string().uuid().describe("Policy group ID"),
2382
2384
  versionId: z5.string().uuid().describe("Version ID to deploy"),
2383
- memo: z5.string().default("").describe("Deployment memo")
2385
+ memo: z5.string().min(1).describe("Live Deployment memo (required)")
2384
2386
  }
2385
2387
  },
2386
2388
  async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/deploy`, {
@@ -2464,10 +2466,8 @@ function registerAnalyticsTools(server, callApi) {
2464
2466
  {
2465
2467
  title: "Dry Run",
2466
2468
  description: `Execute a single dry run against a version. Tests how rules evaluate given input facts without side effects.
2467
-
2468
- Example input: { "facts": { "payment_amount": 100000, "customer_tier": "VIP" } }
2469
-
2470
- Always dry-run before publishing to validate rule behavior.`,
2469
+ Example input: { "facts": { "payment_amount": 100000, "customer_tier": "VIP" } }
2470
+ Always dry-run before publishing to validate rule behavior.`,
2471
2471
  inputSchema: {
2472
2472
  versionId: z6.string().uuid().describe("Policy version ID to test against"),
2473
2473
  facts: z6.string().describe('JSON string of facts object, e.g. {"payment_amount":100000}'),
@@ -2516,14 +2516,21 @@ Always dry-run before publishing to validate rule behavior.`,
2516
2516
  "lexq_simulation_start",
2517
2517
  {
2518
2518
  title: "Start Simulation",
2519
- description: `Start a batch simulation against historical execution data.
2520
-
2521
- Example body:
2522
- {
2523
- "policyVersionId": "<uuid>",
2524
- "dataset": { "type": "HISTORICAL", "source": "EXECUTION_LOGS", "from": "2025-01-01", "to": "2025-01-31" },
2525
- "options": { "baselinePolicyVersionId": "<uuid>", "includeRuleStats": true, "maxRecords": 10000 }
2526
- }`,
2519
+ description: `Start a batch simulation against historical or uploaded data.
2520
+
2521
+ dataset.type: "HISTORICAL" or "UPLOADED"
2522
+ dataset.source (when HISTORICAL): "EXECUTION_LOGS"
2523
+ dataset.from / dataset.to: date range (yyyy-MM-dd, when HISTORICAL)
2524
+ options.maxRecords: number (max 100000, default 10000)
2525
+ options.baselinePolicyVersionId: uuid (optional, for comparison)
2526
+ options.includeRuleStats: boolean
2527
+
2528
+ Example body:
2529
+ {
2530
+ "policyVersionId": "<uuid>",
2531
+ "dataset": { "type": "HISTORICAL", "source": "EXECUTION_LOGS", "from": "2025-01-01", "to": "2025-01-31" },
2532
+ "options": { "baselinePolicyVersionId": "<uuid>", "includeRuleStats": true, "maxRecords": 10000 }
2533
+ }`,
2527
2534
  inputSchema: {
2528
2535
  body: z6.string().describe("JSON string of SimulationRequest")
2529
2536
  }
@@ -2595,16 +2602,15 @@ Example body:
2595
2602
  {
2596
2603
  title: "Upload Dataset",
2597
2604
  description: `Upload inline CSV or JSON content as a simulation dataset.
2598
- The content is uploaded to S3 and a path is returned.
2599
- Use this path in simulation start with dataset type UPLOADED.
2600
-
2601
- CSV example:
2602
- user_id,payment_amount
2603
- user_001,150000
2604
- user_002,50000
2605
-
2606
- JSON example:
2607
- [{"user_id":"user_001","payment_amount":150000}, {"user_id":"user_002","payment_amount":50000}]`,
2605
+ The content is uploaded to S3 and a path is returned.
2606
+ Use this path in simulation start with dataset type UPLOADED.
2607
+
2608
+ CSV example:
2609
+ user_id,payment_amount
2610
+ user_001,150000
2611
+ user_002,50000
2612
+
2613
+ JSON example: [{"user_id":"user_001","payment_amount":150000}, {"user_id":"user_002","payment_amount":50000}]`,
2608
2614
  inputSchema: {
2609
2615
  content: z6.string().describe("CSV or JSON content as string"),
2610
2616
  filename: z6.string().default("dataset.csv").describe("Filename with extension (.csv or .json)")
@@ -2667,10 +2673,10 @@ function registerHistoryTools(server, callApi) {
2667
2673
  title: "Get Execution Detail",
2668
2674
  description: "Get full execution detail including request facts, result traces, and decision traces.",
2669
2675
  inputSchema: {
2670
- executionId: z7.string().uuid().describe("Execution history ID")
2676
+ traceId: z7.string().describe("Trace ID from execution history")
2671
2677
  }
2672
2678
  },
2673
- async ({ executionId }) => callApi("GET", `execution/history/${executionId}`)
2679
+ async ({ traceId }) => callApi("GET", `execution/history/${traceId}`)
2674
2680
  );
2675
2681
  server.registerTool(
2676
2682
  "lexq_history_stats",
@@ -2728,19 +2734,20 @@ function registerIntegrationTools(server, callApi) {
2728
2734
  "lexq_integrations_save",
2729
2735
  {
2730
2736
  title: "Save Integration",
2731
- description: "Create or update an integration. Types: COUPON, POINT, NOTIFICATION, CRM, MESSENGER, WEBHOOK.",
2737
+ 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.",
2732
2738
  inputSchema: {
2739
+ id: z8.string().uuid().optional().describe("Integration ID (omit to create, provide to update)"),
2733
2740
  type: z8.enum(["COUPON", "POINT", "NOTIFICATION", "CRM", "MESSENGER", "WEBHOOK"]).describe("Integration type"),
2734
2741
  name: z8.string().describe("Integration name"),
2735
2742
  baseUrl: z8.string().describe("Base URL of the external service"),
2736
- apiKey: z8.string().optional().describe("API key or token for the service"),
2737
- isActive: z8.boolean().default(true).describe("Whether the integration is active"),
2738
- config: z8.string().optional().describe("JSON string of additional config key-value pairs")
2743
+ credential: z8.string().optional().describe("API key or token for the service"),
2744
+ additionalConfig: z8.string().optional().describe("JSON string of additional config key-value pairs"),
2745
+ isActive: z8.boolean().default(true).describe("Whether the integration is active")
2739
2746
  }
2740
2747
  },
2741
- async ({ config, ...rest }) => {
2748
+ async ({ additionalConfig, ...rest }) => {
2742
2749
  const body = { ...rest };
2743
- if (config) body.config = JSON.parse(config);
2750
+ if (additionalConfig) body.additionalConfig = JSON.parse(additionalConfig);
2744
2751
  return callApi("POST", "integrations", { body });
2745
2752
  }
2746
2753
  );
@@ -66,10 +66,10 @@ function registerGroupTools(server, callApi) {
66
66
  name: z.string().describe("Group name (unique among non-ARCHIVED)"),
67
67
  priority: z.number().int().min(0).describe("Execution priority (lower = higher)"),
68
68
  description: z.string().optional().describe("Group description"),
69
- conflictResolutionMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
70
- conflictResolutionStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
71
- maxSelections: z.number().int().min(1).optional().describe("Max selections (required when mode is MAX_N)"),
72
- activationGroupId: z.string().uuid().optional().describe("Activation group ID")
69
+ activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
70
+ activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
71
+ executionLimit: z.number().int().min(1).optional().describe("Max rule executions (required when mode is MAX_N)"),
72
+ activationGroup: z.string().optional().describe("Activation group name")
73
73
  }
74
74
  },
75
75
  async (args) => {
@@ -78,12 +78,10 @@ function registerGroupTools(server, callApi) {
78
78
  priority: args.priority
79
79
  };
80
80
  if (args.description !== void 0) body.description = args.description;
81
- if (args.conflictResolutionMode !== void 0)
82
- body.conflictResolutionMode = args.conflictResolutionMode;
83
- if (args.conflictResolutionStrategy !== void 0)
84
- body.conflictResolutionStrategy = args.conflictResolutionStrategy;
85
- if (args.maxSelections !== void 0) body.maxSelections = args.maxSelections;
86
- if (args.activationGroupId !== void 0) body.activationGroupId = args.activationGroupId;
81
+ if (args.activationMode !== void 0) body.activationMode = args.activationMode;
82
+ if (args.activationStrategy !== void 0) body.activationStrategy = args.activationStrategy;
83
+ if (args.executionLimit !== void 0) body.executionLimit = args.executionLimit;
84
+ if (args.activationGroup !== void 0) body.activationGroup = args.activationGroup;
87
85
  return callApi("POST", "policy-groups", { body });
88
86
  }
89
87
  );
@@ -91,16 +89,16 @@ function registerGroupTools(server, callApi) {
91
89
  "lexq_groups_update",
92
90
  {
93
91
  title: "Update Policy Group",
94
- description: "Update an existing policy group. Only provided fields are updated.",
92
+ description: "Update a policy group. This is a full replacement \u2014 omitted optional fields will be set to null on the server.",
95
93
  inputSchema: {
96
94
  groupId: z.string().uuid().describe("Policy group ID"),
97
95
  name: z.string().optional().describe("New name"),
98
96
  priority: z.number().int().min(0).optional().describe("New priority"),
99
97
  description: z.string().optional().describe("New description"),
100
98
  status: z.enum(["ACTIVE", "DISABLED"]).optional().describe("Status (DISABLED = emergency stop)"),
101
- conflictResolutionMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
102
- conflictResolutionStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
103
- maxSelections: z.number().int().min(1).optional().describe("Max selections")
99
+ activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
100
+ activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
101
+ executionLimit: z.number().int().min(1).optional().describe("Max rule executions")
104
102
  }
105
103
  },
106
104
  async ({ groupId, ...body }) => callApi("PUT", `policy-groups/${groupId}`, { body })
@@ -279,17 +277,21 @@ function registerRuleTools(server, callApi) {
279
277
  "lexq_rules_create",
280
278
  {
281
279
  title: "Create Rule",
282
- description: `Create a rule in a DRAFT version. Requires name, priority, condition tree, and actions array.
280
+ description: `Create a rule in a DRAFT version. Requires name, priority, condition tree, and actions array.
281
+
282
+ Note: Rules can reference fact keys that are not yet registered in Fact Definitions. The engine allows this for flexibility. However, registering facts via lexq_facts_create is recommended \u2014 it enables type validation, the Console UI autocomplete, and the dry-run requirements analyzer.
283
+
283
284
  Condition: { type: "SINGLE", field, operator, value, valueType } or { type: "GROUP", operator: "AND"|"OR", children: [...] }
284
- Operators: EQUALS, NOT_EQUALS, GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, CONTAINS, IN, NOT_IN
285
+ Operators: EQUALS, NOT_EQUALS, GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, CONTAINS, IN, NOT_IN
285
286
  Value types: STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER
286
-
287
+
287
288
  Actions: [{ type, parameters }]
288
-
289
+
289
290
  Action parameter schemas:
290
291
  - DISCOUNT: { refVar: string, method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT) }
291
292
  - POINT: { refVar: string, targetVar: string, method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT), integrationId: uuid }
292
- - COUPON_ISSUE: { couponId: string, integrationId: uuid }- BLOCK: { reason: string }
293
+ - COUPON_ISSUE: { couponId: string, integrationId: uuid }
294
+ - BLOCK: { reason: string }
293
295
  - NOTIFICATION: { channel: "SMS"|"EMAIL"|"PUSH"|"KAKAO", targetVar: string, templateId: string, integrationId: uuid }
294
296
  - WEBHOOK: { url: string, method: "POST" }
295
297
  - SET_FACT: { key: string, value: string|number|boolean }
@@ -460,7 +462,7 @@ function registerDeployTools(server, callApi) {
460
462
  inputSchema: {
461
463
  groupId: z5.string().uuid().describe("Policy group ID"),
462
464
  versionId: z5.string().uuid().describe("Version ID to publish"),
463
- memo: z5.string().default("").describe("Deployment memo")
465
+ memo: z5.string().min(1).describe("Publish Deployment memo (required)")
464
466
  }
465
467
  },
466
468
  async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/versions/${versionId}/publish`, { body: { memo } })
@@ -473,7 +475,7 @@ function registerDeployTools(server, callApi) {
473
475
  inputSchema: {
474
476
  groupId: z5.string().uuid().describe("Policy group ID"),
475
477
  versionId: z5.string().uuid().describe("Version ID to deploy"),
476
- memo: z5.string().default("").describe("Deployment memo")
478
+ memo: z5.string().min(1).describe("Live Deployment memo (required)")
477
479
  }
478
480
  },
479
481
  async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/deploy`, {
@@ -557,10 +559,8 @@ function registerAnalyticsTools(server, callApi) {
557
559
  {
558
560
  title: "Dry Run",
559
561
  description: `Execute a single dry run against a version. Tests how rules evaluate given input facts without side effects.
560
-
561
- Example input: { "facts": { "payment_amount": 100000, "customer_tier": "VIP" } }
562
-
563
- Always dry-run before publishing to validate rule behavior.`,
562
+ Example input: { "facts": { "payment_amount": 100000, "customer_tier": "VIP" } }
563
+ Always dry-run before publishing to validate rule behavior.`,
564
564
  inputSchema: {
565
565
  versionId: z6.string().uuid().describe("Policy version ID to test against"),
566
566
  facts: z6.string().describe('JSON string of facts object, e.g. {"payment_amount":100000}'),
@@ -609,14 +609,21 @@ Always dry-run before publishing to validate rule behavior.`,
609
609
  "lexq_simulation_start",
610
610
  {
611
611
  title: "Start Simulation",
612
- description: `Start a batch simulation against historical execution data.
613
-
614
- Example body:
615
- {
616
- "policyVersionId": "<uuid>",
617
- "dataset": { "type": "HISTORICAL", "source": "EXECUTION_LOGS", "from": "2025-01-01", "to": "2025-01-31" },
618
- "options": { "baselinePolicyVersionId": "<uuid>", "includeRuleStats": true, "maxRecords": 10000 }
619
- }`,
612
+ description: `Start a batch simulation against historical or uploaded data.
613
+
614
+ dataset.type: "HISTORICAL" or "UPLOADED"
615
+ dataset.source (when HISTORICAL): "EXECUTION_LOGS"
616
+ dataset.from / dataset.to: date range (yyyy-MM-dd, when HISTORICAL)
617
+ options.maxRecords: number (max 100000, default 10000)
618
+ options.baselinePolicyVersionId: uuid (optional, for comparison)
619
+ options.includeRuleStats: boolean
620
+
621
+ Example body:
622
+ {
623
+ "policyVersionId": "<uuid>",
624
+ "dataset": { "type": "HISTORICAL", "source": "EXECUTION_LOGS", "from": "2025-01-01", "to": "2025-01-31" },
625
+ "options": { "baselinePolicyVersionId": "<uuid>", "includeRuleStats": true, "maxRecords": 10000 }
626
+ }`,
620
627
  inputSchema: {
621
628
  body: z6.string().describe("JSON string of SimulationRequest")
622
629
  }
@@ -688,16 +695,15 @@ Example body:
688
695
  {
689
696
  title: "Upload Dataset",
690
697
  description: `Upload inline CSV or JSON content as a simulation dataset.
691
- The content is uploaded to S3 and a path is returned.
692
- Use this path in simulation start with dataset type UPLOADED.
693
-
694
- CSV example:
695
- user_id,payment_amount
696
- user_001,150000
697
- user_002,50000
698
-
699
- JSON example:
700
- [{"user_id":"user_001","payment_amount":150000}, {"user_id":"user_002","payment_amount":50000}]`,
698
+ The content is uploaded to S3 and a path is returned.
699
+ Use this path in simulation start with dataset type UPLOADED.
700
+
701
+ CSV example:
702
+ user_id,payment_amount
703
+ user_001,150000
704
+ user_002,50000
705
+
706
+ JSON example: [{"user_id":"user_001","payment_amount":150000}, {"user_id":"user_002","payment_amount":50000}]`,
701
707
  inputSchema: {
702
708
  content: z6.string().describe("CSV or JSON content as string"),
703
709
  filename: z6.string().default("dataset.csv").describe("Filename with extension (.csv or .json)")
@@ -760,10 +766,10 @@ function registerHistoryTools(server, callApi) {
760
766
  title: "Get Execution Detail",
761
767
  description: "Get full execution detail including request facts, result traces, and decision traces.",
762
768
  inputSchema: {
763
- executionId: z7.string().uuid().describe("Execution history ID")
769
+ traceId: z7.string().describe("Trace ID from execution history")
764
770
  }
765
771
  },
766
- async ({ executionId }) => callApi("GET", `execution/history/${executionId}`)
772
+ async ({ traceId }) => callApi("GET", `execution/history/${traceId}`)
767
773
  );
768
774
  server.registerTool(
769
775
  "lexq_history_stats",
@@ -821,19 +827,20 @@ function registerIntegrationTools(server, callApi) {
821
827
  "lexq_integrations_save",
822
828
  {
823
829
  title: "Save Integration",
824
- description: "Create or update an integration. Types: COUPON, POINT, NOTIFICATION, CRM, MESSENGER, WEBHOOK.",
830
+ 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.",
825
831
  inputSchema: {
832
+ id: z8.string().uuid().optional().describe("Integration ID (omit to create, provide to update)"),
826
833
  type: z8.enum(["COUPON", "POINT", "NOTIFICATION", "CRM", "MESSENGER", "WEBHOOK"]).describe("Integration type"),
827
834
  name: z8.string().describe("Integration name"),
828
835
  baseUrl: z8.string().describe("Base URL of the external service"),
829
- apiKey: z8.string().optional().describe("API key or token for the service"),
830
- isActive: z8.boolean().default(true).describe("Whether the integration is active"),
831
- config: z8.string().optional().describe("JSON string of additional config key-value pairs")
836
+ credential: z8.string().optional().describe("API key or token for the service"),
837
+ additionalConfig: z8.string().optional().describe("JSON string of additional config key-value pairs"),
838
+ isActive: z8.boolean().default(true).describe("Whether the integration is active")
832
839
  }
833
840
  },
834
- async ({ config, ...rest }) => {
841
+ async ({ additionalConfig, ...rest }) => {
835
842
  const body = { ...rest };
836
- if (config) body.config = JSON.parse(config);
843
+ if (additionalConfig) body.additionalConfig = JSON.parse(additionalConfig);
837
844
  return callApi("POST", "integrations", { body });
838
845
  }
839
846
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lexq/cli",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "LexQ CLI — manage policies, simulate rules, and deploy from the terminal. Built for humans and AI agents.",
5
5
  "type": "module",
6
6
  "bin": {