@hasna/logs 0.3.18 → 0.3.19

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/mcp/index.js CHANGED
@@ -4,10 +4,12 @@ import {
4
4
  getHealth
5
5
  } from "../index-cpvq9np9.js";
6
6
  import {
7
+ PACKAGE_VERSION,
7
8
  createAlertRule,
8
9
  createPage,
9
10
  createProject,
10
11
  deleteAlertRule,
12
+ exitIfMetadataRequest,
11
13
  getDb,
12
14
  getLatestSnapshot,
13
15
  getPerfTrend,
@@ -22,7 +24,7 @@ import {
22
24
  scoreLabel,
23
25
  summarizeLogs,
24
26
  updateIssueStatus
25
- } from "../index-hwabsrfh.js";
27
+ } from "../index-9n6bpjxf.js";
26
28
  import {
27
29
  createJob,
28
30
  listJobs
@@ -6524,69 +6526,6 @@ var require_dist = __commonJS((exports, module) => {
6524
6526
  exports.default = formatsPlugin;
6525
6527
  });
6526
6528
 
6527
- // package.json
6528
- var require_package = __commonJS((exports, module) => {
6529
- module.exports = {
6530
- name: "@hasna/logs",
6531
- version: "0.3.18",
6532
- description: "Log aggregation + browser script + headless page scanner + performance monitoring for AI agents",
6533
- type: "module",
6534
- main: "./dist/index.js",
6535
- types: "./dist/index.d.ts",
6536
- bin: {
6537
- logs: "./dist/cli/index.js",
6538
- "logs-mcp": "./dist/mcp/index.js",
6539
- "logs-serve": "./dist/server/index.js"
6540
- },
6541
- scripts: {
6542
- build: "bun build src/cli/index.ts src/mcp/index.ts src/server/index.ts --outdir dist --target bun --splitting --external playwright --external playwright-core --external electron --external chromium-bidi --external lighthouse",
6543
- "build:dashboard": "cd dashboard && bun run build",
6544
- "build:all": "bun run build:dashboard && bun run build",
6545
- dev: "bun run src/server/index.ts",
6546
- test: "bun test",
6547
- "test:coverage": "bun test --coverage",
6548
- lint: "biome check src/",
6549
- postinstall: "mkdir -p $HOME/.hasna/logs 2>/dev/null || true"
6550
- },
6551
- repository: {
6552
- type: "git",
6553
- url: "https://github.com/hasna/logs.git"
6554
- },
6555
- publishConfig: {
6556
- registry: "https://registry.npmjs.org",
6557
- access: "public"
6558
- },
6559
- keywords: [
6560
- "logs",
6561
- "monitoring",
6562
- "mcp",
6563
- "ai-agents",
6564
- "sentry",
6565
- "performance",
6566
- "lighthouse"
6567
- ],
6568
- author: "Andrei Hasna <andrei@hasna.com>",
6569
- license: "Apache-2.0",
6570
- dependencies: {
6571
- "@hasna/cloud": "^0.1.24",
6572
- "@modelcontextprotocol/sdk": "^1.12.1",
6573
- commander: "^14.0.0",
6574
- hono: "^4.7.11",
6575
- ink: "^5.1.0",
6576
- "node-cron": "^3.0.3",
6577
- playwright: "^1.52.0",
6578
- react: "^19.1.0"
6579
- },
6580
- devDependencies: {
6581
- "@biomejs/biome": "^1.9.4",
6582
- "@types/bun": "latest",
6583
- "@types/node-cron": "^3.0.11",
6584
- "@types/react": "^19.1.4",
6585
- typescript: "^5.9.3"
6586
- }
6587
- };
6588
- });
6589
-
6590
6529
  // node_modules/@modelcontextprotocol/sdk/node_modules/zod/v3/helpers/util.js
6591
6530
  var util;
6592
6531
  (function(util2) {
@@ -23996,8 +23935,12 @@ async function getSessionContext(db, sessionId) {
23996
23935
  }
23997
23936
 
23998
23937
  // src/mcp/index.ts
23938
+ exitIfMetadataRequest({
23939
+ name: "logs-mcp",
23940
+ description: "Start the @hasna/logs MCP server over stdio."
23941
+ });
23999
23942
  var db = getDb();
24000
- var server = new McpServer({ name: "logs", version: "0.3.0" });
23943
+ var server = new McpServer({ name: "logs", version: PACKAGE_VERSION });
24001
23944
  var _logsAgents = new Map;
24002
23945
  function applyBrief(rows, brief = true) {
24003
23946
  if (!brief)
@@ -24326,12 +24269,11 @@ server.tool("send_feedback", "Send feedback about this service", {
24326
24269
  category: exports_external.enum(["bug", "feature", "general"]).optional()
24327
24270
  }, async (params) => {
24328
24271
  try {
24329
- const pkg = require_package();
24330
24272
  db.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", [
24331
24273
  params.message,
24332
24274
  params.email || null,
24333
24275
  params.category || "general",
24334
- pkg.version
24276
+ PACKAGE_VERSION
24335
24277
  ]);
24336
24278
  return { content: [{ type: "text", text: "Feedback saved. Thank you!" }] };
24337
24279
  } catch (e) {
@@ -6,7 +6,7 @@ import {
6
6
  setPageAuth,
7
7
  setRetentionPolicy,
8
8
  startScheduler
9
- } from "../index-8pwbytc6.js";
9
+ } from "../index-sgg59p1t.js";
10
10
  import {
11
11
  getHealth
12
12
  } from "../index-cpvq9np9.js";
@@ -15,6 +15,7 @@ import {
15
15
  createPage,
16
16
  createProject,
17
17
  deleteAlertRule,
18
+ exitIfMetadataRequest,
18
19
  getDb,
19
20
  getIssue,
20
21
  getLatestSnapshot,
@@ -26,12 +27,13 @@ import {
26
27
  listIssues,
27
28
  listPages,
28
29
  listProjects,
30
+ readOptionValue,
29
31
  resolveProjectId,
30
32
  summarizeLogs,
31
33
  updateAlertRule,
32
34
  updateIssueStatus,
33
35
  updateProject
34
- } from "../index-hwabsrfh.js";
36
+ } from "../index-9n6bpjxf.js";
35
37
  import {
36
38
  createJob,
37
39
  deleteJob,
@@ -2442,7 +2444,13 @@ function streamRoutes(db) {
2442
2444
  }
2443
2445
 
2444
2446
  // src/server/index.ts
2445
- var PORT = Number(process.env.LOGS_PORT ?? 3460);
2447
+ exitIfMetadataRequest({
2448
+ name: "logs-serve",
2449
+ description: "Start the @hasna/logs REST API server.",
2450
+ options: [" -p, --port <n> Port to listen on (default: LOGS_PORT or 3460)"]
2451
+ });
2452
+ var portArg = readOptionValue(["--port", "-p"]);
2453
+ var PORT = Number(portArg ?? process.env.LOGS_PORT ?? 3460);
2446
2454
  var db = getDb();
2447
2455
  var app = new Hono2;
2448
2456
  app.use("*", cors());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/logs",
3
- "version": "0.3.18",
3
+ "version": "0.3.19",
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",
@@ -0,0 +1,63 @@
1
+ import { expect, test } from "bun:test"
2
+ import { readFileSync } from "node:fs"
3
+ import { fileURLToPath } from "node:url"
4
+
5
+ const packageJson = JSON.parse(
6
+ readFileSync(new URL("../../package.json", import.meta.url), "utf8"),
7
+ ) as { version: string }
8
+
9
+ async function runEntrypoint(entryRelativePath: string, args: string[]) {
10
+ const entry = fileURLToPath(new URL(entryRelativePath, import.meta.url))
11
+ const proc = Bun.spawn(["bun", entry, ...args], {
12
+ env: {
13
+ ...process.env,
14
+ LOGS_PORT: "0",
15
+ },
16
+ stdout: "pipe",
17
+ stderr: "pipe",
18
+ })
19
+
20
+ const timeout = new Promise<never>((_, reject) => {
21
+ const timer = setTimeout(() => {
22
+ proc.kill()
23
+ reject(new Error(`Timed out running ${entryRelativePath} ${args.join(" ")}`.trim()))
24
+ }, 2000)
25
+
26
+ proc.exited.finally(() => clearTimeout(timer))
27
+ })
28
+
29
+ const exitCode = await Promise.race([proc.exited, timeout])
30
+ const stdout = await new Response(proc.stdout).text()
31
+ const stderr = await new Response(proc.stderr).text()
32
+
33
+ return { exitCode, stdout, stderr }
34
+ }
35
+
36
+ test("logs --version matches package.json", async () => {
37
+ const result = await runEntrypoint("./index.ts", ["--version"])
38
+
39
+ expect(result.exitCode).toBe(0)
40
+ expect(result.stdout.trim()).toBe(packageJson.version)
41
+ expect(result.stderr.trim()).toBe("")
42
+ })
43
+
44
+ test("logs-mcp --help prints usage and exits without starting stdio transport", async () => {
45
+ const result = await runEntrypoint("../mcp/index.ts", ["--help"])
46
+
47
+ expect(result.exitCode).toBe(0)
48
+ expect(result.stdout).toContain("Usage: logs-mcp [options]")
49
+ expect(result.stdout).toContain("Start the @hasna/logs MCP server over stdio.")
50
+ expect(result.stdout).not.toContain("Listening")
51
+ expect(result.stderr.trim()).toBe("")
52
+ })
53
+
54
+ test("logs-serve --help prints usage and exits without starting the server", async () => {
55
+ const result = await runEntrypoint("../server/index.ts", ["--help"])
56
+
57
+ expect(result.exitCode).toBe(0)
58
+ expect(result.stdout).toContain("Usage: logs-serve [options]")
59
+ expect(result.stdout).toContain("Start the @hasna/logs REST API server.")
60
+ expect(result.stdout).not.toContain("server running")
61
+ expect(result.stdout).not.toContain("Scheduler started")
62
+ expect(result.stderr.trim()).toBe("")
63
+ })
package/src/cli/index.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  import { Command } from "commander"
3
3
  import { getDb } from "../db/index.ts"
4
4
  import { ingestLog } from "../lib/ingest.ts"
5
+ import { PACKAGE_VERSION } from "../lib/package-meta.ts"
5
6
  import { searchLogs, tailLogs } from "../lib/query.ts"
6
7
  import { summarizeLogs } from "../lib/summarize.ts"
7
8
  import { createJob, listJobs } from "../lib/jobs.ts"
@@ -39,7 +40,7 @@ function resolveProject(nameOrId: string | undefined): string | undefined {
39
40
  const program = new Command()
40
41
  .name("logs")
41
42
  .description("@hasna/logs — log aggregation and monitoring")
42
- .version("0.0.1")
43
+ .version(PACKAGE_VERSION)
43
44
 
44
45
  // ── logs list ──────────────────────────────────────────────
45
46
  program.command("list")
@@ -0,0 +1,62 @@
1
+ import { readFileSync } from "node:fs"
2
+
3
+ type StandaloneCliSpec = {
4
+ name: string
5
+ description: string
6
+ options?: string[]
7
+ }
8
+
9
+ type PackageJson = {
10
+ version?: string
11
+ }
12
+
13
+ const packageJson = JSON.parse(
14
+ readFileSync(new URL("../../package.json", import.meta.url), "utf8"),
15
+ ) as PackageJson
16
+
17
+ export const PACKAGE_VERSION = packageJson.version ?? "0.0.0"
18
+
19
+ export function exitIfMetadataRequest(spec: StandaloneCliSpec, argv = process.argv.slice(2)): void {
20
+ if (argv.includes("--version") || argv.includes("-V")) {
21
+ console.log(PACKAGE_VERSION)
22
+ process.exit(0)
23
+ }
24
+
25
+ if (argv.includes("--help") || argv.includes("-h")) {
26
+ const options = spec.options ?? []
27
+ const renderedOptions = [
28
+ " -V, --version output the version number",
29
+ " -h, --help display help for command",
30
+ ...options,
31
+ ]
32
+
33
+ console.log(
34
+ [
35
+ `Usage: ${spec.name} [options]`,
36
+ "",
37
+ spec.description,
38
+ "",
39
+ "Options:",
40
+ ...renderedOptions,
41
+ ].join("\n"),
42
+ )
43
+ process.exit(0)
44
+ }
45
+ }
46
+
47
+ export function readOptionValue(names: string[], argv = process.argv.slice(2)): string | undefined {
48
+ for (let index = 0; index < argv.length; index += 1) {
49
+ const arg = argv[index]
50
+ if (!arg) continue
51
+
52
+ const inline = names.find((name) => arg.startsWith(`${name}=`))
53
+ if (inline) return arg.slice(inline.length + 1)
54
+
55
+ if (names.includes(arg)) {
56
+ const next = argv[index + 1]
57
+ if (next && !next.startsWith("-")) return next
58
+ }
59
+ }
60
+
61
+ return undefined
62
+ }
package/src/mcp/index.ts CHANGED
@@ -4,6 +4,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
4
4
  import { registerCloudTools } from "@hasna/cloud"
5
5
  import { z } from "zod"
6
6
  import { getDb } from "../db/index.ts"
7
+ import { exitIfMetadataRequest, PACKAGE_VERSION } from "../lib/package-meta.ts"
7
8
  import { ingestBatch, ingestLog } from "../lib/ingest.ts"
8
9
  import { getLogContext, getLogContextFromId, searchLogs, tailLogs } from "../lib/query.ts"
9
10
  import { summarizeLogs } from "../lib/summarize.ts"
@@ -21,8 +22,13 @@ import { getSessionContext } from "../lib/session-context.ts"
21
22
  import { parseTime } from "../lib/parse-time.ts"
22
23
  import type { LogLevel, LogRow } from "../types/index.ts"
23
24
 
25
+ exitIfMetadataRequest({
26
+ name: "logs-mcp",
27
+ description: "Start the @hasna/logs MCP server over stdio.",
28
+ })
29
+
24
30
  const db = getDb()
25
- const server = new McpServer({ name: "logs", version: "0.3.0" })
31
+ const server = new McpServer({ name: "logs", version: PACKAGE_VERSION })
26
32
 
27
33
  // --- in-memory agent registry ---
28
34
  interface _LogsAgent { id: string; name: string; session_id?: string; last_seen_at: string; project_id?: string }
@@ -355,9 +361,8 @@ server.tool(
355
361
  },
356
362
  async (params) => {
357
363
  try {
358
- const pkg = require("../../package.json")
359
364
  db.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", [
360
- params.message, params.email || null, params.category || "general", pkg.version,
365
+ params.message, params.email || null, params.category || "general", PACKAGE_VERSION,
361
366
  ])
362
367
  return { content: [{ type: "text" as const, text: "Feedback saved. Thank you!" }] }
363
368
  } catch (e) {
@@ -5,6 +5,7 @@ import { serveStatic } from "hono/bun"
5
5
  import { getDb } from "../db/index.ts"
6
6
  import { getBrowserScript } from "../lib/browser-script.ts"
7
7
  import { getHealth } from "../lib/health.ts"
8
+ import { exitIfMetadataRequest, readOptionValue } from "../lib/package-meta.ts"
8
9
  import { startScheduler } from "../lib/scheduler.ts"
9
10
  import { alertsRoutes } from "./routes/alerts.ts"
10
11
  import { issuesRoutes } from "./routes/issues.ts"
@@ -14,7 +15,14 @@ import { perfRoutes } from "./routes/perf.ts"
14
15
  import { projectsRoutes } from "./routes/projects.ts"
15
16
  import { streamRoutes } from "./routes/stream.ts"
16
17
 
17
- const PORT = Number(process.env.LOGS_PORT ?? 3460)
18
+ exitIfMetadataRequest({
19
+ name: "logs-serve",
20
+ description: "Start the @hasna/logs REST API server.",
21
+ options: [" -p, --port <n> Port to listen on (default: LOGS_PORT or 3460)"],
22
+ })
23
+
24
+ const portArg = readOptionValue(["--port", "-p"])
25
+ const PORT = Number(portArg ?? process.env.LOGS_PORT ?? 3460)
18
26
  const db = getDb()
19
27
  const app = new Hono()
20
28