@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.
@@ -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-yb8yd4j6.js";
38
+ } from "../index-xjn8gam3.js";
39
39
  import {
40
40
  __commonJS,
41
41
  __export,
42
42
  __toESM
43
- } from "../index-g8dczzvv.js";
43
+ } from "../index-re3ntm60.js";
44
44
 
45
45
  // node_modules/ajv/dist/compile/codegen/code.js
46
46
  var require_code = __commonJS((exports) => {
@@ -0,0 +1,14 @@
1
+ // @bun
2
+ import {
3
+ getLogContext,
4
+ getLogContextFromId,
5
+ searchLogs,
6
+ tailLogs
7
+ } from "./index-rbrsvsyh.js";
8
+ import"./index-re3ntm60.js";
9
+ export {
10
+ tailLogs,
11
+ searchLogs,
12
+ getLogContextFromId,
13
+ getLogContext
14
+ };
@@ -6,7 +6,7 @@ import {
6
6
  setPageAuth,
7
7
  setRetentionPolicy,
8
8
  startScheduler
9
- } from "../index-wbsq8qjd.js";
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-yb8yd4j6.js";
54
- import"../index-g8dczzvv.js";
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/logs",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Log aggregation + browser script + headless page scanner + performance monitoring for AI agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
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: opts.project,
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, 500)
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,