refactor: update memory skill loader, plugin config, and add privacy docs (#4905)

This commit is contained in:
Kartik
2026-04-22 17:15:19 +05:30
committed by GitHub
parent 32b74e18b7
commit f5dc825d47
21 changed files with 1734 additions and 189 deletions
+296
View File
@@ -32,6 +32,7 @@ vi.mock("../skill-loader.ts", () => ({
loadDreamPrompt: vi.fn().mockReturnValue("dream prompt"),
}));
// ---------------------------------------------------------------------------
// Imports (after mocks)
// ---------------------------------------------------------------------------
@@ -251,6 +252,7 @@ describe("registerCliCommands", () => {
warn: ReturnType<typeof vi.spyOn>;
};
let stderrSpy: ReturnType<typeof vi.spyOn>;
let stdoutSpy: ReturnType<typeof vi.spyOn>;
beforeEach(() => {
vi.resetAllMocks();
@@ -267,6 +269,7 @@ describe("registerCliCommands", () => {
warn: vi.spyOn(console, "warn").mockImplementation(() => {}),
};
stderrSpy = vi.spyOn(process.stderr, "write").mockImplementation(() => true);
stdoutSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
});
afterEach(() => {
@@ -274,6 +277,7 @@ describe("registerCliCommands", () => {
consoleSpy.error.mockRestore();
consoleSpy.warn.mockRestore();
stderrSpy.mockRestore();
stdoutSpy.mockRestore();
vi.restoreAllMocks();
});
@@ -527,6 +531,158 @@ describe("registerCliCommands", () => {
vi.unstubAllGlobals();
});
it("outputs JSON for --api-key flow when --json is set", async () => {
const { mem0 } = setup();
const initCmd = findCommand(mem0, "init")!;
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
ok: true,
json: vi.fn().mockResolvedValue({}),
}));
await initCmd._action!({ apiKey: "m0-key", json: true });
const jsonCall = stdoutSpy.mock.calls.find((c) => {
try {
const p = JSON.parse(c[0] as string);
return typeof p.ok === "boolean";
} catch { return false; }
});
expect(jsonCall).toBeDefined();
const parsed = JSON.parse(jsonCall![0] as string);
expect(parsed.ok).toBe(true);
expect(parsed.mode).toBe("platform");
expect(parsed.validated).toBe(true);
vi.unstubAllGlobals();
});
it("outputs JSON for --api-key flow with failed validation when --json is set", async () => {
const { mem0 } = setup();
const initCmd = findCommand(mem0, "init")!;
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
ok: false,
status: 401,
json: vi.fn().mockResolvedValue({}),
}));
await initCmd._action!({ apiKey: "bad-key", json: true });
const jsonCall = stdoutSpy.mock.calls.find((c) => {
try {
const p = JSON.parse(c[0] as string);
return typeof p.ok === "boolean";
} catch { return false; }
});
expect(jsonCall).toBeDefined();
const parsed = JSON.parse(jsonCall![0] as string);
expect(parsed.ok).toBe(false);
expect(parsed.mode).toBe("platform");
expect(parsed.validated).toBe(false);
expect(parsed.httpStatus).toBe(401);
vi.unstubAllGlobals();
});
it("outputs JSON for --api-key + --email conflict when --json is set", async () => {
const { mem0 } = setup();
const initCmd = findCommand(mem0, "init")!;
await initCmd._action!({ apiKey: "key", email: "a@b.com", json: true });
const jsonCall = stdoutSpy.mock.calls.find((c) => {
try {
const p = JSON.parse(c[0] as string);
return p.ok === false;
} catch { return false; }
});
expect(jsonCall).toBeDefined();
const parsed = JSON.parse(jsonCall![0] as string);
expect(parsed.error).toContain("Cannot use both");
expect(writePluginAuth).not.toHaveBeenCalled();
});
it("outputs JSON for email send-code flow when --json is set", async () => {
const { mem0 } = setup();
const initCmd = findCommand(mem0, "init")!;
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
ok: true,
json: vi.fn().mockResolvedValue({}),
}));
await initCmd._action!({ email: "user@example.com", json: true });
const jsonCall = stdoutSpy.mock.calls.find((c) => {
try {
const p = JSON.parse(c[0] as string);
return p.codeSent === true;
} catch { return false; }
});
expect(jsonCall).toBeDefined();
const parsed = JSON.parse(jsonCall![0] as string);
expect(parsed.ok).toBe(true);
expect(parsed.email).toBe("user@example.com");
expect(parsed.nextCommand).toContain("--code");
vi.unstubAllGlobals();
});
it("outputs JSON for email verify flow when --json is set", async () => {
const { mem0 } = setup();
const initCmd = findCommand(mem0, "init")!;
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
ok: true,
json: vi.fn().mockResolvedValue({ api_key: "m0-verified" }),
}));
await initCmd._action!({ email: "u@b.com", code: "123456", json: true });
const jsonCall = stdoutSpy.mock.calls.find((c) => {
try {
const p = JSON.parse(c[0] as string);
return p.ok === true && p.mode === "platform";
} catch { return false; }
});
expect(jsonCall).toBeDefined();
const parsed = JSON.parse(jsonCall![0] as string);
expect(parsed.email).toBe("u@b.com");
expect(parsed.message).toContain("Authenticated");
vi.unstubAllGlobals();
});
it("clears stale apiKey when switching to OSS mode", async () => {
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, json: async () => ({}) }));
const { mem0 } = setup();
const initCmd = findCommand(mem0, "init")!;
(readPluginAuth as ReturnType<typeof vi.fn>).mockReturnValue({
apiKey: "m0-old-platform-key",
mode: "platform",
userId: "testuser",
});
await initCmd._action!({
mode: "open-source",
ossLlm: "ollama",
ossEmbedder: "ollama",
ossVector: "qdrant",
});
expect(writePluginAuth).toHaveBeenCalledWith(
expect.objectContaining({
apiKey: "",
mode: "open-source",
}),
);
vi.unstubAllGlobals();
});
});
// ========================================================================
@@ -1456,4 +1612,144 @@ describe("registerCliCommands", () => {
);
});
});
// ========================================================================
// Restructured init menu flags
// ========================================================================
describe("init — restructured menu", () => {
it("registers --mode flag", () => {
const { mem0 } = setup();
const initCmd = findCommand(mem0, "init")!;
const modeOpt = initCmd._options.find((o) => o.flags.includes("--mode"));
expect(modeOpt).toBeDefined();
});
it("registers --oss-llm flag", () => {
const { mem0 } = setup();
const initCmd = findCommand(mem0, "init")!;
const opt = initCmd._options.find((o) => o.flags.includes("--oss-llm "));
expect(opt).toBeDefined();
});
it("registers --json flag on init", () => {
const { mem0 } = setup();
const initCmd = findCommand(mem0, "init")!;
const opt = initCmd._options.find((o) => o.flags.includes("--json"));
expect(opt).toBeDefined();
});
});
// ========================================================================
// --json flag registration on all commands
// ========================================================================
describe("--json flag registration", () => {
for (const name of ["search", "add", "get", "list", "update", "delete", "status", "import", "dream"]) {
it(`registers --json on ${name}`, () => {
const { mem0 } = setup();
const cmd = findCommand(mem0, name)!;
const opt = cmd._options.find((o) => o.flags.includes("--json"));
expect(opt).toBeDefined();
});
}
it("registers --json on config show", () => {
const { mem0 } = setup();
const configCmd = findCommand(mem0, "config")!;
const showCmd = findCommand(configCmd, "show")!;
expect(showCmd).toBeDefined();
const opt = showCmd._options.find((o) => o.flags.includes("--json"));
expect(opt).toBeDefined();
});
});
// ========================================================================
// Non-interactive OSS init
// ========================================================================
describe("init --mode open-source (non-interactive)", () => {
it("writes LLM, embedder, and vector config for ollama + qdrant", async () => {
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, json: async () => ({}) }));
const { mem0 } = setup();
const initCmd = findCommand(mem0, "init")!;
await initCmd._action!({
mode: "open-source",
ossLlm: "ollama",
ossEmbedder: "ollama",
ossVector: "qdrant",
userId: "test-user",
});
expect(writePluginConfigField).toHaveBeenCalledWith(
["oss", "llm"],
expect.objectContaining({ provider: "ollama" }),
);
expect(writePluginConfigField).toHaveBeenCalledWith(
["oss", "embedder"],
expect.objectContaining({ provider: "ollama" }),
);
expect(writePluginConfigField).toHaveBeenCalledWith(
["oss", "vectorStore"],
expect.objectContaining({ provider: "qdrant" }),
);
expect(writePluginAuth).toHaveBeenCalledWith(
expect.objectContaining({ mode: "open-source", userId: "test-user" }),
);
vi.unstubAllGlobals();
});
it("outputs JSON when --json is passed", async () => {
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, json: async () => ({}) }));
const { mem0 } = setup();
const initCmd = findCommand(mem0, "init")!;
await initCmd._action!({
mode: "open-source",
ossLlm: "ollama",
ossEmbedder: "ollama",
ossVector: "qdrant",
json: true,
});
const jsonCall = stdoutSpy.mock.calls.find((c) => {
try {
const p = JSON.parse(c[0] as string);
return p.ok === true;
} catch {
return false;
}
});
expect(jsonCall).toBeDefined();
if (jsonCall) {
const parsed = JSON.parse(jsonCall[0] as string);
expect(parsed.mode).toBe("open-source");
expect(parsed.config.llm.provider).toBe("ollama");
}
vi.unstubAllGlobals();
});
it("errors when openai LLM has no key", async () => {
const { mem0 } = setup();
const initCmd = findCommand(mem0, "init")!;
// Ensure env var is not set so validation fails
const savedEnv = process.env.OPENAI_API_KEY;
delete process.env.OPENAI_API_KEY;
await initCmd._action!({ mode: "open-source", ossLlm: "openai" });
expect(consoleSpy.error).toHaveBeenCalledWith(
expect.stringContaining("--oss-llm-key"),
);
// Restore env var
if (savedEnv !== undefined) process.env.OPENAI_API_KEY = savedEnv;
});
});
});