opencode-mem 2.1.1 → 2.2.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.
- package/README.md +54 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +0 -4
- package/dist/services/ai/providers/anthropic-messages.d.ts.map +1 -1
- package/dist/services/ai/providers/anthropic-messages.js +11 -17
- package/dist/services/ai/providers/openai-chat-completion.d.ts.map +1 -1
- package/dist/services/ai/providers/openai-chat-completion.js +11 -17
- package/dist/services/ai/providers/openai-responses.d.ts.map +1 -1
- package/dist/services/ai/providers/openai-responses.js +11 -17
- package/dist/services/api-handlers.d.ts +4 -0
- package/dist/services/api-handlers.d.ts.map +1 -1
- package/dist/services/api-handlers.js +155 -1
- package/dist/services/auto-capture.js +2 -2
- package/dist/services/deduplication-service.d.ts.map +1 -1
- package/dist/services/deduplication-service.js +1 -3
- package/dist/services/logger.js +1 -1
- package/dist/services/user-memory-learning.js +3 -1
- package/dist/services/user-profile/user-profile-manager.d.ts.map +1 -1
- package/dist/services/web-server-worker.js +29 -1
- package/dist/web/app.js +249 -29
- package/dist/web/index.html +31 -5
- package/dist/web/styles.css +309 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ OpenCode Memory provides AI coding agents with the ability to remember and recal
|
|
|
14
14
|
- **Dual Memory Scopes**: Separate user-level and project-level memory contexts
|
|
15
15
|
- **Unified Timeline**: Browse memories and prompts together with linking support
|
|
16
16
|
- **Prompt-Memory Linking**: Bidirectional links between prompts and generated memories
|
|
17
|
-
- **User
|
|
17
|
+
- **User Profile System**: Structured learning with preferences, patterns, workflows, and skill assessment
|
|
18
18
|
- **Web Interface**: Full-featured UI for memory management and search
|
|
19
19
|
- **Auto-Capture System**: Intelligent prompt-based memory extraction
|
|
20
20
|
- **Multi-Provider AI**: Support for OpenAI, Anthropic, and OpenAI-compatible APIs
|
|
@@ -52,15 +52,25 @@ bun run build
|
|
|
52
52
|
### Basic Usage
|
|
53
53
|
|
|
54
54
|
```typescript
|
|
55
|
-
memory({ mode: "add", content: "
|
|
56
|
-
memory({ mode: "search", query: "
|
|
55
|
+
memory({ mode: "add", content: "Project uses microservices", scope: "project" })
|
|
56
|
+
memory({ mode: "search", query: "architecture decisions", scope: "project" })
|
|
57
57
|
memory({ mode: "profile" })
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
**Note**: User-scoped `add` is deprecated in v2.2+. Use profile system instead.
|
|
61
|
+
|
|
60
62
|
### Web Interface
|
|
61
63
|
|
|
62
64
|
Access at `http://127.0.0.1:4747` to browse memories, view prompt-memory links, and manage your memory database.
|
|
63
65
|
|
|
66
|
+
**Project Memory Timeline:**
|
|
67
|
+
|
|
68
|
+

|
|
69
|
+
|
|
70
|
+
**User Profile Viewer:**
|
|
71
|
+
|
|
72
|
+

|
|
73
|
+
|
|
64
74
|
### Configuration
|
|
65
75
|
|
|
66
76
|
Configuration file: `~/.config/opencode/opencode-mem.jsonc`
|
|
@@ -75,10 +85,28 @@ Configuration file: `~/.config/opencode/opencode-mem.jsonc`
|
|
|
75
85
|
"memoryProvider": "openai-chat",
|
|
76
86
|
"memoryModel": "gpt-4",
|
|
77
87
|
"memoryApiUrl": "https://api.openai.com/v1",
|
|
78
|
-
"memoryApiKey": "sk-..."
|
|
88
|
+
"memoryApiKey": "sk-...",
|
|
89
|
+
"userMemoryAnalysisInterval": 10,
|
|
90
|
+
"userProfileMaxPreferences": 20,
|
|
91
|
+
"userProfileMaxPatterns": 15,
|
|
92
|
+
"userProfileMaxWorkflows": 10,
|
|
93
|
+
"userProfileConfidenceDecayDays": 30,
|
|
94
|
+
"userProfileChangelogRetentionCount": 5
|
|
79
95
|
}
|
|
80
96
|
```
|
|
81
97
|
|
|
98
|
+
## Breaking Changes (v2.2)
|
|
99
|
+
|
|
100
|
+
**User-scoped memories deprecated in favor of structured user profiles:**
|
|
101
|
+
|
|
102
|
+
- Removed: User-scoped `addMemory` (now returns error)
|
|
103
|
+
- Changed: `memory({ mode: "profile" })` returns new structure (preferences/patterns/workflows/skillLevel)
|
|
104
|
+
- Added: 5 new config options for profile management
|
|
105
|
+
- New behavior: User learning creates/updates structured profile instead of individual memories
|
|
106
|
+
- Migration: Existing user memories remain readable but new ones cannot be created
|
|
107
|
+
|
|
108
|
+
**Migration required**: Update code using `mode: "profile"` to handle new structure.
|
|
109
|
+
|
|
82
110
|
## Breaking Changes (v2.0)
|
|
83
111
|
|
|
84
112
|
**Token-based auto-capture has been replaced with prompt-based system:**
|
|
@@ -98,6 +126,7 @@ For detailed documentation, see the [Wiki](https://github.com/tickernelz/opencod
|
|
|
98
126
|
- [Installation Guide](https://github.com/tickernelz/opencode-mem/wiki/Installation-Guide)
|
|
99
127
|
- [Quick Start](https://github.com/tickernelz/opencode-mem/wiki/Quick-Start)
|
|
100
128
|
- [Configuration Guide](https://github.com/tickernelz/opencode-mem/wiki/Configuration-Guide)
|
|
129
|
+
- [User Profile System](https://github.com/tickernelz/opencode-mem/wiki/User-Profile)
|
|
101
130
|
- [Memory Operations](https://github.com/tickernelz/opencode-mem/wiki/Memory-Operations)
|
|
102
131
|
- [Auto-Capture System](https://github.com/tickernelz/opencode-mem/wiki/Auto-Capture-System)
|
|
103
132
|
- [Web Interface](https://github.com/tickernelz/opencode-mem/wiki/Web-Interface)
|
|
@@ -121,18 +150,21 @@ Automatically extracts memories from conversations:
|
|
|
121
150
|
3. Links memory to source prompt
|
|
122
151
|
4. Skips non-technical conversations
|
|
123
152
|
|
|
124
|
-
### User
|
|
153
|
+
### User Profile System
|
|
154
|
+
|
|
155
|
+
Builds structured user profile from conversation history (default: every 10 prompts):
|
|
125
156
|
|
|
126
|
-
|
|
157
|
+
- **Preferences**: Code style, communication style, tool preferences (with confidence scores)
|
|
158
|
+
- **Patterns**: Recurring topics, problem domains, technical interests (with frequency tracking)
|
|
159
|
+
- **Workflows**: Development sequences, habits, learning style
|
|
160
|
+
- **Skill Level**: Overall and per-domain assessment
|
|
127
161
|
|
|
128
|
-
|
|
129
|
-
- Communication patterns
|
|
130
|
-
- Tool preferences
|
|
131
|
-
- Skill level indicators
|
|
162
|
+
Profile includes versioning, changelog, and confidence decay mechanism.
|
|
132
163
|
|
|
133
164
|
### Web Interface
|
|
134
165
|
|
|
135
166
|
- Unified timeline of memories and prompts
|
|
167
|
+
- User profile viewer with changelog
|
|
136
168
|
- Visual prompt-memory link indicators
|
|
137
169
|
- Cascade delete for linked items
|
|
138
170
|
- Bulk operations
|
|
@@ -144,7 +176,7 @@ Analyzes batches of prompts to identify patterns (default: every 10 prompts):
|
|
|
144
176
|
### Memory Tool
|
|
145
177
|
|
|
146
178
|
```typescript
|
|
147
|
-
memory({ mode: "add", content: "...", scope: "
|
|
179
|
+
memory({ mode: "add", content: "...", scope: "project" })
|
|
148
180
|
memory({ mode: "search", query: "...", scope: "user|project" })
|
|
149
181
|
memory({ mode: "list", scope: "user|project", limit: 10 })
|
|
150
182
|
memory({ mode: "profile" })
|
|
@@ -154,14 +186,25 @@ memory({ mode: "auto-capture-stats" })
|
|
|
154
186
|
memory({ mode: "capture-now" })
|
|
155
187
|
```
|
|
156
188
|
|
|
189
|
+
**Note**: `scope: "user"` for `add` mode is deprecated in v2.2+.
|
|
190
|
+
|
|
157
191
|
### REST API
|
|
158
192
|
|
|
193
|
+
**Memory & Prompt Management:**
|
|
159
194
|
- `GET /api/memories?scope=project&includePrompts=true` - List memories/prompts
|
|
160
195
|
- `POST /api/memories` - Create memory
|
|
161
196
|
- `PUT /api/memories/:id` - Update memory
|
|
162
197
|
- `DELETE /api/memories/:id?cascade=true` - Delete memory (and linked prompt)
|
|
163
198
|
- `DELETE /api/prompts/:id?cascade=true` - Delete prompt (and linked memory)
|
|
164
199
|
- `POST /api/search` - Vector search
|
|
200
|
+
|
|
201
|
+
**User Profile:**
|
|
202
|
+
- `GET /api/profile` - Get user profile
|
|
203
|
+
- `GET /api/profile/changelog?limit=5` - Get profile changelog
|
|
204
|
+
- `GET /api/profile/snapshot/:changelogId` - Get profile snapshot
|
|
205
|
+
- `POST /api/profile/refresh` - Force profile refresh
|
|
206
|
+
|
|
207
|
+
**Maintenance:**
|
|
165
208
|
- `POST /api/cleanup` - Run cleanup
|
|
166
209
|
- `POST /api/deduplicate` - Run deduplication
|
|
167
210
|
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAyC/D,eAAO,MAAM,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAyC/D,eAAO,MAAM,iBAAiB,EAAE,MAulB/B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -109,7 +109,6 @@ export const OpenCodeMemPlugin = async (ctx) => {
|
|
|
109
109
|
"chat.message": async (input, output) => {
|
|
110
110
|
if (!isConfigured())
|
|
111
111
|
return;
|
|
112
|
-
const start = Date.now();
|
|
113
112
|
try {
|
|
114
113
|
const textParts = output.parts.filter((p) => p.type === "text");
|
|
115
114
|
if (textParts.length === 0)
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":";AACA,QAAA,MAAQ,iBAAiB,sCAA+B,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAC7B,eAAe,iBAAiB,CAAC"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
import { dirname } from "node:path";
|
|
4
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = dirname(__filename);
|
|
6
2
|
const { OpenCodeMemPlugin } = await import("./index.js");
|
|
7
3
|
export { OpenCodeMemPlugin };
|
|
8
4
|
export default OpenCodeMemPlugin;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AA2BvF,qBAAa,yBAA0B,SAAQ,cAAc;IAC3D,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,gBAAgB;IAK3D,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,OAAO;IAIpB,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;IAmJ1B,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,gBAAgB;
|
|
1
|
+
{"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AA2BvF,qBAAa,yBAA0B,SAAQ,cAAc;IAC3D,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,gBAAgB;IAK3D,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,OAAO;IAIpB,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;IAmJ1B,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,gBAAgB;CAsBzB"}
|
|
@@ -153,24 +153,18 @@ export class AnthropicMessagesProvider extends BaseAIProvider {
|
|
|
153
153
|
if (!data || typeof data !== "object") {
|
|
154
154
|
throw new Error("Response is not an object");
|
|
155
155
|
}
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
return (m &&
|
|
159
|
-
typeof m === "object" &&
|
|
160
|
-
typeof m.summary === "string" &&
|
|
161
|
-
m.summary.trim().length > 0 &&
|
|
162
|
-
(m.scope === "user" || m.scope === "project") &&
|
|
163
|
-
typeof m.type === "string" &&
|
|
164
|
-
m.type.trim().length > 0);
|
|
165
|
-
});
|
|
166
|
-
if (validMemories.length === 0) {
|
|
167
|
-
throw new Error("No valid memories in response");
|
|
168
|
-
}
|
|
169
|
-
return { memories: validMemories };
|
|
156
|
+
if (Array.isArray(data)) {
|
|
157
|
+
throw new Error("Response cannot be an array");
|
|
170
158
|
}
|
|
171
|
-
|
|
172
|
-
|
|
159
|
+
const keys = Object.keys(data);
|
|
160
|
+
if (keys.length === 0) {
|
|
161
|
+
throw new Error("Response object is empty");
|
|
162
|
+
}
|
|
163
|
+
for (const key of keys) {
|
|
164
|
+
if (data[key] === undefined || data[key] === null) {
|
|
165
|
+
throw new Error(`Response field '${key}' is null or undefined`);
|
|
166
|
+
}
|
|
173
167
|
}
|
|
174
|
-
|
|
168
|
+
return data;
|
|
175
169
|
}
|
|
176
170
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-chat-completion.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/openai-chat-completion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAkBlE,qBAAa,4BAA6B,SAAQ,cAAc;IAC9D,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,gBAAgB;IAK3D,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,OAAO;IAIpB,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;IA2K1B,OAAO,CAAC,gBAAgB;
|
|
1
|
+
{"version":3,"file":"openai-chat-completion.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/openai-chat-completion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAkBlE,qBAAa,4BAA6B,SAAQ,cAAc;IAC9D,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,gBAAgB;IAK3D,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,OAAO;IAIpB,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;IA2K1B,OAAO,CAAC,gBAAgB;CAsBzB"}
|
|
@@ -158,24 +158,18 @@ export class OpenAIChatCompletionProvider extends BaseAIProvider {
|
|
|
158
158
|
if (!data || typeof data !== "object") {
|
|
159
159
|
throw new Error("Response is not an object");
|
|
160
160
|
}
|
|
161
|
-
if (
|
|
162
|
-
|
|
163
|
-
return (m &&
|
|
164
|
-
typeof m === "object" &&
|
|
165
|
-
typeof m.summary === "string" &&
|
|
166
|
-
m.summary.trim().length > 0 &&
|
|
167
|
-
(m.scope === "user" || m.scope === "project") &&
|
|
168
|
-
typeof m.type === "string" &&
|
|
169
|
-
m.type.trim().length > 0);
|
|
170
|
-
});
|
|
171
|
-
if (validMemories.length === 0) {
|
|
172
|
-
throw new Error("No valid memories in response");
|
|
173
|
-
}
|
|
174
|
-
return { memories: validMemories };
|
|
161
|
+
if (Array.isArray(data)) {
|
|
162
|
+
throw new Error("Response cannot be an array");
|
|
175
163
|
}
|
|
176
|
-
|
|
177
|
-
|
|
164
|
+
const keys = Object.keys(data);
|
|
165
|
+
if (keys.length === 0) {
|
|
166
|
+
throw new Error("Response object is empty");
|
|
167
|
+
}
|
|
168
|
+
for (const key of keys) {
|
|
169
|
+
if (data[key] === undefined || data[key] === null) {
|
|
170
|
+
throw new Error(`Response field '${key}' is null or undefined`);
|
|
171
|
+
}
|
|
178
172
|
}
|
|
179
|
-
|
|
173
|
+
return data;
|
|
180
174
|
}
|
|
181
175
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-responses.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/openai-responses.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAsBvF,qBAAa,uBAAwB,SAAQ,cAAc;IACzD,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,gBAAgB;IAK3D,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,OAAO;IAIpB,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;IAuH1B,OAAO,CAAC,eAAe;IAsCvB,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,gBAAgB;
|
|
1
|
+
{"version":3,"file":"openai-responses.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/openai-responses.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAsBvF,qBAAa,uBAAwB,SAAQ,cAAc;IACzD,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,gBAAgB;IAK3D,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,OAAO;IAIpB,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;IAuH1B,OAAO,CAAC,eAAe;IAsCvB,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,gBAAgB;CAsBzB"}
|
|
@@ -168,24 +168,18 @@ export class OpenAIResponsesProvider extends BaseAIProvider {
|
|
|
168
168
|
if (!data || typeof data !== "object") {
|
|
169
169
|
throw new Error("Response is not an object");
|
|
170
170
|
}
|
|
171
|
-
if (
|
|
172
|
-
|
|
173
|
-
return (m &&
|
|
174
|
-
typeof m === "object" &&
|
|
175
|
-
typeof m.summary === "string" &&
|
|
176
|
-
m.summary.trim().length > 0 &&
|
|
177
|
-
(m.scope === "user" || m.scope === "project") &&
|
|
178
|
-
typeof m.type === "string" &&
|
|
179
|
-
m.type.trim().length > 0);
|
|
180
|
-
});
|
|
181
|
-
if (validMemories.length === 0) {
|
|
182
|
-
throw new Error("No valid memories in response");
|
|
183
|
-
}
|
|
184
|
-
return { memories: validMemories };
|
|
171
|
+
if (Array.isArray(data)) {
|
|
172
|
+
throw new Error("Response cannot be an array");
|
|
185
173
|
}
|
|
186
|
-
|
|
187
|
-
|
|
174
|
+
const keys = Object.keys(data);
|
|
175
|
+
if (keys.length === 0) {
|
|
176
|
+
throw new Error("Response object is empty");
|
|
177
|
+
}
|
|
178
|
+
for (const key of keys) {
|
|
179
|
+
if (data[key] === undefined || data[key] === null) {
|
|
180
|
+
throw new Error(`Response field '${key}' is null or undefined`);
|
|
181
|
+
}
|
|
188
182
|
}
|
|
189
|
-
|
|
183
|
+
return data;
|
|
190
184
|
}
|
|
191
185
|
}
|
|
@@ -106,5 +106,9 @@ export declare function handleDeletePrompt(id: string, cascade?: boolean): Promi
|
|
|
106
106
|
export declare function handleBulkDeletePrompts(ids: string[], cascade?: boolean): Promise<ApiResponse<{
|
|
107
107
|
deleted: number;
|
|
108
108
|
}>>;
|
|
109
|
+
export declare function handleGetUserProfile(userId?: string): Promise<ApiResponse<any>>;
|
|
110
|
+
export declare function handleGetProfileChangelog(profileId: string, limit?: number): Promise<ApiResponse<any[]>>;
|
|
111
|
+
export declare function handleGetProfileSnapshot(changelogId: string): Promise<ApiResponse<any>>;
|
|
112
|
+
export declare function handleRefreshProfile(userId?: string): Promise<ApiResponse<any>>;
|
|
109
113
|
export {};
|
|
110
114
|
//# sourceMappingURL=api-handlers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-handlers.d.ts","sourceRoot":"","sources":["../../src/services/api-handlers.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGpD,UAAU,WAAW,CAAC,CAAC,GAAG,GAAG;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,MAAM;IACd,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,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,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,UAAU,OAAO;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,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;CACrB;AAED,UAAU,iBAAiB,CAAC,CAAC;IAC3B,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAwCD,wBAAsB,cAAc,IAAI,OAAO,CAC7C,WAAW,CAAC;IAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC,CACrD,CAgDA;AAED,wBAAsB,kBAAkB,CACtC,GAAG,CAAC,EAAE,MAAM,EACZ,IAAI,GAAE,MAAU,EAChB,QAAQ,GAAE,MAAW,EACrB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,EAC1B,cAAc,GAAE,OAAc,GAC7B,OAAO,CAAC,WAAW,CAAC,iBAAiB,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"api-handlers.d.ts","sourceRoot":"","sources":["../../src/services/api-handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -149,7 +149,40 @@ export async function handleListMemories(tag, page = 1, pageSize = 20, scope, in
|
|
|
149
149
|
}));
|
|
150
150
|
timeline = [...memoriesWithType, ...promptsWithType];
|
|
151
151
|
}
|
|
152
|
-
|
|
152
|
+
const linkedPairs = new Map();
|
|
153
|
+
const standalone = [];
|
|
154
|
+
for (const item of timeline) {
|
|
155
|
+
if (item.type === "memory" && item.linkedPromptId) {
|
|
156
|
+
if (!linkedPairs.has(item.linkedPromptId)) {
|
|
157
|
+
linkedPairs.set(item.linkedPromptId, { memory: item, prompt: null });
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
linkedPairs.get(item.linkedPromptId).memory = item;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else if (item.type === "prompt" && item.linkedMemoryId) {
|
|
164
|
+
if (!linkedPairs.has(item.id)) {
|
|
165
|
+
linkedPairs.set(item.id, { memory: null, prompt: item });
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
linkedPairs.get(item.id).prompt = item;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
standalone.push(item);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const sortedTimeline = [];
|
|
176
|
+
const pairs = Array.from(linkedPairs.values())
|
|
177
|
+
.filter((p) => p.memory && p.prompt)
|
|
178
|
+
.sort((a, b) => b.memory.createdAt - a.memory.createdAt);
|
|
179
|
+
for (const pair of pairs) {
|
|
180
|
+
sortedTimeline.push(pair.memory);
|
|
181
|
+
sortedTimeline.push(pair.prompt);
|
|
182
|
+
}
|
|
183
|
+
standalone.sort((a, b) => b.createdAt - a.createdAt);
|
|
184
|
+
sortedTimeline.push(...standalone);
|
|
185
|
+
timeline = sortedTimeline;
|
|
153
186
|
const total = timeline.length;
|
|
154
187
|
const totalPages = Math.ceil(total / pageSize);
|
|
155
188
|
const offset = (page - 1) * pageSize;
|
|
@@ -211,6 +244,12 @@ export async function handleAddMemory(data) {
|
|
|
211
244
|
await embeddingService.warmup();
|
|
212
245
|
const vector = await embeddingService.embedWithTimeout(data.content);
|
|
213
246
|
const { scope, hash } = extractScopeFromTag(data.containerTag);
|
|
247
|
+
if (scope === "user") {
|
|
248
|
+
return {
|
|
249
|
+
success: false,
|
|
250
|
+
error: "User-scoped memories are deprecated. Use user profile system instead.",
|
|
251
|
+
};
|
|
252
|
+
}
|
|
214
253
|
const shard = shardManager.getWriteShard(scope, hash);
|
|
215
254
|
const id = `mem_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
216
255
|
const now = Date.now();
|
|
@@ -605,3 +644,118 @@ export async function handleBulkDeletePrompts(ids, cascade = false) {
|
|
|
605
644
|
return { success: false, error: String(error) };
|
|
606
645
|
}
|
|
607
646
|
}
|
|
647
|
+
export async function handleGetUserProfile(userId) {
|
|
648
|
+
try {
|
|
649
|
+
const { userProfileManager } = await import("./user-profile/user-profile-manager.js");
|
|
650
|
+
const { getTags } = await import("./tags.js");
|
|
651
|
+
let targetUserId = userId;
|
|
652
|
+
if (!targetUserId) {
|
|
653
|
+
const tags = getTags(process.cwd());
|
|
654
|
+
targetUserId = tags.user.userEmail || "unknown";
|
|
655
|
+
}
|
|
656
|
+
const profile = userProfileManager.getActiveProfile(targetUserId);
|
|
657
|
+
if (!profile) {
|
|
658
|
+
return {
|
|
659
|
+
success: true,
|
|
660
|
+
data: {
|
|
661
|
+
exists: false,
|
|
662
|
+
userId: targetUserId,
|
|
663
|
+
message: "No profile found. Keep chatting to build your profile.",
|
|
664
|
+
},
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
const profileData = JSON.parse(profile.profileData);
|
|
668
|
+
return {
|
|
669
|
+
success: true,
|
|
670
|
+
data: {
|
|
671
|
+
exists: true,
|
|
672
|
+
id: profile.id,
|
|
673
|
+
userId: profile.userId,
|
|
674
|
+
displayName: profile.displayName,
|
|
675
|
+
userName: profile.userName,
|
|
676
|
+
userEmail: profile.userEmail,
|
|
677
|
+
version: profile.version,
|
|
678
|
+
createdAt: safeToISOString(profile.createdAt),
|
|
679
|
+
lastAnalyzedAt: safeToISOString(profile.lastAnalyzedAt),
|
|
680
|
+
totalPromptsAnalyzed: profile.totalPromptsAnalyzed,
|
|
681
|
+
profileData,
|
|
682
|
+
},
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
catch (error) {
|
|
686
|
+
log("handleGetUserProfile: error", { error: String(error) });
|
|
687
|
+
return { success: false, error: String(error) };
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
export async function handleGetProfileChangelog(profileId, limit = 5) {
|
|
691
|
+
try {
|
|
692
|
+
if (!profileId) {
|
|
693
|
+
return { success: false, error: "profileId is required" };
|
|
694
|
+
}
|
|
695
|
+
const { userProfileManager } = await import("./user-profile/user-profile-manager.js");
|
|
696
|
+
const changelogs = userProfileManager.getProfileChangelogs(profileId, limit);
|
|
697
|
+
const formattedChangelogs = changelogs.map((c) => ({
|
|
698
|
+
id: c.id,
|
|
699
|
+
profileId: c.profileId,
|
|
700
|
+
version: c.version,
|
|
701
|
+
changeType: c.changeType,
|
|
702
|
+
changeSummary: c.changeSummary,
|
|
703
|
+
createdAt: safeToISOString(c.createdAt),
|
|
704
|
+
}));
|
|
705
|
+
return { success: true, data: formattedChangelogs };
|
|
706
|
+
}
|
|
707
|
+
catch (error) {
|
|
708
|
+
log("handleGetProfileChangelog: error", { error: String(error) });
|
|
709
|
+
return { success: false, error: String(error) };
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
export async function handleGetProfileSnapshot(changelogId) {
|
|
713
|
+
try {
|
|
714
|
+
if (!changelogId) {
|
|
715
|
+
return { success: false, error: "changelogId is required" };
|
|
716
|
+
}
|
|
717
|
+
const { userProfileManager } = await import("./user-profile/user-profile-manager.js");
|
|
718
|
+
const changelogs = userProfileManager.getProfileChangelogs("", 1000);
|
|
719
|
+
const changelog = changelogs.find((c) => c.id === changelogId);
|
|
720
|
+
if (!changelog) {
|
|
721
|
+
return { success: false, error: "Changelog not found" };
|
|
722
|
+
}
|
|
723
|
+
const profileData = JSON.parse(changelog.profileDataSnapshot);
|
|
724
|
+
return {
|
|
725
|
+
success: true,
|
|
726
|
+
data: {
|
|
727
|
+
version: changelog.version,
|
|
728
|
+
createdAt: safeToISOString(changelog.createdAt),
|
|
729
|
+
profileData,
|
|
730
|
+
},
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
catch (error) {
|
|
734
|
+
log("handleGetProfileSnapshot: error", { error: String(error) });
|
|
735
|
+
return { success: false, error: String(error) };
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
export async function handleRefreshProfile(userId) {
|
|
739
|
+
try {
|
|
740
|
+
const { getTags } = await import("./tags.js");
|
|
741
|
+
const { userPromptManager } = await import("./user-prompt/user-prompt-manager.js");
|
|
742
|
+
let targetUserId = userId;
|
|
743
|
+
if (!targetUserId) {
|
|
744
|
+
const tags = getTags(process.cwd());
|
|
745
|
+
targetUserId = tags.user.userEmail || "unknown";
|
|
746
|
+
}
|
|
747
|
+
const unanalyzedCount = userPromptManager.countUnanalyzedForUserLearning();
|
|
748
|
+
return {
|
|
749
|
+
success: true,
|
|
750
|
+
data: {
|
|
751
|
+
message: "Profile refresh queued",
|
|
752
|
+
unanalyzedPrompts: unanalyzedCount,
|
|
753
|
+
note: "Profile will be updated when threshold is reached",
|
|
754
|
+
},
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
catch (error) {
|
|
758
|
+
log("handleRefreshProfile: error", { error: String(error) });
|
|
759
|
+
return { success: false, error: String(error) };
|
|
760
|
+
}
|
|
761
|
+
}
|
|
@@ -37,7 +37,7 @@ export async function performAutoCapture(ctx, sessionID, directory) {
|
|
|
37
37
|
const tags = getTags(directory);
|
|
38
38
|
const latestMemory = await getLatestProjectMemory(tags.project.tag);
|
|
39
39
|
const context = buildMarkdownContext(prompt.content, textResponses, toolCalls, latestMemory);
|
|
40
|
-
const summaryResult = await generateSummary(
|
|
40
|
+
const summaryResult = await generateSummary(context, sessionID);
|
|
41
41
|
if (!summaryResult || summaryResult.type === "skip") {
|
|
42
42
|
log("Auto-capture: skipped non-technical conversation", { sessionID });
|
|
43
43
|
userPromptManager.deletePrompt(prompt.id);
|
|
@@ -179,7 +179,7 @@ function buildMarkdownContext(userPrompt, textResponses, toolCalls, latestMemory
|
|
|
179
179
|
}
|
|
180
180
|
return sections.join("\n");
|
|
181
181
|
}
|
|
182
|
-
async function generateSummary(
|
|
182
|
+
async function generateSummary(context, sessionID) {
|
|
183
183
|
if (!CONFIG.memoryModel || !CONFIG.memoryApiUrl || !CONFIG.memoryApiKey) {
|
|
184
184
|
throw new Error("External API not configured for auto-capture");
|
|
185
185
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deduplication-service.d.ts","sourceRoot":"","sources":["../../src/services/deduplication-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"deduplication-service.d.ts","sourceRoot":"","sources":["../../src/services/deduplication-service.ts"],"names":[],"mappings":"AAMA,UAAU,cAAc;IACtB,cAAc,EAAE;QACd,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,UAAU,EAAE,KAAK,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACJ;AAED,UAAU,mBAAmB;IAC3B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,mBAAmB,EAAE,cAAc,EAAE,CAAC;CACvC;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,SAAS,CAAkB;IAE7B,yBAAyB,IAAI,OAAO,CAAC,mBAAmB,CAAC;IA+G/D,OAAO,CAAC,gBAAgB;IAoBxB,SAAS;;;;;CAOV;AAED,eAAO,MAAM,oBAAoB,sBAA6B,CAAC"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { embeddingService } from "./embedding.js";
|
|
2
1
|
import { shardManager } from "./sqlite/shard-manager.js";
|
|
3
2
|
import { vectorSearch } from "./sqlite/vector-search.js";
|
|
4
3
|
import { connectionManager } from "./sqlite/connection-manager.js";
|
|
@@ -34,10 +33,9 @@ export class DeduplicationService {
|
|
|
34
33
|
}
|
|
35
34
|
contentMap.get(key).push(memory);
|
|
36
35
|
}
|
|
37
|
-
for (const [
|
|
36
|
+
for (const [, duplicates] of contentMap) {
|
|
38
37
|
if (duplicates.length > 1) {
|
|
39
38
|
duplicates.sort((a, b) => Number(b.created_at) - Number(a.created_at));
|
|
40
|
-
const keep = duplicates[0];
|
|
41
39
|
const toDelete = duplicates.slice(1);
|
|
42
40
|
for (const dup of toDelete) {
|
|
43
41
|
try {
|
package/dist/services/logger.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { appendFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
2
2
|
import { homedir } from "os";
|
|
3
|
-
import { join
|
|
3
|
+
import { join } from "path";
|
|
4
4
|
const LOG_DIR = join(homedir(), ".opencode-mem");
|
|
5
5
|
const LOG_FILE = join(LOG_DIR, "opencode-mem.log");
|
|
6
6
|
if (!existsSync(LOG_DIR)) {
|
|
@@ -135,7 +135,9 @@ Use the update_user_profile tool to save the ${existingProfile ? "updated" : "ne
|
|
|
135
135
|
type: "function",
|
|
136
136
|
function: {
|
|
137
137
|
name: "update_user_profile",
|
|
138
|
-
description: existingProfile
|
|
138
|
+
description: existingProfile
|
|
139
|
+
? "Update existing user profile with new insights"
|
|
140
|
+
: "Create new user profile",
|
|
139
141
|
parameters: {
|
|
140
142
|
type: "object",
|
|
141
143
|
properties: {
|
|
@@ -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,
|
|
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;AAIrF,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;IA8BT,aAAa,CACX,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,eAAe,EAC5B,yBAAyB,EAAE,MAAM,EACjC,aAAa,EAAE,MAAM,GACpB,IAAI;IA6BP,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;CAyFhG;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { join, dirname } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { handleListTags, handleListMemories, handleAddMemory, handleDeleteMemory, handleBulkDelete, handleUpdateMemory, handleSearch, handleStats, handlePinMemory, handleUnpinMemory, handleRunCleanup, handleRunDeduplication, handleDetectMigration, handleRunMigration, handleDeletePrompt, handleBulkDeletePrompts, } from "./api-handlers.js";
|
|
4
|
+
import { handleListTags, handleListMemories, handleAddMemory, handleDeleteMemory, handleBulkDelete, handleUpdateMemory, handleSearch, handleStats, handlePinMemory, handleUnpinMemory, handleRunCleanup, handleRunDeduplication, handleDetectMigration, handleRunMigration, handleDeletePrompt, handleBulkDeletePrompts, handleGetUserProfile, handleGetProfileChangelog, handleGetProfileSnapshot, handleRefreshProfile, } from "./api-handlers.js";
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = dirname(__filename);
|
|
7
7
|
let server = null;
|
|
@@ -133,6 +133,34 @@ async function handleRequest(req) {
|
|
|
133
133
|
const result = await handleBulkDeletePrompts(body.ids || [], cascade);
|
|
134
134
|
return jsonResponse(result);
|
|
135
135
|
}
|
|
136
|
+
if (path === "/api/user-profile" && method === "GET") {
|
|
137
|
+
const userId = url.searchParams.get("userId") || undefined;
|
|
138
|
+
const result = await handleGetUserProfile(userId);
|
|
139
|
+
return jsonResponse(result);
|
|
140
|
+
}
|
|
141
|
+
if (path === "/api/user-profile/changelog" && method === "GET") {
|
|
142
|
+
const profileId = url.searchParams.get("profileId");
|
|
143
|
+
const limit = parseInt(url.searchParams.get("limit") || "5");
|
|
144
|
+
if (!profileId) {
|
|
145
|
+
return jsonResponse({ success: false, error: "profileId parameter required" });
|
|
146
|
+
}
|
|
147
|
+
const result = await handleGetProfileChangelog(profileId, limit);
|
|
148
|
+
return jsonResponse(result);
|
|
149
|
+
}
|
|
150
|
+
if (path === "/api/user-profile/snapshot" && method === "GET") {
|
|
151
|
+
const changelogId = url.searchParams.get("chlogId");
|
|
152
|
+
if (!changelogId) {
|
|
153
|
+
return jsonResponse({ success: false, error: "changelogId parameter required" });
|
|
154
|
+
}
|
|
155
|
+
const result = await handleGetProfileSnapshot(changelogId);
|
|
156
|
+
return jsonResponse(result);
|
|
157
|
+
}
|
|
158
|
+
if (path === "/api/user-profile/refresh" && method === "POST") {
|
|
159
|
+
const body = (await req.json().catch(() => ({})));
|
|
160
|
+
const userId = body.userId || undefined;
|
|
161
|
+
const result = await handleRefreshProfile(userId);
|
|
162
|
+
return jsonResponse(result);
|
|
163
|
+
}
|
|
136
164
|
return new Response("Not Found", { status: 404 });
|
|
137
165
|
}
|
|
138
166
|
catch (error) {
|