ctx7 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -123,6 +123,7 @@ var __dirname = dirname(fileURLToPath(import.meta.url));
123
123
  var pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
124
124
  var VERSION = pkg.version;
125
125
  var NAME = pkg.name;
126
+ var CLI_CLIENT_ID = "2veBSofhicRBguUT";
126
127
 
127
128
  // src/utils/api.ts
128
129
  var baseUrl = "https://context7.com";
@@ -810,6 +811,39 @@ function isTokenExpired(tokens) {
810
811
  }
811
812
  return Date.now() > tokens.expires_at - 6e4;
812
813
  }
814
+ async function refreshAccessToken(refreshToken) {
815
+ const response = await fetch(`${getBaseUrl()}/api/oauth/token`, {
816
+ method: "POST",
817
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
818
+ body: new URLSearchParams({
819
+ grant_type: "refresh_token",
820
+ client_id: CLI_CLIENT_ID,
821
+ refresh_token: refreshToken
822
+ }).toString()
823
+ });
824
+ if (!response.ok) {
825
+ const err = await response.json().catch(() => ({}));
826
+ throw new Error(err.error_description || err.error || "Failed to refresh token");
827
+ }
828
+ return await response.json();
829
+ }
830
+ async function getValidAccessToken() {
831
+ const tokens = loadTokens();
832
+ if (!tokens) return null;
833
+ if (!isTokenExpired(tokens)) {
834
+ return tokens.access_token;
835
+ }
836
+ if (!tokens.refresh_token) {
837
+ return null;
838
+ }
839
+ try {
840
+ const newTokens = await refreshAccessToken(tokens.refresh_token);
841
+ saveTokens(newTokens);
842
+ return newTokens.access_token;
843
+ } catch {
844
+ return null;
845
+ }
846
+ }
813
847
  var CALLBACK_PORT = 52417;
814
848
  function createCallbackServer(expectedState) {
815
849
  let resolvePort;
@@ -953,7 +987,6 @@ function buildAuthorizationUrl(baseUrl3, clientId, redirectUri, codeChallenge, s
953
987
  import pc4 from "picocolors";
954
988
  import ora from "ora";
955
989
  import open from "open";
956
- var CLI_CLIENT_ID = "2veBSofhicRBguUT";
957
990
  var baseUrl2 = "https://context7.com";
958
991
  function setAuthBaseUrl(url) {
959
992
  baseUrl2 = url;
@@ -1029,18 +1062,13 @@ async function performLogin(openBrowser = true) {
1029
1062
  }
1030
1063
  async function loginCommand(options) {
1031
1064
  trackEvent("command", { name: "login" });
1032
- const existingTokens = loadTokens();
1033
- if (existingTokens) {
1034
- const expired = isTokenExpired(existingTokens);
1035
- if (!expired || existingTokens.refresh_token) {
1036
- console.log(pc4.yellow("You are already logged in."));
1037
- console.log(
1038
- pc4.dim("Run 'ctx7 logout' first if you want to log in with a different account.")
1039
- );
1040
- return;
1041
- }
1042
- clearTokens();
1065
+ const existingToken = await getValidAccessToken();
1066
+ if (existingToken) {
1067
+ console.log(pc4.yellow("You are already logged in."));
1068
+ console.log(pc4.dim("Run 'ctx7 logout' first if you want to log in with a different account."));
1069
+ return;
1043
1070
  }
1071
+ clearTokens();
1044
1072
  const token = await performLogin(options.browser);
1045
1073
  if (!token) {
1046
1074
  process.exit(1);
@@ -1058,29 +1086,30 @@ function logoutCommand() {
1058
1086
  }
1059
1087
  async function whoamiCommand() {
1060
1088
  trackEvent("command", { name: "whoami" });
1061
- const tokens = loadTokens();
1062
- if (!tokens) {
1089
+ const accessToken = await getValidAccessToken();
1090
+ if (!accessToken) {
1063
1091
  console.log(pc4.yellow("Not logged in."));
1064
1092
  console.log(pc4.dim("Run 'ctx7 login' to authenticate."));
1065
1093
  return;
1066
1094
  }
1067
1095
  console.log(pc4.green("Logged in"));
1068
1096
  try {
1069
- const userInfo = await fetchUserInfo(tokens.access_token);
1070
- if (userInfo.name) {
1071
- console.log(`${pc4.dim("Name:".padEnd(9))}${userInfo.name}`);
1097
+ const whoami = await fetchWhoami(accessToken);
1098
+ if (whoami.name) {
1099
+ console.log(`${pc4.dim("Name:".padEnd(13))}${whoami.name}`);
1072
1100
  }
1073
- if (userInfo.email) {
1074
- console.log(`${pc4.dim("Email:".padEnd(9))}${userInfo.email}`);
1101
+ if (whoami.email) {
1102
+ console.log(`${pc4.dim("Email:".padEnd(13))}${whoami.email}`);
1075
1103
  }
1076
- } catch {
1077
- if (isTokenExpired(tokens) && !tokens.refresh_token) {
1078
- console.log(pc4.dim("(Session may be expired - run 'ctx7 login' to refresh)"));
1104
+ if (whoami.teamspace) {
1105
+ console.log(`${pc4.dim("Teamspace:".padEnd(13))}${whoami.teamspace.name}`);
1079
1106
  }
1107
+ } catch {
1108
+ console.log(pc4.dim("(Session may be expired - run 'ctx7 login' to refresh)"));
1080
1109
  }
1081
1110
  }
1082
- async function fetchUserInfo(accessToken) {
1083
- const response = await fetch("https://clerk.context7.com/oauth/userinfo", {
1111
+ async function fetchWhoami(accessToken) {
1112
+ const response = await fetch(`${getBaseUrl()}/api/dashboard/whoami`, {
1084
1113
  headers: {
1085
1114
  Authorization: `Bearer ${accessToken}`
1086
1115
  }
@@ -2584,8 +2613,7 @@ function registerSetupCommand(program2) {
2584
2613
  });
2585
2614
  }
2586
2615
  async function authenticateAndGenerateKey() {
2587
- const existingTokens = loadTokens();
2588
- const accessToken = existingTokens && !isTokenExpired(existingTokens) ? existingTokens.access_token : await performLogin();
2616
+ const accessToken = await getValidAccessToken() ?? await performLogin();
2589
2617
  if (!accessToken) return null;
2590
2618
  const spinner = ora4("Configuring authentication...").start();
2591
2619
  try {
@@ -2625,15 +2653,15 @@ async function resolveMode(options) {
2625
2653
  return select3({
2626
2654
  message: "How should your agent access Context7?",
2627
2655
  choices: [
2628
- {
2629
- name: `CLI + Skills
2630
- ${pc8.dim("Installs a find-docs skill that guides your agent to fetch up-to-date library docs using ")}${pc8.dim(pc8.bold("ctx7"))}${pc8.dim(" CLI commands")}`,
2631
- value: "cli"
2632
- },
2633
2656
  {
2634
2657
  name: `MCP server
2635
2658
  ${pc8.dim("Agent calls Context7 tools via MCP protocol to retrieve up-to-date library docs")}`,
2636
2659
  value: "mcp"
2660
+ },
2661
+ {
2662
+ name: `CLI + Skills
2663
+ ${pc8.dim("Installs a find-docs skill that guides your agent to fetch up-to-date library docs using ")}${pc8.dim(pc8.bold("ctx7"))}${pc8.dim(" CLI commands")}`,
2664
+ value: "cli"
2637
2665
  }
2638
2666
  ],
2639
2667
  theme: {
@@ -2651,8 +2679,8 @@ async function resolveCliAuth(apiKey) {
2651
2679
  log.plain(`${pc8.green("\u2714")} Authenticated`);
2652
2680
  return;
2653
2681
  }
2654
- const existingTokens = loadTokens();
2655
- if (existingTokens && !isTokenExpired(existingTokens)) {
2682
+ const validToken = await getValidAccessToken();
2683
+ if (validToken) {
2656
2684
  log.blank();
2657
2685
  log.plain(`${pc8.green("\u2714")} Authenticated`);
2658
2686
  return;
@@ -2798,6 +2826,7 @@ async function setupMcp(agents2, options, scope) {
2798
2826
  }
2799
2827
  log.blank();
2800
2828
  trackEvent("setup", { agents: agents2, scope, authMode: auth.mode });
2829
+ trackEvent("install", { skills: ["/upstash/context7/context7-mcp"], ides: agents2 });
2801
2830
  }
2802
2831
  async function setupCli(options) {
2803
2832
  await resolveCliAuth(options.apiKey);
@@ -2835,6 +2864,7 @@ async function setupCli(options) {
2835
2864
  log.plain(` Ask your agent: ${pc8.cyan(`"Use ctx7 CLI to look up React hooks"`)}`);
2836
2865
  log.blank();
2837
2866
  trackEvent("setup", { mode: "cli" });
2867
+ trackEvent("install", { skills: ["/upstash/context7/find-docs"], ides: targets.ides });
2838
2868
  }
2839
2869
  async function setupCommand(options) {
2840
2870
  trackEvent("command", { name: "setup" });