opencode-mem 1.0.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.
Files changed (84) hide show
  1. package/README.md +588 -0
  2. package/dist/config.d.ts +33 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +258 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +618 -0
  8. package/dist/plugin.d.ts +5 -0
  9. package/dist/plugin.d.ts.map +1 -0
  10. package/dist/plugin.js +15 -0
  11. package/dist/services/api-handlers.d.ts +102 -0
  12. package/dist/services/api-handlers.d.ts.map +1 -0
  13. package/dist/services/api-handlers.js +494 -0
  14. package/dist/services/auto-capture.d.ts +32 -0
  15. package/dist/services/auto-capture.d.ts.map +1 -0
  16. package/dist/services/auto-capture.js +451 -0
  17. package/dist/services/cleanup-service.d.ts +20 -0
  18. package/dist/services/cleanup-service.d.ts.map +1 -0
  19. package/dist/services/cleanup-service.js +88 -0
  20. package/dist/services/client.d.ts +104 -0
  21. package/dist/services/client.d.ts.map +1 -0
  22. package/dist/services/client.js +251 -0
  23. package/dist/services/compaction.d.ts +92 -0
  24. package/dist/services/compaction.d.ts.map +1 -0
  25. package/dist/services/compaction.js +421 -0
  26. package/dist/services/context.d.ts +17 -0
  27. package/dist/services/context.d.ts.map +1 -0
  28. package/dist/services/context.js +41 -0
  29. package/dist/services/deduplication-service.d.ts +30 -0
  30. package/dist/services/deduplication-service.d.ts.map +1 -0
  31. package/dist/services/deduplication-service.js +131 -0
  32. package/dist/services/embedding.d.ts +10 -0
  33. package/dist/services/embedding.d.ts.map +1 -0
  34. package/dist/services/embedding.js +77 -0
  35. package/dist/services/jsonc.d.ts +7 -0
  36. package/dist/services/jsonc.d.ts.map +1 -0
  37. package/dist/services/jsonc.js +76 -0
  38. package/dist/services/logger.d.ts +2 -0
  39. package/dist/services/logger.d.ts.map +1 -0
  40. package/dist/services/logger.js +16 -0
  41. package/dist/services/migration-service.d.ts +42 -0
  42. package/dist/services/migration-service.d.ts.map +1 -0
  43. package/dist/services/migration-service.js +258 -0
  44. package/dist/services/privacy.d.ts +4 -0
  45. package/dist/services/privacy.d.ts.map +1 -0
  46. package/dist/services/privacy.js +10 -0
  47. package/dist/services/sqlite/connection-manager.d.ts +10 -0
  48. package/dist/services/sqlite/connection-manager.d.ts.map +1 -0
  49. package/dist/services/sqlite/connection-manager.js +45 -0
  50. package/dist/services/sqlite/shard-manager.d.ts +20 -0
  51. package/dist/services/sqlite/shard-manager.d.ts.map +1 -0
  52. package/dist/services/sqlite/shard-manager.js +221 -0
  53. package/dist/services/sqlite/types.d.ts +39 -0
  54. package/dist/services/sqlite/types.d.ts.map +1 -0
  55. package/dist/services/sqlite/types.js +1 -0
  56. package/dist/services/sqlite/vector-search.d.ts +18 -0
  57. package/dist/services/sqlite/vector-search.d.ts.map +1 -0
  58. package/dist/services/sqlite/vector-search.js +129 -0
  59. package/dist/services/sqlite-client.d.ts +116 -0
  60. package/dist/services/sqlite-client.d.ts.map +1 -0
  61. package/dist/services/sqlite-client.js +284 -0
  62. package/dist/services/tags.d.ts +20 -0
  63. package/dist/services/tags.d.ts.map +1 -0
  64. package/dist/services/tags.js +76 -0
  65. package/dist/services/web-server-lock.d.ts +12 -0
  66. package/dist/services/web-server-lock.d.ts.map +1 -0
  67. package/dist/services/web-server-lock.js +157 -0
  68. package/dist/services/web-server-worker.d.ts +2 -0
  69. package/dist/services/web-server-worker.d.ts.map +1 -0
  70. package/dist/services/web-server-worker.js +221 -0
  71. package/dist/services/web-server.d.ts +22 -0
  72. package/dist/services/web-server.d.ts.map +1 -0
  73. package/dist/services/web-server.js +134 -0
  74. package/dist/types/index.d.ts +48 -0
  75. package/dist/types/index.d.ts.map +1 -0
  76. package/dist/types/index.js +1 -0
  77. package/dist/web/app.d.ts +2 -0
  78. package/dist/web/app.d.ts.map +1 -0
  79. package/dist/web/app.js +691 -0
  80. package/dist/web/favicon.ico +0 -0
  81. package/dist/web/favicon.svg +14 -0
  82. package/dist/web/index.html +202 -0
  83. package/dist/web/styles.css +851 -0
  84. package/package.json +52 -0
@@ -0,0 +1,221 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join, dirname } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { handleListTags, handleListMemories, handleAddMemory, handleDeleteMemory, handleBulkDelete, handleUpdateMemory, handleSearch, handleStats, handlePinMemory, handleUnpinMemory, handleRunCleanup, handleRunDeduplication, handleDetectMigration, handleRunMigration, } from "./api-handlers.js";
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+ let server = null;
8
+ async function handleRequest(req) {
9
+ const url = new URL(req.url);
10
+ const path = url.pathname;
11
+ const method = req.method;
12
+ try {
13
+ if (path === "/" || path === "/index.html") {
14
+ return serveStaticFile("index.html", "text/html");
15
+ }
16
+ if (path === "/styles.css") {
17
+ return serveStaticFile("styles.css", "text/css");
18
+ }
19
+ if (path === "/app.js") {
20
+ return serveStaticFile("app.js", "application/javascript");
21
+ }
22
+ if (path === "/favicon.ico") {
23
+ return serveStaticFile("favicon.ico", "image/x-icon");
24
+ }
25
+ if (path === "/api/tags" && method === "GET") {
26
+ const result = await handleListTags();
27
+ return jsonResponse(result);
28
+ }
29
+ if (path === "/api/memories" && method === "GET") {
30
+ const tag = url.searchParams.get("tag") || undefined;
31
+ const page = parseInt(url.searchParams.get("page") || "1");
32
+ const pageSize = parseInt(url.searchParams.get("pageSize") || "20");
33
+ const result = await handleListMemories(tag, page, pageSize);
34
+ return jsonResponse(result);
35
+ }
36
+ if (path === "/api/memories" && method === "POST") {
37
+ const body = (await req.json());
38
+ const result = await handleAddMemory(body);
39
+ return jsonResponse(result);
40
+ }
41
+ if (path.startsWith("/api/memories/") && method === "DELETE") {
42
+ const id = path.split("/").pop();
43
+ if (!id) {
44
+ return jsonResponse({ success: false, error: "Invalid ID" });
45
+ }
46
+ const result = await handleDeleteMemory(id);
47
+ return jsonResponse(result);
48
+ }
49
+ if (path.startsWith("/api/memories/") && method === "PUT") {
50
+ const id = path.split("/").pop();
51
+ if (!id) {
52
+ return jsonResponse({ success: false, error: "Invalid ID" });
53
+ }
54
+ const body = (await req.json());
55
+ const result = await handleUpdateMemory(id, body);
56
+ return jsonResponse(result);
57
+ }
58
+ if (path === "/api/memories/bulk-delete" && method === "POST") {
59
+ const body = (await req.json());
60
+ const result = await handleBulkDelete(body.ids || []);
61
+ return jsonResponse(result);
62
+ }
63
+ if (path === "/api/search" && method === "GET") {
64
+ const query = url.searchParams.get("q");
65
+ const tag = url.searchParams.get("tag") || undefined;
66
+ const page = parseInt(url.searchParams.get("page") || "1");
67
+ const pageSize = parseInt(url.searchParams.get("pageSize") || "20");
68
+ if (!query) {
69
+ return jsonResponse({ success: false, error: "query parameter required" });
70
+ }
71
+ const result = await handleSearch(query, tag, page, pageSize);
72
+ return jsonResponse(result);
73
+ }
74
+ if (path === "/api/stats" && method === "GET") {
75
+ const result = await handleStats();
76
+ return jsonResponse(result);
77
+ }
78
+ if (path.match(/^\/api\/memories\/[^/]+\/pin$/) && method === "POST") {
79
+ const id = path.split("/")[3];
80
+ if (!id) {
81
+ return jsonResponse({ success: false, error: "Invalid ID" });
82
+ }
83
+ const result = await handlePinMemory(id);
84
+ return jsonResponse(result);
85
+ }
86
+ if (path.match(/^\/api\/memories\/[^/]+\/unpin$/) && method === "POST") {
87
+ const id = path.split("/")[3];
88
+ if (!id) {
89
+ return jsonResponse({ success: false, error: "Invalid ID" });
90
+ }
91
+ const result = await handleUnpinMemory(id);
92
+ return jsonResponse(result);
93
+ }
94
+ if (path === "/api/cleanup" && method === "POST") {
95
+ const result = await handleRunCleanup();
96
+ return jsonResponse(result);
97
+ }
98
+ if (path === "/api/deduplicate" && method === "POST") {
99
+ const result = await handleRunDeduplication();
100
+ return jsonResponse(result);
101
+ }
102
+ if (path === "/api/migration/detect" && method === "GET") {
103
+ const result = await handleDetectMigration();
104
+ return jsonResponse(result);
105
+ }
106
+ if (path === "/api/migration/run" && method === "POST") {
107
+ const body = (await req.json());
108
+ const strategy = body.strategy || "fresh-start";
109
+ if (strategy !== "fresh-start" && strategy !== "re-embed") {
110
+ return jsonResponse({ success: false, error: "Invalid strategy" });
111
+ }
112
+ const result = await handleRunMigration(strategy);
113
+ return jsonResponse(result);
114
+ }
115
+ return new Response("Not Found", { status: 404 });
116
+ }
117
+ catch (error) {
118
+ return jsonResponse({
119
+ success: false,
120
+ error: String(error),
121
+ }, 500);
122
+ }
123
+ }
124
+ function serveStaticFile(filename, contentType) {
125
+ try {
126
+ const webDir = join(__dirname, "..", "web");
127
+ const filePath = join(webDir, filename);
128
+ if (contentType.startsWith("image/")) {
129
+ const content = readFileSync(filePath);
130
+ return new Response(content, {
131
+ headers: {
132
+ "Content-Type": contentType,
133
+ "Cache-Control": "public, max-age=86400",
134
+ },
135
+ });
136
+ }
137
+ const content = readFileSync(filePath, "utf-8");
138
+ return new Response(content, {
139
+ headers: {
140
+ "Content-Type": contentType,
141
+ "Cache-Control": "no-cache",
142
+ },
143
+ });
144
+ }
145
+ catch (error) {
146
+ return new Response("File not found", { status: 404 });
147
+ }
148
+ }
149
+ function jsonResponse(data, status = 200) {
150
+ return new Response(JSON.stringify(data), {
151
+ status,
152
+ headers: {
153
+ "Content-Type": "application/json",
154
+ "Access-Control-Allow-Origin": "*",
155
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
156
+ "Access-Control-Allow-Headers": "Content-Type",
157
+ },
158
+ });
159
+ }
160
+ self.onmessage = async (event) => {
161
+ const message = event.data;
162
+ try {
163
+ switch (message.type) {
164
+ case "start": {
165
+ if (server) {
166
+ self.postMessage({
167
+ type: "error",
168
+ error: "Server already running",
169
+ });
170
+ return;
171
+ }
172
+ server = Bun.serve({
173
+ port: message.port,
174
+ hostname: message.host,
175
+ fetch: handleRequest,
176
+ });
177
+ self.postMessage({
178
+ type: "started",
179
+ url: `http://${message.host}:${message.port}`,
180
+ });
181
+ break;
182
+ }
183
+ case "stop": {
184
+ if (server) {
185
+ server.stop();
186
+ server = null;
187
+ self.postMessage({
188
+ type: "stopped",
189
+ });
190
+ }
191
+ else {
192
+ self.postMessage({
193
+ type: "error",
194
+ error: "Server not running",
195
+ });
196
+ }
197
+ break;
198
+ }
199
+ case "status": {
200
+ self.postMessage({
201
+ type: "status",
202
+ running: server !== null,
203
+ });
204
+ break;
205
+ }
206
+ default: {
207
+ self.postMessage({
208
+ type: "error",
209
+ error: `Unknown message type: ${message.type}`,
210
+ });
211
+ break;
212
+ }
213
+ }
214
+ }
215
+ catch (error) {
216
+ self.postMessage({
217
+ type: "error",
218
+ error: String(error),
219
+ });
220
+ }
221
+ };
@@ -0,0 +1,22 @@
1
+ interface WebServerConfig {
2
+ port: number;
3
+ host: string;
4
+ enabled: boolean;
5
+ }
6
+ export declare class WebServer {
7
+ private worker;
8
+ private config;
9
+ private isOwner;
10
+ private startPromise;
11
+ constructor(config: WebServerConfig);
12
+ start(): Promise<void>;
13
+ private _start;
14
+ stop(): Promise<void>;
15
+ isRunning(): boolean;
16
+ isServerOwner(): boolean;
17
+ getUrl(): string;
18
+ checkServerAvailable(): Promise<boolean>;
19
+ }
20
+ export declare function startWebServer(config: WebServerConfig): Promise<WebServer>;
21
+ export {};
22
+ //# sourceMappingURL=web-server.d.ts.map
@@ -0,0 +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;gBAEtC,MAAM,EAAE,eAAe;IAI7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YASd,MAAM;IA8Dd,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC3B,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"}
@@ -0,0 +1,134 @@
1
+ import { join, dirname } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { log } from "./logger.js";
4
+ const __filename = fileURLToPath(import.meta.url);
5
+ const __dirname = dirname(__filename);
6
+ export class WebServer {
7
+ worker = null;
8
+ config;
9
+ isOwner = false;
10
+ startPromise = null;
11
+ constructor(config) {
12
+ this.config = config;
13
+ }
14
+ async start() {
15
+ if (this.startPromise) {
16
+ return this.startPromise;
17
+ }
18
+ this.startPromise = this._start();
19
+ return this.startPromise;
20
+ }
21
+ async _start() {
22
+ if (!this.config.enabled) {
23
+ log("Web server disabled in config");
24
+ return;
25
+ }
26
+ try {
27
+ const workerPath = join(__dirname, "web-server-worker.js");
28
+ this.worker = new Worker(workerPath);
29
+ const startedPromise = new Promise((resolve, reject) => {
30
+ const timeout = setTimeout(() => {
31
+ reject(new Error("Worker start timeout"));
32
+ }, 10000);
33
+ this.worker.onmessage = (event) => {
34
+ clearTimeout(timeout);
35
+ const response = event.data;
36
+ if (response.type === "started") {
37
+ this.isOwner = true;
38
+ log("Web server started (owner)", { url: response.url });
39
+ resolve();
40
+ }
41
+ else if (response.type === "error") {
42
+ const errorMsg = response.error || "Unknown error";
43
+ if (errorMsg.includes("EADDRINUSE") || errorMsg.includes("address already in use")) {
44
+ this.isOwner = false;
45
+ log("Web server already running (port in use)");
46
+ resolve();
47
+ }
48
+ else {
49
+ log("Web server worker error", { error: errorMsg });
50
+ reject(new Error(errorMsg));
51
+ }
52
+ }
53
+ };
54
+ this.worker.onerror = (error) => {
55
+ clearTimeout(timeout);
56
+ log("Web server worker error", { error: String(error) });
57
+ reject(error);
58
+ };
59
+ });
60
+ this.worker.postMessage({
61
+ type: "start",
62
+ port: this.config.port,
63
+ host: this.config.host,
64
+ });
65
+ await startedPromise;
66
+ }
67
+ catch (error) {
68
+ this.isOwner = false;
69
+ if (this.worker) {
70
+ this.worker.terminate();
71
+ this.worker = null;
72
+ }
73
+ log("Web server failed to start", { error: String(error) });
74
+ throw error;
75
+ }
76
+ }
77
+ async stop() {
78
+ if (!this.isOwner || !this.worker) {
79
+ log("Web server stop skipped (not owner or no worker)");
80
+ return;
81
+ }
82
+ return new Promise((resolve) => {
83
+ const timeout = setTimeout(() => {
84
+ if (this.worker) {
85
+ this.worker.terminate();
86
+ this.worker = null;
87
+ }
88
+ log("Web server stopped (timeout, forced termination)");
89
+ resolve();
90
+ }, 5000);
91
+ this.worker.onmessage = (event) => {
92
+ clearTimeout(timeout);
93
+ const response = event.data;
94
+ if (response.type === "stopped") {
95
+ if (this.worker) {
96
+ this.worker.terminate();
97
+ this.worker = null;
98
+ }
99
+ log("Web server stopped (owner exiting)");
100
+ resolve();
101
+ }
102
+ };
103
+ this.worker.postMessage({
104
+ type: "stop",
105
+ });
106
+ });
107
+ }
108
+ isRunning() {
109
+ return this.worker !== null;
110
+ }
111
+ isServerOwner() {
112
+ return this.isOwner;
113
+ }
114
+ getUrl() {
115
+ return `http://${this.config.host}:${this.config.port}`;
116
+ }
117
+ async checkServerAvailable() {
118
+ try {
119
+ const response = await fetch(`${this.getUrl()}/api/stats`, {
120
+ method: "GET",
121
+ signal: AbortSignal.timeout(2000),
122
+ });
123
+ return response.ok;
124
+ }
125
+ catch {
126
+ return false;
127
+ }
128
+ }
129
+ }
130
+ export async function startWebServer(config) {
131
+ const server = new WebServer(config);
132
+ await server.start();
133
+ return server;
134
+ }
@@ -0,0 +1,48 @@
1
+ export type MemoryScope = "user" | "project";
2
+ export type MemoryType = string;
3
+ export type ConversationRole = "user" | "assistant" | "system" | "tool";
4
+ export type ConversationContentPart = {
5
+ type: "text";
6
+ text: string;
7
+ } | {
8
+ type: "image_url";
9
+ imageUrl: {
10
+ url: string;
11
+ };
12
+ };
13
+ export interface ConversationToolCall {
14
+ id: string;
15
+ type: "function";
16
+ function: {
17
+ name: string;
18
+ arguments: string;
19
+ };
20
+ }
21
+ export interface ConversationMessage {
22
+ role: ConversationRole;
23
+ content: string | ConversationContentPart[];
24
+ name?: string;
25
+ tool_calls?: ConversationToolCall[];
26
+ tool_call_id?: string;
27
+ }
28
+ export interface ConversationIngestResponse {
29
+ id: string;
30
+ conversationId: string;
31
+ status: string;
32
+ }
33
+ export interface MemoryMetadata {
34
+ type?: MemoryType;
35
+ source?: "manual" | "auto-capture" | "import" | "api";
36
+ tool?: string;
37
+ sessionID?: string;
38
+ reasoning?: string;
39
+ captureTimestamp?: number;
40
+ displayName?: string;
41
+ userName?: string;
42
+ userEmail?: string;
43
+ projectPath?: string;
44
+ projectName?: string;
45
+ gitRepoUrl?: string;
46
+ [key: string]: unknown;
47
+ }
48
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;AAE7C,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC;AAEhC,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,MAAM,CAAC;AAExE,MAAM,MAAM,uBAAuB,GAC/B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAErD,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,uBAAuB,EAAE,CAAC;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,MAAM,CAAC,EAAE,QAAQ,GAAG,cAAc,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/web/app.js"],"names":[],"mappings":""}