From d92fb3984329d8769365f45efd6b1793964bf756 Mon Sep 17 00:00:00 2001 From: Dhravya Shah Date: Mon, 10 Aug 2026 12:56:17 -0700 Subject: [PATCH] Use /v3/session for status account info instead of probing account lifecycle endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The status skill probed /v3/auth/account/memberships (and two other guessed paths) with the user's API key to scrape account info. That endpoint is session-only — every API-key request 403s with 'API keys cannot access account lifecycle endpoints' (Sentry issue SUPERMEMORY-BACKEND-108R). /v3/session is the whoami endpoint that works with API-key auth (it's what the MCP server uses) and returns user email/name/id and org name directly, so the generic JSON scraper is gone too. Bumps to 1.0.13. Co-Authored-By: Claude Fable 5 --- package.json | 2 +- src/skills/status.ts | 44 +++++++++++++------------------------------- 2 files changed, 14 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index eb5ab5e..baa0231 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codex-supermemory", - "version": "1.0.12", + "version": "1.0.13", "description": "Persistent memory for OpenAI Codex CLI — powered by Supermemory", "type": "module", "main": "dist/cli.js", diff --git a/src/skills/status.ts b/src/skills/status.ts index 6a1d6f5..d506921 100644 --- a/src/skills/status.ts +++ b/src/skills/status.ts @@ -66,38 +66,20 @@ async function fetchJson(path: string): Promise { } } -function findAccountInfo(value: unknown): { email?: string; name?: string; userId?: string; orgName?: string } { - const seen = new Set(); - const stack = [value]; - const result: { email?: string; name?: string; userId?: string; orgName?: string } = {}; - - while (stack.length > 0) { - const item = stack.pop(); - if (!item || typeof item !== "object" || seen.has(item)) continue; - seen.add(item); - - const record = item as Record; - for (const [key, raw] of Object.entries(record)) { - const lower = key.toLowerCase(); - if (!result.email && lower === "email" && typeof raw === "string") result.email = raw; - if (!result.name && lower === "name" && typeof raw === "string") result.name = raw; - if (!result.userId && (lower === "userid" || lower === "user_id") && typeof raw === "string") result.userId = raw; - if (!result.orgName && (lower === "organizationname" || lower === "orgname") && typeof raw === "string") result.orgName = raw; - - if (raw && typeof raw === "object") stack.push(raw); - } - } - - return result; -} - async function getAccountInfo(): Promise<{ email?: string; name?: string; userId?: string; orgName?: string }> { - for (const path of ["/v3/auth/account/memberships", "/v3/account/memberships", "/v3/me"]) { - const data = await fetchJson(path); - const info = findAccountInfo(data); - if (info.email || info.name || info.userId || info.orgName) return info; - } - return {}; + const data = await fetchJson("/v3/session"); + if (!data || typeof data !== "object") return {}; + + const session = data as { + user?: { email?: unknown; name?: unknown; id?: unknown }; + org?: { name?: unknown }; + }; + return { + email: typeof session.user?.email === "string" ? session.user.email : undefined, + name: typeof session.user?.name === "string" ? session.user.name : undefined, + userId: typeof session.user?.id === "string" ? session.user.id : undefined, + orgName: typeof session.org?.name === "string" ? session.org.name : undefined, + }; } async function main(): Promise {