@lexq/cli 0.1.6 → 0.1.8

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 CHANGED
@@ -46,7 +46,7 @@ Detailed documentation lives in the `skills/` directory. **Read the relevant ski
46
46
  6. **Copy full UUIDs from output.** Never guess or truncate IDs.
47
47
  7. **Handle errors gracefully.** Check the error code and follow the action table in `lexq-shared/SKILL.md`.
48
48
 
49
- ## Complete Command Inventory (61 commands)
49
+ ## Complete Command Inventory (63 commands)
50
50
 
51
51
  ```
52
52
  lexq auth login|logout|whoami
@@ -59,6 +59,7 @@ lexq facts list|create|update|delete
59
59
  lexq deploy publish|live|rollback|undeploy|history|detail|overview
60
60
  lexq analytics dry-run|dry-run-compare|requirements
61
61
  lexq analytics simulation start|status|list|cancel|export
62
+ lexq analytics dataset upload|template
62
63
  lexq history list|get|stats
63
64
  lexq integrations list|get|save|delete|config-spec
64
65
  lexq logs list|get|action|bulk-action
package/README.md CHANGED
@@ -74,6 +74,7 @@ lexq facts list | create | update | delete
74
74
  lexq deploy publish | live | rollback | undeploy | history | detail | overview
75
75
  lexq analytics dry-run | dry-run-compare | requirements
76
76
  lexq analytics simulation start | status | list | cancel | export
77
+ lexq analytics dataset upload | template
77
78
  lexq history list | get | stats
78
79
  lexq integrations list | get | save | delete | config-spec
79
80
  lexq logs list | get | action | bulk-action
@@ -156,7 +157,7 @@ Run LexQ CLI as an MCP (Model Context Protocol) server for seamless AI agent int
156
157
  lexq serve --mcp
157
158
  ```
158
159
 
159
- This starts a stdio MCP server exposing 53 tools — the full LexQ API — to any MCP-compatible client.
160
+ This starts a stdio MCP server exposing 55 tools — the full LexQ API — to any MCP-compatible client.
160
161
 
161
162
  ### Claude Desktop
162
163
 
package/dist/index.js CHANGED
@@ -1200,7 +1200,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
1200
1200
  }
1201
1201
 
1202
1202
  // src/commands/analytics.ts
1203
- import { readFileSync as readFileSync2 } from "fs";
1203
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
1204
1204
  import "commander";
1205
1205
  function registerAnalyticsCommands(program) {
1206
1206
  const analytics = program.command("analytics").description("Dry run, simulation, and requirements");
@@ -1464,7 +1464,6 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
1464
1464
  }
1465
1465
  );
1466
1466
  if (opts.output) {
1467
- const { writeFileSync: writeFileSync2 } = await import("fs");
1468
1467
  const text = typeof response === "string" ? response : JSON.stringify(response, null, 2);
1469
1468
  writeFileSync2(opts.output, text, "utf-8");
1470
1469
  console.log(`\u2713 Exported to ${opts.output}`);
@@ -1476,6 +1475,83 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
1476
1475
  process.exit(1);
1477
1476
  }
1478
1477
  });
1478
+ const dataset = analytics.command("dataset").description("Upload datasets and download templates");
1479
+ dataset.command("upload").description("Upload a CSV or JSON file as a simulation dataset").requiredOption("--file <path>", "Path to CSV or JSON file").action(async (opts) => {
1480
+ try {
1481
+ const globalOpts = program.opts();
1482
+ const config = loadConfig();
1483
+ const baseUrl = globalOpts.baseUrl ?? config.baseUrl;
1484
+ const apiKey = globalOpts.apiKey ?? config.apiKey;
1485
+ if (!apiKey) {
1486
+ throw new Error('Not authenticated. Run "lexq auth login" first.');
1487
+ }
1488
+ const filePath = opts.file;
1489
+ const fileBuffer = readFileSync2(filePath);
1490
+ const fileName = filePath.split("/").pop() ?? "dataset";
1491
+ const ext = fileName.split(".").pop()?.toLowerCase();
1492
+ let contentType = "application/octet-stream";
1493
+ if (ext === "csv") contentType = "text/csv";
1494
+ else if (ext === "json") contentType = "application/json";
1495
+ const blob = new Blob([fileBuffer], { type: contentType });
1496
+ const formData = new FormData();
1497
+ formData.append("file", blob, fileName);
1498
+ const url = new URL("analytics/datasets/upload", baseUrl.endsWith("/") ? baseUrl : baseUrl + "/");
1499
+ if (globalOpts.verbose) {
1500
+ console.error(`\u2192 POST ${url.toString()}`);
1501
+ console.error(` File: ${filePath} (${fileBuffer.length} bytes)`);
1502
+ }
1503
+ const response = await fetch(url.toString(), {
1504
+ method: "POST",
1505
+ headers: { "X-API-KEY": apiKey },
1506
+ body: formData
1507
+ });
1508
+ if (!response.ok) {
1509
+ const errorText = await response.text();
1510
+ throw new Error(`Upload failed (${response.status}): ${errorText}`);
1511
+ }
1512
+ const envelope = await response.json();
1513
+ const result = envelope.data;
1514
+ console.log(`\u2713 Dataset uploaded: ${result.path}`);
1515
+ console.log(` File: ${result.filename} (${result.size} bytes)`);
1516
+ printJson(result);
1517
+ } catch (error) {
1518
+ printError(error);
1519
+ process.exit(1);
1520
+ }
1521
+ });
1522
+ dataset.command("template").description("Download a dataset template based on version requirements").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Policy version ID").option("--format <fmt>", "Template format: csv or json", "csv").option("--output <path>", "Output file path").action(async (opts) => {
1523
+ try {
1524
+ const globalOpts = program.opts();
1525
+ const config = loadConfig();
1526
+ const baseUrl = globalOpts.baseUrl ?? config.baseUrl;
1527
+ const apiKey = globalOpts.apiKey ?? config.apiKey;
1528
+ if (!apiKey) {
1529
+ throw new Error('Not authenticated. Run "lexq auth login" first.');
1530
+ }
1531
+ const fmt = opts.format === "json" ? "json" : "csv";
1532
+ const url = new URL(
1533
+ `analytics/groups/${opts.groupId}/versions/${opts.versionId}/dataset-template`,
1534
+ baseUrl.endsWith("/") ? baseUrl : baseUrl + "/"
1535
+ );
1536
+ url.searchParams.set("format", fmt);
1537
+ const response = await fetch(url.toString(), {
1538
+ headers: { "X-API-KEY": apiKey, Accept: "*/*" }
1539
+ });
1540
+ if (!response.ok) {
1541
+ throw new Error(`Template download failed (${response.status})`);
1542
+ }
1543
+ const text = await response.text();
1544
+ if (opts.output) {
1545
+ writeFileSync2(opts.output, text, "utf-8");
1546
+ console.log(`\u2713 Template saved to ${opts.output}`);
1547
+ } else {
1548
+ console.log(text);
1549
+ }
1550
+ } catch (error) {
1551
+ printError(error);
1552
+ process.exit(1);
1553
+ }
1554
+ });
1479
1555
  }
1480
1556
  function resolveBody(opts) {
1481
1557
  if (opts.file) {
@@ -1885,32 +1961,52 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1885
1961
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1886
1962
 
1887
1963
  // src/mcp/tools/_shared.ts
1888
- function getMcpClientOptions() {
1889
- const config = loadConfig();
1890
- return {
1891
- apiKey: config.apiKey,
1892
- baseUrl: config.baseUrl
1964
+ function createCallApiFromConfig() {
1965
+ return async (method, path, opts) => {
1966
+ try {
1967
+ const config = loadConfig();
1968
+ if (opts?.upload) {
1969
+ const url = new URL(path, config.baseUrl.endsWith("/") ? config.baseUrl : config.baseUrl + "/");
1970
+ if (opts.params) {
1971
+ for (const [key, value] of Object.entries(opts.params)) {
1972
+ if (value !== void 0) url.searchParams.set(key, value);
1973
+ }
1974
+ }
1975
+ if (!config.apiKey) {
1976
+ throw new Error('Not authenticated. Run "lexq auth login" first.');
1977
+ }
1978
+ const blob = new Blob([opts.upload.content], { type: "text/plain" });
1979
+ const formData = new FormData();
1980
+ formData.append(opts.upload.fieldName, blob, opts.upload.filename);
1981
+ const response = await fetch(url.toString(), {
1982
+ method,
1983
+ headers: { "X-API-KEY": config.apiKey },
1984
+ body: formData
1985
+ });
1986
+ const data2 = await response.json();
1987
+ return { content: [{ type: "text", text: JSON.stringify(data2, null, 2) }] };
1988
+ }
1989
+ const clientOpts = {
1990
+ apiKey: config.apiKey,
1991
+ baseUrl: config.baseUrl
1992
+ };
1993
+ const data = await apiRequest(method, path, {
1994
+ ...clientOpts,
1995
+ body: opts?.body,
1996
+ params: opts?.params
1997
+ });
1998
+ return {
1999
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
2000
+ };
2001
+ } catch (error) {
2002
+ const message = error instanceof Error ? error.message : String(error);
2003
+ return {
2004
+ content: [{ type: "text", text: JSON.stringify({ error: message }) }],
2005
+ isError: true
2006
+ };
2007
+ }
1893
2008
  };
1894
2009
  }
1895
- async function callApi(method, path, opts) {
1896
- try {
1897
- const clientOpts = getMcpClientOptions();
1898
- const data = await apiRequest(method, path, {
1899
- ...clientOpts,
1900
- body: opts?.body,
1901
- params: opts?.params
1902
- });
1903
- return {
1904
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
1905
- };
1906
- } catch (error) {
1907
- const message = error instanceof Error ? error.message : String(error);
1908
- return {
1909
- content: [{ type: "text", text: JSON.stringify({ error: message }) }],
1910
- isError: true
1911
- };
1912
- }
1913
- }
1914
2010
  function paginationParams(page, size) {
1915
2011
  const params = {};
1916
2012
  if (page !== void 0) params.page = String(page);
@@ -1919,7 +2015,7 @@ function paginationParams(page, size) {
1919
2015
  }
1920
2016
 
1921
2017
  // src/mcp/tools/status.ts
1922
- function registerStatusTools(server) {
2018
+ function registerStatusTools(server, callApi) {
1923
2019
  server.registerTool(
1924
2020
  "lexq_whoami",
1925
2021
  {
@@ -1933,7 +2029,7 @@ function registerStatusTools(server) {
1933
2029
 
1934
2030
  // src/mcp/tools/groups.ts
1935
2031
  import { z } from "zod";
1936
- function registerGroupTools(server) {
2032
+ function registerGroupTools(server, callApi) {
1937
2033
  server.registerTool(
1938
2034
  "lexq_groups_list",
1939
2035
  {
@@ -2068,7 +2164,7 @@ function registerGroupTools(server) {
2068
2164
 
2069
2165
  // src/mcp/tools/versions.ts
2070
2166
  import { z as z2 } from "zod";
2071
- function registerVersionTools(server) {
2167
+ function registerVersionTools(server, callApi) {
2072
2168
  server.registerTool(
2073
2169
  "lexq_versions_list",
2074
2170
  {
@@ -2160,7 +2256,7 @@ function registerVersionTools(server) {
2160
2256
 
2161
2257
  // src/mcp/tools/rules.ts
2162
2258
  import { z as z3 } from "zod";
2163
- function registerRuleTools(server) {
2259
+ function registerRuleTools(server, callApi) {
2164
2260
  server.registerTool(
2165
2261
  "lexq_rules_list",
2166
2262
  {
@@ -2312,7 +2408,7 @@ Types: DISCOUNT, POINT, COUPON_ISSUE, BLOCK, NOTIFICATION, WEBHOOK, SET_FACT, AD
2312
2408
 
2313
2409
  // src/mcp/tools/facts.ts
2314
2410
  import { z as z4 } from "zod";
2315
- function registerFactTools(server) {
2411
+ function registerFactTools(server, callApi) {
2316
2412
  server.registerTool(
2317
2413
  "lexq_facts_list",
2318
2414
  {
@@ -2374,7 +2470,7 @@ function registerFactTools(server) {
2374
2470
 
2375
2471
  // src/mcp/tools/deploy.ts
2376
2472
  import { z as z5 } from "zod";
2377
- function registerDeployTools(server) {
2473
+ function registerDeployTools(server, callApi) {
2378
2474
  server.registerTool(
2379
2475
  "lexq_deploy_publish",
2380
2476
  {
@@ -2478,7 +2574,7 @@ function registerDeployTools(server) {
2478
2574
 
2479
2575
  // src/mcp/tools/analytics.ts
2480
2576
  import { z as z6 } from "zod";
2481
- function registerAnalyticsTools(server) {
2577
+ function registerAnalyticsTools(server, callApi) {
2482
2578
  server.registerTool(
2483
2579
  "lexq_dry_run",
2484
2580
  {
@@ -2613,11 +2709,52 @@ Example body:
2613
2709
  params: { format }
2614
2710
  })
2615
2711
  );
2712
+ server.registerTool(
2713
+ "lexq_dataset_upload",
2714
+ {
2715
+ title: "Upload Dataset",
2716
+ description: `Upload inline CSV or JSON content as a simulation dataset.
2717
+ The content is uploaded to S3 and a path is returned.
2718
+ Use this path in simulation start with dataset type UPLOADED.
2719
+
2720
+ CSV example:
2721
+ user_id,payment_amount
2722
+ user_001,150000
2723
+ user_002,50000
2724
+
2725
+ JSON example:
2726
+ [{"user_id":"user_001","payment_amount":150000}, {"user_id":"user_002","payment_amount":50000}]`,
2727
+ inputSchema: {
2728
+ content: z6.string().describe("CSV or JSON content as string"),
2729
+ filename: z6.string().default("dataset.csv").describe("Filename with extension (.csv or .json)")
2730
+ }
2731
+ },
2732
+ async ({ content, filename }) => callApi("POST", "analytics/datasets/upload", {
2733
+ upload: { content, filename, fieldName: "file" }
2734
+ })
2735
+ );
2736
+ server.registerTool(
2737
+ "lexq_dataset_template",
2738
+ {
2739
+ title: "Download Dataset Template",
2740
+ description: "Generate a sample CSV or JSON template based on the required facts of a version. Use this to understand the expected data format before uploading a dataset.",
2741
+ inputSchema: {
2742
+ groupId: z6.string().uuid().describe("Policy group ID"),
2743
+ versionId: z6.string().uuid().describe("Version ID"),
2744
+ format: z6.enum(["csv", "json"]).default("csv").describe("Template format")
2745
+ }
2746
+ },
2747
+ async ({ groupId, versionId, format }) => callApi(
2748
+ "GET",
2749
+ `analytics/groups/${groupId}/versions/${versionId}/dataset-template`,
2750
+ { params: { format } }
2751
+ )
2752
+ );
2616
2753
  }
2617
2754
 
2618
2755
  // src/mcp/tools/history.ts
2619
2756
  import { z as z7 } from "zod";
2620
- function registerHistoryTools(server) {
2757
+ function registerHistoryTools(server, callApi) {
2621
2758
  server.registerTool(
2622
2759
  "lexq_history_list",
2623
2760
  {
@@ -2679,7 +2816,7 @@ function registerHistoryTools(server) {
2679
2816
 
2680
2817
  // src/mcp/tools/integrations.ts
2681
2818
  import { z as z8 } from "zod";
2682
- function registerIntegrationTools(server) {
2819
+ function registerIntegrationTools(server, callApi) {
2683
2820
  server.registerTool(
2684
2821
  "lexq_integrations_list",
2685
2822
  {
@@ -2752,7 +2889,7 @@ function registerIntegrationTools(server) {
2752
2889
 
2753
2890
  // src/mcp/tools/logs.ts
2754
2891
  import { z as z9 } from "zod";
2755
- function registerLogTools(server) {
2892
+ function registerLogTools(server, callApi) {
2756
2893
  server.registerTool(
2757
2894
  "lexq_logs_list",
2758
2895
  {
@@ -2821,6 +2958,20 @@ function registerLogTools(server) {
2821
2958
  );
2822
2959
  }
2823
2960
 
2961
+ // src/mcp/register.ts
2962
+ function registerAllTools(server, callApi) {
2963
+ registerStatusTools(server, callApi);
2964
+ registerGroupTools(server, callApi);
2965
+ registerVersionTools(server, callApi);
2966
+ registerRuleTools(server, callApi);
2967
+ registerFactTools(server, callApi);
2968
+ registerDeployTools(server, callApi);
2969
+ registerAnalyticsTools(server, callApi);
2970
+ registerHistoryTools(server, callApi);
2971
+ registerIntegrationTools(server, callApi);
2972
+ registerLogTools(server, callApi);
2973
+ }
2974
+
2824
2975
  // src/mcp/server.ts
2825
2976
  var __dirname = dirname(fileURLToPath(import.meta.url));
2826
2977
  function getVersion() {
@@ -2838,16 +2989,8 @@ async function startMcpServer() {
2838
2989
  name: "lexq",
2839
2990
  version: getVersion()
2840
2991
  });
2841
- registerStatusTools(server);
2842
- registerGroupTools(server);
2843
- registerVersionTools(server);
2844
- registerRuleTools(server);
2845
- registerFactTools(server);
2846
- registerDeployTools(server);
2847
- registerAnalyticsTools(server);
2848
- registerHistoryTools(server);
2849
- registerIntegrationTools(server);
2850
- registerLogTools(server);
2992
+ const callApi = createCallApiFromConfig();
2993
+ registerAllTools(server, callApi);
2851
2994
  const transport = new StdioServerTransport();
2852
2995
  await server.connect(transport);
2853
2996
  }