chore: remove two dead onboarding routes and a note-content console.log (#1596)

Cherry-picks two cleanup PRs and finishes the job. Net 262 deletions.

- #1473 (@abhay-codes07): drops a `console.log` in the fullscreen note editor that printed the whole note body on every keystroke, which PostHog session replay can capture.
- #1563 (@ishaanxgupta): removes `/api/onboarding/research` and `/api/onboarding/extract-content`. Neither has a caller anywhere in the repo, and both spent metered Exa and xAI quota. This reverts the guards added for them in #1589, which only existed to make unreachable code safe.
- On top: `EXA_API_KEY`, `XAI_API_KEY` and the `@ai-sdk/xai` dependency are removed, since deleting those routes left them with no consumer.

Co-Authored-By: abhay-codes07 <182421137+abhay-codes07@users.noreply.github.com>
Co-Authored-By: ishaanxgupta <124028055+ishaanxgupta@users.noreply.github.com>
This commit is contained in:
MaheshtheDev 2026-08-25 10:19:47 +00:00
parent e4afc770be
commit 3f7b9667c6
6 changed files with 3 additions and 262 deletions

View file

@ -1,5 +1,3 @@
NEXT_PUBLIC_BACKEND_URL=https://api.supermemory.ai
NEXT_PUBLIC_POSTHOG_KEY=
EXA_API_KEY=
XAI_API_KEY=
NEXT_PUBLIC_AGENTID_AUTH_ENABLED=

View file

@ -1,114 +0,0 @@
import { hasVerifiedSession } from "@/lib/verify-session"
export interface ExaContentResult {
url: string
text: string
title: string
author?: string
}
interface ExaApiResponse {
results: ExaContentResult[]
}
const exaApiKey = process.env.EXA_API_KEY
if (!exaApiKey) {
console.error(
"EXA_API_KEY is not configured; /api/onboarding/extract-content will return 503",
)
}
function parseHttpUrl(value: string): URL | null {
try {
const url = new URL(value)
return url.protocol === "http:" || url.protocol === "https:" ? url : null
} catch {
return null
}
}
export async function POST(request: Request) {
try {
if (!(await hasVerifiedSession(request))) {
return Response.json({ error: "Unauthorized" }, { status: 401 })
}
if (!exaApiKey) {
return Response.json(
{ error: "Content extraction is unavailable" },
{ status: 503 },
)
}
const { urls } = await request.json()
if (!Array.isArray(urls) || urls.length === 0) {
return Response.json(
{ error: "Invalid input: urls must be a non-empty array" },
{ status: 400 },
)
}
const MAX_URLS = 10
if (urls.length > MAX_URLS) {
return Response.json(
{ error: `Invalid input: at most ${MAX_URLS} urls per request` },
{ status: 400 },
)
}
const invalid = Response.json(
{
error:
"Invalid input: all urls must be http(s) strings of at most 2048 characters",
},
{ status: 400 },
)
const normalizedUrls: string[] = []
const seen = new Set<string>()
for (const url of urls) {
if (typeof url !== "string" || !url.trim() || url.length > 2048) {
return invalid
}
const parsed = parseHttpUrl(url.trim())
if (!parsed) {
return invalid
}
if (seen.has(parsed.href)) continue
seen.add(parsed.href)
normalizedUrls.push(parsed.href)
}
const response = await fetch("https://api.exa.ai/contents", {
method: "POST",
headers: {
"x-api-key": exaApiKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
urls: normalizedUrls,
text: true,
livecrawl: "fallback",
}),
})
if (!response.ok) {
console.error(
"Exa API request failed:",
response.status,
response.statusText,
)
return Response.json(
{ error: "Failed to fetch content from Exa API" },
{ status: 500 },
)
}
const data: ExaApiResponse = await response.json()
return Response.json({ results: data.results })
} catch (error) {
console.error("Exa API request error:", error)
return Response.json({ error: "Internal server error" }, { status: 500 })
}
}

View file

@ -1,132 +0,0 @@
import { xai } from "@ai-sdk/xai"
import { generateText } from "ai"
import { hasVerifiedSession } from "@/lib/verify-session"
interface ResearchRequest {
xUrl: string
name?: string
email?: string
}
const ALLOWED_X_HOSTS: ReadonlySet<string> = new Set([
"x.com",
"www.x.com",
"twitter.com",
"www.twitter.com",
"mobile.twitter.com",
])
const X_URL_FALLBACK_REGEX =
/^(?:https?:\/\/)?(?:x\.com|www\.x\.com|twitter\.com|www\.twitter\.com|mobile\.twitter\.com)\/([^/\s?#]+)/i
// Each value occupies one prompt line, so collapse whitespace or it can forge extra lines.
function sanitizeContextField(value: unknown): string {
return typeof value === "string" ? value.replace(/\s+/g, " ").trim() : ""
}
function isXHost(hostname: string): boolean {
return ALLOWED_X_HOSTS.has(hostname.toLowerCase())
}
function extractHandle(input: string): string {
const trimmed = input.trim()
if (!trimmed) return ""
let handle = trimmed.replace(/^@+/, "")
const lower = handle.toLowerCase()
if (lower.includes("x.com") || lower.includes("twitter.com")) {
try {
const parsed = new URL(
handle.startsWith("http://") || handle.startsWith("https://")
? handle
: `https://${handle}`,
)
handle = isXHost(parsed.hostname)
? (parsed.pathname.split("/").filter(Boolean)[0] ?? "")
: ""
} catch {
handle = handle.match(X_URL_FALLBACK_REGEX)?.[1] ?? ""
}
}
return handle.replace(/^@+/, "").split(/[/?#]/)[0]?.toLowerCase() ?? ""
}
function finalPrompt(handle: string, userContext: string) {
return `You are researching a user based on their X/Twitter profile to help personalize their experience.
X Handle: @${handle}${userContext}
Please analyze this X/Twitter profile and provide a comprehensive but concise summary of the user. Include:
- Professional background and current role (if available)
- Key interests and topics they engage with
- Notable projects, achievements, or affiliations
- Their expertise areas
- Any other relevant information that helps understand who they are
Format the response as clear, readable paragraphs. Focus on factual information from their profile. If certain information is not available, skip that section rather than speculating.`
}
export async function POST(req: Request) {
try {
if (!(await hasVerifiedSession(req))) {
return Response.json({ error: "Unauthorized" }, { status: 401 })
}
const { xUrl, name, email }: ResearchRequest = await req.json()
if (!xUrl?.trim()) {
return Response.json(
{ error: "X/Twitter URL or handle is required" },
{ status: 400 },
)
}
if (
(name !== undefined && (typeof name !== "string" || name.length > 200)) ||
(email !== undefined && (typeof email !== "string" || email.length > 320))
) {
return Response.json(
{ error: "Invalid input: name/email too long" },
{ status: 400 },
)
}
const handle = extractHandle(xUrl)
if (!/^[A-Za-z0-9_]{1,15}$/.test(handle)) {
return Response.json(
{ error: "Could not parse a valid X/Twitter handle from the input" },
{ status: 400 },
)
}
const safeName = sanitizeContextField(name)
const safeEmail = sanitizeContextField(email)
const contextParts: string[] = []
if (safeName) contextParts.push(`Name: ${safeName}`)
if (safeEmail) contextParts.push(`Email: ${safeEmail}`)
const userContext =
contextParts.length > 0
? `\n\nAdditional context about the user:\n${contextParts.join("\n")}`
: ""
const { text } = await generateText({
model: xai.responses("grok-4-fast"),
prompt: finalPrompt(handle, userContext),
abortSignal: AbortSignal.timeout(60_000),
tools: {
web_search: xai.tools.webSearch(),
x_search: xai.tools.xSearch({
allowedXHandles: [handle],
}),
},
})
return Response.json({ text })
} catch (error) {
console.error("Research API error:", error)
return Response.json({ error: "Internal server error" }, { status: 500 })
}
}

View file

@ -61,7 +61,6 @@ export function FullscreenNoteModal({
const handleContentChange = useCallback(
(newContent: string) => {
console.log("handleContentChange", newContent)
setContent(newContent)
setDraft(newContent)
},

View file

@ -23,7 +23,6 @@
"dependencies": {
"@ai-sdk/google": "^3.0.64",
"@ai-sdk/react": "^3.0.170",
"@ai-sdk/xai": "^3.0.83",
"@better-fetch/fetch": "^1.1.18",
"@cloudflare/ai-chat": "^0.0.7",
"@dnd-kit/core": "^6.3.1",

View file

@ -148,7 +148,6 @@
"dependencies": {
"@ai-sdk/google": "^3.0.64",
"@ai-sdk/react": "^3.0.170",
"@ai-sdk/xai": "^3.0.83",
"@better-fetch/fetch": "^1.1.18",
"@cloudflare/ai-chat": "^0.0.7",
"@dnd-kit/core": "^6.3.1",
@ -470,7 +469,7 @@
"@ai-sdk/vercel": ["@ai-sdk/vercel@2.0.39", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.37", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8eu3ljJpkCTP4ppcyYB+NcBrkcBoSOFthCSgk5VnjaxnDaOJFaxnPwfddM7wx3RwMk2CiK1O61Px/LlqNc7QkQ=="],
"@ai-sdk/xai": ["@ai-sdk/xai@3.0.83", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-SuQz68BZGeuZjrSUJAzku97IlhdiNJJBsvG/Tvm3K2tuxkBS7TJq0fH4/AzAM7w2H2jxVUgboP7kRR6IfpRxcg=="],
"@ai-sdk/xai": ["@ai-sdk/xai@3.0.67", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.35", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-KQQIDc91dUA5IGFMnXBuvPBeraYNTdpDC1qUS+JG8vE+/299//5sZFafI1kKYUu3f3p7LaZrKXYgZ1Ni7QIRbw=="],
"@aihubmix/ai-sdk-provider": ["@aihubmix/ai-sdk-provider@1.0.3", "", { "dependencies": { "@ai-sdk/anthropic": "^3.0.0", "@ai-sdk/google": "^3.0.0", "@ai-sdk/openai": "^3.0.0", "@ai-sdk/provider": "^3.0.0", "@ai-sdk/provider-utils": "^4.0.0", "zod": "3.25.76" } }, "sha512-6YSu/3rLkPlY7fqSHTwq6LMiK+0BLVZsbsi/28w+og2MrpUYPNaBhkj7F80K79NE28+HFOqIJROFgAUFmaoh6w=="],
@ -5118,11 +5117,11 @@
"@ai-sdk/vercel/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="],
"@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
"@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.35", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-g3wA57IAQFb+3j4YuFndgkUdXyRETZVvbfAWM+UX7bZSxA3xjes0v3XKgIdKdekPtDGsh4ZX2byHD0gJIMPfiA=="],
"@ai-sdk/xai/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
"@ai-sdk/xai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="],
"@ai-sdk/xai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.19", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-3eG55CrSWCu2SXlqq2QCsFjo3+E7+Gmg7i/oRVoSZzIodTuDSfLb3MRje67xE9RFea73Zao7Lm4mADIfUETKGg=="],
"@aihubmix/ai-sdk-provider/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.58", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/53SACgmVukO4bkms4dpxpRlYhW8Ct6QZRe6sj1Pi5H00hYhxIrqfiLbZBGxkdRvjsBQeP/4TVGsXgH5rQeb8Q=="],
@ -5578,8 +5577,6 @@
"@voltagent/core/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="],
"@voltagent/core/@ai-sdk/xai": ["@ai-sdk/xai@3.0.67", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.35", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-KQQIDc91dUA5IGFMnXBuvPBeraYNTdpDC1qUS+JG8vE+/299//5sZFafI1kKYUu3f3p7LaZrKXYgZ1Ni7QIRbw=="],
"@voltagent/core/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="],
"@voltagent/core/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.204.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DqxY8yoAaiBPivoJD4UtgrMS8gEmzZ5lnaxzPojzLVHBGqPxgWm4zcuvcUHZiqQ6kRX2Klel2r9y8cA2HAtqpw=="],
@ -6632,10 +6629,6 @@
"@voltagent/core/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@voltagent/core/@ai-sdk/xai/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
"@voltagent/core/@ai-sdk/xai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.19", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-3eG55CrSWCu2SXlqq2QCsFjo3+E7+Gmg7i/oRVoSZzIodTuDSfLb3MRje67xE9RFea73Zao7Lm4mADIfUETKGg=="],
"@voltagent/core/@modelcontextprotocol/sdk/express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
"@voltagent/core/@modelcontextprotocol/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
@ -7288,8 +7281,6 @@
"@voltagent/core/@ai-sdk/openai/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@voltagent/core/@ai-sdk/xai/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@voltagent/core/@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
"@voltagent/core/@modelcontextprotocol/sdk/express/body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],