@unclick/mcp-server 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -13
- package/dist/abn-tool.js +1 -1
- package/dist/bgg-tool.js +1 -1
- package/dist/carboninterface-tool.js +1 -1
- package/dist/cards/card.d.ts +9 -0
- package/dist/cards/card.d.ts.map +1 -0
- package/dist/cards/card.js +4 -0
- package/dist/cards/card.js.map +1 -0
- package/dist/cards/search-memory-card.d.ts +11 -0
- package/dist/cards/search-memory-card.d.ts.map +1 -0
- package/dist/cards/search-memory-card.js +75 -0
- package/dist/cards/search-memory-card.js.map +1 -0
- package/dist/cards/search-memory-card.test.d.ts +2 -0
- package/dist/cards/search-memory-card.test.d.ts.map +1 -0
- package/dist/cards/search-memory-card.test.js +59 -0
- package/dist/cards/search-memory-card.test.js.map +1 -0
- package/dist/catalog.js +36 -36
- package/dist/catalog.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +96 -6
- package/dist/client.js.map +1 -1
- package/dist/converter-tools.js +1 -1
- package/dist/crews-tool.d.ts +12 -0
- package/dist/crews-tool.d.ts.map +1 -0
- package/dist/crews-tool.js +125 -0
- package/dist/crews-tool.js.map +1 -0
- package/dist/gdelt-tool.js +4 -4
- package/dist/hackernews-tool.js +1 -1
- package/dist/index.js +0 -0
- package/dist/line-tool.js +1 -1
- package/dist/local-catalog-handlers.js +1 -1
- package/dist/local-catalog-handlers.js.map +1 -1
- package/dist/local-tools.js +7 -7
- package/dist/local-tools.js.map +1 -1
- package/dist/memory/__tests__/bitemporal.test.d.ts +8 -0
- package/dist/memory/__tests__/bitemporal.test.d.ts.map +1 -0
- package/dist/memory/__tests__/bitemporal.test.js +148 -0
- package/dist/memory/__tests__/bitemporal.test.js.map +1 -0
- package/dist/memory/__tests__/hybrid-search.test.d.ts +14 -0
- package/dist/memory/__tests__/hybrid-search.test.d.ts.map +1 -0
- package/dist/memory/__tests__/hybrid-search.test.js +304 -0
- package/dist/memory/__tests__/hybrid-search.test.js.map +1 -0
- package/dist/memory/agent.d.ts +34 -0
- package/dist/memory/agent.d.ts.map +1 -0
- package/dist/memory/agent.js +69 -0
- package/dist/memory/agent.js.map +1 -0
- package/dist/memory/conflicts.d.ts +48 -0
- package/dist/memory/conflicts.d.ts.map +1 -0
- package/dist/memory/conflicts.js +209 -0
- package/dist/memory/conflicts.js.map +1 -0
- package/dist/memory/db.d.ts +18 -3
- package/dist/memory/db.d.ts.map +1 -1
- package/dist/memory/db.js +133 -11
- package/dist/memory/db.js.map +1 -1
- package/dist/memory/device.d.ts +20 -0
- package/dist/memory/device.d.ts.map +1 -0
- package/dist/memory/device.js +48 -0
- package/dist/memory/device.js.map +1 -0
- package/dist/memory/embeddings.d.ts +10 -0
- package/dist/memory/embeddings.d.ts.map +1 -0
- package/dist/memory/embeddings.js +40 -0
- package/dist/memory/embeddings.js.map +1 -0
- package/dist/memory/handlers.d.ts.map +1 -1
- package/dist/memory/handlers.js +98 -4
- package/dist/memory/handlers.js.map +1 -1
- package/dist/memory/instrumentation.d.ts +38 -0
- package/dist/memory/instrumentation.d.ts.map +1 -0
- package/dist/memory/instrumentation.js +97 -0
- package/dist/memory/instrumentation.js.map +1 -0
- package/dist/memory/load-events.d.ts +18 -0
- package/dist/memory/load-events.d.ts.map +1 -0
- package/dist/memory/load-events.js +61 -0
- package/dist/memory/load-events.js.map +1 -0
- package/dist/memory/local.d.ts +4 -1
- package/dist/memory/local.d.ts.map +1 -1
- package/dist/memory/local.js +14 -0
- package/dist/memory/local.js.map +1 -1
- package/dist/memory/session-state.d.ts +37 -0
- package/dist/memory/session-state.d.ts.map +1 -0
- package/dist/memory/session-state.js +82 -0
- package/dist/memory/session-state.js.map +1 -0
- package/dist/memory/supabase.d.ts +75 -5
- package/dist/memory/supabase.d.ts.map +1 -1
- package/dist/memory/supabase.js +584 -83
- package/dist/memory/supabase.js.map +1 -1
- package/dist/memory/tenant-settings.d.ts +33 -0
- package/dist/memory/tenant-settings.d.ts.map +1 -0
- package/dist/memory/tenant-settings.js +79 -0
- package/dist/memory/tenant-settings.js.map +1 -0
- package/dist/memory/tool-awareness.d.ts +66 -0
- package/dist/memory/tool-awareness.d.ts.map +1 -0
- package/dist/memory/tool-awareness.js +307 -0
- package/dist/memory/tool-awareness.js.map +1 -0
- package/dist/memory/types.d.ts +18 -2
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/numbers-tool.js +2 -2
- package/dist/openfoodfacts-tool.js +1 -1
- package/dist/openmeteo-tool.js +1 -1
- package/dist/radiobrowser-tool.js +2 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +759 -55
- package/dist/server.js.map +1 -1
- package/dist/signals/emit.d.ts +11 -0
- package/dist/signals/emit.d.ts.map +1 -0
- package/dist/signals/emit.js +26 -0
- package/dist/signals/emit.js.map +1 -0
- package/dist/testpass-tool.d.ts +12 -0
- package/dist/testpass-tool.d.ts.map +1 -0
- package/dist/testpass-tool.js +121 -0
- package/dist/testpass-tool.js.map +1 -0
- package/dist/tool-wiring.d.ts +320 -4
- package/dist/tool-wiring.d.ts.map +1 -1
- package/dist/tool-wiring.js +246 -5
- package/dist/tool-wiring.js.map +1 -1
- package/dist/trivia-tool.js +5 -5
- package/dist/usgs-tool.js +1 -1
- package/dist/uxpass-tool.d.ts +24 -0
- package/dist/uxpass-tool.d.ts.map +1 -0
- package/dist/uxpass-tool.js +165 -0
- package/dist/uxpass-tool.js.map +1 -0
- package/dist/vault-bridge.js +7 -7
- package/dist/vercel-tool.d.ts +3 -0
- package/dist/vercel-tool.d.ts.map +1 -1
- package/dist/vercel-tool.js +198 -7
- package/dist/vercel-tool.js.map +1 -1
- package/dist/web-tools.d.ts +62 -0
- package/dist/web-tools.d.ts.map +1 -0
- package/dist/web-tools.js +271 -0
- package/dist/web-tools.js.map +1 -0
- package/package.json +6 -3
- package/server.json +1 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conflict detection for UnClick Memory.
|
|
3
|
+
*
|
|
4
|
+
* Competing memory servers (Mem0, Zep, Hindsight, etc) running alongside
|
|
5
|
+
* UnClick cause duplicate facts and mixed-up responses. This module:
|
|
6
|
+
*
|
|
7
|
+
* 1. Exposes KNOWN_CONFLICTS - a static list of competing tools + how to
|
|
8
|
+
* remove them on each major platform
|
|
9
|
+
* 2. detectConflicts(toolNames) - matches a list of tool names against the
|
|
10
|
+
* known patterns and returns the conflict records that matched
|
|
11
|
+
* 3. buildConflictWarning(conflicts) - renders the friendly amber warning
|
|
12
|
+
* that gets prepended to load_memory responses
|
|
13
|
+
* 4. reportConflictDetection(tool) - fire-and-forget logging to the admin
|
|
14
|
+
* API so the admin panel can show conflict history
|
|
15
|
+
*
|
|
16
|
+
* All messaging follows the brand voice: "we noticed" / "we recommend",
|
|
17
|
+
* never "WARNING" or "You must".
|
|
18
|
+
*/
|
|
19
|
+
type RemoveInstructions = Record<string, string>;
|
|
20
|
+
export interface ConflictDef {
|
|
21
|
+
name: string;
|
|
22
|
+
toolPatterns: string[];
|
|
23
|
+
removeInstructions: RemoveInstructions;
|
|
24
|
+
}
|
|
25
|
+
export declare const KNOWN_CONFLICTS: ConflictDef[];
|
|
26
|
+
export declare function detectConflicts(toolNames: readonly string[]): ConflictDef[];
|
|
27
|
+
/**
|
|
28
|
+
* Build the human-readable amber warning shown at the top of a load_memory
|
|
29
|
+
* response when conflicts are detected. Returns null when there are no
|
|
30
|
+
* conflicts.
|
|
31
|
+
*
|
|
32
|
+
* detectionCount lets us escalate tone if the same conflict has been flagged
|
|
33
|
+
* several times already. After 7+ silent detections we stop warning entirely
|
|
34
|
+
* (respect the user's choice).
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildConflictWarning(conflicts: ConflictDef[], detectionCount?: number): string | null;
|
|
37
|
+
interface ConflictReportResult {
|
|
38
|
+
shouldWarn: boolean;
|
|
39
|
+
detectionCount: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Log a conflict detection to the admin API and return whether we should
|
|
43
|
+
* actually surface a warning (throttled to once per 24h per tool per user).
|
|
44
|
+
* Fire-and-forget safe: if the API is unreachable we default to "warn once".
|
|
45
|
+
*/
|
|
46
|
+
export declare function reportConflictDetection(tool: string, platform?: string): Promise<ConflictReportResult>;
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=conflicts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflicts.d.ts","sourceRoot":"","sources":["../../src/memory/conflicts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,KAAK,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kBAAkB,EAAE,kBAAkB,CAAC;CACxC;AAUD,eAAO,MAAM,eAAe,EAAE,WAAW,EA+DxC,CAAC;AAmBF,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,EAAE,CA2B3E;AAYD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,WAAW,EAAE,EACxB,cAAc,SAAI,GACjB,MAAM,GAAG,IAAI,CA2Bf;AAKD,UAAU,oBAAoB;IAC5B,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,oBAAoB,CAAC,CAwB/B"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conflict detection for UnClick Memory.
|
|
3
|
+
*
|
|
4
|
+
* Competing memory servers (Mem0, Zep, Hindsight, etc) running alongside
|
|
5
|
+
* UnClick cause duplicate facts and mixed-up responses. This module:
|
|
6
|
+
*
|
|
7
|
+
* 1. Exposes KNOWN_CONFLICTS - a static list of competing tools + how to
|
|
8
|
+
* remove them on each major platform
|
|
9
|
+
* 2. detectConflicts(toolNames) - matches a list of tool names against the
|
|
10
|
+
* known patterns and returns the conflict records that matched
|
|
11
|
+
* 3. buildConflictWarning(conflicts) - renders the friendly amber warning
|
|
12
|
+
* that gets prepended to load_memory responses
|
|
13
|
+
* 4. reportConflictDetection(tool) - fire-and-forget logging to the admin
|
|
14
|
+
* API so the admin panel can show conflict history
|
|
15
|
+
*
|
|
16
|
+
* All messaging follows the brand voice: "we noticed" / "we recommend",
|
|
17
|
+
* never "WARNING" or "You must".
|
|
18
|
+
*/
|
|
19
|
+
const GENERIC_REMOVE = {
|
|
20
|
+
"claude-code": "claude mcp remove <tool>",
|
|
21
|
+
cursor: "Settings > Tools & MCP > find the entry > remove",
|
|
22
|
+
windsurf: "Remove the entry from ~/.codeium/windsurf/mcp_config.json",
|
|
23
|
+
copilot: "Remove from VS Code settings.json MCP servers section",
|
|
24
|
+
chatgpt: "Settings > Tools > remove the entry",
|
|
25
|
+
};
|
|
26
|
+
export const KNOWN_CONFLICTS = [
|
|
27
|
+
{
|
|
28
|
+
name: "Mem0",
|
|
29
|
+
toolPatterns: ["add-memory", "search-memories", "add_memory", "search_memory_mem0"],
|
|
30
|
+
removeInstructions: {
|
|
31
|
+
"claude-code": "claude mcp remove mem0",
|
|
32
|
+
cursor: "Settings > Tools & MCP > find mem0 > remove",
|
|
33
|
+
windsurf: "Remove the mem0 entry from ~/.codeium/windsurf/mcp_config.json",
|
|
34
|
+
copilot: "Remove the mem0 block from VS Code settings.json",
|
|
35
|
+
chatgpt: "Settings > Tools > remove mem0",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "Zep",
|
|
40
|
+
toolPatterns: ["zep_memory", "graphiti_"],
|
|
41
|
+
removeInstructions: {
|
|
42
|
+
"claude-code": "claude mcp remove zep",
|
|
43
|
+
cursor: "Settings > Tools & MCP > find zep > remove",
|
|
44
|
+
windsurf: "Remove the zep entry from ~/.codeium/windsurf/mcp_config.json",
|
|
45
|
+
copilot: "Remove the zep block from VS Code settings.json",
|
|
46
|
+
chatgpt: "Settings > Tools > remove zep",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "Hindsight",
|
|
51
|
+
toolPatterns: ["hindsight_"],
|
|
52
|
+
removeInstructions: {
|
|
53
|
+
...GENERIC_REMOVE,
|
|
54
|
+
"claude-code": "claude mcp remove hindsight",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "MemPalace",
|
|
59
|
+
toolPatterns: ["mempalace_"],
|
|
60
|
+
removeInstructions: {
|
|
61
|
+
...GENERIC_REMOVE,
|
|
62
|
+
"claude-code": "claude mcp remove mempalace",
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "mcp-memory-service",
|
|
67
|
+
toolPatterns: ["save_memory", "retrieve_memory", "search_memories"],
|
|
68
|
+
removeInstructions: {
|
|
69
|
+
...GENERIC_REMOVE,
|
|
70
|
+
"claude-code": "claude mcp remove memory-service",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "Basic Memory",
|
|
75
|
+
toolPatterns: ["basic_memory_"],
|
|
76
|
+
removeInstructions: {
|
|
77
|
+
...GENERIC_REMOVE,
|
|
78
|
+
"claude-code": "claude mcp remove basic-memory",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "LangMem",
|
|
83
|
+
toolPatterns: ["langmem_"],
|
|
84
|
+
removeInstructions: {
|
|
85
|
+
...GENERIC_REMOVE,
|
|
86
|
+
"claude-code": "claude mcp remove langmem",
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
/**
|
|
91
|
+
* Match a list of tool names from the active MCP session against KNOWN_CONFLICTS.
|
|
92
|
+
* Tool names that don't match any known pattern are ignored silently - non-memory
|
|
93
|
+
* tools like GitHub, Slack, Exa, Playwright etc are safe to run alongside UnClick.
|
|
94
|
+
*
|
|
95
|
+
* UnClick's own tool names (prefixed with unclick_ or memory.) never trigger a
|
|
96
|
+
* match even if they happen to overlap a pattern.
|
|
97
|
+
*/
|
|
98
|
+
const UNCLICK_OWN_TOOLS = new Set([
|
|
99
|
+
"get_startup_context",
|
|
100
|
+
"write_session_summary",
|
|
101
|
+
"add_fact",
|
|
102
|
+
"search_memory",
|
|
103
|
+
"set_business_context",
|
|
104
|
+
"report_bug",
|
|
105
|
+
]);
|
|
106
|
+
export function detectConflicts(toolNames) {
|
|
107
|
+
if (!Array.isArray(toolNames) || toolNames.length === 0)
|
|
108
|
+
return [];
|
|
109
|
+
const names = toolNames
|
|
110
|
+
.map((n) => String(n ?? "").toLowerCase().trim())
|
|
111
|
+
.filter((n) => n.length > 0 &&
|
|
112
|
+
!n.startsWith("unclick_") &&
|
|
113
|
+
!n.startsWith("memory.") &&
|
|
114
|
+
!UNCLICK_OWN_TOOLS.has(n));
|
|
115
|
+
if (names.length === 0)
|
|
116
|
+
return [];
|
|
117
|
+
const matched = [];
|
|
118
|
+
const seen = new Set();
|
|
119
|
+
for (const conflict of KNOWN_CONFLICTS) {
|
|
120
|
+
const patterns = conflict.toolPatterns.map((p) => p.toLowerCase());
|
|
121
|
+
const hit = names.some((name) => patterns.some((p) => (p.endsWith("_") ? name.startsWith(p) : name === p || name.includes(p))));
|
|
122
|
+
if (hit && !seen.has(conflict.name)) {
|
|
123
|
+
matched.push(conflict);
|
|
124
|
+
seen.add(conflict.name);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return matched;
|
|
128
|
+
}
|
|
129
|
+
function formatRemoveInstructions(instr) {
|
|
130
|
+
const lines = [];
|
|
131
|
+
if (instr["claude-code"])
|
|
132
|
+
lines.push(` - Claude Code: ${instr["claude-code"]}`);
|
|
133
|
+
if (instr.cursor)
|
|
134
|
+
lines.push(` - Cursor: ${instr.cursor}`);
|
|
135
|
+
if (instr.windsurf)
|
|
136
|
+
lines.push(` - Windsurf: ${instr.windsurf}`);
|
|
137
|
+
if (instr.copilot)
|
|
138
|
+
lines.push(` - Copilot: ${instr.copilot}`);
|
|
139
|
+
if (instr.chatgpt)
|
|
140
|
+
lines.push(` - ChatGPT: ${instr.chatgpt}`);
|
|
141
|
+
return lines.join("\n");
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Build the human-readable amber warning shown at the top of a load_memory
|
|
145
|
+
* response when conflicts are detected. Returns null when there are no
|
|
146
|
+
* conflicts.
|
|
147
|
+
*
|
|
148
|
+
* detectionCount lets us escalate tone if the same conflict has been flagged
|
|
149
|
+
* several times already. After 7+ silent detections we stop warning entirely
|
|
150
|
+
* (respect the user's choice).
|
|
151
|
+
*/
|
|
152
|
+
export function buildConflictWarning(conflicts, detectionCount = 0) {
|
|
153
|
+
if (conflicts.length === 0)
|
|
154
|
+
return null;
|
|
155
|
+
if (detectionCount >= 7)
|
|
156
|
+
return null;
|
|
157
|
+
const names = conflicts.map((c) => c.name).join(", ");
|
|
158
|
+
const opener = detectionCount >= 3
|
|
159
|
+
? `Heads up: ${names} is still connected alongside UnClick and this is still causing duplicates.`
|
|
160
|
+
: `Heads up: we noticed you also have ${names} connected.`;
|
|
161
|
+
const lines = [
|
|
162
|
+
opener,
|
|
163
|
+
"",
|
|
164
|
+
"Running two memory tools at the same time causes duplicate facts and mixed-up responses.",
|
|
165
|
+
`We recommend turning off ${names} so UnClick can give you the best experience.`,
|
|
166
|
+
"",
|
|
167
|
+
"Your other tools (GitHub, Exa, browser, file system, etc) are fine - they don't overlap with UnClick.",
|
|
168
|
+
"",
|
|
169
|
+
];
|
|
170
|
+
for (const conflict of conflicts) {
|
|
171
|
+
lines.push(`To remove ${conflict.name}:`);
|
|
172
|
+
lines.push(formatRemoveInstructions(conflict.removeInstructions));
|
|
173
|
+
lines.push("");
|
|
174
|
+
}
|
|
175
|
+
return lines.join("\n").trim();
|
|
176
|
+
}
|
|
177
|
+
const CONFLICT_API_BASE = process.env.UNCLICK_MEMORY_BASE_URL || process.env.UNCLICK_SITE_URL || "https://unclick.world";
|
|
178
|
+
/**
|
|
179
|
+
* Log a conflict detection to the admin API and return whether we should
|
|
180
|
+
* actually surface a warning (throttled to once per 24h per tool per user).
|
|
181
|
+
* Fire-and-forget safe: if the API is unreachable we default to "warn once".
|
|
182
|
+
*/
|
|
183
|
+
export async function reportConflictDetection(tool, platform) {
|
|
184
|
+
const apiKey = process.env.UNCLICK_API_KEY;
|
|
185
|
+
if (!apiKey) {
|
|
186
|
+
return { shouldWarn: true, detectionCount: 0 };
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const res = await fetch(`${CONFLICT_API_BASE}/api/memory-admin?action=conflict_detect`, {
|
|
190
|
+
method: "POST",
|
|
191
|
+
headers: {
|
|
192
|
+
"Content-Type": "application/json",
|
|
193
|
+
Authorization: `Bearer ${apiKey}`,
|
|
194
|
+
},
|
|
195
|
+
body: JSON.stringify({ tool, platform }),
|
|
196
|
+
});
|
|
197
|
+
if (!res.ok)
|
|
198
|
+
return { shouldWarn: true, detectionCount: 0 };
|
|
199
|
+
const data = (await res.json());
|
|
200
|
+
return {
|
|
201
|
+
shouldWarn: data.should_warn !== false,
|
|
202
|
+
detectionCount: typeof data.detection_count === "number" ? data.detection_count : 0,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return { shouldWarn: true, detectionCount: 0 };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=conflicts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflicts.js","sourceRoot":"","sources":["../../src/memory/conflicts.ts"],"names":[],"mappings":"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"}
|
package/dist/memory/db.d.ts
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Backend factory for UnClick Memory.
|
|
3
3
|
*
|
|
4
|
-
* Auto-selects the right storage backend:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Auto-selects the right storage backend per request. Precedence:
|
|
5
|
+
*
|
|
6
|
+
* 1. Validated /api/mcp request (UNCLICK_API_KEY + UNCLICK_API_KEY_HASH set):
|
|
7
|
+
* a. memory_configs row exists for this hash -> BYOD (user's own Supabase,
|
|
8
|
+
* single-tenant tables, decrypted via PBKDF2 from api_key)
|
|
9
|
+
* b. no memory_configs row -> managed cloud (central
|
|
10
|
+
* Supabase, mc_* tables, scoped by api_key_hash)
|
|
11
|
+
*
|
|
12
|
+
* 2. Standalone npm package, explicit BYOD via SUPABASE_URL env -> direct
|
|
13
|
+
* Supabase, single-tenant tables.
|
|
14
|
+
*
|
|
15
|
+
* 3. Standalone npm package with UNCLICK_API_KEY only -> remote BYOD lookup,
|
|
16
|
+
* falls through to local if not configured.
|
|
17
|
+
*
|
|
18
|
+
* 4. Local JSON files (zero-config) for the standalone npm package use case.
|
|
19
|
+
*
|
|
20
|
+
* Local JSON files are intentionally not available to /api/mcp serverless,
|
|
21
|
+
* because Vercel /tmp wipes between cold starts.
|
|
7
22
|
*/
|
|
8
23
|
import type { MemoryBackend } from "./types.js";
|
|
9
24
|
export declare function getBackend(): Promise<MemoryBackend>;
|
package/dist/memory/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/memory/db.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/memory/db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAyDhD,wBAAsB,UAAU,IAAI,OAAO,CAAC,aAAa,CAAC,CAmBzD"}
|
package/dist/memory/db.js
CHANGED
|
@@ -1,22 +1,144 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Backend factory for UnClick Memory.
|
|
3
3
|
*
|
|
4
|
-
* Auto-selects the right storage backend:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Auto-selects the right storage backend per request. Precedence:
|
|
5
|
+
*
|
|
6
|
+
* 1. Validated /api/mcp request (UNCLICK_API_KEY + UNCLICK_API_KEY_HASH set):
|
|
7
|
+
* a. memory_configs row exists for this hash -> BYOD (user's own Supabase,
|
|
8
|
+
* single-tenant tables, decrypted via PBKDF2 from api_key)
|
|
9
|
+
* b. no memory_configs row -> managed cloud (central
|
|
10
|
+
* Supabase, mc_* tables, scoped by api_key_hash)
|
|
11
|
+
*
|
|
12
|
+
* 2. Standalone npm package, explicit BYOD via SUPABASE_URL env -> direct
|
|
13
|
+
* Supabase, single-tenant tables.
|
|
14
|
+
*
|
|
15
|
+
* 3. Standalone npm package with UNCLICK_API_KEY only -> remote BYOD lookup,
|
|
16
|
+
* falls through to local if not configured.
|
|
17
|
+
*
|
|
18
|
+
* 4. Local JSON files (zero-config) for the standalone npm package use case.
|
|
19
|
+
*
|
|
20
|
+
* Local JSON files are intentionally not available to /api/mcp serverless,
|
|
21
|
+
* because Vercel /tmp wipes between cold starts.
|
|
7
22
|
*/
|
|
8
|
-
|
|
23
|
+
// Capture the central Supabase env at module load time, before any request
|
|
24
|
+
// flow could mutate it. These point at UnClick's central Supabase project
|
|
25
|
+
// which hosts api_keys + memory_configs + the mc_* managed cloud memory tables.
|
|
26
|
+
const CENTRAL_SUPABASE_URL = process.env.SUPABASE_URL;
|
|
27
|
+
const CENTRAL_SUPABASE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY;
|
|
28
|
+
// The memory admin endpoints live on the main site, not the tool-calling API.
|
|
29
|
+
const MEMORY_API_BASE = process.env.UNCLICK_MEMORY_BASE_URL ||
|
|
30
|
+
process.env.UNCLICK_SITE_URL ||
|
|
31
|
+
"https://unclick.world";
|
|
32
|
+
async function fetchByodConfig(apiKey) {
|
|
33
|
+
try {
|
|
34
|
+
const res = await fetch(`${MEMORY_API_BASE}/api/memory-admin?action=config`, {
|
|
35
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
36
|
+
});
|
|
37
|
+
if (!res.ok)
|
|
38
|
+
return null;
|
|
39
|
+
const data = (await res.json());
|
|
40
|
+
if (!data.configured || !data.supabase_url || !data.service_role_key) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
supabase_url: data.supabase_url,
|
|
45
|
+
service_role_key: data.service_role_key,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Per-tenant backend cache. Keyed so different api_keys never share a backend.
|
|
53
|
+
// In serverless this lives only for the lifetime of a warm instance, which is
|
|
54
|
+
// the right scope. In long-lived standalone use, the cache key is constant.
|
|
55
|
+
//
|
|
56
|
+
// TODO(phase-1+): bound this with an LRU if a single warm instance ever serves
|
|
57
|
+
// thousands of tenants. For Phase 1 the unbounded map is fine.
|
|
58
|
+
const backendCache = new Map();
|
|
59
|
+
function instanceKey() {
|
|
60
|
+
const hash = process.env.UNCLICK_API_KEY_HASH;
|
|
61
|
+
if (hash)
|
|
62
|
+
return `mc:${hash}`;
|
|
63
|
+
if (process.env.SUPABASE_URL)
|
|
64
|
+
return `byod-explicit:${process.env.SUPABASE_URL}`;
|
|
65
|
+
if (process.env.UNCLICK_API_KEY)
|
|
66
|
+
return `byod-remote:${process.env.UNCLICK_API_KEY}`;
|
|
67
|
+
return "local";
|
|
68
|
+
}
|
|
9
69
|
export async function getBackend() {
|
|
10
|
-
|
|
11
|
-
|
|
70
|
+
const key = instanceKey();
|
|
71
|
+
const cached = backendCache.get(key);
|
|
72
|
+
if (cached)
|
|
73
|
+
return cached;
|
|
74
|
+
const backend = await buildBackend();
|
|
75
|
+
backendCache.set(key, backend);
|
|
76
|
+
// Fire-and-forget device heartbeat (never blocks startup). Mode label is
|
|
77
|
+
// a hint for the heartbeat endpoint; it still classifies as cloud or local.
|
|
78
|
+
if (process.env.UNCLICK_API_KEY) {
|
|
79
|
+
const mode = backend.constructor.name === "LocalBackend" ? "local" : "cloud";
|
|
80
|
+
import("./device.js")
|
|
81
|
+
.then(({ sendDeviceHeartbeat }) => sendDeviceHeartbeat(mode))
|
|
82
|
+
.catch(() => { });
|
|
83
|
+
}
|
|
84
|
+
return backend;
|
|
85
|
+
}
|
|
86
|
+
async function buildBackend() {
|
|
87
|
+
const apiKey = process.env.UNCLICK_API_KEY;
|
|
88
|
+
const apiKeyHash = process.env.UNCLICK_API_KEY_HASH;
|
|
89
|
+
// ── 1. Validated /api/mcp request ─────────────────────────────────────
|
|
90
|
+
if (apiKey && apiKeyHash) {
|
|
91
|
+
// 1a. BYOD: user has set up their own Supabase via the wizard.
|
|
92
|
+
const byod = await fetchByodConfig(apiKey);
|
|
93
|
+
if (byod) {
|
|
94
|
+
const { SupabaseBackend } = await import("./supabase.js");
|
|
95
|
+
return new SupabaseBackend({
|
|
96
|
+
url: byod.supabase_url,
|
|
97
|
+
serviceRoleKey: byod.service_role_key,
|
|
98
|
+
tenancy: { mode: "byod" },
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
// 1b. Managed cloud: central Supabase, scoped by api_key_hash.
|
|
102
|
+
if (!CENTRAL_SUPABASE_URL || !CENTRAL_SUPABASE_KEY) {
|
|
103
|
+
throw new Error("Managed cloud memory unavailable: central Supabase env (SUPABASE_URL, " +
|
|
104
|
+
"SUPABASE_SERVICE_ROLE_KEY) not configured on the server.");
|
|
105
|
+
}
|
|
106
|
+
const { SupabaseBackend } = await import("./supabase.js");
|
|
107
|
+
return new SupabaseBackend({
|
|
108
|
+
url: CENTRAL_SUPABASE_URL,
|
|
109
|
+
serviceRoleKey: CENTRAL_SUPABASE_KEY,
|
|
110
|
+
tenancy: { mode: "managed", apiKeyHash },
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// ── 2. Standalone: explicit BYOD via SUPABASE_URL env ─────────────────
|
|
12
114
|
if (process.env.SUPABASE_URL) {
|
|
115
|
+
const url = process.env.SUPABASE_URL;
|
|
116
|
+
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY;
|
|
117
|
+
if (!serviceRoleKey) {
|
|
118
|
+
throw new Error("SUPABASE_URL is set but SUPABASE_SERVICE_ROLE_KEY (or SUPABASE_ANON_KEY) is missing.");
|
|
119
|
+
}
|
|
13
120
|
const { SupabaseBackend } = await import("./supabase.js");
|
|
14
|
-
|
|
121
|
+
return new SupabaseBackend({
|
|
122
|
+
url,
|
|
123
|
+
serviceRoleKey,
|
|
124
|
+
tenancy: { mode: "byod" },
|
|
125
|
+
});
|
|
15
126
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
127
|
+
// ── 3. Standalone: try remote BYOD lookup via api_key ─────────────────
|
|
128
|
+
if (apiKey) {
|
|
129
|
+
const byod = await fetchByodConfig(apiKey);
|
|
130
|
+
if (byod) {
|
|
131
|
+
const { SupabaseBackend } = await import("./supabase.js");
|
|
132
|
+
return new SupabaseBackend({
|
|
133
|
+
url: byod.supabase_url,
|
|
134
|
+
serviceRoleKey: byod.service_role_key,
|
|
135
|
+
tenancy: { mode: "byod" },
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// No remote config; fall through to local.
|
|
19
139
|
}
|
|
20
|
-
|
|
140
|
+
// ── 4. Local JSON files (zero-config standalone) ──────────────────────
|
|
141
|
+
const { LocalBackend } = await import("./local.js");
|
|
142
|
+
return new LocalBackend();
|
|
21
143
|
}
|
|
22
144
|
//# sourceMappingURL=db.js.map
|
package/dist/memory/db.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/memory/db.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/memory/db.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device heartbeat for UnClick Memory.
|
|
3
|
+
*
|
|
4
|
+
* Sends a lightweight fingerprint to the UnClick control plane on startup so
|
|
5
|
+
* the admin dashboard can show where memory is in use, and so the backend can
|
|
6
|
+
* nudge users toward cloud sync when they start using UnClick on a 2nd device.
|
|
7
|
+
*
|
|
8
|
+
* Fingerprint is SHA-256 of (hostname + platform + arch + home-dir). No PII.
|
|
9
|
+
*/
|
|
10
|
+
interface HeartbeatResult {
|
|
11
|
+
nudge: boolean;
|
|
12
|
+
nudge_message: string | null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Fire-and-forget heartbeat. Returns nudge info if the user has multiple
|
|
16
|
+
* devices on local storage so the MCP server can surface a hint.
|
|
17
|
+
*/
|
|
18
|
+
export declare function sendDeviceHeartbeat(storageMode: "local" | "cloud"): Promise<HeartbeatResult | null>;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=device.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/memory/device.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,UAAU,eAAe;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAwBzG"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device heartbeat for UnClick Memory.
|
|
3
|
+
*
|
|
4
|
+
* Sends a lightweight fingerprint to the UnClick control plane on startup so
|
|
5
|
+
* the admin dashboard can show where memory is in use, and so the backend can
|
|
6
|
+
* nudge users toward cloud sync when they start using UnClick on a 2nd device.
|
|
7
|
+
*
|
|
8
|
+
* Fingerprint is SHA-256 of (hostname + platform + arch + home-dir). No PII.
|
|
9
|
+
*/
|
|
10
|
+
import * as os from "os";
|
|
11
|
+
import * as crypto from "crypto";
|
|
12
|
+
const MEMORY_API_BASE = process.env.UNCLICK_MEMORY_BASE_URL || process.env.UNCLICK_SITE_URL || "https://unclick.world";
|
|
13
|
+
function fingerprint() {
|
|
14
|
+
const parts = [os.hostname(), os.platform(), os.arch(), os.homedir()].join("|");
|
|
15
|
+
return crypto.createHash("sha256").update(parts).digest("hex");
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Fire-and-forget heartbeat. Returns nudge info if the user has multiple
|
|
19
|
+
* devices on local storage so the MCP server can surface a hint.
|
|
20
|
+
*/
|
|
21
|
+
export async function sendDeviceHeartbeat(storageMode) {
|
|
22
|
+
const apiKey = process.env.UNCLICK_API_KEY;
|
|
23
|
+
if (!apiKey)
|
|
24
|
+
return null;
|
|
25
|
+
try {
|
|
26
|
+
const res = await fetch(`${MEMORY_API_BASE}/api/memory-admin?action=device_check`, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: {
|
|
29
|
+
"Content-Type": "application/json",
|
|
30
|
+
Authorization: `Bearer ${apiKey}`,
|
|
31
|
+
},
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
device_fingerprint: fingerprint(),
|
|
34
|
+
label: os.hostname(),
|
|
35
|
+
platform: `${os.platform()}-${os.arch()}`,
|
|
36
|
+
storage_mode: storageMode,
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
if (!res.ok)
|
|
40
|
+
return null;
|
|
41
|
+
const data = (await res.json());
|
|
42
|
+
return { nudge: Boolean(data.nudge), nudge_message: data.nudge_message ?? null };
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../../src/memory/device.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,MAAM,eAAe,GACnB,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,uBAAuB,CAAC;AAEjG,SAAS,WAAW;IAClB,MAAM,KAAK,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChF,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACjE,CAAC;AAOD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,WAA8B;IACtE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAC3C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,eAAe,uCAAuC,EAAE;YACjF,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,MAAM,EAAE;aAClC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,kBAAkB,EAAE,WAAW,EAAE;gBACjC,KAAK,EAAE,EAAE,CAAC,QAAQ,EAAE;gBACpB,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;gBACzC,YAAY,EAAE,WAAW;aAC1B,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAoB,CAAC;QACnD,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC;IACnF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin wrapper around OpenAI text-embedding-3-small.
|
|
3
|
+
*
|
|
4
|
+
* Returns null instead of throwing so callers can fall back gracefully
|
|
5
|
+
* to keyword-only search when OpenAI is unavailable or unconfigured.
|
|
6
|
+
*/
|
|
7
|
+
export declare const EMBEDDING_MODEL = "text-embedding-3-small";
|
|
8
|
+
export declare const EMBEDDING_DIMS = 1536;
|
|
9
|
+
export declare function embedText(text: string): Promise<number[] | null>;
|
|
10
|
+
//# sourceMappingURL=embeddings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../../src/memory/embeddings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,eAAe,2BAA2B,CAAC;AACxD,eAAO,MAAM,cAAc,OAAO,CAAC;AAUnC,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CA0BtE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin wrapper around OpenAI text-embedding-3-small.
|
|
3
|
+
*
|
|
4
|
+
* Returns null instead of throwing so callers can fall back gracefully
|
|
5
|
+
* to keyword-only search when OpenAI is unavailable or unconfigured.
|
|
6
|
+
*/
|
|
7
|
+
export const EMBEDDING_MODEL = "text-embedding-3-small";
|
|
8
|
+
export const EMBEDDING_DIMS = 1536;
|
|
9
|
+
// OpenAI hard limit for text-embedding-3-small is 8191 tokens (~32K chars).
|
|
10
|
+
// Slicing at 32000 chars is a safe approximation.
|
|
11
|
+
const MAX_INPUT_CHARS = 32_000;
|
|
12
|
+
export async function embedText(text) {
|
|
13
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
14
|
+
if (!apiKey)
|
|
15
|
+
return null;
|
|
16
|
+
try {
|
|
17
|
+
const res = await fetch("https://api.openai.com/v1/embeddings", {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: {
|
|
20
|
+
Authorization: `Bearer ${apiKey}`,
|
|
21
|
+
"Content-Type": "application/json",
|
|
22
|
+
},
|
|
23
|
+
body: JSON.stringify({
|
|
24
|
+
model: EMBEDDING_MODEL,
|
|
25
|
+
input: text.slice(0, MAX_INPUT_CHARS),
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
console.error(`[embeddings] OpenAI error ${res.status}: ${await res.text()}`);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const data = (await res.json());
|
|
33
|
+
return data.data[0]?.embedding ?? null;
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
console.error("[embeddings] fetch failed:", err);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=embeddings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../../src/memory/embeddings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,wBAAwB,CAAC;AACxD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AAEnC,4EAA4E;AAC5E,kDAAkD;AAClD,MAAM,eAAe,GAAG,MAAM,CAAC;AAM/B,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY;IAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC1C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,sCAAsC,EAAE;YAC9D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,MAAM,EAAE;gBACjC,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,eAAe;gBACtB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC;aACtC,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,OAAO,CAAC,KAAK,CAAC,6BAA6B,GAAG,CAAC,MAAM,KAAK,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC9E,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA4B,CAAC;QAC3D,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,IAAI,CAAC;IACzC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/memory/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/memory/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAgBH,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAkBpC,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,OAAO,CAAC,CA6N5E,CAAC"}
|