@mem0/cli 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 +75 -0
- package/development.md +91 -0
- package/dist/chunk-EJ5AQPMT.js +120 -0
- package/dist/chunk-I7ABQZUR.js +111 -0
- package/dist/chunk-J7DYZDMM.js +187 -0
- package/dist/chunk-O3XZVUUX.js +252 -0
- package/dist/config-WKOCXNAS.js +86 -0
- package/dist/entities-XPRXH4X4.js +119 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +523 -0
- package/dist/init-N25QFHYP.js +161 -0
- package/dist/memory-JYJGE4VO.js +387 -0
- package/dist/utils-BAMFZ5H5.js +124 -0
- package/package.json +42 -0
- package/src/backend/base.ts +115 -0
- package/src/backend/index.ts +7 -0
- package/src/backend/platform.ts +303 -0
- package/src/branding.ts +145 -0
- package/src/commands/config.ts +90 -0
- package/src/commands/entities.ts +139 -0
- package/src/commands/init.ts +182 -0
- package/src/commands/memory.ts +487 -0
- package/src/commands/utils.ts +139 -0
- package/src/config.ts +159 -0
- package/src/help.ts +374 -0
- package/src/index.ts +501 -0
- package/src/output.ts +230 -0
- package/tests/branding.test.ts +98 -0
- package/tests/cli-integration.test.ts +156 -0
- package/tests/commands.test.ts +221 -0
- package/tests/config.test.ts +113 -0
- package/tests/output.test.ts +115 -0
- package/tests/setup.ts +75 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for configuration management.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import {
|
|
10
|
+
createDefaultConfig,
|
|
11
|
+
loadConfig,
|
|
12
|
+
saveConfig,
|
|
13
|
+
redactKey,
|
|
14
|
+
getNestedValue,
|
|
15
|
+
setNestedValue,
|
|
16
|
+
CONFIG_DIR,
|
|
17
|
+
CONFIG_FILE,
|
|
18
|
+
} from "../src/config.js";
|
|
19
|
+
|
|
20
|
+
// Use a temp directory for config during tests
|
|
21
|
+
let origConfigDir: string;
|
|
22
|
+
let origConfigFile: string;
|
|
23
|
+
let tmpDir: string;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "mem0-test-"));
|
|
27
|
+
// Monkey-patch the module-level constants
|
|
28
|
+
// We'll use env vars and direct file manipulation instead
|
|
29
|
+
// Clear MEM0_ env vars
|
|
30
|
+
for (const key of Object.keys(process.env)) {
|
|
31
|
+
if (key.startsWith("MEM0_")) {
|
|
32
|
+
delete process.env[key];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("redactKey", () => {
|
|
42
|
+
it("returns '(not set)' for empty key", () => {
|
|
43
|
+
expect(redactKey("")).toBe("(not set)");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("redacts short key", () => {
|
|
47
|
+
expect(redactKey("abc")).toBe("ab***");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("redacts normal key", () => {
|
|
51
|
+
const result = redactKey("m0-abcdefgh12345678");
|
|
52
|
+
expect(result).toBe("m0-a...5678");
|
|
53
|
+
expect(result).not.toContain("abcdefgh");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("redacts exactly 8-char key as short", () => {
|
|
57
|
+
expect(redactKey("12345678")).toBe("12***");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("createDefaultConfig", () => {
|
|
62
|
+
it("has correct defaults", () => {
|
|
63
|
+
const config = createDefaultConfig();
|
|
64
|
+
expect(config.platform.baseUrl).toBe("https://api.mem0.ai");
|
|
65
|
+
expect(config.platform.apiKey).toBe("");
|
|
66
|
+
expect(config.defaults.userId).toBe("");
|
|
67
|
+
expect(config.defaults.enableGraph).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe("getNestedValue", () => {
|
|
72
|
+
it("gets platform.api_key", () => {
|
|
73
|
+
const config = createDefaultConfig();
|
|
74
|
+
config.platform.apiKey = "test-key";
|
|
75
|
+
expect(getNestedValue(config, "platform.api_key")).toBe("test-key");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("returns undefined for nonexistent key", () => {
|
|
79
|
+
const config = createDefaultConfig();
|
|
80
|
+
expect(getNestedValue(config, "nonexistent.key")).toBeUndefined();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("gets defaults.user_id", () => {
|
|
84
|
+
const config = createDefaultConfig();
|
|
85
|
+
config.defaults.userId = "alice";
|
|
86
|
+
expect(getNestedValue(config, "defaults.user_id")).toBe("alice");
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("setNestedValue", () => {
|
|
91
|
+
it("sets platform.api_key", () => {
|
|
92
|
+
const config = createDefaultConfig();
|
|
93
|
+
expect(setNestedValue(config, "platform.api_key", "new-key")).toBe(true);
|
|
94
|
+
expect(config.platform.apiKey).toBe("new-key");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("returns false for nonexistent key", () => {
|
|
98
|
+
const config = createDefaultConfig();
|
|
99
|
+
expect(setNestedValue(config, "nonexistent.key", "val")).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("sets defaults.user_id", () => {
|
|
103
|
+
const config = createDefaultConfig();
|
|
104
|
+
expect(setNestedValue(config, "defaults.user_id", "bob")).toBe(true);
|
|
105
|
+
expect(config.defaults.userId).toBe("bob");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("coerces boolean for enable_graph", () => {
|
|
109
|
+
const config = createDefaultConfig();
|
|
110
|
+
expect(setNestedValue(config, "defaults.enable_graph", "true")).toBe(true);
|
|
111
|
+
expect(config.defaults.enableGraph).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for output formatting.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
6
|
+
import {
|
|
7
|
+
formatMemoriesText,
|
|
8
|
+
formatMemoriesTable,
|
|
9
|
+
formatJson,
|
|
10
|
+
formatSingleMemory,
|
|
11
|
+
formatAddResult,
|
|
12
|
+
printResultSummary,
|
|
13
|
+
} from "../src/output.js";
|
|
14
|
+
|
|
15
|
+
let output: string;
|
|
16
|
+
const originalLog = console.log;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
output = "";
|
|
20
|
+
console.log = (...args: unknown[]) => {
|
|
21
|
+
output += args.map(String).join(" ") + "\n";
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
console.log = originalLog;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const sampleMemories = [
|
|
30
|
+
{
|
|
31
|
+
id: "abc-123-def-456",
|
|
32
|
+
memory: "User prefers dark mode",
|
|
33
|
+
score: 0.92,
|
|
34
|
+
created_at: "2026-02-15T10:30:00Z",
|
|
35
|
+
categories: ["preferences"],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "ghi-789-jkl-012",
|
|
39
|
+
memory: "User uses vim keybindings",
|
|
40
|
+
score: 0.78,
|
|
41
|
+
created_at: "2026-03-01T14:00:00Z",
|
|
42
|
+
categories: ["tools"],
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
describe("formatMemoriesText", () => {
|
|
47
|
+
it("shows count and memory content", () => {
|
|
48
|
+
formatMemoriesText(sampleMemories);
|
|
49
|
+
expect(output).toContain("Found 2");
|
|
50
|
+
expect(output).toContain("dark mode");
|
|
51
|
+
expect(output).toContain("vim keybindings");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("shows scores and IDs", () => {
|
|
55
|
+
formatMemoriesText(sampleMemories);
|
|
56
|
+
expect(output).toContain("0.92");
|
|
57
|
+
expect(output).toContain("abc-123-");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("formatMemoriesTable", () => {
|
|
62
|
+
it("renders a table with memory content", () => {
|
|
63
|
+
formatMemoriesTable(sampleMemories);
|
|
64
|
+
expect(output).toContain("dark mode");
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("formatJson", () => {
|
|
69
|
+
it("outputs valid JSON", () => {
|
|
70
|
+
formatJson({ key: "value" });
|
|
71
|
+
expect(JSON.parse(output)).toEqual({ key: "value" });
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("formatSingleMemory", () => {
|
|
76
|
+
it("shows memory text in text mode", () => {
|
|
77
|
+
formatSingleMemory(sampleMemories[0], "text");
|
|
78
|
+
expect(output).toContain("dark mode");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("outputs JSON in json mode", () => {
|
|
82
|
+
formatSingleMemory(sampleMemories[0], "json");
|
|
83
|
+
expect(output).toContain("memory");
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("formatAddResult", () => {
|
|
88
|
+
it("shows ADD event", () => {
|
|
89
|
+
formatAddResult({
|
|
90
|
+
results: [{ id: "abc-123", memory: "Test", event: "ADD" }],
|
|
91
|
+
});
|
|
92
|
+
expect(output).toContain("Added");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("shows PENDING event", () => {
|
|
96
|
+
formatAddResult({
|
|
97
|
+
results: [{ status: "PENDING", event_id: "evt-12345678" }],
|
|
98
|
+
});
|
|
99
|
+
expect(output).toContain("Queued");
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("printResultSummary", () => {
|
|
104
|
+
it("shows count and duration", () => {
|
|
105
|
+
printResultSummary({ count: 5, durationSecs: 1.23 });
|
|
106
|
+
expect(output).toContain("5 results");
|
|
107
|
+
expect(output).toContain("1.23s");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("handles singular", () => {
|
|
111
|
+
printResultSummary({ count: 1 });
|
|
112
|
+
expect(output).toContain("1 result");
|
|
113
|
+
expect(output).not.toContain("results");
|
|
114
|
+
});
|
|
115
|
+
});
|
package/tests/setup.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared test helpers and mock factories for mem0 CLI tests.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { vi } from "vitest";
|
|
6
|
+
import type { Backend } from "../src/backend/base.js";
|
|
7
|
+
|
|
8
|
+
/** Create a mock backend with all methods stubbed with sensible defaults. */
|
|
9
|
+
export function createMockBackend(): Backend {
|
|
10
|
+
return {
|
|
11
|
+
add: vi.fn().mockResolvedValue({
|
|
12
|
+
results: [
|
|
13
|
+
{
|
|
14
|
+
id: "abc-123-def-456",
|
|
15
|
+
memory: "User prefers dark mode",
|
|
16
|
+
event: "ADD",
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
}),
|
|
20
|
+
|
|
21
|
+
search: vi.fn().mockResolvedValue([
|
|
22
|
+
{
|
|
23
|
+
id: "abc-123-def-456",
|
|
24
|
+
memory: "User prefers dark mode",
|
|
25
|
+
score: 0.92,
|
|
26
|
+
created_at: "2026-02-15T10:30:00Z",
|
|
27
|
+
categories: ["preferences"],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "ghi-789-jkl-012",
|
|
31
|
+
memory: "User uses vim keybindings",
|
|
32
|
+
score: 0.78,
|
|
33
|
+
created_at: "2026-03-01T14:00:00Z",
|
|
34
|
+
categories: ["tools"],
|
|
35
|
+
},
|
|
36
|
+
]),
|
|
37
|
+
|
|
38
|
+
get: vi.fn().mockResolvedValue({
|
|
39
|
+
id: "abc-123-def-456",
|
|
40
|
+
memory: "User prefers dark mode",
|
|
41
|
+
created_at: "2026-02-15T10:30:00Z",
|
|
42
|
+
updated_at: "2026-02-20T08:00:00Z",
|
|
43
|
+
metadata: { source: "onboarding" },
|
|
44
|
+
categories: ["preferences"],
|
|
45
|
+
}),
|
|
46
|
+
|
|
47
|
+
listMemories: vi.fn().mockResolvedValue([
|
|
48
|
+
{
|
|
49
|
+
id: "abc-123-def-456",
|
|
50
|
+
memory: "User prefers dark mode",
|
|
51
|
+
created_at: "2026-02-15T10:30:00Z",
|
|
52
|
+
categories: ["preferences"],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "ghi-789-jkl-012",
|
|
56
|
+
memory: "User uses vim keybindings",
|
|
57
|
+
created_at: "2026-03-01T14:00:00Z",
|
|
58
|
+
categories: ["tools"],
|
|
59
|
+
},
|
|
60
|
+
]),
|
|
61
|
+
|
|
62
|
+
update: vi.fn().mockResolvedValue({ id: "abc-123-def-456", memory: "Updated memory" }),
|
|
63
|
+
delete: vi.fn().mockResolvedValue({ status: "deleted" }),
|
|
64
|
+
status: vi.fn().mockResolvedValue({
|
|
65
|
+
connected: true,
|
|
66
|
+
backend: "platform",
|
|
67
|
+
base_url: "https://api.mem0.ai",
|
|
68
|
+
}),
|
|
69
|
+
deleteEntities: vi.fn().mockResolvedValue({ message: "Entity deleted" }),
|
|
70
|
+
entities: vi.fn().mockResolvedValue([
|
|
71
|
+
{ name: "alice", count: 5 },
|
|
72
|
+
{ name: "bob", count: 3 },
|
|
73
|
+
]),
|
|
74
|
+
};
|
|
75
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"rootDir": "src",
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"isolatedModules": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*.ts"],
|
|
17
|
+
"exclude": ["node_modules", "dist", "tests"]
|
|
18
|
+
}
|