@hasna/logs 0.3.2 → 0.3.4
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 +43 -30
- package/dist/export-c3eqjste.js +10 -0
- package/dist/health-9792c1rc.js +8 -0
- package/dist/index-fzmz9aqs.js +1241 -0
- package/dist/index-re3ntm60.js +48 -0
- package/dist/index-xjn8gam3.js +39 -0
- package/dist/jobs-ypmmc2ma.js +22 -0
- package/dist/mcp/index.js +2 -2
- package/dist/query-shjjj67k.js +14 -0
- package/dist/server/index.js +3 -3
- package/package.json +1 -1
- package/src/cli/index.ts +43 -27
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
12
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
for (let key of __getOwnPropNames(mod))
|
|
23
|
+
if (!__hasOwnProp.call(to, key))
|
|
24
|
+
__defProp(to, key, {
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
32
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __returnValue = (v) => v;
|
|
34
|
+
function __exportSetter(name, newValue) {
|
|
35
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
36
|
+
}
|
|
37
|
+
var __export = (target, all) => {
|
|
38
|
+
for (var name in all)
|
|
39
|
+
__defProp(target, name, {
|
|
40
|
+
get: all[name],
|
|
41
|
+
enumerable: true,
|
|
42
|
+
configurable: true,
|
|
43
|
+
set: __exportSetter.bind(all, name)
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
var __require = import.meta.require;
|
|
47
|
+
|
|
48
|
+
export { __toESM, __commonJS, __export, __require };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
__require
|
|
4
|
+
} from "./index-re3ntm60.js";
|
|
5
|
+
|
|
6
|
+
// src/lib/health.ts
|
|
7
|
+
var startTime = Date.now();
|
|
8
|
+
function getHealth(db) {
|
|
9
|
+
const projects = db.prepare("SELECT COUNT(*) as c FROM projects").get().c;
|
|
10
|
+
const total_logs = db.prepare("SELECT COUNT(*) as c FROM logs").get().c;
|
|
11
|
+
const scheduler_jobs = db.prepare("SELECT COUNT(*) as c FROM scan_jobs WHERE enabled = 1").get().c;
|
|
12
|
+
const open_issues = db.prepare("SELECT COUNT(*) as c FROM issues WHERE status = 'open'").get().c;
|
|
13
|
+
const levelRows = db.prepare("SELECT level, COUNT(*) as c FROM logs GROUP BY level").all();
|
|
14
|
+
const logs_by_level = Object.fromEntries(levelRows.map((r) => [r.level, r.c]));
|
|
15
|
+
const oldest = db.prepare("SELECT MIN(timestamp) as t FROM logs").get();
|
|
16
|
+
const newest = db.prepare("SELECT MAX(timestamp) as t FROM logs").get();
|
|
17
|
+
let db_size_bytes = null;
|
|
18
|
+
try {
|
|
19
|
+
const dbPath = process.env.LOGS_DB_PATH;
|
|
20
|
+
if (dbPath) {
|
|
21
|
+
const { statSync } = __require("fs");
|
|
22
|
+
db_size_bytes = statSync(dbPath).size;
|
|
23
|
+
}
|
|
24
|
+
} catch {}
|
|
25
|
+
return {
|
|
26
|
+
status: "ok",
|
|
27
|
+
uptime_seconds: Math.floor((Date.now() - startTime) / 1000),
|
|
28
|
+
db_size_bytes,
|
|
29
|
+
projects,
|
|
30
|
+
total_logs,
|
|
31
|
+
logs_by_level,
|
|
32
|
+
oldest_log: oldest.t,
|
|
33
|
+
newest_log: newest.t,
|
|
34
|
+
scheduler_jobs,
|
|
35
|
+
open_issues
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { getHealth };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
createJob,
|
|
4
|
+
createScanRun,
|
|
5
|
+
deleteJob,
|
|
6
|
+
finishScanRun,
|
|
7
|
+
getJob,
|
|
8
|
+
listJobs,
|
|
9
|
+
listScanRuns,
|
|
10
|
+
updateJob
|
|
11
|
+
} from "./index-3dr7d80h.js";
|
|
12
|
+
import"./index-re3ntm60.js";
|
|
13
|
+
export {
|
|
14
|
+
updateJob,
|
|
15
|
+
listScanRuns,
|
|
16
|
+
listJobs,
|
|
17
|
+
getJob,
|
|
18
|
+
finishScanRun,
|
|
19
|
+
deleteJob,
|
|
20
|
+
createScanRun,
|
|
21
|
+
createJob
|
|
22
|
+
};
|
package/dist/mcp/index.js
CHANGED
|
@@ -35,12 +35,12 @@ import {
|
|
|
35
35
|
} from "../index-rbrsvsyh.js";
|
|
36
36
|
import {
|
|
37
37
|
getHealth
|
|
38
|
-
} from "../index-
|
|
38
|
+
} from "../index-xjn8gam3.js";
|
|
39
39
|
import {
|
|
40
40
|
__commonJS,
|
|
41
41
|
__export,
|
|
42
42
|
__toESM
|
|
43
|
-
} from "../index-
|
|
43
|
+
} from "../index-re3ntm60.js";
|
|
44
44
|
|
|
45
45
|
// node_modules/ajv/dist/compile/codegen/code.js
|
|
46
46
|
var require_code = __commonJS((exports) => {
|
package/dist/server/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
setPageAuth,
|
|
7
7
|
setRetentionPolicy,
|
|
8
8
|
startScheduler
|
|
9
|
-
} from "../index-
|
|
9
|
+
} from "../index-fzmz9aqs.js";
|
|
10
10
|
import {
|
|
11
11
|
countLogs
|
|
12
12
|
} from "../index-6y8pmes4.js";
|
|
@@ -50,8 +50,8 @@ import {
|
|
|
50
50
|
} from "../index-eh9bkbpa.js";
|
|
51
51
|
import {
|
|
52
52
|
getHealth
|
|
53
|
-
} from "../index-
|
|
54
|
-
import"../index-
|
|
53
|
+
} from "../index-xjn8gam3.js";
|
|
54
|
+
import"../index-re3ntm60.js";
|
|
55
55
|
|
|
56
56
|
// node_modules/hono/dist/compose.js
|
|
57
57
|
var compose = (middleware, onError, onNotFound) => {
|
package/package.json
CHANGED
package/src/cli/index.ts
CHANGED
|
@@ -5,10 +5,16 @@ import { ingestLog } from "../lib/ingest.ts"
|
|
|
5
5
|
import { searchLogs, tailLogs } from "../lib/query.ts"
|
|
6
6
|
import { summarizeLogs } from "../lib/summarize.ts"
|
|
7
7
|
import { createJob, listJobs } from "../lib/jobs.ts"
|
|
8
|
-
import { createPage, createProject, listPages, listProjects } from "../lib/projects.ts"
|
|
8
|
+
import { createPage, createProject, listPages, listProjects, resolveProjectId } from "../lib/projects.ts"
|
|
9
9
|
import { runJob } from "../lib/scheduler.ts"
|
|
10
10
|
import type { LogLevel } from "../types/index.ts"
|
|
11
11
|
|
|
12
|
+
/** Resolve a project name or ID from CLI --project flag */
|
|
13
|
+
function resolveProject(nameOrId: string | undefined): string | undefined {
|
|
14
|
+
if (!nameOrId) return undefined;
|
|
15
|
+
return resolveProjectId(getDb(), nameOrId) ?? nameOrId;
|
|
16
|
+
}
|
|
17
|
+
|
|
12
18
|
const program = new Command()
|
|
13
19
|
.name("logs")
|
|
14
20
|
.description("@hasna/logs — log aggregation and monitoring")
|
|
@@ -17,7 +23,7 @@ const program = new Command()
|
|
|
17
23
|
// ── logs list ──────────────────────────────────────────────
|
|
18
24
|
program.command("list")
|
|
19
25
|
.description("Search and list logs")
|
|
20
|
-
.option("--project <id>", "Filter by project ID")
|
|
26
|
+
.option("--project <name|id>", "Filter by project name or ID")
|
|
21
27
|
.option("--page <id>", "Filter by page ID")
|
|
22
28
|
.option("--level <levels>", "Comma-separated levels (error,warn,info,debug,fatal)")
|
|
23
29
|
.option("--service <name>", "Filter by service")
|
|
@@ -29,7 +35,7 @@ program.command("list")
|
|
|
29
35
|
const db = getDb()
|
|
30
36
|
const since = parseRelativeTime(opts.since)
|
|
31
37
|
const rows = searchLogs(db, {
|
|
32
|
-
project_id: opts.project,
|
|
38
|
+
project_id: resolveProject(opts.project),
|
|
33
39
|
page_id: opts.page,
|
|
34
40
|
level: opts.level ? (opts.level.split(",") as LogLevel[]) : undefined,
|
|
35
41
|
service: opts.service,
|
|
@@ -52,20 +58,20 @@ program.command("list")
|
|
|
52
58
|
// ── logs tail ──────────────────────────────────────────────
|
|
53
59
|
program.command("tail")
|
|
54
60
|
.description("Show most recent logs")
|
|
55
|
-
.option("--project <id>")
|
|
61
|
+
.option("--project <name|id>", "Project name or ID")
|
|
56
62
|
.option("--n <count>", "Number of logs", "50")
|
|
57
63
|
.action((opts) => {
|
|
58
|
-
const rows = tailLogs(getDb(), opts.project, Number(opts.n))
|
|
64
|
+
const rows = tailLogs(getDb(), resolveProject(opts.project), Number(opts.n))
|
|
59
65
|
for (const r of rows) console.log(`${r.timestamp} ${pad(r.level.toUpperCase(), 5)} ${r.message}`)
|
|
60
66
|
})
|
|
61
67
|
|
|
62
68
|
// ── logs summary ──────────────────────────────────────────
|
|
63
69
|
program.command("summary")
|
|
64
70
|
.description("Error/warn summary by service")
|
|
65
|
-
.option("--project <id>")
|
|
71
|
+
.option("--project <name|id>", "Project name or ID")
|
|
66
72
|
.option("--since <time>", "Relative time (1h, 24h, 7d)", "24h")
|
|
67
73
|
.action((opts) => {
|
|
68
|
-
const summary = summarizeLogs(getDb(), opts.project, parseRelativeTime(opts.since))
|
|
74
|
+
const summary = summarizeLogs(getDb(), resolveProject(opts.project), parseRelativeTime(opts.since))
|
|
69
75
|
if (!summary.length) { console.log("No errors/warnings in this window."); return }
|
|
70
76
|
for (const s of summary) console.log(`${pad(s.level.toUpperCase(), 5)} ${pad(s.service ?? "-", 15)} count=${s.count} latest=${s.latest}`)
|
|
71
77
|
})
|
|
@@ -75,10 +81,10 @@ program.command("push <message>")
|
|
|
75
81
|
.description("Push a log entry")
|
|
76
82
|
.option("--level <level>", "Log level", "info")
|
|
77
83
|
.option("--service <name>")
|
|
78
|
-
.option("--project <id>")
|
|
84
|
+
.option("--project <name|id>", "Project name or ID")
|
|
79
85
|
.option("--trace <id>", "Trace ID")
|
|
80
86
|
.action((message, opts) => {
|
|
81
|
-
const row = ingestLog(getDb(), { level: opts.level as LogLevel, message, service: opts.service, project_id: opts.project, trace_id: opts.trace })
|
|
87
|
+
const row = ingestLog(getDb(), { level: opts.level as LogLevel, message, service: opts.service, project_id: resolveProject(opts.project), trace_id: opts.trace })
|
|
82
88
|
console.log(`Logged: ${row.id}`)
|
|
83
89
|
})
|
|
84
90
|
|
|
@@ -104,18 +110,18 @@ projectCmd.command("list").action(() => {
|
|
|
104
110
|
const pageCmd = program.command("page").description("Manage pages")
|
|
105
111
|
|
|
106
112
|
pageCmd.command("add")
|
|
107
|
-
.option("--project <id>")
|
|
113
|
+
.option("--project <name|id>", "Project name or ID")
|
|
108
114
|
.option("--url <url>")
|
|
109
115
|
.option("--name <name>")
|
|
110
116
|
.action((opts) => {
|
|
111
117
|
if (!opts.project || !opts.url) { console.error("--project and --url required"); process.exit(1) }
|
|
112
|
-
const p = createPage(getDb(), { project_id: opts.project, url: opts.url, name: opts.name })
|
|
118
|
+
const p = createPage(getDb(), { project_id: resolveProject(opts.project), url: opts.url, name: opts.name })
|
|
113
119
|
console.log(`Page registered: ${p.id} — ${p.url}`)
|
|
114
120
|
})
|
|
115
121
|
|
|
116
|
-
pageCmd.command("list").option("--project <id>").action((opts) => {
|
|
122
|
+
pageCmd.command("list").option("--project <name|id>", "Project name or ID").action((opts) => {
|
|
117
123
|
if (!opts.project) { console.error("--project required"); process.exit(1) }
|
|
118
|
-
const pages = listPages(getDb(), opts.project)
|
|
124
|
+
const pages = listPages(getDb(), resolveProject(opts.project))
|
|
119
125
|
for (const p of pages) console.log(`${p.id} ${p.url} last=${p.last_scanned_at ?? "never"}`)
|
|
120
126
|
})
|
|
121
127
|
|
|
@@ -123,16 +129,16 @@ pageCmd.command("list").option("--project <id>").action((opts) => {
|
|
|
123
129
|
const jobCmd = program.command("job").description("Manage scan jobs")
|
|
124
130
|
|
|
125
131
|
jobCmd.command("create")
|
|
126
|
-
.option("--project <id>")
|
|
132
|
+
.option("--project <name|id>", "Project name or ID")
|
|
127
133
|
.option("--schedule <cron>", "Cron expression", "*/30 * * * *")
|
|
128
134
|
.action((opts) => {
|
|
129
135
|
if (!opts.project) { console.error("--project required"); process.exit(1) }
|
|
130
|
-
const j = createJob(getDb(), { project_id: opts.project, schedule: opts.schedule })
|
|
136
|
+
const j = createJob(getDb(), { project_id: resolveProject(opts.project), schedule: opts.schedule })
|
|
131
137
|
console.log(`Job created: ${j.id} — ${j.schedule}`)
|
|
132
138
|
})
|
|
133
139
|
|
|
134
|
-
jobCmd.command("list").option("--project <id>").action((opts) => {
|
|
135
|
-
const jobs = listJobs(getDb(), opts.project)
|
|
140
|
+
jobCmd.command("list").option("--project <name|id>", "Project name or ID").action((opts) => {
|
|
141
|
+
const jobs = listJobs(getDb(), resolveProject(opts.project))
|
|
136
142
|
for (const j of jobs) console.log(`${j.id} ${j.schedule} enabled=${j.enabled} last=${j.last_run_at ?? "never"}`)
|
|
137
143
|
})
|
|
138
144
|
|
|
@@ -140,7 +146,7 @@ jobCmd.command("list").option("--project <id>").action((opts) => {
|
|
|
140
146
|
program.command("scan")
|
|
141
147
|
.description("Run an immediate scan for a job")
|
|
142
148
|
.option("--job <id>")
|
|
143
|
-
.option("--project <id>")
|
|
149
|
+
.option("--project <name|id>", "Project name or ID")
|
|
144
150
|
.action(async (opts) => {
|
|
145
151
|
if (!opts.job) { console.error("--job required"); process.exit(1) }
|
|
146
152
|
const db = getDb()
|
|
@@ -154,29 +160,39 @@ program.command("scan")
|
|
|
154
160
|
// ── logs watch ────────────────────────────────────────────
|
|
155
161
|
program.command("watch")
|
|
156
162
|
.description("Stream new logs in real time with color coding")
|
|
157
|
-
.option("--project <id>")
|
|
158
|
-
.option("--level <levels>", "Comma-separated levels")
|
|
159
|
-
.option("--service <name>")
|
|
163
|
+
.option("--project <name|id>", "Filter by project name or ID")
|
|
164
|
+
.option("--level <levels>", "Comma-separated levels (debug,info,warn,error,fatal)")
|
|
165
|
+
.option("--service <name>", "Filter by service name")
|
|
166
|
+
.option("--interval <ms>", "Poll interval in milliseconds (default: 500)", "500")
|
|
167
|
+
.option("--since <time>", "Start from this time (default: now)")
|
|
160
168
|
.action(async (opts) => {
|
|
161
169
|
const db = getDb()
|
|
162
170
|
const { searchLogs } = await import("../lib/query.ts")
|
|
163
171
|
|
|
172
|
+
// Resolve project name → ID if needed
|
|
173
|
+
let projectId = opts.project
|
|
174
|
+
if (projectId) {
|
|
175
|
+
const proj = db.query("SELECT id FROM projects WHERE id = ? OR name = ?").get(projectId, projectId) as { id: string } | null
|
|
176
|
+
if (proj) projectId = proj.id
|
|
177
|
+
}
|
|
178
|
+
|
|
164
179
|
const COLORS: Record<string, string> = {
|
|
165
180
|
debug: "\x1b[90m", info: "\x1b[36m", warn: "\x1b[33m", error: "\x1b[31m", fatal: "\x1b[35m",
|
|
166
181
|
}
|
|
167
182
|
const RESET = "\x1b[0m"
|
|
168
183
|
const BOLD = "\x1b[1m"
|
|
169
184
|
|
|
170
|
-
let lastTimestamp = new Date().toISOString()
|
|
185
|
+
let lastTimestamp = opts.since ? new Date(opts.since).toISOString() : new Date().toISOString()
|
|
171
186
|
let errorCount = 0
|
|
172
187
|
let warnCount = 0
|
|
188
|
+
const pollIntervalMs = Math.max(100, Number(opts.interval) || 500)
|
|
173
189
|
|
|
174
190
|
process.stdout.write(`\x1b[2J\x1b[H`) // clear screen
|
|
175
|
-
console.log(`${BOLD}@hasna/logs watch${RESET} — Ctrl+C to exit\n`)
|
|
191
|
+
console.log(`${BOLD}@hasna/logs watch${RESET} — Ctrl+C to exit${projectId ? ` [project: ${opts.project}]` : ''}\n`)
|
|
176
192
|
|
|
177
193
|
const poll = () => {
|
|
178
194
|
const rows = searchLogs(db, {
|
|
179
|
-
project_id:
|
|
195
|
+
project_id: projectId,
|
|
180
196
|
level: opts.level ? (opts.level.split(",") as LogLevel[]) : undefined,
|
|
181
197
|
service: opts.service,
|
|
182
198
|
since: lastTimestamp,
|
|
@@ -199,14 +215,14 @@ program.command("watch")
|
|
|
199
215
|
process.stdout.write(`\x1b]2;logs: ${errorCount}E ${warnCount}W\x07`)
|
|
200
216
|
}
|
|
201
217
|
|
|
202
|
-
const interval = setInterval(poll,
|
|
218
|
+
const interval = setInterval(poll, pollIntervalMs)
|
|
203
219
|
process.on("SIGINT", () => { clearInterval(interval); console.log(`\n\nErrors: ${errorCount} Warnings: ${warnCount}`); process.exit(0) })
|
|
204
220
|
})
|
|
205
221
|
|
|
206
222
|
// ── logs export ───────────────────────────────────────────
|
|
207
223
|
program.command("export")
|
|
208
224
|
.description("Export logs to JSON or CSV")
|
|
209
|
-
.option("--project <id>")
|
|
225
|
+
.option("--project <name|id>", "Project name or ID")
|
|
210
226
|
.option("--since <time>", "Relative time or ISO")
|
|
211
227
|
.option("--level <level>")
|
|
212
228
|
.option("--service <name>")
|
|
@@ -218,7 +234,7 @@ program.command("export")
|
|
|
218
234
|
const { createWriteStream } = await import("node:fs")
|
|
219
235
|
const db = getDb()
|
|
220
236
|
const options = {
|
|
221
|
-
project_id: opts.project,
|
|
237
|
+
project_id: resolveProject(opts.project),
|
|
222
238
|
since: parseRelativeTime(opts.since),
|
|
223
239
|
level: opts.level,
|
|
224
240
|
service: opts.service,
|