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:
@@ -897,6 +897,23 @@ def init(
|
||||
)
|
||||
|
||||
|
||||
@app.command(rich_help_panel="Setup")
|
||||
def identify(
|
||||
name: str = typer.Argument(..., help="Agent identity (e.g. claude-code, cursor, my-bot)."),
|
||||
) -> None:
|
||||
"""Tag your active Agent Mode key with the AI agent that's using it.
|
||||
|
||||
Run this once after `mem0 init --agent` if you didn't pass --agent-caller.
|
||||
Idempotent — re-running just overwrites the value.
|
||||
|
||||
Example:
|
||||
mem0 identify claude-code
|
||||
"""
|
||||
from mem0_cli.commands.identify_cmd import run_identify
|
||||
|
||||
run_identify(name)
|
||||
|
||||
|
||||
# (entity_app registered at module level, below sub-group definitions)
|
||||
|
||||
|
||||
|
||||
@@ -109,6 +109,12 @@ def bootstrap_via_backend(
|
||||
claim_cmd = envelope.get("claim_command", "mem0 init --email <your-email>")
|
||||
console.print(f" [{DIM_COLOR}]To claim this account later: {claim_cmd}[/]")
|
||||
|
||||
if not agent_caller:
|
||||
console.print(
|
||||
f" [{DIM_COLOR}]If you are an AI agent, identify yourself: "
|
||||
f"`mem0 identify <your-name>` (e.g. claude-code, cursor).[/]"
|
||||
)
|
||||
|
||||
|
||||
def claim_via_otp(config: Mem0Config, *, email: str, code: str | None = None) -> None:
|
||||
"""Claim an existing Agent Mode account via OTP — no browser, no polling.
|
||||
|
||||
@@ -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 — running it again just
|
||||
overwrites.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import httpx
|
||||
import typer
|
||||
from rich.console import Console
|
||||
|
||||
from mem0_cli.branding import print_error, print_success
|
||||
from mem0_cli.config import load_config, save_config
|
||||
|
||||
console = Console()
|
||||
err_console = Console(stderr=True)
|
||||
|
||||
_SOURCE_HEADERS = {
|
||||
"X-Mem0-Source": "cli",
|
||||
"X-Mem0-Client-Language": "python",
|
||||
}
|
||||
|
||||
|
||||
def run_identify(name: str) -> None:
|
||||
"""PATCH the active agent-mode key's agent_caller field."""
|
||||
config = load_config()
|
||||
if not config.platform.api_key:
|
||||
print_error(
|
||||
err_console,
|
||||
"No API key configured. Run `mem0 init --agent` first.",
|
||||
)
|
||||
raise typer.Exit(1)
|
||||
if not config.platform.agent_mode:
|
||||
print_error(
|
||||
err_console,
|
||||
"This command only works on unclaimed agent-mode keys.",
|
||||
)
|
||||
raise typer.Exit(1)
|
||||
|
||||
name = (name or "").strip()
|
||||
if not name:
|
||||
print_error(err_console, "Agent name is required.")
|
||||
raise typer.Exit(1)
|
||||
|
||||
base_url = (config.platform.base_url or "https://api.mem0.ai").rstrip("/")
|
||||
try:
|
||||
with httpx.Client(timeout=30.0) as client:
|
||||
resp = client.patch(
|
||||
f"{base_url}/api/v1/auth/agent_mode/caller/",
|
||||
headers={
|
||||
**_SOURCE_HEADERS,
|
||||
"Authorization": f"Token {config.platform.api_key}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
json={"agent_caller": name},
|
||||
)
|
||||
except httpx.HTTPError as exc:
|
||||
print_error(err_console, f"Network error: {exc}")
|
||||
raise typer.Exit(1) from exc
|
||||
|
||||
if resp.status_code != 200:
|
||||
try:
|
||||
detail = resp.json().get("error", resp.text)
|
||||
except Exception:
|
||||
detail = resp.text
|
||||
print_error(err_console, f"Identify failed: {detail}")
|
||||
raise typer.Exit(1)
|
||||
|
||||
canonical = resp.json().get("agent_caller", name)
|
||||
config.platform.agent_caller = canonical
|
||||
save_config(config)
|
||||
print_success(console, f"Identified as {canonical}.")
|
||||
Reference in New Issue
Block a user