@hasna/recordings 0.1.0 → 0.1.2
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/LICENSE +21 -191
- package/bun.lock +219 -0
- package/bunfig.toml +2 -0
- package/dist/__tests__/preload.d.ts +5 -0
- package/dist/__tests__/preload.d.ts.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +1446 -0
- package/dist/db/agents.d.ts +6 -0
- package/dist/db/agents.d.ts.map +1 -0
- package/dist/db/database.d.ts +7 -0
- package/dist/db/database.d.ts.map +1 -0
- package/dist/db/projects.d.ts +6 -0
- package/dist/db/projects.d.ts.map +1 -0
- package/dist/db/recordings.d.ts +15 -0
- package/dist/db/recordings.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +769 -30752
- package/dist/lib/config.d.ts +6 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/enhancer.d.ts +25 -0
- package/dist/lib/enhancer.d.ts.map +1 -0
- package/dist/lib/recorder.d.ts +30 -0
- package/dist/lib/recorder.d.ts.map +1 -0
- package/dist/lib/transcriber.d.ts +5 -0
- package/dist/lib/transcriber.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +4912 -0
- package/dist/types/index.d.ts +103 -0
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +35 -44
- package/src/__tests__/agents.test.ts +136 -0
- package/src/__tests__/config.test.ts +252 -0
- package/src/__tests__/database.test.ts +167 -0
- package/src/__tests__/enhancer.test.ts +574 -0
- package/src/__tests__/preload.ts +4 -0
- package/src/__tests__/projects.test.ts +109 -0
- package/src/__tests__/recorder.test.ts +278 -0
- package/src/__tests__/recordings.test.ts +353 -0
- package/src/__tests__/transcriber.test.ts +322 -0
- package/src/__tests__/types.test.ts +75 -0
- package/src/cli/index.ts +1078 -0
- package/src/db/agents.ts +81 -0
- package/src/db/database.ts +126 -0
- package/src/db/projects.ts +71 -0
- package/src/db/recordings.ts +219 -0
- package/src/index.ts +81 -0
- package/src/lib/config.ts +166 -0
- package/src/lib/enhancer.ts +167 -0
- package/src/lib/recorder.ts +198 -0
- package/src/lib/transcriber.ts +105 -0
- package/src/mcp/index.ts +446 -0
- package/src/native/RecordingsHelper.swift +352 -0
- package/src/types/index.ts +138 -0
- package/tsconfig.json +21 -0
- package/README.md +0 -145
- package/dist/index.js.map +0 -272
- package/scripts/postinstall.ts +0 -80
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
|
2
|
+
import { tmpdir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { mkdirSync, rmSync, existsSync } from "fs";
|
|
5
|
+
import {
|
|
6
|
+
getDatabase,
|
|
7
|
+
closeDatabase,
|
|
8
|
+
resetDatabase,
|
|
9
|
+
shortUuid,
|
|
10
|
+
} from "../db/database.js";
|
|
11
|
+
|
|
12
|
+
let tempDir: string;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
resetDatabase();
|
|
16
|
+
tempDir = join(tmpdir(), `open-recordings-test-db-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
17
|
+
mkdirSync(tempDir, { recursive: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
closeDatabase();
|
|
22
|
+
resetDatabase();
|
|
23
|
+
if (existsSync(tempDir)) {
|
|
24
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("getDatabase", () => {
|
|
29
|
+
test("creates database file and returns a database instance", () => {
|
|
30
|
+
const dbPath = join(tempDir, "test.db");
|
|
31
|
+
const db = getDatabase(dbPath);
|
|
32
|
+
expect(db).toBeDefined();
|
|
33
|
+
expect(existsSync(dbPath)).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("creates parent directories if they don't exist", () => {
|
|
37
|
+
const dbPath = join(tempDir, "nested", "deep", "test.db");
|
|
38
|
+
const db = getDatabase(dbPath);
|
|
39
|
+
expect(db).toBeDefined();
|
|
40
|
+
expect(existsSync(dbPath)).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("returns the same instance on subsequent calls (singleton)", () => {
|
|
44
|
+
const dbPath = join(tempDir, "test.db");
|
|
45
|
+
const db1 = getDatabase(dbPath);
|
|
46
|
+
const db2 = getDatabase(dbPath);
|
|
47
|
+
expect(db1).toBe(db2);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("runs migrations on creation", () => {
|
|
51
|
+
const dbPath = join(tempDir, "test.db");
|
|
52
|
+
const db = getDatabase(dbPath);
|
|
53
|
+
|
|
54
|
+
// Check that the tables exist
|
|
55
|
+
const tables = db
|
|
56
|
+
.query("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
|
|
57
|
+
.all() as { name: string }[];
|
|
58
|
+
const tableNames = tables.map((t) => t.name);
|
|
59
|
+
|
|
60
|
+
expect(tableNames).toContain("recordings");
|
|
61
|
+
expect(tableNames).toContain("agents");
|
|
62
|
+
expect(tableNames).toContain("projects");
|
|
63
|
+
expect(tableNames).toContain("recording_tags");
|
|
64
|
+
expect(tableNames).toContain("_migrations");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("sets WAL journal mode", () => {
|
|
68
|
+
const dbPath = join(tempDir, "test.db");
|
|
69
|
+
const db = getDatabase(dbPath);
|
|
70
|
+
const result = db.query("PRAGMA journal_mode").get() as { journal_mode: string } | null;
|
|
71
|
+
expect(result?.journal_mode).toBe("wal");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("enables foreign keys", () => {
|
|
75
|
+
const dbPath = join(tempDir, "test.db");
|
|
76
|
+
const db = getDatabase(dbPath);
|
|
77
|
+
const result = db.query("PRAGMA foreign_keys").get() as { foreign_keys: number } | null;
|
|
78
|
+
expect(result?.foreign_keys).toBe(1);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("records migration level", () => {
|
|
82
|
+
const dbPath = join(tempDir, "test.db");
|
|
83
|
+
const db = getDatabase(dbPath);
|
|
84
|
+
const result = db
|
|
85
|
+
.query("SELECT MAX(id) as max_id FROM _migrations")
|
|
86
|
+
.get() as { max_id: number };
|
|
87
|
+
expect(result.max_id).toBe(0);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("does not re-run migrations on second open", () => {
|
|
91
|
+
const dbPath = join(tempDir, "test.db");
|
|
92
|
+
const db1 = getDatabase(dbPath);
|
|
93
|
+
// Insert a test row
|
|
94
|
+
db1.query("INSERT INTO agents (id, name, created_at, last_seen_at) VALUES (?, ?, ?, ?)").run(
|
|
95
|
+
"test-id",
|
|
96
|
+
"test-agent",
|
|
97
|
+
new Date().toISOString(),
|
|
98
|
+
new Date().toISOString()
|
|
99
|
+
);
|
|
100
|
+
closeDatabase();
|
|
101
|
+
resetDatabase();
|
|
102
|
+
|
|
103
|
+
// Re-open
|
|
104
|
+
const db2 = getDatabase(dbPath);
|
|
105
|
+
const agent = db2.query("SELECT * FROM agents WHERE id = ?").get("test-id") as Record<string, unknown> | undefined;
|
|
106
|
+
expect(agent).toBeDefined();
|
|
107
|
+
expect(agent!["name"]).toBe("test-agent");
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe("closeDatabase", () => {
|
|
112
|
+
test("closes the database and allows reset", () => {
|
|
113
|
+
const dbPath = join(tempDir, "test.db");
|
|
114
|
+
getDatabase(dbPath);
|
|
115
|
+
closeDatabase();
|
|
116
|
+
resetDatabase();
|
|
117
|
+
|
|
118
|
+
// Should be able to open a new database
|
|
119
|
+
const dbPath2 = join(tempDir, "test2.db");
|
|
120
|
+
const db2 = getDatabase(dbPath2);
|
|
121
|
+
expect(db2).toBeDefined();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("is a no-op when no database is open", () => {
|
|
125
|
+
// Should not throw
|
|
126
|
+
closeDatabase();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe("resetDatabase", () => {
|
|
131
|
+
test("clears the singleton so next getDatabase creates new instance", () => {
|
|
132
|
+
const dbPath1 = join(tempDir, "test1.db");
|
|
133
|
+
const db1 = getDatabase(dbPath1);
|
|
134
|
+
closeDatabase();
|
|
135
|
+
resetDatabase();
|
|
136
|
+
|
|
137
|
+
const dbPath2 = join(tempDir, "test2.db");
|
|
138
|
+
const db2 = getDatabase(dbPath2);
|
|
139
|
+
expect(db2).not.toBe(db1);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("getDbPath", () => {
|
|
144
|
+
test("returns the db_path from config", async () => {
|
|
145
|
+
const { getDbPath } = await import("../db/database.js");
|
|
146
|
+
const path = getDbPath();
|
|
147
|
+
expect(typeof path).toBe("string");
|
|
148
|
+
expect(path).toContain("recordings.db");
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe("shortUuid", () => {
|
|
153
|
+
test("returns an 8-character string", () => {
|
|
154
|
+
const id = shortUuid();
|
|
155
|
+
expect(id).toHaveLength(8);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("returns different values on each call", () => {
|
|
159
|
+
const ids = new Set(Array.from({ length: 100 }, () => shortUuid()));
|
|
160
|
+
expect(ids.size).toBe(100);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("only contains valid UUID characters", () => {
|
|
164
|
+
const id = shortUuid();
|
|
165
|
+
expect(id).toMatch(/^[0-9a-f]{8}$/);
|
|
166
|
+
});
|
|
167
|
+
});
|