@knowzai/local-store 0.1.0

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,342 @@
1
+ import { supportsOutbox } from './types.js';
2
+ import { contentHash } from './hash.js';
3
+ function emptyReport() {
4
+ return { pulled: 0, pushed: 0, conflicts: [], skipped: 0 };
5
+ }
6
+ export class SyncEngine {
7
+ store;
8
+ client;
9
+ encryptor;
10
+ constructor(store, client, opts = {}) {
11
+ this.store = store;
12
+ this.client = client;
13
+ this.encryptor = opts.encryptor;
14
+ }
15
+ /**
16
+ * Iterate EVERY page of `knowledge.list` for a vault, invoking `onSummary`
17
+ * per remote item. Stops when the cumulative count reaches `total` OR a page
18
+ * returns fewer than `pageSize` items (defensive against a 0/undefined total),
19
+ * so the full remote set is always covered without an infinite loop.
20
+ */
21
+ async forEachRemote(opts, onSummary) {
22
+ const pageSize = opts.pageSize;
23
+ let page = 1;
24
+ let fetched = 0;
25
+ for (;;) {
26
+ const result = await this.client.knowledge.list({ vaultId: opts.vaultId, page, pageSize });
27
+ const items = result.items ?? [];
28
+ for (const summary of items)
29
+ await onSummary(summary);
30
+ fetched += items.length;
31
+ const total = result.total;
32
+ const reachedTotal = typeof total === 'number' && total >= 0 && fetched >= total;
33
+ if (items.length < pageSize || reachedTotal)
34
+ break;
35
+ page++;
36
+ }
37
+ }
38
+ /** Cloud → local. Upserts remote items; flags conflicts; skips unchanged. */
39
+ async pull(opts = {}) {
40
+ const report = emptyReport();
41
+ await this.forEachRemote({ vaultId: opts.vaultId, pageSize: opts.pageSize ?? 100 }, async (summary) => {
42
+ const detail = await this.client.knowledge.get(summary.id);
43
+ // Model-3: decrypt ciphertext fetched from the cloud into local plaintext.
44
+ let content = detail.content;
45
+ if (content && this.encryptor?.isEncrypted(content))
46
+ content = this.encryptor.decrypt(content);
47
+ const hash = contentHash(content, detail.title, detail.tags ?? []);
48
+ const local = await this.store.getItem(detail.id);
49
+ if (local && local.contentHash === hash && local.syncState === 'synced') {
50
+ report.skipped++;
51
+ return;
52
+ }
53
+ if (local &&
54
+ local.syncState === 'dirty' &&
55
+ local.remoteVersion != null &&
56
+ detail.version > local.remoteVersion) {
57
+ await this.store.upsertItem({ ...local, syncState: 'conflict' });
58
+ report.conflicts.push(detail.id);
59
+ return;
60
+ }
61
+ await this.store.upsertItem({
62
+ id: detail.id,
63
+ vaultId: detail.vaultId,
64
+ title: detail.title,
65
+ type: detail.type,
66
+ contentHash: hash,
67
+ content,
68
+ tags: detail.tags ?? [],
69
+ updatedAt: detail.updatedAt,
70
+ syncState: 'synced',
71
+ remoteVersion: detail.version,
72
+ });
73
+ report.pulled++;
74
+ });
75
+ return report;
76
+ }
77
+ /** Local dirty/new → cloud. Clears dirty + records remoteVersion. */
78
+ async push(opts = {}) {
79
+ const report = emptyReport();
80
+ const pending = [
81
+ ...(await this.store.listItems({ syncState: 'new', vaultId: opts.vaultId })),
82
+ ...(await this.store.listItems({ syncState: 'dirty', vaultId: opts.vaultId })),
83
+ ];
84
+ for (const it of pending) {
85
+ // Model-3: encrypt before the content leaves the machine.
86
+ const wire = it.content != null && this.encryptor ? this.encryptor.encrypt(it.content) : it.content;
87
+ if (it.remoteVersion == null) {
88
+ const created = await this.client.knowledge.create({
89
+ vaultId: it.vaultId,
90
+ title: it.title,
91
+ content: wire,
92
+ type: it.type,
93
+ tags: it.tags,
94
+ });
95
+ if (created.id !== it.id)
96
+ await this.store.reassignItemId(it.id, created.id);
97
+ await this.store.upsertItem({
98
+ ...it,
99
+ id: created.id,
100
+ syncState: 'synced',
101
+ remoteVersion: created.version,
102
+ // hash the LOCAL plaintext (server echoes ciphertext under CMEK).
103
+ contentHash: contentHash(it.content, it.title, it.tags),
104
+ });
105
+ }
106
+ else {
107
+ // F26 — precondition guard. The API can't yet supply a monotonic
108
+ // version on create/update (create() returns 0, update() falls back to
109
+ // 0), so version-based conflict detection on pull/recover is effectively
110
+ // dead. Re-fetch the remote before overwriting: if it advanced past the
111
+ // version this local copy last saw, mark a conflict instead of silently
112
+ // clobbering the concurrent remote edit.
113
+ if (it.remoteVersion != null) {
114
+ const current = await this.client.knowledge.get(it.id);
115
+ if (current.version > it.remoteVersion) {
116
+ await this.store.upsertItem({ ...it, syncState: 'conflict' });
117
+ report.conflicts.push(it.id);
118
+ continue;
119
+ }
120
+ }
121
+ const updated = await this.client.knowledge.update(it.id, {
122
+ title: it.title,
123
+ content: wire,
124
+ tags: it.tags,
125
+ vaultId: it.vaultId,
126
+ });
127
+ await this.store.upsertItem({
128
+ ...it,
129
+ syncState: 'synced',
130
+ remoteVersion: updated.version,
131
+ contentHash: contentHash(it.content, it.title, it.tags),
132
+ });
133
+ }
134
+ report.pushed++;
135
+ }
136
+ return report;
137
+ }
138
+ async run(opts = {}) {
139
+ const a = await this.pull(opts);
140
+ const b = await this.push(opts);
141
+ return {
142
+ pulled: a.pulled,
143
+ pushed: b.pushed,
144
+ conflicts: [...a.conflicts, ...b.conflicts],
145
+ skipped: a.skipped + b.skipped,
146
+ };
147
+ }
148
+ async status() {
149
+ const all = await this.store.listItems();
150
+ const by = (s) => all.filter((i) => i.syncState === s).length;
151
+ return {
152
+ total: all.length,
153
+ new: by('new'),
154
+ dirty: by('dirty'),
155
+ synced: by('synced'),
156
+ conflict: by('conflict'),
157
+ deleted: by('deleted'),
158
+ };
159
+ }
160
+ /** Items currently in conflict (local + remote both changed). */
161
+ async listConflicts() {
162
+ return this.store.listItems({ syncState: 'conflict' });
163
+ }
164
+ /** Resolve one conflicted item by keeping the remote copy or the local copy. */
165
+ async resolveConflict(id, strategy) {
166
+ const local = await this.store.getItem(id);
167
+ if (!local)
168
+ return 'not-found';
169
+ if (strategy === 'remote') {
170
+ const detail = await this.client.knowledge.get(id);
171
+ let content = detail.content;
172
+ if (content && this.encryptor?.isEncrypted(content))
173
+ content = this.encryptor.decrypt(content);
174
+ await this.store.upsertItem({
175
+ id: detail.id,
176
+ vaultId: detail.vaultId,
177
+ title: detail.title,
178
+ type: detail.type,
179
+ contentHash: contentHash(content, detail.title, detail.tags ?? []),
180
+ content,
181
+ tags: detail.tags ?? [],
182
+ updatedAt: detail.updatedAt,
183
+ syncState: 'synced',
184
+ remoteVersion: detail.version,
185
+ });
186
+ return 'updated-from-remote';
187
+ }
188
+ // keep local → mark dirty so the next push overwrites the remote (last-writer)
189
+ await this.store.upsertItem({ ...local, syncState: 'dirty' });
190
+ return 'kept-local';
191
+ }
192
+ /**
193
+ * Recovery: re-pull authoritative cloud state to repair the local store
194
+ * (overwrites non-dirty items, preserves dirty-vs-advanced as conflicts) and
195
+ * report any local `synced` items no longer present remotely (orphans).
196
+ */
197
+ async recover(opts = {}) {
198
+ const report = { repaired: 0, conflicts: [], orphanedLocal: [] };
199
+ // F3 — repair + collect remoteIds across ALL pages BEFORE the orphan sweep,
200
+ // else a local synced item that only lives on page 2+ is wrongly reported
201
+ // as orphaned.
202
+ const remoteIds = new Set();
203
+ await this.forEachRemote({ vaultId: opts.vaultId, pageSize: opts.pageSize ?? 200 }, async (summary) => {
204
+ remoteIds.add(summary.id);
205
+ const detail = await this.client.knowledge.get(summary.id);
206
+ let content = detail.content;
207
+ if (content && this.encryptor?.isEncrypted(content))
208
+ content = this.encryptor.decrypt(content);
209
+ const local = await this.store.getItem(detail.id);
210
+ if (local?.syncState === 'dirty' && local.remoteVersion != null && detail.version > local.remoteVersion) {
211
+ await this.store.upsertItem({ ...local, syncState: 'conflict' });
212
+ report.conflicts.push(detail.id);
213
+ return;
214
+ }
215
+ await this.store.upsertItem({
216
+ id: detail.id,
217
+ vaultId: detail.vaultId,
218
+ title: detail.title,
219
+ type: detail.type,
220
+ contentHash: contentHash(content, detail.title, detail.tags ?? []),
221
+ content,
222
+ tags: detail.tags ?? [],
223
+ updatedAt: detail.updatedAt,
224
+ syncState: 'synced',
225
+ remoteVersion: detail.version,
226
+ });
227
+ report.repaired++;
228
+ });
229
+ // orphan sweep only after the FULL remote set is known
230
+ for (const it of await this.store.listItems({ syncState: 'synced' })) {
231
+ if (!remoteIds.has(it.id))
232
+ report.orphanedLocal.push(it.id);
233
+ }
234
+ return report;
235
+ }
236
+ /**
237
+ * Drain the capture outbox to the cloud (FEAT_AgentMemoryLocalOutbox).
238
+ *
239
+ * Each claimed entry's sealed content lives in `items`; we create it via the
240
+ * existing `knowledge.create` path (the batch endpoint is not yet shipped),
241
+ * stamping the idempotency key into item metadata for best-effort server-side
242
+ * dedup. Idempotency is enforced LOCALLY: a `done` entry is never reclaimed,
243
+ * and re-enqueue is a no-op on the idempotency-key PK. The only double-create
244
+ * window is "create succeeded but its ack was lost before we marked done" —
245
+ * acceptable until the content-hash-dedup batch endpoint lands.
246
+ *
247
+ * Resilience: claims `pending` (whose backoff elapsed) plus `inflight` whose
248
+ * lease expired (crash recovery). Transient failures back off exponentially;
249
+ * after `maxAttempts` an entry dead-letters (visible via `outboxStats`). This
250
+ * is what makes the orphaned-pending-captures leak structurally impossible —
251
+ * a capture is a committed row drained by an idempotent, retrying consumer.
252
+ */
253
+ async flushOutbox(opts = {}) {
254
+ if (!supportsOutbox(this.store)) {
255
+ throw new Error('flushOutbox requires a store that supports the capture outbox (use the default SQLite store).');
256
+ }
257
+ const store = this.store;
258
+ const nowFn = opts.now ?? (() => Date.now());
259
+ const limit = opts.limit ?? 100;
260
+ const leaseMs = opts.leaseMs ?? 60_000;
261
+ const maxAttempts = opts.maxAttempts ?? 5;
262
+ const baseBackoff = opts.baseBackoffMs ?? 1_000;
263
+ const maxBackoff = opts.maxBackoffMs ?? 5 * 60_000;
264
+ const report = { claimed: 0, sent: 0, failed: 0, dead: 0, conflicts: [] };
265
+ const nowMs = nowFn();
266
+ const nowIso = new Date(nowMs).toISOString();
267
+ const leaseIso = new Date(nowMs + leaseMs).toISOString();
268
+ const claimed = await store.claimOutbox({ now: nowIso, leaseUntil: leaseIso, limit });
269
+ report.claimed = claimed.length;
270
+ for (const entry of claimed) {
271
+ const item = await this.store.getItem(entry.itemId);
272
+ if (!item) {
273
+ await store.updateOutbox(entry.idempotencyKey, {
274
+ state: 'dead',
275
+ attempts: entry.attempts + 1,
276
+ lastAttemptAt: nowIso,
277
+ lastError: 'orphaned: sealed item not found',
278
+ });
279
+ report.dead++;
280
+ continue;
281
+ }
282
+ try {
283
+ const wire = item.content != null && this.encryptor ? this.encryptor.encrypt(item.content) : item.content;
284
+ const created = await this.client.knowledge.create({
285
+ vaultId: item.vaultId,
286
+ title: item.title,
287
+ content: wire,
288
+ type: item.type,
289
+ tags: item.tags,
290
+ metadata: {
291
+ 'knowz-agent-key': entry.idempotencyKey,
292
+ 'knowz-agent-session': entry.sessionId,
293
+ ...(opts.source ? { 'knowz-agent-source': opts.source } : {}),
294
+ },
295
+ });
296
+ if (created.id !== item.id)
297
+ await this.store.reassignItemId(item.id, created.id);
298
+ await this.store.upsertItem({
299
+ ...item,
300
+ id: created.id,
301
+ syncState: 'synced',
302
+ remoteVersion: created.version,
303
+ contentHash: contentHash(item.content, item.title, item.tags),
304
+ });
305
+ await store.updateOutbox(entry.idempotencyKey, {
306
+ state: 'done',
307
+ attempts: entry.attempts + 1,
308
+ lastAttemptAt: nowIso,
309
+ lastError: undefined,
310
+ itemId: created.id,
311
+ });
312
+ report.sent++;
313
+ }
314
+ catch (err) {
315
+ const attempts = entry.attempts + 1;
316
+ const msg = err instanceof Error ? err.message : String(err);
317
+ if (attempts >= maxAttempts) {
318
+ await store.updateOutbox(entry.idempotencyKey, {
319
+ state: 'dead',
320
+ attempts,
321
+ lastAttemptAt: nowIso,
322
+ lastError: msg,
323
+ });
324
+ report.dead++;
325
+ }
326
+ else {
327
+ const backoff = Math.min(maxBackoff, baseBackoff * 2 ** (attempts - 1));
328
+ await store.updateOutbox(entry.idempotencyKey, {
329
+ state: 'pending',
330
+ attempts,
331
+ lastAttemptAt: nowIso,
332
+ lastError: msg,
333
+ leaseUntil: new Date(nowMs + backoff).toISOString(),
334
+ });
335
+ report.failed++;
336
+ }
337
+ }
338
+ }
339
+ return report;
340
+ }
341
+ }
342
+ //# sourceMappingURL=sync-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-engine.js","sourceRoot":"","sources":["../src/sync-engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,150 @@
1
+ export type SyncState = 'new' | 'synced' | 'dirty' | 'conflict' | 'deleted';
2
+ export interface LocalItem {
3
+ id: string;
4
+ vaultId?: string;
5
+ title: string;
6
+ type: string;
7
+ contentHash: string;
8
+ content?: string;
9
+ tags: string[];
10
+ updatedAt: string;
11
+ syncState: SyncState;
12
+ remoteVersion?: number;
13
+ }
14
+ export interface Chunk {
15
+ id: string;
16
+ itemId: string;
17
+ ordinal: number;
18
+ text: string;
19
+ startByte?: number;
20
+ endByte?: number;
21
+ lang?: string;
22
+ }
23
+ export interface FileState {
24
+ path: string;
25
+ hash: string;
26
+ mtimeMs: number;
27
+ size: number;
28
+ itemId?: string;
29
+ lastIndexedAt?: string;
30
+ }
31
+ export interface KnnHit {
32
+ chunk: Chunk;
33
+ distance: number;
34
+ }
35
+ export interface LocalStore {
36
+ readonly driver: 'sqlite' | 'postgres';
37
+ init(): Promise<void>;
38
+ upsertItem(item: LocalItem): Promise<void>;
39
+ getItem(id: string): Promise<LocalItem | undefined>;
40
+ reassignItemId(oldId: string, newId: string): Promise<void>;
41
+ listItems(filter?: {
42
+ vaultId?: string;
43
+ syncState?: SyncState;
44
+ }): Promise<LocalItem[]>;
45
+ deleteItem(id: string): Promise<void>;
46
+ putChunks(itemId: string, chunks: Chunk[], vectors?: Float32Array[]): Promise<void>;
47
+ knnSearch(queryVec: Float32Array, k: number, filter?: {
48
+ vaultId?: string;
49
+ }): Promise<KnnHit[]>;
50
+ setFileState(s: FileState): Promise<void>;
51
+ getFileState(path: string): Promise<FileState | undefined>;
52
+ listFileStates(): Promise<FileState[]>;
53
+ meta(): Promise<{
54
+ schemaVersion: number;
55
+ dim: number;
56
+ itemCount: number;
57
+ chunkCount: number;
58
+ }>;
59
+ close(): Promise<void>;
60
+ }
61
+ export interface OpenStoreOptions {
62
+ driver?: 'sqlite' | 'postgres';
63
+ dbPath?: string;
64
+ dsn?: string;
65
+ dim?: number;
66
+ }
67
+ export declare const SCHEMA_VERSION = 2;
68
+ export declare const DEFAULT_DIM = 384;
69
+ /**
70
+ * Map an L2 distance between L2-normalized vectors to a 0..1 cosine-similarity
71
+ * "relevance". For unit vectors L2² = 2(1 − cos), so cos = 1 − d²/2. Identical
72
+ * vectors → 1; orthogonal (d=√2) → 0. Use this for any "% match" display.
73
+ */
74
+ export declare function relevanceFromDistance(distance: number): number;
75
+ /** Lifecycle of a capture_outbox entry. `done`/`dead` are terminal. */
76
+ export type OutboxState = 'pending' | 'inflight' | 'done' | 'dead' | 'conflict';
77
+ /** A raw, already-redacted observation of one tool-use / agent event. */
78
+ export interface Observation {
79
+ id: string;
80
+ sessionId: string;
81
+ /** ISO timestamp. */
82
+ ts: string;
83
+ /** Event kind, e.g. 'tool', 'prompt', 'precompact'. */
84
+ kind: string;
85
+ toolName?: string;
86
+ /** One-line, redaction-safe summary. Never raw secret-bearing payload. */
87
+ summary: string;
88
+ /** True once the source payload passed through the redactor. */
89
+ redacted: boolean;
90
+ vaultId?: string;
91
+ /** Set once this observation has been rolled into a sealed `items` row. */
92
+ sealedIntoItemId?: string;
93
+ }
94
+ /** Durable delivery record for one sealed observation item bound for the cloud. */
95
+ export interface OutboxEntry {
96
+ /** sha256(sessionId:seq:contentHash) — stable PK, dedupes re-enqueue. */
97
+ idempotencyKey: string;
98
+ /** FK → items.id (the sealed observation content). */
99
+ itemId: string;
100
+ sessionId: string;
101
+ createdAt: string;
102
+ attempts: number;
103
+ lastAttemptAt?: string;
104
+ state: OutboxState;
105
+ lastError?: string;
106
+ /** ISO not-before: claim ignores entries leased into the future. */
107
+ leaseUntil?: string;
108
+ }
109
+ export interface OutboxStats {
110
+ pending: number;
111
+ inflight: number;
112
+ done: number;
113
+ dead: number;
114
+ conflict: number;
115
+ }
116
+ /**
117
+ * Capture/outbox surface, implemented by SqliteStore (the default local store).
118
+ * Kept separate from LocalStore so the Postgres driver is unaffected for now
119
+ * (agent-memory capture targets the default SQLite store; pg outbox is a
120
+ * follow-up). Use `supportsOutbox(store)` to narrow a LocalStore safely.
121
+ */
122
+ export interface ObservationOutboxStore {
123
+ recordObservation(o: Observation): Promise<void>;
124
+ listObservations(filter?: {
125
+ sessionId?: string;
126
+ sealed?: boolean;
127
+ }): Promise<Observation[]>;
128
+ markObservationsSealed(ids: string[], itemId: string): Promise<void>;
129
+ /** Insert-or-ignore on idempotencyKey, so a re-enqueue is a no-op. */
130
+ enqueueOutbox(e: OutboxEntry): Promise<void>;
131
+ /**
132
+ * Atomically claim up to `limit` deliverable entries — `pending` whose lease
133
+ * has elapsed plus `inflight` whose lease expired (crash recovery) — flipping
134
+ * each to `inflight` with a fresh lease. Returns the claimed entries.
135
+ */
136
+ claimOutbox(opts: {
137
+ now: string;
138
+ leaseUntil: string;
139
+ limit: number;
140
+ }): Promise<OutboxEntry[]>;
141
+ updateOutbox(key: string, patch: Partial<Omit<OutboxEntry, 'idempotencyKey'>>): Promise<void>;
142
+ getOutbox(key: string): Promise<OutboxEntry | undefined>;
143
+ listOutbox(filter?: {
144
+ state?: OutboxState;
145
+ }): Promise<OutboxEntry[]>;
146
+ outboxStats(): Promise<OutboxStats>;
147
+ }
148
+ /** Runtime narrow: does this LocalStore also expose the capture/outbox surface? */
149
+ export declare function supportsOutbox(store: unknown): store is LocalStore & ObservationOutboxStore;
150
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,21 @@
1
+ export const SCHEMA_VERSION = 2;
2
+ // Local store holds LOCALLY-computed embeddings only (the local hashing
3
+ // embedder, dim 384). Cloud (1536-dim) vectors are never stored here — synced
4
+ // content is re-embedded locally for KNN. Keep this aligned with the indexer's
5
+ // DEFAULT_EMBED_DIM so every command opens the store at the same dim.
6
+ export const DEFAULT_DIM = 384;
7
+ /**
8
+ * Map an L2 distance between L2-normalized vectors to a 0..1 cosine-similarity
9
+ * "relevance". For unit vectors L2² = 2(1 − cos), so cos = 1 − d²/2. Identical
10
+ * vectors → 1; orthogonal (d=√2) → 0. Use this for any "% match" display.
11
+ */
12
+ export function relevanceFromDistance(distance) {
13
+ return Math.max(0, Math.min(1, 1 - (distance * distance) / 2));
14
+ }
15
+ /** Runtime narrow: does this LocalStore also expose the capture/outbox surface? */
16
+ export function supportsOutbox(store) {
17
+ return (!!store &&
18
+ typeof store.claimOutbox === 'function' &&
19
+ typeof store.enqueueOutbox === 'function');
20
+ }
21
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA+DA,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAChC,wEAAwE;AACxE,8EAA8E;AAC9E,+EAA+E;AAC/E,sEAAsE;AACtE,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAE/B;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACjE,CAAC;AA+ED,mFAAmF;AACnF,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,CACL,CAAC,CAAC,KAAK;QACP,OAAQ,KAAgC,CAAC,WAAW,KAAK,UAAU;QACnE,OAAQ,KAAgC,CAAC,aAAa,KAAK,UAAU,CACtE,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@knowzai/local-store",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.js"
9
+ }
10
+ },
11
+ "main": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "dependencies": {
17
+ "better-sqlite3": "^11.7.0",
18
+ "sqlite-vec": "^0.1.6",
19
+ "@knowzai/types": "0.1.0",
20
+ "@knowzai/api-client": "0.1.0"
21
+ },
22
+ "optionalDependencies": {
23
+ "pg": "^8.13.1"
24
+ },
25
+ "license": "MIT",
26
+ "author": "Knowz",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/knowz-io/knowz-platform.git",
30
+ "directory": "cli/local-store"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "engines": {
36
+ "node": ">=20"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc -b"
40
+ }
41
+ }