db4ai 0.3.0 → 0.3.1

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 (89) hide show
  1. package/dist/chunk-3DWAMVV5.js +305 -0
  2. package/dist/chunk-3DWAMVV5.js.map +1 -0
  3. package/dist/chunk-COTPYBYM.js +618 -0
  4. package/dist/chunk-COTPYBYM.js.map +1 -0
  5. package/dist/chunk-EERD6CDF.js +735 -0
  6. package/dist/chunk-EERD6CDF.js.map +1 -0
  7. package/dist/chunk-FUF4HJTC.js +758 -0
  8. package/dist/chunk-FUF4HJTC.js.map +1 -0
  9. package/dist/chunk-JLL6FH5L.js +16 -0
  10. package/dist/chunk-JLL6FH5L.js.map +1 -0
  11. package/dist/chunk-JXFW6AIT.js +192 -0
  12. package/dist/chunk-JXFW6AIT.js.map +1 -0
  13. package/dist/chunk-XLSYCQPG.js +854 -0
  14. package/dist/chunk-XLSYCQPG.js.map +1 -0
  15. package/dist/chunk-Y5IXAS7F.js +569 -0
  16. package/dist/chunk-Y5IXAS7F.js.map +1 -0
  17. package/dist/cli/bin.d.ts +13 -12
  18. package/dist/cli/bin.js +277 -307
  19. package/dist/cli/bin.js.map +1 -1
  20. package/dist/cli/dashboard/index.d.ts +295 -14
  21. package/dist/cli/dashboard/index.js +60 -15
  22. package/dist/cli/dashboard/index.js.map +1 -1
  23. package/dist/cli/index.d.ts +10 -16
  24. package/dist/cli/index.js +94 -47
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/cli/runtime/index.d.ts +52 -23
  27. package/dist/cli/runtime/index.js +10 -704
  28. package/dist/cli/runtime/index.js.map +1 -1
  29. package/dist/cli/scanner/index.d.ts +17 -15
  30. package/dist/cli/scanner/index.js +8 -639
  31. package/dist/cli/scanner/index.js.map +1 -1
  32. package/dist/cli/seed/index.d.ts +16 -12
  33. package/dist/cli/seed/index.js +12 -773
  34. package/dist/cli/seed/index.js.map +1 -1
  35. package/dist/cli/sync/index.d.ts +54 -53
  36. package/dist/cli/sync/index.js +23 -704
  37. package/dist/cli/sync/index.js.map +1 -1
  38. package/dist/cli/terminal.d.ts +9 -8
  39. package/dist/cli/terminal.js +6 -209
  40. package/dist/cli/terminal.js.map +1 -1
  41. package/dist/cli/workflow/index.d.ts +18 -10
  42. package/dist/cli/workflow/index.js +6 -307
  43. package/dist/cli/workflow/index.js.map +1 -1
  44. package/dist/handlers.d.ts +10 -9
  45. package/dist/handlers.js +6 -38
  46. package/dist/handlers.js.map +1 -1
  47. package/dist/index.d.ts +120 -118
  48. package/dist/index.js +1963 -3125
  49. package/dist/index.js.map +1 -1
  50. package/package.json +3 -4
  51. package/dist/cli/bin.d.ts.map +0 -1
  52. package/dist/cli/dashboard/App.d.ts +0 -16
  53. package/dist/cli/dashboard/App.d.ts.map +0 -1
  54. package/dist/cli/dashboard/App.js +0 -116
  55. package/dist/cli/dashboard/App.js.map +0 -1
  56. package/dist/cli/dashboard/components/index.d.ts +0 -70
  57. package/dist/cli/dashboard/components/index.d.ts.map +0 -1
  58. package/dist/cli/dashboard/components/index.js +0 -192
  59. package/dist/cli/dashboard/components/index.js.map +0 -1
  60. package/dist/cli/dashboard/hooks/index.d.ts +0 -76
  61. package/dist/cli/dashboard/hooks/index.d.ts.map +0 -1
  62. package/dist/cli/dashboard/hooks/index.js +0 -201
  63. package/dist/cli/dashboard/hooks/index.js.map +0 -1
  64. package/dist/cli/dashboard/index.d.ts.map +0 -1
  65. package/dist/cli/dashboard/types.d.ts +0 -84
  66. package/dist/cli/dashboard/types.d.ts.map +0 -1
  67. package/dist/cli/dashboard/types.js +0 -5
  68. package/dist/cli/dashboard/types.js.map +0 -1
  69. package/dist/cli/dashboard/views/index.d.ts +0 -51
  70. package/dist/cli/dashboard/views/index.d.ts.map +0 -1
  71. package/dist/cli/dashboard/views/index.js +0 -72
  72. package/dist/cli/dashboard/views/index.js.map +0 -1
  73. package/dist/cli/index.d.ts.map +0 -1
  74. package/dist/cli/runtime/index.d.ts.map +0 -1
  75. package/dist/cli/scanner/index.d.ts.map +0 -1
  76. package/dist/cli/seed/index.d.ts.map +0 -1
  77. package/dist/cli/sync/index.d.ts.map +0 -1
  78. package/dist/cli/terminal.d.ts.map +0 -1
  79. package/dist/cli/workflow/index.d.ts.map +0 -1
  80. package/dist/errors.d.ts +0 -43
  81. package/dist/errors.d.ts.map +0 -1
  82. package/dist/errors.js +0 -47
  83. package/dist/errors.js.map +0 -1
  84. package/dist/handlers.d.ts.map +0 -1
  85. package/dist/index.d.ts.map +0 -1
  86. package/dist/types.d.ts +0 -276
  87. package/dist/types.d.ts.map +0 -1
  88. package/dist/types.js +0 -12
  89. package/dist/types.js.map +0 -1
@@ -0,0 +1,758 @@
1
+ // cli/runtime/index.ts
2
+ import Database from "better-sqlite3";
3
+ import * as fs from "fs";
4
+ import * as path from "path";
5
+ import * as crypto from "crypto";
6
+ function computeHash(obj) {
7
+ const sortedJson = stableStringify(obj);
8
+ return crypto.createHash("sha256").update(sortedJson).digest("hex").slice(0, 16);
9
+ }
10
+ function stableStringify(obj) {
11
+ if (obj === null || typeof obj !== "object") {
12
+ return JSON.stringify(obj);
13
+ }
14
+ if (Array.isArray(obj)) {
15
+ return "[" + obj.map(stableStringify).join(",") + "]";
16
+ }
17
+ const keys = Object.keys(obj).sort();
18
+ const pairs = keys.map(
19
+ (key) => JSON.stringify(key) + ":" + stableStringify(obj[key])
20
+ );
21
+ return "{" + pairs.join(",") + "}";
22
+ }
23
+ function ensureDirectoryExists(filePath) {
24
+ const dir = path.dirname(filePath);
25
+ try {
26
+ if (!fs.existsSync(dir)) {
27
+ fs.mkdirSync(dir, { recursive: true });
28
+ }
29
+ } catch {
30
+ }
31
+ }
32
+ var ConnectionPool = class {
33
+ static instances = /* @__PURE__ */ new Map();
34
+ /**
35
+ * Acquire a database connection. Creates a new one or returns existing.
36
+ */
37
+ static acquire(dbPath) {
38
+ const normalizedPath = path.resolve(dbPath);
39
+ const existing = this.instances.get(normalizedPath);
40
+ if (existing) {
41
+ existing.refCount++;
42
+ return existing.db;
43
+ }
44
+ ensureDirectoryExists(normalizedPath);
45
+ let db;
46
+ try {
47
+ db = new Database(normalizedPath);
48
+ } catch {
49
+ db = new Database(":memory:");
50
+ }
51
+ try {
52
+ db.pragma("journal_mode = WAL");
53
+ } catch {
54
+ }
55
+ this.instances.set(normalizedPath, { db, refCount: 1 });
56
+ return db;
57
+ }
58
+ /**
59
+ * Release a database connection. Closes when ref count reaches 0.
60
+ */
61
+ static release(dbPath) {
62
+ const normalizedPath = path.resolve(dbPath);
63
+ const entry = this.instances.get(normalizedPath);
64
+ if (!entry) return;
65
+ entry.refCount--;
66
+ if (entry.refCount <= 0) {
67
+ entry.db.close();
68
+ this.instances.delete(normalizedPath);
69
+ }
70
+ }
71
+ /**
72
+ * Force close all connections (for testing cleanup).
73
+ */
74
+ static closeAll() {
75
+ for (const [path2, entry] of this.instances) {
76
+ entry.db.close();
77
+ this.instances.delete(path2);
78
+ }
79
+ }
80
+ };
81
+ var SCHEMA_VERSION = 2;
82
+ var SCHEMA_SQL = `
83
+ -- Things table (entities)
84
+ CREATE TABLE IF NOT EXISTS things (
85
+ rowid INTEGER PRIMARY KEY AUTOINCREMENT,
86
+ type TEXT NOT NULL,
87
+ id TEXT NOT NULL,
88
+ data TEXT NOT NULL,
89
+ content_hash TEXT NOT NULL,
90
+ created_at INTEGER NOT NULL,
91
+ created_by_action INTEGER,
92
+ FOREIGN KEY (created_by_action) REFERENCES actions(rowid)
93
+ );
94
+
95
+ -- Indexes for things table
96
+ CREATE INDEX IF NOT EXISTS idx_things_type_id ON things(type, id);
97
+ CREATE INDEX IF NOT EXISTS idx_things_content_hash ON things(content_hash);
98
+ CREATE INDEX IF NOT EXISTS idx_things_type ON things(type);
99
+ -- Optimized index for latest version lookups (covering index)
100
+ CREATE INDEX IF NOT EXISTS idx_things_type_id_rowid ON things(type, id, rowid DESC);
101
+ -- Index for content hash deduplication queries
102
+ CREATE INDEX IF NOT EXISTS idx_things_type_id_hash ON things(type, id, content_hash);
103
+
104
+ -- Relationships table (edges)
105
+ CREATE TABLE IF NOT EXISTS relationships (
106
+ rowid INTEGER PRIMARY KEY AUTOINCREMENT,
107
+ from_rowid INTEGER NOT NULL,
108
+ to_rowid INTEGER NOT NULL,
109
+ type TEXT NOT NULL,
110
+ path TEXT NOT NULL,
111
+ label TEXT NOT NULL,
112
+ created_at INTEGER NOT NULL,
113
+ created_by_action INTEGER,
114
+ FOREIGN KEY (from_rowid) REFERENCES things(rowid),
115
+ FOREIGN KEY (to_rowid) REFERENCES things(rowid),
116
+ FOREIGN KEY (created_by_action) REFERENCES actions(rowid),
117
+ UNIQUE (from_rowid, to_rowid, path)
118
+ );
119
+
120
+ -- Indexes for relationships table
121
+ CREATE INDEX IF NOT EXISTS idx_relationships_from ON relationships(from_rowid);
122
+ CREATE INDEX IF NOT EXISTS idx_relationships_to ON relationships(to_rowid);
123
+ CREATE INDEX IF NOT EXISTS idx_relationships_type ON relationships(type);
124
+ -- Optimized composite index for filtered traversals
125
+ CREATE INDEX IF NOT EXISTS idx_relationships_from_type ON relationships(from_rowid, type);
126
+ CREATE INDEX IF NOT EXISTS idx_relationships_to_type ON relationships(to_rowid, type);
127
+
128
+ -- Actions table (for caching and tracking)
129
+ CREATE TABLE IF NOT EXISTS actions (
130
+ rowid INTEGER PRIMARY KEY AUTOINCREMENT,
131
+ type TEXT NOT NULL,
132
+ input_hash TEXT NOT NULL,
133
+ input TEXT NOT NULL,
134
+ output TEXT,
135
+ model TEXT,
136
+ status TEXT NOT NULL DEFAULT 'pending',
137
+ error TEXT,
138
+ duration_ms INTEGER,
139
+ cost_tokens INTEGER,
140
+ created_at INTEGER NOT NULL
141
+ );
142
+
143
+ -- Indexes for actions table
144
+ CREATE INDEX IF NOT EXISTS idx_actions_input_hash ON actions(input_hash);
145
+ CREATE INDEX IF NOT EXISTS idx_actions_status ON actions(status);
146
+ -- Optimized composite index for cache lookups (input_hash + status)
147
+ CREATE INDEX IF NOT EXISTS idx_actions_hash_status ON actions(input_hash, status);
148
+
149
+ -- Events table (audit log)
150
+ CREATE TABLE IF NOT EXISTS events (
151
+ rowid INTEGER PRIMARY KEY AUTOINCREMENT,
152
+ type TEXT NOT NULL,
153
+ action TEXT NOT NULL,
154
+ entity_type TEXT NOT NULL,
155
+ entity_id TEXT NOT NULL,
156
+ entity_rowid INTEGER NOT NULL,
157
+ data TEXT NOT NULL,
158
+ changes TEXT,
159
+ timestamp INTEGER NOT NULL,
160
+ action_id INTEGER,
161
+ correlation_id TEXT,
162
+ FOREIGN KEY (action_id) REFERENCES actions(rowid)
163
+ );
164
+
165
+ -- Indexes for events table
166
+ CREATE INDEX IF NOT EXISTS idx_events_type ON events(type);
167
+ CREATE INDEX IF NOT EXISTS idx_events_action ON events(action);
168
+ CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
169
+ CREATE INDEX IF NOT EXISTS idx_events_correlation_id ON events(correlation_id);
170
+ -- Optimized composite index for entity-based event lookups
171
+ CREATE INDEX IF NOT EXISTS idx_events_entity ON events(entity_type, entity_id);
172
+ -- Composite index for filtered event queries
173
+ CREATE INDEX IF NOT EXISTS idx_events_type_action ON events(type, action);
174
+
175
+ -- Schema version tracking
176
+ CREATE TABLE IF NOT EXISTS schema_version (
177
+ version INTEGER NOT NULL
178
+ );
179
+ `;
180
+ var MIGRATION_V1_TO_V2 = `
181
+ -- Add new optimized indexes for version 2
182
+ CREATE INDEX IF NOT EXISTS idx_things_type_id_rowid ON things(type, id, rowid DESC);
183
+ CREATE INDEX IF NOT EXISTS idx_things_type_id_hash ON things(type, id, content_hash);
184
+ CREATE INDEX IF NOT EXISTS idx_relationships_from_type ON relationships(from_rowid, type);
185
+ CREATE INDEX IF NOT EXISTS idx_relationships_to_type ON relationships(to_rowid, type);
186
+ CREATE INDEX IF NOT EXISTS idx_actions_hash_status ON actions(input_hash, status);
187
+ CREATE INDEX IF NOT EXISTS idx_events_entity ON events(entity_type, entity_id);
188
+ CREATE INDEX IF NOT EXISTS idx_events_type_action ON events(type, action);
189
+ `;
190
+ var LocalDB = class {
191
+ db;
192
+ usePool;
193
+ path;
194
+ version = SCHEMA_VERSION;
195
+ things;
196
+ relationships;
197
+ actions;
198
+ events;
199
+ constructor(dbPath, options) {
200
+ this.path = dbPath || path.join(process.cwd(), ".db4", "local.db");
201
+ this.usePool = options?.usePool ?? false;
202
+ if (this.usePool) {
203
+ this.db = ConnectionPool.acquire(this.path);
204
+ } else {
205
+ ensureDirectoryExists(this.path);
206
+ try {
207
+ this.db = new Database(this.path);
208
+ } catch {
209
+ this.db = new Database(":memory:");
210
+ }
211
+ try {
212
+ this.db.pragma("journal_mode = WAL");
213
+ } catch {
214
+ }
215
+ }
216
+ this.initSchema();
217
+ this.things = new ThingsAPI(this.db);
218
+ this.relationships = new RelationshipsAPI(this.db);
219
+ this.actions = new ActionsAPI(this.db);
220
+ this.events = new EventsAPI(this.db);
221
+ }
222
+ initSchema() {
223
+ const tableCheck = this.db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='schema_version'`).get();
224
+ if (!tableCheck) {
225
+ this.db.exec(SCHEMA_SQL);
226
+ this.db.prepare("INSERT INTO schema_version (version) VALUES (?)").run(SCHEMA_VERSION);
227
+ } else {
228
+ this.runMigrations();
229
+ }
230
+ }
231
+ /**
232
+ * Run any pending database migrations.
233
+ */
234
+ runMigrations() {
235
+ const row = this.db.prepare("SELECT version FROM schema_version LIMIT 1").get();
236
+ const currentVersion = row?.version ?? 1;
237
+ if (currentVersion < SCHEMA_VERSION) {
238
+ if (currentVersion < 2) {
239
+ this.db.exec(MIGRATION_V1_TO_V2);
240
+ }
241
+ this.db.prepare("UPDATE schema_version SET version = ?").run(SCHEMA_VERSION);
242
+ }
243
+ }
244
+ /**
245
+ * Closes the database connection.
246
+ */
247
+ close() {
248
+ if (this.usePool) {
249
+ ConnectionPool.release(this.path);
250
+ } else {
251
+ this.db.close();
252
+ }
253
+ }
254
+ /**
255
+ * Runs VACUUM to reclaim disk space.
256
+ */
257
+ vacuum() {
258
+ this.db.exec("VACUUM");
259
+ return Promise.resolve();
260
+ }
261
+ };
262
+ var ThingsAPI = class {
263
+ constructor(db) {
264
+ this.db = db;
265
+ }
266
+ /**
267
+ * Upsert a thing. Returns existing rowid if content_hash matches,
268
+ * otherwise creates a new version.
269
+ */
270
+ async upsert(params) {
271
+ const { type, id, data, created_by_action } = params;
272
+ const dataJson = JSON.stringify(data);
273
+ const content_hash = computeHash({ type, id, data });
274
+ const created_at = Date.now();
275
+ const existing = this.db.prepare("SELECT rowid FROM things WHERE type = ? AND id = ? AND content_hash = ?").get(type, id, content_hash);
276
+ if (existing) {
277
+ return { rowid: existing.rowid, created: false };
278
+ }
279
+ const result = this.db.prepare(
280
+ `INSERT INTO things (type, id, data, content_hash, created_at, created_by_action)
281
+ VALUES (?, ?, ?, ?, ?, ?)`
282
+ ).run(type, id, dataJson, content_hash, created_at, created_by_action || null);
283
+ return { rowid: result.lastInsertRowid, created: true };
284
+ }
285
+ /**
286
+ * Get the latest version of a thing by type and id.
287
+ */
288
+ async get(params) {
289
+ const { type, id } = params;
290
+ const row = this.db.prepare(
291
+ `SELECT rowid, type, id, data, content_hash, created_at, created_by_action
292
+ FROM things
293
+ WHERE type = ? AND id = ?
294
+ ORDER BY rowid DESC
295
+ LIMIT 1`
296
+ ).get(type, id);
297
+ if (!row) return null;
298
+ return {
299
+ rowid: row.rowid,
300
+ type: row.type,
301
+ id: row.id,
302
+ data: JSON.parse(row.data),
303
+ content_hash: row.content_hash,
304
+ created_at: row.created_at,
305
+ created_by_action: row.created_by_action ?? void 0
306
+ };
307
+ }
308
+ /**
309
+ * Get a specific version by rowid.
310
+ */
311
+ async getByRowid(rowid) {
312
+ const row = this.db.prepare(
313
+ `SELECT rowid, type, id, data, content_hash, created_at, created_by_action
314
+ FROM things
315
+ WHERE rowid = ?`
316
+ ).get(rowid);
317
+ if (!row) return null;
318
+ return {
319
+ rowid: row.rowid,
320
+ type: row.type,
321
+ id: row.id,
322
+ data: JSON.parse(row.data),
323
+ content_hash: row.content_hash,
324
+ created_at: row.created_at,
325
+ created_by_action: row.created_by_action ?? void 0
326
+ };
327
+ }
328
+ /**
329
+ * List things with optional filters.
330
+ */
331
+ async list(params = {}) {
332
+ const { type, limit, offset, where, order = "asc" } = params;
333
+ let sql = `
334
+ SELECT t.rowid, t.type, t.id, t.data, t.content_hash, t.created_at, t.created_by_action
335
+ FROM things t
336
+ INNER JOIN (
337
+ SELECT type, id, MAX(rowid) as max_rowid
338
+ FROM things
339
+ ${type ? "WHERE type = ?" : ""}
340
+ GROUP BY type, id
341
+ ) latest ON t.rowid = latest.max_rowid
342
+ `;
343
+ const queryParams = [];
344
+ if (type) {
345
+ queryParams.push(type);
346
+ }
347
+ if (where && Object.keys(where).length > 0) {
348
+ const whereConditions = Object.keys(where).map((key) => {
349
+ const value = where[key];
350
+ queryParams.push(value);
351
+ return `json_extract(t.data, '$.${key}') = ?`;
352
+ });
353
+ sql += ` WHERE ${whereConditions.join(" AND ")}`;
354
+ }
355
+ sql += ` ORDER BY t.rowid ${order === "desc" ? "DESC" : "ASC"}`;
356
+ if (limit !== void 0) {
357
+ sql += ` LIMIT ?`;
358
+ queryParams.push(limit);
359
+ }
360
+ if (offset !== void 0) {
361
+ sql += ` OFFSET ?`;
362
+ queryParams.push(offset);
363
+ }
364
+ const rows = this.db.prepare(sql).all(...queryParams);
365
+ return rows.map((row) => ({
366
+ rowid: row.rowid,
367
+ type: row.type,
368
+ id: row.id,
369
+ data: JSON.parse(row.data),
370
+ content_hash: row.content_hash,
371
+ created_at: row.created_at,
372
+ created_by_action: row.created_by_action ?? void 0
373
+ }));
374
+ }
375
+ /**
376
+ * Delete a thing by rowid.
377
+ */
378
+ async delete(rowid) {
379
+ this.db.prepare("DELETE FROM things WHERE rowid = ?").run(rowid);
380
+ }
381
+ };
382
+ var RelationshipsAPI = class {
383
+ constructor(db) {
384
+ this.db = db;
385
+ }
386
+ /**
387
+ * Create or update a relationship edge.
388
+ * If (from_rowid, to_rowid, path) already exists, updates it.
389
+ */
390
+ async create(params) {
391
+ const { from_rowid, to_rowid, type, path: path2, label, created_by_action } = params;
392
+ const created_at = Date.now();
393
+ const existing = this.db.prepare(
394
+ `SELECT rowid FROM relationships
395
+ WHERE from_rowid = ? AND to_rowid = ? AND path = ?`
396
+ ).get(from_rowid, to_rowid, path2);
397
+ if (existing) {
398
+ this.db.prepare(
399
+ `UPDATE relationships
400
+ SET type = ?, label = ?, created_by_action = ?
401
+ WHERE rowid = ?`
402
+ ).run(type, label, created_by_action || null, existing.rowid);
403
+ const row = this.db.prepare(
404
+ `SELECT rowid, from_rowid, to_rowid, type, path, label, created_at, created_by_action
405
+ FROM relationships WHERE rowid = ?`
406
+ ).get(existing.rowid);
407
+ return {
408
+ rowid: row.rowid,
409
+ from_rowid: row.from_rowid,
410
+ to_rowid: row.to_rowid,
411
+ type: row.type,
412
+ path: row.path,
413
+ label: row.label,
414
+ created_at: row.created_at,
415
+ created_by_action: row.created_by_action ?? void 0
416
+ };
417
+ }
418
+ const result = this.db.prepare(
419
+ `INSERT INTO relationships (from_rowid, to_rowid, type, path, label, created_at, created_by_action)
420
+ VALUES (?, ?, ?, ?, ?, ?, ?)`
421
+ ).run(from_rowid, to_rowid, type, path2, label, created_at, created_by_action || null);
422
+ return {
423
+ rowid: result.lastInsertRowid,
424
+ from_rowid,
425
+ to_rowid,
426
+ type,
427
+ path: path2,
428
+ label,
429
+ created_at,
430
+ created_by_action
431
+ };
432
+ }
433
+ /**
434
+ * List all relationships.
435
+ */
436
+ async list() {
437
+ const rows = this.db.prepare(
438
+ `SELECT rowid, from_rowid, to_rowid, type, path, label, created_at, created_by_action
439
+ FROM relationships`
440
+ ).all();
441
+ return rows.map((row) => ({
442
+ rowid: row.rowid,
443
+ from_rowid: row.from_rowid,
444
+ to_rowid: row.to_rowid,
445
+ type: row.type,
446
+ path: row.path,
447
+ label: row.label,
448
+ created_at: row.created_at,
449
+ created_by_action: row.created_by_action ?? void 0
450
+ }));
451
+ }
452
+ /**
453
+ * Traverse forward from an entity.
454
+ */
455
+ async traverse(params) {
456
+ const { from_rowid, type } = params;
457
+ let sql = `
458
+ SELECT rowid, from_rowid, to_rowid, type, path, label, created_at, created_by_action
459
+ FROM relationships
460
+ WHERE from_rowid = ?
461
+ `;
462
+ const queryParams = [from_rowid];
463
+ if (type) {
464
+ sql += " AND type = ?";
465
+ queryParams.push(type);
466
+ }
467
+ const rows = this.db.prepare(sql).all(...queryParams);
468
+ return rows.map((row) => ({
469
+ rowid: row.rowid,
470
+ from_rowid: row.from_rowid,
471
+ to_rowid: row.to_rowid,
472
+ type: row.type,
473
+ path: row.path,
474
+ label: row.label,
475
+ created_at: row.created_at,
476
+ created_by_action: row.created_by_action ?? void 0
477
+ }));
478
+ }
479
+ /**
480
+ * Find incoming edges to an entity.
481
+ */
482
+ async incoming(params) {
483
+ const { to_rowid, type } = params;
484
+ let sql = `
485
+ SELECT rowid, from_rowid, to_rowid, type, path, label, created_at, created_by_action
486
+ FROM relationships
487
+ WHERE to_rowid = ?
488
+ `;
489
+ const queryParams = [to_rowid];
490
+ if (type) {
491
+ sql += " AND type = ?";
492
+ queryParams.push(type);
493
+ }
494
+ const rows = this.db.prepare(sql).all(...queryParams);
495
+ return rows.map((row) => ({
496
+ rowid: row.rowid,
497
+ from_rowid: row.from_rowid,
498
+ to_rowid: row.to_rowid,
499
+ type: row.type,
500
+ path: row.path,
501
+ label: row.label,
502
+ created_at: row.created_at,
503
+ created_by_action: row.created_by_action ?? void 0
504
+ }));
505
+ }
506
+ /**
507
+ * Traverse multiple hops from an entity (BFS).
508
+ */
509
+ async traverseMultiHop(params) {
510
+ const { from_rowid, depth, type } = params;
511
+ const visited = /* @__PURE__ */ new Set([from_rowid]);
512
+ const results = [];
513
+ let currentLevel = [from_rowid];
514
+ for (let d = 1; d <= depth && currentLevel.length > 0; d++) {
515
+ const nextLevel = [];
516
+ for (const nodeId of currentLevel) {
517
+ const edges = await this.traverse({ from_rowid: nodeId, type });
518
+ for (const edge of edges) {
519
+ if (!visited.has(edge.to_rowid)) {
520
+ visited.add(edge.to_rowid);
521
+ results.push({ to_rowid: edge.to_rowid, depth: d });
522
+ nextLevel.push(edge.to_rowid);
523
+ }
524
+ }
525
+ }
526
+ currentLevel = nextLevel;
527
+ }
528
+ return results;
529
+ }
530
+ };
531
+ var ActionsAPI = class {
532
+ constructor(db) {
533
+ this.db = db;
534
+ }
535
+ /**
536
+ * Create a new action in pending status.
537
+ */
538
+ async create(params) {
539
+ const { type, input, model } = params;
540
+ const inputJson = JSON.stringify(input);
541
+ const input_hash = computeHash(input);
542
+ const created_at = Date.now();
543
+ const result = this.db.prepare(
544
+ `INSERT INTO actions (type, input_hash, input, model, status, created_at)
545
+ VALUES (?, ?, ?, ?, 'pending', ?)`
546
+ ).run(type, input_hash, inputJson, model || null, created_at);
547
+ return {
548
+ rowid: result.lastInsertRowid,
549
+ type,
550
+ input_hash,
551
+ input,
552
+ model,
553
+ status: "pending",
554
+ created_at
555
+ };
556
+ }
557
+ /**
558
+ * Find a completed action by input hash (cache lookup).
559
+ * Only returns actions with status='complete'.
560
+ */
561
+ async findByHash(input_hash) {
562
+ const row = this.db.prepare(
563
+ `SELECT rowid, type, input_hash, input, output, model, status, error, duration_ms, cost_tokens, created_at
564
+ FROM actions
565
+ WHERE input_hash = ? AND status = 'complete'
566
+ ORDER BY rowid DESC
567
+ LIMIT 1`
568
+ ).get(input_hash);
569
+ if (!row) return null;
570
+ return {
571
+ rowid: row.rowid,
572
+ type: row.type,
573
+ input_hash: row.input_hash,
574
+ input: JSON.parse(row.input),
575
+ output: row.output ? JSON.parse(row.output) : void 0,
576
+ model: row.model ?? void 0,
577
+ status: row.status,
578
+ error: row.error ?? void 0,
579
+ duration_ms: row.duration_ms ?? void 0,
580
+ cost_tokens: row.cost_tokens ?? void 0,
581
+ created_at: row.created_at
582
+ };
583
+ }
584
+ /**
585
+ * Complete an action with output.
586
+ */
587
+ async complete(params) {
588
+ const { rowid, output, duration_ms, cost_tokens } = params;
589
+ const outputJson = JSON.stringify(output);
590
+ const existing = this.db.prepare("SELECT status FROM actions WHERE rowid = ?").get(rowid);
591
+ if (!existing) {
592
+ throw new Error(`Action with rowid ${rowid} not found`);
593
+ }
594
+ if (existing.status !== "pending") {
595
+ throw new Error(`Cannot complete action with status '${existing.status}'`);
596
+ }
597
+ this.db.prepare(
598
+ `UPDATE actions
599
+ SET status = 'complete', output = ?, duration_ms = ?, cost_tokens = ?
600
+ WHERE rowid = ?`
601
+ ).run(outputJson, duration_ms || null, cost_tokens || null, rowid);
602
+ const row = this.db.prepare(
603
+ `SELECT rowid, type, input_hash, input, output, model, status, error, duration_ms, cost_tokens, created_at
604
+ FROM actions WHERE rowid = ?`
605
+ ).get(rowid);
606
+ return {
607
+ rowid: row.rowid,
608
+ type: row.type,
609
+ input_hash: row.input_hash,
610
+ input: JSON.parse(row.input),
611
+ output: row.output ? JSON.parse(row.output) : void 0,
612
+ model: row.model ?? void 0,
613
+ status: row.status,
614
+ error: row.error ?? void 0,
615
+ duration_ms: row.duration_ms ?? void 0,
616
+ cost_tokens: row.cost_tokens ?? void 0,
617
+ created_at: row.created_at
618
+ };
619
+ }
620
+ /**
621
+ * Mark an action as failed.
622
+ */
623
+ async fail(params) {
624
+ const { rowid, error } = params;
625
+ const existing = this.db.prepare("SELECT status FROM actions WHERE rowid = ?").get(rowid);
626
+ if (!existing) {
627
+ throw new Error(`Action with rowid ${rowid} not found`);
628
+ }
629
+ if (existing.status !== "pending") {
630
+ throw new Error(`Cannot fail action with status '${existing.status}'`);
631
+ }
632
+ this.db.prepare(`UPDATE actions SET status = 'error', error = ? WHERE rowid = ?`).run(error, rowid);
633
+ const row = this.db.prepare(
634
+ `SELECT rowid, type, input_hash, input, output, model, status, error, duration_ms, cost_tokens, created_at
635
+ FROM actions WHERE rowid = ?`
636
+ ).get(rowid);
637
+ return {
638
+ rowid: row.rowid,
639
+ type: row.type,
640
+ input_hash: row.input_hash,
641
+ input: JSON.parse(row.input),
642
+ output: row.output ? JSON.parse(row.output) : void 0,
643
+ model: row.model ?? void 0,
644
+ status: row.status,
645
+ error: row.error ?? void 0,
646
+ duration_ms: row.duration_ms ?? void 0,
647
+ cost_tokens: row.cost_tokens ?? void 0,
648
+ created_at: row.created_at
649
+ };
650
+ }
651
+ };
652
+ var EventsAPI = class {
653
+ constructor(db) {
654
+ this.db = db;
655
+ }
656
+ /**
657
+ * Emit an event.
658
+ */
659
+ async emit(params) {
660
+ const {
661
+ type,
662
+ action,
663
+ entity_type,
664
+ entity_id,
665
+ entity_rowid,
666
+ data,
667
+ changes,
668
+ action_id,
669
+ correlation_id
670
+ } = params;
671
+ const timestamp = Date.now();
672
+ const dataJson = JSON.stringify(data);
673
+ const changesJson = changes ? JSON.stringify(changes) : null;
674
+ const result = this.db.prepare(
675
+ `INSERT INTO events (type, action, entity_type, entity_id, entity_rowid, data, changes, timestamp, action_id, correlation_id)
676
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
677
+ ).run(
678
+ type,
679
+ action,
680
+ entity_type,
681
+ entity_id,
682
+ entity_rowid,
683
+ dataJson,
684
+ changesJson,
685
+ timestamp,
686
+ action_id || null,
687
+ correlation_id || null
688
+ );
689
+ return {
690
+ rowid: result.lastInsertRowid,
691
+ type,
692
+ action,
693
+ entity_type,
694
+ entity_id,
695
+ entity_rowid,
696
+ data,
697
+ changes,
698
+ timestamp,
699
+ action_id,
700
+ correlation_id
701
+ };
702
+ }
703
+ /**
704
+ * List events with optional filters.
705
+ */
706
+ async list(params = {}) {
707
+ const { type, action, since, correlation_id, limit } = params;
708
+ let sql = `
709
+ SELECT rowid, type, action, entity_type, entity_id, entity_rowid, data, changes, timestamp, action_id, correlation_id
710
+ FROM events
711
+ WHERE 1=1
712
+ `;
713
+ const queryParams = [];
714
+ if (type) {
715
+ sql += " AND type = ?";
716
+ queryParams.push(type);
717
+ }
718
+ if (action) {
719
+ sql += " AND action = ?";
720
+ queryParams.push(action);
721
+ }
722
+ if (since !== void 0) {
723
+ sql += " AND timestamp > ?";
724
+ queryParams.push(since);
725
+ }
726
+ if (correlation_id) {
727
+ sql += " AND correlation_id = ?";
728
+ queryParams.push(correlation_id);
729
+ }
730
+ sql += " ORDER BY timestamp ASC";
731
+ if (limit !== void 0) {
732
+ sql += " LIMIT ?";
733
+ queryParams.push(limit);
734
+ }
735
+ const rows = this.db.prepare(sql).all(...queryParams);
736
+ return rows.map((row) => ({
737
+ rowid: row.rowid,
738
+ type: row.type,
739
+ action: row.action,
740
+ entity_type: row.entity_type,
741
+ entity_id: row.entity_id,
742
+ entity_rowid: row.entity_rowid,
743
+ data: JSON.parse(row.data),
744
+ changes: row.changes ? JSON.parse(row.changes) : void 0,
745
+ timestamp: row.timestamp,
746
+ action_id: row.action_id ?? void 0,
747
+ correlation_id: row.correlation_id ?? void 0
748
+ }));
749
+ }
750
+ };
751
+ var runtime_default = LocalDB;
752
+
753
+ export {
754
+ ConnectionPool,
755
+ LocalDB,
756
+ runtime_default
757
+ };
758
+ //# sourceMappingURL=chunk-FUF4HJTC.js.map