@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/cli/index.js +4 -3
- package/dist/index-9n6bpjxf.js +10787 -0
- package/dist/index-sgg59p1t.js +1241 -0
- package/dist/mcp/index.js +9 -67
- package/dist/server/index.js +11 -3
- package/package.json +1 -1
- package/src/cli/entrypoints.test.ts +63 -0
- package/src/cli/index.ts +2 -1
- package/src/lib/package-meta.ts +62 -0
- package/src/mcp/index.ts +8 -3
- package/src/server/index.ts +9 -1
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-
|
|
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:
|
|
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
|
-
|
|
24276
|
+
PACKAGE_VERSION
|
|
24335
24277
|
]);
|
|
24336
24278
|
return { content: [{ type: "text", text: "Feedback saved. Thank you!" }] };
|
|
24337
24279
|
} catch (e) {
|
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-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-
|
|
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
|
-
|
|
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
|
@@ -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(
|
|
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:
|
|
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",
|
|
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) {
|
package/src/server/index.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|