@a-company/sentinel 0.2.0 → 3.5.0
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/adapters/express.d.ts +3 -1
- package/dist/adapters/fastify.d.ts +3 -1
- package/dist/adapters/hono.d.ts +3 -1
- package/dist/{chunk-KPMG4XED.js → chunk-FOF7CPJ6.js} +994 -2
- package/dist/chunk-VQ3SIN7S.js +422 -0
- package/dist/cli.js +6 -6
- package/dist/{commands-KIMGFR2I.js → commands-7PHRWGOB.js} +1791 -289
- package/dist/{dist-2F7NO4H4.js → dist-AG5JNIZU.js} +27 -2
- package/dist/{dist-BPWLYV4U.js → dist-TYG2XME3.js} +27 -2
- package/dist/index.d.ts +47 -5
- package/dist/index.js +141 -186
- package/dist/mcp.js +1040 -9
- package/dist/sdk-BTblv--p.d.ts +180 -0
- package/dist/server/index.d.ts +19 -3
- package/dist/server/index.js +581 -9
- package/dist/storage-BqCJqZat.d.ts +129 -0
- package/dist/transport-DqamniUy.d.ts +185 -0
- package/dist/transport.d.ts +2 -0
- package/dist/transport.js +10 -0
- package/dist/{sdk-B27_vK1g.d.ts → types-BmVoO1iF.d.ts} +196 -259
- package/package.json +15 -1
- package/ui/dist/assets/{index-DPxatSdT.css → index-9iUtfyBP.css} +1 -1
- package/ui/dist/assets/index-BfINPxlF.js +62 -0
- package/ui/dist/assets/index-BfINPxlF.js.map +1 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-BNgsn_C8.js +0 -62
- package/ui/dist/assets/index-BNgsn_C8.js.map +0 -1
|
@@ -3,7 +3,7 @@ import initSqlJs from "sql.js";
|
|
|
3
3
|
import { v4 as uuidv4 } from "uuid";
|
|
4
4
|
import * as path from "path";
|
|
5
5
|
import * as fs from "fs";
|
|
6
|
-
var SCHEMA_VERSION =
|
|
6
|
+
var SCHEMA_VERSION = 4;
|
|
7
7
|
var DEFAULT_CONFIDENCE = {
|
|
8
8
|
score: 50,
|
|
9
9
|
timesMatched: 0,
|
|
@@ -123,6 +123,72 @@ var SentinelStorage = class {
|
|
|
123
123
|
notes TEXT
|
|
124
124
|
);
|
|
125
125
|
|
|
126
|
+
-- Structured logs table
|
|
127
|
+
CREATE TABLE IF NOT EXISTS logs (
|
|
128
|
+
id TEXT PRIMARY KEY,
|
|
129
|
+
timestamp TEXT NOT NULL,
|
|
130
|
+
level TEXT NOT NULL CHECK (level IN ('debug','info','warn','error')),
|
|
131
|
+
symbol TEXT NOT NULL,
|
|
132
|
+
symbol_type TEXT NOT NULL DEFAULT 'raw',
|
|
133
|
+
message TEXT NOT NULL,
|
|
134
|
+
data_json TEXT,
|
|
135
|
+
service TEXT NOT NULL,
|
|
136
|
+
session_id TEXT,
|
|
137
|
+
correlation_id TEXT,
|
|
138
|
+
duration_ms REAL,
|
|
139
|
+
environment TEXT
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
-- Service registry
|
|
143
|
+
CREATE TABLE IF NOT EXISTS services (
|
|
144
|
+
name TEXT PRIMARY KEY,
|
|
145
|
+
version TEXT,
|
|
146
|
+
pid INTEGER,
|
|
147
|
+
started_at TEXT NOT NULL,
|
|
148
|
+
last_seen_at TEXT NOT NULL,
|
|
149
|
+
environment TEXT,
|
|
150
|
+
metadata_json TEXT
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
-- Live app state snapshots (latest-wins per service+session)
|
|
154
|
+
CREATE TABLE IF NOT EXISTS app_state (
|
|
155
|
+
service TEXT NOT NULL,
|
|
156
|
+
session_id TEXT NOT NULL,
|
|
157
|
+
timestamp TEXT NOT NULL,
|
|
158
|
+
state_json TEXT NOT NULL,
|
|
159
|
+
active_flows_json TEXT,
|
|
160
|
+
active_gates_json TEXT,
|
|
161
|
+
PRIMARY KEY (service, session_id)
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
-- Metrics table
|
|
165
|
+
CREATE TABLE IF NOT EXISTS metrics (
|
|
166
|
+
id TEXT PRIMARY KEY,
|
|
167
|
+
timestamp TEXT NOT NULL,
|
|
168
|
+
name TEXT NOT NULL,
|
|
169
|
+
type TEXT NOT NULL CHECK (type IN ('counter','gauge','histogram')),
|
|
170
|
+
value REAL NOT NULL,
|
|
171
|
+
tags_json TEXT DEFAULT '{}',
|
|
172
|
+
service TEXT NOT NULL,
|
|
173
|
+
environment TEXT
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
-- Traces table
|
|
177
|
+
CREATE TABLE IF NOT EXISTS traces (
|
|
178
|
+
trace_id TEXT NOT NULL,
|
|
179
|
+
span_id TEXT PRIMARY KEY,
|
|
180
|
+
parent_span_id TEXT,
|
|
181
|
+
service TEXT NOT NULL,
|
|
182
|
+
symbol TEXT NOT NULL,
|
|
183
|
+
operation TEXT NOT NULL,
|
|
184
|
+
start_time TEXT NOT NULL,
|
|
185
|
+
end_time TEXT,
|
|
186
|
+
duration_ms REAL,
|
|
187
|
+
status TEXT NOT NULL DEFAULT 'ok',
|
|
188
|
+
tags_json TEXT DEFAULT '{}',
|
|
189
|
+
log_ids_json TEXT DEFAULT '[]'
|
|
190
|
+
);
|
|
191
|
+
|
|
126
192
|
-- Indexes
|
|
127
193
|
CREATE INDEX IF NOT EXISTS idx_incidents_timestamp ON incidents(timestamp);
|
|
128
194
|
CREATE INDEX IF NOT EXISTS idx_incidents_status ON incidents(status);
|
|
@@ -132,6 +198,18 @@ var SentinelStorage = class {
|
|
|
132
198
|
CREATE INDEX IF NOT EXISTS idx_practice_events_habit_id ON practice_events(habit_id);
|
|
133
199
|
CREATE INDEX IF NOT EXISTS idx_practice_events_engineer ON practice_events(engineer);
|
|
134
200
|
CREATE INDEX IF NOT EXISTS idx_practice_events_session_id ON practice_events(session_id);
|
|
201
|
+
CREATE INDEX IF NOT EXISTS idx_logs_timestamp ON logs(timestamp);
|
|
202
|
+
CREATE INDEX IF NOT EXISTS idx_logs_level ON logs(level);
|
|
203
|
+
CREATE INDEX IF NOT EXISTS idx_logs_symbol ON logs(symbol);
|
|
204
|
+
CREATE INDEX IF NOT EXISTS idx_logs_service ON logs(service);
|
|
205
|
+
CREATE INDEX IF NOT EXISTS idx_logs_session_id ON logs(session_id);
|
|
206
|
+
CREATE INDEX IF NOT EXISTS idx_logs_correlation_id ON logs(correlation_id);
|
|
207
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_timestamp ON metrics(timestamp);
|
|
208
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_name ON metrics(name);
|
|
209
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_service ON metrics(service);
|
|
210
|
+
CREATE INDEX IF NOT EXISTS idx_traces_trace_id ON traces(trace_id);
|
|
211
|
+
CREATE INDEX IF NOT EXISTS idx_traces_service ON traces(service);
|
|
212
|
+
CREATE INDEX IF NOT EXISTS idx_traces_start_time ON traces(start_time);
|
|
135
213
|
`);
|
|
136
214
|
this.db.run(
|
|
137
215
|
"INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', ?)",
|
|
@@ -261,6 +339,101 @@ var SentinelStorage = class {
|
|
|
261
339
|
this.db.run(
|
|
262
340
|
"INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', '2')"
|
|
263
341
|
);
|
|
342
|
+
currentVersion = 2;
|
|
343
|
+
}
|
|
344
|
+
if (currentVersion < 3) {
|
|
345
|
+
try {
|
|
346
|
+
this.db.run(`
|
|
347
|
+
CREATE TABLE IF NOT EXISTS logs (
|
|
348
|
+
id TEXT PRIMARY KEY,
|
|
349
|
+
timestamp TEXT NOT NULL,
|
|
350
|
+
level TEXT NOT NULL CHECK (level IN ('debug','info','warn','error')),
|
|
351
|
+
symbol TEXT NOT NULL,
|
|
352
|
+
symbol_type TEXT NOT NULL DEFAULT 'raw',
|
|
353
|
+
message TEXT NOT NULL,
|
|
354
|
+
data_json TEXT,
|
|
355
|
+
service TEXT NOT NULL,
|
|
356
|
+
session_id TEXT,
|
|
357
|
+
correlation_id TEXT,
|
|
358
|
+
duration_ms REAL,
|
|
359
|
+
environment TEXT
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
CREATE TABLE IF NOT EXISTS services (
|
|
363
|
+
name TEXT PRIMARY KEY,
|
|
364
|
+
version TEXT,
|
|
365
|
+
pid INTEGER,
|
|
366
|
+
started_at TEXT NOT NULL,
|
|
367
|
+
last_seen_at TEXT NOT NULL,
|
|
368
|
+
environment TEXT,
|
|
369
|
+
metadata_json TEXT
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
CREATE TABLE IF NOT EXISTS app_state (
|
|
373
|
+
service TEXT NOT NULL,
|
|
374
|
+
session_id TEXT NOT NULL,
|
|
375
|
+
timestamp TEXT NOT NULL,
|
|
376
|
+
state_json TEXT NOT NULL,
|
|
377
|
+
active_flows_json TEXT,
|
|
378
|
+
active_gates_json TEXT,
|
|
379
|
+
PRIMARY KEY (service, session_id)
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
CREATE INDEX IF NOT EXISTS idx_logs_timestamp ON logs(timestamp);
|
|
383
|
+
CREATE INDEX IF NOT EXISTS idx_logs_level ON logs(level);
|
|
384
|
+
CREATE INDEX IF NOT EXISTS idx_logs_symbol ON logs(symbol);
|
|
385
|
+
CREATE INDEX IF NOT EXISTS idx_logs_service ON logs(service);
|
|
386
|
+
CREATE INDEX IF NOT EXISTS idx_logs_session_id ON logs(session_id);
|
|
387
|
+
CREATE INDEX IF NOT EXISTS idx_logs_correlation_id ON logs(correlation_id);
|
|
388
|
+
`);
|
|
389
|
+
} catch {
|
|
390
|
+
}
|
|
391
|
+
this.db.run(
|
|
392
|
+
"INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', '3')"
|
|
393
|
+
);
|
|
394
|
+
currentVersion = 3;
|
|
395
|
+
}
|
|
396
|
+
if (currentVersion < 4) {
|
|
397
|
+
try {
|
|
398
|
+
this.db.run(`
|
|
399
|
+
CREATE TABLE IF NOT EXISTS metrics (
|
|
400
|
+
id TEXT PRIMARY KEY,
|
|
401
|
+
timestamp TEXT NOT NULL,
|
|
402
|
+
name TEXT NOT NULL,
|
|
403
|
+
type TEXT NOT NULL CHECK (type IN ('counter','gauge','histogram')),
|
|
404
|
+
value REAL NOT NULL,
|
|
405
|
+
tags_json TEXT DEFAULT '{}',
|
|
406
|
+
service TEXT NOT NULL,
|
|
407
|
+
environment TEXT
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
CREATE TABLE IF NOT EXISTS traces (
|
|
411
|
+
trace_id TEXT NOT NULL,
|
|
412
|
+
span_id TEXT PRIMARY KEY,
|
|
413
|
+
parent_span_id TEXT,
|
|
414
|
+
service TEXT NOT NULL,
|
|
415
|
+
symbol TEXT NOT NULL,
|
|
416
|
+
operation TEXT NOT NULL,
|
|
417
|
+
start_time TEXT NOT NULL,
|
|
418
|
+
end_time TEXT,
|
|
419
|
+
duration_ms REAL,
|
|
420
|
+
status TEXT NOT NULL DEFAULT 'ok',
|
|
421
|
+
tags_json TEXT DEFAULT '{}',
|
|
422
|
+
log_ids_json TEXT DEFAULT '[]'
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_timestamp ON metrics(timestamp);
|
|
426
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_name ON metrics(name);
|
|
427
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_service ON metrics(service);
|
|
428
|
+
CREATE INDEX IF NOT EXISTS idx_traces_trace_id ON traces(trace_id);
|
|
429
|
+
CREATE INDEX IF NOT EXISTS idx_traces_service ON traces(service);
|
|
430
|
+
CREATE INDEX IF NOT EXISTS idx_traces_start_time ON traces(start_time);
|
|
431
|
+
`);
|
|
432
|
+
} catch {
|
|
433
|
+
}
|
|
434
|
+
this.db.run(
|
|
435
|
+
"INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', '4')"
|
|
436
|
+
);
|
|
264
437
|
}
|
|
265
438
|
}
|
|
266
439
|
/**
|
|
@@ -1235,6 +1408,620 @@ var SentinelStorage = class {
|
|
|
1235
1408
|
notes: obj.notes || void 0
|
|
1236
1409
|
};
|
|
1237
1410
|
}
|
|
1411
|
+
// ─── Structured Logs ─────────────────────────────────────────────
|
|
1412
|
+
inferSymbolType(symbol) {
|
|
1413
|
+
if (symbol.startsWith("#")) return "component";
|
|
1414
|
+
if (symbol.startsWith("^")) return "gate";
|
|
1415
|
+
if (symbol.startsWith("!")) return "signal";
|
|
1416
|
+
if (symbol.startsWith("$")) return "flow";
|
|
1417
|
+
if (symbol.startsWith("~")) return "aspect";
|
|
1418
|
+
return "raw";
|
|
1419
|
+
}
|
|
1420
|
+
insertLog(input) {
|
|
1421
|
+
this.initializeSync();
|
|
1422
|
+
const id = input.id || uuidv4();
|
|
1423
|
+
const timestamp = input.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1424
|
+
const symbolType = input.symbolType || this.inferSymbolType(input.symbol);
|
|
1425
|
+
this.db.run(
|
|
1426
|
+
`INSERT INTO logs (
|
|
1427
|
+
id, timestamp, level, symbol, symbol_type, message, data_json,
|
|
1428
|
+
service, session_id, correlation_id, duration_ms, environment
|
|
1429
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1430
|
+
[
|
|
1431
|
+
id,
|
|
1432
|
+
timestamp,
|
|
1433
|
+
input.level,
|
|
1434
|
+
input.symbol,
|
|
1435
|
+
symbolType,
|
|
1436
|
+
input.message,
|
|
1437
|
+
input.data ? JSON.stringify(input.data) : null,
|
|
1438
|
+
input.service,
|
|
1439
|
+
input.sessionId || null,
|
|
1440
|
+
input.correlationId || null,
|
|
1441
|
+
input.durationMs ?? null,
|
|
1442
|
+
input.environment || null
|
|
1443
|
+
]
|
|
1444
|
+
);
|
|
1445
|
+
this.save();
|
|
1446
|
+
return id;
|
|
1447
|
+
}
|
|
1448
|
+
insertLogBatch(entries) {
|
|
1449
|
+
this.initializeSync();
|
|
1450
|
+
let accepted = 0;
|
|
1451
|
+
const errors = [];
|
|
1452
|
+
for (const input of entries) {
|
|
1453
|
+
try {
|
|
1454
|
+
const id = input.id || uuidv4();
|
|
1455
|
+
const timestamp = input.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1456
|
+
const symbolType = input.symbolType || this.inferSymbolType(input.symbol);
|
|
1457
|
+
this.db.run(
|
|
1458
|
+
`INSERT INTO logs (
|
|
1459
|
+
id, timestamp, level, symbol, symbol_type, message, data_json,
|
|
1460
|
+
service, session_id, correlation_id, duration_ms, environment
|
|
1461
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1462
|
+
[
|
|
1463
|
+
id,
|
|
1464
|
+
timestamp,
|
|
1465
|
+
input.level,
|
|
1466
|
+
input.symbol,
|
|
1467
|
+
symbolType,
|
|
1468
|
+
input.message,
|
|
1469
|
+
input.data ? JSON.stringify(input.data) : null,
|
|
1470
|
+
input.service,
|
|
1471
|
+
input.sessionId || null,
|
|
1472
|
+
input.correlationId || null,
|
|
1473
|
+
input.durationMs ?? null,
|
|
1474
|
+
input.environment || null
|
|
1475
|
+
]
|
|
1476
|
+
);
|
|
1477
|
+
accepted++;
|
|
1478
|
+
} catch (err) {
|
|
1479
|
+
errors.push(err instanceof Error ? err.message : String(err));
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
this.save();
|
|
1483
|
+
return { accepted, errors };
|
|
1484
|
+
}
|
|
1485
|
+
queryLogs(options = {}) {
|
|
1486
|
+
this.initializeSync();
|
|
1487
|
+
const { limit = 100, offset = 0 } = options;
|
|
1488
|
+
const conditions = [];
|
|
1489
|
+
const params = [];
|
|
1490
|
+
if (options.level) {
|
|
1491
|
+
conditions.push("level = ?");
|
|
1492
|
+
params.push(options.level);
|
|
1493
|
+
}
|
|
1494
|
+
if (options.symbol) {
|
|
1495
|
+
conditions.push("symbol LIKE ?");
|
|
1496
|
+
params.push(`%${options.symbol}%`);
|
|
1497
|
+
}
|
|
1498
|
+
if (options.service) {
|
|
1499
|
+
conditions.push("service = ?");
|
|
1500
|
+
params.push(options.service);
|
|
1501
|
+
}
|
|
1502
|
+
if (options.sessionId) {
|
|
1503
|
+
conditions.push("session_id = ?");
|
|
1504
|
+
params.push(options.sessionId);
|
|
1505
|
+
}
|
|
1506
|
+
if (options.correlationId) {
|
|
1507
|
+
conditions.push("correlation_id = ?");
|
|
1508
|
+
params.push(options.correlationId);
|
|
1509
|
+
}
|
|
1510
|
+
if (options.search) {
|
|
1511
|
+
conditions.push("message LIKE ?");
|
|
1512
|
+
params.push(`%${options.search}%`);
|
|
1513
|
+
}
|
|
1514
|
+
if (options.since) {
|
|
1515
|
+
conditions.push("timestamp >= ?");
|
|
1516
|
+
params.push(options.since);
|
|
1517
|
+
}
|
|
1518
|
+
if (options.until) {
|
|
1519
|
+
conditions.push("timestamp <= ?");
|
|
1520
|
+
params.push(options.until);
|
|
1521
|
+
}
|
|
1522
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1523
|
+
const result = this.db.exec(
|
|
1524
|
+
`SELECT * FROM logs ${whereClause} ORDER BY timestamp DESC LIMIT ? OFFSET ?`,
|
|
1525
|
+
[...params, limit, offset]
|
|
1526
|
+
);
|
|
1527
|
+
if (result.length === 0) return [];
|
|
1528
|
+
return result[0].values.map(
|
|
1529
|
+
(row) => this.rowToLogEntry(result[0].columns, row)
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
getLogCount(options = {}) {
|
|
1533
|
+
this.initializeSync();
|
|
1534
|
+
const conditions = [];
|
|
1535
|
+
const params = [];
|
|
1536
|
+
if (options.level) {
|
|
1537
|
+
conditions.push("level = ?");
|
|
1538
|
+
params.push(options.level);
|
|
1539
|
+
}
|
|
1540
|
+
if (options.symbol) {
|
|
1541
|
+
conditions.push("symbol LIKE ?");
|
|
1542
|
+
params.push(`%${options.symbol}%`);
|
|
1543
|
+
}
|
|
1544
|
+
if (options.service) {
|
|
1545
|
+
conditions.push("service = ?");
|
|
1546
|
+
params.push(options.service);
|
|
1547
|
+
}
|
|
1548
|
+
if (options.since) {
|
|
1549
|
+
conditions.push("timestamp >= ?");
|
|
1550
|
+
params.push(options.since);
|
|
1551
|
+
}
|
|
1552
|
+
if (options.until) {
|
|
1553
|
+
conditions.push("timestamp <= ?");
|
|
1554
|
+
params.push(options.until);
|
|
1555
|
+
}
|
|
1556
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1557
|
+
const result = this.db.exec(
|
|
1558
|
+
`SELECT COUNT(*) as count FROM logs ${whereClause}`,
|
|
1559
|
+
params
|
|
1560
|
+
);
|
|
1561
|
+
if (result.length === 0 || result[0].values.length === 0) return 0;
|
|
1562
|
+
return result[0].values[0][0];
|
|
1563
|
+
}
|
|
1564
|
+
pruneLogs(maxCount) {
|
|
1565
|
+
this.initializeSync();
|
|
1566
|
+
if (maxCount <= 0) return 0;
|
|
1567
|
+
const currentCount = this.getLogCount();
|
|
1568
|
+
if (currentCount <= maxCount) return 0;
|
|
1569
|
+
const deleteCount = currentCount - maxCount;
|
|
1570
|
+
this.db.run(
|
|
1571
|
+
`DELETE FROM logs WHERE id IN (
|
|
1572
|
+
SELECT id FROM logs ORDER BY timestamp ASC LIMIT ?
|
|
1573
|
+
)`,
|
|
1574
|
+
[deleteCount]
|
|
1575
|
+
);
|
|
1576
|
+
this.save();
|
|
1577
|
+
return deleteCount;
|
|
1578
|
+
}
|
|
1579
|
+
rowToLogEntry(columns, row) {
|
|
1580
|
+
const obj = {};
|
|
1581
|
+
columns.forEach((col, i) => {
|
|
1582
|
+
obj[col] = row[i];
|
|
1583
|
+
});
|
|
1584
|
+
return {
|
|
1585
|
+
id: obj.id,
|
|
1586
|
+
timestamp: obj.timestamp,
|
|
1587
|
+
level: obj.level,
|
|
1588
|
+
symbol: obj.symbol,
|
|
1589
|
+
symbolType: obj.symbol_type || "raw",
|
|
1590
|
+
message: obj.message,
|
|
1591
|
+
data: obj.data_json ? JSON.parse(obj.data_json) : void 0,
|
|
1592
|
+
service: obj.service,
|
|
1593
|
+
sessionId: obj.session_id || void 0,
|
|
1594
|
+
correlationId: obj.correlation_id || void 0,
|
|
1595
|
+
durationMs: obj.duration_ms || void 0,
|
|
1596
|
+
environment: obj.environment || void 0
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
// ─── Service Registry ──────────────────────────────────────────
|
|
1600
|
+
registerService(reg) {
|
|
1601
|
+
this.initializeSync();
|
|
1602
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1603
|
+
this.db.run(
|
|
1604
|
+
`INSERT INTO services (name, version, pid, started_at, last_seen_at, environment, metadata_json)
|
|
1605
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1606
|
+
ON CONFLICT(name) DO UPDATE SET
|
|
1607
|
+
version = excluded.version,
|
|
1608
|
+
pid = excluded.pid,
|
|
1609
|
+
last_seen_at = excluded.last_seen_at,
|
|
1610
|
+
environment = excluded.environment,
|
|
1611
|
+
metadata_json = excluded.metadata_json`,
|
|
1612
|
+
[
|
|
1613
|
+
reg.name,
|
|
1614
|
+
reg.version || null,
|
|
1615
|
+
reg.pid ?? null,
|
|
1616
|
+
now,
|
|
1617
|
+
now,
|
|
1618
|
+
reg.environment || null,
|
|
1619
|
+
reg.metadata ? JSON.stringify(reg.metadata) : null
|
|
1620
|
+
]
|
|
1621
|
+
);
|
|
1622
|
+
this.save();
|
|
1623
|
+
}
|
|
1624
|
+
updateServiceLastSeen(name) {
|
|
1625
|
+
this.initializeSync();
|
|
1626
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1627
|
+
this.db.run(
|
|
1628
|
+
"UPDATE services SET last_seen_at = ? WHERE name = ?",
|
|
1629
|
+
[now, name]
|
|
1630
|
+
);
|
|
1631
|
+
this.save();
|
|
1632
|
+
}
|
|
1633
|
+
getServices() {
|
|
1634
|
+
this.initializeSync();
|
|
1635
|
+
const result = this.db.exec(
|
|
1636
|
+
"SELECT * FROM services ORDER BY last_seen_at DESC"
|
|
1637
|
+
);
|
|
1638
|
+
if (result.length === 0) return [];
|
|
1639
|
+
return result[0].values.map((row) => {
|
|
1640
|
+
const obj = {};
|
|
1641
|
+
result[0].columns.forEach((col, i) => {
|
|
1642
|
+
obj[col] = row[i];
|
|
1643
|
+
});
|
|
1644
|
+
return {
|
|
1645
|
+
name: obj.name,
|
|
1646
|
+
version: obj.version || void 0,
|
|
1647
|
+
pid: obj.pid || void 0,
|
|
1648
|
+
startedAt: obj.started_at,
|
|
1649
|
+
lastSeenAt: obj.last_seen_at,
|
|
1650
|
+
environment: obj.environment || void 0,
|
|
1651
|
+
metadata: obj.metadata_json ? JSON.parse(obj.metadata_json) : void 0
|
|
1652
|
+
};
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
// ─── App State ──────────────────────────────────────────────────
|
|
1656
|
+
upsertAppState(state) {
|
|
1657
|
+
this.initializeSync();
|
|
1658
|
+
this.db.run(
|
|
1659
|
+
`INSERT INTO app_state (service, session_id, timestamp, state_json, active_flows_json, active_gates_json)
|
|
1660
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
1661
|
+
ON CONFLICT(service, session_id) DO UPDATE SET
|
|
1662
|
+
timestamp = excluded.timestamp,
|
|
1663
|
+
state_json = excluded.state_json,
|
|
1664
|
+
active_flows_json = excluded.active_flows_json,
|
|
1665
|
+
active_gates_json = excluded.active_gates_json`,
|
|
1666
|
+
[
|
|
1667
|
+
state.service,
|
|
1668
|
+
state.sessionId,
|
|
1669
|
+
state.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1670
|
+
JSON.stringify(state.state),
|
|
1671
|
+
state.activeFlows ? JSON.stringify(state.activeFlows) : null,
|
|
1672
|
+
state.activeGates ? JSON.stringify(state.activeGates) : null
|
|
1673
|
+
]
|
|
1674
|
+
);
|
|
1675
|
+
this.save();
|
|
1676
|
+
}
|
|
1677
|
+
getAppState(service, sessionId) {
|
|
1678
|
+
this.initializeSync();
|
|
1679
|
+
let query = "SELECT * FROM app_state WHERE service = ?";
|
|
1680
|
+
const params = [service];
|
|
1681
|
+
if (sessionId) {
|
|
1682
|
+
query += " AND session_id = ?";
|
|
1683
|
+
params.push(sessionId);
|
|
1684
|
+
}
|
|
1685
|
+
query += " ORDER BY timestamp DESC";
|
|
1686
|
+
const result = this.db.exec(query, params);
|
|
1687
|
+
if (result.length === 0) return [];
|
|
1688
|
+
return result[0].values.map((row) => this.rowToAppState(result[0].columns, row));
|
|
1689
|
+
}
|
|
1690
|
+
getAllAppStates() {
|
|
1691
|
+
this.initializeSync();
|
|
1692
|
+
const result = this.db.exec(
|
|
1693
|
+
"SELECT * FROM app_state ORDER BY timestamp DESC"
|
|
1694
|
+
);
|
|
1695
|
+
if (result.length === 0) return [];
|
|
1696
|
+
return result[0].values.map((row) => this.rowToAppState(result[0].columns, row));
|
|
1697
|
+
}
|
|
1698
|
+
rowToAppState(columns, row) {
|
|
1699
|
+
const obj = {};
|
|
1700
|
+
columns.forEach((col, i) => {
|
|
1701
|
+
obj[col] = row[i];
|
|
1702
|
+
});
|
|
1703
|
+
return {
|
|
1704
|
+
service: obj.service,
|
|
1705
|
+
sessionId: obj.session_id,
|
|
1706
|
+
timestamp: obj.timestamp,
|
|
1707
|
+
state: JSON.parse(obj.state_json),
|
|
1708
|
+
activeFlows: obj.active_flows_json ? JSON.parse(obj.active_flows_json) : void 0,
|
|
1709
|
+
activeGates: obj.active_gates_json ? JSON.parse(obj.active_gates_json) : void 0
|
|
1710
|
+
};
|
|
1711
|
+
}
|
|
1712
|
+
// ─── Metrics ───────────────────────────────────────────────────
|
|
1713
|
+
insertMetric(input) {
|
|
1714
|
+
this.initializeSync();
|
|
1715
|
+
const id = uuidv4();
|
|
1716
|
+
const timestamp = input.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1717
|
+
this.db.run(
|
|
1718
|
+
`INSERT INTO metrics (
|
|
1719
|
+
id, timestamp, name, type, value, tags_json, service, environment
|
|
1720
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1721
|
+
[
|
|
1722
|
+
id,
|
|
1723
|
+
timestamp,
|
|
1724
|
+
input.name,
|
|
1725
|
+
input.type,
|
|
1726
|
+
input.value,
|
|
1727
|
+
JSON.stringify(input.tags || {}),
|
|
1728
|
+
input.service,
|
|
1729
|
+
input.environment || null
|
|
1730
|
+
]
|
|
1731
|
+
);
|
|
1732
|
+
this.save();
|
|
1733
|
+
return id;
|
|
1734
|
+
}
|
|
1735
|
+
insertMetricBatch(entries) {
|
|
1736
|
+
this.initializeSync();
|
|
1737
|
+
let accepted = 0;
|
|
1738
|
+
const errors = [];
|
|
1739
|
+
for (const input of entries) {
|
|
1740
|
+
try {
|
|
1741
|
+
const id = uuidv4();
|
|
1742
|
+
const timestamp = input.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1743
|
+
this.db.run(
|
|
1744
|
+
`INSERT INTO metrics (
|
|
1745
|
+
id, timestamp, name, type, value, tags_json, service, environment
|
|
1746
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1747
|
+
[
|
|
1748
|
+
id,
|
|
1749
|
+
timestamp,
|
|
1750
|
+
input.name,
|
|
1751
|
+
input.type,
|
|
1752
|
+
input.value,
|
|
1753
|
+
JSON.stringify(input.tags || {}),
|
|
1754
|
+
input.service,
|
|
1755
|
+
input.environment || null
|
|
1756
|
+
]
|
|
1757
|
+
);
|
|
1758
|
+
accepted++;
|
|
1759
|
+
} catch (err) {
|
|
1760
|
+
errors.push(err instanceof Error ? err.message : String(err));
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
this.save();
|
|
1764
|
+
return { accepted, errors };
|
|
1765
|
+
}
|
|
1766
|
+
queryMetrics(options = {}) {
|
|
1767
|
+
this.initializeSync();
|
|
1768
|
+
const { limit = 100, offset = 0 } = options;
|
|
1769
|
+
const conditions = [];
|
|
1770
|
+
const params = [];
|
|
1771
|
+
if (options.name) {
|
|
1772
|
+
conditions.push("name = ?");
|
|
1773
|
+
params.push(options.name);
|
|
1774
|
+
}
|
|
1775
|
+
if (options.type) {
|
|
1776
|
+
conditions.push("type = ?");
|
|
1777
|
+
params.push(options.type);
|
|
1778
|
+
}
|
|
1779
|
+
if (options.service) {
|
|
1780
|
+
conditions.push("service = ?");
|
|
1781
|
+
params.push(options.service);
|
|
1782
|
+
}
|
|
1783
|
+
if (options.tag) {
|
|
1784
|
+
const eqIdx = options.tag.indexOf("=");
|
|
1785
|
+
if (eqIdx > 0) {
|
|
1786
|
+
const tagKey = options.tag.substring(0, eqIdx);
|
|
1787
|
+
const tagValue = options.tag.substring(eqIdx + 1);
|
|
1788
|
+
conditions.push("tags_json LIKE ?");
|
|
1789
|
+
params.push(`%"${tagKey}":"${tagValue}"%`);
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
if (options.since) {
|
|
1793
|
+
conditions.push("timestamp >= ?");
|
|
1794
|
+
params.push(options.since);
|
|
1795
|
+
}
|
|
1796
|
+
if (options.until) {
|
|
1797
|
+
conditions.push("timestamp <= ?");
|
|
1798
|
+
params.push(options.until);
|
|
1799
|
+
}
|
|
1800
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1801
|
+
const result = this.db.exec(
|
|
1802
|
+
`SELECT * FROM metrics ${whereClause} ORDER BY timestamp DESC LIMIT ? OFFSET ?`,
|
|
1803
|
+
[...params, limit, offset]
|
|
1804
|
+
);
|
|
1805
|
+
if (result.length === 0) return [];
|
|
1806
|
+
return result[0].values.map(
|
|
1807
|
+
(row) => this.rowToMetricEntry(result[0].columns, row)
|
|
1808
|
+
);
|
|
1809
|
+
}
|
|
1810
|
+
getMetricCount(options = {}) {
|
|
1811
|
+
this.initializeSync();
|
|
1812
|
+
const conditions = [];
|
|
1813
|
+
const params = [];
|
|
1814
|
+
if (options.name) {
|
|
1815
|
+
conditions.push("name = ?");
|
|
1816
|
+
params.push(options.name);
|
|
1817
|
+
}
|
|
1818
|
+
if (options.type) {
|
|
1819
|
+
conditions.push("type = ?");
|
|
1820
|
+
params.push(options.type);
|
|
1821
|
+
}
|
|
1822
|
+
if (options.service) {
|
|
1823
|
+
conditions.push("service = ?");
|
|
1824
|
+
params.push(options.service);
|
|
1825
|
+
}
|
|
1826
|
+
if (options.tag) {
|
|
1827
|
+
const eqIdx = options.tag.indexOf("=");
|
|
1828
|
+
if (eqIdx > 0) {
|
|
1829
|
+
const tagKey = options.tag.substring(0, eqIdx);
|
|
1830
|
+
const tagValue = options.tag.substring(eqIdx + 1);
|
|
1831
|
+
conditions.push("tags_json LIKE ?");
|
|
1832
|
+
params.push(`%"${tagKey}":"${tagValue}"%`);
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
if (options.since) {
|
|
1836
|
+
conditions.push("timestamp >= ?");
|
|
1837
|
+
params.push(options.since);
|
|
1838
|
+
}
|
|
1839
|
+
if (options.until) {
|
|
1840
|
+
conditions.push("timestamp <= ?");
|
|
1841
|
+
params.push(options.until);
|
|
1842
|
+
}
|
|
1843
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1844
|
+
const result = this.db.exec(
|
|
1845
|
+
`SELECT COUNT(*) as count FROM metrics ${whereClause}`,
|
|
1846
|
+
params
|
|
1847
|
+
);
|
|
1848
|
+
if (result.length === 0 || result[0].values.length === 0) return 0;
|
|
1849
|
+
return result[0].values[0][0];
|
|
1850
|
+
}
|
|
1851
|
+
aggregateMetric(name, options) {
|
|
1852
|
+
this.initializeSync();
|
|
1853
|
+
const conditions = ["name = ?"];
|
|
1854
|
+
const params = [name];
|
|
1855
|
+
if (options?.service) {
|
|
1856
|
+
conditions.push("service = ?");
|
|
1857
|
+
params.push(options.service);
|
|
1858
|
+
}
|
|
1859
|
+
if (options?.since) {
|
|
1860
|
+
conditions.push("timestamp >= ?");
|
|
1861
|
+
params.push(options.since);
|
|
1862
|
+
}
|
|
1863
|
+
if (options?.until) {
|
|
1864
|
+
conditions.push("timestamp <= ?");
|
|
1865
|
+
params.push(options.until);
|
|
1866
|
+
}
|
|
1867
|
+
const whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
1868
|
+
const result = this.db.exec(
|
|
1869
|
+
`SELECT COUNT(*) as count, SUM(value) as sum, MIN(value) as min, MAX(value) as max, AVG(value) as avg
|
|
1870
|
+
FROM metrics ${whereClause}`,
|
|
1871
|
+
params
|
|
1872
|
+
);
|
|
1873
|
+
if (result.length === 0 || result[0].values.length === 0) {
|
|
1874
|
+
return { name, count: 0, sum: 0, min: 0, max: 0, avg: 0 };
|
|
1875
|
+
}
|
|
1876
|
+
const row = result[0].values[0];
|
|
1877
|
+
return {
|
|
1878
|
+
name,
|
|
1879
|
+
count: row[0] || 0,
|
|
1880
|
+
sum: row[1] || 0,
|
|
1881
|
+
min: row[2] || 0,
|
|
1882
|
+
max: row[3] || 0,
|
|
1883
|
+
avg: row[4] || 0
|
|
1884
|
+
};
|
|
1885
|
+
}
|
|
1886
|
+
pruneMetrics(maxCount) {
|
|
1887
|
+
this.initializeSync();
|
|
1888
|
+
if (maxCount <= 0) return 0;
|
|
1889
|
+
const currentCount = this.getMetricCount();
|
|
1890
|
+
if (currentCount <= maxCount) return 0;
|
|
1891
|
+
const deleteCount = currentCount - maxCount;
|
|
1892
|
+
this.db.run(
|
|
1893
|
+
`DELETE FROM metrics WHERE id IN (
|
|
1894
|
+
SELECT id FROM metrics ORDER BY timestamp ASC LIMIT ?
|
|
1895
|
+
)`,
|
|
1896
|
+
[deleteCount]
|
|
1897
|
+
);
|
|
1898
|
+
this.save();
|
|
1899
|
+
return deleteCount;
|
|
1900
|
+
}
|
|
1901
|
+
rowToMetricEntry(columns, row) {
|
|
1902
|
+
const obj = {};
|
|
1903
|
+
columns.forEach((col, i) => {
|
|
1904
|
+
obj[col] = row[i];
|
|
1905
|
+
});
|
|
1906
|
+
return {
|
|
1907
|
+
id: obj.id,
|
|
1908
|
+
timestamp: obj.timestamp,
|
|
1909
|
+
name: obj.name,
|
|
1910
|
+
type: obj.type,
|
|
1911
|
+
value: obj.value,
|
|
1912
|
+
tags: obj.tags_json ? JSON.parse(obj.tags_json) : {},
|
|
1913
|
+
service: obj.service,
|
|
1914
|
+
environment: obj.environment || void 0
|
|
1915
|
+
};
|
|
1916
|
+
}
|
|
1917
|
+
// ─── Traces ───────────────────────────────────────────────────
|
|
1918
|
+
insertSpan(input) {
|
|
1919
|
+
this.initializeSync();
|
|
1920
|
+
const spanId = input.spanId || uuidv4();
|
|
1921
|
+
const startTime = input.startTime || (/* @__PURE__ */ new Date()).toISOString();
|
|
1922
|
+
this.db.run(
|
|
1923
|
+
`INSERT INTO traces (
|
|
1924
|
+
trace_id, span_id, parent_span_id, service, symbol, operation,
|
|
1925
|
+
start_time, end_time, duration_ms, status, tags_json, log_ids_json
|
|
1926
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1927
|
+
[
|
|
1928
|
+
input.traceId,
|
|
1929
|
+
spanId,
|
|
1930
|
+
input.parentSpanId || null,
|
|
1931
|
+
input.service,
|
|
1932
|
+
input.symbol,
|
|
1933
|
+
input.operation,
|
|
1934
|
+
startTime,
|
|
1935
|
+
input.endTime || null,
|
|
1936
|
+
input.durationMs ?? null,
|
|
1937
|
+
input.status || "ok",
|
|
1938
|
+
JSON.stringify(input.tags || {}),
|
|
1939
|
+
JSON.stringify(input.logIds || [])
|
|
1940
|
+
]
|
|
1941
|
+
);
|
|
1942
|
+
this.save();
|
|
1943
|
+
return spanId;
|
|
1944
|
+
}
|
|
1945
|
+
getTrace(traceId) {
|
|
1946
|
+
this.initializeSync();
|
|
1947
|
+
const result = this.db.exec(
|
|
1948
|
+
"SELECT * FROM traces WHERE trace_id = ? ORDER BY start_time ASC",
|
|
1949
|
+
[traceId]
|
|
1950
|
+
);
|
|
1951
|
+
if (result.length === 0 || result[0].values.length === 0) return null;
|
|
1952
|
+
const spans = result[0].values.map(
|
|
1953
|
+
(row) => this.rowToTraceSpan(result[0].columns, row)
|
|
1954
|
+
);
|
|
1955
|
+
const services = [...new Set(spans.map((s) => s.service))];
|
|
1956
|
+
const startTimes = spans.map((s) => s.startTime);
|
|
1957
|
+
const endTimes = spans.filter((s) => s.endTime).map((s) => s.endTime);
|
|
1958
|
+
const startTime = startTimes.sort()[0];
|
|
1959
|
+
const endTime = endTimes.length > 0 ? endTimes.sort().reverse()[0] : startTime;
|
|
1960
|
+
const startMs = new Date(startTime).getTime();
|
|
1961
|
+
const endMs = new Date(endTime).getTime();
|
|
1962
|
+
const totalDurationMs = endMs - startMs;
|
|
1963
|
+
return {
|
|
1964
|
+
traceId,
|
|
1965
|
+
spans,
|
|
1966
|
+
services,
|
|
1967
|
+
totalDurationMs: totalDurationMs > 0 ? totalDurationMs : 0,
|
|
1968
|
+
startTime,
|
|
1969
|
+
endTime
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1972
|
+
queryTraces(options = {}) {
|
|
1973
|
+
this.initializeSync();
|
|
1974
|
+
const conditions = [];
|
|
1975
|
+
const params = [];
|
|
1976
|
+
if (options.service) {
|
|
1977
|
+
conditions.push("service = ?");
|
|
1978
|
+
params.push(options.service);
|
|
1979
|
+
}
|
|
1980
|
+
if (options.symbol) {
|
|
1981
|
+
conditions.push("symbol = ?");
|
|
1982
|
+
params.push(options.symbol);
|
|
1983
|
+
}
|
|
1984
|
+
if (options.since) {
|
|
1985
|
+
conditions.push("start_time >= ?");
|
|
1986
|
+
params.push(options.since);
|
|
1987
|
+
}
|
|
1988
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1989
|
+
const traceLimit = Math.min(options.limit || 20, 20);
|
|
1990
|
+
const result = this.db.exec(
|
|
1991
|
+
`SELECT DISTINCT trace_id FROM traces ${whereClause} ORDER BY start_time DESC LIMIT ?`,
|
|
1992
|
+
[...params, traceLimit]
|
|
1993
|
+
);
|
|
1994
|
+
if (result.length === 0) return [];
|
|
1995
|
+
const traces = [];
|
|
1996
|
+
for (const row of result[0].values) {
|
|
1997
|
+
const traceId = row[0];
|
|
1998
|
+
const trace = this.getTrace(traceId);
|
|
1999
|
+
if (trace) {
|
|
2000
|
+
traces.push(trace);
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
return traces;
|
|
2004
|
+
}
|
|
2005
|
+
rowToTraceSpan(columns, row) {
|
|
2006
|
+
const obj = {};
|
|
2007
|
+
columns.forEach((col, i) => {
|
|
2008
|
+
obj[col] = row[i];
|
|
2009
|
+
});
|
|
2010
|
+
return {
|
|
2011
|
+
traceId: obj.trace_id,
|
|
2012
|
+
spanId: obj.span_id,
|
|
2013
|
+
parentSpanId: obj.parent_span_id || void 0,
|
|
2014
|
+
service: obj.service,
|
|
2015
|
+
symbol: obj.symbol,
|
|
2016
|
+
operation: obj.operation,
|
|
2017
|
+
startTime: obj.start_time,
|
|
2018
|
+
endTime: obj.end_time || void 0,
|
|
2019
|
+
durationMs: obj.duration_ms || void 0,
|
|
2020
|
+
status: obj.status || "ok",
|
|
2021
|
+
tags: obj.tags_json ? JSON.parse(obj.tags_json) : {},
|
|
2022
|
+
logs: obj.log_ids_json ? JSON.parse(obj.log_ids_json) : []
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
1238
2025
|
close() {
|
|
1239
2026
|
if (this.db) {
|
|
1240
2027
|
this.save();
|
|
@@ -1244,6 +2031,211 @@ var SentinelStorage = class {
|
|
|
1244
2031
|
}
|
|
1245
2032
|
};
|
|
1246
2033
|
|
|
2034
|
+
// src/types.ts
|
|
2035
|
+
var DEFAULT_AUTH_CONFIG = {
|
|
2036
|
+
enabled: false,
|
|
2037
|
+
tokens: []
|
|
2038
|
+
};
|
|
2039
|
+
var DEFAULT_RATE_LIMIT_CONFIG = {
|
|
2040
|
+
enabled: false,
|
|
2041
|
+
global: {
|
|
2042
|
+
maxRequestsPerMinute: 600,
|
|
2043
|
+
maxEntriesPerBatch: 500,
|
|
2044
|
+
samplingRate: 1
|
|
2045
|
+
},
|
|
2046
|
+
perService: {}
|
|
2047
|
+
};
|
|
2048
|
+
var DEFAULT_SERVER_CONFIG = {
|
|
2049
|
+
port: 3838,
|
|
2050
|
+
maxLogs: 1e4,
|
|
2051
|
+
maxBatchSize: 500,
|
|
2052
|
+
wsMaxSubscribers: 256,
|
|
2053
|
+
pruneIntervalInserts: 100,
|
|
2054
|
+
logRetentionDays: 0,
|
|
2055
|
+
auth: DEFAULT_AUTH_CONFIG,
|
|
2056
|
+
rateLimit: DEFAULT_RATE_LIMIT_CONFIG
|
|
2057
|
+
};
|
|
2058
|
+
|
|
2059
|
+
// src/config.ts
|
|
2060
|
+
import * as fs2 from "fs";
|
|
2061
|
+
import * as path2 from "path";
|
|
2062
|
+
var CONFIG_FILES = [".sentinel.yaml", ".sentinel.yml"];
|
|
2063
|
+
function loadConfig(projectDir) {
|
|
2064
|
+
for (const filename of CONFIG_FILES) {
|
|
2065
|
+
const filePath = path2.join(projectDir, filename);
|
|
2066
|
+
if (fs2.existsSync(filePath)) {
|
|
2067
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
2068
|
+
return parseSimpleYaml(content);
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
return null;
|
|
2072
|
+
}
|
|
2073
|
+
function writeConfig(projectDir, config) {
|
|
2074
|
+
const filePath = path2.join(projectDir, ".sentinel.yaml");
|
|
2075
|
+
const content = serializeSimpleYaml(config);
|
|
2076
|
+
fs2.writeFileSync(filePath, content, "utf-8");
|
|
2077
|
+
}
|
|
2078
|
+
function parseSimpleYaml(content) {
|
|
2079
|
+
const config = { version: "1.0", project: "" };
|
|
2080
|
+
const lines = content.split("\n");
|
|
2081
|
+
let currentSection = null;
|
|
2082
|
+
let currentSubSection = null;
|
|
2083
|
+
for (const line of lines) {
|
|
2084
|
+
const trimmed = line.trimEnd();
|
|
2085
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
2086
|
+
const topMatch = trimmed.match(/^(\w+):\s*(.+)$/);
|
|
2087
|
+
if (topMatch) {
|
|
2088
|
+
const [, key, value] = topMatch;
|
|
2089
|
+
if (key === "version") config.version = value.replace(/['"]/g, "");
|
|
2090
|
+
else if (key === "project") config.project = value.replace(/['"]/g, "");
|
|
2091
|
+
else if (key === "environment") config.environment = value.replace(/['"]/g, "");
|
|
2092
|
+
currentSection = null;
|
|
2093
|
+
currentSubSection = null;
|
|
2094
|
+
continue;
|
|
2095
|
+
}
|
|
2096
|
+
const sectionMatch = trimmed.match(/^(\w+):$/);
|
|
2097
|
+
if (sectionMatch) {
|
|
2098
|
+
currentSection = sectionMatch[1];
|
|
2099
|
+
currentSubSection = null;
|
|
2100
|
+
if (currentSection === "symbols" && !config.symbols) {
|
|
2101
|
+
config.symbols = {};
|
|
2102
|
+
}
|
|
2103
|
+
if (currentSection === "routes" && !config.routes) {
|
|
2104
|
+
config.routes = {};
|
|
2105
|
+
}
|
|
2106
|
+
if (currentSection === "scrub" && !config.scrub) {
|
|
2107
|
+
config.scrub = {};
|
|
2108
|
+
}
|
|
2109
|
+
if (currentSection === "server" && !config.server) {
|
|
2110
|
+
config.server = {};
|
|
2111
|
+
}
|
|
2112
|
+
continue;
|
|
2113
|
+
}
|
|
2114
|
+
const subMatch = trimmed.match(/^\s{2}(\w+):$/);
|
|
2115
|
+
if (subMatch && currentSection) {
|
|
2116
|
+
currentSubSection = subMatch[1];
|
|
2117
|
+
if (currentSection === "symbols" && config.symbols) {
|
|
2118
|
+
config.symbols[currentSubSection] = [];
|
|
2119
|
+
}
|
|
2120
|
+
if (currentSection === "scrub" && config.scrub) {
|
|
2121
|
+
config.scrub[currentSubSection] = [];
|
|
2122
|
+
}
|
|
2123
|
+
continue;
|
|
2124
|
+
}
|
|
2125
|
+
const listMatch = trimmed.match(/^\s+-\s+(.+)$/);
|
|
2126
|
+
if (listMatch && currentSection && currentSubSection) {
|
|
2127
|
+
const value = listMatch[1].replace(/['"]/g, "");
|
|
2128
|
+
if (currentSection === "symbols" && config.symbols) {
|
|
2129
|
+
const arr = config.symbols[currentSubSection];
|
|
2130
|
+
if (Array.isArray(arr)) arr.push(value);
|
|
2131
|
+
}
|
|
2132
|
+
if (currentSection === "scrub" && config.scrub) {
|
|
2133
|
+
const arr = config.scrub[currentSubSection];
|
|
2134
|
+
if (Array.isArray(arr)) arr.push(value);
|
|
2135
|
+
}
|
|
2136
|
+
continue;
|
|
2137
|
+
}
|
|
2138
|
+
const routeMatch = trimmed.match(/^\s+(['"]?\/[^'"]+['"]?):\s+['"]?([^'"]+)['"]?$/);
|
|
2139
|
+
if (routeMatch && currentSection === "routes" && config.routes) {
|
|
2140
|
+
const route = routeMatch[1].replace(/['"]/g, "");
|
|
2141
|
+
config.routes[route] = routeMatch[2];
|
|
2142
|
+
continue;
|
|
2143
|
+
}
|
|
2144
|
+
const serverKvMatch = trimmed.match(/^\s+(\w+):\s+(\d+)$/);
|
|
2145
|
+
if (serverKvMatch && currentSection === "server" && config.server) {
|
|
2146
|
+
const key = serverKvMatch[1];
|
|
2147
|
+
const value = parseInt(serverKvMatch[2], 10);
|
|
2148
|
+
if (key in { port: 1, maxLogs: 1, maxBatchSize: 1, wsMaxSubscribers: 1, pruneIntervalInserts: 1, logRetentionDays: 1 }) {
|
|
2149
|
+
config.server[key] = value;
|
|
2150
|
+
}
|
|
2151
|
+
continue;
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
return config;
|
|
2155
|
+
}
|
|
2156
|
+
function serializeSimpleYaml(config) {
|
|
2157
|
+
const lines = [];
|
|
2158
|
+
lines.push(`# Sentinel Configuration`);
|
|
2159
|
+
lines.push(`# Auto-generated \u2014 edit freely`);
|
|
2160
|
+
lines.push("");
|
|
2161
|
+
lines.push(`version: "${config.version}"`);
|
|
2162
|
+
lines.push(`project: "${config.project}"`);
|
|
2163
|
+
if (config.environment) {
|
|
2164
|
+
lines.push(`environment: "${config.environment}"`);
|
|
2165
|
+
}
|
|
2166
|
+
if (config.symbols) {
|
|
2167
|
+
lines.push("");
|
|
2168
|
+
lines.push("symbols:");
|
|
2169
|
+
for (const [key, values] of Object.entries(config.symbols)) {
|
|
2170
|
+
if (values && values.length > 0) {
|
|
2171
|
+
lines.push(` ${key}:`);
|
|
2172
|
+
for (const v of values) {
|
|
2173
|
+
lines.push(` - ${v}`);
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
if (config.routes && Object.keys(config.routes).length > 0) {
|
|
2179
|
+
lines.push("");
|
|
2180
|
+
lines.push("routes:");
|
|
2181
|
+
for (const [route, symbol] of Object.entries(config.routes)) {
|
|
2182
|
+
lines.push(` "${route}": ${symbol}`);
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
if (config.scrub) {
|
|
2186
|
+
lines.push("");
|
|
2187
|
+
lines.push("scrub:");
|
|
2188
|
+
if (config.scrub.headers?.length) {
|
|
2189
|
+
lines.push(" headers:");
|
|
2190
|
+
for (const h of config.scrub.headers) {
|
|
2191
|
+
lines.push(` - ${h}`);
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
if (config.scrub.fields?.length) {
|
|
2195
|
+
lines.push(" fields:");
|
|
2196
|
+
for (const f of config.scrub.fields) {
|
|
2197
|
+
lines.push(` - ${f}`);
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
if (config.server && Object.keys(config.server).length > 0) {
|
|
2202
|
+
lines.push("");
|
|
2203
|
+
lines.push("server:");
|
|
2204
|
+
for (const [key, value] of Object.entries(config.server)) {
|
|
2205
|
+
if (value !== void 0) {
|
|
2206
|
+
lines.push(` ${key}: ${value}`);
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
lines.push("");
|
|
2211
|
+
return lines.join("\n");
|
|
2212
|
+
}
|
|
2213
|
+
function loadServerConfig(projectDir) {
|
|
2214
|
+
const config = { ...DEFAULT_SERVER_CONFIG };
|
|
2215
|
+
const yamlConfig = projectDir ? loadConfig(projectDir) : null;
|
|
2216
|
+
const globalDir = path2.join(process.env.HOME || "~", ".paradigm");
|
|
2217
|
+
const globalConfig = loadConfig(globalDir);
|
|
2218
|
+
for (const src of [globalConfig, yamlConfig]) {
|
|
2219
|
+
if (src?.server) {
|
|
2220
|
+
if (src.server.port !== void 0) config.port = src.server.port;
|
|
2221
|
+
if (src.server.maxLogs !== void 0) config.maxLogs = src.server.maxLogs;
|
|
2222
|
+
if (src.server.maxBatchSize !== void 0) config.maxBatchSize = src.server.maxBatchSize;
|
|
2223
|
+
if (src.server.wsMaxSubscribers !== void 0) config.wsMaxSubscribers = src.server.wsMaxSubscribers;
|
|
2224
|
+
if (src.server.pruneIntervalInserts !== void 0) config.pruneIntervalInserts = src.server.pruneIntervalInserts;
|
|
2225
|
+
if (src.server.logRetentionDays !== void 0) config.logRetentionDays = src.server.logRetentionDays;
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
if (process.env.SENTINEL_PORT) config.port = parseInt(process.env.SENTINEL_PORT, 10);
|
|
2229
|
+
if (process.env.SENTINEL_MAX_LOGS) config.maxLogs = parseInt(process.env.SENTINEL_MAX_LOGS, 10);
|
|
2230
|
+
return config;
|
|
2231
|
+
}
|
|
2232
|
+
|
|
1247
2233
|
export {
|
|
1248
|
-
SentinelStorage
|
|
2234
|
+
SentinelStorage,
|
|
2235
|
+
DEFAULT_AUTH_CONFIG,
|
|
2236
|
+
DEFAULT_RATE_LIMIT_CONFIG,
|
|
2237
|
+
DEFAULT_SERVER_CONFIG,
|
|
2238
|
+
loadConfig,
|
|
2239
|
+
writeConfig,
|
|
2240
|
+
loadServerConfig
|
|
1249
2241
|
};
|