From 90e5d61c461978128b16e18d8dd4fac29df233e4 Mon Sep 17 00:00:00 2001 From: Mahesh Sanikommu Date: Mon, 22 Jun 2026 17:50:46 -0700 Subject: [PATCH 1/2] fix: route session capture to shared repo container Move automatic session writes from the user tag to the repo tag, search legacy containers for read compat, and resolve local auth URLs from install config. --- build.mjs | 1 + src/cli.ts | 3 +- src/config.ts | 25 ++++++++++++ src/hooks/recall.ts | 10 ++--- src/hooks/session-start.ts | 4 +- src/services/auth.ts | 56 ++++++++++++++++++++++++-- src/services/capture.ts | 16 ++++---- src/services/tags.ts | 5 +++ src/skills/forget-memory.ts | 29 +++++--------- src/skills/login.ts | 4 +- src/skills/search-memory.ts | 33 +++++----------- test/unit.mjs | 78 +++++++++++++++++++++++++++++++++++-- 12 files changed, 195 insertions(+), 69 deletions(-) diff --git a/build.mjs b/build.mjs index d59d18d..ac8b045 100644 --- a/build.mjs +++ b/build.mjs @@ -25,6 +25,7 @@ const executableEntries = [ rmSync("dist", { recursive: true, force: true }); const libraryEntries = [ + { in: "src/services/auth.ts", out: "dist/services/auth.js" }, { in: "src/services/session.ts", out: "dist/services/session.js" }, { in: "src/services/tags.ts", out: "dist/services/tags.js" }, ]; diff --git a/src/cli.ts b/src/cli.ts index bb9b2e0..be77189 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -7,7 +7,7 @@ import { rmSync, } from "node:fs"; import { loadCredentials } from "./services/auth.js"; -import { writeInstallDefaults, CONFIG_FILE, getRecallModeSummary, CONFIG } from "./config.js"; +import { writeInstallDefaults, persistInstallEnvUrls, CONFIG_FILE, getRecallModeSummary, CONFIG } from "./config.js"; import { join, dirname } from "node:path"; import { homedir } from "node:os"; import { fileURLToPath } from "node:url"; @@ -259,6 +259,7 @@ function install() { const hadExistingConfig = existsSync(CONFIG_FILE); writeInstallDefaults(hadExistingConfig); + persistInstallEnvUrls(); // Copy hook scripts const recallSrc = join(DIST_HOOKS_DIR, "recall.js"); diff --git a/src/config.ts b/src/config.ts index fe3c08f..bcec2eb 100644 --- a/src/config.ts +++ b/src/config.ts @@ -15,6 +15,7 @@ export interface CustomContainer { interface CodexSupermemoryConfig { apiKey?: string; baseUrl?: string; + authUrl?: string; similarityThreshold?: number; maxMemories?: number; maxProfileItems?: number; @@ -224,6 +225,30 @@ export function validateContainerTag(tag: string): string | null { return `Unknown container tag '${tag}'. Valid containers: ${validList}`; } +/** Persist SUPERMEMORY_API_URL / SUPERMEMORY_AUTH_URL from install env into config. */ +export function persistInstallEnvUrls(): void { + const apiUrl = process.env.SUPERMEMORY_API_URL || process.env.SUPERMEMORY_BASE_URL; + const authUrl = process.env.SUPERMEMORY_AUTH_URL; + if (!apiUrl && !authUrl) return; + + const { config: current } = loadRawConfig(); + const next: CodexSupermemoryConfig = { ...current }; + + const normalizedApiUrl = apiUrl ? normalizeBaseUrl(apiUrl) : null; + if (normalizedApiUrl) next.baseUrl = normalizedApiUrl; + + if (authUrl) { + try { + const url = new URL(authUrl); + if (url.protocol === "http:" || url.protocol === "https:") { + next.authUrl = authUrl.trim().replace(/\/$/, ""); + } + } catch {} + } + + writeFileSync(CONFIG_FILE, JSON.stringify(next, null, 2)); +} + /** Persist explicit recall/capture defaults for fresh installs or legacy upgrades. */ export function writeInstallDefaults(isExistingInstall: boolean): void { const current = loadRawConfig().config; diff --git a/src/hooks/recall.ts b/src/hooks/recall.ts index 09ae88e..9fb96cb 100644 --- a/src/hooks/recall.ts +++ b/src/hooks/recall.ts @@ -3,10 +3,10 @@ import { join, dirname } from "node:path"; import { homedir } from "node:os"; import { isConfigured, CONFIG, reloadApiKey, getContainerCatalog } from "../config.js"; import { SupermemoryClient } from "../services/client.js"; -import { getProjectSearchTags, getTags } from "../services/tags.js"; +import { getRepoSearchTags, getTags } from "../services/tags.js"; import { formatCombinedContext } from "../services/context.js"; import { log } from "../services/logger.js"; -import { startAuthFlow, AUTH_BASE_URL } from "../services/auth.js"; +import { startAuthFlow, getAuthBaseUrl } from "../services/auth.js"; import { captureEntries, resolveTranscriptPath } from "../services/capture.js"; import { getSeenFacts, addSeenFacts } from "../services/factCache.js"; import { getSessionId } from "../services/session.js"; @@ -73,7 +73,7 @@ async function main() { (isTimeout ? "Authentication timed out. Please complete login in the browser.\n" : "Authentication failed.\n") + - `If the browser did not open, visit: ${AUTH_BASE_URL}\n` + + `If the browser did not open, visit: ${getAuthBaseUrl()}\n` + "Run /supermemory-login to try again, or set SUPERMEMORY_CODEX_API_KEY manually." ); } @@ -99,7 +99,7 @@ async function main() { const cwd = payload.cwd || process.cwd(); const tags = getTags(cwd); - const projectSearchTags = getProjectSearchTags(cwd); + const projectSearchTags = getRepoSearchTags(cwd); const sessionId = getSessionId(payload.session_id, tags.project); log("recall: start", { @@ -126,7 +126,7 @@ async function main() { try { const [profileResult, projectSearchResult] = await Promise.all([ - client.getProfileWithSearch(tags.user, query), + client.getProfileWithSearch(tags.project, query), client.searchMemoriesAcrossContainers(query, projectSearchTags), ]); diff --git a/src/hooks/session-start.ts b/src/hooks/session-start.ts index 5af3c55..6deabf6 100644 --- a/src/hooks/session-start.ts +++ b/src/hooks/session-start.ts @@ -6,7 +6,7 @@ import { SupermemoryClient } from "../services/client.js"; import { getTags } from "../services/tags.js"; import { formatCombinedContext } from "../services/context.js"; import { log } from "../services/logger.js"; -import { startAuthFlow, AUTH_BASE_URL } from "../services/auth.js"; +import { startAuthFlow, getAuthBaseUrl } from "../services/auth.js"; import { getSeenFacts, addSeenFacts } from "../services/factCache.js"; import { checkNpmUpdate, formatUpdateNotice } from "../services/version-check.js"; @@ -60,7 +60,7 @@ async function main() { } catch { exitWithContext( "[SUPERMEMORY] Memory is installed but NOT active — missing API key.\n" + - `Visit: ${AUTH_BASE_URL}\n` + + `Visit: ${getAuthBaseUrl()}\n` + "Run /supermemory-login to authenticate." ); } diff --git a/src/services/auth.ts b/src/services/auth.ts index 5f5a773..aae6cb8 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -8,16 +8,64 @@ import { openUrl } from "./openUrl.js"; const SUPERMEMORY_DIR = join(homedir(), ".codex", "supermemory"); const CREDENTIALS_FILE = join(SUPERMEMORY_DIR, "credentials.json"); +const DEFAULT_AUTH_BASE_URL = "https://app.supermemory.ai/auth/agent-connect"; + +function configFilePath(): string { + return join(homedir(), ".codex", "supermemory.json"); +} export interface Credentials { apiKey?: string; apiBaseUrl?: string; savedAt?: string; } -const AUTH_BASE_URL = - process.env.SUPERMEMORY_AUTH_URL || "https://app.supermemory.ai/auth/agent-connect"; const AUTH_TIMEOUT = Number(process.env.SUPERMEMORY_AUTH_TIMEOUT) || 5 * 60_000; +function readFileConfig(): { baseUrl?: string; authUrl?: string } { + try { + const configPath = configFilePath(); + if (existsSync(configPath)) { + return JSON.parse(readFileSync(configPath, "utf-8")) as { + baseUrl?: string; + authUrl?: string; + }; + } + } catch {} + return {}; +} + +function isLocalHost(hostname: string): boolean { + return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0"; +} + +/** Resolve auth URL: env → config → derive from local API URL → prod default. */ +export function getAuthBaseUrl(): string { + if (process.env.SUPERMEMORY_AUTH_URL) { + return process.env.SUPERMEMORY_AUTH_URL.replace(/\/$/, ""); + } + + const fileConfig = readFileConfig(); + if (fileConfig.authUrl) return fileConfig.authUrl; + + const apiUrl = + process.env.SUPERMEMORY_API_URL || + process.env.SUPERMEMORY_BASE_URL || + fileConfig.baseUrl || + loadCredentialData()?.apiBaseUrl; + + if (apiUrl) { + try { + const url = new URL(apiUrl); + if (isLocalHost(url.hostname)) { + const webPort = url.port === "8787" ? "3000" : url.port || "3000"; + return `${url.protocol}//${url.hostname}:${webPort}/auth/agent-connect`; + } + } catch {} + } + + return DEFAULT_AUTH_BASE_URL; +} + const AUTH_SUCCESS_HTML = ` Connected - Supermemory