UNPKG

ctx7

Version:

Context7 CLI - Fetch documentation context and configure Context7

869 lines (856 loc) • 27.9 kB
#!/usr/bin/env node // src/commands/auth.ts import pc2 from "picocolors"; import ora from "ora"; import open from "open"; import boxen from "boxen"; // src/utils/auth.ts import * as fs3 from "fs"; import * as os2 from "os"; import pc from "picocolors"; import { confirm } from "@inquirer/prompts"; // src/constants.ts import { readFileSync } from "fs"; import { fileURLToPath } from "url"; import { dirname, join } from "path"; var __dirname = dirname(fileURLToPath(import.meta.url)); var pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8")); var VERSION = pkg.version; var NAME = pkg.name; var CLI_CLIENT_ID = "2veBSofhicRBguUT"; // src/utils/api.ts var DEFAULT_CONTEXT7_BASE_URL = "https://context7.com"; var baseUrl = DEFAULT_CONTEXT7_BASE_URL; function getBaseUrl() { return baseUrl; } function setBaseUrl(url) { baseUrl = url; } function getAuthHeaders(accessToken) { const headers = { "X-Context7-Source": "cli", "X-Context7-Client-IDE": "ctx7-cli", "X-Context7-Client-Version": VERSION, "X-Context7-Transport": "cli" }; const apiKey = process.env.CONTEXT7_API_KEY; if (apiKey) { headers["Authorization"] = `Bearer ${apiKey}`; } else if (accessToken) { headers["Authorization"] = `Bearer ${accessToken}`; } return headers; } async function resolveLibrary(libraryName, query, accessToken) { const params = new URLSearchParams({ libraryName }); if (query) { params.set("query", query); } const response = await fetch(`${baseUrl}/api/v2/libs/search?${params}`, { headers: getAuthHeaders(accessToken) }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); return { results: [], error: errorData.error || `HTTP error ${response.status}`, message: errorData.message }; } return await response.json(); } var CODE_OPERATIONS = [ "grep", "definition", "references", "outline", "read", "list" ]; var CODE_LIMITS = { context: { min: 0, max: 10 }, headLimit: { min: 1, max: 500 }, offset: { min: 0, max: 1e4 } }; function hasSourceCode(result) { return !/^\/(websites|llmstxt)\//.test(result.id) && !result.branch?.startsWith("_"); } function retryAfterSeconds(bodyValue, header) { if (typeof bodyValue === "number" && Number.isFinite(bodyValue) && bodyValue >= 0) return Math.ceil(bodyValue); if (header) { const seconds = Number(header); if (Number.isFinite(seconds) && seconds >= 0) return Math.ceil(seconds); const date = Date.parse(header); if (Number.isFinite(date)) return Math.max(1, Math.ceil((date - Date.now()) / 1e3)); } return 30; } async function getLibraryCode(libraryId, pattern, options = {}, accessToken) { const params = new URLSearchParams({ libraryId }); if (options.version) params.set("version", options.version); if (pattern) params.set("pattern", pattern); if (options.operation) params.set("operation", options.operation); if (options.fixedStrings) params.set("fixed_strings", "true"); if (options.ignoreCase) params.set("ignore_case", "true"); for (const glob of options.glob ?? []) params.append("glob", glob); if (options.path) params.set("path", options.path); if (options.context !== void 0) params.set("context", String(options.context)); if (options.outputMode) params.set("output_mode", options.outputMode); if (options.headLimit) params.set("head_limit", String(options.headLimit)); if (options.offset) params.set("offset", String(options.offset)); if (options.json) params.set("format", "json"); const response = await fetch(`${baseUrl}/api/v2/code?${params}`, { headers: getAuthHeaders(accessToken) }); if (response.status === 202) { const body = await response.json().catch(() => ({})); return { kind: "indexing", message: body.message ?? "The library source is being indexed.", retryAfter: retryAfterSeconds(body.retryAfter, response.headers.get("Retry-After")) }; } if (!response.ok) { const body = await response.json().catch(() => ({})); return { kind: "error", error: body.error || `HTTP error ${response.status}`, message: body.message }; } return { kind: "result", body: await response.text() }; } async function getLibraryContext(libraryId, query, options, accessToken) { const params = new URLSearchParams({ libraryId, query }); if (options?.type) { params.set("type", options.type); } if (options?.language) { params.set("language", options.language); } const headers = getAuthHeaders(accessToken); const response = await fetch(`${baseUrl}/api/v2/context?${params}`, { headers }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); if (response.status === 301 && errorData.redirectUrl) { return { codeSnippets: [], infoSnippets: [], error: errorData.error || "library_redirected", message: errorData.message, redirectUrl: errorData.redirectUrl }; } return { codeSnippets: [], infoSnippets: [], error: errorData.error || `HTTP error ${response.status}`, message: errorData.message }; } if (options?.type === "txt") { return await response.text(); } return await response.json(); } async function searchDocumentation(query, options, accessToken) { const params = new URLSearchParams({ query, type: options.type }); for (const library of options.libraries ?? []) { params.append("library", library); } if (options.version) { params.set("version", options.version); } if (options.language) { params.set("language", options.language); } const response = await fetch(`${baseUrl}/api/v3/search?${params}`, { headers: getAuthHeaders(accessToken) }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.message || errorData.error || `HTTP error ${response.status}`); } return options.type === "txt" ? response.text() : response.json(); } // src/utils/file-lock.ts import * as fs from "fs"; var DEFAULT_LOCK_STALE_MS = 1e4; var DEFAULT_LOCK_POLL_MS = 50; function errorCode(error) { if (typeof error !== "object" || error === null || !("code" in error)) return void 0; const code = error.code; return typeof code === "string" ? code : void 0; } function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } async function acquireFileLock(lockPath, options = {}) { const staleMs = options.staleMs ?? DEFAULT_LOCK_STALE_MS; const pollMs = options.pollMs ?? DEFAULT_LOCK_POLL_MS; for (; ; ) { try { fs.closeSync(fs.openSync(lockPath, "wx", 384)); return holdLock(lockPath, staleMs); } catch (error) { if (errorCode(error) !== "EEXIST") return null; } let mtimeMs; try { mtimeMs = fs.statSync(lockPath).mtimeMs; } catch (error) { if (errorCode(error) === "ENOENT") continue; return null; } if (Date.now() - mtimeMs > staleMs) { try { fs.unlinkSync(lockPath); } catch (error) { if (errorCode(error) !== "ENOENT") return null; } continue; } await sleep(pollMs); } } function holdLock(lockPath, staleMs) { const heartbeat = setInterval( () => { try { const now = /* @__PURE__ */ new Date(); fs.utimesSync(lockPath, now, now); } catch { } }, Math.max(1, Math.floor(staleMs / 3)) ); heartbeat.unref(); return () => { clearInterval(heartbeat); try { fs.unlinkSync(lockPath); } catch { } }; } // src/utils/storage-paths.ts import * as fs2 from "fs"; import { access, chmod, mkdir, rename } from "fs/promises"; import * as os from "os"; import * as path from "path"; var APP_DIR = "context7"; var LEGACY_DIR = ".context7"; var CREDENTIALS_FILE_NAME = "credentials.json"; var UPDATE_STATE_FILE_NAME = "cli-state.json"; function xdgBase(envVar, ...defaultSegments) { const value = process.env[envVar]; const base = value && path.isAbsolute(value) ? value : path.join(os.homedir(), ...defaultSegments); return path.join(base, APP_DIR); } function getConfigDir() { return xdgBase("XDG_CONFIG_HOME", ".config"); } function getStateDir() { return xdgBase("XDG_STATE_HOME", ".local", "state"); } function getCredentialsFilePath() { return path.join(getConfigDir(), CREDENTIALS_FILE_NAME); } function getUpdateStateFilePath() { return path.join(getStateDir(), UPDATE_STATE_FILE_NAME); } function getLegacyFilePath(fileName) { return path.join(os.homedir(), LEGACY_DIR, fileName); } function migrateLegacyFileSync(fileName, targetPath, mode) { const legacyPath = getLegacyFilePath(fileName); if (legacyPath === targetPath || fs2.existsSync(targetPath) || !fs2.existsSync(legacyPath)) { return; } try { fs2.mkdirSync(path.dirname(targetPath), { recursive: true, mode: 448 }); fs2.renameSync(legacyPath, targetPath); if (mode !== void 0) { fs2.chmodSync(targetPath, mode); } } catch { } } async function migrateLegacyFile(fileName, targetPath, mode) { const legacyPath = getLegacyFilePath(fileName); if (legacyPath === targetPath || await exists(targetPath) || !await exists(legacyPath)) { return; } try { await mkdir(path.dirname(targetPath), { recursive: true, mode: 448 }); await rename(legacyPath, targetPath); if (mode !== void 0) { await chmod(targetPath, mode); } } catch { } } function resolveReadPathSync(fileName, targetPath, mode) { migrateLegacyFileSync(fileName, targetPath, mode); if (fs2.existsSync(targetPath)) { return targetPath; } const legacyPath = getLegacyFilePath(fileName); return fs2.existsSync(legacyPath) ? legacyPath : targetPath; } async function resolveReadPath(fileName, targetPath, mode) { await migrateLegacyFile(fileName, targetPath, mode); if (await exists(targetPath)) { return targetPath; } const legacyPath = getLegacyFilePath(fileName); return await exists(legacyPath) ? legacyPath : targetPath; } async function exists(filePath) { try { await access(filePath); return true; } catch { return false; } } // src/utils/auth.ts var CONTEXT7_API_KEY_PREFIX = "ctx7sk-"; function isContext7ApiKey(accessToken) { return accessToken.startsWith(CONTEXT7_API_KEY_PREFIX); } function ensureConfigDir() { const configDir = getConfigDir(); if (!fs3.existsSync(configDir)) { fs3.mkdirSync(configDir, { recursive: true, mode: 448 }); } } var CREDENTIALS_MODE = 384; function saveTokens(tokens) { const credentialsFile = getCredentialsFilePath(); migrateLegacyFileSync(CREDENTIALS_FILE_NAME, credentialsFile, CREDENTIALS_MODE); ensureConfigDir(); const data = { ...tokens, expires_at: tokens.expires_at ?? (tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0) }; fs3.writeFileSync(credentialsFile, JSON.stringify(data, null, 2), { mode: CREDENTIALS_MODE }); fs3.chmodSync(credentialsFile, CREDENTIALS_MODE); } function loadTokens() { const credentialsFile = resolveReadPathSync( CREDENTIALS_FILE_NAME, getCredentialsFilePath(), CREDENTIALS_MODE ); if (!fs3.existsSync(credentialsFile)) { return null; } try { const data = JSON.parse(fs3.readFileSync(credentialsFile, "utf-8")); return data; } catch { return null; } } function clearTokens() { const credentialsFile = getCredentialsFilePath(); let removed = false; if (fs3.existsSync(credentialsFile)) { fs3.unlinkSync(credentialsFile); removed = true; } const legacyCredentialsFile = getLegacyFilePath(CREDENTIALS_FILE_NAME); if (fs3.existsSync(legacyCredentialsFile)) { fs3.unlinkSync(legacyCredentialsFile); removed = true; } return removed; } function isTokenExpired(tokens) { if (!tokens.expires_at) { return false; } return Date.now() > tokens.expires_at - 6e4; } var RefreshTokenRejectedError = class extends Error { }; var DEFINITIVE_REFRESH_ERRORS = /* @__PURE__ */ new Set(["invalid_grant", "invalid_client"]); async function refreshAccessToken(refreshToken) { const response = await postForm( `${getBaseUrl()}/api/oauth/token`, new URLSearchParams({ grant_type: "refresh_token", client_id: CLI_CLIENT_ID, refresh_token: refreshToken }) ); if (response.ok) { return await response.json(); } const body = await response.text().catch(() => ""); if (response.status < 500 && response.status !== 429) { try { const err = JSON.parse(body); if (err.error && DEFINITIVE_REFRESH_ERRORS.has(err.error)) { throw new RefreshTokenRejectedError(err.error_description || err.error); } } catch (error) { if (error instanceof RefreshTokenRejectedError) throw error; } } throw new Error(`Failed to refresh token (HTTP ${response.status})`); } var SESSION_EXPIRED_NOTICE = "Your Context7 session has expired. Run ctx7 login to sign in again. Continuing without an account."; function isInteractiveSession() { if (!process.stdin.isTTY || !process.stdout.isTTY) return false; return !process.argv.includes("--json"); } async function recoverExpiredSession() { if (!isInteractiveSession()) { console.error(SESSION_EXPIRED_NOTICE); return void 0; } let signIn; try { signIn = await confirm({ message: "Your Context7 session has expired. Sign in again now?", default: true }); } catch (error) { if (error instanceof Error && error.name === "ExitPromptError") { process.stdout.write("\n"); process.exit(130); } signIn = false; } if (signIn) { const { performLogin: performLogin2 } = await import("./auth-TGX3XKCV.js"); const token = await performLogin2(); if (token) { console.log(""); return token; } } console.log(pc.dim("Continuing without an account. Run ctx7 login to sign in later.")); console.log(""); return void 0; } function getRefreshLockPath() { return `${getCredentialsFilePath()}.lock`; } async function acquireRefreshLock() { try { ensureConfigDir(); return await acquireFileLock(getRefreshLockPath()); } catch { return null; } } var REFRESH_REJECTED = Symbol("refresh-rejected"); async function refreshAndStore(refreshToken) { try { const newTokens = await refreshAccessToken(refreshToken); saveTokens({ refresh_token: refreshToken, ...newTokens }); return newTokens.access_token; } catch (error) { if (!(error instanceof RefreshTokenRejectedError)) return void 0; const current = loadTokens(); if (current && current.refresh_token !== refreshToken) { return isTokenExpired(current) ? void 0 : current.access_token; } clearTokens(); return REFRESH_REJECTED; } } async function getValidAccessToken(options = {}) { const tokens = loadTokens(); if (!tokens) return void 0; if (!isTokenExpired(tokens)) { return tokens.access_token; } if (!tokens.refresh_token) { return void 0; } let result; const release = await acquireRefreshLock(); try { const latest = loadTokens(); if (!latest) return void 0; if (!isTokenExpired(latest)) return latest.access_token; if (!latest.refresh_token) return void 0; result = await refreshAndStore(latest.refresh_token); } finally { release?.(); } if (result !== REFRESH_REJECTED) return result; if (options.recoverExpiredSession === false || process.env.CONTEXT7_API_KEY) { return void 0; } return recoverExpiredSession(); } var DEVICE_CODE_GRANT = "urn:ietf:params:oauth:grant-type:device_code"; async function describeErrorResponse(response, fallback) { const body = await response.text().catch(() => ""); try { const err = JSON.parse(body); const message = err.error_description || err.error; if (message) return message; } catch { } const excerpt = body.replace(/\s+/g, " ").trim().slice(0, 200); const detail = `HTTP ${response.status} from ${response.url}`; return excerpt ? `${fallback} (${detail}): ${excerpt}` : `${fallback} (${detail})`; } var TLS_HINT = "The TLS certificate could not be verified, which usually means a proxy is inspecting HTTPS traffic. Point NODE_EXTRA_CA_CERTS at your organization's root CA."; var DNS_HINT = "DNS lookup failed. Check your network or VPN connection."; var BLOCKED_HINT = "The connection was refused or reset, which usually means a firewall or proxy is blocking it."; var TIMEOUT_HINT = "The connection timed out. A proxy or firewall may be dropping the request."; var DEFAULT_HINT = "If you are behind a corporate proxy, note that Node does not use HTTPS_PROXY automatically."; var CONNECTION_HINTS = { UNABLE_TO_VERIFY_LEAF_SIGNATURE: TLS_HINT, SELF_SIGNED_CERT_IN_CHAIN: TLS_HINT, DEPTH_ZERO_SELF_SIGNED_CERT: TLS_HINT, CERT_HAS_EXPIRED: TLS_HINT, ENOTFOUND: DNS_HINT, EAI_AGAIN: DNS_HINT, ECONNREFUSED: BLOCKED_HINT, ECONNRESET: BLOCKED_HINT, EHOSTUNREACH: BLOCKED_HINT, ENETUNREACH: BLOCKED_HINT, UND_ERR_CONNECT_TIMEOUT: TIMEOUT_HINT, ETIMEDOUT: TIMEOUT_HINT }; function getErrorCause(error) { if (typeof error !== "object" || error === null || !("cause" in error)) return {}; const cause = error.cause; if (typeof cause !== "object" || cause === null) return {}; const { code, message } = cause; return { code: typeof code === "string" ? code : void 0, message: typeof message === "string" ? message : void 0 }; } function describeConnectionError(error, url) { const { code, message } = getErrorCause(error); const detail = message || (error instanceof Error ? error.message : String(error)); const hint = code && CONNECTION_HINTS[code] || DEFAULT_HINT; return `Could not reach ${url}: ${detail}${code ? ` (${code})` : ""} ${hint}`; } async function postForm(url, params) { try { return await fetch(url, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: params.toString() }); } catch (error) { throw new Error(describeConnectionError(error, url)); } } async function oauthRequest(url, params, fallback) { const response = await postForm(url, params); if (!response.ok) { throw new Error(await describeErrorResponse(response, fallback)); } return await response.json(); } var DEFAULT_DEVICE_POLL_INTERVAL_SECONDS = 5; async function startDeviceAuthorization(baseUrl2, clientId) { const params = new URLSearchParams({ client_id: clientId }); try { const hostname2 = os2.hostname(); if (hostname2) params.set("hostname", hostname2); } catch { } return oauthRequest( `${baseUrl2}/api/oauth/device/code`, params, "Failed to start device authorization" ); } async function pollDeviceToken(baseUrl2, clientId, deviceCode) { let response; try { response = await postForm( `${baseUrl2}/api/oauth/device/token`, new URLSearchParams({ grant_type: DEVICE_CODE_GRANT, device_code: deviceCode, client_id: clientId }) ); } catch (error) { return { status: "transient", errorMessage: error instanceof Error ? error.message : "network error" }; } if (response.ok) { const tokens = await response.json(); return { status: "approved", tokens }; } if (response.status >= 500) { const err2 = await response.json().catch(() => ({})); return { status: "transient", errorMessage: err2.error_description || err2.error || `HTTP ${response.status}` }; } const err = await response.json().catch(() => ({})); switch (err.error) { case "authorization_pending": return { status: "pending" }; case "slow_down": return { status: "slow_down" }; case "access_denied": return { status: "denied" }; case "expired_token": return { status: "expired" }; default: throw new Error(err.error_description || err.error || "Device token poll failed"); } } // src/utils/tracking.ts function trackEvent(event, data) { if (process.env.CTX7_TELEMETRY_DISABLED || getBaseUrl() !== DEFAULT_CONTEXT7_BASE_URL) return; fetch(`${getBaseUrl()}/api/v2/cli/events`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ event, data }) }).catch(() => { }); } // src/commands/auth.ts function registerAuthCommands(program) { program.command("login").description("Log in to Context7").option("--no-browser", "Don't open browser automatically").action(async (options) => { await loginCommand(options); }); program.command("logout").description("Log out of Context7").action(() => { logoutCommand(); }); program.command("whoami").description("Show current login status").action(async () => { await whoamiCommand(); }); } function renderDeviceCodeBox(userCode, verificationUri, verificationUriComplete) { const codeLine = `${pc2.dim("Your one-time code:")} ${pc2.green(pc2.bold(userCode))}`; const linkLine = verificationUriComplete ? `${pc2.dim("Open this link to approve:")} ${pc2.cyan(verificationUriComplete)} ${pc2.dim("Or visit")} ${pc2.cyan(verificationUri)} ${pc2.dim("and enter the code above.")}` : `${pc2.dim("Visit:")} ${pc2.cyan(verificationUri)}`; return boxen(`${codeLine} ${linkLine}`, { title: "Sign in to Context7", titleAlignment: "left", padding: 1, margin: { top: 1, bottom: 1, left: 2, right: 2 }, borderStyle: "round", borderColor: "gray" }); } function waitForEnter(prompt) { if (!process.stdin.isTTY) return Promise.resolve(); return new Promise((resolve) => { process.stdout.write(` ${pc2.dim(prompt)} `); const onData = (chunk) => { if (chunk[0] === 3) { process.stdin.removeListener("data", onData); process.stdin.setRawMode?.(false); process.stdin.pause(); process.stdout.write("\n"); process.exit(130); } process.stdin.removeListener("data", onData); process.stdin.setRawMode?.(false); process.stdin.pause(); process.stdout.write("\n"); resolve(); }; process.stdin.setRawMode?.(true); process.stdin.resume(); process.stdin.on("data", onData); }); } async function announceIdentity(accessToken) { if (isContext7ApiKey(accessToken)) return "Authenticated with API key"; try { const whoami = await fetchWhoami(accessToken); const name = whoami.email || whoami.name; if (!name) return "Login successful!"; const team = whoami.teamspace?.name; return team ? `Logged in as ${pc2.bold(name)} ${pc2.dim(`(${team})`)}` : `Logged in as ${pc2.bold(name)}`; } catch { return "Login successful!"; } } async function performLogin(openBrowser = true) { const baseUrl2 = getBaseUrl(); const spinner = ora("Preparing login...").start(); let authorization; try { authorization = await startDeviceAuthorization(baseUrl2, CLI_CLIENT_ID); } catch (error) { spinner.fail(pc2.red("Login failed")); if (error instanceof Error) console.error(pc2.red(error.message)); return null; } spinner.stop(); console.log( renderDeviceCodeBox( authorization.user_code, authorization.verification_uri, authorization.verification_uri_complete ) ); const target = authorization.verification_uri_complete ?? authorization.verification_uri; if (openBrowser) { await waitForEnter("Press Enter to open the browser, or Ctrl-C to quit..."); try { await open(target); } catch { console.log(pc2.dim(` Couldn't open a browser \u2014 visit the link above manually.`)); } } else { console.log(pc2.dim(" Open the link above in any browser to continue.")); console.log(""); } const waitingSpinner = ora({ text: "Waiting for authorization...", indent: 2 }).start(); const deadline = Date.now() + authorization.expires_in * 1e3; let intervalMs = (authorization.interval ?? DEFAULT_DEVICE_POLL_INTERVAL_SECONDS) * 1e3; while (Date.now() < deadline) { await new Promise((resolve) => setTimeout(resolve, intervalMs)); try { const result = await pollDeviceToken(baseUrl2, CLI_CLIENT_ID, authorization.device_code); if (result.status === "approved" && result.tokens) { saveTokens(result.tokens); const successText = await announceIdentity(result.tokens.access_token); waitingSpinner.succeed(pc2.green(successText)); return result.tokens.access_token; } if (result.status === "slow_down") { intervalMs += 5e3; continue; } if (result.status === "denied") { waitingSpinner.fail(pc2.red("Authorization denied.")); return null; } if (result.status === "expired") { waitingSpinner.fail(pc2.red("Code expired. Run login again.")); return null; } if (result.status === "transient") { intervalMs += 5e3; continue; } } catch (error) { waitingSpinner.fail(pc2.red("Login failed")); if (error instanceof Error) console.error(pc2.red(error.message)); return null; } } waitingSpinner.fail(pc2.red("Code expired without approval.")); return null; } async function loginCommand(options) { trackEvent("command", { name: "login" }); const existingToken = await getValidAccessToken({ recoverExpiredSession: false }); if (existingToken) { console.log(pc2.yellow("You are already logged in.")); console.log(pc2.dim("Run 'ctx7 logout' first if you want to log in with a different account.")); return; } clearTokens(); const token = await performLogin(options.browser); if (!token) { process.exit(1); } console.log(""); console.log(pc2.dim("You can now use authenticated Context7 features.")); } function logoutCommand() { trackEvent("command", { name: "logout" }); if (clearTokens()) { console.log(pc2.green("Logged out successfully.")); } else { console.log(pc2.yellow("You are not logged in.")); } } async function whoamiCommand() { trackEvent("command", { name: "whoami" }); const accessToken = await getValidAccessToken({ recoverExpiredSession: false }); if (!accessToken) { console.log(pc2.yellow("Not logged in.")); console.log(pc2.dim("Run 'ctx7 login' to authenticate.")); return; } console.log(pc2.green("Logged in")); if (isContext7ApiKey(accessToken)) { return; } try { const whoami = await fetchWhoami(accessToken); if (whoami.name) { console.log(`${pc2.dim("Name:".padEnd(13))}${whoami.name}`); } if (whoami.email) { console.log(`${pc2.dim("Email:".padEnd(13))}${whoami.email}`); } if (whoami.teamspace) { console.log(`${pc2.dim("Teamspace:".padEnd(13))}${whoami.teamspace.name}`); } } catch (error) { console.log( pc2.dim( error instanceof SessionRejectedError ? "Session was rejected by the server. Run 'ctx7 logout', then 'ctx7 login' to sign in again." : "Could not verify your session. Try again later." ) ); } } var SessionRejectedError = class extends Error { }; async function fetchWhoami(accessToken) { const response = await fetch(`${getBaseUrl()}/api/dashboard/whoami`, { headers: { Authorization: `Bearer ${accessToken}` } }); if (response.status === 401) { throw new SessionRejectedError("Session was rejected by the server"); } if (!response.ok) { throw new Error("Failed to fetch user info"); } return await response.json(); } export { VERSION, NAME, DEFAULT_CONTEXT7_BASE_URL, getBaseUrl, setBaseUrl, resolveLibrary, CODE_OPERATIONS, CODE_LIMITS, hasSourceCode, getLibraryCode, getLibraryContext, searchDocumentation, UPDATE_STATE_FILE_NAME, getUpdateStateFilePath, migrateLegacyFile, resolveReadPath, isContext7ApiKey, saveTokens, getValidAccessToken, trackEvent, registerAuthCommands, performLogin }; //# sourceMappingURL=chunk-F53NPYYA.js.map