@loghead/core 0.1.32 → 0.1.33

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.
@@ -1,33 +1,29 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.startApiServer = startApiServer;
7
- const express_1 = __importDefault(require("express"));
8
- const cors_1 = __importDefault(require("cors"));
9
- const path_1 = __importDefault(require("path"));
10
- const auth_1 = require("../services/auth");
11
- const chalk_1 = __importDefault(require("chalk"));
12
- const auth = new auth_1.AuthService();
13
- async function startApiServer(db) {
14
- const app = (0, express_1.default)();
1
+ import express from "express";
2
+ import cors from "cors";
3
+ import path from "path";
4
+ import fs from "fs";
5
+ import { fileURLToPath } from "url";
6
+ import chalk from "chalk";
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ export async function startApiServer(db, auth) {
10
+ const app = express();
15
11
  const port = process.env.PORT || 4567;
16
- app.use((0, cors_1.default)());
17
- app.use(express_1.default.json());
12
+ app.use(cors());
13
+ app.use(express.json());
18
14
  // Serve static frontend files
19
15
  // Determine path based on whether we are running in src (dev) or dist (prod)
20
- let publicPath = path_1.default.join(__dirname, "../public");
21
- if (!require("fs").existsSync(publicPath)) {
16
+ let publicPath = path.join(__dirname, "../public");
17
+ if (!fs.existsSync(publicPath)) {
22
18
  // Try looking in dist/public if we are in src
23
- publicPath = path_1.default.join(__dirname, "../../dist/public");
19
+ publicPath = path.join(__dirname, "../../dist/public");
24
20
  }
25
- if (require("fs").existsSync(publicPath)) {
26
- console.log(chalk_1.default.blue(`Serving frontend from: ${publicPath}`));
27
- app.use(express_1.default.static(publicPath));
21
+ if (fs.existsSync(publicPath)) {
22
+ console.log(chalk.blue(`Serving frontend from: ${publicPath}`));
23
+ app.use(express.static(publicPath));
28
24
  }
29
25
  else {
30
- console.warn(chalk_1.default.yellow("Frontend build not found. Run 'npm run build' in packages/core/frontend to build the UI."));
26
+ console.warn(chalk.yellow("Frontend build not found. Run 'npm run build' in packages/core/frontend to build the UI."));
31
27
  }
32
28
  await auth.initialize();
33
29
  // console.log(chalk.bold.green(`\nšŸ’» API server running on:`));
@@ -195,35 +191,35 @@ async function startApiServer(db) {
195
191
  res.status(500).json({ error: String(e) });
196
192
  }
197
193
  });
198
- app.get("/api/projects", (req, res) => {
199
- const projects = db.listProjects();
194
+ app.get("/api/projects", async (req, res) => {
195
+ const projects = await db.listProjects();
200
196
  res.json(projects);
201
197
  });
202
- app.post("/api/projects", (req, res) => {
198
+ app.post("/api/projects", async (req, res) => {
203
199
  const { name } = req.body;
204
200
  if (!name)
205
201
  return res.status(400).json({ error: "Name required" });
206
- const project = db.createProject(name);
202
+ const project = await db.createProject(name);
207
203
  res.json(project);
208
204
  });
209
- app.delete("/api/projects/:id", (req, res) => {
205
+ app.delete("/api/projects/:id", async (req, res) => {
210
206
  const { id } = req.params;
211
- db.deleteProject(id);
207
+ await db.deleteProject(id);
212
208
  res.json({ success: true });
213
209
  });
214
- app.get("/api/streams", (req, res) => {
210
+ app.get("/api/streams", async (req, res) => {
215
211
  const projectId = req.query.projectId;
216
212
  if (projectId) {
217
- const streams = db.listStreams(projectId);
213
+ const streams = await db.listStreams(projectId);
218
214
  res.json(streams);
219
215
  }
220
216
  else {
221
217
  res.status(400).send("Missing projectId");
222
218
  }
223
219
  });
224
- app.delete("/api/streams/:id", (req, res) => {
220
+ app.delete("/api/streams/:id", async (req, res) => {
225
221
  const { id } = req.params;
226
- db.deleteStream(id);
222
+ await db.deleteStream(id);
227
223
  res.json({ success: true });
228
224
  });
229
225
  app.get("/api/streams/:id/token", async (req, res) => {
@@ -236,19 +232,24 @@ async function startApiServer(db) {
236
232
  res.status(500).json({ error: String(e) });
237
233
  }
238
234
  });
239
- app.post("/api/streams", (req, res) => {
240
- // Deprecated or just listing? The previous code had this returning listStreams for POST?
241
- // I'll remove it or keep it if CLI uses it?
242
- // CLI uses db directly.
243
- // MCP uses GET /api/streams
244
- // I'll replace this with the actual CREATE logic to be RESTful, or keep /create
245
- const projectId = req.body.projectId;
246
- if (projectId) {
247
- const streams = db.listStreams(projectId);
248
- res.json(streams);
235
+ app.post("/api/streams", async (req, res) => {
236
+ try {
237
+ const { projectId, name, type, config } = req.body || {};
238
+ if (!projectId) {
239
+ return res.status(400).send("Missing projectId");
240
+ }
241
+ if (!name || typeof name !== "string" || !name.trim()) {
242
+ return res.status(400).send("Missing name");
243
+ }
244
+ if (!type || typeof type !== "string") {
245
+ return res.status(400).send("Missing type");
246
+ }
247
+ const stream = await db.createStream(projectId, type, name.trim(), config || {});
248
+ res.json(stream);
249
249
  }
250
- else {
251
- res.status(400).send("Missing projectId");
250
+ catch (e) {
251
+ console.error("Create stream error:", e);
252
+ res.status(500).json({ error: String(e) });
252
253
  }
253
254
  });
254
255
  app.post("/api/streams/create", async (req, res) => {
@@ -256,6 +257,23 @@ async function startApiServer(db) {
256
257
  const stream = await db.createStream(body.projectId, body.type, body.name, body.config || {});
257
258
  res.json(stream);
258
259
  });
260
+ app.get("/api/search", async (req, res) => {
261
+ const query = req.query.query || req.query.q;
262
+ const streamId = req.query.streamId;
263
+ const limitStr = req.query.limit;
264
+ const limit = limitStr ? parseInt(limitStr) : 50;
265
+ if (!query) {
266
+ return res.json([]);
267
+ }
268
+ try {
269
+ const results = await db.searchLogs(query, streamId, limit);
270
+ res.json(results);
271
+ }
272
+ catch (e) {
273
+ console.error("Error searching logs:", e);
274
+ res.status(500).send(String(e));
275
+ }
276
+ });
259
277
  app.get("/api/logs", async (req, res) => {
260
278
  const streamId = req.query.streamId;
261
279
  if (!streamId) {
@@ -275,10 +293,10 @@ async function startApiServer(db) {
275
293
  const query = req.query.q;
276
294
  let logs;
277
295
  if (query) {
278
- logs = await db.searchLogs(streamId, query, limit);
296
+ logs = await db.searchLogs(query, streamId, limit);
279
297
  }
280
298
  else {
281
- logs = db.getRecentLogs(streamId, limit, offset);
299
+ logs = await db.getRecentLogs(streamId, limit, offset);
282
300
  }
283
301
  res.json(logs);
284
302
  });
@@ -287,8 +305,8 @@ async function startApiServer(db) {
287
305
  if (req.path.startsWith("/api")) {
288
306
  return res.status(404).json({ error: "Not Found" });
289
307
  }
290
- if (require("fs").existsSync(path_1.default.join(publicPath, "index.html"))) {
291
- res.sendFile(path_1.default.join(publicPath, "index.html"));
308
+ if (fs.existsSync(path.join(publicPath, "index.html"))) {
309
+ res.sendFile(path.join(publicPath, "index.html"));
292
310
  }
293
311
  else {
294
312
  res.status(404).send("Dashboard not found. Please build the frontend.");
package/dist/cli_main.js CHANGED
@@ -1,59 +1,64 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- const yargs_1 = __importDefault(require("yargs"));
8
- const helpers_1 = require("yargs/helpers");
9
- const db_1 = require("./services/db");
10
- const server_1 = require("./api/server");
11
- const migrate_1 = require("./db/migrate");
12
- // import { ensureInfrastructure } from "./utils/startup"; // Might need adjustment
13
- const auth_1 = require("./services/auth");
14
- const chalk_1 = __importDefault(require("chalk"));
15
- const db = new db_1.DbService();
16
- const auth = new auth_1.AuthService();
2
+ import yargs from "yargs";
3
+ import { hideBin } from "yargs/helpers";
4
+ import { dbService, authService, dbAdapter } from "./db/client.js";
5
+ import { startApiServer } from "./api/server.js";
6
+ import { migrate } from "@loghead/db";
7
+ import chalk from "chalk";
8
+ function isUnsupportedVectorIndexError(error) {
9
+ const msg = error instanceof Error ? error.message : String(error);
10
+ return msg.includes("invalid expression in CREATE INDEX") && msg.includes("libsql_vector_idx");
11
+ }
17
12
  async function main() {
18
- const argv = await (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
13
+ const argv = await yargs(hideBin(process.argv))
19
14
  .command(["start", "$0"], "Start API Server", {}, async (argv) => {
20
15
  // console.log("Ensuring database is initialized...");
21
- await (0, migrate_1.migrate)(false); // Run migrations silently
22
- const token = await auth.getOrCreateMcpToken();
16
+ try {
17
+ await migrate(dbAdapter, false); // Run migrations silently
18
+ }
19
+ catch (e) {
20
+ if (isUnsupportedVectorIndexError(e)) {
21
+ console.warn("[Vector] Embedded DB does not support vector index expressions yet; continuing without vec_logs_idx.");
22
+ }
23
+ else {
24
+ throw e;
25
+ }
26
+ }
27
+ const token = await authService.getOrCreateMcpToken();
23
28
  // Start API Server (this sets up express listen)
24
- await (0, server_1.startApiServer)(db);
29
+ await startApiServer(dbService, authService);
25
30
  // console.clear();
26
- console.log(chalk_1.default.bold.green(`
31
+ console.log(chalk.bold.green(`
27
32
  ā–Œ ā–Œ ā–Œ
28
33
  ā–Œ ā–žā–€ā––ā–žā–€ā–Œā–›ā–€ā––ā–žā–€ā––ā–ā–€ā––ā–žā–€ā–Œ
29
34
  ā–Œ ā–Œ ā–Œā–šā–„ā–Œā–Œ ā–Œā–›ā–€ ā–žā–€ā–Œā–Œ ā–Œ
30
35
  ā–€ā–€ā–˜ā–ā–€ ā–—ā–„ā–˜ā–˜ ā–˜ā–ā–€ā–˜ā–ā–€ā–˜ā–ā–€ā–˜
31
36
  `));
32
- console.log(chalk_1.default.gray("--------------------------------------------------"));
33
- console.log(chalk_1.default.bold(" 🟢 Loghead is running"));
34
- console.log(chalk_1.default.gray("--------------------------------------------------"));
37
+ console.log(chalk.gray("--------------------------------------------------"));
38
+ console.log(chalk.bold(" 🟢 Loghead is running"));
39
+ console.log(chalk.gray("--------------------------------------------------"));
35
40
  console.log("");
36
- console.log(chalk_1.default.bold(" šŸ–„ļø Dashboard : ") + chalk_1.default.cyan("http://localhost:4567"));
37
- console.log(chalk_1.default.bold(" šŸ”Œ MCP Server: ") + chalk_1.default.cyan("http://localhost:4567/sse"));
41
+ console.log(chalk.bold(" šŸ–„ļø Dashboard : ") + chalk.cyan("http://localhost:4567"));
42
+ console.log(chalk.bold(" šŸ”Œ MCP Server: ") + chalk.cyan("http://localhost:4567/sse"));
38
43
  console.log("");
39
- console.log(chalk_1.default.bold(" šŸ”‘ MCP Token : "));
40
- console.log(chalk_1.default.yellow(token));
44
+ console.log(chalk.bold(" šŸ”‘ MCP Token : "));
45
+ console.log(chalk.yellow(token));
41
46
  console.log("");
42
- console.log(chalk_1.default.gray("--------------------------------------------------"));
43
- console.log(chalk_1.default.gray(" Press Ctrl+C to stop"));
47
+ console.log(chalk.gray("--------------------------------------------------"));
48
+ console.log(chalk.gray(" Press Ctrl+C to stop"));
44
49
  })
45
50
  .command("projects <cmd> [name]", "Manage projects", (yargs) => {
46
51
  yargs
47
- .command("list", "List projects", {}, () => {
48
- const projects = db.listProjects();
52
+ .command("list", "List projects", {}, async () => {
53
+ const projects = await dbService.listProjects();
49
54
  console.table(projects);
50
55
  })
51
- .command("add <name>", "Add project", {}, (argv) => {
52
- const p = db.createProject(argv.name);
56
+ .command("add <name>", "Add project", {}, async (argv) => {
57
+ const p = await dbService.createProject(argv.name);
53
58
  console.log(`Project created: ${p.id}`);
54
59
  })
55
- .command("delete <id>", "Delete project", {}, (argv) => {
56
- db.deleteProject(argv.id);
60
+ .command("delete <id>", "Delete project", {}, async (argv) => {
61
+ await dbService.deleteProject(argv.id);
57
62
  console.log(`Project deleted: ${argv.id}`);
58
63
  });
59
64
  })
@@ -61,8 +66,8 @@ async function main() {
61
66
  yargs
62
67
  .command("list", "List streams", {
63
68
  project: { type: "string", demandOption: true },
64
- }, (argv) => {
65
- const streams = db.listStreams(argv.project);
69
+ }, async (argv) => {
70
+ const streams = await dbService.listStreams(argv.project);
66
71
  console.table(streams);
67
72
  })
68
73
  .command("add <type> <name>", "Add stream", {
@@ -73,16 +78,16 @@ async function main() {
73
78
  if (argv.type === "docker" && argv.container) {
74
79
  config.container = argv.container;
75
80
  }
76
- const s = await db.createStream(argv.project, argv.type, argv.name, config);
81
+ const s = await dbService.createStream(argv.project, argv.type, argv.name, config);
77
82
  console.log(`Stream created: ${s.id}`);
78
83
  console.log(`Token: ${s.token}`);
79
84
  })
80
85
  .command("token <streamId>", "Get token for stream", {}, async (argv) => {
81
- const token = await auth.createStreamToken(argv.streamId);
86
+ const token = await authService.createStreamToken(argv.streamId);
82
87
  console.log(`Token: ${token}`);
83
88
  })
84
- .command("delete <id>", "Delete stream", {}, (argv) => {
85
- db.deleteStream(argv.id);
89
+ .command("delete <id>", "Delete stream", {}, async (argv) => {
90
+ await dbService.deleteStream(argv.id);
86
91
  console.log(`Stream deleted: ${argv.id}`);
87
92
  });
88
93
  })
package/dist/db/client.js CHANGED
@@ -1,55 +1,39 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.db = void 0;
40
- const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
41
- const sqliteVec = __importStar(require("sqlite-vec"));
42
- const path_1 = __importDefault(require("path"));
43
- const dotenv_1 = __importDefault(require("dotenv"));
44
- dotenv_1.default.config();
1
+ import { connect } from "@tursodatabase/database";
2
+ import { rm } from "fs/promises";
3
+ import path from "path";
4
+ import dotenv from "dotenv";
5
+ import { LocalLibSqlAdapter, DbService, AuthService, OllamaService } from "@loghead/db";
6
+ dotenv.config();
45
7
  const dbPath = process.env.LOGHEAD_DB_PATH || "loghead.db";
46
- console.log(`[DB] Using database at: ${path_1.default.resolve(dbPath)}`);
47
- const db = new better_sqlite3_1.default(dbPath);
48
- exports.db = db;
49
- // Load sqlite-vec extension
50
- try {
51
- sqliteVec.load(db);
52
- }
53
- catch (e) {
54
- console.error("Failed to load sqlite-vec extension:", e);
8
+ const resolvedDbPath = path.resolve(dbPath);
9
+ console.log(`[DB] Using database at: ${resolvedDbPath}`);
10
+ async function openLocalDb() {
11
+ let db = await connect(resolvedDbPath);
12
+ try {
13
+ const legacyRows = (await db
14
+ .prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='vec_logs'")
15
+ .all());
16
+ const hasLegacyVec0 = legacyRows.some((r) => (r.sql || "").toLowerCase().includes("vec0"));
17
+ if (hasLegacyVec0) {
18
+ console.warn("[DB] Legacy sqlite-vec schema detected. Resetting local DB file.");
19
+ db.close();
20
+ await Promise.allSettled([
21
+ rm(resolvedDbPath, { force: true }),
22
+ rm(`${resolvedDbPath}-wal`, { force: true }),
23
+ rm(`${resolvedDbPath}-shm`, { force: true }),
24
+ rm(`${resolvedDbPath}-journal`, { force: true }),
25
+ ]);
26
+ db = await connect(resolvedDbPath);
27
+ }
28
+ }
29
+ catch {
30
+ // If sqlite_master inspection fails, continue and let migrations handle setup.
31
+ }
32
+ return db;
55
33
  }
34
+ const db = await openLocalDb();
35
+ const dbAdapter = new LocalLibSqlAdapter(db);
36
+ const authService = new AuthService(dbAdapter);
37
+ const ollamaService = new OllamaService();
38
+ const dbService = new DbService(dbAdapter, authService, ollamaService);
39
+ export { dbAdapter, authService, ollamaService, dbService };
@@ -60,19 +60,6 @@ function migrate(verbose = true) {
60
60
  catch (e) {
61
61
  console.warn("Failed to create virtual vector table. Is sqlite-vec loaded?", e);
62
62
  }
63
- // Trigger to clean up vec_logs when logs are deleted
64
- try {
65
- client_1.db.exec(`
66
- CREATE TRIGGER IF NOT EXISTS delete_vec_log
67
- AFTER DELETE ON logs
68
- BEGIN
69
- DELETE FROM vec_logs WHERE rowid = old.rowid;
70
- END;
71
- `);
72
- }
73
- catch (e) {
74
- console.warn("Failed to create trigger delete_vec_log", e);
75
- }
76
63
  if (verbose)
77
64
  console.log("Migrations complete.");
78
65
  }