@cortexa/core 1.0.1 → 1.1.1
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/README.md +80 -0
- package/dist/cli/index.js +332 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +286 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +278 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
- [Architecture](#architecture)
|
|
24
24
|
- [Supported Databases](#supported-databases)
|
|
25
25
|
- [CLI Reference](#cli-reference)
|
|
26
|
+
- [REST API](#rest-api)
|
|
26
27
|
- [Configuration](#configuration)
|
|
27
28
|
- [Requirements](#requirements)
|
|
28
29
|
- [Contributing](#contributing)
|
|
@@ -307,6 +308,85 @@ npm install @powersync/mysql-zongji
|
|
|
307
308
|
| `cortexa actions` | View and manage action recommendations |
|
|
308
309
|
| `cortexa explain <type> <id>` | AI explanation of anomaly, insight, or event |
|
|
309
310
|
| `cortexa ask "<question>"` | Ask a natural language question |
|
|
311
|
+
| `cortexa serve` | Start REST API server (`--port`, `--host`) |
|
|
312
|
+
|
|
313
|
+
## REST API
|
|
314
|
+
|
|
315
|
+
Start the HTTP server to access Cortexa from any language (Python, Go, Ruby, etc.):
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
npx cortexa serve
|
|
319
|
+
# Cortexa API running at http://127.0.0.1:3210
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Options: `--port <port>` (default: 3210), `--host <host>` (default: 127.0.0.1), `--no-cors`.
|
|
323
|
+
|
|
324
|
+
### Endpoints
|
|
325
|
+
|
|
326
|
+
| Method | Endpoint | Description |
|
|
327
|
+
|--------|----------|-------------|
|
|
328
|
+
| GET | `/api/status` | Connection status |
|
|
329
|
+
| GET | `/api/entities` | List classified entities |
|
|
330
|
+
| GET | `/api/relationships` | List entity relationships |
|
|
331
|
+
| GET | `/api/events` | List change events (`?entity=`, `?last=`) |
|
|
332
|
+
| GET | `/api/baselines` | Learned rate baselines |
|
|
333
|
+
| GET | `/api/anomalies` | Detected anomalies (`?severity=`, `?entity=`) |
|
|
334
|
+
| GET | `/api/insights` | State analysis insights (`?entity=`, `?severity=`) |
|
|
335
|
+
| GET | `/api/transitions` | Transition statistics (`?entity=`) |
|
|
336
|
+
| GET | `/api/correlations` | Cross-entity correlations |
|
|
337
|
+
| GET | `/api/distributions` | Value distributions (`?entity=`) |
|
|
338
|
+
| GET | `/api/graph` | Knowledge graph summary |
|
|
339
|
+
| GET | `/api/graph/export` | Full graph as JSON |
|
|
340
|
+
| GET | `/api/graph/entity/:name` | Entity intelligence |
|
|
341
|
+
| GET | `/api/actions` | Recommendations (`?status=`, `?action=`) |
|
|
342
|
+
| POST | `/api/discover` | Trigger schema discovery |
|
|
343
|
+
| POST | `/api/explain` | AI explanation (`{ type, id }`) |
|
|
344
|
+
| POST | `/api/ask` | Natural language question (`{ question }`) |
|
|
345
|
+
| POST | `/api/watch` | Start watching (`{ interval, once }`) |
|
|
346
|
+
| POST | `/api/unwatch` | Stop watching |
|
|
347
|
+
| POST | `/api/actions/:id/approve` | Approve recommendation |
|
|
348
|
+
| POST | `/api/actions/:id/reject` | Reject recommendation |
|
|
349
|
+
|
|
350
|
+
All responses return `{ ok: boolean, data?: ..., error?: string }`.
|
|
351
|
+
|
|
352
|
+
### Examples
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
# Get anomalies
|
|
356
|
+
curl http://localhost:3210/api/anomalies?severity=high
|
|
357
|
+
|
|
358
|
+
# Ask a question
|
|
359
|
+
curl -X POST http://localhost:3210/api/ask \
|
|
360
|
+
-H "Content-Type: application/json" \
|
|
361
|
+
-d '{"question": "Why did order activity spike today?"}'
|
|
362
|
+
|
|
363
|
+
# Explain an anomaly
|
|
364
|
+
curl -X POST http://localhost:3210/api/explain \
|
|
365
|
+
-H "Content-Type: application/json" \
|
|
366
|
+
-d '{"type": "anomaly", "id": 1}'
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
```python
|
|
370
|
+
# Python example
|
|
371
|
+
import requests
|
|
372
|
+
|
|
373
|
+
r = requests.get("http://localhost:3210/api/anomalies", params={"severity": "high"})
|
|
374
|
+
print(r.json()["data"])
|
|
375
|
+
|
|
376
|
+
r = requests.post("http://localhost:3210/api/ask", json={"question": "Are orders healthy?"})
|
|
377
|
+
print(r.json()["data"]["answer"])
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Programmatic usage
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
import { CortexaServer } from '@cortexa/core';
|
|
384
|
+
|
|
385
|
+
const server = new CortexaServer(config, { port: 3210, cors: true });
|
|
386
|
+
await server.start();
|
|
387
|
+
// ... later
|
|
388
|
+
await server.stop();
|
|
389
|
+
```
|
|
310
390
|
|
|
311
391
|
## Configuration
|
|
312
392
|
|
package/dist/cli/index.js
CHANGED
|
@@ -4742,6 +4742,283 @@ Return valid JSON in this exact format:
|
|
|
4742
4742
|
}
|
|
4743
4743
|
};
|
|
4744
4744
|
|
|
4745
|
+
// src/server/server.ts
|
|
4746
|
+
import { createServer } from "http";
|
|
4747
|
+
|
|
4748
|
+
// src/server/router.ts
|
|
4749
|
+
var Router = class {
|
|
4750
|
+
routes = [];
|
|
4751
|
+
get(path4, handler) {
|
|
4752
|
+
this.addRoute("GET", path4, handler);
|
|
4753
|
+
}
|
|
4754
|
+
post(path4, handler) {
|
|
4755
|
+
this.addRoute("POST", path4, handler);
|
|
4756
|
+
}
|
|
4757
|
+
addRoute(method, path4, handler) {
|
|
4758
|
+
const paramNames = [];
|
|
4759
|
+
const patternStr = path4.replace(/:(\w+)/g, (_match, name) => {
|
|
4760
|
+
paramNames.push(name);
|
|
4761
|
+
return "([^/]+)";
|
|
4762
|
+
});
|
|
4763
|
+
this.routes.push({
|
|
4764
|
+
method,
|
|
4765
|
+
pattern: new RegExp(`^${patternStr}$`),
|
|
4766
|
+
paramNames,
|
|
4767
|
+
handler
|
|
4768
|
+
});
|
|
4769
|
+
}
|
|
4770
|
+
async handle(req, res, cors) {
|
|
4771
|
+
if (cors) {
|
|
4772
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
4773
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
4774
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
4775
|
+
}
|
|
4776
|
+
if (req.method === "OPTIONS") {
|
|
4777
|
+
res.writeHead(204);
|
|
4778
|
+
res.end();
|
|
4779
|
+
return;
|
|
4780
|
+
}
|
|
4781
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
4782
|
+
const path4 = url.pathname;
|
|
4783
|
+
const method = req.method ?? "GET";
|
|
4784
|
+
const query = {};
|
|
4785
|
+
url.searchParams.forEach((value, key) => {
|
|
4786
|
+
query[key] = value;
|
|
4787
|
+
});
|
|
4788
|
+
for (const route of this.routes) {
|
|
4789
|
+
if (route.method !== method) continue;
|
|
4790
|
+
const match = route.pattern.exec(path4);
|
|
4791
|
+
if (!match) continue;
|
|
4792
|
+
const pathParams = {};
|
|
4793
|
+
for (let i = 0; i < route.paramNames.length; i++) {
|
|
4794
|
+
pathParams[route.paramNames[i]] = match[i + 1];
|
|
4795
|
+
}
|
|
4796
|
+
let body = void 0;
|
|
4797
|
+
if (method === "POST") {
|
|
4798
|
+
body = await readBody(req);
|
|
4799
|
+
}
|
|
4800
|
+
const params = { path: path4, method, query, body, pathParams };
|
|
4801
|
+
try {
|
|
4802
|
+
const result = await route.handler(params);
|
|
4803
|
+
sendJson(res, result.ok ? 200 : 400, result);
|
|
4804
|
+
} catch (err) {
|
|
4805
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
4806
|
+
sendJson(res, 500, { ok: false, error: message });
|
|
4807
|
+
}
|
|
4808
|
+
return;
|
|
4809
|
+
}
|
|
4810
|
+
sendJson(res, 404, { ok: false, error: `Not found: ${method} ${path4}` });
|
|
4811
|
+
}
|
|
4812
|
+
};
|
|
4813
|
+
function sendJson(res, statusCode, data) {
|
|
4814
|
+
res.writeHead(statusCode, { "Content-Type": "application/json" });
|
|
4815
|
+
res.end(JSON.stringify(data));
|
|
4816
|
+
}
|
|
4817
|
+
function readBody(req) {
|
|
4818
|
+
return new Promise((resolve, reject) => {
|
|
4819
|
+
const chunks = [];
|
|
4820
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
4821
|
+
req.on("end", () => {
|
|
4822
|
+
const raw = Buffer.concat(chunks).toString();
|
|
4823
|
+
if (!raw) {
|
|
4824
|
+
resolve(void 0);
|
|
4825
|
+
return;
|
|
4826
|
+
}
|
|
4827
|
+
try {
|
|
4828
|
+
resolve(JSON.parse(raw));
|
|
4829
|
+
} catch {
|
|
4830
|
+
reject(new Error("Invalid JSON body"));
|
|
4831
|
+
}
|
|
4832
|
+
});
|
|
4833
|
+
req.on("error", reject);
|
|
4834
|
+
});
|
|
4835
|
+
}
|
|
4836
|
+
|
|
4837
|
+
// src/server/handlers.ts
|
|
4838
|
+
function registerHandlers(router, cortexa) {
|
|
4839
|
+
router.get("/api/status", async () => {
|
|
4840
|
+
return { ok: true, data: cortexa.status };
|
|
4841
|
+
});
|
|
4842
|
+
router.get("/api/entities", async () => {
|
|
4843
|
+
const { entities } = cortexa.exportGraph();
|
|
4844
|
+
return { ok: true, data: entities };
|
|
4845
|
+
});
|
|
4846
|
+
router.get("/api/relationships", async () => {
|
|
4847
|
+
const { relationships } = cortexa.exportGraph();
|
|
4848
|
+
return { ok: true, data: relationships };
|
|
4849
|
+
});
|
|
4850
|
+
router.post("/api/discover", async (params) => {
|
|
4851
|
+
const body = params.body ?? {};
|
|
4852
|
+
const result = await cortexa.discover({
|
|
4853
|
+
includeSampleData: body.includeSampleData !== false,
|
|
4854
|
+
excludeTables: body.excludeTables,
|
|
4855
|
+
excludeColumns: body.excludeColumns
|
|
4856
|
+
});
|
|
4857
|
+
return {
|
|
4858
|
+
ok: true,
|
|
4859
|
+
data: {
|
|
4860
|
+
tables: result.raw.tables.length,
|
|
4861
|
+
entities: result.entities.length,
|
|
4862
|
+
relationships: result.relationships.length
|
|
4863
|
+
}
|
|
4864
|
+
};
|
|
4865
|
+
});
|
|
4866
|
+
router.get("/api/events", async (params) => {
|
|
4867
|
+
const filter = {};
|
|
4868
|
+
if (params.query.entity) filter.entity = params.query.entity;
|
|
4869
|
+
if (params.query.last) filter.last = parseInt(params.query.last, 10);
|
|
4870
|
+
if (params.query.operation) filter.operation = params.query.operation;
|
|
4871
|
+
return { ok: true, data: cortexa.getEvents(filter) };
|
|
4872
|
+
});
|
|
4873
|
+
router.get("/api/baselines", async () => {
|
|
4874
|
+
return { ok: true, data: cortexa.getBaselines() };
|
|
4875
|
+
});
|
|
4876
|
+
router.get("/api/anomalies", async (params) => {
|
|
4877
|
+
const filter = {};
|
|
4878
|
+
if (params.query.entity) filter.entity = params.query.entity;
|
|
4879
|
+
if (params.query.last) filter.last = parseInt(params.query.last, 10);
|
|
4880
|
+
if (params.query.severity) filter.severity = params.query.severity;
|
|
4881
|
+
return { ok: true, data: cortexa.getAnomalies(filter) };
|
|
4882
|
+
});
|
|
4883
|
+
router.get("/api/insights", async (params) => {
|
|
4884
|
+
const filter = {};
|
|
4885
|
+
if (params.query.entity) filter.entity = params.query.entity;
|
|
4886
|
+
if (params.query.last) filter.last = parseInt(params.query.last, 10);
|
|
4887
|
+
if (params.query.severity) filter.severity = params.query.severity;
|
|
4888
|
+
return { ok: true, data: cortexa.getInsights(filter) };
|
|
4889
|
+
});
|
|
4890
|
+
router.get("/api/transitions", async (params) => {
|
|
4891
|
+
return { ok: true, data: cortexa.getTransitions(params.query.entity) };
|
|
4892
|
+
});
|
|
4893
|
+
router.get("/api/correlations", async () => {
|
|
4894
|
+
return { ok: true, data: cortexa.getCorrelations() };
|
|
4895
|
+
});
|
|
4896
|
+
router.get("/api/distributions", async (params) => {
|
|
4897
|
+
return { ok: true, data: cortexa.getDistributions(params.query.entity) };
|
|
4898
|
+
});
|
|
4899
|
+
router.get("/api/graph", async () => {
|
|
4900
|
+
return { ok: true, data: cortexa.graph().getSummary() };
|
|
4901
|
+
});
|
|
4902
|
+
router.get("/api/graph/export", async () => {
|
|
4903
|
+
return { ok: true, data: cortexa.graph().export() };
|
|
4904
|
+
});
|
|
4905
|
+
router.get("/api/graph/entity/:name", async (params) => {
|
|
4906
|
+
const intel = cortexa.graph().entity(params.pathParams.name).intelligence();
|
|
4907
|
+
if (!intel) {
|
|
4908
|
+
return { ok: false, error: `Entity "${params.pathParams.name}" not found in knowledge graph` };
|
|
4909
|
+
}
|
|
4910
|
+
return { ok: true, data: intel };
|
|
4911
|
+
});
|
|
4912
|
+
router.get("/api/actions", async (params) => {
|
|
4913
|
+
const filter = {};
|
|
4914
|
+
if (params.query.status) filter.status = params.query.status;
|
|
4915
|
+
if (params.query.action) filter.action = params.query.action;
|
|
4916
|
+
return { ok: true, data: cortexa.getRecommendations(filter) };
|
|
4917
|
+
});
|
|
4918
|
+
router.post("/api/actions/:id/approve", async (params) => {
|
|
4919
|
+
const id = parseInt(params.pathParams.id, 10);
|
|
4920
|
+
if (isNaN(id)) return { ok: false, error: "Invalid id" };
|
|
4921
|
+
cortexa.approveRecommendation(id);
|
|
4922
|
+
return { ok: true, data: { id, status: "approved" } };
|
|
4923
|
+
});
|
|
4924
|
+
router.post("/api/actions/:id/reject", async (params) => {
|
|
4925
|
+
const id = parseInt(params.pathParams.id, 10);
|
|
4926
|
+
if (isNaN(id)) return { ok: false, error: "Invalid id" };
|
|
4927
|
+
cortexa.rejectRecommendation(id);
|
|
4928
|
+
return { ok: true, data: { id, status: "rejected" } };
|
|
4929
|
+
});
|
|
4930
|
+
router.post("/api/explain", async (params) => {
|
|
4931
|
+
const body = params.body;
|
|
4932
|
+
if (!body?.type || !body?.id) {
|
|
4933
|
+
return { ok: false, error: 'Required: { type: "anomaly"|"insight"|"event"|"entity", id: number }' };
|
|
4934
|
+
}
|
|
4935
|
+
const target = {
|
|
4936
|
+
type: body.type,
|
|
4937
|
+
id: body.id
|
|
4938
|
+
};
|
|
4939
|
+
const result = await cortexa.explain(target);
|
|
4940
|
+
return { ok: true, data: result };
|
|
4941
|
+
});
|
|
4942
|
+
router.post("/api/ask", async (params) => {
|
|
4943
|
+
const body = params.body;
|
|
4944
|
+
if (!body?.question || typeof body.question !== "string") {
|
|
4945
|
+
return { ok: false, error: "Required: { question: string }" };
|
|
4946
|
+
}
|
|
4947
|
+
const result = await cortexa.ask(body.question, {
|
|
4948
|
+
entityHint: body.entity
|
|
4949
|
+
});
|
|
4950
|
+
return { ok: true, data: result };
|
|
4951
|
+
});
|
|
4952
|
+
router.post("/api/watch", async (params) => {
|
|
4953
|
+
const body = params.body ?? {};
|
|
4954
|
+
await cortexa.watch({
|
|
4955
|
+
interval: body.interval,
|
|
4956
|
+
once: body.once
|
|
4957
|
+
});
|
|
4958
|
+
return { ok: true, data: { watching: true } };
|
|
4959
|
+
});
|
|
4960
|
+
router.post("/api/unwatch", async () => {
|
|
4961
|
+
cortexa.unwatch();
|
|
4962
|
+
return { ok: true, data: { watching: false } };
|
|
4963
|
+
});
|
|
4964
|
+
}
|
|
4965
|
+
|
|
4966
|
+
// src/server/server.ts
|
|
4967
|
+
var CortexaServer = class {
|
|
4968
|
+
cortexa;
|
|
4969
|
+
serverConfig;
|
|
4970
|
+
logger;
|
|
4971
|
+
httpServer = null;
|
|
4972
|
+
constructor(cortexaConfig, serverConfig) {
|
|
4973
|
+
this.cortexa = new Cortexa(cortexaConfig);
|
|
4974
|
+
this.serverConfig = {
|
|
4975
|
+
port: serverConfig?.port ?? 3210,
|
|
4976
|
+
host: serverConfig?.host ?? "127.0.0.1",
|
|
4977
|
+
cors: serverConfig?.cors ?? true
|
|
4978
|
+
};
|
|
4979
|
+
this.logger = createLogger();
|
|
4980
|
+
}
|
|
4981
|
+
async start() {
|
|
4982
|
+
await this.cortexa.connect();
|
|
4983
|
+
const router = new Router();
|
|
4984
|
+
registerHandlers(router, this.cortexa);
|
|
4985
|
+
const cors = this.serverConfig.cors ?? true;
|
|
4986
|
+
this.httpServer = createServer((req, res) => {
|
|
4987
|
+
router.handle(req, res, cors).catch((err) => {
|
|
4988
|
+
this.logger.error({ err }, "Unhandled request error");
|
|
4989
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
4990
|
+
res.end(JSON.stringify({ ok: false, error: "Internal server error" }));
|
|
4991
|
+
});
|
|
4992
|
+
});
|
|
4993
|
+
const { port, host } = this.serverConfig;
|
|
4994
|
+
await new Promise((resolve) => {
|
|
4995
|
+
this.httpServer.listen(port, host, () => {
|
|
4996
|
+
this.logger.info({ port, host }, "Cortexa REST API server started");
|
|
4997
|
+
resolve();
|
|
4998
|
+
});
|
|
4999
|
+
});
|
|
5000
|
+
}
|
|
5001
|
+
async stop() {
|
|
5002
|
+
if (this.httpServer) {
|
|
5003
|
+
await new Promise((resolve, reject) => {
|
|
5004
|
+
this.httpServer.close((err) => {
|
|
5005
|
+
if (err) reject(err);
|
|
5006
|
+
else resolve();
|
|
5007
|
+
});
|
|
5008
|
+
});
|
|
5009
|
+
this.httpServer = null;
|
|
5010
|
+
}
|
|
5011
|
+
await this.cortexa.disconnect();
|
|
5012
|
+
this.logger.info("Cortexa REST API server stopped");
|
|
5013
|
+
}
|
|
5014
|
+
get address() {
|
|
5015
|
+
return {
|
|
5016
|
+
port: this.serverConfig.port ?? 3210,
|
|
5017
|
+
host: this.serverConfig.host ?? "127.0.0.1"
|
|
5018
|
+
};
|
|
5019
|
+
}
|
|
5020
|
+
};
|
|
5021
|
+
|
|
4745
5022
|
// src/index.ts
|
|
4746
5023
|
var KnowledgeGraph = class {
|
|
4747
5024
|
store;
|
|
@@ -5811,6 +6088,58 @@ Related Entities: ${result.relatedEntities.join(", ")}`);
|
|
|
5811
6088
|
}
|
|
5812
6089
|
}
|
|
5813
6090
|
|
|
6091
|
+
// src/cli/serve.ts
|
|
6092
|
+
async function runServe(options) {
|
|
6093
|
+
try {
|
|
6094
|
+
const config = await loadConfig();
|
|
6095
|
+
const port = options.port ? parseInt(options.port, 10) : 3210;
|
|
6096
|
+
const host = options.host ?? "127.0.0.1";
|
|
6097
|
+
const server = new CortexaServer(
|
|
6098
|
+
{ ...config, projectRoot: process.cwd() },
|
|
6099
|
+
{ port, host, cors: !options.noCors }
|
|
6100
|
+
);
|
|
6101
|
+
console.log("Starting Cortexa REST API server...");
|
|
6102
|
+
await server.start();
|
|
6103
|
+
console.log("");
|
|
6104
|
+
console.log(` Cortexa API running at http://${host}:${port}`);
|
|
6105
|
+
console.log("");
|
|
6106
|
+
console.log(" Endpoints:");
|
|
6107
|
+
console.log(" GET /api/status Connection status");
|
|
6108
|
+
console.log(" GET /api/entities List entities");
|
|
6109
|
+
console.log(" GET /api/relationships List relationships");
|
|
6110
|
+
console.log(" GET /api/events List events");
|
|
6111
|
+
console.log(" GET /api/baselines List baselines");
|
|
6112
|
+
console.log(" GET /api/anomalies List anomalies");
|
|
6113
|
+
console.log(" GET /api/insights List insights");
|
|
6114
|
+
console.log(" GET /api/transitions Transition stats");
|
|
6115
|
+
console.log(" GET /api/correlations Correlation status");
|
|
6116
|
+
console.log(" GET /api/distributions Distribution summaries");
|
|
6117
|
+
console.log(" GET /api/graph Knowledge graph summary");
|
|
6118
|
+
console.log(" GET /api/graph/export Export full graph");
|
|
6119
|
+
console.log(" GET /api/graph/entity/:n Entity intelligence");
|
|
6120
|
+
console.log(" GET /api/actions Recommendations");
|
|
6121
|
+
console.log(" POST /api/discover Trigger discovery");
|
|
6122
|
+
console.log(" POST /api/explain Explain anomaly/insight");
|
|
6123
|
+
console.log(" POST /api/ask Ask a question");
|
|
6124
|
+
console.log(" POST /api/watch Start watching");
|
|
6125
|
+
console.log(" POST /api/unwatch Stop watching");
|
|
6126
|
+
console.log(" POST /api/actions/:id/approve");
|
|
6127
|
+
console.log(" POST /api/actions/:id/reject");
|
|
6128
|
+
console.log("");
|
|
6129
|
+
console.log(" Press Ctrl+C to stop");
|
|
6130
|
+
const shutdown = async () => {
|
|
6131
|
+
console.log("\nShutting down...");
|
|
6132
|
+
await server.stop();
|
|
6133
|
+
process.exit(0);
|
|
6134
|
+
};
|
|
6135
|
+
process.on("SIGINT", () => void shutdown());
|
|
6136
|
+
process.on("SIGTERM", () => void shutdown());
|
|
6137
|
+
} catch (error) {
|
|
6138
|
+
console.error(`Error: ${error.message}`);
|
|
6139
|
+
process.exit(1);
|
|
6140
|
+
}
|
|
6141
|
+
}
|
|
6142
|
+
|
|
5814
6143
|
// src/cli/banner.ts
|
|
5815
6144
|
var RESET = "\x1B[0m";
|
|
5816
6145
|
var BOLD = "\x1B[1m";
|
|
@@ -5917,5 +6246,8 @@ program.command("explain <type> <id>").description("Explain why an anomaly, insi
|
|
|
5917
6246
|
program.command("ask <question>").description("Ask a natural language question about your database").option("--json", "Output raw JSON instead of formatted text").option("--entity <name>", "Focus context on a specific entity").action(async (question, options) => {
|
|
5918
6247
|
await runAsk(question, options);
|
|
5919
6248
|
});
|
|
6249
|
+
program.command("serve").description("Start REST API server for cross-language access").option("--port <port>", "Port to listen on (default: 3210)").option("--host <host>", "Host to bind to (default: 127.0.0.1)").option("--no-cors", "Disable CORS headers").action(async (options) => {
|
|
6250
|
+
await runServe(options);
|
|
6251
|
+
});
|
|
5920
6252
|
program.parse();
|
|
5921
6253
|
//# sourceMappingURL=index.js.map
|