opencode-mem 2.8.4 → 2.8.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AA8ZA,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;oBA2Bb,aAAa,GACb,kBAAkB,GAClB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAwCT,OAAO,GACP,QAAQ;;CAEf,CAAC;AAEF,wBAAgB,YAAY,IAAI,OAAO,CAEtC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAobA,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;oBA2Bb,aAAa,GACb,kBAAkB,GAClB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAwCT,OAAO,GACP,QAAQ;;CAEf,CAAC;AAEF,wBAAgB,YAAY,IAAI,OAAO,CAEtC"}
package/dist/config.js CHANGED
@@ -126,6 +126,7 @@ const CONFIG_TEMPLATE = `{
126
126
  // Optional: Use OpenAI-compatible API for embeddings
127
127
  // "embeddingApiUrl": "https://api.openai.com/v1",
128
128
  // "embeddingApiKey": "sk-...",
129
+ // "embeddingModel": "text-embedding-3-small", // 1536 dims, auto-detected
129
130
 
130
131
  // ============================================
131
132
  // Web Server Settings
@@ -302,6 +303,7 @@ function ensureConfigExists() {
302
303
  ensureConfigExists();
303
304
  function getEmbeddingDimensions(model) {
304
305
  const dimensionMap = {
306
+ // Local Xenova models
305
307
  "Xenova/nomic-embed-text-v1": 768,
306
308
  "Xenova/nomic-embed-text-v1-unsupervised": 768,
307
309
  "Xenova/nomic-embed-text-v1-ablated": 768,
@@ -317,6 +319,22 @@ function getEmbeddingDimensions(model) {
317
319
  "Xenova/gte-small": 384,
318
320
  "Xenova/GIST-small-Embedding-v0": 384,
319
321
  "Xenova/text-embedding-ada-002": 1536,
322
+ // OpenAI API models
323
+ "text-embedding-3-small": 1536,
324
+ "text-embedding-3-large": 3072,
325
+ "text-embedding-ada-002": 1536,
326
+ // Cohere API models
327
+ "embed-english-v3.0": 1024,
328
+ "embed-multilingual-v3.0": 1024,
329
+ "embed-english-light-v3.0": 384,
330
+ "embed-multilingual-light-v3.0": 384,
331
+ // Google API models
332
+ "text-embedding-004": 768,
333
+ "text-multilingual-embedding-002": 768,
334
+ // Voyage AI models
335
+ "voyage-3": 1024,
336
+ "voyage-3-lite": 512,
337
+ "voyage-code-3": 1024,
320
338
  };
321
339
  return dimensionMap[model] || 768;
322
340
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ai-session-manager.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/session/ai-session-manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,SAAS,EACT,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACV,MAAM,oBAAoB,CAAC;AAM5B,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;;IAS5C,OAAO,CAAC,YAAY;IAyCpB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI;IAYzE,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,SAAS;IA2BrD,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI;IA8B9F,sBAAsB,IAAI,MAAM;IAKhC,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI;IAOhE,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,IAAI;IAmB9D,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE;IAS7C,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAS5C,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAIxC,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,YAAY;CAarB;AAED,eAAO,MAAM,gBAAgB,kBAAyB,CAAC"}
1
+ {"version":3,"file":"ai-session-manager.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/session/ai-session-manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,SAAS,EACT,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACV,MAAM,oBAAoB,CAAC;AAS5B,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,EAAE,CAAe;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;;IAS5C,OAAO,CAAC,YAAY;IAyCpB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI;IAYzE,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,SAAS;IA2BrD,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI;IA8B9F,sBAAsB,IAAI,MAAM;IAKhC,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI;IAOhE,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,IAAI;IAmB9D,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE;IAS7C,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAS5C,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAIxC,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,YAAY;CAarB;AAED,eAAO,MAAM,gBAAgB,kBAAyB,CAAC"}
@@ -1,7 +1,8 @@
1
- import { Database } from "bun:sqlite";
1
+ import { getDatabase } from "../../sqlite/sqlite-bootstrap.js";
2
2
  import { join } from "node:path";
3
3
  import { connectionManager } from "../../sqlite/connection-manager.js";
4
4
  import { CONFIG } from "../../../config.js";
5
+ const Database = getDatabase();
5
6
  const AI_SESSIONS_DB_NAME = "ai-sessions.db";
6
7
  export class AISessionManager {
7
8
  db;
@@ -1,14 +1,13 @@
1
- import { Database } from "bun:sqlite";
1
+ declare const Database: typeof import("bun:sqlite").Database;
2
2
  export declare class ConnectionManager {
3
3
  private connections;
4
- private sqliteConfigured;
5
- private configureSqlite;
6
4
  private initDatabase;
7
5
  private migrateSchema;
8
- getConnection(dbPath: string): Database;
6
+ getConnection(dbPath: string): typeof Database.prototype;
9
7
  closeConnection(dbPath: string): void;
10
8
  closeAll(): void;
11
9
  checkpointAll(): void;
12
10
  }
13
11
  export declare const connectionManager: ConnectionManager;
12
+ export {};
14
13
  //# sourceMappingURL=connection-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"connection-manager.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/connection-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAOtC,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAAoC;IACvD,OAAO,CAAC,gBAAgB,CAAS;IAEjC,OAAO,CAAC,eAAe;IA2EvB,OAAO,CAAC,YAAY;IAwBpB,OAAO,CAAC,aAAa;IAoBrB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAmBvC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IASrC,QAAQ,IAAI,IAAI;IAYhB,aAAa,IAAI,IAAI;CAStB;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
1
+ {"version":3,"file":"connection-manager.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/connection-manager.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,QAAQ,sCAAgB,CAAC;AAE/B,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAAqD;IAExE,OAAO,CAAC,YAAY;IAwBpB,OAAO,CAAC,aAAa;IAoBrB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,QAAQ,CAAC,SAAS;IAiBxD,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IASrC,QAAQ,IAAI,IAAI;IAYhB,aAAa,IAAI,IAAI;CAStB;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
@@ -1,81 +1,12 @@
1
- import { Database } from "bun:sqlite";
1
+ import { getDatabase } from "./sqlite-bootstrap.js";
2
2
  import * as sqliteVec from "sqlite-vec";
3
3
  import { existsSync, mkdirSync } from "node:fs";
4
4
  import { dirname } from "node:path";
5
5
  import { log } from "../logger.js";
6
6
  import { CONFIG } from "../../config.js";
7
+ const Database = getDatabase();
7
8
  export class ConnectionManager {
8
9
  connections = new Map();
9
- sqliteConfigured = false;
10
- configureSqlite() {
11
- if (this.sqliteConfigured)
12
- return;
13
- if (process.platform === "darwin") {
14
- const customPath = CONFIG.customSqlitePath;
15
- if (customPath) {
16
- if (!existsSync(customPath)) {
17
- throw new Error(`Custom SQLite library not found at: ${customPath}\n` +
18
- `Please verify the path or install Homebrew SQLite:\n` +
19
- ` brew install sqlite\n` +
20
- ` brew --prefix sqlite`);
21
- }
22
- try {
23
- Database.setCustomSQLite(customPath);
24
- }
25
- catch (error) {
26
- const errorStr = String(error);
27
- if (errorStr.includes("SQLite already loaded")) {
28
- }
29
- else {
30
- throw new Error(`Failed to load custom SQLite library: ${error}\n` + `Path: ${customPath}`);
31
- }
32
- }
33
- }
34
- else {
35
- const commonPaths = [
36
- "/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib",
37
- "/usr/local/opt/sqlite/lib/libsqlite3.dylib",
38
- ];
39
- let foundPath = null;
40
- for (const path of commonPaths) {
41
- if (existsSync(path)) {
42
- foundPath = path;
43
- break;
44
- }
45
- }
46
- if (foundPath) {
47
- try {
48
- Database.setCustomSQLite(foundPath);
49
- }
50
- catch (error) {
51
- const errorStr = String(error);
52
- if (errorStr.includes("SQLite already loaded")) {
53
- }
54
- else {
55
- throw new Error(`Failed to load Homebrew SQLite: ${error}\n` + `Path: ${foundPath}`);
56
- }
57
- }
58
- }
59
- else {
60
- throw new Error(`macOS detected but no compatible SQLite library found.\n\n` +
61
- `Apple's default SQLite does not support extension loading.\n` +
62
- `Please install Homebrew SQLite and configure the path:\n\n` +
63
- `1. Install Homebrew SQLite:\n` +
64
- ` brew install sqlite\n\n` +
65
- `2. Find the library path:\n` +
66
- ` brew --prefix sqlite\n\n` +
67
- `3. Add to ~/.config/opencode/opencode-mem.jsonc:\n` +
68
- ` {\n` +
69
- ` "customSqlitePath": "/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib"\n` +
70
- ` }\n\n` +
71
- `Common paths:\n` +
72
- ` - Apple Silicon: /opt/homebrew/opt/sqlite/lib/libsqlite3.dylib\n` +
73
- ` - Intel Mac: /usr/local/opt/sqlite/lib/libsqlite3.dylib`);
74
- }
75
- }
76
- }
77
- this.sqliteConfigured = true;
78
- }
79
10
  initDatabase(db) {
80
11
  db.run("PRAGMA busy_timeout = 5000");
81
12
  db.run("PRAGMA journal_mode = WAL");
@@ -118,7 +49,6 @@ export class ConnectionManager {
118
49
  if (this.connections.has(dbPath)) {
119
50
  return this.connections.get(dbPath);
120
51
  }
121
- this.configureSqlite();
122
52
  const dir = dirname(dbPath);
123
53
  if (!existsSync(dir)) {
124
54
  mkdirSync(dir, { recursive: true });
@@ -10,6 +10,17 @@ export declare class ShardManager {
10
10
  getAllShards(scope: "user" | "project", scopeHash: string): ShardInfo[];
11
11
  createShard(scope: "user" | "project", scopeHash: string, shardIndex: number): ShardInfo;
12
12
  private initShardDb;
13
+ /**
14
+ * Check if the shard DB file exists and contains the required 'memories' table.
15
+ * Returns false if the file is missing or the table doesn't exist.
16
+ */
17
+ private isShardValid;
18
+ /**
19
+ * Ensure the shard DB has all required tables. If tables are missing,
20
+ * re-initialize them. This handles cases where the DB file exists but
21
+ * was corrupted or partially created.
22
+ */
23
+ private ensureShardTables;
13
24
  getWriteShard(scope: "user" | "project", scopeHash: string): ShardInfo;
14
25
  private markShardReadOnly;
15
26
  incrementVectorCount(shardId: number): void;
@@ -1 +1 @@
1
- {"version":3,"file":"shard-manager.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/shard-manager.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAI5C,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAW;IAC7B,OAAO,CAAC,YAAY,CAAS;;IAQ7B,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,iBAAiB;IAKzB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAsB9E,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE;IAgCvE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS;IA2BxF,OAAO,CAAC,WAAW;IA2DnB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS;IAetE,OAAO,CAAC,iBAAiB;IAOzB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO3C,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO3C,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAkBhD,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAqBnC;AAED,eAAO,MAAM,YAAY,cAAqB,CAAC"}
1
+ {"version":3,"file":"shard-manager.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/shard-manager.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAO5C,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAe;IACjC,OAAO,CAAC,YAAY,CAAS;;IAQ7B,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,iBAAiB;IAKzB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAsB9E,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE;IAgCvE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS;IA2BxF,OAAO,CAAC,WAAW;IA2DnB;;;OAGG;IACH,OAAO,CAAC,YAAY;IA4BpB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAYzB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS;IAmCtE,OAAO,CAAC,iBAAiB;IAOzB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO3C,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO3C,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAkBhD,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAwBnC;AAED,eAAO,MAAM,YAAY,cAAqB,CAAC"}
@@ -1,8 +1,10 @@
1
- import { Database } from "bun:sqlite";
1
+ import { getDatabase } from "./sqlite-bootstrap.js";
2
2
  import { join, basename, isAbsolute } from "node:path";
3
+ import { existsSync } from "node:fs";
3
4
  import { CONFIG } from "../../config.js";
4
5
  import { connectionManager } from "./connection-manager.js";
5
6
  import { log } from "../logger.js";
7
+ const Database = getDatabase();
6
8
  const METADATA_DB_NAME = "metadata.db";
7
9
  export class ShardManager {
8
10
  metadataDb;
@@ -163,11 +165,75 @@ export class ShardManager {
163
165
  db.run(`CREATE INDEX IF NOT EXISTS idx_created_at ON memories(created_at DESC)`);
164
166
  db.run(`CREATE INDEX IF NOT EXISTS idx_is_pinned ON memories(is_pinned)`);
165
167
  }
168
+ /**
169
+ * Check if the shard DB file exists and contains the required 'memories' table.
170
+ * Returns false if the file is missing or the table doesn't exist.
171
+ */
172
+ isShardValid(shard) {
173
+ if (!existsSync(shard.dbPath)) {
174
+ log("Shard DB file missing", { dbPath: shard.dbPath, shardId: shard.id });
175
+ return false;
176
+ }
177
+ try {
178
+ const db = connectionManager.getConnection(shard.dbPath);
179
+ const result = db
180
+ .prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='memories'`)
181
+ .get();
182
+ if (!result) {
183
+ log("Shard DB missing 'memories' table", {
184
+ dbPath: shard.dbPath,
185
+ shardId: shard.id,
186
+ });
187
+ return false;
188
+ }
189
+ return true;
190
+ }
191
+ catch (error) {
192
+ log("Error validating shard DB", {
193
+ dbPath: shard.dbPath,
194
+ error: String(error),
195
+ });
196
+ return false;
197
+ }
198
+ }
199
+ /**
200
+ * Ensure the shard DB has all required tables. If tables are missing,
201
+ * re-initialize them. This handles cases where the DB file exists but
202
+ * was corrupted or partially created.
203
+ */
204
+ ensureShardTables(shard) {
205
+ try {
206
+ const db = connectionManager.getConnection(shard.dbPath);
207
+ this.initShardDb(db);
208
+ }
209
+ catch (error) {
210
+ log("Error ensuring shard tables", {
211
+ dbPath: shard.dbPath,
212
+ error: String(error),
213
+ });
214
+ }
215
+ }
166
216
  getWriteShard(scope, scopeHash) {
167
217
  let shard = this.getActiveShard(scope, scopeHash);
168
218
  if (!shard) {
169
219
  return this.createShard(scope, scopeHash, 0);
170
220
  }
221
+ // Validate that the shard DB file exists and has required tables
222
+ if (!this.isShardValid(shard)) {
223
+ log("Active shard is invalid, recreating", {
224
+ scope,
225
+ scopeHash,
226
+ shardIndex: shard.shardIndex,
227
+ dbPath: shard.dbPath,
228
+ });
229
+ // Close any cached connection to the invalid shard
230
+ connectionManager.closeConnection(shard.dbPath);
231
+ // Remove the stale metadata record
232
+ const deleteStmt = this.metadataDb.prepare(`DELETE FROM shards WHERE id = ?`);
233
+ deleteStmt.run(shard.id);
234
+ // Create a fresh shard with the same index
235
+ return this.createShard(scope, scopeHash, shard.shardIndex);
236
+ }
171
237
  if (shard.vectorCount >= CONFIG.maxVectorsPerShard) {
172
238
  this.markShardReadOnly(shard.id);
173
239
  return this.createShard(scope, scopeHash, shard.shardIndex + 1);
@@ -222,7 +288,10 @@ export class ShardManager {
222
288
  }
223
289
  }
224
290
  catch (error) {
225
- log("Error deleting shard file", { dbPath: fullPath, error: String(error) });
291
+ log("Error deleting shard file", {
292
+ dbPath: fullPath,
293
+ error: String(error),
294
+ });
226
295
  }
227
296
  const deleteStmt = this.metadataDb.prepare(`DELETE FROM shards WHERE id = ?`);
228
297
  deleteStmt.run(shardId);
@@ -0,0 +1,18 @@
1
+ /**
2
+ * SQLite Bootstrap Module
3
+ *
4
+ * This module MUST be imported before any other module that uses bun:sqlite.
5
+ * It ensures that setCustomSQLite() is called BEFORE the Database class is
6
+ * instantiated, which is required for custom SQLite paths to work on macOS.
7
+ *
8
+ * Issue: https://github.com/tickernelz/opencode-mem/issues/34
9
+ *
10
+ * Loading priority:
11
+ * 1. Bundled dylib (native/darwin-{arch}/libsqlite3.dylib)
12
+ * 2. Homebrew SQLite (auto-detected common paths)
13
+ * 3. Custom path from config (customSqlitePath)
14
+ */
15
+ export declare function configureSqlite(): void;
16
+ export declare function getDatabase(): typeof import("bun:sqlite").Database;
17
+ export declare function getSqliteSource(): string | null;
18
+ //# sourceMappingURL=sqlite-bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-bootstrap.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/sqlite-bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA2FH,wBAAgB,eAAe,IAAI,IAAI,CAgFtC;AAED,wBAAgB,WAAW,IAAI,cAAc,YAAY,EAAE,QAAQ,CAGlE;AAED,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAE/C"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * SQLite Bootstrap Module
3
+ *
4
+ * This module MUST be imported before any other module that uses bun:sqlite.
5
+ * It ensures that setCustomSQLite() is called BEFORE the Database class is
6
+ * instantiated, which is required for custom SQLite paths to work on macOS.
7
+ *
8
+ * Issue: https://github.com/tickernelz/opencode-mem/issues/34
9
+ *
10
+ * Loading priority:
11
+ * 1. Bundled dylib (native/darwin-{arch}/libsqlite3.dylib)
12
+ * 2. Homebrew SQLite (auto-detected common paths)
13
+ * 3. Custom path from config (customSqlitePath)
14
+ */
15
+ import { existsSync } from "node:fs";
16
+ import { homedir } from "node:os";
17
+ import { join, dirname } from "node:path";
18
+ import { fileURLToPath } from "node:url";
19
+ import { stripJsoncComments } from "../jsonc.js";
20
+ let Database;
21
+ let sqliteConfigured = false;
22
+ let sqliteSource = null;
23
+ const CONFIG_DIR = join(homedir(), ".config", "opencode");
24
+ const CONFIG_FILES = [
25
+ join(CONFIG_DIR, "opencode-mem.jsonc"),
26
+ join(CONFIG_DIR, "opencode-mem.json"),
27
+ ];
28
+ function getBundledSqlitePath() {
29
+ if (process.platform !== "darwin")
30
+ return null;
31
+ const arch = process.arch;
32
+ if (arch !== "x64" && arch !== "arm64")
33
+ return null;
34
+ try {
35
+ const currentDir = dirname(fileURLToPath(import.meta.url));
36
+ const bundledPath = join(currentDir, "..", "..", "..", "native", `darwin-${arch}`, "libsqlite3.dylib");
37
+ if (existsSync(bundledPath)) {
38
+ return bundledPath;
39
+ }
40
+ }
41
+ catch { }
42
+ return null;
43
+ }
44
+ function loadSqliteConfig() {
45
+ for (const path of CONFIG_FILES) {
46
+ if (existsSync(path)) {
47
+ try {
48
+ const content = require("node:fs").readFileSync(path, "utf-8");
49
+ const json = stripJsoncComments(content);
50
+ return JSON.parse(json);
51
+ }
52
+ catch { }
53
+ }
54
+ }
55
+ return {};
56
+ }
57
+ function expandPath(path) {
58
+ if (path.startsWith("~/")) {
59
+ return join(homedir(), path.slice(2));
60
+ }
61
+ if (path === "~") {
62
+ return homedir();
63
+ }
64
+ return path;
65
+ }
66
+ function getHomebrewSqlitePath() {
67
+ const arch = process.arch;
68
+ const paths = arch === "arm64"
69
+ ? ["/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib"]
70
+ : [
71
+ "/usr/local/opt/sqlite/lib/libsqlite3.dylib",
72
+ "/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib",
73
+ ];
74
+ for (const path of paths) {
75
+ if (existsSync(path)) {
76
+ return path;
77
+ }
78
+ }
79
+ return null;
80
+ }
81
+ export function configureSqlite() {
82
+ if (sqliteConfigured)
83
+ return;
84
+ const bunSqlite = require("bun:sqlite");
85
+ Database = bunSqlite.Database;
86
+ if (process.platform !== "darwin") {
87
+ sqliteConfigured = true;
88
+ return;
89
+ }
90
+ const config = loadSqliteConfig();
91
+ const customPath = config.customSqlitePath ? expandPath(config.customSqlitePath) : undefined;
92
+ const trySetCustomSQLite = (path, source) => {
93
+ try {
94
+ Database.setCustomSQLite(path);
95
+ sqliteSource = source;
96
+ return true;
97
+ }
98
+ catch (error) {
99
+ const errorStr = String(error);
100
+ if (errorStr.includes("SQLite already loaded")) {
101
+ return true;
102
+ }
103
+ return false;
104
+ }
105
+ };
106
+ // Priority 1: Bundled dylib
107
+ const bundledPath = getBundledSqlitePath();
108
+ if (bundledPath) {
109
+ if (trySetCustomSQLite(bundledPath, "bundled")) {
110
+ sqliteConfigured = true;
111
+ return;
112
+ }
113
+ }
114
+ // Priority 2: Custom path from config
115
+ if (customPath) {
116
+ if (!existsSync(customPath)) {
117
+ throw new Error(`Custom SQLite library not found at: ${customPath}\n` +
118
+ `Please verify the path or install Homebrew SQLite:\n` +
119
+ ` brew install sqlite\n` +
120
+ ` brew --prefix sqlite`);
121
+ }
122
+ if (trySetCustomSQLite(customPath, "custom")) {
123
+ sqliteConfigured = true;
124
+ return;
125
+ }
126
+ }
127
+ // Priority 3: Homebrew SQLite
128
+ const homebrewPath = getHomebrewSqlitePath();
129
+ if (homebrewPath) {
130
+ if (trySetCustomSQLite(homebrewPath, "homebrew")) {
131
+ sqliteConfigured = true;
132
+ return;
133
+ }
134
+ }
135
+ // No compatible SQLite found
136
+ throw new Error(`macOS detected but no compatible SQLite library found.\n\n` +
137
+ `Apple's default SQLite does not support extension loading.\n` +
138
+ `Solutions:\n\n` +
139
+ `Option 1 - Install Homebrew SQLite (recommended):\n` +
140
+ ` brew install sqlite\n\n` +
141
+ `Option 2 - Download manually and configure:\n` +
142
+ ` 1. Download SQLite with extension support\n` +
143
+ ` 2. Add to ~/.config/opencode/opencode-mem.jsonc:\n` +
144
+ ` {\n` +
145
+ ` "customSqlitePath": "/path/to/libsqlite3.dylib"\n` +
146
+ ` }\n\n` +
147
+ `Common Homebrew paths:\n` +
148
+ ` - Apple Silicon: /opt/homebrew/opt/sqlite/lib/libsqlite3.dylib\n` +
149
+ ` - Intel Mac: /usr/local/opt/sqlite/lib/libsqlite3.dylib`);
150
+ }
151
+ export function getDatabase() {
152
+ configureSqlite();
153
+ return Database;
154
+ }
155
+ export function getSqliteSource() {
156
+ return sqliteSource;
157
+ }
158
+ configureSqlite();
@@ -1,20 +1,22 @@
1
- import { Database } from "bun:sqlite";
2
1
  import type { MemoryRecord, SearchResult, ShardInfo } from "./types.js";
2
+ declare const Database: typeof import("bun:sqlite").Database;
3
+ type DatabaseType = typeof Database.prototype;
3
4
  export declare class VectorSearch {
4
- insertVector(db: Database, record: MemoryRecord): void;
5
+ insertVector(db: DatabaseType, record: MemoryRecord): void;
5
6
  searchInShard(shard: ShardInfo, queryVector: Float32Array, containerTag: string, limit: number, queryText?: string): SearchResult[];
6
7
  searchAcrossShards(shards: ShardInfo[], queryVector: Float32Array, containerTag: string, limit: number, similarityThreshold: number, queryText?: string): Promise<SearchResult[]>;
7
- deleteVector(db: Database, memoryId: string): void;
8
- updateVector(db: Database, memoryId: string, vector: Float32Array, tagsVector?: Float32Array): void;
9
- listMemories(db: Database, containerTag: string, limit: number): any[];
10
- getAllMemories(db: Database): any[];
11
- getMemoryById(db: Database, memoryId: string): any | null;
12
- getMemoriesBySessionID(db: Database, sessionID: string): any[];
13
- countVectors(db: Database, containerTag: string): number;
14
- countAllVectors(db: Database): number;
15
- getDistinctTags(db: Database): any[];
16
- pinMemory(db: Database, memoryId: string): void;
17
- unpinMemory(db: Database, memoryId: string): void;
8
+ deleteVector(db: DatabaseType, memoryId: string): void;
9
+ updateVector(db: DatabaseType, memoryId: string, vector: Float32Array, tagsVector?: Float32Array): void;
10
+ listMemories(db: DatabaseType, containerTag: string, limit: number): any[];
11
+ getAllMemories(db: DatabaseType): any[];
12
+ getMemoryById(db: DatabaseType, memoryId: string): any | null;
13
+ getMemoriesBySessionID(db: DatabaseType, sessionID: string): any[];
14
+ countVectors(db: DatabaseType, containerTag: string): number;
15
+ countAllVectors(db: DatabaseType): number;
16
+ getDistinctTags(db: DatabaseType): any[];
17
+ pinMemory(db: DatabaseType, memoryId: string): void;
18
+ unpinMemory(db: DatabaseType, memoryId: string): void;
18
19
  }
19
20
  export declare const vectorSearch: VectorSearch;
21
+ export {};
20
22
  //# sourceMappingURL=vector-search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vector-search.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/vector-search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAExE,qBAAa,YAAY;IACvB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI;IA0CtD,aAAa,CACX,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,YAAY,EACzB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,GACjB,YAAY,EAAE;IA0FX,kBAAkB,CACtB,MAAM,EAAE,SAAS,EAAE,EACnB,WAAW,EAAE,YAAY,EACzB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,mBAAmB,EAAE,MAAM,EAC3B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC;IAiB1B,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAMlD,YAAY,CACV,EAAE,EAAE,QAAQ,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,YAAY,EACpB,UAAU,CAAC,EAAE,YAAY,GACxB,IAAI;IAkBP,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE;IAWtE,cAAc,CAAC,EAAE,EAAE,QAAQ,GAAG,GAAG,EAAE;IAKnC,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAKzD,sBAAsB,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,GAAG,EAAE;IAgB9D,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAMxD,eAAe,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM;IAMrC,eAAe,CAAC,EAAE,EAAE,QAAQ,GAAG,GAAG,EAAE;IAepC,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK/C,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;CAIlD;AAED,eAAO,MAAM,YAAY,cAAqB,CAAC"}
1
+ {"version":3,"file":"vector-search.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/vector-search.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAExE,QAAA,MAAM,QAAQ,sCAAgB,CAAC;AAC/B,KAAK,YAAY,GAAG,OAAO,QAAQ,CAAC,SAAS,CAAC;AAE9C,qBAAa,YAAY;IACvB,YAAY,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI;IA0C1D,aAAa,CACX,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,YAAY,EACzB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,GACjB,YAAY,EAAE;IA0FX,kBAAkB,CACtB,MAAM,EAAE,SAAS,EAAE,EACnB,WAAW,EAAE,YAAY,EACzB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,mBAAmB,EAAE,MAAM,EAC3B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC;IAiB1B,YAAY,CAAC,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAMtD,YAAY,CACV,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,YAAY,EACpB,UAAU,CAAC,EAAE,YAAY,GACxB,IAAI;IAkBP,YAAY,CAAC,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE;IAW1E,cAAc,CAAC,EAAE,EAAE,YAAY,GAAG,GAAG,EAAE;IAKvC,aAAa,CAAC,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAK7D,sBAAsB,CAAC,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,GAAG,EAAE;IAgBlE,YAAY,CAAC,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAM5D,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,MAAM;IAMzC,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,GAAG,EAAE;IAexC,SAAS,CAAC,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKnD,WAAW,CAAC,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;CAItD;AAED,eAAO,MAAM,YAAY,cAAqB,CAAC"}
@@ -1,6 +1,7 @@
1
- import { Database } from "bun:sqlite";
1
+ import { getDatabase } from "./sqlite-bootstrap.js";
2
2
  import { connectionManager } from "./connection-manager.js";
3
3
  import { log } from "../logger.js";
4
+ const Database = getDatabase();
4
5
  export class VectorSearch {
5
6
  insertVector(db, record) {
6
7
  const insertMemory = db.prepare(`
@@ -1 +1 @@
1
- {"version":3,"file":"user-profile-manager.d.ts","sourceRoot":"","sources":["../../../src/services/user-profile/user-profile-manager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAKrF,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;IAQhC,OAAO,CAAC,YAAY;IA0CpB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAapD,aAAa,CACX,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,eAAe,EAC5B,eAAe,EAAE,MAAM,GACtB,MAAM;IAoCT,aAAa,CACX,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,eAAe,EAC5B,yBAAyB,EAAE,MAAM,EACjC,aAAa,EAAE,MAAM,GACpB,IAAI;IAmCP,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,oBAAoB;IAiB5B,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,oBAAoB,EAAE;IAYnF,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IA2B7C,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKtC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAOrD,oBAAoB,IAAI,WAAW,EAAE;IAMrC,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,cAAc;IAYtB,gBAAgB,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe;IA0F/F,OAAO,CAAC,WAAW;CAWpB;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAC"}
1
+ {"version":3,"file":"user-profile-manager.d.ts","sourceRoot":"","sources":["../../../src/services/user-profile/user-profile-manager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAQrF,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,EAAE,CAAe;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;IAQhC,OAAO,CAAC,YAAY;IA0CpB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAapD,aAAa,CACX,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,eAAe,EAC5B,eAAe,EAAE,MAAM,GACtB,MAAM;IAoCT,aAAa,CACX,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,eAAe,EAC5B,yBAAyB,EAAE,MAAM,EACjC,aAAa,EAAE,MAAM,GACpB,IAAI;IAmCP,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,oBAAoB;IAiB5B,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,oBAAoB,EAAE;IAYnF,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IA2B7C,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKtC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAOrD,oBAAoB,IAAI,WAAW,EAAE;IAMrC,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,cAAc;IAYtB,gBAAgB,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe;IA0F/F,OAAO,CAAC,WAAW;CAWpB;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAC"}
@@ -1,8 +1,9 @@
1
- import { Database } from "bun:sqlite";
1
+ import { getDatabase } from "../sqlite/sqlite-bootstrap.js";
2
2
  import { join } from "node:path";
3
3
  import { connectionManager } from "../sqlite/connection-manager.js";
4
4
  import { CONFIG } from "../../config.js";
5
5
  import { safeArray, safeObject } from "./profile-utils.js";
6
+ const Database = getDatabase();
6
7
  const USER_PROFILES_DB_NAME = "user-profiles.db";
7
8
  export class UserProfileManager {
8
9
  db;
@@ -1 +1 @@
1
- {"version":3,"file":"user-prompt-manager.d.ts","sourceRoot":"","sources":["../../../src/services/user-prompt/user-prompt-manager.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;IAQhC,OAAO,CAAC,YAAY;IAiCpB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAa9F,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAc7D,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKpC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKtC,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAQtC,sBAAsB,IAAI,MAAM;IAMhC,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE;IAYjD,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAUjD,8BAA8B,IAAI,MAAM;IAQxC,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE;IAYtD,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKlD,kCAAkC,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAU7D,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,EAAE,CAAA;KAAE;IAiBpF,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK5D,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAOlD,kBAAkB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE;IAgBtD,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,UAAU,EAAE;IAiBpF,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,UAAU,EAAE;IAQ5C,OAAO,CAAC,WAAW;CAapB;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
1
+ {"version":3,"file":"user-prompt-manager.d.ts","sourceRoot":"","sources":["../../../src/services/user-prompt/user-prompt-manager.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,EAAE,CAAe;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;IAQhC,OAAO,CAAC,YAAY;IAiCpB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAa9F,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAc7D,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKpC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKtC,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAQtC,sBAAsB,IAAI,MAAM;IAMhC,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE;IAYjD,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAUjD,8BAA8B,IAAI,MAAM;IAQxC,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE;IAYtD,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKlD,kCAAkC,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAU7D,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,EAAE,CAAA;KAAE;IAiBpF,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK5D,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAOlD,kBAAkB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE;IAgBtD,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,UAAU,EAAE;IAiBpF,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,UAAU,EAAE;IAQ5C,OAAO,CAAC,WAAW;CAapB;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
@@ -1,7 +1,8 @@
1
- import { Database } from "bun:sqlite";
1
+ import { getDatabase } from "../sqlite/sqlite-bootstrap.js";
2
2
  import { join } from "node:path";
3
3
  import { connectionManager } from "../sqlite/connection-manager.js";
4
4
  import { CONFIG } from "../../config.js";
5
+ const Database = getDatabase();
5
6
  const USER_PROMPTS_DB_NAME = "user-prompts.db";
6
7
  export class UserPromptManager {
7
8
  db;
@@ -4,7 +4,7 @@ interface WebServerConfig {
4
4
  enabled: boolean;
5
5
  }
6
6
  export declare class WebServer {
7
- private worker;
7
+ private server;
8
8
  private config;
9
9
  private isOwner;
10
10
  private startPromise;
@@ -22,6 +22,9 @@ export declare class WebServer {
22
22
  isServerOwner(): boolean;
23
23
  getUrl(): string;
24
24
  checkServerAvailable(): Promise<boolean>;
25
+ private handleRequest;
26
+ private serveStaticFile;
27
+ private jsonResponse;
25
28
  }
26
29
  export declare function startWebServer(config: WebServerConfig): Promise<WebServer>;
27
30
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"web-server.d.ts","sourceRoot":"","sources":["../../src/services/web-server.ts"],"names":[],"mappings":"AAOA,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAeD,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,mBAAmB,CAA+B;IAC1D,OAAO,CAAC,kBAAkB,CAAsC;gBAEpD,MAAM,EAAE,eAAe;IAInC,qBAAqB,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAIpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YASd,MAAM;IAiFpB,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,mBAAmB;YAOb,eAAe;IA2BvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAmC3B,SAAS,IAAI,OAAO;IAIpB,aAAa,IAAI,OAAO;IAIxB,MAAM,IAAI,MAAM;IAIV,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC;CAW/C;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAIhF"}
1
+ {"version":3,"file":"web-server.d.ts","sourceRoot":"","sources":["../../src/services/web-server.ts"],"names":[],"mappings":"AAiCA,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAA6C;IAC3D,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,mBAAmB,CAA+B;IAC1D,OAAO,CAAC,kBAAkB,CAAsC;gBAEpD,MAAM,EAAE,eAAe;IAInC,qBAAqB,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAIpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YASd,MAAM;IAgCpB,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,mBAAmB;YAOb,eAAe;IA+BvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,SAAS,IAAI,OAAO;IAIpB,aAAa,IAAI,OAAO;IAIxB,MAAM,IAAI,MAAM;IAIV,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC;YAchC,aAAa;IAmN3B,OAAO,CAAC,eAAe;IA4BvB,OAAO,CAAC,YAAY;CAWrB;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAIhF"}
@@ -1,10 +1,12 @@
1
+ import { readFileSync } from "node:fs";
1
2
  import { join, dirname } from "node:path";
2
3
  import { fileURLToPath } from "node:url";
3
4
  import { log } from "./logger.js";
5
+ import { handleListTags, handleListMemories, handleAddMemory, handleDeleteMemory, handleBulkDelete, handleUpdateMemory, handleSearch, handleStats, handlePinMemory, handleUnpinMemory, handleRunCleanup, handleRunDeduplication, handleDetectMigration, handleRunMigration, handleDetectTagMigration, handleRunTagMigrationBatch, handleGetTagMigrationProgress, handleDeletePrompt, handleBulkDeletePrompts, handleGetUserProfile, handleGetProfileChangelog, handleGetProfileSnapshot, handleRefreshProfile, } from "./api-handlers.js";
4
6
  const __filename = fileURLToPath(import.meta.url);
5
7
  const __dirname = dirname(__filename);
6
8
  export class WebServer {
7
- worker = null;
9
+ server = null;
8
10
  config;
9
11
  isOwner = false;
10
12
  startPromise = null;
@@ -28,70 +30,28 @@ export class WebServer {
28
30
  return;
29
31
  }
30
32
  try {
31
- const workerPath = join(__dirname, "web-server-worker.js");
32
- this.worker = new Worker(workerPath);
33
- const startedPromise = new Promise((resolve, reject) => {
34
- const timeout = setTimeout(() => {
35
- reject(new Error("Worker start timeout"));
36
- }, 10000);
37
- this.worker.onmessage = (event) => {
38
- clearTimeout(timeout);
39
- const response = event.data;
40
- if (response.type === "started") {
41
- this.isOwner = true;
42
- resolve();
43
- }
44
- else if (response.type === "error") {
45
- const errorMsg = response.error || "Unknown error";
46
- if (errorMsg.includes("EADDRINUSE") ||
47
- errorMsg.includes("address already in use") ||
48
- /^Error: Failed to start server\. Is port \d+ in use\?$/.test(errorMsg)) {
49
- this.isOwner = false;
50
- resolve();
51
- }
52
- else {
53
- log("Web server worker error", { error: errorMsg });
54
- reject(new Error(errorMsg));
55
- }
56
- }
57
- };
58
- this.worker.onerror = (error) => {
59
- clearTimeout(timeout);
60
- const errorDetails = {
61
- message: error.message || "Unknown error",
62
- filename: error.filename || "unknown",
63
- lineno: error.lineno || 0,
64
- colno: error.colno || 0,
65
- error: error.error ? String(error.error) : "no error object",
66
- type: error.type || "error",
67
- };
68
- log("Web server worker error (detailed)", errorDetails);
69
- const errorMsg = error.message
70
- ? `${error.message} (at ${error.filename}:${error.lineno}:${error.colno})`
71
- : error.error
72
- ? String(error.error)
73
- : `Worker failed: ${JSON.stringify(errorDetails)}`;
74
- reject(new Error(errorMsg));
75
- };
76
- });
77
- this.worker.postMessage({
78
- type: "start",
33
+ this.server = Bun.serve({
79
34
  port: this.config.port,
80
- host: this.config.host,
35
+ hostname: this.config.host,
36
+ fetch: this.handleRequest.bind(this),
81
37
  });
82
- await startedPromise;
83
- if (!this.isOwner) {
84
- this.startHealthCheckLoop();
85
- }
38
+ this.isOwner = true;
86
39
  }
87
40
  catch (error) {
88
- this.isOwner = false;
89
- if (this.worker) {
90
- this.worker.terminate();
91
- this.worker = null;
41
+ const errorMsg = String(error);
42
+ if (errorMsg.includes("EADDRINUSE") ||
43
+ errorMsg.includes("address already in use") ||
44
+ /Failed to start server.*Is port \d+ in use/.test(errorMsg)) {
45
+ this.isOwner = false;
46
+ this.server = null;
47
+ this.startHealthCheckLoop();
48
+ }
49
+ else {
50
+ this.isOwner = false;
51
+ this.server = null;
52
+ log("Web server failed to start", { error: errorMsg });
53
+ throw error;
92
54
  }
93
- log("Web server failed to start", { error: String(error) });
94
- throw error;
95
55
  }
96
56
  }
97
57
  startHealthCheckLoop() {
@@ -121,15 +81,18 @@ export class WebServer {
121
81
  return;
122
82
  }
123
83
  try {
84
+ // Reset startPromise so _start() can run again
85
+ this.startPromise = null;
124
86
  await this._start();
125
- this.isOwner = true;
126
- log("Web server takeover successful", { port: this.config.port });
127
- if (this.onTakeoverCallback) {
128
- try {
129
- await this.onTakeoverCallback();
130
- }
131
- catch (error) {
132
- log("Takeover callback error", { error: String(error) });
87
+ if (this.isOwner) {
88
+ log("Web server takeover successful", { port: this.config.port });
89
+ if (this.onTakeoverCallback) {
90
+ try {
91
+ await this.onTakeoverCallback();
92
+ }
93
+ catch (error) {
94
+ log("Takeover callback error", { error: String(error) });
95
+ }
133
96
  }
134
97
  }
135
98
  }
@@ -139,35 +102,15 @@ export class WebServer {
139
102
  }
140
103
  async stop() {
141
104
  this.stopHealthCheckLoop();
142
- if (!this.isOwner || !this.worker) {
105
+ if (!this.isOwner || !this.server) {
143
106
  return;
144
107
  }
145
- return new Promise((resolve) => {
146
- const timeout = setTimeout(() => {
147
- if (this.worker) {
148
- this.worker.terminate();
149
- this.worker = null;
150
- }
151
- resolve();
152
- }, 5000);
153
- this.worker.onmessage = (event) => {
154
- clearTimeout(timeout);
155
- const response = event.data;
156
- if (response.type === "stopped") {
157
- if (this.worker) {
158
- this.worker.terminate();
159
- this.worker = null;
160
- }
161
- resolve();
162
- }
163
- };
164
- this.worker.postMessage({
165
- type: "stop",
166
- });
167
- });
108
+ this.server.stop();
109
+ this.server = null;
110
+ this.isOwner = false;
168
111
  }
169
112
  isRunning() {
170
- return this.worker !== null;
113
+ return this.server !== null;
171
114
  }
172
115
  isServerOwner() {
173
116
  return this.isOwner;
@@ -187,6 +130,221 @@ export class WebServer {
187
130
  return false;
188
131
  }
189
132
  }
133
+ // --- HTTP request handling (inlined from web-server-worker.ts) ---
134
+ async handleRequest(req) {
135
+ const url = new URL(req.url);
136
+ const path = url.pathname;
137
+ const method = req.method;
138
+ try {
139
+ if (path === "/" || path === "/index.html") {
140
+ return this.serveStaticFile("index.html", "text/html");
141
+ }
142
+ if (path === "/styles.css") {
143
+ return this.serveStaticFile("styles.css", "text/css");
144
+ }
145
+ if (path === "/app.js") {
146
+ return this.serveStaticFile("app.js", "application/javascript");
147
+ }
148
+ if (path === "/favicon.ico") {
149
+ return this.serveStaticFile("favicon.ico", "image/x-icon");
150
+ }
151
+ if (path === "/api/tags" && method === "GET") {
152
+ const result = await handleListTags();
153
+ return this.jsonResponse(result);
154
+ }
155
+ if (path === "/api/memories" && method === "GET") {
156
+ const tag = url.searchParams.get("tag") || undefined;
157
+ const page = parseInt(url.searchParams.get("page") || "1");
158
+ const pageSize = parseInt(url.searchParams.get("pageSize") || "20");
159
+ const includePrompts = url.searchParams.get("includePrompts") !== "false";
160
+ const result = await handleListMemories(tag, page, pageSize, includePrompts);
161
+ return this.jsonResponse(result);
162
+ }
163
+ if (path === "/api/memories" && method === "POST") {
164
+ const body = (await req.json());
165
+ const result = await handleAddMemory(body);
166
+ return this.jsonResponse(result);
167
+ }
168
+ if (path.startsWith("/api/memories/") && method === "DELETE") {
169
+ const parts = path.split("/");
170
+ const id = parts[3];
171
+ if (!id || id === "bulk-delete") {
172
+ return this.jsonResponse({ success: false, error: "Invalid ID" });
173
+ }
174
+ const cascade = url.searchParams.get("cascade") === "true";
175
+ const result = await handleDeleteMemory(id, cascade);
176
+ return this.jsonResponse(result);
177
+ }
178
+ if (path.startsWith("/api/memories/") && method === "PUT") {
179
+ const id = path.split("/").pop();
180
+ if (!id) {
181
+ return this.jsonResponse({ success: false, error: "Invalid ID" });
182
+ }
183
+ const body = (await req.json());
184
+ const result = await handleUpdateMemory(id, body);
185
+ return this.jsonResponse(result);
186
+ }
187
+ if (path === "/api/memories/bulk-delete" && method === "POST") {
188
+ const body = (await req.json());
189
+ const cascade = body.cascade !== false;
190
+ const result = await handleBulkDelete(body.ids || [], cascade);
191
+ return this.jsonResponse(result);
192
+ }
193
+ if (path === "/api/search" && method === "GET") {
194
+ const query = url.searchParams.get("q");
195
+ const tag = url.searchParams.get("tag") || undefined;
196
+ const page = parseInt(url.searchParams.get("page") || "1");
197
+ const pageSize = parseInt(url.searchParams.get("pageSize") || "20");
198
+ if (!query) {
199
+ return this.jsonResponse({ success: false, error: "query parameter required" });
200
+ }
201
+ const result = await handleSearch(query, tag, page, pageSize);
202
+ return this.jsonResponse(result);
203
+ }
204
+ if (path === "/api/stats" && method === "GET") {
205
+ const result = await handleStats();
206
+ return this.jsonResponse(result);
207
+ }
208
+ if (path.match(/^\/api\/memories\/[^/]+\/pin$/) && method === "POST") {
209
+ const id = path.split("/")[3];
210
+ if (!id) {
211
+ return this.jsonResponse({ success: false, error: "Invalid ID" });
212
+ }
213
+ const result = await handlePinMemory(id);
214
+ return this.jsonResponse(result);
215
+ }
216
+ if (path.match(/^\/api\/memories\/[^/]+\/unpin$/) && method === "POST") {
217
+ const id = path.split("/")[3];
218
+ if (!id) {
219
+ return this.jsonResponse({ success: false, error: "Invalid ID" });
220
+ }
221
+ const result = await handleUnpinMemory(id);
222
+ return this.jsonResponse(result);
223
+ }
224
+ if (path === "/api/cleanup" && method === "POST") {
225
+ const result = await handleRunCleanup();
226
+ return this.jsonResponse(result);
227
+ }
228
+ if (path === "/api/deduplicate" && method === "POST") {
229
+ const result = await handleRunDeduplication();
230
+ return this.jsonResponse(result);
231
+ }
232
+ if (path === "/api/migration/detect" && method === "GET") {
233
+ const result = await handleDetectMigration();
234
+ return this.jsonResponse(result);
235
+ }
236
+ if (path === "/api/migration/tags/detect" && method === "GET") {
237
+ const result = await handleDetectTagMigration();
238
+ return this.jsonResponse(result);
239
+ }
240
+ if (path === "/api/migration/tags/run-batch" && method === "POST") {
241
+ const body = (await req.json());
242
+ const batchSize = body?.batchSize || 5;
243
+ const result = await handleRunTagMigrationBatch(batchSize);
244
+ return this.jsonResponse(result);
245
+ }
246
+ if (path === "/api/migration/tags/progress" && method === "GET") {
247
+ const result = await handleGetTagMigrationProgress();
248
+ return this.jsonResponse(result);
249
+ }
250
+ if (path === "/api/migration/run" && method === "POST") {
251
+ const body = (await req.json());
252
+ const strategy = body.strategy || "fresh-start";
253
+ if (strategy !== "fresh-start" && strategy !== "re-embed") {
254
+ return this.jsonResponse({ success: false, error: "Invalid strategy" });
255
+ }
256
+ const result = await handleRunMigration(strategy);
257
+ return this.jsonResponse(result);
258
+ }
259
+ if (path.startsWith("/api/prompts/") && method === "DELETE") {
260
+ const parts = path.split("/");
261
+ const id = parts[3];
262
+ if (!id || id === "bulk-delete") {
263
+ return this.jsonResponse({ success: false, error: "Invalid ID" });
264
+ }
265
+ const cascade = url.searchParams.get("cascade") === "true";
266
+ const result = await handleDeletePrompt(id, cascade);
267
+ return this.jsonResponse(result);
268
+ }
269
+ if (path === "/api/prompts/bulk-delete" && method === "POST") {
270
+ const body = (await req.json());
271
+ const cascade = body.cascade !== false;
272
+ const result = await handleBulkDeletePrompts(body.ids || [], cascade);
273
+ return this.jsonResponse(result);
274
+ }
275
+ if (path === "/api/user-profile" && method === "GET") {
276
+ const userId = url.searchParams.get("userId") || undefined;
277
+ const result = await handleGetUserProfile(userId);
278
+ return this.jsonResponse(result);
279
+ }
280
+ if (path === "/api/user-profile/changelog" && method === "GET") {
281
+ const profileId = url.searchParams.get("profileId");
282
+ const limit = parseInt(url.searchParams.get("limit") || "5");
283
+ if (!profileId) {
284
+ return this.jsonResponse({ success: false, error: "profileId parameter required" });
285
+ }
286
+ const result = await handleGetProfileChangelog(profileId, limit);
287
+ return this.jsonResponse(result);
288
+ }
289
+ if (path === "/api/user-profile/snapshot" && method === "GET") {
290
+ const changelogId = url.searchParams.get("chlogId");
291
+ if (!changelogId) {
292
+ return this.jsonResponse({ success: false, error: "changelogId parameter required" });
293
+ }
294
+ const result = await handleGetProfileSnapshot(changelogId);
295
+ return this.jsonResponse(result);
296
+ }
297
+ if (path === "/api/user-profile/refresh" && method === "POST") {
298
+ const body = (await req.json().catch(() => ({})));
299
+ const userId = body.userId || undefined;
300
+ const result = await handleRefreshProfile(userId);
301
+ return this.jsonResponse(result);
302
+ }
303
+ return new Response("Not Found", { status: 404 });
304
+ }
305
+ catch (error) {
306
+ return this.jsonResponse({
307
+ success: false,
308
+ error: String(error),
309
+ }, 500);
310
+ }
311
+ }
312
+ serveStaticFile(filename, contentType) {
313
+ try {
314
+ const webDir = join(__dirname, "..", "web");
315
+ const filePath = join(webDir, filename);
316
+ if (contentType.startsWith("image/")) {
317
+ const content = readFileSync(filePath);
318
+ return new Response(content, {
319
+ headers: {
320
+ "Content-Type": contentType,
321
+ "Cache-Control": "public, max-age=86400",
322
+ },
323
+ });
324
+ }
325
+ const content = readFileSync(filePath, "utf-8");
326
+ return new Response(content, {
327
+ headers: {
328
+ "Content-Type": contentType,
329
+ "Cache-Control": "no-cache",
330
+ },
331
+ });
332
+ }
333
+ catch (error) {
334
+ return new Response("File not found", { status: 404 });
335
+ }
336
+ }
337
+ jsonResponse(data, status = 200) {
338
+ return new Response(JSON.stringify(data), {
339
+ status,
340
+ headers: {
341
+ "Content-Type": "application/json",
342
+ "Access-Control-Allow-Origin": "*",
343
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
344
+ "Access-Control-Allow-Headers": "Content-Type",
345
+ },
346
+ });
347
+ }
190
348
  }
191
349
  export async function startWebServer(config) {
192
350
  const server = new WebServer(config);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-mem",
3
- "version": "2.8.4",
3
+ "version": "2.8.8",
4
4
  "description": "OpenCode plugin that gives coding agents persistent memory using local vector database",
5
5
  "type": "module",
6
6
  "main": "dist/plugin.js",
@@ -55,6 +55,7 @@
55
55
  },
56
56
  "files": [
57
57
  "dist",
58
+ "native",
58
59
  "package.json"
59
60
  ],
60
61
  "lint-staged": {