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