@loghead/core 0.1.31 ā 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.
- package/dist/api/server.js +67 -49
- package/dist/cli_main.js +46 -41
- package/dist/db/client.js +37 -53
- package/dist/db/migrate.js +0 -13
- package/dist/public/assets/index-BFN5xtu_.js +164 -0
- package/dist/public/assets/index-PEkusdx8.css +1 -0
- package/dist/public/index.html +2 -2
- package/dist/public/logo.png +0 -0
- package/dist/services/db.js +0 -2
- package/dist/types.js +1 -2
- package/dist/ui/main.js +43 -50
- package/dist/utils/startup.js +22 -20
- package/package.json +7 -5
- package/build/npm/README.md +0 -194
- package/build/npm/bin/loghead +0 -0
- package/build/npm/package.json +0 -32
- package/dist/public/assets/index-BGOKjK0s.css +0 -1
- package/dist/public/assets/index-BtWnFjRZ.js +0 -111
package/dist/api/server.js
CHANGED
|
@@ -1,33 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const
|
|
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((
|
|
17
|
-
app.use(
|
|
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 =
|
|
21
|
-
if (!
|
|
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 =
|
|
19
|
+
publicPath = path.join(__dirname, "../../dist/public");
|
|
24
20
|
}
|
|
25
|
-
if (
|
|
26
|
-
console.log(
|
|
27
|
-
app.use(
|
|
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(
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
251
|
-
|
|
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(
|
|
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 (
|
|
291
|
-
res.sendFile(
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
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 (
|
|
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
|
-
|
|
22
|
-
|
|
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 (
|
|
29
|
+
await startApiServer(dbService, authService);
|
|
25
30
|
// console.clear();
|
|
26
|
-
console.log(
|
|
31
|
+
console.log(chalk.bold.green(`
|
|
27
32
|
ā ā ā
|
|
28
33
|
ā āāāāāāāāāāāāāāāāāā
|
|
29
34
|
ā ā āāāāā āāā āāāā ā
|
|
30
35
|
āāāāā āāāā āāāāāāāāāā
|
|
31
36
|
`));
|
|
32
|
-
console.log(
|
|
33
|
-
console.log(
|
|
34
|
-
console.log(
|
|
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(
|
|
37
|
-
console.log(
|
|
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(
|
|
40
|
-
console.log(
|
|
44
|
+
console.log(chalk.bold(" š MCP Token : "));
|
|
45
|
+
console.log(chalk.yellow(token));
|
|
41
46
|
console.log("");
|
|
42
|
-
console.log(
|
|
43
|
-
console.log(
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
86
|
+
const token = await authService.createStreamToken(argv.streamId);
|
|
82
87
|
console.log(`Token: ${token}`);
|
|
83
88
|
})
|
|
84
|
-
.command("delete <id>", "Delete stream", {}, (argv) => {
|
|
85
|
-
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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 };
|
package/dist/db/migrate.js
CHANGED
|
@@ -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
|
}
|