@avee1234/memport 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/LICENSE +21 -0
- package/README.md +24 -0
- package/bin/memport.js +377 -0
- package/package.json +44 -0
- package/src/adapters/claude-code.js +317 -0
- package/src/adapters/folder.js +170 -0
- package/src/adapters/index.js +55 -0
- package/src/conformance.js +140 -0
- package/src/export.js +51 -0
- package/src/import.js +119 -0
- package/src/index.js +6 -0
- package/src/validate.js +170 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abhi Das
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# memport
|
|
2
|
+
|
|
3
|
+
[](https://github.com/abhid1234/memport/actions/workflows/ci.yml) [](LICENSE) 
|
|
4
|
+
|
|
5
|
+
**[▶ Play with it live →](https://memport.vercel.app)** · export → import → conformance round-trip on the real library, in your browser.
|
|
6
|
+
|
|
7
|
+
**The open, portable format for AI agent memory.** Export memory from any store, import it into any harness. Zero dependencies.
|
|
8
|
+
|
|
9
|
+
> Working name — the concept was queued as "openmemory," but that collides with Mem0's shipping product, so this is deliberately differentiated. See [`vision.md`](vision.md).
|
|
10
|
+
|
|
11
|
+
Agent memory today is locked in: Mem0 keeps it in its cloud, Zep in a proprietary graph, and every harness in its own private store. There's no vendor-neutral way to move, back up, or audit what an agent remembers. memport is the thin open layer *between* stores — a format, not another backend.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
memport export --from ./mem-store # any store → the neutral .memport format
|
|
15
|
+
memport import --to ./other-store # .memport → any target store
|
|
16
|
+
memport conformance a.memport b.memport # how much memory survived the round-trip?
|
|
17
|
+
memport diff a.memport b.memport # what was added / dropped / changed
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Same playbook as [opentrajectory](https://github.com/abhid1234/opentrajectory) (format for agent traces) and [constraintguard](https://github.com/abhid1234/constraintguard) (format for constraints): own the open interoperability standard, not the storage.
|
|
21
|
+
|
|
22
|
+
Status: **drafting** — see [`roadmap.md`](roadmap.md). Grounded in the memory-portability gap (arXiv:2605.11032).
|
|
23
|
+
|
|
24
|
+
MIT · zero dependencies · harness-neutral
|
package/bin/memport.js
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// memport CLI — a thin subcommand dispatcher. `validate`, `export`, `import`,
|
|
3
|
+
// and `conformance` today; new subcommands slot in without a rewrite.
|
|
4
|
+
//
|
|
5
|
+
// memport validate <file> [--json]
|
|
6
|
+
// memport export --from <dir-or-file> [--to <file>] [--adapter <name>]
|
|
7
|
+
// memport import --from <set-file> --to <target> [--adapter <name>] [--mode merge|replace|fail] [--json]
|
|
8
|
+
// memport conformance <before.memport> <after.memport> [--json] [--min <0..1>]
|
|
9
|
+
//
|
|
10
|
+
// Exit codes:
|
|
11
|
+
// 0 — success (input valid / export written / import written / scored)
|
|
12
|
+
// 1 — input parsed but failed validation (validate, export's produced set,
|
|
13
|
+
// import's input set, conformance's input sets), or `--mode fail` blocked
|
|
14
|
+
// by a non-empty target, or a `--min` gate the `score` fell short of
|
|
15
|
+
// 2 — usage/IO error: no path, unknown subcommand, file not found, invalid JSON
|
|
16
|
+
|
|
17
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
18
|
+
import {
|
|
19
|
+
validateMemoryRecord,
|
|
20
|
+
validateMemorySet,
|
|
21
|
+
exportMemory,
|
|
22
|
+
ExportError,
|
|
23
|
+
importMemory,
|
|
24
|
+
ImportError,
|
|
25
|
+
scoreConformance,
|
|
26
|
+
ConformanceError,
|
|
27
|
+
} from '../src/index.js';
|
|
28
|
+
|
|
29
|
+
const USAGE = [
|
|
30
|
+
'usage: memport validate <file> [--json]',
|
|
31
|
+
' memport export --from <dir-or-file> [--to <file>] [--adapter <name>]',
|
|
32
|
+
' memport import --from <set-file> --to <target> [--adapter <name>] [--mode merge|replace|fail] [--json]',
|
|
33
|
+
' memport conformance <before.memport> <after.memport> [--json] [--min <0..1>]',
|
|
34
|
+
].join('\n');
|
|
35
|
+
|
|
36
|
+
function fail(message, code) {
|
|
37
|
+
process.stderr.write(`${message}\n`);
|
|
38
|
+
process.exit(code);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function runValidate(args) {
|
|
42
|
+
const json = args.includes('--json');
|
|
43
|
+
const paths = args.filter((a) => a !== '--json');
|
|
44
|
+
|
|
45
|
+
if (paths.length === 0) {
|
|
46
|
+
fail(`error: no file given\n${USAGE}`, 2);
|
|
47
|
+
}
|
|
48
|
+
const file = paths[0];
|
|
49
|
+
|
|
50
|
+
let raw;
|
|
51
|
+
try {
|
|
52
|
+
raw = readFileSync(file, 'utf8');
|
|
53
|
+
} catch {
|
|
54
|
+
fail(`error: cannot read file: ${file}`, 2);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let data;
|
|
58
|
+
try {
|
|
59
|
+
data = JSON.parse(raw);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
fail(`error: invalid JSON in ${file}: ${e.message}`, 2);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const isSet = Array.isArray(data);
|
|
65
|
+
const result = isSet ? validateMemorySet(data) : validateMemoryRecord(data);
|
|
66
|
+
|
|
67
|
+
if (json) {
|
|
68
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
69
|
+
process.exit(result.valid ? 0 : 1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (result.valid) {
|
|
73
|
+
const count = isSet ? data.length : 1;
|
|
74
|
+
process.stdout.write(`✓ valid (${count} record${count === 1 ? '' : 's'})\n`);
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
process.stdout.write(`✗ invalid (${result.errors.length} error${result.errors.length === 1 ? '' : 's'})\n`);
|
|
79
|
+
result.errors.forEach((e, i) => {
|
|
80
|
+
const loc = e.index !== undefined ? `[${e.index}]${e.path ? '.' + e.path : ''}` : e.path || '(record)';
|
|
81
|
+
process.stdout.write(` ${i + 1}. ${loc} — ${e.message}\n`);
|
|
82
|
+
});
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Parse `--flag value` options; returns { from, to, adapter } (undefined if
|
|
87
|
+
// absent). Unknown flags and a missing value are usage errors.
|
|
88
|
+
function parseExportArgs(args) {
|
|
89
|
+
const opts = {};
|
|
90
|
+
const known = { '--from': 'from', '--to': 'to', '--adapter': 'adapter' };
|
|
91
|
+
for (let i = 0; i < args.length; i++) {
|
|
92
|
+
const key = known[args[i]];
|
|
93
|
+
if (!key) fail(`error: unknown option: ${args[i]}\n${USAGE}`, 2);
|
|
94
|
+
const value = args[i + 1];
|
|
95
|
+
if (value === undefined || value.startsWith('--')) {
|
|
96
|
+
fail(`error: ${args[i]} requires a value\n${USAGE}`, 2);
|
|
97
|
+
}
|
|
98
|
+
opts[key] = value;
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
return opts;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function runExport(args) {
|
|
105
|
+
const { from, to, adapter } = parseExportArgs(args);
|
|
106
|
+
|
|
107
|
+
if (!from) {
|
|
108
|
+
fail(`error: --from is required\n${USAGE}`, 2);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let set;
|
|
112
|
+
try {
|
|
113
|
+
set = exportMemory(from, adapter ? { adapter } : {});
|
|
114
|
+
} catch (e) {
|
|
115
|
+
if (e instanceof ExportError && e.code === 'invalid_set') {
|
|
116
|
+
// The source produced an invalid set — print the #1 errors, write nothing.
|
|
117
|
+
process.stdout.write(`✗ export produced an invalid memory set (${e.errors.length} error${e.errors.length === 1 ? '' : 's'})\n`);
|
|
118
|
+
e.errors.forEach((err, i) => {
|
|
119
|
+
const loc = err.index !== undefined ? `[${err.index}]${err.path ? '.' + err.path : ''}` : err.path || '(record)';
|
|
120
|
+
process.stdout.write(` ${i + 1}. ${loc} — ${err.message}\n`);
|
|
121
|
+
});
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
// unknown_adapter / source_not_found / invalid_json — usage/IO errors.
|
|
125
|
+
fail(`error: ${e.message}`, 2);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const output = `${JSON.stringify(set, null, 2)}\n`;
|
|
129
|
+
|
|
130
|
+
if (to) {
|
|
131
|
+
try {
|
|
132
|
+
writeFileSync(to, output);
|
|
133
|
+
} catch (e) {
|
|
134
|
+
fail(`error: cannot write file: ${to}: ${e.message}`, 2);
|
|
135
|
+
}
|
|
136
|
+
process.stderr.write(`✓ exported ${set.length} record${set.length === 1 ? '' : 's'} → ${to}\n`);
|
|
137
|
+
} else {
|
|
138
|
+
process.stdout.write(output);
|
|
139
|
+
}
|
|
140
|
+
process.exit(0);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Print the #1 structured errors under a header line (shared by validate,
|
|
144
|
+
// export's invalid-output path, and import's invalid-input path).
|
|
145
|
+
function printErrors(header, errors) {
|
|
146
|
+
process.stdout.write(`${header} (${errors.length} error${errors.length === 1 ? '' : 's'})\n`);
|
|
147
|
+
errors.forEach((e, i) => {
|
|
148
|
+
const loc = e.index !== undefined ? `[${e.index}]${e.path ? '.' + e.path : ''}` : e.path || '(record)';
|
|
149
|
+
process.stdout.write(` ${i + 1}. ${loc} — ${e.message}\n`);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Parse `--flag value` options for import; adds `--mode` and the boolean
|
|
154
|
+
// `--json` on top of export's `--from/--to/--adapter`.
|
|
155
|
+
function parseImportArgs(args) {
|
|
156
|
+
const opts = {};
|
|
157
|
+
const known = { '--from': 'from', '--to': 'to', '--adapter': 'adapter', '--mode': 'mode' };
|
|
158
|
+
for (let i = 0; i < args.length; i++) {
|
|
159
|
+
if (args[i] === '--json') {
|
|
160
|
+
opts.json = true;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const key = known[args[i]];
|
|
164
|
+
if (!key) fail(`error: unknown option: ${args[i]}\n${USAGE}`, 2);
|
|
165
|
+
const value = args[i + 1];
|
|
166
|
+
if (value === undefined || value.startsWith('--')) {
|
|
167
|
+
fail(`error: ${args[i]} requires a value\n${USAGE}`, 2);
|
|
168
|
+
}
|
|
169
|
+
opts[key] = value;
|
|
170
|
+
i++;
|
|
171
|
+
}
|
|
172
|
+
return opts;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function runImport(args) {
|
|
176
|
+
const { from, to, adapter, mode, json } = parseImportArgs(args);
|
|
177
|
+
|
|
178
|
+
if (!from) fail(`error: --from is required\n${USAGE}`, 2);
|
|
179
|
+
if (!to) fail(`error: --to is required\n${USAGE}`, 2);
|
|
180
|
+
|
|
181
|
+
// Load the set from --from. It must parse to a JSON array; a non-array (or
|
|
182
|
+
// unreadable / unparseable) source is a usage error (exit 2). The array's
|
|
183
|
+
// contents are validated inside importMemory.
|
|
184
|
+
let raw;
|
|
185
|
+
try {
|
|
186
|
+
raw = readFileSync(from, 'utf8');
|
|
187
|
+
} catch {
|
|
188
|
+
fail(`error: cannot read file: ${from}`, 2);
|
|
189
|
+
}
|
|
190
|
+
let set;
|
|
191
|
+
try {
|
|
192
|
+
set = JSON.parse(raw);
|
|
193
|
+
} catch (e) {
|
|
194
|
+
fail(`error: invalid JSON in ${from}: ${e.message}`, 2);
|
|
195
|
+
}
|
|
196
|
+
if (!Array.isArray(set)) {
|
|
197
|
+
fail(`error: ${from} must contain a memory set (a JSON array of records)`, 2);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const opts = {};
|
|
201
|
+
if (adapter) opts.adapter = adapter;
|
|
202
|
+
if (mode) opts.mode = mode;
|
|
203
|
+
|
|
204
|
+
let report;
|
|
205
|
+
try {
|
|
206
|
+
report = importMemory(set, to, opts);
|
|
207
|
+
} catch (e) {
|
|
208
|
+
if (e instanceof ImportError && e.code === 'invalid_set') {
|
|
209
|
+
// The input set failed validation — print the #1 errors, write nothing.
|
|
210
|
+
printErrors('✗ import received an invalid memory set', e.errors);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
if (e instanceof ImportError && e.code === 'target_not_empty') {
|
|
214
|
+
// --mode fail blocked by a non-empty target — nothing written.
|
|
215
|
+
fail(`error: ${e.message}`, 1);
|
|
216
|
+
}
|
|
217
|
+
// unknown_adapter / unknown_mode / target_is_directory / write_failed /
|
|
218
|
+
// invalid_json — usage/IO errors.
|
|
219
|
+
if (e instanceof ImportError) fail(`error: ${e.message}`, 2);
|
|
220
|
+
throw e;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (json) {
|
|
224
|
+
process.stdout.write(`${JSON.stringify(report)}\n`);
|
|
225
|
+
process.exit(0);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
process.stderr.write(
|
|
229
|
+
`✓ imported ${report.written} record${report.written === 1 ? '' : 's'} ` +
|
|
230
|
+
`(${report.skipped} skipped, ${report.unsupported.length} unsupported) → ${to}\n`,
|
|
231
|
+
);
|
|
232
|
+
process.exit(0);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Parse `conformance`'s two positional file paths plus the boolean `--json`
|
|
236
|
+
// and the valued `--min`. Positionals (file paths) and `--flag`s can interleave;
|
|
237
|
+
// an unknown option, a missing `--min` value, or the wrong positional count are
|
|
238
|
+
// usage errors caught here or by the caller.
|
|
239
|
+
function parseConformanceArgs(args) {
|
|
240
|
+
const positionals = [];
|
|
241
|
+
const opts = { json: false };
|
|
242
|
+
for (let i = 0; i < args.length; i++) {
|
|
243
|
+
const arg = args[i];
|
|
244
|
+
if (arg === '--json') {
|
|
245
|
+
opts.json = true;
|
|
246
|
+
} else if (arg === '--min') {
|
|
247
|
+
const value = args[i + 1];
|
|
248
|
+
if (value === undefined || value.startsWith('--')) {
|
|
249
|
+
fail(`error: --min requires a value\n${USAGE}`, 2);
|
|
250
|
+
}
|
|
251
|
+
opts.min = value;
|
|
252
|
+
i++;
|
|
253
|
+
} else if (arg.startsWith('--')) {
|
|
254
|
+
fail(`error: unknown option: ${arg}\n${USAGE}`, 2);
|
|
255
|
+
} else {
|
|
256
|
+
positionals.push(arg);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return { positionals, opts };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Load a memport set file for conformance: readable, parseable JSON, and a JSON
|
|
263
|
+
// array. Its records are validated inside `scoreConformance`. Anything else is a
|
|
264
|
+
// usage/IO error (exit 2).
|
|
265
|
+
function loadSetFile(file) {
|
|
266
|
+
let raw;
|
|
267
|
+
try {
|
|
268
|
+
raw = readFileSync(file, 'utf8');
|
|
269
|
+
} catch {
|
|
270
|
+
fail(`error: cannot read file: ${file}`, 2);
|
|
271
|
+
}
|
|
272
|
+
let data;
|
|
273
|
+
try {
|
|
274
|
+
data = JSON.parse(raw);
|
|
275
|
+
} catch (e) {
|
|
276
|
+
fail(`error: invalid JSON in ${file}: ${e.message}`, 2);
|
|
277
|
+
}
|
|
278
|
+
if (!Array.isArray(data)) {
|
|
279
|
+
fail(`error: ${file} must contain a memory set (a JSON array of records)`, 2);
|
|
280
|
+
}
|
|
281
|
+
return data;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function runConformance(args) {
|
|
285
|
+
const { positionals, opts } = parseConformanceArgs(args);
|
|
286
|
+
|
|
287
|
+
if (positionals.length !== 2) {
|
|
288
|
+
fail(`error: conformance takes exactly two files: <before> <after>\n${USAGE}`, 2);
|
|
289
|
+
}
|
|
290
|
+
const [beforeFile, afterFile] = positionals;
|
|
291
|
+
|
|
292
|
+
// Optional `--min` CI gate: a float in [0, 1]. A non-numeric or out-of-range
|
|
293
|
+
// value is a usage error (exit 2), distinct from the gate itself failing.
|
|
294
|
+
let min;
|
|
295
|
+
if (opts.min !== undefined) {
|
|
296
|
+
min = Number(opts.min);
|
|
297
|
+
if (!Number.isFinite(min) || min < 0 || min > 1) {
|
|
298
|
+
fail(`error: --min must be a number in [0, 1], got: ${opts.min}\n${USAGE}`, 2);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const before = loadSetFile(beforeFile);
|
|
303
|
+
const after = loadSetFile(afterFile);
|
|
304
|
+
|
|
305
|
+
let report;
|
|
306
|
+
try {
|
|
307
|
+
report = scoreConformance(before, after);
|
|
308
|
+
} catch (e) {
|
|
309
|
+
if (e instanceof ConformanceError && e.code === 'invalid_set') {
|
|
310
|
+
// An input set failed validation — print the #1 errors naming the side/file.
|
|
311
|
+
const file = e.side === 'before' ? beforeFile : afterFile;
|
|
312
|
+
printErrors(`✗ ${e.side} set is invalid (${file})`, e.errors);
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
if (e instanceof ConformanceError) fail(`error: ${e.message}`, 2);
|
|
316
|
+
throw e;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const gatePassed = min === undefined || report.score >= min;
|
|
320
|
+
|
|
321
|
+
if (opts.json) {
|
|
322
|
+
process.stdout.write(`${JSON.stringify(report)}\n`);
|
|
323
|
+
process.exit(gatePassed ? 0 : 1);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const pct = (report.score * 100).toFixed(1);
|
|
327
|
+
const mark = report.score === 1 || gatePassed ? '✓' : '✗';
|
|
328
|
+
process.stdout.write(
|
|
329
|
+
`${mark} conformance ${pct}% (${report.preserved}/${report.total} preserved, ` +
|
|
330
|
+
`${report.degraded.length} degraded, ${report.lost.length} lost)\n`,
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
if (report.lost.length > 0) {
|
|
334
|
+
process.stdout.write(`lost (${report.lost.length}):\n`);
|
|
335
|
+
for (const r of report.lost) {
|
|
336
|
+
process.stdout.write(` - ${r.id} (${r.kind}) — "${r.content}"\n`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (report.degraded.length > 0) {
|
|
340
|
+
process.stdout.write(`degraded (${report.degraded.length}):\n`);
|
|
341
|
+
for (const r of report.degraded) {
|
|
342
|
+
process.stdout.write(` - ${r.id} — ${r.fields.join(', ')}\n`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (!gatePassed) {
|
|
347
|
+
process.stderr.write(
|
|
348
|
+
`error: conformance ${pct}% is below the --min ${(min * 100).toFixed(1)}% threshold\n`,
|
|
349
|
+
);
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
process.exit(0);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function main() {
|
|
356
|
+
const [subcommand, ...rest] = process.argv.slice(2);
|
|
357
|
+
|
|
358
|
+
switch (subcommand) {
|
|
359
|
+
case 'validate':
|
|
360
|
+
return runValidate(rest);
|
|
361
|
+
case 'export':
|
|
362
|
+
return runExport(rest);
|
|
363
|
+
case 'import':
|
|
364
|
+
return runImport(rest);
|
|
365
|
+
case 'conformance':
|
|
366
|
+
return runConformance(rest);
|
|
367
|
+
default:
|
|
368
|
+
fail(
|
|
369
|
+
subcommand
|
|
370
|
+
? `error: unknown subcommand: ${subcommand}\n${USAGE}`
|
|
371
|
+
: USAGE,
|
|
372
|
+
2,
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@avee1234/memport",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The open, portable format for AI agent memory — export from any store, import into any harness. Zero dependencies.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"memport": "bin/memport.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"src",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "node --test",
|
|
21
|
+
"copy:lib": "node site/sync-lib.mjs"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"ai",
|
|
25
|
+
"agent",
|
|
26
|
+
"memory",
|
|
27
|
+
"portability",
|
|
28
|
+
"interoperability",
|
|
29
|
+
"schema",
|
|
30
|
+
"mem0",
|
|
31
|
+
"open-format",
|
|
32
|
+
"conformance",
|
|
33
|
+
"zero-dependency"
|
|
34
|
+
],
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"homepage": "https://memport.vercel.app",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/abhid1234/memport.git"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/abhid1234/memport/issues"
|
|
43
|
+
}
|
|
44
|
+
}
|