@hasna/logs 0.0.1 → 0.1.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/cli/index.js +40 -7
- package/dist/mcp/index.js +245 -67
- package/dist/server/index.js +313 -3
- package/package.json +10 -2
- package/src/cli/index.ts +44 -0
- package/src/db/index.ts +10 -0
- package/src/db/migrations/001_alert_rules.ts +21 -0
- package/src/db/migrations/002_issues.ts +21 -0
- package/src/db/migrations/003_retention.ts +15 -0
- package/src/db/migrations/004_page_auth.ts +13 -0
- package/src/lib/alerts.test.ts +67 -0
- package/src/lib/alerts.ts +117 -0
- package/src/lib/compare.test.ts +52 -0
- package/src/lib/compare.ts +85 -0
- package/src/lib/diagnose.test.ts +55 -0
- package/src/lib/diagnose.ts +76 -0
- package/src/lib/export.test.ts +66 -0
- package/src/lib/export.ts +65 -0
- package/src/lib/health.test.ts +48 -0
- package/src/lib/health.ts +51 -0
- package/src/lib/ingest.ts +25 -2
- package/src/lib/issues.test.ts +79 -0
- package/src/lib/issues.ts +70 -0
- package/src/lib/page-auth.test.ts +54 -0
- package/src/lib/page-auth.ts +48 -0
- package/src/lib/retention.test.ts +42 -0
- package/src/lib/retention.ts +62 -0
- package/src/lib/scanner.ts +21 -2
- package/src/lib/scheduler.ts +6 -0
- package/src/mcp/index.ts +124 -90
- package/src/server/index.ts +8 -0
- package/src/server/routes/alerts.ts +32 -0
- package/src/server/routes/issues.ts +43 -0
- package/src/server/routes/logs.ts +21 -0
- package/src/server/routes/projects.ts +25 -0
- package/src/server/routes/stream.ts +43 -0
package/dist/cli/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
import {
|
|
4
4
|
runJob
|
|
5
|
-
} from "../index-
|
|
5
|
+
} from "../index-4x090f69.js";
|
|
6
6
|
import {
|
|
7
7
|
createPage,
|
|
8
8
|
createProject,
|
|
@@ -13,14 +13,16 @@ import {
|
|
|
13
13
|
searchLogs,
|
|
14
14
|
summarizeLogs,
|
|
15
15
|
tailLogs
|
|
16
|
-
} from "../index-
|
|
16
|
+
} from "../index-qmsvtxax.js";
|
|
17
17
|
import {
|
|
18
|
-
__commonJS,
|
|
19
|
-
__require,
|
|
20
|
-
__toESM,
|
|
21
18
|
createJob,
|
|
22
19
|
listJobs
|
|
23
|
-
} from "../
|
|
20
|
+
} from "../jobs-124e878j.js";
|
|
21
|
+
import {
|
|
22
|
+
__commonJS,
|
|
23
|
+
__require,
|
|
24
|
+
__toESM
|
|
25
|
+
} from "../index-g8dczzvv.js";
|
|
24
26
|
|
|
25
27
|
// node_modules/commander/lib/error.js
|
|
26
28
|
var require_error = __commonJS((exports) => {
|
|
@@ -2231,7 +2233,7 @@ program2.command("scan").description("Run an immediate scan for a job").option("
|
|
|
2231
2233
|
process.exit(1);
|
|
2232
2234
|
}
|
|
2233
2235
|
const db = getDb();
|
|
2234
|
-
const job = (await import("../jobs-
|
|
2236
|
+
const job = (await import("../jobs-124e878j.js")).getJob(db, opts.job);
|
|
2235
2237
|
if (!job) {
|
|
2236
2238
|
console.error("Job not found");
|
|
2237
2239
|
process.exit(1);
|
|
@@ -2240,6 +2242,37 @@ program2.command("scan").description("Run an immediate scan for a job").option("
|
|
|
2240
2242
|
await runJob(db, job.id, job.project_id, job.page_id ?? undefined);
|
|
2241
2243
|
console.log("Scan complete.");
|
|
2242
2244
|
});
|
|
2245
|
+
program2.command("export").description("Export logs to JSON or CSV").option("--project <id>").option("--since <time>", "Relative time or ISO").option("--level <level>").option("--service <name>").option("--format <fmt>", "json or csv", "json").option("--output <file>", "Output file (default: stdout)").option("--limit <n>", "Max rows", "100000").action(async (opts) => {
|
|
2246
|
+
const { exportToCsv, exportToJson } = await import("../export-yjaw2sr3.js");
|
|
2247
|
+
const { createWriteStream } = await import("fs");
|
|
2248
|
+
const db = getDb();
|
|
2249
|
+
const options = {
|
|
2250
|
+
project_id: opts.project,
|
|
2251
|
+
since: parseRelativeTime(opts.since),
|
|
2252
|
+
level: opts.level,
|
|
2253
|
+
service: opts.service,
|
|
2254
|
+
limit: Number(opts.limit)
|
|
2255
|
+
};
|
|
2256
|
+
let count = 0;
|
|
2257
|
+
if (opts.output) {
|
|
2258
|
+
const stream = createWriteStream(opts.output);
|
|
2259
|
+
const write = (s) => stream.write(s);
|
|
2260
|
+
count = opts.format === "csv" ? exportToCsv(db, options, write) : exportToJson(db, options, write);
|
|
2261
|
+
stream.end();
|
|
2262
|
+
console.error(`Exported ${count} log(s) to ${opts.output}`);
|
|
2263
|
+
} else {
|
|
2264
|
+
const write = (s) => process.stdout.write(s);
|
|
2265
|
+
count = opts.format === "csv" ? exportToCsv(db, options, write) : exportToJson(db, options, write);
|
|
2266
|
+
process.stderr.write(`
|
|
2267
|
+
Exported ${count} log(s)
|
|
2268
|
+
`);
|
|
2269
|
+
}
|
|
2270
|
+
});
|
|
2271
|
+
program2.command("health").description("Show server health and DB stats").action(async () => {
|
|
2272
|
+
const { getHealth } = await import("../health-f2qrebqc.js");
|
|
2273
|
+
const h = getHealth(getDb());
|
|
2274
|
+
console.log(JSON.stringify(h, null, 2));
|
|
2275
|
+
});
|
|
2243
2276
|
program2.command("mcp").description("Start the MCP server").option("--claude", "Install into Claude Code").option("--codex", "Install into Codex").option("--gemini", "Install into Gemini").action(async (opts) => {
|
|
2244
2277
|
if (opts.claude || opts.codex || opts.gemini) {
|
|
2245
2278
|
const bin = process.execPath;
|
package/dist/mcp/index.js
CHANGED
|
@@ -1,27 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
3
|
import {
|
|
4
|
+
createAlertRule,
|
|
4
5
|
createPage,
|
|
5
6
|
createProject,
|
|
7
|
+
deleteAlertRule,
|
|
6
8
|
getDb,
|
|
7
9
|
getLatestSnapshot,
|
|
8
10
|
getLogContext,
|
|
9
11
|
getPerfTrend,
|
|
10
12
|
ingestLog,
|
|
13
|
+
listAlertRules,
|
|
14
|
+
listIssues,
|
|
11
15
|
listPages,
|
|
12
16
|
listProjects,
|
|
13
17
|
scoreLabel,
|
|
14
18
|
searchLogs,
|
|
15
19
|
summarizeLogs,
|
|
16
|
-
tailLogs
|
|
17
|
-
|
|
20
|
+
tailLogs,
|
|
21
|
+
updateIssueStatus
|
|
22
|
+
} from "../index-qmsvtxax.js";
|
|
18
23
|
import {
|
|
19
|
-
__commonJS,
|
|
20
|
-
__export,
|
|
21
|
-
__toESM,
|
|
22
24
|
createJob,
|
|
23
25
|
listJobs
|
|
24
|
-
} from "../
|
|
26
|
+
} from "../jobs-124e878j.js";
|
|
27
|
+
import {
|
|
28
|
+
getHealth
|
|
29
|
+
} from "../health-f2qrebqc.js";
|
|
30
|
+
import {
|
|
31
|
+
__commonJS,
|
|
32
|
+
__export,
|
|
33
|
+
__toESM
|
|
34
|
+
} from "../index-g8dczzvv.js";
|
|
25
35
|
|
|
26
36
|
// node_modules/ajv/dist/compile/codegen/code.js
|
|
27
37
|
var require_code = __commonJS((exports) => {
|
|
@@ -6282,7 +6292,7 @@ var require_formats = __commonJS((exports) => {
|
|
|
6282
6292
|
}
|
|
6283
6293
|
var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
|
|
6284
6294
|
function getTime(strictTimeZone) {
|
|
6285
|
-
return function
|
|
6295
|
+
return function time(str) {
|
|
6286
6296
|
const matches = TIME.exec(str);
|
|
6287
6297
|
if (!matches)
|
|
6288
6298
|
return false;
|
|
@@ -28368,25 +28378,155 @@ class StdioServerTransport {
|
|
|
28368
28378
|
}
|
|
28369
28379
|
}
|
|
28370
28380
|
|
|
28381
|
+
// src/lib/diagnose.ts
|
|
28382
|
+
function diagnose(db, projectId, since) {
|
|
28383
|
+
const window = since ?? new Date(Date.now() - 24 * 3600 * 1000).toISOString();
|
|
28384
|
+
const top_errors = db.prepare(`
|
|
28385
|
+
SELECT message, COUNT(*) as count, service, MAX(timestamp) as last_seen
|
|
28386
|
+
FROM logs
|
|
28387
|
+
WHERE project_id = $p AND level IN ('error','fatal') AND timestamp >= $since
|
|
28388
|
+
GROUP BY message, service
|
|
28389
|
+
ORDER BY count DESC
|
|
28390
|
+
LIMIT 10
|
|
28391
|
+
`).all({ $p: projectId, $since: window });
|
|
28392
|
+
const error_rate_by_service = db.prepare(`
|
|
28393
|
+
SELECT service,
|
|
28394
|
+
SUM(CASE WHEN level IN ('error','fatal') THEN 1 ELSE 0 END) as errors,
|
|
28395
|
+
SUM(CASE WHEN level = 'warn' THEN 1 ELSE 0 END) as warns,
|
|
28396
|
+
COUNT(*) as total
|
|
28397
|
+
FROM logs
|
|
28398
|
+
WHERE project_id = $p AND timestamp >= $since
|
|
28399
|
+
GROUP BY service
|
|
28400
|
+
ORDER BY errors DESC
|
|
28401
|
+
`).all({ $p: projectId, $since: window });
|
|
28402
|
+
const failing_pages = db.prepare(`
|
|
28403
|
+
SELECT l.page_id, p.url, COUNT(*) as error_count
|
|
28404
|
+
FROM logs l
|
|
28405
|
+
JOIN pages p ON p.id = l.page_id
|
|
28406
|
+
WHERE l.project_id = $p AND l.level IN ('error','fatal') AND l.timestamp >= $since AND l.page_id IS NOT NULL
|
|
28407
|
+
GROUP BY l.page_id, p.url
|
|
28408
|
+
ORDER BY error_count DESC
|
|
28409
|
+
LIMIT 10
|
|
28410
|
+
`).all({ $p: projectId, $since: window });
|
|
28411
|
+
const perf_regressions = db.prepare(`
|
|
28412
|
+
SELECT * FROM (
|
|
28413
|
+
SELECT
|
|
28414
|
+
cur.page_id,
|
|
28415
|
+
p.url,
|
|
28416
|
+
cur.score as score_now,
|
|
28417
|
+
prev.score as score_prev,
|
|
28418
|
+
(cur.score - prev.score) as delta
|
|
28419
|
+
FROM performance_snapshots cur
|
|
28420
|
+
JOIN pages p ON p.id = cur.page_id
|
|
28421
|
+
LEFT JOIN performance_snapshots prev ON prev.page_id = cur.page_id AND prev.id != cur.id
|
|
28422
|
+
WHERE cur.project_id = $p
|
|
28423
|
+
AND cur.timestamp = (SELECT MAX(timestamp) FROM performance_snapshots WHERE page_id = cur.page_id)
|
|
28424
|
+
AND (prev.timestamp = (SELECT MAX(timestamp) FROM performance_snapshots WHERE page_id = cur.page_id AND id != cur.id) OR prev.id IS NULL)
|
|
28425
|
+
) WHERE delta < -5 OR delta IS NULL
|
|
28426
|
+
ORDER BY delta ASC
|
|
28427
|
+
LIMIT 10
|
|
28428
|
+
`).all({ $p: projectId });
|
|
28429
|
+
const totalErrors = top_errors.reduce((s, e) => s + e.count, 0);
|
|
28430
|
+
const topService = error_rate_by_service[0];
|
|
28431
|
+
const summary = totalErrors === 0 ? "No errors in this window. All looks good." : `${totalErrors} error(s) detected. Worst service: ${topService?.service ?? "unknown"} (${topService?.errors ?? 0} errors). ${failing_pages.length} page(s) with errors. ${perf_regressions.length} perf regression(s).`;
|
|
28432
|
+
return { project_id: projectId, window, top_errors, error_rate_by_service, failing_pages, perf_regressions, summary };
|
|
28433
|
+
}
|
|
28434
|
+
|
|
28435
|
+
// src/lib/compare.ts
|
|
28436
|
+
function getErrorsByMessage(db, projectId, since, until) {
|
|
28437
|
+
return db.prepare(`
|
|
28438
|
+
SELECT message, service, COUNT(*) as count
|
|
28439
|
+
FROM logs
|
|
28440
|
+
WHERE project_id = $p AND level IN ('error','fatal') AND timestamp >= $since AND timestamp <= $until
|
|
28441
|
+
GROUP BY message, service
|
|
28442
|
+
`).all({ $p: projectId, $since: since, $until: until });
|
|
28443
|
+
}
|
|
28444
|
+
function getErrorsByService(db, projectId, since, until) {
|
|
28445
|
+
return db.prepare(`
|
|
28446
|
+
SELECT service, COUNT(*) as errors
|
|
28447
|
+
FROM logs
|
|
28448
|
+
WHERE project_id = $p AND level IN ('error','fatal') AND timestamp >= $since AND timestamp <= $until
|
|
28449
|
+
GROUP BY service
|
|
28450
|
+
`).all({ $p: projectId, $since: since, $until: until });
|
|
28451
|
+
}
|
|
28452
|
+
function compare(db, projectId, aSince, aUntil, bSince, bUntil) {
|
|
28453
|
+
const errorsA = getErrorsByMessage(db, projectId, aSince, aUntil);
|
|
28454
|
+
const errorsB = getErrorsByMessage(db, projectId, bSince, bUntil);
|
|
28455
|
+
const keyA = new Set(errorsA.map((e) => `${e.service}|${e.message}`));
|
|
28456
|
+
const keyB = new Set(errorsB.map((e) => `${e.service}|${e.message}`));
|
|
28457
|
+
const new_errors = errorsB.filter((e) => !keyA.has(`${e.service}|${e.message}`));
|
|
28458
|
+
const resolved_errors = errorsA.filter((e) => !keyB.has(`${e.service}|${e.message}`));
|
|
28459
|
+
const svcA = getErrorsByService(db, projectId, aSince, aUntil);
|
|
28460
|
+
const svcB = getErrorsByService(db, projectId, bSince, bUntil);
|
|
28461
|
+
const svcMapA = new Map(svcA.map((s) => [s.service, s.errors]));
|
|
28462
|
+
const svcMapB = new Map(svcB.map((s) => [s.service, s.errors]));
|
|
28463
|
+
const allSvcs = new Set([...svcMapA.keys(), ...svcMapB.keys()]);
|
|
28464
|
+
const error_delta_by_service = [...allSvcs].map((svc) => ({
|
|
28465
|
+
service: svc,
|
|
28466
|
+
errors_a: svcMapA.get(svc) ?? 0,
|
|
28467
|
+
errors_b: svcMapB.get(svc) ?? 0,
|
|
28468
|
+
delta: (svcMapB.get(svc) ?? 0) - (svcMapA.get(svc) ?? 0)
|
|
28469
|
+
})).sort((a, b) => Math.abs(b.delta) - Math.abs(a.delta));
|
|
28470
|
+
const perf_delta_by_page = db.prepare(`
|
|
28471
|
+
SELECT
|
|
28472
|
+
pa.page_id, pg.url,
|
|
28473
|
+
pa.score as score_a,
|
|
28474
|
+
pb.score as score_b,
|
|
28475
|
+
(pb.score - pa.score) as delta
|
|
28476
|
+
FROM
|
|
28477
|
+
(SELECT page_id, AVG(score) as score FROM performance_snapshots WHERE project_id = $p AND timestamp >= $as AND timestamp <= $au GROUP BY page_id) pa
|
|
28478
|
+
JOIN pages pg ON pg.id = pa.page_id
|
|
28479
|
+
LEFT JOIN (SELECT page_id, AVG(score) as score FROM performance_snapshots WHERE project_id = $p AND timestamp >= $bs AND timestamp <= $bu GROUP BY page_id) pb ON pb.page_id = pa.page_id
|
|
28480
|
+
ORDER BY delta ASC
|
|
28481
|
+
`).all({ $p: projectId, $as: aSince, $au: aUntil, $bs: bSince, $bu: bUntil });
|
|
28482
|
+
const summary = [
|
|
28483
|
+
`${new_errors.length} new error type(s), ${resolved_errors.length} resolved.`,
|
|
28484
|
+
error_delta_by_service.filter((s) => s.delta > 0).map((s) => `${s.service ?? "unknown"}: +${s.delta}`).join(", ") || "No error increases."
|
|
28485
|
+
].join(" ");
|
|
28486
|
+
return {
|
|
28487
|
+
project_id: projectId,
|
|
28488
|
+
window_a: { since: aSince, until: aUntil },
|
|
28489
|
+
window_b: { since: bSince, until: bUntil },
|
|
28490
|
+
new_errors,
|
|
28491
|
+
resolved_errors,
|
|
28492
|
+
error_delta_by_service,
|
|
28493
|
+
perf_delta_by_page,
|
|
28494
|
+
summary
|
|
28495
|
+
};
|
|
28496
|
+
}
|
|
28497
|
+
|
|
28371
28498
|
// src/mcp/index.ts
|
|
28372
28499
|
var db = getDb();
|
|
28373
|
-
var server = new McpServer({ name: "logs", version: "0.0
|
|
28500
|
+
var server = new McpServer({ name: "logs", version: "0.1.0" });
|
|
28501
|
+
function applyBrief(rows, brief = true) {
|
|
28502
|
+
if (!brief)
|
|
28503
|
+
return rows;
|
|
28504
|
+
return rows.map((r) => ({ id: r.id, timestamp: r.timestamp, level: r.level, message: r.message, service: r.service }));
|
|
28505
|
+
}
|
|
28374
28506
|
var TOOLS = {
|
|
28375
28507
|
register_project: "Register a project (name, github_repo?, base_url?, description?)",
|
|
28376
|
-
register_page: "Register a page URL
|
|
28377
|
-
create_scan_job: "Schedule
|
|
28508
|
+
register_page: "Register a page URL (project_id, url, path?, name?)",
|
|
28509
|
+
create_scan_job: "Schedule page scans (project_id, schedule, page_id?)",
|
|
28378
28510
|
log_push: "Push a log entry (level, message, project_id?, service?, trace_id?, metadata?)",
|
|
28379
|
-
log_search: "Search logs (project_id?,
|
|
28380
|
-
log_tail: "
|
|
28381
|
-
log_summary: "Error/warn counts by service
|
|
28382
|
-
log_context: "All logs for a trace_id",
|
|
28383
|
-
|
|
28384
|
-
|
|
28385
|
-
|
|
28386
|
-
|
|
28511
|
+
log_search: "Search logs (project_id?, level?, since?, text?, brief?=true, limit?)",
|
|
28512
|
+
log_tail: "Recent logs (project_id?, n?, brief?=true)",
|
|
28513
|
+
log_summary: "Error/warn counts by service (project_id?, since?)",
|
|
28514
|
+
log_context: "All logs for a trace_id (trace_id, brief?=true)",
|
|
28515
|
+
log_diagnose: "Full diagnosis: top errors, failing pages, perf regressions (project_id, since?)",
|
|
28516
|
+
log_compare: "Compare two time windows for new/resolved errors and perf delta",
|
|
28517
|
+
perf_snapshot: "Latest perf snapshot (project_id, page_id?)",
|
|
28518
|
+
perf_trend: "Perf over time (project_id, page_id?, since?, limit?)",
|
|
28519
|
+
scan_status: "Last scan jobs (project_id?)",
|
|
28520
|
+
list_projects: "List all projects",
|
|
28387
28521
|
list_pages: "List pages for a project (project_id)",
|
|
28388
|
-
|
|
28389
|
-
|
|
28522
|
+
list_issues: "List grouped error issues (project_id?, status?, limit?)",
|
|
28523
|
+
resolve_issue: "Update issue status (id, status: open|resolved|ignored)",
|
|
28524
|
+
create_alert_rule: "Create alert rule (project_id, name, level, threshold_count, window_seconds, webhook_url?)",
|
|
28525
|
+
list_alert_rules: "List alert rules (project_id?)",
|
|
28526
|
+
delete_alert_rule: "Delete alert rule (id)",
|
|
28527
|
+
get_health: "Server health + DB stats",
|
|
28528
|
+
search_tools: "Search tools by keyword (query)",
|
|
28529
|
+
describe_tools: "List all tools"
|
|
28390
28530
|
};
|
|
28391
28531
|
server.tool("search_tools", { query: exports_external.string() }, ({ query }) => {
|
|
28392
28532
|
const q = query.toLowerCase();
|
|
@@ -28394,37 +28534,27 @@ server.tool("search_tools", { query: exports_external.string() }, ({ query }) =>
|
|
|
28394
28534
|
return { content: [{ type: "text", text: matches.map(([k, v]) => `${k}: ${v}`).join(`
|
|
28395
28535
|
`) || "No matches" }] };
|
|
28396
28536
|
});
|
|
28397
|
-
server.tool("describe_tools", {}, () => {
|
|
28398
|
-
|
|
28399
|
-
`)
|
|
28400
|
-
|
|
28401
|
-
});
|
|
28537
|
+
server.tool("describe_tools", {}, () => ({
|
|
28538
|
+
content: [{ type: "text", text: Object.entries(TOOLS).map(([k, v]) => `${k}: ${v}`).join(`
|
|
28539
|
+
`) }]
|
|
28540
|
+
}));
|
|
28402
28541
|
server.tool("register_project", {
|
|
28403
28542
|
name: exports_external.string(),
|
|
28404
28543
|
github_repo: exports_external.string().optional(),
|
|
28405
28544
|
base_url: exports_external.string().optional(),
|
|
28406
28545
|
description: exports_external.string().optional()
|
|
28407
|
-
}, (args) => {
|
|
28408
|
-
const project = createProject(db, args);
|
|
28409
|
-
return { content: [{ type: "text", text: JSON.stringify(project) }] };
|
|
28410
|
-
});
|
|
28546
|
+
}, (args) => ({ content: [{ type: "text", text: JSON.stringify(createProject(db, args)) }] }));
|
|
28411
28547
|
server.tool("register_page", {
|
|
28412
28548
|
project_id: exports_external.string(),
|
|
28413
28549
|
url: exports_external.string(),
|
|
28414
28550
|
path: exports_external.string().optional(),
|
|
28415
28551
|
name: exports_external.string().optional()
|
|
28416
|
-
}, (args) => {
|
|
28417
|
-
const page = createPage(db, args);
|
|
28418
|
-
return { content: [{ type: "text", text: JSON.stringify(page) }] };
|
|
28419
|
-
});
|
|
28552
|
+
}, (args) => ({ content: [{ type: "text", text: JSON.stringify(createPage(db, args)) }] }));
|
|
28420
28553
|
server.tool("create_scan_job", {
|
|
28421
28554
|
project_id: exports_external.string(),
|
|
28422
28555
|
schedule: exports_external.string(),
|
|
28423
28556
|
page_id: exports_external.string().optional()
|
|
28424
|
-
}, (args) => {
|
|
28425
|
-
const job = createJob(db, args);
|
|
28426
|
-
return { content: [{ type: "text", text: JSON.stringify(job) }] };
|
|
28427
|
-
});
|
|
28557
|
+
}, (args) => ({ content: [{ type: "text", text: JSON.stringify(createJob(db, args)) }] }));
|
|
28428
28558
|
server.tool("log_push", {
|
|
28429
28559
|
level: exports_external.enum(["debug", "info", "warn", "error", "fatal"]),
|
|
28430
28560
|
message: exports_external.string(),
|
|
@@ -28448,60 +28578,108 @@ server.tool("log_search", {
|
|
|
28448
28578
|
until: exports_external.string().optional(),
|
|
28449
28579
|
text: exports_external.string().optional(),
|
|
28450
28580
|
trace_id: exports_external.string().optional(),
|
|
28451
|
-
limit: exports_external.number().optional()
|
|
28581
|
+
limit: exports_external.number().optional(),
|
|
28582
|
+
brief: exports_external.boolean().optional()
|
|
28452
28583
|
}, (args) => {
|
|
28453
|
-
const rows = searchLogs(db, {
|
|
28454
|
-
|
|
28455
|
-
level: args.level ? args.level.split(",") : undefined
|
|
28456
|
-
});
|
|
28457
|
-
return { content: [{ type: "text", text: JSON.stringify(rows) }] };
|
|
28584
|
+
const rows = searchLogs(db, { ...args, level: args.level ? args.level.split(",") : undefined });
|
|
28585
|
+
return { content: [{ type: "text", text: JSON.stringify(applyBrief(rows, args.brief !== false)) }] };
|
|
28458
28586
|
});
|
|
28459
28587
|
server.tool("log_tail", {
|
|
28460
28588
|
project_id: exports_external.string().optional(),
|
|
28461
|
-
n: exports_external.number().optional()
|
|
28462
|
-
|
|
28589
|
+
n: exports_external.number().optional(),
|
|
28590
|
+
brief: exports_external.boolean().optional()
|
|
28591
|
+
}, ({ project_id, n, brief }) => {
|
|
28463
28592
|
const rows = tailLogs(db, project_id, n ?? 50);
|
|
28464
|
-
return { content: [{ type: "text", text: JSON.stringify(rows) }] };
|
|
28593
|
+
return { content: [{ type: "text", text: JSON.stringify(applyBrief(rows, brief !== false)) }] };
|
|
28465
28594
|
});
|
|
28466
28595
|
server.tool("log_summary", {
|
|
28467
28596
|
project_id: exports_external.string().optional(),
|
|
28468
28597
|
since: exports_external.string().optional()
|
|
28469
|
-
}, ({ project_id, since }) => {
|
|
28470
|
-
|
|
28471
|
-
|
|
28472
|
-
|
|
28473
|
-
|
|
28598
|
+
}, ({ project_id, since }) => ({
|
|
28599
|
+
content: [{ type: "text", text: JSON.stringify(summarizeLogs(db, project_id, since)) }]
|
|
28600
|
+
}));
|
|
28601
|
+
server.tool("log_context", {
|
|
28602
|
+
trace_id: exports_external.string(),
|
|
28603
|
+
brief: exports_external.boolean().optional()
|
|
28604
|
+
}, ({ trace_id, brief }) => {
|
|
28474
28605
|
const rows = getLogContext(db, trace_id);
|
|
28475
|
-
return { content: [{ type: "text", text: JSON.stringify(rows) }] };
|
|
28606
|
+
return { content: [{ type: "text", text: JSON.stringify(applyBrief(rows, brief !== false)) }] };
|
|
28476
28607
|
});
|
|
28608
|
+
server.tool("log_diagnose", {
|
|
28609
|
+
project_id: exports_external.string(),
|
|
28610
|
+
since: exports_external.string().optional()
|
|
28611
|
+
}, ({ project_id, since }) => ({
|
|
28612
|
+
content: [{ type: "text", text: JSON.stringify(diagnose(db, project_id, since)) }]
|
|
28613
|
+
}));
|
|
28614
|
+
server.tool("log_compare", {
|
|
28615
|
+
project_id: exports_external.string(),
|
|
28616
|
+
a_since: exports_external.string(),
|
|
28617
|
+
a_until: exports_external.string(),
|
|
28618
|
+
b_since: exports_external.string(),
|
|
28619
|
+
b_until: exports_external.string()
|
|
28620
|
+
}, ({ project_id, a_since, a_until, b_since, b_until }) => ({
|
|
28621
|
+
content: [{ type: "text", text: JSON.stringify(compare(db, project_id, a_since, a_until, b_since, b_until)) }]
|
|
28622
|
+
}));
|
|
28477
28623
|
server.tool("perf_snapshot", {
|
|
28478
28624
|
project_id: exports_external.string(),
|
|
28479
28625
|
page_id: exports_external.string().optional()
|
|
28480
28626
|
}, ({ project_id, page_id }) => {
|
|
28481
28627
|
const snap = getLatestSnapshot(db, project_id, page_id);
|
|
28482
|
-
|
|
28483
|
-
return { content: [{ type: "text", text: JSON.stringify({ ...snap, label }) }] };
|
|
28628
|
+
return { content: [{ type: "text", text: JSON.stringify(snap ? { ...snap, label: scoreLabel(snap.score) } : null) }] };
|
|
28484
28629
|
});
|
|
28485
28630
|
server.tool("perf_trend", {
|
|
28486
28631
|
project_id: exports_external.string(),
|
|
28487
28632
|
page_id: exports_external.string().optional(),
|
|
28488
28633
|
since: exports_external.string().optional(),
|
|
28489
28634
|
limit: exports_external.number().optional()
|
|
28490
|
-
}, ({ project_id, page_id, since, limit }) => {
|
|
28491
|
-
|
|
28492
|
-
|
|
28493
|
-
});
|
|
28635
|
+
}, ({ project_id, page_id, since, limit }) => ({
|
|
28636
|
+
content: [{ type: "text", text: JSON.stringify(getPerfTrend(db, project_id, page_id, since, limit ?? 50)) }]
|
|
28637
|
+
}));
|
|
28494
28638
|
server.tool("scan_status", {
|
|
28495
28639
|
project_id: exports_external.string().optional()
|
|
28496
|
-
}, ({ project_id }) => {
|
|
28497
|
-
|
|
28498
|
-
|
|
28499
|
-
})
|
|
28500
|
-
|
|
28501
|
-
|
|
28502
|
-
})
|
|
28503
|
-
|
|
28504
|
-
|
|
28640
|
+
}, ({ project_id }) => ({
|
|
28641
|
+
content: [{ type: "text", text: JSON.stringify(listJobs(db, project_id)) }]
|
|
28642
|
+
}));
|
|
28643
|
+
server.tool("list_projects", {}, () => ({
|
|
28644
|
+
content: [{ type: "text", text: JSON.stringify(listProjects(db)) }]
|
|
28645
|
+
}));
|
|
28646
|
+
server.tool("list_pages", { project_id: exports_external.string() }, ({ project_id }) => ({
|
|
28647
|
+
content: [{ type: "text", text: JSON.stringify(listPages(db, project_id)) }]
|
|
28648
|
+
}));
|
|
28649
|
+
server.tool("list_issues", {
|
|
28650
|
+
project_id: exports_external.string().optional(),
|
|
28651
|
+
status: exports_external.string().optional(),
|
|
28652
|
+
limit: exports_external.number().optional()
|
|
28653
|
+
}, ({ project_id, status, limit }) => ({
|
|
28654
|
+
content: [{ type: "text", text: JSON.stringify(listIssues(db, project_id, status, limit ?? 50)) }]
|
|
28655
|
+
}));
|
|
28656
|
+
server.tool("resolve_issue", {
|
|
28657
|
+
id: exports_external.string(),
|
|
28658
|
+
status: exports_external.enum(["open", "resolved", "ignored"])
|
|
28659
|
+
}, ({ id, status }) => ({
|
|
28660
|
+
content: [{ type: "text", text: JSON.stringify(updateIssueStatus(db, id, status)) }]
|
|
28661
|
+
}));
|
|
28662
|
+
server.tool("create_alert_rule", {
|
|
28663
|
+
project_id: exports_external.string(),
|
|
28664
|
+
name: exports_external.string(),
|
|
28665
|
+
level: exports_external.string().optional(),
|
|
28666
|
+
service: exports_external.string().optional(),
|
|
28667
|
+
threshold_count: exports_external.number().optional(),
|
|
28668
|
+
window_seconds: exports_external.number().optional(),
|
|
28669
|
+
action: exports_external.enum(["webhook", "log"]).optional(),
|
|
28670
|
+
webhook_url: exports_external.string().optional()
|
|
28671
|
+
}, (args) => ({ content: [{ type: "text", text: JSON.stringify(createAlertRule(db, args)) }] }));
|
|
28672
|
+
server.tool("list_alert_rules", {
|
|
28673
|
+
project_id: exports_external.string().optional()
|
|
28674
|
+
}, ({ project_id }) => ({
|
|
28675
|
+
content: [{ type: "text", text: JSON.stringify(listAlertRules(db, project_id)) }]
|
|
28676
|
+
}));
|
|
28677
|
+
server.tool("delete_alert_rule", { id: exports_external.string() }, ({ id }) => {
|
|
28678
|
+
deleteAlertRule(db, id);
|
|
28679
|
+
return { content: [{ type: "text", text: "deleted" }] };
|
|
28505
28680
|
});
|
|
28681
|
+
server.tool("get_health", {}, () => ({
|
|
28682
|
+
content: [{ type: "text", text: JSON.stringify(getHealth(db)) }]
|
|
28683
|
+
}));
|
|
28506
28684
|
var transport = new StdioServerTransport;
|
|
28507
28685
|
await server.connect(transport);
|