@hasna/logs 0.3.23 → 0.3.24

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.
Files changed (47) hide show
  1. package/LICENSE +1 -2
  2. package/README.md +3 -22
  3. package/bun.lock +7 -14
  4. package/dist/cli/index.js +2 -2
  5. package/dist/{index-t97ttm0a.js → index-75dwghvv.js} +88 -6
  6. package/dist/{index-1f2ghyhm.js → index-g8f8kep6.js} +93 -8
  7. package/dist/{index-zmayq5kj.js → index-pf8hpweg.js} +2 -2
  8. package/dist/{index-2sbhn1ye.js → index-w24zm361.js} +2 -2
  9. package/dist/mcp/index.js +23801 -8266
  10. package/dist/server/index.js +2 -2
  11. package/package.json +4 -3
  12. package/sdk/package.json +4 -9
  13. package/src/cli/entrypoints.test.ts +1 -1
  14. package/src/db/index.ts +1 -1
  15. package/src/lib/cloud-sync.ts +167 -0
  16. package/src/lib/ingest.ts +3 -3
  17. package/src/lib/remote-storage.ts +45 -0
  18. package/src/mcp/index.ts +30 -34
  19. package/dist/export-yjaar93b.js +0 -10
  20. package/dist/health-9792c1rc.js +0 -8
  21. package/dist/health-egdb00st.js +0 -8
  22. package/dist/http-0wsh40x1.js +0 -1240
  23. package/dist/index-14dvwcf1.js +0 -45
  24. package/dist/index-4ba0sabv.js +0 -1241
  25. package/dist/index-4hj4sakk.js +0 -1241
  26. package/dist/index-5cj74qka.js +0 -10803
  27. package/dist/index-5qwba140.js +0 -1241
  28. package/dist/index-5tvnhvgr.js +0 -536
  29. package/dist/index-6y8pmes4.js +0 -45
  30. package/dist/index-6zrkek5y.js +0 -9454
  31. package/dist/index-7qhh666n.js +0 -1241
  32. package/dist/index-86j0hn03.js +0 -540
  33. package/dist/index-exeq2gs6.js +0 -79
  34. package/dist/index-fzmz9aqs.js +0 -1241
  35. package/dist/index-g8dczzvv.js +0 -30
  36. package/dist/index-hjzbctgt.js +0 -5868
  37. package/dist/index-rbrsvsyh.js +0 -88
  38. package/dist/index-vmr85wa1.js +0 -9579
  39. package/dist/index-wbsq8qjd.js +0 -1241
  40. package/dist/index-xjn8gam3.js +0 -39
  41. package/dist/index-yb8yd4j6.js +0 -39
  42. package/dist/jobs-02z4fzsn.js +0 -22
  43. package/dist/query-6s5gqkck.js +0 -15
  44. package/dist/query-shjjj67k.js +0 -14
  45. package/dist/query-tcg3bm9s.js +0 -14
  46. package/src/mcp/http.test.ts +0 -92
  47. package/src/mcp/http.ts +0 -135
package/LICENSE CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  Apache License
3
2
  Version 2.0, January 2004
4
3
  http://www.apache.org/licenses/
@@ -176,7 +175,7 @@
176
175
 
177
176
  END OF TERMS AND CONDITIONS
178
177
 
179
- Copyright 2026 Hasna, Inc.
178
+ Copyright 2026 hasna
180
179
 
181
180
  Licensed under the Apache License, Version 2.0 (the "License");
182
181
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -32,36 +32,17 @@ logs-mcp
32
32
 
33
33
  6 tools available.
34
34
 
35
- ## HTTP mode
36
-
37
- Run a shared Streamable HTTP MCP server (127.0.0.1 only):
38
-
39
- ```bash
40
- logs-mcp --http # default port 8820
41
- logs-mcp --http --port 8820
42
- MCP_HTTP=1 logs-mcp
43
- ```
44
-
45
- - Health: `GET http://127.0.0.1:8820/health`
46
- - MCP: `POST http://127.0.0.1:8820/mcp`
47
-
48
- Stdio remains the default when no `--http` flag is passed.
49
-
50
35
  ## REST API
51
36
 
52
37
  ```bash
53
38
  logs-serve
54
39
  ```
55
40
 
56
- ## Cloud Sync
41
+ ## Remote Sync
57
42
 
58
- This package supports cloud sync via `@hasna/cloud`:
43
+ Logs stores data locally in SQLite and can optionally push/pull service-owned tables to PostgreSQL, including AWS RDS:
59
44
 
60
- ```bash
61
- cloud setup
62
- cloud sync push --service logs
63
- cloud sync pull --service logs
64
- ```
45
+ Configure `HASNA_LOGS_CLOUD_DATABASE_URL` or `LOGS_CLOUD_DATABASE_URL`, then use the MCP tools `cloud_status`, `cloud_push`, `cloud_pull`, and `cloud_sync`.
65
46
 
66
47
  ## Data Directory
67
48
 
package/bun.lock CHANGED
@@ -5,12 +5,12 @@
5
5
  "": {
6
6
  "name": "open-logs",
7
7
  "dependencies": {
8
- "@hasna/cloud": "^0.1.24",
9
- "@modelcontextprotocol/sdk": "^1.29.0",
8
+ "@modelcontextprotocol/sdk": "^1.12.1",
10
9
  "commander": "^14.0.0",
11
10
  "hono": "^4.7.11",
12
11
  "ink": "^5.1.0",
13
12
  "node-cron": "^3.0.3",
13
+ "pg": "^8.20.0",
14
14
  "playwright": "^1.52.0",
15
15
  "react": "^19.1.0",
16
16
  },
@@ -18,6 +18,7 @@
18
18
  "@biomejs/biome": "^1.9.4",
19
19
  "@types/bun": "latest",
20
20
  "@types/node-cron": "^3.0.11",
21
+ "@types/pg": "^8.20.0",
21
22
  "@types/react": "^19.1.4",
22
23
  "typescript": "^5.9.3",
23
24
  },
@@ -44,11 +45,9 @@
44
45
 
45
46
  "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="],
46
47
 
47
- "@hasna/cloud": ["@hasna/cloud@0.1.30", "", { "dependencies": { "@modelcontextprotocol/sdk": "^1.12.1", "commander": "^13.1.0", "pg": "^8.13.3", "zod": "^3.24.2" }, "bin": { "cloud": "dist/cli/index.js", "cloud-mcp": "dist/mcp/index.js" } }, "sha512-qpDiJNsU/K1cFDk1l+xUA1xEEq8+Q37OdAz/ec0LwrQiA1HyWukzR8Lh7igkOqBT2foa46cxhB7yGnfiT2cWrA=="],
48
-
49
48
  "@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="],
50
49
 
51
- "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="],
50
+ "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="],
52
51
 
53
52
  "@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="],
54
53
 
@@ -56,6 +55,8 @@
56
55
 
57
56
  "@types/node-cron": ["@types/node-cron@3.0.11", "", {}, "sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg=="],
58
57
 
58
+ "@types/pg": ["@types/pg@8.20.0", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow=="],
59
+
59
60
  "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
60
61
 
61
62
  "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
@@ -356,20 +357,12 @@
356
357
 
357
358
  "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
358
359
 
359
- "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
360
+ "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
360
361
 
361
362
  "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
362
363
 
363
- "@hasna/cloud/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="],
364
-
365
- "@hasna/cloud/commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="],
366
-
367
- "@modelcontextprotocol/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
368
-
369
364
  "cli-truncate/slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="],
370
365
 
371
366
  "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
372
-
373
- "@hasna/cloud/@modelcontextprotocol/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
374
367
  }
375
368
  }
package/dist/cli/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // @bun
3
3
  import {
4
4
  runJob
5
- } from "../index-zmayq5kj.js";
5
+ } from "../index-pf8hpweg.js";
6
6
  import {
7
7
  PACKAGE_VERSION,
8
8
  createPage,
@@ -13,7 +13,7 @@ import {
13
13
  listProjects,
14
14
  resolveProjectId,
15
15
  summarizeLogs
16
- } from "../index-5cj74qka.js";
16
+ } from "../index-g8f8kep6.js";
17
17
  import {
18
18
  createJob,
19
19
  listJobs
@@ -6,7 +6,7 @@ import {
6
6
  // src/db/index.ts
7
7
  import { Database } from "bun:sqlite";
8
8
  import { join } from "path";
9
- import { existsSync, mkdirSync } from "fs";
9
+ import { existsSync, mkdirSync, cpSync } from "fs";
10
10
 
11
11
  // src/db/migrations/001_alert_rules.ts
12
12
  function migrateAlertRules(db) {
@@ -80,8 +80,21 @@ function migratePageAuth(db) {
80
80
  }
81
81
 
82
82
  // src/db/index.ts
83
- var DATA_DIR = process.env.LOGS_DATA_DIR ?? join(process.env.HOME ?? "~", ".logs");
84
- var DB_PATH = process.env.LOGS_DB_PATH ?? join(DATA_DIR, "logs.db");
83
+ function resolveDataDir() {
84
+ const explicit = process.env.HASNA_LOGS_DATA_DIR ?? process.env.LOGS_DATA_DIR;
85
+ if (explicit)
86
+ return explicit;
87
+ const home = process.env.HOME ?? "~";
88
+ const newDir = join(home, ".hasna", "logs");
89
+ const oldDir = join(home, ".logs");
90
+ if (!existsSync(newDir) && existsSync(oldDir)) {
91
+ mkdirSync(join(home, ".hasna"), { recursive: true });
92
+ cpSync(oldDir, newDir, { recursive: true });
93
+ }
94
+ return newDir;
95
+ }
96
+ var DATA_DIR = resolveDataDir();
97
+ var DB_PATH = process.env.HASNA_LOGS_DB_PATH ?? process.env.LOGS_DB_PATH ?? join(DATA_DIR, "logs.db");
85
98
  var _db = null;
86
99
  function getDb() {
87
100
  if (_db)
@@ -92,6 +105,15 @@ function getDb() {
92
105
  _db.run("PRAGMA journal_mode=WAL");
93
106
  _db.run("PRAGMA foreign_keys=ON");
94
107
  migrate(_db);
108
+ _db.run(`CREATE TABLE IF NOT EXISTS feedback (
109
+ id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
110
+ message TEXT NOT NULL,
111
+ email TEXT,
112
+ category TEXT DEFAULT 'general',
113
+ version TEXT,
114
+ machine_id TEXT,
115
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
116
+ )`);
95
117
  return _db;
96
118
  }
97
119
  function migrate(db) {
@@ -383,7 +405,7 @@ function ingestBatch(db, entries, sharedTraceId) {
383
405
  VALUES ($project_id, $page_id, $level, $source, $service, $message, $trace_id, $session_id, $agent, $url, $stack_trace, $metadata)
384
406
  RETURNING *
385
407
  `);
386
- const tx = db.transaction((items) => items.map((entry) => insert.get({
408
+ const rows = db.transaction(() => entries.map((entry) => insert.get({
387
409
  $project_id: entry.project_id ?? null,
388
410
  $page_id: entry.page_id ?? null,
389
411
  $level: entry.level,
@@ -397,7 +419,6 @@ function ingestBatch(db, entries, sharedTraceId) {
397
419
  $stack_trace: entry.stack_trace ?? null,
398
420
  $metadata: entry.metadata ? JSON.stringify(entry.metadata) : null
399
421
  })));
400
- const rows = tx(entries);
401
422
  for (const entry of entries) {
402
423
  if (ERROR_LEVELS.has(entry.level) && entry.project_id) {
403
424
  upsertIssue(db, { project_id: entry.project_id, level: entry.level, service: entry.service, message: entry.message, stack_trace: entry.stack_trace });
@@ -406,6 +427,67 @@ function ingestBatch(db, entries, sharedTraceId) {
406
427
  return rows;
407
428
  }
408
429
 
430
+ // src/lib/package-meta.ts
431
+ import { existsSync as existsSync2, readFileSync } from "fs";
432
+ var PACKAGE_JSON_CANDIDATES = [
433
+ "../../package.json",
434
+ "../package.json",
435
+ "./package.json"
436
+ ];
437
+ function readPackageJson(baseUrl = import.meta.url) {
438
+ for (const relativePath of PACKAGE_JSON_CANDIDATES) {
439
+ const candidate = new URL(relativePath, baseUrl);
440
+ if (!existsSync2(candidate))
441
+ continue;
442
+ return JSON.parse(readFileSync(candidate, "utf8"));
443
+ }
444
+ throw new Error(`Unable to locate package.json from ${String(baseUrl)}`);
445
+ }
446
+ function readPackageVersion(baseUrl = import.meta.url) {
447
+ return readPackageJson(baseUrl).version ?? "0.0.0";
448
+ }
449
+ var PACKAGE_VERSION = readPackageVersion();
450
+ function exitIfMetadataRequest(spec, argv = process.argv.slice(2)) {
451
+ if (argv.includes("--version") || argv.includes("-V")) {
452
+ console.log(PACKAGE_VERSION);
453
+ process.exit(0);
454
+ }
455
+ if (argv.includes("--help") || argv.includes("-h")) {
456
+ const options = spec.options ?? [];
457
+ const renderedOptions = [
458
+ " -V, --version output the version number",
459
+ " -h, --help display help for command",
460
+ ...options
461
+ ];
462
+ console.log([
463
+ `Usage: ${spec.name} [options]`,
464
+ "",
465
+ spec.description,
466
+ "",
467
+ "Options:",
468
+ ...renderedOptions
469
+ ].join(`
470
+ `));
471
+ process.exit(0);
472
+ }
473
+ }
474
+ function readOptionValue(names, argv = process.argv.slice(2)) {
475
+ for (let index = 0;index < argv.length; index += 1) {
476
+ const arg = argv[index];
477
+ if (!arg)
478
+ continue;
479
+ const inline = names.find((name) => arg.startsWith(`${name}=`));
480
+ if (inline)
481
+ return arg.slice(inline.length + 1);
482
+ if (names.includes(arg)) {
483
+ const next = argv[index + 1];
484
+ if (next && !next.startsWith("-"))
485
+ return next;
486
+ }
487
+ }
488
+ return;
489
+ }
490
+
409
491
  // src/lib/summarize.ts
410
492
  function summarizeLogs(db, projectId, since, until) {
411
493
  const conditions = ["level IN ('warn','error','fatal')"];
@@ -540,4 +622,4 @@ function scoreLabel(score) {
540
622
  return "red";
541
623
  }
542
624
 
543
- export { getDb, listIssues, getIssue, updateIssueStatus, createAlertRule, listAlertRules, updateAlertRule, deleteAlertRule, ingestLog, ingestBatch, summarizeLogs, createProject, listProjects, getProject, updateProject, createPage, listPages, getPage, resolveProjectId, touchPage, saveSnapshot, getLatestSnapshot, getPerfTrend, scoreLabel };
625
+ export { getDb, listIssues, getIssue, updateIssueStatus, createAlertRule, listAlertRules, updateAlertRule, deleteAlertRule, ingestLog, ingestBatch, PACKAGE_VERSION, exitIfMetadataRequest, readOptionValue, summarizeLogs, createProject, listProjects, getProject, updateProject, createPage, listPages, getPage, resolveProjectId, touchPage, saveSnapshot, getLatestSnapshot, getPerfTrend, scoreLabel };
@@ -6,7 +6,7 @@ import {
6
6
  // src/db/index.ts
7
7
  import { Database } from "bun:sqlite";
8
8
  import { join } from "path";
9
- import { existsSync, mkdirSync } from "fs";
9
+ import { existsSync, mkdirSync, cpSync } from "fs";
10
10
 
11
11
  // src/db/migrations/001_alert_rules.ts
12
12
  function migrateAlertRules(db) {
@@ -80,8 +80,21 @@ function migratePageAuth(db) {
80
80
  }
81
81
 
82
82
  // src/db/index.ts
83
- var DATA_DIR = process.env.LOGS_DATA_DIR ?? join(process.env.HOME ?? "~", ".logs");
84
- var DB_PATH = process.env.LOGS_DB_PATH ?? join(DATA_DIR, "logs.db");
83
+ function resolveDataDir() {
84
+ const explicit = process.env.HASNA_LOGS_DATA_DIR ?? process.env.LOGS_DATA_DIR;
85
+ if (explicit)
86
+ return explicit;
87
+ const home = process.env.HOME ?? "~";
88
+ const newDir = join(home, ".hasna", "logs");
89
+ const oldDir = join(home, ".logs");
90
+ if (!existsSync(newDir) && existsSync(oldDir)) {
91
+ mkdirSync(join(home, ".hasna"), { recursive: true });
92
+ cpSync(oldDir, newDir, { recursive: true });
93
+ }
94
+ return newDir;
95
+ }
96
+ var DATA_DIR = resolveDataDir();
97
+ var DB_PATH = process.env.HASNA_LOGS_DB_PATH ?? process.env.LOGS_DB_PATH ?? join(DATA_DIR, "logs.db");
85
98
  var _db = null;
86
99
  function getDb() {
87
100
  if (_db)
@@ -92,6 +105,15 @@ function getDb() {
92
105
  _db.run("PRAGMA journal_mode=WAL");
93
106
  _db.run("PRAGMA foreign_keys=ON");
94
107
  migrate(_db);
108
+ _db.run(`CREATE TABLE IF NOT EXISTS feedback (
109
+ id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
110
+ message TEXT NOT NULL,
111
+ email TEXT,
112
+ category TEXT DEFAULT 'general',
113
+ version TEXT,
114
+ machine_id TEXT,
115
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
116
+ )`);
95
117
  return _db;
96
118
  }
97
119
  function migrate(db) {
@@ -374,13 +396,16 @@ function ingestLog(db, entry) {
374
396
  }
375
397
  return row;
376
398
  }
377
- function ingestBatch(db, entries) {
399
+ function ingestBatch(db, entries, sharedTraceId) {
400
+ if (sharedTraceId) {
401
+ entries = entries.map((e) => e.trace_id ? e : { ...e, trace_id: sharedTraceId });
402
+ }
378
403
  const insert = db.prepare(`
379
404
  INSERT INTO logs (project_id, page_id, level, source, service, message, trace_id, session_id, agent, url, stack_trace, metadata)
380
405
  VALUES ($project_id, $page_id, $level, $source, $service, $message, $trace_id, $session_id, $agent, $url, $stack_trace, $metadata)
381
406
  RETURNING *
382
407
  `);
383
- const tx = db.transaction((items) => items.map((entry) => insert.get({
408
+ const rows = db.transaction(() => entries.map((entry) => insert.get({
384
409
  $project_id: entry.project_id ?? null,
385
410
  $page_id: entry.page_id ?? null,
386
411
  $level: entry.level,
@@ -393,8 +418,7 @@ function ingestBatch(db, entries) {
393
418
  $url: entry.url ?? null,
394
419
  $stack_trace: entry.stack_trace ?? null,
395
420
  $metadata: entry.metadata ? JSON.stringify(entry.metadata) : null
396
- })));
397
- const rows = tx(entries);
421
+ })))();
398
422
  for (const entry of entries) {
399
423
  if (ERROR_LEVELS.has(entry.level) && entry.project_id) {
400
424
  upsertIssue(db, { project_id: entry.project_id, level: entry.level, service: entry.service, message: entry.message, stack_trace: entry.stack_trace });
@@ -403,6 +427,67 @@ function ingestBatch(db, entries) {
403
427
  return rows;
404
428
  }
405
429
 
430
+ // src/lib/package-meta.ts
431
+ import { existsSync as existsSync2, readFileSync } from "fs";
432
+ var PACKAGE_JSON_CANDIDATES = [
433
+ "../../package.json",
434
+ "../package.json",
435
+ "./package.json"
436
+ ];
437
+ function readPackageJson(baseUrl = import.meta.url) {
438
+ for (const relativePath of PACKAGE_JSON_CANDIDATES) {
439
+ const candidate = new URL(relativePath, baseUrl);
440
+ if (!existsSync2(candidate))
441
+ continue;
442
+ return JSON.parse(readFileSync(candidate, "utf8"));
443
+ }
444
+ throw new Error(`Unable to locate package.json from ${String(baseUrl)}`);
445
+ }
446
+ function readPackageVersion(baseUrl = import.meta.url) {
447
+ return readPackageJson(baseUrl).version ?? "0.0.0";
448
+ }
449
+ var PACKAGE_VERSION = readPackageVersion();
450
+ function exitIfMetadataRequest(spec, argv = process.argv.slice(2)) {
451
+ if (argv.includes("--version") || argv.includes("-V")) {
452
+ console.log(PACKAGE_VERSION);
453
+ process.exit(0);
454
+ }
455
+ if (argv.includes("--help") || argv.includes("-h")) {
456
+ const options = spec.options ?? [];
457
+ const renderedOptions = [
458
+ " -V, --version output the version number",
459
+ " -h, --help display help for command",
460
+ ...options
461
+ ];
462
+ console.log([
463
+ `Usage: ${spec.name} [options]`,
464
+ "",
465
+ spec.description,
466
+ "",
467
+ "Options:",
468
+ ...renderedOptions
469
+ ].join(`
470
+ `));
471
+ process.exit(0);
472
+ }
473
+ }
474
+ function readOptionValue(names, argv = process.argv.slice(2)) {
475
+ for (let index = 0;index < argv.length; index += 1) {
476
+ const arg = argv[index];
477
+ if (!arg)
478
+ continue;
479
+ const inline = names.find((name) => arg.startsWith(`${name}=`));
480
+ if (inline)
481
+ return arg.slice(inline.length + 1);
482
+ if (names.includes(arg)) {
483
+ const next = argv[index + 1];
484
+ if (next && !next.startsWith("-"))
485
+ return next;
486
+ }
487
+ }
488
+ return;
489
+ }
490
+
406
491
  // src/lib/summarize.ts
407
492
  function summarizeLogs(db, projectId, since, until) {
408
493
  const conditions = ["level IN ('warn','error','fatal')"];
@@ -537,4 +622,4 @@ function scoreLabel(score) {
537
622
  return "red";
538
623
  }
539
624
 
540
- export { getDb, listIssues, getIssue, updateIssueStatus, createAlertRule, listAlertRules, updateAlertRule, deleteAlertRule, ingestLog, ingestBatch, summarizeLogs, createProject, listProjects, getProject, updateProject, createPage, listPages, getPage, resolveProjectId, touchPage, saveSnapshot, getLatestSnapshot, getPerfTrend, scoreLabel };
625
+ export { getDb, listIssues, getIssue, updateIssueStatus, createAlertRule, listAlertRules, updateAlertRule, deleteAlertRule, ingestLog, ingestBatch, PACKAGE_VERSION, exitIfMetadataRequest, readOptionValue, summarizeLogs, createProject, listProjects, getProject, updateProject, createPage, listPages, getPage, resolveProjectId, touchPage, saveSnapshot, getLatestSnapshot, getPerfTrend, scoreLabel };
@@ -5,7 +5,7 @@ import {
5
5
  listPages,
6
6
  saveSnapshot,
7
7
  touchPage
8
- } from "./index-5cj74qka.js";
8
+ } from "./index-g8f8kep6.js";
9
9
  import {
10
10
  createScanRun,
11
11
  finishScanRun,
@@ -894,7 +894,7 @@ var require_scheduled_task = __commonJS((exports, module) => {
894
894
 
895
895
  // node_modules/node-cron/src/background-scheduled-task/index.js
896
896
  var require_background_scheduled_task = __commonJS((exports, module) => {
897
- var __dirname = "/Users/hasna/Workspace/hasna/opensource/open-logs/node_modules/node-cron/src/background-scheduled-task";
897
+ var __dirname = "/home/hasna/workspace/hasna/opensource/open-logs/node_modules/node-cron/src/background-scheduled-task";
898
898
  var EventEmitter = __require("events");
899
899
  var path = __require("path");
900
900
  var { fork } = __require("child_process");
@@ -5,7 +5,7 @@ import {
5
5
  listPages,
6
6
  saveSnapshot,
7
7
  touchPage
8
- } from "./index-t97ttm0a.js";
8
+ } from "./index-75dwghvv.js";
9
9
  import {
10
10
  createScanRun,
11
11
  finishScanRun,
@@ -894,7 +894,7 @@ var require_scheduled_task = __commonJS((exports, module) => {
894
894
 
895
895
  // node_modules/node-cron/src/background-scheduled-task/index.js
896
896
  var require_background_scheduled_task = __commonJS((exports, module) => {
897
- var __dirname = "/Users/hasna/Workspace/hasna/opensource/opensourcedev/open-logs/node_modules/node-cron/src/background-scheduled-task";
897
+ var __dirname = "/home/hasna/workspace/hasna/opensource/open-logs/node_modules/node-cron/src/background-scheduled-task";
898
898
  var EventEmitter = __require("events");
899
899
  var path = __require("path");
900
900
  var { fork } = __require("child_process");