@loghead/core 0.1.1 → 0.1.3

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.
@@ -0,0 +1,111 @@
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 auth_1 = require("../services/auth");
10
+ const chalk_1 = __importDefault(require("chalk"));
11
+ const auth = new auth_1.AuthService();
12
+ async function startApiServer(db) {
13
+ const app = (0, express_1.default)();
14
+ const port = process.env.PORT || 4567;
15
+ app.use((0, cors_1.default)());
16
+ app.use(express_1.default.json());
17
+ await auth.initialize();
18
+ console.log(chalk_1.default.bold.green(`✔ Loghead Core API Server running on http://localhost:${port}`));
19
+ app.post("/api/ingest", async (req, res) => {
20
+ try {
21
+ const authHeader = req.headers.authorization;
22
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
23
+ return res.status(401).send("Unauthorized: Missing token");
24
+ }
25
+ const token = authHeader.split(" ")[1];
26
+ const payload = await auth.verifyToken(token);
27
+ if (!payload || !payload.streamId) {
28
+ return res.status(401).send("Unauthorized: Invalid token");
29
+ }
30
+ const { streamId, logs } = req.body;
31
+ if (streamId !== payload.streamId) {
32
+ return res.status(403).send("Forbidden: Token does not match streamId");
33
+ }
34
+ if (!logs) {
35
+ return res.status(400).send("Missing logs");
36
+ }
37
+ const logEntries = Array.isArray(logs) ? logs : [logs];
38
+ for (const log of logEntries) {
39
+ let content = "";
40
+ let metadata = {};
41
+ if (typeof log === "string") {
42
+ content = log;
43
+ }
44
+ else if (typeof log === "object") {
45
+ content = log.content || JSON.stringify(log);
46
+ metadata = log.metadata || {};
47
+ }
48
+ if (content) {
49
+ await db.addLog(streamId, content, metadata);
50
+ }
51
+ }
52
+ res.json({ success: true, count: logEntries.length });
53
+ }
54
+ catch (e) {
55
+ console.error("Ingest error:", e);
56
+ res.status(500).json({ error: String(e) });
57
+ }
58
+ });
59
+ app.get("/api/projects", (req, res) => {
60
+ const projects = db.listProjects();
61
+ res.json(projects);
62
+ });
63
+ app.post("/api/projects", (req, res) => {
64
+ const projects = db.listProjects();
65
+ res.json(projects);
66
+ });
67
+ app.get("/api/streams", (req, res) => {
68
+ const projectId = req.query.projectId;
69
+ if (projectId) {
70
+ const streams = db.listStreams(projectId);
71
+ res.json(streams);
72
+ }
73
+ else {
74
+ res.status(400).send("Missing projectId");
75
+ }
76
+ });
77
+ app.post("/api/streams", (req, res) => {
78
+ const projectId = req.body.projectId;
79
+ if (projectId) {
80
+ const streams = db.listStreams(projectId);
81
+ res.json(streams);
82
+ }
83
+ else {
84
+ res.status(400).send("Missing projectId");
85
+ }
86
+ });
87
+ app.post("/api/streams/create", async (req, res) => {
88
+ const body = req.body;
89
+ const stream = await db.createStream(body.projectId, body.type, body.name, {});
90
+ res.json(stream);
91
+ });
92
+ app.get("/api/logs", async (req, res) => {
93
+ const streamId = req.query.streamId;
94
+ if (!streamId) {
95
+ return res.status(400).send("Missing streamId");
96
+ }
97
+ const limit = parseInt(req.query.limit || "50");
98
+ const query = req.query.q;
99
+ let logs;
100
+ if (query) {
101
+ logs = await db.searchLogs(streamId, query, limit);
102
+ }
103
+ else {
104
+ logs = db.getRecentLogs(streamId, limit);
105
+ }
106
+ res.json(logs);
107
+ });
108
+ app.listen(port, () => {
109
+ // listening
110
+ });
111
+ }
@@ -0,0 +1,68 @@
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 main_1 = require("./ui/main");
14
+ const db = new db_1.DbService();
15
+ async function main() {
16
+ const argv = await (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
17
+ .command("init", "Initialize/Migrate database", {}, async () => {
18
+ console.log("Initializing database...");
19
+ await (0, migrate_1.migrate)();
20
+ })
21
+ .command("start", "Start API Server", {}, async () => {
22
+ // await ensureInfrastructure();
23
+ await (0, server_1.startApiServer)(db);
24
+ })
25
+ .command("ui", "Start Terminal UI", {}, async () => {
26
+ await (0, main_1.startTui)(db);
27
+ })
28
+ .command("projects <cmd> [name]", "Manage projects", (yargs) => {
29
+ yargs
30
+ .command("list", "List projects", {}, () => {
31
+ const projects = db.listProjects();
32
+ console.table(projects);
33
+ })
34
+ .command("add <name>", "Add project", {}, (argv) => {
35
+ const p = db.createProject(argv.name);
36
+ console.log(`Project created: ${p.id}`);
37
+ });
38
+ })
39
+ .command("streams <cmd> [type] [name]", "Manage streams", (yargs) => {
40
+ yargs
41
+ .command("list", "List streams", {
42
+ project: { type: "string", demandOption: true }
43
+ }, (argv) => {
44
+ const streams = db.listStreams(argv.project);
45
+ console.table(streams);
46
+ })
47
+ .command("add <type> <name>", "Add stream", {
48
+ project: { type: "string", demandOption: true },
49
+ container: { type: "string" }
50
+ }, async (argv) => {
51
+ const config = {};
52
+ if (argv.type === "docker" && argv.container) {
53
+ config.container = argv.container;
54
+ }
55
+ const s = await db.createStream(argv.project, argv.type, argv.name, config);
56
+ console.log(`Stream created: ${s.id}`);
57
+ console.log(`Token: ${s.token}`);
58
+ });
59
+ })
60
+ .demandCommand(1)
61
+ .strict()
62
+ .help()
63
+ .parse();
64
+ }
65
+ main().catch(err => {
66
+ console.error(err);
67
+ process.exit(1);
68
+ });
@@ -0,0 +1,53 @@
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 dotenv_1 = __importDefault(require("dotenv"));
43
+ dotenv_1.default.config();
44
+ const dbPath = process.env.LOGHEAD_DB_PATH || "loghead.db";
45
+ const db = new better_sqlite3_1.default(dbPath);
46
+ exports.db = db;
47
+ // Load sqlite-vec extension
48
+ try {
49
+ sqliteVec.load(db);
50
+ }
51
+ catch (e) {
52
+ console.error("Failed to load sqlite-vec extension:", e);
53
+ }
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.migrate = migrate;
4
+ const client_1 = require("./client");
5
+ function migrate(verbose = true) {
6
+ if (verbose)
7
+ console.log("Running migrations...");
8
+ // Enable foreign keys
9
+ client_1.db.exec("PRAGMA foreign_keys = ON;");
10
+ // System Config table (for secrets, etc.)
11
+ client_1.db.exec(`
12
+ CREATE TABLE IF NOT EXISTS system_config (
13
+ key TEXT PRIMARY KEY,
14
+ value TEXT NOT NULL
15
+ );
16
+ `);
17
+ // Projects table
18
+ client_1.db.exec(`
19
+ CREATE TABLE IF NOT EXISTS projects (
20
+ id TEXT PRIMARY KEY,
21
+ name TEXT NOT NULL,
22
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
23
+ );
24
+ `);
25
+ // Data Streams table
26
+ client_1.db.exec(`
27
+ CREATE TABLE IF NOT EXISTS data_streams (
28
+ id TEXT PRIMARY KEY,
29
+ project_id TEXT,
30
+ type TEXT NOT NULL,
31
+ name TEXT NOT NULL,
32
+ config TEXT DEFAULT '{}',
33
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
34
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
35
+ );
36
+ `);
37
+ // Logs table
38
+ client_1.db.exec(`
39
+ CREATE TABLE IF NOT EXISTS logs (
40
+ id TEXT PRIMARY KEY,
41
+ stream_id TEXT,
42
+ content TEXT NOT NULL,
43
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
44
+ metadata TEXT DEFAULT '{}',
45
+ FOREIGN KEY(stream_id) REFERENCES data_streams(id) ON DELETE CASCADE
46
+ );
47
+ `);
48
+ // Vector table (using sqlite-vec)
49
+ // Assuming 1024 dimensions for qwen3-embedding:0.6b (check actual dim)
50
+ // qwen2.5-0.5b is 1536?
51
+ // qwen-embedding-0.6b might be 384 or 1024?
52
+ // Let's assume 1024 as per previous code.
53
+ try {
54
+ client_1.db.exec(`
55
+ CREATE VIRTUAL TABLE IF NOT EXISTS vec_logs USING vec0(
56
+ embedding float[1024]
57
+ );
58
+ `);
59
+ }
60
+ catch (e) {
61
+ console.warn("Failed to create virtual vector table. Is sqlite-vec loaded?", e);
62
+ }
63
+ if (verbose)
64
+ console.log("Migrations complete.");
65
+ }
@@ -0,0 +1,48 @@
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.AuthService = void 0;
7
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
8
+ const client_1 = require("../db/client");
9
+ const crypto_1 = require("crypto");
10
+ class AuthService {
11
+ secretKey = null;
12
+ async initialize() {
13
+ if (this.secretKey)
14
+ return;
15
+ // Try to load secret from DB
16
+ const row = client_1.db.prepare("SELECT value FROM system_config WHERE key = 'jwt_secret'").get();
17
+ let rawSecret = row?.value;
18
+ if (!rawSecret) {
19
+ // Generate new secret
20
+ rawSecret = (0, crypto_1.randomBytes)(64).toString('hex');
21
+ client_1.db.prepare("INSERT INTO system_config (key, value) VALUES ('jwt_secret', ?)").run(rawSecret);
22
+ }
23
+ this.secretKey = rawSecret;
24
+ }
25
+ async createStreamToken(streamId) {
26
+ await this.initialize();
27
+ if (!this.secretKey)
28
+ throw new Error("Auth not initialized");
29
+ const token = jsonwebtoken_1.default.sign({ sub: streamId, iss: "loghead" }, this.secretKey, { algorithm: "HS512" });
30
+ return token;
31
+ }
32
+ async verifyToken(token) {
33
+ await this.initialize();
34
+ if (!this.secretKey)
35
+ throw new Error("Auth not initialized");
36
+ try {
37
+ const payload = jsonwebtoken_1.default.verify(token, this.secretKey, { issuer: "loghead", algorithms: ["HS512"] });
38
+ if (!payload.sub)
39
+ return null;
40
+ return { streamId: payload.sub };
41
+ }
42
+ catch (e) {
43
+ console.error("Token verification failed:", e);
44
+ return null;
45
+ }
46
+ }
47
+ }
48
+ exports.AuthService = AuthService;
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DbService = void 0;
4
+ const client_1 = require("../db/client");
5
+ const ollama_1 = require("./ollama");
6
+ const auth_1 = require("./auth");
7
+ const crypto_1 = require("crypto");
8
+ const ollama = new ollama_1.OllamaService();
9
+ const auth = new auth_1.AuthService();
10
+ class DbService {
11
+ createProject(name) {
12
+ const id = (0, crypto_1.randomUUID)();
13
+ client_1.db.prepare("INSERT INTO projects (id, name) VALUES (?, ?)").run(id, name);
14
+ return this.getProject(id);
15
+ }
16
+ getProject(id) {
17
+ return client_1.db.prepare("SELECT * FROM projects WHERE id = ?").get(id);
18
+ }
19
+ deleteProject(id) {
20
+ client_1.db.prepare("DELETE FROM projects WHERE id = ?").run(id);
21
+ return true;
22
+ }
23
+ listProjects() {
24
+ console.error("Listing projects...");
25
+ try {
26
+ const projects = client_1.db.prepare("SELECT * FROM projects ORDER BY created_at DESC").all();
27
+ console.error(`Found ${projects.length} projects.`);
28
+ return projects.map((p) => {
29
+ const streams = client_1.db.prepare("SELECT * FROM data_streams WHERE project_id = ?").all(p.id);
30
+ return { ...p, streams };
31
+ });
32
+ }
33
+ catch (e) {
34
+ console.error("Error in listProjects:", e);
35
+ throw e;
36
+ }
37
+ }
38
+ async createStream(projectId, type, name, config = {}) {
39
+ const id = (0, crypto_1.randomUUID)();
40
+ client_1.db.prepare("INSERT INTO data_streams (id, project_id, type, name, config) VALUES (?, ?, ?, ?, ?)").run(id, projectId, type, name, JSON.stringify(config));
41
+ const token = await auth.createStreamToken(id);
42
+ const stream = this.getStream(id);
43
+ return { ...stream, token };
44
+ }
45
+ getStream(id) {
46
+ const stream = client_1.db.prepare("SELECT * FROM data_streams WHERE id = ?").get(id);
47
+ if (stream && typeof stream.config === "string") {
48
+ try {
49
+ stream.config = JSON.parse(stream.config);
50
+ }
51
+ catch { /* ignore */ }
52
+ }
53
+ return stream;
54
+ }
55
+ deleteStream(id) {
56
+ client_1.db.prepare("DELETE FROM data_streams WHERE id = ?").run(id);
57
+ return true;
58
+ }
59
+ listStreams(projectId) {
60
+ const streams = client_1.db.prepare("SELECT * FROM data_streams WHERE project_id = ? ORDER BY created_at DESC").all(projectId);
61
+ return streams.map((s) => {
62
+ if (typeof s.config === "string")
63
+ try {
64
+ s.config = JSON.parse(s.config);
65
+ }
66
+ catch { /* ignore */ }
67
+ return s;
68
+ });
69
+ }
70
+ async addLog(streamId, content, metadata = {}) {
71
+ // Generate embedding
72
+ let embedding = null;
73
+ try {
74
+ embedding = await ollama.generateEmbedding(content);
75
+ }
76
+ catch (_e) {
77
+ // console.warn("Embedding failed", _e);
78
+ }
79
+ const id = (0, crypto_1.randomUUID)();
80
+ const metadataStr = JSON.stringify(metadata);
81
+ // Manual Transaction
82
+ const insertTx = client_1.db.transaction(() => {
83
+ // 1. Insert into logs
84
+ client_1.db.prepare("INSERT INTO logs (id, stream_id, content, metadata) VALUES (?, ?, ?, ?)").run(id, streamId, content, metadataStr);
85
+ // 2. Get rowid
86
+ const rowInfo = client_1.db.prepare("SELECT last_insert_rowid() as rowid").get();
87
+ const rowid = rowInfo.rowid;
88
+ // 3. Insert into vec_logs if embedding exists
89
+ if (embedding && embedding.length > 0) {
90
+ const vectorJson = JSON.stringify(embedding);
91
+ client_1.db.prepare("INSERT INTO vec_logs(rowid, embedding) VALUES (?, ?)").run(rowid, vectorJson);
92
+ }
93
+ });
94
+ try {
95
+ insertTx();
96
+ }
97
+ catch (e) {
98
+ throw e;
99
+ }
100
+ return { id };
101
+ }
102
+ async searchLogs(streamId, query, limit = 10) {
103
+ const embedding = await ollama.generateEmbedding(query);
104
+ if (!embedding)
105
+ return [];
106
+ const vectorJson = JSON.stringify(embedding);
107
+ // KNN Search
108
+ const rows = client_1.db.prepare(`
109
+ SELECT l.content, l.timestamp, l.metadata, v.distance
110
+ FROM vec_logs v
111
+ JOIN logs l ON l.rowid = v.rowid
112
+ WHERE v.embedding MATCH ? AND k = ? AND l.stream_id = ?
113
+ ORDER BY v.distance
114
+ `).all(vectorJson, limit, streamId);
115
+ return rows.map((row) => {
116
+ let meta;
117
+ try {
118
+ meta = JSON.parse(row.metadata);
119
+ }
120
+ catch { /* ignore */ }
121
+ return {
122
+ content: row.content,
123
+ timestamp: row.timestamp,
124
+ similarity: 1 - row.distance, // Rough approx
125
+ metadata: (meta && Object.keys(meta).length > 0) ? meta : undefined
126
+ };
127
+ });
128
+ }
129
+ getRecentLogs(streamId, limit = 50) {
130
+ const rows = client_1.db.prepare(`
131
+ SELECT content, timestamp, metadata FROM logs
132
+ WHERE stream_id = ?
133
+ ORDER BY timestamp DESC
134
+ LIMIT ?
135
+ `).all(streamId, limit);
136
+ return rows.map((row) => {
137
+ let meta = row.metadata;
138
+ if (typeof meta === "string") {
139
+ try {
140
+ meta = JSON.parse(meta);
141
+ }
142
+ catch { /* ignore */ }
143
+ }
144
+ return {
145
+ id: row.id, // Ensure id is included if needed, or update Log type
146
+ stream_id: streamId,
147
+ content: row.content,
148
+ timestamp: row.timestamp,
149
+ metadata: (typeof meta === "object" && meta && Object.keys(meta).length > 0) ? meta : {}
150
+ };
151
+ });
152
+ }
153
+ close() {
154
+ client_1.db.close();
155
+ }
156
+ }
157
+ exports.DbService = DbService;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OllamaService = void 0;
4
+ const ollama_1 = require("ollama");
5
+ class OllamaService {
6
+ client;
7
+ model;
8
+ constructor(host = "http://localhost:11434", model = "qwen3-embedding:0.6b") {
9
+ this.client = new ollama_1.Ollama({ host });
10
+ this.model = model;
11
+ }
12
+ async generateEmbedding(prompt) {
13
+ try {
14
+ const response = await this.client.embeddings({
15
+ model: this.model,
16
+ prompt: prompt,
17
+ });
18
+ return response.embedding;
19
+ }
20
+ catch (error) {
21
+ console.error("Failed to generate embedding:", error);
22
+ throw error;
23
+ }
24
+ }
25
+ async ensureModel() {
26
+ try {
27
+ const list = await this.client.list();
28
+ const exists = list.models.some((m) => m.name.includes(this.model));
29
+ if (!exists) {
30
+ console.log(`Model ${this.model} not found. Pulling...`);
31
+ await this.client.pull({ model: this.model });
32
+ console.log("Model pulled.");
33
+ }
34
+ }
35
+ catch (e) {
36
+ console.warn("Could not check/pull ollama model:", e);
37
+ }
38
+ }
39
+ }
40
+ exports.OllamaService = OllamaService;
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,71 @@
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.startTui = startTui;
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ async function startTui(db) {
10
+ console.clear();
11
+ console.log(chalk_1.default.bold.blue("Loghead Dashboard\n"));
12
+ while (true) {
13
+ const projects = db.listProjects();
14
+ if (projects.length === 0) {
15
+ console.log("No projects found. Use 'npx loghead projects add <name>' to create one.");
16
+ break;
17
+ }
18
+ const projectChoices = projects.map(p => ({ name: p.name, value: p.id }));
19
+ projectChoices.push({ name: chalk_1.default.red("Exit"), value: "exit" });
20
+ const { projectId } = await inquirer_1.default.prompt([{
21
+ type: "list",
22
+ name: "projectId",
23
+ message: "Select a Project",
24
+ choices: projectChoices
25
+ }]);
26
+ if (projectId === "exit")
27
+ break;
28
+ // List streams for project
29
+ while (true) {
30
+ console.clear();
31
+ const project = projects.find(p => p.id === projectId);
32
+ console.log(chalk_1.default.bold.blue(`Project: ${project?.name}\n`));
33
+ const streams = db.listStreams(projectId);
34
+ if (streams.length === 0) {
35
+ console.log("No streams found.");
36
+ }
37
+ const streamChoices = streams.map(s => ({
38
+ name: `${s.name} (${s.type})`,
39
+ value: s.id
40
+ }));
41
+ streamChoices.push({ name: chalk_1.default.yellow("Back"), value: "back" });
42
+ const { streamId } = await inquirer_1.default.prompt([{
43
+ type: "list",
44
+ name: "streamId",
45
+ message: "Select a Stream to view logs",
46
+ choices: streamChoices
47
+ }]);
48
+ if (streamId === "back")
49
+ break;
50
+ // Show logs (simple tail)
51
+ console.clear();
52
+ const stream = streams.find(s => s.id === streamId);
53
+ console.log(chalk_1.default.bold.green(`Logs for ${stream?.name}:\n`));
54
+ const logs = db.getRecentLogs(streamId, 20);
55
+ if (logs.length === 0) {
56
+ console.log("No logs recorded yet.");
57
+ }
58
+ else {
59
+ // Reverse to show oldest to newest like a log file
60
+ [...logs].reverse().forEach(log => {
61
+ console.log(`${chalk_1.default.dim(log.timestamp)} ${log.content}`);
62
+ });
63
+ }
64
+ console.log("\nPress Enter to return...");
65
+ await new Promise(resolve => {
66
+ process.stdin.once('data', () => resolve());
67
+ });
68
+ }
69
+ console.clear();
70
+ }
71
+ }
@@ -0,0 +1,55 @@
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.ensureInfrastructure = ensureInfrastructure;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const ollama_1 = require("../services/ollama");
9
+ const migrate_1 = require("../db/migrate");
10
+ async function ensureInfrastructure() {
11
+ console.log(chalk_1.default.bold.blue("\n🚀Performing system preflight checks..."));
12
+ // 1. Check Local Ollama
13
+ await checkStep("Checking local Ollama...", async () => {
14
+ try {
15
+ // Node.js fetch needs global fetch or polyfill in Node 18+.
16
+ // Assuming Node 18+ which has fetch.
17
+ const response = await fetch("http://localhost:11434/api/tags");
18
+ if (!response.ok)
19
+ throw new Error("Ollama is not running");
20
+ }
21
+ catch {
22
+ throw new Error("Ollama is not accessible at http://localhost:11434. Please install and run Ollama.");
23
+ }
24
+ });
25
+ // 2. Check Database & Migrations (SQLite)
26
+ await checkStep("Initializing database...", async () => {
27
+ try {
28
+ (0, migrate_1.migrate)(false);
29
+ }
30
+ catch (e) {
31
+ console.log(chalk_1.default.yellow("\n ➤ Migration failed..."));
32
+ throw e;
33
+ }
34
+ });
35
+ // 3. Check Ollama Model
36
+ await checkStep("Checking embedding model (qwen3-embedding)...", async () => {
37
+ const ollama = new ollama_1.OllamaService();
38
+ await ollama.ensureModel();
39
+ });
40
+ console.log(`${chalk_1.default.green("✔")} System preflight checks complete`);
41
+ }
42
+ async function checkStep(name, action) {
43
+ // Print pending state
44
+ process.stdout.write(`${chalk_1.default.cyan("○")} ${name}`);
45
+ try {
46
+ await action();
47
+ // Clear line and print success
48
+ process.stdout.write(`\r${chalk_1.default.green("✔")} ${name} \n`);
49
+ }
50
+ catch (e) {
51
+ process.stdout.write(`\r${chalk_1.default.red("✖")} ${name}\n`);
52
+ console.error(chalk_1.default.red(` Error: ${e instanceof Error ? e.message : e}`));
53
+ process.exit(1);
54
+ }
55
+ }
package/loggerhead.db ADDED
Binary file