@remnic/import-mem0 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.js +301 -0
- package/dist/index.js.map +1 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @remnic/import-mem0
|
|
2
|
+
|
|
3
|
+
Optional importer for memories stored in a mem0.ai account. Ships as a
|
|
4
|
+
separately installable companion to the Remnic CLI.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install -g @remnic/import-mem0
|
|
8
|
+
export MEM0_API_KEY=...
|
|
9
|
+
remnic import --adapter mem0 --rate-limit 2
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## How it imports
|
|
13
|
+
|
|
14
|
+
- Walks the paginated REST endpoint `/v1/memories/` (follows `next` cursors)
|
|
15
|
+
- Default base URL `https://api.mem0.ai`; override via `MEM0_BASE_URL` for
|
|
16
|
+
self-hosted instances
|
|
17
|
+
- `--rate-limit <rps>` throttles page-to-page requests
|
|
18
|
+
- One memory per mem0 record; blank / soft-deleted records are skipped
|
|
19
|
+
|
|
20
|
+
## Offline replay
|
|
21
|
+
|
|
22
|
+
You can also provide a pre-fetched JSON dump via `--file`:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
remnic import --adapter mem0 --file ./mem0-export.json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The parser accepts both the flat `{ results: [...] }` shape and a multi-page
|
|
29
|
+
recording `{ pages: [...] }` used by the package's record/replay fixtures.
|
|
30
|
+
|
|
31
|
+
## À-la-carte contract
|
|
32
|
+
|
|
33
|
+
This package is declared as an **optional peer dependency** of
|
|
34
|
+
`@remnic/cli`. Installing the CLI without this package produces a
|
|
35
|
+
friendly install hint — never `MODULE_NOT_FOUND`.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { ImporterAdapter, ImportedMemory } from '@remnic/core';
|
|
2
|
+
|
|
3
|
+
interface Mem0Memory {
|
|
4
|
+
/** Stable memory id. */
|
|
5
|
+
id: string;
|
|
6
|
+
/** Memory body. API older responses nest this in `memory`. */
|
|
7
|
+
memory?: string;
|
|
8
|
+
content?: string;
|
|
9
|
+
text?: string;
|
|
10
|
+
user_id?: string;
|
|
11
|
+
agent_id?: string;
|
|
12
|
+
created_at?: string;
|
|
13
|
+
updated_at?: string;
|
|
14
|
+
metadata?: Record<string, unknown>;
|
|
15
|
+
categories?: string[];
|
|
16
|
+
score?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Shape returned by the paginated memories endpoint. The real API uses
|
|
20
|
+
* `results` + `next` (cursor URL) on v1 and `memories` + `page` + `total`
|
|
21
|
+
* on v0; the client accepts either so tests can replay both.
|
|
22
|
+
*/
|
|
23
|
+
interface Mem0ListResponse {
|
|
24
|
+
results?: Mem0Memory[];
|
|
25
|
+
memories?: Mem0Memory[];
|
|
26
|
+
next?: string | null;
|
|
27
|
+
total?: number;
|
|
28
|
+
page?: number;
|
|
29
|
+
per_page?: number;
|
|
30
|
+
}
|
|
31
|
+
interface Mem0ClientOptions {
|
|
32
|
+
apiKey: string;
|
|
33
|
+
/** Default: `https://api.mem0.ai`. Trailing slash tolerated. */
|
|
34
|
+
baseUrl?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Path prefix for the list endpoint. Defaults to `/v1/memories/` for
|
|
37
|
+
* the hosted API. Self-hosted mem0-oss deployments typically expose
|
|
38
|
+
* `/memories/` without the `/v1` prefix — set `MEM0_LIST_PATH` /
|
|
39
|
+
* pass this explicitly in that case. Codex review on PR #602.
|
|
40
|
+
*/
|
|
41
|
+
listPath?: string;
|
|
42
|
+
/** Injected for tests. Falls back to `globalThis.fetch`. */
|
|
43
|
+
fetchImpl?: typeof fetch;
|
|
44
|
+
/** Requests per second limiter. Applied between pages. */
|
|
45
|
+
rateLimit?: number;
|
|
46
|
+
/** Abort signal wired through to fetch. */
|
|
47
|
+
signal?: AbortSignal;
|
|
48
|
+
/** Sleep function for rate limiting; injectable so tests run instantly. */
|
|
49
|
+
sleep?: (ms: number) => Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Fetch all mem0 memories across pagination. Returns a flat array; the
|
|
53
|
+
* caller is responsible for deduplication (the orchestrator does this
|
|
54
|
+
* naturally via content hashing).
|
|
55
|
+
*/
|
|
56
|
+
declare function fetchAllMem0Memories(options: Mem0ClientOptions): Promise<Mem0Memory[]>;
|
|
57
|
+
|
|
58
|
+
interface ParsedMem0Export {
|
|
59
|
+
memories: Mem0Memory[];
|
|
60
|
+
/** Provenance — API endpoint URL or replay fixture path. */
|
|
61
|
+
importedFromPath?: string;
|
|
62
|
+
}
|
|
63
|
+
interface Mem0ParseOptions {
|
|
64
|
+
strict?: boolean;
|
|
65
|
+
filePath?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Parse a mem0 payload. Accepts:
|
|
69
|
+
* - a `Mem0Memory[]` (already fetched)
|
|
70
|
+
* - a JSON string
|
|
71
|
+
* - an object like `{ results: [...] }` or `{ memories: [...] }` or
|
|
72
|
+
* `{ all_pages: [...] }` (combined replay fixture)
|
|
73
|
+
*/
|
|
74
|
+
declare function parseMem0Export(input: unknown, options?: Mem0ParseOptions): ParsedMem0Export;
|
|
75
|
+
/** Extract the memory body, preferring explicit `memory` then `content` then `text`. */
|
|
76
|
+
declare function extractMemoryBody(entry: Mem0Memory): string | undefined;
|
|
77
|
+
|
|
78
|
+
/** Visible for tests. */
|
|
79
|
+
declare function setMem0ClientOptionsForTesting(options: Partial<Mem0ClientOptions> | undefined): void;
|
|
80
|
+
declare const adapter: ImporterAdapter<ParsedMem0Export>;
|
|
81
|
+
/** Alias kept for symmetry with other @remnic/import-* packages. */
|
|
82
|
+
declare const mem0Adapter: ImporterAdapter<ParsedMem0Export>;
|
|
83
|
+
|
|
84
|
+
declare const MEM0_SOURCE_LABEL = "mem0";
|
|
85
|
+
interface Mem0TransformOptions {
|
|
86
|
+
/** Optional cap on total memories emitted — primarily for tests. */
|
|
87
|
+
maxMemories?: number;
|
|
88
|
+
}
|
|
89
|
+
declare function transformMem0Export(parsed: ParsedMem0Export, options?: Mem0TransformOptions): ImportedMemory[];
|
|
90
|
+
|
|
91
|
+
export { MEM0_SOURCE_LABEL, type Mem0ClientOptions, type Mem0ListResponse, type Mem0Memory, type Mem0ParseOptions, type Mem0TransformOptions, type ParsedMem0Export, adapter, extractMemoryBody, fetchAllMem0Memories, mem0Adapter, parseMem0Export, setMem0ClientOptionsForTesting, transformMem0Export };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
// openclaw-engram: Local-first memory plugin
|
|
2
|
+
|
|
3
|
+
// src/adapter.ts
|
|
4
|
+
import { defaultWriteMemoriesToOrchestrator } from "@remnic/core";
|
|
5
|
+
|
|
6
|
+
// src/client.ts
|
|
7
|
+
var DEFAULT_BASE_URL = "https://api.mem0.ai";
|
|
8
|
+
var DEFAULT_LIST_PATH = "/v1/memories/";
|
|
9
|
+
async function fetchAllMem0Memories(options) {
|
|
10
|
+
if (!options.apiKey || typeof options.apiKey !== "string") {
|
|
11
|
+
throw new Error("mem0 import requires a non-empty apiKey");
|
|
12
|
+
}
|
|
13
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
14
|
+
if (typeof fetchImpl !== "function") {
|
|
15
|
+
throw new Error(
|
|
16
|
+
"No fetch implementation available. Provide `fetchImpl` or run on Node 18+."
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
const sleep = options.sleep ?? defaultSleep;
|
|
20
|
+
const base = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
21
|
+
const listPath = normalizeListPath(options.listPath ?? DEFAULT_LIST_PATH);
|
|
22
|
+
const intervalMs = options.rateLimit && options.rateLimit > 0 ? 1e3 / options.rateLimit : 0;
|
|
23
|
+
const all = [];
|
|
24
|
+
const firstUrl = `${base}${listPath}`;
|
|
25
|
+
const allowedOrigin = safeUrlOrigin(firstUrl);
|
|
26
|
+
let nextUrl = firstUrl;
|
|
27
|
+
let pageIndex = 0;
|
|
28
|
+
while (nextUrl) {
|
|
29
|
+
throwIfAborted(options.signal);
|
|
30
|
+
if (pageIndex > 0 && intervalMs > 0) {
|
|
31
|
+
await sleep(intervalMs);
|
|
32
|
+
}
|
|
33
|
+
const response = await fetchImpl(nextUrl, {
|
|
34
|
+
method: "GET",
|
|
35
|
+
headers: {
|
|
36
|
+
Authorization: `Token ${options.apiKey}`,
|
|
37
|
+
Accept: "application/json"
|
|
38
|
+
},
|
|
39
|
+
...options.signal ? { signal: options.signal } : {}
|
|
40
|
+
});
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
const body = await safeText(response);
|
|
43
|
+
throw new Error(
|
|
44
|
+
`mem0 API request to ${nextUrl} failed with ${response.status}: ${body}`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const json = await response.json();
|
|
48
|
+
const page = json.results ?? json.memories ?? [];
|
|
49
|
+
for (const entry of page) {
|
|
50
|
+
if (entry && typeof entry === "object" && typeof entry.id === "string") {
|
|
51
|
+
all.push(entry);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
nextUrl = resolveNextUrl(json, firstUrl, pageIndex, page.length, allowedOrigin);
|
|
55
|
+
pageIndex += 1;
|
|
56
|
+
}
|
|
57
|
+
return all;
|
|
58
|
+
}
|
|
59
|
+
function normalizeListPath(p) {
|
|
60
|
+
const withLeadingSlash = p.startsWith("/") ? p : `/${p}`;
|
|
61
|
+
return withLeadingSlash.endsWith("/") ? withLeadingSlash : `${withLeadingSlash}/`;
|
|
62
|
+
}
|
|
63
|
+
function safeUrlOrigin(url) {
|
|
64
|
+
try {
|
|
65
|
+
return new URL(url).origin;
|
|
66
|
+
} catch {
|
|
67
|
+
return void 0;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function resolveNextUrl(json, firstUrl, currentPageIndex, pageSize, allowedOrigin) {
|
|
71
|
+
if ("next" in json) {
|
|
72
|
+
if (json.next === null) return null;
|
|
73
|
+
if (typeof json.next === "string" && json.next.length > 0) {
|
|
74
|
+
return resolveCursorOrThrow(json.next, firstUrl, allowedOrigin);
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const responsePage = typeof json.page === "number" && Number.isFinite(json.page) ? json.page : currentPageIndex + 1;
|
|
79
|
+
const perPage = typeof json.per_page === "number" && Number.isFinite(json.per_page) ? json.per_page : pageSize;
|
|
80
|
+
const total = typeof json.total === "number" && Number.isFinite(json.total) ? json.total : void 0;
|
|
81
|
+
if (total === void 0 || perPage <= 0) return null;
|
|
82
|
+
const fetchedSoFar = responsePage * perPage;
|
|
83
|
+
if (fetchedSoFar >= total) return null;
|
|
84
|
+
const nextPage = responsePage + 1;
|
|
85
|
+
try {
|
|
86
|
+
const u = new URL(firstUrl);
|
|
87
|
+
u.searchParams.set("page", String(nextPage));
|
|
88
|
+
return u.toString();
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function resolveCursorOrThrow(cursor, firstUrl, allowedOrigin) {
|
|
94
|
+
if (!allowedOrigin) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`mem0 pagination cursor '${cursor}' cannot be followed: configured baseUrl is not an absolute URL.`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
let resolved;
|
|
100
|
+
try {
|
|
101
|
+
resolved = new URL(cursor, firstUrl);
|
|
102
|
+
} catch {
|
|
103
|
+
throw new Error(
|
|
104
|
+
`mem0 pagination cursor '${cursor}' is not a valid URL.`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
if (resolved.origin !== allowedOrigin) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`mem0 pagination cursor '${cursor}' points to origin '${resolved.origin}', but the configured mem0 origin is '${allowedOrigin}'. Refusing to forward the API key to a cross-origin endpoint.`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return resolved.toString();
|
|
113
|
+
}
|
|
114
|
+
function throwIfAborted(signal) {
|
|
115
|
+
if (signal?.aborted) {
|
|
116
|
+
const err = new Error("mem0 import aborted");
|
|
117
|
+
err.name = "AbortError";
|
|
118
|
+
throw err;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function safeText(response) {
|
|
122
|
+
try {
|
|
123
|
+
return (await response.text()).slice(0, 500);
|
|
124
|
+
} catch {
|
|
125
|
+
return "(failed to read response body)";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function defaultSleep(ms) {
|
|
129
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/parser.ts
|
|
133
|
+
function parseMem0Export(input, options = {}) {
|
|
134
|
+
const raw = coerceJson(input);
|
|
135
|
+
const memories = [];
|
|
136
|
+
if (Array.isArray(raw)) {
|
|
137
|
+
appendMemories(memories, raw, options);
|
|
138
|
+
return withFilePath(memories, options.filePath);
|
|
139
|
+
}
|
|
140
|
+
if (raw && typeof raw === "object") {
|
|
141
|
+
const obj = raw;
|
|
142
|
+
for (const key of ["results", "memories", "all_pages"]) {
|
|
143
|
+
const v = obj[key];
|
|
144
|
+
if (Array.isArray(v)) {
|
|
145
|
+
appendMemories(memories, v, options);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const pages = obj.pages;
|
|
149
|
+
if (Array.isArray(pages)) {
|
|
150
|
+
for (const page of pages) {
|
|
151
|
+
if (page && typeof page === "object") {
|
|
152
|
+
const p = page;
|
|
153
|
+
for (const key of ["results", "memories"]) {
|
|
154
|
+
const v = p[key];
|
|
155
|
+
if (Array.isArray(v)) appendMemories(memories, v, options);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return withFilePath(memories, options.filePath);
|
|
161
|
+
}
|
|
162
|
+
if (options.strict) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
"mem0 export must be an array or object; received " + typeof raw
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
return withFilePath(memories, options.filePath);
|
|
168
|
+
}
|
|
169
|
+
function appendMemories(dest, src, options) {
|
|
170
|
+
for (const entry of src) {
|
|
171
|
+
if (!entry || typeof entry !== "object") {
|
|
172
|
+
if (options.strict) throw new Error("mem0 entry must be an object");
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const record = entry;
|
|
176
|
+
if (typeof record.id !== "string" || record.id.length === 0) {
|
|
177
|
+
if (options.strict) throw new Error("mem0 entry missing id");
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
dest.push(record);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function withFilePath(memories, importedFromPath) {
|
|
184
|
+
return {
|
|
185
|
+
memories,
|
|
186
|
+
...importedFromPath !== void 0 ? { importedFromPath } : {}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function coerceJson(input) {
|
|
190
|
+
if (typeof input === "string") {
|
|
191
|
+
try {
|
|
192
|
+
return JSON.parse(input);
|
|
193
|
+
} catch (err) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
`mem0 payload is not valid JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return input;
|
|
200
|
+
}
|
|
201
|
+
function extractMemoryBody(entry) {
|
|
202
|
+
for (const candidate of [entry.memory, entry.content, entry.text]) {
|
|
203
|
+
if (typeof candidate === "string") {
|
|
204
|
+
const trimmed = candidate.trim();
|
|
205
|
+
if (trimmed.length > 0) return trimmed;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return void 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/transform.ts
|
|
212
|
+
var MEM0_SOURCE_LABEL = "mem0";
|
|
213
|
+
function transformMem0Export(parsed, options = {}) {
|
|
214
|
+
const out = [];
|
|
215
|
+
const cap = options.maxMemories;
|
|
216
|
+
for (const entry of parsed.memories) {
|
|
217
|
+
if (cap !== void 0 && out.length >= cap) return out;
|
|
218
|
+
const memory = mem0ToImported(entry, parsed.importedFromPath);
|
|
219
|
+
if (memory) out.push(memory);
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
function mem0ToImported(entry, importedFromPath) {
|
|
224
|
+
const content = extractMemoryBody(entry);
|
|
225
|
+
if (!content) return void 0;
|
|
226
|
+
const sourceTimestamp = entry.updated_at ?? entry.created_at;
|
|
227
|
+
const metadata = { kind: "mem0_memory" };
|
|
228
|
+
if (entry.user_id) metadata.userId = entry.user_id;
|
|
229
|
+
if (entry.agent_id) metadata.agentId = entry.agent_id;
|
|
230
|
+
if (Array.isArray(entry.categories) && entry.categories.length > 0) {
|
|
231
|
+
metadata.categories = [...entry.categories];
|
|
232
|
+
}
|
|
233
|
+
if (entry.metadata && typeof entry.metadata === "object") {
|
|
234
|
+
metadata.sourceMetadata = entry.metadata;
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
content,
|
|
238
|
+
sourceLabel: MEM0_SOURCE_LABEL,
|
|
239
|
+
sourceId: entry.id,
|
|
240
|
+
...sourceTimestamp !== void 0 ? { sourceTimestamp } : {},
|
|
241
|
+
...importedFromPath !== void 0 ? { importedFromPath } : {},
|
|
242
|
+
metadata
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/adapter.ts
|
|
247
|
+
var overrideClientOptionsForTesting;
|
|
248
|
+
function setMem0ClientOptionsForTesting(options) {
|
|
249
|
+
overrideClientOptionsForTesting = options;
|
|
250
|
+
}
|
|
251
|
+
var adapter = {
|
|
252
|
+
name: "mem0",
|
|
253
|
+
sourceLabel: MEM0_SOURCE_LABEL,
|
|
254
|
+
async parse(input, options) {
|
|
255
|
+
if (input !== void 0 && input !== null) {
|
|
256
|
+
return parseMem0Export(input, {
|
|
257
|
+
...options?.strict !== void 0 ? { strict: options.strict } : {},
|
|
258
|
+
...options?.filePath !== void 0 ? { filePath: options.filePath } : {}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
const apiKey = overrideClientOptionsForTesting?.apiKey ?? process.env.MEM0_API_KEY;
|
|
262
|
+
if (!apiKey) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
"mem0 import requires an API key. Set MEM0_API_KEY in your environment or pass a replay fixture via --file <export.json>."
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
const baseUrl = overrideClientOptionsForTesting?.baseUrl ?? process.env.MEM0_BASE_URL;
|
|
268
|
+
const listPath = overrideClientOptionsForTesting?.listPath ?? process.env.MEM0_LIST_PATH;
|
|
269
|
+
const importedFromPath = baseUrl ?? "https://api.mem0.ai";
|
|
270
|
+
const rateLimit = typeof options?.rateLimit === "number" ? options.rateLimit : void 0;
|
|
271
|
+
const memories = await fetchAllMem0Memories({
|
|
272
|
+
apiKey,
|
|
273
|
+
...baseUrl !== void 0 ? { baseUrl } : {},
|
|
274
|
+
...listPath !== void 0 ? { listPath } : {},
|
|
275
|
+
...rateLimit !== void 0 ? { rateLimit } : {},
|
|
276
|
+
...overrideClientOptionsForTesting?.fetchImpl ? { fetchImpl: overrideClientOptionsForTesting.fetchImpl } : {},
|
|
277
|
+
...overrideClientOptionsForTesting?.sleep ? { sleep: overrideClientOptionsForTesting.sleep } : {}
|
|
278
|
+
});
|
|
279
|
+
return { memories, importedFromPath };
|
|
280
|
+
},
|
|
281
|
+
transform(parsed, options) {
|
|
282
|
+
return transformMem0Export(parsed, {
|
|
283
|
+
...options?.maxMemories !== void 0 ? { maxMemories: options.maxMemories } : {}
|
|
284
|
+
});
|
|
285
|
+
},
|
|
286
|
+
async writeTo(target, memories, _options) {
|
|
287
|
+
return defaultWriteMemoriesToOrchestrator(target, memories);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
var mem0Adapter = adapter;
|
|
291
|
+
export {
|
|
292
|
+
MEM0_SOURCE_LABEL,
|
|
293
|
+
adapter,
|
|
294
|
+
extractMemoryBody,
|
|
295
|
+
fetchAllMem0Memories,
|
|
296
|
+
mem0Adapter,
|
|
297
|
+
parseMem0Export,
|
|
298
|
+
setMem0ClientOptionsForTesting,
|
|
299
|
+
transformMem0Export
|
|
300
|
+
};
|
|
301
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapter.ts","../src/client.ts","../src/parser.ts","../src/transform.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// mem0 importer adapter (issue #568 slice 5)\n// ---------------------------------------------------------------------------\n//\n// The mem0 adapter is API-driven rather than file-driven. Two call patterns\n// are supported:\n//\n// 1. CLI: `remnic import --adapter mem0 --rate-limit 2`\n// - No `--file`, so the CLI passes `undefined` as the input.\n// - The adapter reads `MEM0_API_KEY` + optional `MEM0_BASE_URL` from env\n// and walks the paginated API using `fetchAllMem0Memories`.\n//\n// 2. Record/replay tests: callers pass a JSON string (replay fixture) OR\n// a pre-fetched `Mem0Memory[]` directly via parse. In this mode no\n// network I/O happens.\n//\n// Adapters are pure by contract (parse MUST NOT call the orchestrator), so\n// the API fetch lives in `parse`. This mirrors the file-reading importers\n// where parse does the I/O of decoding JSON — here it's HTTP instead.\n\nimport type {\n ImportedMemory,\n ImporterAdapter,\n ImporterParseOptions,\n ImporterTransformOptions,\n ImporterWriteResult,\n ImporterWriteTarget,\n RunImportOptions,\n} from \"@remnic/core\";\nimport { defaultWriteMemoriesToOrchestrator } from \"@remnic/core\";\n\nimport { fetchAllMem0Memories, type Mem0ClientOptions } from \"./client.js\";\nimport { parseMem0Export, type ParsedMem0Export } from \"./parser.js\";\nimport { MEM0_SOURCE_LABEL, transformMem0Export } from \"./transform.js\";\n\n/**\n * Test-only backdoor used by `adapter.test.ts` to inject a fake fetch without\n * exposing the ImporterAdapter surface to client-options plumbing at runtime.\n * Cleared after each test.\n */\nlet overrideClientOptionsForTesting:\n | Partial<Mem0ClientOptions>\n | undefined;\n\n/** Visible for tests. */\nexport function setMem0ClientOptionsForTesting(\n options: Partial<Mem0ClientOptions> | undefined,\n): void {\n overrideClientOptionsForTesting = options;\n}\n\nexport const adapter: ImporterAdapter<ParsedMem0Export> = {\n name: \"mem0\",\n sourceLabel: MEM0_SOURCE_LABEL,\n\n async parse(\n input: unknown,\n options?: ImporterParseOptions,\n ): Promise<ParsedMem0Export> {\n // Replay / in-memory path: caller supplied JSON or an array already.\n if (input !== undefined && input !== null) {\n return parseMem0Export(input, {\n ...(options?.strict !== undefined ? { strict: options.strict } : {}),\n ...(options?.filePath !== undefined\n ? { filePath: options.filePath }\n : {}),\n });\n }\n\n // Live path: pull from the API.\n const apiKey = overrideClientOptionsForTesting?.apiKey ?? process.env.MEM0_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"mem0 import requires an API key. Set MEM0_API_KEY in your environment \" +\n \"or pass a replay fixture via --file <export.json>.\",\n );\n }\n const baseUrl =\n overrideClientOptionsForTesting?.baseUrl ?? process.env.MEM0_BASE_URL;\n // Self-hosted mem0-oss exposes `/memories/` without the `/v1` prefix.\n // Let operators override via MEM0_LIST_PATH so those deployments work\n // without patching. Codex review on PR #602.\n const listPath =\n overrideClientOptionsForTesting?.listPath ?? process.env.MEM0_LIST_PATH;\n const importedFromPath = baseUrl ?? \"https://api.mem0.ai\";\n // Forward the validated CLI `--rate-limit` (now carried through\n // ImporterParseOptions.rateLimit by runImporter) into the fetch client\n // so `remnic import --adapter mem0 --rate-limit 2` actually throttles.\n // Cursor review on PR #602 — the original wiring silently ignored it.\n const rateLimit =\n typeof options?.rateLimit === \"number\" ? options.rateLimit : undefined;\n const memories = await fetchAllMem0Memories({\n apiKey,\n ...(baseUrl !== undefined ? { baseUrl } : {}),\n ...(listPath !== undefined ? { listPath } : {}),\n ...(rateLimit !== undefined ? { rateLimit } : {}),\n ...(overrideClientOptionsForTesting?.fetchImpl\n ? { fetchImpl: overrideClientOptionsForTesting.fetchImpl }\n : {}),\n ...(overrideClientOptionsForTesting?.sleep\n ? { sleep: overrideClientOptionsForTesting.sleep }\n : {}),\n });\n return { memories, importedFromPath };\n },\n\n transform(\n parsed: ParsedMem0Export,\n options?: ImporterTransformOptions,\n ): ImportedMemory[] {\n return transformMem0Export(parsed, {\n ...(options?.maxMemories !== undefined\n ? { maxMemories: options.maxMemories }\n : {}),\n });\n },\n\n async writeTo(\n target: ImporterWriteTarget,\n memories: ImportedMemory[],\n _options: RunImportOptions,\n ): Promise<ImporterWriteResult> {\n return defaultWriteMemoriesToOrchestrator(target, memories);\n },\n};\n\n/** Alias kept for symmetry with other @remnic/import-* packages. */\nexport const mem0Adapter = adapter;\n","// ---------------------------------------------------------------------------\n// Mem0 REST client (issue #568 slice 5)\n// ---------------------------------------------------------------------------\n//\n// mem0.ai exposes a paginated memories list endpoint. A production user will\n// typically hit the hosted service at `https://api.mem0.ai/v1/memories/`,\n// supply a Bearer API key, and pull down their account's memories page by\n// page. Some users self-host and need a configurable base URL.\n//\n// This client is intentionally tiny:\n// - fetch-based; no SDK dependency.\n// - Injectable `fetch` impl so tests can replay a record/replay fixture.\n// - Abort-signal aware for clean cancellation.\n// - Rate-limit aware (sleeps between page requests when `rateLimit` is set\n// on `RunImportOptions`).\n//\n// The adapter calls `fetchAllMem0Memories()` once; it walks pagination and\n// returns a flat array. The transform layer then maps each raw record to an\n// `ImportedMemory`.\n\nexport interface Mem0Memory {\n /** Stable memory id. */\n id: string;\n /** Memory body. API older responses nest this in `memory`. */\n memory?: string;\n content?: string;\n text?: string;\n user_id?: string;\n agent_id?: string;\n created_at?: string;\n updated_at?: string;\n metadata?: Record<string, unknown>;\n categories?: string[];\n score?: number;\n}\n\n/**\n * Shape returned by the paginated memories endpoint. The real API uses\n * `results` + `next` (cursor URL) on v1 and `memories` + `page` + `total`\n * on v0; the client accepts either so tests can replay both.\n */\nexport interface Mem0ListResponse {\n results?: Mem0Memory[];\n memories?: Mem0Memory[];\n next?: string | null;\n total?: number;\n page?: number;\n per_page?: number;\n}\n\nexport interface Mem0ClientOptions {\n apiKey: string;\n /** Default: `https://api.mem0.ai`. Trailing slash tolerated. */\n baseUrl?: string;\n /**\n * Path prefix for the list endpoint. Defaults to `/v1/memories/` for\n * the hosted API. Self-hosted mem0-oss deployments typically expose\n * `/memories/` without the `/v1` prefix — set `MEM0_LIST_PATH` /\n * pass this explicitly in that case. Codex review on PR #602.\n */\n listPath?: string;\n /** Injected for tests. Falls back to `globalThis.fetch`. */\n fetchImpl?: typeof fetch;\n /** Requests per second limiter. Applied between pages. */\n rateLimit?: number;\n /** Abort signal wired through to fetch. */\n signal?: AbortSignal;\n /** Sleep function for rate limiting; injectable so tests run instantly. */\n sleep?: (ms: number) => Promise<void>;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.mem0.ai\";\nconst DEFAULT_LIST_PATH = \"/v1/memories/\";\n\n/**\n * Fetch all mem0 memories across pagination. Returns a flat array; the\n * caller is responsible for deduplication (the orchestrator does this\n * naturally via content hashing).\n */\nexport async function fetchAllMem0Memories(\n options: Mem0ClientOptions,\n): Promise<Mem0Memory[]> {\n if (!options.apiKey || typeof options.apiKey !== \"string\") {\n throw new Error(\"mem0 import requires a non-empty apiKey\");\n }\n const fetchImpl = options.fetchImpl ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new Error(\n \"No fetch implementation available. Provide `fetchImpl` or run on Node 18+.\",\n );\n }\n const sleep = options.sleep ?? defaultSleep;\n const base = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n const listPath = normalizeListPath(options.listPath ?? DEFAULT_LIST_PATH);\n const intervalMs =\n options.rateLimit && options.rateLimit > 0 ? 1000 / options.rateLimit : 0;\n\n const all: Mem0Memory[] = [];\n const firstUrl = `${base}${listPath}`;\n // Capture the allow-listed origin ONCE so cross-origin cursors can't\n // tunnel the API key to an attacker-controlled host. Codex review on\n // PR #602. We also validate that the configured base URL itself is a\n // parseable absolute URL — mem0 servers that return relative cursors\n // are then resolved against this origin.\n const allowedOrigin = safeUrlOrigin(firstUrl);\n let nextUrl: string | null = firstUrl;\n let pageIndex = 0;\n while (nextUrl) {\n throwIfAborted(options.signal);\n if (pageIndex > 0 && intervalMs > 0) {\n await sleep(intervalMs);\n }\n const response = await fetchImpl(nextUrl, {\n method: \"GET\",\n headers: {\n Authorization: `Token ${options.apiKey}`,\n Accept: \"application/json\",\n },\n ...(options.signal ? { signal: options.signal } : {}),\n });\n if (!response.ok) {\n const body = await safeText(response);\n throw new Error(\n `mem0 API request to ${nextUrl} failed with ${response.status}: ${body}`,\n );\n }\n const json = (await response.json()) as Mem0ListResponse;\n const page = json.results ?? json.memories ?? [];\n for (const entry of page) {\n if (entry && typeof entry === \"object\" && typeof entry.id === \"string\") {\n all.push(entry);\n }\n }\n nextUrl = resolveNextUrl(json, firstUrl, pageIndex, page.length, allowedOrigin);\n pageIndex += 1;\n }\n return all;\n}\n\nfunction normalizeListPath(p: string): string {\n const withLeadingSlash = p.startsWith(\"/\") ? p : `/${p}`;\n return withLeadingSlash.endsWith(\"/\") ? withLeadingSlash : `${withLeadingSlash}/`;\n}\n\nfunction safeUrlOrigin(url: string): string | undefined {\n try {\n return new URL(url).origin;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Decide the next URL to request.\n *\n * Preferred: the `next` cursor the server returns (v1 shape). When `next`\n * is a **relative** path, it's resolved against the configured base URL.\n * When absolute, it MUST match the allow-listed origin — cross-origin\n * cursors are rejected so an upstream/proxy can't exfiltrate the API key\n * by redirecting the pagination walk. Codex review on PR #602.\n *\n * Fallback: when `next` is explicitly `null` (server says \"no more pages\"),\n * stop. When `next` is absent (field omitted) AND the response exposes\n * numeric pagination fields (`page` / `total` / `per_page`, the older v0\n * shape), synthesize a `?page=<N>` request for the next page. Cursor\n * review on PR #602 flagged that servers returning only numeric\n * pagination were silently truncated under the original code, AND that\n * the implementation conflated `null` with `undefined`.\n */\nfunction resolveNextUrl(\n json: Mem0ListResponse,\n firstUrl: string,\n currentPageIndex: number,\n pageSize: number,\n allowedOrigin: string | undefined,\n): string | null {\n // Has the server explicitly returned a next value?\n if (\"next\" in json) {\n if (json.next === null) return null; // authoritative stop\n if (typeof json.next === \"string\" && json.next.length > 0) {\n return resolveCursorOrThrow(json.next, firstUrl, allowedOrigin);\n }\n // `next` was present but not a non-empty string or null (e.g. number).\n // Don't fall through to numeric pagination — the server sent a signal\n // we can't interpret. Treat as end-of-stream.\n return null;\n }\n\n // Numeric-pagination fallback. `page` is 1-based in the real API.\n const responsePage = typeof json.page === \"number\" && Number.isFinite(json.page)\n ? json.page\n : currentPageIndex + 1;\n const perPage = typeof json.per_page === \"number\" && Number.isFinite(json.per_page)\n ? json.per_page\n : pageSize;\n const total = typeof json.total === \"number\" && Number.isFinite(json.total)\n ? json.total\n : undefined;\n if (total === undefined || perPage <= 0) return null;\n const fetchedSoFar = responsePage * perPage;\n if (fetchedSoFar >= total) return null;\n const nextPage = responsePage + 1;\n try {\n const u = new URL(firstUrl);\n u.searchParams.set(\"page\", String(nextPage));\n return u.toString();\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve a server-provided cursor against the base URL and enforce\n * same-origin. Relative cursors are accepted (resolved against firstUrl);\n * absolute cursors must match `allowedOrigin`. Throws on cross-origin to\n * surface the security-relevant mismatch immediately instead of silently\n * leaking the API key.\n */\nfunction resolveCursorOrThrow(\n cursor: string,\n firstUrl: string,\n allowedOrigin: string | undefined,\n): string {\n if (!allowedOrigin) {\n // Base URL wasn't parseable as an absolute URL; refuse to follow any\n // cursor because we can't compare origins.\n throw new Error(\n `mem0 pagination cursor '${cursor}' cannot be followed: configured baseUrl is not an absolute URL.`,\n );\n }\n let resolved: URL;\n try {\n resolved = new URL(cursor, firstUrl);\n } catch {\n throw new Error(\n `mem0 pagination cursor '${cursor}' is not a valid URL.`,\n );\n }\n if (resolved.origin !== allowedOrigin) {\n throw new Error(\n `mem0 pagination cursor '${cursor}' points to origin '${resolved.origin}', ` +\n `but the configured mem0 origin is '${allowedOrigin}'. ` +\n \"Refusing to forward the API key to a cross-origin endpoint.\",\n );\n }\n return resolved.toString();\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) {\n const err = new Error(\"mem0 import aborted\");\n (err as Error & { name: string }).name = \"AbortError\";\n throw err;\n }\n}\n\nasync function safeText(response: Response): Promise<string> {\n try {\n return (await response.text()).slice(0, 500);\n } catch {\n return \"(failed to read response body)\";\n }\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","// ---------------------------------------------------------------------------\n// mem0 parser (issue #568 slice 5)\n// ---------------------------------------------------------------------------\n//\n// Unlike the file-based importers, mem0 pulls data directly from an API. The\n// \"parse\" step therefore either:\n//\n// 1. Accepts an already-fetched `Mem0Memory[]` (the adapter's primary path,\n// after the API client pulls everything down).\n// 2. Accepts a JSON string / parsed object for record/replay fixtures so\n// tests and offline flows don't need network access.\n//\n// Non-object entries are dropped in non-strict mode (CLAUDE.md rule 51\n// applies only to user-facing CLI inputs; parser leniency here protects\n// against server-side schema drift without crashing the import).\n\nimport type { Mem0Memory } from \"./client.js\";\n\nexport interface ParsedMem0Export {\n memories: Mem0Memory[];\n /** Provenance — API endpoint URL or replay fixture path. */\n importedFromPath?: string;\n}\n\nexport interface Mem0ParseOptions {\n strict?: boolean;\n filePath?: string;\n}\n\n/**\n * Parse a mem0 payload. Accepts:\n * - a `Mem0Memory[]` (already fetched)\n * - a JSON string\n * - an object like `{ results: [...] }` or `{ memories: [...] }` or\n * `{ all_pages: [...] }` (combined replay fixture)\n */\nexport function parseMem0Export(\n input: unknown,\n options: Mem0ParseOptions = {},\n): ParsedMem0Export {\n const raw = coerceJson(input);\n const memories: Mem0Memory[] = [];\n\n if (Array.isArray(raw)) {\n appendMemories(memories, raw, options);\n return withFilePath(memories, options.filePath);\n }\n\n if (raw && typeof raw === \"object\") {\n const obj = raw as Record<string, unknown>;\n for (const key of [\"results\", \"memories\", \"all_pages\"] as const) {\n const v = obj[key];\n if (Array.isArray(v)) {\n appendMemories(memories, v, options);\n }\n }\n // `pages`: an array of page responses (replay fixture for multi-page\n // pulls). We flatten each page's `results` / `memories`.\n const pages = obj.pages;\n if (Array.isArray(pages)) {\n for (const page of pages) {\n if (page && typeof page === \"object\") {\n const p = page as Record<string, unknown>;\n for (const key of [\"results\", \"memories\"] as const) {\n const v = p[key];\n if (Array.isArray(v)) appendMemories(memories, v, options);\n }\n }\n }\n }\n return withFilePath(memories, options.filePath);\n }\n\n if (options.strict) {\n throw new Error(\n \"mem0 export must be an array or object; received \" + typeof raw,\n );\n }\n return withFilePath(memories, options.filePath);\n}\n\nfunction appendMemories(\n dest: Mem0Memory[],\n src: unknown[],\n options: Mem0ParseOptions,\n): void {\n for (const entry of src) {\n if (!entry || typeof entry !== \"object\") {\n if (options.strict) throw new Error(\"mem0 entry must be an object\");\n continue;\n }\n const record = entry as Mem0Memory;\n if (typeof record.id !== \"string\" || record.id.length === 0) {\n if (options.strict) throw new Error(\"mem0 entry missing id\");\n continue;\n }\n dest.push(record);\n }\n}\n\nfunction withFilePath(\n memories: Mem0Memory[],\n importedFromPath: string | undefined,\n): ParsedMem0Export {\n return {\n memories,\n ...(importedFromPath !== undefined ? { importedFromPath } : {}),\n };\n}\n\nfunction coerceJson(input: unknown): unknown {\n if (typeof input === \"string\") {\n try {\n return JSON.parse(input);\n } catch (err) {\n throw new Error(\n `mem0 payload is not valid JSON: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n return input;\n}\n\n/** Extract the memory body, preferring explicit `memory` then `content` then `text`. */\nexport function extractMemoryBody(entry: Mem0Memory): string | undefined {\n for (const candidate of [entry.memory, entry.content, entry.text]) {\n if (typeof candidate === \"string\") {\n const trimmed = candidate.trim();\n if (trimmed.length > 0) return trimmed;\n }\n }\n return undefined;\n}\n","// ---------------------------------------------------------------------------\n// mem0 parsed → ImportedMemory transform (issue #568 slice 5)\n// ---------------------------------------------------------------------------\n\nimport type { ImportedMemory } from \"@remnic/core\";\n\nimport type { Mem0Memory } from \"./client.js\";\nimport type { ParsedMem0Export } from \"./parser.js\";\nimport { extractMemoryBody } from \"./parser.js\";\n\nexport const MEM0_SOURCE_LABEL = \"mem0\";\n\nexport interface Mem0TransformOptions {\n /** Optional cap on total memories emitted — primarily for tests. */\n maxMemories?: number;\n}\n\nexport function transformMem0Export(\n parsed: ParsedMem0Export,\n options: Mem0TransformOptions = {},\n): ImportedMemory[] {\n const out: ImportedMemory[] = [];\n const cap = options.maxMemories;\n for (const entry of parsed.memories) {\n if (cap !== undefined && out.length >= cap) return out;\n const memory = mem0ToImported(entry, parsed.importedFromPath);\n if (memory) out.push(memory);\n }\n return out;\n}\n\nfunction mem0ToImported(\n entry: Mem0Memory,\n importedFromPath: string | undefined,\n): ImportedMemory | undefined {\n const content = extractMemoryBody(entry);\n if (!content) return undefined;\n const sourceTimestamp = entry.updated_at ?? entry.created_at;\n const metadata: Record<string, unknown> = { kind: \"mem0_memory\" };\n if (entry.user_id) metadata.userId = entry.user_id;\n if (entry.agent_id) metadata.agentId = entry.agent_id;\n if (Array.isArray(entry.categories) && entry.categories.length > 0) {\n metadata.categories = [...entry.categories];\n }\n if (entry.metadata && typeof entry.metadata === \"object\") {\n metadata.sourceMetadata = entry.metadata;\n }\n return {\n content,\n sourceLabel: MEM0_SOURCE_LABEL,\n sourceId: entry.id,\n ...(sourceTimestamp !== undefined ? { sourceTimestamp } : {}),\n ...(importedFromPath !== undefined ? { importedFromPath } : {}),\n metadata,\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@remnic/import-mem0",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Import memories from the mem0.ai REST API into Remnic (issue #568)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": ["dist"],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
17
|
+
"check-types": "tsc --noEmit",
|
|
18
|
+
"test": "tsx --test src/adapter.test.ts src/parser.test.ts src/transform.test.ts src/client.test.ts",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public",
|
|
23
|
+
"provenance": true
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@remnic/core": "workspace:^"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@remnic/core": "workspace:*",
|
|
30
|
+
"tsup": "^8.0.0",
|
|
31
|
+
"tsx": "^4.0.0",
|
|
32
|
+
"typescript": "^5.7.0"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/joshuaswarren/remnic.git",
|
|
38
|
+
"directory": "packages/import-mem0"
|
|
39
|
+
},
|
|
40
|
+
"keywords": ["remnic", "memory", "mem0", "import"]
|
|
41
|
+
}
|