feat(cli): mem0 identify <name> — post-init agent self-identify

Adds a one-shot subcommand the agent runs when it bootstrapped without
--agent-caller: `mem0 identify claude-code` PATCHes the active key's
agent_caller via /api/v1/auth/agent_mode/caller/, then mirrors the
canonical value into ~/.mem0/config.json.

Bootstrap success message now prompts unidentified agents to run it.
If --agent-caller was passed on init, no prompt (already attributed).

Both runtimes (Python + Node) get the command + the prompt update.
This commit is contained in:
Mgeeeek
2026-05-14 17:57:35 +05:30
parent 8aa07c2f62
commit a9455313cc
6 changed files with 191 additions and 0 deletions
+6
View File
@@ -110,6 +110,12 @@ export async function bootstrapViaBackend(
const claimCmd = envelope.claim_command ?? "mem0 init --email <your-email>";
console.log(` ${dim(`To claim this account later: ${claimCmd}`)}`);
}
if (!agentCaller) {
console.log(
` ${dim("If you are an AI agent, identify yourself: `mem0 identify <your-name>` (e.g. claude-code, cursor).")}`,
);
}
}
/**
+75
View File
@@ -0,0 +1,75 @@
/**
* mem0 identify — declare which agent owns the current agent-mode key.
*
* Used when `mem0 init --agent` ran without --agent-caller, so the backend
* saved agent_caller=NULL. The agent re-runs `mem0 identify <name>` to PATCH
* its own row with its real identity. Idempotent.
*/
import { printError, printSuccess } from "../branding.js";
import { loadConfig, saveConfig } from "../config.js";
const SOURCE_HEADERS = {
"X-Mem0-Source": "cli",
"X-Mem0-Client-Language": "node",
} as const;
export async function runIdentify(name: string): Promise<void> {
const config = loadConfig();
if (!config.platform.apiKey) {
printError("No API key configured. Run `mem0 init --agent` first.");
process.exit(1);
}
if (!config.platform.agentMode) {
printError("This command only works on unclaimed agent-mode keys.");
process.exit(1);
}
const clean = (name ?? "").trim();
if (!clean) {
printError("Agent name is required.");
process.exit(1);
}
const baseUrl = (config.platform.baseUrl || "https://api.mem0.ai").replace(
/\/+$/,
"",
);
let resp: Response;
try {
resp = await fetch(`${baseUrl}/api/v1/auth/agent_mode/caller/`, {
method: "PATCH",
headers: {
...SOURCE_HEADERS,
Authorization: `Token ${config.platform.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ agent_caller: clean }),
signal: AbortSignal.timeout(30_000),
});
} catch (err) {
printError(
`Network error: ${err instanceof Error ? err.message : String(err)}`,
);
process.exit(1);
}
if (!resp.ok) {
let detail: string = resp.statusText;
try {
const body = (await resp.json()) as { error?: string };
if (body.error) detail = body.error;
} catch {
/* leave as statusText */
}
printError(`Identify failed: ${detail}`);
process.exit(1);
}
const body = (await resp.json()) as { agent_caller?: string };
const canonical = body.agent_caller ?? clean;
config.platform.agentCaller = canonical;
saveConfig(config);
printSuccess(`Identified as ${canonical}.`);
}
+12
View File
@@ -250,6 +250,18 @@ program
});
});
// ── Setup: identify (post-bootstrap agent self-tag) ──────────────────────
program
.command("identify <name>")
.description(
"Tag your active Agent Mode key with the AI agent that's using it (e.g. claude-code, cursor).",
)
.action(async (name: string) => {
const { runIdentify } = await import("./commands/identify.js");
await runIdentify(name);
});
// ── Memory: add ───────────────────────────────────────────────────────────
program