From abc57323c27d184b0654105885da0991a8a94883 Mon Sep 17 00:00:00 2001 From: Mgeeeek Date: Wed, 20 May 2026 03:31:44 +0530 Subject: [PATCH] feat(cli): add `mem0 whoami` + `mem0 agent-rush` subcommands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors implementation across Node (TypeScript) and Python CLIs. mem0 whoami: Prints the agent's default_user_id from local config — the AGENTRUSH leaderboard identifier. Used by players to cross-verify their row. mem0 agent-rush : Wraps the platform's /v1/agent-rush/{memories/, memories/search/} endpoints. Project routing is implicit (server-resolved); no flags. Pretty-prints platform error codes into actionable hints (e.g. agentrush_search_first → "Run 3 'mem0 agent-rush search' commands before adding."). Adds X-Mem0-Mode: agent-rush header for telemetry. Reuses existing agent-mode API key from config; surfaces a graceful error if config is missing or the key was created outside agent-mode. Platform PR: mem0ai/platform#2804 --- cli/node/src/commands/agent-rush.ts | 94 +++++++++++++++++++ cli/node/src/commands/whoami.ts | 20 ++++ cli/node/src/index.ts | 42 +++++++++ cli/python/src/mem0_cli/app.py | 59 ++++++++++++ .../src/mem0_cli/commands/agent_rush_cmd.py | 86 +++++++++++++++++ .../src/mem0_cli/commands/whoami_cmd.py | 25 +++++ 6 files changed, 326 insertions(+) create mode 100644 cli/node/src/commands/agent-rush.ts create mode 100644 cli/node/src/commands/whoami.ts create mode 100644 cli/python/src/mem0_cli/commands/agent_rush_cmd.py create mode 100644 cli/python/src/mem0_cli/commands/whoami_cmd.py diff --git a/cli/node/src/commands/agent-rush.ts b/cli/node/src/commands/agent-rush.ts new file mode 100644 index 000000000..b01f79226 --- /dev/null +++ b/cli/node/src/commands/agent-rush.ts @@ -0,0 +1,94 @@ +/** + * `mem0 agent-rush "..."` — wraps the AGENTRUSH platform endpoints. + * Project routing is implicit (server-side); zero flags needed. + */ + +import { colors, printError, printSuccess } from "../branding.js"; +import { loadConfig } from "../config.js"; +import { CLI_VERSION } from "../version.js"; + +const ERROR_HINTS: Record = { + agentrush_search_first: + "Run 3 'mem0 agent-rush search' commands before adding.", + agentrush_search_quota: "You've used your 3 lifetime searches.", + agentrush_add_quota: "You've used your 3 lifetime adds.", + agentrush_not_agent_mode: + "Re-run 'mem0 init --agent' to bootstrap an agent-mode key.", + agentrush_length: "Memory text must be 50-1000 characters.", + agentrush_no_urls: "URLs are not allowed.", + agentrush_blocklist: "Content contains a blocked term.", + agentrush_global_quota: "Event-wide cap reached. Try again later.", + agentrush_not_provisioned: + "AGENTRUSH is not provisioned in this environment.", +}; + +async function callEndpoint( + path: string, + body: Record, +): Promise { + const config = loadConfig(); + const baseUrl = (config.platform?.baseUrl ?? "https://api.mem0.ai").replace( + /\/+$/, + "", + ); + + if (!config.platform?.apiKey) { + printError("Not initialized. Run `mem0 init --agent` first."); + process.exit(1); + } + + const resp = await fetch(`${baseUrl}${path}`, { + method: "POST", + headers: { + Authorization: `Token ${config.platform.apiKey}`, + "Content-Type": "application/json", + "X-Mem0-Source": "cli", + "X-Mem0-Client-Language": "node", + "X-Mem0-Client-Version": CLI_VERSION, + "X-Mem0-Mode": "agent-rush", + }, + body: JSON.stringify(body), + signal: AbortSignal.timeout(30_000), + }); + + const json = await resp.json().catch(() => ({})); + + if (!resp.ok) { + const code = + (json as { error?: { code?: string } }).error?.code ?? "unknown"; + printError(`AGENTRUSH error: ${code}`); + if (ERROR_HINTS[code]) { + console.log(` ${colors.dim(ERROR_HINTS[code])}`); + } + process.exit(1); + } + + return json; +} + +export async function cmdAgentRushAdd(content: string): Promise { + const result = await callEndpoint("/v1/agent-rush/memories/", { content }); + printSuccess( + `Memory submitted (event_id: ${(result as { event_id?: string }).event_id ?? "?"})`, + ); +} + +export async function cmdAgentRushSearch(query: string): Promise { + const result = (await callEndpoint("/v1/agent-rush/memories/search/", { + query, + })) as { + results?: Array<{ memory?: string }>; + memories?: Array<{ memory?: string }>; + }; + + const memories = result.results ?? result.memories ?? []; + + if (memories.length === 0) { + console.log(colors.dim("(no results)")); + return; + } + + memories.slice(0, 5).forEach((m, i) => { + console.log(` ${i + 1}. ${m.memory ?? JSON.stringify(m)}`); + }); +} diff --git a/cli/node/src/commands/whoami.ts b/cli/node/src/commands/whoami.ts new file mode 100644 index 000000000..7c49901e2 --- /dev/null +++ b/cli/node/src/commands/whoami.ts @@ -0,0 +1,20 @@ +/** + * `mem0 whoami` — print the active agent's default_user_id (AGENTRUSH identifier). + * Reads from local config; no network call. + */ + +import { colors, printError, printInfo } from "../branding.js"; +import { loadConfig } from "../config.js"; + +export async function cmdWhoami(): Promise { + const config = loadConfig(); + const sessionId = config.platform?.defaultUserId; + if (!sessionId) { + printError( + "No default_user_id found. Run `mem0 init --agent` first.", + ); + process.exit(1); + } + console.log(`Your AGENTRUSH identifier: ${colors.brand(sessionId)}`); + printInfo("Find your row at https://mem0.ai/agentrush"); +} diff --git a/cli/node/src/index.ts b/cli/node/src/index.ts index 219c63b1e..b038fb171 100644 --- a/cli/node/src/index.ts +++ b/cli/node/src/index.ts @@ -262,6 +262,48 @@ program await runIdentify(name); }); +// ── Setup: whoami (print active agent identifier) ──────────────────────── + +program + .command("whoami") + .description("Print the active agent's AGENTRUSH identifier.") + .action(async () => { + const { cmdWhoami } = await import("./commands/whoami.js"); + await cmdWhoami(); + }); + +// ── AGENTRUSH subcommand group ──────────────────────────────────────────── + +const agentRush = program + .command("agent-rush") + .description("AGENTRUSH game commands.") + .addHelpCommand(false) + .configureHelp({ formatHelp: richFormatHelp }); + +agentRush + .command("add ") + .description("Submit a memory to AGENTRUSH.") + .addHelpText( + "after", + '\nExamples:\n $ mem0 agent-rush add "I used mem0 to build a coding agent"\n $ mem0 agent-rush add "Agents that remember are better agents"', + ) + .action(async (parts: string[]) => { + const { cmdAgentRushAdd } = await import("./commands/agent-rush.js"); + await cmdAgentRushAdd(parts.join(" ")); + }); + +agentRush + .command("search ") + .description("Search AGENTRUSH memories.") + .addHelpText( + "after", + '\nExamples:\n $ mem0 agent-rush search "agents and memory and tools"\n $ mem0 agent-rush search "coding assistant"', + ) + .action(async (parts: string[]) => { + const { cmdAgentRushSearch } = await import("./commands/agent-rush.js"); + await cmdAgentRushSearch(parts.join(" ")); + }); + // ── Memory: add ─────────────────────────────────────────────────────────── program diff --git a/cli/python/src/mem0_cli/app.py b/cli/python/src/mem0_cli/app.py index b115164e9..8eff3429f 100644 --- a/cli/python/src/mem0_cli/app.py +++ b/cli/python/src/mem0_cli/app.py @@ -914,6 +914,65 @@ def identify( run_identify(name) +@app.command(name="whoami", rich_help_panel="Setup") +def whoami_cmd() -> None: + """Print your AGENTRUSH identifier (default_user_id). + + Example: + mem0 whoami + """ + from mem0_cli.commands.whoami_cmd import run_whoami + + run_whoami() + + +# ── AGENTRUSH sub-app ───────────────────────────────────────────────────── + +agent_rush_app = typer.Typer( + name="agent-rush", + help="AGENTRUSH game commands", + no_args_is_help=True, + rich_markup_mode="rich", +) + + +@agent_rush_app.callback(invoke_without_command=True) +def _agent_rush_callback(ctx: typer.Context) -> None: + if ctx.invoked_subcommand: + _fire_telemetry(f"agent-rush.{ctx.invoked_subcommand}") + + +@agent_rush_app.command(name="add") +def agent_rush_add( + content: str = typer.Argument(..., help="Memory content (50–1000 characters, no URLs)."), +) -> None: + """Submit a memory to AGENTRUSH. + + Example: + mem0 agent-rush add "I enjoy solving constraint-satisfaction problems." + """ + from mem0_cli.commands.agent_rush_cmd import run_agent_rush_add + + run_agent_rush_add(content) + + +@agent_rush_app.command(name="search") +def agent_rush_search( + query: str = typer.Argument(..., help="Search query."), +) -> None: + """Search AGENTRUSH memories. + + Example: + mem0 agent-rush search "constraint satisfaction" + """ + from mem0_cli.commands.agent_rush_cmd import run_agent_rush_search + + run_agent_rush_search(query) + + +app.add_typer(agent_rush_app, name="agent-rush", rich_help_panel="Setup") + + # (entity_app registered at module level, below sub-group definitions) diff --git a/cli/python/src/mem0_cli/commands/agent_rush_cmd.py b/cli/python/src/mem0_cli/commands/agent_rush_cmd.py new file mode 100644 index 000000000..cd958b40d --- /dev/null +++ b/cli/python/src/mem0_cli/commands/agent_rush_cmd.py @@ -0,0 +1,86 @@ +"""mem0 agent-rush — AGENTRUSH game commands. + +Wraps the platform's /v1/agent-rush/{memories/, memories/search/} endpoints. +Hardcoded routing; no flags needed. +""" + +from __future__ import annotations + +import httpx +import typer +from rich.console import Console + +from mem0_cli.branding import BRAND_COLOR, print_error, print_success +from mem0_cli.config import load_config + +console = Console() +err_console = Console(stderr=True) + +_SOURCE_HEADERS = { + "X-Mem0-Source": "cli", + "X-Mem0-Client-Language": "python", + "X-Mem0-Mode": "agent-rush", +} + +_ERROR_HINTS = { + "agentrush_search_first": "Run 3 'mem0 agent-rush search' commands before adding.", + "agentrush_search_quota": "You've used your 3 lifetime searches.", + "agentrush_add_quota": "You've used your 3 lifetime adds.", + "agentrush_not_agent_mode": "Re-run 'mem0 init --agent' to bootstrap an agent-mode key.", + "agentrush_length": "Memory text must be 50-1000 characters.", + "agentrush_no_urls": "URLs are not allowed.", + "agentrush_blocklist": "Content contains a blocked term.", + "agentrush_global_quota": "Event-wide cap reached. Try again later.", + "agentrush_not_provisioned": "AGENTRUSH is not provisioned in this environment.", +} + + +def _call(path: str, body: dict) -> dict: + config = load_config() + if not config.platform.api_key: + print_error(err_console, "Not initialized. Run `mem0 init --agent` first.") + 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.post( + f"{base_url}{path}", + headers={ + **_SOURCE_HEADERS, + "Authorization": f"Token {config.platform.api_key}", + "Content-Type": "application/json", + }, + json=body, + ) + except httpx.HTTPError as exc: + print_error(err_console, f"Network error: {exc}") + raise typer.Exit(1) from exc + try: + data = resp.json() + except Exception: + data = {} + if resp.status_code >= 400: + code = (data.get("error") or {}).get("code", "unknown") if isinstance(data, dict) else "unknown" + print_error(err_console, f"AGENTRUSH error: {code}") + hint = _ERROR_HINTS.get(code) + if hint: + console.print(f" [dim]{hint}[/dim]") + raise typer.Exit(1) + return data + + +def run_agent_rush_add(content: str) -> None: + result = _call("/v1/agent-rush/memories/", {"content": content}) + event_id = result.get("event_id", "?") + print_success(console, f"Memory submitted (event_id: {event_id})") + + +def run_agent_rush_search(query: str) -> None: + result = _call("/v1/agent-rush/memories/search/", {"query": query}) + memories = result.get("results") or result.get("memories") or [] + if not memories: + console.print("[dim](no results)[/dim]") + return + for i, m in enumerate(memories[:5], start=1): + text = m.get("memory") if isinstance(m, dict) else str(m) + console.print(f" {i}. {text}") diff --git a/cli/python/src/mem0_cli/commands/whoami_cmd.py b/cli/python/src/mem0_cli/commands/whoami_cmd.py new file mode 100644 index 000000000..21c30758e --- /dev/null +++ b/cli/python/src/mem0_cli/commands/whoami_cmd.py @@ -0,0 +1,25 @@ +"""mem0 whoami — print the active agent's default_user_id (AGENTRUSH identifier).""" + +from __future__ import annotations + +import typer +from rich.console import Console + +from mem0_cli.branding import BRAND_COLOR, print_error, print_info +from mem0_cli.config import load_config + +console = Console() +err_console = Console(stderr=True) + + +def run_whoami() -> None: + config = load_config() + session_id = config.platform.default_user_id if config.platform else None + if not session_id: + print_error( + err_console, + "No default_user_id found. Run `mem0 init --agent` first.", + ) + raise typer.Exit(1) + console.print(f"Your AGENTRUSH identifier: [{BRAND_COLOR}]{session_id}[/{BRAND_COLOR}]") + print_info(console, "Find your row at https://mem0.ai/agentrush")