md4ai 0.9.9 → 0.9.10

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.
@@ -887,20 +887,26 @@ var init_tooling_detector = __esm({
887
887
  import { readFile as readFile5 } from "node:fs/promises";
888
888
  import { join as join7 } from "node:path";
889
889
  import { homedir as homedir5 } from "node:os";
890
- async function resolveVercelToken() {
890
+ async function resolveVercelTokenWithSource() {
891
891
  const creds = await loadCredentials();
892
- if (creds?.vercelToken)
893
- return creds.vercelToken;
892
+ if (creds?.vercelToken) {
893
+ return {
894
+ token: creds.vercelToken,
895
+ source: "md4ai-credentials",
896
+ sourcePath: join7(homedir5(), ".md4ai", "credentials.json")
897
+ };
898
+ }
894
899
  const possiblePaths = [
895
- join7(homedir5(), ".config", "com.vercel.cli", "auth.json"),
896
- join7(homedir5(), ".local", "share", "com.vercel.cli", "auth.json")
900
+ { path: join7(homedir5(), ".config", "com.vercel.cli", "auth.json"), source: "vercel-cli-config" },
901
+ { path: join7(homedir5(), ".local", "share", "com.vercel.cli", "auth.json"), source: "vercel-cli-local-share" }
897
902
  ];
898
- for (const authPath of possiblePaths) {
903
+ for (const { path: authPath, source } of possiblePaths) {
899
904
  try {
900
905
  const data = await readFile5(authPath, "utf-8");
901
906
  const parsed = JSON.parse(data);
902
- if (parsed.token)
903
- return parsed.token;
907
+ if (parsed.token) {
908
+ return { token: parsed.token, source, sourcePath: authPath };
909
+ }
904
910
  } catch {
905
911
  }
906
912
  }
@@ -956,7 +962,21 @@ async function fetchVercelEnvVars(projectId, orgId, token) {
956
962
  });
957
963
  if (!res.ok) {
958
964
  const body = await res.text().catch(() => "");
959
- throw new Error(`Vercel API ${res.status}: ${body.slice(0, 200)}`);
965
+ let errorCode = null;
966
+ let invalidToken = false;
967
+ try {
968
+ const parsed = JSON.parse(body);
969
+ errorCode = parsed.error?.code ?? null;
970
+ invalidToken = parsed.error?.invalidToken === true;
971
+ } catch {
972
+ }
973
+ if (invalidToken || res.status === 401) {
974
+ throw new VercelApiError("Token is invalid or expired", res.status, errorCode, true);
975
+ }
976
+ if (res.status === 403) {
977
+ throw new VercelApiError("Token lacks permission for this project/team", res.status, errorCode, false);
978
+ }
979
+ throw new VercelApiError(`Vercel API ${res.status}: ${body.slice(0, 200)}`, res.status, errorCode, false);
960
980
  }
961
981
  const data = await res.json();
962
982
  const envs = data.envs ?? [];
@@ -968,9 +988,22 @@ async function fetchVercelEnvVars(projectId, orgId, token) {
968
988
  // populated later by the scanner
969
989
  }));
970
990
  }
991
+ var VercelApiError;
971
992
  var init_fetch_env_vars = __esm({
972
993
  "dist/vercel/fetch-env-vars.js"() {
973
994
  "use strict";
995
+ VercelApiError = class extends Error {
996
+ statusCode;
997
+ errorCode;
998
+ isInvalidToken;
999
+ constructor(message, statusCode, errorCode, isInvalidToken) {
1000
+ super(message);
1001
+ this.statusCode = statusCode;
1002
+ this.errorCode = errorCode;
1003
+ this.isInvalidToken = isInvalidToken;
1004
+ this.name = "VercelApiError";
1005
+ }
1006
+ };
974
1007
  }
975
1008
  });
976
1009
 
@@ -1012,18 +1045,48 @@ async function scanEnvManifest(projectRoot) {
1012
1045
  checkedAt: (/* @__PURE__ */ new Date()).toISOString()
1013
1046
  };
1014
1047
  }
1048
+ function tokenSourceLabel(source) {
1049
+ switch (source) {
1050
+ case "md4ai-credentials":
1051
+ return "md4ai credentials (~/.md4ai/credentials.json)";
1052
+ case "vercel-cli-config":
1053
+ return "Vercel CLI (~/.config/com.vercel.cli/auth.json)";
1054
+ case "vercel-cli-local-share":
1055
+ return "Vercel CLI (~/.local/share/com.vercel.cli/auth.json)";
1056
+ }
1057
+ }
1058
+ function tokenFixInstructions(source) {
1059
+ switch (source) {
1060
+ case "md4ai-credentials":
1061
+ return [
1062
+ "Generate a new token at https://vercel.com/account/tokens",
1063
+ "Then run: md4ai config set vercel-token <new-token>"
1064
+ ];
1065
+ case "vercel-cli-config":
1066
+ case "vercel-cli-local-share":
1067
+ return [
1068
+ "Re-authenticate the Vercel CLI: npx vercel login",
1069
+ "Ensure you log into the correct team account",
1070
+ "Or set a dedicated token: md4ai config set vercel-token <token>"
1071
+ ];
1072
+ }
1073
+ }
1015
1074
  async function checkVercelEnvVars(projectRoot, variables) {
1016
- const token = await resolveVercelToken();
1017
- if (!token) {
1075
+ const tokenResult = await resolveVercelTokenWithSource();
1076
+ if (!tokenResult) {
1018
1077
  console.log(chalk8.dim(" Vercel checks skipped (no token configured)"));
1078
+ console.log(chalk8.dim(" To enable: md4ai config set vercel-token <token>"));
1079
+ console.log(chalk8.dim(" Generate a token at https://vercel.com/account/tokens"));
1019
1080
  return null;
1020
1081
  }
1082
+ const { token, source } = tokenResult;
1021
1083
  const projects = await discoverVercelProjects(projectRoot);
1022
1084
  if (projects.length === 0)
1023
1085
  return null;
1024
1086
  console.log(chalk8.dim(` Checking ${projects.length} Vercel project(s)...`));
1025
1087
  const discovered = [];
1026
1088
  const manifestVarNames = new Set(variables.map((v) => v.name));
1089
+ let tokenErrorShown = false;
1027
1090
  for (const proj of projects) {
1028
1091
  try {
1029
1092
  const vars = await fetchVercelEnvVars(proj.projectId, proj.orgId, token);
@@ -1044,7 +1107,22 @@ async function checkVercelEnvVars(projectRoot, variables) {
1044
1107
  }
1045
1108
  console.log(chalk8.dim(` ${proj.projectName}: ${vars.length} var(s)`));
1046
1109
  } catch (err) {
1047
- console.log(chalk8.yellow(` ${proj.projectName}: ${err instanceof Error ? err.message : "API error"}`));
1110
+ if (err instanceof VercelApiError && err.isInvalidToken && !tokenErrorShown) {
1111
+ tokenErrorShown = true;
1112
+ console.log(chalk8.red(` ${proj.projectName}: Token is invalid or expired`));
1113
+ console.log(chalk8.yellow(` Token source: ${tokenSourceLabel(source)}`));
1114
+ console.log(chalk8.yellow(" To fix:"));
1115
+ for (const step of tokenFixInstructions(source)) {
1116
+ console.log(chalk8.yellow(` \u2192 ${step}`));
1117
+ }
1118
+ } else if (err instanceof VercelApiError && err.statusCode === 403) {
1119
+ console.log(chalk8.yellow(` ${proj.projectName}: ${err.message}`));
1120
+ console.log(chalk8.dim(` Token source: ${tokenSourceLabel(source)}`));
1121
+ console.log(chalk8.dim(" Ensure the token has access to this team/project"));
1122
+ console.log(chalk8.dim(" Check: https://vercel.com/account/tokens"));
1123
+ } else {
1124
+ console.log(chalk8.yellow(` ${proj.projectName}: ${err instanceof Error ? err.message : "API error"}`));
1125
+ }
1048
1126
  }
1049
1127
  }
1050
1128
  return discovered.length > 0 ? discovered : null;
@@ -1645,7 +1723,7 @@ var CURRENT_VERSION;
1645
1723
  var init_check_update = __esm({
1646
1724
  "dist/check-update.js"() {
1647
1725
  "use strict";
1648
- CURRENT_VERSION = true ? "0.9.9" : "0.0.0-dev";
1726
+ CURRENT_VERSION = true ? "0.9.10" : "0.0.0-dev";
1649
1727
  }
1650
1728
  });
1651
1729
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "md4ai",
3
- "version": "0.9.9",
3
+ "version": "0.9.10",
4
4
  "description": "CLI for MD4AI — scan Claude projects and sync to your dashboard",
5
5
  "type": "module",
6
6
  "bin": {