@lexq/cli 0.1.7 → 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 +2 -1
- package/README.md +2 -1
- package/dist/index.js +140 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/register.d.ts +6 -1
- package/dist/mcp/register.js +41 -0
- package/dist/mcp/register.js.map +1 -1
- package/package.json +1 -1
- package/skills/lexq-simulation/SKILL.md +22 -0
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 (
|
|
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
|
|
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) {
|
|
@@ -1889,6 +1965,27 @@ function createCallApiFromConfig() {
|
|
|
1889
1965
|
return async (method, path, opts) => {
|
|
1890
1966
|
try {
|
|
1891
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
|
+
}
|
|
1892
1989
|
const clientOpts = {
|
|
1893
1990
|
apiKey: config.apiKey,
|
|
1894
1991
|
baseUrl: config.baseUrl
|
|
@@ -2612,6 +2709,47 @@ Example body:
|
|
|
2612
2709
|
params: { format }
|
|
2613
2710
|
})
|
|
2614
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
|
+
);
|
|
2615
2753
|
}
|
|
2616
2754
|
|
|
2617
2755
|
// src/mcp/tools/history.ts
|